feat: enhance JPEG processing modules and tests

- Updated  to integrate a coefficient engine and additional signals for coefficient handling.
- Modified  to include a coefficient engine, enhancing the encoding process with new signals.
- Improved  to validate generated blocks against expected values and added timeout handling.
- Introduced  for testing SRAM wrappers in JPEG workspace.
- Enhanced test coverage in  to reject invalid STA net drivers.
- Updated  to ensure proper coverage of canonical domains in timing contracts.
- Added a new test for JPEG coefficient engine in .
- Improved memory map tests in  to validate public active MMIO regions.
- Enhanced management core specification tests in  to verify high-performance core specifications.
- Added behavioral-only checks in  to exclude synthesis and netlist consumers.
- Updated SDRAM tests in  to track active firmware memory.
- Enhanced STA constraints tests in  to cover current clock domains and net drivers.
This commit is contained in:
maksyuki committed 2026-09-01 22:33:35 +08:00
1 parent 39bc1c84b4
commit 2840d22e96
50 files changed
+2640 -1278

No files matched your search

+8 -2
View File
@@ -7,8 +7,14 @@ workflows.
`workflows/quality.yml` runs the fast quality gate. `workflows/regression-smoke.yml`,
`workflows/regression-ihp130.yml`, `workflows/regression-gf180.yml`,
`workflows/regression-ics55.yml`, `workflows/regression-sky130.yml`, and
`workflows/nightly.yml` call the reusable regression workflow. Nightly splits the IHP130 PR matrix from the extra CoreMark and Yosys recipes. `workflows/release.yml` packages tagged releases. `actions/locked-tools/`
installs the locked open-source tools used by CI.
`workflows/nightly.yml` call the reusable regression workflow. The reusable
workflow currently selects `--behavioral-only`: hosted CI skips SoC Yosys
synthesis, OpenSTA, and netlist simulation while the JPEG synthesis-memory
issue is being resolved.
The locked synthesis and timing tools are still installed and checked; only the
SoC synthesis and downstream execution stages are filtered. Local regression
commands retain those stages. `workflows/release.yml` packages tagged releases.
`actions/locked-tools/` installs the locked open-source tools used by CI.
Keep Actions pinned by commit ID and use the dependency lock rather than adding
ad-hoc downloads. Validate workflow changes with `yamllint .github .yamllint.yml`,
+3 -1
View File
@@ -128,7 +128,9 @@ jobs:
make CONFIG=${{ inputs.profile }} formal
- name: Run ${{ inputs.suite }} regression
run: python3 scripts/regress.py --root . --suite "${{ inputs.suite }}" --pdk "${{ inputs.pdk }}"
run: >-
python3 scripts/regress.py --root . --suite "${{ inputs.suite }}"
--pdk "${{ inputs.pdk }}" --behavioral-only
- name: Upload reports and structured results
if: always()
+6 -6
View File
@@ -88,10 +88,10 @@ jobs:
- name: Validate regression definitions
run: |
python3 scripts/regress.py --root . --suite pr --pdk IHP130 --dry-run
python3 scripts/regress.py --root . --suite pr --pdk GF180 --dry-run
python3 scripts/regress.py --root . --suite pr --pdk ICS55 --dry-run
python3 scripts/regress.py --root . --suite pr --pdk SKY130 --dry-run
python3 scripts/regress.py --root . --suite nightly --pdk IHP130 --dry-run
python3 scripts/regress.py --root . --suite nightly-extra --pdk IHP130 --dry-run
python3 scripts/regress.py --root . --suite pr --pdk IHP130 --behavioral-only --dry-run
python3 scripts/regress.py --root . --suite pr --pdk GF180 --behavioral-only --dry-run
python3 scripts/regress.py --root . --suite pr --pdk ICS55 --behavioral-only --dry-run
python3 scripts/regress.py --root . --suite pr --pdk SKY130 --behavioral-only --dry-run
python3 scripts/regress.py --root . --suite nightly --pdk IHP130 --behavioral-only --dry-run
python3 scripts/regress.py --root . --suite nightly-extra --pdk IHP130 --behavioral-only --dry-run
python3 scripts/regress.py --root . --suite smoke --pdk IHP130 --dry-run
+9 -4
View File
@@ -84,10 +84,10 @@ used with commercial 32 KiB SRAM and PLL simulation models.
| Profile | ISA | Application | Coverage |
| --- | --- | --- | --- |
| [`configs/ci/ihp130.mk`](configs/ci/ihp130.mk) | RV32IM | `bringup` | Pull-request open-source regression with a 32 KiB IHP SRAM: firmware, Verilator, Icarus, Yosys, Icarus netlist simulation, and OpenSTA. |
| [`configs/ci/gf180.mk`](configs/ci/gf180.mk) | RV32IM | `bringup` | Pull-request firmware, RTL simulation, Yosys, Icarus netlist, and OpenSTA coverage with a 32 KiB SRAM assembled from GF180 macros. |
| [`configs/ci/ics55.mk`](configs/ci/ics55.mk) | RV32IM | `bringup` | Pull-request firmware, RTL simulation, Yosys, Icarus netlist simulation, and OpenSTA core timing coverage. |
| [`configs/ci/sky130.mk`](configs/ci/sky130.mk) | RV32IM | `bringup` | Pull-request firmware, RTL simulation, Yosys, Icarus netlist, and OpenSTA coverage with a 32 KiB OpenRAM SRAM. |
| [`configs/ci/ihp130.mk`](configs/ci/ihp130.mk) | RV32IM | `bringup` | Firmware and behavioral regression with a 32 KiB IHP SRAM; local full-flow commands also support Yosys, netlist Icarus, and OpenSTA. |
| [`configs/ci/gf180.mk`](configs/ci/gf180.mk) | RV32IM | `bringup` | Firmware and behavioral regression with a 32 KiB SRAM assembled from GF180 macros; local full-flow commands support synthesis and timing. |
| [`configs/ci/ics55.mk`](configs/ci/ics55.mk) | RV32IM | `bringup` | Firmware and behavioral regression without a public SRAM macro; local full-flow commands support synthesis and core timing. |
| [`configs/ci/sky130.mk`](configs/ci/sky130.mk) | RV32IM | `bringup` | Firmware and behavioral regression with a 32 KiB OpenRAM SRAM; local full-flow commands support synthesis and timing. |
| [`configs/ci/ihp130-shell.mk`](configs/ci/ihp130-shell.mk) | RV32IM | `shell` | Pull-request firmware build with CSR support enabled. |
| [`configs/ci/ihp130-debug.mk`](configs/ci/ihp130-debug.mk) | RV32IM | `debug` | Verilator remote-bitbang acceptance of the Hazard3 JTAG DTM, Debug Module, OpenOCD, and GDB. |
| [`configs/ci/ihp130-hp.mk`](configs/ci/ihp130-hp.mk) | LP RV32IM / HP RV32IMAFDC | `hp_boot` | Linux image/bundle flow and HP RTL validation on the fixed product topology. |
@@ -254,6 +254,11 @@ timing, and netlist simulation for fast feedback. `make regress-pr` runs the
supported IHP130, GF180, ICS55, and SKY130 PR matrices in sequence, including
slow-corner OpenSTA core timing analysis for each PDK.
GitHub-hosted regression workflows currently pass `--behavioral-only` to avoid
the unresolved JPEG Yosys memory peak. They retain RTL lint, firmware, Verilator,
and Icarus behavioral coverage; local `make regress-pr` remains the complete
synthesis, netlist simulation, and OpenSTA flow.
Build outputs are isolated below `build/<profile>-<YYYY-MM-DD-HH-MM>-<config-hash>/`. Each variant keeps its
firmware, generated sources, simulator output, synthesis and timing reports, manifest, warning
analysis, and metrics separate from other configurations. Use `make clean` to remove the
+3 -1
View File
@@ -74,6 +74,8 @@ static bool rs_ci_smoke_fabric_monitor_start(void) {
}
static bool rs_ci_smoke_fabric_monitor_check(void) {
const rs_fabric_target_t active_memory_target =
(RS_SOC_HAS_SRAM != 0U) ? RS_FABRIC_TARGET_SRAM : RS_FABRIC_TARGET_SDRAM;
rs_fabric_monitor_status_t status;
rs_fabric_master_stats_t master;
rs_fabric_target_stats_t target;
@@ -83,7 +85,7 @@ static bool rs_ci_smoke_fabric_monitor_check(void) {
if ((rs_fabric_monitor_snapshot() != RS_OK) ||
(rs_fabric_monitor_get_status(&status) != RS_OK) ||
(rs_fabric_monitor_read_master(RS_FABRIC_MASTER_LP, &master) != RS_OK) ||
(rs_fabric_monitor_read_target(RS_FABRIC_TARGET_SRAM, &target) != RS_OK) ||
(rs_fabric_monitor_read_target(active_memory_target, &target) != RS_OK) ||
(rs_fabric_monitor_read_fault(&fault) != RS_OK) ||
(rs_fabric_monitor_get_flush_count(&flush_count) != RS_OK)) {
return false;
+123 -59
View File
@@ -34,6 +34,127 @@ static void rs_print_hazard3_spec(void) {
printf(" One hart: halt/resume, 2 breakpoints; no system-bus access\n");
}
static const char *rs_boolean_name(bool value) {
return value ? "yes" : "no";
}
static void rs_print_hp_core_spec(void) {
rs_sysctrl_hp_status_t status = {0};
if ((rs_sysctrl_get_hp_status(&status) != RS_OK) || !status.present) {
return;
}
printf("\nHigh-Performance-Core Specification:\n");
printf(" Identity\n");
printf(" Core: VexiiRiscv(hart 1), dual-issue in-order\n");
printf(" Role: high-throughput application and Linux\n");
printf(" Source: https://github.com/SpinalHDL/VexiiRiscv\n");
printf(" Hardware ISA and privilege\n");
printf(" Base: RV32IMAFDC_Zicbom_Zicntr_Zihpm\n");
printf(" Mode: M/S/U; Sv32 MMU, 9-bit ASID\n");
printf(" Protection: 16 PMP regions, 4 KiB granularity\n");
printf(" IRQ: machine software/timer/external + supervisor external\n");
printf(" Microarchitecture\n");
printf(" Execution: 2 decoders/lanes, late ALU, full bypassing\n");
printf(" Prediction: BTB + GShare + RAS\n");
printf(" Cache and memory\n");
printf(" L1: separate 16 KiB, 4-way instruction and data caches\n");
printf(" Maintenance: 64-byte Zicbom CBO; no hardware coherency\n");
printf(" Bus: native AXI64 I/D + cacheless AXI32 MMIO\n");
printf(" Debug\n");
printf(" Embedded RISC-V JTAG, 4 triggers\n");
printf(" Runtime\n");
printf(" Released: %s; actual: %s; reset asserted: %s\n", rs_boolean_name(status.released),
rs_boolean_name(status.actual_released), rs_boolean_name(status.reset_asserted));
printf(" Draining: %s; forced fault: %s\n", rs_boolean_name(status.draining),
rs_boolean_name(status.forced_fault));
}
static void rs_print_mmio_map(void) {
printf("Memory-Mapped I/O Devices:\n");
printf(" 1 x HP ACLINT @%p\n",
(void *)(uintptr_t)RS_SOC_HP_ACLINT_BASE);
printf(" 1 x HP PLIC @%p\n",
(void *)(uintptr_t)RS_SOC_HP_PLIC_BASE);
printf(" 1 x GPIO(32PIN) @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_GPIO_BASE);
printf(" 1 x UART0 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_UART0_BASE);
printf(" 1 x TIMER0 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_TIM0_BASE);
printf(" 1 x TIMER1 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_TIM1_BASE);
printf(" 1 x PSRAM CTRL @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_PSRAM_BASE);
printf(" 1 x SPI-SD @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_SPISD_BASE);
printf(" 1 x I2C0 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_I2C0_BASE);
printf(" 1 x I2S @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_I2S_BASE);
printf(" 1 x WS2812 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_WS2812_BASE);
printf(" 1 x XPI CTRL @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_XPI_BASE);
printf(" 1 x DMA(8CH) @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_DMA_BASE);
printf(" 1 x SYSCTRL @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_SYSCTRL_BASE);
printf(" 1 x CRYPTO @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_CRYPTO_BASE);
printf(" 1 x SDRAM CTRL @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_SDRAM_BASE);
printf(" 1 x DVP @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_DVP_BASE);
printf(" 1 x SDIO0 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_SDIO0_BASE);
printf(" 1 x OPI/HB PSRAM @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_OPIPSRAM_BASE);
printf(" 1 x I2C1 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_I2C1_BASE);
printf(" 1 x GPIO ADMIN @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_GPIO_ADMIN_BASE);
printf(" 1 x SDIO1 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_SDIO1_BASE);
printf(" 1 x USB2 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_USB2_BASE);
printf(" 1 x SRAM CTRL @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_SRAM_BASE);
printf(" 1 x UART1 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_UART1_BASE);
printf(" 1 x HP MAILBOX @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_HP_MAILBOX_BASE);
printf(" 1 x JPEG @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_JPEG_BASE);
printf(" 1 x CLINT @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_CLINT_BASE);
printf(" 1 x ARCHINFO @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_ARCHINFO_BASE);
printf(" 1 x RNG @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_RNG_BASE);
printf(" 1 x PWM(4CH) @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_PWM_BASE);
printf(" 1 x PS2 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_PS2_BASE);
printf(" 1 x RTC @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_RTC_BASE);
printf(" 1 x WDG @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_WDG_BASE);
printf(" 1 x CRC @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_CRC_BASE);
printf(" 1 x USER IP COMPAT @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_USER_IP_BASE);
printf(" 1 x EXT-L @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_EXT_L_BASE);
printf(" 1 x EXT-H @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_EXT_H_BASE);
printf(" 1 x RESOURCE CTRL @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_RESOURCE_CTRL_BASE);
printf(" 1 x FABRIC MONITOR @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_FABRIC_MONITOR_BASE);
}
void rs_app_info(void) {
uint8_t selected_core;
uint8_t selected_ip;
@@ -101,6 +222,7 @@ void rs_app_info(void) {
printf(" +------------+ +------------+ +------------+\n");
rs_print_hazard3_spec();
rs_print_hp_core_spec();
printf("\nUser Processors:\n");
uint32_t core_size = RS_SOC_USER_CORE_COUNT;
@@ -148,65 +270,7 @@ void rs_app_info(void) {
printf(" Reserved: @[0x%08x-0xffffffff] %3u MiB\n\n", TF_CARD_START,
(unsigned int)(((UINT32_MAX - TF_CARD_START) / UINT32_C(1048576)) + UINT32_C(1)));
printf("Memory Map IO Device:\n");
printf(" 1 x GPIO(32PIN) @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_GPIO_BASE);
printf(" 1 x UART0 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_UART0_BASE);
printf(" 2 x TIMER(0,1) @%p,%p\n",
(void *)(uintptr_t)RS_SOC_APB4_TIM0_BASE, (void *)(uintptr_t)RS_SOC_APB4_TIM1_BASE);
printf(" 1 x PSRAM @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_PSRAM_BASE);
printf(" 1 x SPISD @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_SPISD_BASE);
printf(" 1 x I2C0 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_I2C0_BASE);
printf(" 1 x I2S @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_I2S_BASE);
printf(" 1 x WS2812 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_WS2812_BASE);
printf(" 1 x XPI @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_XPI_BASE);
printf(" 1 x DMA(6CH) @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_DMA_BASE);
printf(" 1 x SYSCTRL @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_SYSCTRL_BASE);
printf(" 1 x CRYPTO @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_CRYPTO_BASE);
printf(" 1 x SDRAM @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_SDRAM_BASE);
printf(" 1 x DVP @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_DVP_BASE);
printf(" 1 x SDIO0 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_SDIO0_BASE);
printf(" 1 x OPIPSRAM @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_OPIPSRAM_BASE);
printf(" 1 x I2C1 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_I2C1_BASE);
printf(" 1 x GPIO ADMIN @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_GPIO_ADMIN_BASE);
printf(" 1 x SDIO1 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_SDIO1_BASE);
printf(" 1 x USB2 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_USB2_BASE);
printf(" 1 x CLINT @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_CLINT_BASE);
printf(" 1 x ARCHINFO @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_ARCHINFO_BASE);
printf(" 1 x RNG @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_RNG_BASE);
printf(" 1 x PWM(4CH) @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_PWM_BASE);
printf(" 1 x PS2 @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_PS2_BASE);
printf(" 1 x RTC @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_RTC_BASE);
printf(" 1 x WDG @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_WDG_BASE);
printf(" 1 x CRC @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_CRC_BASE);
printf(" 1 x USER_IP(4KiB) @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_USER_IP_BASE);
rs_print_mmio_map();
printf("\n");
printf("#############################################################\n");
printf("#############################################################\n");
+22 -14
View File
@@ -5,12 +5,12 @@
| Tier | Profile | Automated coverage |
| --- | --- | --- |
| Smoke | `configs/ci/ihp130.mk` | strict Verilator RTL lint, firmware, Verilator SVA compilation, Icarus assembly self-test |
| Pull request | `configs/ci/ihp130.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus, Yosys, netlist Icarus, OpenSTA |
| Pull request | `configs/ci/ihp130.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus |
| Pull request | `configs/ci/ihp130-debug.mk` | Verilator remote-bitbang JTAG DTM, Debug Module, OpenOCD, and GDB acceptance |
| Pull request | `configs/ci/gf180.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus, Yosys, netlist Icarus, OpenSTA |
| Pull request | `configs/ci/ics55.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus, Yosys, netlist Icarus, OpenSTA |
| Pull request | `configs/ci/sky130.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus, Yosys, netlist Icarus, OpenSTA |
| Nightly | `configs/ci/ihp130.mk` | repeated full IHP130 regression |
| Pull request | `configs/ci/gf180.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus |
| Pull request | `configs/ci/ics55.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus |
| Pull request | `configs/ci/sky130.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus |
| Nightly | `configs/ci/ihp130.mk` | repeated behavioral IHP130 regression and CoreMark |
| Cluster | `configs/cluster/ics55.mk` | compatibility profile for site-specific ICS55 runs |
Regression Verilator firmware simulations override the profiles' manual
@@ -47,7 +47,8 @@ with `RS_ENOTSUP`; only the diagnostic API returns words. Production use
requires replacing that source with a PDK-qualified entropy macro and
completing the source-specific characterization and NIST SP 800-90B evidence.
OpenSTA runs a reproducible core-STA baseline for every CI PDK: IHP130 uses
The local full flow defines a reproducible OpenSTA core baseline for every CI
PDK profile: IHP130 uses
`slow_1p08V_125C`, GF180 uses `ss_125C_4v50`, ICS55 uses the H7CR
`ss_1p08_125C` view, and SKY130 uses `ss_100C_1v40`. Enabled SRAM macros use
matching slow-corner Liberty views; the SKY130 SRAM view is analytically
@@ -147,9 +148,10 @@ build/<variant>/
`SYNTH_RECIPE` is a flow selector and does not change the configuration hash.
The selected recipe determines the synthesis netlist consumed by OpenSTA and
netlist simulation, and determines which metrics file is written. `balanced`
is the default PR flow. Nightly IHP130 runs `area` and `speed` through synth,
STA, and metrics; their netlist simulation remains an explicit, on-demand
validation because of its runtime.
is the default full-flow recipe. The nightly IHP130 command set also defines
`area` and `speed` synth, STA, and metrics stages; hosted CI currently filters
them through `--behavioral-only`, while local execution remains available.
Netlist simulation is an explicit, on-demand validation because of its runtime.
Use the same commit, PDK, `BUILD_TIMESTAMP`, and `YOSYS_TARGET_PERIOD_PS` for
all recipes in a QoR comparison:
@@ -299,17 +301,23 @@ changes after freeze must retain the baseline revision and equivalence evidence.
`quality.yml` validates C, Makefile, and self-owned RTL formatting as well as Python, YAML, GitHub
Actions, the dependency lock, and script tests. `regression-smoke.yml` provides fast IHP130 feedback;
the four full PDK regression workflows remain required PR coverage and do not repeat the format checks.
the four PDK regression workflows remain required PR coverage and do not repeat the format checks.
Self-owned RTL also passes `rtl-style-check`, which applies the ownership-aware
rules in `rtl/rtl_style_manifest.json`. New positional module connections,
legacy `always @` blocks, and unjustified explicit nets are rejected in changed
self-owned RTL. Formal/DV, PDK, generated, and managed RTL use separate
validation profiles and are not mechanically rewritten by the root formatter.
Quality CI also runs `rtl-readiness-check-all` to validate the maturity record;
regression continues to provide the behavioral, synthesis, netlist, timing,
warning, and metric evidence referenced by that record.
`nightly.yml` repeats the fixed IHP130 architecture as two parallel jobs: the PR IHP130 matrix with a 360-minute budget, and the extra CoreMark plus Yosys area/speed recipes. The split keeps the multi-hour Icarus netlist run from cancelling the remaining nightly coverage at the previous 240-minute single-job limit. Source dependencies, locked tool archives, and Verilator `ccache` use
Quality CI also runs `rtl-readiness-check-all` to validate the maturity record.
GitHub-hosted regression currently passes `--behavioral-only`, so it provides
behavioral and warning evidence but skips SoC Yosys synthesis, netlist
simulation, OpenSTA, and synthesis-recipe metrics. The locked tools are still
installed and checked. This temporary policy avoids the unresolved JPEG
synthesis-memory peak. Local full regressions retain those stages and remain the
source of synthesis and timing evidence until hosted coverage is restored.
`nightly.yml` repeats the fixed IHP130 architecture as two parallel behavioral
jobs: the PR IHP130 matrix and the extra CoreMark coverage. Source dependencies,
locked tool archives, and Verilator `ccache` use
separate cache keys.
Tags matching `v*` run `release.yml`. The release contains a flattened SystemVerilog export, a source
+71 -15
View File
@@ -92,6 +92,23 @@ master before idle is reported.
| Tables | Four encoder contexts in six 1024 x 32 technology SRAMs. Decoder tables come from the input JPEG. Portal writes auto-increment and commit atomically marks a context valid. | Software must load expanded canonical Huffman tables. `TABLE_DEFAULT` is reserved and marks the context erroneous; quality is realized by the loaded quant tables. |
| Faults | APB alignment/access errors, validation errors, first AXI fault address/response, malformed JPEG codes, abort, resource reset, sticky W1C IRQ and first-error state. | Error recovery is job/ring level; no ECC/MBIST on table storage and no duplicated safety controller. |
The encode MCU builder captures at most 768 raster bytes in three 256-byte
banks. On IHP130, three replicas of those banks use nine
`RM_IHPSG13_2P_64x32_c2` macros and provide six synchronous 32-bit read ports;
other targets use the portable `tc_sram_64x32_2p` model. One fetch cycle
precedes each 64-sample block. Replication is limited to the read-only
generation phase, while both write ports broadcast each accepted 64-bit input
beat to every replica. This replaces the former 6,144-bit multi-read inferred
memory without changing the raster or block interface.
Encode and decode share one exact 8x8 transform plus four-lane quant/dequant
engine because the top-level scheduler makes the two directions mutually
exclusive. Table caches stream entries into common `dffl` entry banks instead
of retaining another full copy. The decoder checks 16 Huffman symbols per
cycle and advances through at most 16 groups for an AC symbol; this preserves
the expanded table ABI and exact decode result while bounding comparator area
and synthesis effort.
The locked libjpeg-turbo 3.2.0 archive is a host-verification input, not linked
into freestanding firmware. `tests/jpeg_reference.py` is the deterministic BAM
used for byte-exact RTL vectors; libjpeg-turbo/Pillow interoperability provides
@@ -174,28 +191,67 @@ clock. This is 124.416 Mpixel/s and 186.624 M color samples/s, so a sustained
implementation needs at least 1.728 pixels/cycle or 2.592 color samples/cycle,
before blanking and memory stalls.
The current functional pipeline does not meet that target. The deterministic
Icarus benchmark measured 979 busy cycles for one 16 x 16 4:2:0 MCU and 1347
for two, or about 368 steady-state cycles/MCU. Excluding DMA stalls, that
projects to about 3.00 million cycles for 1080p, approximately 24 fps at
72 MHz. This is an optimization baseline, not a 1080p60 claim.
The current functional pipeline does not meet that target. With SRAM-backed
MCU storage, the deterministic Icarus benchmark measures 1157 busy cycles for
one 16 x 16 4:2:0 MCU and 1703 for two, or 546 steady-state cycles/MCU.
Excluding DMA stalls, that projects to about 4.46 million cycles for 1080p,
approximately 16.1 fps at 72 MHz. The test now gates the steady-state delta at
no more than 550 cycles. This is an optimization baseline, not a 1080p60
claim.
The pre-macro standalone builder check used 172,593 mapped cells. The previous
whole-design run exceeded 500 GiB while techmapping the combinational MCU
builder and had to be terminated. The IHP130 macro mapping removes that
asynchronous multi-read memory from generic logic. Behavioral and real
`FUNCTIONAL` macro tests cover independent dual-port reads, paired writes, and
the bank geometry.
The full IHP130 `balanced` smoke synthesis then completed in 23,603.72 seconds.
Yosys reported a 31,476.61 MiB peak, 3,780,306 cells, and 69,900,602.54 um2
including technology SRAM and I/O macros. The builder accounts for 212,500
generic mapped cells, including 6,144 DFFE and 159,641 mux cells. This confirms
that the former memory explosion is removed, while also quantifying why the
multi-read-port scratchpad must be replaced before a commercial PPA claim.
OpenSTA completed the slow-corner core smoke analysis, but timing does not
close: max WNS/TNS are -4262.54 ns/-611269376.00 ns and min WNS/TNS are
-0.48 ns/-55.37 ns. The worst max paths are high-fanout PCLK reset and recovery
paths, not a JPEG throughput path. One generic clock-gate latch remains a
black box because the Yosys flow does not map `$_DLATCH_N_` to the IHP130 latch
or integrated clock-gate cell. These are explicit reset-tree, clock-gating, and
physical-buffering gaps; a successful OpenSTA process exit is not timing
signoff.
The measured local generic-cell reductions before the final full-chip rerun
are: table cache 91,157 to 5,087 cells per instance; entropy decoder 89,080 to
22,719; transform 308,389 to 233,313; four-lane quantizer 130,094 to 106,420;
and entropy encoder 43,875 to 40,517. One shared coefficient engine replaces
the former encode and decode transform/quant pairs. These local numbers use
the same coarse-plus-techmap recipe and exclude SRAM macro area.
An IHP130-macro-aware `apb4_jpeg` diagnostic synthesis completed in 1,582.78
seconds with an 11.55 GiB peak and 684,337 generic cells, including eight
`1024x32` table macros and nine `64x32` dual-port MCU macros. This is below the
0.7-million-cell JPEG target. It is a block-level measurement, not a substitute
for the final full-SoC mapped area, STA, or post-layout macro utilization.
The commercial-performance revision is ordered as follows:
1. Add two ping-pong MCU raster buffers so DMA captures MCU N+1 while N is
transformed. Coalesce compressed output into 16-beat writes instead of one
AXI transaction per output beat.
2. Split transform, quantize, and entropy into independently backpressured
1. Convert at least two input pixels per cycle into banked Y/Cb/Cr storage,
accumulating 4:2:2 or 4:2:0 chroma while pixels arrive. Give each processing
lane a fixed bank instead of recreating a multi-read-port raster buffer.
2. Add two ping-pong MCU buffers so DMA captures MCU N+1 while N is transformed.
Coalesce compressed output into 16-beat writes instead of one AXI transaction
per output beat.
3. Split transform, quantize, and entropy into independently backpressured
block stages. Bank block storage and carry component/DC metadata beside the
data so at least one block enters every eight cycles.
3. Use four transform lanes and two entropy lanes for the 4:2:0 profile. Compute
4. Use four transform lanes and two entropy lanes for the 4:2:0 profile. Compute
quantized DC values before entropy issue so luma predictor dependencies do
not serialize DCT/quant work. Preserve token order through per-block FIFOs.
4. Convert RGB/YUYV/NV12 with at least two pixels/cycle and bank the raster
buffer to supply four color samples/cycle. Register multiplier outputs and
fanout-heavy table selects before physical synthesis.
5. Mirror the pipeline on decode: speculative Huffman lookahead, four
coefficients/cycle inverse RLE, overlapped dequant/IDCT, and ping-pong
5. Register multiplier outputs and fanout-heavy table selects before physical
synthesis. Mirror the pipeline on decode with speculative Huffman lookahead,
four coefficients/cycle inverse RLE, overlapped dequant/IDCT, and ping-pong
reconstruction. Invalid codes still retire in order and fail closed.
6. Gate the claim with a 1080p frame simulation using randomized AXI
backpressure, `CYCLES <= 1,200,000` at zero memory stall, sustained SDRAM
@@ -60,7 +60,12 @@ def load_contract(domains_path: Path, pin_map_path: Path) -> tuple[list[dict[str
f"clock domain {domain['name']} references an unknown pad: {source_port}"
)
object_type = sta.get("object_type", "pin")
observation = source_port if object_type == "port" else sta["pin"].replace(".", "/")
if object_type == "port":
observation = source_port
elif object_type == "net_driver":
observation = sta["net"]
else:
observation = sta["pin"].replace(".", "/")
parsed.append(
{
"name": domain["name"],
+16
View File
@@ -27,6 +27,19 @@ proc flow::required_pins {label name} {
return $objects
}
proc flow::required_net_driver_pin {label name} {
set nets [get_nets -quiet $name]
if {[sizeof_collection $nets] != 1} {
flow::fail "required $label net is missing or ambiguous: $name"
}
set drivers [filter_collection [get_pins -quiet -of_objects $nets] \
"direction == out"]
if {[sizeof_collection $drivers] != 1} {
flow::fail "required $label net must have one output driver: $name"
}
return $drivers
}
proc flow::domain_object {domain} {
variable canonical_clock_domains
set values [dict get $canonical_clock_domains $domain]
@@ -34,6 +47,9 @@ proc flow::domain_object {domain} {
if {[dict get $values object_type] eq "port"} {
return [flow::required_ports "clock $domain" [list $observation]]
}
if {[dict get $values object_type] eq "net_driver"} {
return [flow::required_net_driver_pin "clock $domain" $observation]
}
return [flow::required_pins "clock $domain" $observation]
}
@@ -70,6 +70,18 @@ def render(domains: list[dict[str, Any]], reset_ports: list[str]) -> str:
" return $objects",
"}",
"",
"proc require_net_driver_pin {label name} {",
" set net [get_nets -quiet $name]",
" if {$net eq \"\"} {",
" error \"required SDC net is missing: $label ($name)\"",
" }",
" set drivers [get_pins -quiet -filter \"direction == output\" -of_objects $net]",
" if {[llength $drivers] != 1} {",
" error \"required SDC net must have one output driver: $label ($name)\"",
" }",
" return $drivers",
"}",
"",
"proc require_ports {label name} {",
" set objects [get_ports -quiet $name]",
" if {$objects eq \"\"} {",
@@ -90,6 +102,12 @@ def render(domains: list[dict[str, Any]], reset_ports: list[str]) -> str:
lines.append(
f"set {pin_variable} [require_ports \"clock {name}\" {{{object_name}}}]"
)
elif object_type == "net_driver":
pin_variable = f"clk_{name}_pin"
lines.append(
f"set {pin_variable} [require_net_driver_pin \"clock {name}\" "
f"{{{sta['net']}}}]"
)
else:
pin_variable = f"clk_{name}_pin"
lines.append(
+3
View File
@@ -2,6 +2,9 @@
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_stdcell/verilog/sg13g2_udp.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_stdcell/verilog/sg13g2_stdcell.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_core_behavioral_bm_bist.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_2P_core_behavioral_ideal.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_64x64_c2_bm_bist.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_2P_64x32_c2.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_1024x32_c2_bm_bist.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_4096x16_c3_bm_bist.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_4096x8_c3_bm_bist.v
+224 -254
View File
@@ -104,38 +104,25 @@ module apb4_jpeg (
logic [ 31:0] s_table_lookup_data;
logic s_table_lookup_valid;
logic s_table_lookup_err;
logic [ 511:0] s_cache_quant;
logic [1599:0] s_cache_reciprocal;
logic [ 191:0] s_cache_dc_code;
logic [ 59:0] s_cache_dc_len;
logic [4095:0] s_cache_ac_code;
logic [1279:0] s_cache_ac_len;
logic [ 3:0] s_cache_entry_kind;
logic [ 7:0] s_cache_entry_index;
logic [ 31:0] s_cache_entry_data;
logic [ 24:0] s_cache_entry_reciprocal;
logic s_cache_entry_valid;
logic s_cache_valid;
logic s_cache_ready;
logic s_cache_err;
logic [ 511:0] s_luma_quant_d;
logic [ 511:0] s_luma_quant_q;
logic [1599:0] s_luma_reciprocal_d;
logic [1599:0] s_luma_reciprocal_q;
logic [ 191:0] s_luma_dc_code_d;
logic [ 191:0] s_luma_dc_code_q;
logic [ 59:0] s_luma_dc_len_d;
logic [ 59:0] s_luma_dc_len_q;
logic [4095:0] s_luma_ac_code_d;
logic [4095:0] s_luma_ac_code_q;
logic [1279:0] s_luma_ac_len_d;
logic [1279:0] s_luma_ac_len_q;
logic [ 511:0] s_chroma_quant_d;
logic [ 511:0] s_chroma_quant_q;
logic [1599:0] s_chroma_reciprocal_d;
logic [1599:0] s_chroma_reciprocal_q;
logic [ 191:0] s_chroma_dc_code_d;
logic [ 191:0] s_chroma_dc_code_q;
logic [ 59:0] s_chroma_dc_len_d;
logic [ 59:0] s_chroma_dc_len_q;
logic [4095:0] s_chroma_ac_code_d;
logic [4095:0] s_chroma_ac_code_q;
logic [1279:0] s_chroma_ac_len_d;
logic [1279:0] s_chroma_ac_len_q;
axi4_stream_if #(
@@ -210,6 +197,29 @@ module apb4_jpeg (
logic [ 1:0] s_decode_sampling;
logic s_decode_err;
logic [ 4:0] s_decode_err_code;
logic s_encode_coefficient_start;
logic [1535:0] s_encode_coefficient_block;
logic [ 511:0] s_encode_coefficient_quant;
logic [1599:0] s_encode_coefficient_reciprocal;
logic s_encode_coefficient_start_ready;
logic s_encode_coefficient_result_ready;
logic s_decode_coefficient_start;
logic [1535:0] s_decode_coefficient_block;
logic [ 511:0] s_decode_coefficient_quant;
logic [1599:0] s_decode_coefficient_reciprocal;
logic s_decode_coefficient_start_ready;
logic s_decode_coefficient_result_ready;
logic s_coefficient_start;
logic s_coefficient_decode;
logic [1535:0] s_coefficient_block;
logic [ 511:0] s_coefficient_quant;
logic [1599:0] s_coefficient_reciprocal;
logic s_coefficient_start_ready;
logic [1535:0] s_coefficient_result;
logic s_coefficient_result_valid;
logic s_coefficient_result_ready;
logic s_coefficient_table_err;
logic s_coefficient_overflow;
logic [ 11:0] s_read_mcu_col_d;
logic [ 11:0] s_read_mcu_col_q;
@@ -609,18 +619,6 @@ module apb4_jpeg (
always_comb begin
s_state_d = s_state_q;
s_luma_quant_d = s_luma_quant_q;
s_luma_reciprocal_d = s_luma_reciprocal_q;
s_luma_dc_code_d = s_luma_dc_code_q;
s_luma_dc_len_d = s_luma_dc_len_q;
s_luma_ac_code_d = s_luma_ac_code_q;
s_luma_ac_len_d = s_luma_ac_len_q;
s_chroma_quant_d = s_chroma_quant_q;
s_chroma_reciprocal_d = s_chroma_reciprocal_q;
s_chroma_dc_code_d = s_chroma_dc_code_q;
s_chroma_dc_len_d = s_chroma_dc_len_q;
s_chroma_ac_code_d = s_chroma_ac_code_q;
s_chroma_ac_len_d = s_chroma_ac_len_q;
s_read_mcu_col_d = s_read_mcu_col_q;
s_read_mcu_row_d = s_read_mcu_row_q;
s_read_uv_d = s_read_uv_q;
@@ -676,7 +674,7 @@ module apb4_jpeg (
end
if ((s_state_q == RingFetchRun) && u_dma_read_axis.tvalid && u_dma_read_axis.tready) begin
s_descriptor_d[s_descriptor_beat_q*64+:64] = u_dma_read_axis.tdata;
s_descriptor_beat_d = s_descriptor_beat_q + 1'b1;
s_descriptor_beat_d = s_descriptor_beat_q + 1'b1;
end
if ((s_state_q == RingWriteRun) && u_dma_write_axis.tvalid && u_dma_write_axis.tready) begin
s_descriptor_beat_d = s_descriptor_beat_q + 1'b1;
@@ -708,8 +706,8 @@ module apb4_jpeg (
s_state_d = DecodeStart;
end
end else if (s_reg_ring_kick) begin
s_ring_stalled_d = 1'b0;
s_ring_err_d = 1'b0;
s_ring_stalled_d = 1'b0;
s_ring_err_d = 1'b0;
s_ring_completed_d = 16'd0;
if (!s_ring_config_valid || quiesce_i || resource_reset_i) begin
s_err_event_d = 1'b1;
@@ -719,14 +717,14 @@ module apb4_jpeg (
end else if (s_ring_head_q != s_ring_tail) begin
s_descriptor_beat_d = 5'd0;
s_ring_job_active_d = 1'b1;
s_state_d = RingFetchStart;
s_state_d = RingFetchStart;
end
end
end
RingFetchStart: begin
if (!s_dma_read_busy) begin
s_descriptor_beat_d = 5'd0;
s_state_d = RingFetchRun;
s_state_d = RingFetchRun;
end
end
RingFetchRun: begin
@@ -748,22 +746,22 @@ module apb4_jpeg (
s_read_pending_d = 1'b0;
s_surface_write_pending_d = 1'b0;
if (!s_descriptor_q[`APB4_JPEG__DESCRIPTOR_OWN]) begin
s_ring_stalled_d = 1'b1;
s_ring_event_d = 1'b1;
s_ring_stalled_d = 1'b1;
s_ring_event_d = 1'b1;
s_ring_job_active_d = 1'b0;
s_state_d = Idle;
s_state_d = Idle;
end else if (!s_start_valid) begin
s_descriptor_d[`APB4_JPEG__DESCRIPTOR_OWN] = 1'b0;
s_descriptor_d[63:32] = 32'd0;
s_descriptor_d[`APB4_JPEG__DESCRIPTOR_OWN] = 1'b0;
s_descriptor_d[63:32] = 32'd0;
s_descriptor_d[32+`APB4_JPEG__DESCRIPTOR_STATUS_DONE] = 1'b1;
s_descriptor_d[32+`APB4_JPEG__DESCRIPTOR_STATUS_ERR] = 1'b1;
s_descriptor_d[40+:5] = 5'd1;
s_err_event_d = 1'b1;
s_err_code_d = 5'd1;
s_err_stage_d = 4'd1;
s_ring_err_d = 1'b1;
s_descriptor_beat_d = 5'd0;
s_state_d = RingWriteStart;
s_descriptor_d[32+`APB4_JPEG__DESCRIPTOR_STATUS_ERR] = 1'b1;
s_descriptor_d[40+:5] = 5'd1;
s_err_event_d = 1'b1;
s_err_code_d = 5'd1;
s_err_stage_d = 4'd1;
s_ring_err_d = 1'b1;
s_descriptor_beat_d = 5'd0;
s_state_d = RingWriteStart;
end else if (s_encode_mode) begin
s_state_d = EncodeCacheLumaStart;
end else begin
@@ -773,34 +771,22 @@ module apb4_jpeg (
EncodeCacheLumaStart: s_state_d = EncodeCacheLumaWait;
EncodeCacheLumaWait: begin
if (s_cache_valid) begin
s_luma_quant_d = s_cache_quant;
s_luma_reciprocal_d = s_cache_reciprocal;
s_luma_dc_code_d = s_cache_dc_code;
s_luma_dc_len_d = s_cache_dc_len;
s_luma_ac_code_d = s_cache_ac_code;
s_luma_ac_len_d = s_cache_ac_len;
if (s_cache_err) begin
s_err_event_d = 1'b1;
s_err_code_d = 5'd3;
s_err_stage_d = 4'd1;
if (s_ring_job_active_q) begin
s_descriptor_d[`APB4_JPEG__DESCRIPTOR_OWN] = 1'b0;
s_descriptor_d[63:32] = 32'd3;
s_descriptor_d[40+:5] = 5'd3;
s_descriptor_beat_d = 5'd0;
s_ring_err_d = 1'b1;
s_state_d = RingWriteStart;
s_descriptor_d[63:32] = 32'd3;
s_descriptor_d[40+:5] = 5'd3;
s_descriptor_beat_d = 5'd0;
s_ring_err_d = 1'b1;
s_state_d = RingWriteStart;
end else begin
s_state_d = Idle;
end
end else if (s_job_sampling == 2'd0) begin
s_chroma_quant_d = s_cache_quant;
s_chroma_reciprocal_d = s_cache_reciprocal;
s_chroma_dc_code_d = s_cache_dc_code;
s_chroma_dc_len_d = s_cache_dc_len;
s_chroma_ac_code_d = s_cache_ac_code;
s_chroma_ac_len_d = s_cache_ac_len;
s_state_d = EncodeStart;
s_state_d = EncodeStart;
end else begin
s_state_d = EncodeCacheChromaStart;
end
@@ -809,23 +795,17 @@ module apb4_jpeg (
EncodeCacheChromaStart: s_state_d = EncodeCacheChromaWait;
EncodeCacheChromaWait: begin
if (s_cache_valid) begin
s_chroma_quant_d = s_cache_quant;
s_chroma_reciprocal_d = s_cache_reciprocal;
s_chroma_dc_code_d = s_cache_dc_code;
s_chroma_dc_len_d = s_cache_dc_len;
s_chroma_ac_code_d = s_cache_ac_code;
s_chroma_ac_len_d = s_cache_ac_len;
if (s_cache_err) begin
s_err_event_d = 1'b1;
s_err_code_d = 5'd3;
s_err_stage_d = 4'd1;
if (s_ring_job_active_q) begin
s_descriptor_d[`APB4_JPEG__DESCRIPTOR_OWN] = 1'b0;
s_descriptor_d[63:32] = 32'd3;
s_descriptor_d[40+:5] = 5'd3;
s_descriptor_beat_d = 5'd0;
s_ring_err_d = 1'b1;
s_state_d = RingWriteStart;
s_descriptor_d[63:32] = 32'd3;
s_descriptor_d[40+:5] = 5'd3;
s_descriptor_beat_d = 5'd0;
s_ring_err_d = 1'b1;
s_state_d = RingWriteStart;
end else begin
s_state_d = Idle;
end
@@ -966,7 +946,7 @@ module apb4_jpeg (
s_abort_done_d = 1'b1;
if (s_ring_job_active_q) begin
s_descriptor_beat_d = 5'd0;
s_state_d = RingWriteStart;
s_state_d = RingWriteStart;
end else begin
s_state_d = Idle;
end
@@ -975,17 +955,17 @@ module apb4_jpeg (
RingWriteStart: begin
if (!s_dma_write_busy) begin
s_descriptor_beat_d = 5'd0;
s_state_d = RingWriteRun;
s_state_d = RingWriteRun;
end
end
RingWriteRun: begin
if (s_dma_write_done) begin
s_ring_head_d = s_ring_next_head;
s_ring_head_d = s_ring_next_head;
s_ring_completed_d = s_ring_completed_q + 1'b1;
if (s_descriptor_q[`APB4_JPEG__DESCRIPTOR_IOC] ||
(s_irq_coalesce[15:0] == 16'd0) ||
(s_ring_completed_q + 1'b1 >= s_irq_coalesce[15:0])) begin
s_ring_event_d = 1'b1;
s_ring_event_d = 1'b1;
s_ring_completed_d = 16'd0;
end
s_ring_job_active_d = 1'b0;
@@ -994,7 +974,7 @@ module apb4_jpeg (
!(s_ring_err_q && s_ring_control[`APB4_JPEG__RING_CONTROL_STOP_ERR])) begin
s_ring_job_active_d = 1'b1;
s_descriptor_beat_d = 5'd0;
s_state_d = RingFetchStart;
s_state_d = RingFetchStart;
end else begin
s_state_d = Idle;
end
@@ -1004,11 +984,11 @@ module apb4_jpeg (
endcase
if (s_reg_soft_reset && (s_state_q == Idle)) begin
s_ring_head_d = 32'd0;
s_ring_head_d = 32'd0;
s_ring_job_active_d = 1'b0;
s_ring_stalled_d = 1'b0;
s_ring_err_d = 1'b0;
s_ring_completed_d = 16'd0;
s_ring_stalled_d = 1'b0;
s_ring_err_d = 1'b0;
s_ring_completed_d = 16'd0;
end
if ((s_reg_abort || resource_reset_i) && (s_state_q != Idle)) begin
@@ -1018,7 +998,7 @@ module apb4_jpeg (
(s_state_q == EncodeStart) || (s_state_q == EncodeRun) ||
(s_state_q == DecodeStart) || (s_state_q == DecodeRun))) begin
s_descriptor_d[`APB4_JPEG__DESCRIPTOR_OWN] = 1'b0;
s_descriptor_d[63:32] = 32'd5;
s_descriptor_d[63:32] = 32'd5;
end else begin
s_ring_job_active_d = 1'b0;
end
@@ -1033,8 +1013,8 @@ module apb4_jpeg (
s_ring_err_d = s_ring_job_active_q;
if (s_ring_job_active_q && ((s_state_q == EncodeRun) || (s_state_q == DecodeRun))) begin
s_descriptor_d[`APB4_JPEG__DESCRIPTOR_OWN] = 1'b0;
s_descriptor_d[63:32] = 32'd3;
s_descriptor_d[40+:5] = s_dma_err_read ? 5'd13 : 5'd14;
s_descriptor_d[63:32] = 32'd3;
s_descriptor_d[40+:5] = s_dma_err_read ? 5'd13 : 5'd14;
end else begin
s_ring_job_active_d = 1'b0;
end
@@ -1046,9 +1026,9 @@ module apb4_jpeg (
s_err_stage_d = 4'd7;
if (s_ring_job_active_q) begin
s_descriptor_d[`APB4_JPEG__DESCRIPTOR_OWN] = 1'b0;
s_descriptor_d[63:32] = 32'd3;
s_descriptor_d[40+:5] = s_encode_err ? 5'd18 : s_decode_err_code;
s_ring_err_d = 1'b1;
s_descriptor_d[63:32] = 32'd3;
s_descriptor_d[40+:5] = s_encode_err ? 5'd18 : s_decode_err_code;
s_ring_err_d = 1'b1;
end
s_state_d = AbortDrain;
end
@@ -1147,30 +1127,60 @@ module apb4_jpeg (
);
jpeg_table_cache u_encode_table_cache (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_cache_start),
.context_i (s_job_config[9:8]),
.quant_id_i (s_cache_quant_id),
.dc_id_i (s_cache_dc_id),
.ac_id_i (s_cache_ac_id),
.start_ready_o (),
.lookup_o (s_table_lookup),
.lookup_context_o(s_table_lookup_context),
.lookup_kind_o (s_table_lookup_kind),
.lookup_index_o (s_table_lookup_index),
.lookup_data_i (s_table_lookup_data),
.lookup_valid_i (s_table_lookup_valid),
.lookup_err_i (s_table_lookup_err),
.quant_o (s_cache_quant),
.reciprocal_o (s_cache_reciprocal),
.dc_code_o (s_cache_dc_code),
.dc_length_o (s_cache_dc_len),
.ac_code_o (s_cache_ac_code),
.ac_length_o (s_cache_ac_len),
.result_valid_o (s_cache_valid),
.result_ready_i (s_cache_ready),
.error_o (s_cache_err)
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_cache_start),
.context_i (s_job_config[9:8]),
.quant_id_i (s_cache_quant_id),
.dc_id_i (s_cache_dc_id),
.ac_id_i (s_cache_ac_id),
.start_ready_o (),
.lookup_o (s_table_lookup),
.lookup_context_o (s_table_lookup_context),
.lookup_kind_o (s_table_lookup_kind),
.lookup_index_o (s_table_lookup_index),
.lookup_data_i (s_table_lookup_data),
.lookup_valid_i (s_table_lookup_valid),
.lookup_err_i (s_table_lookup_err),
.entry_kind_o (s_cache_entry_kind),
.entry_index_o (s_cache_entry_index),
.entry_data_o (s_cache_entry_data),
.entry_reciprocal_o(s_cache_entry_reciprocal),
.entry_valid_o (s_cache_entry_valid),
.entry_ready_i (1'b1),
.result_valid_o (s_cache_valid),
.result_ready_i (s_cache_ready),
.error_o (s_cache_err)
);
jpeg_table_register_bank u_luma_table_register_bank (
.clk_i (clk_i),
.write_i (s_cache_entry_valid && (s_state_q == EncodeCacheLumaWait)),
.entry_kind_i (s_cache_entry_kind),
.entry_index_i (s_cache_entry_index),
.entry_data_i (s_cache_entry_data),
.entry_reciprocal_i(s_cache_entry_reciprocal),
.quant_o (s_luma_quant_q),
.reciprocal_o (s_luma_reciprocal_q),
.dc_code_o (s_luma_dc_code_q),
.dc_length_o (s_luma_dc_len_q),
.ac_code_o (s_luma_ac_code_q),
.ac_length_o (s_luma_ac_len_q)
);
jpeg_table_register_bank u_chroma_table_register_bank (
.clk_i (clk_i),
.write_i (s_cache_entry_valid && (s_state_q == EncodeCacheChromaWait)),
.entry_kind_i (s_cache_entry_kind),
.entry_index_i (s_cache_entry_index),
.entry_data_i (s_cache_entry_data),
.entry_reciprocal_i(s_cache_entry_reciprocal),
.quant_o (s_chroma_quant_q),
.reciprocal_o (s_chroma_reciprocal_q),
.dc_code_o (s_chroma_dc_code_q),
.dc_length_o (s_chroma_dc_len_q),
.ac_code_o (s_chroma_ac_code_q),
.ac_length_o (s_chroma_ac_len_q)
);
jpeg_dma u_dma (
@@ -1205,50 +1215,106 @@ module apb4_jpeg (
.axi4 (axi4)
);
jpeg_encode_core u_encode_core (
.clk_i (clk_i),
.rst_n_i (s_core_rst_n),
.start_i (s_encode_start),
.width_i (s_job_width),
.height_i (s_job_height),
.sampling_i (s_job_sampling),
.input_format_i (s_job_input_format),
.restart_interval_i (s_restart_interval[15:0]),
.luma_quant_i (s_luma_quant_q),
.luma_reciprocal_i (s_luma_reciprocal_q),
.luma_dc_code_i (s_luma_dc_code_q),
.luma_dc_length_i (s_luma_dc_len_q),
.luma_ac_code_i (s_luma_ac_code_q),
.luma_ac_length_i (s_luma_ac_len_q),
.chroma_quant_i (s_chroma_quant_q),
.chroma_reciprocal_i(s_chroma_reciprocal_q),
.chroma_dc_code_i (s_chroma_dc_code_q),
.chroma_dc_length_i (s_chroma_dc_len_q),
.chroma_ac_code_i (s_chroma_ac_code_q),
.chroma_ac_length_i (s_chroma_ac_len_q),
.pixel_axis (u_encode_pixel_axis),
.bitstream_axis (u_encode_bitstream_axis),
.start_ready_o (),
.busy_o (s_encode_busy),
.done_o (s_encode_done),
.error_o (s_encode_err)
assign s_coefficient_start = s_encode_coefficient_start || s_decode_coefficient_start;
assign s_coefficient_decode = s_decode_coefficient_start;
assign s_coefficient_block = s_decode_coefficient_start ?
s_decode_coefficient_block : s_encode_coefficient_block;
assign s_coefficient_quant = s_decode_coefficient_start ?
s_decode_coefficient_quant : s_encode_coefficient_quant;
assign s_coefficient_reciprocal = s_decode_coefficient_start ?
s_decode_coefficient_reciprocal :
s_encode_coefficient_reciprocal;
assign s_encode_coefficient_start_ready = s_coefficient_start_ready &&
!s_decode_coefficient_start;
assign s_decode_coefficient_start_ready = s_coefficient_start_ready;
assign s_coefficient_result_ready = (s_state_q == DecodeRun) ?
s_decode_coefficient_result_ready :
s_encode_coefficient_result_ready;
jpeg_coefficient_engine u_coefficient_engine (
.clk_i (clk_i),
.rst_n_i (s_core_rst_n),
.start_i (s_coefficient_start),
.decode_i (s_coefficient_decode),
.block_i (s_coefficient_block),
.quant_i (s_coefficient_quant),
.reciprocal_i (s_coefficient_reciprocal),
.start_ready_o (s_coefficient_start_ready),
.block_o (s_coefficient_result),
.result_valid_o(s_coefficient_result_valid),
.result_ready_i(s_coefficient_result_ready),
.table_err_o (s_coefficient_table_err),
.overflow_o (s_coefficient_overflow)
);
jpeg_decode_core u_decode_core (
.clk_i (clk_i),
.rst_n_i (s_core_rst_n),
.start_i (s_decode_start),
.output_format_i(s_job_output_format),
.bitstream_axis (u_decode_bitstream_axis),
.pixel_axis (u_decode_pixel_axis),
.start_ready_o (),
.busy_o (s_decode_busy),
.done_o (s_decode_done),
.width_o (s_decode_width),
.height_o (s_decode_height),
.sampling_o (s_decode_sampling),
.error_o (s_decode_err),
.error_code_o (s_decode_err_code)
jpeg_encode_core #(
.ExternalCoefficientEngine(1'b1)
) u_encode_core (
.clk_i (clk_i),
.rst_n_i (s_core_rst_n),
.start_i (s_encode_start),
.width_i (s_job_width),
.height_i (s_job_height),
.sampling_i (s_job_sampling),
.input_format_i (s_job_input_format),
.restart_interval_i (s_restart_interval[15:0]),
.luma_quant_i (s_luma_quant_q),
.luma_reciprocal_i (s_luma_reciprocal_q),
.luma_dc_code_i (s_luma_dc_code_q),
.luma_dc_length_i (s_luma_dc_len_q),
.luma_ac_code_i (s_luma_ac_code_q),
.luma_ac_length_i (s_luma_ac_len_q),
.chroma_quant_i (s_chroma_quant_q),
.chroma_reciprocal_i (s_chroma_reciprocal_q),
.chroma_dc_code_i (s_chroma_dc_code_q),
.chroma_dc_length_i (s_chroma_dc_len_q),
.chroma_ac_code_i (s_chroma_ac_code_q),
.chroma_ac_length_i (s_chroma_ac_len_q),
.coefficient_start_o (s_encode_coefficient_start),
.coefficient_block_o (s_encode_coefficient_block),
.coefficient_quant_o (s_encode_coefficient_quant),
.coefficient_reciprocal_o (s_encode_coefficient_reciprocal),
.coefficient_start_ready_i (s_encode_coefficient_start_ready),
.coefficient_result_i (s_coefficient_result),
.coefficient_result_valid_i(s_coefficient_result_valid && (s_state_q == EncodeRun)),
.coefficient_result_ready_o(s_encode_coefficient_result_ready),
.coefficient_table_err_i (s_coefficient_table_err),
.coefficient_overflow_i (s_coefficient_overflow),
.pixel_axis (u_encode_pixel_axis),
.bitstream_axis (u_encode_bitstream_axis),
.start_ready_o (),
.busy_o (s_encode_busy),
.done_o (s_encode_done),
.error_o (s_encode_err)
);
jpeg_decode_core #(
.ExternalCoefficientEngine(1'b1)
) u_decode_core (
.clk_i (clk_i),
.rst_n_i (s_core_rst_n),
.start_i (s_decode_start),
.output_format_i (s_job_output_format),
.coefficient_start_o (s_decode_coefficient_start),
.coefficient_block_o (s_decode_coefficient_block),
.coefficient_quant_o (s_decode_coefficient_quant),
.coefficient_reciprocal_o (s_decode_coefficient_reciprocal),
.coefficient_start_ready_i (s_decode_coefficient_start_ready),
.coefficient_result_i (s_coefficient_result),
.coefficient_result_valid_i(s_coefficient_result_valid && (s_state_q == DecodeRun)),
.coefficient_result_ready_o(s_decode_coefficient_result_ready),
.coefficient_table_err_i (s_coefficient_table_err),
.coefficient_overflow_i (s_coefficient_overflow),
.bitstream_axis (u_decode_bitstream_axis),
.pixel_axis (u_decode_pixel_axis),
.start_ready_o (),
.busy_o (s_decode_busy),
.done_o (s_decode_done),
.width_o (s_decode_width),
.height_o (s_decode_height),
.sampling_o (s_decode_sampling),
.error_o (s_decode_err),
.error_code_o (s_decode_err_code)
);
dffr #(
@@ -1315,102 +1381,6 @@ module apb4_jpeg (
.dat_i (s_ring_completed_d),
.dat_o (s_ring_completed_q)
);
dffr #(
.DATA_WIDTH(512)
) u_luma_quant_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_luma_quant_d),
.dat_o (s_luma_quant_q)
);
dffr #(
.DATA_WIDTH(1600)
) u_luma_reciprocal_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_luma_reciprocal_d),
.dat_o (s_luma_reciprocal_q)
);
dffr #(
.DATA_WIDTH(192)
) u_luma_dc_code_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_luma_dc_code_d),
.dat_o (s_luma_dc_code_q)
);
dffr #(
.DATA_WIDTH(60)
) u_luma_dc_len_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_luma_dc_len_d),
.dat_o (s_luma_dc_len_q)
);
dffr #(
.DATA_WIDTH(4096)
) u_luma_ac_code_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_luma_ac_code_d),
.dat_o (s_luma_ac_code_q)
);
dffr #(
.DATA_WIDTH(1280)
) u_luma_ac_len_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_luma_ac_len_d),
.dat_o (s_luma_ac_len_q)
);
dffr #(
.DATA_WIDTH(512)
) u_chroma_quant_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_chroma_quant_d),
.dat_o (s_chroma_quant_q)
);
dffr #(
.DATA_WIDTH(1600)
) u_chroma_reciprocal_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_chroma_reciprocal_d),
.dat_o (s_chroma_reciprocal_q)
);
dffr #(
.DATA_WIDTH(192)
) u_chroma_dc_code_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_chroma_dc_code_d),
.dat_o (s_chroma_dc_code_q)
);
dffr #(
.DATA_WIDTH(60)
) u_chroma_dc_len_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_chroma_dc_len_d),
.dat_o (s_chroma_dc_len_q)
);
dffr #(
.DATA_WIDTH(4096)
) u_chroma_ac_code_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_chroma_ac_code_d),
.dat_o (s_chroma_ac_code_q)
);
dffr #(
.DATA_WIDTH(1280)
) u_chroma_ac_len_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_chroma_ac_len_d),
.dat_o (s_chroma_ac_len_q)
);
dffr #(
.DATA_WIDTH(12)
) u_read_mcu_col_dffr (
+70 -44
View File
@@ -3,8 +3,9 @@
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND.
module jpeg_block_decoder #(
parameter int unsigned ElementWidth = 24,
parameter int unsigned WindowWidth = 32
parameter int unsigned ElementWidth = 24,
parameter int unsigned WindowWidth = 32,
parameter bit ExternalCoefficientEngine = 1'b0
) (
// verilog_format: off -- preserve command, table, bit-window, and block columns
input logic clk_i,
@@ -19,6 +20,14 @@ module jpeg_block_decoder #(
input logic [256*5-1:0] ac_length_i,
input logic [WindowWidth-1:0] bit_window_i,
input logic [$clog2(WindowWidth+1)-1:0] bit_count_i,
output logic coefficient_start_o,
output logic [64*ElementWidth-1:0] coefficient_block_o,
input logic coefficient_start_ready_i,
input logic [64*ElementWidth-1:0] coefficient_result_i,
input logic coefficient_result_valid_i,
output logic coefficient_result_ready_o,
input logic coefficient_table_err_i,
input logic coefficient_overflow_i,
output logic [$clog2(WindowWidth+1)-1:0] bit_consume_o,
output logic bit_consume_valid_o,
output logic start_ready_o,
@@ -78,18 +87,23 @@ module jpeg_block_decoder #(
end
endfunction
assign s_state_q = state_e'(s_state_bits_q);
assign start_ready_o = s_state_q == Idle;
assign block_o = s_block_q;
assign dc_o = s_dc_q;
assign result_valid_o = s_state_q == Result;
assign error_o = s_err_q;
assign s_entropy_start = (s_state_q == Idle) && start_i;
assign s_entropy_ready = s_state_q == Entropy;
assign s_quant_start = (s_state_q == Entropy) && s_entropy_valid;
assign s_quant_ready = s_state_q == Dequantize;
assign s_state_q = state_e'(s_state_bits_q);
assign start_ready_o = s_state_q == Idle;
assign block_o = s_block_q;
assign dc_o = s_dc_q;
assign result_valid_o = s_state_q == Result;
assign error_o = s_err_q;
assign s_entropy_start = (s_state_q == Idle) && start_i;
assign s_entropy_ready = (s_state_q == Entropy) &&
(!ExternalCoefficientEngine || coefficient_start_ready_i);
assign s_quant_start = (s_state_q == Entropy) && s_entropy_valid;
assign s_quant_ready = s_state_q == Dequantize;
assign s_transform_start = (s_state_q == Dequantize) && s_quant_valid;
assign s_transform_ready = s_state_q == Transform;
assign coefficient_start_o = ExternalCoefficientEngine && (s_state_q == Entropy) &&
s_entropy_valid;
assign coefficient_block_o = s_entropy_block;
assign coefficient_result_ready_o = ExternalCoefficientEngine && (s_state_q == Transform);
always_comb begin
for (int unsigned index = 0; index < 64; index++) begin
@@ -111,10 +125,10 @@ module jpeg_block_decoder #(
end
end
Entropy: begin
if (s_entropy_valid) begin
if (s_entropy_valid && (!ExternalCoefficientEngine || coefficient_start_ready_i)) begin
s_dc_d = s_entropy_dc;
s_err_d |= s_entropy_err;
s_state_d = Dequantize;
s_state_d = ExternalCoefficientEngine ? Transform : Dequantize;
end
end
Dequantize: begin
@@ -126,6 +140,9 @@ module jpeg_block_decoder #(
Transform: begin
if (s_transform_valid) begin
s_block_d = s_clamped_block;
if (ExternalCoefficientEngine) begin
s_err_d |= coefficient_table_err_i || coefficient_overflow_i;
end
s_state_d = Result;
end
end
@@ -162,37 +179,46 @@ module jpeg_block_decoder #(
.error_o (s_entropy_err)
);
jpeg_quantizer #(
.ElementWidth(ElementWidth)
) u_dequantizer (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_quant_start),
.dequantize_i (1'b1),
.block_i (s_entropy_block),
.quant_i (quant_i),
.reciprocal_i (reciprocal_i),
.start_ready_o (),
.block_o (s_quant_block),
.result_valid_o(s_quant_valid),
.result_ready_i(s_quant_ready),
.table_err_o (s_quant_err),
.overflow_o (s_quant_overflow)
);
if (ExternalCoefficientEngine) begin : gen_external_coefficient_engine
assign s_quant_block = '0;
assign s_quant_valid = 1'b0;
assign s_quant_err = 1'b0;
assign s_quant_overflow = 1'b0;
assign s_transform_block = coefficient_result_i;
assign s_transform_valid = coefficient_result_valid_i;
end else begin : gen_internal_coefficient_engine
jpeg_quantizer #(
.ElementWidth(ElementWidth)
) u_dequantizer (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_quant_start),
.dequantize_i (1'b1),
.block_i (s_entropy_block),
.quant_i (quant_i),
.reciprocal_i (reciprocal_i),
.start_ready_o (),
.block_o (s_quant_block),
.result_valid_o(s_quant_valid),
.result_ready_i(s_quant_ready),
.table_err_o (s_quant_err),
.overflow_o (s_quant_overflow)
);
jpeg_transform #(
.ElementWidth(ElementWidth)
) u_inverse_transform (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_transform_start),
.inverse_i (1'b1),
.block_i (s_quant_block),
.start_ready_o (),
.block_o (s_transform_block),
.result_valid_o(s_transform_valid),
.result_ready_i(s_transform_ready)
);
jpeg_transform #(
.ElementWidth(ElementWidth)
) u_inverse_transform (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_transform_start),
.inverse_i (1'b1),
.block_i (s_quant_block),
.start_ready_o (),
.block_o (s_transform_block),
.result_valid_o(s_transform_valid),
.result_ready_i(s_transform_ready)
);
end
dffr #(
.DATA_WIDTH(3)
+62 -36
View File
@@ -3,9 +3,10 @@
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND.
module jpeg_block_encoder #(
parameter int unsigned ElementWidth = 24,
parameter int unsigned TokenSlots = 4,
parameter int unsigned TokenWidth = 32
parameter int unsigned ElementWidth = 24,
parameter int unsigned TokenSlots = 4,
parameter int unsigned TokenWidth = 32,
parameter bit ExternalCoefficientEngine = 1'b0
) (
// verilog_format: off -- preserve command, table, token, and block columns
input logic clk_i,
@@ -19,6 +20,14 @@ module jpeg_block_encoder #(
input logic [12*5-1:0] dc_length_i,
input logic [256*16-1:0] ac_code_i,
input logic [256*5-1:0] ac_length_i,
output logic coefficient_start_o,
output logic [64*ElementWidth-1:0] coefficient_block_o,
input logic coefficient_start_ready_i,
input logic [64*ElementWidth-1:0] coefficient_result_i,
input logic coefficient_result_valid_i,
output logic coefficient_result_ready_o,
input logic coefficient_table_err_i,
input logic coefficient_overflow_i,
output logic start_ready_o,
output logic [TokenSlots*TokenWidth-1:0] token_bits_o,
output logic [TokenSlots*6-1:0] token_length_o,
@@ -79,9 +88,14 @@ module jpeg_block_encoder #(
assign s_transform_ready = s_state_q == Transform;
assign s_quant_start = (s_state_q == Transform) && s_transform_valid;
assign s_quant_ready = s_state_q == Quantize;
assign s_entropy_start = (s_state_q == Quantize) && s_quant_valid;
assign s_entropy_start = ExternalCoefficientEngine ?
((s_state_q == Transform) && coefficient_result_valid_i) :
((s_state_q == Quantize) && s_quant_valid);
assign s_entropy_last_accept = (s_state_q == Entropy) && token_valid_o && token_ready_i &&
token_last_o;
assign coefficient_start_o = ExternalCoefficientEngine && (s_state_q == Idle) && start_i;
assign coefficient_block_o = s_level_shifted;
assign coefficient_result_ready_o = ExternalCoefficientEngine && (s_state_q == Transform);
always_comb begin
s_state_d = s_state_q;
@@ -89,13 +103,16 @@ module jpeg_block_encoder #(
s_err_d = s_err_q;
unique case (s_state_q)
Idle: begin
if (start_i) begin
if (start_i && (!ExternalCoefficientEngine || coefficient_start_ready_i)) begin
s_err_d = 1'b0;
s_state_d = Transform;
end
end
Transform: begin
if (s_transform_valid) begin
if (ExternalCoefficientEngine && coefficient_result_valid_i) begin
s_err_d |= coefficient_table_err_i || coefficient_overflow_i;
s_state_d = Entropy;
end else if (!ExternalCoefficientEngine && s_transform_valid) begin
s_state_d = Quantize;
end
end
@@ -121,37 +138,46 @@ module jpeg_block_encoder #(
endcase
end
jpeg_transform #(
.ElementWidth(ElementWidth)
) u_forward_transform (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_transform_start),
.inverse_i (1'b0),
.block_i (s_level_shifted),
.start_ready_o (),
.block_o (s_transform_block),
.result_valid_o(s_transform_valid),
.result_ready_i(s_transform_ready)
);
if (ExternalCoefficientEngine) begin : gen_external_coefficient_engine
assign s_transform_block = '0;
assign s_transform_valid = 1'b0;
assign s_quant_block = coefficient_result_i;
assign s_quant_valid = coefficient_result_valid_i;
assign s_quant_err = coefficient_table_err_i;
assign s_quant_overflow = coefficient_overflow_i;
end else begin : gen_internal_coefficient_engine
jpeg_transform #(
.ElementWidth(ElementWidth)
) u_forward_transform (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_transform_start),
.inverse_i (1'b0),
.block_i (s_level_shifted),
.start_ready_o (),
.block_o (s_transform_block),
.result_valid_o(s_transform_valid),
.result_ready_i(s_transform_ready)
);
jpeg_quantizer #(
.ElementWidth(ElementWidth)
) u_quantizer (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_quant_start),
.dequantize_i (1'b0),
.block_i (s_transform_block),
.quant_i (quant_i),
.reciprocal_i (reciprocal_i),
.start_ready_o (),
.block_o (s_quant_block),
.result_valid_o(s_quant_valid),
.result_ready_i(s_quant_ready),
.table_err_o (s_quant_err),
.overflow_o (s_quant_overflow)
);
jpeg_quantizer #(
.ElementWidth(ElementWidth)
) u_quantizer (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_quant_start),
.dequantize_i (1'b0),
.block_i (s_transform_block),
.quant_i (quant_i),
.reciprocal_i (reciprocal_i),
.start_ready_o (),
.block_o (s_quant_block),
.result_valid_o(s_quant_valid),
.result_ready_i(s_quant_ready),
.table_err_o (s_quant_err),
.overflow_o (s_quant_overflow)
);
end
jpeg_entropy_encoder #(
.ElementWidth(ElementWidth),
@@ -0,0 +1,190 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND.
module jpeg_coefficient_engine #(
parameter int unsigned ElementWidth = 24
) (
// verilog_format: off -- preserve request, table, and result interface columns
input logic clk_i,
input logic rst_n_i,
input logic start_i,
input logic decode_i,
input logic [64*ElementWidth-1:0] block_i,
input logic [64*8-1:0] quant_i,
input logic [64*25-1:0] reciprocal_i,
output logic start_ready_o,
output logic [64*ElementWidth-1:0] block_o,
output logic result_valid_o,
input logic result_ready_i,
output logic table_err_o,
output logic overflow_o
// verilog_format: on
);
typedef enum logic [2:0] {
Idle,
EncodeTransform,
EncodeQuantize,
DecodeDequantize,
DecodeTransform,
Result
} state_e;
state_e s_state_d;
state_e s_state_q;
logic [ 2:0] s_state_bits_q;
logic s_decode_d;
logic s_decode_q;
logic s_active_decode;
logic s_table_err_d;
logic s_table_err_q;
logic s_overflow_d;
logic s_overflow_q;
logic s_transform_start;
logic s_transform_ready;
logic [64*ElementWidth-1:0] s_transform_input;
logic [64*ElementWidth-1:0] s_transform_result;
logic s_transform_valid;
logic s_quant_start;
logic s_quant_ready;
logic [64*ElementWidth-1:0] s_quant_input;
logic [64*ElementWidth-1:0] s_quant_result;
logic s_quant_valid;
logic s_quant_table_err;
logic s_quant_overflow;
assign s_state_q = state_e'(s_state_bits_q);
assign start_ready_o = s_state_q == Idle;
assign result_valid_o = s_state_q == Result;
assign block_o = s_decode_q ? s_transform_result : s_quant_result;
assign table_err_o = s_table_err_q;
assign overflow_o = s_overflow_q;
assign s_active_decode = (s_state_q == Idle) ? decode_i : s_decode_q;
assign s_transform_start = ((s_state_q == Idle) && start_i && !decode_i) ||
((s_state_q == DecodeDequantize) && s_quant_valid);
assign s_transform_ready = (s_state_q == EncodeTransform) || (s_state_q == DecodeTransform);
assign s_transform_input = s_active_decode ? s_quant_result : block_i;
assign s_quant_start = ((s_state_q == Idle) && start_i && decode_i) ||
((s_state_q == EncodeTransform) && s_transform_valid);
assign s_quant_ready = (s_state_q == EncodeQuantize) || (s_state_q == DecodeDequantize);
assign s_quant_input = s_active_decode ? block_i : s_transform_result;
always_comb begin
s_state_d = s_state_q;
s_decode_d = s_decode_q;
s_table_err_d = s_table_err_q;
s_overflow_d = s_overflow_q;
unique case (s_state_q)
Idle: begin
if (start_i) begin
s_decode_d = decode_i;
s_table_err_d = 1'b0;
s_overflow_d = 1'b0;
if (decode_i) begin
s_state_d = DecodeDequantize;
end else begin
s_state_d = EncodeTransform;
end
end
end
EncodeTransform: begin
if (s_transform_valid) begin
s_state_d = EncodeQuantize;
end
end
EncodeQuantize: begin
if (s_quant_valid) begin
s_table_err_d |= s_quant_table_err;
s_overflow_d |= s_quant_overflow;
s_state_d = Result;
end
end
DecodeDequantize: begin
if (s_quant_valid) begin
s_table_err_d |= s_quant_table_err;
s_overflow_d |= s_quant_overflow;
s_state_d = DecodeTransform;
end
end
DecodeTransform: begin
if (s_transform_valid) begin
s_state_d = Result;
end
end
Result: begin
if (result_ready_i) begin
s_state_d = Idle;
end
end
default: s_state_d = Idle;
endcase
end
jpeg_transform #(
.ElementWidth(ElementWidth)
) u_transform (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_transform_start),
.inverse_i (s_decode_q),
.block_i (s_transform_input),
.start_ready_o (),
.block_o (s_transform_result),
.result_valid_o(s_transform_valid),
.result_ready_i(s_transform_ready)
);
jpeg_quantizer #(
.ElementWidth(ElementWidth)
) u_quantizer (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_quant_start),
.dequantize_i (s_active_decode),
.block_i (s_quant_input),
.quant_i (quant_i),
.reciprocal_i (reciprocal_i),
.start_ready_o (),
.block_o (s_quant_result),
.result_valid_o(s_quant_valid),
.result_ready_i(s_quant_ready),
.table_err_o (s_quant_table_err),
.overflow_o (s_quant_overflow)
);
dffr #(
.DATA_WIDTH(3)
) u_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_state_d),
.dat_o (s_state_bits_q)
);
dffr u_decode_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_decode_d),
.dat_o (s_decode_q)
);
dffr u_table_err_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_table_err_d),
.dat_o (s_table_err_q)
);
dffr u_overflow_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_overflow_d),
.dat_o (s_overflow_q)
);
`ifndef SV_ASSRT_DISABLE
always_ff @(posedge clk_i) begin
if (rst_n_i && (s_state_q != Idle)) begin
assert ($stable(quant_i));
assert ($stable(reciprocal_i));
end
end
`endif
endmodule
+115 -194
View File
@@ -2,12 +2,24 @@
// retroSoC is licensed under Mulan PSL v2.
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND.
module jpeg_decode_core (
module jpeg_decode_core #(
parameter bit ExternalCoefficientEngine = 1'b0
) (
// verilog_format: off -- preserve command, JPEG input, pixel output, and result columns
input logic clk_i,
input logic rst_n_i,
input logic start_i,
input logic [ 2:0] output_format_i,
output logic coefficient_start_o,
output logic [1535:0] coefficient_block_o,
output logic [ 511:0] coefficient_quant_o,
output logic [1599:0] coefficient_reciprocal_o,
input logic coefficient_start_ready_i,
input logic [1535:0] coefficient_result_i,
input logic coefficient_result_valid_i,
output logic coefficient_result_ready_o,
input logic coefficient_table_err_i,
input logic coefficient_overflow_i,
axi4_stream_if.sink bitstream_axis,
axi4_stream_if.source pixel_axis,
output logic start_ready_o,
@@ -101,38 +113,25 @@ module jpeg_decode_core (
logic [ 1:0] s_cache_quant_id;
logic [ 1:0] s_cache_dc_id;
logic [ 1:0] s_cache_ac_id;
logic [ 511:0] s_cache_quant;
logic [1599:0] s_cache_reciprocal;
logic [ 191:0] s_cache_dc_code;
logic [ 59:0] s_cache_dc_len;
logic [4095:0] s_cache_ac_code;
logic [1279:0] s_cache_ac_len;
logic [ 3:0] s_cache_entry_kind;
logic [ 7:0] s_cache_entry_index;
logic [ 31:0] s_cache_entry_data;
logic [ 24:0] s_cache_entry_reciprocal;
logic s_cache_entry_valid;
logic s_cache_valid;
logic s_cache_ready;
logic s_cache_err;
logic [ 511:0] s_luma_quant_d;
logic [ 511:0] s_luma_quant_q;
logic [1599:0] s_luma_reciprocal_d;
logic [1599:0] s_luma_reciprocal_q;
logic [ 191:0] s_luma_dc_code_d;
logic [ 191:0] s_luma_dc_code_q;
logic [ 59:0] s_luma_dc_len_d;
logic [ 59:0] s_luma_dc_len_q;
logic [4095:0] s_luma_ac_code_d;
logic [4095:0] s_luma_ac_code_q;
logic [1279:0] s_luma_ac_len_d;
logic [1279:0] s_luma_ac_len_q;
logic [ 511:0] s_chroma_quant_d;
logic [ 511:0] s_chroma_quant_q;
logic [1599:0] s_chroma_reciprocal_d;
logic [1599:0] s_chroma_reciprocal_q;
logic [ 191:0] s_chroma_dc_code_d;
logic [ 191:0] s_chroma_dc_code_q;
logic [ 59:0] s_chroma_dc_len_d;
logic [ 59:0] s_chroma_dc_len_q;
logic [4095:0] s_chroma_ac_code_d;
logic [4095:0] s_chroma_ac_code_q;
logic [1279:0] s_chroma_ac_len_d;
logic [1279:0] s_chroma_ac_len_q;
logic [ 31:0] s_bit_window;
@@ -220,6 +219,8 @@ module jpeg_decode_core (
assign s_selected_quant = (s_block_component == 3'd0) ? s_luma_quant_q : s_chroma_quant_q;
assign s_selected_reciprocal = (s_block_component == 3'd0) ?
s_luma_reciprocal_q : s_chroma_reciprocal_q;
assign coefficient_quant_o = s_selected_quant;
assign coefficient_reciprocal_o = s_selected_reciprocal;
assign s_selected_dc_code = (s_block_component == 3'd0) ? s_luma_dc_code_q : s_chroma_dc_code_q;
assign s_selected_dc_len = (s_block_component == 3'd0) ? s_luma_dc_len_q : s_chroma_dc_len_q;
assign s_selected_ac_code = (s_block_component == 3'd0) ? s_luma_ac_code_q : s_chroma_ac_code_q;
@@ -253,28 +254,16 @@ module jpeg_decode_core (
assign s_block_ready = (s_state_q == BlockWait) && s_reconstructor_block_ready;
always_comb begin
s_state_d = s_state_q;
s_output_format_d = s_output_format_q;
s_mcu_col_d = s_mcu_col_q;
s_mcu_row_d = s_mcu_row_q;
s_block_index_d = s_block_index_q;
s_restart_cnt_d = s_restart_cnt_q;
s_restart_index_d = s_restart_index_q;
s_dc_predictor_d = s_dc_predictor_q;
s_err_d = s_err_q;
s_err_code_d = s_err_code_q;
s_luma_quant_d = s_luma_quant_q;
s_luma_reciprocal_d = s_luma_reciprocal_q;
s_luma_dc_code_d = s_luma_dc_code_q;
s_luma_dc_len_d = s_luma_dc_len_q;
s_luma_ac_code_d = s_luma_ac_code_q;
s_luma_ac_len_d = s_luma_ac_len_q;
s_chroma_quant_d = s_chroma_quant_q;
s_chroma_reciprocal_d = s_chroma_reciprocal_q;
s_chroma_dc_code_d = s_chroma_dc_code_q;
s_chroma_dc_len_d = s_chroma_dc_len_q;
s_chroma_ac_code_d = s_chroma_ac_code_q;
s_chroma_ac_len_d = s_chroma_ac_len_q;
s_state_d = s_state_q;
s_output_format_d = s_output_format_q;
s_mcu_col_d = s_mcu_col_q;
s_mcu_row_d = s_mcu_row_q;
s_block_index_d = s_block_index_q;
s_restart_cnt_d = s_restart_cnt_q;
s_restart_index_d = s_restart_index_q;
s_dc_predictor_d = s_dc_predictor_q;
s_err_d = s_err_q;
s_err_code_d = s_err_code_q;
unique case (s_state_q)
Idle: begin
if (start_i) begin
@@ -313,12 +302,6 @@ module jpeg_decode_core (
CacheLumaStart: s_state_d = CacheLumaWait;
CacheLumaWait: begin
if (s_cache_valid) begin
s_luma_quant_d = s_cache_quant;
s_luma_reciprocal_d = s_cache_reciprocal;
s_luma_dc_code_d = s_cache_dc_code;
s_luma_dc_len_d = s_cache_dc_len;
s_luma_ac_code_d = s_cache_ac_code;
s_luma_ac_len_d = s_cache_ac_len;
if (s_cache_err) begin
s_err_d = 1'b1;
s_err_code_d = 5'd3;
@@ -333,12 +316,6 @@ module jpeg_decode_core (
CacheChromaStart: s_state_d = CacheChromaWait;
CacheChromaWait: begin
if (s_cache_valid) begin
s_chroma_quant_d = s_cache_quant;
s_chroma_reciprocal_d = s_cache_reciprocal;
s_chroma_dc_code_d = s_cache_dc_code;
s_chroma_dc_len_d = s_cache_dc_len;
s_chroma_ac_code_d = s_cache_ac_code;
s_chroma_ac_len_d = s_cache_ac_len;
if (s_cache_err) begin
s_err_d = 1'b1;
s_err_code_d = 5'd3;
@@ -499,30 +476,60 @@ module jpeg_decode_core (
);
jpeg_table_cache u_table_cache (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_cache_start),
.context_i (2'd0),
.quant_id_i (s_cache_quant_id),
.dc_id_i (s_cache_dc_id),
.ac_id_i (s_cache_ac_id),
.start_ready_o (),
.lookup_o (s_table_lookup),
.lookup_context_o(s_table_lookup_context),
.lookup_kind_o (s_table_lookup_kind),
.lookup_index_o (s_table_lookup_index),
.lookup_data_i (s_table_lookup_data),
.lookup_valid_i (s_table_lookup_valid),
.lookup_err_i (s_table_lookup_err),
.quant_o (s_cache_quant),
.reciprocal_o (s_cache_reciprocal),
.dc_code_o (s_cache_dc_code),
.dc_length_o (s_cache_dc_len),
.ac_code_o (s_cache_ac_code),
.ac_length_o (s_cache_ac_len),
.result_valid_o (s_cache_valid),
.result_ready_i (s_cache_ready),
.error_o (s_cache_err)
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_cache_start),
.context_i (2'd0),
.quant_id_i (s_cache_quant_id),
.dc_id_i (s_cache_dc_id),
.ac_id_i (s_cache_ac_id),
.start_ready_o (),
.lookup_o (s_table_lookup),
.lookup_context_o (s_table_lookup_context),
.lookup_kind_o (s_table_lookup_kind),
.lookup_index_o (s_table_lookup_index),
.lookup_data_i (s_table_lookup_data),
.lookup_valid_i (s_table_lookup_valid),
.lookup_err_i (s_table_lookup_err),
.entry_kind_o (s_cache_entry_kind),
.entry_index_o (s_cache_entry_index),
.entry_data_o (s_cache_entry_data),
.entry_reciprocal_o(s_cache_entry_reciprocal),
.entry_valid_o (s_cache_entry_valid),
.entry_ready_i (1'b1),
.result_valid_o (s_cache_valid),
.result_ready_i (s_cache_ready),
.error_o (s_cache_err)
);
jpeg_table_register_bank u_luma_table_register_bank (
.clk_i (clk_i),
.write_i (s_cache_entry_valid && (s_state_q == CacheLumaWait)),
.entry_kind_i (s_cache_entry_kind),
.entry_index_i (s_cache_entry_index),
.entry_data_i (s_cache_entry_data),
.entry_reciprocal_i(s_cache_entry_reciprocal),
.quant_o (s_luma_quant_q),
.reciprocal_o (s_luma_reciprocal_q),
.dc_code_o (s_luma_dc_code_q),
.dc_length_o (s_luma_dc_len_q),
.ac_code_o (s_luma_ac_code_q),
.ac_length_o (s_luma_ac_len_q)
);
jpeg_table_register_bank u_chroma_table_register_bank (
.clk_i (clk_i),
.write_i (s_cache_entry_valid && (s_state_q == CacheChromaWait)),
.entry_kind_i (s_cache_entry_kind),
.entry_index_i (s_cache_entry_index),
.entry_data_i (s_cache_entry_data),
.entry_reciprocal_i(s_cache_entry_reciprocal),
.quant_o (s_chroma_quant_q),
.reciprocal_o (s_chroma_reciprocal_q),
.dc_code_o (s_chroma_dc_code_q),
.dc_length_o (s_chroma_dc_len_q),
.ac_code_o (s_chroma_ac_code_q),
.ac_length_o (s_chroma_ac_len_q)
);
jpeg_bit_reader u_bit_reader (
@@ -543,27 +550,37 @@ module jpeg_decode_core (
.error_o (s_bit_err)
);
jpeg_block_decoder u_block_decoder (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_block_start),
.previous_dc_i (s_dc_predictor_q[s_block_component]),
.quant_i (s_selected_quant),
.reciprocal_i (s_selected_reciprocal),
.dc_code_i (s_selected_dc_code),
.dc_length_i (s_selected_dc_len),
.ac_code_i (s_selected_ac_code),
.ac_length_i (s_selected_ac_len),
.bit_window_i (s_bit_window),
.bit_count_i (s_bit_count),
.bit_consume_o (s_bit_consume),
.bit_consume_valid_o(s_bit_consume_valid),
.start_ready_o (),
.block_o (s_decoded_block),
.dc_o (s_decoded_dc),
.result_valid_o (s_block_valid),
.result_ready_i (s_block_ready),
.error_o (s_block_err)
jpeg_block_decoder #(
.ExternalCoefficientEngine(ExternalCoefficientEngine)
) u_block_decoder (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_block_start),
.previous_dc_i (s_dc_predictor_q[s_block_component]),
.quant_i (s_selected_quant),
.reciprocal_i (s_selected_reciprocal),
.dc_code_i (s_selected_dc_code),
.dc_length_i (s_selected_dc_len),
.ac_code_i (s_selected_ac_code),
.ac_length_i (s_selected_ac_len),
.bit_window_i (s_bit_window),
.bit_count_i (s_bit_count),
.coefficient_start_o (coefficient_start_o),
.coefficient_block_o (coefficient_block_o),
.coefficient_start_ready_i (coefficient_start_ready_i),
.coefficient_result_i (coefficient_result_i),
.coefficient_result_valid_i(coefficient_result_valid_i),
.coefficient_result_ready_o(coefficient_result_ready_o),
.coefficient_table_err_i (coefficient_table_err_i),
.coefficient_overflow_i (coefficient_overflow_i),
.bit_consume_o (s_bit_consume),
.bit_consume_valid_o (s_bit_consume_valid),
.start_ready_o (),
.block_o (s_decoded_block),
.dc_o (s_decoded_dc),
.result_valid_o (s_block_valid),
.result_ready_i (s_block_ready),
.error_o (s_block_err)
);
jpeg_mcu_reconstructor u_mcu_reconstructor (
@@ -661,100 +678,4 @@ module jpeg_decode_core (
.dat_i (s_err_code_d),
.dat_o (s_err_code_q)
);
dffr #(
.DATA_WIDTH(512)
) u_luma_quant_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_luma_quant_d),
.dat_o (s_luma_quant_q)
);
dffr #(
.DATA_WIDTH(1600)
) u_luma_reciprocal_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_luma_reciprocal_d),
.dat_o (s_luma_reciprocal_q)
);
dffr #(
.DATA_WIDTH(192)
) u_luma_dc_code_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_luma_dc_code_d),
.dat_o (s_luma_dc_code_q)
);
dffr #(
.DATA_WIDTH(60)
) u_luma_dc_len_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_luma_dc_len_d),
.dat_o (s_luma_dc_len_q)
);
dffr #(
.DATA_WIDTH(4096)
) u_luma_ac_code_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_luma_ac_code_d),
.dat_o (s_luma_ac_code_q)
);
dffr #(
.DATA_WIDTH(1280)
) u_luma_ac_len_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_luma_ac_len_d),
.dat_o (s_luma_ac_len_q)
);
dffr #(
.DATA_WIDTH(512)
) u_chroma_quant_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_chroma_quant_d),
.dat_o (s_chroma_quant_q)
);
dffr #(
.DATA_WIDTH(1600)
) u_chroma_reciprocal_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_chroma_reciprocal_d),
.dat_o (s_chroma_reciprocal_q)
);
dffr #(
.DATA_WIDTH(192)
) u_chroma_dc_code_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_chroma_dc_code_d),
.dat_o (s_chroma_dc_code_q)
);
dffr #(
.DATA_WIDTH(60)
) u_chroma_dc_len_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_chroma_dc_len_d),
.dat_o (s_chroma_dc_len_q)
);
dffr #(
.DATA_WIDTH(4096)
) u_chroma_ac_code_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_chroma_ac_code_d),
.dat_o (s_chroma_ac_code_q)
);
dffr #(
.DATA_WIDTH(1280)
) u_chroma_ac_len_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_chroma_ac_len_d),
.dat_o (s_chroma_ac_len_q)
);
endmodule
+49 -24
View File
@@ -2,7 +2,9 @@
// retroSoC is licensed under Mulan PSL v2.
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND.
module jpeg_encode_core (
module jpeg_encode_core #(
parameter bit ExternalCoefficientEngine = 1'b0
) (
// verilog_format: off -- preserve command, tables, pixel input, and JPEG output columns
input logic clk_i,
input logic rst_n_i,
@@ -24,6 +26,16 @@ module jpeg_encode_core (
input logic [ 59:0] chroma_dc_length_i,
input logic [4095:0] chroma_ac_code_i,
input logic [1279:0] chroma_ac_length_i,
output logic coefficient_start_o,
output logic [1535:0] coefficient_block_o,
output logic [511:0] coefficient_quant_o,
output logic [1599:0] coefficient_reciprocal_o,
input logic coefficient_start_ready_i,
input logic [1535:0] coefficient_result_i,
input logic coefficient_result_valid_i,
output logic coefficient_result_ready_o,
input logic coefficient_table_err_i,
input logic coefficient_overflow_i,
axi4_stream_if.sink pixel_axis,
axi4_stream_if.source bitstream_axis,
output logic start_ready_o,
@@ -178,6 +190,9 @@ module jpeg_encode_core (
end
end
assign coefficient_quant_o = s_selected_quant;
assign coefficient_reciprocal_o = s_selected_reciprocal;
assign s_selected_quant = (s_block_component == 3'd0) ? luma_quant_i : chroma_quant_i;
assign s_selected_reciprocal = (s_block_component == 3'd0) ?
luma_reciprocal_i : chroma_reciprocal_i;
@@ -391,29 +406,39 @@ module jpeg_encode_core (
.error_o (s_builder_err)
);
jpeg_block_encoder u_block_encoder (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_block_encoder_start),
.block_i (s_builder_block),
.previous_dc_i (s_dc_predictor_q[s_block_component]),
.quant_i (s_selected_quant),
.reciprocal_i (s_selected_reciprocal),
.dc_code_i (s_selected_dc_code),
.dc_length_i (s_selected_dc_len),
.ac_code_i (s_selected_ac_code),
.ac_length_i (s_selected_ac_len),
.start_ready_o (),
.token_bits_o (s_token_bits),
.token_length_o(s_token_len),
.token_count_o (s_token_cnt),
.token_valid_o (s_token_valid),
.token_last_o (s_token_last),
.token_ready_i (s_token_ready),
.dc_o (s_block_dc),
.result_valid_o(s_block_result_valid),
.result_ready_i(s_block_result_ready),
.error_o (s_block_err)
jpeg_block_encoder #(
.ExternalCoefficientEngine(ExternalCoefficientEngine)
) u_block_encoder (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_block_encoder_start),
.block_i (s_builder_block),
.previous_dc_i (s_dc_predictor_q[s_block_component]),
.quant_i (s_selected_quant),
.reciprocal_i (s_selected_reciprocal),
.dc_code_i (s_selected_dc_code),
.dc_length_i (s_selected_dc_len),
.ac_code_i (s_selected_ac_code),
.ac_length_i (s_selected_ac_len),
.coefficient_start_o (coefficient_start_o),
.coefficient_block_o (coefficient_block_o),
.coefficient_start_ready_i (coefficient_start_ready_i),
.coefficient_result_i (coefficient_result_i),
.coefficient_result_valid_i(coefficient_result_valid_i),
.coefficient_result_ready_o(coefficient_result_ready_o),
.coefficient_table_err_i (coefficient_table_err_i),
.coefficient_overflow_i (coefficient_overflow_i),
.start_ready_o (),
.token_bits_o (s_token_bits),
.token_length_o (s_token_len),
.token_count_o (s_token_cnt),
.token_valid_o (s_token_valid),
.token_last_o (s_token_last),
.token_ready_i (s_token_ready),
.dc_o (s_block_dc),
.result_valid_o (s_block_result_valid),
.result_ready_i (s_block_result_ready),
.error_o (s_block_err)
);
jpeg_bit_packer u_bit_packer (
+40 -13
View File
@@ -4,7 +4,8 @@
module jpeg_entropy_decoder #(
parameter int unsigned ElementWidth = 24,
parameter int unsigned WindowWidth = 32
parameter int unsigned WindowWidth = 32,
parameter int unsigned SearchLanes = 16
) (
// verilog_format: off -- preserve block, table, and bit-window interface columns
input logic clk_i,
@@ -41,6 +42,8 @@ module jpeg_entropy_decoder #(
logic [ 1:0] s_state_bits_q;
logic [ 6:0] s_coeff_index_d;
logic [ 6:0] s_coeff_index_q;
logic [ 7:0] s_search_base_d;
logic [ 7:0] s_search_base_q;
logic [64*ElementWidth-1:0] s_block_d;
logic [64*ElementWidth-1:0] s_block_q;
logic signed [ ElementWidth-1:0] s_dc_d;
@@ -168,15 +171,17 @@ module jpeg_entropy_decoder #(
end
end
end else if (s_state_q == Ac) begin
for (int unsigned symbol = 0; symbol < 256; symbol++) begin
if (!s_match && (ac_length_i[symbol*5+:5] != 5'd0) &&
(6'(ac_length_i[symbol*5+:5]) <= bit_count_i) &&
(ac_code_i[symbol*16+:16] ==
16'(bit_window_i >> (WindowWidth - int'(ac_length_i[symbol*5+:5]))))) begin
for (int unsigned lane = 0; lane < SearchLanes; lane++) begin
if (!s_match && ((int'(s_search_base_q) + lane) < 256) &&
(ac_length_i[(int'(s_search_base_q)+lane)*5+:5] != 5'd0) &&
(6'(ac_length_i[(int'(s_search_base_q)+lane)*5+:5]) <= bit_count_i) &&
(ac_code_i[(int'(s_search_base_q)+lane)*16+:16] ==
16'(bit_window_i >>
(WindowWidth - int'(ac_length_i[(int'(s_search_base_q)+lane)*5+:5]))))) begin
s_match = 1'b1;
s_symbol = 8'(symbol);
s_code_len = ac_length_i[symbol*5+:5];
s_code = ac_code_i[symbol*16+:16];
s_symbol = s_search_base_q + 8'(lane);
s_code_len = ac_length_i[(int'(s_search_base_q)+lane)*5+:5];
s_code = ac_code_i[(int'(s_search_base_q)+lane)*16+:16];
end
end
end
@@ -185,6 +190,7 @@ module jpeg_entropy_decoder #(
always_comb begin
s_state_d = s_state_q;
s_coeff_index_d = s_coeff_index_q;
s_search_base_d = s_search_base_q;
s_block_d = s_block_q;
s_dc_d = s_dc_q;
s_err_d = s_err_q;
@@ -203,6 +209,7 @@ module jpeg_entropy_decoder #(
Idle: begin
if (start_i) begin
s_coeff_index_d = 7'd1;
s_search_base_d = 8'd0;
s_block_d = '0;
s_dc_d = previous_dc_i;
s_err_d = 1'b0;
@@ -215,6 +222,7 @@ module jpeg_entropy_decoder #(
bit_consume_valid_o = 1'b1;
s_dc_d = s_dc_q + s_value;
s_block_d[0+:ElementWidth] = s_dc_q + s_value;
s_search_base_d = 8'd0;
s_state_d = Ac;
end else if (bit_count_i >= CountWidth'(16)) begin
s_err_d = 1'b1;
@@ -226,6 +234,7 @@ module jpeg_entropy_decoder #(
if (s_symbol == 8'h00) begin
bit_consume_o = CountWidth'(s_code_len);
bit_consume_valid_o = 1'b1;
s_search_base_d = 8'd0;
s_state_d = Result;
end else if (s_symbol == 8'hf0) begin
bit_consume_o = CountWidth'(s_code_len);
@@ -235,6 +244,7 @@ module jpeg_entropy_decoder #(
s_state_d = Result;
end else begin
s_coeff_index_d = s_coeff_index_q + 7'd16;
s_search_base_d = 8'd0;
end
end else if ((s_value_len == 5'd0) || (s_value_len > 5'd10) ||
(s_next_index >= 7'd64)) begin
@@ -248,11 +258,19 @@ module jpeg_entropy_decoder #(
s_state_d = Result;
end else begin
s_coeff_index_d = s_next_index + 1'b1;
s_search_base_d = 8'd0;
end
end
end else if (bit_count_i >= CountWidth'(16)) begin
s_err_d = 1'b1;
s_state_d = Result;
end else if (!s_match) begin
if ((int'(s_search_base_q) + SearchLanes) >= 256) begin
s_search_base_d = 8'd0;
if (bit_count_i >= CountWidth'(16)) begin
s_err_d = 1'b1;
s_state_d = Result;
end
end else begin
s_search_base_d = s_search_base_q + 8'(SearchLanes);
end
end
end
Result: begin
@@ -280,6 +298,14 @@ module jpeg_entropy_decoder #(
.dat_i (s_coeff_index_d),
.dat_o (s_coeff_index_q)
);
dffr #(
.DATA_WIDTH(8)
) u_search_base_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_search_base_d),
.dat_o (s_search_base_q)
);
dffr #(
.DATA_WIDTH(64 * ElementWidth)
) u_block_dffr (
@@ -305,7 +331,8 @@ module jpeg_entropy_decoder #(
`ifndef SYNTHESIS
initial begin
if (ElementWidth < 16 || WindowWidth < 27) begin
if ((ElementWidth < 16) || (WindowWidth < 27) || (SearchLanes < 1) ||
(SearchLanes > 256) || ((256 % SearchLanes) != 0)) begin
$fatal(1, "jpeg_entropy_decoder: invalid coefficient or window width");
end
end
+36 -39
View File
@@ -42,10 +42,8 @@ module jpeg_entropy_encoder #(
logic [ 3:0] s_group_q;
logic [ 3:0] s_run_d;
logic [ 3:0] s_run_q;
logic [ 64*ElementWidth-1:0] s_block_d;
logic [ 64*ElementWidth-1:0] s_block_q;
logic signed [ ElementWidth-1:0] s_previous_dc_d;
logic signed [ ElementWidth-1:0] s_previous_dc_q;
logic [ 5:0] s_last_nonzero_d;
logic [ 5:0] s_last_nonzero_q;
logic signed [ ElementWidth-1:0] s_dc_d;
logic signed [ ElementWidth-1:0] s_dc_q;
logic s_err_d;
@@ -155,19 +153,16 @@ module jpeg_entropy_encoder #(
end
endfunction
function automatic logic has_nonzero_after(input logic [64*ElementWidth-1:0] block_i,
input logic [5:0] scan_index_i);
logic s_found;
function automatic logic [5:0] last_nonzero_index(input logic [64*ElementWidth-1:0] block_i);
logic [5:0] s_last;
begin
s_found = 1'b0;
s_last = 6'd0;
for (int unsigned index = 1; index < 64; index++) begin
if ((index > scan_index_i) && ($signed(
block_i[zigzag_index(6'(index))*ElementWidth+:ElementWidth]
) != 0)) begin
s_found = 1'b1;
if ($signed(block_i[zigzag_index(6'(index))*ElementWidth+:ElementWidth]) != 0) begin
s_last = 6'(index);
end
end
return s_found;
return s_last;
end
endfunction
@@ -178,9 +173,9 @@ module jpeg_entropy_encoder #(
logic [TokenWidth-1:0] s_amplitude;
logic [TokenWidth-1:0] s_mask;
begin
s_code = TokenWidth'(code_i);
s_code = TokenWidth'(code_i);
s_amplitude = TokenWidth'(amplitude_i);
s_mask = (TokenWidth'(1) << category_i) - 1'b1;
s_mask = (TokenWidth'(1) << category_i) - 1'b1;
return (s_code << category_i) | (s_amplitude & s_mask);
end
endfunction
@@ -196,8 +191,7 @@ module jpeg_entropy_encoder #(
s_state_d = s_state_q;
s_group_d = s_group_q;
s_run_d = s_run_q;
s_block_d = s_block_q;
s_previous_dc_d = s_previous_dc_q;
s_last_nonzero_d = s_last_nonzero_q;
s_dc_d = s_dc_q;
s_err_d = s_err_q;
token_bits_o = '0;
@@ -218,15 +212,14 @@ module jpeg_entropy_encoder #(
s_group_d = 4'd0;
s_run_d = 4'd0;
if (start_i) begin
s_block_d = block_i;
s_previous_dc_d = previous_dc_i;
s_dc_d = block_i[0+:ElementWidth];
s_err_d = 1'b0;
s_state_d = Dc;
s_dc_d = block_i[0+:ElementWidth];
s_last_nonzero_d = last_nonzero_index(block_i);
s_err_d = 1'b0;
s_state_d = Dc;
end
end
Dc: begin
s_coefficient_work = s_dc_q - s_previous_dc_q;
s_coefficient_work = s_dc_q - previous_dc_i;
s_category_work = value_category(s_coefficient_work);
if (s_category_work > 5'd11) begin
s_err_d = 1'b1;
@@ -251,11 +244,10 @@ module jpeg_entropy_encoder #(
for (int unsigned lane = 0; lane < 4; lane++) begin
if (((s_group_q * 4) + lane + 1) < 64) begin
s_coefficient_work =
s_block_q[zigzag_index(6'((s_group_q*4)+lane+1))*ElementWidth+:ElementWidth];
block_i[zigzag_index(6'((s_group_q*4)+lane+1))*ElementWidth+:ElementWidth];
if (s_coefficient_work == 0) begin
if ((s_run_work == 4'd15) && has_nonzero_after(
s_block_q, 6'((s_group_q * 4) + lane + 1)
)) begin
if ((s_run_work == 4'd15) &&
(6'((s_group_q * 4) + lane + 1) < s_last_nonzero_q)) begin
s_code_work = ac_code_i[8'hf0*16+:16];
s_code_len_work = ac_length_i[8'hf0*5+:5];
if (s_emit_count < EmitCountWidth'(TokenSlots)) begin
@@ -353,20 +345,12 @@ module jpeg_entropy_encoder #(
.dat_o (s_run_q)
);
dffr #(
.DATA_WIDTH(64 * ElementWidth)
) u_block_dffr (
.DATA_WIDTH(6)
) u_last_nonzero_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_block_d),
.dat_o (s_block_q)
);
dffr #(
.DATA_WIDTH(ElementWidth)
) u_previous_dc_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_previous_dc_d),
.dat_o (s_previous_dc_q)
.dat_i (s_last_nonzero_d),
.dat_o (s_last_nonzero_q)
);
dffr #(
.DATA_WIDTH(ElementWidth)
@@ -390,4 +374,17 @@ module jpeg_entropy_encoder #(
end
end
`endif
`ifndef SV_ASSRT_DISABLE
always_ff @(posedge clk_i) begin
if (rst_n_i && (s_state_q != Idle)) begin
assert ($stable(block_i));
assert ($stable(previous_dc_i));
assert ($stable(dc_code_i));
assert ($stable(dc_length_i));
assert ($stable(ac_code_i));
assert ($stable(ac_length_i));
end
end
`endif
endmodule
+331 -92
View File
@@ -26,52 +26,198 @@ module jpeg_mcu_builder #(
);
localparam int unsigned InputBytes = DataWidth / 8;
localparam int unsigned ByteCountWidth = $clog2(MaxMcuBytes + 1);
localparam int unsigned MemoryWordCount = MaxMcuBytes / 4;
localparam int unsigned MemoryAddressWidth = $clog2(MemoryWordCount);
localparam int unsigned MemoryBankCount = MemoryWordCount / 64;
localparam int unsigned MemoryReplicaCount = 3;
localparam int unsigned MemoryReadPortCount = MemoryReplicaCount * 2;
typedef enum logic [2:0] {
Idle,
Capture,
Fetch,
Generate,
Blocks,
Done
} state_e;
state_e s_state_d;
state_e s_state_q;
logic [ 2:0] s_state_bits_q;
logic [ 1:0] s_sampling_d;
logic [ 1:0] s_sampling_q;
logic [ 2:0] s_format_d;
logic [ 2:0] s_format_q;
logic [ 4:0] s_width_d;
logic [ 4:0] s_width_q;
logic [ 4:0] s_height_d;
logic [ 4:0] s_height_q;
logic [ByteCountWidth-1:0] s_byte_cnt_d;
logic [ByteCountWidth-1:0] s_byte_cnt_q;
logic [ByteCountWidth-1:0] s_expected_bytes_d;
logic [ByteCountWidth-1:0] s_expected_bytes_q;
logic [ 2:0] s_block_cnt_d;
logic [ 2:0] s_block_cnt_q;
logic [ MaxMcuBytes*8-1:0] s_raw_d;
logic [ MaxMcuBytes*8-1:0] s_raw_q;
logic s_err_d;
logic s_err_q;
logic [ 2:0] s_expected_blocks;
logic [ 511:0] s_generated_block;
logic [ ByteCountWidth:0] s_bytes_after_beat;
logic [ 3:0] s_input_byte_cnt;
logic s_keep_legal;
state_e s_state_d;
state_e s_state_q;
logic [ 2:0] s_state_bits_q;
logic [ 1:0] s_sampling_d;
logic [ 1:0] s_sampling_q;
logic [ 2:0] s_format_d;
logic [ 2:0] s_format_q;
logic [ 4:0] s_width_d;
logic [ 4:0] s_width_q;
logic [ 4:0] s_height_d;
logic [ 4:0] s_height_q;
logic [ ByteCountWidth-1:0] s_byte_cnt_d;
logic [ ByteCountWidth-1:0] s_byte_cnt_q;
logic [ ByteCountWidth-1:0] s_expected_bytes_d;
logic [ ByteCountWidth-1:0] s_expected_bytes_q;
logic [ 2:0] s_block_cnt_d;
logic [ 2:0] s_block_cnt_q;
logic [ 5:0] s_sample_cnt_d;
logic [ 5:0] s_sample_cnt_q;
logic [ 511:0] s_block_d;
logic [ 511:0] s_block_q;
logic s_err_d;
logic s_err_q;
logic [ 2:0] s_expected_blocks;
logic [ 7:0] s_generated_sample;
logic [ ByteCountWidth:0] s_bytes_after_beat;
logic [ 3:0] s_input_byte_cnt;
logic s_keep_legal;
logic s_memory_write;
logic s_memory_read;
logic [ MemoryAddressWidth-1:0] s_write_word_a;
logic [ MemoryAddressWidth-1:0] s_write_word_b;
logic [MemoryReadPortCount-1:0][MemoryAddressWidth-1:0] s_read_word;
logic [MemoryReadPortCount-1:0][MemoryAddressWidth-1:0] s_read_word_q;
logic [MemoryReadPortCount-1:0][ 31:0] s_read_data;
logic [ MemoryReplicaCount-1:0][ MemoryBankCount-1:0][31:0] s_memory_a_data;
logic [ MemoryReplicaCount-1:0][ MemoryBankCount-1:0][31:0] s_memory_b_data;
logic [ 5:0] s_fetch_sample;
function automatic logic [7:0] raw_byte(input logic [MaxMcuBytes*8-1:0] raw_i,
input integer index_i);
if ((index_i < 0) || (index_i >= MaxMcuBytes)) begin
return 8'd0;
function automatic logic [MemoryReadPortCount*MemoryAddressWidth-1:0] raw_word_requests(
input logic [1:0] sampling_i, input logic [2:0] format_i, input integer width_i,
input integer height_i, input integer block_i, input integer sample_i);
integer s_x;
integer s_y;
integer s_x_end;
integer s_y_end;
integer s_x_clamped;
integer s_y_clamped;
integer s_x_end_clamped;
integer s_y_end_clamped;
integer s_row0_start;
integer s_row0_end;
integer s_row1_start;
integer s_row1_end;
integer s_uv_stride;
integer s_uv_base;
integer s_uv_start;
integer s_uv_end;
logic [MemoryReadPortCount*MemoryAddressWidth-1:0] s_requests;
begin
s_x = sample_i & 7;
s_y = sample_i >> 3;
s_x_end = s_x;
s_y_end = s_y;
if (sampling_i == 2'd2) begin
if (block_i < 2) begin
s_x += block_i * 8;
s_x_end = s_x;
end else begin
s_x *= 2;
s_x_end = s_x + 1;
end
end else if (sampling_i == 2'd3) begin
if (block_i < 4) begin
s_x += (block_i & 1) * 8;
s_y += (block_i >> 1) * 8;
s_x_end = s_x;
s_y_end = s_y;
end else begin
s_x *= 2;
s_y *= 2;
s_x_end = s_x + 1;
s_y_end = s_y + 1;
end
end
s_x_clamped = (s_x >= width_i) ? width_i - 1 : s_x;
s_y_clamped = (s_y >= height_i) ? height_i - 1 : s_y;
s_x_end_clamped = (s_x_end >= width_i) ? width_i - 1 : s_x_end;
s_y_end_clamped = (s_y_end >= height_i) ? height_i - 1 : s_y_end;
unique case (format_i)
3'd0: begin
s_row0_start = (s_y_clamped * width_i) + s_x_clamped;
s_row0_end = (s_y_clamped * width_i) + s_x_end_clamped;
s_row1_start = (s_y_end_clamped * width_i) + s_x_clamped;
s_row1_end = (s_y_end_clamped * width_i) + s_x_end_clamped;
end
3'd1: begin
s_row0_start = ((s_y_clamped * width_i) + s_x_clamped) * 2;
s_row0_end = (((s_y_clamped * width_i) + s_x_end_clamped) * 2) + 1;
s_row1_start = ((s_y_end_clamped * width_i) + s_x_clamped) * 2;
s_row1_end = (((s_y_end_clamped * width_i) + s_x_end_clamped) * 2) + 1;
end
3'd2: begin
s_row0_start = ((s_y_clamped * width_i) + s_x_clamped) * 3;
s_row0_end = (((s_y_clamped * width_i) + s_x_end_clamped) * 3) + 2;
s_row1_start = ((s_y_end_clamped * width_i) + s_x_clamped) * 3;
s_row1_end = (((s_y_end_clamped * width_i) + s_x_end_clamped) * 3) + 2;
end
3'd3: begin
s_row0_start = ((s_y_clamped * width_i) + (s_x_clamped & ~1)) * 2;
s_row0_end = (((s_y_clamped * width_i) + (s_x_end_clamped & ~1)) * 2) + 3;
s_row1_start = ((s_y_end_clamped * width_i) + (s_x_clamped & ~1)) * 2;
s_row1_end = (((s_y_end_clamped * width_i) + (s_x_end_clamped & ~1)) * 2) + 3;
end
default: begin
s_row0_start = (s_y_clamped * width_i) + s_x_clamped;
s_row0_end = (s_y_clamped * width_i) + s_x_end_clamped;
s_row1_start = (s_y_end_clamped * width_i) + s_x_clamped;
s_row1_end = (s_y_end_clamped * width_i) + s_x_end_clamped;
end
endcase
s_requests = '0;
if (format_i == 3'd4) begin
s_uv_stride = (width_i + 1) & ~1;
s_uv_base = width_i * height_i;
s_uv_start = s_uv_base + ((s_y_clamped >> 1) * s_uv_stride) + ((s_x_clamped >> 1) * 2);
s_uv_end = s_uv_base + ((s_y_end_clamped >> 1) * s_uv_stride) +
((s_x_end_clamped >> 1) * 2) + 1;
s_requests[0*MemoryAddressWidth+:MemoryAddressWidth] =
MemoryAddressWidth'(s_row0_start >> 2);
s_requests[1*MemoryAddressWidth+:MemoryAddressWidth] = MemoryAddressWidth'(s_row0_end >> 2);
s_requests[2*MemoryAddressWidth+:MemoryAddressWidth] =
MemoryAddressWidth'(s_row1_start >> 2);
s_requests[3*MemoryAddressWidth+:MemoryAddressWidth] = MemoryAddressWidth'(s_row1_end >> 2);
s_requests[4*MemoryAddressWidth+:MemoryAddressWidth] = MemoryAddressWidth'(s_uv_start >> 2);
s_requests[5*MemoryAddressWidth+:MemoryAddressWidth] = MemoryAddressWidth'(s_uv_end >> 2);
end else begin
s_requests[0*MemoryAddressWidth+:MemoryAddressWidth] =
MemoryAddressWidth'(s_row0_start >> 2);
s_requests[1*MemoryAddressWidth+:MemoryAddressWidth] =
MemoryAddressWidth'((s_row0_start >> 2) + 1);
s_requests[2*MemoryAddressWidth+:MemoryAddressWidth] = MemoryAddressWidth'(s_row0_end >> 2);
s_requests[3*MemoryAddressWidth+:MemoryAddressWidth] =
MemoryAddressWidth'(s_row1_start >> 2);
s_requests[4*MemoryAddressWidth+:MemoryAddressWidth] =
MemoryAddressWidth'((s_row1_start >> 2) + 1);
s_requests[5*MemoryAddressWidth+:MemoryAddressWidth] = MemoryAddressWidth'(s_row1_end >> 2);
end
return s_requests;
end
endfunction
function automatic logic [7:0] raw_byte(
input integer index_i,
input logic [MemoryReadPortCount-1:0][MemoryAddressWidth-1:0] read_word_i,
input logic [MemoryReadPortCount-1:0][31:0] read_data_i);
logic [MemoryAddressWidth-1:0] s_word;
logic [ 7:0] s_byte;
begin
if ((index_i < 0) || (index_i >= MaxMcuBytes)) begin
return 8'd0;
end
s_word = MemoryAddressWidth'(index_i >> 2);
s_byte = 8'd0;
for (int unsigned port_index = 0; port_index < MemoryReadPortCount; port_index++) begin
if (read_word_i[port_index] == s_word) begin
s_byte = read_data_i[port_index][(index_i&3)*8+:8];
end
end
return s_byte;
end
return raw_i[index_i*8+:8];
endfunction
function automatic logic [23:0] pixel_ycbcr(
input logic [MaxMcuBytes*8-1:0] raw_i, input logic [2:0] format_i, input integer width_i,
input integer height_i, input integer x_i, input integer y_i);
input logic [2:0] format_i, input integer width_i, input integer height_i, input integer x_i,
input integer y_i, input logic [MemoryReadPortCount-1:0][MemoryAddressWidth-1:0] read_word_i,
input logic [MemoryReadPortCount-1:0][31:0] read_data_i);
integer s_x;
integer s_y;
integer s_index;
@@ -96,40 +242,43 @@ module jpeg_mcu_builder #(
s_cr = 8'd128;
unique case (format_i)
3'd0: begin
s_luma = raw_byte(raw_i, (s_y * width_i) + s_x);
s_luma = raw_byte((s_y * width_i) + s_x, read_word_i, read_data_i);
end
3'd1: begin
s_index = ((s_y * width_i) + s_x) * 2;
s_rgb565 = {raw_byte(raw_i, s_index + 1), raw_byte(raw_i, s_index)};
s_red = {s_rgb565[15:11], s_rgb565[15:13]};
s_green = {s_rgb565[10:5], s_rgb565[10:9]};
s_blue = {s_rgb565[4:0], s_rgb565[4:2]};
s_luma = (77 * s_red + 150 * s_green + 29 * s_blue + 128) >> 8;
s_cb = ((-43 * s_red - 85 * s_green + 128 * s_blue + 128) >>> 8) + 128;
s_cr = ((128 * s_red - 107 * s_green - 21 * s_blue + 128) >>> 8) + 128;
s_index = ((s_y * width_i) + s_x) * 2;
s_rgb565 = {
raw_byte(s_index + 1, read_word_i, read_data_i),
raw_byte(s_index, read_word_i, read_data_i)
};
s_red = {s_rgb565[15:11], s_rgb565[15:13]};
s_green = {s_rgb565[10:5], s_rgb565[10:9]};
s_blue = {s_rgb565[4:0], s_rgb565[4:2]};
s_luma = (77 * s_red + 150 * s_green + 29 * s_blue + 128) >> 8;
s_cb = ((-43 * s_red - 85 * s_green + 128 * s_blue + 128) >>> 8) + 128;
s_cr = ((128 * s_red - 107 * s_green - 21 * s_blue + 128) >>> 8) + 128;
end
3'd2: begin
s_index = ((s_y * width_i) + s_x) * 3;
s_red = raw_byte(raw_i, s_index);
s_green = raw_byte(raw_i, s_index + 1);
s_blue = raw_byte(raw_i, s_index + 2);
s_red = raw_byte(s_index, read_word_i, read_data_i);
s_green = raw_byte(s_index + 1, read_word_i, read_data_i);
s_blue = raw_byte(s_index + 2, read_word_i, read_data_i);
s_luma = (77 * s_red + 150 * s_green + 29 * s_blue + 128) >> 8;
s_cb = ((-43 * s_red - 85 * s_green + 128 * s_blue + 128) >>> 8) + 128;
s_cr = ((128 * s_red - 107 * s_green - 21 * s_blue + 128) >>> 8) + 128;
end
3'd3: begin
s_pair = ((s_y * width_i) + (s_x & ~1)) * 2;
s_luma = raw_byte(raw_i, s_pair + ((s_x & 1) * 2));
s_cb = raw_byte(raw_i, s_pair + 1);
s_cr = raw_byte(raw_i, s_pair + 3);
s_luma = raw_byte(s_pair + ((s_x & 1) * 2), read_word_i, read_data_i);
s_cb = raw_byte(s_pair + 1, read_word_i, read_data_i);
s_cr = raw_byte(s_pair + 3, read_word_i, read_data_i);
end
default: begin
s_luma = raw_byte(raw_i, (s_y * width_i) + s_x);
s_luma = raw_byte((s_y * width_i) + s_x, read_word_i, read_data_i);
s_uv_stride = (width_i + 1) & ~1;
s_uv_base = width_i * height_i;
s_pair = s_uv_base + ((s_y >> 1) * s_uv_stride) + ((s_x >> 1) * 2);
s_cb = raw_byte(raw_i, s_pair);
s_cr = raw_byte(raw_i, s_pair + 1);
s_cb = raw_byte(s_pair, read_word_i, read_data_i);
s_cr = raw_byte(s_pair + 1, read_word_i, read_data_i);
end
endcase
return {s_cr, s_cb, s_luma};
@@ -137,9 +286,10 @@ module jpeg_mcu_builder #(
endfunction
function automatic logic [7:0] component_sample(
input logic [MaxMcuBytes*8-1:0] raw_i, input logic [1:0] sampling_i,
input logic [2:0] format_i, input integer width_i, input integer height_i,
input integer block_i, input integer sample_i);
input logic [1:0] sampling_i, input logic [2:0] format_i, input integer width_i,
input integer height_i, input integer block_i, input integer sample_i,
input logic [MemoryReadPortCount-1:0][MemoryAddressWidth-1:0] read_word_i,
input logic [MemoryReadPortCount-1:0][31:0] read_data_i);
integer s_x;
integer s_y;
integer s_component;
@@ -171,17 +321,18 @@ module jpeg_mcu_builder #(
s_y *= 2;
s_component = block_i - 3;
end
s_pixel0 = pixel_ycbcr(raw_i, format_i, width_i, height_i, s_x, s_y);
s_pixel0 = pixel_ycbcr(format_i, width_i, height_i, s_x, s_y, read_word_i, read_data_i);
if ((sampling_i == 2'd2) && (block_i >= 2)) begin
s_pixel1 = pixel_ycbcr(raw_i, format_i, width_i, height_i, s_x + 1, s_y);
s_pixel1 = pixel_ycbcr(format_i, width_i, height_i, s_x + 1, s_y, read_word_i, read_data_i);
s_sum = 10'((s_component == 1) ? s_pixel0[15:8] : s_pixel0[23:16]) +
10'((s_component == 1) ? s_pixel1[15:8] : s_pixel1[23:16]);
return 8'((s_sum + 1'b1) >> 1);
end
if ((sampling_i == 2'd3) && (block_i >= 4)) begin
s_pixel1 = pixel_ycbcr(raw_i, format_i, width_i, height_i, s_x + 1, s_y);
s_pixel2 = pixel_ycbcr(raw_i, format_i, width_i, height_i, s_x, s_y + 1);
s_pixel3 = pixel_ycbcr(raw_i, format_i, width_i, height_i, s_x + 1, s_y + 1);
s_pixel1 = pixel_ycbcr(format_i, width_i, height_i, s_x + 1, s_y, read_word_i, read_data_i);
s_pixel2 = pixel_ycbcr(format_i, width_i, height_i, s_x, s_y + 1, read_word_i, read_data_i);
s_pixel3 =
pixel_ycbcr(format_i, width_i, height_i, s_x + 1, s_y + 1, read_word_i, read_data_i);
s_sum = 10'((s_component == 1) ? s_pixel0[15:8] : s_pixel0[23:16]) +
10'((s_component == 1) ? s_pixel1[15:8] : s_pixel1[23:16]) +
10'((s_component == 1) ? s_pixel2[15:8] : s_pixel2[23:16]) +
@@ -196,6 +347,68 @@ module jpeg_mcu_builder #(
end
endfunction
assign s_memory_write = (s_state_q == Capture) && pixel_axis.tvalid && pixel_axis.tready;
assign s_memory_read = (s_state_q == Fetch) ||
((s_state_q == Generate) && (s_sample_cnt_q != 6'd63));
assign s_write_word_a = MemoryAddressWidth'(s_byte_cnt_q >> 2);
assign s_write_word_b = s_write_word_a + 1'b1;
assign s_fetch_sample = (s_state_q == Fetch) ? 6'd0 : s_sample_cnt_q + 1'b1;
assign s_read_word = raw_word_requests(
s_sampling_q,
s_format_q,
int'(s_width_q),
int'(s_height_q),
int'(s_block_cnt_q),
int'(s_fetch_sample)
);
always_comb begin
s_read_data = '0;
for (int unsigned replica = 0; replica < MemoryReplicaCount; replica++) begin
for (int unsigned bank = 0; bank < MemoryBankCount; bank++) begin
if (s_read_word_q[replica*2][MemoryAddressWidth-1:6] ==
(MemoryAddressWidth - 6)'(bank)) begin
s_read_data[replica*2] = s_memory_a_data[replica][bank];
end
if (s_read_word_q[(replica*2)+1][MemoryAddressWidth-1:6] ==
(MemoryAddressWidth - 6)'(bank)) begin
s_read_data[(replica*2)+1] = s_memory_b_data[replica][bank];
end
end
end
end
for (genvar replica = 0; replica < MemoryReplicaCount; replica++) begin : gen_memory_replica
for (genvar bank = 0; bank < MemoryBankCount; bank++) begin : gen_memory_bank
localparam logic [MemoryAddressWidth-7:0] BankIndex = (MemoryAddressWidth - 6)'(bank);
logic s_a_cs;
logic s_b_cs;
assign s_a_cs = (s_memory_write &&
(s_write_word_a[MemoryAddressWidth-1:6] == BankIndex)) ||
(s_memory_read &&
(s_read_word[replica*2][MemoryAddressWidth-1:6] == BankIndex));
assign s_b_cs = (s_memory_write &&
(s_write_word_b[MemoryAddressWidth-1:6] == BankIndex)) ||
(s_memory_read &&
(s_read_word[(replica*2)+1][MemoryAddressWidth-1:6] == BankIndex));
tc_sram_64x32_2p u_raw_sram (
.clk_i (clk_i),
.a_cs_i (s_a_cs),
.a_addr_i(s_memory_write ? s_write_word_a[5:0] : s_read_word[replica*2][5:0]),
.a_data_i(pixel_axis.tdata[31:0]),
.a_wren_i(s_memory_write),
.a_data_o(s_memory_a_data[replica][bank]),
.b_cs_i (s_b_cs),
.b_addr_i(s_memory_write ? s_write_word_b[5:0] : s_read_word[(replica*2)+1][5:0]),
.b_data_i(pixel_axis.tdata[63:32]),
.b_wren_i(s_memory_write),
.b_data_o(s_memory_b_data[replica][bank])
);
end
end
always_comb begin
unique case (s_sampling_q)
2'd0: s_expected_blocks = 3'd1;
@@ -203,19 +416,19 @@ module jpeg_mcu_builder #(
2'd2: s_expected_blocks = 3'd4;
default: s_expected_blocks = 3'd6;
endcase
for (int unsigned index = 0; index < 64; index++) begin
s_generated_block[index*8+:8] = component_sample(
s_raw_q,
s_sampling_q,
s_format_q,
int'(s_width_q),
int'(s_height_q),
int'(s_block_cnt_q),
index
);
end
end
assign s_generated_sample = component_sample(
s_sampling_q,
s_format_q,
int'(s_width_q),
int'(s_height_q),
int'(s_block_cnt_q),
int'(s_sample_cnt_q),
s_read_word_q,
s_read_data
);
always_comb begin
s_input_byte_cnt = 4'd0;
s_keep_legal = (pixel_axis.tkeep != '0) && (pixel_axis.tkeep == pixel_axis.tstrb);
@@ -232,7 +445,7 @@ module jpeg_mcu_builder #(
assign s_state_q = state_e'(s_state_bits_q);
assign pixel_axis.tready = s_state_q == Capture;
assign start_ready_o = s_state_q == Idle;
assign block_o = s_generated_block;
assign block_o = s_block_q;
assign block_valid_o = s_state_q == Blocks;
assign block_last_o = (s_state_q == Blocks) && (s_block_cnt_q + 1'b1 == s_expected_blocks);
assign done_o = s_state_q == Done;
@@ -247,18 +460,20 @@ module jpeg_mcu_builder #(
s_byte_cnt_d = s_byte_cnt_q;
s_expected_bytes_d = s_expected_bytes_q;
s_block_cnt_d = s_block_cnt_q;
s_raw_d = s_raw_q;
s_sample_cnt_d = s_sample_cnt_q;
s_block_d = s_block_q;
s_err_d = s_err_q;
unique case (s_state_q)
Idle: begin
if (start_i) begin
s_sampling_d = sampling_i;
s_format_d = format_i;
s_width_d = valid_width_i;
s_height_d = valid_height_i;
s_byte_cnt_d = '0;
s_block_cnt_d = '0;
s_raw_d = '0;
s_sampling_d = sampling_i;
s_format_d = format_i;
s_width_d = valid_width_i;
s_height_d = valid_height_i;
s_byte_cnt_d = '0;
s_block_cnt_d = '0;
s_sample_cnt_d = 6'd63;
s_block_d = '0;
unique case (format_i)
3'd0: s_expected_bytes_d = ByteCountWidth'(int'(valid_width_i) * int'(valid_height_i));
3'd1, 3'd3:
@@ -282,24 +497,33 @@ module jpeg_mcu_builder #(
if (!s_keep_legal || (s_bytes_after_beat > {1'b0, s_expected_bytes_q})) begin
s_err_d = 1'b1;
end
for (int unsigned lane = 0; lane < InputBytes; lane++) begin
if (pixel_axis.tkeep[lane] && ((int'(s_byte_cnt_q) + lane) < MaxMcuBytes)) begin
s_raw_d[(int'(s_byte_cnt_q)+lane)*8+:8] = pixel_axis.tdata[lane*8+:8];
end
end
s_byte_cnt_d = ByteCountWidth'(s_bytes_after_beat);
if (s_bytes_after_beat >= {1'b0, s_expected_bytes_q}) begin
s_block_cnt_d = 3'd0;
s_state_d = Blocks;
s_block_cnt_d = 3'd0;
s_sample_cnt_d = 6'd0;
s_block_d = '0;
s_state_d = Fetch;
end
end
end
Fetch: s_state_d = Generate;
Generate: begin
s_block_d[s_sample_cnt_q*8+:8] = s_generated_sample;
if (s_sample_cnt_q == 6'd63) begin
s_state_d = Blocks;
end else begin
s_sample_cnt_d = s_sample_cnt_q + 1'b1;
end
end
Blocks: begin
if (block_valid_o && block_ready_i) begin
if (block_last_o) begin
s_state_d = Done;
end else begin
s_block_cnt_d = s_block_cnt_q + 1'b1;
s_block_cnt_d = s_block_cnt_q + 1'b1;
s_sample_cnt_d = 6'd0;
s_block_d = '0;
s_state_d = Fetch;
end
end
end
@@ -373,12 +597,27 @@ module jpeg_mcu_builder #(
.dat_o (s_block_cnt_q)
);
dffr #(
.DATA_WIDTH(MaxMcuBytes * 8)
) u_raw_dffr (
.DATA_WIDTH(6)
) u_sample_cnt_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_raw_d),
.dat_o (s_raw_q)
.dat_i (s_sample_cnt_d),
.dat_o (s_sample_cnt_q)
);
dffr #(
.DATA_WIDTH(512)
) u_block_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_block_d),
.dat_o (s_block_q)
);
dff #(
.DATA_WIDTH(MemoryReadPortCount * MemoryAddressWidth)
) u_read_word_dff (
.clk_i(clk_i),
.dat_i(s_read_word),
.dat_o(s_read_word_q)
);
dffr u_err_dffr (
.clk_i (clk_i),
@@ -389,7 +628,7 @@ module jpeg_mcu_builder #(
`ifndef SYNTHESIS
initial begin
if (DataWidth != 64 || MaxMcuBytes < 768) begin
if ((DataWidth != 64) || (MaxMcuBytes < 768) || ((MaxMcuBytes % 256) != 0)) begin
$fatal(1, "jpeg_mcu_builder: unsupported buffer geometry");
end
end
+26 -24
View File
@@ -41,7 +41,6 @@ module jpeg_mcu_reconstructor (
logic [ 4:0] s_height_q;
logic [ 2:0] s_block_cnt_d;
logic [ 2:0] s_block_cnt_q;
logic [ 5:0][511:0] s_blocks_d;
logic [ 5:0][511:0] s_blocks_q;
logic [ 4:0] s_x_d;
logic [ 4:0] s_x_q;
@@ -67,6 +66,7 @@ module jpeg_mcu_reconstructor (
logic s_second_valid;
logic s_pixel_accept;
logic [ 4:0] s_x_next;
logic [ 5:0] s_block_write_en;
function automatic logic [7:0] block_sample(input logic [511:0] block_i, input logic [2:0] x_i,
input logic [2:0] y_i);
@@ -94,8 +94,7 @@ module jpeg_mcu_reconstructor (
s_cb = $signed({1'b0, cb_i}) - 10'sd128;
s_cr = $signed({1'b0, cr_i}) - 10'sd128;
s_red = $signed(18'({1'b0, y_i})) + ((18'sd359 * s_cr + 18'sd128) >>> 8);
s_green = $signed(18'({1'b0, y_i})) -
((18'sd88 * s_cb + 18'sd183 * s_cr + 18'sd128) >>> 8);
s_green = $signed(18'({1'b0, y_i})) - ((18'sd88 * s_cb + 18'sd183 * s_cr + 18'sd128) >>> 8);
s_blue = $signed(18'({1'b0, y_i})) + ((18'sd454 * s_cb + 18'sd128) >>> 8);
return {clamp_color(s_red), clamp_color(s_green), clamp_color(s_blue)};
end
@@ -160,16 +159,30 @@ module jpeg_mcu_reconstructor (
assign {s_red0, s_green0, s_blue0} = ycbcr_to_rgb(s_y0, s_cb0, s_cr0);
assign {s_red1, s_green1, s_blue1} = ycbcr_to_rgb(s_y1, s_cb1, s_cr1);
assign s_rgb565_0 = {s_red0[7:3], s_green0[7:2], s_blue0[7:3]};
assign s_rgb565_1 = {s_red1[7:3], s_green1[7:2], s_blue1[7:3]};
assign s_x_next = s_x_q + 1'b1;
assign s_second_valid = (s_x_q + 1'b1) < s_width_q;
assign s_pixel_accept = pixel_axis.tvalid && pixel_axis.tready;
assign s_state_q = state_e'(s_state_bits_q);
assign start_ready_o = s_state_q == Idle;
assign block_ready_o = s_state_q == Load;
assign done_o = s_state_q == Done;
assign error_o = s_err_q;
assign s_rgb565_0 = {s_red0[7:3], s_green0[7:2], s_blue0[7:3]};
assign s_rgb565_1 = {s_red1[7:3], s_green1[7:2], s_blue1[7:3]};
assign s_x_next = s_x_q + 1'b1;
assign s_second_valid = (s_x_q + 1'b1) < s_width_q;
assign s_pixel_accept = pixel_axis.tvalid && pixel_axis.tready;
assign s_state_q = state_e'(s_state_bits_q);
assign start_ready_o = s_state_q == Idle;
assign block_ready_o = s_state_q == Load;
assign done_o = s_state_q == Done;
assign error_o = s_err_q;
assign s_block_write_en = ((s_state_q == Load) && block_valid_i) ?
(6'b000001 << s_block_cnt_q) : 6'b000000;
// Every required block is loaded before Pixels, so the block bank needs no reset.
for (genvar block_index = 0; block_index < 6; block_index++) begin : gen_block_register
dffl #(
.DATA_WIDTH(512)
) u_block_dffl (
.clk_i(clk_i),
.en_i (s_block_write_en[block_index]),
.dat_i(block_i),
.dat_o(s_blocks_q[block_index])
);
end
always_comb begin
pixel_axis.tdata = '0;
@@ -230,7 +243,6 @@ module jpeg_mcu_reconstructor (
s_width_d = s_width_q;
s_height_d = s_height_q;
s_block_cnt_d = s_block_cnt_q;
s_blocks_d = s_blocks_q;
s_x_d = s_x_q;
s_y_d = s_y_q;
s_err_d = s_err_q;
@@ -242,7 +254,6 @@ module jpeg_mcu_reconstructor (
s_width_d = valid_width_i;
s_height_d = valid_height_i;
s_block_cnt_d = 3'd0;
s_blocks_d = '0;
s_x_d = 5'd0;
s_y_d = 5'd0;
s_err_d = (format_i > 3'd4) || (valid_width_i == 5'd0) ||
@@ -253,7 +264,6 @@ module jpeg_mcu_reconstructor (
end
Load: begin
if (block_valid_i) begin
s_blocks_d[s_block_cnt_q] = block_i;
if (s_block_cnt_q + 1'b1 == s_expected_blocks) begin
s_x_d = 5'd0;
s_y_d = 5'd0;
@@ -347,14 +357,6 @@ module jpeg_mcu_reconstructor (
.dat_i (s_block_cnt_d),
.dat_o (s_block_cnt_q)
);
dffr #(
.DATA_WIDTH(6 * 512)
) u_blocks_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_blocks_d),
.dat_o (s_blocks_q)
);
dffr #(
.DATA_WIDTH(5)
) u_x_dffr (
+94 -100
View File
@@ -5,7 +5,8 @@
module jpeg_quantizer #(
parameter int unsigned ElementWidth = 24,
parameter int unsigned ReciprocalWidth = 25,
parameter int unsigned ReciprocalShift = 24
parameter int unsigned ReciprocalShift = 24,
parameter int unsigned LaneCount = 4
) (
// verilog_format: off -- preserve the block and table interface columns
input logic clk_i,
@@ -29,25 +30,21 @@ module jpeg_quantizer #(
Result
} state_e;
state_e s_state_d;
state_e s_state_q;
logic [ 1:0] s_state_bits_q;
logic [ 2:0] s_group_d;
logic [ 2:0] s_group_q;
logic s_dequantize_d;
logic s_dequantize_q;
logic [ 64*ElementWidth-1:0] s_input_d;
logic [ 64*ElementWidth-1:0] s_input_q;
logic [ 64*8-1:0] s_quant_d;
logic [ 64*8-1:0] s_quant_q;
logic [64*ReciprocalWidth-1:0] s_reciprocal_d;
logic [64*ReciprocalWidth-1:0] s_reciprocal_q;
logic [ 64*ElementWidth-1:0] s_result_d;
logic [ 64*ElementWidth-1:0] s_result_q;
logic s_table_err_d;
logic s_table_err_q;
logic s_overflow_d;
logic s_overflow_q;
state_e s_state_d;
state_e s_state_q;
logic [ 1:0] s_state_bits_q;
logic [ 5:0] s_index_d;
logic [ 5:0] s_index_q;
logic [ 5:0] s_process_index;
logic s_dequantize_d;
logic s_dequantize_q;
logic [64*ElementWidth-1:0] s_result_q;
logic [64*ElementWidth-1:0] s_result_write_data;
logic [ 63:0] s_result_write_en;
logic s_table_err_d;
logic s_table_err_q;
logic s_overflow_d;
logic s_overflow_q;
function automatic logic signed [ElementWidth-1:0] quantize_value(
input logic signed [ElementWidth-1:0] value_i, input logic [7:0] quant_i,
@@ -104,58 +101,33 @@ module jpeg_quantizer #(
assign overflow_o = s_overflow_q;
always_comb begin
s_state_d = s_state_q;
s_group_d = s_group_q;
s_dequantize_d = s_dequantize_q;
s_input_d = s_input_q;
s_quant_d = s_quant_q;
s_reciprocal_d = s_reciprocal_q;
s_result_d = s_result_q;
s_table_err_d = s_table_err_q;
s_overflow_d = s_overflow_q;
s_state_d = s_state_q;
s_index_d = s_index_q;
s_dequantize_d = s_dequantize_q;
s_table_err_d = s_table_err_q;
s_overflow_d = s_overflow_q;
s_process_index = s_index_q;
s_result_write_data = '0;
s_result_write_en = '0;
unique case (s_state_q)
Idle: begin
s_group_d = 3'd0;
s_index_d = 6'd0;
if (start_i) begin
s_dequantize_d = dequantize_i;
s_input_d = block_i;
s_quant_d = quant_i;
s_reciprocal_d = reciprocal_i;
s_result_d = '0;
s_table_err_d = 1'b0;
s_overflow_d = 1'b0;
s_state_d = Process;
s_index_d = 6'(LaneCount);
s_process_index = 6'd0;
s_dequantize_d = dequantize_i;
s_table_err_d = 1'b0;
s_overflow_d = 1'b0;
s_state_d = Process;
end
end
Process: begin
for (int unsigned lane = 0; lane < 8; lane++) begin
if (s_quant_q[((s_group_q*8)+lane)*8+:8] == 8'd0) begin
s_table_err_d = 1'b1;
end else if (s_dequantize_q) begin
s_result_d[((s_group_q*8)+lane)*ElementWidth+:ElementWidth] = dequantize_value(
s_input_q[((s_group_q*8)+lane)*ElementWidth+:ElementWidth],
s_quant_q[((s_group_q*8)+lane)*8+:8]
);
if (product_overflows(
s_input_q[((s_group_q*8)+lane)*ElementWidth+:ElementWidth],
s_quant_q[((s_group_q*8)+lane)*8+:8]
)) begin
s_overflow_d = 1'b1;
end
end else begin
s_result_d[((s_group_q*8)+lane)*ElementWidth+:ElementWidth] = quantize_value(
s_input_q[((s_group_q*8)+lane)*ElementWidth+:ElementWidth],
s_quant_q[((s_group_q*8)+lane)*8+:8],
s_reciprocal_q[((s_group_q*8)+lane)*ReciprocalWidth+:ReciprocalWidth]
);
end
end
if (s_group_q == 3'd7) begin
s_group_d = 3'd0;
if ((int'(s_index_q) + LaneCount) >= 64) begin
s_index_d = 6'd0;
s_state_d = Result;
end else begin
s_group_d = s_group_q + 1'b1;
s_index_d = s_index_q + 6'(LaneCount);
end
end
Result: begin
@@ -167,6 +139,49 @@ module jpeg_quantizer #(
s_state_d = Idle;
end
endcase
if ((s_state_q == Process) || ((s_state_q == Idle) && start_i)) begin
for (int unsigned lane = 0; lane < LaneCount; lane++) begin
if ((int'(s_process_index) + lane) < 64) begin
if (quant_i[(int'(s_process_index)+lane)*8+:8] == 8'd0) begin
s_table_err_d = 1'b1;
end else if ((s_state_q == Idle) ? dequantize_i : s_dequantize_q) begin
s_result_write_data[(int'(s_process_index)+lane)*ElementWidth+:ElementWidth] =
dequantize_value(
block_i[(int'(s_process_index)+lane)*ElementWidth+:ElementWidth],
quant_i[(int'(s_process_index)+lane)*8+:8]
);
s_result_write_en[int'(s_process_index)+lane] = 1'b1;
if (product_overflows(
block_i[(int'(s_process_index)+lane)*ElementWidth+:ElementWidth],
quant_i[(int'(s_process_index)+lane)*8+:8]
)) begin
s_overflow_d = 1'b1;
end
end else begin
s_result_write_data[(int'(s_process_index)+lane)*ElementWidth+:ElementWidth] =
quantize_value(
block_i[(int'(s_process_index)+lane)*ElementWidth+:ElementWidth],
quant_i[(int'(s_process_index)+lane)*8+:8],
reciprocal_i[(int'(s_process_index)+lane)*ReciprocalWidth+:ReciprocalWidth]
);
s_result_write_en[int'(s_process_index)+lane] = 1'b1;
end
end
end
end
end
// Every coefficient is overwritten before Result, so the output bank needs no reset.
for (genvar coefficient = 0; coefficient < 64; coefficient++) begin : gen_result_register
dffl #(
.DATA_WIDTH(ElementWidth)
) u_result_dffl (
.clk_i(clk_i),
.en_i (s_result_write_en[coefficient]),
.dat_i(s_result_write_data[coefficient*ElementWidth+:ElementWidth]),
.dat_o(s_result_q[coefficient*ElementWidth+:ElementWidth])
);
end
dffr #(
@@ -178,12 +193,12 @@ module jpeg_quantizer #(
.dat_o (s_state_bits_q)
);
dffr #(
.DATA_WIDTH(3)
) u_group_dffr (
.DATA_WIDTH(6)
) u_index_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_group_d),
.dat_o (s_group_q)
.dat_i (s_index_d),
.dat_o (s_index_q)
);
dffr u_dequantize_dffr (
.clk_i (clk_i),
@@ -191,38 +206,6 @@ module jpeg_quantizer #(
.dat_i (s_dequantize_d),
.dat_o (s_dequantize_q)
);
dffr #(
.DATA_WIDTH(64 * ElementWidth)
) u_input_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_input_d),
.dat_o (s_input_q)
);
dffr #(
.DATA_WIDTH(64 * 8)
) u_quant_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_quant_d),
.dat_o (s_quant_q)
);
dffr #(
.DATA_WIDTH(64 * ReciprocalWidth)
) u_reciprocal_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_reciprocal_d),
.dat_o (s_reciprocal_q)
);
dffr #(
.DATA_WIDTH(64 * ElementWidth)
) u_result_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_result_d),
.dat_o (s_result_q)
);
dffr u_table_err_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
@@ -238,9 +221,20 @@ module jpeg_quantizer #(
`ifndef SYNTHESIS
initial begin
if (ElementWidth < 16 || ReciprocalWidth <= ReciprocalShift) begin
if (ElementWidth < 16 || ReciprocalWidth <= ReciprocalShift || LaneCount < 1 ||
LaneCount > 8) begin
$fatal(1, "jpeg_quantizer: invalid arithmetic widths");
end
end
`endif
`ifndef SV_ASSRT_DISABLE
always_ff @(posedge clk_i) begin
if (rst_n_i && (s_state_q != Idle)) begin
assert ($stable(block_i));
assert ($stable(quant_i));
assert ($stable(reciprocal_i));
end
end
`endif
endmodule
+94 -119
View File
@@ -21,12 +21,12 @@ module jpeg_table_cache #(
input logic [31:0] lookup_data_i,
input logic lookup_valid_i,
input logic lookup_err_i,
output logic [64*8-1:0] quant_o,
output logic [64*ReciprocalWidth-1:0] reciprocal_o,
output logic [12*16-1:0] dc_code_o,
output logic [12*5-1:0] dc_length_o,
output logic [256*16-1:0] ac_code_o,
output logic [256*5-1:0] ac_length_o,
output logic [ 3:0] entry_kind_o,
output logic [ 7:0] entry_index_o,
output logic [31:0] entry_data_o,
output logic [ReciprocalWidth-1:0] entry_reciprocal_o,
output logic entry_valid_o,
input logic entry_ready_i,
output logic result_valid_o,
input logic result_ready_i,
output logic error_o
@@ -43,35 +43,29 @@ module jpeg_table_cache #(
Result
} state_e;
state_e s_state_d;
state_e s_state_q;
logic [ 2:0] s_state_bits_q;
logic [ 1:0] s_context_d;
logic [ 1:0] s_context_q;
logic [ 1:0] s_quant_id_d;
logic [ 1:0] s_quant_id_q;
logic [ 1:0] s_dc_id_d;
logic [ 1:0] s_dc_id_q;
logic [ 1:0] s_ac_id_d;
logic [ 1:0] s_ac_id_q;
logic [ 7:0] s_index_d;
logic [ 7:0] s_index_q;
logic [ 64*8-1:0] s_quant_d;
logic [ 64*8-1:0] s_quant_q;
logic [64*ReciprocalWidth-1:0] s_reciprocal_d;
logic [64*ReciprocalWidth-1:0] s_reciprocal_q;
logic [ 12*16-1:0] s_dc_code_d;
logic [ 12*16-1:0] s_dc_code_q;
logic [ 12*5-1:0] s_dc_len_d;
logic [ 12*5-1:0] s_dc_len_q;
logic [ 256*16-1:0] s_ac_code_d;
logic [ 256*16-1:0] s_ac_code_q;
logic [ 256*5-1:0] s_ac_len_d;
logic [ 256*5-1:0] s_ac_len_q;
logic s_err_d;
logic s_err_q;
logic [ 7:0] s_quant_value;
logic [ ReciprocalWidth-1:0] s_reciprocal_value;
state_e s_state_d;
state_e s_state_q;
logic [ 2:0] s_state_bits_q;
logic [ 1:0] s_context_d;
logic [ 1:0] s_context_q;
logic [ 1:0] s_quant_id_d;
logic [ 1:0] s_quant_id_q;
logic [ 1:0] s_dc_id_d;
logic [ 1:0] s_dc_id_q;
logic [ 1:0] s_ac_id_d;
logic [ 1:0] s_ac_id_q;
logic [ 7:0] s_index_d;
logic [ 7:0] s_index_q;
logic [ 31:0] s_entry_data_d;
logic [ 31:0] s_entry_data_q;
logic [ReciprocalWidth-1:0] s_entry_reciprocal_d;
logic [ReciprocalWidth-1:0] s_entry_reciprocal_q;
logic s_entry_pending_d;
logic s_entry_pending_q;
logic s_err_d;
logic s_err_q;
logic [ 7:0] s_quant_value;
logic [ReciprocalWidth-1:0] s_reciprocal_value;
function automatic logic [ReciprocalWidth-1:0] reciprocal_value(input logic [7:0] quant_i);
logic [ReciprocalWidth:0] s_numerator;
@@ -88,16 +82,17 @@ module jpeg_table_cache #(
assign start_ready_o = s_state_q == Idle;
assign lookup_o = (s_state_q == QuantIssue) || (s_state_q == DcIssue) || (s_state_q == AcIssue);
assign lookup_context_o = s_context_q;
assign lookup_kind_o = (s_state_q == QuantIssue) ? {2'd0, s_quant_id_q} :
(s_state_q == DcIssue) ? (4'd4 + {2'd0, s_dc_id_q}) :
(4'd8 + {2'd0, s_ac_id_q});
assign lookup_kind_o = ((s_state_q == QuantIssue) || (s_state_q == QuantWait)) ?
{2'd0, s_quant_id_q} :
((s_state_q == DcIssue) || (s_state_q == DcWait)) ?
(4'd4 + {2'd0, s_dc_id_q}) :
(4'd8 + {2'd0, s_ac_id_q});
assign lookup_index_o = s_index_q;
assign quant_o = s_quant_q;
assign reciprocal_o = s_reciprocal_q;
assign dc_code_o = s_dc_code_q;
assign dc_length_o = s_dc_len_q;
assign ac_code_o = s_ac_code_q;
assign ac_length_o = s_ac_len_q;
assign entry_kind_o = lookup_kind_o;
assign entry_index_o = s_index_q;
assign entry_data_o = s_entry_data_q;
assign entry_reciprocal_o = s_entry_reciprocal_q;
assign entry_valid_o = s_entry_pending_q;
assign result_valid_o = s_state_q == Result;
assign error_o = s_err_q;
assign s_quant_value = lookup_data_i[7:0];
@@ -108,49 +103,47 @@ module jpeg_table_cache #(
);
always_comb begin
s_state_d = s_state_q;
s_context_d = s_context_q;
s_quant_id_d = s_quant_id_q;
s_dc_id_d = s_dc_id_q;
s_ac_id_d = s_ac_id_q;
s_index_d = s_index_q;
s_quant_d = s_quant_q;
s_reciprocal_d = s_reciprocal_q;
s_dc_code_d = s_dc_code_q;
s_dc_len_d = s_dc_len_q;
s_ac_code_d = s_ac_code_q;
s_ac_len_d = s_ac_len_q;
s_err_d = s_err_q;
s_state_d = s_state_q;
s_context_d = s_context_q;
s_quant_id_d = s_quant_id_q;
s_dc_id_d = s_dc_id_q;
s_ac_id_d = s_ac_id_q;
s_index_d = s_index_q;
s_entry_data_d = s_entry_data_q;
s_entry_reciprocal_d = s_entry_reciprocal_q;
s_entry_pending_d = s_entry_pending_q;
s_err_d = s_err_q;
unique case (s_state_q)
Idle: begin
if (start_i) begin
s_context_d = context_i;
s_quant_id_d = quant_id_i;
s_dc_id_d = dc_id_i;
s_ac_id_d = ac_id_i;
s_index_d = 8'd0;
s_quant_d = '0;
s_reciprocal_d = '0;
s_dc_code_d = '0;
s_dc_len_d = '0;
s_ac_code_d = '0;
s_ac_len_d = '0;
s_err_d = 1'b0;
s_state_d = QuantIssue;
s_context_d = context_i;
s_quant_id_d = quant_id_i;
s_dc_id_d = dc_id_i;
s_ac_id_d = ac_id_i;
s_index_d = 8'd0;
s_entry_pending_d = 1'b0;
s_entry_data_d = '0;
s_entry_reciprocal_d = '0;
s_err_d = 1'b0;
s_state_d = QuantIssue;
end
end
QuantIssue: begin
s_state_d = QuantWait;
end
QuantWait: begin
if (lookup_valid_i || lookup_err_i) begin
if (!s_entry_pending_q && (lookup_valid_i || lookup_err_i)) begin
if (lookup_err_i || (s_quant_value == 8'd0)) begin
s_err_d = 1'b1;
s_err_d = 1'b1;
s_state_d = Result;
end else begin
s_quant_d[s_index_q*8+:8] = s_quant_value;
s_reciprocal_d[s_index_q*ReciprocalWidth+:ReciprocalWidth] = s_reciprocal_value;
s_entry_data_d = lookup_data_i;
s_entry_reciprocal_d = s_reciprocal_value;
s_entry_pending_d = 1'b1;
end
end else if (s_entry_pending_q && entry_ready_i) begin
s_entry_pending_d = 1'b0;
if (s_index_q == 8'd63) begin
s_index_d = 8'd0;
s_state_d = DcIssue;
@@ -164,13 +157,17 @@ module jpeg_table_cache #(
s_state_d = DcWait;
end
DcWait: begin
if (lookup_valid_i || lookup_err_i) begin
if (!s_entry_pending_q && (lookup_valid_i || lookup_err_i)) begin
if (lookup_err_i || (lookup_data_i[20:16] == 5'd0)) begin
s_err_d = 1'b1;
s_err_d = 1'b1;
s_state_d = Result;
end else begin
s_dc_code_d[s_index_q*16+:16] = lookup_data_i[15:0];
s_dc_len_d[s_index_q*5+:5] = lookup_data_i[20:16];
s_entry_data_d = lookup_data_i;
s_entry_reciprocal_d = '0;
s_entry_pending_d = 1'b1;
end
end else if (s_entry_pending_q && entry_ready_i) begin
s_entry_pending_d = 1'b0;
if (s_index_q == 8'd11) begin
s_index_d = 8'd0;
s_state_d = AcIssue;
@@ -184,13 +181,17 @@ module jpeg_table_cache #(
s_state_d = AcWait;
end
AcWait: begin
if (lookup_valid_i || lookup_err_i) begin
if (!s_entry_pending_q && (lookup_valid_i || lookup_err_i)) begin
if (lookup_err_i) begin
s_err_d = 1'b1;
s_err_d = 1'b1;
s_state_d = Result;
end else begin
s_ac_code_d[s_index_q*16+:16] = lookup_data_i[15:0];
s_ac_len_d[s_index_q*5+:5] = lookup_data_i[20:16];
s_entry_data_d = lookup_data_i;
s_entry_reciprocal_d = '0;
s_entry_pending_d = 1'b1;
end
end else if (s_entry_pending_q && entry_ready_i) begin
s_entry_pending_d = 1'b0;
if (s_index_q == 8'hff) begin
s_index_d = 8'd0;
s_state_d = Result;
@@ -258,52 +259,26 @@ module jpeg_table_cache #(
.dat_o (s_index_q)
);
dffr #(
.DATA_WIDTH(64 * 8)
) u_quant_dffr (
.DATA_WIDTH(32)
) u_entry_data_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_quant_d),
.dat_o (s_quant_q)
.dat_i (s_entry_data_d),
.dat_o (s_entry_data_q)
);
dffr #(
.DATA_WIDTH(64 * ReciprocalWidth)
) u_reciprocal_dffr (
.DATA_WIDTH(ReciprocalWidth)
) u_entry_reciprocal_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_reciprocal_d),
.dat_o (s_reciprocal_q)
.dat_i (s_entry_reciprocal_d),
.dat_o (s_entry_reciprocal_q)
);
dffr #(
.DATA_WIDTH(12 * 16)
) u_dc_code_dffr (
dffr u_entry_pending_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_dc_code_d),
.dat_o (s_dc_code_q)
);
dffr #(
.DATA_WIDTH(12 * 5)
) u_dc_len_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_dc_len_d),
.dat_o (s_dc_len_q)
);
dffr #(
.DATA_WIDTH(256 * 16)
) u_ac_code_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ac_code_d),
.dat_o (s_ac_code_q)
);
dffr #(
.DATA_WIDTH(256 * 5)
) u_ac_len_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ac_len_d),
.dat_o (s_ac_len_q)
.dat_i (s_entry_pending_d),
.dat_o (s_entry_pending_q)
);
dffr u_err_dffr (
.clk_i (clk_i),
@@ -0,0 +1,79 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND.
module jpeg_table_register_bank (
// verilog_format: off -- preserve streamed entry and expanded table columns
input logic clk_i,
input logic write_i,
input logic [ 3:0] entry_kind_i,
input logic [ 7:0] entry_index_i,
input logic [ 31:0] entry_data_i,
input logic [ 24:0] entry_reciprocal_i,
output logic [511:0] quant_o,
output logic [1599:0] reciprocal_o,
output logic [191:0] dc_code_o,
output logic [ 59:0] dc_length_o,
output logic [4095:0] ac_code_o,
output logic [1279:0] ac_length_o
// verilog_format: on
);
for (genvar index = 0; index < 64; index++) begin : gen_quant_entry
dffl #(
.DATA_WIDTH(8)
) u_quant_dffl (
.clk_i(clk_i),
.en_i (write_i && (entry_kind_i < 4'd4) && (entry_index_i == 8'(index))),
.dat_i(entry_data_i[7:0]),
.dat_o(quant_o[index*8+:8])
);
dffl #(
.DATA_WIDTH(25)
) u_reciprocal_dffl (
.clk_i(clk_i),
.en_i (write_i && (entry_kind_i < 4'd4) && (entry_index_i == 8'(index))),
.dat_i(entry_reciprocal_i),
.dat_o(reciprocal_o[index*25+:25])
);
end
for (genvar index = 0; index < 12; index++) begin : gen_dc_entry
dffl #(
.DATA_WIDTH(16)
) u_code_dffl (
.clk_i(clk_i),
.en_i (write_i && (entry_kind_i >= 4'd4) && (entry_kind_i < 4'd8) &&
(entry_index_i == 8'(index))),
.dat_i(entry_data_i[15:0]),
.dat_o(dc_code_o[index*16+:16])
);
dffl #(
.DATA_WIDTH(5)
) u_length_dffl (
.clk_i(clk_i),
.en_i (write_i && (entry_kind_i >= 4'd4) && (entry_kind_i < 4'd8) &&
(entry_index_i == 8'(index))),
.dat_i(entry_data_i[20:16]),
.dat_o(dc_length_o[index*5+:5])
);
end
for (genvar index = 0; index < 256; index++) begin : gen_ac_entry
dffl #(
.DATA_WIDTH(16)
) u_code_dffl (
.clk_i(clk_i),
.en_i (write_i && (entry_kind_i >= 4'd8) && (entry_index_i == 8'(index))),
.dat_i(entry_data_i[15:0]),
.dat_o(ac_code_o[index*16+:16])
);
dffl #(
.DATA_WIDTH(5)
) u_length_dffl (
.clk_i(clk_i),
.en_i (write_i && (entry_kind_i >= 4'd8) && (entry_index_i == 8'(index))),
.dat_i(entry_data_i[20:16]),
.dat_o(ac_length_o[index*5+:5])
);
end
endmodule
+78 -103
View File
@@ -32,75 +32,12 @@ module jpeg_transform #(
logic [ 2:0] s_index_q;
logic s_inverse_d;
logic s_inverse_q;
logic [64*ElementWidth-1:0] s_input_d;
logic [64*ElementWidth-1:0] s_input_q;
logic [64*ElementWidth-1:0] s_data_d;
logic [64*ElementWidth-1:0] s_data_q;
logic [64*ElementWidth-1:0] s_temp_d;
logic [64*ElementWidth-1:0] s_temp_q;
logic [64*ElementWidth-1:0] s_result_d;
logic [64*ElementWidth-1:0] s_result_q;
logic [ 8*ElementWidth-1:0] s_vector;
function automatic logic signed [15:0] cosine_value(input logic [2:0] frequency_i,
input logic [2:0] sample_i);
begin
unique case ({
frequency_i, sample_i
})
6'o00, 6'o01, 6'o02, 6'o03, 6'o04, 6'o05, 6'o06, 6'o07: cosine_value = 16'sd11585;
6'o10: cosine_value = 16'sd16069;
6'o11: cosine_value = 16'sd13623;
6'o12: cosine_value = 16'sd9102;
6'o13: cosine_value = 16'sd3196;
6'o14: cosine_value = -16'sd3196;
6'o15: cosine_value = -16'sd9102;
6'o16: cosine_value = -16'sd13623;
6'o17: cosine_value = -16'sd16069;
6'o20: cosine_value = 16'sd15137;
6'o21: cosine_value = 16'sd6270;
6'o22: cosine_value = -16'sd6270;
6'o23: cosine_value = -16'sd15137;
6'o24: cosine_value = -16'sd15137;
6'o25: cosine_value = -16'sd6270;
6'o26: cosine_value = 16'sd6270;
6'o27: cosine_value = 16'sd15137;
6'o30: cosine_value = 16'sd13623;
6'o31: cosine_value = -16'sd3196;
6'o32: cosine_value = -16'sd16069;
6'o33: cosine_value = -16'sd9102;
6'o34: cosine_value = 16'sd9102;
6'o35: cosine_value = 16'sd16069;
6'o36: cosine_value = 16'sd3196;
6'o37: cosine_value = -16'sd13623;
6'o40, 6'o43, 6'o44, 6'o47: cosine_value = 16'sd11585;
6'o41, 6'o42, 6'o45, 6'o46: cosine_value = -16'sd11585;
6'o50: cosine_value = 16'sd9102;
6'o51: cosine_value = -16'sd16069;
6'o52: cosine_value = 16'sd3196;
6'o53: cosine_value = 16'sd13623;
6'o54: cosine_value = -16'sd13623;
6'o55: cosine_value = -16'sd3196;
6'o56: cosine_value = 16'sd16069;
6'o57: cosine_value = -16'sd9102;
6'o60: cosine_value = 16'sd6270;
6'o61: cosine_value = -16'sd15137;
6'o62: cosine_value = 16'sd15137;
6'o63: cosine_value = -16'sd6270;
6'o64: cosine_value = -16'sd6270;
6'o65: cosine_value = 16'sd15137;
6'o66: cosine_value = -16'sd15137;
6'o67: cosine_value = 16'sd6270;
6'o70: cosine_value = 16'sd3196;
6'o71: cosine_value = -16'sd9102;
6'o72: cosine_value = 16'sd13623;
6'o73: cosine_value = -16'sd16069;
6'o74: cosine_value = 16'sd16069;
6'o75: cosine_value = -16'sd13623;
6'o76: cosine_value = 16'sd9102;
6'o77: cosine_value = -16'sd3196;
default: cosine_value = 16'sd0;
endcase
end
endfunction
logic [ 8*ElementWidth-1:0] s_transformed_vector;
function automatic logic signed [ElementWidth-1:0] rounded_scale(
input logic signed [47:0] value_i);
@@ -118,59 +55,105 @@ module jpeg_transform #(
end
endfunction
function automatic logic signed [ElementWidth-1:0] transform_1d(
input logic [8*ElementWidth-1:0] vector_i, input logic [2:0] output_i, input logic inverse_i);
logic signed [ 47:0] s_accumulator;
logic signed [ElementWidth-1:0] s_sample;
logic signed [ 15:0] s_cosine;
function automatic logic [8*ElementWidth-1:0] transform_vector(
input logic [8*ElementWidth-1:0] vector_i, input logic inverse_i);
logic signed [ ElementWidth-1:0] s_x [0:7];
logic signed [ ElementWidth:0] s_pair [0:3];
logic signed [ 47:0] s_even [0:3];
logic signed [ 47:0] s_odd [0:3];
logic signed [ 47:0] s_value [0:7];
logic [8*ElementWidth-1:0] s_result;
begin
s_accumulator = 48'sd0;
for (int unsigned index = 0; index < 8; index++) begin
s_sample = vector_i[index*ElementWidth+:ElementWidth];
s_cosine = inverse_i ? cosine_value(3'(index), output_i) :
cosine_value(output_i, 3'(index));
s_accumulator += s_sample * s_cosine;
s_x[index] = vector_i[index*ElementWidth+:ElementWidth];
s_value[index] = 48'sd0;
end
return rounded_scale(s_accumulator);
if (inverse_i) begin
s_even[0] = (s_x[0] * 16'sd11585) + (s_x[2] * 16'sd15137) +
(s_x[4] * 16'sd11585) + (s_x[6] * 16'sd6270);
s_even[1] = (s_x[0] * 16'sd11585) + (s_x[2] * 16'sd6270) -
(s_x[4] * 16'sd11585) - (s_x[6] * 16'sd15137);
s_even[2] = (s_x[0] * 16'sd11585) - (s_x[2] * 16'sd6270) -
(s_x[4] * 16'sd11585) + (s_x[6] * 16'sd15137);
s_even[3] = (s_x[0] * 16'sd11585) - (s_x[2] * 16'sd15137) +
(s_x[4] * 16'sd11585) - (s_x[6] * 16'sd6270);
s_odd[0] = (s_x[1] * 16'sd16069) + (s_x[3] * 16'sd13623) +
(s_x[5] * 16'sd9102) + (s_x[7] * 16'sd3196);
s_odd[1] = (s_x[1] * 16'sd13623) - (s_x[3] * 16'sd3196) -
(s_x[5] * 16'sd16069) - (s_x[7] * 16'sd9102);
s_odd[2] = (s_x[1] * 16'sd9102) - (s_x[3] * 16'sd16069) +
(s_x[5] * 16'sd3196) + (s_x[7] * 16'sd13623);
s_odd[3] = (s_x[1] * 16'sd3196) - (s_x[3] * 16'sd9102) +
(s_x[5] * 16'sd13623) - (s_x[7] * 16'sd16069);
for (int unsigned index = 0; index < 4; index++) begin
s_value[index] = s_even[index] + s_odd[index];
s_value[7-index] = s_even[index] - s_odd[index];
end
end else begin
for (int unsigned index = 0; index < 4; index++) begin
s_pair[index] = s_x[index] + s_x[7-index];
end
s_value[0] = (s_pair[0] + s_pair[1] + s_pair[2] + s_pair[3]) * 16'sd11585;
s_value[2] = ((s_pair[0] - s_pair[3]) * 16'sd15137) + ((s_pair[1] - s_pair[2]) * 16'sd6270);
s_value[4] = (s_pair[0] - s_pair[1] - s_pair[2] + s_pair[3]) * 16'sd11585;
s_value[6] = ((s_pair[0] - s_pair[3]) * 16'sd6270) - ((s_pair[1] - s_pair[2]) * 16'sd15137);
for (int unsigned index = 0; index < 4; index++) begin
s_pair[index] = s_x[index] - s_x[7-index];
end
s_value[1] = (s_pair[0] * 16'sd16069) + (s_pair[1] * 16'sd13623) +
(s_pair[2] * 16'sd9102) + (s_pair[3] * 16'sd3196);
s_value[3] = (s_pair[0] * 16'sd13623) - (s_pair[1] * 16'sd3196) -
(s_pair[2] * 16'sd16069) - (s_pair[3] * 16'sd9102);
s_value[5] = (s_pair[0] * 16'sd9102) - (s_pair[1] * 16'sd16069) +
(s_pair[2] * 16'sd3196) + (s_pair[3] * 16'sd13623);
s_value[7] = (s_pair[0] * 16'sd3196) - (s_pair[1] * 16'sd9102) +
(s_pair[2] * 16'sd13623) - (s_pair[3] * 16'sd16069);
end
s_result = '0;
for (int unsigned index = 0; index < 8; index++) begin
s_result[index*ElementWidth+:ElementWidth] = rounded_scale(s_value[index]);
end
return s_result;
end
endfunction
assign s_state_q = state_e'(s_state_bits_q);
assign start_ready_o = s_state_q == Idle;
assign block_o = s_result_q;
assign block_o = s_data_q;
assign result_valid_o = s_state_q == Result;
always_comb begin
s_state_d = s_state_q;
s_index_d = s_index_q;
s_inverse_d = s_inverse_q;
s_input_d = s_input_q;
s_temp_d = s_temp_q;
s_result_d = s_result_q;
s_vector = '0;
s_state_d = s_state_q;
s_index_d = s_index_q;
s_inverse_d = s_inverse_q;
s_data_d = s_data_q;
s_temp_d = s_temp_q;
s_vector = '0;
s_transformed_vector = transform_vector(s_vector, s_inverse_q);
unique case (s_state_q)
Idle: begin
s_index_d = 3'd0;
if (start_i) begin
s_input_d = block_i;
s_data_d = block_i;
s_inverse_d = inverse_i;
s_temp_d = '0;
s_result_d = '0;
s_state_d = FirstPass;
end
end
FirstPass: begin
for (int unsigned index = 0; index < 8; index++) begin
s_vector[index*ElementWidth+:ElementWidth] =
s_input_q[((s_index_q*8)+index)*ElementWidth+:ElementWidth];
s_data_q[((s_index_q*8)+index)*ElementWidth+:ElementWidth];
end
s_transformed_vector = transform_vector(s_vector, s_inverse_q);
for (int unsigned output_index = 0; output_index < 8; output_index++) begin
s_temp_d[((s_index_q*8)+output_index)*ElementWidth+:ElementWidth] =
transform_1d(s_vector, 3'(output_index), s_inverse_q);
s_transformed_vector[output_index*ElementWidth+:ElementWidth];
end
if (s_index_q == 3'd7) begin
s_index_d = 3'd0;
s_data_d = '0;
s_state_d = SecondPass;
end else begin
s_index_d = s_index_q + 1'b1;
@@ -181,9 +164,10 @@ module jpeg_transform #(
s_vector[index*ElementWidth+:ElementWidth] =
s_temp_q[((index*8)+int'(s_index_q))*ElementWidth+:ElementWidth];
end
s_transformed_vector = transform_vector(s_vector, s_inverse_q);
for (int unsigned output_index = 0; output_index < 8; output_index++) begin
s_result_d[((output_index*8)+int'(s_index_q))*ElementWidth+:ElementWidth] =
transform_1d(s_vector, 3'(output_index), s_inverse_q);
s_data_d[((output_index*8)+int'(s_index_q))*ElementWidth+:ElementWidth] =
s_transformed_vector[output_index*ElementWidth+:ElementWidth];
end
if (s_index_q == 3'd7) begin
s_index_d = 3'd0;
@@ -227,11 +211,11 @@ module jpeg_transform #(
);
dffr #(
.DATA_WIDTH(64 * ElementWidth)
) u_input_dffr (
) u_data_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_input_d),
.dat_o (s_input_q)
.dat_i (s_data_d),
.dat_o (s_data_q)
);
dffr #(
.DATA_WIDTH(64 * ElementWidth)
@@ -241,15 +225,6 @@ module jpeg_transform #(
.dat_i (s_temp_d),
.dat_o (s_temp_q)
);
dffr #(
.DATA_WIDTH(64 * ElementWidth)
) u_result_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_result_d),
.dat_o (s_result_q)
);
`ifndef SV_ASSRT_DISABLE
always_ff @(posedge clk_i) begin
if (rst_n_i) begin
+2
View File
@@ -128,6 +128,7 @@
/ip/multimedia/jpeg_pkg.sv
/ip/multimedia/jpeg_transform.sv
/ip/multimedia/jpeg_quantizer.sv
/ip/multimedia/jpeg_coefficient_engine.sv
/ip/multimedia/jpeg_entropy_encoder.sv
/ip/multimedia/jpeg_entropy_decoder.sv
/ip/multimedia/jpeg_bit_packer.sv
@@ -138,6 +139,7 @@
/ip/multimedia/jpeg_marker_parser.sv
/ip/multimedia/jpeg_table_store.sv
/ip/multimedia/jpeg_table_cache.sv
/ip/multimedia/jpeg_table_register_bank.sv
/ip/multimedia/jpeg_block_encoder.sv
/ip/multimedia/jpeg_block_decoder.sv
/ip/multimedia/jpeg_mcu_builder.sv
@@ -10,7 +10,7 @@
"reset_primitive": "rst_sync",
"path": "rtl/mini/top/soc_clock_reset_subsystem.sv",
"instance": "u_aon_rst_sync",
"sta": {"source_port": "ref24clk_i_pad", "pin": "u_rcu.u_clock_reset_subsystem/u_ref24_clk_buf", "period_ns": 41.666666667, "async_group": "aon"}
"sta": {"source_port": "ref24clk_i_pad", "pin": "u_rcu.u_clock_reset_subsystem.u_ref24_clk_buf/clk_o", "period_ns": 41.666666667, "async_group": "aon"}
},
{
"name": "lp",
@@ -20,7 +20,7 @@
"reset_primitive": "rst_sync",
"path": "rtl/mini/top/soc_clock_reset_subsystem.sv",
"instance": "u_lp_rst_sync",
"sta": {"source_domain": "aon", "pin": "u_rcu.u_clock_reset_subsystem/u_lp_root_mux", "period_ns": 13.888888889, "async_group": "lp"}
"sta": {"source_domain": "aon", "object_type": "net_driver", "net": "s_sys_clk", "period_ns": 13.888888889, "async_group": "lp"}
},
{
"name": "hp",
@@ -30,7 +30,7 @@
"reset_primitive": "rst_sync",
"path": "rtl/mini/top/soc_clock_reset_subsystem.sv",
"instance": "u_hp_rst_sync",
"sta": {"source_port": "extclk_i_pad", "pin": "u_rcu.u_clock_reset_subsystem/u_hp_root_mux", "period_ns": 4.166666667, "async_group": "hp"}
"sta": {"source_port": "extclk_i_pad", "object_type": "net_driver", "net": "s_hp_clk", "period_ns": 4.166666667, "async_group": "hp"}
},
{
"name": "pclk",
@@ -40,7 +40,7 @@
"reset_primitive": "rst_sync",
"path": "rtl/mini/top/soc_clock_reset_subsystem.sv",
"instance": "u_pclk_rst_sync",
"sta": {"source_domain": "lp", "pin": "u_rcu.u_clock_reset_subsystem/u_pclk_divider", "period_ns": 20.833333333, "async_group": "lp"}
"sta": {"source_domain": "lp", "object_type": "net_driver", "net": "s_pclk", "period_ns": 20.833333333, "async_group": "lp"}
},
{
"name": "memory",
@@ -50,7 +50,7 @@
"reset_primitive": "rst_sync",
"path": "rtl/mini/top/soc_clock_reset_subsystem.sv",
"instance": "u_mem_rst_sync",
"sta": {"source_port": "extclk_i_pad", "pin": "u_rcu.u_clock_reset_subsystem/u_mem_clk_div2", "period_ns": 27.777777778, "async_group": "memory"}
"sta": {"source_port": "extclk_i_pad", "object_type": "net_driver", "net": "s_mem_clk", "period_ns": 27.777777778, "async_group": "memory"}
},
{
"name": "audio",
@@ -60,7 +60,7 @@
"reset_primitive": "rst_sync",
"path": "rtl/mini/top/soc_clock_reset_subsystem.sv",
"instance": "u_aud_rst_sync",
"sta": {"source_port": "audclk_i_pad", "pin": "u_rcu.u_clock_reset_subsystem/u_aud_clk_buf", "period_ns": 54.253472222, "async_group": "audio"}
"sta": {"source_port": "audclk_i_pad", "pin": "u_rcu.u_clock_reset_subsystem.u_aud_clk_buf/clk_o", "period_ns": 54.253472222, "async_group": "audio"}
},
{
"name": "jtag",
@@ -80,7 +80,7 @@
"reset_primitive": "rst_sync",
"path": "rtl/ip/multimedia/axi4s_dvp.sv",
"instance": "u_dvp_pclk_rst_sync",
"sta": {"source_port": "gpio_10_io_pad", "pin": "u_retrosoc.u_apb4_periph.u_axi4_dvp/u_dvp_pclk_clk_buf", "period_ns": 41.666666667, "async_group": "dvp"}
"sta": {"source_port": "gpio_10_io_pad", "pin": "u_retrosoc.u_apb4_periph.u_axi4_dvp.u_dvp_pclk_clk_buf/clk_o", "period_ns": 41.666666667, "async_group": "dvp"}
},
{
"name": "usb2_ulpi",
+1 -1
View File
@@ -224,4 +224,4 @@ firmware: $(FIRMWARE_ELF)
firmware asm: | manifest
.PHONY: FORCE_VERSION
.PHONY: FORCE_VERSION
+33 -33
View File
@@ -853,40 +853,40 @@ core_wrapper u_core_wrapper (
);
apb4_system u_apb4_system (
.clk_i(clk_pclk_i),
.rst_n_i(rst_pclk_n_i),
.clk_aud_i(clk_aud_i),
.rst_aud_n_i(rst_aud_n_i),
.debug_halted_i(s_mgmt_debug_halted),
.ext_h_data_idle_i(s_ext_h_data_idle),
.resource_idle_i(s_resource_idle_combined),
.clk_i (clk_pclk_i),
.rst_n_i (rst_pclk_n_i),
.clk_aud_i (clk_aud_i),
.rst_aud_n_i (rst_aud_n_i),
.debug_halted_i (s_mgmt_debug_halted),
.ext_h_data_idle_i (s_ext_h_data_idle),
.resource_idle_i (s_resource_idle_combined),
.resource_block_ack_i(s_resource_block_ack_pclk),
.resource_irq_i(s_resource_irq_raw),
.cache_request_i(s_hp_cache_req_pclk),
.ext_h_axi4(u_ext_h_wide_axi4_if),
.fabric_monitor(u_fabric_monitor_pclk_if),
.axi4(u_system_pclk_axi4_if),
.pwm(u_pwm_if),
.ps2(u_ps2_if),
.ip_sel_i(u_sysctrl_if.ip_sel_o),
.user_gpio(u_user_gpio_if),
.rtc_wake_o(s_rtc_wake),
.wdg_reset_req_o(wdg_reset_req_o),
.ext_h_block_o(s_ext_h_block),
.ext_h_read_base_o(s_ext_h_read_base),
.ext_h_read_limit_o(s_ext_h_read_limit),
.ext_h_write_base_o(s_ext_h_write_base),
.ext_h_write_limit_o(s_ext_h_write_limit),
.ext_h_timeout_o(s_ext_h_timeout),
.ext_h_irq_raw_o(s_ext_h_irq_raw),
.ext_h_owner_o(s_ext_h_owner),
.cache_clean_o(s_hp_cache_clean_pclk),
.resource_owner_o(s_resource_owner),
.resource_quiesce_o(s_resource_quiesce),
.resource_reset_o(s_resource_reset),
.resource_irq_lp_o(s_resource_irq_lp),
.resource_irq_hp_o(s_resource_irq_hp),
.irq_o(s_apb4_system_irq)
.resource_irq_i (s_resource_irq_raw),
.cache_request_i (s_hp_cache_req_pclk),
.ext_h_axi4 (u_ext_h_wide_axi4_if),
.fabric_monitor (u_fabric_monitor_pclk_if),
.axi4 (u_system_pclk_axi4_if),
.pwm (u_pwm_if),
.ps2 (u_ps2_if),
.ip_sel_i (u_sysctrl_if.ip_sel_o),
.user_gpio (u_user_gpio_if),
.rtc_wake_o (s_rtc_wake),
.wdg_reset_req_o (wdg_reset_req_o),
.ext_h_block_o (s_ext_h_block),
.ext_h_read_base_o (s_ext_h_read_base),
.ext_h_read_limit_o (s_ext_h_read_limit),
.ext_h_write_base_o (s_ext_h_write_base),
.ext_h_write_limit_o (s_ext_h_write_limit),
.ext_h_timeout_o (s_ext_h_timeout),
.ext_h_irq_raw_o (s_ext_h_irq_raw),
.ext_h_owner_o (s_ext_h_owner),
.cache_clean_o (s_hp_cache_clean_pclk),
.resource_owner_o (s_resource_owner),
.resource_quiesce_o (s_resource_quiesce),
.resource_reset_o (s_resource_reset),
.resource_irq_lp_o (s_resource_irq_lp),
.resource_irq_hp_o (s_resource_irq_hp),
.irq_o (s_apb4_system_irq)
);
assign s_resource_idle_combined = {
+1 -3
View File
@@ -639,9 +639,7 @@ module soc_data_plane (
(|s_guard_clear_busy) || s_lp_clear_busy || s_ext_clear_busy ||
s_jpeg_clear_busy;
assign s_unused_epoch = {
^unused_io_epoch,
^unused_target_epoch,
^{unused_lp_epoch, unused_ext_epoch, unused_jpeg_epoch}
^unused_io_epoch, ^unused_target_epoch, ^{unused_lp_epoch, unused_ext_epoch, unused_jpeg_epoch}
};
axi4_connector u_qpi_gateway_connector (
+2
View File
@@ -144,6 +144,7 @@
"jpeg_block_encoder.sv",
"jpeg_byte_joiner.sv",
"jpeg_byte_unpacker.sv",
"jpeg_coefficient_engine.sv",
"jpeg_decode_core.sv",
"jpeg_define.svh",
"jpeg_dma.sv",
@@ -158,6 +159,7 @@
"jpeg_quantizer.sv",
"jpeg_reg.sv",
"jpeg_table_cache.sv",
"jpeg_table_register_bank.sv",
"jpeg_table_store.sv",
"jpeg_transform.sv"
],
+129
View File
@@ -156,6 +156,135 @@ module tc_sram_1024x32 (
`endif
endmodule
module tc_sram_64x64 (
// verilog_format: off -- preserve the technology memory interface columns
input logic clk_i,
input logic cs_i,
input logic [ 5:0] addr_i,
input logic [63:0] data_i,
input logic wren_i,
output logic [63:0] data_o
// verilog_format: on
);
`ifdef PDK_IHP130
`ifdef HAVE_SRAM_MACRO
`define _TC_SRAM_64X64_IHP_MACRO
`endif
`endif
`ifdef _TC_SRAM_64X64_IHP_MACRO
RM_IHPSG13_1P_64x64_c2_bm_bist u_mem (
.A_CLK (clk_i),
.A_ADDR (addr_i),
.A_BM (64'hffff_ffff_ffff_ffff),
.A_MEN (cs_i),
.A_WEN (wren_i),
.A_REN (~wren_i),
.A_DIN (data_i),
.A_DOUT (data_o),
.A_DLY (1'b1),
.A_BIST_CLK (1'b0),
.A_BIST_EN (1'b0),
.A_BIST_MEN (1'b0),
.A_BIST_WEN (1'b0),
.A_BIST_REN (1'b0),
.A_BIST_ADDR(6'b0),
.A_BIST_DIN (64'b0),
.A_BIST_BM (64'b0)
);
`else
logic [63:0] s_storage[64];
always_ff @(posedge clk_i) begin
if (cs_i) begin
if (wren_i) begin
s_storage[addr_i] <= data_i;
end else begin
data_o <= s_storage[addr_i];
end
end
end
`endif
`ifdef _TC_SRAM_64X64_IHP_MACRO
`undef _TC_SRAM_64X64_IHP_MACRO
`endif
endmodule
module tc_sram_64x32_2p (
// verilog_format: off -- preserve the dual-port technology memory columns
input logic clk_i,
input logic a_cs_i,
input logic [ 5:0] a_addr_i,
input logic [31:0] a_data_i,
input logic a_wren_i,
output logic [31:0] a_data_o,
input logic b_cs_i,
input logic [ 5:0] b_addr_i,
input logic [31:0] b_data_i,
input logic b_wren_i,
output logic [31:0] b_data_o
// verilog_format: on
);
`ifdef PDK_IHP130
`ifdef HAVE_SRAM_MACRO
`define _TC_SRAM_64X32_2P_IHP_MACRO
`endif
`endif
`ifdef _TC_SRAM_64X32_2P_IHP_MACRO
RM_IHPSG13_2P_64x32_c2 u_mem (
.A_CLK (clk_i),
.A_ADDR(a_addr_i),
.A_MEN (a_cs_i),
.A_WEN (a_wren_i),
.A_REN (~a_wren_i),
.A_DIN (a_data_i),
.A_DOUT(a_data_o),
.A_DLY (1'b1),
.B_CLK (clk_i),
.B_ADDR(b_addr_i),
.B_MEN (b_cs_i),
.B_WEN (b_wren_i),
.B_REN (~b_wren_i),
.B_DIN (b_data_i),
.B_DOUT(b_data_o),
.B_DLY (1'b1)
);
`else
logic [31:0] s_storage[64];
always_ff @(posedge clk_i) begin
if (a_cs_i) begin
if (a_wren_i) begin
s_storage[a_addr_i] <= a_data_i;
end else begin
a_data_o <= s_storage[a_addr_i];
end
end
if (b_cs_i) begin
if (b_wren_i) begin
s_storage[b_addr_i] <= b_data_i;
end else begin
b_data_o <= s_storage[b_addr_i];
end
end
end
`endif
`ifndef SYNTHESIS
always_ff @(posedge clk_i) begin
if (a_cs_i && b_cs_i && (a_addr_i == b_addr_i) && (a_wren_i || b_wren_i)) begin
$fatal(1, "tc_sram_64x32_2p: conflicting access at address %0d", a_addr_i);
end
end
`endif
`ifdef _TC_SRAM_64X32_2P_IHP_MACRO
`undef _TC_SRAM_64X32_2P_IHP_MACRO
`endif
endmodule
module tc_sram_4096x32 (
// verilog_format: off -- preserve the technology memory interface columns
input logic clk_i,
+4 -2
View File
@@ -107,10 +107,12 @@ def validate(document_path: Path, root: Path) -> None:
else:
sta_sources[name] = require_identifier(source_domain, f"{field}.sta.source_domain")
object_type = sta.get("object_type", "pin")
if object_type not in {"pin", "port"}:
raise ValueError(f"{field}.sta.object_type must be pin or port")
if object_type not in {"net_driver", "pin", "port"}:
raise ValueError(f"{field}.sta.object_type must be net_driver, pin, or port")
if object_type == "pin":
require_hierarchical_pin(sta.get("pin"), f"{field}.sta.pin")
elif object_type == "net_driver":
require_identifier(sta.get("net"), f"{field}.sta.net")
elif source_port is None:
raise ValueError(f"{field}.sta port object requires source_port")
require_positive_number(sta.get("period_ns"), f"{field}.sta.period_ns")
+19
View File
@@ -42,6 +42,7 @@ RTL_LINT_OBSERVATION_VALUES = (
"check-rtl-lint",
)
OBSERVATION_TARGETS = ("check-warnings", "check-metrics")
SYNTHESIS_DEPENDENT_TARGETS = frozenset(("synth", "sta", "netsim", "netsim-boot", "metrics"))
PR_COMMANDS = (
@@ -192,6 +193,17 @@ def with_netsim_boot_only(
return tuple(transformed)
def behavioral_only(
commands: tuple[tuple[str, tuple[str, ...]], ...],
) -> tuple[tuple[str, tuple[str, ...]], ...]:
"""Exclude synthesis and commands that consume its generated netlist."""
return tuple(
(profile, values)
for profile, values in commands
if SYNTHESIS_DEPENDENT_TARGETS.isdisjoint(values)
)
def run_command(
command: list[str], root: Path, capture_output: bool, environment: dict[str, str]
) -> str:
@@ -243,6 +255,11 @@ def main() -> int:
action="store_true",
help="locally stop each Icarus assembly netlist run after Hello retroSoC!",
)
parser.add_argument(
"--behavioral-only",
action="store_true",
help="skip synthesis, STA, netlist simulation, and synthesis-recipe metrics",
)
args = parser.parse_args()
try:
commands, profiles = select_regression(args.suite, args.pdk)
@@ -250,6 +267,8 @@ def main() -> int:
parser.error(str(error))
if args.netsim_boot_only:
commands = with_netsim_boot_only(commands)
if args.behavioral_only:
commands = behavioral_only(commands)
environment = regression_environment()
for profile, values in commands:
command = ["make", f"CONFIG={profile}", *values]
+7 -7
View File
@@ -146,13 +146,13 @@ module apb4_jpeg_ring_tb;
memory[index] = 64'd0;
end
memory[32'h1000>>3] = 64'h0000000000000001;
apb4.paddr = '0;
apb4.pprot = '0;
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pwdata = '0;
apb4.pstrb = '0;
apb4.paddr = '0;
apb4.pprot = '0;
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pwdata = '0;
apb4.pstrb = '0;
repeat (3) @(posedge clk);
rst_n = 1'b1;
+151
View File
@@ -0,0 +1,151 @@
`timescale 1ns / 1ps
module jpeg_coefficient_engine_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic start;
logic decode;
logic [1535:0] block_in;
logic [ 511:0] quant;
logic [1599:0] reciprocal;
logic start_ready;
logic [1535:0] block_out;
logic result_valid;
logic result_ready;
logic table_err;
logic overflow;
always #5 clk = ~clk;
jpeg_coefficient_engine u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.decode_i (decode),
.block_i (block_in),
.quant_i (quant),
.reciprocal_i (reciprocal),
.start_ready_o (start_ready),
.block_o (block_out),
.result_valid_o(result_valid),
.result_ready_i(result_ready),
.table_err_o (table_err),
.overflow_o (overflow)
);
function automatic logic [7:0] luma_quant(input int index_i);
logic [7:0] values[0:63];
begin
values = '{
16,
11,
10,
16,
24,
40,
51,
61,
12,
12,
14,
19,
26,
58,
60,
55,
14,
13,
16,
24,
40,
57,
69,
56,
14,
17,
22,
29,
51,
87,
80,
62,
18,
22,
37,
56,
68,
109,
103,
77,
24,
35,
55,
64,
81,
104,
113,
92,
49,
64,
78,
87,
103,
121,
120,
101,
72,
92,
95,
98,
112,
100,
103,
99
};
return values[index_i];
end
endfunction
function automatic logic signed [23:0] expected_quantized(input int index_i);
begin
unique case (index_i)
1: return -24'sd2;
8: return -24'sd12;
24: return -24'sd1;
default: return 24'sd0;
endcase
end
endfunction
initial begin
start = 1'b0;
decode = 1'b0;
block_in = '0;
quant = '0;
reciprocal = '0;
result_ready = 1'b0;
for (int index = 0; index < 64; index++) begin
block_in[index*24+:24] = 24'(index - 32);
quant[index*8+:8] = luma_quant(index);
reciprocal[index*25+:25] = (25'd16777216 + luma_quant(index) - 1'b1) / luma_quant(index);
end
repeat (3) @(negedge clk);
rst_n = 1'b1;
@(negedge clk);
if (!start_ready) $fatal(1, "coefficient engine not ready");
start = 1'b1;
@(negedge clk);
start = 1'b0;
while (!result_valid) @(negedge clk);
if (table_err || overflow) $fatal(1, "unexpected coefficient engine error");
for (int index = 0; index < 64; index++) begin
if ($signed(block_out[index*24+:24]) != expected_quantized(index)) begin
$fatal(1, "coefficient mismatch at %0d: %0d", index, $signed(block_out[index*24+:24]));
end
end
result_ready = 1'b1;
@(negedge clk);
$display("JPEG coefficient engine test passed");
$finish;
end
endmodule
+74 -35
View File
@@ -1,23 +1,33 @@
`timescale 1ns / 1ps
module jpeg_decode_core_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic start;
logic busy;
logic done;
logic [15:0] width;
logic [15:0] height;
logic [ 1:0] sampling;
logic error_flag;
logic [ 4:0] error_code;
logic [ 7:0] input_bytes [0:2047];
logic [ 7:0] expected_rgb [0:4095];
integer input_size;
integer expected_size;
integer output_index;
string input_path;
string expected_path;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic start;
logic busy;
logic done;
logic [ 15:0] width;
logic [ 15:0] height;
logic [ 1:0] sampling;
logic error_flag;
logic [ 4:0] error_code;
logic coefficient_start;
logic [1535:0] coefficient_block;
logic [ 511:0] coefficient_quant;
logic [1599:0] coefficient_reciprocal;
logic coefficient_start_ready;
logic [1535:0] coefficient_result;
logic coefficient_result_valid;
logic coefficient_result_ready;
logic coefficient_table_err;
logic coefficient_overflow;
logic [ 7:0] input_bytes [0:2047];
logic [ 7:0] expected_rgb [0:4095];
integer input_size;
integer expected_size;
integer output_index;
string input_path;
string expected_path;
axi4_stream_if #(
.DATA_WIDTH(64)
@@ -34,21 +44,49 @@ module jpeg_decode_core_tb;
always #5 clk = ~clk;
jpeg_decode_core u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.output_format_i(3'd2),
.bitstream_axis (bitstream_axis),
.pixel_axis (pixel_axis),
.start_ready_o (),
.busy_o (busy),
.done_o (done),
.width_o (width),
.height_o (height),
.sampling_o (sampling),
.error_o (error_flag),
.error_code_o (error_code)
jpeg_coefficient_engine u_coefficient_engine (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (coefficient_start),
.decode_i (1'b1),
.block_i (coefficient_block),
.quant_i (coefficient_quant),
.reciprocal_i (coefficient_reciprocal),
.start_ready_o (coefficient_start_ready),
.block_o (coefficient_result),
.result_valid_o(coefficient_result_valid),
.result_ready_i(coefficient_result_ready),
.table_err_o (coefficient_table_err),
.overflow_o (coefficient_overflow)
);
jpeg_decode_core #(
.ExternalCoefficientEngine(1'b1)
) u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.output_format_i (3'd2),
.coefficient_start_o (coefficient_start),
.coefficient_block_o (coefficient_block),
.coefficient_quant_o (coefficient_quant),
.coefficient_reciprocal_o (coefficient_reciprocal),
.coefficient_start_ready_i (coefficient_start_ready),
.coefficient_result_i (coefficient_result),
.coefficient_result_valid_i(coefficient_result_valid),
.coefficient_result_ready_o(coefficient_result_ready),
.coefficient_table_err_i (coefficient_table_err),
.coefficient_overflow_i (coefficient_overflow),
.bitstream_axis (bitstream_axis),
.pixel_axis (pixel_axis),
.start_ready_o (),
.busy_o (busy),
.done_o (done),
.width_o (width),
.height_o (height),
.sampling_o (sampling),
.error_o (error_flag),
.error_code_o (error_code)
);
task automatic send_input_beat(input integer offset_i);
@@ -129,9 +167,10 @@ module jpeg_decode_core_tb;
end
if (error_flag || !done || width != 16'd16 || height != 16'd16 || sampling != 2'd3 ||
output_index != expected_size) begin
$fatal(1,
"decode completion mismatch: done=%0d err=%0d code=%0d size=%0dx%0d sampling=%0d bytes=%0d expected=%0d",
done, error_flag, error_code, width, height, sampling, output_index, expected_size);
$fatal(
1,
"decode completion mismatch: done=%0d err=%0d code=%0d size=%0dx%0d sampling=%0d bytes=%0d expected=%0d",
done, error_flag, error_code, width, height, sampling, output_index, expected_size);
end
$display("JPEG decode core tests passed");
$finish;
+80 -38
View File
@@ -19,13 +19,23 @@ module jpeg_encode_core_tb;
logic busy;
logic done;
logic error_flag;
logic [ 7:0] expected [0:2047];
logic [ 7:0] luma_quant_values [ 0:63];
logic [ 7:0] chroma_quant_values [ 0:63];
logic [ 31:0] luma_dc_entries [ 0:11];
logic [ 31:0] chroma_dc_entries [ 0:11];
logic [ 31:0] luma_ac_entries [ 0:255];
logic [ 31:0] chroma_ac_entries [ 0:255];
logic coefficient_start;
logic [1535:0] coefficient_block;
logic [ 511:0] coefficient_quant;
logic [1599:0] coefficient_reciprocal;
logic coefficient_start_ready;
logic [1535:0] coefficient_result;
logic coefficient_result_valid;
logic coefficient_result_ready;
logic coefficient_table_err;
logic coefficient_overflow;
logic [ 7:0] expected [0:2047];
logic [ 7:0] luma_quant_values [ 0:63];
logic [ 7:0] chroma_quant_values [ 0:63];
logic [ 31:0] luma_dc_entries [ 0:11];
logic [ 31:0] chroma_dc_entries [ 0:11];
logic [ 31:0] luma_ac_entries [ 0:255];
logic [ 31:0] chroma_ac_entries [ 0:255];
integer expected_size;
integer output_index;
integer input_beats;
@@ -57,36 +67,68 @@ module jpeg_encode_core_tb;
always #5 clk = ~clk;
jpeg_encode_core u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.width_i (test_width[15:0]),
.height_i (test_height[15:0]),
.sampling_i (2'd3),
.input_format_i (3'd2),
.restart_interval_i (16'd0),
.luma_quant_i (luma_quant),
.luma_reciprocal_i (luma_reciprocal),
.luma_dc_code_i (luma_dc_code),
.luma_dc_length_i (luma_dc_length),
.luma_ac_code_i (luma_ac_code),
.luma_ac_length_i (luma_ac_length),
.chroma_quant_i (chroma_quant),
.chroma_reciprocal_i(chroma_reciprocal),
.chroma_dc_code_i (chroma_dc_code),
.chroma_dc_length_i (chroma_dc_length),
.chroma_ac_code_i (chroma_ac_code),
.chroma_ac_length_i (chroma_ac_length),
.pixel_axis (pixel_axis),
.bitstream_axis (bitstream_axis),
.start_ready_o (),
.busy_o (busy),
.done_o (done),
.error_o (error_flag)
jpeg_coefficient_engine u_coefficient_engine (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (coefficient_start),
.decode_i (1'b0),
.block_i (coefficient_block),
.quant_i (coefficient_quant),
.reciprocal_i (coefficient_reciprocal),
.start_ready_o (coefficient_start_ready),
.block_o (coefficient_result),
.result_valid_o(coefficient_result_valid),
.result_ready_i(coefficient_result_ready),
.table_err_o (coefficient_table_err),
.overflow_o (coefficient_overflow)
);
jpeg_encode_core #(
.ExternalCoefficientEngine(1'b1)
) u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.width_i (test_width[15:0]),
.height_i (test_height[15:0]),
.sampling_i (2'd3),
.input_format_i (3'd2),
.restart_interval_i (16'd0),
.luma_quant_i (luma_quant),
.luma_reciprocal_i (luma_reciprocal),
.luma_dc_code_i (luma_dc_code),
.luma_dc_length_i (luma_dc_length),
.luma_ac_code_i (luma_ac_code),
.luma_ac_length_i (luma_ac_length),
.chroma_quant_i (chroma_quant),
.chroma_reciprocal_i (chroma_reciprocal),
.chroma_dc_code_i (chroma_dc_code),
.chroma_dc_length_i (chroma_dc_length),
.chroma_ac_code_i (chroma_ac_code),
.chroma_ac_length_i (chroma_ac_length),
.coefficient_start_o (coefficient_start),
.coefficient_block_o (coefficient_block),
.coefficient_quant_o (coefficient_quant),
.coefficient_reciprocal_o (coefficient_reciprocal),
.coefficient_start_ready_i (coefficient_start_ready),
.coefficient_result_i (coefficient_result),
.coefficient_result_valid_i(coefficient_result_valid),
.coefficient_result_ready_o(coefficient_result_ready),
.coefficient_table_err_i (coefficient_table_err),
.coefficient_overflow_i (coefficient_overflow),
.pixel_axis (pixel_axis),
.bitstream_axis (bitstream_axis),
.start_ready_o (),
.busy_o (busy),
.done_o (done),
.error_o (error_flag)
);
always @(posedge clk) begin
if (rst_n && u_dut.s_builder_block_valid && (u_dut.s_builder_block !== {64{8'd128}})) begin
$fatal(1, "MCU builder output changed under block backpressure: block=%0d data=%h",
u_dut.u_mcu_builder.s_block_cnt_q, u_dut.s_builder_block);
end
if (rst_n && busy) begin
cycle_count <= cycle_count + 1;
end
@@ -129,8 +171,8 @@ module jpeg_encode_core_tb;
)) begin
$fatal(1, "encode core test requires JPEG and table plusargs");
end
test_width = 16;
test_height = 16;
test_width = 16;
test_height = 16;
expected_input_beats = 96;
void'($value$plusargs("test_width=%d", test_width));
void'($value$plusargs("test_height=%d", test_height));
@@ -199,8 +241,8 @@ module jpeg_encode_core_tb;
pixel_axis.tlast = input_beats + 1 == expected_input_beats;
end
pixel_axis.tvalid = 1'b0;
pixel_axis.tlast = 1'b0;
timeout_cycles = 3000;
pixel_axis.tlast = 1'b0;
timeout_cycles = 10000;
while (!done && (timeout_cycles > 0)) begin
@(negedge clk);
timeout_cycles--;
+30 -4
View File
@@ -9,8 +9,10 @@ module jpeg_mcu_builder_tb;
logic block_last;
logic done;
logic error_flag;
logic [511:0] expected_block;
int input_beats;
int output_blocks;
int timeout_cycles;
axi4_stream_if #(
.DATA_WIDTH(64)
@@ -21,6 +23,16 @@ module jpeg_mcu_builder_tb;
always #5 clk = ~clk;
always_comb begin
expected_block = {64{8'd128}};
if (output_blocks < 4) begin
for (int sample = 0; sample < 64; sample ++) begin
expected_block[sample*8+:8] = 8'((((output_blocks >> 1) * 8 + (sample >> 3)) * 16) +
((output_blocks & 1) * 8) + (sample & 7));
end
end
end
jpeg_mcu_builder u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
@@ -44,8 +56,8 @@ module jpeg_mcu_builder_tb;
input_beats <= input_beats + 1;
end
if (rst_n && block_valid) begin
if (block_data != {64{8'd128}} || (block_last != (output_blocks == 5))) begin
$fatal(1, "generated block mismatch at %0d", output_blocks);
if (block_data !== expected_block || (block_last != (output_blocks == 5))) begin
$fatal(1, "generated block mismatch at %0d: %h", output_blocks, block_data);
end
output_blocks <= output_blocks + 1;
end
@@ -53,7 +65,7 @@ module jpeg_mcu_builder_tb;
initial begin
start = 1'b0;
pixel_axis.tdata = {8{8'd128}};
pixel_axis.tdata = '0;
pixel_axis.tkeep = 8'hff;
pixel_axis.tstrb = 8'hff;
pixel_axis.tlast = 1'b0;
@@ -70,13 +82,27 @@ module jpeg_mcu_builder_tb;
@(negedge clk);
start = 1'b0;
pixel_axis.tvalid = 1'b1;
for (int lane = 0; lane < 8; lane++) begin
pixel_axis.tdata[lane*8+:8] = 8'(lane / 3);
end
while (input_beats < 96) begin
@(negedge clk);
pixel_axis.tlast = input_beats == 95;
for (int lane = 0; lane < 8; lane++) begin
pixel_axis.tdata[lane*8+:8] = 8'(((input_beats * 8) + lane) / 3);
end
end
pixel_axis.tvalid = 1'b0;
pixel_axis.tlast = 1'b0;
while (!done) @(negedge clk);
timeout_cycles = 2000;
while (!done && (timeout_cycles > 0)) begin
@(negedge clk);
timeout_cycles--;
end
if (timeout_cycles == 0) begin
$fatal(1, "MCU builder timeout: state=%0d block=%0d sample=%0d", u_dut.s_state_q,
u_dut.s_block_cnt_q, u_dut.s_sample_cnt_q);
end
if (error_flag || output_blocks != 6) begin
$fatal(1, "MCU builder completion mismatch: err=%0d blocks=%0d", error_flag, output_blocks);
end
+90
View File
@@ -0,0 +1,90 @@
`timescale 1ns / 1ps
module tc_sram_jpeg_workspace_tb;
logic clk = 1'b0;
logic row_cs;
logic [ 5:0] row_addr;
logic [63:0] row_data_i;
logic row_wren;
logic [63:0] row_data_o;
logic coeff_a_cs;
logic [ 5:0] coeff_a_addr;
logic [31:0] coeff_a_data_i;
logic coeff_a_wren;
logic [31:0] coeff_a_data_o;
logic coeff_b_cs;
logic [ 5:0] coeff_b_addr;
logic [31:0] coeff_b_data_i;
logic coeff_b_wren;
logic [31:0] coeff_b_data_o;
always #5 clk = ~clk;
tc_sram_64x64 u_row_sram (
.clk_i (clk),
.cs_i (row_cs),
.addr_i(row_addr),
.data_i(row_data_i),
.wren_i(row_wren),
.data_o(row_data_o)
);
tc_sram_64x32_2p u_coeff_sram (
.clk_i (clk),
.a_cs_i (coeff_a_cs),
.a_addr_i(coeff_a_addr),
.a_data_i(coeff_a_data_i),
.a_wren_i(coeff_a_wren),
.a_data_o(coeff_a_data_o),
.b_cs_i (coeff_b_cs),
.b_addr_i(coeff_b_addr),
.b_data_i(coeff_b_data_i),
.b_wren_i(coeff_b_wren),
.b_data_o(coeff_b_data_o)
);
initial begin
row_cs = 1'b0;
row_addr = '0;
row_data_i = '0;
row_wren = 1'b0;
coeff_a_cs = 1'b0;
coeff_a_addr = '0;
coeff_a_data_i = '0;
coeff_a_wren = 1'b0;
coeff_b_cs = 1'b0;
coeff_b_addr = '0;
coeff_b_data_i = '0;
coeff_b_wren = 1'b0;
@(negedge clk);
row_cs = 1'b1;
row_wren = 1'b1;
row_addr = 6'd47;
row_data_i = 64'h0123_4567_89ab_cdef;
coeff_a_cs = 1'b1;
coeff_a_wren = 1'b1;
coeff_a_addr = 6'd3;
coeff_a_data_i = 32'h1122_3344;
coeff_b_cs = 1'b1;
coeff_b_wren = 1'b1;
coeff_b_addr = 6'd61;
coeff_b_data_i = 32'haabb_ccdd;
@(negedge clk);
row_wren = 1'b0;
coeff_a_wren = 1'b0;
coeff_b_wren = 1'b0;
@(negedge clk);
if (row_data_o !== 64'h0123_4567_89ab_cdef || coeff_a_data_o !== 32'h1122_3344 ||
coeff_b_data_o !== 32'haabb_ccdd) begin
$fatal(1, "JPEG SRAM wrapper data mismatch");
end
row_cs = 1'b0;
coeff_a_cs = 1'b0;
coeff_b_cs = 1'b0;
$display("JPEG SRAM workspace wrapper test passed");
$finish;
end
endmodule
+8
View File
@@ -116,3 +116,11 @@ def test_clock_reset_domain_inventory_rejects_unknown_domain_and_instance(tmp_pa
result = check(write_invalid_map(tmp_path, document))
assert result.returncode != 0
assert "STA source references unknown domain" in result.stderr
def test_clock_reset_domain_inventory_rejects_invalid_sta_net_driver(tmp_path: Path) -> None:
document = json.loads(DOMAIN_MAP.read_text(encoding="utf-8"))
document["domains"][1]["sta"]["net"] = "invalid.net"
result = check(write_invalid_map(tmp_path, document))
assert result.returncode != 0
assert "sta.net must be a SystemVerilog identifier" in result.stderr
+2
View File
@@ -332,6 +332,8 @@ def test_commercial_timing_contract_covers_canonical_domains(tmp_path: Path) ->
assert "u_retrosoc/u_apb4_periph/u_axi4_dvp" in output.read_text(
encoding="utf-8"
)
assert "object_type {net_driver}" in output.read_text(encoding="utf-8")
assert "observation {s_sys_clk}" in output.read_text(encoding="utf-8")
def test_commercial_constraints_do_not_apply_global_io_delays() -> None:
+41 -1
View File
@@ -74,6 +74,35 @@ def test_jpeg_quantize_dequantize(tmp_path: Path) -> None:
assert "JPEG quantizer tests passed" in result.stdout
def test_jpeg_coefficient_engine(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
if iverilog is None or vvp is None:
return
simulation = tmp_path / "jpeg_coefficient_engine_tb"
subprocess.run(
[
iverilog,
"-g2012",
"-DSV_ASSRT_DISABLE",
"-I",
str(ROOT / "rtl/managed/clusterip/common/rtl"),
"-s",
"jpeg_coefficient_engine_tb",
"-o",
str(simulation),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/ip/multimedia/jpeg_transform.sv"),
str(ROOT / "rtl/ip/multimedia/jpeg_quantizer.sv"),
str(ROOT / "rtl/ip/multimedia/jpeg_coefficient_engine.sv"),
str(ROOT / "tests/rtl/jpeg_coefficient_engine_tb.sv"),
],
check=True,
)
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
assert "JPEG coefficient engine test passed" in result.stdout
def test_jpeg_entropy_encoder(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
@@ -226,6 +255,7 @@ def test_jpeg_apb_ring_descriptor_path(tmp_path: Path) -> None:
"jpeg_pkg.sv",
"jpeg_transform.sv",
"jpeg_quantizer.sv",
"jpeg_coefficient_engine.sv",
"jpeg_entropy_encoder.sv",
"jpeg_entropy_decoder.sv",
"jpeg_bit_packer.sv",
@@ -236,6 +266,7 @@ def test_jpeg_apb_ring_descriptor_path(tmp_path: Path) -> None:
"jpeg_marker_parser.sv",
"jpeg_table_store.sv",
"jpeg_table_cache.sv",
"jpeg_table_register_bank.sv",
"jpeg_block_encoder.sv",
"jpeg_block_decoder.sv",
"jpeg_mcu_builder.sv",
@@ -252,6 +283,7 @@ def test_jpeg_apb_ring_descriptor_path(tmp_path: Path) -> None:
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl'}",
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl/interface'}",
f"+incdir+{multimedia}",
"+define+SV_ASSRT_DISABLE",
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/apb4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_stream_if.sv"),
@@ -544,6 +576,7 @@ def test_jpeg_mcu_reconstructor(tmp_path: Path) -> None:
"\n".join(
[
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl'}",
"+define+SV_ASSRT_DISABLE",
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_stream_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/ip/multimedia/jpeg_mcu_reconstructor.sv"),
@@ -595,6 +628,7 @@ def test_jpeg_mcu_builder(tmp_path: Path) -> None:
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl'}",
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_stream_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "rtl/ip/multimedia/jpeg_mcu_builder.sv"),
str(ROOT / "tests/rtl/jpeg_mcu_builder_tb.sv"),
"",
@@ -790,11 +824,14 @@ def test_jpeg_encode_core_complete_file(tmp_path: Path) -> None:
"\n".join(
[
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl'}",
"+define+SV_ASSRT_DISABLE",
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_stream_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/spill_register.sv"),
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "rtl/ip/multimedia/jpeg_transform.sv"),
str(ROOT / "rtl/ip/multimedia/jpeg_quantizer.sv"),
str(ROOT / "rtl/ip/multimedia/jpeg_coefficient_engine.sv"),
str(ROOT / "rtl/ip/multimedia/jpeg_entropy_encoder.sv"),
str(ROOT / "rtl/ip/multimedia/jpeg_block_encoder.sv"),
str(ROOT / "rtl/ip/multimedia/jpeg_bit_packer.sv"),
@@ -853,7 +890,7 @@ def test_jpeg_encode_core_complete_file(tmp_path: Path) -> None:
assert match is not None
benchmark_cycles = int(match.group(1))
assert baseline_cycles < benchmark_cycles
assert benchmark_cycles - baseline_cycles < 1_000
assert benchmark_cycles - baseline_cycles <= 550
def test_jpeg_decode_core_complete_file(tmp_path: Path) -> None:
@@ -881,10 +918,12 @@ def test_jpeg_decode_core_complete_file(tmp_path: Path) -> None:
ROOT / "rtl/ip/multimedia/jpeg_marker_parser.sv",
ROOT / "rtl/ip/multimedia/jpeg_table_store.sv",
ROOT / "rtl/ip/multimedia/jpeg_table_cache.sv",
ROOT / "rtl/ip/multimedia/jpeg_table_register_bank.sv",
ROOT / "rtl/ip/multimedia/jpeg_bit_reader.sv",
ROOT / "rtl/ip/multimedia/jpeg_entropy_decoder.sv",
ROOT / "rtl/ip/multimedia/jpeg_quantizer.sv",
ROOT / "rtl/ip/multimedia/jpeg_transform.sv",
ROOT / "rtl/ip/multimedia/jpeg_coefficient_engine.sv",
ROOT / "rtl/ip/multimedia/jpeg_block_decoder.sv",
ROOT / "rtl/ip/multimedia/jpeg_mcu_reconstructor.sv",
ROOT / "rtl/ip/multimedia/jpeg_decode_core.sv",
@@ -895,6 +934,7 @@ def test_jpeg_decode_core_complete_file(tmp_path: Path) -> None:
[
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl'}",
"+define+PDK_BEHAV",
"+define+SV_ASSRT_DISABLE",
*(str(source) for source in sources),
"",
]
+24
View File
@@ -3,6 +3,7 @@
from __future__ import annotations
import json
import re
import shutil
import subprocess
import sys
@@ -14,6 +15,7 @@ GENERATOR = ROOT / "rtl/mini/address_map/generate_memory_map.py"
MEMORY_MAP = ROOT / "rtl/mini/address_map/memory_map.json"
USER_EXTENSIONS = ROOT / "rtl/mini/integration/user_extensions.json"
USER_GENERATOR = ROOT / "rtl/mini/integration/generate_user_extensions.py"
BOOTER = ROOT / "crt/src/service/booter.c"
def generate(output_dir: Path, *, sram: str = "NO", sram_size_kib: int = 128) -> None:
@@ -104,6 +106,28 @@ def test_generated_artifacts_share_the_capacity_baseline(tmp_path: Path) -> None
assert "OPIPSRAM (wxa!ri) : ORIGIN = 0x48000000, LENGTH = 0x08000000" in linker
def test_booter_prints_every_public_active_mmio_region() -> None:
document = json.loads(MEMORY_MAP.read_text(encoding="utf-8"))
expected = {
region["symbol"]
for region in document["regions"]
if region["kind"] == "active"
and region.get("public") is True
and region["route"] in {"apb4_periph", "apb4_system"}
}
booter = BOOTER.read_text(encoding="utf-8")
mmio_function = booter.split("static void rs_print_mmio_map(void) {", 1)[1].split(
"\n}", 1
)[0]
actual = set(re.findall(r"RS_SOC_([A-Z0-9_]+)_BASE", mmio_function))
assert actual == expected
assert "DMA(8CH)" in mmio_function
assert "DMA(6CH)" not in mmio_function
assert "RS_SOC_APB4_GA_BASE" not in mmio_function
assert "RS_SOC_APB4_APU_BASE" not in mmio_function
def test_sram_capacity_selects_one_consistent_hardware_software_window(tmp_path: Path) -> None:
for size_kib in (4, 16, 32, 64, 128):
output = tmp_path / str(size_kib)
+36
View File
@@ -9,6 +9,7 @@ SOFTWARE_MAKE = ROOT / "rtl/mini/mk/software.mk"
MGMT_CORE = ROOT / "rtl/mini/top/mgmt_core_wrapper.sv"
MGMT_DEBUG = ROOT / "rtl/mini/top/mgmt_debug_wrapper.sv"
AHBL_TO_AXI4 = ROOT / "rtl/mini/top/ahbl2axi4.sv"
VEXII_CONFIG = ROOT / "scripts/vexiiriscv/GenerateRetroSocHp.scala"
def test_booter_prints_the_fixed_hazard3_specification() -> None:
@@ -67,3 +68,38 @@ def test_booter_prints_the_fixed_hazard3_specification() -> None:
assert "axi4.arlen = 8'd0;" in ahbl_to_axi4
assert "axi4.awlock = `AXI4_LOCK_NORM;" in ahbl_to_axi4
assert "axi4.arlock = `AXI4_LOCK_NORM;" in ahbl_to_axi4
def test_booter_prints_present_hp_core_specification() -> None:
booter = BOOTER.read_text(encoding="utf-8")
vexii_config = VEXII_CONFIG.read_text(encoding="utf-8")
for text in (
"High-Performance-Core Specification:",
"Core: VexiiRiscv(hart 1), dual-issue in-order",
"Base: RV32IMAFDC_Zicbom_Zicntr_Zihpm",
"Mode: M/S/U; Sv32 MMU, 9-bit ASID",
"Protection: 16 PMP regions, 4 KiB granularity",
"L1: separate 16 KiB, 4-way instruction and data caches",
"Maintenance: 64-byte Zicbom CBO; no hardware coherency",
"Bus: native AXI64 I/D + cacheless AXI32 MMIO",
"Embedded RISC-V JTAG, 4 triggers",
):
assert text in booter
assert "rs_sysctrl_get_hp_status(&status)" in booter
assert "!status.present" in booter
for parameter in (
'param.addISA("m", "a", "f", "d", "c", "s", "u", "zicbom", "zicntr", "zihpm")',
"param.asidWidth = 9",
"param.decoders = 2",
"param.lanes = 2",
"param.fetchL1Sets = 64",
"param.fetchL1Ways = 4",
"param.lsuL1Sets = 64",
"param.lsuL1Ways = 4",
"param.pmpParam.pmpSize = 16",
"param.pmpParam.granularity = 4096",
"param.privParam.debugTriggers = 4",
):
assert parameter in vexii_config
+58
View File
@@ -267,6 +267,64 @@ def test_ihp130_wrapper_uses_functional_1024x32_macro(tmp_path: Path) -> None:
assert "RM_IHPSG13_1P_1024x64_c2_bm_bist" not in wrapper
@pytest.mark.parametrize("macro", (False, True))
def test_jpeg_workspace_sram_wrappers(tmp_path: Path, macro: bool) -> None:
verilator = shutil.which("verilator")
if verilator is None:
return
output = tmp_path / ("jpeg_workspace_macro" if macro else "jpeg_workspace_behavioral")
ccache_tmp = tmp_path / ("ccache-jpeg-macro" if macro else "ccache-jpeg-behavioral")
ccache_tmp.mkdir()
command = [
verilator,
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
"tc_sram_jpeg_workspace_tb",
]
if macro:
sram_root = (
ROOT / "physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog"
)
command.extend(
[
"+define+PDK_IHP130",
"+define+HAVE_SRAM_MACRO",
"+define+FUNCTIONAL",
"+define+SYNTHESIS",
str(sram_root / "RM_IHPSG13_1P_core_behavioral_bm_bist.v"),
str(sram_root / "RM_IHPSG13_2P_core_behavioral_ideal.v"),
str(sram_root / "RM_IHPSG13_1P_64x64_c2_bm_bist.v"),
str(sram_root / "RM_IHPSG13_2P_64x32_c2.v"),
]
)
command.extend(
[
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "tests/rtl/tc_sram_jpeg_workspace_tb.sv"),
"-Mdir",
str(tmp_path / ("obj-jpeg-macro" if macro else "obj-jpeg-behavioral")),
"-o",
str(output),
]
)
subprocess.run(
command,
check=True,
text=True,
capture_output=True,
env={
**os.environ,
"CCACHE_DIR": str(ccache_tmp),
"CCACHE_TEMPDIR": str(ccache_tmp),
},
)
result = subprocess.run([output], check=True, text=True, capture_output=True)
assert "JPEG SRAM workspace wrapper test passed" in result.stdout
def test_ics55_wrapper_preserves_active_low_controls_and_byte_masks(tmp_path: Path) -> None:
verilator = shutil.which("verilator")
if verilator is None:
+43 -1
View File
@@ -53,6 +53,7 @@ from scripts.regress import ( # noqa: E402
RTL_COMMANDS,
RTL_LINT_VALUES,
SMOKE_COMMANDS,
behavioral_only,
pdk_pr_commands,
regression_environment,
select_regression,
@@ -1145,6 +1146,45 @@ def test_rtl_regression_excludes_synthesis_and_timing() -> None:
assert "STA=OPENSTA" not in dry_run.stdout
def test_behavioral_only_excludes_synthesis_and_netlist_consumers() -> None:
commands = behavioral_only(PR_COMMANDS)
values = [value for _, command_values in commands for value in command_values]
assert commands == RTL_COMMANDS
assert "synth" not in values
assert "sta" not in values
assert "netsim" not in values
dry_run = run(
sys.executable,
str(ROOT / "scripts/regress.py"),
"--root",
str(ROOT),
"--suite",
"pr",
"--pdk",
"GF180",
"--behavioral-only",
"--dry-run",
)
assert "SIMU=VERILATOR" in dry_run.stdout
assert "SIMU=IVERILOG" in dry_run.stdout
assert "SYNTH=YOSYS" not in dry_run.stdout
assert "STA=OPENSTA" not in dry_run.stdout
assert "netsim" not in dry_run.stdout
nightly_extra = behavioral_only(NIGHTLY_EXTRA_COMMANDS)
assert nightly_extra == NIGHTLY_EXTRA_COMMANDS[:1]
def test_hosted_regression_keeps_tools_but_skips_synthesis_execution() -> None:
workflow = (ROOT / ".github/workflows/_regression.yml").read_text(encoding="utf-8")
assert "default: verilator sv2v iverilog yosys opensta openocd riscv_gnu" in workflow
assert "SIMU=IVERILOG SYNTH=YOSYS STA=NONE doctor" in workflow
assert "--behavioral-only" in workflow
def test_pdk_pr_regressions_cover_firmware_rtl_and_selected_netlist_target() -> None:
assert set(PDK_PR_PROFILES) == {"GF180", "IHP130", "ICS55", "SKY130"}
for pdk, profile in PDK_PR_PROFILES.items():
@@ -1312,7 +1352,9 @@ def test_nightly_workflow_splits_netsim_from_extended_recipes() -> None:
assert "suite: nightly-extra" in nightly
assert "timeout_minutes: 180" in nightly
assert "suite: nightly\n" not in nightly
assert "--suite nightly-extra --pdk IHP130 --dry-run" in quality
assert (
"--suite nightly-extra --pdk IHP130 --behavioral-only --dry-run" in quality
)
def test_regression_observations_do_not_block_or_skip_metrics(
+8
View File
@@ -173,3 +173,11 @@ def test_ci_software_covers_sdram_mapped_window() -> None:
assert "RS_SOC_SDRAM_END - RS_CI_SMOKE_SDRAM_SPAN" in smoke
assert "RS_SOC_SDRAM_BASE" in hello
assert "WAIT_SDRAM_READY" in hello
def test_ci_fabric_monitor_tracks_the_active_firmware_memory() -> None:
smoke = (ROOT / "app/apps/ci_smoke/main.c").read_text(encoding="utf-8")
assert "(RS_SOC_HAS_SRAM != 0U)" in smoke
assert "? RS_FABRIC_TARGET_SRAM : RS_FABRIC_TARGET_SDRAM" in smoke
assert "rs_fabric_monitor_read_target(active_memory_target, &target)" in smoke
+14 -1
View File
@@ -59,13 +59,26 @@ def test_core_sdc_covers_current_clock_domains(tmp_path: Path) -> None:
assert "create_clock -name clk_dvp -period 41.666666667" in sdc
assert "create_clock -name clk_usb2_ulpi -period 16.666666667" in sdc
assert "get_pins -quiet" in sdc
assert "get_nets -quiet" in sdc
assert "get_ports -quiet" in sdc
assert (
"set clk_lp_pin [require_net_driver_pin \"clock lp\" {s_sys_clk}]" in sdc
)
assert (
"set clk_hp_pin [require_net_driver_pin \"clock hp\" {s_hp_clk}]" in sdc
)
assert (
"set clk_pclk_pin [require_net_driver_pin \"clock pclk\" {s_pclk}]" in sdc
)
assert (
"set clk_memory_pin [require_net_driver_pin \"clock memory\" {s_mem_clk}]" in sdc
)
assert "set clk_jtag_port [require_ports \"clock jtag\" {jtag_tck_i_pad}]" in sdc
assert (
"set clk_usb2_ulpi_port [require_ports \"clock usb2_ulpi\" "
"{usb2_ulpi_clk_i_pad}]"
) in sdc
assert "u_retrosoc.u_apb4_periph.u_axi4_dvp/u_dvp_pclk_clk_buf" in sdc
assert "u_retrosoc.u_apb4_periph.u_axi4_dvp.u_dvp_pclk_clk_buf/clk_o" in sdc
assert "-group [get_clocks {clk_lp clk_pclk}]" in sdc
assert "set_clock_transition 0.1 [get_clocks {clk_dvp}]" in sdc
assert "set_input_transition" not in sdc