chiptools.testing.testloader module

class chiptools.testing.testloader.ChipToolsTest(methodName='runTest')[source]

Bases: unittest.case.TestCase

The ChipToolsTest class is derived from unittest.TestCase and provides a base class for your unit tests to allow them to make full use of ChipTools.

When creating a unit test class you should override the duration, generics, library and entity attributes to define the test configuration for the simulator. Individual tests can redefine these attributes at run-time to provide a powerful testing mechanism for covering different configurations.

A brief example of a basic unit test case is given below:

>>> from chiptools.testing.testloader import ChipToolsTest
>>> class MyBasicTestCase(ChipToolsTest):
...     duration = 0  # Run forever
...     generics = {'data_width' : 3}  # Set data-width to 3
...     library = 'lib_tb_max_hold'  # Testbench library
...     entity = 'tb_max_hold'  # Entity to simulate
...     # Defining a path to a project allows us to run this test case
...     # with Nosetests etc. as well as through ChipTools.
...     project = os.path.join('max_hold.xml')
...     # The setUp method is called at the beginning of each test:
...     def setUp(self):
...         # Do individual test set-up here
...         pass
...     # Methods starting with 'test_' are considered test cases:
...     def test_max_hold(self):
...         # Run the simulator
...         return_code, stdout, stderr = self.simulate()
...         self.assertEqual(return_code, 0)  # Check error code
...         # More advanced checks could search stdout/stderr for
...         # assertions, or read output files and compare the
...         # response to a Python model.
...         pass
...     # The tearDown method is called at the end of each test:
...     def tearDown(self):
...         # Clean up after your tests here
...         pass

For a complete example refer to the Max Hold example in the examples folder.

duration = 0

The duration attribute defines the time in seconds that the simulation should run for if the chosen simulator supports this as an argument during execution. If a time of 0 is specified the simulation will run until it is terminated automatically by the testbench. e.g. To fix simulation time at 10ms set duration to 10e-3

entity = None

The entity attribute defines the name of the top level component to be simulated when running this test. The entity should name a valid design unit that has been compiled as part of the project.

generics: Dict[str, Any] = {}

The generics attribute is a dictionary of parameter/generic names and associated values. These key, value pairs will be passed to the simulator to override top-level generics or parameters to customise the test environment:

>>> generics = {
...     'data_width' : 3,
...     'invert_bits': True,
...     'test_string': 'hello',
...     'threshold': 0.33,
>>> }

The generics attribute can also be used to dynamically specify parameters for individual tests to check different configurations in your testbench:

>>> def test_32_bit_bus(self):
...     self.generics['data_width'] = 32
...     self.simulate()
>>> def test_16_bit_bus(self):
...     self.generics['data_width'] = 16
...     self.simulate()
static get_environment(project, tool_name=None)[source]

Return the simulation environment items from the supplied project instance as a tuple of (simulator, simulation_root, libraries).

library = None

The library attribute defines the name of the library in which the top level component to be simulated exists.

load_environment(project, tool_name=None)[source]

Initialise the TestCase simulation environment using the supplied Project reference so that the individual tests implemented in this TestCase are able to compile and simulate the design.

project = None

The project attribute is optional, but if used it should supply an absolute path to a valid ChipTools Project XML file that defines the libraries and source files that make up the design that this test case belongs to. This attribute is required when the test case is executed directly by an external test runner instead of ChipTools, as it will be used to prepare the simulation environment for the external test runner.

The following provides a convenient way of setting the project path so that your test can be run from any directory:

>>> from chiptools.testing.testloader import ChipToolsTest
>>> class MyUnitTest(ChipToolsTest)
...     base = os.path.dirname(__file__)
...     # Now use os.path.join to build a relative path to the project.
...     project = os.path.join(base, '..', 'my_project.xml')
classmethod setUpClass()[source]

The setUpClass method prepares the ChipTools simulation environment if it has not already been loaded.

If this test case is loaded via the ChipTools Project API it will be initialised via a call to the load_environment method, which pulls the simulation environment information from the parent Project instance.

If this test case is loaded via an external tool such as Nosetests the setUpClass method will attempt to load the project file pointed to by the project path stored in the project attribute. When you create your test case you can specify this attribute in your test class to allow an external runner like Nosetests to call your test cases.

If the environment was not already initialised by ChipTools and a valid project file path is not stored in the project attribute, this method will raise an EnvironmentError and cause your test to fail.

This method overloads the unittest.TestCase.setUpClass classmethod, which is called once when a TestCase class is instanced.

simulate()[source]

Launch the simulation tool in console mode to execute the testbench.

The simulation tool used and the arguments passed during simulation are defined by the test environment configured by the test case and the Project settings. When the simulation is complete this method will return a tuple of (return_code, stdout, stderr) which can be used to determine if the test was a success or failure. For example your testbench may use assertions to print messages during simulation, your Python TestCase could use regex to match success of failure criteria in the stdout string:

>>> def test_stdout(self):
...     return_code, stdout, stderr = self.simulate()
...     # Use an assertion to check for a negative result on a search
...     # for 'Error:' in the simulator stdout string.
...     self.assertIsNone(re.search('.*Error:.*', stdout))
property simulation_root

The simulation_root property is an absolute path to the directory where the simulator is invoked when simulating the testbench. Any inputs required by the testbench, such as stimulus files, should be placed in this directory by your TestCase. Similarly, any outputs produced by the testbench will be placed in this directory.

For example, to build paths to a testbench input and output file you could do the following:

>>> def setUp(self):
...     self.tb_in = os.path.join(self.simulation_root, 'input.txt')
...     self.tb_out = os.path.join(self.simulation_root, 'output.txt')