Test coverage

Regression tests are defined in the SiFab PDK in order to catch undesired changes and to check desired changes carefully. This is done using the Luceda IP Manager module.

You can use the SiFab tests as an example for adding test coverage to your PDK or component library. Look at the documentation of IP Manager for detailed information on how to define tests, run them, and inspect the results.

File structure

The tests are located in a folder tests at the same level as the main si_fab folder and the building folder:

Tests folder in SiFab

File structure and contents of the tests folder.

The tests folder contains:

  • Configuration files: pytest.ini and conftest.py.

  • Test files: test_awg_designer.py, test_components.py, test_layers.py, test_materials.py and test_waveguides.py.

  • A script generate_reffiles.py to (re)generate the known-good reference files.

Configuration files

The conftest.py file contains definitions common to all tests. In this case, it defines the TECH fixture: the technology to load and use for all tests, which is the si_fab technology. Pytest will execute this fixture before all tests and will make it available to all tests (autouse=True) in the session (scope='session').

import pytest


@pytest.fixture(scope="session", autouse=True)
def TECH():
    from si_fab.technology import TECH

    return TECH

The pytest.ini file contains the configuration of pytest itself. We specify that we want to obtain an HTML report of the tests that have been run together with their results:

[pytest]
addopts =   '--html=report.html' '--self-contained-html'

Layer tests

In tests/test_layers.py, we define a layout that incorporates each layer in the technology. This can be used for cross-checking the layer table and GDSII layer numbers and data types.

Test layout for layers

Test layout for the layers in the technology

A regression test on this full layout catches any changes:

@pytest.mark.comparisons([Compare.LayoutToGds, Compare.LayoutToXML])
class TestLayers(ComponentReferenceTest):
    @pytest.fixture
    def component(self, layer_test_patch):
        return layer_test_patch

Component tests

The file tests/test_waveguides.py contains layout regression tests on the strip and rib waveguides of SiFab. The test is defined on a small cell that contains two waveguides and a transition.

from ip_manager.testing import ComponentReferenceTest, VFab2DReferenceTest, Compare
import pytest

comparisons_layout = [Compare.LayoutToGds, Compare.LayoutToXML]


@pytest.mark.comparisons(comparisons_layout)
class TestWire(ComponentReferenceTest, VFab2DReferenceTest):
    @pytest.fixture
    def component(self):
        from si_fab.all import SiWireWaveguideTemplate, LinearWireTransition
        import ipkiss3.all as i3

        wire_tt = SiWireWaveguideTemplate()
        wire_tt.Layout(core_width=0.4, cladding_width=8.0)
        wire2_tt = SiWireWaveguideTemplate()
        wire2_tt.Layout(core_width=1.0, cladding_width=6.0)
        wg_wire = i3.Waveguide(trace_template=wire_tt)
        wg_wire.Layout(shape=[(0.0, 0.0), (5.0, 0.0)])
        wg_wire2 = i3.Waveguide(trace_template=wire2_tt)
        wg_wire2.Layout(shape=[(25.0, 0.0), (30.0, 0.0)])
        trans = LinearWireTransition(start_trace_template=wire_tt, end_trace_template=wire2_tt)
        trans.Layout(start_position=(5.0, 0.0), end_position=(25.0, 0.0))

        class TestWireCell(i3.PCell):
            class Layout(i3.LayoutView):
                def _generate_instances(self, insts):
                    insts += i3.SRef(wg_wire, name="wire")
                    insts += i3.SRef(trans)
                    insts += i3.SRef(wg_wire2, name="wire2")
                    return insts

                def _generate_ports(self, ports):
                    ports += self.instances["wire"].ports["in"]
                    ports += self.instances["wire2"].ports["out"]
                    return ports

            class NetList(i3.NetlistFromLayout):
                pass

        return TestWireCell(name="TestWire")

In this way, we cover a few different aspects with one test, such as layout drawing and ports as well as the virtual fabrication.

The file tests/test_components.py contains regression tests for the library cells in SiFab. Currently, we implemented the following tests:

  • Layout regression tests for components that only have a layout (e.g. BONDPAD_5050).

  • Layout and model tests for components that have both a layout and a circuit model (e.g. SiDirectionalCouplerS).

from ip_manager.testing import ComponentReferenceTest, CircuitModelTest, Compare
import pytest

layout_comparisons = [Compare.LayoutToGds, Compare.LayoutToXML]
circuit_comparisons = [Compare.SMatrix, Compare.NetlistToXML]

For each PCell in the library, there is a test defined that instantiates the cell with default parameters:

@pytest.mark.comparisons(layout_comparisons)
class TestMMI1x2(ComponentReferenceTest):
    @pytest.fixture
    def component(self):
        from si_fab.components.mmi.pcell.cell import MMI1x2

        sl = MMI1x2(name="TestMMI1x2")
        sl.Layout()
        return sl

Some of the tests are parametric and implement custom checks. For example, for TestFixedPortWithLengthSpiral, we instantiate the PCell for a wide range of lengths and test that the length of the spiral is correct.

class TestFixedPortWithLengthSpiral(ComponentReferenceTest):
    @pytest.fixture
    def component(self, total_length):
        from si_fab.components.spiral.pcell.cell import FixedPortWithLengthSpiral

        sl = FixedPortWithLengthSpiral(total_length=total_length)
        sl.Layout()
        return sl

    @pytest.fixture
    def reference_name(self, total_length):
        return "TestFixedPortWithLengthSpiral_" + str(total_length)

    @pytest.fixture(params=[4000, 5000, 6000])
    def total_length(self, request):
        return request.param

    def test_total_length(self, component, total_length):
        from ipkiss3.pcell.layout.view import LayoutView

        lv = component.get_default_view(LayoutView)
        trace_length = lv.trace_length()
        assert trace_length == pytest.approx(total_length)

AWG Designer tests

SiFab also has support for Luceda AWG Designer. Currently, tests/test_awg_designer.py contains a regression test for the layout of the rib waveguide and rib MMI apertures. It can be extended to cover more components (such as a star coupler) and aspects (such as the netlist or model).

Generating reference files

The tests/generate_reffiles.py file uses the IP Manager function generate_reference_files to generate the known-good reference files.

from ip_manager.testing import generate_reference_files

generate_reference_files()

Calling generate_reference_files without arguments would cause the reference files to be generated for every component reference tests found in the folder in which the script is placed.

Note

The functionality to regenerate reference files should be used with care. Use it only if you are sure you want to overwrite the reference files!