diff --git a/app/apps/ci_smoke/main.c b/app/apps/ci_smoke/main.c index 90be20d0..5e3f4a94 100644 --- a/app/apps/ci_smoke/main.c +++ b/app/apps/ci_smoke/main.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,14 @@ static bool rs_ci_smoke_archinfo_v2(void) { (rs_archinfo_read_device_id(device_id) == RS_ENOTSUP) && (rs_rtc_probe() == RS_OK); } +static bool rs_ci_smoke_apu_p1(void) { + rs_apu_info_t info; + + return (rs_apu_probe(&info) == RS_OK) && (info.capability0 == RS_APU_CAPABILITY0_IMPLEMENTED) && + (info.capability1 == RS_APU_CAPABILITY1_IMPLEMENTED) && + (info.abi_digest == RS_APU_DIGEST_IMPLEMENTED); +} + static bool rs_ci_smoke_onchip_sram(void) { rs_onchip_sram_info_t info; rs_onchip_sram_perf_t performance; @@ -376,6 +385,10 @@ int main(void) { rs_test_finish(RS_TEST_FAILED, 1U); } printf("ci_smoke: archinfo passed\n"); + if (!rs_ci_smoke_apu_p1()) { + rs_test_finish(RS_TEST_FAILED, 14U); + } + printf("ci_smoke: APU-P1 discovery passed\n"); if (!rs_ci_smoke_fabric_monitor_start()) { rs_test_finish(RS_TEST_FAILED, 13U); } diff --git a/crt/README.md b/crt/README.md index 90b56760..dcef7254 100644 --- a/crt/README.md +++ b/crt/README.md @@ -53,10 +53,15 @@ 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, and JPEG owner/lock, lifecycle-request, fault, and HP cache-maintenance +EXT-H, JPEG, and APU owner/lock, lifecycle-request, fault, and HP cache-maintenance handshake. See the [Resource Controller contract](../docs/ip/resource-controller.md). +`` provides APU-P1 identity, ABI version, capability, and +ABI-digest discovery. The current shell reports zero datapath capabilities; +job, DMA, stream, microcode, codec, and KWS APIs remain unavailable until their +frozen implementation phases. + `` 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/apu.h b/crt/include/retrosoc/hal/apu.h new file mode 100644 index 00000000..0cdad704 --- /dev/null +++ b/crt/include/retrosoc/hal/apu.h @@ -0,0 +1,19 @@ +#ifndef RETROSOC_HAL_APU_H +#define RETROSOC_HAL_APU_H + +#include + +#include +#include + +typedef struct { + uint32_t ip_id; + uint32_t version; + uint32_t capability0; + uint32_t capability1; + uint32_t abi_digest; +} rs_apu_info_t; + +rs_status_t rs_apu_probe(rs_apu_info_t *info); + +#endif diff --git a/crt/include/retrosoc/hal/apu_regs.h b/crt/include/retrosoc/hal/apu_regs.h new file mode 100644 index 00000000..ea609239 --- /dev/null +++ b/crt/include/retrosoc/hal/apu_regs.h @@ -0,0 +1,210 @@ +#ifndef RETROSOC_HAL_APU_REGS_H +#define RETROSOC_HAL_APU_REGS_H + +#include + +#include + +/* Independently handwritten mirror of rtl/ip/multimedia/apu_define.svh. */ +#define RS_APU_ABI_IP_ID UINT32_C(0x000) +#define RS_APU_ABI_IP_VERSION UINT32_C(0x004) +#define RS_APU_ABI_CAPABILITY0 UINT32_C(0x008) +#define RS_APU_ABI_CAPABILITY1 UINT32_C(0x00C) +#define RS_APU_ABI_COMMAND UINT32_C(0x010) +#define RS_APU_ABI_STATUS UINT32_C(0x014) +#define RS_APU_ABI_IRQ_STATE UINT32_C(0x018) +#define RS_APU_ABI_IRQ_ENABLE UINT32_C(0x01C) +#define RS_APU_ABI_IRQ_TEST UINT32_C(0x020) +#define RS_APU_ABI_ERROR_STATUS UINT32_C(0x024) +#define RS_APU_ABI_ERROR_ADDRESS UINT32_C(0x028) +#define RS_APU_ABI_ERROR_DETAIL UINT32_C(0x02C) +#define RS_APU_ABI_SEQUENCER_TIMEOUT UINT32_C(0x030) +#define RS_APU_ABI_STREAM_ROUTE UINT32_C(0x034) +#define RS_APU_ABI_STREAM_STATUS UINT32_C(0x038) +#define RS_APU_ABI_OWNER_STATUS UINT32_C(0x03C) +#define RS_APU_ABI_READ_BASE UINT32_C(0x040) +#define RS_APU_ABI_READ_LIMIT UINT32_C(0x044) +#define RS_APU_ABI_WRITE_BASE UINT32_C(0x048) +#define RS_APU_ABI_WRITE_LIMIT UINT32_C(0x04C) +#define RS_APU_ABI_DMA_TIMEOUT UINT32_C(0x050) +#define RS_APU_ABI_ABI_DIGEST UINT32_C(0x054) +#define RS_APU_ABI_SEQUENCER_STATUS UINT32_C(0x058) +#define RS_APU_ABI_SEQUENCER_RETIRED UINT32_C(0x05C) +#define RS_APU_ABI_MC_IMAGE_ADDRESS UINT32_C(0x080) +#define RS_APU_ABI_MC_IMAGE_SIZE UINT32_C(0x084) +#define RS_APU_ABI_MC_EXPECTED_CRC UINT32_C(0x088) +#define RS_APU_ABI_MC_STATUS UINT32_C(0x08C) +#define RS_APU_ABI_MC_ABI UINT32_C(0x090) +#define RS_APU_ABI_MC_BUILD_ID_LO UINT32_C(0x094) +#define RS_APU_ABI_MC_BUILD_ID_HI UINT32_C(0x098) +#define RS_APU_ABI_MC_LOCK UINT32_C(0x09C) +#define RS_APU_ABI_MC_ACTUAL_CRC UINT32_C(0x0A0) +#define RS_APU_ABI_MC_LOAD_COUNT UINT32_C(0x0A4) +#define RS_APU_ABI_JOB_CONTROL UINT32_C(0x100) +#define RS_APU_ABI_JOB_INPUT_ADDRESS UINT32_C(0x104) +#define RS_APU_ABI_JOB_INPUT_LENGTH UINT32_C(0x108) +#define RS_APU_ABI_JOB_OUTPUT_ADDRESS UINT32_C(0x10C) +#define RS_APU_ABI_JOB_OUTPUT_CAPACITY UINT32_C(0x110) +#define RS_APU_ABI_JOB_INPUT_CONFIG UINT32_C(0x114) +#define RS_APU_ABI_JOB_OUTPUT_CONFIG UINT32_C(0x118) +#define RS_APU_ABI_JOB_FLAGS UINT32_C(0x11C) +#define RS_APU_ABI_JOB_STATUS UINT32_C(0x120) +#define RS_APU_ABI_JOB_INPUT_USED UINT32_C(0x124) +#define RS_APU_ABI_JOB_OUTPUT_BYTES UINT32_C(0x128) +#define RS_APU_ABI_JOB_FRAMES UINT32_C(0x12C) +#define RS_APU_ABI_JOB_SOURCE_INFO UINT32_C(0x130) +#define RS_APU_ABI_JOB_CYCLES UINT32_C(0x134) +#define RS_APU_ABI_JOB_DETAIL UINT32_C(0x138) +#define RS_APU_ABI_RING_BASE UINT32_C(0x180) +#define RS_APU_ABI_RING_SIZE UINT32_C(0x184) +#define RS_APU_ABI_RING_HEAD UINT32_C(0x188) +#define RS_APU_ABI_RING_TAIL UINT32_C(0x18C) +#define RS_APU_ABI_RING_CONTROL UINT32_C(0x190) +#define RS_APU_ABI_RING_STATUS UINT32_C(0x194) +#define RS_APU_ABI_RING_COMPLETED UINT32_C(0x198) +#define RS_APU_ABI_RING_COALESCE UINT32_C(0x19C) +#define RS_APU_ABI_RING_DOORBELL UINT32_C(0x1A0) +#define RS_APU_ABI_KWS_MODEL_ADDRESS UINT32_C(0x200) +#define RS_APU_ABI_KWS_MODEL_SIZE UINT32_C(0x204) +#define RS_APU_ABI_KWS_MODEL_EXPECTED_CRC UINT32_C(0x208) +#define RS_APU_ABI_KWS_CONTROL UINT32_C(0x20C) +#define RS_APU_ABI_KWS_CONFIG UINT32_C(0x210) +#define RS_APU_ABI_KWS_STATUS UINT32_C(0x214) +#define RS_APU_ABI_KWS_RESULT UINT32_C(0x218) +#define RS_APU_ABI_KWS_TIMESTAMP_LO UINT32_C(0x21C) +#define RS_APU_ABI_KWS_TIMESTAMP_HI UINT32_C(0x220) +#define RS_APU_ABI_KWS_FRAME_COUNT UINT32_C(0x224) +#define RS_APU_ABI_KWS_INFERENCE_COUNT UINT32_C(0x228) +#define RS_APU_ABI_KWS_HIT_COUNT UINT32_C(0x22C) +#define RS_APU_ABI_KWS_OVERRUN_COUNT UINT32_C(0x230) +#define RS_APU_ABI_KWS_MODEL_STATUS UINT32_C(0x234) +#define RS_APU_ABI_KWS_MODEL_ACTUAL_CRC UINT32_C(0x238) +#define RS_APU_ABI_PERF_CONTROL UINT32_C(0x300) +#define RS_APU_ABI_PERF_STATUS UINT32_C(0x304) +#define RS_APU_ABI_PERF_ACTIVE_CYCLES_LO UINT32_C(0x308) +#define RS_APU_ABI_PERF_ACTIVE_CYCLES_HI UINT32_C(0x30C) +#define RS_APU_ABI_PERF_INPUT_BYTES_LO UINT32_C(0x310) +#define RS_APU_ABI_PERF_INPUT_BYTES_HI UINT32_C(0x314) +#define RS_APU_ABI_PERF_OUTPUT_BYTES_LO UINT32_C(0x318) +#define RS_APU_ABI_PERF_OUTPUT_BYTES_HI UINT32_C(0x31C) +#define RS_APU_ABI_PERF_DECODED_FRAMES_LO UINT32_C(0x320) +#define RS_APU_ABI_PERF_DECODED_FRAMES_HI UINT32_C(0x324) +#define RS_APU_ABI_PERF_DMA_READ_STALLS_LO UINT32_C(0x328) +#define RS_APU_ABI_PERF_DMA_READ_STALLS_HI UINT32_C(0x32C) +#define RS_APU_ABI_PERF_DMA_WRITE_STALLS_LO UINT32_C(0x330) +#define RS_APU_ABI_PERF_DMA_WRITE_STALLS_HI UINT32_C(0x334) +#define RS_APU_ABI_PERF_STREAM_STALLS_LO UINT32_C(0x338) +#define RS_APU_ABI_PERF_STREAM_STALLS_HI UINT32_C(0x33C) +#define RS_APU_ABI_PERF_SEQUENCER_INSTR_LO UINT32_C(0x340) +#define RS_APU_ABI_PERF_SEQUENCER_INSTR_HI UINT32_C(0x344) +#define RS_APU_ABI_PERF_KWS_CYCLES_LO UINT32_C(0x348) +#define RS_APU_ABI_PERF_KWS_CYCLES_HI UINT32_C(0x34C) +#define RS_APU_ABI_PERF_FAULTS_LO UINT32_C(0x350) +#define RS_APU_ABI_PERF_FAULTS_HI UINT32_C(0x354) + +#define RS_APU_ABI_CAPABILITY0_WAV 0U +#define RS_APU_ABI_CAPABILITY0_MP3 1U +#define RS_APU_ABI_CAPABILITY0_FLAC 2U +#define RS_APU_ABI_CAPABILITY0_PRIVATE_DMA 3U +#define RS_APU_ABI_CAPABILITY0_RING 4U +#define RS_APU_ABI_CAPABILITY0_STREAMS 5U +#define RS_APU_ABI_CAPABILITY0_KWS 6U +#define RS_APU_ABI_CAPABILITY0_SEQUENCER 7U +#define RS_APU_ABI_CAPABILITY0_RESAMPLER 8U +#define RS_APU_ABI_CAPABILITY1_CONTROL_KIB 0U +#define RS_APU_ABI_CAPABILITY1_DATA_KIB 8U +#define RS_APU_ABI_CAPABILITY1_MAX_CHANNELS 16U +#define RS_APU_ABI_CAPABILITY1_MAX_RATE_KHZ 18U +#define RS_APU_ABI_COMMAND_START_DIRECT 0U +#define RS_APU_ABI_COMMAND_ABORT 1U +#define RS_APU_ABI_COMMAND_SOFT_RESET 2U +#define RS_APU_ABI_COMMAND_RING_KICK 3U +#define RS_APU_ABI_COMMAND_MICROCODE_LOAD 4U +#define RS_APU_ABI_COMMAND_MODEL_LOAD 5U +#define RS_APU_ABI_COMMAND_CLEAR_COUNTERS 6U +#define RS_APU_ABI_STATUS_MICROCODE_VALID 0U +#define RS_APU_ABI_STATUS_MODEL_VALID 1U +#define RS_APU_ABI_STATUS_BUSY 2U +#define RS_APU_ABI_STATUS_RING 3U +#define RS_APU_ABI_STATUS_DECODE 4U +#define RS_APU_ABI_STATUS_KWS_LISTENING 5U +#define RS_APU_ABI_STATUS_QUIESCED 6U +#define RS_APU_ABI_STATUS_ABORTING 7U +#define RS_APU_ABI_STATUS_IDLE 8U +#define RS_APU_ABI_STATUS_SEQUENCER_TRAPPED 9U +#define RS_APU_ABI_IRQ_DIRECT_DONE 0U +#define RS_APU_ABI_IRQ_RING_EVENT 1U +#define RS_APU_ABI_IRQ_KWS_HIT 2U +#define RS_APU_ABI_IRQ_MICROCODE_LOAD_DONE 3U +#define RS_APU_ABI_IRQ_MODEL_LOAD_DONE 4U +#define RS_APU_ABI_IRQ_ABORT_DONE 5U +#define RS_APU_ABI_IRQ_INPUT_WATERMARK 6U +#define RS_APU_ABI_IRQ_OUTPUT_WATERMARK 7U +#define RS_APU_ABI_IRQ_FIRST_ERROR 8U +#define RS_APU_ABI_IRQ_STREAM_XRUN 9U +#define RS_APU_ABI_IRQ_SEQUENCER_TRAP 10U +#define RS_APU_ABI_ERROR_VALID 0U +#define RS_APU_ABI_ERROR_CODE 1U +#define RS_APU_ABI_ERROR_STAGE 7U +#define RS_APU_ABI_ERROR_AXI_RESPONSE 11U +#define RS_APU_ABI_ERROR_DESCRIPTOR_INDEX 13U +#define RS_APU_ABI_ERROR_CODE_NONE 0U +#define RS_APU_ABI_ERROR_CODE_INVALID_CONFIG 1U +#define RS_APU_ABI_ERROR_CODE_INVALID_RING 2U +#define RS_APU_ABI_ERROR_CODE_UNSUPPORTED 3U +#define RS_APU_ABI_ERROR_CODE_MALFORMED 4U +#define RS_APU_ABI_ERROR_CODE_TRUNCATED 5U +#define RS_APU_ABI_ERROR_CODE_CODEC_CRC 6U +#define RS_APU_ABI_ERROR_CODE_DECODE 7U +#define RS_APU_ABI_ERROR_CODE_RECONSTRUCTION 8U +#define RS_APU_ABI_ERROR_CODE_MICROCODE 9U +#define RS_APU_ABI_ERROR_CODE_MICROCODE_CRC 10U +#define RS_APU_ABI_ERROR_CODE_SEQUENCER 11U +#define RS_APU_ABI_ERROR_CODE_KWS_MODEL 12U +#define RS_APU_ABI_ERROR_CODE_KWS_CRC 13U +#define RS_APU_ABI_ERROR_CODE_KWS_ARITHMETIC 14U +#define RS_APU_ABI_ERROR_CODE_AXI_READ 15U +#define RS_APU_ABI_ERROR_CODE_AXI_WRITE 16U +#define RS_APU_ABI_ERROR_CODE_DMA_TIMEOUT 17U +#define RS_APU_ABI_ERROR_CODE_STREAM_UNDERRUN 18U +#define RS_APU_ABI_ERROR_CODE_STREAM_OVERRUN 19U +#define RS_APU_ABI_ERROR_CODE_ABORT 20U +#define RS_APU_ABI_ERROR_CODE_RESOURCE_RESET 21U +#define RS_APU_ABI_ERROR_CODE_OVERFLOW 22U +#define RS_APU_ABI_ERROR_STAGE_APB 0U +#define RS_APU_ABI_ERROR_STAGE_LOADER 1U +#define RS_APU_ABI_ERROR_STAGE_RING 2U +#define RS_APU_ABI_ERROR_STAGE_DMA_READ 3U +#define RS_APU_ABI_ERROR_STAGE_BITSTREAM 4U +#define RS_APU_ABI_ERROR_STAGE_ENTROPY 5U +#define RS_APU_ABI_ERROR_STAGE_RECONSTRUCTION 6U +#define RS_APU_ABI_ERROR_STAGE_RESAMPLER 7U +#define RS_APU_ABI_ERROR_STAGE_DMA_WRITE 8U +#define RS_APU_ABI_ERROR_STAGE_KWS_FRONTEND 9U +#define RS_APU_ABI_ERROR_STAGE_KWS_INFERENCE 10U +#define RS_APU_ABI_ERROR_STAGE_LIFECYCLE 11U +#define RS_APU_ABI_OWNER_STATUS_OWNER 0U +#define RS_APU_ABI_OWNER_STATUS_LOCK 8U +#define RS_APU_ABI_OWNER_STATUS_QUIESCE 9U +#define RS_APU_ABI_OWNER_STATUS_RESET 10U +#define RS_APU_ABI_PERF_CONTROL_ENABLE 0U +#define RS_APU_ABI_PERF_CONTROL_CLEAR 1U +#define RS_APU_ABI_PERF_CONTROL_SNAPSHOT 2U +#define RS_APU_ABI_PERF_STATUS_SNAPSHOT_VALID 0U + +#define RS_APU_IP_ID_VALUE UINT32_C(0x41505530) +#define RS_APU_IP_VERSION_VALUE UINT32_C(0x00010000) +#define RS_APU_IP_VERSION_MAJOR_MASK UINT32_C(0xFFFF0000) +#define RS_APU_CAPABILITY0_IMPLEMENTED UINT32_C(0x00000000) +#define RS_APU_CAPABILITY1_IMPLEMENTED UINT32_C(0x00000000) +#define RS_APU_DIGEST_IMPLEMENTED UINT32_C(0x00000000) +#define RS_APU_IRQ_ALL UINT32_C(0x000007FF) + +#define RS_APU_REG_IP_ID RS_APU_ABI_IP_ID +#define RS_APU_REG_IP_VERSION RS_APU_ABI_IP_VERSION +#define RS_APU_REG_CAPABILITY0 RS_APU_ABI_CAPABILITY0 +#define RS_APU_REG_CAPABILITY1 RS_APU_ABI_CAPABILITY1 +#define RS_APU_REG_ABI_DIGEST RS_APU_ABI_ABI_DIGEST +#define RS_APU_REG(offset) RS_SOC_REG32(RS_SOC_APB4_APU_BASE, (offset)) + +#endif diff --git a/crt/include/retrosoc/hal/resource.h b/crt/include/retrosoc/hal/resource.h index 7959264f..6fa4b18e 100644 --- a/crt/include/retrosoc/hal/resource.h +++ b/crt/include/retrosoc/hal/resource.h @@ -14,7 +14,8 @@ typedef enum { RS_RESOURCE_SPISD = 4, RS_RESOURCE_EXT_H = 5, RS_RESOURCE_JPEG = 6, - RS_RESOURCE_COUNT = 7, + RS_RESOURCE_APU = 7, + RS_RESOURCE_COUNT = 8, } rs_resource_t; typedef enum { diff --git a/crt/src/hal/apu.c b/crt/src/hal/apu.c new file mode 100644 index 00000000..de3c4951 --- /dev/null +++ b/crt/src/hal/apu.c @@ -0,0 +1,21 @@ +#include + +#include + +rs_status_t rs_apu_probe(rs_apu_info_t *info) { + if (info == NULL) { + return RS_EINVAL; + } + + info->ip_id = RS_APU_REG(RS_APU_REG_IP_ID); + info->version = RS_APU_REG(RS_APU_REG_IP_VERSION); + info->capability0 = RS_APU_REG(RS_APU_REG_CAPABILITY0); + info->capability1 = RS_APU_REG(RS_APU_REG_CAPABILITY1); + info->abi_digest = RS_APU_REG(RS_APU_REG_ABI_DIGEST); + + if ((info->ip_id != RS_APU_IP_ID_VALUE) || + ((info->version & RS_APU_IP_VERSION_MAJOR_MASK) != RS_APU_IP_VERSION_VALUE)) { + return RS_ENOTSUP; + } + return RS_OK; +} diff --git a/crt/src/service/booter.c b/crt/src/service/booter.c index 3069ffe1..8d5b8c30 100644 --- a/crt/src/service/booter.c +++ b/crt/src/service/booter.c @@ -127,6 +127,8 @@ static void rs_print_mmio_map(void) { (void *)(uintptr_t)RS_SOC_APB4_HP_MAILBOX_BASE); printf(" 1 x JPEG @%p\n", (void *)(uintptr_t)RS_SOC_APB4_JPEG_BASE); + printf(" 1 x APU @%p\n", + (void *)(uintptr_t)RS_SOC_APB4_APU_BASE); printf(" 1 x CLINT @%p\n", (void *)(uintptr_t)RS_SOC_APB4_CLINT_BASE); printf(" 1 x ARCHINFO @%p\n", diff --git a/docs/ip/apu.md b/docs/ip/apu.md index 02126d35..16123fa7 100644 --- a/docs/ip/apu.md +++ b/docs/ip/apu.md @@ -26,10 +26,12 @@ The design was frozen on 2026-09-02 from these repository sources of truth: programmable-LUT, and lifecycle patterns; and - `dependencies/dependencies.lock.json` for managed inputs. -This specification defines a target, not an implemented feature. The current -executable address map keeps `APB4_APU` reserved and disabled. No claim is made -that APU RTL, microcode, assembler, codec conformance, KWS model, Linux driver, -timing closure, power closure, CDC/RDC signoff, or silicon qualification exists. +APU-P1 implements the APB4 register shell, Resource Controller index 7, +LP IRQ31/HP PLIC source10 ownership routing, and HAL discovery. The shell +advertises zero datapath capabilities and rejects every deferred datapath +command. No claim is made that APU DMA, streams, microcode, sequencer, codec +engines, KWS, codec conformance, Linux driver, timing closure, power closure, +CDC/RDC signoff, or silicon qualification exists. ## Commercial References diff --git a/docs/ip/hp-platform.md b/docs/ip/hp-platform.md index acec809d..23515858 100644 --- a/docs/ip/hp-platform.md +++ b/docs/ip/hp-platform.md @@ -7,10 +7,12 @@ APB4 peripherals. Their role in boot and lifecycle control is defined by ## PLIC -Source 0 is permanently reserved. Source 1 is UART1 and source 2 is the -HP-side mailbox doorbell; sources 3 through 31 are reserved and tied low. Each -source has a three-bit priority. Context 0 drives HP machine external interrupt -and context 1 drives HP supervisor external interrupt. +Source 0 is permanently reserved. Source 1 is UART1, source 2 is the HP-side +mailbox doorbell, source 3 is EXT-H, and sources 4 through 10 are central DMA, +USB2, SDIO0, SDIO1, SPI-SD, JPEG, and APU respectively. Sources 11 through 31 +are reserved and tied low. Resource-owned sources are suppressed unless HP is +the exclusive owner. Each source has a three-bit priority. Context 0 drives HP +machine external interrupt and context 1 drives HP supervisor external interrupt. | Address offset | Register | Access | | --- | --- | --- | diff --git a/docs/ip/resource-controller.md b/docs/ip/resource-controller.md index 0fabd90c..b01724e6 100644 --- a/docs/ip/resource-controller.md +++ b/docs/ip/resource-controller.md @@ -3,9 +3,9 @@ ## Scope The Resource Controller at `0x2000_A000` is the root-management ownership and -interrupt-routing authority for central DMA, USB2, SDIO0, SDIO1, SPI-SD, and -EXT-H. Hazard3 has read/write access; HP MMIO may inspect status but writes are -rejected by the root-control firewall. +interrupt-routing authority for central DMA, USB2, SDIO0, SDIO1, SPI-SD, +EXT-H, JPEG, and APU. Hazard3 has read/write access; HP MMIO may inspect status +but writes are rejected by the root-control firewall. It is handwritten RTL and has a matching handwritten `` API; no register generator is used. @@ -19,6 +19,8 @@ Resource indices are fixed: | 3 | SDIO1 | 7 | | 4 | SPI-SD | 8 | | 5 | EXT-H | 3 | +| 6 | JPEG | 9 | +| 7 | APU | 10 | 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. Reset masks both @@ -74,6 +76,7 @@ LP/HP IRQ routing are implemented and directed-tested. `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 state machine. It must not be -described as independent peripheral power isolation or reset containment until +engine's internal reset state machine. The APU-P1 shell is an exception: its +index-7 quiesce acknowledgement and reset are connected locally in PCLK. It +must not be described as independent peripheral power isolation or reset containment until those downstream acknowledgements and fault-injection tests exist. diff --git a/docs/lp-hp-architecture.md b/docs/lp-hp-architecture.md index e8a87e4f..b642e036 100644 --- a/docs/lp-hp-architecture.md +++ b/docs/lp-hp-architecture.md @@ -138,9 +138,10 @@ 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, and EXT-H. Handoff requires idle, owner lock is -sticky, and rejected handoffs raise LP IRQ 29. It also carries the AON cache -request/clean acknowledgement used before HP drain. See +for DMA, USB2, SDIO0/1, SPI-SD, EXT-H, JPEG, and APU. 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). The root-only Fabric Monitor at `0x2000_B000` records per-master and diff --git a/rtl/README.md b/rtl/README.md index cd7bdc50..08341273 100644 --- a/rtl/README.md +++ b/rtl/README.md @@ -119,13 +119,12 @@ ownership. Its implemented limits, handwritten ABI, measured performance, and commercial release gates are documented in [Baseline JPEG Codec](../docs/ip/jpeg.md). -The planned Mini Audio Processing Unit has a frozen coreless architecture at -the reserved `APB4_APU` window. LP loads one bounded microcode bundle containing -WAV, MP3, and FLAC entry points; a hardware sequencer drives fixed bitstream, -entropy, transform, reconstruction, resampler, DMA, and I2S stream engines. -Continuous KWS uses a separate fixed INT8 engine. The feature is not implemented -or enabled by the current topology; its ABI, phases, verification, and evidence -gates are defined in [Mini Audio Processing Unit](../docs/ip/apu.md). +The Mini Audio Processing Unit has a frozen coreless architecture. APU-P1 +implements its fail-closed APB4 shell at `APB4_APU`, Resource Controller index +7, and exclusive LP IRQ31/HP PLIC source10 routing. The shell advertises no +datapath capabilities: private DMA, streams, microcode, codec engines, and KWS +remain deferred to their frozen phases. The complete ABI, phase order, and +evidence gates are defined in [Mini Audio Processing Unit](../docs/ip/apu.md). SystemCtrl uses `sysctrl_if.sv`, `sysctrl_define.svh`, `sysctrl_reg.sv`, and `sysctrl_core.sv` behind the stable `apb4_sysctrl` integration wrapper. Its diff --git a/rtl/ip/multimedia/apb4_apu.sv b/rtl/ip/multimedia/apb4_apu.sv new file mode 100644 index 00000000..0e644b84 --- /dev/null +++ b/rtl/ip/multimedia/apb4_apu.sv @@ -0,0 +1,28 @@ +// Copyright (c) 2026 Yuchi Miao +// retroSoC is licensed under Mulan PSL v2. + +module apb4_apu ( + // verilog_format: off -- preserve the APB and lifecycle boundary columns + input logic clk_i, + input logic rst_n_i, + input logic [1:0] owner_i, + input logic owner_lock_i, + input logic quiesce_i, + input logic resource_reset_i, + apb4_if.slave apb4, + output logic idle_o, + output logic irq_o + // verilog_format: on +); + apu_reg u_apu_reg ( + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .owner_i (owner_i), + .owner_lock_i (owner_lock_i), + .quiesce_i (quiesce_i), + .resource_reset_i(resource_reset_i), + .apb4 (apb4), + .idle_o (idle_o), + .irq_o (irq_o) + ); +endmodule diff --git a/rtl/ip/multimedia/apu_define.svh b/rtl/ip/multimedia/apu_define.svh new file mode 100644 index 00000000..d49ae8ea --- /dev/null +++ b/rtl/ip/multimedia/apu_define.svh @@ -0,0 +1,207 @@ +// Copyright (c) 2026 Yuchi Miao +// retroSoC is licensed under Mulan PSL v2. + +`ifndef RETROSOC_APU_DEFINE_SVH +`define RETROSOC_APU_DEFINE_SVH + +// verilog_format: off -- keep the handwritten hardware/software ABI column aligned +`define APB4_APU__IP_ID 12'h000 +`define APB4_APU__IP_VERSION 12'h004 +`define APB4_APU__CAPABILITY0 12'h008 +`define APB4_APU__CAPABILITY1 12'h00c +`define APB4_APU__COMMAND 12'h010 +`define APB4_APU__STATUS 12'h014 +`define APB4_APU__IRQ_STATE 12'h018 +`define APB4_APU__IRQ_ENABLE 12'h01c +`define APB4_APU__IRQ_TEST 12'h020 +`define APB4_APU__ERROR_STATUS 12'h024 +`define APB4_APU__ERROR_ADDRESS 12'h028 +`define APB4_APU__ERROR_DETAIL 12'h02c +`define APB4_APU__SEQUENCER_TIMEOUT 12'h030 +`define APB4_APU__STREAM_ROUTE 12'h034 +`define APB4_APU__STREAM_STATUS 12'h038 +`define APB4_APU__OWNER_STATUS 12'h03c +`define APB4_APU__READ_BASE 12'h040 +`define APB4_APU__READ_LIMIT 12'h044 +`define APB4_APU__WRITE_BASE 12'h048 +`define APB4_APU__WRITE_LIMIT 12'h04c +`define APB4_APU__DMA_TIMEOUT 12'h050 +`define APB4_APU__ABI_DIGEST 12'h054 +`define APB4_APU__SEQUENCER_STATUS 12'h058 +`define APB4_APU__SEQUENCER_RETIRED 12'h05c + +`define APB4_APU__MC_IMAGE_ADDRESS 12'h080 +`define APB4_APU__MC_IMAGE_SIZE 12'h084 +`define APB4_APU__MC_EXPECTED_CRC 12'h088 +`define APB4_APU__MC_STATUS 12'h08c +`define APB4_APU__MC_ABI 12'h090 +`define APB4_APU__MC_BUILD_ID_LO 12'h094 +`define APB4_APU__MC_BUILD_ID_HI 12'h098 +`define APB4_APU__MC_LOCK 12'h09c +`define APB4_APU__MC_ACTUAL_CRC 12'h0a0 +`define APB4_APU__MC_LOAD_COUNT 12'h0a4 + +`define APB4_APU__JOB_CONTROL 12'h100 +`define APB4_APU__JOB_INPUT_ADDRESS 12'h104 +`define APB4_APU__JOB_INPUT_LENGTH 12'h108 +`define APB4_APU__JOB_OUTPUT_ADDRESS 12'h10c +`define APB4_APU__JOB_OUTPUT_CAPACITY 12'h110 +`define APB4_APU__JOB_INPUT_CONFIG 12'h114 +`define APB4_APU__JOB_OUTPUT_CONFIG 12'h118 +`define APB4_APU__JOB_FLAGS 12'h11c +`define APB4_APU__JOB_STATUS 12'h120 +`define APB4_APU__JOB_INPUT_USED 12'h124 +`define APB4_APU__JOB_OUTPUT_BYTES 12'h128 +`define APB4_APU__JOB_FRAMES 12'h12c +`define APB4_APU__JOB_SOURCE_INFO 12'h130 +`define APB4_APU__JOB_CYCLES 12'h134 +`define APB4_APU__JOB_DETAIL 12'h138 + +`define APB4_APU__RING_BASE 12'h180 +`define APB4_APU__RING_SIZE 12'h184 +`define APB4_APU__RING_HEAD 12'h188 +`define APB4_APU__RING_TAIL 12'h18c +`define APB4_APU__RING_CONTROL 12'h190 +`define APB4_APU__RING_STATUS 12'h194 +`define APB4_APU__RING_COMPLETED 12'h198 +`define APB4_APU__RING_COALESCE 12'h19c +`define APB4_APU__RING_DOORBELL 12'h1a0 + +`define APB4_APU__KWS_MODEL_ADDRESS 12'h200 +`define APB4_APU__KWS_MODEL_SIZE 12'h204 +`define APB4_APU__KWS_MODEL_EXPECTED_CRC 12'h208 +`define APB4_APU__KWS_CONTROL 12'h20c +`define APB4_APU__KWS_CONFIG 12'h210 +`define APB4_APU__KWS_STATUS 12'h214 +`define APB4_APU__KWS_RESULT 12'h218 +`define APB4_APU__KWS_TIMESTAMP_LO 12'h21c +`define APB4_APU__KWS_TIMESTAMP_HI 12'h220 +`define APB4_APU__KWS_FRAME_COUNT 12'h224 +`define APB4_APU__KWS_INFERENCE_COUNT 12'h228 +`define APB4_APU__KWS_HIT_COUNT 12'h22c +`define APB4_APU__KWS_OVERRUN_COUNT 12'h230 +`define APB4_APU__KWS_MODEL_STATUS 12'h234 +`define APB4_APU__KWS_MODEL_ACTUAL_CRC 12'h238 + +`define APB4_APU__PERF_CONTROL 12'h300 +`define APB4_APU__PERF_STATUS 12'h304 +`define APB4_APU__PERF_ACTIVE_CYCLES_LO 12'h308 +`define APB4_APU__PERF_ACTIVE_CYCLES_HI 12'h30c +`define APB4_APU__PERF_INPUT_BYTES_LO 12'h310 +`define APB4_APU__PERF_INPUT_BYTES_HI 12'h314 +`define APB4_APU__PERF_OUTPUT_BYTES_LO 12'h318 +`define APB4_APU__PERF_OUTPUT_BYTES_HI 12'h31c +`define APB4_APU__PERF_DECODED_FRAMES_LO 12'h320 +`define APB4_APU__PERF_DECODED_FRAMES_HI 12'h324 +`define APB4_APU__PERF_DMA_READ_STALLS_LO 12'h328 +`define APB4_APU__PERF_DMA_READ_STALLS_HI 12'h32c +`define APB4_APU__PERF_DMA_WRITE_STALLS_LO 12'h330 +`define APB4_APU__PERF_DMA_WRITE_STALLS_HI 12'h334 +`define APB4_APU__PERF_STREAM_STALLS_LO 12'h338 +`define APB4_APU__PERF_STREAM_STALLS_HI 12'h33c +`define APB4_APU__PERF_SEQUENCER_INSTR_LO 12'h340 +`define APB4_APU__PERF_SEQUENCER_INSTR_HI 12'h344 +`define APB4_APU__PERF_KWS_CYCLES_LO 12'h348 +`define APB4_APU__PERF_KWS_CYCLES_HI 12'h34c +`define APB4_APU__PERF_FAULTS_LO 12'h350 +`define APB4_APU__PERF_FAULTS_HI 12'h354 + +`define APB4_APU__CAPABILITY0_WAV 0 +`define APB4_APU__CAPABILITY0_MP3 1 +`define APB4_APU__CAPABILITY0_FLAC 2 +`define APB4_APU__CAPABILITY0_PRIVATE_DMA 3 +`define APB4_APU__CAPABILITY0_RING 4 +`define APB4_APU__CAPABILITY0_STREAMS 5 +`define APB4_APU__CAPABILITY0_KWS 6 +`define APB4_APU__CAPABILITY0_SEQUENCER 7 +`define APB4_APU__CAPABILITY0_RESAMPLER 8 +`define APB4_APU__CAPABILITY1_CONTROL_KIB 0 +`define APB4_APU__CAPABILITY1_DATA_KIB 8 +`define APB4_APU__CAPABILITY1_MAX_CHANNELS 16 +`define APB4_APU__CAPABILITY1_MAX_RATE_KHZ 18 + +`define APB4_APU__COMMAND_START_DIRECT 0 +`define APB4_APU__COMMAND_ABORT 1 +`define APB4_APU__COMMAND_SOFT_RESET 2 +`define APB4_APU__COMMAND_RING_KICK 3 +`define APB4_APU__COMMAND_MICROCODE_LOAD 4 +`define APB4_APU__COMMAND_MODEL_LOAD 5 +`define APB4_APU__COMMAND_CLEAR_COUNTERS 6 + +`define APB4_APU__STATUS_MICROCODE_VALID 0 +`define APB4_APU__STATUS_MODEL_VALID 1 +`define APB4_APU__STATUS_BUSY 2 +`define APB4_APU__STATUS_RING 3 +`define APB4_APU__STATUS_DECODE 4 +`define APB4_APU__STATUS_KWS_LISTENING 5 +`define APB4_APU__STATUS_QUIESCED 6 +`define APB4_APU__STATUS_ABORTING 7 +`define APB4_APU__STATUS_IDLE 8 +`define APB4_APU__STATUS_SEQUENCER_TRAPPED 9 + +`define APB4_APU__IRQ_DIRECT_DONE 0 +`define APB4_APU__IRQ_RING_EVENT 1 +`define APB4_APU__IRQ_KWS_HIT 2 +`define APB4_APU__IRQ_MICROCODE_LOAD_DONE 3 +`define APB4_APU__IRQ_MODEL_LOAD_DONE 4 +`define APB4_APU__IRQ_ABORT_DONE 5 +`define APB4_APU__IRQ_INPUT_WATERMARK 6 +`define APB4_APU__IRQ_OUTPUT_WATERMARK 7 +`define APB4_APU__IRQ_FIRST_ERROR 8 +`define APB4_APU__IRQ_STREAM_XRUN 9 +`define APB4_APU__IRQ_SEQUENCER_TRAP 10 + +`define APB4_APU__ERROR_VALID 0 +`define APB4_APU__ERROR_CODE 1 +`define APB4_APU__ERROR_STAGE 7 +`define APB4_APU__ERROR_AXI_RESPONSE 11 +`define APB4_APU__ERROR_DESCRIPTOR_INDEX 13 + +`define APB4_APU__ERROR_CODE_NONE 0 +`define APB4_APU__ERROR_CODE_INVALID_CONFIG 1 +`define APB4_APU__ERROR_CODE_INVALID_RING 2 +`define APB4_APU__ERROR_CODE_UNSUPPORTED 3 +`define APB4_APU__ERROR_CODE_MALFORMED 4 +`define APB4_APU__ERROR_CODE_TRUNCATED 5 +`define APB4_APU__ERROR_CODE_CODEC_CRC 6 +`define APB4_APU__ERROR_CODE_DECODE 7 +`define APB4_APU__ERROR_CODE_RECONSTRUCTION 8 +`define APB4_APU__ERROR_CODE_MICROCODE 9 +`define APB4_APU__ERROR_CODE_MICROCODE_CRC 10 +`define APB4_APU__ERROR_CODE_SEQUENCER 11 +`define APB4_APU__ERROR_CODE_KWS_MODEL 12 +`define APB4_APU__ERROR_CODE_KWS_CRC 13 +`define APB4_APU__ERROR_CODE_KWS_ARITHMETIC 14 +`define APB4_APU__ERROR_CODE_AXI_READ 15 +`define APB4_APU__ERROR_CODE_AXI_WRITE 16 +`define APB4_APU__ERROR_CODE_DMA_TIMEOUT 17 +`define APB4_APU__ERROR_CODE_STREAM_UNDERRUN 18 +`define APB4_APU__ERROR_CODE_STREAM_OVERRUN 19 +`define APB4_APU__ERROR_CODE_ABORT 20 +`define APB4_APU__ERROR_CODE_RESOURCE_RESET 21 +`define APB4_APU__ERROR_CODE_OVERFLOW 22 + +`define APB4_APU__ERROR_STAGE_APB 0 +`define APB4_APU__ERROR_STAGE_LOADER 1 +`define APB4_APU__ERROR_STAGE_RING 2 +`define APB4_APU__ERROR_STAGE_DMA_READ 3 +`define APB4_APU__ERROR_STAGE_BITSTREAM 4 +`define APB4_APU__ERROR_STAGE_ENTROPY 5 +`define APB4_APU__ERROR_STAGE_RECONSTRUCTION 6 +`define APB4_APU__ERROR_STAGE_RESAMPLER 7 +`define APB4_APU__ERROR_STAGE_DMA_WRITE 8 +`define APB4_APU__ERROR_STAGE_KWS_FRONTEND 9 +`define APB4_APU__ERROR_STAGE_KWS_INFERENCE 10 +`define APB4_APU__ERROR_STAGE_LIFECYCLE 11 + +`define APB4_APU__OWNER_STATUS_OWNER 0 +`define APB4_APU__OWNER_STATUS_LOCK 8 +`define APB4_APU__OWNER_STATUS_QUIESCE 9 +`define APB4_APU__OWNER_STATUS_RESET 10 +`define APB4_APU__PERF_CONTROL_ENABLE 0 +`define APB4_APU__PERF_CONTROL_CLEAR 1 +`define APB4_APU__PERF_CONTROL_SNAPSHOT 2 +`define APB4_APU__PERF_STATUS_SNAPSHOT_VALID 0 +// verilog_format: on + +`endif diff --git a/rtl/ip/multimedia/apu_reg.sv b/rtl/ip/multimedia/apu_reg.sv new file mode 100644 index 00000000..274a3527 --- /dev/null +++ b/rtl/ip/multimedia/apu_reg.sv @@ -0,0 +1,561 @@ +// Copyright (c) 2026 Yuchi Miao +// retroSoC is licensed under Mulan PSL v2. + +`include "apu_define.svh" + +module apu_reg ( + // verilog_format: off -- preserve the APB and lifecycle boundary columns + input logic clk_i, + input logic rst_n_i, + input logic [1:0] owner_i, + input logic owner_lock_i, + input logic quiesce_i, + input logic resource_reset_i, + apb4_if.slave apb4, + output logic idle_o, + output logic irq_o + // verilog_format: on +); + localparam logic [31:0] IpId = 32'h4150_5530; + localparam logic [31:0] IpVersion = 32'h0001_0000; + localparam logic [31:0] Capability0 = 32'd0; + localparam logic [31:0] Capability1 = 32'd0; + localparam logic [31:0] AbiDigest = 32'd0; + localparam logic [10:0] IrqMask = 11'h7ff; + localparam logic [31:0] TimeoutReset = 32'h0000_ffff; + localparam logic [31:0] RingCoalesceReset = 32'h0001_0001; + localparam logic [31:0] KwsConfigReset = 32'h0000_0380; + localparam logic [3:0][31:0] AclReset = {32'd0, 32'hffff_ffff, 32'd0, 32'hffff_ffff}; + localparam logic [4:0][31:0] RingReset = {RingCoalesceReset, 32'd0, 32'd0, 32'd0, 32'd0}; + + logic s_apb4_ready_d, s_apb4_ready_q; + logic [31:0] s_apb4_rdata_q; + logic s_apb4_resp_err_d, s_apb4_resp_err_q; + logic s_req_accept; + logic s_write; + logic [11:0] s_offset; + logic [31:0] s_strobed_write; + logic [31:0] s_merged_write; + logic s_read_err; + logic s_write_err; + logic s_write_unsupported; + logic [31:0] s_read_data; + logic s_soft_reset; + logic s_cnt_clear; + logic [10:0] s_irq_clear; + logic [10:0] s_irq_set; + + logic [10:0] s_irq_state_d, s_irq_state_q; + logic [10:0] s_irq_en_d, s_irq_en_q; + logic [31:0] s_err_stat_d, s_err_stat_q; + logic [31:0] s_err_addr_d, s_err_addr_q; + logic [31:0] s_err_detail_d, s_err_detail_q; + logic [1:0][31:0] s_timeout_d, s_timeout_q; + logic [3:0] s_stream_route_d, s_stream_route_q; + logic [3:0][31:0] s_acl_d, s_acl_q; + logic [2:0][31:0] s_mc_cfg_d, s_mc_cfg_q; + logic [7:0][31:0] s_job_cfg_d, s_job_cfg_q; + logic [4:0][31:0] s_ring_cfg_d, s_ring_cfg_q; + logic [2:0][31:0] s_kws_model_cfg_d, s_kws_model_cfg_q; + logic [15:0] s_kws_cfg_d, s_kws_cfg_q; + logic s_perf_en_d, s_perf_en_q; + logic s_perf_snapshot_valid_d, s_perf_snapshot_valid_q; + + function automatic logic [31:0] apply_wstrb( + input logic [31:0] previous_i, input logic [31:0] value_i, input logic [3:0] strobe_i); + logic [31:0] s_value; + begin + s_value = previous_i; + for (int unsigned byte_index = 0; byte_index < 4; byte_index++) begin + if (strobe_i[byte_index]) begin + s_value[(byte_index*8)+:8] = value_i[(byte_index*8)+:8]; + end + end + return s_value; + end + endfunction + + function automatic logic is_power_of_two(input logic [31:0] value_i); + return (value_i != 32'd0) && ((value_i & (value_i - 1'b1)) == 32'd0); + endfunction + + assign s_req_accept = apb4.psel && apb4.penable && !s_apb4_ready_q; + assign s_write = s_req_accept && apb4.pwrite; + assign s_offset = apb4.paddr[11:0]; + assign s_strobed_write = apply_wstrb(32'd0, apb4.pwdata, apb4.pstrb); + assign s_apb4_ready_d = s_req_accept; + assign s_apb4_resp_err_d = s_req_accept && (apb4.pwrite ? s_write_err : s_read_err); + assign apb4.pready = s_apb4_ready_q; + assign apb4.prdata = s_apb4_rdata_q; + assign apb4.pslverr = s_apb4_resp_err_q; + assign idle_o = 1'b1; + assign irq_o = (s_irq_state_q & s_irq_en_q) != 11'd0; + + always_comb begin + s_read_data = 32'd0; + s_read_err = s_offset[1:0] != 2'b00; + if (!s_read_err) begin + unique case (s_offset) + `APB4_APU__IP_ID: s_read_data = IpId; + `APB4_APU__IP_VERSION: s_read_data = IpVersion; + `APB4_APU__CAPABILITY0: s_read_data = Capability0; + `APB4_APU__CAPABILITY1: s_read_data = Capability1; + `APB4_APU__STATUS: s_read_data = {22'd0, 1'b0, 1'b1, 1'b0, quiesce_i, 6'd0}; + `APB4_APU__IRQ_STATE: s_read_data = {21'd0, s_irq_state_q}; + `APB4_APU__IRQ_ENABLE: s_read_data = {21'd0, s_irq_en_q}; + `APB4_APU__ERROR_STATUS: s_read_data = s_err_stat_q; + `APB4_APU__ERROR_ADDRESS: s_read_data = s_err_addr_q; + `APB4_APU__ERROR_DETAIL: s_read_data = s_err_detail_q; + `APB4_APU__SEQUENCER_TIMEOUT: s_read_data = s_timeout_q[0]; + `APB4_APU__STREAM_ROUTE: s_read_data = {28'd0, s_stream_route_q}; + `APB4_APU__STREAM_STATUS: s_read_data = 32'd0; + `APB4_APU__OWNER_STATUS: + s_read_data = {21'd0, resource_reset_i, quiesce_i, owner_lock_i, 6'd0, owner_i}; + `APB4_APU__READ_BASE: s_read_data = s_acl_q[0]; + `APB4_APU__READ_LIMIT: s_read_data = s_acl_q[1]; + `APB4_APU__WRITE_BASE: s_read_data = s_acl_q[2]; + `APB4_APU__WRITE_LIMIT: s_read_data = s_acl_q[3]; + `APB4_APU__DMA_TIMEOUT: s_read_data = s_timeout_q[1]; + `APB4_APU__ABI_DIGEST: s_read_data = AbiDigest; + `APB4_APU__SEQUENCER_STATUS, `APB4_APU__SEQUENCER_RETIRED, + `APB4_APU__MC_STATUS, `APB4_APU__MC_ABI, `APB4_APU__MC_BUILD_ID_LO, + `APB4_APU__MC_BUILD_ID_HI, `APB4_APU__MC_LOCK, `APB4_APU__MC_ACTUAL_CRC, + `APB4_APU__MC_LOAD_COUNT, `APB4_APU__JOB_STATUS, `APB4_APU__JOB_INPUT_USED, + `APB4_APU__JOB_OUTPUT_BYTES, `APB4_APU__JOB_FRAMES, + `APB4_APU__JOB_SOURCE_INFO, `APB4_APU__JOB_CYCLES, `APB4_APU__JOB_DETAIL, + `APB4_APU__RING_HEAD, `APB4_APU__RING_STATUS, `APB4_APU__RING_COMPLETED, + `APB4_APU__KWS_STATUS, `APB4_APU__KWS_RESULT, `APB4_APU__KWS_TIMESTAMP_LO, + `APB4_APU__KWS_TIMESTAMP_HI, `APB4_APU__KWS_FRAME_COUNT, + `APB4_APU__KWS_INFERENCE_COUNT, `APB4_APU__KWS_HIT_COUNT, + `APB4_APU__KWS_OVERRUN_COUNT, `APB4_APU__KWS_MODEL_STATUS, + `APB4_APU__KWS_MODEL_ACTUAL_CRC: + s_read_data = 32'd0; + `APB4_APU__MC_IMAGE_ADDRESS: s_read_data = s_mc_cfg_q[0]; + `APB4_APU__MC_IMAGE_SIZE: s_read_data = s_mc_cfg_q[1]; + `APB4_APU__MC_EXPECTED_CRC: s_read_data = s_mc_cfg_q[2]; + `APB4_APU__JOB_CONTROL: s_read_data = s_job_cfg_q[0]; + `APB4_APU__JOB_INPUT_ADDRESS: s_read_data = s_job_cfg_q[1]; + `APB4_APU__JOB_INPUT_LENGTH: s_read_data = s_job_cfg_q[2]; + `APB4_APU__JOB_OUTPUT_ADDRESS: s_read_data = s_job_cfg_q[3]; + `APB4_APU__JOB_OUTPUT_CAPACITY: s_read_data = s_job_cfg_q[4]; + `APB4_APU__JOB_INPUT_CONFIG: s_read_data = s_job_cfg_q[5]; + `APB4_APU__JOB_OUTPUT_CONFIG: s_read_data = s_job_cfg_q[6]; + `APB4_APU__JOB_FLAGS: s_read_data = s_job_cfg_q[7]; + `APB4_APU__RING_BASE: s_read_data = s_ring_cfg_q[0]; + `APB4_APU__RING_SIZE: s_read_data = s_ring_cfg_q[1]; + `APB4_APU__RING_TAIL: s_read_data = s_ring_cfg_q[2]; + `APB4_APU__RING_CONTROL: s_read_data = s_ring_cfg_q[3]; + `APB4_APU__RING_COALESCE: s_read_data = s_ring_cfg_q[4]; + `APB4_APU__KWS_MODEL_ADDRESS: s_read_data = s_kws_model_cfg_q[0]; + `APB4_APU__KWS_MODEL_SIZE: s_read_data = s_kws_model_cfg_q[1]; + `APB4_APU__KWS_MODEL_EXPECTED_CRC: s_read_data = s_kws_model_cfg_q[2]; + `APB4_APU__KWS_CONTROL: s_read_data = 32'd0; + `APB4_APU__KWS_CONFIG: s_read_data = {16'd0, s_kws_cfg_q}; + `APB4_APU__PERF_CONTROL: s_read_data = {31'd0, s_perf_en_q}; + `APB4_APU__PERF_STATUS: s_read_data = {31'd0, s_perf_snapshot_valid_q}; + `APB4_APU__PERF_ACTIVE_CYCLES_LO, `APB4_APU__PERF_ACTIVE_CYCLES_HI, + `APB4_APU__PERF_INPUT_BYTES_LO, `APB4_APU__PERF_INPUT_BYTES_HI, + `APB4_APU__PERF_OUTPUT_BYTES_LO, `APB4_APU__PERF_OUTPUT_BYTES_HI, + `APB4_APU__PERF_DECODED_FRAMES_LO, `APB4_APU__PERF_DECODED_FRAMES_HI, + `APB4_APU__PERF_DMA_READ_STALLS_LO, `APB4_APU__PERF_DMA_READ_STALLS_HI, + `APB4_APU__PERF_DMA_WRITE_STALLS_LO, `APB4_APU__PERF_DMA_WRITE_STALLS_HI, + `APB4_APU__PERF_STREAM_STALLS_LO, `APB4_APU__PERF_STREAM_STALLS_HI, + `APB4_APU__PERF_SEQUENCER_INSTR_LO, `APB4_APU__PERF_SEQUENCER_INSTR_HI, + `APB4_APU__PERF_KWS_CYCLES_LO, `APB4_APU__PERF_KWS_CYCLES_HI, + `APB4_APU__PERF_FAULTS_LO, `APB4_APU__PERF_FAULTS_HI: + s_read_data = 32'd0; + `APB4_APU__COMMAND, `APB4_APU__IRQ_TEST, `APB4_APU__RING_DOORBELL: begin + s_read_data = 32'd0; + s_read_err = 1'b1; + end + default: begin + s_read_data = 32'd0; + s_read_err = 1'b1; + end + endcase + end + end + + always_comb begin + s_write_err = s_offset[1:0] != 2'b00; + s_write_unsupported = 1'b0; + s_merged_write = s_strobed_write; + if (!s_write_err) begin + unique case (s_offset) + `APB4_APU__COMMAND: begin + s_write_unsupported = (apb4.pwdata & 32'h0000_003b) != 32'd0; + s_write_err = (apb4.pstrb != 4'hf) || + ((apb4.pwdata & 32'hffff_ff80) != 32'd0) || + s_write_unsupported; + end + `APB4_APU__IRQ_STATE: s_write_err = (s_strobed_write & ~32'(IrqMask)) != 32'd0; + `APB4_APU__IRQ_ENABLE: begin + s_merged_write = apply_wstrb({21'd0, s_irq_en_q}, apb4.pwdata, apb4.pstrb); + s_write_err = (s_merged_write & ~32'(IrqMask)) != 32'd0; + end + `APB4_APU__IRQ_TEST: + s_write_err = (apb4.pstrb != 4'hf) || ((apb4.pwdata & ~32'(IrqMask)) != 32'd0); + `APB4_APU__ERROR_STATUS: s_write_err = (s_strobed_write & 32'hffff_fffe) != 32'd0; + `APB4_APU__SEQUENCER_TIMEOUT: begin + s_merged_write = apply_wstrb(s_timeout_q[0], apb4.pwdata, apb4.pstrb); + s_write_err = s_merged_write == 32'd0; + end + `APB4_APU__STREAM_ROUTE: begin + s_merged_write = apply_wstrb({28'd0, s_stream_route_q}, apb4.pwdata, apb4.pstrb); + s_write_unsupported = s_merged_write != 32'd0; + s_write_err = s_write_unsupported; + end + `APB4_APU__READ_BASE, `APB4_APU__READ_LIMIT, `APB4_APU__WRITE_BASE, + `APB4_APU__WRITE_LIMIT, `APB4_APU__MC_EXPECTED_CRC, + `APB4_APU__KWS_MODEL_EXPECTED_CRC: + s_write_err = apb4.pstrb != 4'hf; + `APB4_APU__DMA_TIMEOUT: begin + s_merged_write = apply_wstrb(s_timeout_q[1], apb4.pwdata, apb4.pstrb); + s_write_err = s_merged_write == 32'd0; + end + `APB4_APU__MC_IMAGE_ADDRESS, `APB4_APU__KWS_MODEL_ADDRESS: + s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata[5:0] != 6'd0); + `APB4_APU__MC_IMAGE_SIZE, `APB4_APU__KWS_MODEL_SIZE: + s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata == 32'd0); + `APB4_APU__JOB_CONTROL: begin + s_merged_write = apply_wstrb(s_job_cfg_q[0], apb4.pwdata, apb4.pstrb); + s_write_err = (s_merged_write[31:12] != 20'd0) || + (s_merged_write[3:0] > 4'd1) || + (s_merged_write[7:4] > 4'd2) || (s_merged_write[9:8] > 2'd1); + end + `APB4_APU__JOB_INPUT_ADDRESS, `APB4_APU__JOB_INPUT_LENGTH, + `APB4_APU__JOB_OUTPUT_ADDRESS, `APB4_APU__JOB_OUTPUT_CAPACITY: + s_write_err = apb4.pstrb != 4'hf; + `APB4_APU__JOB_INPUT_CONFIG: begin + s_merged_write = apply_wstrb(s_job_cfg_q[5], apb4.pwdata, apb4.pstrb); + s_write_err = s_merged_write[31:26] != 6'd0; + end + `APB4_APU__JOB_OUTPUT_CONFIG: begin + s_merged_write = apply_wstrb(s_job_cfg_q[6], apb4.pwdata, apb4.pstrb); + s_write_err = (s_merged_write[31:21] != 11'd0) || (s_merged_write[20:19] > 2'd1); + end + `APB4_APU__JOB_FLAGS: begin + s_merged_write = apply_wstrb(s_job_cfg_q[7], apb4.pwdata, apb4.pstrb); + s_write_err = s_merged_write[31:1] != 31'd0; + end + `APB4_APU__RING_BASE: s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata[6:0] != 7'd0); + `APB4_APU__RING_SIZE: + s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata < 32'd2) || + (apb4.pwdata > 32'd256) || !is_power_of_two(apb4.pwdata); + `APB4_APU__RING_TAIL: s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata[31:8] != 24'd0); + `APB4_APU__RING_CONTROL: s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata[31:2] != 30'd0); + `APB4_APU__RING_COALESCE: + s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata[15:8] != 8'd0) || + (apb4.pwdata[7:0] == 8'd0) || (apb4.pwdata[31:16] == 16'd0); + `APB4_APU__RING_DOORBELL: begin + s_write_unsupported = 1'b1; + s_write_err = 1'b1; + end + `APB4_APU__KWS_CONTROL: begin + s_write_unsupported = apb4.pwdata[1:0] != 2'd0; + s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata[31:3] != 29'd0) || s_write_unsupported; + end + `APB4_APU__KWS_CONFIG: begin + s_merged_write = apply_wstrb({16'd0, s_kws_cfg_q}, apb4.pwdata, apb4.pstrb); + s_write_err = (s_merged_write[31:16] != 16'd0) || (s_merged_write[15:8] == 8'd0); + end + `APB4_APU__PERF_CONTROL: begin + s_merged_write = apply_wstrb({31'd0, s_perf_en_q}, apb4.pwdata, apb4.pstrb); + s_write_err = s_merged_write[31:3] != 29'd0; + end + `APB4_APU__IP_ID, `APB4_APU__IP_VERSION, `APB4_APU__CAPABILITY0, + `APB4_APU__CAPABILITY1, `APB4_APU__STATUS, `APB4_APU__STREAM_STATUS, + `APB4_APU__OWNER_STATUS, `APB4_APU__ABI_DIGEST, + `APB4_APU__SEQUENCER_STATUS, `APB4_APU__SEQUENCER_RETIRED, + `APB4_APU__MC_STATUS, `APB4_APU__MC_ABI, `APB4_APU__MC_BUILD_ID_LO, + `APB4_APU__MC_BUILD_ID_HI, `APB4_APU__MC_LOCK, `APB4_APU__MC_ACTUAL_CRC, + `APB4_APU__MC_LOAD_COUNT, `APB4_APU__JOB_STATUS, `APB4_APU__JOB_INPUT_USED, + `APB4_APU__JOB_OUTPUT_BYTES, `APB4_APU__JOB_FRAMES, + `APB4_APU__JOB_SOURCE_INFO, `APB4_APU__JOB_CYCLES, `APB4_APU__JOB_DETAIL, + `APB4_APU__RING_HEAD, `APB4_APU__RING_STATUS, `APB4_APU__RING_COMPLETED, + `APB4_APU__KWS_STATUS, `APB4_APU__KWS_RESULT, `APB4_APU__KWS_TIMESTAMP_LO, + `APB4_APU__KWS_TIMESTAMP_HI, `APB4_APU__KWS_FRAME_COUNT, + `APB4_APU__KWS_INFERENCE_COUNT, `APB4_APU__KWS_HIT_COUNT, + `APB4_APU__KWS_OVERRUN_COUNT, `APB4_APU__KWS_MODEL_STATUS, + `APB4_APU__KWS_MODEL_ACTUAL_CRC, `APB4_APU__PERF_STATUS, + `APB4_APU__PERF_ACTIVE_CYCLES_LO, `APB4_APU__PERF_ACTIVE_CYCLES_HI, + `APB4_APU__PERF_INPUT_BYTES_LO, `APB4_APU__PERF_INPUT_BYTES_HI, + `APB4_APU__PERF_OUTPUT_BYTES_LO, `APB4_APU__PERF_OUTPUT_BYTES_HI, + `APB4_APU__PERF_DECODED_FRAMES_LO, `APB4_APU__PERF_DECODED_FRAMES_HI, + `APB4_APU__PERF_DMA_READ_STALLS_LO, `APB4_APU__PERF_DMA_READ_STALLS_HI, + `APB4_APU__PERF_DMA_WRITE_STALLS_LO, `APB4_APU__PERF_DMA_WRITE_STALLS_HI, + `APB4_APU__PERF_STREAM_STALLS_LO, `APB4_APU__PERF_STREAM_STALLS_HI, + `APB4_APU__PERF_SEQUENCER_INSTR_LO, `APB4_APU__PERF_SEQUENCER_INSTR_HI, + `APB4_APU__PERF_KWS_CYCLES_LO, `APB4_APU__PERF_KWS_CYCLES_HI, + `APB4_APU__PERF_FAULTS_LO, `APB4_APU__PERF_FAULTS_HI: + s_write_err = 1'b1; + default: s_write_err = 1'b1; + endcase + end + end + + always_comb begin + s_irq_state_d = s_irq_state_q; + s_irq_en_d = s_irq_en_q; + s_err_stat_d = s_err_stat_q; + s_err_addr_d = s_err_addr_q; + s_err_detail_d = s_err_detail_q; + s_timeout_d = s_timeout_q; + s_stream_route_d = s_stream_route_q; + s_acl_d = s_acl_q; + s_mc_cfg_d = s_mc_cfg_q; + s_job_cfg_d = s_job_cfg_q; + s_ring_cfg_d = s_ring_cfg_q; + s_kws_model_cfg_d = s_kws_model_cfg_q; + s_kws_cfg_d = s_kws_cfg_q; + s_perf_en_d = s_perf_en_q; + s_perf_snapshot_valid_d = s_perf_snapshot_valid_q; + s_soft_reset = 1'b0; + s_cnt_clear = 1'b0; + s_irq_clear = 11'd0; + s_irq_set = 11'd0; + + if (s_write && !s_write_err) begin + unique case (s_offset) + `APB4_APU__COMMAND: begin + s_soft_reset = apb4.pwdata[`APB4_APU__COMMAND_SOFT_RESET]; + s_cnt_clear = apb4.pwdata[`APB4_APU__COMMAND_CLEAR_COUNTERS]; + end + `APB4_APU__IRQ_STATE: s_irq_clear = s_strobed_write[10:0]; + `APB4_APU__IRQ_ENABLE: s_irq_en_d = s_merged_write[10:0]; + `APB4_APU__IRQ_TEST: s_irq_set = apb4.pwdata[10:0]; + `APB4_APU__ERROR_STATUS: begin + if (s_strobed_write[0]) begin + s_err_stat_d = 32'd0; + s_err_addr_d = 32'd0; + s_err_detail_d = 32'd0; + end + end + `APB4_APU__SEQUENCER_TIMEOUT: s_timeout_d[0] = s_merged_write; + `APB4_APU__STREAM_ROUTE: s_stream_route_d = s_merged_write[3:0]; + `APB4_APU__READ_BASE: s_acl_d[0] = apb4.pwdata; + `APB4_APU__READ_LIMIT: s_acl_d[1] = apb4.pwdata; + `APB4_APU__WRITE_BASE: s_acl_d[2] = apb4.pwdata; + `APB4_APU__WRITE_LIMIT: s_acl_d[3] = apb4.pwdata; + `APB4_APU__DMA_TIMEOUT: s_timeout_d[1] = s_merged_write; + `APB4_APU__MC_IMAGE_ADDRESS: s_mc_cfg_d[0] = apb4.pwdata; + `APB4_APU__MC_IMAGE_SIZE: s_mc_cfg_d[1] = apb4.pwdata; + `APB4_APU__MC_EXPECTED_CRC: s_mc_cfg_d[2] = apb4.pwdata; + `APB4_APU__JOB_CONTROL: s_job_cfg_d[0] = s_merged_write; + `APB4_APU__JOB_INPUT_ADDRESS: s_job_cfg_d[1] = apb4.pwdata; + `APB4_APU__JOB_INPUT_LENGTH: s_job_cfg_d[2] = apb4.pwdata; + `APB4_APU__JOB_OUTPUT_ADDRESS: s_job_cfg_d[3] = apb4.pwdata; + `APB4_APU__JOB_OUTPUT_CAPACITY: s_job_cfg_d[4] = apb4.pwdata; + `APB4_APU__JOB_INPUT_CONFIG: s_job_cfg_d[5] = s_merged_write; + `APB4_APU__JOB_OUTPUT_CONFIG: s_job_cfg_d[6] = s_merged_write; + `APB4_APU__JOB_FLAGS: s_job_cfg_d[7] = s_merged_write; + `APB4_APU__RING_BASE: s_ring_cfg_d[0] = apb4.pwdata; + `APB4_APU__RING_SIZE: s_ring_cfg_d[1] = apb4.pwdata; + `APB4_APU__RING_TAIL: s_ring_cfg_d[2] = apb4.pwdata; + `APB4_APU__RING_CONTROL: s_ring_cfg_d[3] = apb4.pwdata; + `APB4_APU__RING_COALESCE: s_ring_cfg_d[4] = apb4.pwdata; + `APB4_APU__KWS_MODEL_ADDRESS: s_kws_model_cfg_d[0] = apb4.pwdata; + `APB4_APU__KWS_MODEL_SIZE: s_kws_model_cfg_d[1] = apb4.pwdata; + `APB4_APU__KWS_MODEL_EXPECTED_CRC: s_kws_model_cfg_d[2] = apb4.pwdata; + `APB4_APU__KWS_CONFIG: s_kws_cfg_d = s_merged_write[15:0]; + `APB4_APU__PERF_CONTROL: begin + s_perf_en_d = s_merged_write[`APB4_APU__PERF_CONTROL_ENABLE]; + if (s_merged_write[`APB4_APU__PERF_CONTROL_CLEAR]) begin + s_cnt_clear = 1'b1; + end + if (s_merged_write[`APB4_APU__PERF_CONTROL_SNAPSHOT]) begin + s_perf_snapshot_valid_d = 1'b1; + end + end + default: begin + end + endcase + end + + if (s_cnt_clear) begin + s_perf_snapshot_valid_d = 1'b0; + end + if (s_req_accept && (apb4.pwrite ? s_write_err : s_read_err)) begin + s_irq_set[`APB4_APU__IRQ_FIRST_ERROR] = 1'b1; + if (!s_err_stat_q[`APB4_APU__ERROR_VALID]) begin + s_err_stat_d = 32'd1 | + (32'((apb4.pwrite && s_write_unsupported) ? + `APB4_APU__ERROR_CODE_UNSUPPORTED : + `APB4_APU__ERROR_CODE_INVALID_CONFIG) + << `APB4_APU__ERROR_CODE) | + (32'(`APB4_APU__ERROR_STAGE_APB) << `APB4_APU__ERROR_STAGE); + s_err_addr_d = apb4.paddr; + s_err_detail_d = apb4.pwrite ? apb4.pwdata : {20'd0, s_offset}; + end + end + s_irq_state_d = (s_irq_state_d & ~s_irq_clear) | s_irq_set; + + if (s_soft_reset || resource_reset_i) begin + s_irq_state_d = 11'd0; + s_irq_en_d = 11'd0; + s_err_stat_d = 32'd0; + s_err_addr_d = 32'd0; + s_err_detail_d = 32'd0; + s_timeout_d[0] = TimeoutReset; + s_timeout_d[1] = TimeoutReset; + s_stream_route_d = 4'd0; + s_mc_cfg_d = '0; + s_job_cfg_d = '0; + s_ring_cfg_d = RingReset; + s_kws_model_cfg_d = '0; + s_kws_cfg_d = KwsConfigReset[15:0]; + s_perf_en_d = 1'b0; + s_perf_snapshot_valid_d = 1'b0; + end + end + + dffr #( + .DATA_WIDTH(1) + ) u_ready_dffr ( + .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_resp_err_dffr ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_apb4_resp_err_d), + .dat_o (s_apb4_resp_err_q) + ); + dffer #( + .DATA_WIDTH(32) + ) u_rdata_dffer ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .en_i (s_req_accept && !apb4.pwrite), + .dat_i (s_read_data), + .dat_o (s_apb4_rdata_q) + ); + dffr #( + .DATA_WIDTH(11) + ) u_irq_state_dffr ( + .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(11) + ) u_irq_enable_dffr ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_irq_en_d), + .dat_o (s_irq_en_q) + ); + dffr #( + .DATA_WIDTH(32) + ) u_error_status_dffr ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_err_stat_d), + .dat_o (s_err_stat_q) + ); + dffr #( + .DATA_WIDTH(32) + ) u_error_address_dffr ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_err_addr_d), + .dat_o (s_err_addr_q) + ); + dffr #( + .DATA_WIDTH(32) + ) u_error_detail_dffr ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_err_detail_d), + .dat_o (s_err_detail_q) + ); + dffrc #( + .DATA_WIDTH(2 * 32), + .RESET_VAL ({TimeoutReset, TimeoutReset}) + ) u_timeout_dffrc ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_timeout_d), + .dat_o (s_timeout_q) + ); + dffr #( + .DATA_WIDTH(4) + ) u_stream_route_dffr ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_stream_route_d), + .dat_o (s_stream_route_q) + ); + dffrc #( + .DATA_WIDTH(4 * 32), + .RESET_VAL (AclReset) + ) u_acl_dffrc ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_acl_d), + .dat_o (s_acl_q) + ); + dffr #( + .DATA_WIDTH(3 * 32) + ) u_mc_cfg_dffr ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_mc_cfg_d), + .dat_o (s_mc_cfg_q) + ); + dffr #( + .DATA_WIDTH(8 * 32) + ) u_job_cfg_dffr ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_job_cfg_d), + .dat_o (s_job_cfg_q) + ); + dffrc #( + .DATA_WIDTH(5 * 32), + .RESET_VAL (RingReset) + ) u_ring_cfg_dffrc ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_ring_cfg_d), + .dat_o (s_ring_cfg_q) + ); + dffr #( + .DATA_WIDTH(3 * 32) + ) u_kws_model_cfg_dffr ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_kws_model_cfg_d), + .dat_o (s_kws_model_cfg_q) + ); + dffrc #( + .DATA_WIDTH(16), + .RESET_VAL (KwsConfigReset[15:0]) + ) u_kws_cfg_dffrc ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_kws_cfg_d), + .dat_o (s_kws_cfg_q) + ); + dffr #( + .DATA_WIDTH(1) + ) u_perf_enable_dffr ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_perf_en_d), + .dat_o (s_perf_en_q) + ); + dffr #( + .DATA_WIDTH(1) + ) u_perf_snapshot_valid_dffr ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_perf_snapshot_valid_d), + .dat_o (s_perf_snapshot_valid_q) + ); +endmodule diff --git a/rtl/mini/address_map/memory_map.json b/rtl/mini/address_map/memory_map.json index 9c646ed2..5116dd43 100644 --- a/rtl/mini/address_map/memory_map.json +++ b/rtl/mini/address_map/memory_map.json @@ -421,9 +421,9 @@ "symbol": "APB4_APU", "base": "0x10013000", "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 c07e6098..b9b3fcb1 100644 --- a/rtl/mini/filelist/ip.fl +++ b/rtl/mini/filelist/ip.fl @@ -125,6 +125,8 @@ /ip/multimedia/dvp_core.sv /ip/multimedia/dvp_reg.sv /ip/multimedia/axi4s_dvp.sv +/ip/multimedia/apu_reg.sv +/ip/multimedia/apb4_apu.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 15f21940..c4646776 100644 --- a/rtl/mini/integration/README.md +++ b/rtl/mini/integration/README.md @@ -22,8 +22,8 @@ must appear exactly once. Disabled targets keep their slot and interface declaration but cannot own an address region. This list stays separate from `apb4_system_targets`, which owns the `0x2000_0000` `apb4_system` island. Slot 15 is the active SDIO0 management window; SDIO1 is appended at slot 18, -the management-only crypto controller at slot 19, and USB2 at slot 20, so the -existing slots 0..17 remain stable. +the management-only crypto controller at slot 19, USB2 at slot 20, JPEG at +slot 26, and the APU shell at slot 27, so the existing slots remain stable. Each `gpio_alt_functions` entry defines both alternate modes for one GPIO pin. `inputs` are driven from the selected GPIO input, and `do` and `oe` define the @@ -54,7 +54,8 @@ Existing allocated core IRQ bits retain their compatibility mapping; additions must use currently unallocated bits and preserve the existing entries. SDIO0 and SDIO1 use APB4 group bits 16 and 17 and core bits 10 and 21. Crypto uses APB4 group bit 18 and core bit 23. USB2 uses APB4 group bit 19 and core bit 24. -All four remain management-only in the +JPEG uses group bit 22 and core bit 30; APU uses group bit 23 and core bit 31. +All resource-owned sources remain management-only in the generated user IRQ mask. Removing a source leaves its core IRQ bit unallocated and driven low. Firmware does not expose a generic external interrupt API until the SoC includes a claim/complete diff --git a/rtl/mini/integration/generate_soc_topology.py b/rtl/mini/integration/generate_soc_topology.py index d9e23a05..07553d83 100644 --- a/rtl/mini/integration/generate_soc_topology.py +++ b/rtl/mini/integration/generate_soc_topology.py @@ -48,6 +48,7 @@ COMPATIBILITY_IRQ_BINDINGS = ( ("watchdog_early_warning", "apb4_system", 3, 14, "u_wdg_if.irq_o"), ("rng", "apb4_system", 4, 16, "s_rng_irq"), ("resource_fault", "apb4_system", 7, 29, "s_resource_fault_irq"), + ("apu", "apb4_periph", 23, 31, "resource_irq_lp_i[6]"), ("ws2812", "apb4_periph", 10, 17, "ws2812.irq_o"), ("gpio", "apb4_periph", 11, 18, "gpio.irq_o"), ("i2c1", "apb4_periph", 12, 19, "i2c1.irq_o"), diff --git a/rtl/mini/integration/soc_topology.json b/rtl/mini/integration/soc_topology.json index 779cff6f..290683ef 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": 23, + "width": 24, "top_signal": "s_apb4_periph_irq" }, { @@ -276,6 +276,15 @@ "core_bit": 29, "signal": "s_resource_fault_irq", "user_visible": false + }, + { + "name": "apu", + "description": "APU completion and error interrupt", + "group": "apb4_periph", + "group_bit": 23, + "core_bit": 31, + "signal": "resource_irq_lp_i[6]", + "user_visible": false } ], "wake_sources": [ @@ -600,6 +609,13 @@ "timed_interface": "u_jpeg_apb4_if", "pure_interface": "u_jpeg_apb4_pure_if", "region": "APB4_JPEG" + }, + { + "slot": 27, + "name": "apu", + "timed_interface": "u_apu_apb4_if", + "pure_interface": "u_apu_apb4_pure_if", + "region": "APB4_APU" } ], "apb4_system_targets": [ diff --git a/rtl/mini/mk/software.mk b/rtl/mini/mk/software.mk index fb33ca8f..4fc851b3 100644 --- a/rtl/mini/mk/software.mk +++ b/rtl/mini/mk/software.mk @@ -101,6 +101,7 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \ $(ROOT_PATH)/crt/src/hal/dma_math.c \ $(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/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 65a55d88..f9d92b43 100644 --- a/rtl/mini/top/apb4_periph.sv +++ b/rtl/mini/top/apb4_periph.sv @@ -23,8 +23,12 @@ module apb4_periph ( input logic debug_halted_i, input logic timebase_tick_i, input logic ext_h_hp_irq_i, - input logic [5:0] resource_irq_lp_i, - input logic [5:0] resource_irq_hp_i, + input logic [6:0] resource_irq_lp_i, + input logic [6:0] resource_irq_hp_i, + input logic [1:0] apu_owner_i, + input logic apu_owner_lock_i, + input logic apu_quiesce_i, + input logic apu_reset_i, input logic jpeg_quiesce_i, input logic jpeg_reset_i, axi4_if.slave cfg_axi4, @@ -67,8 +71,9 @@ module apb4_periph ( output logic hp_software_irq_o, output logic hp_machine_external_irq_o, output logic hp_supervisor_external_irq_o, + output logic apu_idle_o, output logic jpeg_idle_o, - output logic [5:0] resource_irq_raw_o, + output logic [6:0] resource_irq_raw_o, output logic [`SOC_IRQ_APB4_PERIPH_WIDTH-1:0] irq_o // verilog_format: on ); @@ -151,6 +156,7 @@ axi4_stream_if #( logic s_dma_crypto_in_proc; logic s_dma_crypto_out_proc; logic s_crypto_irq; + logic s_apu_irq_raw; logic s_jpeg_irq_raw; logic s_usb2_irq; logic s_tim0_irq, s_tim1_irq; @@ -210,7 +216,7 @@ 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_jpeg_irq_raw, spisd.irq_o, sdio1.irq_o, sdio0.irq_o, s_usb2_irq, s_dma_irq + 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 ( @@ -281,16 +287,17 @@ axi4_stream_if #( assign s_opipsram_irq_mem = u_opipsram_mem_if.irq_o; always_comb begin - s_hp_plic_source = '0; - s_hp_plic_source[1] = uart1.irq_o; - s_hp_plic_source[2] = s_mailbox_hp_irq; - s_hp_plic_source[3] = ext_h_hp_irq_i; - s_hp_plic_source[4] = resource_irq_hp_i[0]; - s_hp_plic_source[5] = resource_irq_hp_i[1]; - s_hp_plic_source[6] = resource_irq_hp_i[2]; - s_hp_plic_source[7] = resource_irq_hp_i[3]; - s_hp_plic_source[8] = resource_irq_hp_i[4]; - s_hp_plic_source[9] = resource_irq_hp_i[5]; + s_hp_plic_source = '0; + s_hp_plic_source[1] = uart1.irq_o; + s_hp_plic_source[2] = s_mailbox_hp_irq; + s_hp_plic_source[3] = ext_h_hp_irq_i; + s_hp_plic_source[4] = resource_irq_hp_i[0]; + s_hp_plic_source[5] = resource_irq_hp_i[1]; + s_hp_plic_source[6] = resource_irq_hp_i[2]; + s_hp_plic_source[7] = resource_irq_hp_i[3]; + 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]; end `include "apb4_periph_irq_bindings.svh" @@ -469,6 +476,18 @@ axi4_stream_if #( .irq_o (s_jpeg_irq_raw) ); + apb4_apu u_apb4_apu ( + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .owner_i (apu_owner_i), + .owner_lock_i (apu_owner_lock_i), + .quiesce_i (apu_quiesce_i), + .resource_reset_i(apu_reset_i), + .apb4 (u_apu_apb4_if), + .idle_o (apu_idle_o), + .irq_o (s_apu_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 6dcc6b62..b63a0ea5 100644 --- a/rtl/mini/top/apb4_system.sv +++ b/rtl/mini/top/apb4_system.sv @@ -23,9 +23,9 @@ module apb4_system ( input logic rst_aud_n_i, input logic debug_halted_i, input logic ext_h_data_idle_i, - input logic [6:0] resource_idle_i, - input logic [6:0] resource_block_ack_i, - input logic [5:0] resource_irq_i, + input logic [7:0] resource_idle_i, + input logic [7:0] resource_block_ack_i, + input logic [6:0] resource_irq_i, input logic cache_request_i, axi4_if.slave axi4, axi4_if.master ext_h_axi4, @@ -45,11 +45,12 @@ module apb4_system ( output logic ext_h_irq_raw_o, output logic [1:0] ext_h_owner_o, output logic cache_clean_o, - output logic [6:0][1:0] resource_owner_o, - output logic [6:0] resource_quiesce_o, - output logic [6:0] resource_reset_o, - output logic [5:0] resource_irq_lp_o, - output logic [5:0] resource_irq_hp_o, + output logic [7:0][1:0] resource_owner_o, + output logic [7:0] resource_owner_lock_o, + output logic [7:0] resource_quiesce_o, + output logic [7:0] resource_reset_o, + output logic [6:0] resource_irq_lp_o, + output logic [6:0] resource_irq_hp_o, output logic [`SOC_IRQ_APB4_SYSTEM_WIDTH-1:0] irq_o // verilog_format: on ); @@ -97,12 +98,13 @@ module apb4_system ( logic s_ext_h_irq; logic s_ext_h_irq_raw; logic [ 1:0] unused_ext_h_owner; - logic [ 6:0] s_resource_irq; - logic [ 6:0] s_resource_irq_lp; - logic [ 6:0] s_resource_irq_hp; - logic [ 6:0][1:0] s_resource_owner; - logic [ 6:0] s_resource_quiesce; - logic [ 6:0] s_resource_reset; + logic [ 7:0] s_resource_irq; + logic [ 7:0] s_resource_irq_lp; + logic [ 7:0] s_resource_irq_hp; + logic [ 7:0][1:0] s_resource_owner; + logic [ 7:0] s_resource_owner_lock; + logic [ 7:0] s_resource_quiesce; + logic [ 7:0] s_resource_reset; logic s_resource_fault_irq; logic s_cache_clean; logic s_unused_resource; @@ -110,16 +112,17 @@ module apb4_system ( logic s_unused_product_input; `endif - assign s_resource_irq = {resource_irq_i[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_quiesce_o = s_resource_quiesce; - assign resource_reset_o = s_resource_reset; - assign resource_irq_lp_o = {s_resource_irq_lp[6], s_resource_irq_lp[4:0]}; - assign resource_irq_hp_o = {s_resource_irq_hp[6], s_resource_irq_hp[4:0]}; + assign s_resource_irq = {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]}; `ifdef PDK_IHP130 localparam logic [31:0] ARCHINFO_TECHNOLOGY = 32'h0201_0082; @@ -231,7 +234,7 @@ module apb4_system ( apb4_crc u_apb4_crc (.apb4(u_crc_apb4_if)); resource_controller #( - .ResourceCount(7) + .ResourceCount(8) ) u_resource_controller ( .clk_i (clk_i), .rst_n_i (rst_n_i), @@ -241,6 +244,7 @@ module apb4_system ( .cache_request_i(cache_request_i), .cache_clean_o (s_cache_clean), .owner_o (s_resource_owner), + .owner_lock_o (s_resource_owner_lock), .quiesce_o (s_resource_quiesce), .reset_o (s_resource_reset), .irq_lp_o (s_resource_irq_lp), @@ -297,6 +301,6 @@ axi42apb4_system u_axi42apb4_system ( .ext_h_timeout_o (ext_h_timeout_o) ); - assign s_unused_resource = ^unused_ext_h_owner; + assign s_unused_resource = ^{unused_ext_h_owner, s_resource_irq_hp[5]}; endmodule diff --git a/rtl/mini/top/axi4_interconnect.sv b/rtl/mini/top/axi4_interconnect.sv index 985c1673..6f4565e6 100644 --- a/rtl/mini/top/axi4_interconnect.sv +++ b/rtl/mini/top/axi4_interconnect.sv @@ -433,6 +433,13 @@ module axi4_interconnect #( automatic logic [32:0] wrap_base = {1'b0, addr} & ~(burst_bytes - 1'b1); automatic logic + apu_lp_only = + `SOC_ADDR_IS_APB4_APU(addr) + && (((addr[11:0] >= 12'h040) && (addr[11:0] <= 12'h04c)) || + ((addr[11:0] >= 12'h080) && (addr[11:0] <= 12'h088)) || + ((addr[11:0] >= 12'h200) && (addr[11:0] <= 12'h208))); + automatic + logic burst_legal = (burst == `AXI4_BURST_TYPE_FIXED) || (burst == `AXI4_BURST_TYPE_INCR) || @@ -465,7 +472,7 @@ module axi4_interconnect #( `SOC_ADDR_IS_APB4_EXT_H(addr) && ! `SOC_ADDR_IS_APB4_RESOURCE_CTRL(addr) - ); + && !apu_lp_only); `else s_access_allowed[master] = (master != 1) || ((read_req && `SOC_USER_ADDR_READABLE(addr) diff --git a/rtl/mini/top/resource_controller.sv b/rtl/mini/top/resource_controller.sv index d5ac7adb..95b99796 100644 --- a/rtl/mini/top/resource_controller.sv +++ b/rtl/mini/top/resource_controller.sv @@ -2,7 +2,7 @@ // SPDX-License-Identifier: MulanPSL-2.0 module resource_controller #( - parameter int unsigned ResourceCount = 6 + parameter int unsigned ResourceCount = 8 ) ( // verilog_format: off -- preserve the resource ownership boundary columns input logic clk_i, @@ -13,6 +13,7 @@ module resource_controller #( input logic cache_request_i, output logic cache_clean_o, output logic [ResourceCount-1:0][1:0] owner_o, + output logic [ResourceCount-1:0] owner_lock_o, output logic [ResourceCount-1:0] quiesce_o, output logic [ResourceCount-1:0] reset_o, output logic [ResourceCount-1:0] irq_lp_o, @@ -69,6 +70,7 @@ module resource_controller #( assign apb4.pslverr = s_resp_err_q; assign apb4.prdata = s_rdata_q; assign owner_o = s_owner_q; + assign owner_lock_o = s_owner_lock_q; assign quiesce_o = s_quiesce_q; assign reset_o = s_reset_q; assign cache_clean_o = s_cache_clean_q; diff --git a/rtl/mini/top/retrosoc.sv b/rtl/mini/top/retrosoc.sv index 7ab61f1c..c630c5b4 100644 --- a/rtl/mini/top/retrosoc.sv +++ b/rtl/mini/top/retrosoc.sv @@ -232,18 +232,21 @@ module retrosoc ( logic [31:0] s_ext_h_timeout; logic s_ext_h_irq_raw; logic [ 1:0] s_ext_h_owner; - logic [ 6:0][1:0] s_resource_owner; - logic [ 6:0] s_resource_quiesce; - logic [ 6:0] s_resource_reset; + logic [ 7:0][1:0] s_resource_owner; + logic [ 7:0] s_resource_owner_lock; + logic [ 7:0] s_resource_quiesce; + logic [ 7:0] s_resource_reset; logic [ 6:0] s_resource_idle_hp; logic [ 6:0] s_resource_idle_pclk; logic [ 6:0] s_resource_block_ack_hp; logic [ 6:0] s_resource_block_ack_pclk; - logic [ 5:0] s_resource_irq_raw; - logic [ 5:0] s_resource_irq_lp; - logic [ 5:0] s_resource_irq_hp; + logic [ 6:0] s_resource_irq_raw; + logic [ 6:0] s_resource_irq_lp; + logic [ 6:0] s_resource_irq_hp; + logic s_apu_idle; logic s_jpeg_idle; - logic [ 6:0] s_resource_idle_combined; + logic [ 7:0] s_resource_idle_combined; + logic [ 7:0] s_resource_block_ack_combined; logic [31:0] s_fault_addr_mux; logic [ 3:0] s_fault_wstrb_mux; logic s_fault_reserved_mux; @@ -555,7 +558,7 @@ core_wrapper u_core_wrapper ( .block_new_i (s_hp_block_hp), .recovery_i (s_hp_recovery_hp), .flush_i (s_hp_flush_hp), - .resource_block_i (s_resource_quiesce | s_resource_reset), + .resource_block_i (s_resource_quiesce[6:0] | s_resource_reset[6:0]), .mem_pad_mode_i (s_mem_pad_mode_lp), .ext_h_block_i (s_ext_h_block), .ext_h_read_base_i (s_ext_h_read_base), @@ -797,6 +800,10 @@ core_wrapper u_core_wrapper ( .ext_h_hp_irq_i ((s_ext_h_owner == 2'd1) ? s_ext_h_irq_raw : 1'b0), .resource_irq_lp_i (s_resource_irq_lp), .resource_irq_hp_i (s_resource_irq_hp), + .apu_owner_i (s_resource_owner[7]), + .apu_owner_lock_i (s_resource_owner_lock[7]), + .apu_quiesce_i (s_resource_quiesce[7]), + .apu_reset_i (s_resource_reset[7]), .jpeg_quiesce_i (s_resource_quiesce[6]), .jpeg_reset_i (s_resource_reset[6]), .cfg_axi4 (u_cfg_pclk_axi4_if), @@ -840,6 +847,7 @@ core_wrapper u_core_wrapper ( .hp_machine_external_irq_o (s_hp_machine_external_irq), .hp_supervisor_external_irq_o(s_hp_supervisor_external_irq), .resource_irq_raw_o (s_resource_irq_raw), + .apu_idle_o (s_apu_idle), .jpeg_idle_o (s_jpeg_idle), .irq_o (s_apb4_periph_irq) ); @@ -853,49 +861,54 @@ core_wrapper u_core_wrapper ( ); apb4_system u_apb4_system ( - .clk_i (clk_pclk_i), - .rst_n_i (rst_pclk_n_i), - .clk_aud_i (clk_aud_i), - .rst_aud_n_i (rst_aud_n_i), - .debug_halted_i (s_mgmt_debug_halted), - .ext_h_data_idle_i (s_ext_h_data_idle), - .resource_idle_i (s_resource_idle_combined), - .resource_block_ack_i(s_resource_block_ack_pclk), - .resource_irq_i (s_resource_irq_raw), - .cache_request_i (s_hp_cache_req_pclk), - .ext_h_axi4 (u_ext_h_wide_axi4_if), - .fabric_monitor (u_fabric_monitor_pclk_if), - .axi4 (u_system_pclk_axi4_if), - .pwm (u_pwm_if), - .ps2 (u_ps2_if), - .ip_sel_i (u_sysctrl_if.ip_sel_o), - .user_gpio (u_user_gpio_if), - .rtc_wake_o (s_rtc_wake), - .wdg_reset_req_o (wdg_reset_req_o), - .ext_h_block_o (s_ext_h_block), - .ext_h_read_base_o (s_ext_h_read_base), - .ext_h_read_limit_o (s_ext_h_read_limit), - .ext_h_write_base_o (s_ext_h_write_base), - .ext_h_write_limit_o (s_ext_h_write_limit), - .ext_h_timeout_o (s_ext_h_timeout), - .ext_h_irq_raw_o (s_ext_h_irq_raw), - .ext_h_owner_o (s_ext_h_owner), - .cache_clean_o (s_hp_cache_clean_pclk), - .resource_owner_o (s_resource_owner), - .resource_quiesce_o (s_resource_quiesce), - .resource_reset_o (s_resource_reset), - .resource_irq_lp_o (s_resource_irq_lp), - .resource_irq_hp_o (s_resource_irq_hp), - .irq_o (s_apb4_system_irq) + .clk_i (clk_pclk_i), + .rst_n_i (rst_pclk_n_i), + .clk_aud_i (clk_aud_i), + .rst_aud_n_i (rst_aud_n_i), + .debug_halted_i (s_mgmt_debug_halted), + .ext_h_data_idle_i (s_ext_h_data_idle), + .resource_idle_i (s_resource_idle_combined), + .resource_block_ack_i (s_resource_block_ack_combined), + .resource_irq_i (s_resource_irq_raw), + .cache_request_i (s_hp_cache_req_pclk), + .ext_h_axi4 (u_ext_h_wide_axi4_if), + .fabric_monitor (u_fabric_monitor_pclk_if), + .axi4 (u_system_pclk_axi4_if), + .pwm (u_pwm_if), + .ps2 (u_ps2_if), + .ip_sel_i (u_sysctrl_if.ip_sel_o), + .user_gpio (u_user_gpio_if), + .rtc_wake_o (s_rtc_wake), + .wdg_reset_req_o (wdg_reset_req_o), + .ext_h_block_o (s_ext_h_block), + .ext_h_read_base_o (s_ext_h_read_base), + .ext_h_read_limit_o (s_ext_h_read_limit), + .ext_h_write_base_o (s_ext_h_write_base), + .ext_h_write_limit_o (s_ext_h_write_limit), + .ext_h_timeout_o (s_ext_h_timeout), + .ext_h_irq_raw_o (s_ext_h_irq_raw), + .ext_h_owner_o (s_ext_h_owner), + .cache_clean_o (s_hp_cache_clean_pclk), + .resource_owner_o (s_resource_owner), + .resource_owner_lock_o(s_resource_owner_lock), + .resource_quiesce_o (s_resource_quiesce), + .resource_reset_o (s_resource_reset), + .resource_irq_lp_o (s_resource_irq_lp), + .resource_irq_hp_o (s_resource_irq_hp), + .irq_o (s_apb4_system_irq) ); assign s_resource_idle_combined = { + s_apu_idle, 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_apu_idle && (s_resource_quiesce[7] || s_resource_reset[7]), s_resource_block_ack_pclk + }; - logic [35:0] s_unused_domain_inputs; + logic [36:0] s_unused_domain_inputs; assign s_unused_domain_inputs = { clk_pclk_i, rst_pclk_n_i, @@ -910,6 +923,7 @@ core_wrapper u_core_wrapper ( s_hp_lifecycle_draining, s_hp_lifecycle_fault, ^s_resource_owner, + ^s_resource_owner_lock, ^s_resource_quiesce, ^s_resource_reset, ^unused_cdc_clear_busy, diff --git a/rtl/rtl_style_audit.json b/rtl/rtl_style_audit.json index 49419caf..175b6c4c 100644 --- a/rtl/rtl_style_audit.json +++ b/rtl/rtl_style_audit.json @@ -133,7 +133,10 @@ "sdram_reg.sv" ], "rtl/ip/multimedia": [ + "apb4_apu.sv", "apb4_jpeg.sv", + "apu_define.svh", + "apu_reg.sv", "axi4s_dvp.sv", "dvp_core.sv", "dvp_define.svh", diff --git a/scripts/run_c_tests.py b/scripts/run_c_tests.py index 273119ec..f9024537 100644 --- a/scripts/run_c_tests.py +++ b/scripts/run_c_tests.py @@ -33,6 +33,7 @@ TEST_SOURCES = ( "crt/src/hal/i2s_math.c", "crt/src/hal/dma_math.c", "crt/src/hal/jpeg_math.c", + "crt/src/hal/apu.c", "crt/src/hal/sysctrl.c", "crt/src/hal/clock.c", "crt/src/hal/resource.c", diff --git a/tests/README.md b/tests/README.md index 9a6c3897..87b2ee6b 100644 --- a/tests/README.md +++ b/tests/README.md @@ -16,6 +16,9 @@ tooling. APB4 simulations; `test_crypto_register_parity.py` checks the handwritten RTL/C register ABI and `test_dma.py` covers DMA bursts, TCD fetch, CRC, tail-byte writes, and crypto endpoints. +- `test_apu.py` checks the complete fail-closed APU-P1 APB4 register matrix and + integrated LP/HP IRQ ownership topology; `test_apu_register_parity.py` keeps + its handwritten RTL/C ABI and matrix coverage synchronized. - `test_user_ip_register_parity.py` keeps the integrated slot 1 timer and slot 2 GPIO register offsets synchronized with their application-owned C definitions and checks their extension-manifest slot assignments. diff --git a/tests/c/test_runtime.c b/tests/c/test_runtime.c index d0c9d226..2b72c42f 100644 --- a/tests/c/test_runtime.c +++ b/tests/c/test_runtime.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -864,6 +865,10 @@ static int test_resource_validation(void) { return 0; } +static int test_apu_validation(void) { + return (rs_apu_probe(NULL) == RS_EINVAL) ? 0 : 1; +} + static int test_jpeg_validation(void) { static _Alignas(RS_JPEG_DESCRIPTOR_ALIGNMENT) rs_jpeg_descriptor_t ring[2]; rs_jpeg_descriptor_t descriptor; @@ -1016,6 +1021,7 @@ int main(void) { test_user_ip_validation(), test_extension_validation(), test_resource_validation(), + test_apu_validation(), 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 b5cb88ee..630f0ede 100644 --- a/tests/rtl/apb4_topology_tb.sv +++ b/tests/rtl/apb4_topology_tb.sv @@ -16,7 +16,7 @@ module apb4_topology_tb; begin s_decode_addr = address; #1; - if (s_psel_comb !== (20'd1 << slot)) begin + if (s_psel_comb !== (28'd1 << slot)) begin $fatal(1, "unexpected APB4 target slot for %h: got %b", address, s_psel_comb); end end @@ -44,7 +44,7 @@ module apb4_topology_tb; expect_select(`SOC_ADDR_APB4_SDIO1_BASE, 18); expect_select(`SOC_ADDR_APB4_CRYPTO_BASE, 19); expect_disabled(`SOC_ADDR_APB4_GA_BASE); - expect_disabled(`SOC_ADDR_APB4_APU_BASE); + expect_select(`SOC_ADDR_APB4_APU_BASE, 27); $display("SoC topology APB4 routing test passed"); $finish; end diff --git a/tests/rtl/apu_irq_topology_tb.sv b/tests/rtl/apu_irq_topology_tb.sv new file mode 100644 index 00000000..451065d2 --- /dev/null +++ b/tests/rtl/apu_irq_topology_tb.sv @@ -0,0 +1,271 @@ +`timescale 1ns / 1ps + +`include "apu_define.svh" +`include "soc_irq_config.svh" + +module apu_irq_topology_tb; + localparam logic [31:0] ApuBase = 32'h1001_3000; + localparam logic [11:0] Resource7Owner = 12'h1e0; + localparam logic [11:0] Resource7Control = 12'h1e4; + localparam logic [11:0] Resource7Fault = 12'h1ec; + + logic clk_i = 1'b0; + logic rst_n_i = 1'b0; + logic s_apu_idle; + logic s_apu_irq_raw; + logic [ 7:0] s_resource_idle; + logic [ 7:0] s_resource_block_ack; + logic [ 7:0] s_resource_irq_raw; + logic [ 7:0][1:0] s_resource_owner; + logic [ 7:0] s_resource_owner_lock; + logic [ 7:0] s_resource_quiesce; + logic [ 7:0] s_resource_reset; + logic [ 7:0] s_resource_irq_lp; + logic [ 7:0] s_resource_irq_hp; + logic [ 6:0] s_resource_irq_lp_compact; + logic [ 6:0] s_resource_irq_hp_compact; + logic s_resource_fault_irq; + logic s_cache_clean; + logic [`SOC_IRQ_APB4_PERIPH_WIDTH-1:0] s_apb4_periph_irq; + logic [`SOC_IRQ_APB4_SYSTEM_WIDTH-1:0] s_apb4_system_irq; + logic [ `SOC_IRQ_VECTOR_WIDTH-1:0] s_irq; + logic [ 31:0] s_hp_plic_source; + logic [ 1:0] s_hp_plic_context_irq; + logic [ 31:0] s_value; + + apb4_if u_apu_apb4_if ( + .pclk (clk_i), + .presetn(rst_n_i) + ); + apb4_if u_resource_apb4_if ( + .pclk (clk_i), + .presetn(rst_n_i) + ); + apb4_if u_plic_apb4_if ( + .pclk (clk_i), + .presetn(rst_n_i) + ); + + always #5 clk_i = ~clk_i; + + assign s_resource_idle = {s_apu_idle, 7'h7f}; + assign s_resource_block_ack = { + s_apu_idle && (s_resource_quiesce[7] || s_resource_reset[7]), 7'd0 + }; + assign s_resource_irq_raw = {s_apu_irq_raw, 7'd0}; + assign s_resource_irq_lp_compact = {s_resource_irq_lp[7:6], s_resource_irq_lp[4:0]}; + assign s_resource_irq_hp_compact = {s_resource_irq_hp[7:6], s_resource_irq_hp[4:0]}; + assign s_apb4_periph_irq = {s_resource_irq_lp_compact[6], 23'd0}; + assign s_apb4_system_irq = '0; + assign s_hp_plic_source = {21'd0, s_resource_irq_hp_compact[6], 10'd0}; + + `include "soc_irq_wiring.svh" + +apb4_apu u_apu ( + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .owner_i (s_resource_owner[7]), + .owner_lock_i (s_resource_owner_lock[7]), + .quiesce_i (s_resource_quiesce[7]), + .resource_reset_i(s_resource_reset[7]), + .apb4 (u_apu_apb4_if), + .idle_o (s_apu_idle), + .irq_o (s_apu_irq_raw) + ); + + resource_controller #( + .ResourceCount(8) + ) u_resource_controller ( + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .idle_i (s_resource_idle), + .block_ack_i (s_resource_block_ack), + .irq_i (s_resource_irq_raw), + .cache_request_i(1'b0), + .cache_clean_o (s_cache_clean), + .owner_o (s_resource_owner), + .owner_lock_o (s_resource_owner_lock), + .quiesce_o (s_resource_quiesce), + .reset_o (s_resource_reset), + .irq_lp_o (s_resource_irq_lp), + .irq_hp_o (s_resource_irq_hp), + .fault_irq_o (s_resource_fault_irq), + .apb4 (u_resource_apb4_if) + ); + + apb4_plic u_hp_plic ( + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .source_i (s_hp_plic_source), + .apb4 (u_plic_apb4_if), + .context_irq_o(s_hp_plic_context_irq) + ); + + task automatic drive_apb_idle; + begin + u_apu_apb4_if.paddr = 32'd0; + u_apu_apb4_if.pprot = 3'd0; + u_apu_apb4_if.psel = 1'b0; + u_apu_apb4_if.penable = 1'b0; + u_apu_apb4_if.pwrite = 1'b0; + u_apu_apb4_if.pwdata = 32'd0; + u_apu_apb4_if.pstrb = 4'd0; + u_resource_apb4_if.paddr = 32'd0; + u_resource_apb4_if.pprot = 3'd0; + u_resource_apb4_if.psel = 1'b0; + u_resource_apb4_if.penable = 1'b0; + u_resource_apb4_if.pwrite = 1'b0; + u_resource_apb4_if.pwdata = 32'd0; + u_resource_apb4_if.pstrb = 4'd0; + u_plic_apb4_if.paddr = 32'd0; + u_plic_apb4_if.pprot = 3'd0; + u_plic_apb4_if.psel = 1'b0; + u_plic_apb4_if.penable = 1'b0; + u_plic_apb4_if.pwrite = 1'b0; + u_plic_apb4_if.pwdata = 32'd0; + u_plic_apb4_if.pstrb = 4'd0; + end + endtask + + task automatic apu_write(input logic [11:0] offset_i, input logic [31:0] data_i); + begin + @(negedge clk_i); + u_apu_apb4_if.paddr = ApuBase + offset_i; + u_apu_apb4_if.pwdata = data_i; + u_apu_apb4_if.pstrb = 4'hf; + u_apu_apb4_if.pwrite = 1'b1; + u_apu_apb4_if.psel = 1'b1; + u_apu_apb4_if.penable = 1'b0; + @(negedge clk_i); + u_apu_apb4_if.penable = 1'b1; + while (!u_apu_apb4_if.pready) @(negedge clk_i); + if (u_apu_apb4_if.pslverr) $fatal(1, "APU topology write failed at %h", offset_i); + @(negedge clk_i); + u_apu_apb4_if.psel = 1'b0; + u_apu_apb4_if.penable = 1'b0; + u_apu_apb4_if.pwrite = 1'b0; + u_apu_apb4_if.pstrb = 4'd0; + end + endtask + + task automatic resource_write(input logic [11:0] offset_i, input logic [31:0] data_i, + input logic expected_err_i); + begin + @(negedge clk_i); + u_resource_apb4_if.paddr = {20'd0, offset_i}; + u_resource_apb4_if.pwdata = data_i; + u_resource_apb4_if.pstrb = 4'hf; + u_resource_apb4_if.pwrite = 1'b1; + u_resource_apb4_if.psel = 1'b1; + u_resource_apb4_if.penable = 1'b0; + @(negedge clk_i); + u_resource_apb4_if.penable = 1'b1; + while (!u_resource_apb4_if.pready) @(negedge clk_i); + if (u_resource_apb4_if.pslverr != expected_err_i) begin + $fatal(1, "Resource topology write response mismatch at %h", offset_i); + end + @(negedge clk_i); + u_resource_apb4_if.psel = 1'b0; + u_resource_apb4_if.penable = 1'b0; + u_resource_apb4_if.pwrite = 1'b0; + u_resource_apb4_if.pstrb = 4'd0; + end + endtask + + task automatic plic_write(input logic [25:0] offset_i, input logic [31:0] data_i); + begin + @(negedge clk_i); + u_plic_apb4_if.paddr = {6'd0, offset_i}; + u_plic_apb4_if.pwdata = data_i; + u_plic_apb4_if.pstrb = 4'hf; + u_plic_apb4_if.pwrite = 1'b1; + u_plic_apb4_if.psel = 1'b1; + u_plic_apb4_if.penable = 1'b0; + @(negedge clk_i); + u_plic_apb4_if.penable = 1'b1; + while (!u_plic_apb4_if.pready) @(negedge clk_i); + if (u_plic_apb4_if.pslverr) $fatal(1, "PLIC topology write failed at %h", offset_i); + @(negedge clk_i); + u_plic_apb4_if.psel = 1'b0; + u_plic_apb4_if.penable = 1'b0; + u_plic_apb4_if.pwrite = 1'b0; + u_plic_apb4_if.pstrb = 4'd0; + end + endtask + + task automatic plic_read(input logic [25:0] offset_i, output logic [31:0] data_o); + begin + @(negedge clk_i); + u_plic_apb4_if.paddr = {6'd0, offset_i}; + u_plic_apb4_if.pstrb = 4'd0; + u_plic_apb4_if.pwrite = 1'b0; + u_plic_apb4_if.psel = 1'b1; + u_plic_apb4_if.penable = 1'b0; + @(negedge clk_i); + u_plic_apb4_if.penable = 1'b1; + while (!u_plic_apb4_if.pready) @(negedge clk_i); + if (u_plic_apb4_if.pslverr) $fatal(1, "PLIC topology read failed at %h", offset_i); + data_o = u_plic_apb4_if.prdata; + @(negedge clk_i); + u_plic_apb4_if.psel = 1'b0; + u_plic_apb4_if.penable = 1'b0; + end + endtask + + initial begin + drive_apb_idle(); + repeat (4) @(posedge clk_i); + rst_n_i = 1'b1; + + plic_write(26'h000028, 32'd3); + plic_write(26'h002000, 32'h0000_0400); + plic_write(26'h200000, 32'd0); + apu_write(`APB4_APU__IRQ_ENABLE, 32'h0000_0001); + apu_write(`APB4_APU__IRQ_TEST, 32'h0000_0001); + repeat (2) @(posedge clk_i); + #1; + if ((s_resource_owner[7] != 2'd0) || !s_resource_irq_lp[7] || s_resource_irq_hp[7] || + !s_apb4_periph_irq[23] || !s_irq[31] || s_hp_plic_source[10] || + s_hp_plic_context_irq[0]) begin + $fatal(1, "APU event was not delivered exclusively to LP IRQ31"); + end + + resource_write(Resource7Control, 32'h0000_0001, 1'b0); + resource_write(Resource7Owner, 32'h0000_0101, 1'b0); + repeat (2) @(posedge clk_i); + #1; + if ((s_resource_owner[7] != 2'd1) || !s_resource_owner_lock[7] || + s_resource_irq_lp[7] || !s_resource_irq_hp[7] || s_apb4_periph_irq[23] || + s_irq[31] || !s_hp_plic_source[10] || !s_hp_plic_context_irq[0]) begin + $fatal(1, "APU event was not delivered exclusively to HP PLIC source 10"); + end + plic_read(26'h200004, s_value); + if (s_value != 32'd10) $fatal(1, "HP PLIC did not claim APU source 10"); + + resource_write(Resource7Owner, 32'd0, 1'b1); + if ((s_resource_owner[7] != 2'd1) || !s_resource_owner_lock[7] || !s_resource_fault_irq) begin + $fatal(1, "APU owner lock did not preserve HP ownership"); + end + resource_write(Resource7Fault, 32'h0000_0001, 1'b0); + if (s_resource_fault_irq) $fatal(1, "APU resource fault did not clear"); + + resource_write(Resource7Control, 32'h0000_0003, 1'b0); + repeat (2) @(posedge clk_i); + #1; + if (s_resource_irq_lp[7] || s_resource_irq_hp[7] || s_apb4_periph_irq[23] || s_irq[31] || + s_hp_plic_source[10]) begin + $fatal(1, "APU resource reset did not mask both interrupt routes"); + end + + if (s_resource_fault_irq || s_cache_clean) begin + $fatal(1, "APU topology test left an unrelated controller output asserted"); + end + $display("APU-P1 integrated IRQ ownership topology passed"); + $finish; + end + + initial begin + repeat (1000) @(posedge clk_i); + $fatal(1, "APU-P1 IRQ ownership topology timed out"); + end +endmodule diff --git a/tests/rtl/apu_reg_tb.sv b/tests/rtl/apu_reg_tb.sv new file mode 100644 index 00000000..e0f8a759 --- /dev/null +++ b/tests/rtl/apu_reg_tb.sv @@ -0,0 +1,542 @@ +`timescale 1ns / 1ps + +`include "apu_define.svh" + +module apu_reg_tb; + localparam logic [31:0] ApuBase = 32'h1001_3000; + localparam int unsigned RegisterCount = 95; + localparam logic [1:0] AccessRo = 2'd0; + localparam logic [1:0] AccessWo = 2'd1; + localparam logic [1:0] AccessRw = 2'd2; + + logic clk_i = 1'b0; + logic rst_n_i = 1'b0; + logic [ 1:0] owner_i = 2'd0; + logic owner_lock_i = 1'b0; + logic quiesce_i = 1'b0; + logic resource_reset_i = 1'b0; + logic idle_o; + logic irq_o; + logic [31:0] s_value; + logic [11:0] s_reg_offset [RegisterCount]; + logic [ 1:0] s_reg_access [RegisterCount]; + logic [31:0] s_reg_reset [RegisterCount]; + logic s_reg_full_strobe [RegisterCount]; + int unsigned s_reg_count; + + apb4_if apb4 ( + .pclk (clk_i), + .presetn(rst_n_i) + ); + + always #5 clk_i = ~clk_i; + + apb4_apu u_dut ( + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .owner_i (owner_i), + .owner_lock_i (owner_lock_i), + .quiesce_i (quiesce_i), + .resource_reset_i(resource_reset_i), + .apb4 (apb4), + .idle_o (idle_o), + .irq_o (irq_o) + ); + + function automatic logic [31:0] legal_write_value(input logic [11:0] offset_i); + case (offset_i) + `APB4_APU__IRQ_STATE: legal_write_value = 32'h0000_07ff; + `APB4_APU__IRQ_ENABLE: legal_write_value = 32'h0000_0005; + `APB4_APU__ERROR_STATUS: legal_write_value = 32'h0000_0001; + `APB4_APU__SEQUENCER_TIMEOUT: legal_write_value = 32'h1234_5678; + `APB4_APU__STREAM_ROUTE: legal_write_value = 32'd0; + `APB4_APU__READ_BASE: legal_write_value = 32'h3000_0000; + `APB4_APU__READ_LIMIT: legal_write_value = 32'h3000_ffff; + `APB4_APU__WRITE_BASE: legal_write_value = 32'h3001_0000; + `APB4_APU__WRITE_LIMIT: legal_write_value = 32'h3001_ffff; + `APB4_APU__DMA_TIMEOUT: legal_write_value = 32'h8765_4321; + `APB4_APU__MC_IMAGE_ADDRESS: legal_write_value = 32'h3002_0000; + `APB4_APU__MC_IMAGE_SIZE: legal_write_value = 32'h0000_0040; + `APB4_APU__MC_EXPECTED_CRC: legal_write_value = 32'ha5a5_5a5a; + `APB4_APU__JOB_CONTROL: legal_write_value = 32'h0000_0121; + `APB4_APU__JOB_INPUT_ADDRESS: legal_write_value = 32'h3003_0000; + `APB4_APU__JOB_INPUT_LENGTH: legal_write_value = 32'h0000_0100; + `APB4_APU__JOB_OUTPUT_ADDRESS: legal_write_value = 32'h3003_1000; + `APB4_APU__JOB_OUTPUT_CAPACITY: legal_write_value = 32'h0000_1000; + `APB4_APU__JOB_INPUT_CONFIG: legal_write_value = 32'h0012_3456; + `APB4_APU__JOB_OUTPUT_CONFIG: legal_write_value = 32'h0001_2345; + `APB4_APU__JOB_FLAGS: legal_write_value = 32'h0000_0001; + `APB4_APU__RING_BASE: legal_write_value = 32'h3004_0000; + `APB4_APU__RING_SIZE: legal_write_value = 32'h0000_0008; + `APB4_APU__RING_TAIL: legal_write_value = 32'h0000_0003; + `APB4_APU__RING_CONTROL: legal_write_value = 32'h0000_0001; + `APB4_APU__RING_COALESCE: legal_write_value = 32'h0004_0002; + `APB4_APU__KWS_MODEL_ADDRESS: legal_write_value = 32'h3005_0000; + `APB4_APU__KWS_MODEL_SIZE: legal_write_value = 32'h0000_0100; + `APB4_APU__KWS_MODEL_EXPECTED_CRC: legal_write_value = 32'h5a5a_a5a5; + `APB4_APU__KWS_CONTROL: legal_write_value = 32'h0000_0004; + `APB4_APU__KWS_CONFIG: legal_write_value = 32'h0000_0590; + `APB4_APU__PERF_CONTROL: legal_write_value = 32'h0000_0001; + default: legal_write_value = 32'd0; + endcase + endfunction + + function automatic logic [31:0] legal_readback_value(input logic [11:0] offset_i); + case (offset_i) + `APB4_APU__IRQ_STATE, `APB4_APU__ERROR_STATUS, `APB4_APU__STREAM_ROUTE, + `APB4_APU__KWS_CONTROL: + legal_readback_value = 32'd0; + default: legal_readback_value = legal_write_value(offset_i); + endcase + endfunction + + function automatic logic [31:0] reset_or_preserved_value(input logic [11:0] offset_i, + input logic [31:0] reset_i); + case (offset_i) + `APB4_APU__READ_BASE, `APB4_APU__READ_LIMIT, `APB4_APU__WRITE_BASE, `APB4_APU__WRITE_LIMIT: + reset_or_preserved_value = legal_write_value(offset_i); + default: reset_or_preserved_value = reset_i; + endcase + endfunction + + task automatic add_register(input logic [11:0] offset_i, input logic [1:0] access_i, + input logic [31:0] reset_i, input logic full_strobe_i); + begin + if (s_reg_count >= RegisterCount) $fatal(1, "APU register table overflow"); + s_reg_offset[s_reg_count] = offset_i; + s_reg_access[s_reg_count] = access_i; + s_reg_reset[s_reg_count] = reset_i; + s_reg_full_strobe[s_reg_count] = full_strobe_i; + s_reg_count++; + end + endtask + + task automatic initialize_register_table; + begin + s_reg_count = 0; + add_register(`APB4_APU__IP_ID, AccessRo, 32'h4150_5530, 1'b0); + add_register(`APB4_APU__IP_VERSION, AccessRo, 32'h0001_0000, 1'b0); + add_register(`APB4_APU__CAPABILITY0, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__CAPABILITY1, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__COMMAND, AccessWo, 32'd0, 1'b1); + add_register(`APB4_APU__STATUS, AccessRo, 32'h0000_0100, 1'b0); + add_register(`APB4_APU__IRQ_STATE, AccessRw, 32'd0, 1'b0); + add_register(`APB4_APU__IRQ_ENABLE, AccessRw, 32'd0, 1'b0); + add_register(`APB4_APU__IRQ_TEST, AccessWo, 32'd0, 1'b1); + add_register(`APB4_APU__ERROR_STATUS, AccessRw, 32'd0, 1'b0); + add_register(`APB4_APU__ERROR_ADDRESS, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__ERROR_DETAIL, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__SEQUENCER_TIMEOUT, AccessRw, 32'h0000_ffff, 1'b0); + add_register(`APB4_APU__STREAM_ROUTE, AccessRw, 32'd0, 1'b0); + add_register(`APB4_APU__STREAM_STATUS, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__OWNER_STATUS, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__READ_BASE, AccessRw, 32'hffff_ffff, 1'b1); + add_register(`APB4_APU__READ_LIMIT, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__WRITE_BASE, AccessRw, 32'hffff_ffff, 1'b1); + add_register(`APB4_APU__WRITE_LIMIT, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__DMA_TIMEOUT, AccessRw, 32'h0000_ffff, 1'b0); + add_register(`APB4_APU__ABI_DIGEST, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__SEQUENCER_STATUS, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__SEQUENCER_RETIRED, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__MC_IMAGE_ADDRESS, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__MC_IMAGE_SIZE, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__MC_EXPECTED_CRC, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__MC_STATUS, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__MC_ABI, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__MC_BUILD_ID_LO, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__MC_BUILD_ID_HI, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__MC_LOCK, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__MC_ACTUAL_CRC, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__MC_LOAD_COUNT, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__JOB_CONTROL, AccessRw, 32'd0, 1'b0); + add_register(`APB4_APU__JOB_INPUT_ADDRESS, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__JOB_INPUT_LENGTH, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__JOB_OUTPUT_ADDRESS, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__JOB_OUTPUT_CAPACITY, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__JOB_INPUT_CONFIG, AccessRw, 32'd0, 1'b0); + add_register(`APB4_APU__JOB_OUTPUT_CONFIG, AccessRw, 32'd0, 1'b0); + add_register(`APB4_APU__JOB_FLAGS, AccessRw, 32'd0, 1'b0); + add_register(`APB4_APU__JOB_STATUS, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__JOB_INPUT_USED, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__JOB_OUTPUT_BYTES, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__JOB_FRAMES, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__JOB_SOURCE_INFO, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__JOB_CYCLES, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__JOB_DETAIL, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__RING_BASE, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__RING_SIZE, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__RING_HEAD, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__RING_TAIL, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__RING_CONTROL, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__RING_STATUS, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__RING_COMPLETED, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__RING_COALESCE, AccessRw, 32'h0001_0001, 1'b1); + add_register(`APB4_APU__RING_DOORBELL, AccessWo, 32'd0, 1'b1); + add_register(`APB4_APU__KWS_MODEL_ADDRESS, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__KWS_MODEL_SIZE, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__KWS_MODEL_EXPECTED_CRC, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__KWS_CONTROL, AccessRw, 32'd0, 1'b1); + add_register(`APB4_APU__KWS_CONFIG, AccessRw, 32'h0000_0380, 1'b0); + add_register(`APB4_APU__KWS_STATUS, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__KWS_RESULT, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__KWS_TIMESTAMP_LO, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__KWS_TIMESTAMP_HI, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__KWS_FRAME_COUNT, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__KWS_INFERENCE_COUNT, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__KWS_HIT_COUNT, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__KWS_OVERRUN_COUNT, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__KWS_MODEL_STATUS, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__KWS_MODEL_ACTUAL_CRC, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_CONTROL, AccessRw, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_STATUS, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_ACTIVE_CYCLES_LO, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_ACTIVE_CYCLES_HI, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_INPUT_BYTES_LO, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_INPUT_BYTES_HI, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_OUTPUT_BYTES_LO, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_OUTPUT_BYTES_HI, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_DECODED_FRAMES_LO, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_DECODED_FRAMES_HI, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_DMA_READ_STALLS_LO, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_DMA_READ_STALLS_HI, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_DMA_WRITE_STALLS_LO, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_DMA_WRITE_STALLS_HI, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_STREAM_STALLS_LO, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_STREAM_STALLS_HI, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_SEQUENCER_INSTR_LO, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_SEQUENCER_INSTR_HI, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_KWS_CYCLES_LO, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_KWS_CYCLES_HI, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_FAULTS_LO, AccessRo, 32'd0, 1'b0); + add_register(`APB4_APU__PERF_FAULTS_HI, AccessRo, 32'd0, 1'b0); + if (s_reg_count != RegisterCount) begin + $fatal(1, "APU register table has %0d entries, expected %0d", s_reg_count, RegisterCount); + end + end + endtask + + task automatic drive_apb_idle; + begin + apb4.paddr = 32'd0; + apb4.pprot = 3'd0; + apb4.psel = 1'b0; + apb4.penable = 1'b0; + apb4.pwrite = 1'b0; + apb4.pwdata = 32'd0; + apb4.pstrb = 4'd0; + end + endtask + + task automatic hard_reset; + begin + @(negedge clk_i); + drive_apb_idle(); + owner_i = 2'd0; + owner_lock_i = 1'b0; + quiesce_i = 1'b0; + resource_reset_i = 1'b0; + rst_n_i = 1'b0; + repeat (2) @(posedge clk_i); + @(negedge clk_i); + rst_n_i = 1'b1; + end + endtask + + task automatic apb_write(input logic [11:0] offset_i, input logic [31:0] data_i, + input logic [3:0] strobe_i, input logic expected_err_i); + int unsigned s_wait_cycles; + logic s_response_err; + begin + @(negedge clk_i); + apb4.paddr = ApuBase + offset_i; + apb4.pwdata = data_i; + apb4.pstrb = strobe_i; + apb4.pwrite = 1'b1; + apb4.psel = 1'b1; + apb4.penable = 1'b0; + #1; + if (apb4.pready) $fatal(1, "APU PREADY asserted during write setup at %h", offset_i); + @(negedge clk_i); + apb4.penable = 1'b1; + s_wait_cycles = 0; + #1; + while (!apb4.pready) begin + s_wait_cycles++; + if (s_wait_cycles > 4) $fatal(1, "APU write wait timeout at %h", offset_i); + @(negedge clk_i); + #1; + end + if (s_wait_cycles != 1) begin + $fatal(1, "APU write wait count mismatch at %h: %0d", offset_i, s_wait_cycles); + end + if (apb4.pslverr != expected_err_i) $fatal(1, "APU write response mismatch at %h", offset_i); + s_response_err = apb4.pslverr; + #2; + if (!apb4.pready || (apb4.pslverr != s_response_err)) begin + $fatal(1, "APU write response was not retained through completion at %h", offset_i); + end + @(negedge clk_i); + drive_apb_idle(); + end + endtask + + task automatic apb_read(input logic [11:0] offset_i, output logic [31:0] data_o, + input logic expected_err_i); + int unsigned s_wait_cycles; + logic [31:0] s_response_data; + logic s_response_err; + begin + @(negedge clk_i); + apb4.paddr = ApuBase + offset_i; + apb4.pstrb = 4'd0; + apb4.pwrite = 1'b0; + apb4.psel = 1'b1; + apb4.penable = 1'b0; + #1; + if (apb4.pready) $fatal(1, "APU PREADY asserted during read setup at %h", offset_i); + @(negedge clk_i); + apb4.penable = 1'b1; + s_wait_cycles = 0; + #1; + while (!apb4.pready) begin + s_wait_cycles++; + if (s_wait_cycles > 4) $fatal(1, "APU read wait timeout at %h", offset_i); + @(negedge clk_i); + #1; + end + if (s_wait_cycles != 1) begin + $fatal(1, "APU read wait count mismatch at %h: %0d", offset_i, s_wait_cycles); + end + if (apb4.pslverr != expected_err_i) $fatal(1, "APU read response mismatch at %h", offset_i); + s_response_data = apb4.prdata; + s_response_err = apb4.pslverr; + #2; + if (!apb4.pready || (apb4.prdata != s_response_data) || + (apb4.pslverr != s_response_err)) begin + $fatal(1, "APU read response was not retained through completion at %h", offset_i); + end + data_o = s_response_data; + @(negedge clk_i); + drive_apb_idle(); + end + endtask + + task automatic expect_read(input logic [11:0] offset_i, input logic [31:0] expected_i); + begin + apb_read(offset_i, s_value, 1'b0); + if (s_value != expected_i) begin + $fatal(1, "APU read mismatch at %h: got %h expected %h", offset_i, s_value, expected_i); + end + end + endtask + + task automatic program_rw_registers; + begin + for (int unsigned register_index = 0; register_index < RegisterCount; register_index++) begin + if (s_reg_access[register_index] == AccessRw) begin + apb_write(s_reg_offset[register_index], legal_write_value(s_reg_offset[register_index]), + 4'hf, 1'b0); + end + end + end + endtask + + task automatic check_reset_state(input logic preserve_acl_i); + logic [31:0] s_expected; + begin + for (int unsigned register_index = 0; register_index < RegisterCount; register_index++) begin + if (s_reg_access[register_index] != AccessWo) begin + s_expected = preserve_acl_i ? + reset_or_preserved_value(s_reg_offset[register_index], s_reg_reset[register_index]) : + s_reg_reset[register_index]; + expect_read(s_reg_offset[register_index], s_expected); + end + end + end + endtask + + task automatic check_rejected_write(input logic [11:0] offset_i, + input logic [31:0] rejected_value_i, + input logic [5:0] error_code_i); + logic [31:0] s_before; + begin + hard_reset(); + apb_write(`APB4_APU__SEQUENCER_TIMEOUT, 32'h1234_5678, 4'hf, 1'b0); + if ((offset_i != `APB4_APU__COMMAND) && (offset_i != `APB4_APU__IRQ_TEST) && + (offset_i != `APB4_APU__RING_DOORBELL)) begin + apb_read(offset_i, s_before, 1'b0); + end else begin + s_before = 32'd0; + end + apb_write(offset_i, rejected_value_i, 4'hf, 1'b1); + expect_read(`APB4_APU__SEQUENCER_TIMEOUT, 32'h1234_5678); + expect_read(`APB4_APU__ERROR_STATUS, 32'd1 | (32'(error_code_i) << 1)); + expect_read(`APB4_APU__ERROR_ADDRESS, ApuBase + offset_i); + if ((offset_i != `APB4_APU__COMMAND) && (offset_i != `APB4_APU__IRQ_TEST) && + (offset_i != `APB4_APU__RING_DOORBELL) && (offset_i != `APB4_APU__IRQ_STATE) && + (offset_i != `APB4_APU__ERROR_STATUS) && (offset_i != `APB4_APU__ERROR_ADDRESS) && + (offset_i != `APB4_APU__ERROR_DETAIL)) begin + expect_read(offset_i, s_before); + end + end + endtask + + initial begin + initialize_register_table(); + drive_apb_idle(); + hard_reset(); + if (!idle_o || irq_o) $fatal(1, "APU reset lifecycle outputs mismatch"); + + for (int unsigned register_index = 0; register_index < RegisterCount; register_index++) begin + hard_reset(); + if (s_reg_access[register_index] == AccessWo) begin + apb_read(s_reg_offset[register_index], s_value, 1'b1); + end else begin + expect_read(s_reg_offset[register_index], s_reg_reset[register_index]); + end + end + + for (int unsigned register_index = 0; register_index < RegisterCount; register_index++) begin + hard_reset(); + case (s_reg_access[register_index]) + AccessRo: begin + apb_write(`APB4_APU__SEQUENCER_TIMEOUT, 32'h1234_5678, 4'hf, 1'b0); + apb_write(s_reg_offset[register_index], 32'd0, 4'hf, 1'b1); + expect_read(`APB4_APU__SEQUENCER_TIMEOUT, 32'h1234_5678); + if ((s_reg_offset[register_index] != `APB4_APU__ERROR_ADDRESS) && + (s_reg_offset[register_index] != `APB4_APU__ERROR_DETAIL)) begin + expect_read(s_reg_offset[register_index], s_reg_reset[register_index]); + end + end + AccessWo: begin + apb_read(s_reg_offset[register_index], s_value, 1'b1); + hard_reset(); + if (s_reg_offset[register_index] == `APB4_APU__COMMAND) begin + apb_write(s_reg_offset[register_index], 32'h0000_0004, 4'hf, 1'b0); + end else if (s_reg_offset[register_index] == `APB4_APU__IRQ_TEST) begin + apb_write(s_reg_offset[register_index], 32'd0, 4'hf, 1'b0); + end else begin + apb_write(s_reg_offset[register_index], 32'h0000_0001, 4'hf, 1'b1); + end + end + AccessRw: begin + apb_write(s_reg_offset[register_index], legal_write_value(s_reg_offset[register_index]), + 4'hf, 1'b0); + expect_read(s_reg_offset[register_index], legal_readback_value( + s_reg_offset[register_index])); + end + default: $fatal(1, "APU register table has invalid access class"); + endcase + end + + hard_reset(); + apb_write(`APB4_APU__SEQUENCER_TIMEOUT, 32'h0000_00aa, 4'h1, 1'b0); + expect_read(`APB4_APU__SEQUENCER_TIMEOUT, 32'h0000_ffaa); + apb_write(`APB4_APU__SEQUENCER_TIMEOUT, 32'h0000_bb00, 4'h2, 1'b0); + expect_read(`APB4_APU__SEQUENCER_TIMEOUT, 32'h0000_bbaa); + apb_write(`APB4_APU__SEQUENCER_TIMEOUT, 32'h00cc_0000, 4'h4, 1'b0); + expect_read(`APB4_APU__SEQUENCER_TIMEOUT, 32'h00cc_bbaa); + apb_write(`APB4_APU__SEQUENCER_TIMEOUT, 32'hdd00_0000, 4'h8, 1'b0); + expect_read(`APB4_APU__SEQUENCER_TIMEOUT, 32'hddcc_bbaa); + + for (int unsigned register_index = 0; register_index < RegisterCount; register_index++) begin + if (s_reg_full_strobe[register_index]) begin + hard_reset(); + apb_write(`APB4_APU__SEQUENCER_TIMEOUT, 32'h1234_5678, 4'hf, 1'b0); + apb_write(s_reg_offset[register_index], legal_write_value(s_reg_offset[register_index]), + 4'h7, 1'b1); + expect_read(`APB4_APU__SEQUENCER_TIMEOUT, 32'h1234_5678); + if (s_reg_access[register_index] == AccessRw) begin + expect_read(s_reg_offset[register_index], s_reg_reset[register_index]); + end + end + end + + check_rejected_write(`APB4_APU__COMMAND, 32'h0000_0080, `APB4_APU__ERROR_CODE_INVALID_CONFIG); + for (int bit_index = 0; bit_index < 6; bit_index++) begin + if ((bit_index != `APB4_APU__COMMAND_SOFT_RESET) && + (bit_index != `APB4_APU__COMMAND_CLEAR_COUNTERS)) begin + check_rejected_write(`APB4_APU__COMMAND, 32'd1 << bit_index, + `APB4_APU__ERROR_CODE_UNSUPPORTED); + end + end + check_rejected_write(`APB4_APU__IRQ_STATE, 32'h0000_0800, `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__IRQ_ENABLE, 32'h0000_0800, + `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__IRQ_TEST, 32'h0000_0800, `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__ERROR_STATUS, 32'h0000_0002, + `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__SEQUENCER_TIMEOUT, 32'd0, `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__STREAM_ROUTE, 32'h0000_0001, `APB4_APU__ERROR_CODE_UNSUPPORTED); + check_rejected_write(`APB4_APU__STREAM_ROUTE, 32'h0000_0004, `APB4_APU__ERROR_CODE_UNSUPPORTED); + check_rejected_write(`APB4_APU__DMA_TIMEOUT, 32'd0, `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__MC_IMAGE_ADDRESS, 32'h3002_0001, + `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__MC_IMAGE_SIZE, 32'd0, `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__JOB_CONTROL, 32'h8000_0000, + `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__JOB_INPUT_CONFIG, 32'h8000_0000, + `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__JOB_OUTPUT_CONFIG, 32'h8000_0000, + `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__JOB_FLAGS, 32'h0000_0002, `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__RING_BASE, 32'h3004_0001, `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__RING_SIZE, 32'h0000_0003, `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__RING_TAIL, 32'h0000_0100, `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__RING_CONTROL, 32'h0000_0004, + `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__RING_COALESCE, 32'd0, `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__RING_DOORBELL, 32'h0000_0001, + `APB4_APU__ERROR_CODE_UNSUPPORTED); + check_rejected_write(`APB4_APU__KWS_MODEL_ADDRESS, 32'h3005_0001, + `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__KWS_MODEL_SIZE, 32'd0, `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__KWS_CONTROL, 32'h0000_0001, `APB4_APU__ERROR_CODE_UNSUPPORTED); + check_rejected_write(`APB4_APU__KWS_CONTROL, 32'h0000_0002, `APB4_APU__ERROR_CODE_UNSUPPORTED); + check_rejected_write(`APB4_APU__KWS_CONTROL, 32'h0000_0008, + `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__KWS_CONFIG, 32'h0000_0080, + `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__PERF_CONTROL, 32'h0000_0008, + `APB4_APU__ERROR_CODE_INVALID_CONFIG); + + hard_reset(); + apb_write(`APB4_APU__IRQ_TEST, 32'h0000_0001, 4'hf, 1'b0); + force u_dut.u_apu_reg.s_irq_set = 11'h001; + apb_write(`APB4_APU__IRQ_STATE, 32'h0000_0001, 4'h1, 1'b0); + release u_dut.u_apu_reg.s_irq_set; + expect_read(`APB4_APU__IRQ_STATE, 32'h0000_0001); + apb_write(`APB4_APU__IRQ_STATE, 32'h0000_0001, 4'h1, 1'b0); + expect_read(`APB4_APU__IRQ_STATE, 32'd0); + + hard_reset(); + program_rw_registers(); + apb_write(`APB4_APU__COMMAND, 32'h0000_0004, 4'hf, 1'b0); + check_reset_state(1'b1); + + hard_reset(); + program_rw_registers(); + @(negedge clk_i); + resource_reset_i = 1'b1; + @(posedge clk_i); + @(negedge clk_i); + resource_reset_i = 1'b0; + check_reset_state(1'b1); + + hard_reset(); + owner_i = 2'd1; + owner_lock_i = 1'b1; + quiesce_i = 1'b1; + expect_read(`APB4_APU__OWNER_STATUS, 32'h0000_0301); + apb_read(12'h060, s_value, 1'b1); + apb_read(12'h001, s_value, 1'b1); + + $display("APU-P1 complete APB register matrix passed"); + $finish; + end + + initial begin + repeat (20000) @(posedge clk_i); + $fatal(1, "APU-P1 APB register matrix timed out"); + end +endmodule diff --git a/tests/rtl/axi4_interconnect_tb.sv b/tests/rtl/axi4_interconnect_tb.sv index bfffc47e..4f9ae1fc 100644 --- a/tests/rtl/axi4_interconnect_tb.sv +++ b/tests/rtl/axi4_interconnect_tb.sv @@ -180,6 +180,41 @@ module axi4_interconnect_tb; end endtask + task automatic issue_hp_apu_write(input logic [31:0] address, input logic [1:0] expected_response, + input logic expected_access_fault); + begin + @(negedge clk_i); + masters[6].awaddr = address; + masters[6].awlen = 8'd0; + masters[6].awvalid = 1'b1; + do @(posedge clk_i); while (!masters[6].awready); + @(negedge clk_i); + masters[6].awvalid = 1'b0; + masters[6].wdata = 32'h1234_5678; + masters[6].wstrb = 4'hf; + masters[6].wlast = 1'b1; + masters[6].wvalid = 1'b1; + do @(posedge clk_i); while (!masters[6].wready); + @(negedge clk_i); + masters[6].wvalid = 1'b0; + masters[6].bready = 1'b1; + while (!masters[6].bvalid) @(negedge clk_i); + #1; + if ((masters[6].bresp != expected_response) || + (fault_valid_o != expected_access_fault) || + (expected_access_fault && + (!fault_access_o || (fault_addr_o != address) || (fault_master_o != 3'd6) || + (fault_code_o != `RIB_RESP_PROTERR)))) begin + $fatal( + 1, + "HP APU access policy mismatch at %h: resp=%0d fault=%0d access=%0d master=%0d code=%0d", + address, masters[6].bresp, fault_valid_o, fault_access_o, fault_master_o, fault_code_o); + end + @(negedge clk_i); + masters[6].bready = 1'b0; + end + endtask + task automatic issue_management_with_idle_sdio; integer cycles; begin @@ -347,7 +382,12 @@ module axi4_interconnect_tb; issue_management_with_idle_sdio(); issue_mgmt_error_read(32'hA000_0000, 8'd0, `AXI4_RESP_DECODE_ERROR, `RIB_RESP_DECERR); issue_mgmt_error_read(32'h3000_0000, 8'd16, `AXI4_RESP_SLAVE_ERROR, `RIB_RESP_BURSTERR); +`ifdef MINI_PRODUCT + issue_hp_apu_write(32'h1001_301C, `AXI4_RESP_OKAY, 1'b0); + issue_hp_apu_write(32'h1001_3040, `AXI4_RESP_SLAVE_ERROR, 1'b1); +`else issue_denied_user_write(32'h1000_B000); +`endif issue_contending_sdio_faults(); issue_hp_accesses(); diff --git a/tests/rtl/resource_controller_tb.sv b/tests/rtl/resource_controller_tb.sv index 888baab9..fca13668 100644 --- a/tests/rtl/resource_controller_tb.sv +++ b/tests/rtl/resource_controller_tb.sv @@ -3,16 +3,17 @@ module resource_controller_tb; logic clk_i = 1'b0; logic rst_n_i = 1'b0; - logic [ 5:0] idle_i = '0; - logic [ 5:0] block_ack_i = '0; - logic [ 5:0] irq_i = '0; + logic [ 7:0] idle_i = '0; + logic [ 7:0] block_ack_i = '0; + logic [ 7:0] irq_i = '0; logic cache_request_i = 1'b0; logic cache_clean_o; - logic [ 5:0][1:0] owner_o; - logic [ 5:0] quiesce_o; - logic [ 5:0] reset_o; - logic [ 5:0] irq_lp_o; - logic [ 5:0] irq_hp_o; + logic [ 7:0][1:0] owner_o; + logic [ 7:0] owner_lock_o; + logic [ 7:0] quiesce_o; + logic [ 7:0] reset_o; + logic [ 7:0] irq_lp_o; + logic [ 7:0] irq_hp_o; logic fault_irq_o; logic [31:0] read_data; @@ -32,6 +33,7 @@ module resource_controller_tb; .cache_request_i(cache_request_i), .cache_clean_o (cache_clean_o), .owner_o (owner_o), + .owner_lock_o (owner_lock_o), .quiesce_o (quiesce_o), .reset_o (reset_o), .irq_lp_o (irq_lp_o), @@ -90,7 +92,7 @@ module resource_controller_tb; repeat (3) @(posedge clk_i); rst_n_i = 1'b1; - idle_i = 6'h3F; + idle_i = 8'hFF; apb_read(12'h000, read_data); if (read_data != 32'h5253_4354) $fatal(1, "resource controller ID mismatch"); @@ -135,6 +137,16 @@ module resource_controller_tb; $fatal(1, "resource lifecycle controls did not update"); end + irq_i[7] = 1'b1; + #1; + if (!irq_lp_o[7] || irq_hp_o[7]) $fatal(1, "APU reset owner routing mismatch"); + block_ack_i[7] = 1'b1; + apb_write(12'h1E4, 32'h0000_0001, 1'b0); + apb_write(12'h1E0, 32'h0000_0101, 1'b0); + if ((owner_o[7] != 2'd1) || !owner_lock_o[7] || irq_lp_o[7] || !irq_hp_o[7]) begin + $fatal(1, "APU resource index 7 ownership or IRQ routing mismatch"); + end + $display("Resource Controller ownership, IRQ, and cache handshake test passed"); $finish; end diff --git a/tests/rtl/soc_irq_topology_tb.sv b/tests/rtl/soc_irq_topology_tb.sv index 5cd40e09..954b76b3 100644 --- a/tests/rtl/soc_irq_topology_tb.sv +++ b/tests/rtl/soc_irq_topology_tb.sv @@ -43,6 +43,7 @@ module soc_irq_topology_tb; 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)); endcase end @@ -70,7 +71,7 @@ module soc_irq_topology_tb; $fatal(1, "SDIO core IRQ bits are not low when sources are low: IRQ10=%b IRQ21=%b", s_irq[10], s_irq[21]); end - if ((s_irq[31] !== 1'b0) || (s_irq[19:18] !== '0)) begin + if (s_irq[19:18] !== '0) begin $fatal(1, "unallocated core IRQ bits are not low: %h", s_irq); end diff --git a/tests/test_apu.py b/tests/test_apu.py new file mode 100644 index 00000000..577f34a4 --- /dev/null +++ b/tests/test_apu.py @@ -0,0 +1,137 @@ +"""Directed verification for the APU-P1 APB4 register shell.""" + +from __future__ import annotations + +import shutil +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +TOPOLOGY = ROOT / "rtl/mini/integration/soc_topology.json" +TOPOLOGY_GENERATOR = ROOT / "rtl/mini/integration/generate_soc_topology.py" +MEMORY_MAP = ROOT / "rtl/mini/address_map/memory_map.json" + + +def test_apu_p1_apb_register_shell(tmp_path: Path) -> None: + iverilog = shutil.which("iverilog") + vvp = shutil.which("vvp") + sv2v = shutil.which("sv2v") + if iverilog is None or vvp is None or sv2v is None: + return + + common = ROOT / "rtl/managed/clusterip/common/rtl" + multimedia = ROOT / "rtl/ip/multimedia" + source_list = tmp_path / "apu_reg.fl" + source_list.write_text( + "\n".join( + [ + "+define+SV_ASSRT_DISABLE", + f"+incdir+{common}", + f"+incdir+{common / 'interface'}", + f"+incdir+{multimedia}", + str(common / "interface/apb4_if.sv"), + str(common / "utils/register.sv"), + str(multimedia / "apu_reg.sv"), + str(multimedia / "apb4_apu.sv"), + str(ROOT / "tests/rtl/apu_reg_tb.sv"), + "", + ] + ), + encoding="utf-8", + ) + converted = tmp_path / "apu_reg_tb.v" + subprocess.run( + [ + sys.executable, + str(ROOT / "rtl/mini/script/convt_sv2v.py"), + "-f", + str(source_list), + "--output", + str(converted), + ], + check=True, + ) + simulation = tmp_path / "apu_reg_tb" + subprocess.run( + [iverilog, "-g2012", "-s", "apu_reg_tb", "-o", str(simulation), str(converted)], + check=True, + ) + result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True) + assert "APU-P1 complete APB register matrix passed" in result.stdout + + +def test_apu_p1_integrated_irq_ownership_topology(tmp_path: Path) -> None: + iverilog = shutil.which("iverilog") + vvp = shutil.which("vvp") + sv2v = shutil.which("sv2v") + if iverilog is None or vvp is None or sv2v is None: + return + + common = ROOT / "rtl/managed/clusterip/common/rtl" + multimedia = ROOT / "rtl/ip/multimedia" + peripheral = ROOT / "rtl/ip/peripheral" + topology_output = tmp_path / "topology" + subprocess.run( + [ + sys.executable, + str(TOPOLOGY_GENERATOR), + "--map", + str(TOPOLOGY), + "--memory-map", + str(MEMORY_MAP), + "--output-dir", + str(topology_output), + ], + check=True, + ) + + source_list = tmp_path / "apu_irq_topology.fl" + source_list.write_text( + "\n".join( + [ + "+define+SV_ASSRT_DISABLE", + f"+incdir+{topology_output / 'rtl'}", + f"+incdir+{common}", + f"+incdir+{common / 'interface'}", + f"+incdir+{multimedia}", + str(common / "interface/apb4_if.sv"), + str(common / "utils/register.sv"), + str(multimedia / "apu_reg.sv"), + str(multimedia / "apb4_apu.sv"), + str(ROOT / "rtl/mini/top/resource_controller.sv"), + str(peripheral / "apb4_plic.sv"), + str(ROOT / "tests/rtl/apu_irq_topology_tb.sv"), + "", + ] + ), + encoding="utf-8", + ) + converted = tmp_path / "apu_irq_topology_tb.v" + subprocess.run( + [ + sys.executable, + str(ROOT / "rtl/mini/script/convt_sv2v.py"), + "-f", + str(source_list), + "--output", + str(converted), + ], + check=True, + ) + simulation = tmp_path / "apu_irq_topology_tb" + subprocess.run( + [ + iverilog, + "-g2012", + "-s", + "apu_irq_topology_tb", + "-o", + str(simulation), + str(converted), + ], + check=True, + ) + result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True) + assert "APU-P1 integrated IRQ ownership topology passed" in result.stdout diff --git a/tests/test_apu_register_parity.py b/tests/test_apu_register_parity.py new file mode 100644 index 00000000..e29d5465 --- /dev/null +++ b/tests/test_apu_register_parity.py @@ -0,0 +1,66 @@ +"""Keep the handwritten APU-P1 RTL and SDK register ABI synchronized.""" + +from __future__ import annotations + +import re +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +RTL_DEFINE = ROOT / "rtl/ip/multimedia/apu_define.svh" +C_HEADER = ROOT / "crt/include/retrosoc/hal/apu_regs.h" +REGISTER_TESTBENCH = ROOT / "tests/rtl/apu_reg_tb.sv" + + +def _rtl_values() -> dict[str, int]: + values: dict[str, int] = {} + pattern = re.compile( + r"^`define\s+APB4_APU__(\w+)\s+((?:\d+)'[hHdDbB][0-9a-fA-F_]+|\d+)\s*$" + ) + for line in RTL_DEFINE.read_text(encoding="utf-8").splitlines(): + match = pattern.match(line) + if match is None: + continue + name, literal = match.groups() + if "'" in literal: + _, value = literal.split("'", maxsplit=1) + radix = {"h": 16, "d": 10, "b": 2}[value[0].lower()] + values[name] = int(value[1:].replace("_", ""), radix) + else: + values[name] = int(literal, 10) + return values + + +def _c_values() -> dict[str, int]: + values: dict[str, int] = {} + pattern = re.compile( + r"^#define\s+RS_APU_ABI_(\w+)\s+" + r"(?:UINT32_C\((0[xX][0-9a-fA-F]+)\)|(\d+)[uU]?)\s*$" + ) + for line in C_HEADER.read_text(encoding="utf-8").splitlines(): + match = pattern.match(line) + if match is None: + continue + name, hexadecimal, decimal = match.groups() + values[name] = int(hexadecimal, 16) if hexadecimal is not None else int(decimal, 10) + return values + + +def test_apu_register_constants_match_expected_abi() -> None: + assert _c_values() == _rtl_values() + + +def test_apu_register_matrix_covers_each_offset_once() -> None: + offset_pattern = re.compile( + r"^`define\s+APB4_APU__(\w+)\s+12'[hH][0-9a-fA-F_]+\s*$" + ) + table_pattern = re.compile(r"add_register\(`APB4_APU__(\w+),") + expected = [ + match.group(1) + for line in RTL_DEFINE.read_text(encoding="utf-8").splitlines() + if (match := offset_pattern.match(line)) is not None + ] + covered = table_pattern.findall(REGISTER_TESTBENCH.read_text(encoding="utf-8")) + + assert len(covered) == len(set(covered)) + assert set(covered) == set(expected) diff --git a/tests/test_axi4.py b/tests/test_axi4.py index 65176b76..1a61c8ff 100644 --- a/tests/test_axi4.py +++ b/tests/test_axi4.py @@ -8,6 +8,8 @@ import subprocess import sys from pathlib import Path +import pytest + ROOT = Path(__file__).resolve().parents[1] @@ -173,8 +175,9 @@ def test_axi4_word_bridge_supports_bursts_backpressure_and_errors(tmp_path: Path assert "AXI4 word bridge test passed" in result.stdout +@pytest.mark.parametrize("product_mode", [False, True]) def test_axi4_interconnect_classifies_decode_protocol_and_access_errors( - tmp_path: Path, + tmp_path: Path, product_mode: bool ) -> None: verilator = shutil.which("verilator") if verilator is None: @@ -205,6 +208,7 @@ def test_axi4_interconnect_classifies_decode_protocol_and_access_errors( "-Wno-fatal", "--top-module", "axi4_interconnect_tb", + *(["-DMINI_PRODUCT"] if product_mode else []), "-I" + str(memory_map / "rtl"), "-I" + str(ROOT / "rtl/mini/top"), "-I" + str(ROOT / "rtl/managed/clusterip/common/rtl"), diff --git a/tests/test_memory_map.py b/tests/test_memory_map.py index b03c5270..992959a0 100644 --- a/tests/test_memory_map.py +++ b/tests/test_memory_map.py @@ -125,7 +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_APU_BASE" not in mmio_function + assert "RS_SOC_APB4_APU_BASE" in mmio_function def test_sram_capacity_selects_one_consistent_hardware_software_window(tmp_path: Path) -> None: @@ -173,9 +173,11 @@ def test_user_ip_is_always_emitted_for_the_fixed_platform(tmp_path: Path) -> Non assert "`define SOC_ADDR_APB4_SDIO0_BASE 32'h1000F000" in rtl assert "`define SOC_ADDR_APB4_SDIO1_BASE 32'h10015000" in rtl assert "`define SOC_ADDR_APB4_USB2_BASE 32'h10016000" in rtl + assert "`define SOC_ADDR_APB4_APU_BASE 32'h10013000" in rtl assert "`define SOC_ADDR_APB4_CRYPTO_BASE 32'h1000C000" in rtl 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 def test_bootstrap_assembly_uses_the_generated_gpio_admin_base() -> None: diff --git a/tests/test_soc_topology.py b/tests/test_soc_topology.py index 7d7d347e..731dd401 100644 --- a/tests/test_soc_topology.py +++ b/tests/test_soc_topology.py @@ -77,7 +77,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) - data_policy = (tmp_path / "rtl/soc_data_policy.svh").read_text(encoding="utf-8") filelist = (tmp_path / "soc_topology.fl").read_text(encoding="utf-8") - assert interfaces.count("apb4_if u_") == 25 + assert interfaces.count("apb4_if u_") == 26 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 @@ -93,6 +93,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) - assert "assign s_psel_comb[24] = `SOC_ADDR_IS_HP_ACLINT(s_decode_addr);" in routes 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 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 @@ -141,7 +142,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 32" in irq_config assert "`define SOC_USER_IRQ_MASK 32'h004EFBFC" in irq_config - assert "`define SOC_IRQ_APB4_PERIPH_WIDTH 23" in irq_config + assert "`define SOC_IRQ_APB4_PERIPH_WIDTH 24" 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 @@ -155,6 +156,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) - assert "assign irq_o[18] = s_crypto_irq;" in rib_irq 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[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 @@ -172,6 +174,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) - assert "s_irq[28] = s_apb4_system_irq[6];" in irq_wiring 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 "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 @@ -180,7 +183,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) - assert "irq_i[23] == apb4_periph_irq_i[18]" in irq_sva 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] == 1'b0" in irq_sva + assert "irq_i[31] == apb4_periph_irq_i[23]" 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 @@ -236,9 +239,28 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None: ("extension_h", "apb4_system", 6, 28, "s_ext_h_irq"), ("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]"), ] +def test_apu_p1_fixed_resource_and_hp_irq_allocations() -> None: + periph = (ROOT / "rtl/mini/top/apb4_periph.sv").read_text(encoding="utf-8") + 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") + resource_header = (ROOT / "crt/include/retrosoc/hal/resource.h").read_text( + encoding="utf-8" + ) + + 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 ".ResourceCount(8)" 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 + assert "RS_RESOURCE_APU = 7" in resource_header + assert "RS_RESOURCE_COUNT = 8" in resource_header + + def test_topology_always_adds_the_user_apb_target(tmp_path: Path) -> None: generate(tmp_path)