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:
maksyuki committed 2026-09-05 07:29:50 +08:00
1 parent 29a667415b
commit 57ffd03216
38 files changed
+8446 -138

No files matched your search

+11 -2
View File
@@ -287,6 +287,15 @@
#define RS_APU_ABI_APUMC_ENTRY_WORDS 8U #define RS_APU_ABI_APUMC_ENTRY_WORDS 8U
#define RS_APU_ABI_APUMC_ENTRY_COUNT 3U #define RS_APU_ABI_APUMC_ENTRY_COUNT 3U
#define RS_APU_ABI_APUMC_MAX_INSTRUCTIONS 2048U #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_MAGIC 0U
#define RS_APU_ABI_APUMC_HEADER_ABI 1U #define RS_APU_ABI_APUMC_HEADER_ABI 1U
#define RS_APU_ABI_APUMC_HEADER_TOTAL_BYTES 2U #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_ID_VALUE UINT32_C(0x41505530)
#define RS_APU_IP_VERSION_VALUE UINT32_C(0x00010000) #define RS_APU_IP_VERSION_VALUE UINT32_C(0x00010000)
#define RS_APU_IP_VERSION_MAJOR_MASK UINT32_C(0xFFFF0000) #define RS_APU_IP_VERSION_MAJOR_MASK UINT32_C(0xFFFF0000)
#define RS_APU_CAPABILITY0_IMPLEMENTED UINT32_C(0x00000098) #define RS_APU_CAPABILITY0_IMPLEMENTED UINT32_C(0x00000198)
#define RS_APU_CAPABILITY1_IMPLEMENTED UINT32_C(0x00000010) #define RS_APU_CAPABILITY1_IMPLEMENTED UINT32_C(0x01827010)
#define RS_APU_DIGEST_IMPLEMENTED UINT32_C(0x00000000) #define RS_APU_DIGEST_IMPLEMENTED UINT32_C(0x00000000)
#define RS_APU_IRQ_ALL UINT32_C(0x000007FF) #define RS_APU_IRQ_ALL UINT32_C(0x000007FF)
+4 -3
View File
@@ -119,9 +119,10 @@ ownership. Its implemented limits, handwritten ABI, measured performance, and
commercial release gates are documented in commercial release gates are documented in
[Baseline JPEG Codec](../docs/ip/jpeg.md). [Baseline JPEG Codec](../docs/ip/jpeg.md).
The Mini Audio Processing Unit has a frozen coreless architecture. APU-P2 adds The Mini Audio Processing Unit has a frozen coreless architecture. APU-P4 adds
the private AXI4 DMA, descriptor scheduler, fair Gateway A arbitration, and the private AXI4 DMA and scheduler, microcode loader/sequencer, 112 KiB local
production stream router/FIFOs to the fail-closed APB4 shell at `APB4_APU`. 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 Resource Controller index 7 and exclusive LP IRQ31/HP PLIC source10 routing
remain fixed. Public jobs and APU stream routes remain disabled until their remain fixed. Public jobs and APU stream routes remain disabled until their
later-phase engines exist. The complete ABI, phase order, and evidence gates later-phase engines exist. The complete ABI, phase order, and evidence gates
+158 -10
View File
@@ -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][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][15:0] s_mc_entry_max_loop;
logic [2:0][23:0] s_mc_entry_max_retired; 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 s_store_active, s_store_read, s_store_write, s_store_valid;
logic [10:0] s_store_addr; logic [10:0] s_store_addr;
logic [63:0] s_store_write_data, s_store_read_data; 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_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 [10:0] s_seq_fetch_addr;
logic [63:0] s_seq_fetch_data, s_seq_perf_retired; logic [63:0] s_seq_fetch_data, s_seq_perf_retired;
logic [31:0] s_seq_status, s_seq_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 [ 1:0] s_seq_fault_resp;
logic [ 7:0] s_seq_fault_index; logic [ 7:0] s_seq_fault_index;
logic [31:0] s_seq_fault_addr, s_seq_fault_detail; 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_input_watermark_evt, s_output_watermark_evt, s_xrun_evt;
logic s_ring_event, s_ring_err, s_abort_done; logic s_ring_event, s_ring_err, s_abort_done;
logic [ 5:0] s_ring_err_code; 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_tx_route_apu = s_stream_route[1:0] == 2'd1;
assign s_rx_route_apu = s_stream_route[3:2] == 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 && 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 idle_o = s_transport_idle;
assign s_resource_reset_request = s_resource_reset_pending_q || assign s_resource_reset_request = s_resource_reset_pending_q ||
(resource_reset_i && !s_resource_reset_seen_q); (resource_reset_i && !s_resource_reset_seen_q);
@@ -323,7 +356,9 @@ module apb4_apu (
.axi4 (axi4) .axi4 (axi4)
); );
apu_microcode_loader u_microcode_loader ( apu_microcode_loader #(
.EnableP4(1'b1)
) u_microcode_loader (
.clk_i (clk_i), .clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
.start_i (s_microcode_load), .start_i (s_microcode_load),
@@ -356,6 +391,11 @@ module apb4_apu (
.store_data_o (s_store_write_data), .store_data_o (s_store_write_data),
.store_data_i (s_store_read_data), .store_data_i (s_store_read_data),
.store_valid_i (s_store_valid), .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), .stat_o (s_mc_status),
.abi_o (s_mc_abi), .abi_o (s_mc_abi),
.build_id_o (s_mc_build_id), .build_id_o (s_mc_build_id),
@@ -367,6 +407,11 @@ module apb4_apu (
.entry_last_o (s_mc_entry_last), .entry_last_o (s_mc_entry_last),
.entry_max_loop_o (s_mc_entry_max_loop), .entry_max_loop_o (s_mc_entry_max_loop),
.entry_max_retired_o (s_mc_entry_max_retired), .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), .load_done_o (s_mc_load_done),
.abort_done_o (s_mc_abort_done), .abort_done_o (s_mc_abort_done),
.fault_valid_o (s_mc_fault_valid), .fault_valid_o (s_mc_fault_valid),
@@ -395,7 +440,80 @@ module apb4_apu (
.fetch_valid_o (s_seq_fetch_valid) .fetch_valid_o (s_seq_fetch_valid)
); );
apu_codec_sequencer u_codec_sequencer ( 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), .clk_i (clk_i),
.rst_n_i (rst_n_i), .rst_n_i (rst_n_i),
.soft_reset_i (s_soft_reset), .soft_reset_i (s_soft_reset),
@@ -411,10 +529,15 @@ module apb4_apu (
.entry_last_i (s_mc_entry_last), .entry_last_i (s_mc_entry_last),
.entry_max_loop_i (s_mc_entry_max_loop), .entry_max_loop_i (s_mc_entry_max_loop),
.entry_max_retired_i (s_mc_entry_max_retired), .entry_max_retired_i (s_mc_entry_max_retired),
.input_exhausted_i (1'b1), .entry_scratch_base_i (s_mc_entry_scratch_base),
.input_ready_i (1'b0), .entry_scratch_bytes_i (s_mc_entry_scratch_bytes),
.output_ready_i (1'b1), .entry_primitive_mask_i (s_mc_entry_primitive_mask),
.kernel_done_i (1'b0), .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), .transport_idle_success_i (!s_dma_busy && !s_dma_err),
.stall_i (1'b0), .stall_i (1'b0),
.cause_valid_i (1'b0), .cause_valid_i (1'b0),
@@ -424,6 +547,21 @@ module apb4_apu (
.cause_index_i (8'd0), .cause_index_i (8'd0),
.cause_addr_i (32'd0), .cause_addr_i (32'd0),
.cause_detail_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_o (s_seq_fetch),
.fetch_addr_o (s_seq_fetch_addr), .fetch_addr_o (s_seq_fetch_addr),
.fetch_data_i (s_seq_fetch_data), .fetch_data_i (s_seq_fetch_data),
@@ -442,6 +580,12 @@ module apb4_apu (
.fault_addr_o (s_seq_fault_addr), .fault_addr_o (s_seq_fault_addr),
.fault_detail_o (s_seq_fault_detail), .fault_detail_o (s_seq_fault_detail),
.perf_retired_o (s_seq_perf_retired), .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) .idle_o (s_seq_idle)
); );
@@ -576,10 +720,14 @@ module apb4_apu (
end end
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 ^ 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_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]; ^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_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 endmodule
+210
View File
@@ -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
+282 -12
View File
@@ -3,7 +3,9 @@
`include "apu_define.svh" `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 // verilog_format: off -- preserve launch, predicate, store, and result columns
input logic clk_i, input logic clk_i,
input logic rst_n_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][10:0] entry_last_i,
input logic [2:0][15:0] entry_max_loop_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][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_exhausted_i,
input logic input_ready_i, input logic input_ready_i,
input logic output_ready_i, input logic output_ready_i,
@@ -33,6 +40,21 @@ module apu_codec_sequencer (
input logic [7:0] cause_index_i, input logic [7:0] cause_index_i,
input logic [31:0] cause_addr_i, input logic [31:0] cause_addr_i,
input logic [31:0] cause_detail_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 fetch_o,
output logic [10:0] fetch_addr_o, output logic [10:0] fetch_addr_o,
input logic [63:0] fetch_data_i, 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_addr_o,
output logic [31:0] fault_detail_o, output logic [31:0] fault_detail_o,
output logic [63:0] perf_retired_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 output logic idle_o
// verilog_format: on // verilog_format: on
); );
import apu_microcode_pkg::*; import apu_microcode_pkg::*;
typedef enum logic [1:0] { typedef enum logic [2:0] {
Idle, Idle,
FetchRequest, FetchRequest,
FetchWait, FetchWait,
Execute Execute,
PrimitiveWait
} sequencer_state_e; } sequencer_state_e;
sequencer_state_e s_state_q; 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 [23:0] s_max_retired_q, s_frame_retired_q;
logic [31:0] s_watchdog_q; logic [31:0] s_watchdog_q;
logic [63:0] s_instruction_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 [31:0] s_stat_q;
logic s_trapped_q; logic s_trapped_q;
logic [63:0] s_perf_retired_q; logic [63:0] s_perf_retired_q;
logic [3:0] s_class, s_opcode, s_predicate, s_dst, s_src0, s_src1; 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 [31:0] s_immediate, s_source0, s_source1;
logic s_predicate_true; logic s_predicate_true;
logic [11:0] s_forward_target, s_loop_target; logic [11:0] s_forward_target, s_loop_target;
@@ -89,6 +127,8 @@ module apu_codec_sequencer (
logic s_trap_now; logic s_trap_now;
logic [ 7:0] s_trap_reason; logic [ 7:0] s_trap_reason;
logic [31:0] s_trap_detail; 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_class = s_instruction_q[63:60];
assign s_opcode = s_instruction_q[59:56]; assign s_opcode = s_instruction_q[59:56];
@@ -96,7 +136,7 @@ module apu_codec_sequencer (
assign s_dst = s_instruction_q[51:48]; assign s_dst = s_instruction_q[51:48];
assign s_src0 = s_instruction_q[47:44]; assign s_src0 = s_instruction_q[47:44];
assign s_src1 = s_instruction_q[43:40]; assign s_src1 = s_instruction_q[43:40];
assign s_aux = s_instruction_q[37:32]; assign s_aux = s_instruction_q[39:32];
assign s_immediate = s_instruction_q[31:0]; assign s_immediate = s_instruction_q[31:0];
assign s_source0 = s_gpr_q[s_src0]; assign s_source0 = s_gpr_q[s_src0];
assign s_source1 = s_gpr_q[s_src1]; assign s_source1 = s_gpr_q[s_src1];
@@ -110,6 +150,20 @@ module apu_codec_sequencer (
assign trapped_o = s_trapped_q; assign trapped_o = s_trapped_q;
assign perf_retired_o = s_perf_retired_q; assign perf_retired_o = s_perf_retired_q;
assign idle_o = s_state_q == Idle; 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 always_comb begin
unique case (s_predicate) unique case (s_predicate)
@@ -123,23 +177,87 @@ module apu_codec_sequencer (
4'd7: s_predicate_true = input_exhausted_i; 4'd7: s_predicate_true = input_exhausted_i;
4'd8: s_predicate_true = input_ready_i; 4'd8: s_predicate_true = input_ready_i;
4'd9: s_predicate_true = output_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; 4'd11: s_predicate_true = transport_idle_success_i;
default: s_predicate_true = 1'b0; default: s_predicate_true = 1'b0;
endcase endcase
end 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 always_comb begin
s_trap_now = 1'b0; s_trap_now = 1'b0;
s_trap_reason = 8'd0; s_trap_reason = 8'd0;
s_trap_detail = 32'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_now = 1'b1;
s_trap_reason = 8'd7; s_trap_reason = 8'd7;
end else if (!instruction_encoding_valid(s_instruction_q)) begin end else if (!instruction_encoding_valid(s_instruction_q)) begin
s_trap_now = 1'b1; s_trap_now = 1'b1;
s_trap_reason = 8'd1; 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_now = 1'b1;
s_trap_reason = 8'd6; s_trap_reason = 8'd6;
end else if (s_frame_retired_q >= s_max_retired_q) begin 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_loop_target[10:0] < s_first_pc_q);
s_trap_reason = 8'd4; s_trap_reason = 8'd4;
end end
4'd8: begin
end
default: begin default: begin
s_trap_now = 1'b1; s_trap_now = 1'b1;
s_trap_reason = 8'd1; s_trap_reason = 8'd1;
@@ -237,7 +357,7 @@ module apu_codec_sequencer (
fault_index_o <= 8'd0; \ fault_index_o <= 8'd0; \
fault_addr_o <= {18'd0, s_pc_q, 3'd0}; \ fault_addr_o <= {18'd0, s_pc_q, 3'd0}; \
fault_detail_o <= DETAIL; \ 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 end
`define RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP \ `define RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP \
@@ -275,6 +395,18 @@ module apu_codec_sequencer (
s_frame_retired_q <= 24'd0; s_frame_retired_q <= 24'd0;
s_watchdog_q <= 32'd0; s_watchdog_q <= 32'd0;
s_instruction_q <= 64'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_stat_q <= 32'd0;
s_trapped_q <= 1'b0; s_trapped_q <= 1'b0;
s_perf_retired_q <= 64'd0; s_perf_retired_q <= 64'd0;
@@ -291,6 +423,20 @@ module apu_codec_sequencer (
trap_event_o <= 1'b0; trap_event_o <= 1'b0;
abort_done_o <= 1'b0; abort_done_o <= 1'b0;
fault_valid_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 (counter_clear_i) s_perf_retired_q <= 64'd0;
if (soft_reset_i || resource_reset_i) begin if (soft_reset_i || resource_reset_i) begin
s_state_q <= Idle; s_state_q <= Idle;
@@ -306,6 +452,9 @@ module apu_codec_sequencer (
s_stat_q <= 32'd0; s_stat_q <= 32'd0;
s_trapped_q <= 1'b0; s_trapped_q <= 1'b0;
s_perf_retired_q <= 64'd0; 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 end else begin
unique case (s_state_q) unique case (s_state_q)
Idle: begin Idle: begin
@@ -322,10 +471,18 @@ module apu_codec_sequencer (
s_last_pc_q <= entry_last_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_loop_q <= entry_max_loop_i[launch_entry_i];
s_max_retired_q <= entry_max_retired_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_frame_retired_q <= 24'd0;
s_watchdog_q <= 32'd0; s_watchdog_q <= 32'd0;
s_stat_q <= {21'd0, entry_pc_i[launch_entry_i]}; s_stat_q <= {21'd0, entry_pc_i[launch_entry_i]};
s_trapped_q <= 1'b0; 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
end end
FetchRequest: begin FetchRequest: begin
@@ -334,6 +491,9 @@ module apu_codec_sequencer (
`RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP; `RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP;
end else if (abort_i) begin end else if (abort_i) begin
s_state_q <= Idle; 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; abort_done_o <= 1'b1;
end else begin end else begin
s_watchdog_q <= s_watchdog_q + 1'b1; s_watchdog_q <= s_watchdog_q + 1'b1;
@@ -345,6 +505,9 @@ module apu_codec_sequencer (
`RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP; `RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP;
end else if (abort_i) begin end else if (abort_i) begin
s_state_q <= Idle; 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; abort_done_o <= 1'b1;
end else begin end else begin
s_watchdog_q <= s_watchdog_q + 1'b1; s_watchdog_q <= s_watchdog_q + 1'b1;
@@ -369,13 +532,40 @@ module apu_codec_sequencer (
fault_index_o <= cause_index_i; fault_index_o <= cause_index_i;
fault_addr_o <= cause_addr_i; fault_addr_o <= cause_addr_i;
fault_detail_o <= cause_detail_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}; 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 <= 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 end else if (s_trap_now) begin
`RETROSOC_APU__SET_TRAP(s_trap_detail); `RETROSOC_APU__SET_TRAP(s_trap_detail);
end else if (abort_i) begin end else if (abort_i) begin
s_state_q <= Idle; 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; abort_done_o <= 1'b1;
end else if (stall_i) begin end else if (s_execute_stall) begin
s_stat_q[19] <= 1'b1; s_stat_q[19] <= 1'b1;
if (s_watchdog_q + 1'b1 >= timeout_i) begin if (s_watchdog_q + 1'b1 >= timeout_i) begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd7, s_pc_q, s_instruction_q)); `RETROSOC_APU__SET_TRAP(trap_detail(8'd7, s_pc_q, s_instruction_q));
@@ -384,10 +574,19 @@ module apu_codec_sequencer (
end end
end else if (!instruction_encoding_valid(s_instruction_q)) begin end else if (!instruction_encoding_valid(s_instruction_q)) begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd1, s_pc_q, s_instruction_q)); `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)); `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 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)); `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 end else begin
s_watchdog_q <= 32'd0; s_watchdog_q <= 32'd0;
s_stat_q[19] <= 1'b0; s_stat_q[19] <= 1'b0;
@@ -487,12 +686,35 @@ module apu_codec_sequencer (
s_state_q <= FetchRequest; s_state_q <= FetchRequest;
end end
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 default: begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd1, s_pc_q, s_instruction_q)); `RETROSOC_APU__SET_TRAP(trap_detail(8'd1, s_pc_q, s_instruction_q));
s_frame_retired_q <= s_frame_retired_q; s_frame_retired_q <= s_frame_retired_q;
s_perf_retired_q <= s_perf_retired_q; s_perf_retired_q <= s_perf_retired_q;
end end
endcase 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 end else begin
unique case (s_opcode) unique case (s_opcode)
4'd0: s_gpr_q[s_dst] <= s_source0; 4'd0: s_gpr_q[s_dst] <= s_source0;
@@ -537,6 +759,54 @@ module apu_codec_sequencer (
end end
end 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; default: s_state_q <= Idle;
endcase endcase
end end
+9
View File
@@ -307,6 +307,15 @@
`define APB4_APU__APUMC_ENTRY_WORDS 8 `define APB4_APU__APUMC_ENTRY_WORDS 8
`define APB4_APU__APUMC_ENTRY_COUNT 3 `define APB4_APU__APUMC_ENTRY_COUNT 3
`define APB4_APU__APUMC_MAX_INSTRUCTIONS 2048 `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_MAGIC 0
`define APB4_APU__APUMC_HEADER_ABI 1 `define APB4_APU__APUMC_HEADER_ABI 1
`define APB4_APU__APUMC_HEADER_TOTAL_BYTES 2 `define APB4_APU__APUMC_HEADER_TOTAL_BYTES 2
+454
View File
@@ -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
+149
View File
@@ -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
+100 -17
View File
@@ -4,7 +4,8 @@
`include "apu_define.svh" `include "apu_define.svh"
module apu_microcode_loader #( 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 // verilog_format: off -- preserve command, DMA, store, and result columns
input logic clk_i, input logic clk_i,
@@ -39,6 +40,11 @@ module apu_microcode_loader #(
output logic [63:0] store_data_o, output logic [63:0] store_data_o,
input logic [63:0] store_data_i, input logic [63:0] store_data_i,
input logic store_valid_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 [7:0] stat_o,
output logic [31:0] abi_o, output logic [31:0] abi_o,
output logic [63:0] build_id_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][10:0] entry_last_o,
output logic [2:0][15:0] entry_max_loop_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][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 load_done_o,
output logic abort_done_o, output logic abort_done_o,
output logic fault_valid_o, output logic fault_valid_o,
@@ -63,7 +74,7 @@ module apu_microcode_loader #(
); );
import apu_microcode_pkg::*; import apu_microcode_pkg::*;
localparam logic [11:0] PathStackLimit = PathStackDepth; localparam logic [11:0] PathStackLimit = 12'(PathStackDepth);
typedef enum logic [4:0] { typedef enum logic [4:0] {
Idle, Idle,
@@ -123,6 +134,7 @@ module apu_microcode_loader #(
logic s_cancel_resource_q, s_cancel_abort_q; 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_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_addr, s_header_detail;
logic [31:0] s_header_range_addr, s_header_range_detail; logic [31:0] s_header_range_addr, s_header_range_detail;
logic [31:0] s_range_addr, s_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 [ 4:0] s_entry_word_relative;
logic [63:0] s_scan_instruction; logic [63:0] s_scan_instruction;
logic [3:0] s_scan_class, s_scan_opcode, s_scan_predicate; 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 [10:0] s_scan_immediate;
logic s_scan_last; logic s_scan_last;
logic s_scan_instruction_err, s_scan_target_err, s_scan_call_err, s_scan_loop_err; 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]; s_scan_pc_q : s_instruction_word_relative[11:1];
assign store_data_o = {dma_data_i, s_instruction_low_q}; 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 idle_o = s_state_q == Idle;
assign stat_o = { assign stat_o = {
s_err_bits_q[5], s_err_bits_q[5],
@@ -195,6 +213,11 @@ module apu_microcode_loader #(
assign entry_last_o[entry] = s_entry_word_q[(entry*8)+1][26:16]; 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_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_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 end
assign s_instruction_end = {1'b0, s_header_q[4]} + ({1'b0, s_header_q[3]} << 3); 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_err = 1'b1;
s_header_range_addr = s_source_addr_q + 32'd20; s_header_range_addr = s_source_addr_q + 32'd20;
s_header_range_detail = s_header_q[5]; 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_err = 1'b1;
s_header_range_addr = s_source_addr_q + 32'd40; s_header_range_addr = s_source_addr_q + 32'd40;
s_header_range_detail = s_header_q[10]; s_header_range_detail = s_header_q[10];
@@ -292,10 +316,15 @@ module apu_microcode_loader #(
end end
always_comb begin always_comb begin
logic [31:0] s_scratch_end;
logic [31:0] s_max_scratch_end;
s_range_err = s_header_range_err; s_range_err = s_header_range_err;
s_range_addr = s_header_range_addr; s_range_addr = s_header_range_addr;
s_range_detail = s_header_range_detail; s_range_detail = s_header_range_detail;
s_max_scratch_end = 32'd0;
for (int unsigned entry = 0; entry < 3; entry++) begin 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) || 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_entry_word_q[entry*8][3:0] != 4'(entry)))) begin
s_range_err = 1'b1; s_range_err = 1'b1;
@@ -312,11 +341,19 @@ module apu_microcode_loader #(
s_range_err = 1'b1; s_range_err = 1'b1;
s_range_addr = s_source_addr_q + s_header_q[7] + 32'(entry * 32) + 32'd4; 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]; 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_err = 1'b1;
s_range_addr = s_source_addr_q + s_header_q[7] + 32'(entry * 32) + 32'd8; 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]; 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_err = 1'b1;
s_range_addr = s_source_addr_q + s_header_q[7] + 32'(entry * 32) + 32'd12; 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]; 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]; s_range_detail = s_entry_word_q[(entry*8)+5];
end end
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 end
always_comb begin always_comb begin
logic [16:0] s_entry_table_end;
s_table_err = 1'b0; s_table_err = 1'b0;
s_table_addr = s_source_addr_q + 32'd20; s_table_addr = s_source_addr_q + 32'd20;
s_table_detail = s_header_q[5]; 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; 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_err = 1'b1;
s_table_addr = s_source_addr_q + 32'd24; s_table_addr = s_source_addr_q + 32'd24;
s_table_detail = s_header_q[6]; s_table_detail = s_header_q[6];
end else begin end else begin
for (int unsigned entry = 0; entry < 3; entry++) 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_err = 1'b1;
s_table_addr = s_source_addr_q + s_header_q[7] + 32'(entry * 32) + 32'd28; 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]; 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; s_capability_detail = s_header_q[9] ^ s_entry_mask;
if (s_header_q[9] != s_entry_mask) begin if (s_header_q[9] != s_entry_mask) begin
s_capability_err = 1'b1; 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_err = 1'b1;
s_capability_detail = s_header_q[9]; s_capability_detail = s_header_q[9];
end else begin end else begin
for (int unsigned entry = 0; entry < 3; entry++) 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_err = 1'b1;
s_capability_addr = s_source_addr_q + s_header_q[7] + 32'(entry * 32) + 32'd24; 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]; 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_class = s_scan_instruction[63:60];
assign s_scan_opcode = s_scan_instruction[59:56]; assign s_scan_opcode = s_scan_instruction[59:56];
assign s_scan_predicate = s_scan_instruction[55:52]; 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_immediate = s_scan_instruction[10:0];
assign s_scan_last = s_scan_pc_q == entry_last_o[s_scan_entry_q]; 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}; 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_predicate != 4'd0) && (s_scan_class == 4'd0) &&
(s_scan_opcode inside {4'd3, 4'd4}) && (s_path_stack_count_q < PathStackLimit); (s_scan_opcode inside {4'd3, 4'd4}) && (s_path_stack_count_q < PathStackLimit);
assign s_path_stack_cs = s_path_stack_push || 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_write = s_path_stack_push;
assign s_path_stack_addr = 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; s_path_stack_count_q[10:0] : s_path_stack_count_q[10:0] - 1'b1;
@@ -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'b0001 << s_scan_aux[1:0])) != 4'd0) :
((s_loop_active_q != 4'd0) || ((s_loop_active_q != 4'd0) ||
((s_scan_class == 4'd0) && (s_scan_opcode == 4'd6)))); ((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)) || assign s_scan_capability_instruction = EnableP4 ?
((s_scan_class == 4'd0) && (s_scan_opcode == 4'd8)); ((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 always_comb begin
s_path_fallthrough = s_scan_predicate != 4'd0; 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; s_path_fallthrough = 1'b1;
end else if ((s_scan_class == 4'd0) && (s_scan_opcode inside {4'd0, 4'd6, 4'd7})) begin end else if ((s_scan_class == 4'd0) && (s_scan_opcode inside {4'd0, 4'd6, 4'd7})) begin
s_path_fallthrough = 1'b1; s_path_fallthrough = 1'b1;
@@ -727,6 +794,14 @@ module apu_microcode_loader #(
fault_stage_o <= `APB4_APU__ERROR_STAGE_LOADER; fault_stage_o <= `APB4_APU__ERROR_STAGE_LOADER;
fault_addr_o <= s_header_range_addr; fault_addr_o <= s_header_range_addr;
fault_detail_o <= s_header_range_detail; 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 end else begin
s_transfer_err_q <= 1'b0; s_transfer_err_q <= 1'b0;
s_byte_offset_q <= s_header_q[7]; 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_stage_o <= `APB4_APU__ERROR_STAGE_LOADER;
fault_addr_o <= s_range_addr; fault_addr_o <= s_range_addr;
fault_detail_o <= s_range_detail; 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 end else begin
s_byte_offset_q <= 32'd64; s_byte_offset_q <= 32'd64;
s_crc_q <= 32'hffff_ffff; s_crc_q <= 32'hffff_ffff;
@@ -1000,7 +1083,7 @@ module apu_microcode_loader #(
end end
end end
PathPop: begin 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_path_stack_count_q <= s_path_stack_count_q - 1'b1;
s_state_q <= PathPopLoad; s_state_q <= PathPopLoad;
end else if (s_scan_control_err_q || (s_scan_entry_q == 2'd2)) begin end else if (s_scan_control_err_q || (s_scan_entry_q == 2'd2)) begin
+45
View File
@@ -10,8 +10,53 @@ package apu_microcode_pkg;
logic [10:0] last_pc; logic [10:0] last_pc;
logic [15:0] max_loop_count; logic [15:0] max_loop_count;
logic [23:0] max_retired; 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; } 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, 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); input logic [3:0] keep_i);
logic [31:0] s_crc; 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
+2 -2
View File
@@ -80,8 +80,8 @@ module apu_reg (
); );
localparam logic [31:0] IpId = 32'h4150_5530; localparam logic [31:0] IpId = 32'h4150_5530;
localparam logic [31:0] IpVersion = 32'h0001_0000; localparam logic [31:0] IpVersion = 32'h0001_0000;
localparam logic [31:0] Capability0 = 32'h0000_0098; localparam logic [31:0] Capability0 = 32'h0000_0198;
localparam logic [31:0] Capability1 = 32'h0000_0010; localparam logic [31:0] Capability1 = 32'h0182_7010;
localparam logic [31:0] AbiDigest = 32'd0; localparam logic [31:0] AbiDigest = 32'd0;
localparam logic [10:0] IrqMask = 11'h7ff; localparam logic [10:0] IrqMask = 11'h7ff;
localparam logic [31:0] TimeoutReset = 32'h0000_ffff; localparam logic [31:0] TimeoutReset = 32'h0000_ffff;
+101
View File
@@ -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
+32
View File
@@ -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
+8
View File
@@ -131,6 +131,14 @@
/ip/multimedia/apu_stream_router.sv /ip/multimedia/apu_stream_router.sv
/ip/multimedia/apu_control_store.sv /ip/multimedia/apu_control_store.sv
/ip/multimedia/apu_microcode_loader.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_codec_sequencer.sv
/ip/multimedia/apu_reg.sv /ip/multimedia/apu_reg.sv
/ip/multimedia/apb4_apu.sv /ip/multimedia/apb4_apu.sv
+332
View File
@@ -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
+26
View File
@@ -80,6 +80,11 @@ module apu_sequencer_formal_design (
.entry_last_i (s_entry_last), .entry_last_i (s_entry_last),
.entry_max_loop_i ({16'd4, 16'd4, 16'd4}), .entry_max_loop_i ({16'd4, 16'd4, 16'd4}),
.entry_max_retired_i (s_entry_max_retired), .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_exhausted_i (1'b1),
.input_ready_i (1'b0), .input_ready_i (1'b0),
.output_ready_i (1'b1), .output_ready_i (1'b1),
@@ -93,6 +98,21 @@ module apu_sequencer_formal_design (
.cause_index_i (8'd0), .cause_index_i (8'd0),
.cause_addr_i (32'd0), .cause_addr_i (32'd0),
.cause_detail_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_o (s_fetch),
.fetch_addr_o (s_fetch_addr), .fetch_addr_o (s_fetch_addr),
.fetch_data_i (s_fetch_data), .fetch_data_i (s_fetch_data),
@@ -111,6 +131,12 @@ module apu_sequencer_formal_design (
.fault_addr_o (fault_addr), .fault_addr_o (fault_addr),
.fault_detail_o (fault_detail), .fault_detail_o (fault_detail),
.perf_retired_o (), .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) .idle_o (idle)
); );
@@ -33,6 +33,15 @@ APU_LOADER_SCENARIOS = {
"apu_loader_abort": 5, "apu_loader_abort": 5,
"apu_loader_resource_reset": 6, "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]: def target_defines(target: str) -> list[str]:
@@ -45,6 +54,11 @@ def target_defines(target: str) -> list[str]:
"+define+SYNTHESIS", "+define+SYNTHESIS",
f"+define+APU_LOADER_FORMAL_SCENARIO={scenario}", 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": if target == "opipsram":
return ["+define+PDK_BEHAV"] return ["+define+PDK_BEHAV"]
if target == "sysctrl": if target == "sysctrl":
@@ -273,6 +287,20 @@ def source_files(target: str) -> list[Path]:
MULTIMEDIA / "apu_dma.sv", MULTIMEDIA / "apu_dma.sv",
SCRIPT_DIR / "apu_formal.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: if target == "apu_loader" or target in APU_LOADER_SCENARIOS:
return [ return [
ROOT / "rtl/tech/tc_sram.sv", ROOT / "rtl/tech/tc_sram.sv",
@@ -362,6 +390,8 @@ def parse_args() -> argparse.Namespace:
"opipsram", "opipsram",
"dma", "dma",
"apu", "apu",
"apu_primitives",
*APU_PRIMITIVE_SCENARIOS,
*APU_LOADER_SCENARIOS, *APU_LOADER_SCENARIOS,
"apu_sequencer", "apu_sequencer",
"gateway_a", "gateway_a",
+10 -7
View File
@@ -31,12 +31,13 @@ FORMAL_APU_TIMEOUT ?= 300
FORMAL_GATEWAY_A_TIMEOUT ?= 120 FORMAL_GATEWAY_A_TIMEOUT ?= 120
FORMAL_SDIO_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_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_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_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_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_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_LOADER_TARGETS) apu_sequencer gateway_a sdio 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_FILELIST_GENERATOR := $(RTL_PATH)/formal/generate_formal_filelist.py
FORMAL_SBY_GENERATOR := $(RTL_PATH)/formal/generate_sby_config.py FORMAL_SBY_GENERATOR := $(RTL_PATH)/formal/generate_sby_config.py
FORMAL_RESULT_GENERATOR := $(RTL_PATH)/formal/formal_results.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/dma_formal_props.sv \
$(RTL_PATH)/formal/apu_formal.sv \ $(RTL_PATH)/formal/apu_formal.sv \
$(RTL_PATH)/formal/apu_formal_props.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.sv \
$(RTL_PATH)/formal/apu_loader_formal_props.sv \ $(RTL_PATH)/formal/apu_loader_formal_props.sv \
$(RTL_PATH)/formal/apu_sequencer_formal.sv \ $(RTL_PATH)/formal/apu_sequencer_formal.sv \
@@ -212,14 +215,14 @@ $(FORMAL_DIR)/%/prove.sby: $(FORMAL_DIR)/%/design.v \
$(FORMAL_SBY_GENERATOR) $(FORMAL_SBY_GENERATOR)
python3 $(FORMAL_SBY_GENERATOR) --top $(FORMAL_TARGET_TOP)_formal --input $< \ python3 $(FORMAL_SBY_GENERATOR) --top $(FORMAL_TARGET_TOP)_formal --input $< \
--properties $(RTL_PATH)/formal/$(FORMAL_TARGET_TOP)_formal_props.sv --solver $(FORMAL_SOLVER) \ --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 @if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi
$(FORMAL_DIR)/%/cover.sby: $(FORMAL_DIR)/%/design.v \ $(FORMAL_DIR)/%/cover.sby: $(FORMAL_DIR)/%/design.v \
$(FORMAL_SBY_GENERATOR) $(FORMAL_SBY_GENERATOR)
python3 $(FORMAL_SBY_GENERATOR) --top $(FORMAL_TARGET_TOP)_formal --input $< \ python3 $(FORMAL_SBY_GENERATOR) --top $(FORMAL_TARGET_TOP)_formal --input $< \
--properties $(RTL_PATH)/formal/$(FORMAL_TARGET_TOP)_formal_props.sv --solver $(FORMAL_SOLVER) \ --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 $(filter $(FORMAL_COMPACT_COVER_TARGETS),$*),--no-vcd) --output $@
@if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi @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-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 formal-apu-loader: $(addsuffix /.stamp,$(addprefix $(FORMAL_DIR)/,$(FORMAL_APU_LOADER_TARGETS))) | manifest
+8
View File
@@ -135,15 +135,23 @@
"rtl/ip/multimedia": [ "rtl/ip/multimedia": [
"apb4_apu.sv", "apb4_apu.sv",
"apb4_jpeg.sv", "apb4_jpeg.sv",
"apu_bitstream_engine.sv",
"apu_codec_sequencer.sv", "apu_codec_sequencer.sv",
"apu_control_store.sv", "apu_control_store.sv",
"apu_dma.sv", "apu_dma.sv",
"apu_define.svh", "apu_define.svh",
"apu_entropy_engine.sv",
"apu_kernel_engine.sv",
"apu_local_sram.sv",
"apu_microcode_loader.sv", "apu_microcode_loader.sv",
"apu_microcode_pkg.sv", "apu_microcode_pkg.sv",
"apu_primitive_dispatcher.sv",
"apu_reconstruction_engine.sv",
"apu_resampler.sv",
"apu_ring_scheduler.sv", "apu_ring_scheduler.sv",
"apu_reg.sv", "apu_reg.sv",
"apu_stream_router.sv", "apu_stream_router.sv",
"apu_transform_engine.sv",
"axi4s_dvp.sv", "axi4s_dvp.sv",
"dvp_core.sv", "dvp_core.sv",
"dvp_define.svh", "dvp_define.svh",
+5 -4
View File
@@ -4,10 +4,11 @@ This directory contains the Python implementation of setup, dependency locking,
build manifests, flow execution, simulation checks, regression orchestration, build manifests, flow execution, simulation checks, regression orchestration,
quality checks, metrics, packaging, and cleanup. quality checks, metrics, packaging, and cleanup.
`apu_mcasm.py`, `apu_isa.py`, and `apu_interpreter.py` are the frozen APU `apu_mcasm.py`, `apu_isa.py`, `apu_interpreter.py`, and `apu_primitives.py` are
microcode V1 assembler/ABI model and P3 reference interpreter. They accept only the frozen APU microcode V1 assembler/ABI model, P3/P4 target interpreters, and
the APU instruction language and deliberately have no C, ELF, RV32, dynamic bit-accurate P4 primitive model. They accept only the APU instruction language
linking, or runtime code-generation path. 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 Scripts are part of the build contract. Prefer existing helpers over ad-hoc
shell behavior, preserve structured JSON results, and keep setup/download shell behavior, preserve structured JSON results, and keep setup/download
+219 -12
View File
@@ -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 from __future__ import annotations
@@ -8,15 +8,20 @@ from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from apu_isa import ( from apu_isa import (
APUMC_TARGETS,
BitstreamOpcode,
ControlOpcode, ControlOpcode,
EntropyOpcode,
Entry, Entry,
Instruction, Instruction,
InstructionClass, InstructionClass,
LocalOpcode,
ScalarOpcode, ScalarOpcode,
parse_apumc, parse_apumc,
trap_detail, trap_detail,
validate_p3_instruction, validate_instruction,
) )
from apu_primitives import PrimitiveBam, PrimitiveFault, PrimitiveResult
class SequencerTrap(RuntimeError): class SequencerTrap(RuntimeError):
@@ -55,15 +60,36 @@ class Machine:
loops: dict[int, tuple[int, int]] = field(default_factory=dict) loops: dict[int, tuple[int, int]] = field(default_factory=dict)
watchdog: int = 0 watchdog: int = 0
trace: list[dict[str, object]] = field(default_factory=list) 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: def __post_init__(self) -> None:
self.pc = self.entry.entry_pc self.begin_entry(self.entry)
@staticmethod @staticmethod
def _signed(value: int) -> int: def _signed(value: int) -> int:
return value if value < 0x80000000 else value - 0x100000000 return value if value < 0x80000000 else value - 0x100000000
def _predicate(self, predicate: int) -> bool: 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 = ( values = (
True, True,
self.eq, self.eq,
@@ -72,10 +98,10 @@ class Machine:
not self.signed_lt, not self.signed_lt,
self.unsigned_lt, self.unsigned_lt,
not self.unsigned_lt, not self.unsigned_lt,
self.predicate_inputs.input_exhausted, input_exhausted,
self.predicate_inputs.input_ready, input_ready,
self.predicate_inputs.output_ready, output_ready,
self.predicate_inputs.kernel_done, kernel_done,
self.predicate_inputs.transport_done, self.predicate_inputs.transport_done,
) )
if predicate >= len(values): if predicate >= len(values):
@@ -85,6 +111,86 @@ class Machine:
def _write(self, register: int, value: int) -> None: def _write(self, register: int, value: int) -> None:
self.registers[register] = value & 0xFFFFFFFF 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: def _retire(self, instruction: Instruction, predicate_true: bool) -> None:
self.retired += 1 self.retired += 1
self.watchdog = 0 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 self.watchdog += 1
if self.timeout <= 0 or self.watchdog >= self.timeout: if self.timeout <= 0 or self.watchdog >= self.timeout:
raise SequencerTrap(7, self.pc, instruction) 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: if self.pc < self.entry.first_pc or self.pc > self.entry.last_pc:
raise SequencerTrap(2, self.pc, Instruction(0, 0)) raise SequencerTrap(2, self.pc, Instruction(0, 0))
instruction = self.instructions[self.pc] 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: try:
validate_p3_instruction(instruction) validate_instruction(instruction, self.target)
except ValueError as error: except ValueError as error:
raise SequencerTrap(1, self.pc, instruction) from error raise SequencerTrap(1, self.pc, instruction) from error
if self.retired >= self.entry.max_retired: if self.retired >= self.entry.max_retired:
@@ -129,6 +242,10 @@ class Machine:
self.pc += 1 self.pc += 1
return False return False
if self._pending_dependency(instruction):
self._no_retirement_cycle(instruction, tick_kernel=False)
return False
next_pc = self.pc + 1 next_pc = self.pc + 1
terminal = False terminal = False
if instruction.instruction_class == InstructionClass.CONTROL: if instruction.instruction_class == InstructionClass.CONTROL:
@@ -167,7 +284,18 @@ class Machine:
next_pc = target next_pc = target
else: else:
del self.loops[slot] 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) opcode = ScalarOpcode(instruction.opcode)
source0 = self.registers[instruction.src0] source0 = self.registers[instruction.src0]
source1 = self.registers[instruction.src1] source1 = self.registers[instruction.src1]
@@ -205,6 +333,71 @@ class Machine:
instruction.dst, instruction.dst,
self._saturate(source0, instruction.aux & 0x1F, bool(instruction.aux & 0x20)), 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) self._retire(instruction, predicate_true)
if not terminal: if not terminal:
@@ -214,10 +407,12 @@ class Machine:
return terminal return terminal
def run(self) -> dict[str, object]: def run(self) -> dict[str, object]:
fetch_required = True
while True: while True:
if self.pc < self.entry.first_pc or self.pc > self.entry.last_pc: if self.pc < self.entry.first_pc or self.pc > self.entry.last_pc:
raise SequencerTrap(2, self.pc, Instruction(0, 0)) raise SequencerTrap(2, self.pc, Instruction(0, 0))
instruction = self.instructions[self.pc] instruction = self.instructions[self.pc]
if fetch_required:
for _ in range(self.fetch_no_retirement_cycles): for _ in range(self.fetch_no_retirement_cycles):
self._no_retirement_cycle(instruction) self._no_retirement_cycle(instruction)
remaining_stalls = self.stall_cycles.get(self.pc, 0) remaining_stalls = self.stall_cycles.get(self.pc, 0)
@@ -225,19 +420,23 @@ class Machine:
self._no_retirement_cycle(instruction) self._no_retirement_cycle(instruction)
remaining_stalls -= 1 remaining_stalls -= 1
self.stall_cycles[self.pc] = 0 self.stall_cycles[self.pc] = 0
retired_before = self.retired
if self.step(): if self.step():
break break
fetch_required = self.retired != retired_before
return { return {
"pc": self.pc, "pc": self.pc,
"retired": self.retired, "retired": self.retired,
"registers": self.registers, "registers": self.registers,
"trace": self.trace, "trace": self.trace,
"primitive_cycles": self.primitive_cycles,
} }
def main() -> int: def main() -> int:
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("bundle", type=Path) 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("--entry", type=int, choices=range(3), default=0)
parser.add_argument("--timeout", type=int, default=0xFFFF) parser.add_argument("--timeout", type=int, default=0xFFFF)
parser.add_argument("--input-exhausted", action="store_true") 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("--kernel-done", action="store_true")
parser.add_argument("--transport-done", action="store_true") parser.add_argument("--transport-done", action="store_true")
args = parser.parse_args() 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( inputs = PredicateInputs(
input_exhausted=args.input_exhausted, input_exhausted=args.input_exhausted,
input_ready=args.input_ready, input_ready=args.input_ready,
@@ -256,7 +455,15 @@ def main() -> int:
) )
print( print(
json.dumps( 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 return 0
+298 -48
View File
@@ -17,6 +17,9 @@ APUMC_ENTRY_COUNT = 3
APUMC_INSTRUCTION_OFFSET = 192 APUMC_INSTRUCTION_OFFSET = 192
APUMC_MAX_INSTRUCTIONS = 2048 APUMC_MAX_INSTRUCTIONS = 2048
APUMC_P3_CAPABILITY_MASK = 0 APUMC_P3_CAPABILITY_MASK = 0
APUMC_P4_CAPABILITY_MASK = 0x0000FFFF
APUMC_P4_TABLE_SCRATCH_BYTES = 0x6000
APUMC_TARGETS = ("p3", "p4")
class InstructionClass(IntEnum): class InstructionClass(IntEnum):
@@ -58,6 +61,50 @@ class ScalarOpcode(IntEnum):
SAT = 13 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 = { PREDICATES = {
"always": 0, "always": 0,
"eq": 1, "eq": 1,
@@ -184,6 +231,9 @@ def abi_manifest() -> dict[str, object]:
"table": 7, "table": 7,
}, },
"p3_implemented_primitive_mask": APUMC_P3_CAPABILITY_MASK, "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") raise ValueError(f"{name} is reserved for this opcode")
def validate_p3_instruction(instruction: Instruction) -> None: def _target_mask(target: str) -> int:
"""Validate one instruction against the P3 class-0/class-1 subset.""" 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: if instruction.predicate > 11:
raise ValueError("predicate is reserved") raise ValueError("predicate is reserved")
if instruction.instruction_class == InstructionClass.CONTROL: if instruction.instruction_class == InstructionClass.CONTROL:
@@ -318,16 +395,13 @@ def validate_p3_instruction(instruction: Instruction) -> None:
_require_zero(instruction, ("dst", "src0", "src1", "immediate")) _require_zero(instruction, ("dst", "src0", "src1", "immediate"))
if instruction.predicate != 0 or instruction.aux > 5: if instruction.predicate != 0 or instruction.aux > 5:
raise ValueError("WAIT encoding is invalid") 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 return
if instruction.instruction_class != InstructionClass.SCALAR: if instruction.instruction_class == InstructionClass.SCALAR:
if instruction.instruction_class <= 6:
raise ValueError("instruction class is unavailable in P3")
raise ValueError("instruction class is invalid in V1")
if instruction.opcode > ScalarOpcode.SAT: if instruction.opcode > ScalarOpcode.SAT:
raise ValueError("scalar opcode is unallocated") raise ValueError("scalar opcode is unallocated")
opcode = ScalarOpcode(instruction.opcode) opcode = ScalarOpcode(instruction.opcode)
if opcode == ScalarOpcode.MOV: if opcode == ScalarOpcode.MOV:
_require_zero(instruction, ("src1", "aux", "immediate")) _require_zero(instruction, ("src1", "aux", "immediate"))
@@ -354,9 +428,103 @@ def validate_p3_instruction(instruction: Instruction) -> None:
_require_zero(instruction, ("src1", "immediate")) _require_zero(instruction, ("src1", "immediate"))
if instruction.aux & 0xC0 or (instruction.aux & 0x1F) not in (0, 8, 16, 24): if instruction.aux & 0xC0 or (instruction.aux & 0x1F) not in (0, 8, 16, 24):
raise ValueError("SAT width/sign encoding is invalid") raise ValueError("SAT width/sign encoding is invalid")
return
if instruction.instruction_class > InstructionClass.TRANSPORT:
raise ValueError("instruction class is invalid in V1")
if target == "p3" or instruction.instruction_class == InstructionClass.TRANSPORT:
raise ValueError(f"instruction class is unavailable in {target.upper()}")
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): if entry.format_id not in range(APUMC_ENTRY_COUNT):
raise ValueError("entry format must be 0, 1, or 2") raise ValueError("entry format must be 0, 1, or 2")
if not (0 <= entry.first_pc <= entry.entry_pc <= entry.last_pc < instruction_count): 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") raise ValueError("maximum loop count must be 1..65535")
if not 1 <= entry.max_retired <= 0xFFFFFF: if not 1 <= entry.max_retired <= 0xFFFFFF:
raise ValueError("maximum retired count must be 1..16777215") raise ValueError("maximum retired count must be 1..16777215")
if any( if target == "p3" and any(
( (
entry.scratch_base, entry.scratch_base,
entry.scratch_bytes, 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") 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.""" """Prove bounded V1 control flow and return its deterministic report."""
loop_pairs: dict[int, int] = {} loop_pairs: dict[int, int] = {}
@@ -389,8 +573,8 @@ def control_flow_report(instructions: list[Instruction], entry: Entry) -> dict[s
continue continue
opcode = ControlOpcode(instruction.opcode) opcode = ControlOpcode(instruction.opcode)
if opcode in (ControlOpcode.JUMP_FWD, ControlOpcode.CALL_FWD): if opcode in (ControlOpcode.JUMP_FWD, ControlOpcode.CALL_FWD):
target = pc + 1 + instruction.immediate branch_target = pc + 1 + instruction.immediate
if target > entry.last_pc: if branch_target > entry.last_pc:
raise ValueError(f"entry {entry.format_id} branch at PC {pc} leaves its range") raise ValueError(f"entry {entry.format_id} branch at PC {pc} leaves its range")
elif opcode == ControlOpcode.LOOP_SETUP: elif opcode == ControlOpcode.LOOP_SETUP:
slot = instruction.aux & 3 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): if opcode in (ControlOpcode.END, ControlOpcode.TRAP):
terminals.add(pc) terminals.add(pc)
elif opcode == ControlOpcode.JUMP_FWD: elif opcode == ControlOpcode.JUMP_FWD:
target = pc + 1 + instruction.immediate branch_target = pc + 1 + instruction.immediate
successors.append((target, returns, loops)) successors.append((branch_target, returns, loops))
edges.add((pc, target, "jump_true")) edges.add((pc, branch_target, "jump_true"))
elif opcode == ControlOpcode.CALL_FWD: elif opcode == ControlOpcode.CALL_FWD:
if len(returns) == 4: if len(returns) == 4:
raise ValueError(f"entry {entry.format_id} can exceed four nested calls") raise ValueError(f"entry {entry.format_id} can exceed four nested calls")
target = pc + 1 + instruction.immediate branch_target = pc + 1 + instruction.immediate
successors.append((target, (*returns, pc + 1), loops)) successors.append((branch_target, (*returns, pc + 1), loops))
maximum_call_depth = max(maximum_call_depth, len(returns) + 1) 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: elif opcode == ControlOpcode.RET:
if not returns: if not returns:
raise ValueError(f"entry {entry.format_id} can return with an empty stack") 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: elif opcode == ControlOpcode.NOP:
successors.append((pc + 1, returns, loops)) successors.append((pc + 1, returns, loops))
edges.add((pc, pc + 1, "fallthrough")) 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: else:
raise ValueError("WAIT requires a primitive unavailable in P3") raise ValueError(f"WAIT requires a primitive unavailable in {target.upper()}")
pending.extend(successors) pending.extend(successors)
if len(visited) > APUMC_MAX_INSTRUCTIONS * 64: if len(visited) > APUMC_MAX_INSTRUCTIONS * 64:
raise ValueError(f"entry {entry.format_id} control-flow proof exceeded its bound") 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.""" """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: if not 1 <= len(instructions) <= APUMC_MAX_INSTRUCTIONS:
raise ValueError("instruction count must be 1..2048") 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: if len(entries) != APUMC_ENTRY_COUNT:
raise ValueError("APUMC V1 requires exactly three entries") raise ValueError("APUMC V1 requires exactly three entries")
for index, entry in enumerate(entries): for index, entry in enumerate(entries):
if entry.format_id != index: if entry.format_id != index:
raise ValueError("entry descriptor order must be WAV, MP3, FLAC") 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: for instruction in instructions:
validate_p3_instruction(instruction) validate_instruction(instruction, target)
for entry in entries: 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) descriptor = b"".join(struct.pack("<8I", *entry.words()) for entry in entries)
padding = bytes(APUMC_INSTRUCTION_OFFSET - APUMC_HEADER_BYTES - len(descriptor)) padding = bytes(APUMC_INSTRUCTION_OFFSET - APUMC_HEADER_BYTES - len(descriptor))
encoded = b"".join(struct.pack("<Q", instruction.encode()) for instruction in instructions) 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) total_bytes = APUMC_HEADER_BYTES + len(payload)
crc = crc32_iso_hdlc(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( header = struct.pack(
"<16I", "<16I",
APUMC_MAGIC, APUMC_MAGIC,
@@ -543,12 +762,12 @@ def build_apumc(instructions: list[Instruction], entries: list[Entry], build_id:
total_bytes, total_bytes,
len(instructions), len(instructions),
APUMC_INSTRUCTION_OFFSET, APUMC_INSTRUCTION_OFFSET,
0, table_offset,
0, len(table_payload),
APUMC_HEADER_BYTES, APUMC_HEADER_BYTES,
APUMC_ENTRY_COUNT, APUMC_ENTRY_COUNT,
APUMC_P3_CAPABILITY_MASK, required_mask,
0, scratch_end,
crc, crc,
build_id & 0xFFFFFFFF, build_id & 0xFFFFFFFF,
(build_id >> 32) & 0xFFFFFFFF, (build_id >> 32) & 0xFFFFFFFF,
@@ -558,7 +777,10 @@ def build_apumc(instructions: list[Instruction], entries: list[Entry], build_id:
return header + payload 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: if len(bundle) < APUMC_HEADER_BYTES:
raise ValueError("bundle is smaller than the APUMC header") raise ValueError("bundle is smaller than the APUMC header")
header = struct.unpack_from("<16I", bundle) 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 entry_end = entry_offset + APUMC_ENTRY_COUNT * APUMC_ENTRY_BYTES
if not 1 <= instruction_count <= APUMC_MAX_INSTRUCTIONS: if not 1 <= instruction_count <= APUMC_MAX_INSTRUCTIONS:
raise ValueError("APUMC instruction count is invalid") raise ValueError("APUMC instruction count is invalid")
if ( common_range_error = (
instruction_offset < APUMC_HEADER_BYTES instruction_offset < APUMC_HEADER_BYTES
or instruction_offset % 64 != 0 or instruction_offset % 64 != 0
or instruction_end > len(bundle) 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 entry_end > len(bundle)
or not (instruction_end <= entry_offset or entry_end <= instruction_offset) or not (instruction_end <= entry_offset or entry_end <= instruction_offset)
or table_offset % 4 != 0 or table_offset % 4 != 0
or table_bytes != 0 )
or table_offset != 0 table_end = table_offset + table_bytes
or header[9] != 0 if common_range_error:
or header[10] != 0 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") 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]: if crc32_iso_hdlc(bundle[APUMC_HEADER_BYTES:]) != header[11]:
raise ValueError("APUMC CRC mismatch") raise ValueError("APUMC CRC mismatch")
entries = [] entries = []
for index in range(APUMC_ENTRY_COUNT): for index in range(APUMC_ENTRY_COUNT):
words = struct.unpack_from("<8I", bundle, entry_offset + index * APUMC_ENTRY_BYTES) words = struct.unpack_from("<8I", bundle, entry_offset + index * APUMC_ENTRY_BYTES)
if ( reserved_error = (
words[0] >> 15 words[0] >> 15
or (words[1] & 0xF800F800) or (words[1] & 0xF800F800)
or words[2]
or words[3]
or words[4] >> 16 or words[4] >> 16
or words[5] >> 24 or words[5] >> 24
or words[6] or words[2] >> 17
or words[7] or words[3] >> 17
): )
raise ValueError(f"APUMC entry {index} has invalid reserved or P3 fields") 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( entries.append(
Entry( Entry(
format_id=words[0] & 0xF, 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]) * ( occupied[instruction_offset:instruction_end] = bytes([1]) * (
instruction_end - instruction_offset 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]): if any(byte for index, byte in enumerate(bundle) if not occupied[index]):
raise ValueError("APUMC padding must be zero") raise ValueError("APUMC padding must be zero")
for index, entry in enumerate(entries): for index, entry in enumerate(entries):
if entry.format_id != index: if entry.format_id != index:
raise ValueError("entry descriptor order must be WAV, MP3, FLAC") 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: for instruction in instructions:
validate_p3_instruction(instruction) validate_instruction(instruction, target)
for entry in entries: 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 return instructions, entries, header
+129 -15
View File
@@ -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 from __future__ import annotations
import argparse import argparse
import json import json
import re import re
import struct
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from apu_isa import ( from apu_isa import (
CONTROL_OPCODES, CONTROL_OPCODES,
DEFERRED_OPCODES,
PREDICATES, PREDICATES,
SCALAR_OPCODES, SCALAR_OPCODES,
APUMC_TARGETS,
BitstreamOpcode,
ControlOpcode, ControlOpcode,
EntropyOpcode,
Entry, Entry,
Instruction, Instruction,
InstructionClass, InstructionClass,
KernelOpcode,
LocalOpcode,
ScalarOpcode, ScalarOpcode,
abi_manifest, abi_manifest,
build_apumc, build_apumc,
@@ -31,6 +38,8 @@ class Assembly:
instructions: list[Instruction] instructions: list[Instruction]
entries: list[Entry] entries: list[Entry]
build_id: int build_id: int
target: str
table_payload: bytes
def _number(token: str) -> int: 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]) values["aux"] = _number(args[0])
return Instruction(InstructionClass.CONTROL, opcode, **values) return Instruction(InstructionClass.CONTROL, opcode, **values)
if mnemonic not in SCALAR_OPCODES: if mnemonic in SCALAR_OPCODES:
raise ValueError(f"unknown mnemonic {mnemonic}")
opcode = ScalarOpcode(SCALAR_OPCODES[mnemonic]) opcode = ScalarOpcode(SCALAR_OPCODES[mnemonic])
values = {"predicate": predicate} 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}")
if opcode == ScalarOpcode.MOV: if opcode == ScalarOpcode.MOV:
if len(args) != 2: if len(args) != 2:
raise ValueError("mov takes two registers") 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) 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 = [] lines = []
labels: dict[str, int] = {} labels: dict[str, int] = {}
directives: list[list[str]] = [] directives: list[list[str]] = []
@@ -149,11 +250,14 @@ def assemble(source: str) -> Assembly:
build_id = 0 build_id = 0
entry_directives = [] entry_directives = []
table_words: list[int] = []
for directive in directives: for directive in directives:
if directive[0] == ".build_id" and len(directive) == 2: if directive[0] == ".build_id" and len(directive) == 2:
build_id = _number(directive[1]) 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:]) 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: else:
raise ValueError(f"invalid assembler directive {' '.join(directive)}") raise ValueError(f"invalid assembler directive {' '.join(directive)}")
if len(entry_directives) != 3: if len(entry_directives) != 3:
@@ -169,21 +273,26 @@ def assemble(source: str) -> Assembly:
entries = [] entries = []
for values in entry_directives: for values in entry_directives:
try: try:
entries.append( optional = [0, 0, 0, 0, 0] if len(values) == 6 else [_number(item) for item in values[6:]]
Entry( entries.append(Entry(
format_id=_number(values[0]), format_id=_number(values[0]),
entry_pc=labels[values[1]], entry_pc=labels[values[1]],
first_pc=labels[values[2]], first_pc=labels[values[2]],
last_pc=labels[values[3]], last_pc=labels[values[3]],
max_loop_count=_number(values[4]), max_loop_count=_number(values[4]),
max_retired=_number(values[5]), 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: except KeyError as error:
raise ValueError(f"unknown entry label {error.args[0]}") from error raise ValueError(f"unknown entry label {error.args[0]}") from error
entries.sort(key=lambda entry: entry.format_id) entries.sort(key=lambda entry: entry.format_id)
bundle = build_apumc(instructions, entries, build_id) table_payload = b"".join(struct.pack("<I", word) for word in table_words)
return Assembly(bundle, dict(sorted(labels.items())), instructions, entries, build_id) 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]: def _artifact_data(assembly: Assembly) -> dict[str, object]:
@@ -199,6 +308,7 @@ def _artifact_data(assembly: Assembly) -> dict[str, object]:
def main() -> int: def main() -> int:
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("source", type=Path) 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("-o", "--output", type=Path, required=True)
parser.add_argument("--symbols", type=Path) parser.add_argument("--symbols", type=Path)
parser.add_argument("--cfg-report", 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("--trace-input", type=Path)
parser.add_argument("--abi-input-manifest", type=Path) parser.add_argument("--abi-input-manifest", type=Path)
args = parser.parse_args() 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) args.output.write_bytes(assembly.bundle)
artifacts = { artifacts = {
args.symbols: assembly.symbols, args.symbols: assembly.symbols,
@@ -214,12 +324,16 @@ def main() -> int:
**_artifact_data(assembly), **_artifact_data(assembly),
"entries": [entry.__dict__ for entry in assembly.entries], "entries": [entry.__dict__ for entry in assembly.entries],
"control_flow": [ "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: { args.primitive_manifest: {
"implemented_mask": 0, "target": assembly.target,
"required_mask": 0, "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], "entry_masks": [entry.primitive_mask for entry in assembly.entries],
}, },
args.trace_input: { args.trace_input: {
+680
View File
@@ -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
View File
@@ -19,7 +19,10 @@ tooling.
- `test_apu.py` checks the fail-closed APU APB4/IRQ shell plus the P2 private - `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; DMA, ring scheduler, stream router, Gateway A, and verification-only backend;
`test_apu_register_parity.py` keeps its handwritten RTL/C ABI and matrix `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 - `test_user_ip_register_parity.py` keeps the integrated slot 1 timer and slot
2 GPIO register offsets synchronized with their application-owned C 2 GPIO register offsets synchronized with their application-owned C
definitions and checks their extension-manifest slot assignments. definitions and checks their extension-manifest slot assignments.
+1
View File
@@ -221,6 +221,7 @@ module apu_p3_microcode_tb;
.fault_addr_o (seq_fault_addr), .fault_addr_o (seq_fault_addr),
.fault_detail_o (seq_fault_detail), .fault_detail_o (seq_fault_detail),
.perf_retired_o (seq_perf_retired), .perf_retired_o (seq_perf_retired),
.launch_epoch_o (),
.idle_o (seq_idle) .idle_o (seq_idle)
); );
+306
View File
@@ -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
+766
View File
@@ -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
+380
View File
@@ -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
+2 -2
View File
@@ -191,8 +191,8 @@ module apu_reg_tb;
s_reg_count = 0; s_reg_count = 0;
add_register(`APB4_APU__IP_ID, AccessRo, 32'h4150_5530, 1'b0); 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__IP_VERSION, AccessRo, 32'h0001_0000, 1'b0);
add_register(`APB4_APU__CAPABILITY0, AccessRo, 32'h0000_0098, 1'b0); add_register(`APB4_APU__CAPABILITY0, AccessRo, 32'h0000_0198, 1'b0);
add_register(`APB4_APU__CAPABILITY1, AccessRo, 32'h0000_0010, 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__COMMAND, AccessWo, 32'd0, 1'b1);
add_register(`APB4_APU__STATUS, AccessRo, 32'h0000_0100, 1'b0); add_register(`APB4_APU__STATUS, AccessRo, 32'h0000_0100, 1'b0);
add_register(`APB4_APU__IRQ_STATE, AccessRw, 32'd0, 1'b0); add_register(`APB4_APU__IRQ_STATE, AccessRw, 32'd0, 1'b0);
+37 -1
View File
@@ -51,6 +51,14 @@ def test_apu_p1_apb_register_shell(tmp_path: Path) -> None:
str(multimedia / "apu_stream_router.sv"), str(multimedia / "apu_stream_router.sv"),
str(multimedia / "apu_control_store.sv"), str(multimedia / "apu_control_store.sv"),
str(multimedia / "apu_microcode_loader.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_codec_sequencer.sv"),
str(multimedia / "apu_reg.sv"), str(multimedia / "apu_reg.sv"),
str(multimedia / "apb4_apu.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_stream_router.sv"),
str(multimedia / "apu_control_store.sv"), str(multimedia / "apu_control_store.sv"),
str(multimedia / "apu_microcode_loader.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_codec_sequencer.sv"),
str(multimedia / "apu_reg.sv"), str(multimedia / "apu_reg.sv"),
str(multimedia / "apb4_apu.sv"), str(multimedia / "apb4_apu.sv"),
@@ -532,6 +548,14 @@ done:
str(multimedia / "apu_stream_router.sv"), str(multimedia / "apu_stream_router.sv"),
str(multimedia / "apu_control_store.sv"), str(multimedia / "apu_control_store.sv"),
str(multimedia / "apu_microcode_loader.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_codec_sequencer.sv"),
str(multimedia / "apu_reg.sv"), str(multimedia / "apu_reg.sv"),
str(multimedia / "apb4_apu.sv"), str(multimedia / "apb4_apu.sv"),
@@ -575,7 +599,7 @@ done:
assert "APU-P3 APB, DMA, loader integration passed" in result.stdout 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") 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") 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") 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 "32'h0000_0029" in register_block
assert "s_merged_write != 32'd0" in register_block assert "s_merged_write != 32'd0" in register_block
assert "apu_interpreter" not in product_filelist 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 "apu_p3_microcode_tb" not in product_filelist
assert "VerificationCrcBypass" not in product_filelist assert "VerificationCrcBypass" not in product_filelist
assert ".VerificationCrcBypass" not in top assert ".VerificationCrcBypass" not in top
assert "VerificationControlFlowBypass" not in product_filelist assert "VerificationControlFlowBypass" not in product_filelist
assert ".VerificationControlFlowBypass" not in top 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"): for forbidden in ("hazard3", "vexiiriscv", "rv32", "dsp_core"):
assert forbidden not in top.lower() assert forbidden not in top.lower()
File diff suppressed because it is too large. Load diff
+6 -2
View File
@@ -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 from __future__ import annotations
@@ -72,7 +72,7 @@ def test_apu_register_matrix_covers_each_offset_once() -> None:
assert set(covered) == set(expected) 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() rtl = _rtl_values()
manifest = abi_manifest() manifest = abi_manifest()
exact = { 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_BYTES": manifest["apumc"]["entry_bytes"],
"APUMC_ENTRY_COUNT": manifest["apumc"]["entry_count"], "APUMC_ENTRY_COUNT": manifest["apumc"]["entry_count"],
"APUMC_MAX_INSTRUCTIONS": manifest["apumc"]["max_instructions"], "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(): for name, value in manifest["classes"].items():
exact[f"MC_CLASS_{name.upper()}"] = value exact[f"MC_CLASS_{name.upper()}"] = value
+21
View File
@@ -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" onchip_ram_filelist = tmp_path / "onchip_ram.fl"
opipsram_filelist = tmp_path / "opipsram.fl" opipsram_filelist = tmp_path / "opipsram.fl"
apu_filelist = tmp_path / "apu.fl" apu_filelist = tmp_path / "apu.fl"
apu_primitives_filelist = tmp_path / "apu_primitives.fl"
apu_loader_filelist = tmp_path / "apu_loader.fl" apu_loader_filelist = tmp_path / "apu_loader.fl"
apu_sequencer_filelist = tmp_path / "apu_sequencer.fl" apu_sequencer_filelist = tmp_path / "apu_sequencer.fl"
gateway_a_filelist = tmp_path / "gateway_a.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 "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", 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( assert generate_formal_filelist(
"apu_loader", apu_loader_filelist, memory_map, topology, user_extensions "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) onchip_ram = parse_filelists([onchip_ram_filelist], require_files=False)
opipsram = parse_filelists([opipsram_filelist], require_files=False) opipsram = parse_filelists([opipsram_filelist], require_files=False)
apu = parse_filelists([apu_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_loader = parse_filelists([apu_loader_filelist], require_files=False)
apu_sequencer = parse_filelists([apu_sequencer_filelist], require_files=False) apu_sequencer = parse_filelists([apu_sequencer_filelist], require_files=False)
gateway_a = parse_filelists([gateway_a_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/mini/formal/opipsram_formal.sv" in opipsram.files
assert ROOT / "rtl/ip/multimedia/apu_dma.sv" in apu.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/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/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/mini/formal/apu_loader_formal.sv" in apu_loader.files
assert ROOT / "rtl/ip/multimedia/apu_codec_sequencer.sv" in apu_sequencer.files assert ROOT / "rtl/ip/multimedia/apu_codec_sequencer.sv" in apu_sequencer.files