diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..6152ad93 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,55 @@ +// For format details, see https://containers.dev/implementors/json_reference/ +{ + "name": "PandABlocks-server Developer Container", + "build": { + "dockerfile": "../Dockerfile", + "target": "developer" + }, + "remoteEnv": { + // Allow X11 apps to run inside the container + "DISPLAY": "${localEnv:DISPLAY}", + // Put things that allow it in the persistent cache + "PRE_COMMIT_HOME": "/cache/pre-commit", + "UV_CACHE_DIR": "/cache/uv", + "UV_PYTHON_CACHE_DIR": "/cache/uv-python", + }, + "customizations": { + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + // Workaround to prevent garbled python REPL in the terminal + // https://github.com/microsoft/vscode-python/issues/25505 + "python.terminal.shellIntegration.enabled": false + }, + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-python.python", + "ms-azuretools.vscode-docker" + ] + } + }, + // Create the config folder for the bash-config feature and uv cache + "initializeCommand": "mkdir -p ${localEnv:HOME}/.config/terminal-config", + "runArgs": [ + // Allow the container to access the host X11 display and EPICS CA + "--net=host", + // Make sure SELinux does not disable with access to host filesystems like tmp + "--security-opt=label=disable" + ], + "mounts": [ + // Mount in the user terminal config folder so it can be edited + { + "source": "${localEnv:HOME}/.config/terminal-config", + "target": "/user-terminal-config", + "type": "bind" + }, + // Keep a persistent cross container cache for uv, pre-commit, and the venvs + { + "source": "devcontainer-shared-cache", + "target": "/cache", + "type": "volume" + } + ], + // Mount the parent as /workspaces so we can pip install peers as editable + "workspaceMount": "source=${localWorkspaceFolder}/..,target=/workspaces,type=bind", +} diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..6eaa9eee --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,17 @@ +# Contributing + +Contributions and issues are most welcome! All issues and pull requests are handled +through [GitHub](https://github.com/PandABlocks/PandABlocks-server/issues). Please check +for any existing issues before filing a new one. + +## Issue or Pull Request? + +Raise an issue if you want to report a bug or request a feature. Open a pull request if +you already have a change ready to propose. If you are unsure, raise an issue first to +discuss the change. + +## Documentation + +Documentation is built with [MyST](https://mystmd.org) and published to GitHub Pages. +The docs follow the [Diátaxis](https://diataxis.fr) framework — tutorials, how-to +guides, reference and explanations. diff --git a/.github/pages/index.html b/.github/pages/index.html deleted file mode 100644 index cc33127d..00000000 --- a/.github/pages/index.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - Redirecting to master branch - - - - - diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..4517592d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,67 @@ +name: Docs CI + +# Build + verify the docs on every event, then publish on INTERNAL events. The +# build (the shared `docs.yml`) runs for PRs (including forks), pushes to main, and +# tags, and uploads each build's `docs` artifact. Publishing is nested here (the +# `publish` job -> the shared `publish.yml`) so its status is visible on the +# PR/commit, but ONLY for internal events on this repo: a fork PR's build runs with a +# read-only token and must never deploy. +# +# Both halves are consumed from DiamondLightSource/myst-version-switcher-plugin at a +# pinned tag — this repo carries no docs-build/publish workflow of its own. +# publish.yml reconstructs the whole versioned site from durable sources (main's +# latest build persisted at _sources/main.zip, each release's docs.zip asset, every +# open PR's build artifact) and deploys it to Pages directly; there is no gh-pages +# branch. +on: + pull_request: + push: + branches: [main] + tags: ['*'] # '*' never matches '/' + +jobs: + docs: + uses: DiamondLightSource/myst-version-switcher-plugin/.github/workflows/docs.yml@v0.7.0 + with: + # `make docs` drives npx mystmd (pinned by MYSTMD_VERSION) and reads build + # settings from CONFIG, so create it first. eval runs the compound command. + build-command: cp CONFIG.example CONFIG && make docs + # The fork-PR warning links here so a maintainer can publish a fork preview. + preview-workflow: preview-fork.yml + + # Tag-only: attach this build's docs.zip (bare html/ root) to the GitHub Release + # so `assemble` can reconstruct that released version on future deploys. + docs-release: + needs: [docs] + if: github.ref_type == 'tag' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/download-artifact@v4 + with: + name: docs + - env: + GH_TOKEN: ${{ github.token }} + run: gh release upload "${{ github.ref_name }}" docs.zip --clobber --repo "${{ github.repository }}" + + # Internal events only: an internal PR, or a push to main/tag, has a same-repo + # build we can trust + deploy. Fork PRs (head repo != this repo) are excluded — + # docs.yml's build job warns them instead. publish.yml defaults guard-default-branch + # to true, so every deploy asserts main is present and can never silently drop + # /main/ (main is self-durable via _sources/main.zip). + publish: + needs: [docs] + if: >- + github.repository == 'PandABlocks/PandABlocks-server' && + ( github.event_name != 'pull_request' || + github.event.pull_request.head.repo.full_name == github.repository ) + uses: DiamondLightSource/myst-version-switcher-plugin/.github/workflows/publish.yml@v0.7.0 + with: + version-name: ${{ needs.docs.outputs.version-name }} + permissions: + contents: read + actions: read + pages: write + id-token: write + statuses: write diff --git a/.github/workflows/code.yml b/.github/workflows/code.yml index 103ec395..872a6335 100644 --- a/.github/workflows/code.yml +++ b/.github/workflows/code.yml @@ -42,7 +42,7 @@ jobs: cd PandABlocks-rootfs ln -s CONFIG.example CONFIG make kernel PLATFORM=${{matrix.platform}} - + - name: Make zpkg run: | cd PandABlocks-server diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index c5227cd3..00000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Docs CI - -on: - push: - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - # Use container from ghcr - container: - image: ghcr.io/pandablocks/pandablocks-dev-container:latest - credentials: - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - volumes: - - ${{ github.workspace }}:/repos - - ${{ github.workspace }}/build:/build - - steps: - # Git repositories - - name: Checkout Source - uses: actions/checkout@v2 - with: - path: PandABlocks-server - # require history to get back to last tag for version number of branches - fetch-depth: 0 - - # Build - - name: Build Docs - run: | - cd PandABlocks-server - ln -s CONFIG.example CONFIG - make docs - - # Verification of build files - #- name: List build files - # run: ls ${{ github.workspace }}/build/html - - # Publish - - name: Move to versioned directory - # e.g. master or 0.1.2 - run: mv build/build-server/html "PandABlocks-server/.github/pages/${GITHUB_REF##*/}" - - - name: Publish Docs to gh-pages - # Only master and tags are published - if: "${{ github.repository_owner == 'PandABlocks' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')) }}" - # We pin to the SHA, not the tag, for security reasons. - # https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions - uses: peaceiris/actions-gh-pages@bbdfb200618d235585ad98e965f4aafc39b4c501 # v3.7.3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: PandABlocks-server/.github/pages - keep_files: true \ No newline at end of file diff --git a/.github/workflows/preview-fork.yml b/.github/workflows/preview-fork.yml new file mode 100644 index 00000000..fbdad8ae --- /dev/null +++ b/.github/workflows/preview-fork.yml @@ -0,0 +1,31 @@ +name: Preview fork PR + +# Maintainer opt-in to publish a versioned docs preview for an EXTERNAL fork PR. +# Fork PRs build + verify in Docs CI but never auto-publish (their build runs with a +# read-only token), so this is the only path that deploys one. A reusable workflow +# can't be workflow_dispatch'd cross-repo, so this thin wrapper lives here and passes +# the PR number through to the shared publish.yml via workflow_call. +# +# It approves the fork PR's CURRENT head SHA (a `preview-approved` commit status) and +# assembles the site with that PR's artifact included. A later push to the PR (new +# SHA) drops the preview until you re-run this. version-name is empty: there is no +# in-run build to inject, so publish.yml does a pure durable gather. +on: + workflow_dispatch: + inputs: + pr: + description: Fork PR number to approve + preview. + required: true + +jobs: + preview: + uses: DiamondLightSource/myst-version-switcher-plugin/.github/workflows/publish.yml@v0.7.0 + with: + version-name: "" + pr: ${{ inputs.pr }} + permissions: + contents: read + actions: read + pages: write + id-token: write + statuses: write diff --git a/.gitignore b/.gitignore index 6677454f..39686957 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ *.pyc tests/fpga_sequences /venv + +# MyST build output +docs/_build/ diff --git a/CONFIG.example b/CONFIG.example index 7dfaac73..e160582e 100644 --- a/CONFIG.example +++ b/CONFIG.example @@ -15,9 +15,11 @@ BUILD_DIR = /build/build-server # PYTHON = python3 -# Sphinx build for documentation. +# Version of mystmd (https://mystmd.org) used to build the documentation with +# `make docs` / `make docs-dev`. Run on demand via npx, so no global install is +# needed. # -SPHINX_BUILD = sphinx-build +MYSTMD_VERSION = 1.10.1 # Compiler settings. Default cross compile prefix given here. If BINUTILS_DIR # is specified it will be prepended to the path for building the driver and diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..991306b4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +# The devcontainer should use the developer target and run as root with podman +# or docker with user namespaces. +FROM ghcr.io/diamondlightsource/ubuntu-devcontainer:resolute AS developer + +# Add any system dependencies for the developer/build environment here. +# Candidates: an ARMv7-A cross-compiler toolchain for on-PandA builds; the +# native toolchain below is enough for the simulation server and docs. +RUN apt-get update -y && apt-get install -y --no-install-recommends \ + npm \ + && apt-get dist-clean diff --git a/Makefile b/Makefile index 19461b05..04f3b8a8 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ TOP := $(CURDIR) BUILD_DIR = $(TOP)/build PYTHON = python3 -SPHINX_BUILD = sphinx-build +MYSTMD_VERSION = 1.10.1 COMPILER_PREFIX = $(COMPILER_PREFIX_$(PLATFORM)) KERNEL_DIR = $(error Define KERNEL_DIR in CONFIG file) PANDA_ROOTFS = $(error Define PANDA_ROOTFS in CONFIG file) @@ -140,16 +140,33 @@ slow_load: $(SLOW_LOAD) # ------------------------------------------------------------------------------ # Documentation +# +# Docs are built with MyST (mystmd), run through npx so no global install is +# needed; pin the version with MYSTMD_VERSION (see CONFIG.example). `make docs` +# mirrors `myst build --html --strict` and `make docs-dev` mirrors `myst start`, +# matching the docs/ task runner. MyST writes its output into docs/_build/html. +# --strict exits non-zero on any error-severity message (e.g. an unresolved +# cross-repo xref) so CI fails rather than publishing broken links. -$(DOCS_BUILD_DIR)/index.html: $(wildcard docs/*.rst docs/*/*.rst docs/conf.py) - $(SPHINX_BUILD) -b html docs $(DOCS_BUILD_DIR) +MYST = npx --yes --package mystmd@$(MYSTMD_VERSION) myst -docs: $(DOCS_BUILD_DIR)/index.html +docs: + cd docs && $(MYST) build --html --strict + +docs-dev: + cd docs && $(MYST) start clean-docs: - rm -rf $(DOCS_BUILD_DIR) + rm -rf $(TOP)/docs/_build -.PHONY: docs clean-docs +.PHONY: docs docs-dev clean-docs + +# The zpkg packaging installs the built docs from $(DOCS_BUILD_DIR) (the `html` +# entry in etc/panda-server.list), so stage the MyST output there for zpkg. +$(DOCS_BUILD_DIR)/index.html: docs + rm -rf $(DOCS_BUILD_DIR) + mkdir -p $(DOCS_BUILD_DIR) + cp -r $(TOP)/docs/_build/html/. $(DOCS_BUILD_DIR) # ------------------------------------------------------------------------------ diff --git a/README.md b/README.md new file mode 100644 index 00000000..6506d048 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# PandABlocks-server + +The PandA socket server provides a bridge between the register interface to the +FPGA firmware controlling the PandA hardware and users and other software. The +interface is designed to be simple and robust. + +The PandA firmware is structured into numerous functional blocks, each +configured via a number of fields. This structure is directly reflected in the +functional interface provided by this server: most commands read or write +specific fields. + +The server publishes two socket end points: one for configuration control +(simple ASCII commands, ASCII responses) and one for streamed data capture +(no commands, a lightly structured binary stream). + + + +## Where to find things + +| | | +|---|---| +| Source code | | +| Documentation | | +| Releases | | diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md new file mode 100644 index 00000000..dd316dfb --- /dev/null +++ b/docs/PROGRESS.md @@ -0,0 +1,72 @@ +# Docs Rewrite Progress — PandABlocks-server + +Tracks every target page for this repo. Update the relevant line **in the same commit** as the file +it refers to. Statuses: ☐ todo · ◐ stub · ✅ converted · 🔍 needs-review · ⛔ blocked (→ issue #). +Page list expanded from `06-source-provenance-map.md`. + +## Stage A — scaffold +- ✅ skeleton instantiated (title/github/logo swapped, apidoc + pip-tutorial + run-container removed) +- ✅ TOC + stubs for every target page present (12 stubs + 4 landing + index) +- ✅ `index.md` README-include + `how-to/contribute` CONTRIBUTING-include resolve +- ✅ xref/intersphinx wired + cross-link probe resolves in built output (reference/capture → PandABlocks-client) +- ☐ (FPGA only) — N/A +- ✅ `myst build` green (exit 0, no warnings/errors) +- ✅ Pages deploy wired — `.github/workflows/docs.yml` switched to `myst build` (copier-aligned) + +## Tutorials +- (none in v4 — empty `tutorials.md` landing kept for parity) + +## How-to +- ✅ how-to/startup — converted from startup.rst (Prompt D) +- ✅ how-to/building — converted from building.rst; devcontainer pointer added; CONFIG syntax note = verify (Prompt D) +- ✅ how-to/contribute — `{include} .github/CONTRIBUTING.md` — scaffold include resolves + +## Explanations +- ✅ explanations/architecture — skeleton authored from server code (Prompt D); depth = blocked: author + +## Reference +- ✅ reference/commands — source: commands.rst — converted (Prompt E); Configuration + System command tables preserved, *IDN? kept; *CAPTURE.OPTIONS? points to capture-options +- ✅ reference/fields — source: fields.rst — converted (Prompt E); MODE matrix + Summary-of-Sub-Types + Summary-of-Attributes tables kept; table-streaming detail points to reference/streaming-tables +- ✅ reference/capture — source: capture.rst — converted (Prompt E); ~60 MB/s + "not installed" webcontrol caveat kept; Capture Options extracted to capture-options; data-out links meta-panda integrate-with-a-panda; holds the Stage A client xref probe +- ✅ reference/capture-options — source: capture.rst ⊃ + Interview5 §4 — written (Prompt E); field + connection options; "run *CAPTURE.OPTIONS? live" note added; superset still blocked: verify +- ✅ reference/config — converted from config.rst (Prompt D) +- ⛔ reference/extension — source: extension.rst — blocked: verify +- ✅ reference/streaming-tables — authored from Interview5 §8 + fields.rst streaming section (Prompt D) +- ✅ reference/support — converted from support.rst (Prompt D) +- ⛔ reference/c-standard — source: NEW (Interview1 §4) — blocked: author + +## Blocked (issues raised) +Issues not yet created — Stage B (Prompt B) will create and link them. +- ⛔ reference/extension — issue #80 — verify +- ⛔ reference/c-standard — issue #83 — author + +- ⛔ reference/capture-options (live confirmation) — issue #78 — verify + +- ⛔ reference/capture (60 MB/s figure) — issue #79 — verify + +- ⛔ reference/commands (*IDN? v4.x) — issue #81 — verify + +- ⛔ how-to/building (CONFIG syntax) — issue #82 — verify + +- ⛔ explanations/architecture (depth) — issue #84 — author + +## Notes +- **Legacy sources preserved.** Original Sphinx/RST tree moved to `docs/_legacy_rst/` (not in TOC, + ignored by `myst build`) so Stage D/E conversion can read sources in-tree; also on `origin/main`. + `presentation-2016-05/*` is dropped per 06 (kept under `_legacy_rst/` for now; delete with the rest + of `_legacy_rst/` once conversion is complete). +- **Default branch is `main`** — publish gated on `main`/tags. The root `index.html` redirect is + generated by the version-switcher `switcher` action: it points at the preferred (newest stable + tag) version, falling back to `main` when no stable tag is deployed. +- **xref/intersphinx prototype.** Only `PandABlocks-client` (deployed, Sphinx, stays on /main) is + active; the probe in `reference/capture` resolves in built output. meta-panda + FPGA + fastcs kept + commented until published; uncomment in Stage F and upstream into python-copier-template. Interim + inventory target for those MyST repos is each repo's `docs`-branch build (`/docs/objects.inv`), + switching to `/stable` once the upstream switcher action publishes a `stable` symlink to the + latest release (Pages tars with `--dereference`, so the symlink materialises if its target version + is retained). +- **CI** mirrors python-copier-template-example `_docs.yml` (npm+mystmd build, upload-artifact of + `docs/_build` minus the templates cache, versioned move, peaceiris v4). The switcher.json + root + redirect are written by the `DiamondLightSource/myst-version-switcher-plugin/switcher@v0.2.0` + action; the in-page dropdown is the matching `version-switcher` MyST plugin (release-asset `.mjs` + in `myst.yml` `plugins`, rendered via `navbar_end.md`). diff --git a/docs/PandA-logo-for-black-background.svg b/docs/_legacy_rst/PandA-logo-for-black-background.svg similarity index 100% rename from docs/PandA-logo-for-black-background.svg rename to docs/_legacy_rst/PandA-logo-for-black-background.svg diff --git a/docs/_static/theme_overrides.css b/docs/_legacy_rst/_static/theme_overrides.css similarity index 100% rename from docs/_static/theme_overrides.css rename to docs/_legacy_rst/_static/theme_overrides.css diff --git a/docs/_templates/layout.html b/docs/_legacy_rst/_templates/layout.html similarity index 100% rename from docs/_templates/layout.html rename to docs/_legacy_rst/_templates/layout.html diff --git a/docs/building.rst b/docs/_legacy_rst/building.rst similarity index 100% rename from docs/building.rst rename to docs/_legacy_rst/building.rst diff --git a/docs/capture.rst b/docs/_legacy_rst/capture.rst similarity index 100% rename from docs/capture.rst rename to docs/_legacy_rst/capture.rst diff --git a/docs/commands.rst b/docs/_legacy_rst/commands.rst similarity index 100% rename from docs/commands.rst rename to docs/_legacy_rst/commands.rst diff --git a/docs/conf.py b/docs/_legacy_rst/conf.py similarity index 100% rename from docs/conf.py rename to docs/_legacy_rst/conf.py diff --git a/docs/config.rst b/docs/_legacy_rst/config.rst similarity index 100% rename from docs/config.rst rename to docs/_legacy_rst/config.rst diff --git a/docs/extension.rst b/docs/_legacy_rst/extension.rst similarity index 100% rename from docs/extension.rst rename to docs/_legacy_rst/extension.rst diff --git a/docs/favicon.ico b/docs/_legacy_rst/favicon.ico similarity index 100% rename from docs/favicon.ico rename to docs/_legacy_rst/favicon.ico diff --git a/docs/fields.rst b/docs/_legacy_rst/fields.rst similarity index 100% rename from docs/fields.rst rename to docs/_legacy_rst/fields.rst diff --git a/docs/index.rst b/docs/_legacy_rst/index.rst similarity index 100% rename from docs/index.rst rename to docs/_legacy_rst/index.rst diff --git a/docs/presentation-2016-05/.gitignore b/docs/_legacy_rst/presentation-2016-05/.gitignore similarity index 100% rename from docs/presentation-2016-05/.gitignore rename to docs/_legacy_rst/presentation-2016-05/.gitignore diff --git a/docs/presentation-2016-05/Makefile b/docs/_legacy_rst/presentation-2016-05/Makefile similarity index 100% rename from docs/presentation-2016-05/Makefile rename to docs/_legacy_rst/presentation-2016-05/Makefile diff --git a/docs/presentation-2016-05/building.rst b/docs/_legacy_rst/presentation-2016-05/building.rst similarity index 100% rename from docs/presentation-2016-05/building.rst rename to docs/_legacy_rst/presentation-2016-05/building.rst diff --git a/docs/presentation-2016-05/docutils.conf b/docs/_legacy_rst/presentation-2016-05/docutils.conf similarity index 100% rename from docs/presentation-2016-05/docutils.conf rename to docs/_legacy_rst/presentation-2016-05/docutils.conf diff --git a/docs/presentation-2016-05/images/admin.png b/docs/_legacy_rst/presentation-2016-05/images/admin.png similarity index 100% rename from docs/presentation-2016-05/images/admin.png rename to docs/_legacy_rst/presentation-2016-05/images/admin.png diff --git a/docs/presentation-2016-05/images/architecture.png b/docs/_legacy_rst/presentation-2016-05/images/architecture.png similarity index 100% rename from docs/presentation-2016-05/images/architecture.png rename to docs/_legacy_rst/presentation-2016-05/images/architecture.png diff --git a/docs/presentation-2016-05/images/software.png b/docs/_legacy_rst/presentation-2016-05/images/software.png similarity index 100% rename from docs/presentation-2016-05/images/software.png rename to docs/_legacy_rst/presentation-2016-05/images/software.png diff --git a/docs/presentation-2016-05/images/system.png b/docs/_legacy_rst/presentation-2016-05/images/system.png similarity index 100% rename from docs/presentation-2016-05/images/system.png rename to docs/_legacy_rst/presentation-2016-05/images/system.png diff --git a/docs/presentation-2016-05/panda.rst b/docs/_legacy_rst/presentation-2016-05/panda.rst similarity index 100% rename from docs/presentation-2016-05/panda.rst rename to docs/_legacy_rst/presentation-2016-05/panda.rst diff --git a/docs/presentation-2016-05/server.rst b/docs/_legacy_rst/presentation-2016-05/server.rst similarity index 100% rename from docs/presentation-2016-05/server.rst rename to docs/_legacy_rst/presentation-2016-05/server.rst diff --git a/docs/presentation-2016-05/styles/blank.gif b/docs/_legacy_rst/presentation-2016-05/styles/blank.gif similarity index 100% rename from docs/presentation-2016-05/styles/blank.gif rename to docs/_legacy_rst/presentation-2016-05/styles/blank.gif diff --git a/docs/presentation-2016-05/styles/dlsfooterpad.png b/docs/_legacy_rst/presentation-2016-05/styles/dlsfooterpad.png similarity index 100% rename from docs/presentation-2016-05/styles/dlsfooterpad.png rename to docs/_legacy_rst/presentation-2016-05/styles/dlsfooterpad.png diff --git a/docs/presentation-2016-05/styles/framing.css b/docs/_legacy_rst/presentation-2016-05/styles/framing.css similarity index 100% rename from docs/presentation-2016-05/styles/framing.css rename to docs/_legacy_rst/presentation-2016-05/styles/framing.css diff --git a/docs/presentation-2016-05/styles/html4css1.css b/docs/_legacy_rst/presentation-2016-05/styles/html4css1.css similarity index 100% rename from docs/presentation-2016-05/styles/html4css1.css rename to docs/_legacy_rst/presentation-2016-05/styles/html4css1.css diff --git a/docs/presentation-2016-05/styles/iepngfix.htc b/docs/_legacy_rst/presentation-2016-05/styles/iepngfix.htc similarity index 100% rename from docs/presentation-2016-05/styles/iepngfix.htc rename to docs/_legacy_rst/presentation-2016-05/styles/iepngfix.htc diff --git a/docs/presentation-2016-05/styles/opera.css b/docs/_legacy_rst/presentation-2016-05/styles/opera.css similarity index 100% rename from docs/presentation-2016-05/styles/opera.css rename to docs/_legacy_rst/presentation-2016-05/styles/opera.css diff --git a/docs/presentation-2016-05/styles/outline.css b/docs/_legacy_rst/presentation-2016-05/styles/outline.css similarity index 100% rename from docs/presentation-2016-05/styles/outline.css rename to docs/_legacy_rst/presentation-2016-05/styles/outline.css diff --git a/docs/presentation-2016-05/styles/pretty.css b/docs/_legacy_rst/presentation-2016-05/styles/pretty.css similarity index 100% rename from docs/presentation-2016-05/styles/pretty.css rename to docs/_legacy_rst/presentation-2016-05/styles/pretty.css diff --git a/docs/presentation-2016-05/styles/print.css b/docs/_legacy_rst/presentation-2016-05/styles/print.css similarity index 100% rename from docs/presentation-2016-05/styles/print.css rename to docs/_legacy_rst/presentation-2016-05/styles/print.css diff --git a/docs/presentation-2016-05/styles/pygments.css b/docs/_legacy_rst/presentation-2016-05/styles/pygments.css similarity index 100% rename from docs/presentation-2016-05/styles/pygments.css rename to docs/_legacy_rst/presentation-2016-05/styles/pygments.css diff --git a/docs/presentation-2016-05/styles/s5-core.css b/docs/_legacy_rst/presentation-2016-05/styles/s5-core.css similarity index 100% rename from docs/presentation-2016-05/styles/s5-core.css rename to docs/_legacy_rst/presentation-2016-05/styles/s5-core.css diff --git a/docs/presentation-2016-05/styles/slides.css b/docs/_legacy_rst/presentation-2016-05/styles/slides.css similarity index 100% rename from docs/presentation-2016-05/styles/slides.css rename to docs/_legacy_rst/presentation-2016-05/styles/slides.css diff --git a/docs/presentation-2016-05/styles/slides.js b/docs/_legacy_rst/presentation-2016-05/styles/slides.js similarity index 100% rename from docs/presentation-2016-05/styles/slides.js rename to docs/_legacy_rst/presentation-2016-05/styles/slides.js diff --git a/docs/startup.rst b/docs/_legacy_rst/startup.rst similarity index 100% rename from docs/startup.rst rename to docs/_legacy_rst/startup.rst diff --git a/docs/support.rst b/docs/_legacy_rst/support.rst similarity index 100% rename from docs/support.rst rename to docs/_legacy_rst/support.rst diff --git a/docs/explanations.md b/docs/explanations.md new file mode 100644 index 00000000..a1dca5ac --- /dev/null +++ b/docs/explanations.md @@ -0,0 +1,7 @@ +# Explanations + +Explanations of how it works and why it works that way. + +```{toc} +:context: children +``` diff --git a/docs/explanations/architecture.md b/docs/explanations/architecture.md new file mode 100644 index 00000000..ea86d78f --- /dev/null +++ b/docs/explanations/architecture.md @@ -0,0 +1,55 @@ +# Server architecture + +:::{admonition} Skeleton — depth coming later +:class: note + +This page provides an orientation skeleton. Detailed internals (locking +strategy, DMA pipeline, persistence state machine) are tracked in the issue +backlog as **blocked: author**. +::: + +The PandABlocks server is a C daemon that presents two TCP sockets and bridges +them to the PandA FPGA hardware (or a software simulation of it). + +## Sockets + +**Configuration port (default 8888)** +: An ASCII command/response interface used to read and write block fields and + execute system commands. Described in [](/reference/commands.md). + +**Data port (default 8889)** +: A binary streaming interface for captured experiment data. Described in + [](/reference/capture.md). + +## Internal structure + +The server is organised around three cooperating layers. + +**Configuration thread** (`config_server.c`) +: Accepts connections on the configuration port. Parses incoming ASCII commands, + dispatches them to the block/field database (`config_command.c`, `fields.c`), + and formats responses. One thread per client connection. + +**Data thread** (`data_server.c`) +: Accepts connections on the data port. Reads captured data from the central + circular buffer and streams it to connected clients. Handles framing, + base-64 encoding, and metadata headers. + +**Hardware layer** (`hardware.c` / `sim_hardware.c`) +: Abstracts register reads/writes and DMA transfers to the FPGA. In simulation + mode (`sim_hardware.c`) the hardware layer is replaced by an in-process + emulator. + +## Block model + +The server loads its block and field topology from the `config_d` configuration +files at startup (see [](/reference/config.md)). Each block is a named, +potentially multi-instance hardware component (e.g. `PULSE[4]`). Fields within +a block map to registers and expose typed operations (read, write, capture, etc.) +as documented in [](/reference/fields.md). + +## Persistence + +Field values that survive a server restart are written to a persistence file +(see the `-f` and `-t` options in [](/how-to/startup.md)). The file is updated +on a poll/holdoff/backoff schedule to reduce write pressure. diff --git a/docs/how-to.md b/docs/how-to.md new file mode 100644 index 00000000..ba801fd7 --- /dev/null +++ b/docs/how-to.md @@ -0,0 +1,7 @@ +# How-to Guides + +Practical step-by-step guides for the more experienced user. + +```{toc} +:context: children +``` diff --git a/docs/how-to/building.md b/docs/how-to/building.md new file mode 100644 index 00000000..02e049bb --- /dev/null +++ b/docs/how-to/building.md @@ -0,0 +1,77 @@ +# Build and test the server + +:::{tip} +For local development the devcontainer defined in this repository +(`.devcontainer/devcontainer.json` and the top-level `Dockerfile`) provides a +pre-configured environment and is the recommended starting point. The +instructions below cover a native build for those who need it. +::: + +## Dependencies + +The following must be available before building the server. + +**Zynq cross-compiler toolchain** +: Available as part of the Xilinx Vivado build environment, or any ARMv7-A + cross-compiler. Required for all applications running on PandA. + +**PandABlocks-FPGA** +: Must be available before building the server; it contains the configuration + file defining the low-level register interface to the PandA firmware. + +## Setting up the `CONFIG` file + +Copy `CONFIG.example` to `CONFIG` in the base directory and edit as needed, +commenting out lines that are not required. + +:::{note} +The exact syntax accepted by the server Makefile for `CONFIG` values is pending +verification (tracked in the issue backlog). Use the form shown in +`CONFIG.example`. +::: + +The following symbols must point to the appropriate dependencies: + +`BINUTILS_DIR` +: Path to the Zynq cross-compiler toolchain if it is not on `PATH`. Not required + for the simulation server or documentation builds. + +The following symbols can be left at their defaults: + +`BUILD_DIR` +: Path for built files. + +`PYTHON` +: Path for python interpreter used during the build + +`MYSTMD_VERSION` +: `mystmd version No.` Version of MyST required to build docs. defaults to 1.10.1. + +`DEFAULT_TARGETS` +: Makefile targets built by `make` or `make default`. Default list: `driver`, + `server`, `sim_server`, `docs`. + +## Build targets + +| Target | Description | +|--------------|----------------------------------------------------------| +| `default` | Builds all targets listed in `$(DEFAULT_TARGETS)` | +| `driver` | Kernel driver module | +| `server` | Server binary to run on PandA | +| `sim_server` | Simulation server to run on the local PC | +| `docs` | HTML documentation | +| `clean` | Removes the entire `$(BUILD_DIR)` directory | + +## Generated files + +After a successful build, `$(BUILD_DIR)` contains: + +`driver/` +: Kernel module for hardware access. + +`server/` and `sim_server/` +: The on-PandA server and the local simulation server respectively. + +`html/` +: HTML documentation. + diff --git a/docs/how-to/contribute.md b/docs/how-to/contribute.md new file mode 100644 index 00000000..6e419797 --- /dev/null +++ b/docs/how-to/contribute.md @@ -0,0 +1,2 @@ +```{include} ../../.github/CONTRIBUTING.md +``` diff --git a/docs/how-to/startup.md b/docs/how-to/startup.md new file mode 100644 index 00000000..f72ba4d7 --- /dev/null +++ b/docs/how-to/startup.md @@ -0,0 +1,63 @@ +# Start the PandA server + +The PandA socket server normally starts automatically at boot time or when the +`zpkg-daemon` script is run. It is started and stopped by the script +`etc/panda-server` installed at `/opt/etc/init.d`. + +The server can also be started from the command line with the following options. + +`-h` +: Display help text listing all available command-line options. + +`-p` *port* +: TCP port for configuration commands. Default: `8888`. + +`-d` *port* +: TCP port for data capture. Default: `8889`. + +`-R` +: Allow socket reuse via the `SO_REUSEADDR` socket option. + +`-c` *config-dir* +: Directory from which the `config`, `registers`, and `description` files are + loaded. **This argument must be specified.** + +`-f` *persistence-file* +: File from which persistence state is loaded on startup and to which it is saved + during operation. If not specified, persistence state is not saved. + +`-t` *[poll][:holdoff[:backoff]]* +: Three parameters (in seconds) controlling writes to the persistence file. + + Every *poll* seconds the server checks for configuration changes. If a change + is found, it waits a further *holdoff* seconds before writing. It then waits + *backoff* seconds before resuming polling. + + Defaults: `poll` = 2, `holdoff` = 10, `backoff` = 60. With these defaults all + parameters reach the persistence file within 72 seconds. + + The colon syntax lets you set any subset: for example `-t:20` sets *holdoff* = 20 + while leaving *poll* and *backoff* unchanged. + +`-D` +: Run the server as a daemon. This is the normal production mode; omit for + debugging. + +`-P` *pid-file* +: Write the server process ID to the given file; the file is deleted on exit. + +`-T` +: Config-file validation mode: the server exits immediately after loading + configuration files without accepting connections. + +`-M` *MAC-list* +: Initialise up to four MAC address registers from the given file. The file may + contain comment lines (starting with `#`) and up to four MAC address lines, + each either blank or a six-octet address in `XX:XX:XX:XX:XX:XX` format. + +`-X` *port* +: Connect to an extension server running locally on the specified port. See + [](/reference/extension.md) for details. + +`-r` *rootfs-version* +: Specify the rootfs version string reported by the `*IDN?` command. diff --git a/docs/images/PandA-logo-for-black-background.svg b/docs/images/PandA-logo-for-black-background.svg new file mode 100644 index 00000000..f52449b4 --- /dev/null +++ b/docs/images/PandA-logo-for-black-background.svg @@ -0,0 +1,331 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..2cd059c2 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,41 @@ +--- +site: + hide_outline: true +--- + +```{include} ../README.md +:end-before: +The downside to this mode is that if capture fails for any reason, then the last +framed block of data that the server sent should be discarded, as it will have +been corrupted while being sent. + +## Examples + +Some examples of data capture for different options follow. + +Default: + +``` +arm_time: 2021-05-26T10:34:06.133Z +missed: 0 +process: Scaled +format: ASCII +fields: + PCAP.CAPTURE_TS double Trigger + COUNTER1.OUT double Triggered scale: 1 offset: 0 units: + COUNTER2.OUT double Triggered scale: 1 offset: 0 units: + PGEN1.OUT double Triggered scale: 1 offset: 0 units: + + 1e-06 0 0 262143 + 3e-06 0 0 262142 + 5e-06 0 0 262141 + 7e-06 0 0 262140 + 9e-06 0 0 262139 +END 5 Ok +``` + +`BASE64`: + +``` +arm_time: 2021-05-26T10:34:06.133Z +missed: 0 +process: Scaled +format: Base64 +sample_bytes: 32 +fields: + PCAP.CAPTURE_TS double Trigger + COUNTER1.OUT double Triggered scale: 1 offset: 0 units: + COUNTER2.OUT double Triggered scale: 1 offset: 0 units: + PGEN1.OUT double Triggered scale: 1 offset: 0 units: + + ju21oPfGsD4AAAAAAAAAAAAAAAAAAAAAAAAAAPj/D0FU5BBxcyrJPgAAAAAAAAAAAAAAAAAAAAAA + AAAA8P8PQfFo44i1+NQ+AAAAAAAAAAAAAAAAAAAAAAAAAADo/w9BuF8+WTFc3T4AAAAAAAAAAAAA + AAAAAAAAAAAAAOD/D0E/q8yU1t/iPgAAAAAAAAAAAAAAAAAAAAAAAAAA2P8PQQ== +END 5 Ok +``` + +`XML`: + +``` +
+ + + + + + + +
+ + 1e-06 0 0 262143 + 3e-06 0 0 262142 + 5e-06 0 0 262141 + 7e-06 0 0 262140 + 9e-06 0 0 262139 +END 5 Ok +``` + +## Getting captured data out + +This page describes the wire format on the data port. For end-to-end ways of +retrieving captured data — reading the binary stream directly, using the Python +client, or via EPICS/Tango — see the meta-panda how-to +[Integrate with a PandA](xref:meta-panda/how-to/integrate-with-a-panda). + + +As a starting point, captured data can be read out programmatically with the +Python client's +[`BlockingClient`](xref:PandABlocks-client#pandablocks.blocking.BlockingClient). diff --git a/docs/reference/commands.md b/docs/reference/commands.md new file mode 100644 index 00000000..ef23867e --- /dev/null +++ b/docs/reference/commands.md @@ -0,0 +1,318 @@ +# Command interface + +The default server port for the command interface is port 8888. All commands and +responses are in ASCII with lines separated by newline characters (ASCII +character `0x0A`). + +All commands can be grouped into three forms (query, assignment, table +assignment) and two targets (system and fields). There are exactly four possible +response formats (ok, ok with value, error, multiple value). This section +describes the command interface. + +The three basic command forms are: + +| Name | Format | Description | +|---|---|---| +| Query | *target*`?` | Interrogates *target* for the current value; can return an error, a single value, or a list of multiple values. | +| Assignment | *target*`=`*value* | Updates *target* with the given value; can return an error or success. | +| Table | *target*`<`*format* | Command may be followed by lines of text, and *must* be terminated by a blank line. | + +The four basic command responses are: + +| Name | Format | Description | +|---|---|---| +| Success | `OK` | Returned by assignment and table commands to report a successful update. | +| Value | `OK =`*value* | Successful return of a single value from a query command. | +| Error | `ERR` *error* | Error string returned on any command failure. | +| Multi value | `!`*value* … `.` | Any number of values can be returned, each preceded by `!`, finally `.` by itself indicates end of input. | + +Command forms and their possible responses: + +| Form | Responses | +|---|---| +| Query | Error, Value, Multi value | +| Assignment | Error, Success | +| Table | Error, Success | + +Each individual query target will either return a single value or multi-value, +as documented below. + +Finally, there are two basic types of target: configuration commands and system +commands. + +## Configuration commands + +The entire hardware interface to PandA is structured into "blocks" and "fields", +and each field may have a number of "attributes" depending on the field type. +This structure is reflected in the form of the configuration commands tabulated +below: + +| Command syntax | Description | +|---|---| +| *block*[*number*]`.`*field*`?` | Return current value of field. | +| *block*[*number*]`.`*field*`=`*value* | Assign value to field. | +| *block*[*number*]`.`*field*`<`[[`<`][`\|`]][`B`] | Write table data to field. `<` writes a fixed table, `<<` writes a streaming table and `<<\|` writes the last streaming table. | +| *block*[*number*]`.`*field*`.`*attr*`?` | Return current value of field attribute. | +| *block*[*number*]`.`*field*`.`*attr*`=`*value* | Assign value to field attribute. | +| *block*[*number*]`.*?` | Returns list of fields. | +| *block*[*number*]`.`*field*`.*?` | Returns list of field attributes. | + +In all of these commands the number after the block is optional if there is only +one instance of that block, and is ignored for the two `.*?` commands. See the +description of the `.TABLE` fields in [](/reference/fields.md) for an explanation +of the optional format characters in the table write command. + +## System commands + +All system commands are prefixed with a leading `*` character. The simplest +command is `*IDN?`, which returns a system identification string: + +``` +< *IDN? +> OK =PandA SW: 4.1-1-g2a34851 FPGA: 4.2.0 04e81f37 8a2b0249 rootfs: 2023.2+snapshot-bb27e6aef4dafd501bb5d72c95facd178c92dc48 (langdale) +``` + +The available system commands are tabulated here and listed in more detail +below: + +| Command | Description | +|---|---| +| `*IDN?` | Device identification. | +| `*ECHO `*string*`?` | Echo. | +| `*WHO?` | List connected clients. | +| `*BLOCKS?` | List device blocks. | +| `*DESC.`*block*`.`*field*[`.`*attr*]`?`
`*DESC.`*block*`.`*field*`[].`*subfield*`?` | Show description for field, attribute, or table subfield. | +| `*ENUMS.`*block*`.`*field*[`.`*attr*]`?`
`*ENUMS.`*block*`.`*field*`[].`*subfield*`?` | List enumerations for field, attribute, or table subfield. | +| `*CHANGES`[`.`*group*]`?` | Report changes to values. *group* can be any of `CONFIG`, `BITS`, `POSN`, `READ`, `ATTR`, `TABLE`, or `METADATA`. | +| `*CHANGES`[`.`*group*]`=`[`E`\|`S`] | Reset reported changes; *group* as above. | +| `*CAPTURE?` | Report fields configured for capture. | +| `*CAPTURE.*?` | List all fields that can be captured. | +| `*CAPTURE.`*name*`?` | Interrogate capture options; *name* can be `OPTIONS` or `ENUMS`. | +| `*CAPTURE=` | Reset data capture. | +| `*POSITIONS?` | Enumerate possible capture positions. | +| `*BITS?` | Enumerate possible bit bus positions. | +| `*VERBOSE=`*value* | Control command logging. | +| `*PCAP.`*field*`?` | Special position capture status fields. *field* can be any of `STATUS`, `CAPTURED`, or `COMPLETION`. | +| `*PCAP.`*field*`=` | Position capture actions. *field* can be either `ARM` or `DISARM`. | +| `*SAVESTATE=` | Triggers immediate save to file of the persistence file state. | +| `*CLOCK_FREQ?` | Returns currently configured system clock frequency. | + +`*IDN?` +: Returns the system identification string, for example: + + ``` + OK =PandA SW: 1.1 FPGA: 0.1.9 d1275f61 00000000 rootfs: PandA 1.1 + ``` + + The first field after "PandA" is the software version, the second is the FPGA + version, the third the firmware build number, and the fourth identifies the + supporting firmware. The final fields (prefixed `rootfs:`) identify the + underlying system on which the server is running. + + Note that the `rootfs:` identification is new to version 1.1 of PandA. + +`*ECHO `*string*`?` +: Returns the string back to the caller. Not terribly useful. Note that the + echoed string cannot contain any of `?`, `=` or `<`, as this would cause the + command to be mistaken for another command format. Example usage: + + ``` + < *ECHO This is a test? + > OK =This is a test + ``` + +`*WHO?` +: Returns a list of client connections, for example: + + ``` + < *WHO? + > !2015-12-04T14:30:40.403Z config 127.0.0.1:34185 + > . + ``` + + The first field is the time the connection was made, the second is either + `config` or `data` depending on whether the configuration or data port is + connected, and the third is the remote IP address and socket. + +`*BLOCKS?` +: Returns a list of all the top-level blocks in the system. The order in which + the blocks are returned is somewhat arbitrary. For example (here the list has + been shortened in the middle): + + ``` + < *BLOCKS? + > !TTLIN 6 + > !OUTENC 4 + ... + > !CLOCKS 1 + > !BITS 1 + > !QDEC 4 + > . + ``` + + Block and field commands can be used to interrogate each block. The number + after each block records the number of instances of each block. + +`*DESC.`*block*`?`
`*DESC.`*block*`.`*field*`?`
`*DESC.`*block*`.`*field*`.`*attr*`?`
`*DESC.`*block*`.`*field*`[].`*subfield*`?` +: Returns the description string for the specified block, field, attribute, or + table subfield, e.g.: + + ``` + < *DESC.TTLIN? + > OK =TTL input + < *DESC.TTLIN.TERM? + > OK =Select TTL input termination + < *DESC.TTLIN.TERM.INFO? + > OK =Class information for field + ``` + +`*ENUMS.`*block*`.`*field*`?`
`*ENUMS.`*block*`.`*field*`.`*attr*`?`
`*ENUMS.`*block*`.`*field*`[].`*subfield*`?` +: Returns the list of enumerations for the given field, attribute, or table + subfield, if appropriate. + +`*CHANGES?`
`*CHANGES.CONFIG?`
`*CHANGES.BITS?`
`*CHANGES.POSN?`
`*CHANGES.READ?`
`*CHANGES.ATTR?`
`*CHANGES.TABLE?`
`*CHANGES.METADATA?` +: Reports changes to the appropriate group of values. Changes are reported since + the last request on the connection, and on the first request the current value + for every field will be reported. `*CHANGES?` reports changes for all groups; + otherwise one of the following groups can be selected: + + | Group | Description | + |---|---| + | CONFIG | Configuration settings | + | BITS | Bits on the system bus | + | POSN | Positions | + | READ | Polled read values | + | ATTR | Attributes (including capture enable flags) | + | TABLE | Table changes | + | METADATA | Metadata field changes | + + For example: + + ``` + < *CHANGES.CONFIG? + > !TTLIN1.TERM=High-Z + > !TTLIN2.TERM=50-Ohm + > !TTLIN3.TERM=High-Z + ... + > !QDEC2.B=TTLIN1.VAL + > !QDEC3.B=TTLIN1.VAL + > !QDEC4.B=TTLIN1.VAL + > . + ``` + + Here 804 (at the time of writing) lines have been deleted from the transcript. + If we repeat the call we see that no further changes have happened until + something is actually changed: + + ``` + < *CHANGES.CONFIG? + > . + < TTLOUT4.VAL=TTLIN3.VAL + > OK + < *CHANGES.CONFIG? + > !TTLOUT4.VAL=TTLIN3.VAL + > . + ``` + + Note that for tables only the fact that the table has changed is shown; no + attempt is made to show the current table value: + + ``` + < *CHANGES.TABLE? + > !PCOMP1.TABLE< + > !PCOMP2.TABLE< + > !PCOMP3.TABLE< + > !PCOMP4.TABLE< + > !PGEN1.TABLE< + > !PGEN2.TABLE< + > !SEQ1.TABLE< + > !SEQ2.TABLE< + > !SEQ3.TABLE< + > !SEQ4.TABLE< + > . + ``` + +`*CHANGES=`[`E`\|`S`]
`*CHANGES.CONFIG=`[`E`\|`S`]
`*CHANGES.BITS=`[`E`\|`S`]
`*CHANGES.POSN=`[`E`\|`S`]
`*CHANGES.READ=`[`E`\|`S`]
`*CHANGES.ATTR=`[`E`\|`S`]
`*CHANGES.TABLE=`[`E`\|`S`]
`*CHANGES.METADATA=`[`E`\|`S`] +: These commands reset the change information for the corresponding group so that + only changes occurring after the reset are reported, or so that all changes are + reported. If `=` or `=E` (for End) is specified then only new changes are + reported; if `=S` (for Start) then change reporting is reset to the start as + for a new connection. For example: + + ``` + < TTLIN1.TERM=50-Ohm + > OK + < *CHANGES= + > OK + < *CHANGES.CONFIG? + > . + ``` + +`*CAPTURE?` +: Returns a list of all positions and bit masks that will be written to the data + capture port. This list is controlled by setting the `.CAPTURE` attribute on + the corresponding position fields. + +`*CAPTURE.*?` +: Returns a list of all fields that can be configured for capture. This includes + all `pos_out` and `ext_out` fields. + +`*CAPTURE.OPTIONS?` +: Lists the available capture options for `pos_out` fields. See + [](/reference/capture-options.md) for the full set of options. + +`*CAPTURE.ENUMS?` +: Generates a curated list of capture option selections, designed for presenting + lists of available capture options as an enumeration. Returns the same as + calling `*ENUMS.`*name*`.`*field*`.CAPTURE?` on any `pos_out` field. + +`*CAPTURE=` +: Resets all `.CAPTURE` flags to zero so that no data will be captured. + +`*POSITIONS?` +: Lists all available position capture fields in order. + +`*BITS?` +: Lists all available bit bus positions, but not including the special values + `ZERO` and `ONE`. + +`*VERBOSE=`*value* +: If `*VERBOSE=1` is set then every command will be echoed to the server's log. + Set `*VERBOSE=0` to restore normal quiet behaviour. + +`*PCAP.STATUS?`
`*PCAP.CAPTURED?`
`*PCAP.COMPLETION?` +: Interrogates the status of position capture: + + | Field | Description | + |---|---| + | STATUS | Returns a string with three fields: "Busy" or "Idle", followed by the number of connected readers, and the number taking data. | + | CAPTURED | Returns the number of samples captured in the current or most recent data capture. | + | COMPLETION | Returns the completion status from the most recent data capture, as listed in the table below. | + + The completion codes have the following meaning: + + | Code | Meaning | + |---|---| + | Busy | Capture in progress. | + | Ok | Capture completed without error or intervention. | + | Disarmed | Capture was manually disarmed by `*PCAP.DISARM=` command. | + | Framing error | Data capture framing error, probably due to incorrectly configured capture. | + | DMA data error | Internal data error, should not occur. | + | Driver data overrun | Data capture too fast, internal buffers overrun. Can also occur if the PandA processor is overloaded. | + +`*PCAP.ARM=`
`*PCAP.DISARM=` +: Top-level capture control: + + | Field | Description | + |---|---| + | ARM | Initiates data capture. Will fail if capture is already in progress, or no fields are configured for capture. | + | DISARM | Halts ongoing data capture. | + +`*SAVESTATE=` +: Updates the persistence state file (as configured on the command line when + launched) with the current state. Returns after a filesystem `sync` call, so it + is safe to power off the system after this command has completed. + +`*CLOCK_FREQ?` +: Returns the currently configured FPGA clock frequency, as used to convert + between times in natural units and times in clock ticks. diff --git a/docs/reference/config.md b/docs/reference/config.md new file mode 100644 index 00000000..c5b99e42 --- /dev/null +++ b/docs/reference/config.md @@ -0,0 +1,145 @@ +# Config, registers and description files + +On startup the PandA server loads its configuration from three files: `config`, +`registers`, and `description`. When running the simulation these are loaded from +the `config_d` directory in the build directory; on PandA they are loaded from +`/opt/share/panda/config_d`. + +The syntax of each file mirrors the field definitions described in +[](/reference/fields.md). All three files share a common structure: indentation +indicates hierarchy, `#` starts a comment, block names appear in the first +column, and fields are indented one level. + +| File | Purpose | +|---------------|--------------------------------------------------------------| +| `config` | Defines all blocks, their fields, and each field's behaviour | +| `registers` | Maps each block and field to hardware register offsets | +| `description` | Optionally provides human-readable descriptions | + +## Configuration file `config` + +This file defines all blocks and fields available to this PandA instance and is +processed first. + +**Block definition syntax:** + +``` +block-name [ "[" count "]" ] + [ field-definition ]* +``` + +A block definition gives the block a name and optionally a repeat *count* (the +number of hardware instances). It is followed by indented field definitions. + +**Field definition syntax:** + +``` +field-name field-type [ field-type-data ] +``` + +The *field-type* determines the basic function of the field, what operations are +permitted on it, and how it maps to hardware registers. + +### Field types + +| Field type | Description | +|-----------------------------------------|-----------------------------------------------------| +| `param` *subtype* [`=` *value*] | Single 32-bit value written to a register; optional initial value | +| `read` *subtype* | Read-only register | +| `write` *subtype* | Write-only register (triggers an immediate action) | +| `time` | Like `param` but 64-bit, spanning two registers | +| `bit_out` | Output bit | +| `pos_out` [*scale* [*offset* [*units*]]]| Position bus output; optional default scale/offset/units | +| `ext_out` (`timestamp` \| `samples` \| `bits` *group*) | Extension bus entry needing special treatment | +| `bit_mux` [`=` *value*] | Bit bus input selector; optionally defaults to `ONE` (129) | +| `pos_mux` | Position bus input selector | +| `table` [*row-words*] | Long table of numbers with special access methods | + +`ext_out` *ext-extra* values: + +| Value | Description | +|-----------------|----------------------------------------------------------| +| `timestamp` | Captures a 64-bit timestamp | +| `samples` | Captures the sample count for data capture | +| `bits` *group* | Captures 32 bits of the 128-bit bit bus; *group* selects which 32-bit slice | + +### Field subtypes + +`param`, `read`, and `write` fields require one of the following subtypes: + +| Subtype | Description | +|-----------------------------------------|-----------------------------------------------------| +| `uint` [*max-value*] | Unsigned integer, optionally bounded | +| `int` | Signed integer | +| `scalar` *scale* [*offset* [*units*]] | Fixed-point value with scale and optional offset/units | +| `bit` | Single bit | +| `action` | Write-only trigger with no data payload | +| `lut` | 5-input lookup table function | +| `enum` | Enumeration; followed by indented `number string` pairs | +| `position` | Position value | +| `time` | Time value | + +## Register file `registers` + +This file assigns hardware registers to each block and field. Its structure +mirrors `config` but replaces field type specifications with register +assignments. + +**Block definition syntax:** + +``` +block-name { [ "S" ] block-register | "X" } [ extension-module ] + [ field-definition ]* +``` + +- Prefix `S` on *block-register* allows the same register to be shared across + multiple blocks. +- `X` instead of a register number marks a block with no register-mapped fields + (extension-only fields only). +- An optional *extension-module* name enables the extension register syntax for + this block. + +**Field register syntax by type:** + +| Class | Register syntax | +|------------------------|-------------------------------------------------------| +| `param` | *register* \| *write-extension* | +| `read` | *register* \| *read-extension* | +| `write` | *register* \| *write-extension* | +| `time` | *low-register* *high-register* | +| `bit_out` | `(`*bit-index*`)`N | +| `pos_out` | `(`*pos-index*`)`N | +| `ext_out timestamp` | *ext-index* *ext-index* | +| `ext_out` other | *ext-index* | +| `bit_mux` | *register* | +| `pos_mux` | *register* | +| `table` (short) | `short` *size* *init-reg* *fill-reg* *length-reg* | +| `table` (long/DMA) | `long` `2^`*size* *nbuf* *base-reg* *length-reg* | + +The notation `(...)`N means the register number is repeated N times, once per +hardware instance of the block. + +### Extension register syntax + +When an extension server is enabled (see [](/how-to/startup.md) `-X` option) and +an extension module is associated with a block, `param`, `read`, and `write` +fields may be redirected to it: + +``` +read-extension = [ read-reg ]* "X" field-spec +write-extension = [ read-reg ]* [ "W" [ write-reg ]* ] "X" field-spec +``` + +*field-spec* is passed to the extension module to bind the field. See +[](/reference/extension.md) for details on extension fields. + +## Description file `description` + +The entire file is optional. Its syntax is: + +``` +block-name block-description + field-name field-description +``` + +Descriptions are newline-terminated UTF-8 strings. diff --git a/docs/reference/extension.md b/docs/reference/extension.md new file mode 100644 index 00000000..62ee4d47 --- /dev/null +++ b/docs/reference/extension.md @@ -0,0 +1,13 @@ +# Extension server + +:::{admonition} 🚧 TODO — documentation stub +:class: warning + +This page is a Stage A scaffold stub and has not yet been written. + +**Status:** blocked: verify + +**Source:** extension.rst (739w: Extension Modules, Injected Values) — verify vs implementation + +**Blocked:** [PandABlocks/PandABlocks-server#80](https://github.com/PandABlocks/PandABlocks-server/issues/80) +::: diff --git a/docs/reference/fields.md b/docs/reference/fields.md new file mode 100644 index 00000000..f1e0303e --- /dev/null +++ b/docs/reference/fields.md @@ -0,0 +1,500 @@ +(fields)= +# Blocks, fields and attributes + +The set of hardware blocks can be interrogated with the `*BLOCKS?` command: + +``` +< *BLOCKS? +> !TTLIN 6 +> !OUTENC 4 +> !PCAP 1 +> !PCOMP 4 +> !TTLOUT 10 +> !ADC 8 +> !DIV 4 +> !INENC 4 +> !SLOW 1 +> !PGEN 2 +> !LVDSIN 2 +> !POSITIONS 1 +> !POSENC 4 +> !SEQ 4 +> !PULSE 4 +> !SRGATE 4 +> !LUT 8 +> !LVDSOUT 2 +> !COUNTER 8 +> !ADDER 1 +> !CLOCKS 1 +> !BITS 1 +> !QDEC 4 +> . +``` + +For each block the number after the block tells us how many instances there are +of the block. Each block is controlled and interrogated through a number of +fields, and the `block.*?` command can be used to interrogate the list of +fields: + +``` +< TTLIN.*? +> !VAL 1 bit_out +> !TERM 0 param enum +> . +``` + +This tells us that block `TTLIN` has two fields, `TTLIN.VAL` and `TTLIN.TERM`. +The first field after the field name is a sequence number for user interface +display, and the rest of each response describes the "type" of the field. In +this case we see that `TTLIN.VAL` is a `bit_out` field, which means it can be +used for bit data capture and can be connected to any `param bit_mux` field as a +data source. + +Each field has one or more attributes depending on the field type. The list of +attributes can be interrogated with the `block.field.*?` command: + +``` +< TTLIN.VAL.*? +> !CAPTURE_WORD +> !OFFSET +> !INFO +> . +< TTLIN.TERM.*? +> !INFO +> . +``` + +All fields have the `.INFO` attribute, which just repeats the type information +already reported, e.g. `TTLIN1.VAL.INFO?` returns `bit_out` (note that a block +number must be specified when interrogating fields and attributes). + +## Field types + +Each field type determines the set of attributes available for the field. The +types and their attributes are documented below. + +| Field type | Description | +|---|---| +| `param` *subtype* | Configurable parameter. The *subtype* determines the precise behaviour and the available attributes. | +| `read` *subtype* | A read-only hardware field, used for monitoring status. Again, *subtype* determines available attributes. | +| `write` *subtype* | A write-only field; *subtype* determines possible values and attributes. | +| `time` | Configurable timer parameter. | +| `bit_out` | Bit output, can be configured as bit input for `bit_mux` fields. | +| `pos_out` | Position output, can be configured for data capture and as position input for `pos_mux` fields. | +| `ext_out` *extra* | Extended output values, can be configured for data capture, but not available on position bus. | +| `bit_mux` | Bit input with configurable delay. | +| `pos_mux` | Position input multiplexer selection. | +| `table` | Table data with special access methods. | + +`param` *subtype* +: All fields of this type contribute to the `*CHANGES.CONFIG` change group and + are used to configure the behaviour of the corresponding block. Fields of this + type are used for input configuration and other behavioural settings. + +`read` *subtype* +: All fields of this type contribute to the `*CHANGES.READ` change group, but + are only checked when either the field is read or the change group is polled. + Fields of this type are used for monitoring the internal status of a block, + and they cannot be written to. + +`write` *subtype* +: Fields of this type can only be written and are used for immediate actions on + a block. The `action` subtype is used to support actions without any + parameters; for example the following command forces a soft reset on the given + pulse block: + + ``` + < PULSE1.FORCE_RESET= + > OK + ``` + +`time` +: Fields of this type are used for configuring delays. They also contribute to + `*CHANGES.CONFIG`. The following attributes are supported: + + `UNITS` + : Can be set to any of the strings `min`, `s`, `ms`, or `us`, and is used to + interpret how values read and written to the field are interpreted. + + `RAW` + : Can be read or written to report or set the delay in FPGA ticks. + + The `UNITS` attribute determines how numbers read or written to the field are + interpreted. For example: + + ``` + < PULSE1.DELAY.UNITS=s + > OK + < PULSE1.DELAY=2.5 + > OK + < PULSE1.DELAY.RAW? + > OK =312500000 + < PULSE1.DELAY.UNITS=ms + > OK + < PULSE1.DELAY? + > OK =2500 + ``` + + Note that changing `UNITS` doesn't change the delay, only how it is reported + and interpreted. + +`bit_out` +: Fields of this type are used for block outputs which contribute to the + internal bit system bus, and they contribute to the `*CHANGES.BITS` change + group. They can be captured via the appropriate `PCAP.BITS`*n* block as + reported by the `CAPTURE_WORD` attribute. + + `CAPTURE_WORD` + : Identifies which `pos_out` value can be used to capture this bit. + + `OFFSET` + : The bit offset into the captured word of this particular bit. + + For example: + + ``` + < TTLIN1.VAL.CAPTURE_WORD? + > OK =PCAP.BITS0 + < TTLIN1.VAL.OFFSET? + > OK =2 + ``` + + This tells us that if `PCAP.BITS0` is captured then `TTLIN1.VAL` can be read as + bit 2 of this word, counting from the least significant bit. The field itself + can be read to return the current value of the bit. + +`pos_out` +: Fields of this type are used for block outputs which contribute to the + internal position bus, and they contribute to the `*CHANGES.POSN` change group. + The following attribute supports capture control: + + `CAPTURE` + : Manages capture of this field. One of the following enumeration values can be + written: + + | Value | Description | + |---|---| + | No | Capture is disabled for this field. | + | Value | The value at the time of trigger will be captured. | + | Diff | The difference of values is captured. | + | Sum | The sum of all valid values is captured. This is a 64-bit value, and may be further scaled if `PCAP.SHIFT_SUM` is set. | + | Mean | The average of all valid values is captured. | + | Min | The minimum of all valid values is captured. | + | Max | The maximum of all valid values is captured. | + | Min Max | Both minimum and maximum values are captured. | + | Min Max Mean | All three values — minimum, maximum, average — are captured. | + | StdDev | The standard deviation of valid values is captured. Only available if supported by the FPGA configuration. | + | Mean StdDev | Both average and standard deviation are captured. Only available if supported by the FPGA configuration. | + + Combinations of the individual options can also be written as a + space-separated list — see [](/reference/capture-options.md). + + The following attributes support formatting of the field when reading it; the + current value is returned subject to the formatting rules described below. + + `OFFSET`, `SCALE` + : Configure the conversion from the underlying position to the value captured + when scaling is enabled and read from the `SCALED` attribute. + + `UNITS` + : Can be set to any UTF-8 string, provided for the convenience of the user + interface and returned as part of the data capture heading. + + `SCALED` + : Returns the scaled value computed as `value * scale + offset`. + +`ext_out` *extra* +: Fields of this type represent values that can be captured but which are not + present on the position bus. These fields also support one capture control + field: + + `CAPTURE` + : As for `pos_out`, can be set to control capture of this field: + + | Value | Description | + |---|---| + | No | This field will not be captured. | + | Value | This field will be captured. | + + The *extra* field determines the detailed behaviour of this field, and will be + one of the following values: + + | extra value | Description | + |---|---| + | `timestamp` | Timestamps in clock ticks with optional scaling to seconds on data capture. | + | `samples` | Special internal field for counting captured samples. | + | `bits` | Used to implement bit-bus readout fields. Fields of this sub-type implement an extra `BITS` field. | + + Fields of type `ext_out bits` implement an extra attribute: + + `BITS` + : Returns a list of all bit fields associated with this field. Fields of this + type can be used to capture a snapshot of the bit bus at the trigger time. + +`bit_mux` +: Bit input selectors for blocks. Each of these fields can be set to the name of + a corresponding `bit_out` field, for example: + + ``` + < TTLOUT1.VAL=TTLIN1.VAL + > OK + ``` + + There are two attributes: + + `DELAY` + : Can be set to any value between 0 and `MAX_DELAY` to delay the bit input to + the block by the specified number of clock ticks. + + `MAX_DELAY` + : Returns the maximum delay that can be set for this input. + +`pos_mux` +: Position input selectors for blocks. Each of these fields can be set to the + name of a corresponding `pos_out` field, for example: + + ``` + < ADDER1.INPA=ADC2.OUT + > OK + ``` + +`table` +: Values of this type are used for long tables of numbers. This server imposes + no structure on these values apart from treating them as an array of 32-bit + integers. + + Table values are written with the special `<` syntax: + + | Operator | Description | + |---|---| + | `block`*number*`.field<` | Normal table write, fixed table | + | `block`*number*`.field<<` | Normal table write, streaming table | + | `block`*number*`.field<<\|` | Normal table write, last streaming table | + | `block`*number*`.field OK + < SEQ3.TABLE.LENGTH? + > OK =12 + ``` + + Note that when data is sent in base-64 format, each individual line must + encode a multiple of four bytes, otherwise the write will be rejected. For full + details of fixed vs. streaming (`<<` / `<<|`) table writes and DMA buffer + sizing, see [](/reference/streaming-tables.md). + + The following attributes are provided by this field type: + + `MAX_LENGTH` + : The maximum number of 32-bit words which can be stored in the table. + + `LENGTH` + : The current number of words in the table. + + `B` + : This read-only attribute returns the content of the table in base-64. + + `FIELDS` + : Returns a list of strings which can be used to interpret the content of the + table. Each line returned is of the following format: + + ``` + left:right field-name subtype + ``` + + Here *left* and *right* are bit field indices into a single table row, + consisting of a number of 32-bit words concatenated (in little-endian order) + with bits numbered from 0 in the least significant position up to + 32×`ROW_WORDS`−1, and *left* ≥ *right*. The name of the field is given by + *field-name*, and *subtype* can be one of `int`, `uint`, or `enum`. If + *subtype* is `enum` then the list of enums can be interrogated through the + command: + + ``` + *ENUMS.block.table[].field? + ``` + + where *block*, *table*, *field* are appropriate names. + + `ROW_WORDS` + : Returns the number of 32-bit words in a single row of the table. This can be + used to help interpret the `FIELDS` result. + + `QUEUED_LINES` + : When a fixed table is written, returns the number of lines in that table. + When streaming tables are written, returns the number of lines that have been + scheduled, including the ones currently being used by the FPGA. + + `MODE` + : Indicates the mode that the table is in as a consequence of the last table + write. The possible values are `INIT` (no table), `FIXED` (fixed table), + `STREAMING` (streaming table) and `STREAMING_LAST` (last streaming table). + Writing an empty table always moves the table to `INIT` state. In addition to + this mode, there is also an implicit completed state in the FPGA that happens + either when there is a sudden error or when the streaming is finished; it will + cause any future writes to be rejected and the `MODE` attribute will keep the + last value until a reset is done, to ensure the client is aware of any error. + + The following table summarises the mode transitions for each table command, + where `<0` represents writing an empty table: + + | MODE \ command | `<` | `<<` | `<<\|` | `<0` | + |---|---|---|---|---| + | `INIT` | `FIXED` | `STREAMING` | `STREAMING_LAST` | `INIT` | + | `FIXED` | `FIXED` | `STREAMING` | `STREAMING_LAST` | `INIT` | + | `STREAMING` | Reject | `STREAMING` | `STREAMING_LAST` | `INIT` | + | `STREAMING_LAST` | Reject | Reject | Reject | `INIT` | + | `COMPLETED[HEALTH]` | Reject | Reject | Reject | `INIT` | + + Writing empty tables with `<<` or `<<|` is rejected to avoid accidental + mistakes in streaming mode. + +## Field sub-types + +The following field sub-types can be used for `param`, `read` and `write` +fields. + +`uint` [*max-value*] +: The most basic type: the value read or written is an unsigned 32-bit number. + There is one fixed attribute: + + `MAX` + : Returns the maximum value that can be written to this field. + +`int` +: Similar to `uint`, but signed, and there is no upper limit on the value. + +`scalar` *scale* [*offset* [*units*]] +: Floating point values can be read or written, and are converted from and to the + underlying signed integer type via the equations below: + + ``` + value = scale * raw + offset + raw = (value - offset) / scale + ``` + + The following attributes are supported: + + `UNITS` + : Returns the configured units string. + + `RAW` + : Returns the underlying unconverted integer value. + + `SCALE` + : Returns the configured scaling factor. + + `OFFSET` + : Returns the configured scaling offset. + +`bit` +: A value which is 0 or 1; there are no extra attributes. + +`action` +: A value which cannot be read and always writes as 0. Only useful for `write` + fields. + +`lut` +: This field sub-type is used for the 5-input lookup table function calculation + field. This field can be set to any valid logical expression generated from + inputs `A` to `E` using the standard operators `&`, `|`, `^`, `~`, `?:` from C + together with `=` for equality and `=>` for implication (`A=>B` abbreviates + `~A|B`). All operations have C precedence, `=` has the same precedence as `==` + in C, and `=>` has precedence between `|` and `?:`. + + The following attribute is supported: + + `RAW` + : Returns the corresponding lookup table assignment as a 32-bit number. + + For example: + + ``` + < LUT2.FUNC=A=>B?C:D + > OK + < LUT2.FUNC? + > OK =A=>B?C:D + < LUT2.FUNC.RAW? + > OK =0xF0CCF0F0 + ``` + +`enum` +: Enumeration fields define a list of valid strings which can be written to the + field. To interrogate the list of valid enumeration values use the `*ENUMS` + command, for example: + + ``` + < *ENUMS.TTLIN1.TERM? + > !High-Z + > !50-Ohm + > . + ``` + +`time` +: Converts between time in specified units and time in FPGA clock ticks. The + following attributes are supported: + + `UNITS` + : Can be set to any of the strings `min`, `s`, `ms`, or `us`, and is used to + interpret how values read and written to the field are interpreted. + + `RAW` + : Can be read or written to report or set the delay in FPGA ticks. + +## Summary of sub-types + +| Sub-type | Attributes | Description | +|---|---|---| +| uint | MAX | Possibly bounded 32-bit unsigned integer value | +| int | | Unbounded 32-bit signed integer value | +| scalar | RAW, UNITS, SCALE, OFFSET | Scaled signed floating point value | +| bit | | Bit: 0 or 1 | +| action | | Write only, no value | +| lut | RAW | 5-input lookup table logical formula | +| enum | | Enumeration selection (labels listed via the `*ENUMS` command) | +| time | RAW, UNITS | Time intervals converted to FPGA ticks | + +## Summary of attributes + +| Field (sub)type | Attribute | Description | R | W | C | M | +|---|---|---|:-:|:-:|:-:|:-:| +| (all) | INFO | Returns type of field | R | | | | +| uint | MAX | Maximum allowed integer value | R | | | | +| scalar | RAW | Underlying integer value | R | W | | | +| | UNITS | Configured units for scalar | R | | | | +| | SCALE | Configured scaling factor for scalar | R | | | | +| | OFFSET | Configured scaling offset for scalar | R | | | | +| lut | RAW | Computed lookup table 32-bit value | R | | | | +| time | UNITS | Units and scaling selection for time | R | W | C | | +| | RAW | Raw time in FPGA clock cycles | R | W | | | +| bit_out | CAPTURE_WORD | Capturable word containing this bit | R | | | | +| | OFFSET | Offset of this bit in captured word | R | | | | +| bit_mux | DELAY | Bit input delay in FPGA ticks | R | W | C | | +| | MAX_DELAY | Maximum valid delay | R | | | | +| pos_out | CAPTURE | Position capture control | R | W | C | | +| | OFFSET | Position offset | R | W | C | | +| | SCALE | Position scaling | R | W | C | | +| | UNITS | Position units | R | W | C | | +| | SCALED | Position after applying scaling | R | | | | +| ext_out bits | BITS | List of bit_out fields | R | | | M | +| table | MAX_LENGTH | Maximum table length in 32-bit words | R | | | | +| | LENGTH | Current table length in 32-bit words | R | | | | +| | B | Table data in base-64 | R | | | M | +| | FIELDS | Table field descriptions | R | | | M | +| | ROW_WORDS | Number of words in a table row | R | | | | + +Key: + +- **R** — Attribute can be read. +- **W** — Attribute can be written. +- **C** — Attribute contributes to the `*CHANGES.ATTR` change set. +- **M** — Attribute returns a multiple-value result. diff --git a/docs/reference/streaming-tables.md b/docs/reference/streaming-tables.md new file mode 100644 index 00000000..835ba1d6 --- /dev/null +++ b/docs/reference/streaming-tables.md @@ -0,0 +1,88 @@ +# Streaming tables + +Tables in PandA operate in two modes: **fixed** (a single write replaces the +whole table) and **streaming** (a sequence of writes fills a DMA-backed ring +buffer that the hardware consumes continuously). Streaming is used for long, +DMA-driven tables — for example the SEQ sequencer table — where the host needs +to supply data faster than a single write can deliver. + +## Write operators + +The operator appended to the field name on the configuration port determines +which mode is used: + +| Operator | Encoding | Meaning | +|----------|----------|--------------------------------------------------| +| `<` | text | Fixed table write | +| `<<` | text | Streaming table write (more data follows) | +| `<<\|` | text | Streaming table write, last chunk | +| `