From 75928084646d09582ac84b5d27ebf1a1e7811843 Mon Sep 17 00:00:00 2001 From: maksyuki Date: Thu, 24 Sep 2026 07:41:43 +0800 Subject: [PATCH] feat: refactor JPEG table register bank and enhance flow script - Updated the JPEG table register bank module to use combinational logic for data handling and simplified the write logic for quantization, DC, and AC entries. - Introduced a new function in the run_flow script to handle process termination more gracefully, including signal handling for interrupts. - Enhanced the flow script to log peak RSS memory usage during execution. - Modified multiple testbench files to include new storage access signals and improved the test logic for better validation of KWS and SRAM interactions. - Updated tests to validate the new peak RSS memory logging in the flow script. - Adjusted Yosys synthesis recipes to include timeout settings and ensure proper reporting of pre-memory synthesis outputs. --- docs/engineering.md | 12 + physical/smoke/syn/yosys/apu_block.mk | 5 +- physical/smoke/syn/yosys/ga2d_block.mk | 5 +- .../syn/yosys/script/apu_block_synth.tcl | 4 + physical/smoke/syn/yosys/script/synth.tcl | 4 + physical/smoke/syn/yosys/yosys.mk | 6 +- rtl/ip/multimedia/apb4_apu.sv | 18 + rtl/ip/multimedia/apu_kws_engine.sv | 617 ++++++++++-------- rtl/ip/multimedia/apu_kws_sram_client.sv | 391 ++++++++--- rtl/ip/multimedia/jpeg_table_register_bank.sv | 136 ++-- scripts/run_flow.py | 53 +- tests/rtl/apu_kws_engine_tb.sv | 32 +- tests/rtl/apu_kws_sram_client_tb.sv | 88 ++- tests/rtl/apu_p7_accuracy_tb.sv | 17 + tests/rtl/apu_p7_frontend_dump_tb.sv | 17 + tests/test_script_tools.py | 2 + tests/test_yosys_synth_recipes.py | 6 + 17 files changed, 954 insertions(+), 459 deletions(-) diff --git a/docs/engineering.md b/docs/engineering.md index a382c90a..91d21250 100644 --- a/docs/engineering.md +++ b/docs/engineering.md @@ -172,6 +172,18 @@ duration, warning signatures, and netlist simulation verdict together. The improved metric is not sufficient to replace `balanced`. Metrics policy remains in `observe` mode while recipe baselines are collected and reviewed. +Every Yosys run writes `yosys-perf.json`, `_pre_memory.rpt`, and +`_pre_memory.json` immediately before memory lowering. These reports are +the authoritative pass-timing and inferred-memory inventory for memories that +would otherwise be expanded into registers and muxes. Structured flow results +also record `peak_rss_kib`, the operating system's maximum resident-set +observation for the child tool. Keep both artifacts when a synthesis run times +out or is interrupted; a missing final netlist is never a successful synthesis +result. +The full and block Yosys flows enforce `YOSYS_TIMEOUT=10800` seconds by +default. Override it only for an explicitly reviewed diagnostic run; timeout +exit 124 remains a failed synthesis verdict. + Generated filelists and MPW output are flow-local. Make depfiles track expanded RTL sources and included headers. The shared MPW generator is protected by a file lock. Default tool parallelism is capped at 16 and can be set with `JOBS=` or `MAX_JOBS=`. diff --git a/physical/smoke/syn/yosys/apu_block.mk b/physical/smoke/syn/yosys/apu_block.mk index 540ec063..0a996df2 100644 --- a/physical/smoke/syn/yosys/apu_block.mk +++ b/physical/smoke/syn/yosys/apu_block.mk @@ -120,7 +120,8 @@ $(APU_BLOCK_NETLIST): $(APU_BLOCK_FL) $(APU_BLOCK_YOSYS_SCRIPTS) --env PROJ_NAME=$(APU_BLOCK_TOP) --env WORK=$(APU_BLOCK_TMP) \ --env BUILD=$(APU_BLOCK_OUT) --env REPORTS=$(APU_BLOCK_RPT) \ --env NETLIST=$(APU_BLOCK_NETLIST) -- \ - yosys -c $(APU_BLOCK_SYNTH_TCL) + timeout --foreground --kill-after=5s $(YOSYS_TIMEOUT)s \ + yosys -t --perffile $(APU_BLOCK_SYN)/yosys-perf.json -c $(APU_BLOCK_SYNTH_TCL) apu-block-synth: $(APU_BLOCK_NETLIST) @@ -170,4 +171,4 @@ apu-block-clean: python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(APU_BLOCK_REPORT_DIR) .PHONY: apu-block-filelist apu-block-synth apu-block-sta apu-block-report \ - apu-block-evidence apu-block-clean \ No newline at end of file + apu-block-evidence apu-block-clean diff --git a/physical/smoke/syn/yosys/ga2d_block.mk b/physical/smoke/syn/yosys/ga2d_block.mk index 28d18061..7e1d4e58 100644 --- a/physical/smoke/syn/yosys/ga2d_block.mk +++ b/physical/smoke/syn/yosys/ga2d_block.mk @@ -83,7 +83,8 @@ $(GA2D_BLOCK_NETLIST): $(GA2D_BLOCK_FLIST) $(GA2D_BLOCK_SCRIPTS) --env PROJ_NAME=$(GA2D_BLOCK_TOP) --env WORK=$(GA2D_BLOCK_TMP) \ --env BUILD=$(GA2D_BLOCK_OUT) --env REPORTS=$(GA2D_BLOCK_RPT) \ --env NETLIST=$(GA2D_BLOCK_NETLIST) -- \ - yosys -c $(YOSYS_DIR)/script/synth.tcl + timeout --foreground --kill-after=5s $(YOSYS_TIMEOUT)s \ + yosys -t --perffile $(GA2D_BLOCK_BUILD)/yosys-perf.json -c $(YOSYS_DIR)/script/synth.tcl # Fails loudly on inferred latches or non-PDK (black-box/unmapped) cells and # records the register/FIFO mapping summary consumed by the evidence section. @@ -175,4 +176,4 @@ sta-ga2d-block-clean: python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(GA2D_BLOCK_STA_DIR) .PHONY: synth-ga2d-block synth-ga2d-block-clean netsim-ga2d-block netsim-ga2d-block-clean \ - sta-ga2d-block sta-ga2d-block-clean \ No newline at end of file + sta-ga2d-block sta-ga2d-block-clean diff --git a/physical/smoke/syn/yosys/script/apu_block_synth.tcl b/physical/smoke/syn/yosys/script/apu_block_synth.tcl index 985aee94..b7456084 100644 --- a/physical/smoke/syn/yosys/script/apu_block_synth.tcl +++ b/physical/smoke/syn/yosys/script/apu_block_synth.tcl @@ -87,6 +87,10 @@ yosys booth yosys alumacc yosys share yosys opt +# Capture the hierarchy and inferred-memory inventory immediately before the +# memory lowering pass. This report survives a later timeout or resource abort. +yosys tee -q -o "${report_dir}/${proj_name}_pre_memory.rpt" stat +yosys tee -q -o "${report_dir}/${proj_name}_pre_memory.json" stat -json yosys memory yosys opt -fast diff --git a/physical/smoke/syn/yosys/script/synth.tcl b/physical/smoke/syn/yosys/script/synth.tcl index 68f85e55..a2709b24 100644 --- a/physical/smoke/syn/yosys/script/synth.tcl +++ b/physical/smoke/syn/yosys/script/synth.tcl @@ -68,6 +68,10 @@ yosys booth yosys alumacc yosys share yosys opt +# Capture the hierarchy and inferred-memory inventory immediately before the +# memory lowering pass. This report survives a later timeout or resource abort. +yosys tee -q -o "${report_dir}/${proj_name}_pre_memory.rpt" stat +yosys tee -q -o "${report_dir}/${proj_name}_pre_memory.json" stat -json yosys memory yosys opt -fast diff --git a/physical/smoke/syn/yosys/yosys.mk b/physical/smoke/syn/yosys/yosys.mk index 5fd5b4a3..9971b6ab 100644 --- a/physical/smoke/syn/yosys/yosys.mk +++ b/physical/smoke/syn/yosys/yosys.mk @@ -24,6 +24,7 @@ YOSYS_BUILD := $(SYN_BUILD_ROOT) YOSYS_OUT := $(YOSYS_BUILD)/out YOSYS_TMP := $(YOSYS_BUILD)/tmp YOSYS_RPT := $(YOSYS_BUILD)/rpt +YOSYS_TIMEOUT ?= 10800 include $(YOSYS_DIR)/synth_config.mk @@ -69,7 +70,8 @@ $(NETLIST): $(SV_FLIST) $(YOSYS_SCRIPTS) --env SV_FLIST=$(SV_FLIST) --env TOP_DESIGN=$(TOP_DESIGN) --env CONFIG=$(NETLIST_CONFIG) \ --env PROJ_NAME=$(RTL_NAME) --env WORK=$(YOSYS_TMP) --env BUILD=$(YOSYS_OUT) \ --env REPORTS=$(YOSYS_RPT) --env NETLIST=$(NETLIST) -- \ - yosys -c $(YOSYS_DIR)/script/synth.tcl + timeout --foreground --kill-after=5s $(YOSYS_TIMEOUT)s \ + yosys -t --perffile $(YOSYS_BUILD)/yosys-perf.json -c $(YOSYS_DIR)/script/synth.tcl $(NETLIST_DEBUG): $(NETLIST) @test -f $@ @@ -77,4 +79,4 @@ $(NETLIST_DEBUG): $(NETLIST) synth_clean: python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(YOSYS_BUILD) -.PHONY: gen_synth_filelist synth_clean synth \ No newline at end of file +.PHONY: gen_synth_filelist synth_clean synth diff --git a/rtl/ip/multimedia/apb4_apu.sv b/rtl/ip/multimedia/apb4_apu.sv index 68aa82c9..3555eec3 100644 --- a/rtl/ip/multimedia/apb4_apu.sv +++ b/rtl/ip/multimedia/apb4_apu.sv @@ -248,7 +248,11 @@ module apb4_apu #( logic s_mc_proof_memo_full_unused; logic [ 31:0] s_kws_sram_data_unused; logic s_kws_sram_valid_unused, s_kws_sram_err_unused; + logic s_kws_storage_req, s_kws_storage_ready, s_kws_storage_done; + logic s_kws_storage_progress, s_kws_storage_write; + logic [15:0] s_kws_model_read_valid; logic s_kws_scratch_clear, s_kws_scratch_err; + logic [19:0] s_kws_scratch_read_valid; logic [19:0][15:0] s_kws_scratch_read_addr; logic [19:0][31:0] s_kws_scratch_read_data; logic [ 5:0] s_kws_scratch_write_valid; @@ -718,9 +722,16 @@ module apb4_apu #( .memory_input_used_o (s_kws_mem_input_used), .kws_config_i (s_codec_kws_config_valid ? s_codec_kws_config : s_kws_config), .input_config_i (s_kws_input_config), + .storage_req_o (s_kws_storage_req), + .storage_ready_i (s_kws_storage_ready), + .storage_done_i (s_kws_storage_done), + .storage_progress_i (s_kws_storage_progress), + .storage_write_o (s_kws_storage_write), + .model_read_valid_o (s_kws_model_read_valid), .model_addr_o (s_kws_model_read_addr), .model_data_i (s_kws_model_read_data), .scratch_clear_o (s_kws_scratch_clear), + .scratch_read_valid_o (s_kws_scratch_read_valid), .scratch_read_addr_o (s_kws_scratch_read_addr), .scratch_read_data_i (s_kws_scratch_read_data), .scratch_write_valid_o (s_kws_scratch_write_valid), @@ -920,9 +931,16 @@ module apb4_apu #( .data_o (s_kws_sram_data_unused), .valid_o (s_kws_sram_valid_unused), .access_err_o (s_kws_sram_err_unused), + .access_req_i (s_kws_storage_req), + .access_ready_o (s_kws_storage_ready), + .access_done_o (s_kws_storage_done), + .access_progress_o (s_kws_storage_progress), + .access_write_i (s_kws_storage_write), + .model_read_valid_i (s_kws_model_read_valid), .model_addr_i (s_kws_model_read_addr), .model_data_o (s_kws_model_read_data), .scratch_clear_i (s_kws_scratch_clear), + .scratch_read_valid_i (s_kws_scratch_read_valid), .scratch_read_addr_i (s_kws_scratch_read_addr), .scratch_read_data_o (s_kws_scratch_read_data), .scratch_write_valid_i(s_kws_scratch_write_valid), diff --git a/rtl/ip/multimedia/apu_kws_engine.sv b/rtl/ip/multimedia/apu_kws_engine.sv index 1123ab05..da39223c 100644 --- a/rtl/ip/multimedia/apu_kws_engine.sv +++ b/rtl/ip/multimedia/apu_kws_engine.sv @@ -49,9 +49,16 @@ module apu_kws_engine ( output logic [31:0] memory_input_used_o, input logic [15:0] kws_config_i, input logic [31:0] input_config_i, + output logic storage_req_o, + input logic storage_ready_i, + input logic storage_done_i, + input logic storage_progress_i, + output logic storage_write_o, + output logic [15:0] model_read_valid_o, output logic [15:0][14:0] model_addr_o, input logic [15:0][ 7:0] model_data_i, output logic scratch_clear_o, + output logic [19:0] scratch_read_valid_o, output logic [19:0][15:0] scratch_read_addr_o, input logic [19:0][31:0] scratch_read_data_i, output logic [ 5:0] scratch_write_valid_o, @@ -175,6 +182,9 @@ 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_read_access, s_storage_write_access, s_storage_has_access; + logic s_storage_final_done, s_storage_state_step; 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; @@ -578,8 +588,8 @@ module apu_kws_engine ( !s_stop_latched_q && model_valid_i && model_lock_i && s_config_valid; assign s_listening = s_continuous_active && s_en_q && !flush_busy_i; assign s_capture_available = (s_front_state_q == FrontIdle) && !s_frame_pending_q && - !s_window_pending_q && (s_fir_tap_q == 6'd0); - assign rx_ready_o = s_listening && s_capture_available; + !s_window_pending_q && (s_infer_state_q == InferIdle) && (s_fir_tap_q == 6'd0); + assign rx_ready_o = s_listening && s_capture_available && !s_storage_wait_q && storage_ready_i; assign s_stream_accept = stream_i.tvalid && rx_ready_o; assign s_stream_legal = (stream_i.tkeep == 4'hf) && (stream_i.tstrb == 4'hf) && !stream_i.tlast; assign s_window_deadline = s_commit_valid && @@ -754,9 +764,11 @@ module apu_kws_engine ( assign s_mac_total = s_mac_q + s_lane_sum; always_comb begin - model_addr_o = '0; - scratch_read_addr_o = '0; - s_lane_active = 16'd0; + model_read_valid_o = 16'd0; + model_addr_o = '0; + scratch_read_valid_o = 20'd0; + scratch_read_addr_o = '0; + s_lane_active = 16'd0; for (int lane = 0; lane < 16; lane++) begin s_lane_term[lane] = {1'b0, s_term_q} + 9'(lane); s_lane_input_index[lane] = 13'd0; @@ -766,14 +778,17 @@ module apu_kws_engine ( end unique case (s_front_state_q) FrontFir: begin + scratch_read_valid_o[0] = 1'b1; scratch_read_addr_o[0] = `RETROSOC_APU_KWS__SCRATCH_FIR_BASE + {9'd0, fir_history_index(s_fir_write_q, s_fir_tap_q), 1'b0}; end FrontFftLoad: begin + scratch_read_valid_o[0] = 1'b1; scratch_read_addr_o[0] = `RETROSOC_APU_KWS__SCRATCH_INGRESS_BASE + 16'(ingress_add(s_frame_start_q, s_front_index_q) * 2); end FrontFftRun: begin + scratch_read_valid_o[3:0] = 4'hf; scratch_read_addr_o[0] = `RETROSOC_APU_KWS__SCRATCH_FFT_REAL_BASE + 16'(s_fft_left_index * 4); scratch_read_addr_o[1] = @@ -784,23 +799,28 @@ module apu_kws_engine ( `RETROSOC_APU_KWS__SCRATCH_FFT_IMAG_BASE + 16'(s_fft_right_index * 4); end FrontMagnitude: begin + scratch_read_valid_o[1:0] = 2'b11; scratch_read_addr_o[0] = `RETROSOC_APU_KWS__SCRATCH_FFT_REAL_BASE + 16'(s_front_index_q * 4); scratch_read_addr_o[1] = `RETROSOC_APU_KWS__SCRATCH_FFT_IMAG_BASE + 16'(s_front_index_q * 4); end FrontMel: begin - scratch_read_addr_o[0] = `RETROSOC_APU_KWS__SCRATCH_FFT_REAL_BASE + 16'(s_mel_bin_q * 4); + scratch_read_valid_o[0] = 1'b1; + scratch_read_addr_o[0] = `RETROSOC_APU_KWS__SCRATCH_FFT_REAL_BASE + 16'(s_mel_bin_q * 4); end FrontPeak: begin - scratch_read_addr_o[0] = `RETROSOC_APU_KWS__SCRATCH_PEAK_BASE + 16'(s_peak_scan_q * 2); + scratch_read_valid_o[0] = 1'b1; + scratch_read_addr_o[0] = `RETROSOC_APU_KWS__SCRATCH_PEAK_BASE + 16'(s_peak_scan_q * 2); end FrontLog: begin + scratch_read_valid_o[0] = 1'b1; scratch_read_addr_o[0] = `RETROSOC_APU_KWS__SCRATCH_MEL_BASE + 16'((16'(s_log_row_index) * 16'd40 + 16'(s_mfcc_band_q)) * 16'd4); end FrontDct: begin - scratch_read_addr_o[0] = `RETROSOC_APU_KWS__SCRATCH_FFT_IMAG_BASE + 16'(s_mfcc_band_q * 4); + scratch_read_valid_o[0] = 1'b1; + scratch_read_addr_o[0] = `RETROSOC_APU_KWS__SCRATCH_FFT_IMAG_BASE + 16'(s_mfcc_band_q * 4); end default: begin end @@ -808,34 +828,42 @@ module apu_kws_engine ( if (s_infer_state_q == InferPool) begin for (int lane = 0; lane < 16; lane++) begin if (({1'b0, s_term_q} + 9'(lane)) < 9'd125) begin + scratch_read_valid_o[lane+4] = 1'b1; scratch_read_addr_o[lane+4] = `RETROSOC_APU_KWS__SCRATCH_A_BASE + 16'((32'(s_term_q) + 32'(lane)) * 32'd64 + 32'(s_output_q)); end end end else if ((s_infer_state_q == InferSoftmaxMax) || (s_infer_state_q == InferSoftmaxSum)) begin - scratch_read_addr_o[4] = `RETROSOC_APU_KWS__SCRATCH_LOGITS_BASE + 16'(s_softmax_index_q); + scratch_read_valid_o[4] = 1'b1; + scratch_read_addr_o[4] = `RETROSOC_APU_KWS__SCRATCH_LOGITS_BASE + 16'(s_softmax_index_q); end else if ((s_infer_state_q == InferSoftmaxScale) && (s_softmax_index_q > 0)) begin + scratch_read_valid_o[4] = 1'b1; scratch_read_addr_o[4] = `RETROSOC_APU_KWS__SCRATCH_LOGITS_BASE + 16'(s_softmax_index_q - 1'b1); end else if (s_infer_state_q == InferComplete) begin for (int class_index = 0; class_index < 12; class_index++) begin + scratch_read_valid_o[class_index+4] = 1'b1; scratch_read_addr_o[class_index+4] = `RETROSOC_APU_KWS__SCRATCH_SOFTMAX_BASE + 16'(class_index); end end if (s_infer_state_q == InferBias) begin for (int lane = 0; lane < 4; lane++) begin - model_addr_o[lane] = s_bias_base + 15'(s_output_channel) * 15'd4 + 15'(lane); + model_read_valid_o[lane] = 1'b1; + model_addr_o[lane] = s_bias_base + 15'(s_output_channel) * 15'd4 + 15'(lane); end end else if (s_infer_state_q == InferQuant) begin for (int lane = 0; lane < 4; lane++) begin - model_addr_o[lane] = s_multiplier_base + 15'(s_output_channel) * 15'd4 + 15'(lane); - model_addr_o[lane+4] = s_shift_base + 15'(s_output_channel) * 15'd4 + 15'(lane); + model_read_valid_o[lane] = 1'b1; + model_read_valid_o[lane+4] = 1'b1; + model_addr_o[lane] = s_multiplier_base + 15'(s_output_channel) * 15'd4 + 15'(lane); + model_addr_o[lane+4] = s_shift_base + 15'(s_output_channel) * 15'd4 + 15'(lane); end end else if (s_infer_state_q == InferMac) begin for (int lane = 0; lane < 16; lane++) begin if (s_lane_term[lane] < {1'b0, s_term_count}) begin - s_lane_active[lane] = 1'b1; + s_lane_active[lane] = 1'b1; + model_read_valid_o[lane] = 1'b1; if (s_operator_q == 4'd0) begin s_lane_input_h[lane] = 7'($signed({1'b0, s_output_height_index})) * 7'sd2 + 7'($signed(s_lane_term[lane] / 9'd4)) - 7'sd4; @@ -844,6 +872,7 @@ module apu_kws_engine ( s_lane_weight_index[lane] = 15'(s_output_channel) * 15'd40 + 15'(s_lane_term[lane]); if ((s_lane_input_h[lane] >= 0) && (s_lane_input_h[lane] < 7'sd49) && (s_lane_input_w[lane] >= 0) && (s_lane_input_w[lane] < 5'sd10)) begin + scratch_read_valid_o[lane+4] = 1'b1; s_lane_input_index[lane] = 13'(s_lane_input_h[lane]) * 13'd10 + 13'(s_lane_input_w[lane]); scratch_read_addr_o[lane+4] = @@ -858,6 +887,7 @@ module apu_kws_engine ( s_lane_weight_index[lane] = 15'(s_lane_term[lane]) * 15'd64 + 15'(s_output_channel); if ((s_lane_input_h[lane] >= 0) && (s_lane_input_h[lane] < 7'sd25) && (s_lane_input_w[lane] >= 0) && (s_lane_input_w[lane] < 5'sd5)) begin + scratch_read_valid_o[lane+4] = 1'b1; s_lane_input_index[lane] = (13'(s_lane_input_h[lane]) * 13'd5 + 13'(s_lane_input_w[lane])) * 13'd64 + 13'(s_output_channel); @@ -865,6 +895,7 @@ module apu_kws_engine ( 16'(s_lane_input_index[lane]); end end else begin + scratch_read_valid_o[lane+4] = 1'b1; s_lane_input_index[lane] = (s_operator_q == 4'd10) ? 13'(s_lane_term[lane]) : (13'(s_output_spatial_index) * 13'd64 + 13'(s_lane_term[lane])); @@ -1134,6 +1165,42 @@ module apu_kws_engine ( end end + assign s_storage_read_access = |model_read_valid_o || |scratch_read_valid_o; + 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 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); + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + s_storage_wait_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_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_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_write_phase_q <= 1'b1; + end else begin + s_storage_write_phase_q <= 1'b0; + s_storage_state_owned_q <= 1'b0; + end + end + end + end + assign memory_dma_request_valid_o = s_mem_req_q; assign memory_start_ready_o = enable_i && memory_window_i && model_valid_i && model_lock_i && !abort_i && !soft_reset_i && !resource_reset_i && !s_mem_job_q && @@ -1338,12 +1405,14 @@ module apu_kws_engine ( s_mem_err_detail_q <= 32'd0; end end else begin - if (s_front_state_q == FrontIdle || s_front_progress) begin + if (s_front_state_q == FrontIdle || storage_progress_i || + (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 || s_infer_progress) begin + if (s_infer_state_q == InferIdle || storage_progress_i || + (s_storage_state_step && s_infer_progress)) begin s_infer_watchdog_q <= 32'd0; end else if (!(&s_infer_watchdog_q)) begin s_infer_watchdog_q <= s_infer_watchdog_q + 1'b1; @@ -1493,7 +1562,7 @@ module apu_kws_engine ( s_s24_pending_q <= 1'b0; end - if (s_commit_valid) begin + if (s_commit_valid && ((s_front_state_q == FrontIdle) || 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; @@ -1528,295 +1597,297 @@ module apu_kws_engine ( end end - unique case (s_front_state_q) - FrontIdle: begin - if (s_frame_pending_q) begin - s_frame_pending_q <= 1'b0; - s_front_index_q <= 10'd0; - s_front_state_q <= FrontFftLoad; - end else if (s_window_pending_q && (s_mel_count_q >= 6'd49) && + if (s_storage_state_step) begin + unique case (s_front_state_q) + FrontIdle: begin + if (s_frame_pending_q) begin + s_frame_pending_q <= 1'b0; + s_front_index_q <= 10'd0; + s_front_state_q <= FrontFftLoad; + end else if (s_window_pending_q && (s_mel_count_q >= 6'd49) && (s_infer_state_q == InferIdle)) begin - s_window_pending_q <= 1'b0; - s_peak_scan_q <= 7'd0; - s_window_peak_q <= 16'd1; - s_front_state_q <= FrontPeak; - end - end - FrontFir: begin - if (s_fir_tap_q == 6'd62) begin - s_fir_tap_q <= 6'd0; - s_fir_acc_q <= 64'sd0; - s_fir_phase_q <= (s_fir_phase_q == (s_decimation - 1'b1)) ? - 3'd0 : s_fir_phase_q + 1'b1; - s_front_state_q <= FrontIdle; - end else begin - s_fir_acc_q <= s_fir_total; - s_fir_tap_q <= s_fir_tap_q + 1'b1; - end - end - FrontFftLoad: begin - if (s_front_index_q == 10'd511) begin - s_fft_len_q <= 10'd2; - s_fft_half_q <= 9'd1; - s_fft_group_q <= 9'd0; - s_fft_j_q <= 8'd0; - s_front_state_q <= FrontFftRun; - end else begin - s_front_index_q <= s_front_index_q + 1'b1; - end - end - FrontFftRun: begin - if (s_fft_overflow) 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_detail_q <= 32'h0700_0014; - s_stop_latched_q <= 1'b1; - s_front_state_q <= FrontIdle; - end else begin - if (s_fft_j_q + 1'b1 == s_fft_half_q) begin - s_fft_j_q <= 8'd0; - if (s_fft_group_q + s_fft_len_q == 10'd512) begin - s_fft_group_q <= 9'd0; - if (s_fft_len_q == 10'd512) begin - s_front_index_q <= 10'd0; - s_front_state_q <= FrontMagnitude; - end else begin - s_fft_len_q <= s_fft_len_q << 1; - s_fft_half_q <= s_fft_half_q << 1; - end - end else begin - s_fft_group_q <= 9'(s_fft_group_q + s_fft_len_q); - end - end else begin - s_fft_j_q <= s_fft_j_q + 1'b1; + s_window_pending_q <= 1'b0; + s_peak_scan_q <= 7'd0; + s_window_peak_q <= 16'd1; + s_front_state_q <= FrontPeak; end end - end - FrontMagnitude: begin - if (s_front_index_q == 10'd256) begin - s_mel_band_q <= 8'd0; - s_mel_bin_q <= 9'd0; - s_mel_acc_q <= 64'd0; - s_front_state_q <= FrontMel; - end else begin - s_front_index_q <= s_front_index_q + 1'b1; - end - end - FrontMel: begin - if (s_mel_total < s_mel_acc_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_detail_q <= 32'h0700_0015; - s_stop_latched_q <= 1'b1; - s_front_state_q <= FrontIdle; - end else if (s_mel_overwrite) begin - s_front_state_q <= FrontIdle; - end else if (s_mel_bin_q == 9'd256) begin - s_mel_bin_q <= 9'd0; - s_mel_acc_q <= 64'd0; - if (s_mel_band_q == 8'd39) begin - s_mel_band_q <= 8'd0; - s_mel_write_q <= (s_mel_write_q == 6'd49) ? 6'd0 : s_mel_write_q + 1'b1; - if (s_mel_count_q < 6'd50) s_mel_count_q <= s_mel_count_q + 1'b1; - if (!counter_clear_i) begin - s_frame_count_q <= (s_frame_count_q == 32'hffff_ffff) ? - s_frame_count_q : s_frame_count_q + 1'b1; - end + FrontFir: begin + if (s_fir_tap_q == 6'd62) begin + s_fir_tap_q <= 6'd0; + s_fir_acc_q <= 64'sd0; + s_fir_phase_q <= (s_fir_phase_q == (s_decimation - 1'b1)) ? + 3'd0 : s_fir_phase_q + 1'b1; s_front_state_q <= FrontIdle; end else begin - s_mel_band_q <= s_mel_band_q + 1'b1; + s_fir_acc_q <= s_fir_total; + s_fir_tap_q <= s_fir_tap_q + 1'b1; end - end else begin - s_mel_acc_q <= s_mel_total; - s_mel_bin_q <= s_mel_bin_q + 1'b1; end - end - FrontPeak: begin - if (s_peak_data > s_window_peak_q) begin - s_window_peak_q <= s_peak_data; + FrontFftLoad: begin + if (s_front_index_q == 10'd511) begin + s_fft_len_q <= 10'd2; + s_fft_half_q <= 9'd1; + s_fft_group_q <= 9'd0; + s_fft_j_q <= 8'd0; + s_front_state_q <= FrontFftRun; + end else begin + s_front_index_q <= s_front_index_q + 1'b1; + end end - if (s_peak_scan_q == 7'd99) begin - s_mfcc_row_q <= 6'd0; - s_mfcc_band_q <= 6'd0; - s_front_state_q <= FrontLog; - end else begin - s_peak_scan_q <= s_peak_scan_q + 1'b1; + FrontFftRun: begin + if (s_fft_overflow) 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_detail_q <= 32'h0700_0014; + s_stop_latched_q <= 1'b1; + s_front_state_q <= FrontIdle; + end else begin + if (s_fft_j_q + 1'b1 == s_fft_half_q) begin + s_fft_j_q <= 8'd0; + if (s_fft_group_q + s_fft_len_q == 10'd512) begin + s_fft_group_q <= 9'd0; + if (s_fft_len_q == 10'd512) begin + s_front_index_q <= 10'd0; + s_front_state_q <= FrontMagnitude; + end else begin + s_fft_len_q <= s_fft_len_q << 1; + s_fft_half_q <= s_fft_half_q << 1; + end + end else begin + s_fft_group_q <= 9'(s_fft_group_q + s_fft_len_q); + end + end else begin + s_fft_j_q <= s_fft_j_q + 1'b1; + end + end end - end - FrontLog: begin - if (s_mfcc_band_q == 6'd39) begin - s_mfcc_band_q <= 6'd0; - s_mfcc_coefficient_q <= 4'd0; - s_dct_acc_q <= 64'sd0; - s_front_state_q <= FrontDct; - end else begin - s_mfcc_band_q <= s_mfcc_band_q + 1'b1; + FrontMagnitude: begin + if (s_front_index_q == 10'd256) begin + s_mel_band_q <= 8'd0; + s_mel_bin_q <= 9'd0; + s_mel_acc_q <= 64'd0; + s_front_state_q <= FrontMel; + end else begin + s_front_index_q <= s_front_index_q + 1'b1; + end end - end - FrontDct: begin - if (s_mfcc_band_q == 6'd39) begin - s_mfcc_band_q <= 6'd0; - s_dct_acc_q <= 64'sd0; - if (s_mfcc_coefficient_q == 4'd9) begin - s_mfcc_coefficient_q <= 4'd0; - if (s_mfcc_row_q == 6'd48) begin - s_operator_q <= 4'd0; - s_output_q <= 14'd0; - s_infer_state_q <= InferBias; + FrontMel: begin + if (s_mel_total < s_mel_acc_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_detail_q <= 32'h0700_0015; + s_stop_latched_q <= 1'b1; + s_front_state_q <= FrontIdle; + end else if (s_mel_overwrite) begin + s_front_state_q <= FrontIdle; + end else if (s_mel_bin_q == 9'd256) begin + s_mel_bin_q <= 9'd0; + s_mel_acc_q <= 64'd0; + if (s_mel_band_q == 8'd39) begin + s_mel_band_q <= 8'd0; + s_mel_write_q <= (s_mel_write_q == 6'd49) ? 6'd0 : s_mel_write_q + 1'b1; + if (s_mel_count_q < 6'd50) s_mel_count_q <= s_mel_count_q + 1'b1; + if (!counter_clear_i) begin + s_frame_count_q <= (s_frame_count_q == 32'hffff_ffff) ? + s_frame_count_q : s_frame_count_q + 1'b1; + end s_front_state_q <= FrontIdle; end else begin - s_mfcc_row_q <= s_mfcc_row_q + 1'b1; - s_front_state_q <= FrontLog; + s_mel_band_q <= s_mel_band_q + 1'b1; end end else begin - s_mfcc_coefficient_q <= s_mfcc_coefficient_q + 1'b1; + s_mel_acc_q <= s_mel_total; + s_mel_bin_q <= s_mel_bin_q + 1'b1; end - end else begin - s_dct_acc_q <= s_dct_total; - s_mfcc_band_q <= s_mfcc_band_q + 1'b1; end - end - default: s_front_state_q <= FrontIdle; - endcase - - unique case (s_infer_state_q) - InferIdle: begin - end - InferBias: begin - s_mac_q <= 64'($signed(s_bias_word)); - s_term_q <= 8'd0; - s_infer_state_q <= InferMac; - end - InferMac: begin - if ((s_mac_total > 64'sh0000_0000_7fff_ffff) || - (s_mac_total < -64'sh0000_0000_8000_0000)) 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_detail_q <= 32'h0700_0016; - s_stop_latched_q <= 1'b1; - s_infer_state_q <= InferIdle; - end else if (s_term_q + 8'd16 >= s_term_count) begin - s_mac_q <= s_mac_total; - s_infer_state_q <= InferQuant; - end else begin - s_mac_q <= s_mac_total; - s_term_q <= s_term_q + 8'd16; + FrontPeak: begin + if (s_peak_data > s_window_peak_q) begin + s_window_peak_q <= s_peak_data; + end + if (s_peak_scan_q == 7'd99) begin + s_mfcc_row_q <= 6'd0; + s_mfcc_band_q <= 6'd0; + s_front_state_q <= FrontLog; + end else begin + s_peak_scan_q <= s_peak_scan_q + 1'b1; + end end - end - InferQuant: begin - if (s_requant_overflow) 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_detail_q <= 32'h0700_0017; - s_stop_latched_q <= 1'b1; - s_infer_state_q <= InferIdle; - end else begin - if (s_output_q + 1'b1 == s_output_count) begin - s_output_q <= 14'd0; - if (s_operator_q == 4'd8) begin - s_operator_q <= 4'd9; - s_term_q <= 8'd0; - s_mac_q <= 64'sd0; - s_infer_state_q <= InferPool; - end else if (s_operator_q == 4'd10) begin - s_operator_q <= 4'd11; - s_softmax_index_q <= 4'd0; - s_softmax_max_q <= -8'sd128; - s_infer_state_q <= InferSoftmaxMax; + FrontLog: begin + if (s_mfcc_band_q == 6'd39) begin + s_mfcc_band_q <= 6'd0; + s_mfcc_coefficient_q <= 4'd0; + s_dct_acc_q <= 64'sd0; + s_front_state_q <= FrontDct; + end else begin + s_mfcc_band_q <= s_mfcc_band_q + 1'b1; + end + end + FrontDct: begin + if (s_mfcc_band_q == 6'd39) begin + s_mfcc_band_q <= 6'd0; + s_dct_acc_q <= 64'sd0; + if (s_mfcc_coefficient_q == 4'd9) begin + s_mfcc_coefficient_q <= 4'd0; + if (s_mfcc_row_q == 6'd48) begin + s_operator_q <= 4'd0; + s_output_q <= 14'd0; + s_infer_state_q <= InferBias; + s_front_state_q <= FrontIdle; + end else begin + s_mfcc_row_q <= s_mfcc_row_q + 1'b1; + s_front_state_q <= FrontLog; + end end else begin - s_operator_q <= s_operator_q + 1'b1; + s_mfcc_coefficient_q <= s_mfcc_coefficient_q + 1'b1; + end + end else begin + s_dct_acc_q <= s_dct_total; + s_mfcc_band_q <= s_mfcc_band_q + 1'b1; + end + end + default: s_front_state_q <= FrontIdle; + endcase + + unique case (s_infer_state_q) + InferIdle: begin + end + InferBias: begin + s_mac_q <= 64'($signed(s_bias_word)); + s_term_q <= 8'd0; + s_infer_state_q <= InferMac; + end + InferMac: begin + if ((s_mac_total > 64'sh0000_0000_7fff_ffff) || + (s_mac_total < -64'sh0000_0000_8000_0000)) 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_detail_q <= 32'h0700_0016; + s_stop_latched_q <= 1'b1; + s_infer_state_q <= InferIdle; + end else if (s_term_q + 8'd16 >= s_term_count) begin + s_mac_q <= s_mac_total; + s_infer_state_q <= InferQuant; + end else begin + s_mac_q <= s_mac_total; + s_term_q <= s_term_q + 8'd16; + end + end + InferQuant: begin + if (s_requant_overflow) 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_detail_q <= 32'h0700_0017; + s_stop_latched_q <= 1'b1; + s_infer_state_q <= InferIdle; + end else begin + if (s_output_q + 1'b1 == s_output_count) begin + s_output_q <= 14'd0; + if (s_operator_q == 4'd8) begin + s_operator_q <= 4'd9; + s_term_q <= 8'd0; + s_mac_q <= 64'sd0; + s_infer_state_q <= InferPool; + end else if (s_operator_q == 4'd10) begin + s_operator_q <= 4'd11; + s_softmax_index_q <= 4'd0; + s_softmax_max_q <= -8'sd128; + s_infer_state_q <= InferSoftmaxMax; + end else begin + s_operator_q <= s_operator_q + 1'b1; + s_infer_state_q <= InferBias; + end + end else begin + s_output_q <= s_output_q + 1'b1; s_infer_state_q <= InferBias; end - end else begin - s_output_q <= s_output_q + 1'b1; - s_infer_state_q <= InferBias; end end - end - InferPool: begin - if (s_term_q + 8'd16 >= 8'd125) begin - s_term_q <= 8'd0; - s_mac_q <= 64'sd0; - if (s_output_q == 14'd63) begin - s_output_q <= 14'd0; - s_operator_q <= 4'd10; - s_infer_state_q <= InferBias; + InferPool: begin + if (s_term_q + 8'd16 >= 8'd125) begin + s_term_q <= 8'd0; + s_mac_q <= 64'sd0; + if (s_output_q == 14'd63) begin + s_output_q <= 14'd0; + s_operator_q <= 4'd10; + s_infer_state_q <= InferBias; + end else begin + s_output_q <= s_output_q + 1'b1; + end end else begin - s_output_q <= s_output_q + 1'b1; + s_mac_q <= s_pool_sum; + s_term_q <= s_term_q + 8'd16; end - end else begin - s_mac_q <= s_pool_sum; - s_term_q <= s_term_q + 8'd16; end - end - InferSoftmaxMax: begin - if (s_logit_data > s_softmax_max_q) begin - s_softmax_max_q <= s_logit_data; + InferSoftmaxMax: begin + if (s_logit_data > s_softmax_max_q) begin + s_softmax_max_q <= s_logit_data; + end + if (s_softmax_index_q == 4'd11) begin + s_softmax_index_q <= 4'd0; + s_softmax_sum_q <= 32'd0; + s_infer_state_q <= InferSoftmaxSum; + end else begin + s_softmax_index_q <= s_softmax_index_q + 1'b1; + end end - if (s_softmax_index_q == 4'd11) begin - s_softmax_index_q <= 4'd0; - s_softmax_sum_q <= 32'd0; - s_infer_state_q <= InferSoftmaxSum; - end else begin - s_softmax_index_q <= s_softmax_index_q + 1'b1; + 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); + end + if (s_softmax_index_q == 4'd11) begin + s_softmax_index_q <= 4'd0; + s_infer_state_q <= InferSoftmaxScale; + end else begin + s_softmax_index_q <= s_softmax_index_q + 1'b1; + end end - end - 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); + InferSoftmaxScale: begin + if (s_softmax_index_q == 0) begin + s_softmax_scale_q <= reciprocal_q31(s_softmax_shifted_sum); + s_softmax_bits_q <= 6'(7'sd12 - $signed({1'b0, s_softmax_headroom})); + end + if (s_softmax_index_q == 4'd12) begin + s_softmax_index_q <= 4'd0; + s_infer_state_q <= InferComplete; + end else begin + s_softmax_index_q <= s_softmax_index_q + 1'b1; + end end - if (s_softmax_index_q == 4'd11) begin - s_softmax_index_q <= 4'd0; - s_infer_state_q <= InferSoftmaxScale; - end else begin - s_softmax_index_q <= s_softmax_index_q + 1'b1; - end - end - InferSoftmaxScale: begin - if (s_softmax_index_q == 0) begin - s_softmax_scale_q <= reciprocal_q31(s_softmax_shifted_sum); - s_softmax_bits_q <= 6'(7'sd12 - $signed({1'b0, s_softmax_headroom})); - end - if (s_softmax_index_q == 4'd12) begin - s_softmax_index_q <= 4'd0; - s_infer_state_q <= InferComplete; - end else begin - s_softmax_index_q <= s_softmax_index_q + 1'b1; - end - end - InferComplete: begin - s_result_q <= {15'd0, s_stable_hit, s_best_score, s_best_class}; - s_result_valid_q <= 1'b1; - s_timestamp_lo_q <= s_timestamp_q; - s_timestamp_hi_q <= 32'd0; - if (!counter_clear_i) begin - s_inference_count_q <= (s_inference_count_q == 32'hffff_ffff) ? - s_inference_count_q : s_inference_count_q + 1'b1; - end - s_debounce_run_q <= s_next_debounce_run[7:0]; - s_last_class_q <= s_best_class; - s_last_match_q <= s_match; - s_stable_hit_q <= s_stable_hit; - if (s_hit_event) begin + InferComplete: begin + s_result_q <= {15'd0, s_stable_hit, s_best_score, s_best_class}; + s_result_valid_q <= 1'b1; + s_timestamp_lo_q <= s_timestamp_q; + s_timestamp_hi_q <= 32'd0; if (!counter_clear_i) begin - s_hit_count_q <= (s_hit_count_q == 32'hffff_ffff) ? + s_inference_count_q <= (s_inference_count_q == 32'hffff_ffff) ? + s_inference_count_q : s_inference_count_q + 1'b1; + end + s_debounce_run_q <= s_next_debounce_run[7:0]; + s_last_class_q <= s_best_class; + s_last_match_q <= s_match; + s_stable_hit_q <= s_stable_hit; + if (s_hit_event) begin + if (!counter_clear_i) begin + s_hit_count_q <= (s_hit_count_q == 32'hffff_ffff) ? s_hit_count_q : s_hit_count_q + 1'b1; + end + s_hit_irq_q <= 1'b1; + end + s_infer_state_q <= InferIdle; + if (s_mem_job_q) begin + s_mem_job_q <= 1'b0; + s_mem_done_q <= 1'b1; end - s_hit_irq_q <= 1'b1; end - s_infer_state_q <= InferIdle; - if (s_mem_job_q) begin - s_mem_job_q <= 1'b0; - s_mem_done_q <= 1'b1; - end - end - default: s_infer_state_q <= InferIdle; - endcase + default: s_infer_state_q <= InferIdle; + endcase + end if ((s_mem_job_q && s_mem_dma_done_q && !s_mem_word_valid_q) && ((s_mem_received_q != 32'd32000) || s_mem_err_q)) begin diff --git a/rtl/ip/multimedia/apu_kws_sram_client.sv b/rtl/ip/multimedia/apu_kws_sram_client.sv index b0cbaa35..4dd5cd30 100644 --- a/rtl/ip/multimedia/apu_kws_sram_client.sv +++ b/rtl/ip/multimedia/apu_kws_sram_client.sv @@ -16,9 +16,16 @@ module apu_kws_sram_client ( output logic [31:0] data_o, output logic valid_o, output logic access_err_o, + input logic access_req_i, + output logic access_ready_o, + output logic access_done_o, + output logic access_progress_o, + input logic access_write_i, + input logic [15:0] model_read_valid_i, input logic [15:0][14:0] model_addr_i, output logic [15:0][ 7:0] model_data_o, input logic scratch_clear_i, + input logic [19:0] scratch_read_valid_i, input logic [19:0][15:0] scratch_read_addr_i, output logic [19:0][31:0] scratch_read_data_o, input logic [ 5:0] scratch_write_valid_i, @@ -28,108 +35,328 @@ module apu_kws_sram_client ( output logic scratch_access_err_o // verilog_format: on ); - localparam int unsigned ModelWordCount = `RETROSOC_APU_KWS__MODEL_BYTES / 4; - localparam int unsigned ScratchWordCount = `RETROSOC_APU_KWS__SCRATCH_BYTES / 4; - localparam int unsigned KwsWordCount = ModelWordCount + ScratchWordCount; + localparam int unsigned BankCount = 16; localparam int unsigned FirEntryCount = 63; - logic [ 31:0] mem [0:KwsWordCount-1]; - logic [FirEntryCount-1:0] s_fir_valid_q; - logic s_pending_q; - logic s_err_q; - logic [ 31:0] s_data_q; - logic s_range_ok; - logic [ 13:0] s_word_addr; + 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][ 3:0] s_bank_wmask; + logic [ BankCount-1:0][31:0] s_bank_rdata; - assign s_range_ok = (addr_i[1:0] == 2'd0) && + logic s_busy_q; + logic [ 15:0] s_model_pending_q; + logic [ 15:0][14:0] s_model_addr_q; + logic [ 19:0] s_scratch_read_pending_q; + logic [ 19:0][15:0] s_scratch_read_addr_q; + logic [ 5:0] s_scratch_write_pending_q; + logic [ 5:0][15:0] s_scratch_write_addr_q; + logic [ 5:0][31:0] s_scratch_write_data_q; + logic [ 5:0][ 3:0] s_scratch_write_strb_q; + logic [ BankCount-1:0] s_read_pipe_valid_q; + logic [ BankCount-1:0][15:0] s_read_pipe_model_q; + logic [ BankCount-1:0][19:0] s_read_pipe_scratch_q; + + logic [ BankCount-1:0] s_issue_write; + logic [ BankCount-1:0][ 2:0] s_issue_write_lane; + logic [ BankCount-1:0] s_issue_read; + logic [ BankCount-1:0][15:0] s_issue_model_mask; + logic [ BankCount-1:0][19:0] s_issue_scratch_mask; + logic [FirEntryCount-1:0] s_fir_valid_q; + + logic s_loader_range_ok; + logic [ 14:0] s_loader_offset; + logic [ 3:0] s_loader_bank; + logic [ 9:0] s_loader_row; + logic s_loader_read_pending_q; + logic [ 3:0] s_loader_read_bank_q; + logic s_loader_read_err_q; + +`ifndef SYNTHESIS + // Kept as a simulation-loading boundary for the existing APUM/MFCC fixtures. + // The synthesizable macro path below contains only the sixteen physical banks. + logic [31:0] mem[0:16383]; +`endif + + function automatic logic [3:0] model_bank(input logic [14:0] address_i); + model_bank = {1'b0, address_i[4:2] ^ address_i[8:6]}; + endfunction + + function automatic logic [3:0] scratch_bank(input logic [15:0] address_i); + scratch_bank = {1'b1, address_i[4:2] ^ address_i[8:6]}; + endfunction + + function automatic logic [13:0] bank_word(input logic [3:0] bank_i, input logic [9:0] row_i); + bank_word = {bank_i[3], row_i, bank_i[2:0] ^ row_i[3:1]}; + endfunction + + assign s_loader_range_ok = (addr_i[1:0] == 2'd0) && (addr_i >= `APB4_APU__LOCAL_KWS_BASE) && (addr_i < (`APB4_APU__LOCAL_KWS_BASE + `RETROSOC_APU_KWS__MODEL_BYTES)); - assign s_word_addr = 14'(addr_i[15:2] - 14'h2800); - assign ready_o = rst_n_i && !s_pending_q; - assign data_o = s_data_q; - assign valid_o = s_pending_q && !s_err_q; - assign access_err_o = (req_i && ready_o && !s_range_ok) || (s_pending_q && s_err_q); + assign s_loader_offset = 15'(addr_i - `APB4_APU__LOCAL_KWS_BASE); + assign s_loader_bank = model_bank(s_loader_offset); + assign s_loader_row = s_loader_offset[14:5]; - for (genvar lane = 0; lane < 16; lane++) begin : gen_model_read - assign model_data_o[lane] = mem[{1'b0, model_addr_i[lane][14:2]}][8*model_addr_i[lane][1:0]+:8]; - end - - for (genvar lane = 0; lane < 20; lane++) begin : gen_scratch_read - logic s_fir_range; - logic [ 5:0] s_fir_index; - logic [13:0] s_memory_word; - - assign s_fir_range = - (scratch_read_addr_i[lane] >= `RETROSOC_APU_KWS__SCRATCH_FIR_BASE) && - (scratch_read_addr_i[lane] < - (`RETROSOC_APU_KWS__SCRATCH_FIR_BASE + 16'(FirEntryCount * 2))); - assign s_fir_index = 6'((scratch_read_addr_i[lane] - `RETROSOC_APU_KWS__SCRATCH_FIR_BASE) >> 1); - assign s_memory_word = 14'(ModelWordCount) + {1'b0, scratch_read_addr_i[lane][14:2]}; - assign scratch_read_data_o[lane] = - scratch_read_addr_i[lane][15] || (s_fir_range && !s_fir_valid_q[s_fir_index]) ? - 32'd0 : mem[s_memory_word]; - end + assign ready_o = rst_n_i && !s_busy_q && !s_loader_read_pending_q; + assign data_o = s_bank_rdata[s_loader_read_bank_q]; + assign valid_o = s_loader_read_pending_q && !s_loader_read_err_q; + assign access_err_o = (req_i && ready_o && !s_loader_range_ok) || + (s_loader_read_pending_q && s_loader_read_err_q); + assign access_ready_o = rst_n_i && !s_busy_q && !s_loader_read_pending_q; + assign access_done_o = s_busy_q && (s_model_pending_q == 16'd0) && + (s_scratch_read_pending_q == 20'd0) && (s_scratch_write_pending_q == 6'd0) && + (s_read_pipe_valid_q == BankCount'(0)); + assign access_progress_o = |s_issue_write || |s_issue_read; always_comb begin scratch_access_err_o = 1'b0; + for (int lane = 0; lane < 20; lane++) begin + scratch_access_err_o |= scratch_read_valid_i[lane] && scratch_read_addr_i[lane][15]; + end for (int lane = 0; lane < 6; lane++) begin scratch_access_err_o |= scratch_write_valid_i[lane] && scratch_write_addr_i[lane][15]; end end - always_ff @(posedge clk_i or negedge rst_n_i) begin - if (!rst_n_i) begin - s_fir_valid_q <= FirEntryCount'(0); - s_pending_q <= 1'b0; - s_err_q <= 1'b0; - s_data_q <= 32'd0; - end else begin - s_pending_q <= 1'b0; - s_err_q <= 1'b0; - if (scratch_clear_i) begin - s_fir_valid_q <= FirEntryCount'(0); - end - if (req_i && ready_o) begin - if (!s_range_ok) begin - s_pending_q <= 1'b1; - s_err_q <= 1'b1; - end else if (write_i) begin - if (strb_i[0]) mem[s_word_addr][7:0] <= data_i[7:0]; - if (strb_i[1]) mem[s_word_addr][15:8] <= data_i[15:8]; - if (strb_i[2]) mem[s_word_addr][23:16] <= data_i[23:16]; - if (strb_i[3]) mem[s_word_addr][31:24] <= data_i[31:24]; - end else begin - s_data_q <= mem[s_word_addr]; - s_pending_q <= 1'b1; + always_comb begin + s_bank_cs = BankCount'(0); + s_bank_wren = BankCount'(0); + s_bank_addr = '0; + s_bank_wdata = '0; + s_bank_wmask = '0; + s_issue_write = BankCount'(0); + s_issue_write_lane = '0; + s_issue_read = BankCount'(0); + s_issue_model_mask = '0; + s_issue_scratch_mask = '0; + + if (req_i && ready_o && s_loader_range_ok) begin + s_bank_cs[s_loader_bank] = 1'b1; + s_bank_wren[s_loader_bank] = write_i; + s_bank_addr[s_loader_bank] = s_loader_row; + s_bank_wdata[s_loader_bank] = data_i; + s_bank_wmask[s_loader_bank] = strb_i; + end else if (s_busy_q) begin + for (int bank = 0; bank < BankCount; bank++) begin + for (int lane = 0; lane < 6; lane++) begin + if (!s_bank_cs[bank] && s_scratch_write_pending_q[lane] && (scratch_bank( + s_scratch_write_addr_q[lane] + ) == 4'(bank))) begin + s_issue_write[bank] = 1'b1; + s_issue_write_lane[bank] = 3'(lane); + s_bank_cs[bank] = 1'b1; + s_bank_wren[bank] = 1'b1; + s_bank_addr[bank] = s_scratch_write_addr_q[lane][14:5]; + s_bank_wdata[bank] = s_scratch_write_data_q[lane]; + s_bank_wmask[bank] = s_scratch_write_strb_q[lane]; + end + end + for (int lane = 0; lane < 20; lane++) begin + if (!s_bank_cs[bank] && s_scratch_read_pending_q[lane] && (scratch_bank( + s_scratch_read_addr_q[lane] + ) == 4'(bank))) begin + s_issue_read[bank] = 1'b1; + s_bank_cs[bank] = 1'b1; + s_bank_addr[bank] = s_scratch_read_addr_q[lane][14:5]; + end + end + for (int lane = 0; lane < 16; lane++) begin + if (!s_bank_cs[bank] && s_model_pending_q[lane] && (model_bank( + s_model_addr_q[lane] + ) == 4'(bank))) begin + s_issue_read[bank] = 1'b1; + s_bank_cs[bank] = 1'b1; + s_bank_addr[bank] = s_model_addr_q[lane][14:5]; + end end end - for (int lane = 0; lane < 6; lane++) begin - if (scratch_write_valid_i[lane] && !scratch_write_addr_i[lane][15]) begin - if (scratch_write_strb_i[lane][0]) begin - mem[14'(ModelWordCount)+{1'b0, scratch_write_addr_i[lane][14:2]}][7:0] <= - scratch_write_data_i[lane][7:0]; + for (int bank = 0; bank < BankCount; bank++) begin + for (int lane = 0; lane < 20; lane++) begin + if (s_issue_read[bank] && s_scratch_read_pending_q[lane] && (scratch_bank( + s_scratch_read_addr_q[lane] + ) == 4'(bank)) && (s_scratch_read_addr_q[lane][14:5] == s_bank_addr[bank])) begin + s_issue_scratch_mask[bank][lane] = 1'b1; end - if (scratch_write_strb_i[lane][1]) begin - mem[14'(ModelWordCount)+{1'b0, scratch_write_addr_i[lane][14:2]}][15:8] <= - scratch_write_data_i[lane][15:8]; - end - if (scratch_write_strb_i[lane][2]) begin - mem[14'(ModelWordCount)+{1'b0, scratch_write_addr_i[lane][14:2]}][23:16] <= - scratch_write_data_i[lane][23:16]; - end - if (scratch_write_strb_i[lane][3]) begin - mem[14'(ModelWordCount)+{1'b0, scratch_write_addr_i[lane][14:2]}][31:24] <= - scratch_write_data_i[lane][31:24]; - end - if ((scratch_write_addr_i[lane] >= `RETROSOC_APU_KWS__SCRATCH_FIR_BASE) && - (scratch_write_addr_i[lane] < - (`RETROSOC_APU_KWS__SCRATCH_FIR_BASE + 16'(FirEntryCount * 2)))) begin - s_fir_valid_q[6'( - (scratch_write_addr_i[lane] - `RETROSOC_APU_KWS__SCRATCH_FIR_BASE) >> 1 - )] <= 1'b1; + end + for (int lane = 0; lane < 16; lane++) begin + if (s_issue_read[bank] && s_model_pending_q[lane] && (model_bank( + s_model_addr_q[lane] + ) == 4'(bank)) && (s_model_addr_q[lane][14:5] == s_bank_addr[bank])) begin + s_issue_model_mask[bank][lane] = 1'b1; end end end end end + + for (genvar bank = 0; bank < BankCount; bank++) begin : gen_kws_bank +`ifdef HAVE_SRAM_MACRO + tc_sram_1024x32 u_kws_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(s_bank_wmask[bank]), + .wren_i(s_bank_wren[bank]), + .data_o(s_bank_rdata[bank]) + ); +`elsif PDK_BEHAV + tc_sram_1024x32 u_kws_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(s_bank_wmask[bank]), + .wren_i(s_bank_wren[bank]), + .data_o(s_bank_rdata[bank]) + ); +`else +`ifndef SYNTHESIS + logic [31:0] s_read_data_q; + + assign s_bank_rdata[bank] = s_read_data_q; + always_ff @(posedge clk_i) begin + if (s_bank_cs[bank]) begin + if (s_bank_wren[bank]) begin + if (s_bank_wmask[bank][0]) begin + mem[bank_word(4'(bank), s_bank_addr[bank])][7:0] <= s_bank_wdata[bank][7:0]; + end + if (s_bank_wmask[bank][1]) begin + mem[bank_word(4'(bank), s_bank_addr[bank])][15:8] <= s_bank_wdata[bank][15:8]; + end + if (s_bank_wmask[bank][2]) begin + mem[bank_word(4'(bank), s_bank_addr[bank])][23:16] <= s_bank_wdata[bank][23:16]; + end + if (s_bank_wmask[bank][3]) begin + mem[bank_word(4'(bank), s_bank_addr[bank])][31:24] <= s_bank_wdata[bank][31:24]; + end + end else begin + s_read_data_q <= mem[bank_word(4'(bank), s_bank_addr[bank])]; + end + end + end +`else + logic [31:0] s_storage_q [0:1023]; + logic [31:0] s_read_data_q; + + assign s_bank_rdata[bank] = s_read_data_q; + always_ff @(posedge clk_i) begin + if (s_bank_cs[bank]) begin + if (s_bank_wren[bank]) begin + if (s_bank_wmask[bank][0]) begin + s_storage_q[s_bank_addr[bank]][7:0] <= s_bank_wdata[bank][7:0]; + end + if (s_bank_wmask[bank][1]) begin + s_storage_q[s_bank_addr[bank]][15:8] <= s_bank_wdata[bank][15:8]; + end + if (s_bank_wmask[bank][2]) begin + s_storage_q[s_bank_addr[bank]][23:16] <= s_bank_wdata[bank][23:16]; + end + if (s_bank_wmask[bank][3]) begin + s_storage_q[s_bank_addr[bank]][31:24] <= s_bank_wdata[bank][31:24]; + end + end else begin + s_read_data_q <= s_storage_q[s_bank_addr[bank]]; + end + end + end +`endif +`endif + end + + always_ff @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + s_busy_q <= 1'b0; + s_model_pending_q <= 16'd0; + s_model_addr_q <= '0; + s_scratch_read_pending_q <= 20'd0; + s_scratch_read_addr_q <= '0; + s_scratch_write_pending_q <= 6'd0; + s_scratch_write_addr_q <= '0; + s_scratch_write_data_q <= '0; + s_scratch_write_strb_q <= '0; + s_read_pipe_valid_q <= BankCount'(0); + s_read_pipe_model_q <= '0; + s_read_pipe_scratch_q <= '0; + s_fir_valid_q <= FirEntryCount'(0); + model_data_o <= '0; + scratch_read_data_o <= '0; + s_loader_read_pending_q <= 1'b0; + s_loader_read_bank_q <= 4'd0; + s_loader_read_err_q <= 1'b0; + end else begin + if (scratch_clear_i) s_fir_valid_q <= FirEntryCount'(0); + + if (access_done_o) begin + s_busy_q <= 1'b0; + end else if (access_req_i && access_ready_o) begin + s_busy_q <= 1'b1; + s_model_pending_q <= access_write_i ? 16'd0 : model_read_valid_i; + s_model_addr_q <= model_addr_i; + s_scratch_read_pending_q <= access_write_i ? 20'd0 : scratch_read_valid_i; + s_scratch_read_addr_q <= scratch_read_addr_i; + s_scratch_write_pending_q <= access_write_i ? scratch_write_valid_i : 6'd0; + s_scratch_write_addr_q <= scratch_write_addr_i; + s_scratch_write_data_q <= scratch_write_data_i; + s_scratch_write_strb_q <= scratch_write_strb_i; + end + + for (int bank = 0; bank < BankCount; bank++) begin + if (s_issue_write[bank]) begin + s_scratch_write_pending_q[s_issue_write_lane[bank]] <= 1'b0; + if ((s_scratch_write_addr_q[s_issue_write_lane[bank]] >= + `RETROSOC_APU_KWS__SCRATCH_FIR_BASE) && + (s_scratch_write_addr_q[s_issue_write_lane[bank]] < + (`RETROSOC_APU_KWS__SCRATCH_FIR_BASE + 16'(FirEntryCount * 2)))) begin + s_fir_valid_q[6'((s_scratch_write_addr_q[s_issue_write_lane[bank]] - + `RETROSOC_APU_KWS__SCRATCH_FIR_BASE) >> 1)] <= 1'b1; + end + end + if (s_issue_read[bank]) begin + for (int lane = 0; lane < 16; lane++) begin + if (s_issue_model_mask[bank][lane]) s_model_pending_q[lane] <= 1'b0; + end + for (int lane = 0; lane < 20; lane++) begin + if (s_issue_scratch_mask[bank][lane]) begin + s_scratch_read_pending_q[lane] <= 1'b0; + end + end + end + s_read_pipe_valid_q[bank] <= s_issue_read[bank]; + s_read_pipe_model_q[bank] <= s_issue_model_mask[bank]; + s_read_pipe_scratch_q[bank] <= s_issue_scratch_mask[bank]; + if (s_read_pipe_valid_q[bank]) begin + for (int lane = 0; lane < 16; lane++) begin + if (s_read_pipe_model_q[bank][lane]) begin + model_data_o[lane] <= s_bank_rdata[bank][8*s_model_addr_q[lane][1:0]+:8]; + end + end + for (int lane = 0; lane < 20; lane++) begin + if (s_read_pipe_scratch_q[bank][lane]) begin + if ((s_scratch_read_addr_q[lane] >= `RETROSOC_APU_KWS__SCRATCH_FIR_BASE) && + (s_scratch_read_addr_q[lane] < + (`RETROSOC_APU_KWS__SCRATCH_FIR_BASE + 16'(FirEntryCount * 2))) && + !s_fir_valid_q[6'((s_scratch_read_addr_q[lane] - + `RETROSOC_APU_KWS__SCRATCH_FIR_BASE) >> 1)]) begin + scratch_read_data_o[lane] <= 32'd0; + end else begin + scratch_read_data_o[lane] <= s_bank_rdata[bank]; + end + end + end + end + end + + s_loader_read_pending_q <= 1'b0; + if (req_i && ready_o && s_loader_range_ok && !write_i) begin + s_loader_read_pending_q <= 1'b1; + s_loader_read_bank_q <= s_loader_bank; + s_loader_read_err_q <= 1'b0; + end else if (req_i && ready_o && !s_loader_range_ok) begin + s_loader_read_pending_q <= 1'b1; + s_loader_read_err_q <= 1'b1; + end + end + end endmodule diff --git a/rtl/ip/multimedia/jpeg_table_register_bank.sv b/rtl/ip/multimedia/jpeg_table_register_bank.sv index 11d371b0..5548759c 100644 --- a/rtl/ip/multimedia/jpeg_table_register_bank.sv +++ b/rtl/ip/multimedia/jpeg_table_register_bank.sv @@ -18,62 +18,86 @@ module jpeg_table_register_bank ( output logic [1279:0] ac_length_o // verilog_format: on ); - for (genvar index = 0; index < 64; index++) begin : gen_quant_entry - dffl #( - .DATA_WIDTH(8) - ) u_quant_dffl ( - .clk_i(clk_i), - .en_i (write_i && (entry_kind_i < 4'd4) && (entry_index_i == 8'(index))), - .dat_i(entry_data_i[7:0]), - .dat_o(quant_o[index*8+:8]) - ); - dffl #( - .DATA_WIDTH(25) - ) u_reciprocal_dffl ( - .clk_i(clk_i), - .en_i (write_i && (entry_kind_i < 4'd4) && (entry_index_i == 8'(index))), - .dat_i(entry_reciprocal_i), - .dat_o(reciprocal_o[index*25+:25]) - ); + logic [ 511:0] s_quant_d; + logic [1599:0] s_reciprocal_d; + logic [ 191:0] s_dc_code_d; + logic [ 59:0] s_dc_len_d; + logic [4095:0] s_ac_code_d; + logic [1279:0] s_ac_len_d; + logic s_quant_write, s_dc_write, s_ac_write; + + assign s_quant_write = write_i && (entry_kind_i < 4'd4) && (entry_index_i < 8'd64); + assign s_dc_write = write_i && (entry_kind_i >= 4'd4) && (entry_kind_i < 4'd8) && + (entry_index_i < 8'd12); + assign s_ac_write = write_i && (entry_kind_i >= 4'd8); + + always_comb begin + s_quant_d = quant_o; + s_reciprocal_d = reciprocal_o; + s_dc_code_d = dc_code_o; + s_dc_len_d = dc_length_o; + s_ac_code_d = ac_code_o; + s_ac_len_d = ac_length_o; + if (s_quant_write) begin + s_quant_d[entry_index_i*8+:8] = entry_data_i[7:0]; + s_reciprocal_d[entry_index_i*25+:25] = entry_reciprocal_i; + end + if (s_dc_write) begin + s_dc_code_d[entry_index_i*16+:16] = entry_data_i[15:0]; + s_dc_len_d[entry_index_i*5+:5] = entry_data_i[20:16]; + end + if (s_ac_write) begin + s_ac_code_d[entry_index_i*16+:16] = entry_data_i[15:0]; + s_ac_len_d[entry_index_i*5+:5] = entry_data_i[20:16]; + end end - for (genvar index = 0; index < 12; index++) begin : gen_dc_entry - dffl #( - .DATA_WIDTH(16) - ) u_code_dffl ( - .clk_i(clk_i), - .en_i (write_i && (entry_kind_i >= 4'd4) && (entry_kind_i < 4'd8) && - (entry_index_i == 8'(index))), - .dat_i(entry_data_i[15:0]), - .dat_o(dc_code_o[index*16+:16]) - ); - dffl #( - .DATA_WIDTH(5) - ) u_length_dffl ( - .clk_i(clk_i), - .en_i (write_i && (entry_kind_i >= 4'd4) && (entry_kind_i < 4'd8) && - (entry_index_i == 8'(index))), - .dat_i(entry_data_i[20:16]), - .dat_o(dc_length_o[index*5+:5]) - ); - end - - for (genvar index = 0; index < 256; index++) begin : gen_ac_entry - dffl #( - .DATA_WIDTH(16) - ) u_code_dffl ( - .clk_i(clk_i), - .en_i (write_i && (entry_kind_i >= 4'd8) && (entry_index_i == 8'(index))), - .dat_i(entry_data_i[15:0]), - .dat_o(ac_code_o[index*16+:16]) - ); - dffl #( - .DATA_WIDTH(5) - ) u_length_dffl ( - .clk_i(clk_i), - .en_i (write_i && (entry_kind_i >= 4'd8) && (entry_index_i == 8'(index))), - .dat_i(entry_data_i[20:16]), - .dat_o(ac_length_o[index*5+:5]) - ); - end + dffl #( + .DATA_WIDTH(512) + ) u_quant_dffl ( + .clk_i(clk_i), + .en_i (s_quant_write), + .dat_i(s_quant_d), + .dat_o(quant_o) + ); + dffl #( + .DATA_WIDTH(1600) + ) u_reciprocal_dffl ( + .clk_i(clk_i), + .en_i (s_quant_write), + .dat_i(s_reciprocal_d), + .dat_o(reciprocal_o) + ); + dffl #( + .DATA_WIDTH(192) + ) u_dc_code_dffl ( + .clk_i(clk_i), + .en_i (s_dc_write), + .dat_i(s_dc_code_d), + .dat_o(dc_code_o) + ); + dffl #( + .DATA_WIDTH(60) + ) u_dc_length_dffl ( + .clk_i(clk_i), + .en_i (s_dc_write), + .dat_i(s_dc_len_d), + .dat_o(dc_length_o) + ); + dffl #( + .DATA_WIDTH(4096) + ) u_ac_code_dffl ( + .clk_i(clk_i), + .en_i (s_ac_write), + .dat_i(s_ac_code_d), + .dat_o(ac_code_o) + ); + dffl #( + .DATA_WIDTH(1280) + ) u_ac_length_dffl ( + .clk_i(clk_i), + .en_i (s_ac_write), + .dat_i(s_ac_len_d), + .dat_o(ac_length_o) + ); endmodule diff --git a/scripts/run_flow.py b/scripts/run_flow.py index 73686962..a2716a6d 100644 --- a/scripts/run_flow.py +++ b/scripts/run_flow.py @@ -6,6 +6,8 @@ import argparse import json import os import re +import resource +import signal import subprocess import sys import termios @@ -75,6 +77,17 @@ def write_console_bytes(chunk: bytes) -> None: sys.stdout.buffer.flush() +def terminate_process_group(process: subprocess.Popen[str] | subprocess.Popen[bytes]) -> None: + if process.poll() is not None: + return + os.killpg(process.pid, signal.SIGTERM) + try: + process.wait(timeout=5) + except subprocess.TimeoutExpired: + os.killpg(process.pid, signal.SIGKILL) + process.wait() + + def main() -> int: parser = argparse.ArgumentParser(description="Run a flow command with structured results") parser.add_argument("--tool", required=True) @@ -111,6 +124,28 @@ def main() -> int: args.result.parent.mkdir(parents=True, exist_ok=True) started = datetime.now(timezone.utc) start_clock = time.monotonic() + atomic_write( + args.result, + json.dumps( + { + "schema_version": 1, + "tool": args.tool, + "command": command, + "started_at": started.isoformat(), + "status": "running", + "log": str(args.log.resolve()), + }, + indent=2, + sort_keys=True, + ) + + "\n", + ) + + def interrupt_flow(_signum: int, _frame: object) -> None: + raise KeyboardInterrupt + + signal.signal(signal.SIGINT, interrupt_flow) + signal.signal(signal.SIGTERM, interrupt_flow) returncode = 127 error_message = None process: subprocess.Popen[str] | subprocess.Popen[bytes] | None = None @@ -132,6 +167,7 @@ def main() -> int: errors=None if args.stream_bytes else "replace", bufsize=0 if args.stream_bytes else 1, pass_fds=jobserver_fds(), + start_new_session=True, ) assert process.stdout is not None if args.stream_bytes: @@ -150,25 +186,15 @@ def main() -> int: write_console_output(line) if args.terminate_on_success_marker and args.success_marker in line: marker_seen = True - process.terminate() - try: - process.wait(timeout=5) - except subprocess.TimeoutExpired: - process.kill() - process.wait() + terminate_process_group(process) returncode = 1 if rejected_matches else 0 break if not marker_seen: returncode = process.wait() except KeyboardInterrupt: error_message = "interrupted" - if process is not None and process.poll() is None: - process.terminate() - try: - process.wait(timeout=5) - except subprocess.TimeoutExpired: - process.kill() - process.wait() + if process is not None: + terminate_process_group(process) returncode = 130 except (OSError, subprocess.SubprocessError) as error: error_message = str(error) @@ -182,6 +208,7 @@ def main() -> int: "started_at": started.isoformat(), "finished_at": finished.isoformat(), "duration_seconds": round(time.monotonic() - start_clock, 3), + "peak_rss_kib": resource.getrusage(resource.RUSAGE_CHILDREN).ru_maxrss, "exit_code": returncode, "status": "passed" if returncode == 0 else "failed", "log": str(args.log.resolve()), diff --git a/tests/rtl/apu_kws_engine_tb.sv b/tests/rtl/apu_kws_engine_tb.sv index 7519a09b..a85ee8ef 100644 --- a/tests/rtl/apu_kws_engine_tb.sv +++ b/tests/rtl/apu_kws_engine_tb.sv @@ -40,7 +40,10 @@ module apu_kws_engine_tb; logic [31:0] fault_addr_o, fault_detail_o; logic [15:0][14:0] model_addr_o; logic [15:0][7:0] model_data_i, model_store_data; + logic storage_req, storage_ready, storage_done, storage_progress, storage_write; + logic [15:0] model_read_valid; logic scratch_clear; + logic [19:0] scratch_read_valid; logic [19:0][15:0] scratch_read_addr; logic [19:0][31:0] scratch_read_data; logic [ 5:0] scratch_write_valid; @@ -138,9 +141,16 @@ module apu_kws_engine_tb; .data_o (), .valid_o (), .access_err_o (), + .access_req_i (storage_req), + .access_ready_o (storage_ready), + .access_done_o (storage_done), + .access_progress_o (storage_progress), + .access_write_i (storage_write), + .model_read_valid_i (model_read_valid), .model_addr_i (model_addr_o), .model_data_o (model_store_data), .scratch_clear_i (scratch_clear), + .scratch_read_valid_i (scratch_read_valid), .scratch_read_addr_i (scratch_read_addr), .scratch_read_data_o (scratch_read_data), .scratch_write_valid_i(scratch_write_valid), @@ -195,9 +205,16 @@ module apu_kws_engine_tb; .memory_error_detail_o (memory_error_detail_o), .memory_input_used_o (memory_input_used_o), .input_config_i (32'h01043e80), + .storage_req_o (storage_req), + .storage_ready_i (storage_ready), + .storage_done_i (storage_done), + .storage_progress_i (storage_progress), + .storage_write_o (storage_write), + .model_read_valid_o (model_read_valid), .model_addr_o (model_addr_o), .model_data_i (model_data_i), .scratch_clear_o (scratch_clear), + .scratch_read_valid_o (scratch_read_valid), .scratch_read_addr_o (scratch_read_addr), .scratch_read_data_i (scratch_read_data), .scratch_write_valid_o (scratch_write_valid), @@ -474,15 +491,22 @@ module apu_kws_engine_tb; enable_i = 1'b1; accepted_samples = 0; elapsed_cycles = 0; - while ((accepted_samples < 16000) && (elapsed_cycles < 2000000)) begin + while ((accepted_samples < 16000) && (elapsed_cycles < 4000000)) begin @(posedge clk_i); if (stream_i.tvalid && stream_i.tready) accepted_samples++; elapsed_cycles++; end - if (accepted_samples != 16000) $fatal(1, "KWS did not consume exactly one diagnostic window"); + if (accepted_samples != 16000) begin + $fatal( + 1, + "KWS input timeout accepted=%0d front=%0d infer=%0d wait=%0b req=%0b ready=%0b done=%0b", + accepted_samples, dut.s_front_state_q, dut.s_infer_state_q, dut.s_storage_wait_q, + storage_req, storage_ready, storage_done); + end @(negedge clk_i); - enable_i = 1'b0; - while ((inference_count_o == 0) && (elapsed_cycles < 3000000)) begin + enable_i = 1'b0; + elapsed_cycles = 0; + while ((inference_count_o == 0) && (elapsed_cycles < 4000000)) begin @(negedge clk_i); elapsed_cycles++; end diff --git a/tests/rtl/apu_kws_sram_client_tb.sv b/tests/rtl/apu_kws_sram_client_tb.sv index 62bd6be4..a16f5963 100644 --- a/tests/rtl/apu_kws_sram_client_tb.sv +++ b/tests/rtl/apu_kws_sram_client_tb.sv @@ -9,10 +9,15 @@ module apu_kws_sram_client_tb; logic [31:0] data_i = 32'd0; logic [ 3:0] strb_i = 4'd0; logic ready_o, valid_o, access_err_o; - logic [31:0] data_o; + logic [31:0] data_o; + logic access_req_i = 1'b0; + logic access_ready_o, access_done_o, access_progress_o; + logic access_write_i = 1'b0; + logic [15:0] model_read_valid_i = 16'd0; logic [15:0][14:0] model_addr_i = '0; logic [15:0][ 7:0] model_data_o; logic scratch_clear_i = 1'b0; + logic [19:0] scratch_read_valid_i = 20'd0; logic [19:0][15:0] scratch_read_addr_i = '0; logic [19:0][31:0] scratch_read_data_o; logic [ 5:0] scratch_write_valid_i = 6'd0; @@ -39,9 +44,16 @@ module apu_kws_sram_client_tb; .data_o (data_o), .valid_o (valid_o), .access_err_o (access_err_o), + .access_req_i (access_req_i), + .access_ready_o (access_ready_o), + .access_done_o (access_done_o), + .access_progress_o (access_progress_o), + .access_write_i (access_write_i), + .model_read_valid_i (model_read_valid_i), .model_addr_i (model_addr_i), .model_data_o (model_data_o), .scratch_clear_i (scratch_clear_i), + .scratch_read_valid_i (scratch_read_valid_i), .scratch_read_addr_i (scratch_read_addr_i), .scratch_read_data_o (scratch_read_data_o), .scratch_write_valid_i(scratch_write_valid_i), @@ -88,6 +100,20 @@ module apu_kws_sram_client_tb; end endtask + task automatic run_access(input logic write_access_i); + begin + @(negedge clk_i); + access_write_i = write_access_i; + access_req_i = 1'b1; + if (!access_ready_o) $fatal(1, "KWS SRAM transaction was not accepted"); + @(negedge clk_i); + access_req_i = 1'b0; + wait (access_done_o); + @(negedge clk_i); + access_write_i = 1'b0; + end + endtask + task automatic write_scratch(input logic [15:0] address_i, input logic [31:0] value_i, input logic [3:0] strobe_i); begin @@ -96,11 +122,13 @@ module apu_kws_sram_client_tb; scratch_write_addr_i[0] = address_i; scratch_write_data_i[0] = value_i; scratch_write_strb_i[0] = strobe_i; - @(negedge clk_i); + run_access(1'b1); scratch_write_valid_i[0] = 1'b0; scratch_write_strb_i[0] = 4'd0; scratch_read_addr_i[0] = address_i; - #1; + scratch_read_valid_i[0] = 1'b1; + run_access(1'b0); + scratch_read_valid_i[0] = 1'b0; if (scratch_read_data_o[0] != value_i) begin $fatal(1, "scratch layout write mismatch at %04x", address_i); end @@ -126,25 +154,29 @@ module apu_kws_sram_client_tb; rst_n_i = 1'b1; write_model(`APB4_APU__LOCAL_KWS_BASE, 32'h4433_2211); - model_addr_i[0] = 15'd0; - model_addr_i[1] = 15'd1; - model_addr_i[2] = 15'd2; - model_addr_i[3] = 15'd3; - #1; + model_addr_i[0] = 15'd0; + model_addr_i[1] = 15'd1; + model_addr_i[2] = 15'd2; + model_addr_i[3] = 15'd3; + model_read_valid_i[3:0] = 4'hf; + run_access(1'b0); + model_read_valid_i = 16'd0; if ({model_data_o[3], model_data_o[2], model_data_o[1], model_data_o[0]} != 32'h4433_2211) begin $fatal(1, "banks10..17 model readback mismatch"); end write_model(17'h1_0000, 32'h8877_6655); write_model(`APB4_APU__LOCAL_KWS_BASE + `RETROSOC_APU_KWS__MODEL_BYTES - 4, 32'hccbb_aa99); - model_addr_i[0] = 15'h6000; - model_addr_i[1] = 15'h6001; - model_addr_i[2] = 15'h6002; - model_addr_i[3] = 15'h6003; - model_addr_i[4] = 15'h7ffc; - model_addr_i[5] = 15'h7ffd; - model_addr_i[6] = 15'h7ffe; - model_addr_i[7] = 15'h7fff; - #1; + model_addr_i[0] = 15'h6000; + model_addr_i[1] = 15'h6001; + model_addr_i[2] = 15'h6002; + model_addr_i[3] = 15'h6003; + model_addr_i[4] = 15'h7ffc; + model_addr_i[5] = 15'h7ffd; + model_addr_i[6] = 15'h7ffe; + model_addr_i[7] = 15'h7fff; + model_read_valid_i[7:0] = 8'hff; + run_access(1'b0); + model_read_valid_i = 16'd0; if ({model_data_o[3], model_data_o[2], model_data_o[1], model_data_o[0]} != 32'h8877_6655) begin $fatal(1, "APUM model address crossing 0x10000 did not map to bank16"); end @@ -161,8 +193,10 @@ module apu_kws_sram_client_tb; write_scratch(`RETROSOC_APU_KWS__SCRATCH_PEAK_BASE, 32'h6162_6364, 4'hf); write_scratch(16'h7ffc, 32'h7172_7374, 4'hf); - model_addr_i[0] = 15'd0; - #1; + model_addr_i[0] = 15'd0; + model_read_valid_i[0] = 1'b1; + run_access(1'b0); + model_read_valid_i = 16'd0; if (model_data_o[0] != 8'h11) $fatal(1, "scratch write corrupted APUM storage"); @(negedge clk_i); @@ -185,12 +219,16 @@ module apu_kws_sram_client_tb; @(negedge clk_i); scratch_clear_i = 1'b1; @(negedge clk_i); - scratch_clear_i = 1'b0; - scratch_read_addr_i[0] = `RETROSOC_APU_KWS__SCRATCH_FIR_BASE; - #1; + scratch_clear_i = 1'b0; + scratch_read_addr_i[0] = `RETROSOC_APU_KWS__SCRATCH_FIR_BASE; + scratch_read_valid_i[0] = 1'b1; + run_access(1'b0); + scratch_read_valid_i[0] = 1'b0; if (scratch_read_data_o[0] != 32'd0) $fatal(1, "FIR lifecycle clear exposed stale data"); - scratch_read_addr_i[0] = `RETROSOC_APU_KWS__SCRATCH_A_BASE; - #1; + scratch_read_addr_i[0] = `RETROSOC_APU_KWS__SCRATCH_A_BASE; + scratch_read_valid_i[0] = 1'b1; + run_access(1'b0); + scratch_read_valid_i[0] = 1'b0; if (scratch_read_data_o[0] != 32'h0102_0304) begin $fatal(1, "history clear corrupted non-history scratch"); end @@ -203,5 +241,5 @@ module apu_kws_sram_client_tb; end logic s_unused; - assign s_unused = valid_o ^ ^data_o ^ codec_valid_o ^ ^codec_data_o; + assign s_unused = valid_o ^ ^data_o ^ codec_valid_o ^ ^codec_data_o ^ access_progress_o; endmodule diff --git a/tests/rtl/apu_p7_accuracy_tb.sv b/tests/rtl/apu_p7_accuracy_tb.sv index 0bd6bdaa..9754e5be 100644 --- a/tests/rtl/apu_p7_accuracy_tb.sv +++ b/tests/rtl/apu_p7_accuracy_tb.sv @@ -46,7 +46,10 @@ module apu_p7_accuracy_tb; logic [31:0] fault_addr_o, fault_detail_o; logic [15:0][14:0] model_addr_o; logic [15:0][ 7:0] model_store_data; + logic storage_req, storage_ready, storage_done, storage_progress, storage_write; + logic [15:0] model_read_valid; logic scratch_clear; + logic [19:0] scratch_read_valid; logic [19:0][15:0] scratch_read_addr; logic [19:0][31:0] scratch_read_data; logic [ 5:0] scratch_write_valid; @@ -86,9 +89,16 @@ module apu_p7_accuracy_tb; .data_o (), .valid_o (), .access_err_o (), + .access_req_i (storage_req), + .access_ready_o (storage_ready), + .access_done_o (storage_done), + .access_progress_o (storage_progress), + .access_write_i (storage_write), + .model_read_valid_i (model_read_valid), .model_addr_i (model_addr_o), .model_data_o (model_store_data), .scratch_clear_i (scratch_clear), + .scratch_read_valid_i (scratch_read_valid), .scratch_read_addr_i (scratch_read_addr), .scratch_read_data_o (scratch_read_data), .scratch_write_valid_i(scratch_write_valid), @@ -143,9 +153,16 @@ module apu_p7_accuracy_tb; .memory_input_used_o (memory_input_used_o), .kws_config_i (KwsConfig), .input_config_i (InputConfig), + .storage_req_o (storage_req), + .storage_ready_i (storage_ready), + .storage_done_i (storage_done), + .storage_progress_i (storage_progress), + .storage_write_o (storage_write), + .model_read_valid_o (model_read_valid), .model_addr_o (model_addr_o), .model_data_i (model_store_data), .scratch_clear_o (scratch_clear), + .scratch_read_valid_o (scratch_read_valid), .scratch_read_addr_o (scratch_read_addr), .scratch_read_data_i (scratch_read_data), .scratch_write_valid_o (scratch_write_valid), diff --git a/tests/rtl/apu_p7_frontend_dump_tb.sv b/tests/rtl/apu_p7_frontend_dump_tb.sv index 9ce1ca7b..8e185d5b 100644 --- a/tests/rtl/apu_p7_frontend_dump_tb.sv +++ b/tests/rtl/apu_p7_frontend_dump_tb.sv @@ -49,7 +49,10 @@ module apu_p7_frontend_dump_tb; logic [31:0] fault_addr_o, fault_detail_o; logic [15:0][14:0] model_addr_o; logic [15:0][ 7:0] model_store_data; + logic storage_req, storage_ready, storage_done, storage_progress, storage_write; + logic [15:0] model_read_valid; logic scratch_clear; + logic [19:0] scratch_read_valid; logic [19:0][15:0] scratch_read_addr; logic [19:0][31:0] scratch_read_data; logic [ 5:0] scratch_write_valid; @@ -89,9 +92,16 @@ module apu_p7_frontend_dump_tb; .data_o (), .valid_o (), .access_err_o (), + .access_req_i (storage_req), + .access_ready_o (storage_ready), + .access_done_o (storage_done), + .access_progress_o (storage_progress), + .access_write_i (storage_write), + .model_read_valid_i (model_read_valid), .model_addr_i (model_addr_o), .model_data_o (model_store_data), .scratch_clear_i (scratch_clear), + .scratch_read_valid_i (scratch_read_valid), .scratch_read_addr_i (scratch_read_addr), .scratch_read_data_o (scratch_read_data), .scratch_write_valid_i(scratch_write_valid), @@ -146,9 +156,16 @@ module apu_p7_frontend_dump_tb; .memory_input_used_o (memory_input_used_o), .kws_config_i (KwsConfig), .input_config_i (InputConfig), + .storage_req_o (storage_req), + .storage_ready_i (storage_ready), + .storage_done_i (storage_done), + .storage_progress_i (storage_progress), + .storage_write_o (storage_write), + .model_read_valid_o (model_read_valid), .model_addr_o (model_addr_o), .model_data_i (model_store_data), .scratch_clear_o (scratch_clear), + .scratch_read_valid_o (scratch_read_valid), .scratch_read_addr_o (scratch_read_addr), .scratch_read_data_i (scratch_read_data), .scratch_write_valid_o (scratch_write_valid), diff --git a/tests/test_script_tools.py b/tests/test_script_tools.py index 66784d91..48f5b24a 100644 --- a/tests/test_script_tools.py +++ b/tests/test_script_tools.py @@ -1898,6 +1898,8 @@ def test_run_flow_writes_structured_result(tmp_path: Path) -> None: assert data["status"] == "passed" assert data["exit_code"] == 0 assert data["duration_seconds"] >= 0 + assert isinstance(data["peak_rss_kib"], int) + assert data["peak_rss_kib"] > 0 assert log.read_text(encoding="utf-8") == "flow output\n" diff --git a/tests/test_yosys_synth_recipes.py b/tests/test_yosys_synth_recipes.py index c3ac3e81..b36dfb49 100644 --- a/tests/test_yosys_synth_recipes.py +++ b/tests/test_yosys_synth_recipes.py @@ -55,7 +55,13 @@ def test_yosys_recipe_scripts_and_public_slot_are_stable() -> None: assert "SYNTH_RECIPE" not in makefile.split("CONFIG_KEY_VARS", 1)[1].split("VARIANT_ID", 1)[0] assert "--env SYNTH_RECIPE=$(SYNTH_RECIPE)" in yosys_mk assert "--env YOSYS_TARGET_PERIOD_PS=$(YOSYS_TARGET_PERIOD_PS)" in yosys_mk + assert "YOSYS_TIMEOUT ?= 10800" in yosys_mk + assert "timeout --foreground --kill-after=5s $(YOSYS_TIMEOUT)s" in yosys_mk + assert "yosys -t --perffile $(YOSYS_BUILD)/yosys-perf.json" in yosys_mk assert "abc_${synth_recipe}.script" in synth_tcl + assert '${proj_name}_pre_memory.rpt' in synth_tcl + assert '${proj_name}_pre_memory.json' in synth_tcl + assert synth_tcl.index('${proj_name}_pre_memory.json') < synth_tcl.index("yosys memory") assert "yosys abc {*}$tech_cells_args -D $period_ps -script $abc_script -constr $abc_constr" in synth_tcl assert "S110" not in synth_tcl assert "$(SYN_BUILD_ROOT)/out/retrosoc_asic_yosys.v" in opensta