A XactFlow importer plugin that turns a design described
in a Python script (which IPs it uses, how they are connected, and what the SoC offers to the
outside) into an ipxact.Design and the ipxact.Component containing it, built on
ipxact-compiler's
IEEE 1685-2022 object model.
IP-XACT design files are not made to be handwritten. With this package, a short script lists
the IP instances, the bus connections between them, any direct signal connections, and the
top-level ports and bus interfaces. The importer runs the script and adds the Design and the
Component containing it to a XactFlow Library as targets, for an exporter to write;
XactFlow's link() can check the design against the IP-XACT rules.
The full description of the format is in
doc/python-design-description.md. It describes the tool
as it is meant to work once finished, so the current version can differ from it in small ways.
The differences known today are listed in Known limitations.
pip install xactflow-archidescFor local development, requirements-dev.txt overrides ipxact-compiler, xactflow and
xactflow-design to local installations. It can be modified to adapt the paths or to keep any
of them on PyPI.
pip install -r requirements-dev.txt -e .Requires Python >= 3.9.
A design script builds a Design from an IP library and keeps it in a module-level variable
named design:
from pathlib import Path
from xactflow import Library
from xactflow_archidesc import Design
library = Library.scan(Path(__file__).parent / "ip")
design = Design("example.org:soc:example:0.1", library)
cpu = design.add_ip("cpu", "example.org:ip:cpu:1.0", transforms=["tmrg"])
uart = design.add_ip("uart", "example.org:ip:uart:1.0", parameters={"BAUDRATE": 115200})
timer = design.add_ip("timer", "example.org:ip:timer:1.0")
debug = design.add_ip("debug", "example.org:ip:debug_module:1.0")
bus = design.add_ip("bus", "example.org:ip:interconnect:1.0")
design.connect_bus(cpu, bus)
design.connect_bus(bus.bus("m0"), uart, base_addr=0x4001_0000, size=0x1000)
design.connect_bus(bus, timer, base_addr=0x4002_0000, size=0x1000)
design.connect(debug.debug_req_o, cpu.debug_req_i)
design.connect(cpu.debug_resp_o, debug.debug_resp_i)
design.add_port("inout", "jtag_io", connect=debug.jtag_io)
design.add_port("out", "irq_o", connect=uart.irq_o)Design("example.org:soc:example:0.1", ...) names the SoC: the component is
example.org:soc:example.component:0.1 and the design example.org:soc:example.design:0.1.
A runnable version of this script, with its IP library, is in
tests/fixtures/soc.py.
In short:
add_ip(name, vlnv, ...)adds an IP instance, with optionalparametersoverrides andtransforms.connect_bus(a, b, ...)connects a bus interface ofato one ofb. It picks them by itself, or uses the one named withinstance.bus("name").base_addr/sizeplace a target in the address map.connect(source, target)connects two ports.instance.some_portis short forinstance.port("some_port").add_port(direction, name, width=None)adds a top-level port, andadd_bus(name, bus_type, mode)a top-level bus interface.connect()andconnect_bus()connect them to the instances, orconnect=does it right away. A top-level bus interface gets its ports from the internal interface it connects to, named<bus>_<logical port>.
Each call checks its arguments right away, so a mistake is reported on the line that made it. All the details are in the format description.
Once installed, the importer registers itself under the xactflow.importers entry point group
as archidesc, so it runs as a XactFlow CLI command. With xactflow-design or
xactflow-component installed too, --then hands the library holding the design and the
component straight to that exporter. --lib adds IP-XACT folders to that library, for an
exporter that needs the instantiated components. To try it on the example of this repo,
create a virtual environment and install the package in it first. The xactflow command only
exists inside that environment, so activate it again in each new terminal:
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
xactflow archidesc tests/fixtures/soc.py --lib tests/fixtures/library --then design --output out/This writes out/example.org_soc_example.design_0.1.xml, named after the design's VLNV;
--then component writes the component, out/example.org_soc_example.component_0.1.xml. Both
are regenerated on every run: if out/ is also given with --lib, the new documents replace
the ones read from there instead of being reported as duplicate VLNVs.
Or as a library, for example to link the design first:
from pathlib import Path
import ipxact
from xactflow import Library, link
from xactflow_archidesc import ArchiDescImporter
from xactflow_component import ComponentExporter
from xactflow_design import DesignExporter
library = Library.scan("ip")
ArchiDescImporter().import_(Path("soc.py"), library)
[design] = library.targets(ipxact.Design)
linked = link(design, library) # finds the component containing the design in the library
DesignExporter().export(library, Path("out"))
ComponentExporter().export(library, Path("out"))The script runs like python soc.py would: a helper module next to it can be imported, and a
relative path in it is read from the current folder. Helper modules are forgotten once the
script has run, so scripts in different folders can each have their own helpers.py.
Path(__file__).parent gives paths that do not depend on where the command is run from. The
importer takes no options.
- Vendor extensions. Two things this format records have no place in IP-XACT, so they are
written as vendor extensions in the
https://github.com/IP-XACT/XactFlownamespace:<xactflow:transforms>on a component instance, listing the transformations in order, and<xactflow:addressMapping baseAddr="..." size="..."/>on the target side of a bus connection (a target or mirrored initiator interface). - Transformations are not applied here. A separate tool, a transformer, reads
<xactflow:transforms>, applies the transformations and returns the changedDesign. - Parameter overrides follow the standard. Only a parameter with a
parameterIdandresolve="user"can be overridden. The standard says animmediateparameter (the default) cannot be changed through configuration. - Connection names are unique. A bus connection is named
<a>_to_<b>and a port connection<instance>_<port>_to_<instance>_<port>, a top-level port or bus interface standing for itself by its name alone. A number is added when a name is already taken, by any kind of connection. - Top-level IOs. The component containing the design has one view,
hierarchical, whosedesignInstantiationrefers to the design: that is howlink()finds it. A top-level port is reached with anexternalPortReferenceand connects to an instance port of the same direction; a top-level bus interface is reached with ahierInterfaceand connects to an instance interface of the same mode, as the standard requires (SCR 2.4 to 2.10). - Monitors. Connecting a monitor interface adds a
monitorInterconnection. The interface it watches stays free for a normal connection.
connect_buscompares the bus definitions of the two interfaces, but not the parameter values set on the bus type (a data width, for example). Comparing them needs XactFlow to evaluate those values first. This should be changed in a future update.- XactFlow's
link()does not use the parameter overrides fromadd_ipyet. They are written to the design and exported, but not applied during linking. connectchecks direction only for wire ports. Two transactional or two structured ports are connected with no further check, only a mix of the two kinds is refused.connectdoes not compare port widths, between instance ports or with a top-level port.- A top-level bus interface copies whole internal ports. A port map onto part of a port (a
partSelector sub-port) is refused, and a copied port keeps its width as written, even when it refers to a parameter of the internal component. - A design describes one level only. Hierarchical designs, a real address mapping (instead of
the
addressMappingplaceholder) and protocol bridges are not handled yet, see the open questions at the end of the format description.
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt -e .
pytest
mypy srcThe tests use a small IP library in tests/fixtures/library. tests/test_end_to_end.py runs
the example script through the importer, links the result, exports it with
xactflow-design and reads the file back. mypy src checks the type hints, since the
package ships py.typed. CI runs both.
LGPL-3.0. See LICENSE.