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
+8452 -144

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_COUNT 3U
#define RS_APU_ABI_APUMC_MAX_INSTRUCTIONS 2048U
#define RS_APU_ABI_APUMC_P3_PRIMITIVE_MASK UINT32_C(0x00000000)
#define RS_APU_ABI_APUMC_P4_PRIMITIVE_MASK UINT32_C(0x0000FFFF)
#define RS_APU_ABI_LOCAL_DATA_BYTES 114688U
#define RS_APU_ABI_LOCAL_CODEC_BYTES 40960U
#define RS_APU_ABI_LOCAL_TABLE_SCRATCH_BYTES 24576U
#define RS_APU_ABI_LOCAL_INPUT_BASE 24576U
#define RS_APU_ABI_LOCAL_OUTPUT_BASE 32768U
#define RS_APU_ABI_LOCAL_KWS_BASE 40960U
#define RS_APU_ABI_LOCAL_INTERNAL_BASE 106496U
#define RS_APU_ABI_APUMC_HEADER_MAGIC 0U
#define RS_APU_ABI_APUMC_HEADER_ABI 1U
#define RS_APU_ABI_APUMC_HEADER_TOTAL_BYTES 2U
@@ -439,8 +448,8 @@
#define RS_APU_IP_ID_VALUE UINT32_C(0x41505530)
#define RS_APU_IP_VERSION_VALUE UINT32_C(0x00010000)
#define RS_APU_IP_VERSION_MAJOR_MASK UINT32_C(0xFFFF0000)
#define RS_APU_CAPABILITY0_IMPLEMENTED UINT32_C(0x00000098)
#define RS_APU_CAPABILITY1_IMPLEMENTED UINT32_C(0x00000010)
#define RS_APU_CAPABILITY0_IMPLEMENTED UINT32_C(0x00000198)
#define RS_APU_CAPABILITY1_IMPLEMENTED UINT32_C(0x01827010)
#define RS_APU_DIGEST_IMPLEMENTED UINT32_C(0x00000000)
#define RS_APU_IRQ_ALL UINT32_C(0x000007FF)
+4 -3
View File
@@ -119,9 +119,10 @@ ownership. Its implemented limits, handwritten ABI, measured performance, and
commercial release gates are documented in
[Baseline JPEG Codec](../docs/ip/jpeg.md).
The Mini Audio Processing Unit has a frozen coreless architecture. APU-P2 adds
the private AXI4 DMA, descriptor scheduler, fair Gateway A arbitration, and
production stream router/FIFOs to the fail-closed APB4 shell at `APB4_APU`.
The Mini Audio Processing Unit has a frozen coreless architecture. APU-P4 adds
the private AXI4 DMA and scheduler, microcode loader/sequencer, 112 KiB local
store, primitive FIFOs, and class-2 through class-5 bitstream, entropy, local,
and fixed-point DSP engines to the fail-closed APB4 shell at `APB4_APU`.
Resource Controller index 7 and exclusive LP IRQ31/HP PLIC source10 routing
remain fixed. Public jobs and APU stream routes remain disabled until their
later-phase engines exist. The complete ABI, phase order, and evidence gates
+163 -15
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][15:0] s_mc_entry_max_loop;
logic [2:0][23:0] s_mc_entry_max_retired;
logic [2:0][16:0] s_mc_entry_scratch_base, s_mc_entry_scratch_bytes;
logic [2:0][31:0] s_mc_entry_primitive_mask;
logic [2:0][15:0] s_mc_entry_table_offset, s_mc_entry_table_bytes;
logic [15:0] s_mc_table_bytes;
logic s_store_active, s_store_read, s_store_write, s_store_valid;
logic [10:0] s_store_addr;
logic [63:0] s_store_write_data, s_store_read_data;
logic s_seq_fetch, s_seq_fetch_valid, s_seq_trapped, s_seq_trap_event;
logic s_seq_abort_done, s_seq_fault_valid, s_seq_idle;
logic s_seq_abort_done, s_seq_fault_valid, s_seq_launch_epoch, s_seq_idle;
logic [10:0] s_seq_fetch_addr;
logic [63:0] s_seq_fetch_data, s_seq_perf_retired;
logic [31:0] s_seq_status, s_seq_retired;
@@ -61,6 +65,35 @@ module apb4_apu (
logic [ 1:0] s_seq_fault_resp;
logic [ 7:0] s_seq_fault_index;
logic [31:0] s_seq_fault_addr, s_seq_fault_detail;
logic s_primitive_req_valid, s_primitive_req_ready, s_primitive_result_valid;
logic [63:0] s_primitive_instruction;
logic [31:0] s_primitive_source0, s_primitive_source1, s_primitive_destination;
logic [3:0] s_primitive_result_dst;
logic [3:0][31:0] s_primitive_result_data;
logic [2:0] s_primitive_result_words;
logic s_primitive_result_kernel, s_primitive_err, s_primitive_kernel_done;
logic [5:0] s_primitive_err_code;
logic [3:0] s_primitive_err_stage;
logic [7:0] s_primitive_err_reason;
logic [16:0] s_active_scratch_base, s_active_scratch_bytes;
logic [31:0] s_active_primitive_mask;
logic [15:0] s_active_table_offset, s_active_table_bytes;
logic s_primitive_busy, s_primitive_input_exhausted;
logic s_primitive_input_ready, s_primitive_output_ready;
logic s_primitive_input_accept_unused, s_primitive_output_valid_unused;
logic [6:0] s_primitive_input_count_unused, s_primitive_output_count_unused;
logic s_primitive_kernel_busy_unused;
logic [31:0] s_primitive_cycles_unused;
logic [40:0] s_primitive_output_data_unused;
logic s_local_loader_write, s_local_loader_ready;
logic [16:0] s_local_loader_addr;
logic [31:0] s_local_loader_data;
logic [ 3:0] s_local_loader_strb;
logic s_local_codec_req, s_local_codec_write, s_local_codec_ready;
logic [16:0] s_local_codec_addr;
logic [31:0] s_local_codec_write_data, s_local_codec_read_data;
logic [3:0] s_local_codec_strb;
logic s_local_codec_valid, s_local_codec_err;
logic s_input_watermark_evt, s_output_watermark_evt, s_xrun_evt;
logic s_ring_event, s_ring_err, s_abort_done;
logic [ 5:0] s_ring_err_code;
@@ -164,7 +197,7 @@ module apb4_apu (
assign s_tx_route_apu = s_stream_route[1:0] == 2'd1;
assign s_rx_route_apu = s_stream_route[3:2] == 2'd1;
assign s_transport_idle = s_scheduler_idle && !s_dma_busy && s_stream_idle && s_mc_idle &&
s_seq_idle;
s_seq_idle && !s_primitive_busy;
assign idle_o = s_transport_idle;
assign s_resource_reset_request = s_resource_reset_pending_q ||
(resource_reset_i && !s_resource_reset_seen_q);
@@ -323,7 +356,9 @@ module apb4_apu (
.axi4 (axi4)
);
apu_microcode_loader u_microcode_loader (
apu_microcode_loader #(
.EnableP4(1'b1)
) u_microcode_loader (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_microcode_load),
@@ -334,10 +369,10 @@ module apb4_apu (
.image_addr_i (s_mc_image_addr),
.image_size_i (s_mc_image_size),
.expected_crc_i (s_mc_expected_crc),
.dma_request_valid_o(s_mc_dma_req_valid),
.dma_request_ready_i(s_mc_dma_req_ready),
.dma_request_valid_o (s_mc_dma_req_valid),
.dma_request_ready_i (s_mc_dma_req_ready),
.dma_request_addr_o (s_mc_dma_req_addr),
.dma_request_bytes_o(s_mc_dma_req_bytes),
.dma_request_bytes_o (s_mc_dma_req_bytes),
.dma_data_i (u_dma_read_axis.tdata),
.dma_keep_i (u_dma_read_axis.tkeep),
.dma_last_i (u_dma_read_axis.tlast),
@@ -356,6 +391,11 @@ module apb4_apu (
.store_data_o (s_store_write_data),
.store_data_i (s_store_read_data),
.store_valid_i (s_store_valid),
.local_write_o (s_local_loader_write),
.local_addr_o (s_local_loader_addr),
.local_data_o (s_local_loader_data),
.local_strb_o (s_local_loader_strb),
.table_bytes_o (s_mc_table_bytes),
.stat_o (s_mc_status),
.abi_o (s_mc_abi),
.build_id_o (s_mc_build_id),
@@ -366,7 +406,12 @@ module apb4_apu (
.entry_first_o (s_mc_entry_first),
.entry_last_o (s_mc_entry_last),
.entry_max_loop_o (s_mc_entry_max_loop),
.entry_max_retired_o(s_mc_entry_max_retired),
.entry_max_retired_o (s_mc_entry_max_retired),
.entry_scratch_base_o (s_mc_entry_scratch_base),
.entry_scratch_bytes_o (s_mc_entry_scratch_bytes),
.entry_primitive_mask_o(s_mc_entry_primitive_mask),
.entry_table_offset_o (s_mc_entry_table_offset),
.entry_table_bytes_o (s_mc_entry_table_bytes),
.load_done_o (s_mc_load_done),
.abort_done_o (s_mc_abort_done),
.fault_valid_o (s_mc_fault_valid),
@@ -395,7 +440,80 @@ module apb4_apu (
.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),
.rst_n_i (rst_n_i),
.soft_reset_i (s_soft_reset),
@@ -411,11 +529,16 @@ module apb4_apu (
.entry_last_i (s_mc_entry_last),
.entry_max_loop_i (s_mc_entry_max_loop),
.entry_max_retired_i (s_mc_entry_max_retired),
.input_exhausted_i (1'b1),
.input_ready_i (1'b0),
.output_ready_i (1'b1),
.kernel_done_i (1'b0),
.transport_idle_success_i(!s_dma_busy && !s_dma_err),
.entry_scratch_base_i (s_mc_entry_scratch_base),
.entry_scratch_bytes_i (s_mc_entry_scratch_bytes),
.entry_primitive_mask_i (s_mc_entry_primitive_mask),
.entry_table_offset_i (s_mc_entry_table_offset),
.entry_table_bytes_i (s_mc_entry_table_bytes),
.input_exhausted_i (s_primitive_input_exhausted),
.input_ready_i (s_primitive_input_ready),
.output_ready_i (s_primitive_output_ready),
.kernel_done_i (s_primitive_kernel_done),
.transport_idle_success_i (!s_dma_busy && !s_dma_err),
.stall_i (1'b0),
.cause_valid_i (1'b0),
.cause_code_i (6'd0),
@@ -424,6 +547,21 @@ module apb4_apu (
.cause_index_i (8'd0),
.cause_addr_i (32'd0),
.cause_detail_i (32'd0),
.primitive_req_valid_o (s_primitive_req_valid),
.primitive_req_ready_i (s_primitive_req_ready),
.primitive_instruction_o (s_primitive_instruction),
.primitive_source0_o (s_primitive_source0),
.primitive_source1_o (s_primitive_source1),
.primitive_destination_o (s_primitive_destination),
.primitive_result_valid_i (s_primitive_result_valid),
.primitive_result_dst_i (s_primitive_result_dst),
.primitive_result_data_i (s_primitive_result_data),
.primitive_result_words_i (s_primitive_result_words),
.primitive_result_kernel_i(s_primitive_result_kernel),
.primitive_error_i (s_primitive_err),
.primitive_error_code_i (s_primitive_err_code),
.primitive_error_stage_i (s_primitive_err_stage),
.primitive_error_reason_i (s_primitive_err_reason),
.fetch_o (s_seq_fetch),
.fetch_addr_o (s_seq_fetch_addr),
.fetch_data_i (s_seq_fetch_data),
@@ -442,6 +580,12 @@ module apb4_apu (
.fault_addr_o (s_seq_fault_addr),
.fault_detail_o (s_seq_fault_detail),
.perf_retired_o (s_seq_perf_retired),
.active_scratch_base_o (s_active_scratch_base),
.active_scratch_bytes_o (s_active_scratch_bytes),
.active_primitive_mask_o (s_active_primitive_mask),
.active_table_offset_o (s_active_table_offset),
.active_table_bytes_o (s_active_table_bytes),
.launch_epoch_o (s_seq_launch_epoch),
.idle_o (s_seq_idle)
);
@@ -576,10 +720,14 @@ module apb4_apu (
end
end
logic s_unused_pending;
logic s_unused_pending, s_unused_p3, s_unused_p4;
assign s_unused_pending = s_dma_input_pending ^ s_dma_output_pending ^ s_dma_aborted ^
s_reg_idle_unused ^ s_backend_job_valid_unused ^ s_backend_resp_ready_unused ^
^s_backend_descriptor_unused ^ ^s_backend_index_unused ^ ^s_ring_coalesce[15:8];
logic s_unused_p3;
assign s_unused_p3 = ^s_seq_gpr;
assign s_unused_p4 = ^s_active_primitive_mask ^ s_local_loader_ready ^ s_local_codec_ready ^
s_primitive_input_accept_unused ^ s_primitive_output_valid_unused ^
^s_primitive_cycles_unused ^ ^s_primitive_output_data_unused ^
^s_primitive_input_count_unused ^ ^s_primitive_output_count_unused ^
s_primitive_kernel_busy_unused;
endmodule
+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"
module apu_codec_sequencer (
module apu_codec_sequencer #(
parameter bit EnableP4 = 1'b0
) (
// verilog_format: off -- preserve launch, predicate, store, and result columns
input logic clk_i,
input logic rst_n_i,
@@ -20,6 +22,11 @@ module apu_codec_sequencer (
input logic [2:0][10:0] entry_last_i,
input logic [2:0][15:0] entry_max_loop_i,
input logic [2:0][23:0] entry_max_retired_i,
input logic [2:0][16:0] entry_scratch_base_i,
input logic [2:0][16:0] entry_scratch_bytes_i,
input logic [2:0][31:0] entry_primitive_mask_i,
input logic [2:0][15:0] entry_table_offset_i,
input logic [2:0][15:0] entry_table_bytes_i,
input logic input_exhausted_i,
input logic input_ready_i,
input logic output_ready_i,
@@ -33,6 +40,21 @@ module apu_codec_sequencer (
input logic [7:0] cause_index_i,
input logic [31:0] cause_addr_i,
input logic [31:0] cause_detail_i,
output logic primitive_req_valid_o,
input logic primitive_req_ready_i,
output logic [63:0] primitive_instruction_o,
output logic [31:0] primitive_source0_o,
output logic [31:0] primitive_source1_o,
output logic [31:0] primitive_destination_o,
input logic primitive_result_valid_i,
input logic [ 3:0] primitive_result_dst_i,
input logic [ 3:0][31:0] primitive_result_data_i,
input logic [ 2:0] primitive_result_words_i,
input logic primitive_result_kernel_i,
input logic primitive_error_i,
input logic [ 5:0] primitive_error_code_i,
input logic [ 3:0] primitive_error_stage_i,
input logic [ 7:0] primitive_error_reason_i,
output logic fetch_o,
output logic [10:0] fetch_addr_o,
input logic [63:0] fetch_data_i,
@@ -51,16 +73,23 @@ module apu_codec_sequencer (
output logic [31:0] fault_addr_o,
output logic [31:0] fault_detail_o,
output logic [63:0] perf_retired_o,
output logic [16:0] active_scratch_base_o,
output logic [16:0] active_scratch_bytes_o,
output logic [31:0] active_primitive_mask_o,
output logic [15:0] active_table_offset_o,
output logic [15:0] active_table_bytes_o,
output logic launch_epoch_o,
output logic idle_o
// verilog_format: on
);
import apu_microcode_pkg::*;
typedef enum logic [1:0] {
typedef enum logic [2:0] {
Idle,
FetchRequest,
FetchWait,
Execute
Execute,
PrimitiveWait
} sequencer_state_e;
sequencer_state_e s_state_q;
@@ -76,12 +105,21 @@ module apu_codec_sequencer (
logic [23:0] s_max_retired_q, s_frame_retired_q;
logic [31:0] s_watchdog_q;
logic [63:0] s_instruction_q;
logic [16:0] s_scratch_base_q, s_scratch_bytes_q;
logic [31:0] s_primitive_mask_q;
logic [15:0] s_table_offset_q, s_table_bytes_q;
logic s_kernel_pending_q, s_kernel_done_q;
logic [ 3:0] s_kernel_dst_q;
logic s_kernel_err_q;
logic [ 5:0] s_kernel_err_code_q;
logic [ 3:0] s_kernel_err_stage_q;
logic [ 7:0] s_kernel_err_reason_q;
logic [31:0] s_stat_q;
logic s_trapped_q;
logic [63:0] s_perf_retired_q;
logic [3:0] s_class, s_opcode, s_predicate, s_dst, s_src0, s_src1;
logic [5:0] s_aux;
logic [7:0] s_aux;
logic [31:0] s_immediate, s_source0, s_source1;
logic s_predicate_true;
logic [11:0] s_forward_target, s_loop_target;
@@ -89,6 +127,8 @@ module apu_codec_sequencer (
logic s_trap_now;
logic [ 7:0] s_trap_reason;
logic [31:0] s_trap_detail;
logic s_pending_dependency, s_wait_stall, s_primitive_stall, s_execute_stall;
logic s_primitive_class;
assign s_class = s_instruction_q[63:60];
assign s_opcode = s_instruction_q[59:56];
@@ -96,7 +136,7 @@ module apu_codec_sequencer (
assign s_dst = s_instruction_q[51:48];
assign s_src0 = s_instruction_q[47:44];
assign s_src1 = s_instruction_q[43:40];
assign s_aux = s_instruction_q[37:32];
assign s_aux = s_instruction_q[39:32];
assign s_immediate = s_instruction_q[31:0];
assign s_source0 = s_gpr_q[s_src0];
assign s_source1 = s_gpr_q[s_src1];
@@ -110,6 +150,20 @@ module apu_codec_sequencer (
assign trapped_o = s_trapped_q;
assign perf_retired_o = s_perf_retired_q;
assign idle_o = s_state_q == Idle;
assign active_scratch_base_o = s_scratch_base_q;
assign active_scratch_bytes_o = s_scratch_bytes_q;
assign active_primitive_mask_o = s_primitive_mask_q;
assign active_table_offset_o = s_table_offset_q;
assign active_table_bytes_o = s_table_bytes_q;
assign launch_epoch_o = (s_state_q == Idle) && launch_i && image_valid_i &&
(launch_entry_i < 2'd3);
assign primitive_instruction_o = s_instruction_q;
assign primitive_source0_o = s_source0;
assign primitive_source1_o = s_source1;
assign primitive_destination_o = s_gpr_q[s_dst];
assign s_primitive_class = EnableP4 && (s_class >= 4'd2) && (s_class <= 4'd5);
assign primitive_req_valid_o = (s_state_q == Execute) && s_predicate_true &&
s_primitive_class && !s_pending_dependency && !s_kernel_pending_q;
always_comb begin
unique case (s_predicate)
@@ -123,23 +177,87 @@ module apu_codec_sequencer (
4'd7: s_predicate_true = input_exhausted_i;
4'd8: s_predicate_true = input_ready_i;
4'd9: s_predicate_true = output_ready_i;
4'd10: s_predicate_true = kernel_done_i;
4'd10: s_predicate_true = s_kernel_done_q;
4'd11: s_predicate_true = transport_idle_success_i;
default: s_predicate_true = 1'b0;
endcase
end
always_comb begin
s_pending_dependency = 1'b0;
if (s_kernel_pending_q && s_predicate_true) begin
unique case (s_class)
`APB4_APU__MC_CLASS_CONTROL: begin
s_pending_dependency = (s_opcode == `APB4_APU__MC_CONTROL_LOOP_SETUP) &&
(s_src0 == s_kernel_dst_q);
end
`APB4_APU__MC_CLASS_SCALAR: begin
unique case (s_opcode)
`APB4_APU__MC_SCALAR_MOV:
s_pending_dependency = (s_src0 == s_kernel_dst_q) || (s_dst == s_kernel_dst_q);
`APB4_APU__MC_SCALAR_MOVI: s_pending_dependency = s_dst == s_kernel_dst_q;
`APB4_APU__MC_SCALAR_CMP:
s_pending_dependency = (s_src0 == s_kernel_dst_q) || (s_src1 == s_kernel_dst_q);
`APB4_APU__MC_SCALAR_SAT:
s_pending_dependency = (s_src0 == s_kernel_dst_q) || (s_dst == s_kernel_dst_q);
default:
s_pending_dependency = (s_src0 == s_kernel_dst_q) ||
(s_src1 == s_kernel_dst_q) || (s_dst == s_kernel_dst_q);
endcase
end
`APB4_APU__MC_CLASS_BITSTREAM: begin
s_pending_dependency = (s_dst == s_kernel_dst_q) ||
((s_opcode >= `APB4_APU__MC_BITSTREAM_FRAME_SYNC) &&
((s_src0 == s_kernel_dst_q) || (s_src1 == s_kernel_dst_q)));
end
`APB4_APU__MC_CLASS_ENTROPY, `APB4_APU__MC_CLASS_LOCAL: begin
s_pending_dependency = (s_src0 == s_kernel_dst_q) ||
(s_src1 == s_kernel_dst_q) || (s_dst == s_kernel_dst_q) ||
((s_class == `APB4_APU__MC_CLASS_ENTROPY) &&
(s_opcode inside {`APB4_APU__MC_ENTROPY_HUFF_PAIR,
`APB4_APU__MC_ENTROPY_HUFF_QUAD}) &&
((s_dst + 1'b1) == s_kernel_dst_q)) ||
((s_class == `APB4_APU__MC_CLASS_ENTROPY) &&
(s_opcode == `APB4_APU__MC_ENTROPY_HUFF_QUAD) &&
(((s_dst + 4'd2) == s_kernel_dst_q) ||
((s_dst + 4'd3) == s_kernel_dst_q))) ||
((s_class == `APB4_APU__MC_CLASS_LOCAL) &&
(s_opcode == `APB4_APU__MC_LOCAL_FIFO_POP) &&
((s_dst + 1'b1) == s_kernel_dst_q));
end
`APB4_APU__MC_CLASS_KERNEL: s_pending_dependency = 1'b1;
default: s_pending_dependency = 1'b0;
endcase
end
s_wait_stall = EnableP4 && s_predicate_true &&
(s_class == `APB4_APU__MC_CLASS_CONTROL) &&
(s_opcode == `APB4_APU__MC_CONTROL_WAIT) &&
(((s_aux == `APB4_APU__MC_WAIT_KERNEL) && !s_kernel_done_q) ||
((s_aux == `APB4_APU__MC_WAIT_INPUT_FIFO) && !input_ready_i) ||
((s_aux == `APB4_APU__MC_WAIT_OUTPUT_FIFO) && !output_ready_i));
s_primitive_stall = s_primitive_class &&
(s_kernel_pending_q || s_pending_dependency || !primitive_req_ready_i);
s_execute_stall = stall_i || s_wait_stall || s_primitive_stall ||
(s_kernel_pending_q && s_predicate_true &&
(s_class == `APB4_APU__MC_CLASS_CONTROL) &&
(s_opcode == `APB4_APU__MC_CONTROL_END));
end
always_comb begin
s_trap_now = 1'b0;
s_trap_reason = 8'd0;
s_trap_detail = 32'd0;
if (stall_i && (s_watchdog_q + 1'b1 >= timeout_i)) begin
if (s_execute_stall && (s_watchdog_q + 1'b1 >= timeout_i)) begin
s_trap_now = 1'b1;
s_trap_reason = 8'd7;
end else if (!instruction_encoding_valid(s_instruction_q)) begin
s_trap_now = 1'b1;
s_trap_reason = 8'd1;
end else if ((s_class >= 4'd2) || ((s_class == 4'd0) && (s_opcode == 4'd8))) begin
end else if ((!EnableP4 && ((s_class >= 4'd2) ||
((s_class == 4'd0) && (s_opcode == 4'd8)))) ||
(EnableP4 && ((s_class == 4'd6) ||
((s_class == 4'd0) && (s_opcode == 4'd8) &&
!(s_aux inside {8'd1, 8'd2, 8'd3}))))) begin
s_trap_now = 1'b1;
s_trap_reason = 8'd6;
end else if (s_frame_retired_q >= s_max_retired_q) begin
@@ -186,6 +304,8 @@ module apu_codec_sequencer (
(s_loop_target[10:0] < s_first_pc_q);
s_trap_reason = 8'd4;
end
4'd8: begin
end
default: begin
s_trap_now = 1'b1;
s_trap_reason = 8'd1;
@@ -237,7 +357,7 @@ module apu_codec_sequencer (
fault_index_o <= 8'd0; \
fault_addr_o <= {18'd0, s_pc_q, 3'd0}; \
fault_detail_o <= DETAIL; \
s_stat_q <= {11'd0, |s_loop_active_q, stall_i, s_opcode, s_class, s_pc_q}; \
s_stat_q <= {11'd0, |s_loop_active_q, s_execute_stall, s_opcode, s_class, s_pc_q}; \
end
`define RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP \
@@ -275,6 +395,18 @@ module apu_codec_sequencer (
s_frame_retired_q <= 24'd0;
s_watchdog_q <= 32'd0;
s_instruction_q <= 64'd0;
s_scratch_base_q <= 17'd0;
s_scratch_bytes_q <= 17'd0;
s_primitive_mask_q <= 32'd0;
s_table_offset_q <= 16'd0;
s_table_bytes_q <= 16'd0;
s_kernel_pending_q <= 1'b0;
s_kernel_done_q <= 1'b0;
s_kernel_dst_q <= 4'd0;
s_kernel_err_q <= 1'b0;
s_kernel_err_code_q <= 6'd0;
s_kernel_err_stage_q <= 4'd0;
s_kernel_err_reason_q <= 8'd0;
s_stat_q <= 32'd0;
s_trapped_q <= 1'b0;
s_perf_retired_q <= 64'd0;
@@ -291,6 +423,20 @@ module apu_codec_sequencer (
trap_event_o <= 1'b0;
abort_done_o <= 1'b0;
fault_valid_o <= 1'b0;
if (primitive_result_valid_i && primitive_result_kernel_i) begin
s_kernel_pending_q <= 1'b0;
s_kernel_done_q <= !primitive_error_i;
if (primitive_error_i) begin
s_kernel_err_q <= 1'b1;
s_kernel_err_code_q <= primitive_error_code_i;
s_kernel_err_stage_q <= primitive_error_stage_i;
s_kernel_err_reason_q <= primitive_error_reason_i;
end else begin
s_gpr_q[primitive_result_dst_i] <= primitive_result_data_i[0];
end
end else if (kernel_done_i) begin
s_kernel_done_q <= 1'b1;
end
if (counter_clear_i) s_perf_retired_q <= 64'd0;
if (soft_reset_i || resource_reset_i) begin
s_state_q <= Idle;
@@ -306,6 +452,9 @@ module apu_codec_sequencer (
s_stat_q <= 32'd0;
s_trapped_q <= 1'b0;
s_perf_retired_q <= 64'd0;
s_kernel_pending_q <= 1'b0;
s_kernel_done_q <= 1'b0;
s_kernel_err_q <= 1'b0;
end else begin
unique case (s_state_q)
Idle: begin
@@ -322,10 +471,18 @@ module apu_codec_sequencer (
s_last_pc_q <= entry_last_i[launch_entry_i];
s_max_loop_q <= entry_max_loop_i[launch_entry_i];
s_max_retired_q <= entry_max_retired_i[launch_entry_i];
s_scratch_base_q <= entry_scratch_base_i[launch_entry_i];
s_scratch_bytes_q <= entry_scratch_bytes_i[launch_entry_i];
s_primitive_mask_q <= entry_primitive_mask_i[launch_entry_i];
s_table_offset_q <= entry_table_offset_i[launch_entry_i];
s_table_bytes_q <= entry_table_bytes_i[launch_entry_i];
s_frame_retired_q <= 24'd0;
s_watchdog_q <= 32'd0;
s_stat_q <= {21'd0, entry_pc_i[launch_entry_i]};
s_trapped_q <= 1'b0;
s_kernel_pending_q <= 1'b0;
s_kernel_done_q <= 1'b0;
s_kernel_err_q <= 1'b0;
end
end
FetchRequest: begin
@@ -334,6 +491,9 @@ module apu_codec_sequencer (
`RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP;
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 begin
s_watchdog_q <= s_watchdog_q + 1'b1;
@@ -345,6 +505,9 @@ module apu_codec_sequencer (
`RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP;
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 begin
s_watchdog_q <= s_watchdog_q + 1'b1;
@@ -369,13 +532,40 @@ module apu_codec_sequencer (
fault_index_o <= cause_index_i;
fault_addr_o <= cause_addr_i;
fault_detail_o <= cause_detail_i;
s_stat_q <= {11'd0, |s_loop_active_q, stall_i, s_opcode, s_class, s_pc_q};
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
`RETROSOC_APU__SET_TRAP(s_trap_detail);
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 (stall_i) begin
end else if (s_execute_stall) begin
s_stat_q[19] <= 1'b1;
if (s_watchdog_q + 1'b1 >= timeout_i) begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd7, s_pc_q, s_instruction_q));
@@ -384,10 +574,19 @@ module apu_codec_sequencer (
end
end else if (!instruction_encoding_valid(s_instruction_q)) begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd1, s_pc_q, s_instruction_q));
end else if ((s_class >= 4'd2) || ((s_class == 4'd0) && (s_opcode == 4'd8))) begin
end else if ((!EnableP4 && ((s_class >= 4'd2) ||
((s_class == 4'd0) && (s_opcode == 4'd8)))) ||
(EnableP4 && ((s_class == 4'd6) ||
((s_class == 4'd0) && (s_opcode == 4'd8) &&
!(s_aux inside {8'd1, 8'd2, 8'd3}))))) begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd6, s_pc_q, s_instruction_q));
end else if (s_frame_retired_q >= s_max_retired_q) begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd8, s_pc_q, s_instruction_q));
end else if (s_predicate_true && s_primitive_class &&
(s_class != `APB4_APU__MC_CLASS_KERNEL)) begin
s_watchdog_q <= s_watchdog_q + 1'b1;
s_stat_q[19] <= 1'b1;
s_state_q <= PrimitiveWait;
end else begin
s_watchdog_q <= 32'd0;
s_stat_q[19] <= 1'b0;
@@ -487,12 +686,35 @@ module apu_codec_sequencer (
s_state_q <= FetchRequest;
end
end
4'd8: begin
if ((s_pc_q == s_last_pc_q) || (s_pc_q == 11'd2047)) begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd2, s_pc_q, s_instruction_q));
s_frame_retired_q <= s_frame_retired_q;
s_perf_retired_q <= s_perf_retired_q;
end else begin
s_pc_q <= s_pc_q + 1'b1;
s_state_q <= FetchRequest;
end
end
default: begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd1, s_pc_q, s_instruction_q));
s_frame_retired_q <= s_frame_retired_q;
s_perf_retired_q <= s_perf_retired_q;
end
endcase
end else if (s_class == `APB4_APU__MC_CLASS_KERNEL) begin
s_kernel_pending_q <= 1'b1;
s_kernel_done_q <= 1'b0;
s_kernel_dst_q <= s_dst;
if ((s_pc_q == s_last_pc_q) || (s_pc_q == 11'd2047)) begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd2, s_pc_q, s_instruction_q));
s_frame_retired_q <= s_frame_retired_q;
s_perf_retired_q <= s_perf_retired_q;
s_kernel_pending_q <= 1'b0;
end else begin
s_pc_q <= s_pc_q + 1'b1;
s_state_q <= FetchRequest;
end
end else begin
unique case (s_opcode)
4'd0: s_gpr_q[s_dst] <= s_source0;
@@ -537,6 +759,54 @@ module apu_codec_sequencer (
end
end
end
PrimitiveWait: begin
s_stat_q[19] <= 1'b1;
if (primitive_result_valid_i) begin
if (primitive_error_i) begin
s_state_q <= Idle;
s_trapped_q <= 1'b1;
trap_event_o <= 1'b1;
fault_valid_o <= 1'b1;
fault_code_o <= primitive_error_code_i;
fault_stage_o <= primitive_error_stage_i;
fault_resp_o <= 2'd0;
fault_index_o <= 8'd0;
fault_addr_o <= {18'd0, s_pc_q, 3'd0};
fault_detail_o <= trap_detail(primitive_error_reason_i, s_pc_q, s_instruction_q);
s_stat_q <= {11'd0, |s_loop_active_q, 1'b1, s_opcode, s_class, s_pc_q};
end else begin
for (int index = 0; index < 4; index++) begin
if (index < primitive_result_words_i) begin
s_gpr_q[primitive_result_dst_i+4'(index)] <= primitive_result_data_i[index];
end
end
s_watchdog_q <= 32'd0;
s_stat_q[19] <= 1'b0;
s_frame_retired_q <= s_frame_retired_q + 1'b1;
if (!counter_clear_i && !(&s_perf_retired_q)) begin
s_perf_retired_q <= s_perf_retired_q + 1'b1;
end
if ((s_pc_q == s_last_pc_q) || (s_pc_q == 11'd2047)) begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd2, s_pc_q, s_instruction_q));
s_frame_retired_q <= s_frame_retired_q;
s_perf_retired_q <= s_perf_retired_q;
end else begin
s_pc_q <= s_pc_q + 1'b1;
s_state_q <= FetchRequest;
end
end
end else if (abort_i) begin
s_state_q <= Idle;
s_kernel_pending_q <= 1'b0;
s_kernel_done_q <= 1'b0;
s_kernel_err_q <= 1'b0;
abort_done_o <= 1'b1;
end else if (s_watchdog_q + 1'b1 >= timeout_i) begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd7, s_pc_q, s_instruction_q));
end else begin
s_watchdog_q <= s_watchdog_q + 1'b1;
end
end
default: s_state_q <= Idle;
endcase
end
+9
View File
@@ -307,6 +307,15 @@
`define APB4_APU__APUMC_ENTRY_WORDS 8
`define APB4_APU__APUMC_ENTRY_COUNT 3
`define APB4_APU__APUMC_MAX_INSTRUCTIONS 2048
`define APB4_APU__APUMC_P3_PRIMITIVE_MASK 32'h0000_0000
`define APB4_APU__APUMC_P4_PRIMITIVE_MASK 32'h0000_ffff
`define APB4_APU__LOCAL_DATA_BYTES 114688
`define APB4_APU__LOCAL_CODEC_BYTES 40960
`define APB4_APU__LOCAL_TABLE_SCRATCH_BYTES 24576
`define APB4_APU__LOCAL_INPUT_BASE 24576
`define APB4_APU__LOCAL_OUTPUT_BASE 32768
`define APB4_APU__LOCAL_KWS_BASE 40960
`define APB4_APU__LOCAL_INTERNAL_BASE 106496
`define APB4_APU__APUMC_HEADER_MAGIC 0
`define APB4_APU__APUMC_HEADER_ABI 1
`define APB4_APU__APUMC_HEADER_TOTAL_BYTES 2
+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"
module apu_microcode_loader #(
parameter int unsigned PathStackDepth = 2048
parameter int unsigned PathStackDepth = 2048,
parameter bit EnableP4 = 1'b0
) (
// verilog_format: off -- preserve command, DMA, store, and result columns
input logic clk_i,
@@ -39,6 +40,11 @@ module apu_microcode_loader #(
output logic [63:0] store_data_o,
input logic [63:0] store_data_i,
input logic store_valid_i,
output logic local_write_o,
output logic [16:0] local_addr_o,
output logic [31:0] local_data_o,
output logic [3:0] local_strb_o,
output logic [15:0] table_bytes_o,
output logic [7:0] stat_o,
output logic [31:0] abi_o,
output logic [63:0] build_id_o,
@@ -50,6 +56,11 @@ module apu_microcode_loader #(
output logic [2:0][10:0] entry_last_o,
output logic [2:0][15:0] entry_max_loop_o,
output logic [2:0][23:0] entry_max_retired_o,
output logic [2:0][16:0] entry_scratch_base_o,
output logic [2:0][16:0] entry_scratch_bytes_o,
output logic [2:0][31:0] entry_primitive_mask_o,
output logic [2:0][15:0] entry_table_offset_o,
output logic [2:0][15:0] entry_table_bytes_o,
output logic load_done_o,
output logic abort_done_o,
output logic fault_valid_o,
@@ -63,7 +74,7 @@ module apu_microcode_loader #(
);
import apu_microcode_pkg::*;
localparam logic [11:0] PathStackLimit = PathStackDepth;
localparam logic [11:0] PathStackLimit = 12'(PathStackDepth);
typedef enum logic [4:0] {
Idle,
@@ -123,6 +134,7 @@ module apu_microcode_loader #(
logic s_cancel_resource_q, s_cancel_abort_q;
logic s_header_err, s_header_range_err, s_range_err, s_table_err, s_capability_err;
logic s_table_header_err;
logic [31:0] s_header_addr, s_header_detail;
logic [31:0] s_header_range_addr, s_header_range_detail;
logic [31:0] s_range_addr, s_range_detail;
@@ -136,7 +148,7 @@ module apu_microcode_loader #(
logic [ 4:0] s_entry_word_relative;
logic [63:0] s_scan_instruction;
logic [3:0] s_scan_class, s_scan_opcode, s_scan_predicate;
logic [ 1:0] s_scan_aux;
logic [ 7:0] s_scan_aux;
logic [10:0] s_scan_immediate;
logic s_scan_last;
logic s_scan_instruction_err, s_scan_target_err, s_scan_call_err, s_scan_loop_err;
@@ -172,6 +184,12 @@ module apu_microcode_loader #(
}) ?
s_scan_pc_q : s_instruction_word_relative[11:1];
assign store_data_o = {dma_data_i, s_instruction_low_q};
assign local_write_o = EnableP4 && (s_state_q == PayloadReceive) && s_data_accept &&
s_data_in_table;
assign local_addr_o = 17'(s_byte_offset_q - s_header_q[5]);
assign local_data_o = dma_data_i;
assign local_strb_o = dma_keep_i;
assign table_bytes_o = s_header_q[6][15:0];
assign idle_o = s_state_q == Idle;
assign stat_o = {
s_err_bits_q[5],
@@ -195,6 +213,11 @@ module apu_microcode_loader #(
assign entry_last_o[entry] = s_entry_word_q[(entry*8)+1][26:16];
assign entry_max_loop_o[entry] = s_entry_word_q[(entry*8)+4][15:0];
assign entry_max_retired_o[entry] = s_entry_word_q[(entry*8)+5][23:0];
assign entry_scratch_base_o[entry] = s_entry_word_q[(entry*8)+2][16:0];
assign entry_scratch_bytes_o[entry] = s_entry_word_q[(entry*8)+3][16:0];
assign entry_primitive_mask_o[entry] = s_entry_word_q[(entry*8)+6];
assign entry_table_offset_o[entry] = s_entry_word_q[(entry*8)+7][15:0];
assign entry_table_bytes_o[entry] = s_entry_word_q[(entry*8)+7][31:16];
end
assign s_instruction_end = {1'b0, s_header_q[4]} + ({1'b0, s_header_q[3]} << 3);
@@ -284,7 +307,8 @@ module apu_microcode_loader #(
s_header_range_err = 1'b1;
s_header_range_addr = s_source_addr_q + 32'd20;
s_header_range_detail = s_header_q[5];
end else if (s_header_q[10] != 32'd0) begin
end else if ((!EnableP4 && (s_header_q[10] != 32'd0)) ||
(EnableP4 && (s_header_q[10] > 32'h0000_6000))) begin
s_header_range_err = 1'b1;
s_header_range_addr = s_source_addr_q + 32'd40;
s_header_range_detail = s_header_q[10];
@@ -292,10 +316,15 @@ module apu_microcode_loader #(
end
always_comb begin
logic [31:0] s_scratch_end;
logic [31:0] s_max_scratch_end;
s_range_err = s_header_range_err;
s_range_addr = s_header_range_addr;
s_range_detail = s_header_range_detail;
s_max_scratch_end = 32'd0;
for (int unsigned entry = 0; entry < 3; entry++) begin
s_scratch_end = s_entry_word_q[(entry*8)+2] + s_entry_word_q[(entry*8)+3];
if (s_scratch_end > s_max_scratch_end) s_max_scratch_end = s_scratch_end;
if (!s_range_err && ((s_entry_word_q[entry*8][31:15] != 17'd0) ||
(s_entry_word_q[entry*8][3:0] != 4'(entry)))) begin
s_range_err = 1'b1;
@@ -312,11 +341,19 @@ module apu_microcode_loader #(
s_range_err = 1'b1;
s_range_addr = s_source_addr_q + s_header_q[7] + 32'(entry * 32) + 32'd4;
s_range_detail = s_entry_word_q[(entry*8)+1];
end else if (!s_range_err && (s_entry_word_q[(entry*8)+2] != 32'd0)) begin
end else if (!s_range_err &&
((!EnableP4 && (s_entry_word_q[(entry*8)+2] != 32'd0)) ||
(EnableP4 && ((s_entry_word_q[(entry*8)+2][31:17] != 15'd0) ||
(s_entry_word_q[(entry*8)+2][1:0] != 2'd0) ||
(s_entry_word_q[(entry*8)+3][31:17] != 15'd0) ||
(s_entry_word_q[(entry*8)+3][1:0] != 2'd0) ||
(s_scratch_end > 32'h0000_6000) ||
((s_entry_word_q[(entry*8)+3] != 32'd0) &&
(s_entry_word_q[(entry*8)+2] < s_header_q[6])))))) begin
s_range_err = 1'b1;
s_range_addr = s_source_addr_q + s_header_q[7] + 32'(entry * 32) + 32'd8;
s_range_detail = s_entry_word_q[(entry*8)+2];
end else if (!s_range_err && (s_entry_word_q[(entry*8)+3] != 32'd0)) begin
end else if (!s_range_err && !EnableP4 && (s_entry_word_q[(entry*8)+3] != 32'd0)) begin
s_range_err = 1'b1;
s_range_addr = s_source_addr_q + s_header_q[7] + 32'(entry * 32) + 32'd12;
s_range_detail = s_entry_word_q[(entry*8)+3];
@@ -334,21 +371,35 @@ module apu_microcode_loader #(
s_range_detail = s_entry_word_q[(entry*8)+5];
end
end
if (!s_range_err && EnableP4 && (s_header_q[10] != s_max_scratch_end)) begin
s_range_err = 1'b1;
s_range_addr = s_source_addr_q + 32'd40;
s_range_detail = s_header_q[10];
end
end
always_comb begin
logic [16:0] s_entry_table_end;
s_table_err = 1'b0;
s_table_addr = s_source_addr_q + 32'd20;
s_table_detail = s_header_q[5];
if (s_header_q[5] != 32'd0) begin
s_table_header_err = EnableP4 &&
((s_header_q[6] > 32'h0000_6000) || (s_header_q[6][1:0] != 2'd0));
if (!EnableP4 && (s_header_q[5] != 32'd0)) begin
s_table_err = 1'b1;
end else if (s_header_q[6] != 32'd0) begin
end else if (!EnableP4 && (s_header_q[6] != 32'd0)) begin
s_table_err = 1'b1;
s_table_addr = s_source_addr_q + 32'd24;
s_table_detail = s_header_q[6];
end else begin
for (int unsigned entry = 0; entry < 3; entry++) begin
if (!s_table_err && (s_entry_word_q[(entry*8)+7] != 32'd0)) begin
s_entry_table_end = {1'b0, s_entry_word_q[(entry*8)+7][15:0]} +
{1'b0, s_entry_word_q[(entry*8)+7][31:16]};
if (!s_table_err &&
((!EnableP4 && (s_entry_word_q[(entry*8)+7] != 32'd0)) ||
(EnableP4 && ((s_entry_word_q[(entry*8)+7][1:0] != 2'd0) ||
(s_entry_word_q[(entry*8)+7][17:16] != 2'd0) ||
(s_entry_table_end > {1'b0, s_header_q[6][15:0]}))))) begin
s_table_err = 1'b1;
s_table_addr = s_source_addr_q + s_header_q[7] + 32'(entry * 32) + 32'd28;
s_table_detail = s_entry_word_q[(entry*8)+7];
@@ -365,12 +416,17 @@ module apu_microcode_loader #(
s_capability_detail = s_header_q[9] ^ s_entry_mask;
if (s_header_q[9] != s_entry_mask) begin
s_capability_err = 1'b1;
end else if (s_header_q[9] != 32'd0) begin
end else if ((!EnableP4 && (s_header_q[9] != 32'd0)) ||
(EnableP4 &&
((s_header_q[9] & ~`APB4_APU__APUMC_P4_PRIMITIVE_MASK) != 32'd0))) begin
s_capability_err = 1'b1;
s_capability_detail = s_header_q[9];
end else begin
for (int unsigned entry = 0; entry < 3; entry++) begin
if (!s_capability_err && (s_entry_word_q[(entry*8)+6] != 32'd0)) begin
if (!s_capability_err &&
((!EnableP4 && (s_entry_word_q[(entry*8)+6] != 32'd0)) ||
(EnableP4 && ((s_entry_word_q[(entry*8)+6] &
~`APB4_APU__APUMC_P4_PRIMITIVE_MASK) != 32'd0)))) begin
s_capability_err = 1'b1;
s_capability_addr = s_source_addr_q + s_header_q[7] + 32'(entry * 32) + 32'd24;
s_capability_detail = s_entry_word_q[(entry*8)+6];
@@ -383,7 +439,7 @@ module apu_microcode_loader #(
assign s_scan_class = s_scan_instruction[63:60];
assign s_scan_opcode = s_scan_instruction[59:56];
assign s_scan_predicate = s_scan_instruction[55:52];
assign s_scan_aux = s_scan_instruction[33:32];
assign s_scan_aux = s_scan_instruction[39:32];
assign s_scan_immediate = s_scan_instruction[10:0];
assign s_scan_last = s_scan_pc_q == entry_last_o[s_scan_entry_q];
assign s_scan_target = {1'b0, s_scan_pc_q} + 1'b1 + {1'b0, s_scan_immediate};
@@ -391,7 +447,7 @@ module apu_microcode_loader #(
(s_scan_predicate != 4'd0) && (s_scan_class == 4'd0) &&
(s_scan_opcode inside {4'd3, 4'd4}) && (s_path_stack_count_q < PathStackLimit);
assign s_path_stack_cs = s_path_stack_push ||
((s_state_q == PathPop) && (s_path_stack_count_q != 7'd0));
((s_state_q == PathPop) && (s_path_stack_count_q != 12'd0));
assign s_path_stack_write = s_path_stack_push;
assign s_path_stack_addr = s_path_stack_push ?
s_path_stack_count_q[10:0] : s_path_stack_count_q[10:0] - 1'b1;
@@ -495,12 +551,23 @@ module apu_microcode_loader #(
((s_loop_active_q & ~(4'b0001 << s_scan_aux[1:0])) != 4'd0) :
((s_loop_active_q != 4'd0) ||
((s_scan_class == 4'd0) && (s_scan_opcode == 4'd6))));
assign s_scan_capability_instruction = ((s_scan_class >= 4'd2) && (s_scan_class <= 4'd6)) ||
((s_scan_class == 4'd0) && (s_scan_opcode == 4'd8));
assign s_scan_capability_instruction = EnableP4 ?
((s_scan_class == 4'd6) ||
((s_scan_class == 4'd0) && (s_scan_opcode == 4'd8) &&
!(s_scan_aux inside {8'd1, 8'd2, 8'd3})) ||
(!((s_scan_class == 4'd0) && (s_scan_opcode == 4'd8) &&
(s_scan_aux == 8'd1)) &&
((instruction_primitive_mask(
s_scan_instruction
) & ~entry_primitive_mask_o[s_scan_entry_q]) != 32'd0)) ||
(((s_scan_class == 4'd0) && (s_scan_opcode == 4'd8) && (s_scan_aux == 8'd1)) &&
((entry_primitive_mask_o[s_scan_entry_q] & 32'h0000_ffc0) == 32'd0))) :
(((s_scan_class >= 4'd2) && (s_scan_class <= 4'd6)) ||
((s_scan_class == 4'd0) && (s_scan_opcode == 4'd8)));
always_comb begin
s_path_fallthrough = s_scan_predicate != 4'd0;
if (s_scan_class == 4'd1) begin
if ((s_scan_class >= 4'd1) && (s_scan_class <= (EnableP4 ? 4'd5 : 4'd1))) begin
s_path_fallthrough = 1'b1;
end else if ((s_scan_class == 4'd0) && (s_scan_opcode inside {4'd0, 4'd6, 4'd7})) begin
s_path_fallthrough = 1'b1;
@@ -727,6 +794,14 @@ module apu_microcode_loader #(
fault_stage_o <= `APB4_APU__ERROR_STAGE_LOADER;
fault_addr_o <= s_header_range_addr;
fault_detail_o <= s_header_range_detail;
end else if (s_table_header_err) begin
s_state_q <= Idle;
s_err_bits_q[3] <= 1'b1;
fault_valid_o <= 1'b1;
fault_code_o <= `APB4_APU__ERROR_CODE_MICROCODE;
fault_stage_o <= `APB4_APU__ERROR_STAGE_LOADER;
fault_addr_o <= s_source_addr_q + 32'd24;
fault_detail_o <= s_header_q[6];
end else begin
s_transfer_err_q <= 1'b0;
s_byte_offset_q <= s_header_q[7];
@@ -789,6 +864,14 @@ module apu_microcode_loader #(
fault_stage_o <= `APB4_APU__ERROR_STAGE_LOADER;
fault_addr_o <= s_range_addr;
fault_detail_o <= s_range_detail;
end else if (EnableP4 && s_table_err) begin
s_state_q <= Idle;
s_err_bits_q[3] <= 1'b1;
fault_valid_o <= 1'b1;
fault_code_o <= `APB4_APU__ERROR_CODE_MICROCODE;
fault_stage_o <= `APB4_APU__ERROR_STAGE_LOADER;
fault_addr_o <= s_table_addr;
fault_detail_o <= s_table_detail;
end else begin
s_byte_offset_q <= 32'd64;
s_crc_q <= 32'hffff_ffff;
@@ -1000,7 +1083,7 @@ module apu_microcode_loader #(
end
end
PathPop: begin
if (s_path_stack_count_q != 7'd0) begin
if (s_path_stack_count_q != 12'd0) begin
s_path_stack_count_q <= s_path_stack_count_q - 1'b1;
s_state_q <= PathPopLoad;
end else if (s_scan_control_err_q || (s_scan_entry_q == 2'd2)) begin
+45
View File
@@ -10,8 +10,53 @@ package apu_microcode_pkg;
logic [10:0] last_pc;
logic [15:0] max_loop_count;
logic [23:0] max_retired;
logic [16:0] scratch_base;
logic [16:0] scratch_bytes;
logic [31:0] primitive_mask;
logic [15:0] table_offset;
logic [15:0] table_bytes;
} apu_mc_entry_t;
function automatic logic [31:0] instruction_primitive_mask(input logic [63:0] instruction_i);
logic [3:0] s_class;
logic [3:0] s_opcode;
logic [7:0] s_aux;
logic s_unused;
begin
s_class = instruction_i[63:60];
s_opcode = instruction_i[59:56];
s_aux = instruction_i[39:32];
s_unused = ^{instruction_i[55:40], instruction_i[31:0]};
instruction_primitive_mask = 32'd0;
unique case (s_class)
`APB4_APU__MC_CLASS_CONTROL: begin
if (s_opcode == `APB4_APU__MC_CONTROL_WAIT) begin
if (s_aux == `APB4_APU__MC_WAIT_KERNEL) instruction_primitive_mask = 32'h0000_ffc0;
else if (s_aux inside {`APB4_APU__MC_WAIT_INPUT_FIFO, `APB4_APU__MC_WAIT_OUTPUT_FIFO})
instruction_primitive_mask = 32'd1 << `APB4_APU__MC_PRIMITIVE_LOCAL_FIFO;
end
end
`APB4_APU__MC_CLASS_BITSTREAM: begin
instruction_primitive_mask = 32'd1 << `APB4_APU__MC_PRIMITIVE_BITSTREAM;
if (s_opcode inside {`APB4_APU__MC_BITSTREAM_CRC8, `APB4_APU__MC_BITSTREAM_CRC16})
instruction_primitive_mask |= 32'd1 << `APB4_APU__MC_PRIMITIVE_CRC;
end
`APB4_APU__MC_CLASS_ENTROPY:
instruction_primitive_mask = (32'd1 << `APB4_APU__MC_PRIMITIVE_BITSTREAM) |
(32'd1 << ((s_opcode <= `APB4_APU__MC_ENTROPY_HUFF_QUAD) ?
`APB4_APU__MC_PRIMITIVE_HUFFMAN : `APB4_APU__MC_PRIMITIVE_RICE));
`APB4_APU__MC_CLASS_LOCAL:
instruction_primitive_mask = 32'd1 <<
((s_opcode inside {`APB4_APU__MC_LOCAL_FIFO_POP, `APB4_APU__MC_LOCAL_FIFO_PUSH}) ?
`APB4_APU__MC_PRIMITIVE_LOCAL_FIFO : `APB4_APU__MC_PRIMITIVE_LOCAL_MEMORY);
`APB4_APU__MC_CLASS_KERNEL:
instruction_primitive_mask = 32'd1 << (s_opcode + `APB4_APU__MC_PRIMITIVE_REQUANTIZE);
default: instruction_primitive_mask = 32'd0;
endcase
return instruction_primitive_mask | {32{s_unused && 1'b0}};
end
endfunction
function automatic logic [31:0] crc32_word(input logic [31:0] crc_i, input logic [31:0] data_i,
input logic [3:0] keep_i);
logic [31:0] s_crc;
@@ -0,0 +1,688 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`include "apu_define.svh"
module apu_primitive_dispatcher (
// verilog_format: off -- preserve request, result, memory, and FIFO columns
input logic clk_i,
input logic rst_n_i,
input logic flush_i,
input logic req_valid_i,
output logic req_ready_o,
input logic [63:0] instruction_i,
input logic [31:0] source0_i,
input logic [31:0] source1_i,
input logic [31:0] destination_i,
input logic [16:0] scratch_base_i,
input logic [16:0] scratch_bytes_i,
input logic [15:0] table_offset_i,
input logic [15:0] table_bytes_i,
output logic result_valid_o,
output logic [ 3:0] result_dst_o,
output logic [ 3:0][31:0] result_data_o,
output logic [ 2:0] result_words_o,
output logic result_kernel_o,
output logic error_o,
output logic [ 5:0] error_code_o,
output logic [ 3:0] error_stage_o,
output logic [ 7:0] error_reason_o,
output logic [31:0] cycles_o,
output logic kernel_done_o,
output logic busy_o,
output logic input_exhausted_o,
output logic input_ready_o,
output logic output_ready_o,
output logic [ 6:0] input_count_o,
output logic [ 6:0] output_count_o,
output logic kernel_busy_o,
output logic memory_req_o,
output logic memory_write_o,
output logic [16:0] memory_addr_o,
output logic [31:0] memory_data_o,
output logic [ 3:0] memory_strb_o,
input logic memory_valid_i,
input logic [31:0] memory_data_i,
input logic memory_error_i,
input logic input_valid_i,
input logic [40:0] input_data_i,
output logic input_accept_o,
output logic output_valid_o,
output logic [40:0] output_data_o,
input logic output_accept_i
// verilog_format: on
);
typedef enum logic [3:0] {
Idle,
BitRequest,
BitWait,
FrameAlignRequest,
FrameAlignWait,
FramePeekRequest,
FramePeekWait,
FrameSkipRequest,
FrameSkipWait,
EntropyRequest,
EntropyWait,
LocalRequest,
LocalWait
} state_e;
state_e s_state_q;
logic [63:0] s_instruction_q;
logic [31:0] s_source0_q, s_source1_q;
logic [16:0] s_scratch_base_q, s_scratch_bytes_q;
logic [15:0] s_table_offset_q, s_table_bytes_q;
logic [15:0] s_frame_skipped_q;
logic [16:0] s_local_addr_q;
logic [ 1:0] s_local_lane_q;
logic [ 2:0] s_local_size_q;
logic [31:0] s_cycles_q;
logic [3:0] s_class, s_opcode, s_dst;
logic [ 7:0] s_aux;
logic [31:0] s_immediate;
logic s_input_full, s_input_empty, s_output_full, s_output_empty;
logic [6:0] s_input_count, s_output_count;
logic [40:0] s_input_fifo_data, s_output_fifo_data;
logic s_input_fifo_push, s_input_fifo_pop, s_output_fifo_push, s_output_fifo_pop;
logic [40:0] s_output_fifo_write_data;
logic s_fifo_metadata_ok;
logic s_bit_req_valid, s_bit_req_ready, s_direct_bit_req, s_direct_frame_align;
logic s_frame_match, s_frame_can_skip;
logic [2:0] s_bit_req_op;
logic [5:0] s_bit_req_width;
logic s_bit_result_valid, s_bit_err, s_bit_fifo_pop;
logic [31:0] s_bit_result_data;
logic [ 6:0] s_available_bits;
logic s_reservoir_eof;
logic s_entropy_req_valid, s_entropy_req_ready;
logic s_direct_entropy_req;
logic s_entropy_memory_req, s_entropy_bit_req_valid, s_entropy_bit_req_ready;
logic [16:0] s_entropy_memory_addr;
logic [ 2:0] s_entropy_bit_req_op;
logic [ 5:0] s_entropy_bit_req_width;
logic s_entropy_result_valid, s_entropy_err;
logic [ 3:0] s_entropy_result_dst;
logic [ 3:0][31:0] s_entropy_result_data;
logic [ 2:0] s_entropy_result_words;
logic [ 5:0] s_entropy_err_code;
logic [ 3:0] s_entropy_err_stage;
logic [31:0] s_entropy_cycles;
logic s_kernel_req_valid, s_kernel_req_ready, s_kernel_memory_req, s_kernel_memory_write;
logic [16:0] s_kernel_memory_addr;
logic [31:0] s_kernel_memory_data;
logic [ 3:0] s_kernel_memory_strb;
logic s_kernel_done, s_kernel_err, s_kernel_busy;
logic [5:0] s_kernel_err_code;
logic [3:0] s_kernel_err_stage, s_kernel_result_dst;
logic [7:0] s_kernel_err_reason;
logic [31:0] s_kernel_result_count, s_kernel_cycles;
logic [32:0] s_scratch_end, s_local_end, s_table_end;
logic [32:0] s_entropy_table_end;
logic s_entropy_range_ok;
logic [34:0] s_local_addr_full;
logic s_local_addr_overflow_q;
logic s_local_range_ok, s_local_access_ok, s_local_write;
logic [31:0] s_crc_result;
assign s_class = instruction_i[63:60];
assign s_opcode = instruction_i[59:56];
assign s_dst = instruction_i[51:48];
assign s_aux = instruction_i[39:32];
assign s_immediate = instruction_i[31:0];
assign req_ready_o = (s_state_q == Idle) &&
(((s_class == `APB4_APU__MC_CLASS_KERNEL) && s_kernel_req_ready) ||
(!s_kernel_busy &&
((s_class inside {`APB4_APU__MC_CLASS_BITSTREAM, `APB4_APU__MC_CLASS_ENTROPY}) ||
((s_class == `APB4_APU__MC_CLASS_LOCAL) &&
(((s_opcode == `APB4_APU__MC_LOCAL_FIFO_POP) && !s_input_empty) ||
((s_opcode == `APB4_APU__MC_LOCAL_FIFO_PUSH) && !s_output_full) ||
(s_opcode < `APB4_APU__MC_LOCAL_FIFO_POP))))));
assign busy_o = (s_state_q != Idle) || s_kernel_busy;
assign kernel_done_o = result_valid_o && result_kernel_o && !error_o;
assign input_exhausted_o = s_reservoir_eof;
assign input_ready_o = !s_input_empty;
assign output_ready_o = !s_output_full;
assign input_count_o = s_input_count;
assign output_count_o = s_output_count;
assign kernel_busy_o = s_kernel_busy;
assign input_accept_o = input_valid_i && !s_input_full;
assign s_input_fifo_push = input_accept_o;
assign s_input_fifo_pop = s_bit_fifo_pop ||
(req_valid_i && req_ready_o && (s_class == `APB4_APU__MC_CLASS_LOCAL) &&
(s_opcode == `APB4_APU__MC_LOCAL_FIFO_POP));
assign s_fifo_metadata_ok = ((source1_i & ~32'h0000_0107) == 32'd0) &&
(source1_i[2:0] != 3'd0) && (source1_i[2:0] <= 3'd4);
assign s_output_fifo_push = req_valid_i && req_ready_o && s_fifo_metadata_ok &&
(s_class == `APB4_APU__MC_CLASS_LOCAL) &&
(s_opcode == `APB4_APU__MC_LOCAL_FIFO_PUSH);
assign s_output_fifo_write_data = {source1_i[8], 5'd0, source1_i[2:0], source0_i};
assign output_valid_o = !s_output_empty;
assign output_data_o = s_output_fifo_data;
assign s_output_fifo_pop = output_accept_i && output_valid_o;
stream_fifo #(
.DATA_WIDTH (41),
.BUFFER_DEPTH (64),
.LOG_BUFFER_DEPTH(6)
) u_input_fifo (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.flush_i(flush_i),
.full_o (s_input_full),
.empty_o(s_input_empty),
.cnt_o (s_input_count),
.dat_i (input_data_i),
.push_i (s_input_fifo_push),
.dat_o (s_input_fifo_data),
.pop_i (s_input_fifo_pop)
);
stream_fifo #(
.DATA_WIDTH (41),
.BUFFER_DEPTH (64),
.LOG_BUFFER_DEPTH(6)
) u_output_fifo (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.flush_i(flush_i),
.full_o (s_output_full),
.empty_o(s_output_empty),
.cnt_o (s_output_count),
.dat_i (s_output_fifo_write_data),
.push_i (s_output_fifo_push),
.dat_o (s_output_fifo_data),
.pop_i (s_output_fifo_pop)
);
assign s_direct_bit_req = (s_state_q == Idle) && req_valid_i && req_ready_o &&
(s_class == `APB4_APU__MC_CLASS_BITSTREAM) &&
(s_opcode <= `APB4_APU__MC_BITSTREAM_ALIGN);
assign s_direct_frame_align = (s_state_q == Idle) && req_valid_i && req_ready_o &&
(s_class == `APB4_APU__MC_CLASS_BITSTREAM) &&
(s_opcode == `APB4_APU__MC_BITSTREAM_FRAME_SYNC);
assign s_frame_match = (s_bit_result_data[15:0] & s_source1_q[15:0]) ==
(s_source0_q[15:0] & s_source1_q[15:0]);
assign s_frame_can_skip = !s_bit_err && !s_frame_match &&
(s_frame_skipped_q < s_instruction_q[15:0] - 1'b1);
assign s_bit_req_valid = s_direct_bit_req || s_direct_frame_align ||
((s_state_q == FrameAlignWait) && s_bit_result_valid && !s_bit_err) ||
((s_state_q == FramePeekWait) && s_bit_result_valid && s_frame_can_skip) ||
((s_state_q == FrameSkipWait) && s_bit_result_valid && !s_bit_err) ||
(s_state_q inside {
BitRequest, FrameAlignRequest, FramePeekRequest, FrameSkipRequest
}) || s_entropy_bit_req_valid;
assign s_bit_req_op = s_entropy_bit_req_valid ? s_entropy_bit_req_op :
(s_direct_bit_req ? s_opcode[2:0] :
((s_direct_frame_align || (s_state_q == FrameAlignRequest)) ? 3'd4 :
((s_state_q inside {FrameAlignWait, FramePeekRequest, FrameSkipWait}) ? 3'd1 :
((s_state_q inside {FramePeekWait, FrameSkipRequest}) ?
3'd2 : s_instruction_q[58:56]))));
assign s_bit_req_width = s_entropy_bit_req_valid ? s_entropy_bit_req_width :
(s_direct_bit_req ? s_immediate[5:0] :
((s_direct_frame_align || (s_state_q == FrameAlignRequest)) ? 6'd0 :
((s_state_q inside {FrameAlignWait, FramePeekRequest, FrameSkipWait}) ?
{1'b0, s_instruction_q[36:32]} :
((s_state_q inside {FramePeekWait, FrameSkipRequest}) ?
6'd8 : s_instruction_q[5:0]))));
assign s_entropy_bit_req_ready = s_bit_req_ready && s_entropy_bit_req_valid;
apu_bitstream_engine u_bitstream_engine (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.flush_i (flush_i),
.req_valid_i (s_bit_req_valid),
.req_ready_o (s_bit_req_ready),
.req_op_i (s_bit_req_op),
.req_width_i (s_bit_req_width),
.result_valid_o (s_bit_result_valid),
.result_data_o (s_bit_result_data),
.available_bits_o(s_available_bits),
.eof_o (s_reservoir_eof),
.error_o (s_bit_err),
.fifo_empty_i (s_input_empty),
.fifo_data_i (s_input_fifo_data),
.fifo_pop_o (s_bit_fifo_pop)
);
assign s_direct_entropy_req = (s_state_q == Idle) && req_valid_i && req_ready_o &&
(s_class == `APB4_APU__MC_CLASS_ENTROPY);
assign s_entropy_req_valid = (s_state_q == EntropyRequest) || s_direct_entropy_req;
assign s_kernel_req_valid = (s_state_q == Idle) && req_valid_i &&
(s_class == `APB4_APU__MC_CLASS_KERNEL) && !s_kernel_done &&
!(result_valid_o && result_kernel_o);
apu_entropy_engine u_entropy_engine (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.flush_i (flush_i),
.req_valid_i (s_entropy_req_valid),
.req_ready_o (s_entropy_req_ready),
.opcode_i (s_direct_entropy_req ? s_opcode : s_instruction_q[59:56]),
.dst_i (s_direct_entropy_req ? s_dst : s_instruction_q[51:48]),
.aux_i (s_direct_entropy_req ? s_aux : s_instruction_q[39:32]),
.immediate_i (s_direct_entropy_req ? s_immediate : s_instruction_q[31:0]),
.source0_i (s_direct_entropy_req ? source0_i : s_source0_q),
.source1_i (s_direct_entropy_req ? source1_i : s_source1_q),
.table_offset_i (s_direct_entropy_req ? table_offset_i : s_table_offset_q),
.memory_req_o (s_entropy_memory_req),
.memory_addr_o (s_entropy_memory_addr),
.memory_valid_i (memory_valid_i),
.memory_data_i (memory_data_i),
.memory_error_i (memory_error_i),
.bit_req_valid_o (s_entropy_bit_req_valid),
.bit_req_ready_i (s_entropy_bit_req_ready),
.bit_req_op_o (s_entropy_bit_req_op),
.bit_req_width_o (s_entropy_bit_req_width),
.bit_result_valid_i(s_bit_result_valid),
.bit_result_data_i (s_bit_result_data),
.bit_error_i (s_bit_err),
.result_valid_o (s_entropy_result_valid),
.result_dst_o (s_entropy_result_dst),
.result_data_o (s_entropy_result_data),
.result_words_o (s_entropy_result_words),
.error_o (s_entropy_err),
.error_code_o (s_entropy_err_code),
.error_stage_o (s_entropy_err_stage),
.cycles_o (s_entropy_cycles)
);
apu_kernel_engine u_kernel_engine (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.flush_i (flush_i),
.req_valid_i (s_kernel_req_valid),
.req_ready_o (s_kernel_req_ready),
.opcode_i (s_opcode),
.aux_i (s_aux),
.count_i (s_immediate[15:0]),
.dst_i (s_dst),
.input_offset_i (source0_i),
.parameter_offset_i(source1_i),
.output_offset_i (destination_i),
.scratch_base_i (scratch_base_i),
.scratch_bytes_i (scratch_bytes_i),
.table_offset_i (table_offset_i),
.table_bytes_i (table_bytes_i),
.memory_req_o (s_kernel_memory_req),
.memory_write_o (s_kernel_memory_write),
.memory_addr_o (s_kernel_memory_addr),
.memory_data_o (s_kernel_memory_data),
.memory_strb_o (s_kernel_memory_strb),
.memory_valid_i (memory_valid_i),
.memory_data_i (memory_data_i),
.memory_error_i (memory_error_i),
.done_o (s_kernel_done),
.error_o (s_kernel_err),
.error_code_o (s_kernel_err_code),
.error_stage_o (s_kernel_err_stage),
.error_reason_o (s_kernel_err_reason),
.result_dst_o (s_kernel_result_dst),
.result_count_o (s_kernel_result_count),
.cycles_o (s_kernel_cycles),
.busy_o (s_kernel_busy)
);
assign memory_req_o = s_kernel_busy ? s_kernel_memory_req :
(s_entropy_memory_req || ((s_state_q == LocalRequest) && s_local_access_ok));
assign memory_write_o = s_kernel_busy ? s_kernel_memory_write :
((s_state_q == LocalRequest) && s_local_access_ok && s_local_write);
assign memory_addr_o = s_kernel_busy ? s_kernel_memory_addr :
(s_entropy_memory_req ? s_entropy_memory_addr : {s_local_addr_q[16:2], 2'd0});
assign memory_data_o = s_kernel_busy ? s_kernel_memory_data : s_source1_q;
assign memory_strb_o = s_kernel_busy ? s_kernel_memory_strb : 4'hf;
always_comb begin
logic [15:0] s_crc8;
s_crc8 = (s_opcode == `APB4_APU__MC_BITSTREAM_CRC8) ? {8'd0, source0_i[7:0]} : source0_i[15:0];
for (int bit_index = 0; bit_index < 8; bit_index++) begin
if (s_opcode == `APB4_APU__MC_BITSTREAM_CRC8) begin
s_crc8[7:0] = {s_crc8[6:0], 1'b0} ^ ({8{(s_crc8[7] ^ source1_i[7-bit_index])}} & 8'h07);
end else begin
s_crc8 = {s_crc8[14:0], 1'b0} ^ ({16{(s_crc8[15] ^ source1_i[7-bit_index])}} & 16'h8005);
end
end
s_crc_result = (s_opcode == `APB4_APU__MC_BITSTREAM_CRC8) ?
{24'd0, s_crc8[7:0]} : {16'd0, s_crc8};
end
assign s_scratch_end = {16'd0, s_scratch_base_q} + {16'd0, s_scratch_bytes_q};
assign s_local_end = {16'd0, s_local_addr_q} + {30'd0, s_local_size_q};
assign s_table_end = {17'd0, s_table_offset_q} + {17'd0, s_table_bytes_q};
assign s_entropy_table_end = {1'b0, source0_i} + ({1'b0, source1_i} << 2);
assign s_entropy_range_ok = (source0_i[1:0] == 2'd0) && !s_entropy_table_end[32] &&
(s_entropy_table_end <= {17'd0, table_bytes_i});
assign s_local_range_ok = !s_local_addr_overflow_q &&
((s_instruction_q[59:56] inside {
`APB4_APU__MC_LOCAL_TABLE8, `APB4_APU__MC_LOCAL_TABLE16,
`APB4_APU__MC_LOCAL_TABLE32
}) ? (({16'd0, s_local_addr_q} >= {17'd0, s_table_offset_q}) && (s_local_end <= s_table_end))
: (({16'd0, s_local_addr_q} >= {16'd0, s_scratch_base_q}) &&
(s_local_end <= s_scratch_end)));
assign s_local_access_ok = s_local_range_ok &&
((s_local_size_q == 3'd1) ||
((s_local_addr_q & ({14'd0, s_local_size_q} - 17'd1)) == 17'd0));
always_comb begin
unique case (s_opcode)
`APB4_APU__MC_LOCAL_LD32, `APB4_APU__MC_LOCAL_ST32:
s_local_addr_full = {18'd0, scratch_base_i} + {3'd0, source0_i} +
{{19{instruction_i[15]}}, instruction_i[15:0]};
`APB4_APU__MC_LOCAL_TABLE8:
s_local_addr_full = {19'd0, table_offset_i} + {3'd0, source0_i} + {3'd0, source1_i};
`APB4_APU__MC_LOCAL_TABLE16:
s_local_addr_full = {19'd0, table_offset_i} + {3'd0, source0_i} + ({3'd0, source1_i} << 1);
default:
s_local_addr_full = {19'd0, table_offset_i} + {3'd0, source0_i} + ({3'd0, source1_i} << 2);
endcase
end
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_state_q <= Idle;
s_instruction_q <= 64'd0;
s_source0_q <= 32'd0;
s_source1_q <= 32'd0;
s_scratch_base_q <= 17'd0;
s_scratch_bytes_q <= 17'd0;
s_table_offset_q <= 16'd0;
s_table_bytes_q <= 16'd0;
s_frame_skipped_q <= 16'd0;
s_local_addr_q <= 17'd0;
s_local_lane_q <= 2'd0;
s_local_size_q <= 3'd0;
s_local_write <= 1'b0;
s_local_addr_overflow_q <= 1'b0;
s_cycles_q <= 32'd0;
result_valid_o <= 1'b0;
result_dst_o <= 4'd0;
result_data_o <= '0;
result_words_o <= 3'd0;
result_kernel_o <= 1'b0;
error_o <= 1'b0;
error_code_o <= 6'd0;
error_stage_o <= 4'd0;
error_reason_o <= 8'd0;
cycles_o <= 32'd0;
end else if (flush_i) begin
s_state_q <= Idle;
result_valid_o <= 1'b0;
result_kernel_o <= 1'b0;
error_o <= 1'b0;
s_cycles_q <= 32'd0;
end else begin
result_valid_o <= 1'b0;
result_kernel_o <= 1'b0;
error_o <= 1'b0;
if (s_kernel_done) begin
result_valid_o <= 1'b1;
result_kernel_o <= 1'b1;
result_dst_o <= s_kernel_result_dst;
result_data_o <= s_kernel_err ? '0 : {96'd0, s_kernel_result_count};
result_words_o <= s_kernel_err ? 3'd0 : 3'd1;
cycles_o <= s_kernel_cycles;
error_o <= s_kernel_err;
error_code_o <= s_kernel_err_code;
error_stage_o <= s_kernel_err_stage;
error_reason_o <= s_kernel_err_reason;
end
if (s_state_q != Idle) s_cycles_q <= s_cycles_q + 1'b1;
unique case (s_state_q)
Idle: begin
if (req_valid_i && req_ready_o && (s_class != `APB4_APU__MC_CLASS_KERNEL)) begin
s_instruction_q <= instruction_i;
s_source0_q <= source0_i;
s_source1_q <= source1_i;
s_scratch_base_q <= scratch_base_i;
s_scratch_bytes_q <= scratch_bytes_i;
s_table_offset_q <= table_offset_i;
s_table_bytes_q <= table_bytes_i;
s_cycles_q <= 32'd1;
s_local_addr_overflow_q <= s_local_addr_full[34:17] != 18'd0;
result_dst_o <= instruction_i[51:48];
result_data_o <= '0;
result_words_o <= 3'd0;
if (s_class == `APB4_APU__MC_CLASS_BITSTREAM) begin
if (s_opcode == `APB4_APU__MC_BITSTREAM_FRAME_SYNC) begin
s_frame_skipped_q <= 16'd0;
s_state_q <= s_direct_frame_align ? FrameAlignWait : FrameAlignRequest;
end else if (s_opcode inside {`APB4_APU__MC_BITSTREAM_CRC8,
`APB4_APU__MC_BITSTREAM_CRC16}) begin
result_valid_o <= 1'b1;
result_words_o <= 3'd1;
result_data_o[0] <= s_crc_result;
cycles_o <= 32'd1;
end else begin
s_state_q <= s_direct_bit_req ? BitWait : BitRequest;
end
end else if (s_class == `APB4_APU__MC_CLASS_ENTROPY) begin
if ((s_opcode <= `APB4_APU__MC_ENTROPY_HUFF_QUAD) &&
(source1_i >= 32'd1) && (source1_i <= 32'd4096) &&
!s_entropy_range_ok) begin
result_valid_o <= 1'b1;
result_words_o <= 3'd0;
error_o <= 1'b1;
error_code_o <= `APB4_APU__ERROR_CODE_SEQUENCER;
error_stage_o <= `APB4_APU__ERROR_STAGE_LIFECYCLE;
error_reason_o <= 8'd5;
cycles_o <= 32'd1;
end else begin
s_state_q <= s_direct_entropy_req ? EntropyWait : EntropyRequest;
end
end else if (s_opcode == `APB4_APU__MC_LOCAL_FIFO_POP) begin
result_valid_o <= 1'b1;
result_words_o <= 3'd2;
result_data_o[0] <= s_input_fifo_data[31:0];
result_data_o[1] <= {23'd0, s_input_fifo_data[40], 5'd0, s_input_fifo_data[34:32]};
cycles_o <= 32'd1;
end else if (s_opcode == `APB4_APU__MC_LOCAL_FIFO_PUSH) begin
result_valid_o <= 1'b1;
result_words_o <= 3'd0;
cycles_o <= 32'd1;
if ((source1_i & ~32'h0000_0107) != 32'd0 ||
(source1_i[2:0] == 3'd0) || (source1_i[2:0] > 3'd4)) begin
error_o <= 1'b1;
error_code_o <= `APB4_APU__ERROR_CODE_OVERFLOW;
error_stage_o <= `APB4_APU__ERROR_STAGE_RESAMPLER;
error_reason_o <= 8'd9;
end
end else begin
s_local_write <= s_opcode == `APB4_APU__MC_LOCAL_ST32;
s_local_addr_q <= s_local_addr_full[16:0];
s_local_lane_q <= s_local_addr_full[1:0];
unique case (s_opcode)
`APB4_APU__MC_LOCAL_LD32, `APB4_APU__MC_LOCAL_ST32: begin
s_local_size_q <= 3'd4;
end
`APB4_APU__MC_LOCAL_TABLE8: begin
s_local_size_q <= 3'd1;
end
`APB4_APU__MC_LOCAL_TABLE16: begin
s_local_size_q <= 3'd2;
end
default: begin
s_local_size_q <= 3'd4;
end
endcase
s_state_q <= LocalRequest;
end
end
end
BitRequest: begin
if (s_bit_req_ready) s_state_q <= BitWait;
end
BitWait: begin
if (s_bit_result_valid) begin
s_state_q <= Idle;
result_valid_o <= 1'b1;
result_words_o <= (s_instruction_q[59:56] inside {`APB4_APU__MC_BITSTREAM_SKIP,
`APB4_APU__MC_BITSTREAM_ALIGN
}) ? 3'd0 : 3'd1;
result_data_o[0] <= s_bit_result_data;
cycles_o <= (s_instruction_q[59:56] == `APB4_APU__MC_BITSTREAM_REFILL) ? 32'd3 : 32'd1;
if (s_bit_err && (s_instruction_q[59:56] != `APB4_APU__MC_BITSTREAM_REFILL)) begin
result_data_o <= '0;
result_words_o <= 3'd0;
error_o <= 1'b1;
error_code_o <= `APB4_APU__ERROR_CODE_TRUNCATED;
error_stage_o <= `APB4_APU__ERROR_STAGE_BITSTREAM;
error_reason_o <= 8'd9;
end
end
end
FrameAlignRequest: begin
if (s_bit_req_ready) s_state_q <= FrameAlignWait;
end
FrameAlignWait: begin
if (s_bit_result_valid) begin
if (s_bit_err) begin
s_state_q <= Idle;
result_valid_o <= 1'b1;
result_data_o <= '0;
result_words_o <= 3'd0;
error_o <= 1'b1;
error_code_o <= `APB4_APU__ERROR_CODE_TRUNCATED;
error_stage_o <= `APB4_APU__ERROR_STAGE_BITSTREAM;
error_reason_o <= 8'd9;
end else begin
s_state_q <= FramePeekWait;
end
end
end
FramePeekRequest: begin
if (s_bit_req_ready) s_state_q <= FramePeekWait;
end
FramePeekWait: begin
if (s_bit_result_valid) begin
if (s_bit_err) begin
s_state_q <= Idle;
result_valid_o <= 1'b1;
result_data_o <= '0;
result_words_o <= 3'd0;
error_o <= 1'b1;
error_code_o <= `APB4_APU__ERROR_CODE_MALFORMED;
error_stage_o <= `APB4_APU__ERROR_STAGE_BITSTREAM;
error_reason_o <= 8'd9;
cycles_o <= 32'd2 + ({16'd0, s_instruction_q[15:0]} << 1);
end else if (s_frame_match) begin
s_state_q <= Idle;
result_valid_o <= 1'b1;
result_words_o <= 3'd1;
result_data_o[0] <= {16'd0, s_frame_skipped_q};
cycles_o <= 32'd2 + ({16'd0, s_frame_skipped_q} << 1);
end else if (s_frame_skipped_q >= s_instruction_q[15:0] - 1'b1) begin
s_state_q <= Idle;
result_valid_o <= 1'b1;
result_data_o <= '0;
result_words_o <= 3'd0;
error_o <= 1'b1;
error_code_o <= `APB4_APU__ERROR_CODE_MALFORMED;
error_stage_o <= `APB4_APU__ERROR_STAGE_BITSTREAM;
error_reason_o <= 8'd9;
cycles_o <= 32'd2 + ({16'd0, s_instruction_q[15:0]} << 1);
end else begin
s_state_q <= FrameSkipWait;
end
end
end
FrameSkipRequest: begin
if (s_bit_req_ready) s_state_q <= FrameSkipWait;
end
FrameSkipWait: begin
if (s_bit_result_valid) begin
if (s_bit_err) begin
s_state_q <= Idle;
result_valid_o <= 1'b1;
result_data_o <= '0;
result_words_o <= 3'd0;
error_o <= 1'b1;
error_code_o <= `APB4_APU__ERROR_CODE_MALFORMED;
error_stage_o <= `APB4_APU__ERROR_STAGE_BITSTREAM;
error_reason_o <= 8'd9;
end else begin
s_frame_skipped_q <= s_frame_skipped_q + 1'b1;
s_state_q <= FramePeekWait;
end
end
end
EntropyRequest: begin
if (s_entropy_req_ready) s_state_q <= EntropyWait;
end
EntropyWait: begin
if (s_entropy_result_valid) begin
s_state_q <= Idle;
result_valid_o <= 1'b1;
result_dst_o <= s_entropy_result_dst;
result_data_o <= s_entropy_result_data;
result_words_o <= s_entropy_result_words;
cycles_o <= s_entropy_cycles;
if (s_entropy_err) begin
result_data_o <= '0;
result_words_o <= 3'd0;
error_o <= 1'b1;
error_code_o <= s_entropy_err_code;
error_stage_o <= s_entropy_err_stage;
error_reason_o <= (s_entropy_err_code == `APB4_APU__ERROR_CODE_SEQUENCER) ?
8'd5 : 8'd9;
end
end
end
LocalRequest: begin
if (!s_local_access_ok) begin
s_state_q <= Idle;
result_valid_o <= 1'b1;
result_data_o <= '0;
result_words_o <= 3'd0;
error_o <= 1'b1;
error_code_o <= `APB4_APU__ERROR_CODE_SEQUENCER;
error_stage_o <= `APB4_APU__ERROR_STAGE_LIFECYCLE;
error_reason_o <= 8'd5;
cycles_o <= 32'd1;
end else if (s_local_write) begin
s_state_q <= Idle;
result_valid_o <= 1'b1;
result_words_o <= 3'd0;
cycles_o <= 32'd1;
end else begin
s_state_q <= LocalWait;
end
end
LocalWait: begin
if (memory_error_i) begin
s_state_q <= Idle;
result_valid_o <= 1'b1;
result_data_o <= '0;
result_words_o <= 3'd0;
error_o <= 1'b1;
error_code_o <= `APB4_APU__ERROR_CODE_SEQUENCER;
error_stage_o <= `APB4_APU__ERROR_STAGE_LIFECYCLE;
error_reason_o <= 8'd5;
cycles_o <= 32'd2;
end else if (memory_valid_i) begin
s_state_q <= Idle;
result_valid_o <= 1'b1;
result_words_o <= 3'd1;
cycles_o <= 32'd2;
unique case (s_local_size_q)
3'd1: result_data_o[0] <= {24'd0, memory_data_i[s_local_lane_q*8+:8]};
3'd2: result_data_o[0] <= {16'd0, memory_data_i[s_local_lane_q[1]*16+:16]};
default: result_data_o[0] <= memory_data_i;
endcase
end
end
default: s_state_q <= Idle;
endcase
end
end
logic s_unused;
assign s_unused = s_reservoir_eof ^ ^s_available_bits ^ ^s_input_count ^ ^s_output_count ^
^s_instruction_q[63:60] ^ ^s_instruction_q[55:40] ^ ^s_immediate[31:16];
endmodule
@@ -0,0 +1,174 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`include "apu_define.svh"
module apu_reconstruction_engine (
// verilog_format: off -- preserve arithmetic operand columns
input logic [ 3:0] opcode_i,
input logic [ 7:0] aux_i,
input logic signed [31:0] value0_i,
input logic signed [31:0] value1_i,
input logic signed [31:0] scale_i,
input logic signed [31:0] residual_i,
input logic signed [31:0] predictor_shift_i,
input logic signed [31:0] history_i [0:31],
input logic signed [31:0] coefficient_i [0:31],
output logic signed [31:0] result0_o,
output logic signed [31:0] result1_o,
output logic overflow_o
// verilog_format: on
);
logic signed [71:0] s_accumulator;
logic signed [63:0] s_value0, s_value1;
logic signed [63:0] s_product;
logic signed [63:0] s_predictor;
logic signed [63:0] s_shifted;
logic signed [63:0] s_value0_ext, s_value1_ext, s_residual_ext;
logic signed [71:0] s_lpc_shifted;
logic signed [72:0] s_lpc_value;
logic s_lpc_shift_overflow;
logic [ 5:0] s_order;
function automatic logic signed [63:0] rne(input logic signed [63:0] value_i,
input logic [5:0] shift_i);
logic signed [63:0] s_magnitude;
logic signed [63:0] s_quotient;
logic [63:0] s_remainder;
logic [63:0] s_half;
begin
if (shift_i == 6'd0) return value_i;
s_magnitude = (value_i < 0) ? -value_i : value_i;
s_quotient = s_magnitude >>> shift_i;
s_remainder = s_magnitude & ((64'd1 << shift_i) - 1'b1);
s_half = 64'd1 << (shift_i - 1'b1);
if ((s_remainder > s_half) || ((s_remainder == s_half) && s_quotient[0])) begin
s_quotient = s_quotient + 1'b1;
end
return (value_i < 0) ? -s_quotient : s_quotient;
end
endfunction
function automatic logic signed [31:0] sat32(input logic signed [63:0] value_i);
if (value_i > 64'sh0000_0000_7fff_ffff) return 32'sh7fff_ffff;
if (value_i < -64'sh0000_0000_8000_0000) return -32'sh8000_0000;
return value_i[31:0];
endfunction
function automatic logic signed [31:0] sat_width(input logic signed [63:0] value_i,
input logic [1:0] width_i);
logic signed [63:0] s_minimum, s_maximum;
begin
unique case (width_i)
2'd0: begin
s_minimum = -64'sd32768;
s_maximum = 64'sd32767;
end
2'd1: begin
s_minimum = -64'sd8388608;
s_maximum = 64'sd8388607;
end
default: begin
s_minimum = -64'sh0000_0000_8000_0000;
s_maximum = 64'sh0000_0000_7fff_ffff;
end
endcase
if (value_i < s_minimum) return s_minimum[31:0];
if (value_i > s_maximum) return s_maximum[31:0];
return value_i[31:0];
end
endfunction
always_comb begin
s_order = aux_i[5:0];
s_accumulator = 72'sd0;
for (int index = 0; index < 32; index++) begin
if (index < s_order) begin
s_accumulator += $signed(coefficient_i[index]) * $signed(history_i[index]);
end
end
s_product = $signed(value0_i) * $signed(scale_i);
s_value0_ext = {{32{value0_i[31]}}, value0_i};
s_value1_ext = {{32{value1_i[31]}}, value1_i};
s_residual_ext = {{32{residual_i[31]}}, residual_i};
s_predictor = 64'sd0;
unique case (aux_i[2:0])
3'd1: s_predictor = {{32{history_i[0][31]}}, history_i[0]};
3'd2:
s_predictor = 64'sd2 * {{32{history_i[0][31]}}, history_i[0]} -
{{32{history_i[1][31]}}, history_i[1]};
3'd3:
s_predictor = 64'sd3 * {{32{history_i[0][31]}}, history_i[0]} -
64'sd3 * {{32{history_i[1][31]}}, history_i[1]} +
{{32{history_i[2][31]}}, history_i[2]};
3'd4:
s_predictor = 64'sd4 * {{32{history_i[0][31]}}, history_i[0]} -
64'sd6 * {{32{history_i[1][31]}}, history_i[1]} +
64'sd4 * {{32{history_i[2][31]}}, history_i[2]} -
{{32{history_i[3][31]}}, history_i[3]};
default: s_predictor = 64'sd0;
endcase
s_value0 = s_value0_ext;
s_value1 = s_value1_ext;
if (predictor_shift_i >= 0) begin
s_lpc_shifted = s_accumulator >>> predictor_shift_i[5:0];
s_lpc_shift_overflow = 1'b0;
end else begin
s_lpc_shifted = s_accumulator <<< (-predictor_shift_i);
s_lpc_shift_overflow = (s_lpc_shifted >>> (-predictor_shift_i)) != s_accumulator;
end
s_shifted = s_lpc_shifted[63:0];
s_lpc_value = {s_lpc_shifted[71], s_lpc_shifted} + {{41{residual_i[31]}}, residual_i};
result0_o = value0_i;
result1_o = value1_i;
overflow_o = (s_accumulator[71:63] != {9{s_accumulator[63]}});
unique case (opcode_i)
`APB4_APU__MC_KERNEL_REQUANT: begin
s_shifted = aux_i[5] ?
rne(s_product, 6'(30 + aux_i[4:0])) : (s_product >>> (30 + aux_i[4:0]));
result0_o = sat_width(s_shifted, aux_i[7:6]);
end
`APB4_APU__MC_KERNEL_STEREO, `APB4_APU__MC_KERNEL_DECORRELATE: begin
unique case (aux_i[1:0])
2'd0: begin
s_value0 = s_value0_ext;
s_value1 = s_value1_ext;
end
2'd1: begin
s_value0 = s_value0_ext;
s_value1 = s_value0_ext - s_value1_ext;
end
2'd2: begin
s_value0 = s_value0_ext + s_value1_ext;
s_value1 = s_value1_ext;
end
default: begin
s_predictor = s_value0_ext * 64'sd2 + (s_value1_ext & 64'sd1);
s_value0 = (s_predictor + s_value1_ext) >>> 1;
s_value1 = (s_predictor - s_value1_ext) >>> 1;
end
endcase
overflow_o = (s_value0 > 64'sh0000_0000_7fff_ffff) ||
(s_value0 < -64'sh0000_0000_8000_0000) ||
(s_value1 > 64'sh0000_0000_7fff_ffff) ||
(s_value1 < -64'sh0000_0000_8000_0000);
result0_o = sat32(s_value0);
result1_o = sat32(s_value1);
end
`APB4_APU__MC_KERNEL_FIXED: begin
s_value0 = s_predictor + s_residual_ext;
overflow_o = (s_value0 > 64'sh0000_0000_7fff_ffff) ||
(s_value0 < -64'sh0000_0000_8000_0000);
result0_o = s_value0[31:0];
end
`APB4_APU__MC_KERNEL_LPC: begin
overflow_o = overflow_o || s_lpc_shift_overflow ||
(s_lpc_value > 73'sh0000_0000_7fff_ffff) ||
(s_lpc_value < -73'sh0000_0000_8000_0000);
result0_o = s_lpc_value[31:0];
end
default: begin
end
endcase
end
endmodule
+2 -2
View File
@@ -80,8 +80,8 @@ module apu_reg (
);
localparam logic [31:0] IpId = 32'h4150_5530;
localparam logic [31:0] IpVersion = 32'h0001_0000;
localparam logic [31:0] Capability0 = 32'h0000_0098;
localparam logic [31:0] Capability1 = 32'h0000_0010;
localparam logic [31:0] Capability0 = 32'h0000_0198;
localparam logic [31:0] Capability1 = 32'h0182_7010;
localparam logic [31:0] AbiDigest = 32'd0;
localparam logic [10:0] IrqMask = 11'h7ff;
localparam logic [31:0] TimeoutReset = 32'h0000_ffff;
+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_control_store.sv
/ip/multimedia/apu_microcode_loader.sv
/ip/multimedia/apu_local_sram.sv
/ip/multimedia/apu_bitstream_engine.sv
/ip/multimedia/apu_entropy_engine.sv
/ip/multimedia/apu_reconstruction_engine.sv
/ip/multimedia/apu_transform_engine.sv
/ip/multimedia/apu_resampler.sv
/ip/multimedia/apu_kernel_engine.sv
/ip/multimedia/apu_primitive_dispatcher.sv
/ip/multimedia/apu_codec_sequencer.sv
/ip/multimedia/apu_reg.sv
/ip/multimedia/apb4_apu.sv
+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
+27 -1
View File
@@ -80,11 +80,16 @@ module apu_sequencer_formal_design (
.entry_last_i (s_entry_last),
.entry_max_loop_i ({16'd4, 16'd4, 16'd4}),
.entry_max_retired_i (s_entry_max_retired),
.entry_scratch_base_i ('0),
.entry_scratch_bytes_i ('0),
.entry_primitive_mask_i ('0),
.entry_table_offset_i ('0),
.entry_table_bytes_i ('0),
.input_exhausted_i (1'b1),
.input_ready_i (1'b0),
.output_ready_i (1'b1),
.kernel_done_i (1'b0),
.transport_idle_success_i(1'b1),
.transport_idle_success_i (1'b1),
.stall_i (1'b0),
.cause_valid_i (1'b0),
.cause_code_i (6'd0),
@@ -93,6 +98,21 @@ module apu_sequencer_formal_design (
.cause_index_i (8'd0),
.cause_addr_i (32'd0),
.cause_detail_i (32'd0),
.primitive_req_valid_o (),
.primitive_req_ready_i (1'b0),
.primitive_instruction_o (),
.primitive_source0_o (),
.primitive_source1_o (),
.primitive_destination_o (),
.primitive_result_valid_i (1'b0),
.primitive_result_dst_i (4'd0),
.primitive_result_data_i ('0),
.primitive_result_words_i (3'd0),
.primitive_result_kernel_i(1'b0),
.primitive_error_i (1'b0),
.primitive_error_code_i (6'd0),
.primitive_error_stage_i (4'd0),
.primitive_error_reason_i (8'd0),
.fetch_o (s_fetch),
.fetch_addr_o (s_fetch_addr),
.fetch_data_i (s_fetch_data),
@@ -111,6 +131,12 @@ module apu_sequencer_formal_design (
.fault_addr_o (fault_addr),
.fault_detail_o (fault_detail),
.perf_retired_o (),
.active_scratch_base_o (),
.active_scratch_bytes_o (),
.active_primitive_mask_o (),
.active_table_offset_o (),
.active_table_bytes_o (),
.launch_epoch_o (),
.idle_o (idle)
);
@@ -33,6 +33,15 @@ APU_LOADER_SCENARIOS = {
"apu_loader_abort": 5,
"apu_loader_resource_reset": 6,
}
APU_PRIMITIVE_SCENARIOS = {
"apu_primitives_invalid_read": 0,
"apu_primitives_local": 1,
"apu_primitives_kernel": 2,
"apu_primitives_input_fifo": 3,
"apu_primitives_alignment": 4,
"apu_primitives_output_fifo": 5,
"apu_primitives_local_overflow": 6,
}
def target_defines(target: str) -> list[str]:
@@ -45,6 +54,11 @@ def target_defines(target: str) -> list[str]:
"+define+SYNTHESIS",
f"+define+APU_LOADER_FORMAL_SCENARIO={scenario}",
]
if target in APU_PRIMITIVE_SCENARIOS:
return [
"+define+SV_ASSRT_DISABLE",
f"+define+APU_PRIMITIVES_FORMAL_SCENARIO={APU_PRIMITIVE_SCENARIOS[target]}",
]
if target == "opipsram":
return ["+define+PDK_BEHAV"]
if target == "sysctrl":
@@ -273,6 +287,20 @@ def source_files(target: str) -> list[Path]:
MULTIMEDIA / "apu_dma.sv",
SCRIPT_DIR / "apu_formal.sv",
]
if target == "apu_primitives" or target in APU_PRIMITIVE_SCENARIOS:
return [
COMMON_RTL / "utils/fifo.sv",
MULTIMEDIA / "apu_microcode_pkg.sv",
MULTIMEDIA / "apu_local_sram.sv",
MULTIMEDIA / "apu_bitstream_engine.sv",
MULTIMEDIA / "apu_entropy_engine.sv",
MULTIMEDIA / "apu_reconstruction_engine.sv",
MULTIMEDIA / "apu_transform_engine.sv",
MULTIMEDIA / "apu_resampler.sv",
MULTIMEDIA / "apu_kernel_engine.sv",
MULTIMEDIA / "apu_primitive_dispatcher.sv",
SCRIPT_DIR / "apu_primitives_formal.sv",
]
if target == "apu_loader" or target in APU_LOADER_SCENARIOS:
return [
ROOT / "rtl/tech/tc_sram.sv",
@@ -362,6 +390,8 @@ def parse_args() -> argparse.Namespace:
"opipsram",
"dma",
"apu",
"apu_primitives",
*APU_PRIMITIVE_SCENARIOS,
*APU_LOADER_SCENARIOS,
"apu_sequencer",
"gateway_a",
+10 -7
View File
@@ -31,12 +31,13 @@ FORMAL_APU_TIMEOUT ?= 300
FORMAL_GATEWAY_A_TIMEOUT ?= 120
FORMAL_SDIO_TIMEOUT ?= 120
FORMAL_APU_LOADER_TARGETS := apu_loader_success apu_loader_header_range apu_loader_descriptor_range apu_loader_crc apu_loader_control_flow apu_loader_abort apu_loader_resource_reset
FORMAL_TARGET_TIMEOUT = $(if $(filter dma,$*),$(FORMAL_DMA_TIMEOUT),$(if $(filter apu apu_sequencer $(FORMAL_APU_LOADER_TARGETS),$*),$(FORMAL_APU_TIMEOUT),$(if $(filter gateway_a,$*),$(FORMAL_GATEWAY_A_TIMEOUT),$(if $(filter sdio,$*),$(FORMAL_SDIO_TIMEOUT),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_TIMEOUT),$(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(if $(filter i2c,$*),$(FORMAL_I2C_TIMEOUT),$(FORMAL_TIMEOUT))))))))
FORMAL_APU_PRIMITIVE_TARGETS := apu_primitives_invalid_read apu_primitives_local apu_primitives_local_overflow apu_primitives_kernel apu_primitives_input_fifo apu_primitives_alignment apu_primitives_output_fifo
FORMAL_TARGET_TIMEOUT = $(if $(filter dma,$*),$(FORMAL_DMA_TIMEOUT),$(if $(filter apu apu_primitives apu_sequencer $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS),$*),$(FORMAL_APU_TIMEOUT),$(if $(filter gateway_a,$*),$(FORMAL_GATEWAY_A_TIMEOUT),$(if $(filter sdio,$*),$(FORMAL_SDIO_TIMEOUT),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_TIMEOUT),$(if $(filter ws2812,$*),$(FORMAL_WS2812_TIMEOUT),$(if $(filter i2c,$*),$(FORMAL_I2C_TIMEOUT),$(FORMAL_TIMEOUT))))))))
FORMAL_APU_TARGET_DEPTH = $(if $(filter $(FORMAL_APU_LOADER_TARGETS),$*),$(FORMAL_APU_LOADER_DEPTH),$(if $(filter apu_sequencer,$*),$(FORMAL_APU_SEQUENCER_DEPTH),$(FORMAL_APU_DEPTH)))
FORMAL_APU_TARGET_COVER_DEPTH = $(if $(filter $(FORMAL_APU_LOADER_TARGETS),$*),$(FORMAL_APU_LOADER_COVER_DEPTH),$(FORMAL_APU_TARGET_DEPTH))
FORMAL_TARGET_TOP = $(if $(filter $(FORMAL_APU_LOADER_TARGETS),$*),apu_loader,$*)
FORMAL_COMPACT_COVER_TARGETS := i2c opipsram dma $(FORMAL_APU_LOADER_TARGETS)
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint dvp i2s psram onchip_ram opipsram dma apu $(FORMAL_APU_LOADER_TARGETS) apu_sequencer gateway_a sdio
FORMAL_TARGET_TOP = $(if $(filter $(FORMAL_APU_LOADER_TARGETS),$*),apu_loader,$(if $(filter $(FORMAL_APU_PRIMITIVE_TARGETS),$*),apu_primitives,$*))
FORMAL_COMPACT_COVER_TARGETS := i2c opipsram dma $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS)
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint dvp i2s psram onchip_ram opipsram dma apu $(FORMAL_APU_PRIMITIVE_TARGETS) $(FORMAL_APU_LOADER_TARGETS) apu_sequencer gateway_a sdio
FORMAL_FILELIST_GENERATOR := $(RTL_PATH)/formal/generate_formal_filelist.py
FORMAL_SBY_GENERATOR := $(RTL_PATH)/formal/generate_sby_config.py
FORMAL_RESULT_GENERATOR := $(RTL_PATH)/formal/formal_results.py
@@ -76,6 +77,8 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
$(RTL_PATH)/formal/dma_formal_props.sv \
$(RTL_PATH)/formal/apu_formal.sv \
$(RTL_PATH)/formal/apu_formal_props.sv \
$(RTL_PATH)/formal/apu_primitives_formal.sv \
$(RTL_PATH)/formal/apu_primitives_formal_props.sv \
$(RTL_PATH)/formal/apu_loader_formal.sv \
$(RTL_PATH)/formal/apu_loader_formal_props.sv \
$(RTL_PATH)/formal/apu_sequencer_formal.sv \
@@ -212,14 +215,14 @@ $(FORMAL_DIR)/%/prove.sby: $(FORMAL_DIR)/%/design.v \
$(FORMAL_SBY_GENERATOR)
python3 $(FORMAL_SBY_GENERATOR) --top $(FORMAL_TARGET_TOP)_formal --input $< \
--properties $(RTL_PATH)/formal/$(FORMAL_TARGET_TOP)_formal_props.sv --solver $(FORMAL_SOLVER) \
--mode $(if $(filter dma apu apu_sequencer $(FORMAL_APU_LOADER_TARGETS) gateway_a sdio onchip_ram,$*),bmc,prove) --depth $(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_DEPTH),$(if $(filter apu apu_sequencer $(FORMAL_APU_LOADER_TARGETS),$*),$(FORMAL_APU_TARGET_DEPTH),$(if $(filter gateway_a,$*),$(FORMAL_GATEWAY_A_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))))))) --output $@
--mode $(if $(filter dma apu apu_primitives apu_sequencer $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS) gateway_a sdio onchip_ram,$*),bmc,prove) --depth $(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_DEPTH),$(if $(filter apu apu_primitives apu_sequencer $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS),$*),$(FORMAL_APU_TARGET_DEPTH),$(if $(filter gateway_a,$*),$(FORMAL_GATEWAY_A_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))))))) --output $@
@if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi
$(FORMAL_DIR)/%/cover.sby: $(FORMAL_DIR)/%/design.v \
$(FORMAL_SBY_GENERATOR)
python3 $(FORMAL_SBY_GENERATOR) --top $(FORMAL_TARGET_TOP)_formal --input $< \
--properties $(RTL_PATH)/formal/$(FORMAL_TARGET_TOP)_formal_props.sv --solver $(FORMAL_SOLVER) \
--mode cover --depth $(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_COVER_DEPTH),$(if $(filter apu apu_sequencer $(FORMAL_APU_LOADER_TARGETS),$*),$(FORMAL_APU_TARGET_COVER_DEPTH),$(if $(filter gateway_a,$*),$(FORMAL_GATEWAY_A_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_COVER_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))))))) \
--mode cover --depth $(if $(filter pll_rcu,$*),$(FORMAL_PLL_RCU_DEPTH),$(if $(filter dma,$*),$(FORMAL_DMA_COVER_DEPTH),$(if $(filter apu apu_primitives apu_sequencer $(FORMAL_APU_LOADER_TARGETS) $(FORMAL_APU_PRIMITIVE_TARGETS),$*),$(FORMAL_APU_TARGET_COVER_DEPTH),$(if $(filter gateway_a,$*),$(FORMAL_GATEWAY_A_DEPTH),$(if $(filter sdio,$*),$(FORMAL_SDIO_COVER_DEPTH),$(if $(filter opipsram,$*),$(FORMAL_OPIPSRAM_DEPTH),$(if $(filter ws2812,$*),$(FORMAL_WS2812_DEPTH),$(if $(filter i2c,$*),$(FORMAL_I2C_DEPTH),$(if $(filter clint,$*),$(FORMAL_CLINT_DEPTH),$(if $(filter psram,$*),$(FORMAL_PSRAM_DEPTH),$(FORMAL_DEPTH))))))))))) \
$(if $(filter $(FORMAL_COMPACT_COVER_TARGETS),$*),--no-vcd) --output $@
@if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi
@@ -316,7 +319,7 @@ formal-opipsram: $(FORMAL_DIR)/opipsram/.stamp | manifest
formal-dma: $(FORMAL_DIR)/dma/.stamp | manifest
formal-apu: $(FORMAL_DIR)/apu/.stamp | manifest
formal-apu: $(FORMAL_DIR)/apu/.stamp $(addsuffix /.stamp,$(addprefix $(FORMAL_DIR)/,$(FORMAL_APU_PRIMITIVE_TARGETS))) | manifest
formal-apu-loader: $(addsuffix /.stamp,$(addprefix $(FORMAL_DIR)/,$(FORMAL_APU_LOADER_TARGETS))) | manifest
+8
View File
@@ -135,15 +135,23 @@
"rtl/ip/multimedia": [
"apb4_apu.sv",
"apb4_jpeg.sv",
"apu_bitstream_engine.sv",
"apu_codec_sequencer.sv",
"apu_control_store.sv",
"apu_dma.sv",
"apu_define.svh",
"apu_entropy_engine.sv",
"apu_kernel_engine.sv",
"apu_local_sram.sv",
"apu_microcode_loader.sv",
"apu_microcode_pkg.sv",
"apu_primitive_dispatcher.sv",
"apu_reconstruction_engine.sv",
"apu_resampler.sv",
"apu_ring_scheduler.sv",
"apu_reg.sv",
"apu_stream_router.sv",
"apu_transform_engine.sv",
"axi4s_dvp.sv",
"dvp_core.sv",
"dvp_define.svh",
+5 -4
View File
@@ -4,10 +4,11 @@ This directory contains the Python implementation of setup, dependency locking,
build manifests, flow execution, simulation checks, regression orchestration,
quality checks, metrics, packaging, and cleanup.
`apu_mcasm.py`, `apu_isa.py`, and `apu_interpreter.py` are the frozen APU
microcode V1 assembler/ABI model and P3 reference interpreter. They accept only
the APU instruction language and deliberately have no C, ELF, RV32, dynamic
linking, or runtime code-generation path.
`apu_mcasm.py`, `apu_isa.py`, `apu_interpreter.py`, and `apu_primitives.py` are
the frozen APU microcode V1 assembler/ABI model, P3/P4 target interpreters, and
bit-accurate P4 primitive model. They accept only the APU instruction language
and deliberately have no C, ELF, RV32, dynamic linking, or runtime
code-generation path.
Scripts are part of the build contract. Prefer existing helpers over ad-hoc
shell behavior, preserve structured JSON results, and keep setup/download
+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
@@ -8,15 +8,20 @@ from dataclasses import dataclass, field
from pathlib import Path
from apu_isa import (
APUMC_TARGETS,
BitstreamOpcode,
ControlOpcode,
EntropyOpcode,
Entry,
Instruction,
InstructionClass,
LocalOpcode,
ScalarOpcode,
parse_apumc,
trap_detail,
validate_p3_instruction,
validate_instruction,
)
from apu_primitives import PrimitiveBam, PrimitiveFault, PrimitiveResult
class SequencerTrap(RuntimeError):
@@ -55,15 +60,36 @@ class Machine:
loops: dict[int, tuple[int, int]] = field(default_factory=dict)
watchdog: int = 0
trace: list[dict[str, object]] = field(default_factory=list)
target: str = "p3"
primitives: PrimitiveBam | None = None
primitive_cycles: list[int] = field(default_factory=list)
kernel_result: PrimitiveResult | None = field(default=None, init=False)
kernel_fault: PrimitiveFault | None = field(default=None, init=False)
kernel_pending_fault: PrimitiveFault | None = field(default=None, init=False)
kernel_pending_dst: int | None = field(default=None, init=False)
kernel_cycles_remaining: int = field(default=0, init=False)
def __post_init__(self) -> None:
self.pc = self.entry.entry_pc
self.begin_entry(self.entry)
@staticmethod
def _signed(value: int) -> int:
return value if value < 0x80000000 else value - 0x100000000
def _predicate(self, predicate: int) -> bool:
input_ready = self.predicate_inputs.input_ready
output_ready = self.predicate_inputs.output_ready
kernel_done = self.predicate_inputs.kernel_done
input_exhausted = self.predicate_inputs.input_exhausted
if self.target == "p4" and self.primitives is not None:
input_ready = not self.primitives.input_fifo.empty
output_ready = not self.primitives.output_fifo.full
kernel_done = self.primitives.kernel_done
input_exhausted = (
self.primitives.eof
and self.primitives.input_fifo.empty
and not self.primitives.bits
)
values = (
True,
self.eq,
@@ -72,10 +98,10 @@ class Machine:
not self.signed_lt,
self.unsigned_lt,
not self.unsigned_lt,
self.predicate_inputs.input_exhausted,
self.predicate_inputs.input_ready,
self.predicate_inputs.output_ready,
self.predicate_inputs.kernel_done,
input_exhausted,
input_ready,
output_ready,
kernel_done,
self.predicate_inputs.transport_done,
)
if predicate >= len(values):
@@ -85,6 +111,86 @@ class Machine:
def _write(self, register: int, value: int) -> None:
self.registers[register] = value & 0xFFFFFFFF
def _complete_kernel(self) -> None:
if self.kernel_pending_fault is not None:
self.kernel_fault = self.kernel_pending_fault
elif self.kernel_result is not None:
for register, value in self.kernel_result.writes.items():
self._write(register, value)
if self.primitives is not None:
self.primitives.kernel_done = True
self.kernel_result = None
self.kernel_pending_fault = None
self.kernel_pending_dst = None
self.kernel_cycles_remaining = 0
def _tick_kernel(self) -> None:
if self.kernel_pending_dst is None:
return
if self.kernel_cycles_remaining > 0:
self.kernel_cycles_remaining -= 1
if self.kernel_cycles_remaining == 0:
self._complete_kernel()
def _pending_dependency(self, instruction: Instruction) -> bool:
pending = self.kernel_pending_dst
if pending is None:
return False
instruction_class = instruction.instruction_class
opcode = instruction.opcode
if instruction_class == InstructionClass.CONTROL:
if opcode == ControlOpcode.END:
return True
return opcode == ControlOpcode.LOOP_SETUP and instruction.src0 == pending
if instruction_class == InstructionClass.SCALAR:
if opcode == ScalarOpcode.MOV:
return instruction.src0 == pending or instruction.dst == pending
if opcode == ScalarOpcode.MOVI:
return instruction.dst == pending
if opcode == ScalarOpcode.CMP:
return instruction.src0 == pending or instruction.src1 == pending
if opcode == ScalarOpcode.SAT:
return instruction.src0 == pending or instruction.dst == pending
return (
instruction.src0 == pending
or instruction.src1 == pending
or instruction.dst == pending
)
return True
def begin_entry(self, entry: Entry | None = None) -> None:
if entry is not None:
self.entry = entry
self.pc = self.entry.entry_pc
self.retired = 0
self.registers = [0] * 16
self.eq = False
self.signed_lt = False
self.unsigned_lt = False
self.return_stack.clear()
self.loops.clear()
self.watchdog = 0
self.kernel_result = None
self.kernel_fault = None
self.kernel_pending_fault = None
self.kernel_pending_dst = None
self.kernel_cycles_remaining = 0
if self.primitives is not None:
self.primitives.reset_mutable()
def abort(self) -> None:
self.kernel_result = None
self.kernel_fault = None
self.kernel_pending_fault = None
self.kernel_pending_dst = None
self.kernel_cycles_remaining = 0
self.watchdog = 0
if self.primitives is not None:
self.primitives.reset_mutable()
def reset(self) -> None:
self.begin_entry(self.entry)
def _retire(self, instruction: Instruction, predicate_true: bool) -> None:
self.retired += 1
self.watchdog = 0
@@ -97,7 +203,9 @@ class Machine:
}
)
def _no_retirement_cycle(self, instruction: Instruction) -> None:
def _no_retirement_cycle(self, instruction: Instruction, *, tick_kernel: bool = True) -> None:
if tick_kernel:
self._tick_kernel()
self.watchdog += 1
if self.timeout <= 0 or self.watchdog >= self.timeout:
raise SequencerTrap(7, self.pc, instruction)
@@ -115,8 +223,13 @@ class Machine:
if self.pc < self.entry.first_pc or self.pc > self.entry.last_pc:
raise SequencerTrap(2, self.pc, Instruction(0, 0))
instruction = self.instructions[self.pc]
self._tick_kernel()
if self.kernel_fault is not None:
fault = self.kernel_fault
self.kernel_fault = None
raise SequencerTrap(fault.reason, self.pc, instruction)
try:
validate_p3_instruction(instruction)
validate_instruction(instruction, self.target)
except ValueError as error:
raise SequencerTrap(1, self.pc, instruction) from error
if self.retired >= self.entry.max_retired:
@@ -129,6 +242,10 @@ class Machine:
self.pc += 1
return False
if self._pending_dependency(instruction):
self._no_retirement_cycle(instruction, tick_kernel=False)
return False
next_pc = self.pc + 1
terminal = False
if instruction.instruction_class == InstructionClass.CONTROL:
@@ -167,7 +284,18 @@ class Machine:
next_pc = target
else:
del self.loops[slot]
else:
elif opcode == ControlOpcode.WAIT:
ready = False
if self.primitives is not None:
ready = (
(instruction.aux == 1 and self.primitives.kernel_done)
or (instruction.aux == 2 and not self.primitives.input_fifo.empty)
or (instruction.aux == 3 and not self.primitives.output_fifo.full)
)
if not ready:
self._no_retirement_cycle(instruction, tick_kernel=False)
return False
elif instruction.instruction_class == InstructionClass.SCALAR:
opcode = ScalarOpcode(instruction.opcode)
source0 = self.registers[instruction.src0]
source1 = self.registers[instruction.src1]
@@ -205,6 +333,71 @@ class Machine:
instruction.dst,
self._saturate(source0, instruction.aux & 0x1F, bool(instruction.aux & 0x20)),
)
else:
if self.primitives is None:
raise SequencerTrap(6, self.pc, instruction)
if instruction.instruction_class == InstructionClass.LOCAL:
opcode = LocalOpcode(instruction.opcode)
if (opcode == LocalOpcode.FIFO_POP and self.primitives.input_fifo.empty) or (
opcode == LocalOpcode.FIFO_PUSH and self.primitives.output_fifo.full
):
self._no_retirement_cycle(instruction, tick_kernel=False)
return False
if instruction.instruction_class == InstructionClass.BITSTREAM:
opcode = BitstreamOpcode(instruction.opcode)
required = (
instruction.immediate & 0x3F
if opcode
in (
BitstreamOpcode.REFILL,
BitstreamOpcode.PEEK,
BitstreamOpcode.GET,
BitstreamOpcode.SKIP,
)
else 1
)
self.primitives._refill(required)
if len(self.primitives.bits) < required and not self.primitives.eof:
self._no_retirement_cycle(instruction, tick_kernel=False)
return False
if instruction.instruction_class == InstructionClass.ENTROPY:
opcode = EntropyOpcode(instruction.opcode)
if opcode <= EntropyOpcode.HUFF_QUAD:
required = instruction.aux
elif opcode == EntropyOpcode.UNARY:
required = instruction.immediate + 1
elif opcode == EntropyOpcode.SIGN_RESTORE:
required = 0 if self.registers[instruction.src0] == 0 else (
self.registers[instruction.src1] & 0x1F
) + 1
else:
required = 1
self.primitives._refill(required)
if len(self.primitives.bits) < required and not self.primitives.eof:
self._no_retirement_cycle(instruction, tick_kernel=False)
return False
try:
if instruction.instruction_class == InstructionClass.KERNEL:
self.primitives.kernel_done = False
result = self.primitives.execute(instruction, self.registers, self.entry)
except PrimitiveFault as error:
if instruction.instruction_class != InstructionClass.KERNEL or error.reason == 5:
raise SequencerTrap(error.reason, self.pc, instruction) from error
result = PrimitiveResult(cycles=1, kernel_done=True)
self.kernel_pending_fault = error
self.primitive_cycles.append(result.cycles)
if instruction.instruction_class == InstructionClass.KERNEL:
self.primitives.kernel_done = False
self.kernel_result = result
self.kernel_pending_dst = instruction.dst
self.kernel_cycles_remaining = max(result.cycles - 1, 0)
if self.kernel_cycles_remaining == 0:
self._complete_kernel()
else:
for register, value in result.writes.items():
self._write(register, value)
for _ in range(max(result.cycles - 1, 0)):
self._no_retirement_cycle(instruction)
self._retire(instruction, predicate_true)
if not terminal:
@@ -214,10 +407,12 @@ class Machine:
return terminal
def run(self) -> dict[str, object]:
fetch_required = True
while True:
if self.pc < self.entry.first_pc or self.pc > self.entry.last_pc:
raise SequencerTrap(2, self.pc, Instruction(0, 0))
instruction = self.instructions[self.pc]
if fetch_required:
for _ in range(self.fetch_no_retirement_cycles):
self._no_retirement_cycle(instruction)
remaining_stalls = self.stall_cycles.get(self.pc, 0)
@@ -225,19 +420,23 @@ class Machine:
self._no_retirement_cycle(instruction)
remaining_stalls -= 1
self.stall_cycles[self.pc] = 0
retired_before = self.retired
if self.step():
break
fetch_required = self.retired != retired_before
return {
"pc": self.pc,
"retired": self.retired,
"registers": self.registers,
"trace": self.trace,
"primitive_cycles": self.primitive_cycles,
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("bundle", type=Path)
parser.add_argument("--target", choices=APUMC_TARGETS, default="p3")
parser.add_argument("--entry", type=int, choices=range(3), default=0)
parser.add_argument("--timeout", type=int, default=0xFFFF)
parser.add_argument("--input-exhausted", action="store_true")
@@ -246,7 +445,7 @@ def main() -> int:
parser.add_argument("--kernel-done", action="store_true")
parser.add_argument("--transport-done", action="store_true")
args = parser.parse_args()
instructions, entries, _ = parse_apumc(args.bundle.read_bytes())
instructions, entries, header = parse_apumc(args.bundle.read_bytes(), args.target)
inputs = PredicateInputs(
input_exhausted=args.input_exhausted,
input_ready=args.input_ready,
@@ -256,7 +455,15 @@ def main() -> int:
)
print(
json.dumps(
Machine(instructions, entries[args.entry], inputs, args.timeout).run(), sort_keys=True
Machine(
instructions,
entries[args.entry],
inputs,
args.timeout,
target=args.target,
primitives=PrimitiveBam() if args.target == "p4" else None,
).run(),
sort_keys=True,
)
)
return 0
+298 -48
View File
@@ -17,6 +17,9 @@ APUMC_ENTRY_COUNT = 3
APUMC_INSTRUCTION_OFFSET = 192
APUMC_MAX_INSTRUCTIONS = 2048
APUMC_P3_CAPABILITY_MASK = 0
APUMC_P4_CAPABILITY_MASK = 0x0000FFFF
APUMC_P4_TABLE_SCRATCH_BYTES = 0x6000
APUMC_TARGETS = ("p3", "p4")
class InstructionClass(IntEnum):
@@ -58,6 +61,50 @@ class ScalarOpcode(IntEnum):
SAT = 13
class BitstreamOpcode(IntEnum):
REFILL = 0
PEEK = 1
GET = 2
SKIP = 3
ALIGN = 4
FRAME_SYNC = 5
CRC8 = 6
CRC16 = 7
class EntropyOpcode(IntEnum):
HUFF_SYMBOL = 0
HUFF_PAIR = 1
HUFF_QUAD = 2
UNARY = 3
RICE4 = 4
RICE5 = 5
SIGN_RESTORE = 6
class LocalOpcode(IntEnum):
LD32 = 0
ST32 = 1
TABLE8 = 2
TABLE16 = 3
TABLE32 = 4
FIFO_POP = 5
FIFO_PUSH = 6
class KernelOpcode(IntEnum):
REQUANT = 0
STEREO = 1
IMDCT6 = 2
IMDCT18 = 3
DCT32_POLY = 4
FIXED = 5
LPC = 6
DECORRELATE = 7
RESAMPLE = 8
PCM_PACK = 9
PREDICATES = {
"always": 0,
"eq": 1,
@@ -184,6 +231,9 @@ def abi_manifest() -> dict[str, object]:
"table": 7,
},
"p3_implemented_primitive_mask": APUMC_P3_CAPABILITY_MASK,
"p4_implemented_primitive_mask": APUMC_P4_CAPABILITY_MASK,
"p4_local_data_bytes": 0x1C000,
"p4_local_table_scratch_bytes": APUMC_P4_TABLE_SCRATCH_BYTES,
}
@@ -281,9 +331,36 @@ def _require_zero(instruction: Instruction, names: Iterable[str]) -> None:
raise ValueError(f"{name} is reserved for this opcode")
def validate_p3_instruction(instruction: Instruction) -> None:
"""Validate one instruction against the P3 class-0/class-1 subset."""
def _target_mask(target: str) -> int:
if target not in APUMC_TARGETS:
raise ValueError(f"unknown APU target {target}")
return APUMC_P3_CAPABILITY_MASK if target == "p3" else APUMC_P4_CAPABILITY_MASK
def instruction_required_mask(instruction: Instruction) -> int:
instruction_class = instruction.instruction_class
opcode = instruction.opcode
if instruction_class == InstructionClass.CONTROL and opcode == ControlOpcode.WAIT:
if instruction.aux == 1:
return 0x0000FFC0
if instruction.aux in (2, 3):
return 1 << 5
return 0
if instruction_class == InstructionClass.BITSTREAM:
return (1 << 0) | ((1 << 1) if opcode in (6, 7) else 0)
if instruction_class == InstructionClass.ENTROPY:
return (1 << 0) | (1 << (2 if opcode <= 2 else 3))
if instruction_class == InstructionClass.LOCAL:
return 1 << (5 if opcode in (5, 6) else 4)
if instruction_class == InstructionClass.KERNEL:
return 1 << (opcode + 6)
return 0
def validate_instruction(instruction: Instruction, target: str = "p3") -> None:
"""Validate one frozen V1 instruction for the selected implementation target."""
target_mask = _target_mask(target)
if instruction.predicate > 11:
raise ValueError("predicate is reserved")
if instruction.instruction_class == InstructionClass.CONTROL:
@@ -318,16 +395,13 @@ def validate_p3_instruction(instruction: Instruction) -> None:
_require_zero(instruction, ("dst", "src0", "src1", "immediate"))
if instruction.predicate != 0 or instruction.aux > 5:
raise ValueError("WAIT encoding is invalid")
raise ValueError("WAIT requires a primitive unavailable in P3")
if target == "p3" or instruction.aux not in (1, 2, 3):
raise ValueError(f"WAIT source is unavailable in {target.upper()}")
return
if instruction.instruction_class != InstructionClass.SCALAR:
if instruction.instruction_class <= 6:
raise ValueError("instruction class is unavailable in P3")
raise ValueError("instruction class is invalid in V1")
if instruction.instruction_class == InstructionClass.SCALAR:
if instruction.opcode > ScalarOpcode.SAT:
raise ValueError("scalar opcode is unallocated")
opcode = ScalarOpcode(instruction.opcode)
if opcode == ScalarOpcode.MOV:
_require_zero(instruction, ("src1", "aux", "immediate"))
@@ -354,9 +428,103 @@ def validate_p3_instruction(instruction: Instruction) -> None:
_require_zero(instruction, ("src1", "immediate"))
if instruction.aux & 0xC0 or (instruction.aux & 0x1F) not in (0, 8, 16, 24):
raise ValueError("SAT width/sign encoding is invalid")
return
if instruction.instruction_class > InstructionClass.TRANSPORT:
raise ValueError("instruction class is invalid in V1")
if 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):
raise ValueError("entry format must be 0, 1, or 2")
if not (0 <= entry.first_pc <= entry.entry_pc <= entry.last_pc < instruction_count):
@@ -365,7 +533,7 @@ def validate_entry(entry: Entry, instruction_count: int) -> None:
raise ValueError("maximum loop count must be 1..65535")
if not 1 <= entry.max_retired <= 0xFFFFFF:
raise ValueError("maximum retired count must be 1..16777215")
if any(
if target == "p3" and any(
(
entry.scratch_base,
entry.scratch_bytes,
@@ -375,9 +543,25 @@ def validate_entry(entry: Entry, instruction_count: int) -> None:
)
):
raise ValueError("P3 entries cannot use scratch, table, or primitives")
if target == "p4":
if entry.primitive_mask & ~APUMC_P4_CAPABILITY_MASK:
raise ValueError("P4 entry requires an unavailable primitive")
if entry.scratch_base & 3 or entry.scratch_bytes & 3:
raise ValueError("scratch range must be four-byte aligned")
scratch_end = entry.scratch_base + entry.scratch_bytes
if scratch_end > APUMC_P4_TABLE_SCRATCH_BYTES:
raise ValueError("scratch range exceeds the P4 codec workspace")
if entry.scratch_bytes and entry.scratch_base < table_payload_bytes:
raise ValueError("scratch range overlaps the active table payload")
if entry.table_offset & 3 or entry.table_bytes & 3:
raise ValueError("entry table range must be four-byte aligned")
if entry.table_offset + entry.table_bytes > table_payload_bytes:
raise ValueError("entry table range exceeds the active table payload")
def control_flow_report(instructions: list[Instruction], entry: Entry) -> dict[str, object]:
def control_flow_report(
instructions: list[Instruction], entry: Entry, target: str = "p3"
) -> dict[str, object]:
"""Prove bounded V1 control flow and return its deterministic report."""
loop_pairs: dict[int, int] = {}
@@ -389,8 +573,8 @@ def control_flow_report(instructions: list[Instruction], entry: Entry) -> dict[s
continue
opcode = ControlOpcode(instruction.opcode)
if opcode in (ControlOpcode.JUMP_FWD, ControlOpcode.CALL_FWD):
target = pc + 1 + instruction.immediate
if target > entry.last_pc:
branch_target = pc + 1 + instruction.immediate
if branch_target > entry.last_pc:
raise ValueError(f"entry {entry.format_id} branch at PC {pc} leaves its range")
elif opcode == ControlOpcode.LOOP_SETUP:
slot = instruction.aux & 3
@@ -445,16 +629,16 @@ def control_flow_report(instructions: list[Instruction], entry: Entry) -> dict[s
if opcode in (ControlOpcode.END, ControlOpcode.TRAP):
terminals.add(pc)
elif opcode == ControlOpcode.JUMP_FWD:
target = pc + 1 + instruction.immediate
successors.append((target, returns, loops))
edges.add((pc, target, "jump_true"))
branch_target = pc + 1 + instruction.immediate
successors.append((branch_target, returns, loops))
edges.add((pc, branch_target, "jump_true"))
elif opcode == ControlOpcode.CALL_FWD:
if len(returns) == 4:
raise ValueError(f"entry {entry.format_id} can exceed four nested calls")
target = pc + 1 + instruction.immediate
successors.append((target, (*returns, pc + 1), loops))
branch_target = pc + 1 + instruction.immediate
successors.append((branch_target, (*returns, pc + 1), loops))
maximum_call_depth = max(maximum_call_depth, len(returns) + 1)
edges.add((pc, target, "call_true"))
edges.add((pc, branch_target, "call_true"))
elif opcode == ControlOpcode.RET:
if not returns:
raise ValueError(f"entry {entry.format_id} can return with an empty stack")
@@ -481,8 +665,11 @@ def control_flow_report(instructions: list[Instruction], entry: Entry) -> dict[s
elif opcode == ControlOpcode.NOP:
successors.append((pc + 1, returns, loops))
edges.add((pc, pc + 1, "fallthrough"))
elif opcode == ControlOpcode.WAIT and target == "p4":
successors.append((pc + 1, returns, loops))
edges.add((pc, pc + 1, "wait_ready"))
else:
raise ValueError("WAIT requires a primitive unavailable in P3")
raise ValueError(f"WAIT requires a primitive unavailable in {target.upper()}")
pending.extend(successors)
if len(visited) > APUMC_MAX_INSTRUCTIONS * 64:
raise ValueError(f"entry {entry.format_id} control-flow proof exceeded its bound")
@@ -510,32 +697,64 @@ def control_flow_report(instructions: list[Instruction], entry: Entry) -> dict[s
}
def validate_control_flow(instructions: list[Instruction], entry: Entry) -> None:
def validate_control_flow(
instructions: list[Instruction], entry: Entry, target: str = "p3"
) -> None:
"""Prove bounded V1 control flow for one entry descriptor."""
control_flow_report(instructions, entry)
control_flow_report(instructions, entry, target)
def build_apumc(instructions: list[Instruction], entries: list[Entry], build_id: int = 0) -> bytes:
def build_apumc(
instructions: list[Instruction],
entries: list[Entry],
build_id: int = 0,
*,
target: str = "p3",
table_payload: bytes = b"",
) -> bytes:
target_mask = _target_mask(target)
if not 1 <= len(instructions) <= APUMC_MAX_INSTRUCTIONS:
raise ValueError("instruction count must be 1..2048")
if len(table_payload) & 3 or len(table_payload) > APUMC_P4_TABLE_SCRATCH_BYTES:
raise ValueError("table payload must be four-byte aligned and at most 24 KiB")
if target == "p3" and table_payload:
raise ValueError("P3 does not admit a table payload")
if len(entries) != APUMC_ENTRY_COUNT:
raise ValueError("APUMC V1 requires exactly three entries")
for index, entry in enumerate(entries):
if entry.format_id != index:
raise ValueError("entry descriptor order must be WAV, MP3, FLAC")
validate_entry(entry, len(instructions))
validate_entry(entry, len(instructions), target, len(table_payload))
for instruction in instructions:
validate_p3_instruction(instruction)
validate_instruction(instruction, target)
for entry in entries:
validate_control_flow(instructions, entry)
validate_control_flow(instructions, entry, target)
for pc in range(entry.first_pc, entry.last_pc + 1):
required = instruction_required_mask(instructions[pc])
if instructions[pc].instruction_class == InstructionClass.CONTROL and required:
if instructions[pc].aux == 1:
if not (entry.primitive_mask & required):
raise ValueError("kernel WAIT requires a class-5 primitive")
elif required & ~entry.primitive_mask:
raise ValueError("FIFO WAIT requires the local-FIFO primitive")
elif required & ~entry.primitive_mask:
raise ValueError(f"entry {entry.format_id} omits an instruction primitive")
descriptor = b"".join(struct.pack("<8I", *entry.words()) for entry in entries)
padding = bytes(APUMC_INSTRUCTION_OFFSET - APUMC_HEADER_BYTES - len(descriptor))
encoded = b"".join(struct.pack("<Q", instruction.encode()) for instruction in instructions)
payload = descriptor + padding + encoded
table_offset = APUMC_INSTRUCTION_OFFSET + len(encoded) if table_payload else 0
payload = descriptor + padding + encoded + table_payload
total_bytes = APUMC_HEADER_BYTES + len(payload)
crc = crc32_iso_hdlc(payload)
required_mask = 0
scratch_end = 0
for entry in entries:
required_mask |= entry.primitive_mask
scratch_end = max(scratch_end, entry.scratch_base + entry.scratch_bytes)
if required_mask & ~target_mask:
raise ValueError(f"bundle requires a primitive unavailable in {target.upper()}")
header = struct.pack(
"<16I",
APUMC_MAGIC,
@@ -543,12 +762,12 @@ def build_apumc(instructions: list[Instruction], entries: list[Entry], build_id:
total_bytes,
len(instructions),
APUMC_INSTRUCTION_OFFSET,
0,
0,
table_offset,
len(table_payload),
APUMC_HEADER_BYTES,
APUMC_ENTRY_COUNT,
APUMC_P3_CAPABILITY_MASK,
0,
required_mask,
scratch_end,
crc,
build_id & 0xFFFFFFFF,
(build_id >> 32) & 0xFFFFFFFF,
@@ -558,7 +777,10 @@ def build_apumc(instructions: list[Instruction], entries: list[Entry], build_id:
return header + payload
def parse_apumc(bundle: bytes) -> tuple[list[Instruction], list[Entry], tuple[int, ...]]:
def parse_apumc(
bundle: bytes, target: str = "p3"
) -> tuple[list[Instruction], list[Entry], tuple[int, ...]]:
target_mask = _target_mask(target)
if len(bundle) < APUMC_HEADER_BYTES:
raise ValueError("bundle is smaller than the APUMC header")
header = struct.unpack_from("<16I", bundle)
@@ -582,7 +804,7 @@ def parse_apumc(bundle: bytes) -> tuple[list[Instruction], list[Entry], tuple[in
entry_end = entry_offset + APUMC_ENTRY_COUNT * APUMC_ENTRY_BYTES
if not 1 <= instruction_count <= APUMC_MAX_INSTRUCTIONS:
raise ValueError("APUMC instruction count is invalid")
if (
common_range_error = (
instruction_offset < APUMC_HEADER_BYTES
or instruction_offset % 64 != 0
or instruction_end > len(bundle)
@@ -591,28 +813,40 @@ def parse_apumc(bundle: bytes) -> tuple[list[Instruction], list[Entry], tuple[in
or entry_end > len(bundle)
or not (instruction_end <= entry_offset or entry_end <= instruction_offset)
or table_offset % 4 != 0
or table_bytes != 0
or table_offset != 0
or header[9] != 0
or header[10] != 0
):
)
table_end = table_offset + table_bytes
if common_range_error:
raise ValueError("APUMC range fields are invalid")
if target == "p3" and (table_bytes or table_offset or header[9] or header[10]):
raise ValueError("APUMC range or P3 capability fields are invalid")
if target == "p4" and (
table_bytes > APUMC_P4_TABLE_SCRATCH_BYTES
or table_bytes & 3
or table_offset > len(bundle)
or (table_bytes and (table_offset < APUMC_HEADER_BYTES or table_end > len(bundle)))
or (table_bytes and not (
(table_end <= instruction_offset or table_offset >= instruction_end)
and (table_end <= entry_offset or table_offset >= entry_end)
))
or header[9] & ~target_mask
or header[10] > APUMC_P4_TABLE_SCRATCH_BYTES
):
raise ValueError("APUMC range or P4 capability fields are invalid")
if crc32_iso_hdlc(bundle[APUMC_HEADER_BYTES:]) != header[11]:
raise ValueError("APUMC CRC mismatch")
entries = []
for index in range(APUMC_ENTRY_COUNT):
words = struct.unpack_from("<8I", bundle, entry_offset + index * APUMC_ENTRY_BYTES)
if (
reserved_error = (
words[0] >> 15
or (words[1] & 0xF800F800)
or words[2]
or words[3]
or words[4] >> 16
or words[5] >> 24
or words[6]
or words[7]
):
raise ValueError(f"APUMC entry {index} has invalid reserved or P3 fields")
or words[2] >> 17
or words[3] >> 17
)
if reserved_error or (target == "p3" and any(words[item] for item in (2, 3, 6, 7))):
raise ValueError(f"APUMC entry {index} has invalid reserved or target fields")
entries.append(
Entry(
format_id=words[0] & 0xF,
@@ -638,14 +872,30 @@ def parse_apumc(bundle: bytes) -> tuple[list[Instruction], list[Entry], tuple[in
occupied[instruction_offset:instruction_end] = bytes([1]) * (
instruction_end - instruction_offset
)
if table_bytes:
occupied[table_offset:table_end] = bytes([1]) * table_bytes
if any(byte for index, byte in enumerate(bundle) if not occupied[index]):
raise ValueError("APUMC padding must be zero")
for index, entry in enumerate(entries):
if entry.format_id != index:
raise ValueError("entry descriptor order must be WAV, MP3, FLAC")
validate_entry(entry, instruction_count)
validate_entry(entry, instruction_count, target, table_bytes)
for instruction in instructions:
validate_p3_instruction(instruction)
validate_instruction(instruction, target)
for entry in entries:
validate_control_flow(instructions, entry)
validate_control_flow(instructions, entry, target)
for pc in range(entry.first_pc, entry.last_pc + 1):
required = instruction_required_mask(instructions[pc])
if instructions[pc].instruction_class == InstructionClass.CONTROL and required:
if instructions[pc].aux == 1:
if not (entry.primitive_mask & required):
raise ValueError("kernel WAIT requires a class-5 primitive")
elif required & ~entry.primitive_mask:
raise ValueError("FIFO WAIT requires the local-FIFO primitive")
elif required & ~entry.primitive_mask:
raise ValueError(f"entry {entry.format_id} omits an instruction primitive")
if header[9] != (entries[0].primitive_mask | entries[1].primitive_mask | entries[2].primitive_mask):
raise ValueError("APUMC header primitive mask does not match entry masks")
if header[10] != max(entry.scratch_base + entry.scratch_bytes for entry in entries):
raise ValueError("APUMC scratch end does not match entry descriptors")
return instructions, entries, header
+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
import argparse
import json
import re
import struct
from dataclasses import dataclass
from pathlib import Path
from apu_isa import (
CONTROL_OPCODES,
DEFERRED_OPCODES,
PREDICATES,
SCALAR_OPCODES,
APUMC_TARGETS,
BitstreamOpcode,
ControlOpcode,
EntropyOpcode,
Entry,
Instruction,
InstructionClass,
KernelOpcode,
LocalOpcode,
ScalarOpcode,
abi_manifest,
build_apumc,
@@ -31,6 +38,8 @@ class Assembly:
instructions: list[Instruction]
entries: list[Entry]
build_id: int
target: str
table_payload: bytes
def _number(token: str) -> int:
@@ -91,10 +100,100 @@ def _encode_instruction(tokens: list[str], pc: int, labels: dict[str, int]) -> I
values["aux"] = _number(args[0])
return Instruction(InstructionClass.CONTROL, opcode, **values)
if mnemonic not in SCALAR_OPCODES:
raise ValueError(f"unknown mnemonic {mnemonic}")
if mnemonic in SCALAR_OPCODES:
opcode = ScalarOpcode(SCALAR_OPCODES[mnemonic])
values = {"predicate": predicate}
elif mnemonic in DEFERRED_OPCODES["bitstream"]:
opcode = BitstreamOpcode(DEFERRED_OPCODES["bitstream"].index(mnemonic))
values = {"predicate": predicate}
if opcode in (BitstreamOpcode.REFILL, BitstreamOpcode.PEEK, BitstreamOpcode.GET):
if len(args) != 2:
raise ValueError(f"{mnemonic} takes a destination and bit width")
values.update(dst=_register(args[0]), immediate=_number(args[1]))
elif opcode == BitstreamOpcode.SKIP:
if len(args) != 1:
raise ValueError("skip takes one bit width")
values["immediate"] = _number(args[0])
elif opcode == BitstreamOpcode.ALIGN:
if args:
raise ValueError("align takes no operands")
elif opcode == BitstreamOpcode.FRAME_SYNC:
if len(args) != 5:
raise ValueError("frame_sync takes dst, pattern, mask, width, and limit")
values.update(
dst=_register(args[0]),
src0=_register(args[1]),
src1=_register(args[2]),
aux=_number(args[3]),
immediate=_number(args[4]),
)
else:
if len(args) != 3:
raise ValueError(f"{mnemonic} takes dst, accumulator, and byte registers")
values.update(dst=_register(args[0]), src0=_register(args[1]), src1=_register(args[2]))
return Instruction(InstructionClass.BITSTREAM, opcode, **values)
elif mnemonic in DEFERRED_OPCODES["entropy"]:
opcode = EntropyOpcode(DEFERRED_OPCODES["entropy"].index(mnemonic))
values = {"predicate": predicate}
if opcode <= EntropyOpcode.HUFF_QUAD:
if len(args) != 4:
raise ValueError(f"{mnemonic} takes dst, table, entries, and maximum length")
values.update(
dst=_register(args[0]),
src0=_register(args[1]),
src1=_register(args[2]),
aux=_number(args[3]),
)
elif opcode == EntropyOpcode.UNARY:
if len(args) != 3:
raise ValueError("unary takes dst, polarity, and maximum run")
values.update(dst=_register(args[0]), aux=_number(args[1]), immediate=_number(args[2]))
else:
if len(args) != 3:
raise ValueError(f"{mnemonic} takes three registers")
values.update(dst=_register(args[0]), src0=_register(args[1]), src1=_register(args[2]))
return Instruction(InstructionClass.ENTROPY, opcode, **values)
elif mnemonic in DEFERRED_OPCODES["local"]:
opcode = LocalOpcode(DEFERRED_OPCODES["local"].index(mnemonic))
values = {"predicate": predicate}
if opcode == LocalOpcode.LD32:
if len(args) != 3:
raise ValueError("ld32 takes destination, address register, and offset")
values.update(dst=_register(args[0]), src0=_register(args[1]), immediate=_number(args[2]) & 0xFFFF)
elif opcode == LocalOpcode.ST32:
if len(args) != 3:
raise ValueError("st32 takes address, value, and offset")
values.update(src0=_register(args[0]), src1=_register(args[1]), immediate=_number(args[2]) & 0xFFFF)
elif opcode in (LocalOpcode.TABLE8, LocalOpcode.TABLE16, LocalOpcode.TABLE32):
if len(args) != 3:
raise ValueError(f"{mnemonic} takes destination, base, and index registers")
values.update(dst=_register(args[0]), src0=_register(args[1]), src1=_register(args[2]))
elif opcode == LocalOpcode.FIFO_POP:
if len(args) != 1:
raise ValueError("fifo_pop takes one destination")
values["dst"] = _register(args[0])
else:
if len(args) != 2:
raise ValueError("fifo_push takes data and metadata registers")
values.update(src0=_register(args[0]), src1=_register(args[1]))
return Instruction(InstructionClass.LOCAL, opcode, **values)
elif mnemonic in DEFERRED_OPCODES["kernel"]:
opcode = KernelOpcode(DEFERRED_OPCODES["kernel"].index(mnemonic))
if len(args) != 5:
raise ValueError(f"{mnemonic} takes output, input, parameter, aux, and count")
return Instruction(
InstructionClass.KERNEL,
opcode,
predicate=predicate,
dst=_register(args[0]),
src0=_register(args[1]),
src1=_register(args[2]),
aux=_number(args[3]),
immediate=_number(args[4]),
)
else:
raise ValueError(f"unknown mnemonic {mnemonic}")
if opcode == ScalarOpcode.MOV:
if len(args) != 2:
raise ValueError("mov takes two registers")
@@ -127,7 +226,9 @@ def _encode_instruction(tokens: list[str], pc: int, labels: dict[str, int]) -> I
return Instruction(InstructionClass.SCALAR, opcode, **values)
def assemble(source: str) -> Assembly:
def assemble(source: str, target: str = "p3") -> Assembly:
if target not in APUMC_TARGETS:
raise ValueError(f"unknown APU target {target}")
lines = []
labels: dict[str, int] = {}
directives: list[list[str]] = []
@@ -149,11 +250,14 @@ def assemble(source: str) -> Assembly:
build_id = 0
entry_directives = []
table_words: list[int] = []
for directive in directives:
if directive[0] == ".build_id" and len(directive) == 2:
build_id = _number(directive[1])
elif directive[0] == ".entry" and len(directive) == 7:
elif directive[0] == ".entry" and len(directive) in (7, 12):
entry_directives.append(directive[1:])
elif directive[0] == ".table32" and len(directive) > 1:
table_words.extend(_number(value) & 0xFFFFFFFF for value in directive[1:])
else:
raise ValueError(f"invalid assembler directive {' '.join(directive)}")
if len(entry_directives) != 3:
@@ -169,21 +273,26 @@ def assemble(source: str) -> Assembly:
entries = []
for values in entry_directives:
try:
entries.append(
Entry(
optional = [0, 0, 0, 0, 0] if len(values) == 6 else [_number(item) for item in values[6:]]
entries.append(Entry(
format_id=_number(values[0]),
entry_pc=labels[values[1]],
first_pc=labels[values[2]],
last_pc=labels[values[3]],
max_loop_count=_number(values[4]),
max_retired=_number(values[5]),
)
)
scratch_base=optional[0],
scratch_bytes=optional[1],
primitive_mask=optional[2],
table_offset=optional[3],
table_bytes=optional[4],
))
except KeyError as error:
raise ValueError(f"unknown entry label {error.args[0]}") from error
entries.sort(key=lambda entry: entry.format_id)
bundle = build_apumc(instructions, entries, build_id)
return Assembly(bundle, dict(sorted(labels.items())), instructions, entries, build_id)
table_payload = b"".join(struct.pack("<I", word) for word in table_words)
bundle = build_apumc(instructions, entries, build_id, target=target, table_payload=table_payload)
return Assembly(bundle, dict(sorted(labels.items())), instructions, entries, build_id, target, table_payload)
def _artifact_data(assembly: Assembly) -> dict[str, object]:
@@ -199,6 +308,7 @@ def _artifact_data(assembly: Assembly) -> dict[str, object]:
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("source", type=Path)
parser.add_argument("--target", choices=APUMC_TARGETS, default="p3")
parser.add_argument("-o", "--output", type=Path, required=True)
parser.add_argument("--symbols", type=Path)
parser.add_argument("--cfg-report", type=Path)
@@ -206,7 +316,7 @@ def main() -> int:
parser.add_argument("--trace-input", type=Path)
parser.add_argument("--abi-input-manifest", type=Path)
args = parser.parse_args()
assembly = assemble(args.source.read_text(encoding="utf-8"))
assembly = assemble(args.source.read_text(encoding="utf-8"), args.target)
args.output.write_bytes(assembly.bundle)
artifacts = {
args.symbols: assembly.symbols,
@@ -214,12 +324,16 @@ def main() -> int:
**_artifact_data(assembly),
"entries": [entry.__dict__ for entry in assembly.entries],
"control_flow": [
control_flow_report(assembly.instructions, entry) for entry in assembly.entries
control_flow_report(assembly.instructions, entry, assembly.target)
for entry in assembly.entries
],
},
args.primitive_manifest: {
"implemented_mask": 0,
"required_mask": 0,
"target": assembly.target,
"implemented_mask": 0 if assembly.target == "p3" else 0x0000FFFF,
"required_mask": __import__("functools").reduce(
int.__or__, (entry.primitive_mask for entry in assembly.entries), 0
),
"entry_masks": [entry.primitive_mask for entry in assembly.entries],
},
args.trace_input: {
+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
DMA, ring scheduler, stream router, Gateway A, and verification-only backend;
`test_apu_register_parity.py` keeps its handwritten RTL/C ABI and matrix
coverage synchronized.
coverage synchronized. `test_apu_primitives.py` compares the P4 assembler,
BAM, loader, sequencer, local SRAM, FIFOs, and production primitive engines
with Icarus and Verilator while keeping its injectors out of product
filelists.
- `test_user_ip_register_parity.py` keeps the integrated slot 1 timer and slot
2 GPIO register offsets synchronized with their application-owned C
definitions and checks their extension-manifest slot assignments.
+1
View File
@@ -221,6 +221,7 @@ module apu_p3_microcode_tb;
.fault_addr_o (seq_fault_addr),
.fault_detail_o (seq_fault_detail),
.perf_retired_o (seq_perf_retired),
.launch_epoch_o (),
.idle_o (seq_idle)
);
+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;
add_register(`APB4_APU__IP_ID, AccessRo, 32'h4150_5530, 1'b0);
add_register(`APB4_APU__IP_VERSION, AccessRo, 32'h0001_0000, 1'b0);
add_register(`APB4_APU__CAPABILITY0, AccessRo, 32'h0000_0098, 1'b0);
add_register(`APB4_APU__CAPABILITY1, AccessRo, 32'h0000_0010, 1'b0);
add_register(`APB4_APU__CAPABILITY0, AccessRo, 32'h0000_0198, 1'b0);
add_register(`APB4_APU__CAPABILITY1, AccessRo, 32'h0182_7010, 1'b0);
add_register(`APB4_APU__COMMAND, AccessWo, 32'd0, 1'b1);
add_register(`APB4_APU__STATUS, AccessRo, 32'h0000_0100, 1'b0);
add_register(`APB4_APU__IRQ_STATE, AccessRw, 32'd0, 1'b0);
+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_control_store.sv"),
str(multimedia / "apu_microcode_loader.sv"),
str(multimedia / "apu_local_sram.sv"),
str(multimedia / "apu_bitstream_engine.sv"),
str(multimedia / "apu_entropy_engine.sv"),
str(multimedia / "apu_reconstruction_engine.sv"),
str(multimedia / "apu_transform_engine.sv"),
str(multimedia / "apu_resampler.sv"),
str(multimedia / "apu_kernel_engine.sv"),
str(multimedia / "apu_primitive_dispatcher.sv"),
str(multimedia / "apu_codec_sequencer.sv"),
str(multimedia / "apu_reg.sv"),
str(multimedia / "apb4_apu.sv"),
@@ -128,6 +136,14 @@ def test_apu_p1_integrated_irq_ownership_topology(tmp_path: Path) -> None:
str(multimedia / "apu_stream_router.sv"),
str(multimedia / "apu_control_store.sv"),
str(multimedia / "apu_microcode_loader.sv"),
str(multimedia / "apu_local_sram.sv"),
str(multimedia / "apu_bitstream_engine.sv"),
str(multimedia / "apu_entropy_engine.sv"),
str(multimedia / "apu_reconstruction_engine.sv"),
str(multimedia / "apu_transform_engine.sv"),
str(multimedia / "apu_resampler.sv"),
str(multimedia / "apu_kernel_engine.sv"),
str(multimedia / "apu_primitive_dispatcher.sv"),
str(multimedia / "apu_codec_sequencer.sv"),
str(multimedia / "apu_reg.sv"),
str(multimedia / "apb4_apu.sv"),
@@ -532,6 +548,14 @@ done:
str(multimedia / "apu_stream_router.sv"),
str(multimedia / "apu_control_store.sv"),
str(multimedia / "apu_microcode_loader.sv"),
str(multimedia / "apu_local_sram.sv"),
str(multimedia / "apu_bitstream_engine.sv"),
str(multimedia / "apu_entropy_engine.sv"),
str(multimedia / "apu_reconstruction_engine.sv"),
str(multimedia / "apu_transform_engine.sv"),
str(multimedia / "apu_resampler.sv"),
str(multimedia / "apu_kernel_engine.sv"),
str(multimedia / "apu_primitive_dispatcher.sv"),
str(multimedia / "apu_codec_sequencer.sv"),
str(multimedia / "apu_reg.sv"),
str(multimedia / "apb4_apu.sv"),
@@ -575,7 +599,7 @@ done:
assert "APU-P3 APB, DMA, loader integration passed" in result.stdout
def test_apu_p3_product_scope_remains_coreless_and_fail_closed() -> None:
def test_apu_p4_product_scope_remains_coreless_and_fail_closed() -> None:
top = (ROOT / "rtl/ip/multimedia/apb4_apu.sv").read_text(encoding="utf-8")
register_block = (ROOT / "rtl/ip/multimedia/apu_reg.sv").read_text(encoding="utf-8")
product_filelist = (ROOT / "rtl/mini/filelist/ip.fl").read_text(encoding="utf-8")
@@ -585,11 +609,23 @@ def test_apu_p3_product_scope_remains_coreless_and_fail_closed() -> None:
assert "32'h0000_0029" in register_block
assert "s_merged_write != 32'd0" in register_block
assert "apu_interpreter" not in product_filelist
assert "apu_primitives.py" not in product_filelist
assert "apu_p4_primitives_tb" not in product_filelist
assert "apu_p4_loader_tb" not in product_filelist
assert "apu_p4_sequencer_tb" not in product_filelist
assert "apu_p3_microcode_tb" not in product_filelist
assert "VerificationCrcBypass" not in product_filelist
assert ".VerificationCrcBypass" not in top
assert "VerificationControlFlowBypass" not in product_filelist
assert ".VerificationControlFlowBypass" not in top
for production_module in (
"apu_local_sram.sv",
"apu_bitstream_engine.sv",
"apu_entropy_engine.sv",
"apu_kernel_engine.sv",
"apu_primitive_dispatcher.sv",
):
assert production_module in product_filelist
for forbidden in ("hazard3", "vexiiriscv", "rv32", "dsp_core"):
assert forbidden not in top.lower()
File diff suppressed because it is too large. Load diff
+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
@@ -72,7 +72,7 @@ def test_apu_register_matrix_covers_each_offset_once() -> None:
assert set(covered) == set(expected)
def test_apu_p3_tool_isa_matches_handwritten_rtl_and_c() -> None:
def test_apu_p4_tool_isa_matches_handwritten_rtl_and_c() -> None:
rtl = _rtl_values()
manifest = abi_manifest()
exact = {
@@ -82,6 +82,10 @@ def test_apu_p3_tool_isa_matches_handwritten_rtl_and_c() -> None:
"APUMC_ENTRY_BYTES": manifest["apumc"]["entry_bytes"],
"APUMC_ENTRY_COUNT": manifest["apumc"]["entry_count"],
"APUMC_MAX_INSTRUCTIONS": manifest["apumc"]["max_instructions"],
"APUMC_P3_PRIMITIVE_MASK": manifest["p3_implemented_primitive_mask"],
"APUMC_P4_PRIMITIVE_MASK": manifest["p4_implemented_primitive_mask"],
"LOCAL_DATA_BYTES": manifest["p4_local_data_bytes"],
"LOCAL_TABLE_SCRATCH_BYTES": manifest["p4_local_table_scratch_bytes"],
}
for name, value in manifest["classes"].items():
exact[f"MC_CLASS_{name.upper()}"] = value
+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"
opipsram_filelist = tmp_path / "opipsram.fl"
apu_filelist = tmp_path / "apu.fl"
apu_primitives_filelist = tmp_path / "apu_primitives.fl"
apu_loader_filelist = tmp_path / "apu_loader.fl"
apu_sequencer_filelist = tmp_path / "apu_sequencer.fl"
gateway_a_filelist = tmp_path / "gateway_a.fl"
@@ -359,6 +360,9 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
"opipsram", opipsram_filelist, memory_map, topology, user_extensions
)
assert generate_formal_filelist("apu", apu_filelist, memory_map, topology, user_extensions)
assert generate_formal_filelist(
"apu_primitives", apu_primitives_filelist, memory_map, topology, user_extensions
)
assert generate_formal_filelist(
"apu_loader", apu_loader_filelist, memory_map, topology, user_extensions
)
@@ -381,6 +385,7 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
onchip_ram = parse_filelists([onchip_ram_filelist], require_files=False)
opipsram = parse_filelists([opipsram_filelist], require_files=False)
apu = parse_filelists([apu_filelist], require_files=False)
apu_primitives = parse_filelists([apu_primitives_filelist], require_files=False)
apu_loader = parse_filelists([apu_loader_filelist], require_files=False)
apu_sequencer = parse_filelists([apu_sequencer_filelist], require_files=False)
gateway_a = parse_filelists([gateway_a_filelist], require_files=False)
@@ -441,6 +446,22 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
assert ROOT / "rtl/mini/formal/opipsram_formal.sv" in opipsram.files
assert ROOT / "rtl/ip/multimedia/apu_dma.sv" in apu.files
assert ROOT / "rtl/mini/formal/apu_formal.sv" in apu.files
assert ROOT / "rtl/ip/multimedia/apu_resampler.sv" in apu_primitives.files
assert ROOT / "rtl/ip/multimedia/apu_local_sram.sv" in apu_primitives.files
assert ROOT / "rtl/ip/multimedia/apu_kernel_engine.sv" in apu_primitives.files
assert ROOT / "rtl/ip/multimedia/apu_primitive_dispatcher.sv" in apu_primitives.files
assert ROOT / "rtl/managed/clusterip/common/rtl/utils/fifo.sv" in apu_primitives.files
assert ROOT / "rtl/mini/formal/apu_primitives_formal.sv" in apu_primitives.files
formal_makefile = (ROOT / "rtl/mini/mk/formal.mk").read_text(encoding="utf-8")
for scenario in (
"apu_primitives_invalid_read",
"apu_primitives_local",
"apu_primitives_kernel",
"apu_primitives_input_fifo",
"apu_primitives_alignment",
"apu_primitives_output_fifo",
):
assert scenario in formal_makefile
assert ROOT / "rtl/ip/multimedia/apu_microcode_loader.sv" in apu_loader.files
assert ROOT / "rtl/mini/formal/apu_loader_formal.sv" in apu_loader.files
assert ROOT / "rtl/ip/multimedia/apu_codec_sequencer.sv" in apu_sequencer.files