Metadata-Version: 2.4
Name: weft-soc
Version: 0.1.0
Summary: Weave IP-XACT components into SoC designs from a YAML file: generated SystemVerilog top, filelists, FPGA and ASIC flow scripts. A CLI/file-based alternative to Kactus2, friendly to humans and AI agents.
Project-URL: Homepage, https://github.com/hakursn/weft
Author: Hasan Kurşun
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: asic,eda,fpga,ip-xact,rtl,soc,systemverilog
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
Requires-Python: >=3.10
Requires-Dist: pyyaml>=6.0
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == 'dev'
Description-Content-Type: text/markdown

# weft

**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.

```bash
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 `#define`s: 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

```yaml
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](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

```bash
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.
