bayerlink-fpga 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bayerlink_fpga-0.3.0/.github/workflows/ci.yml +149 -0
- bayerlink_fpga-0.3.0/.github/workflows/publish.yml +73 -0
- bayerlink_fpga-0.3.0/.gitignore +32 -0
- bayerlink_fpga-0.3.0/CHANGELOG.md +235 -0
- bayerlink_fpga-0.3.0/LICENSE +45 -0
- bayerlink_fpga-0.3.0/LICENSE-APACHE +202 -0
- bayerlink_fpga-0.3.0/PKG-INFO +234 -0
- bayerlink_fpga-0.3.0/README.md +218 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/__init__.py +25 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/ddc.py +107 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/frontends/__init__.py +17 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/frontends/hdmi_rx.json +73 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/hdl/axis_spy.v +46 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/hdl/axis_unpack.v +37 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/hdl/bayerlink_in.v +843 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/hdl/ddc_phy.v +21 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/hdl/link_reset.v +93 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/hdl/rx_axis.v +50 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/hdl/stream_switch.v +58 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/hdl/tmds_align.v +291 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/hdl/tmds_bond.v +163 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/hdl/tmds_rx.v +203 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/hdl/tmds_rx.xdc +22 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/hdl/tmds_rx_lane.v +89 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/modules.json +245 -0
- bayerlink_fpga-0.3.0/bayerlink_fpga/receiver.py +257 -0
- bayerlink_fpga-0.3.0/pyproject.toml +30 -0
- bayerlink_fpga-0.3.0/scripts/checklicence.py +84 -0
- bayerlink_fpga-0.3.0/scripts/judge.py +180 -0
- bayerlink_fpga-0.3.0/sim/tb_link_reset.v +91 -0
- bayerlink_fpga-0.3.0/tests/receiver_proof.py +599 -0
- bayerlink_fpga-0.3.0/tests/sim/tb_tmds_rx.v +353 -0
- bayerlink_fpga-0.3.0/tests/sim/xilinx_models.v +155 -0
- bayerlink_fpga-0.3.0/tests/test_frontends.py +69 -0
- bayerlink_fpga-0.3.0/tests/test_receiver.py +40 -0
- bayerlink_fpga-0.3.0/tests/test_tmds_rx_sim.py +36 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-24.04
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
- name: Install iverilog
|
|
22
|
+
run: sudo apt-get update && sudo apt-get install -y iverilog
|
|
23
|
+
- name: Install Verilator
|
|
24
|
+
run: |
|
|
25
|
+
curl -fsSL -o /tmp/oss-cad.tgz \
|
|
26
|
+
https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2026-08-08/oss-cad-suite-linux-x64-20260808.tgz
|
|
27
|
+
tar -xzf /tmp/oss-cad.tgz -C /opt
|
|
28
|
+
echo "/opt/oss-cad-suite/bin" >> "$GITHUB_PATH"
|
|
29
|
+
/opt/oss-cad-suite/bin/verilator --version
|
|
30
|
+
- run: |
|
|
31
|
+
python -m pip install --upgrade pip
|
|
32
|
+
pip install -e . pytest
|
|
33
|
+
- name: Every source file states this repository's licence
|
|
34
|
+
run: python scripts/checklicence.py
|
|
35
|
+
- name: Dependency licences are permissive
|
|
36
|
+
# An ALLOW-LIST, checked against the resolved environment. A deny-list
|
|
37
|
+
# only catches the licences somebody thought to name; the one that
|
|
38
|
+
# causes trouble is the one nobody anticipated, and an allow-list
|
|
39
|
+
# catches that by construction.
|
|
40
|
+
#
|
|
41
|
+
# A new dependency under an unlisted licence fails here. If the licence
|
|
42
|
+
# is genuinely permissive, add it to PERMITTED with a one-line reason.
|
|
43
|
+
# The full dependency audit lives in revela's docs/THIRD-PARTY.md,
|
|
44
|
+
# which covers this package transitively.
|
|
45
|
+
run: |
|
|
46
|
+
python - <<'PY'
|
|
47
|
+
import re, sys
|
|
48
|
+
from importlib.metadata import distributions
|
|
49
|
+
|
|
50
|
+
PERMITTED = {
|
|
51
|
+
"MIT", "MIT-0", "BSD-2-Clause", "BSD-3-Clause", "0BSD",
|
|
52
|
+
"Apache-2.0", "PSF-2.0", "ISC", "Zlib", "CC0-1.0", "HPND",
|
|
53
|
+
"Python-2.0", "Unlicense", "MPL-2.0",
|
|
54
|
+
"Apache-2.0 WITH SHL-2.1", # bayerlink-fpga itself
|
|
55
|
+
}
|
|
56
|
+
# MPL-2.0 is permitted only where it appears: a build-time transitive
|
|
57
|
+
# of the build backend, never imported, never redistributed in the
|
|
58
|
+
# wheel.
|
|
59
|
+
|
|
60
|
+
def terms(expression):
|
|
61
|
+
"""The identifiers an SPDX expression names.
|
|
62
|
+
|
|
63
|
+
Split on AND/OR only: `WITH` binds an exception to its licence
|
|
64
|
+
("Apache-2.0 WITH SHL-2.1") and the pair is one identifier.
|
|
65
|
+
Every branch of an OR must be permitted, which is stricter than
|
|
66
|
+
SPDX requires and is the right way round for this check.
|
|
67
|
+
"""
|
|
68
|
+
parts = re.split(r"\s+(?:AND|OR)\s+|[()]", expression)
|
|
69
|
+
return {p.strip() for p in parts if p.strip()}
|
|
70
|
+
|
|
71
|
+
# Three metadata generations are in the wild at once, so all three are
|
|
72
|
+
# read: the PEP 639 `License-Expression`, the `License ::` trove
|
|
73
|
+
# classifiers, and the legacy free-text `License` field. A package
|
|
74
|
+
# that declares its licence only in the oldest form is not an
|
|
75
|
+
# undeclared package, and reporting it as one would train everyone to
|
|
76
|
+
# ignore this check.
|
|
77
|
+
LEGACY = {
|
|
78
|
+
"mit": "MIT", "mit license": "MIT", "expat": "MIT",
|
|
79
|
+
"bsd": "BSD-3-Clause", "bsd license": "BSD-3-Clause",
|
|
80
|
+
"bsd-3-clause": "BSD-3-Clause", "bsd 3-clause": "BSD-3-Clause",
|
|
81
|
+
"bsd-2-clause": "BSD-2-Clause", "apache-2.0": "Apache-2.0",
|
|
82
|
+
"apache 2.0": "Apache-2.0", "apache software license": "Apache-2.0",
|
|
83
|
+
"isc": "ISC", "psf-2.0": "PSF-2.0", "mpl-2.0": "MPL-2.0",
|
|
84
|
+
}
|
|
85
|
+
CLASSIFIER = {
|
|
86
|
+
"MIT License": "MIT", "MIT No Attribution License (MIT-0)": "MIT-0",
|
|
87
|
+
"BSD License": "BSD-3-Clause",
|
|
88
|
+
"Apache Software License": "Apache-2.0",
|
|
89
|
+
"Python Software Foundation License": "PSF-2.0",
|
|
90
|
+
"Mozilla Public License 2.0 (MPL 2.0)": "MPL-2.0",
|
|
91
|
+
"ISC License (ISCL)": "ISC", "The Unlicense (Unlicense)": "Unlicense",
|
|
92
|
+
"Zope Public License": None,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
def declared(dist):
|
|
96
|
+
"""(identifiers, how it was declared) for one distribution."""
|
|
97
|
+
expression = (dist.metadata.get("License-Expression") or "").strip()
|
|
98
|
+
if expression:
|
|
99
|
+
return terms(expression), expression
|
|
100
|
+
classifiers = [c for c in dist.metadata.get_all("Classifier") or []
|
|
101
|
+
if c.startswith("License ::")]
|
|
102
|
+
if classifiers:
|
|
103
|
+
names = [c.rsplit(" :: ", 1)[-1] for c in classifiers]
|
|
104
|
+
return ({CLASSIFIER.get(n, n) for n in names},
|
|
105
|
+
"classifier: " + "; ".join(names))
|
|
106
|
+
legacy = (dist.metadata.get("License") or "").strip()
|
|
107
|
+
if legacy and "\n" not in legacy and len(legacy) < 60:
|
|
108
|
+
return ({LEGACY.get(legacy.lower(), legacy)},
|
|
109
|
+
f"legacy License field: {legacy}")
|
|
110
|
+
return set(), "no declared licence"
|
|
111
|
+
|
|
112
|
+
# setuptools 78/79 dropped its `License ::` classifiers, and its
|
|
113
|
+
# metadata predates `License-Expression`, so it declares its licence
|
|
114
|
+
# in none of the three places read above. It is MIT; it is a build
|
|
115
|
+
# tool, never imported and never redistributed in a wheel; and pip
|
|
116
|
+
# brings it whether or not anything asked. Named here rather than
|
|
117
|
+
# waved through: anything ELSE that declares nothing still fails.
|
|
118
|
+
UNDECLARED_UPSTREAM = {"setuptools": "MIT"}
|
|
119
|
+
|
|
120
|
+
problems, checked = [], 0
|
|
121
|
+
for dist in distributions():
|
|
122
|
+
name = dist.metadata["Name"]
|
|
123
|
+
if not name:
|
|
124
|
+
continue
|
|
125
|
+
identifiers, how = declared(dist)
|
|
126
|
+
if not identifiers and name.lower() in UNDECLARED_UPSTREAM:
|
|
127
|
+
identifiers = {UNDECLARED_UPSTREAM[name.lower()]}
|
|
128
|
+
how = "known upstream packaging gap"
|
|
129
|
+
checked += 1
|
|
130
|
+
if not identifiers:
|
|
131
|
+
problems.append(f"{name} {dist.version}: no declared licence")
|
|
132
|
+
continue
|
|
133
|
+
unlisted = identifiers - PERMITTED
|
|
134
|
+
if unlisted:
|
|
135
|
+
problems.append(f"{name} {dist.version}: {how} "
|
|
136
|
+
f"-> not permitted: {sorted(unlisted)}")
|
|
137
|
+
|
|
138
|
+
print(f"checked {checked} distributions against the allow-list")
|
|
139
|
+
if problems:
|
|
140
|
+
print("\nNOT PERMITTED:")
|
|
141
|
+
for p in problems:
|
|
142
|
+
print(" " + p)
|
|
143
|
+
print("\nA dependency whose licence is not on the allow-list "
|
|
144
|
+
"cannot be added without a decision.")
|
|
145
|
+
sys.exit(1)
|
|
146
|
+
print("every dependency licence is on the allow-list")
|
|
147
|
+
PY
|
|
148
|
+
|
|
149
|
+
- run: pytest -q
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Publish bayerlink-fpga to PyPI via Trusted Publishing (OIDC) -- no stored token.
|
|
2
|
+
#
|
|
3
|
+
# One-time setup on PyPI:
|
|
4
|
+
# https://pypi.org/manage/account/publishing/ -> add a pending publisher
|
|
5
|
+
# PyPI project name : bayerlink-fpga
|
|
6
|
+
# Owner : bayerlink
|
|
7
|
+
# Repository : bayerlink-fpga
|
|
8
|
+
# Workflow : publish.yml
|
|
9
|
+
# Environment : pypi
|
|
10
|
+
# Then in this repository: Settings -> Environments -> create `pypi`,
|
|
11
|
+
# with a required reviewer so an accidental tag cannot publish.
|
|
12
|
+
#
|
|
13
|
+
# To release: bump version in pyproject.toml, tag vX.Y.Z, publish a GitHub
|
|
14
|
+
# Release from the tag.
|
|
15
|
+
name: Publish
|
|
16
|
+
|
|
17
|
+
on:
|
|
18
|
+
release:
|
|
19
|
+
types: [published]
|
|
20
|
+
workflow_dispatch:
|
|
21
|
+
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
|
|
25
|
+
jobs:
|
|
26
|
+
build:
|
|
27
|
+
runs-on: ubuntu-24.04
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
- uses: actions/setup-python@v5
|
|
31
|
+
with:
|
|
32
|
+
python-version: "3.12"
|
|
33
|
+
- name: Build and check
|
|
34
|
+
run: |
|
|
35
|
+
python -m pip install --upgrade pip build twine packaging
|
|
36
|
+
python -m build
|
|
37
|
+
twine check --strict dist/*
|
|
38
|
+
- name: Verify the tag matches the declared version
|
|
39
|
+
if: github.event_name == 'release'
|
|
40
|
+
run: |
|
|
41
|
+
python - <<'PY'
|
|
42
|
+
import os, pathlib, sys, tomllib
|
|
43
|
+
from packaging.version import Version
|
|
44
|
+
declared = tomllib.loads(
|
|
45
|
+
pathlib.Path("pyproject.toml").read_text())["project"]["version"]
|
|
46
|
+
tag = os.environ["GITHUB_REF_NAME"].lstrip("v")
|
|
47
|
+
if Version(declared) != Version(tag):
|
|
48
|
+
sys.exit(f"tag {tag!r} != pyproject version {declared!r}")
|
|
49
|
+
print("tag and version agree:", declared)
|
|
50
|
+
PY
|
|
51
|
+
- name: Install the wheel into a clean environment
|
|
52
|
+
run: |
|
|
53
|
+
python -m venv /tmp/clean
|
|
54
|
+
/tmp/clean/bin/pip install dist/*.whl
|
|
55
|
+
/tmp/clean/bin/python -c "import bayerlink_fpga.receiver, bayerlink_fpga.ddc; print('bayerlink_fpga imports')"
|
|
56
|
+
- uses: actions/upload-artifact@v4
|
|
57
|
+
with:
|
|
58
|
+
name: distributions
|
|
59
|
+
path: dist/
|
|
60
|
+
|
|
61
|
+
pypi:
|
|
62
|
+
needs: build
|
|
63
|
+
if: github.event_name == 'release'
|
|
64
|
+
runs-on: ubuntu-24.04
|
|
65
|
+
environment: pypi
|
|
66
|
+
permissions:
|
|
67
|
+
id-token: write
|
|
68
|
+
steps:
|
|
69
|
+
- uses: actions/download-artifact@v4
|
|
70
|
+
with:
|
|
71
|
+
name: distributions
|
|
72
|
+
path: dist/
|
|
73
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# GENERATED, and so not source. Every number in these is read from the
|
|
2
|
+
# file that owns it -- the board, the design, or the ISP's own published
|
|
3
|
+
# contract -- and build.sh regenerates all of them before Vivado starts.
|
|
4
|
+
# Committed, they would be a second copy of facts that already have
|
|
5
|
+
# owners, going stale in silence the first time someone edits an owner
|
|
6
|
+
# and does not rebuild. That disagreement does not surface as an error:
|
|
7
|
+
# a width mismatch in a block design is a CRITICAL WARNING and a quiet
|
|
8
|
+
# connection of the low bits.
|
|
9
|
+
hdl/generated/
|
|
10
|
+
boards/*/generated/
|
|
11
|
+
board-files
|
|
12
|
+
vivado-library
|
|
13
|
+
build/
|
|
14
|
+
out/
|
|
15
|
+
*.jou
|
|
16
|
+
*.log
|
|
17
|
+
*.bit
|
|
18
|
+
*.hwh
|
|
19
|
+
.Xil/
|
|
20
|
+
NA/
|
|
21
|
+
# dvi2rgb writes its timing tables beside the board while it elaborates
|
|
22
|
+
# and removes them again. They exist only during a build, which is
|
|
23
|
+
# exactly when someone is most likely to stage everything.
|
|
24
|
+
boards/*/dgl_*.data
|
|
25
|
+
clockInfo.txt
|
|
26
|
+
tight_setup_hold_pins.txt
|
|
27
|
+
.DS_Store
|
|
28
|
+
__pycache__/
|
|
29
|
+
|
|
30
|
+
# Packaging
|
|
31
|
+
dist/
|
|
32
|
+
*.egg-info/
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes to bayerlink-fpga — the bayerlink receiver on real
|
|
4
|
+
silicon: the receiver and its TMDS front ends. (Through 0.2.1 it also
|
|
5
|
+
carried the PYNQ-Z2 board design: a generated ISP and a display raster.)
|
|
6
|
+
|
|
7
|
+
Up to 0.2.1 this repository published no package: a tag said which
|
|
8
|
+
**generator versions** the recorded bitstream was built from, because the
|
|
9
|
+
claim was reproducibility. From the next release it publishes
|
|
10
|
+
`bayerlink-fpga`, the receiver's package, and the board design lives in
|
|
11
|
+
revela.
|
|
12
|
+
|
|
13
|
+
## 0.3.0
|
|
14
|
+
|
|
15
|
+
**An HDMI receiver of our own, `tmds_rx`** (`hdl/tmds_rx.v`), as a
|
|
16
|
+
second implementation of `frontends/hdmi_rx`: an MMCM at five times the
|
|
17
|
+
link into BUFIO and BUFR, an IDELAYE2 and an ISERDESE2 pair a lane, and
|
|
18
|
+
an alignment made for links that blank every line. Every delay tap of
|
|
19
|
+
every bit slip is watched for tens of lines, for whether it sees blanks
|
|
20
|
+
at all and how many control tokens it misreads; a lane samples at the
|
|
21
|
+
middle of the taps that see blanks -- the walls of its eye, not its
|
|
22
|
+
error-free taps, which sit off the middle -- at the least delay, so every
|
|
23
|
+
lock lands on the same taps, and a mode change costs ~150 ms where
|
|
24
|
+
dvi2rgb's 50 ms per tap cost seconds. The lanes are bonded where their
|
|
25
|
+
blanking ends, held bonded through a stray misread token, and a line's
|
|
26
|
+
data is what two of the three lanes say; `locked` is the clock locked and
|
|
27
|
+
the lanes delivering, falling at once when the cable is pulled. What it
|
|
28
|
+
saw is the block's optional account of the link, as named ports: the
|
|
29
|
+
bond, each lane's alignment, eye and bit slip, and a survey of every
|
|
30
|
+
lane's misread tokens at every tap of every slip, an entry at a time.
|
|
31
|
+
Proven in simulation against a DVI source skewed by bits and whole
|
|
32
|
+
words, with stray bit errors in its blanking, through a cable pull, at
|
|
33
|
+
148.5, 74.25 and 27 MHz links (`tests/test_tmds_rx_sim.py`), and on the
|
|
34
|
+
PYNQ-Z2 at 148.5 MHz: twenty locks bit-exact, and a 1080p30 camera's
|
|
35
|
+
stream for minutes without a dropped lock.
|
|
36
|
+
|
|
37
|
+
**The EDID's range limits are its link mode's.** The monitor range
|
|
38
|
+
descriptor stated 29-31 Hz and 30-40 kHz whatever the build advertised;
|
|
39
|
+
it is derived from the container's own timing now, a step either side,
|
|
40
|
+
with its pixel clock as the ceiling.
|
|
41
|
+
|
|
42
|
+
**The board design moved to revela; this repository became the receiver's
|
|
43
|
+
package.** It keeps what any FPGA receiving bayerlink needs, and nothing
|
|
44
|
+
that belongs to one design:
|
|
45
|
+
|
|
46
|
+
- `bayerlink_fpga`, importable, in two layers: the receiver
|
|
47
|
+
(`receiver.py`, `ddc.py`, `hdl/`) and front ends (`frontends/hdmi_rx`).
|
|
48
|
+
The receiver and the DDC slave are leaves a design names --
|
|
49
|
+
`bayerlink_fpga.receiver:bayerlink_in`, `bayerlink_fpga.ddc:ddc_slave`
|
|
50
|
+
-- and its build sets them from the design's settings. Nothing in
|
|
51
|
+
it knows a board: a
|
|
52
|
+
board's pins and its front-end configuration live in that board's
|
|
53
|
+
library.
|
|
54
|
+
- Front ends are one block, `frontends/hdmi_rx`, described once with its
|
|
55
|
+
implementations; a design names the block, and a parameter chooses the
|
|
56
|
+
implementation. The lane map moved from the board to the front end: it
|
|
57
|
+
is a fact of the dvi2rgb implementation, following that IP's bus
|
|
58
|
+
order, so any board receiving through dvi2rgb inherits it, and the
|
|
59
|
+
receiver's lanes are assigned from it.
|
|
60
|
+
- The PYNQ-Z2 design -- block design, datapath, ISP pipeline, raster,
|
|
61
|
+
tee, bench scripts, clocking notes -- is `revela/boards/pynq_z2` in
|
|
62
|
+
revela, and builds there. Its generated Verilog is byte-identical to
|
|
63
|
+
this repository's 0.2.1 flow on the same generators.
|
|
64
|
+
|
|
65
|
+
**The judge finds registers through the build's address map.** revela's
|
|
66
|
+
design now has its own register bus, which Vivado cannot see through, so
|
|
67
|
+
the `.hwh` no longer lists the slaves; `scripts/judge.py` reads
|
|
68
|
+
`address_map.json`, written beside the bitstream, instead of PYNQ's
|
|
69
|
+
`ip_dict`. Judged 4/4 on that build.
|
|
70
|
+
|
|
71
|
+
**The receiver is this package's Verilog.** It was generated by np2hw,
|
|
72
|
+
which is a generic library; it is `bayerlink_fpga/hdl/bayerlink_in.v` now,
|
|
73
|
+
beside the protocol it implements -- its structure fixed, its capacity,
|
|
74
|
+
output depth, lane order and vsync polarity parameters, which
|
|
75
|
+
`bayerlink_fpga.receiver` works out from a design's settings and its
|
|
76
|
+
front end. The protocol's fourcc table and magic are generated from the
|
|
77
|
+
reference codec as `bayerlink_formats`, so the file restates none of
|
|
78
|
+
them. `tests/` proves it bit-exact against that codec and holds its
|
|
79
|
+
published ports to the file. The `python3 -m bayerlink_fpga` step and
|
|
80
|
+
`frontend.tcl` are gone: what dvi2rgb needs beside a batch run -- its
|
|
81
|
+
EDID data files -- a run copies for every IP that has them.
|
|
82
|
+
|
|
83
|
+
**The receiver is a library a design composes.** `modules.json` describes
|
|
84
|
+
each hand-written module as a block in revela's composite format, by its
|
|
85
|
+
sources, parameters and every port it has; the generated ones publish
|
|
86
|
+
their ports themselves. Each module's clock names the streams it drives
|
|
87
|
+
and its reset, so a block design need not guess either.
|
|
88
|
+
`judge.py` reads the GPIO fields by name from the `gpio_layout.json` built
|
|
89
|
+
beside the bitstream, and refuses a layout whose checksum, built into the
|
|
90
|
+
fabric, does not read back. `vid_probe`, whose word no design read, is
|
|
91
|
+
retired.
|
|
92
|
+
|
|
93
|
+
**The EDID reads bayerlink's rasters.** The advertised timing is the link
|
|
94
|
+
mode's raster from `bayerlink.rasters`, the table the source reads too,
|
|
95
|
+
rather than np2hw's; the bytes are unchanged.
|
|
96
|
+
|
|
97
|
+
## 0.2.1
|
|
98
|
+
|
|
99
|
+
Built with `np2hw==0.6.1`, `bayerlink==0.5.0`, `revela==0.2.0`.
|
|
100
|
+
|
|
101
|
+
**The pinned install line resolves again.** 0.2.0 pinned `np2hw==0.6.0`,
|
|
102
|
+
which was withdrawn from PyPI before it was announced; a burned version
|
|
103
|
+
does not come back, so the documented `pip install` could not complete
|
|
104
|
+
and the reproducible build this repository promises was not reproducible
|
|
105
|
+
by anyone.
|
|
106
|
+
|
|
107
|
+
The same generator ships as np2hw 0.6.1. **The pin moved and the design
|
|
108
|
+
did not**: generating this pipeline under 0.6.0 and under 0.6.1 produces
|
|
109
|
+
a byte-identical pack — Verilog, register map, SystemRDL, FuseSoC core
|
|
110
|
+
and build manifest all compare equal. Between the two releases the only
|
|
111
|
+
code that changed is `ipxact.py`, which emits IP-XACT XML rather than
|
|
112
|
+
Verilog, plus one docstring. So the bitstream measured for 0.2.0 is the
|
|
113
|
+
bitstream this pin builds; nothing about the board result below is
|
|
114
|
+
restated on weaker evidence.
|
|
115
|
+
|
|
116
|
+
## 0.2.0
|
|
117
|
+
|
|
118
|
+
Built with `np2hw==0.6.0`, `bayerlink==0.5.0`, `revela==0.2.0`.
|
|
119
|
+
|
|
120
|
+
Measured on the board: **WNS +0.134 ns MET, WHS +0.050 ns**, TNS and THS
|
|
121
|
+
zero — more margin on both than 0.1.0 had. `rx.bit` and `rx.hwh`
|
|
122
|
+
written, the as-built audit passes on seven addressable slaves, and no
|
|
123
|
+
critical-warning class appears that an earlier build's log did not.
|
|
124
|
+
|
|
125
|
+
**And it ran.** Loaded on the PYNQ-Z2, the bridge restored fifty
|
|
126
|
+
registers over the display cable, the link locked and genlocked, and the
|
|
127
|
+
board produced a correct 1920x1080 picture from a live sensor — the
|
|
128
|
+
first tagged build here proven on glass in the same pass that built it.
|
|
129
|
+
|
|
130
|
+
### Line buffers moved behind a module boundary
|
|
131
|
+
|
|
132
|
+
np2hw 0.6.0 emits each line buffer as its own module rather than an
|
|
133
|
+
inline array, so an integrator targeting silicon can bind a memory macro
|
|
134
|
+
to it by name. On this board it is inert, and that is the point:
|
|
135
|
+
**block RAM 23.5 tiles (22 x RAMB36 + 3 x RAMB18), identical to the tile
|
|
136
|
+
against the previous build**, with LUTs and flip-flops within twenty of
|
|
137
|
+
where they were. A claim about inference that hardware could refute, and
|
|
138
|
+
did not.
|
|
139
|
+
|
|
140
|
+
### The RTL says what its ports are
|
|
141
|
+
|
|
142
|
+
Every AXI4-Lite port and every elastic-stream port now names the bus it
|
|
143
|
+
belongs to, and the block design is given the bus definition that makes
|
|
144
|
+
those names resolvable. `gen/netlist.py` reads those declarations
|
|
145
|
+
instead of matching port names with a regex — the grouping is stated by
|
|
146
|
+
the module that owns the ports rather than inferred by everything
|
|
147
|
+
downstream — and refuses a module that carries a handshake while
|
|
148
|
+
declaring nothing.
|
|
149
|
+
|
|
150
|
+
The six hand-written board modules that speak the protocol declare it
|
|
151
|
+
now. The emitted wiring is unchanged: `netlist.tcl` is byte-identical
|
|
152
|
+
across the whole change.
|
|
153
|
+
|
|
154
|
+
### The build says how much of the part it used
|
|
155
|
+
|
|
156
|
+
`report_utilization` runs after routing and lands beside the bitstream.
|
|
157
|
+
For its whole history this build reported whether it met timing and
|
|
158
|
+
never once how full the device was, which is not a question that can be
|
|
159
|
+
answered later.
|
|
160
|
+
|
|
161
|
+
### Known gaps
|
|
162
|
+
|
|
163
|
+
Unchanged from 0.1.0: nothing runs `sim/`, and the link-judge capture
|
|
164
|
+
path is off by default.
|
|
165
|
+
|
|
166
|
+
## 0.1.0 — first tagged build
|
|
167
|
+
|
|
168
|
+
Built with `np2hw==0.5.1`, `bayerlink==0.5.0`, `revela==0.2.0`.
|
|
169
|
+
|
|
170
|
+
Measured on the board: **WNS +0.047 ns MET, WHS +0.013 ns**, TNS and THS
|
|
171
|
+
zero, `rx.bit` and `rx.hwh` written, and no critical-warning class absent
|
|
172
|
+
from an earlier build's log.
|
|
173
|
+
|
|
174
|
+
### The design
|
|
175
|
+
|
|
176
|
+
- **Three of the four sources are generated and none is committed** — the
|
|
177
|
+
receiver, the ISP and the display raster all come from their models.
|
|
178
|
+
There is no hand-edited Verilog to drift, which is why a build that
|
|
179
|
+
skips a generator fails on a missing file rather than quietly using a
|
|
180
|
+
stale one.
|
|
181
|
+
|
|
182
|
+
- **The ISP is a description.** `gen/pipeline.json` says what the
|
|
183
|
+
pipeline is, independent of any board; `revela` builds it and publishes
|
|
184
|
+
what it traced. The last scripted pipeline is gone.
|
|
185
|
+
|
|
186
|
+
- **The block design's numbers are generated from the files that own
|
|
187
|
+
them.** It used to learn them three ways — environment variables, Tcl
|
|
188
|
+
defaults beside them, and constants in the script — which agreed until
|
|
189
|
+
the depth moved and then did not. What a disagreement produces here is
|
|
190
|
+
not an error but a picture, because Vivado answers a width mismatch
|
|
191
|
+
with a CRITICAL WARNING and a silent connection of the low bits.
|
|
192
|
+
|
|
193
|
+
Three owners now: `gen/pipeline.json` (the design), `board.json` (the
|
|
194
|
+
target) and `<name>_build.json` (what the ISP actually traced, which
|
|
195
|
+
nobody may restate). `bd.tcl` reads no environment and declares no
|
|
196
|
+
defaults.
|
|
197
|
+
|
|
198
|
+
- **The datapath is described, not scripted.** `design.json` names the
|
|
199
|
+
blocks, their widths and their connections; `gen/netlist.py` emits the
|
|
200
|
+
Tcl and checks every width against the modules' own ports first. Every
|
|
201
|
+
instance declares its clock domain, and an undeclared crossing is
|
|
202
|
+
refused.
|
|
203
|
+
|
|
204
|
+
- **Two clock territories**: the cable's, quarantined because it stops
|
|
205
|
+
when the cable is pulled, and the board's. Nothing addressable lives on
|
|
206
|
+
a clock that can stop — a slave with no clock never answers, AXI has no
|
|
207
|
+
timeout, and the first software access after that takes the processor
|
|
208
|
+
with it.
|
|
209
|
+
|
|
210
|
+
- **Display placement is a register**, taken in vertical blanking behind
|
|
211
|
+
an arm/ack handshake, so a frame is drawn with one coherent set or the
|
|
212
|
+
previous one. A window that does not fit is refused rather than
|
|
213
|
+
clipped.
|
|
214
|
+
|
|
215
|
+
- **A line wider than the pack was built for is cropped, not
|
|
216
|
+
reinterpreted** — visibly, with a sticky `truncated`. A narrower
|
|
217
|
+
picture can be recognised and tuned; a refused one is a black screen
|
|
218
|
+
indistinguishable from a dead cable.
|
|
219
|
+
|
|
220
|
+
### Checks that run before Vivado does
|
|
221
|
+
|
|
222
|
+
`checkbd`, `checklicence` and `checknets` run in seconds, against twenty
|
|
223
|
+
minutes of synthesis. `checknets` compares every wire drawn between our
|
|
224
|
+
own blocks against the widths those blocks actually have — every bug of
|
|
225
|
+
that shape this repository has had lived in exactly that gap. `checkhwh`
|
|
226
|
+
then audits the design **as built**, because the intent and the
|
|
227
|
+
automation's result are different documents.
|
|
228
|
+
|
|
229
|
+
### Known gaps
|
|
230
|
+
|
|
231
|
+
- Nothing runs `sim/`: the four testbenches are launched by hand from a
|
|
232
|
+
`// Run:` comment in each. Two had rotted unnoticed before this
|
|
233
|
+
release and are fixed here.
|
|
234
|
+
- The link-judge capture path is off by default (`capture: false`); it is
|
|
235
|
+
an episodic instrument, not a daily tax.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
SOLDERPAD HARDWARE LICENSE VERSION 2.1
|
|
2
|
+
|
|
3
|
+
This license operates as a wraparound license to the Apache License Version 2.0 (the "Apache License") and incorporates the terms and conditions of the Apache License (which can be found here: http://apache.org/licenses/LICENSE-2.0), with the following additions and modifications. It must be read in conjunction with the Apache License. Section 1 below modifies definitions and terminology in the Apache License and Section 2 below replaces Section 2 of the Apache License. The Appendix replaces the Appendix in the Apache License. You may, at your option, choose to treat any Work released under this license as released under the Apache License (thus ignoring all sections written below entirely).
|
|
4
|
+
|
|
5
|
+
1. Terminology in the Apache License is supplemented or modified as follows:
|
|
6
|
+
|
|
7
|
+
"Authorship": any reference to 'authorship' shall be taken to read "authorship or design".
|
|
8
|
+
|
|
9
|
+
"Copyright owner": any reference to 'copyright owner' shall be taken to read "Rights owner".
|
|
10
|
+
|
|
11
|
+
"Copyright statement": the reference to 'copyright statement' shall be taken to read 'copyright or other statement pertaining to Rights'
|
|
12
|
+
|
|
13
|
+
The following new definition shall be added to the Definitions section of the Apache License:
|
|
14
|
+
|
|
15
|
+
"Rights" means copyright and any similar right including design right (whether registered or unregistered), rights in semiconductor topographies (mask works) and database rights (but excluding Patents and Trademarks).
|
|
16
|
+
|
|
17
|
+
The following definitions shall replace the corresponding definitions in the Apache License:
|
|
18
|
+
|
|
19
|
+
"License" shall mean this Solderpad Hardware License version 2.1, being the terms and conditions for use, manufacture, instantiation, adaptation, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
|
20
|
+
|
|
21
|
+
"Licensor" shall mean the Rights owner or entity authorized by the Rights owner that is granting the License.
|
|
22
|
+
|
|
23
|
+
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship or design. For the purposes of this License, Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the Work and Derivative Works thereof.
|
|
24
|
+
|
|
25
|
+
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object or material and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works).
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files.
|
|
28
|
+
|
|
29
|
+
"Work" shall mean the work of authorship or design, whether in Source or Object form, made available under the License, as indicated by a notice relating to Rights that is included in or attached to the work (an example is provided in the Appendix below).
|
|
30
|
+
|
|
31
|
+
2. Grant of License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under the Rights to reproduce, prepare Derivative Works of, make, adapt, repair, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form and do anything in relation to the Work as if the Rights did not exist.
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
APPENDIX
|
|
35
|
+
|
|
36
|
+
Copyright [yyyy] [name of copyright owner]
|
|
37
|
+
SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
|
38
|
+
|
|
39
|
+
Licensed under the Solderpad Hardware License v 2.1 (the "License"); you may not use this file except in compliance with the License, or, at your option, the Apache License version 2.0.
|
|
40
|
+
You may obtain a copy of the License at
|
|
41
|
+
|
|
42
|
+
https://solderpad.org/licenses/SHL-2.1/
|
|
43
|
+
|
|
44
|
+
Unless required by applicable law or agreed to in writing, any work distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
45
|
+
See the License for the specific language governing permissions and limitations under the License.
|