diff --git a/README.md b/README.md index 06b7d2c2..64c6a883 100644 --- a/README.md +++ b/README.md @@ -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`, diff --git a/app/apps/ci_smoke/main.c b/app/apps/ci_smoke/main.c index 15ea0a7f..bf4ca74a 100644 --- a/app/apps/ci_smoke/main.c +++ b/app/apps/ci_smoke/main.c @@ -3,11 +3,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -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); } diff --git a/crt/README.md b/crt/README.md index 701d5697..d4c177ec 100644 --- a/crt/README.md +++ b/crt/README.md @@ -59,8 +59,8 @@ See the [extension contract](../docs/ip/extensions.md) and [legacy user-IP contract](../docs/ip/user-ip.md). `` 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). `` 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. +`` 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`. + `` 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 diff --git a/crt/include/retrosoc/hal/ga2d.h b/crt/include/retrosoc/hal/ga2d.h new file mode 100644 index 00000000..ddafae2f --- /dev/null +++ b/crt/include/retrosoc/hal/ga2d.h @@ -0,0 +1,93 @@ +#ifndef RETROSOC_HAL_GA2D_H +#define RETROSOC_HAL_GA2D_H + +#include +#include + +#include +#include + +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 diff --git a/crt/include/retrosoc/hal/ga2d_regs.h b/crt/include/retrosoc/hal/ga2d_regs.h new file mode 100644 index 00000000..76a3a24c --- /dev/null +++ b/crt/include/retrosoc/hal/ga2d_regs.h @@ -0,0 +1,138 @@ +#ifndef RETROSOC_HAL_GA2D_REGS_H +#define RETROSOC_HAL_GA2D_REGS_H + +#include + +#include + +#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 diff --git a/crt/src/hal/ga2d.c b/crt/src/hal/ga2d.c new file mode 100644 index 00000000..03688e0a --- /dev/null +++ b/crt/src/hal/ga2d.c @@ -0,0 +1,218 @@ +#include +#include + +#include + +#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; +} diff --git a/crt/src/service/booter.c b/crt/src/service/booter.c index ffa4ab55..89f5ace3 100644 --- a/crt/src/service/booter.c +++ b/crt/src/service/booter.c @@ -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", diff --git a/docs/axi4-interconnect.md b/docs/axi4-interconnect.md index bf77a8cb..3eba5f61 100644 --- a/docs/axi4-interconnect.md +++ b/docs/axi4-interconnect.md @@ -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 | diff --git a/docs/commercial-soc-alignment.md b/docs/commercial-soc-alignment.md index 08a9efdb..903de9d0 100644 --- a/docs/commercial-soc-alignment.md +++ b/docs/commercial-soc-alignment.md @@ -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: diff --git a/docs/ip/fabric-monitor.md b/docs/ip/fabric-monitor.md index a13c9f67..5bea9bab 100644 --- a/docs/ip/fabric-monitor.md +++ b/docs/ip/fabric-monitor.md @@ -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 diff --git a/docs/ip/resource-controller.md b/docs/ip/resource-controller.md index b49f135e..a0ed022e 100644 --- a/docs/ip/resource-controller.md +++ b/docs/ip/resource-controller.md @@ -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 diff --git a/docs/ip/sysctrl.md b/docs/ip/sysctrl.md index 06c189be..bf455260 100644 --- a/docs/ip/sysctrl.md +++ b/docs/ip/sysctrl.md @@ -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. | diff --git a/docs/lp-hp-architecture.md b/docs/lp-hp-architecture.md index e98c7a64..fc39ee86 100644 --- a/docs/lp-hp-architecture.md +++ b/docs/lp-hp-architecture.md @@ -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 ``. 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 diff --git a/docs/soc-family-positioning.md b/docs/soc-family-positioning.md index cc33cddc..841ce3a8 100644 --- a/docs/soc-family-positioning.md +++ b/docs/soc-family-positioning.md @@ -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. diff --git a/publications/datasheets/diagram-circuits.json b/publications/datasheets/diagram-circuits.json index 6b1ff36f..ef091dc1 100644 --- a/publications/datasheets/diagram-circuits.json +++ b/publications/datasheets/diagram-circuits.json @@ -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": [ diff --git a/publications/datasheets/features.json b/publications/datasheets/features.json index a7bd07c7..547a4e3c 100644 --- a/publications/datasheets/features.json +++ b/publications/datasheets/features.json @@ -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." ], diff --git a/publications/datasheets/ip-catalog.json b/publications/datasheets/ip-catalog.json index fc5b10b0..17570bda 100644 --- a/publications/datasheets/ip-catalog.json +++ b/publications/datasheets/ip-catalog.json @@ -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." } ] diff --git a/publications/datasheets/ips/resource.typ b/publications/datasheets/ips/resource.typ index 0d5956a3..04bd7328 100644 --- a/publications/datasheets/ips/resource.typ +++ b/publications/datasheets/ips/resource.typ @@ -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] +#register-section("ga2d",4) + #context metadata((kind:"ip-end",id:"resource",page:here().page())) diff --git a/publications/datasheets/overview-groups.json b/publications/datasheets/overview-groups.json index 93ad95e7..dceb00dd 100644 --- a/publications/datasheets/overview-groups.json +++ b/publications/datasheets/overview-groups.json @@ -76,7 +76,8 @@ "I2S", "DVP", "JPEG", - "APU" + "APU", + "GA2D" ] }, { diff --git a/publications/datasheets/register-annotations.json b/publications/datasheets/register-annotations.json index 71328bf2..641b9c39 100644 --- a/publications/datasheets/register-annotations.json +++ b/publications/datasheets/register-annotations.json @@ -1976,6 +1976,1024 @@ "reset": "0" } }, + "ga2d": { + "main.IP_ID": { + "access": "RO", + "reset": "0x47413244", + "description": "Fixed GA2D identifier.", + "fields": [ + { + "name": "IDENTIFIER", + "lsb": 0, + "msb": 31, + "description": "Fixed value 0x47413244 (GA2D).", + "reset": "0x47413244", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.IP_VERSION": { + "access": "RO", + "reset": "0x00010000", + "description": "GA2D register ABI version.", + "fields": [ + { + "name": "MINOR", + "lsb": 0, + "msb": 15, + "description": "ABI minor version.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "MAJOR", + "lsb": 16, + "msb": 31, + "description": "ABI major version; P3 implements major 1.", + "reset": "1", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.CAPABILITY": { + "access": "RO", + "reset": "0x00000040", + "description": "P3 capability bitmap; only IRQ routing is advertised.", + "fields": [ + { + "name": "FILL", + "lsb": 0, + "msb": 0, + "description": "Fill operation capability; clear in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "COPY", + "lsb": 1, + "msb": 1, + "description": "Copy operation capability; clear in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "CONVERT", + "lsb": 2, + "msb": 2, + "description": "Conversion operation capability; clear in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "BLEND", + "lsb": 3, + "msb": 3, + "description": "Blend operation capability; clear in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "A8_MASK", + "lsb": 4, + "msb": 4, + "description": "A8 mask capability; clear in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "PRIVATE_DMA", + "lsb": 5, + "msb": 5, + "description": "Private AXI DMA capability; clear in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "IRQ", + "lsb": 6, + "msb": 6, + "description": "Shell IRQ capability.", + "reset": "1", + "expression": "reviewed P3 ABI" + }, + { + "name": "SNAPSHOT", + "lsb": 7, + "msb": 7, + "description": "Performance snapshot capability; clear in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "TWO_DIMENSIONAL_PITCH", + "lsb": 8, + "msb": 8, + "description": "Two-dimensional pitch capability; clear in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "BYTE_EDGES", + "lsb": 9, + "msb": 9, + "description": "Byte-edge DMA capability; clear in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "INPLACE_BACKGROUND", + "lsb": 10, + "msb": 10, + "description": "In-place background capability; clear in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 11, + "msb": 31, + "description": "Reserved; reads zero.", + "reset": "0", + "expression": "reserved" + } + ] + }, + "main.LIMITS": { + "access": "RO", + "reset": "0x00000000", + "description": "DMA geometry limits; all fields are zero until private DMA is implemented.", + "fields": [ + { + "name": "MAX_BURST_BEATS", + "lsb": 0, + "msb": 7, + "description": "Maximum DMA burst beats; zero in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "INPUT_FIFO_DEPTH", + "lsb": 8, + "msb": 15, + "description": "Input FIFO depth; zero in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "OUTPUT_FIFO_DEPTH", + "lsb": 16, + "msb": 23, + "description": "Output FIFO depth; zero in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "AXI_BEAT_BYTES", + "lsb": 24, + "msb": 31, + "description": "AXI DMA beat width; zero in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.COMMAND": { + "access": "WO", + "reset": "Not retained", + "description": "Single-command write port; START is unsupported in P3.", + "fields": [ + { + "name": "START", + "lsb": 0, + "msb": 0, + "description": "Start a job; rejected with PSLVERR in P3.", + "reset": "Not retained", + "expression": "reviewed P3 ABI" + }, + { + "name": "ABORT", + "lsb": 1, + "msb": 1, + "description": "Request a safe abort; idle P3 shell is a no-op.", + "reset": "Not retained", + "expression": "reviewed P3 ABI" + }, + { + "name": "SOFT_RESET", + "lsb": 2, + "msb": 2, + "description": "Clear local shell state only when safe idle and recovered.", + "reset": "Not retained", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 3, + "msb": 31, + "description": "Reserved; write zero.", + "reset": "Not retained", + "expression": "reserved" + } + ] + }, + "main.STATUS": { + "access": "RO", + "reset": "Dynamic", + "description": "Live shell, lifecycle, and recovery status.", + "fields": [ + { + "name": "BUSY", + "lsb": 0, + "msb": 0, + "description": "Live job-active status; clear in the P3 shell.", + "reset": "Dynamic", + "expression": "reviewed P3 ABI" + }, + { + "name": "DRAINING", + "lsb": 1, + "msb": 1, + "description": "Live drain status; clear in the P3 shell.", + "reset": "Dynamic", + "expression": "reviewed P3 ABI" + }, + { + "name": "QUIESCED", + "lsb": 2, + "msb": 2, + "description": "Resource stop has reached safe source idle and block acknowledgement.", + "reset": "Dynamic", + "expression": "reviewed P3 ABI" + }, + { + "name": "DATA_READY", + "lsb": 3, + "msb": 3, + "description": "Qualified bridge data readiness.", + "reset": "Dynamic", + "expression": "reviewed P3 ABI" + }, + { + "name": "DONE", + "lsb": 4, + "msb": 4, + "description": "Last job completed; clear in the P3 shell.", + "reset": "Dynamic", + "expression": "reviewed P3 ABI" + }, + { + "name": "ABORTED", + "lsb": 5, + "msb": 5, + "description": "Last job aborted; clear in the P3 shell.", + "reset": "Dynamic", + "expression": "reviewed P3 ABI" + }, + { + "name": "ERROR", + "lsb": 6, + "msb": 6, + "description": "Sticky first-error validity.", + "reset": "Dynamic", + "expression": "reviewed P3 ABI" + }, + { + "name": "RECOVERY_REQUIRED", + "lsb": 7, + "msb": 7, + "description": "Bridge clear, reset epoch, or data-readiness recovery is unresolved.", + "reset": "Dynamic", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 8, + "msb": 31, + "description": "Reserved; reads zero.", + "reset": "0", + "expression": "reserved" + } + ] + }, + "main.IRQ_STATE": { + "access": "RW1C", + "reset": "0x00000000", + "description": "Sticky IRQ causes; a same-cycle new event wins over a clear.", + "fields": [ + { + "name": "DONE", + "lsb": 0, + "msb": 0, + "description": "Successful terminal job event.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "ERROR", + "lsb": 1, + "msb": 1, + "description": "First-error event.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "ABORT_DONE", + "lsb": 2, + "msb": 2, + "description": "Safe abort completion event.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 3, + "msb": 31, + "description": "Reserved; write zero.", + "reset": "0", + "expression": "reserved" + } + ] + }, + "main.IRQ_ENABLE": { + "access": "RW", + "reset": "0x00000000", + "description": "Interrupt-cause enable mask.", + "fields": [ + { + "name": "DONE", + "lsb": 0, + "msb": 0, + "description": "Enable DONE raw IRQ contribution.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "ERROR", + "lsb": 1, + "msb": 1, + "description": "Enable ERROR raw IRQ contribution.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "ABORT_DONE", + "lsb": 2, + "msb": 2, + "description": "Enable ABORT_DONE raw IRQ contribution.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 3, + "msb": 31, + "description": "Reserved; write zero.", + "reset": "0", + "expression": "reserved" + } + ] + }, + "main.IRQ_TEST": { + "access": "WO", + "reset": "Not retained", + "description": "Set selected IRQ_STATE causes for route testing without job progress.", + "fields": [ + { + "name": "DONE", + "lsb": 0, + "msb": 0, + "description": "Set DONE state for route testing.", + "reset": "Not retained", + "expression": "reviewed P3 ABI" + }, + { + "name": "ERROR", + "lsb": 1, + "msb": 1, + "description": "Set ERROR state for route testing.", + "reset": "Not retained", + "expression": "reviewed P3 ABI" + }, + { + "name": "ABORT_DONE", + "lsb": 2, + "msb": 2, + "description": "Set ABORT_DONE state for route testing.", + "reset": "Not retained", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 3, + "msb": 31, + "description": "Reserved; write zero.", + "reset": "Not retained", + "expression": "reserved" + } + ] + }, + "main.ERROR_STATUS": { + "access": "RO/W1C", + "reset": "0x00000000", + "description": "Sticky first-error record; write one clears a safe idle record.", + "fields": [ + { + "name": "VALID", + "lsb": 0, + "msb": 0, + "description": "First-error record is valid.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "CODE", + "lsb": 1, + "msb": 7, + "description": "First-error code.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "STAGE", + "lsb": 8, + "msb": 11, + "description": "First-error processing stage.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "AXI_RESPONSE", + "lsb": 12, + "msb": 13, + "description": "Captured AXI response for a bus error.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 14, + "msb": 31, + "description": "Reserved; reads zero.", + "reset": "0", + "expression": "reserved" + } + ] + }, + "main.ERROR_ADDRESS": { + "access": "RO", + "reset": "0x00000000", + "description": "Address associated with the sticky first-error record.", + "fields": [ + { + "name": "ADDRESS", + "lsb": 0, + "msb": 31, + "description": "Captured first-error byte address.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.TIMEOUT_CYCLES": { + "access": "RW idle", + "reset": "0x00100000", + "description": "Idle-only no-progress timeout threshold; zero is rejected.", + "fields": [ + { + "name": "CYCLES", + "lsb": 0, + "msb": 31, + "description": "PCLK no-progress timeout budget.", + "reset": "0x00100000", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.JOB_CONFIG": { + "access": "RW idle", + "reset": "0x00000000", + "description": "Shadow job operation selector; no operation capability is advertised in P3.", + "fields": [ + { + "name": "OPERATION", + "lsb": 0, + "msb": 1, + "description": "0: FILL; 1: COPY; 2: CONVERT; 3: BLEND.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 2, + "msb": 31, + "description": "Reserved; write zero.", + "reset": "0", + "expression": "reserved" + } + ] + }, + "main.GLOBAL_ALPHA": { + "access": "RW idle", + "reset": "0x000000FF", + "description": "Shadow global alpha value.", + "fields": [ + { + "name": "VALUE", + "lsb": 0, + "msb": 7, + "description": "Global alpha value.", + "reset": "0xFF", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 8, + "msb": 31, + "description": "Reserved; write zero.", + "reset": "0", + "expression": "reserved" + } + ] + }, + "main.COLOR": { + "access": "RW idle", + "reset": "0x00000000", + "description": "Shadow constant color in AARRGGBB order.", + "fields": [ + { + "name": "COLOR", + "lsb": 0, + "msb": 31, + "description": "AARRGGBB constant color.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.SIZE": { + "access": "RW idle", + "reset": "0x00000000", + "description": "Shadow job dimensions.", + "fields": [ + { + "name": "WIDTH", + "lsb": 0, + "msb": 15, + "description": "Job width in pixels.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "HEIGHT", + "lsb": 16, + "msb": 31, + "description": "Job height in pixels.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.FG_ADDRESS": { + "access": "RW idle", + "reset": "0x00000000", + "description": "Shadow foreground byte address.", + "fields": [ + { + "name": "ADDRESS", + "lsb": 0, + "msb": 31, + "description": "Foreground byte address.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.FG_PITCH": { + "access": "RW idle", + "reset": "0x00000000", + "description": "Shadow foreground row pitch in bytes.", + "fields": [ + { + "name": "PITCH", + "lsb": 0, + "msb": 31, + "description": "Foreground bytes between rows.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.FG_FORMAT": { + "access": "RW idle", + "reset": "0x00000000", + "description": "Shadow foreground format selector.", + "fields": [ + { + "name": "FORMAT", + "lsb": 0, + "msb": 2, + "description": "0: RGB565; 1: RGB888; 2: XRGB8888; 3: ARGB8888; 4: A8.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 3, + "msb": 31, + "description": "Reserved; write zero.", + "reset": "0", + "expression": "reserved" + } + ] + }, + "main.BG_ADDRESS": { + "access": "RW idle", + "reset": "0x00000000", + "description": "Shadow background byte address.", + "fields": [ + { + "name": "ADDRESS", + "lsb": 0, + "msb": 31, + "description": "Background byte address.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.BG_PITCH": { + "access": "RW idle", + "reset": "0x00000000", + "description": "Shadow background row pitch in bytes.", + "fields": [ + { + "name": "PITCH", + "lsb": 0, + "msb": 31, + "description": "Background bytes between rows.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.BG_FORMAT": { + "access": "RW idle", + "reset": "0x00000000", + "description": "Shadow background format selector.", + "fields": [ + { + "name": "FORMAT", + "lsb": 0, + "msb": 2, + "description": "0: RGB565; 1: RGB888; 2: XRGB8888; 3: ARGB8888; 4: A8.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 3, + "msb": 31, + "description": "Reserved; write zero.", + "reset": "0", + "expression": "reserved" + } + ] + }, + "main.DST_ADDRESS": { + "access": "RW idle", + "reset": "0x00000000", + "description": "Shadow destination byte address.", + "fields": [ + { + "name": "ADDRESS", + "lsb": 0, + "msb": 31, + "description": "Destination byte address.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.DST_PITCH": { + "access": "RW idle", + "reset": "0x00000000", + "description": "Shadow destination row pitch in bytes.", + "fields": [ + { + "name": "PITCH", + "lsb": 0, + "msb": 31, + "description": "Destination bytes between rows.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.DST_FORMAT": { + "access": "RW idle", + "reset": "0x00000000", + "description": "Shadow destination format selector.", + "fields": [ + { + "name": "FORMAT", + "lsb": 0, + "msb": 2, + "description": "0: RGB565; 1: RGB888; 2: XRGB8888; 3: ARGB8888; 4: A8.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 3, + "msb": 31, + "description": "Reserved; write zero.", + "reset": "0", + "expression": "reserved" + } + ] + }, + "main.PERF_SNAPSHOT": { + "access": "WO", + "reset": "Not retained", + "description": "Performance snapshot command; rejected with PSLVERR in P3.", + "fields": [ + { + "name": "SNAPSHOT", + "lsb": 0, + "msb": 0, + "description": "Capture live counters; unsupported in P3.", + "reset": "Not retained", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 1, + "msb": 31, + "description": "Reserved; write zero.", + "reset": "Not retained", + "expression": "reserved" + } + ] + }, + "main.FORMAT_CAPABILITY": { + "access": "RO", + "reset": "0x00000000", + "description": "Format support bitmap; all fields are zero in P3.", + "fields": [ + { + "name": "FOREGROUND", + "lsb": 0, + "msb": 4, + "description": "Foreground format capability bits; zero in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 5, + "msb": 7, + "description": "Reserved; reads zero.", + "reset": "0", + "expression": "reserved" + }, + { + "name": "BACKGROUND", + "lsb": 8, + "msb": 12, + "description": "Background format capability bits; zero in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 13, + "msb": 15, + "description": "Reserved; reads zero.", + "reset": "0", + "expression": "reserved" + }, + { + "name": "DESTINATION", + "lsb": 16, + "msb": 20, + "description": "Destination format capability bits; zero in P3.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 21, + "msb": 31, + "description": "Reserved; reads zero.", + "reset": "0", + "expression": "reserved" + } + ] + }, + "main.SNAP_CYCLES_LO": { + "access": "RO", + "reset": "0x00000000", + "description": "Low 32 bits of the active-job PCLK cycle snapshot; zero in P3.", + "fields": [ + { + "name": "VALUE", + "lsb": 0, + "msb": 31, + "description": "Low cycle-counter snapshot word.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.SNAP_CYCLES_HI": { + "access": "RO", + "reset": "0x00000000", + "description": "High 32 bits of the active-job PCLK cycle snapshot; zero in P3.", + "fields": [ + { + "name": "VALUE", + "lsb": 0, + "msb": 31, + "description": "High cycle-counter snapshot word.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.SNAP_READ_BYTES_LO": { + "access": "RO", + "reset": "0x00000000", + "description": "Low 32 bits of successful logical input bytes; zero in P3.", + "fields": [ + { + "name": "VALUE", + "lsb": 0, + "msb": 31, + "description": "Low read-byte snapshot word.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.SNAP_READ_BYTES_HI": { + "access": "RO", + "reset": "0x00000000", + "description": "High 32 bits of successful logical input bytes; zero in P3.", + "fields": [ + { + "name": "VALUE", + "lsb": 0, + "msb": 31, + "description": "High read-byte snapshot word.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.SNAP_WRITE_BYTES_LO": { + "access": "RO", + "reset": "0x00000000", + "description": "Low 32 bits of acknowledged output bytes; zero in P3.", + "fields": [ + { + "name": "VALUE", + "lsb": 0, + "msb": 31, + "description": "Low write-byte snapshot word.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.SNAP_WRITE_BYTES_HI": { + "access": "RO", + "reset": "0x00000000", + "description": "High 32 bits of acknowledged output bytes; zero in P3.", + "fields": [ + { + "name": "VALUE", + "lsb": 0, + "msb": 31, + "description": "High write-byte snapshot word.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.SNAP_READ_STALL_LO": { + "access": "RO", + "reset": "0x00000000", + "description": "Low 32 bits of read-channel stall cycles; zero in P3.", + "fields": [ + { + "name": "VALUE", + "lsb": 0, + "msb": 31, + "description": "Low read-stall snapshot word.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.SNAP_READ_STALL_HI": { + "access": "RO", + "reset": "0x00000000", + "description": "High 32 bits of read-channel stall cycles; zero in P3.", + "fields": [ + { + "name": "VALUE", + "lsb": 0, + "msb": 31, + "description": "High read-stall snapshot word.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.SNAP_WRITE_STALL_LO": { + "access": "RO", + "reset": "0x00000000", + "description": "Low 32 bits of write-channel stall cycles; zero in P3.", + "fields": [ + { + "name": "VALUE", + "lsb": 0, + "msb": 31, + "description": "Low write-stall snapshot word.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.SNAP_WRITE_STALL_HI": { + "access": "RO", + "reset": "0x00000000", + "description": "High 32 bits of write-channel stall cycles; zero in P3.", + "fields": [ + { + "name": "VALUE", + "lsb": 0, + "msb": 31, + "description": "High write-stall snapshot word.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.SNAP_PIPE_STALL_LO": { + "access": "RO", + "reset": "0x00000000", + "description": "Low 32 bits of pixel-pipeline stall cycles; zero in P3.", + "fields": [ + { + "name": "VALUE", + "lsb": 0, + "msb": 31, + "description": "Low pipeline-stall snapshot word.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.SNAP_PIPE_STALL_HI": { + "access": "RO", + "reset": "0x00000000", + "description": "High 32 bits of pixel-pipeline stall cycles; zero in P3.", + "fields": [ + { + "name": "VALUE", + "lsb": 0, + "msb": 31, + "description": "High pipeline-stall snapshot word.", + "reset": "0", + "expression": "reviewed P3 ABI" + } + ] + }, + "main.SNAP_LINES_DONE": { + "access": "RO", + "reset": "0x00000000", + "description": "Completed destination rows; zero in P3.", + "fields": [ + { + "name": "LINES_DONE", + "lsb": 0, + "msb": 15, + "description": "Completed destination row count.", + "reset": "0", + "expression": "reviewed P3 ABI" + }, + { + "name": "Reserved", + "lsb": 16, + "msb": 31, + "description": "Reserved; reads zero.", + "reset": "0", + "expression": "reserved" + } + ] + } + }, "crypto": { "main.COMMAND": { "fields": [ diff --git a/publications/datasheets/register-profiles.json b/publications/datasheets/register-profiles.json index dc1b3767..23bedc33 100644 --- a/publications/datasheets/register-profiles.json +++ b/publications/datasheets/register-profiles.json @@ -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", diff --git a/publications/datasheets/system-reference.json b/publications/datasheets/system-reference.json index 9d19ef56..38b89fcc 100644 --- a/publications/datasheets/system-reference.json +++ b/publications/datasheets/system-reference.json @@ -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", diff --git a/rtl/README.md b/rtl/README.md index 70d8a9b9..00c5de53 100644 --- a/rtl/README.md +++ b/rtl/README.md @@ -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 diff --git a/rtl/ip/multimedia/apb4_ga2d.sv b/rtl/ip/multimedia/apb4_ga2d.sv new file mode 100644 index 00000000..47672357 --- /dev/null +++ b/rtl/ip/multimedia/apb4_ga2d.sv @@ -0,0 +1,74 @@ +// Copyright (c) 2026 Yuchi Miao +// 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 diff --git a/rtl/ip/multimedia/ga2d_define.svh b/rtl/ip/multimedia/ga2d_define.svh new file mode 100644 index 00000000..4b812b3f --- /dev/null +++ b/rtl/ip/multimedia/ga2d_define.svh @@ -0,0 +1,137 @@ +// Copyright (c) 2026 Yuchi Miao +// 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 diff --git a/rtl/ip/multimedia/ga2d_pkg.sv b/rtl/ip/multimedia/ga2d_pkg.sv new file mode 100644 index 00000000..fb4bb1a2 --- /dev/null +++ b/rtl/ip/multimedia/ga2d_pkg.sv @@ -0,0 +1,46 @@ +// Copyright (c) 2026 Yuchi Miao +// 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 diff --git a/rtl/ip/multimedia/ga2d_reg.sv b/rtl/ip/multimedia/ga2d_reg.sv new file mode 100644 index 00000000..055afbcb --- /dev/null +++ b/rtl/ip/multimedia/ga2d_reg.sv @@ -0,0 +1,372 @@ +// Copyright (c) 2026 Yuchi Miao +// 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 diff --git a/rtl/mini/address_map/memory_map.json b/rtl/mini/address_map/memory_map.json index 5116dd43..aad35ec3 100644 --- a/rtl/mini/address_map/memory_map.json +++ b/rtl/mini/address_map/memory_map.json @@ -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" }, { diff --git a/rtl/mini/filelist/ip.fl b/rtl/mini/filelist/ip.fl index 0dbb68bc..9ed7e0d3 100644 --- a/rtl/mini/filelist/ip.fl +++ b/rtl/mini/filelist/ip.fl @@ -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 diff --git a/rtl/mini/integration/README.md b/rtl/mini/integration/README.md index 36f87c56..8a43456d 100644 --- a/rtl/mini/integration/README.md +++ b/rtl/mini/integration/README.md @@ -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 diff --git a/rtl/mini/integration/soc_topology.json b/rtl/mini/integration/soc_topology.json index 31e82ba8..10df6506 100644 --- a/rtl/mini/integration/soc_topology.json +++ b/rtl/mini/integration/soc_topology.json @@ -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": [ diff --git a/rtl/mini/mk/software.mk b/rtl/mini/mk/software.mk index 00c5c90a..76ac44c5 100644 --- a/rtl/mini/mk/software.mk +++ b/rtl/mini/mk/software.mk @@ -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 \ diff --git a/rtl/mini/top/apb4_periph.sv b/rtl/mini/top/apb4_periph.sv index bf1dca05..c185da32 100644 --- a/rtl/mini/top/apb4_periph.sv +++ b/rtl/mini/top/apb4_periph.sv @@ -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), diff --git a/rtl/mini/top/apb4_system.sv b/rtl/mini/top/apb4_system.sv index 54bedd24..6d7001d8 100644 --- a/rtl/mini/top/apb4_system.sv +++ b/rtl/mini/top/apb4_system.sv @@ -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; diff --git a/rtl/mini/top/retrosoc.sv b/rtl/mini/top/retrosoc.sv index ac374d72..60c14202 100644 --- a/rtl/mini/top/retrosoc.sv +++ b/rtl/mini/top/retrosoc.sv @@ -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] }; diff --git a/rtl/rtl_style_audit.json b/rtl/rtl_style_audit.json index 920198fe..8c6824e9 100644 --- a/rtl/rtl_style_audit.json +++ b/rtl/rtl_style_audit.json @@ -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", diff --git a/scripts/run_c_tests.py b/scripts/run_c_tests.py index 3ea6574b..aedaebdb 100644 --- a/scripts/run_c_tests.py +++ b/scripts/run_c_tests.py @@ -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"), diff --git a/tests/c/test_runtime.c b/tests/c/test_runtime.c index 73e78197..7b3ae722 100644 --- a/tests/c/test_runtime.c +++ b/tests/c/test_runtime.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -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(), diff --git a/tests/rtl/apb4_topology_tb.sv b/tests/rtl/apb4_topology_tb.sv index 630f0ede..4bdd287d 100644 --- a/tests/rtl/apb4_topology_tb.sv +++ b/tests/rtl/apb4_topology_tb.sv @@ -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; diff --git a/tests/rtl/bus_fault_tb.sv b/tests/rtl/bus_fault_tb.sv index d3d4b45a..772337c9 100644 --- a/tests/rtl/bus_fault_tb.sv +++ b/tests/rtl/bus_fault_tb.sv @@ -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"); diff --git a/tests/rtl/ga2d_tb.sv b/tests/rtl/ga2d_tb.sv new file mode 100644 index 00000000..e9ffcdff --- /dev/null +++ b/tests/rtl/ga2d_tb.sv @@ -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 diff --git a/tests/rtl/ga2d_wrapper_tb.sv b/tests/rtl/ga2d_wrapper_tb.sv new file mode 100644 index 00000000..2ac6368c --- /dev/null +++ b/tests/rtl/ga2d_wrapper_tb.sv @@ -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 diff --git a/tests/rtl/resource_controller_tb.sv b/tests/rtl/resource_controller_tb.sv index 3757e934..2273343b 100644 --- a/tests/rtl/resource_controller_tb.sv +++ b/tests/rtl/resource_controller_tb.sv @@ -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"); diff --git a/tests/rtl/soc_irq_topology_tb.sv b/tests/rtl/soc_irq_topology_tb.sv index 198ac3f7..34400dd6 100644 --- a/tests/rtl/soc_irq_topology_tb.sv +++ b/tests/rtl/soc_irq_topology_tb.sv @@ -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", diff --git a/tests/test_ga2d.py b/tests/test_ga2d.py new file mode 100644 index 00000000..eccb5a3e --- /dev/null +++ b/tests/test_ga2d.py @@ -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", + ) diff --git a/tests/test_ga2d_platform.py b/tests/test_ga2d_platform.py index eee939cd..22e5c06a 100644 --- a/tests/test_ga2d_platform.py +++ b/tests/test_ga2d_platform.py @@ -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: diff --git a/tests/test_ga2d_register_parity.py b/tests/test_ga2d_register_parity.py new file mode 100644 index 00000000..30656a49 --- /dev/null +++ b/tests/test_ga2d_register_parity.py @@ -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 diff --git a/tests/test_lp_irq.py b/tests/test_lp_irq.py index 5faae074..8e3abf02 100644 --- a/tests/test_lp_irq.py +++ b/tests/test_lp_irq.py @@ -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 diff --git a/tests/test_memory_map.py b/tests/test_memory_map.py index 992959a0..83213e5b 100644 --- a/tests/test_memory_map.py +++ b/tests/test_memory_map.py @@ -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: diff --git a/tests/test_publication_software.py b/tests/test_publication_software.py index becc4987..a49feea2 100644 --- a/tests/test_publication_software.py +++ b/tests/test_publication_software.py @@ -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"]) diff --git a/tests/test_soc_topology.py b/tests/test_soc_topology.py index 1dbc389a..012633a3 100644 --- a/tests/test_soc_topology.py +++ b/tests/test_soc_topology.py @@ -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