mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
feat: replace the rom of apu into sram
This commit is contained in:
74 files changed
+4726
-297
No files matched your search
@@ -115,7 +115,7 @@ jobs:
|
||||
tools: verilator sv2v iverilog yosys
|
||||
|
||||
- name: Set up locked accelerator references
|
||||
run: make setup-apu-reference setup-npu-reference
|
||||
run: make setup-apu-reference setup-apu-kws-reference setup-npu-reference
|
||||
|
||||
- name: Test scripts and RTL fixtures
|
||||
run: python3 -m pytest -q
|
||||
|
||||
@@ -136,6 +136,11 @@ APU_P5_CORPUS_RTL_DIR := $(APU_P5_DIR)/corpus-rtl
|
||||
APU_P7_DIR := $(VARIANT_ROOT)/apu/kws
|
||||
APU_P7_MODEL := $(APU_P7_DIR)/apu-p7.apum
|
||||
APU_P7_MODEL_MANIFEST := $(APU_P7_DIR)/apu-p7-manifest.json
|
||||
APU_P9_COEFFICIENT_DIR := $(VARIANT_ROOT)/apu/coefficients
|
||||
APU_P9_APUC := $(APU_P9_COEFFICIENT_DIR)/apu-p9.apuc
|
||||
APU_P9_LAYOUT_MANIFEST := $(APU_P9_COEFFICIENT_DIR)/coefficient-layout.json
|
||||
APU_P9_EVIDENCE_DIR := $(VARIANT_ROOT)/apu/p9/evidence
|
||||
APU_P9_EVIDENCE_LAYOUT := $(APU_P9_EVIDENCE_DIR)/coefficient-layout.json
|
||||
APU_P7_KWS_TFLITE := $(CACHE_ROOT)/sources/apu-mlperf-tiny/benchmark/training/keyword_spotting/trained_models/kws_ref_model.tflite
|
||||
NPU_P0_DIR := $(VARIANT_ROOT)/npu/p0
|
||||
NPU_P5_DIR := $(VARIANT_ROOT)/npu/p5
|
||||
@@ -416,7 +421,7 @@ endif
|
||||
include physical/librelane/Makefile
|
||||
include physical/ecc/Makefile
|
||||
|
||||
.PHONY: help config doctor setup setup-regression setup-mpw setup-vexiiriscv setup-clusterip setup-ip setup-pdk setup-app setup-apu-reference setup-apu-kws-reference apu-p5-bundle apu-p5-corpus apu-p7-model setup-hp-linux hp-linux hp-bundle hp-linux-sim hp-smoke-bundle hp-smoke-sim hp-apu-bundle hp-apu-sim \
|
||||
.PHONY: help config doctor setup setup-regression setup-mpw setup-vexiiriscv setup-clusterip setup-ip setup-pdk setup-app setup-apu-reference setup-apu-kws-reference apu-p5-bundle apu-p5-corpus apu-p7-model apu-p9-coefficients apu-p9-evidence apu-p9-memory-ab setup-hp-linux hp-linux hp-bundle hp-linux-sim hp-smoke-bundle hp-smoke-sim hp-apu-bundle hp-apu-sim \
|
||||
clean-all purge-cache manifest check-warnings metrics check-metrics package commercial-package \
|
||||
regress-smoke regress-rtl regress-pr regress-nightly sim-asm format format-check sw-format sw-format-check mk-format \
|
||||
mk-format-check rtl-format rtl-format-check rtl-style-check rtl-migrate-connections rtl-migrate-names sw-policy-check sw-host-test \
|
||||
@@ -425,7 +430,7 @@ include physical/ecc/Makefile
|
||||
apu-block-filelist apu-block-synth apu-block-sta apu-block-report apu-block-evidence apu-block-clean \
|
||||
pin-map check-pin-map soc-topology check-soc-topology user-extensions check-user-extensions \
|
||||
check-clock-reset-domains tech-cell-test rtl-lint check-rtl-lint \
|
||||
formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-dvp formal-i2s formal-onchip-ram formal-opipsram formal-dma formal-apu formal-apu-kws formal-gateway-a formal-sdio formal-clean formal-doctor \
|
||||
formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-dvp formal-i2s formal-onchip-ram formal-opipsram formal-dma formal-apu formal-apu-kws formal-apu-p9 formal-gateway-a formal-sdio formal-clean formal-doctor \
|
||||
rtl-style-check-all rtl-readiness-check rtl-readiness-check-all vexii-generate
|
||||
.NOTPARALLEL: setup
|
||||
|
||||
@@ -458,6 +463,9 @@ help:
|
||||
' apu-p5-bundle build the deterministic WAV/FLAC APUMC bundle' \
|
||||
' apu-p5-corpus qualify pinned FLAC with BAM/libFLAC and production RTL' \
|
||||
' apu-p7-model convert the locked MLPerf Tiny KWS model to APUM' \
|
||||
' apu-p9-coefficients build the frozen APUC image and 15-bank manifest' \
|
||||
' apu-p9-evidence initialize the six fail-closed P9 evidence reports' \
|
||||
' apu-p9-memory-ab compare explicit baseline/candidate synthesis roots' \
|
||||
' setup-npu-reference install/verify locked NPU models, corpora, and oracle' \
|
||||
' npu-p0-qualify qualify full corpora against the independent oracle' \
|
||||
' npu-p5-deployments build deterministic KWS/VWW ABI-1 packages' \
|
||||
@@ -497,7 +505,7 @@ help:
|
||||
' check-clock-reset-domains validate the root clock/reset and CDC inventory' \
|
||||
' rtl-lint | check-rtl-lint run/check strict Verilator RTL lint warnings' \
|
||||
' formal | formal-bus | formal-rib-adapter | formal-rib2apb run SBY protocol proofs' \
|
||||
'formal-sysctrl | formal-pll-rcu | formal-gpio | formal-ws2812 | formal-uart | formal-i2c | formal-timer | formal-clint | formal-dvp | formal-i2s | formal-onchip-ram | formal-opipsram | formal-dma | formal-ga2d | formal-apu | formal-apu-kws | formal-gateway-a | formal-sdio run peripheral proofs' \
|
||||
'formal-sysctrl | formal-pll-rcu | formal-gpio | formal-ws2812 | formal-uart | formal-i2c | formal-timer | formal-clint | formal-dvp | formal-i2s | formal-onchip-ram | formal-opipsram | formal-dma | formal-ga2d | formal-apu | formal-apu-kws | formal-apu-p9 | formal-gateway-a | formal-sdio run peripheral proofs' \
|
||||
' formal-doctor check the SBY, Yosys, sv2v, and Bitwuzla formal toolchain' \
|
||||
' benchmark-report run the memory/DMA profile and write meta/performance.json' \
|
||||
' coremark-report run the quick CoreMark profile and write meta/coremark.json' \
|
||||
@@ -758,6 +766,32 @@ $(APU_P7_MODEL): $(ROOT_PATH)/scripts/apu_kws_convert.py \
|
||||
|
||||
apu-p7-model: $(APU_P7_MODEL)
|
||||
|
||||
$(APU_P9_APUC) $(APU_P9_LAYOUT_MANIFEST) &: $(APU_P7_MODEL) \
|
||||
$(ROOT_PATH)/scripts/generate_apu_kws_rtl_constants.py \
|
||||
$(ROOT_PATH)/scripts/apu_kws_coeff.py
|
||||
python3 $(ROOT_PATH)/scripts/generate_apu_kws_rtl_constants.py \
|
||||
--apum $(APU_P7_MODEL) \
|
||||
--output $(APU_P9_COEFFICIENT_DIR)/apu_kws_rom.svh \
|
||||
--profile-output $(APU_P9_COEFFICIENT_DIR)/apu_kws_apum_profile.svh \
|
||||
--apuc-output $(APU_P9_APUC) --manifest-output $(APU_P9_LAYOUT_MANIFEST)
|
||||
|
||||
apu-p9-coefficients: $(APU_P9_APUC) $(APU_P9_LAYOUT_MANIFEST)
|
||||
|
||||
$(APU_P9_EVIDENCE_LAYOUT): $(APU_P9_LAYOUT_MANIFEST) $(APU_P9_APUC) \
|
||||
$(ROOT_PATH)/scripts/apu_p9_evidence.py
|
||||
python3 $(ROOT_PATH)/scripts/apu_p9_evidence.py initialize \
|
||||
--layout $(APU_P9_LAYOUT_MANIFEST) --apuc $(APU_P9_APUC) \
|
||||
--profile $(CONFIG) \
|
||||
--output-dir $(APU_P9_EVIDENCE_DIR)
|
||||
|
||||
apu-p9-evidence: $(APU_P9_EVIDENCE_LAYOUT)
|
||||
|
||||
apu-p9-memory-ab:
|
||||
@test -n '$(APU_P9_BASELINE_ROOT)' -a -n '$(APU_P9_CANDIDATE_ROOT)'
|
||||
python3 $(ROOT_PATH)/scripts/apu_p9_memory_ab.py \
|
||||
--baseline-root $(APU_P9_BASELINE_ROOT) --candidate-root $(APU_P9_CANDIDATE_ROOT) \
|
||||
--output $(APU_P9_EVIDENCE_DIR)/memory-synthesis-ab.json
|
||||
|
||||
apu-p5-corpus: setup-apu-reference $(APU_P5_BUNDLE)
|
||||
python3 $(ROOT_PATH)/scripts/qualify_apu_p5_corpus.py \
|
||||
--flac $(APU_P5_REFERENCE_DIR)/src/flac/flac \
|
||||
|
||||
@@ -4,12 +4,13 @@ APP_INC_DIRS += $(ROOT_PATH)/app/apps/apu_release $(ROOT_PATH)/app/apps/hp_boot
|
||||
|
||||
APU_RELEASE_ASSETS_H := $(SW_BUILD_DIR)/include/apu_release_assets.h
|
||||
|
||||
$(APU_RELEASE_ASSETS_H): $(APU_P5_BUNDLE) $(APU_P7_MODEL) \
|
||||
$(APU_RELEASE_ASSETS_H): $(APU_P5_BUNDLE) $(APU_P7_MODEL) $(APU_P9_APUC) \
|
||||
$(ROOT_PATH)/scripts/build_apu_release_assets.py \
|
||||
$(ROOT_PATH)/scripts/apu_codecs.py $(ROOT_PATH)/scripts/apu_kws.py \
|
||||
$(ROOT_PATH)/scripts/apu_kws_coeff.py \
|
||||
$(ROOT_PATH)/scripts/apu_p5_coefficients.py
|
||||
python3 $(ROOT_PATH)/scripts/build_apu_release_assets.py \
|
||||
--apumc $(APU_P5_BUNDLE) --apum $(APU_P7_MODEL) \
|
||||
--apumc $(APU_P5_BUNDLE) --apuc $(APU_P9_APUC) --apum $(APU_P7_MODEL) \
|
||||
--kws-corpus $(CACHE_ROOT)/sources/apu-kws-pcm \
|
||||
--output $@
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#define RS_APU_RELEASE_MODEL_CAPACITY UINT32_C(0x00008000)
|
||||
#define RS_APU_RELEASE_WAV_ADDRESS UINT32_C(0x3BC20000)
|
||||
#define RS_APU_RELEASE_WAV_CAPACITY UINT32_C(0x00010000)
|
||||
#define RS_APU_RELEASE_COEFF_ADDRESS UINT32_C(0x3BC30000)
|
||||
#define RS_APU_RELEASE_COEFF_CAPACITY UINT32_C(0x00010000)
|
||||
#define RS_APU_RELEASE_PCM_ADDRESS UINT32_C(0x3BC40000)
|
||||
#define RS_APU_RELEASE_PCM_CAPACITY UINT32_C(0x00010000)
|
||||
#define RS_APU_RELEASE_KWS_ADDRESS UINT32_C(0x3BC60000)
|
||||
|
||||
@@ -39,6 +39,17 @@ static rs_dma_tcd_t s_apu_release_tcd __attribute__((aligned(64)));
|
||||
|
||||
_Static_assert(sizeof(rs_hp_boot_header_t) == UINT32_C(128), "HP bundle header ABI mismatch");
|
||||
|
||||
static uint32_t rs_apu_release_cycle(void) {
|
||||
uint32_t cycle;
|
||||
|
||||
__asm__ volatile("csrr %0, mcycle" : "=r"(cycle));
|
||||
return cycle;
|
||||
}
|
||||
|
||||
static void rs_apu_release_milestone(const char *name) {
|
||||
printf("APU_RELEASE_CYCLE:%s:%08lx\n", name, (unsigned long)rs_apu_release_cycle());
|
||||
}
|
||||
|
||||
static uint32_t rs_apu_release_crc32_byte(uint32_t crc, uint8_t value) {
|
||||
crc ^= value;
|
||||
for (uint32_t bit = 0U; bit < 8U; ++bit) {
|
||||
@@ -232,6 +243,11 @@ static bool rs_apu_release_stage_assets(void) {
|
||||
RS_APU_RELEASE_APUMC_SIZE, RS_APU_RELEASE_APUMC_CRC)) {
|
||||
return false;
|
||||
}
|
||||
if (!rs_apu_release_stage((uint32_t)(uintptr_t)rs_apu_release_apuc,
|
||||
RS_APU_RELEASE_COEFF_ADDRESS, RS_APU_RELEASE_APUC_SIZE,
|
||||
RS_APU_RELEASE_APUC_CRC)) {
|
||||
return false;
|
||||
}
|
||||
if (!rs_apu_release_stage((uint32_t)(uintptr_t)rs_apu_release_apum,
|
||||
RS_APU_RELEASE_MODEL_ADDRESS, RS_APU_RELEASE_APUM_SIZE,
|
||||
RS_APU_RELEASE_APUM_CRC)) {
|
||||
@@ -272,6 +288,11 @@ static uint32_t rs_apu_release_load_images(void) {
|
||||
.bytes = RS_APU_RELEASE_APUM_SIZE,
|
||||
.expected_crc = RS_APU_RELEASE_APUM_PAYLOAD_CRC,
|
||||
};
|
||||
const rs_apu_image_t coefficient_image = {
|
||||
.address = RS_APU_RELEASE_COEFF_ADDRESS,
|
||||
.bytes = RS_APU_RELEASE_APUC_SIZE,
|
||||
.expected_crc = RS_APU_RELEASE_APUC_PAYLOAD_CRC,
|
||||
};
|
||||
uint32_t status;
|
||||
rs_status_t load_status;
|
||||
|
||||
@@ -290,20 +311,37 @@ static uint32_t rs_apu_release_load_images(void) {
|
||||
return 2U;
|
||||
}
|
||||
printf("APU_RELEASE_MC:%08lx\n", (unsigned long)RS_APU_REG(RS_APU_ABI_MC_ACTUAL_CRC));
|
||||
rs_apu_release_milestone("MC");
|
||||
__asm__ volatile("fence rw, rw" ::: "memory");
|
||||
load_status = rs_apu_kws_coeff_load(&coefficient_image, RS_APU_RELEASE_LOAD_TIMEOUT);
|
||||
if (load_status != RS_OK) {
|
||||
printf("APU_RELEASE_COEFFERR:%d\n", (int)load_status);
|
||||
rs_apu_release_dump_apu();
|
||||
return 3U;
|
||||
}
|
||||
status = RS_APU_REG(RS_APU_ABI_KWS_COEFF_STATUS);
|
||||
if (((status & UINT32_C(6)) != UINT32_C(6)) ||
|
||||
(RS_APU_REG(RS_APU_ABI_KWS_COEFF_ACTUAL_CRC) != RS_APU_RELEASE_APUC_PAYLOAD_CRC)) {
|
||||
rs_apu_release_dump_apu();
|
||||
return 4U;
|
||||
}
|
||||
printf("APU_RELEASE_COEFF:%08lx\n", (unsigned long)RS_APU_REG(RS_APU_ABI_KWS_COEFF_ACTUAL_CRC));
|
||||
rs_apu_release_milestone("COEFF");
|
||||
__asm__ volatile("fence rw, rw" ::: "memory");
|
||||
load_status = rs_apu_kws_model_load(&model_image, RS_APU_RELEASE_LOAD_TIMEOUT);
|
||||
if (load_status != RS_OK) {
|
||||
printf("APU_RELEASE_MODELERR:%d\n", (int)load_status);
|
||||
rs_apu_release_dump_apu();
|
||||
return 3U;
|
||||
return 5U;
|
||||
}
|
||||
status = RS_APU_REG(RS_APU_ABI_KWS_MODEL_STATUS);
|
||||
if (((status & UINT32_C(6)) != UINT32_C(6)) ||
|
||||
(RS_APU_REG(RS_APU_ABI_KWS_MODEL_ACTUAL_CRC) != RS_APU_RELEASE_APUM_PAYLOAD_CRC)) {
|
||||
rs_apu_release_dump_apu();
|
||||
return 4U;
|
||||
return 6U;
|
||||
}
|
||||
printf("APU_RELEASE_MODEL:%08lx\n", (unsigned long)RS_APU_REG(RS_APU_ABI_KWS_MODEL_ACTUAL_CRC));
|
||||
rs_apu_release_milestone("MODEL");
|
||||
return 0U;
|
||||
}
|
||||
|
||||
@@ -361,6 +399,7 @@ static bool rs_apu_release_handoff(void) {
|
||||
((owner & (UINT32_C(1) << RS_APU_ABI_OWNER_STATUS_QUIESCE)) == 0U) &&
|
||||
((owner & (UINT32_C(1) << RS_APU_ABI_OWNER_STATUS_RESET)) == 0U)) {
|
||||
printf("APU_RELEASE_HANDOFF:%u\n", (unsigned int)after.handoff_count);
|
||||
rs_apu_release_milestone("HANDOFF");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -418,6 +457,7 @@ static void rs_apu_release_run(void) {
|
||||
rs_test_finish(RS_TEST_FAILED, UINT8_C(1));
|
||||
}
|
||||
printf("APU_RELEASE_BOOT\n");
|
||||
rs_apu_release_milestone("BOOT");
|
||||
if ((rs_sysctrl_set_hp_release(false) != RS_OK) ||
|
||||
(rs_sysctrl_select_hp_debug(true) != RS_OK) ||
|
||||
(rs_sysctrl_get_hp_status(&hp_status) != RS_OK) || !hp_status.present ||
|
||||
@@ -425,14 +465,17 @@ static void rs_apu_release_run(void) {
|
||||
rs_apu_release_fail(UINT8_C(2));
|
||||
}
|
||||
printf("APU_RELEASE_HP_HELD\n");
|
||||
rs_apu_release_milestone("HP_HELD");
|
||||
if (!rs_apu_release_wait_sdram()) {
|
||||
rs_apu_release_fail(UINT8_C(3));
|
||||
}
|
||||
printf("APU_RELEASE_SDRAM\n");
|
||||
rs_apu_release_milestone("SDRAM");
|
||||
if (!rs_apu_release_load_bundle()) {
|
||||
rs_apu_release_fail(UINT8_C(4));
|
||||
}
|
||||
printf("APU_RELEASE_BUNDLE\n");
|
||||
rs_apu_release_milestone("BUNDLE");
|
||||
if ((rs_apu_probe(&info) != RS_OK) || (info.capability0 != RS_APU_CAPABILITY0_P7_IMPLEMENTED) ||
|
||||
(info.abi_digest != RS_APU_DIGEST_P7_IMPLEMENTED)) {
|
||||
rs_apu_release_fail(UINT8_C(5));
|
||||
@@ -443,10 +486,12 @@ static void rs_apu_release_run(void) {
|
||||
rs_apu_release_fail(UINT8_C(6));
|
||||
}
|
||||
printf("APU_RELEASE_QUIESCED\n");
|
||||
rs_apu_release_milestone("QUIESCED");
|
||||
if (!rs_apu_release_stage_assets()) {
|
||||
rs_apu_release_fail(UINT8_C(7));
|
||||
}
|
||||
printf("APU_RELEASE_STAGED\n");
|
||||
rs_apu_release_milestone("STAGED");
|
||||
if (rs_apu_set_acl(RS_APU_RELEASE_ACL_READ_BASE, RS_APU_RELEASE_ACL_READ_LIMIT,
|
||||
RS_APU_RELEASE_ACL_WRITE_BASE, RS_APU_RELEASE_ACL_WRITE_LIMIT) != RS_OK) {
|
||||
rs_apu_release_fail(UINT8_C(8));
|
||||
@@ -471,6 +516,7 @@ static void rs_apu_release_run(void) {
|
||||
rs_apu_release_fail(UINT8_C(12));
|
||||
}
|
||||
printf("APU_RELEASE_HP_RUN\n");
|
||||
rs_apu_release_milestone("HP_RUN");
|
||||
|
||||
/* Wait for the HP done event; the bounded poll only exits early on a stuck
|
||||
* hart because every hardware failure path publishes its own event. */
|
||||
@@ -484,6 +530,7 @@ static void rs_apu_release_run(void) {
|
||||
(event.argument != 0U)) {
|
||||
rs_apu_release_fail(UINT8_C(13));
|
||||
}
|
||||
rs_apu_release_milestone("HP_EVENT");
|
||||
for (budget = 0U; budget < RS_APU_RELEASE_SETTLE_SPINS; ++budget) {
|
||||
__asm__ volatile("nop" ::: "memory");
|
||||
}
|
||||
@@ -501,6 +548,7 @@ static void rs_apu_release_run(void) {
|
||||
printf("APU_RELEASE_TRAP:%lu:%lu\n", (unsigned long)page->fault_mcause,
|
||||
(unsigned long)page->fault_mtval);
|
||||
printf("APU_RELEASE_PASS\n");
|
||||
rs_apu_release_milestone("PASS");
|
||||
rs_test_finish(RS_TEST_PASSED, UINT8_C(0));
|
||||
}
|
||||
|
||||
|
||||
@@ -309,6 +309,21 @@ static void hp_report(const char *marker, uint32_t value) {
|
||||
hp_uart_puts("\n");
|
||||
}
|
||||
|
||||
static uint32_t hp_cycle(void) {
|
||||
uint32_t cycle;
|
||||
|
||||
__asm__ volatile("csrr %0, mcycle" : "=r"(cycle));
|
||||
return cycle;
|
||||
}
|
||||
|
||||
static void hp_milestone(const char *name) {
|
||||
hp_uart_puts("HP_APU_CYCLE:");
|
||||
hp_uart_puts(name);
|
||||
hp_uart_puts(":");
|
||||
hp_uart_puthex(hp_cycle());
|
||||
hp_uart_puts("\n");
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
volatile rs_apu_release_page_t *page =
|
||||
(volatile rs_apu_release_page_t *)(uintptr_t)RS_APU_RELEASE_PAGE_ADDRESS;
|
||||
@@ -318,11 +333,13 @@ void main(void) {
|
||||
|
||||
hp_uart_init();
|
||||
hp_uart_puts("HP_APU_BOOT\n");
|
||||
hp_milestone("BOOT");
|
||||
if (!hp_mailbox_wait(&code, &argument)) {
|
||||
hp_uart_puts("HP_APU_NO_MAILBOX\n");
|
||||
for (;;) {
|
||||
}
|
||||
}
|
||||
hp_milestone("MAILBOX");
|
||||
hp_cbo_inval(RS_APU_RELEASE_PAGE_ADDRESS, RS_APU_RELEASE_PAGE_BYTES);
|
||||
page->hp_steps = RS_APU_RELEASE_STEP_MAILBOX;
|
||||
if ((code != RS_APU_RELEASE_JOB_RUN) || (argument != RS_APU_RELEASE_PAGE_ADDRESS) ||
|
||||
@@ -343,6 +360,7 @@ void main(void) {
|
||||
}
|
||||
}
|
||||
hp_report("HP_APU_WAV_DONE", page->wav_output_bytes);
|
||||
hp_milestone("WAV_DONE");
|
||||
if (!hp_submit_kws(page)) {
|
||||
hp_report("HP_APU_KWS_STOP", page->kws_status);
|
||||
page->hp_error = 4U;
|
||||
@@ -351,12 +369,15 @@ void main(void) {
|
||||
}
|
||||
}
|
||||
hp_report("HP_APU_KWS_DONE", page->kws_class_id);
|
||||
hp_milestone("KWS_DONE");
|
||||
hp_probe_lp_only(page);
|
||||
hp_report("HP_APU_TRAP_SEEN", page->fault_mcause);
|
||||
hp_milestone("TRAP");
|
||||
summary = (page->hp_steps == RS_APU_RELEASE_STEP_ALL) ? 0U : 5U;
|
||||
page->hp_error = summary;
|
||||
hp_publish(page, summary);
|
||||
hp_uart_puts("HP_APU_DONE\n");
|
||||
hp_milestone("DONE");
|
||||
for (;;) {
|
||||
}
|
||||
}
|
||||
@@ -112,6 +112,13 @@ typedef struct {
|
||||
uint32_t overrun_count;
|
||||
} rs_apu_kws_status_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t status;
|
||||
uint32_t actual_crc;
|
||||
uint32_t coefficient_id[2];
|
||||
uint32_t capacity_bytes;
|
||||
} rs_apu_kws_coeff_status_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t status;
|
||||
uint32_t address;
|
||||
@@ -199,6 +206,8 @@ rs_status_t rs_apu_irq_read(uint32_t *state);
|
||||
rs_status_t rs_apu_irq_enable(uint32_t mask);
|
||||
rs_status_t rs_apu_irq_ack(uint32_t mask);
|
||||
rs_status_t rs_apu_kws_model_load(const rs_apu_image_t *image, rs_timeout_t timeout);
|
||||
rs_status_t rs_apu_kws_coeff_load(const rs_apu_image_t *image, rs_timeout_t timeout);
|
||||
rs_status_t rs_apu_kws_coeff_status_read(rs_apu_kws_coeff_status_t *status);
|
||||
rs_status_t rs_apu_kws_configure(const rs_apu_kws_config_t *config);
|
||||
rs_status_t rs_apu_kws_enable(uint32_t memory_window);
|
||||
rs_status_t rs_apu_kws_disable(rs_timeout_t timeout);
|
||||
|
||||
@@ -81,6 +81,15 @@
|
||||
#define RS_APU_ABI_KWS_MODEL_STATUS UINT32_C(0x234)
|
||||
#define RS_APU_ABI_KWS_MODEL_ACTUAL_CRC UINT32_C(0x238)
|
||||
#define RS_APU_ABI_KWS_INPUT_CONFIG UINT32_C(0x23C)
|
||||
#define RS_APU_ABI_KWS_COEFF_ADDRESS UINT32_C(0x240)
|
||||
#define RS_APU_ABI_KWS_COEFF_SIZE UINT32_C(0x244)
|
||||
#define RS_APU_ABI_KWS_COEFF_EXPECTED_CRC UINT32_C(0x248)
|
||||
#define RS_APU_ABI_KWS_COEFF_COMMAND UINT32_C(0x24C)
|
||||
#define RS_APU_ABI_KWS_COEFF_STATUS UINT32_C(0x250)
|
||||
#define RS_APU_ABI_KWS_COEFF_ACTUAL_CRC UINT32_C(0x254)
|
||||
#define RS_APU_ABI_KWS_COEFF_ID_LO UINT32_C(0x258)
|
||||
#define RS_APU_ABI_KWS_COEFF_ID_HI UINT32_C(0x25C)
|
||||
#define RS_APU_ABI_KWS_COEFF_CAPACITY UINT32_C(0x260)
|
||||
#define RS_APU_ABI_PERF_CONTROL UINT32_C(0x300)
|
||||
#define RS_APU_ABI_PERF_STATUS UINT32_C(0x304)
|
||||
#define RS_APU_ABI_PERF_ACTIVE_CYCLES_LO UINT32_C(0x308)
|
||||
@@ -128,6 +137,7 @@
|
||||
#define RS_APU_ABI_CAPABILITY0_KWS 6U
|
||||
#define RS_APU_ABI_CAPABILITY0_SEQUENCER 7U
|
||||
#define RS_APU_ABI_CAPABILITY0_RESAMPLER 8U
|
||||
#define RS_APU_ABI_CAPABILITY0_COEFFICIENT_LOAD 9U
|
||||
#define RS_APU_ABI_CAPABILITY1_CONTROL_KIB 0U
|
||||
#define RS_APU_ABI_CAPABILITY1_DATA_KIB 8U
|
||||
#define RS_APU_ABI_CAPABILITY1_MAX_CHANNELS 16U
|
||||
@@ -172,6 +182,14 @@
|
||||
#define RS_APU_ABI_IRQ_FIRST_ERROR 8U
|
||||
#define RS_APU_ABI_IRQ_STREAM_XRUN 9U
|
||||
#define RS_APU_ABI_IRQ_SEQUENCER_TRAP 10U
|
||||
#define RS_APU_ABI_IRQ_COEFFICIENT_LOAD_DONE 11U
|
||||
#define RS_APU_ABI_KWS_COEFF_STATUS_BUSY 0U
|
||||
#define RS_APU_ABI_KWS_COEFF_STATUS_VALID 1U
|
||||
#define RS_APU_ABI_KWS_COEFF_STATUS_LOCKED 2U
|
||||
#define RS_APU_ABI_KWS_COEFF_STATUS_SIZE_ERROR 8U
|
||||
#define RS_APU_ABI_KWS_COEFF_STATUS_HEADER_ERROR 9U
|
||||
#define RS_APU_ABI_KWS_COEFF_STATUS_CRC_ERROR 10U
|
||||
#define RS_APU_ABI_KWS_COEFF_STATUS_PADDING_ERROR 11U
|
||||
#define RS_APU_ABI_ERROR_VALID 0U
|
||||
#define RS_APU_ABI_ERROR_CODE 1U
|
||||
#define RS_APU_ABI_ERROR_STAGE 7U
|
||||
@@ -325,6 +343,14 @@
|
||||
#define RS_APU_ABI_APUM_ABI UINT32_C(0x00010000)
|
||||
#define RS_APU_ABI_APUM_IMAGE_BYTES 32768U
|
||||
#define RS_APU_ABI_APUM_PAYLOAD_CRC UINT32_C(0xB9034B22)
|
||||
#define RS_APU_ABI_APUC_MAGIC UINT32_C(0x43555041)
|
||||
#define RS_APU_ABI_APUC_ABI UINT32_C(0x00010000)
|
||||
#define RS_APU_ABI_APUC_IMAGE_BYTES 61504U
|
||||
#define RS_APU_ABI_APUC_HEADER_BYTES 64U
|
||||
#define RS_APU_ABI_APUC_PAYLOAD_BYTES 61440U
|
||||
#define RS_APU_ABI_APUC_PAYLOAD_CRC UINT32_C(0x25E7C27D)
|
||||
#define RS_APU_ABI_APUC_COEFFICIENT_ID_LO UINT32_C(0x8A0B038D)
|
||||
#define RS_APU_ABI_APUC_COEFFICIENT_ID_HI UINT32_C(0x806C780D)
|
||||
#define RS_APU_ABI_APUMC_HEADER_MAGIC 0U
|
||||
#define RS_APU_ABI_APUMC_HEADER_ABI 1U
|
||||
#define RS_APU_ABI_APUMC_HEADER_TOTAL_BYTES 2U
|
||||
@@ -479,21 +505,22 @@
|
||||
#define RS_APU_ABI_MC_TRAP_EXPLICIT 10U
|
||||
|
||||
#define RS_APU_IP_ID_VALUE UINT32_C(0x41505530)
|
||||
#define RS_APU_IP_VERSION_VALUE UINT32_C(0x00010001)
|
||||
#define RS_APU_IP_VERSION_VALUE UINT32_C(0x00010002)
|
||||
#define RS_APU_IP_VERSION_MAJOR_MASK UINT32_C(0xFFFF0000)
|
||||
#define RS_APU_CAPABILITY0_P7_IMPLEMENTED UINT32_C(0x000001FD)
|
||||
#define RS_APU_CAPABILITY0_P7_IMPLEMENTED UINT32_C(0x000003FD)
|
||||
#define RS_APU_CAPABILITY0_P5_IMPLEMENTED UINT32_C(0x000001BD)
|
||||
#define RS_APU_CAPABILITY1_P7_IMPLEMENTED UINT32_C(0x01827020)
|
||||
#define RS_APU_CAPABILITY1_IMPLEMENTED UINT32_C(0x01827020)
|
||||
#define RS_APU_DIGEST_P7_IMPLEMENTED UINT32_C(0xF5005D7C)
|
||||
#define RS_APU_DIGEST_P9_IMPLEMENTED UINT32_C(0x63E96066)
|
||||
#define RS_APU_DIGEST_P7_IMPLEMENTED RS_APU_DIGEST_P9_IMPLEMENTED
|
||||
#if defined(RS_APU_RELEASE_P7)
|
||||
#define RS_APU_CAPABILITY0_IMPLEMENTED RS_APU_CAPABILITY0_P7_IMPLEMENTED
|
||||
#define RS_APU_DIGEST_IMPLEMENTED RS_APU_DIGEST_P7_IMPLEMENTED
|
||||
#define RS_APU_DIGEST_IMPLEMENTED RS_APU_DIGEST_P9_IMPLEMENTED
|
||||
#else
|
||||
#define RS_APU_CAPABILITY0_IMPLEMENTED RS_APU_CAPABILITY0_P5_IMPLEMENTED
|
||||
#define RS_APU_DIGEST_IMPLEMENTED UINT32_C(0x00000000)
|
||||
#endif
|
||||
#define RS_APU_IRQ_ALL UINT32_C(0x000007FF)
|
||||
#define RS_APU_IRQ_ALL UINT32_C(0x00000FFF)
|
||||
|
||||
#define RS_APU_REG_IP_ID RS_APU_ABI_IP_ID
|
||||
#define RS_APU_REG_IP_VERSION RS_APU_ABI_IP_VERSION
|
||||
|
||||
+95
-3
@@ -202,7 +202,7 @@ rs_status_t rs_apu_probe(rs_apu_info_t *info) {
|
||||
if ((info->ip_id != RS_APU_IP_ID_VALUE) ||
|
||||
((info->version & RS_APU_IP_VERSION_MAJOR_MASK) !=
|
||||
(RS_APU_IP_VERSION_VALUE & RS_APU_IP_VERSION_MAJOR_MASK)) ||
|
||||
((info->version & UINT32_C(0xFFFF)) > 1U)) {
|
||||
((info->version & UINT32_C(0xFFFF)) > 2U)) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
return RS_OK;
|
||||
@@ -620,6 +620,26 @@ static rs_status_t rs_apu_kws_discover(void) {
|
||||
return (rs_apu_bit(info.capability0, RS_APU_ABI_CAPABILITY0_KWS) != 0U) ? RS_OK : RS_ENOTSUP;
|
||||
}
|
||||
|
||||
static rs_status_t rs_apu_kws_coeff_ready_if_required(void) {
|
||||
rs_apu_info_t info;
|
||||
uint32_t status;
|
||||
|
||||
if (rs_apu_probe(&info) != RS_OK) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
if (rs_apu_bit(info.capability0, RS_APU_ABI_CAPABILITY0_COEFFICIENT_LOAD) == 0U) {
|
||||
return RS_OK;
|
||||
}
|
||||
status = RS_APU_REG(RS_APU_ABI_KWS_COEFF_STATUS);
|
||||
if (((status & UINT32_C(6)) != UINT32_C(6)) ||
|
||||
(RS_APU_REG(RS_APU_ABI_KWS_COEFF_ACTUAL_CRC) != RS_APU_ABI_APUC_PAYLOAD_CRC) ||
|
||||
(RS_APU_REG(RS_APU_ABI_KWS_COEFF_ID_LO) != RS_APU_ABI_APUC_COEFFICIENT_ID_LO) ||
|
||||
(RS_APU_REG(RS_APU_ABI_KWS_COEFF_ID_HI) != RS_APU_ABI_APUC_COEFFICIENT_ID_HI)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
static rs_status_t rs_apu_kws_admission(const rs_apu_kws_job_t *job) {
|
||||
uint32_t model_status;
|
||||
|
||||
@@ -629,7 +649,8 @@ static rs_status_t rs_apu_kws_admission(const rs_apu_kws_job_t *job) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
model_status = RS_APU_REG(RS_APU_ABI_KWS_MODEL_STATUS);
|
||||
if ((model_status & UINT32_C(6)) != UINT32_C(6) ||
|
||||
if ((rs_apu_kws_coeff_ready_if_required() != RS_OK) ||
|
||||
(model_status & UINT32_C(6)) != UINT32_C(6) ||
|
||||
(RS_APU_REG(RS_APU_ABI_KWS_CONTROL) != UINT32_C(3)) ||
|
||||
(rs_apu_bit(RS_APU_REG(RS_APU_ABI_KWS_STATUS), RS_APU_ABI_KWS_STATUS_LISTENING) != 0U)) {
|
||||
return RS_EINVAL;
|
||||
@@ -655,6 +676,9 @@ rs_status_t rs_apu_kws_model_load(const rs_apu_image_t *image, rs_timeout_t time
|
||||
if (rs_apu_kws_discover() != RS_OK) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
if (rs_apu_kws_coeff_ready_if_required() != RS_OK) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
if ((rs_apu_lp_quiesced() == 0U) ||
|
||||
(rs_apu_bit(RS_APU_REG(RS_APU_ABI_STATUS), RS_APU_ABI_STATUS_IDLE) == 0U) ||
|
||||
(rs_apu_range_in_acl(image->address, image->bytes, RS_APU_REG(RS_APU_ABI_READ_BASE),
|
||||
@@ -682,6 +706,73 @@ rs_status_t rs_apu_kws_model_load(const rs_apu_image_t *image, rs_timeout_t time
|
||||
return RS_ETIMEOUT;
|
||||
}
|
||||
|
||||
rs_status_t rs_apu_kws_coeff_load(const rs_apu_image_t *image, rs_timeout_t timeout) {
|
||||
rs_apu_info_t info;
|
||||
uint32_t polls;
|
||||
uint32_t status;
|
||||
|
||||
if ((image == NULL) || ((image->address & UINT32_C(63)) != 0U) ||
|
||||
(image->bytes != RS_APU_ABI_APUC_IMAGE_BYTES) ||
|
||||
(image->expected_crc != RS_APU_ABI_APUC_PAYLOAD_CRC) ||
|
||||
(image->address > (UINT32_MAX - (RS_APU_ABI_APUC_IMAGE_BYTES - 1U)))) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
if ((rs_apu_probe(&info) != RS_OK) ||
|
||||
(rs_apu_bit(info.capability0, RS_APU_ABI_CAPABILITY0_COEFFICIENT_LOAD) == 0U)) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
status = RS_APU_REG(RS_APU_ABI_KWS_COEFF_STATUS);
|
||||
if ((rs_apu_lp_quiesced() == 0U) ||
|
||||
(rs_apu_bit(RS_APU_REG(RS_APU_ABI_STATUS), RS_APU_ABI_STATUS_IDLE) == 0U) ||
|
||||
(rs_apu_range_in_acl(image->address, image->bytes, RS_APU_REG(RS_APU_ABI_READ_BASE),
|
||||
RS_APU_REG(RS_APU_ABI_READ_LIMIT)) == 0U) ||
|
||||
((RS_APU_REG(RS_APU_ABI_KWS_CONTROL) & UINT32_C(1)) != 0U) ||
|
||||
((status & UINT32_C(4)) != 0U) ||
|
||||
((RS_APU_REG(RS_APU_ABI_KWS_MODEL_STATUS) & UINT32_C(2)) != 0U)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
RS_APU_REG(RS_APU_ABI_KWS_COEFF_ADDRESS) = image->address;
|
||||
RS_APU_REG(RS_APU_ABI_KWS_COEFF_SIZE) = image->bytes;
|
||||
RS_APU_REG(RS_APU_ABI_KWS_COEFF_EXPECTED_CRC) = image->expected_crc;
|
||||
rs_apu_fence();
|
||||
RS_APU_REG(RS_APU_ABI_KWS_COEFF_COMMAND) = UINT32_C(1);
|
||||
polls = rs_apu_poll_count(timeout);
|
||||
while (polls-- != 0U) {
|
||||
status = RS_APU_REG(RS_APU_ABI_KWS_COEFF_STATUS);
|
||||
if ((status & UINT32_C(1)) == 0U) {
|
||||
if (((status & UINT32_C(6)) == UINT32_C(6)) &&
|
||||
(RS_APU_REG(RS_APU_ABI_KWS_COEFF_ACTUAL_CRC) == image->expected_crc) &&
|
||||
(RS_APU_REG(RS_APU_ABI_KWS_COEFF_ID_LO) == RS_APU_ABI_APUC_COEFFICIENT_ID_LO) &&
|
||||
(RS_APU_REG(RS_APU_ABI_KWS_COEFF_ID_HI) == RS_APU_ABI_APUC_COEFFICIENT_ID_HI)) {
|
||||
return RS_OK;
|
||||
}
|
||||
if ((status & UINT32_C(0x0B00)) != 0U) {
|
||||
return RS_EFORMAT;
|
||||
}
|
||||
return RS_EIO;
|
||||
}
|
||||
}
|
||||
return RS_ETIMEOUT;
|
||||
}
|
||||
|
||||
rs_status_t rs_apu_kws_coeff_status_read(rs_apu_kws_coeff_status_t *status) {
|
||||
rs_apu_info_t info;
|
||||
|
||||
if (status == NULL) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
if ((rs_apu_probe(&info) != RS_OK) ||
|
||||
(rs_apu_bit(info.capability0, RS_APU_ABI_CAPABILITY0_COEFFICIENT_LOAD) == 0U)) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
status->status = RS_APU_REG(RS_APU_ABI_KWS_COEFF_STATUS);
|
||||
status->actual_crc = RS_APU_REG(RS_APU_ABI_KWS_COEFF_ACTUAL_CRC);
|
||||
status->coefficient_id[0] = RS_APU_REG(RS_APU_ABI_KWS_COEFF_ID_LO);
|
||||
status->coefficient_id[1] = RS_APU_REG(RS_APU_ABI_KWS_COEFF_ID_HI);
|
||||
status->capacity_bytes = RS_APU_REG(RS_APU_ABI_KWS_COEFF_CAPACITY);
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
rs_status_t rs_apu_kws_configure(const rs_apu_kws_config_t *config) {
|
||||
uint32_t input_config;
|
||||
|
||||
@@ -716,7 +807,8 @@ rs_status_t rs_apu_kws_enable(uint32_t memory_window) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
model_status = RS_APU_REG(RS_APU_ABI_KWS_MODEL_STATUS);
|
||||
if ((rs_apu_owner_unblocked() == 0U) || ((model_status & UINT32_C(6)) != UINT32_C(6)) ||
|
||||
if ((rs_apu_owner_unblocked() == 0U) || (rs_apu_kws_coeff_ready_if_required() != RS_OK) ||
|
||||
((model_status & UINT32_C(6)) != UINT32_C(6)) ||
|
||||
(RS_APU_REG(RS_APU_ABI_KWS_CONTROL) != 0U)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
|
||||
+20
-11
@@ -564,7 +564,15 @@ Unlisted offsets are reserved and return `PSLVERR`.
|
||||
| `0x234` | `KWS_MODEL_STATUS` | RO | `0` | Busy, valid/lock, header/range/size/CRC/operator errors. |
|
||||
| `0x238` | `KWS_MODEL_ACTUAL_CRC` | RO | `0` | Observed payload CRC32. |
|
||||
| `0x23c` | `KWS_INPUT_CONFIG` | RW disabled/idle | `0x0104bb80` | P7 append: rate `[16:0]`, physical channels `[18:17]`, precision `[25:20]`; see P7 capture rules. |
|
||||
| `0x240..0x260` | `KWS_COEFF_*` | P9 only | see P9 | Additive coefficient DMA/CRC/readback/lock interface; exact fields and resets below. |
|
||||
| `0x240` | `KWS_COEFF_ADDRESS` | RW idle/LP | `0` | P9: 64-byte-aligned APUC DMA source. |
|
||||
| `0x244` | `KWS_COEFF_SIZE` | RW idle/LP | `0` | P9: exact 61504-byte APUC image size. |
|
||||
| `0x248` | `KWS_COEFF_EXPECTED_CRC` | RW idle/LP | `0` | P9: expected APUC payload CRC32. |
|
||||
| `0x24c` | `KWS_COEFF_COMMAND` | WO | `0` | P9: full-word value 1 starts coefficient loading. |
|
||||
| `0x250` | `KWS_COEFF_STATUS` | RO | `0` | P9: busy, valid, lock and terminal validation errors. |
|
||||
| `0x254` | `KWS_COEFF_ACTUAL_CRC` | RO | `0` | P9: finalized SRAM-readback CRC32. |
|
||||
| `0x258` | `KWS_COEFF_ID_LO` | RO | `0` | P9: published coefficient ID bits 31:0. |
|
||||
| `0x25c` | `KWS_COEFF_ID_HI` | RO | `0` | P9: published coefficient ID bits 63:32. |
|
||||
| `0x260` | `KWS_COEFF_CAPACITY` | RO | `0x0000f000` | P9: auxiliary coefficient payload capacity in bytes. |
|
||||
| `0x300` | `PERF_CONTROL` | RW | `0` | Enable 0, clear pulse 1, snapshot pulse 2. |
|
||||
| `0x304` | `PERF_STATUS` | RO | `0` | Snapshot valid and overflow summary. |
|
||||
| `0x308..0x354` | `PERF_*` | RO snapshot | `0` | Ten 64-bit pairs: active cycles, input/output bytes, decoded frames, DMA read/write stalls, stream stalls, sequencer instructions, KWS cycles, and faults. |
|
||||
@@ -2815,16 +2823,17 @@ offsets, descriptor, APUM/APUMC versions, MP3 stub and system IRQ/resource/DMA/
|
||||
clock/reset allocations are unchanged. Earlier profiles reject these new
|
||||
offsets and bit11 IRQ writes. P9-only software tests discovery before access.
|
||||
|
||||
| Offset | Register | Access / reset | Contract |
|
||||
| ---: | --- | --- | --- |
|
||||
| `0x240` | KWS_COEFF_ADDRESS | RW idle/LP / 0 | 64-byte-aligned DMA source. |
|
||||
| `0x244` | KWS_COEFF_SIZE | RW idle/LP / 0 | Exact 61504 bytes. |
|
||||
| `0x248` | KWS_COEFF_EXPECTED_CRC | RW idle/LP / 0 | Expected APUC payload CRC. |
|
||||
| `0x24c` | KWS_COEFF_COMMAND | WO / 0 | Full-word value 1 starts load; all other values reject. |
|
||||
| `0x250` | KWS_COEFF_STATUS | RO / 0 | Busy0, valid1, locked2; header-declared size8, other header9, CRC10, padding11 error flags. |
|
||||
| `0x254` | KWS_COEFF_ACTUAL_CRC | RO / 0 | Finalized SRAM-readback CRC, zero until a complete readback. |
|
||||
| `0x258`, `0x25c` | KWS_COEFF_ID_LO/HI | RO / 0 | Published coefficient ID; zero until success. |
|
||||
| `0x260` | KWS_COEFF_CAPACITY | RO / `0x0000f000` | Auxiliary payload bytes, not APUM scratch bytes. |
|
||||
| Offset | Register | Access | Reset | Contract |
|
||||
| ---: | --- | --- | --- | --- |
|
||||
| `0x240` | KWS_COEFF_ADDRESS | RW idle/LP | `0` | 64-byte-aligned DMA source. |
|
||||
| `0x244` | KWS_COEFF_SIZE | RW idle/LP | `0` | Exact 61504 bytes. |
|
||||
| `0x248` | KWS_COEFF_EXPECTED_CRC | RW idle/LP | `0` | Expected APUC payload CRC. |
|
||||
| `0x24c` | KWS_COEFF_COMMAND | WO | `0` | Full-word value 1 starts load; all other values reject. |
|
||||
| `0x250` | KWS_COEFF_STATUS | RO | `0` | Busy0, valid1, locked2; header-declared size8, other header9, CRC10, padding11 error flags. |
|
||||
| `0x254` | KWS_COEFF_ACTUAL_CRC | RO | `0` | Finalized SRAM-readback CRC, zero until a complete readback. |
|
||||
| `0x258` | KWS_COEFF_ID_LO | RO | `0` | Published coefficient ID bits 31:0; zero until success. |
|
||||
| `0x25c` | KWS_COEFF_ID_HI | RO | `0` | Published coefficient ID bits 63:32; zero until success. |
|
||||
| `0x260` | KWS_COEFF_CAPACITY | RO | `0x0000f000` | Auxiliary payload bytes, not APUM scratch bytes. |
|
||||
|
||||
RW/WO writes require full-word strobes; reserved status bits read zero and
|
||||
RO writes reject. Load admission requires LP owner, globally idle/quiesced,
|
||||
|
||||
@@ -33,15 +33,18 @@ SRAM_WRAPPER_PREFIX = "tc_sram"
|
||||
CLOCK_GATE_RE = re.compile(r"^sg13g2_(s)?lgcp_\d+$")
|
||||
|
||||
MACRO_CLASS_BY_ANCESTOR = {
|
||||
"u_proof_memo": "verifier_memo",
|
||||
"u_kws_coeff_store": "coefficient_store",
|
||||
"u_kws_sram_client": "kws_model_scratch",
|
||||
"u_control_store": "control_store",
|
||||
"u_local_sram": "local_data",
|
||||
"u_local_sram": "codec_common_data",
|
||||
"u_microcode_loader": "loader_path_stack",
|
||||
}
|
||||
|
||||
LOCAL_SRAM_RTL = REPO_ROOT / "rtl/ip/multimedia/apu_local_sram.sv"
|
||||
APU_TOP_RTL = REPO_ROOT / "rtl/ip/multimedia/apb4_apu.sv"
|
||||
APU_DEFINE_RTL = REPO_ROOT / "rtl/ip/multimedia/apu_define.svh"
|
||||
DOC_REFERENCE = "docs/ip/apu.md:3536-3541"
|
||||
DOC_REFERENCE = "docs/ip/apu.md:2698-2714"
|
||||
|
||||
MODULE_DEF_RE = re.compile(r"^module\s+\\?([^\s(]+)", re.MULTILINE)
|
||||
WORST_BLOCK_RE = re.compile(r"(?=^Startpoint: )", re.MULTILINE)
|
||||
@@ -502,6 +505,15 @@ def storage_accounting(synths: dict[str, dict[str, Any]]) -> dict[str, Any]:
|
||||
"wrappers": control_kib // wrapper_kib + constants["local_data_logical_banks"],
|
||||
"kib": control_kib + data_kib,
|
||||
},
|
||||
"p9_physical": {
|
||||
"control_store": {"wrappers": 8, "kib": 32},
|
||||
"codec_common_data": {"wrappers": 12, "kib": 48},
|
||||
"kws_model_scratch": {"wrappers": 16, "kib": 64},
|
||||
"loader_path_stack": {"wrappers": 8, "kib": 32},
|
||||
"verifier_memo": {"wrappers": 17, "kib": 68},
|
||||
"coefficient_store": {"wrappers": 15, "kib": 60},
|
||||
"total": {"wrappers": 76, "kib": 304},
|
||||
},
|
||||
"rtl_artifacts": [str(LOCAL_SRAM_RTL), str(APU_TOP_RTL), str(APU_DEFINE_RTL)],
|
||||
"per_configuration": {},
|
||||
}
|
||||
@@ -513,6 +525,22 @@ def storage_accounting(synths: dict[str, dict[str, Any]]) -> dict[str, Any]:
|
||||
"by_type": macros["by_type"],
|
||||
"unclassified": macros["by_class"].get("unclassified", 0),
|
||||
}
|
||||
expected_p9 = {
|
||||
"control_store": 8,
|
||||
"codec_common_data": 12,
|
||||
"kws_model_scratch": 16,
|
||||
"loader_path_stack": 8,
|
||||
"verifier_memo": 17,
|
||||
"coefficient_store": 15,
|
||||
}
|
||||
expanded = result["per_configuration"]["expanded"]
|
||||
observed = {name: expanded["by_class"].get(name, 0) for name in expected_p9}
|
||||
if observed != expected_p9 or expanded["physical_macro_instances"] != 76:
|
||||
raise SystemExit(
|
||||
"P9 expanded macro inventory mismatch: "
|
||||
f"observed {observed}, total {expanded['physical_macro_instances']}; "
|
||||
f"expected {expected_p9}, total 76"
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@@ -597,31 +625,31 @@ def render_markdown(report: dict[str, Any]) -> str:
|
||||
f"{storage['control_store']['wrappers']} wrappers, {storage['control_store']['kib']} KiB",
|
||||
f"- local data: {storage['local_data']['logical_banks']} logical banks -> "
|
||||
f"{storage['local_data']['kib']} KiB "
|
||||
f"(macro-backed {storage['local_data']['macro_backed_kib']} KiB + "
|
||||
f"KWS register-file window {storage['local_data']['kws_window_kib']} KiB)",
|
||||
f"(codec/common {storage['local_data']['macro_backed_kib']} KiB + "
|
||||
f"KWS model/scratch {storage['local_data']['kws_window_kib']} KiB)",
|
||||
f"- combined logical: {storage['combined_logical']['wrappers']} wrappers, "
|
||||
f"{storage['combined_logical']['kib']} KiB",
|
||||
"",
|
||||
"| configuration | control-store macros | local-data macros | loader path-stack macros | total physical macros |",
|
||||
"| --- | --- | --- | --- | --- |",
|
||||
"| configuration | control | codec/common | KWS | path stack | memo/bitmap | coefficients | total physical |",
|
||||
"| --- | --- | --- | --- | --- | --- | --- | --- |",
|
||||
]
|
||||
for name in ("baseline", "expanded"):
|
||||
per = storage["per_configuration"][name]
|
||||
lines.append(
|
||||
f"| {name} | {per['by_class'].get('control_store', 0)} | "
|
||||
f"{per['by_class'].get('local_data', 0)} | "
|
||||
f"{per['by_class'].get('codec_common_data', 0)} | "
|
||||
f"{per['by_class'].get('kws_model_scratch', 0)} | "
|
||||
f"{per['by_class'].get('loader_path_stack', 0)} | "
|
||||
f"{per['by_class'].get('verifier_memo', 0)} | "
|
||||
f"{per['by_class'].get('coefficient_store', 0)} | "
|
||||
f"{per['physical_macro_instances']} |"
|
||||
)
|
||||
lines += [
|
||||
"",
|
||||
f"Capacity reference ({DOC_REFERENCE}): 8 control-store + 28 local-data = 36 logical",
|
||||
"wrappers (144 KiB combined) vs the pre-expansion P5 baseline of 4 + 28 = 32",
|
||||
"(128 KiB). The loader path-stack macros above are the verifier workspace and",
|
||||
"are reported separately as required. Note: on the current RTL the control-store",
|
||||
"Depth is fixed at 4096 for both EnableP7 values, so both configurations measure",
|
||||
"36 logical wrappers; the 32-wrapper P5 baseline is the pre-expansion RTL, not",
|
||||
"EnableP7=0 of the current RTL.",
|
||||
f"P9 physical reference ({DOC_REFERENCE}): 76 wrappers / 304 KiB. The software",
|
||||
"capacity remains 8 control-store + 28 local-data wrappers / 144 KiB; path stack,",
|
||||
"memo/bitmap and coefficient banks are auxiliary physical storage. The inspected",
|
||||
"pre-P9 source inventory was 44 wrappers, so P9 adds exactly 32 wrappers.",
|
||||
"",
|
||||
"## Per-block cell/macro/area accounting (hierarchy subtree totals)",
|
||||
"",
|
||||
@@ -641,8 +669,8 @@ def render_markdown(report: dict[str, Any]) -> str:
|
||||
"",
|
||||
"Cells are leaf standard cells plus macro cells inside the kept hierarchy;",
|
||||
"areas are the sums of the Liberty cell areas inside each subtree. The",
|
||||
"KWS register-file window lives in `u_kws_sram_client` flops, not in",
|
||||
"SRAM macros, so it appears as standard-cell area above.",
|
||||
"All six P9 storage classes above must resolve to SRAM macros in the IHP130 flow;",
|
||||
"a register-file or giant mux implementation is not accepted as physical storage.",
|
||||
"",
|
||||
"## Worst max-delay paths (top 5 per configuration)",
|
||||
"",
|
||||
@@ -727,10 +755,12 @@ def main() -> int:
|
||||
"storage_accounting": storage_accounting(synths),
|
||||
"documentation_reference": {
|
||||
"source": DOC_REFERENCE,
|
||||
"expanded_logical_wrappers": 36,
|
||||
"baseline_logical_wrappers": 32,
|
||||
"expanded_combined_kib": 144,
|
||||
"baseline_combined_kib": 128,
|
||||
"p9_physical_wrappers": 76,
|
||||
"p9_physical_kib": 304,
|
||||
"pre_p9_source_wrappers": 44,
|
||||
"added_wrappers": 32,
|
||||
"advertised_capacity_wrappers": 36,
|
||||
"advertised_capacity_kib": 144,
|
||||
},
|
||||
}
|
||||
report["blocks"] = block_table(synths)
|
||||
|
||||
@@ -45,10 +45,11 @@ measurement evidence. APU's checked-in WAV/FLAC image is statically assembled by
|
||||
The [109-entry chapter review](datasheets/chapter-review.md) records final page
|
||||
numbers, implementation status, document completeness, sources and recommendations
|
||||
for the delivered PDF snapshot.
|
||||
MP3 remains a reserved trap entry. Default PRODUCT keeps KWS gated off; the P7
|
||||
acceptance profile advertises capability `0x1FD` and digest `0xF5005D7C`.
|
||||
APU KWS storage is described from the inferred implementation, not an unimplemented
|
||||
macro claim. NPU has eight executable operator classes, 64 KiB private SRAM and
|
||||
MP3 remains a reserved trap entry. Default PRODUCT keeps KWS gated off; the P9
|
||||
acceptance profile advertises capability `0x3FD` and digest `0x63E96066`.
|
||||
APU KWS storage includes 15 coefficient and 17 proof-memo macro wrappers in the
|
||||
qualified configuration; physical replacement remains evidence-bound. NPU has
|
||||
eight executable operator classes, 64 KiB private SRAM and
|
||||
an integrated eight-beat AXI64 DMA limit; its reusable DMA default is not the
|
||||
deployed limit. Native Linux NPU/ASoC support is not inferred from HP smoke code.
|
||||
CI workflow outcomes are a separately scoped snapshot, including failures and
|
||||
|
||||
@@ -66,7 +66,7 @@ See [content review](content-review.md) for the bounded commercial comparison, [
|
||||
| 51 | Digital Video Port (DVP) / 469-479 | Covered and retained. Integrated: 8-bit parallel RGB565/YUV422 capture. Tests available; 0 attached per-IP report records. Workflow outcomes do not replace scoped reports. | Selected feature, programming and restriction phrases emphasized; shared register/ABI descriptions retained. | [dvp.h](../../crt/include/retrosoc/hal/dvp.h), [dvp.md](../../docs/ip/dvp.md), [dvp.c](../../publications/examples/dvp.c). Retain the documented subset and prerequisites. Parallel camera, sensor configuration and pixel clock. Attach a matching scoped report before qualification. |
|
||||
| 52 | JPEG Encoder / Decoder / 480-503 | Covered and retained. Partial: 8-bit Baseline Sequential JPEG encode or decode. Tests available; 0 attached per-IP report records. Workflow outcomes do not replace scoped reports. | Selected feature, programming and restriction phrases emphasized; shared register/ABI descriptions retained. | [jpeg.h](../../crt/include/retrosoc/hal/jpeg.h), [jpeg.md](../../docs/ip/jpeg.md), [jpeg.c](../../publications/examples/jpeg.c). Retain the documented subset and prerequisites. Native slot-6 payload admission has one normal read and one normal write credit at class 8; standalone codec behavior and end-to-end system qualification remain separate scopes. Attach a matching scoped report before qualification. |
|
||||
| 53 | 2D Graphics Accelerator (GA2D) / 504-523 | Covered and retained. Integrated: P5 direct single-job FILL, COPY, CONVERT, opaque alpha BLEND, A8 masks and exact background/destination in-place composition. Tests available; 0 attached per-IP report records. Workflow outcomes do not replace scoped reports. | Selected feature, programming and restriction phrases emphasized; shared register/ABI descriptions retained. | [ga2d.md](../../docs/ip/ga2d.md), [apb4_ga2d.sv](../../rtl/ip/multimedia/apb4_ga2d.sv), [ga2d_core.sv](../../rtl/ip/multimedia/ga2d_core.sv). P6 test sources and dated records already exist. Attach current-commit full-product contention, workload throughput and physical closure reports; preserve the documented subset. |
|
||||
| 54 | Audio Processing Unit (APU, partial) / 524-569 | Covered and retained. Partial: WAV/FLAC image and job/stream execution; fixed KWS is enabled only in the P7 acceptance configuration. MP3 remains unsupported. Tests available; 0 attached per-IP report records. Workflow outcomes do not replace scoped reports. | Selected feature, programming and restriction phrases emphasized; shared register/ABI descriptions retained. | [Makefile](../../Makefile), [main.c](../../app/apps/apu_release/main.c), [main.c](../../app/ports/hp-apu/main.c). Keep default/P7 capability and digest distinct, MP3 unsupported and KWS configuration-dependent. A shipped WAV/FLAC image or smoke wrapper is not full corpus or sustained-audio qualification. Keep inferred KWS storage distinct from the specified technology-macro target. |
|
||||
| 54 | Audio Processing Unit (APU, partial) / 524-569 | Covered and retained. Partial: WAV/FLAC image and job/stream execution; APUC-backed KWS is enabled only in the P9 acceptance configuration. MP3 remains unsupported. Tests available; 0 attached per-IP report records. Workflow outcomes do not replace scoped reports. | Selected feature, programming and restriction phrases emphasized; shared register/ABI descriptions retained. | [Makefile](../../Makefile), [main.c](../../app/apps/apu_release/main.c), [main.c](../../app/ports/hp-apu/main.c). Keep default/P9 capability and digest distinct, MP3 unsupported and KWS configuration-dependent. A shipped image or smoke wrapper is not full corpus or sustained-audio qualification. Keep wrapper inventory distinct from revision-qualified physical replacement evidence. |
|
||||
| 55 | Neural Processing Unit (NPU) / 570-594 | Covered and retained. Integrated: Eight INT8 operators with native HP descriptor/DMA execution and private local SRAM. Tests available; 0 attached per-IP report records. Workflow outcomes do not replace scoped reports. | Selected feature, programming and restriction phrases emphasized; shared register/ABI descriptions retained. | [npu.h](../../crt/include/retrosoc/hal/npu.h), [npu_regs.h](../../crt/include/retrosoc/hal/npu_regs.h), [npu.c](../../crt/src/hal/npu.c). Attach matching KWS/VWW raw reports including CPU preprocessing/Softmax and cache costs. Keep unknown-operator rejection, INT32 limits, abort/drain and private SRAM boundaries explicit; no arbitrary TFLite or certification claim. |
|
||||
| 56 | Crypto Controller / 595-616 | Covered and retained. Integrated: AES-128/192/256, SHA-224/256 and raw RSA-2048. Tests available; 0 attached per-IP report records. Workflow outcomes do not replace scoped reports. | Selected feature, programming and restriction phrases emphasized; shared register/ABI descriptions retained. | [crypto.h](../../crt/include/retrosoc/hal/crypto.h), [crypto.md](../../docs/ip/crypto.md), [crypto.c](../../publications/examples/crypto.c). Retain the documented subset and prerequisites. LP-controlled buffers and key lifecycle; no secure key-store claim. Attach a matching scoped report before qualification. |
|
||||
| 57 | Cyclic Redundancy Check (CRC) / 617-623 | Covered and retained. Integrated: Programmable 7-, 8-, 16- or 32-bit CRC. Source reviewed; 0 attached per-IP report records. Workflow outcomes do not replace scoped reports. | Selected feature, programming and restriction phrases emphasized; shared register/ABI descriptions retained. | [crc.h](../../crt/include/retrosoc/hal/crc.h), [crc.c](../../publications/examples/crc.c), [datasheet.md](../../rtl/managed/clusterip/crc/doc/datasheet.md). Retain the documented subset and prerequisites. No additional off-chip component implied; use the selected clock/reset and access policy. Attach a matching scoped report before qualification. |
|
||||
|
||||
@@ -33,7 +33,7 @@ remain. Strict source checks remain enabled.
|
||||
|
||||
The preceding technical refresh retained **v0.5 DRAFT / 2026-09-22** and reviewed commit
|
||||
`b209ccedfe6f9c33078ef082a4d31580d4d1a845`. The main reference remains
|
||||
IHP130 PRODUCT / 32 KiB SRAM; the APU P7 acceptance profile is separate.
|
||||
IHP130 PRODUCT / 32 KiB SRAM; the APU P9 acceptance profile is separate.
|
||||
This is a publication refresh, not a hardware change or a new qualification campaign.
|
||||
|
||||
That technical refresh compared against the 777-page baseline bound to
|
||||
@@ -62,7 +62,7 @@ release-status policies are not retroSoC specifications.
|
||||
| --- | --- | --- | --- |
|
||||
| NPU | The existing chapter adequately covers the software-launchable eight-operator engine, private DMA/SRAM, compiler/HAL, descriptor/parameter layouts and normal/abort behavior. | Already covered; retain the chapter, geometry, static graph subset and numeric profile. No new NPU capability is introduced by this refresh. | Attach matching full-corpus and physical reports before claiming speedup, accuracy or timing closure. Distinguish the deployed burst limit from the reusable DMA default. |
|
||||
| APU formats and base programming | APUMC V1/V2, 4096-word current store, seven classes/62 opcodes, APUM model and 128-byte job format are already covered. | Already covered; retain the formats, register/HAL reference and default/P7 identity distinction. | Preserve format compatibility and separate the fixed APUM engine from NPU deployment. MP3 remains unsupported. |
|
||||
| APU summary, configuration and acceptance | Default KWS is off; P7 enables it with digest `0xF5005D7C`. The checked-in WAV/FLAC image is distinct from full production-job qualification. | Correct summary/body/table consistency while retaining the detailed formats and bare-metal acceptance reference. No native Linux ASoC support is claimed. | A smoke wrapper or missing-data early return is not the full accuracy or 60-second-per-scenario campaign. Keep actual workload, clock, source and result artifacts with any pass. |
|
||||
| APU summary, configuration and acceptance | Default KWS is off; the P9 acceptance profile enables KWS and APUC loading with digest `0x63E96066`. The checked-in WAV/FLAC image is distinct from full production-job qualification. | Correct summary/body/table consistency while retaining the detailed formats and bare-metal acceptance reference. No native Linux ASoC support is claimed. | A smoke wrapper or missing-data early return is not the full accuracy or 60-second-per-scenario campaign. Keep actual workload, clock, source and result artifacts with any pass. |
|
||||
| GA2D | Existing FILL/COPY/CONVERT/opaque BLEND/A8 programming coverage and P6 workload/evidence boundaries are adequate. | Already covered; retain functional ABI and schema-3 performance-method descriptions. | The dated composition target and 48 MHz slow-corner timing were not met. Full-product closure and current-source measurements still require matching artifacts. |
|
||||
| Fabric, memory conversion and resources | Ten masters/resources, NPU master/resource 9, Resource ABI 1.2 and fragmented 64-to-32 long-burst conversion are already described. | Already covered; retain source-bound topology, matrices, circuits, credits and lifecycle references. | Preserve permission, source-ID and boundary rules. Burst admission is not a throughput guarantee. |
|
||||
| Address/register/IRQ/pad inventories | Existing generation and field-reference mechanisms cover their declared scope, including the independent NPU chapter. | Already covered; retain inventories, instance distinctions and stable anchors. Correct mailbox-purpose summaries from the actual register offsets and HAL/RTL producer paths. | Keep chapter, feature, register, diagram and API coverage synchronized. NPU has no implied new external pins. |
|
||||
@@ -76,11 +76,10 @@ release-status policies are not retroSoC specifications.
|
||||
|
||||
## Source/specification discrepancies and follow-up
|
||||
|
||||
- [APU specification](../../docs/ip/apu.md) describes sixteen KWS technology-macro
|
||||
banks, while [the SRAM client](../../rtl/ip/multimedia/apu_kws_sram_client.sv)
|
||||
uses inferred storage with combinational reads. Preserve the logical/physical
|
||||
distinction in the datasheet. Reconcile the engineering contract or implement
|
||||
and verify the macro change as a separate hardware task.
|
||||
- [APU specification](../../docs/ip/apu.md) defines synchronous macro-backed KWS,
|
||||
coefficient and proof-memo storage. The RTL exposes the frozen wrapper inventory,
|
||||
but physical replacement, synthesis-time and peak-RSS claims remain conditional on
|
||||
the revision-qualified P9 A/B evidence rather than on source structure alone.
|
||||
- Older NPU phase comments and some descriptions predate the enabled scheduler
|
||||
and capability returns. Keep publication claims tied to instantiated parameters,
|
||||
register logic and the compute/DMA path. Clean up stale engineering comments
|
||||
|
||||
@@ -633,7 +633,7 @@
|
||||
"rtl/ip/multimedia/apu_local_sram.sv"
|
||||
],
|
||||
"bindings": [],
|
||||
"note": "Offsets address the 112 KiB logical local-data space, distinct from the 32 KiB control store and SoC SRAM. The current codec/internal macro branch uses 12 four-KiB banks; the separate 64 KiB KWS client uses inferred storage and is gated off by default. Logical capacity does not establish a 28-macro physical implementation or completed PPA qualification.",
|
||||
"note": "Offsets address the 112 KiB logical local-data space, distinct from the 32 KiB control store and SoC SRAM. The codec/internal branch uses 12 four-KiB banks and the separate 64 KiB KWS client uses 16 banks. P9 adds 15 coefficient and 17 proof-memo wrappers outside that logical data window. Wrapper inventory does not establish physical replacement or completed PPA qualification.",
|
||||
"derived": "apu-local"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
"apu": [
|
||||
"**Coreless microcode sequencer** with private DMA and local storage.",
|
||||
"WAV/FLAC jobs and streams are enabled in the deployed P5 path; their format, image and buffer requirements still apply.",
|
||||
"Fixed KWS is disabled in default PRODUCT and enabled by the P7 acceptance profile with its own ABI digest.",
|
||||
"Fixed KWS is disabled in default PRODUCT and enabled with APUC loading by the P9 acceptance profile with its own ABI digest.",
|
||||
"MP3 is unsupported. Advertised capabilities do not establish full-corpus, sustained-audio or production qualification."
|
||||
],
|
||||
"npu": [
|
||||
|
||||
@@ -1713,9 +1713,9 @@
|
||||
"function_sections": [],
|
||||
"software_sections": [],
|
||||
"notes": [
|
||||
"The deployed P5 register, loader and sequencer paths enable WAV/FLAC jobs. Default PRODUCT advertises 0x000001BD with KWS disabled; the dedicated P7 acceptance profile advertises 0x000001FD with fixed-model KWS and ABI digest 0xF5005D7C. MP3 remains unsupported in both configurations.",
|
||||
"Capability and ABI identities describe the selected implementation. Full-corpus codec/KWS accuracy, sustained-audio acceptance and production qualification require their own matching reports; none is implied by enabling the P7 configuration.",
|
||||
"LP loads a bounded APUMC image before submitting work. Expanded P5 RTL has 4096 64-bit control-store words (32 KiB), CAPABILITY1=0x01827020 and APB version 1.1 while retaining APUMC V1 image compatibility.",
|
||||
"The deployed P5 register, loader and sequencer paths enable WAV/FLAC jobs. Default PRODUCT advertises 0x000001BD with KWS disabled; the dedicated P9 acceptance profile advertises 0x000003FD with APUC-backed KWS and ABI digest 0x63E96066. MP3 remains unsupported in both configurations.",
|
||||
"Capability and ABI identities describe the selected implementation. Full-corpus codec/KWS accuracy, sustained-audio acceptance and production qualification require their own matching reports; none is implied by enabling the P9 configuration.",
|
||||
"LP loads bounded APUMC, APUC and APUM images before submitting KWS work. Expanded P5 RTL retains 4096 64-bit control-store words (32 KiB), CAPABILITY1=0x01827020 and APUMC V1 compatibility; P9 reports APB version 1.2.",
|
||||
"The loader checks header, ABI, bounds, entry descriptors and CRC before publishing valid/lock state. Rejected or aborted loads must leave stale instructions unexecutable.",
|
||||
"Direct jobs use JOB_* staging registers; ring jobs use descriptor ownership and producer/consumer rules. Buffer limits, codec entry bounds, ownership, quiesce and transport completion participate in admission and termination.",
|
||||
"The codec controller coordinates parser microcode, transport and processing primitives. Private DMA moves input/output while the stream router connects permitted audio paths. Capabilities do not waive rate, format, microcode or lifecycle checks.",
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
#implementation.kws_tensors tensor records and #implementation.kws_parameter_bytes parameter bytes.
|
||||
It is not a general TFLite interpreter or a qualified MLPerf result.
|
||||
The 112 KiB logical data map includes a 64 KiB KWS region; the codec/internal macro branch
|
||||
uses 12 four-KiB banks, while the separate KWS client has inferred storage. Do not infer a
|
||||
completed 28-macro physical implementation from the logical map.
|
||||
uses 12 four-KiB banks. The P9 KWS implementation adds 15 coefficient and 17 proof-memo
|
||||
macro wrappers; physical replacement remains subject to the exact synthesis evidence gate.
|
||||
#source-note("rtl/ip/multimedia/apb4_apu.sv",title:"Default P7 gate and implemented internal connections")
|
||||
#source-note("rtl/ip/multimedia/apu_kws_sram_client.sv",title:"Current KWS storage implementation")
|
||||
#source-note("scripts/apu_kws.py",title:"Fixed APUM model-container definitions")
|
||||
@@ -58,7 +58,7 @@
|
||||
]
|
||||
#apu-family-figures()
|
||||
],legacy:[
|
||||
Default PRODUCT advertises capability word `0x000001BD`, APB version `0x00010001`, and a 32 KiB control store through `CAPABILITY1=0x01827020`. The P7 acceptance configuration instead advertises `0x000001FD` and ABI digest `0xF5005D7C`. WAV/FLAC transport, job submission and stream paths are integrated; APUMC V1 images remain loadable, while new P5 release images use APUMC V2. MP3 remains unsupported; KWS is configuration-dependent. The checked-in P5 image can be assembled and fits the current control store. Complete corpus,
|
||||
Default PRODUCT advertises capability word `0x000001BD`, while the P9 acceptance configuration advertises APB version `0x00010002`, capability `0x000003FD` and ABI digest `0x63E96066`. WAV/FLAC transport, job submission and stream paths are integrated; APUMC V1 images remain loadable, while new P5 release images use APUMC V2. MP3 remains unsupported; KWS and APUC loading are configuration-dependent. The checked-in P5 image can be assembled and fits the current control store. Complete corpus,
|
||||
sustained real-time and physical qualification require their own reports; they are not inferred from
|
||||
image availability. KWS implementation exists but EnableP7 defaults to false in PRODUCT.
|
||||
|
||||
|
||||
@@ -1828,6 +1828,14 @@
|
||||
},
|
||||
{
|
||||
"lsb": 11,
|
||||
"msb": 11,
|
||||
"name": "COEFFICIENT_LOAD_DONE",
|
||||
"description": "Coefficient load reached a terminal success or error state.",
|
||||
"reset": "Register reset applies",
|
||||
"expression": "`APB4_APU__IRQ_COEFFICIENT_LOAD_DONE"
|
||||
},
|
||||
{
|
||||
"lsb": 12,
|
||||
"msb": 31,
|
||||
"name": "Reserved",
|
||||
"description": "Reserved; write zero.",
|
||||
@@ -1838,7 +1846,7 @@
|
||||
"reset": "Not retained (command/data port)"
|
||||
},
|
||||
"main.IP_VERSION": {
|
||||
"description": "Expanded P5 uses APB interface V1.1 (0x00010001) and retains the frozen V1.0 register offsets and accepted fields."
|
||||
"description": "P9 uses additive APB interface V1.2 (0x00010002), preserving the frozen V1.0/V1.1 offsets and accepted fields while adding coefficient discovery and loading."
|
||||
},
|
||||
"main.CAPABILITY1": {
|
||||
"description": "Expanded P5 geometry is 0x01827020: 32 KiB control store, 112 KiB local data, two channels, and 96 kHz maximum source-rate encoding."
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
#let apu-release-reference() = [
|
||||
#change-start("v05-refresh-apu","Source-bound APU availability and acceptance order")
|
||||
#minor-title("Default and P7 acceptance configurations")
|
||||
#minor-title("Default and P9 acceptance configurations")
|
||||
#ds-table("apu-profile-capabilities",[APU capability and digest by committed configuration],
|
||||
([Configuration],[CAPABILITY0],[ABI_DIGEST],[Software boundary]),
|
||||
data.system_reference.apu_implementation.profiles.map(r=>([#r.name \ #code(r.profile)],
|
||||
code("0x"+upper(str(r.capability,base:16))),code("0x"+upper(str(r.digest,base:16))),
|
||||
if r.enabled {[Fixed APUM KWS is enabled; APP=apu_release and CSR-enabled IRQ acceptance.]} else {[WAV/FLAC enabled; KWS discovery returns RS_ENOTSUP.]})),
|
||||
widths:(1.45fr,0.65fr,0.65fr,1.25fr))
|
||||
The P7 digest is an *ABI identity derived from the frozen register, model and microcode*
|
||||
The P9 digest is an *ABI identity derived from the frozen register, coefficient, model and microcode*
|
||||
interfaces. It is not a runtime accuracy result, firmware authentication or a physical
|
||||
qualification certificate. *MP3 capability remains clear* in both configurations.
|
||||
#source-note("rtl/ip/multimedia/apu_reg.sv",title:"Actual configured capability and digest values")
|
||||
|
||||
@@ -15,7 +15,7 @@ board wiring, a technology macro or additional software. A reserved address rang
|
||||
([Role],[Committed profile],[Boundary]),
|
||||
(([PRODUCT reference],code(doc.profile),[Fixed LP/HP topology; reference for the main register and pad inventories.]),
|
||||
([HP boot acceptance],code(doc.hp_profile),[Same PRODUCT organization; SRAM-resident LP loader and an external HP bundle.]),
|
||||
([APU P7 acceptance],code("configs/ci/ihp130-apu.mk"),[32 KiB SRAM, CSR-enabled LP orchestration and a bare-metal HP audio/KWS payload.]),
|
||||
([APU P9 acceptance],code("configs/ci/ihp130-apu.mk"),[32 KiB SRAM, CSR-enabled LP orchestration, APUC loading and a bare-metal HP audio/KWS payload.]),
|
||||
([MPW compatibility],code(doc.mpw_profile),[Selectable legacy user core/IP; use only the separate MPW appendix.])),
|
||||
widths:(0.75fr,1.35fr,1.6fr))
|
||||
The source revision printed in Document Control identifies the hardware/software contract.
|
||||
|
||||
@@ -11,7 +11,7 @@ The management hart starts from the reset flash alias. Normal firmware compositi
|
||||
placement depend on the committed application and linker profile. The HP acceptance example
|
||||
instead uses the *SRAM-resident `hp_boot` image* and validates an external boot bundle before
|
||||
releasing HP. It must not be assumed that every bringup image automatically boots Linux.
|
||||
The separate APU P7 acceptance profile performs LP image/model loading and bare-metal HP
|
||||
The separate APU P9 acceptance profile performs LP APUMC/APUC/APUM loading and bare-metal HP
|
||||
audio/KWS work; it is not the Linux boot profile. NPU compiler output and HP smoke payloads
|
||||
likewise do not supply a native Linux driver. See @apu and @npu for their software boundaries.
|
||||
|
||||
|
||||
@@ -774,8 +774,8 @@
|
||||
"id": "apu",
|
||||
"title": "Audio Processing Unit",
|
||||
"implementation": "Partial",
|
||||
"scope": "WAV/FLAC image and job/stream execution; fixed KWS is enabled only in the P7 acceptance configuration. MP3 remains unsupported.",
|
||||
"dependencies": "Load the compatible checked-in P5 APUMC V2 image. MP3 is unsupported and KWS is gated off by default; corpus, real-time and physical qualification remain separate.",
|
||||
"scope": "WAV/FLAC image and job/stream execution; APUC-backed KWS is enabled only in the P9 acceptance configuration. MP3 remains unsupported.",
|
||||
"dependencies": "Load compatible APUMC V2, APUC and APUM images in order. MP3 is unsupported and KWS is gated off by default; corpus, real-time and physical qualification remain separate.",
|
||||
"hal": "Published API and syntax example; see the IP chapter.",
|
||||
"linux": "No delivered Linux ASoC integration. The LP/HP APU release image is bare-metal acceptance firmware, not a kernel driver.",
|
||||
"verification": "Tests available",
|
||||
@@ -791,13 +791,17 @@
|
||||
"publications/examples/apu.c",
|
||||
"rtl/ip/multimedia/apb4_apu.sv",
|
||||
"rtl/ip/multimedia/apu_define.svh",
|
||||
"rtl/ip/multimedia/apu_kws_coeff_loader.sv",
|
||||
"rtl/ip/multimedia/apu_kws_coeff_store.sv",
|
||||
"rtl/ip/multimedia/apu_kws_sram_client.sv",
|
||||
"rtl/ip/multimedia/apu_local_sram.sv",
|
||||
"rtl/ip/multimedia/apu_p5_codecs.apus",
|
||||
"rtl/ip/multimedia/apu_reg.sv",
|
||||
"rtl/ip/multimedia/apu_proof_memo.sv",
|
||||
"scripts/apu_abi_digest.py",
|
||||
"scripts/apu_isa.py",
|
||||
"scripts/apu_kws.py",
|
||||
"scripts/apu_kws_coeff.py",
|
||||
"scripts/apu_mcasm.py",
|
||||
"scripts/apu_p5_coefficients.py",
|
||||
"scripts/build_apu_p5_bundle.py"
|
||||
|
||||
@@ -21,10 +21,11 @@ APU_CONFIGURATION_SOURCES = (
|
||||
|
||||
APU_SOURCES = (
|
||||
"scripts/apu_mcasm.py", "scripts/apu_isa.py", "scripts/apu_p5_coefficients.py",
|
||||
"scripts/apu_kws.py", "scripts/build_apu_p5_bundle.py",
|
||||
"scripts/apu_kws.py", "scripts/apu_kws_coeff.py", "scripts/build_apu_p5_bundle.py",
|
||||
"rtl/ip/multimedia/apu_p5_codecs.apus", "rtl/ip/multimedia/apu_define.svh",
|
||||
"rtl/ip/multimedia/apb4_apu.sv", "rtl/ip/multimedia/apu_local_sram.sv",
|
||||
"rtl/ip/multimedia/apu_kws_sram_client.sv",
|
||||
"rtl/ip/multimedia/apu_kws_sram_client.sv", "rtl/ip/multimedia/apu_kws_coeff_store.sv",
|
||||
"rtl/ip/multimedia/apu_kws_coeff_loader.sv", "rtl/ip/multimedia/apu_proof_memo.sv",
|
||||
"rtl/ip/multimedia/apu_reg.sv", "crt/include/retrosoc/hal/apu_regs.h",
|
||||
"scripts/apu_abi_digest.py", "app/apps/apu_release/main.c",
|
||||
*APU_CONFIGURATION_SOURCES,
|
||||
@@ -124,7 +125,7 @@ def apu_configurations(root: Path) -> list[dict]:
|
||||
bits = constants(root, "rtl/ip/multimedia/apu_define.svh")
|
||||
rows = []
|
||||
for name, profile in (("Default PRODUCT", "configs/ci/ihp130.mk"),
|
||||
("P7 acceptance", "configs/ci/ihp130-apu.mk")):
|
||||
("P9 acceptance", "configs/ci/ihp130-apu.mk")):
|
||||
source = (root / profile).read_text(encoding="utf-8")
|
||||
assignments = re.findall(r"^APU_ENABLE_P7\s*[:?+]?=\s*([^\n#]+)", source, re.M)
|
||||
if len(assignments) > 1 or (assignments and assignments[0].strip() not in {"YES", "NO"}):
|
||||
@@ -137,11 +138,11 @@ def apu_configurations(root: Path) -> list[dict]:
|
||||
for codec in ("WAV", "FLAC", "MP3", "KWS")}})
|
||||
if rows[-1]["formats"] != {"WAV": True, "FLAC": True, "MP3": False, "KWS": enabled}:
|
||||
raise ValueError("APU deployed format capability meanings changed")
|
||||
if name == "P7 acceptance" and (re.findall(r"^APP\s*:=\s*(\w+)\s*$", source, re.M) != ["apu_release"]
|
||||
if name == "P9 acceptance" and (re.findall(r"^APP\s*:=\s*(\w+)\s*$", source, re.M) != ["apu_release"]
|
||||
or re.findall(r"^HAVE_CSR\s*:=\s*(\w+)\s*$", source, re.M) != ["YES"]):
|
||||
raise ValueError("APU P7 acceptance application or IRQ configuration changed")
|
||||
raise ValueError("APU P9 acceptance application or IRQ configuration changed")
|
||||
if [(r["enabled"], r["capability"], r["digest"]) for r in rows] != [
|
||||
(False, 0x1BD, 0), (True, 0x1FD, 0xF5005D7C)]:
|
||||
(False, 0x1BD, 0), (True, 0x3FD, 0x63E96066)]:
|
||||
raise ValueError("review APU configuration identities")
|
||||
values = constants(root, "crt/include/retrosoc/hal/apu_regs.h")
|
||||
if (values.get("RS_APU_DIGEST_P7_IMPLEMENTED"), values.get("RS_APU_CAPABILITY0_P5_IMPLEMENTED"),
|
||||
@@ -156,11 +157,11 @@ def apu_acceptance_steps(root: Path) -> list[dict]:
|
||||
text = (root / source).read_text(encoding="utf-8")
|
||||
body = function_body(text, "rs_apu_release_run")
|
||||
stages = (
|
||||
("probe", "rs_apu_probe", 5, "After holding HP in reset, waiting for SDRAM and loading the HP bundle, LP checks the P7 capability and ABI digest."),
|
||||
("probe", "rs_apu_probe", 5, "After holding HP in reset, waiting for SDRAM and loading the HP bundle, LP checks the P9 capability and ABI digest."),
|
||||
("quiesce", "rs_apu_release_quiesce", 6, "LP quiesces the APU resource and waits for LP ownership, asserted quiesce, reset released and engine idle."),
|
||||
("stage", "rs_apu_release_stage_assets", 7, "With the resource quiesced, LP stages the APUMC image, APUM model, WAV input and KWS PCM window in SDRAM."),
|
||||
("acl", "rs_apu_set_acl", 8, "LP configures the permitted read and write address ranges before requesting either image load."),
|
||||
("load", "rs_apu_release_load_images", 9, "LP loads the microcode, checks its valid/lock state and CRC, then loads the model and checks its status and CRC."),
|
||||
("stage", "rs_apu_release_stage_assets", 7, "With the resource quiesced, LP stages the APUMC, APUC and APUM images plus WAV input and the KWS PCM window in SDRAM."),
|
||||
("acl", "rs_apu_set_acl", 8, "LP configures the permitted read and write address ranges before requesting the three image loads."),
|
||||
("load", "rs_apu_release_load_images", 9, "LP loads and verifies APUMC, APUC and APUM in that order before publishing any KWS work."),
|
||||
("handoff", "rs_apu_release_handoff", 10, "LP hands ownership to HP after successful image loading, then publishes the shared page and mailbox request before releasing HP."),
|
||||
)
|
||||
calls = list(re.finditer(r"\b(" + "|".join(row[1] for row in stages) + r")\s*\(", body))
|
||||
@@ -172,8 +173,8 @@ def apu_acceptance_steps(root: Path) -> list[dict]:
|
||||
if failures != [str(stage[2])]:
|
||||
raise ValueError("APU acceptance failure checkpoint changed")
|
||||
loads = function_body(text, "rs_apu_release_load_images")
|
||||
if re.findall(r"\b(rs_apu_(?:microcode|kws_model)_load)\s*\(", loads) != [
|
||||
"rs_apu_microcode_load", "rs_apu_kws_model_load"]:
|
||||
if re.findall(r"\b(rs_apu_(?:microcode|kws_coeff|kws_model)_load)\s*\(", loads) != [
|
||||
"rs_apu_microcode_load", "rs_apu_kws_coeff_load", "rs_apu_kws_model_load"]:
|
||||
raise ValueError("APU acceptance image-load order changed")
|
||||
tail = body[calls[-1].end():]
|
||||
if re.findall(r"\b(rs_apu_release_fill_page|rs_hp_mailbox_clear_lp_interrupt|rs_hp_mailbox_send_to_hp|"
|
||||
|
||||
+4
-2
@@ -164,8 +164,10 @@ retained, not an enabled third codec. The P9 refreeze requires macro-backed
|
||||
coefficient/profile storage and microcode memoization instead of large inferred
|
||||
arrays/case ROMs. Its full IHP130 inventory is 76 4 KiB wrappers: the existing
|
||||
44 (including proof stack), 17 memo/bitmap and 15 coefficient/profile wrappers.
|
||||
APUC startup loading and additive APB V1.2 are future P9 implementation
|
||||
obligations; this refreeze does not claim that they are already implemented.
|
||||
The implementation provides LP APUC startup loading, bounded synchronous
|
||||
coefficient access, macro-backed proof memoization and additive APB V1.2.
|
||||
Release qualification still requires the frozen P9 evidence rather than source
|
||||
presence alone.
|
||||
The model, fixed-point results, 32/112 KiB advertised capacities and existing
|
||||
system clock/reset/IRQ/resource assignments remain fixed. Macro/init/latency
|
||||
changes must re-pass P5/P7 functional/concurrency tests and the P9 synthesis
|
||||
|
||||
+194
-16
@@ -40,21 +40,26 @@ module apb4_apu #(
|
||||
logic [31:0] s_stream_stat, s_job_stat, s_scheduler_job_stat, s_ring_stat, s_ring_completed;
|
||||
logic [ 7:0][31:0] s_job_config;
|
||||
logic [1023:0] s_direct_descriptor;
|
||||
logic [ 10:0] s_irq_set;
|
||||
logic [ 11:0] s_irq_set;
|
||||
logic [ 7:0] s_mc_status;
|
||||
logic [31:0] s_mc_abi, s_mc_actual_crc, s_mc_load_count;
|
||||
logic [31:0] s_kws_status, s_kws_result, s_kws_timestamp_lo, s_kws_timestamp_hi;
|
||||
logic [31:0] s_kws_frame_count, s_kws_inference_count, s_kws_hit_count, s_kws_overrun_count;
|
||||
logic [31:0] s_kws_model_status, s_kws_model_actual_crc;
|
||||
logic [31:0] s_kws_model_addr, s_kws_model_size, s_kws_model_crc;
|
||||
logic [31:0] s_kws_coeff_addr, s_kws_coeff_size, s_kws_coeff_crc;
|
||||
logic [31:0] s_kws_coeff_status, s_kws_coeff_actual_crc;
|
||||
logic [63:0] s_kws_coefficient_id;
|
||||
logic [15:0] s_kws_config;
|
||||
logic [31:0] s_kws_input_config;
|
||||
logic [ 1:0] s_kws_control;
|
||||
logic s_kws_clear_history;
|
||||
logic s_kws_disable_request;
|
||||
logic [10:0] s_kws_irq_set;
|
||||
logic s_kws_model_load, s_kws_rx_ready, s_kws_busy, s_kws_idle, s_kws_disable_done;
|
||||
logic s_kws_model_load, s_kws_coeff_load, s_kws_rx_ready, s_kws_busy, s_kws_idle;
|
||||
logic s_kws_disable_done;
|
||||
logic s_kws_loader_busy;
|
||||
logic s_kws_coeff_loader_busy, s_kws_coeff_valid, s_kws_coeff_lock;
|
||||
logic s_kws_config_publish;
|
||||
logic [15:0] s_kws_config_default, s_codec_kws_config;
|
||||
logic s_codec_kws_config_valid;
|
||||
@@ -78,6 +83,34 @@ module apb4_apu #(
|
||||
logic [3:0] s_kws_loader_fault_stage;
|
||||
logic [1:0] s_kws_loader_fault_resp;
|
||||
logic [31:0] s_kws_loader_fault_addr, s_kws_loader_fault_detail;
|
||||
logic s_kws_coeff_loader_done, s_kws_coeff_loader_abort_done, s_kws_coeff_loader_fault_v;
|
||||
logic [5:0] s_kws_coeff_loader_fault_code;
|
||||
logic [3:0] s_kws_coeff_loader_fault_stage;
|
||||
logic [1:0] s_kws_coeff_loader_fault_resp;
|
||||
logic [31:0] s_kws_coeff_loader_fault_addr, s_kws_coeff_loader_fault_detail;
|
||||
logic s_kws_coeff_dma_req, s_kws_coeff_dma_rdy, s_kws_coeff_dma_data_rdy;
|
||||
logic [31:0] s_kws_coeff_dma_addr, s_kws_coeff_dma_bytes;
|
||||
logic s_kws_coeff_store_active, s_kws_coeff_store_req, s_kws_coeff_store_write;
|
||||
logic [13:0] s_kws_coeff_store_addr;
|
||||
logic [31:0] s_kws_coeff_store_wdata;
|
||||
logic s_kws_coeff_store_ready, s_kws_coeff_store_valid, s_kws_coeff_store_fault;
|
||||
logic [31:0] s_kws_coeff_store_rdata;
|
||||
logic s_kws_coeff_front_req, s_kws_coeff_front_ready;
|
||||
logic [ 3:0] s_kws_coeff_front_kind;
|
||||
logic [13:0] s_kws_coeff_front_index;
|
||||
logic s_kws_coeff_front_resp_valid, s_kws_coeff_front_resp_ready;
|
||||
logic [63:0] s_kws_coeff_front_resp_data;
|
||||
logic s_kws_coeff_front_resp_fault;
|
||||
logic s_kws_coeff_infer_req, s_kws_coeff_infer_ready;
|
||||
logic [6:0] s_kws_coeff_infer_index;
|
||||
logic s_kws_coeff_infer_resp_valid, s_kws_coeff_infer_resp_ready;
|
||||
logic [31:0] s_kws_coeff_infer_resp_data;
|
||||
logic s_kws_coeff_infer_resp_fault;
|
||||
logic s_kws_coeff_profile_req, s_kws_coeff_profile_ready;
|
||||
logic [10:0] s_kws_coeff_profile_index;
|
||||
logic s_kws_coeff_profile_resp_valid, s_kws_coeff_profile_resp_ready;
|
||||
logic [31:0] s_kws_coeff_profile_resp_data;
|
||||
logic s_kws_coeff_profile_resp_fault;
|
||||
logic s_kws_engine_model_v_unused, s_kws_engine_model_lock_unused;
|
||||
logic [31:0] s_kws_engine_model_stat_unused, s_kws_engine_model_crc_unused;
|
||||
logic s_direct_start_allowed;
|
||||
@@ -238,6 +271,7 @@ module apb4_apu #(
|
||||
logic s_transport_frame_commit, s_transport_cancel;
|
||||
logic s_transport_input_pending, s_transport_output_pending, s_codec_transport_idle;
|
||||
logic s_dma_admission_block;
|
||||
logic s_dma_quiesce, s_kws_memory_start;
|
||||
logic s_system_idle, s_resource_reset_request, s_resource_reset_apply_q;
|
||||
logic s_resource_reset_pending_q, s_resource_reset_seen_q;
|
||||
logic s_tx_route_apu, s_rx_route_apu;
|
||||
@@ -331,7 +365,8 @@ module apb4_apu #(
|
||||
((s_dma_owner_q == 3'd1) ? u_scheduler_read_axis.tready :
|
||||
((s_dma_owner_q == 3'd2) ? u_transport_read_axis.tready :
|
||||
((s_dma_owner_q == 3'd3) ? s_kws_loader_dma_rdy_data :
|
||||
s_kws_mem_dma_data_rdy)));
|
||||
((s_dma_owner_q == 3'd4) ? s_kws_mem_dma_data_rdy :
|
||||
s_kws_coeff_dma_data_rdy))));
|
||||
assign u_dma_write_axis.tdata = (s_dma_owner_q == 3'd2) ?
|
||||
u_transport_write_axis.tdata : u_scheduler_write_axis.tdata;
|
||||
assign u_dma_write_axis.tkeep = (s_dma_owner_q == 3'd2) ?
|
||||
@@ -348,12 +383,14 @@ module apb4_apu #(
|
||||
assign u_transport_write_axis.tready = (s_dma_owner_q == 3'd2) && u_dma_write_axis.tready;
|
||||
assign u_scheduler_write_axis.tready = (s_dma_owner_q == 3'd1) && u_dma_write_axis.tready;
|
||||
assign s_irq_set = {
|
||||
s_kws_coeff_loader_done && (s_kws_coeff_loader_fault_code != 6'd0 || s_kws_coeff_valid),
|
||||
s_seq_trap_event,
|
||||
s_xrun_evt || s_kws_irq_set[`APB4_APU__IRQ_STREAM_XRUN],
|
||||
s_fault_valid || s_kws_loader_fault_v || s_kws_fault_valid,
|
||||
s_output_watermark_evt || s_transport_event_output,
|
||||
s_input_watermark_evt || s_transport_event_input,
|
||||
s_abort_done || s_mc_abort_done || s_seq_abort_done || s_kws_loader_abort_done,
|
||||
s_abort_done || s_mc_abort_done || s_seq_abort_done || s_kws_loader_abort_done ||
|
||||
s_kws_coeff_loader_abort_done,
|
||||
s_kws_loader_done,
|
||||
s_mc_load_done,
|
||||
s_kws_irq_set[`APB4_APU__IRQ_KWS_HIT],
|
||||
@@ -362,13 +399,15 @@ module apb4_apu #(
|
||||
};
|
||||
assign s_tx_route_apu = s_stream_route[1:0] == 2'd1;
|
||||
assign s_rx_route_apu = s_stream_route[3:2] == 2'd1;
|
||||
assign s_direct_start_allowed = !s_kws_loader_busy && !s_kws_disable_request &&
|
||||
assign s_direct_start_allowed = !s_kws_loader_busy && !s_kws_coeff_loader_busy &&
|
||||
!s_kws_disable_request &&
|
||||
s_codec_direct_allowed &&
|
||||
((s_job_config[0][3:0] != 4'd1) ||
|
||||
(s_kws_model_valid && s_kws_model_lock && (s_kws_control == 2'd3)));
|
||||
(s_kws_model_valid && s_kws_model_lock && s_kws_coeff_valid && s_kws_coeff_lock &&
|
||||
(s_kws_control == 2'd3)));
|
||||
assign s_system_idle = s_scheduler_idle && !s_dma_busy && s_stream_idle && s_mc_idle &&
|
||||
s_seq_idle && !s_primitive_busy && s_codec_idle && s_codec_transport_idle &&
|
||||
s_kws_idle && !s_kws_busy && !s_kws_loader_busy;
|
||||
s_kws_idle && !s_kws_busy && !s_kws_loader_busy && !s_kws_coeff_loader_busy;
|
||||
assign s_codec_kws_active = s_codec_busy && (s_codec_descriptor[3:0] == 4'd1);
|
||||
assign s_kws_disable_done = s_kws_idle && !s_codec_kws_active;
|
||||
assign idle_o = s_system_idle;
|
||||
@@ -379,10 +418,15 @@ module apb4_apu #(
|
||||
assign s_dma_admission_block = (quiesce_i || s_resource_reset_request) &&
|
||||
!s_job_stat[`APB4_APU__JOB_STATUS_BUSY] &&
|
||||
!s_ring_stat[`APB4_APU__RING_STATUS_WRITEBACK_PENDING];
|
||||
assign s_dma_quiesce = s_dma_admission_block && s_mc_idle && !s_kws_loader_busy &&
|
||||
!s_kws_coeff_loader_busy;
|
||||
assign s_kws_memory_start = s_codec_kws_start && s_kws_coeff_valid && s_kws_coeff_lock &&
|
||||
!s_resource_reset_request;
|
||||
assign s_dma_select = !s_mc_idle ? 3'd0 :
|
||||
(s_kws_coeff_dma_req ? 3'd5 :
|
||||
(s_kws_loader_dma_req ? 3'd3 :
|
||||
(s_kws_mem_dma_req ? 3'd4 :
|
||||
(s_ring_dma_req_valid ? 3'd1 : 3'd2)));
|
||||
(s_ring_dma_req_valid ? 3'd1 : 3'd2))));
|
||||
assign s_dma_req_valid = (s_dma_select == 3'd0) ?
|
||||
(s_mc_dma_req_valid && !s_resource_reset_request) :
|
||||
((s_dma_select == 3'd1) ? s_ring_dma_req_valid :
|
||||
@@ -392,17 +436,20 @@ module apb4_apu #(
|
||||
assign s_dma_req_addr = (s_dma_select == 3'd0) ? s_mc_dma_req_addr :
|
||||
((s_dma_select == 3'd1) ? s_ring_dma_req_addr :
|
||||
((s_dma_select == 3'd2) ? s_transport_dma_req_addr :
|
||||
((s_dma_select == 3'd3) ? s_kws_loader_dma_addr : s_kws_mem_dma_addr)));
|
||||
((s_dma_select == 3'd3) ? s_kws_loader_dma_addr :
|
||||
((s_dma_select == 3'd4) ? s_kws_mem_dma_addr : s_kws_coeff_dma_addr))));
|
||||
assign s_dma_req_bytes = (s_dma_select == 3'd0) ? s_mc_dma_req_bytes :
|
||||
((s_dma_select == 3'd1) ? s_ring_dma_req_bytes :
|
||||
((s_dma_select == 3'd2) ? s_transport_dma_req_bytes :
|
||||
((s_dma_select == 3'd3) ? s_kws_loader_dma_bytes : s_kws_mem_dma_bytes)));
|
||||
((s_dma_select == 3'd3) ? s_kws_loader_dma_bytes :
|
||||
((s_dma_select == 3'd4) ? s_kws_mem_dma_bytes : s_kws_coeff_dma_bytes))));
|
||||
assign s_ring_dma_req_ready = (s_dma_select == 3'd1) && s_dma_req_ready;
|
||||
assign s_mc_dma_req_ready = (s_dma_select == 3'd0) && s_dma_req_ready &&
|
||||
!s_resource_reset_request;
|
||||
assign s_transport_dma_req_ready = (s_dma_select == 3'd2) && s_dma_req_ready;
|
||||
assign s_kws_loader_dma_rdy = (s_dma_select == 3'd3) && s_dma_req_ready;
|
||||
assign s_kws_mem_dma_rdy = (s_dma_select == 3'd4) && s_dma_req_ready;
|
||||
assign s_kws_coeff_dma_rdy = (s_dma_select == 3'd5) && s_dma_req_ready;
|
||||
assign s_kws_mem_data_v = u_dma_read_axis.tvalid && (s_dma_owner_q == 3'd4);
|
||||
assign s_kws_mem_dma_done = s_dma_done && (s_dma_owner_q == 3'd4);
|
||||
assign s_kws_mem_dma_error = s_dma_err && (s_dma_owner_q == 3'd4);
|
||||
@@ -492,6 +539,14 @@ module apb4_apu #(
|
||||
s_fault_index = 8'd0;
|
||||
s_fault_addr = s_kws_loader_fault_addr;
|
||||
s_fault_detail = s_kws_loader_fault_detail;
|
||||
end else if (!s_fault_valid && s_kws_coeff_loader_fault_v) begin
|
||||
s_fault_valid = 1'b1;
|
||||
s_fault_code = s_kws_coeff_loader_fault_code;
|
||||
s_fault_stage = s_kws_coeff_loader_fault_stage;
|
||||
s_fault_resp = s_kws_coeff_loader_fault_resp;
|
||||
s_fault_index = 8'd0;
|
||||
s_fault_addr = s_kws_coeff_loader_fault_addr;
|
||||
s_fault_detail = s_kws_coeff_loader_fault_detail;
|
||||
end else if (!s_fault_valid && s_kws_fault_valid) begin
|
||||
s_fault_valid = 1'b1;
|
||||
s_fault_code = s_kws_fault_code;
|
||||
@@ -532,7 +587,7 @@ module apb4_apu #(
|
||||
.core_idle_i(idle_o),
|
||||
.finite_idle_i (s_scheduler_idle && s_codec_idle && s_codec_transport_idle &&
|
||||
!s_dma_busy && s_mc_idle && s_seq_idle && !s_primitive_busy &&
|
||||
!s_kws_loader_busy),
|
||||
!s_kws_loader_busy && !s_kws_coeff_loader_busy),
|
||||
.core_busy_i(!idle_o),
|
||||
.core_aborting_i(s_dma_aborting || s_scheduler_aborting || s_resource_reset_request),
|
||||
.direct_start_allowed_i(s_direct_start_allowed),
|
||||
@@ -575,6 +630,9 @@ module apb4_apu #(
|
||||
.kws_overrun_count_i(s_kws_overrun_count),
|
||||
.kws_model_status_i(s_kws_model_status),
|
||||
.kws_model_actual_crc_i(s_kws_model_actual_crc),
|
||||
.kws_coeff_status_i(s_kws_coeff_status),
|
||||
.kws_coeff_actual_crc_i(s_kws_coeff_actual_crc),
|
||||
.kws_coefficient_id_i(s_kws_coefficient_id),
|
||||
.kws_config_publish_i(s_kws_config_publish),
|
||||
.kws_config_default_i(s_kws_config_default),
|
||||
.kws_disable_done_i(s_kws_disable_done),
|
||||
@@ -593,6 +651,7 @@ module apb4_apu #(
|
||||
.ring_kick_o(s_ring_kick),
|
||||
.microcode_load_o(s_microcode_load),
|
||||
.model_load_o(s_kws_model_load),
|
||||
.coefficient_load_o(s_kws_coeff_load),
|
||||
.counter_clear_o(s_cnt_clear),
|
||||
.perf_enable_o(s_perf_enable),
|
||||
.xrun_clear_o(s_xrun_clear),
|
||||
@@ -616,6 +675,9 @@ module apb4_apu #(
|
||||
.kws_model_address_o(s_kws_model_addr),
|
||||
.kws_model_size_o(s_kws_model_size),
|
||||
.kws_model_expected_crc_o(s_kws_model_crc),
|
||||
.kws_coeff_address_o(s_kws_coeff_addr),
|
||||
.kws_coeff_size_o(s_kws_coeff_size),
|
||||
.kws_coeff_expected_crc_o(s_kws_coeff_crc),
|
||||
.kws_control_o(s_kws_control),
|
||||
.kws_config_o(s_kws_config),
|
||||
.kws_input_config_o(s_kws_input_config),
|
||||
@@ -626,6 +688,99 @@ module apb4_apu #(
|
||||
);
|
||||
|
||||
assign s_kws_loader_fault_v = s_kws_loader_done && (s_kws_loader_fault_code != 6'd0);
|
||||
assign s_kws_coeff_loader_fault_v = s_kws_coeff_loader_done &&
|
||||
(s_kws_coeff_loader_fault_code != 6'd0);
|
||||
|
||||
apu_kws_coeff_loader u_kws_coeff_loader (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.start_i (s_kws_coeff_load),
|
||||
.abort_i (s_abort),
|
||||
.soft_reset_i (s_soft_reset),
|
||||
.resource_reset_request_i(s_resource_reset_request),
|
||||
.resource_reset_i (s_resource_reset_apply_q),
|
||||
.quiesce_i (quiesce_i),
|
||||
.address_i (s_kws_coeff_addr),
|
||||
.size_i (s_kws_coeff_size),
|
||||
.expected_crc_i (s_kws_coeff_crc),
|
||||
.acl_base_i (s_read_base),
|
||||
.acl_limit_i (s_read_limit),
|
||||
.dma_request_valid_o (s_kws_coeff_dma_req),
|
||||
.dma_request_ready_i (s_kws_coeff_dma_rdy),
|
||||
.dma_request_address_o (s_kws_coeff_dma_addr),
|
||||
.dma_request_bytes_o (s_kws_coeff_dma_bytes),
|
||||
.dma_data_i (u_dma_read_axis.tdata),
|
||||
.dma_keep_i (u_dma_read_axis.tkeep),
|
||||
.dma_last_i (u_dma_read_axis.tlast),
|
||||
.dma_valid_i (u_dma_read_axis.tvalid && (s_dma_owner_q == 3'd5)),
|
||||
.dma_ready_o (s_kws_coeff_dma_data_rdy),
|
||||
.dma_done_i (s_dma_done && (s_dma_owner_q == 3'd5)),
|
||||
.dma_error_i (s_dma_err && (s_dma_owner_q == 3'd5)),
|
||||
.dma_error_code_i (s_dma_err_code),
|
||||
.dma_error_stage_i (s_dma_err_stage),
|
||||
.dma_error_resp_i (s_dma_err_resp),
|
||||
.dma_error_address_i (s_dma_err_addr),
|
||||
.store_active_o (s_kws_coeff_store_active),
|
||||
.store_req_o (s_kws_coeff_store_req),
|
||||
.store_write_o (s_kws_coeff_store_write),
|
||||
.store_addr_o (s_kws_coeff_store_addr),
|
||||
.store_data_o (s_kws_coeff_store_wdata),
|
||||
.store_ready_i (s_kws_coeff_store_ready),
|
||||
.store_valid_i (s_kws_coeff_store_valid),
|
||||
.store_data_i (s_kws_coeff_store_rdata),
|
||||
.store_fault_i (s_kws_coeff_store_fault),
|
||||
.busy_o (s_kws_coeff_loader_busy),
|
||||
.valid_o (s_kws_coeff_valid),
|
||||
.lock_o (s_kws_coeff_lock),
|
||||
.actual_crc_o (s_kws_coeff_actual_crc),
|
||||
.coefficient_id_o (s_kws_coefficient_id),
|
||||
.status_o (s_kws_coeff_status),
|
||||
.error_code_o (s_kws_coeff_loader_fault_code),
|
||||
.error_stage_o (s_kws_coeff_loader_fault_stage),
|
||||
.error_resp_o (s_kws_coeff_loader_fault_resp),
|
||||
.error_address_o (s_kws_coeff_loader_fault_addr),
|
||||
.error_detail_o (s_kws_coeff_loader_fault_detail),
|
||||
.done_o (s_kws_coeff_loader_done),
|
||||
.abort_done_o (s_kws_coeff_loader_abort_done)
|
||||
);
|
||||
|
||||
apu_kws_coeff_store u_kws_coeff_store (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.abort_i (s_abort || s_soft_reset || s_resource_reset_apply_q),
|
||||
.valid_i (s_kws_coeff_valid),
|
||||
.loader_active_i (s_kws_coeff_store_active),
|
||||
.loader_req_i (s_kws_coeff_store_req),
|
||||
.loader_write_i (s_kws_coeff_store_write),
|
||||
.loader_addr_i (s_kws_coeff_store_addr),
|
||||
.loader_data_i (s_kws_coeff_store_wdata),
|
||||
.loader_ready_o (s_kws_coeff_store_ready),
|
||||
.loader_valid_o (s_kws_coeff_store_valid),
|
||||
.loader_data_o (s_kws_coeff_store_rdata),
|
||||
.loader_fault_o (s_kws_coeff_store_fault),
|
||||
.frontend_req_valid_i (s_kws_coeff_front_req),
|
||||
.frontend_req_ready_o (s_kws_coeff_front_ready),
|
||||
.frontend_kind_i (s_kws_coeff_front_kind),
|
||||
.frontend_index_i (s_kws_coeff_front_index),
|
||||
.frontend_resp_valid_o (s_kws_coeff_front_resp_valid),
|
||||
.frontend_resp_ready_i (s_kws_coeff_front_resp_ready),
|
||||
.frontend_resp_data_o (s_kws_coeff_front_resp_data),
|
||||
.frontend_resp_fault_o (s_kws_coeff_front_resp_fault),
|
||||
.inference_req_valid_i (s_kws_coeff_infer_req),
|
||||
.inference_req_ready_o (s_kws_coeff_infer_ready),
|
||||
.inference_index_i (s_kws_coeff_infer_index),
|
||||
.inference_resp_valid_o(s_kws_coeff_infer_resp_valid),
|
||||
.inference_resp_ready_i(s_kws_coeff_infer_resp_ready),
|
||||
.inference_resp_data_o (s_kws_coeff_infer_resp_data),
|
||||
.inference_resp_fault_o(s_kws_coeff_infer_resp_fault),
|
||||
.profile_req_valid_i (s_kws_coeff_profile_req),
|
||||
.profile_req_ready_o (s_kws_coeff_profile_ready),
|
||||
.profile_index_i (s_kws_coeff_profile_index),
|
||||
.profile_resp_valid_o (s_kws_coeff_profile_resp_valid),
|
||||
.profile_resp_ready_i (s_kws_coeff_profile_resp_ready),
|
||||
.profile_resp_data_o (s_kws_coeff_profile_resp_data),
|
||||
.profile_resp_fault_o (s_kws_coeff_profile_resp_fault)
|
||||
);
|
||||
|
||||
apu_kws_model_loader u_kws_model_loader (
|
||||
.clk_i (clk_i),
|
||||
@@ -661,6 +816,13 @@ module apb4_apu #(
|
||||
.local_data_o (s_kws_loader_local_data),
|
||||
.local_strb_o (s_kws_loader_local_strb),
|
||||
.local_ready_i (s_kws_loader_local_rdy),
|
||||
.profile_request_valid_o (s_kws_coeff_profile_req),
|
||||
.profile_request_ready_i (s_kws_coeff_profile_ready),
|
||||
.profile_index_o (s_kws_coeff_profile_index),
|
||||
.profile_response_valid_i(s_kws_coeff_profile_resp_valid),
|
||||
.profile_response_ready_o(s_kws_coeff_profile_resp_ready),
|
||||
.profile_data_i (s_kws_coeff_profile_resp_data),
|
||||
.profile_fault_i (s_kws_coeff_profile_resp_fault),
|
||||
.busy_o (s_kws_loader_busy),
|
||||
.valid_o (s_kws_model_valid),
|
||||
.lock_o (s_kws_model_lock),
|
||||
@@ -694,7 +856,7 @@ module apb4_apu #(
|
||||
.model_valid_i(s_kws_model_valid),
|
||||
.model_lock_i(s_kws_model_lock),
|
||||
.sequencer_timeout_i(s_sequencer_timeout),
|
||||
.memory_start_i (s_codec_kws_start && !s_resource_reset_request),
|
||||
.memory_start_i(s_kws_memory_start),
|
||||
.memory_start_ready_o(s_kws_mem_start_ready),
|
||||
.memory_address_i(s_codec_descriptor[(2*32)+:32]),
|
||||
.memory_dma_request_valid_o(s_kws_mem_dma_req),
|
||||
@@ -739,6 +901,21 @@ module apb4_apu #(
|
||||
.scratch_write_data_o(s_kws_scratch_write_data),
|
||||
.scratch_write_strb_o(s_kws_scratch_write_strb),
|
||||
.scratch_access_err_i(s_kws_scratch_err),
|
||||
.coeff_frontend_req_valid_o(s_kws_coeff_front_req),
|
||||
.coeff_frontend_req_ready_i(s_kws_coeff_front_ready),
|
||||
.coeff_frontend_kind_o(s_kws_coeff_front_kind),
|
||||
.coeff_frontend_index_o(s_kws_coeff_front_index),
|
||||
.coeff_frontend_resp_valid_i(s_kws_coeff_front_resp_valid),
|
||||
.coeff_frontend_resp_ready_o(s_kws_coeff_front_resp_ready),
|
||||
.coeff_frontend_data_i(s_kws_coeff_front_resp_data),
|
||||
.coeff_frontend_fault_i(s_kws_coeff_front_resp_fault),
|
||||
.coeff_inference_req_valid_o(s_kws_coeff_infer_req),
|
||||
.coeff_inference_req_ready_i(s_kws_coeff_infer_ready),
|
||||
.coeff_inference_index_o(s_kws_coeff_infer_index),
|
||||
.coeff_inference_resp_valid_i(s_kws_coeff_infer_resp_valid),
|
||||
.coeff_inference_resp_ready_o(s_kws_coeff_infer_resp_ready),
|
||||
.coeff_inference_data_i(s_kws_coeff_infer_resp_data),
|
||||
.coeff_inference_fault_i(s_kws_coeff_infer_resp_fault),
|
||||
.stream_i(u_apu_rx_axis),
|
||||
.rx_ready_o(s_kws_rx_ready),
|
||||
.status_o(s_kws_status),
|
||||
@@ -769,7 +946,7 @@ module apb4_apu #(
|
||||
.clk_i(clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.abort_i(s_dma_abort),
|
||||
.quiesce_i (s_dma_admission_block && s_mc_idle && !s_kws_loader_busy),
|
||||
.quiesce_i(s_dma_quiesce),
|
||||
.bridge_epoch_i(bridge_epoch_i),
|
||||
.perf_enable_i(s_perf_enable),
|
||||
.counter_clear_i(s_cnt_clear),
|
||||
@@ -1137,9 +1314,10 @@ module apb4_apu #(
|
||||
.ring_result_kws_o(s_codec_ring_result_kws),
|
||||
.microcode_valid_i(s_mc_status[`APB4_APU__MC_STATUS_VALID]),
|
||||
.microcode_lock_i(s_mc_lock),
|
||||
.kws_model_valid_i(s_kws_model_valid),
|
||||
.kws_model_lock_i(s_kws_model_lock),
|
||||
.kws_memory_armed_i((s_kws_control == 2'd3) && !s_kws_disable_request),
|
||||
.kws_model_valid_i(s_kws_model_valid && s_kws_coeff_valid),
|
||||
.kws_model_lock_i(s_kws_model_lock && s_kws_coeff_lock),
|
||||
.kws_memory_armed_i((s_kws_control == 2'd3) && s_kws_coeff_valid &&
|
||||
s_kws_coeff_lock && !s_kws_disable_request),
|
||||
.entry_scratch_base_i(s_mc_entry_scratch_base),
|
||||
.entry_scratch_bytes_i(s_mc_entry_scratch_bytes),
|
||||
.sequencer_launch_o(s_codec_seq_launch),
|
||||
|
||||
@@ -84,6 +84,15 @@
|
||||
`define APB4_APU__KWS_MODEL_STATUS 12'h234
|
||||
`define APB4_APU__KWS_MODEL_ACTUAL_CRC 12'h238
|
||||
`define APB4_APU__KWS_INPUT_CONFIG 12'h23c
|
||||
`define APB4_APU__KWS_COEFF_ADDRESS 12'h240
|
||||
`define APB4_APU__KWS_COEFF_SIZE 12'h244
|
||||
`define APB4_APU__KWS_COEFF_EXPECTED_CRC 12'h248
|
||||
`define APB4_APU__KWS_COEFF_COMMAND 12'h24c
|
||||
`define APB4_APU__KWS_COEFF_STATUS 12'h250
|
||||
`define APB4_APU__KWS_COEFF_ACTUAL_CRC 12'h254
|
||||
`define APB4_APU__KWS_COEFF_ID_LO 12'h258
|
||||
`define APB4_APU__KWS_COEFF_ID_HI 12'h25c
|
||||
`define APB4_APU__KWS_COEFF_CAPACITY 12'h260
|
||||
|
||||
`define APB4_APU__PERF_CONTROL 12'h300
|
||||
`define APB4_APU__PERF_STATUS 12'h304
|
||||
@@ -117,6 +126,7 @@
|
||||
`define APB4_APU__CAPABILITY0_KWS 6
|
||||
`define APB4_APU__CAPABILITY0_SEQUENCER 7
|
||||
`define APB4_APU__CAPABILITY0_RESAMPLER 8
|
||||
`define APB4_APU__CAPABILITY0_COEFFICIENT_LOAD 9
|
||||
`define APB4_APU__KWS_INPUT_CONFIG_RATE_MASK 32'h0001ffff
|
||||
`define APB4_APU__KWS_INPUT_CONFIG_CHANNELS_SHIFT 17
|
||||
`define APB4_APU__KWS_INPUT_CONFIG_PRECISION_SHIFT 20
|
||||
@@ -180,6 +190,15 @@
|
||||
`define APB4_APU__IRQ_FIRST_ERROR 8
|
||||
`define APB4_APU__IRQ_STREAM_XRUN 9
|
||||
`define APB4_APU__IRQ_SEQUENCER_TRAP 10
|
||||
`define APB4_APU__IRQ_COEFFICIENT_LOAD_DONE 11
|
||||
|
||||
`define APB4_APU__KWS_COEFF_STATUS_BUSY 0
|
||||
`define APB4_APU__KWS_COEFF_STATUS_VALID 1
|
||||
`define APB4_APU__KWS_COEFF_STATUS_LOCKED 2
|
||||
`define APB4_APU__KWS_COEFF_STATUS_SIZE_ERROR 8
|
||||
`define APB4_APU__KWS_COEFF_STATUS_HEADER_ERROR 9
|
||||
`define APB4_APU__KWS_COEFF_STATUS_CRC_ERROR 10
|
||||
`define APB4_APU__KWS_COEFF_STATUS_PADDING_ERROR 11
|
||||
|
||||
`define APB4_APU__ERROR_VALID 0
|
||||
`define APB4_APU__ERROR_CODE 1
|
||||
@@ -359,6 +378,14 @@
|
||||
`define APB4_APU__APUM_ABI 32'h0001_0000
|
||||
`define APB4_APU__APUM_IMAGE_BYTES 32768
|
||||
`define APB4_APU__APUM_PAYLOAD_CRC 32'hb903_4b22
|
||||
`define APB4_APU__APUC_MAGIC 32'h4355_5041
|
||||
`define APB4_APU__APUC_ABI 32'h0001_0000
|
||||
`define APB4_APU__APUC_IMAGE_BYTES 61504
|
||||
`define APB4_APU__APUC_HEADER_BYTES 64
|
||||
`define APB4_APU__APUC_PAYLOAD_BYTES 61440
|
||||
`define APB4_APU__APUC_PAYLOAD_CRC 32'h25e7_c27d
|
||||
`define APB4_APU__APUC_COEFFICIENT_ID_LO 32'h8a0b_038d
|
||||
`define APB4_APU__APUC_COEFFICIENT_ID_HI 32'h806c_780d
|
||||
`define APB4_APU__APUMC_HEADER_MAGIC 0
|
||||
`define APB4_APU__APUMC_HEADER_ABI 1
|
||||
`define APB4_APU__APUMC_HEADER_TOTAL_BYTES 2
|
||||
|
||||
@@ -0,0 +1,501 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
`include "apu_define.svh"
|
||||
|
||||
module apu_kws_coeff_loader (
|
||||
// verilog_format: off -- preserve the private-DMA and coefficient-store columns
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic start_i,
|
||||
input logic abort_i,
|
||||
input logic soft_reset_i,
|
||||
input logic resource_reset_request_i,
|
||||
input logic resource_reset_i,
|
||||
input logic quiesce_i,
|
||||
input logic [31:0] address_i,
|
||||
input logic [31:0] size_i,
|
||||
input logic [31:0] expected_crc_i,
|
||||
input logic [31:0] acl_base_i,
|
||||
input logic [31:0] acl_limit_i,
|
||||
output logic dma_request_valid_o,
|
||||
input logic dma_request_ready_i,
|
||||
output logic [31:0] dma_request_address_o,
|
||||
output logic [31:0] dma_request_bytes_o,
|
||||
input logic [31:0] dma_data_i,
|
||||
input logic [ 3:0] dma_keep_i,
|
||||
input logic dma_last_i,
|
||||
input logic dma_valid_i,
|
||||
output logic dma_ready_o,
|
||||
input logic dma_done_i,
|
||||
input logic dma_error_i,
|
||||
input logic [ 5:0] dma_error_code_i,
|
||||
input logic [ 3:0] dma_error_stage_i,
|
||||
input logic [ 1:0] dma_error_resp_i,
|
||||
input logic [31:0] dma_error_address_i,
|
||||
output logic store_active_o,
|
||||
output logic store_req_o,
|
||||
output logic store_write_o,
|
||||
output logic [13:0] store_addr_o,
|
||||
output logic [31:0] store_data_o,
|
||||
input logic store_ready_i,
|
||||
input logic store_valid_i,
|
||||
input logic [31:0] store_data_i,
|
||||
input logic store_fault_i,
|
||||
output logic busy_o,
|
||||
output logic valid_o,
|
||||
output logic lock_o,
|
||||
output logic [31:0] actual_crc_o,
|
||||
output logic [63:0] coefficient_id_o,
|
||||
output logic [31:0] status_o,
|
||||
output logic [ 5:0] error_code_o,
|
||||
output logic [ 3:0] error_stage_o,
|
||||
output logic [ 1:0] error_resp_o,
|
||||
output logic [31:0] error_address_o,
|
||||
output logic [31:0] error_detail_o,
|
||||
output logic done_o,
|
||||
output logic abort_done_o
|
||||
// verilog_format: on
|
||||
);
|
||||
localparam logic [31:0] ImageBytes = 32'd61504;
|
||||
localparam logic [31:0] PayloadBytes = 32'd61440;
|
||||
localparam logic [31:0] PayloadCrc = 32'h25e7_c27d;
|
||||
localparam logic [31:0] CoefficientIdLow = 32'h8a0b_038d;
|
||||
localparam logic [31:0] CoefficientIdHigh = 32'h806c_780d;
|
||||
|
||||
typedef enum logic [3:0] {
|
||||
Idle,
|
||||
RequestHeader,
|
||||
ReceiveHeader,
|
||||
WaitHeader,
|
||||
CheckHeader,
|
||||
RequestPayload,
|
||||
ReceivePayload,
|
||||
WaitPayload,
|
||||
ReadbackRequest,
|
||||
Validate,
|
||||
Complete,
|
||||
CancelWait
|
||||
} loader_state_e;
|
||||
|
||||
loader_state_e s_state_q;
|
||||
logic [15:0][31:0] s_header_q;
|
||||
logic [31:0] s_addr_q, s_size_q, s_expected_crc_q, s_acl_base_q, s_acl_limit_q;
|
||||
logic [31:0] s_received_q;
|
||||
logic [31:0] s_crc_q, s_readback_crc_q;
|
||||
logic [13:0] s_readback_word_q;
|
||||
logic [13:0] s_readback_received_q;
|
||||
logic s_transfer_err_q, s_padding_err_q, s_store_pending_q;
|
||||
logic [31:0] s_padding_addr_q;
|
||||
logic s_valid_q, s_lock_q, s_done_q, s_abort_done_q, s_abort_pending_q;
|
||||
logic [31:0] s_actual_crc_q, s_stat_q;
|
||||
logic [63:0] s_coefficient_id_q;
|
||||
logic [ 5:0] s_err_code_q;
|
||||
logic [ 3:0] s_err_stage_q;
|
||||
logic [ 1:0] s_err_resp_q;
|
||||
logic [31:0] s_err_addr_q, s_err_detail_q;
|
||||
logic [32:0] s_last_addr, s_snapshot_last_addr;
|
||||
logic s_admit_ok, s_snapshot_range_ok, s_data_accept;
|
||||
logic [31:0] s_crc_after_word, s_readback_crc_after_word;
|
||||
logic s_header_size_err, s_header_err;
|
||||
logic [31:0] s_header_err_addr;
|
||||
|
||||
function automatic logic [31:0] crc32_byte(input logic [31:0] crc_i, input logic [7:0] byte_i);
|
||||
logic [31:0] s_crc;
|
||||
begin
|
||||
s_crc = crc_i ^ {24'd0, byte_i};
|
||||
for (int unsigned bit_index = 0; bit_index < 8; bit_index++) begin
|
||||
s_crc = s_crc[0] ? ((s_crc >> 1) ^ 32'hedb8_8320) : (s_crc >> 1);
|
||||
end
|
||||
return s_crc;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic [31:0] crc32_word(input logic [31:0] crc_i, input logic [31:0] data_i);
|
||||
logic [31:0] s_crc;
|
||||
begin
|
||||
s_crc = crc_i;
|
||||
for (int unsigned byte_index = 0; byte_index < 4; byte_index++) begin
|
||||
s_crc = crc32_byte(s_crc, data_i[(byte_index*8)+:8]);
|
||||
end
|
||||
return s_crc;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic payload_word_is_padding(input logic [13:0] word_i);
|
||||
logic [3:0] s_bank;
|
||||
logic [9:0] s_row;
|
||||
begin
|
||||
s_bank = word_i[13:10];
|
||||
s_row = word_i[9:0];
|
||||
unique case (s_bank)
|
||||
4'd0: payload_word_is_padding = s_row >= 10'd1006;
|
||||
4'd1: payload_word_is_padding = s_row >= 10'd769;
|
||||
4'd2: payload_word_is_padding = s_row >= 10'd768;
|
||||
4'd14: payload_word_is_padding = s_row >= 10'd637;
|
||||
default: payload_word_is_padding = 1'b0;
|
||||
endcase
|
||||
end
|
||||
endfunction
|
||||
|
||||
assign s_last_addr = {1'b0, address_i} + {1'b0, size_i} - 1'b1;
|
||||
assign s_admit_ok = (size_i == ImageBytes) && (address_i[5:0] == 6'd0) &&
|
||||
(address_i >= acl_base_i) && !s_last_addr[32] && (s_last_addr[31:0] <= acl_limit_i) &&
|
||||
quiesce_i && !s_lock_q;
|
||||
assign s_snapshot_last_addr = {1'b0, s_addr_q} + {1'b0, s_size_q} - 1'b1;
|
||||
assign s_snapshot_range_ok = (s_size_q == ImageBytes) && (s_addr_q[5:0] == 6'd0) &&
|
||||
(s_addr_q >= s_acl_base_q) && !s_snapshot_last_addr[32] &&
|
||||
(s_snapshot_last_addr[31:0] <= s_acl_limit_q);
|
||||
assign s_data_accept = dma_valid_i && dma_ready_o;
|
||||
assign s_crc_after_word = crc32_word(s_crc_q, dma_data_i);
|
||||
assign s_readback_crc_after_word = crc32_word(s_readback_crc_q, store_data_i);
|
||||
|
||||
always_comb begin
|
||||
s_header_size_err = 1'b0;
|
||||
s_header_err = 1'b0;
|
||||
s_header_err_addr = s_addr_q;
|
||||
if (s_header_q[2] != ImageBytes) begin
|
||||
s_header_size_err = 1'b1;
|
||||
s_header_err_addr = s_addr_q + 32'h08;
|
||||
end else if (s_header_q[4] != PayloadBytes) begin
|
||||
s_header_size_err = 1'b1;
|
||||
s_header_err_addr = s_addr_q + 32'h10;
|
||||
end else if (s_header_q[0] != 32'h4355_5041) begin
|
||||
s_header_err = 1'b1;
|
||||
end else if (s_header_q[1] != 32'h0001_0000) begin
|
||||
s_header_err = 1'b1;
|
||||
s_header_err_addr = s_addr_q + 32'h04;
|
||||
end else if (s_header_q[3] != 32'd64) begin
|
||||
s_header_err = 1'b1;
|
||||
s_header_err_addr = s_addr_q + 32'h0c;
|
||||
end else if (s_header_q[5] != 32'd1) begin
|
||||
s_header_err = 1'b1;
|
||||
s_header_err_addr = s_addr_q + 32'h14;
|
||||
end else if (s_header_q[6] != 32'd1) begin
|
||||
s_header_err = 1'b1;
|
||||
s_header_err_addr = s_addr_q + 32'h18;
|
||||
end else if (s_header_q[7] != 32'd15) begin
|
||||
s_header_err = 1'b1;
|
||||
s_header_err_addr = s_addr_q + 32'h1c;
|
||||
end else if (s_header_q[8] != 32'd10) begin
|
||||
s_header_err = 1'b1;
|
||||
s_header_err_addr = s_addr_q + 32'h20;
|
||||
end else if (s_header_q[9] != PayloadCrc) begin
|
||||
s_header_err = 1'b1;
|
||||
s_header_err_addr = s_addr_q + 32'h24;
|
||||
end else if (s_header_q[10] != CoefficientIdLow) begin
|
||||
s_header_err = 1'b1;
|
||||
s_header_err_addr = s_addr_q + 32'h28;
|
||||
end else if (s_header_q[11] != CoefficientIdHigh) begin
|
||||
s_header_err = 1'b1;
|
||||
s_header_err_addr = s_addr_q + 32'h2c;
|
||||
end else if (s_header_q[12] != 32'hb903_4b22) begin
|
||||
s_header_err = 1'b1;
|
||||
s_header_err_addr = s_addr_q + 32'h30;
|
||||
end else if (s_header_q[13] != 32'd11629080) begin
|
||||
s_header_err = 1'b1;
|
||||
s_header_err_addr = s_addr_q + 32'h34;
|
||||
end else if (s_header_q[14] != 32'd0) begin
|
||||
s_header_err = 1'b1;
|
||||
s_header_err_addr = s_addr_q + 32'h38;
|
||||
end else if (s_header_q[15] != 32'd0) begin
|
||||
s_header_err = 1'b1;
|
||||
s_header_err_addr = s_addr_q + 32'h3c;
|
||||
end
|
||||
end
|
||||
|
||||
assign dma_request_valid_o = (s_state_q inside {RequestHeader, RequestPayload}) &&
|
||||
s_snapshot_range_ok;
|
||||
assign dma_request_address_o = (s_state_q == RequestPayload) ? s_addr_q + 32'd64 : s_addr_q;
|
||||
assign dma_request_bytes_o = (s_state_q == RequestPayload) ? PayloadBytes : 32'd64;
|
||||
assign dma_ready_o = ((s_state_q == ReceiveHeader) || (s_state_q == ReceivePayload)) &&
|
||||
((s_state_q == ReceiveHeader) || store_ready_i);
|
||||
|
||||
assign store_active_o = s_state_q inside {ReceivePayload, WaitPayload, ReadbackRequest, Validate};
|
||||
assign store_req_o = ((s_state_q == ReceivePayload) && s_data_accept) ||
|
||||
((s_state_q == ReadbackRequest) && (s_readback_word_q < 14'd15360));
|
||||
assign store_write_o = s_state_q == ReceivePayload;
|
||||
assign store_addr_o = (s_state_q == ReceivePayload) ?
|
||||
14'((s_received_q - 32'd64) >> 2) : s_readback_word_q;
|
||||
assign store_data_o = dma_data_i;
|
||||
|
||||
assign busy_o = s_state_q != Idle;
|
||||
assign valid_o = s_valid_q;
|
||||
assign lock_o = s_lock_q;
|
||||
assign actual_crc_o = s_actual_crc_q;
|
||||
assign coefficient_id_o = s_coefficient_id_q;
|
||||
assign status_o = {20'd0, s_stat_q[11:8], 5'd0, s_lock_q, s_valid_q, busy_o};
|
||||
assign error_code_o = s_err_code_q;
|
||||
assign error_stage_o = s_err_stage_q;
|
||||
assign error_resp_o = s_err_resp_q;
|
||||
assign error_address_o = s_err_addr_q;
|
||||
assign error_detail_o = s_err_detail_q;
|
||||
assign done_o = s_done_q;
|
||||
assign abort_done_o = s_abort_done_q;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_header_q <= '0;
|
||||
s_addr_q <= 32'd0;
|
||||
s_size_q <= 32'd0;
|
||||
s_expected_crc_q <= 32'd0;
|
||||
s_acl_base_q <= 32'd0;
|
||||
s_acl_limit_q <= 32'd0;
|
||||
s_received_q <= 32'd0;
|
||||
s_crc_q <= 32'hffff_ffff;
|
||||
s_readback_crc_q <= 32'hffff_ffff;
|
||||
s_readback_word_q <= 14'd0;
|
||||
s_readback_received_q <= 14'd0;
|
||||
s_transfer_err_q <= 1'b0;
|
||||
s_padding_err_q <= 1'b0;
|
||||
s_store_pending_q <= 1'b0;
|
||||
s_padding_addr_q <= 32'd0;
|
||||
s_valid_q <= 1'b0;
|
||||
s_lock_q <= 1'b0;
|
||||
s_done_q <= 1'b0;
|
||||
s_abort_done_q <= 1'b0;
|
||||
s_abort_pending_q <= 1'b0;
|
||||
s_actual_crc_q <= 32'd0;
|
||||
s_stat_q <= 32'd0;
|
||||
s_coefficient_id_q <= 64'd0;
|
||||
s_err_code_q <= 6'd0;
|
||||
s_err_stage_q <= 4'd0;
|
||||
s_err_resp_q <= 2'd0;
|
||||
s_err_addr_q <= 32'd0;
|
||||
s_err_detail_q <= 32'd0;
|
||||
end else begin
|
||||
s_done_q <= 1'b0;
|
||||
s_abort_done_q <= 1'b0;
|
||||
if (store_valid_i) s_store_pending_q <= 1'b0;
|
||||
|
||||
if ((soft_reset_i || resource_reset_i) && (s_state_q == Idle)) begin
|
||||
if (!s_lock_q) begin
|
||||
s_stat_q <= 32'd0;
|
||||
s_actual_crc_q <= 32'd0;
|
||||
s_coefficient_id_q <= 64'd0;
|
||||
end
|
||||
end
|
||||
|
||||
unique case (s_state_q)
|
||||
Idle: begin
|
||||
if (start_i && s_admit_ok) begin
|
||||
s_state_q <= RequestHeader;
|
||||
s_header_q <= '0;
|
||||
s_addr_q <= address_i;
|
||||
s_size_q <= size_i;
|
||||
s_expected_crc_q <= expected_crc_i;
|
||||
s_acl_base_q <= acl_base_i;
|
||||
s_acl_limit_q <= acl_limit_i;
|
||||
s_received_q <= 32'd0;
|
||||
s_crc_q <= 32'hffff_ffff;
|
||||
s_readback_crc_q <= 32'hffff_ffff;
|
||||
s_readback_word_q <= 14'd0;
|
||||
s_readback_received_q <= 14'd0;
|
||||
s_transfer_err_q <= 1'b0;
|
||||
s_padding_err_q <= 1'b0;
|
||||
s_store_pending_q <= 1'b0;
|
||||
s_stat_q <= 32'd0;
|
||||
s_actual_crc_q <= 32'd0;
|
||||
s_coefficient_id_q <= 64'd0;
|
||||
s_err_code_q <= 6'd0;
|
||||
s_err_stage_q <= 4'd0;
|
||||
s_err_resp_q <= 2'd0;
|
||||
s_err_addr_q <= 32'd0;
|
||||
s_err_detail_q <= 32'd0;
|
||||
end
|
||||
end
|
||||
RequestHeader: begin
|
||||
if (abort_i || resource_reset_request_i || resource_reset_i || soft_reset_i) begin
|
||||
s_abort_done_q <= abort_i && !resource_reset_request_i && !resource_reset_i;
|
||||
s_state_q <= Idle;
|
||||
end else if (dma_request_ready_i) begin
|
||||
s_received_q <= 32'd0;
|
||||
s_state_q <= ReceiveHeader;
|
||||
end
|
||||
end
|
||||
ReceiveHeader: begin
|
||||
if (abort_i || resource_reset_request_i || resource_reset_i || soft_reset_i) begin
|
||||
s_abort_pending_q <= abort_i && !resource_reset_request_i && !resource_reset_i;
|
||||
s_state_q <= CancelWait;
|
||||
end else begin
|
||||
if (s_data_accept) begin
|
||||
if ((dma_keep_i != 4'hf) || (s_received_q >= 32'd64) ||
|
||||
(dma_last_i != (s_received_q == 32'd60))) begin
|
||||
s_transfer_err_q <= 1'b1;
|
||||
end else begin
|
||||
s_header_q[s_received_q[5:2]] <= dma_data_i;
|
||||
end
|
||||
s_received_q <= s_received_q + 32'd4;
|
||||
end
|
||||
if (dma_error_i) begin
|
||||
s_err_code_q <= dma_error_code_i;
|
||||
s_err_stage_q <= dma_error_stage_i;
|
||||
s_err_resp_q <= dma_error_resp_i;
|
||||
s_err_addr_q <= dma_error_address_i;
|
||||
s_state_q <= Complete;
|
||||
end else if (dma_done_i) begin
|
||||
s_state_q <= WaitHeader;
|
||||
end
|
||||
end
|
||||
end
|
||||
WaitHeader: begin
|
||||
if (s_transfer_err_q || (s_received_q != 32'd64)) begin
|
||||
s_err_code_q <= `APB4_APU__ERROR_CODE_AXI_READ;
|
||||
s_err_stage_q <= `APB4_APU__ERROR_STAGE_DMA_READ;
|
||||
s_err_addr_q <= s_addr_q + s_received_q;
|
||||
s_state_q <= Complete;
|
||||
end else begin
|
||||
s_state_q <= CheckHeader;
|
||||
end
|
||||
end
|
||||
CheckHeader: begin
|
||||
if (s_header_size_err) begin
|
||||
s_stat_q[8] <= 1'b1;
|
||||
s_err_code_q <= `APB4_APU__ERROR_CODE_KWS_MODEL;
|
||||
s_err_stage_q <= `APB4_APU__ERROR_STAGE_LOADER;
|
||||
s_err_addr_q <= s_header_err_addr;
|
||||
s_err_detail_q <= 32'h0900_0001;
|
||||
s_state_q <= Complete;
|
||||
end else if (s_header_err) begin
|
||||
s_stat_q[9] <= 1'b1;
|
||||
s_err_code_q <= `APB4_APU__ERROR_CODE_KWS_MODEL;
|
||||
s_err_stage_q <= `APB4_APU__ERROR_STAGE_LOADER;
|
||||
s_err_addr_q <= s_header_err_addr;
|
||||
s_err_detail_q <= 32'h0900_0002;
|
||||
s_state_q <= Complete;
|
||||
end else begin
|
||||
s_received_q <= 32'd64;
|
||||
s_crc_q <= 32'hffff_ffff;
|
||||
s_transfer_err_q <= 1'b0;
|
||||
s_state_q <= RequestPayload;
|
||||
end
|
||||
end
|
||||
RequestPayload: begin
|
||||
if (abort_i || resource_reset_request_i || resource_reset_i || soft_reset_i) begin
|
||||
s_abort_done_q <= abort_i && !resource_reset_request_i && !resource_reset_i;
|
||||
s_state_q <= Idle;
|
||||
end else if (dma_request_ready_i) begin
|
||||
s_state_q <= ReceivePayload;
|
||||
end
|
||||
end
|
||||
ReceivePayload: begin
|
||||
if (abort_i || resource_reset_request_i || resource_reset_i || soft_reset_i) begin
|
||||
s_abort_pending_q <= abort_i && !resource_reset_request_i && !resource_reset_i;
|
||||
s_state_q <= CancelWait;
|
||||
end else begin
|
||||
if (s_data_accept) begin
|
||||
if ((dma_keep_i != 4'hf) || (s_received_q >= ImageBytes) ||
|
||||
(dma_last_i != (s_received_q == (ImageBytes - 32'd4)))) begin
|
||||
s_transfer_err_q <= 1'b1;
|
||||
end else begin
|
||||
s_crc_q <= s_crc_after_word;
|
||||
s_store_pending_q <= 1'b1;
|
||||
if (payload_word_is_padding(
|
||||
14'((s_received_q - 32'd64) >> 2)
|
||||
) && (dma_data_i != 32'd0) && !s_padding_err_q) begin
|
||||
s_padding_err_q <= 1'b1;
|
||||
s_padding_addr_q <= s_addr_q + s_received_q;
|
||||
end
|
||||
end
|
||||
s_received_q <= s_received_q + 32'd4;
|
||||
end
|
||||
if (dma_error_i) begin
|
||||
s_err_code_q <= dma_error_code_i;
|
||||
s_err_stage_q <= dma_error_stage_i;
|
||||
s_err_resp_q <= dma_error_resp_i;
|
||||
s_err_addr_q <= dma_error_address_i;
|
||||
s_state_q <= Complete;
|
||||
end else if (dma_done_i) begin
|
||||
s_state_q <= WaitPayload;
|
||||
end
|
||||
end
|
||||
end
|
||||
WaitPayload: begin
|
||||
if (!s_store_pending_q) begin
|
||||
if (s_transfer_err_q || (s_received_q != ImageBytes) || store_fault_i) begin
|
||||
s_err_code_q <= `APB4_APU__ERROR_CODE_AXI_READ;
|
||||
s_err_stage_q <= `APB4_APU__ERROR_STAGE_DMA_READ;
|
||||
s_err_addr_q <= s_addr_q + s_received_q;
|
||||
s_state_q <= Complete;
|
||||
end else if (s_padding_err_q) begin
|
||||
s_stat_q[11] <= 1'b1;
|
||||
s_err_code_q <= `APB4_APU__ERROR_CODE_KWS_MODEL;
|
||||
s_err_stage_q <= `APB4_APU__ERROR_STAGE_LOADER;
|
||||
s_err_addr_q <= s_padding_addr_q;
|
||||
s_err_detail_q <= 32'h0900_0004;
|
||||
s_state_q <= Complete;
|
||||
end else if ((~s_crc_q != PayloadCrc) || (s_header_q[9] != ~s_crc_q) ||
|
||||
(s_expected_crc_q != ~s_crc_q)) begin
|
||||
s_stat_q[10] <= 1'b1;
|
||||
s_err_code_q <= `APB4_APU__ERROR_CODE_KWS_CRC;
|
||||
s_err_stage_q <= `APB4_APU__ERROR_STAGE_LOADER;
|
||||
s_err_addr_q <= s_addr_q + 32'h24;
|
||||
s_err_detail_q <= 32'h0900_0003;
|
||||
s_state_q <= Complete;
|
||||
end else begin
|
||||
s_readback_crc_q <= 32'hffff_ffff;
|
||||
s_readback_word_q <= 14'd0;
|
||||
s_readback_received_q <= 14'd0;
|
||||
s_state_q <= ReadbackRequest;
|
||||
end
|
||||
end
|
||||
end
|
||||
ReadbackRequest: begin
|
||||
if (abort_i || resource_reset_request_i || resource_reset_i || soft_reset_i) begin
|
||||
s_abort_done_q <= abort_i && !resource_reset_request_i && !resource_reset_i;
|
||||
s_state_q <= Idle;
|
||||
end else begin
|
||||
if (store_req_o && store_ready_i) begin
|
||||
s_readback_word_q <= s_readback_word_q + 1'b1;
|
||||
end
|
||||
if (store_valid_i) begin
|
||||
if (store_fault_i) begin
|
||||
s_stat_q[10] <= 1'b1;
|
||||
s_err_code_q <= `APB4_APU__ERROR_CODE_KWS_CRC;
|
||||
s_err_stage_q <= `APB4_APU__ERROR_STAGE_LOADER;
|
||||
s_err_addr_q <= s_addr_q + 32'h24;
|
||||
s_err_detail_q <= 32'h0900_0005;
|
||||
s_state_q <= Complete;
|
||||
end else begin
|
||||
s_readback_crc_q <= s_readback_crc_after_word;
|
||||
if (s_readback_received_q == 14'd15359) begin
|
||||
s_actual_crc_q <= ~s_readback_crc_after_word;
|
||||
s_state_q <= Validate;
|
||||
end else begin
|
||||
s_readback_received_q <= s_readback_received_q + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Validate: begin
|
||||
if (s_actual_crc_q != PayloadCrc) begin
|
||||
s_stat_q[10] <= 1'b1;
|
||||
s_err_code_q <= `APB4_APU__ERROR_CODE_KWS_CRC;
|
||||
s_err_stage_q <= `APB4_APU__ERROR_STAGE_LOADER;
|
||||
s_err_addr_q <= s_addr_q + 32'h24;
|
||||
s_err_detail_q <= 32'h0900_0005;
|
||||
end else begin
|
||||
s_valid_q <= 1'b1;
|
||||
s_lock_q <= 1'b1;
|
||||
s_coefficient_id_q <= {CoefficientIdHigh, CoefficientIdLow};
|
||||
end
|
||||
s_state_q <= Complete;
|
||||
end
|
||||
Complete: begin
|
||||
s_done_q <= 1'b1;
|
||||
s_state_q <= Idle;
|
||||
end
|
||||
CancelWait: begin
|
||||
if (dma_error_i || dma_done_i) begin
|
||||
s_abort_done_q <= s_abort_pending_q;
|
||||
s_abort_pending_q <= 1'b0;
|
||||
s_state_q <= Idle;
|
||||
end
|
||||
end
|
||||
default: s_state_q <= Idle;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,314 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
module apu_kws_coeff_store (
|
||||
// verilog_format: off -- preserve the four coefficient-client columns
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic abort_i,
|
||||
input logic valid_i,
|
||||
input logic loader_active_i,
|
||||
input logic loader_req_i,
|
||||
input logic loader_write_i,
|
||||
input logic [13:0] loader_addr_i,
|
||||
input logic [31:0] loader_data_i,
|
||||
output logic loader_ready_o,
|
||||
output logic loader_valid_o,
|
||||
output logic [31:0] loader_data_o,
|
||||
output logic loader_fault_o,
|
||||
input logic frontend_req_valid_i,
|
||||
output logic frontend_req_ready_o,
|
||||
input logic [ 3:0] frontend_kind_i,
|
||||
input logic [13:0] frontend_index_i,
|
||||
output logic frontend_resp_valid_o,
|
||||
input logic frontend_resp_ready_i,
|
||||
output logic [63:0] frontend_resp_data_o,
|
||||
output logic frontend_resp_fault_o,
|
||||
input logic inference_req_valid_i,
|
||||
output logic inference_req_ready_o,
|
||||
input logic [ 6:0] inference_index_i,
|
||||
output logic inference_resp_valid_o,
|
||||
input logic inference_resp_ready_i,
|
||||
output logic [31:0] inference_resp_data_o,
|
||||
output logic inference_resp_fault_o,
|
||||
input logic profile_req_valid_i,
|
||||
output logic profile_req_ready_o,
|
||||
input logic [10:0] profile_index_i,
|
||||
output logic profile_resp_valid_o,
|
||||
input logic profile_resp_ready_i,
|
||||
output logic [31:0] profile_resp_data_o,
|
||||
output logic profile_resp_fault_o
|
||||
// verilog_format: on
|
||||
);
|
||||
localparam int unsigned BankCount = 15;
|
||||
|
||||
logic [BankCount-1:0] s_bank_cs;
|
||||
logic [BankCount-1:0] s_bank_wren;
|
||||
logic [BankCount-1:0][ 9:0] s_bank_addr;
|
||||
logic [BankCount-1:0][31:0] s_bank_wdata;
|
||||
logic [BankCount-1:0][31:0] s_bank_rdata;
|
||||
|
||||
logic s_front_legal;
|
||||
logic [3:0] s_front_bank_low, s_front_bank_high;
|
||||
logic [9:0] s_front_row_low, s_front_row_high;
|
||||
logic s_front_pair;
|
||||
logic s_front_accept;
|
||||
logic s_front_pending_q;
|
||||
logic s_front_fault_q;
|
||||
logic [3:0] s_front_bank_low_q, s_front_bank_high_q;
|
||||
logic s_front_pair_q;
|
||||
|
||||
logic s_infer_legal;
|
||||
logic s_infer_accept;
|
||||
logic s_infer_pending_q;
|
||||
logic s_infer_fault_q;
|
||||
|
||||
logic s_profile_legal;
|
||||
logic [3:0] s_profile_bank;
|
||||
logic [9:0] s_profile_row;
|
||||
logic s_profile_accept;
|
||||
logic s_profile_pending_q;
|
||||
logic s_profile_fault_q;
|
||||
logic [3:0] s_profile_bank_q;
|
||||
|
||||
logic s_loader_accept;
|
||||
logic s_loader_pending_q;
|
||||
logic s_loader_fault_q;
|
||||
logic [3:0] s_loader_bank_q;
|
||||
|
||||
always_comb begin
|
||||
s_front_legal = 1'b1;
|
||||
s_front_bank_low = 4'd0;
|
||||
s_front_bank_high = 4'd0;
|
||||
s_front_row_low = 10'd0;
|
||||
s_front_row_high = 10'd0;
|
||||
s_front_pair = 1'b0;
|
||||
unique case (frontend_kind_i)
|
||||
4'd0: begin
|
||||
s_front_legal = frontend_index_i < 14'd480;
|
||||
s_front_row_low = frontend_index_i[9:0];
|
||||
end
|
||||
4'd1: begin
|
||||
s_front_legal = frontend_index_i < 14'd256;
|
||||
s_front_bank_low = 4'd1;
|
||||
s_front_bank_high = 4'd2;
|
||||
s_front_row_low = frontend_index_i[9:0];
|
||||
s_front_row_high = frontend_index_i[9:0];
|
||||
s_front_pair = 1'b1;
|
||||
end
|
||||
4'd2: begin
|
||||
s_front_legal = frontend_index_i < 14'd10280;
|
||||
if (frontend_index_i < 14'd10240) begin
|
||||
s_front_bank_low = 4'd3 + frontend_index_i[13:10];
|
||||
s_front_row_low = frontend_index_i[9:0];
|
||||
end else begin
|
||||
s_front_bank_low = 4'd13;
|
||||
s_front_row_low = 10'(frontend_index_i - 14'd10240);
|
||||
end
|
||||
end
|
||||
4'd3: begin
|
||||
s_front_legal = frontend_index_i < 14'd400;
|
||||
s_front_row_low = 10'd480 + frontend_index_i[9:0];
|
||||
end
|
||||
4'd4: begin
|
||||
s_front_legal = frontend_index_i < 14'd1024;
|
||||
s_front_pair = 1'b1;
|
||||
if (!frontend_index_i[0]) begin
|
||||
s_front_bank_low = 4'd1;
|
||||
s_front_bank_high = 4'd2;
|
||||
s_front_row_low = 10'd256 + frontend_index_i[10:1];
|
||||
s_front_row_high = 10'd256 + frontend_index_i[10:1];
|
||||
end else begin
|
||||
s_front_bank_low = 4'd2;
|
||||
s_front_bank_high = 4'd1;
|
||||
s_front_row_low = 10'd256 + frontend_index_i[10:1];
|
||||
s_front_row_high = 10'd257 + frontend_index_i[10:1];
|
||||
end
|
||||
end
|
||||
4'd5: begin
|
||||
s_front_legal = frontend_index_i < 14'd63;
|
||||
s_front_row_low = 10'd880 + frontend_index_i[9:0];
|
||||
end
|
||||
4'd6: begin
|
||||
s_front_legal = frontend_index_i < 14'd63;
|
||||
s_front_row_low = 10'd943 + frontend_index_i[9:0];
|
||||
end
|
||||
default: s_front_legal = 1'b0;
|
||||
endcase
|
||||
end
|
||||
|
||||
assign s_infer_legal = inference_index_i < 7'd125;
|
||||
always_comb begin
|
||||
s_profile_legal = profile_index_i < 11'd1496;
|
||||
if (profile_index_i < 11'd984) begin
|
||||
s_profile_bank = 4'd13;
|
||||
s_profile_row = 10'd40 + profile_index_i[9:0];
|
||||
end else begin
|
||||
s_profile_bank = 4'd14;
|
||||
s_profile_row = 10'(profile_index_i - 11'd984);
|
||||
end
|
||||
end
|
||||
|
||||
assign loader_ready_o = loader_active_i;
|
||||
assign frontend_req_ready_o = !loader_active_i && !profile_req_valid_i &&
|
||||
!s_front_pending_q && (!frontend_resp_valid_o || frontend_resp_ready_i);
|
||||
assign inference_req_ready_o = !loader_active_i && !profile_req_valid_i &&
|
||||
!s_infer_pending_q && (!inference_resp_valid_o || inference_resp_ready_i);
|
||||
assign profile_req_ready_o = !loader_active_i && !frontend_req_valid_i &&
|
||||
!inference_req_valid_i && !s_profile_pending_q &&
|
||||
(!profile_resp_valid_o || profile_resp_ready_i);
|
||||
assign s_loader_accept = loader_req_i && loader_ready_o;
|
||||
assign s_front_accept = frontend_req_valid_i && frontend_req_ready_o;
|
||||
assign s_infer_accept = inference_req_valid_i && inference_req_ready_o;
|
||||
assign s_profile_accept = profile_req_valid_i && profile_req_ready_o;
|
||||
|
||||
always_comb begin
|
||||
s_bank_cs = BankCount'(0);
|
||||
s_bank_wren = BankCount'(0);
|
||||
s_bank_addr = '0;
|
||||
s_bank_wdata = '0;
|
||||
if (s_loader_accept && (loader_addr_i < 14'd15360)) begin
|
||||
s_bank_cs[loader_addr_i[13:10]] = 1'b1;
|
||||
s_bank_wren[loader_addr_i[13:10]] = loader_write_i;
|
||||
s_bank_addr[loader_addr_i[13:10]] = loader_addr_i[9:0];
|
||||
s_bank_wdata[loader_addr_i[13:10]] = loader_data_i;
|
||||
end else begin
|
||||
if (s_profile_accept && s_profile_legal && valid_i) begin
|
||||
s_bank_cs[s_profile_bank] = 1'b1;
|
||||
s_bank_addr[s_profile_bank] = s_profile_row;
|
||||
end else begin
|
||||
if (s_front_accept && s_front_legal && valid_i) begin
|
||||
s_bank_cs[s_front_bank_low] = 1'b1;
|
||||
s_bank_addr[s_front_bank_low] = s_front_row_low;
|
||||
if (s_front_pair) begin
|
||||
s_bank_cs[s_front_bank_high] = 1'b1;
|
||||
s_bank_addr[s_front_bank_high] = s_front_row_high;
|
||||
end
|
||||
end
|
||||
if (s_infer_accept && s_infer_legal && valid_i) begin
|
||||
s_bank_cs[14] = 1'b1;
|
||||
s_bank_addr[14] = 10'd512 + {3'd0, inference_index_i};
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for (genvar bank = 0; bank < BankCount; bank++) begin : gen_coefficient_bank
|
||||
`ifdef HAVE_SRAM_MACRO
|
||||
tc_sram_1024x32 u_coefficient_sram (
|
||||
.clk_i (clk_i),
|
||||
.cs_i (s_bank_cs[bank]),
|
||||
.addr_i(s_bank_addr[bank]),
|
||||
.data_i(s_bank_wdata[bank]),
|
||||
.mask_i(4'hf),
|
||||
.wren_i(s_bank_wren[bank]),
|
||||
.data_o(s_bank_rdata[bank])
|
||||
);
|
||||
`else
|
||||
logic [31:0] s_storage_q[0:1023];
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (s_bank_cs[bank]) begin
|
||||
if (s_bank_wren[bank]) begin
|
||||
s_storage_q[s_bank_addr[bank]] <= s_bank_wdata[bank];
|
||||
end else begin
|
||||
s_bank_rdata[bank] <= s_storage_q[s_bank_addr[bank]];
|
||||
end
|
||||
end
|
||||
end
|
||||
`endif
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_front_pending_q <= 1'b0;
|
||||
s_front_fault_q <= 1'b0;
|
||||
s_front_bank_low_q <= 4'd0;
|
||||
s_front_bank_high_q <= 4'd0;
|
||||
s_front_pair_q <= 1'b0;
|
||||
frontend_resp_valid_o <= 1'b0;
|
||||
frontend_resp_data_o <= 64'd0;
|
||||
frontend_resp_fault_o <= 1'b0;
|
||||
s_infer_pending_q <= 1'b0;
|
||||
s_infer_fault_q <= 1'b0;
|
||||
inference_resp_valid_o <= 1'b0;
|
||||
inference_resp_data_o <= 32'd0;
|
||||
inference_resp_fault_o <= 1'b0;
|
||||
s_profile_pending_q <= 1'b0;
|
||||
s_profile_fault_q <= 1'b0;
|
||||
s_profile_bank_q <= 4'd0;
|
||||
profile_resp_valid_o <= 1'b0;
|
||||
profile_resp_data_o <= 32'd0;
|
||||
profile_resp_fault_o <= 1'b0;
|
||||
s_loader_pending_q <= 1'b0;
|
||||
s_loader_fault_q <= 1'b0;
|
||||
s_loader_bank_q <= 4'd0;
|
||||
loader_valid_o <= 1'b0;
|
||||
loader_data_o <= 32'd0;
|
||||
loader_fault_o <= 1'b0;
|
||||
end else if (abort_i) begin
|
||||
s_front_pending_q <= 1'b0;
|
||||
frontend_resp_valid_o <= 1'b0;
|
||||
s_infer_pending_q <= 1'b0;
|
||||
inference_resp_valid_o <= 1'b0;
|
||||
s_profile_pending_q <= 1'b0;
|
||||
profile_resp_valid_o <= 1'b0;
|
||||
s_loader_pending_q <= 1'b0;
|
||||
loader_valid_o <= 1'b0;
|
||||
end else begin
|
||||
if (frontend_resp_valid_o && frontend_resp_ready_i) frontend_resp_valid_o <= 1'b0;
|
||||
if (inference_resp_valid_o && inference_resp_ready_i) inference_resp_valid_o <= 1'b0;
|
||||
if (profile_resp_valid_o && profile_resp_ready_i) profile_resp_valid_o <= 1'b0;
|
||||
loader_valid_o <= s_loader_pending_q || (s_loader_accept && loader_write_i);
|
||||
if (s_loader_pending_q) begin
|
||||
loader_fault_o <= s_loader_fault_q;
|
||||
loader_data_o <= s_loader_fault_q ? 32'd0 : s_bank_rdata[s_loader_bank_q];
|
||||
end else if (s_loader_accept && loader_write_i) begin
|
||||
loader_fault_o <= loader_addr_i >= 14'd15360;
|
||||
end
|
||||
|
||||
if (s_front_accept) begin
|
||||
s_front_pending_q <= 1'b1;
|
||||
s_front_fault_q <= !s_front_legal || !valid_i;
|
||||
s_front_bank_low_q <= s_front_bank_low;
|
||||
s_front_bank_high_q <= s_front_bank_high;
|
||||
s_front_pair_q <= s_front_pair;
|
||||
end else if (s_front_pending_q) begin
|
||||
s_front_pending_q <= 1'b0;
|
||||
frontend_resp_valid_o <= 1'b1;
|
||||
frontend_resp_fault_o <= s_front_fault_q;
|
||||
frontend_resp_data_o <= s_front_fault_q ? 64'd0 :
|
||||
{s_front_pair_q ? s_bank_rdata[s_front_bank_high_q] : 32'd0,
|
||||
s_bank_rdata[s_front_bank_low_q]};
|
||||
end
|
||||
|
||||
if (s_infer_accept) begin
|
||||
s_infer_pending_q <= 1'b1;
|
||||
s_infer_fault_q <= !s_infer_legal || !valid_i;
|
||||
end else if (s_infer_pending_q) begin
|
||||
s_infer_pending_q <= 1'b0;
|
||||
inference_resp_valid_o <= 1'b1;
|
||||
inference_resp_fault_o <= s_infer_fault_q;
|
||||
inference_resp_data_o <= s_infer_fault_q ? 32'd0 : s_bank_rdata[14];
|
||||
end
|
||||
|
||||
if (s_profile_accept) begin
|
||||
s_profile_pending_q <= 1'b1;
|
||||
s_profile_fault_q <= !s_profile_legal || !valid_i;
|
||||
s_profile_bank_q <= s_profile_bank;
|
||||
end else if (s_profile_pending_q) begin
|
||||
s_profile_pending_q <= 1'b0;
|
||||
profile_resp_valid_o <= 1'b1;
|
||||
profile_resp_fault_o <= s_profile_fault_q;
|
||||
profile_resp_data_o <= s_profile_fault_q ? 32'd0 : s_bank_rdata[s_profile_bank_q];
|
||||
end
|
||||
|
||||
if (s_loader_accept) begin
|
||||
s_loader_fault_q <= loader_addr_i >= 14'd15360;
|
||||
s_loader_bank_q <= loader_addr_i[13:10];
|
||||
s_loader_pending_q <= !loader_write_i;
|
||||
end else begin
|
||||
s_loader_pending_q <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
@@ -66,6 +66,21 @@ module apu_kws_engine (
|
||||
output logic [ 5:0][31:0] scratch_write_data_o,
|
||||
output logic [ 5:0][ 3:0] scratch_write_strb_o,
|
||||
input logic scratch_access_err_i,
|
||||
output logic coeff_frontend_req_valid_o,
|
||||
input logic coeff_frontend_req_ready_i,
|
||||
output logic [ 3:0] coeff_frontend_kind_o,
|
||||
output logic [13:0] coeff_frontend_index_o,
|
||||
input logic coeff_frontend_resp_valid_i,
|
||||
output logic coeff_frontend_resp_ready_o,
|
||||
input logic [63:0] coeff_frontend_data_i,
|
||||
input logic coeff_frontend_fault_i,
|
||||
output logic coeff_inference_req_valid_o,
|
||||
input logic coeff_inference_req_ready_i,
|
||||
output logic [ 6:0] coeff_inference_index_o,
|
||||
input logic coeff_inference_resp_valid_i,
|
||||
output logic coeff_inference_resp_ready_o,
|
||||
input logic [31:0] coeff_inference_data_i,
|
||||
input logic coeff_inference_fault_i,
|
||||
axi4_stream_if.sink stream_i,
|
||||
output logic rx_ready_o,
|
||||
output logic [31:0] status_o,
|
||||
@@ -92,11 +107,10 @@ module apu_kws_engine (
|
||||
output logic idle_o
|
||||
// verilog_format: on
|
||||
);
|
||||
`include "apu_kws_rom.svh"
|
||||
|
||||
localparam logic [15:0] ScratchFftRealBase = `RETROSOC_APU_KWS__SCRATCH_FFT_REAL_BASE;
|
||||
localparam logic [15:0] ScratchFftImagBase = `RETROSOC_APU_KWS__SCRATCH_FFT_IMAG_BASE;
|
||||
localparam logic [15:0] ScratchSoftmaxBase = `RETROSOC_APU_KWS__SCRATCH_SOFTMAX_BASE;
|
||||
localparam logic signed [31:0] ApuKwsLn2Q24 = 32'sd11629080;
|
||||
|
||||
typedef enum logic [3:0] {
|
||||
FrontIdle,
|
||||
@@ -122,6 +136,12 @@ module apu_kws_engine (
|
||||
InferComplete
|
||||
} infer_state_e;
|
||||
|
||||
typedef struct packed {
|
||||
logic signed [7:0] exponent;
|
||||
logic [10:0] index;
|
||||
logic [16:0] fraction;
|
||||
} log_lookup_t;
|
||||
|
||||
front_state_e s_front_state_q;
|
||||
infer_state_e s_infer_state_q;
|
||||
|
||||
@@ -182,9 +202,15 @@ module apu_kws_engine (
|
||||
logic [31:0] s_fault_addr_q, s_fault_detail_q;
|
||||
logic [31:0] s_front_watchdog_q, s_infer_watchdog_q;
|
||||
logic s_front_progress, s_infer_progress;
|
||||
logic s_storage_wait_q, s_storage_write_phase_q, s_storage_state_owned_q;
|
||||
logic s_storage_wait_q, s_storage_complete_q, s_storage_write_phase_q;
|
||||
logic s_storage_state_owned_q;
|
||||
logic s_storage_read_access, s_storage_write_access, s_storage_has_access;
|
||||
logic s_storage_final_done, s_storage_state_step;
|
||||
logic s_front_coeff_needed, s_infer_coeff_needed, s_coeff_progress, s_coeff_operand_ready;
|
||||
logic s_front_coeff_pending_q, s_front_coeff_have_q, s_front_coeff_fault_q;
|
||||
logic [63:0] s_front_coeff_data_q;
|
||||
logic s_infer_coeff_pending_q, s_infer_coeff_have_q, s_infer_coeff_fault_q;
|
||||
logic [31:0] s_infer_coeff_data_q;
|
||||
|
||||
logic s_config_valid, s_continuous_active, s_listening, s_capture_available;
|
||||
logic s_stream_accept, s_stream_legal, s_window_deadline, s_mel_overwrite;
|
||||
@@ -248,6 +274,7 @@ module apu_kws_engine (
|
||||
logic signed [31:0] s_fft_left_real_data, s_fft_left_imag_data;
|
||||
logic signed [31:0] s_fft_right_real_data, s_fft_right_imag_data;
|
||||
logic signed [31:0] s_log_data;
|
||||
log_lookup_t s_log_lookup;
|
||||
logic [ 7:0] s_dct_output_value;
|
||||
logic signed [7:0] s_logit_data, s_softmax_data[0:11];
|
||||
logic s_fft_overflow;
|
||||
@@ -449,25 +476,22 @@ module apu_kws_engine (
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic signed [31:0] log_mel_q24(input logic [31:0] mel_i,
|
||||
function automatic log_lookup_t log_mel_lookup(input logic [31:0] mel_i,
|
||||
input logic [15:0] peak_i);
|
||||
logic [95:0] numerator, denominator, normalized_denominator;
|
||||
logic [95:0] scaled, remainder;
|
||||
logic signed [ 7:0] exponent;
|
||||
logic [10:0] index;
|
||||
logic [16:0] fraction;
|
||||
log_lookup_t s_lookup;
|
||||
logic [95:0] fraction_scaled, fraction_quotient, fraction_remainder;
|
||||
logic signed [63:0] interpolation;
|
||||
begin
|
||||
numerator = 96'd1000000 * mel_i + 96'd16 * peak_i;
|
||||
denominator = 96'd16000000 * peak_i;
|
||||
exponent = 0;
|
||||
s_lookup.exponent = 0;
|
||||
if (numerator >= denominator) begin
|
||||
normalized_denominator = denominator;
|
||||
for (int iteration = 0; iteration < 95; iteration++) begin
|
||||
if (numerator >= (normalized_denominator << 1)) begin
|
||||
normalized_denominator = normalized_denominator << 1;
|
||||
exponent = exponent + 1'b1;
|
||||
s_lookup.exponent = s_lookup.exponent + 1'b1;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
@@ -475,13 +499,13 @@ module apu_kws_engine (
|
||||
for (int iteration = 0; iteration < 95; iteration++) begin
|
||||
if (numerator < normalized_denominator) begin
|
||||
numerator = numerator << 1;
|
||||
exponent = exponent - 1'b1;
|
||||
s_lookup.exponent = s_lookup.exponent - 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
scaled = (numerator - normalized_denominator) * 96'd1024;
|
||||
index = 11'(scaled / normalized_denominator);
|
||||
remainder = scaled - index * normalized_denominator;
|
||||
s_lookup.index = 11'(scaled / normalized_denominator);
|
||||
remainder = scaled - s_lookup.index * normalized_denominator;
|
||||
fraction_scaled = remainder * 96'd65536;
|
||||
fraction_quotient = fraction_scaled / normalized_denominator;
|
||||
fraction_remainder = fraction_scaled - fraction_quotient * normalized_denominator;
|
||||
@@ -489,15 +513,22 @@ module apu_kws_engine (
|
||||
((fraction_remainder * 2 == normalized_denominator) && fraction_quotient[0])) begin
|
||||
fraction_quotient = fraction_quotient + 1'b1;
|
||||
end
|
||||
fraction = fraction_quotient[16:0];
|
||||
interpolation = 64'($signed(apu_kws_log_q24(index + 11'd1))) -
|
||||
64'($signed(apu_kws_log_q24(index)));
|
||||
interpolation = rne_shift(interpolation * $signed({1'b0, fraction}), 16);
|
||||
s_lookup.fraction = fraction_quotient[16:0];
|
||||
return s_lookup;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic signed [31:0] log_mel_result(input log_lookup_t lookup_i,
|
||||
input logic [63:0] values_i);
|
||||
logic signed [63:0] s_interpolation;
|
||||
begin
|
||||
s_interpolation = 64'($signed(values_i[63:32])) - 64'($signed(values_i[31:0]));
|
||||
s_interpolation = rne_shift(s_interpolation * $signed({1'b0, lookup_i.fraction}), 6'd16);
|
||||
return 32'(64'($signed(
|
||||
exponent
|
||||
lookup_i.exponent
|
||||
)) * 64'(ApuKwsLn2Q24) + 64'($signed(
|
||||
apu_kws_log_q24(index)
|
||||
)) + interpolation);
|
||||
values_i[31:0]
|
||||
)) + s_interpolation);
|
||||
end
|
||||
endfunction
|
||||
|
||||
@@ -642,7 +673,7 @@ module apu_kws_engine (
|
||||
assign s_fir_term = $signed(
|
||||
(s_fir_tap_q == 0) ? s_fir_sample_q : s_fir_history_data
|
||||
) * $signed(
|
||||
(s_decimation == 3'd3) ? apu_kws_fir3_q30(s_fir_tap_q) : apu_kws_fir6_q30(s_fir_tap_q)
|
||||
s_front_coeff_data_q[31:0]
|
||||
);
|
||||
assign s_fir_total = s_fir_acc_q + s_fir_term;
|
||||
assign s_fir_output = saturate_s16(rne_shift(s_fir_total, 30));
|
||||
@@ -680,25 +711,25 @@ module apu_kws_engine (
|
||||
assign s_windowed = (s_front_index_q < 10'd480) ? 64'($signed(
|
||||
s_ingress_data
|
||||
)) * 64'($signed(
|
||||
apu_kws_hann_q30(s_front_index_q[8:0])
|
||||
s_front_coeff_data_q[31:0]
|
||||
)) : 64'sd0;
|
||||
assign s_fft_product_real = $signed(
|
||||
s_fft_right_real_data
|
||||
) * $signed(
|
||||
apu_kws_twiddle_real_q30(s_twiddle_index)
|
||||
s_front_coeff_data_q[31:0]
|
||||
) - $signed(
|
||||
s_fft_right_imag_data
|
||||
) * $signed(
|
||||
apu_kws_twiddle_imag_q30(s_twiddle_index)
|
||||
s_front_coeff_data_q[63:32]
|
||||
);
|
||||
assign s_fft_product_imag = $signed(
|
||||
s_fft_right_real_data
|
||||
) * $signed(
|
||||
apu_kws_twiddle_imag_q30(s_twiddle_index)
|
||||
s_front_coeff_data_q[63:32]
|
||||
) + $signed(
|
||||
s_fft_right_imag_data
|
||||
) * $signed(
|
||||
apu_kws_twiddle_real_q30(s_twiddle_index)
|
||||
s_front_coeff_data_q[31:0]
|
||||
);
|
||||
assign s_fft_twiddled_real = rne_shift_s32(s_fft_product_real, 6'd30);
|
||||
assign s_fft_twiddled_imag = rne_shift_s32(s_fft_product_imag, 6'd30);
|
||||
@@ -721,15 +752,9 @@ module apu_kws_engine (
|
||||
) + $unsigned(
|
||||
64'($signed(s_fft_left_imag_data)) * 64'($signed(s_fft_left_imag_data))
|
||||
);
|
||||
assign s_mel_product = s_magnitude_data * apu_kws_mel_q30(
|
||||
14'(s_mel_band_q) * 14'd257 + 14'(s_mel_bin_q)
|
||||
);
|
||||
assign s_mel_product = s_magnitude_data * s_front_coeff_data_q[31:0];
|
||||
assign s_mel_total = s_mel_acc_q + s_mel_product;
|
||||
assign s_dct_product = $signed(
|
||||
s_log_data
|
||||
) * $signed(
|
||||
apu_kws_dct_q30(9'(s_mfcc_coefficient_q) * 9'd40 + 9'(s_mfcc_band_q))
|
||||
);
|
||||
assign s_dct_product = $signed(s_log_data) * $signed(s_front_coeff_data_q[31:0]);
|
||||
assign s_dct_total = s_dct_acc_q + s_dct_product;
|
||||
assign s_log_row_sum = {1'b0, s_snapshot_start_q} + {1'b0, s_mfcc_row_q};
|
||||
assign s_log_row_index = (s_log_row_sum >= 7'd50) ? 6'(s_log_row_sum - 7'd50) : 6'(s_log_row_sum);
|
||||
@@ -978,12 +1003,76 @@ module apu_kws_engine (
|
||||
end
|
||||
|
||||
assign s_logit_data = scratch_s8(scratch_read_data_i[4], scratch_read_addr_o[4][1:0]);
|
||||
assign s_log_lookup = log_mel_lookup(s_mel_data, s_window_peak_q);
|
||||
for (genvar class_index = 0; class_index < 12; class_index++) begin : gen_softmax_read
|
||||
assign s_softmax_data[class_index] = scratch_s8(
|
||||
scratch_read_data_i[class_index+4], scratch_read_addr_o[class_index+4][1:0]
|
||||
);
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
s_front_coeff_needed = 1'b0;
|
||||
coeff_frontend_kind_o = 4'd0;
|
||||
coeff_frontend_index_o = 14'd0;
|
||||
unique case (s_front_state_q)
|
||||
FrontFir: begin
|
||||
s_front_coeff_needed = 1'b1;
|
||||
coeff_frontend_kind_o = (s_decimation == 3'd3) ? 4'd5 : 4'd6;
|
||||
coeff_frontend_index_o = {8'd0, s_fir_tap_q};
|
||||
end
|
||||
FrontFftLoad: begin
|
||||
s_front_coeff_needed = s_front_index_q < 10'd480;
|
||||
coeff_frontend_kind_o = 4'd0;
|
||||
coeff_frontend_index_o = {5'd0, s_front_index_q[8:0]};
|
||||
end
|
||||
FrontFftRun: begin
|
||||
s_front_coeff_needed = 1'b1;
|
||||
coeff_frontend_kind_o = 4'd1;
|
||||
coeff_frontend_index_o = {6'd0, s_twiddle_index};
|
||||
end
|
||||
FrontMel: begin
|
||||
s_front_coeff_needed = 1'b1;
|
||||
coeff_frontend_kind_o = 4'd2;
|
||||
coeff_frontend_index_o = 14'(s_mel_band_q) * 14'd257 + 14'(s_mel_bin_q);
|
||||
end
|
||||
FrontLog: begin
|
||||
s_front_coeff_needed = 1'b1;
|
||||
coeff_frontend_kind_o = 4'd4;
|
||||
coeff_frontend_index_o = {3'd0, s_log_lookup.index};
|
||||
end
|
||||
FrontDct: begin
|
||||
s_front_coeff_needed = 1'b1;
|
||||
coeff_frontend_kind_o = 4'd3;
|
||||
coeff_frontend_index_o = 14'(s_mfcc_coefficient_q) * 14'd40 + 14'(s_mfcc_band_q);
|
||||
end
|
||||
default: begin
|
||||
end
|
||||
endcase
|
||||
|
||||
s_infer_coeff_needed = 1'b0;
|
||||
coeff_inference_index_o = 7'd0;
|
||||
if (s_infer_state_q == InferSoftmaxSum) begin
|
||||
s_infer_coeff_needed = !s_coeff_operand_ready || (s_softmax_sum_diff >= -9'sd124);
|
||||
coeff_inference_index_o = 7'(-s_softmax_sum_diff);
|
||||
end else if ((s_infer_state_q == InferSoftmaxScale) && (s_softmax_index_q != 4'd0)) begin
|
||||
s_infer_coeff_needed = !s_coeff_operand_ready || (s_softmax_scale_diff >= -9'sd124);
|
||||
coeff_inference_index_o = 7'(-s_softmax_scale_diff);
|
||||
end
|
||||
end
|
||||
|
||||
assign s_coeff_operand_ready = s_storage_write_phase_q || s_storage_complete_q;
|
||||
assign coeff_frontend_req_valid_o = s_front_coeff_needed && !s_front_coeff_pending_q &&
|
||||
!s_front_coeff_have_q && ((s_front_state_q != FrontLog) || s_coeff_operand_ready);
|
||||
assign coeff_frontend_resp_ready_o = s_front_coeff_pending_q;
|
||||
assign coeff_inference_req_valid_o = s_infer_coeff_needed && !s_infer_coeff_pending_q &&
|
||||
!s_infer_coeff_have_q && s_coeff_operand_ready;
|
||||
assign coeff_inference_resp_ready_o = s_infer_coeff_pending_q;
|
||||
assign s_coeff_progress =
|
||||
(coeff_frontend_req_valid_o && coeff_frontend_req_ready_i) ||
|
||||
(coeff_frontend_resp_valid_i && coeff_frontend_resp_ready_o) ||
|
||||
(coeff_inference_req_valid_o && coeff_inference_req_ready_i) ||
|
||||
(coeff_inference_resp_valid_i && coeff_inference_resp_ready_o);
|
||||
|
||||
always_comb begin
|
||||
s_softmax_sum_diff = 9'($signed(s_logit_data)) - 9'($signed(s_softmax_max_q));
|
||||
s_softmax_scale_diff = 9'sd0;
|
||||
@@ -992,8 +1081,8 @@ module apu_kws_engine (
|
||||
end
|
||||
s_softmax_headroom = count_leading_zero(s_softmax_sum_q);
|
||||
s_softmax_shifted_sum = 32'($unsigned(s_softmax_sum_q) << s_softmax_headroom) - 32'h8000_0000;
|
||||
s_softmax_exp_value = (s_softmax_scale_diff >= -9'sd124) ?
|
||||
apu_kws_softmax_exp_q31(-s_softmax_scale_diff[6:0]) : 32'sd0;
|
||||
s_softmax_exp_value = (s_softmax_scale_diff >= -9'sd124) ? $signed(s_infer_coeff_data_q) :
|
||||
32'sd0;
|
||||
s_softmax_prob = rounding_divide_pot(
|
||||
saturating_high_mul(s_softmax_scale_q, s_softmax_exp_value), s_softmax_bits_q + 6'd23);
|
||||
if (s_softmax_prob > 32'sd255) s_softmax_prob = 32'sd255;
|
||||
@@ -1117,7 +1206,7 @@ module apu_kws_engine (
|
||||
scratch_write_valid_o[0] = 1'b1;
|
||||
scratch_write_addr_o[0] =
|
||||
`RETROSOC_APU_KWS__SCRATCH_FFT_IMAG_BASE + 16'(s_mfcc_band_q * 4);
|
||||
scratch_write_data_o[0] = log_mel_q24(s_mel_data, s_window_peak_q);
|
||||
scratch_write_data_o[0] = log_mel_result(s_log_lookup, s_front_coeff_data_q);
|
||||
scratch_write_strb_o[0] = 4'hf;
|
||||
end
|
||||
FrontDct: begin
|
||||
@@ -1169,36 +1258,96 @@ module apu_kws_engine (
|
||||
assign s_storage_write_access = |scratch_write_valid_o;
|
||||
assign s_storage_has_access = s_storage_read_access || s_storage_write_access;
|
||||
assign storage_write_o = s_storage_write_phase_q || !s_storage_read_access;
|
||||
assign storage_req_o = !s_storage_wait_q && s_storage_has_access;
|
||||
assign storage_req_o = !s_storage_wait_q && !s_storage_complete_q && s_storage_has_access &&
|
||||
(!s_storage_write_phase_q ||
|
||||
((!s_front_coeff_needed || s_front_coeff_have_q) &&
|
||||
(!s_infer_coeff_needed || s_infer_coeff_have_q)));
|
||||
assign s_storage_final_done = storage_done_i &&
|
||||
(s_storage_write_phase_q || !s_storage_read_access || !s_storage_write_access);
|
||||
assign s_storage_state_step = (!s_storage_wait_q && !s_storage_has_access) ||
|
||||
(s_storage_final_done && s_storage_state_owned_q);
|
||||
assign s_storage_state_step = ((!s_storage_wait_q && !s_storage_has_access) ||
|
||||
((s_storage_complete_q || s_storage_final_done) &&
|
||||
s_storage_state_owned_q)) &&
|
||||
(!s_front_coeff_needed || s_front_coeff_have_q) &&
|
||||
(!s_infer_coeff_needed || s_infer_coeff_have_q);
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_storage_wait_q <= 1'b0;
|
||||
s_storage_complete_q <= 1'b0;
|
||||
s_storage_write_phase_q <= 1'b0;
|
||||
s_storage_state_owned_q <= 1'b0;
|
||||
end else if (soft_reset_i || resource_reset_i || abort_i) begin
|
||||
s_storage_wait_q <= 1'b0;
|
||||
s_storage_complete_q <= 1'b0;
|
||||
s_storage_write_phase_q <= 1'b0;
|
||||
s_storage_state_owned_q <= 1'b0;
|
||||
end else begin
|
||||
if (storage_req_o && storage_ready_i) begin
|
||||
s_storage_wait_q <= 1'b1;
|
||||
s_storage_complete_q <= 1'b0;
|
||||
s_storage_state_owned_q <= (s_front_state_q != FrontIdle) || (s_infer_state_q != InferIdle);
|
||||
end
|
||||
if (storage_done_i && s_storage_wait_q) begin
|
||||
s_storage_wait_q <= 1'b0;
|
||||
if (!s_storage_write_phase_q && s_storage_read_access && s_storage_write_access) begin
|
||||
s_storage_wait_q <= 1'b0;
|
||||
s_storage_write_phase_q <= 1'b1;
|
||||
end else begin
|
||||
s_storage_wait_q <= 1'b0;
|
||||
s_storage_complete_q <= s_storage_state_owned_q;
|
||||
s_storage_write_phase_q <= 1'b0;
|
||||
if (!s_storage_state_owned_q) s_storage_state_owned_q <= 1'b0;
|
||||
end
|
||||
end
|
||||
if (s_storage_state_step) begin
|
||||
s_storage_complete_q <= 1'b0;
|
||||
s_storage_state_owned_q <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_front_coeff_pending_q <= 1'b0;
|
||||
s_front_coeff_have_q <= 1'b0;
|
||||
s_front_coeff_fault_q <= 1'b0;
|
||||
s_front_coeff_data_q <= 64'd0;
|
||||
s_infer_coeff_pending_q <= 1'b0;
|
||||
s_infer_coeff_have_q <= 1'b0;
|
||||
s_infer_coeff_fault_q <= 1'b0;
|
||||
s_infer_coeff_data_q <= 32'd0;
|
||||
end else if (soft_reset_i || resource_reset_i || abort_i) begin
|
||||
s_front_coeff_pending_q <= 1'b0;
|
||||
s_front_coeff_have_q <= 1'b0;
|
||||
s_front_coeff_fault_q <= 1'b0;
|
||||
s_infer_coeff_pending_q <= 1'b0;
|
||||
s_infer_coeff_have_q <= 1'b0;
|
||||
s_infer_coeff_fault_q <= 1'b0;
|
||||
end else begin
|
||||
if (coeff_frontend_req_valid_o && coeff_frontend_req_ready_i) begin
|
||||
s_front_coeff_pending_q <= 1'b1;
|
||||
end
|
||||
if (coeff_frontend_resp_valid_i && coeff_frontend_resp_ready_o) begin
|
||||
s_front_coeff_pending_q <= 1'b0;
|
||||
s_front_coeff_have_q <= 1'b1;
|
||||
s_front_coeff_fault_q <= coeff_frontend_fault_i;
|
||||
s_front_coeff_data_q <= coeff_frontend_data_i;
|
||||
end
|
||||
if (coeff_inference_req_valid_o && coeff_inference_req_ready_i) begin
|
||||
s_infer_coeff_pending_q <= 1'b1;
|
||||
end
|
||||
if (coeff_inference_resp_valid_i && coeff_inference_resp_ready_o) begin
|
||||
s_infer_coeff_pending_q <= 1'b0;
|
||||
s_infer_coeff_have_q <= 1'b1;
|
||||
s_infer_coeff_fault_q <= coeff_inference_fault_i;
|
||||
s_infer_coeff_data_q <= coeff_inference_data_i;
|
||||
end
|
||||
if (s_storage_state_step) begin
|
||||
s_front_coeff_have_q <= 1'b0;
|
||||
s_front_coeff_fault_q <= 1'b0;
|
||||
s_infer_coeff_have_q <= 1'b0;
|
||||
s_infer_coeff_fault_q <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assign memory_dma_request_valid_o = s_mem_req_q;
|
||||
@@ -1207,7 +1356,8 @@ module apu_kws_engine (
|
||||
(s_infer_state_q == InferIdle) && (s_front_state_q == FrontIdle);
|
||||
assign memory_dma_request_address_o = memory_address_i;
|
||||
assign memory_dma_request_bytes_o = 32'd32000;
|
||||
assign memory_dma_ready_o = s_mem_dma_active_q && !s_mem_word_valid_q;
|
||||
assign memory_dma_ready_o = s_mem_dma_active_q && !s_mem_word_valid_q &&
|
||||
!s_storage_wait_q && !s_storage_complete_q && storage_ready_i;
|
||||
assign memory_done_o = s_mem_done_q;
|
||||
assign memory_error_o = s_mem_err_q;
|
||||
assign memory_error_code_o = s_mem_err_code_q;
|
||||
@@ -1405,13 +1555,13 @@ module apu_kws_engine (
|
||||
s_mem_err_detail_q <= 32'd0;
|
||||
end
|
||||
end else begin
|
||||
if (s_front_state_q == FrontIdle || storage_progress_i ||
|
||||
if (s_front_state_q == FrontIdle || storage_progress_i || s_coeff_progress ||
|
||||
(s_storage_state_step && s_front_progress)) begin
|
||||
s_front_watchdog_q <= 32'd0;
|
||||
end else if (!(&s_front_watchdog_q)) begin
|
||||
s_front_watchdog_q <= s_front_watchdog_q + 1'b1;
|
||||
end
|
||||
if (s_infer_state_q == InferIdle || storage_progress_i ||
|
||||
if (s_infer_state_q == InferIdle || storage_progress_i || s_coeff_progress ||
|
||||
(s_storage_state_step && s_infer_progress)) begin
|
||||
s_infer_watchdog_q <= 32'd0;
|
||||
end else if (!(&s_infer_watchdog_q)) begin
|
||||
@@ -1562,7 +1712,9 @@ module apu_kws_engine (
|
||||
s_s24_pending_q <= 1'b0;
|
||||
end
|
||||
|
||||
if (s_commit_valid && ((s_front_state_q == FrontIdle) || s_storage_state_step)) begin
|
||||
if (s_commit_valid &&
|
||||
(((s_front_state_q == FrontIdle) && storage_req_o && storage_ready_i) ||
|
||||
s_storage_state_step)) begin
|
||||
s_ingress_write_q <= s_next_write;
|
||||
s_epoch_samples_q <= s_next_samples;
|
||||
s_timestamp_q <= s_timestamp_q + 1'b1;
|
||||
@@ -1598,6 +1750,15 @@ module apu_kws_engine (
|
||||
end
|
||||
|
||||
if (s_storage_state_step) begin
|
||||
if (s_front_coeff_needed && s_front_coeff_fault_q) begin
|
||||
s_fault_q <= 1'b1;
|
||||
s_fault_code_q <= `APB4_APU__ERROR_CODE_KWS_ARITHMETIC;
|
||||
s_fault_stage_q <= `APB4_APU__ERROR_STAGE_KWS_FRONTEND;
|
||||
s_fault_addr_q <= {16'd0, coeff_frontend_index_o, 2'd0};
|
||||
s_fault_detail_q <= 32'h0900_0100 | {28'd0, coeff_frontend_kind_o};
|
||||
s_stop_latched_q <= 1'b1;
|
||||
s_front_state_q <= FrontIdle;
|
||||
end else
|
||||
unique case (s_front_state_q)
|
||||
FrontIdle: begin
|
||||
if (s_frame_pending_q) begin
|
||||
@@ -1751,6 +1912,15 @@ module apu_kws_engine (
|
||||
default: s_front_state_q <= FrontIdle;
|
||||
endcase
|
||||
|
||||
if (s_infer_coeff_needed && s_infer_coeff_fault_q) begin
|
||||
s_fault_q <= 1'b1;
|
||||
s_fault_code_q <= `APB4_APU__ERROR_CODE_KWS_ARITHMETIC;
|
||||
s_fault_stage_q <= `APB4_APU__ERROR_STAGE_KWS_INFERENCE;
|
||||
s_fault_addr_q <= {23'd0, coeff_inference_index_o, 2'd0};
|
||||
s_fault_detail_q <= 32'h0900_0107;
|
||||
s_stop_latched_q <= 1'b1;
|
||||
s_infer_state_q <= InferIdle;
|
||||
end else
|
||||
unique case (s_infer_state_q)
|
||||
InferIdle: begin
|
||||
end
|
||||
@@ -1838,7 +2008,7 @@ module apu_kws_engine (
|
||||
InferSoftmaxSum: begin
|
||||
if (s_softmax_sum_diff >= -9'sd124) begin
|
||||
s_softmax_sum_q <= s_softmax_sum_q +
|
||||
rounding_divide_pot(apu_kws_softmax_exp_q31(-s_softmax_sum_diff[6:0]), 12);
|
||||
rounding_divide_pot($signed(s_infer_coeff_data_q), 12);
|
||||
end
|
||||
if (s_softmax_index_q == 4'd11) begin
|
||||
s_softmax_index_q <= 4'd0;
|
||||
|
||||
@@ -40,6 +40,13 @@ module apu_kws_model_loader (
|
||||
output logic [31:0] local_data_o,
|
||||
output logic [ 3:0] local_strb_o,
|
||||
input logic local_ready_i,
|
||||
output logic profile_request_valid_o,
|
||||
input logic profile_request_ready_i,
|
||||
output logic [10:0] profile_index_o,
|
||||
input logic profile_response_valid_i,
|
||||
output logic profile_response_ready_o,
|
||||
input logic [31:0] profile_data_i,
|
||||
input logic profile_fault_i,
|
||||
output logic busy_o,
|
||||
output logic valid_o,
|
||||
output logic lock_o,
|
||||
@@ -56,8 +63,6 @@ module apu_kws_model_loader (
|
||||
output logic abort_done_o
|
||||
// verilog_format: on
|
||||
);
|
||||
`include "apu_kws_apum_profile.svh"
|
||||
|
||||
localparam logic [31:0] ModelBytes = 32'd32768;
|
||||
localparam logic [31:0] ModelCrc = 32'hb903_4b22;
|
||||
localparam logic [31:0] ModelAddress = 32'hce4f_7000;
|
||||
@@ -88,9 +93,12 @@ module apu_kws_model_loader (
|
||||
logic [15:0][31:0] s_header_q;
|
||||
logic s_padding_err_q;
|
||||
logic s_profile_err_q;
|
||||
logic s_profile_pending_q;
|
||||
logic s_dma_complete_q;
|
||||
logic [31:0] s_profile_word_q;
|
||||
logic [31:0] s_profile_offset_q;
|
||||
logic s_receive_err_q;
|
||||
logic [31:0] s_profile_err_addr_q;
|
||||
logic [ 1:0] s_profile_word_check;
|
||||
logic s_profile_candidate;
|
||||
logic [32:0] s_last_addr;
|
||||
logic [32:0] s_snapshot_last_addr;
|
||||
@@ -154,6 +162,33 @@ module apu_kws_model_loader (
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic [10:0] profile_word_index(input logic [31:0] offset_i);
|
||||
begin
|
||||
if (offset_i <= 32'h04fc) begin
|
||||
return 11'(offset_i >> 2);
|
||||
end else if ((offset_i >= 32'h1000) && (offset_i <= 32'h11fc)) begin
|
||||
return 11'd320 + 11'((offset_i - 32'h1000) >> 2);
|
||||
end else if ((offset_i >= 32'h1540) && (offset_i <= 32'h173c)) begin
|
||||
return 11'd448 + 11'((offset_i - 32'h1540) >> 2);
|
||||
end else if ((offset_i >= 32'h2840) && (offset_i <= 32'h2a3c)) begin
|
||||
return 11'd576 + 11'((offset_i - 32'h2840) >> 2);
|
||||
end else if ((offset_i >= 32'h2d80) && (offset_i <= 32'h2f7c)) begin
|
||||
return 11'd704 + 11'((offset_i - 32'h2d80) >> 2);
|
||||
end else if ((offset_i >= 32'h4080) && (offset_i <= 32'h427c)) begin
|
||||
return 11'd832 + 11'((offset_i - 32'h4080) >> 2);
|
||||
end else if ((offset_i >= 32'h45c0) && (offset_i <= 32'h47bc)) begin
|
||||
return 11'd960 + 11'((offset_i - 32'h45c0) >> 2);
|
||||
end else if ((offset_i >= 32'h58c0) && (offset_i <= 32'h5abc)) begin
|
||||
return 11'd1088 + 11'((offset_i - 32'h58c0) >> 2);
|
||||
end else if ((offset_i >= 32'h5e00) && (offset_i <= 32'h5ffc)) begin
|
||||
return 11'd1216 + 11'((offset_i - 32'h5e00) >> 2);
|
||||
end else if ((offset_i >= 32'h7100) && (offset_i <= 32'h72fc)) begin
|
||||
return 11'd1344 + 11'((offset_i - 32'h7100) >> 2);
|
||||
end
|
||||
return 11'd1472 + 11'((offset_i - 32'h7630) >> 2);
|
||||
end
|
||||
endfunction
|
||||
|
||||
assign s_last_addr = {1'b0, address_i} + {1'b0, size_i} - 1'b1;
|
||||
assign s_admit_ok = (size_i == ModelBytes) && (address_i[5:0] == 6'd0) &&
|
||||
(address_i >= acl_base_i) && !s_last_addr[32] && (s_last_addr[31:0] <= acl_limit_i) &&
|
||||
@@ -165,16 +200,18 @@ module apu_kws_model_loader (
|
||||
assign s_data_accept = dma_valid_i && dma_ready_o;
|
||||
assign s_crc_after_word = crc32_word(s_crc_q, dma_data_i, dma_keep_i);
|
||||
assign s_profile_candidate = profile_word_candidate(s_received_q);
|
||||
assign s_profile_word_check = s_profile_candidate ? apu_kws_apum_fixed_word_check(
|
||||
s_received_q[14:0], dma_data_i
|
||||
) : 2'b00;
|
||||
assign profile_request_valid_o = (s_state_q == ReceivePayload) && dma_valid_i &&
|
||||
local_ready_i && s_profile_candidate && !s_profile_pending_q;
|
||||
assign profile_index_o = profile_word_index(s_received_q);
|
||||
assign profile_response_ready_o = s_profile_pending_q;
|
||||
|
||||
assign dma_request_valid_o = ((s_state_q == RequestHeader) ||
|
||||
(s_state_q == RequestPayload)) && s_snapshot_range_ok;
|
||||
assign dma_request_address_o = (s_state_q == RequestPayload) ? s_addr_q + 32'd64 : s_addr_q;
|
||||
assign dma_request_bytes_o = (s_state_q == RequestPayload) ? s_size_q - 32'd64 : 32'd64;
|
||||
assign dma_ready_o = ((s_state_q == ReceiveHeader) ||
|
||||
(s_state_q == ReceivePayload)) && local_ready_i;
|
||||
assign dma_ready_o = (s_state_q == ReceiveHeader) ||
|
||||
((s_state_q == ReceivePayload) && local_ready_i && !s_profile_pending_q &&
|
||||
(!s_profile_candidate || profile_request_ready_i));
|
||||
assign local_request_o = s_data_accept;
|
||||
assign local_address_o = KwsBase[16:0] + s_received_q[16:0];
|
||||
assign local_data_o = dma_data_i;
|
||||
@@ -217,6 +254,10 @@ module apu_kws_model_loader (
|
||||
s_header_q <= '0;
|
||||
s_padding_err_q <= 1'b0;
|
||||
s_profile_err_q <= 1'b0;
|
||||
s_profile_pending_q <= 1'b0;
|
||||
s_dma_complete_q <= 1'b0;
|
||||
s_profile_word_q <= 32'd0;
|
||||
s_profile_offset_q <= 32'd0;
|
||||
s_receive_err_q <= 1'b0;
|
||||
s_profile_err_addr_q <= 32'd0;
|
||||
s_addr_q <= 32'd0;
|
||||
@@ -245,6 +286,10 @@ module apu_kws_model_loader (
|
||||
s_received_q <= 32'd0;
|
||||
s_padding_err_q <= 1'b0;
|
||||
s_profile_err_q <= 1'b0;
|
||||
s_profile_pending_q <= 1'b0;
|
||||
s_dma_complete_q <= 1'b0;
|
||||
s_profile_word_q <= 32'd0;
|
||||
s_profile_offset_q <= 32'd0;
|
||||
s_receive_err_q <= 1'b0;
|
||||
s_profile_err_addr_q <= 32'd0;
|
||||
s_addr_q <= 32'd0;
|
||||
@@ -253,6 +298,13 @@ module apu_kws_model_loader (
|
||||
s_acl_base_q <= 32'd0;
|
||||
s_acl_limit_q <= 32'd0;
|
||||
end else begin
|
||||
if (profile_response_valid_i && profile_response_ready_o) begin
|
||||
s_profile_pending_q <= 1'b0;
|
||||
if ((profile_data_i != s_profile_word_q) || profile_fault_i) begin
|
||||
s_profile_err_q <= 1'b1;
|
||||
s_profile_err_addr_q <= s_addr_q + s_profile_offset_q;
|
||||
end
|
||||
end
|
||||
unique case (s_state_q)
|
||||
Idle: begin
|
||||
if (start_i && !s_lock_q) begin
|
||||
@@ -270,6 +322,10 @@ module apu_kws_model_loader (
|
||||
s_header_q <= '0;
|
||||
s_padding_err_q <= 1'b0;
|
||||
s_profile_err_q <= 1'b0;
|
||||
s_profile_pending_q <= 1'b0;
|
||||
s_dma_complete_q <= 1'b0;
|
||||
s_profile_word_q <= 32'd0;
|
||||
s_profile_offset_q <= 32'd0;
|
||||
s_receive_err_q <= 1'b0;
|
||||
s_profile_err_addr_q <= 32'd0;
|
||||
s_addr_q <= address_i;
|
||||
@@ -475,6 +531,7 @@ module apu_kws_model_loader (
|
||||
end
|
||||
ReceivePayload: begin
|
||||
if (dma_done_i) begin
|
||||
s_dma_complete_q <= 1'b1;
|
||||
if (dma_error_i) begin
|
||||
s_stat_q <= 32'd0;
|
||||
s_err_code_q <= dma_error_code_i;
|
||||
@@ -501,15 +558,14 @@ module apu_kws_model_loader (
|
||||
s_padding_err_q <= 1'b1;
|
||||
s_profile_err_addr_q <= s_addr_q + s_received_q;
|
||||
end
|
||||
if (s_profile_candidate && !s_profile_err_q && s_profile_word_check[1] &&
|
||||
!s_profile_word_check[0]) begin
|
||||
s_profile_err_q <= 1'b1;
|
||||
s_profile_err_addr_q <= s_addr_q + s_received_q;
|
||||
if (s_profile_candidate && !s_profile_err_q) begin
|
||||
s_profile_pending_q <= 1'b1;
|
||||
s_profile_word_q <= dma_data_i;
|
||||
s_profile_offset_q <= s_received_q;
|
||||
end
|
||||
s_crc_q <= s_crc_after_word;
|
||||
s_actual_crc_q <= ~s_crc_after_word;
|
||||
s_received_q <= s_received_q + 32'd4;
|
||||
s_state_q <= Validate;
|
||||
s_state_q <= WaitPayload;
|
||||
end else begin
|
||||
s_stat_q <= 32'h0000_0200;
|
||||
s_err_code_q <= `APB4_APU__ERROR_CODE_KWS_MODEL;
|
||||
@@ -539,10 +595,9 @@ module apu_kws_model_loader (
|
||||
s_profile_err_addr_q <= s_addr_q + s_received_q;
|
||||
end
|
||||
if (s_profile_candidate && !s_profile_err_q) begin
|
||||
if (s_profile_word_check[1] && !s_profile_word_check[0]) begin
|
||||
s_profile_err_q <= 1'b1;
|
||||
s_profile_err_addr_q <= s_addr_q + s_received_q;
|
||||
end
|
||||
s_profile_pending_q <= 1'b1;
|
||||
s_profile_word_q <= dma_data_i;
|
||||
s_profile_offset_q <= s_received_q;
|
||||
end
|
||||
s_crc_q <= s_crc_after_word;
|
||||
s_received_q <= s_received_q + 32'd4;
|
||||
@@ -554,7 +609,9 @@ module apu_kws_model_loader (
|
||||
end
|
||||
WaitPayload: begin
|
||||
if (abort_i && !resource_reset_request_i) s_abort_pending_q <= 1'b1;
|
||||
if (dma_done_i) begin
|
||||
if (dma_done_i) s_dma_complete_q <= 1'b1;
|
||||
if ((dma_done_i || s_dma_complete_q) && !s_profile_pending_q &&
|
||||
!profile_response_valid_i) begin
|
||||
if (dma_error_i) begin
|
||||
s_stat_q <= 32'd0;
|
||||
s_err_code_q <= dma_error_code_i;
|
||||
@@ -585,6 +642,7 @@ module apu_kws_model_loader (
|
||||
s_state_q <= Complete;
|
||||
end else begin
|
||||
s_actual_crc_q <= ~s_crc_q;
|
||||
s_dma_complete_q <= 1'b0;
|
||||
s_state_q <= Validate;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -110,9 +110,13 @@ module apu_microcode_loader #(
|
||||
PathPop,
|
||||
PathPopLoad,
|
||||
PathMemoClear,
|
||||
PathMemoClearWait,
|
||||
PathMemoStart,
|
||||
PathMemoRead,
|
||||
PathMemoReadWait,
|
||||
PathMemoCheck,
|
||||
PathMemoInsert,
|
||||
PathMemoInsertWait,
|
||||
Finish,
|
||||
CancelWait,
|
||||
DescriptorDmaRequest,
|
||||
@@ -148,9 +152,9 @@ module apu_microcode_loader #(
|
||||
logic s_path_stack_cs, s_path_stack_write, s_path_stack_push;
|
||||
logic [11:0] s_path_stack_addr;
|
||||
logic [63:0] s_path_stack_write_data, s_path_stack_read_data;
|
||||
logic [12:0] s_path_memo_addr_q, s_path_memo_probe_q, s_path_memo_clear_q;
|
||||
logic [63:0] s_path_memo_key, s_path_memo_read_key_q;
|
||||
logic s_path_memo_read_valid_q;
|
||||
logic [12:0] s_path_memo_addr_q, s_path_memo_probe_q;
|
||||
logic [63:0] s_path_memo_key, s_path_memo_read_key;
|
||||
logic s_path_memo_ready, s_path_memo_done, s_path_memo_read_valid;
|
||||
logic [ 2:0][63:0] s_entry_instruction_q;
|
||||
logic s_path_stop_valid_q;
|
||||
logic [11:0] s_path_stop_pc_q;
|
||||
@@ -258,8 +262,8 @@ module apu_microcode_loader #(
|
||||
assign actual_crc_o = s_actual_crc_q;
|
||||
assign load_count_o = s_load_count_q;
|
||||
assign proof_visit_count_o = s_path_visit_q;
|
||||
assign proof_memo_full_o = (s_state_q == PathMemoCheck) && s_path_memo_read_valid_q &&
|
||||
(s_path_memo_read_key_q != s_path_memo_key) && (s_path_memo_probe_q == PathMemoLast);
|
||||
assign proof_memo_full_o = (s_state_q == PathMemoCheck) && s_path_memo_read_valid &&
|
||||
(s_path_memo_read_key != s_path_memo_key) && (s_path_memo_probe_q == PathMemoLast);
|
||||
|
||||
for (genvar entry = 0; entry < 3; entry++) begin : gen_entry_outputs
|
||||
assign entry_pc_o[entry] = s_header_v2 ?
|
||||
@@ -522,27 +526,27 @@ module apu_microcode_loader #(
|
||||
assign s_path_visit_limit = s_header_v2 ? 19'(PathTraversalLimitV2) : 19'(PathTraversalLimitV1);
|
||||
|
||||
if (EnableP5) begin : gen_path_memo
|
||||
logic [63:0] s_key_mem [0:PathMemoDepth-1];
|
||||
logic s_valid_mem[0:PathMemoDepth-1];
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (s_state_q == PathMemoClear) begin
|
||||
s_valid_mem[s_path_memo_clear_q] <= 1'b0;
|
||||
end
|
||||
if (s_state_q == PathMemoRead) begin
|
||||
s_path_memo_read_key_q <= s_key_mem[s_path_memo_addr_q];
|
||||
s_path_memo_read_valid_q <= s_valid_mem[s_path_memo_addr_q];
|
||||
end
|
||||
if ((s_state_q == PathMemoCheck) && !s_path_memo_read_valid_q) begin
|
||||
s_key_mem[s_path_memo_addr_q] <= s_path_memo_key;
|
||||
s_valid_mem[s_path_memo_addr_q] <= 1'b1;
|
||||
end
|
||||
end
|
||||
apu_proof_memo #(
|
||||
.Depth(PathMemoDepth)
|
||||
) u_proof_memo (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.abort_i (resource_reset_i || abort_i),
|
||||
.clear_i (s_state_q == PathMemoClear),
|
||||
.lookup_i (s_state_q == PathMemoRead),
|
||||
.insert_i (s_state_q == PathMemoInsert),
|
||||
.addr_i (s_path_memo_addr_q),
|
||||
.key_i (s_path_memo_key),
|
||||
.ready_o (s_path_memo_ready),
|
||||
.done_o (s_path_memo_done),
|
||||
.read_valid_o(s_path_memo_read_valid),
|
||||
.read_key_o (s_path_memo_read_key)
|
||||
);
|
||||
end else begin : gen_no_path_memo
|
||||
always_comb begin
|
||||
s_path_memo_read_key_q = 64'd0;
|
||||
s_path_memo_read_valid_q = 1'b0;
|
||||
end
|
||||
assign s_path_memo_ready = 1'b1;
|
||||
assign s_path_memo_done = 1'b1;
|
||||
assign s_path_memo_read_valid = 1'b0;
|
||||
assign s_path_memo_read_key = 64'd0;
|
||||
end
|
||||
|
||||
`ifdef HAVE_SRAM_MACRO
|
||||
@@ -777,7 +781,6 @@ module apu_microcode_loader #(
|
||||
s_path_visit_q <= 19'd0;
|
||||
s_path_memo_addr_q <= 13'd0;
|
||||
s_path_memo_probe_q <= 13'd0;
|
||||
s_path_memo_clear_q <= 13'd0;
|
||||
s_path_stop_valid_q <= 1'b0;
|
||||
s_path_stop_pc_q <= 12'd0;
|
||||
s_entry_instruction_q <= '0;
|
||||
@@ -815,7 +818,8 @@ module apu_microcode_loader #(
|
||||
if ((s_state_q inside {
|
||||
HeaderValidate, DescriptorValidate, Validate, ScanRequest, ScanWait, ScanCheck,
|
||||
PathRequest, PathWait, PathCheck, PathPop, PathPopLoad, PathMemoClear,
|
||||
PathMemoStart, PathMemoRead, PathMemoCheck, Finish
|
||||
PathMemoClearWait, PathMemoStart, PathMemoRead, PathMemoReadWait,
|
||||
PathMemoCheck, PathMemoInsert, PathMemoInsertWait, Finish
|
||||
}) &&
|
||||
(resource_reset_i || abort_i)) begin
|
||||
s_state_q <= Idle;
|
||||
@@ -1119,7 +1123,6 @@ module apu_microcode_loader #(
|
||||
s_path_visit_q <= 19'd0;
|
||||
s_path_memo_addr_q <= 13'd0;
|
||||
s_path_memo_probe_q <= 13'd0;
|
||||
s_path_memo_clear_q <= 13'd0;
|
||||
s_path_stop_valid_q <= 1'b0;
|
||||
s_state_q <= ScanRequest;
|
||||
end
|
||||
@@ -1190,13 +1193,9 @@ module apu_microcode_loader #(
|
||||
end
|
||||
end
|
||||
PathMemoClear: begin
|
||||
if (s_path_memo_clear_q == PathMemoLast) begin
|
||||
s_path_memo_clear_q <= 13'd0;
|
||||
s_state_q <= PathMemoStart;
|
||||
end else begin
|
||||
s_path_memo_clear_q <= s_path_memo_clear_q + 1'b1;
|
||||
end
|
||||
if (s_path_memo_ready) s_state_q <= PathMemoClearWait;
|
||||
end
|
||||
PathMemoClearWait: if (s_path_memo_done) s_state_q <= PathMemoStart;
|
||||
PathMemoStart: begin
|
||||
if (s_call_depth_q > 3'd2) begin
|
||||
s_state_q <= PathRequest;
|
||||
@@ -1206,12 +1205,15 @@ module apu_microcode_loader #(
|
||||
s_state_q <= PathMemoRead;
|
||||
end
|
||||
end
|
||||
PathMemoRead: s_state_q <= PathMemoCheck;
|
||||
PathMemoRead: begin
|
||||
if (s_path_memo_ready) s_state_q <= PathMemoReadWait;
|
||||
end
|
||||
PathMemoReadWait: if (s_path_memo_done) s_state_q <= PathMemoCheck;
|
||||
PathMemoCheck: begin
|
||||
if (s_path_memo_read_valid_q && (s_path_memo_read_key_q == s_path_memo_key)) begin
|
||||
if (s_path_memo_read_valid && (s_path_memo_read_key == s_path_memo_key)) begin
|
||||
s_state_q <= PathPop;
|
||||
end else if (!s_path_memo_read_valid_q) begin
|
||||
s_state_q <= PathRequest;
|
||||
end else if (!s_path_memo_read_valid) begin
|
||||
s_state_q <= PathMemoInsert;
|
||||
end else if (s_path_memo_probe_q == PathMemoLast) begin
|
||||
// A full table cannot weaken or reject the proof; traverse this state normally.
|
||||
s_state_q <= PathRequest;
|
||||
@@ -1221,6 +1223,10 @@ module apu_microcode_loader #(
|
||||
s_state_q <= PathMemoRead;
|
||||
end
|
||||
end
|
||||
PathMemoInsert: begin
|
||||
if (s_path_memo_ready) s_state_q <= PathMemoInsertWait;
|
||||
end
|
||||
PathMemoInsertWait: if (s_path_memo_done) s_state_q <= PathRequest;
|
||||
PathRequest: begin
|
||||
if (s_path_visit_q >= s_path_visit_limit) begin
|
||||
if (!s_scan_control_err_q || (entry_pc_o[s_scan_entry_q] < s_scan_control_pc_q)) begin
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
module apu_proof_memo #(
|
||||
parameter int unsigned Depth = 8192
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic abort_i,
|
||||
input logic clear_i,
|
||||
input logic lookup_i,
|
||||
input logic insert_i,
|
||||
input logic [12:0] addr_i,
|
||||
input logic [63:0] key_i,
|
||||
output logic ready_o,
|
||||
output logic done_o,
|
||||
output logic read_valid_o,
|
||||
output logic [63:0] read_key_o
|
||||
);
|
||||
localparam int unsigned BitmapRows = (Depth + 31) / 32;
|
||||
localparam logic [7:0] BitmapLast = 8'(BitmapRows - 1);
|
||||
typedef enum logic [2:0] {
|
||||
Idle,
|
||||
LookupWait,
|
||||
InsertBitmapWrite,
|
||||
ClearBitmap,
|
||||
ClearDrain
|
||||
} memo_state_e;
|
||||
|
||||
memo_state_e s_state_q;
|
||||
logic [12:0] s_addr_q;
|
||||
logic [63:0] s_key_q;
|
||||
logic [ 7:0] s_clear_row_q;
|
||||
logic [ 2:0] s_read_bank_q;
|
||||
|
||||
logic [ 7:0] s_key_cs;
|
||||
logic [ 7:0] s_key_wren;
|
||||
logic [ 7:0][31:0] s_key_low_rdata;
|
||||
logic [ 7:0][31:0] s_key_high_rdata;
|
||||
logic s_bitmap_cs;
|
||||
logic s_bitmap_wren;
|
||||
logic [ 9:0] s_bitmap_addr;
|
||||
logic [31:0] s_bitmap_wdata;
|
||||
logic [31:0] s_bitmap_rdata;
|
||||
|
||||
`ifndef SYNTHESIS
|
||||
initial begin
|
||||
if ((Depth == 0) || (Depth > 8192) || ((Depth & (Depth - 1)) != 0)) begin
|
||||
$fatal(1, "apu_proof_memo: depth must be a power of two up to 8192");
|
||||
end
|
||||
end
|
||||
`endif
|
||||
|
||||
assign ready_o = s_state_q == Idle;
|
||||
|
||||
always_comb begin
|
||||
s_key_cs = 8'd0;
|
||||
s_key_wren = 8'd0;
|
||||
s_bitmap_cs = 1'b0;
|
||||
s_bitmap_wren = 1'b0;
|
||||
s_bitmap_addr = 10'd0;
|
||||
s_bitmap_wdata = 32'd0;
|
||||
|
||||
if ((s_state_q == Idle) && clear_i) begin
|
||||
s_bitmap_cs = 1'b1;
|
||||
s_bitmap_wren = 1'b1;
|
||||
s_bitmap_addr = 10'd0;
|
||||
s_bitmap_wdata = 32'd0;
|
||||
end else if ((s_state_q == Idle) && lookup_i) begin
|
||||
s_key_cs[addr_i[12:10]] = 1'b1;
|
||||
s_bitmap_cs = 1'b1;
|
||||
s_bitmap_addr = {2'd0, addr_i[12:5]};
|
||||
end else if ((s_state_q == Idle) && insert_i) begin
|
||||
s_key_cs[addr_i[12:10]] = 1'b1;
|
||||
s_key_wren[addr_i[12:10]] = 1'b1;
|
||||
s_bitmap_cs = 1'b1;
|
||||
s_bitmap_addr = {2'd0, addr_i[12:5]};
|
||||
end else if (s_state_q == InsertBitmapWrite) begin
|
||||
s_bitmap_cs = 1'b1;
|
||||
s_bitmap_wren = 1'b1;
|
||||
s_bitmap_addr = {2'd0, s_addr_q[12:5]};
|
||||
s_bitmap_wdata = s_bitmap_rdata | (32'd1 << s_addr_q[4:0]);
|
||||
end else if (s_state_q == ClearBitmap) begin
|
||||
s_bitmap_cs = 1'b1;
|
||||
s_bitmap_wren = 1'b1;
|
||||
s_bitmap_addr = {2'd0, s_clear_row_q};
|
||||
s_bitmap_wdata = 32'd0;
|
||||
end
|
||||
end
|
||||
|
||||
`ifdef HAVE_SRAM_MACRO
|
||||
for (genvar bank = 0; bank < 8; bank++) begin : gen_key_bank
|
||||
tc_sram_1024x32 u_key_low_sram (
|
||||
.clk_i (clk_i),
|
||||
.cs_i (s_key_cs[bank]),
|
||||
.addr_i((s_state_q == Idle) ? addr_i[9:0] : s_addr_q[9:0]),
|
||||
.data_i((s_state_q == Idle) ? key_i[31:0] : s_key_q[31:0]),
|
||||
.mask_i(4'hf),
|
||||
.wren_i(s_key_wren[bank]),
|
||||
.data_o(s_key_low_rdata[bank])
|
||||
);
|
||||
tc_sram_1024x32 u_key_high_sram (
|
||||
.clk_i (clk_i),
|
||||
.cs_i (s_key_cs[bank]),
|
||||
.addr_i((s_state_q == Idle) ? addr_i[9:0] : s_addr_q[9:0]),
|
||||
.data_i((s_state_q == Idle) ? key_i[63:32] : s_key_q[63:32]),
|
||||
.mask_i(4'hf),
|
||||
.wren_i(s_key_wren[bank]),
|
||||
.data_o(s_key_high_rdata[bank])
|
||||
);
|
||||
end
|
||||
tc_sram_1024x32 u_valid_bitmap_sram (
|
||||
.clk_i (clk_i),
|
||||
.cs_i (s_bitmap_cs),
|
||||
.addr_i(s_bitmap_addr),
|
||||
.data_i(s_bitmap_wdata),
|
||||
.mask_i(4'hf),
|
||||
.wren_i(s_bitmap_wren),
|
||||
.data_o(s_bitmap_rdata)
|
||||
);
|
||||
`else
|
||||
logic [63:0] s_key_mem [0:Depth-1];
|
||||
logic [31:0] s_bitmap_mem[0:BitmapRows-1];
|
||||
logic [12:0] s_key_access_addr;
|
||||
logic [63:0] s_key_access_data;
|
||||
|
||||
assign s_key_access_addr = (s_state_q == Idle) ? addr_i : s_addr_q;
|
||||
assign s_key_access_data = (s_state_q == Idle) ? key_i : s_key_q;
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (|s_key_cs) begin
|
||||
if (|s_key_wren) begin
|
||||
s_key_mem[s_key_access_addr] <= s_key_access_data;
|
||||
end else begin
|
||||
s_key_low_rdata[s_key_access_addr[12:10]] <= s_key_mem[s_key_access_addr][31:0];
|
||||
s_key_high_rdata[s_key_access_addr[12:10]] <= s_key_mem[s_key_access_addr][63:32];
|
||||
end
|
||||
end
|
||||
if (s_bitmap_cs) begin
|
||||
if (s_bitmap_wren) begin
|
||||
s_bitmap_mem[s_bitmap_addr[7:0]] <= s_bitmap_wdata;
|
||||
end else begin
|
||||
s_bitmap_rdata <= s_bitmap_mem[s_bitmap_addr[7:0]];
|
||||
end
|
||||
end
|
||||
end
|
||||
`endif
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_addr_q <= 13'd0;
|
||||
s_key_q <= 64'd0;
|
||||
s_clear_row_q <= 8'd0;
|
||||
s_read_bank_q <= 3'd0;
|
||||
done_o <= 1'b0;
|
||||
read_valid_o <= 1'b0;
|
||||
read_key_o <= 64'd0;
|
||||
end else begin
|
||||
done_o <= 1'b0;
|
||||
if (abort_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_clear_row_q <= 8'd0;
|
||||
end else begin
|
||||
unique case (s_state_q)
|
||||
Idle: begin
|
||||
if (clear_i) begin
|
||||
s_clear_row_q <= 8'd1;
|
||||
s_state_q <= (BitmapRows == 1) ? ClearDrain : ClearBitmap;
|
||||
end else if (lookup_i) begin
|
||||
s_addr_q <= addr_i;
|
||||
s_key_q <= key_i;
|
||||
s_read_bank_q <= addr_i[12:10];
|
||||
s_state_q <= LookupWait;
|
||||
end else if (insert_i) begin
|
||||
s_addr_q <= addr_i;
|
||||
s_key_q <= key_i;
|
||||
s_state_q <= InsertBitmapWrite;
|
||||
end
|
||||
end
|
||||
LookupWait: begin
|
||||
read_key_o <= {s_key_high_rdata[s_read_bank_q], s_key_low_rdata[s_read_bank_q]};
|
||||
read_valid_o <= s_bitmap_rdata[s_addr_q[4:0]];
|
||||
done_o <= 1'b1;
|
||||
s_state_q <= Idle;
|
||||
end
|
||||
InsertBitmapWrite: begin
|
||||
done_o <= 1'b1;
|
||||
s_state_q <= Idle;
|
||||
end
|
||||
ClearBitmap: begin
|
||||
if (s_clear_row_q == BitmapLast) begin
|
||||
s_clear_row_q <= 8'd0;
|
||||
s_state_q <= ClearDrain;
|
||||
end else begin
|
||||
s_clear_row_q <= s_clear_row_q + 1'b1;
|
||||
end
|
||||
end
|
||||
ClearDrain: begin
|
||||
done_o <= 1'b1;
|
||||
s_state_q <= Idle;
|
||||
end
|
||||
default: s_state_q <= Idle;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
+112
-25
@@ -41,7 +41,7 @@ module apu_reg #(
|
||||
input logic [31:0] sequencer_status_i,
|
||||
input logic [31:0] sequencer_retired_i,
|
||||
input logic sequencer_trapped_i,
|
||||
input logic [10:0] irq_set_i,
|
||||
input logic [11:0] irq_set_i,
|
||||
input logic fault_valid_i,
|
||||
input logic [5:0] fault_code_i,
|
||||
input logic [3:0] fault_stage_i,
|
||||
@@ -59,6 +59,9 @@ module apu_reg #(
|
||||
input logic [31:0] kws_overrun_count_i,
|
||||
input logic [31:0] kws_model_status_i,
|
||||
input logic [31:0] kws_model_actual_crc_i,
|
||||
input logic [31:0] kws_coeff_status_i,
|
||||
input logic [31:0] kws_coeff_actual_crc_i,
|
||||
input logic [63:0] kws_coefficient_id_i,
|
||||
input logic kws_config_publish_i,
|
||||
input logic [15:0] kws_config_default_i,
|
||||
input logic kws_disable_done_i,
|
||||
@@ -77,6 +80,7 @@ module apu_reg #(
|
||||
output logic ring_kick_o,
|
||||
output logic microcode_load_o,
|
||||
output logic model_load_o,
|
||||
output logic coefficient_load_o,
|
||||
output logic counter_clear_o,
|
||||
output logic perf_enable_o,
|
||||
output logic xrun_clear_o,
|
||||
@@ -100,6 +104,9 @@ module apu_reg #(
|
||||
output logic [31:0] kws_model_address_o,
|
||||
output logic [31:0] kws_model_size_o,
|
||||
output logic [31:0] kws_model_expected_crc_o,
|
||||
output logic [31:0] kws_coeff_address_o,
|
||||
output logic [31:0] kws_coeff_size_o,
|
||||
output logic [31:0] kws_coeff_expected_crc_o,
|
||||
output logic [1:0] kws_control_o,
|
||||
output logic [15:0] kws_config_o,
|
||||
output logic [31:0] kws_input_config_o,
|
||||
@@ -110,14 +117,15 @@ module apu_reg #(
|
||||
// verilog_format: on
|
||||
);
|
||||
localparam logic [31:0] IpId = 32'h4150_5530;
|
||||
localparam logic [31:0] IpVersion = (EnableP5 || EnableP7) ? 32'h0001_0001 : 32'h0001_0000;
|
||||
localparam logic [31:0] IpVersion = EnableP7 ? 32'h0001_0002 :
|
||||
(EnableP5 ? 32'h0001_0001 : 32'h0001_0000);
|
||||
localparam logic [31:0] P5Capability0 = EnableP5 ? 32'h0000_01bd : 32'h0000_0198;
|
||||
// EnableP7 advertises the qualified KWS capability after the frozen release
|
||||
// gates pass; the MP3 bit 1 stays zero for the deferred P6.
|
||||
localparam logic [31:0] Capability0 = EnableP7 ? 32'h0000_01fd : P5Capability0;
|
||||
localparam logic [31:0] Capability0 = EnableP7 ? 32'h0000_03fd : P5Capability0;
|
||||
localparam logic [31:0] Capability1 = (EnableP5 || EnableP7) ? 32'h0182_7020 : 32'h0182_7010;
|
||||
localparam logic [31:0] AbiDigest = EnableP7 ? 32'hf500_5d7c : 32'd0;
|
||||
localparam logic [10:0] IrqMask = 11'h7ff;
|
||||
localparam logic [31:0] AbiDigest = EnableP7 ? 32'h63e9_6066 : 32'd0;
|
||||
localparam logic [11:0] IrqMask = EnableP7 ? 12'hfff : 12'h7ff;
|
||||
localparam logic [31:0] TimeoutReset = 32'h0000_ffff;
|
||||
localparam logic [31:0] RingCoalesceReset = 32'h0001_0001;
|
||||
localparam logic [31:0] KwsConfigReset = 32'h0000_0380;
|
||||
@@ -143,13 +151,14 @@ module apu_reg #(
|
||||
logic s_ring_kick;
|
||||
logic s_microcode_load;
|
||||
logic s_model_load;
|
||||
logic s_coefficient_load;
|
||||
logic s_kws_clear_history;
|
||||
logic s_cnt_clear;
|
||||
logic [10:0] s_irq_clear;
|
||||
logic [10:0] s_irq_set;
|
||||
logic [11:0] s_irq_clear;
|
||||
logic [11:0] s_irq_set;
|
||||
|
||||
logic [10:0] s_irq_state_d, s_irq_state_q;
|
||||
logic [10:0] s_irq_en_d, s_irq_en_q;
|
||||
logic [11:0] s_irq_state_d, s_irq_state_q;
|
||||
logic [11:0] s_irq_en_d, s_irq_en_q;
|
||||
logic [31:0] s_err_stat_d, s_err_stat_q;
|
||||
logic [31:0] s_err_addr_d, s_err_addr_q;
|
||||
logic [31:0] s_err_detail_d, s_err_detail_q;
|
||||
@@ -160,6 +169,7 @@ module apu_reg #(
|
||||
logic [7:0][31:0] s_job_cfg_d, s_job_cfg_q;
|
||||
logic [4:0][31:0] s_ring_cfg_d, s_ring_cfg_q;
|
||||
logic [2:0][31:0] s_kws_model_cfg_d, s_kws_model_cfg_q;
|
||||
logic [2:0][31:0] s_kws_coeff_cfg_d, s_kws_coeff_cfg_q;
|
||||
logic [15:0] s_kws_cfg_d, s_kws_cfg_q;
|
||||
logic [31:0] s_kws_input_cfg_d, s_kws_input_cfg_q;
|
||||
logic [1:0] s_kws_control_d, s_kws_control_q;
|
||||
@@ -171,6 +181,7 @@ module apu_reg #(
|
||||
logic s_err_clear;
|
||||
logic s_unused_snapshot;
|
||||
logic [32:0] s_mc_image_end;
|
||||
logic [32:0] s_kws_coeff_image_end;
|
||||
logic s_kws_disable_write;
|
||||
|
||||
function automatic logic [31:0] apply_wstrb(
|
||||
@@ -201,13 +212,14 @@ module apu_reg #(
|
||||
assign apb4.prdata = s_apb4_rdata_q;
|
||||
assign apb4.pslverr = s_apb4_resp_err_q;
|
||||
assign idle_o = core_idle_i;
|
||||
assign irq_o = (s_irq_state_q & s_irq_en_q) != 11'd0;
|
||||
assign irq_o = (s_irq_state_q & s_irq_en_q) != 12'd0;
|
||||
assign soft_reset_o = s_soft_reset;
|
||||
assign abort_o = s_abort;
|
||||
assign direct_start_o = s_direct_start;
|
||||
assign ring_kick_o = s_ring_kick;
|
||||
assign microcode_load_o = s_microcode_load;
|
||||
assign model_load_o = s_model_load;
|
||||
assign coefficient_load_o = s_coefficient_load;
|
||||
assign kws_clear_history_o = s_kws_clear_history;
|
||||
assign counter_clear_o = s_cnt_clear;
|
||||
assign perf_enable_o = s_perf_en_q;
|
||||
@@ -232,6 +244,9 @@ module apu_reg #(
|
||||
assign kws_model_address_o = s_kws_model_cfg_q[0];
|
||||
assign kws_model_size_o = s_kws_model_cfg_q[1];
|
||||
assign kws_model_expected_crc_o = s_kws_model_cfg_q[2];
|
||||
assign kws_coeff_address_o = s_kws_coeff_cfg_q[0];
|
||||
assign kws_coeff_size_o = s_kws_coeff_cfg_q[1];
|
||||
assign kws_coeff_expected_crc_o = s_kws_coeff_cfg_q[2];
|
||||
assign kws_control_o = s_kws_control_q;
|
||||
assign kws_config_o = s_kws_cfg_q;
|
||||
assign kws_input_config_o = s_kws_input_cfg_q;
|
||||
@@ -243,6 +258,7 @@ module apu_reg #(
|
||||
(s_offset == `APB4_APU__ERROR_STATUS) && s_strobed_write[0];
|
||||
assign s_unused_snapshot = ^s_perf_snapshot_q[3] ^ ^s_perf_snapshot_q[7] ^ ^s_perf_snapshot_q[8];
|
||||
assign s_mc_image_end = {1'b0, s_mc_cfg_q[0]} + {1'b0, s_mc_cfg_q[1]} - 1'b1;
|
||||
assign s_kws_coeff_image_end = {1'b0, s_kws_coeff_cfg_q[0]} + {1'b0, s_kws_coeff_cfg_q[1]} - 1'b1;
|
||||
|
||||
always_comb begin
|
||||
s_read_data = 32'd0;
|
||||
@@ -267,8 +283,8 @@ module apu_reg #(
|
||||
kws_status_i[0],
|
||||
mc_status_i[`APB4_APU__MC_STATUS_VALID]
|
||||
};
|
||||
`APB4_APU__IRQ_STATE: s_read_data = {21'd0, s_irq_state_q};
|
||||
`APB4_APU__IRQ_ENABLE: s_read_data = {21'd0, s_irq_en_q};
|
||||
`APB4_APU__IRQ_STATE: s_read_data = {20'd0, s_irq_state_q};
|
||||
`APB4_APU__IRQ_ENABLE: s_read_data = {20'd0, s_irq_en_q};
|
||||
`APB4_APU__ERROR_STATUS: s_read_data = s_err_stat_q;
|
||||
`APB4_APU__ERROR_ADDRESS: s_read_data = s_err_addr_q;
|
||||
`APB4_APU__ERROR_DETAIL: s_read_data = s_err_detail_q;
|
||||
@@ -302,6 +318,38 @@ module apu_reg #(
|
||||
`APB4_APU__KWS_OVERRUN_COUNT: s_read_data = kws_overrun_count_i;
|
||||
`APB4_APU__KWS_MODEL_STATUS: s_read_data = kws_model_status_i;
|
||||
`APB4_APU__KWS_MODEL_ACTUAL_CRC: s_read_data = kws_model_actual_crc_i;
|
||||
`APB4_APU__KWS_COEFF_ADDRESS: begin
|
||||
s_read_data = s_kws_coeff_cfg_q[0];
|
||||
s_read_err = !EnableP7;
|
||||
end
|
||||
`APB4_APU__KWS_COEFF_SIZE: begin
|
||||
s_read_data = s_kws_coeff_cfg_q[1];
|
||||
s_read_err = !EnableP7;
|
||||
end
|
||||
`APB4_APU__KWS_COEFF_EXPECTED_CRC: begin
|
||||
s_read_data = s_kws_coeff_cfg_q[2];
|
||||
s_read_err = !EnableP7;
|
||||
end
|
||||
`APB4_APU__KWS_COEFF_STATUS: begin
|
||||
s_read_data = kws_coeff_status_i;
|
||||
s_read_err = !EnableP7;
|
||||
end
|
||||
`APB4_APU__KWS_COEFF_ACTUAL_CRC: begin
|
||||
s_read_data = kws_coeff_actual_crc_i;
|
||||
s_read_err = !EnableP7;
|
||||
end
|
||||
`APB4_APU__KWS_COEFF_ID_LO: begin
|
||||
s_read_data = kws_coefficient_id_i[31:0];
|
||||
s_read_err = !EnableP7;
|
||||
end
|
||||
`APB4_APU__KWS_COEFF_ID_HI: begin
|
||||
s_read_data = kws_coefficient_id_i[63:32];
|
||||
s_read_err = !EnableP7;
|
||||
end
|
||||
`APB4_APU__KWS_COEFF_CAPACITY: begin
|
||||
s_read_data = 32'h0000_f000;
|
||||
s_read_err = !EnableP7;
|
||||
end
|
||||
`APB4_APU__STREAM_WATERMARK: s_read_data = {16'd0, s_stream_watermark_q};
|
||||
`APB4_APU__MC_IMAGE_ADDRESS: s_read_data = s_mc_cfg_q[0];
|
||||
`APB4_APU__MC_IMAGE_SIZE: s_read_data = s_mc_cfg_q[1];
|
||||
@@ -358,7 +406,8 @@ module apu_reg #(
|
||||
`APB4_APU__PERF_KWS_CYCLES_LO, `APB4_APU__PERF_KWS_CYCLES_HI: s_read_data = 32'd0;
|
||||
`APB4_APU__PERF_FAULTS_LO: s_read_data = s_perf_snapshot_q[9][31:0];
|
||||
`APB4_APU__PERF_FAULTS_HI: s_read_data = s_perf_snapshot_q[9][63:32];
|
||||
`APB4_APU__COMMAND, `APB4_APU__IRQ_TEST, `APB4_APU__RING_DOORBELL: begin
|
||||
`APB4_APU__COMMAND, `APB4_APU__IRQ_TEST, `APB4_APU__RING_DOORBELL,
|
||||
`APB4_APU__KWS_COEFF_COMMAND: begin
|
||||
s_read_data = 32'd0;
|
||||
s_read_err = 1'b1;
|
||||
end
|
||||
@@ -394,7 +443,8 @@ module apu_reg #(
|
||||
(!EnableP7 || !core_idle_i || !quiesce_i || (owner_i != 2'd0) ||
|
||||
(s_kws_model_cfg_q[0][5:0] != 6'd0) ||
|
||||
(s_kws_model_cfg_q[1] != 32'd32768) ||
|
||||
kws_model_status_i[2] || s_kws_control_q[0] ||
|
||||
kws_model_status_i[2] || (kws_coeff_status_i[2:1] != 2'b11) ||
|
||||
s_kws_control_q[0] ||
|
||||
(s_kws_model_cfg_q[0] < s_acl_q[0]) ||
|
||||
(({1'b0, s_kws_model_cfg_q[0]} + 33'd32768 - 33'd1) >
|
||||
{1'b0, s_acl_q[1]}))) ||
|
||||
@@ -404,7 +454,7 @@ module apu_reg #(
|
||||
end
|
||||
`APB4_APU__IRQ_STATE: s_write_err = (s_strobed_write & ~32'(IrqMask)) != 32'd0;
|
||||
`APB4_APU__IRQ_ENABLE: begin
|
||||
s_merged_write = apply_wstrb({21'd0, s_irq_en_q}, apb4.pwdata, apb4.pstrb);
|
||||
s_merged_write = apply_wstrb({20'd0, s_irq_en_q}, apb4.pwdata, apb4.pstrb);
|
||||
s_write_err = (s_merged_write & ~32'(IrqMask)) != 32'd0;
|
||||
end
|
||||
`APB4_APU__IRQ_TEST:
|
||||
@@ -449,6 +499,25 @@ module apu_reg #(
|
||||
s_write_err = (apb4.pstrb != 4'hf) || (apb4.pwdata == 32'd0) ||
|
||||
!core_idle_i || (owner_i != 2'd0) ||
|
||||
((s_offset == `APB4_APU__KWS_MODEL_SIZE) && kws_model_status_i[2]);
|
||||
`APB4_APU__KWS_COEFF_ADDRESS:
|
||||
s_write_err = !EnableP7 || (apb4.pstrb != 4'hf) || (apb4.pwdata[5:0] != 6'd0) ||
|
||||
!core_idle_i || (owner_i != 2'd0) || kws_coeff_status_i[2];
|
||||
`APB4_APU__KWS_COEFF_SIZE:
|
||||
s_write_err = !EnableP7 || (apb4.pstrb != 4'hf) || (apb4.pwdata != 32'd61504) ||
|
||||
!core_idle_i || (owner_i != 2'd0) || kws_coeff_status_i[2];
|
||||
`APB4_APU__KWS_COEFF_EXPECTED_CRC:
|
||||
s_write_err = !EnableP7 || (apb4.pstrb != 4'hf) || !core_idle_i ||
|
||||
(owner_i != 2'd0) || kws_coeff_status_i[2];
|
||||
`APB4_APU__KWS_COEFF_COMMAND: begin
|
||||
s_write_unsupported = !EnableP7;
|
||||
s_write_err = s_write_unsupported || (apb4.pstrb != 4'hf) ||
|
||||
(apb4.pwdata != 32'd1) || !core_idle_i || !quiesce_i ||
|
||||
(owner_i != 2'd0) || s_kws_control_q[0] || kws_coeff_status_i[2] ||
|
||||
kws_model_status_i[1] || (s_kws_coeff_cfg_q[0][5:0] != 6'd0) ||
|
||||
(s_kws_coeff_cfg_q[1] != 32'd61504) || s_kws_coeff_image_end[32] ||
|
||||
(s_kws_coeff_cfg_q[0] < s_acl_q[0]) ||
|
||||
(s_kws_coeff_image_end[31:0] > s_acl_q[1]);
|
||||
end
|
||||
`APB4_APU__JOB_CONTROL: begin
|
||||
s_merged_write = apply_wstrb(s_job_cfg_q[0], apb4.pwdata, apb4.pstrb);
|
||||
s_write_err = (s_merged_write[31:12] != 20'd0) ||
|
||||
@@ -494,7 +563,8 @@ module apu_reg #(
|
||||
(s_kws_control_q[0] &&
|
||||
((apb4.pwdata[1] != s_kws_control_q[1]) || apb4.pwdata[2])) ||
|
||||
((apb4.pwdata[0] && !s_kws_control_q[0]) &&
|
||||
((kws_model_status_i[2:1] != 2'd3) || !core_idle_i)) ||
|
||||
((kws_model_status_i[2:1] != 2'd3) ||
|
||||
(kws_coeff_status_i[2:1] != 2'd3) || !core_idle_i)) ||
|
||||
(!core_idle_i && !s_kws_disable_write);
|
||||
end
|
||||
`APB4_APU__KWS_CONFIG: begin
|
||||
@@ -530,7 +600,9 @@ module apu_reg #(
|
||||
`APB4_APU__KWS_TIMESTAMP_HI, `APB4_APU__KWS_FRAME_COUNT,
|
||||
`APB4_APU__KWS_INFERENCE_COUNT, `APB4_APU__KWS_HIT_COUNT,
|
||||
`APB4_APU__KWS_OVERRUN_COUNT, `APB4_APU__KWS_MODEL_STATUS,
|
||||
`APB4_APU__KWS_MODEL_ACTUAL_CRC,
|
||||
`APB4_APU__KWS_MODEL_ACTUAL_CRC, `APB4_APU__KWS_COEFF_STATUS,
|
||||
`APB4_APU__KWS_COEFF_ACTUAL_CRC, `APB4_APU__KWS_COEFF_ID_LO,
|
||||
`APB4_APU__KWS_COEFF_ID_HI, `APB4_APU__KWS_COEFF_CAPACITY,
|
||||
`APB4_APU__PERF_STATUS,
|
||||
`APB4_APU__PERF_ACTIVE_CYCLES_LO, `APB4_APU__PERF_ACTIVE_CYCLES_HI,
|
||||
`APB4_APU__PERF_INPUT_BYTES_LO, `APB4_APU__PERF_INPUT_BYTES_HI,
|
||||
@@ -561,6 +633,7 @@ module apu_reg #(
|
||||
s_job_cfg_d = s_job_cfg_q;
|
||||
s_ring_cfg_d = s_ring_cfg_q;
|
||||
s_kws_model_cfg_d = s_kws_model_cfg_q;
|
||||
s_kws_coeff_cfg_d = s_kws_coeff_cfg_q;
|
||||
s_kws_cfg_d = s_kws_cfg_q;
|
||||
s_kws_input_cfg_d = s_kws_input_cfg_q;
|
||||
s_kws_control_d = s_kws_control_q;
|
||||
@@ -574,9 +647,10 @@ module apu_reg #(
|
||||
s_ring_kick = 1'b0;
|
||||
s_microcode_load = 1'b0;
|
||||
s_model_load = 1'b0;
|
||||
s_coefficient_load = 1'b0;
|
||||
s_kws_clear_history = 1'b0;
|
||||
s_cnt_clear = 1'b0;
|
||||
s_irq_clear = 11'd0;
|
||||
s_irq_clear = 12'd0;
|
||||
s_irq_set = irq_set_i;
|
||||
|
||||
if (s_write && !s_write_err) begin
|
||||
@@ -591,9 +665,9 @@ module apu_reg #(
|
||||
s_ring_kick = apb4.pwdata[`APB4_APU__COMMAND_RING_KICK];
|
||||
end
|
||||
`APB4_APU__RING_DOORBELL: s_ring_kick = 1'b1;
|
||||
`APB4_APU__IRQ_STATE: s_irq_clear = s_strobed_write[10:0];
|
||||
`APB4_APU__IRQ_ENABLE: s_irq_en_d = s_merged_write[10:0];
|
||||
`APB4_APU__IRQ_TEST: s_irq_set = s_irq_set | apb4.pwdata[10:0];
|
||||
`APB4_APU__IRQ_STATE: s_irq_clear = s_strobed_write[11:0];
|
||||
`APB4_APU__IRQ_ENABLE: s_irq_en_d = s_merged_write[11:0];
|
||||
`APB4_APU__IRQ_TEST: s_irq_set = s_irq_set | apb4.pwdata[11:0];
|
||||
`APB4_APU__ERROR_STATUS: begin
|
||||
if (s_strobed_write[0]) begin
|
||||
s_err_stat_d = 32'd0;
|
||||
@@ -628,6 +702,10 @@ module apu_reg #(
|
||||
`APB4_APU__KWS_MODEL_ADDRESS: s_kws_model_cfg_d[0] = apb4.pwdata;
|
||||
`APB4_APU__KWS_MODEL_SIZE: s_kws_model_cfg_d[1] = apb4.pwdata;
|
||||
`APB4_APU__KWS_MODEL_EXPECTED_CRC: s_kws_model_cfg_d[2] = apb4.pwdata;
|
||||
`APB4_APU__KWS_COEFF_ADDRESS: s_kws_coeff_cfg_d[0] = apb4.pwdata;
|
||||
`APB4_APU__KWS_COEFF_SIZE: s_kws_coeff_cfg_d[1] = apb4.pwdata;
|
||||
`APB4_APU__KWS_COEFF_EXPECTED_CRC: s_kws_coeff_cfg_d[2] = apb4.pwdata;
|
||||
`APB4_APU__KWS_COEFF_COMMAND: s_coefficient_load = 1'b1;
|
||||
`APB4_APU__KWS_CONTROL: begin
|
||||
if (s_kws_disable_write) begin
|
||||
if (kws_disable_done_i) begin
|
||||
@@ -694,8 +772,8 @@ module apu_reg #(
|
||||
s_irq_state_d = (s_irq_state_d & ~s_irq_clear) | s_irq_set;
|
||||
|
||||
if (s_soft_reset || resource_reset_apply_i) begin
|
||||
s_irq_state_d = 11'd0;
|
||||
s_irq_en_d = 11'd0;
|
||||
s_irq_state_d = 12'd0;
|
||||
s_irq_en_d = 12'd0;
|
||||
s_err_stat_d = 32'd0;
|
||||
s_err_addr_d = 32'd0;
|
||||
s_err_detail_d = 32'd0;
|
||||
@@ -706,6 +784,7 @@ module apu_reg #(
|
||||
s_job_cfg_d = '0;
|
||||
s_ring_cfg_d = RingReset;
|
||||
s_kws_model_cfg_d = '0;
|
||||
s_kws_coeff_cfg_d = '0;
|
||||
s_kws_cfg_d = KwsConfigReset[15:0];
|
||||
s_kws_control_d = 2'd0;
|
||||
s_kws_input_cfg_d = KwsInputConfigReset;
|
||||
@@ -758,7 +837,7 @@ module apu_reg #(
|
||||
.dat_o (s_apb4_rdata_q)
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(11)
|
||||
.DATA_WIDTH(12)
|
||||
) u_irq_state_dffr (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
@@ -766,7 +845,7 @@ module apu_reg #(
|
||||
.dat_o (s_irq_state_q)
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(11)
|
||||
.DATA_WIDTH(12)
|
||||
) u_irq_enable_dffr (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
@@ -856,6 +935,14 @@ module apu_reg #(
|
||||
.dat_i (s_kws_model_cfg_d),
|
||||
.dat_o (s_kws_model_cfg_q)
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(3 * 32)
|
||||
) u_kws_coeff_cfg_dffr (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_kws_coeff_cfg_d),
|
||||
.dat_o (s_kws_coeff_cfg_q)
|
||||
);
|
||||
dffrc #(
|
||||
.DATA_WIDTH(16),
|
||||
.RESET_VAL (KwsConfigReset[15:0])
|
||||
|
||||
@@ -130,8 +130,11 @@
|
||||
/ip/multimedia/apu_ring_scheduler.sv
|
||||
/ip/multimedia/apu_stream_router.sv
|
||||
/ip/multimedia/apu_control_store.sv
|
||||
/ip/multimedia/apu_proof_memo.sv
|
||||
/ip/multimedia/apu_microcode_loader.sv
|
||||
/ip/multimedia/apu_local_sram.sv
|
||||
/ip/multimedia/apu_kws_coeff_store.sv
|
||||
/ip/multimedia/apu_kws_coeff_loader.sv
|
||||
/ip/multimedia/apu_kws_engine.sv
|
||||
/ip/multimedia/apu_kws_sram_client.sv
|
||||
/ip/multimedia/apu_kws_model_loader.sv
|
||||
|
||||
@@ -97,6 +97,13 @@ module apu_kws_formal_design (
|
||||
.local_data_o (),
|
||||
.local_strb_o (),
|
||||
.local_ready_i (1'b1),
|
||||
.profile_request_valid_o (),
|
||||
.profile_request_ready_i (1'b1),
|
||||
.profile_index_o (),
|
||||
.profile_response_valid_i(1'b0),
|
||||
.profile_response_ready_o(),
|
||||
.profile_data_i (32'd0),
|
||||
.profile_fault_i (1'b0),
|
||||
.busy_o (loader_busy),
|
||||
.valid_o (valid),
|
||||
.lock_o (lock),
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
module apu_p9_formal_design (
|
||||
input logic clk_i,
|
||||
output logic rst_n_i,
|
||||
output logic f_past_valid,
|
||||
output logic busy,
|
||||
output logic valid,
|
||||
output logic lock,
|
||||
output logic [31:0] actual_crc,
|
||||
output logic [63:0] coefficient_id,
|
||||
output logic [31:0] status,
|
||||
output logic [ 5:0] error_code,
|
||||
output logic done,
|
||||
output logic abort_done,
|
||||
output logic dma_request_valid,
|
||||
output logic [31:0] dma_request_address,
|
||||
output logic [31:0] dma_request_bytes,
|
||||
output logic store_active,
|
||||
output logic store_req,
|
||||
output logic store_write,
|
||||
output logic [13:0] store_addr
|
||||
);
|
||||
logic s_start_q;
|
||||
(* anyseq *) logic s_abort;
|
||||
(* anyseq *) logic s_dma_request_ready;
|
||||
(* anyseq *) logic s_dma_valid;
|
||||
(* anyseq *) logic s_dma_done;
|
||||
(* anyseq *) logic s_dma_error;
|
||||
(* anyseq *) logic [31:0] s_dma_data;
|
||||
(* anyseq *) logic s_store_ready;
|
||||
(* anyseq *) logic s_store_valid;
|
||||
(* anyseq *) logic [31:0] s_store_data;
|
||||
(* anyseq *) logic s_store_fault;
|
||||
|
||||
apu_kws_coeff_loader u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.start_i (s_start_q),
|
||||
.abort_i (s_abort),
|
||||
.soft_reset_i (1'b0),
|
||||
.resource_reset_request_i(1'b0),
|
||||
.resource_reset_i (1'b0),
|
||||
.quiesce_i (1'b1),
|
||||
.address_i (32'h1000_0000),
|
||||
.size_i (32'd61504),
|
||||
.expected_crc_i (32'h25e7_c27d),
|
||||
.acl_base_i (32'h1000_0000),
|
||||
.acl_limit_i (32'h1000_f03f),
|
||||
.dma_request_valid_o (dma_request_valid),
|
||||
.dma_request_ready_i (s_dma_request_ready),
|
||||
.dma_request_address_o (dma_request_address),
|
||||
.dma_request_bytes_o (dma_request_bytes),
|
||||
.dma_data_i (s_dma_data),
|
||||
.dma_keep_i (4'hf),
|
||||
.dma_last_i (1'b0),
|
||||
.dma_valid_i (s_dma_valid),
|
||||
.dma_ready_o (),
|
||||
.dma_done_i (s_dma_done),
|
||||
.dma_error_i (s_dma_error),
|
||||
.dma_error_code_i (6'd15),
|
||||
.dma_error_stage_i (4'd3),
|
||||
.dma_error_resp_i (2'd2),
|
||||
.dma_error_address_i (32'h1000_0040),
|
||||
.store_active_o (store_active),
|
||||
.store_req_o (store_req),
|
||||
.store_write_o (store_write),
|
||||
.store_addr_o (store_addr),
|
||||
.store_data_o (),
|
||||
.store_ready_i (s_store_ready),
|
||||
.store_valid_i (s_store_valid),
|
||||
.store_data_i (s_store_data),
|
||||
.store_fault_i (s_store_fault),
|
||||
.busy_o (busy),
|
||||
.valid_o (valid),
|
||||
.lock_o (lock),
|
||||
.actual_crc_o (actual_crc),
|
||||
.coefficient_id_o (coefficient_id),
|
||||
.status_o (status),
|
||||
.error_code_o (error_code),
|
||||
.error_stage_o (),
|
||||
.error_resp_o (),
|
||||
.error_address_o (),
|
||||
.error_detail_o (),
|
||||
.done_o (done),
|
||||
.abort_done_o (abort_done)
|
||||
);
|
||||
|
||||
initial begin
|
||||
rst_n_i = 1'b0;
|
||||
f_past_valid = 1'b0;
|
||||
s_start_q = 1'b0;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
rst_n_i <= 1'b1;
|
||||
f_past_valid <= 1'b1;
|
||||
s_start_q <= !f_past_valid;
|
||||
if (rst_n_i) begin
|
||||
assume (!s_dma_valid || busy);
|
||||
assume (!s_dma_done || busy);
|
||||
assume (!s_store_valid || store_active);
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
module apu_p9_formal;
|
||||
(* anyseq *) (* gclk *) reg clk_i;
|
||||
wire rst_n_i;
|
||||
wire f_past_valid;
|
||||
wire busy;
|
||||
wire valid;
|
||||
wire lock;
|
||||
wire [31:0] actual_crc;
|
||||
wire [63:0] coefficient_id;
|
||||
wire [31:0] status;
|
||||
wire [5:0] error_code;
|
||||
wire done;
|
||||
wire abort_done;
|
||||
wire dma_request_valid;
|
||||
wire [31:0] dma_request_address;
|
||||
wire [31:0] dma_request_bytes;
|
||||
wire store_active;
|
||||
wire store_req;
|
||||
wire store_write;
|
||||
wire [13:0] store_addr;
|
||||
|
||||
apu_p9_formal_design u_design (.*);
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (rst_n_i) begin
|
||||
assert (valid == lock);
|
||||
assert (!(done && abort_done));
|
||||
assert (!store_req || store_active);
|
||||
assert (!store_req || (store_addr < 14'd15360));
|
||||
if (store_write) assert (store_active && busy);
|
||||
if (valid) begin
|
||||
assert (actual_crc == 32'h25e7_c27d);
|
||||
assert (coefficient_id == 64'h806c_780d_8a0b_038d);
|
||||
assert (status[2:1] == 2'b11);
|
||||
end
|
||||
if (done && valid) assert (!busy);
|
||||
if (done && (error_code != 6'd0)) begin
|
||||
assert (!valid);
|
||||
assert (!lock);
|
||||
end
|
||||
if (dma_request_valid) begin
|
||||
assert (busy);
|
||||
assert ((dma_request_address == 32'h1000_0000) || (dma_request_address == 32'h1000_0040));
|
||||
assert ((dma_request_bytes == 32'd64) || (dma_request_bytes == 32'd61440));
|
||||
end
|
||||
end
|
||||
cover (rst_n_i && dma_request_valid);
|
||||
cover (rst_n_i && abort_done && !done);
|
||||
end
|
||||
endmodule
|
||||
@@ -324,6 +324,11 @@ def source_files(target: str) -> list[Path]:
|
||||
MULTIMEDIA / "apu_codec_controller.sv",
|
||||
SCRIPT_DIR / "apu_kws_formal.sv",
|
||||
]
|
||||
if target == "apu_p9":
|
||||
return [
|
||||
MULTIMEDIA / "apu_kws_coeff_loader.sv",
|
||||
SCRIPT_DIR / "apu_p9_formal.sv",
|
||||
]
|
||||
if target == "apu_primitives" or target in APU_PRIMITIVE_SCENARIOS:
|
||||
return [
|
||||
COMMON_RTL / "utils/fifo.sv",
|
||||
@@ -342,6 +347,7 @@ def source_files(target: str) -> list[Path]:
|
||||
return [
|
||||
ROOT / "rtl/tech/tc_sram.sv",
|
||||
MULTIMEDIA / "apu_microcode_pkg.sv",
|
||||
MULTIMEDIA / "apu_proof_memo.sv",
|
||||
MULTIMEDIA / "apu_microcode_loader.sv",
|
||||
SCRIPT_DIR / "apu_loader_formal.sv",
|
||||
]
|
||||
@@ -453,6 +459,7 @@ def parse_args() -> argparse.Namespace:
|
||||
"apu",
|
||||
"apu_codec",
|
||||
"apu_kws",
|
||||
"apu_p9",
|
||||
"apu_primitives",
|
||||
*APU_PRIMITIVE_SCENARIOS,
|
||||
*APU_LOADER_SCENARIOS,
|
||||
|
||||
+14
-8
@@ -25,10 +25,11 @@ FORMAL_APU_CODEC_DEPTH ?= 32
|
||||
FORMAL_APU_LOADER_DEPTH ?= 128
|
||||
FORMAL_APU_LOADER_COVER_DEPTH ?= 128
|
||||
FORMAL_APU_P5_LOADER_DEPTH ?= 48
|
||||
FORMAL_APU_P5_LOADER_COVER_DEPTH ?= 145
|
||||
FORMAL_APU_P5_LOADER_COVER_SKIP = $(if $(filter apu_loader_p5_success,$*),120,$(if $(filter apu_loader_p5_bound,$*),107,$(if $(filter apu_loader_p5_memo_fallback,$*),105,30)))
|
||||
FORMAL_APU_P5_LOADER_COVER_DEPTH ?= 160
|
||||
FORMAL_APU_P5_LOADER_COVER_SKIP = $(if $(filter apu_loader_p5_success,$*),130,$(if $(filter apu_loader_p5_bound,$*),110,$(if $(filter apu_loader_p5_memo_fallback,$*),115,30)))
|
||||
FORMAL_APU_SEQUENCER_DEPTH ?= 38
|
||||
FORMAL_APU_KWS_DEPTH ?= 32
|
||||
FORMAL_APU_P9_DEPTH ?= 40
|
||||
FORMAL_GATEWAY_A_DEPTH ?= 20
|
||||
FORMAL_SDIO_DEPTH ?= 20
|
||||
FORMAL_SDIO_COVER_DEPTH ?= 24
|
||||
@@ -42,20 +43,21 @@ FORMAL_GA2D_TIMEOUT ?= 7200
|
||||
FORMAL_NPU_DMA_TIMEOUT ?= 7200
|
||||
FORMAL_NPU_P6_TIMEOUT ?= 7200
|
||||
FORMAL_APU_TIMEOUT ?= 300
|
||||
FORMAL_APU_P5_LOADER_TIMEOUT ?= 600
|
||||
FORMAL_GATEWAY_A_TIMEOUT ?= 120
|
||||
FORMAL_SDIO_TIMEOUT ?= 120
|
||||
FORMAL_APU_P5_LOADER_TARGETS := apu_loader_p5_success apu_loader_p5_bound apu_loader_p5_abort apu_loader_p5_resource_reset apu_loader_p5_memo_fallback
|
||||
FORMAL_APU_LOADER_TARGETS := apu_loader_success apu_loader_header_range apu_loader_descriptor_range apu_loader_crc apu_loader_control_flow apu_loader_abort apu_loader_resource_reset $(FORMAL_APU_P5_LOADER_TARGETS)
|
||||
FORMAL_APU_PRIMITIVE_TARGETS := apu_primitives_invalid_read apu_primitives_local apu_primitives_local_overflow apu_primitives_kernel apu_primitives_input_fifo apu_primitives_alignment apu_primitives_output_fifo
|
||||
FORMAL_TARGET_TIMEOUT = $(if $(filter ga2d,$*),$(FORMAL_GA2D_TIMEOUT),$(if $(filter npu_dma,$*),$(FORMAL_NPU_DMA_TIMEOUT),$(if $(filter npu_context npu_control,$*),$(FORMAL_NPU_P6_TIMEOUT),$(if $(filter dma,$*),$(FORMAL_DMA_TIMEOUT),$(if $(filter apu apu_codec apu_kws apu_primitives apu_sequencer $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS),$*),$(FORMAL_APU_TIMEOUT),$(if $(filter gateway_a,$*),$(FORMAL_GATEWAY_A_TIMEOUT),$(if $(filter sdio,$*),$(FORMAL_SDIO_TIMEOUT),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_TIMEOUT),$(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(if $(filter i2c,$*),$(FORMAL_I2C_TIMEOUT),$(FORMAL_TIMEOUT)))))))))))
|
||||
FORMAL_APU_TARGET_DEPTH = $(if $(filter $(FORMAL_APU_P5_LOADER_TARGETS),$*),$(FORMAL_APU_P5_LOADER_DEPTH),$(if $(filter $(FORMAL_APU_LOADER_TARGETS),$*),$(FORMAL_APU_LOADER_DEPTH),$(if $(filter apu_kws,$*),$(FORMAL_APU_KWS_DEPTH),$(if $(filter apu_sequencer,$*),$(FORMAL_APU_SEQUENCER_DEPTH),$(if $(filter apu_codec,$*),$(FORMAL_APU_CODEC_DEPTH),$(FORMAL_APU_DEPTH))))))
|
||||
FORMAL_TARGET_TIMEOUT = $(if $(filter ga2d,$*),$(FORMAL_GA2D_TIMEOUT),$(if $(filter npu_dma,$*),$(FORMAL_NPU_DMA_TIMEOUT),$(if $(filter npu_context npu_control,$*),$(FORMAL_NPU_P6_TIMEOUT),$(if $(filter $(FORMAL_APU_P5_LOADER_TARGETS),$*),$(FORMAL_APU_P5_LOADER_TIMEOUT),$(if $(filter dma,$*),$(FORMAL_DMA_TIMEOUT),$(if $(filter apu apu_codec apu_kws apu_p9 apu_primitives apu_sequencer $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS),$*),$(FORMAL_APU_TIMEOUT),$(if $(filter gateway_a,$*),$(FORMAL_GATEWAY_A_TIMEOUT),$(if $(filter sdio,$*),$(FORMAL_SDIO_TIMEOUT),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_TIMEOUT),$(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(if $(filter i2c,$*),$(FORMAL_I2C_TIMEOUT),$(FORMAL_TIMEOUT))))))))))))
|
||||
FORMAL_APU_TARGET_DEPTH = $(if $(filter $(FORMAL_APU_P5_LOADER_TARGETS),$*),$(FORMAL_APU_P5_LOADER_DEPTH),$(if $(filter $(FORMAL_APU_LOADER_TARGETS),$*),$(FORMAL_APU_LOADER_DEPTH),$(if $(filter apu_p9,$*),$(FORMAL_APU_P9_DEPTH),$(if $(filter apu_kws,$*),$(FORMAL_APU_KWS_DEPTH),$(if $(filter apu_sequencer,$*),$(FORMAL_APU_SEQUENCER_DEPTH),$(if $(filter apu_codec,$*),$(FORMAL_APU_CODEC_DEPTH),$(FORMAL_APU_DEPTH)))))))
|
||||
FORMAL_APU_TARGET_COVER_DEPTH = $(if $(filter $(FORMAL_APU_P5_LOADER_TARGETS),$*),$(FORMAL_APU_P5_LOADER_COVER_DEPTH),$(if $(filter $(FORMAL_APU_LOADER_TARGETS),$*),$(FORMAL_APU_LOADER_COVER_DEPTH),$(FORMAL_APU_TARGET_DEPTH)))
|
||||
FORMAL_TARGET_TOP = $(if $(filter $(FORMAL_APU_LOADER_TARGETS),$*),apu_loader,$(if $(filter $(FORMAL_APU_PRIMITIVE_TARGETS),$*),apu_primitives,$*))
|
||||
FORMAL_PROPERTIES_SOURCE = $(if $(filter ga2d,$*),$(RTL_PATH)/formal/ga2d_formal.sv,$(RTL_PATH)/formal/$(FORMAL_TARGET_TOP)_formal_props.sv)
|
||||
FORMAL_PROPERTIES_DEFINE = $(if $(filter ga2d,$*),--properties-define GA2D_FORMAL_PROPERTIES,)
|
||||
FORMAL_COMPACT_COVER_TARGETS := i2c opipsram dma ga2d npu_context npu_control $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS)
|
||||
FORMAL_P6_TARGETS := npu_context npu_control
|
||||
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint dvp i2s psram onchip_ram opipsram dma ga2d apu apu_codec apu_kws $(FORMAL_APU_PRIMITIVE_TARGETS) $(FORMAL_APU_LOADER_TARGETS) apu_sequencer gateway_a sdio npu_dma
|
||||
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint dvp i2s psram onchip_ram opipsram dma ga2d apu apu_codec apu_kws apu_p9 $(FORMAL_APU_PRIMITIVE_TARGETS) $(FORMAL_APU_LOADER_TARGETS) apu_sequencer gateway_a sdio npu_dma
|
||||
FORMAL_ALL_TARGETS := $(FORMAL_TARGETS) $(FORMAL_P6_TARGETS)
|
||||
FORMAL_FILELIST_GENERATOR := $(RTL_PATH)/formal/generate_formal_filelist.py
|
||||
FORMAL_SBY_GENERATOR := $(RTL_PATH)/formal/generate_sby_config.py
|
||||
@@ -102,6 +104,8 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
|
||||
$(RTL_PATH)/formal/apu_codec_formal_props.sv \
|
||||
$(RTL_PATH)/formal/apu_kws_formal.sv \
|
||||
$(RTL_PATH)/formal/apu_kws_formal_props.sv \
|
||||
$(RTL_PATH)/formal/apu_p9_formal.sv \
|
||||
$(RTL_PATH)/formal/apu_p9_formal_props.sv \
|
||||
$(RTL_PATH)/formal/apu_primitives_formal.sv \
|
||||
$(RTL_PATH)/formal/apu_primitives_formal_props.sv \
|
||||
$(RTL_PATH)/formal/apu_loader_formal.sv \
|
||||
@@ -248,14 +252,14 @@ $(FORMAL_DIR)/%/prove.sby: $(FORMAL_DIR)/%/design.v \
|
||||
$(FORMAL_SBY_GENERATOR)
|
||||
python3 $(FORMAL_SBY_GENERATOR) --top $(FORMAL_TARGET_TOP)_formal --input $< \
|
||||
--properties $(FORMAL_PROPERTIES_SOURCE) $(FORMAL_PROPERTIES_DEFINE) --solver $(FORMAL_SOLVER) \
|
||||
--mode $(if $(filter ga2d,$*),bmc,$(if $(filter dma npu_dma npu_context npu_control apu apu_codec apu_kws apu_primitives apu_sequencer $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS) gateway_a sdio onchip_ram,$*),bmc,prove)) --depth $(if $(filter ga2d,$*),$(FORMAL_GA2D_DEPTH),$(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter npu_dma,$*),$(FORMAL_NPU_DMA_DEPTH),$(if $(filter npu_context,$*),$(FORMAL_NPU_CONTEXT_DEPTH),$(if $(filter npu_control,$*),$(FORMAL_NPU_CONTROL_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_DEPTH),$(if $(filter apu apu_codec apu_kws apu_primitives apu_sequencer $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS),$*),$(FORMAL_APU_TARGET_DEPTH),$(if $(filter gateway_a,$*),$(FORMAL_GATEWAY_A_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))))))))))) --output $@
|
||||
--mode $(if $(filter ga2d,$*),bmc,$(if $(filter dma npu_dma npu_context npu_control apu apu_codec apu_kws apu_p9 apu_primitives apu_sequencer $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS) gateway_a sdio onchip_ram,$*),bmc,prove)) --depth $(if $(filter ga2d,$*),$(FORMAL_GA2D_DEPTH),$(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter npu_dma,$*),$(FORMAL_NPU_DMA_DEPTH),$(if $(filter npu_context,$*),$(FORMAL_NPU_CONTEXT_DEPTH),$(if $(filter npu_control,$*),$(FORMAL_NPU_CONTROL_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_DEPTH),$(if $(filter apu apu_codec apu_kws apu_p9 apu_primitives apu_sequencer $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS),$*),$(FORMAL_APU_TARGET_DEPTH),$(if $(filter gateway_a,$*),$(FORMAL_GATEWAY_A_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))))))))))) --output $@
|
||||
@if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi
|
||||
|
||||
$(FORMAL_DIR)/%/cover.sby: $(FORMAL_DIR)/%/design.v \
|
||||
$(FORMAL_SBY_GENERATOR)
|
||||
python3 $(FORMAL_SBY_GENERATOR) --top $(FORMAL_TARGET_TOP)_formal --input $< \
|
||||
--properties $(FORMAL_PROPERTIES_SOURCE) $(FORMAL_PROPERTIES_DEFINE) --solver $(FORMAL_SOLVER) \
|
||||
--mode cover --depth $(if $(filter ga2d,$*),$(FORMAL_GA2D_COVER_DEPTH),$(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter npu_dma,$*),$(FORMAL_NPU_DMA_COVER_DEPTH),$(if $(filter npu_context,$*),$(FORMAL_NPU_CONTEXT_DEPTH),$(if $(filter npu_control,$*),$(FORMAL_NPU_CONTROL_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_COVER_DEPTH),$(if $(filter apu apu_codec apu_kws apu_primitives apu_sequencer $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS),$*),$(FORMAL_APU_TARGET_COVER_DEPTH),$(if $(filter gateway_a,$*),$(FORMAL_GATEWAY_A_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_COVER_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))))))))))) \
|
||||
--mode cover --depth $(if $(filter ga2d,$*),$(FORMAL_GA2D_COVER_DEPTH),$(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter npu_dma,$*),$(FORMAL_NPU_DMA_COVER_DEPTH),$(if $(filter npu_context,$*),$(FORMAL_NPU_CONTEXT_DEPTH),$(if $(filter npu_control,$*),$(FORMAL_NPU_CONTROL_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_COVER_DEPTH),$(if $(filter apu apu_codec apu_kws apu_p9 apu_primitives apu_sequencer $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS),$*),$(FORMAL_APU_TARGET_COVER_DEPTH),$(if $(filter gateway_a,$*),$(FORMAL_GATEWAY_A_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_COVER_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))))))))))) \
|
||||
$(if $(filter $(FORMAL_APU_P5_LOADER_TARGETS),$*),--skip $(FORMAL_APU_P5_LOADER_COVER_SKIP)) \
|
||||
$(if $(filter $(FORMAL_COMPACT_COVER_TARGETS),$*),--no-vcd) --output $@
|
||||
@if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi
|
||||
@@ -321,6 +325,8 @@ formal: $(FORMAL_RESULT) | manifest
|
||||
|
||||
formal-apu-kws: $(FORMAL_DIR)/apu_kws/.stamp | manifest
|
||||
|
||||
formal-apu-p9: $(FORMAL_DIR)/apu_p9/.stamp | manifest
|
||||
|
||||
formal-bus: $(FORMAL_DIR)/bus/.stamp | manifest
|
||||
|
||||
formal-rib-adapter: $(FORMAL_DIR)/rib_adapter/.stamp | manifest
|
||||
@@ -384,4 +390,4 @@ formal-doctor:
|
||||
formal-clean:
|
||||
python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(FORMAL_DIR)
|
||||
|
||||
.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-clint formal-dvp formal-i2s formal-psram formal-onchip-ram formal-opipsram formal-dma formal-ga2d formal-apu formal-apu-kws formal-apu-loader formal-apu-loader-p5 formal-apu-sequencer formal-gateway-a formal-sdio formal-npu_dma formal-npu-context formal-npu-control formal-npu-p6 formal-doctor formal-clean
|
||||
.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-clint formal-dvp formal-i2s formal-psram formal-onchip-ram formal-opipsram formal-dma formal-ga2d formal-apu formal-apu-kws formal-apu-p9 formal-apu-loader formal-apu-loader-p5 formal-apu-sequencer formal-gateway-a formal-sdio formal-npu_dma formal-npu-context formal-npu-control formal-npu-p6 formal-doctor formal-clean
|
||||
@@ -147,6 +147,8 @@
|
||||
"apu_entropy_engine.sv",
|
||||
"apu_kernel_engine.sv",
|
||||
"apu_kws_apum_profile.svh",
|
||||
"apu_kws_coeff_loader.sv",
|
||||
"apu_kws_coeff_store.sv",
|
||||
"apu_kws_engine.sv",
|
||||
"apu_kws_model_loader.sv",
|
||||
"apu_kws_rom.svh",
|
||||
@@ -155,6 +157,7 @@
|
||||
"apu_microcode_loader.sv",
|
||||
"apu_microcode_pkg.sv",
|
||||
"apu_primitive_dispatcher.sv",
|
||||
"apu_proof_memo.sv",
|
||||
"apu_reconstruction_engine.sv",
|
||||
"apu_resampler.sv",
|
||||
"apu_ring_scheduler.sv",
|
||||
|
||||
@@ -26,6 +26,35 @@ Scripts are part of the build contract. Prefer existing helpers over ad-hoc
|
||||
shell behavior, preserve structured JSON results, and keep setup/download
|
||||
behavior controlled by `dependencies/dependencies.lock.json`.
|
||||
|
||||
`generate_apu_kws_rtl_constants.py` also emits the frozen APU-P9 APUC image
|
||||
and 15-bank layout manifest. `apu_kws_coeff.py` owns the independent APUC 1.0
|
||||
schema and release validator; generated assets remain below the selected
|
||||
variant's `apu/coefficients/` directory.
|
||||
`apu_p9_evidence.py` initializes or assembles the six frozen P9 evidence files;
|
||||
missing qualifying runs stay explicitly `unrun` rather than becoming passes.
|
||||
`apu_p9_memory_ab.py` performs the like-for-like inferred-memory, macro-count,
|
||||
synthesis-time and process-tree peak-RSS comparison from two explicit variant
|
||||
roots; it never creates or switches Git worktrees itself.
|
||||
|
||||
The frozen P9 qualification entry points are explicit and do not turn smoke
|
||||
runs into release evidence:
|
||||
|
||||
```sh
|
||||
make CONFIG=configs/ci/ihp130.mk setup-apu-kws-reference apu-p9-coefficients apu-p9-evidence
|
||||
python3 scripts/run_apu_p7_kws_rtl.py \
|
||||
--build-dir build/<candidate>/apu/p9/accuracy --profile ihp130-p9
|
||||
python3 scripts/run_apu_p7_concurrent_rtl.py \
|
||||
--build-dir build/<candidate>/apu/p9/concurrent --seconds 60
|
||||
make CONFIG=configs/ci/ihp130.mk apu-p9-memory-ab \
|
||||
APU_P9_BASELINE_ROOT=build/<baseline> APU_P9_CANDIDATE_ROOT=build/<candidate>
|
||||
```
|
||||
|
||||
Both A/B roots must come from clean committed revisions; the baseline is the
|
||||
frozen pre-P9 revision and both configurations must enable P7 with the same
|
||||
IHP130 tool, PDK, recipe, and dependency inputs. Assemble the six final reports
|
||||
with `scripts/apu_p9_evidence.py assemble`; a `passed` input is accepted only
|
||||
when its report-specific checks and named source artifacts are complete.
|
||||
|
||||
`setup_npu_reference.py` prepares the locked KWS/VWW inputs and the existing
|
||||
TensorFlow/gemmlowp reference sources. `npu_framework_reference.py` builds the
|
||||
host-only adapter in `tests/cpp/npu_framework_reference.cc`; its integer kernels
|
||||
|
||||
@@ -51,6 +51,7 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "scripts"))
|
||||
|
||||
import apu_kws # noqa: E402
|
||||
import apu_kws_coeff # noqa: E402
|
||||
from apu_isa import ( # noqa: E402
|
||||
APUMC_ABI_V2,
|
||||
ControlOpcode,
|
||||
@@ -73,6 +74,7 @@ SECTION_ORDER = (
|
||||
"formats",
|
||||
"apumc",
|
||||
"apum",
|
||||
"apuc",
|
||||
"isa",
|
||||
"mp3_stub",
|
||||
)
|
||||
@@ -147,16 +149,26 @@ def _rtl_discovery_values() -> dict[str, int]:
|
||||
"""Extract the release-selected discovery localparams from apu_reg.sv."""
|
||||
|
||||
text = RTL_REGISTER.read_text(encoding="utf-8")
|
||||
branch = r"localparam logic \[31:0\] %s = (?:\([^)]*\)|\w+) \? 32'h([0-9a-fA-F_]+) :"
|
||||
return {
|
||||
"IP_ID_VALUE": _hex_literal(
|
||||
r"localparam logic \[31:0\] IpId = 32'h([0-9a-fA-F_]+);", text
|
||||
),
|
||||
"IP_VERSION_VALUE": _hex_literal(branch % "IpVersion", text),
|
||||
"CAPABILITY0_IMPLEMENTED": _hex_literal(branch % "Capability0", text),
|
||||
"CAPABILITY1_IMPLEMENTED": _hex_literal(branch % "Capability1", text),
|
||||
"IP_VERSION_VALUE": _hex_literal(
|
||||
r"localparam logic \[31:0\] IpVersion = EnableP7 \? 32'h([0-9a-fA-F_]+) :",
|
||||
text,
|
||||
),
|
||||
"CAPABILITY0_IMPLEMENTED": _hex_literal(
|
||||
r"localparam logic \[31:0\] Capability0 = EnableP7 \? 32'h([0-9a-fA-F_]+) :",
|
||||
text,
|
||||
),
|
||||
"CAPABILITY1_IMPLEMENTED": _hex_literal(
|
||||
r"localparam logic \[31:0\] Capability1 = \(EnableP5 \|\| EnableP7\) \? "
|
||||
r"32'h([0-9a-fA-F_]+) :",
|
||||
text,
|
||||
),
|
||||
"IRQ_ALL": _hex_literal(
|
||||
r"localparam logic \[10:0\] IrqMask = 11'h([0-9a-fA-F_]+);", text
|
||||
r"localparam logic \[11:0\] IrqMask = EnableP7 \? 12'h([0-9a-fA-F_]+) :",
|
||||
text,
|
||||
),
|
||||
}
|
||||
|
||||
@@ -350,6 +362,32 @@ def canonical_inventory() -> dict[str, dict[str, int]]:
|
||||
{name: apb[name] for name in ("APUM_MAGIC", "APUM_ABI", "APUM_IMAGE_BYTES",
|
||||
"APUM_PAYLOAD_CRC")},
|
||||
)
|
||||
_require_equal(
|
||||
"APUC container/SVH",
|
||||
{
|
||||
"APUC_MAGIC": apu_kws_coeff.APUC_MAGIC,
|
||||
"APUC_ABI": apu_kws_coeff.APUC_ABI,
|
||||
"APUC_IMAGE_BYTES": apu_kws_coeff.APUC_BYTES,
|
||||
"APUC_HEADER_BYTES": apu_kws_coeff.APUC_HEADER_BYTES,
|
||||
"APUC_PAYLOAD_BYTES": apu_kws_coeff.APUC_PAYLOAD_BYTES,
|
||||
"APUC_PAYLOAD_CRC": apu_kws_coeff.APUC_PAYLOAD_CRC,
|
||||
"APUC_COEFFICIENT_ID_LO": apu_kws_coeff.APUC_COEFFICIENT_ID[0],
|
||||
"APUC_COEFFICIENT_ID_HI": apu_kws_coeff.APUC_COEFFICIENT_ID[1],
|
||||
},
|
||||
{
|
||||
name: apb[name]
|
||||
for name in (
|
||||
"APUC_MAGIC",
|
||||
"APUC_ABI",
|
||||
"APUC_IMAGE_BYTES",
|
||||
"APUC_HEADER_BYTES",
|
||||
"APUC_PAYLOAD_BYTES",
|
||||
"APUC_PAYLOAD_CRC",
|
||||
"APUC_COEFFICIENT_ID_LO",
|
||||
"APUC_COEFFICIENT_ID_HI",
|
||||
)
|
||||
},
|
||||
)
|
||||
discovery_c = _c_discovery_defines()
|
||||
discovery_rtl = _rtl_discovery_values()
|
||||
_require_equal("discovery C/RTL", discovery_rtl,
|
||||
@@ -400,6 +438,22 @@ def canonical_inventory() -> dict[str, dict[str, int]]:
|
||||
"parameter_end": apu_kws.APUM_PARAMETER_END,
|
||||
"payload_crc": apu_kws.APUM_PAYLOAD_CRC,
|
||||
}
|
||||
apuc = {
|
||||
"magic": apu_kws_coeff.APUC_MAGIC,
|
||||
"abi": apu_kws_coeff.APUC_ABI,
|
||||
"image_bytes": apu_kws_coeff.APUC_BYTES,
|
||||
"header_bytes": apu_kws_coeff.APUC_HEADER_BYTES,
|
||||
"payload_bytes": apu_kws_coeff.APUC_PAYLOAD_BYTES,
|
||||
"profile": apu_kws_coeff.APUC_PROFILE,
|
||||
"layout_id": apu_kws_coeff.APUC_LAYOUT_ID,
|
||||
"bank_count": apu_kws_coeff.APUC_BANK_COUNT,
|
||||
"table_count": apu_kws_coeff.APUC_TABLE_COUNT,
|
||||
"payload_crc": apu_kws_coeff.APUC_PAYLOAD_CRC,
|
||||
"coefficient_id_low": apu_kws_coeff.APUC_COEFFICIENT_ID[0],
|
||||
"coefficient_id_high": apu_kws_coeff.APUC_COEFFICIENT_ID[1],
|
||||
"associated_apum_crc": apu_kws_coeff.APUC_APUM_PAYLOAD_CRC,
|
||||
"ln2_q24": apu_kws_coeff.APUC_LN2_Q24,
|
||||
}
|
||||
|
||||
discovery = {
|
||||
"ip_id": discovery_c["IP_ID_VALUE"],
|
||||
@@ -425,6 +479,7 @@ def canonical_inventory() -> dict[str, dict[str, int]]:
|
||||
"formats": format_section,
|
||||
"apumc": apumc,
|
||||
"apum": apum,
|
||||
"apuc": apuc,
|
||||
"isa": isa,
|
||||
"mp3_stub": _mp3_stub_section(apb),
|
||||
}
|
||||
@@ -465,16 +520,16 @@ def rtl_implemented_digest() -> int:
|
||||
|
||||
|
||||
def c_implemented_digest() -> int:
|
||||
"""Extract the handwritten P7 release digest from apu_regs.h."""
|
||||
"""Extract the handwritten P9 release digest from apu_regs.h."""
|
||||
|
||||
text = C_HEADER.read_text(encoding="utf-8")
|
||||
match = re.search(
|
||||
r"^#define\s+RS_APU_DIGEST_P7_IMPLEMENTED\s+UINT32_C\((0[xX][0-9a-fA-F]+)\)\s*$",
|
||||
r"^#define\s+RS_APU_DIGEST_P9_IMPLEMENTED\s+UINT32_C\((0[xX][0-9a-fA-F]+)\)\s*$",
|
||||
text,
|
||||
re.MULTILINE,
|
||||
)
|
||||
if match is None:
|
||||
raise ValueError(f"RS_APU_DIGEST_P7_IMPLEMENTED not found in {C_HEADER.name}")
|
||||
raise ValueError(f"RS_APU_DIGEST_P9_IMPLEMENTED not found in {C_HEADER.name}")
|
||||
return int(match.group(1), 16)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
"""Frozen APUC 1.0 coefficient-container definitions and validator."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import struct
|
||||
import zlib
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any, Final
|
||||
|
||||
|
||||
APUC_MAGIC: Final = 0x43555041
|
||||
APUC_ABI: Final = 0x00010000
|
||||
APUC_HEADER_BYTES: Final = 64
|
||||
APUC_PAYLOAD_BYTES: Final = 15 * 1024 * 4
|
||||
APUC_BYTES: Final = APUC_HEADER_BYTES + APUC_PAYLOAD_BYTES
|
||||
APUC_PROFILE: Final = 1
|
||||
APUC_LAYOUT_ID: Final = 1
|
||||
APUC_BANK_COUNT: Final = 15
|
||||
APUC_TABLE_COUNT: Final = 10
|
||||
APUC_PAYLOAD_CRC: Final = 0x25E7C27D
|
||||
APUC_COEFFICIENT_ID: Final = (0x8A0B038D, 0x806C780D)
|
||||
APUC_APUM_PAYLOAD_CRC: Final = 0xB9034B22
|
||||
APUC_LN2_Q24: Final = 11629080
|
||||
APUC_LOGICAL_SHA256: Final = "f416585119e488c82e5cea1dabb17424e33a16dd8041393135903a613edced46"
|
||||
APUC_PAYLOAD_SHA256: Final = "8d030b8a0d786c80a7fa072439700a4485711971d79a6a62e78fcafb1657d83a"
|
||||
APUC_SHA256: Final = "d670688442ea6efe1e638108157f55fc55c75218c8edbee931aaca3497567818"
|
||||
|
||||
APUC_TABLES: Final = {
|
||||
"hann": (480, "5fb8748b60ff53c721d3f97c2516b9f77aebd8bb15103a072219505769d4af61"),
|
||||
"twiddle_real": (256, "49faa57826aa487032eef985d7d25f29b5346692f22ab9780209cb118c9c5b8a"),
|
||||
"twiddle_imag": (256, "3552f5ed86694388ee674dcb988509e1f8fec5c61c7435e289a27591a2c817ec"),
|
||||
"mel": (10280, "a13dc9eaf00e6e2be9710c4a1bd432239c0181b5d2ee9903250c667d906f16be"),
|
||||
"dct": (400, "93a71d890110064feb515cb7527740ce3ff0116451931e27925ac1c36339153e"),
|
||||
"log": (1025, "20b0b265de2c9e319dc70b2fa0ee136add8693937108c5f0c65ccc0ca146050a"),
|
||||
"fir3": (63, "77ccbd59d8346f709d299ce762c1976dd8abbba3263d47ed65ef394db570870a"),
|
||||
"fir6": (63, "a00ceab9105dddbed5e6d2426753dd5dbc99c9b0c166b1f19f43c530ceb9faac"),
|
||||
"softmax": (125, "410f11dd7f10d5516b41efdec861afe6a7f2a34dd116163f7758ac5110b65ae7"),
|
||||
"apum_profile": (
|
||||
1496,
|
||||
"4c30d9a5e054436bab5acfb4651ea361d0211956c9211e9d36d182b9fdc88991",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _expected_locations() -> dict[str, list[tuple[int, int]]]:
|
||||
locations: dict[str, list[tuple[int, int]]] = {
|
||||
name: [] for name in APUC_TABLES
|
||||
}
|
||||
locations["hann"] = [(0, index) for index in range(480)]
|
||||
locations["dct"] = [(0, 480 + index) for index in range(400)]
|
||||
locations["fir3"] = [(0, 880 + index) for index in range(63)]
|
||||
locations["fir6"] = [(0, 943 + index) for index in range(63)]
|
||||
locations["twiddle_real"] = [(1, index) for index in range(256)]
|
||||
locations["twiddle_imag"] = [(2, index) for index in range(256)]
|
||||
locations["log"] = [(1 + (index & 1), 256 + (index >> 1)) for index in range(1025)]
|
||||
locations["mel"] = [
|
||||
(3 + (index >> 10), index & 1023) if index < 10240 else (13, index - 10240)
|
||||
for index in range(10280)
|
||||
]
|
||||
locations["apum_profile"] = [
|
||||
(13, 40 + index) if index < 984 else (14, index - 984)
|
||||
for index in range(1496)
|
||||
]
|
||||
locations["softmax"] = [(14, 512 + index) for index in range(125)]
|
||||
return locations
|
||||
|
||||
|
||||
def _expected_profile_offsets() -> list[int]:
|
||||
offsets = list(range(0, 0x500, 4))
|
||||
for start in (0x1000, 0x1540, 0x2840, 0x2D80, 0x4080, 0x45C0, 0x58C0, 0x5E00, 0x7100):
|
||||
offsets.extend(range(start, start + 0x200, 4))
|
||||
offsets.extend(range(0x7630, 0x7690, 4))
|
||||
return offsets
|
||||
|
||||
|
||||
def validate_layout_manifest(image: bytes, manifest: dict[str, Any]) -> None:
|
||||
"""Validate the generated layout with a frozen oracle independent of its packer."""
|
||||
|
||||
validate_apuc(image)
|
||||
if manifest.get("schema_version") != 1:
|
||||
raise ValueError("APUC layout manifest schema version is not 1")
|
||||
generator = manifest.get("generator")
|
||||
if not isinstance(generator, dict):
|
||||
raise ValueError("APUC layout manifest lacks generator identity")
|
||||
generator_path = generator.get("path")
|
||||
generator_sha = generator.get("sha256")
|
||||
generator_revision = generator.get("revision")
|
||||
repository_commit = generator.get("repository_commit")
|
||||
if generator_path != "scripts/generate_apu_kws_rtl_constants.py" or not isinstance(
|
||||
generator_sha, str
|
||||
):
|
||||
raise ValueError("APUC generator path/SHA identity is incomplete")
|
||||
if generator_revision != f"sha256:{generator_sha}":
|
||||
raise ValueError("APUC generator revision does not match its source SHA")
|
||||
if (
|
||||
not isinstance(repository_commit, str)
|
||||
or len(repository_commit) != 40
|
||||
or any(character not in "0123456789abcdef" for character in repository_commit.lower())
|
||||
):
|
||||
raise ValueError("APUC generator repository revision is not a full Git commit")
|
||||
source = Path(__file__).resolve().parents[1] / generator_path
|
||||
if not source.is_file() or hashlib.sha256(source.read_bytes()).hexdigest() != generator_sha:
|
||||
raise ValueError("APUC generator source does not match the recorded revision")
|
||||
|
||||
expected_container = {
|
||||
"magic": f"0x{APUC_MAGIC:08x}",
|
||||
"abi": f"0x{APUC_ABI:08x}",
|
||||
"bytes": APUC_BYTES,
|
||||
"header_bytes": APUC_HEADER_BYTES,
|
||||
"payload_bytes": APUC_PAYLOAD_BYTES,
|
||||
"payload_crc32": f"0x{APUC_PAYLOAD_CRC:08x}",
|
||||
"coefficient_id": [f"0x{word:08x}" for word in APUC_COEFFICIENT_ID],
|
||||
"associated_apum_crc32": f"0x{APUC_APUM_PAYLOAD_CRC:08x}",
|
||||
"logical_sha256": APUC_LOGICAL_SHA256,
|
||||
"payload_sha256": APUC_PAYLOAD_SHA256,
|
||||
"sha256": APUC_SHA256,
|
||||
}
|
||||
if manifest.get("container") != expected_container:
|
||||
raise ValueError("APUC container manifest does not match the frozen identity")
|
||||
if manifest.get("layout") != {
|
||||
"geometry": "tc_sram_1024x32",
|
||||
"banks": APUC_BANK_COUNT,
|
||||
"words_per_bank": 1024,
|
||||
"populated_words": 14444,
|
||||
"zero_padding_words": 916,
|
||||
}:
|
||||
raise ValueError("APUC physical layout summary does not match the frozen geometry")
|
||||
if manifest.get("apum_profile_offsets") != _expected_profile_offsets():
|
||||
raise ValueError("APUC APUM-profile offsets do not match the independent oracle")
|
||||
|
||||
table_records = manifest.get("tables")
|
||||
if not isinstance(table_records, dict) or set(table_records) != set(APUC_TABLES):
|
||||
raise ValueError("APUC layout manifest does not contain the exact ten tables")
|
||||
payload_words = struct.unpack(f"<{APUC_BANK_COUNT * 1024}I", image[APUC_HEADER_BYTES:])
|
||||
expected_locations = _expected_locations()
|
||||
occupied: set[tuple[int, int]] = set()
|
||||
for name, (word_count, frozen_sha) in APUC_TABLES.items():
|
||||
record = table_records.get(name)
|
||||
locations = expected_locations[name]
|
||||
expected_record = {
|
||||
"words": word_count,
|
||||
"sha256_le32": frozen_sha,
|
||||
"locations": [[bank, row] for bank, row in locations],
|
||||
}
|
||||
if record != expected_record:
|
||||
raise ValueError(f"APUC table {name} does not match the independent map/hash oracle")
|
||||
if any(location in occupied for location in locations):
|
||||
raise ValueError(f"APUC table {name} overlaps another table")
|
||||
occupied.update(locations)
|
||||
table_bytes = b"".join(
|
||||
struct.pack("<I", payload_words[(bank * 1024) + row]) for bank, row in locations
|
||||
)
|
||||
if hashlib.sha256(table_bytes).hexdigest() != frozen_sha:
|
||||
raise ValueError(f"APUC payload words for {name} do not match the frozen hash")
|
||||
if len(occupied) != 14444:
|
||||
raise ValueError("APUC independent populated-position count is not 14444")
|
||||
for bank in range(APUC_BANK_COUNT):
|
||||
for row in range(1024):
|
||||
if (bank, row) not in occupied and payload_words[(bank * 1024) + row] != 0:
|
||||
raise ValueError(f"APUC padding is nonzero at bank {bank} row {row}")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ApucHeader:
|
||||
magic: int
|
||||
abi: int
|
||||
total_bytes: int
|
||||
payload_offset: int
|
||||
payload_bytes: int
|
||||
profile: int
|
||||
layout_id: int
|
||||
bank_count: int
|
||||
table_count: int
|
||||
payload_crc: int
|
||||
coefficient_id: tuple[int, int]
|
||||
associated_apum_crc: int
|
||||
ln2_q24: int
|
||||
|
||||
|
||||
def validate_apuc(image: bytes) -> ApucHeader:
|
||||
"""Validate exact APUC 1.0 bytes and return the frozen header."""
|
||||
|
||||
if len(image) != APUC_BYTES:
|
||||
raise ValueError(f"APUC image must be exactly {APUC_BYTES} bytes")
|
||||
words = struct.unpack_from("<16I", image)
|
||||
expected = (
|
||||
APUC_MAGIC,
|
||||
APUC_ABI,
|
||||
APUC_BYTES,
|
||||
APUC_HEADER_BYTES,
|
||||
APUC_PAYLOAD_BYTES,
|
||||
APUC_PROFILE,
|
||||
APUC_LAYOUT_ID,
|
||||
APUC_BANK_COUNT,
|
||||
APUC_TABLE_COUNT,
|
||||
APUC_PAYLOAD_CRC,
|
||||
*APUC_COEFFICIENT_ID,
|
||||
APUC_APUM_PAYLOAD_CRC,
|
||||
APUC_LN2_Q24,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if words != expected:
|
||||
raise ValueError("APUC header does not match the frozen profile")
|
||||
payload = image[APUC_HEADER_BYTES:]
|
||||
if zlib.crc32(payload) & 0xFFFFFFFF != APUC_PAYLOAD_CRC:
|
||||
raise ValueError("APUC payload CRC mismatch")
|
||||
if hashlib.sha256(payload).hexdigest() != APUC_PAYLOAD_SHA256:
|
||||
raise ValueError("APUC payload SHA-256 mismatch")
|
||||
if hashlib.sha256(image).hexdigest() != APUC_SHA256:
|
||||
raise ValueError("APUC image SHA-256 mismatch")
|
||||
return ApucHeader(
|
||||
magic=words[0],
|
||||
abi=words[1],
|
||||
total_bytes=words[2],
|
||||
payload_offset=words[3],
|
||||
payload_bytes=words[4],
|
||||
profile=words[5],
|
||||
layout_id=words[6],
|
||||
bank_count=words[7],
|
||||
table_count=words[8],
|
||||
payload_crc=words[9],
|
||||
coefficient_id=(words[10], words[11]),
|
||||
associated_apum_crc=words[12],
|
||||
ln2_q24=words[13],
|
||||
)
|
||||
@@ -0,0 +1,342 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Initialize or assemble fail-closed APU-P9 evidence reports."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from apu_kws_coeff import validate_layout_manifest
|
||||
|
||||
|
||||
REPORTS = (
|
||||
"coefficient-layout.json",
|
||||
"coefficient-lifecycle.json",
|
||||
"memo-equivalence.json",
|
||||
"numerical-latency.json",
|
||||
"memory-synthesis-ab.json",
|
||||
"physical.json",
|
||||
)
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
REQUIRED_CHECKS = {
|
||||
REPORTS[1]: (
|
||||
"lp_admission",
|
||||
"unsupported_discovery",
|
||||
"cache_acl",
|
||||
"dma_backpressure",
|
||||
"payload_corruption",
|
||||
"readback_corruption",
|
||||
"partial_retry",
|
||||
"lock",
|
||||
"ownership",
|
||||
"hard_reset",
|
||||
"soft_reset",
|
||||
"resource_reset",
|
||||
"quiesce_load",
|
||||
"abort_every_state",
|
||||
),
|
||||
REPORTS[2]: (
|
||||
"valid_v1_v2",
|
||||
"mutated_v1_v2",
|
||||
"duplicate_keys",
|
||||
"collisions",
|
||||
"full_wrap",
|
||||
"full_table",
|
||||
"deeper_call_bypass",
|
||||
"key_boundaries",
|
||||
"bitmap_boundaries",
|
||||
"interrupted_clear_insert",
|
||||
"verdict_equivalence",
|
||||
"first_error_equivalence",
|
||||
"visit_count_equivalence",
|
||||
),
|
||||
REPORTS[3]: (
|
||||
"all_coefficients",
|
||||
"scalar_table_boundaries",
|
||||
"variable_backpressure",
|
||||
"no_double_commit",
|
||||
"no_stale_response",
|
||||
"mfcc_bit_exact",
|
||||
"layers_bit_exact",
|
||||
"full_1000_windows",
|
||||
"complete_window_before_successor",
|
||||
"concurrent_60s_zero_xrun",
|
||||
),
|
||||
REPORTS[4]: (
|
||||
"configuration_matches",
|
||||
"revision_qualified",
|
||||
"candidate_synthesis_completed",
|
||||
"target_inferred_bits_zero",
|
||||
"apu_reduction_at_least_90_percent",
|
||||
"candidate_apu_bits_at_most_65536",
|
||||
"candidate_macro_inventory_76",
|
||||
"pass_timing_reported",
|
||||
"post_memory_inventory_reported",
|
||||
"peak_rss_measured_both",
|
||||
"candidate_peak_rss_lower",
|
||||
"synthesis_duration_measured_both",
|
||||
"candidate_duration_lower",
|
||||
),
|
||||
REPORTS[5]: (
|
||||
"revision_qualified",
|
||||
"macro_inventory_76",
|
||||
"logic_macro_area_separate",
|
||||
"wns_nonnegative",
|
||||
"tns_zero",
|
||||
"hold_reported",
|
||||
"no_unconstrained_paths",
|
||||
"macro_views_complete",
|
||||
"netlist_models_complete",
|
||||
),
|
||||
}
|
||||
BASELINE_REVISION = "0b73994772068e5fac00fb1a98f87f88ed121cdf"
|
||||
|
||||
|
||||
def _read_json(path: Path) -> dict[str, Any]:
|
||||
try:
|
||||
value = json.loads(path.read_text(encoding="utf-8"))
|
||||
except (OSError, json.JSONDecodeError) as error:
|
||||
raise ValueError(f"cannot read JSON evidence {path}: {error}") from error
|
||||
if not isinstance(value, dict):
|
||||
raise ValueError(f"JSON evidence is not an object: {path}")
|
||||
return value
|
||||
|
||||
|
||||
def _write_json(path: Path, value: dict[str, Any]) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(value, indent=2, sort_keys=True) + "\n", encoding="ascii")
|
||||
|
||||
|
||||
def _full_revision(value: object) -> bool:
|
||||
return isinstance(value, str) and len(value) == 40 and all(
|
||||
character in "0123456789abcdef" for character in value.lower()
|
||||
)
|
||||
|
||||
|
||||
def _require_artifacts(report: dict[str, Any], name: str) -> None:
|
||||
artifacts = report.get("artifacts")
|
||||
if not isinstance(artifacts, dict) or not artifacts:
|
||||
raise ValueError(f"{name} passed report lacks named artifacts")
|
||||
missing = [
|
||||
key
|
||||
for key, value in artifacts.items()
|
||||
if not isinstance(value, str) or not Path(value).is_file()
|
||||
]
|
||||
if missing:
|
||||
raise ValueError(f"{name} passed report has missing artifacts: {missing}")
|
||||
|
||||
|
||||
def _require_passed_tests(report: dict[str, Any], name: str) -> None:
|
||||
tests = report.get("tests")
|
||||
if not isinstance(tests, list) or not tests or any(
|
||||
not isinstance(test, dict) or test.get("status") != "passed" for test in tests
|
||||
):
|
||||
raise ValueError(f"{name} passed report lacks passed test records")
|
||||
|
||||
|
||||
def _validate_memory_ab(report: dict[str, Any]) -> None:
|
||||
baseline = report.get("baseline")
|
||||
candidate = report.get("candidate")
|
||||
tool = report.get("tool_identity")
|
||||
if not isinstance(baseline, dict) or not isinstance(candidate, dict) or not isinstance(tool, dict):
|
||||
raise ValueError("memory-synthesis-ab.json lacks baseline/candidate/tool identity")
|
||||
if (
|
||||
baseline.get("revision") != BASELINE_REVISION
|
||||
or baseline.get("dirty") is not False
|
||||
or candidate.get("revision") != report.get("revision")
|
||||
or candidate.get("dirty") is not False
|
||||
):
|
||||
raise ValueError("memory-synthesis-ab.json revisions are not frozen and clean")
|
||||
memory = candidate.get("memory")
|
||||
macros = candidate.get("macros")
|
||||
pass_timing = candidate.get("pass_timing")
|
||||
post_memory = candidate.get("post_memory")
|
||||
if (
|
||||
not isinstance(memory, dict)
|
||||
or memory.get("target_bits") != 0
|
||||
or not isinstance(memory.get("apu_bits"), int)
|
||||
or memory["apu_bits"] > 65536
|
||||
or not isinstance(macros, dict)
|
||||
or macros.get("apu_wrappers") != 76
|
||||
or not isinstance(pass_timing, dict)
|
||||
or any(pass_timing.get(name) is None for name in ("memory", "opt_dff", "abc", "total_ns"))
|
||||
or not isinstance(post_memory, dict)
|
||||
or not isinstance(post_memory.get("cell_types"), dict)
|
||||
):
|
||||
raise ValueError("memory-synthesis-ab.json lacks the frozen memory/pass inventory")
|
||||
yosys_path = Path(str(tool.get("yosys_path", "")))
|
||||
yosys_sha = tool.get("yosys_sha256")
|
||||
if not yosys_path.is_file() or not isinstance(yosys_sha, str):
|
||||
raise ValueError("memory-synthesis-ab.json lacks the Yosys executable identity")
|
||||
if hashlib.sha256(yosys_path.read_bytes()).hexdigest() != yosys_sha:
|
||||
raise ValueError("memory-synthesis-ab.json Yosys executable SHA-256 does not match")
|
||||
if tool.get("read_slang_provider") != "yosys-executable":
|
||||
raise ValueError("memory-synthesis-ab.json does not identify the Slang provider")
|
||||
|
||||
|
||||
def _validate_numerical(report: dict[str, Any]) -> None:
|
||||
accuracy = report.get("accuracy")
|
||||
concurrency = report.get("concurrency")
|
||||
latency = report.get("latency")
|
||||
if (
|
||||
not isinstance(accuracy, dict)
|
||||
or accuracy.get("windows") != 1000
|
||||
or not isinstance(accuracy.get("top1_correct"), int)
|
||||
or accuracy["top1_correct"] < 900
|
||||
or not isinstance(concurrency, dict)
|
||||
or concurrency.get("seconds_per_scenario", 0) < 60
|
||||
or sorted(concurrency.get("rates", [])) != [48000, 96000]
|
||||
or sorted(concurrency.get("precisions", [])) != [16, 24]
|
||||
or concurrency.get("xrun_count") != 0
|
||||
or not isinstance(latency, dict)
|
||||
or not isinstance(latency.get("maximum_window_cycles"), int)
|
||||
or latency["maximum_window_cycles"] > 4_800_000
|
||||
):
|
||||
raise ValueError("numerical-latency.json lacks the frozen accuracy/latency/concurrency result")
|
||||
|
||||
|
||||
def _validate_physical(report: dict[str, Any]) -> None:
|
||||
inventory = report.get("macro_inventory")
|
||||
area = report.get("area")
|
||||
timing = report.get("timing")
|
||||
if (
|
||||
not isinstance(inventory, dict)
|
||||
or inventory.get("wrappers") != 76
|
||||
or not isinstance(area, dict)
|
||||
or any(not isinstance(area.get(name), (int, float)) for name in ("logic_um2", "macro_um2", "total_um2"))
|
||||
or not isinstance(timing, dict)
|
||||
or not isinstance(timing.get("wns_ns"), (int, float))
|
||||
or timing["wns_ns"] < 0
|
||||
or timing.get("tns_ns") != 0
|
||||
or timing.get("unconstrained_paths") != 0
|
||||
or not isinstance(timing.get("hold"), dict)
|
||||
):
|
||||
raise ValueError("physical.json lacks the frozen macro/area/timing result")
|
||||
|
||||
|
||||
def _validate_passed(name: str, report: dict[str, Any]) -> None:
|
||||
if name == REPORTS[0]:
|
||||
if report.get("profile") != "configs/ci/ihp130.mk":
|
||||
raise ValueError("coefficient-layout.json does not use configs/ci/ihp130.mk")
|
||||
layout_source = Path(str(report.get("source", "")))
|
||||
apuc_source = Path(str(report.get("apuc_source", "")))
|
||||
if not layout_source.is_file() or not apuc_source.is_file():
|
||||
raise ValueError("coefficient-layout.json lacks its layout/APUC source artifacts")
|
||||
validate_layout_manifest(apuc_source.read_bytes(), _read_json(layout_source))
|
||||
return
|
||||
if report.get("schema_version") != 1 or report.get("phase") != "APU-P9":
|
||||
raise ValueError(f"{name} passed report lacks the APU-P9 schema identity")
|
||||
if report.get("profile") != "configs/ci/ihp130.mk":
|
||||
raise ValueError(f"{name} passed report does not use configs/ci/ihp130.mk")
|
||||
if not _full_revision(report.get("revision")) or report.get("dirty") is not False:
|
||||
raise ValueError(f"{name} passed report is not revision-qualified")
|
||||
checks = report.get("checks")
|
||||
required = REQUIRED_CHECKS[name]
|
||||
if not isinstance(checks, dict) or any(checks.get(check) is not True for check in required):
|
||||
raise ValueError(f"{name} passed report lacks one or more frozen checks")
|
||||
if name in (REPORTS[1], REPORTS[2]):
|
||||
_require_passed_tests(report, name)
|
||||
if name == REPORTS[2] and (
|
||||
report.get("baseline_revision") != BASELINE_REVISION
|
||||
or report.get("candidate_revision") != report.get("revision")
|
||||
):
|
||||
raise ValueError("memo-equivalence.json revisions do not match the frozen comparison")
|
||||
if name == REPORTS[3]:
|
||||
_validate_numerical(report)
|
||||
elif name == REPORTS[4]:
|
||||
_validate_memory_ab(report)
|
||||
elif name == REPORTS[5]:
|
||||
_validate_physical(report)
|
||||
_require_artifacts(report, name)
|
||||
|
||||
|
||||
def _initialize(args: argparse.Namespace) -> None:
|
||||
layout = _read_json(args.layout)
|
||||
try:
|
||||
apuc = args.apuc.read_bytes()
|
||||
except OSError as error:
|
||||
raise ValueError(f"cannot read APUC evidence {args.apuc}: {error}") from error
|
||||
validate_layout_manifest(apuc, layout)
|
||||
profile_path = args.profile.resolve()
|
||||
try:
|
||||
profile = profile_path.relative_to(ROOT).as_posix()
|
||||
except ValueError:
|
||||
profile = str(profile_path)
|
||||
layout["phase"] = "APU-P9"
|
||||
layout["profile"] = profile
|
||||
layout["status"] = "passed"
|
||||
layout["source"] = str(args.layout.resolve())
|
||||
layout["apuc_source"] = str(args.apuc.resolve())
|
||||
_write_json(args.output_dir / REPORTS[0], layout)
|
||||
requirements = {
|
||||
REPORTS[1]: "APUC admission, corruption, retry, lock, reset, quiesce and abort matrix",
|
||||
REPORTS[2]: "baseline versus macro memo verdict, first-error and visit-count equivalence",
|
||||
REPORTS[3]: "bit-exact coefficient, MFCC/layer/window and bounded-cycle qualification",
|
||||
REPORTS[4]: "same-input baseline/candidate inferred memory, runtime and peak-RSS comparison",
|
||||
REPORTS[5]: "76-wrapper area, WNS/TNS, unconstrained-path and macro-view accounting",
|
||||
}
|
||||
for name, requirement in requirements.items():
|
||||
destination = args.output_dir / name
|
||||
if not destination.exists():
|
||||
_write_json(
|
||||
destination,
|
||||
{
|
||||
"schema_version": 1,
|
||||
"phase": "APU-P9",
|
||||
"status": "unrun",
|
||||
"requirement": requirement,
|
||||
"reason": "required qualifying run has not been supplied",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _assemble(args: argparse.Namespace) -> None:
|
||||
sources: dict[str, Path] = {}
|
||||
for item in args.report:
|
||||
name, separator, raw_path = item.partition("=")
|
||||
if not separator or name not in REPORTS:
|
||||
raise ValueError(f"report must be NAME=PATH for one of {REPORTS}: {item}")
|
||||
sources[name] = Path(raw_path)
|
||||
missing = [name for name in REPORTS if name not in sources]
|
||||
if missing:
|
||||
raise ValueError(f"missing required P9 reports: {missing}")
|
||||
for name, source in sources.items():
|
||||
report = _read_json(source)
|
||||
status = report.get("status")
|
||||
if status not in ("passed", "unrun", "failed", "timed_out"):
|
||||
raise ValueError(f"{name} lacks a recognized fail-closed status")
|
||||
if status == "passed":
|
||||
_validate_passed(name, report)
|
||||
elif not isinstance(report.get("reason", report.get("failure")), str):
|
||||
raise ValueError(f"{name} {status} report lacks a reason/failure")
|
||||
report["source"] = str(source.resolve())
|
||||
_write_json(args.output_dir / name, report)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||
initialize = subparsers.add_parser("initialize")
|
||||
initialize.add_argument("--layout", type=Path, required=True)
|
||||
initialize.add_argument("--apuc", type=Path, required=True)
|
||||
initialize.add_argument("--profile", type=Path, required=True)
|
||||
initialize.add_argument("--output-dir", type=Path, required=True)
|
||||
initialize.set_defaults(handler=_initialize)
|
||||
assemble = subparsers.add_parser("assemble")
|
||||
assemble.add_argument("--report", action="append", default=[])
|
||||
assemble.add_argument("--output-dir", type=Path, required=True)
|
||||
assemble.set_defaults(handler=_assemble)
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
args.handler(args)
|
||||
except ValueError as error:
|
||||
parser.error(str(error))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,306 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare revision-qualified APU-P9 baseline/candidate synthesis artifacts."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
BASELINE_REVISION = "0b73994772068e5fac00fb1a98f87f88ed121cdf"
|
||||
TARGET_MODULES = (
|
||||
"apu_kws_engine",
|
||||
"apu_kws_model_loader",
|
||||
"apu_microcode_loader",
|
||||
"apu_kws_coeff_store",
|
||||
"apu_proof_memo",
|
||||
)
|
||||
APU_PATH_MARKER = ".u_apb4_apu."
|
||||
REQUIRED_CONFIGURATION = {
|
||||
"PDK": "IHP130",
|
||||
"APU_ENABLE_P7": "YES",
|
||||
"HAVE_SRAM_MACRO": "YES",
|
||||
"SYNTH": "YOSYS",
|
||||
}
|
||||
|
||||
|
||||
def _one(root: Path, pattern: str) -> Path:
|
||||
matches = sorted(root.glob(pattern))
|
||||
if len(matches) != 1:
|
||||
raise ValueError(f"expected one {pattern} below {root}, found {len(matches)}")
|
||||
return matches[0]
|
||||
|
||||
|
||||
def _json(path: Path) -> dict[str, Any]:
|
||||
try:
|
||||
value = json.loads(path.read_text(encoding="utf-8"))
|
||||
except (OSError, json.JSONDecodeError) as error:
|
||||
raise ValueError(f"cannot read {path}: {error}") from error
|
||||
if not isinstance(value, dict):
|
||||
raise ValueError(f"expected JSON object: {path}")
|
||||
return value
|
||||
|
||||
|
||||
def _memory_inventory(path: Path) -> dict[str, Any]:
|
||||
report = _json(path)
|
||||
modules = report.get("modules")
|
||||
if not isinstance(modules, dict):
|
||||
raise ValueError(f"Yosys stat report lacks modules: {path}")
|
||||
rows: list[dict[str, Any]] = []
|
||||
for name, values in modules.items():
|
||||
if not isinstance(values, dict):
|
||||
continue
|
||||
bits = int(values.get("num_memory_bits", 0))
|
||||
memories = int(values.get("num_memories", 0))
|
||||
if bits == 0 and memories == 0:
|
||||
continue
|
||||
rows.append({"module": name, "memories": memories, "bits": bits})
|
||||
apu_rows = [row for row in rows if APU_PATH_MARKER in row["module"]]
|
||||
target_rows = [
|
||||
row for row in apu_rows if any(name in row["module"] for name in TARGET_MODULES)
|
||||
]
|
||||
return {
|
||||
"artifact": str(path.resolve()),
|
||||
"whole_soc_bits": sum(row["bits"] for row in rows),
|
||||
"apu_bits": sum(row["bits"] for row in apu_rows),
|
||||
"target_bits": sum(row["bits"] for row in target_rows),
|
||||
"apu_rows": sorted(apu_rows, key=lambda row: (-row["bits"], row["module"])),
|
||||
"target_rows": sorted(target_rows, key=lambda row: (-row["bits"], row["module"])),
|
||||
}
|
||||
|
||||
|
||||
def _logic_inventory(path: Path) -> dict[str, Any]:
|
||||
report = _json(path)
|
||||
design = report.get("design")
|
||||
if not isinstance(design, dict):
|
||||
raise ValueError(f"post-memory report lacks design statistics: {path}")
|
||||
cell_types = design.get("num_cells_by_type")
|
||||
if not isinstance(cell_types, dict):
|
||||
raise ValueError(f"post-memory report lacks cell types: {path}")
|
||||
normalized = {str(name): int(count) for name, count in cell_types.items()}
|
||||
return {
|
||||
"artifact": str(path.resolve()),
|
||||
"cells": int(design.get("num_cells", sum(normalized.values()))),
|
||||
"flops": sum(count for name, count in normalized.items() if "DFF" in name.upper()),
|
||||
"muxes": sum(count for name, count in normalized.items() if "MUX" in name.upper()),
|
||||
"cell_types": normalized,
|
||||
}
|
||||
|
||||
|
||||
def _pass_timing(path: Path) -> dict[str, Any]:
|
||||
report = _json(path)
|
||||
passes = report.get("passes")
|
||||
if not isinstance(passes, dict) or not isinstance(report.get("total_ns"), int):
|
||||
raise ValueError(f"Yosys performance report is incomplete: {path}")
|
||||
timings: dict[str, Any] = {
|
||||
"artifact": str(path.resolve()),
|
||||
"total_ns": report["total_ns"],
|
||||
"generator": report.get("generator"),
|
||||
}
|
||||
for name in ("memory", "opt_dff", "abc"):
|
||||
entry = passes.get(name)
|
||||
timings[name] = int(entry["runtime_ns"]) if isinstance(entry, dict) and "runtime_ns" in entry else None
|
||||
return timings
|
||||
|
||||
|
||||
def _macro_inventory(path: Path) -> dict[str, Any]:
|
||||
instances = [line.strip().replace("/", ".") for line in path.read_text().splitlines() if line.strip()]
|
||||
apu = [instance for instance in instances if APU_PATH_MARKER in instance]
|
||||
classes = {
|
||||
"control_store": sum(".u_control_store." in value for value in apu),
|
||||
"codec_common_data": sum(".u_local_sram." in value for value in apu),
|
||||
"kws_model_scratch": sum(".u_kws_sram_client." in value for value in apu),
|
||||
"loader_path_stack": sum(
|
||||
(".u_microcode_loader." in value) and (".u_proof_memo." not in value) for value in apu
|
||||
),
|
||||
"verifier_memo": sum(".u_proof_memo." in value for value in apu),
|
||||
"coefficient_store": sum(".u_kws_coeff_store." in value for value in apu),
|
||||
}
|
||||
return {
|
||||
"artifact": str(path.resolve()),
|
||||
"apu_wrappers": len(apu),
|
||||
"classes": classes,
|
||||
"instances": apu,
|
||||
}
|
||||
|
||||
|
||||
def _variant(root: Path) -> dict[str, Any]:
|
||||
pre_memory_path = _one(root, "syn/yosys/rpt/*_pre_memory.json")
|
||||
pre_tech_path = _one(root, "syn/yosys/rpt/*_pre_tech.json")
|
||||
macro_path = _one(root, "syn/yosys/rpt/*_macros.rpt")
|
||||
perf_path = _one(root, "syn/yosys/yosys-perf.json")
|
||||
result_path = _one(root, "syn/yosys/result-synth.json")
|
||||
manifest_path = _one(root, "meta/manifest.json")
|
||||
memory = _memory_inventory(pre_memory_path)
|
||||
result = _json(result_path)
|
||||
manifest = _json(manifest_path)
|
||||
repository = manifest.get("repository")
|
||||
if not isinstance(repository, dict):
|
||||
raise ValueError(f"manifest lacks repository identity: {manifest_path}")
|
||||
return {
|
||||
"root": str(root.resolve()),
|
||||
"memory": memory,
|
||||
"post_memory": _logic_inventory(pre_tech_path),
|
||||
"pass_timing": _pass_timing(perf_path),
|
||||
"synthesis": {
|
||||
"status": result.get("status"),
|
||||
"exit_code": result.get("exit_code"),
|
||||
"duration_seconds": result.get("duration_seconds"),
|
||||
"peak_rss_kib": result.get("peak_rss_kib"),
|
||||
"result": str(result_path.resolve()),
|
||||
},
|
||||
"macros": _macro_inventory(macro_path),
|
||||
"manifest": manifest,
|
||||
"revision": repository.get("commit"),
|
||||
"dirty": repository.get("dirty"),
|
||||
"artifacts": {
|
||||
"pre_memory": str(pre_memory_path.resolve()),
|
||||
"pre_tech": str(pre_tech_path.resolve()),
|
||||
"macros": str(macro_path.resolve()),
|
||||
"yosys_perf": str(perf_path.resolve()),
|
||||
"synthesis_result": str(result_path.resolve()),
|
||||
"manifest": str(manifest_path.resolve()),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _configuration_digest(variant: dict[str, Any]) -> Any:
|
||||
manifest = variant.get("manifest")
|
||||
if not isinstance(manifest, dict):
|
||||
return None
|
||||
comparable = {
|
||||
"configuration": manifest.get("configuration"),
|
||||
"dependency_lock": manifest.get("dependency_lock"),
|
||||
"tools": manifest.get("tools"),
|
||||
}
|
||||
if any(value is None for value in comparable.values()):
|
||||
return None
|
||||
return json.dumps(comparable, sort_keys=True, separators=(",", ":"))
|
||||
|
||||
|
||||
def _configuration_is_frozen(variant: dict[str, Any]) -> bool:
|
||||
configuration = variant["manifest"].get("configuration")
|
||||
return (
|
||||
variant["manifest"].get("profile") == "ihp130"
|
||||
and isinstance(configuration, dict)
|
||||
and all(configuration.get(name) == value for name, value in REQUIRED_CONFIGURATION.items())
|
||||
)
|
||||
|
||||
|
||||
def _sha256(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as source:
|
||||
for block in iter(lambda: source.read(1024 * 1024), b""):
|
||||
digest.update(block)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--baseline-root", type=Path, required=True)
|
||||
parser.add_argument("--candidate-root", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("--yosys", type=Path, default=None)
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
baseline = _variant(args.baseline_root)
|
||||
candidate = _variant(args.candidate_root)
|
||||
except ValueError as error:
|
||||
parser.error(str(error))
|
||||
|
||||
baseline_bits = baseline["memory"]["apu_bits"]
|
||||
candidate_bits = candidate["memory"]["apu_bits"]
|
||||
reduction = 0.0 if baseline_bits == 0 else 1.0 - (candidate_bits / baseline_bits)
|
||||
expected_macros = {
|
||||
"control_store": 8,
|
||||
"codec_common_data": 12,
|
||||
"kws_model_scratch": 16,
|
||||
"loader_path_stack": 8,
|
||||
"verifier_memo": 17,
|
||||
"coefficient_store": 15,
|
||||
}
|
||||
baseline_digest = _configuration_digest(baseline)
|
||||
candidate_digest = _configuration_digest(candidate)
|
||||
baseline_duration = baseline["synthesis"]["duration_seconds"]
|
||||
candidate_duration = candidate["synthesis"]["duration_seconds"]
|
||||
yosys_path = args.yosys
|
||||
if yosys_path is None:
|
||||
resolved_yosys = shutil.which("yosys")
|
||||
if resolved_yosys is None:
|
||||
parser.error("yosys executable is required to record its SHA-256")
|
||||
yosys_path = Path(resolved_yosys)
|
||||
if not yosys_path.is_file():
|
||||
parser.error(f"yosys executable does not exist: {yosys_path}")
|
||||
baseline_passes = baseline["pass_timing"]
|
||||
candidate_passes = candidate["pass_timing"]
|
||||
revision_qualified = (
|
||||
baseline["revision"] == BASELINE_REVISION
|
||||
and candidate["revision"] != BASELINE_REVISION
|
||||
and isinstance(candidate["revision"], str)
|
||||
and len(candidate["revision"]) == 40
|
||||
and baseline["dirty"] is False
|
||||
and candidate["dirty"] is False
|
||||
)
|
||||
checks = {
|
||||
"configuration_matches": baseline_digest is not None
|
||||
and baseline_digest == candidate_digest
|
||||
and _configuration_is_frozen(baseline)
|
||||
and _configuration_is_frozen(candidate),
|
||||
"revision_qualified": revision_qualified,
|
||||
"candidate_synthesis_completed": candidate["synthesis"]["status"] == "passed",
|
||||
"target_inferred_bits_zero": candidate["memory"]["target_bits"] == 0,
|
||||
"apu_reduction_at_least_90_percent": reduction >= 0.90,
|
||||
"candidate_apu_bits_at_most_65536": candidate_bits <= 65536,
|
||||
"candidate_macro_inventory_76": candidate["macros"]["apu_wrappers"] == 76
|
||||
and candidate["macros"]["classes"] == expected_macros,
|
||||
"pass_timing_reported": all(candidate_passes[name] is not None for name in ("memory", "opt_dff", "abc"))
|
||||
and baseline_passes["memory"] is not None
|
||||
and baseline_passes["opt_dff"] is not None,
|
||||
"post_memory_inventory_reported": baseline["post_memory"]["cells"] > 0
|
||||
and candidate["post_memory"]["cells"] > 0,
|
||||
"peak_rss_measured_both": baseline["synthesis"]["peak_rss_kib"] is not None
|
||||
and candidate["synthesis"]["peak_rss_kib"] is not None,
|
||||
"candidate_peak_rss_lower": baseline["synthesis"]["peak_rss_kib"] is not None
|
||||
and candidate["synthesis"]["peak_rss_kib"] is not None
|
||||
and candidate["synthesis"]["peak_rss_kib"] < baseline["synthesis"]["peak_rss_kib"],
|
||||
"synthesis_duration_measured_both": baseline_duration is not None
|
||||
and candidate_duration is not None,
|
||||
"candidate_duration_lower": baseline_duration is not None
|
||||
and candidate_duration is not None
|
||||
and candidate_duration < baseline_duration,
|
||||
}
|
||||
report = {
|
||||
"schema_version": 1,
|
||||
"phase": "APU-P9",
|
||||
"profile": "configs/ci/ihp130.mk",
|
||||
"revision": candidate["revision"],
|
||||
"dirty": candidate["dirty"],
|
||||
"status": "passed" if all(checks.values()) else "failed",
|
||||
"reason": None if all(checks.values()) else "one or more frozen APU-P9 A/B checks failed",
|
||||
"baseline": baseline,
|
||||
"candidate": candidate,
|
||||
"apu_inferred_reduction": reduction,
|
||||
"expected_candidate_macro_classes": expected_macros,
|
||||
"tool_identity": {
|
||||
"yosys_path": str(yosys_path.resolve()),
|
||||
"yosys_sha256": _sha256(yosys_path),
|
||||
"read_slang_provider": "yosys-executable",
|
||||
},
|
||||
"artifacts": {
|
||||
**{f"baseline_{name}": path for name, path in baseline["artifacts"].items()},
|
||||
**{f"candidate_{name}": path for name, path in candidate["artifacts"].items()},
|
||||
},
|
||||
"checks": checks,
|
||||
}
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="ascii")
|
||||
print(f"APU-P9 memory A/B: {report['status']} ({args.output})")
|
||||
return 0 if report["status"] == "passed" else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -1,8 +1,9 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate the embedded APU release-evidence assets for the apu_release app.
|
||||
|
||||
Reads the deterministic P5 APUMC microcode bundle, the frozen P7 APUM KWS
|
||||
model, and one locked KWS corpus utterance; synthesizes a small deterministic
|
||||
Reads the deterministic P5 APUMC microcode bundle, the frozen P9 APUC
|
||||
coefficient image, the P7 APUM KWS model, and one locked KWS corpus utterance;
|
||||
synthesizes a small deterministic
|
||||
mono 48 kHz S16 WAV fixture; computes the frozen expected PCM through the
|
||||
bit-accurate codec model; and emits a C header with every byte array and CRC
|
||||
the LP/HP evidence flow needs. The generator is deterministic: no network, no
|
||||
@@ -24,6 +25,7 @@ sys.path.insert(0, str(ROOT / "scripts"))
|
||||
|
||||
from apu_codecs import decode_wav, process_pcm # noqa: E402
|
||||
from apu_kws import APUM_ABI, APUM_BYTES, APUM_MAGIC, APUM_PAYLOAD_CRC, APUM_SHA256 # noqa: E402
|
||||
from apu_kws_coeff import APUC_PAYLOAD_CRC, validate_apuc # noqa: E402
|
||||
|
||||
APUMC_MAGIC = 0x41504D43
|
||||
APUMC_ABI_VERSIONS = (0x00010000, 0x00020000)
|
||||
@@ -82,6 +84,12 @@ def _load_apum(path: Path) -> tuple[bytes, int]:
|
||||
return image, _crc32(image)
|
||||
|
||||
|
||||
def _load_apuc(path: Path) -> tuple[bytes, int]:
|
||||
image = path.read_bytes()
|
||||
validate_apuc(image)
|
||||
return image, _crc32(image)
|
||||
|
||||
|
||||
def _synthesize_wav() -> bytes:
|
||||
samples = [((index * 48271 + 12345) % 65536) - 32768 for index in range(WAV_FRAMES)]
|
||||
payload = struct.pack(f"<{WAV_FRAMES}h", *samples)
|
||||
@@ -150,6 +158,7 @@ def _format_array(name: str, data: bytes) -> str:
|
||||
|
||||
def _emit(args: argparse.Namespace) -> str:
|
||||
apumc, apumc_crc, apumc_payload_crc = _load_apumc(args.apumc)
|
||||
apuc, apuc_crc = _load_apuc(args.apuc)
|
||||
apum, apum_crc = _load_apum(args.apum)
|
||||
wav = _synthesize_wav()
|
||||
pcm_crc, pcm_bytes, pcm_frames = _expected_pcm(wav)
|
||||
@@ -165,6 +174,9 @@ def _emit(args: argparse.Namespace) -> str:
|
||||
f"#define RS_APU_RELEASE_APUMC_SIZE UINT32_C({len(apumc)})",
|
||||
f"#define RS_APU_RELEASE_APUMC_CRC UINT32_C(0x{apumc_crc:08X})",
|
||||
f"#define RS_APU_RELEASE_APUMC_PAYLOAD_CRC UINT32_C(0x{apumc_payload_crc:08X})",
|
||||
f"#define RS_APU_RELEASE_APUC_SIZE UINT32_C({len(apuc)})",
|
||||
f"#define RS_APU_RELEASE_APUC_CRC UINT32_C(0x{apuc_crc:08X})",
|
||||
f"#define RS_APU_RELEASE_APUC_PAYLOAD_CRC UINT32_C(0x{APUC_PAYLOAD_CRC:08X})",
|
||||
f"#define RS_APU_RELEASE_APUM_SIZE UINT32_C({len(apum)})",
|
||||
f"#define RS_APU_RELEASE_APUM_CRC UINT32_C(0x{apum_crc:08X})",
|
||||
f"#define RS_APU_RELEASE_APUM_PAYLOAD_CRC UINT32_C(0x{APUM_PAYLOAD_CRC:08X})",
|
||||
@@ -184,6 +196,8 @@ def _emit(args: argparse.Namespace) -> str:
|
||||
"",
|
||||
_format_array("rs_apu_release_apumc", apumc),
|
||||
"",
|
||||
_format_array("rs_apu_release_apuc", apuc),
|
||||
"",
|
||||
_format_array("rs_apu_release_apum", apum),
|
||||
"",
|
||||
_format_array("rs_apu_release_wav", wav),
|
||||
@@ -199,6 +213,7 @@ def _emit(args: argparse.Namespace) -> str:
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--apumc", type=Path, required=True, help="P5 APUMC bundle path")
|
||||
parser.add_argument("--apuc", type=Path, required=True, help="P9 APUC coefficient image path")
|
||||
parser.add_argument("--apum", type=Path, required=True, help="P7 APUM KWS model path")
|
||||
parser.add_argument(
|
||||
"--kws-corpus",
|
||||
|
||||
@@ -1,20 +1,41 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate the frozen APU-P7 frontend ROM and APUM validation constants."""
|
||||
"""Generate frozen APU-P7 references and the APU-P9 coefficient image."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import struct
|
||||
import subprocess
|
||||
import zlib
|
||||
from decimal import Decimal, ROUND_HALF_EVEN, getcontext
|
||||
from pathlib import Path
|
||||
|
||||
from apu_kws_coeff import (
|
||||
APUC_ABI,
|
||||
APUC_APUM_PAYLOAD_CRC,
|
||||
APUC_BANK_COUNT,
|
||||
APUC_BYTES,
|
||||
APUC_COEFFICIENT_ID,
|
||||
APUC_HEADER_BYTES,
|
||||
APUC_LAYOUT_ID,
|
||||
APUC_LOGICAL_SHA256,
|
||||
APUC_MAGIC,
|
||||
APUC_PAYLOAD_BYTES,
|
||||
APUC_PAYLOAD_CRC,
|
||||
APUC_PAYLOAD_SHA256,
|
||||
APUC_PROFILE,
|
||||
APUC_SHA256,
|
||||
APUC_TABLE_COUNT,
|
||||
)
|
||||
|
||||
getcontext().prec = 100
|
||||
|
||||
ZERO = Decimal(0)
|
||||
ONE = Decimal(1)
|
||||
TWO = Decimal(2)
|
||||
|
||||
|
||||
def _atan(value: Decimal) -> Decimal:
|
||||
square = value * value
|
||||
term = value
|
||||
@@ -224,6 +245,160 @@ def _fixed_apum_words(image: bytes) -> tuple[list[int], list[int]]:
|
||||
return offsets, [struct.unpack_from("<I", image, offset)[0] for offset in offsets]
|
||||
|
||||
|
||||
def _word_bytes(words: list[int]) -> bytes:
|
||||
return b"".join(struct.pack("<I", value & 0xFFFFFFFF) for value in words)
|
||||
|
||||
|
||||
def _coefficient_tables(image: bytes) -> tuple[list[tuple[str, list[int]]], list[int], list[int]]:
|
||||
fixed_offsets, fixed_words = _fixed_apum_words(image)
|
||||
twiddle_real, twiddle_imag = _twiddle()
|
||||
tables = [
|
||||
("hann", _hann()),
|
||||
("twiddle_real", twiddle_real),
|
||||
("twiddle_imag", twiddle_imag),
|
||||
("mel", _mel_weights()),
|
||||
("dct", _dct()),
|
||||
("log", _log_rom()),
|
||||
("fir3", _fir(3)),
|
||||
("fir6", _fir(6)),
|
||||
("softmax", _softmax_exp()),
|
||||
("apum_profile", fixed_words),
|
||||
]
|
||||
return tables, fixed_offsets, fixed_words
|
||||
|
||||
|
||||
def _pack_coefficient_banks(image: bytes) -> tuple[list[list[int]], dict[str, list[tuple[int, int]]]]:
|
||||
tables, _fixed_offsets, fixed_words = _coefficient_tables(image)
|
||||
by_name = dict(tables)
|
||||
banks = [[0] * 1024 for _ in range(APUC_BANK_COUNT)]
|
||||
inverse: dict[str, list[tuple[int, int]]] = {name: [] for name, _values in tables}
|
||||
|
||||
def place(name: str, index: int, bank: int, row: int) -> None:
|
||||
banks[bank][row] = by_name[name][index] & 0xFFFFFFFF
|
||||
inverse[name].append((bank, row))
|
||||
|
||||
for index in range(480):
|
||||
place("hann", index, 0, index)
|
||||
for index in range(400):
|
||||
place("dct", index, 0, 480 + index)
|
||||
for index in range(63):
|
||||
place("fir3", index, 0, 880 + index)
|
||||
place("fir6", index, 0, 943 + index)
|
||||
for index in range(256):
|
||||
place("twiddle_real", index, 1, index)
|
||||
place("twiddle_imag", index, 2, index)
|
||||
for index in range(1025):
|
||||
bank = 1 + (index & 1)
|
||||
place("log", index, bank, 256 + (index >> 1))
|
||||
for index in range(10280):
|
||||
if index < 10240:
|
||||
place("mel", index, 3 + (index >> 10), index & 1023)
|
||||
else:
|
||||
place("mel", index, 13, index - 10240)
|
||||
for index in range(len(fixed_words)):
|
||||
if index < 984:
|
||||
place("apum_profile", index, 13, 40 + index)
|
||||
else:
|
||||
place("apum_profile", index, 14, index - 984)
|
||||
for index in range(125):
|
||||
place("softmax", index, 14, 512 + index)
|
||||
return banks, inverse
|
||||
|
||||
|
||||
def build_apuc(image: bytes) -> tuple[bytes, dict[str, object]]:
|
||||
"""Build the exact APUC 1.0 image and its reviewable layout manifest."""
|
||||
|
||||
if len(image) != 32768:
|
||||
raise ValueError("APUM image must be exactly 32768 bytes")
|
||||
tables, fixed_offsets, _fixed_words = _coefficient_tables(image)
|
||||
banks, inverse = _pack_coefficient_banks(image)
|
||||
logical = b"".join(_word_bytes(values) for _name, values in tables)
|
||||
payload = b"".join(_word_bytes(bank) for bank in banks)
|
||||
payload_crc = zlib.crc32(payload) & 0xFFFFFFFF
|
||||
ln2_q24 = _rne(TWO.ln() * Decimal(1 << 24))
|
||||
header = struct.pack(
|
||||
"<16I",
|
||||
APUC_MAGIC,
|
||||
APUC_ABI,
|
||||
APUC_BYTES,
|
||||
APUC_HEADER_BYTES,
|
||||
APUC_PAYLOAD_BYTES,
|
||||
APUC_PROFILE,
|
||||
APUC_LAYOUT_ID,
|
||||
APUC_BANK_COUNT,
|
||||
APUC_TABLE_COUNT,
|
||||
payload_crc,
|
||||
APUC_COEFFICIENT_ID[0],
|
||||
APUC_COEFFICIENT_ID[1],
|
||||
APUC_APUM_PAYLOAD_CRC,
|
||||
ln2_q24,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
apuc = header + payload
|
||||
logical_sha = hashlib.sha256(logical).hexdigest()
|
||||
payload_sha = hashlib.sha256(payload).hexdigest()
|
||||
apuc_sha = hashlib.sha256(apuc).hexdigest()
|
||||
if payload_crc != APUC_PAYLOAD_CRC:
|
||||
raise ValueError(f"APUC payload CRC drift: 0x{payload_crc:08x}")
|
||||
if logical_sha != APUC_LOGICAL_SHA256:
|
||||
raise ValueError(f"APUC logical SHA-256 drift: {logical_sha}")
|
||||
if payload_sha != APUC_PAYLOAD_SHA256:
|
||||
raise ValueError(f"APUC payload SHA-256 drift: {payload_sha}")
|
||||
if apuc_sha != APUC_SHA256:
|
||||
raise ValueError(f"APUC image SHA-256 drift: {apuc_sha}")
|
||||
|
||||
populated = sum(len(locations) for locations in inverse.values())
|
||||
generator_path = Path(__file__).resolve()
|
||||
generator_sha = hashlib.sha256(generator_path.read_bytes()).hexdigest()
|
||||
revision = subprocess.run(
|
||||
["git", "-C", str(generator_path.parents[1]), "rev-parse", "HEAD"],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
repository_commit = revision.stdout.strip() if revision.returncode == 0 else ""
|
||||
manifest: dict[str, object] = {
|
||||
"schema_version": 1,
|
||||
"generator": {
|
||||
"path": "scripts/generate_apu_kws_rtl_constants.py",
|
||||
"revision": f"sha256:{generator_sha}",
|
||||
"sha256": generator_sha,
|
||||
"repository_commit": repository_commit,
|
||||
},
|
||||
"container": {
|
||||
"magic": f"0x{APUC_MAGIC:08x}",
|
||||
"abi": f"0x{APUC_ABI:08x}",
|
||||
"bytes": len(apuc),
|
||||
"header_bytes": APUC_HEADER_BYTES,
|
||||
"payload_bytes": len(payload),
|
||||
"payload_crc32": f"0x{payload_crc:08x}",
|
||||
"coefficient_id": [f"0x{word:08x}" for word in APUC_COEFFICIENT_ID],
|
||||
"associated_apum_crc32": f"0x{APUC_APUM_PAYLOAD_CRC:08x}",
|
||||
"logical_sha256": logical_sha,
|
||||
"payload_sha256": payload_sha,
|
||||
"sha256": apuc_sha,
|
||||
},
|
||||
"layout": {
|
||||
"geometry": "tc_sram_1024x32",
|
||||
"banks": APUC_BANK_COUNT,
|
||||
"words_per_bank": 1024,
|
||||
"populated_words": populated,
|
||||
"zero_padding_words": APUC_BANK_COUNT * 1024 - populated,
|
||||
},
|
||||
"tables": {
|
||||
name: {
|
||||
"words": len(values),
|
||||
"sha256_le32": hashlib.sha256(_word_bytes(values)).hexdigest(),
|
||||
"locations": [[bank, row] for bank, row in inverse[name]],
|
||||
}
|
||||
for name, values in tables
|
||||
},
|
||||
"apum_profile_offsets": fixed_offsets,
|
||||
}
|
||||
return apuc, manifest
|
||||
|
||||
|
||||
def _emit_profile_words(offsets: list[int], values: list[int]) -> str:
|
||||
ranges: list[tuple[int, int, int]] = []
|
||||
range_start = offsets[0]
|
||||
@@ -284,10 +459,18 @@ def _emit_profile_words(offsets: list[int], values: list[int]) -> str:
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def generate(apum: Path, output: Path, profile_output: Path) -> None:
|
||||
def generate(
|
||||
apum: Path,
|
||||
output: Path,
|
||||
profile_output: Path,
|
||||
apuc_output: Path | None = None,
|
||||
manifest_output: Path | None = None,
|
||||
) -> None:
|
||||
image = apum.read_bytes()
|
||||
if len(image) != 32768:
|
||||
raise ValueError("APUM image must be exactly 32768 bytes")
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
profile_output.parent.mkdir(parents=True, exist_ok=True)
|
||||
fixed_offsets, fixed_words = _fixed_apum_words(image)
|
||||
text = [
|
||||
"// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>",
|
||||
@@ -328,6 +511,14 @@ def generate(apum: Path, output: Path, profile_output: Path) -> None:
|
||||
),
|
||||
encoding="ascii",
|
||||
)
|
||||
if (apuc_output is None) != (manifest_output is None):
|
||||
raise ValueError("APUC output and manifest output must be requested together")
|
||||
if apuc_output is not None and manifest_output is not None:
|
||||
apuc, manifest = build_apuc(image)
|
||||
apuc_output.parent.mkdir(parents=True, exist_ok=True)
|
||||
manifest_output.parent.mkdir(parents=True, exist_ok=True)
|
||||
apuc_output.write_bytes(apuc)
|
||||
manifest_output.write_text(json.dumps(manifest, indent=2) + "\n", encoding="ascii")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
@@ -335,11 +526,13 @@ def main() -> int:
|
||||
parser.add_argument("--apum", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("--profile-output", type=Path)
|
||||
parser.add_argument("--apuc-output", type=Path)
|
||||
parser.add_argument("--manifest-output", type=Path)
|
||||
args = parser.parse_args()
|
||||
profile_output = args.profile_output
|
||||
if profile_output is None:
|
||||
profile_output = args.output.with_name("apu_kws_apum_profile.svh")
|
||||
generate(args.apum, args.output, profile_output)
|
||||
generate(args.apum, args.output, profile_output, args.apuc_output, args.manifest_output)
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@@ -112,8 +112,11 @@ def _production_sources() -> list[Path]:
|
||||
"apu_ring_scheduler.sv",
|
||||
"apu_stream_router.sv",
|
||||
"apu_control_store.sv",
|
||||
"apu_proof_memo.sv",
|
||||
"apu_microcode_loader.sv",
|
||||
"apu_local_sram.sv",
|
||||
"apu_kws_coeff_store.sv",
|
||||
"apu_kws_coeff_loader.sv",
|
||||
"apu_kws_engine.sv",
|
||||
"apu_kws_sram_client.sv",
|
||||
"apu_kws_model_loader.sv",
|
||||
|
||||
@@ -45,6 +45,7 @@ sys.path.insert(0, str(ROOT / "scripts"))
|
||||
|
||||
from apu_codecs import crc8, crc16, decode_flac, decode_wav, process_pcm # noqa: E402
|
||||
from apu_kws import APUM_PAYLOAD_CRC, APUM_SHA256 # noqa: E402
|
||||
from generate_apu_kws_rtl_constants import build_apuc # noqa: E402
|
||||
|
||||
PCLK_HZ = 48_000_000
|
||||
FULL_GATE_SECONDS = 60.0
|
||||
@@ -175,8 +176,11 @@ def _production_sources() -> list[Path]:
|
||||
"apu_ring_scheduler.sv",
|
||||
"apu_stream_router.sv",
|
||||
"apu_control_store.sv",
|
||||
"apu_proof_memo.sv",
|
||||
"apu_microcode_loader.sv",
|
||||
"apu_local_sram.sv",
|
||||
"apu_kws_coeff_store.sv",
|
||||
"apu_kws_coeff_loader.sv",
|
||||
"apu_kws_engine.sv",
|
||||
"apu_kws_sram_client.sv",
|
||||
"apu_kws_model_loader.sv",
|
||||
@@ -452,6 +456,7 @@ def _run_scenario(
|
||||
compile_result["binary"],
|
||||
f"+APUMC_HEX={artifacts['apumc_hex']}",
|
||||
f"+APUM_HEX={artifacts['apum_hex']}",
|
||||
f"+APUC_HEX={artifacts['apuc_hex']}",
|
||||
f"+WAV_HEX={scenario_dir / 'wav.hex'}",
|
||||
f"+FLAC_HEX={scenario_dir / 'flac.hex'}",
|
||||
f"+WAV_PCM_HEX={scenario_dir / 'wav_pcm.hex'}",
|
||||
@@ -658,7 +663,15 @@ def main() -> int:
|
||||
_hex_words(apumc_hex, apumc_payload, 16384)
|
||||
apum_hex = build_dir / "kws-apum.hex"
|
||||
_hex_words(apum_hex, apum_payload, 8192)
|
||||
artifacts = {"apumc_hex": str(apumc_hex), "apum_hex": str(apum_hex), "scenario_dir": ""}
|
||||
apuc_hex = build_dir / "kws-apuc.hex"
|
||||
apuc_payload, _layout = build_apuc(apum_payload)
|
||||
_hex_words(apuc_hex, apuc_payload, 15376)
|
||||
artifacts = {
|
||||
"apumc_hex": str(apumc_hex),
|
||||
"apum_hex": str(apum_hex),
|
||||
"apuc_hex": str(apuc_hex),
|
||||
"scenario_dir": "",
|
||||
}
|
||||
|
||||
started = time.monotonic()
|
||||
compile_result = _compile(build_dir)
|
||||
|
||||
@@ -30,6 +30,7 @@ from apu_kws import ( # noqa: E402
|
||||
parse_apum,
|
||||
)
|
||||
from apu_kws_convert import TfliteError, import_tflite # noqa: E402
|
||||
from generate_apu_kws_rtl_constants import build_apuc # noqa: E402
|
||||
from setup_apu_kws_reference import REQUIRED_ARCHIVE, REQUIRED_SOURCES # noqa: E402
|
||||
|
||||
MANIFEST = ROOT / "docs/ip/apu-kws-corpus.tsv"
|
||||
@@ -220,7 +221,9 @@ def _production_sources() -> list[Path]:
|
||||
return [
|
||||
common / "interface/axi4_stream_if.sv",
|
||||
multimedia / "apu_kws_sram_client.sv",
|
||||
multimedia / "apu_kws_coeff_store.sv",
|
||||
multimedia / "apu_kws_engine.sv",
|
||||
ROOT / "tests/rtl/apu_kws_coeff_fixture.sv",
|
||||
FIXTURE,
|
||||
]
|
||||
|
||||
@@ -448,6 +451,9 @@ def main() -> int:
|
||||
)
|
||||
apum_hex = build_dir / "kws_apum.hex"
|
||||
_write_hex(apum_hex, image)
|
||||
apuc_hex = build_dir / "kws_apuc.hex"
|
||||
apuc, _layout = build_apuc(image)
|
||||
_write_hex(apuc_hex, apuc)
|
||||
built = _compile(build_dir, args.simulator)
|
||||
except (OSError, TfliteError, ValueError, QualificationError) as error:
|
||||
raise SystemExit(str(error)) from error
|
||||
@@ -459,6 +465,7 @@ def main() -> int:
|
||||
)
|
||||
plusargs = [
|
||||
f"+APUM_HEX={apum_hex}",
|
||||
f"+APUC_HEX={apuc_hex}",
|
||||
f"+PCM_DIR={pcm_dir}",
|
||||
f"+WINDOW_COUNT={window_count}",
|
||||
"+FIRST_INDEX=0",
|
||||
|
||||
@@ -50,6 +50,13 @@ tooling.
|
||||
every pinned official FLAC file against locked libFLAC and records matching
|
||||
released-APUMC production results from identical verification-only Icarus
|
||||
and Verilator fixtures.
|
||||
- `test_apu_p9_coefficients.py` checks the frozen APUC hashes and complete
|
||||
logical-to-bank inverse map with an independent frozen oracle.
|
||||
`test_apu_p9_storage.py` runs the synchronous
|
||||
coefficient clients and macro-memo clear/insert/lookup boundary fixture;
|
||||
the P8 quiesced-loader test covers the successful integrated APUC DMA and
|
||||
readback path. The full corruption/retry/reset/abort lifecycle remains a
|
||||
separate required P9 evidence matrix and is not inferred from that smoke.
|
||||
- `test_apu_p8_quiesce_load.py` drives the full `apb4_apu` shell and checks
|
||||
that MICROCODE_LOAD and MODEL_LOAD DMA fetches are admitted and complete
|
||||
while the LP quiesce is held (the APU-P8 loader-admission regression).
|
||||
|
||||
@@ -1037,13 +1037,20 @@ static int test_apu_kws_validation(void) {
|
||||
.bytes = UINT32_C(32768),
|
||||
.expected_crc = UINT32_C(0xB9034B22),
|
||||
};
|
||||
const rs_apu_image_t coefficient_image = {
|
||||
.address = UINT32_C(0x10008000),
|
||||
.bytes = RS_APU_ABI_APUC_IMAGE_BYTES,
|
||||
.expected_crc = RS_APU_ABI_APUC_PAYLOAD_CRC,
|
||||
};
|
||||
rs_apu_image_t bad_image = image;
|
||||
rs_apu_kws_completion_t completion;
|
||||
rs_apu_kws_coeff_status_t coefficient_status;
|
||||
|
||||
test_apu_mmio_reset();
|
||||
APU_TEST_REG(RS_APU_ABI_KWS_MODEL_ADDRESS) = UINT32_C(0xA5A5A5A5);
|
||||
if ((rs_apu_kws_validate_job(&job) != RS_OK) ||
|
||||
(rs_apu_kws_model_load(&image, 0U) != RS_ENOTSUP) ||
|
||||
(rs_apu_kws_coeff_load(&coefficient_image, 0U) != RS_ENOTSUP) ||
|
||||
(APU_TEST_REG(RS_APU_ABI_KWS_MODEL_ADDRESS) != UINT32_C(0xA5A5A5A5))) {
|
||||
return 1;
|
||||
}
|
||||
@@ -1072,6 +1079,25 @@ static int test_apu_kws_validation(void) {
|
||||
return 5;
|
||||
}
|
||||
APU_TEST_REG(RS_APU_ABI_OWNER_STATUS) = UINT32_C(1) << RS_APU_ABI_OWNER_STATUS_QUIESCE;
|
||||
APU_TEST_REG(RS_APU_ABI_KWS_COEFF_STATUS) = 0U;
|
||||
if ((rs_apu_kws_coeff_load(&coefficient_image, 1U) != RS_EIO) ||
|
||||
(APU_TEST_REG(RS_APU_ABI_KWS_COEFF_COMMAND) != UINT32_C(1))) {
|
||||
return 11;
|
||||
}
|
||||
APU_TEST_REG(RS_APU_ABI_KWS_COEFF_STATUS) = UINT32_C(6);
|
||||
APU_TEST_REG(RS_APU_ABI_KWS_COEFF_ACTUAL_CRC) = RS_APU_ABI_APUC_PAYLOAD_CRC;
|
||||
APU_TEST_REG(RS_APU_ABI_KWS_COEFF_ID_LO) = RS_APU_ABI_APUC_COEFFICIENT_ID_LO;
|
||||
APU_TEST_REG(RS_APU_ABI_KWS_COEFF_ID_HI) = RS_APU_ABI_APUC_COEFFICIENT_ID_HI;
|
||||
APU_TEST_REG(RS_APU_ABI_KWS_COEFF_CAPACITY) = RS_APU_ABI_APUC_PAYLOAD_BYTES;
|
||||
if ((rs_apu_kws_coeff_status_read(NULL) != RS_EINVAL) ||
|
||||
(rs_apu_kws_coeff_status_read(&coefficient_status) != RS_OK) ||
|
||||
(coefficient_status.status != UINT32_C(6)) ||
|
||||
(coefficient_status.actual_crc != RS_APU_ABI_APUC_PAYLOAD_CRC) ||
|
||||
(coefficient_status.coefficient_id[0] != RS_APU_ABI_APUC_COEFFICIENT_ID_LO) ||
|
||||
(coefficient_status.coefficient_id[1] != RS_APU_ABI_APUC_COEFFICIENT_ID_HI) ||
|
||||
(coefficient_status.capacity_bytes != RS_APU_ABI_APUC_PAYLOAD_BYTES)) {
|
||||
return 12;
|
||||
}
|
||||
APU_TEST_REG(RS_APU_ABI_KWS_MODEL_STATUS) = 0U;
|
||||
if ((rs_apu_kws_model_load(&image, 1U) != RS_EIO) ||
|
||||
(APU_TEST_REG(RS_APU_ABI_COMMAND) != (UINT32_C(1) << RS_APU_ABI_COMMAND_MODEL_LOAD))) {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
// Verification-only APUC loader for focused engine tests. Product integration
|
||||
// always uses apu_kws_coeff_loader through the private AXI DMA.
|
||||
module apu_kws_coeff_fixture (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic frontend_req_valid_i,
|
||||
output logic frontend_req_ready_o,
|
||||
input logic [ 3:0] frontend_kind_i,
|
||||
input logic [13:0] frontend_index_i,
|
||||
output logic frontend_resp_valid_o,
|
||||
input logic frontend_resp_ready_i,
|
||||
output logic [63:0] frontend_resp_data_o,
|
||||
output logic frontend_resp_fault_o,
|
||||
input logic inference_req_valid_i,
|
||||
output logic inference_req_ready_o,
|
||||
input logic [ 6:0] inference_index_i,
|
||||
output logic inference_resp_valid_o,
|
||||
input logic inference_resp_ready_i,
|
||||
output logic [31:0] inference_resp_data_o,
|
||||
output logic inference_resp_fault_o,
|
||||
output logic initialized_o
|
||||
);
|
||||
logic [31:0] s_apuc_words[0:15375];
|
||||
logic s_loader_active, s_loader_req, s_loader_ready, s_loader_valid, s_loader_fault;
|
||||
logic [13:0] s_loader_addr;
|
||||
logic [31:0] s_loader_data, s_loader_rdata;
|
||||
string s_apuc_path;
|
||||
|
||||
apu_kws_coeff_store u_coeff_store (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.abort_i (1'b0),
|
||||
.valid_i (initialized_o),
|
||||
.loader_active_i (s_loader_active),
|
||||
.loader_req_i (s_loader_req),
|
||||
.loader_write_i (1'b1),
|
||||
.loader_addr_i (s_loader_addr),
|
||||
.loader_data_i (s_loader_data),
|
||||
.loader_ready_o (s_loader_ready),
|
||||
.loader_valid_o (s_loader_valid),
|
||||
.loader_data_o (s_loader_rdata),
|
||||
.loader_fault_o (s_loader_fault),
|
||||
.frontend_req_valid_i (frontend_req_valid_i),
|
||||
.frontend_req_ready_o (frontend_req_ready_o),
|
||||
.frontend_kind_i (frontend_kind_i),
|
||||
.frontend_index_i (frontend_index_i),
|
||||
.frontend_resp_valid_o (frontend_resp_valid_o),
|
||||
.frontend_resp_ready_i (frontend_resp_ready_i),
|
||||
.frontend_resp_data_o (frontend_resp_data_o),
|
||||
.frontend_resp_fault_o (frontend_resp_fault_o),
|
||||
.inference_req_valid_i (inference_req_valid_i),
|
||||
.inference_req_ready_o (inference_req_ready_o),
|
||||
.inference_index_i (inference_index_i),
|
||||
.inference_resp_valid_o(inference_resp_valid_o),
|
||||
.inference_resp_ready_i(inference_resp_ready_i),
|
||||
.inference_resp_data_o (inference_resp_data_o),
|
||||
.inference_resp_fault_o(inference_resp_fault_o),
|
||||
.profile_req_valid_i (1'b0),
|
||||
.profile_req_ready_o (),
|
||||
.profile_index_i (11'd0),
|
||||
.profile_resp_valid_o (),
|
||||
.profile_resp_ready_i (1'b0),
|
||||
.profile_resp_data_o (),
|
||||
.profile_resp_fault_o ()
|
||||
);
|
||||
|
||||
initial begin
|
||||
initialized_o = 1'b0;
|
||||
s_loader_active = 1'b0;
|
||||
s_loader_req = 1'b0;
|
||||
s_loader_addr = 14'd0;
|
||||
s_loader_data = 32'd0;
|
||||
if (!$value$plusargs("APUC_HEX=%s", s_apuc_path)) begin
|
||||
$fatal(1, "missing +APUC_HEX=<path>");
|
||||
end
|
||||
$readmemh(s_apuc_path, s_apuc_words);
|
||||
wait (rst_n_i);
|
||||
@(negedge clk_i);
|
||||
s_loader_active = 1'b1;
|
||||
for (int unsigned word_index = 0; word_index < 15360; word_index++) begin
|
||||
while (!s_loader_ready) @(negedge clk_i);
|
||||
s_loader_addr = 14'(word_index);
|
||||
s_loader_data = s_apuc_words[word_index+16];
|
||||
s_loader_req = 1'b1;
|
||||
@(negedge clk_i);
|
||||
s_loader_req = 1'b0;
|
||||
while (!s_loader_valid) @(negedge clk_i);
|
||||
if (s_loader_fault) $fatal(1, "APUC fixture store fault at word %0d", word_index);
|
||||
end
|
||||
s_loader_active = 1'b0;
|
||||
initialized_o = 1'b1;
|
||||
end
|
||||
|
||||
logic s_unused_loader_data;
|
||||
assign s_unused_loader_data = ^s_loader_rdata;
|
||||
endmodule
|
||||
@@ -51,6 +51,15 @@ module apu_kws_engine_tb;
|
||||
logic [ 5:0][31:0] scratch_write_data;
|
||||
logic [ 5:0][ 3:0] scratch_write_strb;
|
||||
logic scratch_access_err;
|
||||
logic coeff_front_req, coeff_front_ready, coeff_front_resp_valid, coeff_front_resp_ready;
|
||||
logic [3:0] coeff_front_kind;
|
||||
logic [13:0] coeff_front_index;
|
||||
logic [63:0] coeff_front_data;
|
||||
logic coeff_front_fault;
|
||||
logic coeff_infer_req, coeff_infer_ready, coeff_infer_resp_valid, coeff_infer_resp_ready;
|
||||
logic [ 6:0] coeff_infer_index;
|
||||
logic [31:0] coeff_infer_data;
|
||||
logic coeff_infer_fault, coeff_initialized;
|
||||
logic [ 7:0] mfcc_bytes [ 0:489];
|
||||
logic [ 7:0] expected_layers [0:72087];
|
||||
logic use_apum = 1'b0;
|
||||
@@ -160,6 +169,27 @@ module apu_kws_engine_tb;
|
||||
.scratch_access_err_o (scratch_access_err)
|
||||
);
|
||||
|
||||
apu_kws_coeff_fixture u_coeff_fixture (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.frontend_req_valid_i (coeff_front_req),
|
||||
.frontend_req_ready_o (coeff_front_ready),
|
||||
.frontend_kind_i (coeff_front_kind),
|
||||
.frontend_index_i (coeff_front_index),
|
||||
.frontend_resp_valid_o (coeff_front_resp_valid),
|
||||
.frontend_resp_ready_i (coeff_front_resp_ready),
|
||||
.frontend_resp_data_o (coeff_front_data),
|
||||
.frontend_resp_fault_o (coeff_front_fault),
|
||||
.inference_req_valid_i (coeff_infer_req),
|
||||
.inference_req_ready_o (coeff_infer_ready),
|
||||
.inference_index_i (coeff_infer_index),
|
||||
.inference_resp_valid_o(coeff_infer_resp_valid),
|
||||
.inference_resp_ready_i(coeff_infer_resp_ready),
|
||||
.inference_resp_data_o (coeff_infer_data),
|
||||
.inference_resp_fault_o(coeff_infer_fault),
|
||||
.initialized_o (coeff_initialized)
|
||||
);
|
||||
|
||||
apu_kws_engine dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
@@ -222,6 +252,21 @@ module apu_kws_engine_tb;
|
||||
.scratch_write_data_o (scratch_write_data),
|
||||
.scratch_write_strb_o (scratch_write_strb),
|
||||
.scratch_access_err_i (scratch_access_err),
|
||||
.coeff_frontend_req_valid_o (coeff_front_req),
|
||||
.coeff_frontend_req_ready_i (coeff_front_ready),
|
||||
.coeff_frontend_kind_o (coeff_front_kind),
|
||||
.coeff_frontend_index_o (coeff_front_index),
|
||||
.coeff_frontend_resp_valid_i (coeff_front_resp_valid),
|
||||
.coeff_frontend_resp_ready_o (coeff_front_resp_ready),
|
||||
.coeff_frontend_data_i (coeff_front_data),
|
||||
.coeff_frontend_fault_i (coeff_front_fault),
|
||||
.coeff_inference_req_valid_o (coeff_infer_req),
|
||||
.coeff_inference_req_ready_i (coeff_infer_ready),
|
||||
.coeff_inference_index_o (coeff_infer_index),
|
||||
.coeff_inference_resp_valid_i(coeff_infer_resp_valid),
|
||||
.coeff_inference_resp_ready_o(coeff_infer_resp_ready),
|
||||
.coeff_inference_data_i (coeff_infer_data),
|
||||
.coeff_inference_fault_i (coeff_infer_fault),
|
||||
.stream_i (stream_i),
|
||||
.rx_ready_o (rx_ready_o),
|
||||
.status_o (status_o),
|
||||
@@ -258,6 +303,7 @@ module apu_kws_engine_tb;
|
||||
|
||||
repeat (2) @(negedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
wait (coeff_initialized);
|
||||
@(negedge clk_i);
|
||||
if (!model_valid_o || !model_lock_o) $fatal(1, "KWS model admission was not observed");
|
||||
dut.s_frame_count_q = 32'd11;
|
||||
@@ -381,7 +427,7 @@ module apu_kws_engine_tb;
|
||||
dut.s_infer_state_q = 4'd1;
|
||||
elapsed_cycles = 0;
|
||||
observed_operator = 0;
|
||||
while ((inference_count_o == 0) && (elapsed_cycles < 600000)) begin
|
||||
while ((inference_count_o == 0) && (elapsed_cycles < 4000000)) begin
|
||||
@(negedge clk_i);
|
||||
if (check_layers && (dut.s_operator_q != observed_operator)) begin
|
||||
check_layer(observed_operator);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module apu_kws_loader_tb;
|
||||
`include "apu_kws_apum_profile.svh"
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic start_i = 1'b0;
|
||||
@@ -50,6 +51,10 @@ module apu_kws_loader_tb;
|
||||
integer abort_done_count;
|
||||
integer cycle_count;
|
||||
integer writes_before_success;
|
||||
logic profile_request_valid_o;
|
||||
logic [10:0] profile_index_o;
|
||||
logic profile_response_valid_i;
|
||||
logic [31:0] profile_data_i;
|
||||
|
||||
task automatic drive_dma(input integer first_word, input integer word_count,
|
||||
input logic [31:0] expected_address, input logic [31:0] expected_bytes);
|
||||
@@ -137,6 +142,19 @@ module apu_kws_loader_tb;
|
||||
abort_done_count <= abort_done_count + 1;
|
||||
end
|
||||
end
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
profile_response_valid_i <= 1'b0;
|
||||
profile_data_i <= 32'd0;
|
||||
end else begin
|
||||
if (profile_request_valid_o) begin
|
||||
profile_response_valid_i <= 1'b1;
|
||||
profile_data_i <= apu_kws_apum_fixed_word(profile_index_o);
|
||||
end else if (profile_response_valid_i) begin
|
||||
profile_response_valid_i <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
apu_kws_model_loader dut (
|
||||
.clk_i (clk_i),
|
||||
@@ -172,6 +190,13 @@ module apu_kws_loader_tb;
|
||||
.local_data_o (),
|
||||
.local_strb_o (),
|
||||
.local_ready_i (1'b1),
|
||||
.profile_request_valid_o (profile_request_valid_o),
|
||||
.profile_request_ready_i (1'b1),
|
||||
.profile_index_o (profile_index_o),
|
||||
.profile_response_valid_i(profile_response_valid_i),
|
||||
.profile_response_ready_o(),
|
||||
.profile_data_i (profile_data_i),
|
||||
.profile_fault_i (1'b0),
|
||||
.busy_o (busy_o),
|
||||
.valid_o (valid_o),
|
||||
.lock_o (lock_o),
|
||||
|
||||
@@ -57,6 +57,15 @@ module apu_p7_accuracy_tb;
|
||||
logic [ 5:0][31:0] scratch_write_data;
|
||||
logic [ 5:0][ 3:0] scratch_write_strb;
|
||||
logic scratch_access_err;
|
||||
logic coeff_front_req, coeff_front_ready, coeff_front_resp_valid, coeff_front_resp_ready;
|
||||
logic [3:0] coeff_front_kind;
|
||||
logic [13:0] coeff_front_index;
|
||||
logic [63:0] coeff_front_data;
|
||||
logic coeff_front_fault;
|
||||
logic coeff_infer_req, coeff_infer_ready, coeff_infer_resp_valid, coeff_infer_resp_ready;
|
||||
logic [ 6:0] coeff_infer_index;
|
||||
logic [31:0] coeff_infer_data;
|
||||
logic coeff_infer_fault, coeff_initialized;
|
||||
logic [31:0] pcm_mem[0:PcmWords-1];
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
@@ -108,6 +117,27 @@ module apu_p7_accuracy_tb;
|
||||
.scratch_access_err_o (scratch_access_err)
|
||||
);
|
||||
|
||||
apu_kws_coeff_fixture u_coeff_fixture (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.frontend_req_valid_i (coeff_front_req),
|
||||
.frontend_req_ready_o (coeff_front_ready),
|
||||
.frontend_kind_i (coeff_front_kind),
|
||||
.frontend_index_i (coeff_front_index),
|
||||
.frontend_resp_valid_o (coeff_front_resp_valid),
|
||||
.frontend_resp_ready_i (coeff_front_resp_ready),
|
||||
.frontend_resp_data_o (coeff_front_data),
|
||||
.frontend_resp_fault_o (coeff_front_fault),
|
||||
.inference_req_valid_i (coeff_infer_req),
|
||||
.inference_req_ready_o (coeff_infer_ready),
|
||||
.inference_index_i (coeff_infer_index),
|
||||
.inference_resp_valid_o(coeff_infer_resp_valid),
|
||||
.inference_resp_ready_i(coeff_infer_resp_ready),
|
||||
.inference_resp_data_o (coeff_infer_data),
|
||||
.inference_resp_fault_o(coeff_infer_fault),
|
||||
.initialized_o (coeff_initialized)
|
||||
);
|
||||
|
||||
apu_kws_engine dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
@@ -170,6 +200,21 @@ module apu_p7_accuracy_tb;
|
||||
.scratch_write_data_o (scratch_write_data),
|
||||
.scratch_write_strb_o (scratch_write_strb),
|
||||
.scratch_access_err_i (scratch_access_err),
|
||||
.coeff_frontend_req_valid_o (coeff_front_req),
|
||||
.coeff_frontend_req_ready_i (coeff_front_ready),
|
||||
.coeff_frontend_kind_o (coeff_front_kind),
|
||||
.coeff_frontend_index_o (coeff_front_index),
|
||||
.coeff_frontend_resp_valid_i (coeff_front_resp_valid),
|
||||
.coeff_frontend_resp_ready_o (coeff_front_resp_ready),
|
||||
.coeff_frontend_data_i (coeff_front_data),
|
||||
.coeff_frontend_fault_i (coeff_front_fault),
|
||||
.coeff_inference_req_valid_o (coeff_infer_req),
|
||||
.coeff_inference_req_ready_i (coeff_infer_ready),
|
||||
.coeff_inference_index_o (coeff_infer_index),
|
||||
.coeff_inference_resp_valid_i(coeff_infer_resp_valid),
|
||||
.coeff_inference_resp_ready_o(coeff_infer_resp_ready),
|
||||
.coeff_inference_data_i (coeff_infer_data),
|
||||
.coeff_inference_fault_i (coeff_infer_fault),
|
||||
.stream_i (stream_i),
|
||||
.rx_ready_o (rx_ready_o),
|
||||
.status_o (status_o),
|
||||
@@ -293,6 +338,7 @@ module apu_p7_accuracy_tb;
|
||||
|
||||
repeat (4) @(negedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
wait (coeff_initialized);
|
||||
@(negedge clk_i);
|
||||
if (!model_valid_o || !model_lock_o) $fatal(1, "KWS model admission was not observed");
|
||||
$readmemh(apum_hex, u_kws_sram_client.mem, 0, ModelWords - 1);
|
||||
|
||||
@@ -16,6 +16,7 @@ module apu_p7_concurrent_tb;
|
||||
localparam logic [31:0] WavBase = 32'h3002_0000;
|
||||
localparam logic [31:0] FlacBase = 32'h3003_0000;
|
||||
localparam logic [31:0] WriteBase = 32'h3004_0000;
|
||||
localparam logic [31:0] CoeffBase = 32'h3005_0000;
|
||||
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
@@ -24,6 +25,7 @@ module apu_p7_concurrent_tb;
|
||||
logic [31:0] model [ 0:8191];
|
||||
logic [31:0] wav_image [ 0:8191];
|
||||
logic [31:0] flac_image [ 0:1023];
|
||||
logic [31:0] coefficient_image[0:15375];
|
||||
logic [31:0] wav_pcm [0:16383];
|
||||
logic [31:0] flac_pcm [0:16383];
|
||||
logic read_active_q;
|
||||
@@ -33,7 +35,7 @@ module apu_p7_concurrent_tb;
|
||||
logic [31:0] s_value;
|
||||
|
||||
// Scenario plusarg state.
|
||||
string apumc_path, apum_path, wav_path, flac_path, wav_pcm_path, flac_pcm_path;
|
||||
string apumc_path, apum_path, apuc_path, wav_path, flac_path, wav_pcm_path, flac_pcm_path;
|
||||
logic [31:0] wav_bytes, wav_frames, wav_out_words;
|
||||
logic [31:0] flac_bytes, flac_frames, flac_out_words;
|
||||
logic [31:0] rate_hz, precision_bits, seconds_ms, seed_value, workload_mask, job_log;
|
||||
@@ -115,10 +117,12 @@ module apu_p7_concurrent_tb;
|
||||
// Ready-memory AXI read model: no injected stalls, back-to-back beats.
|
||||
assign axi4.arready = !read_active_q;
|
||||
assign axi4.rid = 1'b0;
|
||||
assign axi4.rdata = (read_addr_q >= FlacBase) ? flac_image[(read_addr_q-FlacBase)>>2] :
|
||||
assign axi4.rdata = (read_addr_q >= CoeffBase) ?
|
||||
coefficient_image[(read_addr_q-CoeffBase)>>2] :
|
||||
((read_addr_q >= FlacBase) ? flac_image[(read_addr_q-FlacBase)>>2] :
|
||||
((read_addr_q >= WavBase) ? wav_image[(read_addr_q-WavBase)>>2] :
|
||||
((read_addr_q >= ModelBase) ? model[(read_addr_q-ModelBase)>>2] :
|
||||
image[(read_addr_q-ImageBase)>>2]));
|
||||
image[(read_addr_q-ImageBase)>>2])));
|
||||
assign axi4.rresp = 2'd0;
|
||||
assign axi4.rlast = read_beat_q == read_len_q;
|
||||
assign axi4.ruser = 1'b0;
|
||||
@@ -479,6 +483,7 @@ module apu_p7_concurrent_tb;
|
||||
wav_source_rate = 32'd0;
|
||||
if (!$value$plusargs("APUMC_HEX=%s", apumc_path)) $fatal(1, "APUMC_HEX plusarg missing");
|
||||
if (!$value$plusargs("APUM_HEX=%s", apum_path)) $fatal(1, "APUM_HEX plusarg missing");
|
||||
if (!$value$plusargs("APUC_HEX=%s", apuc_path)) $fatal(1, "APUC_HEX plusarg missing");
|
||||
if (!$value$plusargs("WAV_HEX=%s", wav_path)) $fatal(1, "WAV_HEX plusarg missing");
|
||||
if (!$value$plusargs("FLAC_HEX=%s", flac_path)) $fatal(1, "FLAC_HEX plusarg missing");
|
||||
if (!$value$plusargs("WAV_PCM_HEX=%s", wav_pcm_path)) $fatal(1, "WAV_PCM_HEX missing");
|
||||
@@ -508,6 +513,7 @@ module apu_p7_concurrent_tb;
|
||||
|
||||
$readmemh(apumc_path, image);
|
||||
$readmemh(apum_path, model);
|
||||
$readmemh(apuc_path, coefficient_image);
|
||||
$readmemh(wav_path, wav_image);
|
||||
$readmemh(flac_path, flac_image);
|
||||
$readmemh(wav_pcm_path, wav_pcm);
|
||||
@@ -542,7 +548,7 @@ module apu_p7_concurrent_tb;
|
||||
repeat (2) @(posedge clk_i);
|
||||
|
||||
apb_write(`APB4_APU__READ_BASE, ImageBase);
|
||||
apb_write(`APB4_APU__READ_LIMIT, FlacBase + 32'd4095);
|
||||
apb_write(`APB4_APU__READ_LIMIT, CoeffBase + 32'd61503);
|
||||
apb_write(`APB4_APU__WRITE_BASE, WriteBase);
|
||||
apb_write(`APB4_APU__WRITE_LIMIT, WriteBase + 32'd255);
|
||||
apb_write(`APB4_APU__MC_IMAGE_ADDRESS, ImageBase);
|
||||
@@ -558,14 +564,24 @@ module apu_p7_concurrent_tb;
|
||||
if (!s_value[`APB4_APU__MC_STATUS_VALID]) $fatal(1, "P7 concurrent microcode load timeout");
|
||||
$display("CONCURRENT_SETUP microcode loaded crc=%h", image[11]);
|
||||
|
||||
apb_write(`APB4_APU__KWS_COEFF_ADDRESS, CoeffBase);
|
||||
apb_write(`APB4_APU__KWS_COEFF_SIZE, 32'd61504);
|
||||
apb_write(`APB4_APU__KWS_COEFF_EXPECTED_CRC, `APB4_APU__APUC_PAYLOAD_CRC);
|
||||
apb_write(`APB4_APU__KWS_COEFF_COMMAND, 32'd1);
|
||||
for (int poll = 0; poll < 200000; poll++) begin
|
||||
apb_read(`APB4_APU__KWS_COEFF_STATUS, s_value);
|
||||
if (!s_value[`APB4_APU__KWS_COEFF_STATUS_BUSY]) break;
|
||||
end
|
||||
if (s_value[2:1] != 2'b11) begin
|
||||
apb_read(`APB4_APU__ERROR_DETAIL, s_value);
|
||||
$fatal(1, "P7 concurrent coefficient load failed detail=%h", s_value);
|
||||
end
|
||||
$display("CONCURRENT_SETUP coefficients loaded status=%h", s_value);
|
||||
|
||||
apb_write(`APB4_APU__KWS_MODEL_ADDRESS, ModelBase);
|
||||
apb_write(`APB4_APU__KWS_MODEL_SIZE, 32'd32768);
|
||||
apb_write(`APB4_APU__KWS_MODEL_EXPECTED_CRC, `APB4_APU__APUM_PAYLOAD_CRC);
|
||||
apb_write(`APB4_APU__COMMAND, 32'd1 << `APB4_APU__COMMAND_MODEL_LOAD);
|
||||
// The model load is admitted under quiesce, but its DMA only runs once
|
||||
// quiesce deasserts (apu_dma blocks new requests while quiesced and the
|
||||
// microcode loader is idle).
|
||||
apb4.pprot = 3'd0;
|
||||
for (int poll = 0; poll < 500000; poll++) begin
|
||||
apb_read(`APB4_APU__KWS_MODEL_STATUS, s_value);
|
||||
if (!s_value[0]) break;
|
||||
@@ -577,6 +593,7 @@ module apu_p7_concurrent_tb;
|
||||
$fatal(1, "P7 concurrent model load failed detail=%h", s_value);
|
||||
end
|
||||
$display("CONCURRENT_SETUP model loaded status=%h", s_value);
|
||||
apb4.pprot = 3'd0;
|
||||
|
||||
apb_write(`APB4_APU__KWS_INPUT_CONFIG, kws_input_config);
|
||||
apb_write(`APB4_APU__KWS_CONFIG, 32'h0000_0180);
|
||||
|
||||
@@ -60,10 +60,37 @@ module apu_p7_frontend_dump_tb;
|
||||
logic [ 5:0][31:0] scratch_write_data;
|
||||
logic [ 5:0][ 3:0] scratch_write_strb;
|
||||
logic scratch_access_err;
|
||||
logic coeff_front_req, coeff_front_ready, coeff_front_resp_valid, coeff_front_resp_ready;
|
||||
logic [3:0] coeff_front_kind;
|
||||
logic [13:0] coeff_front_index;
|
||||
logic [63:0] coeff_front_data;
|
||||
logic coeff_front_fault;
|
||||
logic coeff_infer_req, coeff_infer_ready, coeff_infer_resp_valid, coeff_infer_resp_ready;
|
||||
logic [ 6:0] coeff_infer_index;
|
||||
logic [31:0] coeff_infer_data;
|
||||
logic coeff_infer_fault, coeff_initialized;
|
||||
int unsigned debug_commit_count;
|
||||
logic [31:0] pcm_mem[0:PcmWords-1];
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (rst_n_i && $test$plusargs(
|
||||
"DEBUG_P9"
|
||||
) && dut.s_commit_valid && (debug_commit_count < 5)) begin
|
||||
$display("P9_COMMIT index=%0d sample=%0d next_peak=%0d wait=%0b ready=%0b",
|
||||
debug_commit_count, $signed(dut.s_commit_sample), dut.s_next_peak,
|
||||
dut.s_storage_wait_q, storage_ready);
|
||||
debug_commit_count <= debug_commit_count + 1'b1;
|
||||
end
|
||||
if (rst_n_i && $test$plusargs(
|
||||
"DEBUG_P9"
|
||||
) && dut.s_commit_valid && (dut.s_block_samples_q == 8'd159) &&
|
||||
(dut.s_peak_write_q == 7'd0)) begin
|
||||
$display("P9_PEAK_WRITE value=%0d", dut.s_next_peak);
|
||||
end
|
||||
end
|
||||
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) stream_i (
|
||||
@@ -111,6 +138,27 @@ module apu_p7_frontend_dump_tb;
|
||||
.scratch_access_err_o (scratch_access_err)
|
||||
);
|
||||
|
||||
apu_kws_coeff_fixture u_coeff_fixture (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.frontend_req_valid_i (coeff_front_req),
|
||||
.frontend_req_ready_o (coeff_front_ready),
|
||||
.frontend_kind_i (coeff_front_kind),
|
||||
.frontend_index_i (coeff_front_index),
|
||||
.frontend_resp_valid_o (coeff_front_resp_valid),
|
||||
.frontend_resp_ready_i (coeff_front_resp_ready),
|
||||
.frontend_resp_data_o (coeff_front_data),
|
||||
.frontend_resp_fault_o (coeff_front_fault),
|
||||
.inference_req_valid_i (coeff_infer_req),
|
||||
.inference_req_ready_o (coeff_infer_ready),
|
||||
.inference_index_i (coeff_infer_index),
|
||||
.inference_resp_valid_o(coeff_infer_resp_valid),
|
||||
.inference_resp_ready_i(coeff_infer_resp_ready),
|
||||
.inference_resp_data_o (coeff_infer_data),
|
||||
.inference_resp_fault_o(coeff_infer_fault),
|
||||
.initialized_o (coeff_initialized)
|
||||
);
|
||||
|
||||
apu_kws_engine dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
@@ -173,6 +221,21 @@ module apu_p7_frontend_dump_tb;
|
||||
.scratch_write_data_o (scratch_write_data),
|
||||
.scratch_write_strb_o (scratch_write_strb),
|
||||
.scratch_access_err_i (scratch_access_err),
|
||||
.coeff_frontend_req_valid_o (coeff_front_req),
|
||||
.coeff_frontend_req_ready_i (coeff_front_ready),
|
||||
.coeff_frontend_kind_o (coeff_front_kind),
|
||||
.coeff_frontend_index_o (coeff_front_index),
|
||||
.coeff_frontend_resp_valid_i (coeff_front_resp_valid),
|
||||
.coeff_frontend_resp_ready_o (coeff_front_resp_ready),
|
||||
.coeff_frontend_data_i (coeff_front_data),
|
||||
.coeff_frontend_fault_i (coeff_front_fault),
|
||||
.coeff_inference_req_valid_o (coeff_infer_req),
|
||||
.coeff_inference_req_ready_i (coeff_infer_ready),
|
||||
.coeff_inference_index_o (coeff_infer_index),
|
||||
.coeff_inference_resp_valid_i(coeff_infer_resp_valid),
|
||||
.coeff_inference_resp_ready_o(coeff_infer_resp_ready),
|
||||
.coeff_inference_data_i (coeff_infer_data),
|
||||
.coeff_inference_fault_i (coeff_infer_fault),
|
||||
.stream_i (stream_i),
|
||||
.rx_ready_o (rx_ready_o),
|
||||
.status_o (status_o),
|
||||
@@ -303,9 +366,11 @@ module apu_p7_frontend_dump_tb;
|
||||
max_window_cycles = 4000000;
|
||||
end
|
||||
if (window_count == 0) $fatal(1, "WINDOW_COUNT must be positive");
|
||||
debug_commit_count = 0;
|
||||
|
||||
repeat (4) @(negedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
wait (coeff_initialized);
|
||||
@(negedge clk_i);
|
||||
if (!model_valid_o || !model_lock_o) $fatal(1, "KWS model admission was not observed");
|
||||
$readmemh(apum_hex, u_kws_sram_client.mem, 0, ModelWords - 1);
|
||||
|
||||
@@ -19,6 +19,7 @@ module apu_p8_quiesce_load_tb;
|
||||
localparam logic [31:0] ImageBase = 32'h3000_0000;
|
||||
localparam logic [31:0] ModelBase = 32'h3001_0000;
|
||||
localparam logic [31:0] ModelBytes = 32'd32768;
|
||||
localparam logic [31:0] CoeffBase = 32'h3002_0000;
|
||||
localparam logic [63:0] MaxCycles = 64'd2000000;
|
||||
|
||||
logic clk_i = 1'b0;
|
||||
@@ -26,6 +27,7 @@ module apu_p8_quiesce_load_tb;
|
||||
logic quiesce_q;
|
||||
logic [31:0] image [0:16383];
|
||||
logic [31:0] model [ 0:8191];
|
||||
logic [31:0] coefficient [0:15375];
|
||||
logic read_active_q;
|
||||
logic [31:0] read_addr_q;
|
||||
logic [7:0] read_len_q, read_beat_q;
|
||||
@@ -34,7 +36,7 @@ module apu_p8_quiesce_load_tb;
|
||||
logic [63:0] beats_before;
|
||||
logic [31:0] s_value;
|
||||
|
||||
string apumc_path, apum_path;
|
||||
string apumc_path, apum_path, apuc_path;
|
||||
|
||||
apb4_if apb4 (
|
||||
.pclk (clk_i),
|
||||
@@ -79,8 +81,9 @@ module apu_p8_quiesce_load_tb;
|
||||
// Ready-memory AXI read model: no injected stalls, back-to-back beats.
|
||||
assign axi4.arready = !read_active_q;
|
||||
assign axi4.rid = 1'b0;
|
||||
assign axi4.rdata = (read_addr_q >= ModelBase) ? model[(read_addr_q-ModelBase)>>2] :
|
||||
image[(read_addr_q-ImageBase)>>2];
|
||||
assign axi4.rdata = (read_addr_q >= CoeffBase) ? coefficient[(read_addr_q-CoeffBase)>>2] :
|
||||
((read_addr_q >= ModelBase) ? model[(read_addr_q-ModelBase)>>2] :
|
||||
image[(read_addr_q-ImageBase)>>2]);
|
||||
assign axi4.rresp = 2'd0;
|
||||
assign axi4.rlast = read_beat_q == read_len_q;
|
||||
assign axi4.ruser = 1'b0;
|
||||
@@ -206,8 +209,10 @@ module apu_p8_quiesce_load_tb;
|
||||
initial begin
|
||||
if (!$value$plusargs("APUMC_HEX=%s", apumc_path)) $fatal(1, "APUMC_HEX plusarg missing");
|
||||
if (!$value$plusargs("APUM_HEX=%s", apum_path)) $fatal(1, "APUM_HEX plusarg missing");
|
||||
if (!$value$plusargs("APUC_HEX=%s", apuc_path)) $fatal(1, "APUC_HEX plusarg missing");
|
||||
$readmemh(apumc_path, image);
|
||||
$readmemh(apum_path, model);
|
||||
$readmemh(apuc_path, coefficient);
|
||||
|
||||
apb4.paddr = 32'd0;
|
||||
apb4.pprot = 3'd0;
|
||||
@@ -232,7 +237,7 @@ module apu_p8_quiesce_load_tb;
|
||||
if (!s_value[`APB4_APU__STATUS_IDLE]) $fatal(1, "P8 quiesce load core not idle %h", s_value);
|
||||
|
||||
apb_write(`APB4_APU__READ_BASE, ImageBase);
|
||||
apb_write(`APB4_APU__READ_LIMIT, ModelBase + ModelBytes - 32'd1);
|
||||
apb_write(`APB4_APU__READ_LIMIT, CoeffBase + 32'd61503);
|
||||
|
||||
// MICROCODE_LOAD under quiesce: the microcode loader was always exempt
|
||||
// from the DMA quiesce while it is itself busy; this leg must keep working.
|
||||
@@ -262,6 +267,26 @@ module apu_p8_quiesce_load_tb;
|
||||
);
|
||||
$display("P8_QUIESCE_MC crc=%h beats=%0d", s_value, quiesced_beats_q - beats_before);
|
||||
|
||||
apb_write(`APB4_APU__KWS_COEFF_ADDRESS, CoeffBase);
|
||||
apb_write(`APB4_APU__KWS_COEFF_SIZE, 32'd61504);
|
||||
apb_write(`APB4_APU__KWS_COEFF_EXPECTED_CRC, `APB4_APU__APUC_PAYLOAD_CRC);
|
||||
beats_before = quiesced_beats_q;
|
||||
apb_write(`APB4_APU__KWS_COEFF_COMMAND, 32'd1);
|
||||
for (int poll = 0; poll < 200000; poll++) begin
|
||||
apb_read(`APB4_APU__KWS_COEFF_STATUS, s_value);
|
||||
if (!s_value[`APB4_APU__KWS_COEFF_STATUS_BUSY]) break;
|
||||
end
|
||||
if (s_value[2:1] != 2'b11) begin
|
||||
apb_read(`APB4_APU__ERROR_DETAIL, s_value);
|
||||
$fatal(1, "P8 quiesce load coefficient load failed detail=%h", s_value);
|
||||
end
|
||||
apb_read(`APB4_APU__KWS_COEFF_ACTUAL_CRC, s_value);
|
||||
if (s_value != `APB4_APU__APUC_PAYLOAD_CRC)
|
||||
$fatal(1, "P8 quiesce load coefficient CRC mismatch actual=%h", s_value);
|
||||
if (quiesced_beats_q - beats_before < 64'd15376)
|
||||
$fatal(1, "P8 quiesce load coefficient bytes did not cross AXI under quiesce");
|
||||
$display("P8_QUIESCE_COEFF crc=%h beats=%0d", s_value, quiesced_beats_q - beats_before);
|
||||
|
||||
apb_read(`APB4_APU__STATUS, s_value);
|
||||
if (!s_value[`APB4_APU__STATUS_IDLE])
|
||||
$fatal(1, "P8 quiesce load core not idle before model load %h", s_value);
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module apu_p9_storage_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic loader_active, loader_req, loader_write, loader_ready, loader_valid, loader_fault;
|
||||
logic [13:0] loader_addr;
|
||||
logic [31:0] loader_data, loader_rdata;
|
||||
logic front_req, front_ready, front_valid, front_consume, front_fault;
|
||||
logic [ 3:0] front_kind;
|
||||
logic [13:0] front_index;
|
||||
logic [63:0] front_data;
|
||||
logic infer_req, infer_ready, infer_valid, infer_consume, infer_fault;
|
||||
logic [ 6:0] infer_index;
|
||||
logic [31:0] infer_data;
|
||||
logic memo_clear, memo_lookup, memo_insert, memo_ready, memo_done, memo_valid;
|
||||
logic [12:0] memo_addr;
|
||||
logic [63:0] memo_key, memo_read_key;
|
||||
int unsigned clear_cycles;
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
apu_kws_coeff_store u_coeff_store (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.abort_i (1'b0),
|
||||
.valid_i (!loader_active),
|
||||
.loader_active_i (loader_active),
|
||||
.loader_req_i (loader_req),
|
||||
.loader_write_i (loader_write),
|
||||
.loader_addr_i (loader_addr),
|
||||
.loader_data_i (loader_data),
|
||||
.loader_ready_o (loader_ready),
|
||||
.loader_valid_o (loader_valid),
|
||||
.loader_data_o (loader_rdata),
|
||||
.loader_fault_o (loader_fault),
|
||||
.frontend_req_valid_i (front_req),
|
||||
.frontend_req_ready_o (front_ready),
|
||||
.frontend_kind_i (front_kind),
|
||||
.frontend_index_i (front_index),
|
||||
.frontend_resp_valid_o (front_valid),
|
||||
.frontend_resp_ready_i (front_consume),
|
||||
.frontend_resp_data_o (front_data),
|
||||
.frontend_resp_fault_o (front_fault),
|
||||
.inference_req_valid_i (infer_req),
|
||||
.inference_req_ready_o (infer_ready),
|
||||
.inference_index_i (infer_index),
|
||||
.inference_resp_valid_o(infer_valid),
|
||||
.inference_resp_ready_i(infer_consume),
|
||||
.inference_resp_data_o (infer_data),
|
||||
.inference_resp_fault_o(infer_fault),
|
||||
.profile_req_valid_i (1'b0),
|
||||
.profile_req_ready_o (),
|
||||
.profile_index_i (11'd0),
|
||||
.profile_resp_valid_o (),
|
||||
.profile_resp_ready_i (1'b0),
|
||||
.profile_resp_data_o (),
|
||||
.profile_resp_fault_o ()
|
||||
);
|
||||
|
||||
apu_proof_memo u_proof_memo (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.abort_i (1'b0),
|
||||
.clear_i (memo_clear),
|
||||
.lookup_i (memo_lookup),
|
||||
.insert_i (memo_insert),
|
||||
.addr_i (memo_addr),
|
||||
.key_i (memo_key),
|
||||
.ready_o (memo_ready),
|
||||
.done_o (memo_done),
|
||||
.read_valid_o(memo_valid),
|
||||
.read_key_o (memo_read_key)
|
||||
);
|
||||
|
||||
task automatic write_coefficient(input logic [13:0] address_i, input logic [31:0] data_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
loader_addr = address_i;
|
||||
loader_data = data_i;
|
||||
loader_req = 1'b1;
|
||||
@(negedge clk_i);
|
||||
loader_req = 1'b0;
|
||||
wait (loader_valid);
|
||||
if (loader_fault) $fatal(1, "coefficient write fault at %0d", address_i);
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic memo_command(input logic clear_i, input logic lookup_i, input logic insert_i,
|
||||
input logic [12:0] address_i, input logic [63:0] key_i);
|
||||
begin
|
||||
wait (memo_ready);
|
||||
@(negedge clk_i);
|
||||
memo_addr = address_i;
|
||||
memo_key = key_i;
|
||||
memo_clear = clear_i;
|
||||
memo_lookup = lookup_i;
|
||||
memo_insert = insert_i;
|
||||
@(negedge clk_i);
|
||||
memo_clear = 1'b0;
|
||||
memo_lookup = 1'b0;
|
||||
memo_insert = 1'b0;
|
||||
wait (memo_done);
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
loader_active = 1'b0;
|
||||
loader_req = 1'b0;
|
||||
loader_write = 1'b1;
|
||||
loader_addr = 14'd0;
|
||||
loader_data = 32'd0;
|
||||
front_req = 1'b0;
|
||||
front_kind = 4'd0;
|
||||
front_index = 14'd0;
|
||||
front_consume = 1'b0;
|
||||
infer_req = 1'b0;
|
||||
infer_index = 7'd0;
|
||||
infer_consume = 1'b0;
|
||||
memo_clear = 1'b0;
|
||||
memo_lookup = 1'b0;
|
||||
memo_insert = 1'b0;
|
||||
memo_addr = 13'd0;
|
||||
memo_key = 64'd0;
|
||||
repeat (3) @(negedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
|
||||
loader_active = 1'b1;
|
||||
write_coefficient(14'(1024 + 5), 32'h1111_1111);
|
||||
write_coefficient(14'(2048 + 5), 32'h2222_2222);
|
||||
write_coefficient(14'(14 * 1024 + 512 + 3), 32'h3333_3333);
|
||||
loader_active = 1'b0;
|
||||
|
||||
@(negedge clk_i);
|
||||
front_kind = 4'd1;
|
||||
front_index = 14'd5;
|
||||
front_req = 1'b1;
|
||||
infer_index = 7'd3;
|
||||
infer_req = 1'b1;
|
||||
if (!front_ready || !infer_ready) $fatal(1, "parallel coefficient clients were not ready");
|
||||
@(negedge clk_i);
|
||||
front_req = 1'b0;
|
||||
infer_req = 1'b0;
|
||||
wait (front_valid && infer_valid);
|
||||
if (front_fault || infer_fault || (front_data != 64'h2222_2222_1111_1111) ||
|
||||
(infer_data != 32'h3333_3333)) begin
|
||||
$fatal(1, "parallel coefficient response mismatch");
|
||||
end
|
||||
repeat (2) begin
|
||||
@(negedge clk_i);
|
||||
if (!front_valid || !infer_valid) $fatal(1, "coefficient response was not retained");
|
||||
end
|
||||
front_consume = 1'b1;
|
||||
infer_consume = 1'b1;
|
||||
@(negedge clk_i);
|
||||
front_consume = 1'b0;
|
||||
infer_consume = 1'b0;
|
||||
|
||||
front_kind = 4'd9;
|
||||
front_index = 14'd0;
|
||||
front_req = 1'b1;
|
||||
@(negedge clk_i);
|
||||
front_req = 1'b0;
|
||||
wait (front_valid);
|
||||
if (!front_fault) $fatal(1, "illegal coefficient kind did not fault");
|
||||
front_consume = 1'b1;
|
||||
@(negedge clk_i);
|
||||
front_consume = 1'b0;
|
||||
|
||||
clear_cycles = 0;
|
||||
@(negedge clk_i);
|
||||
memo_clear = 1'b1;
|
||||
@(negedge clk_i);
|
||||
memo_clear = 1'b0;
|
||||
while (!memo_done) begin
|
||||
clear_cycles++;
|
||||
@(negedge clk_i);
|
||||
end
|
||||
if (clear_cycles > 260) $fatal(1, "memo clear exceeded 260 cycles");
|
||||
memo_command(1'b0, 1'b0, 1'b1, 13'd31, 64'h0123_4567_89ab_cdef);
|
||||
memo_command(1'b0, 1'b1, 1'b0, 13'd31, 64'h0123_4567_89ab_cdef);
|
||||
if (!memo_valid || (memo_read_key != 64'h0123_4567_89ab_cdef)) begin
|
||||
$fatal(1, "memo key/bitmap lookup mismatch");
|
||||
end
|
||||
memo_command(1'b0, 1'b1, 1'b0, 13'd32, 64'd0);
|
||||
if (memo_valid) $fatal(1, "adjacent bitmap row was unexpectedly valid");
|
||||
|
||||
$display("APU-P9 coefficient store and proof memo test passed");
|
||||
$finish;
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (2000) @(posedge clk_i);
|
||||
$fatal(1, "APU-P9 storage test timeout");
|
||||
end
|
||||
endmodule
|
||||
+55
-14
@@ -2,9 +2,11 @@
|
||||
|
||||
`include "apu_define.svh"
|
||||
|
||||
module apu_reg_tb;
|
||||
module apu_reg_tb #(
|
||||
parameter bit EnableP7 = 1'b1
|
||||
);
|
||||
localparam logic [31:0] ApuBase = 32'h1001_3000;
|
||||
localparam int unsigned RegisterCount = 97;
|
||||
localparam int unsigned RegisterCount = 106;
|
||||
localparam logic [1:0] AccessRo = 2'd0;
|
||||
localparam logic [1:0] AccessWo = 2'd1;
|
||||
localparam logic [1:0] AccessRw = 2'd2;
|
||||
@@ -98,7 +100,7 @@ module apu_reg_tb;
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
apb4_apu #(
|
||||
.EnableP7(1'b1)
|
||||
.EnableP7(EnableP7)
|
||||
) u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
@@ -122,7 +124,7 @@ module apu_reg_tb;
|
||||
|
||||
function automatic logic [31:0] legal_write_value(input logic [11:0] offset_i);
|
||||
case (offset_i)
|
||||
`APB4_APU__IRQ_STATE: legal_write_value = 32'h0000_07ff;
|
||||
`APB4_APU__IRQ_STATE: legal_write_value = 32'h0000_0fff;
|
||||
`APB4_APU__IRQ_ENABLE: legal_write_value = 32'h0000_0005;
|
||||
`APB4_APU__ERROR_STATUS: legal_write_value = 32'h0000_0001;
|
||||
`APB4_APU__SEQUENCER_TIMEOUT: legal_write_value = 32'h1234_5678;
|
||||
@@ -155,6 +157,9 @@ module apu_reg_tb;
|
||||
`APB4_APU__KWS_CONTROL: legal_write_value = 32'h0000_0004;
|
||||
`APB4_APU__KWS_CONFIG: legal_write_value = 32'h0000_0590;
|
||||
`APB4_APU__KWS_INPUT_CONFIG: legal_write_value = 32'h0104_bb80;
|
||||
`APB4_APU__KWS_COEFF_ADDRESS: legal_write_value = 32'd0;
|
||||
`APB4_APU__KWS_COEFF_SIZE: legal_write_value = 32'd61504;
|
||||
`APB4_APU__KWS_COEFF_EXPECTED_CRC: legal_write_value = 32'h25e7_c27d;
|
||||
`APB4_APU__PERF_CONTROL: legal_write_value = 32'h0000_0001;
|
||||
default: legal_write_value = 32'd0;
|
||||
endcase
|
||||
@@ -194,8 +199,8 @@ module apu_reg_tb;
|
||||
begin
|
||||
s_reg_count = 0;
|
||||
add_register(`APB4_APU__IP_ID, AccessRo, 32'h4150_5530, 1'b0);
|
||||
add_register(`APB4_APU__IP_VERSION, AccessRo, 32'h0001_0001, 1'b0);
|
||||
add_register(`APB4_APU__CAPABILITY0, AccessRo, 32'h0000_01fd, 1'b0);
|
||||
add_register(`APB4_APU__IP_VERSION, AccessRo, 32'h0001_0002, 1'b0);
|
||||
add_register(`APB4_APU__CAPABILITY0, AccessRo, 32'h0000_03fd, 1'b0);
|
||||
add_register(`APB4_APU__CAPABILITY1, AccessRo, 32'h0182_7020, 1'b0);
|
||||
add_register(`APB4_APU__COMMAND, AccessWo, 32'd0, 1'b1);
|
||||
add_register(`APB4_APU__STATUS, AccessRo, 32'h0000_0100, 1'b0);
|
||||
@@ -214,7 +219,7 @@ module apu_reg_tb;
|
||||
add_register(`APB4_APU__WRITE_BASE, AccessRw, 32'hffff_ffff, 1'b1);
|
||||
add_register(`APB4_APU__WRITE_LIMIT, AccessRw, 32'd0, 1'b1);
|
||||
add_register(`APB4_APU__DMA_TIMEOUT, AccessRw, 32'h0000_ffff, 1'b0);
|
||||
add_register(`APB4_APU__ABI_DIGEST, AccessRo, 32'hf500_5d7c, 1'b0);
|
||||
add_register(`APB4_APU__ABI_DIGEST, AccessRo, 32'h63e9_6066, 1'b0);
|
||||
add_register(`APB4_APU__SEQUENCER_STATUS, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__SEQUENCER_RETIRED, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__STREAM_WATERMARK, AccessRw, 32'd0, 1'b0);
|
||||
@@ -268,6 +273,15 @@ module apu_reg_tb;
|
||||
add_register(`APB4_APU__KWS_MODEL_STATUS, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__KWS_MODEL_ACTUAL_CRC, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__KWS_INPUT_CONFIG, AccessRw, 32'h0104_bb80, 1'b0);
|
||||
add_register(`APB4_APU__KWS_COEFF_ADDRESS, AccessRw, 32'd0, 1'b1);
|
||||
add_register(`APB4_APU__KWS_COEFF_SIZE, AccessRw, 32'd0, 1'b1);
|
||||
add_register(`APB4_APU__KWS_COEFF_EXPECTED_CRC, AccessRw, 32'd0, 1'b1);
|
||||
add_register(`APB4_APU__KWS_COEFF_COMMAND, AccessWo, 32'd0, 1'b1);
|
||||
add_register(`APB4_APU__KWS_COEFF_STATUS, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__KWS_COEFF_ACTUAL_CRC, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__KWS_COEFF_ID_LO, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__KWS_COEFF_ID_HI, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__KWS_COEFF_CAPACITY, AccessRo, 32'h0000_f000, 1'b0);
|
||||
add_register(`APB4_APU__PERF_CONTROL, AccessRw, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__PERF_STATUS, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__PERF_ACTIVE_CYCLES_LO, AccessRo, 32'd0, 1'b0);
|
||||
@@ -448,7 +462,8 @@ module apu_reg_tb;
|
||||
hard_reset();
|
||||
apb_write(`APB4_APU__SEQUENCER_TIMEOUT, 32'h1234_5678, 4'hf, 1'b0);
|
||||
if ((offset_i != `APB4_APU__COMMAND) && (offset_i != `APB4_APU__IRQ_TEST) &&
|
||||
(offset_i != `APB4_APU__RING_DOORBELL)) begin
|
||||
(offset_i != `APB4_APU__RING_DOORBELL) &&
|
||||
(offset_i != `APB4_APU__KWS_COEFF_COMMAND)) begin
|
||||
apb_read(offset_i, s_before, 1'b0);
|
||||
end else begin
|
||||
s_before = 32'd0;
|
||||
@@ -458,7 +473,9 @@ module apu_reg_tb;
|
||||
expect_read(`APB4_APU__ERROR_STATUS, 32'd1 | (32'(error_code_i) << 1));
|
||||
expect_read(`APB4_APU__ERROR_ADDRESS, ApuBase + offset_i);
|
||||
if ((offset_i != `APB4_APU__COMMAND) && (offset_i != `APB4_APU__IRQ_TEST) &&
|
||||
(offset_i != `APB4_APU__RING_DOORBELL) && (offset_i != `APB4_APU__IRQ_STATE) &&
|
||||
(offset_i != `APB4_APU__RING_DOORBELL) &&
|
||||
(offset_i != `APB4_APU__KWS_COEFF_COMMAND) &&
|
||||
(offset_i != `APB4_APU__IRQ_STATE) &&
|
||||
(offset_i != `APB4_APU__ERROR_STATUS) && (offset_i != `APB4_APU__ERROR_ADDRESS) &&
|
||||
(offset_i != `APB4_APU__ERROR_DETAIL)) begin
|
||||
expect_read(offset_i, s_before);
|
||||
@@ -488,6 +505,22 @@ module apu_reg_tb;
|
||||
hard_reset();
|
||||
if (!idle_o || irq_o) $fatal(1, "APU reset lifecycle outputs mismatch");
|
||||
|
||||
if ($test$plusargs("LEGACY_IRQ_ONLY")) begin
|
||||
s_phase = "legacy IRQ mask";
|
||||
expect_read(`APB4_APU__IP_VERSION, 32'h0001_0001);
|
||||
check_rejected_write(`APB4_APU__IRQ_STATE, 32'd1 << `APB4_APU__IRQ_COEFFICIENT_LOAD_DONE,
|
||||
`APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__IRQ_ENABLE, 32'd1 << `APB4_APU__IRQ_COEFFICIENT_LOAD_DONE,
|
||||
`APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__IRQ_TEST, 32'd1 << `APB4_APU__IRQ_COEFFICIENT_LOAD_DONE,
|
||||
`APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
hard_reset();
|
||||
apb_write(`APB4_APU__IRQ_TEST, 32'd1 << `APB4_APU__IRQ_SEQUENCER_TRAP, 4'hf, 1'b0);
|
||||
expect_read(`APB4_APU__IRQ_STATE, 32'd1 << `APB4_APU__IRQ_SEQUENCER_TRAP);
|
||||
$display("APU legacy APB IRQ mask passed");
|
||||
$finish;
|
||||
end
|
||||
|
||||
s_phase = "access matrix";
|
||||
for (int unsigned register_index = 0; register_index < RegisterCount; register_index++) begin
|
||||
hard_reset();
|
||||
@@ -566,10 +599,10 @@ module apu_reg_tb;
|
||||
check_rejected_write(`APB4_APU__COMMAND, 32'd1 << `APB4_APU__COMMAND_MODEL_LOAD,
|
||||
`APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__COMMAND, 32'h0000_0010, `APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__IRQ_STATE, 32'h0000_0800, `APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__IRQ_ENABLE, 32'h0000_0800,
|
||||
check_rejected_write(`APB4_APU__IRQ_STATE, 32'h0000_1000, `APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__IRQ_ENABLE, 32'h0000_1000,
|
||||
`APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__IRQ_TEST, 32'h0000_0800, `APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__IRQ_TEST, 32'h0000_1000, `APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__ERROR_STATUS, 32'h0000_0002,
|
||||
`APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__SEQUENCER_TIMEOUT, 32'd0, `APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
@@ -605,6 +638,10 @@ module apu_reg_tb;
|
||||
check_rejected_write(`APB4_APU__KWS_MODEL_ADDRESS, 32'h3005_0001,
|
||||
`APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__KWS_MODEL_SIZE, 32'd0, `APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__KWS_COEFF_ADDRESS, 32'h3005_0001,
|
||||
`APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__KWS_COEFF_SIZE, 32'd61440,
|
||||
`APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__KWS_CONTROL, 32'h0000_0001,
|
||||
`APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
hard_reset();
|
||||
@@ -620,7 +657,7 @@ module apu_reg_tb;
|
||||
s_phase = "set over clear";
|
||||
hard_reset();
|
||||
apb_write(`APB4_APU__IRQ_TEST, 32'h0000_0001, 4'hf, 1'b0);
|
||||
force u_dut.u_apu_reg.s_irq_set = 11'h001;
|
||||
force u_dut.u_apu_reg.s_irq_set = 12'h001;
|
||||
apb_write(`APB4_APU__IRQ_STATE, 32'h0000_0001, 4'h1, 1'b0);
|
||||
release u_dut.u_apu_reg.s_irq_set;
|
||||
expect_read(`APB4_APU__IRQ_STATE, 32'h0000_0001);
|
||||
@@ -790,7 +827,7 @@ module apu_reg_tb;
|
||||
(32'd1 << `APB4_APU__IRQ_MODEL_LOAD_DONE) | (32'd1 << `APB4_APU__IRQ_FIRST_ERROR));
|
||||
expect_read(`APB4_APU__ERROR_ADDRESS, 32'h3005_1234);
|
||||
expect_read(`APB4_APU__ERROR_DETAIL, 32'd0);
|
||||
apb_write(`APB4_APU__IRQ_STATE, 32'h0000_07ff, 4'hf, 1'b0);
|
||||
apb_write(`APB4_APU__IRQ_STATE, 32'h0000_0fff, 4'hf, 1'b0);
|
||||
release u_dut.s_kws_loader_done;
|
||||
force u_dut.s_kws_loader_abort_done = 1'b1;
|
||||
@(posedge clk_i);
|
||||
@@ -844,6 +881,8 @@ module apu_reg_tb;
|
||||
force u_dut.u_apu_reg.s_kws_control_q = 2'd3;
|
||||
force u_dut.s_kws_model_valid = 1'b1;
|
||||
force u_dut.s_kws_model_lock = 1'b1;
|
||||
force u_dut.s_kws_coeff_valid = 1'b1;
|
||||
force u_dut.s_kws_coeff_lock = 1'b1;
|
||||
force u_dut.s_kws_disable_request = 1'b1;
|
||||
@(negedge clk_i);
|
||||
u_dut.u_codec_controller.s_descriptor_q[(0*32)+:32] = 32'd1;
|
||||
@@ -862,6 +901,8 @@ module apu_reg_tb;
|
||||
release u_dut.s_kws_disable_request;
|
||||
release u_dut.s_kws_model_lock;
|
||||
release u_dut.s_kws_model_valid;
|
||||
release u_dut.s_kws_coeff_lock;
|
||||
release u_dut.s_kws_coeff_valid;
|
||||
release u_dut.u_apu_reg.s_kws_control_q;
|
||||
|
||||
s_phase = "KWS model default publication";
|
||||
|
||||
+83
-4
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import struct
|
||||
import subprocess
|
||||
@@ -90,8 +91,11 @@ def test_apu_p1_apb_register_shell(tmp_path: Path) -> None:
|
||||
str(multimedia / "apu_ring_scheduler.sv"),
|
||||
str(multimedia / "apu_stream_router.sv"),
|
||||
str(multimedia / "apu_control_store.sv"),
|
||||
str(multimedia / "apu_proof_memo.sv"),
|
||||
str(multimedia / "apu_microcode_loader.sv"),
|
||||
str(multimedia / "apu_local_sram.sv"),
|
||||
str(multimedia / "apu_kws_coeff_store.sv"),
|
||||
str(multimedia / "apu_kws_coeff_loader.sv"),
|
||||
str(multimedia / "apu_kws_engine.sv"),
|
||||
str(multimedia / "apu_kws_sram_client.sv"),
|
||||
str(multimedia / "apu_kws_model_loader.sv"),
|
||||
@@ -133,6 +137,28 @@ def test_apu_p1_apb_register_shell(tmp_path: Path) -> None:
|
||||
result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True)
|
||||
assert "APU-P1 complete APB register matrix passed" in result.stdout
|
||||
|
||||
legacy_simulation = tmp_path / "apu_reg_legacy_tb"
|
||||
subprocess.run(
|
||||
[
|
||||
iverilog,
|
||||
"-g2012",
|
||||
"-s",
|
||||
"apu_reg_tb",
|
||||
"-Papu_reg_tb.EnableP7=0",
|
||||
"-o",
|
||||
str(legacy_simulation),
|
||||
str(converted),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
legacy = subprocess.run(
|
||||
[vvp, str(legacy_simulation), "+LEGACY_IRQ_ONLY"],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=True,
|
||||
)
|
||||
assert "APU legacy APB IRQ mask passed" in legacy.stdout
|
||||
|
||||
|
||||
def test_apu_p1_integrated_irq_ownership_topology(tmp_path: Path) -> None:
|
||||
iverilog = shutil.which("iverilog")
|
||||
@@ -180,8 +206,11 @@ def test_apu_p1_integrated_irq_ownership_topology(tmp_path: Path) -> None:
|
||||
str(multimedia / "apu_ring_scheduler.sv"),
|
||||
str(multimedia / "apu_stream_router.sv"),
|
||||
str(multimedia / "apu_control_store.sv"),
|
||||
str(multimedia / "apu_proof_memo.sv"),
|
||||
str(multimedia / "apu_microcode_loader.sv"),
|
||||
str(multimedia / "apu_local_sram.sv"),
|
||||
str(multimedia / "apu_kws_coeff_store.sv"),
|
||||
str(multimedia / "apu_kws_coeff_loader.sv"),
|
||||
str(multimedia / "apu_kws_engine.sv"),
|
||||
str(multimedia / "apu_kws_sram_client.sv"),
|
||||
str(multimedia / "apu_kws_model_loader.sv"),
|
||||
@@ -488,6 +517,7 @@ last:
|
||||
str(ROOT / "rtl/tech/tc_sram.sv"),
|
||||
str(multimedia / "apu_microcode_pkg.sv"),
|
||||
str(multimedia / "apu_control_store.sv"),
|
||||
str(multimedia / "apu_proof_memo.sv"),
|
||||
str(multimedia / "apu_microcode_loader.sv"),
|
||||
str(multimedia / "apu_codec_sequencer.sv"),
|
||||
str(ROOT / "tests/rtl/apu_p3_microcode_tb.sv"),
|
||||
@@ -598,8 +628,11 @@ done:
|
||||
str(multimedia / "apu_ring_scheduler.sv"),
|
||||
str(multimedia / "apu_stream_router.sv"),
|
||||
str(multimedia / "apu_control_store.sv"),
|
||||
str(multimedia / "apu_proof_memo.sv"),
|
||||
str(multimedia / "apu_microcode_loader.sv"),
|
||||
str(multimedia / "apu_local_sram.sv"),
|
||||
str(multimedia / "apu_kws_coeff_store.sv"),
|
||||
str(multimedia / "apu_kws_coeff_loader.sv"),
|
||||
str(multimedia / "apu_kws_engine.sv"),
|
||||
str(multimedia / "apu_kws_sram_client.sv"),
|
||||
str(multimedia / "apu_kws_model_loader.sv"),
|
||||
@@ -973,6 +1006,13 @@ def test_apu_p7_kws_engine_directed(tmp_path: Path) -> None:
|
||||
sv2v = shutil.which("sv2v")
|
||||
if iverilog is None or vvp is None or sv2v is None:
|
||||
return
|
||||
feature = (
|
||||
ROOT
|
||||
/ ".cache/retrosoc/sources/apu-kws-mfcc/datasets/kws01"
|
||||
/ "tst_000000_Stop_7.bin"
|
||||
)
|
||||
if not KWS_MODEL.is_file() or not feature.is_file():
|
||||
return
|
||||
common = ROOT / "rtl/managed/clusterip/common/rtl"
|
||||
multimedia = ROOT / "rtl/ip/multimedia"
|
||||
source_list = tmp_path / "apu_kws_engine.fl"
|
||||
@@ -984,8 +1024,11 @@ def test_apu_p7_kws_engine_directed(tmp_path: Path) -> None:
|
||||
f"+incdir+{common / 'interface'}",
|
||||
f"+incdir+{multimedia}",
|
||||
str(common / "interface/axi4_stream_if.sv"),
|
||||
str(ROOT / "rtl/tech/tc_sram.sv"),
|
||||
str(multimedia / "apu_kws_sram_client.sv"),
|
||||
str(multimedia / "apu_kws_coeff_store.sv"),
|
||||
str(multimedia / "apu_kws_engine.sv"),
|
||||
str(ROOT / "tests/rtl/apu_kws_coeff_fixture.sv"),
|
||||
str(ROOT / "tests/rtl/apu_kws_engine_tb.sv"),
|
||||
"",
|
||||
]
|
||||
@@ -1005,19 +1048,46 @@ def test_apu_p7_kws_engine_directed(tmp_path: Path) -> None:
|
||||
check=True,
|
||||
)
|
||||
simulation = tmp_path / "apu_kws_engine"
|
||||
verilator = shutil.which("verilator")
|
||||
if verilator is None:
|
||||
subprocess.run(
|
||||
[iverilog, "-g2012", "-s", "apu_kws_engine_tb", "-o", str(simulation), str(converted)],
|
||||
check=True,
|
||||
)
|
||||
command = [vvp, str(simulation)]
|
||||
feature = (
|
||||
ROOT
|
||||
/ ".cache/retrosoc/sources/apu-kws-mfcc/datasets/kws01"
|
||||
/ "tst_000000_Stop_7.bin"
|
||||
else:
|
||||
verilator_dir = tmp_path / "verilator"
|
||||
ccache_dir = tmp_path / "ccache"
|
||||
ccache_temp = tmp_path / "ccache-tmp"
|
||||
ccache_dir.mkdir()
|
||||
ccache_temp.mkdir()
|
||||
environment = os.environ.copy()
|
||||
environment["CCACHE_DIR"] = str(ccache_dir)
|
||||
environment["CCACHE_TEMPDIR"] = str(ccache_temp)
|
||||
subprocess.run(
|
||||
[
|
||||
verilator,
|
||||
"--binary",
|
||||
"--timing",
|
||||
"-Wno-fatal",
|
||||
"--top-module",
|
||||
"apu_kws_engine_tb",
|
||||
"--Mdir",
|
||||
str(verilator_dir),
|
||||
str(converted),
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=environment,
|
||||
)
|
||||
command = [str(verilator_dir / "Vapu_kws_engine_tb")]
|
||||
if KWS_MODEL.is_file() and feature.is_file():
|
||||
from generate_apu_kws_rtl_constants import build_apuc
|
||||
|
||||
image = import_tflite(KWS_MODEL)
|
||||
apum_hex = tmp_path / "kws_apum.hex"
|
||||
apuc_hex = tmp_path / "kws_apuc.hex"
|
||||
mfcc_hex = tmp_path / "kws_mfcc.hex"
|
||||
layers_hex = tmp_path / "kws_layers.hex"
|
||||
apum_hex.write_text(
|
||||
@@ -1031,6 +1101,14 @@ def test_apu_p7_kws_engine_directed(tmp_path: Path) -> None:
|
||||
"".join(f"{value:02x}\n" for value in feature.read_bytes()),
|
||||
encoding="ascii",
|
||||
)
|
||||
apuc, _manifest = build_apuc(image)
|
||||
apuc_hex.write_text(
|
||||
"".join(
|
||||
f"{int.from_bytes(apuc[offset : offset + 4], 'little'):08x}\n"
|
||||
for offset in range(0, len(apuc), 4)
|
||||
),
|
||||
encoding="ascii",
|
||||
)
|
||||
layers = infer_apum(image, feature.read_bytes())
|
||||
layers_hex.write_text(
|
||||
"".join(f"{value & 0xff:02x}\n" for layer in layers for value in layer),
|
||||
@@ -1039,6 +1117,7 @@ def test_apu_p7_kws_engine_directed(tmp_path: Path) -> None:
|
||||
command.extend(
|
||||
(
|
||||
f"+APUM_HEX={apum_hex}",
|
||||
f"+APUC_HEX={apuc_hex}",
|
||||
f"+MFCC_HEX={mfcc_hex}",
|
||||
f"+LAYERS_HEX={layers_hex}",
|
||||
)
|
||||
|
||||
@@ -486,6 +486,7 @@ def test_p5_v2_4096_word_loader_store_and_sequencer(tmp_path: Path) -> None:
|
||||
multimedia = ROOT / "rtl/ip/multimedia"
|
||||
sources = [
|
||||
multimedia / "apu_microcode_pkg.sv",
|
||||
multimedia / "apu_proof_memo.sv",
|
||||
multimedia / "apu_microcode_loader.sv",
|
||||
multimedia / "apu_control_store.sv",
|
||||
multimedia / "apu_codec_sequencer.sv",
|
||||
@@ -662,8 +663,11 @@ def test_p5_direct_wav_uses_product_loader_dma_sequencer_and_tx(tmp_path: Path)
|
||||
"apu_ring_scheduler.sv",
|
||||
"apu_stream_router.sv",
|
||||
"apu_control_store.sv",
|
||||
"apu_proof_memo.sv",
|
||||
"apu_microcode_loader.sv",
|
||||
"apu_local_sram.sv",
|
||||
"apu_kws_coeff_store.sv",
|
||||
"apu_kws_coeff_loader.sv",
|
||||
"apu_kws_engine.sv",
|
||||
"apu_kws_sram_client.sv",
|
||||
"apu_kws_model_loader.sv",
|
||||
|
||||
@@ -135,7 +135,9 @@ def _compile_dump_tb(build_dir: Path) -> dict[str, Path | None]:
|
||||
f"+incdir+{multimedia}",
|
||||
str(common / "interface/axi4_stream_if.sv"),
|
||||
str(multimedia / "apu_kws_sram_client.sv"),
|
||||
str(multimedia / "apu_kws_coeff_store.sv"),
|
||||
str(multimedia / "apu_kws_engine.sv"),
|
||||
str(ROOT / "tests/rtl/apu_kws_coeff_fixture.sv"),
|
||||
str(DUMP_TB),
|
||||
"",
|
||||
]
|
||||
@@ -209,12 +211,17 @@ def test_apu_p7_frontend_rtl_differential(tmp_path: Path) -> None:
|
||||
windows[index] = padded
|
||||
_write_hex(pcm_dir / f"pcm_{index:06d}.hex", padded)
|
||||
apum_hex = tmp_path / "kws_apum.hex"
|
||||
_write_hex(apum_hex, import_tflite(TFLITE))
|
||||
image = import_tflite(TFLITE)
|
||||
_write_hex(apum_hex, image)
|
||||
apuc_hex = tmp_path / "kws_apuc.hex"
|
||||
apuc, _manifest = gen.build_apuc(image)
|
||||
_write_hex(apuc_hex, apuc)
|
||||
binaries = _compile_dump_tb(tmp_path)
|
||||
dumps: dict[int, tuple[bytes, int]] = {}
|
||||
for first, count in spans:
|
||||
plusargs = [
|
||||
f"+APUM_HEX={apum_hex}",
|
||||
f"+APUC_HEX={apuc_hex}",
|
||||
f"+PCM_DIR={pcm_dir}",
|
||||
f"+WINDOW_COUNT={count}",
|
||||
f"+FIRST_INDEX={first}",
|
||||
|
||||
@@ -24,8 +24,11 @@ PRODUCTION_SOURCES = (
|
||||
"apu_ring_scheduler.sv",
|
||||
"apu_stream_router.sv",
|
||||
"apu_control_store.sv",
|
||||
"apu_proof_memo.sv",
|
||||
"apu_microcode_loader.sv",
|
||||
"apu_local_sram.sv",
|
||||
"apu_kws_coeff_store.sv",
|
||||
"apu_kws_coeff_loader.sv",
|
||||
"apu_kws_engine.sv",
|
||||
"apu_kws_sram_client.sv",
|
||||
"apu_kws_model_loader.sv",
|
||||
@@ -72,11 +75,16 @@ def test_apu_p8_quiesced_loader_admission(tmp_path: Path) -> None:
|
||||
pytest.skip("locked P7 model was not installed")
|
||||
|
||||
from apu_kws_convert import import_tflite
|
||||
from generate_apu_kws_rtl_constants import build_apuc
|
||||
|
||||
apumc_hex = tmp_path / "apu-p5.hex"
|
||||
_hex_words(apumc_hex, bundle.read_bytes(), 16384)
|
||||
apum_hex = tmp_path / "kws-apum.hex"
|
||||
_hex_words(apum_hex, import_tflite(KWS_MODEL), 8192)
|
||||
apum = import_tflite(KWS_MODEL)
|
||||
_hex_words(apum_hex, apum, 8192)
|
||||
apuc_hex = tmp_path / "kws-apuc.hex"
|
||||
apuc, _layout = build_apuc(apum)
|
||||
_hex_words(apuc_hex, apuc, 15376)
|
||||
|
||||
common = ROOT / "rtl/managed/clusterip/common/rtl"
|
||||
multimedia = ROOT / "rtl/ip/multimedia"
|
||||
@@ -125,11 +133,13 @@ def test_apu_p8_quiesced_loader_admission(tmp_path: Path) -> None:
|
||||
str(simulation),
|
||||
f"+APUMC_HEX={apumc_hex}",
|
||||
f"+APUM_HEX={apum_hex}",
|
||||
f"+APUC_HEX={apuc_hex}",
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
assert "P8_QUIESCE_MC" in result.stdout
|
||||
assert "P8_QUIESCE_COEFF" in result.stdout
|
||||
assert "P8_QUIESCE_MODEL" in result.stdout
|
||||
assert "APU-P8 quiesced loader admission test passed" in result.stdout
|
||||
@@ -0,0 +1,100 @@
|
||||
"""Frozen APU-P9 APUC image and physical-bank layout coverage."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import copy
|
||||
import struct
|
||||
import sys
|
||||
import zlib
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "scripts"))
|
||||
|
||||
import generate_apu_kws_rtl_constants as gen # noqa: E402
|
||||
from apu_kws_coeff import validate_layout_manifest # noqa: E402
|
||||
from apu_kws_convert import import_tflite # noqa: E402
|
||||
|
||||
|
||||
TFLITE = (
|
||||
ROOT
|
||||
/ ".cache/retrosoc/sources/apu-mlperf-tiny/benchmark/training/keyword_spotting"
|
||||
/ "trained_models/kws_ref_model.tflite"
|
||||
)
|
||||
|
||||
|
||||
def _require_tflite() -> Path:
|
||||
if not TFLITE.is_file():
|
||||
pytest.fail(
|
||||
"locked P7 model is required for APU-P9 coefficient validation; "
|
||||
"run `make setup-apu-kws-reference`"
|
||||
)
|
||||
return TFLITE
|
||||
|
||||
|
||||
def test_apu_p9_frozen_apuc_and_layout() -> None:
|
||||
image = import_tflite(_require_tflite())
|
||||
apuc, manifest = gen.build_apuc(image)
|
||||
validate_layout_manifest(apuc, manifest)
|
||||
header = struct.unpack_from("<16I", apuc)
|
||||
|
||||
assert len(apuc) == gen.APUC_BYTES == 61504
|
||||
assert header[:10] == (
|
||||
gen.APUC_MAGIC,
|
||||
gen.APUC_ABI,
|
||||
gen.APUC_BYTES,
|
||||
gen.APUC_HEADER_BYTES,
|
||||
gen.APUC_PAYLOAD_BYTES,
|
||||
gen.APUC_PROFILE,
|
||||
gen.APUC_LAYOUT_ID,
|
||||
gen.APUC_BANK_COUNT,
|
||||
gen.APUC_TABLE_COUNT,
|
||||
gen.APUC_PAYLOAD_CRC,
|
||||
)
|
||||
assert header[10:13] == (*gen.APUC_COEFFICIENT_ID, gen.APUC_APUM_PAYLOAD_CRC)
|
||||
assert header[14:] == (0, 0)
|
||||
assert zlib.crc32(apuc[64:]) & 0xFFFFFFFF == gen.APUC_PAYLOAD_CRC
|
||||
assert hashlib.sha256(apuc[64:]).hexdigest() == gen.APUC_PAYLOAD_SHA256
|
||||
assert hashlib.sha256(apuc).hexdigest() == gen.APUC_SHA256
|
||||
assert manifest["layout"] == {
|
||||
"geometry": "tc_sram_1024x32",
|
||||
"banks": 15,
|
||||
"words_per_bank": 1024,
|
||||
"populated_words": 14444,
|
||||
"zero_padding_words": 916,
|
||||
}
|
||||
|
||||
|
||||
def test_apu_p9_layout_locations_are_unique_and_complete() -> None:
|
||||
apuc, manifest = gen.build_apuc(import_tflite(_require_tflite()))
|
||||
validate_layout_manifest(apuc, manifest)
|
||||
locations = [
|
||||
tuple(location)
|
||||
for table in manifest["tables"].values()
|
||||
for location in table["locations"]
|
||||
]
|
||||
assert len(locations) == 14444
|
||||
assert len(set(locations)) == len(locations)
|
||||
assert all(0 <= bank < 15 and 0 <= row < 1024 for bank, row in locations)
|
||||
|
||||
|
||||
def test_apu_p9_layout_oracle_rejects_generator_self_consistency() -> None:
|
||||
apuc, manifest = gen.build_apuc(import_tflite(_require_tflite()))
|
||||
stale = copy.deepcopy(manifest)
|
||||
stale["tables"]["hann"]["locations"][0] = [14, 1023]
|
||||
with pytest.raises(ValueError, match="independent map/hash oracle"):
|
||||
validate_layout_manifest(apuc, stale)
|
||||
|
||||
missing_revision = copy.deepcopy(manifest)
|
||||
del missing_revision["generator"]["revision"]
|
||||
with pytest.raises(ValueError, match="generator revision"):
|
||||
validate_layout_manifest(apuc, missing_revision)
|
||||
|
||||
|
||||
def test_apu_p9_irq_mask_preserves_legacy_rejection() -> None:
|
||||
register_source = (ROOT / "rtl/ip/multimedia/apu_reg.sv").read_text(encoding="utf-8")
|
||||
assert "IrqMask = EnableP7 ? 12'hfff : 12'h7ff" in register_source
|
||||
@@ -0,0 +1,315 @@
|
||||
"""Fail-closed APU-P9 evidence assembly and synthesis A/B coverage."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from copy import deepcopy
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
|
||||
|
||||
import generate_apu_kws_rtl_constants as coefficient_generator # noqa: E402
|
||||
from apu_kws_convert import import_tflite # noqa: E402
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
TFLITE = (
|
||||
ROOT
|
||||
/ ".cache/retrosoc/sources/apu-mlperf-tiny/benchmark/training/keyword_spotting"
|
||||
/ "trained_models/kws_ref_model.tflite"
|
||||
)
|
||||
|
||||
|
||||
def _write_json(path: Path, value: object) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(value) + "\n", encoding="ascii")
|
||||
|
||||
|
||||
def _variant(root: Path, *, baseline: bool) -> None:
|
||||
apu_path = "retrosoc_asic.u_retrosoc.u_apb4_periph.u_apb4_apu."
|
||||
modules = {
|
||||
f"\\fifo${apu_path}u_stream_fifo": {"num_memories": 1, "num_memory_bits": 60000},
|
||||
}
|
||||
if baseline:
|
||||
modules.update(
|
||||
{
|
||||
f"\\apu_kws_engine${apu_path}u_kws_engine": {
|
||||
"num_memories": 18,
|
||||
"num_memory_bits": 799680,
|
||||
},
|
||||
f"\\apu_microcode_loader${apu_path}u_microcode_loader": {
|
||||
"num_memories": 2,
|
||||
"num_memory_bits": 532480,
|
||||
},
|
||||
}
|
||||
)
|
||||
_write_json(root / "syn/yosys/rpt/retrosoc_asic_pre_memory.json", {"modules": modules})
|
||||
_write_json(
|
||||
root / "syn/yosys/rpt/retrosoc_asic_pre_tech.json",
|
||||
{
|
||||
"design": {
|
||||
"num_cells": 1200 if baseline else 600,
|
||||
"num_cells_by_type": {"$_DFF_P_": 400 if baseline else 180, "$_MUX_": 300 if baseline else 90},
|
||||
}
|
||||
},
|
||||
)
|
||||
_write_json(
|
||||
root / "syn/yosys/yosys-perf.json",
|
||||
{
|
||||
"generator": "Yosys 0.67",
|
||||
"total_ns": 100_000_000_000 if baseline else 60_000_000_000,
|
||||
"passes": {
|
||||
"memory": {"runtime_ns": 10_000_000_000 if baseline else 5_000_000_000},
|
||||
"opt_dff": {"runtime_ns": 70_000_000_000 if baseline else 10_000_000_000},
|
||||
**({} if baseline else {"abc": {"runtime_ns": 20_000_000_000}}),
|
||||
},
|
||||
},
|
||||
)
|
||||
_write_json(
|
||||
root / "syn/yosys/result-synth.json",
|
||||
{
|
||||
"status": "timed_out" if baseline else "passed",
|
||||
"exit_code": 124 if baseline else 0,
|
||||
"duration_seconds": 100 if baseline else 60,
|
||||
"peak_rss_kib": 1000 if baseline else 500,
|
||||
},
|
||||
)
|
||||
configuration = {"PDK": "IHP130", "APU_ENABLE_P7": "YES", "SYNTH": "YOSYS"}
|
||||
configuration["HAVE_SRAM_MACRO"] = "YES"
|
||||
revision = (
|
||||
"0b73994772068e5fac00fb1a98f87f88ed121cdf"
|
||||
if baseline
|
||||
else "1111111111111111111111111111111111111111"
|
||||
)
|
||||
_write_json(
|
||||
root / "meta/manifest.json",
|
||||
{
|
||||
"configuration": configuration,
|
||||
"dependency_lock": {"sha256": "abc"},
|
||||
"tools": {"yosys": "0.67"},
|
||||
"profile": "ihp130",
|
||||
"repository": {"commit": revision, "dirty": False},
|
||||
},
|
||||
)
|
||||
groups = {
|
||||
"u_control_store": 8,
|
||||
"u_local_sram": 12,
|
||||
"u_kws_sram_client": 16,
|
||||
"u_microcode_loader": 8,
|
||||
**({} if baseline else {"u_proof_memo": 17, "u_kws_coeff_store": 15}),
|
||||
}
|
||||
lines: list[str] = []
|
||||
for group, count in groups.items():
|
||||
for index in range(count):
|
||||
if group == "u_proof_memo":
|
||||
path = f"{apu_path}u_microcode_loader.{group}.u_mem_{index}"
|
||||
else:
|
||||
path = f"{apu_path}{group}.u_mem_{index}"
|
||||
lines.append(path)
|
||||
macro_path = root / "syn/yosys/rpt/retrosoc_asic_macros.rpt"
|
||||
macro_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
macro_path.write_text("\n".join(lines) + "\n", encoding="ascii")
|
||||
|
||||
|
||||
def test_apu_p9_memory_ab_requires_all_frozen_checks(tmp_path: Path) -> None:
|
||||
baseline = tmp_path / "baseline"
|
||||
candidate = tmp_path / "candidate"
|
||||
output = tmp_path / "memory-synthesis-ab.json"
|
||||
_variant(baseline, baseline=True)
|
||||
_variant(candidate, baseline=False)
|
||||
subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(ROOT / "scripts/apu_p9_memory_ab.py"),
|
||||
"--baseline-root",
|
||||
str(baseline),
|
||||
"--candidate-root",
|
||||
str(candidate),
|
||||
"--output",
|
||||
str(output),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
report = json.loads(output.read_text(encoding="ascii"))
|
||||
assert report["status"] == "passed"
|
||||
assert report["candidate"]["memory"]["target_bits"] == 0
|
||||
assert report["candidate"]["macros"]["apu_wrappers"] == 76
|
||||
assert all(report["checks"].values())
|
||||
|
||||
assembled = tmp_path / "assembled"
|
||||
report_arguments: list[str] = []
|
||||
for name in (
|
||||
"coefficient-layout.json",
|
||||
"coefficient-lifecycle.json",
|
||||
"memo-equivalence.json",
|
||||
"numerical-latency.json",
|
||||
"memory-synthesis-ab.json",
|
||||
"physical.json",
|
||||
):
|
||||
source = output if name == "memory-synthesis-ab.json" else tmp_path / name
|
||||
if source != output:
|
||||
_write_json(source, {"status": "unrun", "reason": "fixture intentionally unrun"})
|
||||
report_arguments.extend(("--report", f"{name}={source}"))
|
||||
subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(ROOT / "scripts/apu_p9_evidence.py"),
|
||||
"assemble",
|
||||
*report_arguments,
|
||||
"--output-dir",
|
||||
str(assembled),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
assert json.loads((assembled / "memory-synthesis-ab.json").read_text())["status"] == "passed"
|
||||
|
||||
|
||||
def test_apu_p9_memory_ab_rejects_unqualified_revisions(tmp_path: Path) -> None:
|
||||
baseline = tmp_path / "baseline"
|
||||
candidate = tmp_path / "candidate"
|
||||
output = tmp_path / "memory-synthesis-ab.json"
|
||||
_variant(baseline, baseline=True)
|
||||
_variant(candidate, baseline=False)
|
||||
manifest = json.loads((candidate / "meta/manifest.json").read_text())
|
||||
manifest["repository"] = {"commit": "0b73994772068e5fac00fb1a98f87f88ed121cdf", "dirty": False}
|
||||
_write_json(candidate / "meta/manifest.json", manifest)
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(ROOT / "scripts/apu_p9_memory_ab.py"),
|
||||
"--baseline-root",
|
||||
str(baseline),
|
||||
"--candidate-root",
|
||||
str(candidate),
|
||||
"--output",
|
||||
str(output),
|
||||
],
|
||||
check=False,
|
||||
)
|
||||
assert result.returncode != 0
|
||||
assert json.loads(output.read_text())["checks"]["revision_qualified"] is False
|
||||
|
||||
|
||||
def test_apu_p9_memory_ab_counts_new_storage_modules_as_targets(tmp_path: Path) -> None:
|
||||
baseline = tmp_path / "baseline"
|
||||
candidate = tmp_path / "candidate"
|
||||
output = tmp_path / "memory-synthesis-ab.json"
|
||||
_variant(baseline, baseline=True)
|
||||
_variant(candidate, baseline=False)
|
||||
inventory_path = candidate / "syn/yosys/rpt/retrosoc_asic_pre_memory.json"
|
||||
inventory = json.loads(inventory_path.read_text())
|
||||
apu_path = "retrosoc_asic.u_retrosoc.u_apb4_periph.u_apb4_apu."
|
||||
inventory["modules"][f"\\apu_proof_memo${apu_path}u_microcode_loader.u_proof_memo"] = {
|
||||
"num_memories": 1,
|
||||
"num_memory_bits": 32,
|
||||
}
|
||||
_write_json(inventory_path, inventory)
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(ROOT / "scripts/apu_p9_memory_ab.py"),
|
||||
"--baseline-root",
|
||||
str(baseline),
|
||||
"--candidate-root",
|
||||
str(candidate),
|
||||
"--output",
|
||||
str(output),
|
||||
],
|
||||
check=False,
|
||||
)
|
||||
assert result.returncode != 0
|
||||
report = json.loads(output.read_text())
|
||||
assert report["candidate"]["memory"]["target_bits"] == 32
|
||||
assert report["checks"]["target_inferred_bits_zero"] is False
|
||||
|
||||
|
||||
def test_apu_p9_evidence_initialization_keeps_missing_runs_unrun(tmp_path: Path) -> None:
|
||||
layout = tmp_path / "layout.json"
|
||||
apuc_path = tmp_path / "apu-p9.apuc"
|
||||
output = tmp_path / "evidence"
|
||||
apuc, manifest = coefficient_generator.build_apuc(import_tflite(TFLITE))
|
||||
apuc_path.write_bytes(apuc)
|
||||
_write_json(layout, manifest)
|
||||
subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(ROOT / "scripts/apu_p9_evidence.py"),
|
||||
"initialize",
|
||||
"--layout",
|
||||
str(layout),
|
||||
"--apuc",
|
||||
str(apuc_path),
|
||||
"--profile",
|
||||
str(ROOT / "configs/ci/ihp130.mk"),
|
||||
"--output-dir",
|
||||
str(output),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
assert json.loads((output / "coefficient-layout.json").read_text())["status"] == "passed"
|
||||
assert json.loads((output / "memory-synthesis-ab.json").read_text())["status"] == "unrun"
|
||||
|
||||
|
||||
def test_apu_p9_evidence_rejects_incomplete_passed_reports(tmp_path: Path) -> None:
|
||||
reports = tmp_path / "reports"
|
||||
output = tmp_path / "evidence"
|
||||
sources: list[str] = []
|
||||
for name in (
|
||||
"coefficient-layout.json",
|
||||
"coefficient-lifecycle.json",
|
||||
"memo-equivalence.json",
|
||||
"numerical-latency.json",
|
||||
"memory-synthesis-ab.json",
|
||||
"physical.json",
|
||||
):
|
||||
source = reports / name
|
||||
_write_json(source, {"status": "passed"})
|
||||
sources.extend(("--report", f"{name}={source}"))
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(ROOT / "scripts/apu_p9_evidence.py"),
|
||||
"assemble",
|
||||
*sources,
|
||||
"--output-dir",
|
||||
str(output),
|
||||
],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
assert result.returncode != 0
|
||||
assert "coefficient-layout.json" in result.stderr
|
||||
|
||||
|
||||
def test_apu_p9_evidence_rejects_stale_layout_mapping(tmp_path: Path) -> None:
|
||||
layout = tmp_path / "layout.json"
|
||||
apuc_path = tmp_path / "apu-p9.apuc"
|
||||
output = tmp_path / "evidence"
|
||||
apuc, manifest = coefficient_generator.build_apuc(import_tflite(TFLITE))
|
||||
stale = deepcopy(manifest)
|
||||
stale["tables"]["log"]["locations"][1024] = [0, 0]
|
||||
apuc_path.write_bytes(apuc)
|
||||
_write_json(layout, stale)
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(ROOT / "scripts/apu_p9_evidence.py"),
|
||||
"initialize",
|
||||
"--layout",
|
||||
str(layout),
|
||||
"--apuc",
|
||||
str(apuc_path),
|
||||
"--profile",
|
||||
str(ROOT / "configs/ci/ihp130.mk"),
|
||||
"--output-dir",
|
||||
str(output),
|
||||
],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
assert result.returncode != 0
|
||||
assert "independent map/hash oracle" in result.stderr
|
||||
@@ -0,0 +1,84 @@
|
||||
"""Focused RTL coverage for the APU-P9 physical storage transactions."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def test_apu_p9_production_storage_inventory() -> None:
|
||||
multimedia = ROOT / "rtl/ip/multimedia"
|
||||
engine = (multimedia / "apu_kws_engine.sv").read_text(encoding="utf-8")
|
||||
model_loader = (multimedia / "apu_kws_model_loader.sv").read_text(encoding="utf-8")
|
||||
microcode_loader = (multimedia / "apu_microcode_loader.sv").read_text(encoding="utf-8")
|
||||
coefficient_store = (multimedia / "apu_kws_coeff_store.sv").read_text(encoding="utf-8")
|
||||
proof_memo = (multimedia / "apu_proof_memo.sv").read_text(encoding="utf-8")
|
||||
|
||||
assert 'include "apu_kws_rom.svh"' not in engine
|
||||
assert 'include "apu_kws_apum_profile.svh"' not in model_loader
|
||||
assert "s_key_mem" not in microcode_loader
|
||||
assert "localparam int unsigned BankCount = 15" in coefficient_store
|
||||
assert "for (genvar bank = 0; bank < BankCount; bank++)" in coefficient_store
|
||||
assert proof_memo.count("tc_sram_1024x32") == 3
|
||||
assert "for (genvar bank = 0; bank < 8; bank++)" in proof_memo
|
||||
assert "u_valid_bitmap_sram" in proof_memo
|
||||
|
||||
inventory = {
|
||||
"control_store": 8,
|
||||
"codec_common_data": 12,
|
||||
"kws_model_scratch": 16,
|
||||
"loader_path_stack": 8,
|
||||
"verifier_memo": 17,
|
||||
"coefficient_store": 15,
|
||||
}
|
||||
assert sum(inventory.values()) == 76
|
||||
assert sum(inventory.values()) * 4 == 304
|
||||
|
||||
|
||||
def test_apu_p9_coefficient_store_and_proof_memo(tmp_path: Path) -> None:
|
||||
iverilog = shutil.which("iverilog")
|
||||
vvp = shutil.which("vvp")
|
||||
sv2v = shutil.which("sv2v")
|
||||
if iverilog is None or vvp is None or sv2v is None:
|
||||
pytest.fail("APU-P9 storage validation requires locked iverilog, vvp, and sv2v tools")
|
||||
multimedia = ROOT / "rtl/ip/multimedia"
|
||||
source_list = tmp_path / "apu_p9_storage.fl"
|
||||
source_list.write_text(
|
||||
"\n".join(
|
||||
(
|
||||
f"+incdir+{multimedia}",
|
||||
str(ROOT / "rtl/tech/tc_sram.sv"),
|
||||
str(multimedia / "apu_kws_coeff_store.sv"),
|
||||
str(multimedia / "apu_proof_memo.sv"),
|
||||
str(ROOT / "tests/rtl/apu_p9_storage_tb.sv"),
|
||||
"",
|
||||
)
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
converted = tmp_path / "apu_p9_storage.v"
|
||||
subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(ROOT / "rtl/mini/script/convt_sv2v.py"),
|
||||
"-f",
|
||||
str(source_list),
|
||||
"--output",
|
||||
str(converted),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
simulation = tmp_path / "apu_p9_storage"
|
||||
subprocess.run(
|
||||
[iverilog, "-g2012", "-s", "apu_p9_storage_tb", "-o", str(simulation), str(converted)],
|
||||
check=True,
|
||||
)
|
||||
result = subprocess.run([vvp, str(simulation)], check=True, capture_output=True, text=True)
|
||||
assert "APU-P9 coefficient store and proof memo test passed" in result.stdout
|
||||
@@ -167,8 +167,28 @@ def test_p4_local_sram_has_macro_and_inferred_profiles() -> None:
|
||||
encoding="utf-8"
|
||||
)
|
||||
compact_engine = "".join(engine_source.split())
|
||||
assert "KwsWordCount=ModelWordCount+ScratchWordCount" in "".join(kws_source.split())
|
||||
assert "`RETROSOC_APU_KWS__SCRATCH_BYTES/4" in "".join(kws_source.split())
|
||||
compact_kws = "".join(kws_source.split())
|
||||
assert "localparamintunsignedBankCount=16" in compact_kws
|
||||
assert "tc_sram_1024x32u_kws_sram" in compact_kws
|
||||
assert "logic[31:0]mem[0:16383]" in compact_kws
|
||||
assert "`ifdefHAVE_SRAM_MACRO" in compact_kws
|
||||
|
||||
coefficient_source = (
|
||||
ROOT / "rtl/ip/multimedia/apu_kws_coeff_store.sv"
|
||||
).read_text(encoding="utf-8")
|
||||
compact_coefficient = "".join(coefficient_source.split())
|
||||
assert "localparamintunsignedBankCount=15" in compact_coefficient
|
||||
assert "tc_sram_1024x32u_coefficient_sram" in compact_coefficient
|
||||
assert "logics_storage_q[0:1023]" not in compact_coefficient
|
||||
assert "logic[31:0]s_storage_q[0:1023]" in compact_coefficient
|
||||
|
||||
memo_source = (ROOT / "rtl/ip/multimedia/apu_proof_memo.sv").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
compact_memo = "".join(memo_source.split())
|
||||
assert "tc_sram_1024x32u_key_low_sram" in compact_memo
|
||||
assert "tc_sram_1024x32u_key_high_sram" in compact_memo
|
||||
assert "tc_sram_1024x32u_valid_bitmap_sram" in compact_memo
|
||||
assert "logicsigned[7:0]s_tensor_a_q[" not in compact_engine
|
||||
assert "logicsigned[7:0]s_tensor_b_q[" not in compact_engine
|
||||
assert "logic[31:0]s_mel_q[" not in compact_engine
|
||||
@@ -1372,6 +1392,7 @@ def test_p4_loader_publishes_control_and_table_atomically(tmp_path: Path) -> Non
|
||||
str(multimedia / "apu_microcode_pkg.sv"),
|
||||
str(multimedia / "apu_control_store.sv"),
|
||||
str(multimedia / "apu_local_sram.sv"),
|
||||
str(multimedia / "apu_proof_memo.sv"),
|
||||
str(multimedia / "apu_microcode_loader.sv"),
|
||||
str(ROOT / "tests/rtl/apu_p4_loader_tb.sv"),
|
||||
"",
|
||||
|
||||
@@ -38,7 +38,7 @@ def test_apu_profiles_follow_deployed_p5_and_committed_p7_selection():
|
||||
assert ordinary["formats"] == {"WAV": True, "FLAC": True, "MP3": False, "KWS": False}
|
||||
assert acceptance["formats"] == {"WAV": True, "FLAC": True, "MP3": False, "KWS": True}
|
||||
assert (ordinary["capability"], ordinary["digest"]) == (0x1BD, 0)
|
||||
assert (acceptance["capability"], acceptance["digest"]) == (0x1FD, 0xF5005D7C)
|
||||
assert (acceptance["capability"], acceptance["digest"]) == (0x3FD, 0x63E96066)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("relative,before,after,message", [
|
||||
@@ -69,6 +69,7 @@ def test_apu_rendered_acceptance_is_source_ordered_and_failure_checked():
|
||||
("if (!rs_apu_release_quiesce())", "if (!rs_apu_release_stage_assets())", "call order"),
|
||||
("rs_apu_release_fail(UINT8_C(6));", "rs_apu_release_fail(UINT8_C(7));", "failure checkpoint"),
|
||||
("load_status = rs_apu_microcode_load(&mc_image", "load_status = rs_apu_kws_model_load(&mc_image", "image-load order"),
|
||||
("load_status = rs_apu_kws_coeff_load(&coefficient_image", "load_status = rs_apu_kws_model_load(&coefficient_image", "image-load order"),
|
||||
("rs_sysctrl_set_hp_release(true)", "rs_sysctrl_set_hp_release(false)", "publication/release order"),
|
||||
])
|
||||
def test_changed_acceptance_cannot_retain_published_steps(tmp_path, before, after, message):
|
||||
|
||||
@@ -27,16 +27,16 @@ def reference():
|
||||
return collect_registers()
|
||||
|
||||
|
||||
def test_current_apu_interface_declares_p5_default_and_p7_release(reference):
|
||||
def test_current_apu_interface_declares_p5_default_and_p9_release(reference):
|
||||
registers = {r["name"]: r for r in reference["apu"]["registers"]}
|
||||
constants = (ROOT / "crt/include/retrosoc/hal/apu_regs.h").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
assert re.search(r"RS_APU_IP_VERSION_VALUE\s+UINT32_C\(0x00010001\)", constants)
|
||||
assert re.search(r"RS_APU_IP_VERSION_VALUE\s+UINT32_C\(0x00010002\)", constants)
|
||||
assert re.search(r"RS_APU_CAPABILITY0_P5_IMPLEMENTED\s+UINT32_C\(0x000001BD\)", constants)
|
||||
assert re.search(r"RS_APU_CAPABILITY0_P7_IMPLEMENTED\s+UINT32_C\(0x000001FD\)", constants)
|
||||
assert re.search(r"RS_APU_CAPABILITY0_P7_IMPLEMENTED\s+UINT32_C\(0x000003FD\)", constants)
|
||||
assert re.search(r"RS_APU_CAPABILITY1_IMPLEMENTED\s+UINT32_C\(0x01827020\)", constants)
|
||||
assert "V1.1" in registers["IP_VERSION"]["description"]
|
||||
assert "V1.2" in registers["IP_VERSION"]["description"]
|
||||
assert "32 KiB" in registers["CAPABILITY1"]["description"]
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ def test_npu_deployed_geometry_dma_and_wire_layout(data):
|
||||
|
||||
def test_apu_capability_and_digest_remain_profile_specific(data):
|
||||
rows = data["system_reference"]["apu_implementation"]["profiles"]
|
||||
assert [(r["enabled"], r["capability"], r["digest"]) for r in rows] == [(False, 0x1BD, 0), (True, 0x1FD, 0xF5005D7C)]
|
||||
assert [(r["enabled"], r["capability"], r["digest"]) for r in rows] == [(False, 0x1BD, 0), (True, 0x3FD, 0x63E96066)]
|
||||
assert all(r["capability"] & 2 == 0 for r in rows) # MP3 remains clear.
|
||||
|
||||
|
||||
|
||||
@@ -1726,7 +1726,7 @@ def test_quality_runs_accelerator_tests_with_locked_references_and_simulators()
|
||||
assert "python3 rtl/ip/setup.py" in quality
|
||||
assert "make CONFIG=configs/ci/ihp130.mk setup-pdk" in quality
|
||||
assert "--requirement requirements/build.txt" in quality
|
||||
assert "make setup-apu-reference setup-npu-reference" in quality
|
||||
assert "make setup-apu-reference setup-apu-kws-reference setup-npu-reference" in quality
|
||||
for path in (
|
||||
".cache/retrosoc/sources/apu-mlperf-tiny",
|
||||
".cache/retrosoc/sources/apu-kws-mfcc",
|
||||
|
||||
Reference in new issue
Block a user