Files
maksyuki 8f5630fb41 feat(tiny): implement R2-P9 DVP capture and recovery
Connect DVP to Tiny DMA2/request 11, IRQ15 and the camera pad/reset lifecycle.
Harden shared capture CDC, frame statistics, per-row size validation and HAL
cleanup while preserving the Mini DMA3 binding and existing register ABI.

Add deterministic capture/recovery firmware, source-bound evidence tooling,
focused RTL/host tests and compact netlist acceptance. Preserve SAFE24,
128 KiB main SRAM, private Crypto storage and the locked dependencies.

Retained validation: focused RTL, host/policy checks, Mini firmware, and
ICS55/IHP130 RECOVERY, QVGA, synthesis and boot/DVP netlist checks pass.
Full-system VGA remains historical; long full Icarus and broad regressions
remain deferred. Global publication/format gaps and negative observational
STA are retained, not reported as passing qualification.

Specification: docs/ip/tiny-soc.md, TINY-R2-P9.
Evidence: docs/ip/tiny-soc-verification.md.
2026-10-17 15:32:59 +08:00

330 lines
19 KiB
Markdown

# APB4 DVP
The DVP block captures an 8-bit parallel camera stream and presents the
captured pixels through an AXI4-Stream source. APB4 is used for configuration,
status, statistics, error reporting, and interrupts. The existing DMA engine
can transfer the AXI4-Stream payload into AXI4 memory using DVP receive mode.
The input path supports RGB565 and YUV422 byte formats, programmable VSYNC
and HREF polarity, selectable PCLK sampling edge, snapshot and continuous
capture, and rectangular cropping.
## Integration
| Property | Value |
| --- | --- |
| APB4 base address | `0x1000E000` |
| APB4 interrupt group bit | 13 |
| Management-core interrupt | 15 |
| Input interface | 8-bit parallel data, PCLK, HREF, VSYNC |
| AXI4-Stream data width | 32 bits |
| AXI4-Stream frame markers | `TUSER[0]` SOF, `TLAST` EOL |
| DMA request selector | `DVP_RX=11`; Mini default bulk DMA channel 3 |
| ABI version | `0x00020000` |
The [Tiny Gen1 R2 target](tiny-soc.md) reuses this IP specification and ABI
unchanged at the same base/core IRQ, with product-selected DMA channel 2,
PCLK host/stream, GPIO12-23 ALT1 camera routing and optional XPI NSS1 PSRAM
frame buffers. Camera and I2S profiles are mutually exclusive. Its
`TINY-R2-P9` integration must validate exact transfer length, shared HAL
channel/burst binding and final-word drain without changing the DVP registers,
formats, FIFO or stream semantics. The product contract requires measuring
the existing 512-byte FIFO's stall budget and qualifying PSRAM transport in
`TINY-R2-P8` before capture integration.
Tiny support remains a frozen target, not existing end-to-end or physical
evidence; Mini's channel-3 default remains unchanged.
The separately approved [PPALite extension](ppalite.md) inserts an optional
processing route after this existing CDC FIFO in the future standard Tiny
product. RAW remains reset-default and preserves this DVP V2 stream/ABI.
PROCESS performs pixel/line selection and row-aligned output packing before
the same DVP_RX request11/DMA2 destination path. It adds no source format,
Pad, FIFO depth, central-DMA request or private memory master to DVP.
Mini retains RAW integration. No PPALite path is implemented by this freeze.
The pixel clock is buffered and can be inverted before the pixel-domain reset
synchronizer. Configuration and frame statistics cross between the system and
pixel domains through the common `cdc_2phase` primitive. Captured payloads use
the common `cdc_fifo_warm_flush` primitive and carry
`{TUSER, TLAST, TKEEP, TSTRB, TDATA}`. FIFO overflow drops the active frame,
sets an error flag, and requires software to discard the affected DMA buffer.
## Register ABI
All registers are 32 bits and naturally aligned. Unmapped, unaligned, and
direction-invalid accesses complete with `resp_err`. Configuration writes are
rejected while capture is active. Frame dimensions must be non-zero; invalid
format or crop values set the configuration error path and do not start a
valid capture. RW registers support byte strobes.
| Offset | Name | Access | Description |
| --- | --- | --- | --- |
| `0x000` | `CTRL` | RW | Enable, snapshot, and crop-enable controls. |
| `0x004` | `RXDATA` | RO/pop | PIO receive word when AXI4-Stream output is disabled. |
| `0x008` | `STATUS` | RO | Active, FIFO, stream, and error state. |
| `0x00C` | `STREAM_CTRL` | RW | Enable AXI4-Stream output. |
| `0x010` | `FORMAT` | RW | RGB565/YUV422 selection and byte/pixel swap controls. |
| `0x014` | `SYNC_CFG` | RW | VSYNC, HREF, and PCLK polarity controls. |
| `0x018` | `FRAME_SIZE` | RW | Frame width in bits 15:0 and height in bits 31:16. |
| `0x01C` | `CROP_START` | RW | Crop X coordinate in bits 15:0 and Y coordinate in bits 31:16. |
| `0x020` | `CROP_SIZE` | RW | Crop width in bits 15:0 and height in bits 31:16. |
| `0x024` | `FRAME_COUNT` | RO | Completed frame count. |
| `0x028` | `LINE_COUNT` | RO | Completed line count from the last frame. |
| `0x02C` | `PIXEL_COUNT` | RO | Captured pixel count from the last frame. |
| `0x030` | `WORD_COUNT` | RO | Full 32-bit AXI4-Stream word count from the last frame. |
| `0x034` | `DROP_COUNT` | RO | Number of dropped words caused by overflow. |
| `0x038` | `ERROR_STATUS` | RW1C | Sticky overflow, sync, size, partial, config, and abort errors. |
| `0x03C` | `INTR_STATE` | RW1C | Sticky frame, line, overflow, configuration, and abort events. |
| `0x040` | `INTR_ENABLE` | RW | Interrupt enable mask. |
| `0x044` | `INTR_STATUS` | RO | `INTR_STATE & INTR_ENABLE`. |
| `0x048` | `INTR_TEST` | WO | Write-one software interrupt injection. |
| `0x04C` | `COMMAND` | WO | Abort and FIFO flush commands. |
| `0x0F8` | `IP_VERSION` | RO | ABI version, currently `0x00020000` (2.0). |
| `0x0FC` | `CAPABILITY` | RO | Implemented format, crop, statistics, interrupt, and stream features. |
`CTRL` bit 0 enables capture, bit 1 selects snapshot mode, and bit 2 enables
cropping. `STREAM_CTRL` bit 0 enables AXI4-Stream output; when it is clear,
software can consume captured words through `RXDATA`. `FORMAT` values `0` and
`1` select RGB565 and YUV422 respectively. Values above `1` are invalid.
`FORMAT` bits 2 and 3 control byte and pixel ordering. `SYNC_CFG` bits 0, 1,
and 2 select active-low VSYNC, active-low HREF, and falling-edge PCLK.
The seven interrupt bits are frame start, line done, frame done, overflow,
synchronization error, configuration error, and aborted capture. Error and
interrupt state are write-one-to-clear. Hardware events have priority over a
simultaneous software clear so that an event cannot be lost. The external
interrupt is the reduction OR of `INTR_STATE & INTR_ENABLE`.
`STATUS` bit 0 is ENABLE, bit 1 is ACTIVE, bit 2 is FIFO_EMPTY and bit 5
is ERROR. ENABLE reports the configuration actually applied in the pixel
domain; `CTRL` continues to read back the requested control value. Software
can therefore acknowledge a disable/enable transition without a new register.
ACTIVE is delivered with a coherent pixel-domain status snapshot;
it is not an alias for ENABLE. An error W1C is retained until the pixel domain
applies its mask and returns a status packet with the matching clear epoch.
Old reports cannot acknowledge that operation. New source errors take priority
over a simultaneous clear. APB response timing and register offsets remain
unchanged; software must poll with a timeout. A stopped pixel clock cannot
complete a clear or recovery operation.
Configuration transfer is held behind pending error clears. A separate applied
pixel configuration is updated only when the core accepts the mailbox; the
mailbox's earlier data presentation cannot modify an active frame.
Configuration and command transactions carry internal epochs; lifecycle idle
stays low until the matching applied/drained status is returned. Waiting for
the first VSYNC is armed work, not idle. Local reset-ready requires a native
pixel-domain status after reset. Tiny global boot uses its existing internal
leaf/APB/AXI prerequisites independently of this local-reset readiness, so an
absent sensor clock does not become a global CPU boot dependency.
ABORT cancels older configuration requests still waiting in the APB register
shell without changing requested-register readback. Its command mailbox also
carries the last issued configuration epoch. If that configuration arrives or
applies after ABORT, it completes the existing acknowledgement but cannot
rearm the core. The cancellation record is retired by the next different
applied configuration epoch, before the one-bit epoch can be reused. Repeated
or coalesced ABORT commands do not advance configuration epochs. ABORT does
not wait for configuration readiness, including when an active frame blocks
configuration application. Later configuration writes remain eligible after
the command drains; normal recovery still clears errors and acknowledges
disable/enable before using a new frame. No register or FIFO ABI is added.
Error interrupt causes use a retained six-bit pending mask and an acknowledged
mailbox, separate from the status snapshots. Repeated causes can coalesce while
pending, but two events between status samples cannot cancel one another. New
causes on mailbox acceptance remain pending for the next transfer. Clearing
an interrupt does not repeatedly reassert it merely because ERROR_STATUS still
holds an old error; a new occurrence does notify again.
The RTL ABI constants are maintained directly in
`rtl/ip/multimedia/dvp_define.svh`. Matching HAL offsets and masks are
maintained directly in `crt/src/hal/dvp.c`. Changes to either definition must
update the other definition, this document, and the RTL/software tests in the
same change.
## AXI4-Stream and Capture Semantics
Two 16-bit pixels, each formed from two 8-bit sensor samples, are packed into
one 32-bit stream word. The first
pixel occupies `TDATA[15:0]` and the second pixel occupies `TDATA[31:16]`.
`TUSER[0]` is asserted on the first word of a frame, and `TLAST` is asserted
on the last word of each line. `TID` and `TDEST` are tied to zero.
For an even number of pixels, `TKEEP` and `TSTRB` are `4'b1111`. For an odd
number of pixels, the final word of each line uses `4'b0011` and the unused
upper half of `TDATA` is zero. AXI4-Stream `TVALID` remains asserted and the
payload remains stable while `TREADY` is low. The FIFO backpressures the pixel
domain; an inability to accept a word during an active frame records an
overflow and terminates that frame.
VSYNC rising after polarity normalization starts a frame. HREF qualifies
pixel sampling and its falling edge completes a line. Snapshot mode stops
after the next completed frame; continuous mode resets line and pixel counters
and captures the next frame. Crop coordinates are applied in the normalized
frame coordinate space, and a crop must fit within the configured frame size.
Snapshot completion remains stopped until a disabled configuration rearms it.
Source row width is checked against `FRAME_SIZE`, independently of crop
selection. The first complete pixel beyond the configured width reports
SIZE and ends the active frame before the column counter can wrap. HREF
falling with fewer complete pixels also reports SIZE. Compensating long and
short rows cannot form a valid frame merely because aggregate counts match.
Rows outside the crop are checked too. An incomplete final byte additionally
reports PARTIAL. These faults retain the existing sticky-error/event path;
they do not flush already accepted FIFO or DMA data. Software discards the
failed destination, drains/cancels DMA before source cleanup, and uses the
existing ABORT, W1C and acknowledged disable/enable rearm sequence.
An enabled source waiting for its first VSYNC does not treat the preceding
partial frame as captured data. ABORT/FLUSH execute only on an accepted
command handshake, not repeatedly from a retained CDC payload.
The PIO path and AXI4-Stream path are mutually exclusive at the output:
`RXDATA` pops a word only while stream output is disabled. The DMA HAL uses a
single-frame transfer length; descriptor rings, double buffering, and frame
metadata chaining are not part of the current ABI.
## DMA and Software Contract
The DMA MVP treats each full AXI4-Stream word as one 32-bit memory write.
It accepts only `TKEEP=4'hf`; an odd-pixel DVP line produces a partial final
word and therefore requires PIO handling or a future narrow-transfer/DRE DMA
extension. Software should configure the frame and crop dimensions, clear
stale error and interrupt state, program the product-selected `DVP_RX` destination
and capacity, enable stream output, and then start DVP capture. A frame that
reports overflow, synchronization, partial, DMA, or abort errors must not be
consumed by software.
The existing convenience helper uses Mini's channel-3 binding; Tiny must
supply its product transport configuration as specified in its integration
contract before that helper is advertised as supported there.
The public HAL exposes structured configuration, bounded status polling,
command and interrupt control, capability discovery, and a convenience API
for one-frame DMA capture. Applications should use `rs_dvp_configure()`,
`rs_dma_configure()`, `rs_dma_start()`, `rs_dvp_start()`, and
`rs_dvp_capture_dma()` rather than
accessing the registers directly.
The snapshot helpers derive transfer length from the current frame/crop
geometry. `word_capacity` bounds the destination; it is not a request to fill
the entire allocation. DMA rejects odd active widths, insufficient capacity
and address overflow before starting. PIO uses `ceil(width / 2) * height`
storage words and preserves the zero-padded final halfword in each odd row.
Success requires a new completed frame, exact pixel/word and DMA counts, an
empty FIFO and all destination responses. The helper initializes CRC options
explicitly. Tiny uses channel 2/four-beat bursts; Mini retains channel 3 and
its existing maximum-burst default.
Each convenience capture rearms the existing snapshot configuration through
an acknowledged non-flushing disable/enable sequence; callers need not repeat
`rs_dvp_configure()` between successful captures. PIO refuses a busy or suspended
product-selected DMA consumer before changing stream mode, interrupts or FIFO
contents. The source must also be inactive with an empty FIFO before rearm.
Recovery drains/aborts DMA before issuing a source abort that clears FIFO
storage. A failed DMA drain is returned to the caller without clearing the
source tail. `rs_dvp_abort()` refuses a busy product-selected DMA channel and
waits for an acknowledged abort, FIFO empty and error clear. Recovery timeout
is an error, not permission to reuse the buffer or rearm capture.
## Frozen PPALite Source and Route Contract
The [PPALite contract](ppalite.md) owns the processed pixel formats, derived
geometry, padded row pitch and buffer metadata. In PROCESS, DVP cropping stays
here, BYTE_SWAP/PIXEL_SWAP are zero, and the processor interprets the explicit
sensor byte order. Raw DVP still exposes two16-bit pixels per full stream word,
or a final halfword for an odd-width row. A legal halfword row tail must not be
confused with an invalid incomplete sensor pixel.
RAW DMA retains its even-active-width requirement. PROCESS may consume the
legal halfword tail and emit full padded words; that is a new downstream
operation, not a retroactive change to this source stream or a narrow-DMA mode.
Raw helpers on PPALite-capable Tiny must reject PROCESS before programming
capture, and the PPALite HAL must use its derived length rather than raw
`2 * width * height`. The unchanged Mini default channel3 is not copied to Tiny.
Route changes require stopped/acknowledged DVP configuration and cleanup,
empty source/processor endpoints and the full central-DMA barrier defined by
PPALite. The route is locked through capture/failed cleanup. Fixed PROCESS
may be retained between frames without stopping unrelated DMA clients.
The Tiny source guard freezes geometry/format/crop while owned, verifies
actual configuration/command CDC completion, and admits only the ordered
source start, non-flushing snapshot stop and isolated cleanup operations.
Diagnostic reads and the DVP register map remain unchanged.
Before processed capture is qualified, PPALITE-P2 must close or reuse current
R2 evidence for source correctness: real SYNC/SIZE/PARTIAL detection, exact
statistics beyond65535 words, coherent host-side active/configuration/status,
every repeated frame event, error epochs and snapshot stop/rearm behavior.
The R2-P9 candidate implements SYNC/SIZE/PARTIAL detection and a 32-bit word
counter transported through a 144-bit completed-frame mailbox. These source
guarantees still require matching validation; a downstream image/checksum
alone does not establish them. Minimal shared correctness fixes must retain
register offsets/meanings, the512 B payload FIFO and Mini compatibility.
The internal statistics layout is `{drop[31:0], words[31:0], pixels[31:0],
lines[15:0], frames[31:0]}`. The existing two-phase CDC carries the entire
144-bit snapshot; register offsets and the separate 128-bit configuration
channel are unchanged. VGA 640x480 at two bytes per pixel reports 153600
words rather than the former 16-bit wrapped value 22528. PIO counts one
additional halfword-bearing word at each odd-width row end.
Valid processed completion requires this source's full-frame/statistic/error
verdict, a checked non-flushing stop, processor counts and exact DMA length
including final memory write responses. PPALite PIPE_DONE or a last EOL is
not source-frame validity. Continue monitoring late source faults until the
capturing HAL finalizes its result; do not publish a plausible prefix.
The current ABORT command also requests source FIFO clearing. Failed processed
capture therefore closes admission, drains/aborts its DMA, acknowledges old
stream isolation, and only then issues source ABORT/FLUSH and processor cleanup.
Ordinary stream VALID cannot be withdrawn while a live consumer still owns
the beat. Missing PIXCLK or flush/reset acknowledgement retains failed-session
ownership; no fabricated padding or common-DMA reset substitutes for recovery.
The existing helper's early success-path abort must be corrected/avoided with
shared checked lifecycle helpers before either route is advertised as complete.
The original FIFO still receives raw sensor bytes. Its overflow budget is
not enlarged simply by dividing final image width or extracting Y. Preserve
raw-rate occupancy/margin calculations and validate any extra downstream
buffer credit explicitly. These new obligations have no passing evidence yet.
## Verification and Current Scope
The self-checking RTL test covers the versioned ABI, invalid APB4 accesses,
configuration writes, RGB565 packing, SOF/EOL framing, even and odd line
widths, AXI4-Stream output, frame statistics, and interrupt state handling.
The SBY target proves APB4 response behavior, request/response progress,
interrupt composition, and normal/error coverage for the register shell.
Host tests compile and execute the DVP directed test, while the IHP130
regression exercises behavior simulation, synthesis, netlist simulation, and
STA integration.
The current ABI does not include a native DVP AXI4 memory master, outstanding
AXI transactions, frame descriptor rings, line stride configuration, JPEG or
other compressed formats, external sync metadata, or multiple camera clocks.
Those features require a versioned ABI extension and explicit SoC routing;
existing register fields must not be repurposed for them.
## RTL Coding-Style Migration
The current DVP maintenance pass is limited to source organization and naming;
it does not change the register ABI, APB4 timing, AXI4-Stream framing, CDC
topology, pixel packing, interrupt behavior, or error semantics. Register
address macros retain their `APB4_DVP_*` names so the software HAL remains
compatible. Internal field macros use the `APB4_DVP__*` namespace, and local
parameters use UpperCamelCase. Internal configuration and command paths use
the project abbreviations `cfg` and `cmd`, while public APB4 and AXI4-Stream
interfaces remain unchanged.
Owned DVP modules use explicit named-port connections. Module ports and macro
definitions are manually column-aligned only inside narrow
`verilog_format: off/on` regions where Verible cannot preserve the alignment;
ordinary declarations, assignments, and instances remain formatter-managed.
The `dvp_camera` simulation model follows the same naming and named-port
rules. Its delayed falling-edge assignment is intentionally retained because
it models camera HREF timing and is not synthesizable IP behavior.
The local validation entry points are:
```sh
python3 -m pytest -q tests/test_dvp.py
make CONFIG=configs/ci/ihp130.mk formal-dvp
make format-check rtl-style-check-all rtl-readiness-check-all
```