feat: enhance GA2D RTL and Test Infrastructure

- Updated resource_controller_tb.sv to include additional checks for IRQ handling and lifecycle states for GA2D resource.
- Modified soc_irq_topology_tb.sv to accommodate 64-bit IRQ vector handling and updated expectations for IRQ routing.
- Introduced test_ga2d.py for directed verification of GA2D Phase 3 APB4 shell functionality.
- Revised test_ga2d_platform.py to reflect changes in GA2D integration and ensure proper IRQ routing.
- Added test_ga2d_register_parity.py to synchronize GA2D RTL and SDK register constants.
- Updated test_lp_irq.py to reflect changes in IRQ allocation and metadata for GA2D.
- Enhanced test_memory_map.py to verify GA2D MMIO region presence in the memory map.
- Adjusted test_publication_software.py to include GA2D in application results.
- Modified test_soc_topology.py to ensure correct topology generation for GA2D, including IRQ and APB bindings.
This commit is contained in:
maksyuki committed 2026-09-14 09:49:08 +08:00
1 parent e77d7b3299
commit 9a3bf4a6ca
51 files changed
+3573 -172

No files matched your search

+3 -1
View File
@@ -42,7 +42,9 @@ under the [Mulan Permissive Software License, Version 2](LICENSE).
[bidirectional PS/2](docs/ip/ps2.md),
WS2812, SPI/QSPI, SDIO, PSRAM/[OPI-PSRAM](docs/ip/opipsram.md), SDRAM, DMA, LCD, RTC, an
independent-clock window watchdog, CRC, and a
management-only RNG entropy controller. The current deterministic RNG integration source is
management-only RNG entropy controller. The P3 [GA2D](docs/ip/ga2d.md) APB4 shell and
ownership-routed IRQ are integrated, while its 2D DMA and pixel operations remain unavailable.
The current deterministic RNG integration source is
explicitly unqualified and intended only for diagnostics until a PDK-qualified entropy source is integrated.
support. Available interfaces depend on the selected SoC configuration.
- A standalone RISC-V runtime, HAL, board support, middleware, and `benchmark`, `bringup`,
+58 -6
View File
@@ -3,11 +3,13 @@
#include <retrosoc/core/irq.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/arch/riscv/system_base.h>
#include <retrosoc/generated/irq_metadata.h>
#include <retrosoc/hal/apu.h>
#include <retrosoc/hal/clint.h>
#include <retrosoc/hal/crypto.h>
#include <retrosoc/hal/extension.h>
#include <retrosoc/hal/fabric_monitor.h>
#include <retrosoc/hal/ga2d.h>
#include <retrosoc/hal/gpio.h>
#include <retrosoc/hal/onchip_sram.h>
#include <retrosoc/hal/rng.h>
@@ -34,6 +36,7 @@ static volatile uint32_t rs_ci_smoke_external_irq_sequence[4];
static volatile uint32_t rs_ci_smoke_external_irq_sequence_count;
static volatile uint32_t rs_ci_smoke_timer_irq_count;
static volatile uint32_t rs_ci_smoke_software_irq_count;
static volatile uint32_t rs_ci_smoke_ga2d_irq_count;
static void rs_ci_smoke_external_irq_handler(uintptr_t mcause, uintptr_t stack_pointer) {
const uint32_t context = (uint32_t)__RV_CSR_READ(CSR_HAZARD3_MEICONTEXT);
@@ -66,6 +69,13 @@ static void rs_ci_smoke_software_irq_handler(uintptr_t mcause, uintptr_t stack_p
++rs_ci_smoke_software_irq_count;
}
static void rs_ci_smoke_ga2d_irq_handler(uintptr_t mcause, uintptr_t stack_pointer) {
(void)mcause;
(void)stack_pointer;
RS_GA2D_REG(RS_GA2D_REG_IRQ_STATE) = RS_GA2D_IRQ_DONE;
++rs_ci_smoke_ga2d_irq_count;
}
static void rs_ci_smoke_force_external_irq(uint32_t id) {
const uint32_t bank = id >> 4U;
const uint32_t mask = UINT32_C(1) << (id & UINT32_C(0xf));
@@ -125,26 +135,27 @@ static bool rs_ci_smoke_external_priority_matrix(void) {
rs_ci_smoke_external_irq_sequence_count = 0U;
__RV_CSR_WRITE(CSR_HAZARD3_MEICONTEXT, UINT32_C(0));
if ((rs_irq_set_external_priority(29U, UINT8_C(0)) != RS_OK) ||
(rs_irq_set_external_priority(30U, UINT8_C(1)) != RS_OK) ||
(rs_irq_set_external_priority(RS_SOC_EXT_IRQ_GA2D, UINT8_C(1)) != RS_OK) ||
(rs_irq_set_external_priority(31U, UINT8_C(2)) != RS_OK) ||
(rs_irq_set_external_priority(32U, UINT8_C(3)) != RS_OK) ||
(rs_irq_enable_external(29U, rs_ci_smoke_external_irq_handler) != RS_OK) ||
(rs_irq_enable_external(30U, rs_ci_smoke_external_irq_handler) != RS_OK) ||
(rs_irq_enable_external(RS_SOC_EXT_IRQ_GA2D, rs_ci_smoke_external_irq_handler) != RS_OK) ||
(rs_irq_enable_external(31U, rs_ci_smoke_external_irq_handler) != RS_OK) ||
(rs_irq_enable_external(32U, rs_ci_smoke_external_irq_handler) != RS_OK)) {
return false;
}
rs_ci_smoke_force_external_irq(29U);
rs_ci_smoke_force_external_irq(30U);
rs_ci_smoke_force_external_irq(RS_SOC_EXT_IRQ_GA2D);
rs_ci_smoke_force_external_irq(31U);
rs_ci_smoke_force_external_irq(32U);
__enable_irq();
passed = rs_ci_smoke_wait_external_count(4U) && (rs_ci_smoke_external_irq_sequence[0] == 32U) &&
(rs_ci_smoke_external_irq_sequence[1] == 31U) &&
(rs_ci_smoke_external_irq_sequence[2] == 30U) &&
(rs_ci_smoke_external_irq_sequence[2] == RS_SOC_EXT_IRQ_GA2D) &&
(rs_ci_smoke_external_irq_sequence[3] == 29U);
__disable_irq();
if ((rs_irq_disable_external(29U) != RS_OK) || (rs_irq_disable_external(30U) != RS_OK) ||
if ((rs_irq_disable_external(29U) != RS_OK) ||
(rs_irq_disable_external(RS_SOC_EXT_IRQ_GA2D) != RS_OK) ||
(rs_irq_disable_external(31U) != RS_OK) || (rs_irq_disable_external(32U) != RS_OK)) {
passed = false;
}
@@ -153,7 +164,8 @@ static bool rs_ci_smoke_external_priority_matrix(void) {
}
static bool rs_ci_smoke_external_irq(void) {
static const uint32_t tested_ordinals[] = {0U, 15U, 16U, 29U, 30U, 31U, 32U, 61U};
static const uint32_t tested_ordinals[] = {0U, 15U, 16U, 29U, RS_SOC_EXT_IRQ_GA2D,
31U, 32U, 61U};
uint64_t now;
uint32_t index;
@@ -255,10 +267,46 @@ static bool rs_ci_smoke_external_irq(void) {
__disable_ext_irq();
return (rs_ci_smoke_timer_irq_count != 0U) && (rs_ci_smoke_software_irq_count != 0U);
}
static bool rs_ci_smoke_ga2d_irq(void) {
rs_ga2d_capability_t capability;
rs_ci_smoke_ga2d_irq_count = 0U;
if ((rs_ga2d_get_capability(&capability) != RS_OK) ||
(capability.features != RS_GA2D_CAPABILITY_P3) ||
(rs_ga2d_irq_clear(RS_GA2D_IRQ_ALL) != RS_OK) ||
(rs_ga2d_irq_enable(RS_GA2D_IRQ_DONE) != RS_OK) ||
(rs_irq_enable_external(RS_SOC_EXT_IRQ_GA2D, rs_ci_smoke_ga2d_irq_handler) != RS_OK)) {
return false;
}
RS_GA2D_REG(RS_GA2D_REG_IRQ_TEST) = RS_GA2D_IRQ_DONE;
__enable_irq();
for (rs_timeout_t timeout = 100000U; (timeout != 0U) && (rs_ci_smoke_ga2d_irq_count < 1U);
--timeout) {
}
if (rs_ci_smoke_ga2d_irq_count != 1U) {
__disable_irq();
(void)rs_irq_disable_external(RS_SOC_EXT_IRQ_GA2D);
__disable_ext_irq();
return false;
}
__disable_irq();
if ((rs_irq_disable_external(RS_SOC_EXT_IRQ_GA2D) != RS_OK) ||
(rs_ga2d_irq_enable(0U) != RS_OK)) {
__disable_ext_irq();
return false;
}
__disable_ext_irq();
return true;
}
#else
static bool rs_ci_smoke_external_irq(void) {
return true;
}
static bool rs_ci_smoke_ga2d_irq(void) {
return true;
}
#endif
static bool rs_ci_smoke_apu(void) {
@@ -624,6 +672,10 @@ int main(void) {
rs_test_finish(RS_TEST_FAILED, 15U);
}
printf("ci_smoke: external irq passed\n");
if (!rs_ci_smoke_ga2d_irq()) {
rs_test_finish(RS_TEST_FAILED, 15U);
}
printf("ci_smoke: GA2D shell IRQ passed\n");
if (!rs_ci_smoke_apu()) {
rs_test_finish(RS_TEST_FAILED, 14U);
}
+6 -2
View File
@@ -59,8 +59,8 @@ See the [extension contract](../docs/ip/extensions.md) and
[legacy user-IP contract](../docs/ip/user-ip.md).
`<retrosoc/hal/resource.h>` owns the central DMA, USB2, SDIO0/1, SPI-SD,
EXT-H, JPEG, and APU owner/lock, lifecycle-request, fault, and HP cache-maintenance
handshake.
EXT-H, JPEG, APU, and GA2D owner/lock, lifecycle-request, fault, and HP
cache-maintenance handshake.
See the [Resource Controller contract](../docs/ip/resource-controller.md).
`<retrosoc/hal/apu.h>` provides APU discovery, ACL and microcode loading,
@@ -69,6 +69,10 @@ abort/reset, interrupt, and first-error access. The P5 surface remains coreless;
MP3, KWS, model loading, and the APU RX route stay unavailable until their
frozen later phases.
`<retrosoc/hal/ga2d.h>` provides GA2D shell discovery, status/error, IRQ,
and bounded abort/reset access. In Phase 3 its private DMA, 2D job, and pixel
operations remain unavailable and return `RS_ENOTSUP`.
`<retrosoc/hal/fabric_monitor.h>` owns root-management access to native AXI64
master/target counters, stable snapshots, warm-flush counts, target isolation,
and the sticky first-fault record. See the
+93
View File
@@ -0,0 +1,93 @@
#ifndef RETROSOC_HAL_GA2D_H
#define RETROSOC_HAL_GA2D_H
#include <stdbool.h>
#include <stdint.h>
#include <retrosoc/core/status.h>
#include <retrosoc/hal/ga2d_regs.h>
typedef enum {
RS_GA2D_OP_FILL = 0,
RS_GA2D_OP_COPY = 1,
RS_GA2D_OP_CONVERT = 2,
RS_GA2D_OP_BLEND = 3,
} rs_ga2d_operation_t;
typedef enum {
RS_GA2D_FORMAT_RGB565 = 0,
RS_GA2D_FORMAT_RGB888 = 1,
RS_GA2D_FORMAT_XRGB8888 = 2,
RS_GA2D_FORMAT_ARGB8888 = 3,
RS_GA2D_FORMAT_A8 = 4,
} rs_ga2d_format_t;
typedef struct {
uintptr_t address;
uint32_t pitch;
rs_ga2d_format_t format;
} rs_ga2d_surface_t;
typedef struct {
rs_ga2d_operation_t operation;
rs_ga2d_surface_t foreground;
rs_ga2d_surface_t background;
rs_ga2d_surface_t destination;
uint16_t width;
uint16_t height;
uint8_t global_alpha;
uint32_t color;
} rs_ga2d_job_t;
typedef struct {
uint32_t version;
uint32_t features;
uint32_t limits;
uint32_t formats;
} rs_ga2d_capability_t;
typedef struct {
bool busy;
bool draining;
bool quiesced;
bool data_ready;
bool done;
bool aborted;
bool error;
bool recovery_required;
uint32_t irq_state;
} rs_ga2d_status_t;
typedef struct {
bool valid;
uint8_t code;
uint8_t stage;
uint8_t axi_response;
uintptr_t address;
} rs_ga2d_error_t;
typedef struct {
uint64_t cycles;
uint64_t read_bytes;
uint64_t write_bytes;
uint64_t read_stalls;
uint64_t write_stalls;
uint64_t pipe_stalls;
uint16_t lines_done;
} rs_ga2d_stats_t;
rs_status_t rs_ga2d_get_capability(rs_ga2d_capability_t *capability);
rs_status_t rs_ga2d_job_validate(const rs_ga2d_job_t *job);
rs_status_t rs_ga2d_configure(const rs_ga2d_job_t *job);
rs_status_t rs_ga2d_start(void);
rs_status_t rs_ga2d_wait(rs_timeout_t timeout);
rs_status_t rs_ga2d_abort_wait(rs_timeout_t timeout);
rs_status_t rs_ga2d_reset(void);
rs_status_t rs_ga2d_get_status(rs_ga2d_status_t *status);
rs_status_t rs_ga2d_get_error(rs_ga2d_error_t *error);
rs_status_t rs_ga2d_get_stats(rs_ga2d_stats_t *stats);
rs_status_t rs_ga2d_irq_enable(uint32_t mask);
rs_status_t rs_ga2d_irq_pending(uint32_t *pending);
rs_status_t rs_ga2d_irq_clear(uint32_t mask);
#endif
+138
View File
@@ -0,0 +1,138 @@
#ifndef RETROSOC_HAL_GA2D_REGS_H
#define RETROSOC_HAL_GA2D_REGS_H
#include <stdint.h>
#include <retrosoc/core/soc.h>
#define RS_GA2D_REG_IP_ID UINT32_C(0x000)
#define RS_GA2D_REG_IP_VERSION UINT32_C(0x004)
#define RS_GA2D_REG_CAPABILITY UINT32_C(0x008)
#define RS_GA2D_REG_LIMITS UINT32_C(0x00C)
#define RS_GA2D_REG_COMMAND UINT32_C(0x010)
#define RS_GA2D_REG_STATUS UINT32_C(0x014)
#define RS_GA2D_REG_IRQ_STATE UINT32_C(0x018)
#define RS_GA2D_REG_IRQ_ENABLE UINT32_C(0x01C)
#define RS_GA2D_REG_IRQ_TEST UINT32_C(0x020)
#define RS_GA2D_REG_ERROR_STATUS UINT32_C(0x024)
#define RS_GA2D_REG_ERROR_ADDRESS UINT32_C(0x028)
#define RS_GA2D_REG_TIMEOUT_CYCLES UINT32_C(0x02C)
#define RS_GA2D_REG_JOB_CONFIG UINT32_C(0x030)
#define RS_GA2D_REG_GLOBAL_ALPHA UINT32_C(0x034)
#define RS_GA2D_REG_COLOR UINT32_C(0x038)
#define RS_GA2D_REG_SIZE UINT32_C(0x03C)
#define RS_GA2D_REG_FG_ADDRESS UINT32_C(0x040)
#define RS_GA2D_REG_FG_PITCH UINT32_C(0x044)
#define RS_GA2D_REG_FG_FORMAT UINT32_C(0x048)
#define RS_GA2D_REG_BG_ADDRESS UINT32_C(0x04C)
#define RS_GA2D_REG_BG_PITCH UINT32_C(0x050)
#define RS_GA2D_REG_BG_FORMAT UINT32_C(0x054)
#define RS_GA2D_REG_DST_ADDRESS UINT32_C(0x058)
#define RS_GA2D_REG_DST_PITCH UINT32_C(0x05C)
#define RS_GA2D_REG_DST_FORMAT UINT32_C(0x060)
#define RS_GA2D_REG_PERF_SNAPSHOT UINT32_C(0x064)
#define RS_GA2D_REG_FORMAT_CAPABILITY UINT32_C(0x068)
#define RS_GA2D_REG_SNAP_CYCLES_LO UINT32_C(0x080)
#define RS_GA2D_REG_SNAP_CYCLES_HI UINT32_C(0x084)
#define RS_GA2D_REG_SNAP_READ_BYTES_LO UINT32_C(0x088)
#define RS_GA2D_REG_SNAP_READ_BYTES_HI UINT32_C(0x08C)
#define RS_GA2D_REG_SNAP_WRITE_BYTES_LO UINT32_C(0x090)
#define RS_GA2D_REG_SNAP_WRITE_BYTES_HI UINT32_C(0x094)
#define RS_GA2D_REG_SNAP_READ_STALL_LO UINT32_C(0x098)
#define RS_GA2D_REG_SNAP_READ_STALL_HI UINT32_C(0x09C)
#define RS_GA2D_REG_SNAP_WRITE_STALL_LO UINT32_C(0x0A0)
#define RS_GA2D_REG_SNAP_WRITE_STALL_HI UINT32_C(0x0A4)
#define RS_GA2D_REG_SNAP_PIPE_STALL_LO UINT32_C(0x0A8)
#define RS_GA2D_REG_SNAP_PIPE_STALL_HI UINT32_C(0x0AC)
#define RS_GA2D_REG_SNAP_LINES_DONE UINT32_C(0x0B0)
#define RS_GA2D_IP_ID_VALUE UINT32_C(0x47413244)
#define RS_GA2D_IP_VERSION_VALUE UINT32_C(0x00010000)
#define RS_GA2D_IP_VERSION_MAJOR_MASK UINT32_C(0xFFFF0000)
#define RS_GA2D_IP_VERSION_MAJOR_1 UINT32_C(0x00010000)
#define RS_GA2D_CAPABILITY_P3 UINT32_C(0x00000040)
#define RS_GA2D_LIMITS_P3 UINT32_C(0x00000000)
#define RS_GA2D_FORMAT_CAPABILITY_P3 UINT32_C(0x00000000)
#define RS_GA2D_TIMEOUT_CYCLES_RESET UINT32_C(0x00100000)
#define RS_GA2D_GLOBAL_ALPHA_RESET UINT32_C(0x000000FF)
#define RS_GA2D_COMMAND_START UINT32_C(0x00000001)
#define RS_GA2D_COMMAND_ABORT UINT32_C(0x00000002)
#define RS_GA2D_COMMAND_SOFT_RESET UINT32_C(0x00000004)
#define RS_GA2D_STATUS_BUSY UINT32_C(0x00000001)
#define RS_GA2D_STATUS_DRAINING UINT32_C(0x00000002)
#define RS_GA2D_STATUS_QUIESCED UINT32_C(0x00000004)
#define RS_GA2D_STATUS_DATA_READY UINT32_C(0x00000008)
#define RS_GA2D_STATUS_DONE UINT32_C(0x00000010)
#define RS_GA2D_STATUS_ABORTED UINT32_C(0x00000020)
#define RS_GA2D_STATUS_ERROR UINT32_C(0x00000040)
#define RS_GA2D_STATUS_RECOVERY_REQUIRED UINT32_C(0x00000080)
#define RS_GA2D_IRQ_DONE UINT32_C(0x00000001)
#define RS_GA2D_IRQ_ERROR UINT32_C(0x00000002)
#define RS_GA2D_IRQ_ABORT_DONE UINT32_C(0x00000004)
#define RS_GA2D_IRQ_ALL UINT32_C(0x00000007)
#define RS_GA2D_ERROR_STATUS_VALID UINT32_C(0x00000001)
#define RS_GA2D_ERROR_STATUS_CODE_SHIFT 1U
#define RS_GA2D_ERROR_STATUS_CODE_MASK UINT32_C(0x000000FE)
#define RS_GA2D_ERROR_STATUS_STAGE_SHIFT 8U
#define RS_GA2D_ERROR_STATUS_STAGE_MASK UINT32_C(0x00000F00)
#define RS_GA2D_ERROR_STATUS_AXI_RESPONSE_SHIFT 12U
#define RS_GA2D_ERROR_STATUS_AXI_RESPONSE_MASK UINT32_C(0x00003000)
#define RS_GA2D_JOB_CONFIG_OPERATION_SHIFT 0U
#define RS_GA2D_JOB_CONFIG_OPERATION_MASK UINT32_C(0x00000003)
#define RS_GA2D_GLOBAL_ALPHA_VALUE_SHIFT 0U
#define RS_GA2D_GLOBAL_ALPHA_VALUE_MASK UINT32_C(0x000000FF)
#define RS_GA2D_SIZE_WIDTH_SHIFT 0U
#define RS_GA2D_SIZE_WIDTH_MASK UINT32_C(0x0000FFFF)
#define RS_GA2D_SIZE_HEIGHT_SHIFT 16U
#define RS_GA2D_SIZE_HEIGHT_MASK UINT32_C(0xFFFF0000)
#define RS_GA2D_FG_FORMAT_VALUE_SHIFT 0U
#define RS_GA2D_BG_FORMAT_VALUE_SHIFT 0U
#define RS_GA2D_DST_FORMAT_VALUE_SHIFT 0U
#define RS_GA2D_FORMAT_VALUE_MASK UINT32_C(0x00000007)
#define RS_GA2D_FORMAT_CAPABILITY_FOREGROUND_SHIFT 0U
#define RS_GA2D_FORMAT_CAPABILITY_BACKGROUND_SHIFT 8U
#define RS_GA2D_FORMAT_CAPABILITY_DESTINATION_SHIFT 16U
#define RS_GA2D_FORMAT_CAPABILITY_MASK UINT32_C(0x0000001F)
#define RS_GA2D_CAPABILITY_FILL UINT32_C(0x00000001)
#define RS_GA2D_CAPABILITY_COPY UINT32_C(0x00000002)
#define RS_GA2D_CAPABILITY_CONVERT UINT32_C(0x00000004)
#define RS_GA2D_CAPABILITY_BLEND UINT32_C(0x00000008)
#define RS_GA2D_CAPABILITY_A8_MASK UINT32_C(0x00000010)
#define RS_GA2D_CAPABILITY_PRIVATE_DMA UINT32_C(0x00000020)
#define RS_GA2D_CAPABILITY_IRQ UINT32_C(0x00000040)
#define RS_GA2D_CAPABILITY_SNAPSHOT UINT32_C(0x00000080)
#define RS_GA2D_CAPABILITY_TWO_DIMENSIONAL_PITCH UINT32_C(0x00000100)
#define RS_GA2D_CAPABILITY_BYTE_EDGES UINT32_C(0x00000200)
#define RS_GA2D_CAPABILITY_INPLACE_BACKGROUND UINT32_C(0x00000400)
#define RS_GA2D_ERROR_CODE_NONE 0U
#define RS_GA2D_ERROR_CODE_INVALID_SIZE 1U
#define RS_GA2D_ERROR_CODE_INVALID_FORMAT 2U
#define RS_GA2D_ERROR_CODE_INVALID_PITCH 3U
#define RS_GA2D_ERROR_CODE_INVALID_ALIGNMENT 4U
#define RS_GA2D_ERROR_CODE_ADDRESS_OVERFLOW 5U
#define RS_GA2D_ERROR_CODE_ADDRESS_RANGE 6U
#define RS_GA2D_ERROR_CODE_OVERLAP 7U
#define RS_GA2D_ERROR_CODE_AXI_READ 8U
#define RS_GA2D_ERROR_CODE_AXI_WRITE 9U
#define RS_GA2D_ERROR_CODE_AXI_PROTOCOL 10U
#define RS_GA2D_ERROR_CODE_TIMEOUT 11U
#define RS_GA2D_ERROR_CODE_EPOCH_LOST 12U
#define RS_GA2D_ERROR_CODE_INTERNAL 13U
#define RS_GA2D_ERROR_STAGE_NONE 0U
#define RS_GA2D_ERROR_STAGE_VALIDATE 1U
#define RS_GA2D_ERROR_STAGE_FOREGROUND 2U
#define RS_GA2D_ERROR_STAGE_BACKGROUND 3U
#define RS_GA2D_ERROR_STAGE_DESTINATION 4U
#define RS_GA2D_ERROR_STAGE_LIFECYCLE 5U
#define RS_GA2D_REG(offset) RS_SOC_REG32(RS_SOC_APB4_GA2D_BASE, (offset))
#endif
+218
View File
@@ -0,0 +1,218 @@
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/hal/ga2d.h>
#if defined(RS_GA2D_TEST_MMIO)
extern volatile uint32_t rs_ga2d_test_mmio[1024];
#undef RS_GA2D_REG
#define RS_GA2D_REG(offset) rs_ga2d_test_mmio[(offset) / 4U]
#endif
static void rs_ga2d_fence(void) {
#if defined(__riscv)
__asm__ volatile("fence rw, rw" ::: "memory");
#else
__asm__ volatile("" ::: "memory");
#endif
}
static uint32_t rs_ga2d_poll_count(rs_timeout_t timeout) {
return (timeout == 0U) ? 1U : timeout;
}
static uint32_t rs_ga2d_bit(uint32_t value, uint32_t mask) {
return ((value & mask) != 0U) ? 1U : 0U;
}
static rs_status_t rs_ga2d_require_shell(void) {
rs_ga2d_capability_t capability;
return rs_ga2d_get_capability(&capability);
}
static rs_status_t rs_ga2d_require_irq(void) {
rs_ga2d_capability_t capability;
rs_status_t status;
status = rs_ga2d_get_capability(&capability);
if (status != RS_OK) {
return status;
}
return ((capability.features & RS_GA2D_CAPABILITY_IRQ) != 0U) ? RS_OK : RS_ENOTSUP;
}
rs_status_t rs_ga2d_get_capability(rs_ga2d_capability_t *capability) {
uint32_t ip_id;
if (capability == NULL) {
return RS_EINVAL;
}
ip_id = RS_GA2D_REG(RS_GA2D_REG_IP_ID);
capability->version = RS_GA2D_REG(RS_GA2D_REG_IP_VERSION);
capability->features = RS_GA2D_REG(RS_GA2D_REG_CAPABILITY);
capability->limits = RS_GA2D_REG(RS_GA2D_REG_LIMITS);
capability->formats = RS_GA2D_REG(RS_GA2D_REG_FORMAT_CAPABILITY);
if ((ip_id != RS_GA2D_IP_ID_VALUE) ||
((capability->version & RS_GA2D_IP_VERSION_MAJOR_MASK) != RS_GA2D_IP_VERSION_MAJOR_1)) {
return RS_ENOTSUP;
}
return RS_OK;
}
rs_status_t rs_ga2d_job_validate(const rs_ga2d_job_t *job) {
if (job == NULL) {
return RS_EINVAL;
}
return RS_ENOTSUP;
}
rs_status_t rs_ga2d_configure(const rs_ga2d_job_t *job) {
if (job == NULL) {
return RS_EINVAL;
}
return RS_ENOTSUP;
}
rs_status_t rs_ga2d_start(void) {
return RS_ENOTSUP;
}
rs_status_t rs_ga2d_wait(rs_timeout_t timeout) {
(void)timeout;
return RS_ENOTSUP;
}
rs_status_t rs_ga2d_abort_wait(rs_timeout_t timeout) {
uint32_t polls;
if (rs_ga2d_require_shell() != RS_OK) {
return RS_ENOTSUP;
}
RS_GA2D_REG(RS_GA2D_REG_COMMAND) = RS_GA2D_COMMAND_ABORT;
polls = rs_ga2d_poll_count(timeout);
while (polls-- != 0U) {
const uint32_t status = RS_GA2D_REG(RS_GA2D_REG_STATUS);
if ((status & (RS_GA2D_STATUS_BUSY | RS_GA2D_STATUS_DRAINING)) == 0U) {
rs_ga2d_fence();
return RS_OK;
}
}
return RS_ETIMEOUT;
}
rs_status_t rs_ga2d_reset(void) {
uint32_t status;
rs_status_t result;
result = rs_ga2d_require_shell();
if (result != RS_OK) {
return result;
}
status = RS_GA2D_REG(RS_GA2D_REG_STATUS);
if ((status & (RS_GA2D_STATUS_BUSY | RS_GA2D_STATUS_DRAINING | RS_GA2D_STATUS_QUIESCED |
RS_GA2D_STATUS_RECOVERY_REQUIRED)) != 0U ||
(rs_ga2d_bit(status, RS_GA2D_STATUS_DATA_READY) == 0U)) {
return RS_EIO;
}
RS_GA2D_REG(RS_GA2D_REG_COMMAND) = RS_GA2D_COMMAND_SOFT_RESET;
return RS_OK;
}
rs_status_t rs_ga2d_get_status(rs_ga2d_status_t *status) {
uint32_t flags;
rs_status_t result;
if (status == NULL) {
return RS_EINVAL;
}
result = rs_ga2d_require_shell();
if (result != RS_OK) {
return result;
}
flags = RS_GA2D_REG(RS_GA2D_REG_STATUS);
status->busy = rs_ga2d_bit(flags, RS_GA2D_STATUS_BUSY) != 0U;
status->draining = rs_ga2d_bit(flags, RS_GA2D_STATUS_DRAINING) != 0U;
status->quiesced = rs_ga2d_bit(flags, RS_GA2D_STATUS_QUIESCED) != 0U;
status->data_ready = rs_ga2d_bit(flags, RS_GA2D_STATUS_DATA_READY) != 0U;
status->done = rs_ga2d_bit(flags, RS_GA2D_STATUS_DONE) != 0U;
status->aborted = rs_ga2d_bit(flags, RS_GA2D_STATUS_ABORTED) != 0U;
status->error = rs_ga2d_bit(flags, RS_GA2D_STATUS_ERROR) != 0U;
status->recovery_required = rs_ga2d_bit(flags, RS_GA2D_STATUS_RECOVERY_REQUIRED) != 0U;
status->irq_state = RS_GA2D_REG(RS_GA2D_REG_IRQ_STATE) & RS_GA2D_IRQ_ALL;
return RS_OK;
}
rs_status_t rs_ga2d_get_error(rs_ga2d_error_t *error) {
uint32_t status;
rs_status_t result;
if (error == NULL) {
return RS_EINVAL;
}
result = rs_ga2d_require_shell();
if (result != RS_OK) {
return result;
}
status = RS_GA2D_REG(RS_GA2D_REG_ERROR_STATUS);
error->valid = rs_ga2d_bit(status, RS_GA2D_ERROR_STATUS_VALID) != 0U;
error->code =
(uint8_t)((status & RS_GA2D_ERROR_STATUS_CODE_MASK) >> RS_GA2D_ERROR_STATUS_CODE_SHIFT);
error->stage =
(uint8_t)((status & RS_GA2D_ERROR_STATUS_STAGE_MASK) >> RS_GA2D_ERROR_STATUS_STAGE_SHIFT);
error->axi_response = (uint8_t)((status & RS_GA2D_ERROR_STATUS_AXI_RESPONSE_MASK) >>
RS_GA2D_ERROR_STATUS_AXI_RESPONSE_SHIFT);
error->address = (uintptr_t)RS_GA2D_REG(RS_GA2D_REG_ERROR_ADDRESS);
return RS_OK;
}
rs_status_t rs_ga2d_get_stats(rs_ga2d_stats_t *stats) {
if (stats == NULL) {
return RS_EINVAL;
}
return RS_ENOTSUP;
}
rs_status_t rs_ga2d_irq_enable(uint32_t mask) {
rs_status_t result;
if ((mask & ~RS_GA2D_IRQ_ALL) != 0U) {
return RS_EINVAL;
}
result = rs_ga2d_require_irq();
if (result != RS_OK) {
return result;
}
RS_GA2D_REG(RS_GA2D_REG_IRQ_ENABLE) = mask;
return RS_OK;
}
rs_status_t rs_ga2d_irq_pending(uint32_t *pending) {
rs_status_t result;
if (pending == NULL) {
return RS_EINVAL;
}
result = rs_ga2d_require_irq();
if (result != RS_OK) {
return result;
}
*pending =
RS_GA2D_REG(RS_GA2D_REG_IRQ_STATE) & RS_GA2D_REG(RS_GA2D_REG_IRQ_ENABLE) & RS_GA2D_IRQ_ALL;
return RS_OK;
}
rs_status_t rs_ga2d_irq_clear(uint32_t mask) {
rs_status_t result;
if ((mask & ~RS_GA2D_IRQ_ALL) != 0U) {
return RS_EINVAL;
}
result = rs_ga2d_require_irq();
if (result != RS_OK) {
return result;
}
RS_GA2D_REG(RS_GA2D_REG_IRQ_STATE) = mask;
return RS_OK;
}
+2
View File
@@ -115,6 +115,8 @@ static void rs_print_mmio_map(void) {
(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 GA2D SHELL @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_GA2D_BASE);
printf(" 1 x GPIO ADMIN @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_GPIO_ADMIN_BASE);
printf(" 1 x SDIO1 @%p\n",
+9 -6
View File
@@ -3,16 +3,19 @@
Mini uses two cooperating AXI fabrics. `axi4_interconnect` is the 32-bit LP
control/compatibility plane; `axi4_data_crossbar` is the 64-bit HP payload
plane. Common `axi4_if`, `memory_map.json`, and `soc_topology.json` are the
executable protocol, address, and integration sources of truth.
executable protocol, address, and integration sources of truth. GA2D Phase 3
activates the PCLK-only `APB4_GA2D` shell and its resource-owned interrupt;
the dedicated master-8 AXI bridge remains an idle source until a later phase
implements payload/DMA traffic.
[GA2D Phase 2](ip/ga2d.md#phase-2---expand-axi64-fabric-and-resource-integration)
expands the fabric to nine native masters and seven-bit global IDs. It adds
master 8 and preserves all existing identities, target policy, and timeout
behavior. Master 8 is fed only by its dedicated PCLK-to-HP AXI64/ID3 bridge,
whose source is idle. `APB4_GA` remains reserved and inactive; the GA2D APB
shell, IRQ, payload/DMA, and pixel function remain deferred. The GA2D
specification also records the existing master-6/JPEG admission discrepancy;
it must not be interpreted as a free port.
whose source remains idle. Phase 3 activates the separate PCLK `APB4_GA2D`
shell and resource-owned IRQ, while payload/DMA, pixel work, and START remain
deferred. The GA2D specification also records the existing master-6/JPEG
admission discrepancy; it must not be interpreted as a free port.
## LP control plane
@@ -74,7 +77,7 @@ the integration RTL:
| HP I-cache | all five memories | none | allowed | cache attributes preserved |
| HP D-cache | all five memories | SRAM, SDRAM, QPI, OPI | denied | cache attributes preserved |
| DMA, I/O A/B, LP gateway | all five memories | SRAM, SDRAM, QPI, OPI | denied | `AxCACHE=0` required |
| GA2D bridge placeholder | all five memories | SRAM, SDRAM, QPI, OPI | denied | `AxCACHE=0` required; source is idle in P2 |
| GA2D bridge placeholder | all five memories | SRAM, SDRAM, QPI, OPI | denied | `AxCACHE=0` required; source remains idle in P3 |
| JPEG | none | none | denied | zero normal admission credit (`GA2D-GAP-01`) |
| EXT-H | all five memories within slot ACL | SRAM, SDRAM, QPI, OPI within slot ACL | denied | `AxCACHE=0` required |
+6 -5
View File
@@ -23,11 +23,12 @@ Mini remains a small, non-coherent, performance-first SoC. Hazard3 is the
only root-management master. Vexii I/D, DMA, I/O gateways, the LP data gateway,
EXT-H, and an idle GA2D bridge placeholder use a nine-master native AXI64 data
plane with seven-bit global IDs. The placeholder is a dedicated
PCLK-to-HP AXI64/ID3 bridge only: `APB4_GA` remains reserved and inactive, and
the GA2D APB shell, IRQ, payload/DMA, and pixel function remain deferred. APB
remains a separate control plane. Every data initiator has a generated fixed
identity, target permissions, execute/cache-attribute rules, outstanding
limit, QoS class, and fault attribution.
PCLK-to-HP AXI64/ID3 bridge whose source remains idle. Phase 3 activates a
separate PCLK `APB4_GA2D` register shell and resource-owned IRQ route, but
payload/DMA and pixel functions remain deferred. APB remains a separate control
plane. Every data initiator has a generated fixed identity, target permissions,
execute/cache-attribute rules, outstanding limit, QoS class, and fault
attribution.
The design combines the references at their smallest useful boundary:
+2 -1
View File
@@ -5,7 +5,8 @@ adds master bank 8, version 1.1, and master bit 3 at `FAULT[12]`, preserving
the existing packed fields. It also carries the complete four-bit master
identity through SYSCTRL. Bank 8 observes the dedicated PCLK-to-HP
AXI64/ID3 GA2D bridge placeholder, whose source is idle; it does not imply an
implemented GA2D APB shell, IRQ, payload/DMA, or pixel function.
implemented GA2D payload/DMA or pixel function. Phase 3 adds an `APB4_GA2D`
register shell and resource-owned IRQ without changing the idle bridge source.
## Scope
+10 -9
View File
@@ -3,8 +3,9 @@
[GA2D Phase 2](ga2d.md#phase-2---expand-axi64-fabric-and-resource-integration)
adds resource 8 and version 1.1. It covers only the dedicated PCLK-to-HP
AXI64/ID3 GA2D bridge placeholder, whose source is idle. LP vector 32 / HP
PLIC source 11 and GA2D APB-shell wiring remain Phase 3 work; `APB4_GA`
therefore remains reserved and inactive. Its source-stop/drain-before-block
PLIC source 11 and `APB4_GA2D` shell wiring are implemented in Phase 3. The
shell has IRQ-only capability; it does not create GA2D payload/DMA or pixel
work, and the bridge source remains idle. Its source-stop/drain-before-block
sequence is specified there. Existing resources and register fields are not
renumbered or reinterpreted.
@@ -29,13 +30,13 @@ Resource indices are fixed:
| 5 | EXT-H | 3 |
| 6 | JPEG | 9 |
| 7 | APU | 10 |
| 8 | GA2D bridge placeholder (idle) | 11, not routed until Phase 3 |
| 8 | GA2D APB shell and idle bridge placeholder | 11 |
For resources 0 through 7, owner `0` routes the resource interrupt to the
existing LP vector. Owner `1` removes it from LP and routes it to the listed HP
PLIC source. Resource 8 has a fixed-inactive raw IRQ and no LP or HP IRQ route
until Phase 3. Reset masks both applicable routes. Hardware never delivers one
resource interrupt to both owners.
PLIC source. Resource 8 routes the GA2D shell raw IRQ to LP vector 32/external
ordinal 30 for owner `0`, or HP PLIC source 11 for owner `1`. Reset masks both
routes. Hardware never delivers one resource interrupt to both owners.
## Register ABI
@@ -90,9 +91,9 @@ and a Linux platform driver remain software responsibilities.
## Delivery Boundary
Central owner/lock, quiesce-gated handoff, cache request/ACK, fault IRQ, and
LP/HP IRQ routing are implemented and directed-tested. P2 appends resource 8
for the idle GA2D bridge placeholder only; it creates no GA2D IRQ route or
functional GA2D peripheral. `CONTROL.QUIESCE` blocks the corresponding
LP/HP IRQ routing are implemented and directed-tested. Phase 3 adds the GA2D
PCLK shell raw IRQ to resource 8, while its AXI bridge remains idle and it does
not provide a functional GA2D engine. `CONTROL.QUIESCE` blocks the corresponding
data-crossbar master and waits per-master outstanding zero; shared I/O gateways
are conservatively blocked as a pair. `CONTROL.RESET` also blocks new data and
masks IRQ but is not yet connected to every peripheral engine's internal reset
+1 -1
View File
@@ -63,7 +63,7 @@ offset macros.
| `0x01C` | `PLL_STATUS` | RO | Active profile, valid, busy, error/error reason, safe clock, lock, and capability. |
| `0x020` | `USER_CORE_RESET` | RO/unsupported | Product reads all ones and rejects writes. MPW retains its reset mask. |
| `0x024` | `USER_CORE_STATUS` | RO/unsupported | Product reports present=0, idle=1, and sticky unsupported-write error. |
| `0x028` | `FAULT_MASTER` | RO | First fault master, a four-bit AXI master ID (0..8); HP is 7 and the P2 GA2D bridge placeholder is 8. The offset is unchanged. |
| `0x028` | `FAULT_MASTER` | RO | First fault master, a four-bit AXI master ID (0..8); HP is 7 and the P3 GA2D shell's idle bridge placeholder is 8. The offset is unchanged. |
| `0x02C` | `FAULT_DETAIL` | RO | First raw RIB response code. |
| `0x040` | `PERF_CTRL` | RW | Bit 0 enable, bit 1 clear pulse, bit 2 snapshot pulse. |
| `0x044`-`0x098` | `PERF_*_WAIT_{LO,HI}` | RO | Snapshot of management, user, central DMA, SDIO0, SDIO1, APB4, SDRAM, PSRAM, and flash wait counters. |
+12 -9
View File
@@ -3,9 +3,10 @@
The frozen [GA2D specification](ip/ga2d.md) defines the phased GA2D delivery.
Phase 2 expands the implemented platform to nine AXI64 masters with seven-bit
global IDs and Resource Controller entry 8. Its dedicated PCLK-to-HP
AXI64/ID3 bridge is an idle placeholder only. `APB4_GA` remains reserved and
inactive, and the GA2D APB shell, IRQ, payload/DMA, and pixel function remain
deferred. Existing hart, memory, and source identities remain fixed.
AXI64/ID3 bridge remains an idle placeholder. Phase 3 activates
`APB4_GA2D` as a PCLK register shell and connects its resource-owned IRQ, but
payload/DMA, pixel function, and START remain deferred. Existing hart, memory,
and source identities remain fixed.
## Product contract
@@ -94,6 +95,7 @@ and a Common FIFO preserves write-data order where AXI4 W has no ID.
| I/O gateway A | USB2 and SDIO0, then PCLK-to-HP CDC and upsizer |
| I/O gateway B | SDIO1 and SPI-SD, then PCLK-to-HP CDC and upsizer |
| LP data gateway | Hazard3 memory traffic, LP-to-HP CDC and upsizer |
| JPEG | PCLK-to-HP AXI64 async bridge, ID prefix 6; zero normal admission credit (`GA2D-GAP-01`) |
| EXT-H | PCLK-to-HP AXI64 async bridge, ID prefix 7 |
| GA2D bridge placeholder | dedicated PCLK-to-HP AXI64/ID3 async bridge; source held idle |
@@ -154,12 +156,13 @@ Its IRQ is delivered to LP IRQ 28 or HP PLIC source 3 according to owner,
never both. Software discovers it through `<retrosoc/hal/extension.h>`.
The Resource Controller at `0x2000_A000` is the central owner and IRQ authority
for DMA, USB2, SDIO0/1, SPI-SD, EXT-H, JPEG, and APU, and the lifecycle
authority for the idle GA2D bridge placeholder. Resource 8 has no routed IRQ
until Phase 3. Handoff requires idle,
owner lock is sticky, and rejected handoffs raise LP IRQ 29. APU index 7 routes
exclusively to LP IRQ31 or HP PLIC source10. The controller also carries the
AON cache request/clean acknowledgement used before HP drain. See
for DMA, USB2, SDIO0/1, SPI-SD, EXT-H, JPEG, APU, and the P3 GA2D shell. Resource
8 routes its raw IRQ exclusively to LP vector 32/external ordinal 30 or HP PLIC
source 11 according to owner, while the associated AXI bridge remains idle.
Handoff requires idle, owner lock is sticky, and rejected handoffs raise LP IRQ
29. APU index 7 routes exclusively to LP IRQ31 or HP PLIC source10. The
controller also carries the AON cache request/clean acknowledgement used before
HP drain. See
[`ip/resource-controller.md`](ip/resource-controller.md).
Resource 8 qualifies its HP block acknowledgement with a fresh synchronized
source-quiesced state, so an old idle sample cannot hand off a
+3 -2
View File
@@ -145,8 +145,9 @@ capabilities:
with seven-bit global IDs. Vexii I/D ports remain independent; DMA and I/O
masters cross into HP and current memory frontends are reached through
64-to-32 target adapters. Master 8 is a dedicated PCLK-to-HP AXI64/ID3 GA2D
bridge whose source is idle; `APB4_GA` remains reserved and inactive, and no
GA2D APB shell, IRQ, payload/DMA, or pixel function is implemented.
bridge whose source is idle. `APB4_GA2D` is an active PCLK register shell
with resource-owned IRQ routing, while GA2D payload/DMA and pixel functions
remain unavailable.
- Current external-memory targets serialize accepted AXI4 bursts into ordered
scalar engine accesses. They do not yet combine a burst into a native SDRAM,
PSRAM, flash, or SPI-SD physical transaction.
@@ -8131,7 +8131,7 @@
"from": "resources.p1",
"to": "core.p1",
"kind": "control",
"label": "IDLE / 8",
"label": "IDLE / 9",
"style": "direct",
"bindings": [
{
@@ -8159,7 +8159,7 @@
"from": "core.p2",
"to": "owners.p2",
"kind": "control",
"label": "OWNER / 16",
"label": "OWNER / 18",
"style": "direct",
"bindings": [
{
@@ -8187,7 +8187,7 @@
"from": "core.p3",
"to": "irq.p3",
"kind": "control",
"label": "LP IRQ / 8",
"label": "LP IRQ / 9",
"style": "direct",
"bindings": [
{
@@ -8213,7 +8213,7 @@
"zigzag-dir": "horizontal"
}
],
"note": "Nine resources retain independent owner/lock/quiesce/reset state; resource 8 is the idle GA2D bridge placeholder only. IRQ steering and cache-clean coordination are control relationships, not a hardware cache-coherence implementation."
"note": "Nine resources retain independent owner/lock/quiesce/reset state; resource 8 routes the P3 GA2D shell IRQ while its AXI bridge stays idle. IRQ steering and cache-clean coordination are control relationships, not a hardware cache-coherence implementation."
},
"monitor": {
"nodes": [
+2 -2
View File
@@ -150,12 +150,12 @@
"Lifecycle-gated device identity reads."
],
"resource": [
"Nine independently managed shared resources; resource 8 is the idle GA2D bridge placeholder only.",
"Nine independently managed shared resources; resource 8 owns the P3 GA2D shell IRQ and idle bridge placeholder.",
"Owner locks, quiesce/reset and LP/HP IRQ routing.",
"Explicit cache-maintenance handoff and sticky faults."
],
"monitor": [
"Nine master and six target observation banks; bank 8 observes the idle GA2D bridge placeholder.",
"Nine master and six target observation banks; bank 8 observes the idle GA2D bridge behind the P3 shell.",
"Saturating counters, snapshots and outstanding high-water marks.",
"Sticky first-fault attribution and live isolation status."
],
+18 -4
View File
@@ -333,7 +333,7 @@
"sources": [
"docs/ip/resource-controller.md"
],
"summary": "LP-controlled ownership, quiesce, reset and LP/HP IRQ routing for nine resources; resource 8 is the idle GA2D bridge placeholder only."
"summary": "LP-controlled ownership, quiesce, reset and LP/HP IRQ routing for nine resources; resource 8 owns the P3 GA2D shell IRQ and an idle AXI bridge placeholder."
},
{
"id": "monitor",
@@ -344,7 +344,7 @@
"sources": [
"docs/ip/fabric-monitor.md"
],
"summary": "Nine-master/six-target snapshot counters and sticky fault attribution beside the HP data crossbar; bank 8 observes the idle GA2D bridge placeholder."
"summary": "Nine-master/six-target snapshot counters and sticky fault attribution beside the HP data crossbar; bank 8 observes the idle GA2D bridge behind the P3 shell."
},
{
"id": "i2s",
@@ -476,16 +476,30 @@
],
"summary": "MPW selectable user-IP window; PRODUCT exposes a compatibility responder."
},
{
"id": "ga2d",
"regions": [
"APB4_GA2D"
],
"irqs": [
"ga2d"
],
"sources": [
"docs/ip/ga2d.md",
"rtl/ip/multimedia/apb4_ga2d.sv",
"rtl/ip/multimedia/ga2d_reg.sv"
],
"summary": "P3 APB4 register shell with resource-owned IRQ; START, 2D DMA, and pixel operations remain unavailable."
},
{
"id": "reserved",
"regions": [
"APB4_GA",
"SPISD"
],
"irqs": [],
"sources": [
"rtl/mini/address_map/memory_map.json"
],
"summary": "Reserved address ranges; no integrated graphics accelerator or card data aperture."
"summary": "Reserved card data aperture."
}
]
+6 -2
View File
@@ -1,6 +1,6 @@
#import "../style.typ": *
#import "../ip-reference.typ": ip-reference
#import "../ip-reference.typ": ip-reference, register-section
#pagebreak(weak:true)
@@ -12,10 +12,14 @@
#ip-reference("resource","resource",4,legacy:[
Nine resources are managed: central DMA, USB2, SDIO0, SDIO1, SPI-SD, EXT-H, JPEG,
APU, and the idle GA2D bridge placeholder. Resource 8 has no routed IRQ in Phase 2.
APU, and the P3 GA2D shell with its idle AXI bridge placeholder. Resource 8 routes
the shell IRQ according to its current owner; the shell does not submit GA2D work.
Ownership changes require quiesce, completion of accepted traffic and cache-maintenance
handoff. HP can inspect allowed status but cannot take over root writes. An ownership grant
does not bypass the target access policy or EXT-H address bounds.
])
#heading(level:5,numbering:none,outlined:false,bookmarked:false)[GA2D Phase 3 Register Shell] <ga2d>
#register-section("ga2d",4)
#context metadata((kind:"ip-end",id:"resource",page:here().page()))
+2 -1
View File
@@ -76,7 +76,8 @@
"I2S",
"DVP",
"JPEG",
"APU"
"APU",
"GA2D"
]
},
{
File diff suppressed because it is too large. Load diff
+28 -4
View File
@@ -962,6 +962,30 @@
"rtl/ip/multimedia/apu_transform_engine.sv"
]
},
"ga2d": {
"document": "docs/ip/ga2d.md",
"defines": [
"rtl/ip/multimedia/ga2d_define.svh"
],
"rtl": [
"rtl/ip/multimedia/ga2d_reg.sv"
],
"parameters": {},
"groups": [
{
"id": "main",
"title": "Phase 3 register shell",
"pattern": "APB4_GA2D__.+",
"strip": "^APB4_GA2D__",
"base": 0,
"stride": 0,
"count": 1
}
],
"support": [
"rtl/ip/multimedia/apb4_ga2d.sv"
]
},
"dvp": {
"document": "docs/ip/dvp.md",
"defines": [
@@ -1746,7 +1770,7 @@
"parameters": {
"NumMasters": 8,
"NumTargets": 6,
"ResourceCount": 8
"ResourceCount": 9
},
"groups": [
{
@@ -1765,7 +1789,7 @@
"strip": "^",
"base": 256,
"stride": 32,
"count": 8
"count": 9
}
],
"support": []
@@ -1777,7 +1801,7 @@
"rtl/mini/top/fabric_monitor.sv"
],
"parameters": {
"NumMasters": 8,
"NumMasters": 9,
"NumTargets": 6,
"ResourceCount": 8
},
@@ -1798,7 +1822,7 @@
"strip": "^",
"base": 256,
"stride": 32,
"count": 8
"count": 9
},
{
"id": "target",
+26 -3
View File
@@ -590,7 +590,7 @@
"id": "resource",
"title": "Resource Controller",
"implementation": "Partial",
"scope": "Nine independently managed shared resources; resource 8 is the idle GA2D bridge placeholder only.",
"scope": "Nine independently managed shared resources; resource 8 owns the P3 GA2D shell IRQ and an idle AXI bridge placeholder.",
"dependencies": "No additional off-chip component implied; use the selected clock/reset and access policy.",
"hal": "Published API and syntax example; see the IP chapter.",
"linux": "Native driver support not established in this publication.",
@@ -610,7 +610,7 @@
"id": "monitor",
"title": "Fabric Monitor",
"implementation": "Integrated",
"scope": "Nine master and six target observation banks; bank 8 observes the idle GA2D bridge placeholder.",
"scope": "Nine master and six target observation banks; bank 8 observes the idle GA2D bridge behind the P3 shell.",
"dependencies": "No additional off-chip component implied; use the selected clock/reset and access policy.",
"hal": "Published API and syntax example; see the IP chapter.",
"linux": "Native driver support not established in this publication.",
@@ -3625,6 +3625,20 @@
"rtl/ip/multimedia/apb4_apu.sv"
]
},
{
"id": "apb4_ga2d",
"chapter": "resource",
"family": "ga2d",
"mode": "PRODUCT",
"region": "APB4_GA2D",
"catalog": "ga2d",
"qualification": "Phase 3 exposes only the GA2D APB4 shell and its resource-owned IRQ route. START, private DMA, and pixel operations remain unavailable.",
"sources": [
"docs/ip/ga2d.md",
"rtl/ip/multimedia/ga2d_reg.sv",
"rtl/ip/multimedia/apb4_ga2d.sv"
]
},
{
"id": "apb4_crypto",
"chapter": "crypto",
@@ -5307,7 +5321,16 @@
"code": 15,
"trigger": "rs_ci_smoke_external_irq(",
"coverage": "Exercise Xh3irq ordinals 0/15/16/29/30/31/32/61, invalid 62, priority/masking, no-handler masking, timer/software coexistence, and handler entry/return.",
"boundary": "Only the CSR-enabled build exercises dispatch; no peripheral GA2D source is wired."
"boundary": "Only the CSR-enabled build exercises dispatch; the physical P3 GA2D shell route is covered by the following stage."
},
{
"id": "ga2d-irq",
"title": "GA2D shell IRQ route",
"kind": "test-fail",
"code": 15,
"trigger": "rs_ci_smoke_ga2d_irq(",
"coverage": "Discover the P3 shell and use IRQ_TEST to exercise its resource-owned LP external route.",
"boundary": "Does not submit a GA2D datapath job or claim private DMA or pixel-operation availability."
},
{
"id": "apu",
+7 -6
View File
@@ -119,12 +119,13 @@ ownership. Its implemented limits, handwritten ABI, measured performance, and
commercial release gates are documented in
[Baseline JPEG Codec](../docs/ip/jpeg.md).
The Mini GA2D graphics accelerator has a frozen architecture and ABI, with
implementation pending. Its approved phases expand the LP interrupt platform
and native AXI64 fabric before activating the former GA reservation as an
APB4-controlled fill/copy/convert/alpha engine with private 2D DMA. The exact
allocations, Common reuse limits, lifecycle, HAL, and evidence gates are in
[GA2D](../docs/ip/ga2d.md). The freeze itself changes no RTL or filelists.
The Mini GA2D graphics accelerator has a frozen architecture and ABI. Phase 3
activates `APB4_GA2D` as a PCLK register shell with ownership-routed IRQ
support; it does not implement START, 2D DMA, pixel operations, or AXI payload
traffic. Its approved phases first expanded the LP interrupt platform and
native AXI64 fabric, then activated the former GA reservation before later
fill/copy/convert/alpha engine work. The exact allocations, Common reuse
limits, lifecycle, HAL, and evidence gates are in [GA2D](../docs/ip/ga2d.md).
The Mini Audio Processing Unit has a frozen coreless architecture. APU-P5 adds
the private AXI4 DMA and scheduler, microcode loader/sequencer, 112 KiB local
+74
View File
@@ -0,0 +1,74 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
module apb4_ga2d (
// verilog_format: off -- preserve the shell lifecycle boundary columns
input logic clk_i,
input logic rst_n_i,
input logic resource_quiesce_i,
input logic resource_reset_i,
input logic source_stop_i,
input logic source_safe_idle_i,
input logic block_ack_i,
input logic bridge_clear_busy_i,
input logic [7:0] bridge_epoch_i,
input logic data_ready_i,
apb4_if.slave apb4,
output logic idle_o,
output logic irq_o
// verilog_format: on
);
ga2d_pkg::ga2d_config_t s_config;
logic [7:0] s_epoch_seen_d, s_epoch_seen_q;
logic s_epoch_changed;
logic s_quiesced;
logic s_data_ready;
logic s_recovery_required;
logic s_soft_reset;
logic s_abort;
logic s_reg_idle;
logic s_unused;
assign s_epoch_seen_d = bridge_epoch_i;
assign s_epoch_changed = bridge_epoch_i != s_epoch_seen_q;
assign s_quiesced = (resource_quiesce_i || resource_reset_i) && source_stop_i &&
source_safe_idle_i && block_ack_i;
assign s_data_ready = data_ready_i && !resource_quiesce_i && !resource_reset_i;
assign s_recovery_required = bridge_clear_busy_i || !data_ready_i || s_epoch_changed;
assign idle_o = s_reg_idle && source_safe_idle_i;
assign s_unused = ^s_config ^ s_soft_reset ^ s_abort;
dffr #(
.DATA_WIDTH(8)
) u_epoch_seen (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_epoch_seen_d),
.dat_o (s_epoch_seen_q)
);
ga2d_reg u_ga2d_reg (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.busy_i (1'b0),
.draining_i (1'b0),
.quiesced_i (s_quiesced),
.data_ready_i (s_data_ready),
.recovery_required_i (s_recovery_required),
.idle_i (source_safe_idle_i),
.done_i (1'b0),
.aborted_i (1'b0),
.irq_event_i (3'd0),
.error_valid_i (1'b0),
.error_code_i (7'd0),
.error_stage_i (4'd0),
.error_axi_response_i(2'd0),
.error_address_i (32'd0),
.apb4 (apb4),
.config_o (s_config),
.soft_reset_o (s_soft_reset),
.abort_o (s_abort),
.idle_o (s_reg_idle),
.irq_o (irq_o)
);
endmodule
+137
View File
@@ -0,0 +1,137 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`ifndef RETROSOC_GA2D_DEFINE_SVH
`define RETROSOC_GA2D_DEFINE_SVH
// verilog_format: off -- keep the handwritten hardware/software ABI columns aligned
`define APB4_GA2D__IP_ID 12'h000
`define APB4_GA2D__IP_VERSION 12'h004
`define APB4_GA2D__CAPABILITY 12'h008
`define APB4_GA2D__LIMITS 12'h00c
`define APB4_GA2D__COMMAND 12'h010
`define APB4_GA2D__STATUS 12'h014
`define APB4_GA2D__IRQ_STATE 12'h018
`define APB4_GA2D__IRQ_ENABLE 12'h01c
`define APB4_GA2D__IRQ_TEST 12'h020
`define APB4_GA2D__ERROR_STATUS 12'h024
`define APB4_GA2D__ERROR_ADDRESS 12'h028
`define APB4_GA2D__TIMEOUT_CYCLES 12'h02c
`define APB4_GA2D__JOB_CONFIG 12'h030
`define APB4_GA2D__GLOBAL_ALPHA 12'h034
`define APB4_GA2D__COLOR 12'h038
`define APB4_GA2D__SIZE 12'h03c
`define APB4_GA2D__FG_ADDRESS 12'h040
`define APB4_GA2D__FG_PITCH 12'h044
`define APB4_GA2D__FG_FORMAT 12'h048
`define APB4_GA2D__BG_ADDRESS 12'h04c
`define APB4_GA2D__BG_PITCH 12'h050
`define APB4_GA2D__BG_FORMAT 12'h054
`define APB4_GA2D__DST_ADDRESS 12'h058
`define APB4_GA2D__DST_PITCH 12'h05c
`define APB4_GA2D__DST_FORMAT 12'h060
`define APB4_GA2D__PERF_SNAPSHOT 12'h064
`define APB4_GA2D__FORMAT_CAPABILITY 12'h068
`define APB4_GA2D__SNAP_CYCLES_LO 12'h080
`define APB4_GA2D__SNAP_CYCLES_HI 12'h084
`define APB4_GA2D__SNAP_READ_BYTES_LO 12'h088
`define APB4_GA2D__SNAP_READ_BYTES_HI 12'h08c
`define APB4_GA2D__SNAP_WRITE_BYTES_LO 12'h090
`define APB4_GA2D__SNAP_WRITE_BYTES_HI 12'h094
`define APB4_GA2D__SNAP_READ_STALL_LO 12'h098
`define APB4_GA2D__SNAP_READ_STALL_HI 12'h09c
`define APB4_GA2D__SNAP_WRITE_STALL_LO 12'h0a0
`define APB4_GA2D__SNAP_WRITE_STALL_HI 12'h0a4
`define APB4_GA2D__SNAP_PIPE_STALL_LO 12'h0a8
`define APB4_GA2D__SNAP_PIPE_STALL_HI 12'h0ac
`define APB4_GA2D__SNAP_LINES_DONE 12'h0b0
`define APB4_GA2D__IP_ID_VALUE 32'h4741_3244
`define APB4_GA2D__IP_VERSION_VALUE 32'h0001_0000
`define APB4_GA2D__CAPABILITY_P3 32'h0000_0040
`define APB4_GA2D__LIMITS_P3 32'h0000_0000
`define APB4_GA2D__FORMAT_CAPABILITY_P3 32'h0000_0000
`define APB4_GA2D__TIMEOUT_CYCLES_RESET 32'h0010_0000
`define APB4_GA2D__GLOBAL_ALPHA_RESET 32'h0000_00ff
`define APB4_GA2D__COMMAND_START 0
`define APB4_GA2D__COMMAND_ABORT 1
`define APB4_GA2D__COMMAND_SOFT_RESET 2
`define APB4_GA2D__STATUS_BUSY 0
`define APB4_GA2D__STATUS_DRAINING 1
`define APB4_GA2D__STATUS_QUIESCED 2
`define APB4_GA2D__STATUS_DATA_READY 3
`define APB4_GA2D__STATUS_DONE 4
`define APB4_GA2D__STATUS_ABORTED 5
`define APB4_GA2D__STATUS_ERROR 6
`define APB4_GA2D__STATUS_RECOVERY_REQUIRED 7
`define APB4_GA2D__IRQ_DONE 0
`define APB4_GA2D__IRQ_ERROR 1
`define APB4_GA2D__IRQ_ABORT_DONE 2
`define APB4_GA2D__IRQ_ALL 3'h7
`define APB4_GA2D__ERROR_STATUS_VALID 0
`define APB4_GA2D__ERROR_STATUS_CODE 1
`define APB4_GA2D__ERROR_STATUS_STAGE 8
`define APB4_GA2D__ERROR_STATUS_AXI_RESPONSE 12
`define APB4_GA2D__JOB_CONFIG_OPERATION 0
`define APB4_GA2D__GLOBAL_ALPHA_VALUE 0
`define APB4_GA2D__SIZE_WIDTH 0
`define APB4_GA2D__SIZE_HEIGHT 16
`define APB4_GA2D__FG_FORMAT_VALUE 0
`define APB4_GA2D__BG_FORMAT_VALUE 0
`define APB4_GA2D__DST_FORMAT_VALUE 0
`define APB4_GA2D__FORMAT_CAPABILITY_FOREGROUND 0
`define APB4_GA2D__FORMAT_CAPABILITY_BACKGROUND 8
`define APB4_GA2D__FORMAT_CAPABILITY_DESTINATION 16
`define APB4_GA2D__CAPABILITY_FILL 0
`define APB4_GA2D__CAPABILITY_COPY 1
`define APB4_GA2D__CAPABILITY_CONVERT 2
`define APB4_GA2D__CAPABILITY_BLEND 3
`define APB4_GA2D__CAPABILITY_A8_MASK 4
`define APB4_GA2D__CAPABILITY_PRIVATE_DMA 5
`define APB4_GA2D__CAPABILITY_IRQ 6
`define APB4_GA2D__CAPABILITY_SNAPSHOT 7
`define APB4_GA2D__CAPABILITY_TWO_DIMENSIONAL_PITCH 8
`define APB4_GA2D__CAPABILITY_BYTE_EDGES 9
`define APB4_GA2D__CAPABILITY_INPLACE_BACKGROUND 10
`define APB4_GA2D__OP_FILL 0
`define APB4_GA2D__OP_COPY 1
`define APB4_GA2D__OP_CONVERT 2
`define APB4_GA2D__OP_BLEND 3
`define APB4_GA2D__FORMAT_RGB565 0
`define APB4_GA2D__FORMAT_RGB888 1
`define APB4_GA2D__FORMAT_XRGB8888 2
`define APB4_GA2D__FORMAT_ARGB8888 3
`define APB4_GA2D__FORMAT_A8 4
`define APB4_GA2D__ERROR_NONE 0
`define APB4_GA2D__ERROR_INVALID_SIZE 1
`define APB4_GA2D__ERROR_INVALID_FORMAT 2
`define APB4_GA2D__ERROR_INVALID_PITCH 3
`define APB4_GA2D__ERROR_INVALID_ALIGNMENT 4
`define APB4_GA2D__ERROR_ADDRESS_OVERFLOW 5
`define APB4_GA2D__ERROR_ADDRESS_RANGE 6
`define APB4_GA2D__ERROR_OVERLAP 7
`define APB4_GA2D__ERROR_AXI_READ 8
`define APB4_GA2D__ERROR_AXI_WRITE 9
`define APB4_GA2D__ERROR_AXI_PROTOCOL 10
`define APB4_GA2D__ERROR_TIMEOUT 11
`define APB4_GA2D__ERROR_EPOCH_LOST 12
`define APB4_GA2D__ERROR_INTERNAL 13
`define APB4_GA2D__ERROR_STAGE_NONE 0
`define APB4_GA2D__ERROR_STAGE_VALIDATE 1
`define APB4_GA2D__ERROR_STAGE_FOREGROUND 2
`define APB4_GA2D__ERROR_STAGE_BACKGROUND 3
`define APB4_GA2D__ERROR_STAGE_DESTINATION 4
`define APB4_GA2D__ERROR_STAGE_LIFECYCLE 5
// verilog_format: on
`endif
+46
View File
@@ -0,0 +1,46 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`include "ga2d_define.svh"
package ga2d_pkg;
typedef enum logic [1:0] {
Fill = `APB4_GA2D__OP_FILL,
Copy = `APB4_GA2D__OP_COPY,
Convert = `APB4_GA2D__OP_CONVERT,
Blend = `APB4_GA2D__OP_BLEND
} ga2d_operation_e;
typedef enum logic [2:0] {
Rgb565 = `APB4_GA2D__FORMAT_RGB565,
Rgb888 = `APB4_GA2D__FORMAT_RGB888,
Xrgb8888 = `APB4_GA2D__FORMAT_XRGB8888,
Argb8888 = `APB4_GA2D__FORMAT_ARGB8888,
A8 = `APB4_GA2D__FORMAT_A8
} ga2d_format_e;
typedef struct packed {
logic [31:0] timeout_cycles;
logic [31:0] job_config;
logic [31:0] global_alpha;
logic [31:0] color;
logic [31:0] size;
logic [31:0] fg_address;
logic [31:0] fg_pitch;
logic [31:0] fg_format;
logic [31:0] bg_address;
logic [31:0] bg_pitch;
logic [31:0] bg_format;
logic [31:0] dst_address;
logic [31:0] dst_pitch;
logic [31:0] dst_format;
} ga2d_config_t;
typedef struct packed {
logic valid;
logic [6:0] code;
logic [3:0] stage;
logic [1:0] axi_response;
logic [31:0] address;
} ga2d_error_t;
endpackage
+372
View File
@@ -0,0 +1,372 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`include "ga2d_define.svh"
module ga2d_reg (
// verilog_format: off -- preserve the APB, lifecycle, and event boundary columns
input logic clk_i,
input logic rst_n_i,
input logic busy_i,
input logic draining_i,
input logic quiesced_i,
input logic data_ready_i,
input logic recovery_required_i,
input logic idle_i,
input logic done_i,
input logic aborted_i,
input logic [2:0] irq_event_i,
input logic error_valid_i,
input logic [6:0] error_code_i,
input logic [3:0] error_stage_i,
input logic [1:0] error_axi_response_i,
input logic [31:0] error_address_i,
apb4_if.slave apb4,
output ga2d_pkg::ga2d_config_t config_o,
output logic soft_reset_o,
output logic abort_o,
output logic idle_o,
output logic irq_o
// verilog_format: on
);
import ga2d_pkg::ga2d_config_t;
import ga2d_pkg::ga2d_error_t;
localparam ga2d_config_t ConfigReset = '{
timeout_cycles: `APB4_GA2D__TIMEOUT_CYCLES_RESET,
job_config: '0,
global_alpha: `APB4_GA2D__GLOBAL_ALPHA_RESET,
color: '0,
size: '0,
fg_address: '0,
fg_pitch: '0,
fg_format: '0,
bg_address: '0,
bg_pitch: '0,
bg_format: '0,
dst_address: '0,
dst_pitch: '0,
dst_format: '0
};
logic s_apb4_ready_d, s_apb4_ready_q;
logic s_access_seen_d, s_access_seen_q;
logic [31:0] s_apb4_rdata_d, s_apb4_rdata_q;
logic s_apb4_resp_err_d, s_apb4_resp_err_q;
ga2d_config_t s_config_d, s_config_q;
ga2d_error_t s_err_d, s_err_q;
logic [2:0] s_irq_state_d, s_irq_state_q;
logic [2:0] s_irq_en_d, s_irq_en_q;
logic s_req_accept;
logic s_write;
logic [11:0] s_offset;
logic [31:0] s_write_value;
logic [31:0] s_read_data;
logic s_read_valid;
logic s_read_err;
logic s_write_err;
logic [ 2:0] s_irq_clear;
logic [ 2:0] s_irq_test;
logic s_err_clear;
logic s_cmd_soft_reset;
logic s_cmd_abort;
assign s_req_accept = apb4.psel && apb4.penable && !s_access_seen_q;
assign s_write = s_req_accept && apb4.pwrite;
assign s_offset = apb4.paddr[11:0];
assign s_write_value = apb4.pwdata;
assign apb4.pready = s_apb4_ready_q;
assign apb4.prdata = s_apb4_rdata_q;
assign apb4.pslverr = s_apb4_resp_err_q;
assign config_o = s_config_q;
assign soft_reset_o = s_cmd_soft_reset;
assign abort_o = s_cmd_abort;
assign idle_o = idle_i;
assign irq_o = (s_irq_state_q & s_irq_en_q) != 3'd0;
always_comb begin
s_read_data = 32'd0;
s_read_valid = 1'b1;
unique case (s_offset)
`APB4_GA2D__IP_ID: s_read_data = `APB4_GA2D__IP_ID_VALUE;
`APB4_GA2D__IP_VERSION: s_read_data = `APB4_GA2D__IP_VERSION_VALUE;
`APB4_GA2D__CAPABILITY: s_read_data = `APB4_GA2D__CAPABILITY_P3;
`APB4_GA2D__LIMITS: s_read_data = `APB4_GA2D__LIMITS_P3;
`APB4_GA2D__STATUS:
s_read_data = {
24'd0,
recovery_required_i,
s_err_q.valid,
aborted_i,
done_i,
data_ready_i,
quiesced_i,
draining_i,
busy_i
};
`APB4_GA2D__IRQ_STATE: s_read_data = {29'd0, s_irq_state_q};
`APB4_GA2D__IRQ_ENABLE: s_read_data = {29'd0, s_irq_en_q};
`APB4_GA2D__ERROR_STATUS:
s_read_data = {18'd0, s_err_q.axi_response, s_err_q.stage, s_err_q.code, s_err_q.valid};
`APB4_GA2D__ERROR_ADDRESS: s_read_data = s_err_q.address;
`APB4_GA2D__TIMEOUT_CYCLES: s_read_data = s_config_q.timeout_cycles;
`APB4_GA2D__JOB_CONFIG: s_read_data = s_config_q.job_config;
`APB4_GA2D__GLOBAL_ALPHA: s_read_data = s_config_q.global_alpha;
`APB4_GA2D__COLOR: s_read_data = s_config_q.color;
`APB4_GA2D__SIZE: s_read_data = s_config_q.size;
`APB4_GA2D__FG_ADDRESS: s_read_data = s_config_q.fg_address;
`APB4_GA2D__FG_PITCH: s_read_data = s_config_q.fg_pitch;
`APB4_GA2D__FG_FORMAT: s_read_data = s_config_q.fg_format;
`APB4_GA2D__BG_ADDRESS: s_read_data = s_config_q.bg_address;
`APB4_GA2D__BG_PITCH: s_read_data = s_config_q.bg_pitch;
`APB4_GA2D__BG_FORMAT: s_read_data = s_config_q.bg_format;
`APB4_GA2D__DST_ADDRESS: s_read_data = s_config_q.dst_address;
`APB4_GA2D__DST_PITCH: s_read_data = s_config_q.dst_pitch;
`APB4_GA2D__DST_FORMAT: s_read_data = s_config_q.dst_format;
`APB4_GA2D__FORMAT_CAPABILITY: s_read_data = `APB4_GA2D__FORMAT_CAPABILITY_P3;
`APB4_GA2D__SNAP_CYCLES_LO,
`APB4_GA2D__SNAP_CYCLES_HI,
`APB4_GA2D__SNAP_READ_BYTES_LO,
`APB4_GA2D__SNAP_READ_BYTES_HI,
`APB4_GA2D__SNAP_WRITE_BYTES_LO,
`APB4_GA2D__SNAP_WRITE_BYTES_HI,
`APB4_GA2D__SNAP_READ_STALL_LO,
`APB4_GA2D__SNAP_READ_STALL_HI,
`APB4_GA2D__SNAP_WRITE_STALL_LO,
`APB4_GA2D__SNAP_WRITE_STALL_HI,
`APB4_GA2D__SNAP_PIPE_STALL_LO,
`APB4_GA2D__SNAP_PIPE_STALL_HI,
`APB4_GA2D__SNAP_LINES_DONE,
`APB4_GA2D__COMMAND,
`APB4_GA2D__IRQ_TEST,
`APB4_GA2D__PERF_SNAPSHOT: begin
end
default: s_read_valid = 1'b0;
endcase
end
always_comb begin
s_read_err = (s_offset[1:0] != 2'b00) || !s_read_valid;
end
always_comb begin
s_write_err = 1'b0;
s_irq_clear = 3'd0;
s_irq_test = 3'd0;
s_err_clear = 1'b0;
s_cmd_soft_reset = 1'b0;
s_cmd_abort = 1'b0;
if (s_write) begin
if ((s_offset[1:0] != 2'b00) || (apb4.pstrb != 4'hf)) begin
s_write_err = 1'b1;
end else begin
unique case (s_offset)
`APB4_GA2D__COMMAND: begin
unique case (s_write_value)
32'd0: begin
end
32'h0000_0001: s_write_err = 1'b1;
32'h0000_0002: s_cmd_abort = 1'b1;
32'h0000_0004: begin
if (busy_i || draining_i || recovery_required_i || !idle_i || !data_ready_i) begin
s_write_err = 1'b1;
end else begin
s_cmd_soft_reset = 1'b1;
end
end
default: s_write_err = 1'b1;
endcase
end
`APB4_GA2D__IRQ_STATE: begin
if (s_write_value[31:3] != '0) begin
s_write_err = 1'b1;
end else begin
s_irq_clear = s_write_value[2:0];
end
end
`APB4_GA2D__IRQ_ENABLE: begin
if (s_write_value[31:3] != '0) begin
s_write_err = 1'b1;
end
end
`APB4_GA2D__IRQ_TEST: begin
if (s_write_value[31:3] != '0) begin
s_write_err = 1'b1;
end else begin
s_irq_test = s_write_value[2:0];
end
end
`APB4_GA2D__ERROR_STATUS: begin
if (busy_i || recovery_required_i ||
((s_write_value != 32'd0) && (s_write_value != 32'd1))) begin
s_write_err = 1'b1;
end else if (s_write_value == 32'd1) begin
s_err_clear = 1'b1;
end
end
`APB4_GA2D__TIMEOUT_CYCLES: begin
if (busy_i || (s_write_value == 32'd0)) begin
s_write_err = 1'b1;
end
end
`APB4_GA2D__JOB_CONFIG: begin
if (busy_i || (s_write_value[31:2] != '0)) begin
s_write_err = 1'b1;
end
end
`APB4_GA2D__GLOBAL_ALPHA: begin
if (busy_i || (s_write_value[31:8] != '0)) begin
s_write_err = 1'b1;
end
end
`APB4_GA2D__COLOR,
`APB4_GA2D__SIZE,
`APB4_GA2D__FG_ADDRESS,
`APB4_GA2D__FG_PITCH,
`APB4_GA2D__BG_ADDRESS,
`APB4_GA2D__BG_PITCH,
`APB4_GA2D__DST_ADDRESS,
`APB4_GA2D__DST_PITCH: begin
if (busy_i) begin
s_write_err = 1'b1;
end
end
`APB4_GA2D__FG_FORMAT, `APB4_GA2D__BG_FORMAT, `APB4_GA2D__DST_FORMAT: begin
if (busy_i || (s_write_value[31:3] != '0)) begin
s_write_err = 1'b1;
end
end
`APB4_GA2D__PERF_SNAPSHOT: s_write_err = 1'b1;
default: s_write_err = 1'b1;
endcase
end
end
end
always_comb begin
s_apb4_ready_d = s_req_accept;
s_access_seen_d = s_access_seen_q;
if (!apb4.psel || !apb4.penable) begin
s_access_seen_d = 1'b0;
end else if (s_req_accept) begin
s_access_seen_d = 1'b1;
end
s_apb4_rdata_d = s_apb4_rdata_q;
s_apb4_resp_err_d = s_req_accept && (s_write ? s_write_err : s_read_err);
if (s_req_accept) begin
s_apb4_rdata_d = (!s_write && !s_read_err) ? s_read_data : 32'd0;
end
s_config_d = s_config_q;
s_irq_state_d = (s_irq_state_q & ~s_irq_clear) | irq_event_i | s_irq_test;
s_irq_en_d = s_irq_en_q;
s_err_d = s_err_q;
if (s_err_clear) begin
s_err_d = '0;
end
if (error_valid_i && !s_err_q.valid) begin
s_err_d = '{
valid: 1'b1,
code: error_code_i,
stage: error_stage_i,
axi_response: error_axi_response_i,
address: error_address_i
};
end
if (s_cmd_soft_reset) begin
s_config_d = ConfigReset;
s_irq_state_d = '0;
s_irq_en_d = '0;
s_err_d = '0;
end else if (s_write && !s_write_err) begin
unique case (s_offset)
`APB4_GA2D__IRQ_ENABLE: s_irq_en_d = s_write_value[2:0];
`APB4_GA2D__TIMEOUT_CYCLES: s_config_d.timeout_cycles = s_write_value;
`APB4_GA2D__JOB_CONFIG: s_config_d.job_config = s_write_value;
`APB4_GA2D__GLOBAL_ALPHA: s_config_d.global_alpha = s_write_value;
`APB4_GA2D__COLOR: s_config_d.color = s_write_value;
`APB4_GA2D__SIZE: s_config_d.size = s_write_value;
`APB4_GA2D__FG_ADDRESS: s_config_d.fg_address = s_write_value;
`APB4_GA2D__FG_PITCH: s_config_d.fg_pitch = s_write_value;
`APB4_GA2D__FG_FORMAT: s_config_d.fg_format = s_write_value;
`APB4_GA2D__BG_ADDRESS: s_config_d.bg_address = s_write_value;
`APB4_GA2D__BG_PITCH: s_config_d.bg_pitch = s_write_value;
`APB4_GA2D__BG_FORMAT: s_config_d.bg_format = s_write_value;
`APB4_GA2D__DST_ADDRESS: s_config_d.dst_address = s_write_value;
`APB4_GA2D__DST_PITCH: s_config_d.dst_pitch = s_write_value;
`APB4_GA2D__DST_FORMAT: s_config_d.dst_format = s_write_value;
default: begin
end
endcase
end
end
dffr #(
.DATA_WIDTH(1)
) u_apb4_ready (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_apb4_ready_d),
.dat_o (s_apb4_ready_q)
);
dffr #(
.DATA_WIDTH(1)
) u_access_seen (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_access_seen_d),
.dat_o (s_access_seen_q)
);
dffr #(
.DATA_WIDTH(32)
) u_apb4_rdata (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_apb4_rdata_d),
.dat_o (s_apb4_rdata_q)
);
dffr #(
.DATA_WIDTH(1)
) u_apb4_resp_err (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_apb4_resp_err_d),
.dat_o (s_apb4_resp_err_q)
);
dffrc #(
.DATA_WIDTH($bits(ga2d_config_t)),
.RESET_VAL (ConfigReset)
) u_config (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_config_d),
.dat_o (s_config_q)
);
dffr #(
.DATA_WIDTH($bits(ga2d_error_t))
) u_error (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_d),
.dat_o (s_err_q)
);
dffr #(
.DATA_WIDTH(3)
) u_irq_state (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_irq_state_d),
.dat_o (s_irq_state_q)
);
dffr #(
.DATA_WIDTH(3)
) u_irq_enable (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_irq_en_d),
.dat_o (s_irq_en_q)
);
endmodule
+4 -4
View File
@@ -409,12 +409,12 @@
"user_access": "rw"
},
{
"symbol": "APB4_GA",
"symbol": "APB4_GA2D",
"base": "0x10012000",
"size": "0x00001000",
"route": "reserved",
"kind": "reserved",
"public": false,
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "none"
},
{
+3
View File
@@ -144,6 +144,9 @@
/ip/multimedia/apu_codec_controller.sv
/ip/multimedia/apu_reg.sv
/ip/multimedia/apb4_apu.sv
/ip/multimedia/ga2d_pkg.sv
/ip/multimedia/ga2d_reg.sv
/ip/multimedia/apb4_ga2d.sv
/ip/multimedia/jpeg_pkg.sv
/ip/multimedia/jpeg_transform.sv
/ip/multimedia/jpeg_quantizer.sv
+6 -5
View File
@@ -5,11 +5,12 @@ that is not part of the software address-map ABI or the package pad map.
[GA2D](../../../docs/ip/ga2d.md) Phase 2 expands the topology to data
master/resource 8 and seven-bit global IDs while retaining all existing
allocations. It adds only a dedicated PCLK-to-HP AXI64/ID3 GA2D bridge whose
source is held idle. `APB4_GA` remains reserved and inactive; the GA2D APB
shell, IRQ wiring, payload/DMA, and pixel function remain deferred to later
phases. Phase 3 appends APB slot 28 and GA2D's LP32/HP PLIC11 interrupt. The
Xh3irq SDK path also requires the Phase 1 implementation; a wiring-only
allocations. It adds a dedicated PCLK-to-HP AXI64/ID3 GA2D bridge whose source
remains idle. Phase 3 activates `APB4_GA2D` at slot 28 as a PCLK register
shell and routes its raw IRQ through resource 8: LP group bit 24, core vector
bit 32/external ordinal 30, or HP PLIC source 11 according to owner. The shell
exposes IRQ-only capability; payload/DMA, pixel work, and START remain deferred.
The Xh3irq SDK path also requires the Phase 1 implementation; a wiring-only
expansion is insufficient.
The topology generator validates APB4 island ownership against
+17 -1
View File
@@ -5,7 +5,7 @@
"irq_groups": [
{
"name": "apb4_periph",
"width": 24,
"width": 25,
"top_signal": "s_apb4_periph_irq"
},
{
@@ -285,6 +285,15 @@
"core_bit": 31,
"signal": "resource_irq_lp_i[6]",
"user_visible": false
},
{
"name": "ga2d",
"description": "GA2D shell completion and error interrupt",
"group": "apb4_periph",
"group_bit": 24,
"core_bit": 32,
"signal": "resource_irq_lp_i[7]",
"user_visible": false
}
],
"wake_sources": [
@@ -624,6 +633,13 @@
"timed_interface": "u_apu_apb4_if",
"pure_interface": "u_apu_apb4_pure_if",
"region": "APB4_APU"
},
{
"slot": 28,
"name": "ga2d",
"timed_interface": "u_ga2d_apb4_if",
"pure_interface": "u_ga2d_apb4_pure_if",
"region": "APB4_GA2D"
}
],
"apb4_system_targets": [
+1
View File
@@ -103,6 +103,7 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \
$(ROOT_PATH)/crt/src/hal/dma.c \
$(ROOT_PATH)/crt/src/hal/crypto.c \
$(ROOT_PATH)/crt/src/hal/apu.c \
$(ROOT_PATH)/crt/src/hal/ga2d.c \
$(ROOT_PATH)/crt/src/hal/jpeg_math.c \
$(ROOT_PATH)/crt/src/hal/jpeg.c \
$(ROOT_PATH)/crt/src/hal/dvp.c \
+38 -4
View File
@@ -23,8 +23,8 @@ module apb4_periph (
input logic debug_halted_i,
input logic timebase_tick_i,
input logic ext_h_hp_irq_i,
input logic [6:0] resource_irq_lp_i,
input logic [6:0] resource_irq_hp_i,
input logic [7:0] resource_irq_lp_i,
input logic [7:0] resource_irq_hp_i,
input logic [1:0] apu_owner_i,
input logic apu_owner_lock_i,
input logic apu_quiesce_i,
@@ -32,6 +32,14 @@ module apb4_periph (
input logic [7:0] apu_bridge_epoch_i,
input logic jpeg_quiesce_i,
input logic jpeg_reset_i,
input logic ga2d_quiesce_i,
input logic ga2d_reset_i,
input logic ga2d_source_stop_i,
input logic ga2d_source_safe_idle_i,
input logic ga2d_block_ack_i,
input logic ga2d_bridge_clear_busy_i,
input logic [7:0] ga2d_bridge_epoch_i,
input logic ga2d_data_ready_i,
axi4_if.slave cfg_axi4,
axi4_if.slave psram_axi4,
axi4_if.slave xpi_axi4,
@@ -75,7 +83,8 @@ module apb4_periph (
output logic hp_supervisor_external_irq_o,
output logic apu_idle_o,
output logic jpeg_idle_o,
output logic [6:0] resource_irq_raw_o,
output logic ga2d_idle_o,
output logic [7:0] resource_irq_raw_o,
output logic [`SOC_IRQ_APB4_PERIPH_WIDTH-1:0] irq_o
// verilog_format: on
);
@@ -179,6 +188,7 @@ axi4_stream_if #(
logic s_apu_irq_raw;
logic s_i2s_tx_underrun_evt, s_i2s_rx_overrun_evt;
logic s_jpeg_irq_raw;
logic s_ga2d_irq_raw;
logic s_usb2_irq;
logic s_tim0_irq, s_tim1_irq;
logic s_dvp_irq;
@@ -237,7 +247,14 @@ axi4_stream_if #(
assign hp_machine_external_irq_o = s_hp_plic_context_irq[0];
assign hp_supervisor_external_irq_o = s_hp_plic_context_irq[1];
assign resource_irq_raw_o = {
s_apu_irq_raw, s_jpeg_irq_raw, spisd.irq_o, sdio1.irq_o, sdio0.irq_o, s_usb2_irq, s_dma_irq
s_ga2d_irq_raw,
s_apu_irq_raw,
s_jpeg_irq_raw,
spisd.irq_o,
sdio1.irq_o,
sdio0.irq_o,
s_usb2_irq,
s_dma_irq
};
apb4_async_bridge u_psram_cfg_mem_cdc (
@@ -319,6 +336,7 @@ axi4_stream_if #(
s_hp_plic_source[8] = resource_irq_hp_i[4];
s_hp_plic_source[9] = resource_irq_hp_i[5];
s_hp_plic_source[10] = resource_irq_hp_i[6];
s_hp_plic_source[11] = resource_irq_hp_i[7];
end
`include "apb4_periph_irq_bindings.svh"
@@ -519,6 +537,22 @@ axi4_stream_if #(
.irq_o (s_apu_irq_raw)
);
apb4_ga2d u_apb4_ga2d (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.resource_quiesce_i (ga2d_quiesce_i),
.resource_reset_i (ga2d_reset_i),
.source_stop_i (ga2d_source_stop_i),
.source_safe_idle_i (ga2d_source_safe_idle_i),
.block_ack_i (ga2d_block_ack_i),
.bridge_clear_busy_i(ga2d_bridge_clear_busy_i),
.bridge_epoch_i (ga2d_bridge_epoch_i),
.data_ready_i (ga2d_data_ready_i),
.apb4 (u_ga2d_apb4_if),
.idle_o (ga2d_idle_o),
.irq_o (s_ga2d_irq_raw)
);
apb4_sysctrl u_apb4_sysctrl (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
+15 -13
View File
@@ -25,7 +25,7 @@ module apb4_system (
input logic ext_h_data_idle_i,
input logic [8:0] resource_idle_i,
input logic [8:0] resource_block_ack_i,
input logic [6:0] resource_irq_i,
input logic [7:0] resource_irq_i,
input logic cache_request_i,
axi4_if.slave axi4,
axi4_if.master ext_h_axi4,
@@ -49,8 +49,8 @@ module apb4_system (
output logic [8:0] resource_owner_lock_o,
output logic [8:0] resource_quiesce_o,
output logic [8:0] resource_reset_o,
output logic [6:0] resource_irq_lp_o,
output logic [6:0] resource_irq_hp_o,
output logic [7:0] resource_irq_lp_o,
output logic [7:0] resource_irq_hp_o,
output logic [`SOC_IRQ_APB4_SYSTEM_WIDTH-1:0] irq_o
// verilog_format: on
);
@@ -112,17 +112,19 @@ module apb4_system (
logic s_unused_product_input;
`endif
assign s_resource_irq = {1'b0, resource_irq_i[6:5], s_ext_h_irq_raw, resource_irq_i[4:0]};
assign s_ext_h_irq = s_resource_irq_lp[5];
assign ext_h_irq_raw_o = s_ext_h_irq_raw;
assign ext_h_owner_o = s_resource_owner[5];
assign cache_clean_o = s_cache_clean;
assign resource_owner_o = s_resource_owner;
assign s_resource_irq = {
resource_irq_i[7], resource_irq_i[6:5], s_ext_h_irq_raw, resource_irq_i[4:0]
};
assign s_ext_h_irq = s_resource_irq_lp[5];
assign ext_h_irq_raw_o = s_ext_h_irq_raw;
assign ext_h_owner_o = s_resource_owner[5];
assign cache_clean_o = s_cache_clean;
assign resource_owner_o = s_resource_owner;
assign resource_owner_lock_o = s_resource_owner_lock;
assign resource_quiesce_o = s_resource_quiesce;
assign resource_reset_o = s_resource_reset;
assign resource_irq_lp_o = {s_resource_irq_lp[7:6], s_resource_irq_lp[4:0]};
assign resource_irq_hp_o = {s_resource_irq_hp[7:6], s_resource_irq_hp[4:0]};
assign resource_quiesce_o = s_resource_quiesce;
assign resource_reset_o = s_resource_reset;
assign resource_irq_lp_o = {s_resource_irq_lp[8], s_resource_irq_lp[7:6], s_resource_irq_lp[4:0]};
assign resource_irq_hp_o = {s_resource_irq_hp[8], s_resource_irq_hp[7:6], s_resource_irq_hp[4:0]};
`ifdef PDK_IHP130
localparam logic [31:0] ARCHINFO_TECHNOLOGY = 32'h0201_0082;
+15 -5
View File
@@ -252,11 +252,12 @@ module retrosoc (
logic [ 8:0] s_resource_idle_pclk;
logic [ 8:0] s_resource_block_ack_hp;
logic [ 8:0] s_resource_block_ack_pclk;
logic [ 6:0] s_resource_irq_raw;
logic [ 6:0] s_resource_irq_lp;
logic [ 6:0] s_resource_irq_hp;
logic [ 7:0] s_resource_irq_raw;
logic [ 7:0] s_resource_irq_lp;
logic [ 7:0] s_resource_irq_hp;
logic s_apu_idle;
logic s_jpeg_idle;
logic s_ga2d_idle;
logic [ 8:0] s_resource_idle_combined;
logic [ 8:0] s_resource_block_ack_combined;
logic s_ga2d_source_stop;
@@ -869,6 +870,14 @@ module retrosoc (
.apu_bridge_epoch_i (s_apu_bridge_epoch),
.jpeg_quiesce_i (s_resource_quiesce[6]),
.jpeg_reset_i (s_resource_reset[6]),
.ga2d_quiesce_i (s_resource_quiesce[8]),
.ga2d_reset_i (s_resource_reset[8]),
.ga2d_source_stop_i (s_ga2d_source_stop),
.ga2d_source_safe_idle_i (s_ga2d_source_safe_idle),
.ga2d_block_ack_i (s_resource_block_ack_pclk[8]),
.ga2d_bridge_clear_busy_i (s_ga2d_bridge_clear_busy),
.ga2d_bridge_epoch_i (s_ga2d_bridge_epoch),
.ga2d_data_ready_i (s_ga2d_data_ready),
.cfg_axi4 (u_cfg_pclk_axi4_if),
.dma_axi4 (u_dma_axi4_if),
.sdio0_axi4 (u_sdio0_axi4_if),
@@ -913,6 +922,7 @@ module retrosoc (
.resource_irq_raw_o (s_resource_irq_raw),
.apu_idle_o (s_apu_idle),
.jpeg_idle_o (s_jpeg_idle),
.ga2d_idle_o (s_ga2d_idle),
.irq_o (s_apb4_periph_irq)
);
@@ -963,14 +973,14 @@ module retrosoc (
);
assign s_resource_idle_combined = {
s_ga2d_source_safe_idle && s_resource_idle_pclk[8],
s_ga2d_idle && s_ga2d_source_safe_idle && s_resource_idle_pclk[8],
s_apu_idle && s_resource_idle_pclk[7],
s_jpeg_idle && s_resource_idle_pclk[6],
s_ext_h_data_idle && s_resource_idle_pclk[5],
s_resource_idle_pclk[4:0]
};
assign s_resource_block_ack_combined = {
s_ga2d_source_safe_idle && s_resource_block_ack_pclk[8],
s_ga2d_idle && s_ga2d_source_safe_idle && s_resource_block_ack_pclk[8],
s_apu_idle && s_resource_block_ack_pclk[7],
s_resource_block_ack_pclk[6:0]
};
+4
View File
@@ -134,6 +134,7 @@
],
"rtl/ip/multimedia": [
"apb4_apu.sv",
"apb4_ga2d.sv",
"apb4_jpeg.sv",
"apu_bitstream_engine.sv",
"apu_codec_controller.sv",
@@ -158,6 +159,9 @@
"dvp_core.sv",
"dvp_define.svh",
"dvp_reg.sv",
"ga2d_define.svh",
"ga2d_pkg.sv",
"ga2d_reg.sv",
"jpeg_bit_packer.sv",
"jpeg_bit_reader.sv",
"jpeg_block_decoder.sv",
+2
View File
@@ -34,6 +34,7 @@ TEST_SOURCES = (
"crt/src/hal/dma_math.c",
"crt/src/hal/jpeg_math.c",
"crt/src/hal/apu.c",
"crt/src/hal/ga2d.c",
"crt/src/hal/fabric_monitor.c",
"crt/src/hal/sysctrl.c",
"crt/src/hal/clock.c",
@@ -96,6 +97,7 @@ def main() -> int:
"-Werror",
"-fno-builtin",
"-DRS_APU_TEST_MMIO",
"-DRS_GA2D_TEST_MMIO",
"-DRS_FABRIC_MONITOR_TEST_MMIO",
"-I",
str(memory_map_root / "include"),
+102 -4
View File
@@ -12,6 +12,7 @@
#include <retrosoc/hal/extension.h>
#include <retrosoc/hal/dma.h>
#include <retrosoc/hal/fabric_monitor.h>
#include <retrosoc/hal/ga2d.h>
#include <retrosoc/hal/i2c.h>
#include <retrosoc/hal/lcd.h>
#include <retrosoc/hal/i2s.h>
@@ -45,9 +46,11 @@ unsigned long long __umoddi3(unsigned long long dividend, unsigned long long div
static uint8_t storage[32];
static uint32_t image_call_count;
volatile uint32_t rs_apu_test_mmio[1024];
volatile uint32_t rs_ga2d_test_mmio[1024];
volatile uint32_t rs_fabric_monitor_test_mmio[1024];
#define APU_TEST_REG(offset) rs_apu_test_mmio[(offset) / 4U]
#define APU_TEST_REG(offset) rs_apu_test_mmio[(offset) / 4U]
#define GA2D_TEST_REG(offset) rs_ga2d_test_mmio[(offset) / 4U]
#define FABRIC_MONITOR_TEST_REG(offset) rs_fabric_monitor_test_mmio[(offset) / 4U]
void putch(char ch) {
@@ -899,9 +902,9 @@ static int test_fabric_monitor_hal_contract(void) {
test_fabric_monitor_mmio_reset();
FABRIC_MONITOR_TEST_REG(UINT32_C(0x004)) = UINT32_C(0x00010000);
FABRIC_MONITOR_TEST_REG(UINT32_C(0x014)) =
UINT32_C(0x00001000) | UINT32_C(0x0A00) | UINT32_C(0x0080) | UINT32_C(0x000C) |
UINT32_C(0x0003);
FABRIC_MONITOR_TEST_REG(UINT32_C(0x014)) = UINT32_C(0x00001000) | UINT32_C(0x0A00) |
UINT32_C(0x0080) | UINT32_C(0x000C) |
UINT32_C(0x0003);
FABRIC_MONITOR_TEST_REG(UINT32_C(0x018)) = UINT32_C(0x12345678);
FABRIC_MONITOR_TEST_REG(UINT32_C(0x020)) = UINT32_C(0x0000002A);
if ((rs_fabric_monitor_read_fault(&fault) != RS_OK) || (fault.master != 3U) ||
@@ -1108,6 +1111,100 @@ static int test_apu_hal_contract(void) {
return 0;
}
static void test_ga2d_mmio_reset(void) {
for (size_t index = 0U; index < (sizeof(rs_ga2d_test_mmio) / sizeof(rs_ga2d_test_mmio[0]));
++index) {
rs_ga2d_test_mmio[index] = 0U;
}
GA2D_TEST_REG(RS_GA2D_REG_IP_ID) = RS_GA2D_IP_ID_VALUE;
GA2D_TEST_REG(RS_GA2D_REG_IP_VERSION) = RS_GA2D_IP_VERSION_VALUE;
GA2D_TEST_REG(RS_GA2D_REG_CAPABILITY) = RS_GA2D_CAPABILITY_P3;
GA2D_TEST_REG(RS_GA2D_REG_STATUS) = RS_GA2D_STATUS_DATA_READY;
}
static int test_ga2d_hal_contract(void) {
rs_ga2d_capability_t capability;
rs_ga2d_status_t status;
rs_ga2d_error_t error;
rs_ga2d_stats_t stats;
rs_ga2d_job_t job = {0};
test_ga2d_mmio_reset();
if ((rs_ga2d_get_capability(NULL) != RS_EINVAL) ||
(rs_ga2d_get_capability(&capability) != RS_OK) ||
(capability.version != RS_GA2D_IP_VERSION_VALUE) ||
(capability.features != RS_GA2D_CAPABILITY_P3) || (capability.limits != 0U) ||
(capability.formats != 0U)) {
return 1;
}
GA2D_TEST_REG(RS_GA2D_REG_IP_VERSION) = UINT32_C(0x00020000);
if (rs_ga2d_get_capability(&capability) != RS_ENOTSUP) {
return 2;
}
test_ga2d_mmio_reset();
GA2D_TEST_REG(RS_GA2D_REG_COMMAND) = UINT32_C(0xA5A5A5A5);
if ((rs_ga2d_job_validate(NULL) != RS_EINVAL) || (rs_ga2d_job_validate(&job) != RS_ENOTSUP) ||
(rs_ga2d_configure(NULL) != RS_EINVAL) || (rs_ga2d_configure(&job) != RS_ENOTSUP) ||
(rs_ga2d_start() != RS_ENOTSUP) || (rs_ga2d_wait(1U) != RS_ENOTSUP) ||
(rs_ga2d_get_stats(NULL) != RS_EINVAL) || (rs_ga2d_get_stats(&stats) != RS_ENOTSUP) ||
(GA2D_TEST_REG(RS_GA2D_REG_COMMAND) != UINT32_C(0xA5A5A5A5))) {
return 3;
}
GA2D_TEST_REG(RS_GA2D_REG_STATUS) =
RS_GA2D_STATUS_BUSY | RS_GA2D_STATUS_DATA_READY | RS_GA2D_STATUS_ERROR;
GA2D_TEST_REG(RS_GA2D_REG_IRQ_STATE) = RS_GA2D_IRQ_DONE | RS_GA2D_IRQ_ABORT_DONE;
if ((rs_ga2d_get_status(NULL) != RS_EINVAL) || (rs_ga2d_get_status(&status) != RS_OK) ||
!status.busy || !status.data_ready || !status.error ||
(status.irq_state != (RS_GA2D_IRQ_DONE | RS_GA2D_IRQ_ABORT_DONE))) {
return 4;
}
GA2D_TEST_REG(RS_GA2D_REG_ERROR_STATUS) =
RS_GA2D_ERROR_STATUS_VALID | (UINT32_C(11) << RS_GA2D_ERROR_STATUS_CODE_SHIFT) |
(UINT32_C(5) << RS_GA2D_ERROR_STATUS_STAGE_SHIFT) |
(UINT32_C(2) << RS_GA2D_ERROR_STATUS_AXI_RESPONSE_SHIFT);
GA2D_TEST_REG(RS_GA2D_REG_ERROR_ADDRESS) = UINT32_C(0x30000040);
if ((rs_ga2d_get_error(NULL) != RS_EINVAL) || (rs_ga2d_get_error(&error) != RS_OK) ||
!error.valid || (error.code != 11U) || (error.stage != 5U) || (error.axi_response != 2U) ||
(error.address != (uintptr_t)UINT32_C(0x30000040))) {
return 5;
}
if ((rs_ga2d_irq_enable(RS_GA2D_IRQ_ALL) != RS_OK) ||
(GA2D_TEST_REG(RS_GA2D_REG_IRQ_ENABLE) != RS_GA2D_IRQ_ALL) ||
(rs_ga2d_irq_pending(NULL) != RS_EINVAL) ||
(rs_ga2d_irq_pending(&capability.features) != RS_OK) ||
(capability.features != (RS_GA2D_IRQ_DONE | RS_GA2D_IRQ_ABORT_DONE)) ||
(rs_ga2d_irq_clear(RS_GA2D_IRQ_ERROR) != RS_OK) ||
(GA2D_TEST_REG(RS_GA2D_REG_IRQ_STATE) != RS_GA2D_IRQ_ERROR) ||
(rs_ga2d_irq_enable(UINT32_C(0x8)) != RS_EINVAL)) {
return 6;
}
GA2D_TEST_REG(RS_GA2D_REG_STATUS) = RS_GA2D_STATUS_DATA_READY;
if ((rs_ga2d_reset() != RS_OK) ||
(GA2D_TEST_REG(RS_GA2D_REG_COMMAND) != RS_GA2D_COMMAND_SOFT_RESET)) {
return 7;
}
GA2D_TEST_REG(RS_GA2D_REG_STATUS) = RS_GA2D_STATUS_RECOVERY_REQUIRED;
GA2D_TEST_REG(RS_GA2D_REG_COMMAND) = UINT32_C(0x5A5A5A5A);
if ((rs_ga2d_reset() != RS_EIO) ||
(GA2D_TEST_REG(RS_GA2D_REG_COMMAND) != UINT32_C(0x5A5A5A5A))) {
return 8;
}
GA2D_TEST_REG(RS_GA2D_REG_STATUS) = 0U;
if ((rs_ga2d_abort_wait(0U) != RS_OK) ||
(GA2D_TEST_REG(RS_GA2D_REG_COMMAND) != RS_GA2D_COMMAND_ABORT)) {
return 9;
}
GA2D_TEST_REG(RS_GA2D_REG_STATUS) = RS_GA2D_STATUS_BUSY;
if (rs_ga2d_abort_wait(1U) != RS_ETIMEOUT) {
return 10;
}
return 0;
}
static int test_jpeg_validation(void) {
static _Alignas(RS_JPEG_DESCRIPTOR_ALIGNMENT) rs_jpeg_descriptor_t ring[2];
rs_jpeg_descriptor_t descriptor;
@@ -1263,6 +1360,7 @@ int main(void) {
test_fabric_monitor_hal_contract(),
test_apu_validation(),
test_apu_hal_contract(),
test_ga2d_hal_contract(),
test_jpeg_validation(),
test_ps2_decoders(),
test_wav_parser(),
+2 -12
View File
@@ -16,22 +16,12 @@ module apb4_topology_tb;
begin
s_decode_addr = address;
#1;
if (s_psel_comb !== (28'd1 << slot)) begin
if (s_psel_comb !== (29'd1 << slot)) begin
$fatal(1, "unexpected APB4 target slot for %h: got %b", address, s_psel_comb);
end
end
endtask
task automatic expect_disabled(input logic [31:0] address);
begin
s_decode_addr = address;
#1;
if (s_psel_comb !== '0) begin
$fatal(1, "disabled APB4 target accepted %h: got %b", address, s_psel_comb);
end
end
endtask
initial begin
expect_select(`SOC_ADDR_APB4_GPIO_BASE, 1);
expect_select(`SOC_ADDR_APB4_GPIO_ADMIN_BASE, 1);
@@ -43,7 +33,7 @@ module apb4_topology_tb;
expect_select(`SOC_ADDR_APB4_SDIO0_BASE, 15);
expect_select(`SOC_ADDR_APB4_SDIO1_BASE, 18);
expect_select(`SOC_ADDR_APB4_CRYPTO_BASE, 19);
expect_disabled(`SOC_ADDR_APB4_GA_BASE);
expect_select(`SOC_ADDR_APB4_GA2D_BASE, 28);
expect_select(`SOC_ADDR_APB4_APU_BASE, 27);
$display("SoC topology APB4 routing test passed");
$finish;
+1 -1
View File
@@ -154,7 +154,7 @@ module bus_fault_tb;
repeat (2) @(posedge clk_i);
rst_n_i = 1'b1;
expect_fault(32'h1001_2000, 4'hF, 1'b1, `RIB_RESP_RESERVED);
expect_fault(32'h6000_0000, 4'hF, 1'b1, `RIB_RESP_RESERVED);
expect_fault(32'hA000_0000, 4'h0, 1'b0, `RIB_RESP_DECERR);
expect_user_denied(32'h1000_B000, 4'hF);
$display("bus fault responder test passed");
+250
View File
@@ -0,0 +1,250 @@
`timescale 1ns / 1ps
`include "ga2d_define.svh"
module ga2d_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic busy_i = 1'b0;
logic draining_i = 1'b0;
logic quiesced_i = 1'b0;
logic data_ready_i = 1'b1;
logic recovery_required_i = 1'b0;
logic idle_i = 1'b1;
logic done_i = 1'b0;
logic aborted_i = 1'b0;
logic [ 2:0] irq_event_i = 3'd0;
logic error_valid_i = 1'b0;
logic [ 6:0] error_code_i = 7'd0;
logic [ 3:0] error_stage_i = 4'd0;
logic [ 1:0] error_axi_response_i = 2'd0;
logic [31:0] error_address_i = 32'd0;
logic soft_reset_o;
logic abort_o;
logic idle_o;
logic irq_o;
logic [31:0] read_data;
int unsigned soft_reset_count;
apb4_if apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
always @(posedge clk_i) begin
if (soft_reset_o) begin
soft_reset_count++;
end
end
ga2d_reg u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.busy_i (busy_i),
.draining_i (draining_i),
.quiesced_i (quiesced_i),
.data_ready_i (data_ready_i),
.recovery_required_i (recovery_required_i),
.idle_i (idle_i),
.done_i (done_i),
.aborted_i (aborted_i),
.irq_event_i (irq_event_i),
.error_valid_i (error_valid_i),
.error_code_i (error_code_i),
.error_stage_i (error_stage_i),
.error_axi_response_i(error_axi_response_i),
.error_address_i (error_address_i),
.apb4 (apb4),
.config_o (),
.soft_reset_o (soft_reset_o),
.abort_o (abort_o),
.idle_o (idle_o),
.irq_o (irq_o)
);
task automatic apb_write(input logic [11:0] offset_i, input logic [31:0] value_i,
input logic [3:0] strb_i, input logic expected_error_i);
begin
@(negedge clk_i);
apb4.paddr = {20'd0, offset_i};
apb4.pwrite = 1'b1;
apb4.pwdata = value_i;
apb4.pstrb = strb_i;
apb4.psel = 1'b1;
apb4.penable = 1'b1;
@(posedge clk_i);
#1;
if (!apb4.pready || (apb4.pslverr != expected_error_i)) begin
$fatal(1, "GA2D APB write response mismatch at %h", offset_i);
end
@(negedge clk_i);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pstrb = '0;
end
endtask
task automatic apb_read(input logic [11:0] offset_i, input logic expected_error_i,
output logic [31:0] value_o);
begin
@(negedge clk_i);
apb4.paddr = {20'd0, offset_i};
apb4.pwrite = 1'b0;
apb4.pstrb = '0;
apb4.psel = 1'b1;
apb4.penable = 1'b1;
@(posedge clk_i);
#1;
if (!apb4.pready || (apb4.pslverr != expected_error_i)) begin
$fatal(1, "GA2D APB read response mismatch at %h", offset_i);
end
value_o = apb4.prdata;
@(negedge clk_i);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
end
endtask
task automatic held_soft_reset;
begin
@(negedge clk_i);
apb4.paddr = {20'd0, `APB4_GA2D__COMMAND};
apb4.pwrite = 1'b1;
apb4.pwdata = 32'h0000_0004;
apb4.pstrb = 4'hf;
apb4.psel = 1'b1;
apb4.penable = 1'b1;
@(posedge clk_i);
#1;
if (!apb4.pready || apb4.pslverr) begin
$fatal(1, "GA2D soft reset response mismatch");
end
repeat (3) begin
@(posedge clk_i);
#1;
if (apb4.pready) begin
$fatal(1, "held GA2D APB access produced a second response");
end
end
if (soft_reset_count != 1) begin
$fatal(1, "held GA2D APB access repeated SOFT_RESET");
end
@(negedge clk_i);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pstrb = '0;
end
endtask
initial begin
apb4.paddr = '0;
apb4.pprot = '0;
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pwdata = '0;
apb4.pstrb = '0;
soft_reset_count = 0;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
apb_read(`APB4_GA2D__IP_ID, 1'b0, read_data);
if (read_data != 32'h4741_3244) $fatal(1, "GA2D IP_ID mismatch");
apb_read(`APB4_GA2D__IP_VERSION, 1'b0, read_data);
if (read_data != 32'h0001_0000) $fatal(1, "GA2D IP_VERSION mismatch");
apb_read(`APB4_GA2D__CAPABILITY, 1'b0, read_data);
if (read_data != 32'h0000_0040) $fatal(1, "GA2D shell capability mismatch");
apb_read(`APB4_GA2D__TIMEOUT_CYCLES, 1'b0, read_data);
if (read_data != 32'h0010_0000) $fatal(1, "GA2D timeout reset mismatch");
apb_read(`APB4_GA2D__GLOBAL_ALPHA, 1'b0, read_data);
if (read_data != 32'h0000_00ff) $fatal(1, "GA2D alpha reset mismatch");
apb_write(`APB4_GA2D__TIMEOUT_CYCLES, 32'd0, 4'hf, 1'b1);
apb_write(`APB4_GA2D__TIMEOUT_CYCLES, 32'h1122_3344, 4'h7, 1'b1);
apb_write(`APB4_GA2D__GLOBAL_ALPHA, 32'h0000_0055, 4'hf, 1'b0);
apb_write(`APB4_GA2D__FG_FORMAT, 32'h0000_0005, 4'hf, 1'b0);
apb_write(`APB4_GA2D__JOB_CONFIG, 32'h0000_0004, 4'hf, 1'b1);
apb_write(`APB4_GA2D__IP_ID, 32'h0000_0000, 4'hf, 1'b1);
apb_read(`APB4_GA2D__GLOBAL_ALPHA, 1'b0, read_data);
if (read_data != 32'h0000_0055) $fatal(1, "GA2D alpha write mismatch");
apb_read(`APB4_GA2D__FG_FORMAT, 1'b0, read_data);
if (read_data != 32'h0000_0005) $fatal(1, "GA2D format shadow mismatch");
apb_read(12'h06c, 1'b1, read_data);
apb_read(12'h003, 1'b1, read_data);
apb_write(`APB4_GA2D__COMMAND, 32'h0000_0001, 4'hf, 1'b1);
apb_write(`APB4_GA2D__PERF_SNAPSHOT, 32'h0000_0001, 4'hf, 1'b1);
apb_read(`APB4_GA2D__SNAP_CYCLES_LO, 1'b0, read_data);
if (read_data != 32'd0) $fatal(1, "GA2D P3 counter stub is nonzero");
@(negedge clk_i);
irq_event_i = `APB4_GA2D__IRQ_ALL;
@(posedge clk_i);
#1;
irq_event_i = '0;
apb_write(`APB4_GA2D__IRQ_ENABLE, `APB4_GA2D__IRQ_ALL, 4'hf, 1'b0);
if (!irq_o) $fatal(1, "GA2D raw IRQ did not assert");
irq_event_i = 3'b001;
apb_write(`APB4_GA2D__IRQ_STATE, 32'h0000_0007, 4'hf, 1'b0);
irq_event_i = '0;
apb_read(`APB4_GA2D__IRQ_STATE, 1'b0, read_data);
if (read_data != 32'h0000_0001) $fatal(1, "GA2D IRQ set did not dominate W1C");
apb_write(`APB4_GA2D__IRQ_STATE, 32'h0000_0001, 4'hf, 1'b0);
apb_read(`APB4_GA2D__IRQ_STATE, 1'b0, read_data);
if (read_data != 32'd0 || irq_o) $fatal(1, "GA2D IRQ clear mismatch");
apb_write(`APB4_GA2D__IRQ_ENABLE, 32'h0000_0008, 4'hf, 1'b1);
@(negedge clk_i);
error_valid_i = 1'b1;
error_code_i = 7'd11;
error_stage_i = 4'd5;
error_axi_response_i = 2'd2;
error_address_i = 32'h3000_0040;
@(posedge clk_i);
#1;
error_valid_i = 1'b0;
apb_read(`APB4_GA2D__ERROR_STATUS, 1'b0, read_data);
if (read_data != 32'h0000_2517) $fatal(1, "GA2D first error status mismatch");
apb_read(`APB4_GA2D__ERROR_ADDRESS, 1'b0, read_data);
if (read_data != 32'h3000_0040) $fatal(1, "GA2D first error address mismatch");
apb_write(`APB4_GA2D__ERROR_STATUS, 32'h0000_0001, 4'hf, 1'b0);
apb_read(`APB4_GA2D__ERROR_STATUS, 1'b0, read_data);
if (read_data != 32'd0) $fatal(1, "GA2D error W1C mismatch");
held_soft_reset();
apb_read(`APB4_GA2D__GLOBAL_ALPHA, 1'b0, read_data);
if (read_data != 32'h0000_00ff) $fatal(1, "GA2D soft reset did not restore alpha");
apb_read(`APB4_GA2D__IRQ_ENABLE, 1'b0, read_data);
if (read_data != 32'd0) $fatal(1, "GA2D soft reset did not clear IRQ enable");
data_ready_i = 1'b0;
apb_write(`APB4_GA2D__COMMAND, 32'h0000_0004, 4'hf, 1'b1);
data_ready_i = 1'b1;
busy_i = 1'b1;
apb_write(`APB4_GA2D__COLOR, 32'h1122_3344, 4'hf, 1'b1);
busy_i = 1'b0;
busy_i = 1'b1;
draining_i = 1'b1;
quiesced_i = 1'b1;
data_ready_i = 1'b0;
done_i = 1'b1;
aborted_i = 1'b1;
recovery_required_i = 1'b1;
apb_read(`APB4_GA2D__STATUS, 1'b0, read_data);
if (read_data != 32'h0000_00b7) $fatal(1, "GA2D live status mismatch");
$display("GA2D P3 APB shell test passed");
$finish;
end
initial begin
repeat (400) @(posedge clk_i);
$fatal(1, "GA2D P3 APB shell test timed out");
end
endmodule
+259
View File
@@ -0,0 +1,259 @@
`timescale 1ns / 1ps
`include "ga2d_define.svh"
module ga2d_wrapper_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic resource_quiesce_i = 1'b0;
logic resource_reset_i = 1'b0;
logic source_stop_i = 1'b0;
logic source_safe_idle_i = 1'b1;
logic block_ack_i = 1'b0;
logic bridge_clear_busy_i = 1'b0;
logic [ 7:0] bridge_epoch_i = 8'd0;
logic data_ready_i = 1'b1;
logic idle_o;
logic irq_o;
logic [31:0] read_data;
apb4_if apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
apb4_ga2d u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.resource_quiesce_i (resource_quiesce_i),
.resource_reset_i (resource_reset_i),
.source_stop_i (source_stop_i),
.source_safe_idle_i (source_safe_idle_i),
.block_ack_i (block_ack_i),
.bridge_clear_busy_i(bridge_clear_busy_i),
.bridge_epoch_i (bridge_epoch_i),
.data_ready_i (data_ready_i),
.apb4 (apb4),
.idle_o (idle_o),
.irq_o (irq_o)
);
task automatic apb_write(input logic [11:0] offset_i, input logic [31:0] value_i,
input logic expected_error_i);
begin
@(negedge clk_i);
apb4.paddr = {20'd0, offset_i};
apb4.pwrite = 1'b1;
apb4.pwdata = value_i;
apb4.pstrb = 4'hf;
apb4.psel = 1'b1;
apb4.penable = 1'b1;
@(posedge clk_i);
#1;
if (!apb4.pready || (apb4.pslverr != expected_error_i)) begin
$fatal(1, "GA2D wrapper APB write response mismatch at %h", offset_i);
end
@(negedge clk_i);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pstrb = '0;
end
endtask
task automatic apb_read(input logic [11:0] offset_i, output logic [31:0] value_o);
begin
@(negedge clk_i);
apb4.paddr = {20'd0, offset_i};
apb4.pwrite = 1'b0;
apb4.pstrb = '0;
apb4.psel = 1'b1;
apb4.penable = 1'b1;
@(posedge clk_i);
#1;
if (!apb4.pready || apb4.pslverr) begin
$fatal(1, "GA2D wrapper APB read response mismatch at %h", offset_i);
end
value_o = apb4.prdata;
@(negedge clk_i);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
end
endtask
task automatic apb_lifecycle_soft_reset(input logic reset_request_i);
begin
@(negedge clk_i);
resource_quiesce_i = !reset_request_i;
resource_reset_i = reset_request_i;
apb4.paddr = {20'd0, `APB4_GA2D__COMMAND};
apb4.pwrite = 1'b1;
apb4.pwdata = 32'h0000_0004;
apb4.pstrb = 4'hf;
apb4.psel = 1'b1;
apb4.penable = 1'b1;
@(posedge clk_i);
#1;
if (!apb4.pready || !apb4.pslverr) begin
$fatal(1, "GA2D lifecycle request allowed unsafe soft reset");
end
@(negedge clk_i);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pstrb = '0;
end
endtask
initial begin
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_i);
rst_n_i = 1'b1;
if (!idle_o) $fatal(1, "GA2D wrapper did not expose source-safe idle");
apb_write(`APB4_GA2D__GLOBAL_ALPHA, 32'h0000_0055, 1'b0);
apb_write(`APB4_GA2D__IRQ_ENABLE, 32'h0000_0001, 1'b0);
apb_write(`APB4_GA2D__IRQ_TEST, 32'h0000_0001, 1'b0);
if (!irq_o) $fatal(1, "GA2D wrapper IRQ_TEST did not assert raw IRQ");
apb_lifecycle_soft_reset(1'b0);
apb_read(`APB4_GA2D__STATUS, read_data);
if (read_data[`APB4_GA2D__STATUS_DATA_READY]) begin
$fatal(1, "GA2D resource quiesce left data ready asserted");
end
apb_read(`APB4_GA2D__GLOBAL_ALPHA, read_data);
if (read_data != 32'h0000_0055) begin
$fatal(1, "GA2D resource quiesce allowed unsafe soft reset");
end
apb_read(`APB4_GA2D__IRQ_STATE, read_data);
if (read_data != 32'h0000_0001 || !irq_o) begin
$fatal(1, "GA2D resource quiesce cleared retained diagnostics");
end
resource_quiesce_i = 1'b0;
apb_write(`APB4_GA2D__COMMAND, 32'h0000_0004, 1'b0);
apb_read(`APB4_GA2D__GLOBAL_ALPHA, read_data);
if (read_data != 32'h0000_00ff) begin
$fatal(1, "GA2D post-quiesce soft reset did not clear configuration");
end
apb_read(`APB4_GA2D__IRQ_STATE, read_data);
if (read_data != 32'd0 || irq_o) begin
$fatal(1, "GA2D post-quiesce soft reset did not clear diagnostics");
end
apb_write(`APB4_GA2D__GLOBAL_ALPHA, 32'h0000_0055, 1'b0);
apb_write(`APB4_GA2D__IRQ_ENABLE, 32'h0000_0001, 1'b0);
apb_write(`APB4_GA2D__IRQ_TEST, 32'h0000_0001, 1'b0);
apb_lifecycle_soft_reset(1'b1);
apb_read(`APB4_GA2D__STATUS, read_data);
if (read_data[`APB4_GA2D__STATUS_DATA_READY]) begin
$fatal(1, "GA2D resource reset left data ready asserted");
end
apb_read(`APB4_GA2D__GLOBAL_ALPHA, read_data);
if (read_data != 32'h0000_0055) begin
$fatal(1, "GA2D resource reset allowed unsafe soft reset");
end
apb_read(`APB4_GA2D__IRQ_STATE, read_data);
if (read_data != 32'h0000_0001 || !irq_o) begin
$fatal(1, "GA2D resource reset cleared retained diagnostics");
end
resource_reset_i = 1'b0;
apb_write(`APB4_GA2D__COMMAND, 32'h0000_0004, 1'b0);
apb_read(`APB4_GA2D__GLOBAL_ALPHA, read_data);
if (read_data != 32'h0000_00ff) begin
$fatal(1, "GA2D post-reset soft reset did not clear configuration");
end
apb_read(`APB4_GA2D__IRQ_STATE, read_data);
if (read_data != 32'd0 || irq_o) begin
$fatal(1, "GA2D post-reset soft reset did not clear diagnostics");
end
apb_write(`APB4_GA2D__GLOBAL_ALPHA, 32'h0000_0055, 1'b0);
apb_write(`APB4_GA2D__IRQ_ENABLE, 32'h0000_0001, 1'b0);
apb_write(`APB4_GA2D__IRQ_TEST, 32'h0000_0001, 1'b0);
resource_reset_i = 1'b1;
source_stop_i = 1'b1;
source_safe_idle_i = 1'b0;
apb_read(`APB4_GA2D__STATUS, read_data);
if (read_data[`APB4_GA2D__STATUS_QUIESCED]) begin
$fatal(1, "GA2D resource reset quiesced before source drain");
end
if (!irq_o) $fatal(1, "GA2D resource reset cleared local IRQ state");
source_safe_idle_i = 1'b1;
apb_read(`APB4_GA2D__STATUS, read_data);
if (read_data[`APB4_GA2D__STATUS_QUIESCED]) begin
$fatal(1, "GA2D resource reset quiesced before HP block acknowledgement");
end
if (!idle_o) $fatal(1, "GA2D wrapper did not reassert idle after source drain");
block_ack_i = 1'b1;
apb_read(`APB4_GA2D__STATUS, read_data);
if (!read_data[`APB4_GA2D__STATUS_QUIESCED]) begin
$fatal(1, "GA2D resource reset did not report drain-and-hold quiesce");
end
apb_read(`APB4_GA2D__GLOBAL_ALPHA, read_data);
if (read_data != 32'h0000_0055) begin
$fatal(1, "GA2D resource reset cleared local configuration");
end
apb_read(`APB4_GA2D__IRQ_STATE, read_data);
if (read_data != 32'h0000_0001 || !irq_o) begin
$fatal(1, "GA2D resource reset cleared local diagnostic state");
end
bridge_clear_busy_i = 1'b1;
apb_read(`APB4_GA2D__STATUS, read_data);
if (!read_data[`APB4_GA2D__STATUS_RECOVERY_REQUIRED]) begin
$fatal(1, "GA2D wrapper missed bridge clear-busy recovery state");
end
bridge_clear_busy_i = 1'b0;
data_ready_i = 1'b0;
apb_read(`APB4_GA2D__STATUS, read_data);
if (!read_data[`APB4_GA2D__STATUS_RECOVERY_REQUIRED] ||
read_data[`APB4_GA2D__STATUS_DATA_READY]) begin
$fatal(1, "GA2D wrapper missed data-ready recovery state");
end
data_ready_i = 1'b1;
bridge_epoch_i = 8'd1;
#1;
if (!u_dut.s_epoch_changed) begin
$fatal(1, "GA2D wrapper missed bridge epoch change");
end
data_ready_i = 1'b0;
apb_read(`APB4_GA2D__STATUS, read_data);
if (!read_data[`APB4_GA2D__STATUS_RECOVERY_REQUIRED] ||
read_data[`APB4_GA2D__STATUS_DATA_READY]) begin
$fatal(1, "GA2D wrapper missed bridge epoch recovery state");
end
data_ready_i = 1'b1;
resource_reset_i = 1'b0;
source_stop_i = 1'b0;
block_ack_i = 1'b0;
apb_write(`APB4_GA2D__COMMAND, 32'h0000_0004, 1'b0);
apb_read(`APB4_GA2D__GLOBAL_ALPHA, read_data);
if (read_data != 32'h0000_00ff) begin
$fatal(1, "GA2D soft reset did not clear retained configuration");
end
apb_read(`APB4_GA2D__IRQ_STATE, read_data);
if (read_data != 32'd0 || irq_o) begin
$fatal(1, "GA2D soft reset did not clear retained diagnostic state");
end
$display("GA2D P3 wrapper lifecycle test passed");
$finish;
end
initial begin
repeat (400) @(posedge clk_i);
$fatal(1, "GA2D P3 wrapper lifecycle test timed out");
end
endmodule
+9 -3
View File
@@ -151,15 +151,21 @@ module resource_controller_tb;
$fatal(1, "APU resource index 7 ownership or IRQ routing mismatch");
end
irq_i[8] = 1'b1;
apb_read(12'h200, read_data);
if (read_data != 32'd0 || owner_o[8] != 2'd0 || owner_lock_o[8]) begin
if (read_data != 32'd0 || owner_o[8] != 2'd0 || owner_lock_o[8] || !irq_lp_o[8] ||
irq_hp_o[8]) begin
$fatal(1, "GA2D resource 8 did not reset to unlocked LP ownership");
end
apb_write(12'h204, 32'h0000_0001, 1'b0);
block_ack_i[8] = 1'b1;
apb_write(12'h200, 32'h0000_0001, 1'b0);
if ((owner_o[8] != 2'd1) || irq_lp_o[8] || irq_hp_o[8]) begin
$fatal(1, "GA2D resource 8 lifecycle or inactive IRQ mismatch");
if ((owner_o[8] != 2'd1) || irq_lp_o[8] || !irq_hp_o[8]) begin
$fatal(1, "GA2D resource 8 LP-to-HP IRQ handoff mismatch");
end
apb_write(12'h204, 32'h0000_0003, 1'b0);
if (irq_lp_o[8] || irq_hp_o[8]) begin
$fatal(1, "GA2D resource reset did not mask both IRQ routes");
end
$display("Resource Controller ownership, IRQ, and cache handshake test passed");
+23 -22
View File
@@ -27,24 +27,25 @@ module soc_irq_topology_tb;
s_apb4_periph_irq = '0;
s_apb4_periph_irq[bit_index] = 1'b1;
s_apb4_system_irq = '0;
expect_irq(32'd1 << bit_index);
expect_irq(64'd1 << bit_index);
end
for (int bit_index = 10; bit_index < `SOC_IRQ_APB4_PERIPH_WIDTH; bit_index++) begin
s_apb4_periph_irq = '0;
s_apb4_periph_irq[bit_index] = 1'b1;
case (bit_index)
13: expect_irq(32'd1 << 15);
14: expect_irq(32'd1 << 20);
16: expect_irq(32'd1 << 10);
17: expect_irq(32'd1 << 21);
18: expect_irq(32'd1 << 23);
19: expect_irq(32'd1 << 24);
20: expect_irq(32'd1 << 25);
21: expect_irq(32'd1 << 26);
22: expect_irq(32'd1 << 30);
23: expect_irq(32'd1 << 31);
default: expect_irq(32'd1 << (bit_index + 7));
13: expect_irq(64'd1 << 15);
14: expect_irq(64'd1 << 20);
16: expect_irq(64'd1 << 10);
17: expect_irq(64'd1 << 21);
18: expect_irq(64'd1 << 23);
19: expect_irq(64'd1 << 24);
20: expect_irq(64'd1 << 25);
21: expect_irq(64'd1 << 26);
22: expect_irq(64'd1 << 30);
23: expect_irq(64'd1 << 31);
24: expect_irq(64'd1 << 32);
default: expect_irq(64'd1 << (bit_index + 7));
endcase
end
@@ -53,22 +54,22 @@ module soc_irq_topology_tb;
s_apb4_system_irq = '0;
s_apb4_system_irq[bit_index] = 1'b1;
case (bit_index)
0: expect_irq(32'd1 << 11);
1: expect_irq(32'd1 << 12);
2: expect_irq(32'd1 << 13);
3: expect_irq(32'd1 << 14);
4: expect_irq(32'd1 << 16);
5: expect_irq(32'd1 << 27);
6: expect_irq(32'd1 << 28);
7: expect_irq(32'd1 << 29);
0: expect_irq(64'd1 << 11);
1: expect_irq(64'd1 << 12);
2: expect_irq(64'd1 << 13);
3: expect_irq(64'd1 << 14);
4: expect_irq(64'd1 << 16);
5: expect_irq(64'd1 << 27);
6: expect_irq(64'd1 << 28);
7: expect_irq(64'd1 << 29);
default: $fatal(1, "unexpected APB IRQ bit %0d", bit_index);
endcase
end
s_apb4_system_irq = '0;
expect_irq('0);
if (s_irq[63:32] !== '0) begin
$fatal(1, "unallocated high IRQ vector bits are not low: %h", s_irq[63:32]);
if (s_irq[63:33] !== '0) begin
$fatal(1, "unallocated high IRQ vector bits are not low: %h", s_irq[63:33]);
end
if ((s_irq[10] !== 1'b0) || (s_irq[21] !== 1'b0)) begin
$fatal(1, "SDIO core IRQ bits are not low when sources are low: IRQ10=%b IRQ21=%b",
+78
View File
@@ -0,0 +1,78 @@
"""Directed GA2D Phase 3 APB4 shell verification."""
from __future__ import annotations
import os
import shutil
import subprocess
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
def run_ga2d_rtl_test(
tmp_path: Path, top_module: str, testbench: str, success_marker: str
) -> None:
verilator = shutil.which("verilator")
if verilator is None:
pytest.fail("GA2D P3 RTL tests require Verilator")
common = ROOT / "rtl/managed/clusterip/common/rtl"
multimedia = ROOT / "rtl/ip/multimedia"
output = tmp_path / top_module
ccache_tmp = tmp_path / "ccache"
ccache_tmp.mkdir()
subprocess.run(
[
verilator,
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
top_module,
"-I" + str(common),
"-I" + str(common / "interface"),
"-I" + str(multimedia),
str(common / "interface/apb4_if.sv"),
str(common / "utils/register.sv"),
str(multimedia / "ga2d_pkg.sv"),
str(multimedia / "ga2d_reg.sv"),
str(multimedia / "apb4_ga2d.sv"),
str(ROOT / "tests/rtl" / testbench),
"-Mdir",
str(tmp_path / "obj"),
"-o",
str(output),
],
check=True,
capture_output=True,
text=True,
env={
**os.environ,
"CCACHE_DIR": str(ccache_tmp),
"CCACHE_TEMPDIR": str(ccache_tmp),
},
)
result = subprocess.run([output], check=True, capture_output=True, text=True)
assert success_marker in result.stdout
def test_ga2d_p3_apb_shell(tmp_path: Path) -> None:
run_ga2d_rtl_test(
tmp_path,
"ga2d_tb",
"ga2d_tb.sv",
"GA2D P3 APB shell test passed",
)
def test_ga2d_p3_wrapper_lifecycle(tmp_path: Path) -> None:
run_ga2d_rtl_test(
tmp_path,
"ga2d_wrapper_tb",
"ga2d_wrapper_tb.sv",
"GA2D P3 wrapper lifecycle test passed",
)
+31 -13
View File
@@ -1,4 +1,4 @@
"""Directed GA2D Phase 2 fabric and lifecycle verification."""
"""Directed GA2D Phase 2 fabric/lifecycle and Phase 3 shell boundaries."""
from __future__ import annotations
@@ -13,7 +13,7 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def test_ga2d_p2_preserves_deferred_apb_irq_and_payload_boundaries() -> None:
def test_ga2d_p3_activates_only_the_apb_shell_and_irq_route() -> None:
memory_map = json.loads(
(ROOT / "rtl/mini/address_map/memory_map.json").read_text(encoding="utf-8")
)
@@ -21,25 +21,43 @@ def test_ga2d_p2_preserves_deferred_apb_irq_and_payload_boundaries() -> None:
topology = json.loads(
(ROOT / "rtl/mini/integration/soc_topology.json").read_text(encoding="utf-8")
)
apb4_periph = (ROOT / "rtl/mini/top/apb4_periph.sv").read_text(encoding="utf-8")
apb4_system = (ROOT / "rtl/mini/top/apb4_system.sv").read_text(encoding="utf-8")
top = (ROOT / "rtl/mini/top/retrosoc.sv").read_text(encoding="utf-8")
assert regions["APB4_GA"] == {
"symbol": "APB4_GA",
assert "APB4_GA" not in regions
assert regions["APB4_GA2D"] == {
"symbol": "APB4_GA2D",
"base": "0x10012000",
"size": "0x00001000",
"route": "reserved",
"kind": "reserved",
"public": False,
"route": "apb4_periph",
"kind": "active",
"public": True,
"user_access": "none",
}
assert all("ga2d" not in interrupt["name"].lower() for interrupt in topology["interrupts"])
assert not list((ROOT / "rtl/ip/multimedia").glob("ga2d_*.sv"))
assert topology["apb4_periph_targets"][-1] == {
"slot": 28,
"name": "ga2d",
"timed_interface": "u_ga2d_apb4_if",
"pure_interface": "u_ga2d_apb4_pure_if",
"region": "APB4_GA2D",
}
assert topology["interrupts"][-1]["name"] == "ga2d"
assert topology["interrupts"][-1]["group_bit"] == 24
assert topology["interrupts"][-1]["core_bit"] == 32
assert topology["interrupts"][-1]["signal"] == "resource_irq_lp_i[7]"
assert {
"ga2d_pkg.sv",
"ga2d_reg.sv",
"apb4_ga2d.sv",
} <= {path.name for path in (ROOT / "rtl/ip/multimedia").glob("*ga2d*.sv")}
assert ".ResourceCount(9)" in apb4_system
assert "1'b0, resource_irq_i[6:5], s_ext_h_irq_raw, resource_irq_i[4:0]" in apb4_system
assert "resource_irq_lp_o = {s_resource_irq_lp[7:6], s_resource_irq_lp[4:0]}" in apb4_system
assert "s_ga2d_source_safe_idle && s_resource_idle_pclk[8]" in top
assert "s_ga2d_source_safe_idle && s_resource_block_ack_pclk[8]" in top
assert "resource_irq_i[7]" in apb4_system
assert "s_resource_irq_lp[8]" in apb4_system
assert "s_hp_plic_source[11] = resource_irq_hp_i[7];" in apb4_periph
assert "s_ga2d_idle && s_ga2d_source_safe_idle && s_resource_idle_pclk[8]" in top
assert "s_ga2d_idle && s_ga2d_source_safe_idle && s_resource_block_ack_pclk[8]" in top
assert "axi4_master_idle u_ga2d_master_idle" in top
def test_ga2d_p2_bridge_prefix_lifecycle_and_recovery(tmp_path: Path) -> None:
+377
View File
@@ -0,0 +1,377 @@
"""Keep the handwritten GA2D RTL and SDK register constants synchronized."""
from __future__ import annotations
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
RTL_DEFINE = ROOT / "rtl/ip/multimedia/ga2d_define.svh"
C_DEFINE = ROOT / "crt/include/retrosoc/hal/ga2d_regs.h"
C_API = ROOT / "crt/include/retrosoc/hal/ga2d.h"
def _literal_value(value: str) -> int:
value = value.replace("_", "")
if "'" in value:
_, encoded = value.split("'", maxsplit=1)
base = 16 if encoded[0].lower() == "h" else 10
return int(encoded[1:], base)
return int(value, 10)
def _rtl_values() -> dict[str, int]:
pattern = re.compile(
r"^`define\s+(APB4_GA2D__\w+)\s+((?:\d+)'[hHdD][0-9a-fA-F_]+|\d+)\s*$"
)
values: dict[str, int] = {}
for line in RTL_DEFINE.read_text(encoding="utf-8").splitlines():
match = pattern.match(line)
if match is not None:
values[match.group(1)] = _literal_value(match.group(2))
return values
def _c_values() -> dict[str, int]:
pattern = re.compile(r"^#define\s+(RS_GA2D_\w+)\s+(?:UINT32_C\((0x[0-9a-fA-F]+)\)|(\d+)U)\s*$")
values: dict[str, int] = {}
for line in C_DEFINE.read_text(encoding="utf-8").splitlines():
match = pattern.match(line)
if match is not None:
values[match.group(1)] = int(
match.group(2) if match.group(2) is not None else match.group(3), 0
)
return values
def _enum_values() -> dict[str, int]:
pattern = re.compile(r"^\s*(RS_GA2D_(?:OP|FORMAT)_\w+)\s*=\s*(\d+),\s*$")
values: dict[str, int] = {}
for line in C_API.read_text(encoding="utf-8").splitlines():
match = pattern.match(line)
if match is not None:
values[match.group(1)] = int(match.group(2), 10)
return values
OFFSETS = (
"IP_ID",
"IP_VERSION",
"CAPABILITY",
"LIMITS",
"COMMAND",
"STATUS",
"IRQ_STATE",
"IRQ_ENABLE",
"IRQ_TEST",
"ERROR_STATUS",
"ERROR_ADDRESS",
"TIMEOUT_CYCLES",
"JOB_CONFIG",
"GLOBAL_ALPHA",
"COLOR",
"SIZE",
"FG_ADDRESS",
"FG_PITCH",
"FG_FORMAT",
"BG_ADDRESS",
"BG_PITCH",
"BG_FORMAT",
"DST_ADDRESS",
"DST_PITCH",
"DST_FORMAT",
"PERF_SNAPSHOT",
"FORMAT_CAPABILITY",
"SNAP_CYCLES_LO",
"SNAP_CYCLES_HI",
"SNAP_READ_BYTES_LO",
"SNAP_READ_BYTES_HI",
"SNAP_WRITE_BYTES_LO",
"SNAP_WRITE_BYTES_HI",
"SNAP_READ_STALL_LO",
"SNAP_READ_STALL_HI",
"SNAP_WRITE_STALL_LO",
"SNAP_WRITE_STALL_HI",
"SNAP_PIPE_STALL_LO",
"SNAP_PIPE_STALL_HI",
"SNAP_LINES_DONE",
)
def test_ga2d_register_offsets_and_scalar_encodings_are_exhaustive() -> None:
rtl = _rtl_values()
c = _c_values()
offset_mapping = {f"APB4_GA2D__{name}": f"RS_GA2D_REG_{name}" for name in OFFSETS}
scalar_mapping = {
"APB4_GA2D__IP_ID_VALUE": "RS_GA2D_IP_ID_VALUE",
"APB4_GA2D__IP_VERSION_VALUE": "RS_GA2D_IP_VERSION_VALUE",
"APB4_GA2D__CAPABILITY_P3": "RS_GA2D_CAPABILITY_P3",
"APB4_GA2D__LIMITS_P3": "RS_GA2D_LIMITS_P3",
"APB4_GA2D__FORMAT_CAPABILITY_P3": "RS_GA2D_FORMAT_CAPABILITY_P3",
"APB4_GA2D__TIMEOUT_CYCLES_RESET": "RS_GA2D_TIMEOUT_CYCLES_RESET",
"APB4_GA2D__GLOBAL_ALPHA_RESET": "RS_GA2D_GLOBAL_ALPHA_RESET",
"APB4_GA2D__IRQ_ALL": "RS_GA2D_IRQ_ALL",
}
expected_c = set(offset_mapping.values()) | set(scalar_mapping.values())
for rtl_name, c_name in {**offset_mapping, **scalar_mapping}.items():
assert rtl[rtl_name] == c[c_name], f"{rtl_name} != {c_name}"
assert {name for name in rtl if name.endswith(tuple(OFFSETS))} >= set(offset_mapping)
assert expected_c <= set(c)
def test_ga2d_register_bit_shifts_and_enums_are_exhaustive() -> None:
rtl = _rtl_values()
c = _c_values()
enums = _enum_values()
bit_mapping = {
"APB4_GA2D__COMMAND_START": "RS_GA2D_COMMAND_START",
"APB4_GA2D__COMMAND_ABORT": "RS_GA2D_COMMAND_ABORT",
"APB4_GA2D__COMMAND_SOFT_RESET": "RS_GA2D_COMMAND_SOFT_RESET",
"APB4_GA2D__STATUS_BUSY": "RS_GA2D_STATUS_BUSY",
"APB4_GA2D__STATUS_DRAINING": "RS_GA2D_STATUS_DRAINING",
"APB4_GA2D__STATUS_QUIESCED": "RS_GA2D_STATUS_QUIESCED",
"APB4_GA2D__STATUS_DATA_READY": "RS_GA2D_STATUS_DATA_READY",
"APB4_GA2D__STATUS_DONE": "RS_GA2D_STATUS_DONE",
"APB4_GA2D__STATUS_ABORTED": "RS_GA2D_STATUS_ABORTED",
"APB4_GA2D__STATUS_ERROR": "RS_GA2D_STATUS_ERROR",
"APB4_GA2D__STATUS_RECOVERY_REQUIRED": "RS_GA2D_STATUS_RECOVERY_REQUIRED",
"APB4_GA2D__IRQ_DONE": "RS_GA2D_IRQ_DONE",
"APB4_GA2D__IRQ_ERROR": "RS_GA2D_IRQ_ERROR",
"APB4_GA2D__IRQ_ABORT_DONE": "RS_GA2D_IRQ_ABORT_DONE",
"APB4_GA2D__ERROR_STATUS_VALID": "RS_GA2D_ERROR_STATUS_VALID",
"APB4_GA2D__CAPABILITY_FILL": "RS_GA2D_CAPABILITY_FILL",
"APB4_GA2D__CAPABILITY_COPY": "RS_GA2D_CAPABILITY_COPY",
"APB4_GA2D__CAPABILITY_CONVERT": "RS_GA2D_CAPABILITY_CONVERT",
"APB4_GA2D__CAPABILITY_BLEND": "RS_GA2D_CAPABILITY_BLEND",
"APB4_GA2D__CAPABILITY_A8_MASK": "RS_GA2D_CAPABILITY_A8_MASK",
"APB4_GA2D__CAPABILITY_PRIVATE_DMA": "RS_GA2D_CAPABILITY_PRIVATE_DMA",
"APB4_GA2D__CAPABILITY_IRQ": "RS_GA2D_CAPABILITY_IRQ",
"APB4_GA2D__CAPABILITY_SNAPSHOT": "RS_GA2D_CAPABILITY_SNAPSHOT",
"APB4_GA2D__CAPABILITY_TWO_DIMENSIONAL_PITCH": (
"RS_GA2D_CAPABILITY_TWO_DIMENSIONAL_PITCH"
),
"APB4_GA2D__CAPABILITY_BYTE_EDGES": "RS_GA2D_CAPABILITY_BYTE_EDGES",
"APB4_GA2D__CAPABILITY_INPLACE_BACKGROUND": (
"RS_GA2D_CAPABILITY_INPLACE_BACKGROUND"
),
}
shift_mapping = {
"APB4_GA2D__ERROR_STATUS_CODE": "RS_GA2D_ERROR_STATUS_CODE_SHIFT",
"APB4_GA2D__ERROR_STATUS_STAGE": "RS_GA2D_ERROR_STATUS_STAGE_SHIFT",
"APB4_GA2D__ERROR_STATUS_AXI_RESPONSE": "RS_GA2D_ERROR_STATUS_AXI_RESPONSE_SHIFT",
"APB4_GA2D__JOB_CONFIG_OPERATION": "RS_GA2D_JOB_CONFIG_OPERATION_SHIFT",
"APB4_GA2D__GLOBAL_ALPHA_VALUE": "RS_GA2D_GLOBAL_ALPHA_VALUE_SHIFT",
"APB4_GA2D__SIZE_WIDTH": "RS_GA2D_SIZE_WIDTH_SHIFT",
"APB4_GA2D__SIZE_HEIGHT": "RS_GA2D_SIZE_HEIGHT_SHIFT",
"APB4_GA2D__FG_FORMAT_VALUE": "RS_GA2D_FG_FORMAT_VALUE_SHIFT",
"APB4_GA2D__BG_FORMAT_VALUE": "RS_GA2D_BG_FORMAT_VALUE_SHIFT",
"APB4_GA2D__DST_FORMAT_VALUE": "RS_GA2D_DST_FORMAT_VALUE_SHIFT",
"APB4_GA2D__FORMAT_CAPABILITY_FOREGROUND": (
"RS_GA2D_FORMAT_CAPABILITY_FOREGROUND_SHIFT"
),
"APB4_GA2D__FORMAT_CAPABILITY_BACKGROUND": (
"RS_GA2D_FORMAT_CAPABILITY_BACKGROUND_SHIFT"
),
"APB4_GA2D__FORMAT_CAPABILITY_DESTINATION": (
"RS_GA2D_FORMAT_CAPABILITY_DESTINATION_SHIFT"
),
}
error_mapping = {
f"APB4_GA2D__ERROR_{name}": f"RS_GA2D_ERROR_CODE_{name}"
for name in (
"NONE",
"INVALID_SIZE",
"INVALID_FORMAT",
"INVALID_PITCH",
"INVALID_ALIGNMENT",
"ADDRESS_OVERFLOW",
"ADDRESS_RANGE",
"OVERLAP",
"AXI_READ",
"AXI_WRITE",
"AXI_PROTOCOL",
"TIMEOUT",
"EPOCH_LOST",
"INTERNAL",
)
}
stage_mapping = {
f"APB4_GA2D__ERROR_STAGE_{name}": f"RS_GA2D_ERROR_STAGE_{name}"
for name in ("NONE", "VALIDATE", "FOREGROUND", "BACKGROUND", "DESTINATION", "LIFECYCLE")
}
enum_mapping = {
"APB4_GA2D__OP_FILL": "RS_GA2D_OP_FILL",
"APB4_GA2D__OP_COPY": "RS_GA2D_OP_COPY",
"APB4_GA2D__OP_CONVERT": "RS_GA2D_OP_CONVERT",
"APB4_GA2D__OP_BLEND": "RS_GA2D_OP_BLEND",
"APB4_GA2D__FORMAT_RGB565": "RS_GA2D_FORMAT_RGB565",
"APB4_GA2D__FORMAT_RGB888": "RS_GA2D_FORMAT_RGB888",
"APB4_GA2D__FORMAT_XRGB8888": "RS_GA2D_FORMAT_XRGB8888",
"APB4_GA2D__FORMAT_ARGB8888": "RS_GA2D_FORMAT_ARGB8888",
"APB4_GA2D__FORMAT_A8": "RS_GA2D_FORMAT_A8",
}
for rtl_name, c_name in bit_mapping.items():
assert c[c_name] == 1 << rtl[rtl_name], f"{rtl_name} != {c_name}"
for rtl_name, c_name in {**shift_mapping, **error_mapping, **stage_mapping}.items():
assert rtl[rtl_name] == c[c_name], f"{rtl_name} != {c_name}"
for rtl_name, enum_name in enum_mapping.items():
assert rtl[rtl_name] == enums[enum_name], f"{rtl_name} != {enum_name}"
assert set(enums) == set(enum_mapping.values())
def test_ga2d_handwritten_definition_sets_have_no_missing_or_extra_entries() -> None:
rtl = _rtl_values()
c = _c_values()
rtl_scalar_names = {
"IP_ID_VALUE",
"IP_VERSION_VALUE",
"CAPABILITY_P3",
"LIMITS_P3",
"FORMAT_CAPABILITY_P3",
"TIMEOUT_CYCLES_RESET",
"GLOBAL_ALPHA_RESET",
"IRQ_ALL",
}
rtl_bit_names = {
"COMMAND_START",
"COMMAND_ABORT",
"COMMAND_SOFT_RESET",
"STATUS_BUSY",
"STATUS_DRAINING",
"STATUS_QUIESCED",
"STATUS_DATA_READY",
"STATUS_DONE",
"STATUS_ABORTED",
"STATUS_ERROR",
"STATUS_RECOVERY_REQUIRED",
"IRQ_DONE",
"IRQ_ERROR",
"IRQ_ABORT_DONE",
"ERROR_STATUS_VALID",
"CAPABILITY_FILL",
"CAPABILITY_COPY",
"CAPABILITY_CONVERT",
"CAPABILITY_BLEND",
"CAPABILITY_A8_MASK",
"CAPABILITY_PRIVATE_DMA",
"CAPABILITY_IRQ",
"CAPABILITY_SNAPSHOT",
"CAPABILITY_TWO_DIMENSIONAL_PITCH",
"CAPABILITY_BYTE_EDGES",
"CAPABILITY_INPLACE_BACKGROUND",
}
rtl_shift_names = {
"ERROR_STATUS_CODE",
"ERROR_STATUS_STAGE",
"ERROR_STATUS_AXI_RESPONSE",
"JOB_CONFIG_OPERATION",
"GLOBAL_ALPHA_VALUE",
"SIZE_WIDTH",
"SIZE_HEIGHT",
"FG_FORMAT_VALUE",
"BG_FORMAT_VALUE",
"DST_FORMAT_VALUE",
"FORMAT_CAPABILITY_FOREGROUND",
"FORMAT_CAPABILITY_BACKGROUND",
"FORMAT_CAPABILITY_DESTINATION",
}
error_names = {
"NONE",
"INVALID_SIZE",
"INVALID_FORMAT",
"INVALID_PITCH",
"INVALID_ALIGNMENT",
"ADDRESS_OVERFLOW",
"ADDRESS_RANGE",
"OVERLAP",
"AXI_READ",
"AXI_WRITE",
"AXI_PROTOCOL",
"TIMEOUT",
"EPOCH_LOST",
"INTERNAL",
}
stage_names = {"NONE", "VALIDATE", "FOREGROUND", "BACKGROUND", "DESTINATION", "LIFECYCLE"}
operation_names = {"FILL", "COPY", "CONVERT", "BLEND"}
format_names = {"RGB565", "RGB888", "XRGB8888", "ARGB8888", "A8"}
expected_rtl = (
{f"APB4_GA2D__{name}" for name in OFFSETS}
| {f"APB4_GA2D__{name}" for name in rtl_scalar_names}
| {f"APB4_GA2D__{name}" for name in rtl_bit_names}
| {f"APB4_GA2D__{name}" for name in rtl_shift_names}
| {f"APB4_GA2D__ERROR_{name}" for name in error_names}
| {f"APB4_GA2D__ERROR_STAGE_{name}" for name in stage_names}
| {f"APB4_GA2D__OP_{name}" for name in operation_names}
| {f"APB4_GA2D__FORMAT_{name}" for name in format_names}
)
expected_c = (
{f"RS_GA2D_REG_{name}" for name in OFFSETS}
| {
"RS_GA2D_IP_ID_VALUE",
"RS_GA2D_IP_VERSION_VALUE",
"RS_GA2D_IP_VERSION_MAJOR_MASK",
"RS_GA2D_IP_VERSION_MAJOR_1",
"RS_GA2D_CAPABILITY_P3",
"RS_GA2D_LIMITS_P3",
"RS_GA2D_FORMAT_CAPABILITY_P3",
"RS_GA2D_TIMEOUT_CYCLES_RESET",
"RS_GA2D_GLOBAL_ALPHA_RESET",
"RS_GA2D_COMMAND_START",
"RS_GA2D_COMMAND_ABORT",
"RS_GA2D_COMMAND_SOFT_RESET",
"RS_GA2D_STATUS_BUSY",
"RS_GA2D_STATUS_DRAINING",
"RS_GA2D_STATUS_QUIESCED",
"RS_GA2D_STATUS_DATA_READY",
"RS_GA2D_STATUS_DONE",
"RS_GA2D_STATUS_ABORTED",
"RS_GA2D_STATUS_ERROR",
"RS_GA2D_STATUS_RECOVERY_REQUIRED",
"RS_GA2D_IRQ_DONE",
"RS_GA2D_IRQ_ERROR",
"RS_GA2D_IRQ_ABORT_DONE",
"RS_GA2D_IRQ_ALL",
"RS_GA2D_ERROR_STATUS_VALID",
"RS_GA2D_ERROR_STATUS_CODE_SHIFT",
"RS_GA2D_ERROR_STATUS_CODE_MASK",
"RS_GA2D_ERROR_STATUS_STAGE_SHIFT",
"RS_GA2D_ERROR_STATUS_STAGE_MASK",
"RS_GA2D_ERROR_STATUS_AXI_RESPONSE_SHIFT",
"RS_GA2D_ERROR_STATUS_AXI_RESPONSE_MASK",
"RS_GA2D_JOB_CONFIG_OPERATION_SHIFT",
"RS_GA2D_JOB_CONFIG_OPERATION_MASK",
"RS_GA2D_GLOBAL_ALPHA_VALUE_SHIFT",
"RS_GA2D_GLOBAL_ALPHA_VALUE_MASK",
"RS_GA2D_SIZE_WIDTH_SHIFT",
"RS_GA2D_SIZE_WIDTH_MASK",
"RS_GA2D_SIZE_HEIGHT_SHIFT",
"RS_GA2D_SIZE_HEIGHT_MASK",
"RS_GA2D_FG_FORMAT_VALUE_SHIFT",
"RS_GA2D_BG_FORMAT_VALUE_SHIFT",
"RS_GA2D_DST_FORMAT_VALUE_SHIFT",
"RS_GA2D_FORMAT_VALUE_MASK",
"RS_GA2D_FORMAT_CAPABILITY_FOREGROUND_SHIFT",
"RS_GA2D_FORMAT_CAPABILITY_BACKGROUND_SHIFT",
"RS_GA2D_FORMAT_CAPABILITY_DESTINATION_SHIFT",
"RS_GA2D_FORMAT_CAPABILITY_MASK",
}
| {f"RS_GA2D_CAPABILITY_{name}" for name in operation_names | {
"A8_MASK",
"PRIVATE_DMA",
"IRQ",
"SNAPSHOT",
"TWO_DIMENSIONAL_PITCH",
"BYTE_EDGES",
"INPLACE_BACKGROUND",
}}
| {f"RS_GA2D_ERROR_CODE_{name}" for name in error_names}
| {f"RS_GA2D_ERROR_STAGE_{name}" for name in stage_names}
)
assert set(rtl) == expected_rtl
assert set(c) == expected_c
+9 -6
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
import re
import shutil
import subprocess
import sys
@@ -45,14 +46,15 @@ def test_phase1_generates_64bit_vector_and_sdk_metadata(tmp_path: Path) -> None:
assert "`define SOC_IRQ_VECTOR_WIDTH 64" in irq_config
assert "`define SOC_USER_IRQ_MASK 64'h00000000004EFBFC" in irq_config
assert "s_irq[31] = s_apb4_periph_irq[23];" in irq_wiring
assert "s_irq[32]" not in irq_wiring
assert "irq_i[32] == 1'b0" in irq_sva
assert "s_irq[32] = s_apb4_periph_irq[24];" in irq_wiring
assert "irq_i[32] == apb4_periph_irq_i[24]" in irq_sva
assert "irq_i[63] == 1'b0" in irq_sva
assert "RS_SOC_IRQ_VECTOR_WIDTH UINT32_C(64)" in metadata
assert "RS_SOC_EXTERNAL_IRQ_COUNT UINT32_C(62)" in metadata
assert "RS_SOC_ALLOCATED_IRQ_COUNT UINT32_C(30)" in metadata
assert "RS_SOC_ALLOCATED_IRQ_COUNT UINT32_C(31)" in metadata
assert "RS_SOC_EXT_IRQ_APU UINT32_C(29)" in metadata
assert "RS_SOC_IRQ_GA2D" not in metadata
assert "RS_SOC_IRQ_GA2D UINT32_C(32)" in metadata
assert "RS_SOC_EXT_IRQ_GA2D UINT32_C(30)" in metadata
def test_phase1_supports_32bit_compatibility_metadata(tmp_path: Path) -> None:
@@ -128,10 +130,11 @@ def test_phase1_wrapper_and_backend_contracts() -> None:
assert "CSR_HAZARD3_MEIEA 0xbe0" in csr
assert "CSR_HAZARD3_MEINEXT 0xbe4" in csr
assert "CSR_HAZARD3_MEICONTEXT 0xbe5" in csr
assert "{0U, 15U, 16U, 29U, 30U, 31U, 32U, 61U}" in firmware
assert "static const uint32_t tested_ordinals[]" in firmware
assert re.search(r"29U,\s*RS_SOC_EXT_IRQ_GA2D,\s*31U", firmware) is not None
assert "rs_irq_register_external(62U" in firmware
assert "rs_irq_set_external_priority(29U, UINT8_C(0))" in firmware
assert "rs_irq_set_external_priority(30U, UINT8_C(1))" in firmware
assert "rs_irq_set_external_priority(RS_SOC_EXT_IRQ_GA2D, UINT8_C(1))" in firmware
assert "rs_irq_set_external_priority(31U, UINT8_C(2))" in firmware
assert "rs_irq_set_external_priority(32U, UINT8_C(3))" in firmware
assert "rs_irq_set_external_priority(15U, UINT8_C(3))" in firmware
+2
View File
@@ -125,6 +125,7 @@ def test_booter_prints_every_public_active_mmio_region() -> None:
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_GA2D_BASE" in mmio_function
assert "RS_SOC_APB4_APU_BASE" in mmio_function
@@ -178,6 +179,7 @@ def test_user_ip_is_always_emitted_for_the_fixed_platform(tmp_path: Path) -> Non
assert "#define RS_SOC_APB4_CRYPTO_BASE UINT32_C(0x1000C000)" in header
assert "#define RS_SOC_APB4_USB2_BASE UINT32_C(0x10016000)" in header
assert "#define RS_SOC_APB4_APU_BASE UINT32_C(0x10013000)" in header
assert "#define RS_SOC_APB4_GA2D_BASE UINT32_C(0x10012000)" in header
def test_bootstrap_assembly_uses_the_generated_gpio_admin_base() -> None:
+2 -1
View File
@@ -95,7 +95,8 @@ def test_application_results_keep_early_return_and_repeated_stage_values():
("return", 1), ("test-fail", 1), ("test-fail", 2), ("test-fail", 3), ("test-pass", 0)]
assert [r["id"] for r in smoke["stages"] if r["code"] == 12] == ["sram", "usb"]
assert [r["id"] for r in smoke["stages"] if r["code"] == 13] == ["monitor-start", "monitor-check"]
assert len(smoke["stages"]) == 18
assert [r["id"] for r in smoke["stages"] if r["code"] == 15] == ["external-irq", "ga2d-irq"]
assert len(smoke["stages"]) == 19
@pytest.mark.parametrize("mutation", ["omit", "duplicate-stage", "value", "scope", "trigger"])
+22 -10
View File
@@ -80,7 +80,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
).read_text(encoding="utf-8")
filelist = (tmp_path / "soc_topology.fl").read_text(encoding="utf-8")
assert interfaces.count("apb4_if u_") == 26
assert interfaces.count("apb4_if u_") == 27
assert "nmi_if" not in interfaces
assert "soc_nmi" not in interfaces
assert "assign s_psel_comb[17] = `SOC_ADDR_IS_APB4_I2C1(s_decode_addr);" in routes
@@ -97,6 +97,8 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "assign s_psel_comb[25] = `SOC_ADDR_IS_HP_PLIC(s_decode_addr);" in routes
assert "assign s_psel_comb[26] = `SOC_ADDR_IS_APB4_JPEG(s_decode_addr);" in routes
assert "assign s_psel_comb[27] = `SOC_ADDR_IS_APB4_APU(s_decode_addr);" in routes
assert "assign s_psel_comb[28] = `SOC_ADDR_IS_APB4_GA2D(s_decode_addr);" in routes
assert "apb4_if u_ga2d_apb4_if (clk_i, rst_n_i);" in interfaces
assert gpio.count("// GPIO") == 64
assert "u_uart1_if" not in gpio
assert "assign u_uart0_if.cts_n_i = u_gpio_if.di_i[0];" in gpio
@@ -145,7 +147,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert ".usb2_axi4(u_usb2_axi4_if)" in apb_periph_fabric
assert "`define SOC_IRQ_VECTOR_WIDTH 64" in irq_config
assert "`define SOC_USER_IRQ_MASK 64'h00000000004EFBFC" in irq_config
assert "`define SOC_IRQ_APB4_PERIPH_WIDTH 24" in irq_config
assert "`define SOC_IRQ_APB4_PERIPH_WIDTH 25" in irq_config
assert "`define SOC_IRQ_APB4_SYSTEM_WIDTH 8" in irq_config
assert "assign irq_o[0] = u_clint_if.software_irq_o[0];" in rib_irq
assert "assign irq_o[10] = ws2812.irq_o;" in rib_irq
@@ -160,6 +162,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "assign irq_o[19] = resource_irq_lp_i[1];" in rib_irq
assert "assign irq_o[22] = resource_irq_lp_i[5];" in rib_irq
assert "assign irq_o[23] = resource_irq_lp_i[6];" in rib_irq
assert "assign irq_o[24] = resource_irq_lp_i[7];" in rib_irq
assert "assign irq_o[0] = pwm.irq_o;" in apb_irq
assert "assign irq_o[4] = s_rng_irq;" in apb_irq
assert "assign irq_o[5] = s_ext_l_irq;" in apb_irq
@@ -178,6 +181,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "s_irq[29] = s_apb4_system_irq[7];" in irq_wiring
assert "s_irq[30] = s_apb4_periph_irq[22];" in irq_wiring
assert "s_irq[31] = s_apb4_periph_irq[23];" in irq_wiring
assert "s_irq[32] = s_apb4_periph_irq[24];" in irq_wiring
assert "irq_i[10] == apb4_periph_irq_i[16]" in irq_sva
assert "irq_i[15] == apb4_periph_irq_i[13]" in irq_sva
assert "irq_i[20] == apb4_periph_irq_i[14]" in irq_sva
@@ -187,6 +191,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "irq_i[24] == apb4_periph_irq_i[19]" in irq_sva
assert "irq_i[30] == apb4_periph_irq_i[22]" in irq_sva
assert "irq_i[31] == apb4_periph_irq_i[23]" in irq_sva
assert "irq_i[32] == apb4_periph_irq_i[24]" in irq_sva
assert "bind retrosoc soc_irq_topology_sva" in irq_sva
assert ".clk_i(clk_lp_i)" in irq_sva
assert ".rst_n_i(rst_lp_n_i)" in irq_sva
@@ -203,7 +208,8 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "RS_SOC_IRQ_UART0 UINT32_C(2)" in irq_metadata
assert "RS_SOC_EXT_IRQ_UART0 UINT32_C(0)" in irq_metadata
assert "RS_SOC_IRQ_APU" in irq_metadata
assert "RS_SOC_IRQ_GA2D" not in irq_metadata
assert "RS_SOC_IRQ_GA2D UINT32_C(32)" in irq_metadata
assert "RS_SOC_EXT_IRQ_GA2D UINT32_C(30)" in irq_metadata
assert filelist.startswith("+incdir+")
@@ -253,10 +259,11 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None:
("jpeg", "apb4_periph", 22, 30, "resource_irq_lp_i[5]"),
("resource_fault", "apb4_system", 7, 29, "s_resource_fault_irq"),
("apu", "apb4_periph", 23, 31, "resource_irq_lp_i[6]"),
("ga2d", "apb4_periph", 24, 32, "resource_irq_lp_i[7]"),
]
def test_ga2d_p2_topology_is_data_master_only() -> None:
def test_ga2d_p3_topology_activates_the_shell_without_a_data_source() -> None:
document = json.loads(TOPOLOGY.read_text(encoding="utf-8"))
policies = document["data_master_policies"]
@@ -280,11 +287,12 @@ def test_ga2d_p2_topology_is_data_master_only() -> None:
"allow_instruction": False,
"require_noncacheable": True,
}
assert all(
target["name"] != "ga2d"
for target in document["apb4_periph_targets"] + document["apb4_system_targets"]
)
assert all("ga2d" not in interrupt["name"] for interrupt in document["interrupts"])
assert document["apb4_periph_targets"][-1]["name"] == "ga2d"
assert document["apb4_periph_targets"][-1]["slot"] == 28
assert document["apb4_periph_targets"][-1]["region"] == "APB4_GA2D"
assert document["interrupts"][-1]["name"] == "ga2d"
assert document["interrupts"][-1]["group_bit"] == 24
assert document["interrupts"][-1]["core_bit"] == 32
def test_apu_p1_fixed_resource_and_hp_irq_allocations() -> None:
@@ -296,11 +304,15 @@ def test_apu_p1_fixed_resource_and_hp_irq_allocations() -> None:
)
assert "s_hp_plic_source[10] = resource_irq_hp_i[6];" in periph
assert "s_apu_irq_raw, s_jpeg_irq_raw" in periph
assert "s_hp_plic_source[11] = resource_irq_hp_i[7];" in periph
assert "s_ga2d_irq_raw," in periph
assert ".ResourceCount(9)" in system
assert ".apu_owner_i (s_resource_owner[7])" in top
assert ".apu_quiesce_i (s_resource_quiesce[7])" in top
assert ".apu_reset_i (s_resource_reset[7])" in top
compact_top = "".join(top.split())
assert ".ga2d_quiesce_i(s_resource_quiesce[8])" in compact_top
assert ".ga2d_reset_i(s_resource_reset[8])" in compact_top
assert "RS_RESOURCE_APU = 7" in resource_header
assert "RS_RESOURCE_COUNT = 9" in resource_header