weft-soc
Weave IP-XACT components into SoC designs — from a file, not a GUI.
Weave IP-XACT components into SoC designs — from a file, not a GUI.
weft reads IEEE 1685-2014 IP-XACT component descriptions, takes a small
YAML design file, and emits everything needed to build the SoC: a generated
SystemVerilog top, a compile-order filelist, an FPGA build script (Vivado)
and a silicon synthesis script (sv2v + yosys), plus a wiring report. It
replaces the pointy-clicky part of tools like Kactus2 with something people
and AI agents can drive, diff, and code-review.
pip install weft-soc
weft ls path/to/ipxact/ # what components do I have?
weft check my_soc.yaml # parse + weave, report issues
weft map my_soc.yaml --svg map.svg # validate + draw the address map
weft headers my_soc.yaml -o regs.h # C header from the register maps
weft gen my_soc.yaml -o out/ --tb # generate the SoC (+ cocotb TB)
weft tb my_soc.yaml -o out/ # just the cocotb testbench
weft lint my_soc.yaml # generate + Verilator-lint it
# out/: my_soc.sv, my_soc.f, build_fpga.tcl, build_asic.sh, report.md,
# address_map.md, address_map.svg, my_soc_regs.h,
# runner.py, test_regs.py (headers/map when address_map is set;
# TB with --tb)
What it generates
| Output | From | For |
|---|---|---|
<top>.sv | the weave | synthesizable SoC top |
<top>.f | component fileSets | compile-order filelist |
build_fpga.tcl | design + weave | timing-gated Vivado OOC flow |
build_asic.sh | design + weave | sv2v + yosys silicon synthesis (LIBERTY= to map cells) |
address_map.{md,svg} | address_map | validated map + proportional diagram |
<top>_regs.h | IP-XACT memoryMaps | C #defines: bases, offsets, absolute addrs, field masks/shifts |
runner.py + test_regs.py | memoryMaps + weave | cocotb runner + auto register-regression (reset check + R/W round-trip over every register) |
Run the generated testbench with pip install cocotb and a supported
simulator (e.g. Verilator) on PATH, then python runner.py in the output
directory. The register-regression drives the promoted AXI4-Lite manager
port; if the design promotes none, weft emits a clock/reset smoke stub
instead and says so.
The only new RTL is the generated top; by default the filelist references
each IP's original sources in place (relative paths), so the component
repos stay the single source of truth. Pass --bundle to instead copy every
referenced source into out/src/<component>/… (one subfolder per IP,
preserving each component's internal layout) — the output directory then
ships as one self-contained artifact. The ASIC flow additionally produces a single flattened
<top>.flat.v via sv2v as its intermediate.
The design file
top: soc_demo # generated module name
part: xc7a100tcsg324-1 # optional: FPGA part for build_fpga.tcl
clocks: [clk] # optional: constrained in flow scripts
libraries: # dirs scanned recursively for IP-XACT
- ../aes/ipxact
- ../xbar/ipxact
instances:
xbar0: {vlnv: "hasankursun.dev:ip:xbar_axil:1.0"}
uart0: {vlnv: "hasankursun.dev:ip:uart_axil:1.0"}
ila0: {vlnv: "hasankursun.dev:ip:ila_axil:1.0", params: {DEPTH: 256}}
connections: # bus interface <-> bus interface
- xbar0.m00_axi <-> uart0.s_axi
- xbar0.m01_axi <-> ila0.s_axi
external: # promote to top-level ports
ctrl: {interface: xbar0.s00_axi} # whole bus interface
tx: {port: uart0.tx_o} # single port
clk: {drive: [xbar0.aclk, uart0.aclk, ila0.aclk]} # fanned-out input
assigns: # raw escape hatch; wires are
- "ila0_probe_i = {30'd0, uart0_tx_o, uart0_rx_i}" # {instance}_{port}
address_map: # friendly base/size per peripheral
uart0: {base: 0x0000_0000, size: 0x1000}
ila0: {base: 0x0000_1000, size: 0x1000}
params_from_map: [xbar0] # derive this interconnect's BASE/MASK
Address map, interconnect derivation, and the "configure, don't generate" stance
weft map validates the address map — power-of-two sizes, natural
alignment, no overlap, and each window at least as large as the IP's own
register span — and fails loudly on any violation (the classic silent SoC
killer, caught statically). params_from_map then derives the crossbar's
BASE/MASK parameters from that map, so the address map is the single
source of truth and the interconnect can't disagree with the headers or the
docs.
Note the deliberate choice: weft configures a formally-proven interconnect
IP (xbar_axil, with a proven fairness bound and end-to-end proofs) rather
than emitting fresh, unverified crossbar RTL. The map decides addresses; the
proven RTL does the decoding.
Semantics
- Connections match the logical port names shared by the two
interfaces' port maps (
AWADDR,TDATA, …), honouringpartSelectslices — interconnects that expose per-index interfaces as slices of flat vectors work out of the box. Bus VLNVs must match; master/slave modes are checked. - Robustness ("never emit a broken top"): width mismatches connect the
low bits with a warning (e.g. a 32-bit crossbar address meeting an 8-bit
peripheral); one-sided logicals warn and zero-tie receivers; any instance
input bits left undriven are zero-tied with a warning. Every decision is
in
report.md. - Parameters are passed through verbatim (bit-vector literals fine) and
used to evaluate expression port widths like
ADDR_W-1. Note that IP-XACT components describe one static configuration — if a component was generated for a 2×4 crossbar, instantiate it as 2×4 or regenerate its XML. - FPGA vs silicon:
build_fpga.tclruns a timing-gated Vivado out-of-context flow (bring your own XDC for a board build);build_asic.shflattens with sv2v and synthesizes with yosys, mapping to your standard cells whenLIBERTY=is provided.
Tested against a real portfolio
The examples/soc_demo.yaml design weaves four independently-verified IPs (AES crypto core, AXI4-Lite crossbar, UART, internal logic analyzer — each with formal proofs and on-board validation) into a Verilator-clean SoC. The unit suite runs on self-contained fixtures; the integration test picks up the sibling repos when present.
Development
pip install -e .[dev]
pytest
Releases are automated: pushing a v* tag builds and publishes to PyPI via
GitHub Actions trusted publishing (see .github/workflows/release.yml).
License
Apache-2.0. Copyright 2026 Hasan Kurşun.