diff --git a/Makefile b/Makefile index a6d2a31e..25ebe89b 100644 --- a/Makefile +++ b/Makefile @@ -343,7 +343,7 @@ include physical/ecc/Makefile hp-performance-check \ pin-map check-pin-map soc-topology check-soc-topology user-extensions check-user-extensions \ check-clock-reset-domains tech-cell-test rtl-lint check-rtl-lint \ - formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-dvp formal-i2s formal-onchip-ram formal-opipsram formal-dma formal-sdio formal-clean formal-doctor \ + formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-dvp formal-i2s formal-onchip-ram formal-opipsram formal-dma formal-apu formal-gateway-a formal-sdio formal-clean formal-doctor \ rtl-style-check-all rtl-readiness-check rtl-readiness-check-all vexii-generate .NOTPARALLEL: setup @@ -387,7 +387,7 @@ help: ' check-clock-reset-domains validate the root clock/reset and CDC inventory' \ ' rtl-lint | check-rtl-lint run/check strict Verilator RTL lint warnings' \ ' formal | formal-bus | formal-rib-adapter | formal-rib2apb run SBY protocol proofs' \ - 'formal-sysctrl | formal-pll-rcu | formal-gpio | formal-ws2812 | formal-uart | formal-i2c | formal-timer | formal-clint | formal-dvp | formal-i2s | formal-onchip-ram | formal-opipsram | formal-dma | formal-sdio run peripheral proofs' \ + 'formal-sysctrl | formal-pll-rcu | formal-gpio | formal-ws2812 | formal-uart | formal-i2c | formal-timer | formal-clint | formal-dvp | formal-i2s | formal-onchip-ram | formal-opipsram | formal-dma | formal-apu | formal-gateway-a | formal-sdio run peripheral proofs' \ ' formal-doctor check the SBY, Yosys, sv2v, and Bitwuzla formal toolchain' \ ' benchmark-report run the memory/DMA profile and write meta/performance.json' \ ' coremark-report run the quick CoreMark profile and write meta/coremark.json' \ @@ -684,4 +684,4 @@ regress-nightly: python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly sim-asm: asm - $(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim \ No newline at end of file + $(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim diff --git a/app/apps/ci_smoke/main.c b/app/apps/ci_smoke/main.c index 5e3f4a94..7a086e3f 100644 --- a/app/apps/ci_smoke/main.c +++ b/app/apps/ci_smoke/main.c @@ -26,7 +26,7 @@ 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) { +static bool rs_ci_smoke_apu(void) { rs_apu_info_t info; return (rs_apu_probe(&info) == RS_OK) && (info.capability0 == RS_APU_CAPABILITY0_IMPLEMENTED) && @@ -385,10 +385,10 @@ int main(void) { rs_test_finish(RS_TEST_FAILED, 1U); } printf("ci_smoke: archinfo passed\n"); - if (!rs_ci_smoke_apu_p1()) { + if (!rs_ci_smoke_apu()) { rs_test_finish(RS_TEST_FAILED, 14U); } - printf("ci_smoke: APU-P1 discovery passed\n"); + printf("ci_smoke: APU-P2 discovery passed\n"); if (!rs_ci_smoke_fabric_monitor_start()) { rs_test_finish(RS_TEST_FAILED, 13U); } diff --git a/crt/include/retrosoc/hal/apu.h b/crt/include/retrosoc/hal/apu.h index 0cdad704..771a8431 100644 --- a/crt/include/retrosoc/hal/apu.h +++ b/crt/include/retrosoc/hal/apu.h @@ -1,6 +1,7 @@ #ifndef RETROSOC_HAL_APU_H #define RETROSOC_HAL_APU_H +#include #include #include @@ -14,6 +15,59 @@ typedef struct { uint32_t abi_digest; } rs_apu_info_t; +typedef struct { + uint32_t control; + uint32_t result_status; + uint32_t input_address; + uint32_t input_length; + uint32_t output_address; + uint32_t output_capacity; + uint32_t input_config; + uint32_t output_config; + uint32_t job_flags; + uint32_t kws_config; + uint32_t result_input_used; + uint32_t result_output_bytes; + uint32_t result_frames; + uint32_t result_source_info; + uint32_t result_cycles; + uint32_t result_detail; + uint32_t cookie[2]; + uint32_t start_timestamp[2]; + uint32_t finish_timestamp[2]; + uint32_t kws_result; + uint32_t microcode_build_id; + uint32_t reserved[8]; +} rs_apu_descriptor_t; + +_Static_assert(sizeof(rs_apu_descriptor_t) == RS_APU_ABI_DESCRIPTOR_BYTES, + "APU descriptor ABI must be 128 bytes"); +#define RS_APU_DESCRIPTOR_OFFSET_ASSERT(member, word) \ + _Static_assert(offsetof(rs_apu_descriptor_t, member) == ((word) * sizeof(uint32_t)), \ + "APU descriptor member offset mismatch") +RS_APU_DESCRIPTOR_OFFSET_ASSERT(control, RS_APU_ABI_DESCRIPTOR_CONTROL); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(result_status, RS_APU_ABI_DESCRIPTOR_RESULT_STATUS); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(input_address, RS_APU_ABI_DESCRIPTOR_INPUT_ADDRESS); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(input_length, RS_APU_ABI_DESCRIPTOR_INPUT_LENGTH); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(output_address, RS_APU_ABI_DESCRIPTOR_OUTPUT_ADDRESS); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(output_capacity, RS_APU_ABI_DESCRIPTOR_OUTPUT_CAPACITY); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(input_config, RS_APU_ABI_DESCRIPTOR_INPUT_CONFIG); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(output_config, RS_APU_ABI_DESCRIPTOR_OUTPUT_CONFIG); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(job_flags, RS_APU_ABI_DESCRIPTOR_JOB_FLAGS); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(kws_config, RS_APU_ABI_DESCRIPTOR_KWS_CONFIG); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(result_input_used, RS_APU_ABI_DESCRIPTOR_RESULT_INPUT_USED); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(result_output_bytes, RS_APU_ABI_DESCRIPTOR_RESULT_OUTPUT_BYTES); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(result_frames, RS_APU_ABI_DESCRIPTOR_RESULT_FRAMES); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(result_source_info, RS_APU_ABI_DESCRIPTOR_RESULT_SOURCE_INFO); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(result_cycles, RS_APU_ABI_DESCRIPTOR_RESULT_CYCLES); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(result_detail, RS_APU_ABI_DESCRIPTOR_RESULT_DETAIL); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(cookie, RS_APU_ABI_DESCRIPTOR_COOKIE_LO); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(start_timestamp, RS_APU_ABI_DESCRIPTOR_START_TIMESTAMP_LO); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(finish_timestamp, RS_APU_ABI_DESCRIPTOR_FINISH_TIMESTAMP_LO); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(kws_result, RS_APU_ABI_DESCRIPTOR_KWS_RESULT); +RS_APU_DESCRIPTOR_OFFSET_ASSERT(microcode_build_id, RS_APU_ABI_DESCRIPTOR_MICROCODE_BUILD_ID); +#undef RS_APU_DESCRIPTOR_OFFSET_ASSERT + 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 index ea609239..8cbad6a5 100644 --- a/crt/include/retrosoc/hal/apu_regs.h +++ b/crt/include/retrosoc/hal/apu_regs.h @@ -6,205 +6,279 @@ #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_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_STREAM_WATERMARK UINT32_C(0x060) +#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_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_STREAM_STATUS_TX_ACTIVE 0U +#define RS_APU_ABI_STREAM_STATUS_RX_ACTIVE 1U +#define RS_APU_ABI_STREAM_STATUS_TX_EMPTY 2U +#define RS_APU_ABI_STREAM_STATUS_TX_FULL 3U +#define RS_APU_ABI_STREAM_STATUS_RX_EMPTY 4U +#define RS_APU_ABI_STREAM_STATUS_RX_FULL 5U +#define RS_APU_ABI_STREAM_STATUS_TX_UNDERRUN 6U +#define RS_APU_ABI_STREAM_STATUS_RX_OVERRUN 7U +#define RS_APU_ABI_STREAM_STATUS_TX_COUNT 8U +#define RS_APU_ABI_STREAM_STATUS_RX_COUNT 16U +#define RS_APU_ABI_STREAM_WATERMARK_RX_HIGH 0U +#define RS_APU_ABI_STREAM_WATERMARK_TX_LOW 8U +#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_JOB_STATUS_BUSY 0U +#define RS_APU_ABI_JOB_STATUS_DONE 1U +#define RS_APU_ABI_JOB_STATUS_ERROR 2U +#define RS_APU_ABI_JOB_STATUS_ABORTED 3U +#define RS_APU_ABI_JOB_STATUS_DIRECT_ACTIVE 4U +#define RS_APU_ABI_JOB_STATUS_INPUT_PENDING 5U +#define RS_APU_ABI_JOB_STATUS_OUTPUT_PENDING 6U +#define RS_APU_ABI_JOB_STATUS_WRITEBACK_PENDING 7U +#define RS_APU_ABI_JOB_STATUS_ERROR_CODE 8U +#define RS_APU_ABI_JOB_STATUS_STAGE 14U +#define RS_APU_ABI_JOB_STATUS_AXI_RESPONSE 18U +#define RS_APU_ABI_RING_STATUS_ACTIVE 0U +#define RS_APU_ABI_RING_STATUS_STALLED_UNOWNED 1U +#define RS_APU_ABI_RING_STATUS_EMPTY 2U +#define RS_APU_ABI_RING_STATUS_ERROR 3U +#define RS_APU_ABI_RING_STATUS_WRAPPED 4U +#define RS_APU_ABI_RING_STATUS_COALESCE_PENDING 5U +#define RS_APU_ABI_RING_STATUS_WRITEBACK_PENDING 6U +#define RS_APU_ABI_RING_STATUS_STOPPED_ON_ERROR 7U +#define RS_APU_ABI_RING_STATUS_PENDING_COUNT 8U +#define RS_APU_ABI_RING_STATUS_LAST_INDEX 16U +#define RS_APU_ABI_DESCRIPTOR_WORDS 32U +#define RS_APU_ABI_DESCRIPTOR_BYTES 128U +#define RS_APU_ABI_DESCRIPTOR_ALIGNMENT 128U +#define RS_APU_ABI_DESCRIPTOR_CONTROL 0U +#define RS_APU_ABI_DESCRIPTOR_RESULT_STATUS 1U +#define RS_APU_ABI_DESCRIPTOR_INPUT_ADDRESS 2U +#define RS_APU_ABI_DESCRIPTOR_INPUT_LENGTH 3U +#define RS_APU_ABI_DESCRIPTOR_OUTPUT_ADDRESS 4U +#define RS_APU_ABI_DESCRIPTOR_OUTPUT_CAPACITY 5U +#define RS_APU_ABI_DESCRIPTOR_INPUT_CONFIG 6U +#define RS_APU_ABI_DESCRIPTOR_OUTPUT_CONFIG 7U +#define RS_APU_ABI_DESCRIPTOR_JOB_FLAGS 8U +#define RS_APU_ABI_DESCRIPTOR_KWS_CONFIG 9U +#define RS_APU_ABI_DESCRIPTOR_RESULT_INPUT_USED 10U +#define RS_APU_ABI_DESCRIPTOR_RESULT_OUTPUT_BYTES 11U +#define RS_APU_ABI_DESCRIPTOR_RESULT_FRAMES 12U +#define RS_APU_ABI_DESCRIPTOR_RESULT_SOURCE_INFO 13U +#define RS_APU_ABI_DESCRIPTOR_RESULT_CYCLES 14U +#define RS_APU_ABI_DESCRIPTOR_RESULT_DETAIL 15U +#define RS_APU_ABI_DESCRIPTOR_COOKIE_LO 16U +#define RS_APU_ABI_DESCRIPTOR_COOKIE_HI 17U +#define RS_APU_ABI_DESCRIPTOR_START_TIMESTAMP_LO 18U +#define RS_APU_ABI_DESCRIPTOR_START_TIMESTAMP_HI 19U +#define RS_APU_ABI_DESCRIPTOR_FINISH_TIMESTAMP_LO 20U +#define RS_APU_ABI_DESCRIPTOR_FINISH_TIMESTAMP_HI 21U +#define RS_APU_ABI_DESCRIPTOR_KWS_RESULT 22U +#define RS_APU_ABI_DESCRIPTOR_MICROCODE_BUILD_ID 23U +#define RS_APU_ABI_DESCRIPTOR_CONTROL_OPERATION 0U +#define RS_APU_ABI_DESCRIPTOR_CONTROL_INPUT_FORMAT 4U +#define RS_APU_ABI_DESCRIPTOR_CONTROL_OUTPUT_MODE 8U +#define RS_APU_ABI_DESCRIPTOR_CONTROL_DOWNMIX 10U +#define RS_APU_ABI_DESCRIPTOR_CONTROL_RESAMPLE 11U +#define RS_APU_ABI_DESCRIPTOR_CONTROL_IOC 30U +#define RS_APU_ABI_DESCRIPTOR_CONTROL_OWN 31U +#define RS_APU_ABI_DESCRIPTOR_STATUS_DONE 0U +#define RS_APU_ABI_DESCRIPTOR_STATUS_ERROR 1U +#define RS_APU_ABI_DESCRIPTOR_STATUS_ABORTED 2U +#define RS_APU_ABI_DESCRIPTOR_STATUS_ERROR_CODE 3U +#define RS_APU_ABI_DESCRIPTOR_STATUS_STAGE 9U +#define RS_APU_ABI_DESCRIPTOR_STATUS_AXI_RESPONSE 13U +#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_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(0x00000018) +#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)) +#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/docs/ip/apu.md b/docs/ip/apu.md index 19dcf0e5..20c9502e 100644 --- a/docs/ip/apu.md +++ b/docs/ip/apu.md @@ -26,10 +26,11 @@ 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. -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 +APU-P2 implements the private AXI4 DMA, descriptor ring scheduler, fair Gateway +A arbitration, and the production audio stream router with two 64-word Common +FIFOs. The shell reports only private-DMA and ring capabilities; public direct +start, ring doorbell, and APU stream routes remain fail-closed until their +later-phase engines exist. No claim is made that microcode, sequencer, codec engines, KWS, codec conformance, Linux driver, timing closure, power closure, CDC/RDC signoff, or silicon qualification exists. diff --git a/rtl/README.md b/rtl/README.md index 08341273..4c39aa1d 100644 --- a/rtl/README.md +++ b/rtl/README.md @@ -119,12 +119,13 @@ ownership. Its implemented limits, handwritten ABI, measured performance, and commercial release gates are documented in [Baseline JPEG Codec](../docs/ip/jpeg.md). -The Mini 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). +The Mini Audio Processing Unit has a frozen coreless architecture. APU-P2 adds +the private AXI4 DMA, descriptor scheduler, fair Gateway A arbitration, and +production stream router/FIFOs to the fail-closed APB4 shell at `APB4_APU`. +Resource Controller index 7 and exclusive LP IRQ31/HP PLIC source10 routing +remain fixed. Public jobs and APU stream routes remain disabled until their +later-phase engines exist. 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 index 0e644b84..4981f884 100644 --- a/rtl/ip/multimedia/apb4_apu.sv +++ b/rtl/ip/multimedia/apb4_apu.sv @@ -1,28 +1,380 @@ // Copyright (c) 2026 Yuchi Miao -// retroSoC is licensed under Mulan PSL v2. +// SPDX-License-Identifier: MulanPSL-2.0 + +`include "apu_define.svh" 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: off -- preserve APB, lifecycle, DMA, and stream 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, + input logic [7:0] bridge_epoch_i, + input logic i2s_tx_underrun_i, + input logic i2s_rx_overrun_i, + apb4_if.slave apb4, + axi4_if.master axi4, + axi4_stream_if.sink dma_tx_axis, + axi4_stream_if.source dma_rx_axis, + axi4_stream_if.source i2s_tx_axis, + axi4_stream_if.sink i2s_rx_axis, + output logic idle_o, + output logic irq_o // verilog_format: on ); + logic s_soft_reset, s_abort, s_cnt_clear, s_xrun_clear, s_perf_enable; + logic [ 3:0] s_stream_route; + logic [15:0] s_stream_watermark; + logic [31:0] s_read_base, s_read_limit, s_write_base, s_write_limit; + logic [31:0] s_dma_timeout, s_ring_base, s_ring_coalesce; + logic [8:0] s_ring_size; + logic [7:0] s_ring_tail, s_ring_head; + logic [1:0] s_ring_control; + logic [31:0] s_stream_stat, s_job_stat, s_ring_stat, s_ring_completed; + logic [10:0] s_irq_set; + logic s_input_watermark_evt, s_output_watermark_evt, s_xrun_evt; + logic s_ring_event, s_ring_err, s_abort_done; + logic [ 5:0] s_ring_err_code; + logic [ 3:0] s_ring_err_stage; + logic [ 1:0] s_ring_err_resp; + logic [ 7:0] s_ring_err_index; + logic [31:0] s_ring_err_addr; + logic s_fault_valid; + logic [ 5:0] s_fault_code; + logic [ 3:0] s_fault_stage; + logic [ 1:0] s_fault_resp; + logic [ 7:0] s_fault_index; + logic [31:0] s_fault_addr; + logic s_dma_req_valid, s_dma_req_ready, s_dma_req_write; + logic [31:0] s_dma_req_addr, s_dma_req_bytes; + logic s_dma_busy, s_dma_done, s_dma_err, s_dma_aborted, s_dma_aborting; + logic s_dma_abort; + logic [ 5:0] s_dma_err_code; + logic [ 3:0] s_dma_err_stage; + logic [ 1:0] s_dma_err_resp; + logic [31:0] s_dma_err_addr; + logic s_dma_input_pending, s_dma_output_pending; + logic [63:0] s_dma_read_bytes, s_dma_write_bytes; + logic [63:0] s_dma_read_stalls, s_dma_write_stalls; + logic s_scheduler_idle, s_scheduler_aborting, s_stream_idle; + logic s_dma_admission_block; + logic s_transport_idle, s_resource_reset_request, s_resource_reset_apply_q; + logic s_resource_reset_pending_q, s_resource_reset_seen_q; + logic s_tx_route_apu, s_rx_route_apu; + logic s_reg_idle_unused, s_backend_job_valid_unused, s_backend_resp_ready_unused; + logic [1023:0] s_backend_descriptor_unused; + logic [ 7:0] s_backend_index_unused; + logic [63:0] s_active_cycles_q, s_stream_stalls_q, s_faults_q; + + axi4_stream_if #( + .DATA_WIDTH(32) + ) u_dma_read_axis ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) u_dma_write_axis ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) u_apu_tx_axis ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) u_apu_rx_axis ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + + assign u_apu_tx_axis.tdata = 32'd0; + assign u_apu_tx_axis.tkeep = 4'hf; + assign u_apu_tx_axis.tstrb = 4'hf; + assign u_apu_tx_axis.tlast = 1'b0; + assign u_apu_tx_axis.tid = '0; + assign u_apu_tx_axis.tdest = '0; + assign u_apu_tx_axis.tuser = '0; + assign u_apu_tx_axis.tvalid = 1'b0; + assign u_apu_rx_axis.tready = 1'b0; + assign s_irq_set = { + 1'b0, + s_xrun_evt, + s_fault_valid, + s_output_watermark_evt, + s_input_watermark_evt, + s_abort_done, + 3'b000, + s_ring_event, + 1'b0 + }; + assign s_tx_route_apu = s_stream_route[1:0] == 2'd1; + assign s_rx_route_apu = s_stream_route[3:2] == 2'd1; + assign s_transport_idle = s_scheduler_idle && !s_dma_busy && s_stream_idle; + assign idle_o = s_transport_idle; + assign s_resource_reset_request = s_resource_reset_pending_q || + (resource_reset_i && !s_resource_reset_seen_q); + assign s_dma_abort = (s_abort || s_resource_reset_request) && + !s_ring_stat[`APB4_APU__RING_STATUS_WRITEBACK_PENDING]; + assign s_dma_admission_block = (quiesce_i || s_resource_reset_request) && + !s_job_stat[`APB4_APU__JOB_STATUS_BUSY] && + !s_ring_stat[`APB4_APU__RING_STATUS_WRITEBACK_PENDING]; + + always_comb begin + s_fault_valid = s_ring_err; + s_fault_code = s_ring_err_code; + s_fault_stage = s_ring_err_stage; + s_fault_resp = s_ring_err_resp; + s_fault_index = s_ring_err_index; + s_fault_addr = s_ring_err_addr; + if (!s_ring_err && s_rx_route_apu && i2s_rx_overrun_i) begin + s_fault_valid = 1'b1; + s_fault_code = `APB4_APU__ERROR_CODE_STREAM_OVERRUN; + s_fault_stage = `APB4_APU__ERROR_STAGE_KWS_FRONTEND; + s_fault_resp = 2'd0; + s_fault_index = 8'd0; + s_fault_addr = 32'd0; + end else if (!s_ring_err && s_tx_route_apu && i2s_tx_underrun_i) begin + s_fault_valid = 1'b1; + s_fault_code = `APB4_APU__ERROR_CODE_STREAM_UNDERRUN; + s_fault_stage = `APB4_APU__ERROR_STAGE_DMA_WRITE; + s_fault_resp = 2'd0; + s_fault_index = 8'd0; + s_fault_addr = 32'd0; + end + end + 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), + .resource_reset_apply_i (s_resource_reset_apply_q), + .core_idle_i (idle_o), + .core_busy_i (!idle_o), + .core_aborting_i (s_dma_aborting || s_scheduler_aborting || s_resource_reset_request), + .stream_status_i (s_stream_stat), + .job_status_i (s_job_stat), + .job_input_used_i (32'd0), + .job_output_bytes_i (32'd0), + .job_frames_i (32'd0), + .job_source_info_i (32'd0), + .job_cycles_i (32'd0), + .job_detail_i (32'd0), + .ring_status_i (s_ring_stat), + .ring_head_i (s_ring_head), + .ring_completed_i (s_ring_completed), + .irq_set_i (s_irq_set), + .fault_valid_i (s_fault_valid), + .fault_code_i (s_fault_code), + .fault_stage_i (s_fault_stage), + .fault_resp_i (s_fault_resp), + .fault_index_i (s_fault_index), + .fault_addr_i (s_fault_addr), + .fault_detail_i (32'd0), + .perf_active_cycles_i (s_active_cycles_q), + .perf_input_bytes_i (s_dma_read_bytes), + .perf_output_bytes_i (s_dma_write_bytes), + .perf_dma_read_stalls_i (s_dma_read_stalls), + .perf_dma_write_stalls_i(s_dma_write_stalls), + .perf_stream_stalls_i (s_stream_stalls_q), + .perf_faults_i (s_faults_q), + .apb4 (apb4), + .soft_reset_o (s_soft_reset), + .abort_o (s_abort), + .counter_clear_o (s_cnt_clear), + .perf_enable_o (s_perf_enable), + .xrun_clear_o (s_xrun_clear), + .stream_route_o (s_stream_route), + .stream_watermark_o (s_stream_watermark), + .read_base_o (s_read_base), + .read_limit_o (s_read_limit), + .write_base_o (s_write_base), + .write_limit_o (s_write_limit), + .dma_timeout_o (s_dma_timeout), + .ring_base_o (s_ring_base), + .ring_size_o (s_ring_size), + .ring_tail_o (s_ring_tail), + .ring_control_o (s_ring_control), + .ring_coalesce_o (s_ring_coalesce), + .idle_o (s_reg_idle_unused), + .irq_o (irq_o) + ); + + apu_dma u_apu_dma ( .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) + .abort_i (s_dma_abort), + .quiesce_i (s_dma_admission_block), + .bridge_epoch_i (bridge_epoch_i), + .perf_enable_i (s_perf_enable), + .counter_clear_i (s_cnt_clear), + .request_valid_i (s_dma_req_valid), + .request_ready_o (s_dma_req_ready), + .request_write_i (s_dma_req_write), + .request_addr_i (s_dma_req_addr), + .request_bytes_i (s_dma_req_bytes), + .read_base_i (s_read_base), + .read_limit_i (s_read_limit), + .write_base_i (s_write_base), + .write_limit_i (s_write_limit), + .timeout_i (s_dma_timeout), + .read_axis (u_dma_read_axis), + .write_axis (u_dma_write_axis), + .busy_o (s_dma_busy), + .done_o (s_dma_done), + .error_o (s_dma_err), + .aborted_o (s_dma_aborted), + .aborting_o (s_dma_aborting), + .error_code_o (s_dma_err_code), + .error_stage_o (s_dma_err_stage), + .error_resp_o (s_dma_err_resp), + .error_addr_o (s_dma_err_addr), + .input_pending_o (s_dma_input_pending), + .output_pending_o(s_dma_output_pending), + .read_bytes_o (s_dma_read_bytes), + .write_bytes_o (s_dma_write_bytes), + .read_stalls_o (s_dma_read_stalls), + .write_stalls_o (s_dma_write_stalls), + .axi4 (axi4) ); + + apu_ring_scheduler u_ring_scheduler ( + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .soft_reset_i (s_soft_reset || s_resource_reset_apply_q), + .counter_clear_i (s_cnt_clear), + .abort_i (s_abort || s_resource_reset_request), + .quiesce_i (quiesce_i || s_resource_reset_request), + .start_i (1'b0), + .ring_base_i (s_ring_base), + .ring_size_i (s_ring_size), + .ring_tail_i (s_ring_tail), + .ring_enable_i (s_ring_control[0]), + .stop_on_error_i (s_ring_control[1]), + .coalesce_count_i (s_ring_coalesce[7:0]), + .coalesce_timeout_i (s_ring_coalesce[31:16]), + .dma_request_valid_o (s_dma_req_valid), + .dma_request_ready_i (s_dma_req_ready), + .dma_request_write_o (s_dma_req_write), + .dma_request_addr_o (s_dma_req_addr), + .dma_request_bytes_o (s_dma_req_bytes), + .dma_read_axis (u_dma_read_axis), + .dma_write_axis (u_dma_write_axis), + .dma_done_i (s_dma_done), + .dma_error_i (s_dma_err), + .dma_error_code_i (s_dma_err_code), + .dma_error_stage_i (s_dma_err_stage), + .dma_error_resp_i (s_dma_err_resp), + .dma_error_addr_i (s_dma_err_addr), + .backend_job_valid_o (s_backend_job_valid_unused), + .backend_job_ready_i (1'b0), + .backend_descriptor_o (s_backend_descriptor_unused), + .backend_index_o (s_backend_index_unused), + .backend_result_valid_i(1'b0), + .backend_result_ready_o(s_backend_resp_ready_unused), + .backend_result_error_i(1'b0), + .backend_result_code_i (6'd0), + .backend_result_stage_i(4'd0), + .backend_result_resp_i (2'd0), + .backend_input_used_i (32'd0), + .backend_output_bytes_i(32'd0), + .backend_frames_i (32'd0), + .backend_source_info_i (32'd0), + .backend_cycles_i (32'd0), + .backend_detail_i (32'd0), + .job_status_o (s_job_stat), + .ring_status_o (s_ring_stat), + .ring_head_o (s_ring_head), + .ring_completed_o (s_ring_completed), + .ring_event_o (s_ring_event), + .error_event_o (s_ring_err), + .error_code_o (s_ring_err_code), + .error_stage_o (s_ring_err_stage), + .error_resp_o (s_ring_err_resp), + .error_index_o (s_ring_err_index), + .error_addr_o (s_ring_err_addr), + .abort_done_o (s_abort_done), + .aborting_o (s_scheduler_aborting), + .idle_o (s_scheduler_idle) + ); + + apu_stream_router u_stream_router ( + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .flush_i (s_soft_reset || s_resource_reset_apply_q), + .counter_clear_i (s_cnt_clear), + .xrun_clear_i (s_xrun_clear), + .tx_route_apu_i (s_tx_route_apu), + .rx_route_apu_i (s_rx_route_apu), + .tx_session_active_i (1'b0), + .rx_session_active_i (1'b0), + .rx_high_watermark_i (s_stream_watermark[7:0]), + .tx_low_watermark_i (s_stream_watermark[15:8]), + .tx_underrun_i (i2s_tx_underrun_i), + .rx_overrun_i (i2s_rx_overrun_i), + .dma_tx_axis (dma_tx_axis), + .dma_rx_axis (dma_rx_axis), + .i2s_tx_axis (i2s_tx_axis), + .i2s_rx_axis (i2s_rx_axis), + .apu_tx_axis (u_apu_tx_axis), + .apu_rx_axis (u_apu_rx_axis), + .status_o (s_stream_stat), + .input_watermark_evt_o (s_input_watermark_evt), + .output_watermark_evt_o(s_output_watermark_evt), + .stream_xrun_evt_o (s_xrun_evt), + .idle_o (s_stream_idle) + ); + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + s_resource_reset_pending_q <= 1'b0; + s_resource_reset_seen_q <= 1'b0; + s_resource_reset_apply_q <= 1'b0; + end else begin + s_resource_reset_apply_q <= 1'b0; + if (resource_reset_i && !s_resource_reset_seen_q) begin + s_resource_reset_pending_q <= 1'b1; + s_resource_reset_seen_q <= 1'b1; + end + if (s_resource_reset_request && s_transport_idle) begin + s_resource_reset_pending_q <= 1'b0; + s_resource_reset_apply_q <= 1'b1; + end + if (!resource_reset_i && !s_resource_reset_pending_q) begin + s_resource_reset_seen_q <= 1'b0; + end + end + end + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i || s_soft_reset || s_resource_reset_apply_q || s_cnt_clear) begin + s_active_cycles_q <= 64'd0; + s_stream_stalls_q <= 64'd0; + s_faults_q <= 64'd0; + end else begin + if (s_perf_enable && !idle_o && !(&s_active_cycles_q)) begin + s_active_cycles_q <= s_active_cycles_q + 1'b1; + end + if (s_perf_enable && ((i2s_tx_axis.tvalid && !i2s_tx_axis.tready) || + (i2s_rx_axis.tvalid && !i2s_rx_axis.tready)) && !(&s_stream_stalls_q)) begin + s_stream_stalls_q <= s_stream_stalls_q + 1'b1; + end + if (s_perf_enable && s_fault_valid && !(&s_faults_q)) begin + s_faults_q <= s_faults_q + 1'b1; + end + end + end + + logic s_unused_pending; + assign s_unused_pending = s_dma_input_pending ^ s_dma_output_pending ^ s_dma_aborted ^ + s_reg_idle_unused ^ s_backend_job_valid_unused ^ s_backend_resp_ready_unused ^ + ^s_backend_descriptor_unused ^ ^s_backend_index_unused ^ ^s_ring_coalesce[15:8]; endmodule diff --git a/rtl/ip/multimedia/apu_define.svh b/rtl/ip/multimedia/apu_define.svh index d49ae8ea..fe10a1a4 100644 --- a/rtl/ip/multimedia/apu_define.svh +++ b/rtl/ip/multimedia/apu_define.svh @@ -29,6 +29,7 @@ `define APB4_APU__ABI_DIGEST 12'h054 `define APB4_APU__SEQUENCER_STATUS 12'h058 `define APB4_APU__SEQUENCER_RETIRED 12'h05c +`define APB4_APU__STREAM_WATERMARK 12'h060 `define APB4_APU__MC_IMAGE_ADDRESS 12'h080 `define APB4_APU__MC_IMAGE_SIZE 12'h084 @@ -139,6 +140,20 @@ `define APB4_APU__STATUS_IDLE 8 `define APB4_APU__STATUS_SEQUENCER_TRAPPED 9 +`define APB4_APU__STREAM_STATUS_TX_ACTIVE 0 +`define APB4_APU__STREAM_STATUS_RX_ACTIVE 1 +`define APB4_APU__STREAM_STATUS_TX_EMPTY 2 +`define APB4_APU__STREAM_STATUS_TX_FULL 3 +`define APB4_APU__STREAM_STATUS_RX_EMPTY 4 +`define APB4_APU__STREAM_STATUS_RX_FULL 5 +`define APB4_APU__STREAM_STATUS_TX_UNDERRUN 6 +`define APB4_APU__STREAM_STATUS_RX_OVERRUN 7 +`define APB4_APU__STREAM_STATUS_TX_COUNT 8 +`define APB4_APU__STREAM_STATUS_RX_COUNT 16 + +`define APB4_APU__STREAM_WATERMARK_RX_HIGH 0 +`define APB4_APU__STREAM_WATERMARK_TX_LOW 8 + `define APB4_APU__IRQ_DIRECT_DONE 0 `define APB4_APU__IRQ_RING_EVENT 1 `define APB4_APU__IRQ_KWS_HIT 2 @@ -198,6 +213,71 @@ `define APB4_APU__OWNER_STATUS_LOCK 8 `define APB4_APU__OWNER_STATUS_QUIESCE 9 `define APB4_APU__OWNER_STATUS_RESET 10 + +`define APB4_APU__JOB_STATUS_BUSY 0 +`define APB4_APU__JOB_STATUS_DONE 1 +`define APB4_APU__JOB_STATUS_ERROR 2 +`define APB4_APU__JOB_STATUS_ABORTED 3 +`define APB4_APU__JOB_STATUS_DIRECT_ACTIVE 4 +`define APB4_APU__JOB_STATUS_INPUT_PENDING 5 +`define APB4_APU__JOB_STATUS_OUTPUT_PENDING 6 +`define APB4_APU__JOB_STATUS_WRITEBACK_PENDING 7 +`define APB4_APU__JOB_STATUS_ERROR_CODE 8 +`define APB4_APU__JOB_STATUS_STAGE 14 +`define APB4_APU__JOB_STATUS_AXI_RESPONSE 18 + +`define APB4_APU__RING_STATUS_ACTIVE 0 +`define APB4_APU__RING_STATUS_STALLED_UNOWNED 1 +`define APB4_APU__RING_STATUS_EMPTY 2 +`define APB4_APU__RING_STATUS_ERROR 3 +`define APB4_APU__RING_STATUS_WRAPPED 4 +`define APB4_APU__RING_STATUS_COALESCE_PENDING 5 +`define APB4_APU__RING_STATUS_WRITEBACK_PENDING 6 +`define APB4_APU__RING_STATUS_STOPPED_ON_ERROR 7 +`define APB4_APU__RING_STATUS_PENDING_COUNT 8 +`define APB4_APU__RING_STATUS_LAST_INDEX 16 + +`define APB4_APU__DESCRIPTOR_WORDS 32 +`define APB4_APU__DESCRIPTOR_BYTES 128 +`define APB4_APU__DESCRIPTOR_ALIGNMENT 128 +`define APB4_APU__DESCRIPTOR_CONTROL 0 +`define APB4_APU__DESCRIPTOR_RESULT_STATUS 1 +`define APB4_APU__DESCRIPTOR_INPUT_ADDRESS 2 +`define APB4_APU__DESCRIPTOR_INPUT_LENGTH 3 +`define APB4_APU__DESCRIPTOR_OUTPUT_ADDRESS 4 +`define APB4_APU__DESCRIPTOR_OUTPUT_CAPACITY 5 +`define APB4_APU__DESCRIPTOR_INPUT_CONFIG 6 +`define APB4_APU__DESCRIPTOR_OUTPUT_CONFIG 7 +`define APB4_APU__DESCRIPTOR_JOB_FLAGS 8 +`define APB4_APU__DESCRIPTOR_KWS_CONFIG 9 +`define APB4_APU__DESCRIPTOR_RESULT_INPUT_USED 10 +`define APB4_APU__DESCRIPTOR_RESULT_OUTPUT_BYTES 11 +`define APB4_APU__DESCRIPTOR_RESULT_FRAMES 12 +`define APB4_APU__DESCRIPTOR_RESULT_SOURCE_INFO 13 +`define APB4_APU__DESCRIPTOR_RESULT_CYCLES 14 +`define APB4_APU__DESCRIPTOR_RESULT_DETAIL 15 +`define APB4_APU__DESCRIPTOR_COOKIE_LO 16 +`define APB4_APU__DESCRIPTOR_COOKIE_HI 17 +`define APB4_APU__DESCRIPTOR_START_TIMESTAMP_LO 18 +`define APB4_APU__DESCRIPTOR_START_TIMESTAMP_HI 19 +`define APB4_APU__DESCRIPTOR_FINISH_TIMESTAMP_LO 20 +`define APB4_APU__DESCRIPTOR_FINISH_TIMESTAMP_HI 21 +`define APB4_APU__DESCRIPTOR_KWS_RESULT 22 +`define APB4_APU__DESCRIPTOR_MICROCODE_BUILD_ID 23 +`define APB4_APU__DESCRIPTOR_CONTROL_OPERATION 0 +`define APB4_APU__DESCRIPTOR_CONTROL_INPUT_FORMAT 4 +`define APB4_APU__DESCRIPTOR_CONTROL_OUTPUT_MODE 8 +`define APB4_APU__DESCRIPTOR_CONTROL_DOWNMIX 10 +`define APB4_APU__DESCRIPTOR_CONTROL_RESAMPLE 11 +`define APB4_APU__DESCRIPTOR_CONTROL_IOC 30 +`define APB4_APU__DESCRIPTOR_CONTROL_OWN 31 +`define APB4_APU__DESCRIPTOR_STATUS_DONE 0 +`define APB4_APU__DESCRIPTOR_STATUS_ERROR 1 +`define APB4_APU__DESCRIPTOR_STATUS_ABORTED 2 +`define APB4_APU__DESCRIPTOR_STATUS_ERROR_CODE 3 +`define APB4_APU__DESCRIPTOR_STATUS_STAGE 9 +`define APB4_APU__DESCRIPTOR_STATUS_AXI_RESPONSE 13 + `define APB4_APU__PERF_CONTROL_ENABLE 0 `define APB4_APU__PERF_CONTROL_CLEAR 1 `define APB4_APU__PERF_CONTROL_SNAPSHOT 2 diff --git a/rtl/ip/multimedia/apu_dma.sv b/rtl/ip/multimedia/apu_dma.sv new file mode 100644 index 00000000..80ae6a92 --- /dev/null +++ b/rtl/ip/multimedia/apu_dma.sv @@ -0,0 +1,410 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 + +`include "apu_define.svh" +`include "axi4_define.svh" + +module apu_dma ( + // verilog_format: off -- preserve command, result, and bus columns + input logic clk_i, + input logic rst_n_i, + input logic abort_i, + input logic quiesce_i, + input logic [7:0] bridge_epoch_i, + input logic perf_enable_i, + input logic counter_clear_i, + input logic request_valid_i, + output logic request_ready_o, + input logic request_write_i, + input logic [31:0] request_addr_i, + input logic [31:0] request_bytes_i, + input logic [31:0] read_base_i, + input logic [31:0] read_limit_i, + input logic [31:0] write_base_i, + input logic [31:0] write_limit_i, + input logic [31:0] timeout_i, + axi4_stream_if.source read_axis, + axi4_stream_if.sink write_axis, + output logic busy_o, + output logic done_o, + output logic error_o, + output logic aborted_o, + output logic aborting_o, + output logic [5:0] error_code_o, + output logic [3:0] error_stage_o, + output logic [1:0] error_resp_o, + output logic [31:0] error_addr_o, + output logic input_pending_o, + output logic output_pending_o, + output logic [63:0] read_bytes_o, + output logic [63:0] write_bytes_o, + output logic [63:0] read_stalls_o, + output logic [63:0] write_stalls_o, + axi4_if.master axi4 + // verilog_format: on +); + typedef enum logic [2:0] { + Idle, + ReadStart, + ReadData, + WriteStart, + WriteData, + WriteResponse + } state_e; + + state_e s_state_q; + logic [31:0] s_addr_q; + logic [31:0] s_remaining_q; + logic [ 4:0] s_burst_beats_q; + logic [31:0] s_timeout_q; + logic s_drain_q; + logic s_err_q; + logic s_aborted_q; + logic [ 5:0] s_err_code_q; + logic [ 3:0] s_err_stage_q; + logic [ 1:0] s_err_resp_q; + logic [31:0] s_err_addr_q; + logic s_done_q; + logic [63:0] s_read_bytes_q; + logic [63:0] s_write_bytes_q; + logic [63:0] s_read_stalls_q; + logic [63:0] s_write_stalls_q; + logic [ 4:0] s_sel_beats; + logic [31:0] s_beat_bytes; + logic [ 3:0] s_expected_keep; + logic s_read_start_ready; + logic s_read_busy; + logic s_read_valid; + logic s_read_ready; + logic [31:0] s_read_data; + logic [ 1:0] s_read_resp; + logic s_read_last; + logic s_read_expected_last; + logic s_read_id_error; + logic s_read_done; + logic s_write_start_ready; + logic s_write_busy; + logic s_write_ready; + logic s_write_done; + logic [ 1:0] s_write_resp; + logic s_write_id_error; + logic s_progress; + logic s_timeout_expire; + logic s_read_fault; + logic s_write_fault; + logic s_payload_fault; + logic s_timeout_active; + logic s_bridge_flush; + logic s_read_missing_last; + logic s_req_range_valid; + logic [32:0] s_req_last; + logic [ 7:0] s_bridge_epoch_q; + + function automatic logic [4:0] choose_burst(input logic [11:0] addr_i, + input logic [31:0] bytes_i); + logic [31:0] s_needed; + logic [31:0] s_boundary; + logic [31:0] s_sel; + begin + s_needed = (bytes_i + 32'd3) >> 2; + s_boundary = (32'd4096 - {20'd0, addr_i}) >> 2; + s_sel = s_needed; + if (s_sel > 32'd16) s_sel = 32'd16; + if (s_sel > s_boundary) s_sel = s_boundary; + if (s_sel == 32'd0) s_sel = 32'd1; + return s_sel[4:0]; + end + endfunction + + function automatic logic [3:0] byte_mask(input logic [31:0] bytes_i); + case (bytes_i) + 32'd1: byte_mask = 4'b0001; + 32'd2: byte_mask = 4'b0011; + 32'd3: byte_mask = 4'b0111; + default: byte_mask = 4'b1111; + endcase + endfunction + + function automatic logic [63:0] saturating_add(input logic [63:0] value_i, + input logic [31:0] increment_i); + logic [64:0] s_sum; + begin + s_sum = {1'b0, value_i} + {33'd0, increment_i}; + return s_sum[64] ? 64'hffff_ffff_ffff_ffff : s_sum[63:0]; + end + endfunction + + assign s_sel_beats = choose_burst(s_addr_q[11:0], s_remaining_q); + assign s_beat_bytes = (s_remaining_q < 32'd4) ? s_remaining_q : 32'd4; + assign s_expected_keep = byte_mask(s_beat_bytes); + assign s_req_last = {1'b0, request_addr_i} + {1'b0, request_bytes_i} - 1'b1; + assign s_req_range_valid = (request_bytes_i != 32'd0) && !s_req_last[32] && + (request_addr_i[1:0] == 2'd0) && + (request_write_i ? ((request_addr_i >= write_base_i) && + (s_req_last[31:0] <= write_limit_i)) : + ((request_addr_i >= read_base_i) && + (s_req_last[31:0] <= read_limit_i))); + assign request_ready_o = (s_state_q == Idle) && !quiesce_i && !abort_i && !s_bridge_flush; + assign busy_o = s_state_q != Idle; + assign done_o = s_done_q; + assign error_o = s_err_q; + assign aborted_o = s_aborted_q; + assign aborting_o = busy_o && s_drain_q && s_aborted_q; + assign error_code_o = s_err_code_q; + assign error_stage_o = s_err_stage_q; + assign error_resp_o = s_err_resp_q; + assign error_addr_o = s_err_addr_q; + assign input_pending_o = (s_state_q == ReadStart) || (s_state_q == ReadData); + assign output_pending_o = (s_state_q == WriteStart) || (s_state_q == WriteData) || + (s_state_q == WriteResponse); + assign read_bytes_o = s_read_bytes_q; + assign write_bytes_o = s_write_bytes_q; + assign read_stalls_o = s_read_stalls_q; + assign write_stalls_o = s_write_stalls_q; + + assign read_axis.tdata = s_read_data; + assign read_axis.tkeep = s_expected_keep; + assign read_axis.tstrb = s_expected_keep; + assign read_axis.tlast = s_remaining_q <= 32'd4; + assign read_axis.tid = '0; + assign read_axis.tdest = '0; + assign read_axis.tuser = '0; + assign read_axis.tvalid = (s_state_q == ReadData) && s_read_valid && !s_drain_q && !s_read_fault; + assign s_read_ready = (s_state_q == ReadData) && (s_drain_q || s_read_fault || read_axis.tready); + assign s_read_fault = s_read_valid && + ((s_read_resp != `AXI4_RESP_OKAY) || s_read_id_error || + (s_read_last != s_read_expected_last)); + assign s_read_missing_last = (s_state_q == ReadData) && s_read_valid && s_read_ready && + s_read_expected_last && !s_read_last; + + assign s_payload_fault = write_axis.tvalid && + ((write_axis.tkeep != s_expected_keep) || (write_axis.tstrb != s_expected_keep) || + (write_axis.tlast != (s_remaining_q <= 32'd4))); + assign write_axis.tready = (s_state_q == WriteData) && s_write_ready && !s_drain_q && + !s_payload_fault; + assign s_write_fault = s_write_done && ((s_write_resp != `AXI4_RESP_OKAY) || s_write_id_error); + assign s_progress = (axi4.awvalid && axi4.awready) || (axi4.wvalid && axi4.wready) || + (axi4.bvalid && axi4.bready) || (axi4.arvalid && axi4.arready) || + (axi4.rvalid && axi4.rready); + assign s_timeout_active = s_read_busy || s_write_busy; + assign s_timeout_expire = s_timeout_active && !s_progress && (s_timeout_q == 32'd1); + assign s_bridge_flush = bridge_epoch_i != s_bridge_epoch_q; + + dma_axi4_master #( + .AddrWidth (32), + .DataWidth (32), + .MaxBurstBeats(16) + ) u_axi4_master ( + .clk_i(clk_i), + .rst_n_i(rst_n_i), + .clear_i(s_bridge_flush || s_read_missing_last), + .read_start_valid_i((s_state_q == ReadStart) && !s_drain_q && !abort_i && !s_timeout_expire), + .read_start_ready_o(s_read_start_ready), + .read_addr_i(s_addr_q), + .read_beats_i(s_sel_beats), + .read_fixed_i(1'b0), + .read_busy_o(s_read_busy), + .read_beat_valid_o(s_read_valid), + .read_beat_ready_i(s_read_ready), + .read_data_o(s_read_data), + .read_resp_o(s_read_resp), + .read_last_o(s_read_last), + .read_expected_last_o(s_read_expected_last), + .read_id_error_o(s_read_id_error), + .read_done_o(s_read_done), + .write_start_valid_i((s_state_q == WriteStart) && !s_drain_q && !abort_i && + !s_timeout_expire), + .write_start_ready_o(s_write_start_ready), + .write_addr_i(s_addr_q), + .write_beats_i(s_sel_beats), + .write_fixed_i(1'b0), + .write_busy_o(s_write_busy), + .write_data_valid_i ((s_state_q == WriteData) && + (s_drain_q || s_payload_fault || write_axis.tvalid)), + .write_data_ready_o(s_write_ready), + .write_data_i((s_drain_q || s_payload_fault) ? 32'd0 : write_axis.tdata), + .write_strb_i((s_drain_q || s_payload_fault) ? 4'd0 : write_axis.tkeep), + .write_done_o(s_write_done), + .write_resp_o(s_write_resp), + .write_id_error_o(s_write_id_error), + .axi4(axi4) + ); + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + s_state_q <= Idle; + s_addr_q <= 32'd0; + s_remaining_q <= 32'd0; + s_burst_beats_q <= 5'd0; + s_timeout_q <= 32'd0; + s_drain_q <= 1'b0; + s_err_q <= 1'b0; + s_aborted_q <= 1'b0; + s_err_code_q <= 6'd0; + s_err_stage_q <= 4'd0; + s_err_resp_q <= 2'd0; + s_err_addr_q <= 32'd0; + s_done_q <= 1'b0; + s_read_bytes_q <= 64'd0; + s_write_bytes_q <= 64'd0; + s_read_stalls_q <= 64'd0; + s_write_stalls_q <= 64'd0; + s_bridge_epoch_q <= 8'd0; + end else begin + s_done_q <= 1'b0; + s_bridge_epoch_q <= bridge_epoch_i; + if (counter_clear_i) begin + s_read_bytes_q <= 64'd0; + s_write_bytes_q <= 64'd0; + s_read_stalls_q <= 64'd0; + s_write_stalls_q <= 64'd0; + end + if (s_bridge_flush) begin + s_timeout_q <= 32'd0; + s_drain_q <= 1'b0; + if (s_state_q != Idle) begin + s_state_q <= Idle; + s_done_q <= 1'b1; + s_err_q <= 1'b1; + s_aborted_q <= s_aborted_q || abort_i; + s_err_code_q <= output_pending_o ? 6'd16 : 6'd15; + s_err_stage_q <= output_pending_o ? 4'd8 : 4'd3; + s_err_resp_q <= `AXI4_RESP_DECODE_ERROR; + s_err_addr_q <= s_addr_q; + end + end else if (request_valid_i && request_ready_o) begin + s_drain_q <= 1'b0; + s_err_q <= 1'b0; + s_aborted_q <= 1'b0; + s_err_code_q <= 6'd0; + s_err_stage_q <= 4'd0; + s_err_resp_q <= 2'd0; + s_err_addr_q <= 32'd0; + s_addr_q <= request_addr_i; + s_remaining_q <= request_bytes_i; + s_timeout_q <= timeout_i; + if (!s_req_range_valid) begin + s_err_q <= 1'b1; + s_err_code_q <= request_write_i ? 6'd16 : 6'd15; + s_err_stage_q <= request_write_i ? 4'd8 : 4'd3; + s_err_resp_q <= `AXI4_RESP_SLAVE_ERROR; + s_err_addr_q <= request_addr_i; + s_done_q <= 1'b1; + end else begin + s_state_q <= request_write_i ? WriteStart : ReadStart; + end + end else if (s_state_q != Idle) begin + if (s_progress) s_timeout_q <= timeout_i; + else if (s_timeout_active && (s_timeout_q != 32'd0)) s_timeout_q <= s_timeout_q - 1'b1; + if (perf_enable_i && !counter_clear_i && input_pending_o && !s_progress) + s_read_stalls_q <= saturating_add(s_read_stalls_q, 1); + if (perf_enable_i && !counter_clear_i && output_pending_o && !s_progress) + s_write_stalls_q <= saturating_add(s_write_stalls_q, 1); + if ((abort_i || s_timeout_expire) && !s_drain_q) begin + s_drain_q <= 1'b1; + s_timeout_q <= 32'd0; + s_err_q <= s_timeout_expire; + s_aborted_q <= abort_i; + s_err_code_q <= s_timeout_expire ? 6'd17 : 6'd20; + s_err_stage_q <= output_pending_o ? 4'd8 : 4'd3; + s_err_addr_q <= s_addr_q; + end + case (s_state_q) + ReadStart: begin + if (s_drain_q) begin + s_state_q <= Idle; + s_done_q <= 1'b1; + s_drain_q <= 1'b0; + s_timeout_q <= 32'd0; + end else if (s_read_start_ready && !abort_i && !s_timeout_expire) begin + s_state_q <= ReadData; + end + end + ReadData: begin + if (s_read_valid && s_read_ready) begin + if (s_read_fault) begin + s_err_q <= 1'b1; + s_err_code_q <= 6'd15; + s_err_stage_q <= 4'd3; + s_err_resp_q <= s_read_resp; + s_err_addr_q <= s_addr_q; + s_drain_q <= 1'b1; + end else if (perf_enable_i && !s_drain_q && !counter_clear_i) begin + s_read_bytes_q <= saturating_add(s_read_bytes_q, s_beat_bytes); + end + s_addr_q <= s_addr_q + s_beat_bytes; + s_remaining_q <= s_remaining_q - s_beat_bytes; + if (s_read_missing_last) begin + s_state_q <= Idle; + s_done_q <= 1'b1; + s_drain_q <= 1'b0; + s_timeout_q <= 32'd0; + end else if (s_read_last) begin + if (s_drain_q || s_read_fault || (s_remaining_q <= 32'd4)) begin + s_state_q <= Idle; + s_done_q <= 1'b1; + s_drain_q <= 1'b0; + s_timeout_q <= 32'd0; + end else begin + s_state_q <= ReadStart; + end + end + end + end + WriteStart: begin + if (s_drain_q) begin + s_state_q <= Idle; + s_done_q <= 1'b1; + s_drain_q <= 1'b0; + s_timeout_q <= 32'd0; + end else if (s_write_start_ready && !abort_i && !s_timeout_expire) begin + s_burst_beats_q <= s_sel_beats; + s_state_q <= WriteData; + end + end + WriteData: begin + if (s_write_ready && (s_drain_q || s_payload_fault || write_axis.tvalid)) begin + if (s_payload_fault && !s_err_q && !s_drain_q) begin + s_err_q <= 1'b1; + s_err_code_q <= 6'd16; + s_err_stage_q <= 4'd8; + s_err_resp_q <= `AXI4_RESP_SLAVE_ERROR; + s_err_addr_q <= s_addr_q; + s_drain_q <= 1'b1; + end else if (perf_enable_i && !s_drain_q && !counter_clear_i) begin + s_write_bytes_q <= saturating_add(s_write_bytes_q, s_beat_bytes); + end + s_addr_q <= s_addr_q + s_beat_bytes; + s_remaining_q <= s_remaining_q - s_beat_bytes; + s_burst_beats_q <= s_burst_beats_q - 1'b1; + if (s_burst_beats_q == 5'd1) s_state_q <= WriteResponse; + end + end + WriteResponse: begin + if (s_write_done) begin + if (s_write_fault) begin + s_err_q <= 1'b1; + s_err_code_q <= 6'd16; + s_err_stage_q <= 4'd8; + s_err_resp_q <= s_write_resp; + s_err_addr_q <= s_addr_q; + end + if (s_drain_q || s_write_fault || (s_remaining_q == 32'd0)) begin + s_state_q <= Idle; + s_done_q <= 1'b1; + s_drain_q <= 1'b0; + s_timeout_q <= 32'd0; + end else begin + s_state_q <= WriteStart; + end + end + end + default: s_state_q <= Idle; + endcase + end + end + end + + logic s_unused_master_status; + assign s_unused_master_status = s_read_busy ^ s_read_done ^ s_write_busy; +endmodule diff --git a/rtl/ip/multimedia/apu_reg.sv b/rtl/ip/multimedia/apu_reg.sv index 274a3527..59e8bf03 100644 --- a/rtl/ip/multimedia/apu_reg.sv +++ b/rtl/ip/multimedia/apu_reg.sv @@ -11,14 +11,61 @@ module apu_reg ( input logic owner_lock_i, input logic quiesce_i, input logic resource_reset_i, + input logic resource_reset_apply_i, + input logic core_idle_i, + input logic core_busy_i, + input logic core_aborting_i, + input logic [31:0] stream_status_i, + input logic [31:0] job_status_i, + input logic [31:0] job_input_used_i, + input logic [31:0] job_output_bytes_i, + input logic [31:0] job_frames_i, + input logic [31:0] job_source_info_i, + input logic [31:0] job_cycles_i, + input logic [31:0] job_detail_i, + input logic [31:0] ring_status_i, + input logic [7:0] ring_head_i, + input logic [31:0] ring_completed_i, + input logic [10:0] irq_set_i, + input logic fault_valid_i, + input logic [5:0] fault_code_i, + input logic [3:0] fault_stage_i, + input logic [1:0] fault_resp_i, + input logic [7:0] fault_index_i, + input logic [31:0] fault_addr_i, + input logic [31:0] fault_detail_i, + input logic [63:0] perf_active_cycles_i, + input logic [63:0] perf_input_bytes_i, + input logic [63:0] perf_output_bytes_i, + input logic [63:0] perf_dma_read_stalls_i, + input logic [63:0] perf_dma_write_stalls_i, + input logic [63:0] perf_stream_stalls_i, + input logic [63:0] perf_faults_i, apb4_if.slave apb4, + output logic soft_reset_o, + output logic abort_o, + output logic counter_clear_o, + output logic perf_enable_o, + output logic xrun_clear_o, + output logic [3:0] stream_route_o, + output logic [15:0] stream_watermark_o, + output logic [31:0] read_base_o, + output logic [31:0] read_limit_o, + output logic [31:0] write_base_o, + output logic [31:0] write_limit_o, + output logic [31:0] dma_timeout_o, + output logic [31:0] ring_base_o, + output logic [8:0] ring_size_o, + output logic [7:0] ring_tail_o, + output logic [1:0] ring_control_o, + output logic [31:0] ring_coalesce_o, 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] Capability0 = 32'h0000_0018; localparam logic [31:0] Capability1 = 32'd0; localparam logic [31:0] AbiDigest = 32'd0; localparam logic [10:0] IrqMask = 11'h7ff; @@ -41,6 +88,7 @@ module apu_reg ( logic s_write_unsupported; logic [31:0] s_read_data; logic s_soft_reset; + logic s_abort; logic s_cnt_clear; logic [10:0] s_irq_clear; logic [10:0] s_irq_set; @@ -58,8 +106,12 @@ module apu_reg ( 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 [15:0] s_stream_watermark_d, s_stream_watermark_q; logic s_perf_en_d, s_perf_en_q; logic s_perf_snapshot_valid_d, s_perf_snapshot_valid_q; + logic [9:0][63:0] s_perf_snapshot_q; + logic s_err_clear; + logic s_unused_snapshot; 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); @@ -79,17 +131,37 @@ module apu_reg ( 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_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; + 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 = core_idle_i; + assign irq_o = (s_irq_state_q & s_irq_en_q) != 11'd0; + assign soft_reset_o = s_soft_reset; + assign abort_o = s_abort; + assign counter_clear_o = s_cnt_clear; + assign perf_enable_o = s_perf_en_q; + assign xrun_clear_o = s_irq_clear[`APB4_APU__IRQ_STREAM_XRUN]; + assign stream_route_o = s_stream_route_q; + assign stream_watermark_o = s_stream_watermark_q; + assign read_base_o = s_acl_q[0]; + assign read_limit_o = s_acl_q[1]; + assign write_base_o = s_acl_q[2]; + assign write_limit_o = s_acl_q[3]; + assign dma_timeout_o = s_timeout_q[1]; + assign ring_base_o = s_ring_cfg_q[0]; + assign ring_size_o = s_ring_cfg_q[1][8:0]; + assign ring_tail_o = s_ring_cfg_q[2][7:0]; + assign ring_control_o = s_ring_cfg_q[3][1:0]; + assign ring_coalesce_o = s_ring_cfg_q[4]; + assign s_err_clear = s_write && !s_write_err && + (s_offset == `APB4_APU__ERROR_STATUS) && s_strobed_write[0]; + assign s_unused_snapshot = ^s_perf_snapshot_q[3] ^ ^s_perf_snapshot_q[7] ^ ^s_perf_snapshot_q[8]; always_comb begin s_read_data = 32'd0; @@ -100,7 +172,18 @@ module apu_reg ( `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__STATUS: + s_read_data = { + 22'd0, + 1'b0, + !core_busy_i, + core_aborting_i, + quiesce_i, + 2'd0, + ring_status_i[`APB4_APU__RING_STATUS_ACTIVE], + core_busy_i, + 2'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; @@ -108,7 +191,7 @@ module apu_reg ( `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__STREAM_STATUS: s_read_data = stream_status_i; `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]; @@ -120,16 +203,14 @@ module apu_reg ( `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__MC_LOAD_COUNT, `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__STREAM_WATERMARK: s_read_data = {16'd0, s_stream_watermark_q}; `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]; @@ -141,11 +222,21 @@ module apu_reg ( `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__JOB_STATUS: s_read_data = job_status_i; + `APB4_APU__JOB_INPUT_USED: s_read_data = job_input_used_i; + `APB4_APU__JOB_OUTPUT_BYTES: s_read_data = job_output_bytes_i; + `APB4_APU__JOB_FRAMES: s_read_data = job_frames_i; + `APB4_APU__JOB_SOURCE_INFO: s_read_data = job_source_info_i; + `APB4_APU__JOB_CYCLES: s_read_data = job_cycles_i; + `APB4_APU__JOB_DETAIL: s_read_data = job_detail_i; `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__RING_HEAD: s_read_data = {24'd0, ring_head_i}; + `APB4_APU__RING_STATUS: s_read_data = ring_status_i; + `APB4_APU__RING_COMPLETED: s_read_data = ring_completed_i; `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]; @@ -153,17 +244,24 @@ module apu_reg ( `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_ACTIVE_CYCLES_LO: s_read_data = s_perf_snapshot_q[0][31:0]; + `APB4_APU__PERF_ACTIVE_CYCLES_HI: s_read_data = s_perf_snapshot_q[0][63:32]; + `APB4_APU__PERF_INPUT_BYTES_LO: s_read_data = s_perf_snapshot_q[1][31:0]; + `APB4_APU__PERF_INPUT_BYTES_HI: s_read_data = s_perf_snapshot_q[1][63:32]; + `APB4_APU__PERF_OUTPUT_BYTES_LO: s_read_data = s_perf_snapshot_q[2][31:0]; + `APB4_APU__PERF_OUTPUT_BYTES_HI: s_read_data = s_perf_snapshot_q[2][63:32]; + `APB4_APU__PERF_DECODED_FRAMES_LO, `APB4_APU__PERF_DECODED_FRAMES_HI: s_read_data = 32'd0; + `APB4_APU__PERF_DMA_READ_STALLS_LO: s_read_data = s_perf_snapshot_q[4][31:0]; + `APB4_APU__PERF_DMA_READ_STALLS_HI: s_read_data = s_perf_snapshot_q[4][63:32]; + `APB4_APU__PERF_DMA_WRITE_STALLS_LO: s_read_data = s_perf_snapshot_q[5][31:0]; + `APB4_APU__PERF_DMA_WRITE_STALLS_HI: s_read_data = s_perf_snapshot_q[5][63:32]; + `APB4_APU__PERF_STREAM_STALLS_LO: s_read_data = s_perf_snapshot_q[6][31:0]; + `APB4_APU__PERF_STREAM_STALLS_HI: s_read_data = s_perf_snapshot_q[6][63:32]; `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: + `APB4_APU__PERF_KWS_CYCLES_LO, `APB4_APU__PERF_KWS_CYCLES_HI: s_read_data = 32'd0; + `APB4_APU__PERF_FAULTS_LO: s_read_data = s_perf_snapshot_q[9][31:0]; + `APB4_APU__PERF_FAULTS_HI: s_read_data = s_perf_snapshot_q[9][63:32]; `APB4_APU__COMMAND, `APB4_APU__IRQ_TEST, `APB4_APU__RING_DOORBELL: begin s_read_data = 32'd0; s_read_err = 1'b1; @@ -183,9 +281,12 @@ module apu_reg ( 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_unsupported = (apb4.pwdata & 32'h0000_0039) != 32'd0; s_write_err = (apb4.pstrb != 4'hf) || ((apb4.pwdata & 32'hffff_ff80) != 32'd0) || + (apb4.pwdata[`APB4_APU__COMMAND_ABORT] && !core_busy_i) || + ((apb4.pwdata[`APB4_APU__COMMAND_SOFT_RESET] || + apb4.pwdata[`APB4_APU__COMMAND_CLEAR_COUNTERS]) && !core_idle_i) || s_write_unsupported; end `APB4_APU__IRQ_STATE: s_write_err = (s_strobed_write & ~32'(IrqMask)) != 32'd0; @@ -198,66 +299,81 @@ module apu_reg ( `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; + s_write_err = (s_merged_write == 32'd0) || !core_idle_i; 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; + s_write_err = s_write_unsupported || !core_idle_i; 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; + s_write_err = (apb4.pstrb != 4'hf) || !core_idle_i || (owner_i != 2'd0); `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; + s_write_err = (s_merged_write == 32'd0) || !core_idle_i; + end + `APB4_APU__STREAM_WATERMARK: begin + s_merged_write = apply_wstrb({16'd0, s_stream_watermark_q}, apb4.pwdata, apb4.pstrb); + s_write_err = (s_merged_write[31:16] != 16'd0) || + (s_merged_write[7:0] > 8'd64) || (s_merged_write[15:8] > 8'd64) || + !core_idle_i; end `APB4_APU__MC_IMAGE_ADDRESS, `APB4_APU__KWS_MODEL_ADDRESS: - s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata[5:0] != 6'd0); + s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata[5:0] != 6'd0) || + !core_idle_i || (owner_i != 2'd0); `APB4_APU__MC_IMAGE_SIZE, `APB4_APU__KWS_MODEL_SIZE: - s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata == 32'd0); + s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata == 32'd0) || + !core_idle_i || (owner_i != 2'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); + (s_merged_write[7:4] > 4'd2) || (s_merged_write[9:8] > 2'd1) || + !core_idle_i; 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; + s_write_err = (apb4.pstrb != 4'hf) || !core_idle_i; `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; + s_write_err = (s_merged_write[31:26] != 6'd0) || !core_idle_i; 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); + s_write_err = (s_merged_write[31:21] != 11'd0) || + (s_merged_write[20:19] > 2'd1) || !core_idle_i; 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; + s_write_err = (s_merged_write[31:1] != 31'd0) || !core_idle_i; end - `APB4_APU__RING_BASE: s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata[6:0] != 7'd0); + `APB4_APU__RING_BASE: + s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata[6:0] != 7'd0) || !core_idle_i; `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.pwdata > 32'd256) || !is_power_of_two(apb4.pwdata) || !core_idle_i; `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_CONTROL: + s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata[31:2] != 30'd0) || !core_idle_i; `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.pwdata[7:0] == 8'd0) || (apb4.pwdata[31:16] == 16'd0) || + !core_idle_i; `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; + s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata[31:3] != 29'd0) || + s_write_unsupported || !core_idle_i; 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); + s_write_err = (s_merged_write[31:16] != 16'd0) || + (s_merged_write[15:8] == 8'd0) || !core_idle_i; end `APB4_APU__PERF_CONTROL: begin s_merged_write = apply_wstrb({31'd0, s_perf_en_q}, apb4.pwdata, apb4.pstrb); @@ -308,22 +424,25 @@ module apu_reg ( 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_stream_watermark_d = s_stream_watermark_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_abort = 1'b0; s_cnt_clear = 1'b0; s_irq_clear = 11'd0; - s_irq_set = 11'd0; + s_irq_set = irq_set_i; if (s_write && !s_write_err) begin unique case (s_offset) `APB4_APU__COMMAND: begin + s_abort = apb4.pwdata[`APB4_APU__COMMAND_ABORT]; 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__IRQ_TEST: s_irq_set = s_irq_set | apb4.pwdata[10:0]; `APB4_APU__ERROR_STATUS: begin if (s_strobed_write[0]) begin s_err_stat_d = 32'd0; @@ -338,6 +457,7 @@ module apu_reg ( `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__STREAM_WATERMARK: s_stream_watermark_d = s_merged_write[15:0]; `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; @@ -388,9 +508,17 @@ module apu_reg ( s_err_detail_d = apb4.pwrite ? apb4.pwdata : {20'd0, s_offset}; end end + if (fault_valid_i && (!s_err_stat_q[`APB4_APU__ERROR_VALID] || s_err_clear)) begin + s_err_stat_d = 32'd1 | (32'(fault_code_i) << `APB4_APU__ERROR_CODE) | + (32'(fault_stage_i) << `APB4_APU__ERROR_STAGE) | + (32'(fault_resp_i) << `APB4_APU__ERROR_AXI_RESPONSE) | + (32'(fault_index_i) << `APB4_APU__ERROR_DESCRIPTOR_INDEX); + s_err_addr_d = fault_addr_i; + s_err_detail_d = fault_detail_i; + end s_irq_state_d = (s_irq_state_d & ~s_irq_clear) | s_irq_set; - if (s_soft_reset || resource_reset_i) begin + if (s_soft_reset || resource_reset_apply_i) begin s_irq_state_d = 11'd0; s_irq_en_d = 11'd0; s_err_stat_d = 32'd0; @@ -404,6 +532,7 @@ module apu_reg ( s_ring_cfg_d = RingReset; s_kws_model_cfg_d = '0; s_kws_cfg_d = KwsConfigReset[15:0]; + s_stream_watermark_d = 16'd0; s_perf_en_d = 1'b0; s_perf_snapshot_valid_d = 1'b0; end @@ -542,6 +671,14 @@ module apu_reg ( .dat_i (s_kws_cfg_d), .dat_o (s_kws_cfg_q) ); + dffr #( + .DATA_WIDTH(16) + ) u_stream_watermark_dffr ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_stream_watermark_d), + .dat_o (s_stream_watermark_q) + ); dffr #( .DATA_WIDTH(1) ) u_perf_enable_dffr ( @@ -558,4 +695,22 @@ module apu_reg ( .dat_i (s_perf_snapshot_valid_d), .dat_o (s_perf_snapshot_valid_q) ); + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i || s_soft_reset || resource_reset_apply_i || s_cnt_clear) begin + s_perf_snapshot_q <= '0; + end else if (s_write && !s_write_err && (s_offset == `APB4_APU__PERF_CONTROL) && + s_merged_write[`APB4_APU__PERF_CONTROL_SNAPSHOT]) begin + s_perf_snapshot_q[0] <= perf_active_cycles_i; + s_perf_snapshot_q[1] <= perf_input_bytes_i; + s_perf_snapshot_q[2] <= perf_output_bytes_i; + s_perf_snapshot_q[3] <= 64'd0; + s_perf_snapshot_q[4] <= perf_dma_read_stalls_i; + s_perf_snapshot_q[5] <= perf_dma_write_stalls_i; + s_perf_snapshot_q[6] <= perf_stream_stalls_i; + s_perf_snapshot_q[7] <= 64'd0; + s_perf_snapshot_q[8] <= 64'd0; + s_perf_snapshot_q[9] <= perf_faults_i; + end + end endmodule diff --git a/rtl/ip/multimedia/apu_ring_scheduler.sv b/rtl/ip/multimedia/apu_ring_scheduler.sv new file mode 100644 index 00000000..88884962 --- /dev/null +++ b/rtl/ip/multimedia/apu_ring_scheduler.sv @@ -0,0 +1,731 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 + +`include "apu_define.svh" + +module apu_ring_scheduler ( + // verilog_format: off -- preserve ring, backend, and DMA service columns + input logic clk_i, + input logic rst_n_i, + input logic soft_reset_i, + input logic counter_clear_i, + input logic abort_i, + input logic quiesce_i, + input logic start_i, + input logic [31:0] ring_base_i, + input logic [8:0] ring_size_i, + input logic [7:0] ring_tail_i, + input logic ring_enable_i, + input logic stop_on_error_i, + input logic [7:0] coalesce_count_i, + input logic [15:0] coalesce_timeout_i, + output logic dma_request_valid_o, + input logic dma_request_ready_i, + output logic dma_request_write_o, + output logic [31:0] dma_request_addr_o, + output logic [31:0] dma_request_bytes_o, + axi4_stream_if.sink dma_read_axis, + axi4_stream_if.source dma_write_axis, + input logic dma_done_i, + input logic dma_error_i, + input logic [5:0] dma_error_code_i, + input logic [3:0] dma_error_stage_i, + input logic [1:0] dma_error_resp_i, + input logic [31:0] dma_error_addr_i, + output logic backend_job_valid_o, + input logic backend_job_ready_i, + output logic [1023:0] backend_descriptor_o, + output logic [7:0] backend_index_o, + input logic backend_result_valid_i, + output logic backend_result_ready_o, + input logic backend_result_error_i, + input logic [5:0] backend_result_code_i, + input logic [3:0] backend_result_stage_i, + input logic [1:0] backend_result_resp_i, + input logic [31:0] backend_input_used_i, + input logic [31:0] backend_output_bytes_i, + input logic [31:0] backend_frames_i, + input logic [31:0] backend_source_info_i, + input logic [31:0] backend_cycles_i, + input logic [31:0] backend_detail_i, + output logic [31:0] job_status_o, + output logic [31:0] ring_status_o, + output logic [7:0] ring_head_o, + output logic [31:0] ring_completed_o, + output logic ring_event_o, + output logic error_event_o, + output logic [5:0] error_code_o, + output logic [3:0] error_stage_o, + output logic [1:0] error_resp_o, + output logic [7:0] error_index_o, + output logic [31:0] error_addr_o, + output logic abort_done_o, + output logic aborting_o, + output logic idle_o + // verilog_format: on +); + typedef enum logic [3:0] { + Idle, + FetchRequest, + FetchData, + Evaluate, + BackendRequest, + BackendWait, + ResultWriteRequest, + ResultWriteData, + ResultWriteWait, + OwnerWriteRequest, + OwnerWriteData, + OwnerWriteWait, + Stalled + } state_e; + + state_e s_state_q; + logic [31:0] s_descriptor_q [32]; + logic [ 5:0] s_word_index_q; + logic [ 7:0] s_head_q; + logic [31:0] s_completed_q; + logic s_active_q; + logic s_ring_err_q; + logic s_wrapped_q; + logic s_stopped_q; + logic [ 7:0] s_pending_q; + logic [15:0] s_coalesce_timer_q; + logic [ 7:0] s_last_index_q; + logic s_job_err_q; + logic s_job_busy_q; + logic s_job_done_q; + logic s_job_aborted_q; + logic [ 5:0] s_job_code_q; + logic [ 3:0] s_job_stage_q; + logic [ 1:0] s_job_resp_q; + logic s_ring_event_q; + logic s_err_event_q; + logic s_abort_done_q; + logic [ 5:0] s_err_code_q; + logic [ 3:0] s_err_stage_q; + logic [ 1:0] s_err_resp_q; + logic [ 7:0] s_err_index_q; + logic [31:0] s_err_addr_q; + logic s_descriptor_valid; + logic s_descriptor_ioc; + logic [31:0] s_descriptor_addr; + logic [32:0] s_descriptor_addr_ext; + logic [32:0] s_ring_last; + logic s_ring_config_valid; + logic s_writeback_pending; + logic [ 7:0] s_next_head; + logic [ 8:0] s_incremented_head; + logic [ 7:0] s_post_pending; + logic s_abort_pending_q; + logic s_ring_en_q; + logic [63:0] s_timestamp_q; + + function automatic logic [31:0] saturating_increment(input logic [31:0] value_i); + return (&value_i) ? value_i : value_i + 1'b1; + endfunction + + function automatic logic is_power_of_two(input logic [8:0] value_i); + return (value_i != 9'd0) && ((value_i & (value_i - 1'b1)) == 9'd0); + endfunction + + always_comb begin + s_descriptor_valid = s_descriptor_q[0][`APB4_APU__DESCRIPTOR_CONTROL_OWN] && + (s_descriptor_q[0][29:12] == 18'd0) && + (s_descriptor_q[0][3:0] <= 4'd1) && (s_descriptor_q[0][7:4] <= 4'd2) && + (s_descriptor_q[0][9:8] <= 2'd1) && + (s_descriptor_q[2][1:0] == 2'd0) && (s_descriptor_q[3] != 32'd0) && + (((s_descriptor_q[0][9:8] == 2'd0) && (s_descriptor_q[4][1:0] == 2'd0) && + (s_descriptor_q[5] != 32'd0)) || + ((s_descriptor_q[0][9:8] == 2'd1) && (s_descriptor_q[4] == 32'd0) && + (s_descriptor_q[5] == 32'd0))) && + (s_descriptor_q[6][31:26] == 6'd0) && (s_descriptor_q[6][19] == 1'b0) && + (s_descriptor_q[6][18:17] <= 2'd2) && + ((s_descriptor_q[6][25:20] == 6'd0) || (s_descriptor_q[6][25:20] == 6'd16) || + (s_descriptor_q[6][25:20] == 6'd24)) && + (s_descriptor_q[7][31:21] == 11'd0) && (s_descriptor_q[7][18:17] <= 2'd2) && + (s_descriptor_q[7][20:19] <= 2'd1) && (s_descriptor_q[8][31:1] == 31'd0) && + (((s_descriptor_q[0][3:0] == 4'd0) && (s_descriptor_q[9] == 32'd0)) || + (s_descriptor_q[0][3:0] == 4'd1)); + for (int word_index = 24; word_index < 32; word_index++) begin + s_descriptor_valid = s_descriptor_valid && (s_descriptor_q[word_index] == 32'd0); + end + end + + assign s_descriptor_ioc = s_descriptor_q[0][`APB4_APU__DESCRIPTOR_CONTROL_IOC]; + assign s_descriptor_addr_ext = {1'b0, ring_base_i} + ({25'd0, s_head_q} << 7); + assign s_descriptor_addr = s_descriptor_addr_ext[31:0]; + assign s_ring_last = {1'b0, ring_base_i} + ({24'd0, ring_size_i} << 7) - 1'b1; + assign s_ring_config_valid = (ring_base_i[6:0] == 7'd0) && + (ring_size_i >= 9'd2) && (ring_size_i <= 9'd256) && is_power_of_two( + ring_size_i + ) && (s_ring_last <= 33'h0_ffff_ffff) && (s_descriptor_addr_ext <= 33'h0_ffff_ffff) && + ({1'b0, s_head_q} < ring_size_i) && ({1'b0, ring_tail_i} < ring_size_i); + assign s_incremented_head = {1'b0, s_head_q} + 1'b1; + assign s_next_head = (s_incremented_head >= ring_size_i) ? 8'd0 : s_incremented_head[7:0]; + assign s_post_pending = (&s_pending_q) ? s_pending_q : s_pending_q + 1'b1; + assign s_writeback_pending = (s_state_q == ResultWriteRequest) || + (s_state_q == ResultWriteData) || (s_state_q == ResultWriteWait) || + (s_state_q == OwnerWriteRequest) || (s_state_q == OwnerWriteData) || + (s_state_q == OwnerWriteWait); + assign dma_request_valid_o = (s_state_q == FetchRequest) || + (s_state_q == ResultWriteRequest) || (s_state_q == OwnerWriteRequest); + assign dma_request_write_o = s_state_q != FetchRequest; + assign dma_request_addr_o = ((s_state_q == OwnerWriteRequest) || + (s_state_q == OwnerWriteData) || + (s_state_q == OwnerWriteWait)) ? + s_descriptor_addr : + (((s_state_q == ResultWriteRequest) || (s_state_q == ResultWriteData) || + (s_state_q == ResultWriteWait)) ? s_descriptor_addr + 32'd4 : s_descriptor_addr); + assign dma_request_bytes_o = ((s_state_q == OwnerWriteRequest) || + (s_state_q == OwnerWriteData) || + (s_state_q == OwnerWriteWait)) ? 32'd4 : + (((s_state_q == ResultWriteRequest) || (s_state_q == ResultWriteData) || + (s_state_q == ResultWriteWait)) ? 32'd124 : 32'd128); + assign dma_read_axis.tready = s_state_q == FetchData; + assign dma_write_axis.tdata = (s_state_q == OwnerWriteData) ? + s_descriptor_q[0] : s_descriptor_q[s_word_index_q[4:0]]; + assign dma_write_axis.tkeep = 4'hf; + assign dma_write_axis.tstrb = 4'hf; + assign dma_write_axis.tlast = (s_state_q == OwnerWriteData) || (s_word_index_q == 6'd31); + assign dma_write_axis.tid = '0; + assign dma_write_axis.tdest = '0; + assign dma_write_axis.tuser = '0; + assign dma_write_axis.tvalid = (s_state_q == ResultWriteData) || (s_state_q == OwnerWriteData); + assign backend_job_valid_o = (s_state_q == BackendRequest) && !abort_i; + assign backend_descriptor_o = { + s_descriptor_q[31], + s_descriptor_q[30], + s_descriptor_q[29], + s_descriptor_q[28], + s_descriptor_q[27], + s_descriptor_q[26], + s_descriptor_q[25], + s_descriptor_q[24], + s_descriptor_q[23], + s_descriptor_q[22], + s_descriptor_q[21], + s_descriptor_q[20], + s_descriptor_q[19], + s_descriptor_q[18], + s_descriptor_q[17], + s_descriptor_q[16], + s_descriptor_q[15], + s_descriptor_q[14], + s_descriptor_q[13], + s_descriptor_q[12], + s_descriptor_q[11], + s_descriptor_q[10], + s_descriptor_q[9], + s_descriptor_q[8], + s_descriptor_q[7], + s_descriptor_q[6], + s_descriptor_q[5], + s_descriptor_q[4], + s_descriptor_q[3], + s_descriptor_q[2], + s_descriptor_q[1], + s_descriptor_q[0] + }; + assign backend_index_o = s_head_q; + assign backend_result_ready_o = s_state_q == BackendWait; + assign ring_head_o = s_head_q; + assign ring_completed_o = s_completed_q; + assign ring_event_o = s_ring_event_q; + assign error_event_o = s_err_event_q; + assign abort_done_o = s_abort_done_q; + assign aborting_o = s_abort_pending_q || (s_job_busy_q && s_job_aborted_q); + assign error_code_o = s_err_code_q; + assign error_stage_o = s_err_stage_q; + assign error_resp_o = s_err_resp_q; + assign error_index_o = s_err_index_q; + assign error_addr_o = s_err_addr_q; + assign idle_o = s_state_q == Idle; + assign job_status_o = { + 12'd0, + s_job_resp_q, + s_job_stage_q, + s_job_code_q, + s_writeback_pending, + 1'b0, + (s_state_q == FetchRequest) || (s_state_q == FetchData), + 1'b0, + s_job_aborted_q, + s_job_err_q, + s_job_done_q, + s_job_busy_q + }; + assign ring_status_o = { + 8'd0, + s_last_index_q, + s_pending_q, + s_stopped_q, + s_writeback_pending, + s_pending_q != 8'd0, + s_wrapped_q, + s_ring_err_q, + (s_head_q == ring_tail_i) && (s_state_q == Idle), + s_state_q == Stalled, + s_active_q + }; + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + s_timestamp_q <= 64'd0; + end else begin + s_timestamp_q <= s_timestamp_q + 1'b1; + end + end + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i || soft_reset_i) begin + s_state_q <= Idle; + s_word_index_q <= 6'd0; + s_head_q <= 8'd0; + s_completed_q <= 32'd0; + s_active_q <= 1'b0; + s_ring_err_q <= 1'b0; + s_wrapped_q <= 1'b0; + s_stopped_q <= 1'b0; + s_pending_q <= 8'd0; + s_coalesce_timer_q <= 16'd0; + s_last_index_q <= 8'd0; + s_job_busy_q <= 1'b0; + s_job_done_q <= 1'b0; + s_job_err_q <= 1'b0; + s_job_aborted_q <= 1'b0; + s_job_code_q <= 6'd0; + s_job_stage_q <= 4'd0; + s_job_resp_q <= 2'd0; + s_ring_event_q <= 1'b0; + s_err_event_q <= 1'b0; + s_abort_done_q <= 1'b0; + s_err_code_q <= 6'd0; + s_err_stage_q <= 4'd0; + s_err_resp_q <= 2'd0; + s_err_index_q <= 8'd0; + s_err_addr_q <= 32'd0; + s_abort_pending_q <= 1'b0; + s_ring_en_q <= 1'b0; + end else begin + s_ring_event_q <= 1'b0; + s_err_event_q <= 1'b0; + s_abort_done_q <= 1'b0; + s_ring_en_q <= ring_enable_i; + + if (counter_clear_i) s_completed_q <= 32'd0; + if (ring_enable_i && !s_ring_en_q) begin + s_ring_err_q <= 1'b0; + s_wrapped_q <= 1'b0; + s_stopped_q <= 1'b0; + s_pending_q <= 8'd0; + s_coalesce_timer_q <= 16'd0; + s_last_index_q <= 8'd0; + end + if (!ring_enable_i) begin + s_active_q <= 1'b0; + s_pending_q <= 8'd0; + s_coalesce_timer_q <= 16'd0; + if (s_state_q == Stalled) s_state_q <= Idle; + end + if (start_i && ring_enable_i && !quiesce_i && !abort_i && !s_active_q) begin + s_wrapped_q <= 1'b0; + s_stopped_q <= 1'b0; + s_pending_q <= 8'd0; + s_coalesce_timer_q <= 16'd0; + s_last_index_q <= 8'd0; + s_state_q <= Idle; + if (s_ring_config_valid) begin + s_active_q <= 1'b1; + s_ring_err_q <= 1'b0; + end else begin + s_active_q <= 1'b0; + s_ring_err_q <= 1'b1; + s_err_event_q <= 1'b1; + s_err_code_q <= 6'd2; + s_err_stage_q <= 4'd2; + s_err_resp_q <= 2'd0; + s_err_index_q <= 8'd0; + s_err_addr_q <= ring_base_i; + s_job_busy_q <= 1'b0; + s_job_done_q <= 1'b0; + s_job_err_q <= 1'b1; + s_job_code_q <= 6'd2; + s_job_stage_q <= 4'd2; + s_job_resp_q <= 2'd0; + end + end + + if (abort_i && (s_active_q || (s_state_q != Idle))) begin + s_abort_pending_q <= 1'b1; + s_pending_q <= 8'd0; + s_coalesce_timer_q <= 16'd0; + s_active_q <= 1'b0; + end + + case (s_state_q) + Idle: begin + if (abort_i && s_active_q) begin + s_job_busy_q <= 1'b0; + s_abort_pending_q <= 1'b0; + s_abort_done_q <= 1'b1; + end else if (s_active_q && (s_head_q != ring_tail_i) && !quiesce_i) begin + s_word_index_q <= 6'd0; + s_state_q <= FetchRequest; + end + end + FetchRequest: begin + if (abort_i) begin + s_state_q <= Idle; + s_job_busy_q <= 1'b0; + s_abort_pending_q <= 1'b0; + s_abort_done_q <= 1'b1; + end else if (quiesce_i) begin + s_state_q <= Idle; + s_job_busy_q <= 1'b0; + end else if (dma_request_ready_i) begin + s_state_q <= FetchData; + end + end + FetchData: begin + if (dma_read_axis.tvalid && dma_read_axis.tready) begin + s_descriptor_q[s_word_index_q[4:0]] <= dma_read_axis.tdata; + s_word_index_q <= s_word_index_q + 1'b1; + end + if (dma_done_i) begin + if (dma_error_i) begin + s_ring_err_q <= 1'b1; + s_err_event_q <= 1'b1; + s_err_code_q <= dma_error_code_i; + s_err_stage_q <= dma_error_stage_i; + s_err_resp_q <= dma_error_resp_i; + s_err_index_q <= s_head_q; + s_err_addr_q <= dma_error_addr_i; + s_active_q <= 1'b0; + s_job_busy_q <= 1'b0; + s_job_done_q <= 1'b0; + s_job_err_q <= 1'b1; + s_job_code_q <= dma_error_code_i; + s_job_stage_q <= dma_error_stage_i; + s_job_resp_q <= dma_error_resp_i; + if (abort_i || s_abort_pending_q) s_abort_done_q <= 1'b1; + s_abort_pending_q <= 1'b0; + s_state_q <= Idle; + end else if (abort_i || s_abort_pending_q) begin + s_active_q <= 1'b0; + s_job_busy_q <= 1'b0; + s_abort_pending_q <= 1'b0; + s_abort_done_q <= 1'b1; + s_state_q <= Idle; + end else begin + s_state_q <= Evaluate; + end + end + end + Evaluate: begin + if (abort_i) begin + if (s_descriptor_q[0][`APB4_APU__DESCRIPTOR_CONTROL_OWN]) begin + s_job_busy_q <= 1'b1; + s_job_done_q <= 1'b0; + s_job_err_q <= 1'b0; + s_job_aborted_q <= 1'b1; + s_job_code_q <= 6'd20; + s_job_stage_q <= 4'd11; + s_job_resp_q <= 2'd0; + s_descriptor_q[0][`APB4_APU__DESCRIPTOR_CONTROL_OWN] <= 1'b0; + s_descriptor_q[1] <= 32'd1 << `APB4_APU__DESCRIPTOR_STATUS_ABORTED; + for (int result_word = 10; result_word < 16; result_word++) begin + s_descriptor_q[result_word] <= 32'd0; + end + s_descriptor_q[18] <= s_timestamp_q[31:0]; + s_descriptor_q[19] <= s_timestamp_q[63:32]; + s_descriptor_q[20] <= s_timestamp_q[31:0]; + s_descriptor_q[21] <= s_timestamp_q[63:32]; + s_descriptor_q[22] <= 32'd0; + s_descriptor_q[23] <= 32'd0; + s_word_index_q <= 6'd1; + s_state_q <= ResultWriteRequest; + end else begin + s_job_busy_q <= 1'b0; + s_abort_pending_q <= 1'b0; + s_abort_done_q <= 1'b1; + s_state_q <= Idle; + end + end else if (!s_descriptor_q[0][`APB4_APU__DESCRIPTOR_CONTROL_OWN]) begin + s_state_q <= Stalled; + end else if (!s_descriptor_valid) begin + s_job_busy_q <= 1'b1; + s_job_done_q <= 1'b0; + s_job_err_q <= 1'b1; + s_job_aborted_q <= 1'b0; + s_job_code_q <= 6'd2; + s_job_stage_q <= 4'd2; + s_job_resp_q <= 2'd0; + s_descriptor_q[0][`APB4_APU__DESCRIPTOR_CONTROL_OWN] <= 1'b0; + s_descriptor_q[1] <= (32'd1 << `APB4_APU__DESCRIPTOR_STATUS_ERROR) | + (32'd2 << `APB4_APU__DESCRIPTOR_STATUS_ERROR_CODE) | + (32'd2 << `APB4_APU__DESCRIPTOR_STATUS_STAGE); + for (int result_word = 10; result_word < 16; result_word++) begin + s_descriptor_q[result_word] <= 32'd0; + end + s_descriptor_q[18] <= s_timestamp_q[31:0]; + s_descriptor_q[19] <= s_timestamp_q[63:32]; + s_descriptor_q[20] <= s_timestamp_q[31:0]; + s_descriptor_q[21] <= s_timestamp_q[63:32]; + s_descriptor_q[22] <= 32'd0; + s_descriptor_q[23] <= 32'd0; + s_word_index_q <= 6'd1; + s_state_q <= ResultWriteRequest; + end else begin + s_job_busy_q <= 1'b1; + s_job_done_q <= 1'b0; + s_job_err_q <= 1'b0; + s_job_aborted_q <= 1'b0; + s_job_code_q <= 6'd0; + s_job_stage_q <= 4'd0; + s_job_resp_q <= 2'd0; + for (int result_word = 10; result_word < 16; result_word++) begin + s_descriptor_q[result_word] <= 32'd0; + end + s_descriptor_q[18] <= s_timestamp_q[31:0]; + s_descriptor_q[19] <= s_timestamp_q[63:32]; + s_descriptor_q[20] <= 32'd0; + s_descriptor_q[21] <= 32'd0; + s_descriptor_q[22] <= 32'd0; + s_descriptor_q[23] <= 32'd0; + s_state_q <= BackendRequest; + end + end + BackendRequest: begin + if (abort_i) begin + s_job_busy_q <= 1'b1; + s_job_done_q <= 1'b0; + s_job_err_q <= 1'b0; + s_job_aborted_q <= 1'b1; + s_job_code_q <= 6'd20; + s_job_stage_q <= 4'd11; + s_job_resp_q <= 2'd0; + s_descriptor_q[0][`APB4_APU__DESCRIPTOR_CONTROL_OWN] <= 1'b0; + s_descriptor_q[1] <= 32'd1 << `APB4_APU__DESCRIPTOR_STATUS_ABORTED; + s_descriptor_q[20] <= s_timestamp_q[31:0]; + s_descriptor_q[21] <= s_timestamp_q[63:32]; + s_descriptor_q[22] <= 32'd0; + s_descriptor_q[23] <= 32'd0; + s_word_index_q <= 6'd1; + s_state_q <= ResultWriteRequest; + end else if (backend_job_ready_i) begin + s_state_q <= BackendWait; + end + end + BackendWait: begin + if (backend_result_valid_i) begin + s_job_err_q <= backend_result_error_i; + s_job_code_q <= backend_result_code_i; + s_job_stage_q <= backend_result_stage_i; + s_job_resp_q <= backend_result_resp_i; + s_job_aborted_q <= abort_i && backend_result_error_i; + s_descriptor_q[0][`APB4_APU__DESCRIPTOR_CONTROL_OWN] <= 1'b0; + s_descriptor_q[1] <= (backend_result_error_i ? + (32'd1 << `APB4_APU__DESCRIPTOR_STATUS_ERROR) : + (32'd1 << `APB4_APU__DESCRIPTOR_STATUS_DONE)) | + ((abort_i && backend_result_error_i) ? + (32'd1 << `APB4_APU__DESCRIPTOR_STATUS_ABORTED) : 32'd0) | + (32'(backend_result_code_i) << `APB4_APU__DESCRIPTOR_STATUS_ERROR_CODE) | + (32'(backend_result_stage_i) << `APB4_APU__DESCRIPTOR_STATUS_STAGE) | + (32'(backend_result_resp_i) << `APB4_APU__DESCRIPTOR_STATUS_AXI_RESPONSE); + s_descriptor_q[10] <= backend_input_used_i; + s_descriptor_q[11] <= backend_output_bytes_i; + s_descriptor_q[12] <= backend_frames_i; + s_descriptor_q[13] <= backend_source_info_i; + s_descriptor_q[14] <= backend_cycles_i; + s_descriptor_q[15] <= backend_detail_i; + s_descriptor_q[20] <= s_timestamp_q[31:0]; + s_descriptor_q[21] <= s_timestamp_q[63:32]; + s_word_index_q <= 6'd1; + s_state_q <= ResultWriteRequest; + end else if (abort_i) begin + s_job_busy_q <= 1'b1; + s_job_done_q <= 1'b0; + s_job_err_q <= 1'b0; + s_job_aborted_q <= 1'b1; + s_job_code_q <= 6'd20; + s_job_stage_q <= 4'd11; + s_job_resp_q <= 2'd0; + s_descriptor_q[0][`APB4_APU__DESCRIPTOR_CONTROL_OWN] <= 1'b0; + s_descriptor_q[1] <= 32'd1 << `APB4_APU__DESCRIPTOR_STATUS_ABORTED; + s_descriptor_q[20] <= s_timestamp_q[31:0]; + s_descriptor_q[21] <= s_timestamp_q[63:32]; + s_descriptor_q[22] <= 32'd0; + s_descriptor_q[23] <= 32'd0; + s_word_index_q <= 6'd1; + s_state_q <= ResultWriteRequest; + end + end + ResultWriteRequest: begin + if (dma_request_ready_i) s_state_q <= ResultWriteData; + end + ResultWriteData: begin + if (dma_done_i) begin + s_ring_err_q <= 1'b1; + s_err_event_q <= 1'b1; + s_err_code_q <= dma_error_code_i; + s_err_stage_q <= dma_error_stage_i; + s_err_resp_q <= dma_error_resp_i; + s_err_index_q <= s_head_q; + s_err_addr_q <= dma_error_addr_i; + s_active_q <= 1'b0; + s_job_busy_q <= 1'b0; + s_job_done_q <= 1'b0; + s_job_err_q <= 1'b1; + s_job_code_q <= dma_error_code_i; + s_job_stage_q <= dma_error_stage_i; + s_job_resp_q <= dma_error_resp_i; + if (abort_i || s_abort_pending_q) s_abort_done_q <= 1'b1; + s_abort_pending_q <= 1'b0; + s_state_q <= Idle; + end else if (dma_write_axis.tvalid && dma_write_axis.tready) begin + s_word_index_q <= s_word_index_q + 1'b1; + if (s_word_index_q == 6'd31) s_state_q <= ResultWriteWait; + end + end + ResultWriteWait: begin + if (dma_done_i) begin + if (dma_error_i) begin + s_ring_err_q <= 1'b1; + s_err_event_q <= 1'b1; + s_err_code_q <= dma_error_code_i; + s_err_stage_q <= dma_error_stage_i; + s_err_resp_q <= dma_error_resp_i; + s_err_index_q <= s_head_q; + s_err_addr_q <= dma_error_addr_i; + s_active_q <= 1'b0; + s_job_busy_q <= 1'b0; + s_job_done_q <= 1'b0; + s_job_err_q <= 1'b1; + s_job_code_q <= dma_error_code_i; + s_job_stage_q <= dma_error_stage_i; + s_job_resp_q <= dma_error_resp_i; + if (abort_i || s_abort_pending_q) s_abort_done_q <= 1'b1; + s_abort_pending_q <= 1'b0; + s_state_q <= Idle; + end else begin + s_state_q <= OwnerWriteRequest; + end + end + end + OwnerWriteRequest: begin + if (dma_request_ready_i) s_state_q <= OwnerWriteData; + end + OwnerWriteData: begin + if (dma_done_i) begin + s_ring_err_q <= 1'b1; + s_err_event_q <= 1'b1; + s_err_code_q <= dma_error_code_i; + s_err_stage_q <= dma_error_stage_i; + s_err_resp_q <= dma_error_resp_i; + s_err_index_q <= s_head_q; + s_err_addr_q <= dma_error_addr_i; + s_active_q <= 1'b0; + s_job_busy_q <= 1'b0; + s_job_done_q <= 1'b0; + s_job_err_q <= 1'b1; + s_job_code_q <= dma_error_code_i; + s_job_stage_q <= dma_error_stage_i; + s_job_resp_q <= dma_error_resp_i; + if (abort_i || s_abort_pending_q) s_abort_done_q <= 1'b1; + s_abort_pending_q <= 1'b0; + s_state_q <= Idle; + end else if (dma_write_axis.tvalid && dma_write_axis.tready) begin + s_state_q <= OwnerWriteWait; + end + end + OwnerWriteWait: begin + if (dma_done_i) begin + if (dma_error_i) begin + s_ring_err_q <= 1'b1; + s_err_event_q <= 1'b1; + s_err_code_q <= dma_error_code_i; + s_err_stage_q <= dma_error_stage_i; + s_err_resp_q <= dma_error_resp_i; + s_err_index_q <= s_head_q; + s_err_addr_q <= dma_error_addr_i; + s_active_q <= 1'b0; + s_job_busy_q <= 1'b0; + s_job_done_q <= 1'b0; + s_job_err_q <= 1'b1; + s_job_code_q <= dma_error_code_i; + s_job_stage_q <= dma_error_stage_i; + s_job_resp_q <= dma_error_resp_i; + end else begin + s_job_busy_q <= 1'b0; + s_job_done_q <= !s_job_err_q && !s_job_aborted_q; + if (!counter_clear_i) s_completed_q <= saturating_increment(s_completed_q); + s_last_index_q <= s_head_q; + s_head_q <= s_next_head; + if (s_next_head == 8'd0) s_wrapped_q <= 1'b1; + if (abort_i || s_abort_pending_q || s_job_aborted_q) begin + s_pending_q <= 8'd0; + s_coalesce_timer_q <= 16'd0; + end else if (s_descriptor_ioc || (s_post_pending >= coalesce_count_i)) begin + s_ring_event_q <= 1'b1; + s_pending_q <= 8'd0; + s_coalesce_timer_q <= 16'd0; + end else begin + s_pending_q <= s_post_pending; + if (s_pending_q == 8'd0) s_coalesce_timer_q <= coalesce_timeout_i; + end + if (s_job_err_q) begin + s_ring_err_q <= 1'b1; + s_err_event_q <= 1'b1; + s_err_code_q <= s_job_code_q; + s_err_stage_q <= s_job_stage_q; + s_err_resp_q <= s_job_resp_q; + s_err_index_q <= s_head_q; + s_err_addr_q <= s_descriptor_addr; + if (stop_on_error_i) begin + s_stopped_q <= 1'b1; + s_active_q <= 1'b0; + s_pending_q <= 8'd0; + s_coalesce_timer_q <= 16'd0; + end + end + end + if (abort_i || s_abort_pending_q || s_job_aborted_q) begin + s_abort_pending_q <= 1'b0; + s_abort_done_q <= 1'b1; + end + s_state_q <= Idle; + end + end + Stalled: begin + if (abort_i) begin + s_state_q <= Idle; + s_job_busy_q <= 1'b0; + s_abort_pending_q <= 1'b0; + s_abort_done_q <= 1'b1; + end else if (quiesce_i) begin + s_state_q <= Idle; + s_job_busy_q <= 1'b0; + end else if ((s_head_q == ring_tail_i) || !s_active_q) begin + s_state_q <= Idle; + end else if (start_i && !quiesce_i) begin + s_word_index_q <= 6'd0; + s_state_q <= FetchRequest; + end + end + default: s_state_q <= Idle; + endcase + + if ((s_pending_q != 8'd0) && (s_coalesce_timer_q != 16'd0) && + !((s_state_q == OwnerWriteWait) && dma_done_i) && !abort_i && ring_enable_i) begin + if (s_coalesce_timer_q == 16'd1) begin + s_ring_event_q <= 1'b1; + s_pending_q <= 8'd0; + s_coalesce_timer_q <= 16'd0; + end else begin + s_coalesce_timer_q <= s_coalesce_timer_q - 1'b1; + end + end + end + end +endmodule diff --git a/rtl/ip/multimedia/apu_stream_router.sv b/rtl/ip/multimedia/apu_stream_router.sv new file mode 100644 index 00000000..f80e2e5e --- /dev/null +++ b/rtl/ip/multimedia/apu_stream_router.sv @@ -0,0 +1,205 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 + +module apu_stream_router #( + parameter int unsigned FifoDepth = 64 +) ( + // verilog_format: off -- preserve stream direction columns + input logic clk_i, + input logic rst_n_i, + input logic flush_i, + input logic counter_clear_i, + input logic xrun_clear_i, + input logic tx_route_apu_i, + input logic rx_route_apu_i, + input logic tx_session_active_i, + input logic rx_session_active_i, + input logic [7:0] rx_high_watermark_i, + input logic [7:0] tx_low_watermark_i, + input logic tx_underrun_i, + input logic rx_overrun_i, + axi4_stream_if.sink dma_tx_axis, + axi4_stream_if.source dma_rx_axis, + axi4_stream_if.source i2s_tx_axis, + axi4_stream_if.sink i2s_rx_axis, + axi4_stream_if.sink apu_tx_axis, + axi4_stream_if.source apu_rx_axis, + output logic [31:0] status_o, + output logic input_watermark_evt_o, + output logic output_watermark_evt_o, + output logic stream_xrun_evt_o, + output logic idle_o + // verilog_format: on +); + localparam int unsigned PayloadWidth = 44; + localparam int unsigned CountWidth = $clog2(FifoDepth) + 1; + + logic [PayloadWidth-1:0] s_tx_push_data; + logic [PayloadWidth-1:0] s_tx_pop_data; + logic [PayloadWidth-1:0] s_rx_push_data; + logic [PayloadWidth-1:0] s_rx_pop_data; + logic [ CountWidth-1:0] s_tx_count; + logic [ CountWidth-1:0] s_rx_count; + logic s_tx_full, s_tx_empty, s_rx_full, s_rx_empty; + logic s_tx_push, s_tx_pop, s_rx_push, s_rx_pop; + logic s_tx_active, s_rx_active; + logic s_tx_low_armed_q, s_rx_high_armed_q; + logic s_tx_underrun_q, s_rx_overrun_q; + logic [7:0] s_tx_words_q, s_rx_words_q; + logic [CountWidth-1:0] s_tx_count_after; + logic [CountWidth-1:0] s_rx_count_after; + + function automatic logic [7:0] saturating_increment(input logic [7:0] value_i); + return (&value_i) ? value_i : value_i + 1'b1; + endfunction + + assign s_tx_push_data = { + apu_tx_axis.tuser, + apu_tx_axis.tdest, + apu_tx_axis.tid, + apu_tx_axis.tlast, + apu_tx_axis.tstrb, + apu_tx_axis.tkeep, + apu_tx_axis.tdata + }; + assign s_rx_push_data = { + i2s_rx_axis.tuser, + i2s_rx_axis.tdest, + i2s_rx_axis.tid, + i2s_rx_axis.tlast, + i2s_rx_axis.tstrb, + i2s_rx_axis.tkeep, + i2s_rx_axis.tdata + }; + assign s_tx_push = apu_tx_axis.tvalid && apu_tx_axis.tready; + assign s_tx_pop = tx_route_apu_i && i2s_tx_axis.tvalid && i2s_tx_axis.tready; + assign s_rx_push = rx_route_apu_i && i2s_rx_axis.tvalid && i2s_rx_axis.tready; + assign s_rx_pop = apu_rx_axis.tvalid && apu_rx_axis.tready; + assign apu_tx_axis.tready = !s_tx_full; + + assign i2s_tx_axis.tdata = tx_route_apu_i ? s_tx_pop_data[31:0] : dma_tx_axis.tdata; + assign i2s_tx_axis.tkeep = tx_route_apu_i ? s_tx_pop_data[35:32] : dma_tx_axis.tkeep; + assign i2s_tx_axis.tstrb = tx_route_apu_i ? s_tx_pop_data[39:36] : dma_tx_axis.tstrb; + assign i2s_tx_axis.tlast = tx_route_apu_i ? s_tx_pop_data[40] : dma_tx_axis.tlast; + assign i2s_tx_axis.tid = tx_route_apu_i ? s_tx_pop_data[41] : dma_tx_axis.tid; + assign i2s_tx_axis.tdest = tx_route_apu_i ? s_tx_pop_data[42] : dma_tx_axis.tdest; + assign i2s_tx_axis.tuser = tx_route_apu_i ? s_tx_pop_data[43] : dma_tx_axis.tuser; + assign i2s_tx_axis.tvalid = tx_route_apu_i ? !s_tx_empty : dma_tx_axis.tvalid; + assign dma_tx_axis.tready = !tx_route_apu_i && i2s_tx_axis.tready; + + assign i2s_rx_axis.tready = rx_route_apu_i ? !s_rx_full : dma_rx_axis.tready; + assign dma_rx_axis.tdata = i2s_rx_axis.tdata; + assign dma_rx_axis.tkeep = i2s_rx_axis.tkeep; + assign dma_rx_axis.tstrb = i2s_rx_axis.tstrb; + assign dma_rx_axis.tlast = i2s_rx_axis.tlast; + assign dma_rx_axis.tid = i2s_rx_axis.tid; + assign dma_rx_axis.tdest = i2s_rx_axis.tdest; + assign dma_rx_axis.tuser = i2s_rx_axis.tuser; + assign dma_rx_axis.tvalid = !rx_route_apu_i && i2s_rx_axis.tvalid; + + assign apu_rx_axis.tdata = s_rx_pop_data[31:0]; + assign apu_rx_axis.tkeep = s_rx_pop_data[35:32]; + assign apu_rx_axis.tstrb = s_rx_pop_data[39:36]; + assign apu_rx_axis.tlast = s_rx_pop_data[40]; + assign apu_rx_axis.tid = s_rx_pop_data[41]; + assign apu_rx_axis.tdest = s_rx_pop_data[42]; + assign apu_rx_axis.tuser = s_rx_pop_data[43]; + assign apu_rx_axis.tvalid = !s_rx_empty; + + assign s_tx_active = tx_route_apu_i && + (tx_session_active_i || !s_tx_empty || (i2s_tx_axis.tvalid && !i2s_tx_axis.tready)); + assign s_rx_active = rx_route_apu_i && (rx_session_active_i || !s_rx_empty || s_rx_push); + assign idle_o = !s_tx_active && !s_rx_active; + assign s_tx_count_after = s_tx_count + CountWidth'(s_tx_push) - CountWidth'(s_tx_pop); + assign s_rx_count_after = s_rx_count + CountWidth'(s_rx_push) - CountWidth'(s_rx_pop); + assign input_watermark_evt_o = (rx_high_watermark_i != 8'd0) && s_rx_high_armed_q && + (s_rx_count < CountWidth'(rx_high_watermark_i)) && + (s_rx_count_after >= CountWidth'(rx_high_watermark_i)); + assign output_watermark_evt_o = (tx_low_watermark_i != 8'd0) && s_tx_low_armed_q && + s_tx_active && (s_tx_count > CountWidth'(tx_low_watermark_i)) && + (s_tx_count_after <= CountWidth'(tx_low_watermark_i)); + assign stream_xrun_evt_o = (tx_route_apu_i && tx_underrun_i) || (rx_route_apu_i && rx_overrun_i); + assign status_o = { + 8'd0, + s_rx_words_q, + s_tx_words_q, + s_rx_overrun_q, + s_tx_underrun_q, + s_rx_full, + s_rx_empty, + s_tx_full, + s_tx_empty, + s_rx_active, + s_tx_active + }; + + stream_fifo #( + .DATA_WIDTH (PayloadWidth), + .BUFFER_DEPTH(FifoDepth) + ) u_tx_fifo ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .flush_i(flush_i), + .full_o (s_tx_full), + .empty_o(s_tx_empty), + .cnt_o (s_tx_count), + .dat_i (s_tx_push_data), + .push_i (s_tx_push), + .dat_o (s_tx_pop_data), + .pop_i (s_tx_pop) + ); + stream_fifo #( + .DATA_WIDTH (PayloadWidth), + .BUFFER_DEPTH(FifoDepth) + ) u_rx_fifo ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .flush_i(flush_i), + .full_o (s_rx_full), + .empty_o(s_rx_empty), + .cnt_o (s_rx_count), + .dat_i (s_rx_push_data), + .push_i (s_rx_push), + .dat_o (s_rx_pop_data), + .pop_i (s_rx_pop) + ); + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i || flush_i) begin + s_tx_low_armed_q <= 1'b0; + s_rx_high_armed_q <= 1'b1; + s_tx_underrun_q <= 1'b0; + s_rx_overrun_q <= 1'b0; + s_tx_words_q <= 8'd0; + s_rx_words_q <= 8'd0; + end else begin + if ((tx_low_watermark_i == 8'd0) || + (s_tx_count_after > CountWidth'(tx_low_watermark_i))) begin + s_tx_low_armed_q <= tx_low_watermark_i != 8'd0; + end else if (output_watermark_evt_o) begin + s_tx_low_armed_q <= 1'b0; + end + if ((rx_high_watermark_i == 8'd0) || + (s_rx_count_after < CountWidth'(rx_high_watermark_i))) begin + s_rx_high_armed_q <= 1'b1; + end else if (input_watermark_evt_o) begin + s_rx_high_armed_q <= 1'b0; + end + s_tx_underrun_q <= (s_tx_underrun_q && !xrun_clear_i) || (tx_route_apu_i && tx_underrun_i); + s_rx_overrun_q <= (s_rx_overrun_q && !xrun_clear_i) || (rx_route_apu_i && rx_overrun_i); + if (counter_clear_i) begin + s_tx_words_q <= 8'd0; + s_rx_words_q <= 8'd0; + end else begin + if (s_tx_pop) s_tx_words_q <= saturating_increment(s_tx_words_q); + if (s_rx_push) s_rx_words_q <= saturating_increment(s_rx_words_q); + end + end + end + +`ifndef SYNTHESIS + initial begin + if (FifoDepth != 64) $fatal(1, "apu_stream_router: P2 requires 64-word FIFOs"); + end +`endif +endmodule diff --git a/rtl/ip/multimedia/jpeg_dma.sv b/rtl/ip/multimedia/jpeg_dma.sv index a9ce411a..381cedc1 100644 --- a/rtl/ip/multimedia/jpeg_dma.sv +++ b/rtl/ip/multimedia/jpeg_dma.sv @@ -442,6 +442,7 @@ module jpeg_dma #( ) u_axi4_master ( .clk_i (clk_i), .rst_n_i (rst_n_i), + .clear_i (1'b0), .read_start_valid_i (s_read_start_valid), .read_start_ready_o (s_read_start_ready), .read_addr_i (s_read_addr_q), diff --git a/rtl/ip/peripheral/dma_axi4_master.sv b/rtl/ip/peripheral/dma_axi4_master.sv index 79e11325..e10aec71 100644 --- a/rtl/ip/peripheral/dma_axi4_master.sv +++ b/rtl/ip/peripheral/dma_axi4_master.sv @@ -18,6 +18,7 @@ module dma_axi4_master #( // verilog_format: off -- AXI command/data ports are aligned with their response groups. input logic clk_i, input logic rst_n_i, + input logic clear_i, input logic read_start_valid_i, output logic read_start_ready_o, input logic [AddrWidth-1:0] read_addr_i, @@ -141,6 +142,12 @@ module dma_axi4_master #( s_read_beats_q <= 5'd1; s_read_beat_q <= '0; s_read_fixed_q <= 1'b0; + end else if (clear_i) begin + s_read_state_q <= ReadIdle; + s_read_addr_q <= '0; + s_read_beats_q <= 5'd1; + s_read_beat_q <= '0; + s_read_fixed_q <= 1'b0; end else begin unique case (s_read_state_q) ReadIdle: begin @@ -178,6 +185,12 @@ module dma_axi4_master #( s_write_beats_q <= 5'd1; s_write_beat_q <= '0; s_write_fixed_q <= 1'b0; + end else if (clear_i) begin + s_write_state_q <= WriteIdle; + s_write_addr_q <= '0; + s_write_beats_q <= 5'd1; + s_write_beat_q <= '0; + s_write_fixed_q <= 1'b0; end else begin unique case (s_write_state_q) WriteIdle: begin diff --git a/rtl/ip/peripheral/dma_core.sv b/rtl/ip/peripheral/dma_core.sv index b9595ae6..b7c28ae5 100644 --- a/rtl/ip/peripheral/dma_core.sv +++ b/rtl/ip/peripheral/dma_core.sv @@ -341,6 +341,7 @@ module dma_core #( ) u_dma_axi4_master ( .clk_i (clk_i), .rst_n_i (rst_n_i), + .clear_i (1'b0), .read_start_valid_i (s_axi_read_start_valid), .read_start_ready_o (s_axi_read_start_ready), .read_addr_i (s_axi_read_start_addr), diff --git a/rtl/ip/serial/apb4_i2s.sv b/rtl/ip/serial/apb4_i2s.sv index b81a1c29..1c11028e 100644 --- a/rtl/ip/serial/apb4_i2s.sv +++ b/rtl/ip/serial/apb4_i2s.sv @@ -38,6 +38,8 @@ module apb4_i2s ( input logic rst_aud_n_i, output logic dma_tx_stall_o, output logic dma_rx_stall_o, + output logic tx_underrun_evt_o, + output logic rx_overrun_evt_o, apb4_if.slave apb4, axi4_stream_if.sink tx_axis, axi4_stream_if.source rx_axis, @@ -126,6 +128,8 @@ module apb4_i2s ( assign s_rx_flush_busy = s_cmd_rx_flush || s_rx_dst_clear_busy || s_rx_src_busy_sys; assign s_tx_underrun_sys = s_tx_underrun_re || s_tx_underrun_fe; assign s_rx_overrun_sys = s_rx_overrun_re || s_rx_overrun_fe; + assign tx_underrun_evt_o = s_tx_underrun_sys; + assign rx_overrun_evt_o = s_rx_overrun_sys; assign s_tx_push = s_tx_src_valid && s_tx_src_ready; assign s_rx_pop = s_rx_dst_valid && s_rx_dst_ready; assign s_tx_level = s_tx_flush_busy ? 8'd0 : (s_tx_push_cnt_q - s_tx_pop_cnt_sys_q); diff --git a/rtl/ip/storage/sdio_dma.sv b/rtl/ip/storage/sdio_dma.sv index 30a65a65..8cea0c60 100644 --- a/rtl/ip/storage/sdio_dma.sv +++ b/rtl/ip/storage/sdio_dma.sv @@ -222,6 +222,7 @@ module sdio_dma #( ) u_dma_axi4_master ( .clk_i (clk_i), .rst_n_i (rst_n_i), + .clear_i (1'b0), .read_start_valid_i (s_read_start_valid), .read_start_ready_o (s_read_start_ready), .read_addr_i (s_read_addr), diff --git a/rtl/ip/usb/usb2_dma.sv b/rtl/ip/usb/usb2_dma.sv index 4c0121df..126352b4 100644 --- a/rtl/ip/usb/usb2_dma.sv +++ b/rtl/ip/usb/usb2_dma.sv @@ -301,6 +301,7 @@ module usb2_dma #( ) u_axi4_master ( .clk_i (clk_i), .rst_n_i (rst_n_i), + .clear_i (1'b0), .read_start_valid_i (s_read_start_valid), .read_start_ready_o (s_read_start_ready), .read_addr_i (s_read_addr), diff --git a/rtl/mini/filelist/ip.fl b/rtl/mini/filelist/ip.fl index b9b3fcb1..8353d9b1 100644 --- a/rtl/mini/filelist/ip.fl +++ b/rtl/mini/filelist/ip.fl @@ -125,6 +125,9 @@ /ip/multimedia/dvp_core.sv /ip/multimedia/dvp_reg.sv /ip/multimedia/axi4s_dvp.sv +/ip/multimedia/apu_dma.sv +/ip/multimedia/apu_ring_scheduler.sv +/ip/multimedia/apu_stream_router.sv /ip/multimedia/apu_reg.sv /ip/multimedia/apb4_apu.sv /ip/multimedia/jpeg_pkg.sv diff --git a/rtl/mini/formal/apu_formal.sv b/rtl/mini/formal/apu_formal.sv new file mode 100644 index 00000000..bfc768be --- /dev/null +++ b/rtl/mini/formal/apu_formal.sv @@ -0,0 +1,248 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 + +`include "axi4_define.svh" + +module apu_formal_design ( + // verilog_format: off -- expose the bounded DMA scenario and AXI observations + input logic clk_i, + output logic rst_n_i, + output logic f_past_valid, + output logic [2:0] scenario, + output logic [5:0] cycle, + output logic busy, + output logic done, + output logic error, + output logic aborted, + output logic [5:0] error_code, + output logic [63:0] read_bytes, + output logic [63:0] write_bytes, + output logic terminal_seen, + output logic terminal_error, + output logic terminal_aborted, + output logic [5:0] terminal_code, + output logic awvalid, + output logic awready, + output logic [31:0] awaddr, + output logic [7:0] awlen, + output logic [2:0] awsize, + output logic [1:0] awburst, + output logic [3:0] awcache, + output logic [2:0] awprot, + output logic wvalid, + output logic wready, + output logic [3:0] wstrb, + output logic wlast, + output logic bvalid, + output logic bready, + output logic arvalid, + output logic arready, + output logic [31:0] araddr, + output logic [7:0] arlen, + output logic [2:0] arsize, + output logic [1:0] arburst, + output logic [3:0] arcache, + output logic [2:0] arprot, + output logic rvalid, + output logic rready, + output logic rlast + // verilog_format: on +); + (* anyconst *)logic [ 2:0] f_scenario; + (* anyconst *)logic [31:0] f_read_data; + logic [ 5:0] s_cycle_q; + logic s_read_active_q; + logic s_write_active_q; + logic s_write_response_q; + logic s_terminal_seen_q; + logic s_terminal_error_q; + logic s_terminal_aborted_q; + logic [ 5:0] s_terminal_code_q; + logic s_request_write; + logic s_abort; + logic [ 1:0] s_source_beat_q; + logic s_done, s_error, s_aborted; + logic [ 5:0] s_error_code; + logic [ 3:0] s_error_stage; + logic [ 1:0] s_error_resp; + logic [31:0] s_error_addr; + logic [63:0] s_read_bytes, s_write_bytes; + + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) axi4 ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) read_axis ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) write_axis ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + + assign s_request_write = f_scenario == 2'd1; + assign s_abort = (f_scenario == 2'd3) && (s_cycle_q == 6'd4); + assign read_axis.tready = 1'b1; + assign write_axis.tdata = 32'hcafe_0000 | {30'd0, s_source_beat_q}; + assign write_axis.tkeep = 4'hf; + assign write_axis.tstrb = 4'hf; + assign write_axis.tlast = s_source_beat_q == 2'd1; + assign write_axis.tid = '0; + assign write_axis.tdest = '0; + assign write_axis.tuser = '0; + assign write_axis.tvalid = f_scenario == 2'd1; + + assign axi4.arready = !s_read_active_q && + ((f_scenario == 3'd0) || (f_scenario == 3'd4) || (f_scenario == 3'd5) || + ((f_scenario == 3'd2) && (s_cycle_q >= 6'd12)) || + ((f_scenario == 3'd3) && (s_cycle_q >= 6'd7))); + assign axi4.rid = 1'b0; + assign axi4.rdata = f_read_data; + assign axi4.rresp = `AXI4_RESP_OKAY; + assign axi4.rlast = f_scenario != 3'd5; + assign axi4.ruser = 1'b0; + assign axi4.rvalid = s_read_active_q; + assign axi4.awready = (f_scenario == 2'd1) && !s_write_active_q && !s_write_response_q; + assign axi4.wready = s_write_active_q; + assign axi4.bid = 1'b0; + assign axi4.bresp = `AXI4_RESP_OKAY; + assign axi4.buser = 1'b0; + assign axi4.bvalid = s_write_response_q; + + apu_dma u_dut ( + .clk_i, + .rst_n_i, + .abort_i(s_abort), + .quiesce_i(1'b0), + .bridge_epoch_i((f_scenario == 3'd4) ? 8'd1 : 8'd0), + .perf_enable_i(1'b1), + .counter_clear_i(1'b0), + .request_valid_i (rst_n_i && + ((s_cycle_q == 6'd0) || + ((f_scenario == 3'd4) && (s_cycle_q < 6'd3)))), + .request_ready_o(), + .request_write_i(s_request_write), + .request_addr_i(32'h0000_0ffc), + .request_bytes_i(32'd8), + .read_base_i(32'd0), + .read_limit_i(32'h0000_ffff), + .write_base_i(32'd0), + .write_limit_i(32'h0000_ffff), + .timeout_i(32'd8), + .read_axis, + .write_axis, + .busy_o(busy), + .done_o(s_done), + .error_o(s_error), + .aborted_o(s_aborted), + .aborting_o(), + .error_code_o(s_error_code), + .error_stage_o(s_error_stage), + .error_resp_o(s_error_resp), + .error_addr_o(s_error_addr), + .input_pending_o(), + .output_pending_o(), + .read_bytes_o(s_read_bytes), + .write_bytes_o(s_write_bytes), + .read_stalls_o(), + .write_stalls_o(), + .axi4 + ); + + assign scenario = f_scenario; + assign cycle = s_cycle_q; + assign done = s_done; + assign error = s_error; + assign aborted = s_aborted; + assign error_code = s_error_code; + assign read_bytes = s_read_bytes; + assign write_bytes = s_write_bytes; + assign terminal_seen = s_terminal_seen_q; + assign terminal_error = s_terminal_error_q; + assign terminal_aborted = s_terminal_aborted_q; + assign terminal_code = s_terminal_code_q; + assign awvalid = axi4.awvalid; + assign awready = axi4.awready; + assign awaddr = axi4.awaddr; + assign awlen = axi4.awlen; + assign awsize = axi4.awsize; + assign awburst = axi4.awburst; + assign awcache = axi4.awcache; + assign awprot = axi4.awprot; + assign wvalid = axi4.wvalid; + assign wready = axi4.wready; + assign wstrb = axi4.wstrb; + assign wlast = axi4.wlast; + assign bvalid = axi4.bvalid; + assign bready = axi4.bready; + assign arvalid = axi4.arvalid; + assign arready = axi4.arready; + assign araddr = axi4.araddr; + assign arlen = axi4.arlen; + assign arsize = axi4.arsize; + assign arburst = axi4.arburst; + assign arcache = axi4.arcache; + assign arprot = axi4.arprot; + assign rvalid = axi4.rvalid; + assign rready = axi4.rready; + assign rlast = axi4.rlast; + + initial begin + rst_n_i = 1'b0; + f_past_valid = 1'b0; + end + + always_ff @(posedge clk_i) begin + rst_n_i <= 1'b1; + f_past_valid <= 1'b1; + if (!rst_n_i) begin + s_cycle_q <= 6'd0; + s_terminal_seen_q <= 1'b0; + s_terminal_error_q <= 1'b0; + s_terminal_aborted_q <= 1'b0; + s_terminal_code_q <= 6'd0; + end else begin + if (s_cycle_q != 6'h3f) s_cycle_q <= s_cycle_q + 1'b1; + if (s_done) begin + s_terminal_seen_q <= 1'b1; + s_terminal_error_q <= s_error; + s_terminal_aborted_q <= s_aborted; + s_terminal_code_q <= s_error_code; + end + end + end + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + s_read_active_q <= 1'b0; + s_write_active_q <= 1'b0; + s_write_response_q <= 1'b0; + s_source_beat_q <= 2'd0; + end else begin + if (axi4.arvalid && axi4.arready) s_read_active_q <= 1'b1; + if (axi4.rvalid && axi4.rready) s_read_active_q <= 1'b0; + if (axi4.awvalid && axi4.awready) s_write_active_q <= 1'b1; + if (axi4.wvalid && axi4.wready) begin + s_source_beat_q <= s_source_beat_q + 1'b1; + if (axi4.wlast) begin + s_write_active_q <= 1'b0; + s_write_response_q <= 1'b1; + end + end + if (axi4.bvalid && axi4.bready) s_write_response_q <= 1'b0; + end + end + + logic s_unused_result; + assign s_unused_result = ^s_error_stage ^ ^s_error_resp ^ ^s_error_addr; +endmodule diff --git a/rtl/mini/formal/apu_formal_props.sv b/rtl/mini/formal/apu_formal_props.sv new file mode 100644 index 00000000..a2b4bf3e --- /dev/null +++ b/rtl/mini/formal/apu_formal_props.sv @@ -0,0 +1,93 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 + +module apu_formal; + (* anyseq *) (* gclk *) reg clk_i; + wire rst_n_i, f_past_valid; + wire [2:0] scenario; + wire [5:0] cycle; + wire busy, done, error, aborted; + wire [5:0] error_code; + wire [63:0] read_bytes, write_bytes; + wire terminal_seen, terminal_error, terminal_aborted; + wire [5:0] terminal_code; + wire awvalid, awready, wvalid, wready, wlast, bvalid, bready; + wire arvalid, arready, rvalid, rready, rlast; + wire [31:0] awaddr, araddr; + wire [7:0] awlen, arlen; + wire [2:0] awsize, arsize; + wire [1:0] awburst, arburst; + wire [3:0] awcache, arcache, wstrb; + wire [2:0] awprot, arprot; + + apu_formal_design u_design (.*); + + always @(posedge clk_i) begin + if (rst_n_i) begin + assume (scenario <= 3'd5); + assert (!(awvalid && arvalid)); + if (awvalid) begin + assert (awlen < 8'd16); + assert (awsize == 3'd2); + assert (awburst == 2'd1); + assert (awcache == 4'd0); + assert (awprot[2] == 1'b0); + assert ({1'b0, awaddr[11:0]} + (({5'd0, awlen} + 1'b1) << awsize) <= 13'd4096); + end + if (arvalid) begin + assert (arlen < 8'd16); + assert (arsize == 3'd2); + assert (arburst == 2'd1); + assert (arcache == 4'd0); + assert (arprot[2] == 1'b0); + assert ({1'b0, araddr[11:0]} + (({5'd0, arlen} + 1'b1) << arsize) <= 13'd4096); + end + if (wvalid) assert (wstrb != 4'd0); + if (done) begin + if (scenario == 3'd0) assert (!error && !aborted && (read_bytes == 64'd8)); + if (scenario == 3'd1) assert (!error && !aborted && (write_bytes == 64'd8)); + if (scenario == 3'd2) assert (error && !aborted && (error_code == 6'd17)); + if (scenario == 3'd3) assert (!error && aborted && (error_code == 6'd20)); + if (scenario == 3'd4) assert (!error && !aborted && (read_bytes == 64'd8)); + if (scenario == 3'd5) assert (error && !aborted && (error_code == 6'd15)); + end + if (cycle >= 6'd20) begin + assert (terminal_seen); + if (scenario == 3'd2) assert (terminal_error && (terminal_code == 6'd17)); + if (scenario == 3'd3) + assert (!terminal_error && terminal_aborted && (terminal_code == 6'd20)); + if (scenario == 3'd4) assert (!terminal_error && !terminal_aborted); + if (scenario == 3'd5) assert (terminal_error && (terminal_code == 6'd15)); + end + end + if (f_past_valid && $past(rst_n_i)) begin + if ($past(awvalid && !awready)) begin + assert (awvalid); + assert (awaddr == $past(awaddr)); + assert (awlen == $past(awlen)); + end + if ($past(arvalid && !arready)) begin + assert (arvalid); + assert (araddr == $past(araddr)); + assert (arlen == $past(arlen)); + end + if ($past(wvalid && !wready)) begin + assert (wvalid); + assert (wstrb == $past(wstrb)); + assert (wlast == $past(wlast)); + end + if ($past(bvalid && !bready)) assert (bvalid); + if ($past(rvalid && !rready)) begin + assert (rvalid); + assert (rlast == $past(rlast)); + end + end + + cover (rst_n_i && done && (scenario == 3'd0)); + cover (rst_n_i && done && (scenario == 3'd1)); + cover (rst_n_i && done && (scenario == 3'd2)); + cover (rst_n_i && done && (scenario == 3'd3)); + cover (rst_n_i && done && (scenario == 3'd4)); + cover (rst_n_i && done && (scenario == 3'd5)); + end +endmodule diff --git a/rtl/mini/formal/gateway_a_formal.sv b/rtl/mini/formal/gateway_a_formal.sv new file mode 100644 index 00000000..5a9ce428 --- /dev/null +++ b/rtl/mini/formal/gateway_a_formal.sv @@ -0,0 +1,220 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 + +module gateway_a_formal_design ( + input logic clk_i, + output logic rst_n_i, + output logic f_past_valid, + output logic [ 5:0] cycle, + output logic [ 2:0] response, + output logic [ 2:0] seen, + output logic address_valid, + output logic address_ready, + output logic [31:0] address_value, + output logic address_accept, + output logic terminal +); + logic [ 5:0] s_cycle_q; + logic s_response_valid_q; + logic [31:0] s_response_data_q; + logic [ 2:0] s_seen_q; + + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) icache ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) dcache ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) mmio ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) axi4 ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + + assign icache.arid = 1'b0; + assign dcache.arid = 1'b0; + assign mmio.arid = 1'b0; + assign icache.araddr = 32'h0000_1000; + assign dcache.araddr = 32'h0000_2000; + assign mmio.araddr = 32'h0000_3000; + assign icache.arlen = 8'd0; + assign dcache.arlen = 8'd0; + assign mmio.arlen = 8'd0; + assign icache.arsize = 3'd2; + assign dcache.arsize = 3'd2; + assign mmio.arsize = 3'd2; + assign icache.arburst = 2'd1; + assign dcache.arburst = 2'd1; + assign mmio.arburst = 2'd1; + assign icache.arlock = 1'b0; + assign dcache.arlock = 1'b0; + assign mmio.arlock = 1'b0; + assign icache.arcache = 4'd0; + assign dcache.arcache = 4'd0; + assign mmio.arcache = 4'd0; + assign icache.arprot = 3'd0; + assign dcache.arprot = 3'd0; + assign mmio.arprot = 3'd0; + assign icache.arqos = 4'd0; + assign dcache.arqos = 4'd0; + assign mmio.arqos = 4'd0; + assign icache.arregion = 4'd0; + assign dcache.arregion = 4'd0; + assign mmio.arregion = 4'd0; + assign icache.aruser = 1'b0; + assign dcache.aruser = 1'b0; + assign mmio.aruser = 1'b0; + assign icache.arvalid = rst_n_i; + assign dcache.arvalid = rst_n_i && (s_cycle_q >= 6'd1); + assign mmio.arvalid = rst_n_i && (s_cycle_q >= 6'd2); + assign icache.rready = 1'b1; + assign dcache.rready = 1'b1; + assign mmio.rready = 1'b1; + + assign icache.awid = 1'b0; + assign dcache.awid = 1'b0; + assign mmio.awid = 1'b0; + assign icache.awaddr = 32'd0; + assign dcache.awaddr = 32'd0; + assign mmio.awaddr = 32'd0; + assign icache.awlen = 8'd0; + assign dcache.awlen = 8'd0; + assign mmio.awlen = 8'd0; + assign icache.awsize = 3'd2; + assign dcache.awsize = 3'd2; + assign mmio.awsize = 3'd2; + assign icache.awburst = 2'd1; + assign dcache.awburst = 2'd1; + assign mmio.awburst = 2'd1; + assign icache.awlock = 1'b0; + assign dcache.awlock = 1'b0; + assign mmio.awlock = 1'b0; + assign icache.awcache = 4'd0; + assign dcache.awcache = 4'd0; + assign mmio.awcache = 4'd0; + assign icache.awprot = 3'd0; + assign dcache.awprot = 3'd0; + assign mmio.awprot = 3'd0; + assign icache.awqos = 4'd0; + assign dcache.awqos = 4'd0; + assign mmio.awqos = 4'd0; + assign icache.awregion = 4'd0; + assign dcache.awregion = 4'd0; + assign mmio.awregion = 4'd0; + assign icache.awuser = 1'b0; + assign dcache.awuser = 1'b0; + assign mmio.awuser = 1'b0; + assign icache.awvalid = 1'b0; + assign dcache.awvalid = 1'b0; + assign mmio.awvalid = 1'b0; + assign icache.wdata = 32'd0; + assign dcache.wdata = 32'd0; + assign mmio.wdata = 32'd0; + assign icache.wstrb = 4'd0; + assign dcache.wstrb = 4'd0; + assign mmio.wstrb = 4'd0; + assign icache.wlast = 1'b0; + assign dcache.wlast = 1'b0; + assign mmio.wlast = 1'b0; + assign icache.wuser = 1'b0; + assign dcache.wuser = 1'b0; + assign mmio.wuser = 1'b0; + assign icache.wvalid = 1'b0; + assign dcache.wvalid = 1'b0; + assign mmio.wvalid = 1'b0; + assign icache.bready = 1'b1; + assign dcache.bready = 1'b1; + assign mmio.bready = 1'b1; + + assign axi4.awready = 1'b0; + assign axi4.wready = 1'b0; + assign axi4.bid = 1'b0; + assign axi4.bresp = 2'd0; + assign axi4.buser = 1'b0; + assign axi4.bvalid = 1'b0; + assign axi4.arready = !s_response_valid_q && (s_cycle_q >= 6'd3); + assign axi4.rid = 1'b0; + assign axi4.rdata = s_response_data_q; + assign axi4.rresp = 2'd0; + assign axi4.rlast = 1'b1; + assign axi4.ruser = 1'b0; + assign axi4.rvalid = s_response_valid_q; + + hp_axi4_mux3 #( + .RoundRobin(1'b1) + ) u_dut ( + .clk_i, + .rst_n_i, + .epoch_i(8'd0), + .icache, + .dcache, + .mmio, + .axi4 + ); + + assign cycle = s_cycle_q; + assign response = { + mmio.rvalid && mmio.rready, dcache.rvalid && dcache.rready, icache.rvalid && icache.rready + }; + assign seen = s_seen_q; + assign address_valid = axi4.arvalid; + assign address_ready = axi4.arready; + assign address_value = axi4.araddr; + assign address_accept = axi4.arvalid && axi4.arready; + assign terminal = axi4.rvalid && axi4.rready && axi4.rlast; + + initial begin + rst_n_i = 1'b0; + f_past_valid = 1'b0; + end + + always_ff @(posedge clk_i) begin + rst_n_i <= 1'b1; + f_past_valid <= 1'b1; + if (!rst_n_i) begin + s_cycle_q <= 6'd0; + s_seen_q <= 3'd0; + end else begin + if (s_cycle_q != 6'h3f) s_cycle_q <= s_cycle_q + 1'b1; + s_seen_q <= s_seen_q | response; + end + end + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + s_response_valid_q <= 1'b0; + s_response_data_q <= 32'd0; + end else begin + if (axi4.arvalid && axi4.arready) begin + s_response_valid_q <= 1'b1; + s_response_data_q <= axi4.araddr; + end + if (axi4.rvalid && axi4.rready) s_response_valid_q <= 1'b0; + end + end +endmodule diff --git a/rtl/mini/formal/gateway_a_formal_props.sv b/rtl/mini/formal/gateway_a_formal_props.sv new file mode 100644 index 00000000..354bfa37 --- /dev/null +++ b/rtl/mini/formal/gateway_a_formal_props.sv @@ -0,0 +1,43 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 + +module gateway_a_formal; + (* anyseq *) (* gclk *) reg clk_i; + wire rst_n_i, f_past_valid; + wire [5:0] cycle; + wire [2:0] response, seen; + wire address_valid, address_ready; + wire [31:0] address_value; + wire address_accept, terminal; + logic [1:0] f_expected_q; + + gateway_a_formal_design u_design (.*); + + always @(posedge clk_i) begin + if (!rst_n_i) begin + f_expected_q <= 2'd0; + end else begin + assert ($onehot0(response)); + if (response != 3'd0) begin + case (f_expected_q) + 2'd0: assert (response == 3'b001); + 2'd1: assert (response == 3'b010); + default: assert (response == 3'b100); + endcase + f_expected_q <= (f_expected_q == 2'd2) ? 2'd0 : f_expected_q + 1'b1; + end + if (cycle >= 6'd12) assert (seen == 3'b111); + assert (terminal == (response != 3'd0)); + end + + if (f_past_valid && $past(rst_n_i) && $past(address_valid && !address_ready)) begin + assert (address_valid); + assert (address_value == $past(address_value)); + end + + cover (rst_n_i && (seen == 3'b111)); + end + + logic s_unused_observation; + assign s_unused_observation = f_past_valid ^ address_accept; +endmodule diff --git a/rtl/mini/formal/generate_formal_filelist.py b/rtl/mini/formal/generate_formal_filelist.py index dba496e7..66d62bcc 100644 --- a/rtl/mini/formal/generate_formal_filelist.py +++ b/rtl/mini/formal/generate_formal_filelist.py @@ -22,6 +22,7 @@ PERIPHERAL = ROOT / "rtl/ip/peripheral" SERIAL = ROOT / "rtl/ip/serial" MEMORY = ROOT / "rtl/ip/memory" STORAGE = ROOT / "rtl/ip/storage" +MULTIMEDIA = ROOT / "rtl/ip/multimedia" TOP = ROOT / "rtl/mini/top" @@ -247,6 +248,23 @@ def source_files(target: str) -> list[Path]: PERIPHERAL / "dma_core.sv", SCRIPT_DIR / "dma_formal.sv", ] + if target == "apu": + return [ + COMMON_RTL / "interface/axi4_if.sv", + COMMON_RTL / "interface/axi4_stream_if.sv", + COMMON_RTL / "utils/register.sv", + PERIPHERAL / "dma_axi4_master.sv", + MULTIMEDIA / "apu_dma.sv", + SCRIPT_DIR / "apu_formal.sv", + ] + if target == "gateway_a": + return [ + COMMON_RTL / "interface/axi4_if.sv", + COMMON_RTL / "utils/register.sv", + COMMON_RTL / "stream/round_robin_arbiter.sv", + TOP / "hp_axi4_mux3.sv", + SCRIPT_DIR / "gateway_a_formal.sv", + ] if target == "sdio": return [ COMMON_RTL / "interface/apb4_if.sv", @@ -285,6 +303,7 @@ def generate( PERIPHERAL, SERIAL, MEMORY, + MULTIMEDIA, ], files=source_files(target), ).deduplicate() @@ -313,6 +332,8 @@ def parse_args() -> argparse.Namespace: "onchip_ram", "opipsram", "dma", + "apu", + "gateway_a", "sdio", ), required=True, diff --git a/rtl/mini/integration/clock_reset_domains.json b/rtl/mini/integration/clock_reset_domains.json index 95909680..0b2eb85b 100644 --- a/rtl/mini/integration/clock_reset_domains.json +++ b/rtl/mini/integration/clock_reset_domains.json @@ -109,6 +109,7 @@ {"name": "sram_config", "source": "pclk", "destination": "hp", "primitive": "apb4_async_bridge", "path": "rtl/mini/top/retrosoc.sv", "instance": "u_sram_cfg_cdc"}, {"name": "fabric_monitor", "source": "pclk", "destination": "hp", "primitive": "apb4_async_bridge", "path": "rtl/mini/top/retrosoc.sv", "instance": "u_fabric_monitor_cdc"}, {"name": "io_dma_data", "source": "pclk", "destination": "hp", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_io_cdc"}, + {"name": "apu_data", "source": "pclk", "destination": "hp", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_io_cdc"}, {"name": "jpeg_data", "source": "pclk", "destination": "hp", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_jpeg_cdc"}, {"name": "extension_h_data", "source": "pclk", "destination": "hp", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_ext_h_cdc"}, {"name": "sdram_data", "source": "hp", "destination": "memory", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_target_cdc"}, diff --git a/rtl/mini/mk/formal.mk b/rtl/mini/mk/formal.mk index 11ee9886..2db67b14 100644 --- a/rtl/mini/mk/formal.mk +++ b/rtl/mini/mk/formal.mk @@ -14,6 +14,8 @@ FORMAL_OPIPSRAM_DEPTH ?= 40 FORMAL_OPIPSRAM_BMC_DEPTH ?= 40 FORMAL_DMA_DEPTH ?= 24 FORMAL_DMA_COVER_DEPTH ?= 32 +FORMAL_APU_DEPTH ?= 24 +FORMAL_GATEWAY_A_DEPTH ?= 20 FORMAL_SDIO_DEPTH ?= 20 FORMAL_SDIO_COVER_DEPTH ?= 24 FORMAL_TIMEOUT ?= 60 @@ -22,9 +24,12 @@ FORMAL_I2C_TIMEOUT ?= 300 FORMAL_OPIPSRAM_TIMEOUT ?= 300 FORMAL_OPIPSRAM_BMC_TIMEOUT ?= 600 FORMAL_DMA_TIMEOUT ?= 120 +FORMAL_APU_TIMEOUT ?= 120 +FORMAL_GATEWAY_A_TIMEOUT ?= 120 FORMAL_SDIO_TIMEOUT ?= 120 +FORMAL_TARGET_TIMEOUT = $(if $(filter dma,$*),$(FORMAL_DMA_TIMEOUT),$(if $(filter apu,$*),$(FORMAL_APU_TIMEOUT),$(if $(filter gateway_a,$*),$(FORMAL_GATEWAY_A_TIMEOUT),$(if $(filter sdio,$*),$(FORMAL_SDIO_TIMEOUT),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_TIMEOUT),$(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(if $(filter i2c,$*),$(FORMAL_I2C_TIMEOUT),$(FORMAL_TIMEOUT)))))))) FORMAL_COMPACT_COVER_TARGETS := i2c opipsram dma -FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint dvp i2s psram onchip_ram opipsram dma sdio +FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint dvp i2s psram onchip_ram opipsram dma apu gateway_a sdio FORMAL_FILELIST_GENERATOR := $(RTL_PATH)/formal/generate_formal_filelist.py FORMAL_SBY_GENERATOR := $(RTL_PATH)/formal/generate_sby_config.py FORMAL_RESULT_GENERATOR := $(RTL_PATH)/formal/formal_results.py @@ -62,6 +67,10 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \ $(RTL_PATH)/formal/opipsram_formal_props.sv \ $(RTL_PATH)/formal/dma_formal.sv \ $(RTL_PATH)/formal/dma_formal_props.sv \ + $(RTL_PATH)/formal/apu_formal.sv \ + $(RTL_PATH)/formal/apu_formal_props.sv \ + $(RTL_PATH)/formal/gateway_a_formal.sv \ + $(RTL_PATH)/formal/gateway_a_formal_props.sv \ $(RTL_PATH)/formal/sdio_formal.sv \ $(RTL_PATH)/formal/sdio_formal_props.sv \ $(RTL_PATH)/top/rib_bus.sv \ @@ -140,10 +149,12 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \ $(ROOT_PATH)/rtl/ip/peripheral/dma_pkg.sv \ $(ROOT_PATH)/rtl/ip/peripheral/dma_req_if.sv \ $(ROOT_PATH)/rtl/ip/peripheral/dma_axi4_master.sv \ + $(ROOT_PATH)/rtl/ip/multimedia/apu_dma.sv \ $(ROOT_PATH)/rtl/ip/peripheral/dma_core.sv \ $(ROOT_PATH)/rtl/managed/clusterip/common/rtl/clkrst/counter.sv \ $(RTL_PATH)/top/rcu.sv \ $(RTL_PATH)/top/pll_rcu_controller.sv \ + $(RTL_PATH)/top/hp_axi4_mux3.sv \ $(ROOT_PATH)/rtl/managed/clusterip/common/rtl/interface/apb4_if.sv \ $(ROOT_PATH)/rtl/managed/clusterip/common/rtl/interface/ribp_if.sv \ $(ROOT_PATH)/rtl/managed/clusterip/common/rtl/interface/apb4_pure_if.sv \ @@ -187,14 +198,14 @@ $(FORMAL_DIR)/%/prove.sby: $(FORMAL_DIR)/%/design.v $(RTL_PATH)/formal/%_formal_ $(FORMAL_SBY_GENERATOR) python3 $(FORMAL_SBY_GENERATOR) --top $*_formal --input $< \ --properties $(RTL_PATH)/formal/$*_formal_props.sv --solver $(FORMAL_SOLVER) \ - --mode $(if $(filter dma sdio onchip_ram,$*),bmc,prove) --depth $(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))))) --output $@ + --mode $(if $(filter dma apu gateway_a sdio onchip_ram,$*),bmc,prove) --depth $(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_DEPTH),$(if $(filter apu,$*),$(FORMAL_APU_DEPTH),$(if $(filter gateway_a,$*),$(FORMAL_GATEWAY_A_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))))))) --output $@ @if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi $(FORMAL_DIR)/%/cover.sby: $(FORMAL_DIR)/%/design.v $(RTL_PATH)/formal/%_formal_props.sv \ $(FORMAL_SBY_GENERATOR) python3 $(FORMAL_SBY_GENERATOR) --top $*_formal --input $< \ --properties $(RTL_PATH)/formal/$*_formal_props.sv --solver $(FORMAL_SOLVER) \ - --mode cover --depth $(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_COVER_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_COVER_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))))) \ + --mode cover --depth $(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_COVER_DEPTH),$(if $(filter apu,$*),$(FORMAL_APU_DEPTH),$(if $(filter gateway_a,$*),$(FORMAL_GATEWAY_A_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_COVER_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))))))) \ $(if $(filter $(FORMAL_COMPACT_COVER_TARGETS),$*),--no-vcd) --output $@ @if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi @@ -218,7 +229,7 @@ $(FORMAL_DIR)/%/prove.stamp: $(FORMAL_DIR)/%/prove.sby $(FORMAL_SOLVER_WRAPPER) --log $(@D)/prove.log --result $(@D)/result-prove.json \ --env RETROSOC_BITWUZLA=$(FORMAL_BITWUZLA) \ --env PATH=$(FORMAL_SOLVER_DIR):$(PATH) \ - -- timeout --foreground --kill-after=5s $(if $(filter dma,$*),$(FORMAL_DMA_TIMEOUT),$(if $(filter sdio,$*),$(FORMAL_SDIO_TIMEOUT),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_TIMEOUT),$(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(if $(filter i2c,$*),$(FORMAL_I2C_TIMEOUT),$(FORMAL_TIMEOUT))))))s $(FORMAL_SBY) \ + -- timeout --foreground --kill-after=5s $(FORMAL_TARGET_TIMEOUT)s $(FORMAL_SBY) \ -f -d $(@D)/prove $< @test "$$(awk '{print $$1}' $(@D)/prove/status)" = PASS @touch $@ @@ -228,7 +239,7 @@ $(FORMAL_DIR)/%/cover.stamp: $(FORMAL_DIR)/%/cover.sby $(FORMAL_SOLVER_WRAPPER) --log $(@D)/cover.log --result $(@D)/result-cover.json \ --env RETROSOC_BITWUZLA=$(FORMAL_BITWUZLA) \ --env PATH=$(FORMAL_SOLVER_DIR):$(PATH) \ - -- timeout --foreground --kill-after=5s $(if $(filter dma,$*),$(FORMAL_DMA_TIMEOUT),$(if $(filter sdio,$*),$(FORMAL_SDIO_TIMEOUT),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_TIMEOUT),$(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(if $(filter i2c,$*),$(FORMAL_I2C_TIMEOUT),$(FORMAL_TIMEOUT))))))s $(FORMAL_SBY) \ + -- timeout --foreground --kill-after=5s $(FORMAL_TARGET_TIMEOUT)s $(FORMAL_SBY) \ -f -d $(@D)/cover $< @test "$$(awk '{print $$1}' $(@D)/cover/status)" = PASS @touch $@ @@ -291,6 +302,10 @@ formal-opipsram: $(FORMAL_DIR)/opipsram/.stamp | manifest formal-dma: $(FORMAL_DIR)/dma/.stamp | manifest +formal-apu: $(FORMAL_DIR)/apu/.stamp | manifest + +formal-gateway-a: $(FORMAL_DIR)/gateway_a/.stamp | manifest + formal-sdio: $(FORMAL_DIR)/sdio/.stamp | manifest formal-doctor: @@ -299,4 +314,4 @@ formal-doctor: formal-clean: python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(FORMAL_DIR) -.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-clint formal-dvp formal-i2s formal-psram formal-onchip-ram formal-opipsram formal-dma formal-sdio formal-doctor formal-clean \ No newline at end of file +.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-clint formal-dvp formal-i2s formal-psram formal-onchip-ram formal-opipsram formal-dma formal-apu formal-gateway-a formal-sdio formal-doctor formal-clean diff --git a/rtl/mini/top/apb4_periph.sv b/rtl/mini/top/apb4_periph.sv index f9d92b43..bf1dca05 100644 --- a/rtl/mini/top/apb4_periph.sv +++ b/rtl/mini/top/apb4_periph.sv @@ -29,6 +29,7 @@ module apb4_periph ( input logic apu_owner_lock_i, input logic apu_quiesce_i, input logic apu_reset_i, + input logic [7:0] apu_bridge_epoch_i, input logic jpeg_quiesce_i, input logic jpeg_reset_i, axi4_if.slave cfg_axi4, @@ -50,6 +51,7 @@ module apb4_periph ( axi4_if.master sdio0_axi4, axi4_if.master sdio1_axi4, axi4_if.master usb2_axi4, + axi4_if.master apu_axi4, axi4_if.master jpeg_axi4, sysctrl_if.dut sysctrl, pll_ctrl_if.sysctrl pll_ctrl, @@ -100,6 +102,24 @@ axi4_stream_if #( .aclk (clk_i), .aresetn(rst_n_i) ); + axi4_stream_if #( + .DATA_WIDTH(32), + .ID_WIDTH (1), + .DEST_WIDTH(1), + .USER_WIDTH(1) + ) u_dma_i2s_tx_axis_if ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); + axi4_stream_if #( + .DATA_WIDTH(32), + .ID_WIDTH (1), + .DEST_WIDTH(1), + .USER_WIDTH(1) + ) u_dma_i2s_rx_axis_if ( + .aclk (clk_i), + .aresetn(rst_n_i) + ); axi4_stream_if #( .DATA_WIDTH(32), .ID_WIDTH (1), @@ -157,6 +177,7 @@ axi4_stream_if #( logic s_dma_crypto_out_proc; logic s_crypto_irq; logic s_apu_irq_raw; + logic s_i2s_tx_underrun_evt, s_i2s_rx_overrun_evt; logic s_jpeg_irq_raw; logic s_usb2_irq; logic s_tim0_irq, s_tim1_irq; @@ -405,16 +426,18 @@ axi4_stream_if #( ); apb4_i2s u_apb4_i2s ( - .clk_i (clk_i), - .rst_n_i (rst_n_i), - .clk_aud_i (clk_aud_i), - .rst_aud_n_i (rst_aud_n_i), - .dma_tx_stall_o(s_dma_i2s_tx_stall), - .dma_rx_stall_o(s_dma_i2s_rx_stall), - .apb4 (u_i2s_apb4_if), - .tx_axis (u_i2s_tx_axis_if), - .rx_axis (u_i2s_rx_axis_if), - .i2s (i2s) + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .clk_aud_i (clk_aud_i), + .rst_aud_n_i (rst_aud_n_i), + .dma_tx_stall_o (s_dma_i2s_tx_stall), + .dma_rx_stall_o (s_dma_i2s_rx_stall), + .tx_underrun_evt_o(s_i2s_tx_underrun_evt), + .rx_overrun_evt_o (s_i2s_rx_overrun_evt), + .apb4 (u_i2s_apb4_if), + .tx_axis (u_i2s_tx_axis_if), + .rx_axis (u_i2s_rx_axis_if), + .i2s (i2s) ); apb4_ws2812 u_apb4_ws2812 ( @@ -447,8 +470,8 @@ axi4_stream_if #( .hw_trg (u_dma_req_if), .apb4 (u_dma_apb4_if), .axi4 (dma_axi4), - .i2s_tx_axis (u_i2s_tx_axis_if), - .i2s_rx_axis (u_i2s_rx_axis_if), + .i2s_tx_axis (u_dma_i2s_tx_axis_if), + .i2s_rx_axis (u_dma_i2s_rx_axis_if), .dvp_rx_axis (u_dvp_rx_axis_if), .crypto_in_axis (u_crypto_in_axis_if), .crypto_out_axis(u_crypto_out_axis_if) @@ -477,15 +500,23 @@ axi4_stream_if #( ); 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) + .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), + .bridge_epoch_i (apu_bridge_epoch_i), + .i2s_tx_underrun_i(s_i2s_tx_underrun_evt), + .i2s_rx_overrun_i (s_i2s_rx_overrun_evt), + .apb4 (u_apu_apb4_if), + .axi4 (apu_axi4), + .dma_tx_axis (u_dma_i2s_tx_axis_if), + .dma_rx_axis (u_dma_i2s_rx_axis_if), + .i2s_tx_axis (u_i2s_tx_axis_if), + .i2s_rx_axis (u_i2s_rx_axis_if), + .idle_o (apu_idle_o), + .irq_o (s_apu_irq_raw) ); apb4_sysctrl u_apb4_sysctrl ( diff --git a/rtl/mini/top/axi4_async_bridge.sv b/rtl/mini/top/axi4_async_bridge.sv index 577def70..a1be95d8 100644 --- a/rtl/mini/top/axi4_async_bridge.sv +++ b/rtl/mini/top/axi4_async_bridge.sv @@ -43,6 +43,9 @@ module axi4_async_bridge #( logic s_clear_dst_q; logic s_clear_src_pulse; logic s_clear_dst_pulse; + logic s_dst_rst_n_src; + logic s_dst_rst_n_src_q; + logic s_dst_reset_evt; logic [7:0] s_epoch_d, s_epoch_q; logic s_aw_src_clear_busy, s_aw_dst_clear_busy; logic s_w_src_clear_busy, s_w_dst_clear_busy; @@ -68,9 +71,19 @@ module axi4_async_bridge #( .dat_i (clear_i), .dat_o (s_clear_dst) ); + cdc_sync #( + .STAGE (2), + .DATA_WIDTH(1) + ) u_dst_reset_src_sync ( + .clk_i (src_clk_i), + .rst_n_i(src_rst_n_i), + .dat_i (dst_rst_n_i), + .dat_o (s_dst_rst_n_src) + ); assign s_clear_src_pulse = s_clear_src && !s_clear_q; assign s_clear_dst_pulse = s_clear_dst && !s_clear_dst_q; + assign s_dst_reset_evt = s_dst_rst_n_src_q && !s_dst_rst_n_src; assign s_aw_src_data = { src_axi4.awid, @@ -238,16 +251,19 @@ module axi4_async_bridge #( s_b_src_clear_busy || s_b_dst_clear_busy || s_ar_src_clear_busy || s_ar_dst_clear_busy || s_r_src_clear_busy || s_r_dst_clear_busy; - assign s_epoch_d = (s_clear_src_pulse && !clear_busy_o) ? s_epoch_q + 1'b1 : s_epoch_q; + assign s_epoch_d = ((s_clear_src_pulse && !clear_busy_o) || s_dst_reset_evt) ? + s_epoch_q + 1'b1 : s_epoch_q; assign epoch_o = s_epoch_q; always_ff @(posedge src_clk_i or negedge src_rst_n_i) begin if (!src_rst_n_i) begin - s_clear_q <= 1'b0; - s_epoch_q <= '0; + s_clear_q <= 1'b0; + s_dst_rst_n_src_q <= 1'b0; + s_epoch_q <= '0; end else begin - s_clear_q <= s_clear_src; - s_epoch_q <= s_epoch_d; + s_clear_q <= s_clear_src; + s_dst_rst_n_src_q <= s_dst_rst_n_src; + s_epoch_q <= s_epoch_d; end end diff --git a/rtl/mini/top/axi4_target_guard.sv b/rtl/mini/top/axi4_target_guard.sv index 185e6aaa..664f3306 100644 --- a/rtl/mini/top/axi4_target_guard.sv +++ b/rtl/mini/top/axi4_target_guard.sv @@ -106,6 +106,7 @@ module axi4_target_guard #( logic s_write_progress; logic s_read_timeout_enable; logic s_write_timeout_enable; + logic s_read_missing_last; assign s_ar_push_data = { source.arid, @@ -212,11 +213,14 @@ module axi4_target_guard #( (sink.arready || s_timeout_read || s_isolated_q)) || ((s_read_state_q == ReadIdle) && s_isolated_q && !s_ar_empty); + assign s_read_missing_last = (s_read_state_q == ReadForward) && sink.rvalid && + (s_read_remaining_q == 9'd1) && !sink.rlast; assign source.rid = (s_read_state_q == ReadSynthetic) ? s_read_id_q : sink.rid; assign source.rdata = (s_read_state_q == ReadSynthetic) ? '0 : sink.rdata; - assign source.rresp = (s_read_state_q == ReadSynthetic) ? `AXI4_RESP_SLAVE_ERROR : sink.rresp; + assign source.rresp = ((s_read_state_q == ReadSynthetic) || s_read_missing_last) ? + `AXI4_RESP_SLAVE_ERROR : sink.rresp; assign source.rlast = (s_read_state_q == ReadSynthetic) ? - (s_read_remaining_q == 9'd1) : sink.rlast; + (s_read_remaining_q == 9'd1) : (sink.rlast || s_read_missing_last); assign source.ruser = (s_read_state_q == ReadSynthetic) ? '0 : sink.ruser; assign source.rvalid = (s_read_state_q == ReadSynthetic) || ((s_read_state_q == ReadForward) && sink.rvalid); diff --git a/rtl/mini/top/hp_axi4_mux3.sv b/rtl/mini/top/hp_axi4_mux3.sv index 91a9d308..a7874b2e 100644 --- a/rtl/mini/top/hp_axi4_mux3.sv +++ b/rtl/mini/top/hp_axi4_mux3.sv @@ -2,34 +2,89 @@ // SPDX-License-Identifier: MulanPSL-2.0 // Serializes the three HP compatibility ports onto one legacy AXI4 master. -module hp_axi4_mux3 ( - input logic clk_i, - input logic rst_n_i, - axi4_if.slave icache, - axi4_if.slave dcache, - axi4_if.slave mmio, - axi4_if.master axi4 +module hp_axi4_mux3 #( + parameter bit RoundRobin = 1'b0, + parameter bit Client0EpochAware = 1'b0 +) ( + input logic clk_i, + input logic rst_n_i, + input logic [7:0] epoch_i, + axi4_if.slave icache, + axi4_if.slave dcache, + axi4_if.slave mmio, + axi4_if.master axi4 ); - typedef enum logic { + typedef enum logic [2:0] { Idle, - Active + Address, + Active, + RecoverRead, + RecoverWriteData, + RecoverWriteResponse } state_e; state_e s_state_d, s_state_q; - logic s_state_bits_q; + logic [2:0] s_state_bits_q; logic [1:0] s_owner_d, s_owner_q; logic s_write_d, s_write_q; logic [1:0] s_selected; + logic [1:0] s_route_owner; + logic [1:0] s_rr_selected; + logic [2:0] s_rr_grant; + logic [2:0] s_rr_request; + logic s_rr_valid; logic s_selected_write; + logic s_route_write; + logic s_addr_state; + logic s_addr_valid; logic s_addr_accept; logic s_terminal; + logic s_recovery_terminal; + logic s_recovery_write_last; + logic s_write_data_accept_last; + logic s_recover; + logic s_write_data_done_d, s_write_data_done_q; + logic [7:0] s_epoch_q; assign s_state_q = state_e'(s_state_bits_q); + assign s_rr_request = { + mmio.arvalid || mmio.awvalid, dcache.arvalid || dcache.awvalid, icache.arvalid || icache.awvalid + }; + assign s_route_owner = (s_state_q == Idle) ? s_selected : s_owner_q; + assign s_route_write = (s_state_q == Idle) ? s_selected_write : s_write_q; + assign s_addr_state = (s_state_q == Idle) || (s_state_q == Address); + assign s_addr_valid = s_route_write ? + ((s_route_owner == 2'd2) ? mmio.awvalid : + (s_route_owner == 2'd1) ? dcache.awvalid : icache.awvalid) : + ((s_route_owner == 2'd2) ? mmio.arvalid : + (s_route_owner == 2'd1) ? dcache.arvalid : icache.arvalid); + assign s_recover = epoch_i != s_epoch_q; + + round_robin_arbiter #( + .CLIENTS (3), + .INDEX_WIDTH(2) + ) u_round_robin ( + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .advance_i (s_addr_accept && RoundRobin), + .request_i (s_rr_request), + .grant_o (s_rr_grant), + .selected_o(s_rr_selected), + .valid_o (s_rr_valid) + ); + always_comb begin s_selected = 2'd0; s_selected_write = 1'b0; - if (mmio.arvalid || mmio.awvalid) begin + if (RoundRobin && s_rr_valid) begin + s_selected = s_rr_selected; + case (s_rr_selected) + 2'd2: s_selected_write = mmio.awvalid && !mmio.arvalid; + 2'd1: s_selected_write = dcache.awvalid && !dcache.arvalid; + default: s_selected_write = icache.awvalid && !icache.arvalid; + endcase + end else if (mmio.arvalid || mmio.awvalid) begin s_selected = 2'd2; s_selected_write = mmio.awvalid && !mmio.arvalid; end else if (dcache.arvalid || dcache.awvalid) begin @@ -41,70 +96,69 @@ module hp_axi4_mux3 ( end end - assign axi4.awid = (s_state_q == Idle) ? - ((s_selected == 2'd2) ? mmio.awid : - (s_selected == 2'd1) ? dcache.awid : icache.awid) : '0; - assign axi4.awaddr = (s_selected == 2'd2) ? mmio.awaddr : - (s_selected == 2'd1) ? dcache.awaddr : icache.awaddr; - assign axi4.awlen = (s_selected == 2'd2) ? mmio.awlen : - (s_selected == 2'd1) ? dcache.awlen : icache.awlen; - assign axi4.awsize = (s_selected == 2'd2) ? mmio.awsize : - (s_selected == 2'd1) ? dcache.awsize : icache.awsize; - assign axi4.awburst = (s_selected == 2'd2) ? mmio.awburst : - (s_selected == 2'd1) ? dcache.awburst : icache.awburst; - assign axi4.awlock = (s_selected == 2'd2) ? mmio.awlock : - (s_selected == 2'd1) ? dcache.awlock : icache.awlock; - assign axi4.awcache = (s_selected == 2'd2) ? mmio.awcache : - (s_selected == 2'd1) ? dcache.awcache : icache.awcache; - assign axi4.awprot = (s_selected == 2'd2) ? mmio.awprot : - (s_selected == 2'd1) ? dcache.awprot : icache.awprot; - assign axi4.awqos = (s_selected == 2'd2) ? mmio.awqos : - (s_selected == 2'd1) ? dcache.awqos : icache.awqos; - assign axi4.awregion = (s_selected == 2'd2) ? mmio.awregion : - (s_selected == 2'd1) ? dcache.awregion : icache.awregion; + assign axi4.awid = (s_route_owner == 2'd2) ? mmio.awid : + (s_route_owner == 2'd1) ? dcache.awid : icache.awid; + assign axi4.awaddr = (s_route_owner == 2'd2) ? mmio.awaddr : + (s_route_owner == 2'd1) ? dcache.awaddr : icache.awaddr; + assign axi4.awlen = (s_route_owner == 2'd2) ? mmio.awlen : + (s_route_owner == 2'd1) ? dcache.awlen : icache.awlen; + assign axi4.awsize = (s_route_owner == 2'd2) ? mmio.awsize : + (s_route_owner == 2'd1) ? dcache.awsize : icache.awsize; + assign axi4.awburst = (s_route_owner == 2'd2) ? mmio.awburst : + (s_route_owner == 2'd1) ? dcache.awburst : icache.awburst; + assign axi4.awlock = (s_route_owner == 2'd2) ? mmio.awlock : + (s_route_owner == 2'd1) ? dcache.awlock : icache.awlock; + assign axi4.awcache = (s_route_owner == 2'd2) ? mmio.awcache : + (s_route_owner == 2'd1) ? dcache.awcache : icache.awcache; + assign axi4.awprot = (s_route_owner == 2'd2) ? mmio.awprot : + (s_route_owner == 2'd1) ? dcache.awprot : icache.awprot; + assign axi4.awqos = (s_route_owner == 2'd2) ? mmio.awqos : + (s_route_owner == 2'd1) ? dcache.awqos : icache.awqos; + assign axi4.awregion = (s_route_owner == 2'd2) ? mmio.awregion : + (s_route_owner == 2'd1) ? dcache.awregion : icache.awregion; assign axi4.awuser = '0; - assign axi4.awvalid = (s_state_q == Idle) && s_selected_write && - ((s_selected == 2'd2) ? mmio.awvalid : - (s_selected == 2'd1) ? dcache.awvalid : icache.awvalid); + assign axi4.awvalid = s_addr_state && !s_recover && s_route_write && + ((s_route_owner == 2'd2) ? mmio.awvalid : + (s_route_owner == 2'd1) ? dcache.awvalid : icache.awvalid); - assign mmio.awready = (s_state_q == Idle) && (s_selected == 2'd2) && - s_selected_write && axi4.awready; - assign dcache.awready = (s_state_q == Idle) && (s_selected == 2'd1) && - s_selected_write && axi4.awready; - assign icache.awready = (s_state_q == Idle) && (s_selected == 2'd0) && - s_selected_write && axi4.awready; + assign mmio.awready = s_addr_state && !s_recover && (s_route_owner == 2'd2) && + s_route_write && axi4.awready; + assign dcache.awready = s_addr_state && !s_recover && (s_route_owner == 2'd1) && + s_route_write && axi4.awready; + assign icache.awready = s_addr_state && !s_recover && (s_route_owner == 2'd0) && + s_route_write && axi4.awready; - assign axi4.arid = (s_selected == 2'd2) ? mmio.arid : - (s_selected == 2'd1) ? dcache.arid : icache.arid; - assign axi4.araddr = (s_selected == 2'd2) ? mmio.araddr : - (s_selected == 2'd1) ? dcache.araddr : icache.araddr; - assign axi4.arlen = (s_selected == 2'd2) ? mmio.arlen : - (s_selected == 2'd1) ? dcache.arlen : icache.arlen; - assign axi4.arsize = (s_selected == 2'd2) ? mmio.arsize : - (s_selected == 2'd1) ? dcache.arsize : icache.arsize; - assign axi4.arburst = (s_selected == 2'd2) ? mmio.arburst : - (s_selected == 2'd1) ? dcache.arburst : icache.arburst; - assign axi4.arlock = (s_selected == 2'd2) ? mmio.arlock : - (s_selected == 2'd1) ? dcache.arlock : icache.arlock; - assign axi4.arcache = (s_selected == 2'd2) ? mmio.arcache : - (s_selected == 2'd1) ? dcache.arcache : icache.arcache; - assign axi4.arprot = (s_selected == 2'd2) ? mmio.arprot : - (s_selected == 2'd1) ? dcache.arprot : icache.arprot; - assign axi4.arqos = (s_selected == 2'd2) ? mmio.arqos : - (s_selected == 2'd1) ? dcache.arqos : icache.arqos; - assign axi4.arregion = (s_selected == 2'd2) ? mmio.arregion : - (s_selected == 2'd1) ? dcache.arregion : icache.arregion; + assign axi4.arid = (s_route_owner == 2'd2) ? mmio.arid : + (s_route_owner == 2'd1) ? dcache.arid : icache.arid; + assign axi4.araddr = (s_route_owner == 2'd2) ? mmio.araddr : + (s_route_owner == 2'd1) ? dcache.araddr : icache.araddr; + assign axi4.arlen = (s_route_owner == 2'd2) ? mmio.arlen : + (s_route_owner == 2'd1) ? dcache.arlen : icache.arlen; + assign axi4.arsize = (s_route_owner == 2'd2) ? mmio.arsize : + (s_route_owner == 2'd1) ? dcache.arsize : icache.arsize; + assign axi4.arburst = (s_route_owner == 2'd2) ? mmio.arburst : + (s_route_owner == 2'd1) ? dcache.arburst : icache.arburst; + assign axi4.arlock = (s_route_owner == 2'd2) ? mmio.arlock : + (s_route_owner == 2'd1) ? dcache.arlock : icache.arlock; + assign axi4.arcache = (s_route_owner == 2'd2) ? mmio.arcache : + (s_route_owner == 2'd1) ? dcache.arcache : icache.arcache; + assign axi4.arprot = (s_route_owner == 2'd2) ? mmio.arprot : + (s_route_owner == 2'd1) ? dcache.arprot : icache.arprot; + assign axi4.arqos = (s_route_owner == 2'd2) ? mmio.arqos : + (s_route_owner == 2'd1) ? dcache.arqos : icache.arqos; + assign axi4.arregion = (s_route_owner == 2'd2) ? mmio.arregion : + (s_route_owner == 2'd1) ? dcache.arregion : icache.arregion; assign axi4.aruser = '0; - assign axi4.arvalid = (s_state_q == Idle) && !s_selected_write && - ((s_selected == 2'd2) ? mmio.arvalid : - (s_selected == 2'd1) ? dcache.arvalid : icache.arvalid); + assign axi4.arvalid = s_addr_state && !s_recover && !s_route_write && + ((s_route_owner == 2'd2) ? mmio.arvalid : + (s_route_owner == 2'd1) ? dcache.arvalid : icache.arvalid); - assign mmio.arready = (s_state_q == Idle) && (s_selected == 2'd2) && - !s_selected_write && axi4.arready; - assign dcache.arready = (s_state_q == Idle) && (s_selected == 2'd1) && - !s_selected_write && axi4.arready; - assign icache.arready = (s_state_q == Idle) && (s_selected == 2'd0) && - !s_selected_write && axi4.arready; + assign mmio.arready = s_addr_state && !s_recover && (s_route_owner == 2'd2) && + !s_route_write && axi4.arready; + assign dcache.arready = s_addr_state && !s_recover && (s_route_owner == 2'd1) && + !s_route_write && axi4.arready; + assign icache.arready = s_addr_state && !s_recover && (s_route_owner == 2'd0) && + !s_route_write && axi4.arready; assign axi4.wdata = (s_owner_q == 2'd2) ? mmio.wdata : (s_owner_q == 2'd1) ? dcache.wdata : icache.wdata; @@ -116,44 +170,56 @@ module hp_axi4_mux3 ( assign axi4.wvalid = (s_state_q == Active) && s_write_q && ((s_owner_q == 2'd2) ? mmio.wvalid : (s_owner_q == 2'd1) ? dcache.wvalid : icache.wvalid); - assign mmio.wready = (s_state_q == Active) && s_write_q && (s_owner_q == 2'd2) && axi4.wready; - assign dcache.wready = (s_state_q == Active) && s_write_q && (s_owner_q == 2'd1) && axi4.wready; - assign icache.wready = (s_state_q == Active) && s_write_q && (s_owner_q == 2'd0) && axi4.wready; + assign mmio.wready = s_write_q && (s_owner_q == 2'd2) && + (((s_state_q == Active) && axi4.wready) || (s_state_q == RecoverWriteData)); + assign dcache.wready = s_write_q && (s_owner_q == 2'd1) && + (((s_state_q == Active) && axi4.wready) || (s_state_q == RecoverWriteData)); + assign icache.wready = s_write_q && (s_owner_q == 2'd0) && + (((s_state_q == Active) && axi4.wready) || (s_state_q == RecoverWriteData)); - assign mmio.bid = axi4.bid; - assign dcache.bid = axi4.bid; - assign icache.bid = axi4.bid; - assign mmio.bresp = axi4.bresp; - assign dcache.bresp = axi4.bresp; - assign icache.bresp = axi4.bresp; + assign mmio.bid = (s_state_q == RecoverWriteResponse) ? 1'b0 : axi4.bid; + assign dcache.bid = (s_state_q == RecoverWriteResponse) ? 1'b0 : axi4.bid; + assign icache.bid = (s_state_q == RecoverWriteResponse) ? 1'b0 : axi4.bid; + assign mmio.bresp = (s_state_q == RecoverWriteResponse) ? 2'b11 : axi4.bresp; + assign dcache.bresp = (s_state_q == RecoverWriteResponse) ? 2'b11 : axi4.bresp; + assign icache.bresp = (s_state_q == RecoverWriteResponse) ? 2'b11 : axi4.bresp; assign mmio.buser = '0; assign dcache.buser = '0; assign icache.buser = '0; - assign mmio.bvalid = (s_state_q == Active) && s_write_q && (s_owner_q == 2'd2) && axi4.bvalid; - assign dcache.bvalid = (s_state_q == Active) && s_write_q && (s_owner_q == 2'd1) && axi4.bvalid; - assign icache.bvalid = (s_state_q == Active) && s_write_q && (s_owner_q == 2'd0) && axi4.bvalid; + assign mmio.bvalid = s_write_q && (s_owner_q == 2'd2) && + (((s_state_q == Active) && !s_recover && axi4.bvalid) || + (s_state_q == RecoverWriteResponse)); + assign dcache.bvalid = s_write_q && (s_owner_q == 2'd1) && + (((s_state_q == Active) && !s_recover && axi4.bvalid) || + (s_state_q == RecoverWriteResponse)); + assign icache.bvalid = s_write_q && (s_owner_q == 2'd0) && + (((s_state_q == Active) && !s_recover && axi4.bvalid) || + (s_state_q == RecoverWriteResponse)); assign axi4.bready = ((s_state_q == Active) && s_write_q) ? ((s_owner_q == 2'd2) ? mmio.bready : (s_owner_q == 2'd1) ? dcache.bready : icache.bready) : 1'b0; - assign mmio.rid = axi4.rid; - assign dcache.rid = axi4.rid; - assign icache.rid = axi4.rid; - assign mmio.rdata = axi4.rdata; - assign dcache.rdata = axi4.rdata; - assign icache.rdata = axi4.rdata; - assign mmio.rresp = axi4.rresp; - assign dcache.rresp = axi4.rresp; - assign icache.rresp = axi4.rresp; - assign mmio.rlast = axi4.rlast; - assign dcache.rlast = axi4.rlast; - assign icache.rlast = axi4.rlast; + assign mmio.rid = (s_state_q == RecoverRead) ? 1'b0 : axi4.rid; + assign dcache.rid = (s_state_q == RecoverRead) ? 1'b0 : axi4.rid; + assign icache.rid = (s_state_q == RecoverRead) ? 1'b0 : axi4.rid; + assign mmio.rdata = (s_state_q == RecoverRead) ? 32'd0 : axi4.rdata; + assign dcache.rdata = (s_state_q == RecoverRead) ? 32'd0 : axi4.rdata; + assign icache.rdata = (s_state_q == RecoverRead) ? 32'd0 : axi4.rdata; + assign mmio.rresp = (s_state_q == RecoverRead) ? 2'b11 : axi4.rresp; + assign dcache.rresp = (s_state_q == RecoverRead) ? 2'b11 : axi4.rresp; + assign icache.rresp = (s_state_q == RecoverRead) ? 2'b11 : axi4.rresp; + assign mmio.rlast = (s_state_q == RecoverRead) ? 1'b1 : axi4.rlast; + assign dcache.rlast = (s_state_q == RecoverRead) ? 1'b1 : axi4.rlast; + assign icache.rlast = (s_state_q == RecoverRead) ? 1'b1 : axi4.rlast; assign mmio.ruser = '0; assign dcache.ruser = '0; assign icache.ruser = '0; - assign mmio.rvalid = (s_state_q == Active) && !s_write_q && (s_owner_q == 2'd2) && axi4.rvalid; - assign dcache.rvalid = (s_state_q == Active) && !s_write_q && (s_owner_q == 2'd1) && axi4.rvalid; - assign icache.rvalid = (s_state_q == Active) && !s_write_q && (s_owner_q == 2'd0) && axi4.rvalid; + assign mmio.rvalid = !s_write_q && (s_owner_q == 2'd2) && + (((s_state_q == Active) && !s_recover && axi4.rvalid) || (s_state_q == RecoverRead)); + assign dcache.rvalid = !s_write_q && (s_owner_q == 2'd1) && + (((s_state_q == Active) && !s_recover && axi4.rvalid) || (s_state_q == RecoverRead)); + assign icache.rvalid = !s_write_q && (s_owner_q == 2'd0) && + (((s_state_q == Active) && !s_recover && axi4.rvalid) || (s_state_q == RecoverRead)); assign axi4.rready = ((s_state_q == Active) && !s_write_q) ? ((s_owner_q == 2'd2) ? mmio.rready : (s_owner_q == 2'd1) ? dcache.rready : icache.rready) : 1'b0; @@ -161,21 +227,68 @@ module hp_axi4_mux3 ( assign s_addr_accept = (axi4.awvalid && axi4.awready) || (axi4.arvalid && axi4.arready); assign s_terminal = (s_write_q && axi4.bvalid && axi4.bready) || (!s_write_q && axi4.rvalid && axi4.rready && axi4.rlast); + assign s_recovery_terminal = ((s_state_q == RecoverWriteResponse) && + ((s_owner_q == 2'd2) ? mmio.bready : + (s_owner_q == 2'd1) ? dcache.bready : icache.bready)) || + ((s_state_q == RecoverRead) && + ((s_owner_q == 2'd2) ? mmio.rready : + (s_owner_q == 2'd1) ? dcache.rready : icache.rready)); + assign s_recovery_write_last = (s_state_q == RecoverWriteData) && + ((s_owner_q == 2'd2) ? (mmio.wvalid && mmio.wready && mmio.wlast) : + (s_owner_q == 2'd1) ? (dcache.wvalid && dcache.wready && dcache.wlast) : + (icache.wvalid && icache.wready && icache.wlast)); + assign s_write_data_accept_last = (s_state_q == Active) && s_write_q && + axi4.wvalid && axi4.wready && axi4.wlast; always_comb begin - s_state_d = s_state_q; - s_owner_d = s_owner_q; - s_write_d = s_write_q; - if ((s_state_q == Idle) && s_addr_accept) begin - s_state_d = Active; - s_owner_d = s_selected; - s_write_d = axi4.awvalid && axi4.awready; + s_state_d = s_state_q; + s_owner_d = s_owner_q; + s_write_d = s_write_q; + s_write_data_done_d = s_write_data_done_q; + if (s_recover) begin + unique case (s_state_q) + Active: begin + if (Client0EpochAware && (s_owner_q == 2'd0)) begin + s_state_d = Idle; + end else if (s_write_q) begin + s_state_d = (s_write_data_done_q || s_write_data_accept_last) ? + RecoverWriteResponse : RecoverWriteData; + end else begin + s_state_d = RecoverRead; + end + end + RecoverRead, RecoverWriteData, RecoverWriteResponse: s_state_d = s_state_q; + default: s_state_d = Idle; + endcase + end else begin + unique case (s_state_q) + Idle: begin + if (s_addr_valid) begin + s_owner_d = s_selected; + s_write_d = s_selected_write; + s_write_data_done_d = 1'b0; + s_state_d = s_addr_accept ? Active : Address; + end + end + Address: if (s_addr_accept) s_state_d = Active; + Active: begin + if (s_write_data_accept_last) begin + s_write_data_done_d = 1'b1; + end + if (s_terminal) s_state_d = Idle; + end + RecoverRead: if (s_recovery_terminal) s_state_d = Idle; + RecoverWriteData: begin + if (s_recovery_write_last) s_state_d = RecoverWriteResponse; + end + RecoverWriteResponse: if (s_recovery_terminal) s_state_d = Idle; + default: s_state_d = Idle; + endcase end - if ((s_state_q == Active) && s_terminal) s_state_d = Idle; end dffr #( - .DATA_WIDTH(1) + .DATA_WIDTH(3) ) u_state_dffr ( .clk_i (clk_i), .rst_n_i(rst_n_i), @@ -198,4 +311,23 @@ module hp_axi4_mux3 ( .dat_i (s_write_d), .dat_o (s_write_q) ); + dffr #( + .DATA_WIDTH(1) + ) u_write_data_done_dffr ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_write_data_done_d), + .dat_o (s_write_data_done_q) + ); + dffr #( + .DATA_WIDTH(8) + ) u_epoch_dffr ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (epoch_i), + .dat_o (s_epoch_q) + ); + + logic [2:0] s_unused_rr_grant; + assign s_unused_rr_grant = RoundRobin ? 3'd0 : s_rr_grant; endmodule diff --git a/rtl/mini/top/retrosoc.sv b/rtl/mini/top/retrosoc.sv index c630c5b4..2c3db466 100644 --- a/rtl/mini/top/retrosoc.sv +++ b/rtl/mini/top/retrosoc.sv @@ -87,6 +87,8 @@ module retrosoc ( u_ext_h_wide_axi4_if (.aclk(clk_pclk_i), .aresetn(rst_pclk_n_i)); axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(64), .ID_WIDTH(3), .USER_WIDTH(1)) u_jpeg_wide_axi4_if (.aclk(clk_pclk_i), .aresetn(rst_pclk_n_i)); + axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH(1), .USER_WIDTH(1)) + u_apu_axi4_if (.aclk(clk_pclk_i), .aresetn(rst_pclk_n_i)); axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(64), .ID_WIDTH(3), .USER_WIDTH(1)) u_hp_icache_axi4_if (.aclk(clk_hp_i), .aresetn(rst_hp_n_i)); axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(64), .ID_WIDTH(3), .USER_WIDTH(1)) @@ -191,6 +193,7 @@ module retrosoc ( logic s_mgmt_router_idle; logic [ 7:0] s_data_plane_outstanding_read; logic [ 7:0] s_data_plane_outstanding_write; + logic [ 7:0] s_apu_bridge_epoch; logic s_data_plane_fault_valid; logic [ 2:0] s_data_plane_fault_master; logic [ 2:0] s_data_plane_fault_target; @@ -236,10 +239,10 @@ module retrosoc ( 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 [ 7:0] s_resource_idle_hp; + logic [ 7:0] s_resource_idle_pclk; + logic [ 7:0] s_resource_block_ack_hp; + logic [ 7:0] s_resource_block_ack_pclk; logic [ 6:0] s_resource_irq_raw; logic [ 6:0] s_resource_irq_lp; logic [ 6:0] s_resource_irq_hp; @@ -558,7 +561,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[6:0] | s_resource_reset[6:0]), + .resource_block_i (s_resource_quiesce | s_resource_reset), .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), @@ -572,6 +575,7 @@ core_wrapper u_core_wrapper ( .sdio1_axi4 (u_sdio1_axi4_if), .spisd_axi4 (u_spisd_axi4_if), .usb2_axi4 (u_usb2_axi4_if), + .apu_axi4 (u_apu_axi4_if), .jpeg_axi4 (u_jpeg_wide_axi4_if), .lp_data_axi4 (u_mgmt_data_axi4_if), .ext_h_axi4 (u_ext_h_wide_axi4_if), @@ -584,6 +588,7 @@ core_wrapper u_core_wrapper ( .idle_o (s_data_plane_idle), .flush_busy_o (s_data_plane_flush_busy), .ext_h_idle_o (s_ext_h_data_idle), + .apu_bridge_epoch_o (s_apu_bridge_epoch), .resource_idle_o (s_resource_idle_hp), .resource_block_ack_o(s_resource_block_ack_hp), .outstanding_read_o (s_data_plane_outstanding_read), @@ -598,7 +603,7 @@ core_wrapper u_core_wrapper ( cdc_sync #( .STAGE (2), - .DATA_WIDTH(7) + .DATA_WIDTH(8) ) u_resource_idle_sync ( .clk_i (clk_pclk_i), .rst_n_i(rst_pclk_n_i), @@ -607,7 +612,7 @@ core_wrapper u_core_wrapper ( ); cdc_sync #( .STAGE (2), - .DATA_WIDTH(7) + .DATA_WIDTH(8) ) u_resource_block_ack_sync ( .clk_i (clk_pclk_i), .rst_n_i(rst_pclk_n_i), @@ -804,6 +809,7 @@ core_wrapper u_core_wrapper ( .apu_owner_lock_i (s_resource_owner_lock[7]), .apu_quiesce_i (s_resource_quiesce[7]), .apu_reset_i (s_resource_reset[7]), + .apu_bridge_epoch_i (s_apu_bridge_epoch), .jpeg_quiesce_i (s_resource_quiesce[6]), .jpeg_reset_i (s_resource_reset[6]), .cfg_axi4 (u_cfg_pclk_axi4_if), @@ -811,6 +817,7 @@ core_wrapper u_core_wrapper ( .sdio0_axi4 (u_sdio0_axi4_if), .sdio1_axi4 (u_sdio1_axi4_if), .usb2_axi4 (u_usb2_axi4_if), + .apu_axi4 (u_apu_axi4_if), .jpeg_axi4 (u_jpeg_wide_axi4_if), .psram_axi4 (u_data_qpi_axi4_if), .xpi_axi4 (u_data_xpi_axi4_if), @@ -899,13 +906,13 @@ core_wrapper u_core_wrapper ( ); assign s_resource_idle_combined = { - s_apu_idle, + s_apu_idle && s_resource_idle_pclk[7], s_jpeg_idle && s_resource_idle_pclk[6], s_ext_h_data_idle && s_resource_idle_pclk[5], s_resource_idle_pclk[4:0] }; assign s_resource_block_ack_combined = { - s_apu_idle && (s_resource_quiesce[7] || s_resource_reset[7]), s_resource_block_ack_pclk + s_apu_idle && s_resource_block_ack_pclk[7], s_resource_block_ack_pclk[6:0] }; logic [36:0] s_unused_domain_inputs; diff --git a/rtl/mini/top/soc_data_plane.sv b/rtl/mini/top/soc_data_plane.sv index 188c1569..1fb21bcf 100644 --- a/rtl/mini/top/soc_data_plane.sv +++ b/rtl/mini/top/soc_data_plane.sv @@ -16,7 +16,7 @@ module soc_data_plane ( input logic block_new_i, input logic recovery_i, input logic flush_i, - input logic [6:0] resource_block_i, + input logic [7:0] resource_block_i, input logic [1:0] mem_pad_mode_i, input logic ext_h_block_i, input logic [31:0] ext_h_read_base_i, @@ -30,6 +30,7 @@ module soc_data_plane ( axi4_if.slave sdio1_axi4, axi4_if.slave spisd_axi4, axi4_if.slave usb2_axi4, + axi4_if.slave apu_axi4, axi4_if.slave jpeg_axi4, axi4_if.slave lp_data_axi4, axi4_if.slave ext_h_axi4, @@ -42,8 +43,9 @@ module soc_data_plane ( output logic idle_o, output logic flush_busy_o, output logic ext_h_idle_o, - output logic [6:0] resource_idle_o, - output logic [6:0] resource_block_ack_o, + output logic [7:0] apu_bridge_epoch_o, + output logic [7:0] resource_idle_o, + output logic [7:0] resource_block_ack_o, output logic [7:0] outstanding_read_o, output logic [7:0] outstanding_write_o, output logic fault_valid_o, @@ -86,7 +88,7 @@ module soc_data_plane ( logic [ 7:0] unused_ext_epoch; logic [ 7:0] unused_jpeg_epoch; logic [ 2:0] s_unused_epoch; - logic [ 6:0] s_resource_block_hp; + logic [ 7:0] s_resource_block_hp; logic [ 7:0] s_master_idle; logic [ 7:0] s_master_block; logic [ 7:0] s_monitor_master_read_accept; @@ -193,7 +195,7 @@ module soc_data_plane ( .DATA_WIDTH(32), .ID_WIDTH (1), .USER_WIDTH(1) - ) u_io_idle_axi4[2] ( + ) u_io_idle_axi4[1] ( .aclk (clk_io_i), .aresetn(rst_io_n_i) ); @@ -262,7 +264,7 @@ module soc_data_plane ( ); cdc_sync #( .STAGE (2), - .DATA_WIDTH(7) + .DATA_WIDTH(8) ) u_resource_block_sync ( .clk_i (clk_hp_i), .rst_n_i(rst_hp_n_i), @@ -275,11 +277,12 @@ module soc_data_plane ( s_resource_block_hp[6], 1'b0, s_resource_block_hp[4] || s_resource_block_hp[3], - s_resource_block_hp[2] || s_resource_block_hp[1], + s_resource_block_hp[7] || s_resource_block_hp[2] || s_resource_block_hp[1], s_resource_block_hp[0], 2'b00 }; assign resource_idle_o = { + s_master_idle[3], s_master_idle[6], s_master_idle[7], s_master_idle[4], @@ -315,10 +318,14 @@ module soc_data_plane ( .source(dma_axi4), .sink (u_io_lp_axi4[0]) ); - hp_axi4_mux3 u_io_gateway_a ( + hp_axi4_mux3 #( + .RoundRobin (1'b1), + .Client0EpochAware(1'b1) + ) u_io_gateway_a ( .clk_i (clk_io_i), .rst_n_i(rst_io_n_i), - .icache (u_io_idle_axi4[0]), + .epoch_i(unused_io_epoch[1]), + .icache (apu_axi4), .dcache (sdio0_axi4), .mmio (usb2_axi4), .axi4 (u_io_lp_axi4[1]) @@ -326,13 +333,13 @@ module soc_data_plane ( hp_axi4_mux3 u_io_gateway_b ( .clk_i (clk_io_i), .rst_n_i(rst_io_n_i), - .icache (u_io_idle_axi4[1]), + .epoch_i(unused_io_epoch[2]), + .icache (u_io_idle_axi4[0]), .dcache (spisd_axi4), .mmio (sdio1_axi4), .axi4 (u_io_lp_axi4[2]) ); - axi4_master_idle u_io_gateway_a_idle (.axi4(u_io_idle_axi4[0])); - axi4_master_idle u_io_gateway_b_idle (.axi4(u_io_idle_axi4[1])); + axi4_master_idle u_io_gateway_b_idle (.axi4(u_io_idle_axi4[0])); for (genvar master = 0; master < NumIoMasters; master++) begin : gen_io_master axi4_async_bridge #( @@ -638,6 +645,7 @@ module soc_data_plane ( assign flush_busy_o = (|s_io_clear_busy) || (|s_target_clear_busy) || (|s_guard_clear_busy) || s_lp_clear_busy || s_ext_clear_busy || s_jpeg_clear_busy; + assign apu_bridge_epoch_o = unused_io_epoch[1]; assign s_unused_epoch = { ^unused_io_epoch, ^unused_target_epoch, ^{unused_lp_epoch, unused_ext_epoch, unused_jpeg_epoch} }; diff --git a/rtl/rtl_style_audit.json b/rtl/rtl_style_audit.json index 175b6c4c..4a4d0702 100644 --- a/rtl/rtl_style_audit.json +++ b/rtl/rtl_style_audit.json @@ -135,8 +135,11 @@ "rtl/ip/multimedia": [ "apb4_apu.sv", "apb4_jpeg.sv", + "apu_dma.sv", "apu_define.svh", + "apu_ring_scheduler.sv", "apu_reg.sv", + "apu_stream_router.sv", "axi4s_dvp.sv", "dvp_core.sv", "dvp_define.svh", diff --git a/tests/README.md b/tests/README.md index 87b2ee6b..b0ea44ac 100644 --- a/tests/README.md +++ b/tests/README.md @@ -16,9 +16,10 @@ 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_apu.py` checks the fail-closed APU APB4/IRQ shell plus the P2 private + DMA, ring scheduler, stream router, Gateway A, and verification-only backend; + `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/rtl/apu_dma_tb.sv b/tests/rtl/apu_dma_tb.sv new file mode 100644 index 00000000..f43b2504 --- /dev/null +++ b/tests/rtl/apu_dma_tb.sv @@ -0,0 +1,516 @@ +`timescale 1ns / 1ps + +`include "axi4_define.svh" + +module apu_dma_tb; + logic clk_i = 1'b0; + logic rst_n_i = 1'b0; + logic abort_i = 1'b0; + logic quiesce_i = 1'b0; + logic counter_clear_i = 1'b0; + logic [ 7:0] bridge_epoch_i = 8'd0; + logic request_valid_i = 1'b0; + logic request_ready_o; + logic request_write_i; + logic [31:0] request_addr_i; + logic [31:0] request_bytes_i; + logic [31:0] timeout_i = 32'd32; + logic [31:0] read_acl_base_i = 32'd0; + logic [31:0] read_acl_limit_i = 32'h0000_ffff; + logic [31:0] write_acl_base_i = 32'd0; + logic [31:0] write_acl_limit_i = 32'h0000_ffff; + logic busy_o, done_o, error_o, aborted_o, aborting_o; + logic [ 5:0] error_code_o; + logic [ 3:0] error_stage_o; + logic [ 1:0] error_resp_o; + logic [31:0] error_addr_o; + logic [63:0] read_bytes_o, write_bytes_o; + logic [31:0] memory [0:32767]; + logic [31:0] buffer [ 0:31]; + logic [ 3:0] buffer_keep[ 0:31]; + logic read_active, write_active, write_source_active; + logic [31:0] read_base, write_base; + logic [7:0] read_len, write_len, read_index, write_index, source_index; + logic bvalid; + logic allow_addresses = 1'b1; + logic read_sink_ready = 1'b1; + logic [1:0] read_response = `AXI4_RESP_OKAY; + logic [1:0] write_response = `AXI4_RESP_OKAY; + logic read_id = 1'b0; + logic write_id = 1'b0; + logic early_read_last = 1'b0; + logic suppress_read_last = 1'b0; + logic allow_write_response = 1'b1; + logic [7:0] source_last_index = 8'd0; + int unsigned read_bursts, write_bursts; + string s_phase; + + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) axi4 ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) read_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) write_axis ( + clk_i, + rst_n_i + ); + + always #5 clk_i = ~clk_i; + + assign read_axis.tready = read_sink_ready; + assign write_axis.tdata = buffer[source_index]; + assign write_axis.tkeep = buffer_keep[source_index]; + assign write_axis.tstrb = buffer_keep[source_index]; + assign write_axis.tlast = source_index == source_last_index; + assign write_axis.tid = '0; + assign write_axis.tdest = '0; + assign write_axis.tuser = '0; + assign write_axis.tvalid = write_source_active; + + assign axi4.arready = allow_addresses && !read_active; + assign axi4.rid = read_id; + assign axi4.rdata = memory[(read_base>>2)+read_index]; + assign axi4.rresp = read_response; + assign axi4.rlast = !suppress_read_last && + (early_read_last ? (read_index == 8'd0) : (read_index == read_len)); + assign axi4.ruser = 1'b0; + assign axi4.rvalid = read_active; + assign axi4.awready = allow_addresses && !write_active && !bvalid; + assign axi4.wready = write_active; + assign axi4.bid = write_id; + assign axi4.bresp = write_response; + assign axi4.buser = 1'b0; + assign axi4.bvalid = bvalid && allow_write_response; + + apu_dma u_dut ( + .clk_i, + .rst_n_i, + .abort_i, + .quiesce_i, + .bridge_epoch_i, + .perf_enable_i (1'b1), + .counter_clear_i, + .request_valid_i, + .request_ready_o, + .request_write_i, + .request_addr_i, + .request_bytes_i, + .read_base_i (read_acl_base_i), + .read_limit_i (read_acl_limit_i), + .write_base_i (write_acl_base_i), + .write_limit_i (write_acl_limit_i), + .timeout_i, + .read_axis, + .write_axis, + .busy_o, + .done_o, + .error_o, + .aborted_o, + .aborting_o, + .error_code_o, + .error_stage_o, + .error_resp_o, + .error_addr_o, + .input_pending_o (), + .output_pending_o(), + .read_bytes_o, + .write_bytes_o, + .read_stalls_o (), + .write_stalls_o (), + .axi4 + ); + + task automatic issue_request(input logic write_i, input logic [31:0] addr_i, + input logic [31:0] bytes_i); + begin + @(negedge clk_i); + request_write_i = write_i; + request_addr_i = addr_i; + request_bytes_i = bytes_i; + if (write_i) source_last_index = ((bytes_i + 32'd3) >> 2) - 1'b1; + request_valid_i = 1'b1; + while (!request_ready_o) @(negedge clk_i); + @(posedge clk_i); + @(negedge clk_i); + request_valid_i = 1'b0; + end + endtask + + always @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + read_active <= 1'b0; + write_active <= 1'b0; + bvalid <= 1'b0; + read_index <= 8'd0; + write_index <= 8'd0; + source_index <= 8'd0; + read_bursts <= 0; + write_bursts <= 0; + end else begin + if (axi4.arvalid && axi4.arready) begin + read_active <= 1'b1; + read_base <= axi4.araddr; + read_len <= axi4.arlen; + read_index <= 8'd0; + read_bursts <= read_bursts + 1; + end else if (axi4.rvalid && axi4.rready) begin + if (axi4.rlast) read_active <= 1'b0; + else read_index <= read_index + 1'b1; + end + if (read_axis.tvalid && read_axis.tready) begin + buffer[source_index] <= read_axis.tdata; + buffer_keep[source_index] <= read_axis.tkeep; + source_index <= source_index + 1'b1; + end + if (axi4.awvalid && axi4.awready) begin + write_active <= 1'b1; + write_base <= axi4.awaddr; + write_len <= axi4.awlen; + write_index <= 8'd0; + write_bursts <= write_bursts + 1; + end + if (axi4.wvalid && axi4.wready) begin + for (int byte_index = 0; byte_index < 4; byte_index++) begin + if (axi4.wstrb[byte_index]) begin + memory[(write_base>>2)+write_index][byte_index*8+:8] <= axi4.wdata[byte_index*8+:8]; + end + end + if (axi4.wlast) begin + write_active <= 1'b0; + bvalid <= 1'b1; + end else begin + write_index <= write_index + 1'b1; + end + end + if (write_axis.tvalid && write_axis.tready) source_index <= source_index + 1'b1; + if (axi4.bvalid && axi4.bready) bvalid <= 1'b0; + end + end + + initial begin + request_write_i = 1'b0; + request_addr_i = 32'd0; + request_bytes_i = 32'd0; + write_source_active = 1'b0; + for (int index = 0; index < 32768; index++) memory[index] = 32'hdead_beef; + for (int index = 0; index < 17; index++) memory[(32'h0ff0>>2)+index] = 32'h1000 + index; + repeat (3) @(posedge clk_i); + rst_n_i = 1'b1; + + source_index = 8'd0; + s_phase = "read"; + issue_request(1'b0, 32'h0000_0ff0, 32'd67); + while (!done_o) @(posedge clk_i); + if (error_o || (read_bytes_o != 64'd67) || (read_bursts < 2)) begin + $fatal(1, "APU DMA read/split failed"); + end + source_index = 8'd0; + s_phase = "write"; + write_source_active = 1'b1; + issue_request(1'b1, 32'h0000_2000, 32'd67); + while (!done_o) @(posedge clk_i); + write_source_active = 1'b0; + if (error_o || (write_bytes_o != 64'd67) || (write_bursts < 2)) begin + $fatal(1, "APU DMA write/split failed"); + end + for (int index = 0; index < 16; index++) begin + if (memory[(32'h2000>>2)+index] != 32'h1000 + index) $fatal(1, "DMA copy mismatch"); + end + if ((memory[(32'h2040>>2)][23:0] != 24'h001010) || (memory[(32'h2040>>2)][31:24] != 8'hde)) + $fatal(1, "DMA tail strobe mismatch"); + + s_phase = "acl"; + issue_request(1'b0, 32'h0002_0000, 32'd4); + while (!done_o) @(posedge clk_i); + if (!error_o || (error_code_o != 6'd15)) $fatal(1, "DMA ACL denial failed"); + read_acl_limit_i = 32'hffff_ffff; + issue_request(1'b0, 32'hffff_fffc, 32'd8); + while (!done_o) @(posedge clk_i); + if (!error_o || (error_code_o != 6'd15) || busy_o) begin + $fatal(1, "DMA ACL arithmetic overflow was accepted"); + end + read_acl_limit_i = 32'h0000_ffff; + + s_phase = "read response faults"; + read_response = `AXI4_RESP_SLAVE_ERROR; + issue_request(1'b0, 32'h0000_4000, 32'd4); + while (!done_o) @(posedge clk_i); + if (!error_o || (error_code_o != 6'd15) || (error_resp_o != `AXI4_RESP_SLAVE_ERROR)) + $fatal(1, "DMA read SLVERR failed"); + read_response = `AXI4_RESP_DECODE_ERROR; + issue_request(1'b0, 32'h0000_4000, 32'd4); + while (!done_o) @(posedge clk_i); + if (!error_o || (error_resp_o != `AXI4_RESP_DECODE_ERROR)) begin + $fatal(1, "DMA read DECERR failed"); + end + read_response = `AXI4_RESP_OKAY; + read_id = 1'b1; + issue_request(1'b0, 32'h0000_4000, 32'd4); + while (!done_o) @(posedge clk_i); + if (!error_o || (error_code_o != 6'd15)) $fatal(1, "DMA read ID failure missed"); + read_id = 1'b0; + early_read_last = 1'b1; + issue_request(1'b0, 32'h0000_4000, 32'd8); + while (!done_o) @(posedge clk_i); + if (!error_o || (error_code_o != 6'd15)) $fatal(1, "DMA malformed RLAST missed"); + early_read_last = 1'b0; + suppress_read_last = 1'b1; + issue_request(1'b0, 32'h0000_4000, 32'd8); + while (!done_o) @(posedge clk_i); + if (!error_o || busy_o || (error_code_o != 6'd15)) begin + $fatal(1, "DMA missing terminal RLAST did not terminate boundedly"); + end + suppress_read_last = 1'b0; + read_active = 1'b0; + + s_phase = "epoch/request coincidence"; + @(negedge clk_i); + request_write_i = 1'b0; + request_addr_i = 32'h0000_4000; + request_bytes_i = 32'd4; + request_valid_i = 1'b1; + bridge_epoch_i = bridge_epoch_i + 1'b1; + #1; + if (request_ready_o) $fatal(1, "DMA accepted a request during epoch recovery"); + @(posedge clk_i); + do @(posedge clk_i); while (!request_ready_o); + @(negedge clk_i); + request_valid_i = 1'b0; + while (!done_o) @(posedge clk_i); + if (error_o || (read_bytes_o != 64'd75)) begin + $fatal(1, "DMA lost the request coincident with epoch recovery"); + end + + s_phase = "write response faults"; + source_index = 8'd0; + write_source_active = 1'b1; + write_response = `AXI4_RESP_SLAVE_ERROR; + issue_request(1'b1, 32'h0000_5000, 32'd4); + while (!done_o) @(posedge clk_i); + if (!error_o || (error_code_o != 6'd16) || (error_resp_o != `AXI4_RESP_SLAVE_ERROR)) + $fatal(1, "DMA write SLVERR failed"); + source_index = 8'd0; + write_response = `AXI4_RESP_DECODE_ERROR; + issue_request(1'b1, 32'h0000_5004, 32'd4); + while (!done_o) @(posedge clk_i); + if (!error_o || (error_resp_o != `AXI4_RESP_DECODE_ERROR)) begin + $fatal(1, "DMA write DECERR failed"); + end + source_index = 8'd0; + write_response = `AXI4_RESP_OKAY; + write_id = 1'b1; + issue_request(1'b1, 32'h0000_5008, 32'd4); + while (!done_o) @(posedge clk_i); + if (!error_o || (error_code_o != 6'd16)) $fatal(1, "DMA write ID failure missed"); + write_id = 1'b0; + write_source_active = 1'b0; + + s_phase = "read-start abort"; + issue_request(1'b0, 32'h0000_6000, 32'd4); + if (u_dut.s_state_q != 3'd1) $fatal(1, "DMA did not hold ReadStart for abort injection"); + abort_i = 1'b1; + @(negedge clk_i); + abort_i = 1'b0; + while (!done_o) @(posedge clk_i); + if (error_o || !aborted_o || busy_o) $fatal(1, "DMA ReadStart abort failed"); + + s_phase = "write-start abort"; + source_index = 8'd0; + write_source_active = 1'b1; + issue_request(1'b1, 32'h0000_5010, 32'd4); + if (u_dut.s_state_q != 3'd3) $fatal(1, "DMA did not hold WriteStart for abort injection"); + abort_i = 1'b1; + @(posedge clk_i); + #1; + if (!aborted_o || !aborting_o) begin + $fatal(1, "DMA did not latch the WriteStart abort state=%0d drain=%0d", u_dut.s_state_q, + u_dut.s_drain_q); + end + @(negedge clk_i); + abort_i = 1'b0; + while (!done_o) @(posedge clk_i); + write_source_active = 1'b0; + if (error_o || !aborted_o || busy_o) begin + $fatal(1, "DMA WriteStart abort failed error=%0d aborted=%0d busy=%0d code=%0d state=%0d", + error_o, aborted_o, busy_o, error_code_o, u_dut.s_state_q); + end + + s_phase = "write-data abort"; + source_index = 8'd0; + write_source_active = 1'b0; + issue_request(1'b1, 32'h0000_5020, 32'd16); + wait (write_active && (u_dut.s_state_q == 3'd4)); + @(negedge clk_i); + abort_i = 1'b1; + @(negedge clk_i); + abort_i = 1'b0; + while (!done_o) @(posedge clk_i); + if (error_o || !aborted_o || busy_o) $fatal(1, "DMA WriteData abort failed"); + + s_phase = "write response abort"; + source_index = 8'd0; + write_source_active = 1'b1; + allow_write_response = 1'b0; + issue_request(1'b1, 32'h0000_5010, 32'd4); + while (!bvalid) @(posedge clk_i); + @(negedge clk_i); + abort_i = 1'b1; + @(negedge clk_i); + abort_i = 1'b0; + if (!aborting_o) $fatal(1, "DMA did not report aborting while waiting for B"); + allow_write_response = 1'b1; + while (!done_o) @(posedge clk_i); + write_source_active = 1'b0; + if (error_o || !aborted_o || (error_code_o != 6'd20)) begin + $fatal(1, "DMA write-response abort did not drain"); + end + + s_phase = "write error over abort"; + source_index = 8'd0; + write_source_active = 1'b1; + write_response = `AXI4_RESP_SLAVE_ERROR; + allow_write_response = 1'b0; + issue_request(1'b1, 32'h0000_5014, 32'd4); + while (!bvalid) @(posedge clk_i); + @(negedge clk_i); + abort_i = 1'b1; + allow_write_response = 1'b1; + @(negedge clk_i); + abort_i = 1'b0; + while (!done_o) @(posedge clk_i); + write_source_active = 1'b0; + write_response = `AXI4_RESP_OKAY; + if (!error_o || !aborted_o || (error_code_o != 6'd16) || + (error_resp_o != `AXI4_RESP_SLAVE_ERROR)) begin + $fatal(1, "DMA write error did not outrank simultaneous abort"); + end + + s_phase = "accepted abort drain"; + read_sink_ready = 1'b0; + issue_request(1'b0, 32'h0000_6000, 32'd16); + while (!read_active) @(posedge clk_i); + @(negedge clk_i); + abort_i = 1'b1; + @(negedge clk_i); + if (!aborting_o) $fatal(1, "DMA live aborting status was not asserted"); + abort_i = 1'b0; + while (!done_o) @(posedge clk_i); + read_sink_ready = 1'b1; + if (error_o || !aborted_o || (error_code_o != 6'd20)) begin + $fatal(1, "DMA accepted-read abort drain failed"); + end + if (aborting_o) $fatal(1, "DMA live aborting status remained set after drain"); + + s_phase = "read error over abort"; + read_sink_ready = 1'b0; + issue_request(1'b0, 32'h0000_6000, 32'd16); + while (!read_active) @(posedge clk_i); + read_response = `AXI4_RESP_SLAVE_ERROR; + @(negedge clk_i); + abort_i = 1'b1; + @(negedge clk_i); + abort_i = 1'b0; + while (!done_o) @(posedge clk_i); + read_response = `AXI4_RESP_OKAY; + read_sink_ready = 1'b1; + if (!error_o || !aborted_o || (error_code_o != 6'd15)) begin + $fatal(1, "DMA read error did not outrank abort"); + end + + s_phase = "unaccepted abort cancellation"; + allow_addresses = 1'b0; + issue_request(1'b0, 32'h0000_6000, 32'd4); + repeat (2) @(posedge clk_i); + @(negedge clk_i); + abort_i = 1'b1; + @(negedge clk_i); + abort_i = 1'b0; + while (!aborted_o) @(posedge clk_i); + if (!axi4.arvalid || !busy_o) $fatal(1, "aborted AR was withdrawn before handshake"); + allow_addresses = 1'b1; + while (!done_o) @(posedge clk_i); + if (error_o || !aborted_o || (error_code_o != 6'd20)) begin + $fatal(1, "DMA unaccepted-address abort drain failed"); + end + + s_phase = "deterministic random lengths"; + for (int unsigned transfer = 0; transfer < 12; transfer++) begin + automatic int unsigned length = ((transfer * 29) % 95) + 1; + automatic int unsigned source_addr = 32'h0000_7000 + (transfer * 32'h100); + automatic int unsigned dest_addr = 32'h0000_9000 + (transfer * 32'h100); + automatic int unsigned beats = (length + 3) / 4; + automatic logic [63:0] read_before = read_bytes_o; + automatic logic [63:0] write_before = write_bytes_o; + for (int unsigned word_index = 0; word_index < beats; word_index++) begin + memory[(source_addr>>2)+word_index] = 32'h600d_0000 ^ (transfer << 8) ^ word_index; + memory[(dest_addr>>2)+word_index] = 32'hdead_beef; + end + source_index = 8'd0; + issue_request(1'b0, source_addr, length); + while (!done_o) @(posedge clk_i); + if (error_o || (read_bytes_o != read_before + length)) begin + $fatal(1, "DMA randomized read failed"); + end + source_index = 8'd0; + write_source_active = 1'b1; + issue_request(1'b1, dest_addr, length); + while (!done_o) @(posedge clk_i); + write_source_active = 1'b0; + if (error_o || (write_bytes_o != write_before + length)) begin + $fatal(1, "DMA randomized write failed"); + end + for (int unsigned byte_index = 0; byte_index < length; byte_index++) begin + if (memory[(dest_addr >> 2) + (byte_index / 4)][(byte_index%4)*8+:8] !== + memory[(source_addr >> 2) + (byte_index / 4)][(byte_index%4)*8+:8]) begin + $fatal(1, "DMA randomized copy mismatch"); + end + end + end + + allow_addresses = 1'b0; + s_phase = "timeout"; + timeout_i = 32'd1; + source_index = 8'd0; + write_source_active = 1'b1; + issue_request(1'b1, 32'h0000_3000, 32'd4); + while (!error_o) @(posedge clk_i); + if (!axi4.awvalid || !busy_o || (error_code_o != 6'd17)) begin + $fatal(1, "timed-out AW was withdrawn before handshake"); + end + allow_addresses = 1'b1; + while (!done_o) @(posedge clk_i); + write_source_active = 1'b0; + if (!error_o || (error_code_o != 6'd17) || aborted_o) $fatal(1, "DMA timeout failed"); + + @(negedge clk_i); + counter_clear_i = 1'b1; + @(negedge clk_i); + counter_clear_i = 1'b0; + #1; + if ((read_bytes_o != 64'd0) || (write_bytes_o != 64'd0) || + (u_dut.s_read_stalls_q != 64'd0) || (u_dut.s_write_stalls_q != 64'd0)) begin + $fatal(1, "DMA counter clear failed"); + end + + $display("APU-P2 DMA tests passed"); + $finish; + end + + initial begin + repeat (3000) @(posedge clk_i); + $fatal(1, "APU-P2 DMA test timed out in %s state=%0d remaining=%0d", s_phase, u_dut.s_state_q, + u_dut.s_remaining_q); + end +endmodule diff --git a/tests/rtl/apu_irq_topology_tb.sv b/tests/rtl/apu_irq_topology_tb.sv index 451065d2..f3a0bf66 100644 --- a/tests/rtl/apu_irq_topology_tb.sv +++ b/tests/rtl/apu_irq_topology_tb.sv @@ -45,6 +45,39 @@ module apu_irq_topology_tb; .pclk (clk_i), .presetn(rst_n_i) ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) u_apu_axi4_if ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) u_dma_tx_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) u_dma_rx_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) u_i2s_tx_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) u_i2s_rx_axis ( + clk_i, + rst_n_i + ); always #5 clk_i = ~clk_i; @@ -62,15 +95,23 @@ module apu_irq_topology_tb; `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) + .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]), + .bridge_epoch_i (8'd0), + .i2s_tx_underrun_i(1'b0), + .i2s_rx_overrun_i (1'b0), + .apb4 (u_apu_apb4_if), + .axi4 (u_apu_axi4_if), + .dma_tx_axis (u_dma_tx_axis), + .dma_rx_axis (u_dma_rx_axis), + .i2s_tx_axis (u_i2s_tx_axis), + .i2s_rx_axis (u_i2s_rx_axis), + .idle_o (s_apu_idle), + .irq_o (s_apu_irq_raw) ); resource_controller #( @@ -214,6 +255,37 @@ apb4_apu u_apu ( initial begin drive_apb_idle(); + u_apu_axi4_if.awready = 1'b0; + u_apu_axi4_if.wready = 1'b0; + u_apu_axi4_if.bid = '0; + u_apu_axi4_if.bresp = '0; + u_apu_axi4_if.buser = '0; + u_apu_axi4_if.bvalid = 1'b0; + u_apu_axi4_if.arready = 1'b0; + u_apu_axi4_if.rid = '0; + u_apu_axi4_if.rdata = '0; + u_apu_axi4_if.rresp = '0; + u_apu_axi4_if.rlast = 1'b0; + u_apu_axi4_if.ruser = '0; + u_apu_axi4_if.rvalid = 1'b0; + u_dma_tx_axis.tdata = '0; + u_dma_tx_axis.tkeep = '1; + u_dma_tx_axis.tstrb = '1; + u_dma_tx_axis.tlast = 1'b0; + u_dma_tx_axis.tid = '0; + u_dma_tx_axis.tdest = '0; + u_dma_tx_axis.tuser = '0; + u_dma_tx_axis.tvalid = 1'b0; + u_dma_rx_axis.tready = 1'b1; + u_i2s_tx_axis.tready = 1'b1; + u_i2s_rx_axis.tdata = '0; + u_i2s_rx_axis.tkeep = '1; + u_i2s_rx_axis.tstrb = '1; + u_i2s_rx_axis.tlast = 1'b0; + u_i2s_rx_axis.tid = '0; + u_i2s_rx_axis.tdest = '0; + u_i2s_rx_axis.tuser = '0; + u_i2s_rx_axis.tvalid = 1'b0; repeat (4) @(posedge clk_i); rst_n_i = 1'b1; diff --git a/tests/rtl/apu_p2_backend.sv b/tests/rtl/apu_p2_backend.sv new file mode 100644 index 00000000..42f0d8b1 --- /dev/null +++ b/tests/rtl/apu_p2_backend.sv @@ -0,0 +1,83 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 + +// Verification-only deterministic endpoint for the production P2 ring scheduler. +module apu_p2_backend ( + input logic clk_i, + input logic rst_n_i, + input logic abort_i, + input logic hold_result_i, + input logic result_error_i, + input logic [ 5:0] result_code_i, + input logic [ 3:0] result_stage_i, + input logic [ 1:0] result_resp_i, + input logic job_valid_i, + output logic job_ready_o, + input logic [1023:0] descriptor_i, + input logic [ 7:0] index_i, + output logic result_valid_o, + input logic result_ready_i, + output logic result_error_o, + output logic [ 5:0] result_code_o, + output logic [ 3:0] result_stage_o, + output logic [ 1:0] result_resp_o, + output logic [ 31:0] input_used_o, + output logic [ 31:0] output_bytes_o, + output logic [ 31:0] frames_o, + output logic [ 31:0] source_info_o, + output logic [ 31:0] cycles_o, + output logic [ 31:0] detail_o, + output logic [ 31:0] accepted_jobs_o +); + logic s_pending_q; + logic s_error_q; + logic [ 5:0] s_code_q; + logic [ 3:0] s_stage_q; + logic [ 1:0] s_resp_q; + logic [ 7:0] s_index_q; + logic [31:0] s_input_length_q; + logic [31:0] s_output_capacity_q; + logic [31:0] s_accepted_jobs_q; + + assign job_ready_o = !s_pending_q && !abort_i; + assign result_valid_o = s_pending_q && !hold_result_i; + assign result_error_o = s_error_q; + assign result_code_o = s_code_q; + assign result_stage_o = s_stage_q; + assign result_resp_o = s_resp_q; + assign input_used_o = s_input_length_q; + assign output_bytes_o = s_output_capacity_q; + assign frames_o = s_input_length_q >> 2; + assign source_info_o = 32'h0004_0000 | {24'd0, s_index_q}; + assign cycles_o = 32'd64 + {24'd0, s_index_q}; + assign detail_o = 32'h5032_0000 | {24'd0, s_index_q}; + assign accepted_jobs_o = s_accepted_jobs_q; + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + s_pending_q <= 1'b0; + s_error_q <= 1'b0; + s_code_q <= 6'd0; + s_stage_q <= 4'd0; + s_resp_q <= 2'd0; + s_index_q <= 8'd0; + s_input_length_q <= 32'd0; + s_output_capacity_q <= 32'd0; + s_accepted_jobs_q <= 32'd0; + end else begin + if (abort_i) s_pending_q <= 1'b0; + if (job_valid_i && job_ready_o) begin + s_pending_q <= 1'b1; + s_error_q <= result_error_i; + s_code_q <= result_code_i; + s_stage_q <= result_stage_i; + s_resp_q <= result_resp_i; + s_index_q <= index_i; + s_input_length_q <= descriptor_i[(3*32)+:32]; + s_output_capacity_q <= descriptor_i[(5*32)+:32]; + s_accepted_jobs_q <= s_accepted_jobs_q + 1'b1; + end + if (result_valid_o && result_ready_i) s_pending_q <= 1'b0; + end + end +endmodule diff --git a/tests/rtl/apu_p2_integration_tb.sv b/tests/rtl/apu_p2_integration_tb.sv new file mode 100644 index 00000000..ce15f04b --- /dev/null +++ b/tests/rtl/apu_p2_integration_tb.sv @@ -0,0 +1,1180 @@ +`timescale 1ns / 1ps + +`include "apu_define.svh" +`include "axi4_define.svh" + +module apu_p2_integration_tb; + localparam logic [31:0] RingBase = 32'h0000_1000; + + logic clk_i = 1'b0; + logic clk_hp_i = 1'b0; + logic rst_n_i = 1'b0; + logic rst_hp_n_i = 1'b0; + logic soft_reset_i = 1'b0; + logic abort_i = 1'b0; + logic resource_reset_i = 1'b0; + logic resource_reset_pending_q = 1'b0; + logic resource_reset_apply_q = 1'b0; + logic resource_reset_seen_q = 1'b0; + logic bridge_clear_i = 1'b0; + logic bridge_clear_busy; + logic target_guard_clear_busy; + logic target_guard_abort; + logic effective_abort; + logic effective_quiesce; + logic dma_admission_block; + logic quiesce_i = 1'b0; + logic start_i = 1'b0; + logic ring_enable_i = 1'b0; + logic stop_on_error_i = 1'b0; + logic [31:0] ring_base_i = RingBase; + logic [ 8:0] ring_size_i = 9'd2; + logic [ 7:0] ring_tail_i = 8'd0; + logic [ 7:0] bridge_epoch_i; + logic fault_read_i = 1'b0; + logic fault_payload_read_i = 1'b0; + logic fault_payload_write_i = 1'b0; + logic fault_result_write_i = 1'b0; + logic fault_owner_write_i = 1'b0; + logic suppress_read_last_i = 1'b0; + logic hold_addresses_i = 1'b0; + logic hold_read_data_i = 1'b0; + logic hold_write_data_i = 1'b0; + logic hold_write_response_i = 1'b0; + logic hold_backend_i = 1'b0; + logic backend_error_i = 1'b0; + logic [ 5:0] backend_code_i = 6'd0; + logic [ 3:0] backend_stage_i = 4'd0; + logic [ 1:0] backend_resp_i = 2'd0; + logic sdio_read_request_i = 1'b0; + logic usb_read_request_i = 1'b0; + logic [31:0] sdio_read_addr_i = 32'd0; + logic [31:0] usb_read_addr_i = 32'd0; + logic [ 7:0] missing_last_epoch_q; + + logic direct_start_i = 1'b0; + logic [31:0] direct_source_i = 32'h0000_2000; + logic [31:0] direct_destination_i = 32'h0000_2400; + logic [31:0] direct_bytes_i = 32'd4; + logic direct_busy, direct_done, direct_error; + logic direct_done_seen_q; + logic direct_request_valid, direct_request_ready, direct_request_write; + logic [31:0] direct_request_addr, direct_request_bytes; + logic scheduler_request_valid, scheduler_request_ready, scheduler_request_write; + logic [31:0] scheduler_request_addr, scheduler_request_bytes; + logic dma_request_valid, dma_request_ready, dma_request_write; + logic dma_backend_owner_q; + logic select_scheduler_request; + logic [31:0] dma_request_addr, dma_request_bytes; + logic dma_busy, dma_done, dma_error, dma_aborted, dma_aborting; + logic [ 5:0] dma_error_code; + logic [ 3:0] dma_error_stage; + logic [ 1:0] dma_error_resp; + logic [31:0] dma_error_addr; + logic backend_job_valid, backend_job_ready; + logic [1023:0] backend_descriptor; + logic [ 7:0] backend_index; + logic backend_result_valid, backend_result_ready; + logic backend_result_error; + logic [5:0] backend_result_code; + logic [3:0] backend_result_stage; + logic [1:0] backend_result_resp; + logic [31:0] backend_input_used, backend_output_bytes, backend_frames; + logic [31:0] backend_source_info, backend_cycles, backend_detail; + logic [31:0] backend_accepted; + logic [31:0] job_status, ring_status, ring_completed; + logic [7:0] ring_head; + logic ring_event, error_event, abort_done, scheduler_aborting, scheduler_idle; + logic [ 5:0] ring_error_code; + logic [ 3:0] ring_error_stage; + logic [ 1:0] ring_error_resp; + logic [ 7:0] ring_error_index; + logic [31:0] ring_error_addr; + + logic [31:0] memory [0:4095]; + logic [31:0] read_addr_q, write_addr_q; + logic [7:0] read_len_q, write_len_q, read_beat_q, write_beat_q; + logic read_active_q, write_active_q, bvalid_q; + logic [ 1:0] bresp_q; + logic [15:0] cycle_q; + logic result_retired_q; + logic owner_write_seen_q; + int unsigned aw_count_q; + string s_phase; + + function automatic logic is_descriptor_addr(input logic [31:0] addr_i); + return (addr_i >= RingBase) && (addr_i < (RingBase + 32'd256)); + endfunction + + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) axi4 ( + clk_i, + rst_n_i + ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) gateway_axi4 ( + clk_i, + rst_n_i + ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) guarded_axi4 ( + clk_hp_i, + rst_hp_n_i + ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) memory_axi4 ( + clk_hp_i, + rst_hp_n_i + ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) sdio_axi4 ( + clk_i, + rst_n_i + ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) usb_axi4 ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) dma_read_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) dma_write_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) scheduler_read_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) scheduler_write_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) direct_read_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) direct_write_axis ( + clk_i, + rst_n_i + ); + + always #5 clk_i = ~clk_i; + always #7 clk_hp_i = ~clk_hp_i; + + assign effective_abort = abort_i || resource_reset_i || resource_reset_pending_q; + assign effective_quiesce = quiesce_i || resource_reset_i || resource_reset_pending_q; + assign dma_admission_block = effective_quiesce && + !job_status[`APB4_APU__JOB_STATUS_BUSY] && + !ring_status[`APB4_APU__RING_STATUS_WRITEBACK_PENDING] && !direct_busy; + + assign select_scheduler_request = scheduler_request_valid; + assign dma_request_valid = scheduler_request_valid || direct_request_valid; + assign dma_request_write = select_scheduler_request ? scheduler_request_write : direct_request_write; + assign dma_request_addr = select_scheduler_request ? scheduler_request_addr : direct_request_addr; + assign dma_request_bytes = select_scheduler_request ? scheduler_request_bytes : direct_request_bytes; + assign direct_request_ready = !select_scheduler_request && dma_request_ready; + assign scheduler_request_ready = select_scheduler_request && dma_request_ready; + + assign scheduler_read_axis.tdata = dma_read_axis.tdata; + assign scheduler_read_axis.tkeep = dma_read_axis.tkeep; + assign scheduler_read_axis.tstrb = dma_read_axis.tstrb; + assign scheduler_read_axis.tlast = dma_read_axis.tlast; + assign scheduler_read_axis.tid = dma_read_axis.tid; + assign scheduler_read_axis.tdest = dma_read_axis.tdest; + assign scheduler_read_axis.tuser = dma_read_axis.tuser; + assign scheduler_read_axis.tvalid = !dma_backend_owner_q && dma_read_axis.tvalid; + assign direct_read_axis.tdata = dma_read_axis.tdata; + assign direct_read_axis.tkeep = dma_read_axis.tkeep; + assign direct_read_axis.tstrb = dma_read_axis.tstrb; + assign direct_read_axis.tlast = dma_read_axis.tlast; + assign direct_read_axis.tid = dma_read_axis.tid; + assign direct_read_axis.tdest = dma_read_axis.tdest; + assign direct_read_axis.tuser = dma_read_axis.tuser; + assign direct_read_axis.tvalid = dma_backend_owner_q && dma_read_axis.tvalid; + assign dma_read_axis.tready = dma_backend_owner_q ? direct_read_axis.tready : + scheduler_read_axis.tready; + + assign dma_write_axis.tdata = dma_backend_owner_q ? direct_write_axis.tdata : + scheduler_write_axis.tdata; + assign dma_write_axis.tkeep = dma_backend_owner_q ? direct_write_axis.tkeep : + scheduler_write_axis.tkeep; + assign dma_write_axis.tstrb = dma_backend_owner_q ? direct_write_axis.tstrb : + scheduler_write_axis.tstrb; + assign dma_write_axis.tlast = dma_backend_owner_q ? direct_write_axis.tlast : + scheduler_write_axis.tlast; + assign dma_write_axis.tid = dma_backend_owner_q ? direct_write_axis.tid : scheduler_write_axis.tid; + assign dma_write_axis.tdest = dma_backend_owner_q ? direct_write_axis.tdest : + scheduler_write_axis.tdest; + assign dma_write_axis.tuser = dma_backend_owner_q ? direct_write_axis.tuser : + scheduler_write_axis.tuser; + assign dma_write_axis.tvalid = dma_backend_owner_q ? direct_write_axis.tvalid : + scheduler_write_axis.tvalid; + assign direct_write_axis.tready = dma_backend_owner_q && dma_write_axis.tready; + assign scheduler_write_axis.tready = !dma_backend_owner_q && dma_write_axis.tready; + + assign sdio_axi4.awid = '0; + assign sdio_axi4.awaddr = '0; + assign sdio_axi4.awlen = '0; + assign sdio_axi4.awsize = 3'd2; + assign sdio_axi4.awburst = `AXI4_BURST_TYPE_INCR; + assign sdio_axi4.awlock = 1'b0; + assign sdio_axi4.awcache = '0; + assign sdio_axi4.awprot = `AXI4_PROT_DATA; + assign sdio_axi4.awqos = '0; + assign sdio_axi4.awregion = '0; + assign sdio_axi4.awuser = '0; + assign sdio_axi4.awvalid = 1'b0; + assign sdio_axi4.wdata = '0; + assign sdio_axi4.wstrb = '0; + assign sdio_axi4.wlast = 1'b0; + assign sdio_axi4.wuser = '0; + assign sdio_axi4.wvalid = 1'b0; + assign sdio_axi4.bready = 1'b1; + assign sdio_axi4.arid = '0; + assign sdio_axi4.araddr = sdio_read_addr_i; + assign sdio_axi4.arlen = '0; + assign sdio_axi4.arsize = 3'd2; + assign sdio_axi4.arburst = `AXI4_BURST_TYPE_INCR; + assign sdio_axi4.arlock = 1'b0; + assign sdio_axi4.arcache = '0; + assign sdio_axi4.arprot = `AXI4_PROT_DATA; + assign sdio_axi4.arqos = '0; + assign sdio_axi4.arregion = '0; + assign sdio_axi4.aruser = '0; + assign sdio_axi4.arvalid = sdio_read_request_i; + assign sdio_axi4.rready = 1'b1; + + assign usb_axi4.awid = '0; + assign usb_axi4.awaddr = '0; + assign usb_axi4.awlen = '0; + assign usb_axi4.awsize = 3'd2; + assign usb_axi4.awburst = `AXI4_BURST_TYPE_INCR; + assign usb_axi4.awlock = 1'b0; + assign usb_axi4.awcache = '0; + assign usb_axi4.awprot = `AXI4_PROT_DATA; + assign usb_axi4.awqos = '0; + assign usb_axi4.awregion = '0; + assign usb_axi4.awuser = '0; + assign usb_axi4.awvalid = 1'b0; + assign usb_axi4.wdata = '0; + assign usb_axi4.wstrb = '0; + assign usb_axi4.wlast = 1'b0; + assign usb_axi4.wuser = '0; + assign usb_axi4.wvalid = 1'b0; + assign usb_axi4.bready = 1'b1; + assign usb_axi4.arid = '0; + assign usb_axi4.araddr = usb_read_addr_i; + assign usb_axi4.arlen = '0; + assign usb_axi4.arsize = 3'd2; + assign usb_axi4.arburst = `AXI4_BURST_TYPE_INCR; + assign usb_axi4.arlock = 1'b0; + assign usb_axi4.arcache = '0; + assign usb_axi4.arprot = `AXI4_PROT_DATA; + assign usb_axi4.arqos = '0; + assign usb_axi4.arregion = '0; + assign usb_axi4.aruser = '0; + assign usb_axi4.arvalid = usb_read_request_i; + assign usb_axi4.rready = 1'b1; + + assign memory_axi4.arready = !hold_addresses_i && !read_active_q && cycle_q[0]; + assign memory_axi4.rid = 1'b0; + assign memory_axi4.rdata = memory[((read_addr_q-RingBase)>>2)+read_beat_q]; + assign memory_axi4.rresp = ((fault_read_i && is_descriptor_addr( + read_addr_q + )) || (fault_payload_read_i && !is_descriptor_addr( + read_addr_q + ))) ? `AXI4_RESP_SLAVE_ERROR : `AXI4_RESP_OKAY; + assign memory_axi4.rlast = !suppress_read_last_i && (read_beat_q == read_len_q); + assign memory_axi4.ruser = 1'b0; + assign memory_axi4.rvalid = read_active_q && !hold_read_data_i; + assign memory_axi4.awready = !hold_addresses_i && !write_active_q && !bvalid_q && cycle_q[0]; + assign memory_axi4.wready = !hold_write_data_i && write_active_q && (cycle_q[1:0] != 2'b00); + assign memory_axi4.bid = 1'b0; + assign memory_axi4.bresp = bresp_q; + assign memory_axi4.buser = 1'b0; + assign memory_axi4.bvalid = bvalid_q && !hold_write_response_i; + + hp_axi4_mux3 #( + .RoundRobin (1'b1), + .Client0EpochAware(1'b1) + ) u_gateway_a ( + .clk_i, + .rst_n_i, + .epoch_i(bridge_epoch_i), + .icache (axi4), + .dcache (sdio_axi4), + .mmio (usb_axi4), + .axi4 (gateway_axi4) + ); + + axi4_async_bridge u_bridge ( + .src_clk_i (clk_i), + .src_rst_n_i (rst_n_i), + .dst_clk_i (clk_hp_i), + .dst_rst_n_i (rst_hp_n_i), + .clear_i (bridge_clear_i), + .clear_busy_o(bridge_clear_busy), + .epoch_o (bridge_epoch_i), + .src_axi4 (gateway_axi4), + .dst_axi4 (guarded_axi4) + ); + + axi4_target_guard #( + .AddrWidth (32), + .DataWidth (32), + .IdWidth (1), + .UserWidth (1), + .ReadDepth (4), + .WriteDepth(2) + ) u_target_guard ( + .clk_i (clk_hp_i), + .rst_n_i (rst_hp_n_i), + .clear_i (bridge_clear_i), + .timeout_i (32'd256), + .clear_busy_o (target_guard_clear_busy), + .abort_o (target_guard_abort), + .abort_done_i (target_guard_abort), + .timeout_valid_o(), + .isolated_o (), + .timeout_write_o(), + .timeout_id_o (), + .timeout_addr_o (), + .source (guarded_axi4), + .sink (memory_axi4) + ); + + apu_dma u_dma ( + .clk_i, + .rst_n_i, + .abort_i (effective_abort && !ring_status[`APB4_APU__RING_STATUS_WRITEBACK_PENDING]), + .quiesce_i (dma_admission_block), + .bridge_epoch_i, + .perf_enable_i (1'b1), + .counter_clear_i (1'b0), + .request_valid_i (dma_request_valid), + .request_ready_o (dma_request_ready), + .request_write_i (dma_request_write), + .request_addr_i (dma_request_addr), + .request_bytes_i (dma_request_bytes), + .read_base_i (RingBase), + .read_limit_i (32'h0000_2fff), + .write_base_i (RingBase), + .write_limit_i (32'h0000_2fff), + .timeout_i (32'd24), + .read_axis (dma_read_axis), + .write_axis (dma_write_axis), + .busy_o (dma_busy), + .done_o (dma_done), + .error_o (dma_error), + .aborted_o (dma_aborted), + .aborting_o (dma_aborting), + .error_code_o (dma_error_code), + .error_stage_o (dma_error_stage), + .error_resp_o (dma_error_resp), + .error_addr_o (dma_error_addr), + .input_pending_o (), + .output_pending_o(), + .read_bytes_o (), + .write_bytes_o (), + .read_stalls_o (), + .write_stalls_o (), + .axi4 + ); + + apu_ring_scheduler u_scheduler ( + .clk_i, + .rst_n_i, + .soft_reset_i (soft_reset_i || resource_reset_apply_q), + .counter_clear_i (1'b0), + .abort_i (effective_abort), + .quiesce_i (effective_quiesce), + .start_i, + .ring_base_i, + .ring_size_i, + .ring_tail_i, + .ring_enable_i, + .stop_on_error_i, + .coalesce_count_i (8'd2), + .coalesce_timeout_i (16'd8), + .dma_request_valid_o (scheduler_request_valid), + .dma_request_ready_i (scheduler_request_ready), + .dma_request_write_o (scheduler_request_write), + .dma_request_addr_o (scheduler_request_addr), + .dma_request_bytes_o (scheduler_request_bytes), + .dma_read_axis (scheduler_read_axis), + .dma_write_axis (scheduler_write_axis), + .dma_done_i (dma_done), + .dma_error_i (dma_error), + .dma_error_code_i (dma_error_code), + .dma_error_stage_i (dma_error_stage), + .dma_error_resp_i (dma_error_resp), + .dma_error_addr_i (dma_error_addr), + .backend_job_valid_o (backend_job_valid), + .backend_job_ready_i (backend_job_ready), + .backend_descriptor_o (backend_descriptor), + .backend_index_o (backend_index), + .backend_result_valid_i(backend_result_valid), + .backend_result_ready_o(backend_result_ready), + .backend_result_error_i(backend_result_error), + .backend_result_code_i (backend_result_code), + .backend_result_stage_i(backend_result_stage), + .backend_result_resp_i (backend_result_resp), + .backend_input_used_i (backend_input_used), + .backend_output_bytes_i(backend_output_bytes), + .backend_frames_i (backend_frames), + .backend_source_info_i (backend_source_info), + .backend_cycles_i (backend_cycles), + .backend_detail_i (backend_detail), + .job_status_o (job_status), + .ring_status_o (ring_status), + .ring_head_o (ring_head), + .ring_completed_o (ring_completed), + .ring_event_o (ring_event), + .error_event_o (error_event), + .error_code_o (ring_error_code), + .error_stage_o (ring_error_stage), + .error_resp_o (ring_error_resp), + .error_index_o (ring_error_index), + .error_addr_o (ring_error_addr), + .abort_done_o (abort_done), + .aborting_o (scheduler_aborting), + .idle_o (scheduler_idle) + ); + + apu_p2_transport_backend u_transport_backend ( + .clk_i, + .rst_n_i, + .abort_i (effective_abort), + .direct_start_i, + .direct_source_i, + .direct_destination_i, + .direct_bytes_i, + .job_valid_i (backend_job_valid), + .job_ready_o (backend_job_ready), + .descriptor_i (backend_descriptor), + .index_i (backend_index), + .hold_result_i (hold_backend_i), + .inject_result_error_i(backend_error_i), + .inject_result_code_i (backend_code_i), + .inject_result_stage_i(backend_stage_i), + .inject_result_resp_i (backend_resp_i), + .result_valid_o (backend_result_valid), + .result_ready_i (backend_result_ready), + .result_error_o (backend_result_error), + .result_code_o (backend_result_code), + .result_stage_o (backend_result_stage), + .result_resp_o (backend_result_resp), + .input_used_o (backend_input_used), + .output_bytes_o (backend_output_bytes), + .frames_o (backend_frames), + .source_info_o (backend_source_info), + .cycles_o (backend_cycles), + .detail_o (backend_detail), + .accepted_jobs_o (backend_accepted), + .request_valid_o (direct_request_valid), + .request_ready_i (direct_request_ready), + .request_write_o (direct_request_write), + .request_addr_o (direct_request_addr), + .request_bytes_o (direct_request_bytes), + .read_axis (direct_read_axis), + .write_axis (direct_write_axis), + .dma_done_i (dma_done), + .dma_error_i (dma_error), + .dma_aborted_i (dma_aborted), + .dma_error_code_i (dma_error_code), + .dma_error_stage_i (dma_error_stage), + .dma_error_resp_i (dma_error_resp), + .busy_o (direct_busy), + .direct_done_o (direct_done), + .direct_error_o (direct_error) + ); + + task automatic hard_reset; + begin + rst_n_i = 1'b0; + rst_hp_n_i = 1'b0; + repeat (3) @(posedge clk_i); + @(negedge clk_i); + rst_n_i = 1'b1; + rst_hp_n_i = 1'b1; + repeat (3) @(posedge clk_i); + end + endtask + + task automatic pulse_start; + begin + @(negedge clk_i); + start_i = 1'b1; + @(negedge clk_i); + start_i = 1'b0; + end + endtask + + task automatic pulse_abort; + begin + @(negedge clk_i); + abort_i = 1'b1; + @(negedge clk_i); + abort_i = 1'b0; + end + endtask + + task automatic pulse_direct_start; + begin + @(negedge clk_i); + direct_start_i = 1'b1; + @(negedge clk_i); + direct_start_i = 1'b0; + end + endtask + + task automatic pulse_bridge_clear; + begin + @(negedge clk_i); + bridge_clear_i = 1'b1; + wait (bridge_clear_busy); + @(negedge clk_i); + bridge_clear_i = 1'b0; + wait (!bridge_clear_busy); + end + endtask + + task automatic pulse_resource_reset; + begin + @(negedge clk_i); + resource_reset_i = 1'b1; + @(negedge clk_i); + resource_reset_i = 1'b0; + end + endtask + + task automatic clear_memory; + for (int unsigned word_index = 0; word_index < 4096; word_index++) begin + memory[word_index] = 32'd0; + end + endtask + + task automatic make_descriptor(input int unsigned descriptor_i); + begin + for (int unsigned word_index = 0; word_index < 32; word_index++) begin + memory[(descriptor_i*32)+word_index] = 32'd0; + end + memory[descriptor_i*32] = 32'd1 << `APB4_APU__DESCRIPTOR_CONTROL_OWN; + memory[(descriptor_i*32)+2] = 32'h0000_1800; + memory[(descriptor_i*32)+3] = 32'd65; + memory[(descriptor_i*32)+4] = 32'h0000_1c00; + memory[(descriptor_i*32)+5] = 32'd128; + memory[(descriptor_i*32)+16] = 32'h1234_5678; + memory[(descriptor_i*32)+17] = 32'h9abc_def0; + for (int unsigned word_index = 0; word_index < 17; word_index++) begin + memory[((32'h0000_1800-RingBase)>>2)+word_index] = 32'h5100_0000 + word_index; + memory[((32'h0000_1c00-RingBase)>>2)+word_index] = 32'hdead_beef; + end + end + endtask + + task automatic wait_idle; + int unsigned timeout; + begin + timeout = 0; + while ((!scheduler_idle || dma_busy || direct_busy || + (u_gateway_a.s_state_q != 3'd0) || + (u_target_guard.s_read_state_q != 3'd0) || + (u_target_guard.s_write_state_q != 3'd0)) && (timeout < 4000)) begin + @(posedge clk_i); + timeout++; + end + if (!scheduler_idle || dma_busy || direct_busy || (u_gateway_a.s_state_q != 3'd0) || + (u_target_guard.s_read_state_q != 3'd0) || + (u_target_guard.s_write_state_q != 3'd0)) begin + $fatal(1, "integrated APU did not become idle in %s", s_phase); + end + end + endtask + + task automatic issue_peer_read(input logic usb_i, input logic [31:0] addr_i); + begin + @(negedge clk_i); + if (usb_i) begin + usb_read_addr_i = addr_i; + usb_read_request_i = 1'b1; + end else begin + sdio_read_addr_i = addr_i; + sdio_read_request_i = 1'b1; + end + if (usb_i) begin + do @(posedge clk_i); while (!usb_axi4.arready); + end else begin + do @(posedge clk_i); while (!sdio_axi4.arready); + end + @(negedge clk_i); + if (usb_i) usb_read_request_i = 1'b0; + else sdio_read_request_i = 1'b0; + if (usb_i) begin + do @(posedge clk_i); while (!usb_axi4.rvalid); + if ((usb_axi4.rresp != `AXI4_RESP_OKAY) || !usb_axi4.rlast) begin + $fatal(1, "USB2 traffic failed after missing RLAST recovery"); + end + end else begin + do @(posedge clk_i); while (!sdio_axi4.rvalid); + if ((sdio_axi4.rresp != `AXI4_RESP_OKAY) || !sdio_axi4.rlast) begin + $fatal(1, "SDIO0 traffic failed after missing RLAST recovery"); + end + end + end + endtask + + task automatic run_ring_lifecycle_case(input logic resource_reset_case_i, + input logic [3:0] state_i); + int unsigned timeout; + begin + ring_enable_i = 1'b0; + quiesce_i = 1'b0; + resource_reset_i = 1'b0; + hold_addresses_i = 1'b0; + hold_read_data_i = 1'b0; + hold_write_data_i = 1'b0; + hold_write_response_i = 1'b0; + hold_backend_i = 1'b0; + suppress_read_last_i = 1'b0; + hard_reset(); + clear_memory(); + make_descriptor(0); + ring_base_i = RingBase; + ring_size_i = 9'd2; + ring_tail_i = 8'd1; + ring_enable_i = 1'b1; + pulse_start(); + wait (u_scheduler.s_state_q == state_i); + if (resource_reset_case_i) begin + s_phase = $sformatf("resource reset drain state %0d", state_i); + end else begin + s_phase = $sformatf("ownership quiesce drain state %0d", state_i); + end + @(negedge clk_i); + if (resource_reset_case_i) resource_reset_i = 1'b1; + else quiesce_i = 1'b1; + @(negedge clk_i); + if (resource_reset_case_i) resource_reset_i = 1'b0; + + if (resource_reset_case_i) begin + timeout = 0; + while (!resource_reset_apply_q && (timeout < 4000)) begin + @(posedge clk_i); + #1; + timeout++; + if (resource_reset_apply_q && memory[0][31]) begin + $fatal(1, "resource reset applied before OWN clear in state %0d", state_i); + end + end + if (!resource_reset_apply_q) begin + $fatal(1, "resource reset did not drain state %0d", state_i); + end + end else begin + wait_idle(); + end + + if (memory[0][31] || (memory[1] == 32'd0) || !result_retired_q || + !owner_write_seen_q) begin + $fatal(1, "lifecycle drain lost ordered descriptor writeback state %0d reset=%0d", + state_i, resource_reset_case_i); + end + if (!resource_reset_case_i) begin + @(negedge clk_i); + quiesce_i = 1'b0; + end + end + endtask + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + dma_backend_owner_q <= 1'b0; + end else if (dma_request_valid && dma_request_ready) begin + dma_backend_owner_q <= !select_scheduler_request; + end + end + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + direct_done_seen_q <= 1'b0; + end else if (direct_start_i) begin + direct_done_seen_q <= 1'b0; + end else if (direct_done) begin + direct_done_seen_q <= 1'b1; + end + end + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + resource_reset_pending_q <= 1'b0; + resource_reset_apply_q <= 1'b0; + resource_reset_seen_q <= 1'b0; + end else begin + resource_reset_apply_q <= 1'b0; + if (resource_reset_i && !resource_reset_seen_q) begin + resource_reset_pending_q <= 1'b1; + resource_reset_seen_q <= 1'b1; + end + if (resource_reset_pending_q && scheduler_idle && !dma_busy && !direct_busy && + (u_gateway_a.s_state_q == 3'd0) && (u_target_guard.s_read_state_q == 3'd0) && + (u_target_guard.s_write_state_q == 3'd0) && !bridge_clear_busy && + !target_guard_clear_busy) begin + resource_reset_pending_q <= 1'b0; + resource_reset_apply_q <= 1'b1; + end + if (!resource_reset_i && !resource_reset_pending_q) begin + resource_reset_seen_q <= 1'b0; + end + end + end + + always_ff @(posedge clk_hp_i or negedge rst_hp_n_i) begin + if (!rst_hp_n_i) begin + read_addr_q <= 32'd0; + write_addr_q <= 32'd0; + read_len_q <= 8'd0; + write_len_q <= 8'd0; + read_beat_q <= 8'd0; + write_beat_q <= 8'd0; + read_active_q <= 1'b0; + write_active_q <= 1'b0; + bvalid_q <= 1'b0; + bresp_q <= `AXI4_RESP_OKAY; + cycle_q <= 16'd0; + result_retired_q <= 1'b0; + owner_write_seen_q <= 1'b0; + aw_count_q <= 0; + end else if (bridge_clear_i) begin + read_active_q <= 1'b0; + write_active_q <= 1'b0; + bvalid_q <= 1'b0; + end else begin + cycle_q <= cycle_q + 1'b1; + if (memory_axi4.arvalid && memory_axi4.arready) begin + read_addr_q <= memory_axi4.araddr; + read_len_q <= memory_axi4.arlen; + read_beat_q <= 8'd0; + read_active_q <= 1'b1; + if (is_descriptor_addr(memory_axi4.araddr) && (memory_axi4.araddr[6:0] == 7'd0)) begin + result_retired_q <= 1'b0; + end + end + if (memory_axi4.rvalid && memory_axi4.rready) begin + if (memory_axi4.rlast || (read_beat_q == read_len_q)) read_active_q <= 1'b0; + else read_beat_q <= read_beat_q + 1'b1; + end + if (memory_axi4.awvalid && memory_axi4.awready) begin + write_addr_q <= memory_axi4.awaddr; + write_len_q <= memory_axi4.awlen; + write_beat_q <= 8'd0; + write_active_q <= 1'b1; + aw_count_q <= aw_count_q + 1; + if (is_descriptor_addr( + memory_axi4.awaddr + ) && (memory_axi4.awaddr[6:0] == 7'd0) && !result_retired_q) begin + $fatal(1, "OWN clear address issued before result response retired"); + end + end + if (memory_axi4.wvalid && memory_axi4.wready) begin + for (int unsigned byte_index = 0; byte_index < 4; byte_index++) begin + if (memory_axi4.wstrb[byte_index]) begin + memory[((write_addr_q-RingBase)>>2)+write_beat_q][byte_index*8+:8] <= + memory_axi4.wdata[byte_index*8+:8]; + end + end + if (is_descriptor_addr( + write_addr_q + ) && (write_addr_q[6:0] == 7'd0) && !result_retired_q) begin + $fatal(1, "OWN cleared before result response retired"); + end + if (memory_axi4.wlast) begin + write_active_q <= 1'b0; + bvalid_q <= 1'b1; + if (fault_owner_write_i && is_descriptor_addr( + write_addr_q + ) && (write_addr_q[6:0] == 7'd0)) begin + bresp_q <= `AXI4_RESP_SLAVE_ERROR; + end else if (fault_result_write_i && is_descriptor_addr( + write_addr_q + ) && (write_addr_q[6:0] != 7'd0)) begin + bresp_q <= `AXI4_RESP_SLAVE_ERROR; + end else if (fault_payload_write_i && !is_descriptor_addr(write_addr_q)) begin + bresp_q <= `AXI4_RESP_SLAVE_ERROR; + end else begin + bresp_q <= `AXI4_RESP_OKAY; + end + if (is_descriptor_addr(write_addr_q) && (write_addr_q[6:0] == 7'd0)) begin + owner_write_seen_q <= 1'b1; + end + end else begin + write_beat_q <= write_beat_q + 1'b1; + end + end + if (memory_axi4.bvalid && memory_axi4.bready) begin + bvalid_q <= 1'b0; + if ((bresp_q == `AXI4_RESP_OKAY) && is_descriptor_addr( + write_addr_q + ) && (write_addr_q[6:0] != 7'd0) && ((write_addr_q + (({24'd0, write_len_q} + 1'b1) << 2 + )) == (RingBase + 32'd128))) begin + result_retired_q <= 1'b1; + end + end + end + end + + initial begin + clear_memory(); + s_phase = "direct randomized transport backend"; + hard_reset(); + for (int unsigned transfer = 0; transfer < 12; transfer++) begin + automatic int unsigned length = ((transfer * 29) % 95) + 1; + automatic int unsigned words = (length + 3) / 4; + for (int unsigned word_index = 0; word_index < words; word_index++) begin + memory[((direct_source_i-RingBase)>>2)+word_index] = + 32'h600d_0000 ^ (transfer << 8) ^ word_index; + memory[((direct_destination_i-RingBase)>>2)+word_index] = 32'hdead_beef; + end + direct_bytes_i = length; + pulse_direct_start(); + wait (direct_done); + if (direct_error || direct_busy) $fatal(1, "direct backend transfer failed"); + for (int unsigned byte_index = 0; byte_index < length; byte_index++) begin + if (memory[((direct_destination_i-RingBase)>>2)+(byte_index/4)] + [(byte_index%4)*8+:8] !== + memory[((direct_source_i-RingBase)>>2)+(byte_index/4)][(byte_index%4)*8+:8]) begin + $fatal(1, "direct backend copy mismatch transfer=%0d byte=%0d", transfer, byte_index); + end + end + end + + s_phase = "direct backend abort"; + direct_bytes_i = 32'd16; + pulse_direct_start(); + wait (dma_busy && read_active_q); + pulse_abort(); + wait (direct_done); + if (!direct_error || direct_busy || dma_busy) $fatal(1, "direct backend abort did not retire"); + + s_phase = "deferred resource reset drain"; + hard_reset(); + hold_read_data_i = 1'b1; + direct_bytes_i = 32'd16; + pulse_direct_start(); + wait (dma_busy && read_active_q); + pulse_resource_reset(); + repeat (3) begin + @(posedge clk_i); + if (resource_reset_apply_q) $fatal(1, "resource reset applied before DMA drain"); + end + hold_read_data_i = 1'b0; + wait (resource_reset_apply_q); + @(posedge clk_i); + if (resource_reset_pending_q || dma_busy || direct_busy || !scheduler_idle) begin + $fatal(1, "resource reset did not retire after transport drain"); + end + + s_phase = "successful ordered writeback"; + hard_reset(); + clear_memory(); + make_descriptor(0); + ring_tail_i = 8'd1; + ring_enable_i = 1'b1; + pulse_start(); + while (ring_completed != 32'd1) @(posedge clk_i); + wait_idle(); + if (memory[0][31] || !memory[1][0] || !owner_write_seen_q || !result_retired_q || + (memory[10] != 32'd65) || (memory[11] != 32'd65) || (ring_head != 8'd1)) begin + $fatal(1, "ordered integrated descriptor writeback failed"); + end + for (int unsigned byte_index = 0; byte_index < 65; byte_index++) begin + if (memory[((32'h0000_1c00-RingBase)>>2)+(byte_index/4)][(byte_index%4)*8+:8] !== + memory[((32'h0000_1800-RingBase)>>2)+(byte_index/4)][(byte_index%4)*8+:8]) begin + $fatal(1, "ring backend payload copy mismatch at byte %0d", byte_index); + end + end + + for (int unsigned lifecycle_state = 4; lifecycle_state <= 11; lifecycle_state++) begin + run_ring_lifecycle_case(1'b1, 4'(lifecycle_state)); + run_ring_lifecycle_case(1'b0, 4'(lifecycle_state)); + end + + s_phase = "randomized ring payload transport"; + for (int unsigned job = 0; job < 8; job++) begin + automatic int unsigned length = ((job * 37) % 95) + 1; + automatic int unsigned words = (length + 3) / 4; + hard_reset(); + clear_memory(); + make_descriptor(0); + memory[3] = length; + memory[5] = length + 32'd7; + for (int unsigned word_index = 0; word_index < words; word_index++) begin + memory[((32'h0000_1800-RingBase)>>2)+word_index] = 32'h7100_0000 ^ (job << 8) ^ word_index; + end + ring_tail_i = 8'd1; + ring_enable_i = 1'b1; + pulse_start(); + wait (ring_completed == 32'd1); + wait_idle(); + if (memory[0][31] || !memory[1][0] || (memory[10] != length) || (memory[11] != length)) begin + $fatal(1, "randomized ring result mismatch job=%0d", job); + end + for (int unsigned byte_index = 0; byte_index < length; byte_index++) begin + if (memory[((32'h0000_1c00-RingBase)>>2)+(byte_index/4)][(byte_index%4)*8+:8] !== + memory[((32'h0000_1800-RingBase)>>2)+(byte_index/4)][(byte_index%4)*8+:8]) begin + $fatal(1, "randomized ring payload mismatch job=%0d byte=%0d", job, byte_index); + end + end + end + + s_phase = "unowned abort containment"; + memory[32] = 32'd0; + memory[33] = 32'hfeed_cafe; + ring_tail_i = 8'd0; + wait (u_scheduler.s_state_q == 4'd3); + @(negedge clk_i); + abort_i = 1'b1; + @(negedge clk_i); + abort_i = 1'b0; + wait (abort_done); + wait_idle(); + if ((memory[32] != 32'd0) || (memory[33] != 32'hfeed_cafe) || (ring_head != 8'd1)) begin + $fatal(1, "abort mutated an unowned descriptor"); + end + + s_phase = "simultaneous fetch completion and abort"; + ring_enable_i = 1'b0; + hard_reset(); + clear_memory(); + make_descriptor(0); + ring_enable_i = 1'b1; + ring_tail_i = 8'd1; + pulse_start(); + wait (dma_done && (u_scheduler.s_state_q == 4'd2)); + @(negedge clk_i); + abort_i = 1'b1; + @(negedge clk_i); + abort_i = 1'b0; + wait (abort_done); + wait_idle(); + + s_phase = "result writeback response fault"; + hard_reset(); + clear_memory(); + make_descriptor(0); + fault_result_write_i = 1'b1; + ring_enable_i = 1'b1; + ring_tail_i = 8'd1; + pulse_start(); + wait (ring_status[`APB4_APU__RING_STATUS_ERROR]); + wait_idle(); + if (!memory[0][31] || (ring_completed != 32'd0) || (ring_head != 8'd0) || + (ring_error_code != 6'd16)) begin + $fatal(1, "result writeback fault released descriptor ownership"); + end + fault_result_write_i = 1'b0; + + s_phase = "owner writeback response fault"; + hard_reset(); + clear_memory(); + make_descriptor(0); + fault_owner_write_i = 1'b1; + ring_enable_i = 1'b1; + ring_tail_i = 8'd1; + pulse_start(); + wait (error_event); + wait_idle(); + if ((ring_completed != 32'd0) || (ring_head != 8'd0) || (ring_error_code != 6'd16)) begin + $fatal(1, "owner writeback fault advanced ring state"); + end + fault_owner_write_i = 1'b0; + + s_phase = "descriptor read response fault"; + hard_reset(); + clear_memory(); + make_descriptor(0); + fault_read_i = 1'b1; + ring_enable_i = 1'b1; + ring_tail_i = 8'd1; + pulse_start(); + wait (error_event); + wait_idle(); + if ((ring_error_code != 6'd15) || (ring_error_stage != 4'd3) || + (ring_error_resp != `AXI4_RESP_SLAVE_ERROR)) begin + $fatal(1, "descriptor read response fault attribution failed"); + end + fault_read_i = 1'b0; + + s_phase = "payload read response fault"; + hard_reset(); + clear_memory(); + make_descriptor(0); + fault_payload_read_i = 1'b1; + ring_enable_i = 1'b1; + ring_tail_i = 8'd1; + pulse_start(); + wait (error_event); + wait_idle(); + if (memory[0][31] || !memory[1][1] || (memory[1][8:3] != 6'd15) || + (ring_error_code != 6'd15)) begin + $fatal(1, "payload read fault was not published through descriptor writeback"); + end + fault_payload_read_i = 1'b0; + + s_phase = "payload write response fault"; + hard_reset(); + clear_memory(); + make_descriptor(0); + fault_payload_write_i = 1'b1; + ring_enable_i = 1'b1; + ring_tail_i = 8'd1; + pulse_start(); + wait (error_event); + wait_idle(); + if (memory[0][31] || !memory[1][1] || (memory[1][8:3] != 6'd16) || + (ring_error_code != 6'd16)) begin + $fatal(1, "payload write fault was not published through descriptor writeback"); + end + fault_payload_write_i = 1'b0; + + s_phase = "explicit bridge flush recovery"; + hard_reset(); + clear_memory(); + make_descriptor(0); + ring_enable_i = 1'b1; + ring_tail_i = 8'd1; + pulse_start(); + wait (read_active_q && dma_busy); + pulse_bridge_clear(); + wait (ring_status[`APB4_APU__RING_STATUS_ERROR]); + wait_idle(); + if ((ring_error_code != 6'd15) || (ring_error_resp != `AXI4_RESP_DECODE_ERROR)) begin + $fatal(1, "bridge epoch did not terminate active descriptor DMA"); + end + + s_phase = "unilateral HP reset recovery"; + hard_reset(); + clear_memory(); + direct_bytes_i = 32'd16; + hold_read_data_i = 1'b1; + pulse_direct_start(); + wait (read_active_q && dma_busy); + @(negedge clk_hp_i); + rst_hp_n_i = 1'b0; + repeat (3) @(posedge clk_i); + @(negedge clk_hp_i); + rst_hp_n_i = 1'b1; + hold_read_data_i = 1'b0; + wait (direct_done_seen_q); + if (!direct_error || dma_busy || (u_gateway_a.s_state_q != 2'd0)) begin + $fatal(1, "unilateral HP reset did not recover the APU Gateway path"); + end + direct_bytes_i = 32'd4; + pulse_direct_start(); + wait (direct_done); + if (direct_error) $fatal(1, "Gateway A did not accept work after HP reset recovery"); + + s_phase = "missing terminal RLAST recovery"; + hard_reset(); + missing_last_epoch_q = bridge_epoch_i; + suppress_read_last_i = 1'b1; + direct_bytes_i = 32'd8; + pulse_direct_start(); + wait (direct_done); + if (!direct_error || dma_busy) $fatal(1, "missing RLAST did not terminate the DMA"); + suppress_read_last_i = 1'b0; + wait_idle(); + if (bridge_epoch_i != missing_last_epoch_q) begin + $fatal(1, "missing RLAST recovery required an external bridge epoch"); + end + direct_bytes_i = 32'd4; + pulse_direct_start(); + wait (direct_done); + if (direct_error) $fatal(1, "APU traffic failed after missing RLAST recovery"); + memory[((32'h0000_2800-RingBase)>>2)] = 32'h5d10_0001; + memory[((32'h0000_2804-RingBase)>>2)] = 32'h5b20_0001; + issue_peer_read(1'b0, 32'h0000_2800); + issue_peer_read(1'b1, 32'h0000_2804); + wait_idle(); + + s_phase = "ring arithmetic validation"; + hard_reset(); + ring_base_i = 32'hffff_ff80; + ring_size_i = 9'd2; + ring_tail_i = 8'd1; + ring_enable_i = 1'b1; + pulse_start(); + wait (error_event); + if ((ring_error_code != 6'd2) || dma_busy) $fatal(1, "ring extent overflow was accepted"); + hard_reset(); + ring_base_i = RingBase; + ring_size_i = 9'd2; + ring_tail_i = 8'd2; + ring_enable_i = 1'b1; + pulse_start(); + wait (error_event); + if ((ring_error_code != 6'd2) || dma_busy) $fatal(1, "out-of-range ring tail was accepted"); + hard_reset(); + ring_base_i = RingBase; + ring_size_i = 9'd2; + ring_tail_i = 8'd1; + ring_enable_i = 1'b1; + force u_scheduler.s_head_q = 8'd3; + pulse_start(); + wait (error_event); + if ((ring_error_code != 6'd2) || dma_busy) + $fatal(1, "stale out-of-range ring head was accepted"); + release u_scheduler.s_head_q; + + $display("APU-P2 integrated DMA/ring/backend tests passed"); + $finish; + end + + initial begin + repeat (60000) @(posedge clk_i); + $fatal(1, "APU-P2 integration test timed out in %s scheduler=%0d dma=%0d", s_phase, + u_scheduler.s_state_q, u_dma.s_state_q); + end + + logic s_unused_status; + assign s_unused_status = ring_event ^ scheduler_aborting ^ dma_aborted ^ dma_aborting ^ + ^ring_error_index ^ ^ring_error_addr ^ ^job_status ^ ^backend_accepted ^ ^aw_count_q; +endmodule diff --git a/tests/rtl/apu_p2_transport_backend.sv b/tests/rtl/apu_p2_transport_backend.sv new file mode 100644 index 00000000..f4d06c28 --- /dev/null +++ b/tests/rtl/apu_p2_transport_backend.sv @@ -0,0 +1,312 @@ +// Copyright (c) 2026 Yuchi Miao +// SPDX-License-Identifier: MulanPSL-2.0 + +// Verification-only direct/ring client for the production APU DMA. +module apu_p2_transport_backend ( + // verilog_format: off -- preserve job, request, stream, and result columns + input logic clk_i, + input logic rst_n_i, + input logic abort_i, + input logic direct_start_i, + input logic [31:0] direct_source_i, + input logic [31:0] direct_destination_i, + input logic [31:0] direct_bytes_i, + input logic job_valid_i, + output logic job_ready_o, + input logic [1023:0] descriptor_i, + input logic [7:0] index_i, + input logic hold_result_i, + input logic inject_result_error_i, + input logic [5:0] inject_result_code_i, + input logic [3:0] inject_result_stage_i, + input logic [1:0] inject_result_resp_i, + output logic result_valid_o, + input logic result_ready_i, + output logic result_error_o, + output logic [5:0] result_code_o, + output logic [3:0] result_stage_o, + output logic [1:0] result_resp_o, + output logic [31:0] input_used_o, + output logic [31:0] output_bytes_o, + output logic [31:0] frames_o, + output logic [31:0] source_info_o, + output logic [31:0] cycles_o, + output logic [31:0] detail_o, + output logic [31:0] accepted_jobs_o, + output logic request_valid_o, + input logic request_ready_i, + output logic request_write_o, + output logic [31:0] request_addr_o, + output logic [31:0] request_bytes_o, + axi4_stream_if.sink read_axis, + axi4_stream_if.source write_axis, + input logic dma_done_i, + input logic dma_error_i, + input logic dma_aborted_i, + input logic [5:0] dma_error_code_i, + input logic [3:0] dma_error_stage_i, + input logic [1:0] dma_error_resp_i, + output logic busy_o, + output logic direct_done_o, + output logic direct_error_o + // verilog_format: on +); + typedef enum logic [3:0] { + Idle, + ReadRequest, + ReadData, + ReadWait, + WriteRequest, + WriteData, + WriteWait, + Result + } state_e; + + state_e s_state_q; + logic s_ring_q; + logic [ 7:0] s_index_q; + logic [31:0] s_source_addr_q; + logic [31:0] s_destination_addr_q; + logic [31:0] s_bytes_q; + logic [ 5:0] s_read_index_q; + logic [ 5:0] s_write_index_q; + logic [31:0] s_buffer_q [32]; + logic [ 3:0] s_keep_q [32]; + logic s_result_error_q; + logic [ 5:0] s_result_code_q; + logic [ 3:0] s_result_stage_q; + logic [ 1:0] s_result_resp_q; + logic [31:0] s_accepted_jobs_q; + logic s_direct_done_q; + logic s_direct_error_q; + logic s_abort_pending_q; + + function automatic logic [31:0] bounded_bytes(input logic [31:0] length_i, + input logic [31:0] capacity_i); + return (length_i < capacity_i) ? length_i : capacity_i; + endfunction + + assign job_ready_o = (s_state_q == Idle) && !direct_start_i && !abort_i; + assign result_valid_o = (s_state_q == Result) && !hold_result_i; + assign result_error_o = s_result_error_q; + assign result_code_o = s_result_code_q; + assign result_stage_o = s_result_stage_q; + assign result_resp_o = s_result_resp_q; + assign input_used_o = s_bytes_q; + assign output_bytes_o = s_bytes_q; + assign frames_o = s_bytes_q >> 2; + assign source_info_o = 32'h0004_0000 | {24'd0, s_index_q}; + assign cycles_o = 32'd64 + {24'd0, s_index_q}; + assign detail_o = 32'h5032_0000 | {24'd0, s_index_q}; + assign accepted_jobs_o = s_accepted_jobs_q; + assign request_valid_o = !abort_i && ((s_state_q == ReadRequest) || (s_state_q == WriteRequest)); + assign request_write_o = s_state_q == WriteRequest; + assign request_addr_o = request_write_o ? s_destination_addr_q : s_source_addr_q; + assign request_bytes_o = s_bytes_q; + assign read_axis.tready = s_state_q == ReadData; + assign write_axis.tdata = s_buffer_q[s_write_index_q[4:0]]; + assign write_axis.tkeep = s_keep_q[s_write_index_q[4:0]]; + assign write_axis.tstrb = s_keep_q[s_write_index_q[4:0]]; + assign write_axis.tlast = s_write_index_q == s_read_index_q; + assign write_axis.tid = '0; + assign write_axis.tdest = '0; + assign write_axis.tuser = '0; + assign write_axis.tvalid = s_state_q == WriteData; + assign busy_o = s_state_q != Idle; + assign direct_done_o = s_direct_done_q; + assign direct_error_o = s_direct_error_q; + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + s_state_q <= Idle; + s_ring_q <= 1'b0; + s_index_q <= 8'd0; + s_source_addr_q <= 32'd0; + s_destination_addr_q <= 32'd0; + s_bytes_q <= 32'd0; + s_read_index_q <= 6'd0; + s_write_index_q <= 6'd0; + s_result_error_q <= 1'b0; + s_result_code_q <= 6'd0; + s_result_stage_q <= 4'd0; + s_result_resp_q <= 2'd0; + s_accepted_jobs_q <= 32'd0; + s_direct_done_q <= 1'b0; + s_direct_error_q <= 1'b0; + s_abort_pending_q <= 1'b0; + end else begin + s_direct_done_q <= 1'b0; + if (abort_i && (s_state_q != Idle)) s_abort_pending_q <= 1'b1; + unique case (s_state_q) + Idle: begin + if (job_valid_i && job_ready_o) begin + s_ring_q <= 1'b1; + s_index_q <= index_i; + s_source_addr_q <= descriptor_i[(2*32)+:32]; + s_destination_addr_q <= descriptor_i[(4*32)+:32]; + s_bytes_q <= bounded_bytes(descriptor_i[(3*32)+:32], descriptor_i[(5*32)+:32]); + s_read_index_q <= 6'd0; + s_write_index_q <= 6'd0; + s_result_error_q <= 1'b0; + s_result_code_q <= 6'd0; + s_result_stage_q <= 4'd0; + s_result_resp_q <= 2'd0; + s_accepted_jobs_q <= s_accepted_jobs_q + 1'b1; + s_abort_pending_q <= 1'b0; + s_state_q <= ReadRequest; + end else if (direct_start_i && !abort_i) begin + s_ring_q <= 1'b0; + s_index_q <= 8'd0; + s_source_addr_q <= direct_source_i; + s_destination_addr_q <= direct_destination_i; + s_bytes_q <= direct_bytes_i; + s_read_index_q <= 6'd0; + s_write_index_q <= 6'd0; + s_direct_error_q <= 1'b0; + s_abort_pending_q <= 1'b0; + s_state_q <= ReadRequest; + end + end + ReadRequest: begin + if (abort_i) begin + s_state_q <= Idle; + s_abort_pending_q <= 1'b0; + if (!s_ring_q) begin + s_direct_done_q <= 1'b1; + s_direct_error_q <= 1'b1; + end + end else if (request_ready_i) begin + s_state_q <= ReadData; + end + end + ReadData: begin + if (read_axis.tvalid && read_axis.tready) begin + s_buffer_q[s_read_index_q[4:0]] <= read_axis.tdata; + s_keep_q[s_read_index_q[4:0]] <= read_axis.tkeep; + if (read_axis.tlast) s_state_q <= ReadWait; + else s_read_index_q <= s_read_index_q + 1'b1; + end + if (dma_done_i && (s_abort_pending_q || dma_aborted_i)) begin + s_state_q <= Idle; + s_abort_pending_q <= 1'b0; + if (!s_ring_q) begin + s_direct_done_q <= 1'b1; + s_direct_error_q <= 1'b1; + end + end else if (dma_done_i && dma_error_i) begin + s_result_error_q <= 1'b1; + s_result_code_q <= dma_error_code_i; + s_result_stage_q <= dma_error_stage_i; + s_result_resp_q <= dma_error_resp_i; + s_state_q <= s_ring_q ? Result : Idle; + if (!s_ring_q) begin + s_direct_done_q <= 1'b1; + s_direct_error_q <= 1'b1; + end + end + end + ReadWait: begin + if (dma_done_i) begin + if (s_abort_pending_q || dma_aborted_i) begin + s_state_q <= Idle; + s_abort_pending_q <= 1'b0; + if (!s_ring_q) begin + s_direct_done_q <= 1'b1; + s_direct_error_q <= 1'b1; + end + end else if (dma_error_i) begin + s_result_error_q <= 1'b1; + s_result_code_q <= dma_error_code_i; + s_result_stage_q <= dma_error_stage_i; + s_result_resp_q <= dma_error_resp_i; + s_state_q <= s_ring_q ? Result : Idle; + if (!s_ring_q) begin + s_direct_done_q <= 1'b1; + s_direct_error_q <= 1'b1; + end + end else begin + s_write_index_q <= 6'd0; + s_state_q <= WriteRequest; + end + end + end + WriteRequest: begin + if (abort_i) begin + s_state_q <= Idle; + s_abort_pending_q <= 1'b0; + if (!s_ring_q) begin + s_direct_done_q <= 1'b1; + s_direct_error_q <= 1'b1; + end + end else if (request_ready_i) begin + s_state_q <= WriteData; + end + end + WriteData: begin + if (dma_done_i && (s_abort_pending_q || dma_aborted_i)) begin + s_state_q <= Idle; + s_abort_pending_q <= 1'b0; + if (!s_ring_q) begin + s_direct_done_q <= 1'b1; + s_direct_error_q <= 1'b1; + end + end else if (dma_done_i && dma_error_i) begin + s_result_error_q <= 1'b1; + s_result_code_q <= dma_error_code_i; + s_result_stage_q <= dma_error_stage_i; + s_result_resp_q <= dma_error_resp_i; + s_state_q <= s_ring_q ? Result : Idle; + if (!s_ring_q) begin + s_direct_done_q <= 1'b1; + s_direct_error_q <= 1'b1; + end + end else if (write_axis.tvalid && write_axis.tready) begin + if (write_axis.tlast) s_state_q <= WriteWait; + else s_write_index_q <= s_write_index_q + 1'b1; + end + end + WriteWait: begin + if (dma_done_i) begin + s_result_error_q <= dma_error_i || inject_result_error_i; + s_result_code_q <= dma_error_i ? dma_error_code_i : inject_result_code_i; + s_result_stage_q <= dma_error_i ? dma_error_stage_i : inject_result_stage_i; + s_result_resp_q <= dma_error_i ? dma_error_resp_i : inject_result_resp_i; + if (s_abort_pending_q || dma_aborted_i) begin + s_state_q <= Idle; + s_abort_pending_q <= 1'b0; + end else begin + s_state_q <= s_ring_q ? Result : Idle; + end + if (!s_ring_q) begin + s_direct_done_q <= 1'b1; + s_direct_error_q <= dma_error_i || dma_aborted_i || s_abort_pending_q; + end + end + end + Result: begin + if (result_valid_o && result_ready_i) begin + s_state_q <= Idle; + s_abort_pending_q <= 1'b0; + end + end + default: s_state_q <= Idle; + endcase + end + end + +`ifndef SYNTHESIS + always_ff @(posedge clk_i) begin + if (rst_n_i && job_valid_i && job_ready_o && ((bounded_bytes( + descriptor_i[(3*32)+:32], descriptor_i[(5*32)+:32] + ) == 32'd0) || (bounded_bytes( + descriptor_i[(3*32)+:32], descriptor_i[(5*32)+:32] + ) > 32'd128))) begin + $fatal(1, "apu_p2_transport_backend: ring transfer bytes must be in [1, 128]"); + end + if (rst_n_i && direct_start_i && + ((direct_bytes_i == 32'd0) || (direct_bytes_i > 32'd128))) begin + $fatal(1, "apu_p2_transport_backend: direct transfer bytes must be in [1, 128]"); + end + end +`endif +endmodule diff --git a/tests/rtl/apu_reg_tb.sv b/tests/rtl/apu_reg_tb.sv index e0f8a759..ebf6ad4c 100644 --- a/tests/rtl/apu_reg_tb.sv +++ b/tests/rtl/apu_reg_tb.sv @@ -4,7 +4,7 @@ module apu_reg_tb; localparam logic [31:0] ApuBase = 32'h1001_3000; - localparam int unsigned RegisterCount = 95; + localparam int unsigned RegisterCount = 96; localparam logic [1:0] AccessRo = 2'd0; localparam logic [1:0] AccessWo = 2'd1; localparam logic [1:0] AccessRw = 2'd2; @@ -23,24 +23,98 @@ module apu_reg_tb; logic [31:0] s_reg_reset [RegisterCount]; logic s_reg_full_strobe [RegisterCount]; int unsigned s_reg_count; + string s_phase; apb4_if apb4 ( .pclk (clk_i), .presetn(rst_n_i) ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) axi4 ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) dma_tx_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) dma_rx_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) i2s_tx_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) i2s_rx_axis ( + clk_i, + rst_n_i + ); + + assign axi4.awready = 1'b0; + assign axi4.wready = 1'b0; + assign axi4.bid = '0; + assign axi4.bresp = '0; + assign axi4.buser = '0; + assign axi4.bvalid = 1'b0; + assign axi4.arready = 1'b0; + assign axi4.rid = '0; + assign axi4.rdata = '0; + assign axi4.rresp = '0; + assign axi4.rlast = 1'b0; + assign axi4.ruser = '0; + assign axi4.rvalid = 1'b0; + assign dma_tx_axis.tdata = '0; + assign dma_tx_axis.tkeep = '1; + assign dma_tx_axis.tstrb = '1; + assign dma_tx_axis.tlast = 1'b0; + assign dma_tx_axis.tid = '0; + assign dma_tx_axis.tdest = '0; + assign dma_tx_axis.tuser = '0; + assign dma_tx_axis.tvalid = 1'b0; + assign dma_rx_axis.tready = 1'b1; + assign i2s_tx_axis.tready = 1'b1; + assign i2s_rx_axis.tdata = '0; + assign i2s_rx_axis.tkeep = '1; + assign i2s_rx_axis.tstrb = '1; + assign i2s_rx_axis.tlast = 1'b0; + assign i2s_rx_axis.tid = '0; + assign i2s_rx_axis.tdest = '0; + assign i2s_rx_axis.tuser = '0; + assign i2s_rx_axis.tvalid = 1'b0; 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) + .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), + .bridge_epoch_i (8'd0), + .i2s_tx_underrun_i(1'b0), + .i2s_rx_overrun_i (1'b0), + .apb4 (apb4), + .axi4 (axi4), + .dma_tx_axis (dma_tx_axis), + .dma_rx_axis (dma_rx_axis), + .i2s_tx_axis (i2s_tx_axis), + .i2s_rx_axis (i2s_rx_axis), + .idle_o (idle_o), + .irq_o (irq_o) ); function automatic logic [31:0] legal_write_value(input logic [11:0] offset_i); @@ -50,6 +124,7 @@ module apu_reg_tb; `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__STREAM_WATERMARK: legal_write_value = 32'h0000_2020; `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; @@ -116,7 +191,7 @@ module apu_reg_tb; 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__CAPABILITY0, AccessRo, 32'h0000_0018, 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); @@ -128,7 +203,7 @@ module apu_reg_tb; 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__STREAM_STATUS, AccessRo, 32'h0000_0014, 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); @@ -138,6 +213,7 @@ module apu_reg_tb; 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__STREAM_WATERMARK, AccessRw, 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); @@ -168,7 +244,7 @@ module apu_reg_tb; 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_STATUS, AccessRo, 32'h0000_0004, 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); @@ -269,7 +345,9 @@ module apu_reg_tb; 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); + if (apb4.pslverr != expected_err_i) begin + $fatal(1, "APU write response mismatch at %h in %s", offset_i, s_phase); + end s_response_err = apb4.pslverr; #2; if (!apb4.pready || (apb4.pslverr != s_response_err)) begin @@ -307,7 +385,9 @@ module apu_reg_tb; 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); + if (apb4.pslverr != expected_err_i) begin + $fatal(1, "APU read response mismatch at %h during %s", offset_i, s_phase); + end s_response_data = apb4.prdata; s_response_err = apb4.pslverr; #2; @@ -383,10 +463,12 @@ module apu_reg_tb; initial begin initialize_register_table(); + s_phase = "initial reset matrix"; drive_apb_idle(); hard_reset(); if (!idle_o || irq_o) $fatal(1, "APU reset lifecycle outputs mismatch"); + s_phase = "access matrix"; for (int unsigned register_index = 0; register_index < RegisterCount; register_index++) begin hard_reset(); if (s_reg_access[register_index] == AccessWo) begin @@ -429,6 +511,7 @@ module apu_reg_tb; endcase end + s_phase = "valid byte strobes"; hard_reset(); apb_write(`APB4_APU__SEQUENCER_TIMEOUT, 32'h0000_00aa, 4'h1, 1'b0); expect_read(`APB4_APU__SEQUENCER_TIMEOUT, 32'h0000_ffaa); @@ -439,6 +522,7 @@ module apu_reg_tb; apb_write(`APB4_APU__SEQUENCER_TIMEOUT, 32'hdd00_0000, 4'h8, 1'b0); expect_read(`APB4_APU__SEQUENCER_TIMEOUT, 32'hddcc_bbaa); + s_phase = "invalid full strobes"; for (int unsigned register_index = 0; register_index < RegisterCount; register_index++) begin if (s_reg_full_strobe[register_index]) begin hard_reset(); @@ -452,9 +536,12 @@ module apu_reg_tb; end end + s_phase = "rejected values"; check_rejected_write(`APB4_APU__COMMAND, 32'h0000_0080, `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__COMMAND, 32'h0000_0002, `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_ABORT) && (bit_index != `APB4_APU__COMMAND_CLEAR_COUNTERS)) begin check_rejected_write(`APB4_APU__COMMAND, 32'd1 << bit_index, `APB4_APU__ERROR_CODE_UNSUPPORTED); @@ -469,6 +556,10 @@ module apu_reg_tb; 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__STREAM_WATERMARK, 32'h0000_0041, + `APB4_APU__ERROR_CODE_INVALID_CONFIG); + check_rejected_write(`APB4_APU__STREAM_WATERMARK, 32'h0001_0000, + `APB4_APU__ERROR_CODE_INVALID_CONFIG); 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); @@ -500,6 +591,7 @@ module apu_reg_tb; check_rejected_write(`APB4_APU__PERF_CONTROL, 32'h0000_0008, `APB4_APU__ERROR_CODE_INVALID_CONFIG); + s_phase = "set over clear"; 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; @@ -509,34 +601,71 @@ module apu_reg_tb; apb_write(`APB4_APU__IRQ_STATE, 32'h0000_0001, 4'h1, 1'b0); expect_read(`APB4_APU__IRQ_STATE, 32'd0); + s_phase = "soft reset"; hard_reset(); program_rw_registers(); apb_write(`APB4_APU__COMMAND, 32'h0000_0004, 4'hf, 1'b0); check_reset_state(1'b1); + s_phase = "resource reset"; hard_reset(); program_rw_registers(); + force u_dut.s_dma_busy = 1'b1; @(negedge clk_i); resource_reset_i = 1'b1; + repeat (3) @(posedge clk_i); + if (u_dut.s_resource_reset_apply_q || (u_dut.u_apu_reg.s_timeout_q[0] != 32'h1234_5678)) begin + $fatal(1, "resource reset applied before transport drain"); + end + force u_dut.s_dma_busy = 1'b0; + wait (u_dut.s_resource_reset_apply_q); + release u_dut.s_dma_busy; @(posedge clk_i); @(negedge clk_i); resource_reset_i = 1'b0; check_reset_state(1'b1); + s_phase = "performance enable"; + hard_reset(); + force u_dut.s_dma_busy = 1'b1; + repeat (5) @(posedge clk_i); + if (u_dut.s_active_cycles_q != 64'd0) begin + $fatal(1, "disabled performance counter advanced"); + end + force u_dut.s_dma_busy = 1'b0; + release u_dut.s_dma_busy; + hard_reset(); + apb_write(`APB4_APU__PERF_CONTROL, 32'h0000_0001, 4'hf, 1'b0); + force u_dut.s_dma_busy = 1'b1; + repeat (5) @(posedge clk_i); + if (u_dut.s_active_cycles_q == 64'd0) begin + $fatal(1, "enabled performance counter did not advance"); + end + force u_dut.s_dma_busy = 1'b0; + release u_dut.s_dma_busy; + 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'h064, s_value, 1'b1); apb_read(12'h001, s_value, 1'b1); + s_phase = "command state predicates"; + hard_reset(); + force u_dut.u_apu_reg.core_busy_i = 1'b1; + force u_dut.u_apu_reg.core_idle_i = 1'b0; + apb_write(`APB4_APU__COMMAND, 32'h0000_0002, 4'hf, 1'b0); + apb_write(`APB4_APU__COMMAND, 32'h0000_0040, 4'hf, 1'b1); + expect_read(`APB4_APU__ERROR_STATUS, 32'd1 | (32'(`APB4_APU__ERROR_CODE_INVALID_CONFIG) << 1)); + $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"); + $fatal(1, "APU-P1 APB register matrix timed out in %s", s_phase); end endmodule diff --git a/tests/rtl/apu_ring_scheduler_tb.sv b/tests/rtl/apu_ring_scheduler_tb.sv new file mode 100644 index 00000000..a563fc5a --- /dev/null +++ b/tests/rtl/apu_ring_scheduler_tb.sv @@ -0,0 +1,626 @@ +`timescale 1ns / 1ps + +`include "apu_define.svh" + +module apu_ring_scheduler_tb; + logic clk_i = 1'b0; + logic rst_n_i = 1'b0; + logic soft_reset_i = 1'b0; + logic counter_clear_i = 1'b0; + logic abort_i = 1'b0; + logic quiesce_i = 1'b0; + logic start_i = 1'b0; + logic [31:0] ring_base_i = 32'h0000_1000; + logic [ 8:0] ring_size_i = 9'd2; + logic [ 7:0] ring_tail_i = 8'd0; + logic ring_enable_i = 1'b0; + logic stop_on_error_i = 1'b0; + logic [ 7:0] coalesce_count_i = 8'd3; + logic [15:0] coalesce_timeout_i = 16'd4; + logic dma_request_valid_o, dma_request_ready_i, dma_request_write_o; + logic [31:0] dma_request_addr_o, dma_request_bytes_o; + logic dma_done_i = 1'b0; + logic dma_error_i = 1'b0; + logic [ 5:0] dma_error_code_i = 6'd0; + logic [ 3:0] dma_error_stage_i = 4'd0; + logic [ 1:0] dma_error_resp_i = 2'd0; + logic [31:0] dma_error_addr_i = 32'd0; + logic backend_job_valid, backend_job_ready; + logic [1023:0] backend_descriptor; + logic [ 7:0] backend_index; + logic backend_result_valid, backend_result_ready; + logic backend_result_error = 1'b0; + logic [5:0] backend_result_code = 6'd0; + logic [3:0] backend_result_stage = 4'd0; + logic [1:0] backend_result_resp = 2'd0; + logic backend_latched_error; + logic [5:0] backend_latched_code; + logic [3:0] backend_latched_stage; + logic [1:0] backend_latched_resp; + logic [31:0] backend_input_used, backend_output_bytes, backend_frames; + logic [31:0] backend_source_info, backend_cycles, backend_detail; + logic backend_hold = 1'b0; + logic hold_dma_request = 1'b0; + logic hold_dma_read = 1'b0; + logic hold_dma_write = 1'b0; + logic [31:0] backend_accepted; + logic [31:0] job_status_o, ring_status_o; + logic [ 7:0] ring_head_o; + logic [31:0] ring_completed_o; + logic ring_event_o, error_event_o, abort_done_o, aborting_o, idle_o; + logic [ 5:0] error_code_o; + logic [ 3:0] error_stage_o; + logic [ 1:0] error_resp_o; + logic [ 7:0] error_index_o; + logic [31:0] error_addr_o; + logic [31:0] memory [0:63]; + logic dma_active_q, dma_write_q; + logic [5:0] dma_word_q, dma_last_word_q; + logic inject_dma_error_q; + int unsigned ring_events, error_events, abort_events; + int unsigned events_before_invalid, errors_before_invalid; + string s_phase; + + axi4_stream_if #( + .DATA_WIDTH(32) + ) dma_read_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) dma_write_axis ( + clk_i, + rst_n_i + ); + + always #5 clk_i = ~clk_i; + + assign dma_request_ready_i = !dma_active_q && !hold_dma_request; + assign dma_read_axis.tdata = memory[((dma_request_addr_o-ring_base_i)>>2)+dma_word_q]; + assign dma_read_axis.tkeep = 4'hf; + assign dma_read_axis.tstrb = 4'hf; + assign dma_read_axis.tlast = dma_word_q == dma_last_word_q; + assign dma_read_axis.tid = '0; + assign dma_read_axis.tdest = '0; + assign dma_read_axis.tuser = '0; + assign dma_read_axis.tvalid = dma_active_q && !dma_write_q && !hold_dma_read; + assign dma_write_axis.tready = dma_active_q && dma_write_q && !hold_dma_write; + + apu_ring_scheduler u_dut ( + .clk_i, + .rst_n_i, + .soft_reset_i, + .counter_clear_i, + .abort_i, + .quiesce_i, + .start_i, + .ring_base_i, + .ring_size_i, + .ring_tail_i, + .ring_enable_i, + .stop_on_error_i, + .coalesce_count_i, + .coalesce_timeout_i, + .dma_request_valid_o, + .dma_request_ready_i, + .dma_request_write_o, + .dma_request_addr_o, + .dma_request_bytes_o, + .dma_read_axis, + .dma_write_axis, + .dma_done_i, + .dma_error_i, + .dma_error_code_i, + .dma_error_stage_i, + .dma_error_resp_i, + .dma_error_addr_i, + .backend_job_valid_o (backend_job_valid), + .backend_job_ready_i (backend_job_ready), + .backend_descriptor_o (backend_descriptor), + .backend_index_o (backend_index), + .backend_result_valid_i(backend_result_valid), + .backend_result_ready_o(backend_result_ready), + .backend_result_error_i(backend_latched_error), + .backend_result_code_i (backend_latched_code), + .backend_result_stage_i(backend_latched_stage), + .backend_result_resp_i (backend_latched_resp), + .backend_input_used_i (backend_input_used), + .backend_output_bytes_i(backend_output_bytes), + .backend_frames_i (backend_frames), + .backend_source_info_i (backend_source_info), + .backend_cycles_i (backend_cycles), + .backend_detail_i (backend_detail), + .job_status_o, + .ring_status_o, + .ring_head_o, + .ring_completed_o, + .ring_event_o, + .error_event_o, + .error_code_o, + .error_stage_o, + .error_resp_o, + .error_index_o, + .error_addr_o, + .abort_done_o, + .aborting_o, + .idle_o + ); + + apu_p2_backend u_backend ( + .clk_i, + .rst_n_i, + .abort_i, + .hold_result_i (backend_hold), + .result_error_i (backend_result_error), + .result_code_i (backend_result_code), + .result_stage_i (backend_result_stage), + .result_resp_i (backend_result_resp), + .job_valid_i (backend_job_valid), + .job_ready_o (backend_job_ready), + .descriptor_i (backend_descriptor), + .index_i (backend_index), + .result_valid_o (backend_result_valid), + .result_ready_i (backend_result_ready), + .result_error_o (backend_latched_error), + .result_code_o (backend_latched_code), + .result_stage_o (backend_latched_stage), + .result_resp_o (backend_latched_resp), + .input_used_o (backend_input_used), + .output_bytes_o (backend_output_bytes), + .frames_o (backend_frames), + .source_info_o (backend_source_info), + .cycles_o (backend_cycles), + .detail_o (backend_detail), + .accepted_jobs_o(backend_accepted) + ); + + task automatic pulse_start; + begin + @(negedge clk_i); + start_i = 1'b1; + @(negedge clk_i); + start_i = 1'b0; + end + endtask + + task automatic pulse_abort; + begin + @(negedge clk_i); + abort_i = 1'b1; + @(negedge clk_i); + abort_i = 1'b0; + end + endtask + + task automatic hard_reset; + begin + rst_n_i = 1'b0; + repeat (3) @(posedge clk_i); + @(negedge clk_i); + rst_n_i = 1'b1; + repeat (2) @(posedge clk_i); + end + endtask + + task automatic wait_completed(input logic [31:0] expected_i); + int unsigned timeout; + begin + timeout = 0; + while ((ring_completed_o != expected_i) && (timeout < 1000)) begin + @(posedge clk_i); + timeout++; + end + if (ring_completed_o != expected_i) $fatal(1, "ring completion timeout in %s", s_phase); + end + endtask + + task automatic clear_descriptor(input int unsigned descriptor_i); + for (int unsigned word_index = 0; word_index < 32; word_index++) begin + memory[(descriptor_i*32)+word_index] = 32'd0; + end + endtask + + task automatic make_valid_descriptor(input int unsigned descriptor_i, input logic ioc_i); + begin + clear_descriptor(descriptor_i); + memory[descriptor_i*32] = (32'd1 << `APB4_APU__DESCRIPTOR_CONTROL_OWN) | + (ioc_i ? (32'd1 << `APB4_APU__DESCRIPTOR_CONTROL_IOC) : 32'd0); + memory[(descriptor_i*32)+2] = 32'h0000_4000 + (descriptor_i * 32'h100); + memory[(descriptor_i*32)+3] = 32'd256 + descriptor_i; + memory[(descriptor_i*32)+4] = 32'h0000_8000 + (descriptor_i * 32'h100); + memory[(descriptor_i*32)+5] = 32'd512 + descriptor_i; + memory[(descriptor_i*32)+16] = 32'hc001_0000 + descriptor_i; + memory[(descriptor_i*32)+17] = 32'hc002_0000 + descriptor_i; + end + endtask + + task automatic abort_writeback_state(input logic [3:0] state_i); + begin + hard_reset(); + clear_descriptor(0); + make_valid_descriptor(0, 1'b0); + ring_size_i = 9'd2; + ring_tail_i = 8'd1; + ring_enable_i = 1'b1; + backend_hold = 1'b1; + hold_dma_request = 1'b0; + hold_dma_write = 1'b0; + pulse_start(); + wait (u_dut.s_state_q == 4'd5); + @(negedge clk_i); + if (state_i == 4'd6) hold_dma_request = 1'b1; + if (state_i == 4'd7) hold_dma_write = 1'b1; + backend_hold = 1'b0; + if (state_i == 4'd9) begin + wait (u_dut.s_state_q == 4'd8); + @(negedge clk_i); + hold_dma_request = 1'b1; + end + if (state_i == 4'd10) begin + wait (u_dut.s_state_q == 4'd9); + @(negedge clk_i); + hold_dma_write = 1'b1; + end + wait (u_dut.s_state_q == state_i); + pulse_abort(); + hold_dma_request = 1'b0; + hold_dma_write = 1'b0; + wait (abort_done_o); + wait (idle_o); + if (memory[0][31] || (ring_completed_o != 32'd1)) begin + $fatal(1, "abort did not retire ordered writeback state %0d", state_i); + end + end + endtask + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + dma_active_q <= 1'b0; + dma_write_q <= 1'b0; + dma_word_q <= 6'd0; + dma_last_word_q <= 6'd0; + dma_done_i <= 1'b0; + dma_error_i <= 1'b0; + inject_dma_error_q <= 1'b0; + ring_events <= 0; + error_events <= 0; + abort_events <= 0; + end else begin + dma_done_i <= 1'b0; + dma_error_i <= 1'b0; + if (dma_request_valid_o && dma_request_ready_i) begin + if ((!dma_request_write_o && + ((dma_request_bytes_o != 32'd128) || (dma_request_addr_o[6:0] != 7'd0))) || + (dma_request_write_o && (dma_request_bytes_o == 32'd124) && + (dma_request_addr_o[6:0] != 7'd4)) || + (dma_request_write_o && (dma_request_bytes_o == 32'd4) && + (dma_request_addr_o[6:0] != 7'd0)) || + (dma_request_write_o && (dma_request_bytes_o != 32'd124) && + (dma_request_bytes_o != 32'd4))) begin + $fatal(1, "malformed descriptor DMA request"); + end + if (dma_request_write_o && (dma_request_bytes_o == 32'd124) && + !memory[((dma_request_addr_o-ring_base_i)>>2)-1][31]) begin + $fatal(1, "descriptor OWN cleared before result writeback"); + end + if (dma_request_write_o && (dma_request_bytes_o == 32'd4) && + (!memory[((dma_request_addr_o-ring_base_i)>>2)][31] || + (memory[((dma_request_addr_o-ring_base_i)>>2)+1] == 32'd0))) begin + $fatal(1, "descriptor OWN publication preceded result retirement"); + end + dma_active_q <= 1'b1; + dma_write_q <= dma_request_write_o; + dma_word_q <= 6'd0; + dma_last_word_q <= 6'(((dma_request_bytes_o + 32'd3) >> 2) - 1'b1); + end + if (dma_read_axis.tvalid && dma_read_axis.tready) begin + if (dma_word_q == dma_last_word_q) begin + dma_active_q <= 1'b0; + dma_done_i <= 1'b1; + dma_error_i <= inject_dma_error_q; + dma_error_code_i <= inject_dma_error_q ? 6'd15 : 6'd0; + dma_error_stage_i <= inject_dma_error_q ? 4'd3 : 4'd0; + dma_error_resp_i <= inject_dma_error_q ? 2'd2 : 2'd0; + dma_error_addr_i <= dma_request_addr_o; + inject_dma_error_q <= 1'b0; + end else begin + dma_word_q <= dma_word_q + 1'b1; + end + end + if (dma_write_axis.tvalid && dma_write_axis.tready) begin + memory[((dma_request_addr_o-ring_base_i)>>2)+dma_word_q] <= dma_write_axis.tdata; + if (dma_word_q == dma_last_word_q) begin + dma_active_q <= 1'b0; + dma_done_i <= 1'b1; + end else begin + dma_word_q <= dma_word_q + 1'b1; + end + end + if (ring_event_o) ring_events <= ring_events + 1; + if (error_event_o) error_events <= error_events + 1; + if (abort_done_o) abort_events <= abort_events + 1; + end + end + + initial begin + for (int unsigned index = 0; index < 64; index++) memory[index] = 32'd0; + s_phase = "reset"; + repeat (3) @(posedge clk_i); + rst_n_i = 1'b1; + repeat (2) @(posedge clk_i); + if (ring_status_o != 32'h0000_0004 || job_status_o != 32'd0) begin + $fatal(1, "ring/job reset status mismatch"); + end + ring_size_i = 9'd256; + force u_dut.s_head_q = 8'd254; + #1; + if (u_dut.s_next_head != 8'd255) $fatal(1, "256-entry ring truncated before index 255"); + force u_dut.s_head_q = 8'd255; + #1; + if (u_dut.s_next_head != 8'd0) $fatal(1, "256-entry ring did not wrap after index 255"); + release u_dut.s_head_q; + + s_phase = "stale head validation"; + ring_size_i = 9'd2; + ring_tail_i = 8'd1; + ring_enable_i = 1'b1; + force u_dut.s_head_q = 8'd3; + pulse_start(); + #1; + if (!error_event_o || (error_code_o != 6'd2) || dma_active_q) begin + $fatal(1, "stale ring head outside the configured extent was accepted"); + end + release u_dut.s_head_q; + + soft_reset_i = 1'b1; + @(posedge clk_i); + @(negedge clk_i); + soft_reset_i = 1'b0; + ring_size_i = 9'd2; + + s_phase = "backend request abort exclusion"; + make_valid_descriptor(0, 1'b0); + ring_tail_i = 8'd1; + ring_enable_i = 1'b1; + pulse_start(); + wait (u_dut.s_state_q == 4'd4); + @(negedge clk_i); + abort_i = 1'b1; + #1; + if (backend_job_valid || backend_job_ready) begin + $fatal(1, "backend handshake remained eligible during abort"); + end + @(negedge clk_i); + abort_i = 1'b0; + wait_completed(32'd1); + if ((backend_accepted != 32'd0) || !memory[1][2]) begin + $fatal(1, "backend request abort was accepted or not published"); + end + + hard_reset(); + clear_descriptor(0); + s_phase = "simultaneous backend error and abort"; + backend_hold = 1'b1; + backend_result_error = 1'b1; + backend_result_code = 6'd7; + backend_result_stage = 4'd4; + make_valid_descriptor(0, 1'b0); + ring_tail_i = 8'd1; + ring_enable_i = 1'b1; + pulse_start(); + wait (u_dut.s_state_q == 4'd5); + @(negedge clk_i); + backend_hold = 1'b0; + abort_i = 1'b1; + @(negedge clk_i); + abort_i = 1'b0; + wait_completed(32'd1); + @(posedge clk_i); + if (!memory[1][1] || !memory[1][2] || (memory[1][8:3] != 6'd7) || + (error_events != 1) || (abort_events != 1)) begin + $fatal(1, "simultaneous backend error did not outrank and retain abort"); + end + + backend_result_error = 1'b0; + backend_result_code = 6'd0; + backend_result_stage = 4'd0; + hard_reset(); + clear_descriptor(0); + + s_phase = "idle-active abort"; + ring_tail_i = 8'd0; + ring_enable_i = 1'b1; + pulse_start(); + wait (ring_status_o[`APB4_APU__RING_STATUS_ACTIVE] && idle_o); + pulse_abort(); + wait (abort_done_o); + + hard_reset(); + clear_descriptor(0); + make_valid_descriptor(0, 1'b0); + s_phase = "fetch-request abort"; + hold_dma_request = 1'b1; + ring_tail_i = 8'd1; + ring_enable_i = 1'b1; + pulse_start(); + wait (u_dut.s_state_q == 4'd1); + pulse_abort(); + hold_dma_request = 1'b0; + wait (abort_done_o); + if (!memory[0][31]) $fatal(1, "FetchRequest abort mutated descriptor ownership"); + + hard_reset(); + clear_descriptor(0); + s_phase = "stalled-unowned abort"; + ring_tail_i = 8'd1; + ring_enable_i = 1'b1; + pulse_start(); + wait (u_dut.s_state_q == 4'd12); + pulse_abort(); + wait (abort_done_o); + + hard_reset(); + clear_descriptor(0); + + for (int unsigned writeback_state = 6; writeback_state <= 11; writeback_state++) begin + s_phase = "writeback abort matrix"; + abort_writeback_state(4'(writeback_state)); + end + hard_reset(); + clear_descriptor(0); + + s_phase = "timeout coalescing"; + make_valid_descriptor(0, 1'b0); + ring_enable_i = 1'b1; + ring_tail_i = 8'd1; + pulse_start(); + wait_completed(32'd1); + if (memory[0][31] || !memory[1][0] || (ring_head_o != 8'd1) || !job_status_o[1] || + (memory[16] != 32'hc001_0000) || (memory[17] != 32'hc002_0000) || + ({memory[21], memory[20]} < {memory[19], memory[18]}) || + (memory[22] != 32'd0) || (memory[23] != 32'd0)) begin + $fatal(1, "successful descriptor writeback mismatch"); + end + while (ring_events == 0) @(posedge clk_i); + if (ring_status_o[5] || (ring_status_o[15:8] != 8'd0)) begin + $fatal(1, "coalescing timeout did not clear pending state"); + end + + s_phase = "unowned and IOC wrap"; + clear_descriptor(1); + ring_tail_i = 8'd0; + repeat (40) @(posedge clk_i); + if (!ring_status_o[1] || (ring_head_o != 8'd1) || (ring_completed_o != 32'd1)) begin + $fatal(1, "unowned descriptor was not stalled"); + end + make_valid_descriptor(1, 1'b1); + pulse_start(); + wait_completed(32'd2); + @(posedge clk_i); + if ((ring_head_o != 8'd0) || !ring_status_o[4] || (ring_events != 2)) begin + $fatal(1, "IOC/wrap behavior mismatch events=%0d", ring_events); + end + + s_phase = "count coalescing"; + coalesce_count_i = 8'd2; + coalesce_timeout_i = 16'd100; + make_valid_descriptor(0, 1'b0); + ring_tail_i = 8'd1; + pulse_start(); + wait_completed(32'd3); + if ((ring_events != 2) || !ring_status_o[5] || (ring_status_o[15:8] != 8'd1)) begin + $fatal(1, "coalescing count epoch did not retain first completion"); + end + make_valid_descriptor(1, 1'b0); + ring_tail_i = 8'd0; + pulse_start(); + wait_completed(32'd4); + @(posedge clk_i); + if ((ring_events != 3) || ring_status_o[5] || (ring_status_o[15:8] != 8'd0)) begin + $fatal(1, "coalescing count threshold mismatch"); + end + + s_phase = "deterministic randomized descriptors"; + coalesce_count_i = 8'd4; + coalesce_timeout_i = 16'd7; + for (int unsigned job = 0; job < 8; job++) begin + automatic int unsigned descriptor = ring_head_o; + automatic logic expected_error = ((job * 5 + 1) % 7) == 2; + make_valid_descriptor(descriptor, ((job * 3 + 1) % 5) == 0); + memory[(descriptor*32)+3] = 32'd33 + ((job * 29) % 511); + memory[(descriptor*32)+5] = 32'd65 + ((job * 17) % 1023); + backend_result_error = expected_error; + backend_result_code = expected_error ? 6'd7 : 6'd0; + backend_result_stage = expected_error ? 4'd4 : 4'd0; + ring_tail_i = descriptor == 0 ? 8'd1 : 8'd0; + pulse_start(); + wait_completed(32'(5 + job)); + @(posedge clk_i); + if (memory[descriptor*32][31] || + (memory[(descriptor*32)+1][1] != expected_error) || + (memory[(descriptor*32)+1][0] == expected_error)) begin + $fatal(1, "randomized descriptor result mismatch job=%0d", job); + end + end + backend_result_error = 1'b0; + backend_result_code = 6'd0; + backend_result_stage = 4'd0; + + s_phase = "invalid stop on error"; + events_before_invalid = ring_events; + errors_before_invalid = error_events; + ring_enable_i = 1'b0; + repeat (2) @(posedge clk_i); + stop_on_error_i = 1'b1; + make_valid_descriptor(0, 1'b1); + memory[0][12] = 1'b1; + memory[10] = 32'hdead_beef; + ring_tail_i = 8'd1; + ring_enable_i = 1'b1; + pulse_start(); + wait_completed(32'd13); + @(posedge clk_i); + if (memory[0][31] || !memory[1][1] || (memory[1][8:3] != 6'd2) || + (memory[10] != 32'd0) || + !ring_status_o[3] || !ring_status_o[7] || ring_status_o[0] || + (ring_events != events_before_invalid + 1) || + (error_events != errors_before_invalid + 1)) begin + $fatal(1, "invalid-owned stop-on-error behavior mismatch"); + end + + s_phase = "abort writeback"; + ring_enable_i = 1'b0; + stop_on_error_i = 1'b0; + repeat (2) @(posedge clk_i); + make_valid_descriptor(1, 1'b0); + ring_tail_i = 8'd0; + backend_hold = 1'b1; + ring_enable_i = 1'b1; + pulse_start(); + while (!backend_job_valid && !backend_result_ready) @(posedge clk_i); + repeat (2) @(posedge clk_i); + pulse_abort(); + if (!aborting_o) $fatal(1, "scheduler live aborting status was not asserted"); + backend_hold = 1'b0; + wait_completed(32'd14); + @(posedge clk_i); + if (memory[32][31] || !memory[33][2] || !job_status_o[3] || + (abort_events != 1) || ring_status_o[0]) begin + $fatal(1, "abort terminal writeback mismatch"); + end + if (aborting_o) $fatal(1, "scheduler live aborting status remained set after writeback"); + + s_phase = "descriptor DMA error"; + ring_enable_i = 1'b0; + repeat (2) @(posedge clk_i); + make_valid_descriptor(0, 1'b0); + ring_tail_i = 8'd1; + inject_dma_error_q = 1'b1; + ring_enable_i = 1'b1; + pulse_start(); + while (!error_event_o) @(posedge clk_i); + repeat (2) @(posedge clk_i); + if ((ring_completed_o != 32'd14) || !ring_status_o[3] || ring_status_o[0] || + (error_code_o != 6'd15) || (error_stage_o != 4'd3) || + (error_resp_o != 2'd2) || (error_index_o != 8'd0) || + (error_addr_o != ring_base_i)) begin + $fatal(1, "descriptor DMA error attribution mismatch"); + end + + if (backend_accepted != 32'd13) $fatal(1, "unexpected backend job count"); + @(negedge clk_i); + counter_clear_i = 1'b1; + @(negedge clk_i); + counter_clear_i = 1'b0; + #1; + if (ring_completed_o != 32'd0) $fatal(1, "ring completion counter clear failed"); + $display("APU-P2 ring scheduler/backend tests passed"); + $finish; + end + + initial begin + repeat (10000) @(posedge clk_i); + $fatal(1, "APU-P2 ring scheduler test timed out in %s state=%0d", s_phase, u_dut.s_state_q); + end +endmodule diff --git a/tests/rtl/apu_stream_router_tb.sv b/tests/rtl/apu_stream_router_tb.sv new file mode 100644 index 00000000..e6facfcd --- /dev/null +++ b/tests/rtl/apu_stream_router_tb.sv @@ -0,0 +1,294 @@ +`timescale 1ns / 1ps + +module apu_stream_router_tb; + logic clk_i = 1'b0; + logic rst_n_i = 1'b0; + logic flush_i = 1'b0; + logic counter_clear_i = 1'b0; + logic xrun_clear_i = 1'b0; + logic tx_route_apu_i = 1'b0; + logic rx_route_apu_i = 1'b0; + logic tx_session_active_i = 1'b0; + logic rx_session_active_i = 1'b0; + logic [ 7:0] rx_high_watermark_i = 8'd0; + logic [ 7:0] tx_low_watermark_i = 8'd0; + logic tx_underrun_i = 1'b0; + logic rx_overrun_i = 1'b0; + logic [31:0] status_o; + logic input_watermark_evt_o; + logic output_watermark_evt_o; + logic stream_xrun_evt_o; + logic idle_o; + logic input_watermark_seen; + logic output_watermark_seen; + + axi4_stream_if #( + .DATA_WIDTH(32) + ) dma_tx_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) dma_rx_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) i2s_tx_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) i2s_rx_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) apu_tx_axis ( + clk_i, + rst_n_i + ); + axi4_stream_if #( + .DATA_WIDTH(32) + ) apu_rx_axis ( + clk_i, + rst_n_i + ); + + always #5 clk_i = ~clk_i; + always @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + input_watermark_seen <= 1'b0; + output_watermark_seen <= 1'b0; + end else begin + if (input_watermark_evt_o) input_watermark_seen <= 1'b1; + if (output_watermark_evt_o) output_watermark_seen <= 1'b1; + end + end + + apu_stream_router u_dut ( + .clk_i, + .rst_n_i, + .flush_i, + .counter_clear_i, + .xrun_clear_i, + .tx_route_apu_i, + .rx_route_apu_i, + .tx_session_active_i, + .rx_session_active_i, + .rx_high_watermark_i, + .tx_low_watermark_i, + .tx_underrun_i, + .rx_overrun_i, + .dma_tx_axis, + .dma_rx_axis, + .i2s_tx_axis, + .i2s_rx_axis, + .apu_tx_axis, + .apu_rx_axis, + .status_o, + .input_watermark_evt_o, + .output_watermark_evt_o, + .stream_xrun_evt_o, + .idle_o + ); + + task automatic push_apu_tx(input logic [31:0] data_i); + begin + @(negedge clk_i); + apu_tx_axis.tdata = data_i; + apu_tx_axis.tvalid = 1'b1; + do @(posedge clk_i); while (!apu_tx_axis.tready); + @(negedge clk_i); + apu_tx_axis.tvalid = 1'b0; + end + endtask + + task automatic push_i2s_rx(input logic [31:0] data_i); + begin + @(negedge clk_i); + i2s_rx_axis.tdata = data_i; + i2s_rx_axis.tvalid = 1'b1; + do @(posedge clk_i); while (!i2s_rx_axis.tready); + @(negedge clk_i); + i2s_rx_axis.tvalid = 1'b0; + end + endtask + + + task automatic pop_i2s_tx(input logic [31:0] expected_i, input int unsigned stall_cycles_i); + logic [31:0] held_data; + begin + i2s_tx_axis.tready = 1'b0; + held_data = i2s_tx_axis.tdata; + repeat (stall_cycles_i) begin + @(posedge clk_i); + #1; + if (!i2s_tx_axis.tvalid || (i2s_tx_axis.tdata != held_data)) begin + $fatal(1, "TX payload changed under backpressure"); + end + end + if (held_data != expected_i) $fatal(1, "TX FIFO ordering mismatch"); + @(negedge clk_i); + i2s_tx_axis.tready = 1'b1; + @(posedge clk_i); + @(negedge clk_i); + i2s_tx_axis.tready = 1'b0; + end + endtask + + task automatic pop_apu_rx(input logic [31:0] expected_i, input int unsigned stall_cycles_i); + logic [31:0] held_data; + begin + apu_rx_axis.tready = 1'b0; + held_data = apu_rx_axis.tdata; + repeat (stall_cycles_i) begin + @(posedge clk_i); + #1; + if (!apu_rx_axis.tvalid || (apu_rx_axis.tdata != held_data)) begin + $fatal(1, "RX payload changed under backpressure"); + end + end + if (held_data != expected_i) $fatal(1, "RX FIFO ordering mismatch"); + @(negedge clk_i); + apu_rx_axis.tready = 1'b1; + @(posedge clk_i); + @(negedge clk_i); + apu_rx_axis.tready = 1'b0; + end + endtask + + initial begin + dma_tx_axis.tdata = 32'h1122_3344; + dma_tx_axis.tkeep = 4'hf; + dma_tx_axis.tstrb = 4'hf; + dma_tx_axis.tlast = 1'b1; + dma_tx_axis.tid = '0; + dma_tx_axis.tdest = '0; + dma_tx_axis.tuser = '0; + dma_tx_axis.tvalid = 1'b0; + dma_rx_axis.tready = 1'b1; + i2s_tx_axis.tready = 1'b1; + i2s_rx_axis.tdata = 32'd0; + i2s_rx_axis.tkeep = 4'hf; + i2s_rx_axis.tstrb = 4'hf; + i2s_rx_axis.tlast = 1'b0; + i2s_rx_axis.tid = '0; + i2s_rx_axis.tdest = '0; + i2s_rx_axis.tuser = '0; + i2s_rx_axis.tvalid = 1'b0; + apu_tx_axis.tdata = 32'd0; + apu_tx_axis.tkeep = 4'hf; + apu_tx_axis.tstrb = 4'hf; + apu_tx_axis.tlast = 1'b0; + apu_tx_axis.tid = '0; + apu_tx_axis.tdest = '0; + apu_tx_axis.tuser = '0; + apu_tx_axis.tvalid = 1'b0; + apu_rx_axis.tready = 1'b0; + repeat (3) @(posedge clk_i); + rst_n_i = 1'b1; + #1; + if ((status_o != 32'h0000_0014) || !idle_o) $fatal(1, "stream reset status mismatch"); + + dma_tx_axis.tvalid = 1'b1; + #1; + if (!i2s_tx_axis.tvalid || (i2s_tx_axis.tdata != 32'h1122_3344) || !dma_tx_axis.tready) + $fatal(1, "central DMA TX bypass failed"); + dma_tx_axis.tvalid = 1'b0; + i2s_rx_axis.tdata = 32'haabb_ccdd; + i2s_rx_axis.tvalid = 1'b1; + #1; + if (!dma_rx_axis.tvalid || (dma_rx_axis.tdata != 32'haabb_ccdd) || !i2s_rx_axis.tready) + $fatal(1, "central DMA RX bypass failed"); + i2s_rx_axis.tvalid = 1'b0; + + tx_route_apu_i = 1'b1; + rx_route_apu_i = 1'b1; + tx_session_active_i = 1'b1; + rx_session_active_i = 1'b1; + tx_low_watermark_i = 8'd2; + rx_high_watermark_i = 8'd2; + i2s_tx_axis.tready = 1'b0; + push_apu_tx(32'd1); + push_apu_tx(32'd2); + push_apu_tx(32'd3); + if (!status_o[0]) $fatal(1, "APU TX route did not become active"); + i2s_tx_axis.tready = 1'b1; + @(posedge clk_i); + #1; + if (!output_watermark_seen) $fatal(1, "TX low-watermark crossing was not detected"); + + push_i2s_rx(32'h1010_1010); + push_i2s_rx(32'h2020_2020); + if (!input_watermark_seen) $fatal(1, "RX high-watermark crossing was not detected"); + apu_rx_axis.tready = 1'b1; + repeat (2) @(posedge clk_i); + + @(negedge clk_i); + flush_i = 1'b1; + @(negedge clk_i); + flush_i = 1'b0; + i2s_tx_axis.tready = 1'b0; + apu_rx_axis.tready = 1'b0; + for (int unsigned word_index = 0; word_index < 64; word_index++) begin + push_apu_tx(32'h7100_0000 + word_index); + end + #1; + if (!status_o[3] || apu_tx_axis.tready) $fatal(1, "TX FIFO did not reach depth 64"); + for (int unsigned word_index = 0; word_index < 64; word_index++) begin + pop_i2s_tx(32'h7100_0000 + word_index, (word_index * 5 + 1) % 4); + end + #1; + if (!status_o[2] || (status_o[15:8] != 8'd64)) begin + $fatal(1, "TX FIFO empty/count status mismatch"); + end + + for (int unsigned word_index = 0; word_index < 64; word_index++) begin + push_i2s_rx(32'h7200_0000 + word_index); + end + #1; + if (!status_o[5] || i2s_rx_axis.tready) $fatal(1, "RX FIFO did not reach depth 64"); + for (int unsigned word_index = 0; word_index < 64; word_index++) begin + pop_apu_rx(32'h7200_0000 + word_index, (word_index * 7 + 2) % 5); + end + #1; + if (!status_o[4] || (status_o[23:16] != 8'd64)) begin + $fatal(1, "RX FIFO empty/count status mismatch"); + end + counter_clear_i = 1'b1; + @(posedge clk_i); + counter_clear_i = 1'b0; + #1; + if (status_o[23:8] != 16'd0) $fatal(1, "stream counter clear failed"); + + tx_underrun_i = 1'b1; + rx_overrun_i = 1'b1; + #1; + if (!stream_xrun_evt_o) $fatal(1, "xrun event pulse failed"); + @(posedge clk_i); + tx_underrun_i = 1'b0; + rx_overrun_i = 1'b0; + #1; + if (status_o[7:6] != 2'b11) $fatal(1, "xrun capture failed"); + xrun_clear_i = 1'b1; + tx_underrun_i = 1'b1; + @(posedge clk_i); + xrun_clear_i = 1'b0; + tx_underrun_i = 1'b0; + #1; + if (!status_o[6]) $fatal(1, "hardware xrun did not win same-cycle clear"); + + $display("APU-P2 stream router tests passed"); + $finish; + end + + initial begin + repeat (5000) @(posedge clk_i); + $fatal(1, "APU-P2 stream router timed out"); + end +endmodule diff --git a/tests/rtl/axi4_async_bridge_tb.sv b/tests/rtl/axi4_async_bridge_tb.sv index d8885379..8af9f341 100644 --- a/tests/rtl/axi4_async_bridge_tb.sv +++ b/tests/rtl/axi4_async_bridge_tb.sv @@ -118,7 +118,14 @@ module axi4_async_bridge_tb; wait (!clear_busy_o); if (epoch_o != 8'd1) $fatal(1, "async bridge epoch did not advance"); - $display("AXI4 async bridge transfer and warm flush test passed"); + @(negedge dst_clk_i); + dst_rst_n_i = 1'b0; + repeat (3) @(posedge src_clk_i); + @(negedge dst_clk_i); + dst_rst_n_i = 1'b1; + wait (epoch_o == 8'd2); + + $display("AXI4 async bridge transfer and warm flush test passed; unilateral reset passed"); $finish; end diff --git a/tests/rtl/axi4_target_guard_tb.sv b/tests/rtl/axi4_target_guard_tb.sv index df70a84c..f1b027a9 100644 --- a/tests/rtl/axi4_target_guard_tb.sv +++ b/tests/rtl/axi4_target_guard_tb.sv @@ -74,6 +74,28 @@ module axi4_target_guard_tb; end endtask + task automatic return_read_beat(input logic [5:0] id, input logic last, + input logic expected_last, input logic [1:0] expected_resp); + begin + @(negedge clk_i); + sink.rid = id; + sink.rdata = 64'h0123_4567_89ab_cdef; + sink.rresp = 2'b00; + sink.rlast = last; + sink.rvalid = 1'b1; + wait (source.rvalid); + #1; + if ((source.rid != id) || (source.rresp != expected_resp) || + (source.rlast != expected_last)) begin + $fatal(1, "target guard read normalization mismatch"); + end + do @(posedge clk_i); while (!sink.rready); + @(negedge clk_i); + sink.rvalid = 1'b0; + sink.rlast = 1'b0; + end + endtask + initial begin source.awid = '0; source.awaddr = '0; @@ -124,6 +146,15 @@ module axi4_target_guard_tb; repeat (3) @(posedge clk_i); rst_n_i = 1'b1; + issue_read(6'h07, 8'd1, 32'h3800_0020); + do @(posedge clk_i); while (!sink.arvalid); + return_read_beat(6'h07, 1'b0, 1'b0, 2'b00); + return_read_beat(6'h07, 1'b0, 1'b1, 2'b10); + + issue_read(6'h08, 8'd0, 32'h3800_0030); + do @(posedge clk_i); while (!sink.arvalid); + return_read_beat(6'h08, 1'b1, 1'b1, 2'b00); + issue_read(6'h09, 8'd1, 32'h3800_0040); wait (timeout_valid_o); if (timeout_write_o || (timeout_id_o != 6'h09) || (timeout_addr_o != 32'h3800_0040)) begin diff --git a/tests/rtl/hp_axi4_mux3_rr_tb.sv b/tests/rtl/hp_axi4_mux3_rr_tb.sv new file mode 100644 index 00000000..812cbfee --- /dev/null +++ b/tests/rtl/hp_axi4_mux3_rr_tb.sv @@ -0,0 +1,532 @@ +`timescale 1ns / 1ps + +module hp_axi4_mux3_rr_tb; + logic clk_i = 1'b0; + logic rst_n_i = 1'b0; + logic [ 7:0] epoch_i = 8'd0; + logic [ 2:0] read_request = 3'd0; + logic [ 2:0] write_request = 3'd0; + logic [ 2:0] write_data_valid = 3'b111; + logic [ 2:0] read_ready = 3'b111; + logic [ 2:0] write_response_ready = 3'b111; + logic response_valid_q; + logic [31:0] response_data_q; + logic write_active_q; + logic write_response_valid_q; + logic [31:0] write_address_q; + logic mixed_mode_q; + logic recovery_response_q = 1'b0; + logic allow_read_address = 1'b1; + logic allow_write_address = 1'b1; + int unsigned response_count [0:2]; + int unsigned write_response_count [0:2]; + int unsigned mixed_response_count [0:2]; + int unsigned total_responses; + int unsigned total_write_responses; + int unsigned total_mixed_responses; + logic [15:0] cycle_q; + string s_phase; + + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) icache ( + clk_i, + rst_n_i + ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) dcache ( + clk_i, + rst_n_i + ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) mmio ( + clk_i, + rst_n_i + ); + axi4_if #( + .ADDR_WIDTH(32), + .DATA_WIDTH(32), + .ID_WIDTH (1), + .USER_WIDTH(1) + ) axi4 ( + clk_i, + rst_n_i + ); + + always #5 clk_i = ~clk_i; + + assign icache.arid = 1'b0; + assign icache.araddr = 32'h0000_1000; + assign dcache.arid = 1'b0; + assign dcache.araddr = 32'h0000_2000; + assign mmio.arid = 1'b0; + assign mmio.araddr = 32'h0000_3000; + assign icache.arvalid = read_request[0]; + assign dcache.arvalid = read_request[1]; + assign mmio.arvalid = read_request[2]; + + assign icache.arlen = 8'd0; + assign dcache.arlen = 8'd0; + assign mmio.arlen = 8'd0; + assign icache.arsize = 3'd2; + assign dcache.arsize = 3'd2; + assign mmio.arsize = 3'd2; + assign icache.arburst = 2'd1; + assign dcache.arburst = 2'd1; + assign mmio.arburst = 2'd1; + assign icache.arlock = 1'b0; + assign dcache.arlock = 1'b0; + assign mmio.arlock = 1'b0; + assign icache.arcache = 4'd0; + assign dcache.arcache = 4'd0; + assign mmio.arcache = 4'd0; + assign icache.arprot = 3'd0; + assign dcache.arprot = 3'd0; + assign mmio.arprot = 3'd0; + assign icache.arqos = 4'd0; + assign dcache.arqos = 4'd0; + assign mmio.arqos = 4'd0; + assign icache.arregion = 4'd0; + assign dcache.arregion = 4'd0; + assign mmio.arregion = 4'd0; + assign icache.aruser = 1'b0; + assign dcache.aruser = 1'b0; + assign mmio.aruser = 1'b0; + assign icache.rready = read_ready[0]; + assign dcache.rready = read_ready[1]; + assign mmio.rready = read_ready[2]; + + assign icache.awid = 1'b0; + assign dcache.awid = 1'b0; + assign mmio.awid = 1'b0; + assign icache.awaddr = 32'h0000_4000; + assign dcache.awaddr = 32'h0000_5000; + assign mmio.awaddr = 32'h0000_6000; + assign icache.awlen = 8'd0; + assign dcache.awlen = 8'd0; + assign mmio.awlen = 8'd0; + assign icache.awsize = 3'd2; + assign dcache.awsize = 3'd2; + assign mmio.awsize = 3'd2; + assign icache.awburst = 2'd1; + assign dcache.awburst = 2'd1; + assign mmio.awburst = 2'd1; + assign icache.awlock = 1'b0; + assign dcache.awlock = 1'b0; + assign mmio.awlock = 1'b0; + assign icache.awcache = 4'd0; + assign dcache.awcache = 4'd0; + assign mmio.awcache = 4'd0; + assign icache.awprot = 3'd0; + assign dcache.awprot = 3'd0; + assign mmio.awprot = 3'd0; + assign icache.awqos = 4'd0; + assign dcache.awqos = 4'd0; + assign mmio.awqos = 4'd0; + assign icache.awregion = 4'd0; + assign dcache.awregion = 4'd0; + assign mmio.awregion = 4'd0; + assign icache.awuser = 1'b0; + assign dcache.awuser = 1'b0; + assign mmio.awuser = 1'b0; + assign icache.awvalid = write_request[0]; + assign dcache.awvalid = write_request[1]; + assign mmio.awvalid = write_request[2]; + assign icache.wdata = 32'ha000_0000; + assign dcache.wdata = 32'hb000_0000; + assign mmio.wdata = 32'hc000_0000; + assign icache.wstrb = 4'hf; + assign dcache.wstrb = 4'hf; + assign mmio.wstrb = 4'hf; + assign icache.wlast = 1'b1; + assign dcache.wlast = 1'b1; + assign mmio.wlast = 1'b1; + assign icache.wuser = 1'b0; + assign dcache.wuser = 1'b0; + assign mmio.wuser = 1'b0; + assign icache.wvalid = write_request[0] && write_data_valid[0]; + assign dcache.wvalid = write_request[1] && write_data_valid[1]; + assign mmio.wvalid = write_request[2] && write_data_valid[2]; + assign icache.bready = write_response_ready[0]; + assign dcache.bready = write_response_ready[1]; + assign mmio.bready = write_response_ready[2]; + + assign axi4.awready = allow_write_address && !write_active_q && + !write_response_valid_q && cycle_q[0]; + assign axi4.wready = write_active_q && cycle_q[1]; + assign axi4.bid = 1'b0; + assign axi4.bresp = 2'd0; + assign axi4.buser = 1'b0; + assign axi4.bvalid = write_response_valid_q; + assign axi4.arready = allow_read_address && !response_valid_q && cycle_q[0]; + assign axi4.rid = 1'b0; + assign axi4.rdata = response_data_q; + assign axi4.rresp = 2'd0; + assign axi4.rlast = 1'b1; + assign axi4.ruser = 1'b0; + assign axi4.rvalid = response_valid_q; + + hp_axi4_mux3 #( + .RoundRobin (1'b1), + .Client0EpochAware(1'b1) + ) u_dut ( + .clk_i, + .rst_n_i, + .epoch_i, + .icache, + .dcache, + .mmio, + .axi4 + ); + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + response_valid_q <= 1'b0; + response_data_q <= 32'd0; + response_count[0] <= 0; + response_count[1] <= 0; + response_count[2] <= 0; + write_response_count[0] <= 0; + write_response_count[1] <= 0; + write_response_count[2] <= 0; + mixed_response_count[0] <= 0; + mixed_response_count[1] <= 0; + mixed_response_count[2] <= 0; + total_responses <= 0; + total_write_responses <= 0; + total_mixed_responses <= 0; + write_active_q <= 1'b0; + write_response_valid_q <= 1'b0; + write_address_q <= 32'd0; + cycle_q <= 16'd0; + end else if (epoch_i != u_dut.s_epoch_q) begin + response_valid_q <= 1'b0; + write_active_q <= 1'b0; + write_response_valid_q <= 1'b0; + end else begin + cycle_q <= cycle_q + 1'b1; + if (axi4.arvalid && axi4.arready) begin + response_valid_q <= 1'b1; + response_data_q <= axi4.araddr; + end + if (axi4.rvalid && axi4.rready) response_valid_q <= 1'b0; + if (axi4.awvalid && axi4.awready) begin + write_active_q <= 1'b1; + write_address_q <= axi4.awaddr; + end + if (axi4.wvalid && axi4.wready) begin + unique case (write_address_q) + 32'h0000_4000: if (axi4.wdata != 32'ha000_0000) $fatal(1, "APU write data mismatch"); + 32'h0000_5000: if (axi4.wdata != 32'hb000_0000) $fatal(1, "SDIO0 write data mismatch"); + 32'h0000_6000: if (axi4.wdata != 32'hc000_0000) $fatal(1, "USB2 write data mismatch"); + default: $fatal(1, "Gateway A write owner address changed"); + endcase + write_active_q <= 1'b0; + write_response_valid_q <= 1'b1; + end + if (axi4.bvalid && axi4.bready) write_response_valid_q <= 1'b0; + if (icache.rvalid && icache.rready) begin + if (recovery_response_q) begin + if ((icache.rresp != 2'b11) || !icache.rlast) begin + $fatal(1, "Gateway A malformed synthetic APU read response"); + end + end else if ((!mixed_mode_q && ((total_responses % 3) != 0)) || + (icache.rdata != 32'h0000_1000)) begin + $fatal(1, "Gateway A round-robin order/ownership failure for APU"); + end + if (mixed_mode_q) begin + mixed_response_count[0] <= mixed_response_count[0] + 1; + total_mixed_responses <= total_mixed_responses + 1; + end else begin + response_count[0] <= response_count[0] + 1; + total_responses <= total_responses + 1; + end + end + if (dcache.rvalid && dcache.rready) begin + if (recovery_response_q) begin + if ((dcache.rresp != 2'b11) || !dcache.rlast) begin + $fatal(1, "Gateway A malformed synthetic SDIO0 read response"); + end + end else if ((!mixed_mode_q && ((total_responses % 3) != 1)) || + (dcache.rdata != 32'h0000_2000)) begin + $fatal(1, "Gateway A round-robin order/ownership failure for SDIO0"); + end + if (mixed_mode_q) begin + mixed_response_count[1] <= mixed_response_count[1] + 1; + total_mixed_responses <= total_mixed_responses + 1; + end else begin + response_count[1] <= response_count[1] + 1; + total_responses <= total_responses + 1; + end + end + if (mmio.rvalid && mmio.rready) begin + if (recovery_response_q) begin + if ((mmio.rresp != 2'b11) || !mmio.rlast) begin + $fatal(1, "Gateway A malformed synthetic USB2 read response"); + end + end else if ((!mixed_mode_q && ((total_responses % 3) != 2)) || + (mmio.rdata != 32'h0000_3000)) begin + $fatal(1, "Gateway A round-robin order/ownership failure for USB2"); + end + if (mixed_mode_q) begin + mixed_response_count[2] <= mixed_response_count[2] + 1; + total_mixed_responses <= total_mixed_responses + 1; + end else begin + response_count[2] <= response_count[2] + 1; + total_responses <= total_responses + 1; + end + end + if (icache.bvalid && icache.bready) begin + if (recovery_response_q) begin + if (icache.bresp != 2'b11) $fatal(1, "Gateway A malformed synthetic APU B response"); + end else if (mixed_mode_q) begin + mixed_response_count[0] <= mixed_response_count[0] + 1; + total_mixed_responses <= total_mixed_responses + 1; + end else begin + write_response_count[0] <= write_response_count[0] + 1; + total_write_responses <= total_write_responses + 1; + end + end + if (dcache.bvalid && dcache.bready) begin + if (recovery_response_q) begin + if (dcache.bresp != 2'b11) begin + $fatal(1, "Gateway A malformed synthetic SDIO0 B response"); + end + end else if (mixed_mode_q) begin + mixed_response_count[1] <= mixed_response_count[1] + 1; + total_mixed_responses <= total_mixed_responses + 1; + end else begin + write_response_count[1] <= write_response_count[1] + 1; + total_write_responses <= total_write_responses + 1; + end + end + if (mmio.bvalid && mmio.bready) begin + if (recovery_response_q) begin + if (mmio.bresp != 2'b11) $fatal(1, "Gateway A malformed synthetic USB2 B response"); + end else if (mixed_mode_q) begin + mixed_response_count[2] <= mixed_response_count[2] + 1; + total_mixed_responses <= total_mixed_responses + 1; + end else begin + write_response_count[2] <= write_response_count[2] + 1; + total_write_responses <= total_write_responses + 1; + end + end + end + end + + initial begin + s_phase = "read fairness"; + mixed_mode_q = 1'b0; + repeat (3) @(posedge clk_i); + rst_n_i = 1'b1; + @(negedge clk_i); + read_request = 3'b111; + read_ready = 3'd0; + wait (axi4.rvalid); + repeat (3) begin + @(posedge clk_i); + #1; + unique case (response_data_q) + 32'h0000_1000: + if (!icache.rvalid || dcache.rvalid || mmio.rvalid || !axi4.rlast) + $fatal(1, "Gateway A did not retain the APU R owner/RLAST"); + 32'h0000_2000: + if (icache.rvalid || !dcache.rvalid || mmio.rvalid || !axi4.rlast) + $fatal(1, "Gateway A did not retain the SDIO0 R owner/RLAST"); + 32'h0000_3000: + if (icache.rvalid || dcache.rvalid || !mmio.rvalid || !axi4.rlast) + $fatal(1, "Gateway A did not retain the USB2 R owner/RLAST"); + default: $fatal(1, "Gateway A retained an unknown R owner"); + endcase + end + read_ready = 3'b111; + wait (total_responses >= 18); + @(negedge clk_i); + read_request = 3'd0; + if ((response_count[0] != 6) || (response_count[1] != 6) || (response_count[2] != 6)) begin + $fatal(1, "Gateway A client starvation detected"); + end + + s_phase = "write fairness"; + write_response_ready = 3'd0; + write_request = 3'b111; + wait (axi4.bvalid); + repeat (3) begin + @(posedge clk_i); + #1; + unique case (write_address_q) + 32'h0000_4000: + if (!icache.bvalid || dcache.bvalid || mmio.bvalid || !axi4.bvalid) + $fatal(1, "Gateway A did not retain the APU B owner"); + 32'h0000_5000: + if (icache.bvalid || !dcache.bvalid || mmio.bvalid || !axi4.bvalid) + $fatal(1, "Gateway A did not retain the SDIO0 B owner"); + 32'h0000_6000: + if (icache.bvalid || dcache.bvalid || !mmio.bvalid || !axi4.bvalid) + $fatal(1, "Gateway A did not retain the USB2 B owner"); + default: $fatal(1, "Gateway A retained an unknown B owner"); + endcase + end + write_response_ready = 3'b111; + wait (total_write_responses >= 18); + @(negedge clk_i); + write_request = 3'd0; + if ((write_response_count[0] != 6) || (write_response_count[1] != 6) || + (write_response_count[2] != 6)) begin + $fatal(1, "Gateway A write client starvation detected"); + end + + s_phase = "mixed fairness"; + wait (!write_active_q && !write_response_valid_q); + mixed_mode_q = 1'b1; + read_request = 3'b101; + write_request = 3'b010; + wait (total_mixed_responses >= 9); + @(negedge clk_i); + read_request = 3'd0; + write_request = 3'd0; + if ((mixed_response_count[0] != 3) || (mixed_response_count[1] != 3) || + (mixed_response_count[2] != 3)) begin + $fatal(1, "Gateway A mixed-traffic fairness mismatch"); + end + + s_phase = "dynamic read address hold"; + wait (!response_valid_q && !write_active_q && !write_response_valid_q); + mixed_mode_q = 1'b1; + allow_read_address = 1'b0; + read_request = 3'b010; + wait (axi4.arvalid); + repeat (2) @(posedge clk_i); + @(negedge clk_i); + read_request = 3'b011; + repeat (3) begin + @(posedge clk_i); + #1; + if (!axi4.arvalid || (axi4.araddr != 32'h0000_2000)) begin + $fatal(1, "Gateway A changed a stalled read address owner"); + end + end + allow_read_address = 1'b1; + do @(posedge clk_i); while (!dcache.arready); + @(negedge clk_i); + read_request = 3'd0; + wait (u_dut.s_state_q == 3'd0); + + s_phase = "dynamic write address hold"; + allow_write_address = 1'b0; + write_request = 3'b100; + wait (axi4.awvalid); + repeat (2) @(posedge clk_i); + @(negedge clk_i); + write_request = 3'b101; + repeat (3) begin + @(posedge clk_i); + #1; + if (!axi4.awvalid || (axi4.awaddr != 32'h0000_6000)) begin + $fatal(1, "Gateway A changed a stalled write address owner"); + end + end + allow_write_address = 1'b1; + do @(posedge clk_i); while (!mmio.awready); + @(negedge clk_i); + write_request = 3'b100; + wait (u_dut.s_state_q == 3'd0); + write_request = 3'd0; + + s_phase = "active response flush recovery"; + read_ready = 3'd0; + read_request = 3'b001; + wait (icache.rvalid); + @(negedge clk_i); + epoch_i = epoch_i + 1'b1; + @(negedge clk_i); + read_request = 3'b010; + read_ready = 3'b111; + do @(posedge clk_i); while (!dcache.arready); + @(negedge clk_i); + read_request = 3'd0; + wait (u_dut.s_state_q == 3'd0); + + @(negedge clk_i); + s_phase = "non-aware read recovery"; + read_ready = 3'd0; + read_request = 3'b010; + s_phase = "non-aware read accepted"; + do @(posedge clk_i); while (!dcache.rvalid); + @(negedge clk_i); + epoch_i = epoch_i + 1'b1; + read_request = 3'd0; + s_phase = "non-aware read recovery state"; + wait (u_dut.s_state_q == 3'd3); + s_phase = "non-aware read retained response"; + repeat (3) begin + @(posedge clk_i); + #1; + if (!dcache.rvalid || icache.rvalid || mmio.rvalid || (dcache.rresp != 2'b11) || + !dcache.rlast || axi4.arvalid || axi4.awvalid) begin + $fatal(1, "Gateway A did not retain synthetic read recovery response"); + end + end + @(negedge clk_i); + recovery_response_q = 1'b1; + read_ready = 3'b111; + @(posedge clk_i); + @(negedge clk_i); + recovery_response_q = 1'b0; + s_phase = "non-aware read completion"; + wait (u_dut.s_state_q == 3'd0); + + s_phase = "non-aware write recovery"; + write_response_ready = 3'd0; + write_data_valid = 3'b011; + write_request = 3'b100; + wait (u_dut.s_state_q == 3'd2); + @(negedge clk_i); + epoch_i = epoch_i + 1'b1; + wait (u_dut.s_state_q == 3'd4); + if (axi4.awvalid || axi4.arvalid) $fatal(1, "Gateway A issued an address during recovery"); + repeat (2) @(posedge clk_i); + @(negedge clk_i); + write_data_valid = 3'b111; + wait (u_dut.s_state_q == 3'd5); + write_request = 3'd0; + repeat (3) begin + @(posedge clk_i); + #1; + if (!mmio.bvalid || icache.bvalid || dcache.bvalid || (mmio.bresp != 2'b11) || + axi4.awvalid || axi4.arvalid) begin + $fatal(1, "Gateway A did not retain synthetic write recovery response"); + end + end + @(negedge clk_i); + recovery_response_q = 1'b1; + write_response_ready = 3'b111; + @(posedge clk_i); + @(negedge clk_i); + recovery_response_q = 1'b0; + wait (u_dut.s_state_q == 3'd0); + $display("APU-P2 Gateway A round-robin fairness passed with read/write/mixed retention"); + $finish; + end + + initial begin + repeat (1000) @(posedge clk_i); + $fatal( + 1, + "Gateway A round-robin test timed out in %s state=%0d rr=%0d read=%b write=%b rv=%0d bv=%0d", + s_phase, u_dut.s_state_q, u_dut.s_rr_selected, read_request, write_request, + response_valid_q, write_response_valid_q); + end + +endmodule diff --git a/tests/test_apu.py b/tests/test_apu.py index 577f34a4..24dac722 100644 --- a/tests/test_apu.py +++ b/tests/test_apu.py @@ -32,7 +32,14 @@ def test_apu_p1_apb_register_shell(tmp_path: Path) -> None: f"+incdir+{common / 'interface'}", f"+incdir+{multimedia}", str(common / "interface/apb4_if.sv"), + str(common / "interface/axi4_if.sv"), + str(common / "interface/axi4_stream_if.sv"), str(common / "utils/register.sv"), + str(common / "utils/fifo.sv"), + str(ROOT / "rtl/ip/peripheral/dma_axi4_master.sv"), + str(multimedia / "apu_dma.sv"), + str(multimedia / "apu_ring_scheduler.sv"), + str(multimedia / "apu_stream_router.sv"), str(multimedia / "apu_reg.sv"), str(multimedia / "apb4_apu.sv"), str(ROOT / "tests/rtl/apu_reg_tb.sv"), @@ -97,7 +104,14 @@ def test_apu_p1_integrated_irq_ownership_topology(tmp_path: Path) -> None: f"+incdir+{common / 'interface'}", f"+incdir+{multimedia}", str(common / "interface/apb4_if.sv"), + str(common / "interface/axi4_if.sv"), + str(common / "interface/axi4_stream_if.sv"), str(common / "utils/register.sv"), + str(common / "utils/fifo.sv"), + str(ROOT / "rtl/ip/peripheral/dma_axi4_master.sv"), + str(multimedia / "apu_dma.sv"), + str(multimedia / "apu_ring_scheduler.sv"), + str(multimedia / "apu_stream_router.sv"), str(multimedia / "apu_reg.sv"), str(multimedia / "apb4_apu.sv"), str(ROOT / "rtl/mini/top/resource_controller.sv"), @@ -135,3 +149,279 @@ def test_apu_p1_integrated_irq_ownership_topology(tmp_path: Path) -> None: ) result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True) assert "APU-P1 integrated IRQ ownership topology passed" in result.stdout + + +def test_apu_p2_stream_router(tmp_path: Path) -> None: + iverilog = shutil.which("iverilog") + vvp = shutil.which("vvp") + if iverilog is None or vvp is None: + return + + common = ROOT / "rtl/managed/clusterip/common/rtl" + source_list = tmp_path / "apu_stream_router.fl" + source_list.write_text( + "\n".join( + [ + f"+incdir+{common}", + f"+incdir+{common / 'interface'}", + str(common / "interface/axi4_stream_if.sv"), + str(common / "utils/fifo.sv"), + str(ROOT / "rtl/ip/multimedia/apu_stream_router.sv"), + str(ROOT / "tests/rtl/apu_stream_router_tb.sv"), + "", + ] + ), + encoding="utf-8", + ) + converted = tmp_path / "apu_stream_router_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_stream_router_tb" + subprocess.run( + [ + iverilog, + "-g2012", + "-s", + "apu_stream_router_tb", + "-o", + str(simulation), + str(converted), + ], + check=True, + ) + result = subprocess.run([vvp, str(simulation)], check=True, capture_output=True, text=True) + assert "APU-P2 stream router tests passed" in result.stdout + + +def test_apu_p2_private_dma(tmp_path: Path) -> None: + iverilog = shutil.which("iverilog") + vvp = shutil.which("vvp") + if iverilog is None or vvp is None: + return + + common = ROOT / "rtl/managed/clusterip/common/rtl" + source_list = tmp_path / "apu_dma.fl" + source_list.write_text( + "\n".join( + [ + f"+incdir+{common}", + f"+incdir+{common / 'interface'}", + f"+incdir+{ROOT / 'rtl/ip/multimedia'}", + str(common / "interface/axi4_if.sv"), + str(common / "interface/axi4_stream_if.sv"), + str(common / "utils/register.sv"), + str(ROOT / "rtl/ip/peripheral/dma_axi4_master.sv"), + str(ROOT / "rtl/ip/multimedia/apu_dma.sv"), + str(ROOT / "tests/rtl/apu_dma_tb.sv"), + "", + ] + ), + encoding="utf-8", + ) + converted = tmp_path / "apu_dma_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_dma_tb" + subprocess.run( + [iverilog, "-g2012", "-s", "apu_dma_tb", "-o", str(simulation), str(converted)], + check=True, + ) + result = subprocess.run([vvp, str(simulation)], check=True, capture_output=True, text=True) + assert "APU-P2 DMA tests passed" in result.stdout + + +def test_apu_p2_ring_scheduler_backend(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" + source_list = tmp_path / "apu_ring_scheduler.fl" + source_list.write_text( + "\n".join( + [ + f"+incdir+{common}", + f"+incdir+{common / 'interface'}", + f"+incdir+{ROOT / 'rtl/ip/multimedia'}", + str(common / "interface/axi4_stream_if.sv"), + str(ROOT / "rtl/ip/multimedia/apu_ring_scheduler.sv"), + str(ROOT / "tests/rtl/apu_p2_backend.sv"), + str(ROOT / "tests/rtl/apu_ring_scheduler_tb.sv"), + "", + ] + ), + encoding="utf-8", + ) + converted = tmp_path / "apu_ring_scheduler_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_ring_scheduler_tb" + subprocess.run( + [ + iverilog, + "-g2012", + "-s", + "apu_ring_scheduler_tb", + "-o", + str(simulation), + str(converted), + ], + check=True, + ) + result = subprocess.run([vvp, str(simulation)], check=True, capture_output=True, text=True) + assert "APU-P2 ring scheduler/backend tests passed" in result.stdout + + +def test_apu_p2_integrated_dma_ring_backend(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_p2_integration.fl" + source_list.write_text( + "\n".join( + [ + f"+incdir+{common}", + f"+incdir+{common / 'interface'}", + f"+incdir+{common / 'cdc'}", + f"+incdir+{multimedia}", + str(common / "interface/axi4_if.sv"), + str(common / "interface/axi4_stream_if.sv"), + str(common / "utils/register.sv"), + str(common / "utils/fifo.sv"), + str(common / "utils/xchecker.sv"), + str(common / "utils/spill_register.sv"), + str(common / "utils/bin2gray.sv"), + str(common / "utils/gray2bin.sv"), + str(common / "stream/round_robin_arbiter.sv"), + str(common / "cdc/cdc_sync.sv"), + str(common / "cdc/cdc_rst_ctrlr.sv"), + str(common / "cdc/cdc_2phase.sv"), + str(common / "clkrst/rst_sync.sv"), + str(ROOT / "rtl/ip/peripheral/dma_axi4_master.sv"), + str(multimedia / "apu_dma.sv"), + str(multimedia / "apu_ring_scheduler.sv"), + str(ROOT / "rtl/mini/top/soc_common_cdc.sv"), + str(ROOT / "rtl/mini/top/axi4_async_bridge.sv"), + str(ROOT / "rtl/mini/top/axi4_target_guard.sv"), + str(ROOT / "rtl/mini/top/hp_axi4_mux3.sv"), + str(ROOT / "tests/rtl/apu_p2_transport_backend.sv"), + str(ROOT / "tests/rtl/apu_p2_integration_tb.sv"), + "", + ] + ), + encoding="utf-8", + ) + converted = tmp_path / "apu_p2_integration_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_p2_integration_tb" + subprocess.run( + [ + iverilog, + "-g2012", + "-s", + "apu_p2_integration_tb", + "-o", + str(simulation), + str(converted), + ], + check=True, + ) + result = subprocess.run([vvp, str(simulation)], check=True, capture_output=True, text=True) + assert "APU-P2 integrated DMA/ring/backend tests passed" in result.stdout + + +def test_apu_p2_gateway_a_round_robin_fairness(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" + source_list = tmp_path / "gateway_a_rr.fl" + source_list.write_text( + "\n".join( + [ + f"+incdir+{common}", + f"+incdir+{common / 'interface'}", + str(common / "interface/axi4_if.sv"), + str(common / "utils/register.sv"), + str(common / "stream/round_robin_arbiter.sv"), + str(ROOT / "rtl/mini/top/hp_axi4_mux3.sv"), + str(ROOT / "tests/rtl/hp_axi4_mux3_rr_tb.sv"), + "", + ] + ), + encoding="utf-8", + ) + converted = tmp_path / "hp_axi4_mux3_rr_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 / "hp_axi4_mux3_rr_tb" + subprocess.run( + [ + iverilog, + "-g2012", + "-s", + "hp_axi4_mux3_rr_tb", + "-o", + str(simulation), + str(converted), + ], + check=True, + ) + result = subprocess.run([vvp, str(simulation)], check=True, capture_output=True, text=True) + assert "APU-P2 Gateway A round-robin fairness passed with read/write/mixed retention" in result.stdout diff --git a/tests/test_clock_reset_domains.py b/tests/test_clock_reset_domains.py index 80e052fe..5329266d 100644 --- a/tests/test_clock_reset_domains.py +++ b/tests/test_clock_reset_domains.py @@ -52,6 +52,7 @@ def test_clock_reset_domain_inventory_matches_the_rcu() -> None: ("jtag_dmi", "jtag", "lp"), ("hp_mmio", "hp", "lp"), ("io_dma_data", "pclk", "hp"), + ("apu_data", "pclk", "hp"), } clint = next( crossing for crossing in document["crossings"] if crossing["name"] == "clint_timebase" diff --git a/tests/test_script_tools.py b/tests/test_script_tools.py index 9cf3e469..065807ea 100644 --- a/tests/test_script_tools.py +++ b/tests/test_script_tools.py @@ -329,6 +329,8 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non clint_filelist = tmp_path / "clint.fl" onchip_ram_filelist = tmp_path / "onchip_ram.fl" opipsram_filelist = tmp_path / "opipsram.fl" + apu_filelist = tmp_path / "apu.fl" + gateway_a_filelist = tmp_path / "gateway_a.fl" assert generate_formal_filelist("bus", bus_filelist, memory_map, topology, user_extensions) assert generate_formal_filelist( "rib_adapter", rib_adapter_filelist, memory_map, topology, user_extensions @@ -354,6 +356,10 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non assert generate_formal_filelist( "opipsram", opipsram_filelist, memory_map, topology, user_extensions ) + assert generate_formal_filelist("apu", apu_filelist, memory_map, topology, user_extensions) + assert generate_formal_filelist( + "gateway_a", gateway_a_filelist, memory_map, topology, user_extensions + ) bus = parse_filelists([bus_filelist], require_files=False) rib_adapter = parse_filelists([rib_adapter_filelist], require_files=False) @@ -366,6 +372,8 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non clint = parse_filelists([clint_filelist], require_files=False) onchip_ram = parse_filelists([onchip_ram_filelist], require_files=False) opipsram = parse_filelists([opipsram_filelist], require_files=False) + apu = parse_filelists([apu_filelist], require_files=False) + gateway_a = parse_filelists([gateway_a_filelist], require_files=False) assert "+define+SV_ASSRT_DISABLE" in bus.defines assert "+define+PDK_BEHAV" in onchip_ram.defines assert "+define+SYNTHESIS" in onchip_ram.defines @@ -421,6 +429,11 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non assert ROOT / "rtl/tech/tc_clk.sv" in opipsram.files assert ROOT / "rtl/tech/tc_opipsram_delay.sv" in opipsram.files assert ROOT / "rtl/mini/formal/opipsram_formal.sv" in opipsram.files + assert ROOT / "rtl/ip/multimedia/apu_dma.sv" in apu.files + assert ROOT / "rtl/mini/formal/apu_formal.sv" in apu.files + assert ROOT / "rtl/mini/top/hp_axi4_mux3.sv" in gateway_a.files + assert ROOT / "rtl/managed/clusterip/common/rtl/stream/round_robin_arbiter.sv" in gateway_a.files + assert ROOT / "rtl/mini/formal/gateway_a_formal.sv" in gateway_a.files def test_opipsram_formal_keeps_full_depth_with_hosted_runner_budget() -> None: