Docs/SoC

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

OutputFromFor
<top>.svthe weavesynthesizable SoC top
<top>.fcomponent fileSetscompile-order filelist
build_fpga.tcldesign + weavetiming-gated Vivado OOC flow
build_asic.shdesign + weavesv2v + yosys silicon synthesis (LIBERTY= to map cells)
address_map.{md,svg}address_mapvalidated map + proportional diagram
<top>_regs.hIP-XACT memoryMapsC #defines: bases, offsets, absolute addrs, field masks/shifts
runner.py + test_regs.pymemoryMaps + weavecocotb 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, …), honouring partSelect slices — 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.tcl runs a timing-gated Vivado out-of-context flow (bring your own XDC for a board build); build_asic.sh flattens with sv2v and synthesizes with yosys, mapping to your standard cells when LIBERTY= 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.