feat: add DVP (Digital Video Processor) support and related formal verification

- Modified dma_core to accommodate a wider data width for FIFO operations.
- Updated filelists to include new DVP source files and formal verification files.
- Introduced formal verification modules for DVP functionality.
- Enhanced clock and reset domain configurations for DVP integration.
- Added DVP interrupt handling in the IRQ topology.
- Implemented a testbench for DVP to validate register access and frame processing.
- Updated software build scripts to include DVP-related source files.
- Adjusted the system topology to reflect the new DVP interrupt and IRQ mapping.
This commit is contained in:
maksyuki committed 2026-08-13 08:36:38 +08:00
1 parent 4e311b1a51
commit 3cf9e4c6a6
31 files changed
+1617 -332

No files matched your search

+2 -2
View File
@@ -262,7 +262,7 @@ endif
benchmark-report coremark-report \
pin-map check-pin-map soc-topology check-soc-topology user-extensions check-user-extensions \
check-clock-reset-domains tech-cell-test rtl-lint check-rtl-lint \
formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-clean formal-doctor
formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-dvp formal-clean formal-doctor
.NOTPARALLEL: setup
help:
@@ -290,7 +290,7 @@ help:
' check-clock-reset-domains validate the root clock/reset and CDC inventory' \
' rtl-lint | check-rtl-lint run/check strict Verilator RTL lint warnings' \
' formal | formal-bus | formal-rib-adapter | formal-rib2apb run SBY protocol proofs' \
' formal-sysctrl | formal-pll-rcu | formal-gpio | formal-ws2812 | formal-uart | formal-i2c | formal-timer | formal-clint run peripheral proofs' \
' formal-sysctrl | formal-pll-rcu | formal-gpio | formal-ws2812 | formal-uart | formal-i2c | formal-timer | formal-clint | formal-dvp run peripheral proofs' \
' formal-doctor check the SBY, Yosys, sv2v, and Bitwuzla formal toolchain' \
' benchmark-report run the memory/DMA profile and write meta/performance.json' \
' coremark-report run the quick CoreMark profile and write meta/coremark.json' \
-5
View File
@@ -183,11 +183,6 @@
// sdram
#define reg_sdram_clkdiv RS_SOC_REG32(RS_SOC_RIBP_SDRAM_BASE, UINT32_C(0x00))
#define reg_sdram_cfg RS_SOC_REG32(RS_SOC_RIBP_SDRAM_BASE, UINT32_C(0x04))
// dvp
#define reg_dvp_recven RS_SOC_REG32(RS_SOC_RIBP_DVP_BASE, UINT32_C(0x00))
#define reg_dvp_rxdata RS_SOC_REG32(RS_SOC_RIBP_DVP_BASE, UINT32_C(0x04))
#define reg_dvp_status RS_SOC_REG32(RS_SOC_RIBP_DVP_BASE, UINT32_C(0x08))
#define reg_dvp_stream_ctrl RS_SOC_REG32(RS_SOC_RIBP_DVP_BASE, UINT32_C(0x0C))
#define reg_user_ip_reg0 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x00))
#define reg_user_ip_reg1 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x04))
#define reg_user_ip_reg2 RS_SOC_REG32(RS_SOC_APB_USER_IP_BASE, UINT32_C(0x08))
+72
View File
@@ -0,0 +1,72 @@
#ifndef RETROSOC_HAL_DVP_H
#define RETROSOC_HAL_DVP_H
#include <stdbool.h>
#include <stdint.h>
#include <retrosoc/core/status.h>
#define RS_DVP_INTERRUPT_FRAME_START UINT32_C(0x00000001)
#define RS_DVP_INTERRUPT_LINE_DONE UINT32_C(0x00000002)
#define RS_DVP_INTERRUPT_FRAME_DONE UINT32_C(0x00000004)
#define RS_DVP_INTERRUPT_OVERFLOW UINT32_C(0x00000008)
#define RS_DVP_INTERRUPT_SYNC_ERROR UINT32_C(0x00000010)
#define RS_DVP_INTERRUPT_CONFIG UINT32_C(0x00000020)
#define RS_DVP_INTERRUPT_ABORTED UINT32_C(0x00000040)
#define RS_DVP_INTERRUPT_ALL UINT32_C(0x0000007F)
#define RS_DVP_ERROR_OVERFLOW UINT32_C(0x00000001)
#define RS_DVP_ERROR_SYNC UINT32_C(0x00000002)
#define RS_DVP_ERROR_SIZE UINT32_C(0x00000004)
#define RS_DVP_ERROR_PARTIAL UINT32_C(0x00000008)
#define RS_DVP_ERROR_CONFIG UINT32_C(0x00000010)
#define RS_DVP_ERROR_ABORT UINT32_C(0x00000020)
typedef enum {
RS_DVP_FORMAT_RGB565 = 0,
RS_DVP_FORMAT_YUV422 = 1,
} rs_dvp_format_t;
typedef struct {
rs_dvp_format_t format;
uint16_t frame_width;
uint16_t frame_height;
uint16_t crop_x;
uint16_t crop_y;
uint16_t crop_width;
uint16_t crop_height;
bool crop_enable;
bool snapshot;
bool stream_enable;
bool byte_swap;
bool pixel_swap;
bool vsync_active_low;
bool href_active_low;
bool pclk_falling;
} rs_dvp_config_t;
typedef struct {
uint32_t status;
uint32_t frame_count;
uint32_t line_count;
uint32_t pixel_count;
uint32_t word_count;
uint32_t drop_count;
uint32_t error_status;
uint32_t interrupt_state;
bool active;
} rs_dvp_status_t;
rs_status_t rs_dvp_probe(uint32_t *version, uint32_t *capability);
rs_status_t rs_dvp_configure(const rs_dvp_config_t *config);
rs_status_t rs_dvp_start(void);
rs_status_t rs_dvp_abort(void);
rs_status_t rs_dvp_flush(void);
rs_status_t rs_dvp_get_status(rs_dvp_status_t *status);
rs_status_t rs_dvp_interrupt_enable(uint32_t mask);
rs_status_t rs_dvp_interrupt_clear(uint32_t mask);
rs_status_t rs_dvp_interrupt_test(uint32_t mask);
rs_status_t rs_dvp_capture_dma(uintptr_t destination, uint32_t word_capacity, rs_timeout_t timeout);
void ip_dvp_test(void);
#endif
+182
View File
@@ -0,0 +1,182 @@
#include <stddef.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/hal/dma.h>
#include <retrosoc/hal/dvp.h>
#include <retrosoc/lib/printf.h>
#define RS_DVP_CTRL_OFFSET UINT32_C(0x000)
#define RS_DVP_RXDATA_OFFSET UINT32_C(0x004)
#define RS_DVP_STATUS_OFFSET UINT32_C(0x008)
#define RS_DVP_STREAM_CTRL_OFFSET UINT32_C(0x00C)
#define RS_DVP_FORMAT_OFFSET UINT32_C(0x010)
#define RS_DVP_SYNC_CFG_OFFSET UINT32_C(0x014)
#define RS_DVP_FRAME_SIZE_OFFSET UINT32_C(0x018)
#define RS_DVP_CROP_START_OFFSET UINT32_C(0x01C)
#define RS_DVP_CROP_SIZE_OFFSET UINT32_C(0x020)
#define RS_DVP_FRAME_COUNT_OFFSET UINT32_C(0x024)
#define RS_DVP_LINE_COUNT_OFFSET UINT32_C(0x028)
#define RS_DVP_PIXEL_COUNT_OFFSET UINT32_C(0x02C)
#define RS_DVP_WORD_COUNT_OFFSET UINT32_C(0x030)
#define RS_DVP_DROP_COUNT_OFFSET UINT32_C(0x034)
#define RS_DVP_ERROR_STATUS_OFFSET UINT32_C(0x038)
#define RS_DVP_INTR_STATE_OFFSET UINT32_C(0x03C)
#define RS_DVP_INTR_ENABLE_OFFSET UINT32_C(0x040)
#define RS_DVP_INTR_STATUS_OFFSET UINT32_C(0x044)
#define RS_DVP_INTR_TEST_OFFSET UINT32_C(0x048)
#define RS_DVP_COMMAND_OFFSET UINT32_C(0x04C)
#define RS_DVP_VERSION_OFFSET UINT32_C(0x0F8)
#define RS_DVP_CAPABILITY_OFFSET UINT32_C(0x0FC)
#define RS_DVP_CTRL_ENABLE_MASK UINT32_C(0x00000001)
#define RS_DVP_CTRL_SNAPSHOT_MASK UINT32_C(0x00000002)
#define RS_DVP_CTRL_CROP_MASK UINT32_C(0x00000004)
#define RS_DVP_COMMAND_ABORT_MASK UINT32_C(0x00000001)
#define RS_DVP_COMMAND_FLUSH_MASK UINT32_C(0x00000002)
#define RS_DVP_STATUS_ACTIVE_MASK UINT32_C(0x00000001)
#define RS_DVP_STATUS_ERROR_MASK UINT32_C(0x00000040)
static volatile uint32_t *rs_dvp_register(uint32_t offset) {
return (volatile uint32_t *)(RS_SOC_RIBP_DVP_BASE + (uintptr_t)offset);
}
static bool rs_dvp_config_valid(const rs_dvp_config_t *config) {
if ((config == NULL) || ((uint32_t)config->format > (uint32_t)RS_DVP_FORMAT_YUV422) ||
(config->frame_width == 0U) || (config->frame_height == 0U)) {
return false;
}
if (config->crop_enable &&
((config->crop_width == 0U) || (config->crop_height == 0U) ||
((uint32_t)config->crop_x + config->crop_width > config->frame_width) ||
((uint32_t)config->crop_y + config->crop_height > config->frame_height))) {
return false;
}
return true;
}
rs_status_t rs_dvp_probe(uint32_t *version, uint32_t *capability) {
if ((version == NULL) || (capability == NULL))
return RS_EINVAL;
*version = *rs_dvp_register(RS_DVP_VERSION_OFFSET);
*capability = *rs_dvp_register(RS_DVP_CAPABILITY_OFFSET);
return (*version == UINT32_C(0x00020000)) ? RS_OK : RS_EIO;
}
rs_status_t rs_dvp_configure(const rs_dvp_config_t *config) {
uint32_t format;
uint32_t sync;
uint32_t control;
if (!rs_dvp_config_valid(config))
return RS_EINVAL;
(void)rs_dvp_abort();
format = (uint32_t)config->format;
if (config->byte_swap)
format |= UINT32_C(0x00000004);
if (config->pixel_swap)
format |= UINT32_C(0x00000008);
sync = (config->vsync_active_low ? 1U : 0U) | (config->href_active_low ? 2U : 0U) |
(config->pclk_falling ? 4U : 0U);
control = (config->snapshot ? RS_DVP_CTRL_SNAPSHOT_MASK : 0U) |
(config->crop_enable ? RS_DVP_CTRL_CROP_MASK : 0U);
*rs_dvp_register(RS_DVP_FORMAT_OFFSET) = format;
*rs_dvp_register(RS_DVP_SYNC_CFG_OFFSET) = sync;
*rs_dvp_register(RS_DVP_FRAME_SIZE_OFFSET) =
((uint32_t)config->frame_height << 16) | config->frame_width;
*rs_dvp_register(RS_DVP_CROP_START_OFFSET) = ((uint32_t)config->crop_y << 16) | config->crop_x;
*rs_dvp_register(RS_DVP_CROP_SIZE_OFFSET) =
((uint32_t)config->crop_height << 16) | config->crop_width;
*rs_dvp_register(RS_DVP_STREAM_CTRL_OFFSET) = config->stream_enable ? 1U : 0U;
*rs_dvp_register(RS_DVP_ERROR_STATUS_OFFSET) = RS_DVP_ERROR_OVERFLOW | RS_DVP_ERROR_SYNC |
RS_DVP_ERROR_SIZE | RS_DVP_ERROR_PARTIAL |
RS_DVP_ERROR_CONFIG | RS_DVP_ERROR_ABORT;
*rs_dvp_register(RS_DVP_INTR_STATE_OFFSET) = RS_DVP_INTERRUPT_ALL;
*rs_dvp_register(RS_DVP_CTRL_OFFSET) = control;
return RS_OK;
}
rs_status_t rs_dvp_start(void) {
*rs_dvp_register(RS_DVP_CTRL_OFFSET) |= RS_DVP_CTRL_ENABLE_MASK;
return RS_OK;
}
rs_status_t rs_dvp_abort(void) {
*rs_dvp_register(RS_DVP_COMMAND_OFFSET) = RS_DVP_COMMAND_ABORT_MASK;
*rs_dvp_register(RS_DVP_CTRL_OFFSET) &= ~RS_DVP_CTRL_ENABLE_MASK;
return RS_OK;
}
rs_status_t rs_dvp_flush(void) {
*rs_dvp_register(RS_DVP_COMMAND_OFFSET) = RS_DVP_COMMAND_FLUSH_MASK;
return RS_OK;
}
rs_status_t rs_dvp_get_status(rs_dvp_status_t *status) {
if (status == NULL)
return RS_EINVAL;
status->status = *rs_dvp_register(RS_DVP_STATUS_OFFSET);
status->active = (status->status & RS_DVP_STATUS_ACTIVE_MASK) != 0U;
status->frame_count = *rs_dvp_register(RS_DVP_FRAME_COUNT_OFFSET);
status->line_count = *rs_dvp_register(RS_DVP_LINE_COUNT_OFFSET);
status->pixel_count = *rs_dvp_register(RS_DVP_PIXEL_COUNT_OFFSET);
status->word_count = *rs_dvp_register(RS_DVP_WORD_COUNT_OFFSET);
status->drop_count = *rs_dvp_register(RS_DVP_DROP_COUNT_OFFSET);
status->error_status = *rs_dvp_register(RS_DVP_ERROR_STATUS_OFFSET);
status->interrupt_state = *rs_dvp_register(RS_DVP_INTR_STATE_OFFSET);
return RS_OK;
}
rs_status_t rs_dvp_interrupt_enable(uint32_t mask) {
if ((mask & ~RS_DVP_INTERRUPT_ALL) != 0U)
return RS_EINVAL;
*rs_dvp_register(RS_DVP_INTR_ENABLE_OFFSET) = mask;
return RS_OK;
}
rs_status_t rs_dvp_interrupt_clear(uint32_t mask) {
if ((mask & ~RS_DVP_INTERRUPT_ALL) != 0U)
return RS_EINVAL;
*rs_dvp_register(RS_DVP_INTR_STATE_OFFSET) = mask;
return RS_OK;
}
rs_status_t rs_dvp_interrupt_test(uint32_t mask) {
if ((mask & ~RS_DVP_INTERRUPT_ALL) != 0U)
return RS_EINVAL;
*rs_dvp_register(RS_DVP_INTR_TEST_OFFSET) = mask;
return RS_OK;
}
rs_status_t rs_dvp_capture_dma(uintptr_t destination, uint32_t word_capacity,
rs_timeout_t timeout) {
rs_dvp_status_t status;
if ((destination == 0U) || (word_capacity == 0U))
return RS_EINVAL;
if ((rs_dma_config(RS_DMA_MODE_DVP_RX, RS_SOC_RIBP_DVP_BASE, 0U, destination, 1U,
word_capacity) != RS_OK) ||
(rs_dma_start() != RS_OK) || (rs_dvp_start() != RS_OK)) {
return RS_EIO;
}
while (timeout-- != 0U) {
if (rs_dvp_get_status(&status) != RS_OK)
return RS_EIO;
if ((status.error_status != 0U) ||
((status.interrupt_state & RS_DVP_INTERRUPT_FRAME_DONE) != 0U)) {
(void)rs_dvp_abort();
return (status.error_status == 0U) && (rs_dma_wait(timeout) == RS_OK) ? RS_OK : RS_EIO;
}
}
(void)rs_dvp_abort();
return RS_ETIMEOUT;
}
void ip_dvp_test(void) {
uint32_t version;
uint32_t capability;
printf("dvp test\n");
if (rs_dvp_probe(&version, &capability) == RS_OK) {
printf("dvp v%lx capability %lx\n", (unsigned long)version, (unsigned long)capability);
} else {
printf("dvp probe failed\n");
}
}
+2 -1
View File
@@ -113,7 +113,8 @@ void rs_app_info(void) {
printf(" 1 x CLINT @%p\n",
(void *)(uintptr_t)RS_SOC_RIBP_CLINT_BASE);
printf(" 1 x SDRAM @%p\n", (void *)&reg_sdram_clkdiv);
printf(" 1 x DVP @%p\n", (void *)&reg_dvp_recven);
printf(" 1 x DVP @%p\n",
(void *)(uintptr_t)RS_SOC_RIBP_DVP_BASE);
printf(" 1 x I2C1 @%p\n",
(void *)(uintptr_t)RS_SOC_RIBP_I2C1_BASE);
printf(" 1 x ARCHINFO @%p\n",
+1
View File
@@ -9,6 +9,7 @@ verification documentation for self-owned retroSoC peripheral IP.
- [gpio.md](gpio.md) defines the dual-window GPIO controller.
- [uart.md](uart.md) defines the RIBP UART V3 controller.
- [i2c.md](i2c.md) defines the dual RIBP I2C V2 controllers.
- [dvp.md](dvp.md) defines the RIBP DVP V2 capture controller.
The corresponding RTL and HAL implementations remain the executable sources
of truth. Update the affected document whenever an IP interface or register ABI
+138
View File
@@ -0,0 +1,138 @@
# RIBP DVP V2
The DVP V2 block captures an 8-bit parallel camera stream and presents the
captured pixels through an AXI4-Stream source. RIBP 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 |
| --- | --- |
| RIBP base address | `0x1000E000` |
| RIBP 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 mode | 11 DVP RX |
| ABI version | `0x00020000` |
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 | 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`.
The RTL ABI constants are maintained directly in
`rtl/ip/ribp/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 8-bit input 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.
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
DMA mode 11 treats each AXI4-Stream word as one 32-bit memory write and uses
the stream `TKEEP` value for the final partial word. Software should configure
the frame and crop dimensions, clear stale error and interrupt state, program
the DMA destination and capacity, enable stream output, and then start DVP
capture. A frame that reports overflow, synchronization, partial, or abort
errors must not be consumed by software.
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_config()`, `rs_dvp_start()`, and `rs_dvp_capture_dma()` rather than
accessing the registers directly.
## Verification and Current Scope
The self-checking RTL test covers the versioned ABI, invalid RIBP 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 RIBP 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.
+1 -1
View File
@@ -164,7 +164,7 @@ digraph mini_soc_block_diagram {
];
stream_io [
fillcolor="#FFF3C9",
label="AXI4-Stream data endpoints\nI2S TX/RX and DVP RX\nRIBP configuration with PIO fallback"
label="AXI4-Stream data endpoints\nI2S TX/RX and DVP RX\nSOF/EOL/TKEEP framing"
];
uart0 [
fillcolor="#F9E1E7",
+30 -30
View File
@@ -4,24 +4,24 @@
<!-- Generated by graphviz version 2.43.0 (0)
-->
<!-- Title: mini_soc_block_diagram Pages: 1 -->
<svg width="4505pt" height="963pt"
viewBox="0.00 0.00 4505.00 963.48" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="4467pt" height="963pt"
viewBox="0.00 0.00 4467.00 963.48" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 959.48)">
<title>mini_soc_block_diagram</title>
<text text-anchor="middle" x="132" y="-937.08" font-family="Arial" font-size="18.00">retroSoC Mini SoC Architecture</text>
<g id="clust1" class="cluster">
<title>cluster_rtl_ownership</title>
<path fill="transparent" stroke="#cbd5e1" stroke-dasharray="5,2" d="M3182,-821.48C3182,-821.48 3837,-821.48 3837,-821.48 3843,-821.48 3849,-827.48 3849,-833.48 3849,-833.48 3849,-907.48 3849,-907.48 3849,-913.48 3843,-919.48 3837,-919.48 3837,-919.48 3182,-919.48 3182,-919.48 3176,-919.48 3170,-913.48 3170,-907.48 3170,-907.48 3170,-833.48 3170,-833.48 3170,-827.48 3176,-821.48 3182,-821.48"/>
<text text-anchor="middle" x="3259.5" y="-907.48" font-family="Arial" font-size="10.00" fill="#334155">RTL ownership and validation profiles</text>
<path fill="transparent" stroke="#cbd5e1" stroke-dasharray="5,2" d="M3163,-821.48C3163,-821.48 3818,-821.48 3818,-821.48 3824,-821.48 3830,-827.48 3830,-833.48 3830,-833.48 3830,-907.48 3830,-907.48 3830,-913.48 3824,-919.48 3818,-919.48 3818,-919.48 3163,-919.48 3163,-919.48 3157,-919.48 3151,-913.48 3151,-907.48 3151,-907.48 3151,-833.48 3151,-833.48 3151,-827.48 3157,-821.48 3163,-821.48"/>
<text text-anchor="middle" x="3240.5" y="-907.48" font-family="Arial" font-size="10.00" fill="#334155">RTL ownership and validation profiles</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_asic</title>
<path fill="transparent" stroke="#111827" d="M160,-85C160,-85 4477,-85 4477,-85 4483,-85 4489,-91 4489,-97 4489,-97 4489,-781.48 4489,-781.48 4489,-787.48 4483,-793.48 4477,-793.48 4477,-793.48 160,-793.48 160,-793.48 154,-793.48 148,-787.48 148,-781.48 148,-781.48 148,-97 148,-97 148,-91 154,-85 160,-85"/>
<path fill="transparent" stroke="#111827" d="M160,-85C160,-85 4439,-85 4439,-85 4445,-85 4451,-91 4451,-97 4451,-97 4451,-781.48 4451,-781.48 4451,-787.48 4445,-793.48 4439,-793.48 4439,-793.48 160,-793.48 160,-793.48 154,-793.48 148,-787.48 148,-781.48 148,-781.48 148,-97 148,-97 148,-91 154,-85 160,-85"/>
<text text-anchor="middle" x="196.5" y="-778.28" font-family="Arial" font-size="14.00" fill="#111827">retrosoc_asic</text>
</g>
<g id="clust3" class="cluster">
<title>cluster_soc</title>
<path fill="transparent" stroke="#94a3b8" stroke-dasharray="5,2" d="M390,-103C390,-103 4459,-103 4459,-103 4465,-103 4471,-109 4471,-115 4471,-115 4471,-738.48 4471,-738.48 4471,-744.48 4465,-750.48 4459,-750.48 4459,-750.48 390,-750.48 390,-750.48 384,-750.48 378,-744.48 378,-738.48 378,-738.48 378,-115 378,-115 378,-109 384,-103 390,-103"/>
<path fill="transparent" stroke="#94a3b8" stroke-dasharray="5,2" d="M390,-103C390,-103 4421,-103 4421,-103 4427,-103 4433,-109 4433,-115 4433,-115 4433,-738.48 4433,-738.48 4433,-744.48 4427,-750.48 4421,-750.48 4421,-750.48 390,-750.48 390,-750.48 384,-750.48 378,-744.48 378,-738.48 378,-738.48 378,-115 378,-115 378,-109 384,-103 390,-103"/>
<text text-anchor="middle" x="407.5" y="-736.88" font-family="Arial" font-size="12.00" fill="#334155">retrosoc</text>
</g>
<!-- ext_clock -->
@@ -78,30 +78,30 @@
<!-- rtl_owned -->
<g id="node4" class="node">
<title>rtl_owned</title>
<path fill="#dcecf8" stroke="#4b5563" stroke-width="1.1" d="M3825,-869.48C3825,-869.48 3713,-869.48 3713,-869.48 3707,-869.48 3701,-863.48 3701,-857.48 3701,-857.48 3701,-845.48 3701,-845.48 3701,-839.48 3707,-833.48 3713,-833.48 3713,-833.48 3825,-833.48 3825,-833.48 3831,-833.48 3837,-839.48 3837,-845.48 3837,-845.48 3837,-857.48 3837,-857.48 3837,-863.48 3831,-869.48 3825,-869.48"/>
<text text-anchor="middle" x="3769" y="-854.48" font-family="Arial" font-size="10.00" fill="#111827">Self&#45;owned RTL</text>
<text text-anchor="middle" x="3769" y="-843.48" font-family="Arial" font-size="10.00" fill="#111827">Verible format + strict style</text>
<path fill="#dcecf8" stroke="#4b5563" stroke-width="1.1" d="M3806,-869.48C3806,-869.48 3694,-869.48 3694,-869.48 3688,-869.48 3682,-863.48 3682,-857.48 3682,-857.48 3682,-845.48 3682,-845.48 3682,-839.48 3688,-833.48 3694,-833.48 3694,-833.48 3806,-833.48 3806,-833.48 3812,-833.48 3818,-839.48 3818,-845.48 3818,-845.48 3818,-857.48 3818,-857.48 3818,-863.48 3812,-869.48 3806,-869.48"/>
<text text-anchor="middle" x="3750" y="-854.48" font-family="Arial" font-size="10.00" fill="#111827">Self&#45;owned RTL</text>
<text text-anchor="middle" x="3750" y="-843.48" font-family="Arial" font-size="10.00" fill="#111827">Verible format + strict style</text>
</g>
<!-- rtl_verif -->
<g id="node5" class="node">
<title>rtl_verif</title>
<path fill="#fce7f3" stroke="#4b5563" stroke-width="1.1" d="M3661.5,-869.48C3661.5,-869.48 3574.5,-869.48 3574.5,-869.48 3568.5,-869.48 3562.5,-863.48 3562.5,-857.48 3562.5,-857.48 3562.5,-845.48 3562.5,-845.48 3562.5,-839.48 3568.5,-833.48 3574.5,-833.48 3574.5,-833.48 3661.5,-833.48 3661.5,-833.48 3667.5,-833.48 3673.5,-839.48 3673.5,-845.48 3673.5,-845.48 3673.5,-857.48 3673.5,-857.48 3673.5,-863.48 3667.5,-869.48 3661.5,-869.48"/>
<text text-anchor="middle" x="3618" y="-854.48" font-family="Arial" font-size="10.00" fill="#111827">Formal and DV RTL</text>
<text text-anchor="middle" x="3618" y="-843.48" font-family="Arial" font-size="10.00" fill="#111827">verification lint profile</text>
<path fill="#fce7f3" stroke="#4b5563" stroke-width="1.1" d="M3642.5,-869.48C3642.5,-869.48 3555.5,-869.48 3555.5,-869.48 3549.5,-869.48 3543.5,-863.48 3543.5,-857.48 3543.5,-857.48 3543.5,-845.48 3543.5,-845.48 3543.5,-839.48 3549.5,-833.48 3555.5,-833.48 3555.5,-833.48 3642.5,-833.48 3642.5,-833.48 3648.5,-833.48 3654.5,-839.48 3654.5,-845.48 3654.5,-845.48 3654.5,-857.48 3654.5,-857.48 3654.5,-863.48 3648.5,-869.48 3642.5,-869.48"/>
<text text-anchor="middle" x="3599" y="-854.48" font-family="Arial" font-size="10.00" fill="#111827">Formal and DV RTL</text>
<text text-anchor="middle" x="3599" y="-843.48" font-family="Arial" font-size="10.00" fill="#111827">verification lint profile</text>
</g>
<!-- rtl_tech -->
<g id="node6" class="node">
<title>rtl_tech</title>
<path fill="#fff3c9" stroke="#4b5563" stroke-width="1.1" d="M3523.5,-869.48C3523.5,-869.48 3396.5,-869.48 3396.5,-869.48 3390.5,-869.48 3384.5,-863.48 3384.5,-857.48 3384.5,-857.48 3384.5,-845.48 3384.5,-845.48 3384.5,-839.48 3390.5,-833.48 3396.5,-833.48 3396.5,-833.48 3523.5,-833.48 3523.5,-833.48 3529.5,-833.48 3535.5,-839.48 3535.5,-845.48 3535.5,-845.48 3535.5,-857.48 3535.5,-857.48 3535.5,-863.48 3529.5,-869.48 3523.5,-869.48"/>
<text text-anchor="middle" x="3460" y="-854.48" font-family="Arial" font-size="10.00" fill="#111827">PDK and technology wrappers</text>
<text text-anchor="middle" x="3460" y="-843.48" font-family="Arial" font-size="10.00" fill="#111827">compile/elaboration profile</text>
<path fill="#fff3c9" stroke="#4b5563" stroke-width="1.1" d="M3504.5,-869.48C3504.5,-869.48 3377.5,-869.48 3377.5,-869.48 3371.5,-869.48 3365.5,-863.48 3365.5,-857.48 3365.5,-857.48 3365.5,-845.48 3365.5,-845.48 3365.5,-839.48 3371.5,-833.48 3377.5,-833.48 3377.5,-833.48 3504.5,-833.48 3504.5,-833.48 3510.5,-833.48 3516.5,-839.48 3516.5,-845.48 3516.5,-845.48 3516.5,-857.48 3516.5,-857.48 3516.5,-863.48 3510.5,-869.48 3504.5,-869.48"/>
<text text-anchor="middle" x="3441" y="-854.48" font-family="Arial" font-size="10.00" fill="#111827">PDK and technology wrappers</text>
<text text-anchor="middle" x="3441" y="-843.48" font-family="Arial" font-size="10.00" fill="#111827">compile/elaboration profile</text>
</g>
<!-- rtl_generated -->
<g id="node7" class="node">
<title>rtl_generated</title>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M3345.5,-869.48C3345.5,-869.48 3194.5,-869.48 3194.5,-869.48 3188.5,-869.48 3182.5,-863.48 3182.5,-857.48 3182.5,-857.48 3182.5,-845.48 3182.5,-845.48 3182.5,-839.48 3188.5,-833.48 3194.5,-833.48 3194.5,-833.48 3345.5,-833.48 3345.5,-833.48 3351.5,-833.48 3357.5,-839.48 3357.5,-845.48 3357.5,-845.48 3357.5,-857.48 3357.5,-857.48 3357.5,-863.48 3351.5,-869.48 3345.5,-869.48"/>
<text text-anchor="middle" x="3270" y="-854.48" font-family="Arial" font-size="10.00" fill="#111827">Generated and managed RTL</text>
<text text-anchor="middle" x="3270" y="-843.48" font-family="Arial" font-size="10.00" fill="#111827">locked source and regeneration flow</text>
<path fill="#e5e7eb" stroke="#4b5563" stroke-width="1.1" d="M3326.5,-869.48C3326.5,-869.48 3175.5,-869.48 3175.5,-869.48 3169.5,-869.48 3163.5,-863.48 3163.5,-857.48 3163.5,-857.48 3163.5,-845.48 3163.5,-845.48 3163.5,-839.48 3169.5,-833.48 3175.5,-833.48 3175.5,-833.48 3326.5,-833.48 3326.5,-833.48 3332.5,-833.48 3338.5,-839.48 3338.5,-845.48 3338.5,-845.48 3338.5,-857.48 3338.5,-857.48 3338.5,-863.48 3332.5,-869.48 3326.5,-869.48"/>
<text text-anchor="middle" x="3251" y="-854.48" font-family="Arial" font-size="10.00" fill="#111827">Generated and managed RTL</text>
<text text-anchor="middle" x="3251" y="-843.48" font-family="Arial" font-size="10.00" fill="#111827">locked source and regeneration flow</text>
</g>
<!-- fabric -->
<g id="node15" class="node">
@@ -268,18 +268,18 @@
<!-- stream_io -->
<g id="node24" class="node">
<title>stream_io</title>
<path fill="#fff3c9" stroke="#4b5563" stroke-width="1.1" d="M4442.5,-255C4442.5,-255 4287.5,-255 4287.5,-255 4281.5,-255 4275.5,-249 4275.5,-243 4275.5,-243 4275.5,-222 4275.5,-222 4275.5,-216 4281.5,-210 4287.5,-210 4287.5,-210 4442.5,-210 4442.5,-210 4448.5,-210 4454.5,-216 4454.5,-222 4454.5,-222 4454.5,-243 4454.5,-243 4454.5,-249 4448.5,-255 4442.5,-255"/>
<text text-anchor="middle" x="4365" y="-241" font-family="Arial" font-size="10.00" fill="#111827">AXI4&#45;Stream data endpoints</text>
<text text-anchor="middle" x="4365" y="-230" font-family="Arial" font-size="10.00" fill="#111827">I2S TX/RX and DVP RX</text>
<text text-anchor="middle" x="4365" y="-219" font-family="Arial" font-size="10.00" fill="#111827">RIBP configuration with PIO fallback</text>
<path fill="#fff3c9" stroke="#4b5563" stroke-width="1.1" d="M4405,-255C4405,-255 4287,-255 4287,-255 4281,-255 4275,-249 4275,-243 4275,-243 4275,-222 4275,-222 4275,-216 4281,-210 4287,-210 4287,-210 4405,-210 4405,-210 4411,-210 4417,-216 4417,-222 4417,-222 4417,-243 4417,-243 4417,-249 4411,-255 4405,-255"/>
<text text-anchor="middle" x="4346" y="-241" font-family="Arial" font-size="10.00" fill="#111827">AXI4&#45;Stream data endpoints</text>
<text text-anchor="middle" x="4346" y="-230" font-family="Arial" font-size="10.00" fill="#111827">I2S TX/RX and DVP RX</text>
<text text-anchor="middle" x="4346" y="-219" font-family="Arial" font-size="10.00" fill="#111827">SOF/EOL/TKEEP framing</text>
</g>
<!-- dma&#45;&gt;stream_io -->
<g id="edge13" class="edge">
<title>dma&#45;&gt;stream_io</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1916.58,-564.04C2167.25,-550.79 2835,-515.48 2835,-515.48 2835,-515.48 3342,-492.48 3342,-492.48 3342,-492.48 4021.7,-320.41 4273.03,-256.78"/>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M1916.58,-564.04C2167.25,-550.79 2835,-515.48 2835,-515.48 2835,-515.48 3342,-492.48 3342,-492.48 3342,-492.48 4031.56,-314.61 4267.75,-253.69"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="1916.14,-561.61 1909.28,-564.42 1916.4,-566.5 1916.14,-561.61"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="4273.79,-259.12 4279.98,-255.02 4272.59,-254.37 4273.79,-259.12"/>
<text text-anchor="middle" x="3770.5" y="-389.6" font-family="Arial" font-size="8.00" fill="#374151">AXI4&#45;Stream</text>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="4268.43,-256.04 4274.6,-251.92 4267.21,-251.29 4268.43,-256.04"/>
<text text-anchor="middle" x="3763.5" y="-389.6" font-family="Arial" font-size="8.00" fill="#374151">AXI4&#45;Stream</text>
</g>
<!-- uart0 -->
<g id="node25" class="node">
@@ -605,8 +605,8 @@
<!-- ribp_targets&#45;&gt;stream_io -->
<g id="edge62" class="edge">
<title>ribp_targets&#45;&gt;stream_io</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M2141,-250.5C2155.7,-253.15 2166,-255 2166,-255 2166,-255 2931.5,-287 2931.5,-287 2931.5,-287 2998.5,-287 2998.5,-287 2998.5,-287 4262,-255 4262,-255 4262,-255 4264.39,-254.48 4268.43,-253.59"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="4269.15,-255.95 4275.47,-252.06 4268.1,-251.16 4269.15,-255.95"/>
<path fill="none" stroke="#4b5563" stroke-width="1.2" stroke-dasharray="5,2" d="M2141,-250.5C2155.7,-253.15 2166,-255 2166,-255 2166,-255 2931.5,-287 2931.5,-287 2931.5,-287 2998.5,-287 2998.5,-287 2998.5,-287 4262,-255 4262,-255 4262,-255 4264.19,-254.41 4267.86,-253.43"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="4268.63,-255.76 4274.76,-251.58 4267.36,-251.03 4268.63,-255.76"/>
<text text-anchor="middle" x="2965" y="-289.6" font-family="Arial" font-size="8.00" fill="#374151">RIBP configuration</text>
</g>
<!-- ribp_targets&#45;&gt;gpio_controller -->
@@ -627,10 +627,10 @@
<!-- stream_io&#45;&gt;ext_io -->
<g id="edge64" class="edge">
<title>stream_io&#45;&gt;ext_io</title>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M4314.21,-206.7C4228.65,-164.91 4065,-85 4065,-85 4065,-85 3955.01,-63.86 3863.99,-46.37"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="4313.37,-209.01 4320.73,-209.88 4315.52,-204.61 4313.37,-209.01"/>
<path fill="none" stroke="#4b5563" stroke-width="1.2" d="M4297.94,-206.45C4217.67,-164.59 4065,-85 4065,-85 4065,-85 3955.01,-63.86 3863.99,-46.37"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="4297.2,-208.82 4304.54,-209.88 4299.46,-204.47 4297.2,-208.82"/>
<polygon fill="#4b5563" stroke="#4b5563" stroke-width="1.2" points="3864.26,-43.92 3856.92,-45.01 3863.33,-48.74 3864.26,-43.92"/>
<text text-anchor="middle" x="4236" y="-135.1" font-family="Arial" font-size="8.00" fill="#374151">I2S / DVP signals</text>
<text text-anchor="middle" x="4227" y="-135.1" font-family="Arial" font-size="8.00" fill="#374151">I2S / DVP signals</text>
</g>
<!-- uart0&#45;&gt;ext_io -->
<g id="edge65" class="edge">

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

+258 -164
View File
@@ -1,25 +1,9 @@
// Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
`ifndef RIBP_DVP_DEF_SV
`define RIBP_DVP_DEF_SV
// verilog_format: off
`define RIBP_DVP_RECVEN 8'h00
`define RIBP_DVP_RXDATA 8'h04
`define RIBP_DVP_STATUS 8'h08
`define RIBP_DVP_STREAM_CTRL 8'h0C
// verilog_format: on
`endif
`include "dvp_define.svh"
/* verilator lint_off DECLFILENAME */
interface dvp_if ();
logic pclk_i;
logic href_i;
@@ -28,178 +12,288 @@ interface dvp_if ();
modport dut(input pclk_i, input href_i, input vsync_i, input dat_i);
endinterface
/* verilator lint_on DECLFILENAME */
module ribp_dvp (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
ribp_if.slave ribp,
axi4_stream_if.source rx_axis,
dvp_if.dut dvp
input logic clk_i,
input logic rst_n_i,
ribp_if.slave ribp,
axi4_stream_if.source rx_axis,
dvp_if.dut dvp,
output logic irq_o
// verilog_format: on
);
// ribp
logic s_ribp_wr_hdshk, s_ribp_rd_hdshk;
logic s_ribp_ready_d, s_ribp_ready_q;
logic s_ribp_rdata_en;
logic [31:0] s_ribp_rdata_d, s_ribp_rdata_q;
// register
logic s_dvp_recven_en;
logic s_dvp_recven_d, s_dvp_recven_q;
logic s_dvp_stat_d, s_dvp_stat_q;
logic s_dvp_stream_en;
logic s_dvp_stream_d, s_dvp_stream_q;
// dvp clk
logic s_dvp_pclk_buf;
logic s_dvp_pclk_rst_n_sync;
logic s_dvp_recven_pclk_q;
// rx fifo
logic s_rx_push_valid, s_rx_full, s_rx_empty;
logic s_rx_pop_valid, s_rx_pop_ready;
logic [31:0] s_rx_push_data, s_rx_pop_data;
logic [7:0] s_rx_elem_num;
localparam int DVP_PAYLOAD_WIDTH = 42;
// ribp
assign s_ribp_wr_hdshk = ribp.valid && (~s_ribp_ready_q) && ribp.wstrb[0];
assign s_ribp_rd_hdshk = ribp.valid && (~s_ribp_ready_q) && (~(|ribp.wstrb));
assign ribp.ready = s_ribp_ready_q;
assign ribp.resp_err = 1'b0;
assign ribp.rdata = s_ribp_rdata_q;
logic [ 127:0] s_config_sys;
logic s_cfg_v_sys;
logic s_cfg_rdy_sys;
logic [ 127:0] s_cfg_pclk_q;
logic s_cfg_v_pclk;
logic s_cfg_rdy_pclk;
logic [ 1:0] s_cmd_sys;
logic [ 1:0] s_cmd_pclk;
logic s_cmd_v_pclk;
logic s_cmd_rdy_pclk;
logic s_cmd_rdy_sys;
logic s_cmd_v_sys;
logic [ 1:0] s_cmd_core;
logic s_cmd_abort_sys;
logic s_cmd_flush_sys;
logic s_pclk_inv;
logic s_pclk_buf;
logic s_pclk_rst_n;
logic s_pclk_sel;
logic s_pclk_falling;
logic s_active;
logic s_stream_en;
logic s_fifo_src_ready;
logic s_fifo_dst_valid;
logic [DVP_PAYLOAD_WIDTH-1:0] s_fifo_dst_data;
logic s_fifo_dst_ready;
logic s_fifo_src_clear;
logic s_fifo_src_clear_busy;
logic s_fifo_dst_clear_busy;
logic [ 31:0] s_fifo_rx_data;
logic s_rx_pop;
logic s_fifo_full_sys;
logic s_fifo_empty_sys;
logic s_frm_start_tgl;
logic s_line_done_tgl;
logic s_frm_done_tgl;
logic s_err_tgl;
logic s_frame_start_event;
logic s_line_done_event;
logic s_frame_done_event;
logic s_err_evt;
logic [ 5:0] s_err_flags_pclk;
logic [ 5:0] s_err_flags_sys;
logic [ 127:0] s_frm_stats_pclk;
logic s_frm_stats_v_pclk;
logic s_frm_stats_rdy_pclk;
logic [ 127:0] s_frm_stats_sys;
logic s_frm_stats_v_sys;
assign s_dvp_recven_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_DVP_RECVEN;
assign s_dvp_recven_d = ribp.wdata[0];
dffer #(
.DATA_WIDTH(1)
) u_dvp_recven_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_dvp_recven_en),
.dat_i (s_dvp_recven_d),
.dat_o (s_dvp_recven_q)
);
assign s_cmd_sys = {s_cmd_flush_sys, s_cmd_abort_sys};
// Commands are consumed in the pixel domain as soon as they cross the CDC.
assign s_cmd_rdy_pclk = !s_fifo_src_clear_busy;
assign s_pclk_falling = s_config_sys[106];
assign s_fifo_src_clear = s_cmd_pclk[`DVP_COMMAND_FLUSH] || s_cmd_pclk[`DVP_COMMAND_ABORT];
assign s_fifo_dst_ready = !s_fifo_dst_clear_busy && (s_stream_en ? rx_axis.tready : s_rx_pop);
assign s_fifo_rx_data = s_fifo_dst_data[31:0];
assign s_fifo_empty_sys = !s_fifo_dst_valid;
assign s_dvp_stream_en = s_ribp_wr_hdshk && ribp.addr[7:0] == `RIBP_DVP_STREAM_CTRL;
assign s_dvp_stream_d = ribp.wdata[0];
dffer #(
.DATA_WIDTH(1)
) u_dvp_stream_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_dvp_stream_en),
.dat_i (s_dvp_stream_d),
.dat_o (s_dvp_stream_q)
);
assign s_dvp_stat_d = s_rx_empty;
dffr #(
.DATA_WIDTH(1)
) u_dvp_status_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_dvp_stat_d),
.dat_o (s_dvp_stat_q)
);
assign s_ribp_ready_d = ribp.valid && (~s_ribp_ready_q);
dffr #(
.DATA_WIDTH(1)
) u_ribp_ready_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ribp_ready_d),
.dat_o (s_ribp_ready_q)
);
assign s_ribp_rdata_en = s_ribp_rd_hdshk;
always_comb begin
s_rx_pop_valid = s_dvp_stream_q && rx_axis.tvalid && rx_axis.tready;
s_ribp_rdata_d = s_ribp_rdata_q;
unique case (ribp.addr[7:0])
`RIBP_DVP_RECVEN: s_ribp_rdata_d = {31'd0, s_dvp_recven_q};
`RIBP_DVP_RXDATA: begin
if (s_ribp_rd_hdshk && !s_dvp_stream_q) begin
s_rx_pop_valid = 1'b1;
if (!s_rx_empty) s_ribp_rdata_d = s_rx_pop_data;
else s_ribp_rdata_d = '0;
end
end
`RIBP_DVP_STATUS: s_ribp_rdata_d = {31'd0, s_dvp_stat_q};
`RIBP_DVP_STREAM_CTRL: s_ribp_rdata_d = {31'd0, s_dvp_stream_q};
default: s_ribp_rdata_d = s_ribp_rdata_q;
endcase
end
dffer #(
.DATA_WIDTH(32)
) u_ribp_rdata_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_ribp_rdata_en),
.dat_i (s_ribp_rdata_d),
.dat_o (s_ribp_rdata_q)
);
assign rx_axis.tdata = s_rx_pop_data;
assign rx_axis.tkeep = '1;
assign rx_axis.tstrb = '1;
assign rx_axis.tlast = 1'b0;
assign rx_axis.tid = '0;
assign rx_axis.tdest = '0;
assign rx_axis.tuser = '0;
assign rx_axis.tvalid = s_dvp_stream_q && !s_rx_empty;
// clk buffer
tc_clk_buf u_dvp_pclk_clk_buf (
.clk_i(dvp.pclk_i),
.clk_o(s_dvp_pclk_buf)
.clk_o(s_pclk_buf)
);
tc_clk_inv u_dvp_pclk_inv (
.clk_i(s_pclk_buf),
.clk_o(s_pclk_inv)
);
tc_clk_mux2 u_dvp_pclk_mux (
.clk0_i (s_pclk_buf),
.clk1_i (s_pclk_inv),
.clk_sel_i(s_pclk_falling),
.clk_o (s_pclk_sel)
);
rst_sync #(
.STAGE(5)
) u_dvp_pclk_rst_sync (
.clk_i (s_dvp_pclk_buf),
.clk_i (s_pclk_sel),
.rst_n_i(rst_n_i),
.rst_n_o(s_dvp_pclk_rst_n_sync)
.rst_n_o(s_pclk_rst_n)
);
// The software enable is stable while a frame is captured, but it originates
// in the system domain and must be synchronized before it gates DVP writes.
dvp_reg u_dvp_reg (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.ribp (ribp),
.active_i (s_active),
.fifo_empty_i (s_fifo_empty_sys),
.fifo_full_i (s_fifo_full_sys),
.rx_data_i (s_fifo_rx_data),
.frame_start_i (s_frame_start_event),
.line_done_i (s_line_done_event),
.frame_done_i (s_frame_done_event),
.error_event_i (s_err_evt),
.error_flags_i (s_err_flags_sys),
.frame_stats_i (s_frm_stats_sys),
.frame_stats_valid_i(s_frm_stats_v_sys),
.config_o (s_config_sys),
.config_valid_o (s_cfg_v_sys),
.config_ready_i (s_cfg_rdy_sys),
.command_abort_o (s_cmd_abort_sys),
.command_flush_o (s_cmd_flush_sys),
.command_valid_o (s_cmd_v_sys),
.command_ready_i (s_cmd_rdy_sys),
.rx_pop_o (s_rx_pop),
.stream_enable_o (s_stream_en),
.irq_o (irq_o)
);
cdc_2phase #(
.DATA_WIDTH(128)
) u_dvp_config_cdc (
.src_clk_i (clk_i),
.src_rst_n_i(rst_n_i),
.src_data_i (s_config_sys),
.src_valid_i(s_cfg_v_sys),
.src_ready_o(s_cfg_rdy_sys),
.dst_clk_i (s_pclk_sel),
.dst_rst_n_i(s_pclk_rst_n),
.dst_data_o (s_cfg_pclk_q),
.dst_valid_o(s_cfg_v_pclk),
.dst_ready_i(s_cfg_rdy_pclk)
);
cdc_2phase #(
.DATA_WIDTH(2)
) u_dvp_command_cdc (
.src_clk_i (clk_i),
.src_rst_n_i(rst_n_i),
.src_data_i (s_cmd_sys),
.src_valid_i(s_cmd_v_sys),
.src_ready_o(s_cmd_rdy_sys),
.dst_clk_i (s_pclk_sel),
.dst_rst_n_i(s_pclk_rst_n),
.dst_data_o (s_cmd_pclk),
.dst_valid_o(s_cmd_v_pclk),
.dst_ready_i(s_cmd_rdy_pclk)
);
cdc_fifo_warm_flush #(
.DATA_WIDTH (DVP_PAYLOAD_WIDTH),
.BUFFER_DEPTH(128)
) u_dvp_payload_fifo (
.src_clk_i (s_pclk_sel),
.src_rst_n_i (s_pclk_rst_n),
.src_clear_i (s_fifo_src_clear),
.src_clear_busy_o(s_fifo_src_clear_busy),
.src_data_i (s_core_push_data),
.src_valid_i (s_core_push_valid),
.src_ready_o (s_fifo_src_ready),
.dst_clk_i (clk_i),
.dst_rst_n_i (rst_n_i),
.dst_clear_busy_o(s_fifo_dst_clear_busy),
.dst_data_o (s_fifo_dst_data),
.dst_valid_o (s_fifo_dst_valid),
.dst_ready_i (s_fifo_dst_ready)
);
logic [DVP_PAYLOAD_WIDTH-1:0] s_core_push_data;
logic s_core_push_valid;
assign s_cmd_core = s_cmd_v_pclk ? s_cmd_pclk : 2'b00;
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(1)
) u_dvp_recven_cdc_sync (
.clk_i (s_dvp_pclk_buf),
.rst_n_i(s_dvp_pclk_rst_n_sync),
.dat_i (s_dvp_recven_q),
.dat_o (s_dvp_recven_pclk_q)
) u_fifo_full_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (!s_fifo_src_ready),
.dat_o (s_fifo_full_sys)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(6)
) u_error_flags_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_flags_pclk),
.dat_o (s_err_flags_sys)
);
async_fifo #(
.DATA_WIDTH (32),
.DEPTH_POWER(7)
) u_rx_async_fifo (
.wr_clk_i (s_dvp_pclk_buf),
.wr_rst_n_i(s_dvp_pclk_rst_n_sync),
.wr_en_i (s_rx_push_valid & s_dvp_recven_pclk_q),
.wr_data_i (s_rx_push_data),
.wr_full_o (s_rx_full),
.rd_clk_i (clk_i),
.rd_rst_n_i(rst_n_i),
.rd_en_i (s_rx_pop_valid),
.rd_data_o (s_rx_pop_data),
.rd_empty_o(s_rx_empty),
.elem_num_o(s_rx_elem_num)
/* verilator lint_off PINCONNECTEMPTY */
edge_det #(
.STAGE(2)
) u_frame_start_event (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_frm_start_tgl),
.dat_o (),
.re_o (s_frame_start_event),
.fe_o ()
);
edge_det #(
.STAGE(2)
) u_line_done_event (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_line_done_tgl),
.dat_o (),
.re_o (s_line_done_event),
.fe_o ()
);
edge_det #(
.STAGE(2)
) u_frame_done_event (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_frm_done_tgl),
.dat_o (),
.re_o (s_frame_done_event),
.fe_o ()
);
edge_det #(
.STAGE(2)
) u_error_event (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_tgl),
.dat_o (),
.re_o (s_err_evt),
.fe_o ()
);
/* verilator lint_on PINCONNECTEMPTY */
cdc_2phase #(
.DATA_WIDTH(128)
) u_dvp_stats_cdc (
.src_clk_i (s_pclk_sel),
.src_rst_n_i(s_pclk_rst_n),
.src_data_i (s_frm_stats_pclk),
.src_valid_i(s_frm_stats_v_pclk),
.src_ready_o(s_frm_stats_rdy_pclk),
.dst_clk_i (clk_i),
.dst_rst_n_i(rst_n_i),
.dst_data_o (s_frm_stats_sys),
.dst_valid_o(s_frm_stats_v_sys),
.dst_ready_i(1'b1)
);
assign rx_axis.tdata = s_fifo_dst_data[31:0];
assign rx_axis.tkeep = s_fifo_dst_data[39:36];
assign rx_axis.tstrb = s_fifo_dst_data[35:32];
assign rx_axis.tlast = s_fifo_dst_data[40];
assign rx_axis.tid = '0;
assign rx_axis.tdest = '0;
assign rx_axis.tuser = s_fifo_dst_data[41];
assign rx_axis.tvalid = s_stream_en && s_fifo_dst_valid;
dvp_core u_dvp_core (
.clk_i (s_dvp_pclk_buf),
.rst_n_i (s_dvp_pclk_rst_n_sync),
.dvp_href_i (dvp.href_i),
.dvp_vsync_i(dvp.vsync_i),
.dvp_dat_i (dvp.dat_i),
.wr_en_o (s_rx_push_valid),
.rgb_dat_o (s_rx_push_data)
.clk_i (s_pclk_sel),
.rst_n_i (s_pclk_rst_n),
.config_i (s_cfg_pclk_q),
.config_valid_i (s_cfg_v_pclk),
.command_i (s_cmd_core),
.config_ready_o (s_cfg_rdy_pclk),
.push_ready_i (s_fifo_src_ready),
.push_valid_o (s_core_push_valid),
.push_data_o (s_core_push_data),
.active_o (s_active),
.frame_start_toggle_o(s_frm_start_tgl),
.line_done_toggle_o (s_line_done_tgl),
.frame_done_toggle_o (s_frm_done_tgl),
.error_toggle_o (s_err_tgl),
.error_flags_o (s_err_flags_pclk),
.frame_stats_o (s_frm_stats_pclk),
.frame_stats_valid_o (s_frm_stats_v_pclk),
.frame_stats_ready_i (s_frm_stats_rdy_pclk),
.href_i (dvp.href_i),
.vsync_i (dvp.vsync_i),
.dat_i (dvp.dat_i)
);
endmodule
+237 -106
View File
@@ -1,120 +1,251 @@
// Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
`include "dvp_define.svh"
module dvp_core (
input logic clk_i,
input logic rst_n_i,
input logic dvp_href_i,
input logic dvp_vsync_i,
input logic [ 7:0] dvp_dat_i,
output logic wr_en_o,
output logic [31:0] rgb_dat_o
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
/* verilator lint_off UNUSEDSIGNAL */
input logic [127:0] config_i,
/* verilator lint_on UNUSEDSIGNAL */
input logic config_valid_i,
input logic [ 1:0] command_i,
output logic config_ready_o,
input logic push_ready_i,
output logic push_valid_o,
output logic [ 41:0] push_data_o,
output logic active_o,
output logic frame_start_toggle_o,
output logic line_done_toggle_o,
output logic frame_done_toggle_o,
output logic error_toggle_o,
output logic [ 5:0] error_flags_o,
output logic [127:0] frame_stats_o,
output logic frame_stats_valid_o,
input logic frame_stats_ready_i,
input logic href_i,
input logic vsync_i,
input logic [ 7:0] dat_i
// verilog_format: on
);
logic s_en;
logic s_snap;
logic s_crop_en;
logic s_byte_swap;
logic [ 1:0] s_format;
logic s_pixel_swap;
logic [15:0] s_frm_width;
logic [15:0] s_frm_height;
logic [15:0] s_crop_x;
logic [15:0] s_crop_y;
logic [15:0] s_crop_width;
logic [15:0] s_crop_height;
logic s_vsync_low;
logic s_href_low;
// localparam FRAME_WAIT = 4'd3; // for sim
localparam FRAME_WAIT = 4'd12;
logic s_vsync_q;
logic s_href_q;
logic s_frm_active_q;
logic [15:0] s_line_q;
logic [15:0] s_pixel_q;
logic s_byte_phase_q;
logic s_word_phase_q;
logic [ 7:0] s_first_byte_q;
logic [15:0] s_word_q;
logic [31:0] s_frm_seq_q;
logic [31:0] s_pixel_total_q;
logic [15:0] s_word_total_q;
logic [31:0] s_drop_count_q;
logic [ 5:0] s_err_flags_q;
logic s_out_valid_q;
logic [41:0] s_out_data_q;
logic s_stat_pending_q;
logic s_first_word_q;
logic s_vsync_active;
logic s_href_active;
logic s_vsync_rise;
logic s_href_fall;
logic s_crop_line;
logic s_crop_pixel;
logic s_last_pixel;
logic [15:0] s_pixel_word;
logic s_dvp_vsync_re;
logic s_frame_cnt_en;
logic [3:0] s_frame_cnt_d, s_frame_cnt_q;
logic s_frame_valid_en;
logic s_frame_valid_d, s_frame_valid_q;
logic [1:0] s_pix_cnt_d, s_pix_cnt_q;
logic s_rgb_flag_d, s_rgb_flag_q;
logic s_rgb_data_en;
logic [31:0] s_rgb_data_d, s_rgb_data_q;
assign s_vsync_active = s_vsync_low ? !vsync_i : vsync_i;
assign s_href_active = s_href_low ? !href_i : href_i;
assign s_vsync_rise = s_vsync_active && !s_vsync_q;
assign s_href_fall = s_href_q && !s_href_active;
assign s_crop_line = !s_crop_en ||
((s_line_q >= s_crop_y) &&
(s_line_q < (s_crop_y + s_crop_height)));
assign s_crop_pixel = !s_crop_en ||
((s_pixel_q >= s_crop_x) &&
(s_pixel_q < (s_crop_x + s_crop_width)));
assign s_last_pixel = s_crop_en ?
(s_pixel_q + 16'd1 >= s_crop_x + s_crop_width) :
(s_pixel_q + 16'd1 >= s_frm_width);
assign s_pixel_word = s_pixel_swap ?
{s_byte_swap ? dat_i : s_first_byte_q,
s_byte_swap ? s_first_byte_q : dat_i} :
(s_byte_swap ? {dat_i, s_first_byte_q} :
{s_first_byte_q, dat_i});
assign config_ready_o = !s_stat_pending_q;
assign push_valid_o = s_out_valid_q;
assign push_data_o = s_out_data_q;
assign active_o = s_frm_active_q;
assign wr_en_o = s_frame_valid_q & s_rgb_flag_q;
assign rgb_dat_o = s_rgb_data_q;
edge_det_sync_re #(
.DATA_WIDTH(1)
) u_dvp_vsync_edge_det_sync_re (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (dvp_vsync_i),
.re_o (s_dvp_vsync_re)
);
assign s_frame_cnt_en = (s_frame_cnt_q < FRAME_WAIT) && s_dvp_vsync_re;
assign s_frame_cnt_d = s_frame_cnt_q + 4'd1;
dffer #(
.DATA_WIDTH(4)
) u_frame_cnt_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_frame_cnt_en),
.dat_i (s_frame_cnt_d),
.dat_o (s_frame_cnt_q)
);
assign s_frame_valid_en = (s_frame_cnt_q == FRAME_WAIT) && s_dvp_vsync_re;
assign s_frame_valid_d = 1'b1;
dffer #(
.DATA_WIDTH(1)
) u_frame_valid_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_frame_valid_en),
.dat_i (s_frame_valid_d),
.dat_o (s_frame_valid_q)
);
assign s_pix_cnt_d = dvp_href_i ? s_pix_cnt_q + 2'd1 : '0;
dffr #(
.DATA_WIDTH(2)
) u_pix_cnt_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_pix_cnt_d),
.dat_o (s_pix_cnt_q)
);
assign s_rgb_flag_d = s_pix_cnt_q == 2'd3;
dffr #(
.DATA_WIDTH(1)
) u_rgb_flag_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_rgb_flag_d),
.dat_o (s_rgb_flag_q)
);
assign s_rgb_data_en = dvp_href_i;
always_comb begin
s_rgb_data_d = s_rgb_data_q;
if (s_pix_cnt_q == 2'd0) s_rgb_data_d[15:8] = dvp_dat_i;
else if (s_pix_cnt_q == 2'd1) s_rgb_data_d[7:0] = dvp_dat_i;
else if (s_pix_cnt_q == 2'd2) s_rgb_data_d[31:24] = dvp_dat_i;
else if (s_pix_cnt_q == 2'd3) s_rgb_data_d[23:16] = dvp_dat_i;
s_en = config_i[96];
s_snap = config_i[97];
s_crop_en = config_i[98];
s_byte_swap = config_i[102];
s_vsync_low = config_i[104];
s_href_low = config_i[105];
s_frm_width = config_i[15:0];
s_frm_height = config_i[31:16];
s_crop_x = config_i[47:32];
s_crop_y = config_i[63:48];
s_crop_width = config_i[79:64];
s_crop_height = config_i[95:80];
s_format = config_i[101:100];
s_pixel_swap = config_i[103];
end
dffer #(
.DATA_WIDTH(32)
) u_rgb_data_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_rgb_data_en),
.dat_i (s_rgb_data_d),
.dat_o (s_rgb_data_q)
);
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_vsync_q <= 1'b0;
s_href_q <= 1'b0;
s_frm_active_q <= 1'b0;
s_line_q <= '0;
s_pixel_q <= '0;
s_byte_phase_q <= 1'b0;
s_word_phase_q <= 1'b0;
s_first_byte_q <= '0;
s_word_q <= '0;
s_frm_seq_q <= '0;
s_pixel_total_q <= '0;
s_word_total_q <= '0;
s_drop_count_q <= '0;
s_err_flags_q <= '0;
s_out_valid_q <= 1'b0;
s_out_data_q <= '0;
s_stat_pending_q <= 1'b0;
s_first_word_q <= 1'b0;
frame_start_toggle_o <= 1'b0;
line_done_toggle_o <= 1'b0;
frame_done_toggle_o <= 1'b0;
error_toggle_o <= 1'b0;
frame_stats_o <= '0;
frame_stats_valid_o <= 1'b0;
end else begin
s_vsync_q <= s_vsync_active;
s_href_q <= s_href_active;
frame_stats_valid_o <= 1'b0;
if (s_out_valid_q && push_ready_i) s_out_valid_q <= 1'b0;
if (s_stat_pending_q && frame_stats_ready_i) begin
s_stat_pending_q <= 1'b0;
frame_stats_valid_o <= 1'b1;
end
if (config_valid_i && !s_frm_active_q) begin
if ((s_frm_width == 0) || (s_frm_height == 0) || (s_format > 2'd1) ||
(s_crop_en && ((s_crop_width == 0) || (s_crop_height == 0)))) begin
s_err_flags_q[`DVP_ERROR_CONFIG] <= 1'b1;
error_toggle_o <= ~error_toggle_o;
end
if (!s_en) s_frm_active_q <= 1'b0;
end
if (command_i[`DVP_COMMAND_ABORT]) begin
s_frm_active_q <= 1'b0;
s_err_flags_q[`DVP_ERROR_ABORT] <= 1'b1;
error_toggle_o <= ~error_toggle_o;
end
if (command_i[`DVP_COMMAND_FLUSH]) begin
s_out_valid_q <= 1'b0;
s_byte_phase_q <= 1'b0;
s_word_phase_q <= 1'b0;
end
if (s_en && s_vsync_rise) begin
if (s_frm_active_q) begin
frame_done_toggle_o <= ~frame_done_toggle_o;
frame_stats_o <= {
s_drop_count_q, s_word_total_q, s_pixel_total_q, s_line_q, s_frm_seq_q + 1'b1
};
s_stat_pending_q <= 1'b1;
s_frm_seq_q <= s_frm_seq_q + 1'b1;
if (s_snap) begin
s_frm_active_q <= 1'b0;
end else begin
s_line_q <= '0;
s_pixel_q <= '0;
s_pixel_total_q <= '0;
s_word_total_q <= '0;
end
end else begin
s_frm_active_q <= 1'b1;
frame_start_toggle_o <= ~frame_start_toggle_o;
s_first_word_q <= 1'b1;
s_line_q <= '0;
s_pixel_q <= '0;
s_pixel_total_q <= '0;
s_word_total_q <= '0;
end
end
if (s_frm_active_q && s_href_active) begin
if (!s_byte_phase_q) begin
s_first_byte_q <= dat_i;
s_byte_phase_q <= 1'b1;
end else begin
if (s_crop_line && s_crop_pixel) begin
if (!s_word_phase_q) begin
s_word_q <= s_pixel_word;
s_word_phase_q <= 1'b1;
end else if (!s_out_valid_q || push_ready_i) begin
s_out_data_q <= {
s_first_word_q, s_last_pixel, 4'b1111, 4'b1111, s_pixel_word, s_word_q
};
s_out_valid_q <= 1'b1;
s_first_word_q <= 1'b0;
s_word_phase_q <= 1'b0;
s_word_total_q <= s_word_total_q + 1'b1;
end else begin
s_err_flags_q[`DVP_ERROR_OVERFLOW] <= 1'b1;
s_drop_count_q <= s_drop_count_q + 1'b1;
error_toggle_o <= ~error_toggle_o;
s_frm_active_q <= 1'b0;
end
s_pixel_total_q <= s_pixel_total_q + 1'b1;
end
s_byte_phase_q <= 1'b0;
s_pixel_q <= s_pixel_q + 1'b1;
end
end
if (s_frm_active_q && s_href_fall) begin
line_done_toggle_o <= ~line_done_toggle_o;
s_line_q <= s_line_q + 1'b1;
s_pixel_q <= '0;
s_byte_phase_q <= 1'b0;
if (s_word_phase_q && (!s_out_valid_q || push_ready_i)) begin
s_out_data_q <= {s_first_word_q, 1'b1, 4'b0011, 4'b0011, 16'd0, s_word_q[15:0]};
s_out_valid_q <= 1'b1;
s_first_word_q <= 1'b0;
s_word_phase_q <= 1'b0;
s_word_total_q <= s_word_total_q + 1'b1;
end else if (s_word_phase_q) begin
s_err_flags_q[`DVP_ERROR_OVERFLOW] <= 1'b1;
error_toggle_o <= ~error_toggle_o;
s_frm_active_q <= 1'b0;
end
end
end
end
assign error_flags_o = s_err_flags_q;
endmodule
+63
View File
@@ -0,0 +1,63 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
`ifndef RIBP_DVP_DEFINE_SVH
`define RIBP_DVP_DEFINE_SVH
// verilog_format: off
`define RIBP_DVP_CTRL 12'h000
`define RIBP_DVP_RXDATA 12'h004
`define RIBP_DVP_STATUS 12'h008
`define RIBP_DVP_STREAM_CTRL 12'h00C
`define RIBP_DVP_FORMAT 12'h010
`define RIBP_DVP_SYNC_CFG 12'h014
`define RIBP_DVP_FRAME_SIZE 12'h018
`define RIBP_DVP_CROP_START 12'h01C
`define RIBP_DVP_CROP_SIZE 12'h020
`define RIBP_DVP_FRAME_COUNT 12'h024
`define RIBP_DVP_LINE_COUNT 12'h028
`define RIBP_DVP_PIXEL_COUNT 12'h02C
`define RIBP_DVP_WORD_COUNT 12'h030
`define RIBP_DVP_DROP_COUNT 12'h034
`define RIBP_DVP_ERROR_STATUS 12'h038
`define RIBP_DVP_INTR_STATE 12'h03C
`define RIBP_DVP_INTR_ENABLE 12'h040
`define RIBP_DVP_INTR_STATUS 12'h044
`define RIBP_DVP_INTR_TEST 12'h048
`define RIBP_DVP_COMMAND 12'h04C
`define RIBP_DVP_IP_VERSION 12'h0F8
`define RIBP_DVP_CAPABILITY 12'h0FC
`define DVP_CTRL_ENABLE 0
`define DVP_CTRL_SNAPSHOT 1
`define DVP_CTRL_CROP_ENABLE 2
`define DVP_STREAM_ENABLE 0
`define DVP_FORMAT_SELECT 1:0
`define DVP_FORMAT_BYTE_SWAP 2
`define DVP_FORMAT_PIXEL_SWAP 3
`define DVP_SYNC_VSYNC_LOW 0
`define DVP_SYNC_HREF_LOW 1
`define DVP_SYNC_PCLK_FALLING 2
`define DVP_INTR_FRAME_START 0
`define DVP_INTR_LINE_DONE 1
`define DVP_INTR_FRAME_DONE 2
`define DVP_INTR_OVERFLOW 3
`define DVP_INTR_SYNC_ERROR 4
`define DVP_INTR_CONFIG_ERROR 5
`define DVP_INTR_ABORTED 6
`define DVP_INTR_ALL 7'h7F
`define DVP_ERROR_OVERFLOW 0
`define DVP_ERROR_SYNC 1
`define DVP_ERROR_SIZE 2
`define DVP_ERROR_PARTIAL 3
`define DVP_ERROR_CONFIG 4
`define DVP_ERROR_ABORT 5
`define DVP_ERROR_ALL 6'h3F
`define DVP_COMMAND_ABORT 0
`define DVP_COMMAND_FLUSH 1
// verilog_format: on
`endif
+310
View File
@@ -0,0 +1,310 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
`include "dvp_define.svh"
module dvp_reg (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
ribp_if.slave ribp,
input logic active_i,
input logic fifo_empty_i,
input logic fifo_full_i,
input logic [31:0] rx_data_i,
input logic frame_start_i,
input logic line_done_i,
input logic frame_done_i,
input logic error_event_i,
input logic [ 5:0] error_flags_i,
input logic [127:0] frame_stats_i,
input logic frame_stats_valid_i,
output logic [127:0] config_o,
output logic config_valid_o,
output logic command_abort_o,
output logic command_flush_o,
output logic command_valid_o,
input logic command_ready_i,
output logic rx_pop_o,
input logic config_ready_i,
output logic stream_enable_o,
output logic irq_o
// verilog_format: on
);
localparam logic [31:0] IP_VERSION = 32'h0002_0000;
localparam logic [31:0] CAPABILITY = 32'h0000_3F7F;
localparam logic [31:0] CTRL_MASK = 32'h0000_0007;
localparam logic [31:0] STREAM_MASK = 32'h0000_0001;
localparam logic [31:0] FORMAT_MASK = 32'h0000_000F;
localparam logic [31:0] SYNC_MASK = 32'h0000_0007;
logic s_req;
logic s_write;
logic s_accept;
logic [11:0] s_offset;
logic s_aligned;
logic s_access_err;
logic s_ready_q;
logic s_resp_err_d, s_resp_err_q;
logic [31:0] s_rdata_d, s_rdata_q;
logic [31:0] s_ctrl_d, s_ctrl_q;
logic [31:0] s_stream_d, s_stream_q;
logic [31:0] s_format_d, s_format_q;
logic [31:0] s_sync_d, s_sync_q;
logic [31:0] s_frame_size_d, s_frame_size_q;
logic [31:0] s_crop_start_d, s_crop_start_q;
logic [31:0] s_crop_size_d, s_crop_size_q;
logic [5:0] s_error_q;
logic [6:0] s_intr_state_q;
logic [6:0] s_intr_enable_q;
logic [31:0] s_frame_count_q, s_line_count_q;
logic [31:0] s_pixel_count_q, s_word_count_q, s_drop_count_q;
logic [31:0] s_config_seq_d, s_config_seq_q;
logic [31:0] s_config_sent_q;
logic [ 1:0] s_command_q;
function automatic logic [31:0] merge_wstrb(input logic [31:0] current, input logic [31:0] value,
input logic [3:0] strobe);
logic [31:0] merged;
begin
merged = current;
for (int index = 0; index < 4; index++) begin
if (strobe[index]) merged[index*8+:8] = value[index*8+:8];
end
return merged;
end
endfunction
assign s_req = ribp.valid && !s_ready_q;
assign s_write = |ribp.wstrb;
assign s_accept = s_req;
assign s_offset = ribp.addr[11:0];
assign s_aligned = ribp.addr[1:0] == 2'b00;
assign ribp.ready = s_ready_q;
assign ribp.rdata = s_rdata_q;
assign ribp.resp_err = s_resp_err_q;
assign stream_enable_o = s_stream_q[`DVP_STREAM_ENABLE];
always_comb begin
config_o = '0;
config_o[15:0] = s_frame_size_q[15:0];
config_o[31:16] = s_frame_size_q[31:16];
config_o[47:32] = s_crop_start_q[15:0];
config_o[63:48] = s_crop_start_q[31:16];
config_o[79:64] = s_crop_size_q[15:0];
config_o[95:80] = s_crop_size_q[31:16];
config_o[96] = s_ctrl_q[`DVP_CTRL_ENABLE];
config_o[97] = s_ctrl_q[`DVP_CTRL_SNAPSHOT];
config_o[98] = s_ctrl_q[`DVP_CTRL_CROP_ENABLE];
config_o[99] = s_stream_q[`DVP_STREAM_ENABLE];
config_o[103:100] = s_format_q[3:0];
config_o[106:104] = s_sync_q[2:0];
end
assign config_valid_o = s_config_seq_q != s_config_sent_q;
assign rx_pop_o = s_accept && !s_write && !stream_enable_o && (s_offset == `RIBP_DVP_RXDATA);
assign irq_o = |(s_intr_state_q & s_intr_enable_q);
always_comb begin
s_access_err = !s_aligned;
s_rdata_d = 32'd0;
if (s_aligned) begin
unique case (s_offset)
`RIBP_DVP_CTRL: s_rdata_d = s_ctrl_q;
`RIBP_DVP_RXDATA: s_rdata_d = rx_data_i;
`RIBP_DVP_STATUS:
s_rdata_d = {
26'd0, s_error_q != 0, stream_enable_o, !fifo_full_i, fifo_empty_i, active_i, s_ctrl_q[0]
};
`RIBP_DVP_STREAM_CTRL: s_rdata_d = s_stream_q;
`RIBP_DVP_FORMAT: s_rdata_d = s_format_q;
`RIBP_DVP_SYNC_CFG: s_rdata_d = s_sync_q;
`RIBP_DVP_FRAME_SIZE: s_rdata_d = s_frame_size_q;
`RIBP_DVP_CROP_START: s_rdata_d = s_crop_start_q;
`RIBP_DVP_CROP_SIZE: s_rdata_d = s_crop_size_q;
`RIBP_DVP_FRAME_COUNT: s_rdata_d = s_frame_count_q;
`RIBP_DVP_LINE_COUNT: s_rdata_d = s_line_count_q;
`RIBP_DVP_PIXEL_COUNT: s_rdata_d = s_pixel_count_q;
`RIBP_DVP_WORD_COUNT: s_rdata_d = s_word_count_q;
`RIBP_DVP_DROP_COUNT: s_rdata_d = s_drop_count_q;
`RIBP_DVP_ERROR_STATUS: s_rdata_d = {26'd0, s_error_q};
`RIBP_DVP_INTR_STATE: s_rdata_d = {25'd0, s_intr_state_q};
`RIBP_DVP_INTR_ENABLE: s_rdata_d = {25'd0, s_intr_enable_q};
`RIBP_DVP_INTR_STATUS: s_rdata_d = {25'd0, s_intr_state_q & s_intr_enable_q};
`RIBP_DVP_INTR_TEST: s_access_err = !s_write;
`RIBP_DVP_IP_VERSION: s_rdata_d = IP_VERSION;
`RIBP_DVP_CAPABILITY: s_rdata_d = CAPABILITY;
`RIBP_DVP_COMMAND: s_access_err = !s_write;
default: s_access_err = 1'b1;
endcase
end
if (s_accept && s_write && active_i &&
((s_offset == `RIBP_DVP_CTRL) || (s_offset == `RIBP_DVP_STREAM_CTRL) ||
(s_offset == `RIBP_DVP_FORMAT) || (s_offset == `RIBP_DVP_SYNC_CFG) ||
(s_offset == `RIBP_DVP_FRAME_SIZE) || (s_offset == `RIBP_DVP_CROP_START) ||
(s_offset == `RIBP_DVP_CROP_SIZE))) begin
s_access_err = 1'b1;
end
if (s_accept && s_write && (s_offset == `RIBP_DVP_FRAME_SIZE) &&
((ribp.wdata[15:0] == 0) || (ribp.wdata[31:16] == 0))) begin
s_access_err = 1'b1;
end
s_resp_err_d = s_accept && s_access_err;
end
assign command_valid_o = |s_command_q;
assign command_abort_o = s_command_q[`DVP_COMMAND_ABORT];
assign command_flush_o = s_command_q[`DVP_COMMAND_FLUSH];
always_comb begin
s_ctrl_d = s_ctrl_q;
s_stream_d = s_stream_q;
s_format_d = s_format_q;
s_sync_d = s_sync_q;
s_frame_size_d = s_frame_size_q;
s_crop_start_d = s_crop_start_q;
s_crop_size_d = s_crop_size_q;
s_config_seq_d = s_config_seq_q;
if (s_accept && s_write && !s_access_err) begin
unique case (s_offset)
`RIBP_DVP_CTRL: begin
s_ctrl_d = merge_wstrb(s_ctrl_q, ribp.wdata, ribp.wstrb) & CTRL_MASK;
s_config_seq_d = s_config_seq_q + 1'b1;
end
`RIBP_DVP_STREAM_CTRL: begin
s_stream_d = merge_wstrb(s_stream_q, ribp.wdata, ribp.wstrb) & STREAM_MASK;
s_config_seq_d = s_config_seq_q + 1'b1;
end
`RIBP_DVP_FORMAT: begin
s_format_d = merge_wstrb(s_format_q, ribp.wdata, ribp.wstrb) & FORMAT_MASK;
s_config_seq_d = s_config_seq_q + 1'b1;
end
`RIBP_DVP_SYNC_CFG: begin
s_sync_d = merge_wstrb(s_sync_q, ribp.wdata, ribp.wstrb) & SYNC_MASK;
s_config_seq_d = s_config_seq_q + 1'b1;
end
`RIBP_DVP_FRAME_SIZE: begin
s_frame_size_d = merge_wstrb(s_frame_size_q, ribp.wdata, ribp.wstrb);
s_config_seq_d = s_config_seq_q + 1'b1;
end
`RIBP_DVP_CROP_START: begin
s_crop_start_d = merge_wstrb(s_crop_start_q, ribp.wdata, ribp.wstrb);
s_config_seq_d = s_config_seq_q + 1'b1;
end
`RIBP_DVP_CROP_SIZE: begin
s_crop_size_d = merge_wstrb(s_crop_size_q, ribp.wdata, ribp.wstrb);
s_config_seq_d = s_config_seq_q + 1'b1;
end
default: begin
end
endcase
end
end
dffr #(32) u_ctrl_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ctrl_d),
.dat_o (s_ctrl_q)
);
dffr #(32) u_stream_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_stream_d),
.dat_o (s_stream_q)
);
dffr #(32) u_format_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_format_d),
.dat_o (s_format_q)
);
dffr #(32) u_sync_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_sync_d),
.dat_o (s_sync_q)
);
dffr #(32) u_frame_size_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_frame_size_d),
.dat_o (s_frame_size_q)
);
dffr #(32) u_crop_start_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_crop_start_d),
.dat_o (s_crop_start_q)
);
dffr #(32) u_crop_size_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_crop_size_d),
.dat_o (s_crop_size_q)
);
dffr #(32) u_config_seq_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_config_seq_d),
.dat_o (s_config_seq_q)
);
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_config_sent_q <= '0;
s_command_q <= '0;
s_error_q <= '0;
s_intr_state_q <= '0;
s_intr_enable_q <= '0;
s_frame_count_q <= '0;
s_line_count_q <= '0;
s_pixel_count_q <= '0;
s_word_count_q <= '0;
s_drop_count_q <= '0;
end else begin
if (config_valid_o && config_ready_i) s_config_sent_q <= s_config_seq_q;
if (command_valid_o && command_ready_i) s_command_q <= '0;
if (s_accept && s_write && !s_access_err && (s_offset == `RIBP_DVP_COMMAND)) begin
s_command_q <= s_command_q | ribp.wdata[1:0];
end
s_error_q <= (s_error_q | error_flags_i) & ~((s_accept && s_write && (s_offset == `RIBP_DVP_ERROR_STATUS)) ? ribp.wdata[5:0] : 6'd0);
if (frame_start_i) s_intr_state_q[`DVP_INTR_FRAME_START] <= 1'b1;
if (line_done_i) s_intr_state_q[`DVP_INTR_LINE_DONE] <= 1'b1;
if (frame_done_i) s_intr_state_q[`DVP_INTR_FRAME_DONE] <= 1'b1;
if (error_event_i) s_intr_state_q[`DVP_INTR_OVERFLOW] <= 1'b1;
if (s_accept && s_write && (s_offset == `RIBP_DVP_INTR_STATE))
s_intr_state_q <= s_intr_state_q & ~ribp.wdata[6:0];
if (s_accept && s_write && (s_offset == `RIBP_DVP_INTR_ENABLE))
s_intr_enable_q <= ribp.wdata[6:0];
if (s_accept && s_write && (s_offset == `RIBP_DVP_INTR_TEST))
s_intr_state_q <= s_intr_state_q | ribp.wdata[6:0];
if (frame_stats_valid_i) begin
s_frame_count_q <= frame_stats_i[31:0];
s_line_count_q <= {16'd0, frame_stats_i[47:32]};
s_pixel_count_q <= frame_stats_i[79:48];
s_word_count_q <= {16'd0, frame_stats_i[95:80]};
s_drop_count_q <= frame_stats_i[127:96];
end
end
end
dffr #(1) u_ready_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (ribp.valid && !s_ready_q),
.dat_o (s_ready_q)
);
dffer #(32) u_rdata_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_accept && !s_write),
.dat_i (s_rdata_d),
.dat_o (s_rdata_q)
);
dffer #(1) u_resp_err_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_accept),
.dat_i (s_resp_err_d),
.dat_o (s_resp_err_q)
);
endmodule
+8 -6
View File
@@ -74,7 +74,7 @@ module dma_core (
logic s_fifo_flush, s_fifo_push, s_fifo_pop;
logic s_fifo_full, s_fifo_empty;
logic [ 2:0] s_fifo_count;
logic [31:0] s_fifo_rdata;
logic [35:0] s_fifo_rdata;
logic s_rsp_err;
logic s_i2s_tx_stream;
logic s_i2s_rx_stream;
@@ -99,7 +99,7 @@ module dma_core (
assign s_i2s_rx_stream = mode_i == HWT_I2S_RX_TRG;
assign s_dvp_rx_stream = mode_i == HWT_DVP_RX_TRG;
assign i2s_tx_axis.tdata = s_fifo_rdata;
assign i2s_tx_axis.tdata = s_fifo_rdata[31:0];
assign i2s_tx_axis.tkeep = '1;
assign i2s_tx_axis.tstrb = '1;
assign i2s_tx_axis.tlast = (s_xfer_cnt_q + 1'b1) >= xferlen_i;
@@ -148,8 +148,8 @@ module dma_core (
assign rib.cmd_len = s_chunk_len;
assign rib.w_valid = (s_fsm_q == FSM_WR_DATA) && !s_i2s_tx_stream &&
~s_fifo_empty && (s_fifo_count != 3'd0);
assign rib.wdata = s_fifo_rdata;
assign rib.wstrb = '1;
assign rib.wdata = s_fifo_rdata[31:0];
assign rib.wstrb = s_dvp_rx_stream ? s_fifo_rdata[35:32] : '1;
assign rib.wlast = s_wr_beat_q == s_chunk_len;
assign rib.rsp_ready = (s_fsm_q == FSM_RD_RESP) && !(s_i2s_rx_stream || s_dvp_rx_stream) ?
(~s_fifo_full && (s_fifo_count != 3'd4)) :
@@ -166,7 +166,7 @@ module dma_core (
assign s_fifo_pop = s_i2s_tx_stream ? s_stream_tx_hdshk : ((s_fsm_q == FSM_WR_DATA) && s_w_hdshk);
fifo #(
.DATA_WIDTH (32),
.DATA_WIDTH (36),
.BUFFER_DEPTH(4)
) u_data_fifo (
.clk_i(clk_i),
@@ -174,7 +174,9 @@ module dma_core (
.flush_i(s_fifo_flush),
.push_i(s_fifo_push),
.full_o(s_fifo_full),
.dat_i(s_i2s_rx_stream ? i2s_rx_axis.tdata : s_dvp_rx_stream ? dvp_rx_axis.tdata : rib.rdata),
.dat_i(s_i2s_rx_stream ? {4'b1111, i2s_rx_axis.tdata} :
s_dvp_rx_stream ? {dvp_rx_axis.tkeep, dvp_rx_axis.tdata} :
{4'b1111, rib.rdata}),
.pop_i(s_fifo_pop),
.empty_o(s_fifo_empty),
.dat_o(s_fifo_rdata),
+1
View File
@@ -11,6 +11,7 @@
/clusterip/common/rtl/utils/spill_register.sv
/clusterip/common/rtl/stream/round_robin_arbiter.sv
/clusterip/common/rtl/utils/lfsr.sv
/clusterip/common/rtl/utils/bin2gray.sv
/clusterip/common/rtl/utils/gray2bin.sv
/clusterip/common/rtl/utils/edge_det.sv
/clusterip/common/rtl/cdc/cdc_sync.sv
+2
View File
@@ -51,7 +51,9 @@
/ip/ribp/memory/sdram_reg.sv
/ip/ribp/memory/sdram_core.sv
/ip/ribp/memory/sdram.sv
/ip/ribp/multimedia/dvp_define.svh
/ip/ribp/multimedia/dvp_core.sv
/ip/ribp/multimedia/dvp_reg.sv
/ip/ribp/multimedia/dvp.sv
/ip/ribp/peripheral/timer_core.sv
/ip/ribp/peripheral/timer_reg.sv
+44
View File
@@ -0,0 +1,44 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module dvp_formal_design (
input logic clk_i,
output logic rst_n_i,
output logic f_past_valid,
output logic rib_valid,
output logic [31:0] rib_addr,
output logic [31:0] rib_wdata,
output logic [3:0] rib_wstrb,
output logic rib_ready,
output logic rib_resp_err,
output logic [6:0] intr_state,
output logic [6:0] intr_enable,
output logic irq
);
ribp_if rib ();
(* anyseq *) logic f_rib_valid;
(* anyseq *) logic [31:0] f_rib_addr, f_rib_wdata;
(* anyseq *) logic [3:0] f_rib_wstrb;
(* anyseq *) logic f_active, f_fifo_empty, f_fifo_full;
(* anyseq *) logic [5:0] f_error_flags;
(* anyseq *) logic [127:0] f_frame_stats;
(* anyseq *) logic f_frame_stats_valid;
assign rib.valid = f_rib_valid; assign rib.addr = f_rib_addr;
assign rib.wdata = f_rib_wdata; assign rib.wstrb = f_rib_wstrb;
assign rib_valid = rib.valid; assign rib_addr = rib.addr; assign rib_wdata = rib.wdata;
assign rib_wstrb = rib.wstrb; assign rib_ready = rib.ready; assign rib_resp_err = rib.resp_err;
assign intr_state = u_dut.s_intr_state_q;
assign intr_enable = u_dut.s_intr_enable_q;
dvp_reg u_dut (
.clk_i(clk_i), .rst_n_i(rst_n_i), .ribp(rib), .active_i(f_active),
.fifo_empty_i(f_fifo_empty), .fifo_full_i(f_fifo_full), .rx_data_i(32'd0),
.frame_start_i(1'b0), .line_done_i(1'b0), .frame_done_i(1'b0),
.error_event_i(|f_error_flags), .error_flags_i(f_error_flags),
.frame_stats_i(f_frame_stats), .frame_stats_valid_i(f_frame_stats_valid),
.config_o(), .config_valid_o(), .config_ready_i(1'b1),
.command_abort_o(), .command_flush_o(), .command_valid_o(), .command_ready_i(1'b1),
.rx_pop_o(), .stream_enable_o(), .irq_o(irq)
);
initial begin rst_n_i = 1'b0; f_past_valid = 1'b0; end
always_ff @(posedge clk_i) begin rst_n_i <= 1'b1; f_past_valid <= 1'b1; end
endmodule
+21
View File
@@ -0,0 +1,21 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module dvp_formal;
(* anyseq *) (* gclk *) reg clk_i;
wire rst_n_i, f_past_valid, rib_valid, rib_ready, rib_resp_err, irq;
wire [31:0] rib_addr, rib_wdata; wire [3:0] rib_wstrb;
wire [6:0] intr_state, intr_enable;
dvp_formal_design u_design (.*);
always @(posedge clk_i) begin
if (rst_n_i) begin
if (f_past_valid && $past(rst_n_i && rib_valid && !rib_ready)) begin
assume (rib_valid); assume (rib_addr == $past(rib_addr));
assume (rib_wdata == $past(rib_wdata)); assume (rib_wstrb == $past(rib_wstrb));
assert (rib_ready);
end
assert (irq == ((intr_state & intr_enable) != 0));
cover (rib_resp_err); cover (irq);
end
end
endmodule
@@ -160,6 +160,13 @@ def source_files(target: str) -> list[Path]:
PERIPHERAL / "ribp_clint.sv",
SCRIPT_DIR / "clint_formal.sv",
]
if target == "dvp":
return [
COMMON_RTL / "interface/ribp_if.sv", COMMON_RTL / "utils/register.sv",
ROOT / "rtl/ip/ribp/multimedia/dvp_define.svh",
ROOT / "rtl/ip/ribp/multimedia/dvp_reg.sv",
SCRIPT_DIR / "dvp_formal.sv",
]
raise ValueError(f"unsupported formal target: {target}")
@@ -205,6 +212,7 @@ def parse_args() -> argparse.Namespace:
"i2c",
"timer",
"clint",
"dvp",
),
required=True,
)
@@ -64,7 +64,7 @@
},
{
"name": "dvp",
"clock": "s_dvp_pclk_buf",
"clock": "s_pclk_sel",
"reset": "s_dvp_pclk_rst_n_sync",
"reset_source": "rst_n_i",
"reset_primitive": "rst_sync",
@@ -107,17 +107,17 @@
"name": "dvp_enable",
"source": "system",
"destination": "dvp",
"primitive": "cdc_sync",
"primitive": "cdc_2phase",
"path": "rtl/ip/ribp/multimedia/dvp.sv",
"instance": "u_dvp_recven_cdc_sync"
"instance": "u_dvp_config_cdc"
},
{
"name": "dvp_fifo",
"source": "dvp",
"destination": "system",
"primitive": "async_fifo",
"primitive": "cdc_fifo_warm_flush",
"path": "rtl/ip/ribp/multimedia/dvp.sv",
"instance": "u_rx_async_fifo"
"instance": "u_dvp_payload_fifo"
},
{
"name": "rtc_command",
@@ -39,6 +39,7 @@ COMPATIBILITY_IRQ_BINDINGS = (
("ws2812", "ribp", 10, 17, "ws2812.irq_o"),
("gpio", "ribp", 11, 18, "gpio.irq_o"),
("i2c1", "ribp", 12, 19, "i2c1.irq_o"),
("dvp", "ribp", 13, 15, "s_dvp_irq"),
)
+3 -2
View File
@@ -3,7 +3,7 @@
"gpio_pins": 32,
"irq_vector_width": 32,
"irq_groups": [
{"name": "ribp", "width": 13, "top_signal": "s_ribp_irq"},
{"name": "ribp", "width": 14, "top_signal": "s_ribp_irq"},
{"name": "apb", "width": 5, "top_signal": "s_apb_irq"}
],
"interrupts": [
@@ -24,7 +24,8 @@
{"name": "rng", "description": "APB RNG data/error interrupt", "group": "apb", "group_bit": 4, "core_bit": 16, "signal": "s_rng_irq", "user_visible": false},
{"name": "ws2812", "description": "WS2812 transmitter interrupt", "group": "ribp", "group_bit": 10, "core_bit": 17, "signal": "ws2812.irq_o"},
{"name": "gpio", "description": "RIBP GPIO combined interrupt", "group": "ribp", "group_bit": 11, "core_bit": 18, "signal": "gpio.irq_o"},
{"name": "i2c1", "description": "RIBP I2C1 interrupt", "group": "ribp", "group_bit": 12, "core_bit": 19, "signal": "i2c1.irq_o"}
{"name": "i2c1", "description": "RIBP I2C1 interrupt", "group": "ribp", "group_bit": 12, "core_bit": 19, "signal": "i2c1.irq_o"},
{"name": "dvp", "description": "RIBP DVP capture interrupt", "group": "ribp", "group_bit": 13, "core_bit": 15, "signal": "s_dvp_irq"}
],
"wake_sources": [
{"name": "rtc", "signal": "u_rtc_if.wake_o", "observer": "sysctrl.rtc_wake_i", "retained": false}
+10 -2
View File
@@ -11,7 +11,7 @@ FORMAL_CLINT_DEPTH ?= 32
FORMAL_TIMEOUT ?= 60
FORMAL_WS2812_TIMEOUT ?= 120
FORMAL_I2C_TIMEOUT ?= 300
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint dvp
FORMAL_FILELIST_GENERATOR := $(RTL_PATH)/formal/generate_formal_filelist.py
FORMAL_SBY_GENERATOR := $(RTL_PATH)/formal/generate_sby_config.py
FORMAL_RESULT_GENERATOR := $(RTL_PATH)/formal/formal_results.py
@@ -37,6 +37,8 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
$(RTL_PATH)/formal/timer_formal_props.sv \
$(RTL_PATH)/formal/clint_formal.sv \
$(RTL_PATH)/formal/clint_formal_props.sv \
$(RTL_PATH)/formal/dvp_formal.sv \
$(RTL_PATH)/formal/dvp_formal_props.sv \
$(RTL_PATH)/top/bus.sv \
$(RTL_PATH)/top/rib_error_slave.sv \
$(RTL_PATH)/top/rib_if.sv \
@@ -78,6 +80,10 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
$(ROOT_PATH)/rtl/ip/ribp/peripheral/clint_reg.sv \
$(ROOT_PATH)/rtl/ip/ribp/peripheral/clint_core.sv \
$(ROOT_PATH)/rtl/ip/ribp/peripheral/ribp_clint.sv \
$(ROOT_PATH)/rtl/ip/ribp/multimedia/dvp_define.svh \
$(ROOT_PATH)/rtl/ip/ribp/multimedia/dvp_core.sv \
$(ROOT_PATH)/rtl/ip/ribp/multimedia/dvp_reg.sv \
$(ROOT_PATH)/rtl/ip/ribp/multimedia/dvp.sv \
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/clkrst/counter.sv \
$(RTL_PATH)/top/rcu.sv \
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/interface/ribp_if.sv \
@@ -182,10 +188,12 @@ formal-timer: $(FORMAL_DIR)/timer/.stamp | manifest
formal-clint: $(FORMAL_DIR)/clint/.stamp | manifest
formal-dvp: $(FORMAL_DIR)/dvp/.stamp | manifest
formal-doctor:
$(MAKE) FORMAL=YES SIMU=IVERILOG SYNTH=YOSYS STA=NONE doctor
formal-clean:
python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(FORMAL_DIR)
.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-clint formal-doctor formal-clean
.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-clint formal-dvp formal-doctor formal-clean
+1
View File
@@ -94,6 +94,7 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \
$(ROOT_PATH)/crt/src/hal/ws2812_math.c \
$(ROOT_PATH)/crt/src/hal/ws2812.c \
$(ROOT_PATH)/crt/src/hal/dma.c \
$(ROOT_PATH)/crt/src/hal/dvp.c \
$(ROOT_PATH)/crt/src/hal/perf.c \
$(ROOT_PATH)/crt/src/hal/lcd.c \
$(ROOT_PATH)/crt/src/hal/psram.c \
+3 -1
View File
@@ -82,6 +82,7 @@ module ip_ribp_wrapper (
logic s_dma_i2c1_tx_stall, s_dma_i2c1_rx_stall;
logic s_dma_xfer_done;
logic s_tim0_irq, s_tim1_irq;
logic s_dvp_irq;
`ifdef PDK_GF180
localparam bit GPIO_HAS_INPUT_CMOS = 1'b1;
@@ -333,7 +334,8 @@ module ip_ribp_wrapper (
.rst_n_i(rst_n_i),
.ribp (u_dvp_ribp_if),
.rx_axis(u_dvp_rx_axis_if),
.dvp (dvp)
.dvp (dvp),
.irq_o (s_dvp_irq)
);
ribp_sdio u_rib_sdio (
+3
View File
@@ -6,6 +6,9 @@
// waiver at the SoC integration boundary without modifying the dependency.
/* verilator lint_off DECLFILENAME */
/* verilator lint_off SYNCASYNCNET */
`include "cdc_fifo.sv"
`include "async_reqack.sv"
`include "cdc_warm_flush.sv"
/* verilator lint_on SYNCASYNCNET */
/* verilator lint_on DECLFILENAME */
+1
View File
@@ -16,6 +16,7 @@ CDC_PRIMITIVES = {
"async_reqack",
"cdc_2phase",
"cdc_sync",
"cdc_fifo_warm_flush",
"edge_det",
"hazard3_apb_async_bridge",
}
+135
View File
@@ -0,0 +1,135 @@
`timescale 1ns / 1ps
module dvp_tb;
logic clk_i = 1'b0;
logic pclk_i = 1'b0;
logic rst_n_i = 1'b0;
logic href_i = 1'b0;
logic vsync_i = 1'b0;
logic [ 7:0] dat_i = 8'd0;
logic [31:0] ribp_rdata;
logic irq_o;
ribp_if ribp ();
axi4_stream_if #(
.DATA_WIDTH(32),
.ID_WIDTH (1),
.DEST_WIDTH(1),
.USER_WIDTH(1)
) axis (
.aclk (clk_i),
.aresetn(rst_n_i)
);
dvp_if dvp ();
assign dvp.pclk_i = pclk_i;
assign dvp.href_i = href_i;
assign dvp.vsync_i = vsync_i;
assign dvp.dat_i = dat_i;
assign axis.tready = 1'b1;
always #5 clk_i = ~clk_i;
always #10 pclk_i = ~pclk_i;
ribp_dvp u_dut (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.ribp (ribp),
.rx_axis(axis),
.dvp (dvp),
.irq_o (irq_o)
);
task automatic write(input logic [31:0] address, input logic [31:0] data);
begin
@(negedge clk_i);
ribp.valid = 1'b1;
ribp.addr = address;
ribp.wdata = data;
ribp.wstrb = 4'hf;
do @(negedge clk_i); while (!ribp.ready);
if (ribp.resp_err) $fatal(1, "unexpected write error at %h", address);
ribp.valid = 1'b0;
ribp.wstrb = '0;
end
endtask
task automatic read(input logic [31:0] address, output logic [31:0] data);
begin
@(negedge clk_i);
ribp.valid = 1'b1;
ribp.addr = address;
ribp.wdata = '0;
ribp.wstrb = '0;
do @(negedge clk_i); while (!ribp.ready);
data = ribp.rdata;
ribp.valid = 1'b0;
end
endtask
task automatic frame(input int lines, input int pixels);
begin
vsync_i = 1'b1;
repeat (3) @(posedge pclk_i);
vsync_i = 1'b0;
repeat (2) @(posedge pclk_i);
for (int line = 0; line < lines; line++) begin
href_i = 1'b1;
for (int index = 0; index < pixels * 2; index++) begin
dat_i = index + line * 8;
@(posedge pclk_i);
end
href_i = 1'b0;
repeat (2) @(posedge pclk_i);
end
vsync_i = 1'b1;
repeat (3) @(posedge pclk_i);
vsync_i = 1'b0;
end
endtask
task automatic check_frame(input int lines, input int pixels, input int expected_beats);
begin
beats = 0;
frame(lines, pixels);
repeat (80) @(posedge clk_i);
if (beats != expected_beats)
$fatal(1, "unexpected AXI beat count=%0d expected=%0d", beats, expected_beats);
end
endtask
logic [31:0] value;
int beats;
always @(posedge clk_i) begin
if (axis.tvalid && axis.tready) begin
beats = beats + 1;
if (beats == 1 && !axis.tuser[0]) $fatal(1, "SOF missing");
if ((beats == 2 || beats == 4) && !axis.tlast) $fatal(1, "EOL missing");
end
end
initial begin
ribp.valid = 1'b0;
ribp.addr = '0;
ribp.wdata = '0;
ribp.wstrb = '0;
repeat (4) @(posedge clk_i);
rst_n_i = 1'b1;
read(32'h0000_00f8, value);
if (value != 32'h0002_0000) $fatal(1, "bad DVP version %h", value);
read(32'h0000_0100, value);
if (!ribp.resp_err) $fatal(1, "invalid DVP access was accepted");
write(32'h0000_0018, {16'd2, 16'd4});
write(32'h0000_000c, 32'd1);
write(32'h0000_0000, 32'd3);
repeat (50) @(posedge pclk_i);
check_frame(2, 4, 4);
read(32'h0000_0024, value);
if (value == 0)
$fatal(1, "frame count/beats invalid count=%h beats=%0d", value, beats);
write(32'h0000_0018, {16'd2, 16'd3});
check_frame(1, 3, 2);
write(32'h0000_0048, 32'h0000_0004);
read(32'h0000_003c, value);
if (ribp.resp_err) $fatal(1, "DVP interrupt read failed");
$display("RIBP DVP V2 capture test passed");
$finish;
end
endmodule
+6 -3
View File
@@ -33,7 +33,10 @@ module soc_irq_topology_tb;
for (int bit_index = 10; bit_index < `SOC_IRQ_RIBP_WIDTH; bit_index++) begin
s_ribp_irq = '0;
s_ribp_irq[bit_index] = 1'b1;
expect_irq(32'd1 << (bit_index + 7));
case (bit_index)
13: expect_irq(32'd1 << 15);
default: expect_irq(32'd1 << (bit_index + 7));
endcase
end
for (int bit_index = 0; bit_index < `SOC_IRQ_APB_WIDTH; bit_index++) begin
@@ -52,8 +55,8 @@ module soc_irq_topology_tb;
s_apb_irq = '0;
expect_irq('0);
if (s_irq[10] !== 1'b0 || s_irq[15] !== 1'b0) begin
$fatal(1, "removed core IRQ bits are not low: IRQ10=%b IRQ15=%b", s_irq[10], s_irq[15]);
if (s_irq[10] !== 1'b0) begin
$fatal(1, "removed core IRQ bit is not low: IRQ10=%b", s_irq[10]);
end
if (s_irq[31:18] !== '0) begin
$fatal(1, "unallocated core IRQ bits are not low: %h", s_irq[31:18]);
+63
View File
@@ -0,0 +1,63 @@
"""RIBP DVP V2 register, CDC, framing, and packing regression."""
from __future__ import annotations
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def test_dvp_capture_framing(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
if iverilog is None or vvp is None:
return
common = ROOT / "rtl/managed/clusterip/common/rtl"
ip = ROOT / "rtl/ip/ribp/multimedia"
defines = [
"+define+PDK_BEHAV",
"+define+SV_ASSRT_DISABLE",
]
source = [
"+incdir+" + str(common),
"+incdir+" + str(common / "interface"),
"+incdir+" + str(common / "utils"),
"+incdir+" + str(common / "cdc"),
"+incdir+" + str(common / "clkrst"),
"+incdir+" + str(ip),
str(common / "interface/ribp_if.sv"),
str(common / "interface/axi4_stream_if.sv"),
str(common / "utils/register.sv"),
str(common / "utils/xchecker.sv"),
str(common / "utils/bin2gray.sv"),
str(common / "utils/gray2bin.sv"),
str(common / "utils/spill_register.sv"),
str(common / "cdc/cdc_sync.sv"),
str(common / "cdc/cdc_rst_ctrlr.sv"),
str(common / "cdc/async_reqack.sv"),
str(common / "cdc/cdc_fifo.sv"),
str(common / "cdc/cdc_2phase.sv"),
str(common / "cdc/cdc_warm_flush.sv"),
str(common / "utils/edge_det.sv"),
str(common / "clkrst/rst_sync.sv"),
str(ROOT / "rtl/tech/tc_clk.sv"),
str(ip / "dvp_core.sv"),
str(ip / "dvp_reg.sv"),
str(ip / "dvp.sv"),
str(ROOT / "tests/rtl/dvp_tb.sv"),
]
source_list = tmp_path / "dvp.fl"
source_list.write_text("\n".join([*defines, *source, ""]), encoding="utf-8")
converted = tmp_path / "dvp.v"
subprocess.run(
[sys.executable, str(ROOT / "rtl/mini/script/convt_sv2v.py"), "-f", str(source_list), "--output", str(converted)],
check=True,
)
simulation = tmp_path / "dvp_tb"
subprocess.run([iverilog, "-g2012", "-s", "dvp_tb", "-o", str(simulation), str(converted)], check=True)
result = subprocess.run([vvp, str(simulation)], check=True, capture_output=True, text=True)
assert "RIBP DVP V2 capture test passed" in result.stdout
+6 -4
View File
@@ -113,20 +113,21 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert ".cfg_axi4(u_cfg_axi4_if)" in bus_fabric
assert ".apb_axi4(u_apb_axi4_if)" in bus_fabric
assert "`define SOC_IRQ_VECTOR_WIDTH 32" in irq_config
assert "`define SOC_USER_IRQ_MASK 32'h000E7BFC" in irq_config
assert "`define SOC_IRQ_RIBP_WIDTH 13" in irq_config
assert "`define SOC_USER_IRQ_MASK 32'h000EFBFC" in irq_config
assert "`define SOC_IRQ_RIBP_WIDTH 14" in irq_config
assert "`define SOC_IRQ_APB_WIDTH 5" in irq_config
assert "assign irq_o[0] = u_clint_if.software_irq_o[0];" in rib_irq
assert "assign irq_o[10] = ws2812.irq_o;" in rib_irq
assert "assign irq_o[11] = gpio.irq_o;" in rib_irq
assert "assign irq_o[12] = i2c1.irq_o;" in rib_irq
assert "assign irq_o[13] = s_dvp_irq;" in rib_irq
assert "assign irq_o[0] = pwm.irq_o;" in apb_irq
assert "assign irq_o[4] = s_rng_irq;" in apb_irq
assert "s_irq[10]" not in irq_wiring
assert "s_irq[15]" not in irq_wiring
assert "s_irq[15] = s_ribp_irq[13];" in irq_wiring
assert "s_irq[16] = s_apb_irq[4];" in irq_wiring
assert "irq_i[10] == 1'b0" in irq_sva
assert "irq_i[15] == 1'b0" in irq_sva
assert "irq_i[15] == ribp_irq_i[13]" in irq_sva
assert "irq_i[31] == 1'b0" in irq_sva
assert "bind retrosoc soc_irq_topology_sva" in irq_sva
assert filelist.startswith("+incdir+")
@@ -164,6 +165,7 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None:
("ws2812", "ribp", 10, 17, "ws2812.irq_o"),
("gpio", "ribp", 11, 18, "gpio.irq_o"),
("i2c1", "ribp", 12, 19, "i2c1.irq_o"),
("dvp", "ribp", 13, 15, "s_dvp_irq"),
]