mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
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:
54 files changed
+6482
-190
No files matched your search
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
|
|||||||
@@ -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 |
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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": [
|
||||||
|
|||||||
@@ -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.",
|
||||||
|
|||||||
@@ -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": [
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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",
|
||||||
|
|||||||
@@ -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 = (
|
||||||
|
|||||||
@@ -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": [
|
||||||
|
|||||||
@@ -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
@@ -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),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,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
@@ -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},
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -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
@@ -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(),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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",
|
||||||
|
)
|
||||||
@@ -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
@@ -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:
|
||||||
|
|||||||
Reference in new issue
Block a user