feat: add NPU register tests for RTL and SDK synchronization

- Introduced  to ensure consistency between RTL and C register definitions.
- Implemented exhaustive checks for offsets, scalar encodings, bit shifts, and enumerations.
- Added  for shell and control-CDC unit tests using Icarus and Verilator.
- Created test cases for NPU register and control CDC functionality, verifying successful simulation outputs.
This commit is contained in:
maksyuki committed 2026-09-18 20:47:18 +08:00
1 parent f85ff59e33
commit f94cf5e7e2
54 files changed
+6482 -190

No files matched your search

+55
View File
@@ -11,7 +11,9 @@
#include <retrosoc/hal/fabric_monitor.h> #include <retrosoc/hal/fabric_monitor.h>
#include <retrosoc/hal/ga2d.h> #include <retrosoc/hal/ga2d.h>
#include <retrosoc/hal/gpio.h> #include <retrosoc/hal/gpio.h>
#include <retrosoc/hal/npu_regs.h>
#include <retrosoc/hal/onchip_sram.h> #include <retrosoc/hal/onchip_sram.h>
#include <retrosoc/hal/resource.h>
#include <retrosoc/hal/rng.h> #include <retrosoc/hal/rng.h>
#include <retrosoc/hal/rtc.h> #include <retrosoc/hal/rtc.h>
#include <retrosoc/hal/sdram.h> #include <retrosoc/hal/sdram.h>
@@ -41,6 +43,7 @@ static volatile uint32_t rs_ci_smoke_external_irq_sequence_count;
static volatile uint32_t rs_ci_smoke_timer_irq_count; static volatile uint32_t rs_ci_smoke_timer_irq_count;
static volatile uint32_t rs_ci_smoke_software_irq_count; static volatile uint32_t rs_ci_smoke_software_irq_count;
static volatile uint32_t rs_ci_smoke_ga2d_irq_count; static volatile uint32_t rs_ci_smoke_ga2d_irq_count;
static volatile uint32_t rs_ci_smoke_npu_irq_count;
static volatile uint8_t rs_ci_smoke_ga2d_fill_buffer[RS_CI_SMOKE_GA2D_BUFFER_BYTES]; static volatile uint8_t rs_ci_smoke_ga2d_fill_buffer[RS_CI_SMOKE_GA2D_BUFFER_BYTES];
static volatile uint8_t rs_ci_smoke_ga2d_copy_source_buffer[RS_CI_SMOKE_GA2D_BUFFER_BYTES]; static volatile uint8_t rs_ci_smoke_ga2d_copy_source_buffer[RS_CI_SMOKE_GA2D_BUFFER_BYTES];
static volatile uint8_t rs_ci_smoke_ga2d_copy_destination_buffer[RS_CI_SMOKE_GA2D_BUFFER_BYTES]; static volatile uint8_t rs_ci_smoke_ga2d_copy_destination_buffer[RS_CI_SMOKE_GA2D_BUFFER_BYTES];
@@ -97,6 +100,13 @@ static void rs_ci_smoke_ga2d_irq_handler(uintptr_t mcause, uintptr_t stack_point
++rs_ci_smoke_ga2d_irq_count; ++rs_ci_smoke_ga2d_irq_count;
} }
static void rs_ci_smoke_npu_irq_handler(uintptr_t mcause, uintptr_t stack_pointer) {
(void)mcause;
(void)stack_pointer;
RS_NPU_REG(RS_NPU_REG_IRQ_STATE) = RS_NPU_IRQ_ALL;
++rs_ci_smoke_npu_irq_count;
}
static void rs_ci_smoke_force_external_irq(uint32_t id) { static void rs_ci_smoke_force_external_irq(uint32_t id) {
const uint32_t bank = id >> 4U; const uint32_t bank = id >> 4U;
const uint32_t mask = UINT32_C(1) << (id & UINT32_C(0xf)); const uint32_t mask = UINT32_C(1) << (id & UINT32_C(0xf));
@@ -133,6 +143,13 @@ static bool rs_ci_smoke_wait_ga2d_irq(uint32_t target) {
return rs_ci_smoke_ga2d_irq_count >= target; return rs_ci_smoke_ga2d_irq_count >= target;
} }
static bool rs_ci_smoke_wait_npu_irq(uint32_t target) {
for (rs_timeout_t timeout = RS_TIMEOUT_DEFAULT;
(timeout != 0U) && (rs_ci_smoke_npu_irq_count < target); --timeout) {
}
return rs_ci_smoke_npu_irq_count >= target;
}
static bool rs_ci_smoke_external_single(uint32_t id) { static bool rs_ci_smoke_external_single(uint32_t id) {
rs_ci_smoke_external_irq_count = 0U; rs_ci_smoke_external_irq_count = 0U;
rs_ci_smoke_external_irq_sequence_count = 0U; rs_ci_smoke_external_irq_sequence_count = 0U;
@@ -613,6 +630,36 @@ static bool rs_ci_smoke_ga2d_irq(void) {
__disable_ext_irq(); __disable_ext_irq();
return true; return true;
} }
static bool rs_ci_smoke_npu_irq(void) {
bool passed;
rs_ci_smoke_npu_irq_count = 0U;
if (rs_resource_set_owner(RS_RESOURCE_NPU, RS_RESOURCE_OWNER_LP, false) != RS_OK) {
return false;
}
if ((RS_NPU_REG(RS_NPU_REG_IP_ID) != RS_NPU_IP_ID_VALUE) ||
(RS_NPU_REG(RS_NPU_REG_IP_VERSION) != RS_NPU_IP_VERSION_VALUE) ||
(RS_NPU_REG(RS_NPU_REG_CAPABILITY) != RS_NPU_CAPABILITY_P2) ||
(RS_NPU_REG(RS_NPU_REG_IRQ_STATE) != 0U)) {
return false;
}
RS_NPU_REG(RS_NPU_REG_IRQ_STATE) = RS_NPU_IRQ_ALL;
RS_NPU_REG(RS_NPU_REG_IRQ_ENABLE) = RS_NPU_IRQ_ALL;
if (rs_irq_enable_external(RS_SOC_EXT_IRQ_NPU, rs_ci_smoke_npu_irq_handler) != RS_OK) {
return false;
}
RS_NPU_REG(RS_NPU_REG_IRQ_TEST) = RS_NPU_IRQ_ALL;
__enable_irq();
passed = rs_ci_smoke_wait_npu_irq(1U);
__disable_irq();
RS_NPU_REG(RS_NPU_REG_IRQ_ENABLE) = 0U;
if (rs_irq_disable_external(RS_SOC_EXT_IRQ_NPU) != RS_OK) {
passed = false;
}
__disable_ext_irq();
return passed;
}
#else #else
static bool rs_ci_smoke_external_irq(void) { static bool rs_ci_smoke_external_irq(void) {
return true; return true;
@@ -621,6 +668,10 @@ static bool rs_ci_smoke_external_irq(void) {
static bool rs_ci_smoke_ga2d_irq(void) { static bool rs_ci_smoke_ga2d_irq(void) {
return true; return true;
} }
static bool rs_ci_smoke_npu_irq(void) {
return true;
}
#endif #endif
static bool rs_ci_smoke_apu(void) { static bool rs_ci_smoke_apu(void) {
@@ -997,6 +1048,10 @@ int main(void) {
rs_test_finish(RS_TEST_FAILED, 15U); rs_test_finish(RS_TEST_FAILED, 15U);
} }
printf("ci_smoke: GA2D P5 IRQ passed\n"); printf("ci_smoke: GA2D P5 IRQ passed\n");
if (!rs_ci_smoke_npu_irq()) {
rs_test_finish(RS_TEST_FAILED, 16U);
}
printf("ci_smoke: NPU P2 IRQ passed\n");
if (!rs_ci_smoke_apu()) { if (!rs_ci_smoke_apu()) {
rs_test_finish(RS_TEST_FAILED, 14U); rs_test_finish(RS_TEST_FAILED, 14U);
} }
+2 -1
View File
@@ -17,7 +17,8 @@ typedef enum {
RS_FABRIC_MASTER_RESERVED = RS_FABRIC_MASTER_JPEG, RS_FABRIC_MASTER_RESERVED = RS_FABRIC_MASTER_JPEG,
RS_FABRIC_MASTER_EXT_H = 7, RS_FABRIC_MASTER_EXT_H = 7,
RS_FABRIC_MASTER_GA2D = 8, RS_FABRIC_MASTER_GA2D = 8,
RS_FABRIC_MASTER_COUNT = 9, RS_FABRIC_MASTER_NPU = 9,
RS_FABRIC_MASTER_COUNT = 10,
} rs_fabric_master_t; } rs_fabric_master_t;
typedef enum { typedef enum {
+71
View File
@@ -0,0 +1,71 @@
#ifndef RETROSOC_HAL_NPU_H
#define RETROSOC_HAL_NPU_H
#include <stdint.h>
#include <retrosoc/core/status.h>
#include <retrosoc/hal/npu_regs.h>
typedef struct {
uint32_t descriptor_address;
uint32_t descriptor_count;
uint32_t job_id;
uint32_t timeout_cycles;
} rs_npu_job_t;
typedef struct {
uint32_t ip_id;
uint32_t ip_version;
uint32_t flags;
uint32_t numeric_profile;
uint32_t local_bytes;
uint32_t dense_macs;
uint32_t depthwise_macs;
uint32_t max_k_slice;
uint32_t max_dimension;
uint32_t op_mask;
} rs_npu_capability_t;
typedef struct {
uint32_t flags;
uint32_t job_id;
uint32_t result_code;
uint32_t completed_descriptors;
uint32_t recovery_generation;
} rs_npu_status_t;
typedef struct {
uint32_t code;
uint32_t address;
uint32_t descriptor_index;
uint32_t info;
} rs_npu_error_t;
typedef struct {
uint32_t job_id;
uint32_t recovery_generation;
uint64_t active_cycles;
uint64_t clock_pause_cycles;
uint64_t useful_macs;
uint64_t pack_cycles;
uint64_t local_bank_stall_cycles;
uint64_t dma_read_bytes;
uint64_t dma_write_bytes;
uint64_t dma_stall_cycles;
uint64_t requant_stall_cycles;
uint64_t retired_descriptors;
} rs_npu_counters_t;
rs_status_t rs_npu_get_capability(rs_npu_capability_t *capability);
rs_status_t rs_npu_submit(const rs_npu_job_t *job);
rs_status_t rs_npu_wait(uint32_t job_id, rs_timeout_t timeout, rs_npu_status_t *status);
rs_status_t rs_npu_abort_wait(uint32_t job_id, rs_timeout_t timeout, rs_npu_status_t *status);
rs_status_t rs_npu_reset(rs_timeout_t timeout);
rs_status_t rs_npu_get_status(rs_npu_status_t *status);
rs_status_t rs_npu_get_error(rs_npu_error_t *error);
rs_status_t rs_npu_snapshot_counters(rs_timeout_t timeout, rs_npu_counters_t *counters);
rs_status_t rs_npu_irq_enable(uint32_t events);
rs_status_t rs_npu_irq_pending(uint32_t *events);
rs_status_t rs_npu_irq_ack(uint32_t events);
#endif
+196
View File
@@ -0,0 +1,196 @@
#ifndef RETROSOC_HAL_NPU_REGS_H
#define RETROSOC_HAL_NPU_REGS_H
#include <stdint.h>
#include <retrosoc/core/soc.h>
#define RS_NPU_REG_IP_ID UINT32_C(0x000)
#define RS_NPU_REG_IP_VERSION UINT32_C(0x004)
#define RS_NPU_REG_CAPABILITY UINT32_C(0x008)
#define RS_NPU_REG_STATUS UINT32_C(0x00C)
#define RS_NPU_REG_CONTROL UINT32_C(0x010)
#define RS_NPU_REG_IRQ_STATE UINT32_C(0x014)
#define RS_NPU_REG_IRQ_ENABLE UINT32_C(0x018)
#define RS_NPU_REG_IRQ_TEST UINT32_C(0x01C)
#define RS_NPU_REG_JOB_BASE UINT32_C(0x020)
#define RS_NPU_REG_JOB_COUNT UINT32_C(0x024)
#define RS_NPU_REG_JOB_ID UINT32_C(0x028)
#define RS_NPU_REG_TIMEOUT_CYCLES UINT32_C(0x02C)
#define RS_NPU_REG_RESULT_JOB_ID UINT32_C(0x030)
#define RS_NPU_REG_RESULT_CODE UINT32_C(0x034)
#define RS_NPU_REG_COMPLETED_DESCRIPTORS UINT32_C(0x038)
#define RS_NPU_REG_FAULT_DESCRIPTOR UINT32_C(0x03C)
#define RS_NPU_REG_FAULT_CODE UINT32_C(0x040)
#define RS_NPU_REG_FAULT_ADDRESS UINT32_C(0x044)
#define RS_NPU_REG_FAULT_INFO UINT32_C(0x048)
#define RS_NPU_REG_NUMERIC_PROFILE UINT32_C(0x04C)
#define RS_NPU_REG_LOCAL_BYTES UINT32_C(0x050)
#define RS_NPU_REG_MAC_CONFIG UINT32_C(0x054)
#define RS_NPU_REG_MAX_K_SLICE UINT32_C(0x058)
#define RS_NPU_REG_MAX_DIMENSION UINT32_C(0x05C)
#define RS_NPU_REG_OP_CAPABILITY UINT32_C(0x060)
#define RS_NPU_REG_OWNER_STATUS UINT32_C(0x064)
#define RS_NPU_REG_RECOVERY_GENERATION UINT32_C(0x068)
#define RS_NPU_REG_PERF_CONTROL UINT32_C(0x06C)
#define RS_NPU_REG_PERF_STATUS UINT32_C(0x070)
#define RS_NPU_REG_PERF_JOB_ID UINT32_C(0x074)
#define RS_NPU_REG_PERF_GENERATION UINT32_C(0x078)
#define RS_NPU_REG_DESCRIPTOR_BYTES UINT32_C(0x07C)
#define RS_NPU_REG_PERF_ACTIVE_CYCLES_LO UINT32_C(0x080)
#define RS_NPU_REG_PERF_ACTIVE_CYCLES_HI UINT32_C(0x084)
#define RS_NPU_REG_PERF_CLOCK_PAUSE_CYCLES_LO UINT32_C(0x088)
#define RS_NPU_REG_PERF_CLOCK_PAUSE_CYCLES_HI UINT32_C(0x08C)
#define RS_NPU_REG_PERF_USEFUL_MACS_LO UINT32_C(0x090)
#define RS_NPU_REG_PERF_USEFUL_MACS_HI UINT32_C(0x094)
#define RS_NPU_REG_PERF_PACK_CYCLES_LO UINT32_C(0x098)
#define RS_NPU_REG_PERF_PACK_CYCLES_HI UINT32_C(0x09C)
#define RS_NPU_REG_PERF_LOCAL_BANK_STALL_LO UINT32_C(0x0A0)
#define RS_NPU_REG_PERF_LOCAL_BANK_STALL_HI UINT32_C(0x0A4)
#define RS_NPU_REG_PERF_DMA_READ_BYTES_LO UINT32_C(0x0A8)
#define RS_NPU_REG_PERF_DMA_READ_BYTES_HI UINT32_C(0x0AC)
#define RS_NPU_REG_PERF_DMA_WRITE_BYTES_LO UINT32_C(0x0B0)
#define RS_NPU_REG_PERF_DMA_WRITE_BYTES_HI UINT32_C(0x0B4)
#define RS_NPU_REG_PERF_DMA_STALL_CYCLES_LO UINT32_C(0x0B8)
#define RS_NPU_REG_PERF_DMA_STALL_CYCLES_HI UINT32_C(0x0BC)
#define RS_NPU_REG_PERF_REQUANT_STALL_LO UINT32_C(0x0C0)
#define RS_NPU_REG_PERF_REQUANT_STALL_HI UINT32_C(0x0C4)
#define RS_NPU_REG_PERF_RETIRED_DESCRIPTORS_LO UINT32_C(0x0C8)
#define RS_NPU_REG_PERF_RETIRED_DESCRIPTORS_HI UINT32_C(0x0CC)
#define RS_NPU_IP_ID_VALUE UINT32_C(0x4E505531)
#define RS_NPU_IP_VERSION_VALUE UINT32_C(0x00010000)
#define RS_NPU_IP_VERSION_MAJOR_MASK UINT32_C(0xFFFF0000)
#define RS_NPU_IP_VERSION_MAJOR_1 UINT32_C(0x00010000)
#define RS_NPU_CAPABILITY_P2 UINT32_C(0x00000009)
#define RS_NPU_NUMERIC_PROFILE_VALUE UINT32_C(0x00000001)
#define RS_NPU_TIMEOUT_CYCLES_RESET UINT32_C(0x044AA200)
#define RS_NPU_LOCAL_BYTES_VALUE UINT32_C(0x00010000)
#define RS_NPU_MAC_CONFIG_VALUE UINT32_C(0x00080040)
#define RS_NPU_MAX_K_SLICE UINT32_C(1024)
#define RS_NPU_MAX_DIMENSION UINT32_C(4096)
#define RS_NPU_MAX_TILE UINT32_C(8)
#define RS_NPU_OP_CAPABILITY_MVP UINT32_C(0x000001FE)
#define RS_NPU_DESCRIPTOR_ABI_VERSION UINT32_C(0x0100)
#define RS_NPU_DESCRIPTOR_WORDS UINT32_C(32)
#define RS_NPU_DESCRIPTOR_BYTES UINT32_C(128)
#define RS_NPU_DESCRIPTOR_BASE_ALIGNMENT UINT32_C(64)
#define RS_NPU_PARAM_RECORD_BYTES UINT32_C(16)
#define RS_NPU_ADD_PARAM_BYTES UINT32_C(32)
#define RS_NPU_FAULT_DESCRIPTOR_RESET UINT32_C(0xFFFFFFFF)
#define RS_NPU_IRQ_ALL UINT32_C(0x00000007)
#define RS_NPU_CONTROL_START UINT32_C(0x00000001)
#define RS_NPU_CONTROL_ABORT UINT32_C(0x00000002)
#define RS_NPU_CONTROL_SOFT_RESET UINT32_C(0x00000004)
#define RS_NPU_STATUS_READY UINT32_C(0x00000001)
#define RS_NPU_STATUS_BUSY UINT32_C(0x00000002)
#define RS_NPU_STATUS_DRAINING UINT32_C(0x00000004)
#define RS_NPU_STATUS_CLOCK_PAUSED UINT32_C(0x00000008)
#define RS_NPU_STATUS_RECOVERING UINT32_C(0x00000010)
#define RS_NPU_STATUS_RESULT_VALID UINT32_C(0x00000020)
#define RS_NPU_IRQ_DONE UINT32_C(0x00000001)
#define RS_NPU_IRQ_ERROR UINT32_C(0x00000002)
#define RS_NPU_IRQ_ABORTED UINT32_C(0x00000004)
#define RS_NPU_CAPABILITY_PRESENT UINT32_C(0x00000001)
#define RS_NPU_CAPABILITY_EXECUTION_READY UINT32_C(0x00000002)
#define RS_NPU_CAPABILITY_PRIVATE_AXI64_DMA UINT32_C(0x00000004)
#define RS_NPU_CAPABILITY_INTERRUPTS UINT32_C(0x00000008)
#define RS_NPU_CAPABILITY_DOUBLE_ROUNDING UINT32_C(0x00000010)
#define RS_NPU_CAPABILITY_NATIVE_HP_CLOCK UINT32_C(0x00000020)
#define RS_NPU_CAPABILITY_SOFTWARE_ABORT UINT32_C(0x00000040)
#define RS_NPU_PERF_CONTROL_SNAPSHOT UINT32_C(0x00000001)
#define RS_NPU_PERF_STATUS_SNAP_BUSY UINT32_C(0x00000001)
#define RS_NPU_PERF_STATUS_SNAP_VALID UINT32_C(0x00000002)
#define RS_NPU_OWNER_STATUS_LOCK UINT32_C(0x00000100)
#define RS_NPU_OWNER_STATUS_QUIESCE UINT32_C(0x00000200)
#define RS_NPU_OWNER_STATUS_RESET_REQUEST UINT32_C(0x00000400)
#define RS_NPU_MAC_CONFIG_DENSE_SHIFT 0U
#define RS_NPU_MAC_CONFIG_DENSE_MASK UINT32_C(0x0000FFFF)
#define RS_NPU_MAC_CONFIG_DEPTHWISE_SHIFT 16U
#define RS_NPU_MAC_CONFIG_DEPTHWISE_MASK UINT32_C(0xFFFF0000)
#define RS_NPU_JOB_COUNT_VALUE_SHIFT 0U
#define RS_NPU_JOB_COUNT_VALUE_MASK UINT32_C(0x0000FFFF)
#define RS_NPU_OWNER_STATUS_OWNER_SHIFT 0U
#define RS_NPU_OWNER_STATUS_OWNER_MASK UINT32_C(0x00000003)
#define RS_NPU_FAULT_INFO_AXI_RESPONSE_SHIFT 0U
#define RS_NPU_FAULT_INFO_AXI_RESPONSE_MASK UINT32_C(0x00000003)
#define RS_NPU_FAULT_INFO_DIRECTION_SHIFT 2U
#define RS_NPU_FAULT_INFO_DIRECTION_MASK UINT32_C(0x0000000C)
#define RS_NPU_FAULT_INFO_LANE_SHIFT 8U
#define RS_NPU_FAULT_INFO_LANE_MASK UINT32_C(0x0000FF00)
#define RS_NPU_VERSION_OPCODE_VERSION_SHIFT 16U
#define RS_NPU_VERSION_OPCODE_VERSION_MASK UINT32_C(0xFFFF0000)
#define RS_NPU_VERSION_OPCODE_OPCODE_SHIFT 0U
#define RS_NPU_VERSION_OPCODE_OPCODE_MASK UINT32_C(0x000000FF)
#define RS_NPU_FAULT_INFO_DIRECTION_INTERNAL 0U
#define RS_NPU_FAULT_INFO_DIRECTION_READ 1U
#define RS_NPU_FAULT_INFO_DIRECTION_WRITE 2U
#define RS_NPU_FAULT_INFO_LANE_NONE UINT32_C(0xFF)
#define RS_NPU_RESULT_NONE 0U
#define RS_NPU_RESULT_DONE 1U
#define RS_NPU_RESULT_ERROR 2U
#define RS_NPU_RESULT_ABORTED 3U
#define RS_NPU_RESULT_RESET_CANCELLED 4U
#define RS_NPU_FAULT_NONE 0U
#define RS_NPU_FAULT_DESCRIPTOR 1U
#define RS_NPU_FAULT_UNSUPPORTED 2U
#define RS_NPU_FAULT_RANGE 3U
#define RS_NPU_FAULT_AXI_READ 4U
#define RS_NPU_FAULT_AXI_WRITE 5U
#define RS_NPU_FAULT_AXI_PROTOCOL 6U
#define RS_NPU_FAULT_ARITHMETIC 7U
#define RS_NPU_FAULT_NO_PROGRESS 8U
#define RS_NPU_FAULT_LOCAL_STATE 9U
#define RS_NPU_FAULT_RESET_CANCELLED 10U
#define RS_NPU_OPCODE_CONV2D 1U
#define RS_NPU_OPCODE_DEPTHWISE3X3 2U
#define RS_NPU_OPCODE_FULLY_CONNECTED 3U
#define RS_NPU_OPCODE_ADD 4U
#define RS_NPU_OPCODE_MAX_POOL 5U
#define RS_NPU_OPCODE_AVERAGE_POOL 6U
#define RS_NPU_OPCODE_GLOBAL_AVERAGE_POOL 7U
#define RS_NPU_OPCODE_CLAMP 8U
#define RS_NPU_DESCRIPTOR_WORD_VERSION_OPCODE 0U
#define RS_NPU_DESCRIPTOR_WORD_RESERVED 1U
#define RS_NPU_DESCRIPTOR_WORD_INPUT0_BASE 2U
#define RS_NPU_DESCRIPTOR_WORD_INPUT1_BASE 3U
#define RS_NPU_DESCRIPTOR_WORD_OUTPUT_BASE 4U
#define RS_NPU_DESCRIPTOR_WORD_PARAM_BASE 5U
#define RS_NPU_DESCRIPTOR_WORD_INPUT_HW 6U
#define RS_NPU_DESCRIPTOR_WORD_CHANNELS 7U
#define RS_NPU_DESCRIPTOR_WORD_OUTPUT_HW 8U
#define RS_NPU_DESCRIPTOR_WORD_INPUT0_ROW_BYTES 9U
#define RS_NPU_DESCRIPTOR_WORD_OUTPUT_ROW_BYTES 10U
#define RS_NPU_DESCRIPTOR_WORD_INPUT1_ROW_BYTES 11U
#define RS_NPU_DESCRIPTOR_WORD_KERNEL_STRIDE 12U
#define RS_NPU_DESCRIPTOR_WORD_PADDING 13U
#define RS_NPU_DESCRIPTOR_WORD_TILE_HW 14U
#define RS_NPU_DESCRIPTOR_WORD_K_SLICE 15U
#define RS_NPU_DESCRIPTOR_WORD_INPUT0_BYTES 16U
#define RS_NPU_DESCRIPTOR_WORD_INPUT1_BYTES 17U
#define RS_NPU_DESCRIPTOR_WORD_OUTPUT_BYTES 18U
#define RS_NPU_DESCRIPTOR_WORD_PARAM_BYTES 19U
#define RS_NPU_DESCRIPTOR_WORD_INPUT0_ZERO 20U
#define RS_NPU_DESCRIPTOR_WORD_INPUT1_ZERO 21U
#define RS_NPU_DESCRIPTOR_WORD_OUTPUT_ZERO 22U
#define RS_NPU_DESCRIPTOR_WORD_ACTIVATION_BOUNDS 23U
#define RS_NPU_DESCRIPTOR_WORD_WEIGHT_BASE 24U
#define RS_NPU_DESCRIPTOR_WORD_WEIGHT_BYTES 25U
#if defined(RS_SOC_APB4_NPU_BASE)
#define RS_NPU_REG(offset) RS_SOC_REG32(RS_SOC_APB4_NPU_BASE, (offset))
#endif
#endif
+2 -1
View File
@@ -16,7 +16,8 @@ typedef enum {
RS_RESOURCE_JPEG = 6, RS_RESOURCE_JPEG = 6,
RS_RESOURCE_APU = 7, RS_RESOURCE_APU = 7,
RS_RESOURCE_GA2D = 8, RS_RESOURCE_GA2D = 8,
RS_RESOURCE_COUNT = 9, RS_RESOURCE_NPU = 9,
RS_RESOURCE_COUNT = 10,
} rs_resource_t; } rs_resource_t;
typedef enum { typedef enum {
+367
View File
@@ -0,0 +1,367 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/hal/npu.h>
#if defined(RS_NPU_TEST_MMIO)
extern volatile uint32_t rs_npu_test_mmio[1024];
#undef RS_NPU_REG
#define RS_NPU_REG(offset) rs_npu_test_mmio[(offset) / 4U]
#endif
#if defined(RS_NPU_REG)
static void rs_npu_fence(void) {
#if defined(__riscv)
__asm__ volatile("fence rw, rw" ::: "memory");
#else
__asm__ volatile("" ::: "memory");
#endif
}
static uint32_t rs_npu_poll_count(rs_timeout_t timeout) {
return (timeout == 0U) ? 1U : timeout;
}
static rs_status_t rs_npu_require_shell(void) {
rs_npu_capability_t capability;
return rs_npu_get_capability(&capability);
}
static rs_status_t rs_npu_require_irq(void) {
rs_npu_capability_t capability;
rs_status_t status;
status = rs_npu_get_capability(&capability);
if (status != RS_OK) {
return status;
}
return ((capability.flags & RS_NPU_CAPABILITY_INTERRUPTS) != 0U) ? RS_OK : RS_ENOTSUP;
}
static bool rs_npu_token_known(uint32_t job_id, uint32_t flags) {
if ((flags & (RS_NPU_STATUS_BUSY | RS_NPU_STATUS_DRAINING)) != 0U) {
return job_id == RS_NPU_REG(RS_NPU_REG_JOB_ID);
}
return ((flags & RS_NPU_STATUS_RESULT_VALID) != 0U) &&
(job_id == RS_NPU_REG(RS_NPU_REG_RESULT_JOB_ID));
}
static void rs_npu_read_status(rs_npu_status_t *status) {
status->flags = RS_NPU_REG(RS_NPU_REG_STATUS);
status->job_id = RS_NPU_REG(RS_NPU_REG_RESULT_JOB_ID);
status->result_code = RS_NPU_REG(RS_NPU_REG_RESULT_CODE);
status->completed_descriptors = RS_NPU_REG(RS_NPU_REG_COMPLETED_DESCRIPTORS);
status->recovery_generation = RS_NPU_REG(RS_NPU_REG_RECOVERY_GENERATION);
}
static rs_status_t rs_npu_result_status(uint32_t result_code) {
return (result_code == RS_NPU_RESULT_DONE) ? RS_OK : RS_EIO;
}
static uint64_t rs_npu_read_counter(uint32_t low_offset) {
return ((uint64_t)RS_NPU_REG(low_offset + 4U) << 32U) | (uint64_t)RS_NPU_REG(low_offset);
}
static rs_status_t rs_npu_wait_terminal(uint32_t job_id, rs_timeout_t timeout,
rs_npu_status_t *status) {
uint32_t polls = rs_npu_poll_count(timeout);
while (polls-- != 0U) {
if ((RS_NPU_REG(RS_NPU_REG_STATUS) & RS_NPU_STATUS_RESULT_VALID) != 0U) {
if (RS_NPU_REG(RS_NPU_REG_RESULT_JOB_ID) != job_id) {
return RS_EIO;
}
rs_npu_read_status(status);
rs_npu_fence();
return rs_npu_result_status(status->result_code);
}
}
rs_npu_read_status(status);
return RS_ETIMEOUT;
}
rs_status_t rs_npu_get_capability(rs_npu_capability_t *capability) {
uint32_t ip_id;
uint32_t mac_config;
if (capability == NULL) {
return RS_EINVAL;
}
ip_id = RS_NPU_REG(RS_NPU_REG_IP_ID);
mac_config = RS_NPU_REG(RS_NPU_REG_MAC_CONFIG);
capability->ip_id = ip_id;
capability->ip_version = RS_NPU_REG(RS_NPU_REG_IP_VERSION);
capability->flags = RS_NPU_REG(RS_NPU_REG_CAPABILITY);
capability->numeric_profile = RS_NPU_REG(RS_NPU_REG_NUMERIC_PROFILE);
capability->local_bytes = RS_NPU_REG(RS_NPU_REG_LOCAL_BYTES);
capability->dense_macs =
(mac_config & RS_NPU_MAC_CONFIG_DENSE_MASK) >> RS_NPU_MAC_CONFIG_DENSE_SHIFT;
capability->depthwise_macs =
(mac_config & RS_NPU_MAC_CONFIG_DEPTHWISE_MASK) >> RS_NPU_MAC_CONFIG_DEPTHWISE_SHIFT;
capability->max_k_slice = RS_NPU_REG(RS_NPU_REG_MAX_K_SLICE);
capability->max_dimension = RS_NPU_REG(RS_NPU_REG_MAX_DIMENSION);
capability->op_mask = RS_NPU_REG(RS_NPU_REG_OP_CAPABILITY);
if ((ip_id != RS_NPU_IP_ID_VALUE) ||
((capability->ip_version & RS_NPU_IP_VERSION_MAJOR_MASK) != RS_NPU_IP_VERSION_MAJOR_1)) {
return RS_ENOTSUP;
}
return RS_OK;
}
rs_status_t rs_npu_submit(const rs_npu_job_t *job) {
rs_npu_capability_t capability;
rs_status_t status;
uint64_t descriptor_end;
if (job == NULL) {
return RS_EINVAL;
}
descriptor_end = (uint64_t)job->descriptor_address +
((uint64_t)job->descriptor_count * (uint64_t)RS_NPU_DESCRIPTOR_BYTES);
if (((job->descriptor_address % RS_NPU_DESCRIPTOR_BASE_ALIGNMENT) != 0U) ||
(job->descriptor_count == 0U) || (job->descriptor_count > RS_NPU_JOB_COUNT_VALUE_MASK) ||
(job->timeout_cycles == 0U) || (descriptor_end > UINT64_C(0x100000000))) {
return RS_EINVAL;
}
status = rs_npu_get_capability(&capability);
if (status != RS_OK) {
return status;
}
/* P2 integrates the register shell without a job execution path: START is never written. */
return RS_ENOTSUP;
}
rs_status_t rs_npu_wait(uint32_t job_id, rs_timeout_t timeout, rs_npu_status_t *status) {
rs_status_t result;
if (status == NULL) {
return RS_EINVAL;
}
result = rs_npu_require_shell();
if (result != RS_OK) {
return result;
}
if (!rs_npu_token_known(job_id, RS_NPU_REG(RS_NPU_REG_STATUS))) {
return RS_EINVAL;
}
return rs_npu_wait_terminal(job_id, timeout, status);
}
rs_status_t rs_npu_abort_wait(uint32_t job_id, rs_timeout_t timeout, rs_npu_status_t *status) {
uint32_t flags;
rs_status_t result;
if (status == NULL) {
return RS_EINVAL;
}
result = rs_npu_require_shell();
if (result != RS_OK) {
return result;
}
flags = RS_NPU_REG(RS_NPU_REG_STATUS);
if (!rs_npu_token_known(job_id, flags)) {
return RS_EINVAL;
}
if ((flags & (RS_NPU_STATUS_BUSY | RS_NPU_STATUS_DRAINING)) != 0U) {
rs_npu_fence();
RS_NPU_REG(RS_NPU_REG_CONTROL) = RS_NPU_CONTROL_ABORT;
}
return rs_npu_wait_terminal(job_id, timeout, status);
}
rs_status_t rs_npu_reset(rs_timeout_t timeout) {
uint32_t polls;
rs_status_t result;
result = rs_npu_require_shell();
if (result != RS_OK) {
return result;
}
if (((RS_NPU_REG(RS_NPU_REG_STATUS) &
(RS_NPU_STATUS_BUSY | RS_NPU_STATUS_DRAINING | RS_NPU_STATUS_RECOVERING)) != 0U) ||
((RS_NPU_REG(RS_NPU_REG_PERF_STATUS) & RS_NPU_PERF_STATUS_SNAP_BUSY) != 0U)) {
return RS_EINVAL;
}
rs_npu_fence();
RS_NPU_REG(RS_NPU_REG_CONTROL) = RS_NPU_CONTROL_SOFT_RESET;
polls = rs_npu_poll_count(timeout);
while (polls-- != 0U) {
if ((RS_NPU_REG(RS_NPU_REG_STATUS) &
(RS_NPU_STATUS_BUSY | RS_NPU_STATUS_DRAINING | RS_NPU_STATUS_RECOVERING)) == 0U) {
return RS_OK;
}
}
return RS_ETIMEOUT;
}
rs_status_t rs_npu_get_status(rs_npu_status_t *status) {
rs_status_t result;
if (status == NULL) {
return RS_EINVAL;
}
result = rs_npu_require_shell();
if (result != RS_OK) {
return result;
}
rs_npu_read_status(status);
return RS_OK;
}
rs_status_t rs_npu_get_error(rs_npu_error_t *error) {
rs_status_t result;
if (error == NULL) {
return RS_EINVAL;
}
result = rs_npu_require_shell();
if (result != RS_OK) {
return result;
}
error->code = RS_NPU_REG(RS_NPU_REG_FAULT_CODE);
error->address = RS_NPU_REG(RS_NPU_REG_FAULT_ADDRESS);
error->descriptor_index = RS_NPU_REG(RS_NPU_REG_FAULT_DESCRIPTOR);
error->info = RS_NPU_REG(RS_NPU_REG_FAULT_INFO);
return RS_OK;
}
rs_status_t rs_npu_snapshot_counters(rs_timeout_t timeout, rs_npu_counters_t *counters) {
uint32_t polls;
rs_status_t result;
if (counters == NULL) {
return RS_EINVAL;
}
result = rs_npu_require_shell();
if (result != RS_OK) {
return result;
}
if ((RS_NPU_REG(RS_NPU_REG_PERF_STATUS) & RS_NPU_PERF_STATUS_SNAP_BUSY) != 0U) {
return RS_EINVAL;
}
RS_NPU_REG(RS_NPU_REG_PERF_CONTROL) = RS_NPU_PERF_CONTROL_SNAPSHOT;
polls = rs_npu_poll_count(timeout);
while (polls-- != 0U) {
const uint32_t perf = RS_NPU_REG(RS_NPU_REG_PERF_STATUS);
if (((perf & RS_NPU_PERF_STATUS_SNAP_BUSY) == 0U) &&
((perf & RS_NPU_PERF_STATUS_SNAP_VALID) != 0U)) {
counters->job_id = RS_NPU_REG(RS_NPU_REG_PERF_JOB_ID);
counters->recovery_generation = RS_NPU_REG(RS_NPU_REG_PERF_GENERATION);
counters->active_cycles = rs_npu_read_counter(RS_NPU_REG_PERF_ACTIVE_CYCLES_LO);
counters->clock_pause_cycles =
rs_npu_read_counter(RS_NPU_REG_PERF_CLOCK_PAUSE_CYCLES_LO);
counters->useful_macs = rs_npu_read_counter(RS_NPU_REG_PERF_USEFUL_MACS_LO);
counters->pack_cycles = rs_npu_read_counter(RS_NPU_REG_PERF_PACK_CYCLES_LO);
counters->local_bank_stall_cycles =
rs_npu_read_counter(RS_NPU_REG_PERF_LOCAL_BANK_STALL_LO);
counters->dma_read_bytes = rs_npu_read_counter(RS_NPU_REG_PERF_DMA_READ_BYTES_LO);
counters->dma_write_bytes = rs_npu_read_counter(RS_NPU_REG_PERF_DMA_WRITE_BYTES_LO);
counters->dma_stall_cycles = rs_npu_read_counter(RS_NPU_REG_PERF_DMA_STALL_CYCLES_LO);
counters->requant_stall_cycles = rs_npu_read_counter(RS_NPU_REG_PERF_REQUANT_STALL_LO);
counters->retired_descriptors =
rs_npu_read_counter(RS_NPU_REG_PERF_RETIRED_DESCRIPTORS_LO);
return RS_OK;
}
}
return RS_ETIMEOUT;
}
rs_status_t rs_npu_irq_enable(uint32_t events) {
rs_status_t result;
if ((events & ~RS_NPU_IRQ_ALL) != 0U) {
return RS_EINVAL;
}
result = rs_npu_require_irq();
if (result != RS_OK) {
return result;
}
RS_NPU_REG(RS_NPU_REG_IRQ_ENABLE) = events;
return RS_OK;
}
rs_status_t rs_npu_irq_pending(uint32_t *events) {
rs_status_t result;
if (events == NULL) {
return RS_EINVAL;
}
result = rs_npu_require_irq();
if (result != RS_OK) {
return result;
}
*events = RS_NPU_REG(RS_NPU_REG_IRQ_STATE) & RS_NPU_IRQ_ALL;
return RS_OK;
}
rs_status_t rs_npu_irq_ack(uint32_t events) {
rs_status_t result;
if ((events & ~RS_NPU_IRQ_ALL) != 0U) {
return RS_EINVAL;
}
result = rs_npu_require_irq();
if (result != RS_OK) {
return result;
}
RS_NPU_REG(RS_NPU_REG_IRQ_STATE) = events;
return RS_OK;
}
#else
rs_status_t rs_npu_get_capability(rs_npu_capability_t *capability) {
return (capability == NULL) ? RS_EINVAL : RS_ENOTSUP;
}
rs_status_t rs_npu_submit(const rs_npu_job_t *job) {
return (job == NULL) ? RS_EINVAL : RS_ENOTSUP;
}
rs_status_t rs_npu_wait(uint32_t job_id, rs_timeout_t timeout, rs_npu_status_t *status) {
(void)job_id;
(void)timeout;
return (status == NULL) ? RS_EINVAL : RS_ENOTSUP;
}
rs_status_t rs_npu_abort_wait(uint32_t job_id, rs_timeout_t timeout, rs_npu_status_t *status) {
(void)job_id;
(void)timeout;
return (status == NULL) ? RS_EINVAL : RS_ENOTSUP;
}
rs_status_t rs_npu_reset(rs_timeout_t timeout) {
(void)timeout;
return RS_ENOTSUP;
}
rs_status_t rs_npu_get_status(rs_npu_status_t *status) {
return (status == NULL) ? RS_EINVAL : RS_ENOTSUP;
}
rs_status_t rs_npu_get_error(rs_npu_error_t *error) {
return (error == NULL) ? RS_EINVAL : RS_ENOTSUP;
}
rs_status_t rs_npu_snapshot_counters(rs_timeout_t timeout, rs_npu_counters_t *counters) {
(void)timeout;
return (counters == NULL) ? RS_EINVAL : RS_ENOTSUP;
}
rs_status_t rs_npu_irq_enable(uint32_t events) {
return ((events & ~RS_NPU_IRQ_ALL) != 0U) ? RS_EINVAL : RS_ENOTSUP;
}
rs_status_t rs_npu_irq_pending(uint32_t *events) {
return (events == NULL) ? RS_EINVAL : RS_ENOTSUP;
}
rs_status_t rs_npu_irq_ack(uint32_t events) {
return ((events & ~RS_NPU_IRQ_ALL) != 0U) ? RS_EINVAL : RS_ENOTSUP;
}
#endif
+2
View File
@@ -133,6 +133,8 @@ static void rs_print_mmio_map(void) {
(void *)(uintptr_t)RS_SOC_APB4_JPEG_BASE); (void *)(uintptr_t)RS_SOC_APB4_JPEG_BASE);
printf(" 1 x APU @%p\n", printf(" 1 x APU @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_APU_BASE); (void *)(uintptr_t)RS_SOC_APB4_APU_BASE);
printf(" 1 x NPU SHELL @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_NPU_BASE);
printf(" 1 x CLINT @%p\n", printf(" 1 x CLINT @%p\n",
(void *)(uintptr_t)RS_SOC_APB4_CLINT_BASE); (void *)(uintptr_t)RS_SOC_APB4_CLINT_BASE);
printf(" 1 x ARCHINFO @%p\n", printf(" 1 x ARCHINFO @%p\n",
+1 -1
View File
@@ -15,7 +15,7 @@ ecosystem of the referenced devices. Official sources were rechecked on
| [NXP i.MX RT1180](https://www.nxp.com/products/i.MX-RT1180) and [reference manual](https://www.nxp.com/docs/en/reference-manual/IMXRT1180RM.pdf) | Heterogeneous M7/M33 resource isolation and recovery; TRDC assigns initiators to fixed domains and protects memory/peripheral regions, while SRC owns slice reset | Current product; RM Rev 10.0 and data-sheet updates were published in 2026. Depends on TrustZone/security lifecycle and NXP clock/reset IP | Reuse immutable master identity, root-owned policy, fail-closed regions, and slice lifecycle. Avoid pretending a software owner field alone is a security boundary or importing the full secure lifecycle | | [NXP i.MX RT1180](https://www.nxp.com/products/i.MX-RT1180) and [reference manual](https://www.nxp.com/docs/en/reference-manual/IMXRT1180RM.pdf) | Heterogeneous M7/M33 resource isolation and recovery; TRDC assigns initiators to fixed domains and protects memory/peripheral regions, while SRC owns slice reset | Current product; RM Rev 10.0 and data-sheet updates were published in 2026. Depends on TrustZone/security lifecycle and NXP clock/reset IP | Reuse immutable master identity, root-owned policy, fail-closed regions, and slice lifecycle. Avoid pretending a software owner field alone is a security boundary or importing the full secure lifecycle |
| [ST STM32MP resource manager](https://wiki.st.com/stm32mpu/wiki/Resource_manager_for_coprocessing), [coprocessor management](https://wiki.st.com/stm32mpu/wiki/Coprocessor_management_overview), and [RIFSC](https://wiki.st.com/stm32mpu/wiki/RIFSC_internal_peripheral) | Prevents Linux and MCU firmware from driving the same peripheral; boot-time ETZPC/RIF hardware assignment is mirrored in device tree, RemoteProc/RPMsg/mailbox, and resource-manager software | Active STM32MP1/MP2 documentation, updated through 2025-2026. Depends on TF-A, OP-TEE, Linux device tree, mailbox, and remoteproc services | Reuse quiesce-before-owner-change, hardware exclusion, and one-owner IRQ routing. Defer the Linux resource driver; avoid split hardware/software ownership tables without parity checks | | [ST STM32MP resource manager](https://wiki.st.com/stm32mpu/wiki/Resource_manager_for_coprocessing), [coprocessor management](https://wiki.st.com/stm32mpu/wiki/Coprocessor_management_overview), and [RIFSC](https://wiki.st.com/stm32mpu/wiki/RIFSC_internal_peripheral) | Prevents Linux and MCU firmware from driving the same peripheral; boot-time ETZPC/RIF hardware assignment is mirrored in device tree, RemoteProc/RPMsg/mailbox, and resource-manager software | Active STM32MP1/MP2 documentation, updated through 2025-2026. Depends on TF-A, OP-TEE, Linux device tree, mailbox, and remoteproc services | Reuse quiesce-before-owner-change, hardware exclusion, and one-owner IRQ routing. Defer the Linux resource driver; avoid split hardware/software ownership tables without parity checks |
| [TI AM62x host identities](https://software-dl.ti.com/tisci/esd/latest/5_soc_doc/am62x/hosts.html), [firewalls](https://software-dl.ti.com/tisci/esd/11_01_05/5_soc_doc/am62x/firewalls.html), and [RM board configuration](https://software-dl.ti.com/mcu-plus-sdk/esd/AM62X/09_00_00_19/exports/docs/api_guide_am62x/RESOURCE_ALLOCATION_GUIDE.html) | Prevents initiator spoofing and unauthorized memory/resource use; immutable host IDs select boot-configured firewall permissions and resource ranges through system firmware | Current TISCI/Processor SDK pages were published or crawled in 2026. Depends on TIFS/DM firmware, secure proxy, boardcfg, and interconnect privilege IDs | Reuse generated fixed identities, boot policy, explicit read/write/cache permissions, and first-fault evidence. Avoid a privileged message stack and runtime-reprogrammable policy in this MVP | | [TI AM62x host identities](https://software-dl.ti.com/tisci/esd/latest/5_soc_doc/am62x/hosts.html), [firewalls](https://software-dl.ti.com/tisci/esd/11_01_05/5_soc_doc/am62x/firewalls.html), and [RM board configuration](https://software-dl.ti.com/mcu-plus-sdk/esd/AM62X/09_00_00_19/exports/docs/api_guide_am62x/RESOURCE_ALLOCATION_GUIDE.html) | Prevents initiator spoofing and unauthorized memory/resource use; immutable host IDs select boot-configured firewall permissions and resource ranges through system firmware | Current TISCI/Processor SDK pages were published or crawled in 2026. Depends on TIFS/DM firmware, secure proxy, boardcfg, and interconnect privilege IDs | Reuse generated fixed identities, boot policy, explicit read/write/cache permissions, and first-fault evidence. Avoid a privileged message stack and runtime-reprogrammable policy in this MVP |
| [Arm NIC-400 optimization](https://developer.arm.com/-/media/Arm%20Developer%20Community/PDF/Cycle%20Models/White%20Papers/ARM%20CoreLink%20NIC-400%20Optimization.pdf) and [Arm system QoS](https://developer.arm.com/-/media/Arm%20Developer%20Community/PDF/Quality%20of%20Service%20QoS%20in%20Arm%20Systems.pdf) | Scales mixed AXI/AHB/APB widths and clocks while controlling latency and bandwidth; target-local buffering, register slices, outstanding limits, hierarchical gateways, and QoS regulators are topology parameters | NIC-400 is mature rather than new; Arm still published an updated QoS guide in 2026. Commercial use depends on licensed IP/configuration and performance modelling | Reuse target-aware credits, independent channel/CDC boundaries, hierarchical I/O gateways, measurable wait/high-water counters, and bounded promotion. Avoid a packetized NoC, virtual networks, and opaque generated RTL for a 9x6 fabric | | [Arm NIC-400 optimization](https://developer.arm.com/-/media/Arm%20Developer%20Community/PDF/Cycle%20Models/White%20Papers/ARM%20CoreLink%20NIC-400%20Optimization.pdf) and [Arm system QoS](https://developer.arm.com/-/media/Arm%20Developer%20Community/PDF/Quality%20of%20Service%20QoS%20in%20Arm%20Systems.pdf) | Scales mixed AXI/AHB/APB widths and clocks while controlling latency and bandwidth; target-local buffering, register slices, outstanding limits, hierarchical gateways, and QoS regulators are topology parameters | NIC-400 is mature rather than new; Arm still published an updated QoS guide in 2026. Commercial use depends on licensed IP/configuration and performance modelling | Reuse target-aware credits, independent channel/CDC boundaries, hierarchical I/O gateways, measurable wait/high-water counters, and bounded promotion. Avoid a packetized NoC, virtual networks, and opaque generated RTL for a 10x6 fabric |
## Technical Selection ## Technical Selection
+2 -1
View File
@@ -9,7 +9,8 @@ APB4 peripherals. Their role in boot and lifecycle control is defined by
Source 0 is permanently reserved. Source 1 is UART1, source 2 is the HP-side Source 0 is permanently reserved. Source 1 is UART1, source 2 is the HP-side
mailbox doorbell, source 3 is EXT-H, and sources 4 through 10 are central DMA, mailbox doorbell, source 3 is EXT-H, and sources 4 through 10 are central DMA,
USB2, SDIO0, SDIO1, SPI-SD, JPEG, and APU respectively. Sources 11 through 31 USB2, SDIO0, SDIO1, SPI-SD, JPEG, and APU respectively. Source 11 is GA2D and
source 12 is the NPU shell. Sources 13 through 31
are reserved and tied low. Resource-owned sources are suppressed unless HP is are reserved and tied low. Resource-owned sources are suppressed unless HP is
the exclusive owner. Each source has a three-bit priority. Context 0 drives HP the exclusive owner. Each source has a three-bit priority. Context 0 drives HP
machine external interrupt and context 1 drives HP supervisor external interrupt. machine external interrupt and context 1 drives HP supervisor external interrupt.
+2 -2
View File
@@ -266,8 +266,8 @@ provide a reference boundary, not shared hardware or an APUM-compatible ABI.
### Mini allocations and capability integration ### Mini allocations and capability integration
These are frozen future allocations. The current executable topology remains These are frozen allocations. Phase 2 wires them exactly as specified; the
unchanged until Phase 2; the specification does not claim they are wired now. integration advertises no execution readiness, DMA, or operator capability.
| Allocation | Value | | Allocation | Value |
| --- | --- | | --- | --- |
+13 -4
View File
@@ -8,14 +8,20 @@ allocations. The engine supports FILL, COPY, bit-exact CONVERT, opaque alpha
BLEND, A8 fixed-color foreground masks, and exact equal BLEND, A8 fixed-color foreground masks, and exact equal
background/destination in-place composition. RGB565, RGB888, XRGB8888, and background/destination in-place composition. RGB565, RGB888, XRGB8888, and
ARGB8888 are color surfaces; A8 is BLEND foreground-only. Its ARGB8888 are color surfaces; A8 is BLEND foreground-only. Its
source-stop/drain-before-block sequence is specified there. Existing resources source-stop/drain-before-block sequence is specified there.
[NPU Phase 2](npu.md#phase-2---soc-control-and-resource-integration) adds
resource 9 and the append-only ABI version 1.2. LP vector 33 / HP PLIC source
12 and the `APB4_NPU` routing are its frozen allocations. The NPU shell owns
its PCLK-side idle and round-trip quiesce acknowledgement; the data plane
combines them with the crossbar-side master idle exactly like the GA2D
source state. Existing resources
and register fields are not renumbered or reinterpreted. and register fields are not renumbered or reinterpreted.
## Scope ## Scope
The Resource Controller at `0x2000_A000` is the root-management ownership and The Resource Controller at `0x2000_A000` is the root-management ownership and
interrupt-routing authority for central DMA, USB2, SDIO0, SDIO1, SPI-SD, interrupt-routing authority for central DMA, USB2, SDIO0, SDIO1, SPI-SD,
EXT-H, JPEG, and APU. Hazard3 has read/write access; HP MMIO may inspect status EXT-H, JPEG, APU, GA2D, and NPU. Hazard3 has read/write access; HP MMIO may inspect status
but writes are rejected by the root-control firewall. but writes are rejected by the root-control firewall.
It is handwritten RTL and has a matching handwritten It is handwritten RTL and has a matching handwritten
`<retrosoc/hal/resource.h>` API; no register generator is used. `<retrosoc/hal/resource.h>` API; no register generator is used.
@@ -33,11 +39,14 @@ Resource indices are fixed:
| 6 | JPEG | 9 | | 6 | JPEG | 9 |
| 7 | APU | 10 | | 7 | APU | 10 |
| 8 | GA2D direct 2D engine | 11 | | 8 | GA2D direct 2D engine | 11 |
| 9 | NPU shell | 12 |
For resources 0 through 7, owner `0` routes the resource interrupt to the For resources 0 through 7, owner `0` routes the resource interrupt to the
existing LP vector. Owner `1` removes it from LP and routes it to the listed HP existing LP vector. Owner `1` removes it from LP and routes it to the listed HP
PLIC source. Resource 8 routes the GA2D raw IRQ to LP vector 32/external PLIC source. Resource 8 routes the GA2D raw IRQ to LP vector 32/external
ordinal 30 for owner `0`, or HP PLIC source 11 for owner `1`. Reset masks both ordinal 30 for owner `0`, or HP PLIC source 11 for owner `1`. Resource 9
routes the NPU raw IRQ to LP vector 33/external ordinal 31 for owner `0`, or
HP PLIC source 12 for owner `1`. Reset masks both
routes. Hardware never delivers one resource interrupt to both owners. routes. Hardware never delivers one resource interrupt to both owners.
## Register ABI ## Register ABI
@@ -45,7 +54,7 @@ routes. Hardware never delivers one resource interrupt to both owners.
| Offset | Name | Access | Contract | | Offset | Name | Access | Contract |
| ---: | --- | --- | --- | | ---: | --- | --- | --- |
| `0x000` | `IP_ID` | RO | `0x52534354` (`RSCT`) | | `0x000` | `IP_ID` | RO | `0x52534354` (`RSCT`) |
| `0x004` | `IP_VERSION` | RO | `0x00010001` | | `0x004` | `IP_VERSION` | RO | `0x00010002` |
| `0x008` | `CAPABILITY` | RO | resource count and ABI capability | | `0x008` | `CAPABILITY` | RO | resource count and ABI capability |
| `0x00C` | `GLOBAL_STATUS` | RO | cache request/clean and resource fault summary | | `0x00C` | `GLOBAL_STATUS` | RO | cache request/clean and resource fault summary |
| `0x010` | `CACHE_CONTROL` | RW | bit 0 clean ACK, bit 1 live request | | `0x010` | `CACHE_CONTROL` | RW | bit 0 clean ACK, bit 1 live request |
+3 -2
View File
@@ -141,11 +141,12 @@ capabilities:
- The canonical address map exposes configurable on-chip SRAM, a 64 MiB SDRAM - The canonical address map exposes configurable on-chip SRAM, a 64 MiB SDRAM
window, and an 8 MiB PSRAM window. Address-window capacity does not guarantee window, and an 8 MiB PSRAM window. Address-window capacity does not guarantee
that every implementation includes the corresponding physical memory. that every implementation includes the corresponding physical memory.
- AXI4 uses a 32-bit LP control plane and a 9x6 native AXI64 HP data plane - AXI4 uses a 32-bit LP control plane and a 10x6 native AXI64 HP data plane
with seven-bit global IDs. Vexii I/D ports remain independent; DMA and I/O with seven-bit global IDs. Vexii I/D ports remain independent; DMA and I/O
masters cross into HP and current memory frontends are reached through masters cross into HP and current memory frontends are reached through
64-to-32 target adapters. Master 8 is the dedicated PCLK-to-HP AXI64/ID3 64-to-32 target adapters. Master 8 is the dedicated PCLK-to-HP AXI64/ID3
GA2D bridge for a direct single-job private-AXI64 2D engine. GA2D bridge for a direct single-job private-AXI64 2D engine; master 9 is
the HP-native NPU path, tied safely idle until the Phase 3 DMA lands.
`APB4_GA2D` provides its PCLK control plane and resource-owned IRQ routing. `APB4_GA2D` provides its PCLK control plane and resource-owned IRQ routing.
The P5 engine supports FILL, COPY, bit-exact CONVERT, opaque alpha BLEND, The P5 engine supports FILL, COPY, bit-exact CONVERT, opaque alpha BLEND,
A8 fixed-color foreground masks, and exact equal background/destination A8 fixed-color foreground masks, and exact equal background/destination
+14 -14
View File
@@ -8020,19 +8020,19 @@
{ {
"id": "p1", "id": "p1",
"direction": "in", "direction": "in",
"width": 9, "width": 10,
"side": "west" "side": "west"
}, },
{ {
"id": "p2", "id": "p2",
"direction": "out", "direction": "out",
"width": 18, "width": 20,
"side": "east" "side": "east"
}, },
{ {
"id": "p3", "id": "p3",
"direction": "out", "direction": "out",
"width": 9, "width": 10,
"side": "south" "side": "south"
} }
], ],
@@ -8058,7 +8058,7 @@
{ {
"id": "p1", "id": "p1",
"direction": "out", "direction": "out",
"width": 9, "width": 10,
"side": "east" "side": "east"
} }
], ],
@@ -8077,7 +8077,7 @@
{ {
"id": "p2", "id": "p2",
"direction": "in", "direction": "in",
"width": 18, "width": 20,
"side": "west" "side": "west"
} }
], ],
@@ -8096,7 +8096,7 @@
{ {
"id": "p3", "id": "p3",
"direction": "in", "direction": "in",
"width": 9, "width": 10,
"side": "north" "side": "north"
} }
], ],
@@ -8144,7 +8144,7 @@
"file": "rtl/mini/top/apb4_system.sv", "file": "rtl/mini/top/apb4_system.sv",
"scope": "apb4_system", "scope": "apb4_system",
"symbol": "resource_idle_i", "symbol": "resource_idle_i",
"bits": 9 "bits": 10
} }
], ],
"instance_ports": [ "instance_ports": [
@@ -8172,7 +8172,7 @@
"file": "rtl/mini/top/apb4_system.sv", "file": "rtl/mini/top/apb4_system.sv",
"scope": "apb4_system", "scope": "apb4_system",
"symbol": "s_resource_owner", "symbol": "s_resource_owner",
"bits": 18 "bits": 20
} }
], ],
"instance_ports": [ "instance_ports": [
@@ -8200,7 +8200,7 @@
"file": "rtl/mini/top/apb4_system.sv", "file": "rtl/mini/top/apb4_system.sv",
"scope": "apb4_system", "scope": "apb4_system",
"symbol": "s_resource_irq_lp", "symbol": "s_resource_irq_lp",
"bits": 9 "bits": 10
} }
], ],
"instance_ports": [ "instance_ports": [
@@ -8228,7 +8228,7 @@
{ {
"id": "p0", "id": "p0",
"direction": "out", "direction": "out",
"width": 9, "width": 10,
"side": "east" "side": "east"
} }
], ],
@@ -8247,7 +8247,7 @@
{ {
"id": "p0", "id": "p0",
"direction": "in", "direction": "in",
"width": 9, "width": 10,
"side": "west" "side": "west"
}, },
{ {
@@ -8331,7 +8331,7 @@
"file": "rtl/mini/top/soc_data_plane.sv", "file": "rtl/mini/top/soc_data_plane.sv",
"scope": "soc_data_plane", "scope": "soc_data_plane",
"symbol": "s_monitor_master_read_accept", "symbol": "s_monitor_master_read_accept",
"bits": 9 "bits": 10
} }
], ],
"instance_ports": [ "instance_ports": [
@@ -9676,7 +9676,7 @@
}, },
{ {
"file": "rtl/mini/top/axi4_data_crossbar.sv", "file": "rtl/mini/top/axi4_data_crossbar.sv",
"text": "function automatic logic [CountWidth-1:0] master_read_limit(input int unsigned master); if ((master <= 2) || (master == 7)) return CountWidth'(4); if ((master == 3) || (master == 4)) return CountWidth'(2); if ((master == 5) || (master == 6) || (master == 8)) return CountWidth'(1); return '0; endfunction" "text": "function automatic logic [CountWidth-1:0] master_read_limit(input int unsigned master); if ((master <= 2) || (master == 7)) return CountWidth'(4); if ((master == 3) || (master == 4)) return CountWidth'(2); if ((master == 5) || (master == 6) || (master == 8) || (master == 9)) return CountWidth'(1); return '0; endfunction"
} }
], ],
"instance_ports": [ "instance_ports": [
@@ -11513,7 +11513,7 @@
}, },
{ {
"file": "rtl/mini/top/axi4_data_crossbar.sv", "file": "rtl/mini/top/axi4_data_crossbar.sv",
"text": "function automatic logic [CountWidth-1:0] master_read_limit(input int unsigned master); if ((master <= 2) || (master == 7)) return CountWidth'(4); if ((master == 3) || (master == 4)) return CountWidth'(2); if ((master == 5) || (master == 6) || (master == 8)) return CountWidth'(1); return '0; endfunction" "text": "function automatic logic [CountWidth-1:0] master_read_limit(input int unsigned master); if ((master <= 2) || (master == 7)) return CountWidth'(4); if ((master == 3) || (master == 4)) return CountWidth'(2); if ((master == 5) || (master == 6) || (master == 8) || (master == 9)) return CountWidth'(1); return '0; endfunction"
} }
], ],
"instance_ports": [ "instance_ports": [
+6 -1
View File
@@ -81,7 +81,7 @@
], ],
"jpeg": [ "jpeg": [
"8-bit Baseline Sequential JPEG encode or decode.", "8-bit Baseline Sequential JPEG encode or decode.",
"Images up to 2048 × 2048; modes are mutually exclusive.", "Images up to 2048 \u00d7 2048; modes are mutually exclusive.",
"Private AXI64 DMA, direct jobs and a scatter-gather ring." "Private AXI64 DMA, direct jobs and a scatter-gather ring."
], ],
"apu": [ "apu": [
@@ -89,6 +89,11 @@
"WAV/FLAC job and stream infrastructure in the current capability word.", "WAV/FLAC job and stream infrastructure in the current capability word.",
"MP3/KWS are not advertised; production bundle and capacity qualification remain distinct gates." "MP3/KWS are not advertised; production bundle and capacity qualification remain distinct gates."
], ],
"npu": [
"Phase 2 register shell with PCLK/HP control CDC, epoch reconciliation, and resource-9 ownership.",
"Interrupt round trip (DONE/ERROR/ABORTED) routed to LP vector 33 or HP PLIC source 12 by owner.",
"No execution readiness, operator, or DMA capability is advertised until the production path is verified."
],
"dvp": [ "dvp": [
"8-bit parallel RGB565/YUV422 capture.", "8-bit parallel RGB565/YUV422 capture.",
"Programmable sync polarity, sample edge, crop and snapshot mode.", "Programmable sync polarity, sample edge, crop and snapshot mode.",
+17
View File
@@ -497,6 +497,23 @@
], ],
"summary": "P5 APB4-controlled direct single-job private-AXI64 2D engine with FILL, COPY, bit-exact CONVERT, opaque alpha BLEND, A8 fixed-color foreground masks, and exact equal background/destination in-place composition. RGB565, RGB888, XRGB8888, and ARGB8888 are color surfaces; A8 is BLEND foreground-only. Resource-owned IRQ, snapshots, pitch, and byte-edge support remain available; transparent-background/premultiplied-alpha modes, scaling, rendering, descriptor/ring/queue submission, hardware cache coherency, and a Linux graphics driver are unavailable." "summary": "P5 APB4-controlled direct single-job private-AXI64 2D engine with FILL, COPY, bit-exact CONVERT, opaque alpha BLEND, A8 fixed-color foreground masks, and exact equal background/destination in-place composition. RGB565, RGB888, XRGB8888, and ARGB8888 are color surfaces; A8 is BLEND foreground-only. Resource-owned IRQ, snapshots, pitch, and byte-edge support remain available; transparent-background/premultiplied-alpha modes, scaling, rendering, descriptor/ring/queue submission, hardware cache coherency, and a Linux graphics driver are unavailable."
}, },
{
"id": "npu",
"regions": [
"APB4_NPU"
],
"irqs": [
"npu"
],
"sources": [
"docs/ip/npu.md",
"rtl/ip/multimedia/apb4_npu.sv",
"rtl/ip/multimedia/npu_control_cdc.sv",
"rtl/ip/multimedia/npu_core.sv",
"rtl/ip/multimedia/npu_reg.sv"
],
"summary": "P2 APB4 control shell for the neural processing unit: resource-9 ownership with epoch-reconciled PCLK/HP lifecycle CDC, DONE/ERROR/ABORTED interrupt round trip (LP vector 33 or HP PLIC source 12), performance snapshot bank, and a safely idle private AXI64 master. EXECUTION_READY, operator, and DMA capabilities stay clear until the production compute and descriptor path are verified."
},
{ {
"id": "reserved", "id": "reserved",
"regions": [ "regions": [
+69
View File
@@ -1769,6 +1769,75 @@
"Private DMA / I2S stream" "Private DMA / I2S stream"
] ]
}, },
"npu": {
"title": "Neural Processing Unit",
"blocks": [
"APB register shell",
"Control/lifecycle CDC",
"HP lifecycle endpoint",
"Private AXI64 master (tied idle at P2)"
],
"function_sections": [],
"software_sections": [],
"notes": [
"Phase 2 integrates the control shell only: the capability word is 0x00000009 (PRESENT and INTERRUPTS). EXECUTION_READY, PRIVATE_AXI64_DMA, and every operator bit stay clear, and START never passes READY.",
"The HP-native AXI64 master index 9 is tied safely idle at P2; production descriptor DMA is Phase 3 scope. No central-DMA channel is allocated.",
"The shell holds resource index 9, LP vector 33, and HP PLIC source 12. Ownership handoff uses the Resource Controller quiesce/acknowledge round trip reconciled across the PCLK/HP epoch boundary.",
"All writes require full word strobes; unknown offsets, reserved bits, and permission violations complete with PSLVERR and no side effect."
],
"software_steps": [
"Read capability through rs_npu_get_capability and treat EXECUTION_READY=0 as not deployable; P2 supports register, IRQ, and snapshot round trips only.",
"Set ownership and lifecycle through the Resource Controller HAL (rs_resource_set_owner/rs_resource_set_lifecycle) before relying on the LP/HP IRQ route.",
"Exercise the interrupt path with IRQ_ENABLE/IRQ_TEST and acknowledge with the W1C IRQ_STATE write.",
"Capture the performance bank with rs_npu_snapshot_counters; at P2 the bank reads back zero with the snapshot job token and recovery generation."
],
"api": [
{
"name": "rs_npu_get_capability",
"signature": "rs_status_t rs_npu_get_capability(rs_npu_capability_t *capability);"
},
{
"name": "rs_npu_submit",
"signature": "rs_status_t rs_npu_submit(const rs_npu_job_t *job);"
},
{
"name": "rs_npu_wait",
"signature": "rs_status_t rs_npu_wait(uint32_t job_id, rs_timeout_t timeout, rs_npu_status_t *status);"
},
{
"name": "rs_npu_abort_wait",
"signature": "rs_status_t rs_npu_abort_wait(uint32_t job_id, rs_timeout_t timeout, rs_npu_status_t *status);"
},
{
"name": "rs_npu_reset",
"signature": "rs_status_t rs_npu_reset(rs_timeout_t timeout);"
},
{
"name": "rs_npu_get_status",
"signature": "rs_status_t rs_npu_get_status(rs_npu_status_t *status);"
},
{
"name": "rs_npu_get_error",
"signature": "rs_status_t rs_npu_get_error(rs_npu_error_t *error);"
},
{
"name": "rs_npu_snapshot_counters",
"signature": "rs_status_t rs_npu_snapshot_counters(rs_timeout_t timeout, rs_npu_counters_t *counters);"
},
{
"name": "rs_npu_irq_enable",
"signature": "rs_status_t rs_npu_irq_enable(uint32_t events);"
},
{
"name": "rs_npu_irq_pending",
"signature": "rs_status_t rs_npu_irq_pending(uint32_t *events);"
},
{
"name": "rs_npu_irq_ack",
"signature": "rs_status_t rs_npu_irq_ack(uint32_t events);"
}
]
},
"crypto": { "crypto": {
"title": "Crypto Controller", "title": "Crypto Controller",
"blocks": [ "blocks": [
File diff suppressed because it is too large. Load diff
@@ -992,6 +992,35 @@
"rtl/ip/multimedia/ga2d_core.sv" "rtl/ip/multimedia/ga2d_core.sv"
] ]
}, },
"npu": {
"document": "docs/ip/npu.md",
"defines": [
"rtl/ip/multimedia/npu_define.svh"
],
"rtl": [
"rtl/ip/multimedia/npu_reg.sv"
],
"parameters": {},
"groups": [
{
"id": "main",
"title": "Phase 2 control shell and lifecycle endpoint",
"pattern": "APB4_NPU__.+",
"strip": "^APB4_NPU__",
"base": 0,
"stride": 0,
"count": 1,
"exclude": [
"APB4_NPU__DESCRIPTOR_VERSION"
]
}
],
"support": [
"rtl/ip/multimedia/apb4_npu.sv",
"rtl/ip/multimedia/npu_control_cdc.sv",
"rtl/ip/multimedia/npu_core.sv"
]
},
"dvp": { "dvp": {
"document": "docs/ip/dvp.md", "document": "docs/ip/dvp.md",
"defines": [ "defines": [
@@ -3646,6 +3646,22 @@
"rtl/ip/multimedia/ga2d_core.sv" "rtl/ip/multimedia/ga2d_core.sv"
] ]
}, },
{
"id": "apb4_npu",
"chapter": "npu",
"family": "npu",
"mode": "PRODUCT",
"region": "APB4_NPU",
"catalog": "npu",
"qualification": "Phase 2 integrates the NPU control shell only: register window, epoch-reconciled PCLK/HP lifecycle CDC, resource-9 ownership with round-trip quiesce acknowledgement, DONE/ERROR/ABORTED interrupt routing (LP vector 33 or HP PLIC source 12 by owner), and an atomic performance snapshot bank. The private AXI64 master is tied safely idle; EXECUTION_READY, operator, DMA, and numeric capabilities stay clear. Job execution, descriptor DMA, and inference are Phase 3/4 scope and are not advertised.",
"sources": [
"docs/ip/npu.md",
"rtl/ip/multimedia/npu_reg.sv",
"rtl/ip/multimedia/apb4_npu.sv",
"rtl/ip/multimedia/npu_control_cdc.sv",
"rtl/ip/multimedia/npu_core.sv"
]
},
{ {
"id": "apb4_crypto", "id": "apb4_crypto",
"chapter": "crypto", "chapter": "crypto",
@@ -5364,6 +5380,15 @@
"coverage": "Discover P5 capability, submit direct single-job FILL/COPY/CONVERT/BLEND work through master 8, including A8 fixed-color foreground masks and exact equal background/destination in-place composition, verify the resource-owned LP external completion route, and inspect destinations.", "coverage": "Discover P5 capability, submit direct single-job FILL/COPY/CONVERT/BLEND work through master 8, including A8 fixed-color foreground masks and exact equal background/destination in-place composition, verify the resource-owned LP external completion route, and inspect destinations.",
"boundary": "Exercises the direct private-AXI64 P5 functional subset only; it does not cover transparent-background/premultiplied-alpha modes, scaling, rendering, descriptor/ring/queue submission, cache coherency, a Linux graphics driver, HP P5 composition acceptance, or physical performance." "boundary": "Exercises the direct private-AXI64 P5 functional subset only; it does not cover transparent-background/premultiplied-alpha modes, scaling, rendering, descriptor/ring/queue submission, cache coherency, a Linux graphics driver, HP P5 composition acceptance, or physical performance."
}, },
{
"id": "npu-p2-shell-irq",
"title": "NPU P2 shell ownership and IRQ route",
"kind": "test-fail",
"code": 16,
"trigger": "rs_ci_smoke_npu_irq(",
"coverage": "Hand resource 9 to LP through the Resource Controller quiesce/acknowledge round trip, read back the shell identity/capability, and verify the IRQ_TEST-injected event reaches external ordinal 31 and clears by W1C.",
"boundary": "Covers the P2 register shell and interrupt route only; EXECUTION_READY stays clear, the private master is tied idle, and no job, DMA, or operator behavior is exercised."
},
{ {
"id": "apu", "id": "apu",
"title": "APU discovery", "title": "APU discovery",
+237
View File
@@ -0,0 +1,237 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
// NPU Phase 2 top shell: the PCLK register file (npu_reg), the shell/HP
// control CDC with epoch reconciliation (npu_control_cdc), and the minimal HP
// lifecycle endpoint (npu_core). The private AXI4 master is tied safely idle:
// no AW/W/AR request is ever issued, the R/B receivers stay ready as required
// by the reserved-burst rule, and W never precedes AW by construction. The
// production DMA that will drive this master is Phase 3 scope.
`include "npu_define.svh"
module apb4_npu (
// verilog_format: off -- preserve the shell lifecycle boundary columns
input logic clk_i,
input logic rst_n_i,
input logic clk_hp_i,
input logic rst_hp_n_i,
input logic [1:0] resource_owner_i,
input logic resource_owner_lock_i,
input logic resource_quiesce_i,
input logic resource_reset_i,
apb4_if.slave apb4,
output logic idle_o,
output logic block_ack_o,
output logic irq_o,
input logic hp_block_new_i,
output logic hp_pause_ack_o,
input logic hp_flush_i,
output logic hp_flush_busy_o,
output logic hp_idle_o,
axi4_if.master npu_axi4
// verilog_format: on
);
localparam int unsigned LaunchPayloadWidth = `APB4_NPU__LAUNCH_PAYLOAD_WIDTH;
localparam int unsigned ResultPayloadWidth = `APB4_NPU__RESULT_PAYLOAD_WIDTH;
localparam int unsigned SnapshotPayloadWidth = `APB4_NPU__SNAPSHOT_PAYLOAD_WIDTH;
localparam int unsigned EpochWidth = `APB4_NPU__EPOCH_WIDTH;
logic s_reg_idle;
logic s_abort;
logic s_soft_reset;
logic s_launch_req_valid;
logic s_launch_req_ready;
logic [ LaunchPayloadWidth-1:0] s_launch_req_payload;
logic s_result_valid;
logic s_result_ready;
logic [ ResultPayloadWidth-1:0] s_result_payload;
logic s_snap_req_valid;
logic s_snap_req_ready;
logic s_snap_resp_valid;
logic s_snap_resp_ready;
logic [SnapshotPayloadWidth-1:0] s_snap_resp_payload;
logic s_link_up;
logic s_flush_active;
logic [ 31:0] s_recovery_generation;
logic s_hp_launch_valid;
logic s_hp_launch_ready;
logic [ LaunchPayloadWidth-1:0] s_hp_launch_payload;
logic s_hp_result_valid;
logic s_hp_result_ready;
logic [ ResultPayloadWidth-1:0] s_hp_result_payload;
logic s_hp_snap_req_valid;
logic s_hp_snap_req_ready;
logic s_hp_snap_resp_valid;
logic s_hp_snap_resp_ready;
logic [SnapshotPayloadWidth-1:0] s_hp_snap_resp_payload;
logic [ EpochWidth-1:0] s_hp_epoch_req;
logic [ EpochWidth-1:0] s_hp_epoch_ack;
logic s_hp_quiesce_req;
logic s_hp_quiesce_ack;
logic s_hp_busy;
logic s_hp_draining;
logic s_hp_pause_active;
logic s_hp_busy_pclk;
logic s_hp_draining_pclk;
logic s_clock_paused;
logic s_abort_unused;
// PCLK shell idle for the resource controller: no launch slot or snapshot
// handshake pending, the link reconciled, and the HP side quiet.
assign idle_o = s_reg_idle && s_link_up && !s_hp_busy_pclk && !s_hp_draining_pclk;
npu_reg u_npu_reg (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.apb4 (apb4),
.resource_owner_i (resource_owner_i),
.resource_owner_lock_i (resource_owner_lock_i),
.resource_quiesce_i (resource_quiesce_i),
.resource_reset_i (resource_reset_i),
.hp_busy_i (s_hp_busy_pclk),
.hp_draining_i (s_hp_draining_pclk),
.clock_paused_i (s_clock_paused),
.link_up_i (s_link_up),
.flush_active_i (s_flush_active),
.recovery_generation_i (s_recovery_generation),
.launch_req_valid_o (s_launch_req_valid),
.launch_req_ready_i (s_launch_req_ready),
.launch_req_payload_o (s_launch_req_payload),
.result_valid_i (s_result_valid),
.result_ready_o (s_result_ready),
.result_payload_i (s_result_payload),
.snapshot_req_valid_o (s_snap_req_valid),
.snapshot_req_ready_i (s_snap_req_ready),
.snapshot_resp_valid_i (s_snap_resp_valid),
.snapshot_resp_ready_o (s_snap_resp_ready),
.snapshot_resp_payload_i(s_snap_resp_payload),
.abort_o (s_abort),
.soft_reset_o (s_soft_reset),
.idle_o (s_reg_idle),
.irq_o (irq_o)
);
npu_control_cdc u_npu_control_cdc (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.clk_hp_i (clk_hp_i),
.rst_hp_n_i (rst_hp_n_i),
.launch_req_valid_i (s_launch_req_valid),
.launch_req_ready_o (s_launch_req_ready),
.launch_req_payload_i (s_launch_req_payload),
.result_valid_o (s_result_valid),
.result_ready_i (s_result_ready),
.result_payload_o (s_result_payload),
.snapshot_req_valid_i (s_snap_req_valid),
.snapshot_req_ready_o (s_snap_req_ready),
.snapshot_resp_valid_o (s_snap_resp_valid),
.snapshot_resp_ready_i (s_snap_resp_ready),
.snapshot_resp_payload_o (s_snap_resp_payload),
.soft_reset_i (s_soft_reset),
.shell_idle_i (s_reg_idle),
.resource_quiesce_i (resource_quiesce_i),
.resource_reset_i (resource_reset_i),
.link_up_o (s_link_up),
.flush_active_o (s_flush_active),
.recovery_generation_o (s_recovery_generation),
.block_ack_o (block_ack_o),
.hp_launch_valid_o (s_hp_launch_valid),
.hp_launch_ready_i (s_hp_launch_ready),
.hp_launch_payload_o (s_hp_launch_payload),
.hp_result_valid_i (s_hp_result_valid),
.hp_result_ready_o (s_hp_result_ready),
.hp_result_payload_i (s_hp_result_payload),
.hp_snapshot_req_valid_o (s_hp_snap_req_valid),
.hp_snapshot_req_ready_i (s_hp_snap_req_ready),
.hp_snapshot_resp_valid_i (s_hp_snap_resp_valid),
.hp_snapshot_resp_ready_o (s_hp_snap_resp_ready),
.hp_snapshot_resp_payload_i(s_hp_snap_resp_payload),
.hp_epoch_req_o (s_hp_epoch_req),
.hp_epoch_ack_i (s_hp_epoch_ack),
.hp_quiesce_req_o (s_hp_quiesce_req),
.hp_quiesce_ack_i (s_hp_quiesce_ack),
.hp_busy_i (s_hp_busy),
.hp_draining_i (s_hp_draining),
.hp_pause_active_i (s_hp_pause_active),
.hp_flush_i (hp_flush_i),
.hp_busy_o (s_hp_busy_pclk),
.hp_draining_o (s_hp_draining_pclk),
.clock_paused_o (s_clock_paused)
);
npu_core u_npu_core (
.clk_hp_i (clk_hp_i),
.rst_hp_n_i (rst_hp_n_i),
.launch_valid_i (s_hp_launch_valid),
.launch_ready_o (s_hp_launch_ready),
.launch_data_i (s_hp_launch_payload),
.result_valid_o (s_hp_result_valid),
.result_ready_i (s_hp_result_ready),
.result_data_o (s_hp_result_payload),
.snapshot_req_valid_i (s_hp_snap_req_valid),
.snapshot_req_ready_o (s_hp_snap_req_ready),
.snapshot_resp_valid_o(s_hp_snap_resp_valid),
.snapshot_resp_ready_i(s_hp_snap_resp_ready),
.snapshot_resp_data_o (s_hp_snap_resp_payload),
.epoch_req_i (s_hp_epoch_req),
.epoch_ack_o (s_hp_epoch_ack),
.quiesce_req_i (s_hp_quiesce_req),
.quiesce_ack_o (s_hp_quiesce_ack),
.busy_o (s_hp_busy),
.draining_o (s_hp_draining),
.block_new_i (hp_block_new_i),
.clock_pause_ack_o (hp_pause_ack_o),
.flush_i (hp_flush_i),
.flush_busy_o (hp_flush_busy_o),
.pause_active_o (s_hp_pause_active),
.idle_o (hp_idle_o)
);
// Phase 2: the AXI4 master is tied safely idle. No request is ever issued
// and the response receivers remain ready; production DMA is Phase 3.
assign npu_axi4.awid = '0;
assign npu_axi4.awaddr = '0;
assign npu_axi4.awlen = '0;
assign npu_axi4.awsize = '0;
assign npu_axi4.awburst = '0;
assign npu_axi4.awlock = 1'b0;
assign npu_axi4.awcache = '0;
assign npu_axi4.awprot = '0;
assign npu_axi4.awqos = '0;
assign npu_axi4.awregion = '0;
assign npu_axi4.awuser = '0;
assign npu_axi4.awvalid = 1'b0;
assign npu_axi4.wdata = '0;
assign npu_axi4.wstrb = '0;
assign npu_axi4.wlast = 1'b0;
assign npu_axi4.wuser = '0;
assign npu_axi4.wvalid = 1'b0;
assign npu_axi4.bready = 1'b1;
assign npu_axi4.arid = '0;
assign npu_axi4.araddr = '0;
assign npu_axi4.arlen = '0;
assign npu_axi4.arsize = '0;
assign npu_axi4.arburst = '0;
assign npu_axi4.arlock = 1'b0;
assign npu_axi4.arcache = '0;
assign npu_axi4.arprot = '0;
assign npu_axi4.arqos = '0;
assign npu_axi4.arregion = '0;
assign npu_axi4.aruser = '0;
assign npu_axi4.arvalid = 1'b0;
assign npu_axi4.rready = 1'b1;
// The HP abort route is Phase 3 scope; the Phase 2 ABORT command on an idle
// NPU is a successful no-op and the busy path is unreachable.
assign s_abort_unused = s_abort;
`ifndef SV_ASSRT_DISABLE
always_ff @(posedge clk_hp_i) begin
if (rst_hp_n_i) begin
// The tied-off master never issues a request and stays ready to drain.
assert (!npu_axi4.awvalid && !npu_axi4.wvalid && !npu_axi4.arvalid);
assert (npu_axi4.bready && npu_axi4.rready);
end
end
`endif
endmodule
+472
View File
@@ -0,0 +1,472 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
// NPU Phase 2 shell/HP control CDC: launch, result, and snapshot
// async_reqack mailboxes, lifecycle scalar synchronizers, and epoch
// reconciliation. An either-domain reset, a coordinated fabric flush, a
// resource reset, or an accepted SOFT_RESET flushes the mailboxes and holds
// the link down until the HP endpoint acknowledges a fresh Gray-coded epoch;
// every completed recovery after the initial reconcile increments the
// PCLK-visible RECOVERY_GENERATION counter (soft reset and the initial
// reconcile rezero it instead). The one-entry Common mailboxes also flush
// themselves on either endpoint reset through their internal reset barrier.
`include "npu_define.svh"
module npu_control_cdc #(
parameter int unsigned LaunchPayloadWidth = `APB4_NPU__LAUNCH_PAYLOAD_WIDTH,
parameter int unsigned ResultPayloadWidth = `APB4_NPU__RESULT_PAYLOAD_WIDTH,
parameter int unsigned SnapshotPayloadWidth = `APB4_NPU__SNAPSHOT_PAYLOAD_WIDTH,
parameter int unsigned EpochWidth = `APB4_NPU__EPOCH_WIDTH,
parameter int unsigned SyncStages = 2
) (
// verilog_format: off -- preserve the PCLK/HP mailbox boundary columns
input logic clk_i,
input logic rst_n_i,
input logic clk_hp_i,
input logic rst_hp_n_i,
input logic launch_req_valid_i,
output logic launch_req_ready_o,
input logic [LaunchPayloadWidth-1:0] launch_req_payload_i,
output logic result_valid_o,
input logic result_ready_i,
output logic [ResultPayloadWidth-1:0] result_payload_o,
input logic snapshot_req_valid_i,
output logic snapshot_req_ready_o,
output logic snapshot_resp_valid_o,
input logic snapshot_resp_ready_i,
output logic [SnapshotPayloadWidth-1:0] snapshot_resp_payload_o,
input logic soft_reset_i,
input logic shell_idle_i,
input logic resource_quiesce_i,
input logic resource_reset_i,
output logic link_up_o,
output logic flush_active_o,
output logic [31:0] recovery_generation_o,
output logic block_ack_o,
output logic hp_launch_valid_o,
input logic hp_launch_ready_i,
output logic [LaunchPayloadWidth-1:0] hp_launch_payload_o,
input logic hp_result_valid_i,
output logic hp_result_ready_o,
input logic [ResultPayloadWidth-1:0] hp_result_payload_i,
output logic hp_snapshot_req_valid_o,
input logic hp_snapshot_req_ready_i,
input logic hp_snapshot_resp_valid_i,
output logic hp_snapshot_resp_ready_o,
input logic [SnapshotPayloadWidth-1:0] hp_snapshot_resp_payload_i,
output logic [EpochWidth-1:0] hp_epoch_req_o,
input logic [EpochWidth-1:0] hp_epoch_ack_i,
output logic hp_quiesce_req_o,
input logic hp_quiesce_ack_i,
input logic hp_busy_i,
input logic hp_draining_i,
input logic hp_pause_active_i,
input logic hp_flush_i,
output logic hp_busy_o,
output logic hp_draining_o,
output logic clock_paused_o
// verilog_format: on
);
// The epoch is a PCLK counter crossed as reflected Gray code. A fresh epoch
// is never toggled blindly: the request is derived from the observed
// acknowledgement as bin(ack)+1, so every new epoch is one the HP endpoint
// has never acknowledged and a match is always genuine round-trip evidence.
// One synchronizer bit changes per Gray transition, including the wrap.
typedef enum logic [0:0] {
LinkDown,
LinkUp
} state_e;
// The acknowledgement and HP-reset synchronizers flush on a PCLK reset and
// then briefly replay stale samples; reconciliation waits out this refill
// margin (SYNC_STAGES plus two edges) before evaluating any crossing.
localparam int unsigned RefillCycles = 4;
state_e s_state_d, s_state_q;
logic [EpochWidth-1:0] s_epoch_d, s_epoch_q;
logic [EpochWidth-1:0] s_epoch_gray;
logic [EpochWidth-1:0] s_epoch_next;
logic [EpochWidth-1:0] s_hp_epoch_ack_gray;
logic [31:0] s_generation_d, s_generation_q;
logic s_init_done_d, s_init_done_q;
logic s_soft_pending_d, s_soft_pending_q;
logic s_block_ack_d, s_block_ack_q;
logic s_hold_link_d, s_hold_link_q;
logic [2:0] s_refill_cnt_d, s_refill_cnt_q;
logic s_epoch_armed_d, s_epoch_armed_q;
logic s_hp_rst_n_sync;
logic s_hp_flush_sync;
logic s_hp_quiesce_ack_sync;
logic s_hold_link;
logic s_hold_link_rise;
logic s_resource_hold;
logic s_epoch_matched;
logic s_refill_done;
logic s_link_up;
logic s_mbox_pclk_rst_n;
function automatic logic [EpochWidth-1:0] gray_decode(input logic [EpochWidth-1:0] gray_i);
logic [EpochWidth-1:0] decoded;
begin
decoded = '0;
for (int unsigned bit_index = 0; bit_index < EpochWidth; bit_index++) begin
decoded[bit_index] = ^(gray_i >> bit_index);
end
return decoded;
end
endfunction
`ifndef SYNTHESIS
initial begin
if ((EpochWidth < 2) || (SyncStages < 2) || (LaunchPayloadWidth < 1) ||
(ResultPayloadWidth < 1) || (SnapshotPayloadWidth < 1)) begin
$fatal(1, "npu_control_cdc: invalid payload, epoch, or synchronizer geometry");
end
end
`endif
assign s_epoch_gray = s_epoch_q ^ (s_epoch_q >> 1);
assign s_epoch_next = gray_decode(s_hp_epoch_ack_gray) + EpochWidth'(1);
assign s_hold_link = resource_reset_i || !s_hp_rst_n_sync || s_hp_flush_sync;
assign s_hold_link_rise = s_hold_link && !s_hold_link_q;
assign s_resource_hold = resource_quiesce_i || resource_reset_i;
assign s_epoch_matched = (s_hp_epoch_ack_gray == s_epoch_gray);
assign s_refill_done = (s_refill_cnt_q == 3'(RefillCycles));
assign s_link_up = (s_state_q == LinkUp);
assign link_up_o = s_link_up;
assign flush_active_o = !s_link_up;
assign recovery_generation_o = s_generation_q;
assign block_ack_o = s_block_ack_q;
// The mailbox PCLK-side reset flushes both domains through the mailbox
// internal reset barrier whenever the link is down.
assign s_mbox_pclk_rst_n = rst_n_i && s_link_up;
always_comb begin
s_state_d = s_state_q;
s_epoch_d = s_epoch_q;
s_generation_d = s_generation_q;
s_init_done_d = s_init_done_q;
s_soft_pending_d = s_soft_pending_q;
s_block_ack_d = s_block_ack_q;
s_hold_link_d = s_hold_link;
s_refill_cnt_d = s_refill_cnt_q;
s_epoch_armed_d = s_epoch_armed_q;
// The resource quiesce/reset acknowledgement is a genuine round trip:
// the HP endpoint must register the request and the current epoch must be
// acknowledged after the hold began, with the PCLK shell launch slot and
// snapshot handshake idle (a pending shell launch still participates here
// even though Phase 2 never accepts one). A hold that invalidates the
// link enters LinkDown and derives a fresh epoch first, so a matched
// epoch in LinkDown is always fresh; a quiesce-only hold keeps the link
// up. Epoch or request evidence lost mid-hold (for example an HP reset
// during a quiesce) revokes the acknowledgement until reconciled again.
if (!s_resource_hold) begin
s_block_ack_d = 1'b0;
end else if (!s_epoch_matched || !s_hp_quiesce_ack_sync) begin
s_block_ack_d = 1'b0;
end else if (shell_idle_i && ((s_state_q == LinkDown) || !s_hold_link)) begin
s_block_ack_d = 1'b1;
end
unique case (s_state_q)
LinkDown: begin
if (!s_refill_done) begin
s_refill_cnt_d = s_refill_cnt_q + 3'd1;
end
if (soft_reset_i) begin
s_soft_pending_d = 1'b1;
end
// The first epoch after a shell reset is armed only after the
// synchronizers refilled; a fresh hold condition arriving after the
// current epoch already reconciled forces another acknowledged epoch
// before rearm.
if (s_refill_done && !s_epoch_armed_q) begin
s_epoch_d = s_epoch_next;
s_epoch_armed_d = 1'b1;
end else if (s_hold_link_rise && s_epoch_matched) begin
s_epoch_d = s_epoch_next;
end
if (s_refill_done && s_epoch_armed_q && s_epoch_matched && !s_hold_link) begin
s_state_d = LinkUp;
s_init_done_d = 1'b1;
s_soft_pending_d = 1'b0;
if (s_soft_pending_q || soft_reset_i || !s_init_done_q) begin
s_generation_d = 32'd0;
end else begin
s_generation_d = s_generation_q + 32'd1;
end
end
end
LinkUp: begin
if (s_hold_link || soft_reset_i) begin
s_state_d = LinkDown;
s_soft_pending_d = soft_reset_i;
s_epoch_d = s_epoch_next;
end
end
default: begin
s_state_d = LinkDown;
s_epoch_d = '0;
s_generation_d = 32'd0;
s_init_done_d = 1'b0;
s_soft_pending_d = 1'b0;
s_block_ack_d = 1'b0;
s_refill_cnt_d = 3'd0;
s_epoch_armed_d = 1'b0;
end
endcase
end
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_state_q <= LinkDown;
end else begin
s_state_q <= s_state_d;
end
end
dffr #(
.DATA_WIDTH(EpochWidth)
) u_epoch (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_epoch_d),
.dat_o (s_epoch_q)
);
dffr #(
.DATA_WIDTH(32)
) u_generation (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_generation_d),
.dat_o (s_generation_q)
);
dffr #(
.DATA_WIDTH(1)
) u_init_done (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_init_done_d),
.dat_o (s_init_done_q)
);
dffr #(
.DATA_WIDTH(1)
) u_soft_pending (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_soft_pending_d),
.dat_o (s_soft_pending_q)
);
dffr #(
.DATA_WIDTH(1)
) u_block_ack (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_block_ack_d),
.dat_o (s_block_ack_q)
);
dffr #(
.DATA_WIDTH(1)
) u_hold_link (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_hold_link_d),
.dat_o (s_hold_link_q)
);
dffr #(
.DATA_WIDTH(3)
) u_refill_cnt (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_refill_cnt_d),
.dat_o (s_refill_cnt_q)
);
dffr #(
.DATA_WIDTH(1)
) u_epoch_armed (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_epoch_armed_d),
.dat_o (s_epoch_armed_q)
);
// Launch mailbox, PCLK shell to HP endpoint.
async_reqack #(
.DATA_WIDTH (LaunchPayloadWidth),
.SYNC_STAGES(SyncStages)
) u_launch_mailbox (
.src_clk_i (clk_i),
.src_rst_n_i(s_mbox_pclk_rst_n),
.src_valid_i(launch_req_valid_i),
.src_ready_o(launch_req_ready_o),
.src_data_i (launch_req_payload_i),
.dst_clk_i (clk_hp_i),
.dst_rst_n_i(rst_hp_n_i),
.dst_valid_o(hp_launch_valid_o),
.dst_ready_i(hp_launch_ready_i),
.dst_data_o (hp_launch_payload_o)
);
// Result mailbox, HP endpoint to PCLK shell.
async_reqack #(
.DATA_WIDTH (ResultPayloadWidth),
.SYNC_STAGES(SyncStages)
) u_result_mailbox (
.src_clk_i (clk_hp_i),
.src_rst_n_i(rst_hp_n_i),
.src_valid_i(hp_result_valid_i),
.src_ready_o(hp_result_ready_o),
.src_data_i (hp_result_payload_i),
.dst_clk_i (clk_i),
.dst_rst_n_i(s_mbox_pclk_rst_n),
.dst_valid_o(result_valid_o),
.dst_ready_i(result_ready_i),
.dst_data_o (result_payload_o)
);
// Snapshot request mailbox, PCLK shell to HP endpoint.
async_reqack #(
.DATA_WIDTH (1),
.SYNC_STAGES(SyncStages)
) u_snapshot_req_mailbox (
.src_clk_i (clk_i),
.src_rst_n_i(s_mbox_pclk_rst_n),
.src_valid_i(snapshot_req_valid_i),
.src_ready_o(snapshot_req_ready_o),
.src_data_i (1'b0),
.dst_clk_i (clk_hp_i),
.dst_rst_n_i(rst_hp_n_i),
.dst_valid_o(hp_snapshot_req_valid_o),
.dst_ready_i(hp_snapshot_req_ready_i),
.dst_data_o ()
);
// Snapshot response mailbox, HP endpoint to PCLK shell. The whole counter
// bank and job token cross as one mailbox item; independent synchronizers
// for counter bits are prohibited by the ABI.
async_reqack #(
.DATA_WIDTH (SnapshotPayloadWidth),
.SYNC_STAGES(SyncStages)
) u_snapshot_resp_mailbox (
.src_clk_i (clk_hp_i),
.src_rst_n_i(rst_hp_n_i),
.src_valid_i(hp_snapshot_resp_valid_i),
.src_ready_o(hp_snapshot_resp_ready_o),
.src_data_i (hp_snapshot_resp_payload_i),
.dst_clk_i (clk_i),
.dst_rst_n_i(s_mbox_pclk_rst_n),
.dst_valid_o(snapshot_resp_valid_o),
.dst_ready_i(snapshot_resp_ready_i),
.dst_data_o (snapshot_resp_payload_o)
);
// Lifecycle scalar crossings. cdc_sync is restricted to these
// independently encoded controls; multi-bit payloads use the mailboxes.
cdc_sync #(
.STAGE(SyncStages)
) u_hp_reset_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (rst_hp_n_i),
.dat_o (s_hp_rst_n_sync)
);
cdc_sync #(
.STAGE(SyncStages)
) u_hp_flush_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (hp_flush_i),
.dat_o (s_hp_flush_sync)
);
cdc_sync #(
.STAGE (SyncStages),
.DATA_WIDTH(EpochWidth)
) u_epoch_req_sync (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.dat_i (s_epoch_gray),
.dat_o (hp_epoch_req_o)
);
cdc_sync #(
.STAGE (SyncStages),
.DATA_WIDTH(EpochWidth)
) u_epoch_ack_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (hp_epoch_ack_i),
.dat_o (s_hp_epoch_ack_gray)
);
cdc_sync #(
.STAGE(SyncStages)
) u_quiesce_req_sync (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.dat_i (s_resource_hold),
.dat_o (hp_quiesce_req_o)
);
cdc_sync #(
.STAGE(SyncStages)
) u_quiesce_ack_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (hp_quiesce_ack_i),
.dat_o (s_hp_quiesce_ack_sync)
);
cdc_sync #(
.STAGE(SyncStages)
) u_hp_busy_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (hp_busy_i),
.dat_o (hp_busy_o)
);
cdc_sync #(
.STAGE(SyncStages)
) u_hp_draining_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (hp_draining_i),
.dat_o (hp_draining_o)
);
cdc_sync #(
.STAGE(SyncStages)
) u_hp_pause_active_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (hp_pause_active_i),
.dat_o (clock_paused_o)
);
`ifndef SV_ASSRT_DISABLE
logic s_launch_wait_q;
logic s_link_down_q;
logic s_hold_prev_q;
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_launch_wait_q <= 1'b0;
s_link_down_q <= 1'b0;
s_hold_prev_q <= 1'b0;
end else begin
s_launch_wait_q <= launch_req_valid_i && !launch_req_ready_o;
s_link_down_q <= !s_link_up;
s_hold_prev_q <= s_resource_hold;
// A launch request holds valid and payload until accepted; only the
// mailbox flush of a link-down event may withdraw it.
if (s_launch_wait_q) begin
assert ((launch_req_valid_i && $stable(launch_req_payload_i)) || s_link_down_q);
end
// An operational link always runs on an acknowledged current epoch.
if (s_link_up && !s_hold_link) begin
assert (s_epoch_matched);
end
// The registered acknowledgement trails the request by one cycle;
// outside that trailing edge it is impossible without a request.
if (block_ack_o) begin
assert (s_resource_hold || s_hold_prev_q);
end
end
end
`endif
endmodule
+168
View File
@@ -0,0 +1,168 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
// NPU Phase 2 HP-domain lifecycle endpoint. It terminates the launch, result,
// and snapshot mailboxes of npu_control_cdc, participates in the epoch
// reconciliation handshake, and answers idle snapshots with a zero counter
// bank. Phase 2 has no jobs and no DMA: launches can never be accepted by the
// PCLK shell (START never passes), the sink below exists only to keep the
// mailbox protocol complete, no result is ever produced, and there is no AXI
// work to pause, drain, or flush.
`include "npu_define.svh"
module npu_core #(
parameter int unsigned LaunchPayloadWidth = `APB4_NPU__LAUNCH_PAYLOAD_WIDTH,
parameter int unsigned ResultPayloadWidth = `APB4_NPU__RESULT_PAYLOAD_WIDTH,
parameter int unsigned SnapshotPayloadWidth = `APB4_NPU__SNAPSHOT_PAYLOAD_WIDTH,
parameter int unsigned EpochWidth = `APB4_NPU__EPOCH_WIDTH
) (
// verilog_format: off -- preserve the HP lifecycle boundary columns
input logic clk_hp_i,
input logic rst_hp_n_i,
input logic launch_valid_i,
output logic launch_ready_o,
input logic [LaunchPayloadWidth-1:0] launch_data_i,
output logic result_valid_o,
input logic result_ready_i,
output logic [ResultPayloadWidth-1:0] result_data_o,
input logic snapshot_req_valid_i,
output logic snapshot_req_ready_o,
output logic snapshot_resp_valid_o,
input logic snapshot_resp_ready_i,
output logic [SnapshotPayloadWidth-1:0] snapshot_resp_data_o,
input logic [EpochWidth-1:0] epoch_req_i,
output logic [EpochWidth-1:0] epoch_ack_o,
input logic quiesce_req_i,
output logic quiesce_ack_o,
output logic busy_o,
output logic draining_o,
input logic block_new_i,
output logic clock_pause_ack_o,
input logic flush_i,
output logic flush_busy_o,
output logic pause_active_o,
output logic idle_o
// verilog_format: on
);
typedef enum logic [0:0] {
SnapIdle,
SnapRespond
} state_e;
state_e s_state_d, s_state_q;
logic [EpochWidth-1:0] s_epoch_ack_d, s_epoch_ack_q;
logic s_quiesce_ack_d, s_quiesce_ack_q;
logic s_epoch_restart;
logic s_unused;
`ifndef SYNTHESIS
initial begin
if ((EpochWidth < 2) || (LaunchPayloadWidth < 1) || (ResultPayloadWidth < 1) ||
(SnapshotPayloadWidth < 1)) begin
$fatal(1, "npu_core: invalid payload or epoch geometry");
end
end
`endif
// Phase 2 protocol-complete sinks and constant sources: a launch is sunk
// (unreachable), no terminal result exists, the idle snapshot returns the
// zero bank with a zero job token, and no AXI activity ever needs draining.
assign launch_ready_o = 1'b1;
assign result_valid_o = 1'b0;
assign result_data_o = '0;
assign busy_o = 1'b0;
assign draining_o = 1'b0;
assign flush_busy_o = 1'b0;
assign snapshot_resp_data_o = '0;
// With zero accepted AXI obligations and no internal work, the clock-pause
// acknowledge is immediate and the HP idle contribution follows the
// platform formula !block_new_i || clock_pause_ack_o.
assign clock_pause_ack_o = block_new_i;
assign pause_active_o = block_new_i && clock_pause_ack_o;
assign idle_o = !block_new_i || clock_pause_ack_o;
assign epoch_ack_o = s_epoch_ack_q;
assign quiesce_ack_o = s_quiesce_ack_q;
assign s_epoch_restart = (epoch_req_i != s_epoch_ack_q);
assign s_unused = ^{launch_data_i, result_ready_i, flush_i, 1'b0};
always_comb begin
s_state_d = s_state_q;
s_epoch_ack_d = s_epoch_ack_q;
s_quiesce_ack_d = quiesce_req_i;
snapshot_req_ready_o = 1'b0;
snapshot_resp_valid_o = 1'b0;
unique case (s_state_q)
SnapIdle: begin
snapshot_req_ready_o = 1'b1;
if (snapshot_req_valid_i) begin
s_state_d = SnapRespond;
end
end
SnapRespond: begin
snapshot_resp_valid_o = 1'b1;
if (snapshot_resp_ready_i) begin
s_state_d = SnapIdle;
end
end
default: s_state_d = SnapIdle;
endcase
// A fresh epoch restarts the endpoint bookkeeping before it is
// acknowledged: any in-flight mailbox item is being flushed by the link
// reset held for the whole reconciliation, so the endpoint returns to
// SnapIdle first and only then publishes the new epoch.
if (s_epoch_restart) begin
s_state_d = SnapIdle;
s_epoch_ack_d = epoch_req_i;
end
end
always_ff @(posedge clk_hp_i or negedge rst_hp_n_i) begin
if (!rst_hp_n_i) begin
s_state_q <= SnapIdle;
end else begin
s_state_q <= s_state_d;
end
end
dffr #(
.DATA_WIDTH(EpochWidth)
) u_epoch_ack (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.dat_i (s_epoch_ack_d),
.dat_o (s_epoch_ack_q)
);
dffr #(
.DATA_WIDTH(1)
) u_quiesce_ack (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.dat_i (s_quiesce_ack_d),
.dat_o (s_quiesce_ack_q)
);
`ifndef SV_ASSRT_DISABLE
logic s_resp_wait_q;
logic s_epoch_restart_q;
always_ff @(posedge clk_hp_i or negedge rst_hp_n_i) begin
if (!rst_hp_n_i) begin
s_resp_wait_q <= 1'b0;
s_epoch_restart_q <= 1'b0;
end else begin
s_resp_wait_q <= snapshot_resp_valid_o && !snapshot_resp_ready_i;
s_epoch_restart_q <= s_epoch_restart;
// The snapshot response holds valid and its payload stable until the
// mailbox accepts it; only an epoch restart may withdraw it.
if (s_resp_wait_q) begin
assert ((snapshot_resp_valid_o && $stable(snapshot_resp_data_o)) || s_epoch_restart_q);
end
// No terminal result can ever appear at Phase 2.
assert (!result_valid_o);
end
end
`endif
endmodule
+190
View File
@@ -0,0 +1,190 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`ifndef RETROSOC_NPU_DEFINE_SVH
`define RETROSOC_NPU_DEFINE_SVH
// Handwritten NPU hardware/software ABI. Offsets, reset values, bit fields,
// result/fault enumerations, opcodes, and descriptor word positions follow
// docs/ip/npu.md "Register and Software ABI"; the C HAL header keeps parity.
// verilog_format: off -- keep the handwritten hardware/software ABI columns aligned
`define APB4_NPU__IP_ID 12'h000
`define APB4_NPU__IP_VERSION 12'h004
`define APB4_NPU__CAPABILITY 12'h008
`define APB4_NPU__STATUS 12'h00c
`define APB4_NPU__CONTROL 12'h010
`define APB4_NPU__IRQ_STATE 12'h014
`define APB4_NPU__IRQ_ENABLE 12'h018
`define APB4_NPU__IRQ_TEST 12'h01c
`define APB4_NPU__JOB_BASE 12'h020
`define APB4_NPU__JOB_COUNT 12'h024
`define APB4_NPU__JOB_ID 12'h028
`define APB4_NPU__TIMEOUT_CYCLES 12'h02c
`define APB4_NPU__RESULT_JOB_ID 12'h030
`define APB4_NPU__RESULT_CODE 12'h034
`define APB4_NPU__COMPLETED_DESCRIPTORS 12'h038
`define APB4_NPU__FAULT_DESCRIPTOR 12'h03c
`define APB4_NPU__FAULT_CODE 12'h040
`define APB4_NPU__FAULT_ADDRESS 12'h044
`define APB4_NPU__FAULT_INFO 12'h048
`define APB4_NPU__NUMERIC_PROFILE 12'h04c
`define APB4_NPU__LOCAL_BYTES 12'h050
`define APB4_NPU__MAC_CONFIG 12'h054
`define APB4_NPU__MAX_K_SLICE 12'h058
`define APB4_NPU__MAX_DIMENSION 12'h05c
`define APB4_NPU__OP_CAPABILITY 12'h060
`define APB4_NPU__OWNER_STATUS 12'h064
`define APB4_NPU__RECOVERY_GENERATION 12'h068
`define APB4_NPU__PERF_CONTROL 12'h06c
`define APB4_NPU__PERF_STATUS 12'h070
`define APB4_NPU__PERF_JOB_ID 12'h074
`define APB4_NPU__PERF_GENERATION 12'h078
`define APB4_NPU__DESCRIPTOR_BYTES 12'h07c
`define APB4_NPU__PERF_ACTIVE_CYCLES_LO 12'h080
`define APB4_NPU__PERF_ACTIVE_CYCLES_HI 12'h084
`define APB4_NPU__PERF_CLOCK_PAUSE_CYCLES_LO 12'h088
`define APB4_NPU__PERF_CLOCK_PAUSE_CYCLES_HI 12'h08c
`define APB4_NPU__PERF_USEFUL_MACS_LO 12'h090
`define APB4_NPU__PERF_USEFUL_MACS_HI 12'h094
`define APB4_NPU__PERF_PACK_CYCLES_LO 12'h098
`define APB4_NPU__PERF_PACK_CYCLES_HI 12'h09c
`define APB4_NPU__PERF_LOCAL_BANK_STALL_LO 12'h0a0
`define APB4_NPU__PERF_LOCAL_BANK_STALL_HI 12'h0a4
`define APB4_NPU__PERF_DMA_READ_BYTES_LO 12'h0a8
`define APB4_NPU__PERF_DMA_READ_BYTES_HI 12'h0ac
`define APB4_NPU__PERF_DMA_WRITE_BYTES_LO 12'h0b0
`define APB4_NPU__PERF_DMA_WRITE_BYTES_HI 12'h0b4
`define APB4_NPU__PERF_DMA_STALL_CYCLES_LO 12'h0b8
`define APB4_NPU__PERF_DMA_STALL_CYCLES_HI 12'h0bc
`define APB4_NPU__PERF_REQUANT_STALL_LO 12'h0c0
`define APB4_NPU__PERF_REQUANT_STALL_HI 12'h0c4
`define APB4_NPU__PERF_RETIRED_DESCRIPTORS_LO 12'h0c8
`define APB4_NPU__PERF_RETIRED_DESCRIPTORS_HI 12'h0cc
`define APB4_NPU__IP_ID_VALUE 32'h4e50_5531
`define APB4_NPU__IP_VERSION_VALUE 32'h0001_0000
`define APB4_NPU__CAPABILITY_P2 32'h0000_0009
`define APB4_NPU__NUMERIC_PROFILE_VALUE 32'h0000_0001
`define APB4_NPU__DESCRIPTOR_BYTES_VALUE 32'h0000_0080
`define APB4_NPU__FAULT_DESCRIPTOR_RESET 32'hffff_ffff
`define APB4_NPU__CAPABILITY_PRESENT 0
`define APB4_NPU__CAPABILITY_EXECUTION_READY 1
`define APB4_NPU__CAPABILITY_PRIVATE_AXI64_DMA 2
`define APB4_NPU__CAPABILITY_INTERRUPTS 3
`define APB4_NPU__CAPABILITY_DOUBLE_ROUNDING 4
`define APB4_NPU__CAPABILITY_NATIVE_HP_CLOCK 5
`define APB4_NPU__CAPABILITY_SOFTWARE_ABORT 6
`define APB4_NPU__STATUS_READY 0
`define APB4_NPU__STATUS_BUSY 1
`define APB4_NPU__STATUS_DRAINING 2
`define APB4_NPU__STATUS_CLOCK_PAUSED 3
`define APB4_NPU__STATUS_RECOVERING 4
`define APB4_NPU__STATUS_RESULT_VALID 5
`define APB4_NPU__CONTROL_START 0
`define APB4_NPU__CONTROL_ABORT 1
`define APB4_NPU__CONTROL_SOFT_RESET 2
`define APB4_NPU__IRQ_DONE 0
`define APB4_NPU__IRQ_ERROR 1
`define APB4_NPU__IRQ_ABORTED 2
`define APB4_NPU__IRQ_ALL 3'h7
`define APB4_NPU__RESULT_CODE_NONE 0
`define APB4_NPU__RESULT_CODE_DONE 1
`define APB4_NPU__RESULT_CODE_ERROR 2
`define APB4_NPU__RESULT_CODE_ABORTED 3
`define APB4_NPU__RESULT_CODE_RESET_CANCELLED 4
`define APB4_NPU__FAULT_CODE_NONE 0
`define APB4_NPU__FAULT_CODE_DESCRIPTOR 1
`define APB4_NPU__FAULT_CODE_UNSUPPORTED 2
`define APB4_NPU__FAULT_CODE_RANGE 3
`define APB4_NPU__FAULT_CODE_AXI_READ 4
`define APB4_NPU__FAULT_CODE_AXI_WRITE 5
`define APB4_NPU__FAULT_CODE_AXI_PROTOCOL 6
`define APB4_NPU__FAULT_CODE_ARITHMETIC 7
`define APB4_NPU__FAULT_CODE_NO_PROGRESS 8
`define APB4_NPU__FAULT_CODE_LOCAL_STATE 9
`define APB4_NPU__FAULT_CODE_RESET_CANCELLED 10
`define APB4_NPU__FAULT_INFO_AXI_RESPONSE 0
`define APB4_NPU__FAULT_INFO_DIRECTION 2
`define APB4_NPU__FAULT_INFO_LANE 8
`define APB4_NPU__OWNER_STATUS_OWNER 0
`define APB4_NPU__OWNER_STATUS_LOCK 8
`define APB4_NPU__OWNER_STATUS_QUIESCE 9
`define APB4_NPU__OWNER_STATUS_RESET_REQUEST 10
`define APB4_NPU__PERF_CONTROL_SNAPSHOT 0
`define APB4_NPU__PERF_STATUS_SNAP_BUSY 0
`define APB4_NPU__PERF_STATUS_SNAP_VALID 1
`define APB4_NPU__PERF_COUNTER_COUNT 10
`define APB4_NPU__OP_CONV2D 1
`define APB4_NPU__OP_DEPTHWISE3X3 2
`define APB4_NPU__OP_FULLY_CONNECTED 3
`define APB4_NPU__OP_ADD 4
`define APB4_NPU__OP_MAX_POOL 5
`define APB4_NPU__OP_AVERAGE_POOL 6
`define APB4_NPU__OP_GLOBAL_AVERAGE_POOL 7
`define APB4_NPU__OP_CLAMP 8
`define APB4_NPU__DESCRIPTOR_VERSION 16'h0100
`define APB4_NPU__DESCRIPTOR_WORD_VERSION_OPCODE 0
`define APB4_NPU__DESCRIPTOR_WORD_RESERVED 1
`define APB4_NPU__DESCRIPTOR_WORD_INPUT0_BASE 2
`define APB4_NPU__DESCRIPTOR_WORD_INPUT1_BASE 3
`define APB4_NPU__DESCRIPTOR_WORD_OUTPUT_BASE 4
`define APB4_NPU__DESCRIPTOR_WORD_PARAM_BASE 5
`define APB4_NPU__DESCRIPTOR_WORD_INPUT_HW 6
`define APB4_NPU__DESCRIPTOR_WORD_CHANNELS 7
`define APB4_NPU__DESCRIPTOR_WORD_OUTPUT_HW 8
`define APB4_NPU__DESCRIPTOR_WORD_INPUT0_ROW_BYTES 9
`define APB4_NPU__DESCRIPTOR_WORD_OUTPUT_ROW_BYTES 10
`define APB4_NPU__DESCRIPTOR_WORD_INPUT1_ROW_BYTES 11
`define APB4_NPU__DESCRIPTOR_WORD_KERNEL_STRIDE 12
`define APB4_NPU__DESCRIPTOR_WORD_PADDING 13
`define APB4_NPU__DESCRIPTOR_WORD_TILE_HW 14
`define APB4_NPU__DESCRIPTOR_WORD_K_SLICE 15
`define APB4_NPU__DESCRIPTOR_WORD_INPUT0_BYTES 16
`define APB4_NPU__DESCRIPTOR_WORD_INPUT1_BYTES 17
`define APB4_NPU__DESCRIPTOR_WORD_OUTPUT_BYTES 18
`define APB4_NPU__DESCRIPTOR_WORD_PARAM_BYTES 19
`define APB4_NPU__DESCRIPTOR_WORD_INPUT0_ZERO 20
`define APB4_NPU__DESCRIPTOR_WORD_INPUT1_ZERO 21
`define APB4_NPU__DESCRIPTOR_WORD_OUTPUT_ZERO 22
`define APB4_NPU__DESCRIPTOR_WORD_ACTIVATION_BOUNDS 23
`define APB4_NPU__DESCRIPTOR_WORD_WEIGHT_BASE 24
`define APB4_NPU__DESCRIPTOR_WORD_WEIGHT_BYTES 25
// Shell/HP mailbox payload layouts: the hardware-internal ABI between npu_reg
// (PCLK) and npu_core (HP) carried by npu_control_cdc async_reqack instances.
`define APB4_NPU__LAUNCH_PAYLOAD_WIDTH 128
`define APB4_NPU__LAUNCH_JOB_BASE 0
`define APB4_NPU__LAUNCH_JOB_ID 32
`define APB4_NPU__LAUNCH_TIMEOUT_CYCLES 64
`define APB4_NPU__LAUNCH_JOB_COUNT 96
`define APB4_NPU__RESULT_PAYLOAD_WIDTH 170
`define APB4_NPU__RESULT_PAYLOAD_JOB_ID 0
`define APB4_NPU__RESULT_PAYLOAD_COMPLETED 32
`define APB4_NPU__RESULT_PAYLOAD_FAULT_DESC 64
`define APB4_NPU__RESULT_PAYLOAD_FAULT_ADDR 96
`define APB4_NPU__RESULT_PAYLOAD_FAULT_INFO 128
`define APB4_NPU__RESULT_PAYLOAD_CODE 160
`define APB4_NPU__RESULT_PAYLOAD_FAULT_CODE 163
`define APB4_NPU__RESULT_PAYLOAD_IRQ_EVENTS 167
`define APB4_NPU__SNAPSHOT_PAYLOAD_WIDTH 672
`define APB4_NPU__SNAPSHOT_PAYLOAD_JOB_ID 0
`define APB4_NPU__SNAPSHOT_PAYLOAD_COUNTERS 32
`define APB4_NPU__EPOCH_WIDTH 4
// verilog_format: on
`endif
+690
View File
@@ -0,0 +1,690 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
// NPU Phase 2 PCLK register shell: the complete APB4 register ABI, the launch
// slot, the result/IRQ mirror, and the PERF snapshot bank. Phase 2 compiles in
// no executable implementation: STATUS.READY is always 0, CONTROL.START always
// completes with PSLVERR and no side effect, and hardware terminal events
// never occur (only IRQ_TEST sets IRQ_STATE bits). The launch slot and result
// mirror logic exists and participates in the lifecycle protocol so the
// Phase 3 execution path drops in without a shell redesign.
`include "npu_define.svh"
module npu_reg #(
parameter int unsigned LaunchPayloadWidth = `APB4_NPU__LAUNCH_PAYLOAD_WIDTH,
parameter int unsigned ResultPayloadWidth = `APB4_NPU__RESULT_PAYLOAD_WIDTH,
parameter int unsigned SnapshotPayloadWidth = `APB4_NPU__SNAPSHOT_PAYLOAD_WIDTH
) (
// verilog_format: off -- preserve the APB, lifecycle, and mailbox boundary columns
input logic clk_i,
input logic rst_n_i,
apb4_if.slave apb4,
input logic [1:0] resource_owner_i,
input logic resource_owner_lock_i,
input logic resource_quiesce_i,
input logic resource_reset_i,
input logic hp_busy_i,
input logic hp_draining_i,
input logic clock_paused_i,
input logic link_up_i,
input logic flush_active_i,
input logic [31:0] recovery_generation_i,
output logic launch_req_valid_o,
input logic launch_req_ready_i,
output logic [LaunchPayloadWidth-1:0] launch_req_payload_o,
input logic result_valid_i,
output logic result_ready_o,
input logic [ResultPayloadWidth-1:0] result_payload_i,
output logic snapshot_req_valid_o,
input logic snapshot_req_ready_i,
input logic snapshot_resp_valid_i,
output logic snapshot_resp_ready_o,
input logic [SnapshotPayloadWidth-1:0] snapshot_resp_payload_i,
output logic abort_o,
output logic soft_reset_o,
output logic idle_o,
output logic irq_o
// verilog_format: on
);
// Phase 2 truthfulness: with no executable implementation compiled in,
// READY can never assert and START can never be accepted.
localparam logic ExecutionReady = 1'b0;
localparam int unsigned CounterBankWidth = 8 * `APB4_NPU__PERF_COUNTER_COUNT * 8;
logic s_apb4_ready_d, s_apb4_ready_q;
logic s_access_seen_d, s_access_seen_q;
logic [31:0] s_apb4_rdata_d, s_apb4_rdata_q;
logic s_apb4_resp_err_d, s_apb4_resp_err_q;
logic [2:0] s_irq_state_d, s_irq_state_q;
logic [2:0] s_irq_en_d, s_irq_en_q;
logic [31:0] s_job_base_d, s_job_base_q;
logic [15:0] s_job_count_d, s_job_count_q;
logic [31:0] s_job_id_d, s_job_id_q;
logic [31:0] s_timeout_d, s_timeout_q;
logic s_launch_busy_d, s_launch_busy_q;
logic s_launch_req_valid_d, s_launch_req_valid_q;
logic s_result_valid_d, s_result_valid_q;
logic [31:0] s_result_job_id_d, s_result_job_id_q;
logic [2:0] s_result_code_d, s_result_code_q;
logic [31:0] s_completed_d, s_completed_q;
logic [31:0] s_fault_desc_d, s_fault_desc_q;
logic [3:0] s_fault_code_d, s_fault_code_q;
logic [31:0] s_fault_addr_d, s_fault_addr_q;
logic [31:0] s_fault_info_d, s_fault_info_q;
logic s_snap_req_valid_d, s_snap_req_valid_q;
logic s_snap_busy_d, s_snap_busy_q;
logic s_snap_valid_d, s_snap_valid_q;
logic [CounterBankWidth-1:0] s_perf_bank_d, s_perf_bank_q;
logic [31:0] s_perf_job_id_d, s_perf_job_id_q;
logic [31:0] s_perf_generation_d, s_perf_generation_q;
logic s_req_accept;
logic s_write;
logic [11:0] s_offset;
logic [31:0] s_write_value;
logic [31:0] s_read_data;
logic s_read_valid;
logic s_read_err;
logic s_write_err;
logic s_perf_range;
logic [ 5:0] s_perf_word;
logic [ 9:0] s_perf_bit;
logic [ 2:0] s_irq_clear;
logic [ 2:0] s_irq_test;
logic [ 2:0] s_result_irq_events;
logic s_cmd_start;
logic s_cmd_abort;
logic s_cmd_soft_reset;
logic s_cmd_snapshot;
logic s_result_accept;
logic s_snap_resp_accept;
logic s_ready;
logic s_shell_busy;
`ifndef SYNTHESIS
initial begin
if ((LaunchPayloadWidth != 128) || (ResultPayloadWidth != 170) ||
(SnapshotPayloadWidth != CounterBankWidth + 32)) begin
$fatal(1, "npu_reg: payload widths must match the npu_define.svh layouts");
end
end
`endif
assign s_req_accept = apb4.psel && apb4.penable && !s_access_seen_q;
assign s_write = s_req_accept && apb4.pwrite;
assign s_offset = apb4.paddr[11:0];
assign s_write_value = apb4.pwdata;
assign apb4.pready = s_apb4_ready_q;
assign apb4.prdata = s_apb4_rdata_q;
assign apb4.pslverr = s_apb4_resp_err_q;
assign s_shell_busy = s_launch_busy_q || hp_busy_i;
// READY also requires a reconciled link, no resource/reset/pause request, no
// pending terminal IRQ, and no active snapshot handshake; at Phase 2 the
// missing executable implementation keeps it permanently low.
assign s_ready = ExecutionReady && link_up_i && !s_launch_busy_q && !hp_busy_i &&
!hp_draining_i && !resource_quiesce_i && !resource_reset_i &&
!clock_paused_i && (s_irq_state_q == 3'd0) && !s_snap_busy_q &&
!s_snap_req_valid_q;
assign launch_req_valid_o = s_launch_req_valid_q;
assign launch_req_payload_o = {16'd0, s_job_count_q, s_timeout_q, s_job_id_q, s_job_base_q};
assign result_ready_o = 1'b1;
assign s_result_accept = result_valid_i && result_ready_o;
assign s_result_irq_events = s_result_accept ?
result_payload_i[`APB4_NPU__RESULT_PAYLOAD_IRQ_EVENTS+:3] : 3'd0;
assign snapshot_req_valid_o = s_snap_req_valid_q;
assign snapshot_resp_ready_o = s_snap_busy_q;
assign s_snap_resp_accept = snapshot_resp_valid_i && snapshot_resp_ready_o;
assign abort_o = s_cmd_abort;
assign soft_reset_o = s_cmd_soft_reset;
assign idle_o = !s_launch_busy_q && !s_launch_req_valid_q && !s_snap_busy_q &&
!s_snap_req_valid_q;
assign irq_o = (s_irq_state_q & s_irq_en_q) != 3'd0;
always_comb begin
s_perf_range = (s_offset >= `APB4_NPU__PERF_ACTIVE_CYCLES_LO) &&
(s_offset <= `APB4_NPU__PERF_RETIRED_DESCRIPTORS_HI) &&
(s_offset[1:0] == 2'b00);
s_perf_word = s_offset[7:2] - 6'h20;
s_perf_bit = {s_perf_word[4:0], 5'b00000};
end
always_comb begin
s_read_data = 32'd0;
s_read_valid = 1'b1;
if (s_perf_range) begin
s_read_data = s_perf_bank_q[s_perf_bit+:32];
end else begin
unique case (s_offset)
`APB4_NPU__IP_ID: s_read_data = `APB4_NPU__IP_ID_VALUE;
`APB4_NPU__IP_VERSION: s_read_data = `APB4_NPU__IP_VERSION_VALUE;
`APB4_NPU__CAPABILITY: s_read_data = `APB4_NPU__CAPABILITY_P2;
`APB4_NPU__STATUS:
s_read_data = {
26'd0, s_result_valid_q, !link_up_i, clock_paused_i, hp_draining_i, s_shell_busy, s_ready
};
`APB4_NPU__IRQ_STATE: s_read_data = {29'd0, s_irq_state_q};
`APB4_NPU__IRQ_ENABLE: s_read_data = {29'd0, s_irq_en_q};
`APB4_NPU__JOB_BASE: s_read_data = s_job_base_q;
`APB4_NPU__JOB_COUNT: s_read_data = {16'd0, s_job_count_q};
`APB4_NPU__JOB_ID: s_read_data = s_job_id_q;
`APB4_NPU__TIMEOUT_CYCLES: s_read_data = s_timeout_q;
`APB4_NPU__RESULT_JOB_ID: s_read_data = s_result_job_id_q;
`APB4_NPU__RESULT_CODE: s_read_data = {29'd0, s_result_code_q};
`APB4_NPU__COMPLETED_DESCRIPTORS: s_read_data = s_completed_q;
`APB4_NPU__FAULT_DESCRIPTOR: s_read_data = s_fault_desc_q;
`APB4_NPU__FAULT_CODE: s_read_data = {28'd0, s_fault_code_q};
`APB4_NPU__FAULT_ADDRESS: s_read_data = s_fault_addr_q;
`APB4_NPU__FAULT_INFO: s_read_data = s_fault_info_q;
`APB4_NPU__NUMERIC_PROFILE: s_read_data = `APB4_NPU__NUMERIC_PROFILE_VALUE;
// Phase 2 advertises no instantiated storage, compute, or operators.
`APB4_NPU__LOCAL_BYTES: s_read_data = 32'd0;
`APB4_NPU__MAC_CONFIG: s_read_data = 32'd0;
`APB4_NPU__MAX_K_SLICE: s_read_data = 32'd0;
`APB4_NPU__MAX_DIMENSION: s_read_data = 32'd0;
`APB4_NPU__OP_CAPABILITY: s_read_data = 32'd0;
`APB4_NPU__OWNER_STATUS:
s_read_data = {
21'd0, resource_reset_i, resource_quiesce_i, resource_owner_lock_i, 6'd0, resource_owner_i
};
`APB4_NPU__RECOVERY_GENERATION: s_read_data = recovery_generation_i;
`APB4_NPU__PERF_STATUS: s_read_data = {30'd0, s_snap_valid_q, s_snap_busy_q};
`APB4_NPU__PERF_JOB_ID: s_read_data = s_perf_job_id_q;
`APB4_NPU__PERF_GENERATION: s_read_data = s_perf_generation_q;
`APB4_NPU__DESCRIPTOR_BYTES: s_read_data = `APB4_NPU__DESCRIPTOR_BYTES_VALUE;
`APB4_NPU__CONTROL, `APB4_NPU__IRQ_TEST, `APB4_NPU__PERF_CONTROL: begin
s_read_valid = 1'b0;
end
default: s_read_valid = 1'b0;
endcase
end
end
always_comb begin
s_read_err = (s_offset[1:0] != 2'b00) || !s_read_valid;
end
always_comb begin
s_write_err = 1'b0;
s_irq_clear = 3'd0;
s_irq_test = 3'd0;
s_cmd_start = 1'b0;
s_cmd_abort = 1'b0;
s_cmd_soft_reset = 1'b0;
s_cmd_snapshot = 1'b0;
if (s_write) begin
if ((s_offset[1:0] != 2'b00) || (apb4.pstrb != 4'hf)) begin
s_write_err = 1'b1;
end else begin
unique case (s_offset)
`APB4_NPU__CONTROL: begin
unique case (s_write_value)
32'h0000_0001: begin
// An unavailable START is a local APB error, not a job fault.
if (!s_ready) begin
s_write_err = 1'b1;
end else begin
s_cmd_start = 1'b1;
end
end
32'h0000_0002: begin
// ABORT on an idle NPU is a successful no-op.
if (s_shell_busy) begin
s_cmd_abort = 1'b1;
end
end
32'h0000_0004: begin
if (s_shell_busy || hp_draining_i || s_snap_busy_q || s_snap_req_valid_q) begin
s_write_err = 1'b1;
end else begin
s_cmd_soft_reset = 1'b1;
end
end
default: s_write_err = 1'b1;
endcase
end
`APB4_NPU__IRQ_STATE: begin
if (s_write_value[31:3] != 29'd0) begin
s_write_err = 1'b1;
end else begin
s_irq_clear = s_write_value[2:0];
end
end
`APB4_NPU__IRQ_ENABLE: begin
if (s_write_value[31:3] != 29'd0) begin
s_write_err = 1'b1;
end
end
`APB4_NPU__IRQ_TEST: begin
if (s_write_value[31:3] != 29'd0) begin
s_write_err = 1'b1;
end else begin
s_irq_test = s_write_value[2:0];
end
end
`APB4_NPU__JOB_BASE: begin
if (s_shell_busy) begin
s_write_err = 1'b1;
end
end
`APB4_NPU__JOB_COUNT: begin
if (s_shell_busy || (s_write_value[31:16] != 16'd0)) begin
s_write_err = 1'b1;
end
end
`APB4_NPU__JOB_ID: begin
if (s_shell_busy) begin
s_write_err = 1'b1;
end
end
`APB4_NPU__TIMEOUT_CYCLES: begin
if (s_shell_busy || (s_write_value == 32'd0)) begin
s_write_err = 1'b1;
end
end
`APB4_NPU__PERF_CONTROL: begin
if ((s_write_value != 32'h0000_0001) || s_snap_busy_q || s_snap_req_valid_q ||
!link_up_i || resource_quiesce_i || resource_reset_i) begin
s_write_err = 1'b1;
end else begin
s_cmd_snapshot = 1'b1;
end
end
default: s_write_err = 1'b1;
endcase
end
end
end
always_comb begin
s_apb4_ready_d = s_req_accept;
s_access_seen_d = s_access_seen_q;
if (!apb4.psel || !apb4.penable) begin
s_access_seen_d = 1'b0;
end else if (s_req_accept) begin
s_access_seen_d = 1'b1;
end
s_apb4_rdata_d = s_apb4_rdata_q;
s_apb4_resp_err_d = s_req_accept && (s_write ? s_write_err : s_read_err);
if (s_req_accept) begin
s_apb4_rdata_d = (!s_write && !s_read_err) ? s_read_data : 32'd0;
end
// Hardware/test set wins a same-cycle W1C clear; reads never clear.
s_irq_state_d = (s_irq_state_q & ~s_irq_clear) | s_irq_test | s_result_irq_events;
s_irq_en_d = s_irq_en_q;
s_job_base_d = s_job_base_q;
s_job_count_d = s_job_count_q;
s_job_id_d = s_job_id_q;
s_timeout_d = s_timeout_q;
s_launch_busy_d = s_launch_busy_q;
s_launch_req_valid_d = s_launch_req_valid_q;
// An accepted START latches the launch slot and raises the mailbox
// request; the slot releases only on terminal result or epoch recovery.
// Unreachable at Phase 2 because START never passes the READY check.
if (s_cmd_start) begin
s_launch_busy_d = 1'b1;
s_launch_req_valid_d = 1'b1;
s_result_valid_d = 1'b0;
end
if (s_launch_req_valid_q && launch_req_ready_i) begin
s_launch_req_valid_d = 1'b0;
end
s_result_valid_d = s_result_valid_q;
s_result_job_id_d = s_result_job_id_q;
s_result_code_d = s_result_code_q;
s_completed_d = s_completed_q;
s_fault_desc_d = s_fault_desc_q;
s_fault_code_d = s_fault_code_q;
s_fault_addr_d = s_fault_addr_q;
s_fault_info_d = s_fault_info_q;
// Terminal result mirror; no result source exists at Phase 2.
if (s_result_accept) begin
s_launch_busy_d = 1'b0;
s_result_valid_d = 1'b1;
s_result_job_id_d = result_payload_i[`APB4_NPU__RESULT_PAYLOAD_JOB_ID+:32];
s_result_code_d = result_payload_i[`APB4_NPU__RESULT_PAYLOAD_CODE+:3];
s_completed_d = result_payload_i[`APB4_NPU__RESULT_PAYLOAD_COMPLETED+:32];
s_fault_desc_d = result_payload_i[`APB4_NPU__RESULT_PAYLOAD_FAULT_DESC+:32];
s_fault_code_d = result_payload_i[`APB4_NPU__RESULT_PAYLOAD_FAULT_CODE+:4];
s_fault_addr_d = result_payload_i[`APB4_NPU__RESULT_PAYLOAD_FAULT_ADDR+:32];
s_fault_info_d = result_payload_i[`APB4_NPU__RESULT_PAYLOAD_FAULT_INFO+:32];
end
s_snap_req_valid_d = s_snap_req_valid_q;
s_snap_busy_d = s_snap_busy_q;
s_snap_valid_d = s_snap_valid_q;
s_perf_bank_d = s_perf_bank_q;
s_perf_job_id_d = s_perf_job_id_q;
s_perf_generation_d = s_perf_generation_q;
if (s_cmd_snapshot) begin
s_snap_req_valid_d = 1'b1;
s_snap_busy_d = 1'b1;
s_snap_valid_d = 1'b0;
end
if (s_snap_req_valid_q && snapshot_req_ready_i) begin
s_snap_req_valid_d = 1'b0;
end
// Capture completion replaces the whole bank and job/generation together,
// so a low/high counter tear can never be observed.
if (s_snap_resp_accept) begin
s_perf_bank_d = snapshot_resp_payload_i[SnapshotPayloadWidth-1:32];
s_perf_job_id_d = snapshot_resp_payload_i[31:0];
s_perf_generation_d = recovery_generation_i;
s_snap_busy_d = 1'b0;
s_snap_valid_d = 1'b1;
end
// Epoch recovery releases the launch slot and cancels a pending capture.
if (flush_active_i) begin
s_launch_busy_d = 1'b0;
s_launch_req_valid_d = 1'b0;
s_snap_req_valid_d = 1'b0;
s_snap_busy_d = 1'b0;
s_snap_valid_d = 1'b0;
end
if (s_write && !s_write_err) begin
unique case (s_offset)
`APB4_NPU__IRQ_ENABLE: s_irq_en_d = s_write_value[2:0];
`APB4_NPU__JOB_BASE: s_job_base_d = s_write_value;
`APB4_NPU__JOB_COUNT: s_job_count_d = s_write_value[15:0];
`APB4_NPU__JOB_ID: s_job_id_d = s_write_value;
`APB4_NPU__TIMEOUT_CYCLES: s_timeout_d = s_write_value;
default: begin
end
endcase
end
// Idle SOFT_RESET clears programmable, IRQ, and result state; the CDC
// reconciliation it triggers runs before READY could return.
if (s_cmd_soft_reset) begin
s_irq_state_d = 3'd0;
s_irq_en_d = 3'd0;
s_job_base_d = 32'd0;
s_job_count_d = 16'd0;
s_job_id_d = 32'd0;
s_timeout_d = 32'd0;
s_launch_busy_d = 1'b0;
s_launch_req_valid_d = 1'b0;
s_result_valid_d = 1'b0;
s_result_job_id_d = 32'd0;
s_result_code_d = 3'd0;
s_completed_d = 32'd0;
s_fault_desc_d = `APB4_NPU__FAULT_DESCRIPTOR_RESET;
s_fault_code_d = 4'd0;
s_fault_addr_d = 32'd0;
s_fault_info_d = 32'd0;
s_snap_req_valid_d = 1'b0;
s_snap_busy_d = 1'b0;
s_snap_valid_d = 1'b0;
s_perf_bank_d = '0;
s_perf_job_id_d = 32'd0;
s_perf_generation_d = 32'd0;
end
end
dffr #(
.DATA_WIDTH(1)
) u_apb4_ready (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_apb4_ready_d),
.dat_o (s_apb4_ready_q)
);
dffr #(
.DATA_WIDTH(1)
) u_access_seen (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_access_seen_d),
.dat_o (s_access_seen_q)
);
dffr #(
.DATA_WIDTH(32)
) u_apb4_rdata (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_apb4_rdata_d),
.dat_o (s_apb4_rdata_q)
);
dffr #(
.DATA_WIDTH(1)
) u_apb4_resp_err (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_apb4_resp_err_d),
.dat_o (s_apb4_resp_err_q)
);
dffr #(
.DATA_WIDTH(3)
) u_irq_state (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_irq_state_d),
.dat_o (s_irq_state_q)
);
dffr #(
.DATA_WIDTH(3)
) u_irq_enable (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_irq_en_d),
.dat_o (s_irq_en_q)
);
dffr #(
.DATA_WIDTH(32)
) u_job_base (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_job_base_d),
.dat_o (s_job_base_q)
);
dffr #(
.DATA_WIDTH(16)
) u_job_count (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_job_count_d),
.dat_o (s_job_count_q)
);
dffr #(
.DATA_WIDTH(32)
) u_job_id (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_job_id_d),
.dat_o (s_job_id_q)
);
dffr #(
.DATA_WIDTH(32)
) u_timeout (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_timeout_d),
.dat_o (s_timeout_q)
);
dffr #(
.DATA_WIDTH(1)
) u_launch_busy (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_launch_busy_d),
.dat_o (s_launch_busy_q)
);
dffr #(
.DATA_WIDTH(1)
) u_launch_req_valid (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_launch_req_valid_d),
.dat_o (s_launch_req_valid_q)
);
dffr #(
.DATA_WIDTH(1)
) u_result_valid (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_result_valid_d),
.dat_o (s_result_valid_q)
);
dffr #(
.DATA_WIDTH(32)
) u_result_job_id (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_result_job_id_d),
.dat_o (s_result_job_id_q)
);
dffr #(
.DATA_WIDTH(3)
) u_result_code (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_result_code_d),
.dat_o (s_result_code_q)
);
dffr #(
.DATA_WIDTH(32)
) u_completed (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_completed_d),
.dat_o (s_completed_q)
);
dffrc #(
.DATA_WIDTH(32),
.RESET_VAL (`APB4_NPU__FAULT_DESCRIPTOR_RESET)
) u_fault_desc (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_fault_desc_d),
.dat_o (s_fault_desc_q)
);
dffr #(
.DATA_WIDTH(4)
) u_fault_code (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_fault_code_d),
.dat_o (s_fault_code_q)
);
dffr #(
.DATA_WIDTH(32)
) u_fault_addr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_fault_addr_d),
.dat_o (s_fault_addr_q)
);
dffr #(
.DATA_WIDTH(32)
) u_fault_info (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_fault_info_d),
.dat_o (s_fault_info_q)
);
dffr #(
.DATA_WIDTH(1)
) u_snap_req_valid (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_snap_req_valid_d),
.dat_o (s_snap_req_valid_q)
);
dffr #(
.DATA_WIDTH(1)
) u_snap_busy (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_snap_busy_d),
.dat_o (s_snap_busy_q)
);
dffr #(
.DATA_WIDTH(1)
) u_snap_valid (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_snap_valid_d),
.dat_o (s_snap_valid_q)
);
dffr #(
.DATA_WIDTH(CounterBankWidth)
) u_perf_bank (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_bank_d),
.dat_o (s_perf_bank_q)
);
dffr #(
.DATA_WIDTH(32)
) u_perf_job_id (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_job_id_d),
.dat_o (s_perf_job_id_q)
);
dffr #(
.DATA_WIDTH(32)
) u_perf_generation (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_generation_d),
.dat_o (s_perf_generation_q)
);
`ifndef SV_ASSRT_DISABLE
logic s_apb_setup_q;
logic s_apb_wait_q;
logic s_launch_wait_q;
logic s_snap_req_wait_q;
logic s_req_cancel_q;
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_apb_setup_q <= 1'b0;
s_apb_wait_q <= 1'b0;
s_launch_wait_q <= 1'b0;
s_snap_req_wait_q <= 1'b0;
s_req_cancel_q <= 1'b0;
end else begin
s_apb_setup_q <= apb4.psel && !apb4.penable;
s_apb_wait_q <= apb4.psel && apb4.penable && !apb4.pready;
s_launch_wait_q <= launch_req_valid_o && !launch_req_ready_i;
s_snap_req_wait_q <= snapshot_req_valid_o && !snapshot_req_ready_i;
s_req_cancel_q <= flush_active_i || s_cmd_soft_reset;
// APB payload is stable from setup through every wait state.
if (s_apb_setup_q || s_apb_wait_q) begin
assert (apb4.psel && $stable(
apb4.paddr
) && $stable(
apb4.pwrite
) && $stable(
apb4.pwdata
) && $stable(
apb4.pstrb
));
assert (s_apb_setup_q || apb4.penable);
end
// START can never be accepted without READY (never, at Phase 2).
if (s_cmd_start) begin
assert (s_ready);
end
// irq_o is exactly the enabled pending events.
assert (irq_o == ((s_irq_state_q & s_irq_en_q) != 3'd0));
// Mailbox requests hold valid and payload until accepted; only an epoch
// flush or a soft reset may withdraw them.
if (s_launch_wait_q) begin
assert ((launch_req_valid_o && $stable(launch_req_payload_o)) || s_req_cancel_q);
end
if (s_snap_req_wait_q) begin
assert (snapshot_req_valid_o || s_req_cancel_q);
end
end
end
`endif
endmodule
+9
View File
@@ -507,6 +507,15 @@
"public": true, "public": true,
"user_access": "none" "user_access": "none"
}, },
{
"symbol": "APB4_NPU",
"base": "0x1001B000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "none"
},
{ {
"symbol": "APB4_PWM", "symbol": "APB4_PWM",
"base": "0x20002000", "base": "0x20002000",
+4
View File
@@ -179,6 +179,10 @@
/ip/multimedia/jpeg_dma.sv /ip/multimedia/jpeg_dma.sv
/ip/multimedia/jpeg_reg.sv /ip/multimedia/jpeg_reg.sv
/ip/multimedia/apb4_jpeg.sv /ip/multimedia/apb4_jpeg.sv
/ip/multimedia/npu_reg.sv
/ip/multimedia/npu_control_cdc.sv
/ip/multimedia/npu_core.sv
/ip/multimedia/apb4_npu.sv
/ip/peripheral/timer_core.sv /ip/peripheral/timer_core.sv
/ip/peripheral/timer_reg.sv /ip/peripheral/timer_reg.sv
/ip/peripheral/apb4_timer.sv /ip/peripheral/apb4_timer.sv
@@ -130,6 +130,65 @@
{"name": "ga2d_flush", "source": "hp", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_ga2d_flush_sync"}, {"name": "ga2d_flush", "source": "hp", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_ga2d_flush_sync"},
{"name": "ga2d_master_idle", "source": "hp", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_ga2d_master_idle_sync"}, {"name": "ga2d_master_idle", "source": "hp", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_ga2d_master_idle_sync"},
{"name": "ga2d_block_ack", "source": "hp", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_ga2d_block_ack_sync"}, {"name": "ga2d_block_ack", "source": "hp", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_ga2d_block_ack_sync"},
{
"name": "npu_launch_mailbox",
"source": "pclk",
"destination": "hp",
"primitive": "async_reqack",
"path": "rtl/ip/multimedia/npu_control_cdc.sv",
"instance": "u_launch_mailbox",
"unilateral_reset": {
"source_reset": "flush_mailbox_via_reset_barrier",
"destination_reset": "flush_mailbox_via_reset_barrier"
}
},
{
"name": "npu_result_mailbox",
"source": "hp",
"destination": "pclk",
"primitive": "async_reqack",
"path": "rtl/ip/multimedia/npu_control_cdc.sv",
"instance": "u_result_mailbox",
"unilateral_reset": {
"source_reset": "flush_mailbox_via_reset_barrier",
"destination_reset": "flush_mailbox_via_reset_barrier"
}
},
{
"name": "npu_snapshot_req_mailbox",
"source": "pclk",
"destination": "hp",
"primitive": "async_reqack",
"path": "rtl/ip/multimedia/npu_control_cdc.sv",
"instance": "u_snapshot_req_mailbox",
"unilateral_reset": {
"source_reset": "flush_mailbox_via_reset_barrier",
"destination_reset": "flush_mailbox_via_reset_barrier"
}
},
{
"name": "npu_snapshot_resp_mailbox",
"source": "hp",
"destination": "pclk",
"primitive": "async_reqack",
"path": "rtl/ip/multimedia/npu_control_cdc.sv",
"instance": "u_snapshot_resp_mailbox",
"unilateral_reset": {
"source_reset": "flush_mailbox_via_reset_barrier",
"destination_reset": "flush_mailbox_via_reset_barrier"
}
},
{"name": "npu_hp_reset", "source": "hp", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/ip/multimedia/npu_control_cdc.sv", "instance": "u_hp_reset_sync"},
{"name": "npu_hp_flush", "source": "hp", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/ip/multimedia/npu_control_cdc.sv", "instance": "u_hp_flush_sync"},
{"name": "npu_epoch_req", "source": "pclk", "destination": "hp", "primitive": "cdc_sync", "path": "rtl/ip/multimedia/npu_control_cdc.sv", "instance": "u_epoch_req_sync"},
{"name": "npu_epoch_ack", "source": "hp", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/ip/multimedia/npu_control_cdc.sv", "instance": "u_epoch_ack_sync"},
{"name": "npu_quiesce_req", "source": "pclk", "destination": "hp", "primitive": "cdc_sync", "path": "rtl/ip/multimedia/npu_control_cdc.sv", "instance": "u_quiesce_req_sync"},
{"name": "npu_quiesce_ack", "source": "hp", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/ip/multimedia/npu_control_cdc.sv", "instance": "u_quiesce_ack_sync"},
{"name": "npu_hp_busy", "source": "hp", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/ip/multimedia/npu_control_cdc.sv", "instance": "u_hp_busy_sync"},
{"name": "npu_hp_draining", "source": "hp", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/ip/multimedia/npu_control_cdc.sv", "instance": "u_hp_draining_sync"},
{"name": "npu_hp_pause_active", "source": "hp", "destination": "pclk", "primitive": "cdc_sync", "path": "rtl/ip/multimedia/npu_control_cdc.sv", "instance": "u_hp_pause_active_sync"},
{"name": "npu_source_idle", "source": "pclk", "destination": "hp", "primitive": "cdc_sync", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_npu_source_idle_sync"},
{"name": "npu_source_quiesced", "source": "pclk", "destination": "hp", "primitive": "cdc_sync", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_npu_source_quiesced_sync"},
{"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": "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"}, {"name": "sdram_data", "source": "hp", "destination": "memory", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_target_cdc"},
{"name": "serial_memory_data", "source": "hp", "destination": "memory", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_target_cdc"}, {"name": "serial_memory_data", "source": "hp", "destination": "memory", "primitive": "axi4_async_bridge", "path": "rtl/mini/top/soc_data_plane.sv", "instance": "u_target_cdc"},
@@ -30,6 +30,7 @@ DATA_MASTER_NAMES = (
"jpeg", "jpeg",
"ext_h", "ext_h",
"ga2d", "ga2d",
"npu",
) )
DATA_TARGET_NAMES = ("sram", "sdram", "qpi", "opi", "xpi") DATA_TARGET_NAMES = ("sram", "sdram", "qpi", "opi", "xpi")
COMPATIBILITY_IRQ_BINDINGS = ( COMPATIBILITY_IRQ_BINDINGS = (
+25 -1
View File
@@ -5,7 +5,7 @@
"irq_groups": [ "irq_groups": [
{ {
"name": "apb4_periph", "name": "apb4_periph",
"width": 25, "width": 26,
"top_signal": "s_apb4_periph_irq" "top_signal": "s_apb4_periph_irq"
}, },
{ {
@@ -294,6 +294,15 @@
"core_bit": 32, "core_bit": 32,
"signal": "resource_irq_lp_i[7]", "signal": "resource_irq_lp_i[7]",
"user_visible": false "user_visible": false
},
{
"name": "npu",
"description": "NPU shell completion and error interrupt",
"group": "apb4_periph",
"group_bit": 25,
"core_bit": 33,
"signal": "resource_irq_lp_i[8]",
"user_visible": false
} }
], ],
"wake_sources": [ "wake_sources": [
@@ -426,6 +435,14 @@
"write_targets": ["sram", "sdram", "qpi", "opi"], "write_targets": ["sram", "sdram", "qpi", "opi"],
"allow_instruction": false, "allow_instruction": false,
"require_noncacheable": true "require_noncacheable": true
},
{
"index": 9,
"name": "npu",
"read_targets": ["sram", "sdram", "qpi", "opi", "xpi"],
"write_targets": ["sram", "sdram", "qpi", "opi"],
"allow_instruction": false,
"require_noncacheable": true
} }
], ],
"apb4_periph_targets": [ "apb4_periph_targets": [
@@ -640,6 +657,13 @@
"timed_interface": "u_ga2d_apb4_if", "timed_interface": "u_ga2d_apb4_if",
"pure_interface": "u_ga2d_apb4_pure_if", "pure_interface": "u_ga2d_apb4_pure_if",
"region": "APB4_GA2D" "region": "APB4_GA2D"
},
{
"slot": 29,
"name": "npu",
"timed_interface": "u_npu_apb4_if",
"pure_interface": "u_npu_apb4_pure_if",
"region": "APB4_NPU"
} }
], ],
"apb4_system_targets": [ "apb4_system_targets": [
+41 -3
View File
@@ -22,11 +22,13 @@ module apb4_periph #(
input logic clk_ulpi_i, input logic clk_ulpi_i,
input logic clk_mem_i, input logic clk_mem_i,
input logic rst_mem_n_i, input logic rst_mem_n_i,
input logic clk_hp_i,
input logic rst_hp_n_i,
input logic debug_halted_i, input logic debug_halted_i,
input logic timebase_tick_i, input logic timebase_tick_i,
input logic ext_h_hp_irq_i, input logic ext_h_hp_irq_i,
input logic [7:0] resource_irq_lp_i, input logic [8:0] resource_irq_lp_i,
input logic [7:0] resource_irq_hp_i, input logic [8:0] resource_irq_hp_i,
input logic [1:0] apu_owner_i, input logic [1:0] apu_owner_i,
input logic apu_owner_lock_i, input logic apu_owner_lock_i,
input logic apu_quiesce_i, input logic apu_quiesce_i,
@@ -42,6 +44,12 @@ module apb4_periph #(
input logic ga2d_bridge_clear_busy_i, input logic ga2d_bridge_clear_busy_i,
input logic [7:0] ga2d_bridge_epoch_i, input logic [7:0] ga2d_bridge_epoch_i,
input logic ga2d_data_ready_i, input logic ga2d_data_ready_i,
input logic [1:0] npu_owner_i,
input logic npu_owner_lock_i,
input logic npu_quiesce_i,
input logic npu_reset_i,
input logic npu_hp_block_new_i,
input logic npu_hp_flush_i,
input logic [1:0] mem_pad_mode_i, input logic [1:0] mem_pad_mode_i,
axi4_if.slave cfg_axi4, axi4_if.slave cfg_axi4,
axi4_if.slave psram_axi4, axi4_if.slave psram_axi4,
@@ -65,6 +73,7 @@ module apb4_periph #(
axi4_if.master apu_axi4, axi4_if.master apu_axi4,
axi4_if.master jpeg_axi4, axi4_if.master jpeg_axi4,
axi4_if.master ga2d_axi4, axi4_if.master ga2d_axi4,
axi4_if.master npu_axi4,
sysctrl_if.dut sysctrl, sysctrl_if.dut sysctrl,
pll_ctrl_if.sysctrl pll_ctrl, pll_ctrl_if.sysctrl pll_ctrl,
clock_ctrl_if.sysctrl clock_ctrl, clock_ctrl_if.sysctrl clock_ctrl,
@@ -89,7 +98,12 @@ module apb4_periph #(
output logic jpeg_idle_o, output logic jpeg_idle_o,
output logic ga2d_idle_o, output logic ga2d_idle_o,
output logic ga2d_core_safe_idle_o, output logic ga2d_core_safe_idle_o,
output logic [7:0] resource_irq_raw_o, output logic npu_idle_o,
output logic npu_block_ack_o,
output logic npu_hp_pause_ack_o,
output logic npu_hp_flush_busy_o,
output logic npu_hp_idle_o,
output logic [8:0] resource_irq_raw_o,
output logic [`SOC_IRQ_APB4_PERIPH_WIDTH-1:0] irq_o output logic [`SOC_IRQ_APB4_PERIPH_WIDTH-1:0] irq_o
// verilog_format: on // verilog_format: on
); );
@@ -195,6 +209,7 @@ axi4_stream_if #(
logic s_i2s_rx_flush_busy; logic s_i2s_rx_flush_busy;
logic s_jpeg_irq_raw; logic s_jpeg_irq_raw;
logic s_ga2d_irq_raw; logic s_ga2d_irq_raw;
logic s_npu_irq_raw;
logic s_usb2_irq; logic s_usb2_irq;
logic s_tim0_irq, s_tim1_irq; logic s_tim0_irq, s_tim1_irq;
logic s_dvp_irq; logic s_dvp_irq;
@@ -253,6 +268,7 @@ axi4_stream_if #(
assign hp_machine_external_irq_o = s_hp_plic_context_irq[0]; assign hp_machine_external_irq_o = s_hp_plic_context_irq[0];
assign hp_supervisor_external_irq_o = s_hp_plic_context_irq[1]; assign hp_supervisor_external_irq_o = s_hp_plic_context_irq[1];
assign resource_irq_raw_o = { assign resource_irq_raw_o = {
s_npu_irq_raw,
s_ga2d_irq_raw, s_ga2d_irq_raw,
s_apu_irq_raw, s_apu_irq_raw,
s_jpeg_irq_raw, s_jpeg_irq_raw,
@@ -343,6 +359,7 @@ axi4_stream_if #(
s_hp_plic_source[9] = resource_irq_hp_i[5]; s_hp_plic_source[9] = resource_irq_hp_i[5];
s_hp_plic_source[10] = resource_irq_hp_i[6]; s_hp_plic_source[10] = resource_irq_hp_i[6];
s_hp_plic_source[11] = resource_irq_hp_i[7]; s_hp_plic_source[11] = resource_irq_hp_i[7];
s_hp_plic_source[12] = resource_irq_hp_i[8];
end end
`include "apb4_periph_irq_bindings.svh" `include "apb4_periph_irq_bindings.svh"
@@ -566,6 +583,27 @@ axi4_stream_if #(
.irq_o (s_ga2d_irq_raw) .irq_o (s_ga2d_irq_raw)
); );
apb4_npu u_apb4_npu (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.clk_hp_i (clk_hp_i),
.rst_hp_n_i (rst_hp_n_i),
.resource_owner_i (npu_owner_i),
.resource_owner_lock_i(npu_owner_lock_i),
.resource_quiesce_i (npu_quiesce_i),
.resource_reset_i (npu_reset_i),
.apb4 (u_npu_apb4_if),
.idle_o (npu_idle_o),
.block_ack_o (npu_block_ack_o),
.irq_o (s_npu_irq_raw),
.hp_block_new_i (npu_hp_block_new_i),
.hp_pause_ack_o (npu_hp_pause_ack_o),
.hp_flush_i (npu_hp_flush_i),
.hp_flush_busy_o (npu_hp_flush_busy_o),
.hp_idle_o (npu_hp_idle_o),
.npu_axi4 (npu_axi4)
);
apb4_sysctrl u_apb4_sysctrl ( apb4_sysctrl u_apb4_sysctrl (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
+24 -20
View File
@@ -23,9 +23,9 @@ module apb4_system (
input logic rst_aud_n_i, input logic rst_aud_n_i,
input logic debug_halted_i, input logic debug_halted_i,
input logic ext_h_data_idle_i, input logic ext_h_data_idle_i,
input logic [8:0] resource_idle_i, input logic [9:0] resource_idle_i,
input logic [8:0] resource_block_ack_i, input logic [9:0] resource_block_ack_i,
input logic [7:0] resource_irq_i, input logic [8:0] resource_irq_i,
input logic cache_request_i, input logic cache_request_i,
axi4_if.slave axi4, axi4_if.slave axi4,
axi4_if.master ext_h_axi4, axi4_if.master ext_h_axi4,
@@ -45,12 +45,12 @@ module apb4_system (
output logic ext_h_irq_raw_o, output logic ext_h_irq_raw_o,
output logic [1:0] ext_h_owner_o, output logic [1:0] ext_h_owner_o,
output logic cache_clean_o, output logic cache_clean_o,
output logic [8:0][1:0] resource_owner_o, output logic [9:0][1:0] resource_owner_o,
output logic [8:0] resource_owner_lock_o, output logic [9:0] resource_owner_lock_o,
output logic [8:0] resource_quiesce_o, output logic [9:0] resource_quiesce_o,
output logic [8:0] resource_reset_o, output logic [9:0] resource_reset_o,
output logic [7:0] resource_irq_lp_o, output logic [8:0] resource_irq_lp_o,
output logic [7:0] resource_irq_hp_o, output logic [8:0] resource_irq_hp_o,
output logic [`SOC_IRQ_APB4_SYSTEM_WIDTH-1:0] irq_o output logic [`SOC_IRQ_APB4_SYSTEM_WIDTH-1:0] irq_o
// verilog_format: on // verilog_format: on
); );
@@ -98,13 +98,13 @@ module apb4_system (
logic s_ext_h_irq; logic s_ext_h_irq;
logic s_ext_h_irq_raw; logic s_ext_h_irq_raw;
logic [ 1:0] unused_ext_h_owner; logic [ 1:0] unused_ext_h_owner;
logic [ 8:0] s_resource_irq; logic [ 9:0] s_resource_irq;
logic [ 8:0] s_resource_irq_lp; logic [ 9:0] s_resource_irq_lp;
logic [ 8:0] s_resource_irq_hp; logic [ 9:0] s_resource_irq_hp;
logic [ 8:0][1:0] s_resource_owner; logic [ 9:0][1:0] s_resource_owner;
logic [ 8:0] s_resource_owner_lock; logic [ 9:0] s_resource_owner_lock;
logic [ 8:0] s_resource_quiesce; logic [ 9:0] s_resource_quiesce;
logic [ 8:0] s_resource_reset; logic [ 9:0] s_resource_reset;
logic s_resource_fault_irq; logic s_resource_fault_irq;
logic s_cache_clean; logic s_cache_clean;
logic s_unused_resource; logic s_unused_resource;
@@ -113,7 +113,7 @@ module apb4_system (
`endif `endif
assign s_resource_irq = { assign s_resource_irq = {
resource_irq_i[7], resource_irq_i[6:5], s_ext_h_irq_raw, resource_irq_i[4:0] resource_irq_i[8], resource_irq_i[7], resource_irq_i[6:5], s_ext_h_irq_raw, resource_irq_i[4:0]
}; };
assign s_ext_h_irq = s_resource_irq_lp[5]; assign s_ext_h_irq = s_resource_irq_lp[5];
assign ext_h_irq_raw_o = s_ext_h_irq_raw; assign ext_h_irq_raw_o = s_ext_h_irq_raw;
@@ -123,8 +123,12 @@ module apb4_system (
assign resource_owner_lock_o = s_resource_owner_lock; assign resource_owner_lock_o = s_resource_owner_lock;
assign resource_quiesce_o = s_resource_quiesce; assign resource_quiesce_o = s_resource_quiesce;
assign resource_reset_o = s_resource_reset; assign resource_reset_o = s_resource_reset;
assign resource_irq_lp_o = {s_resource_irq_lp[8], s_resource_irq_lp[7:6], s_resource_irq_lp[4:0]}; assign resource_irq_lp_o = {
assign resource_irq_hp_o = {s_resource_irq_hp[8], s_resource_irq_hp[7:6], s_resource_irq_hp[4:0]}; s_resource_irq_lp[9], s_resource_irq_lp[8], s_resource_irq_lp[7:6], s_resource_irq_lp[4:0]
};
assign resource_irq_hp_o = {
s_resource_irq_hp[9], s_resource_irq_hp[8], s_resource_irq_hp[7:6], s_resource_irq_hp[4:0]
};
`ifdef PDK_IHP130 `ifdef PDK_IHP130
localparam logic [31:0] ARCHINFO_TECHNOLOGY = 32'h0201_0082; localparam logic [31:0] ARCHINFO_TECHNOLOGY = 32'h0201_0082;
@@ -236,7 +240,7 @@ module apb4_system (
apb4_crc u_apb4_crc (.apb4(u_crc_apb4_if)); apb4_crc u_apb4_crc (.apb4(u_crc_apb4_if));
resource_controller #( resource_controller #(
.ResourceCount(9) .ResourceCount(10)
) u_resource_controller ( ) u_resource_controller (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
+12 -12
View File
@@ -4,7 +4,7 @@
`include "mmap_define.svh" `include "mmap_define.svh"
module axi4_data_crossbar #( module axi4_data_crossbar #(
parameter int unsigned NumMasters = 9, parameter int unsigned NumMasters = 10,
parameter int unsigned NumTargets = 6, parameter int unsigned NumTargets = 6,
parameter int unsigned StarvationCycles = 256, parameter int unsigned StarvationCycles = 256,
parameter logic [NumMasters-1:0][NumTargets-2:0] ReadTargetMask = '1, parameter logic [NumMasters-1:0][NumTargets-2:0] ReadTargetMask = '1,
@@ -295,13 +295,13 @@ module axi4_data_crossbar #(
function automatic logic [CountWidth-1:0] master_read_limit(input int unsigned master); function automatic logic [CountWidth-1:0] master_read_limit(input int unsigned master);
if ((master <= 2) || (master == 7)) return CountWidth'(4); if ((master <= 2) || (master == 7)) return CountWidth'(4);
if ((master == 3) || (master == 4)) return CountWidth'(2); if ((master == 3) || (master == 4)) return CountWidth'(2);
if ((master == 5) || (master == 6) || (master == 8)) return CountWidth'(1); if ((master == 5) || (master == 6) || (master == 8) || (master == 9)) return CountWidth'(1);
return '0; return '0;
endfunction endfunction
function automatic logic [CountWidth-1:0] master_write_limit(input int unsigned master); function automatic logic [CountWidth-1:0] master_write_limit(input int unsigned master);
if ((master == 1) || (master == 2) || (master == 7)) return CountWidth'(2); if ((master == 1) || (master == 2) || (master == 7)) return CountWidth'(2);
if (((master >= 3) && (master <= 6)) || (master == 8)) return CountWidth'(1); if (((master >= 3) && (master <= 6)) || (master == 8) || (master == 9)) return CountWidth'(1);
return '0; return '0;
endfunction endfunction
@@ -321,15 +321,15 @@ module axi4_data_crossbar #(
input logic recovery); input logic recovery);
logic [3:0] base_priority; logic [3:0] base_priority;
unique case (master) unique case (master)
0, 1: base_priority = 4'd12; 0, 1: base_priority = 4'd12;
3, 4: base_priority = 4'd10; 3, 4: base_priority = 4'd10;
2, 6, 7, 8: base_priority = 4'd8; 2, 6, 7, 8, 9: base_priority = 4'd8;
5: base_priority = recovery ? 4'd15 : 4'd2; 5: base_priority = recovery ? 4'd15 : 4'd2;
default: base_priority = 4'd0; default: base_priority = 4'd0;
endcase endcase
if (recovery && (master == 5)) return 5'd31; if (recovery && (master == 5)) return 5'd31;
if (aged) return 5'd16; if (aged) return 5'd16;
if (master == 8) return {1'b0, base_priority}; if ((master == 8) || (master == 9)) return {1'b0, base_priority};
return {1'b0, (request_qos > base_priority) ? request_qos : base_priority}; return {1'b0, (request_qos > base_priority) ? request_qos : base_priority};
endfunction endfunction
@@ -660,7 +660,7 @@ module axi4_data_crossbar #(
if (s_read_grant_valid[target]) begin if (s_read_grant_valid[target]) begin
automatic logic [MasterWidth-1:0] owner = s_read_selected[target]; automatic logic [MasterWidth-1:0] owner = s_read_selected[target];
t_ar[target] = m_ar[owner]; t_ar[target] = m_ar[owner];
if (owner == MasterWidth'(8)) t_ar[target].qos = 4'd0; if ((owner == MasterWidth'(8)) || (owner == MasterWidth'(9))) t_ar[target].qos = 4'd0;
if ((s_read_fault_reason[owner] == 4'd0) || s_read_fault_accept[owner]) begin if ((s_read_fault_reason[owner] == 4'd0) || s_read_fault_accept[owner]) begin
t_arvalid[target] = m_arvalid[owner]; t_arvalid[target] = m_arvalid[owner];
m_arready[owner] = t_arready[target]; m_arready[owner] = t_arready[target];
@@ -688,7 +688,7 @@ module axi4_data_crossbar #(
if (s_write_grant_valid[target]) begin if (s_write_grant_valid[target]) begin
automatic logic [MasterWidth-1:0] owner = s_write_selected[target]; automatic logic [MasterWidth-1:0] owner = s_write_selected[target];
t_aw[target] = m_aw[owner]; t_aw[target] = m_aw[owner];
if (owner == MasterWidth'(8)) t_aw[target].qos = 4'd0; if ((owner == MasterWidth'(8)) || (owner == MasterWidth'(9))) t_aw[target].qos = 4'd0;
if ((s_write_fault_reason[owner] == 4'd0) || s_write_fault_accept[owner]) begin if ((s_write_fault_reason[owner] == 4'd0) || s_write_fault_accept[owner]) begin
t_awvalid[target] = m_awvalid[owner]; t_awvalid[target] = m_awvalid[owner];
m_awready[owner] = t_awready[target]; m_awready[owner] = t_awready[target];
@@ -1045,7 +1045,7 @@ module axi4_data_crossbar #(
end end
initial begin initial begin
if ((NumMasters != 9) || (NumTargets != 6) || (StarvationCycles < 2) || if ((NumMasters != 10) || (NumTargets != 6) || (StarvationCycles < 2) ||
((StarvationCycles & (StarvationCycles - 1)) != 0)) begin ((StarvationCycles & (StarvationCycles - 1)) != 0)) begin
$fatal(1, "axi4_data_crossbar: invalid product topology or aging interval"); $fatal(1, "axi4_data_crossbar: invalid product topology or aging interval");
end end
+3 -3
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: MulanPSL-2.0 // SPDX-License-Identifier: MulanPSL-2.0
module fabric_monitor #( module fabric_monitor #(
parameter int unsigned NumMasters = 9, parameter int unsigned NumMasters = 10,
parameter int unsigned NumTargets = 6 parameter int unsigned NumTargets = 6
) ( ) (
// verilog_format: off -- preserve the monitor event boundary columns // verilog_format: off -- preserve the monitor event boundary columns
@@ -418,8 +418,8 @@ module fabric_monitor #(
`ifndef SYNTHESIS `ifndef SYNTHESIS
initial begin initial begin
if ((NumMasters != 9) || (NumTargets != 6)) begin if ((NumMasters != 10) || (NumTargets != 6)) begin
$fatal(1, "fabric_monitor: product topology must remain 9x6"); $fatal(1, "fabric_monitor: product topology must remain 10x6");
end end
end end
`endif `endif
+2 -2
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: MulanPSL-2.0 // SPDX-License-Identifier: MulanPSL-2.0
module resource_controller #( module resource_controller #(
parameter int unsigned ResourceCount = 9 parameter int unsigned ResourceCount = 10
) ( ) (
// verilog_format: off -- preserve the resource ownership boundary columns // verilog_format: off -- preserve the resource ownership boundary columns
input logic clk_i, input logic clk_i,
@@ -23,7 +23,7 @@ module resource_controller #(
// verilog_format: on // verilog_format: on
); );
localparam logic [31:0] IpId = 32'h5253_4354; localparam logic [31:0] IpId = 32'h5253_4354;
localparam logic [31:0] IpVersion = 32'h0001_0001; localparam logic [31:0] IpVersion = 32'h0001_0002;
localparam logic [11:0] IpIdOffset = 12'h000; localparam logic [11:0] IpIdOffset = 12'h000;
localparam logic [11:0] IpVersionOffset = 12'h004; localparam logic [11:0] IpVersionOffset = 12'h004;
localparam logic [11:0] CapabilityOffset = 12'h008; localparam logic [11:0] CapabilityOffset = 12'h008;
+46 -18
View File
@@ -89,6 +89,8 @@ module retrosoc (
u_jpeg_wide_axi4_if (.aclk(clk_pclk_i), .aresetn(rst_pclk_n_i)); u_jpeg_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)) axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(64), .ID_WIDTH(3), .USER_WIDTH(1))
u_ga2d_wide_axi4_if (.aclk(clk_pclk_i), .aresetn(rst_pclk_n_i)); u_ga2d_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_npu_wide_axi4_if (.aclk(clk_hp_i), .aresetn(rst_hp_n_i));
axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(32), .ID_WIDTH(1), .USER_WIDTH(1)) 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)); 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)) axi4_if #(.ADDR_WIDTH(32), .DATA_WIDTH(64), .ID_WIDTH(3), .USER_WIDTH(1))
@@ -244,23 +246,28 @@ module retrosoc (
logic [31:0] s_ext_h_timeout; logic [31:0] s_ext_h_timeout;
logic s_ext_h_irq_raw; logic s_ext_h_irq_raw;
logic [ 1:0] s_ext_h_owner; logic [ 1:0] s_ext_h_owner;
logic [ 8:0][1:0] s_resource_owner; logic [ 9:0][1:0] s_resource_owner;
logic [ 8:0] s_resource_owner_lock; logic [ 9:0] s_resource_owner_lock;
logic [ 8:0] s_resource_quiesce; logic [ 9:0] s_resource_quiesce;
logic [ 8:0] s_resource_reset; logic [ 9:0] s_resource_reset;
logic [ 8:0] s_resource_idle_hp; logic [ 9:0] s_resource_idle_hp;
logic [ 8:0] s_resource_idle_pclk; logic [ 9:0] s_resource_idle_pclk;
logic [ 8:0] s_resource_block_ack_hp; logic [ 9:0] s_resource_block_ack_hp;
logic [ 8:0] s_resource_block_ack_pclk; logic [ 9:0] s_resource_block_ack_pclk;
logic [ 7:0] s_resource_irq_raw; logic [ 8:0] s_resource_irq_raw;
logic [ 7:0] s_resource_irq_lp; logic [ 8:0] s_resource_irq_lp;
logic [ 7:0] s_resource_irq_hp; logic [ 8:0] s_resource_irq_hp;
logic s_apu_idle; logic s_apu_idle;
logic s_jpeg_idle; logic s_jpeg_idle;
logic s_ga2d_idle; logic s_ga2d_idle;
logic s_ga2d_core_safe_idle; logic s_ga2d_core_safe_idle;
logic [ 8:0] s_resource_idle_combined; logic [ 9:0] s_resource_idle_combined;
logic [ 8:0] s_resource_block_ack_combined; logic [ 9:0] s_resource_block_ack_combined;
logic s_npu_idle;
logic s_npu_block_ack;
logic s_npu_hp_pause_ack;
logic s_npu_hp_flush_busy;
logic s_npu_hp_idle;
logic s_ga2d_source_stop; logic s_ga2d_source_stop;
logic s_ga2d_source_safe_idle; logic s_ga2d_source_safe_idle;
logic s_ga2d_bridge_clear_busy; logic s_ga2d_bridge_clear_busy;
@@ -506,7 +513,7 @@ module retrosoc (
.dat_i({ .dat_i({
u_sysctrl_if.hp_release_o, u_sysctrl_if.hp_release_o,
s_hp_debug_reset_req, s_hp_debug_reset_req,
s_data_plane_idle && s_hp_mmio_idle, s_data_plane_idle && s_hp_mmio_idle && (!s_hp_block_hp || s_npu_hp_pause_ack),
s_data_plane_flush_busy || s_hp_mmio_clear_busy s_data_plane_flush_busy || s_hp_mmio_clear_busy
}), }),
.dat_o({s_hp_release_req_aon, s_hp_debug_reset_req_aon, s_hp_idle_aon, s_hp_flush_busy_aon}) .dat_o({s_hp_release_req_aon, s_hp_debug_reset_req_aon, s_hp_idle_aon, s_hp_flush_busy_aon})
@@ -612,6 +619,10 @@ module retrosoc (
.jpeg_axi4 (u_jpeg_wide_axi4_if), .jpeg_axi4 (u_jpeg_wide_axi4_if),
.ga2d_axi4 (u_ga2d_wide_axi4_if), .ga2d_axi4 (u_ga2d_wide_axi4_if),
.ga2d_core_safe_idle_i (s_ga2d_core_safe_idle), .ga2d_core_safe_idle_i (s_ga2d_core_safe_idle),
.npu_axi4 (u_npu_wide_axi4_if),
.npu_source_idle_i (s_npu_idle),
.npu_source_quiesced_i (s_npu_block_ack),
.npu_flush_busy_i (s_npu_hp_flush_busy),
.lp_data_axi4 (u_mgmt_data_axi4_if), .lp_data_axi4 (u_mgmt_data_axi4_if),
.ext_h_axi4 (u_ext_h_wide_axi4_if), .ext_h_axi4 (u_ext_h_wide_axi4_if),
.sram_gateway_axi4 (u_data_sram_axi4_if), .sram_gateway_axi4 (u_data_sram_axi4_if),
@@ -664,7 +675,7 @@ module retrosoc (
cdc_sync #( cdc_sync #(
.STAGE (2), .STAGE (2),
.DATA_WIDTH(9) .DATA_WIDTH(10)
) u_resource_idle_sync ( ) u_resource_idle_sync (
.clk_i (clk_pclk_i), .clk_i (clk_pclk_i),
.rst_n_i(rst_pclk_n_i), .rst_n_i(rst_pclk_n_i),
@@ -673,7 +684,7 @@ module retrosoc (
); );
cdc_sync #( cdc_sync #(
.STAGE (2), .STAGE (2),
.DATA_WIDTH(9) .DATA_WIDTH(10)
) u_resource_block_ack_sync ( ) u_resource_block_ack_sync (
.clk_i (clk_pclk_i), .clk_i (clk_pclk_i),
.rst_n_i(rst_pclk_n_i), .rst_n_i(rst_pclk_n_i),
@@ -764,7 +775,7 @@ module retrosoc (
.dat_o (s_pclk_pending_q) .dat_o (s_pclk_pending_q)
); );
assign hp_idle_o = s_data_plane_idle && s_hp_mmio_idle; assign hp_idle_o = s_data_plane_idle && s_hp_mmio_idle && (!s_hp_block_hp || s_npu_hp_pause_ack);
assign pclk_idle_o = !(|s_pclk_pending_q) && !u_cfg_axi4_if.awvalid && assign pclk_idle_o = !(|s_pclk_pending_q) && !u_cfg_axi4_if.awvalid &&
!u_cfg_axi4_if.arvalid && !u_system_axi4_if.awvalid && !u_cfg_axi4_if.arvalid && !u_system_axi4_if.awvalid &&
!u_system_axi4_if.arvalid; !u_system_axi4_if.arvalid;
@@ -861,6 +872,8 @@ module retrosoc (
.clk_ulpi_i (clk_ulpi_i), .clk_ulpi_i (clk_ulpi_i),
.clk_mem_i (clk_mem_i), .clk_mem_i (clk_mem_i),
.rst_mem_n_i (rst_mem_n_i), .rst_mem_n_i (rst_mem_n_i),
.clk_hp_i (clk_hp_i),
.rst_hp_n_i (rst_hp_n_i),
.debug_halted_i (s_mgmt_debug_halted), .debug_halted_i (s_mgmt_debug_halted),
.timebase_tick_i (timebase_tick_i), .timebase_tick_i (timebase_tick_i),
.ext_h_hp_irq_i ((s_ext_h_owner == 2'd1) ? s_ext_h_irq_raw : 1'b0), .ext_h_hp_irq_i ((s_ext_h_owner == 2'd1) ? s_ext_h_irq_raw : 1'b0),
@@ -881,6 +894,12 @@ module retrosoc (
.ga2d_bridge_clear_busy_i (s_ga2d_bridge_clear_busy), .ga2d_bridge_clear_busy_i (s_ga2d_bridge_clear_busy),
.ga2d_bridge_epoch_i (s_ga2d_bridge_epoch), .ga2d_bridge_epoch_i (s_ga2d_bridge_epoch),
.ga2d_data_ready_i (s_ga2d_data_ready), .ga2d_data_ready_i (s_ga2d_data_ready),
.npu_owner_i (s_resource_owner[9]),
.npu_owner_lock_i (s_resource_owner_lock[9]),
.npu_quiesce_i (s_resource_quiesce[9]),
.npu_reset_i (s_resource_reset[9]),
.npu_hp_block_new_i (s_hp_block_hp),
.npu_hp_flush_i (s_hp_flush_hp),
.mem_pad_mode_i (s_mem_pad_mode_lp), .mem_pad_mode_i (s_mem_pad_mode_lp),
.cfg_axi4 (u_cfg_pclk_axi4_if), .cfg_axi4 (u_cfg_pclk_axi4_if),
.dma_axi4 (u_dma_axi4_if), .dma_axi4 (u_dma_axi4_if),
@@ -890,6 +909,7 @@ module retrosoc (
.apu_axi4 (u_apu_axi4_if), .apu_axi4 (u_apu_axi4_if),
.jpeg_axi4 (u_jpeg_wide_axi4_if), .jpeg_axi4 (u_jpeg_wide_axi4_if),
.ga2d_axi4 (u_ga2d_wide_axi4_if), .ga2d_axi4 (u_ga2d_wide_axi4_if),
.npu_axi4 (u_npu_wide_axi4_if),
.psram_axi4 (u_data_qpi_axi4_if), .psram_axi4 (u_data_qpi_axi4_if),
.xpi_axi4 (u_data_xpi_axi4_if), .xpi_axi4 (u_data_xpi_axi4_if),
.spisd_axi4 (u_spisd_axi4_if), .spisd_axi4 (u_spisd_axi4_if),
@@ -929,6 +949,11 @@ module retrosoc (
.jpeg_idle_o (s_jpeg_idle), .jpeg_idle_o (s_jpeg_idle),
.ga2d_idle_o (s_ga2d_idle), .ga2d_idle_o (s_ga2d_idle),
.ga2d_core_safe_idle_o (s_ga2d_core_safe_idle), .ga2d_core_safe_idle_o (s_ga2d_core_safe_idle),
.npu_idle_o (s_npu_idle),
.npu_block_ack_o (s_npu_block_ack),
.npu_hp_pause_ack_o (s_npu_hp_pause_ack),
.npu_hp_flush_busy_o (s_npu_hp_flush_busy),
.npu_hp_idle_o (s_npu_hp_idle),
.irq_o (s_apb4_periph_irq) .irq_o (s_apb4_periph_irq)
); );
@@ -979,6 +1004,7 @@ module retrosoc (
); );
assign s_resource_idle_combined = { assign s_resource_idle_combined = {
s_npu_idle && s_resource_idle_pclk[9],
s_ga2d_idle && s_ga2d_source_safe_idle && s_resource_idle_pclk[8], s_ga2d_idle && s_ga2d_source_safe_idle && s_resource_idle_pclk[8],
s_apu_idle && s_resource_idle_pclk[7], s_apu_idle && s_resource_idle_pclk[7],
s_jpeg_idle && s_resource_idle_pclk[6], s_jpeg_idle && s_resource_idle_pclk[6],
@@ -986,12 +1012,13 @@ module retrosoc (
s_resource_idle_pclk[4:0] s_resource_idle_pclk[4:0]
}; };
assign s_resource_block_ack_combined = { assign s_resource_block_ack_combined = {
s_npu_idle && s_resource_block_ack_pclk[9],
s_ga2d_idle && s_ga2d_source_safe_idle && s_resource_block_ack_pclk[8], s_ga2d_idle && s_ga2d_source_safe_idle && s_resource_block_ack_pclk[8],
s_apu_idle && s_resource_block_ack_pclk[7], s_apu_idle && s_resource_block_ack_pclk[7],
s_resource_block_ack_pclk[6:0] s_resource_block_ack_pclk[6:0]
}; };
logic [41:0] s_unused_domain_inputs; logic [42:0] s_unused_domain_inputs;
assign s_unused_domain_inputs = { assign s_unused_domain_inputs = {
clk_pclk_i, clk_pclk_i,
rst_pclk_n_i, rst_pclk_n_i,
@@ -1014,6 +1041,7 @@ module retrosoc (
s_ga2d_bridge_clear_busy, s_ga2d_bridge_clear_busy,
^s_ga2d_bridge_epoch, ^s_ga2d_bridge_epoch,
s_ga2d_data_ready, s_ga2d_data_ready,
s_npu_hp_idle,
^unused_cdc_clear_busy, ^unused_cdc_clear_busy,
^unused_cdc_epoch, ^unused_cdc_epoch,
^{s_hp_mmio_epoch, s_hp_mmio_clear_busy}, ^{s_hp_mmio_epoch, s_hp_mmio_clear_busy},
+69 -17
View File
@@ -16,7 +16,7 @@ module soc_data_plane (
input logic block_new_i, input logic block_new_i,
input logic recovery_i, input logic recovery_i,
input logic flush_i, input logic flush_i,
input logic [8:0] resource_block_i, input logic [9:0] resource_block_i,
input logic [1:0] mem_pad_mode_i, input logic [1:0] mem_pad_mode_i,
input logic ext_h_block_i, input logic ext_h_block_i,
input logic [31:0] ext_h_read_base_i, input logic [31:0] ext_h_read_base_i,
@@ -35,6 +35,10 @@ module soc_data_plane (
axi4_if.slave ga2d_axi4, axi4_if.slave ga2d_axi4,
input logic ga2d_core_safe_idle_i input logic ga2d_core_safe_idle_i
= 1'b1, = 1'b1,
axi4_if.slave npu_axi4,
input logic npu_source_idle_i,
input logic npu_source_quiesced_i,
input logic npu_flush_busy_i,
axi4_if.slave lp_data_axi4, axi4_if.slave lp_data_axi4,
axi4_if.slave ext_h_axi4, axi4_if.slave ext_h_axi4,
axi4_if.master sram_gateway_axi4, axi4_if.master sram_gateway_axi4,
@@ -53,8 +57,8 @@ module soc_data_plane (
output logic ga2d_bridge_clear_busy_o, output logic ga2d_bridge_clear_busy_o,
output logic [7:0] ga2d_bridge_epoch_o, output logic [7:0] ga2d_bridge_epoch_o,
output logic ga2d_data_ready_o, output logic ga2d_data_ready_o,
output logic [8:0] resource_idle_o, output logic [9:0] resource_idle_o,
output logic [8:0] resource_block_ack_o, output logic [9:0] resource_block_ack_o,
output logic [7:0] outstanding_read_o, output logic [7:0] outstanding_read_o,
output logic [7:0] outstanding_write_o, output logic [7:0] outstanding_write_o,
output logic fault_valid_o, output logic fault_valid_o,
@@ -67,7 +71,7 @@ module soc_data_plane (
); );
localparam int unsigned NumIoMasters = 3; localparam int unsigned NumIoMasters = 3;
localparam int unsigned NumMemoryTargets = 5; localparam int unsigned NumMemoryTargets = 5;
localparam int unsigned NumDataMasters = 9; localparam int unsigned NumDataMasters = 10;
localparam int unsigned NumFaultSources = NumMemoryTargets + 1; localparam int unsigned NumFaultSources = NumMemoryTargets + 1;
localparam int unsigned FaultSourceWidth = $clog2(NumFaultSources); localparam int unsigned FaultSourceWidth = $clog2(NumFaultSources);
localparam logic [3:0] FaultTimeout = 4'd5; localparam logic [3:0] FaultTimeout = 4'd5;
@@ -129,17 +133,19 @@ module soc_data_plane (
logic s_ga2d_rearm_q; logic s_ga2d_rearm_q;
logic s_ga2d_master_idle_pclk; logic s_ga2d_master_idle_pclk;
logic s_ga2d_block_ack_pclk; logic s_ga2d_block_ack_pclk;
logic [ 8:0] s_resource_block_hp; logic s_npu_source_safe_idle_hp;
logic [ 8:0] s_master_idle; logic s_npu_source_quiesced_hp;
logic [ 8:0] s_master_block; logic [ 9:0] s_resource_block_hp;
logic [ 8:0] s_monitor_master_read_accept; logic [ 9:0] s_master_idle;
logic [ 8:0] s_monitor_master_write_accept; logic [ 9:0] s_master_block;
logic [ 8:0] s_monitor_master_read_beat; logic [ 9:0] s_monitor_master_read_accept;
logic [ 8:0] s_monitor_master_write_beat; logic [ 9:0] s_monitor_master_write_accept;
logic [ 8:0] s_monitor_master_wait; logic [ 9:0] s_monitor_master_read_beat;
logic [ 8:0] s_monitor_master_promotion; logic [ 9:0] s_monitor_master_write_beat;
logic [ 8:0][2:0] s_monitor_master_read_outstanding; logic [ 9:0] s_monitor_master_wait;
logic [ 8:0][2:0] s_monitor_master_write_outstanding; logic [ 9:0] s_monitor_master_promotion;
logic [ 9:0][2:0] s_monitor_master_read_outstanding;
logic [ 9:0][2:0] s_monitor_master_write_outstanding;
logic [ 5:0] s_monitor_target_read_accept; logic [ 5:0] s_monitor_target_read_accept;
logic [ 5:0] s_monitor_target_write_accept; logic [ 5:0] s_monitor_target_write_accept;
logic [ 5:0] s_monitor_target_read_beat; logic [ 5:0] s_monitor_target_read_beat;
@@ -321,6 +327,15 @@ module soc_data_plane (
.aclk (clk_hp_i), .aclk (clk_hp_i),
.aresetn(rst_hp_n_i) .aresetn(rst_hp_n_i)
); );
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (7),
.USER_WIDTH(1)
) u_npu_prefixed_axi4 (
.aclk (clk_hp_i),
.aresetn(rst_hp_n_i)
);
cdc_sync #( cdc_sync #(
.STAGE (2), .STAGE (2),
.DATA_WIDTH(2) .DATA_WIDTH(2)
@@ -332,7 +347,7 @@ module soc_data_plane (
); );
cdc_sync #( cdc_sync #(
.STAGE (2), .STAGE (2),
.DATA_WIDTH(9) .DATA_WIDTH(10)
) u_resource_block_sync ( ) u_resource_block_sync (
.clk_i (clk_hp_i), .clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i), .rst_n_i(rst_hp_n_i),
@@ -394,6 +409,26 @@ module soc_data_plane (
.dat_i (resource_block_ack_o[8]), .dat_i (resource_block_ack_o[8]),
.dat_o (s_ga2d_block_ack_pclk) .dat_o (s_ga2d_block_ack_pclk)
); );
// The NPU shell owns its PCLK-side idle and round-trip quiesce
// acknowledgement; both cross into HP exactly like the GA2D source state.
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(1)
) u_npu_source_idle_sync (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.dat_i (npu_source_idle_i),
.dat_o (s_npu_source_safe_idle_hp)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(1)
) u_npu_source_quiesced_sync (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.dat_i (npu_source_quiesced_i),
.dat_o (s_npu_source_quiesced_hp)
);
assign s_ga2d_recovery_condition = resource_block_i[8] || !s_ga2d_hp_reset_n_pclk || assign s_ga2d_recovery_condition = resource_block_i[8] || !s_ga2d_hp_reset_n_pclk ||
s_ga2d_flush_pclk || s_ga2d_clear_busy || s_ga2d_flush_pclk || s_ga2d_clear_busy ||
@@ -439,6 +474,7 @@ module soc_data_plane (
end end
assign s_master_block = { assign s_master_block = {
s_resource_block_hp[9] && s_npu_source_quiesced_hp,
s_resource_block_hp[8] && s_ga2d_source_quiesced_hp, s_resource_block_hp[8] && s_ga2d_source_quiesced_hp,
s_resource_block_hp[5], s_resource_block_hp[5],
s_resource_block_hp[6], s_resource_block_hp[6],
@@ -449,6 +485,8 @@ module soc_data_plane (
2'b00 2'b00
}; };
assign resource_idle_o = { assign resource_idle_o = {
(s_resource_block_hp[9] ? s_npu_source_quiesced_hp : s_npu_source_safe_idle_hp) &&
s_master_idle[9],
(s_resource_block_hp[8] ? s_ga2d_source_quiesced_hp : s_ga2d_source_safe_idle_hp) && (s_resource_block_hp[8] ? s_ga2d_source_quiesced_hp : s_ga2d_source_safe_idle_hp) &&
s_master_idle[8], s_master_idle[8],
s_master_idle[3], s_master_idle[3],
@@ -461,6 +499,7 @@ module soc_data_plane (
s_master_idle[2] s_master_idle[2]
}; };
assign resource_block_ack_o = { assign resource_block_ack_o = {
s_resource_block_hp[9] && s_npu_source_quiesced_hp && s_master_idle[9],
s_resource_block_hp[8] && s_ga2d_source_quiesced_hp && s_master_idle[8], s_resource_block_hp[8] && s_ga2d_source_quiesced_hp && s_master_idle[8],
s_resource_block_hp[7:0] s_resource_block_hp[7:0]
}; };
@@ -622,6 +661,19 @@ module soc_data_plane (
.sink (u_master_axi4[8]) .sink (u_master_axi4[8])
); );
// The NPU master is HP-native: no payload CDC or width upsizer. The shell
// ties it safely idle in Phase 2; the production DMA is Phase 3 scope.
axi4_id_prefix #(
.MasterIndex(4'd9)
) u_npu_prefix (
.source(npu_axi4),
.sink (u_npu_prefixed_axi4)
);
axi4_connector u_npu_connector (
.source(u_npu_prefixed_axi4),
.sink (u_master_axi4[9])
);
axi4_async_bridge #( axi4_async_bridge #(
.DataWidth(64), .DataWidth(64),
.IdWidth (3) .IdWidth (3)
@@ -958,7 +1010,7 @@ module soc_data_plane (
assign flush_busy_o = (|s_io_clear_busy) || (|s_target_clear_busy) || 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_guard_clear_busy) || s_lp_clear_busy || s_ext_clear_busy ||
s_jpeg_clear_busy || s_ga2d_clear_busy; s_jpeg_clear_busy || s_ga2d_clear_busy || npu_flush_busy_i;
assign apu_bridge_epoch_o = unused_io_epoch[1]; assign apu_bridge_epoch_o = unused_io_epoch[1];
assign s_unused_epoch = { assign s_unused_epoch = {
^unused_io_epoch, ^unused_target_epoch, ^{unused_lp_epoch, unused_ext_epoch, unused_jpeg_epoch} ^unused_io_epoch, ^unused_target_epoch, ^{unused_lp_epoch, unused_ext_epoch, unused_jpeg_epoch}
+5
View File
@@ -136,6 +136,7 @@
"apb4_apu.sv", "apb4_apu.sv",
"apb4_ga2d.sv", "apb4_ga2d.sv",
"apb4_jpeg.sv", "apb4_jpeg.sv",
"apb4_npu.sv",
"apu_bitstream_engine.sv", "apu_bitstream_engine.sv",
"apu_codec_controller.sv", "apu_codec_controller.sv",
"apu_codec_sequencer.sv", "apu_codec_sequencer.sv",
@@ -197,10 +198,14 @@
"jpeg_table_store.sv", "jpeg_table_store.sv",
"jpeg_transform.sv", "jpeg_transform.sv",
"npu_accumulator.sv", "npu_accumulator.sv",
"npu_control_cdc.sv",
"npu_core.sv",
"npu_define.svh",
"npu_local_sram.sv", "npu_local_sram.sv",
"npu_mac_array.sv", "npu_mac_array.sv",
"npu_patch_packer.sv", "npu_patch_packer.sv",
"npu_pkg.sv", "npu_pkg.sv",
"npu_reg.sv",
"npu_requantizer.sv", "npu_requantizer.sv",
"npu_vector.sv" "npu_vector.sv"
], ],
+2
View File
@@ -36,6 +36,7 @@ TEST_SOURCES = (
"crt/src/hal/apu.c", "crt/src/hal/apu.c",
"crt/src/hal/ga2d_math.c", "crt/src/hal/ga2d_math.c",
"crt/src/hal/ga2d.c", "crt/src/hal/ga2d.c",
"crt/src/hal/npu.c",
"crt/src/hal/fabric_monitor.c", "crt/src/hal/fabric_monitor.c",
"crt/src/hal/sysctrl.c", "crt/src/hal/sysctrl.c",
"crt/src/hal/clock.c", "crt/src/hal/clock.c",
@@ -99,6 +100,7 @@ def main() -> int:
"-fno-builtin", "-fno-builtin",
"-DRS_APU_TEST_MMIO", "-DRS_APU_TEST_MMIO",
"-DRS_GA2D_TEST_MMIO", "-DRS_GA2D_TEST_MMIO",
"-DRS_NPU_TEST_MMIO",
"-DRS_FABRIC_MONITOR_TEST_MMIO", "-DRS_FABRIC_MONITOR_TEST_MMIO",
"-DRS_RESOURCE_TEST_MMIO", "-DRS_RESOURCE_TEST_MMIO",
"-I", "-I",
+401 -26
View File
@@ -18,6 +18,7 @@
#include <retrosoc/hal/i2s.h> #include <retrosoc/hal/i2s.h>
#include <retrosoc/hal/jpeg.h> #include <retrosoc/hal/jpeg.h>
#include <retrosoc/hal/memory.h> #include <retrosoc/hal/memory.h>
#include <retrosoc/hal/npu.h>
#include <retrosoc/hal/psram.h> #include <retrosoc/hal/psram.h>
#include <retrosoc/hal/resource.h> #include <retrosoc/hal/resource.h>
#include <retrosoc/hal/sdram.h> #include <retrosoc/hal/sdram.h>
@@ -50,11 +51,13 @@ static uint32_t image_call_count;
volatile uint32_t rs_apu_test_mmio[1024]; volatile uint32_t rs_apu_test_mmio[1024];
volatile uint32_t rs_ga2d_test_mmio[1024]; volatile uint32_t rs_ga2d_test_mmio[1024];
uint32_t rs_ga2d_test_mem_pad_mode; uint32_t rs_ga2d_test_mem_pad_mode;
volatile uint32_t rs_npu_test_mmio[1024];
volatile uint32_t rs_fabric_monitor_test_mmio[1024]; volatile uint32_t rs_fabric_monitor_test_mmio[1024];
volatile uint32_t rs_resource_test_mmio[1024]; volatile uint32_t rs_resource_test_mmio[1024];
#define APU_TEST_REG(offset) rs_apu_test_mmio[(offset) / 4U] #define APU_TEST_REG(offset) rs_apu_test_mmio[(offset) / 4U]
#define GA2D_TEST_REG(offset) rs_ga2d_test_mmio[(offset) / 4U] #define GA2D_TEST_REG(offset) rs_ga2d_test_mmio[(offset) / 4U]
#define NPU_TEST_REG(offset) rs_npu_test_mmio[(offset) / 4U]
#define FABRIC_MONITOR_TEST_REG(offset) rs_fabric_monitor_test_mmio[(offset) / 4U] #define FABRIC_MONITOR_TEST_REG(offset) rs_fabric_monitor_test_mmio[(offset) / 4U]
#define RESOURCE_TEST_REG(offset) rs_resource_test_mmio[(offset) / 4U] #define RESOURCE_TEST_REG(offset) rs_resource_test_mmio[(offset) / 4U]
@@ -870,7 +873,7 @@ static int test_resource_validation(void) {
if ((RS_RESOURCE_DMA != 0) || (RS_RESOURCE_USB2 != 1) || (RS_RESOURCE_SDIO0 != 2) || if ((RS_RESOURCE_DMA != 0) || (RS_RESOURCE_USB2 != 1) || (RS_RESOURCE_SDIO0 != 2) ||
(RS_RESOURCE_SDIO1 != 3) || (RS_RESOURCE_SPISD != 4) || (RS_RESOURCE_EXT_H != 5) || (RS_RESOURCE_SDIO1 != 3) || (RS_RESOURCE_SPISD != 4) || (RS_RESOURCE_EXT_H != 5) ||
(RS_RESOURCE_JPEG != 6) || (RS_RESOURCE_APU != 7) || (RS_RESOURCE_GA2D != 8) || (RS_RESOURCE_JPEG != 6) || (RS_RESOURCE_APU != 7) || (RS_RESOURCE_GA2D != 8) ||
(RS_RESOURCE_COUNT != 9)) { (RS_RESOURCE_NPU != 9) || (RS_RESOURCE_COUNT != 10)) {
return 1; return 1;
} }
if ((rs_resource_get_status((rs_resource_t)RS_RESOURCE_COUNT, &status) != RS_EINVAL) || if ((rs_resource_get_status((rs_resource_t)RS_RESOURCE_COUNT, &status) != RS_EINVAL) ||
@@ -926,7 +929,7 @@ static int test_fabric_monitor_hal_contract(void) {
(RS_FABRIC_MASTER_IO_B != 4) || (RS_FABRIC_MASTER_LP != 5) || (RS_FABRIC_MASTER_IO_B != 4) || (RS_FABRIC_MASTER_LP != 5) ||
(RS_FABRIC_MASTER_JPEG != 6) || (RS_FABRIC_MASTER_RESERVED != 6) || (RS_FABRIC_MASTER_JPEG != 6) || (RS_FABRIC_MASTER_RESERVED != 6) ||
(RS_FABRIC_MASTER_EXT_H != 7) || (RS_FABRIC_MASTER_GA2D != 8) || (RS_FABRIC_MASTER_EXT_H != 7) || (RS_FABRIC_MASTER_GA2D != 8) ||
(RS_FABRIC_MASTER_COUNT != 9)) { (RS_FABRIC_MASTER_NPU != 9) || (RS_FABRIC_MASTER_COUNT != 10)) {
return 1; return 1;
} }
@@ -1066,12 +1069,10 @@ static int test_apu_kws_validation(void) {
(APU_TEST_REG(RS_APU_ABI_KWS_MODEL_ADDRESS) != UINT32_C(0xA5A5A5A5))) { (APU_TEST_REG(RS_APU_ABI_KWS_MODEL_ADDRESS) != UINT32_C(0xA5A5A5A5))) {
return 5; return 5;
} }
APU_TEST_REG(RS_APU_ABI_OWNER_STATUS) = APU_TEST_REG(RS_APU_ABI_OWNER_STATUS) = UINT32_C(1) << RS_APU_ABI_OWNER_STATUS_QUIESCE;
UINT32_C(1) << RS_APU_ABI_OWNER_STATUS_QUIESCE;
APU_TEST_REG(RS_APU_ABI_KWS_MODEL_STATUS) = 0U; APU_TEST_REG(RS_APU_ABI_KWS_MODEL_STATUS) = 0U;
if ((rs_apu_kws_model_load(&image, 1U) != RS_EIO) || if ((rs_apu_kws_model_load(&image, 1U) != RS_EIO) ||
(APU_TEST_REG(RS_APU_ABI_COMMAND) != (APU_TEST_REG(RS_APU_ABI_COMMAND) != (UINT32_C(1) << RS_APU_ABI_COMMAND_MODEL_LOAD))) {
(UINT32_C(1) << RS_APU_ABI_COMMAND_MODEL_LOAD))) {
return 6; return 6;
} }
APU_TEST_REG(RS_APU_ABI_OWNER_STATUS) = 0U; APU_TEST_REG(RS_APU_ABI_OWNER_STATUS) = 0U;
@@ -1247,9 +1248,11 @@ static void test_ga2d_mmio_reset(void) {
rs_ga2d_test_mem_pad_mode = (uint32_t)RS_MEMORY_PAD_QPI; rs_ga2d_test_mem_pad_mode = (uint32_t)RS_MEMORY_PAD_QPI;
} }
static int check_ga2d_validation_result( static int check_ga2d_validation_result(const rs_ga2d_job_t *job,
const rs_ga2d_job_t *job, const rs_ga2d_capability_t *capability, rs_status_t expected_status, const rs_ga2d_capability_t *capability,
rs_ga2d_validation_category_t expected_category, rs_ga2d_plane_t expected_plane) { rs_status_t expected_status,
rs_ga2d_validation_category_t expected_category,
rs_ga2d_plane_t expected_plane) {
const rs_ga2d_validation_result_t result = const rs_ga2d_validation_result_t result =
rs_ga2d_job_validate_capability_result(job, capability); rs_ga2d_job_validate_capability_result(job, capability);
@@ -1308,8 +1311,8 @@ static int test_ga2d_pixel_math(void) {
if ((rs_ga2d_convert_pixel(source, RS_GA2D_FORMAT_XRGB8888, destination, if ((rs_ga2d_convert_pixel(source, RS_GA2D_FORMAT_XRGB8888, destination,
RS_GA2D_FORMAT_ARGB8888) != RS_OK) || RS_GA2D_FORMAT_ARGB8888) != RS_OK) ||
(destination[3] != UINT8_C(0xFF)) || (destination[3] != UINT8_C(0xFF)) ||
(rs_ga2d_convert_pixel(source, RS_GA2D_FORMAT_A8, destination, (rs_ga2d_convert_pixel(source, RS_GA2D_FORMAT_A8, destination, RS_GA2D_FORMAT_RGB888) !=
RS_GA2D_FORMAT_RGB888) != RS_EINVAL) || RS_EINVAL) ||
(rs_ga2d_pack_pixel(destination, RS_GA2D_FORMAT_A8, &pixel) != RS_EINVAL)) { (rs_ga2d_pack_pixel(destination, RS_GA2D_FORMAT_A8, &pixel) != RS_EINVAL)) {
return 5; return 5;
} }
@@ -1354,9 +1357,9 @@ static int test_ga2d_pixel_math(void) {
destination[1] = UINT8_C(0x20); destination[1] = UINT8_C(0x20);
destination[2] = UINT8_C(0x10); destination[2] = UINT8_C(0x10);
destination[3] = UINT8_C(0x00); destination[3] = UINT8_C(0x00);
if ((rs_ga2d_blend_pixel(source, RS_GA2D_FORMAT_ARGB8888, destination, if ((rs_ga2d_blend_pixel(source, RS_GA2D_FORMAT_ARGB8888, destination, RS_GA2D_FORMAT_XRGB8888,
RS_GA2D_FORMAT_XRGB8888, destination, RS_GA2D_FORMAT_XRGB8888, destination, RS_GA2D_FORMAT_XRGB8888, UINT32_C(0),
UINT32_C(0), UINT8_C(128)) != RS_OK) || UINT8_C(128)) != RS_OK) ||
(destination[0] != UINT8_C(0x78)) || (destination[1] != UINT8_C(0x50)) || (destination[0] != UINT8_C(0x78)) || (destination[1] != UINT8_C(0x50)) ||
(destination[2] != UINT8_C(0x28)) || (destination[3] != UINT8_C(0xFF))) { (destination[2] != UINT8_C(0x28)) || (destination[3] != UINT8_C(0xFF))) {
return 9; return 9;
@@ -1460,8 +1463,7 @@ static int test_ga2d_p5_validation(void) {
for (uint32_t foreground_format = 0U; foreground_format < 4U; ++foreground_format) { for (uint32_t foreground_format = 0U; foreground_format < 4U; ++foreground_format) {
convert.foreground.format = (rs_ga2d_format_t)foreground_format; convert.foreground.format = (rs_ga2d_format_t)foreground_format;
convert.foreground.pitch = color_pitches[foreground_format]; convert.foreground.pitch = color_pitches[foreground_format];
for (uint32_t destination_format = 0U; destination_format < 4U; for (uint32_t destination_format = 0U; destination_format < 4U; ++destination_format) {
++destination_format) {
convert.destination.format = (rs_ga2d_format_t)destination_format; convert.destination.format = (rs_ga2d_format_t)destination_format;
convert.destination.pitch = color_pitches[destination_format]; convert.destination.pitch = color_pitches[destination_format];
if (rs_ga2d_job_validate(&convert) != RS_OK) { if (rs_ga2d_job_validate(&convert) != RS_OK) {
@@ -1478,8 +1480,7 @@ static int test_ga2d_p5_validation(void) {
convert.foreground.pitch = color_pitches[RS_GA2D_FORMAT_RGB565]; convert.foreground.pitch = color_pitches[RS_GA2D_FORMAT_RGB565];
convert.destination.format = RS_GA2D_FORMAT_RGB565; convert.destination.format = RS_GA2D_FORMAT_RGB565;
convert.destination.pitch = color_pitches[RS_GA2D_FORMAT_RGB565]; convert.destination.pitch = color_pitches[RS_GA2D_FORMAT_RGB565];
GA2D_TEST_REG(RS_GA2D_REG_CAPABILITY) = GA2D_TEST_REG(RS_GA2D_REG_CAPABILITY) = RS_GA2D_CAPABILITY_P5 & ~RS_GA2D_CAPABILITY_CONVERT;
RS_GA2D_CAPABILITY_P5 & ~RS_GA2D_CAPABILITY_CONVERT;
if (rs_ga2d_job_validate(&convert) != RS_ENOTSUP) { if (rs_ga2d_job_validate(&convert) != RS_ENOTSUP) {
return 10; return 10;
} }
@@ -1495,13 +1496,13 @@ static int test_ga2d_p5_validation(void) {
for (uint32_t foreground_format = 0U; foreground_format < 5U; ++foreground_format) { for (uint32_t foreground_format = 0U; foreground_format < 5U; ++foreground_format) {
blend.foreground.format = (rs_ga2d_format_t)foreground_format; blend.foreground.format = (rs_ga2d_format_t)foreground_format;
blend.foreground.pitch = blend.foreground.pitch = (foreground_format == (uint32_t)RS_GA2D_FORMAT_A8)
(foreground_format == (uint32_t)RS_GA2D_FORMAT_A8) ? 5U : color_pitches[foreground_format]; ? 5U
: color_pitches[foreground_format];
for (uint32_t background_format = 0U; background_format < 4U; ++background_format) { for (uint32_t background_format = 0U; background_format < 4U; ++background_format) {
blend.background.format = (rs_ga2d_format_t)background_format; blend.background.format = (rs_ga2d_format_t)background_format;
blend.background.pitch = color_pitches[background_format]; blend.background.pitch = color_pitches[background_format];
for (uint32_t destination_format = 0U; destination_format < 4U; for (uint32_t destination_format = 0U; destination_format < 4U; ++destination_format) {
++destination_format) {
blend.destination.format = (rs_ga2d_format_t)destination_format; blend.destination.format = (rs_ga2d_format_t)destination_format;
blend.destination.pitch = color_pitches[destination_format]; blend.destination.pitch = color_pitches[destination_format];
if (rs_ga2d_job_validate(&blend) != RS_OK) { if (rs_ga2d_job_validate(&blend) != RS_OK) {
@@ -1516,8 +1517,7 @@ static int test_ga2d_p5_validation(void) {
blend.background.pitch = 15U; blend.background.pitch = 15U;
blend.destination.format = RS_GA2D_FORMAT_RGB888; blend.destination.format = RS_GA2D_FORMAT_RGB888;
blend.destination.pitch = 15U; blend.destination.pitch = 15U;
GA2D_TEST_REG(RS_GA2D_REG_CAPABILITY) = GA2D_TEST_REG(RS_GA2D_REG_CAPABILITY) = RS_GA2D_CAPABILITY_P5 & ~RS_GA2D_CAPABILITY_A8_MASK;
RS_GA2D_CAPABILITY_P5 & ~RS_GA2D_CAPABILITY_A8_MASK;
if (rs_ga2d_job_validate(&blend) != RS_ENOTSUP) { if (rs_ga2d_job_validate(&blend) != RS_ENOTSUP) {
return 13; return 13;
} }
@@ -1633,8 +1633,7 @@ static int test_ga2d_validation_precedence(void) {
job.width = 0U; job.width = 0U;
job.foreground.format = RS_GA2D_FORMAT_A8; job.foreground.format = RS_GA2D_FORMAT_A8;
if (check_ga2d_validation_result(&job, &capability, RS_EINVAL, if (check_ga2d_validation_result(&job, &capability, RS_EINVAL, RS_GA2D_VALIDATION_CATEGORY_SIZE,
RS_GA2D_VALIDATION_CATEGORY_SIZE,
RS_GA2D_PLANE_NONE) != 0) { RS_GA2D_PLANE_NONE) != 0) {
return 1; return 1;
} }
@@ -2140,6 +2139,375 @@ static int test_video_parser(void) {
return 0; return 0;
} }
static void test_npu_mmio_reset(void) {
for (size_t index = 0U; index < (sizeof(rs_npu_test_mmio) / sizeof(rs_npu_test_mmio[0]));
++index) {
rs_npu_test_mmio[index] = 0U;
}
NPU_TEST_REG(RS_NPU_REG_IP_ID) = RS_NPU_IP_ID_VALUE;
NPU_TEST_REG(RS_NPU_REG_IP_VERSION) = RS_NPU_IP_VERSION_VALUE;
NPU_TEST_REG(RS_NPU_REG_CAPABILITY) = RS_NPU_CAPABILITY_P2;
NPU_TEST_REG(RS_NPU_REG_NUMERIC_PROFILE) = RS_NPU_NUMERIC_PROFILE_VALUE;
NPU_TEST_REG(RS_NPU_REG_DESCRIPTOR_BYTES) = RS_NPU_DESCRIPTOR_BYTES;
NPU_TEST_REG(RS_NPU_REG_TIMEOUT_CYCLES) = RS_NPU_TIMEOUT_CYCLES_RESET;
NPU_TEST_REG(RS_NPU_REG_FAULT_DESCRIPTOR) = RS_NPU_FAULT_DESCRIPTOR_RESET;
}
static int test_npu_capability_contract(void) {
rs_npu_capability_t capability;
test_npu_mmio_reset();
if (rs_npu_get_capability(NULL) != RS_EINVAL) {
return 1;
}
if ((rs_npu_get_capability(&capability) != RS_OK) || (capability.ip_id != RS_NPU_IP_ID_VALUE) ||
(capability.ip_version != RS_NPU_IP_VERSION_VALUE) ||
(capability.flags != RS_NPU_CAPABILITY_P2) ||
(capability.numeric_profile != RS_NPU_NUMERIC_PROFILE_VALUE) ||
(capability.local_bytes != 0U) || (capability.dense_macs != 0U) ||
(capability.depthwise_macs != 0U) || (capability.max_k_slice != 0U) ||
(capability.max_dimension != 0U) || (capability.op_mask != 0U)) {
return 2;
}
NPU_TEST_REG(RS_NPU_REG_MAC_CONFIG) = RS_NPU_MAC_CONFIG_VALUE;
NPU_TEST_REG(RS_NPU_REG_LOCAL_BYTES) = RS_NPU_LOCAL_BYTES_VALUE;
NPU_TEST_REG(RS_NPU_REG_MAX_K_SLICE) = RS_NPU_MAX_K_SLICE;
NPU_TEST_REG(RS_NPU_REG_MAX_DIMENSION) = RS_NPU_MAX_DIMENSION;
NPU_TEST_REG(RS_NPU_REG_OP_CAPABILITY) = RS_NPU_OP_CAPABILITY_MVP;
if ((rs_npu_get_capability(&capability) != RS_OK) ||
(capability.local_bytes != RS_NPU_LOCAL_BYTES_VALUE) || (capability.dense_macs != 64U) ||
(capability.depthwise_macs != 8U) || (capability.max_k_slice != RS_NPU_MAX_K_SLICE) ||
(capability.max_dimension != RS_NPU_MAX_DIMENSION) ||
(capability.op_mask != RS_NPU_OP_CAPABILITY_MVP)) {
return 3;
}
test_npu_mmio_reset();
NPU_TEST_REG(RS_NPU_REG_IP_ID) = 0U;
if (rs_npu_get_capability(&capability) != RS_ENOTSUP) {
return 4;
}
test_npu_mmio_reset();
NPU_TEST_REG(RS_NPU_REG_IP_VERSION) = UINT32_C(0x00020000);
if (rs_npu_get_capability(&capability) != RS_ENOTSUP) {
return 5;
}
return 0;
}
static int test_npu_irq_contract(void) {
uint32_t events = 0U;
test_npu_mmio_reset();
if (rs_npu_irq_pending(NULL) != RS_EINVAL) {
return 1;
}
if ((rs_npu_irq_enable(RS_NPU_IRQ_ALL | UINT32_C(0x8)) != RS_EINVAL) ||
(rs_npu_irq_ack(UINT32_C(0x80000000)) != RS_EINVAL)) {
return 2;
}
if ((rs_npu_irq_enable(RS_NPU_IRQ_DONE | RS_NPU_IRQ_ERROR) != RS_OK) ||
(NPU_TEST_REG(RS_NPU_REG_IRQ_ENABLE) != (RS_NPU_IRQ_DONE | RS_NPU_IRQ_ERROR))) {
return 3;
}
if ((rs_npu_irq_enable(RS_NPU_IRQ_ABORTED) != RS_OK) ||
(NPU_TEST_REG(RS_NPU_REG_IRQ_ENABLE) != RS_NPU_IRQ_ABORTED)) {
return 4;
}
NPU_TEST_REG(RS_NPU_REG_IRQ_STATE) = RS_NPU_IRQ_DONE | RS_NPU_IRQ_ABORTED;
if ((rs_npu_irq_pending(&events) != RS_OK) ||
(events != (RS_NPU_IRQ_DONE | RS_NPU_IRQ_ABORTED))) {
return 5;
}
/* The MMIO array is plain memory: the HAL writes the W1C mask and the test
then models the hardware clear that the write requests. */
if ((rs_npu_irq_ack(RS_NPU_IRQ_DONE) != RS_OK) ||
(NPU_TEST_REG(RS_NPU_REG_IRQ_STATE) != RS_NPU_IRQ_DONE)) {
return 6;
}
NPU_TEST_REG(RS_NPU_REG_IRQ_STATE) = (RS_NPU_IRQ_DONE | RS_NPU_IRQ_ABORTED) & ~RS_NPU_IRQ_DONE;
if ((rs_npu_irq_pending(&events) != RS_OK) || (events != RS_NPU_IRQ_ABORTED)) {
return 7;
}
NPU_TEST_REG(RS_NPU_REG_CAPABILITY) = RS_NPU_CAPABILITY_PRESENT;
if ((rs_npu_irq_enable(RS_NPU_IRQ_DONE) != RS_ENOTSUP) ||
(rs_npu_irq_pending(&events) != RS_ENOTSUP) ||
(rs_npu_irq_ack(RS_NPU_IRQ_DONE) != RS_ENOTSUP)) {
return 8;
}
return 0;
}
static int test_npu_submit_p2(void) {
const rs_npu_job_t job = {
.descriptor_address = UINT32_C(0x10000000),
.descriptor_count = 2U,
.job_id = UINT32_C(0xA5),
.timeout_cycles = UINT32_C(72000000),
};
rs_npu_job_t malformed = job;
test_npu_mmio_reset();
if (rs_npu_submit(NULL) != RS_EINVAL) {
return 1;
}
malformed.descriptor_address = UINT32_C(0x10000001);
if (rs_npu_submit(&malformed) != RS_EINVAL) {
return 2;
}
malformed.descriptor_address = job.descriptor_address;
malformed.descriptor_count = 0U;
if (rs_npu_submit(&malformed) != RS_EINVAL) {
return 3;
}
malformed.descriptor_count = RS_NPU_JOB_COUNT_VALUE_MASK + 1U;
if (rs_npu_submit(&malformed) != RS_EINVAL) {
return 4;
}
malformed.descriptor_count = job.descriptor_count;
malformed.timeout_cycles = 0U;
if (rs_npu_submit(&malformed) != RS_EINVAL) {
return 5;
}
malformed.timeout_cycles = job.timeout_cycles;
malformed.descriptor_address = UINT32_C(0xFFFFFFC0);
malformed.descriptor_count = 2U;
if (rs_npu_submit(&malformed) != RS_EINVAL) {
return 6;
}
/* P2 advertises no EXECUTION_READY: a valid job is refused without writes. */
if ((rs_npu_submit(&job) != RS_ENOTSUP) || (NPU_TEST_REG(RS_NPU_REG_CONTROL) != 0U) ||
(NPU_TEST_REG(RS_NPU_REG_JOB_BASE) != 0U) || (NPU_TEST_REG(RS_NPU_REG_JOB_COUNT) != 0U) ||
(NPU_TEST_REG(RS_NPU_REG_JOB_ID) != 0U)) {
return 7;
}
NPU_TEST_REG(RS_NPU_REG_CAPABILITY) = 0U;
if (rs_npu_submit(&job) != RS_ENOTSUP) {
return 8;
}
return 0;
}
static int test_npu_wait_abort_contract(void) {
rs_npu_status_t status;
test_npu_mmio_reset();
if ((rs_npu_wait(0U, 0U, NULL) != RS_EINVAL) ||
(rs_npu_abort_wait(0U, 0U, NULL) != RS_EINVAL)) {
return 1;
}
/* P2 retains no active or terminal job: no token is known. */
if ((rs_npu_wait(UINT32_C(0xA5), 0U, &status) != RS_EINVAL) ||
(rs_npu_abort_wait(UINT32_C(0xA5), 0U, &status) != RS_EINVAL)) {
return 2;
}
NPU_TEST_REG(RS_NPU_REG_IP_ID) = 0U;
if ((rs_npu_wait(UINT32_C(0xA5), 0U, &status) != RS_ENOTSUP) ||
(rs_npu_abort_wait(UINT32_C(0xA5), 0U, &status) != RS_ENOTSUP)) {
return 3;
}
test_npu_mmio_reset();
NPU_TEST_REG(RS_NPU_REG_STATUS) = RS_NPU_STATUS_RESULT_VALID;
NPU_TEST_REG(RS_NPU_REG_RESULT_JOB_ID) = UINT32_C(0xA5);
NPU_TEST_REG(RS_NPU_REG_RESULT_CODE) = RS_NPU_RESULT_DONE;
NPU_TEST_REG(RS_NPU_REG_COMPLETED_DESCRIPTORS) = 7U;
NPU_TEST_REG(RS_NPU_REG_RECOVERY_GENERATION) = 3U;
if ((rs_npu_wait(UINT32_C(0xA5), 0U, &status) != RS_OK) ||
(status.flags != RS_NPU_STATUS_RESULT_VALID) || (status.job_id != UINT32_C(0xA5)) ||
(status.result_code != RS_NPU_RESULT_DONE) || (status.completed_descriptors != 7U) ||
(status.recovery_generation != 3U)) {
return 4;
}
/* abort on an already terminal token is a no-op that reports the result */
if ((rs_npu_abort_wait(UINT32_C(0xA5), 0U, &status) != RS_OK) ||
(NPU_TEST_REG(RS_NPU_REG_CONTROL) != 0U)) {
return 5;
}
if (rs_npu_wait(UINT32_C(0x5A), 0U, &status) != RS_EINVAL) {
return 6;
}
NPU_TEST_REG(RS_NPU_REG_RESULT_CODE) = RS_NPU_RESULT_ERROR;
if (rs_npu_wait(UINT32_C(0xA5), 0U, &status) != RS_EIO) {
return 7;
}
NPU_TEST_REG(RS_NPU_REG_RESULT_CODE) = RS_NPU_RESULT_ABORTED;
if (rs_npu_abort_wait(UINT32_C(0xA5), 0U, &status) != RS_EIO) {
return 8;
}
NPU_TEST_REG(RS_NPU_REG_RESULT_CODE) = RS_NPU_RESULT_RESET_CANCELLED;
if (rs_npu_wait(UINT32_C(0xA5), 0U, &status) != RS_EIO) {
return 9;
}
test_npu_mmio_reset();
NPU_TEST_REG(RS_NPU_REG_STATUS) = RS_NPU_STATUS_BUSY;
NPU_TEST_REG(RS_NPU_REG_JOB_ID) = UINT32_C(0xA5);
if ((rs_npu_wait(UINT32_C(0xA5), 4U, &status) != RS_ETIMEOUT) ||
(status.flags != RS_NPU_STATUS_BUSY)) {
return 10;
}
/* abort writes CONTROL.ABORT once for the active token, then expires */
if ((rs_npu_abort_wait(UINT32_C(0xA5), 4U, &status) != RS_ETIMEOUT) ||
(NPU_TEST_REG(RS_NPU_REG_CONTROL) != RS_NPU_CONTROL_ABORT)) {
return 11;
}
return 0;
}
static int test_npu_status_error_contract(void) {
rs_npu_status_t status;
rs_npu_error_t error;
test_npu_mmio_reset();
if ((rs_npu_get_status(NULL) != RS_EINVAL) || (rs_npu_get_error(NULL) != RS_EINVAL)) {
return 1;
}
/* P2 reset state: not READY, no retained result, fault index all ones */
if ((rs_npu_get_status(&status) != RS_OK) || (status.flags != 0U) || (status.job_id != 0U) ||
(status.result_code != RS_NPU_RESULT_NONE) || (status.completed_descriptors != 0U) ||
(status.recovery_generation != 0U)) {
return 2;
}
if ((rs_npu_get_error(&error) != RS_OK) || (error.code != RS_NPU_FAULT_NONE) ||
(error.address != 0U) || (error.descriptor_index != RS_NPU_FAULT_DESCRIPTOR_RESET) ||
(error.info != 0U)) {
return 3;
}
NPU_TEST_REG(RS_NPU_REG_STATUS) = RS_NPU_STATUS_BUSY | RS_NPU_STATUS_DRAINING |
RS_NPU_STATUS_CLOCK_PAUSED | RS_NPU_STATUS_RECOVERING |
RS_NPU_STATUS_RESULT_VALID;
NPU_TEST_REG(RS_NPU_REG_RESULT_JOB_ID) = UINT32_C(0x55);
NPU_TEST_REG(RS_NPU_REG_RESULT_CODE) = RS_NPU_RESULT_ERROR;
NPU_TEST_REG(RS_NPU_REG_COMPLETED_DESCRIPTORS) = 5U;
NPU_TEST_REG(RS_NPU_REG_RECOVERY_GENERATION) = 9U;
NPU_TEST_REG(RS_NPU_REG_FAULT_CODE) = RS_NPU_FAULT_AXI_READ;
NPU_TEST_REG(RS_NPU_REG_FAULT_ADDRESS) = UINT32_C(0x10000040);
NPU_TEST_REG(RS_NPU_REG_FAULT_DESCRIPTOR) = 2U;
NPU_TEST_REG(RS_NPU_REG_FAULT_INFO) =
(UINT32_C(2) << RS_NPU_FAULT_INFO_AXI_RESPONSE_SHIFT) |
(RS_NPU_FAULT_INFO_DIRECTION_READ << RS_NPU_FAULT_INFO_DIRECTION_SHIFT) |
(UINT32_C(12) << RS_NPU_FAULT_INFO_LANE_SHIFT);
if ((rs_npu_get_status(&status) != RS_OK) ||
(status.flags != NPU_TEST_REG(RS_NPU_REG_STATUS)) || (status.job_id != UINT32_C(0x55)) ||
(status.result_code != RS_NPU_RESULT_ERROR) || (status.completed_descriptors != 5U) ||
(status.recovery_generation != 9U)) {
return 4;
}
if ((rs_npu_get_error(&error) != RS_OK) || (error.code != RS_NPU_FAULT_AXI_READ) ||
(error.address != UINT32_C(0x10000040)) || (error.descriptor_index != 2U) ||
(error.info != NPU_TEST_REG(RS_NPU_REG_FAULT_INFO))) {
return 5;
}
NPU_TEST_REG(RS_NPU_REG_IP_VERSION) = 0U;
if ((rs_npu_get_status(&status) != RS_ENOTSUP) || (rs_npu_get_error(&error) != RS_ENOTSUP)) {
return 6;
}
return 0;
}
static int test_npu_snapshot_contract(void) {
rs_npu_counters_t counters;
test_npu_mmio_reset();
if (rs_npu_snapshot_counters(0U, NULL) != RS_EINVAL) {
return 1;
}
NPU_TEST_REG(RS_NPU_REG_PERF_STATUS) = RS_NPU_PERF_STATUS_SNAP_BUSY;
if (rs_npu_snapshot_counters(0U, &counters) != RS_EINVAL) {
return 2;
}
test_npu_mmio_reset();
if ((rs_npu_snapshot_counters(4U, &counters) != RS_ETIMEOUT) ||
(NPU_TEST_REG(RS_NPU_REG_PERF_CONTROL) != RS_NPU_PERF_CONTROL_SNAPSHOT)) {
return 3;
}
/* idle zero-bank snapshot: valid without any job execution evidence */
NPU_TEST_REG(RS_NPU_REG_PERF_STATUS) = RS_NPU_PERF_STATUS_SNAP_VALID;
if ((rs_npu_snapshot_counters(0U, &counters) != RS_OK) || (counters.job_id != 0U) ||
(counters.recovery_generation != 0U) || (counters.active_cycles != 0U) ||
(counters.clock_pause_cycles != 0U) || (counters.useful_macs != 0U) ||
(counters.pack_cycles != 0U) || (counters.local_bank_stall_cycles != 0U) ||
(counters.dma_read_bytes != 0U) || (counters.dma_write_bytes != 0U) ||
(counters.dma_stall_cycles != 0U) || (counters.requant_stall_cycles != 0U) ||
(counters.retired_descriptors != 0U)) {
return 4;
}
test_npu_mmio_reset();
NPU_TEST_REG(RS_NPU_REG_PERF_STATUS) = RS_NPU_PERF_STATUS_SNAP_VALID;
NPU_TEST_REG(RS_NPU_REG_PERF_JOB_ID) = UINT32_C(0x5A);
NPU_TEST_REG(RS_NPU_REG_PERF_GENERATION) = 2U;
for (uint32_t index = 0U; index < 10U; ++index) {
NPU_TEST_REG(RS_NPU_REG_PERF_ACTIVE_CYCLES_LO + (index * 8U)) = index + 1U;
NPU_TEST_REG(RS_NPU_REG_PERF_ACTIVE_CYCLES_HI + (index * 8U)) = index + 101U;
}
if (rs_npu_snapshot_counters(0U, &counters) != RS_OK) {
return 5;
}
{
const uint64_t values[10] = {
counters.active_cycles,
counters.clock_pause_cycles,
counters.useful_macs,
counters.pack_cycles,
counters.local_bank_stall_cycles,
counters.dma_read_bytes,
counters.dma_write_bytes,
counters.dma_stall_cycles,
counters.requant_stall_cycles,
counters.retired_descriptors,
};
for (uint32_t index = 0U; index < 10U; ++index) {
const uint64_t expected = ((uint64_t)(index + 101U) << 32U) | (uint64_t)(index + 1U);
if (values[index] != expected) {
return 6;
}
}
}
if ((counters.job_id != UINT32_C(0x5A)) || (counters.recovery_generation != 2U)) {
return 7;
}
NPU_TEST_REG(RS_NPU_REG_IP_ID) = 0U;
if (rs_npu_snapshot_counters(0U, &counters) != RS_ENOTSUP) {
return 8;
}
return 0;
}
static int test_npu_reset_contract(void) {
test_npu_mmio_reset();
/* the idle P2 shell accepts SOFT_RESET; READY stays low by design */
if ((rs_npu_reset(0U) != RS_OK) ||
(NPU_TEST_REG(RS_NPU_REG_CONTROL) != RS_NPU_CONTROL_SOFT_RESET)) {
return 1;
}
NPU_TEST_REG(RS_NPU_REG_CONTROL) = 0U;
NPU_TEST_REG(RS_NPU_REG_STATUS) = RS_NPU_STATUS_BUSY;
if ((rs_npu_reset(0U) != RS_EINVAL) || (NPU_TEST_REG(RS_NPU_REG_CONTROL) != 0U)) {
return 2;
}
NPU_TEST_REG(RS_NPU_REG_STATUS) = RS_NPU_STATUS_DRAINING;
if (rs_npu_reset(0U) != RS_EINVAL) {
return 3;
}
NPU_TEST_REG(RS_NPU_REG_STATUS) = RS_NPU_STATUS_RECOVERING;
if (rs_npu_reset(0U) != RS_EINVAL) {
return 4;
}
/* a snapshot transfer in flight also rejects reset */
test_npu_mmio_reset();
NPU_TEST_REG(RS_NPU_REG_PERF_STATUS) = RS_NPU_PERF_STATUS_SNAP_BUSY;
if ((rs_npu_reset(0U) != RS_EINVAL) || (NPU_TEST_REG(RS_NPU_REG_CONTROL) != 0U)) {
return 5;
}
NPU_TEST_REG(RS_NPU_REG_PERF_STATUS) = 0U;
NPU_TEST_REG(RS_NPU_REG_IP_ID) = 0U;
if (rs_npu_reset(0U) != RS_ENOTSUP) {
return 6;
}
return 0;
}
int main(void) { int main(void) {
const int results[] = { const int results[] = {
test_string_helpers(), test_string_helpers(),
@@ -2171,6 +2539,13 @@ int main(void) {
test_ga2d_p5_validation(), test_ga2d_p5_validation(),
test_ga2d_validation_precedence(), test_ga2d_validation_precedence(),
test_ga2d_hal_contract(), test_ga2d_hal_contract(),
test_npu_capability_contract(),
test_npu_irq_contract(),
test_npu_submit_p2(),
test_npu_wait_abort_contract(),
test_npu_status_error_contract(),
test_npu_snapshot_contract(),
test_npu_reset_contract(),
test_jpeg_validation(), test_jpeg_validation(),
test_ps2_decoders(), test_ps2_decoders(),
test_wav_parser(), test_wav_parser(),
+2 -1
View File
@@ -16,7 +16,7 @@ module apb4_topology_tb;
begin begin
s_decode_addr = address; s_decode_addr = address;
#1; #1;
if (s_psel_comb !== (29'd1 << slot)) begin if (s_psel_comb !== (30'd1 << slot)) begin
$fatal(1, "unexpected APB4 target slot for %h: got %b", address, s_psel_comb); $fatal(1, "unexpected APB4 target slot for %h: got %b", address, s_psel_comb);
end end
end end
@@ -35,6 +35,7 @@ module apb4_topology_tb;
expect_select(`SOC_ADDR_APB4_CRYPTO_BASE, 19); expect_select(`SOC_ADDR_APB4_CRYPTO_BASE, 19);
expect_select(`SOC_ADDR_APB4_GA2D_BASE, 28); expect_select(`SOC_ADDR_APB4_GA2D_BASE, 28);
expect_select(`SOC_ADDR_APB4_APU_BASE, 27); expect_select(`SOC_ADDR_APB4_APU_BASE, 27);
expect_select(`SOC_ADDR_APB4_NPU_BASE, 29);
$display("SoC topology APB4 routing test passed"); $display("SoC topology APB4 routing test passed");
$finish; $finish;
end end
+33 -10
View File
@@ -1,14 +1,14 @@
`timescale 1ns / 1ps `timescale 1ns / 1ps
module axi4_data_crossbar_tb; module axi4_data_crossbar_tb;
localparam int NumMasters = 9; localparam int NumMasters = 10;
localparam int NumTargets = 6; localparam int NumTargets = 6;
logic clk_i = 1'b0; logic clk_i = 1'b0;
logic rst_n_i = 1'b0; logic rst_n_i = 1'b0;
logic idle_o; logic idle_o;
logic [ 8:0] master_block_i = '0; logic [ 9:0] master_block_i = '0;
logic [ 8:0] master_idle_o; logic [ 9:0] master_idle_o;
logic [ 7:0] outstanding_read_o; logic [ 7:0] outstanding_read_o;
logic [ 7:0] outstanding_write_o; logic [ 7:0] outstanding_write_o;
logic fault_valid_o; logic fault_valid_o;
@@ -20,7 +20,7 @@ module axi4_data_crossbar_tb;
logic [ 3:0] fault_reason_o; logic [ 3:0] fault_reason_o;
logic recovery_i = 1'b0; logic recovery_i = 1'b0;
logic flush_i = 1'b0; logic flush_i = 1'b0;
logic [ 8:0] monitor_master_promotion_o; logic [ 9:0] monitor_master_promotion_o;
axi4_if #( axi4_if #(
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
@@ -51,13 +51,35 @@ module axi4_data_crossbar_tb;
axi4_data_crossbar #( axi4_data_crossbar #(
.StarvationCycles(4), .StarvationCycles(4),
.ReadTargetMask( .ReadTargetMask(
'{5'b11111, 5'b11111, 5'b11111, 5'b11111, 5'b11111, 5'b11111, 5'b11111, 5'b11111, 5'b11111} '{
5'b11111,
5'b11111,
5'b11111,
5'b11111,
5'b11111,
5'b11111,
5'b11111,
5'b11111,
5'b11111,
5'b11111
}
), ),
.WriteTargetMask( .WriteTargetMask(
'{5'b01111, 5'b01111, 5'b01111, 5'b01111, 5'b01111, 5'b01111, 5'b01111, 5'b01111, 5'b00000} '{
5'b01111,
5'b01111,
5'b01111,
5'b01111,
5'b01111,
5'b01111,
5'b01111,
5'b01111,
5'b01111,
5'b00000
}
), ),
.AllowInstruction(9'b000000001), .AllowInstruction(10'b0000000001),
.RequireNoncacheable(9'b111111100) .RequireNoncacheable(10'b1111111100)
) u_dut ( ) u_dut (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
@@ -248,6 +270,7 @@ module axi4_data_crossbar_tb;
`INIT_MASTER(6) `INIT_MASTER(6)
`INIT_MASTER(7) `INIT_MASTER(7)
`INIT_MASTER(8) `INIT_MASTER(8)
`INIT_MASTER(9)
`INIT_TARGET(0) `INIT_TARGET(0)
`INIT_TARGET(1) `INIT_TARGET(1)
`INIT_TARGET(2) `INIT_TARGET(2)
@@ -727,10 +750,10 @@ module axi4_data_crossbar_tb;
end end
invalid_return_prefix_recovery : begin invalid_return_prefix_recovery : begin
for (int prefix = 9; prefix < 16; prefix++) begin for (int prefix = 10; prefix < 16; prefix++) begin
return_invalid_target0_response(4'(prefix), prefix[0]); return_invalid_target0_response(4'(prefix), prefix[0]);
if (prefix == 9) begin if (prefix == 10) begin
masters[8].arid = 7'h42; masters[8].arid = 7'h42;
masters[8].araddr = 32'h3000_0140; masters[8].araddr = 32'h3000_0140;
masters[8].arvalid = 1'b1; masters[8].arvalid = 1'b1;
+12 -9
View File
@@ -3,14 +3,14 @@
module fabric_monitor_tb; module fabric_monitor_tb;
logic clk_i = 1'b0; logic clk_i = 1'b0;
logic rst_n_i = 1'b0; logic rst_n_i = 1'b0;
logic [ 8:0] master_read_accept_i = '0; logic [ 9:0] master_read_accept_i = '0;
logic [ 8:0] master_write_accept_i = '0; logic [ 9:0] master_write_accept_i = '0;
logic [ 8:0] master_read_beat_i = '0; logic [ 9:0] master_read_beat_i = '0;
logic [ 8:0] master_write_beat_i = '0; logic [ 9:0] master_write_beat_i = '0;
logic [ 8:0] master_wait_i = '0; logic [ 9:0] master_wait_i = '0;
logic [ 8:0] master_promotion_i = '0; logic [ 9:0] master_promotion_i = '0;
logic [ 8:0][2:0] master_read_outstanding_i = '0; logic [ 9:0][2:0] master_read_outstanding_i = '0;
logic [ 8:0][2:0] master_write_outstanding_i = '0; logic [ 9:0][2:0] master_write_outstanding_i = '0;
logic [ 5:0] target_read_accept_i = '0; logic [ 5:0] target_read_accept_i = '0;
logic [ 5:0] target_write_accept_i = '0; logic [ 5:0] target_write_accept_i = '0;
logic [ 5:0] target_read_beat_i = '0; logic [ 5:0] target_read_beat_i = '0;
@@ -115,11 +115,12 @@ module fabric_monitor_tb;
apb_read(12'h004, read_data); apb_read(12'h004, read_data);
if (read_data != 32'h0001_0001) $fatal(1, "fabric monitor version mismatch"); if (read_data != 32'h0001_0001) $fatal(1, "fabric monitor version mismatch");
apb_read(12'h008, read_data); apb_read(12'h008, read_data);
if (read_data != 32'h0609_0003) $fatal(1, "fabric monitor capability mismatch"); if (read_data != 32'h060A_0003) $fatal(1, "fabric monitor capability mismatch");
apb_write(12'h00C, 32'h0000_0001); apb_write(12'h00C, 32'h0000_0001);
@(negedge clk_i); @(negedge clk_i);
master_read_accept_i[0] = 1'b1; master_read_accept_i[0] = 1'b1;
master_read_accept_i[8] = 1'b1; master_read_accept_i[8] = 1'b1;
master_read_accept_i[9] = 1'b1;
master_read_beat_i[0] = 1'b1; master_read_beat_i[0] = 1'b1;
master_promotion_i[0] = 1'b1; master_promotion_i[0] = 1'b1;
master_read_outstanding_i[0] = 3'd3; master_read_outstanding_i[0] = 3'd3;
@@ -154,6 +155,8 @@ module fabric_monitor_tb;
if (read_data != 32'd1) $fatal(1, "master read request count mismatch"); if (read_data != 32'd1) $fatal(1, "master read request count mismatch");
apb_read(12'h200, read_data); apb_read(12'h200, read_data);
if (read_data != 32'd1) $fatal(1, "GA2D master bank request count mismatch"); if (read_data != 32'd1) $fatal(1, "GA2D master bank request count mismatch");
apb_read(12'h220, read_data);
if (read_data != 32'd1) $fatal(1, "NPU master bank request count mismatch");
apb_read(12'h110, read_data); apb_read(12'h110, read_data);
if (read_data != 32'd3) $fatal(1, "master wait count mismatch"); if (read_data != 32'd3) $fatal(1, "master wait count mismatch");
apb_read(12'h114, read_data); apb_read(12'h114, read_data);
+17 -3
View File
@@ -120,7 +120,7 @@ module ga2d_platform_tb;
logic block_new_i = 1'b0; logic block_new_i = 1'b0;
logic recovery_i = 1'b0; logic recovery_i = 1'b0;
logic flush_i = 1'b0; logic flush_i = 1'b0;
logic [ 8:0] resource_block_i = '0; logic [ 9:0] resource_block_i = '0;
logic [ 1:0] mem_pad_mode_i = 2'd1; logic [ 1:0] mem_pad_mode_i = 2'd1;
logic ga2d_core_safe_idle_i = 1'b1; logic ga2d_core_safe_idle_i = 1'b1;
logic ext_h_block_i = 1'b0; logic ext_h_block_i = 1'b0;
@@ -141,8 +141,8 @@ module ga2d_platform_tb;
logic ga2d_bridge_clear_busy_o; logic ga2d_bridge_clear_busy_o;
logic [ 7:0] ga2d_bridge_epoch_o; logic [ 7:0] ga2d_bridge_epoch_o;
logic ga2d_data_ready_o; logic ga2d_data_ready_o;
logic [ 8:0] resource_idle_o; logic [ 9:0] resource_idle_o;
logic [ 8:0] resource_block_ack_o; logic [ 9:0] resource_block_ack_o;
logic [ 7:0] outstanding_read_o; logic [ 7:0] outstanding_read_o;
logic [ 7:0] outstanding_write_o; logic [ 7:0] outstanding_write_o;
logic fault_valid_o; logic fault_valid_o;
@@ -243,6 +243,15 @@ module ga2d_platform_tb;
.aclk (clk_io_i), .aclk (clk_io_i),
.aresetn(rst_io_n_i) .aresetn(rst_io_n_i)
); );
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (3),
.USER_WIDTH(1)
) npu_axi4 (
.aclk (clk_hp_i),
.aresetn(rst_hp_n_i)
);
axi4_if #( axi4_if #(
.ADDR_WIDTH(32), .ADDR_WIDTH(32),
.DATA_WIDTH(32), .DATA_WIDTH(32),
@@ -324,6 +333,7 @@ module ga2d_platform_tb;
axi4_master_idle u_usb2_idle (.axi4(usb2_axi4)); axi4_master_idle u_usb2_idle (.axi4(usb2_axi4));
axi4_master_idle u_apu_idle (.axi4(apu_axi4)); axi4_master_idle u_apu_idle (.axi4(apu_axi4));
axi4_master_idle u_ext_h_idle (.axi4(ext_h_axi4)); axi4_master_idle u_ext_h_idle (.axi4(ext_h_axi4));
axi4_master_idle u_npu_idle (.axi4(npu_axi4));
ga2d_platform_sram_target u_sram_target ( ga2d_platform_sram_target u_sram_target (
.clk_i (clk_hp_i), .clk_i (clk_hp_i),
.rst_n_i (rst_hp_n_i), .rst_n_i (rst_hp_n_i),
@@ -372,6 +382,10 @@ module ga2d_platform_tb;
.apu_axi4 (apu_axi4), .apu_axi4 (apu_axi4),
.jpeg_axi4 (jpeg_axi4), .jpeg_axi4 (jpeg_axi4),
.ga2d_axi4 (ga2d_axi4), .ga2d_axi4 (ga2d_axi4),
.npu_axi4 (npu_axi4),
.npu_source_idle_i (1'b1),
.npu_source_quiesced_i (1'b1),
.npu_flush_busy_i (1'b0),
.lp_data_axi4 (lp_data_axi4), .lp_data_axi4 (lp_data_axi4),
.ext_h_axi4 (ext_h_axi4), .ext_h_axi4 (ext_h_axi4),
.sram_gateway_axi4 (sram_gateway_axi4), .sram_gateway_axi4 (sram_gateway_axi4),
+489
View File
@@ -0,0 +1,489 @@
`timescale 1ns / 1ps
// NPU-P2 adversarial control-CDC test: two independent clock domains (10 ns
// PCLK, 14 ns HP, drifting phases), snapshot round trips, either-side reset
// mid-handshake, epoch reconciliation with generation counting including the
// counter wrap, double and rapid reset sequences, quiesce/reset round-trip
// acknowledgement timing, immediate idle clock-pause acknowledge, and fabric
// flush on an idle endpoint (no artificial cancellation event).
`include "npu_define.svh"
module npu_control_cdc_tb;
localparam int unsigned LaunchWidth = `APB4_NPU__LAUNCH_PAYLOAD_WIDTH;
localparam int unsigned ResultWidth = `APB4_NPU__RESULT_PAYLOAD_WIDTH;
localparam int unsigned SnapWidth = `APB4_NPU__SNAPSHOT_PAYLOAD_WIDTH;
localparam int unsigned EpochWidth = `APB4_NPU__EPOCH_WIDTH;
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 s_launch_req_valid = 1'b0;
logic s_launch_req_ready;
logic [LaunchWidth-1:0] s_launch_req_payload = '0;
logic s_result_valid;
logic s_result_ready = 1'b1;
logic [ResultWidth-1:0] s_result_payload;
logic s_snap_req_valid = 1'b0;
logic s_snap_req_ready;
logic s_snap_resp_valid;
logic s_snap_resp_ready = 1'b0;
logic [ SnapWidth-1:0] s_snap_resp_payload;
logic s_soft_reset = 1'b0;
logic s_shell_idle = 1'b1;
logic s_resource_quiesce = 1'b0;
logic s_resource_reset = 1'b0;
logic s_link_up;
logic s_flush_active;
logic [ 31:0] s_generation;
logic s_block_ack;
logic hp_launch_valid;
logic hp_launch_ready;
logic [LaunchWidth-1:0] hp_launch_payload;
logic hp_result_valid;
logic hp_result_ready;
logic [ResultWidth-1:0] hp_result_payload;
logic hp_snap_req_valid;
logic hp_snap_req_ready;
logic hp_snap_resp_valid;
logic hp_snap_resp_ready;
logic [ SnapWidth-1:0] hp_snap_resp_payload;
logic [ EpochWidth-1:0] hp_epoch_req;
logic [ EpochWidth-1:0] hp_epoch_ack;
logic hp_quiesce_req;
logic hp_quiesce_ack;
logic hp_busy;
logic hp_draining;
logic hp_pause_active;
logic hp_busy_pclk;
logic hp_draining_pclk;
logic clock_paused;
logic hp_block_new = 1'b0;
logic hp_pause_ack;
logic hp_flush = 1'b0;
logic hp_flush_busy;
logic hp_idle;
int unsigned s_wait_cycles;
int unsigned s_ack_latency;
string s_phase;
always #5 clk_i = ~clk_i;
always #7 clk_hp_i = ~clk_hp_i;
npu_control_cdc u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.clk_hp_i (clk_hp_i),
.rst_hp_n_i (rst_hp_n_i),
.launch_req_valid_i (s_launch_req_valid),
.launch_req_ready_o (s_launch_req_ready),
.launch_req_payload_i (s_launch_req_payload),
.result_valid_o (s_result_valid),
.result_ready_i (s_result_ready),
.result_payload_o (s_result_payload),
.snapshot_req_valid_i (s_snap_req_valid),
.snapshot_req_ready_o (s_snap_req_ready),
.snapshot_resp_valid_o (s_snap_resp_valid),
.snapshot_resp_ready_i (s_snap_resp_ready),
.snapshot_resp_payload_o (s_snap_resp_payload),
.soft_reset_i (s_soft_reset),
.shell_idle_i (s_shell_idle),
.resource_quiesce_i (s_resource_quiesce),
.resource_reset_i (s_resource_reset),
.link_up_o (s_link_up),
.flush_active_o (s_flush_active),
.recovery_generation_o (s_generation),
.block_ack_o (s_block_ack),
.hp_launch_valid_o (hp_launch_valid),
.hp_launch_ready_i (hp_launch_ready),
.hp_launch_payload_o (hp_launch_payload),
.hp_result_valid_i (hp_result_valid),
.hp_result_ready_o (hp_result_ready),
.hp_result_payload_i (hp_result_payload),
.hp_snapshot_req_valid_o (hp_snap_req_valid),
.hp_snapshot_req_ready_i (hp_snap_req_ready),
.hp_snapshot_resp_valid_i (hp_snap_resp_valid),
.hp_snapshot_resp_ready_o (hp_snap_resp_ready),
.hp_snapshot_resp_payload_i(hp_snap_resp_payload),
.hp_epoch_req_o (hp_epoch_req),
.hp_epoch_ack_i (hp_epoch_ack),
.hp_quiesce_req_o (hp_quiesce_req),
.hp_quiesce_ack_i (hp_quiesce_ack),
.hp_busy_i (hp_busy),
.hp_draining_i (hp_draining),
.hp_pause_active_i (hp_pause_active),
.hp_flush_i (hp_flush),
.hp_busy_o (hp_busy_pclk),
.hp_draining_o (hp_draining_pclk),
.clock_paused_o (clock_paused)
);
npu_core u_core (
.clk_hp_i (clk_hp_i),
.rst_hp_n_i (rst_hp_n_i),
.launch_valid_i (hp_launch_valid),
.launch_ready_o (hp_launch_ready),
.launch_data_i (hp_launch_payload),
.result_valid_o (hp_result_valid),
.result_ready_i (hp_result_ready),
.result_data_o (hp_result_payload),
.snapshot_req_valid_i (hp_snap_req_valid),
.snapshot_req_ready_o (hp_snap_req_ready),
.snapshot_resp_valid_o(hp_snap_resp_valid),
.snapshot_resp_ready_i(hp_snap_resp_ready),
.snapshot_resp_data_o (hp_snap_resp_payload),
.epoch_req_i (hp_epoch_req),
.epoch_ack_o (hp_epoch_ack),
.quiesce_req_i (hp_quiesce_req),
.quiesce_ack_o (hp_quiesce_ack),
.busy_o (hp_busy),
.draining_o (hp_draining),
.block_new_i (hp_block_new),
.clock_pause_ack_o (hp_pause_ack),
.flush_i (hp_flush),
.flush_busy_o (hp_flush_busy),
.pause_active_o (hp_pause_active),
.idle_o (hp_idle)
);
// No terminal result may ever appear at Phase 2, in any scenario.
always @(posedge clk_i) begin
if (rst_n_i && s_result_valid) $fatal(1, "NPU CDC produced a result at Phase 2");
end
task automatic wait_link_up;
begin
s_wait_cycles = 0;
while (!s_link_up) begin
@(posedge clk_i);
s_wait_cycles++;
if (s_wait_cycles > 2000) $fatal(1, "NPU CDC link did not reconcile in %s", s_phase);
end
end
endtask
task automatic wait_link_down;
begin
s_wait_cycles = 0;
while (!s_flush_active) begin
@(posedge clk_i);
s_wait_cycles++;
if (s_wait_cycles > 2000) $fatal(1, "NPU CDC link did not drop in %s", s_phase);
end
end
endtask
task automatic wait_block_ack(input logic expected_i);
begin
s_wait_cycles = 0;
while (s_block_ack != expected_i) begin
@(posedge clk_i);
s_wait_cycles++;
if (s_wait_cycles > 2000)
$fatal(1, "NPU CDC block_ack %0b timed out in %s", expected_i, s_phase);
end
end
endtask
// A full shell-side snapshot round trip with the response payload check.
task automatic shell_snapshot;
begin
@(negedge clk_i);
s_snap_req_valid = 1'b1;
s_shell_idle = 1'b0;
s_wait_cycles = 0;
while (!s_snap_req_ready) begin
@(posedge clk_i);
s_wait_cycles++;
if (s_wait_cycles > 200) $fatal(1, "NPU snapshot request not accepted in %s", s_phase);
end
@(negedge clk_i);
s_snap_req_valid = 1'b0;
s_snap_resp_ready = 1'b1;
s_wait_cycles = 0;
while (!s_snap_resp_valid) begin
@(posedge clk_i);
s_wait_cycles++;
if (s_wait_cycles > 500) $fatal(1, "NPU snapshot response missing in %s", s_phase);
end
if (s_snap_resp_payload !== {SnapWidth{1'b0}}) begin
$fatal(1, "NPU idle snapshot returned a nonzero payload");
end
@(posedge clk_i);
@(negedge clk_i);
s_snap_resp_ready = 1'b0;
s_shell_idle = 1'b1;
end
endtask
// Launch one payload through the PCLK->HP mailbox and check its integrity.
task automatic shell_launch(input logic [LaunchWidth-1:0] payload_i);
begin
@(negedge clk_i);
s_launch_req_payload = payload_i;
s_launch_req_valid = 1'b1;
s_shell_idle = 1'b0;
s_wait_cycles = 0;
while (!hp_launch_valid) begin
@(posedge clk_hp_i);
s_wait_cycles++;
if (s_wait_cycles > 200) $fatal(1, "NPU launch never reached the HP endpoint");
end
if (hp_launch_payload !== payload_i) begin
$fatal(1, "NPU launch payload mismatch: got %h expected %h", hp_launch_payload, payload_i);
end
s_wait_cycles = 0;
while (!s_launch_req_ready) begin
@(posedge clk_i);
s_wait_cycles++;
if (s_wait_cycles > 200) $fatal(1, "NPU launch source never completed");
end
@(negedge clk_i);
s_launch_req_valid = 1'b0;
s_shell_idle = 1'b1;
end
endtask
task automatic pulse_hp_reset(input int unsigned hold_cycles);
begin
#3 rst_hp_n_i = 1'b0;
repeat (hold_cycles) @(posedge clk_hp_i);
#2 rst_hp_n_i = 1'b1;
end
endtask
task automatic pulse_pclk_reset(input int unsigned hold_cycles);
begin
#2 rst_n_i = 1'b0;
s_snap_req_valid = 1'b0;
s_snap_resp_ready = 1'b0;
s_launch_req_valid = 1'b0;
s_soft_reset = 1'b0;
s_shell_idle = 1'b1;
repeat (hold_cycles) @(posedge clk_i);
#3 rst_n_i = 1'b1;
end
endtask
initial begin
s_phase = "power-on reconcile";
repeat (5) @(posedge clk_i);
#2 rst_hp_n_i = 1'b1;
@(negedge clk_i);
rst_n_i = 1'b1;
wait_link_up();
if (s_generation !== 32'd0) $fatal(1, "NPU initial reconcile changed the generation");
if (s_flush_active) $fatal(1, "NPU link flush stuck after power-on");
if (hp_busy_pclk || hp_draining_pclk || clock_paused) begin
$fatal(1, "NPU idle HP endpoint reported activity");
end
s_phase = "snapshot round trips";
shell_snapshot();
shell_snapshot();
shell_launch({16'd0, 16'd5, 32'h044a_a200, 32'hcafe_babe, 32'h3000_0000});
shell_snapshot();
s_phase = "hp reset mid snapshot";
@(negedge clk_i);
s_snap_req_valid = 1'b1;
s_shell_idle = 1'b0;
#9;
pulse_hp_reset(4);
wait_link_down();
@(negedge clk_i);
s_snap_req_valid = 1'b0;
s_shell_idle = 1'b1;
if (s_snap_resp_valid) $fatal(1, "NPU stale snapshot response survived the HP reset");
wait_link_up();
if (s_generation !== 32'd1) $fatal(1, "NPU HP-reset recovery did not count exactly once");
shell_snapshot();
s_phase = "pclk reset mid snapshot";
@(negedge clk_i);
s_snap_req_valid = 1'b1;
s_shell_idle = 1'b0;
#9;
pulse_pclk_reset(3);
if (s_generation !== 32'd0) $fatal(1, "NPU shell reset did not rezero the generation");
wait_link_up();
if (s_generation !== 32'd0) begin
$fatal(1, "NPU initial reconcile after shell reset changed the generation");
end
shell_snapshot();
s_phase = "pclk reset during snapshot response";
@(negedge clk_i);
s_snap_req_valid = 1'b1;
s_shell_idle = 1'b0;
s_wait_cycles = 0;
while (!s_snap_req_ready) begin
@(posedge clk_i);
s_wait_cycles++;
if (s_wait_cycles > 200) $fatal(1, "NPU snapshot request not accepted");
end
@(negedge clk_i);
s_snap_req_valid = 1'b0;
s_wait_cycles = 0;
while (!s_snap_resp_valid) begin
@(posedge clk_i);
s_wait_cycles++;
if (s_wait_cycles > 500) $fatal(1, "NPU snapshot response missing before reset");
end
pulse_pclk_reset(3);
if (s_snap_resp_valid) $fatal(1, "NPU shell reset kept a stale snapshot response");
wait_link_up();
shell_snapshot();
s_phase = "double reset";
#2 rst_n_i = 1'b0;
#17 rst_hp_n_i = 1'b0;
repeat (4) @(posedge clk_i);
#3 rst_n_i = 1'b1;
repeat (2) @(posedge clk_hp_i);
#2 rst_hp_n_i = 1'b1;
wait_link_up();
if (s_generation !== 32'd0) $fatal(1, "NPU double reset did not rezero the generation");
shell_snapshot();
s_phase = "rapid reset sequence";
for (int unsigned reset_index = 0; reset_index < 3; reset_index++) begin
pulse_hp_reset(2 + reset_index);
end
pulse_pclk_reset(2);
pulse_hp_reset(3);
wait_link_up();
// The PCLK pulse rezeroes the counter and the completing reconcile is the
// post-reset initial one, which does not count as a recovery.
if (s_generation !== 32'd0) begin
$fatal(1, "NPU rapid reset sequence generation mismatch: %0d", s_generation);
end
shell_snapshot();
s_phase = "quiesce round trip";
s_wait_cycles = 0;
@(negedge clk_i);
s_resource_quiesce = 1'b1;
#1;
if (s_block_ack) $fatal(1, "NPU quiesce acknowledged combinationally");
while (!s_block_ack) begin
@(posedge clk_i);
s_wait_cycles++;
if (s_wait_cycles > 200) $fatal(1, "NPU quiesce acknowledgement timed out");
end
if (s_wait_cycles < 3) $fatal(1, "NPU quiesce acknowledged without a CDC round trip");
s_ack_latency = s_wait_cycles;
shell_snapshot();
if (s_generation !== 32'd0) $fatal(1, "NPU idle quiesce changed the generation");
@(negedge clk_i);
s_resource_quiesce = 1'b0;
wait_block_ack(1'b0);
s_phase = "resource reset handshake";
@(negedge clk_i);
s_resource_reset = 1'b1;
#1;
if (s_block_ack) $fatal(1, "NPU reset acknowledged combinationally");
wait_link_down();
s_wait_cycles = 0;
while (!s_block_ack) begin
@(posedge clk_i);
s_wait_cycles++;
if (s_wait_cycles > 200) $fatal(1, "NPU reset acknowledgement timed out");
end
if (s_wait_cycles < 3) $fatal(1, "NPU reset acknowledged without a fresh epoch");
@(negedge clk_i);
s_resource_reset = 1'b0;
wait_block_ack(1'b0);
wait_link_up();
if (s_generation !== 32'd1) $fatal(1, "NPU resource reset recovery miscounted");
s_phase = "reset cancels pending snapshot before ack";
@(negedge clk_i);
s_snap_req_valid = 1'b1;
s_shell_idle = 1'b0;
#6;
@(negedge clk_i);
s_resource_reset = 1'b1;
wait_link_down();
@(negedge clk_i);
s_snap_req_valid = 1'b0;
repeat (10) @(posedge clk_i);
if (s_block_ack) begin
$fatal(1, "NPU reset acknowledged with a pending shell snapshot");
end
s_shell_idle = 1'b1;
wait_block_ack(1'b1);
@(negedge clk_i);
s_resource_reset = 1'b0;
wait_link_up();
if (s_snap_resp_valid) $fatal(1, "NPU stale snapshot response survived the reset");
shell_snapshot();
s_phase = "hp reset during quiesce window";
@(negedge clk_i);
s_resource_quiesce = 1'b1;
wait_block_ack(1'b1);
pulse_hp_reset(3);
wait_block_ack(1'b0);
wait_block_ack(1'b1);
@(negedge clk_i);
s_resource_quiesce = 1'b0;
wait_block_ack(1'b0);
wait_link_up();
s_phase = "clock pause and flush on idle";
@(negedge clk_hp_i);
hp_block_new = 1'b1;
#1;
if (!hp_pause_ack) $fatal(1, "NPU idle clock pause was not acknowledged immediately");
if (!hp_idle) $fatal(1, "NPU paused idle endpoint held global idle low");
shell_snapshot();
@(negedge clk_hp_i);
hp_block_new = 1'b0;
#1;
if (hp_pause_ack) $fatal(1, "NPU clock pause acknowledge stuck");
begin
logic [31:0] s_generation_before_flush;
s_generation_before_flush = s_generation;
@(negedge clk_hp_i);
hp_flush = 1'b1;
repeat (4) @(posedge clk_hp_i);
if (hp_flush_busy) $fatal(1, "NPU idle endpoint reported flush drain work");
@(negedge clk_hp_i);
hp_flush = 1'b0;
wait_link_up();
if (s_result_valid) $fatal(1, "NPU idle flush invented a cancellation event");
if (s_generation != s_generation_before_flush + 32'd1) begin
$fatal(1, "NPU flush did not advance the epoch generation exactly once");
end
end
s_phase = "generation wrap";
// Hold the poke for one PCLK edge so the register primitive internalizes
// it; the steady-state d=q feedback then retains the poked value.
@(negedge clk_i);
force u_dut.s_generation_q = 32'hffff_fffe;
@(posedge clk_i);
#1;
if (u_dut.s_generation_q != 32'hffff_fffe) $fatal(1, "NPU generation poke failed");
release u_dut.s_generation_q;
if (u_dut.s_generation_q != 32'hffff_fffe) $fatal(1, "NPU generation poke did not hold");
pulse_hp_reset(3);
wait_link_up();
if (s_generation !== 32'hffff_ffff) $fatal(1, "NPU generation did not reach all ones");
pulse_hp_reset(3);
wait_link_up();
if (s_generation !== 32'd0) $fatal(1, "NPU generation did not wrap to zero");
shell_snapshot();
s_phase = "done";
$display("NPU control CDC test passed");
$finish;
end
initial begin
repeat (200000) @(posedge clk_i);
$fatal(1, "NPU control CDC test timed out in %s", s_phase);
end
endmodule
+744
View File
@@ -0,0 +1,744 @@
`timescale 1ns / 1ps
// NPU-P2 APB register matrix test: every documented offset, access classes,
// alignment/strobe enforcement, CONTROL exact-one-bit semantics with START
// always rejected, IRQ test/W1C/enable gating with set-wins-clear, live
// OWNER_STATUS, snapshot round trip with zero bank and no tear, recovery
// generation counting, and the P2 truthfulness invariants (READY=0,
// RESULT_VALID never set).
`include "npu_define.svh"
module npu_reg_tb;
localparam logic [31:0] NpuBase = 32'h1001_b000;
localparam int unsigned RegisterCount = 52;
localparam logic [1:0] AccessRo = 2'd0;
localparam logic [1:0] AccessWo = 2'd1;
localparam logic [1:0] AccessRw = 2'd2;
logic clk_i = 1'b0;
logic clk_hp_i = 1'b0;
logic rst_n_i = 1'b0;
logic rst_hp_n_i = 1'b0;
logic [ 1:0] resource_owner_i = 2'd0;
logic resource_owner_lock_i = 1'b0;
logic resource_quiesce_i = 1'b0;
logic resource_reset_i = 1'b0;
logic idle_o;
logic block_ack_o;
logic irq_o;
logic hp_block_new_i = 1'b0;
logic hp_pause_ack_o;
logic hp_flush_i = 1'b0;
logic hp_flush_busy_o;
logic hp_idle_o;
logic [31:0] s_value;
logic [11:0] s_reg_offset [RegisterCount];
logic [ 1:0] s_reg_access [RegisterCount];
logic [31:0] s_reg_reset [RegisterCount];
logic s_reg_full_strobe [RegisterCount];
int unsigned s_reg_count;
int unsigned s_ack_wait_cycles;
logic [11:0] s_bad_offset;
logic [11:0] s_unknown_offsets [ 6];
logic [31:0] s_bad_values [ 20];
string s_phase;
apb4_if apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(64),
.ID_WIDTH (3),
.USER_WIDTH(1)
) npu_axi4 (
clk_hp_i,
rst_hp_n_i
);
assign npu_axi4.awready = 1'b0;
assign npu_axi4.wready = 1'b0;
assign npu_axi4.bid = '0;
assign npu_axi4.bresp = '0;
assign npu_axi4.buser = '0;
assign npu_axi4.bvalid = 1'b0;
assign npu_axi4.arready = 1'b0;
assign npu_axi4.rid = '0;
assign npu_axi4.rdata = '0;
assign npu_axi4.rresp = '0;
assign npu_axi4.rlast = 1'b0;
assign npu_axi4.ruser = '0;
assign npu_axi4.rvalid = 1'b0;
always #5 clk_i = ~clk_i;
always #7 clk_hp_i = ~clk_hp_i;
apb4_npu u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.clk_hp_i (clk_hp_i),
.rst_hp_n_i (rst_hp_n_i),
.resource_owner_i (resource_owner_i),
.resource_owner_lock_i(resource_owner_lock_i),
.resource_quiesce_i (resource_quiesce_i),
.resource_reset_i (resource_reset_i),
.apb4 (apb4),
.idle_o (idle_o),
.block_ack_o (block_ack_o),
.irq_o (irq_o),
.hp_block_new_i (hp_block_new_i),
.hp_pause_ack_o (hp_pause_ack_o),
.hp_flush_i (hp_flush_i),
.hp_flush_busy_o (hp_flush_busy_o),
.hp_idle_o (hp_idle_o),
.npu_axi4 (npu_axi4)
);
function automatic logic [31:0] legal_write_value(input logic [11:0] offset_i);
case (offset_i)
`APB4_NPU__IRQ_STATE: legal_write_value = 32'h0000_0007;
`APB4_NPU__IRQ_ENABLE: legal_write_value = 32'h0000_0005;
`APB4_NPU__JOB_BASE: legal_write_value = 32'h3000_0000;
`APB4_NPU__JOB_COUNT: legal_write_value = 32'h0000_0040;
`APB4_NPU__JOB_ID: legal_write_value = 32'ha5a5_5a5a;
`APB4_NPU__TIMEOUT_CYCLES: legal_write_value = 32'h044a_a200;
`APB4_NPU__PERF_CONTROL: legal_write_value = 32'h0000_0001;
default: legal_write_value = 32'd0;
endcase
endfunction
function automatic logic [31:0] legal_readback_value(input logic [11:0] offset_i);
case (offset_i)
`APB4_NPU__IRQ_STATE: legal_readback_value = 32'd0;
default: legal_readback_value = legal_write_value(offset_i);
endcase
endfunction
task automatic add_register(input logic [11:0] offset_i, input logic [1:0] access_i,
input logic [31:0] reset_i, input logic full_strobe_i);
begin
if (s_reg_count >= RegisterCount) $fatal(1, "NPU register table overflow");
s_reg_offset[s_reg_count] = offset_i;
s_reg_access[s_reg_count] = access_i;
s_reg_reset[s_reg_count] = reset_i;
s_reg_full_strobe[s_reg_count] = full_strobe_i;
s_reg_count++;
end
endtask
task automatic initialize_register_table;
begin
s_reg_count = 0;
add_register(`APB4_NPU__IP_ID, AccessRo, `APB4_NPU__IP_ID_VALUE, 1'b1);
add_register(`APB4_NPU__IP_VERSION, AccessRo, `APB4_NPU__IP_VERSION_VALUE, 1'b1);
add_register(`APB4_NPU__CAPABILITY, AccessRo, `APB4_NPU__CAPABILITY_P2, 1'b1);
add_register(`APB4_NPU__STATUS, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__CONTROL, AccessWo, 32'd0, 1'b1);
add_register(`APB4_NPU__IRQ_STATE, AccessRw, 32'd0, 1'b1);
add_register(`APB4_NPU__IRQ_ENABLE, AccessRw, 32'd0, 1'b1);
add_register(`APB4_NPU__IRQ_TEST, AccessWo, 32'd0, 1'b1);
add_register(`APB4_NPU__JOB_BASE, AccessRw, 32'd0, 1'b1);
add_register(`APB4_NPU__JOB_COUNT, AccessRw, 32'd0, 1'b1);
add_register(`APB4_NPU__JOB_ID, AccessRw, 32'd0, 1'b1);
add_register(`APB4_NPU__TIMEOUT_CYCLES, AccessRw, 32'd0, 1'b1);
add_register(`APB4_NPU__RESULT_JOB_ID, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__RESULT_CODE, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__COMPLETED_DESCRIPTORS, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__FAULT_DESCRIPTOR, AccessRo, `APB4_NPU__FAULT_DESCRIPTOR_RESET, 1'b1);
add_register(`APB4_NPU__FAULT_CODE, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__FAULT_ADDRESS, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__FAULT_INFO, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__NUMERIC_PROFILE, AccessRo, `APB4_NPU__NUMERIC_PROFILE_VALUE, 1'b1);
add_register(`APB4_NPU__LOCAL_BYTES, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__MAC_CONFIG, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__MAX_K_SLICE, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__MAX_DIMENSION, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__OP_CAPABILITY, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__OWNER_STATUS, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__RECOVERY_GENERATION, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_CONTROL, AccessWo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_STATUS, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_JOB_ID, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_GENERATION, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__DESCRIPTOR_BYTES, AccessRo, `APB4_NPU__DESCRIPTOR_BYTES_VALUE, 1'b1);
add_register(`APB4_NPU__PERF_ACTIVE_CYCLES_LO, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_ACTIVE_CYCLES_HI, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_CLOCK_PAUSE_CYCLES_LO, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_CLOCK_PAUSE_CYCLES_HI, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_USEFUL_MACS_LO, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_USEFUL_MACS_HI, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_PACK_CYCLES_LO, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_PACK_CYCLES_HI, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_LOCAL_BANK_STALL_LO, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_LOCAL_BANK_STALL_HI, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_DMA_READ_BYTES_LO, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_DMA_READ_BYTES_HI, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_DMA_WRITE_BYTES_LO, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_DMA_WRITE_BYTES_HI, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_DMA_STALL_CYCLES_LO, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_DMA_STALL_CYCLES_HI, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_REQUANT_STALL_LO, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_REQUANT_STALL_HI, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_RETIRED_DESCRIPTORS_LO, AccessRo, 32'd0, 1'b1);
add_register(`APB4_NPU__PERF_RETIRED_DESCRIPTORS_HI, AccessRo, 32'd0, 1'b1);
if (s_reg_count != RegisterCount) begin
$fatal(1, "NPU register table has %0d entries, expected %0d", s_reg_count, RegisterCount);
end
end
endtask
task automatic drive_apb_idle;
begin
apb4.paddr = 32'd0;
apb4.pprot = 3'd0;
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pwdata = 32'd0;
apb4.pstrb = 4'd0;
end
endtask
task automatic wait_link_up;
int unsigned s_polls;
begin
s_polls = 0;
apb_read(`APB4_NPU__STATUS, s_value, 1'b0);
while (s_value[`APB4_NPU__STATUS_RECOVERING] != 1'b0) begin
s_polls++;
if (s_polls > 200) $fatal(1, "NPU link did not reconcile after reset");
apb_read(`APB4_NPU__STATUS, s_value, 1'b0);
end
end
endtask
task automatic wait_snapshot_valid;
int unsigned s_polls;
begin
s_polls = 0;
apb_read(`APB4_NPU__PERF_STATUS, s_value, 1'b0);
while (s_value[`APB4_NPU__PERF_STATUS_SNAP_VALID] != 1'b1) begin
s_polls++;
if (s_polls > 500) $fatal(1, "NPU snapshot did not complete");
apb_read(`APB4_NPU__PERF_STATUS, s_value, 1'b0);
end
end
endtask
task automatic hard_reset;
begin
@(negedge clk_i);
drive_apb_idle();
resource_owner_i = 2'd0;
resource_owner_lock_i = 1'b0;
resource_quiesce_i = 1'b0;
resource_reset_i = 1'b0;
hp_block_new_i = 1'b0;
hp_flush_i = 1'b0;
rst_n_i = 1'b0;
repeat (3) @(posedge clk_i);
@(negedge clk_i);
rst_n_i = 1'b1;
wait_link_up();
end
endtask
task automatic pulse_hp_reset;
begin
@(negedge clk_hp_i);
rst_hp_n_i = 1'b0;
repeat (4) @(posedge clk_hp_i);
@(negedge clk_hp_i);
rst_hp_n_i = 1'b1;
wait_link_up();
end
endtask
task automatic apb_write(input logic [11:0] offset_i, input logic [31:0] data_i,
input logic [3:0] strobe_i, input logic expected_err_i);
int unsigned s_wait_cycles;
logic s_response_err;
begin
@(negedge clk_i);
apb4.paddr = NpuBase + offset_i;
apb4.pwdata = data_i;
apb4.pstrb = strobe_i;
apb4.pwrite = 1'b1;
apb4.psel = 1'b1;
apb4.penable = 1'b0;
#1;
if (apb4.pready) $fatal(1, "NPU PREADY asserted during write setup at %h", offset_i);
@(negedge clk_i);
apb4.penable = 1'b1;
s_wait_cycles = 0;
#1;
while (!apb4.pready) begin
s_wait_cycles++;
if (s_wait_cycles > 4) $fatal(1, "NPU write wait timeout at %h", offset_i);
@(negedge clk_i);
#1;
end
if (s_wait_cycles != 1) begin
$fatal(1, "NPU write wait count mismatch at %h: %0d", offset_i, s_wait_cycles);
end
if (apb4.pslverr != expected_err_i) begin
$fatal(1, "NPU 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
$fatal(1, "NPU write response was not retained through completion at %h", offset_i);
end
@(negedge clk_i);
drive_apb_idle();
end
endtask
task automatic apb_read(input logic [11:0] offset_i, output logic [31:0] data_o,
input logic expected_err_i);
int unsigned s_wait_cycles;
logic [31:0] s_response_data;
logic s_response_err;
begin
@(negedge clk_i);
apb4.paddr = NpuBase + offset_i;
apb4.pstrb = 4'd0;
apb4.pwrite = 1'b0;
apb4.psel = 1'b1;
apb4.penable = 1'b0;
#1;
if (apb4.pready) $fatal(1, "NPU PREADY asserted during read setup at %h", offset_i);
@(negedge clk_i);
apb4.penable = 1'b1;
s_wait_cycles = 0;
#1;
while (!apb4.pready) begin
s_wait_cycles++;
if (s_wait_cycles > 4) $fatal(1, "NPU read wait timeout at %h", offset_i);
@(negedge clk_i);
#1;
end
if (s_wait_cycles != 1) begin
$fatal(1, "NPU read wait count mismatch at %h: %0d", offset_i, s_wait_cycles);
end
if (apb4.pslverr != expected_err_i) begin
$fatal(1, "NPU read response mismatch at %h during %s", offset_i, s_phase);
end
s_response_data = apb4.prdata;
s_response_err = apb4.pslverr;
#2;
if (!apb4.pready || (apb4.prdata != s_response_data) ||
(apb4.pslverr != s_response_err)) begin
$fatal(1, "NPU read response was not retained through completion at %h", offset_i);
end
data_o = s_response_data;
@(negedge clk_i);
drive_apb_idle();
end
endtask
task automatic expect_read(input logic [11:0] offset_i, input logic [31:0] expected_i);
begin
apb_read(offset_i, s_value, 1'b0);
if (s_value != expected_i) begin
$fatal(1, "NPU read mismatch at %h: got %h expected %h", offset_i, s_value, expected_i);
end
end
endtask
task automatic check_reset_state;
begin
for (int unsigned register_index = 0; register_index < RegisterCount; register_index++) begin
if (s_reg_access[register_index] != AccessWo) begin
expect_read(s_reg_offset[register_index], s_reg_reset[register_index]);
end
end
end
endtask
task automatic expect_perf_bank_zero;
begin
for (int unsigned counter_index = 0; counter_index < 10; counter_index++) begin
expect_read(12'(`APB4_NPU__PERF_ACTIVE_CYCLES_LO + 8 * counter_index), 32'd0);
expect_read(12'(`APB4_NPU__PERF_ACTIVE_CYCLES_LO + 8 * counter_index + 4), 32'd0);
end
end
endtask
initial begin
initialize_register_table();
s_phase = "initial reset";
drive_apb_idle();
@(negedge clk_i);
rst_n_i = 1'b0;
rst_hp_n_i = 1'b0;
repeat (4) @(posedge clk_i);
@(negedge clk_i);
rst_hp_n_i = 1'b1;
@(negedge clk_i);
rst_n_i = 1'b1;
wait_link_up();
if (!idle_o || irq_o) $fatal(1, "NPU reset lifecycle outputs mismatch");
check_reset_state();
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
apb_read(s_reg_offset[register_index], s_value, 1'b1);
end else begin
expect_read(s_reg_offset[register_index], s_reg_reset[register_index]);
end
end
for (int unsigned register_index = 0; register_index < RegisterCount; register_index++) begin
hard_reset();
case (s_reg_access[register_index])
AccessRo: begin
apb_write(`APB4_NPU__TIMEOUT_CYCLES, 32'h044a_a200, 4'hf, 1'b0);
apb_write(s_reg_offset[register_index], 32'hffff_ffff, 4'hf, 1'b1);
expect_read(`APB4_NPU__TIMEOUT_CYCLES, 32'h044a_a200);
expect_read(s_reg_offset[register_index], s_reg_reset[register_index]);
end
AccessWo: begin
apb_read(s_reg_offset[register_index], s_value, 1'b1);
hard_reset();
if (s_reg_offset[register_index] == `APB4_NPU__CONTROL) begin
apb_write(s_reg_offset[register_index], 32'h0000_0004, 4'hf, 1'b0);
wait_link_up();
end else if (s_reg_offset[register_index] == `APB4_NPU__IRQ_TEST) begin
apb_write(s_reg_offset[register_index], 32'd0, 4'hf, 1'b0);
end else begin
apb_write(s_reg_offset[register_index], 32'h0000_0001, 4'hf, 1'b0);
wait_snapshot_valid();
end
end
AccessRw: begin
apb_write(s_reg_offset[register_index], legal_write_value(s_reg_offset[register_index]),
4'hf, 1'b0);
expect_read(s_reg_offset[register_index], legal_readback_value(
s_reg_offset[register_index]));
end
default: $fatal(1, "NPU register table has invalid access class");
endcase
end
s_phase = "full strobe enforcement";
for (int unsigned register_index = 0; register_index < RegisterCount; register_index++) begin
if (s_reg_full_strobe[register_index] && (s_reg_access[register_index] != AccessRo)) begin
hard_reset();
if (s_reg_offset[register_index] == `APB4_NPU__TIMEOUT_CYCLES) begin
apb_write(`APB4_NPU__JOB_ID, 32'ha5a5_5a5a, 4'hf, 1'b0);
apb_write(s_reg_offset[register_index], legal_write_value(s_reg_offset[register_index]),
4'h7, 1'b1);
apb_write(s_reg_offset[register_index], legal_write_value(s_reg_offset[register_index]),
4'h0, 1'b1);
expect_read(`APB4_NPU__JOB_ID, 32'ha5a5_5a5a);
expect_read(s_reg_offset[register_index], s_reg_reset[register_index]);
end else begin
apb_write(`APB4_NPU__TIMEOUT_CYCLES, 32'h044a_a200, 4'hf, 1'b0);
apb_write(s_reg_offset[register_index], legal_write_value(s_reg_offset[register_index]),
4'h7, 1'b1);
apb_write(s_reg_offset[register_index], legal_write_value(s_reg_offset[register_index]),
4'h0, 1'b1);
expect_read(`APB4_NPU__TIMEOUT_CYCLES, 32'h044a_a200);
if (s_reg_access[register_index] == AccessRw) begin
expect_read(s_reg_offset[register_index], s_reg_reset[register_index]);
end
end
end
end
s_phase = "misaligned accesses";
for (int unsigned register_index = 0; register_index < RegisterCount; register_index++) begin
hard_reset();
apb_write(`APB4_NPU__TIMEOUT_CYCLES, 32'h044a_a200, 4'hf, 1'b0);
apb_read(s_reg_offset[register_index] | 12'h002, s_value, 1'b1);
if (s_value != 32'd0) $fatal(1, "NPU misaligned read returned nonzero data");
apb_write(s_reg_offset[register_index] | 12'h001, legal_write_value(
s_reg_offset[register_index]), 4'hf, 1'b1);
expect_read(`APB4_NPU__TIMEOUT_CYCLES, 32'h044a_a200);
end
s_phase = "unknown offsets";
hard_reset();
apb_write(`APB4_NPU__TIMEOUT_CYCLES, 32'h044a_a200, 4'hf, 1'b0);
s_unknown_offsets[0] = 12'h0d0;
s_unknown_offsets[1] = 12'h0d4;
s_unknown_offsets[2] = 12'h0f8;
s_unknown_offsets[3] = 12'h100;
s_unknown_offsets[4] = 12'h800;
s_unknown_offsets[5] = 12'hffc;
for (int unsigned unknown_index = 0; unknown_index < 6; unknown_index++) begin
apb_read(s_unknown_offsets[unknown_index], s_value, 1'b1);
if (s_value != 32'd0) $fatal(1, "NPU invalid read returned nonzero data");
apb_write(s_unknown_offsets[unknown_index], 32'd0, 4'hf, 1'b1);
end
expect_read(`APB4_NPU__TIMEOUT_CYCLES, 32'h044a_a200);
s_phase = "rejected values";
s_bad_values[0] = 32'h0000_0000;
s_bad_values[1] = 32'h0000_0003;
s_bad_values[2] = 32'h0000_0005;
s_bad_values[3] = 32'h0000_0007;
s_bad_values[4] = 32'h0000_0008;
s_bad_values[5] = 32'h8000_0001;
s_bad_values[6] = 32'h0000_0001;
s_bad_values[7] = 32'h0000_0008;
s_bad_values[8] = 32'hffff_ffff;
s_bad_values[9] = 32'h0000_0008;
s_bad_values[10] = 32'h0000_0008;
s_bad_values[11] = 32'h0001_0000;
s_bad_values[12] = 32'h0000_0000;
s_bad_values[13] = 32'h0000_0000;
s_bad_values[14] = 32'h0000_0002;
s_bad_values[15] = 32'h0000_0003;
s_bad_values[16] = 32'h8000_0001;
s_bad_values[17] = 32'hffff_ffff;
s_bad_values[18] = 32'h0000_0001;
s_bad_values[19] = 32'hffff_ffff;
for (int unsigned bad_index = 0; bad_index < 20; bad_index++) begin
case (bad_index)
0, 1, 2, 3, 4, 5, 6: s_bad_offset = `APB4_NPU__CONTROL;
7, 8: s_bad_offset = `APB4_NPU__IRQ_STATE;
9: s_bad_offset = `APB4_NPU__IRQ_ENABLE;
10: s_bad_offset = `APB4_NPU__IRQ_TEST;
11: s_bad_offset = `APB4_NPU__JOB_COUNT;
12: s_bad_offset = `APB4_NPU__TIMEOUT_CYCLES;
13, 14, 15, 16: s_bad_offset = `APB4_NPU__PERF_CONTROL;
17: s_bad_offset = `APB4_NPU__IP_ID;
18: s_bad_offset = `APB4_NPU__STATUS;
default: s_bad_offset = `APB4_NPU__FAULT_DESCRIPTOR;
endcase
hard_reset();
apb_write(`APB4_NPU__TIMEOUT_CYCLES, 32'h044a_a200, 4'hf, 1'b0);
apb_write(s_bad_offset, s_bad_values[bad_index], 4'hf, 1'b1);
expect_read(`APB4_NPU__TIMEOUT_CYCLES, 32'h044a_a200);
expect_read(`APB4_NPU__IRQ_STATE, 32'd0);
expect_read(`APB4_NPU__PERF_STATUS, 32'd0);
end
s_phase = "control semantics";
hard_reset();
apb_write(`APB4_NPU__CONTROL, 32'h0000_0001, 4'hf, 1'b1);
expect_read(`APB4_NPU__STATUS, 32'd0);
expect_read(`APB4_NPU__RECOVERY_GENERATION, 32'd0);
if (u_dut.u_npu_reg.s_launch_busy_q !== 1'b0) begin
$fatal(1, "NPU rejected START had a launch side effect");
end
apb_write(`APB4_NPU__CONTROL, 32'h0000_0002, 4'hf, 1'b0);
expect_read(`APB4_NPU__IRQ_STATE, 32'd0);
expect_read(`APB4_NPU__RESULT_CODE, 32'd0);
apb_write(`APB4_NPU__JOB_BASE, 32'h3000_0000, 4'hf, 1'b0);
apb_write(`APB4_NPU__JOB_COUNT, 32'h0000_0040, 4'hf, 1'b0);
apb_write(`APB4_NPU__JOB_ID, 32'ha5a5_5a5a, 4'hf, 1'b0);
apb_write(`APB4_NPU__TIMEOUT_CYCLES, 32'h044a_a200, 4'hf, 1'b0);
apb_write(`APB4_NPU__IRQ_ENABLE, 32'h0000_0007, 4'hf, 1'b0);
apb_write(`APB4_NPU__IRQ_TEST, 32'h0000_0007, 4'hf, 1'b0);
apb_write(`APB4_NPU__CONTROL, 32'h0000_0004, 4'hf, 1'b0);
wait_link_up();
check_reset_state();
expect_read(`APB4_NPU__RECOVERY_GENERATION, 32'd0);
if (irq_o) $fatal(1, "NPU SOFT_RESET retained interrupt state");
s_phase = "irq events";
hard_reset();
apb_write(`APB4_NPU__IRQ_ENABLE, 32'h0000_0007, 4'hf, 1'b0);
if (irq_o) $fatal(1, "NPU interrupt asserted without events");
apb_write(`APB4_NPU__IRQ_TEST, 32'h0000_0007, 4'hf, 1'b0);
expect_read(`APB4_NPU__IRQ_STATE, 32'h0000_0007);
if (!irq_o) $fatal(1, "NPU enabled interrupt did not assert");
apb_write(`APB4_NPU__IRQ_ENABLE, 32'd0, 4'hf, 1'b0);
if (irq_o) $fatal(1, "NPU interrupt enable modified collection");
expect_read(`APB4_NPU__IRQ_STATE, 32'h0000_0007);
apb_write(`APB4_NPU__IRQ_STATE, 32'h0000_0005, 4'hf, 1'b0);
expect_read(`APB4_NPU__IRQ_STATE, 32'h0000_0002);
apb_write(`APB4_NPU__IRQ_STATE, 32'h0000_0002, 4'hf, 1'b0);
expect_read(`APB4_NPU__IRQ_STATE, 32'd0);
apb_write(`APB4_NPU__IRQ_TEST, 32'h0000_0001, 4'hf, 1'b0);
force u_dut.u_npu_reg.s_irq_test = 3'h1;
apb_write(`APB4_NPU__IRQ_STATE, 32'h0000_0001, 4'hf, 1'b0);
release u_dut.u_npu_reg.s_irq_test;
expect_read(`APB4_NPU__IRQ_STATE, 32'h0000_0001);
apb_write(`APB4_NPU__IRQ_STATE, 32'h0000_0001, 4'hf, 1'b0);
expect_read(`APB4_NPU__IRQ_STATE, 32'd0);
apb_write(`APB4_NPU__IRQ_ENABLE, 32'h0000_0003, 4'hf, 1'b0);
apb_write(`APB4_NPU__IRQ_TEST, 32'h0000_0001, 4'hf, 1'b0);
if (!irq_o) $fatal(1, "NPU masked interrupt event did not assert");
apb_write(`APB4_NPU__IRQ_ENABLE, 32'h0000_0002, 4'hf, 1'b0);
if (irq_o) $fatal(1, "NPU irq_o is not exactly IRQ_STATE & IRQ_ENABLE");
expect_read(`APB4_NPU__STATUS, 32'd0);
expect_read(`APB4_NPU__RESULT_CODE, 32'd0);
expect_read(`APB4_NPU__RESULT_JOB_ID, 32'd0);
s_phase = "owner status";
hard_reset();
resource_owner_i = 2'd1;
resource_owner_lock_i = 1'b1;
expect_read(`APB4_NPU__OWNER_STATUS, 32'h0000_0101);
resource_quiesce_i = 1'b1;
expect_read(`APB4_NPU__OWNER_STATUS, 32'h0000_0301);
s_ack_wait_cycles = 0;
while (!block_ack_o) begin
@(posedge clk_i);
s_ack_wait_cycles++;
if (s_ack_wait_cycles > 200) $fatal(1, "NPU quiesce acknowledgement timed out");
end
resource_quiesce_i = 1'b0;
resource_owner_i = 2'd2;
resource_owner_lock_i = 1'b0;
expect_read(`APB4_NPU__OWNER_STATUS, 32'h0000_0002);
@(negedge clk_i);
resource_reset_i = 1'b1;
expect_read(`APB4_NPU__OWNER_STATUS, 32'h0000_0402);
s_ack_wait_cycles = 0;
while (!block_ack_o) begin
@(posedge clk_i);
s_ack_wait_cycles++;
if (s_ack_wait_cycles > 200) $fatal(1, "NPU reset acknowledgement timed out");
end
@(negedge clk_i);
resource_reset_i = 1'b0;
resource_owner_i = 2'd0;
wait_link_up();
expect_read(`APB4_NPU__RECOVERY_GENERATION, 32'd1);
s_phase = "snapshot flow";
hard_reset();
expect_read(`APB4_NPU__PERF_STATUS, 32'd0);
apb_write(`APB4_NPU__PERF_CONTROL, 32'h0000_0001, 4'hf, 1'b0);
if (idle_o) $fatal(1, "NPU reported idle with a snapshot in flight");
expect_read(`APB4_NPU__PERF_STATUS, 32'h0000_0001);
apb_write(`APB4_NPU__PERF_CONTROL, 32'h0000_0001, 4'hf, 1'b1);
expect_read(`APB4_NPU__PERF_ACTIVE_CYCLES_LO, 32'd0);
expect_read(`APB4_NPU__PERF_RETIRED_DESCRIPTORS_HI, 32'd0);
wait_snapshot_valid();
expect_read(`APB4_NPU__PERF_STATUS, 32'h0000_0002);
expect_perf_bank_zero();
expect_read(`APB4_NPU__PERF_JOB_ID, 32'd0);
expect_read(`APB4_NPU__PERF_GENERATION, 32'd0);
expect_read(`APB4_NPU__STATUS, 32'd0);
if (!idle_o) $fatal(1, "NPU stayed busy after snapshot completion");
apb_write(`APB4_NPU__PERF_CONTROL, 32'h0000_0001, 4'hf, 1'b0);
wait_snapshot_valid();
expect_perf_bank_zero();
apb_write(`APB4_NPU__PERF_CONTROL, 32'h0000_0001, 4'hf, 1'b0);
apb_write(`APB4_NPU__CONTROL, 32'h0000_0004, 4'hf, 1'b1);
wait_snapshot_valid();
expect_perf_bank_zero();
apb_write(`APB4_NPU__PERF_CONTROL, 32'h0000_0001, 4'hf, 1'b0);
@(negedge clk_i);
rst_n_i = 1'b0;
repeat (3) @(posedge clk_i);
@(negedge clk_i);
rst_n_i = 1'b1;
wait_link_up();
expect_read(`APB4_NPU__PERF_STATUS, 32'd0);
expect_perf_bank_zero();
expect_read(`APB4_NPU__STATUS, 32'd0);
s_phase = "recovery generation";
hard_reset();
expect_read(`APB4_NPU__RECOVERY_GENERATION, 32'd0);
pulse_hp_reset();
expect_read(`APB4_NPU__RECOVERY_GENERATION, 32'd1);
pulse_hp_reset();
expect_read(`APB4_NPU__RECOVERY_GENERATION, 32'd2);
@(negedge clk_i);
resource_reset_i = 1'b1;
s_ack_wait_cycles = 0;
while (!block_ack_o) begin
@(posedge clk_i);
s_ack_wait_cycles++;
if (s_ack_wait_cycles > 200) $fatal(1, "NPU resource-reset acknowledgement timed out");
end
@(negedge clk_i);
resource_reset_i = 1'b0;
wait_link_up();
expect_read(`APB4_NPU__RECOVERY_GENERATION, 32'd3);
apb_write(`APB4_NPU__CONTROL, 32'h0000_0004, 4'hf, 1'b0);
wait_link_up();
expect_read(`APB4_NPU__RECOVERY_GENERATION, 32'd0);
s_phase = "forced busy lifecycle";
hard_reset();
// Force the next-state input: the launch slot holds the forced value
// through its own d->q feedback until a fresh clear is forced.
force u_dut.u_npu_reg.s_launch_busy_d = 1'b1;
@(posedge clk_i);
#1;
apb_read(`APB4_NPU__STATUS, s_value, 1'b0);
if (s_value[`APB4_NPU__STATUS_BUSY] != 1'b1) $fatal(1, "NPU BUSY not visible");
if (idle_o) $fatal(1, "NPU reported idle with a pending launch");
apb_write(`APB4_NPU__JOB_BASE, 32'h3000_0000, 4'hf, 1'b1);
apb_write(`APB4_NPU__JOB_COUNT, 32'h0000_0040, 4'hf, 1'b1);
apb_write(`APB4_NPU__JOB_ID, 32'ha5a5_5a5a, 4'hf, 1'b1);
apb_write(`APB4_NPU__TIMEOUT_CYCLES, 32'h0000_1000, 4'hf, 1'b1);
apb_write(`APB4_NPU__CONTROL, 32'h0000_0004, 4'hf, 1'b1);
apb_write(`APB4_NPU__CONTROL, 32'h0000_0001, 4'hf, 1'b1);
apb_write(`APB4_NPU__CONTROL, 32'h0000_0002, 4'hf, 1'b0);
apb_write(`APB4_NPU__PERF_CONTROL, 32'h0000_0001, 4'hf, 1'b0);
wait_snapshot_valid();
expect_perf_bank_zero();
@(negedge clk_i);
resource_quiesce_i = 1'b1;
repeat (20) @(posedge clk_i);
if (block_ack_o) begin
$fatal(1, "NPU quiesce acknowledged with a pending shell launch");
end
force u_dut.u_npu_reg.s_launch_busy_d = 1'b0;
@(posedge clk_i);
#1;
release u_dut.u_npu_reg.s_launch_busy_d;
if (u_dut.u_npu_reg.s_launch_busy_q !== 1'b0) begin
$fatal(1, "NPU forced launch slot did not clear");
end
s_ack_wait_cycles = 0;
while (!block_ack_o) begin
@(posedge clk_i);
s_ack_wait_cycles++;
if (s_ack_wait_cycles > 200) $fatal(1, "NPU quiesce acknowledgement timed out");
end
@(negedge clk_i);
resource_quiesce_i = 1'b0;
s_ack_wait_cycles = 0;
while (block_ack_o) begin
@(posedge clk_i);
s_ack_wait_cycles++;
if (s_ack_wait_cycles > 200) $fatal(1, "NPU quiesce acknowledgement did not release");
end
s_phase = "p2 truthfulness";
hard_reset();
expect_read(`APB4_NPU__STATUS, 32'd0);
expect_read(`APB4_NPU__RESULT_CODE, 32'd0);
expect_read(`APB4_NPU__RESULT_JOB_ID, 32'd0);
expect_read(`APB4_NPU__COMPLETED_DESCRIPTORS, 32'd0);
expect_read(`APB4_NPU__FAULT_DESCRIPTOR, `APB4_NPU__FAULT_DESCRIPTOR_RESET);
expect_read(`APB4_NPU__FAULT_CODE, 32'd0);
expect_read(`APB4_NPU__FAULT_ADDRESS, 32'd0);
expect_read(`APB4_NPU__FAULT_INFO, 32'd0);
if (!idle_o || irq_o) $fatal(1, "NPU final lifecycle outputs mismatch");
$display("NPU reg test passed");
$finish;
end
// The tied-off Phase 2 master must never issue a request and stays ready.
always @(posedge clk_hp_i) begin
if (rst_hp_n_i) begin
if (npu_axi4.awvalid || npu_axi4.wvalid || npu_axi4.arvalid) begin
$fatal(1, "NPU Phase 2 AXI master issued a request");
end
if (!npu_axi4.bready || !npu_axi4.rready) begin
$fatal(1, "NPU Phase 2 AXI receivers not ready");
end
end
end
initial begin
repeat (300000) @(posedge clk_i);
$fatal(1, "NPU register matrix timed out in %s", s_phase);
end
endmodule
+29 -12
View File
@@ -3,17 +3,17 @@
module resource_controller_tb; module resource_controller_tb;
logic clk_i = 1'b0; logic clk_i = 1'b0;
logic rst_n_i = 1'b0; logic rst_n_i = 1'b0;
logic [ 8:0] idle_i = '0; logic [ 9:0] idle_i = '0;
logic [ 8:0] block_ack_i = '0; logic [ 9:0] block_ack_i = '0;
logic [ 8:0] irq_i = '0; logic [ 9:0] irq_i = '0;
logic cache_request_i = 1'b0; logic cache_request_i = 1'b0;
logic cache_clean_o; logic cache_clean_o;
logic [ 8:0][1:0] owner_o; logic [ 9:0][1:0] owner_o;
logic [ 8:0] owner_lock_o; logic [ 9:0] owner_lock_o;
logic [ 8:0] quiesce_o; logic [ 9:0] quiesce_o;
logic [ 8:0] reset_o; logic [ 9:0] reset_o;
logic [ 8:0] irq_lp_o; logic [ 9:0] irq_lp_o;
logic [ 8:0] irq_hp_o; logic [ 9:0] irq_hp_o;
logic fault_irq_o; logic fault_irq_o;
logic [31:0] read_data; logic [31:0] read_data;
@@ -92,14 +92,14 @@ module resource_controller_tb;
repeat (3) @(posedge clk_i); repeat (3) @(posedge clk_i);
rst_n_i = 1'b1; rst_n_i = 1'b1;
idle_i = 9'h1FF; idle_i = 10'h3FF;
apb_read(12'h000, read_data); apb_read(12'h000, read_data);
if (read_data != 32'h5253_4354) $fatal(1, "resource controller ID mismatch"); if (read_data != 32'h5253_4354) $fatal(1, "resource controller ID mismatch");
apb_read(12'h004, read_data); apb_read(12'h004, read_data);
if (read_data != 32'h0001_0001) $fatal(1, "resource controller version mismatch"); if (read_data != 32'h0001_0002) $fatal(1, "resource controller version mismatch");
apb_read(12'h008, read_data); apb_read(12'h008, read_data);
if (read_data != 32'h0000_0901) $fatal(1, "resource controller capability mismatch"); if (read_data != 32'h0000_0A01) $fatal(1, "resource controller capability mismatch");
irq_i[1] = 1'b1; irq_i[1] = 1'b1;
#1; #1;
@@ -176,6 +176,23 @@ module resource_controller_tb;
$fatal(1, "GA2D resource reset did not mask both IRQ routes"); $fatal(1, "GA2D resource reset did not mask both IRQ routes");
end end
irq_i[9] = 1'b1;
apb_read(12'h220, read_data);
if (read_data != 32'd0 || owner_o[9] != 2'd0 || owner_lock_o[9] || !irq_lp_o[9] ||
irq_hp_o[9]) begin
$fatal(1, "NPU resource 9 did not reset to unlocked LP ownership");
end
apb_write(12'h224, 32'h0000_0001, 1'b0);
block_ack_i[9] = 1'b1;
apb_write(12'h220, 32'h0000_0001, 1'b0);
if ((owner_o[9] != 2'd1) || irq_lp_o[9] || !irq_hp_o[9]) begin
$fatal(1, "NPU resource 9 LP-to-HP IRQ handoff mismatch");
end
apb_write(12'h224, 32'h0000_0003, 1'b0);
if (irq_lp_o[9] || irq_hp_o[9]) begin
$fatal(1, "NPU resource reset did not mask both IRQ routes");
end
$display("Resource Controller ownership, IRQ, and cache handshake test passed"); $display("Resource Controller ownership, IRQ, and cache handshake test passed");
$finish; $finish;
end end
+3 -2
View File
@@ -45,6 +45,7 @@ module soc_irq_topology_tb;
22: expect_irq(64'd1 << 30); 22: expect_irq(64'd1 << 30);
23: expect_irq(64'd1 << 31); 23: expect_irq(64'd1 << 31);
24: expect_irq(64'd1 << 32); 24: expect_irq(64'd1 << 32);
25: expect_irq(64'd1 << 33);
default: expect_irq(64'd1 << (bit_index + 7)); default: expect_irq(64'd1 << (bit_index + 7));
endcase endcase
end end
@@ -68,8 +69,8 @@ module soc_irq_topology_tb;
s_apb4_system_irq = '0; s_apb4_system_irq = '0;
expect_irq('0); expect_irq('0);
if (s_irq[63:33] !== '0) begin if (s_irq[63:34] !== '0) begin
$fatal(1, "unallocated high IRQ vector bits are not low: %h", s_irq[63:33]); $fatal(1, "unallocated high IRQ vector bits are not low: %h", s_irq[63:34]);
end end
if ((s_irq[10] !== 1'b0) || (s_irq[21] !== 1'b0)) begin if ((s_irq[10] !== 1'b0) || (s_irq[21] !== 1'b0)) begin
$fatal(1, "SDIO core IRQ bits are not low when sources are low: IRQ10=%b IRQ21=%b", $fatal(1, "SDIO core IRQ bits are not low when sources are low: IRQ10=%b IRQ21=%b",
+15
View File
@@ -61,6 +61,21 @@ def test_clock_reset_domain_inventory_matches_the_rcu() -> None:
("ga2d_flush", "hp", "pclk"), ("ga2d_flush", "hp", "pclk"),
("ga2d_master_idle", "hp", "pclk"), ("ga2d_master_idle", "hp", "pclk"),
("ga2d_block_ack", "hp", "pclk"), ("ga2d_block_ack", "hp", "pclk"),
("npu_launch_mailbox", "pclk", "hp"),
("npu_result_mailbox", "hp", "pclk"),
("npu_snapshot_req_mailbox", "pclk", "hp"),
("npu_snapshot_resp_mailbox", "hp", "pclk"),
("npu_hp_reset", "hp", "pclk"),
("npu_hp_flush", "hp", "pclk"),
("npu_epoch_req", "pclk", "hp"),
("npu_epoch_ack", "hp", "pclk"),
("npu_quiesce_req", "pclk", "hp"),
("npu_quiesce_ack", "hp", "pclk"),
("npu_hp_busy", "hp", "pclk"),
("npu_hp_draining", "hp", "pclk"),
("npu_hp_pause_active", "hp", "pclk"),
("npu_source_idle", "pclk", "hp"),
("npu_source_quiesced", "pclk", "hp"),
} }
clint = next( clint = next(
crossing for crossing in document["crossings"] if crossing["name"] == "clint_timebase" crossing for crossing in document["crossings"] if crossing["name"] == "clint_timebase"
+18 -6
View File
@@ -38,17 +38,28 @@ def test_ga2d_p5_preserves_the_real_private_axi64_master_and_irq_route() -> None
"public": True, "public": True,
"user_access": "none", "user_access": "none",
} }
assert topology["apb4_periph_targets"][-1] == { assert topology["apb4_periph_targets"][-2] == {
"slot": 28, "slot": 28,
"name": "ga2d", "name": "ga2d",
"timed_interface": "u_ga2d_apb4_if", "timed_interface": "u_ga2d_apb4_if",
"pure_interface": "u_ga2d_apb4_pure_if", "pure_interface": "u_ga2d_apb4_pure_if",
"region": "APB4_GA2D", "region": "APB4_GA2D",
} }
assert topology["interrupts"][-1]["name"] == "ga2d" assert topology["apb4_periph_targets"][-1] == {
assert topology["interrupts"][-1]["group_bit"] == 24 "slot": 29,
assert topology["interrupts"][-1]["core_bit"] == 32 "name": "npu",
assert topology["interrupts"][-1]["signal"] == "resource_irq_lp_i[7]" "timed_interface": "u_npu_apb4_if",
"pure_interface": "u_npu_apb4_pure_if",
"region": "APB4_NPU",
}
assert topology["interrupts"][-2]["name"] == "ga2d"
assert topology["interrupts"][-2]["group_bit"] == 24
assert topology["interrupts"][-2]["core_bit"] == 32
assert topology["interrupts"][-2]["signal"] == "resource_irq_lp_i[7]"
assert topology["interrupts"][-1]["name"] == "npu"
assert topology["interrupts"][-1]["group_bit"] == 25
assert topology["interrupts"][-1]["core_bit"] == 33
assert topology["interrupts"][-1]["signal"] == "resource_irq_lp_i[8]"
assert { assert {
"ga2d_pkg.sv", "ga2d_pkg.sv",
"ga2d_addr_gen.sv", "ga2d_addr_gen.sv",
@@ -59,10 +70,11 @@ def test_ga2d_p5_preserves_the_real_private_axi64_master_and_irq_route() -> None
"ga2d_reg.sv", "ga2d_reg.sv",
"apb4_ga2d.sv", "apb4_ga2d.sv",
} <= {path.name for path in (ROOT / "rtl/ip/multimedia").glob("*ga2d*.sv")} } <= {path.name for path in (ROOT / "rtl/ip/multimedia").glob("*ga2d*.sv")}
assert ".ResourceCount(9)" in apb4_system assert ".ResourceCount(10)" in apb4_system
assert "resource_irq_i[7]" in apb4_system assert "resource_irq_i[7]" in apb4_system
assert "s_resource_irq_lp[8]" in apb4_system assert "s_resource_irq_lp[8]" in apb4_system
assert "s_hp_plic_source[11] = resource_irq_hp_i[7];" in apb4_periph assert "s_hp_plic_source[11] = resource_irq_hp_i[7];" in apb4_periph
assert "s_hp_plic_source[12] = resource_irq_hp_i[8];" in apb4_periph
assert "s_ga2d_idle && s_ga2d_source_safe_idle && s_resource_idle_pclk[8]" in top assert "s_ga2d_idle && s_ga2d_source_safe_idle && s_resource_idle_pclk[8]" in top
assert "s_ga2d_idle && s_ga2d_source_safe_idle && s_resource_block_ack_pclk[8]" in top assert "s_ga2d_idle && s_ga2d_source_safe_idle && s_resource_block_ack_pclk[8]" in top
assert "axi4_master_idle u_ga2d_master_idle" not in top assert "axi4_master_idle u_ga2d_master_idle" not in top
+5 -1
View File
@@ -47,14 +47,18 @@ def test_phase1_generates_64bit_vector_and_sdk_metadata(tmp_path: Path) -> None:
assert "`define SOC_USER_IRQ_MASK 64'h00000000004EFBFC" in irq_config assert "`define SOC_USER_IRQ_MASK 64'h00000000004EFBFC" in irq_config
assert "s_irq[31] = s_apb4_periph_irq[23];" in irq_wiring assert "s_irq[31] = s_apb4_periph_irq[23];" in irq_wiring
assert "s_irq[32] = s_apb4_periph_irq[24];" in irq_wiring assert "s_irq[32] = s_apb4_periph_irq[24];" in irq_wiring
assert "s_irq[33] = s_apb4_periph_irq[25];" in irq_wiring
assert "irq_i[32] == apb4_periph_irq_i[24]" in irq_sva assert "irq_i[32] == apb4_periph_irq_i[24]" in irq_sva
assert "irq_i[33] == apb4_periph_irq_i[25]" in irq_sva
assert "irq_i[63] == 1'b0" in irq_sva assert "irq_i[63] == 1'b0" in irq_sva
assert "RS_SOC_IRQ_VECTOR_WIDTH UINT32_C(64)" in metadata assert "RS_SOC_IRQ_VECTOR_WIDTH UINT32_C(64)" in metadata
assert "RS_SOC_EXTERNAL_IRQ_COUNT UINT32_C(62)" in metadata assert "RS_SOC_EXTERNAL_IRQ_COUNT UINT32_C(62)" in metadata
assert "RS_SOC_ALLOCATED_IRQ_COUNT UINT32_C(31)" in metadata assert "RS_SOC_ALLOCATED_IRQ_COUNT UINT32_C(32)" in metadata
assert "RS_SOC_EXT_IRQ_APU UINT32_C(29)" in metadata assert "RS_SOC_EXT_IRQ_APU UINT32_C(29)" in metadata
assert "RS_SOC_IRQ_GA2D UINT32_C(32)" in metadata assert "RS_SOC_IRQ_GA2D UINT32_C(32)" in metadata
assert "RS_SOC_EXT_IRQ_GA2D UINT32_C(30)" in metadata assert "RS_SOC_EXT_IRQ_GA2D UINT32_C(30)" in metadata
assert "RS_SOC_IRQ_NPU UINT32_C(33)" in metadata
assert "RS_SOC_EXT_IRQ_NPU UINT32_C(31)" in metadata
def test_phase1_supports_32bit_compatibility_metadata(tmp_path: Path) -> None: def test_phase1_supports_32bit_compatibility_metadata(tmp_path: Path) -> None:
+387
View File
@@ -0,0 +1,387 @@
"""Keep the handwritten NPU RTL and SDK register constants synchronized.
Both sides are handwritten from the frozen register/ABI tables in
docs/ip/npu.md; no register generator is involved. The naming and radix
conventions follow ga2d_define.svh / ga2d_regs.h:
- `APB4_NPU__<REGISTER>` (12'h...) in the svh matches `RS_NPU_REG_<REGISTER>`
(UINT32_C hex) in C, one entry per register map row including the ten
PERF counter low/high pairs at 8-byte stride.
- Bit groups (CONTROL, STATUS, IRQ, CAPABILITY, PERF_CONTROL, PERF_STATUS,
OWNER_STATUS lock/quiesce/reset-request) carry bit INDICES as plain
decimals in the svh and one-bit MASKS UINT32_C(1U << index) in C.
- Sub-field shifts (FAULT_INFO, OWNER_STATUS owner) are plain decimals in
the svh; C keeps the matching _SHIFT plus a C-only _MASK. Further C-only
shifts/masks (MAC_CONFIG, JOB_COUNT, VERSION_OPCODE) decode fields the
RTL does not name in the svh.
- Scalar values are 32'h.../16'h.../decimal in the svh and UINT32_C(...) in
C. DESCRIPTOR_BYTES_VALUE carries a _VALUE suffix only on the svh side
because the bare name is a register offset there; DESCRIPTOR_VERSION
matches RS_NPU_DESCRIPTOR_ABI_VERSION. Reset/limit values the RTL does
not instantiate (timeout reset, implemented memory/lane limits, MVP
operator mask, descriptor ABI sizes) are C-only constants.
- Enumerations are plain decimals on both sides with asymmetric group
names: APB4_NPU__RESULT_CODE_<NAME> vs RS_NPU_RESULT_<NAME>,
APB4_NPU__FAULT_CODE_<NAME> vs RS_NPU_FAULT_<NAME> (the bare FAULT_CODE
and FAULT_DESCRIPTOR names are register offsets), and
APB4_NPU__OP_<NAME> vs RS_NPU_OPCODE_<NAME>.
- svh entries under LAUNCH_PAYLOAD/RESULT_PAYLOAD/SNAPSHOT_PAYLOAD plus
EPOCH_WIDTH and PERF_COUNTER_COUNT describe the hardware-internal
shell/HP mailbox ABI and have no C counterpart; they are pinned by an
explicit RTL-internal list so the exhaustiveness check still rejects
unexpected names on either side.
"""
from __future__ import annotations
import re
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
RTL_DEFINE = ROOT / "rtl/ip/multimedia/npu_define.svh"
C_DEFINE = ROOT / "crt/include/retrosoc/hal/npu_regs.h"
def _literal_value(value: str) -> int:
value = value.replace("_", "")
if "'" in value:
_, encoded = value.split("'", maxsplit=1)
base = 16 if encoded[0].lower() == "h" else 10
return int(encoded[1:], base)
return int(value, 10)
def _rtl_values() -> dict[str, int]:
if not RTL_DEFINE.is_file():
pytest.skip("rtl/ip/multimedia/npu_define.svh is not present yet")
pattern = re.compile(r"^`define\s+(APB4_NPU__\w+)\s+((?:\d+)'[hHdD][0-9a-fA-F_]+|\d+)\s*$")
values: dict[str, int] = {}
for line in RTL_DEFINE.read_text(encoding="utf-8").splitlines():
match = pattern.match(line)
if match is not None:
values[match.group(1)] = _literal_value(match.group(2))
return values
def _c_values() -> dict[str, int]:
pattern = re.compile(r"^#define\s+(RS_NPU_\w+)\s+(?:UINT32_C\((0x[0-9a-fA-F]+|\d+)\)|(\d+)U)\s*$")
values: dict[str, int] = {}
for line in C_DEFINE.read_text(encoding="utf-8").splitlines():
match = pattern.match(line)
if match is not None:
values[match.group(1)] = int(
match.group(2) if match.group(2) is not None else match.group(3), 0
)
return values
OFFSETS = (
"IP_ID",
"IP_VERSION",
"CAPABILITY",
"STATUS",
"CONTROL",
"IRQ_STATE",
"IRQ_ENABLE",
"IRQ_TEST",
"JOB_BASE",
"JOB_COUNT",
"JOB_ID",
"TIMEOUT_CYCLES",
"RESULT_JOB_ID",
"RESULT_CODE",
"COMPLETED_DESCRIPTORS",
"FAULT_DESCRIPTOR",
"FAULT_CODE",
"FAULT_ADDRESS",
"FAULT_INFO",
"NUMERIC_PROFILE",
"LOCAL_BYTES",
"MAC_CONFIG",
"MAX_K_SLICE",
"MAX_DIMENSION",
"OP_CAPABILITY",
"OWNER_STATUS",
"RECOVERY_GENERATION",
"PERF_CONTROL",
"PERF_STATUS",
"PERF_JOB_ID",
"PERF_GENERATION",
"DESCRIPTOR_BYTES",
"PERF_ACTIVE_CYCLES_LO",
"PERF_ACTIVE_CYCLES_HI",
"PERF_CLOCK_PAUSE_CYCLES_LO",
"PERF_CLOCK_PAUSE_CYCLES_HI",
"PERF_USEFUL_MACS_LO",
"PERF_USEFUL_MACS_HI",
"PERF_PACK_CYCLES_LO",
"PERF_PACK_CYCLES_HI",
"PERF_LOCAL_BANK_STALL_LO",
"PERF_LOCAL_BANK_STALL_HI",
"PERF_DMA_READ_BYTES_LO",
"PERF_DMA_READ_BYTES_HI",
"PERF_DMA_WRITE_BYTES_LO",
"PERF_DMA_WRITE_BYTES_HI",
"PERF_DMA_STALL_CYCLES_LO",
"PERF_DMA_STALL_CYCLES_HI",
"PERF_REQUANT_STALL_LO",
"PERF_REQUANT_STALL_HI",
"PERF_RETIRED_DESCRIPTORS_LO",
"PERF_RETIRED_DESCRIPTORS_HI",
)
SCALARS = {
"APB4_NPU__IP_ID_VALUE": "RS_NPU_IP_ID_VALUE",
"APB4_NPU__IP_VERSION_VALUE": "RS_NPU_IP_VERSION_VALUE",
"APB4_NPU__CAPABILITY_P2": "RS_NPU_CAPABILITY_P2",
"APB4_NPU__NUMERIC_PROFILE_VALUE": "RS_NPU_NUMERIC_PROFILE_VALUE",
"APB4_NPU__DESCRIPTOR_BYTES_VALUE": "RS_NPU_DESCRIPTOR_BYTES",
"APB4_NPU__DESCRIPTOR_VERSION": "RS_NPU_DESCRIPTOR_ABI_VERSION",
"APB4_NPU__FAULT_DESCRIPTOR_RESET": "RS_NPU_FAULT_DESCRIPTOR_RESET",
"APB4_NPU__IRQ_ALL": "RS_NPU_IRQ_ALL",
}
C_ONLY_SCALARS = (
"RS_NPU_IP_VERSION_MAJOR_MASK",
"RS_NPU_IP_VERSION_MAJOR_1",
"RS_NPU_TIMEOUT_CYCLES_RESET",
"RS_NPU_LOCAL_BYTES_VALUE",
"RS_NPU_MAC_CONFIG_VALUE",
"RS_NPU_MAX_K_SLICE",
"RS_NPU_MAX_DIMENSION",
"RS_NPU_MAX_TILE",
"RS_NPU_OP_CAPABILITY_MVP",
"RS_NPU_DESCRIPTOR_WORDS",
"RS_NPU_DESCRIPTOR_BASE_ALIGNMENT",
"RS_NPU_PARAM_RECORD_BYTES",
"RS_NPU_ADD_PARAM_BYTES",
)
BIT_GROUPS = {
"CONTROL": ("START", "ABORT", "SOFT_RESET"),
"STATUS": ("READY", "BUSY", "DRAINING", "CLOCK_PAUSED", "RECOVERING", "RESULT_VALID"),
"IRQ": ("DONE", "ERROR", "ABORTED"),
"CAPABILITY": (
"PRESENT",
"EXECUTION_READY",
"PRIVATE_AXI64_DMA",
"INTERRUPTS",
"DOUBLE_ROUNDING",
"NATIVE_HP_CLOCK",
"SOFTWARE_ABORT",
),
"PERF_CONTROL": ("SNAPSHOT",),
"PERF_STATUS": ("SNAP_BUSY", "SNAP_VALID"),
"OWNER_STATUS": ("LOCK", "QUIESCE", "RESET_REQUEST"),
}
SHIFT_GROUPS = {
"FAULT_INFO": ("AXI_RESPONSE", "DIRECTION", "LANE"),
"OWNER_STATUS": ("OWNER",),
}
C_ONLY_SHIFT_GROUPS = {
"MAC_CONFIG": ("DENSE", "DEPTHWISE"),
"JOB_COUNT": ("VALUE",),
"VERSION_OPCODE": ("VERSION", "OPCODE"),
}
C_ONLY_VALUES = (
"RS_NPU_FAULT_INFO_DIRECTION_INTERNAL",
"RS_NPU_FAULT_INFO_DIRECTION_READ",
"RS_NPU_FAULT_INFO_DIRECTION_WRITE",
"RS_NPU_FAULT_INFO_LANE_NONE",
)
RTL_INTERNAL = (
"APB4_NPU__PERF_COUNTER_COUNT",
"APB4_NPU__LAUNCH_PAYLOAD_WIDTH",
"APB4_NPU__LAUNCH_JOB_BASE",
"APB4_NPU__LAUNCH_JOB_ID",
"APB4_NPU__LAUNCH_TIMEOUT_CYCLES",
"APB4_NPU__LAUNCH_JOB_COUNT",
"APB4_NPU__RESULT_PAYLOAD_WIDTH",
"APB4_NPU__RESULT_PAYLOAD_JOB_ID",
"APB4_NPU__RESULT_PAYLOAD_COMPLETED",
"APB4_NPU__RESULT_PAYLOAD_FAULT_DESC",
"APB4_NPU__RESULT_PAYLOAD_FAULT_ADDR",
"APB4_NPU__RESULT_PAYLOAD_FAULT_INFO",
"APB4_NPU__RESULT_PAYLOAD_CODE",
"APB4_NPU__RESULT_PAYLOAD_FAULT_CODE",
"APB4_NPU__RESULT_PAYLOAD_IRQ_EVENTS",
"APB4_NPU__SNAPSHOT_PAYLOAD_WIDTH",
"APB4_NPU__SNAPSHOT_PAYLOAD_JOB_ID",
"APB4_NPU__SNAPSHOT_PAYLOAD_COUNTERS",
"APB4_NPU__EPOCH_WIDTH",
)
RESULT_NAMES = ("NONE", "DONE", "ERROR", "ABORTED", "RESET_CANCELLED")
FAULT_NAMES = (
"NONE",
"DESCRIPTOR",
"UNSUPPORTED",
"RANGE",
"AXI_READ",
"AXI_WRITE",
"AXI_PROTOCOL",
"ARITHMETIC",
"NO_PROGRESS",
"LOCAL_STATE",
"RESET_CANCELLED",
)
OPCODE_NAMES = (
"CONV2D",
"DEPTHWISE3X3",
"FULLY_CONNECTED",
"ADD",
"MAX_POOL",
"AVERAGE_POOL",
"GLOBAL_AVERAGE_POOL",
"CLAMP",
)
DESCRIPTOR_WORD_NAMES = (
"VERSION_OPCODE",
"RESERVED",
"INPUT0_BASE",
"INPUT1_BASE",
"OUTPUT_BASE",
"PARAM_BASE",
"INPUT_HW",
"CHANNELS",
"OUTPUT_HW",
"INPUT0_ROW_BYTES",
"OUTPUT_ROW_BYTES",
"INPUT1_ROW_BYTES",
"KERNEL_STRIDE",
"PADDING",
"TILE_HW",
"K_SLICE",
"INPUT0_BYTES",
"INPUT1_BYTES",
"OUTPUT_BYTES",
"PARAM_BYTES",
"INPUT0_ZERO",
"INPUT1_ZERO",
"OUTPUT_ZERO",
"ACTIVATION_BOUNDS",
"WEIGHT_BASE",
"WEIGHT_BYTES",
)
def test_npu_register_offsets_and_scalar_encodings_are_exhaustive() -> None:
rtl = _rtl_values()
c = _c_values()
offset_mapping = {f"APB4_NPU__{name}": f"RS_NPU_REG_{name}" for name in OFFSETS}
for rtl_name, c_name in {**offset_mapping, **SCALARS}.items():
assert rtl[rtl_name] == c[c_name], f"{rtl_name} != {c_name}"
assert rtl["APB4_NPU__IP_ID_VALUE"] == 0x4E505531
assert rtl["APB4_NPU__IP_VERSION_VALUE"] == 0x00010000
assert rtl["APB4_NPU__CAPABILITY_P2"] == 0x00000009
assert rtl["APB4_NPU__NUMERIC_PROFILE_VALUE"] == 1
assert rtl["APB4_NPU__DESCRIPTOR_BYTES_VALUE"] == 128
assert rtl["APB4_NPU__DESCRIPTOR_VERSION"] == 0x0100
assert rtl["APB4_NPU__FAULT_DESCRIPTOR_RESET"] == 0xFFFFFFFF
assert rtl["APB4_NPU__IRQ_ALL"] == 0x7
assert c["RS_NPU_TIMEOUT_CYCLES_RESET"] == 72000000
assert c["RS_NPU_LOCAL_BYTES_VALUE"] == 65536
assert c["RS_NPU_MAC_CONFIG_VALUE"] == 0x00080040
assert c["RS_NPU_MAX_K_SLICE"] == 1024
assert c["RS_NPU_MAX_DIMENSION"] == 4096
assert c["RS_NPU_OP_CAPABILITY_MVP"] == 0x000001FE
assert c["RS_NPU_DESCRIPTOR_WORDS"] == 32
for index, name in enumerate(OFFSETS):
if index < 32:
expected = 4 * index
else:
expected = 0x080 + 8 * ((index - 32) // 2) + (4 if index % 2 == 1 else 0)
assert rtl[f"APB4_NPU__{name}"] == expected, f"offset order mismatch for {name}"
assert set(offset_mapping) <= set(rtl)
assert set(offset_mapping.values()) | set(SCALARS.values()) | set(C_ONLY_SCALARS) <= set(c)
def test_npu_register_bit_shifts_and_enums_are_exhaustive() -> None:
rtl = _rtl_values()
c = _c_values()
bit_mapping = {
f"APB4_NPU__{group}_{name}": f"RS_NPU_{group}_{name}"
for group, names in BIT_GROUPS.items()
for name in names
}
shift_mapping = {
f"APB4_NPU__{group}_{name}": f"RS_NPU_{group}_{name}_SHIFT"
for group, names in SHIFT_GROUPS.items()
for name in names
}
result_mapping = {
f"APB4_NPU__RESULT_CODE_{name}": f"RS_NPU_RESULT_{name}" for name in RESULT_NAMES
}
fault_mapping = {f"APB4_NPU__FAULT_CODE_{name}": f"RS_NPU_FAULT_{name}" for name in FAULT_NAMES}
opcode_mapping = {f"APB4_NPU__OP_{name}": f"RS_NPU_OPCODE_{name}" for name in OPCODE_NAMES}
word_mapping = {
f"APB4_NPU__DESCRIPTOR_WORD_{name}": f"RS_NPU_DESCRIPTOR_WORD_{name}"
for name in DESCRIPTOR_WORD_NAMES
}
for rtl_name, c_name in bit_mapping.items():
assert c[c_name] == 1 << rtl[rtl_name], f"{rtl_name} != {c_name}"
for rtl_name, c_name in {
**shift_mapping,
**result_mapping,
**fault_mapping,
**opcode_mapping,
**word_mapping,
}.items():
assert rtl[rtl_name] == c[c_name], f"{rtl_name} != {c_name}"
for index, name in enumerate(OPCODE_NAMES):
assert rtl[f"APB4_NPU__OP_{name}"] == index + 1
for index, name in enumerate(DESCRIPTOR_WORD_NAMES):
assert rtl[f"APB4_NPU__DESCRIPTOR_WORD_{name}"] == index
for index, name in enumerate(FAULT_NAMES):
assert rtl[f"APB4_NPU__FAULT_CODE_{name}"] == index
def test_npu_handwritten_definition_sets_have_no_missing_or_extra_entries() -> None:
rtl = _rtl_values()
c = _c_values()
expected_rtl = (
{f"APB4_NPU__{name}" for name in OFFSETS}
| set(SCALARS)
| {f"APB4_NPU__{group}_{name}" for group, names in BIT_GROUPS.items() for name in names}
| {f"APB4_NPU__{group}_{name}" for group, names in SHIFT_GROUPS.items() for name in names}
| {f"APB4_NPU__RESULT_CODE_{name}" for name in RESULT_NAMES}
| {f"APB4_NPU__FAULT_CODE_{name}" for name in FAULT_NAMES}
| {f"APB4_NPU__OP_{name}" for name in OPCODE_NAMES}
| {f"APB4_NPU__DESCRIPTOR_WORD_{name}" for name in DESCRIPTOR_WORD_NAMES}
| set(RTL_INTERNAL)
)
expected_c = (
{f"RS_NPU_REG_{name}" for name in OFFSETS}
| set(SCALARS.values())
| set(C_ONLY_SCALARS)
| {f"RS_NPU_{group}_{name}" for group, names in BIT_GROUPS.items() for name in names}
| {
f"RS_NPU_{group}_{name}_{suffix}"
for groups in (SHIFT_GROUPS, C_ONLY_SHIFT_GROUPS)
for group, names in groups.items()
for name in names
for suffix in ("SHIFT", "MASK")
}
| set(C_ONLY_VALUES)
| {f"RS_NPU_RESULT_{name}" for name in RESULT_NAMES}
| {f"RS_NPU_FAULT_{name}" for name in FAULT_NAMES}
| {f"RS_NPU_OPCODE_{name}" for name in OPCODE_NAMES}
| {f"RS_NPU_DESCRIPTOR_WORD_{name}" for name in DESCRIPTOR_WORD_NAMES}
)
assert set(rtl) == expected_rtl
assert set(c) == expected_c
+156
View File
@@ -0,0 +1,156 @@
"""NPU-P2 register shell and control-CDC unit tests (Icarus and Verilator)."""
from __future__ import annotations
import os
import shutil
import subprocess
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
COMMON = ROOT / "rtl/managed/clusterip/common/rtl"
MULTIMEDIA = ROOT / "rtl/ip/multimedia"
INCDIRS = [COMMON, COMMON / "interface", MULTIMEDIA]
COMMON_SOURCES = [
COMMON / "interface/apb4_if.sv",
COMMON / "interface/axi4_if.sv",
COMMON / "utils/register.sv",
COMMON / "utils/xchecker.sv",
COMMON / "clkrst/rst_sync.sv",
COMMON / "cdc/cdc_sync.sv",
COMMON / "cdc/cdc_rst_ctrlr.sv",
COMMON / "cdc/async_reqack.sv",
]
SHELL_SOURCES = [
MULTIMEDIA / "npu_reg.sv",
MULTIMEDIA / "npu_control_cdc.sv",
MULTIMEDIA / "npu_core.sv",
MULTIMEDIA / "apb4_npu.sv",
]
def _tools() -> dict[str, str]:
tools: dict[str, str] = {}
missing = []
for name in ("iverilog", "vvp", "sv2v", "verilator"):
path = shutil.which(name)
if path is None:
missing.append(name)
else:
tools[name] = path
if missing:
pytest.fail(f"required simulation tools missing: {', '.join(missing)}")
return tools
def _build_iverilog(tools: dict[str, str], tmp_path: Path, name: str, top: str,
sources: list[Path]) -> Path:
filelist = tmp_path / f"{name}.fl"
filelist.write_text(
"\n".join(
[
"+define+SV_ASSRT_DISABLE",
*(f"+incdir+{incdir}" for incdir in INCDIRS),
*(str(source) for source in sources),
"",
]
),
encoding="utf-8",
)
converted = tmp_path / f"{name}.v"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/script/convt_sv2v.py"),
"-f",
str(filelist),
"--output",
str(converted),
],
check=True,
)
simulation = tmp_path / f"{name}.iverilog"
subprocess.run(
[tools["iverilog"], "-g2012", "-s", top, "-o", str(simulation), str(converted)],
check=True,
)
return simulation
def _build_verilator(tools: dict[str, str], tmp_path: Path, name: str, top: str,
sources: list[Path]) -> Path:
object_dir = tmp_path / f"obj-{name}"
ccache_dir = tmp_path / f"ccache-{name}"
ccache_dir.mkdir()
subprocess.run(
[
tools["verilator"],
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
top,
*(f"+incdir+{incdir}" for incdir in INCDIRS),
*(str(source) for source in sources),
"-Mdir",
str(object_dir),
"-o",
"simv",
],
check=True,
text=True,
capture_output=True,
env={
**os.environ,
"CCACHE_DIR": str(ccache_dir),
"CCACHE_TEMPDIR": str(ccache_dir),
},
)
return object_dir / "simv"
def _run_shell_test(tools: dict[str, str], simulator: str, tmp_path: Path, name: str,
top: str, testbench: Path, marker: str) -> None:
sources = [*COMMON_SOURCES, *SHELL_SOURCES, testbench]
if simulator == "iverilog":
simulation = _build_iverilog(tools, tmp_path, name, top, sources)
command = [tools["vvp"], str(simulation)]
elif simulator == "verilator":
command = [str(_build_verilator(tools, tmp_path, name, top, sources))]
else:
raise AssertionError(f"unknown simulator {simulator}")
result = subprocess.run(command, check=True, text=True, capture_output=True)
assert marker in result.stdout
@pytest.mark.parametrize("simulator", ("iverilog", "verilator"))
def test_npu_reg_shell(tmp_path: Path, simulator: str) -> None:
tools = _tools()
_run_shell_test(
tools,
simulator,
tmp_path,
f"npu_reg_{simulator}",
"npu_reg_tb",
ROOT / "tests/rtl/npu_reg_tb.sv",
"NPU reg test passed",
)
@pytest.mark.parametrize("simulator", ("iverilog", "verilator"))
def test_npu_control_cdc(tmp_path: Path, simulator: str) -> None:
tools = _tools()
_run_shell_test(
tools,
simulator,
tmp_path,
f"npu_control_cdc_{simulator}",
"npu_control_cdc_tb",
ROOT / "tests/rtl/npu_control_cdc_tb.sv",
"NPU control CDC test passed",
)
+2 -1
View File
@@ -96,7 +96,8 @@ def test_application_results_keep_early_return_and_repeated_stage_values():
assert [r["id"] for r in smoke["stages"] if r["code"] == 12] == ["sram", "usb"] assert [r["id"] for r in smoke["stages"] if r["code"] == 12] == ["sram", "usb"]
assert [r["id"] for r in smoke["stages"] if r["code"] == 13] == ["monitor-start", "monitor-check"] assert [r["id"] for r in smoke["stages"] if r["code"] == 13] == ["monitor-start", "monitor-check"]
assert [r["id"] for r in smoke["stages"] if r["code"] == 15] == ["external-irq", "ga2d-p5-composition-dma"] assert [r["id"] for r in smoke["stages"] if r["code"] == 15] == ["external-irq", "ga2d-p5-composition-dma"]
assert len(smoke["stages"]) == 19 assert [r["id"] for r in smoke["stages"] if r["code"] == 16] == ["npu-p2-shell-irq"]
assert len(smoke["stages"]) == 20
@pytest.mark.parametrize("mutation", ["omit", "duplicate-stage", "value", "scope", "trigger"]) @pytest.mark.parametrize("mutation", ["omit", "duplicate-stage", "value", "scope", "trigger"])
+44 -16
View File
@@ -80,7 +80,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
).read_text(encoding="utf-8") ).read_text(encoding="utf-8")
filelist = (tmp_path / "soc_topology.fl").read_text(encoding="utf-8") filelist = (tmp_path / "soc_topology.fl").read_text(encoding="utf-8")
assert interfaces.count("apb4_if u_") == 27 assert interfaces.count("apb4_if u_") == 28
assert "nmi_if" not in interfaces assert "nmi_if" not in interfaces
assert "soc_nmi" not in interfaces assert "soc_nmi" not in interfaces
assert "assign s_psel_comb[17] = `SOC_ADDR_IS_APB4_I2C1(s_decode_addr);" in routes assert "assign s_psel_comb[17] = `SOC_ADDR_IS_APB4_I2C1(s_decode_addr);" in routes
@@ -98,7 +98,9 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "assign s_psel_comb[26] = `SOC_ADDR_IS_APB4_JPEG(s_decode_addr);" in routes assert "assign s_psel_comb[26] = `SOC_ADDR_IS_APB4_JPEG(s_decode_addr);" in routes
assert "assign s_psel_comb[27] = `SOC_ADDR_IS_APB4_APU(s_decode_addr);" in routes assert "assign s_psel_comb[27] = `SOC_ADDR_IS_APB4_APU(s_decode_addr);" in routes
assert "assign s_psel_comb[28] = `SOC_ADDR_IS_APB4_GA2D(s_decode_addr);" in routes assert "assign s_psel_comb[28] = `SOC_ADDR_IS_APB4_GA2D(s_decode_addr);" in routes
assert "assign s_psel_comb[29] = `SOC_ADDR_IS_APB4_NPU(s_decode_addr);" in routes
assert "apb4_if u_ga2d_apb4_if (clk_i, rst_n_i);" in interfaces assert "apb4_if u_ga2d_apb4_if (clk_i, rst_n_i);" in interfaces
assert "apb4_if u_npu_apb4_if (clk_i, rst_n_i);" in interfaces
assert gpio.count("// GPIO") == 64 assert gpio.count("// GPIO") == 64
assert "u_uart1_if" not in gpio assert "u_uart1_if" not in gpio
assert "assign u_uart0_if.cts_n_i = u_gpio_if.di_i[0];" in gpio assert "assign u_uart0_if.cts_n_i = u_gpio_if.di_i[0];" in gpio
@@ -147,7 +149,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert ".usb2_axi4(u_usb2_axi4_if)" in apb_periph_fabric assert ".usb2_axi4(u_usb2_axi4_if)" in apb_periph_fabric
assert "`define SOC_IRQ_VECTOR_WIDTH 64" in irq_config assert "`define SOC_IRQ_VECTOR_WIDTH 64" in irq_config
assert "`define SOC_USER_IRQ_MASK 64'h00000000004EFBFC" in irq_config assert "`define SOC_USER_IRQ_MASK 64'h00000000004EFBFC" in irq_config
assert "`define SOC_IRQ_APB4_PERIPH_WIDTH 25" in irq_config assert "`define SOC_IRQ_APB4_PERIPH_WIDTH 26" in irq_config
assert "`define SOC_IRQ_APB4_SYSTEM_WIDTH 8" in irq_config assert "`define SOC_IRQ_APB4_SYSTEM_WIDTH 8" in irq_config
assert "assign irq_o[0] = u_clint_if.software_irq_o[0];" in rib_irq assert "assign irq_o[0] = u_clint_if.software_irq_o[0];" in rib_irq
assert "assign irq_o[10] = ws2812.irq_o;" in rib_irq assert "assign irq_o[10] = ws2812.irq_o;" in rib_irq
@@ -163,6 +165,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "assign irq_o[22] = resource_irq_lp_i[5];" in rib_irq assert "assign irq_o[22] = resource_irq_lp_i[5];" in rib_irq
assert "assign irq_o[23] = resource_irq_lp_i[6];" in rib_irq assert "assign irq_o[23] = resource_irq_lp_i[6];" in rib_irq
assert "assign irq_o[24] = resource_irq_lp_i[7];" in rib_irq assert "assign irq_o[24] = resource_irq_lp_i[7];" in rib_irq
assert "assign irq_o[25] = resource_irq_lp_i[8];" in rib_irq
assert "assign irq_o[0] = pwm.irq_o;" in apb_irq assert "assign irq_o[0] = pwm.irq_o;" in apb_irq
assert "assign irq_o[4] = s_rng_irq;" in apb_irq assert "assign irq_o[4] = s_rng_irq;" in apb_irq
assert "assign irq_o[5] = s_ext_l_irq;" in apb_irq assert "assign irq_o[5] = s_ext_l_irq;" in apb_irq
@@ -182,6 +185,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "s_irq[30] = s_apb4_periph_irq[22];" in irq_wiring assert "s_irq[30] = s_apb4_periph_irq[22];" in irq_wiring
assert "s_irq[31] = s_apb4_periph_irq[23];" in irq_wiring assert "s_irq[31] = s_apb4_periph_irq[23];" in irq_wiring
assert "s_irq[32] = s_apb4_periph_irq[24];" in irq_wiring assert "s_irq[32] = s_apb4_periph_irq[24];" in irq_wiring
assert "s_irq[33] = s_apb4_periph_irq[25];" in irq_wiring
assert "irq_i[10] == apb4_periph_irq_i[16]" in irq_sva assert "irq_i[10] == apb4_periph_irq_i[16]" in irq_sva
assert "irq_i[15] == apb4_periph_irq_i[13]" in irq_sva assert "irq_i[15] == apb4_periph_irq_i[13]" in irq_sva
assert "irq_i[20] == apb4_periph_irq_i[14]" in irq_sva assert "irq_i[20] == apb4_periph_irq_i[14]" in irq_sva
@@ -192,6 +196,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "irq_i[30] == apb4_periph_irq_i[22]" in irq_sva assert "irq_i[30] == apb4_periph_irq_i[22]" in irq_sva
assert "irq_i[31] == apb4_periph_irq_i[23]" in irq_sva assert "irq_i[31] == apb4_periph_irq_i[23]" in irq_sva
assert "irq_i[32] == apb4_periph_irq_i[24]" in irq_sva assert "irq_i[32] == apb4_periph_irq_i[24]" in irq_sva
assert "irq_i[33] == apb4_periph_irq_i[25]" in irq_sva
assert "bind retrosoc soc_irq_topology_sva" in irq_sva assert "bind retrosoc soc_irq_topology_sva" in irq_sva
assert ".clk_i(clk_lp_i)" in irq_sva assert ".clk_i(clk_lp_i)" in irq_sva
assert ".rst_n_i(rst_lp_n_i)" in irq_sva assert ".rst_n_i(rst_lp_n_i)" in irq_sva
@@ -199,10 +204,10 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "SOC_DATA_POLICY_WRITE_TARGET_MASK" in data_policy assert "SOC_DATA_POLICY_WRITE_TARGET_MASK" in data_policy
assert "SOC_DATA_POLICY_ALLOW_INSTRUCTION" in data_policy assert "SOC_DATA_POLICY_ALLOW_INSTRUCTION" in data_policy
assert "SOC_DATA_POLICY_REQUIRE_NONCACHEABLE" in data_policy assert "SOC_DATA_POLICY_REQUIRE_NONCACHEABLE" in data_policy
assert "`define SOC_DATA_POLICY_READ_TARGET_MASK 45'b111111111111111111111111111111111111111111111" in data_policy assert "`define SOC_DATA_POLICY_READ_TARGET_MASK 50'b11111111111111111111111111111111111111111111111111" in data_policy
assert "`define SOC_DATA_POLICY_WRITE_TARGET_MASK 45'b011110111101111011110111101111011110111100000" in data_policy assert "`define SOC_DATA_POLICY_WRITE_TARGET_MASK 50'b01111011110111101111011110111101111011110111100000" in data_policy
assert "`define SOC_DATA_POLICY_ALLOW_INSTRUCTION 9'b000000001" in data_policy assert "`define SOC_DATA_POLICY_ALLOW_INSTRUCTION 10'b0000000001" in data_policy
assert "`define SOC_DATA_POLICY_REQUIRE_NONCACHEABLE 9'b111111100" in data_policy assert "`define SOC_DATA_POLICY_REQUIRE_NONCACHEABLE 10'b1111111100" in data_policy
assert "RS_SOC_IRQ_VECTOR_WIDTH UINT32_C(64)" in irq_metadata assert "RS_SOC_IRQ_VECTOR_WIDTH UINT32_C(64)" in irq_metadata
assert "RS_SOC_EXTERNAL_IRQ_COUNT UINT32_C(62)" in irq_metadata assert "RS_SOC_EXTERNAL_IRQ_COUNT UINT32_C(62)" in irq_metadata
assert "RS_SOC_IRQ_UART0 UINT32_C(2)" in irq_metadata assert "RS_SOC_IRQ_UART0 UINT32_C(2)" in irq_metadata
@@ -210,6 +215,8 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "RS_SOC_IRQ_APU" in irq_metadata assert "RS_SOC_IRQ_APU" in irq_metadata
assert "RS_SOC_IRQ_GA2D UINT32_C(32)" in irq_metadata assert "RS_SOC_IRQ_GA2D UINT32_C(32)" in irq_metadata
assert "RS_SOC_EXT_IRQ_GA2D UINT32_C(30)" in irq_metadata assert "RS_SOC_EXT_IRQ_GA2D UINT32_C(30)" in irq_metadata
assert "RS_SOC_IRQ_NPU UINT32_C(33)" in irq_metadata
assert "RS_SOC_EXT_IRQ_NPU UINT32_C(31)" in irq_metadata
assert filelist.startswith("+incdir+") assert filelist.startswith("+incdir+")
@@ -260,6 +267,7 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None:
("resource_fault", "apb4_system", 7, 29, "s_resource_fault_irq"), ("resource_fault", "apb4_system", 7, 29, "s_resource_fault_irq"),
("apu", "apb4_periph", 23, 31, "resource_irq_lp_i[6]"), ("apu", "apb4_periph", 23, 31, "resource_irq_lp_i[6]"),
("ga2d", "apb4_periph", 24, 32, "resource_irq_lp_i[7]"), ("ga2d", "apb4_periph", 24, 32, "resource_irq_lp_i[7]"),
("npu", "apb4_periph", 25, 33, "resource_irq_lp_i[8]"),
] ]
@@ -277,8 +285,9 @@ def test_ga2d_p4_topology_preserves_the_private_master_policy_and_active_shell()
"jpeg", "jpeg",
"ext_h", "ext_h",
"ga2d", "ga2d",
"npu",
] ]
assert [policy["index"] for policy in policies] == list(range(9)) assert [policy["index"] for policy in policies] == list(range(10))
assert policies[8] == { assert policies[8] == {
"index": 8, "index": 8,
"name": "ga2d", "name": "ga2d",
@@ -287,12 +296,26 @@ def test_ga2d_p4_topology_preserves_the_private_master_policy_and_active_shell()
"allow_instruction": False, "allow_instruction": False,
"require_noncacheable": True, "require_noncacheable": True,
} }
assert document["apb4_periph_targets"][-1]["name"] == "ga2d" assert policies[9] == {
assert document["apb4_periph_targets"][-1]["slot"] == 28 "index": 9,
assert document["apb4_periph_targets"][-1]["region"] == "APB4_GA2D" "name": "npu",
assert document["interrupts"][-1]["name"] == "ga2d" "read_targets": ["sram", "sdram", "qpi", "opi", "xpi"],
assert document["interrupts"][-1]["group_bit"] == 24 "write_targets": ["sram", "sdram", "qpi", "opi"],
assert document["interrupts"][-1]["core_bit"] == 32 "allow_instruction": False,
"require_noncacheable": True,
}
assert document["apb4_periph_targets"][-2]["name"] == "ga2d"
assert document["apb4_periph_targets"][-2]["slot"] == 28
assert document["apb4_periph_targets"][-2]["region"] == "APB4_GA2D"
assert document["apb4_periph_targets"][-1]["name"] == "npu"
assert document["apb4_periph_targets"][-1]["slot"] == 29
assert document["apb4_periph_targets"][-1]["region"] == "APB4_NPU"
assert document["interrupts"][-2]["name"] == "ga2d"
assert document["interrupts"][-2]["group_bit"] == 24
assert document["interrupts"][-2]["core_bit"] == 32
assert document["interrupts"][-1]["name"] == "npu"
assert document["interrupts"][-1]["group_bit"] == 25
assert document["interrupts"][-1]["core_bit"] == 33
def test_apu_p1_fixed_resource_and_hp_irq_allocations() -> None: def test_apu_p1_fixed_resource_and_hp_irq_allocations() -> None:
@@ -305,16 +328,21 @@ def test_apu_p1_fixed_resource_and_hp_irq_allocations() -> None:
assert "s_hp_plic_source[10] = resource_irq_hp_i[6];" in periph assert "s_hp_plic_source[10] = resource_irq_hp_i[6];" in periph
assert "s_hp_plic_source[11] = resource_irq_hp_i[7];" in periph assert "s_hp_plic_source[11] = resource_irq_hp_i[7];" in periph
assert "s_hp_plic_source[12] = resource_irq_hp_i[8];" in periph
assert "s_ga2d_irq_raw," in periph assert "s_ga2d_irq_raw," in periph
assert ".ResourceCount(9)" in system assert ".ResourceCount(10)" in system
assert ".apu_owner_i (s_resource_owner[7])" in top assert ".apu_owner_i (s_resource_owner[7])" in top
assert ".apu_quiesce_i (s_resource_quiesce[7])" in top assert ".apu_quiesce_i (s_resource_quiesce[7])" in top
assert ".apu_reset_i (s_resource_reset[7])" in top assert ".apu_reset_i (s_resource_reset[7])" in top
compact_top = "".join(top.split()) compact_top = "".join(top.split())
assert ".ga2d_quiesce_i(s_resource_quiesce[8])" in compact_top assert ".ga2d_quiesce_i(s_resource_quiesce[8])" in compact_top
assert ".ga2d_reset_i(s_resource_reset[8])" in compact_top assert ".ga2d_reset_i(s_resource_reset[8])" in compact_top
assert ".npu_quiesce_i(s_resource_quiesce[9])" in compact_top
assert ".npu_reset_i(s_resource_reset[9])" in compact_top
assert "RS_RESOURCE_APU = 7" in resource_header assert "RS_RESOURCE_APU = 7" in resource_header
assert "RS_RESOURCE_COUNT = 9" in resource_header assert "RS_RESOURCE_GA2D = 8" in resource_header
assert "RS_RESOURCE_NPU = 9" in resource_header
assert "RS_RESOURCE_COUNT = 10" in resource_header
def test_topology_always_adds_the_user_apb_target(tmp_path: Path) -> None: def test_topology_always_adds_the_user_apb_target(tmp_path: Path) -> None:
@@ -471,7 +499,7 @@ def test_topology_rejects_missing_ga2d_data_policy(tmp_path: Path) -> None:
document["data_master_policies"].pop() document["data_master_policies"].pop()
result = validate(write_invalid_topology(tmp_path, document)) result = validate(write_invalid_topology(tmp_path, document))
assert result.returncode != 0 assert result.returncode != 0
assert "all 9 data masters" in result.stderr assert "all 10 data masters" in result.stderr
def test_generated_irq_wiring_preserves_the_expected_core_vector(tmp_path: Path) -> None: def test_generated_irq_wiring_preserves_the_expected_core_vector(tmp_path: Path) -> None: