mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
feat: update APU tests to synchronize with P4 ISA and add APU primitives filelist checks
- Modified test_apu_register_parity.py to reflect changes in P4 ISA definitions. - Updated test_apu_register_parity.py to include new manifest entries for P4. - Enhanced test_script_tools.py to include checks for APU primitives filelists. - Added assertions for APU primitives in the formal filelists test to ensure proper scoping and inclusion.
This commit is contained in:
38 files changed
+8742
-434
No files matched your search
@@ -287,6 +287,15 @@
|
||||
#define RS_APU_ABI_APUMC_ENTRY_WORDS 8U
|
||||
#define RS_APU_ABI_APUMC_ENTRY_COUNT 3U
|
||||
#define RS_APU_ABI_APUMC_MAX_INSTRUCTIONS 2048U
|
||||
#define RS_APU_ABI_APUMC_P3_PRIMITIVE_MASK UINT32_C(0x00000000)
|
||||
#define RS_APU_ABI_APUMC_P4_PRIMITIVE_MASK UINT32_C(0x0000FFFF)
|
||||
#define RS_APU_ABI_LOCAL_DATA_BYTES 114688U
|
||||
#define RS_APU_ABI_LOCAL_CODEC_BYTES 40960U
|
||||
#define RS_APU_ABI_LOCAL_TABLE_SCRATCH_BYTES 24576U
|
||||
#define RS_APU_ABI_LOCAL_INPUT_BASE 24576U
|
||||
#define RS_APU_ABI_LOCAL_OUTPUT_BASE 32768U
|
||||
#define RS_APU_ABI_LOCAL_KWS_BASE 40960U
|
||||
#define RS_APU_ABI_LOCAL_INTERNAL_BASE 106496U
|
||||
#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
|
||||
@@ -439,8 +448,8 @@
|
||||
#define RS_APU_IP_ID_VALUE UINT32_C(0x41505530)
|
||||
#define RS_APU_IP_VERSION_VALUE UINT32_C(0x00010000)
|
||||
#define RS_APU_IP_VERSION_MAJOR_MASK UINT32_C(0xFFFF0000)
|
||||
#define RS_APU_CAPABILITY0_IMPLEMENTED UINT32_C(0x00000098)
|
||||
#define RS_APU_CAPABILITY1_IMPLEMENTED UINT32_C(0x00000010)
|
||||
#define RS_APU_CAPABILITY0_IMPLEMENTED UINT32_C(0x00000198)
|
||||
#define RS_APU_CAPABILITY1_IMPLEMENTED UINT32_C(0x01827010)
|
||||
#define RS_APU_DIGEST_IMPLEMENTED UINT32_C(0x00000000)
|
||||
#define RS_APU_IRQ_ALL UINT32_C(0x000007FF)
|
||||
|
||||
|
||||
+4
-3
@@ -119,9 +119,10 @@ ownership. Its implemented limits, handwritten ABI, measured performance, and
|
||||
commercial release gates are documented in
|
||||
[Baseline JPEG Codec](../docs/ip/jpeg.md).
|
||||
|
||||
The Mini Audio Processing Unit has a frozen coreless architecture. APU-P2 adds
|
||||
the private AXI4 DMA, descriptor scheduler, fair Gateway A arbitration, and
|
||||
production stream router/FIFOs to the fail-closed APB4 shell at `APB4_APU`.
|
||||
The Mini Audio Processing Unit has a frozen coreless architecture. APU-P4 adds
|
||||
the private AXI4 DMA and scheduler, microcode loader/sequencer, 112 KiB local
|
||||
store, primitive FIFOs, and class-2 through class-5 bitstream, entropy, local,
|
||||
and fixed-point DSP engines to the fail-closed APB4 shell at `APB4_APU`.
|
||||
Resource Controller index 7 and exclusive LP IRQ31/HP PLIC source10 routing
|
||||
remain fixed. Public jobs and APU stream routes remain disabled until their
|
||||
later-phase engines exist. The complete ABI, phase order, and evidence gates
|
||||
|
||||
+253
-105
@@ -47,11 +47,15 @@ module apb4_apu (
|
||||
logic [2:0][10:0] s_mc_entry_pc, s_mc_entry_first, s_mc_entry_last;
|
||||
logic [2:0][15:0] s_mc_entry_max_loop;
|
||||
logic [2:0][23:0] s_mc_entry_max_retired;
|
||||
logic [2:0][16:0] s_mc_entry_scratch_base, s_mc_entry_scratch_bytes;
|
||||
logic [2:0][31:0] s_mc_entry_primitive_mask;
|
||||
logic [2:0][15:0] s_mc_entry_table_offset, s_mc_entry_table_bytes;
|
||||
logic [15:0] s_mc_table_bytes;
|
||||
logic s_store_active, s_store_read, s_store_write, s_store_valid;
|
||||
logic [10:0] s_store_addr;
|
||||
logic [63:0] s_store_write_data, s_store_read_data;
|
||||
logic s_seq_fetch, s_seq_fetch_valid, s_seq_trapped, s_seq_trap_event;
|
||||
logic s_seq_abort_done, s_seq_fault_valid, s_seq_idle;
|
||||
logic s_seq_abort_done, s_seq_fault_valid, s_seq_launch_epoch, s_seq_idle;
|
||||
logic [10:0] s_seq_fetch_addr;
|
||||
logic [63:0] s_seq_fetch_data, s_seq_perf_retired;
|
||||
logic [31:0] s_seq_status, s_seq_retired;
|
||||
@@ -61,6 +65,35 @@ module apb4_apu (
|
||||
logic [ 1:0] s_seq_fault_resp;
|
||||
logic [ 7:0] s_seq_fault_index;
|
||||
logic [31:0] s_seq_fault_addr, s_seq_fault_detail;
|
||||
logic s_primitive_req_valid, s_primitive_req_ready, s_primitive_result_valid;
|
||||
logic [63:0] s_primitive_instruction;
|
||||
logic [31:0] s_primitive_source0, s_primitive_source1, s_primitive_destination;
|
||||
logic [3:0] s_primitive_result_dst;
|
||||
logic [3:0][31:0] s_primitive_result_data;
|
||||
logic [2:0] s_primitive_result_words;
|
||||
logic s_primitive_result_kernel, s_primitive_err, s_primitive_kernel_done;
|
||||
logic [5:0] s_primitive_err_code;
|
||||
logic [3:0] s_primitive_err_stage;
|
||||
logic [7:0] s_primitive_err_reason;
|
||||
logic [16:0] s_active_scratch_base, s_active_scratch_bytes;
|
||||
logic [31:0] s_active_primitive_mask;
|
||||
logic [15:0] s_active_table_offset, s_active_table_bytes;
|
||||
logic s_primitive_busy, s_primitive_input_exhausted;
|
||||
logic s_primitive_input_ready, s_primitive_output_ready;
|
||||
logic s_primitive_input_accept_unused, s_primitive_output_valid_unused;
|
||||
logic [6:0] s_primitive_input_count_unused, s_primitive_output_count_unused;
|
||||
logic s_primitive_kernel_busy_unused;
|
||||
logic [31:0] s_primitive_cycles_unused;
|
||||
logic [40:0] s_primitive_output_data_unused;
|
||||
logic s_local_loader_write, s_local_loader_ready;
|
||||
logic [16:0] s_local_loader_addr;
|
||||
logic [31:0] s_local_loader_data;
|
||||
logic [ 3:0] s_local_loader_strb;
|
||||
logic s_local_codec_req, s_local_codec_write, s_local_codec_ready;
|
||||
logic [16:0] s_local_codec_addr;
|
||||
logic [31:0] s_local_codec_write_data, s_local_codec_read_data;
|
||||
logic [3:0] s_local_codec_strb;
|
||||
logic s_local_codec_valid, s_local_codec_err;
|
||||
logic s_input_watermark_evt, s_output_watermark_evt, s_xrun_evt;
|
||||
logic s_ring_event, s_ring_err, s_abort_done;
|
||||
logic [ 5:0] s_ring_err_code;
|
||||
@@ -164,7 +197,7 @@ 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_transport_idle = s_scheduler_idle && !s_dma_busy && s_stream_idle && s_mc_idle &&
|
||||
s_seq_idle;
|
||||
s_seq_idle && !s_primitive_busy;
|
||||
assign idle_o = s_transport_idle;
|
||||
assign s_resource_reset_request = s_resource_reset_pending_q ||
|
||||
(resource_reset_i && !s_resource_reset_seen_q);
|
||||
@@ -323,59 +356,71 @@ module apb4_apu (
|
||||
.axi4 (axi4)
|
||||
);
|
||||
|
||||
apu_microcode_loader u_microcode_loader (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.start_i (s_microcode_load),
|
||||
.abort_i (s_abort),
|
||||
.resource_reset_i (s_resource_reset_request),
|
||||
.soft_reset_i (s_soft_reset),
|
||||
.counter_clear_i (s_cnt_clear),
|
||||
.image_addr_i (s_mc_image_addr),
|
||||
.image_size_i (s_mc_image_size),
|
||||
.expected_crc_i (s_mc_expected_crc),
|
||||
.dma_request_valid_o(s_mc_dma_req_valid),
|
||||
.dma_request_ready_i(s_mc_dma_req_ready),
|
||||
.dma_request_addr_o (s_mc_dma_req_addr),
|
||||
.dma_request_bytes_o(s_mc_dma_req_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_mc_idle),
|
||||
.dma_ready_o (s_mc_dma_ready),
|
||||
.dma_done_i (s_dma_done),
|
||||
.dma_err_i (s_dma_err),
|
||||
.dma_err_code_i (s_dma_err_code),
|
||||
.dma_err_stage_i (s_dma_err_stage),
|
||||
.dma_err_resp_i (s_dma_err_resp),
|
||||
.dma_err_addr_i (s_dma_err_addr),
|
||||
.store_active_o (s_store_active),
|
||||
.store_read_o (s_store_read),
|
||||
.store_write_o (s_store_write),
|
||||
.store_addr_o (s_store_addr),
|
||||
.store_data_o (s_store_write_data),
|
||||
.store_data_i (s_store_read_data),
|
||||
.store_valid_i (s_store_valid),
|
||||
.stat_o (s_mc_status),
|
||||
.abi_o (s_mc_abi),
|
||||
.build_id_o (s_mc_build_id),
|
||||
.lock_o (s_mc_lock),
|
||||
.actual_crc_o (s_mc_actual_crc),
|
||||
.load_count_o (s_mc_load_count),
|
||||
.entry_pc_o (s_mc_entry_pc),
|
||||
.entry_first_o (s_mc_entry_first),
|
||||
.entry_last_o (s_mc_entry_last),
|
||||
.entry_max_loop_o (s_mc_entry_max_loop),
|
||||
.entry_max_retired_o(s_mc_entry_max_retired),
|
||||
.load_done_o (s_mc_load_done),
|
||||
.abort_done_o (s_mc_abort_done),
|
||||
.fault_valid_o (s_mc_fault_valid),
|
||||
.fault_code_o (s_mc_fault_code),
|
||||
.fault_stage_o (s_mc_fault_stage),
|
||||
.fault_resp_o (s_mc_fault_resp),
|
||||
.fault_addr_o (s_mc_fault_addr),
|
||||
.fault_detail_o (s_mc_fault_detail),
|
||||
.idle_o (s_mc_idle)
|
||||
apu_microcode_loader #(
|
||||
.EnableP4(1'b1)
|
||||
) u_microcode_loader (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.start_i (s_microcode_load),
|
||||
.abort_i (s_abort),
|
||||
.resource_reset_i (s_resource_reset_request),
|
||||
.soft_reset_i (s_soft_reset),
|
||||
.counter_clear_i (s_cnt_clear),
|
||||
.image_addr_i (s_mc_image_addr),
|
||||
.image_size_i (s_mc_image_size),
|
||||
.expected_crc_i (s_mc_expected_crc),
|
||||
.dma_request_valid_o (s_mc_dma_req_valid),
|
||||
.dma_request_ready_i (s_mc_dma_req_ready),
|
||||
.dma_request_addr_o (s_mc_dma_req_addr),
|
||||
.dma_request_bytes_o (s_mc_dma_req_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_mc_idle),
|
||||
.dma_ready_o (s_mc_dma_ready),
|
||||
.dma_done_i (s_dma_done),
|
||||
.dma_err_i (s_dma_err),
|
||||
.dma_err_code_i (s_dma_err_code),
|
||||
.dma_err_stage_i (s_dma_err_stage),
|
||||
.dma_err_resp_i (s_dma_err_resp),
|
||||
.dma_err_addr_i (s_dma_err_addr),
|
||||
.store_active_o (s_store_active),
|
||||
.store_read_o (s_store_read),
|
||||
.store_write_o (s_store_write),
|
||||
.store_addr_o (s_store_addr),
|
||||
.store_data_o (s_store_write_data),
|
||||
.store_data_i (s_store_read_data),
|
||||
.store_valid_i (s_store_valid),
|
||||
.local_write_o (s_local_loader_write),
|
||||
.local_addr_o (s_local_loader_addr),
|
||||
.local_data_o (s_local_loader_data),
|
||||
.local_strb_o (s_local_loader_strb),
|
||||
.table_bytes_o (s_mc_table_bytes),
|
||||
.stat_o (s_mc_status),
|
||||
.abi_o (s_mc_abi),
|
||||
.build_id_o (s_mc_build_id),
|
||||
.lock_o (s_mc_lock),
|
||||
.actual_crc_o (s_mc_actual_crc),
|
||||
.load_count_o (s_mc_load_count),
|
||||
.entry_pc_o (s_mc_entry_pc),
|
||||
.entry_first_o (s_mc_entry_first),
|
||||
.entry_last_o (s_mc_entry_last),
|
||||
.entry_max_loop_o (s_mc_entry_max_loop),
|
||||
.entry_max_retired_o (s_mc_entry_max_retired),
|
||||
.entry_scratch_base_o (s_mc_entry_scratch_base),
|
||||
.entry_scratch_bytes_o (s_mc_entry_scratch_bytes),
|
||||
.entry_primitive_mask_o(s_mc_entry_primitive_mask),
|
||||
.entry_table_offset_o (s_mc_entry_table_offset),
|
||||
.entry_table_bytes_o (s_mc_entry_table_bytes),
|
||||
.load_done_o (s_mc_load_done),
|
||||
.abort_done_o (s_mc_abort_done),
|
||||
.fault_valid_o (s_mc_fault_valid),
|
||||
.fault_code_o (s_mc_fault_code),
|
||||
.fault_stage_o (s_mc_fault_stage),
|
||||
.fault_resp_o (s_mc_fault_resp),
|
||||
.fault_addr_o (s_mc_fault_addr),
|
||||
.fault_detail_o (s_mc_fault_detail),
|
||||
.idle_o (s_mc_idle)
|
||||
);
|
||||
|
||||
apu_control_store u_control_store (
|
||||
@@ -395,54 +440,153 @@ module apb4_apu (
|
||||
.fetch_valid_o (s_seq_fetch_valid)
|
||||
);
|
||||
|
||||
apu_codec_sequencer u_codec_sequencer (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.soft_reset_i (s_soft_reset),
|
||||
.resource_reset_i (s_resource_reset_apply_q),
|
||||
.counter_clear_i (s_cnt_clear),
|
||||
.abort_i (s_abort),
|
||||
.launch_i (1'b0),
|
||||
.launch_entry_i (2'd0),
|
||||
.image_valid_i (s_mc_status[`APB4_APU__MC_STATUS_VALID]),
|
||||
.timeout_i (s_sequencer_timeout),
|
||||
.entry_pc_i (s_mc_entry_pc),
|
||||
.entry_first_i (s_mc_entry_first),
|
||||
.entry_last_i (s_mc_entry_last),
|
||||
.entry_max_loop_i (s_mc_entry_max_loop),
|
||||
.entry_max_retired_i (s_mc_entry_max_retired),
|
||||
.input_exhausted_i (1'b1),
|
||||
.input_ready_i (1'b0),
|
||||
.output_ready_i (1'b1),
|
||||
.kernel_done_i (1'b0),
|
||||
.transport_idle_success_i(!s_dma_busy && !s_dma_err),
|
||||
.stall_i (1'b0),
|
||||
.cause_valid_i (1'b0),
|
||||
.cause_code_i (6'd0),
|
||||
.cause_stage_i (4'd0),
|
||||
.cause_resp_i (2'd0),
|
||||
.cause_index_i (8'd0),
|
||||
.cause_addr_i (32'd0),
|
||||
.cause_detail_i (32'd0),
|
||||
.fetch_o (s_seq_fetch),
|
||||
.fetch_addr_o (s_seq_fetch_addr),
|
||||
.fetch_data_i (s_seq_fetch_data),
|
||||
.fetch_valid_i (s_seq_fetch_valid),
|
||||
.stat_o (s_seq_status),
|
||||
.retired_o (s_seq_retired),
|
||||
.gpr_o (s_seq_gpr),
|
||||
.trapped_o (s_seq_trapped),
|
||||
.trap_event_o (s_seq_trap_event),
|
||||
.abort_done_o (s_seq_abort_done),
|
||||
.fault_valid_o (s_seq_fault_valid),
|
||||
.fault_code_o (s_seq_fault_code),
|
||||
.fault_stage_o (s_seq_fault_stage),
|
||||
.fault_resp_o (s_seq_fault_resp),
|
||||
.fault_index_o (s_seq_fault_index),
|
||||
.fault_addr_o (s_seq_fault_addr),
|
||||
.fault_detail_o (s_seq_fault_detail),
|
||||
.perf_retired_o (s_seq_perf_retired),
|
||||
.idle_o (s_seq_idle)
|
||||
apu_local_sram u_local_sram (
|
||||
.clk_i(clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.image_valid_i(s_mc_status[`APB4_APU__MC_STATUS_VALID]),
|
||||
.table_bytes_i(s_mc_table_bytes),
|
||||
.epoch_clear_i(s_seq_launch_epoch || s_soft_reset || s_resource_reset_apply_q || s_abort),
|
||||
.loader_active_i(!s_mc_idle),
|
||||
.loader_req_i(s_local_loader_write),
|
||||
.loader_addr_i(s_local_loader_addr),
|
||||
.loader_data_i(s_local_loader_data),
|
||||
.loader_strb_i(s_local_loader_strb),
|
||||
.loader_ready_o(s_local_loader_ready),
|
||||
.codec_req_i(s_local_codec_req),
|
||||
.codec_write_i(s_local_codec_write),
|
||||
.codec_addr_i(s_local_codec_addr),
|
||||
.codec_data_i(s_local_codec_write_data),
|
||||
.codec_strb_i(s_local_codec_strb),
|
||||
.codec_ready_o(s_local_codec_ready),
|
||||
.codec_data_o(s_local_codec_read_data),
|
||||
.codec_valid_o(s_local_codec_valid),
|
||||
.codec_access_err_o(s_local_codec_err)
|
||||
);
|
||||
|
||||
apu_primitive_dispatcher u_primitive_dispatcher (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.flush_i (s_seq_launch_epoch || s_soft_reset || s_resource_reset_apply_q || s_abort),
|
||||
.req_valid_i (s_primitive_req_valid),
|
||||
.req_ready_o (s_primitive_req_ready),
|
||||
.instruction_i (s_primitive_instruction),
|
||||
.source0_i (s_primitive_source0),
|
||||
.source1_i (s_primitive_source1),
|
||||
.destination_i (s_primitive_destination),
|
||||
.scratch_base_i (s_active_scratch_base),
|
||||
.scratch_bytes_i (s_active_scratch_bytes),
|
||||
.table_offset_i (s_active_table_offset),
|
||||
.table_bytes_i (s_active_table_bytes),
|
||||
.result_valid_o (s_primitive_result_valid),
|
||||
.result_dst_o (s_primitive_result_dst),
|
||||
.result_data_o (s_primitive_result_data),
|
||||
.result_words_o (s_primitive_result_words),
|
||||
.result_kernel_o (s_primitive_result_kernel),
|
||||
.error_o (s_primitive_err),
|
||||
.error_code_o (s_primitive_err_code),
|
||||
.error_stage_o (s_primitive_err_stage),
|
||||
.error_reason_o (s_primitive_err_reason),
|
||||
.cycles_o (s_primitive_cycles_unused),
|
||||
.kernel_done_o (s_primitive_kernel_done),
|
||||
.busy_o (s_primitive_busy),
|
||||
.input_exhausted_o(s_primitive_input_exhausted),
|
||||
.input_ready_o (s_primitive_input_ready),
|
||||
.output_ready_o (s_primitive_output_ready),
|
||||
.input_count_o (s_primitive_input_count_unused),
|
||||
.output_count_o (s_primitive_output_count_unused),
|
||||
.kernel_busy_o (s_primitive_kernel_busy_unused),
|
||||
.memory_req_o (s_local_codec_req),
|
||||
.memory_write_o (s_local_codec_write),
|
||||
.memory_addr_o (s_local_codec_addr),
|
||||
.memory_data_o (s_local_codec_write_data),
|
||||
.memory_strb_o (s_local_codec_strb),
|
||||
.memory_valid_i (s_local_codec_valid),
|
||||
.memory_data_i (s_local_codec_read_data),
|
||||
.memory_error_i (s_local_codec_err),
|
||||
.input_valid_i (1'b0),
|
||||
.input_data_i (41'd0),
|
||||
.input_accept_o (s_primitive_input_accept_unused),
|
||||
.output_valid_o (s_primitive_output_valid_unused),
|
||||
.output_data_o (s_primitive_output_data_unused),
|
||||
.output_accept_i (1'b0)
|
||||
);
|
||||
|
||||
apu_codec_sequencer #(
|
||||
.EnableP4(1'b1)
|
||||
) u_codec_sequencer (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.soft_reset_i (s_soft_reset),
|
||||
.resource_reset_i (s_resource_reset_apply_q),
|
||||
.counter_clear_i (s_cnt_clear),
|
||||
.abort_i (s_abort),
|
||||
.launch_i (1'b0),
|
||||
.launch_entry_i (2'd0),
|
||||
.image_valid_i (s_mc_status[`APB4_APU__MC_STATUS_VALID]),
|
||||
.timeout_i (s_sequencer_timeout),
|
||||
.entry_pc_i (s_mc_entry_pc),
|
||||
.entry_first_i (s_mc_entry_first),
|
||||
.entry_last_i (s_mc_entry_last),
|
||||
.entry_max_loop_i (s_mc_entry_max_loop),
|
||||
.entry_max_retired_i (s_mc_entry_max_retired),
|
||||
.entry_scratch_base_i (s_mc_entry_scratch_base),
|
||||
.entry_scratch_bytes_i (s_mc_entry_scratch_bytes),
|
||||
.entry_primitive_mask_i (s_mc_entry_primitive_mask),
|
||||
.entry_table_offset_i (s_mc_entry_table_offset),
|
||||
.entry_table_bytes_i (s_mc_entry_table_bytes),
|
||||
.input_exhausted_i (s_primitive_input_exhausted),
|
||||
.input_ready_i (s_primitive_input_ready),
|
||||
.output_ready_i (s_primitive_output_ready),
|
||||
.kernel_done_i (s_primitive_kernel_done),
|
||||
.transport_idle_success_i (!s_dma_busy && !s_dma_err),
|
||||
.stall_i (1'b0),
|
||||
.cause_valid_i (1'b0),
|
||||
.cause_code_i (6'd0),
|
||||
.cause_stage_i (4'd0),
|
||||
.cause_resp_i (2'd0),
|
||||
.cause_index_i (8'd0),
|
||||
.cause_addr_i (32'd0),
|
||||
.cause_detail_i (32'd0),
|
||||
.primitive_req_valid_o (s_primitive_req_valid),
|
||||
.primitive_req_ready_i (s_primitive_req_ready),
|
||||
.primitive_instruction_o (s_primitive_instruction),
|
||||
.primitive_source0_o (s_primitive_source0),
|
||||
.primitive_source1_o (s_primitive_source1),
|
||||
.primitive_destination_o (s_primitive_destination),
|
||||
.primitive_result_valid_i (s_primitive_result_valid),
|
||||
.primitive_result_dst_i (s_primitive_result_dst),
|
||||
.primitive_result_data_i (s_primitive_result_data),
|
||||
.primitive_result_words_i (s_primitive_result_words),
|
||||
.primitive_result_kernel_i(s_primitive_result_kernel),
|
||||
.primitive_error_i (s_primitive_err),
|
||||
.primitive_error_code_i (s_primitive_err_code),
|
||||
.primitive_error_stage_i (s_primitive_err_stage),
|
||||
.primitive_error_reason_i (s_primitive_err_reason),
|
||||
.fetch_o (s_seq_fetch),
|
||||
.fetch_addr_o (s_seq_fetch_addr),
|
||||
.fetch_data_i (s_seq_fetch_data),
|
||||
.fetch_valid_i (s_seq_fetch_valid),
|
||||
.stat_o (s_seq_status),
|
||||
.retired_o (s_seq_retired),
|
||||
.gpr_o (s_seq_gpr),
|
||||
.trapped_o (s_seq_trapped),
|
||||
.trap_event_o (s_seq_trap_event),
|
||||
.abort_done_o (s_seq_abort_done),
|
||||
.fault_valid_o (s_seq_fault_valid),
|
||||
.fault_code_o (s_seq_fault_code),
|
||||
.fault_stage_o (s_seq_fault_stage),
|
||||
.fault_resp_o (s_seq_fault_resp),
|
||||
.fault_index_o (s_seq_fault_index),
|
||||
.fault_addr_o (s_seq_fault_addr),
|
||||
.fault_detail_o (s_seq_fault_detail),
|
||||
.perf_retired_o (s_seq_perf_retired),
|
||||
.active_scratch_base_o (s_active_scratch_base),
|
||||
.active_scratch_bytes_o (s_active_scratch_bytes),
|
||||
.active_primitive_mask_o (s_active_primitive_mask),
|
||||
.active_table_offset_o (s_active_table_offset),
|
||||
.active_table_bytes_o (s_active_table_bytes),
|
||||
.launch_epoch_o (s_seq_launch_epoch),
|
||||
.idle_o (s_seq_idle)
|
||||
);
|
||||
|
||||
apu_ring_scheduler u_ring_scheduler (
|
||||
@@ -576,10 +720,14 @@ module apb4_apu (
|
||||
end
|
||||
end
|
||||
|
||||
logic s_unused_pending;
|
||||
logic s_unused_pending, s_unused_p3, s_unused_p4;
|
||||
assign s_unused_pending = s_dma_input_pending ^ s_dma_output_pending ^ s_dma_aborted ^
|
||||
s_reg_idle_unused ^ s_backend_job_valid_unused ^ s_backend_resp_ready_unused ^
|
||||
^s_backend_descriptor_unused ^ ^s_backend_index_unused ^ ^s_ring_coalesce[15:8];
|
||||
logic s_unused_p3;
|
||||
assign s_unused_p3 = ^s_seq_gpr;
|
||||
assign s_unused_p4 = ^s_active_primitive_mask ^ s_local_loader_ready ^ s_local_codec_ready ^
|
||||
s_primitive_input_accept_unused ^ s_primitive_output_valid_unused ^
|
||||
^s_primitive_cycles_unused ^ ^s_primitive_output_data_unused ^
|
||||
^s_primitive_input_count_unused ^ ^s_primitive_output_count_unused ^
|
||||
s_primitive_kernel_busy_unused;
|
||||
endmodule
|
||||
@@ -0,0 +1,210 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
module apu_bitstream_engine (
|
||||
// verilog_format: off -- preserve request and FIFO columns
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic flush_i,
|
||||
input logic req_valid_i,
|
||||
output logic req_ready_o,
|
||||
input logic [ 2:0] req_op_i,
|
||||
input logic [ 5:0] req_width_i,
|
||||
output logic result_valid_o,
|
||||
output logic [31:0] result_data_o,
|
||||
output logic [ 6:0] available_bits_o,
|
||||
output logic eof_o,
|
||||
output logic error_o,
|
||||
input logic fifo_empty_i,
|
||||
input logic [40:0] fifo_data_i,
|
||||
output logic fifo_pop_o
|
||||
// verilog_format: on
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
Idle,
|
||||
Refill,
|
||||
Check,
|
||||
Execute
|
||||
} state_e;
|
||||
|
||||
state_e s_state_q;
|
||||
logic [127:0] s_bits_q;
|
||||
logic [ 6:0] s_count_q;
|
||||
logic [ 2:0] s_op_q;
|
||||
logic [ 5:0] s_width_q;
|
||||
logic [ 2:0] s_bit_position_q;
|
||||
logic s_eof_q;
|
||||
logic [ 6:0] s_append_bits;
|
||||
logic [127:0] s_append_value;
|
||||
logic [ 31:0] s_peek_value;
|
||||
logic [ 5:0] s_effective_width;
|
||||
logic [ 31:0] s_req_peek_value;
|
||||
logic [ 5:0] s_req_effective_width;
|
||||
logic [127:0] s_req_buffer;
|
||||
logic [ 6:0] s_req_buffer_count;
|
||||
|
||||
function automatic logic [7:0] reverse_byte(input logic [7:0] value_i);
|
||||
logic [7:0] s_value;
|
||||
begin
|
||||
for (int bit_index = 0; bit_index < 8; bit_index++) begin
|
||||
s_value[bit_index] = value_i[7-bit_index];
|
||||
end
|
||||
return s_value;
|
||||
end
|
||||
endfunction
|
||||
|
||||
always_comb begin
|
||||
s_append_bits = 7'(fifo_data_i[34:32] * 8);
|
||||
s_append_value = 128'd0;
|
||||
for (int byte_index = 0; byte_index < 4; byte_index++) begin
|
||||
if (byte_index < fifo_data_i[34:32]) begin
|
||||
s_append_value[(byte_index*8)+:8] = reverse_byte(fifo_data_i[(byte_index*8)+:8]);
|
||||
end
|
||||
end
|
||||
s_peek_value = 32'd0;
|
||||
s_req_peek_value = 32'd0;
|
||||
s_req_buffer = s_bits_q | (s_append_value << s_count_q);
|
||||
s_req_buffer_count = s_count_q + s_append_bits;
|
||||
for (int bit_index = 0; bit_index < 32; bit_index++) begin
|
||||
if (bit_index < s_width_q) begin
|
||||
s_peek_value[32'(s_width_q)-1-bit_index] = s_bits_q[bit_index];
|
||||
end
|
||||
if (bit_index < req_width_i) begin
|
||||
s_req_peek_value[32'(req_width_i)-1-bit_index] = s_req_buffer[bit_index];
|
||||
end
|
||||
end
|
||||
s_effective_width = (s_op_q == 3'd4) ? {3'd0, ((3'd0 - s_bit_position_q) & 3'd7)} : s_width_q;
|
||||
s_req_effective_width = (req_op_i == 3'd4) ?
|
||||
{3'd0, ((3'd0 - s_bit_position_q) & 3'd7)} : req_width_i;
|
||||
end
|
||||
|
||||
assign req_ready_o = s_state_q == Idle;
|
||||
assign available_bits_o = s_count_q;
|
||||
assign eof_o = s_eof_q && fifo_empty_i && (s_count_q == 7'd0);
|
||||
assign fifo_pop_o = ((s_state_q == Refill) && !fifo_empty_i) ||
|
||||
((s_state_q == Idle) && req_valid_i && req_ready_o &&
|
||||
(req_op_i != 3'd4) && (s_count_q < {1'b0, req_width_i}) && !fifo_empty_i);
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_bits_q <= '0;
|
||||
s_count_q <= 7'd0;
|
||||
s_op_q <= 3'd0;
|
||||
s_width_q <= 6'd0;
|
||||
s_bit_position_q <= 3'd0;
|
||||
s_eof_q <= 1'b0;
|
||||
result_valid_o <= 1'b0;
|
||||
result_data_o <= 32'd0;
|
||||
error_o <= 1'b0;
|
||||
end else if (flush_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_bits_q <= '0;
|
||||
s_count_q <= 7'd0;
|
||||
s_bit_position_q <= 3'd0;
|
||||
s_eof_q <= 1'b0;
|
||||
result_valid_o <= 1'b0;
|
||||
result_data_o <= 32'd0;
|
||||
error_o <= 1'b0;
|
||||
end else begin
|
||||
result_valid_o <= 1'b0;
|
||||
error_o <= 1'b0;
|
||||
unique case (s_state_q)
|
||||
Idle: begin
|
||||
if (req_valid_i) begin
|
||||
s_op_q <= req_op_i;
|
||||
s_width_q <= req_width_i;
|
||||
if ((req_op_i == 3'd4) || (s_count_q >= {1'b0, req_width_i})) begin
|
||||
result_data_o <= (req_op_i == 3'd0) ? {25'd0, s_count_q} : s_req_peek_value;
|
||||
if (req_op_i inside {3'd2, 3'd3, 3'd4}) begin
|
||||
s_bits_q <= s_bits_q >> s_req_effective_width;
|
||||
s_count_q <= s_count_q - s_req_effective_width;
|
||||
s_bit_position_q <= s_bit_position_q + s_req_effective_width[2:0];
|
||||
end
|
||||
result_valid_o <= 1'b1;
|
||||
s_state_q <= Idle;
|
||||
end else if (!fifo_empty_i) begin
|
||||
s_bits_q <= s_req_buffer;
|
||||
s_count_q <= s_req_buffer_count;
|
||||
if (fifo_data_i[40]) s_eof_q <= 1'b1;
|
||||
if (fifo_data_i[40] && (req_op_i != 3'd0) &&
|
||||
(s_req_buffer_count < {1'b0, req_width_i})) begin
|
||||
error_o <= 1'b1;
|
||||
result_valid_o <= 1'b1;
|
||||
s_state_q <= Idle;
|
||||
end else if ((req_op_i != 3'd0) && (s_req_buffer_count >= {1'b0, req_width_i})) begin
|
||||
result_data_o <= s_req_peek_value;
|
||||
if (req_op_i inside {3'd2, 3'd3}) begin
|
||||
s_bits_q <= s_req_buffer >> req_width_i;
|
||||
s_count_q <= s_req_buffer_count - req_width_i;
|
||||
s_bit_position_q <= s_bit_position_q + req_width_i[2:0];
|
||||
end
|
||||
result_valid_o <= 1'b1;
|
||||
s_state_q <= Idle;
|
||||
end else begin
|
||||
s_state_q <= Check;
|
||||
end
|
||||
end else begin
|
||||
s_state_q <= Refill;
|
||||
end
|
||||
end
|
||||
end
|
||||
Refill: begin
|
||||
if (!fifo_empty_i) begin
|
||||
s_bits_q <= s_bits_q | (s_append_value << s_count_q);
|
||||
s_count_q <= s_count_q + s_append_bits;
|
||||
if (fifo_data_i[40]) s_eof_q <= 1'b1;
|
||||
s_state_q <= Check;
|
||||
end else if (s_eof_q) begin
|
||||
if (s_op_q == 3'd0) begin
|
||||
s_state_q <= Execute;
|
||||
end else begin
|
||||
error_o <= 1'b1;
|
||||
result_valid_o <= 1'b1;
|
||||
s_state_q <= Idle;
|
||||
end
|
||||
end
|
||||
end
|
||||
Check: begin
|
||||
if (s_count_q >= {1'b0, s_width_q}) begin
|
||||
result_data_o <= (s_op_q == 3'd0) ? {25'd0, s_count_q} : s_peek_value;
|
||||
if (s_op_q inside {3'd2, 3'd3, 3'd4}) begin
|
||||
s_bits_q <= s_bits_q >> s_effective_width;
|
||||
s_count_q <= s_count_q - s_effective_width;
|
||||
s_bit_position_q <= s_bit_position_q + s_effective_width[2:0];
|
||||
end
|
||||
result_valid_o <= 1'b1;
|
||||
s_state_q <= Idle;
|
||||
end else if (s_eof_q && fifo_empty_i) begin
|
||||
if (s_op_q == 3'd0) begin
|
||||
result_data_o <= {25'd0, s_count_q};
|
||||
result_valid_o <= 1'b1;
|
||||
s_state_q <= Idle;
|
||||
end else begin
|
||||
error_o <= 1'b1;
|
||||
result_valid_o <= 1'b1;
|
||||
s_state_q <= Idle;
|
||||
end
|
||||
end else begin
|
||||
s_state_q <= Refill;
|
||||
end
|
||||
end
|
||||
Execute: begin
|
||||
if ((s_op_q != 3'd0) && (s_count_q < {1'b0, s_effective_width})) begin
|
||||
s_state_q <= Refill;
|
||||
end else begin
|
||||
result_data_o <= (s_op_q == 3'd0) ? {25'd0, s_count_q} : s_peek_value;
|
||||
if (s_op_q inside {3'd2, 3'd3, 3'd4}) begin
|
||||
s_bits_q <= s_bits_q >> s_effective_width;
|
||||
s_count_q <= s_count_q - s_effective_width;
|
||||
s_bit_position_q <= s_bit_position_q + s_effective_width[2:0];
|
||||
end
|
||||
result_valid_o <= 1'b1;
|
||||
s_state_q <= Idle;
|
||||
end
|
||||
end
|
||||
default: s_state_q <= Idle;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
@@ -3,7 +3,9 @@
|
||||
|
||||
`include "apu_define.svh"
|
||||
|
||||
module apu_codec_sequencer (
|
||||
module apu_codec_sequencer #(
|
||||
parameter bit EnableP4 = 1'b0
|
||||
) (
|
||||
// verilog_format: off -- preserve launch, predicate, store, and result columns
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
@@ -20,6 +22,11 @@ module apu_codec_sequencer (
|
||||
input logic [2:0][10:0] entry_last_i,
|
||||
input logic [2:0][15:0] entry_max_loop_i,
|
||||
input logic [2:0][23:0] entry_max_retired_i,
|
||||
input logic [2:0][16:0] entry_scratch_base_i,
|
||||
input logic [2:0][16:0] entry_scratch_bytes_i,
|
||||
input logic [2:0][31:0] entry_primitive_mask_i,
|
||||
input logic [2:0][15:0] entry_table_offset_i,
|
||||
input logic [2:0][15:0] entry_table_bytes_i,
|
||||
input logic input_exhausted_i,
|
||||
input logic input_ready_i,
|
||||
input logic output_ready_i,
|
||||
@@ -33,6 +40,21 @@ module apu_codec_sequencer (
|
||||
input logic [7:0] cause_index_i,
|
||||
input logic [31:0] cause_addr_i,
|
||||
input logic [31:0] cause_detail_i,
|
||||
output logic primitive_req_valid_o,
|
||||
input logic primitive_req_ready_i,
|
||||
output logic [63:0] primitive_instruction_o,
|
||||
output logic [31:0] primitive_source0_o,
|
||||
output logic [31:0] primitive_source1_o,
|
||||
output logic [31:0] primitive_destination_o,
|
||||
input logic primitive_result_valid_i,
|
||||
input logic [ 3:0] primitive_result_dst_i,
|
||||
input logic [ 3:0][31:0] primitive_result_data_i,
|
||||
input logic [ 2:0] primitive_result_words_i,
|
||||
input logic primitive_result_kernel_i,
|
||||
input logic primitive_error_i,
|
||||
input logic [ 5:0] primitive_error_code_i,
|
||||
input logic [ 3:0] primitive_error_stage_i,
|
||||
input logic [ 7:0] primitive_error_reason_i,
|
||||
output logic fetch_o,
|
||||
output logic [10:0] fetch_addr_o,
|
||||
input logic [63:0] fetch_data_i,
|
||||
@@ -51,16 +73,23 @@ module apu_codec_sequencer (
|
||||
output logic [31:0] fault_addr_o,
|
||||
output logic [31:0] fault_detail_o,
|
||||
output logic [63:0] perf_retired_o,
|
||||
output logic [16:0] active_scratch_base_o,
|
||||
output logic [16:0] active_scratch_bytes_o,
|
||||
output logic [31:0] active_primitive_mask_o,
|
||||
output logic [15:0] active_table_offset_o,
|
||||
output logic [15:0] active_table_bytes_o,
|
||||
output logic launch_epoch_o,
|
||||
output logic idle_o
|
||||
// verilog_format: on
|
||||
);
|
||||
import apu_microcode_pkg::*;
|
||||
|
||||
typedef enum logic [1:0] {
|
||||
typedef enum logic [2:0] {
|
||||
Idle,
|
||||
FetchRequest,
|
||||
FetchWait,
|
||||
Execute
|
||||
Execute,
|
||||
PrimitiveWait
|
||||
} sequencer_state_e;
|
||||
|
||||
sequencer_state_e s_state_q;
|
||||
@@ -76,12 +105,21 @@ module apu_codec_sequencer (
|
||||
logic [23:0] s_max_retired_q, s_frame_retired_q;
|
||||
logic [31:0] s_watchdog_q;
|
||||
logic [63:0] s_instruction_q;
|
||||
logic [16:0] s_scratch_base_q, s_scratch_bytes_q;
|
||||
logic [31:0] s_primitive_mask_q;
|
||||
logic [15:0] s_table_offset_q, s_table_bytes_q;
|
||||
logic s_kernel_pending_q, s_kernel_done_q;
|
||||
logic [ 3:0] s_kernel_dst_q;
|
||||
logic s_kernel_err_q;
|
||||
logic [ 5:0] s_kernel_err_code_q;
|
||||
logic [ 3:0] s_kernel_err_stage_q;
|
||||
logic [ 7:0] s_kernel_err_reason_q;
|
||||
logic [31:0] s_stat_q;
|
||||
logic s_trapped_q;
|
||||
logic [63:0] s_perf_retired_q;
|
||||
|
||||
logic [3:0] s_class, s_opcode, s_predicate, s_dst, s_src0, s_src1;
|
||||
logic [5:0] s_aux;
|
||||
logic [7:0] s_aux;
|
||||
logic [31:0] s_immediate, s_source0, s_source1;
|
||||
logic s_predicate_true;
|
||||
logic [11:0] s_forward_target, s_loop_target;
|
||||
@@ -89,27 +127,43 @@ module apu_codec_sequencer (
|
||||
logic s_trap_now;
|
||||
logic [ 7:0] s_trap_reason;
|
||||
logic [31:0] s_trap_detail;
|
||||
logic s_pending_dependency, s_wait_stall, s_primitive_stall, s_execute_stall;
|
||||
logic s_primitive_class;
|
||||
|
||||
assign s_class = s_instruction_q[63:60];
|
||||
assign s_opcode = s_instruction_q[59:56];
|
||||
assign s_predicate = s_instruction_q[55:52];
|
||||
assign s_dst = s_instruction_q[51:48];
|
||||
assign s_src0 = s_instruction_q[47:44];
|
||||
assign s_src1 = s_instruction_q[43:40];
|
||||
assign s_aux = s_instruction_q[37:32];
|
||||
assign s_immediate = s_instruction_q[31:0];
|
||||
assign s_source0 = s_gpr_q[s_src0];
|
||||
assign s_source1 = s_gpr_q[s_src1];
|
||||
assign s_class = s_instruction_q[63:60];
|
||||
assign s_opcode = s_instruction_q[59:56];
|
||||
assign s_predicate = s_instruction_q[55:52];
|
||||
assign s_dst = s_instruction_q[51:48];
|
||||
assign s_src0 = s_instruction_q[47:44];
|
||||
assign s_src1 = s_instruction_q[43:40];
|
||||
assign s_aux = s_instruction_q[39:32];
|
||||
assign s_immediate = s_instruction_q[31:0];
|
||||
assign s_source0 = s_gpr_q[s_src0];
|
||||
assign s_source1 = s_gpr_q[s_src1];
|
||||
assign s_forward_target = {1'b0, s_pc_q} + 1'b1 + {1'b0, s_immediate[10:0]};
|
||||
assign s_loop_target = {1'b0, s_pc_q} + 1'b1 - {1'b0, s_immediate[10:0]};
|
||||
assign fetch_o = s_state_q == FetchRequest;
|
||||
assign fetch_addr_o = s_pc_q;
|
||||
assign stat_o = s_stat_q;
|
||||
assign retired_o = {8'd0, s_frame_retired_q};
|
||||
assign gpr_o = s_gpr_q;
|
||||
assign trapped_o = s_trapped_q;
|
||||
assign perf_retired_o = s_perf_retired_q;
|
||||
assign idle_o = s_state_q == Idle;
|
||||
assign s_loop_target = {1'b0, s_pc_q} + 1'b1 - {1'b0, s_immediate[10:0]};
|
||||
assign fetch_o = s_state_q == FetchRequest;
|
||||
assign fetch_addr_o = s_pc_q;
|
||||
assign stat_o = s_stat_q;
|
||||
assign retired_o = {8'd0, s_frame_retired_q};
|
||||
assign gpr_o = s_gpr_q;
|
||||
assign trapped_o = s_trapped_q;
|
||||
assign perf_retired_o = s_perf_retired_q;
|
||||
assign idle_o = s_state_q == Idle;
|
||||
assign active_scratch_base_o = s_scratch_base_q;
|
||||
assign active_scratch_bytes_o = s_scratch_bytes_q;
|
||||
assign active_primitive_mask_o = s_primitive_mask_q;
|
||||
assign active_table_offset_o = s_table_offset_q;
|
||||
assign active_table_bytes_o = s_table_bytes_q;
|
||||
assign launch_epoch_o = (s_state_q == Idle) && launch_i && image_valid_i &&
|
||||
(launch_entry_i < 2'd3);
|
||||
assign primitive_instruction_o = s_instruction_q;
|
||||
assign primitive_source0_o = s_source0;
|
||||
assign primitive_source1_o = s_source1;
|
||||
assign primitive_destination_o = s_gpr_q[s_dst];
|
||||
assign s_primitive_class = EnableP4 && (s_class >= 4'd2) && (s_class <= 4'd5);
|
||||
assign primitive_req_valid_o = (s_state_q == Execute) && s_predicate_true &&
|
||||
s_primitive_class && !s_pending_dependency && !s_kernel_pending_q;
|
||||
|
||||
always_comb begin
|
||||
unique case (s_predicate)
|
||||
@@ -123,23 +177,87 @@ module apu_codec_sequencer (
|
||||
4'd7: s_predicate_true = input_exhausted_i;
|
||||
4'd8: s_predicate_true = input_ready_i;
|
||||
4'd9: s_predicate_true = output_ready_i;
|
||||
4'd10: s_predicate_true = kernel_done_i;
|
||||
4'd10: s_predicate_true = s_kernel_done_q;
|
||||
4'd11: s_predicate_true = transport_idle_success_i;
|
||||
default: s_predicate_true = 1'b0;
|
||||
endcase
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
s_pending_dependency = 1'b0;
|
||||
if (s_kernel_pending_q && s_predicate_true) begin
|
||||
unique case (s_class)
|
||||
`APB4_APU__MC_CLASS_CONTROL: begin
|
||||
s_pending_dependency = (s_opcode == `APB4_APU__MC_CONTROL_LOOP_SETUP) &&
|
||||
(s_src0 == s_kernel_dst_q);
|
||||
end
|
||||
`APB4_APU__MC_CLASS_SCALAR: begin
|
||||
unique case (s_opcode)
|
||||
`APB4_APU__MC_SCALAR_MOV:
|
||||
s_pending_dependency = (s_src0 == s_kernel_dst_q) || (s_dst == s_kernel_dst_q);
|
||||
`APB4_APU__MC_SCALAR_MOVI: s_pending_dependency = s_dst == s_kernel_dst_q;
|
||||
`APB4_APU__MC_SCALAR_CMP:
|
||||
s_pending_dependency = (s_src0 == s_kernel_dst_q) || (s_src1 == s_kernel_dst_q);
|
||||
`APB4_APU__MC_SCALAR_SAT:
|
||||
s_pending_dependency = (s_src0 == s_kernel_dst_q) || (s_dst == s_kernel_dst_q);
|
||||
default:
|
||||
s_pending_dependency = (s_src0 == s_kernel_dst_q) ||
|
||||
(s_src1 == s_kernel_dst_q) || (s_dst == s_kernel_dst_q);
|
||||
endcase
|
||||
end
|
||||
`APB4_APU__MC_CLASS_BITSTREAM: begin
|
||||
s_pending_dependency = (s_dst == s_kernel_dst_q) ||
|
||||
((s_opcode >= `APB4_APU__MC_BITSTREAM_FRAME_SYNC) &&
|
||||
((s_src0 == s_kernel_dst_q) || (s_src1 == s_kernel_dst_q)));
|
||||
end
|
||||
`APB4_APU__MC_CLASS_ENTROPY, `APB4_APU__MC_CLASS_LOCAL: begin
|
||||
s_pending_dependency = (s_src0 == s_kernel_dst_q) ||
|
||||
(s_src1 == s_kernel_dst_q) || (s_dst == s_kernel_dst_q) ||
|
||||
((s_class == `APB4_APU__MC_CLASS_ENTROPY) &&
|
||||
(s_opcode inside {`APB4_APU__MC_ENTROPY_HUFF_PAIR,
|
||||
`APB4_APU__MC_ENTROPY_HUFF_QUAD}) &&
|
||||
((s_dst + 1'b1) == s_kernel_dst_q)) ||
|
||||
((s_class == `APB4_APU__MC_CLASS_ENTROPY) &&
|
||||
(s_opcode == `APB4_APU__MC_ENTROPY_HUFF_QUAD) &&
|
||||
(((s_dst + 4'd2) == s_kernel_dst_q) ||
|
||||
((s_dst + 4'd3) == s_kernel_dst_q))) ||
|
||||
((s_class == `APB4_APU__MC_CLASS_LOCAL) &&
|
||||
(s_opcode == `APB4_APU__MC_LOCAL_FIFO_POP) &&
|
||||
((s_dst + 1'b1) == s_kernel_dst_q));
|
||||
end
|
||||
`APB4_APU__MC_CLASS_KERNEL: s_pending_dependency = 1'b1;
|
||||
default: s_pending_dependency = 1'b0;
|
||||
endcase
|
||||
end
|
||||
s_wait_stall = EnableP4 && s_predicate_true &&
|
||||
(s_class == `APB4_APU__MC_CLASS_CONTROL) &&
|
||||
(s_opcode == `APB4_APU__MC_CONTROL_WAIT) &&
|
||||
(((s_aux == `APB4_APU__MC_WAIT_KERNEL) && !s_kernel_done_q) ||
|
||||
((s_aux == `APB4_APU__MC_WAIT_INPUT_FIFO) && !input_ready_i) ||
|
||||
((s_aux == `APB4_APU__MC_WAIT_OUTPUT_FIFO) && !output_ready_i));
|
||||
s_primitive_stall = s_primitive_class &&
|
||||
(s_kernel_pending_q || s_pending_dependency || !primitive_req_ready_i);
|
||||
s_execute_stall = stall_i || s_wait_stall || s_primitive_stall ||
|
||||
(s_kernel_pending_q && s_predicate_true &&
|
||||
(s_class == `APB4_APU__MC_CLASS_CONTROL) &&
|
||||
(s_opcode == `APB4_APU__MC_CONTROL_END));
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
s_trap_now = 1'b0;
|
||||
s_trap_reason = 8'd0;
|
||||
s_trap_detail = 32'd0;
|
||||
if (stall_i && (s_watchdog_q + 1'b1 >= timeout_i)) begin
|
||||
if (s_execute_stall && (s_watchdog_q + 1'b1 >= timeout_i)) begin
|
||||
s_trap_now = 1'b1;
|
||||
s_trap_reason = 8'd7;
|
||||
end else if (!instruction_encoding_valid(s_instruction_q)) begin
|
||||
s_trap_now = 1'b1;
|
||||
s_trap_reason = 8'd1;
|
||||
end else if ((s_class >= 4'd2) || ((s_class == 4'd0) && (s_opcode == 4'd8))) begin
|
||||
end else if ((!EnableP4 && ((s_class >= 4'd2) ||
|
||||
((s_class == 4'd0) && (s_opcode == 4'd8)))) ||
|
||||
(EnableP4 && ((s_class == 4'd6) ||
|
||||
((s_class == 4'd0) && (s_opcode == 4'd8) &&
|
||||
!(s_aux inside {8'd1, 8'd2, 8'd3}))))) begin
|
||||
s_trap_now = 1'b1;
|
||||
s_trap_reason = 8'd6;
|
||||
end else if (s_frame_retired_q >= s_max_retired_q) begin
|
||||
@@ -186,6 +304,8 @@ module apu_codec_sequencer (
|
||||
(s_loop_target[10:0] < s_first_pc_q);
|
||||
s_trap_reason = 8'd4;
|
||||
end
|
||||
4'd8: begin
|
||||
end
|
||||
default: begin
|
||||
s_trap_now = 1'b1;
|
||||
s_trap_reason = 8'd1;
|
||||
@@ -237,7 +357,7 @@ module apu_codec_sequencer (
|
||||
fault_index_o <= 8'd0; \
|
||||
fault_addr_o <= {18'd0, s_pc_q, 3'd0}; \
|
||||
fault_detail_o <= DETAIL; \
|
||||
s_stat_q <= {11'd0, |s_loop_active_q, stall_i, s_opcode, s_class, s_pc_q}; \
|
||||
s_stat_q <= {11'd0, |s_loop_active_q, s_execute_stall, s_opcode, s_class, s_pc_q}; \
|
||||
end
|
||||
|
||||
`define RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP \
|
||||
@@ -257,75 +377,112 @@ module apu_codec_sequencer (
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_gpr_q <= '0;
|
||||
s_eq_q <= 1'b0;
|
||||
s_signed_lt_q <= 1'b0;
|
||||
s_unsigned_lt_q <= 1'b0;
|
||||
s_return_pc_q <= '0;
|
||||
s_return_depth_q <= 3'd0;
|
||||
s_loop_active_q <= 4'd0;
|
||||
s_loop_count_q <= '0;
|
||||
s_loop_start_q <= '0;
|
||||
s_pc_q <= 11'd0;
|
||||
s_first_pc_q <= 11'd0;
|
||||
s_last_pc_q <= 11'd0;
|
||||
s_max_loop_q <= 16'd0;
|
||||
s_max_retired_q <= 24'd0;
|
||||
s_frame_retired_q <= 24'd0;
|
||||
s_watchdog_q <= 32'd0;
|
||||
s_instruction_q <= 64'd0;
|
||||
s_stat_q <= 32'd0;
|
||||
s_trapped_q <= 1'b0;
|
||||
s_perf_retired_q <= 64'd0;
|
||||
trap_event_o <= 1'b0;
|
||||
abort_done_o <= 1'b0;
|
||||
fault_valid_o <= 1'b0;
|
||||
fault_code_o <= 6'd0;
|
||||
fault_stage_o <= 4'd0;
|
||||
fault_resp_o <= 2'd0;
|
||||
fault_index_o <= 8'd0;
|
||||
fault_addr_o <= 32'd0;
|
||||
fault_detail_o <= 32'd0;
|
||||
s_state_q <= Idle;
|
||||
s_gpr_q <= '0;
|
||||
s_eq_q <= 1'b0;
|
||||
s_signed_lt_q <= 1'b0;
|
||||
s_unsigned_lt_q <= 1'b0;
|
||||
s_return_pc_q <= '0;
|
||||
s_return_depth_q <= 3'd0;
|
||||
s_loop_active_q <= 4'd0;
|
||||
s_loop_count_q <= '0;
|
||||
s_loop_start_q <= '0;
|
||||
s_pc_q <= 11'd0;
|
||||
s_first_pc_q <= 11'd0;
|
||||
s_last_pc_q <= 11'd0;
|
||||
s_max_loop_q <= 16'd0;
|
||||
s_max_retired_q <= 24'd0;
|
||||
s_frame_retired_q <= 24'd0;
|
||||
s_watchdog_q <= 32'd0;
|
||||
s_instruction_q <= 64'd0;
|
||||
s_scratch_base_q <= 17'd0;
|
||||
s_scratch_bytes_q <= 17'd0;
|
||||
s_primitive_mask_q <= 32'd0;
|
||||
s_table_offset_q <= 16'd0;
|
||||
s_table_bytes_q <= 16'd0;
|
||||
s_kernel_pending_q <= 1'b0;
|
||||
s_kernel_done_q <= 1'b0;
|
||||
s_kernel_dst_q <= 4'd0;
|
||||
s_kernel_err_q <= 1'b0;
|
||||
s_kernel_err_code_q <= 6'd0;
|
||||
s_kernel_err_stage_q <= 4'd0;
|
||||
s_kernel_err_reason_q <= 8'd0;
|
||||
s_stat_q <= 32'd0;
|
||||
s_trapped_q <= 1'b0;
|
||||
s_perf_retired_q <= 64'd0;
|
||||
trap_event_o <= 1'b0;
|
||||
abort_done_o <= 1'b0;
|
||||
fault_valid_o <= 1'b0;
|
||||
fault_code_o <= 6'd0;
|
||||
fault_stage_o <= 4'd0;
|
||||
fault_resp_o <= 2'd0;
|
||||
fault_index_o <= 8'd0;
|
||||
fault_addr_o <= 32'd0;
|
||||
fault_detail_o <= 32'd0;
|
||||
end else begin
|
||||
trap_event_o <= 1'b0;
|
||||
abort_done_o <= 1'b0;
|
||||
fault_valid_o <= 1'b0;
|
||||
if (primitive_result_valid_i && primitive_result_kernel_i) begin
|
||||
s_kernel_pending_q <= 1'b0;
|
||||
s_kernel_done_q <= !primitive_error_i;
|
||||
if (primitive_error_i) begin
|
||||
s_kernel_err_q <= 1'b1;
|
||||
s_kernel_err_code_q <= primitive_error_code_i;
|
||||
s_kernel_err_stage_q <= primitive_error_stage_i;
|
||||
s_kernel_err_reason_q <= primitive_error_reason_i;
|
||||
end else begin
|
||||
s_gpr_q[primitive_result_dst_i] <= primitive_result_data_i[0];
|
||||
end
|
||||
end else if (kernel_done_i) begin
|
||||
s_kernel_done_q <= 1'b1;
|
||||
end
|
||||
if (counter_clear_i) s_perf_retired_q <= 64'd0;
|
||||
if (soft_reset_i || resource_reset_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_gpr_q <= '0;
|
||||
s_eq_q <= 1'b0;
|
||||
s_signed_lt_q <= 1'b0;
|
||||
s_unsigned_lt_q <= 1'b0;
|
||||
s_return_depth_q <= 3'd0;
|
||||
s_loop_active_q <= 4'd0;
|
||||
s_pc_q <= 11'd0;
|
||||
s_frame_retired_q <= 24'd0;
|
||||
s_watchdog_q <= 32'd0;
|
||||
s_stat_q <= 32'd0;
|
||||
s_trapped_q <= 1'b0;
|
||||
s_perf_retired_q <= 64'd0;
|
||||
s_state_q <= Idle;
|
||||
s_gpr_q <= '0;
|
||||
s_eq_q <= 1'b0;
|
||||
s_signed_lt_q <= 1'b0;
|
||||
s_unsigned_lt_q <= 1'b0;
|
||||
s_return_depth_q <= 3'd0;
|
||||
s_loop_active_q <= 4'd0;
|
||||
s_pc_q <= 11'd0;
|
||||
s_frame_retired_q <= 24'd0;
|
||||
s_watchdog_q <= 32'd0;
|
||||
s_stat_q <= 32'd0;
|
||||
s_trapped_q <= 1'b0;
|
||||
s_perf_retired_q <= 64'd0;
|
||||
s_kernel_pending_q <= 1'b0;
|
||||
s_kernel_done_q <= 1'b0;
|
||||
s_kernel_err_q <= 1'b0;
|
||||
end else begin
|
||||
unique case (s_state_q)
|
||||
Idle: begin
|
||||
if (launch_i && image_valid_i && (launch_entry_i < 2'd3)) begin
|
||||
s_state_q <= FetchRequest;
|
||||
s_gpr_q <= '0;
|
||||
s_eq_q <= 1'b0;
|
||||
s_signed_lt_q <= 1'b0;
|
||||
s_unsigned_lt_q <= 1'b0;
|
||||
s_return_depth_q <= 3'd0;
|
||||
s_loop_active_q <= 4'd0;
|
||||
s_pc_q <= entry_pc_i[launch_entry_i];
|
||||
s_first_pc_q <= entry_first_i[launch_entry_i];
|
||||
s_last_pc_q <= entry_last_i[launch_entry_i];
|
||||
s_max_loop_q <= entry_max_loop_i[launch_entry_i];
|
||||
s_max_retired_q <= entry_max_retired_i[launch_entry_i];
|
||||
s_frame_retired_q <= 24'd0;
|
||||
s_watchdog_q <= 32'd0;
|
||||
s_stat_q <= {21'd0, entry_pc_i[launch_entry_i]};
|
||||
s_trapped_q <= 1'b0;
|
||||
s_state_q <= FetchRequest;
|
||||
s_gpr_q <= '0;
|
||||
s_eq_q <= 1'b0;
|
||||
s_signed_lt_q <= 1'b0;
|
||||
s_unsigned_lt_q <= 1'b0;
|
||||
s_return_depth_q <= 3'd0;
|
||||
s_loop_active_q <= 4'd0;
|
||||
s_pc_q <= entry_pc_i[launch_entry_i];
|
||||
s_first_pc_q <= entry_first_i[launch_entry_i];
|
||||
s_last_pc_q <= entry_last_i[launch_entry_i];
|
||||
s_max_loop_q <= entry_max_loop_i[launch_entry_i];
|
||||
s_max_retired_q <= entry_max_retired_i[launch_entry_i];
|
||||
s_scratch_base_q <= entry_scratch_base_i[launch_entry_i];
|
||||
s_scratch_bytes_q <= entry_scratch_bytes_i[launch_entry_i];
|
||||
s_primitive_mask_q <= entry_primitive_mask_i[launch_entry_i];
|
||||
s_table_offset_q <= entry_table_offset_i[launch_entry_i];
|
||||
s_table_bytes_q <= entry_table_bytes_i[launch_entry_i];
|
||||
s_frame_retired_q <= 24'd0;
|
||||
s_watchdog_q <= 32'd0;
|
||||
s_stat_q <= {21'd0, entry_pc_i[launch_entry_i]};
|
||||
s_trapped_q <= 1'b0;
|
||||
s_kernel_pending_q <= 1'b0;
|
||||
s_kernel_done_q <= 1'b0;
|
||||
s_kernel_err_q <= 1'b0;
|
||||
end
|
||||
end
|
||||
FetchRequest: begin
|
||||
@@ -333,8 +490,11 @@ module apu_codec_sequencer (
|
||||
if (s_watchdog_q + 1'b1 >= timeout_i) begin
|
||||
`RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP;
|
||||
end else if (abort_i) begin
|
||||
s_state_q <= Idle;
|
||||
abort_done_o <= 1'b1;
|
||||
s_state_q <= Idle;
|
||||
s_kernel_pending_q <= 1'b0;
|
||||
s_kernel_done_q <= 1'b0;
|
||||
s_kernel_err_q <= 1'b0;
|
||||
abort_done_o <= 1'b1;
|
||||
end else begin
|
||||
s_watchdog_q <= s_watchdog_q + 1'b1;
|
||||
s_state_q <= FetchWait;
|
||||
@@ -344,8 +504,11 @@ module apu_codec_sequencer (
|
||||
if (s_watchdog_q + 1'b1 >= timeout_i) begin
|
||||
`RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP;
|
||||
end else if (abort_i) begin
|
||||
s_state_q <= Idle;
|
||||
abort_done_o <= 1'b1;
|
||||
s_state_q <= Idle;
|
||||
s_kernel_pending_q <= 1'b0;
|
||||
s_kernel_done_q <= 1'b0;
|
||||
s_kernel_err_q <= 1'b0;
|
||||
abort_done_o <= 1'b1;
|
||||
end else begin
|
||||
s_watchdog_q <= s_watchdog_q + 1'b1;
|
||||
if (fetch_valid_i) begin
|
||||
@@ -359,23 +522,50 @@ module apu_codec_sequencer (
|
||||
end
|
||||
Execute: begin
|
||||
if (cause_valid_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_trapped_q <= 1'b1;
|
||||
trap_event_o <= 1'b1;
|
||||
fault_valid_o <= 1'b1;
|
||||
fault_code_o <= cause_code_i;
|
||||
fault_stage_o <= cause_stage_i;
|
||||
fault_resp_o <= cause_resp_i;
|
||||
fault_index_o <= cause_index_i;
|
||||
fault_addr_o <= cause_addr_i;
|
||||
fault_detail_o <= cause_detail_i;
|
||||
s_stat_q <= {11'd0, |s_loop_active_q, s_execute_stall, s_opcode, s_class, s_pc_q};
|
||||
end else if (primitive_result_valid_i && primitive_error_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_trapped_q <= 1'b1;
|
||||
trap_event_o <= 1'b1;
|
||||
fault_valid_o <= 1'b1;
|
||||
fault_code_o <= primitive_error_code_i;
|
||||
fault_stage_o <= primitive_error_stage_i;
|
||||
fault_resp_o <= 2'd0;
|
||||
fault_index_o <= 8'd0;
|
||||
fault_addr_o <= {18'd0, s_pc_q, 3'd0};
|
||||
fault_detail_o <= trap_detail(primitive_error_reason_i, s_pc_q, s_instruction_q);
|
||||
s_stat_q <= {11'd0, |s_loop_active_q, s_execute_stall, s_opcode, s_class, s_pc_q};
|
||||
end else if (s_kernel_err_q) begin
|
||||
s_state_q <= Idle;
|
||||
s_trapped_q <= 1'b1;
|
||||
trap_event_o <= 1'b1;
|
||||
fault_valid_o <= 1'b1;
|
||||
fault_code_o <= cause_code_i;
|
||||
fault_stage_o <= cause_stage_i;
|
||||
fault_resp_o <= cause_resp_i;
|
||||
fault_index_o <= cause_index_i;
|
||||
fault_addr_o <= cause_addr_i;
|
||||
fault_detail_o <= cause_detail_i;
|
||||
s_stat_q <= {11'd0, |s_loop_active_q, stall_i, s_opcode, s_class, s_pc_q};
|
||||
fault_code_o <= s_kernel_err_code_q;
|
||||
fault_stage_o <= s_kernel_err_stage_q;
|
||||
fault_resp_o <= 2'd0;
|
||||
fault_index_o <= 8'd0;
|
||||
fault_addr_o <= {18'd0, s_pc_q, 3'd0};
|
||||
fault_detail_o <= trap_detail(s_kernel_err_reason_q, s_pc_q, s_instruction_q);
|
||||
s_kernel_err_q <= 1'b0;
|
||||
end else if (s_trap_now) begin
|
||||
`RETROSOC_APU__SET_TRAP(s_trap_detail);
|
||||
end else if (abort_i) begin
|
||||
s_state_q <= Idle;
|
||||
abort_done_o <= 1'b1;
|
||||
end else if (stall_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_kernel_pending_q <= 1'b0;
|
||||
s_kernel_done_q <= 1'b0;
|
||||
s_kernel_err_q <= 1'b0;
|
||||
abort_done_o <= 1'b1;
|
||||
end else if (s_execute_stall) begin
|
||||
s_stat_q[19] <= 1'b1;
|
||||
if (s_watchdog_q + 1'b1 >= timeout_i) begin
|
||||
`RETROSOC_APU__SET_TRAP(trap_detail(8'd7, s_pc_q, s_instruction_q));
|
||||
@@ -384,10 +574,19 @@ module apu_codec_sequencer (
|
||||
end
|
||||
end else if (!instruction_encoding_valid(s_instruction_q)) begin
|
||||
`RETROSOC_APU__SET_TRAP(trap_detail(8'd1, s_pc_q, s_instruction_q));
|
||||
end else if ((s_class >= 4'd2) || ((s_class == 4'd0) && (s_opcode == 4'd8))) begin
|
||||
end else if ((!EnableP4 && ((s_class >= 4'd2) ||
|
||||
((s_class == 4'd0) && (s_opcode == 4'd8)))) ||
|
||||
(EnableP4 && ((s_class == 4'd6) ||
|
||||
((s_class == 4'd0) && (s_opcode == 4'd8) &&
|
||||
!(s_aux inside {8'd1, 8'd2, 8'd3}))))) begin
|
||||
`RETROSOC_APU__SET_TRAP(trap_detail(8'd6, s_pc_q, s_instruction_q));
|
||||
end else if (s_frame_retired_q >= s_max_retired_q) begin
|
||||
`RETROSOC_APU__SET_TRAP(trap_detail(8'd8, s_pc_q, s_instruction_q));
|
||||
end else if (s_predicate_true && s_primitive_class &&
|
||||
(s_class != `APB4_APU__MC_CLASS_KERNEL)) begin
|
||||
s_watchdog_q <= s_watchdog_q + 1'b1;
|
||||
s_stat_q[19] <= 1'b1;
|
||||
s_state_q <= PrimitiveWait;
|
||||
end else begin
|
||||
s_watchdog_q <= 32'd0;
|
||||
s_stat_q[19] <= 1'b0;
|
||||
@@ -487,12 +686,35 @@ module apu_codec_sequencer (
|
||||
s_state_q <= FetchRequest;
|
||||
end
|
||||
end
|
||||
4'd8: begin
|
||||
if ((s_pc_q == s_last_pc_q) || (s_pc_q == 11'd2047)) begin
|
||||
`RETROSOC_APU__SET_TRAP(trap_detail(8'd2, s_pc_q, s_instruction_q));
|
||||
s_frame_retired_q <= s_frame_retired_q;
|
||||
s_perf_retired_q <= s_perf_retired_q;
|
||||
end else begin
|
||||
s_pc_q <= s_pc_q + 1'b1;
|
||||
s_state_q <= FetchRequest;
|
||||
end
|
||||
end
|
||||
default: begin
|
||||
`RETROSOC_APU__SET_TRAP(trap_detail(8'd1, s_pc_q, s_instruction_q));
|
||||
s_frame_retired_q <= s_frame_retired_q;
|
||||
s_perf_retired_q <= s_perf_retired_q;
|
||||
end
|
||||
endcase
|
||||
end else if (s_class == `APB4_APU__MC_CLASS_KERNEL) begin
|
||||
s_kernel_pending_q <= 1'b1;
|
||||
s_kernel_done_q <= 1'b0;
|
||||
s_kernel_dst_q <= s_dst;
|
||||
if ((s_pc_q == s_last_pc_q) || (s_pc_q == 11'd2047)) begin
|
||||
`RETROSOC_APU__SET_TRAP(trap_detail(8'd2, s_pc_q, s_instruction_q));
|
||||
s_frame_retired_q <= s_frame_retired_q;
|
||||
s_perf_retired_q <= s_perf_retired_q;
|
||||
s_kernel_pending_q <= 1'b0;
|
||||
end else begin
|
||||
s_pc_q <= s_pc_q + 1'b1;
|
||||
s_state_q <= FetchRequest;
|
||||
end
|
||||
end else begin
|
||||
unique case (s_opcode)
|
||||
4'd0: s_gpr_q[s_dst] <= s_source0;
|
||||
@@ -537,6 +759,54 @@ module apu_codec_sequencer (
|
||||
end
|
||||
end
|
||||
end
|
||||
PrimitiveWait: begin
|
||||
s_stat_q[19] <= 1'b1;
|
||||
if (primitive_result_valid_i) begin
|
||||
if (primitive_error_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_trapped_q <= 1'b1;
|
||||
trap_event_o <= 1'b1;
|
||||
fault_valid_o <= 1'b1;
|
||||
fault_code_o <= primitive_error_code_i;
|
||||
fault_stage_o <= primitive_error_stage_i;
|
||||
fault_resp_o <= 2'd0;
|
||||
fault_index_o <= 8'd0;
|
||||
fault_addr_o <= {18'd0, s_pc_q, 3'd0};
|
||||
fault_detail_o <= trap_detail(primitive_error_reason_i, s_pc_q, s_instruction_q);
|
||||
s_stat_q <= {11'd0, |s_loop_active_q, 1'b1, s_opcode, s_class, s_pc_q};
|
||||
end else begin
|
||||
for (int index = 0; index < 4; index++) begin
|
||||
if (index < primitive_result_words_i) begin
|
||||
s_gpr_q[primitive_result_dst_i+4'(index)] <= primitive_result_data_i[index];
|
||||
end
|
||||
end
|
||||
s_watchdog_q <= 32'd0;
|
||||
s_stat_q[19] <= 1'b0;
|
||||
s_frame_retired_q <= s_frame_retired_q + 1'b1;
|
||||
if (!counter_clear_i && !(&s_perf_retired_q)) begin
|
||||
s_perf_retired_q <= s_perf_retired_q + 1'b1;
|
||||
end
|
||||
if ((s_pc_q == s_last_pc_q) || (s_pc_q == 11'd2047)) begin
|
||||
`RETROSOC_APU__SET_TRAP(trap_detail(8'd2, s_pc_q, s_instruction_q));
|
||||
s_frame_retired_q <= s_frame_retired_q;
|
||||
s_perf_retired_q <= s_perf_retired_q;
|
||||
end else begin
|
||||
s_pc_q <= s_pc_q + 1'b1;
|
||||
s_state_q <= FetchRequest;
|
||||
end
|
||||
end
|
||||
end else if (abort_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_kernel_pending_q <= 1'b0;
|
||||
s_kernel_done_q <= 1'b0;
|
||||
s_kernel_err_q <= 1'b0;
|
||||
abort_done_o <= 1'b1;
|
||||
end else if (s_watchdog_q + 1'b1 >= timeout_i) begin
|
||||
`RETROSOC_APU__SET_TRAP(trap_detail(8'd7, s_pc_q, s_instruction_q));
|
||||
end else begin
|
||||
s_watchdog_q <= s_watchdog_q + 1'b1;
|
||||
end
|
||||
end
|
||||
default: s_state_q <= Idle;
|
||||
endcase
|
||||
end
|
||||
|
||||
@@ -307,6 +307,15 @@
|
||||
`define APB4_APU__APUMC_ENTRY_WORDS 8
|
||||
`define APB4_APU__APUMC_ENTRY_COUNT 3
|
||||
`define APB4_APU__APUMC_MAX_INSTRUCTIONS 2048
|
||||
`define APB4_APU__APUMC_P3_PRIMITIVE_MASK 32'h0000_0000
|
||||
`define APB4_APU__APUMC_P4_PRIMITIVE_MASK 32'h0000_ffff
|
||||
`define APB4_APU__LOCAL_DATA_BYTES 114688
|
||||
`define APB4_APU__LOCAL_CODEC_BYTES 40960
|
||||
`define APB4_APU__LOCAL_TABLE_SCRATCH_BYTES 24576
|
||||
`define APB4_APU__LOCAL_INPUT_BASE 24576
|
||||
`define APB4_APU__LOCAL_OUTPUT_BASE 32768
|
||||
`define APB4_APU__LOCAL_KWS_BASE 40960
|
||||
`define APB4_APU__LOCAL_INTERNAL_BASE 106496
|
||||
`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,454 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
`include "apu_define.svh"
|
||||
|
||||
module apu_entropy_engine (
|
||||
// verilog_format: off -- preserve command, memory, and reservoir columns
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic flush_i,
|
||||
input logic req_valid_i,
|
||||
output logic req_ready_o,
|
||||
input logic [ 3:0] opcode_i,
|
||||
input logic [ 3:0] dst_i,
|
||||
input logic [ 7:0] aux_i,
|
||||
input logic [31:0] immediate_i,
|
||||
input logic [31:0] source0_i,
|
||||
input logic [31:0] source1_i,
|
||||
input logic [15:0] table_offset_i,
|
||||
output logic memory_req_o,
|
||||
output logic [16:0] memory_addr_o,
|
||||
input logic memory_valid_i,
|
||||
input logic [31:0] memory_data_i,
|
||||
input logic memory_error_i,
|
||||
output logic bit_req_valid_o,
|
||||
input logic bit_req_ready_i,
|
||||
output logic [ 2:0] bit_req_op_o,
|
||||
output logic [ 5:0] bit_req_width_o,
|
||||
input logic bit_result_valid_i,
|
||||
input logic [31:0] bit_result_data_i,
|
||||
input logic bit_error_i,
|
||||
output logic result_valid_o,
|
||||
output logic [ 3:0] result_dst_o,
|
||||
output logic [ 3:0][31:0] result_data_o,
|
||||
output logic [ 2:0] result_words_o,
|
||||
output logic error_o,
|
||||
output logic [ 5:0] error_code_o,
|
||||
output logic [ 3:0] error_stage_o,
|
||||
output logic [31:0] cycles_o
|
||||
// verilog_format: on
|
||||
);
|
||||
typedef enum logic [3:0] {
|
||||
Idle,
|
||||
TableRequest,
|
||||
TableWait,
|
||||
BitRequest,
|
||||
BitWait,
|
||||
ConsumeRequest,
|
||||
ConsumeWait,
|
||||
UnaryRequest,
|
||||
UnaryWait,
|
||||
RiceRequest,
|
||||
RiceWait,
|
||||
ValueRequest,
|
||||
ValueWait,
|
||||
SignRequest,
|
||||
SignWait
|
||||
} state_e;
|
||||
|
||||
state_e s_state_q;
|
||||
logic [3:0] s_opcode_q, s_dst_q;
|
||||
logic [4:0] s_aux_q;
|
||||
logic [31:0] s_immediate_q, s_source0_q;
|
||||
logic [15:0] s_table_offset_q;
|
||||
logic [31:0] s_entry_index_q, s_entry_count_q, s_entry_issue_q;
|
||||
logic [24:0] s_code_q, s_candidate_code_q;
|
||||
logic [4:0] s_previous_len_q, s_len_q;
|
||||
logic [31:0] s_symbol_q, s_run_q, s_quotient_q, s_magnitude_q;
|
||||
logic [5:0] s_value_width_q;
|
||||
logic s_unused;
|
||||
logic s_direct_bit_req, s_huff_peek_req, s_huff_consume_req;
|
||||
logic s_unary_continue_req, s_rice_continue_req, s_rice_value_req, s_sign_req;
|
||||
logic [5:0] s_rice_width;
|
||||
logic s_direct_huff_bit_req, s_huff_entry_valid, s_huff_entry_match;
|
||||
logic [5:0] s_huff_width_q;
|
||||
logic [31:0] s_huff_bits_q, s_huff_prefix;
|
||||
logic s_huff_peek_pending_q;
|
||||
|
||||
assign req_ready_o = s_state_q == Idle;
|
||||
assign s_rice_width = (s_opcode_q == `APB4_APU__MC_ENTROPY_RICE4) ?
|
||||
{2'd0, s_source0_q[3:0]} : {1'b0, s_source0_q[4:0]};
|
||||
assign s_direct_huff_bit_req = (s_state_q == Idle) && req_valid_i && req_ready_o &&
|
||||
(opcode_i <= `APB4_APU__MC_ENTROPY_HUFF_QUAD) &&
|
||||
(source1_i >= 32'd1) && (source1_i <= 32'd4096);
|
||||
assign s_direct_bit_req = (s_state_q == Idle) && req_valid_i && req_ready_o &&
|
||||
(opcode_i > `APB4_APU__MC_ENTROPY_HUFF_QUAD) &&
|
||||
!((opcode_i == `APB4_APU__MC_ENTROPY_SIGN_RESTORE) && (source0_i == 32'd0)) &&
|
||||
!((opcode_i inside {`APB4_APU__MC_ENTROPY_RICE4,
|
||||
`APB4_APU__MC_ENTROPY_RICE5}) &&
|
||||
(((opcode_i == `APB4_APU__MC_ENTROPY_RICE4) && (source0_i[3:0] == 4'd15)) ||
|
||||
((opcode_i == `APB4_APU__MC_ENTROPY_RICE5) && (source0_i[4:0] == 5'd31))) &&
|
||||
((source1_i[5:0] == 6'd0) || (source1_i[5:0] > 6'd32)));
|
||||
assign s_huff_peek_req = (s_state_q == BitWait) && !s_huff_peek_pending_q &&
|
||||
bit_result_valid_i && !bit_error_i && (bit_result_data_i[6:0] != 7'd0);
|
||||
assign s_huff_consume_req = (s_state_q == TableRequest) && memory_valid_i &&
|
||||
!memory_error_i && s_huff_entry_match;
|
||||
assign s_unary_continue_req = (s_state_q == UnaryWait) && bit_result_valid_i && !bit_error_i &&
|
||||
(bit_result_data_i[0] == s_aux_q[0]) && (s_run_q < s_immediate_q);
|
||||
assign s_rice_continue_req = (s_state_q == RiceWait) && bit_result_valid_i && !bit_error_i &&
|
||||
!bit_result_data_i[0] && (s_quotient_q < 32'd65535);
|
||||
assign s_rice_value_req = (s_state_q == RiceWait) && bit_result_valid_i && !bit_error_i &&
|
||||
bit_result_data_i[0] && (s_rice_width != 6'd0);
|
||||
assign s_sign_req = (s_state_q == ValueWait) && bit_result_valid_i && !bit_error_i &&
|
||||
(s_opcode_q == `APB4_APU__MC_ENTROPY_SIGN_RESTORE);
|
||||
assign memory_req_o = (s_state_q == TableRequest) && (s_entry_issue_q < s_entry_count_q);
|
||||
assign memory_addr_o = 17'({16'd0, s_table_offset_q} + s_source0_q + (s_entry_issue_q << 2));
|
||||
assign bit_req_valid_o = s_direct_huff_bit_req || s_direct_bit_req || s_huff_peek_req ||
|
||||
s_huff_consume_req || s_unary_continue_req || s_rice_continue_req ||
|
||||
s_rice_value_req || s_sign_req || (s_state_q inside {
|
||||
BitRequest, ConsumeRequest, UnaryRequest, RiceRequest, ValueRequest, SignRequest
|
||||
});
|
||||
assign bit_req_op_o = s_direct_huff_bit_req ? 3'd0 :
|
||||
(((s_state_q == BitRequest) || s_huff_peek_req) ? 3'd1 : 3'd2);
|
||||
assign bit_req_width_o = s_direct_huff_bit_req ? {1'b0, aux_i[4:0]} :
|
||||
(s_direct_bit_req ?
|
||||
((opcode_i == `APB4_APU__MC_ENTROPY_UNARY) ? 6'd1 :
|
||||
((opcode_i inside {`APB4_APU__MC_ENTROPY_RICE4,
|
||||
`APB4_APU__MC_ENTROPY_RICE5}) ?
|
||||
((((opcode_i == `APB4_APU__MC_ENTROPY_RICE4) && (source0_i[3:0] == 4'd15)) ||
|
||||
((opcode_i == `APB4_APU__MC_ENTROPY_RICE5) && (source0_i[4:0] == 5'd31))) ?
|
||||
source1_i[5:0] : 6'd1) :
|
||||
((source1_i[4:0] == 5'd0) ? 6'd1 : {1'b0, source1_i[4:0]}))) :
|
||||
(s_huff_peek_req ? bit_result_data_i[5:0] :
|
||||
(s_huff_consume_req ? {1'b0, memory_data_i[20:16]} :
|
||||
(s_rice_value_req ? s_rice_width :
|
||||
((s_state_q inside {UnaryRequest, RiceRequest, SignRequest}) ||
|
||||
s_unary_continue_req || s_rice_continue_req || s_sign_req ? 6'd1 :
|
||||
((s_state_q == ValueRequest) ? s_value_width_q : {1'b0, s_len_q}))))));
|
||||
|
||||
always_comb begin
|
||||
s_candidate_code_q = s_code_q << (memory_data_i[20:16] - s_previous_len_q);
|
||||
s_huff_entry_valid = (memory_data_i[31:21] == 11'd0) &&
|
||||
(memory_data_i[20:16] >= s_previous_len_q) &&
|
||||
(memory_data_i[20:16] != 5'd0) && (memory_data_i[20:16] <= s_aux_q) &&
|
||||
(s_candidate_code_q < (25'd1 << memory_data_i[20:16]));
|
||||
s_huff_prefix = 32'd0;
|
||||
if ({1'b0, memory_data_i[20:16]} <= s_huff_width_q) begin
|
||||
s_huff_prefix = s_huff_bits_q >> (s_huff_width_q - memory_data_i[20:16]);
|
||||
end
|
||||
s_huff_entry_match = s_huff_entry_valid &&
|
||||
({1'b0, memory_data_i[20:16]} <= s_huff_width_q) &&
|
||||
(s_huff_prefix == {7'd0, s_candidate_code_q});
|
||||
end
|
||||
always_comb begin
|
||||
if (s_opcode_q <= `APB4_APU__MC_ENTROPY_HUFF_QUAD) begin
|
||||
cycles_o = 32'd26 + s_entry_count_q;
|
||||
end else if (s_opcode_q == `APB4_APU__MC_ENTROPY_UNARY) begin
|
||||
cycles_o = 32'd2 + s_run_q;
|
||||
end else if (s_opcode_q inside {`APB4_APU__MC_ENTROPY_RICE4, `APB4_APU__MC_ENTROPY_RICE5}) begin
|
||||
if (((s_opcode_q == `APB4_APU__MC_ENTROPY_RICE4) &&
|
||||
(s_source0_q[3:0] == 4'd15)) ||
|
||||
((s_opcode_q == `APB4_APU__MC_ENTROPY_RICE5) &&
|
||||
(s_source0_q[4:0] == 5'd31))) begin
|
||||
cycles_o = 32'd4 + {26'd0, s_value_width_q};
|
||||
end else begin
|
||||
cycles_o = 32'd4 + s_quotient_q + {26'd0, s_value_width_q};
|
||||
end
|
||||
end else begin
|
||||
cycles_o = 32'd3 + {26'd0, s_value_width_q};
|
||||
end
|
||||
end
|
||||
assign s_unused = ^aux_i[7:5] && 1'b0;
|
||||
|
||||
function automatic logic [31:0] rice_signed(
|
||||
input logic [31:0] quotient_i, input logic [31:0] remainder_i, input logic [5:0] width_i);
|
||||
logic [31:0] s_unsigned;
|
||||
begin
|
||||
s_unsigned = (quotient_i << width_i) | remainder_i;
|
||||
return (s_unsigned >> 1) ^ (32'd0 - s_unsigned[0]);
|
||||
end
|
||||
endfunction
|
||||
|
||||
`define RETROSOC_APU__ENTROPY_ERROR(CODE) \
|
||||
begin \
|
||||
s_state_q <= Idle; \
|
||||
error_o <= 1'b1; \
|
||||
error_code_o <= CODE; \
|
||||
error_stage_o <= `APB4_APU__ERROR_STAGE_ENTROPY; \
|
||||
result_valid_o <= 1'b1; \
|
||||
result_words_o <= 3'd0; \
|
||||
end
|
||||
|
||||
`define RETROSOC_APU__ENTROPY_LOCAL_ERROR \
|
||||
begin \
|
||||
s_state_q <= Idle; \
|
||||
error_o <= 1'b1; \
|
||||
error_code_o <= `APB4_APU__ERROR_CODE_SEQUENCER; \
|
||||
error_stage_o <= `APB4_APU__ERROR_STAGE_LIFECYCLE; \
|
||||
result_valid_o <= 1'b1; \
|
||||
result_words_o <= 3'd0; \
|
||||
end
|
||||
|
||||
`define RETROSOC_APU__ENTROPY_FINISH_ONE(VALUE) \
|
||||
begin \
|
||||
s_state_q <= Idle; \
|
||||
result_valid_o <= 1'b1; \
|
||||
result_words_o <= 3'd1; \
|
||||
result_dst_o <= s_dst_q; \
|
||||
result_data_o[0] <= VALUE; \
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_opcode_q <= 4'd0;
|
||||
s_dst_q <= 4'd0;
|
||||
s_aux_q <= 5'd0;
|
||||
s_immediate_q <= 32'd0;
|
||||
s_source0_q <= 32'd0;
|
||||
s_table_offset_q <= 16'd0;
|
||||
s_entry_index_q <= 32'd0;
|
||||
s_entry_count_q <= 32'd0;
|
||||
s_entry_issue_q <= 32'd0;
|
||||
s_code_q <= 25'd0;
|
||||
s_previous_len_q <= 5'd0;
|
||||
s_len_q <= 5'd0;
|
||||
s_symbol_q <= 32'd0;
|
||||
s_run_q <= 32'd0;
|
||||
s_quotient_q <= 32'd0;
|
||||
s_magnitude_q <= 32'd0;
|
||||
s_value_width_q <= 6'd0;
|
||||
s_huff_width_q <= 6'd0;
|
||||
s_huff_bits_q <= 32'd0;
|
||||
s_huff_peek_pending_q <= 1'b0;
|
||||
result_valid_o <= 1'b0;
|
||||
result_dst_o <= 4'd0;
|
||||
result_data_o <= '0;
|
||||
result_words_o <= 3'd0;
|
||||
error_o <= 1'b0;
|
||||
error_code_o <= 6'd0;
|
||||
error_stage_o <= 4'd0;
|
||||
end else if (flush_i) begin
|
||||
s_state_q <= Idle;
|
||||
result_valid_o <= 1'b0;
|
||||
error_o <= 1'b0;
|
||||
end else begin
|
||||
result_valid_o <= 1'b0;
|
||||
error_o <= 1'b0;
|
||||
unique case (s_state_q)
|
||||
Idle: begin
|
||||
if (req_valid_i) begin
|
||||
s_opcode_q <= opcode_i;
|
||||
s_dst_q <= dst_i;
|
||||
s_aux_q <= aux_i[4:0];
|
||||
s_immediate_q <= immediate_i;
|
||||
s_source0_q <= source0_i;
|
||||
s_table_offset_q <= table_offset_i;
|
||||
s_entry_index_q <= 32'd0;
|
||||
s_entry_count_q <= source1_i;
|
||||
s_entry_issue_q <= 32'd0;
|
||||
s_code_q <= 25'd0;
|
||||
s_previous_len_q <= 5'd0;
|
||||
s_run_q <= 32'd0;
|
||||
s_quotient_q <= 32'd0;
|
||||
s_huff_peek_pending_q <= 1'b0;
|
||||
if (opcode_i <= `APB4_APU__MC_ENTROPY_HUFF_QUAD) begin
|
||||
if ((source1_i == 32'd0) || (source1_i > 32'd4096)) begin
|
||||
`RETROSOC_APU__ENTROPY_ERROR(6'd7)
|
||||
end else begin
|
||||
s_state_q <= s_direct_huff_bit_req ? BitWait : BitRequest;
|
||||
end
|
||||
end else if (opcode_i == `APB4_APU__MC_ENTROPY_UNARY) begin
|
||||
s_state_q <= s_direct_bit_req ? UnaryWait : UnaryRequest;
|
||||
end else if (opcode_i inside {`APB4_APU__MC_ENTROPY_RICE4,
|
||||
`APB4_APU__MC_ENTROPY_RICE5}) begin
|
||||
if (((opcode_i == `APB4_APU__MC_ENTROPY_RICE4) &&
|
||||
(source0_i[3:0] == 4'd15)) ||
|
||||
((opcode_i == `APB4_APU__MC_ENTROPY_RICE5) &&
|
||||
(source0_i[4:0] == 5'd31))) begin
|
||||
s_value_width_q <= source1_i[5:0];
|
||||
if ((source1_i[5:0] == 6'd0) || (source1_i[5:0] > 6'd32)) begin
|
||||
`RETROSOC_APU__ENTROPY_ERROR(6'd7)
|
||||
end else begin
|
||||
s_state_q <= s_direct_bit_req ? ValueWait : ValueRequest;
|
||||
end
|
||||
end else begin
|
||||
s_state_q <= s_direct_bit_req ? RiceWait : RiceRequest;
|
||||
end
|
||||
end else begin
|
||||
s_magnitude_q <= source0_i;
|
||||
if (source0_i == 32'd0) begin
|
||||
`RETROSOC_APU__ENTROPY_FINISH_ONE(32'd0)
|
||||
end else begin
|
||||
s_value_width_q <= {1'b0, source1_i[4:0]};
|
||||
if (source1_i[4:0] == 5'd0) begin
|
||||
s_state_q <= s_direct_bit_req ? SignWait : SignRequest;
|
||||
end else begin
|
||||
s_state_q <= s_direct_bit_req ? ValueWait : ValueRequest;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
TableRequest: begin
|
||||
if (memory_req_o) s_entry_issue_q <= s_entry_issue_q + 1'b1;
|
||||
if (memory_error_i) begin
|
||||
`RETROSOC_APU__ENTROPY_LOCAL_ERROR
|
||||
end else if (memory_valid_i) begin
|
||||
if (!s_huff_entry_valid) begin
|
||||
`RETROSOC_APU__ENTROPY_ERROR(6'd7);
|
||||
end else if ({1'b0, memory_data_i[20:16]} > s_huff_width_q) begin
|
||||
`RETROSOC_APU__ENTROPY_ERROR(6'd5);
|
||||
end else if (s_huff_entry_match) begin
|
||||
s_len_q <= memory_data_i[20:16];
|
||||
s_symbol_q <= {16'd0, memory_data_i[15:0]};
|
||||
s_state_q <= s_huff_consume_req ? ConsumeWait : ConsumeRequest;
|
||||
end else if (s_entry_index_q + 1'b1 >= s_entry_count_q) begin
|
||||
`RETROSOC_APU__ENTROPY_ERROR(6'd7);
|
||||
end else begin
|
||||
s_code_q <= s_candidate_code_q + 1'b1;
|
||||
s_previous_len_q <= memory_data_i[20:16];
|
||||
s_entry_index_q <= s_entry_index_q + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
TableWait: s_state_q <= TableRequest;
|
||||
BitRequest: begin
|
||||
if (bit_req_ready_i) s_state_q <= BitWait;
|
||||
end
|
||||
BitWait: begin
|
||||
if (bit_error_i) begin
|
||||
`RETROSOC_APU__ENTROPY_ERROR(6'd5);
|
||||
end else if (bit_result_valid_i) begin
|
||||
if (!s_huff_peek_pending_q) begin
|
||||
if (bit_result_data_i[6:0] == 7'd0) begin
|
||||
`RETROSOC_APU__ENTROPY_ERROR(6'd5);
|
||||
end else begin
|
||||
s_huff_width_q <= bit_result_data_i[5:0];
|
||||
s_huff_peek_pending_q <= 1'b1;
|
||||
end
|
||||
end else begin
|
||||
s_huff_bits_q <= bit_result_data_i;
|
||||
s_entry_index_q <= 32'd0;
|
||||
s_entry_issue_q <= 32'd0;
|
||||
s_code_q <= 25'd0;
|
||||
s_previous_len_q <= 5'd0;
|
||||
s_huff_peek_pending_q <= 1'b0;
|
||||
s_state_q <= TableRequest;
|
||||
end
|
||||
end
|
||||
end
|
||||
ConsumeRequest: begin
|
||||
if (bit_req_ready_i) s_state_q <= ConsumeWait;
|
||||
end
|
||||
ConsumeWait: begin
|
||||
if (bit_error_i) begin
|
||||
`RETROSOC_APU__ENTROPY_ERROR(6'd5);
|
||||
end else if (bit_result_valid_i) begin
|
||||
s_state_q <= Idle;
|
||||
result_valid_o <= 1'b1;
|
||||
result_dst_o <= s_dst_q;
|
||||
if (s_opcode_q == `APB4_APU__MC_ENTROPY_HUFF_SYMBOL) begin
|
||||
result_words_o <= 3'd1;
|
||||
result_data_o[0] <= s_symbol_q;
|
||||
end else if (s_opcode_q == `APB4_APU__MC_ENTROPY_HUFF_PAIR) begin
|
||||
result_words_o <= 3'd2;
|
||||
result_data_o[0] <= {28'd0, s_symbol_q[7:4]};
|
||||
result_data_o[1] <= {28'd0, s_symbol_q[3:0]};
|
||||
end else begin
|
||||
result_words_o <= 3'd4;
|
||||
for (int index = 0; index < 4; index++) begin
|
||||
result_data_o[index] <= {31'd0, s_symbol_q[3-index]};
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
UnaryRequest, RiceRequest, ValueRequest, SignRequest: begin
|
||||
if (bit_req_ready_i) begin
|
||||
unique case (s_state_q)
|
||||
UnaryRequest: s_state_q <= UnaryWait;
|
||||
RiceRequest: s_state_q <= RiceWait;
|
||||
ValueRequest: s_state_q <= ValueWait;
|
||||
default: s_state_q <= SignWait;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
UnaryWait: begin
|
||||
if (bit_error_i) begin
|
||||
`RETROSOC_APU__ENTROPY_ERROR(6'd5)
|
||||
end else if (bit_result_valid_i) begin
|
||||
if (bit_result_data_i[0] != s_aux_q[0]) begin
|
||||
`RETROSOC_APU__ENTROPY_FINISH_ONE(s_run_q)
|
||||
end else if (s_run_q >= s_immediate_q) begin
|
||||
`RETROSOC_APU__ENTROPY_ERROR(6'd7)
|
||||
end else begin
|
||||
s_run_q <= s_run_q + 1'b1;
|
||||
s_state_q <= s_unary_continue_req ? UnaryWait : UnaryRequest;
|
||||
end
|
||||
end
|
||||
end
|
||||
RiceWait: begin
|
||||
if (bit_error_i) begin
|
||||
`RETROSOC_APU__ENTROPY_ERROR(6'd5)
|
||||
end else if (bit_result_valid_i) begin
|
||||
if (bit_result_data_i[0]) begin
|
||||
s_value_width_q <= (s_opcode_q == `APB4_APU__MC_ENTROPY_RICE4) ?
|
||||
{2'd0, s_source0_q[3:0]} : {1'd0, s_source0_q[4:0]};
|
||||
if (((s_opcode_q == `APB4_APU__MC_ENTROPY_RICE4) &&
|
||||
(s_source0_q[3:0] == 4'd0)) ||
|
||||
((s_opcode_q == `APB4_APU__MC_ENTROPY_RICE5) &&
|
||||
(s_source0_q[4:0] == 5'd0))) begin
|
||||
`RETROSOC_APU__ENTROPY_FINISH_ONE((s_quotient_q >> 1) ^ (32'd0 - s_quotient_q[0]));
|
||||
end else begin
|
||||
s_state_q <= s_rice_value_req ? ValueWait : ValueRequest;
|
||||
end
|
||||
end else if (s_quotient_q == 32'd65535) begin
|
||||
`RETROSOC_APU__ENTROPY_ERROR(6'd7);
|
||||
end else begin
|
||||
s_quotient_q <= s_quotient_q + 1'b1;
|
||||
s_state_q <= s_rice_continue_req ? RiceWait : RiceRequest;
|
||||
end
|
||||
end
|
||||
end
|
||||
ValueWait: begin
|
||||
if (bit_error_i) begin
|
||||
`RETROSOC_APU__ENTROPY_ERROR(6'd5)
|
||||
end else if (bit_result_valid_i) begin
|
||||
if (s_opcode_q inside {`APB4_APU__MC_ENTROPY_RICE4, `APB4_APU__MC_ENTROPY_RICE5}) begin
|
||||
if (((s_opcode_q == `APB4_APU__MC_ENTROPY_RICE4) &&
|
||||
(s_source0_q[3:0] == 4'd15)) ||
|
||||
((s_opcode_q == `APB4_APU__MC_ENTROPY_RICE5) &&
|
||||
(s_source0_q[4:0] == 5'd31))) begin
|
||||
`RETROSOC_APU__ENTROPY_FINISH_ONE($unsigned(
|
||||
$signed(
|
||||
bit_result_data_i << (6'd32 - s_value_width_q)
|
||||
) >>> (6'd32 - s_value_width_q)
|
||||
));
|
||||
end else begin
|
||||
`RETROSOC_APU__ENTROPY_FINISH_ONE(
|
||||
rice_signed(s_quotient_q, bit_result_data_i, s_value_width_q));
|
||||
end
|
||||
end else begin
|
||||
s_magnitude_q <= s_magnitude_q + bit_result_data_i;
|
||||
s_state_q <= s_sign_req ? SignWait : SignRequest;
|
||||
end
|
||||
end
|
||||
end
|
||||
SignWait: begin
|
||||
if (bit_error_i) begin
|
||||
`RETROSOC_APU__ENTROPY_ERROR(6'd5)
|
||||
end else if (bit_result_valid_i) begin
|
||||
`RETROSOC_APU__ENTROPY_FINISH_ONE(
|
||||
bit_result_data_i[0] ? (32'd0 - s_magnitude_q) : s_magnitude_q);
|
||||
end
|
||||
end
|
||||
default: s_state_q <= Idle;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
`undef RETROSOC_APU__ENTROPY_ERROR
|
||||
`undef RETROSOC_APU__ENTROPY_LOCAL_ERROR
|
||||
`undef RETROSOC_APU__ENTROPY_FINISH_ONE
|
||||
endmodule
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,149 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
`include "apu_define.svh"
|
||||
|
||||
module apu_local_sram #(
|
||||
parameter int unsigned MemoryWordCount = `APB4_APU__LOCAL_DATA_BYTES / 4
|
||||
) (
|
||||
// verilog_format: off -- preserve loader and codec port columns
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic image_valid_i,
|
||||
input logic [15:0] table_bytes_i,
|
||||
input logic epoch_clear_i,
|
||||
input logic loader_active_i,
|
||||
input logic loader_req_i,
|
||||
input logic [16:0] loader_addr_i,
|
||||
input logic [31:0] loader_data_i,
|
||||
input logic [ 3:0] loader_strb_i,
|
||||
output logic loader_ready_o,
|
||||
input logic codec_req_i,
|
||||
input logic codec_write_i,
|
||||
input logic [16:0] codec_addr_i,
|
||||
input logic [31:0] codec_data_i,
|
||||
input logic [ 3:0] codec_strb_i,
|
||||
output logic codec_ready_o,
|
||||
output logic [31:0] codec_data_o,
|
||||
output logic codec_valid_o,
|
||||
output logic codec_access_err_o
|
||||
// verilog_format: on
|
||||
);
|
||||
localparam logic [14:0] MemoryWordCountValue = 15'(MemoryWordCount);
|
||||
|
||||
logic s_req;
|
||||
logic s_write;
|
||||
logic [ 16:0] s_addr;
|
||||
logic [ 31:0] s_write_data;
|
||||
logic [ 3:0] s_write_strb;
|
||||
logic [ 14:0] s_word_addr;
|
||||
logic s_codec_range_ok;
|
||||
logic s_codec_table;
|
||||
logic s_codec_mutable_valid;
|
||||
logic s_codec_access_err;
|
||||
logic [MemoryWordCount-1:0] s_mutable_valid_q;
|
||||
logic s_read_q, s_read_codec_q, s_read_err_q;
|
||||
logic [31:0] s_read_data;
|
||||
logic s_unused;
|
||||
|
||||
assign loader_ready_o = loader_active_i;
|
||||
assign codec_ready_o = !loader_active_i;
|
||||
assign s_req = loader_active_i ? loader_req_i : codec_req_i;
|
||||
assign s_write = loader_active_i || codec_write_i;
|
||||
assign s_addr = loader_active_i ? loader_addr_i : codec_addr_i;
|
||||
assign s_write_data = loader_active_i ? loader_data_i : codec_data_i;
|
||||
assign s_write_strb = loader_active_i ? loader_strb_i : codec_strb_i;
|
||||
assign s_word_addr = s_addr[16:2];
|
||||
assign s_codec_range_ok = (codec_addr_i[1:0] == 2'd0) &&
|
||||
((codec_addr_i < `APB4_APU__LOCAL_CODEC_BYTES) ||
|
||||
((codec_addr_i >= `APB4_APU__LOCAL_INTERNAL_BASE) &&
|
||||
(codec_addr_i < `APB4_APU__LOCAL_DATA_BYTES)));
|
||||
assign s_codec_table = codec_addr_i < {1'b0, table_bytes_i};
|
||||
assign s_codec_mutable_valid = (s_word_addr < MemoryWordCountValue) &&
|
||||
s_mutable_valid_q[s_word_addr];
|
||||
assign s_codec_access_err = codec_req_i &&
|
||||
((s_word_addr >= MemoryWordCountValue) || !s_codec_range_ok ||
|
||||
(codec_write_i && s_codec_table) ||
|
||||
(!codec_write_i && (s_codec_table ? !image_valid_i : !s_codec_mutable_valid)));
|
||||
assign codec_data_o = s_read_data;
|
||||
assign codec_valid_o = s_read_q && s_read_codec_q;
|
||||
assign codec_access_err_o = (codec_req_i && codec_ready_o && s_codec_access_err) ||
|
||||
(codec_valid_o && s_read_err_q);
|
||||
|
||||
`ifdef HAVE_SRAM_MACRO
|
||||
localparam int unsigned BankCount = 28;
|
||||
|
||||
logic [ 4:0] s_bank;
|
||||
logic [ 9:0] s_bank_addr;
|
||||
logic [BankCount-1:0][31:0] s_bank_data;
|
||||
logic [ 4:0] s_read_bank_q;
|
||||
|
||||
assign s_bank = s_word_addr[14:10];
|
||||
assign s_bank_addr = s_word_addr[9:0];
|
||||
|
||||
for (genvar bank = 0; bank < BankCount; bank++) begin : gen_local_bank
|
||||
tc_sram_1024x32 u_local_sram (
|
||||
.clk_i (clk_i),
|
||||
.cs_i (s_req && (s_bank == 5'(bank)) && (loader_active_i || !s_codec_access_err)),
|
||||
.addr_i(s_bank_addr),
|
||||
.data_i(s_write_data),
|
||||
.mask_i(s_write_strb),
|
||||
.wren_i(s_write),
|
||||
.data_o(s_bank_data[bank])
|
||||
);
|
||||
end
|
||||
|
||||
assign s_read_data = s_bank_data[s_read_bank_q];
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_read_bank_q <= 5'd0;
|
||||
end else if (s_req && !s_write && (loader_active_i || !s_codec_access_err)) begin
|
||||
s_read_bank_q <= s_bank;
|
||||
end
|
||||
end
|
||||
`else
|
||||
logic [31:0] mem [0:MemoryWordCount-1];
|
||||
logic [31:0] s_read_data_q;
|
||||
|
||||
assign s_read_data = s_read_data_q;
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (s_req && (loader_active_i || !s_codec_access_err)) begin
|
||||
if (s_write) begin
|
||||
if (s_write_strb[0]) mem[s_word_addr][7:0] <= s_write_data[7:0];
|
||||
if (s_write_strb[1]) mem[s_word_addr][15:8] <= s_write_data[15:8];
|
||||
if (s_write_strb[2]) mem[s_word_addr][23:16] <= s_write_data[23:16];
|
||||
if (s_write_strb[3]) mem[s_word_addr][31:24] <= s_write_data[31:24];
|
||||
end else begin
|
||||
s_read_data_q <= mem[s_word_addr];
|
||||
end
|
||||
end
|
||||
end
|
||||
`endif
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
for (int word_index = 0; word_index < MemoryWordCount; word_index++) begin
|
||||
s_mutable_valid_q[word_index] <= 1'b0;
|
||||
end
|
||||
s_read_q <= 1'b0;
|
||||
s_read_codec_q <= 1'b0;
|
||||
s_read_err_q <= 1'b0;
|
||||
end else begin
|
||||
s_read_q <= s_req && !s_write;
|
||||
s_read_codec_q <= !loader_active_i;
|
||||
s_read_err_q <= !loader_active_i && s_codec_access_err;
|
||||
if (epoch_clear_i) begin
|
||||
for (int word_index = 0; word_index < MemoryWordCount; word_index++) begin
|
||||
s_mutable_valid_q[word_index] <= 1'b0;
|
||||
end
|
||||
end else if (!loader_active_i && codec_req_i && codec_ready_o && codec_write_i &&
|
||||
!s_codec_access_err && (s_word_addr < MemoryWordCountValue)) begin
|
||||
s_mutable_valid_q[s_word_addr] <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assign s_unused = ^s_addr[1:0] && 1'b0;
|
||||
endmodule
|
||||
@@ -4,7 +4,8 @@
|
||||
`include "apu_define.svh"
|
||||
|
||||
module apu_microcode_loader #(
|
||||
parameter int unsigned PathStackDepth = 2048
|
||||
parameter int unsigned PathStackDepth = 2048,
|
||||
parameter bit EnableP4 = 1'b0
|
||||
) (
|
||||
// verilog_format: off -- preserve command, DMA, store, and result columns
|
||||
input logic clk_i,
|
||||
@@ -39,6 +40,11 @@ module apu_microcode_loader #(
|
||||
output logic [63:0] store_data_o,
|
||||
input logic [63:0] store_data_i,
|
||||
input logic store_valid_i,
|
||||
output logic local_write_o,
|
||||
output logic [16:0] local_addr_o,
|
||||
output logic [31:0] local_data_o,
|
||||
output logic [3:0] local_strb_o,
|
||||
output logic [15:0] table_bytes_o,
|
||||
output logic [7:0] stat_o,
|
||||
output logic [31:0] abi_o,
|
||||
output logic [63:0] build_id_o,
|
||||
@@ -50,6 +56,11 @@ module apu_microcode_loader #(
|
||||
output logic [2:0][10:0] entry_last_o,
|
||||
output logic [2:0][15:0] entry_max_loop_o,
|
||||
output logic [2:0][23:0] entry_max_retired_o,
|
||||
output logic [2:0][16:0] entry_scratch_base_o,
|
||||
output logic [2:0][16:0] entry_scratch_bytes_o,
|
||||
output logic [2:0][31:0] entry_primitive_mask_o,
|
||||
output logic [2:0][15:0] entry_table_offset_o,
|
||||
output logic [2:0][15:0] entry_table_bytes_o,
|
||||
output logic load_done_o,
|
||||
output logic abort_done_o,
|
||||
output logic fault_valid_o,
|
||||
@@ -63,7 +74,7 @@ module apu_microcode_loader #(
|
||||
);
|
||||
import apu_microcode_pkg::*;
|
||||
|
||||
localparam logic [11:0] PathStackLimit = PathStackDepth;
|
||||
localparam logic [11:0] PathStackLimit = 12'(PathStackDepth);
|
||||
|
||||
typedef enum logic [4:0] {
|
||||
Idle,
|
||||
@@ -115,7 +126,7 @@ module apu_microcode_loader #(
|
||||
logic s_path_stack_cs, s_path_stack_write, s_path_stack_push;
|
||||
logic [10:0] s_path_stack_addr;
|
||||
logic [63:0] s_path_stack_write_data, s_path_stack_read_data;
|
||||
logic s_path_stop_valid_q;
|
||||
logic s_path_stop_valid_q;
|
||||
logic [10:0] s_path_stop_pc_q;
|
||||
logic s_scan_control_err_q, s_scan_capability_err_q;
|
||||
logic [31:0] s_scan_control_addr_q, s_scan_control_detail_q;
|
||||
@@ -123,6 +134,7 @@ module apu_microcode_loader #(
|
||||
logic s_cancel_resource_q, s_cancel_abort_q;
|
||||
|
||||
logic s_header_err, s_header_range_err, s_range_err, s_table_err, s_capability_err;
|
||||
logic s_table_header_err;
|
||||
logic [31:0] s_header_addr, s_header_detail;
|
||||
logic [31:0] s_header_range_addr, s_header_range_detail;
|
||||
logic [31:0] s_range_addr, s_range_detail;
|
||||
@@ -136,7 +148,7 @@ module apu_microcode_loader #(
|
||||
logic [ 4:0] s_entry_word_relative;
|
||||
logic [63:0] s_scan_instruction;
|
||||
logic [3:0] s_scan_class, s_scan_opcode, s_scan_predicate;
|
||||
logic [ 1:0] s_scan_aux;
|
||||
logic [ 7:0] s_scan_aux;
|
||||
logic [10:0] s_scan_immediate;
|
||||
logic s_scan_last;
|
||||
logic s_scan_instruction_err, s_scan_target_err, s_scan_call_err, s_scan_loop_err;
|
||||
@@ -172,6 +184,12 @@ module apu_microcode_loader #(
|
||||
}) ?
|
||||
s_scan_pc_q : s_instruction_word_relative[11:1];
|
||||
assign store_data_o = {dma_data_i, s_instruction_low_q};
|
||||
assign local_write_o = EnableP4 && (s_state_q == PayloadReceive) && s_data_accept &&
|
||||
s_data_in_table;
|
||||
assign local_addr_o = 17'(s_byte_offset_q - s_header_q[5]);
|
||||
assign local_data_o = dma_data_i;
|
||||
assign local_strb_o = dma_keep_i;
|
||||
assign table_bytes_o = s_header_q[6][15:0];
|
||||
assign idle_o = s_state_q == Idle;
|
||||
assign stat_o = {
|
||||
s_err_bits_q[5],
|
||||
@@ -190,11 +208,16 @@ module apu_microcode_loader #(
|
||||
assign load_count_o = s_load_count_q;
|
||||
|
||||
for (genvar entry = 0; entry < 3; entry++) begin : gen_entry_outputs
|
||||
assign entry_pc_o[entry] = s_entry_word_q[entry*8][14:4];
|
||||
assign entry_first_o[entry] = s_entry_word_q[(entry*8)+1][10:0];
|
||||
assign entry_last_o[entry] = s_entry_word_q[(entry*8)+1][26:16];
|
||||
assign entry_max_loop_o[entry] = s_entry_word_q[(entry*8)+4][15:0];
|
||||
assign entry_max_retired_o[entry] = s_entry_word_q[(entry*8)+5][23:0];
|
||||
assign entry_pc_o[entry] = s_entry_word_q[entry*8][14:4];
|
||||
assign entry_first_o[entry] = s_entry_word_q[(entry*8)+1][10:0];
|
||||
assign entry_last_o[entry] = s_entry_word_q[(entry*8)+1][26:16];
|
||||
assign entry_max_loop_o[entry] = s_entry_word_q[(entry*8)+4][15:0];
|
||||
assign entry_max_retired_o[entry] = s_entry_word_q[(entry*8)+5][23:0];
|
||||
assign entry_scratch_base_o[entry] = s_entry_word_q[(entry*8)+2][16:0];
|
||||
assign entry_scratch_bytes_o[entry] = s_entry_word_q[(entry*8)+3][16:0];
|
||||
assign entry_primitive_mask_o[entry] = s_entry_word_q[(entry*8)+6];
|
||||
assign entry_table_offset_o[entry] = s_entry_word_q[(entry*8)+7][15:0];
|
||||
assign entry_table_bytes_o[entry] = s_entry_word_q[(entry*8)+7][31:16];
|
||||
end
|
||||
|
||||
assign s_instruction_end = {1'b0, s_header_q[4]} + ({1'b0, s_header_q[3]} << 3);
|
||||
@@ -284,7 +307,8 @@ module apu_microcode_loader #(
|
||||
s_header_range_err = 1'b1;
|
||||
s_header_range_addr = s_source_addr_q + 32'd20;
|
||||
s_header_range_detail = s_header_q[5];
|
||||
end else if (s_header_q[10] != 32'd0) begin
|
||||
end else if ((!EnableP4 && (s_header_q[10] != 32'd0)) ||
|
||||
(EnableP4 && (s_header_q[10] > 32'h0000_6000))) begin
|
||||
s_header_range_err = 1'b1;
|
||||
s_header_range_addr = s_source_addr_q + 32'd40;
|
||||
s_header_range_detail = s_header_q[10];
|
||||
@@ -292,10 +316,15 @@ module apu_microcode_loader #(
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
s_range_err = s_header_range_err;
|
||||
s_range_addr = s_header_range_addr;
|
||||
s_range_detail = s_header_range_detail;
|
||||
logic [31:0] s_scratch_end;
|
||||
logic [31:0] s_max_scratch_end;
|
||||
s_range_err = s_header_range_err;
|
||||
s_range_addr = s_header_range_addr;
|
||||
s_range_detail = s_header_range_detail;
|
||||
s_max_scratch_end = 32'd0;
|
||||
for (int unsigned entry = 0; entry < 3; entry++) begin
|
||||
s_scratch_end = s_entry_word_q[(entry*8)+2] + s_entry_word_q[(entry*8)+3];
|
||||
if (s_scratch_end > s_max_scratch_end) s_max_scratch_end = s_scratch_end;
|
||||
if (!s_range_err && ((s_entry_word_q[entry*8][31:15] != 17'd0) ||
|
||||
(s_entry_word_q[entry*8][3:0] != 4'(entry)))) begin
|
||||
s_range_err = 1'b1;
|
||||
@@ -312,11 +341,19 @@ module apu_microcode_loader #(
|
||||
s_range_err = 1'b1;
|
||||
s_range_addr = s_source_addr_q + s_header_q[7] + 32'(entry * 32) + 32'd4;
|
||||
s_range_detail = s_entry_word_q[(entry*8)+1];
|
||||
end else if (!s_range_err && (s_entry_word_q[(entry*8)+2] != 32'd0)) begin
|
||||
end else if (!s_range_err &&
|
||||
((!EnableP4 && (s_entry_word_q[(entry*8)+2] != 32'd0)) ||
|
||||
(EnableP4 && ((s_entry_word_q[(entry*8)+2][31:17] != 15'd0) ||
|
||||
(s_entry_word_q[(entry*8)+2][1:0] != 2'd0) ||
|
||||
(s_entry_word_q[(entry*8)+3][31:17] != 15'd0) ||
|
||||
(s_entry_word_q[(entry*8)+3][1:0] != 2'd0) ||
|
||||
(s_scratch_end > 32'h0000_6000) ||
|
||||
((s_entry_word_q[(entry*8)+3] != 32'd0) &&
|
||||
(s_entry_word_q[(entry*8)+2] < s_header_q[6])))))) begin
|
||||
s_range_err = 1'b1;
|
||||
s_range_addr = s_source_addr_q + s_header_q[7] + 32'(entry * 32) + 32'd8;
|
||||
s_range_detail = s_entry_word_q[(entry*8)+2];
|
||||
end else if (!s_range_err && (s_entry_word_q[(entry*8)+3] != 32'd0)) begin
|
||||
end else if (!s_range_err && !EnableP4 && (s_entry_word_q[(entry*8)+3] != 32'd0)) begin
|
||||
s_range_err = 1'b1;
|
||||
s_range_addr = s_source_addr_q + s_header_q[7] + 32'(entry * 32) + 32'd12;
|
||||
s_range_detail = s_entry_word_q[(entry*8)+3];
|
||||
@@ -334,21 +371,35 @@ module apu_microcode_loader #(
|
||||
s_range_detail = s_entry_word_q[(entry*8)+5];
|
||||
end
|
||||
end
|
||||
if (!s_range_err && EnableP4 && (s_header_q[10] != s_max_scratch_end)) begin
|
||||
s_range_err = 1'b1;
|
||||
s_range_addr = s_source_addr_q + 32'd40;
|
||||
s_range_detail = s_header_q[10];
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
s_table_err = 1'b0;
|
||||
s_table_addr = s_source_addr_q + 32'd20;
|
||||
logic [16:0] s_entry_table_end;
|
||||
s_table_err = 1'b0;
|
||||
s_table_addr = s_source_addr_q + 32'd20;
|
||||
s_table_detail = s_header_q[5];
|
||||
if (s_header_q[5] != 32'd0) begin
|
||||
s_table_header_err = EnableP4 &&
|
||||
((s_header_q[6] > 32'h0000_6000) || (s_header_q[6][1:0] != 2'd0));
|
||||
if (!EnableP4 && (s_header_q[5] != 32'd0)) begin
|
||||
s_table_err = 1'b1;
|
||||
end else if (s_header_q[6] != 32'd0) begin
|
||||
end else if (!EnableP4 && (s_header_q[6] != 32'd0)) begin
|
||||
s_table_err = 1'b1;
|
||||
s_table_addr = s_source_addr_q + 32'd24;
|
||||
s_table_detail = s_header_q[6];
|
||||
end else begin
|
||||
for (int unsigned entry = 0; entry < 3; entry++) begin
|
||||
if (!s_table_err && (s_entry_word_q[(entry*8)+7] != 32'd0)) begin
|
||||
s_entry_table_end = {1'b0, s_entry_word_q[(entry*8)+7][15:0]} +
|
||||
{1'b0, s_entry_word_q[(entry*8)+7][31:16]};
|
||||
if (!s_table_err &&
|
||||
((!EnableP4 && (s_entry_word_q[(entry*8)+7] != 32'd0)) ||
|
||||
(EnableP4 && ((s_entry_word_q[(entry*8)+7][1:0] != 2'd0) ||
|
||||
(s_entry_word_q[(entry*8)+7][17:16] != 2'd0) ||
|
||||
(s_entry_table_end > {1'b0, s_header_q[6][15:0]}))))) begin
|
||||
s_table_err = 1'b1;
|
||||
s_table_addr = s_source_addr_q + s_header_q[7] + 32'(entry * 32) + 32'd28;
|
||||
s_table_detail = s_entry_word_q[(entry*8)+7];
|
||||
@@ -365,12 +416,17 @@ module apu_microcode_loader #(
|
||||
s_capability_detail = s_header_q[9] ^ s_entry_mask;
|
||||
if (s_header_q[9] != s_entry_mask) begin
|
||||
s_capability_err = 1'b1;
|
||||
end else if (s_header_q[9] != 32'd0) begin
|
||||
end else if ((!EnableP4 && (s_header_q[9] != 32'd0)) ||
|
||||
(EnableP4 &&
|
||||
((s_header_q[9] & ~`APB4_APU__APUMC_P4_PRIMITIVE_MASK) != 32'd0))) begin
|
||||
s_capability_err = 1'b1;
|
||||
s_capability_detail = s_header_q[9];
|
||||
end else begin
|
||||
for (int unsigned entry = 0; entry < 3; entry++) begin
|
||||
if (!s_capability_err && (s_entry_word_q[(entry*8)+6] != 32'd0)) begin
|
||||
if (!s_capability_err &&
|
||||
((!EnableP4 && (s_entry_word_q[(entry*8)+6] != 32'd0)) ||
|
||||
(EnableP4 && ((s_entry_word_q[(entry*8)+6] &
|
||||
~`APB4_APU__APUMC_P4_PRIMITIVE_MASK) != 32'd0)))) begin
|
||||
s_capability_err = 1'b1;
|
||||
s_capability_addr = s_source_addr_q + s_header_q[7] + 32'(entry * 32) + 32'd24;
|
||||
s_capability_detail = s_entry_word_q[(entry*8)+6];
|
||||
@@ -383,7 +439,7 @@ module apu_microcode_loader #(
|
||||
assign s_scan_class = s_scan_instruction[63:60];
|
||||
assign s_scan_opcode = s_scan_instruction[59:56];
|
||||
assign s_scan_predicate = s_scan_instruction[55:52];
|
||||
assign s_scan_aux = s_scan_instruction[33:32];
|
||||
assign s_scan_aux = s_scan_instruction[39:32];
|
||||
assign s_scan_immediate = s_scan_instruction[10:0];
|
||||
assign s_scan_last = s_scan_pc_q == entry_last_o[s_scan_entry_q];
|
||||
assign s_scan_target = {1'b0, s_scan_pc_q} + 1'b1 + {1'b0, s_scan_immediate};
|
||||
@@ -391,7 +447,7 @@ module apu_microcode_loader #(
|
||||
(s_scan_predicate != 4'd0) && (s_scan_class == 4'd0) &&
|
||||
(s_scan_opcode inside {4'd3, 4'd4}) && (s_path_stack_count_q < PathStackLimit);
|
||||
assign s_path_stack_cs = s_path_stack_push ||
|
||||
((s_state_q == PathPop) && (s_path_stack_count_q != 7'd0));
|
||||
((s_state_q == PathPop) && (s_path_stack_count_q != 12'd0));
|
||||
assign s_path_stack_write = s_path_stack_push;
|
||||
assign s_path_stack_addr = s_path_stack_push ?
|
||||
s_path_stack_count_q[10:0] : s_path_stack_count_q[10:0] - 1'b1;
|
||||
@@ -452,7 +508,7 @@ module apu_microcode_loader #(
|
||||
end
|
||||
end
|
||||
`else
|
||||
logic [63:0] s_path_stack_mem [0:PathStackDepth-1];
|
||||
logic [63:0] s_path_stack_mem [0:PathStackDepth-1];
|
||||
logic [63:0] s_path_stack_read_data_q;
|
||||
|
||||
assign s_path_stack_read_data = s_path_stack_read_data_q;
|
||||
@@ -495,12 +551,23 @@ module apu_microcode_loader #(
|
||||
((s_loop_active_q & ~(4'b0001 << s_scan_aux[1:0])) != 4'd0) :
|
||||
((s_loop_active_q != 4'd0) ||
|
||||
((s_scan_class == 4'd0) && (s_scan_opcode == 4'd6))));
|
||||
assign s_scan_capability_instruction = ((s_scan_class >= 4'd2) && (s_scan_class <= 4'd6)) ||
|
||||
((s_scan_class == 4'd0) && (s_scan_opcode == 4'd8));
|
||||
assign s_scan_capability_instruction = EnableP4 ?
|
||||
((s_scan_class == 4'd6) ||
|
||||
((s_scan_class == 4'd0) && (s_scan_opcode == 4'd8) &&
|
||||
!(s_scan_aux inside {8'd1, 8'd2, 8'd3})) ||
|
||||
(!((s_scan_class == 4'd0) && (s_scan_opcode == 4'd8) &&
|
||||
(s_scan_aux == 8'd1)) &&
|
||||
((instruction_primitive_mask(
|
||||
s_scan_instruction
|
||||
) & ~entry_primitive_mask_o[s_scan_entry_q]) != 32'd0)) ||
|
||||
(((s_scan_class == 4'd0) && (s_scan_opcode == 4'd8) && (s_scan_aux == 8'd1)) &&
|
||||
((entry_primitive_mask_o[s_scan_entry_q] & 32'h0000_ffc0) == 32'd0))) :
|
||||
(((s_scan_class >= 4'd2) && (s_scan_class <= 4'd6)) ||
|
||||
((s_scan_class == 4'd0) && (s_scan_opcode == 4'd8)));
|
||||
|
||||
always_comb begin
|
||||
s_path_fallthrough = s_scan_predicate != 4'd0;
|
||||
if (s_scan_class == 4'd1) begin
|
||||
if ((s_scan_class >= 4'd1) && (s_scan_class <= (EnableP4 ? 4'd5 : 4'd1))) begin
|
||||
s_path_fallthrough = 1'b1;
|
||||
end else if ((s_scan_class == 4'd0) && (s_scan_opcode inside {4'd0, 4'd6, 4'd7})) begin
|
||||
s_path_fallthrough = 1'b1;
|
||||
@@ -727,6 +794,14 @@ module apu_microcode_loader #(
|
||||
fault_stage_o <= `APB4_APU__ERROR_STAGE_LOADER;
|
||||
fault_addr_o <= s_header_range_addr;
|
||||
fault_detail_o <= s_header_range_detail;
|
||||
end else if (s_table_header_err) begin
|
||||
s_state_q <= Idle;
|
||||
s_err_bits_q[3] <= 1'b1;
|
||||
fault_valid_o <= 1'b1;
|
||||
fault_code_o <= `APB4_APU__ERROR_CODE_MICROCODE;
|
||||
fault_stage_o <= `APB4_APU__ERROR_STAGE_LOADER;
|
||||
fault_addr_o <= s_source_addr_q + 32'd24;
|
||||
fault_detail_o <= s_header_q[6];
|
||||
end else begin
|
||||
s_transfer_err_q <= 1'b0;
|
||||
s_byte_offset_q <= s_header_q[7];
|
||||
@@ -789,6 +864,14 @@ module apu_microcode_loader #(
|
||||
fault_stage_o <= `APB4_APU__ERROR_STAGE_LOADER;
|
||||
fault_addr_o <= s_range_addr;
|
||||
fault_detail_o <= s_range_detail;
|
||||
end else if (EnableP4 && s_table_err) begin
|
||||
s_state_q <= Idle;
|
||||
s_err_bits_q[3] <= 1'b1;
|
||||
fault_valid_o <= 1'b1;
|
||||
fault_code_o <= `APB4_APU__ERROR_CODE_MICROCODE;
|
||||
fault_stage_o <= `APB4_APU__ERROR_STAGE_LOADER;
|
||||
fault_addr_o <= s_table_addr;
|
||||
fault_detail_o <= s_table_detail;
|
||||
end else begin
|
||||
s_byte_offset_q <= 32'd64;
|
||||
s_crc_q <= 32'hffff_ffff;
|
||||
@@ -865,7 +948,7 @@ module apu_microcode_loader #(
|
||||
fault_addr_o <= s_range_addr;
|
||||
fault_detail_o <= s_range_detail;
|
||||
end else begin
|
||||
s_actual_crc_q <= ~s_crc_q;
|
||||
s_actual_crc_q <= ~s_crc_q;
|
||||
s_scan_entry_q <= 2'd0;
|
||||
s_scan_pc_q <= entry_first_o[0];
|
||||
s_call_depth_q <= 3'd0;
|
||||
@@ -894,15 +977,15 @@ module apu_microcode_loader #(
|
||||
s_scan_pc_q,
|
||||
s_scan_instruction
|
||||
);
|
||||
s_scan_pc_q <= entry_pc_o[s_scan_entry_q];
|
||||
s_call_depth_q <= 3'd0;
|
||||
s_return_pc_q <= '0;
|
||||
s_loop_active_q <= 4'd0;
|
||||
s_loop_start_q <= '0;
|
||||
s_scan_pc_q <= entry_pc_o[s_scan_entry_q];
|
||||
s_call_depth_q <= 3'd0;
|
||||
s_return_pc_q <= '0;
|
||||
s_loop_active_q <= 4'd0;
|
||||
s_loop_start_q <= '0;
|
||||
s_path_stack_count_q <= '0;
|
||||
s_path_stop_valid_q <= 1'b1;
|
||||
s_path_stop_pc_q <= s_scan_pc_q;
|
||||
s_state_q <= PathRequest;
|
||||
s_path_stop_valid_q <= 1'b1;
|
||||
s_path_stop_pc_q <= s_scan_pc_q;
|
||||
s_state_q <= PathRequest;
|
||||
end else begin
|
||||
if (!s_scan_capability_err_q && s_scan_capability_instruction &&
|
||||
instruction_encoding_valid(
|
||||
@@ -1000,7 +1083,7 @@ module apu_microcode_loader #(
|
||||
end
|
||||
end
|
||||
PathPop: begin
|
||||
if (s_path_stack_count_q != 7'd0) begin
|
||||
if (s_path_stack_count_q != 12'd0) begin
|
||||
s_path_stack_count_q <= s_path_stack_count_q - 1'b1;
|
||||
s_state_q <= PathPopLoad;
|
||||
end else if (s_scan_control_err_q || (s_scan_entry_q == 2'd2)) begin
|
||||
|
||||
@@ -10,8 +10,53 @@ package apu_microcode_pkg;
|
||||
logic [10:0] last_pc;
|
||||
logic [15:0] max_loop_count;
|
||||
logic [23:0] max_retired;
|
||||
logic [16:0] scratch_base;
|
||||
logic [16:0] scratch_bytes;
|
||||
logic [31:0] primitive_mask;
|
||||
logic [15:0] table_offset;
|
||||
logic [15:0] table_bytes;
|
||||
} apu_mc_entry_t;
|
||||
|
||||
function automatic logic [31:0] instruction_primitive_mask(input logic [63:0] instruction_i);
|
||||
logic [3:0] s_class;
|
||||
logic [3:0] s_opcode;
|
||||
logic [7:0] s_aux;
|
||||
logic s_unused;
|
||||
begin
|
||||
s_class = instruction_i[63:60];
|
||||
s_opcode = instruction_i[59:56];
|
||||
s_aux = instruction_i[39:32];
|
||||
s_unused = ^{instruction_i[55:40], instruction_i[31:0]};
|
||||
instruction_primitive_mask = 32'd0;
|
||||
unique case (s_class)
|
||||
`APB4_APU__MC_CLASS_CONTROL: begin
|
||||
if (s_opcode == `APB4_APU__MC_CONTROL_WAIT) begin
|
||||
if (s_aux == `APB4_APU__MC_WAIT_KERNEL) instruction_primitive_mask = 32'h0000_ffc0;
|
||||
else if (s_aux inside {`APB4_APU__MC_WAIT_INPUT_FIFO, `APB4_APU__MC_WAIT_OUTPUT_FIFO})
|
||||
instruction_primitive_mask = 32'd1 << `APB4_APU__MC_PRIMITIVE_LOCAL_FIFO;
|
||||
end
|
||||
end
|
||||
`APB4_APU__MC_CLASS_BITSTREAM: begin
|
||||
instruction_primitive_mask = 32'd1 << `APB4_APU__MC_PRIMITIVE_BITSTREAM;
|
||||
if (s_opcode inside {`APB4_APU__MC_BITSTREAM_CRC8, `APB4_APU__MC_BITSTREAM_CRC16})
|
||||
instruction_primitive_mask |= 32'd1 << `APB4_APU__MC_PRIMITIVE_CRC;
|
||||
end
|
||||
`APB4_APU__MC_CLASS_ENTROPY:
|
||||
instruction_primitive_mask = (32'd1 << `APB4_APU__MC_PRIMITIVE_BITSTREAM) |
|
||||
(32'd1 << ((s_opcode <= `APB4_APU__MC_ENTROPY_HUFF_QUAD) ?
|
||||
`APB4_APU__MC_PRIMITIVE_HUFFMAN : `APB4_APU__MC_PRIMITIVE_RICE));
|
||||
`APB4_APU__MC_CLASS_LOCAL:
|
||||
instruction_primitive_mask = 32'd1 <<
|
||||
((s_opcode inside {`APB4_APU__MC_LOCAL_FIFO_POP, `APB4_APU__MC_LOCAL_FIFO_PUSH}) ?
|
||||
`APB4_APU__MC_PRIMITIVE_LOCAL_FIFO : `APB4_APU__MC_PRIMITIVE_LOCAL_MEMORY);
|
||||
`APB4_APU__MC_CLASS_KERNEL:
|
||||
instruction_primitive_mask = 32'd1 << (s_opcode + `APB4_APU__MC_PRIMITIVE_REQUANTIZE);
|
||||
default: instruction_primitive_mask = 32'd0;
|
||||
endcase
|
||||
return instruction_primitive_mask | {32{s_unused && 1'b0}};
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic [31:0] crc32_word(input logic [31:0] crc_i, input logic [31:0] data_i,
|
||||
input logic [3:0] keep_i);
|
||||
logic [31:0] s_crc;
|
||||
|
||||
@@ -0,0 +1,688 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
`include "apu_define.svh"
|
||||
|
||||
module apu_primitive_dispatcher (
|
||||
// verilog_format: off -- preserve request, result, memory, and FIFO columns
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic flush_i,
|
||||
input logic req_valid_i,
|
||||
output logic req_ready_o,
|
||||
input logic [63:0] instruction_i,
|
||||
input logic [31:0] source0_i,
|
||||
input logic [31:0] source1_i,
|
||||
input logic [31:0] destination_i,
|
||||
input logic [16:0] scratch_base_i,
|
||||
input logic [16:0] scratch_bytes_i,
|
||||
input logic [15:0] table_offset_i,
|
||||
input logic [15:0] table_bytes_i,
|
||||
output logic result_valid_o,
|
||||
output logic [ 3:0] result_dst_o,
|
||||
output logic [ 3:0][31:0] result_data_o,
|
||||
output logic [ 2:0] result_words_o,
|
||||
output logic result_kernel_o,
|
||||
output logic error_o,
|
||||
output logic [ 5:0] error_code_o,
|
||||
output logic [ 3:0] error_stage_o,
|
||||
output logic [ 7:0] error_reason_o,
|
||||
output logic [31:0] cycles_o,
|
||||
output logic kernel_done_o,
|
||||
output logic busy_o,
|
||||
output logic input_exhausted_o,
|
||||
output logic input_ready_o,
|
||||
output logic output_ready_o,
|
||||
output logic [ 6:0] input_count_o,
|
||||
output logic [ 6:0] output_count_o,
|
||||
output logic kernel_busy_o,
|
||||
output logic memory_req_o,
|
||||
output logic memory_write_o,
|
||||
output logic [16:0] memory_addr_o,
|
||||
output logic [31:0] memory_data_o,
|
||||
output logic [ 3:0] memory_strb_o,
|
||||
input logic memory_valid_i,
|
||||
input logic [31:0] memory_data_i,
|
||||
input logic memory_error_i,
|
||||
input logic input_valid_i,
|
||||
input logic [40:0] input_data_i,
|
||||
output logic input_accept_o,
|
||||
output logic output_valid_o,
|
||||
output logic [40:0] output_data_o,
|
||||
input logic output_accept_i
|
||||
// verilog_format: on
|
||||
);
|
||||
typedef enum logic [3:0] {
|
||||
Idle,
|
||||
BitRequest,
|
||||
BitWait,
|
||||
FrameAlignRequest,
|
||||
FrameAlignWait,
|
||||
FramePeekRequest,
|
||||
FramePeekWait,
|
||||
FrameSkipRequest,
|
||||
FrameSkipWait,
|
||||
EntropyRequest,
|
||||
EntropyWait,
|
||||
LocalRequest,
|
||||
LocalWait
|
||||
} state_e;
|
||||
|
||||
state_e s_state_q;
|
||||
logic [63:0] s_instruction_q;
|
||||
logic [31:0] s_source0_q, s_source1_q;
|
||||
logic [16:0] s_scratch_base_q, s_scratch_bytes_q;
|
||||
logic [15:0] s_table_offset_q, s_table_bytes_q;
|
||||
logic [15:0] s_frame_skipped_q;
|
||||
logic [16:0] s_local_addr_q;
|
||||
logic [ 1:0] s_local_lane_q;
|
||||
logic [ 2:0] s_local_size_q;
|
||||
logic [31:0] s_cycles_q;
|
||||
|
||||
logic [3:0] s_class, s_opcode, s_dst;
|
||||
logic [ 7:0] s_aux;
|
||||
logic [31:0] s_immediate;
|
||||
logic s_input_full, s_input_empty, s_output_full, s_output_empty;
|
||||
logic [6:0] s_input_count, s_output_count;
|
||||
logic [40:0] s_input_fifo_data, s_output_fifo_data;
|
||||
logic s_input_fifo_push, s_input_fifo_pop, s_output_fifo_push, s_output_fifo_pop;
|
||||
logic [40:0] s_output_fifo_write_data;
|
||||
logic s_fifo_metadata_ok;
|
||||
|
||||
logic s_bit_req_valid, s_bit_req_ready, s_direct_bit_req, s_direct_frame_align;
|
||||
logic s_frame_match, s_frame_can_skip;
|
||||
logic [2:0] s_bit_req_op;
|
||||
logic [5:0] s_bit_req_width;
|
||||
logic s_bit_result_valid, s_bit_err, s_bit_fifo_pop;
|
||||
logic [31:0] s_bit_result_data;
|
||||
logic [ 6:0] s_available_bits;
|
||||
logic s_reservoir_eof;
|
||||
|
||||
logic s_entropy_req_valid, s_entropy_req_ready;
|
||||
logic s_direct_entropy_req;
|
||||
logic s_entropy_memory_req, s_entropy_bit_req_valid, s_entropy_bit_req_ready;
|
||||
logic [16:0] s_entropy_memory_addr;
|
||||
logic [ 2:0] s_entropy_bit_req_op;
|
||||
logic [ 5:0] s_entropy_bit_req_width;
|
||||
logic s_entropy_result_valid, s_entropy_err;
|
||||
logic [ 3:0] s_entropy_result_dst;
|
||||
logic [ 3:0][31:0] s_entropy_result_data;
|
||||
logic [ 2:0] s_entropy_result_words;
|
||||
logic [ 5:0] s_entropy_err_code;
|
||||
logic [ 3:0] s_entropy_err_stage;
|
||||
logic [31:0] s_entropy_cycles;
|
||||
|
||||
logic s_kernel_req_valid, s_kernel_req_ready, s_kernel_memory_req, s_kernel_memory_write;
|
||||
logic [16:0] s_kernel_memory_addr;
|
||||
logic [31:0] s_kernel_memory_data;
|
||||
logic [ 3:0] s_kernel_memory_strb;
|
||||
logic s_kernel_done, s_kernel_err, s_kernel_busy;
|
||||
logic [5:0] s_kernel_err_code;
|
||||
logic [3:0] s_kernel_err_stage, s_kernel_result_dst;
|
||||
logic [7:0] s_kernel_err_reason;
|
||||
logic [31:0] s_kernel_result_count, s_kernel_cycles;
|
||||
|
||||
logic [32:0] s_scratch_end, s_local_end, s_table_end;
|
||||
logic [32:0] s_entropy_table_end;
|
||||
logic s_entropy_range_ok;
|
||||
logic [34:0] s_local_addr_full;
|
||||
logic s_local_addr_overflow_q;
|
||||
logic s_local_range_ok, s_local_access_ok, s_local_write;
|
||||
logic [31:0] s_crc_result;
|
||||
|
||||
assign s_class = instruction_i[63:60];
|
||||
assign s_opcode = instruction_i[59:56];
|
||||
assign s_dst = instruction_i[51:48];
|
||||
assign s_aux = instruction_i[39:32];
|
||||
assign s_immediate = instruction_i[31:0];
|
||||
|
||||
assign req_ready_o = (s_state_q == Idle) &&
|
||||
(((s_class == `APB4_APU__MC_CLASS_KERNEL) && s_kernel_req_ready) ||
|
||||
(!s_kernel_busy &&
|
||||
((s_class inside {`APB4_APU__MC_CLASS_BITSTREAM, `APB4_APU__MC_CLASS_ENTROPY}) ||
|
||||
((s_class == `APB4_APU__MC_CLASS_LOCAL) &&
|
||||
(((s_opcode == `APB4_APU__MC_LOCAL_FIFO_POP) && !s_input_empty) ||
|
||||
((s_opcode == `APB4_APU__MC_LOCAL_FIFO_PUSH) && !s_output_full) ||
|
||||
(s_opcode < `APB4_APU__MC_LOCAL_FIFO_POP))))));
|
||||
assign busy_o = (s_state_q != Idle) || s_kernel_busy;
|
||||
assign kernel_done_o = result_valid_o && result_kernel_o && !error_o;
|
||||
assign input_exhausted_o = s_reservoir_eof;
|
||||
assign input_ready_o = !s_input_empty;
|
||||
assign output_ready_o = !s_output_full;
|
||||
assign input_count_o = s_input_count;
|
||||
assign output_count_o = s_output_count;
|
||||
assign kernel_busy_o = s_kernel_busy;
|
||||
|
||||
assign input_accept_o = input_valid_i && !s_input_full;
|
||||
assign s_input_fifo_push = input_accept_o;
|
||||
assign s_input_fifo_pop = s_bit_fifo_pop ||
|
||||
(req_valid_i && req_ready_o && (s_class == `APB4_APU__MC_CLASS_LOCAL) &&
|
||||
(s_opcode == `APB4_APU__MC_LOCAL_FIFO_POP));
|
||||
assign s_fifo_metadata_ok = ((source1_i & ~32'h0000_0107) == 32'd0) &&
|
||||
(source1_i[2:0] != 3'd0) && (source1_i[2:0] <= 3'd4);
|
||||
assign s_output_fifo_push = req_valid_i && req_ready_o && s_fifo_metadata_ok &&
|
||||
(s_class == `APB4_APU__MC_CLASS_LOCAL) &&
|
||||
(s_opcode == `APB4_APU__MC_LOCAL_FIFO_PUSH);
|
||||
assign s_output_fifo_write_data = {source1_i[8], 5'd0, source1_i[2:0], source0_i};
|
||||
assign output_valid_o = !s_output_empty;
|
||||
assign output_data_o = s_output_fifo_data;
|
||||
assign s_output_fifo_pop = output_accept_i && output_valid_o;
|
||||
|
||||
stream_fifo #(
|
||||
.DATA_WIDTH (41),
|
||||
.BUFFER_DEPTH (64),
|
||||
.LOG_BUFFER_DEPTH(6)
|
||||
) u_input_fifo (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.flush_i(flush_i),
|
||||
.full_o (s_input_full),
|
||||
.empty_o(s_input_empty),
|
||||
.cnt_o (s_input_count),
|
||||
.dat_i (input_data_i),
|
||||
.push_i (s_input_fifo_push),
|
||||
.dat_o (s_input_fifo_data),
|
||||
.pop_i (s_input_fifo_pop)
|
||||
);
|
||||
|
||||
stream_fifo #(
|
||||
.DATA_WIDTH (41),
|
||||
.BUFFER_DEPTH (64),
|
||||
.LOG_BUFFER_DEPTH(6)
|
||||
) u_output_fifo (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.flush_i(flush_i),
|
||||
.full_o (s_output_full),
|
||||
.empty_o(s_output_empty),
|
||||
.cnt_o (s_output_count),
|
||||
.dat_i (s_output_fifo_write_data),
|
||||
.push_i (s_output_fifo_push),
|
||||
.dat_o (s_output_fifo_data),
|
||||
.pop_i (s_output_fifo_pop)
|
||||
);
|
||||
|
||||
assign s_direct_bit_req = (s_state_q == Idle) && req_valid_i && req_ready_o &&
|
||||
(s_class == `APB4_APU__MC_CLASS_BITSTREAM) &&
|
||||
(s_opcode <= `APB4_APU__MC_BITSTREAM_ALIGN);
|
||||
assign s_direct_frame_align = (s_state_q == Idle) && req_valid_i && req_ready_o &&
|
||||
(s_class == `APB4_APU__MC_CLASS_BITSTREAM) &&
|
||||
(s_opcode == `APB4_APU__MC_BITSTREAM_FRAME_SYNC);
|
||||
assign s_frame_match = (s_bit_result_data[15:0] & s_source1_q[15:0]) ==
|
||||
(s_source0_q[15:0] & s_source1_q[15:0]);
|
||||
assign s_frame_can_skip = !s_bit_err && !s_frame_match &&
|
||||
(s_frame_skipped_q < s_instruction_q[15:0] - 1'b1);
|
||||
assign s_bit_req_valid = s_direct_bit_req || s_direct_frame_align ||
|
||||
((s_state_q == FrameAlignWait) && s_bit_result_valid && !s_bit_err) ||
|
||||
((s_state_q == FramePeekWait) && s_bit_result_valid && s_frame_can_skip) ||
|
||||
((s_state_q == FrameSkipWait) && s_bit_result_valid && !s_bit_err) ||
|
||||
(s_state_q inside {
|
||||
BitRequest, FrameAlignRequest, FramePeekRequest, FrameSkipRequest
|
||||
}) || s_entropy_bit_req_valid;
|
||||
assign s_bit_req_op = s_entropy_bit_req_valid ? s_entropy_bit_req_op :
|
||||
(s_direct_bit_req ? s_opcode[2:0] :
|
||||
((s_direct_frame_align || (s_state_q == FrameAlignRequest)) ? 3'd4 :
|
||||
((s_state_q inside {FrameAlignWait, FramePeekRequest, FrameSkipWait}) ? 3'd1 :
|
||||
((s_state_q inside {FramePeekWait, FrameSkipRequest}) ?
|
||||
3'd2 : s_instruction_q[58:56]))));
|
||||
assign s_bit_req_width = s_entropy_bit_req_valid ? s_entropy_bit_req_width :
|
||||
(s_direct_bit_req ? s_immediate[5:0] :
|
||||
((s_direct_frame_align || (s_state_q == FrameAlignRequest)) ? 6'd0 :
|
||||
((s_state_q inside {FrameAlignWait, FramePeekRequest, FrameSkipWait}) ?
|
||||
{1'b0, s_instruction_q[36:32]} :
|
||||
((s_state_q inside {FramePeekWait, FrameSkipRequest}) ?
|
||||
6'd8 : s_instruction_q[5:0]))));
|
||||
assign s_entropy_bit_req_ready = s_bit_req_ready && s_entropy_bit_req_valid;
|
||||
|
||||
apu_bitstream_engine u_bitstream_engine (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.flush_i (flush_i),
|
||||
.req_valid_i (s_bit_req_valid),
|
||||
.req_ready_o (s_bit_req_ready),
|
||||
.req_op_i (s_bit_req_op),
|
||||
.req_width_i (s_bit_req_width),
|
||||
.result_valid_o (s_bit_result_valid),
|
||||
.result_data_o (s_bit_result_data),
|
||||
.available_bits_o(s_available_bits),
|
||||
.eof_o (s_reservoir_eof),
|
||||
.error_o (s_bit_err),
|
||||
.fifo_empty_i (s_input_empty),
|
||||
.fifo_data_i (s_input_fifo_data),
|
||||
.fifo_pop_o (s_bit_fifo_pop)
|
||||
);
|
||||
|
||||
assign s_direct_entropy_req = (s_state_q == Idle) && req_valid_i && req_ready_o &&
|
||||
(s_class == `APB4_APU__MC_CLASS_ENTROPY);
|
||||
assign s_entropy_req_valid = (s_state_q == EntropyRequest) || s_direct_entropy_req;
|
||||
assign s_kernel_req_valid = (s_state_q == Idle) && req_valid_i &&
|
||||
(s_class == `APB4_APU__MC_CLASS_KERNEL) && !s_kernel_done &&
|
||||
!(result_valid_o && result_kernel_o);
|
||||
|
||||
apu_entropy_engine u_entropy_engine (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.flush_i (flush_i),
|
||||
.req_valid_i (s_entropy_req_valid),
|
||||
.req_ready_o (s_entropy_req_ready),
|
||||
.opcode_i (s_direct_entropy_req ? s_opcode : s_instruction_q[59:56]),
|
||||
.dst_i (s_direct_entropy_req ? s_dst : s_instruction_q[51:48]),
|
||||
.aux_i (s_direct_entropy_req ? s_aux : s_instruction_q[39:32]),
|
||||
.immediate_i (s_direct_entropy_req ? s_immediate : s_instruction_q[31:0]),
|
||||
.source0_i (s_direct_entropy_req ? source0_i : s_source0_q),
|
||||
.source1_i (s_direct_entropy_req ? source1_i : s_source1_q),
|
||||
.table_offset_i (s_direct_entropy_req ? table_offset_i : s_table_offset_q),
|
||||
.memory_req_o (s_entropy_memory_req),
|
||||
.memory_addr_o (s_entropy_memory_addr),
|
||||
.memory_valid_i (memory_valid_i),
|
||||
.memory_data_i (memory_data_i),
|
||||
.memory_error_i (memory_error_i),
|
||||
.bit_req_valid_o (s_entropy_bit_req_valid),
|
||||
.bit_req_ready_i (s_entropy_bit_req_ready),
|
||||
.bit_req_op_o (s_entropy_bit_req_op),
|
||||
.bit_req_width_o (s_entropy_bit_req_width),
|
||||
.bit_result_valid_i(s_bit_result_valid),
|
||||
.bit_result_data_i (s_bit_result_data),
|
||||
.bit_error_i (s_bit_err),
|
||||
.result_valid_o (s_entropy_result_valid),
|
||||
.result_dst_o (s_entropy_result_dst),
|
||||
.result_data_o (s_entropy_result_data),
|
||||
.result_words_o (s_entropy_result_words),
|
||||
.error_o (s_entropy_err),
|
||||
.error_code_o (s_entropy_err_code),
|
||||
.error_stage_o (s_entropy_err_stage),
|
||||
.cycles_o (s_entropy_cycles)
|
||||
);
|
||||
|
||||
apu_kernel_engine u_kernel_engine (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.flush_i (flush_i),
|
||||
.req_valid_i (s_kernel_req_valid),
|
||||
.req_ready_o (s_kernel_req_ready),
|
||||
.opcode_i (s_opcode),
|
||||
.aux_i (s_aux),
|
||||
.count_i (s_immediate[15:0]),
|
||||
.dst_i (s_dst),
|
||||
.input_offset_i (source0_i),
|
||||
.parameter_offset_i(source1_i),
|
||||
.output_offset_i (destination_i),
|
||||
.scratch_base_i (scratch_base_i),
|
||||
.scratch_bytes_i (scratch_bytes_i),
|
||||
.table_offset_i (table_offset_i),
|
||||
.table_bytes_i (table_bytes_i),
|
||||
.memory_req_o (s_kernel_memory_req),
|
||||
.memory_write_o (s_kernel_memory_write),
|
||||
.memory_addr_o (s_kernel_memory_addr),
|
||||
.memory_data_o (s_kernel_memory_data),
|
||||
.memory_strb_o (s_kernel_memory_strb),
|
||||
.memory_valid_i (memory_valid_i),
|
||||
.memory_data_i (memory_data_i),
|
||||
.memory_error_i (memory_error_i),
|
||||
.done_o (s_kernel_done),
|
||||
.error_o (s_kernel_err),
|
||||
.error_code_o (s_kernel_err_code),
|
||||
.error_stage_o (s_kernel_err_stage),
|
||||
.error_reason_o (s_kernel_err_reason),
|
||||
.result_dst_o (s_kernel_result_dst),
|
||||
.result_count_o (s_kernel_result_count),
|
||||
.cycles_o (s_kernel_cycles),
|
||||
.busy_o (s_kernel_busy)
|
||||
);
|
||||
|
||||
assign memory_req_o = s_kernel_busy ? s_kernel_memory_req :
|
||||
(s_entropy_memory_req || ((s_state_q == LocalRequest) && s_local_access_ok));
|
||||
assign memory_write_o = s_kernel_busy ? s_kernel_memory_write :
|
||||
((s_state_q == LocalRequest) && s_local_access_ok && s_local_write);
|
||||
assign memory_addr_o = s_kernel_busy ? s_kernel_memory_addr :
|
||||
(s_entropy_memory_req ? s_entropy_memory_addr : {s_local_addr_q[16:2], 2'd0});
|
||||
assign memory_data_o = s_kernel_busy ? s_kernel_memory_data : s_source1_q;
|
||||
assign memory_strb_o = s_kernel_busy ? s_kernel_memory_strb : 4'hf;
|
||||
|
||||
always_comb begin
|
||||
logic [15:0] s_crc8;
|
||||
s_crc8 = (s_opcode == `APB4_APU__MC_BITSTREAM_CRC8) ? {8'd0, source0_i[7:0]} : source0_i[15:0];
|
||||
for (int bit_index = 0; bit_index < 8; bit_index++) begin
|
||||
if (s_opcode == `APB4_APU__MC_BITSTREAM_CRC8) begin
|
||||
s_crc8[7:0] = {s_crc8[6:0], 1'b0} ^ ({8{(s_crc8[7] ^ source1_i[7-bit_index])}} & 8'h07);
|
||||
end else begin
|
||||
s_crc8 = {s_crc8[14:0], 1'b0} ^ ({16{(s_crc8[15] ^ source1_i[7-bit_index])}} & 16'h8005);
|
||||
end
|
||||
end
|
||||
s_crc_result = (s_opcode == `APB4_APU__MC_BITSTREAM_CRC8) ?
|
||||
{24'd0, s_crc8[7:0]} : {16'd0, s_crc8};
|
||||
end
|
||||
|
||||
assign s_scratch_end = {16'd0, s_scratch_base_q} + {16'd0, s_scratch_bytes_q};
|
||||
assign s_local_end = {16'd0, s_local_addr_q} + {30'd0, s_local_size_q};
|
||||
assign s_table_end = {17'd0, s_table_offset_q} + {17'd0, s_table_bytes_q};
|
||||
assign s_entropy_table_end = {1'b0, source0_i} + ({1'b0, source1_i} << 2);
|
||||
assign s_entropy_range_ok = (source0_i[1:0] == 2'd0) && !s_entropy_table_end[32] &&
|
||||
(s_entropy_table_end <= {17'd0, table_bytes_i});
|
||||
assign s_local_range_ok = !s_local_addr_overflow_q &&
|
||||
((s_instruction_q[59:56] inside {
|
||||
`APB4_APU__MC_LOCAL_TABLE8, `APB4_APU__MC_LOCAL_TABLE16,
|
||||
`APB4_APU__MC_LOCAL_TABLE32
|
||||
}) ? (({16'd0, s_local_addr_q} >= {17'd0, s_table_offset_q}) && (s_local_end <= s_table_end))
|
||||
: (({16'd0, s_local_addr_q} >= {16'd0, s_scratch_base_q}) &&
|
||||
(s_local_end <= s_scratch_end)));
|
||||
assign s_local_access_ok = s_local_range_ok &&
|
||||
((s_local_size_q == 3'd1) ||
|
||||
((s_local_addr_q & ({14'd0, s_local_size_q} - 17'd1)) == 17'd0));
|
||||
|
||||
always_comb begin
|
||||
unique case (s_opcode)
|
||||
`APB4_APU__MC_LOCAL_LD32, `APB4_APU__MC_LOCAL_ST32:
|
||||
s_local_addr_full = {18'd0, scratch_base_i} + {3'd0, source0_i} +
|
||||
{{19{instruction_i[15]}}, instruction_i[15:0]};
|
||||
`APB4_APU__MC_LOCAL_TABLE8:
|
||||
s_local_addr_full = {19'd0, table_offset_i} + {3'd0, source0_i} + {3'd0, source1_i};
|
||||
`APB4_APU__MC_LOCAL_TABLE16:
|
||||
s_local_addr_full = {19'd0, table_offset_i} + {3'd0, source0_i} + ({3'd0, source1_i} << 1);
|
||||
default:
|
||||
s_local_addr_full = {19'd0, table_offset_i} + {3'd0, source0_i} + ({3'd0, source1_i} << 2);
|
||||
endcase
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_instruction_q <= 64'd0;
|
||||
s_source0_q <= 32'd0;
|
||||
s_source1_q <= 32'd0;
|
||||
s_scratch_base_q <= 17'd0;
|
||||
s_scratch_bytes_q <= 17'd0;
|
||||
s_table_offset_q <= 16'd0;
|
||||
s_table_bytes_q <= 16'd0;
|
||||
s_frame_skipped_q <= 16'd0;
|
||||
s_local_addr_q <= 17'd0;
|
||||
s_local_lane_q <= 2'd0;
|
||||
s_local_size_q <= 3'd0;
|
||||
s_local_write <= 1'b0;
|
||||
s_local_addr_overflow_q <= 1'b0;
|
||||
s_cycles_q <= 32'd0;
|
||||
result_valid_o <= 1'b0;
|
||||
result_dst_o <= 4'd0;
|
||||
result_data_o <= '0;
|
||||
result_words_o <= 3'd0;
|
||||
result_kernel_o <= 1'b0;
|
||||
error_o <= 1'b0;
|
||||
error_code_o <= 6'd0;
|
||||
error_stage_o <= 4'd0;
|
||||
error_reason_o <= 8'd0;
|
||||
cycles_o <= 32'd0;
|
||||
end else if (flush_i) begin
|
||||
s_state_q <= Idle;
|
||||
result_valid_o <= 1'b0;
|
||||
result_kernel_o <= 1'b0;
|
||||
error_o <= 1'b0;
|
||||
s_cycles_q <= 32'd0;
|
||||
end else begin
|
||||
result_valid_o <= 1'b0;
|
||||
result_kernel_o <= 1'b0;
|
||||
error_o <= 1'b0;
|
||||
if (s_kernel_done) begin
|
||||
result_valid_o <= 1'b1;
|
||||
result_kernel_o <= 1'b1;
|
||||
result_dst_o <= s_kernel_result_dst;
|
||||
result_data_o <= s_kernel_err ? '0 : {96'd0, s_kernel_result_count};
|
||||
result_words_o <= s_kernel_err ? 3'd0 : 3'd1;
|
||||
cycles_o <= s_kernel_cycles;
|
||||
error_o <= s_kernel_err;
|
||||
error_code_o <= s_kernel_err_code;
|
||||
error_stage_o <= s_kernel_err_stage;
|
||||
error_reason_o <= s_kernel_err_reason;
|
||||
end
|
||||
if (s_state_q != Idle) s_cycles_q <= s_cycles_q + 1'b1;
|
||||
unique case (s_state_q)
|
||||
Idle: begin
|
||||
if (req_valid_i && req_ready_o && (s_class != `APB4_APU__MC_CLASS_KERNEL)) begin
|
||||
s_instruction_q <= instruction_i;
|
||||
s_source0_q <= source0_i;
|
||||
s_source1_q <= source1_i;
|
||||
s_scratch_base_q <= scratch_base_i;
|
||||
s_scratch_bytes_q <= scratch_bytes_i;
|
||||
s_table_offset_q <= table_offset_i;
|
||||
s_table_bytes_q <= table_bytes_i;
|
||||
s_cycles_q <= 32'd1;
|
||||
s_local_addr_overflow_q <= s_local_addr_full[34:17] != 18'd0;
|
||||
result_dst_o <= instruction_i[51:48];
|
||||
result_data_o <= '0;
|
||||
result_words_o <= 3'd0;
|
||||
if (s_class == `APB4_APU__MC_CLASS_BITSTREAM) begin
|
||||
if (s_opcode == `APB4_APU__MC_BITSTREAM_FRAME_SYNC) begin
|
||||
s_frame_skipped_q <= 16'd0;
|
||||
s_state_q <= s_direct_frame_align ? FrameAlignWait : FrameAlignRequest;
|
||||
end else if (s_opcode inside {`APB4_APU__MC_BITSTREAM_CRC8,
|
||||
`APB4_APU__MC_BITSTREAM_CRC16}) begin
|
||||
result_valid_o <= 1'b1;
|
||||
result_words_o <= 3'd1;
|
||||
result_data_o[0] <= s_crc_result;
|
||||
cycles_o <= 32'd1;
|
||||
end else begin
|
||||
s_state_q <= s_direct_bit_req ? BitWait : BitRequest;
|
||||
end
|
||||
end else if (s_class == `APB4_APU__MC_CLASS_ENTROPY) begin
|
||||
if ((s_opcode <= `APB4_APU__MC_ENTROPY_HUFF_QUAD) &&
|
||||
(source1_i >= 32'd1) && (source1_i <= 32'd4096) &&
|
||||
!s_entropy_range_ok) begin
|
||||
result_valid_o <= 1'b1;
|
||||
result_words_o <= 3'd0;
|
||||
error_o <= 1'b1;
|
||||
error_code_o <= `APB4_APU__ERROR_CODE_SEQUENCER;
|
||||
error_stage_o <= `APB4_APU__ERROR_STAGE_LIFECYCLE;
|
||||
error_reason_o <= 8'd5;
|
||||
cycles_o <= 32'd1;
|
||||
end else begin
|
||||
s_state_q <= s_direct_entropy_req ? EntropyWait : EntropyRequest;
|
||||
end
|
||||
end else if (s_opcode == `APB4_APU__MC_LOCAL_FIFO_POP) begin
|
||||
result_valid_o <= 1'b1;
|
||||
result_words_o <= 3'd2;
|
||||
result_data_o[0] <= s_input_fifo_data[31:0];
|
||||
result_data_o[1] <= {23'd0, s_input_fifo_data[40], 5'd0, s_input_fifo_data[34:32]};
|
||||
cycles_o <= 32'd1;
|
||||
end else if (s_opcode == `APB4_APU__MC_LOCAL_FIFO_PUSH) begin
|
||||
result_valid_o <= 1'b1;
|
||||
result_words_o <= 3'd0;
|
||||
cycles_o <= 32'd1;
|
||||
if ((source1_i & ~32'h0000_0107) != 32'd0 ||
|
||||
(source1_i[2:0] == 3'd0) || (source1_i[2:0] > 3'd4)) begin
|
||||
error_o <= 1'b1;
|
||||
error_code_o <= `APB4_APU__ERROR_CODE_OVERFLOW;
|
||||
error_stage_o <= `APB4_APU__ERROR_STAGE_RESAMPLER;
|
||||
error_reason_o <= 8'd9;
|
||||
end
|
||||
end else begin
|
||||
s_local_write <= s_opcode == `APB4_APU__MC_LOCAL_ST32;
|
||||
s_local_addr_q <= s_local_addr_full[16:0];
|
||||
s_local_lane_q <= s_local_addr_full[1:0];
|
||||
unique case (s_opcode)
|
||||
`APB4_APU__MC_LOCAL_LD32, `APB4_APU__MC_LOCAL_ST32: begin
|
||||
s_local_size_q <= 3'd4;
|
||||
end
|
||||
`APB4_APU__MC_LOCAL_TABLE8: begin
|
||||
s_local_size_q <= 3'd1;
|
||||
end
|
||||
`APB4_APU__MC_LOCAL_TABLE16: begin
|
||||
s_local_size_q <= 3'd2;
|
||||
end
|
||||
default: begin
|
||||
s_local_size_q <= 3'd4;
|
||||
end
|
||||
endcase
|
||||
s_state_q <= LocalRequest;
|
||||
end
|
||||
end
|
||||
end
|
||||
BitRequest: begin
|
||||
if (s_bit_req_ready) s_state_q <= BitWait;
|
||||
end
|
||||
BitWait: begin
|
||||
if (s_bit_result_valid) begin
|
||||
s_state_q <= Idle;
|
||||
result_valid_o <= 1'b1;
|
||||
result_words_o <= (s_instruction_q[59:56] inside {`APB4_APU__MC_BITSTREAM_SKIP,
|
||||
`APB4_APU__MC_BITSTREAM_ALIGN
|
||||
}) ? 3'd0 : 3'd1;
|
||||
result_data_o[0] <= s_bit_result_data;
|
||||
cycles_o <= (s_instruction_q[59:56] == `APB4_APU__MC_BITSTREAM_REFILL) ? 32'd3 : 32'd1;
|
||||
if (s_bit_err && (s_instruction_q[59:56] != `APB4_APU__MC_BITSTREAM_REFILL)) begin
|
||||
result_data_o <= '0;
|
||||
result_words_o <= 3'd0;
|
||||
error_o <= 1'b1;
|
||||
error_code_o <= `APB4_APU__ERROR_CODE_TRUNCATED;
|
||||
error_stage_o <= `APB4_APU__ERROR_STAGE_BITSTREAM;
|
||||
error_reason_o <= 8'd9;
|
||||
end
|
||||
end
|
||||
end
|
||||
FrameAlignRequest: begin
|
||||
if (s_bit_req_ready) s_state_q <= FrameAlignWait;
|
||||
end
|
||||
FrameAlignWait: begin
|
||||
if (s_bit_result_valid) begin
|
||||
if (s_bit_err) begin
|
||||
s_state_q <= Idle;
|
||||
result_valid_o <= 1'b1;
|
||||
result_data_o <= '0;
|
||||
result_words_o <= 3'd0;
|
||||
error_o <= 1'b1;
|
||||
error_code_o <= `APB4_APU__ERROR_CODE_TRUNCATED;
|
||||
error_stage_o <= `APB4_APU__ERROR_STAGE_BITSTREAM;
|
||||
error_reason_o <= 8'd9;
|
||||
end else begin
|
||||
s_state_q <= FramePeekWait;
|
||||
end
|
||||
end
|
||||
end
|
||||
FramePeekRequest: begin
|
||||
if (s_bit_req_ready) s_state_q <= FramePeekWait;
|
||||
end
|
||||
FramePeekWait: begin
|
||||
if (s_bit_result_valid) begin
|
||||
if (s_bit_err) begin
|
||||
s_state_q <= Idle;
|
||||
result_valid_o <= 1'b1;
|
||||
result_data_o <= '0;
|
||||
result_words_o <= 3'd0;
|
||||
error_o <= 1'b1;
|
||||
error_code_o <= `APB4_APU__ERROR_CODE_MALFORMED;
|
||||
error_stage_o <= `APB4_APU__ERROR_STAGE_BITSTREAM;
|
||||
error_reason_o <= 8'd9;
|
||||
cycles_o <= 32'd2 + ({16'd0, s_instruction_q[15:0]} << 1);
|
||||
end else if (s_frame_match) begin
|
||||
s_state_q <= Idle;
|
||||
result_valid_o <= 1'b1;
|
||||
result_words_o <= 3'd1;
|
||||
result_data_o[0] <= {16'd0, s_frame_skipped_q};
|
||||
cycles_o <= 32'd2 + ({16'd0, s_frame_skipped_q} << 1);
|
||||
end else if (s_frame_skipped_q >= s_instruction_q[15:0] - 1'b1) begin
|
||||
s_state_q <= Idle;
|
||||
result_valid_o <= 1'b1;
|
||||
result_data_o <= '0;
|
||||
result_words_o <= 3'd0;
|
||||
error_o <= 1'b1;
|
||||
error_code_o <= `APB4_APU__ERROR_CODE_MALFORMED;
|
||||
error_stage_o <= `APB4_APU__ERROR_STAGE_BITSTREAM;
|
||||
error_reason_o <= 8'd9;
|
||||
cycles_o <= 32'd2 + ({16'd0, s_instruction_q[15:0]} << 1);
|
||||
end else begin
|
||||
s_state_q <= FrameSkipWait;
|
||||
end
|
||||
end
|
||||
end
|
||||
FrameSkipRequest: begin
|
||||
if (s_bit_req_ready) s_state_q <= FrameSkipWait;
|
||||
end
|
||||
FrameSkipWait: begin
|
||||
if (s_bit_result_valid) begin
|
||||
if (s_bit_err) begin
|
||||
s_state_q <= Idle;
|
||||
result_valid_o <= 1'b1;
|
||||
result_data_o <= '0;
|
||||
result_words_o <= 3'd0;
|
||||
error_o <= 1'b1;
|
||||
error_code_o <= `APB4_APU__ERROR_CODE_MALFORMED;
|
||||
error_stage_o <= `APB4_APU__ERROR_STAGE_BITSTREAM;
|
||||
error_reason_o <= 8'd9;
|
||||
end else begin
|
||||
s_frame_skipped_q <= s_frame_skipped_q + 1'b1;
|
||||
s_state_q <= FramePeekWait;
|
||||
end
|
||||
end
|
||||
end
|
||||
EntropyRequest: begin
|
||||
if (s_entropy_req_ready) s_state_q <= EntropyWait;
|
||||
end
|
||||
EntropyWait: begin
|
||||
if (s_entropy_result_valid) begin
|
||||
s_state_q <= Idle;
|
||||
result_valid_o <= 1'b1;
|
||||
result_dst_o <= s_entropy_result_dst;
|
||||
result_data_o <= s_entropy_result_data;
|
||||
result_words_o <= s_entropy_result_words;
|
||||
cycles_o <= s_entropy_cycles;
|
||||
if (s_entropy_err) begin
|
||||
result_data_o <= '0;
|
||||
result_words_o <= 3'd0;
|
||||
error_o <= 1'b1;
|
||||
error_code_o <= s_entropy_err_code;
|
||||
error_stage_o <= s_entropy_err_stage;
|
||||
error_reason_o <= (s_entropy_err_code == `APB4_APU__ERROR_CODE_SEQUENCER) ?
|
||||
8'd5 : 8'd9;
|
||||
end
|
||||
end
|
||||
end
|
||||
LocalRequest: begin
|
||||
if (!s_local_access_ok) begin
|
||||
s_state_q <= Idle;
|
||||
result_valid_o <= 1'b1;
|
||||
result_data_o <= '0;
|
||||
result_words_o <= 3'd0;
|
||||
error_o <= 1'b1;
|
||||
error_code_o <= `APB4_APU__ERROR_CODE_SEQUENCER;
|
||||
error_stage_o <= `APB4_APU__ERROR_STAGE_LIFECYCLE;
|
||||
error_reason_o <= 8'd5;
|
||||
cycles_o <= 32'd1;
|
||||
end else if (s_local_write) begin
|
||||
s_state_q <= Idle;
|
||||
result_valid_o <= 1'b1;
|
||||
result_words_o <= 3'd0;
|
||||
cycles_o <= 32'd1;
|
||||
end else begin
|
||||
s_state_q <= LocalWait;
|
||||
end
|
||||
end
|
||||
LocalWait: begin
|
||||
if (memory_error_i) begin
|
||||
s_state_q <= Idle;
|
||||
result_valid_o <= 1'b1;
|
||||
result_data_o <= '0;
|
||||
result_words_o <= 3'd0;
|
||||
error_o <= 1'b1;
|
||||
error_code_o <= `APB4_APU__ERROR_CODE_SEQUENCER;
|
||||
error_stage_o <= `APB4_APU__ERROR_STAGE_LIFECYCLE;
|
||||
error_reason_o <= 8'd5;
|
||||
cycles_o <= 32'd2;
|
||||
end else if (memory_valid_i) begin
|
||||
s_state_q <= Idle;
|
||||
result_valid_o <= 1'b1;
|
||||
result_words_o <= 3'd1;
|
||||
cycles_o <= 32'd2;
|
||||
unique case (s_local_size_q)
|
||||
3'd1: result_data_o[0] <= {24'd0, memory_data_i[s_local_lane_q*8+:8]};
|
||||
3'd2: result_data_o[0] <= {16'd0, memory_data_i[s_local_lane_q[1]*16+:16]};
|
||||
default: result_data_o[0] <= memory_data_i;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
default: s_state_q <= Idle;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
logic s_unused;
|
||||
assign s_unused = s_reservoir_eof ^ ^s_available_bits ^ ^s_input_count ^ ^s_output_count ^
|
||||
^s_instruction_q[63:60] ^ ^s_instruction_q[55:40] ^ ^s_immediate[31:16];
|
||||
endmodule
|
||||
@@ -0,0 +1,174 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
`include "apu_define.svh"
|
||||
|
||||
module apu_reconstruction_engine (
|
||||
// verilog_format: off -- preserve arithmetic operand columns
|
||||
input logic [ 3:0] opcode_i,
|
||||
input logic [ 7:0] aux_i,
|
||||
input logic signed [31:0] value0_i,
|
||||
input logic signed [31:0] value1_i,
|
||||
input logic signed [31:0] scale_i,
|
||||
input logic signed [31:0] residual_i,
|
||||
input logic signed [31:0] predictor_shift_i,
|
||||
input logic signed [31:0] history_i [0:31],
|
||||
input logic signed [31:0] coefficient_i [0:31],
|
||||
output logic signed [31:0] result0_o,
|
||||
output logic signed [31:0] result1_o,
|
||||
output logic overflow_o
|
||||
// verilog_format: on
|
||||
);
|
||||
logic signed [71:0] s_accumulator;
|
||||
logic signed [63:0] s_value0, s_value1;
|
||||
logic signed [63:0] s_product;
|
||||
logic signed [63:0] s_predictor;
|
||||
logic signed [63:0] s_shifted;
|
||||
logic signed [63:0] s_value0_ext, s_value1_ext, s_residual_ext;
|
||||
logic signed [71:0] s_lpc_shifted;
|
||||
logic signed [72:0] s_lpc_value;
|
||||
logic s_lpc_shift_overflow;
|
||||
logic [ 5:0] s_order;
|
||||
|
||||
function automatic logic signed [63:0] rne(input logic signed [63:0] value_i,
|
||||
input logic [5:0] shift_i);
|
||||
logic signed [63:0] s_magnitude;
|
||||
logic signed [63:0] s_quotient;
|
||||
logic [63:0] s_remainder;
|
||||
logic [63:0] s_half;
|
||||
begin
|
||||
if (shift_i == 6'd0) return value_i;
|
||||
s_magnitude = (value_i < 0) ? -value_i : value_i;
|
||||
s_quotient = s_magnitude >>> shift_i;
|
||||
s_remainder = s_magnitude & ((64'd1 << shift_i) - 1'b1);
|
||||
s_half = 64'd1 << (shift_i - 1'b1);
|
||||
if ((s_remainder > s_half) || ((s_remainder == s_half) && s_quotient[0])) begin
|
||||
s_quotient = s_quotient + 1'b1;
|
||||
end
|
||||
return (value_i < 0) ? -s_quotient : s_quotient;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function automatic logic signed [31:0] sat32(input logic signed [63:0] value_i);
|
||||
if (value_i > 64'sh0000_0000_7fff_ffff) return 32'sh7fff_ffff;
|
||||
if (value_i < -64'sh0000_0000_8000_0000) return -32'sh8000_0000;
|
||||
return value_i[31:0];
|
||||
endfunction
|
||||
|
||||
function automatic logic signed [31:0] sat_width(input logic signed [63:0] value_i,
|
||||
input logic [1:0] width_i);
|
||||
logic signed [63:0] s_minimum, s_maximum;
|
||||
begin
|
||||
unique case (width_i)
|
||||
2'd0: begin
|
||||
s_minimum = -64'sd32768;
|
||||
s_maximum = 64'sd32767;
|
||||
end
|
||||
2'd1: begin
|
||||
s_minimum = -64'sd8388608;
|
||||
s_maximum = 64'sd8388607;
|
||||
end
|
||||
default: begin
|
||||
s_minimum = -64'sh0000_0000_8000_0000;
|
||||
s_maximum = 64'sh0000_0000_7fff_ffff;
|
||||
end
|
||||
endcase
|
||||
if (value_i < s_minimum) return s_minimum[31:0];
|
||||
if (value_i > s_maximum) return s_maximum[31:0];
|
||||
return value_i[31:0];
|
||||
end
|
||||
endfunction
|
||||
|
||||
always_comb begin
|
||||
s_order = aux_i[5:0];
|
||||
s_accumulator = 72'sd0;
|
||||
for (int index = 0; index < 32; index++) begin
|
||||
if (index < s_order) begin
|
||||
s_accumulator += $signed(coefficient_i[index]) * $signed(history_i[index]);
|
||||
end
|
||||
end
|
||||
s_product = $signed(value0_i) * $signed(scale_i);
|
||||
s_value0_ext = {{32{value0_i[31]}}, value0_i};
|
||||
s_value1_ext = {{32{value1_i[31]}}, value1_i};
|
||||
s_residual_ext = {{32{residual_i[31]}}, residual_i};
|
||||
s_predictor = 64'sd0;
|
||||
unique case (aux_i[2:0])
|
||||
3'd1: s_predictor = {{32{history_i[0][31]}}, history_i[0]};
|
||||
3'd2:
|
||||
s_predictor = 64'sd2 * {{32{history_i[0][31]}}, history_i[0]} -
|
||||
{{32{history_i[1][31]}}, history_i[1]};
|
||||
3'd3:
|
||||
s_predictor = 64'sd3 * {{32{history_i[0][31]}}, history_i[0]} -
|
||||
64'sd3 * {{32{history_i[1][31]}}, history_i[1]} +
|
||||
{{32{history_i[2][31]}}, history_i[2]};
|
||||
3'd4:
|
||||
s_predictor = 64'sd4 * {{32{history_i[0][31]}}, history_i[0]} -
|
||||
64'sd6 * {{32{history_i[1][31]}}, history_i[1]} +
|
||||
64'sd4 * {{32{history_i[2][31]}}, history_i[2]} -
|
||||
{{32{history_i[3][31]}}, history_i[3]};
|
||||
default: s_predictor = 64'sd0;
|
||||
endcase
|
||||
s_value0 = s_value0_ext;
|
||||
s_value1 = s_value1_ext;
|
||||
if (predictor_shift_i >= 0) begin
|
||||
s_lpc_shifted = s_accumulator >>> predictor_shift_i[5:0];
|
||||
s_lpc_shift_overflow = 1'b0;
|
||||
end else begin
|
||||
s_lpc_shifted = s_accumulator <<< (-predictor_shift_i);
|
||||
s_lpc_shift_overflow = (s_lpc_shifted >>> (-predictor_shift_i)) != s_accumulator;
|
||||
end
|
||||
s_shifted = s_lpc_shifted[63:0];
|
||||
s_lpc_value = {s_lpc_shifted[71], s_lpc_shifted} + {{41{residual_i[31]}}, residual_i};
|
||||
result0_o = value0_i;
|
||||
result1_o = value1_i;
|
||||
overflow_o = (s_accumulator[71:63] != {9{s_accumulator[63]}});
|
||||
unique case (opcode_i)
|
||||
`APB4_APU__MC_KERNEL_REQUANT: begin
|
||||
s_shifted = aux_i[5] ?
|
||||
rne(s_product, 6'(30 + aux_i[4:0])) : (s_product >>> (30 + aux_i[4:0]));
|
||||
result0_o = sat_width(s_shifted, aux_i[7:6]);
|
||||
end
|
||||
`APB4_APU__MC_KERNEL_STEREO, `APB4_APU__MC_KERNEL_DECORRELATE: begin
|
||||
unique case (aux_i[1:0])
|
||||
2'd0: begin
|
||||
s_value0 = s_value0_ext;
|
||||
s_value1 = s_value1_ext;
|
||||
end
|
||||
2'd1: begin
|
||||
s_value0 = s_value0_ext;
|
||||
s_value1 = s_value0_ext - s_value1_ext;
|
||||
end
|
||||
2'd2: begin
|
||||
s_value0 = s_value0_ext + s_value1_ext;
|
||||
s_value1 = s_value1_ext;
|
||||
end
|
||||
default: begin
|
||||
s_predictor = s_value0_ext * 64'sd2 + (s_value1_ext & 64'sd1);
|
||||
s_value0 = (s_predictor + s_value1_ext) >>> 1;
|
||||
s_value1 = (s_predictor - s_value1_ext) >>> 1;
|
||||
end
|
||||
endcase
|
||||
overflow_o = (s_value0 > 64'sh0000_0000_7fff_ffff) ||
|
||||
(s_value0 < -64'sh0000_0000_8000_0000) ||
|
||||
(s_value1 > 64'sh0000_0000_7fff_ffff) ||
|
||||
(s_value1 < -64'sh0000_0000_8000_0000);
|
||||
result0_o = sat32(s_value0);
|
||||
result1_o = sat32(s_value1);
|
||||
end
|
||||
`APB4_APU__MC_KERNEL_FIXED: begin
|
||||
s_value0 = s_predictor + s_residual_ext;
|
||||
overflow_o = (s_value0 > 64'sh0000_0000_7fff_ffff) ||
|
||||
(s_value0 < -64'sh0000_0000_8000_0000);
|
||||
result0_o = s_value0[31:0];
|
||||
end
|
||||
`APB4_APU__MC_KERNEL_LPC: begin
|
||||
overflow_o = overflow_o || s_lpc_shift_overflow ||
|
||||
(s_lpc_value > 73'sh0000_0000_7fff_ffff) ||
|
||||
(s_lpc_value < -73'sh0000_0000_8000_0000);
|
||||
result0_o = s_lpc_value[31:0];
|
||||
end
|
||||
default: begin
|
||||
end
|
||||
endcase
|
||||
end
|
||||
endmodule
|
||||
@@ -80,8 +80,8 @@ module apu_reg (
|
||||
);
|
||||
localparam logic [31:0] IpId = 32'h4150_5530;
|
||||
localparam logic [31:0] IpVersion = 32'h0001_0000;
|
||||
localparam logic [31:0] Capability0 = 32'h0000_0098;
|
||||
localparam logic [31:0] Capability1 = 32'h0000_0010;
|
||||
localparam logic [31:0] Capability0 = 32'h0000_0198;
|
||||
localparam logic [31:0] Capability1 = 32'h0182_7010;
|
||||
localparam logic [31:0] AbiDigest = 32'd0;
|
||||
localparam logic [10:0] IrqMask = 11'h7ff;
|
||||
localparam logic [31:0] TimeoutReset = 32'h0000_ffff;
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
module apu_resampler (
|
||||
input logic [ 3:0] profile_i,
|
||||
input logic [63:0] next_output_num_i,
|
||||
output logic [15:0] ratio_l_o,
|
||||
output logic [15:0] ratio_m_o,
|
||||
output logic [ 5:0] phase_o,
|
||||
output logic [63:0] source_index_o
|
||||
);
|
||||
logic [69:0] s_phase_numerator;
|
||||
logic [63:0] s_source_remainder;
|
||||
logic [ 5:0] s_phase_quotient;
|
||||
logic [15:0] s_phase_remainder;
|
||||
logic [16:0] s_phase_remainder_twice;
|
||||
|
||||
always_comb begin
|
||||
unique case (profile_i)
|
||||
4'd0: begin
|
||||
ratio_l_o = 16'd1;
|
||||
ratio_m_o = 16'd1;
|
||||
end
|
||||
4'd1: begin
|
||||
ratio_l_o = 16'd1;
|
||||
ratio_m_o = 16'd2;
|
||||
end
|
||||
4'd2: begin
|
||||
ratio_l_o = 16'd80;
|
||||
ratio_m_o = 16'd147;
|
||||
end
|
||||
4'd3: begin
|
||||
ratio_l_o = 16'd160;
|
||||
ratio_m_o = 16'd147;
|
||||
end
|
||||
4'd4: begin
|
||||
ratio_l_o = 16'd3;
|
||||
ratio_m_o = 16'd2;
|
||||
end
|
||||
4'd5: begin
|
||||
ratio_l_o = 16'd2;
|
||||
ratio_m_o = 16'd1;
|
||||
end
|
||||
4'd6: begin
|
||||
ratio_l_o = 16'd3;
|
||||
ratio_m_o = 16'd1;
|
||||
end
|
||||
4'd7: begin
|
||||
ratio_l_o = 16'd4;
|
||||
ratio_m_o = 16'd1;
|
||||
end
|
||||
4'd8: begin
|
||||
ratio_l_o = 16'd6;
|
||||
ratio_m_o = 16'd1;
|
||||
end
|
||||
4'd9: begin
|
||||
ratio_l_o = 16'd8;
|
||||
ratio_m_o = 16'd1;
|
||||
end
|
||||
4'd10: begin
|
||||
ratio_l_o = 16'd12;
|
||||
ratio_m_o = 16'd1;
|
||||
end
|
||||
4'd11: begin
|
||||
ratio_l_o = 16'd320;
|
||||
ratio_m_o = 16'd147;
|
||||
end
|
||||
4'd12: begin
|
||||
ratio_l_o = 16'd640;
|
||||
ratio_m_o = 16'd147;
|
||||
end
|
||||
4'd13: begin
|
||||
ratio_l_o = 16'd1280;
|
||||
ratio_m_o = 16'd147;
|
||||
end
|
||||
4'd14: begin
|
||||
ratio_l_o = 16'd2;
|
||||
ratio_m_o = 16'd3;
|
||||
end
|
||||
default: begin
|
||||
ratio_l_o = 16'd4;
|
||||
ratio_m_o = 16'd3;
|
||||
end
|
||||
endcase
|
||||
source_index_o = next_output_num_i / {48'd0, ratio_l_o};
|
||||
s_source_remainder = next_output_num_i % {48'd0, ratio_l_o};
|
||||
s_phase_numerator = {6'd0, s_source_remainder} * 70'd32;
|
||||
s_phase_quotient = 6'(s_phase_numerator / {54'd0, ratio_l_o});
|
||||
s_phase_remainder = 16'(s_phase_numerator % {54'd0, ratio_l_o});
|
||||
s_phase_remainder_twice = {1'b0, s_phase_remainder} << 1;
|
||||
phase_o = s_phase_quotient;
|
||||
if ((s_phase_remainder_twice > {1'b0, ratio_l_o}) ||
|
||||
((s_phase_remainder_twice == {1'b0, ratio_l_o}) && s_phase_quotient[0])) begin
|
||||
phase_o = phase_o + 1'b1;
|
||||
end
|
||||
if (phase_o == 6'd32) begin
|
||||
phase_o = 6'd0;
|
||||
source_index_o = source_index_o + 1'b1;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
module apu_transform_engine (
|
||||
input logic [ 5:0] order_i,
|
||||
input logic signed [31:0] sample_i [0:31],
|
||||
input logic signed [31:0] coefficient_i[0:31],
|
||||
output logic signed [31:0] result_o,
|
||||
output logic overflow_o
|
||||
);
|
||||
logic signed [71:0] s_accumulator;
|
||||
logic signed [71:0] s_magnitude;
|
||||
logic signed [71:0] s_rounded;
|
||||
|
||||
always_comb begin
|
||||
s_accumulator = 72'sd0;
|
||||
for (int index = 0; index < 32; index++) begin
|
||||
if (index < order_i) begin
|
||||
s_accumulator += $signed(sample_i[index]) * $signed(coefficient_i[index]);
|
||||
end
|
||||
end
|
||||
s_magnitude = (s_accumulator < 0) ? -s_accumulator : s_accumulator;
|
||||
s_rounded = (s_magnitude >>> 30) +
|
||||
(((s_magnitude[29:0] > 30'h2000_0000) ||
|
||||
((s_magnitude[29:0] == 30'h2000_0000) && s_magnitude[30])) ? 72'd1 : 72'd0);
|
||||
if (s_accumulator < 0) s_rounded = -s_rounded;
|
||||
overflow_o = 1'b0;
|
||||
if (s_rounded > 72'sh0000_0000_7fff_ffff) result_o = 32'sh7fff_ffff;
|
||||
else if (s_rounded < -72'sh0000_0000_8000_0000) result_o = -32'sh8000_0000;
|
||||
else result_o = s_rounded[31:0];
|
||||
end
|
||||
endmodule
|
||||
@@ -131,6 +131,14 @@
|
||||
/ip/multimedia/apu_stream_router.sv
|
||||
/ip/multimedia/apu_control_store.sv
|
||||
/ip/multimedia/apu_microcode_loader.sv
|
||||
/ip/multimedia/apu_local_sram.sv
|
||||
/ip/multimedia/apu_bitstream_engine.sv
|
||||
/ip/multimedia/apu_entropy_engine.sv
|
||||
/ip/multimedia/apu_reconstruction_engine.sv
|
||||
/ip/multimedia/apu_transform_engine.sv
|
||||
/ip/multimedia/apu_resampler.sv
|
||||
/ip/multimedia/apu_kernel_engine.sv
|
||||
/ip/multimedia/apu_primitive_dispatcher.sv
|
||||
/ip/multimedia/apu_codec_sequencer.sv
|
||||
/ip/multimedia/apu_reg.sv
|
||||
/ip/multimedia/apb4_apu.sv
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
`include "apu_define.svh"
|
||||
|
||||
module apu_primitives_formal_design (
|
||||
// verilog_format: off -- preserve formal observation columns
|
||||
input logic clk_i,
|
||||
output logic rst_n_i,
|
||||
output logic f_past_valid,
|
||||
output logic [ 2:0] scenario_o,
|
||||
output logic [ 2:0] phase_o,
|
||||
output logic request_valid_o,
|
||||
output logic request_ready_o,
|
||||
output logic [ 3:0] request_class_o,
|
||||
output logic request_accept_o,
|
||||
output logic kernel_busy_o,
|
||||
output logic kernel_outstanding_o,
|
||||
output logic [31:0] kernel_latency_o,
|
||||
output logic result_valid_o,
|
||||
output logic result_kernel_o,
|
||||
output logic result_error_o,
|
||||
output logic [ 5:0] result_error_code_o,
|
||||
output logic [ 3:0] result_error_stage_o,
|
||||
output logic [ 7:0] result_error_reason_o,
|
||||
output logic [31:0] result_cycles_o,
|
||||
output logic done_o,
|
||||
output logic last_error_o,
|
||||
output logic [ 5:0] last_error_code_o,
|
||||
output logic [ 3:0] last_error_stage_o,
|
||||
output logic [ 7:0] last_error_reason_o,
|
||||
output logic [31:0] last_data_o,
|
||||
output logic [ 6:0] input_count_o,
|
||||
output logic [ 6:0] output_count_o,
|
||||
output logic memory_request_o
|
||||
// verilog_format: on
|
||||
);
|
||||
`ifdef APU_PRIMITIVES_FORMAL_SCENARIO
|
||||
localparam logic [2:0] f_scenario = `APU_PRIMITIVES_FORMAL_SCENARIO;
|
||||
`else
|
||||
(* anyconst *) logic [2:0] f_scenario;
|
||||
`endif
|
||||
|
||||
logic [ 7:0] s_cycle_q;
|
||||
logic [ 2:0] s_phase_q;
|
||||
logic [63:0] s_instruction;
|
||||
logic [31:0] s_source0, s_source1, s_destination;
|
||||
logic s_req_valid, s_req_ready;
|
||||
logic s_result_valid, s_result_kernel, s_result_error;
|
||||
logic [ 3:0] s_result_dst;
|
||||
logic [ 3:0][31:0] s_result_data;
|
||||
logic [ 2:0] s_result_words;
|
||||
logic [ 5:0] s_result_error_code;
|
||||
logic [ 3:0] s_result_error_stage;
|
||||
logic [ 7:0] s_result_error_reason;
|
||||
logic [31:0] s_result_cycles;
|
||||
logic s_kernel_done, s_dispatcher_busy;
|
||||
logic s_input_exhausted, s_input_ready, s_output_ready;
|
||||
logic s_memory_req, s_memory_write, s_memory_valid, s_memory_error;
|
||||
logic [16:0] s_memory_addr;
|
||||
logic [31:0] s_memory_write_data, s_memory_read_data;
|
||||
logic [3:0] s_memory_strb;
|
||||
logic s_input_valid, s_input_accept, s_output_valid;
|
||||
logic [40:0] s_input_data, s_output_data;
|
||||
logic s_inject, s_codec_req, s_codec_write;
|
||||
logic [16:0] s_codec_addr;
|
||||
logic [31:0] s_codec_data;
|
||||
logic [ 3:0] s_codec_strb;
|
||||
logic s_kernel_outstanding_q;
|
||||
logic [31:0] s_kernel_latency_q;
|
||||
logic s_done_q, s_last_error_q;
|
||||
logic [ 5:0] s_last_error_code_q;
|
||||
logic [ 3:0] s_last_error_stage_q;
|
||||
logic [ 7:0] s_last_error_reason_q;
|
||||
logic [31:0] s_last_data_q;
|
||||
|
||||
function automatic logic [63:0] encode_instruction(
|
||||
input logic [3:0] class_i, input logic [3:0] opcode_i, input logic [3:0] dst_i,
|
||||
input logic [3:0] src0_i, input logic [3:0] src1_i, input logic [7:0] aux_i,
|
||||
input logic [31:0] immediate_i);
|
||||
return {class_i, opcode_i, 4'd0, dst_i, src0_i, src1_i, aux_i, immediate_i};
|
||||
endfunction
|
||||
|
||||
assign scenario_o = f_scenario;
|
||||
assign phase_o = s_phase_q;
|
||||
assign request_valid_o = s_req_valid;
|
||||
assign request_ready_o = s_req_ready;
|
||||
assign request_class_o = s_instruction[63:60];
|
||||
assign request_accept_o = s_req_valid && s_req_ready;
|
||||
assign kernel_outstanding_o = s_kernel_outstanding_q;
|
||||
assign kernel_latency_o = s_kernel_latency_q;
|
||||
assign result_valid_o = s_result_valid;
|
||||
assign result_kernel_o = s_result_kernel;
|
||||
assign result_error_o = s_result_error;
|
||||
assign result_error_code_o = s_result_error_code;
|
||||
assign result_error_stage_o = s_result_error_stage;
|
||||
assign result_error_reason_o = s_result_error_reason;
|
||||
assign result_cycles_o = s_result_cycles;
|
||||
assign done_o = s_done_q;
|
||||
assign last_error_o = s_last_error_q;
|
||||
assign last_error_code_o = s_last_error_code_q;
|
||||
assign last_error_stage_o = s_last_error_stage_q;
|
||||
assign last_error_reason_o = s_last_error_reason_q;
|
||||
assign last_data_o = s_last_data_q;
|
||||
assign memory_request_o = s_memory_req;
|
||||
|
||||
always_comb begin
|
||||
s_instruction = 64'd0;
|
||||
s_source0 = 32'd0;
|
||||
s_source1 = 32'd0;
|
||||
s_destination = 32'd0;
|
||||
s_req_valid = 1'b0;
|
||||
if (rst_n_i) begin
|
||||
unique case (f_scenario)
|
||||
3'd0: begin
|
||||
s_instruction = encode_instruction(4'd4, 4'd0, 4'd0, 4'd0, 4'd0, 8'd0, 32'd0);
|
||||
s_req_valid = s_phase_q == 3'd0;
|
||||
end
|
||||
3'd1: begin
|
||||
if (s_phase_q == 3'd0) begin
|
||||
s_instruction = encode_instruction(4'd4, 4'd1, 4'd0, 4'd0, 4'd1, 8'd0, 32'd0);
|
||||
s_source1 = 32'hdead_beef;
|
||||
s_req_valid = 1'b1;
|
||||
end else if (s_phase_q == 3'd2) begin
|
||||
s_instruction = encode_instruction(4'd4, 4'd0, 4'd0, 4'd0, 4'd0, 8'd0, 32'd0);
|
||||
s_req_valid = 1'b1;
|
||||
end
|
||||
end
|
||||
3'd2: begin
|
||||
if (s_phase_q == 3'd0) begin
|
||||
s_instruction = encode_instruction(4'd5, 4'd0, 4'd2, 4'd0, 4'd1, 8'h80, 32'd1);
|
||||
s_source0 = 32'h20;
|
||||
s_source1 = 32'h28;
|
||||
s_destination = 32'h2c;
|
||||
s_req_valid = s_cycle_q >= 8'd4;
|
||||
end else if (s_phase_q == 3'd1) begin
|
||||
s_instruction = encode_instruction(4'd4, 4'd1, 4'd0, 4'd0, 4'd1, 8'd0, 32'd0);
|
||||
s_source0 = 32'h30;
|
||||
s_source1 = 32'h1357_9bdf;
|
||||
s_req_valid = 1'b1;
|
||||
end else if (s_phase_q == 3'd3) begin
|
||||
s_instruction = encode_instruction(4'd4, 4'd0, 4'd0, 4'd0, 4'd0, 8'd0, 32'd0);
|
||||
s_source0 = 32'h30;
|
||||
s_req_valid = 1'b1;
|
||||
end
|
||||
end
|
||||
3'd3: begin
|
||||
s_instruction = encode_instruction(4'd4, 4'd5, 4'd0, 4'd0, 4'd0, 8'd0, 32'd0);
|
||||
s_req_valid = (s_phase_q == 3'd0) && (s_cycle_q >= 8'd2);
|
||||
end
|
||||
3'd4: begin
|
||||
s_instruction = encode_instruction(4'd5, 4'd0, 4'd2, 4'd0, 4'd1, 8'h80, 32'd1);
|
||||
s_source0 = 32'h21;
|
||||
s_source1 = 32'h28;
|
||||
s_destination = 32'h2c;
|
||||
s_req_valid = (s_phase_q == 3'd0) && (s_cycle_q >= 8'd4);
|
||||
end
|
||||
3'd5: begin
|
||||
s_instruction = encode_instruction(4'd4, 4'd6, 4'd0, 4'd0, 4'd1, 8'd0, 32'd0);
|
||||
s_source0 = 32'hcafe_f00d;
|
||||
s_source1 = 32'h0000_0004;
|
||||
s_req_valid = s_phase_q == 3'd0;
|
||||
end
|
||||
default: begin
|
||||
s_instruction = encode_instruction(4'd4, 4'd1, 4'd0, 4'd0, 4'd1, 8'd0, 32'd1);
|
||||
s_source0 = 32'hffff_ffff;
|
||||
s_source1 = 32'hdead_beef;
|
||||
s_req_valid = s_phase_q == 3'd0;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
assign s_input_valid = (f_scenario == 3'd3) && (s_cycle_q == 8'd1);
|
||||
assign s_input_data = {1'b1, 5'd0, 3'd4, 32'h2468_ace0};
|
||||
assign s_inject = ((f_scenario == 3'd2) || (f_scenario == 3'd4)) &&
|
||||
(s_cycle_q inside {8'd1, 8'd2, 8'd3});
|
||||
assign s_codec_req = s_inject || s_memory_req;
|
||||
assign s_codec_write = s_inject || s_memory_write;
|
||||
assign s_codec_addr = s_inject ?
|
||||
((s_cycle_q == 8'd1) ? 17'h20 : ((s_cycle_q == 8'd2) ? 17'h24 : 17'h28)) :
|
||||
s_memory_addr;
|
||||
assign s_codec_data = s_inject ?
|
||||
((s_cycle_q == 8'd1) ? 32'd1000 :
|
||||
((s_cycle_q == 8'd2) ? 32'h4000_0000 : 32'h0000_0024)) :
|
||||
s_memory_write_data;
|
||||
assign s_codec_strb = s_inject ? 4'hf : s_memory_strb;
|
||||
|
||||
apu_local_sram #(
|
||||
.MemoryWordCount(64)
|
||||
) u_local_sram (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.image_valid_i (1'b1),
|
||||
.table_bytes_i (16'd0),
|
||||
.epoch_clear_i (1'b0),
|
||||
.loader_active_i (1'b0),
|
||||
.loader_req_i (1'b0),
|
||||
.loader_addr_i (17'd0),
|
||||
.loader_data_i (32'd0),
|
||||
.loader_strb_i (4'd0),
|
||||
.loader_ready_o (),
|
||||
.codec_req_i (s_codec_req),
|
||||
.codec_write_i (s_codec_write),
|
||||
.codec_addr_i (s_codec_addr),
|
||||
.codec_data_i (s_codec_data),
|
||||
.codec_strb_i (s_codec_strb),
|
||||
.codec_ready_o (),
|
||||
.codec_data_o (s_memory_read_data),
|
||||
.codec_valid_o (s_memory_valid),
|
||||
.codec_access_err_o(s_memory_error)
|
||||
);
|
||||
|
||||
apu_primitive_dispatcher u_dispatcher (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.flush_i (1'b0),
|
||||
.req_valid_i (s_req_valid),
|
||||
.req_ready_o (s_req_ready),
|
||||
.instruction_i (s_instruction),
|
||||
.source0_i (s_source0),
|
||||
.source1_i (s_source1),
|
||||
.destination_i (s_destination),
|
||||
.scratch_base_i (17'd0),
|
||||
.scratch_bytes_i (17'h100),
|
||||
.table_offset_i (16'd0),
|
||||
.table_bytes_i (16'd0),
|
||||
.result_valid_o (s_result_valid),
|
||||
.result_dst_o (s_result_dst),
|
||||
.result_data_o (s_result_data),
|
||||
.result_words_o (s_result_words),
|
||||
.result_kernel_o (s_result_kernel),
|
||||
.error_o (s_result_error),
|
||||
.error_code_o (s_result_error_code),
|
||||
.error_stage_o (s_result_error_stage),
|
||||
.error_reason_o (s_result_error_reason),
|
||||
.cycles_o (s_result_cycles),
|
||||
.kernel_done_o (s_kernel_done),
|
||||
.busy_o (s_dispatcher_busy),
|
||||
.input_exhausted_o(s_input_exhausted),
|
||||
.input_ready_o (s_input_ready),
|
||||
.output_ready_o (s_output_ready),
|
||||
.input_count_o (input_count_o),
|
||||
.output_count_o (output_count_o),
|
||||
.kernel_busy_o (kernel_busy_o),
|
||||
.memory_req_o (s_memory_req),
|
||||
.memory_write_o (s_memory_write),
|
||||
.memory_addr_o (s_memory_addr),
|
||||
.memory_data_o (s_memory_write_data),
|
||||
.memory_strb_o (s_memory_strb),
|
||||
.memory_valid_i (s_memory_valid),
|
||||
.memory_data_i (s_memory_read_data),
|
||||
.memory_error_i (s_memory_error),
|
||||
.input_valid_i (s_input_valid),
|
||||
.input_data_i (s_input_data),
|
||||
.input_accept_o (s_input_accept),
|
||||
.output_valid_o (s_output_valid),
|
||||
.output_data_o (s_output_data),
|
||||
.output_accept_i (1'b0)
|
||||
);
|
||||
|
||||
initial begin
|
||||
rst_n_i = 1'b0;
|
||||
f_past_valid = 1'b0;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
rst_n_i <= 1'b1;
|
||||
f_past_valid <= 1'b1;
|
||||
if (!rst_n_i) begin
|
||||
s_cycle_q <= 8'd0;
|
||||
s_phase_q <= 3'd0;
|
||||
s_kernel_outstanding_q <= 1'b0;
|
||||
s_kernel_latency_q <= 32'd0;
|
||||
s_done_q <= 1'b0;
|
||||
s_last_error_q <= 1'b0;
|
||||
s_last_error_code_q <= 6'd0;
|
||||
s_last_error_stage_q <= 4'd0;
|
||||
s_last_error_reason_q <= 8'd0;
|
||||
s_last_data_q <= 32'd0;
|
||||
end else begin
|
||||
s_cycle_q <= s_cycle_q + 1'b1;
|
||||
if (s_kernel_outstanding_q) s_kernel_latency_q <= s_kernel_latency_q + 1'b1;
|
||||
if (request_accept_o && (request_class_o == `APB4_APU__MC_CLASS_KERNEL)) begin
|
||||
s_kernel_outstanding_q <= 1'b1;
|
||||
s_kernel_latency_q <= 32'd1;
|
||||
end
|
||||
if (s_result_valid) begin
|
||||
s_last_error_q <= s_result_error;
|
||||
s_last_error_code_q <= s_result_error_code;
|
||||
s_last_error_stage_q <= s_result_error_stage;
|
||||
s_last_error_reason_q <= s_result_error_reason;
|
||||
s_last_data_q <= s_result_data[0];
|
||||
if (s_result_kernel) s_kernel_outstanding_q <= 1'b0;
|
||||
end
|
||||
|
||||
unique case (f_scenario)
|
||||
3'd0, 3'd3, 3'd4, 3'd5, 3'd6: begin
|
||||
if (request_accept_o) s_phase_q <= 3'd1;
|
||||
if (s_result_valid) begin
|
||||
s_phase_q <= 3'd7;
|
||||
s_done_q <= 1'b1;
|
||||
end
|
||||
end
|
||||
3'd1: begin
|
||||
if ((s_phase_q == 3'd0) && request_accept_o) s_phase_q <= 3'd1;
|
||||
if ((s_phase_q == 3'd1) && s_result_valid) s_phase_q <= 3'd2;
|
||||
if ((s_phase_q == 3'd2) && request_accept_o) s_phase_q <= 3'd3;
|
||||
if ((s_phase_q == 3'd3) && s_result_valid) begin
|
||||
s_phase_q <= 3'd7;
|
||||
s_done_q <= 1'b1;
|
||||
end
|
||||
end
|
||||
default: begin
|
||||
if ((s_phase_q == 3'd0) && request_accept_o) s_phase_q <= 3'd1;
|
||||
if ((s_phase_q == 3'd1) && request_accept_o) s_phase_q <= 3'd2;
|
||||
if ((s_phase_q == 3'd2) && s_result_valid && !s_result_kernel) s_phase_q <= 3'd3;
|
||||
if ((s_phase_q == 3'd3) && request_accept_o) s_phase_q <= 3'd4;
|
||||
if ((s_phase_q == 3'd4) && s_result_valid) begin
|
||||
s_phase_q <= 3'd7;
|
||||
s_done_q <= 1'b1;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
logic s_unused;
|
||||
assign s_unused = s_result_dst[0] ^ s_result_words[0] ^ s_kernel_done ^ s_dispatcher_busy ^
|
||||
s_input_exhausted ^ s_input_ready ^ s_output_ready ^ s_input_accept ^ s_output_valid ^
|
||||
s_output_data[0];
|
||||
endmodule
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
module apu_primitives_formal;
|
||||
(* anyseq *) (* gclk *) reg clk_i;
|
||||
wire rst_n_i, f_past_valid;
|
||||
wire [2:0] scenario_o, phase_o;
|
||||
wire request_valid_o, request_ready_o, request_accept_o;
|
||||
wire [3:0] request_class_o;
|
||||
wire kernel_busy_o, kernel_outstanding_o;
|
||||
wire [31:0] kernel_latency_o;
|
||||
wire result_valid_o, result_kernel_o, result_error_o;
|
||||
wire [ 5:0] result_error_code_o;
|
||||
wire [ 3:0] result_error_stage_o;
|
||||
wire [ 7:0] result_error_reason_o;
|
||||
wire [31:0] result_cycles_o;
|
||||
wire done_o, last_error_o;
|
||||
wire [ 5:0] last_error_code_o;
|
||||
wire [ 3:0] last_error_stage_o;
|
||||
wire [ 7:0] last_error_reason_o;
|
||||
wire [31:0] last_data_o;
|
||||
wire [6:0] input_count_o, output_count_o;
|
||||
wire memory_request_o;
|
||||
|
||||
apu_primitives_formal_design u_design (.*);
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
assume (scenario_o <= 3'd6);
|
||||
if (rst_n_i) begin
|
||||
assert (input_count_o <= 7'd64);
|
||||
assert (output_count_o <= 7'd64);
|
||||
if (kernel_busy_o && request_valid_o && (request_class_o != 4'd5)) begin
|
||||
assert (!request_ready_o);
|
||||
end
|
||||
if (request_accept_o && (request_class_o != 4'd5)) begin
|
||||
assert (!kernel_busy_o);
|
||||
end
|
||||
if (result_valid_o && result_kernel_o && !result_error_o) begin
|
||||
assert (kernel_outstanding_o);
|
||||
assert (kernel_latency_o <= result_cycles_o);
|
||||
end
|
||||
if (done_o && (scenario_o == 3'd0)) begin
|
||||
assert (last_error_o);
|
||||
assert ({last_error_code_o, last_error_stage_o, last_error_reason_o} ==
|
||||
{6'd11, 4'd11, 8'd5});
|
||||
end
|
||||
if (done_o && (scenario_o == 3'd1)) begin
|
||||
assert (!last_error_o);
|
||||
assert (last_data_o == 32'hdead_beef);
|
||||
end
|
||||
if (done_o && (scenario_o == 3'd2)) begin
|
||||
assert (!last_error_o);
|
||||
assert (last_data_o == 32'h1357_9bdf);
|
||||
end
|
||||
if (done_o && (scenario_o == 3'd3)) begin
|
||||
assert (!last_error_o);
|
||||
assert (last_data_o == 32'h2468_ace0);
|
||||
end
|
||||
if (done_o && (scenario_o == 3'd4)) begin
|
||||
assert (last_error_o);
|
||||
assert ({last_error_code_o, last_error_stage_o, last_error_reason_o} ==
|
||||
{6'd11, 4'd11, 8'd5});
|
||||
end
|
||||
if (done_o && (scenario_o == 3'd5)) begin
|
||||
assert (!last_error_o);
|
||||
assert (output_count_o == 7'd1);
|
||||
end
|
||||
if ((scenario_o == 3'd6) && (phase_o == 3'd1)) begin
|
||||
assert (!memory_request_o);
|
||||
end
|
||||
if (done_o && (scenario_o == 3'd6)) begin
|
||||
assert (last_error_o);
|
||||
assert ({last_error_code_o, last_error_stage_o, last_error_reason_o} ==
|
||||
{6'd11, 4'd11, 8'd5});
|
||||
assert (last_data_o == 32'd0);
|
||||
end
|
||||
end
|
||||
|
||||
cover (rst_n_i && done_o);
|
||||
cover (rst_n_i && ((scenario_o != 3'd2) ||
|
||||
(kernel_outstanding_o && request_valid_o && !request_ready_o &&
|
||||
(request_class_o == 4'd4))));
|
||||
end
|
||||
|
||||
logic s_unused;
|
||||
assign s_unused = f_past_valid ^ phase_o[0];
|
||||
endmodule
|
||||
@@ -67,51 +67,77 @@ module apu_sequencer_formal_design (
|
||||
apu_codec_sequencer u_dut (
|
||||
.clk_i,
|
||||
.rst_n_i,
|
||||
.soft_reset_i (1'b0),
|
||||
.resource_reset_i (1'b0),
|
||||
.counter_clear_i (1'b0),
|
||||
.abort_i ((f_scenario == 2'd2) && (s_cycle_q == 6'd8)),
|
||||
.launch_i (s_cycle_q == 6'd1),
|
||||
.launch_entry_i (2'd0),
|
||||
.image_valid_i (1'b1),
|
||||
.timeout_i ((f_scenario == 2'd1) ? 32'd1 : 32'd16),
|
||||
.entry_pc_i ({11'd0, 11'd0, 11'd0}),
|
||||
.entry_first_i ({11'd0, 11'd0, 11'd0}),
|
||||
.entry_last_i (s_entry_last),
|
||||
.entry_max_loop_i ({16'd4, 16'd4, 16'd4}),
|
||||
.entry_max_retired_i (s_entry_max_retired),
|
||||
.input_exhausted_i (1'b1),
|
||||
.input_ready_i (1'b0),
|
||||
.output_ready_i (1'b1),
|
||||
.kernel_done_i (1'b0),
|
||||
.transport_idle_success_i(1'b1),
|
||||
.stall_i (1'b0),
|
||||
.cause_valid_i (1'b0),
|
||||
.cause_code_i (6'd0),
|
||||
.cause_stage_i (4'd0),
|
||||
.cause_resp_i (2'd0),
|
||||
.cause_index_i (8'd0),
|
||||
.cause_addr_i (32'd0),
|
||||
.cause_detail_i (32'd0),
|
||||
.fetch_o (s_fetch),
|
||||
.fetch_addr_o (s_fetch_addr),
|
||||
.fetch_data_i (s_fetch_data),
|
||||
.fetch_valid_i (s_fetch_pending_q),
|
||||
.stat_o (status),
|
||||
.retired_o (retired),
|
||||
.gpr_o (s_gpr),
|
||||
.trapped_o (trapped),
|
||||
.trap_event_o (trap_event),
|
||||
.abort_done_o (abort_done),
|
||||
.fault_valid_o (fault_valid),
|
||||
.fault_code_o (fault_code),
|
||||
.fault_stage_o (fault_stage),
|
||||
.fault_resp_o (fault_resp),
|
||||
.fault_index_o (fault_index),
|
||||
.fault_addr_o (fault_addr),
|
||||
.fault_detail_o (fault_detail),
|
||||
.perf_retired_o (),
|
||||
.idle_o (idle)
|
||||
.soft_reset_i (1'b0),
|
||||
.resource_reset_i (1'b0),
|
||||
.counter_clear_i (1'b0),
|
||||
.abort_i ((f_scenario == 2'd2) && (s_cycle_q == 6'd8)),
|
||||
.launch_i (s_cycle_q == 6'd1),
|
||||
.launch_entry_i (2'd0),
|
||||
.image_valid_i (1'b1),
|
||||
.timeout_i ((f_scenario == 2'd1) ? 32'd1 : 32'd16),
|
||||
.entry_pc_i ({11'd0, 11'd0, 11'd0}),
|
||||
.entry_first_i ({11'd0, 11'd0, 11'd0}),
|
||||
.entry_last_i (s_entry_last),
|
||||
.entry_max_loop_i ({16'd4, 16'd4, 16'd4}),
|
||||
.entry_max_retired_i (s_entry_max_retired),
|
||||
.entry_scratch_base_i ('0),
|
||||
.entry_scratch_bytes_i ('0),
|
||||
.entry_primitive_mask_i ('0),
|
||||
.entry_table_offset_i ('0),
|
||||
.entry_table_bytes_i ('0),
|
||||
.input_exhausted_i (1'b1),
|
||||
.input_ready_i (1'b0),
|
||||
.output_ready_i (1'b1),
|
||||
.kernel_done_i (1'b0),
|
||||
.transport_idle_success_i (1'b1),
|
||||
.stall_i (1'b0),
|
||||
.cause_valid_i (1'b0),
|
||||
.cause_code_i (6'd0),
|
||||
.cause_stage_i (4'd0),
|
||||
.cause_resp_i (2'd0),
|
||||
.cause_index_i (8'd0),
|
||||
.cause_addr_i (32'd0),
|
||||
.cause_detail_i (32'd0),
|
||||
.primitive_req_valid_o (),
|
||||
.primitive_req_ready_i (1'b0),
|
||||
.primitive_instruction_o (),
|
||||
.primitive_source0_o (),
|
||||
.primitive_source1_o (),
|
||||
.primitive_destination_o (),
|
||||
.primitive_result_valid_i (1'b0),
|
||||
.primitive_result_dst_i (4'd0),
|
||||
.primitive_result_data_i ('0),
|
||||
.primitive_result_words_i (3'd0),
|
||||
.primitive_result_kernel_i(1'b0),
|
||||
.primitive_error_i (1'b0),
|
||||
.primitive_error_code_i (6'd0),
|
||||
.primitive_error_stage_i (4'd0),
|
||||
.primitive_error_reason_i (8'd0),
|
||||
.fetch_o (s_fetch),
|
||||
.fetch_addr_o (s_fetch_addr),
|
||||
.fetch_data_i (s_fetch_data),
|
||||
.fetch_valid_i (s_fetch_pending_q),
|
||||
.stat_o (status),
|
||||
.retired_o (retired),
|
||||
.gpr_o (s_gpr),
|
||||
.trapped_o (trapped),
|
||||
.trap_event_o (trap_event),
|
||||
.abort_done_o (abort_done),
|
||||
.fault_valid_o (fault_valid),
|
||||
.fault_code_o (fault_code),
|
||||
.fault_stage_o (fault_stage),
|
||||
.fault_resp_o (fault_resp),
|
||||
.fault_index_o (fault_index),
|
||||
.fault_addr_o (fault_addr),
|
||||
.fault_detail_o (fault_detail),
|
||||
.perf_retired_o (),
|
||||
.active_scratch_base_o (),
|
||||
.active_scratch_bytes_o (),
|
||||
.active_primitive_mask_o (),
|
||||
.active_table_offset_o (),
|
||||
.active_table_bytes_o (),
|
||||
.launch_epoch_o (),
|
||||
.idle_o (idle)
|
||||
);
|
||||
|
||||
initial begin
|
||||
|
||||
@@ -33,6 +33,15 @@ APU_LOADER_SCENARIOS = {
|
||||
"apu_loader_abort": 5,
|
||||
"apu_loader_resource_reset": 6,
|
||||
}
|
||||
APU_PRIMITIVE_SCENARIOS = {
|
||||
"apu_primitives_invalid_read": 0,
|
||||
"apu_primitives_local": 1,
|
||||
"apu_primitives_kernel": 2,
|
||||
"apu_primitives_input_fifo": 3,
|
||||
"apu_primitives_alignment": 4,
|
||||
"apu_primitives_output_fifo": 5,
|
||||
"apu_primitives_local_overflow": 6,
|
||||
}
|
||||
|
||||
|
||||
def target_defines(target: str) -> list[str]:
|
||||
@@ -45,6 +54,11 @@ def target_defines(target: str) -> list[str]:
|
||||
"+define+SYNTHESIS",
|
||||
f"+define+APU_LOADER_FORMAL_SCENARIO={scenario}",
|
||||
]
|
||||
if target in APU_PRIMITIVE_SCENARIOS:
|
||||
return [
|
||||
"+define+SV_ASSRT_DISABLE",
|
||||
f"+define+APU_PRIMITIVES_FORMAL_SCENARIO={APU_PRIMITIVE_SCENARIOS[target]}",
|
||||
]
|
||||
if target == "opipsram":
|
||||
return ["+define+PDK_BEHAV"]
|
||||
if target == "sysctrl":
|
||||
@@ -273,6 +287,20 @@ def source_files(target: str) -> list[Path]:
|
||||
MULTIMEDIA / "apu_dma.sv",
|
||||
SCRIPT_DIR / "apu_formal.sv",
|
||||
]
|
||||
if target == "apu_primitives" or target in APU_PRIMITIVE_SCENARIOS:
|
||||
return [
|
||||
COMMON_RTL / "utils/fifo.sv",
|
||||
MULTIMEDIA / "apu_microcode_pkg.sv",
|
||||
MULTIMEDIA / "apu_local_sram.sv",
|
||||
MULTIMEDIA / "apu_bitstream_engine.sv",
|
||||
MULTIMEDIA / "apu_entropy_engine.sv",
|
||||
MULTIMEDIA / "apu_reconstruction_engine.sv",
|
||||
MULTIMEDIA / "apu_transform_engine.sv",
|
||||
MULTIMEDIA / "apu_resampler.sv",
|
||||
MULTIMEDIA / "apu_kernel_engine.sv",
|
||||
MULTIMEDIA / "apu_primitive_dispatcher.sv",
|
||||
SCRIPT_DIR / "apu_primitives_formal.sv",
|
||||
]
|
||||
if target == "apu_loader" or target in APU_LOADER_SCENARIOS:
|
||||
return [
|
||||
ROOT / "rtl/tech/tc_sram.sv",
|
||||
@@ -362,6 +390,8 @@ def parse_args() -> argparse.Namespace:
|
||||
"opipsram",
|
||||
"dma",
|
||||
"apu",
|
||||
"apu_primitives",
|
||||
*APU_PRIMITIVE_SCENARIOS,
|
||||
*APU_LOADER_SCENARIOS,
|
||||
"apu_sequencer",
|
||||
"gateway_a",
|
||||
|
||||
+10
-7
@@ -31,12 +31,13 @@ FORMAL_APU_TIMEOUT ?= 300
|
||||
FORMAL_GATEWAY_A_TIMEOUT ?= 120
|
||||
FORMAL_SDIO_TIMEOUT ?= 120
|
||||
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_TARGET_TIMEOUT = $(if $(filter dma,$*),$(FORMAL_DMA_TIMEOUT),$(if $(filter apu apu_sequencer $(FORMAL_APU_LOADER_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_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 dma,$*),$(FORMAL_DMA_TIMEOUT),$(if $(filter apu 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_LOADER_TARGETS),$*),$(FORMAL_APU_LOADER_DEPTH),$(if $(filter apu_sequencer,$*),$(FORMAL_APU_SEQUENCER_DEPTH),$(FORMAL_APU_DEPTH)))
|
||||
FORMAL_APU_TARGET_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,$*)
|
||||
FORMAL_COMPACT_COVER_TARGETS := i2c opipsram dma $(FORMAL_APU_LOADER_TARGETS)
|
||||
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint dvp i2s psram onchip_ram opipsram dma apu $(FORMAL_APU_LOADER_TARGETS) apu_sequencer gateway_a sdio
|
||||
FORMAL_TARGET_TOP = $(if $(filter $(FORMAL_APU_LOADER_TARGETS),$*),apu_loader,$(if $(filter $(FORMAL_APU_PRIMITIVE_TARGETS),$*),apu_primitives,$*))
|
||||
FORMAL_COMPACT_COVER_TARGETS := i2c opipsram dma $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS)
|
||||
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint dvp i2s psram onchip_ram opipsram dma apu $(FORMAL_APU_PRIMITIVE_TARGETS) $(FORMAL_APU_LOADER_TARGETS) apu_sequencer gateway_a sdio
|
||||
FORMAL_FILELIST_GENERATOR := $(RTL_PATH)/formal/generate_formal_filelist.py
|
||||
FORMAL_SBY_GENERATOR := $(RTL_PATH)/formal/generate_sby_config.py
|
||||
FORMAL_RESULT_GENERATOR := $(RTL_PATH)/formal/formal_results.py
|
||||
@@ -76,6 +77,8 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
|
||||
$(RTL_PATH)/formal/dma_formal_props.sv \
|
||||
$(RTL_PATH)/formal/apu_formal.sv \
|
||||
$(RTL_PATH)/formal/apu_formal_props.sv \
|
||||
$(RTL_PATH)/formal/apu_primitives_formal.sv \
|
||||
$(RTL_PATH)/formal/apu_primitives_formal_props.sv \
|
||||
$(RTL_PATH)/formal/apu_loader_formal.sv \
|
||||
$(RTL_PATH)/formal/apu_loader_formal_props.sv \
|
||||
$(RTL_PATH)/formal/apu_sequencer_formal.sv \
|
||||
@@ -212,14 +215,14 @@ $(FORMAL_DIR)/%/prove.sby: $(FORMAL_DIR)/%/design.v \
|
||||
$(FORMAL_SBY_GENERATOR)
|
||||
python3 $(FORMAL_SBY_GENERATOR) --top $(FORMAL_TARGET_TOP)_formal --input $< \
|
||||
--properties $(RTL_PATH)/formal/$(FORMAL_TARGET_TOP)_formal_props.sv --solver $(FORMAL_SOLVER) \
|
||||
--mode $(if $(filter dma apu apu_sequencer $(FORMAL_APU_LOADER_TARGETS) gateway_a sdio onchip_ram,$*),bmc,prove) --depth $(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_DEPTH),$(if $(filter apu apu_sequencer $(FORMAL_APU_LOADER_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 dma apu apu_primitives apu_sequencer $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS) gateway_a sdio onchip_ram,$*),bmc,prove) --depth $(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_DEPTH),$(if $(filter apu 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 $(RTL_PATH)/formal/$(FORMAL_TARGET_TOP)_formal_props.sv --solver $(FORMAL_SOLVER) \
|
||||
--mode cover --depth $(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_COVER_DEPTH),$(if $(filter apu apu_sequencer $(FORMAL_APU_LOADER_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 pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_COVER_DEPTH),$(if $(filter apu 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_COMPACT_COVER_TARGETS),$*),--no-vcd) --output $@
|
||||
@if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi
|
||||
|
||||
@@ -316,7 +319,7 @@ formal-opipsram: $(FORMAL_DIR)/opipsram/.stamp | manifest
|
||||
|
||||
formal-dma: $(FORMAL_DIR)/dma/.stamp | manifest
|
||||
|
||||
formal-apu: $(FORMAL_DIR)/apu/.stamp | manifest
|
||||
formal-apu: $(FORMAL_DIR)/apu/.stamp $(addsuffix /.stamp,$(addprefix $(FORMAL_DIR)/,$(FORMAL_APU_PRIMITIVE_TARGETS))) | manifest
|
||||
|
||||
formal-apu-loader: $(addsuffix /.stamp,$(addprefix $(FORMAL_DIR)/,$(FORMAL_APU_LOADER_TARGETS))) | manifest
|
||||
|
||||
|
||||
@@ -135,15 +135,23 @@
|
||||
"rtl/ip/multimedia": [
|
||||
"apb4_apu.sv",
|
||||
"apb4_jpeg.sv",
|
||||
"apu_bitstream_engine.sv",
|
||||
"apu_codec_sequencer.sv",
|
||||
"apu_control_store.sv",
|
||||
"apu_dma.sv",
|
||||
"apu_define.svh",
|
||||
"apu_entropy_engine.sv",
|
||||
"apu_kernel_engine.sv",
|
||||
"apu_local_sram.sv",
|
||||
"apu_microcode_loader.sv",
|
||||
"apu_microcode_pkg.sv",
|
||||
"apu_primitive_dispatcher.sv",
|
||||
"apu_reconstruction_engine.sv",
|
||||
"apu_resampler.sv",
|
||||
"apu_ring_scheduler.sv",
|
||||
"apu_reg.sv",
|
||||
"apu_stream_router.sv",
|
||||
"apu_transform_engine.sv",
|
||||
"axi4s_dvp.sv",
|
||||
"dvp_core.sv",
|
||||
"dvp_define.svh",
|
||||
|
||||
+5
-4
@@ -4,10 +4,11 @@ This directory contains the Python implementation of setup, dependency locking,
|
||||
build manifests, flow execution, simulation checks, regression orchestration,
|
||||
quality checks, metrics, packaging, and cleanup.
|
||||
|
||||
`apu_mcasm.py`, `apu_isa.py`, and `apu_interpreter.py` are the frozen APU
|
||||
microcode V1 assembler/ABI model and P3 reference interpreter. They accept only
|
||||
the APU instruction language and deliberately have no C, ELF, RV32, dynamic
|
||||
linking, or runtime code-generation path.
|
||||
`apu_mcasm.py`, `apu_isa.py`, `apu_interpreter.py`, and `apu_primitives.py` are
|
||||
the frozen APU microcode V1 assembler/ABI model, P3/P4 target interpreters, and
|
||||
bit-accurate P4 primitive model. They accept only the APU instruction language
|
||||
and deliberately have no C, ELF, RV32, dynamic linking, or runtime
|
||||
code-generation path.
|
||||
|
||||
Scripts are part of the build contract. Prefer existing helpers over ad-hoc
|
||||
shell behavior, preserve structured JSON results, and keep setup/download
|
||||
|
||||
+226
-19
@@ -1,4 +1,4 @@
|
||||
"""Reference interpreter for the enabled APU-P3 microcode subset."""
|
||||
"""Reference interpreter for the enabled APU-P3/P4 microcode targets."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -8,15 +8,20 @@ from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
|
||||
from apu_isa import (
|
||||
APUMC_TARGETS,
|
||||
BitstreamOpcode,
|
||||
ControlOpcode,
|
||||
EntropyOpcode,
|
||||
Entry,
|
||||
Instruction,
|
||||
InstructionClass,
|
||||
LocalOpcode,
|
||||
ScalarOpcode,
|
||||
parse_apumc,
|
||||
trap_detail,
|
||||
validate_p3_instruction,
|
||||
validate_instruction,
|
||||
)
|
||||
from apu_primitives import PrimitiveBam, PrimitiveFault, PrimitiveResult
|
||||
|
||||
|
||||
class SequencerTrap(RuntimeError):
|
||||
@@ -55,15 +60,36 @@ class Machine:
|
||||
loops: dict[int, tuple[int, int]] = field(default_factory=dict)
|
||||
watchdog: int = 0
|
||||
trace: list[dict[str, object]] = field(default_factory=list)
|
||||
target: str = "p3"
|
||||
primitives: PrimitiveBam | None = None
|
||||
primitive_cycles: list[int] = field(default_factory=list)
|
||||
kernel_result: PrimitiveResult | None = field(default=None, init=False)
|
||||
kernel_fault: PrimitiveFault | None = field(default=None, init=False)
|
||||
kernel_pending_fault: PrimitiveFault | None = field(default=None, init=False)
|
||||
kernel_pending_dst: int | None = field(default=None, init=False)
|
||||
kernel_cycles_remaining: int = field(default=0, init=False)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self.pc = self.entry.entry_pc
|
||||
self.begin_entry(self.entry)
|
||||
|
||||
@staticmethod
|
||||
def _signed(value: int) -> int:
|
||||
return value if value < 0x80000000 else value - 0x100000000
|
||||
|
||||
def _predicate(self, predicate: int) -> bool:
|
||||
input_ready = self.predicate_inputs.input_ready
|
||||
output_ready = self.predicate_inputs.output_ready
|
||||
kernel_done = self.predicate_inputs.kernel_done
|
||||
input_exhausted = self.predicate_inputs.input_exhausted
|
||||
if self.target == "p4" and self.primitives is not None:
|
||||
input_ready = not self.primitives.input_fifo.empty
|
||||
output_ready = not self.primitives.output_fifo.full
|
||||
kernel_done = self.primitives.kernel_done
|
||||
input_exhausted = (
|
||||
self.primitives.eof
|
||||
and self.primitives.input_fifo.empty
|
||||
and not self.primitives.bits
|
||||
)
|
||||
values = (
|
||||
True,
|
||||
self.eq,
|
||||
@@ -72,10 +98,10 @@ class Machine:
|
||||
not self.signed_lt,
|
||||
self.unsigned_lt,
|
||||
not self.unsigned_lt,
|
||||
self.predicate_inputs.input_exhausted,
|
||||
self.predicate_inputs.input_ready,
|
||||
self.predicate_inputs.output_ready,
|
||||
self.predicate_inputs.kernel_done,
|
||||
input_exhausted,
|
||||
input_ready,
|
||||
output_ready,
|
||||
kernel_done,
|
||||
self.predicate_inputs.transport_done,
|
||||
)
|
||||
if predicate >= len(values):
|
||||
@@ -85,6 +111,86 @@ class Machine:
|
||||
def _write(self, register: int, value: int) -> None:
|
||||
self.registers[register] = value & 0xFFFFFFFF
|
||||
|
||||
def _complete_kernel(self) -> None:
|
||||
if self.kernel_pending_fault is not None:
|
||||
self.kernel_fault = self.kernel_pending_fault
|
||||
elif self.kernel_result is not None:
|
||||
for register, value in self.kernel_result.writes.items():
|
||||
self._write(register, value)
|
||||
if self.primitives is not None:
|
||||
self.primitives.kernel_done = True
|
||||
self.kernel_result = None
|
||||
self.kernel_pending_fault = None
|
||||
self.kernel_pending_dst = None
|
||||
self.kernel_cycles_remaining = 0
|
||||
|
||||
def _tick_kernel(self) -> None:
|
||||
if self.kernel_pending_dst is None:
|
||||
return
|
||||
if self.kernel_cycles_remaining > 0:
|
||||
self.kernel_cycles_remaining -= 1
|
||||
if self.kernel_cycles_remaining == 0:
|
||||
self._complete_kernel()
|
||||
|
||||
def _pending_dependency(self, instruction: Instruction) -> bool:
|
||||
pending = self.kernel_pending_dst
|
||||
if pending is None:
|
||||
return False
|
||||
instruction_class = instruction.instruction_class
|
||||
opcode = instruction.opcode
|
||||
if instruction_class == InstructionClass.CONTROL:
|
||||
if opcode == ControlOpcode.END:
|
||||
return True
|
||||
return opcode == ControlOpcode.LOOP_SETUP and instruction.src0 == pending
|
||||
if instruction_class == InstructionClass.SCALAR:
|
||||
if opcode == ScalarOpcode.MOV:
|
||||
return instruction.src0 == pending or instruction.dst == pending
|
||||
if opcode == ScalarOpcode.MOVI:
|
||||
return instruction.dst == pending
|
||||
if opcode == ScalarOpcode.CMP:
|
||||
return instruction.src0 == pending or instruction.src1 == pending
|
||||
if opcode == ScalarOpcode.SAT:
|
||||
return instruction.src0 == pending or instruction.dst == pending
|
||||
return (
|
||||
instruction.src0 == pending
|
||||
or instruction.src1 == pending
|
||||
or instruction.dst == pending
|
||||
)
|
||||
return True
|
||||
|
||||
def begin_entry(self, entry: Entry | None = None) -> None:
|
||||
if entry is not None:
|
||||
self.entry = entry
|
||||
self.pc = self.entry.entry_pc
|
||||
self.retired = 0
|
||||
self.registers = [0] * 16
|
||||
self.eq = False
|
||||
self.signed_lt = False
|
||||
self.unsigned_lt = False
|
||||
self.return_stack.clear()
|
||||
self.loops.clear()
|
||||
self.watchdog = 0
|
||||
self.kernel_result = None
|
||||
self.kernel_fault = None
|
||||
self.kernel_pending_fault = None
|
||||
self.kernel_pending_dst = None
|
||||
self.kernel_cycles_remaining = 0
|
||||
if self.primitives is not None:
|
||||
self.primitives.reset_mutable()
|
||||
|
||||
def abort(self) -> None:
|
||||
self.kernel_result = None
|
||||
self.kernel_fault = None
|
||||
self.kernel_pending_fault = None
|
||||
self.kernel_pending_dst = None
|
||||
self.kernel_cycles_remaining = 0
|
||||
self.watchdog = 0
|
||||
if self.primitives is not None:
|
||||
self.primitives.reset_mutable()
|
||||
|
||||
def reset(self) -> None:
|
||||
self.begin_entry(self.entry)
|
||||
|
||||
def _retire(self, instruction: Instruction, predicate_true: bool) -> None:
|
||||
self.retired += 1
|
||||
self.watchdog = 0
|
||||
@@ -97,7 +203,9 @@ class Machine:
|
||||
}
|
||||
)
|
||||
|
||||
def _no_retirement_cycle(self, instruction: Instruction) -> None:
|
||||
def _no_retirement_cycle(self, instruction: Instruction, *, tick_kernel: bool = True) -> None:
|
||||
if tick_kernel:
|
||||
self._tick_kernel()
|
||||
self.watchdog += 1
|
||||
if self.timeout <= 0 or self.watchdog >= self.timeout:
|
||||
raise SequencerTrap(7, self.pc, instruction)
|
||||
@@ -115,8 +223,13 @@ class Machine:
|
||||
if self.pc < self.entry.first_pc or self.pc > self.entry.last_pc:
|
||||
raise SequencerTrap(2, self.pc, Instruction(0, 0))
|
||||
instruction = self.instructions[self.pc]
|
||||
self._tick_kernel()
|
||||
if self.kernel_fault is not None:
|
||||
fault = self.kernel_fault
|
||||
self.kernel_fault = None
|
||||
raise SequencerTrap(fault.reason, self.pc, instruction)
|
||||
try:
|
||||
validate_p3_instruction(instruction)
|
||||
validate_instruction(instruction, self.target)
|
||||
except ValueError as error:
|
||||
raise SequencerTrap(1, self.pc, instruction) from error
|
||||
if self.retired >= self.entry.max_retired:
|
||||
@@ -129,6 +242,10 @@ class Machine:
|
||||
self.pc += 1
|
||||
return False
|
||||
|
||||
if self._pending_dependency(instruction):
|
||||
self._no_retirement_cycle(instruction, tick_kernel=False)
|
||||
return False
|
||||
|
||||
next_pc = self.pc + 1
|
||||
terminal = False
|
||||
if instruction.instruction_class == InstructionClass.CONTROL:
|
||||
@@ -167,7 +284,18 @@ class Machine:
|
||||
next_pc = target
|
||||
else:
|
||||
del self.loops[slot]
|
||||
else:
|
||||
elif opcode == ControlOpcode.WAIT:
|
||||
ready = False
|
||||
if self.primitives is not None:
|
||||
ready = (
|
||||
(instruction.aux == 1 and self.primitives.kernel_done)
|
||||
or (instruction.aux == 2 and not self.primitives.input_fifo.empty)
|
||||
or (instruction.aux == 3 and not self.primitives.output_fifo.full)
|
||||
)
|
||||
if not ready:
|
||||
self._no_retirement_cycle(instruction, tick_kernel=False)
|
||||
return False
|
||||
elif instruction.instruction_class == InstructionClass.SCALAR:
|
||||
opcode = ScalarOpcode(instruction.opcode)
|
||||
source0 = self.registers[instruction.src0]
|
||||
source1 = self.registers[instruction.src1]
|
||||
@@ -205,6 +333,71 @@ class Machine:
|
||||
instruction.dst,
|
||||
self._saturate(source0, instruction.aux & 0x1F, bool(instruction.aux & 0x20)),
|
||||
)
|
||||
else:
|
||||
if self.primitives is None:
|
||||
raise SequencerTrap(6, self.pc, instruction)
|
||||
if instruction.instruction_class == InstructionClass.LOCAL:
|
||||
opcode = LocalOpcode(instruction.opcode)
|
||||
if (opcode == LocalOpcode.FIFO_POP and self.primitives.input_fifo.empty) or (
|
||||
opcode == LocalOpcode.FIFO_PUSH and self.primitives.output_fifo.full
|
||||
):
|
||||
self._no_retirement_cycle(instruction, tick_kernel=False)
|
||||
return False
|
||||
if instruction.instruction_class == InstructionClass.BITSTREAM:
|
||||
opcode = BitstreamOpcode(instruction.opcode)
|
||||
required = (
|
||||
instruction.immediate & 0x3F
|
||||
if opcode
|
||||
in (
|
||||
BitstreamOpcode.REFILL,
|
||||
BitstreamOpcode.PEEK,
|
||||
BitstreamOpcode.GET,
|
||||
BitstreamOpcode.SKIP,
|
||||
)
|
||||
else 1
|
||||
)
|
||||
self.primitives._refill(required)
|
||||
if len(self.primitives.bits) < required and not self.primitives.eof:
|
||||
self._no_retirement_cycle(instruction, tick_kernel=False)
|
||||
return False
|
||||
if instruction.instruction_class == InstructionClass.ENTROPY:
|
||||
opcode = EntropyOpcode(instruction.opcode)
|
||||
if opcode <= EntropyOpcode.HUFF_QUAD:
|
||||
required = instruction.aux
|
||||
elif opcode == EntropyOpcode.UNARY:
|
||||
required = instruction.immediate + 1
|
||||
elif opcode == EntropyOpcode.SIGN_RESTORE:
|
||||
required = 0 if self.registers[instruction.src0] == 0 else (
|
||||
self.registers[instruction.src1] & 0x1F
|
||||
) + 1
|
||||
else:
|
||||
required = 1
|
||||
self.primitives._refill(required)
|
||||
if len(self.primitives.bits) < required and not self.primitives.eof:
|
||||
self._no_retirement_cycle(instruction, tick_kernel=False)
|
||||
return False
|
||||
try:
|
||||
if instruction.instruction_class == InstructionClass.KERNEL:
|
||||
self.primitives.kernel_done = False
|
||||
result = self.primitives.execute(instruction, self.registers, self.entry)
|
||||
except PrimitiveFault as error:
|
||||
if instruction.instruction_class != InstructionClass.KERNEL or error.reason == 5:
|
||||
raise SequencerTrap(error.reason, self.pc, instruction) from error
|
||||
result = PrimitiveResult(cycles=1, kernel_done=True)
|
||||
self.kernel_pending_fault = error
|
||||
self.primitive_cycles.append(result.cycles)
|
||||
if instruction.instruction_class == InstructionClass.KERNEL:
|
||||
self.primitives.kernel_done = False
|
||||
self.kernel_result = result
|
||||
self.kernel_pending_dst = instruction.dst
|
||||
self.kernel_cycles_remaining = max(result.cycles - 1, 0)
|
||||
if self.kernel_cycles_remaining == 0:
|
||||
self._complete_kernel()
|
||||
else:
|
||||
for register, value in result.writes.items():
|
||||
self._write(register, value)
|
||||
for _ in range(max(result.cycles - 1, 0)):
|
||||
self._no_retirement_cycle(instruction)
|
||||
|
||||
self._retire(instruction, predicate_true)
|
||||
if not terminal:
|
||||
@@ -214,30 +407,36 @@ class Machine:
|
||||
return terminal
|
||||
|
||||
def run(self) -> dict[str, object]:
|
||||
fetch_required = True
|
||||
while True:
|
||||
if self.pc < self.entry.first_pc or self.pc > self.entry.last_pc:
|
||||
raise SequencerTrap(2, self.pc, Instruction(0, 0))
|
||||
instruction = self.instructions[self.pc]
|
||||
for _ in range(self.fetch_no_retirement_cycles):
|
||||
self._no_retirement_cycle(instruction)
|
||||
remaining_stalls = self.stall_cycles.get(self.pc, 0)
|
||||
while remaining_stalls > 0:
|
||||
self._no_retirement_cycle(instruction)
|
||||
remaining_stalls -= 1
|
||||
self.stall_cycles[self.pc] = 0
|
||||
if fetch_required:
|
||||
for _ in range(self.fetch_no_retirement_cycles):
|
||||
self._no_retirement_cycle(instruction)
|
||||
remaining_stalls = self.stall_cycles.get(self.pc, 0)
|
||||
while remaining_stalls > 0:
|
||||
self._no_retirement_cycle(instruction)
|
||||
remaining_stalls -= 1
|
||||
self.stall_cycles[self.pc] = 0
|
||||
retired_before = self.retired
|
||||
if self.step():
|
||||
break
|
||||
fetch_required = self.retired != retired_before
|
||||
return {
|
||||
"pc": self.pc,
|
||||
"retired": self.retired,
|
||||
"registers": self.registers,
|
||||
"trace": self.trace,
|
||||
"primitive_cycles": self.primitive_cycles,
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("bundle", type=Path)
|
||||
parser.add_argument("--target", choices=APUMC_TARGETS, default="p3")
|
||||
parser.add_argument("--entry", type=int, choices=range(3), default=0)
|
||||
parser.add_argument("--timeout", type=int, default=0xFFFF)
|
||||
parser.add_argument("--input-exhausted", action="store_true")
|
||||
@@ -246,7 +445,7 @@ def main() -> int:
|
||||
parser.add_argument("--kernel-done", action="store_true")
|
||||
parser.add_argument("--transport-done", action="store_true")
|
||||
args = parser.parse_args()
|
||||
instructions, entries, _ = parse_apumc(args.bundle.read_bytes())
|
||||
instructions, entries, header = parse_apumc(args.bundle.read_bytes(), args.target)
|
||||
inputs = PredicateInputs(
|
||||
input_exhausted=args.input_exhausted,
|
||||
input_ready=args.input_ready,
|
||||
@@ -256,7 +455,15 @@ def main() -> int:
|
||||
)
|
||||
print(
|
||||
json.dumps(
|
||||
Machine(instructions, entries[args.entry], inputs, args.timeout).run(), sort_keys=True
|
||||
Machine(
|
||||
instructions,
|
||||
entries[args.entry],
|
||||
inputs,
|
||||
args.timeout,
|
||||
target=args.target,
|
||||
primitives=PrimitiveBam() if args.target == "p4" else None,
|
||||
).run(),
|
||||
sort_keys=True,
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
+324
-74
@@ -17,6 +17,9 @@ APUMC_ENTRY_COUNT = 3
|
||||
APUMC_INSTRUCTION_OFFSET = 192
|
||||
APUMC_MAX_INSTRUCTIONS = 2048
|
||||
APUMC_P3_CAPABILITY_MASK = 0
|
||||
APUMC_P4_CAPABILITY_MASK = 0x0000FFFF
|
||||
APUMC_P4_TABLE_SCRATCH_BYTES = 0x6000
|
||||
APUMC_TARGETS = ("p3", "p4")
|
||||
|
||||
|
||||
class InstructionClass(IntEnum):
|
||||
@@ -58,6 +61,50 @@ class ScalarOpcode(IntEnum):
|
||||
SAT = 13
|
||||
|
||||
|
||||
class BitstreamOpcode(IntEnum):
|
||||
REFILL = 0
|
||||
PEEK = 1
|
||||
GET = 2
|
||||
SKIP = 3
|
||||
ALIGN = 4
|
||||
FRAME_SYNC = 5
|
||||
CRC8 = 6
|
||||
CRC16 = 7
|
||||
|
||||
|
||||
class EntropyOpcode(IntEnum):
|
||||
HUFF_SYMBOL = 0
|
||||
HUFF_PAIR = 1
|
||||
HUFF_QUAD = 2
|
||||
UNARY = 3
|
||||
RICE4 = 4
|
||||
RICE5 = 5
|
||||
SIGN_RESTORE = 6
|
||||
|
||||
|
||||
class LocalOpcode(IntEnum):
|
||||
LD32 = 0
|
||||
ST32 = 1
|
||||
TABLE8 = 2
|
||||
TABLE16 = 3
|
||||
TABLE32 = 4
|
||||
FIFO_POP = 5
|
||||
FIFO_PUSH = 6
|
||||
|
||||
|
||||
class KernelOpcode(IntEnum):
|
||||
REQUANT = 0
|
||||
STEREO = 1
|
||||
IMDCT6 = 2
|
||||
IMDCT18 = 3
|
||||
DCT32_POLY = 4
|
||||
FIXED = 5
|
||||
LPC = 6
|
||||
DECORRELATE = 7
|
||||
RESAMPLE = 8
|
||||
PCM_PACK = 9
|
||||
|
||||
|
||||
PREDICATES = {
|
||||
"always": 0,
|
||||
"eq": 1,
|
||||
@@ -184,6 +231,9 @@ def abi_manifest() -> dict[str, object]:
|
||||
"table": 7,
|
||||
},
|
||||
"p3_implemented_primitive_mask": APUMC_P3_CAPABILITY_MASK,
|
||||
"p4_implemented_primitive_mask": APUMC_P4_CAPABILITY_MASK,
|
||||
"p4_local_data_bytes": 0x1C000,
|
||||
"p4_local_table_scratch_bytes": APUMC_P4_TABLE_SCRATCH_BYTES,
|
||||
}
|
||||
|
||||
|
||||
@@ -281,9 +331,36 @@ def _require_zero(instruction: Instruction, names: Iterable[str]) -> None:
|
||||
raise ValueError(f"{name} is reserved for this opcode")
|
||||
|
||||
|
||||
def validate_p3_instruction(instruction: Instruction) -> None:
|
||||
"""Validate one instruction against the P3 class-0/class-1 subset."""
|
||||
def _target_mask(target: str) -> int:
|
||||
if target not in APUMC_TARGETS:
|
||||
raise ValueError(f"unknown APU target {target}")
|
||||
return APUMC_P3_CAPABILITY_MASK if target == "p3" else APUMC_P4_CAPABILITY_MASK
|
||||
|
||||
|
||||
def instruction_required_mask(instruction: Instruction) -> int:
|
||||
instruction_class = instruction.instruction_class
|
||||
opcode = instruction.opcode
|
||||
if instruction_class == InstructionClass.CONTROL and opcode == ControlOpcode.WAIT:
|
||||
if instruction.aux == 1:
|
||||
return 0x0000FFC0
|
||||
if instruction.aux in (2, 3):
|
||||
return 1 << 5
|
||||
return 0
|
||||
if instruction_class == InstructionClass.BITSTREAM:
|
||||
return (1 << 0) | ((1 << 1) if opcode in (6, 7) else 0)
|
||||
if instruction_class == InstructionClass.ENTROPY:
|
||||
return (1 << 0) | (1 << (2 if opcode <= 2 else 3))
|
||||
if instruction_class == InstructionClass.LOCAL:
|
||||
return 1 << (5 if opcode in (5, 6) else 4)
|
||||
if instruction_class == InstructionClass.KERNEL:
|
||||
return 1 << (opcode + 6)
|
||||
return 0
|
||||
|
||||
|
||||
def validate_instruction(instruction: Instruction, target: str = "p3") -> None:
|
||||
"""Validate one frozen V1 instruction for the selected implementation target."""
|
||||
|
||||
target_mask = _target_mask(target)
|
||||
if instruction.predicate > 11:
|
||||
raise ValueError("predicate is reserved")
|
||||
if instruction.instruction_class == InstructionClass.CONTROL:
|
||||
@@ -318,45 +395,136 @@ def validate_p3_instruction(instruction: Instruction) -> None:
|
||||
_require_zero(instruction, ("dst", "src0", "src1", "immediate"))
|
||||
if instruction.predicate != 0 or instruction.aux > 5:
|
||||
raise ValueError("WAIT encoding is invalid")
|
||||
raise ValueError("WAIT requires a primitive unavailable in P3")
|
||||
if target == "p3" or instruction.aux not in (1, 2, 3):
|
||||
raise ValueError(f"WAIT source is unavailable in {target.upper()}")
|
||||
return
|
||||
|
||||
if instruction.instruction_class != InstructionClass.SCALAR:
|
||||
if instruction.instruction_class <= 6:
|
||||
raise ValueError("instruction class is unavailable in P3")
|
||||
if instruction.instruction_class == InstructionClass.SCALAR:
|
||||
if instruction.opcode > ScalarOpcode.SAT:
|
||||
raise ValueError("scalar opcode is unallocated")
|
||||
opcode = ScalarOpcode(instruction.opcode)
|
||||
if opcode == ScalarOpcode.MOV:
|
||||
_require_zero(instruction, ("src1", "aux", "immediate"))
|
||||
elif opcode == ScalarOpcode.MOVI:
|
||||
_require_zero(instruction, ("src0", "src1", "aux"))
|
||||
elif opcode in (
|
||||
ScalarOpcode.ADD,
|
||||
ScalarOpcode.SUB,
|
||||
ScalarOpcode.AND,
|
||||
ScalarOpcode.OR,
|
||||
ScalarOpcode.XOR,
|
||||
ScalarOpcode.SHL,
|
||||
ScalarOpcode.SHR,
|
||||
ScalarOpcode.SAR,
|
||||
):
|
||||
_require_zero(instruction, ("aux", "immediate"))
|
||||
elif opcode == ScalarOpcode.CMP:
|
||||
_require_zero(instruction, ("dst", "aux", "immediate"))
|
||||
elif opcode in (ScalarOpcode.MIN, ScalarOpcode.MAX):
|
||||
_require_zero(instruction, ("immediate",))
|
||||
if instruction.aux > 1:
|
||||
raise ValueError("MIN/MAX aux must select unsigned or signed")
|
||||
elif opcode == ScalarOpcode.SAT:
|
||||
_require_zero(instruction, ("src1", "immediate"))
|
||||
if instruction.aux & 0xC0 or (instruction.aux & 0x1F) not in (0, 8, 16, 24):
|
||||
raise ValueError("SAT width/sign encoding is invalid")
|
||||
return
|
||||
|
||||
if instruction.instruction_class > InstructionClass.TRANSPORT:
|
||||
raise ValueError("instruction class is invalid in V1")
|
||||
if instruction.opcode > ScalarOpcode.SAT:
|
||||
raise ValueError("scalar opcode is unallocated")
|
||||
if target == "p3" or instruction.instruction_class == InstructionClass.TRANSPORT:
|
||||
raise ValueError(f"instruction class is unavailable in {target.upper()}")
|
||||
|
||||
opcode = ScalarOpcode(instruction.opcode)
|
||||
if opcode == ScalarOpcode.MOV:
|
||||
_require_zero(instruction, ("src1", "aux", "immediate"))
|
||||
elif opcode == ScalarOpcode.MOVI:
|
||||
_require_zero(instruction, ("src0", "src1", "aux"))
|
||||
elif opcode in (
|
||||
ScalarOpcode.ADD,
|
||||
ScalarOpcode.SUB,
|
||||
ScalarOpcode.AND,
|
||||
ScalarOpcode.OR,
|
||||
ScalarOpcode.XOR,
|
||||
ScalarOpcode.SHL,
|
||||
ScalarOpcode.SHR,
|
||||
ScalarOpcode.SAR,
|
||||
):
|
||||
_require_zero(instruction, ("aux", "immediate"))
|
||||
elif opcode == ScalarOpcode.CMP:
|
||||
_require_zero(instruction, ("dst", "aux", "immediate"))
|
||||
elif opcode in (ScalarOpcode.MIN, ScalarOpcode.MAX):
|
||||
_require_zero(instruction, ("immediate",))
|
||||
if instruction.aux > 1:
|
||||
raise ValueError("MIN/MAX aux must select unsigned or signed")
|
||||
elif opcode == ScalarOpcode.SAT:
|
||||
_require_zero(instruction, ("src1", "immediate"))
|
||||
if instruction.aux & 0xC0 or (instruction.aux & 0x1F) not in (0, 8, 16, 24):
|
||||
raise ValueError("SAT width/sign encoding is invalid")
|
||||
if instruction.instruction_class == InstructionClass.BITSTREAM:
|
||||
if instruction.opcode > BitstreamOpcode.CRC16:
|
||||
raise ValueError("bitstream opcode is unallocated")
|
||||
opcode = BitstreamOpcode(instruction.opcode)
|
||||
if opcode in (BitstreamOpcode.REFILL, BitstreamOpcode.PEEK, BitstreamOpcode.GET):
|
||||
_require_zero(instruction, ("src0", "src1", "aux"))
|
||||
if not 1 <= instruction.immediate <= 32:
|
||||
raise ValueError("bit width must be 1..32")
|
||||
elif opcode == BitstreamOpcode.SKIP:
|
||||
_require_zero(instruction, ("dst", "src0", "src1", "aux"))
|
||||
if not 1 <= instruction.immediate <= 32:
|
||||
raise ValueError("bit width must be 1..32")
|
||||
elif opcode == BitstreamOpcode.ALIGN:
|
||||
_require_zero(instruction, ("dst", "src0", "src1", "aux", "immediate"))
|
||||
elif opcode == BitstreamOpcode.FRAME_SYNC:
|
||||
if not 8 <= instruction.aux <= 16 or not 1 <= instruction.immediate <= 0xFFFF:
|
||||
raise ValueError("FRAME_SYNC width/limit is invalid")
|
||||
else:
|
||||
_require_zero(instruction, ("aux", "immediate"))
|
||||
elif instruction.instruction_class == InstructionClass.ENTROPY:
|
||||
if instruction.opcode > EntropyOpcode.SIGN_RESTORE:
|
||||
raise ValueError("entropy opcode is unallocated")
|
||||
opcode = EntropyOpcode(instruction.opcode)
|
||||
if opcode <= EntropyOpcode.HUFF_QUAD:
|
||||
if not 1 <= instruction.aux <= 24:
|
||||
raise ValueError("Huffman maximum length must be 1..24")
|
||||
if opcode == EntropyOpcode.HUFF_PAIR and instruction.dst > 14:
|
||||
raise ValueError("HUFF_PAIR destination must be 0..14")
|
||||
if opcode == EntropyOpcode.HUFF_QUAD and instruction.dst > 12:
|
||||
raise ValueError("HUFF_QUAD destination must be 0..12")
|
||||
_require_zero(instruction, ("immediate",))
|
||||
elif opcode == EntropyOpcode.UNARY:
|
||||
_require_zero(instruction, ("src0", "src1"))
|
||||
if instruction.aux > 1 or not 1 <= instruction.immediate <= 0xFFFF:
|
||||
raise ValueError("UNARY polarity/maximum is invalid")
|
||||
else:
|
||||
_require_zero(instruction, ("aux", "immediate"))
|
||||
elif instruction.instruction_class == InstructionClass.LOCAL:
|
||||
if instruction.opcode > LocalOpcode.FIFO_PUSH:
|
||||
raise ValueError("local opcode is unallocated")
|
||||
opcode = LocalOpcode(instruction.opcode)
|
||||
if opcode == LocalOpcode.LD32:
|
||||
_require_zero(instruction, ("src1", "aux"))
|
||||
if instruction.immediate >> 16:
|
||||
raise ValueError("LD32 immediate must fit 16 bits")
|
||||
elif opcode == LocalOpcode.ST32:
|
||||
_require_zero(instruction, ("dst", "aux"))
|
||||
if instruction.immediate >> 16:
|
||||
raise ValueError("ST32 immediate must fit 16 bits")
|
||||
elif opcode in (LocalOpcode.TABLE8, LocalOpcode.TABLE16, LocalOpcode.TABLE32):
|
||||
_require_zero(instruction, ("aux", "immediate"))
|
||||
elif opcode == LocalOpcode.FIFO_POP:
|
||||
_require_zero(instruction, ("src0", "src1", "aux", "immediate"))
|
||||
if instruction.dst > 14:
|
||||
raise ValueError("FIFO_POP destination must be 0..14")
|
||||
else:
|
||||
_require_zero(instruction, ("dst", "aux", "immediate"))
|
||||
elif instruction.instruction_class == InstructionClass.KERNEL:
|
||||
if instruction.opcode > KernelOpcode.PCM_PACK or not 1 <= instruction.immediate <= 0xFFFF:
|
||||
raise ValueError("kernel opcode/count is invalid")
|
||||
opcode = KernelOpcode(instruction.opcode)
|
||||
if opcode == KernelOpcode.REQUANT and (instruction.aux >> 6) == 3:
|
||||
raise ValueError("REQUANT output width is invalid")
|
||||
if opcode in (KernelOpcode.STEREO, KernelOpcode.DECORRELATE) and instruction.aux >> 2:
|
||||
raise ValueError("stereo mode is invalid")
|
||||
if opcode in (KernelOpcode.IMDCT6, KernelOpcode.IMDCT18, KernelOpcode.DCT32_POLY) and instruction.aux:
|
||||
raise ValueError("transform aux is reserved")
|
||||
if opcode == KernelOpcode.FIXED and instruction.aux > 4:
|
||||
raise ValueError("fixed predictor order is invalid")
|
||||
if opcode == KernelOpcode.LPC and not 1 <= instruction.aux <= 32:
|
||||
raise ValueError("LPC order is invalid")
|
||||
if opcode == KernelOpcode.RESAMPLE and instruction.aux > 15:
|
||||
raise ValueError("resampler profile is invalid")
|
||||
if opcode == KernelOpcode.PCM_PACK and (instruction.aux >> 3 or (instruction.aux & 3) > 1):
|
||||
raise ValueError("PCM pack format is invalid")
|
||||
|
||||
if instruction_required_mask(instruction) & ~target_mask:
|
||||
raise ValueError(f"required primitive is unavailable in {target.upper()}")
|
||||
|
||||
|
||||
def validate_entry(entry: Entry, instruction_count: int) -> None:
|
||||
def validate_p3_instruction(instruction: Instruction) -> None:
|
||||
validate_instruction(instruction, "p3")
|
||||
|
||||
|
||||
def validate_p4_instruction(instruction: Instruction) -> None:
|
||||
validate_instruction(instruction, "p4")
|
||||
|
||||
|
||||
def validate_entry(entry: Entry, instruction_count: int, target: str = "p3",
|
||||
table_payload_bytes: int = 0) -> None:
|
||||
if entry.format_id not in range(APUMC_ENTRY_COUNT):
|
||||
raise ValueError("entry format must be 0, 1, or 2")
|
||||
if not (0 <= entry.first_pc <= entry.entry_pc <= entry.last_pc < instruction_count):
|
||||
@@ -365,7 +533,7 @@ def validate_entry(entry: Entry, instruction_count: int) -> None:
|
||||
raise ValueError("maximum loop count must be 1..65535")
|
||||
if not 1 <= entry.max_retired <= 0xFFFFFF:
|
||||
raise ValueError("maximum retired count must be 1..16777215")
|
||||
if any(
|
||||
if target == "p3" and any(
|
||||
(
|
||||
entry.scratch_base,
|
||||
entry.scratch_bytes,
|
||||
@@ -375,9 +543,25 @@ def validate_entry(entry: Entry, instruction_count: int) -> None:
|
||||
)
|
||||
):
|
||||
raise ValueError("P3 entries cannot use scratch, table, or primitives")
|
||||
if target == "p4":
|
||||
if entry.primitive_mask & ~APUMC_P4_CAPABILITY_MASK:
|
||||
raise ValueError("P4 entry requires an unavailable primitive")
|
||||
if entry.scratch_base & 3 or entry.scratch_bytes & 3:
|
||||
raise ValueError("scratch range must be four-byte aligned")
|
||||
scratch_end = entry.scratch_base + entry.scratch_bytes
|
||||
if scratch_end > APUMC_P4_TABLE_SCRATCH_BYTES:
|
||||
raise ValueError("scratch range exceeds the P4 codec workspace")
|
||||
if entry.scratch_bytes and entry.scratch_base < table_payload_bytes:
|
||||
raise ValueError("scratch range overlaps the active table payload")
|
||||
if entry.table_offset & 3 or entry.table_bytes & 3:
|
||||
raise ValueError("entry table range must be four-byte aligned")
|
||||
if entry.table_offset + entry.table_bytes > table_payload_bytes:
|
||||
raise ValueError("entry table range exceeds the active table payload")
|
||||
|
||||
|
||||
def control_flow_report(instructions: list[Instruction], entry: Entry) -> dict[str, object]:
|
||||
def control_flow_report(
|
||||
instructions: list[Instruction], entry: Entry, target: str = "p3"
|
||||
) -> dict[str, object]:
|
||||
"""Prove bounded V1 control flow and return its deterministic report."""
|
||||
|
||||
loop_pairs: dict[int, int] = {}
|
||||
@@ -389,8 +573,8 @@ def control_flow_report(instructions: list[Instruction], entry: Entry) -> dict[s
|
||||
continue
|
||||
opcode = ControlOpcode(instruction.opcode)
|
||||
if opcode in (ControlOpcode.JUMP_FWD, ControlOpcode.CALL_FWD):
|
||||
target = pc + 1 + instruction.immediate
|
||||
if target > entry.last_pc:
|
||||
branch_target = pc + 1 + instruction.immediate
|
||||
if branch_target > entry.last_pc:
|
||||
raise ValueError(f"entry {entry.format_id} branch at PC {pc} leaves its range")
|
||||
elif opcode == ControlOpcode.LOOP_SETUP:
|
||||
slot = instruction.aux & 3
|
||||
@@ -445,16 +629,16 @@ def control_flow_report(instructions: list[Instruction], entry: Entry) -> dict[s
|
||||
if opcode in (ControlOpcode.END, ControlOpcode.TRAP):
|
||||
terminals.add(pc)
|
||||
elif opcode == ControlOpcode.JUMP_FWD:
|
||||
target = pc + 1 + instruction.immediate
|
||||
successors.append((target, returns, loops))
|
||||
edges.add((pc, target, "jump_true"))
|
||||
branch_target = pc + 1 + instruction.immediate
|
||||
successors.append((branch_target, returns, loops))
|
||||
edges.add((pc, branch_target, "jump_true"))
|
||||
elif opcode == ControlOpcode.CALL_FWD:
|
||||
if len(returns) == 4:
|
||||
raise ValueError(f"entry {entry.format_id} can exceed four nested calls")
|
||||
target = pc + 1 + instruction.immediate
|
||||
successors.append((target, (*returns, pc + 1), loops))
|
||||
branch_target = pc + 1 + instruction.immediate
|
||||
successors.append((branch_target, (*returns, pc + 1), loops))
|
||||
maximum_call_depth = max(maximum_call_depth, len(returns) + 1)
|
||||
edges.add((pc, target, "call_true"))
|
||||
edges.add((pc, branch_target, "call_true"))
|
||||
elif opcode == ControlOpcode.RET:
|
||||
if not returns:
|
||||
raise ValueError(f"entry {entry.format_id} can return with an empty stack")
|
||||
@@ -481,8 +665,11 @@ def control_flow_report(instructions: list[Instruction], entry: Entry) -> dict[s
|
||||
elif opcode == ControlOpcode.NOP:
|
||||
successors.append((pc + 1, returns, loops))
|
||||
edges.add((pc, pc + 1, "fallthrough"))
|
||||
elif opcode == ControlOpcode.WAIT and target == "p4":
|
||||
successors.append((pc + 1, returns, loops))
|
||||
edges.add((pc, pc + 1, "wait_ready"))
|
||||
else:
|
||||
raise ValueError("WAIT requires a primitive unavailable in P3")
|
||||
raise ValueError(f"WAIT requires a primitive unavailable in {target.upper()}")
|
||||
pending.extend(successors)
|
||||
if len(visited) > APUMC_MAX_INSTRUCTIONS * 64:
|
||||
raise ValueError(f"entry {entry.format_id} control-flow proof exceeded its bound")
|
||||
@@ -510,32 +697,64 @@ def control_flow_report(instructions: list[Instruction], entry: Entry) -> dict[s
|
||||
}
|
||||
|
||||
|
||||
def validate_control_flow(instructions: list[Instruction], entry: Entry) -> None:
|
||||
def validate_control_flow(
|
||||
instructions: list[Instruction], entry: Entry, target: str = "p3"
|
||||
) -> None:
|
||||
"""Prove bounded V1 control flow for one entry descriptor."""
|
||||
|
||||
control_flow_report(instructions, entry)
|
||||
control_flow_report(instructions, entry, target)
|
||||
|
||||
|
||||
def build_apumc(instructions: list[Instruction], entries: list[Entry], build_id: int = 0) -> bytes:
|
||||
def build_apumc(
|
||||
instructions: list[Instruction],
|
||||
entries: list[Entry],
|
||||
build_id: int = 0,
|
||||
*,
|
||||
target: str = "p3",
|
||||
table_payload: bytes = b"",
|
||||
) -> bytes:
|
||||
target_mask = _target_mask(target)
|
||||
if not 1 <= len(instructions) <= APUMC_MAX_INSTRUCTIONS:
|
||||
raise ValueError("instruction count must be 1..2048")
|
||||
if len(table_payload) & 3 or len(table_payload) > APUMC_P4_TABLE_SCRATCH_BYTES:
|
||||
raise ValueError("table payload must be four-byte aligned and at most 24 KiB")
|
||||
if target == "p3" and table_payload:
|
||||
raise ValueError("P3 does not admit a table payload")
|
||||
if len(entries) != APUMC_ENTRY_COUNT:
|
||||
raise ValueError("APUMC V1 requires exactly three entries")
|
||||
for index, entry in enumerate(entries):
|
||||
if entry.format_id != index:
|
||||
raise ValueError("entry descriptor order must be WAV, MP3, FLAC")
|
||||
validate_entry(entry, len(instructions))
|
||||
validate_entry(entry, len(instructions), target, len(table_payload))
|
||||
for instruction in instructions:
|
||||
validate_p3_instruction(instruction)
|
||||
validate_instruction(instruction, target)
|
||||
for entry in entries:
|
||||
validate_control_flow(instructions, entry)
|
||||
validate_control_flow(instructions, entry, target)
|
||||
for pc in range(entry.first_pc, entry.last_pc + 1):
|
||||
required = instruction_required_mask(instructions[pc])
|
||||
if instructions[pc].instruction_class == InstructionClass.CONTROL and required:
|
||||
if instructions[pc].aux == 1:
|
||||
if not (entry.primitive_mask & required):
|
||||
raise ValueError("kernel WAIT requires a class-5 primitive")
|
||||
elif required & ~entry.primitive_mask:
|
||||
raise ValueError("FIFO WAIT requires the local-FIFO primitive")
|
||||
elif required & ~entry.primitive_mask:
|
||||
raise ValueError(f"entry {entry.format_id} omits an instruction primitive")
|
||||
|
||||
descriptor = b"".join(struct.pack("<8I", *entry.words()) for entry in entries)
|
||||
padding = bytes(APUMC_INSTRUCTION_OFFSET - APUMC_HEADER_BYTES - len(descriptor))
|
||||
encoded = b"".join(struct.pack("<Q", instruction.encode()) for instruction in instructions)
|
||||
payload = descriptor + padding + encoded
|
||||
table_offset = APUMC_INSTRUCTION_OFFSET + len(encoded) if table_payload else 0
|
||||
payload = descriptor + padding + encoded + table_payload
|
||||
total_bytes = APUMC_HEADER_BYTES + len(payload)
|
||||
crc = crc32_iso_hdlc(payload)
|
||||
required_mask = 0
|
||||
scratch_end = 0
|
||||
for entry in entries:
|
||||
required_mask |= entry.primitive_mask
|
||||
scratch_end = max(scratch_end, entry.scratch_base + entry.scratch_bytes)
|
||||
if required_mask & ~target_mask:
|
||||
raise ValueError(f"bundle requires a primitive unavailable in {target.upper()}")
|
||||
header = struct.pack(
|
||||
"<16I",
|
||||
APUMC_MAGIC,
|
||||
@@ -543,12 +762,12 @@ def build_apumc(instructions: list[Instruction], entries: list[Entry], build_id:
|
||||
total_bytes,
|
||||
len(instructions),
|
||||
APUMC_INSTRUCTION_OFFSET,
|
||||
0,
|
||||
0,
|
||||
table_offset,
|
||||
len(table_payload),
|
||||
APUMC_HEADER_BYTES,
|
||||
APUMC_ENTRY_COUNT,
|
||||
APUMC_P3_CAPABILITY_MASK,
|
||||
0,
|
||||
required_mask,
|
||||
scratch_end,
|
||||
crc,
|
||||
build_id & 0xFFFFFFFF,
|
||||
(build_id >> 32) & 0xFFFFFFFF,
|
||||
@@ -558,7 +777,10 @@ def build_apumc(instructions: list[Instruction], entries: list[Entry], build_id:
|
||||
return header + payload
|
||||
|
||||
|
||||
def parse_apumc(bundle: bytes) -> tuple[list[Instruction], list[Entry], tuple[int, ...]]:
|
||||
def parse_apumc(
|
||||
bundle: bytes, target: str = "p3"
|
||||
) -> tuple[list[Instruction], list[Entry], tuple[int, ...]]:
|
||||
target_mask = _target_mask(target)
|
||||
if len(bundle) < APUMC_HEADER_BYTES:
|
||||
raise ValueError("bundle is smaller than the APUMC header")
|
||||
header = struct.unpack_from("<16I", bundle)
|
||||
@@ -582,7 +804,7 @@ def parse_apumc(bundle: bytes) -> tuple[list[Instruction], list[Entry], tuple[in
|
||||
entry_end = entry_offset + APUMC_ENTRY_COUNT * APUMC_ENTRY_BYTES
|
||||
if not 1 <= instruction_count <= APUMC_MAX_INSTRUCTIONS:
|
||||
raise ValueError("APUMC instruction count is invalid")
|
||||
if (
|
||||
common_range_error = (
|
||||
instruction_offset < APUMC_HEADER_BYTES
|
||||
or instruction_offset % 64 != 0
|
||||
or instruction_end > len(bundle)
|
||||
@@ -591,28 +813,40 @@ def parse_apumc(bundle: bytes) -> tuple[list[Instruction], list[Entry], tuple[in
|
||||
or entry_end > len(bundle)
|
||||
or not (instruction_end <= entry_offset or entry_end <= instruction_offset)
|
||||
or table_offset % 4 != 0
|
||||
or table_bytes != 0
|
||||
or table_offset != 0
|
||||
or header[9] != 0
|
||||
or header[10] != 0
|
||||
):
|
||||
)
|
||||
table_end = table_offset + table_bytes
|
||||
if common_range_error:
|
||||
raise ValueError("APUMC range fields are invalid")
|
||||
if target == "p3" and (table_bytes or table_offset or header[9] or header[10]):
|
||||
raise ValueError("APUMC range or P3 capability fields are invalid")
|
||||
if target == "p4" and (
|
||||
table_bytes > APUMC_P4_TABLE_SCRATCH_BYTES
|
||||
or table_bytes & 3
|
||||
or table_offset > len(bundle)
|
||||
or (table_bytes and (table_offset < APUMC_HEADER_BYTES or table_end > len(bundle)))
|
||||
or (table_bytes and not (
|
||||
(table_end <= instruction_offset or table_offset >= instruction_end)
|
||||
and (table_end <= entry_offset or table_offset >= entry_end)
|
||||
))
|
||||
or header[9] & ~target_mask
|
||||
or header[10] > APUMC_P4_TABLE_SCRATCH_BYTES
|
||||
):
|
||||
raise ValueError("APUMC range or P4 capability fields are invalid")
|
||||
if crc32_iso_hdlc(bundle[APUMC_HEADER_BYTES:]) != header[11]:
|
||||
raise ValueError("APUMC CRC mismatch")
|
||||
entries = []
|
||||
for index in range(APUMC_ENTRY_COUNT):
|
||||
words = struct.unpack_from("<8I", bundle, entry_offset + index * APUMC_ENTRY_BYTES)
|
||||
if (
|
||||
reserved_error = (
|
||||
words[0] >> 15
|
||||
or (words[1] & 0xF800F800)
|
||||
or words[2]
|
||||
or words[3]
|
||||
or words[4] >> 16
|
||||
or words[5] >> 24
|
||||
or words[6]
|
||||
or words[7]
|
||||
):
|
||||
raise ValueError(f"APUMC entry {index} has invalid reserved or P3 fields")
|
||||
or words[2] >> 17
|
||||
or words[3] >> 17
|
||||
)
|
||||
if reserved_error or (target == "p3" and any(words[item] for item in (2, 3, 6, 7))):
|
||||
raise ValueError(f"APUMC entry {index} has invalid reserved or target fields")
|
||||
entries.append(
|
||||
Entry(
|
||||
format_id=words[0] & 0xF,
|
||||
@@ -638,14 +872,30 @@ def parse_apumc(bundle: bytes) -> tuple[list[Instruction], list[Entry], tuple[in
|
||||
occupied[instruction_offset:instruction_end] = bytes([1]) * (
|
||||
instruction_end - instruction_offset
|
||||
)
|
||||
if table_bytes:
|
||||
occupied[table_offset:table_end] = bytes([1]) * table_bytes
|
||||
if any(byte for index, byte in enumerate(bundle) if not occupied[index]):
|
||||
raise ValueError("APUMC padding must be zero")
|
||||
for index, entry in enumerate(entries):
|
||||
if entry.format_id != index:
|
||||
raise ValueError("entry descriptor order must be WAV, MP3, FLAC")
|
||||
validate_entry(entry, instruction_count)
|
||||
validate_entry(entry, instruction_count, target, table_bytes)
|
||||
for instruction in instructions:
|
||||
validate_p3_instruction(instruction)
|
||||
validate_instruction(instruction, target)
|
||||
for entry in entries:
|
||||
validate_control_flow(instructions, entry)
|
||||
validate_control_flow(instructions, entry, target)
|
||||
for pc in range(entry.first_pc, entry.last_pc + 1):
|
||||
required = instruction_required_mask(instructions[pc])
|
||||
if instructions[pc].instruction_class == InstructionClass.CONTROL and required:
|
||||
if instructions[pc].aux == 1:
|
||||
if not (entry.primitive_mask & required):
|
||||
raise ValueError("kernel WAIT requires a class-5 primitive")
|
||||
elif required & ~entry.primitive_mask:
|
||||
raise ValueError("FIFO WAIT requires the local-FIFO primitive")
|
||||
elif required & ~entry.primitive_mask:
|
||||
raise ValueError(f"entry {entry.format_id} omits an instruction primitive")
|
||||
if header[9] != (entries[0].primitive_mask | entries[1].primitive_mask | entries[2].primitive_mask):
|
||||
raise ValueError("APUMC header primitive mask does not match entry masks")
|
||||
if header[10] != max(entry.scratch_base + entry.scratch_bytes for entry in entries):
|
||||
raise ValueError("APUMC scratch end does not match entry descriptors")
|
||||
return instructions, entries, header
|
||||
+136
-22
@@ -1,21 +1,28 @@
|
||||
"""Assemble and statically verify frozen APU-P3 microcode bundles."""
|
||||
"""Assemble and statically verify frozen APU-P3/P4 microcode bundles."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import struct
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
from apu_isa import (
|
||||
CONTROL_OPCODES,
|
||||
DEFERRED_OPCODES,
|
||||
PREDICATES,
|
||||
SCALAR_OPCODES,
|
||||
APUMC_TARGETS,
|
||||
BitstreamOpcode,
|
||||
ControlOpcode,
|
||||
EntropyOpcode,
|
||||
Entry,
|
||||
Instruction,
|
||||
InstructionClass,
|
||||
KernelOpcode,
|
||||
LocalOpcode,
|
||||
ScalarOpcode,
|
||||
abi_manifest,
|
||||
build_apumc,
|
||||
@@ -31,6 +38,8 @@ class Assembly:
|
||||
instructions: list[Instruction]
|
||||
entries: list[Entry]
|
||||
build_id: int
|
||||
target: str
|
||||
table_payload: bytes
|
||||
|
||||
|
||||
def _number(token: str) -> int:
|
||||
@@ -91,10 +100,100 @@ def _encode_instruction(tokens: list[str], pc: int, labels: dict[str, int]) -> I
|
||||
values["aux"] = _number(args[0])
|
||||
return Instruction(InstructionClass.CONTROL, opcode, **values)
|
||||
|
||||
if mnemonic not in SCALAR_OPCODES:
|
||||
if mnemonic in SCALAR_OPCODES:
|
||||
opcode = ScalarOpcode(SCALAR_OPCODES[mnemonic])
|
||||
values = {"predicate": predicate}
|
||||
elif mnemonic in DEFERRED_OPCODES["bitstream"]:
|
||||
opcode = BitstreamOpcode(DEFERRED_OPCODES["bitstream"].index(mnemonic))
|
||||
values = {"predicate": predicate}
|
||||
if opcode in (BitstreamOpcode.REFILL, BitstreamOpcode.PEEK, BitstreamOpcode.GET):
|
||||
if len(args) != 2:
|
||||
raise ValueError(f"{mnemonic} takes a destination and bit width")
|
||||
values.update(dst=_register(args[0]), immediate=_number(args[1]))
|
||||
elif opcode == BitstreamOpcode.SKIP:
|
||||
if len(args) != 1:
|
||||
raise ValueError("skip takes one bit width")
|
||||
values["immediate"] = _number(args[0])
|
||||
elif opcode == BitstreamOpcode.ALIGN:
|
||||
if args:
|
||||
raise ValueError("align takes no operands")
|
||||
elif opcode == BitstreamOpcode.FRAME_SYNC:
|
||||
if len(args) != 5:
|
||||
raise ValueError("frame_sync takes dst, pattern, mask, width, and limit")
|
||||
values.update(
|
||||
dst=_register(args[0]),
|
||||
src0=_register(args[1]),
|
||||
src1=_register(args[2]),
|
||||
aux=_number(args[3]),
|
||||
immediate=_number(args[4]),
|
||||
)
|
||||
else:
|
||||
if len(args) != 3:
|
||||
raise ValueError(f"{mnemonic} takes dst, accumulator, and byte registers")
|
||||
values.update(dst=_register(args[0]), src0=_register(args[1]), src1=_register(args[2]))
|
||||
return Instruction(InstructionClass.BITSTREAM, opcode, **values)
|
||||
elif mnemonic in DEFERRED_OPCODES["entropy"]:
|
||||
opcode = EntropyOpcode(DEFERRED_OPCODES["entropy"].index(mnemonic))
|
||||
values = {"predicate": predicate}
|
||||
if opcode <= EntropyOpcode.HUFF_QUAD:
|
||||
if len(args) != 4:
|
||||
raise ValueError(f"{mnemonic} takes dst, table, entries, and maximum length")
|
||||
values.update(
|
||||
dst=_register(args[0]),
|
||||
src0=_register(args[1]),
|
||||
src1=_register(args[2]),
|
||||
aux=_number(args[3]),
|
||||
)
|
||||
elif opcode == EntropyOpcode.UNARY:
|
||||
if len(args) != 3:
|
||||
raise ValueError("unary takes dst, polarity, and maximum run")
|
||||
values.update(dst=_register(args[0]), aux=_number(args[1]), immediate=_number(args[2]))
|
||||
else:
|
||||
if len(args) != 3:
|
||||
raise ValueError(f"{mnemonic} takes three registers")
|
||||
values.update(dst=_register(args[0]), src0=_register(args[1]), src1=_register(args[2]))
|
||||
return Instruction(InstructionClass.ENTROPY, opcode, **values)
|
||||
elif mnemonic in DEFERRED_OPCODES["local"]:
|
||||
opcode = LocalOpcode(DEFERRED_OPCODES["local"].index(mnemonic))
|
||||
values = {"predicate": predicate}
|
||||
if opcode == LocalOpcode.LD32:
|
||||
if len(args) != 3:
|
||||
raise ValueError("ld32 takes destination, address register, and offset")
|
||||
values.update(dst=_register(args[0]), src0=_register(args[1]), immediate=_number(args[2]) & 0xFFFF)
|
||||
elif opcode == LocalOpcode.ST32:
|
||||
if len(args) != 3:
|
||||
raise ValueError("st32 takes address, value, and offset")
|
||||
values.update(src0=_register(args[0]), src1=_register(args[1]), immediate=_number(args[2]) & 0xFFFF)
|
||||
elif opcode in (LocalOpcode.TABLE8, LocalOpcode.TABLE16, LocalOpcode.TABLE32):
|
||||
if len(args) != 3:
|
||||
raise ValueError(f"{mnemonic} takes destination, base, and index registers")
|
||||
values.update(dst=_register(args[0]), src0=_register(args[1]), src1=_register(args[2]))
|
||||
elif opcode == LocalOpcode.FIFO_POP:
|
||||
if len(args) != 1:
|
||||
raise ValueError("fifo_pop takes one destination")
|
||||
values["dst"] = _register(args[0])
|
||||
else:
|
||||
if len(args) != 2:
|
||||
raise ValueError("fifo_push takes data and metadata registers")
|
||||
values.update(src0=_register(args[0]), src1=_register(args[1]))
|
||||
return Instruction(InstructionClass.LOCAL, opcode, **values)
|
||||
elif mnemonic in DEFERRED_OPCODES["kernel"]:
|
||||
opcode = KernelOpcode(DEFERRED_OPCODES["kernel"].index(mnemonic))
|
||||
if len(args) != 5:
|
||||
raise ValueError(f"{mnemonic} takes output, input, parameter, aux, and count")
|
||||
return Instruction(
|
||||
InstructionClass.KERNEL,
|
||||
opcode,
|
||||
predicate=predicate,
|
||||
dst=_register(args[0]),
|
||||
src0=_register(args[1]),
|
||||
src1=_register(args[2]),
|
||||
aux=_number(args[3]),
|
||||
immediate=_number(args[4]),
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"unknown mnemonic {mnemonic}")
|
||||
opcode = ScalarOpcode(SCALAR_OPCODES[mnemonic])
|
||||
values = {"predicate": predicate}
|
||||
|
||||
if opcode == ScalarOpcode.MOV:
|
||||
if len(args) != 2:
|
||||
raise ValueError("mov takes two registers")
|
||||
@@ -127,7 +226,9 @@ def _encode_instruction(tokens: list[str], pc: int, labels: dict[str, int]) -> I
|
||||
return Instruction(InstructionClass.SCALAR, opcode, **values)
|
||||
|
||||
|
||||
def assemble(source: str) -> Assembly:
|
||||
def assemble(source: str, target: str = "p3") -> Assembly:
|
||||
if target not in APUMC_TARGETS:
|
||||
raise ValueError(f"unknown APU target {target}")
|
||||
lines = []
|
||||
labels: dict[str, int] = {}
|
||||
directives: list[list[str]] = []
|
||||
@@ -149,11 +250,14 @@ def assemble(source: str) -> Assembly:
|
||||
|
||||
build_id = 0
|
||||
entry_directives = []
|
||||
table_words: list[int] = []
|
||||
for directive in directives:
|
||||
if directive[0] == ".build_id" and len(directive) == 2:
|
||||
build_id = _number(directive[1])
|
||||
elif directive[0] == ".entry" and len(directive) == 7:
|
||||
elif directive[0] == ".entry" and len(directive) in (7, 12):
|
||||
entry_directives.append(directive[1:])
|
||||
elif directive[0] == ".table32" and len(directive) > 1:
|
||||
table_words.extend(_number(value) & 0xFFFFFFFF for value in directive[1:])
|
||||
else:
|
||||
raise ValueError(f"invalid assembler directive {' '.join(directive)}")
|
||||
if len(entry_directives) != 3:
|
||||
@@ -169,21 +273,26 @@ def assemble(source: str) -> Assembly:
|
||||
entries = []
|
||||
for values in entry_directives:
|
||||
try:
|
||||
entries.append(
|
||||
Entry(
|
||||
format_id=_number(values[0]),
|
||||
entry_pc=labels[values[1]],
|
||||
first_pc=labels[values[2]],
|
||||
last_pc=labels[values[3]],
|
||||
max_loop_count=_number(values[4]),
|
||||
max_retired=_number(values[5]),
|
||||
)
|
||||
)
|
||||
optional = [0, 0, 0, 0, 0] if len(values) == 6 else [_number(item) for item in values[6:]]
|
||||
entries.append(Entry(
|
||||
format_id=_number(values[0]),
|
||||
entry_pc=labels[values[1]],
|
||||
first_pc=labels[values[2]],
|
||||
last_pc=labels[values[3]],
|
||||
max_loop_count=_number(values[4]),
|
||||
max_retired=_number(values[5]),
|
||||
scratch_base=optional[0],
|
||||
scratch_bytes=optional[1],
|
||||
primitive_mask=optional[2],
|
||||
table_offset=optional[3],
|
||||
table_bytes=optional[4],
|
||||
))
|
||||
except KeyError as error:
|
||||
raise ValueError(f"unknown entry label {error.args[0]}") from error
|
||||
entries.sort(key=lambda entry: entry.format_id)
|
||||
bundle = build_apumc(instructions, entries, build_id)
|
||||
return Assembly(bundle, dict(sorted(labels.items())), instructions, entries, build_id)
|
||||
table_payload = b"".join(struct.pack("<I", word) for word in table_words)
|
||||
bundle = build_apumc(instructions, entries, build_id, target=target, table_payload=table_payload)
|
||||
return Assembly(bundle, dict(sorted(labels.items())), instructions, entries, build_id, target, table_payload)
|
||||
|
||||
|
||||
def _artifact_data(assembly: Assembly) -> dict[str, object]:
|
||||
@@ -199,6 +308,7 @@ def _artifact_data(assembly: Assembly) -> dict[str, object]:
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("source", type=Path)
|
||||
parser.add_argument("--target", choices=APUMC_TARGETS, default="p3")
|
||||
parser.add_argument("-o", "--output", type=Path, required=True)
|
||||
parser.add_argument("--symbols", type=Path)
|
||||
parser.add_argument("--cfg-report", type=Path)
|
||||
@@ -206,7 +316,7 @@ def main() -> int:
|
||||
parser.add_argument("--trace-input", type=Path)
|
||||
parser.add_argument("--abi-input-manifest", type=Path)
|
||||
args = parser.parse_args()
|
||||
assembly = assemble(args.source.read_text(encoding="utf-8"))
|
||||
assembly = assemble(args.source.read_text(encoding="utf-8"), args.target)
|
||||
args.output.write_bytes(assembly.bundle)
|
||||
artifacts = {
|
||||
args.symbols: assembly.symbols,
|
||||
@@ -214,12 +324,16 @@ def main() -> int:
|
||||
**_artifact_data(assembly),
|
||||
"entries": [entry.__dict__ for entry in assembly.entries],
|
||||
"control_flow": [
|
||||
control_flow_report(assembly.instructions, entry) for entry in assembly.entries
|
||||
control_flow_report(assembly.instructions, entry, assembly.target)
|
||||
for entry in assembly.entries
|
||||
],
|
||||
},
|
||||
args.primitive_manifest: {
|
||||
"implemented_mask": 0,
|
||||
"required_mask": 0,
|
||||
"target": assembly.target,
|
||||
"implemented_mask": 0 if assembly.target == "p3" else 0x0000FFFF,
|
||||
"required_mask": __import__("functools").reduce(
|
||||
int.__or__, (entry.primitive_mask for entry in assembly.entries), 0
|
||||
),
|
||||
"entry_masks": [entry.primitive_mask for entry in assembly.entries],
|
||||
},
|
||||
args.trace_input: {
|
||||
|
||||
@@ -0,0 +1,680 @@
|
||||
"""Bit-accurate APU-P4 primitive reference model."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from apu_isa import (
|
||||
BitstreamOpcode,
|
||||
EntropyOpcode,
|
||||
Entry,
|
||||
Instruction,
|
||||
InstructionClass,
|
||||
KernelOpcode,
|
||||
LocalOpcode,
|
||||
)
|
||||
|
||||
|
||||
LOCAL_DATA_BYTES = 0x1C000
|
||||
CODEC_BYTES = 0xA000
|
||||
TABLE_SCRATCH_BYTES = 0x6000
|
||||
INPUT_BASE = 0x6000
|
||||
OUTPUT_BASE = 0x8000
|
||||
KWS_BASE = 0xA000
|
||||
INTERNAL_BASE = 0x1A000
|
||||
|
||||
|
||||
class PrimitiveFault(RuntimeError):
|
||||
def __init__(self, code: int, stage: int, reason: int = 9):
|
||||
self.code = code
|
||||
self.stage = stage
|
||||
self.reason = reason
|
||||
super().__init__(f"APU primitive fault code={code} stage={stage} reason={reason}")
|
||||
|
||||
|
||||
@dataclass
|
||||
class LocalMemory:
|
||||
data: bytearray = field(default_factory=lambda: bytearray(LOCAL_DATA_BYTES))
|
||||
valid_words: list[bool] = field(default_factory=lambda: [False] * (CODEC_BYTES // 4))
|
||||
table_bytes: int = 0
|
||||
image_valid: bool = False
|
||||
|
||||
def publish_table(self, payload: bytes) -> None:
|
||||
if len(payload) > TABLE_SCRATCH_BYTES or len(payload) & 3:
|
||||
raise ValueError("P4 table payload is outside the frozen workspace")
|
||||
self.data[: len(payload)] = payload
|
||||
self.table_bytes = len(payload)
|
||||
self.image_valid = True
|
||||
|
||||
def invalidate_image(self) -> None:
|
||||
self.image_valid = False
|
||||
self.table_bytes = 0
|
||||
|
||||
def new_epoch(self) -> None:
|
||||
self.valid_words = [False] * len(self.valid_words)
|
||||
|
||||
def _check_range(self, address: int, size: int, entry: Entry, table: bool = False) -> None:
|
||||
if address < 0 or size < 1 or address + size > LOCAL_DATA_BYTES:
|
||||
raise PrimitiveFault(11, 11, 5)
|
||||
if table:
|
||||
first = entry.table_offset
|
||||
last = first + entry.table_bytes
|
||||
if not self.image_valid or address < first or address + size > last:
|
||||
raise PrimitiveFault(11, 11, 5)
|
||||
else:
|
||||
first = entry.scratch_base
|
||||
last = first + entry.scratch_bytes
|
||||
if address < first or address + size > last:
|
||||
raise PrimitiveFault(11, 11, 5)
|
||||
|
||||
def read(self, address: int, size: int, entry: Entry, table: bool = False) -> bytes:
|
||||
self._check_range(address, size, entry, table)
|
||||
if not table:
|
||||
first_word = address // 4
|
||||
last_word = (address + size - 1) // 4
|
||||
if any(not self.valid_words[index] for index in range(first_word, last_word + 1)):
|
||||
raise PrimitiveFault(11, 11, 5)
|
||||
return bytes(self.data[address : address + size])
|
||||
|
||||
def write(self, address: int, payload: bytes, entry: Entry) -> None:
|
||||
self._check_range(address, len(payload), entry)
|
||||
if address < self.table_bytes:
|
||||
raise PrimitiveFault(11, 11, 5)
|
||||
self.data[address : address + len(payload)] = payload
|
||||
first_word = address // 4
|
||||
last_word = (address + len(payload) - 1) // 4
|
||||
for index in range(first_word, last_word + 1):
|
||||
self.valid_words[index] = True
|
||||
|
||||
def inject(self, address: int, payload: bytes) -> None:
|
||||
if address < 0 or address + len(payload) > CODEC_BYTES:
|
||||
raise ValueError("verification injection exceeds the P4 codec region")
|
||||
self.data[address : address + len(payload)] = payload
|
||||
first_word = address // 4
|
||||
last_word = (address + len(payload) - 1) // 4
|
||||
for index in range(first_word, last_word + 1):
|
||||
self.valid_words[index] = True
|
||||
|
||||
|
||||
@dataclass
|
||||
class PrimitiveFifo:
|
||||
words: list[int] = field(default_factory=list)
|
||||
|
||||
@property
|
||||
def full(self) -> bool:
|
||||
return len(self.words) == 64
|
||||
|
||||
@property
|
||||
def empty(self) -> bool:
|
||||
return not self.words
|
||||
|
||||
def push(self, word: int) -> None:
|
||||
if self.full or word & (0x1F << 35) or not 1 <= ((word >> 32) & 7) <= 4:
|
||||
raise PrimitiveFault(22, 7)
|
||||
self.words.append(word & ((1 << 41) - 1))
|
||||
|
||||
def pop(self) -> int:
|
||||
if self.empty:
|
||||
raise PrimitiveFault(22, 4)
|
||||
return self.words.pop(0)
|
||||
|
||||
def flush(self) -> None:
|
||||
self.words.clear()
|
||||
|
||||
|
||||
@dataclass
|
||||
class PrimitiveResult:
|
||||
writes: dict[int, int] = field(default_factory=dict)
|
||||
cycles: int = 1
|
||||
kernel_done: bool = False
|
||||
|
||||
|
||||
class PrimitiveBam:
|
||||
def __init__(self, memory: LocalMemory | None = None):
|
||||
self.memory = memory if memory is not None else LocalMemory()
|
||||
self.input_fifo = PrimitiveFifo()
|
||||
self.output_fifo = PrimitiveFifo()
|
||||
self.bits: list[int] = []
|
||||
self.eof = False
|
||||
self.bit_position = 0
|
||||
self.kernel_done = False
|
||||
self.resample_profile: int | None = None
|
||||
|
||||
@staticmethod
|
||||
def signed(value: int, width: int = 32) -> int:
|
||||
value &= (1 << width) - 1
|
||||
return value - (1 << width) if value & (1 << (width - 1)) else value
|
||||
|
||||
@staticmethod
|
||||
def sat(value: int, width: int) -> int:
|
||||
return min(max(value, -(1 << (width - 1))), (1 << (width - 1)) - 1)
|
||||
|
||||
@staticmethod
|
||||
def rne(value: int, shift: int) -> int:
|
||||
if shift == 0:
|
||||
return value
|
||||
magnitude = abs(value)
|
||||
quotient, remainder = divmod(magnitude, 1 << shift)
|
||||
halfway = 1 << (shift - 1)
|
||||
if remainder > halfway or (remainder == halfway and quotient & 1):
|
||||
quotient += 1
|
||||
return -quotient if value < 0 else quotient
|
||||
|
||||
@staticmethod
|
||||
def _u32(value: int) -> int:
|
||||
return value & 0xFFFFFFFF
|
||||
|
||||
def reset_mutable(self) -> None:
|
||||
self.memory.new_epoch()
|
||||
self.input_fifo.flush()
|
||||
self.output_fifo.flush()
|
||||
self.bits.clear()
|
||||
self.eof = False
|
||||
self.bit_position = 0
|
||||
self.kernel_done = False
|
||||
self.resample_profile = None
|
||||
|
||||
def _refill(self, width: int) -> None:
|
||||
while len(self.bits) < width and not self.input_fifo.empty:
|
||||
word = self.input_fifo.pop()
|
||||
count = (word >> 32) & 7
|
||||
for byte_index in range(count):
|
||||
byte = (word >> (8 * byte_index)) & 0xFF
|
||||
self.bits.extend((byte >> bit_index) & 1 for bit_index in range(7, -1, -1))
|
||||
if word & (1 << 40):
|
||||
self.eof = True
|
||||
|
||||
def _peek_bits(self, width: int, stage: int) -> int:
|
||||
self._refill(width)
|
||||
if len(self.bits) < width:
|
||||
raise PrimitiveFault(5, stage)
|
||||
value = 0
|
||||
for bit in self.bits[:width]:
|
||||
value = (value << 1) | bit
|
||||
return value
|
||||
|
||||
def _get_bits(self, width: int, stage: int) -> int:
|
||||
value = self._peek_bits(width, stage)
|
||||
del self.bits[:width]
|
||||
self.bit_position = (self.bit_position + width) & 7
|
||||
return value
|
||||
|
||||
def _read_u32(self, address: int, entry: Entry, table: bool = False) -> int:
|
||||
return int.from_bytes(self.memory.read(address, 4, entry, table), "little")
|
||||
|
||||
def _read_s32(self, address: int, entry: Entry, table: bool = False) -> int:
|
||||
return self.signed(self._read_u32(address, entry, table))
|
||||
|
||||
def _write_s32(self, address: int, value: int, entry: Entry) -> None:
|
||||
self.memory.write(address, self._u32(value).to_bytes(4, "little"), entry)
|
||||
|
||||
def _resolve_ref(self, word: int, entry: Entry, size: int, *, state: bool = False) -> tuple[int, bool]:
|
||||
table = bool(word & 0x80000000)
|
||||
if word & (0x7FFF0000 if table else 0x7FFE0000) or word & 3:
|
||||
raise PrimitiveFault(11, 11, 5)
|
||||
if state and table:
|
||||
raise PrimitiveFault(11, 11, 5)
|
||||
offset = word & (0xFFFF if table else 0x1FFFF)
|
||||
address = (entry.table_offset if table else entry.scratch_base) + offset
|
||||
self.memory._check_range(address, size, entry, table)
|
||||
return address, table
|
||||
|
||||
def _dot(self, left: list[int], right: list[int]) -> int:
|
||||
total = sum(a * b for a, b in zip(left, right, strict=True))
|
||||
if total < -(1 << 71) or total > (1 << 71) - 1:
|
||||
raise PrimitiveFault(22, 6)
|
||||
return total
|
||||
|
||||
def execute(self, instruction: Instruction, registers: list[int], entry: Entry) -> PrimitiveResult:
|
||||
instruction_class = instruction.instruction_class
|
||||
if instruction_class == InstructionClass.BITSTREAM:
|
||||
return self._execute_bitstream(instruction, registers)
|
||||
if instruction_class == InstructionClass.ENTROPY:
|
||||
return self._execute_entropy(instruction, registers, entry)
|
||||
if instruction_class == InstructionClass.LOCAL:
|
||||
return self._execute_local(instruction, registers, entry)
|
||||
if instruction_class == InstructionClass.KERNEL:
|
||||
return self._execute_kernel(instruction, registers, entry)
|
||||
raise ValueError("instruction is not an APU-P4 primitive")
|
||||
|
||||
def _execute_bitstream(self, instruction: Instruction, registers: list[int]) -> PrimitiveResult:
|
||||
opcode = BitstreamOpcode(instruction.opcode)
|
||||
width = instruction.immediate & 0x3F
|
||||
writes: dict[int, int] = {}
|
||||
cycles = 1
|
||||
if opcode == BitstreamOpcode.REFILL:
|
||||
self._refill(width)
|
||||
writes[instruction.dst] = min(len(self.bits), 64)
|
||||
cycles = 3
|
||||
elif opcode == BitstreamOpcode.PEEK:
|
||||
writes[instruction.dst] = self._peek_bits(width, 4)
|
||||
elif opcode == BitstreamOpcode.GET:
|
||||
writes[instruction.dst] = self._get_bits(width, 4)
|
||||
elif opcode == BitstreamOpcode.SKIP:
|
||||
self._get_bits(width, 4)
|
||||
elif opcode == BitstreamOpcode.ALIGN:
|
||||
skip = (-self.bit_position) & 7
|
||||
if skip:
|
||||
self._get_bits(skip, 4)
|
||||
elif opcode == BitstreamOpcode.FRAME_SYNC:
|
||||
if self.bit_position:
|
||||
self._get_bits((-self.bit_position) & 7, 4)
|
||||
pattern = registers[instruction.src0] & 0xFFFF
|
||||
mask = registers[instruction.src1] & 0xFFFF
|
||||
width = instruction.aux & 0x1F
|
||||
skipped = 0
|
||||
while skipped < instruction.immediate:
|
||||
if self._peek_bits(width, 4) & mask == pattern & mask:
|
||||
writes[instruction.dst] = skipped
|
||||
cycles = 2 + 2 * skipped
|
||||
break
|
||||
if skipped + 1 == instruction.immediate:
|
||||
raise PrimitiveFault(4, 4)
|
||||
self._get_bits(8, 4)
|
||||
skipped += 1
|
||||
else:
|
||||
raise PrimitiveFault(4, 4)
|
||||
elif opcode == BitstreamOpcode.CRC8:
|
||||
crc = registers[instruction.src0] & 0xFF
|
||||
byte = registers[instruction.src1] & 0xFF
|
||||
for bit_index in range(8):
|
||||
feedback = ((crc >> 7) ^ (byte >> (7 - bit_index))) & 1
|
||||
crc = (crc << 1) & 0xFF
|
||||
if feedback:
|
||||
crc ^= 0x07
|
||||
writes[instruction.dst] = crc
|
||||
else:
|
||||
crc = registers[instruction.src0] & 0xFFFF
|
||||
byte = registers[instruction.src1] & 0xFF
|
||||
for bit_index in range(8):
|
||||
feedback = ((crc >> 15) ^ (byte >> (7 - bit_index))) & 1
|
||||
crc = (crc << 1) & 0xFFFF
|
||||
if feedback:
|
||||
crc ^= 0x8005
|
||||
writes[instruction.dst] = crc
|
||||
return PrimitiveResult(writes, cycles)
|
||||
|
||||
def _execute_entropy(
|
||||
self, instruction: Instruction, registers: list[int], entry: Entry
|
||||
) -> PrimitiveResult:
|
||||
opcode = EntropyOpcode(instruction.opcode)
|
||||
writes: dict[int, int] = {}
|
||||
if opcode <= EntropyOpcode.HUFF_QUAD:
|
||||
count = registers[instruction.src1]
|
||||
if not 1 <= count <= 4096:
|
||||
raise PrimitiveFault(7, 5)
|
||||
start = entry.table_offset + registers[instruction.src0]
|
||||
entries = [self._read_u32(start + index * 4, entry, True) for index in range(count)]
|
||||
code = 0
|
||||
previous_length = 0
|
||||
match = None
|
||||
for word in entries:
|
||||
length = (word >> 16) & 0x1F
|
||||
if not previous_length <= length <= instruction.aux or word >> 21:
|
||||
raise PrimitiveFault(7, 5)
|
||||
code <<= length - previous_length
|
||||
if self._peek_bits(length, 5) == code:
|
||||
match = (word & 0xFFFF, length)
|
||||
break
|
||||
code += 1
|
||||
previous_length = length
|
||||
if match is None:
|
||||
raise PrimitiveFault(7, 5)
|
||||
symbol, length = match
|
||||
self._get_bits(length, 5)
|
||||
if opcode == EntropyOpcode.HUFF_SYMBOL:
|
||||
writes[instruction.dst] = symbol
|
||||
elif opcode == EntropyOpcode.HUFF_PAIR:
|
||||
writes[instruction.dst] = (symbol >> 4) & 0xF
|
||||
writes[instruction.dst + 1] = symbol & 0xF
|
||||
else:
|
||||
for index in range(4):
|
||||
writes[instruction.dst + index] = (symbol >> (3 - index)) & 1
|
||||
return PrimitiveResult(writes, 2 + 24 + count)
|
||||
if opcode == EntropyOpcode.UNARY:
|
||||
polarity = instruction.aux & 1
|
||||
run = 0
|
||||
while run <= instruction.immediate:
|
||||
bit = self._get_bits(1, 5)
|
||||
if bit != polarity:
|
||||
writes[instruction.dst] = run
|
||||
return PrimitiveResult(writes, 2 + run)
|
||||
run += 1
|
||||
raise PrimitiveFault(7, 5)
|
||||
if opcode in (EntropyOpcode.RICE4, EntropyOpcode.RICE5):
|
||||
width = 4 if opcode == EntropyOpcode.RICE4 else 5
|
||||
escape = 15 if width == 4 else 31
|
||||
parameter = registers[instruction.src0] & ((1 << width) - 1)
|
||||
if parameter == escape:
|
||||
raw_width = registers[instruction.src1] & 0x3F
|
||||
if not 1 <= raw_width <= 32:
|
||||
raise PrimitiveFault(7, 5)
|
||||
value = self._get_bits(raw_width, 5)
|
||||
writes[instruction.dst] = self._u32(self.signed(value, raw_width))
|
||||
return PrimitiveResult(writes, 4 + raw_width)
|
||||
quotient = 0
|
||||
while self._get_bits(1, 5) == 0:
|
||||
quotient += 1
|
||||
if quotient > 65535:
|
||||
raise PrimitiveFault(7, 5)
|
||||
remainder = self._get_bits(parameter, 5) if parameter else 0
|
||||
unsigned = (quotient << parameter) | remainder
|
||||
writes[instruction.dst] = self._u32((unsigned >> 1) ^ -(unsigned & 1))
|
||||
return PrimitiveResult(writes, 4 + quotient + parameter)
|
||||
magnitude = registers[instruction.src0]
|
||||
if magnitude == 0:
|
||||
writes[instruction.dst] = 0
|
||||
return PrimitiveResult(writes, 3)
|
||||
linbits = registers[instruction.src1] & 0x1F
|
||||
extra = self._get_bits(linbits, 5) if linbits else 0
|
||||
value = magnitude + extra
|
||||
if self._get_bits(1, 5):
|
||||
value = -value
|
||||
writes[instruction.dst] = self._u32(value)
|
||||
return PrimitiveResult(writes, 3 + linbits)
|
||||
|
||||
def _execute_local(
|
||||
self, instruction: Instruction, registers: list[int], entry: Entry
|
||||
) -> PrimitiveResult:
|
||||
opcode = LocalOpcode(instruction.opcode)
|
||||
writes: dict[int, int] = {}
|
||||
if opcode in (LocalOpcode.LD32, LocalOpcode.ST32):
|
||||
offset = self.signed(instruction.immediate, 16)
|
||||
address = entry.scratch_base + registers[instruction.src0] + offset
|
||||
if address & 3:
|
||||
raise PrimitiveFault(11, 11, 5)
|
||||
if opcode == LocalOpcode.LD32:
|
||||
writes[instruction.dst] = self._read_u32(address, entry)
|
||||
return PrimitiveResult(writes, 2)
|
||||
self._write_s32(address, registers[instruction.src1], entry)
|
||||
return PrimitiveResult(cycles=1)
|
||||
if opcode in (LocalOpcode.TABLE8, LocalOpcode.TABLE16, LocalOpcode.TABLE32):
|
||||
size = 1 << (instruction.opcode - LocalOpcode.TABLE8)
|
||||
address = entry.table_offset + registers[instruction.src0] + registers[instruction.src1] * size
|
||||
if address & (size - 1):
|
||||
raise PrimitiveFault(11, 11, 5)
|
||||
writes[instruction.dst] = int.from_bytes(self.memory.read(address, size, entry, True), "little")
|
||||
return PrimitiveResult(writes, 2)
|
||||
if opcode == LocalOpcode.FIFO_POP:
|
||||
word = self.input_fifo.pop()
|
||||
writes[instruction.dst] = word & 0xFFFFFFFF
|
||||
writes[instruction.dst + 1] = ((word >> 32) & 7) | (((word >> 40) & 1) << 8)
|
||||
return PrimitiveResult(writes)
|
||||
metadata = registers[instruction.src1]
|
||||
if metadata & ~0x107 or not 1 <= (metadata & 7) <= 4:
|
||||
raise PrimitiveFault(22, 7)
|
||||
self.output_fifo.push(
|
||||
(registers[instruction.src0] & 0xFFFFFFFF)
|
||||
| ((metadata & 7) << 32)
|
||||
| (((metadata >> 8) & 1) << 40)
|
||||
)
|
||||
return PrimitiveResult()
|
||||
|
||||
def _kernel_arrays(
|
||||
self, instruction: Instruction, registers: list[int], entry: Entry
|
||||
) -> tuple[int, int, int, int]:
|
||||
count = instruction.immediate & 0xFFFF
|
||||
input_address = entry.scratch_base + registers[instruction.src0]
|
||||
parameter_address = entry.scratch_base + registers[instruction.src1]
|
||||
output_address = entry.scratch_base + registers[instruction.dst]
|
||||
output_alignment = (
|
||||
2
|
||||
if instruction.opcode == KernelOpcode.PCM_PACK and (instruction.aux & 3) == 0
|
||||
else 4
|
||||
)
|
||||
if (
|
||||
entry.scratch_base & 3
|
||||
or input_address & 3
|
||||
or parameter_address & 3
|
||||
or output_address & (output_alignment - 1)
|
||||
):
|
||||
raise PrimitiveFault(11, 11, 5)
|
||||
self.memory._check_range(parameter_address, 4, entry)
|
||||
return count, input_address, parameter_address, output_address
|
||||
|
||||
def _execute_kernel(
|
||||
self, instruction: Instruction, registers: list[int], entry: Entry
|
||||
) -> PrimitiveResult:
|
||||
opcode = KernelOpcode(instruction.opcode)
|
||||
count, input_address, parameter_address, output_address = self._kernel_arrays(
|
||||
instruction, registers, entry
|
||||
)
|
||||
produced = 0
|
||||
cycles = 8
|
||||
if opcode == KernelOpcode.REQUANT:
|
||||
reference = self._read_u32(parameter_address, entry)
|
||||
scale_address, scale_table = self._resolve_ref(reference, entry, count * 4)
|
||||
self.memory._check_range(input_address, count * 4, entry)
|
||||
width = (16, 24, 32)[instruction.aux >> 6]
|
||||
shift = 30 + (instruction.aux & 0x1F)
|
||||
for index in range(count):
|
||||
sample = self._read_s32(input_address + index * 4, entry)
|
||||
scale = self._read_s32(scale_address + index * 4, entry, scale_table)
|
||||
product = sample * scale
|
||||
value = self.rne(product, shift) if instruction.aux & 0x20 else product >> shift
|
||||
self._write_s32(output_address + index * 4, self.sat(value, width), entry)
|
||||
produced, cycles = count, 8 + 4 * count
|
||||
elif opcode in (KernelOpcode.STEREO, KernelOpcode.DECORRELATE):
|
||||
reference = self._read_u32(parameter_address, entry)
|
||||
second_address, _ = self._resolve_ref(reference, entry, count * 4, state=True)
|
||||
self.memory._check_range(input_address, count * 4, entry)
|
||||
for index in range(count):
|
||||
left = self._read_s32(input_address + index * 4, entry)
|
||||
right = self._read_s32(second_address + index * 4, entry)
|
||||
mode = instruction.aux & 3
|
||||
if mode == 0:
|
||||
pair = left, right
|
||||
elif mode == 1:
|
||||
pair = left, left - right
|
||||
elif mode == 2:
|
||||
pair = left + right, right
|
||||
else:
|
||||
middle = left * 2 + (right & 1)
|
||||
pair = (middle + right) >> 1, (middle - right) >> 1
|
||||
if opcode == KernelOpcode.DECORRELATE and any(
|
||||
value < -(1 << 31) or value > (1 << 31) - 1 for value in pair
|
||||
):
|
||||
raise PrimitiveFault(8, 6)
|
||||
for channel, value in enumerate(pair):
|
||||
self._write_s32(output_address + (channel * count + index) * 4, self.sat(value, 32), entry)
|
||||
produced, cycles = 2 * count, 8 + 4 * count
|
||||
elif opcode in (KernelOpcode.IMDCT6, KernelOpcode.IMDCT18):
|
||||
order = 6 if opcode == KernelOpcode.IMDCT6 else 18
|
||||
reference = self._read_u32(parameter_address, entry)
|
||||
matrix_address, matrix_table = self._resolve_ref(reference, entry, 2 * order * order * 4)
|
||||
self.memory._check_range(input_address, count * order * 4, entry)
|
||||
for block in range(count):
|
||||
values = [self._read_s32(input_address + (block * order + item) * 4, entry) for item in range(order)]
|
||||
for row in range(2 * order):
|
||||
coefficients = [
|
||||
self._read_s32(matrix_address + (row * order + item) * 4, entry, matrix_table)
|
||||
for item in range(order)
|
||||
]
|
||||
value = self.sat(self.rne(self._dot(values, coefficients), 30), 32)
|
||||
self._write_s32(output_address + (block * 2 * order + row) * 4, value, entry)
|
||||
produced = count * 2 * order
|
||||
cycles = 8 + (94 if order == 6 else 706) * count
|
||||
elif opcode == KernelOpcode.FIXED:
|
||||
order = instruction.aux & 7
|
||||
reference = self._read_u32(parameter_address, entry)
|
||||
history_address, _ = self._resolve_ref(reference, entry, max(order, 1) * 4, state=True)
|
||||
history = [self._read_s32(history_address + index * 4, entry) for index in range(order)]
|
||||
self.memory._check_range(input_address, count * 4, entry)
|
||||
for index in range(count):
|
||||
if order == 0:
|
||||
predictor = 0
|
||||
elif order == 1:
|
||||
predictor = history[0]
|
||||
elif order == 2:
|
||||
predictor = 2 * history[0] - history[1]
|
||||
elif order == 3:
|
||||
predictor = 3 * history[0] - 3 * history[1] + history[2]
|
||||
else:
|
||||
predictor = 4 * history[0] - 6 * history[1] + 4 * history[2] - history[3]
|
||||
value = predictor + self._read_s32(input_address + index * 4, entry)
|
||||
if value < -(1 << 31) or value > (1 << 31) - 1:
|
||||
raise PrimitiveFault(8, 6)
|
||||
self._write_s32(output_address + index * 4, value, entry)
|
||||
if order:
|
||||
history = [value, *history[:-1]]
|
||||
for index, value in enumerate(history):
|
||||
self._write_s32(history_address + index * 4, value, entry)
|
||||
produced, cycles = count, 8 + (order + 4) * count
|
||||
elif opcode == KernelOpcode.LPC:
|
||||
order = instruction.aux & 0x3F
|
||||
coefficient_ref = self._read_u32(parameter_address, entry)
|
||||
history_ref = self._read_u32(parameter_address + 4, entry)
|
||||
shift = self.signed(self._read_u32(parameter_address + 8, entry), 32)
|
||||
if not -31 <= shift <= 31:
|
||||
raise PrimitiveFault(11, 11, 5)
|
||||
coefficient_address, coefficient_table = self._resolve_ref(coefficient_ref, entry, order * 4)
|
||||
history_address, _ = self._resolve_ref(history_ref, entry, order * 4, state=True)
|
||||
coefficients = [
|
||||
self._read_s32(coefficient_address + index * 4, entry, coefficient_table)
|
||||
for index in range(order)
|
||||
]
|
||||
history = [self._read_s32(history_address + index * 4, entry) for index in range(order)]
|
||||
for index in range(count):
|
||||
predictor = self._dot(coefficients, history)
|
||||
predictor = predictor >> shift if shift >= 0 else predictor << -shift
|
||||
value = predictor + self._read_s32(input_address + index * 4, entry)
|
||||
if value < -(1 << 31) or value > (1 << 31) - 1:
|
||||
raise PrimitiveFault(8, 6)
|
||||
self._write_s32(output_address + index * 4, value, entry)
|
||||
history = [value, *history[:-1]]
|
||||
for index, value in enumerate(history):
|
||||
self._write_s32(history_address + index * 4, value, entry)
|
||||
produced, cycles = count, 8 + (2 * order + 5) * count
|
||||
elif opcode == KernelOpcode.PCM_PACK:
|
||||
gain = self._read_s32(parameter_address, entry)
|
||||
channels = self._read_u32(parameter_address + 4, entry)
|
||||
if channels not in (1, 2) or (instruction.aux & 4 and channels != 1):
|
||||
raise PrimitiveFault(8, 7)
|
||||
samples = count * channels
|
||||
output = bytearray()
|
||||
for index in range(samples):
|
||||
sample = self._read_s32(input_address + index * 4, entry)
|
||||
value = self.rne(sample * gain, 30)
|
||||
packed = self.sat(value, 16 if (instruction.aux & 3) == 0 else 24)
|
||||
encoded = self._u32(packed).to_bytes(4, "little")
|
||||
encoded = encoded[:2] if (instruction.aux & 3) == 0 else encoded
|
||||
output.extend(encoded)
|
||||
if instruction.aux & 4:
|
||||
output.extend(encoded)
|
||||
self.memory.write(output_address, output, entry)
|
||||
produced, cycles = len(output), 8 + 5 * (count * (2 if instruction.aux & 4 else channels))
|
||||
elif opcode == KernelOpcode.RESAMPLE:
|
||||
produced, cycles = self._resample(instruction, count, input_address, parameter_address, output_address, entry)
|
||||
else:
|
||||
produced, cycles = self._dct32(count, input_address, parameter_address, output_address, entry)
|
||||
self.kernel_done = True
|
||||
return PrimitiveResult({instruction.dst: produced}, cycles, True)
|
||||
|
||||
def _dct32(self, count: int, input_address: int, parameter_address: int,
|
||||
output_address: int, entry: Entry) -> tuple[int, int]:
|
||||
refs = [self._read_u32(parameter_address + index * 4, entry) for index in range(3)]
|
||||
phase = self._read_u32(parameter_address + 12, entry)
|
||||
if phase >> 4:
|
||||
raise PrimitiveFault(11, 11, 5)
|
||||
matrix_address, matrix_table = self._resolve_ref(refs[0], entry, 32 * 32 * 4)
|
||||
window_address, window_table = self._resolve_ref(refs[1], entry, 16 * 32 * 4)
|
||||
history_address, _ = self._resolve_ref(refs[2], entry, 16 * 32 * 4, state=True)
|
||||
for block in range(count):
|
||||
values = [self._read_s32(input_address + (block * 32 + item) * 4, entry) for item in range(32)]
|
||||
transformed = []
|
||||
for row in range(32):
|
||||
coefficients = [
|
||||
self._read_s32(matrix_address + (row * 32 + item) * 4, entry, matrix_table)
|
||||
for item in range(32)
|
||||
]
|
||||
transformed.append(self.sat(self.rne(self._dot(values, coefficients), 30), 32))
|
||||
for item, value in enumerate(transformed):
|
||||
self._write_s32(history_address + (phase * 32 + item) * 4, value, entry)
|
||||
for item in range(32):
|
||||
history = [
|
||||
self._read_s32(history_address + (((phase - tap) & 15) * 32 + item) * 4, entry)
|
||||
for tap in range(16)
|
||||
]
|
||||
window = [
|
||||
self._read_s32(window_address + (tap * 32 + item) * 4, entry, window_table)
|
||||
for tap in range(16)
|
||||
]
|
||||
self._write_s32(
|
||||
output_address + (block * 32 + item) * 4,
|
||||
self.sat(self.rne(self._dot(history, window), 30), 32),
|
||||
entry,
|
||||
)
|
||||
phase = (phase + 1) & 15
|
||||
self._write_s32(parameter_address + 12, phase, entry)
|
||||
return 32 * count, 8 + 2304 * count
|
||||
|
||||
def _resample(self, instruction: Instruction, count: int, input_address: int,
|
||||
parameter_address: int, output_address: int, entry: Entry) -> tuple[int, int]:
|
||||
ratios = (
|
||||
(1, 1), (1, 2), (80, 147), (160, 147), (3, 2), (2, 1), (3, 1), (4, 1),
|
||||
(6, 1), (8, 1), (12, 1), (320, 147), (640, 147), (1280, 147), (2, 3), (4, 3),
|
||||
)
|
||||
profile = instruction.aux & 15
|
||||
if self.resample_profile is None:
|
||||
self.resample_profile = profile
|
||||
elif profile != self.resample_profile:
|
||||
raise PrimitiveFault(8, 7)
|
||||
refs = [self._read_u32(parameter_address + index * 4, entry) for index in range(3)]
|
||||
next_output = self._read_u32(parameter_address + 12, entry) | (
|
||||
self._read_u32(parameter_address + 16, entry) << 32
|
||||
)
|
||||
input_base = self._read_u32(parameter_address + 20, entry) | (
|
||||
self._read_u32(parameter_address + 24, entry) << 32
|
||||
)
|
||||
channels = self._read_u32(parameter_address + 28, entry)
|
||||
if channels not in (1, 2):
|
||||
raise PrimitiveFault(8, 7)
|
||||
if profile == 0:
|
||||
payload = self.memory.read(input_address, count * channels * 4, entry)
|
||||
self.memory.write(output_address, payload, entry)
|
||||
next_output += count
|
||||
input_base += count
|
||||
produced = count
|
||||
cycles = 8 + 2 * count * channels
|
||||
else:
|
||||
bank_index = 2 if profile in (1, 2) else (1 if profile == 14 else 0)
|
||||
coefficient_address, coefficient_table = self._resolve_ref(refs[bank_index], entry, 32 * 16 * 4)
|
||||
numerator, denominator = ratios[profile]
|
||||
produced = 0
|
||||
while next_output // numerator < input_base + count:
|
||||
source_index, remainder = divmod(next_output, numerator)
|
||||
quotient, phase_remainder = divmod(32 * remainder, numerator)
|
||||
phase = quotient
|
||||
doubled_remainder = 2 * phase_remainder
|
||||
if doubled_remainder > numerator or (
|
||||
doubled_remainder == numerator and phase & 1
|
||||
):
|
||||
phase += 1
|
||||
if phase == 32:
|
||||
phase = 0
|
||||
source_index += 1
|
||||
for channel in range(channels):
|
||||
samples = [
|
||||
self._read_s32(
|
||||
input_address + ((source_index + tap - 7 - input_base) * channels + channel) * 4,
|
||||
entry,
|
||||
)
|
||||
for tap in range(16)
|
||||
]
|
||||
coefficients = [
|
||||
self._read_s32(coefficient_address + (phase * 16 + tap) * 4, entry, coefficient_table)
|
||||
for tap in range(16)
|
||||
]
|
||||
self._write_s32(
|
||||
output_address + (produced * channels + channel) * 4,
|
||||
self.sat(self.rne(self._dot(samples, coefficients), 30), 32),
|
||||
entry,
|
||||
)
|
||||
produced += 1
|
||||
next_output += denominator
|
||||
input_base += count
|
||||
cycles = 8 + 2 * count * channels + 36 * produced * channels
|
||||
for index, value in enumerate((next_output, input_base)):
|
||||
self._write_s32(parameter_address + 12 + index * 8, value & 0xFFFFFFFF, entry)
|
||||
self._write_s32(parameter_address + 16 + index * 8, value >> 32, entry)
|
||||
return produced, cycles
|
||||
+4
-1
@@ -19,7 +19,10 @@ tooling.
|
||||
- `test_apu.py` checks the fail-closed APU APB4/IRQ shell plus the P2 private
|
||||
DMA, ring scheduler, stream router, Gateway A, and verification-only backend;
|
||||
`test_apu_register_parity.py` keeps its handwritten RTL/C ABI and matrix
|
||||
coverage synchronized.
|
||||
coverage synchronized. `test_apu_primitives.py` compares the P4 assembler,
|
||||
BAM, loader, sequencer, local SRAM, FIFOs, and production primitive engines
|
||||
with Icarus and Verilator while keeping its injectors out of product
|
||||
filelists.
|
||||
- `test_user_ip_register_parity.py` keeps the integrated slot 1 timer and slot
|
||||
2 GPIO register offsets synchronized with their application-owned C
|
||||
definitions and checks their extension-manifest slot assignments.
|
||||
|
||||
@@ -221,6 +221,7 @@ module apu_p3_microcode_tb;
|
||||
.fault_addr_o (seq_fault_addr),
|
||||
.fault_detail_o (seq_fault_detail),
|
||||
.perf_retired_o (seq_perf_retired),
|
||||
.launch_epoch_o (),
|
||||
.idle_o (seq_idle)
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
`include "apu_define.svh"
|
||||
|
||||
module apu_p4_loader_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic start_i, abort_i, resource_reset_i, soft_reset_i, counter_clear_i;
|
||||
logic [31:0] image_addr_i, image_size_i, expected_crc_i;
|
||||
logic dma_request_valid, dma_request_ready, dma_ready, dma_done;
|
||||
logic [31:0] dma_request_addr, dma_request_bytes, dma_data;
|
||||
logic [3:0] dma_keep;
|
||||
logic dma_last, dma_valid;
|
||||
logic store_active, store_read, store_write, store_valid;
|
||||
logic [10:0] store_addr, fetch_addr;
|
||||
logic [63:0] store_write_data, store_read_data, fetch_data;
|
||||
logic fetch, fetch_valid;
|
||||
logic local_write;
|
||||
logic [16:0] local_addr;
|
||||
logic [31:0] local_data, local_read_data;
|
||||
logic [3:0] local_strb;
|
||||
logic local_read_req, local_read_valid, local_read_error;
|
||||
logic [15:0] table_bytes;
|
||||
logic [ 7:0] status;
|
||||
logic [31:0] abi, actual_crc, load_count;
|
||||
logic [63:0] build_id;
|
||||
logic lock, load_done, abort_done, fault_valid, loader_idle;
|
||||
logic [5:0] fault_code;
|
||||
logic [3:0] fault_stage;
|
||||
logic [1:0] fault_resp;
|
||||
logic [31:0] fault_addr, fault_detail;
|
||||
logic [2:0][10:0] entry_pc, entry_first, entry_last;
|
||||
logic [2:0][15:0] entry_max_loop;
|
||||
logic [2:0][23:0] entry_max_retired;
|
||||
logic [2:0][16:0] entry_scratch_base, entry_scratch_bytes;
|
||||
logic [2:0][31:0] entry_primitive_mask;
|
||||
logic [2:0][15:0] entry_table_offset, entry_table_bytes;
|
||||
logic [31:0] image [0:4095];
|
||||
logic transfer_active_q;
|
||||
logic [11:0] transfer_word_q, transfer_beat_q;
|
||||
logic [31:0] transfer_bytes_q;
|
||||
logic [ 3:0] request_count_q;
|
||||
logic [11:0] store_write_count_q, table_write_count_q;
|
||||
string image_path, invalid_image_path;
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
assign dma_request_ready = 1'b1;
|
||||
assign dma_valid = transfer_active_q;
|
||||
assign dma_data = image[transfer_word_q];
|
||||
assign dma_keep = 4'hf;
|
||||
assign dma_last = transfer_beat_q == ((transfer_bytes_q >> 2) - 1'b1);
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
transfer_active_q <= 1'b0;
|
||||
transfer_word_q <= 12'd0;
|
||||
transfer_beat_q <= 12'd0;
|
||||
transfer_bytes_q <= 32'd0;
|
||||
request_count_q <= 4'd0;
|
||||
store_write_count_q <= 12'd0;
|
||||
table_write_count_q <= 12'd0;
|
||||
dma_done <= 1'b0;
|
||||
end else begin
|
||||
dma_done <= 1'b0;
|
||||
if (dma_request_valid && dma_request_ready) begin
|
||||
transfer_active_q <= 1'b1;
|
||||
transfer_word_q <= 12'((dma_request_addr - image_addr_i) >> 2);
|
||||
transfer_beat_q <= 12'd0;
|
||||
transfer_bytes_q <= dma_request_bytes;
|
||||
request_count_q <= request_count_q + 1'b1;
|
||||
end
|
||||
if (dma_valid && dma_ready) begin
|
||||
if (dma_last) begin
|
||||
transfer_active_q <= 1'b0;
|
||||
dma_done <= 1'b1;
|
||||
end else begin
|
||||
transfer_word_q <= transfer_word_q + 1'b1;
|
||||
transfer_beat_q <= transfer_beat_q + 1'b1;
|
||||
end
|
||||
end
|
||||
if (store_write) begin
|
||||
if (request_count_q < 4'd3) $fatal(1, "P4 store write preceded range admission");
|
||||
store_write_count_q <= store_write_count_q + 1'b1;
|
||||
end
|
||||
if (local_write) begin
|
||||
if (request_count_q < 4'd3) $fatal(1, "P4 table write preceded range admission");
|
||||
if (local_addr >= table_bytes) $fatal(1, "P4 loader wrote outside table payload");
|
||||
table_write_count_q <= table_write_count_q + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
apu_microcode_loader #(
|
||||
.EnableP4(1'b1)
|
||||
) u_loader (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.start_i (start_i),
|
||||
.abort_i (abort_i),
|
||||
.resource_reset_i (resource_reset_i),
|
||||
.soft_reset_i (soft_reset_i),
|
||||
.counter_clear_i (counter_clear_i),
|
||||
.image_addr_i (image_addr_i),
|
||||
.image_size_i (image_size_i),
|
||||
.expected_crc_i (expected_crc_i),
|
||||
.dma_request_valid_o (dma_request_valid),
|
||||
.dma_request_ready_i (dma_request_ready),
|
||||
.dma_request_addr_o (dma_request_addr),
|
||||
.dma_request_bytes_o (dma_request_bytes),
|
||||
.dma_data_i (dma_data),
|
||||
.dma_keep_i (dma_keep),
|
||||
.dma_last_i (dma_last),
|
||||
.dma_valid_i (dma_valid),
|
||||
.dma_ready_o (dma_ready),
|
||||
.dma_done_i (dma_done),
|
||||
.dma_err_i (1'b0),
|
||||
.dma_err_code_i (6'd0),
|
||||
.dma_err_stage_i (4'd0),
|
||||
.dma_err_resp_i (2'd0),
|
||||
.dma_err_addr_i (32'd0),
|
||||
.store_active_o (store_active),
|
||||
.store_read_o (store_read),
|
||||
.store_write_o (store_write),
|
||||
.store_addr_o (store_addr),
|
||||
.store_data_o (store_write_data),
|
||||
.store_data_i (store_read_data),
|
||||
.store_valid_i (store_valid),
|
||||
.local_write_o (local_write),
|
||||
.local_addr_o (local_addr),
|
||||
.local_data_o (local_data),
|
||||
.local_strb_o (local_strb),
|
||||
.table_bytes_o (table_bytes),
|
||||
.stat_o (status),
|
||||
.abi_o (abi),
|
||||
.build_id_o (build_id),
|
||||
.lock_o (lock),
|
||||
.actual_crc_o (actual_crc),
|
||||
.load_count_o (load_count),
|
||||
.entry_pc_o (entry_pc),
|
||||
.entry_first_o (entry_first),
|
||||
.entry_last_o (entry_last),
|
||||
.entry_max_loop_o (entry_max_loop),
|
||||
.entry_max_retired_o (entry_max_retired),
|
||||
.entry_scratch_base_o (entry_scratch_base),
|
||||
.entry_scratch_bytes_o (entry_scratch_bytes),
|
||||
.entry_primitive_mask_o(entry_primitive_mask),
|
||||
.entry_table_offset_o (entry_table_offset),
|
||||
.entry_table_bytes_o (entry_table_bytes),
|
||||
.load_done_o (load_done),
|
||||
.abort_done_o (abort_done),
|
||||
.fault_valid_o (fault_valid),
|
||||
.fault_code_o (fault_code),
|
||||
.fault_stage_o (fault_stage),
|
||||
.fault_resp_o (fault_resp),
|
||||
.fault_addr_o (fault_addr),
|
||||
.fault_detail_o (fault_detail),
|
||||
.idle_o (loader_idle)
|
||||
);
|
||||
|
||||
apu_control_store u_control_store (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.loader_active_i(store_active),
|
||||
.loader_read_i (store_read),
|
||||
.loader_write_i (store_write),
|
||||
.loader_addr_i (store_addr),
|
||||
.loader_data_i (store_write_data),
|
||||
.loader_data_o (store_read_data),
|
||||
.loader_valid_o (store_valid),
|
||||
.image_valid_i (status[`APB4_APU__MC_STATUS_VALID]),
|
||||
.fetch_i (fetch),
|
||||
.fetch_addr_i (fetch_addr),
|
||||
.fetch_data_o (fetch_data),
|
||||
.fetch_valid_o (fetch_valid)
|
||||
);
|
||||
|
||||
apu_local_sram u_local_sram (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.image_valid_i (status[`APB4_APU__MC_STATUS_VALID]),
|
||||
.table_bytes_i (table_bytes),
|
||||
.epoch_clear_i (1'b0),
|
||||
.loader_active_i (!loader_idle),
|
||||
.loader_req_i (local_write),
|
||||
.loader_addr_i (local_addr),
|
||||
.loader_data_i (local_data),
|
||||
.loader_strb_i (local_strb),
|
||||
.loader_ready_o (),
|
||||
.codec_req_i (local_read_req),
|
||||
.codec_write_i (1'b0),
|
||||
.codec_addr_i (17'd0),
|
||||
.codec_data_i (32'd0),
|
||||
.codec_strb_i (4'd0),
|
||||
.codec_ready_o (),
|
||||
.codec_data_o (local_read_data),
|
||||
.codec_valid_o (local_read_valid),
|
||||
.codec_access_err_o(local_read_error)
|
||||
);
|
||||
|
||||
task automatic hard_reset;
|
||||
begin
|
||||
rst_n_i = 1'b0;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
repeat (2) @(posedge clk_i);
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
if (!$value$plusargs("IMAGE=%s", image_path)) $fatal(1, "IMAGE plusarg missing");
|
||||
if (!$value$plusargs("INVALID_IMAGE=%s", invalid_image_path)) begin
|
||||
$fatal(1, "INVALID_IMAGE plusarg missing");
|
||||
end
|
||||
$readmemh(image_path, image);
|
||||
start_i = 1'b0;
|
||||
abort_i = 1'b0;
|
||||
resource_reset_i = 1'b0;
|
||||
soft_reset_i = 1'b0;
|
||||
counter_clear_i = 1'b0;
|
||||
image_addr_i = 32'h2000_0000;
|
||||
image_size_i = image[2];
|
||||
expected_crc_i = image[11];
|
||||
fetch = 1'b0;
|
||||
fetch_addr = 11'd0;
|
||||
local_read_req = 1'b0;
|
||||
hard_reset();
|
||||
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b0;
|
||||
for (int cycle = 0; cycle < 30000; cycle++) begin
|
||||
@(posedge clk_i);
|
||||
if (loader_idle && status[`APB4_APU__MC_STATUS_VALID]) break;
|
||||
if (fault_valid) $fatal(1, "P4 loader fault code=%0d detail=%08x", fault_code, fault_detail);
|
||||
if (cycle == 29999) $fatal(1, "P4 loader timed out");
|
||||
end
|
||||
if (!lock || !load_done || (abi != 32'h0001_0000) || (table_bytes != 16'd4) ||
|
||||
(store_write_count_q != 12'd2) || (table_write_count_q != 12'd1)) begin
|
||||
$fatal(1, "P4 loader publication mismatch");
|
||||
end
|
||||
if ((entry_scratch_base[0] != 17'h01000) || (entry_scratch_bytes[0] != 17'h01000) ||
|
||||
(entry_primitive_mask[0] != 32'h0000_0040) ||
|
||||
(entry_table_bytes[0] != 16'd4)) begin
|
||||
$fatal(1, "P4 descriptor publication mismatch");
|
||||
end
|
||||
|
||||
@(negedge clk_i);
|
||||
fetch = 1'b1;
|
||||
@(negedge clk_i);
|
||||
fetch = 1'b0;
|
||||
if (!fetch_valid || (fetch_data != 64'h0800_0001_0000_0000)) begin
|
||||
$fatal(1, "P4 control-store publication mismatch");
|
||||
end
|
||||
@(negedge clk_i);
|
||||
local_read_req = 1'b1;
|
||||
@(negedge clk_i);
|
||||
local_read_req = 1'b0;
|
||||
if (!local_read_valid || local_read_error || (local_read_data != 32'h0001_0001)) begin
|
||||
$fatal(1, "P4 table publication mismatch");
|
||||
end
|
||||
|
||||
@(negedge clk_i);
|
||||
soft_reset_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
soft_reset_i = 1'b0;
|
||||
@(posedge clk_i);
|
||||
if (!status[`APB4_APU__MC_STATUS_VALID] || !lock || (table_bytes != 16'd4)) begin
|
||||
$fatal(1, "P4 soft reset did not retain the locked image");
|
||||
end
|
||||
|
||||
hard_reset();
|
||||
$readmemh(invalid_image_path, image);
|
||||
image_size_i = image[2];
|
||||
expected_crc_i = image[11];
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b0;
|
||||
for (int cycle = 0; cycle < 1000; cycle++) begin
|
||||
@(posedge clk_i);
|
||||
if (fault_valid) begin
|
||||
if ((fault_code != `APB4_APU__ERROR_CODE_MICROCODE) ||
|
||||
(fault_stage != `APB4_APU__ERROR_STAGE_LOADER) ||
|
||||
(fault_addr != image_addr_i + 32'd92) || (fault_detail != 32'h0000_0008)) begin
|
||||
$fatal(1, "P4 zero-byte table diagnostic mismatch");
|
||||
end
|
||||
break;
|
||||
end
|
||||
if (cycle == 999) $fatal(1, "P4 invalid-table load timed out");
|
||||
end
|
||||
if (status[`APB4_APU__MC_STATUS_VALID] || lock || !status[5] ||
|
||||
(store_write_count_q != 12'd0) || (table_write_count_q != 12'd0)) begin
|
||||
$fatal(1, "P4 invalid descriptor mutated unpublished stores");
|
||||
end
|
||||
$display("APU_P4_LOADER_PASS");
|
||||
$finish;
|
||||
end
|
||||
|
||||
logic s_unused;
|
||||
assign s_unused = abort_done ^ ^actual_crc ^ ^load_count ^ ^build_id ^ ^entry_pc ^
|
||||
^entry_first ^ ^entry_last ^ ^entry_max_loop ^ ^entry_max_retired ^
|
||||
^entry_table_offset ^ fault_stage[0] ^ fault_resp[0] ^ fault_addr[0];
|
||||
endmodule
|
||||
@@ -0,0 +1,766 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
`include "apu_define.svh"
|
||||
|
||||
module apu_p4_primitives_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic flush_i, epoch_clear_i, image_valid_i;
|
||||
logic req_valid, req_ready;
|
||||
logic [63:0] instruction;
|
||||
logic [31:0] source0, source1, destination;
|
||||
logic result_valid, result_kernel, result_error, kernel_done, dispatcher_busy;
|
||||
logic [ 3:0] result_dst;
|
||||
logic [ 3:0][31:0] result_data;
|
||||
logic [ 2:0] result_words;
|
||||
logic [ 5:0] error_code;
|
||||
logic [ 3:0] error_stage;
|
||||
logic [ 7:0] error_reason;
|
||||
logic [31:0] result_cycles;
|
||||
logic input_exhausted, input_ready, output_ready;
|
||||
logic [6:0] input_count, output_count;
|
||||
logic input_valid, input_accept, output_valid, output_accept;
|
||||
logic [40:0] input_data, output_data;
|
||||
logic dispatcher_memory_req, dispatcher_memory_write;
|
||||
logic [16:0] dispatcher_memory_addr;
|
||||
logic [31:0] dispatcher_memory_data;
|
||||
logic [ 3:0] dispatcher_memory_strb;
|
||||
logic memory_valid, memory_error, memory_ready;
|
||||
logic [31:0] memory_read_data;
|
||||
logic loader_active, loader_req;
|
||||
logic [16:0] loader_addr;
|
||||
logic [31:0] loader_data;
|
||||
logic [ 3:0] loader_strb;
|
||||
logic inject_active, inject_req, inject_write;
|
||||
logic [16:0] inject_addr;
|
||||
logic [31:0] inject_data;
|
||||
logic [ 3:0] inject_strb;
|
||||
logic codec_req, codec_write;
|
||||
logic [16:0] codec_addr;
|
||||
logic [31:0] codec_data;
|
||||
logic [ 3:0] codec_strb;
|
||||
logic s_latency_active_q;
|
||||
logic [31:0] s_latency_cycles_q;
|
||||
logic [31:0] s_dispatcher_request_count_q;
|
||||
logic [31:0] s_dispatcher_write_count_q;
|
||||
logic [31:0] requant_vectors [0:255];
|
||||
integer requant_vector_count;
|
||||
string requant_vector_path;
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_latency_active_q <= 1'b0;
|
||||
s_latency_cycles_q <= 32'd0;
|
||||
end else if (flush_i) begin
|
||||
s_latency_active_q <= 1'b0;
|
||||
s_latency_cycles_q <= 32'd0;
|
||||
end else if (result_valid) begin
|
||||
if (s_latency_active_q && (s_latency_cycles_q > result_cycles)) begin
|
||||
$fatal(1, "APU-P4 request latency exceeded bound actual=%0d bound=%0d instruction=%016x",
|
||||
s_latency_cycles_q, result_cycles, instruction);
|
||||
end
|
||||
s_latency_active_q <= 1'b0;
|
||||
s_latency_cycles_q <= 32'd0;
|
||||
end else if (req_valid && req_ready) begin
|
||||
s_latency_active_q <= 1'b1;
|
||||
s_latency_cycles_q <= 32'd0;
|
||||
end else if (s_latency_active_q) begin
|
||||
s_latency_cycles_q <= s_latency_cycles_q + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_dispatcher_request_count_q <= 32'd0;
|
||||
s_dispatcher_write_count_q <= 32'd0;
|
||||
end else begin
|
||||
if (dispatcher_memory_req && memory_ready) begin
|
||||
s_dispatcher_request_count_q <= s_dispatcher_request_count_q + 1'b1;
|
||||
end
|
||||
if (dispatcher_memory_req && dispatcher_memory_write && memory_ready) begin
|
||||
s_dispatcher_write_count_q <= s_dispatcher_write_count_q + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assign codec_req = inject_active ? inject_req : dispatcher_memory_req;
|
||||
assign codec_write = inject_active ? inject_write : dispatcher_memory_write;
|
||||
assign codec_addr = inject_active ? inject_addr : dispatcher_memory_addr;
|
||||
assign codec_data = inject_active ? inject_data : dispatcher_memory_data;
|
||||
assign codec_strb = inject_active ? inject_strb : dispatcher_memory_strb;
|
||||
|
||||
apu_local_sram u_local_sram (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.image_valid_i (image_valid_i),
|
||||
.table_bytes_i (16'h1800),
|
||||
.epoch_clear_i (epoch_clear_i),
|
||||
.loader_active_i (loader_active),
|
||||
.loader_req_i (loader_req),
|
||||
.loader_addr_i (loader_addr),
|
||||
.loader_data_i (loader_data),
|
||||
.loader_strb_i (loader_strb),
|
||||
.loader_ready_o (),
|
||||
.codec_req_i (codec_req),
|
||||
.codec_write_i (codec_write),
|
||||
.codec_addr_i (codec_addr),
|
||||
.codec_data_i (codec_data),
|
||||
.codec_strb_i (codec_strb),
|
||||
.codec_ready_o (memory_ready),
|
||||
.codec_data_o (memory_read_data),
|
||||
.codec_valid_o (memory_valid),
|
||||
.codec_access_err_o(memory_error)
|
||||
);
|
||||
|
||||
apu_primitive_dispatcher u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.flush_i (flush_i),
|
||||
.req_valid_i (req_valid),
|
||||
.req_ready_o (req_ready),
|
||||
.instruction_i (instruction),
|
||||
.source0_i (source0),
|
||||
.source1_i (source1),
|
||||
.destination_i (destination),
|
||||
.scratch_base_i (17'h02000),
|
||||
.scratch_bytes_i (17'h04000),
|
||||
.table_offset_i (16'h0000),
|
||||
.table_bytes_i (16'h1800),
|
||||
.result_valid_o (result_valid),
|
||||
.result_dst_o (result_dst),
|
||||
.result_data_o (result_data),
|
||||
.result_words_o (result_words),
|
||||
.result_kernel_o (result_kernel),
|
||||
.error_o (result_error),
|
||||
.error_code_o (error_code),
|
||||
.error_stage_o (error_stage),
|
||||
.error_reason_o (error_reason),
|
||||
.cycles_o (result_cycles),
|
||||
.kernel_done_o (kernel_done),
|
||||
.busy_o (dispatcher_busy),
|
||||
.input_exhausted_o(input_exhausted),
|
||||
.input_ready_o (input_ready),
|
||||
.output_ready_o (output_ready),
|
||||
.input_count_o (input_count),
|
||||
.output_count_o (output_count),
|
||||
.kernel_busy_o (),
|
||||
.memory_req_o (dispatcher_memory_req),
|
||||
.memory_write_o (dispatcher_memory_write),
|
||||
.memory_addr_o (dispatcher_memory_addr),
|
||||
.memory_data_o (dispatcher_memory_data),
|
||||
.memory_strb_o (dispatcher_memory_strb),
|
||||
.memory_valid_i (memory_valid),
|
||||
.memory_data_i (memory_read_data),
|
||||
.memory_error_i (memory_error),
|
||||
.input_valid_i (input_valid),
|
||||
.input_data_i (input_data),
|
||||
.input_accept_o (input_accept),
|
||||
.output_valid_o (output_valid),
|
||||
.output_data_o (output_data),
|
||||
.output_accept_i (output_accept)
|
||||
);
|
||||
|
||||
function automatic logic [63:0] encode_instruction(
|
||||
input logic [3:0] instruction_class_i, input logic [3:0] opcode_i, input logic [3:0] dst_i,
|
||||
input logic [3:0] src0_i, input logic [3:0] src1_i, input logic [7:0] aux_i,
|
||||
input logic [31:0] immediate_i);
|
||||
return {instruction_class_i, opcode_i, 4'd0, dst_i, src0_i, src1_i, aux_i, immediate_i};
|
||||
endfunction
|
||||
|
||||
function automatic logic [15:0] expected_resample_frames(input logic [3:0] profile_i);
|
||||
begin
|
||||
unique case (profile_i)
|
||||
4'd0, 4'd1, 4'd2: expected_resample_frames = 16'd1;
|
||||
4'd3: expected_resample_frames = 16'd2;
|
||||
4'd4: expected_resample_frames = 16'd2;
|
||||
4'd5: expected_resample_frames = 16'd2;
|
||||
4'd6: expected_resample_frames = 16'd3;
|
||||
4'd7: expected_resample_frames = 16'd4;
|
||||
4'd8: expected_resample_frames = 16'd6;
|
||||
4'd9: expected_resample_frames = 16'd8;
|
||||
4'd10: expected_resample_frames = 16'd12;
|
||||
4'd11: expected_resample_frames = 16'd3;
|
||||
4'd12: expected_resample_frames = 16'd5;
|
||||
4'd13: expected_resample_frames = 16'd9;
|
||||
4'd14: expected_resample_frames = 16'd1;
|
||||
default: expected_resample_frames = 16'd2;
|
||||
endcase
|
||||
end
|
||||
endfunction
|
||||
|
||||
task automatic hard_reset;
|
||||
begin
|
||||
rst_n_i = 1'b0;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
repeat (2) @(posedge clk_i);
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic publish_table_word(input logic [16:0] address_i, input logic [31:0] data_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
loader_active = 1'b1;
|
||||
loader_req = 1'b1;
|
||||
loader_addr = address_i;
|
||||
loader_data = data_i;
|
||||
loader_strb = 4'hf;
|
||||
@(negedge clk_i);
|
||||
loader_active = 1'b0;
|
||||
loader_req = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic inject_word(input logic [16:0] address_i, input logic [31:0] data_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
inject_active = 1'b1;
|
||||
inject_req = 1'b1;
|
||||
inject_write = 1'b1;
|
||||
inject_addr = address_i;
|
||||
inject_data = data_i;
|
||||
inject_strb = 4'hf;
|
||||
@(negedge clk_i);
|
||||
inject_active = 1'b0;
|
||||
inject_req = 1'b0;
|
||||
inject_write = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic read_word(input logic [16:0] address_i, output logic [31:0] data_o);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
inject_active = 1'b1;
|
||||
inject_req = 1'b1;
|
||||
inject_write = 1'b0;
|
||||
inject_addr = address_i;
|
||||
@(negedge clk_i);
|
||||
inject_req = 1'b0;
|
||||
for (int cycle = 0; cycle < 4; cycle++) begin
|
||||
@(posedge clk_i);
|
||||
if (memory_valid) begin
|
||||
data_o = memory_read_data;
|
||||
inject_active = 1'b0;
|
||||
return;
|
||||
end
|
||||
end
|
||||
$fatal(1, "APU-P4 injected read timed out");
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic push_input(input logic [40:0] data_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
input_data = data_i;
|
||||
input_valid = 1'b1;
|
||||
do @(posedge clk_i); while (!input_accept);
|
||||
@(negedge clk_i);
|
||||
input_valid = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic issue(input logic [63:0] instruction_i, input logic [31:0] source0_i,
|
||||
input logic [31:0] source1_i, input logic [31:0] destination_i,
|
||||
output logic [31:0] result0_o, output logic [31:0] cycles_o);
|
||||
begin
|
||||
int wall_cycles;
|
||||
wall_cycles = 0;
|
||||
@(negedge clk_i);
|
||||
instruction = instruction_i;
|
||||
source0 = source0_i;
|
||||
source1 = source1_i;
|
||||
destination = destination_i;
|
||||
req_valid = 1'b1;
|
||||
for (int cycle = 0; cycle < 10000; cycle++) begin
|
||||
@(posedge clk_i);
|
||||
if (req_ready) begin
|
||||
wall_cycles = 1;
|
||||
@(negedge clk_i);
|
||||
req_valid = 1'b0;
|
||||
break;
|
||||
end
|
||||
end
|
||||
for (int cycle = 0; cycle < 20000; cycle++) begin
|
||||
@(posedge clk_i);
|
||||
wall_cycles++;
|
||||
if (result_valid) begin
|
||||
if (result_error) begin
|
||||
$fatal(1, "APU-P4 operation failed code=%0d stage=%0d reason=%0d", error_code,
|
||||
error_stage, error_reason);
|
||||
end
|
||||
result0_o = result_data[0];
|
||||
cycles_o = result_cycles;
|
||||
if (result_kernel && (u_dut.u_kernel_engine.s_cycles_q > result_cycles)) begin
|
||||
$fatal(1, "APU-P4 wall latency exceeded bound actual=%0d bound=%0d opcode=%0d",
|
||||
u_dut.u_kernel_engine.s_cycles_q, result_cycles, instruction_i[59:56]);
|
||||
end
|
||||
if (result_kernel && (wall_cycles > result_cycles)) begin
|
||||
$fatal(1, "APU-P4 external latency exceeded bound actual=%0d bound=%0d opcode=%0d",
|
||||
wall_cycles, result_cycles, instruction_i[59:56]);
|
||||
end
|
||||
return;
|
||||
end
|
||||
end
|
||||
$fatal(1, "APU-P4 operation timed out");
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic issue_expect_error(
|
||||
input logic [63:0] instruction_i, input logic [31:0] source0_i, input logic [31:0] source1_i,
|
||||
input logic [31:0] destination_i, input logic [5:0] expected_code_i,
|
||||
input logic [3:0] expected_stage_i, input logic [7:0] expected_reason_i);
|
||||
begin
|
||||
logic [31:0] s_write_count_before;
|
||||
s_write_count_before = s_dispatcher_write_count_q;
|
||||
@(negedge clk_i);
|
||||
instruction = instruction_i;
|
||||
source0 = source0_i;
|
||||
source1 = source1_i;
|
||||
destination = destination_i;
|
||||
req_valid = 1'b1;
|
||||
for (int cycle = 0; cycle < 1000; cycle++) begin
|
||||
@(posedge clk_i);
|
||||
if (result_valid) begin
|
||||
if (!result_error || (error_code != expected_code_i) ||
|
||||
(error_stage != expected_stage_i) || (error_reason != expected_reason_i)) begin
|
||||
$fatal(1, "APU-P4 error tuple mismatch got=%0d/%0d/%0d expected=%0d/%0d/%0d",
|
||||
error_code, error_stage, error_reason, expected_code_i, expected_stage_i,
|
||||
expected_reason_i);
|
||||
end
|
||||
@(negedge clk_i);
|
||||
req_valid = 1'b0;
|
||||
if ((result_words != 3'd0) || (result_data != '0)) begin
|
||||
$fatal(1, "APU-P4 error exposed destination data words=%0d data=%h", result_words,
|
||||
result_data);
|
||||
end
|
||||
if (s_dispatcher_write_count_q != s_write_count_before) begin
|
||||
$fatal(
|
||||
1,
|
||||
"APU-P4 rejected operation wrote local SRAM before=%0d after=%0d instruction=%016x",
|
||||
s_write_count_before, s_dispatcher_write_count_q, instruction_i);
|
||||
end
|
||||
return;
|
||||
end
|
||||
if (req_ready) begin
|
||||
@(negedge clk_i);
|
||||
req_valid = 1'b0;
|
||||
end
|
||||
end
|
||||
$fatal(1, "APU-P4 expected error timed out");
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic issue_expect_corpus_error(
|
||||
input logic [63:0] instruction_i, input logic [31:0] source0_i, input logic [31:0] source1_i,
|
||||
input logic [31:0] destination_i, input logic [5:0] expected_code_i,
|
||||
input logic [3:0] expected_stage_i, input logic [7:0] expected_reason_i,
|
||||
input logic check_latency_i, input logic [31:0] maximum_cycles_i,
|
||||
input logic check_no_memory_request_i);
|
||||
begin
|
||||
logic [31:0] s_request_count_before;
|
||||
s_request_count_before = s_dispatcher_request_count_q;
|
||||
issue_expect_error(instruction_i, source0_i, source1_i, destination_i, expected_code_i,
|
||||
expected_stage_i, expected_reason_i);
|
||||
if (check_latency_i && (result_cycles > maximum_cycles_i)) begin
|
||||
$fatal(1, "APU-P4 fault latency exceeded bound actual=%0d bound=%0d instruction=%016x",
|
||||
result_cycles, maximum_cycles_i, instruction_i);
|
||||
end
|
||||
if (check_no_memory_request_i &&
|
||||
(s_dispatcher_request_count_q != s_request_count_before)) begin
|
||||
$fatal(1, "APU-P4 rejected local operation reached SRAM instruction=%016x", instruction_i);
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
logic [31:0] observed, cycles;
|
||||
requant_vector_count = 0;
|
||||
if ($value$plusargs("VECTOR_COUNT=%d", requant_vector_count)) begin
|
||||
if (!$value$plusargs("VECTORS=%s", requant_vector_path)) begin
|
||||
$fatal(1, "APU-P4 vector path missing");
|
||||
end
|
||||
$readmemh(requant_vector_path, requant_vectors);
|
||||
end
|
||||
flush_i = 1'b0;
|
||||
epoch_clear_i = 1'b0;
|
||||
image_valid_i = 1'b0;
|
||||
req_valid = 1'b0;
|
||||
instruction = 64'd0;
|
||||
source0 = 32'd0;
|
||||
source1 = 32'd0;
|
||||
destination = 32'd0;
|
||||
input_valid = 1'b0;
|
||||
input_data = 41'd0;
|
||||
output_accept = 1'b0;
|
||||
loader_active = 1'b0;
|
||||
loader_req = 1'b0;
|
||||
loader_addr = 17'd0;
|
||||
loader_data = 32'd0;
|
||||
loader_strb = 4'd0;
|
||||
inject_active = 1'b0;
|
||||
inject_req = 1'b0;
|
||||
inject_write = 1'b0;
|
||||
inject_addr = 17'd0;
|
||||
inject_data = 32'd0;
|
||||
inject_strb = 4'd0;
|
||||
|
||||
hard_reset();
|
||||
publish_table_word(17'h00000, 32'h0001_0001);
|
||||
image_valid_i = 1'b1;
|
||||
epoch_clear_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
epoch_clear_i = 1'b0;
|
||||
|
||||
inject_word(17'h1a000, 32'h1357_9bdf);
|
||||
read_word(17'h1a000, observed);
|
||||
if (observed != 32'h1357_9bdf) $fatal(1, "APU-P4 internal-bank mapping mismatch");
|
||||
|
||||
issue(encode_instruction(4'd4, 4'd1, 4'd0, 4'd0, 4'd1, 8'd0, 32'd0), 32'd0, 32'ha5a5_5a5a,
|
||||
32'd0, observed, cycles);
|
||||
issue(encode_instruction(4'd4, 4'd0, 4'd2, 4'd0, 4'd0, 8'd0, 32'd0), 32'd0, 32'd0, 32'd0,
|
||||
observed, cycles);
|
||||
if ((observed != 32'ha5a5_5a5a) || (cycles != 32'd2)) begin
|
||||
$fatal(1, "APU-P4 local load/store mismatch");
|
||||
end
|
||||
|
||||
push_input({1'b0, 5'd0, 3'd4, 32'hdead_beef});
|
||||
issue(encode_instruction(4'd4, 4'd5, 4'd4, 4'd0, 4'd0, 8'd0, 32'd0), 32'd0, 32'd0, 32'd0,
|
||||
observed, cycles);
|
||||
if ((observed != 32'hdead_beef) || (result_data[1] != 32'd4)) begin
|
||||
$fatal(1, "APU-P4 FIFO pop mismatch");
|
||||
end
|
||||
issue(encode_instruction(4'd4, 4'd6, 4'd0, 4'd4, 4'd5, 8'd0, 32'd0), 32'h0102_0304,
|
||||
32'h0000_0104, 32'd0, observed, cycles);
|
||||
if (!output_valid || (output_data != {1'b1, 5'd0, 3'd4, 32'h0102_0304})) begin
|
||||
$fatal(1, "APU-P4 FIFO push mismatch");
|
||||
end
|
||||
output_accept = 1'b1;
|
||||
@(posedge clk_i);
|
||||
output_accept = 1'b0;
|
||||
|
||||
push_input({1'b1, 5'd0, 3'd4, 32'h4433_2211});
|
||||
issue(encode_instruction(4'd2, 4'd0, 4'd0, 4'd0, 4'd0, 8'd0, 32'd16), 32'd0, 32'd0, 32'd0,
|
||||
observed, cycles);
|
||||
if ((observed != 32'd32) || (cycles != 32'd3)) $fatal(1, "APU-P4 REFILL mismatch");
|
||||
issue(encode_instruction(4'd2, 4'd1, 4'd1, 4'd0, 4'd0, 8'd0, 32'd8), 32'd0, 32'd0, 32'd0,
|
||||
observed, cycles);
|
||||
if (observed != 32'h11) $fatal(1, "APU-P4 PEEK mismatch");
|
||||
issue(encode_instruction(4'd2, 4'd2, 4'd1, 4'd0, 4'd0, 8'd0, 32'd8), 32'd0, 32'd0, 32'd0,
|
||||
observed, cycles);
|
||||
if (observed != 32'h11) $fatal(1, "APU-P4 GET mismatch");
|
||||
issue(encode_instruction(4'd2, 4'd5, 4'd2, 4'd3, 4'd4, 8'd16, 32'd2), 32'h3344, 32'hffff, 32'd0,
|
||||
observed, cycles);
|
||||
if ((observed != 32'd1) || (cycles != 32'd4)) $fatal(1, "APU-P4 FRAME_SYNC mismatch");
|
||||
issue(encode_instruction(4'd2, 4'd3, 4'd0, 4'd0, 4'd0, 8'd0, 32'd4), 32'd0, 32'd0, 32'd0,
|
||||
observed, cycles);
|
||||
issue(encode_instruction(4'd2, 4'd4, 4'd0, 4'd0, 4'd0, 8'd0, 32'd0), 32'd0, 32'd0, 32'd0,
|
||||
observed, cycles);
|
||||
issue(encode_instruction(4'd2, 4'd6, 4'd5, 4'd6, 4'd7, 8'd0, 32'd0), 32'd0, 32'h31, 32'd0,
|
||||
observed, cycles);
|
||||
if (observed != 32'h97) $fatal(1, "APU-P4 CRC8 mismatch");
|
||||
issue(encode_instruction(4'd2, 4'd7, 4'd5, 4'd6, 4'd7, 8'd0, 32'd0), 32'd0, 32'h31, 32'd0,
|
||||
observed, cycles);
|
||||
if (observed != 32'h80a5) $fatal(1, "APU-P4 CRC16 mismatch");
|
||||
|
||||
flush_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
flush_i = 1'b0;
|
||||
read_word(17'h00000, observed);
|
||||
if (observed != 32'h0001_0001) $fatal(1, "APU-P4 table preload mismatch");
|
||||
push_input({1'b1, 5'd0, 3'd1, 32'h0000_0000});
|
||||
issue(encode_instruction(4'd3, 4'd0, 4'd2, 4'd0, 4'd1, 8'd1, 32'd0), 32'd0, 32'd1, 32'd0,
|
||||
observed, cycles);
|
||||
if (observed != 32'd1) begin
|
||||
$fatal(1, "APU-P4 Huffman mismatch observed=%08x symbol=%08x memory=%08x", observed,
|
||||
u_dut.u_entropy_engine.s_symbol_q, memory_read_data);
|
||||
end
|
||||
|
||||
flush_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
flush_i = 1'b0;
|
||||
for (int index = 0; index < 32; index++) begin
|
||||
publish_table_word(17'(index * 4), 32'((5 << 16) | index));
|
||||
end
|
||||
push_input({1'b1, 5'd0, 3'd1, 32'h0000_00f8});
|
||||
issue(encode_instruction(4'd3, 4'd0, 4'd2, 4'd0, 4'd1, 8'd5, 32'd0), 32'd0, 32'd32, 32'd0,
|
||||
observed, cycles);
|
||||
if ((observed != 32'd31) || (cycles != 32'd58)) begin
|
||||
$fatal(1, "APU-P4 32-entry Huffman mismatch observed=%0d cycles=%0d", observed, cycles);
|
||||
end
|
||||
|
||||
flush_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
flush_i = 1'b0;
|
||||
publish_table_word(17'h00000, 32'h0001_0023);
|
||||
push_input({1'b1, 5'd0, 3'd1, 32'h0000_0000});
|
||||
issue(encode_instruction(4'd3, 4'd1, 4'd2, 4'd0, 4'd1, 8'd1, 32'd0), 32'd0, 32'd1, 32'd0,
|
||||
observed, cycles);
|
||||
if ((observed != 32'd2) || (result_data[1] != 32'd3)) $fatal(1, "APU-P4 Huffman pair mismatch");
|
||||
|
||||
flush_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
flush_i = 1'b0;
|
||||
publish_table_word(17'h00000, 32'h0001_000a);
|
||||
push_input({1'b1, 5'd0, 3'd1, 32'h0000_0000});
|
||||
issue(encode_instruction(4'd3, 4'd2, 4'd2, 4'd0, 4'd1, 8'd1, 32'd0), 32'd0, 32'd1, 32'd0,
|
||||
observed, cycles);
|
||||
if ((observed != 32'd1) || (result_data[1] != 32'd0) ||
|
||||
(result_data[2] != 32'd1) || (result_data[3] != 32'd0)) begin
|
||||
$fatal(1, "APU-P4 Huffman quad mismatch");
|
||||
end
|
||||
|
||||
flush_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
flush_i = 1'b0;
|
||||
push_input({1'b1, 5'd0, 3'd1, 32'h0000_0020});
|
||||
issue(encode_instruction(4'd3, 4'd3, 4'd2, 4'd0, 4'd0, 8'd0, 32'd8), 32'd0, 32'd0, 32'd0,
|
||||
observed, cycles);
|
||||
if (observed != 32'd2) $fatal(1, "APU-P4 unary mismatch");
|
||||
|
||||
flush_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
flush_i = 1'b0;
|
||||
push_input({1'b1, 5'd0, 3'd1, 32'h0000_0040});
|
||||
issue(encode_instruction(4'd3, 4'd4, 4'd2, 4'd0, 4'd1, 8'd0, 32'd0), 32'd1, 32'd0, 32'd0,
|
||||
observed, cycles);
|
||||
if (observed != 32'd1) $fatal(1, "APU-P4 Rice4 mismatch");
|
||||
|
||||
flush_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
flush_i = 1'b0;
|
||||
push_input({1'b1, 5'd0, 3'd1, 32'h0000_0040});
|
||||
issue(encode_instruction(4'd3, 4'd5, 4'd2, 4'd0, 4'd1, 8'd0, 32'd0), 32'd1, 32'd0, 32'd0,
|
||||
observed, cycles);
|
||||
if (observed != 32'd1) $fatal(1, "APU-P4 Rice5 mismatch");
|
||||
|
||||
flush_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
flush_i = 1'b0;
|
||||
push_input({1'b1, 5'd0, 3'd1, 32'h0000_00c0});
|
||||
issue(encode_instruction(4'd3, 4'd6, 4'd2, 4'd0, 4'd1, 8'd0, 32'd0), 32'd4, 32'd1, 32'd0,
|
||||
observed, cycles);
|
||||
if (observed != 32'hffff_fffb) $fatal(1, "APU-P4 sign restore mismatch");
|
||||
|
||||
issue_expect_error(encode_instruction(4'd4, 4'd6, 4'd0, 4'd0, 4'd1, 8'd0, 32'd0), 32'h1234_5678,
|
||||
32'd0, 32'd0, `APB4_APU__ERROR_CODE_OVERFLOW,
|
||||
`APB4_APU__ERROR_STAGE_RESAMPLER, 8'd9);
|
||||
if (output_valid) $fatal(1, "APU-P4 rejected FIFO push mutated output FIFO");
|
||||
issue_expect_error(encode_instruction(4'd4, 4'd0, 4'd0, 4'd0, 4'd0, 8'd0, 32'd0), 32'd1, 32'd0,
|
||||
32'd0, `APB4_APU__ERROR_CODE_SEQUENCER, `APB4_APU__ERROR_STAGE_LIFECYCLE,
|
||||
8'd5);
|
||||
issue_expect_error(encode_instruction(4'd3, 4'd0, 4'd0, 4'd0, 4'd1, 8'd1, 32'd0), 32'h1800,
|
||||
32'd1, 32'd0, `APB4_APU__ERROR_CODE_SEQUENCER,
|
||||
`APB4_APU__ERROR_STAGE_LIFECYCLE, 8'd5);
|
||||
|
||||
flush_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
flush_i = 1'b0;
|
||||
push_input({1'b1, 5'd0, 3'd1, 32'h0000_0000});
|
||||
issue_expect_error(encode_instruction(4'd2, 4'd2, 4'd0, 4'd0, 4'd0, 8'd0, 32'd16), 32'd0, 32'd0,
|
||||
32'd0, `APB4_APU__ERROR_CODE_TRUNCATED, `APB4_APU__ERROR_STAGE_BITSTREAM,
|
||||
8'd9);
|
||||
|
||||
flush_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
flush_i = 1'b0;
|
||||
push_input({1'b1, 5'd0, 3'd1, 32'h0000_0000});
|
||||
issue_expect_error(encode_instruction(4'd2, 4'd5, 4'd0, 4'd1, 4'd2, 8'd16, 32'd1), 32'hffff,
|
||||
32'hffff, 32'd0, `APB4_APU__ERROR_CODE_MALFORMED,
|
||||
`APB4_APU__ERROR_STAGE_BITSTREAM, 8'd9);
|
||||
|
||||
flush_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
flush_i = 1'b0;
|
||||
push_input({1'b1, 5'd0, 3'd1, 32'h0000_0000});
|
||||
issue_expect_error(encode_instruction(4'd3, 4'd3, 4'd0, 4'd0, 4'd0, 8'd0, 32'd1), 32'd0, 32'd0,
|
||||
32'd0, `APB4_APU__ERROR_CODE_DECODE, `APB4_APU__ERROR_STAGE_ENTROPY, 8'd9);
|
||||
|
||||
inject_word(17'h02100, 32'd100);
|
||||
inject_word(17'h02140, 32'h4000_0000);
|
||||
inject_word(17'h02144, 32'd3);
|
||||
issue_expect_error(encode_instruction(4'd5, 4'd9, 4'd2, 4'd0, 4'd1, 8'd0, 32'd1), 32'h100,
|
||||
32'h140, 32'h180, `APB4_APU__ERROR_CODE_RECONSTRUCTION,
|
||||
`APB4_APU__ERROR_STAGE_RESAMPLER, 8'd9);
|
||||
|
||||
inject_word(17'h02100, 32'd1000);
|
||||
inject_word(17'h02140, 32'h4000_0000);
|
||||
inject_word(17'h02180, 32'h0000_0140);
|
||||
issue_expect_error(encode_instruction(4'd5, 4'd0, 4'd2, 4'd0, 4'd1, 8'h80, 32'd1), 32'h101,
|
||||
32'h180, 32'h1c0, `APB4_APU__ERROR_CODE_SEQUENCER,
|
||||
`APB4_APU__ERROR_STAGE_LIFECYCLE, 8'd5);
|
||||
issue_expect_error(encode_instruction(4'd5, 4'd0, 4'd2, 4'd0, 4'd1, 8'h80, 32'd1), 32'h100,
|
||||
32'h181, 32'h1c0, `APB4_APU__ERROR_CODE_SEQUENCER,
|
||||
`APB4_APU__ERROR_STAGE_LIFECYCLE, 8'd5);
|
||||
issue_expect_error(encode_instruction(4'd5, 4'd0, 4'd2, 4'd0, 4'd1, 8'h80, 32'd1), 32'h100,
|
||||
32'h180, 32'h1c1, `APB4_APU__ERROR_CODE_SEQUENCER,
|
||||
`APB4_APU__ERROR_STAGE_LIFECYCLE, 8'd5);
|
||||
inject_word(17'h02180, 32'h0000_0141);
|
||||
issue_expect_error(encode_instruction(4'd5, 4'd0, 4'd2, 4'd0, 4'd1, 8'h80, 32'd1), 32'h100,
|
||||
32'h180, 32'h1c0, `APB4_APU__ERROR_CODE_SEQUENCER,
|
||||
`APB4_APU__ERROR_STAGE_LIFECYCLE, 8'd5);
|
||||
inject_word(17'h02180, 32'h0000_0140);
|
||||
issue(encode_instruction(4'd5, 4'd0, 4'd2, 4'd0, 4'd1, 8'h80, 32'd1), 32'h100, 32'h180, 32'h1c0,
|
||||
observed, cycles);
|
||||
if ((observed != 32'd1) || (cycles != 32'd12) || !result_kernel || !kernel_done) begin
|
||||
$fatal(1, "APU-P4 REQUANT completion mismatch result=%0d cycles=%0d kernel=%0d done=%0d",
|
||||
observed, cycles, result_kernel, kernel_done);
|
||||
end
|
||||
read_word(17'h021c0, observed);
|
||||
if (observed != 32'd1000) $fatal(1, "APU-P4 REQUANT data mismatch");
|
||||
|
||||
inject_word(17'h02200, 32'd10);
|
||||
inject_word(17'h02240, 32'd3);
|
||||
inject_word(17'h02280, 32'h0000_0240);
|
||||
issue(encode_instruction(4'd5, 4'd1, 4'd2, 4'd0, 4'd1, 8'd1, 32'd1), 32'h200, 32'h280, 32'h300,
|
||||
observed, cycles);
|
||||
if ((observed != 32'd2) || (cycles != 32'd12)) $fatal(1, "APU-P4 STEREO mismatch");
|
||||
read_word(17'h02304, observed);
|
||||
if (observed != 32'd7) $fatal(1, "APU-P4 STEREO output mismatch");
|
||||
|
||||
for (int index = 0; index < 1536; index++) begin
|
||||
publish_table_word(17'(index * 4), 32'd0);
|
||||
end
|
||||
for (int row = 0; row < 6; row++) begin
|
||||
publish_table_word(17'(((row * 6) + row) * 4), 32'h4000_0000);
|
||||
inject_word(17'(17'h02400 + (row * 4)), 32'(row + 1));
|
||||
end
|
||||
inject_word(17'h02480, 32'h8000_0000);
|
||||
issue(encode_instruction(4'd5, 4'd2, 4'd2, 4'd0, 4'd1, 8'd0, 32'd1), 32'h400, 32'h480, 32'h500,
|
||||
observed, cycles);
|
||||
if ((observed != 32'd12) || (cycles != 32'd102)) $fatal(1, "APU-P4 IMDCT6 mismatch");
|
||||
read_word(17'h02514, observed);
|
||||
if (observed != 32'd6) $fatal(1, "APU-P4 IMDCT6 output mismatch");
|
||||
|
||||
for (int index = 0; index < 648; index++) begin
|
||||
publish_table_word(17'(index * 4), 32'd0);
|
||||
end
|
||||
for (int row = 0; row < 18; row++) begin
|
||||
publish_table_word(17'(((row * 18) + row) * 4), 32'h4000_0000);
|
||||
inject_word(17'(17'h02600 + (row * 4)), 32'(row + 1));
|
||||
end
|
||||
inject_word(17'h02680, 32'h8000_0000);
|
||||
issue(encode_instruction(4'd5, 4'd3, 4'd2, 4'd0, 4'd1, 8'd0, 32'd1), 32'h600, 32'h680, 32'h700,
|
||||
observed, cycles);
|
||||
if ((observed != 32'd36) || (cycles != 32'd714)) $fatal(1, "APU-P4 IMDCT18 mismatch");
|
||||
read_word(17'h02744, observed);
|
||||
if (observed != 32'd18) $fatal(1, "APU-P4 IMDCT18 output mismatch");
|
||||
|
||||
for (int index = 0; index < 1536; index++) begin
|
||||
publish_table_word(17'(index * 4), 32'd0);
|
||||
end
|
||||
for (int row = 0; row < 32; row++) begin
|
||||
publish_table_word(17'(((row * 32) + row) * 4), 32'h4000_0000);
|
||||
publish_table_word(17'(17'h01000 + (row * 4)), 32'h4000_0000);
|
||||
inject_word(17'(17'h02000 + (row * 4)), 32'(row + 1));
|
||||
end
|
||||
for (int index = 0; index < 512; index++) begin
|
||||
inject_word(17'(17'h03000 + (index * 4)), 32'd0);
|
||||
end
|
||||
inject_word(17'h02800, 32'h8000_0000);
|
||||
inject_word(17'h02804, 32'h8000_1000);
|
||||
inject_word(17'h02808, 32'h0000_1000);
|
||||
inject_word(17'h0280c, 32'd0);
|
||||
issue(encode_instruction(4'd5, 4'd4, 4'd2, 4'd0, 4'd1, 8'd0, 32'd1), 32'h000, 32'h800, 32'h2000,
|
||||
observed, cycles);
|
||||
if ((observed != 32'd32) || (cycles != 32'd2312)) $fatal(1, "APU-P4 DCT32 mismatch");
|
||||
read_word(17'h0407c, observed);
|
||||
if (observed != 32'd32) $fatal(1, "APU-P4 DCT32 output mismatch");
|
||||
|
||||
inject_word(17'h04200, 32'd1);
|
||||
inject_word(17'h04204, 32'd1);
|
||||
inject_word(17'h04240, 32'd2);
|
||||
inject_word(17'h04244, 32'd1);
|
||||
inject_word(17'h04280, 32'h0000_2240);
|
||||
issue(encode_instruction(4'd5, 4'd5, 4'd2, 4'd0, 4'd1, 8'd2, 32'd2), 32'h2200, 32'h2280,
|
||||
32'h2300, observed, cycles);
|
||||
if ((observed != 32'd2) || (cycles != 32'd20)) $fatal(1, "APU-P4 FIXED mismatch");
|
||||
read_word(17'h04304, observed);
|
||||
if (observed != 32'd7) $fatal(1, "APU-P4 FIXED output mismatch");
|
||||
|
||||
inject_word(17'h04400, 32'd1);
|
||||
inject_word(17'h04404, 32'd1);
|
||||
inject_word(17'h04440, 32'd1);
|
||||
inject_word(17'h04444, 32'd1);
|
||||
inject_word(17'h04480, 32'd1);
|
||||
inject_word(17'h04484, 32'd0);
|
||||
inject_word(17'h044c0, 32'h0000_2440);
|
||||
inject_word(17'h044c4, 32'h0000_2480);
|
||||
inject_word(17'h044c8, 32'd0);
|
||||
issue(encode_instruction(4'd5, 4'd6, 4'd2, 4'd0, 4'd1, 8'd2, 32'd2), 32'h2400, 32'h24c0,
|
||||
32'h2500, observed, cycles);
|
||||
if ((observed != 32'd2) || (cycles != 32'd26)) $fatal(1, "APU-P4 LPC mismatch");
|
||||
read_word(17'h04504, observed);
|
||||
if (observed != 32'd4) $fatal(1, "APU-P4 LPC output mismatch");
|
||||
|
||||
inject_word(17'h04600, 32'd10);
|
||||
inject_word(17'h04640, 32'd3);
|
||||
inject_word(17'h04680, 32'h0000_2640);
|
||||
issue(encode_instruction(4'd5, 4'd7, 4'd2, 4'd0, 4'd1, 8'd1, 32'd1), 32'h2600, 32'h2680,
|
||||
32'h2700, observed, cycles);
|
||||
if (observed != 32'd2) $fatal(1, "APU-P4 DECORRELATE mismatch");
|
||||
read_word(17'h04704, observed);
|
||||
if (observed != 32'd7) $fatal(1, "APU-P4 DECORRELATE output mismatch");
|
||||
|
||||
for (int profile = 0; profile < 16; profile++) begin
|
||||
flush_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
flush_i = 1'b0;
|
||||
for (int index = 0; index < 17; index++) begin
|
||||
inject_word(17'(17'h048e4 + (index * 4)), 32'd0);
|
||||
end
|
||||
inject_word(17'h04a00, 32'h8000_0000);
|
||||
inject_word(17'h04a04, 32'h8000_0800);
|
||||
inject_word(17'h04a08, 32'h8000_1000);
|
||||
inject_word(17'h04a0c, 32'd0);
|
||||
inject_word(17'h04a10, 32'd0);
|
||||
inject_word(17'h04a14, 32'd0);
|
||||
inject_word(17'h04a18, 32'd0);
|
||||
inject_word(17'h04a1c, 32'd1);
|
||||
issue(encode_instruction(4'd5, 4'd8, 4'd2, 4'd0, 4'd1, 8'(profile), 32'd1), 32'h2900,
|
||||
32'h2a00, 32'h2b00, observed, cycles);
|
||||
if (observed != expected_resample_frames(4'(profile))) begin
|
||||
$fatal(1, "APU-P4 RESAMPLE profile %0d mismatch result=%0d", profile, observed);
|
||||
end
|
||||
end
|
||||
issue_expect_error(encode_instruction(4'd5, 4'd8, 4'd2, 4'd0, 4'd1, 8'd14, 32'd1), 32'h2900,
|
||||
32'h2a00, 32'h2b00, `APB4_APU__ERROR_CODE_RECONSTRUCTION,
|
||||
`APB4_APU__ERROR_STAGE_RESAMPLER, 8'd9);
|
||||
|
||||
inject_word(17'h04c00, 32'd100);
|
||||
inject_word(17'h04c40, 32'h4000_0000);
|
||||
inject_word(17'h04c44, 32'd1);
|
||||
issue(encode_instruction(4'd5, 4'd9, 4'd2, 4'd0, 4'd1, 8'd4, 32'd1), 32'h2c00, 32'h2c40,
|
||||
32'h2d00, observed, cycles);
|
||||
if ((observed != 32'd4) || (cycles != 32'd18)) $fatal(1, "APU-P4 PCM_PACK mismatch");
|
||||
read_word(17'h04d00, observed);
|
||||
if (observed != 32'h0064_0064) $fatal(1, "APU-P4 PCM_PACK output mismatch");
|
||||
|
||||
`ifdef APU_P4_SHARED_CORPUS
|
||||
`include "apu_p4_corpus_generated.svh"
|
||||
`endif
|
||||
|
||||
for (int vector_index = 0; vector_index < requant_vector_count; vector_index++) begin
|
||||
inject_word(17'h02e00, requant_vectors[(vector_index*4)]);
|
||||
inject_word(17'h02e40, requant_vectors[(vector_index*4)+1]);
|
||||
inject_word(17'h02e80, 32'h0000_0e40);
|
||||
issue(encode_instruction(
|
||||
4'd5, 4'd0, 4'd2, 4'd0, 4'd1, requant_vectors[(vector_index*4)+2][7:0], 32'd1),
|
||||
32'h0e00, 32'h0e80, 32'h0ec0, observed, cycles);
|
||||
read_word(17'h02ec0, observed);
|
||||
if (observed != requant_vectors[(vector_index*4)+3]) begin
|
||||
$fatal(1, "APU-P4 requant differential mismatch vector=%0d got=%08x expected=%08x",
|
||||
vector_index, observed, requant_vectors[(vector_index*4)+3]);
|
||||
end
|
||||
end
|
||||
|
||||
$display("APU_P4_PRIMITIVES_PASS");
|
||||
$finish;
|
||||
end
|
||||
|
||||
logic s_unused;
|
||||
assign s_unused = memory_ready ^ input_exhausted ^ input_ready ^ output_ready ^
|
||||
dispatcher_busy ^ ^result_dst ^ ^result_words;
|
||||
endmodule
|
||||
@@ -0,0 +1,380 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
`include "apu_define.svh"
|
||||
|
||||
module apu_p4_sequencer_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic soft_reset_i, resource_reset_i, abort_i, counter_clear_i, launch_i;
|
||||
logic store_loader_active, store_loader_write;
|
||||
logic [10:0] store_loader_addr;
|
||||
logic [63:0] store_loader_data, store_loader_read_data;
|
||||
logic store_loader_valid;
|
||||
logic fetch, fetch_valid;
|
||||
logic [10:0] fetch_addr;
|
||||
logic [63:0] fetch_data;
|
||||
logic [2:0][10:0] entry_pc, entry_first, entry_last;
|
||||
logic [2:0][15:0] entry_max_loop;
|
||||
logic [2:0][23:0] entry_max_retired;
|
||||
logic [2:0][16:0] entry_scratch_base, entry_scratch_bytes;
|
||||
logic [2:0][31:0] entry_primitive_mask;
|
||||
logic [2:0][15:0] entry_table_offset, entry_table_bytes;
|
||||
logic primitive_req_valid, primitive_req_ready, primitive_result_valid;
|
||||
logic [63:0] primitive_instruction;
|
||||
logic [31:0] primitive_source0, primitive_source1, primitive_destination;
|
||||
logic [3:0] primitive_result_dst;
|
||||
logic [3:0][31:0] primitive_result_data;
|
||||
logic [2:0] primitive_result_words;
|
||||
logic primitive_result_kernel, primitive_error, primitive_kernel_done, primitive_busy;
|
||||
logic [5:0] primitive_error_code;
|
||||
logic [3:0] primitive_error_stage;
|
||||
logic [7:0] primitive_error_reason;
|
||||
logic primitive_input_exhausted, primitive_input_ready, primitive_output_ready;
|
||||
logic memory_req, memory_write, memory_valid, memory_error;
|
||||
logic [16:0] memory_addr;
|
||||
logic [31:0] memory_write_data, memory_read_data;
|
||||
logic [3:0] memory_strb;
|
||||
logic inject_active, inject_req, inject_write;
|
||||
logic [16:0] inject_addr;
|
||||
logic [31:0] inject_data;
|
||||
logic [ 3:0] inject_strb;
|
||||
logic codec_req, codec_write;
|
||||
logic [16:0] codec_addr;
|
||||
logic [31:0] codec_data;
|
||||
logic [ 3:0] codec_strb;
|
||||
logic [31:0] status, retired;
|
||||
logic [15:0][31:0] gpr;
|
||||
logic trapped, trap_event, abort_done, fault_valid, sequencer_idle;
|
||||
logic [5:0] fault_code;
|
||||
logic [3:0] fault_stage;
|
||||
logic [1:0] fault_resp;
|
||||
logic [7:0] fault_index;
|
||||
logic [31:0] fault_addr, fault_detail;
|
||||
logic [63:0] perf_retired;
|
||||
logic [16:0] active_scratch_base, active_scratch_bytes;
|
||||
logic [31:0] active_primitive_mask;
|
||||
logic [15:0] active_table_offset, active_table_bytes;
|
||||
logic launch_epoch;
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (rst_n_i && primitive_busy && primitive_req_valid &&
|
||||
(primitive_instruction[63:60] != `APB4_APU__MC_CLASS_KERNEL)) begin
|
||||
$fatal(1, "APU-P4 issued a second primitive while the kernel owned local SRAM");
|
||||
end
|
||||
end
|
||||
|
||||
assign codec_req = inject_active ? inject_req : memory_req;
|
||||
assign codec_write = inject_active ? inject_write : memory_write;
|
||||
assign codec_addr = inject_active ? inject_addr : memory_addr;
|
||||
assign codec_data = inject_active ? inject_data : memory_write_data;
|
||||
assign codec_strb = inject_active ? inject_strb : memory_strb;
|
||||
|
||||
apu_control_store u_control_store (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.loader_active_i(store_loader_active),
|
||||
.loader_read_i (1'b0),
|
||||
.loader_write_i (store_loader_write),
|
||||
.loader_addr_i (store_loader_addr),
|
||||
.loader_data_i (store_loader_data),
|
||||
.loader_data_o (store_loader_read_data),
|
||||
.loader_valid_o (store_loader_valid),
|
||||
.image_valid_i (1'b1),
|
||||
.fetch_i (fetch),
|
||||
.fetch_addr_i (fetch_addr),
|
||||
.fetch_data_o (fetch_data),
|
||||
.fetch_valid_o (fetch_valid)
|
||||
);
|
||||
|
||||
apu_local_sram u_local_sram (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.image_valid_i (1'b1),
|
||||
.table_bytes_i (16'd0),
|
||||
.epoch_clear_i (launch_epoch || soft_reset_i || resource_reset_i || abort_i),
|
||||
.loader_active_i (1'b0),
|
||||
.loader_req_i (1'b0),
|
||||
.loader_addr_i (17'd0),
|
||||
.loader_data_i (32'd0),
|
||||
.loader_strb_i (4'd0),
|
||||
.loader_ready_o (),
|
||||
.codec_req_i (codec_req),
|
||||
.codec_write_i (codec_write),
|
||||
.codec_addr_i (codec_addr),
|
||||
.codec_data_i (codec_data),
|
||||
.codec_strb_i (codec_strb),
|
||||
.codec_ready_o (),
|
||||
.codec_data_o (memory_read_data),
|
||||
.codec_valid_o (memory_valid),
|
||||
.codec_access_err_o(memory_error)
|
||||
);
|
||||
|
||||
apu_primitive_dispatcher u_dispatcher (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.flush_i (launch_epoch || soft_reset_i || resource_reset_i || abort_i),
|
||||
.req_valid_i (primitive_req_valid),
|
||||
.req_ready_o (primitive_req_ready),
|
||||
.instruction_i (primitive_instruction),
|
||||
.source0_i (primitive_source0),
|
||||
.source1_i (primitive_source1),
|
||||
.destination_i (primitive_destination),
|
||||
.scratch_base_i (active_scratch_base),
|
||||
.scratch_bytes_i (active_scratch_bytes),
|
||||
.table_offset_i (active_table_offset),
|
||||
.table_bytes_i (active_table_bytes),
|
||||
.result_valid_o (primitive_result_valid),
|
||||
.result_dst_o (primitive_result_dst),
|
||||
.result_data_o (primitive_result_data),
|
||||
.result_words_o (primitive_result_words),
|
||||
.result_kernel_o (primitive_result_kernel),
|
||||
.error_o (primitive_error),
|
||||
.error_code_o (primitive_error_code),
|
||||
.error_stage_o (primitive_error_stage),
|
||||
.error_reason_o (primitive_error_reason),
|
||||
.cycles_o (),
|
||||
.kernel_done_o (primitive_kernel_done),
|
||||
.busy_o (primitive_busy),
|
||||
.input_exhausted_o(primitive_input_exhausted),
|
||||
.input_ready_o (primitive_input_ready),
|
||||
.output_ready_o (primitive_output_ready),
|
||||
.input_count_o (),
|
||||
.output_count_o (),
|
||||
.kernel_busy_o (),
|
||||
.memory_req_o (memory_req),
|
||||
.memory_write_o (memory_write),
|
||||
.memory_addr_o (memory_addr),
|
||||
.memory_data_o (memory_write_data),
|
||||
.memory_strb_o (memory_strb),
|
||||
.memory_valid_i (memory_valid),
|
||||
.memory_data_i (memory_read_data),
|
||||
.memory_error_i (memory_error),
|
||||
.input_valid_i (1'b0),
|
||||
.input_data_i (41'd0),
|
||||
.input_accept_o (),
|
||||
.output_valid_o (),
|
||||
.output_data_o (),
|
||||
.output_accept_i (1'b0)
|
||||
);
|
||||
|
||||
apu_codec_sequencer #(
|
||||
.EnableP4(1'b1)
|
||||
) u_sequencer (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.soft_reset_i (soft_reset_i),
|
||||
.resource_reset_i (resource_reset_i),
|
||||
.counter_clear_i (counter_clear_i),
|
||||
.abort_i (abort_i),
|
||||
.launch_i (launch_i),
|
||||
.launch_entry_i (2'd0),
|
||||
.image_valid_i (1'b1),
|
||||
.timeout_i (32'd4096),
|
||||
.entry_pc_i (entry_pc),
|
||||
.entry_first_i (entry_first),
|
||||
.entry_last_i (entry_last),
|
||||
.entry_max_loop_i (entry_max_loop),
|
||||
.entry_max_retired_i (entry_max_retired),
|
||||
.entry_scratch_base_i (entry_scratch_base),
|
||||
.entry_scratch_bytes_i (entry_scratch_bytes),
|
||||
.entry_primitive_mask_i (entry_primitive_mask),
|
||||
.entry_table_offset_i (entry_table_offset),
|
||||
.entry_table_bytes_i (entry_table_bytes),
|
||||
.input_exhausted_i (primitive_input_exhausted),
|
||||
.input_ready_i (primitive_input_ready),
|
||||
.output_ready_i (primitive_output_ready),
|
||||
.kernel_done_i (primitive_kernel_done),
|
||||
.transport_idle_success_i (1'b1),
|
||||
.stall_i (1'b0),
|
||||
.cause_valid_i (1'b0),
|
||||
.cause_code_i (6'd0),
|
||||
.cause_stage_i (4'd0),
|
||||
.cause_resp_i (2'd0),
|
||||
.cause_index_i (8'd0),
|
||||
.cause_addr_i (32'd0),
|
||||
.cause_detail_i (32'd0),
|
||||
.primitive_req_valid_o (primitive_req_valid),
|
||||
.primitive_req_ready_i (primitive_req_ready),
|
||||
.primitive_instruction_o (primitive_instruction),
|
||||
.primitive_source0_o (primitive_source0),
|
||||
.primitive_source1_o (primitive_source1),
|
||||
.primitive_destination_o (primitive_destination),
|
||||
.primitive_result_valid_i (primitive_result_valid),
|
||||
.primitive_result_dst_i (primitive_result_dst),
|
||||
.primitive_result_data_i (primitive_result_data),
|
||||
.primitive_result_words_i (primitive_result_words),
|
||||
.primitive_result_kernel_i(primitive_result_kernel),
|
||||
.primitive_error_i (primitive_error),
|
||||
.primitive_error_code_i (primitive_error_code),
|
||||
.primitive_error_stage_i (primitive_error_stage),
|
||||
.primitive_error_reason_i (primitive_error_reason),
|
||||
.fetch_o (fetch),
|
||||
.fetch_addr_o (fetch_addr),
|
||||
.fetch_data_i (fetch_data),
|
||||
.fetch_valid_i (fetch_valid),
|
||||
.stat_o (status),
|
||||
.retired_o (retired),
|
||||
.gpr_o (gpr),
|
||||
.trapped_o (trapped),
|
||||
.trap_event_o (trap_event),
|
||||
.abort_done_o (abort_done),
|
||||
.fault_valid_o (fault_valid),
|
||||
.fault_code_o (fault_code),
|
||||
.fault_stage_o (fault_stage),
|
||||
.fault_resp_o (fault_resp),
|
||||
.fault_index_o (fault_index),
|
||||
.fault_addr_o (fault_addr),
|
||||
.fault_detail_o (fault_detail),
|
||||
.perf_retired_o (perf_retired),
|
||||
.active_scratch_base_o (active_scratch_base),
|
||||
.active_scratch_bytes_o (active_scratch_bytes),
|
||||
.active_primitive_mask_o (active_primitive_mask),
|
||||
.active_table_offset_o (active_table_offset),
|
||||
.active_table_bytes_o (active_table_bytes),
|
||||
.launch_epoch_o (launch_epoch),
|
||||
.idle_o (sequencer_idle)
|
||||
);
|
||||
|
||||
function automatic logic [63:0] encode_instruction(
|
||||
input logic [3:0] instruction_class_i, input logic [3:0] opcode_i, input logic [3:0] dst_i,
|
||||
input logic [3:0] src0_i, input logic [3:0] src1_i, input logic [7:0] aux_i,
|
||||
input logic [31:0] immediate_i);
|
||||
return {instruction_class_i, opcode_i, 4'd0, dst_i, src0_i, src1_i, aux_i, immediate_i};
|
||||
endfunction
|
||||
|
||||
task automatic load_instruction(input logic [10:0] address_i, input logic [63:0] data_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
store_loader_active = 1'b1;
|
||||
store_loader_write = 1'b1;
|
||||
store_loader_addr = address_i;
|
||||
store_loader_data = data_i;
|
||||
@(negedge clk_i);
|
||||
store_loader_write = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic inject_word(input logic [16:0] address_i, input logic [31:0] data_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
inject_active = 1'b1;
|
||||
inject_req = 1'b1;
|
||||
inject_write = 1'b1;
|
||||
inject_addr = address_i;
|
||||
inject_data = data_i;
|
||||
inject_strb = 4'hf;
|
||||
@(negedge clk_i);
|
||||
inject_active = 1'b0;
|
||||
inject_req = 1'b0;
|
||||
inject_write = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
soft_reset_i = 1'b0;
|
||||
resource_reset_i = 1'b0;
|
||||
abort_i = 1'b0;
|
||||
counter_clear_i = 1'b0;
|
||||
launch_i = 1'b0;
|
||||
store_loader_active = 1'b0;
|
||||
store_loader_write = 1'b0;
|
||||
store_loader_addr = 11'd0;
|
||||
store_loader_data = 64'd0;
|
||||
inject_active = 1'b0;
|
||||
inject_req = 1'b0;
|
||||
inject_write = 1'b0;
|
||||
inject_addr = 17'd0;
|
||||
inject_data = 32'd0;
|
||||
inject_strb = 4'd0;
|
||||
entry_pc = {11'd0, 11'd0, 11'd0};
|
||||
entry_first = {11'd0, 11'd0, 11'd0};
|
||||
entry_last = {11'd10, 11'd10, 11'd10};
|
||||
entry_max_loop = {16'd4, 16'd4, 16'd4};
|
||||
entry_max_retired = {24'd32, 24'd32, 24'd32};
|
||||
entry_scratch_base = {17'd0, 17'd0, 17'd0};
|
||||
entry_scratch_bytes = {17'h06000, 17'h06000, 17'h06000};
|
||||
entry_primitive_mask = {32'h0000_0070, 32'h0000_0070, 32'h0000_0070};
|
||||
entry_table_offset = {16'd0, 16'd0, 16'd0};
|
||||
entry_table_bytes = {16'd0, 16'd0, 16'd0};
|
||||
|
||||
rst_n_i = 1'b0;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
repeat (2) @(posedge clk_i);
|
||||
|
||||
load_instruction(11'd0, encode_instruction(4'd1, 4'd1, 4'd0, 4'd0, 4'd0, 8'd0, 32'h0000_0100));
|
||||
load_instruction(11'd1, encode_instruction(4'd1, 4'd1, 4'd1, 4'd0, 4'd0, 8'd0, 32'h0000_0180));
|
||||
load_instruction(11'd2, encode_instruction(4'd1, 4'd1, 4'd4, 4'd0, 4'd0, 8'd0, 32'h0000_01c0));
|
||||
load_instruction(11'd3, encode_instruction(4'd1, 4'd1, 4'd5, 4'd0, 4'd0, 8'd0, 32'h0000_0200));
|
||||
load_instruction(11'd4, encode_instruction(4'd1, 4'd1, 4'd6, 4'd0, 4'd0, 8'd0, 32'h1234_5678));
|
||||
load_instruction(11'd5, encode_instruction(4'd5, 4'd0, 4'd4, 4'd0, 4'd1, 8'h80, 32'd1));
|
||||
load_instruction(11'd6, encode_instruction(4'd1, 4'd1, 4'd7, 4'd0, 4'd0, 8'd0, 32'h0000_0055));
|
||||
load_instruction(11'd7, encode_instruction(4'd4, 4'd1, 4'd0, 4'd5, 4'd6, 8'd0, 32'd0));
|
||||
load_instruction(11'd8, encode_instruction(4'd0, 4'd8, 4'd0, 4'd0, 4'd0, 8'd1, 32'd0));
|
||||
load_instruction(11'd9, encode_instruction(4'd4, 4'd0, 4'd3, 4'd5, 4'd0, 8'd0, 32'd0));
|
||||
load_instruction(11'd10, encode_instruction(4'd0, 4'd1, 4'd0, 4'd0, 4'd0, 8'd0, 32'd0));
|
||||
|
||||
@(negedge clk_i);
|
||||
launch_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
launch_i = 1'b0;
|
||||
store_loader_active = 1'b0;
|
||||
inject_word(17'h00100, 32'd1000);
|
||||
inject_word(17'h00140, 32'h4000_0000);
|
||||
inject_word(17'h00180, 32'h0000_0140);
|
||||
for (int cycle = 0; cycle < 10000; cycle++) begin
|
||||
@(posedge clk_i);
|
||||
if (fault_valid || trapped) begin
|
||||
$fatal(
|
||||
1,
|
||||
"APU-P4 sequencer fault code=%0d detail=%08x status=%08x insn=%016x legal=%0d r5=%08x r6=%08x valid=%0d",
|
||||
fault_code, fault_detail, status, u_sequencer.s_instruction_q,
|
||||
apu_microcode_pkg::instruction_encoding_valid(u_sequencer.s_instruction_q), gpr[5],
|
||||
gpr[6], u_local_sram.s_mutable_valid_q[17'h00200>>2]);
|
||||
end
|
||||
if (sequencer_idle && !primitive_busy && (retired != 32'd0)) break;
|
||||
if (cycle == 9999) $fatal(1, "APU-P4 sequencer timed out");
|
||||
end
|
||||
if ((gpr[3] != 32'h1234_5678) || (gpr[4] != 32'd1) || (gpr[7] != 32'h55) ||
|
||||
(retired != 32'd11) || (perf_retired != 64'd11)) begin
|
||||
$fatal(1, "APU-P4 sequencer mismatch r3=%0d r4=%0d r7=%0d retired=%0d perf=%0d", gpr[3],
|
||||
gpr[4], gpr[7], retired, perf_retired);
|
||||
end
|
||||
|
||||
@(negedge clk_i);
|
||||
store_loader_active = 1'b1;
|
||||
launch_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
launch_i = 1'b0;
|
||||
store_loader_active = 1'b0;
|
||||
@(posedge clk_i);
|
||||
if (u_local_sram.s_mutable_valid_q[17'h00200>>2] ||
|
||||
u_dispatcher.u_kernel_engine.s_resample_profile_valid_q) begin
|
||||
$fatal(1, "APU-P4 launch did not clear the mutable-data epoch");
|
||||
end
|
||||
inject_word(17'h00100, 32'd2000);
|
||||
inject_word(17'h00140, 32'h4000_0000);
|
||||
inject_word(17'h00180, 32'h0000_0140);
|
||||
for (int cycle = 0; cycle < 10000; cycle++) begin
|
||||
@(posedge clk_i);
|
||||
if (fault_valid || trapped) begin
|
||||
$fatal(1, "APU-P4 second launch fault code=%0d detail=%08x", fault_code, fault_detail);
|
||||
end
|
||||
if (sequencer_idle && !primitive_busy && (retired != 32'd0)) break;
|
||||
if (cycle == 9999) $fatal(1, "APU-P4 second launch timed out");
|
||||
end
|
||||
if ((gpr[3] != 32'h1234_5678) || (gpr[4] != 32'd1) || (retired != 32'd11)) begin
|
||||
$fatal(1, "APU-P4 second launch result mismatch");
|
||||
end
|
||||
$display("APU_P4_SEQUENCER_PASS");
|
||||
$finish;
|
||||
end
|
||||
|
||||
logic s_unused;
|
||||
assign s_unused = store_loader_read_data[0] ^ store_loader_valid ^ trap_event ^ abort_done ^
|
||||
fault_stage[0] ^ fault_resp[0] ^ fault_index[0] ^ fault_addr[0] ^
|
||||
active_primitive_mask[0];
|
||||
endmodule
|
||||
@@ -191,8 +191,8 @@ module apu_reg_tb;
|
||||
s_reg_count = 0;
|
||||
add_register(`APB4_APU__IP_ID, AccessRo, 32'h4150_5530, 1'b0);
|
||||
add_register(`APB4_APU__IP_VERSION, AccessRo, 32'h0001_0000, 1'b0);
|
||||
add_register(`APB4_APU__CAPABILITY0, AccessRo, 32'h0000_0098, 1'b0);
|
||||
add_register(`APB4_APU__CAPABILITY1, AccessRo, 32'h0000_0010, 1'b0);
|
||||
add_register(`APB4_APU__CAPABILITY0, AccessRo, 32'h0000_0198, 1'b0);
|
||||
add_register(`APB4_APU__CAPABILITY1, AccessRo, 32'h0182_7010, 1'b0);
|
||||
add_register(`APB4_APU__COMMAND, AccessWo, 32'd0, 1'b1);
|
||||
add_register(`APB4_APU__STATUS, AccessRo, 32'h0000_0100, 1'b0);
|
||||
add_register(`APB4_APU__IRQ_STATE, AccessRw, 32'd0, 1'b0);
|
||||
|
||||
+43
-7
@@ -51,6 +51,14 @@ def test_apu_p1_apb_register_shell(tmp_path: Path) -> None:
|
||||
str(multimedia / "apu_stream_router.sv"),
|
||||
str(multimedia / "apu_control_store.sv"),
|
||||
str(multimedia / "apu_microcode_loader.sv"),
|
||||
str(multimedia / "apu_local_sram.sv"),
|
||||
str(multimedia / "apu_bitstream_engine.sv"),
|
||||
str(multimedia / "apu_entropy_engine.sv"),
|
||||
str(multimedia / "apu_reconstruction_engine.sv"),
|
||||
str(multimedia / "apu_transform_engine.sv"),
|
||||
str(multimedia / "apu_resampler.sv"),
|
||||
str(multimedia / "apu_kernel_engine.sv"),
|
||||
str(multimedia / "apu_primitive_dispatcher.sv"),
|
||||
str(multimedia / "apu_codec_sequencer.sv"),
|
||||
str(multimedia / "apu_reg.sv"),
|
||||
str(multimedia / "apb4_apu.sv"),
|
||||
@@ -128,6 +136,14 @@ def test_apu_p1_integrated_irq_ownership_topology(tmp_path: Path) -> None:
|
||||
str(multimedia / "apu_stream_router.sv"),
|
||||
str(multimedia / "apu_control_store.sv"),
|
||||
str(multimedia / "apu_microcode_loader.sv"),
|
||||
str(multimedia / "apu_local_sram.sv"),
|
||||
str(multimedia / "apu_bitstream_engine.sv"),
|
||||
str(multimedia / "apu_entropy_engine.sv"),
|
||||
str(multimedia / "apu_reconstruction_engine.sv"),
|
||||
str(multimedia / "apu_transform_engine.sv"),
|
||||
str(multimedia / "apu_resampler.sv"),
|
||||
str(multimedia / "apu_kernel_engine.sv"),
|
||||
str(multimedia / "apu_primitive_dispatcher.sv"),
|
||||
str(multimedia / "apu_codec_sequencer.sv"),
|
||||
str(multimedia / "apu_reg.sv"),
|
||||
str(multimedia / "apb4_apu.sv"),
|
||||
@@ -415,12 +431,12 @@ last:
|
||||
source_list = tmp_path / "apu_p3_microcode.fl"
|
||||
source_list.write_text(
|
||||
"\n".join(
|
||||
[
|
||||
"+define+SV_ASSRT_DISABLE",
|
||||
f"+incdir+{multimedia}",
|
||||
f"+incdir+{common}",
|
||||
str(ROOT / "rtl/tech/tc_sram.sv"),
|
||||
str(multimedia / "apu_microcode_pkg.sv"),
|
||||
[
|
||||
"+define+SV_ASSRT_DISABLE",
|
||||
f"+incdir+{multimedia}",
|
||||
f"+incdir+{common}",
|
||||
str(ROOT / "rtl/tech/tc_sram.sv"),
|
||||
str(multimedia / "apu_microcode_pkg.sv"),
|
||||
str(multimedia / "apu_control_store.sv"),
|
||||
str(multimedia / "apu_microcode_loader.sv"),
|
||||
str(multimedia / "apu_codec_sequencer.sv"),
|
||||
@@ -532,6 +548,14 @@ done:
|
||||
str(multimedia / "apu_stream_router.sv"),
|
||||
str(multimedia / "apu_control_store.sv"),
|
||||
str(multimedia / "apu_microcode_loader.sv"),
|
||||
str(multimedia / "apu_local_sram.sv"),
|
||||
str(multimedia / "apu_bitstream_engine.sv"),
|
||||
str(multimedia / "apu_entropy_engine.sv"),
|
||||
str(multimedia / "apu_reconstruction_engine.sv"),
|
||||
str(multimedia / "apu_transform_engine.sv"),
|
||||
str(multimedia / "apu_resampler.sv"),
|
||||
str(multimedia / "apu_kernel_engine.sv"),
|
||||
str(multimedia / "apu_primitive_dispatcher.sv"),
|
||||
str(multimedia / "apu_codec_sequencer.sv"),
|
||||
str(multimedia / "apu_reg.sv"),
|
||||
str(multimedia / "apb4_apu.sv"),
|
||||
@@ -575,7 +599,7 @@ done:
|
||||
assert "APU-P3 APB, DMA, loader integration passed" in result.stdout
|
||||
|
||||
|
||||
def test_apu_p3_product_scope_remains_coreless_and_fail_closed() -> None:
|
||||
def test_apu_p4_product_scope_remains_coreless_and_fail_closed() -> None:
|
||||
top = (ROOT / "rtl/ip/multimedia/apb4_apu.sv").read_text(encoding="utf-8")
|
||||
register_block = (ROOT / "rtl/ip/multimedia/apu_reg.sv").read_text(encoding="utf-8")
|
||||
product_filelist = (ROOT / "rtl/mini/filelist/ip.fl").read_text(encoding="utf-8")
|
||||
@@ -585,11 +609,23 @@ def test_apu_p3_product_scope_remains_coreless_and_fail_closed() -> None:
|
||||
assert "32'h0000_0029" in register_block
|
||||
assert "s_merged_write != 32'd0" in register_block
|
||||
assert "apu_interpreter" not in product_filelist
|
||||
assert "apu_primitives.py" not in product_filelist
|
||||
assert "apu_p4_primitives_tb" not in product_filelist
|
||||
assert "apu_p4_loader_tb" not in product_filelist
|
||||
assert "apu_p4_sequencer_tb" not in product_filelist
|
||||
assert "apu_p3_microcode_tb" not in product_filelist
|
||||
assert "VerificationCrcBypass" not in product_filelist
|
||||
assert ".VerificationCrcBypass" not in top
|
||||
assert "VerificationControlFlowBypass" not in product_filelist
|
||||
assert ".VerificationControlFlowBypass" not in top
|
||||
for production_module in (
|
||||
"apu_local_sram.sv",
|
||||
"apu_bitstream_engine.sv",
|
||||
"apu_entropy_engine.sv",
|
||||
"apu_kernel_engine.sv",
|
||||
"apu_primitive_dispatcher.sv",
|
||||
):
|
||||
assert production_module in product_filelist
|
||||
for forbidden in ("hazard3", "vexiiriscv", "rv32", "dsp_core"):
|
||||
assert forbidden not in top.lower()
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,4 +1,4 @@
|
||||
"""Keep the handwritten APU RTL and SDK register ABI synchronized through P3."""
|
||||
"""Keep the handwritten APU RTL, SDK, and P4 ISA definitions synchronized."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -72,7 +72,7 @@ def test_apu_register_matrix_covers_each_offset_once() -> None:
|
||||
assert set(covered) == set(expected)
|
||||
|
||||
|
||||
def test_apu_p3_tool_isa_matches_handwritten_rtl_and_c() -> None:
|
||||
def test_apu_p4_tool_isa_matches_handwritten_rtl_and_c() -> None:
|
||||
rtl = _rtl_values()
|
||||
manifest = abi_manifest()
|
||||
exact = {
|
||||
@@ -82,6 +82,10 @@ def test_apu_p3_tool_isa_matches_handwritten_rtl_and_c() -> None:
|
||||
"APUMC_ENTRY_BYTES": manifest["apumc"]["entry_bytes"],
|
||||
"APUMC_ENTRY_COUNT": manifest["apumc"]["entry_count"],
|
||||
"APUMC_MAX_INSTRUCTIONS": manifest["apumc"]["max_instructions"],
|
||||
"APUMC_P3_PRIMITIVE_MASK": manifest["p3_implemented_primitive_mask"],
|
||||
"APUMC_P4_PRIMITIVE_MASK": manifest["p4_implemented_primitive_mask"],
|
||||
"LOCAL_DATA_BYTES": manifest["p4_local_data_bytes"],
|
||||
"LOCAL_TABLE_SCRATCH_BYTES": manifest["p4_local_table_scratch_bytes"],
|
||||
}
|
||||
for name, value in manifest["classes"].items():
|
||||
exact[f"MC_CLASS_{name.upper()}"] = value
|
||||
|
||||
@@ -330,6 +330,7 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
|
||||
onchip_ram_filelist = tmp_path / "onchip_ram.fl"
|
||||
opipsram_filelist = tmp_path / "opipsram.fl"
|
||||
apu_filelist = tmp_path / "apu.fl"
|
||||
apu_primitives_filelist = tmp_path / "apu_primitives.fl"
|
||||
apu_loader_filelist = tmp_path / "apu_loader.fl"
|
||||
apu_sequencer_filelist = tmp_path / "apu_sequencer.fl"
|
||||
gateway_a_filelist = tmp_path / "gateway_a.fl"
|
||||
@@ -359,6 +360,9 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
|
||||
"opipsram", opipsram_filelist, memory_map, topology, user_extensions
|
||||
)
|
||||
assert generate_formal_filelist("apu", apu_filelist, memory_map, topology, user_extensions)
|
||||
assert generate_formal_filelist(
|
||||
"apu_primitives", apu_primitives_filelist, memory_map, topology, user_extensions
|
||||
)
|
||||
assert generate_formal_filelist(
|
||||
"apu_loader", apu_loader_filelist, memory_map, topology, user_extensions
|
||||
)
|
||||
@@ -381,6 +385,7 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
|
||||
onchip_ram = parse_filelists([onchip_ram_filelist], require_files=False)
|
||||
opipsram = parse_filelists([opipsram_filelist], require_files=False)
|
||||
apu = parse_filelists([apu_filelist], require_files=False)
|
||||
apu_primitives = parse_filelists([apu_primitives_filelist], require_files=False)
|
||||
apu_loader = parse_filelists([apu_loader_filelist], require_files=False)
|
||||
apu_sequencer = parse_filelists([apu_sequencer_filelist], require_files=False)
|
||||
gateway_a = parse_filelists([gateway_a_filelist], require_files=False)
|
||||
@@ -441,6 +446,22 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
|
||||
assert ROOT / "rtl/mini/formal/opipsram_formal.sv" in opipsram.files
|
||||
assert ROOT / "rtl/ip/multimedia/apu_dma.sv" in apu.files
|
||||
assert ROOT / "rtl/mini/formal/apu_formal.sv" in apu.files
|
||||
assert ROOT / "rtl/ip/multimedia/apu_resampler.sv" in apu_primitives.files
|
||||
assert ROOT / "rtl/ip/multimedia/apu_local_sram.sv" in apu_primitives.files
|
||||
assert ROOT / "rtl/ip/multimedia/apu_kernel_engine.sv" in apu_primitives.files
|
||||
assert ROOT / "rtl/ip/multimedia/apu_primitive_dispatcher.sv" in apu_primitives.files
|
||||
assert ROOT / "rtl/managed/clusterip/common/rtl/utils/fifo.sv" in apu_primitives.files
|
||||
assert ROOT / "rtl/mini/formal/apu_primitives_formal.sv" in apu_primitives.files
|
||||
formal_makefile = (ROOT / "rtl/mini/mk/formal.mk").read_text(encoding="utf-8")
|
||||
for scenario in (
|
||||
"apu_primitives_invalid_read",
|
||||
"apu_primitives_local",
|
||||
"apu_primitives_kernel",
|
||||
"apu_primitives_input_fifo",
|
||||
"apu_primitives_alignment",
|
||||
"apu_primitives_output_fifo",
|
||||
):
|
||||
assert scenario in formal_makefile
|
||||
assert ROOT / "rtl/ip/multimedia/apu_microcode_loader.sv" in apu_loader.files
|
||||
assert ROOT / "rtl/mini/formal/apu_loader_formal.sv" in apu_loader.files
|
||||
assert ROOT / "rtl/ip/multimedia/apu_codec_sequencer.sv" in apu_sequencer.files
|
||||
|
||||
Reference in new issue
Block a user