feat: add APU-P3 tests and enhancements for RTL and microcode integration

- Introduced new test cases for APU-P3 loader, control store, and sequencer functionality in .
- Enhanced the APU ring scheduler and stream router testbenches to validate reset timing and state transitions.
- Added comprehensive tests for APU microcode tools, ensuring assembler and interpreter consistency.
- Updated register parity tests to synchronize APU RTL and SDK ABI across P3.
- Expanded formal filelist generation to include APU loader and sequencer.
- Improved SBY configuration for formal verification with Bitwuzla.
This commit is contained in:
maksyuki committed 2026-09-04 11:56:05 +08:00
1 parent d3990fdbd4
commit 67281e224f
33 files changed
+6171 -100

No files matched your search

+1 -1
View File
@@ -388,7 +388,7 @@ int main(void) {
if (!rs_ci_smoke_apu()) {
rs_test_finish(RS_TEST_FAILED, 14U);
}
printf("ci_smoke: APU-P2 discovery passed\n");
printf("ci_smoke: APU-P3 discovery passed\n");
if (!rs_ci_smoke_fabric_monitor_start()) {
rs_test_finish(RS_TEST_FAILED, 13U);
}
+172 -2
View File
@@ -265,12 +265,182 @@
#define RS_APU_ABI_PERF_CONTROL_CLEAR 1U
#define RS_APU_ABI_PERF_CONTROL_SNAPSHOT 2U
#define RS_APU_ABI_PERF_STATUS_SNAPSHOT_VALID 0U
#define RS_APU_ABI_MC_STATUS_BUSY 0U
#define RS_APU_ABI_MC_STATUS_VALID 1U
#define RS_APU_ABI_MC_STATUS_HEADER_ERROR 2U
#define RS_APU_ABI_MC_STATUS_RANGE_ERROR 3U
#define RS_APU_ABI_MC_STATUS_CONTROL_ERROR 4U
#define RS_APU_ABI_MC_STATUS_TABLE_ERROR 5U
#define RS_APU_ABI_MC_STATUS_CRC_ERROR 6U
#define RS_APU_ABI_MC_STATUS_CAPABILITY_ERROR 7U
#define RS_APU_ABI_MC_LOCK_LOCKED 0U
#define RS_APU_ABI_SEQUENCER_STATUS_PC 0U
#define RS_APU_ABI_SEQUENCER_STATUS_CLASS 11U
#define RS_APU_ABI_SEQUENCER_STATUS_OPCODE 15U
#define RS_APU_ABI_SEQUENCER_STATUS_WAIT 19U
#define RS_APU_ABI_SEQUENCER_STATUS_LOOP 20U
#define RS_APU_ABI_APUMC_MAGIC UINT32_C(0x41504D43)
#define RS_APU_ABI_APUMC_ABI UINT32_C(0x00010000)
#define RS_APU_ABI_APUMC_HEADER_BYTES 64U
#define RS_APU_ABI_APUMC_HEADER_WORDS 16U
#define RS_APU_ABI_APUMC_ENTRY_BYTES 32U
#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_HEADER_MAGIC 0U
#define RS_APU_ABI_APUMC_HEADER_ABI 1U
#define RS_APU_ABI_APUMC_HEADER_TOTAL_BYTES 2U
#define RS_APU_ABI_APUMC_HEADER_INSTRUCTIONS 3U
#define RS_APU_ABI_APUMC_HEADER_INSTR_OFFSET 4U
#define RS_APU_ABI_APUMC_HEADER_TABLE_OFFSET 5U
#define RS_APU_ABI_APUMC_HEADER_TABLE_BYTES 6U
#define RS_APU_ABI_APUMC_HEADER_ENTRY_OFFSET 7U
#define RS_APU_ABI_APUMC_HEADER_ENTRY_COUNT 8U
#define RS_APU_ABI_APUMC_HEADER_PRIMITIVES 9U
#define RS_APU_ABI_APUMC_HEADER_SCRATCH_END 10U
#define RS_APU_ABI_APUMC_HEADER_CRC 11U
#define RS_APU_ABI_APUMC_HEADER_BUILD_LO 12U
#define RS_APU_ABI_APUMC_HEADER_BUILD_HI 13U
#define RS_APU_ABI_APUMC_ENTRY_FORMAT_PC 0U
#define RS_APU_ABI_APUMC_ENTRY_RANGE 1U
#define RS_APU_ABI_APUMC_ENTRY_SCRATCH_BASE 2U
#define RS_APU_ABI_APUMC_ENTRY_SCRATCH_BYTES 3U
#define RS_APU_ABI_APUMC_ENTRY_MAX_LOOP 4U
#define RS_APU_ABI_APUMC_ENTRY_MAX_RETIRED 5U
#define RS_APU_ABI_APUMC_ENTRY_PRIMITIVES 6U
#define RS_APU_ABI_APUMC_ENTRY_TABLE 7U
#define RS_APU_ABI_MC_INSTRUCTION_IMMEDIATE 0U
#define RS_APU_ABI_MC_INSTRUCTION_AUX 32U
#define RS_APU_ABI_MC_INSTRUCTION_SRC1 40U
#define RS_APU_ABI_MC_INSTRUCTION_SRC0 44U
#define RS_APU_ABI_MC_INSTRUCTION_DST 48U
#define RS_APU_ABI_MC_INSTRUCTION_PREDICATE 52U
#define RS_APU_ABI_MC_INSTRUCTION_OPCODE 56U
#define RS_APU_ABI_MC_INSTRUCTION_CLASS 60U
#define RS_APU_ABI_MC_CLASS_CONTROL 0U
#define RS_APU_ABI_MC_CLASS_SCALAR 1U
#define RS_APU_ABI_MC_CLASS_BITSTREAM 2U
#define RS_APU_ABI_MC_CLASS_ENTROPY 3U
#define RS_APU_ABI_MC_CLASS_LOCAL 4U
#define RS_APU_ABI_MC_CLASS_KERNEL 5U
#define RS_APU_ABI_MC_CLASS_TRANSPORT 6U
#define RS_APU_ABI_MC_CONTROL_NOP 0U
#define RS_APU_ABI_MC_CONTROL_END 1U
#define RS_APU_ABI_MC_CONTROL_TRAP 2U
#define RS_APU_ABI_MC_CONTROL_JUMP_FWD 3U
#define RS_APU_ABI_MC_CONTROL_CALL_FWD 4U
#define RS_APU_ABI_MC_CONTROL_RET 5U
#define RS_APU_ABI_MC_CONTROL_LOOP_SETUP 6U
#define RS_APU_ABI_MC_CONTROL_LOOP_BACK 7U
#define RS_APU_ABI_MC_CONTROL_WAIT 8U
#define RS_APU_ABI_MC_SCALAR_MOV 0U
#define RS_APU_ABI_MC_SCALAR_MOVI 1U
#define RS_APU_ABI_MC_SCALAR_ADD 2U
#define RS_APU_ABI_MC_SCALAR_SUB 3U
#define RS_APU_ABI_MC_SCALAR_AND 4U
#define RS_APU_ABI_MC_SCALAR_OR 5U
#define RS_APU_ABI_MC_SCALAR_XOR 6U
#define RS_APU_ABI_MC_SCALAR_SHL 7U
#define RS_APU_ABI_MC_SCALAR_SHR 8U
#define RS_APU_ABI_MC_SCALAR_SAR 9U
#define RS_APU_ABI_MC_SCALAR_CMP 10U
#define RS_APU_ABI_MC_SCALAR_MIN 11U
#define RS_APU_ABI_MC_SCALAR_MAX 12U
#define RS_APU_ABI_MC_SCALAR_SAT 13U
#define RS_APU_ABI_MC_BITSTREAM_REFILL 0U
#define RS_APU_ABI_MC_BITSTREAM_PEEK 1U
#define RS_APU_ABI_MC_BITSTREAM_GET 2U
#define RS_APU_ABI_MC_BITSTREAM_SKIP 3U
#define RS_APU_ABI_MC_BITSTREAM_ALIGN 4U
#define RS_APU_ABI_MC_BITSTREAM_FRAME_SYNC 5U
#define RS_APU_ABI_MC_BITSTREAM_CRC8 6U
#define RS_APU_ABI_MC_BITSTREAM_CRC16 7U
#define RS_APU_ABI_MC_ENTROPY_HUFF_SYMBOL 0U
#define RS_APU_ABI_MC_ENTROPY_HUFF_PAIR 1U
#define RS_APU_ABI_MC_ENTROPY_HUFF_QUAD 2U
#define RS_APU_ABI_MC_ENTROPY_UNARY 3U
#define RS_APU_ABI_MC_ENTROPY_RICE4 4U
#define RS_APU_ABI_MC_ENTROPY_RICE5 5U
#define RS_APU_ABI_MC_ENTROPY_SIGN_RESTORE 6U
#define RS_APU_ABI_MC_LOCAL_LD32 0U
#define RS_APU_ABI_MC_LOCAL_ST32 1U
#define RS_APU_ABI_MC_LOCAL_TABLE8 2U
#define RS_APU_ABI_MC_LOCAL_TABLE16 3U
#define RS_APU_ABI_MC_LOCAL_TABLE32 4U
#define RS_APU_ABI_MC_LOCAL_FIFO_POP 5U
#define RS_APU_ABI_MC_LOCAL_FIFO_PUSH 6U
#define RS_APU_ABI_MC_KERNEL_REQUANT 0U
#define RS_APU_ABI_MC_KERNEL_STEREO 1U
#define RS_APU_ABI_MC_KERNEL_IMDCT6 2U
#define RS_APU_ABI_MC_KERNEL_IMDCT18 3U
#define RS_APU_ABI_MC_KERNEL_DCT32_POLY 4U
#define RS_APU_ABI_MC_KERNEL_FIXED 5U
#define RS_APU_ABI_MC_KERNEL_LPC 6U
#define RS_APU_ABI_MC_KERNEL_DECORRELATE 7U
#define RS_APU_ABI_MC_KERNEL_RESAMPLE 8U
#define RS_APU_ABI_MC_KERNEL_PCM_PACK 9U
#define RS_APU_ABI_MC_TRANSPORT_INPUT_REFILL 0U
#define RS_APU_ABI_MC_TRANSPORT_OUTPUT_COMMIT 1U
#define RS_APU_ABI_MC_TRANSPORT_OUTPUT_STREAM 2U
#define RS_APU_ABI_MC_TRANSPORT_DMA_WAIT 3U
#define RS_APU_ABI_MC_TRANSPORT_FRAME_COMMIT 4U
#define RS_APU_ABI_MC_TRANSPORT_JOB_RESULT 5U
#define RS_APU_ABI_MC_TRANSPORT_EVENT 6U
#define RS_APU_ABI_MC_PRED_ALWAYS 0U
#define RS_APU_ABI_MC_PRED_EQ 1U
#define RS_APU_ABI_MC_PRED_NE 2U
#define RS_APU_ABI_MC_PRED_SIGNED_LT 3U
#define RS_APU_ABI_MC_PRED_SIGNED_GE 4U
#define RS_APU_ABI_MC_PRED_UNSIGNED_LT 5U
#define RS_APU_ABI_MC_PRED_UNSIGNED_GE 6U
#define RS_APU_ABI_MC_PRED_INPUT_EXHAUSTED 7U
#define RS_APU_ABI_MC_PRED_INPUT_READY 8U
#define RS_APU_ABI_MC_PRED_OUTPUT_READY 9U
#define RS_APU_ABI_MC_PRED_KERNEL_DONE 10U
#define RS_APU_ABI_MC_PRED_TRANSPORT_DONE 11U
#define RS_APU_ABI_MC_WAIT_DMA 0U
#define RS_APU_ABI_MC_WAIT_KERNEL 1U
#define RS_APU_ABI_MC_WAIT_INPUT_FIFO 2U
#define RS_APU_ABI_MC_WAIT_OUTPUT_FIFO 3U
#define RS_APU_ABI_MC_WAIT_TX_STREAM 4U
#define RS_APU_ABI_MC_WAIT_RING_WRITEBACK 5U
#define RS_APU_ABI_MC_PRIMITIVE_BITSTREAM 0U
#define RS_APU_ABI_MC_PRIMITIVE_CRC 1U
#define RS_APU_ABI_MC_PRIMITIVE_HUFFMAN 2U
#define RS_APU_ABI_MC_PRIMITIVE_RICE 3U
#define RS_APU_ABI_MC_PRIMITIVE_LOCAL_MEMORY 4U
#define RS_APU_ABI_MC_PRIMITIVE_LOCAL_FIFO 5U
#define RS_APU_ABI_MC_PRIMITIVE_REQUANTIZE 6U
#define RS_APU_ABI_MC_PRIMITIVE_STEREO 7U
#define RS_APU_ABI_MC_PRIMITIVE_IMDCT6 8U
#define RS_APU_ABI_MC_PRIMITIVE_IMDCT18 9U
#define RS_APU_ABI_MC_PRIMITIVE_DCT32_POLY 10U
#define RS_APU_ABI_MC_PRIMITIVE_FIXED 11U
#define RS_APU_ABI_MC_PRIMITIVE_LPC 12U
#define RS_APU_ABI_MC_PRIMITIVE_DECORRELATE 13U
#define RS_APU_ABI_MC_PRIMITIVE_RESAMPLER 14U
#define RS_APU_ABI_MC_PRIMITIVE_PCM_PACK 15U
#define RS_APU_ABI_MC_PRIMITIVE_DMA_INPUT 16U
#define RS_APU_ABI_MC_PRIMITIVE_DMA_OUTPUT 17U
#define RS_APU_ABI_MC_PRIMITIVE_STREAM_OUTPUT 18U
#define RS_APU_ABI_MC_PRIMITIVE_FRAME_COMMIT 19U
#define RS_APU_ABI_MC_PRIMITIVE_EVENT 20U
#define RS_APU_ABI_MC_TRAP_ILLEGAL 1U
#define RS_APU_ABI_MC_TRAP_PC_RANGE 2U
#define RS_APU_ABI_MC_TRAP_CALL_STACK 3U
#define RS_APU_ABI_MC_TRAP_LOOP 4U
#define RS_APU_ABI_MC_TRAP_LOCAL_RANGE 5U
#define RS_APU_ABI_MC_TRAP_UNAVAILABLE 6U
#define RS_APU_ABI_MC_TRAP_WATCHDOG 7U
#define RS_APU_ABI_MC_TRAP_RETIRED_BUDGET 8U
#define RS_APU_ABI_MC_TRAP_ENGINE 9U
#define RS_APU_ABI_MC_TRAP_EXPLICIT 10U
#define RS_APU_IP_ID_VALUE UINT32_C(0x41505530)
#define RS_APU_IP_VERSION_VALUE UINT32_C(0x00010000)
#define RS_APU_IP_VERSION_MAJOR_MASK UINT32_C(0xFFFF0000)
#define RS_APU_CAPABILITY0_IMPLEMENTED UINT32_C(0x00000018)
#define RS_APU_CAPABILITY1_IMPLEMENTED UINT32_C(0x00000000)
#define RS_APU_CAPABILITY0_IMPLEMENTED UINT32_C(0x00000098)
#define RS_APU_CAPABILITY1_IMPLEMENTED UINT32_C(0x00000010)
#define RS_APU_DIGEST_IMPLEMENTED UINT32_C(0x00000000)
#define RS_APU_IRQ_ALL UINT32_C(0x000007FF)
+228 -23
View File
@@ -24,16 +24,43 @@ module apb4_apu (
output logic irq_o
// verilog_format: on
);
logic s_soft_reset, s_abort, s_cnt_clear, s_xrun_clear, s_perf_enable;
logic s_soft_reset, s_abort, s_microcode_load, s_cnt_clear, s_xrun_clear, s_perf_enable;
logic [ 3:0] s_stream_route;
logic [15:0] s_stream_watermark;
logic [31:0] s_read_base, s_read_limit, s_write_base, s_write_limit;
logic [31:0] s_dma_timeout, s_ring_base, s_ring_coalesce;
logic [31:0] s_sequencer_timeout, s_mc_image_addr, s_mc_image_size, s_mc_expected_crc;
logic [8:0] s_ring_size;
logic [7:0] s_ring_tail, s_ring_head;
logic [1:0] s_ring_control;
logic [31:0] s_stream_stat, s_job_stat, s_ring_stat, s_ring_completed;
logic [10:0] s_irq_set;
logic [ 7:0] s_mc_status;
logic [31:0] s_mc_abi, s_mc_actual_crc, s_mc_load_count;
logic [63:0] s_mc_build_id;
logic s_mc_lock, s_mc_load_done, s_mc_abort_done, s_mc_idle;
logic s_mc_fault_valid;
logic [5:0] s_mc_fault_code;
logic [3:0] s_mc_fault_stage;
logic [1:0] s_mc_fault_resp;
logic [31:0] s_mc_fault_addr, s_mc_fault_detail;
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 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 [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;
logic [15:0][31:0] s_seq_gpr;
logic [ 5:0] s_seq_fault_code;
logic [ 3:0] s_seq_fault_stage;
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_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;
@@ -46,9 +73,13 @@ module apb4_apu (
logic [ 3:0] s_fault_stage;
logic [ 1:0] s_fault_resp;
logic [ 7:0] s_fault_index;
logic [31:0] s_fault_addr;
logic [31:0] s_fault_addr, s_fault_detail;
logic s_dma_req_valid, s_dma_req_ready, s_dma_req_write;
logic [31:0] s_dma_req_addr, s_dma_req_bytes;
logic s_ring_dma_req_valid, s_ring_dma_req_ready, s_ring_dma_req_write;
logic [31:0] s_ring_dma_req_addr, s_ring_dma_req_bytes;
logic s_mc_dma_req_valid, s_mc_dma_req_ready, s_mc_dma_ready;
logic [31:0] s_mc_dma_req_addr, s_mc_dma_req_bytes;
logic s_dma_busy, s_dma_done, s_dma_err, s_dma_aborted, s_dma_aborting;
logic s_dma_abort;
logic [ 5:0] s_dma_err_code;
@@ -80,6 +111,12 @@ module apb4_apu (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_stream_if #(
.DATA_WIDTH(32)
) u_scheduler_read_axis (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_stream_if #(
.DATA_WIDTH(32)
) u_apu_tx_axis (
@@ -102,20 +139,32 @@ module apb4_apu (
assign u_apu_tx_axis.tuser = '0;
assign u_apu_tx_axis.tvalid = 1'b0;
assign u_apu_rx_axis.tready = 1'b0;
assign u_scheduler_read_axis.tdata = u_dma_read_axis.tdata;
assign u_scheduler_read_axis.tkeep = u_dma_read_axis.tkeep;
assign u_scheduler_read_axis.tstrb = u_dma_read_axis.tstrb;
assign u_scheduler_read_axis.tlast = u_dma_read_axis.tlast;
assign u_scheduler_read_axis.tid = u_dma_read_axis.tid;
assign u_scheduler_read_axis.tdest = u_dma_read_axis.tdest;
assign u_scheduler_read_axis.tuser = u_dma_read_axis.tuser;
assign u_scheduler_read_axis.tvalid = u_dma_read_axis.tvalid && s_mc_idle;
assign u_dma_read_axis.tready = s_mc_idle ? u_scheduler_read_axis.tready : s_mc_dma_ready;
assign s_irq_set = {
1'b0,
s_seq_trap_event,
s_xrun_evt,
s_fault_valid,
s_output_watermark_evt,
s_input_watermark_evt,
s_abort_done,
3'b000,
s_abort_done || s_mc_abort_done || s_seq_abort_done,
1'b0,
s_mc_load_done,
1'b0,
s_ring_event,
1'b0
};
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;
assign s_transport_idle = s_scheduler_idle && !s_dma_busy && s_stream_idle && s_mc_idle &&
s_seq_idle;
assign idle_o = s_transport_idle;
assign s_resource_reset_request = s_resource_reset_pending_q ||
(resource_reset_i && !s_resource_reset_seen_q);
@@ -124,22 +173,35 @@ module apb4_apu (
assign s_dma_admission_block = (quiesce_i || s_resource_reset_request) &&
!s_job_stat[`APB4_APU__JOB_STATUS_BUSY] &&
!s_ring_stat[`APB4_APU__RING_STATUS_WRITEBACK_PENDING];
assign s_dma_req_valid = s_mc_idle ? s_ring_dma_req_valid :
(s_mc_dma_req_valid && !s_resource_reset_request);
assign s_dma_req_write = s_mc_idle && s_ring_dma_req_write;
assign s_dma_req_addr = s_mc_idle ? s_ring_dma_req_addr : s_mc_dma_req_addr;
assign s_dma_req_bytes = s_mc_idle ? s_ring_dma_req_bytes : s_mc_dma_req_bytes;
assign s_ring_dma_req_ready = s_mc_idle && s_dma_req_ready;
assign s_mc_dma_req_ready = !s_mc_idle && s_dma_req_ready && !s_resource_reset_request;
always_comb begin
s_fault_valid = s_ring_err;
s_fault_code = s_ring_err_code;
s_fault_stage = s_ring_err_stage;
s_fault_resp = s_ring_err_resp;
s_fault_index = s_ring_err_index;
s_fault_addr = s_ring_err_addr;
if (!s_ring_err && s_rx_route_apu && i2s_rx_overrun_i) begin
s_fault_valid = s_ring_err || s_mc_fault_valid || s_seq_fault_valid;
s_fault_code = s_ring_err ? s_ring_err_code :
(s_mc_fault_valid ? s_mc_fault_code : s_seq_fault_code);
s_fault_stage = s_ring_err ? s_ring_err_stage :
(s_mc_fault_valid ? s_mc_fault_stage : s_seq_fault_stage);
s_fault_resp = s_ring_err ? s_ring_err_resp :
(s_mc_fault_valid ? s_mc_fault_resp : s_seq_fault_resp);
s_fault_index = s_ring_err ? s_ring_err_index : (s_mc_fault_valid ? 8'd0 : s_seq_fault_index);
s_fault_addr = s_ring_err ? s_ring_err_addr :
(s_mc_fault_valid ? s_mc_fault_addr : s_seq_fault_addr);
s_fault_detail = s_mc_fault_valid ? s_mc_fault_detail :
(s_seq_fault_valid ? s_seq_fault_detail : 32'd0);
if (!s_fault_valid && s_rx_route_apu && i2s_rx_overrun_i) begin
s_fault_valid = 1'b1;
s_fault_code = `APB4_APU__ERROR_CODE_STREAM_OVERRUN;
s_fault_stage = `APB4_APU__ERROR_STAGE_KWS_FRONTEND;
s_fault_resp = 2'd0;
s_fault_index = 8'd0;
s_fault_addr = 32'd0;
end else if (!s_ring_err && s_tx_route_apu && i2s_tx_underrun_i) begin
end else if (!s_fault_valid && s_tx_route_apu && i2s_tx_underrun_i) begin
s_fault_valid = 1'b1;
s_fault_code = `APB4_APU__ERROR_CODE_STREAM_UNDERRUN;
s_fault_stage = `APB4_APU__ERROR_STAGE_DMA_WRITE;
@@ -171,6 +233,15 @@ module apb4_apu (
.ring_status_i (s_ring_stat),
.ring_head_i (s_ring_head),
.ring_completed_i (s_ring_completed),
.mc_status_i (s_mc_status),
.mc_abi_i (s_mc_abi),
.mc_build_id_i (s_mc_build_id),
.mc_lock_i (s_mc_lock),
.mc_actual_crc_i (s_mc_actual_crc),
.mc_load_count_i (s_mc_load_count),
.sequencer_status_i (s_seq_status),
.sequencer_retired_i (s_seq_retired),
.sequencer_trapped_i (s_seq_trapped),
.irq_set_i (s_irq_set),
.fault_valid_i (s_fault_valid),
.fault_code_i (s_fault_code),
@@ -178,17 +249,19 @@ module apb4_apu (
.fault_resp_i (s_fault_resp),
.fault_index_i (s_fault_index),
.fault_addr_i (s_fault_addr),
.fault_detail_i (32'd0),
.fault_detail_i (s_fault_detail),
.perf_active_cycles_i (s_active_cycles_q),
.perf_input_bytes_i (s_dma_read_bytes),
.perf_output_bytes_i (s_dma_write_bytes),
.perf_dma_read_stalls_i (s_dma_read_stalls),
.perf_dma_write_stalls_i(s_dma_write_stalls),
.perf_stream_stalls_i (s_stream_stalls_q),
.perf_sequencer_instr_i (s_seq_perf_retired),
.perf_faults_i (s_faults_q),
.apb4 (apb4),
.soft_reset_o (s_soft_reset),
.abort_o (s_abort),
.microcode_load_o (s_microcode_load),
.counter_clear_o (s_cnt_clear),
.perf_enable_o (s_perf_enable),
.xrun_clear_o (s_xrun_clear),
@@ -199,6 +272,10 @@ module apb4_apu (
.write_base_o (s_write_base),
.write_limit_o (s_write_limit),
.dma_timeout_o (s_dma_timeout),
.sequencer_timeout_o (s_sequencer_timeout),
.mc_image_addr_o (s_mc_image_addr),
.mc_image_size_o (s_mc_image_size),
.mc_expected_crc_o (s_mc_expected_crc),
.ring_base_o (s_ring_base),
.ring_size_o (s_ring_size),
.ring_tail_o (s_ring_tail),
@@ -212,7 +289,7 @@ module apb4_apu (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.abort_i (s_dma_abort),
.quiesce_i (s_dma_admission_block),
.quiesce_i (s_dma_admission_block && s_mc_idle),
.bridge_epoch_i (bridge_epoch_i),
.perf_enable_i (s_perf_enable),
.counter_clear_i (s_cnt_clear),
@@ -246,6 +323,128 @@ module apb4_apu (
.axi4 (axi4)
);
apu_microcode_loader u_microcode_loader (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_microcode_load),
.abort_i (s_abort),
.resource_reset_i (s_resource_reset_request),
.soft_reset_i (s_soft_reset),
.counter_clear_i (s_cnt_clear),
.image_addr_i (s_mc_image_addr),
.image_size_i (s_mc_image_size),
.expected_crc_i (s_mc_expected_crc),
.dma_request_valid_o(s_mc_dma_req_valid),
.dma_request_ready_i(s_mc_dma_req_ready),
.dma_request_addr_o (s_mc_dma_req_addr),
.dma_request_bytes_o(s_mc_dma_req_bytes),
.dma_data_i (u_dma_read_axis.tdata),
.dma_keep_i (u_dma_read_axis.tkeep),
.dma_last_i (u_dma_read_axis.tlast),
.dma_valid_i (u_dma_read_axis.tvalid && !s_mc_idle),
.dma_ready_o (s_mc_dma_ready),
.dma_done_i (s_dma_done),
.dma_err_i (s_dma_err),
.dma_err_code_i (s_dma_err_code),
.dma_err_stage_i (s_dma_err_stage),
.dma_err_resp_i (s_dma_err_resp),
.dma_err_addr_i (s_dma_err_addr),
.store_active_o (s_store_active),
.store_read_o (s_store_read),
.store_write_o (s_store_write),
.store_addr_o (s_store_addr),
.store_data_o (s_store_write_data),
.store_data_i (s_store_read_data),
.store_valid_i (s_store_valid),
.stat_o (s_mc_status),
.abi_o (s_mc_abi),
.build_id_o (s_mc_build_id),
.lock_o (s_mc_lock),
.actual_crc_o (s_mc_actual_crc),
.load_count_o (s_mc_load_count),
.entry_pc_o (s_mc_entry_pc),
.entry_first_o (s_mc_entry_first),
.entry_last_o (s_mc_entry_last),
.entry_max_loop_o (s_mc_entry_max_loop),
.entry_max_retired_o(s_mc_entry_max_retired),
.load_done_o (s_mc_load_done),
.abort_done_o (s_mc_abort_done),
.fault_valid_o (s_mc_fault_valid),
.fault_code_o (s_mc_fault_code),
.fault_stage_o (s_mc_fault_stage),
.fault_resp_o (s_mc_fault_resp),
.fault_addr_o (s_mc_fault_addr),
.fault_detail_o (s_mc_fault_detail),
.idle_o (s_mc_idle)
);
apu_control_store u_control_store (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.loader_active_i(s_store_active),
.loader_read_i (s_store_read),
.loader_write_i (s_store_write),
.loader_addr_i (s_store_addr),
.loader_data_i (s_store_write_data),
.loader_data_o (s_store_read_data),
.loader_valid_o (s_store_valid),
.image_valid_i (s_mc_status[`APB4_APU__MC_STATUS_VALID]),
.fetch_i (s_seq_fetch),
.fetch_addr_i (s_seq_fetch_addr),
.fetch_data_o (s_seq_fetch_data),
.fetch_valid_o (s_seq_fetch_valid)
);
apu_codec_sequencer u_codec_sequencer (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.soft_reset_i (s_soft_reset),
.resource_reset_i (s_resource_reset_apply_q),
.counter_clear_i (s_cnt_clear),
.abort_i (s_abort),
.launch_i (1'b0),
.launch_entry_i (2'd0),
.image_valid_i (s_mc_status[`APB4_APU__MC_STATUS_VALID]),
.timeout_i (s_sequencer_timeout),
.entry_pc_i (s_mc_entry_pc),
.entry_first_i (s_mc_entry_first),
.entry_last_i (s_mc_entry_last),
.entry_max_loop_i (s_mc_entry_max_loop),
.entry_max_retired_i (s_mc_entry_max_retired),
.input_exhausted_i (1'b1),
.input_ready_i (1'b0),
.output_ready_i (1'b1),
.kernel_done_i (1'b0),
.transport_idle_success_i(!s_dma_busy && !s_dma_err),
.stall_i (1'b0),
.cause_valid_i (1'b0),
.cause_code_i (6'd0),
.cause_stage_i (4'd0),
.cause_resp_i (2'd0),
.cause_index_i (8'd0),
.cause_addr_i (32'd0),
.cause_detail_i (32'd0),
.fetch_o (s_seq_fetch),
.fetch_addr_o (s_seq_fetch_addr),
.fetch_data_i (s_seq_fetch_data),
.fetch_valid_i (s_seq_fetch_valid),
.stat_o (s_seq_status),
.retired_o (s_seq_retired),
.gpr_o (s_seq_gpr),
.trapped_o (s_seq_trapped),
.trap_event_o (s_seq_trap_event),
.abort_done_o (s_seq_abort_done),
.fault_valid_o (s_seq_fault_valid),
.fault_code_o (s_seq_fault_code),
.fault_stage_o (s_seq_fault_stage),
.fault_resp_o (s_seq_fault_resp),
.fault_index_o (s_seq_fault_index),
.fault_addr_o (s_seq_fault_addr),
.fault_detail_o (s_seq_fault_detail),
.perf_retired_o (s_seq_perf_retired),
.idle_o (s_seq_idle)
);
apu_ring_scheduler u_ring_scheduler (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
@@ -261,12 +460,12 @@ module apb4_apu (
.stop_on_error_i (s_ring_control[1]),
.coalesce_count_i (s_ring_coalesce[7:0]),
.coalesce_timeout_i (s_ring_coalesce[31:16]),
.dma_request_valid_o (s_dma_req_valid),
.dma_request_ready_i (s_dma_req_ready),
.dma_request_write_o (s_dma_req_write),
.dma_request_addr_o (s_dma_req_addr),
.dma_request_bytes_o (s_dma_req_bytes),
.dma_read_axis (u_dma_read_axis),
.dma_request_valid_o (s_ring_dma_req_valid),
.dma_request_ready_i (s_ring_dma_req_ready),
.dma_request_write_o (s_ring_dma_req_write),
.dma_request_addr_o (s_ring_dma_req_addr),
.dma_request_bytes_o (s_ring_dma_req_bytes),
.dma_read_axis (u_scheduler_read_axis),
.dma_write_axis (u_dma_write_axis),
.dma_done_i (s_dma_done),
.dma_error_i (s_dma_err),
@@ -355,7 +554,11 @@ module apb4_apu (
end
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i || s_soft_reset || s_resource_reset_apply_q || s_cnt_clear) begin
if (!rst_n_i) begin
s_active_cycles_q <= 64'd0;
s_stream_stalls_q <= 64'd0;
s_faults_q <= 64'd0;
end else if (s_soft_reset || s_resource_reset_apply_q || s_cnt_clear) begin
s_active_cycles_q <= 64'd0;
s_stream_stalls_q <= 64'd0;
s_faults_q <= 64'd0;
@@ -377,4 +580,6 @@ module apb4_apu (
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;
endmodule
+548
View File
@@ -0,0 +1,548 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`include "apu_define.svh"
module apu_codec_sequencer (
// verilog_format: off -- preserve launch, predicate, store, and result columns
input logic clk_i,
input logic rst_n_i,
input logic soft_reset_i,
input logic resource_reset_i,
input logic counter_clear_i,
input logic abort_i,
input logic launch_i,
input logic [1:0] launch_entry_i,
input logic image_valid_i,
input logic [31:0] timeout_i,
input logic [2:0][10:0] entry_pc_i,
input logic [2:0][10:0] entry_first_i,
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 input_exhausted_i,
input logic input_ready_i,
input logic output_ready_i,
input logic kernel_done_i,
input logic transport_idle_success_i,
input logic stall_i,
input logic cause_valid_i,
input logic [5:0] cause_code_i,
input logic [3:0] cause_stage_i,
input logic [1:0] cause_resp_i,
input logic [7:0] cause_index_i,
input logic [31:0] cause_addr_i,
input logic [31:0] cause_detail_i,
output logic fetch_o,
output logic [10:0] fetch_addr_o,
input logic [63:0] fetch_data_i,
input logic fetch_valid_i,
output logic [31:0] stat_o,
output logic [31:0] retired_o,
output logic [15:0][31:0] gpr_o,
output logic trapped_o,
output logic trap_event_o,
output logic abort_done_o,
output logic fault_valid_o,
output logic [5:0] fault_code_o,
output logic [3:0] fault_stage_o,
output logic [1:0] fault_resp_o,
output logic [7:0] fault_index_o,
output logic [31:0] fault_addr_o,
output logic [31:0] fault_detail_o,
output logic [63:0] perf_retired_o,
output logic idle_o
// verilog_format: on
);
import apu_microcode_pkg::*;
typedef enum logic [1:0] {
Idle,
FetchRequest,
FetchWait,
Execute
} sequencer_state_e;
sequencer_state_e s_state_q;
logic [15:0][31:0] s_gpr_q;
logic s_eq_q, s_signed_lt_q, s_unsigned_lt_q;
logic [3:0][10:0] s_return_pc_q;
logic [2:0] s_return_depth_q;
logic [3:0] s_loop_active_q;
logic [3:0][15:0] s_loop_count_q;
logic [3:0][10:0] s_loop_start_q;
logic [10:0] s_pc_q, s_first_pc_q, s_last_pc_q;
logic [15:0] s_max_loop_q;
logic [23:0] s_max_retired_q, s_frame_retired_q;
logic [31:0] s_watchdog_q;
logic [63:0] s_instruction_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 [31:0] s_immediate, s_source0, s_source1;
logic s_predicate_true;
logic [11:0] s_forward_target, s_loop_target;
logic [31:0] s_sat_result;
logic s_trap_now;
logic [ 7:0] s_trap_reason;
logic [31:0] s_trap_detail;
assign s_class = s_instruction_q[63:60];
assign s_opcode = s_instruction_q[59:56];
assign s_predicate = s_instruction_q[55:52];
assign s_dst = s_instruction_q[51:48];
assign s_src0 = s_instruction_q[47:44];
assign s_src1 = s_instruction_q[43:40];
assign s_aux = s_instruction_q[37:32];
assign s_immediate = s_instruction_q[31:0];
assign s_source0 = s_gpr_q[s_src0];
assign s_source1 = s_gpr_q[s_src1];
assign s_forward_target = {1'b0, s_pc_q} + 1'b1 + {1'b0, s_immediate[10:0]};
assign s_loop_target = {1'b0, s_pc_q} + 1'b1 - {1'b0, s_immediate[10:0]};
assign fetch_o = s_state_q == FetchRequest;
assign fetch_addr_o = s_pc_q;
assign stat_o = s_stat_q;
assign retired_o = {8'd0, s_frame_retired_q};
assign gpr_o = s_gpr_q;
assign trapped_o = s_trapped_q;
assign perf_retired_o = s_perf_retired_q;
assign idle_o = s_state_q == Idle;
always_comb begin
unique case (s_predicate)
4'd0: s_predicate_true = 1'b1;
4'd1: s_predicate_true = s_eq_q;
4'd2: s_predicate_true = !s_eq_q;
4'd3: s_predicate_true = s_signed_lt_q;
4'd4: s_predicate_true = !s_signed_lt_q;
4'd5: s_predicate_true = s_unsigned_lt_q;
4'd6: s_predicate_true = !s_unsigned_lt_q;
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'd11: s_predicate_true = transport_idle_success_i;
default: s_predicate_true = 1'b0;
endcase
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
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
s_trap_now = 1'b1;
s_trap_reason = 8'd6;
end else if (s_frame_retired_q >= s_max_retired_q) begin
s_trap_now = 1'b1;
s_trap_reason = 8'd8;
end else if (!s_predicate_true && ((s_pc_q == s_last_pc_q) || (s_pc_q == 11'd2047))) begin
s_trap_now = 1'b1;
s_trap_reason = 8'd2;
end else if (s_predicate_true && (s_class == 4'd0)) begin
unique case (s_opcode)
4'd0: begin
s_trap_now = (s_pc_q == s_last_pc_q) || (s_pc_q == 11'd2047);
s_trap_reason = 8'd2;
end
4'd1: begin
end
4'd2: begin
s_trap_now = 1'b1;
s_trap_reason = 8'd10;
s_trap_detail = s_immediate;
end
4'd3, 4'd4: begin
if (s_forward_target[11] || (s_forward_target[10:0] < s_first_pc_q) ||
(s_forward_target[10:0] > s_last_pc_q)) begin
s_trap_now = 1'b1;
s_trap_reason = 8'd2;
end else if ((s_opcode == 4'd4) && (s_return_depth_q == 3'd4)) begin
s_trap_now = 1'b1;
s_trap_reason = 8'd3;
end
end
4'd5: begin
s_trap_now = s_return_depth_q == 3'd0;
s_trap_reason = 8'd3;
end
4'd6: begin
s_trap_now = s_loop_active_q[s_aux[1:0]] || (s_source0[15:0] == 16'd0) ||
(s_source0[15:0] > s_max_loop_q);
s_trap_reason = 8'd4;
end
4'd7: begin
s_trap_now = s_loop_target[11] || !s_loop_active_q[s_aux[1:0]] ||
(s_loop_target[10:0] != s_loop_start_q[s_aux[1:0]]) ||
(s_loop_target[10:0] < s_first_pc_q);
s_trap_reason = 8'd4;
end
default: begin
s_trap_now = 1'b1;
s_trap_reason = 8'd1;
end
endcase
end else if (s_predicate_true && (s_class == 4'd1) &&
((s_pc_q == s_last_pc_q) || (s_pc_q == 11'd2047))) begin
s_trap_now = 1'b1;
s_trap_reason = 8'd2;
end
if (s_trap_now && (s_trap_reason != 8'd10)) begin
s_trap_detail = trap_detail(s_trap_reason, s_pc_q, s_instruction_q);
end
end
always_comb begin
logic signed [32:0] s_signed_value;
logic signed [32:0] s_signed_min;
logic signed [32:0] s_signed_max;
logic [ 4:0] s_width;
s_width = s_aux[4:0] == 5'd0 ? 5'd31 : s_aux[4:0];
s_signed_value = {s_source0[31], s_source0};
s_signed_min = -33'sh08000_0000;
s_signed_max = 33'sh07fff_ffff;
s_sat_result = s_source0;
if ((s_aux[4:0] != 5'd0) && s_aux[5]) begin
s_signed_value = {s_source0[31], s_source0};
s_signed_min = -(33'sd1 <<< (s_width - 1'b1));
s_signed_max = (33'sd1 <<< (s_width - 1'b1)) - 1'b1;
if (s_signed_value < s_signed_min) begin
s_sat_result = s_signed_min[31:0];
end else if (s_signed_value > s_signed_max) begin
s_sat_result = s_signed_max[31:0];
end
end else if ((s_aux[4:0] != 5'd0) && (s_source0 > ((32'd1 << s_aux[4:0]) - 1'b1))) begin
s_sat_result = (32'd1 << s_aux[4:0]) - 1'b1;
end
end
`define RETROSOC_APU__SET_TRAP(DETAIL) \
begin \
s_state_q <= Idle; \
s_trapped_q <= 1'b1; \
trap_event_o <= 1'b1; \
fault_valid_o <= 1'b1; \
fault_code_o <= `APB4_APU__ERROR_CODE_SEQUENCER; \
fault_stage_o <= `APB4_APU__ERROR_STAGE_LIFECYCLE; \
fault_resp_o <= 2'd0; \
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}; \
end
`define RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP \
begin \
s_state_q <= Idle; \
s_trapped_q <= 1'b1; \
trap_event_o <= 1'b1; \
fault_valid_o <= 1'b1; \
fault_code_o <= `APB4_APU__ERROR_CODE_SEQUENCER; \
fault_stage_o <= `APB4_APU__ERROR_STAGE_LIFECYCLE; \
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(8'd7, s_pc_q, 64'd0); \
s_stat_q <= {11'd0, |s_loop_active_q, 1'b0, 8'd0, s_pc_q}; \
end
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_state_q <= Idle;
s_gpr_q <= '0;
s_eq_q <= 1'b0;
s_signed_lt_q <= 1'b0;
s_unsigned_lt_q <= 1'b0;
s_return_pc_q <= '0;
s_return_depth_q <= 3'd0;
s_loop_active_q <= 4'd0;
s_loop_count_q <= '0;
s_loop_start_q <= '0;
s_pc_q <= 11'd0;
s_first_pc_q <= 11'd0;
s_last_pc_q <= 11'd0;
s_max_loop_q <= 16'd0;
s_max_retired_q <= 24'd0;
s_frame_retired_q <= 24'd0;
s_watchdog_q <= 32'd0;
s_instruction_q <= 64'd0;
s_stat_q <= 32'd0;
s_trapped_q <= 1'b0;
s_perf_retired_q <= 64'd0;
trap_event_o <= 1'b0;
abort_done_o <= 1'b0;
fault_valid_o <= 1'b0;
fault_code_o <= 6'd0;
fault_stage_o <= 4'd0;
fault_resp_o <= 2'd0;
fault_index_o <= 8'd0;
fault_addr_o <= 32'd0;
fault_detail_o <= 32'd0;
end else begin
trap_event_o <= 1'b0;
abort_done_o <= 1'b0;
fault_valid_o <= 1'b0;
if (counter_clear_i) s_perf_retired_q <= 64'd0;
if (soft_reset_i || resource_reset_i) begin
s_state_q <= Idle;
s_gpr_q <= '0;
s_eq_q <= 1'b0;
s_signed_lt_q <= 1'b0;
s_unsigned_lt_q <= 1'b0;
s_return_depth_q <= 3'd0;
s_loop_active_q <= 4'd0;
s_pc_q <= 11'd0;
s_frame_retired_q <= 24'd0;
s_watchdog_q <= 32'd0;
s_stat_q <= 32'd0;
s_trapped_q <= 1'b0;
s_perf_retired_q <= 64'd0;
end else begin
unique case (s_state_q)
Idle: begin
if (launch_i && image_valid_i && (launch_entry_i < 2'd3)) begin
s_state_q <= FetchRequest;
s_gpr_q <= '0;
s_eq_q <= 1'b0;
s_signed_lt_q <= 1'b0;
s_unsigned_lt_q <= 1'b0;
s_return_depth_q <= 3'd0;
s_loop_active_q <= 4'd0;
s_pc_q <= entry_pc_i[launch_entry_i];
s_first_pc_q <= entry_first_i[launch_entry_i];
s_last_pc_q <= entry_last_i[launch_entry_i];
s_max_loop_q <= entry_max_loop_i[launch_entry_i];
s_max_retired_q <= entry_max_retired_i[launch_entry_i];
s_frame_retired_q <= 24'd0;
s_watchdog_q <= 32'd0;
s_stat_q <= {21'd0, entry_pc_i[launch_entry_i]};
s_trapped_q <= 1'b0;
end
end
FetchRequest: begin
s_stat_q <= {21'd0, s_pc_q};
if (s_watchdog_q + 1'b1 >= timeout_i) begin
`RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP;
end else if (abort_i) begin
s_state_q <= Idle;
abort_done_o <= 1'b1;
end else begin
s_watchdog_q <= s_watchdog_q + 1'b1;
s_state_q <= FetchWait;
end
end
FetchWait: begin
if (s_watchdog_q + 1'b1 >= timeout_i) begin
`RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP;
end else if (abort_i) begin
s_state_q <= Idle;
abort_done_o <= 1'b1;
end else begin
s_watchdog_q <= s_watchdog_q + 1'b1;
if (fetch_valid_i) begin
s_instruction_q <= fetch_data_i;
s_stat_q <= {
11'd0, |s_loop_active_q, 1'b0, fetch_data_i[59:56], fetch_data_i[63:60], s_pc_q
};
s_state_q <= Execute;
end
end
end
Execute: begin
if (cause_valid_i) begin
s_state_q <= Idle;
s_trapped_q <= 1'b1;
trap_event_o <= 1'b1;
fault_valid_o <= 1'b1;
fault_code_o <= cause_code_i;
fault_stage_o <= cause_stage_i;
fault_resp_o <= cause_resp_i;
fault_index_o <= cause_index_i;
fault_addr_o <= cause_addr_i;
fault_detail_o <= cause_detail_i;
s_stat_q <= {11'd0, |s_loop_active_q, stall_i, s_opcode, s_class, s_pc_q};
end else if (s_trap_now) begin
`RETROSOC_APU__SET_TRAP(s_trap_detail);
end else if (abort_i) begin
s_state_q <= Idle;
abort_done_o <= 1'b1;
end else if (stall_i) begin
s_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));
end else begin
s_watchdog_q <= s_watchdog_q + 1'b1;
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
`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 begin
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_predicate_true) 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 else if (s_class == 4'd0) begin
unique case (s_opcode)
4'd0: 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
4'd1: s_state_q <= Idle;
4'd2: begin
`RETROSOC_APU__SET_TRAP(s_immediate);
s_frame_retired_q <= s_frame_retired_q;
s_perf_retired_q <= s_perf_retired_q;
end
4'd3: begin
if (s_forward_target[11] || (s_forward_target[10:0] < s_first_pc_q) ||
(s_forward_target[10:0] > s_last_pc_q)) 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_forward_target[10:0];
s_state_q <= FetchRequest;
end
end
4'd4: begin
if (s_return_depth_q == 3'd4) begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd3, 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_return_pc_q[s_return_depth_q] <= s_pc_q + 1'b1;
s_return_depth_q <= s_return_depth_q + 1'b1;
s_pc_q <= s_forward_target[10:0];
s_state_q <= FetchRequest;
end
end
4'd5: begin
if (s_return_depth_q == 3'd0) begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd3, 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_return_depth_q <= s_return_depth_q - 1'b1;
s_pc_q <= s_return_pc_q[s_return_depth_q-1'b1];
s_state_q <= FetchRequest;
end
end
4'd6: begin
if (s_loop_active_q[s_aux[1:0]] || (s_source0[15:0] == 16'd0) ||
(s_source0[15:0] > s_max_loop_q)) begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd4, 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_loop_active_q[s_aux[1:0]] <= 1'b1;
s_loop_count_q[s_aux[1:0]] <= s_source0[15:0];
s_loop_start_q[s_aux[1:0]] <= s_pc_q + 1'b1;
s_pc_q <= s_pc_q + 1'b1;
s_state_q <= FetchRequest;
end
end
4'd7: begin
if (s_loop_target[11] || !s_loop_active_q[s_aux[1:0]] ||
(s_loop_target[10:0] != s_loop_start_q[s_aux[1:0]]) ||
(s_loop_target[10:0] < s_first_pc_q)) begin
`RETROSOC_APU__SET_TRAP(trap_detail(8'd4, s_pc_q, s_instruction_q));
s_frame_retired_q <= s_frame_retired_q;
s_perf_retired_q <= s_perf_retired_q;
end else if (s_loop_count_q[s_aux[1:0]] > 16'd1) begin
s_loop_count_q[s_aux[1:0]] <= s_loop_count_q[s_aux[1:0]] - 1'b1;
s_pc_q <= s_loop_target[10:0];
s_state_q <= FetchRequest;
end else begin
s_loop_active_q[s_aux[1:0]] <= 1'b0;
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 begin
unique case (s_opcode)
4'd0: s_gpr_q[s_dst] <= s_source0;
4'd1: s_gpr_q[s_dst] <= s_immediate;
4'd2: s_gpr_q[s_dst] <= s_source0 + s_source1;
4'd3: s_gpr_q[s_dst] <= s_source0 - s_source1;
4'd4: s_gpr_q[s_dst] <= s_source0 & s_source1;
4'd5: s_gpr_q[s_dst] <= s_source0 | s_source1;
4'd6: s_gpr_q[s_dst] <= s_source0 ^ s_source1;
4'd7: s_gpr_q[s_dst] <= s_source0 << s_source1[4:0];
4'd8: s_gpr_q[s_dst] <= s_source0 >> s_source1[4:0];
4'd9: s_gpr_q[s_dst] <= $unsigned($signed(s_source0) >>> s_source1[4:0]);
4'd10: begin
s_eq_q <= s_source0 == s_source1;
s_signed_lt_q <= $signed(s_source0) < $signed(s_source1);
s_unsigned_lt_q <= s_source0 < s_source1;
end
4'd11:
s_gpr_q[s_dst] <= s_aux[0] ? (($signed(
s_source0
) < $signed(
s_source1
)) ? s_source0 : s_source1) : ((s_source0 < s_source1) ? s_source0 : s_source1);
4'd12:
s_gpr_q[s_dst] <= s_aux[0] ? (($signed(
s_source0
) > $signed(
s_source1
)) ? s_source0 : s_source1) : ((s_source0 > s_source1) ? s_source0 : s_source1);
4'd13: s_gpr_q[s_dst] <= s_sat_result;
default: begin
end
endcase
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
end
default: s_state_q <= Idle;
endcase
end
end
end
endmodule
`undef RETROSOC_APU__SET_TRAP
`undef RETROSOC_APU__SET_FETCH_WATCHDOG_TRAP
+113
View File
@@ -0,0 +1,113 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
module apu_control_store (
// verilog_format: off -- preserve loader and sequencer access columns
input logic clk_i,
input logic rst_n_i,
input logic loader_active_i,
input logic loader_read_i,
input logic loader_write_i,
input logic [10:0] loader_addr_i,
input logic [63:0] loader_data_i,
output logic [63:0] loader_data_o,
output logic loader_valid_o,
input logic image_valid_i,
input logic fetch_i,
input logic [10:0] fetch_addr_i,
output logic [63:0] fetch_data_o,
output logic fetch_valid_o
// verilog_format: on
);
logic s_access;
logic s_write;
logic [10:0] s_addr;
logic [63:0] s_read_data;
logic s_loader_read_q, s_fetch_q;
assign s_access = loader_active_i ? (loader_read_i || loader_write_i) :
(fetch_i && image_valid_i);
assign s_write = loader_active_i && loader_write_i;
assign s_addr = loader_active_i ? loader_addr_i : fetch_addr_i;
assign loader_data_o = s_read_data;
assign loader_valid_o = s_loader_read_q;
assign fetch_data_o = s_read_data;
assign fetch_valid_o = s_fetch_q;
`ifdef HAVE_SRAM_MACRO
logic s_read_bank_q;
logic [1:0][31:0] s_low_data;
logic [1:0][31:0] s_high_data;
tc_sram_1024x32 u_control_low_bank0 (
.clk_i (clk_i),
.cs_i (s_access && (s_addr[10] == 1'b0)),
.addr_i(s_addr[9:0]),
.data_i(loader_data_i[31:0]),
.mask_i(4'hf),
.wren_i(s_write),
.data_o(s_low_data[0])
);
tc_sram_1024x32 u_control_high_bank0 (
.clk_i (clk_i),
.cs_i (s_access && (s_addr[10] == 1'b0)),
.addr_i(s_addr[9:0]),
.data_i(loader_data_i[63:32]),
.mask_i(4'hf),
.wren_i(s_write),
.data_o(s_high_data[0])
);
tc_sram_1024x32 u_control_low_bank1 (
.clk_i (clk_i),
.cs_i (s_access && (s_addr[10] == 1'b1)),
.addr_i(s_addr[9:0]),
.data_i(loader_data_i[31:0]),
.mask_i(4'hf),
.wren_i(s_write),
.data_o(s_low_data[1])
);
tc_sram_1024x32 u_control_high_bank1 (
.clk_i (clk_i),
.cs_i (s_access && (s_addr[10] == 1'b1)),
.addr_i(s_addr[9:0]),
.data_i(loader_data_i[63:32]),
.mask_i(4'hf),
.wren_i(s_write),
.data_o(s_high_data[1])
);
assign s_read_data = {s_high_data[s_read_bank_q], s_low_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 <= 1'b0;
end else if (s_access && !s_write) begin
s_read_bank_q <= s_addr[10];
end
end
`else
logic [63:0] mem [0:2047];
logic [63:0] s_read_data_q;
assign s_read_data = s_read_data_q;
always_ff @(posedge clk_i) begin
if (s_access) begin
if (s_write) begin
mem[s_addr] <= loader_data_i;
end else begin
s_read_data_q <= mem[s_addr];
end
end
end
`endif
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_loader_read_q <= 1'b0;
s_fetch_q <= 1'b0;
end else begin
s_loader_read_q <= loader_active_i && loader_read_i;
s_fetch_q <= !loader_active_i && fetch_i && image_valid_i;
end
end
endmodule
+174
View File
@@ -282,6 +282,180 @@
`define APB4_APU__PERF_CONTROL_CLEAR 1
`define APB4_APU__PERF_CONTROL_SNAPSHOT 2
`define APB4_APU__PERF_STATUS_SNAPSHOT_VALID 0
`define APB4_APU__MC_STATUS_BUSY 0
`define APB4_APU__MC_STATUS_VALID 1
`define APB4_APU__MC_STATUS_HEADER_ERROR 2
`define APB4_APU__MC_STATUS_RANGE_ERROR 3
`define APB4_APU__MC_STATUS_CONTROL_ERROR 4
`define APB4_APU__MC_STATUS_TABLE_ERROR 5
`define APB4_APU__MC_STATUS_CRC_ERROR 6
`define APB4_APU__MC_STATUS_CAPABILITY_ERROR 7
`define APB4_APU__MC_LOCK_LOCKED 0
`define APB4_APU__SEQUENCER_STATUS_PC 0
`define APB4_APU__SEQUENCER_STATUS_CLASS 11
`define APB4_APU__SEQUENCER_STATUS_OPCODE 15
`define APB4_APU__SEQUENCER_STATUS_WAIT 19
`define APB4_APU__SEQUENCER_STATUS_LOOP 20
`define APB4_APU__APUMC_MAGIC 32'h4150_4d43
`define APB4_APU__APUMC_ABI 32'h0001_0000
`define APB4_APU__APUMC_HEADER_BYTES 64
`define APB4_APU__APUMC_HEADER_WORDS 16
`define APB4_APU__APUMC_ENTRY_BYTES 32
`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_HEADER_MAGIC 0
`define APB4_APU__APUMC_HEADER_ABI 1
`define APB4_APU__APUMC_HEADER_TOTAL_BYTES 2
`define APB4_APU__APUMC_HEADER_INSTRUCTIONS 3
`define APB4_APU__APUMC_HEADER_INSTR_OFFSET 4
`define APB4_APU__APUMC_HEADER_TABLE_OFFSET 5
`define APB4_APU__APUMC_HEADER_TABLE_BYTES 6
`define APB4_APU__APUMC_HEADER_ENTRY_OFFSET 7
`define APB4_APU__APUMC_HEADER_ENTRY_COUNT 8
`define APB4_APU__APUMC_HEADER_PRIMITIVES 9
`define APB4_APU__APUMC_HEADER_SCRATCH_END 10
`define APB4_APU__APUMC_HEADER_CRC 11
`define APB4_APU__APUMC_HEADER_BUILD_LO 12
`define APB4_APU__APUMC_HEADER_BUILD_HI 13
`define APB4_APU__APUMC_ENTRY_FORMAT_PC 0
`define APB4_APU__APUMC_ENTRY_RANGE 1
`define APB4_APU__APUMC_ENTRY_SCRATCH_BASE 2
`define APB4_APU__APUMC_ENTRY_SCRATCH_BYTES 3
`define APB4_APU__APUMC_ENTRY_MAX_LOOP 4
`define APB4_APU__APUMC_ENTRY_MAX_RETIRED 5
`define APB4_APU__APUMC_ENTRY_PRIMITIVES 6
`define APB4_APU__APUMC_ENTRY_TABLE 7
`define APB4_APU__MC_INSTRUCTION_IMMEDIATE 0
`define APB4_APU__MC_INSTRUCTION_AUX 32
`define APB4_APU__MC_INSTRUCTION_SRC1 40
`define APB4_APU__MC_INSTRUCTION_SRC0 44
`define APB4_APU__MC_INSTRUCTION_DST 48
`define APB4_APU__MC_INSTRUCTION_PREDICATE 52
`define APB4_APU__MC_INSTRUCTION_OPCODE 56
`define APB4_APU__MC_INSTRUCTION_CLASS 60
`define APB4_APU__MC_CLASS_CONTROL 0
`define APB4_APU__MC_CLASS_SCALAR 1
`define APB4_APU__MC_CLASS_BITSTREAM 2
`define APB4_APU__MC_CLASS_ENTROPY 3
`define APB4_APU__MC_CLASS_LOCAL 4
`define APB4_APU__MC_CLASS_KERNEL 5
`define APB4_APU__MC_CLASS_TRANSPORT 6
`define APB4_APU__MC_CONTROL_NOP 0
`define APB4_APU__MC_CONTROL_END 1
`define APB4_APU__MC_CONTROL_TRAP 2
`define APB4_APU__MC_CONTROL_JUMP_FWD 3
`define APB4_APU__MC_CONTROL_CALL_FWD 4
`define APB4_APU__MC_CONTROL_RET 5
`define APB4_APU__MC_CONTROL_LOOP_SETUP 6
`define APB4_APU__MC_CONTROL_LOOP_BACK 7
`define APB4_APU__MC_CONTROL_WAIT 8
`define APB4_APU__MC_SCALAR_MOV 0
`define APB4_APU__MC_SCALAR_MOVI 1
`define APB4_APU__MC_SCALAR_ADD 2
`define APB4_APU__MC_SCALAR_SUB 3
`define APB4_APU__MC_SCALAR_AND 4
`define APB4_APU__MC_SCALAR_OR 5
`define APB4_APU__MC_SCALAR_XOR 6
`define APB4_APU__MC_SCALAR_SHL 7
`define APB4_APU__MC_SCALAR_SHR 8
`define APB4_APU__MC_SCALAR_SAR 9
`define APB4_APU__MC_SCALAR_CMP 10
`define APB4_APU__MC_SCALAR_MIN 11
`define APB4_APU__MC_SCALAR_MAX 12
`define APB4_APU__MC_SCALAR_SAT 13
`define APB4_APU__MC_BITSTREAM_REFILL 0
`define APB4_APU__MC_BITSTREAM_PEEK 1
`define APB4_APU__MC_BITSTREAM_GET 2
`define APB4_APU__MC_BITSTREAM_SKIP 3
`define APB4_APU__MC_BITSTREAM_ALIGN 4
`define APB4_APU__MC_BITSTREAM_FRAME_SYNC 5
`define APB4_APU__MC_BITSTREAM_CRC8 6
`define APB4_APU__MC_BITSTREAM_CRC16 7
`define APB4_APU__MC_ENTROPY_HUFF_SYMBOL 0
`define APB4_APU__MC_ENTROPY_HUFF_PAIR 1
`define APB4_APU__MC_ENTROPY_HUFF_QUAD 2
`define APB4_APU__MC_ENTROPY_UNARY 3
`define APB4_APU__MC_ENTROPY_RICE4 4
`define APB4_APU__MC_ENTROPY_RICE5 5
`define APB4_APU__MC_ENTROPY_SIGN_RESTORE 6
`define APB4_APU__MC_LOCAL_LD32 0
`define APB4_APU__MC_LOCAL_ST32 1
`define APB4_APU__MC_LOCAL_TABLE8 2
`define APB4_APU__MC_LOCAL_TABLE16 3
`define APB4_APU__MC_LOCAL_TABLE32 4
`define APB4_APU__MC_LOCAL_FIFO_POP 5
`define APB4_APU__MC_LOCAL_FIFO_PUSH 6
`define APB4_APU__MC_KERNEL_REQUANT 0
`define APB4_APU__MC_KERNEL_STEREO 1
`define APB4_APU__MC_KERNEL_IMDCT6 2
`define APB4_APU__MC_KERNEL_IMDCT18 3
`define APB4_APU__MC_KERNEL_DCT32_POLY 4
`define APB4_APU__MC_KERNEL_FIXED 5
`define APB4_APU__MC_KERNEL_LPC 6
`define APB4_APU__MC_KERNEL_DECORRELATE 7
`define APB4_APU__MC_KERNEL_RESAMPLE 8
`define APB4_APU__MC_KERNEL_PCM_PACK 9
`define APB4_APU__MC_TRANSPORT_INPUT_REFILL 0
`define APB4_APU__MC_TRANSPORT_OUTPUT_COMMIT 1
`define APB4_APU__MC_TRANSPORT_OUTPUT_STREAM 2
`define APB4_APU__MC_TRANSPORT_DMA_WAIT 3
`define APB4_APU__MC_TRANSPORT_FRAME_COMMIT 4
`define APB4_APU__MC_TRANSPORT_JOB_RESULT 5
`define APB4_APU__MC_TRANSPORT_EVENT 6
`define APB4_APU__MC_PRED_ALWAYS 0
`define APB4_APU__MC_PRED_EQ 1
`define APB4_APU__MC_PRED_NE 2
`define APB4_APU__MC_PRED_SIGNED_LT 3
`define APB4_APU__MC_PRED_SIGNED_GE 4
`define APB4_APU__MC_PRED_UNSIGNED_LT 5
`define APB4_APU__MC_PRED_UNSIGNED_GE 6
`define APB4_APU__MC_PRED_INPUT_EXHAUSTED 7
`define APB4_APU__MC_PRED_INPUT_READY 8
`define APB4_APU__MC_PRED_OUTPUT_READY 9
`define APB4_APU__MC_PRED_KERNEL_DONE 10
`define APB4_APU__MC_PRED_TRANSPORT_DONE 11
`define APB4_APU__MC_WAIT_DMA 0
`define APB4_APU__MC_WAIT_KERNEL 1
`define APB4_APU__MC_WAIT_INPUT_FIFO 2
`define APB4_APU__MC_WAIT_OUTPUT_FIFO 3
`define APB4_APU__MC_WAIT_TX_STREAM 4
`define APB4_APU__MC_WAIT_RING_WRITEBACK 5
`define APB4_APU__MC_PRIMITIVE_BITSTREAM 0
`define APB4_APU__MC_PRIMITIVE_CRC 1
`define APB4_APU__MC_PRIMITIVE_HUFFMAN 2
`define APB4_APU__MC_PRIMITIVE_RICE 3
`define APB4_APU__MC_PRIMITIVE_LOCAL_MEMORY 4
`define APB4_APU__MC_PRIMITIVE_LOCAL_FIFO 5
`define APB4_APU__MC_PRIMITIVE_REQUANTIZE 6
`define APB4_APU__MC_PRIMITIVE_STEREO 7
`define APB4_APU__MC_PRIMITIVE_IMDCT6 8
`define APB4_APU__MC_PRIMITIVE_IMDCT18 9
`define APB4_APU__MC_PRIMITIVE_DCT32_POLY 10
`define APB4_APU__MC_PRIMITIVE_FIXED 11
`define APB4_APU__MC_PRIMITIVE_LPC 12
`define APB4_APU__MC_PRIMITIVE_DECORRELATE 13
`define APB4_APU__MC_PRIMITIVE_RESAMPLER 14
`define APB4_APU__MC_PRIMITIVE_PCM_PACK 15
`define APB4_APU__MC_PRIMITIVE_DMA_INPUT 16
`define APB4_APU__MC_PRIMITIVE_DMA_OUTPUT 17
`define APB4_APU__MC_PRIMITIVE_STREAM_OUTPUT 18
`define APB4_APU__MC_PRIMITIVE_FRAME_COMMIT 19
`define APB4_APU__MC_PRIMITIVE_EVENT 20
`define APB4_APU__MC_TRAP_ILLEGAL 1
`define APB4_APU__MC_TRAP_PC_RANGE 2
`define APB4_APU__MC_TRAP_CALL_STACK 3
`define APB4_APU__MC_TRAP_LOOP 4
`define APB4_APU__MC_TRAP_LOCAL_RANGE 5
`define APB4_APU__MC_TRAP_UNAVAILABLE 6
`define APB4_APU__MC_TRAP_WATCHDOG 7
`define APB4_APU__MC_TRAP_RETIRED_BUDGET 8
`define APB4_APU__MC_TRAP_ENGINE 9
`define APB4_APU__MC_TRAP_EXPLICIT 10
// verilog_format: on
`endif
File diff suppressed because it is too large. Load diff
+182
View File
@@ -0,0 +1,182 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`include "apu_define.svh"
package apu_microcode_pkg;
typedef struct packed {
logic [10:0] entry_pc;
logic [10:0] first_pc;
logic [10:0] last_pc;
logic [15:0] max_loop_count;
logic [23:0] max_retired;
} apu_mc_entry_t;
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;
logic [ 7:0] s_byte;
begin
s_crc = crc_i;
for (int unsigned byte_index = 0; byte_index < 4; byte_index++) begin
if (keep_i[byte_index]) begin
s_byte = data_i[(byte_index*8)+:8];
s_crc = s_crc ^ {24'd0, s_byte};
for (int unsigned bit_index = 0; bit_index < 8; bit_index++) begin
s_crc = s_crc[0] ? ((s_crc >> 1) ^ 32'hedb8_8320) : (s_crc >> 1);
end
end
end
return s_crc;
end
endfunction
function automatic logic [31:0] trap_detail(input logic [7:0] reason_i, input logic [10:0] pc_i,
input logic [63:0] instruction_i);
logic s_unused;
begin
s_unused = ^{instruction_i[55:37], instruction_i[31:0]};
return {instruction_i[36:32], instruction_i[59:56], instruction_i[63:60], pc_i, reason_i} |
{32{s_unused && 1'b0}};
end
endfunction
function automatic logic instruction_encoding_valid(input logic [63:0] instruction_i);
logic [ 3:0] s_class;
logic [ 3:0] s_opcode;
logic [ 3:0] s_predicate;
logic [ 3:0] s_dst;
logic [ 3:0] s_src0;
logic [ 3:0] s_src1;
logic [ 7:0] s_aux;
logic [31:0] s_immediate;
logic s_valid;
begin
s_class = instruction_i[63:60];
s_opcode = instruction_i[59:56];
s_predicate = instruction_i[55:52];
s_dst = instruction_i[51:48];
s_src0 = instruction_i[47:44];
s_src1 = instruction_i[43:40];
s_aux = instruction_i[39:32];
s_immediate = instruction_i[31:0];
s_valid = s_predicate <= `APB4_APU__MC_PRED_TRANSPORT_DONE;
if (s_class == `APB4_APU__MC_CLASS_CONTROL) begin
unique case (s_opcode)
`APB4_APU__MC_CONTROL_NOP, `APB4_APU__MC_CONTROL_END:
s_valid = s_valid && ({s_dst, s_src0, s_src1, s_aux, s_immediate} == 52'd0);
`APB4_APU__MC_CONTROL_TRAP:
s_valid = s_valid && ({s_dst, s_src0, s_src1, s_aux} == 20'd0);
`APB4_APU__MC_CONTROL_JUMP_FWD, `APB4_APU__MC_CONTROL_CALL_FWD:
s_valid = s_valid && ({s_dst, s_src0, s_src1, s_aux} == 20'd0) &&
(s_immediate[31:11] == 21'd0) && (s_immediate[10:0] != 11'd0);
`APB4_APU__MC_CONTROL_RET:
s_valid = s_valid && ({s_dst, s_src0, s_src1, s_aux, s_immediate} == 52'd0) &&
(s_predicate == `APB4_APU__MC_PRED_ALWAYS);
`APB4_APU__MC_CONTROL_LOOP_SETUP:
s_valid = s_valid && ({s_dst, s_src1, s_immediate} == 40'd0) &&
(s_aux[7:2] == 6'd0) && (s_predicate == `APB4_APU__MC_PRED_ALWAYS);
`APB4_APU__MC_CONTROL_LOOP_BACK:
s_valid = s_valid && ({s_dst, s_src0, s_src1} == 12'd0) &&
(s_aux[7:2] == 6'd0) && (s_immediate[31:11] == 21'd0) &&
(s_immediate[10:0] != 11'd0) &&
(s_predicate == `APB4_APU__MC_PRED_ALWAYS);
`APB4_APU__MC_CONTROL_WAIT:
s_valid = s_valid && ({s_dst, s_src0, s_src1, s_immediate} == 44'd0) &&
(s_aux <= 8'd5) && (s_predicate == `APB4_APU__MC_PRED_ALWAYS);
default: s_valid = 1'b0;
endcase
end else if (s_class == `APB4_APU__MC_CLASS_SCALAR) begin
unique case (s_opcode)
`APB4_APU__MC_SCALAR_MOV: s_valid = s_valid && ({s_src1, s_aux, s_immediate} == 44'd0);
`APB4_APU__MC_SCALAR_MOVI: s_valid = s_valid && ({s_src0, s_src1, s_aux} == 16'd0);
`APB4_APU__MC_SCALAR_ADD, `APB4_APU__MC_SCALAR_SUB,
`APB4_APU__MC_SCALAR_AND, `APB4_APU__MC_SCALAR_OR,
`APB4_APU__MC_SCALAR_XOR, `APB4_APU__MC_SCALAR_SHL,
`APB4_APU__MC_SCALAR_SHR, `APB4_APU__MC_SCALAR_SAR:
s_valid = s_valid && ({s_aux, s_immediate} == 40'd0);
`APB4_APU__MC_SCALAR_CMP: s_valid = s_valid && ({s_dst, s_aux, s_immediate} == 44'd0);
`APB4_APU__MC_SCALAR_MIN, `APB4_APU__MC_SCALAR_MAX:
s_valid = s_valid && (s_aux[7:1] == 7'd0) && (s_immediate == 32'd0);
`APB4_APU__MC_SCALAR_SAT:
s_valid = s_valid && (s_src1 == 4'd0) && (s_aux[7:6] == 2'd0) &&
((s_aux[4:0] == 5'd0) || (s_aux[4:0] == 5'd8) ||
(s_aux[4:0] == 5'd16) || (s_aux[4:0] == 5'd24)) &&
(s_immediate == 32'd0);
default: s_valid = 1'b0;
endcase
end else if (s_class == 4'd2) begin
unique case (s_opcode)
4'd0, 4'd1, 4'd2:
s_valid = s_valid && ({s_src0, s_src1, s_aux} == 16'd0) &&
(s_immediate[31:6] == 26'd0) && (s_immediate[5:0] != 6'd0);
4'd3:
s_valid = s_valid && ({s_dst, s_src0, s_src1, s_aux} == 20'd0) &&
(s_immediate[31:6] == 26'd0) && (s_immediate[5:0] != 6'd0);
4'd4: s_valid = s_valid && ({s_dst, s_src0, s_src1, s_aux, s_immediate} == 52'd0);
4'd5:
s_valid = s_valid && (s_aux[7:5] == 3'd0) && (s_aux[4:0] >= 5'd8) &&
(s_aux[4:0] <= 5'd16) && (s_immediate[31:16] == 16'd0) &&
(s_immediate[15:0] != 16'd0);
4'd6, 4'd7: s_valid = s_valid && ({s_aux, s_immediate} == 40'd0);
default: s_valid = 1'b0;
endcase
end else if (s_class == 4'd3) begin
unique case (s_opcode)
4'd0, 4'd1, 4'd2:
s_valid = s_valid && (s_aux[7:5] == 3'd0) && (s_aux[4:0] != 5'd0) &&
(s_aux[4:0] <= 5'd24) && (s_immediate == 32'd0) &&
((s_opcode != 4'd1) || (s_dst <= 4'd14)) &&
((s_opcode != 4'd2) || (s_dst <= 4'd12));
4'd3:
s_valid = s_valid && ({s_src0, s_src1} == 8'd0) && (s_aux[7:1] == 7'd0) &&
(s_immediate[31:16] == 16'd0) && (s_immediate[15:0] != 16'd0);
4'd4, 4'd5, 4'd6: s_valid = s_valid && ({s_aux, s_immediate} == 40'd0);
default: s_valid = 1'b0;
endcase
end else if (s_class == 4'd4) begin
unique case (s_opcode)
4'd0: s_valid = s_valid && ({s_src1, s_aux} == 12'd0) && (s_immediate[31:16] == 16'd0);
4'd1: s_valid = s_valid && ({s_dst, s_aux} == 12'd0) && (s_immediate[31:16] == 16'd0);
4'd2, 4'd3, 4'd4: s_valid = s_valid && ({s_aux, s_immediate} == 40'd0);
4'd5:
s_valid = s_valid && (s_dst <= 4'd14) && ({s_src0, s_src1, s_aux, s_immediate} == 48'd0);
4'd6: s_valid = s_valid && ({s_dst, s_aux, s_immediate} == 44'd0);
default: s_valid = 1'b0;
endcase
end else if (s_class == 4'd5) begin
s_valid = s_valid && (s_opcode <= 4'd9) && (s_immediate[31:16] == 16'd0) &&
(s_immediate[15:0] != 16'd0);
unique case (s_opcode)
4'd0: s_valid = s_valid && (s_aux[7:6] != 2'd3);
4'd1, 4'd7: s_valid = s_valid && (s_aux[7:2] == 6'd0);
4'd2, 4'd3, 4'd4: s_valid = s_valid && (s_aux == 8'd0);
4'd5: s_valid = s_valid && (s_aux[7:3] == 5'd0) && (s_aux[2:0] <= 3'd4);
4'd6:
s_valid = s_valid && (s_aux[7:6] == 2'd0) && (s_aux[5:0] != 6'd0) &&
(s_aux[5:0] <= 6'd32);
4'd8: s_valid = s_valid && (s_aux[7:4] == 4'd0);
4'd9: s_valid = s_valid && (s_aux[7:3] == 5'd0) && (s_aux[1:0] <= 2'd1);
default: s_valid = 1'b0;
endcase
end else if (s_class == 4'd6) begin
unique case (s_opcode)
4'd0, 4'd1, 4'd2: s_valid = s_valid && ({s_aux, s_immediate} == 40'd0);
4'd3: s_valid = s_valid && ({s_src0, s_src1, s_aux, s_immediate} == 48'd0);
4'd4: s_valid = s_valid && ({s_dst, s_aux, s_immediate} == 44'd0);
4'd5:
s_valid = s_valid && (s_dst == 4'd0) && (s_src0[3:0] == s_src0) &&
(s_aux[7:4] == 4'd0) && (s_immediate == 32'd0);
4'd6:
s_valid = s_valid && ({s_dst, s_src0, s_src1, s_aux} == 20'd0) &&
(s_immediate[31:11] == 21'd0) && (s_immediate[10:0] != 11'd0) &&
((s_immediate[10:0] & ~11'h0c0) == 11'd0);
default: s_valid = 1'b0;
endcase
end else begin
s_valid = 1'b0;
end
return s_valid;
end
endfunction
endpackage
+74 -20
View File
@@ -26,6 +26,15 @@ module apu_reg (
input logic [31:0] ring_status_i,
input logic [7:0] ring_head_i,
input logic [31:0] ring_completed_i,
input logic [7:0] mc_status_i,
input logic [31:0] mc_abi_i,
input logic [63:0] mc_build_id_i,
input logic mc_lock_i,
input logic [31:0] mc_actual_crc_i,
input logic [31:0] mc_load_count_i,
input logic [31:0] sequencer_status_i,
input logic [31:0] sequencer_retired_i,
input logic sequencer_trapped_i,
input logic [10:0] irq_set_i,
input logic fault_valid_i,
input logic [5:0] fault_code_i,
@@ -40,10 +49,12 @@ module apu_reg (
input logic [63:0] perf_dma_read_stalls_i,
input logic [63:0] perf_dma_write_stalls_i,
input logic [63:0] perf_stream_stalls_i,
input logic [63:0] perf_sequencer_instr_i,
input logic [63:0] perf_faults_i,
apb4_if.slave apb4,
output logic soft_reset_o,
output logic abort_o,
output logic microcode_load_o,
output logic counter_clear_o,
output logic perf_enable_o,
output logic xrun_clear_o,
@@ -54,6 +65,10 @@ module apu_reg (
output logic [31:0] write_base_o,
output logic [31:0] write_limit_o,
output logic [31:0] dma_timeout_o,
output logic [31:0] sequencer_timeout_o,
output logic [31:0] mc_image_addr_o,
output logic [31:0] mc_image_size_o,
output logic [31:0] mc_expected_crc_o,
output logic [31:0] ring_base_o,
output logic [8:0] ring_size_o,
output logic [7:0] ring_tail_o,
@@ -65,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_0018;
localparam logic [31:0] Capability1 = 32'd0;
localparam logic [31:0] Capability0 = 32'h0000_0098;
localparam logic [31:0] Capability1 = 32'h0000_0010;
localparam logic [31:0] AbiDigest = 32'd0;
localparam logic [10:0] IrqMask = 11'h7ff;
localparam logic [31:0] TimeoutReset = 32'h0000_ffff;
@@ -89,6 +104,7 @@ module apu_reg (
logic [31:0] s_read_data;
logic s_soft_reset;
logic s_abort;
logic s_microcode_load;
logic s_cnt_clear;
logic [10:0] s_irq_clear;
logic [10:0] s_irq_set;
@@ -109,9 +125,10 @@ module apu_reg (
logic [15:0] s_stream_watermark_d, s_stream_watermark_q;
logic s_perf_en_d, s_perf_en_q;
logic s_perf_snapshot_valid_d, s_perf_snapshot_valid_q;
logic [9:0][63:0] s_perf_snapshot_q;
logic s_err_clear;
logic s_unused_snapshot;
logic [ 9:0][63:0] s_perf_snapshot_q;
logic s_err_clear;
logic s_unused_snapshot;
logic [32:0] s_mc_image_end;
function automatic logic [31:0] apply_wstrb(
input logic [31:0] previous_i, input logic [31:0] value_i, input logic [3:0] strobe_i);
@@ -144,6 +161,7 @@ module apu_reg (
assign irq_o = (s_irq_state_q & s_irq_en_q) != 11'd0;
assign soft_reset_o = s_soft_reset;
assign abort_o = s_abort;
assign microcode_load_o = s_microcode_load;
assign counter_clear_o = s_cnt_clear;
assign perf_enable_o = s_perf_en_q;
assign xrun_clear_o = s_irq_clear[`APB4_APU__IRQ_STREAM_XRUN];
@@ -154,6 +172,10 @@ module apu_reg (
assign write_base_o = s_acl_q[2];
assign write_limit_o = s_acl_q[3];
assign dma_timeout_o = s_timeout_q[1];
assign sequencer_timeout_o = s_timeout_q[0];
assign mc_image_addr_o = s_mc_cfg_q[0];
assign mc_image_size_o = s_mc_cfg_q[1];
assign mc_expected_crc_o = s_mc_cfg_q[2];
assign ring_base_o = s_ring_cfg_q[0];
assign ring_size_o = s_ring_cfg_q[1][8:0];
assign ring_tail_o = s_ring_cfg_q[2][7:0];
@@ -162,6 +184,7 @@ module apu_reg (
assign s_err_clear = s_write && !s_write_err &&
(s_offset == `APB4_APU__ERROR_STATUS) && s_strobed_write[0];
assign s_unused_snapshot = ^s_perf_snapshot_q[3] ^ ^s_perf_snapshot_q[7] ^ ^s_perf_snapshot_q[8];
assign s_mc_image_end = {1'b0, s_mc_cfg_q[0]} + {1'b0, s_mc_cfg_q[1]} - 1'b1;
always_comb begin
s_read_data = 32'd0;
@@ -175,14 +198,15 @@ module apu_reg (
`APB4_APU__STATUS:
s_read_data = {
22'd0,
1'b0,
sequencer_trapped_i,
!core_busy_i,
core_aborting_i,
quiesce_i,
2'd0,
ring_status_i[`APB4_APU__RING_STATUS_ACTIVE],
core_busy_i,
2'd0
1'b0,
mc_status_i[`APB4_APU__MC_STATUS_VALID]
};
`APB4_APU__IRQ_STATE: s_read_data = {21'd0, s_irq_state_q};
`APB4_APU__IRQ_ENABLE: s_read_data = {21'd0, s_irq_en_q};
@@ -200,10 +224,15 @@ module apu_reg (
`APB4_APU__WRITE_LIMIT: s_read_data = s_acl_q[3];
`APB4_APU__DMA_TIMEOUT: s_read_data = s_timeout_q[1];
`APB4_APU__ABI_DIGEST: s_read_data = AbiDigest;
`APB4_APU__SEQUENCER_STATUS, `APB4_APU__SEQUENCER_RETIRED,
`APB4_APU__MC_STATUS, `APB4_APU__MC_ABI, `APB4_APU__MC_BUILD_ID_LO,
`APB4_APU__MC_BUILD_ID_HI, `APB4_APU__MC_LOCK, `APB4_APU__MC_ACTUAL_CRC,
`APB4_APU__MC_LOAD_COUNT,
`APB4_APU__SEQUENCER_STATUS: s_read_data = sequencer_status_i;
`APB4_APU__SEQUENCER_RETIRED: s_read_data = sequencer_retired_i;
`APB4_APU__MC_STATUS: s_read_data = {24'd0, mc_status_i};
`APB4_APU__MC_ABI: s_read_data = mc_abi_i;
`APB4_APU__MC_BUILD_ID_LO: s_read_data = mc_build_id_i[31:0];
`APB4_APU__MC_BUILD_ID_HI: s_read_data = mc_build_id_i[63:32];
`APB4_APU__MC_LOCK: s_read_data = {31'd0, mc_lock_i};
`APB4_APU__MC_ACTUAL_CRC: s_read_data = mc_actual_crc_i;
`APB4_APU__MC_LOAD_COUNT: s_read_data = mc_load_count_i;
`APB4_APU__KWS_STATUS, `APB4_APU__KWS_RESULT, `APB4_APU__KWS_TIMESTAMP_LO,
`APB4_APU__KWS_TIMESTAMP_HI, `APB4_APU__KWS_FRAME_COUNT,
`APB4_APU__KWS_INFERENCE_COUNT, `APB4_APU__KWS_HIT_COUNT,
@@ -257,9 +286,9 @@ module apu_reg (
`APB4_APU__PERF_DMA_WRITE_STALLS_HI: s_read_data = s_perf_snapshot_q[5][63:32];
`APB4_APU__PERF_STREAM_STALLS_LO: s_read_data = s_perf_snapshot_q[6][31:0];
`APB4_APU__PERF_STREAM_STALLS_HI: s_read_data = s_perf_snapshot_q[6][63:32];
`APB4_APU__PERF_SEQUENCER_INSTR_LO, `APB4_APU__PERF_SEQUENCER_INSTR_HI,
`APB4_APU__PERF_KWS_CYCLES_LO, `APB4_APU__PERF_KWS_CYCLES_HI:
s_read_data = 32'd0;
`APB4_APU__PERF_SEQUENCER_INSTR_LO: s_read_data = s_perf_snapshot_q[7][31:0];
`APB4_APU__PERF_SEQUENCER_INSTR_HI: s_read_data = s_perf_snapshot_q[7][63:32];
`APB4_APU__PERF_KWS_CYCLES_LO, `APB4_APU__PERF_KWS_CYCLES_HI: s_read_data = 32'd0;
`APB4_APU__PERF_FAULTS_LO: s_read_data = s_perf_snapshot_q[9][31:0];
`APB4_APU__PERF_FAULTS_HI: s_read_data = s_perf_snapshot_q[9][63:32];
`APB4_APU__COMMAND, `APB4_APU__IRQ_TEST, `APB4_APU__RING_DOORBELL: begin
@@ -281,10 +310,15 @@ module apu_reg (
if (!s_write_err) begin
unique case (s_offset)
`APB4_APU__COMMAND: begin
s_write_unsupported = (apb4.pwdata & 32'h0000_0039) != 32'd0;
s_write_unsupported = (apb4.pwdata & 32'h0000_0029) != 32'd0;
s_write_err = (apb4.pstrb != 4'hf) ||
((apb4.pwdata & 32'hffff_ff80) != 32'd0) ||
(apb4.pwdata[`APB4_APU__COMMAND_ABORT] && !core_busy_i) ||
(apb4.pwdata[`APB4_APU__COMMAND_MICROCODE_LOAD] &&
(!core_idle_i || !quiesce_i || (owner_i != 2'd0) || mc_lock_i ||
(s_mc_cfg_q[0][5:0] != 6'd0) || (s_mc_cfg_q[1] == 32'd0) ||
s_mc_image_end[32] || (s_mc_cfg_q[0] < s_acl_q[0]) ||
(s_mc_image_end[31:0] > s_acl_q[1]))) ||
((apb4.pwdata[`APB4_APU__COMMAND_SOFT_RESET] ||
apb4.pwdata[`APB4_APU__COMMAND_CLEAR_COUNTERS]) && !core_idle_i) ||
s_write_unsupported;
@@ -429,6 +463,7 @@ module apu_reg (
s_perf_snapshot_valid_d = s_perf_snapshot_valid_q;
s_soft_reset = 1'b0;
s_abort = 1'b0;
s_microcode_load = 1'b0;
s_cnt_clear = 1'b0;
s_irq_clear = 11'd0;
s_irq_set = irq_set_i;
@@ -436,9 +471,10 @@ module apu_reg (
if (s_write && !s_write_err) begin
unique case (s_offset)
`APB4_APU__COMMAND: begin
s_abort = apb4.pwdata[`APB4_APU__COMMAND_ABORT];
s_soft_reset = apb4.pwdata[`APB4_APU__COMMAND_SOFT_RESET];
s_cnt_clear = apb4.pwdata[`APB4_APU__COMMAND_CLEAR_COUNTERS];
s_abort = apb4.pwdata[`APB4_APU__COMMAND_ABORT];
s_soft_reset = apb4.pwdata[`APB4_APU__COMMAND_SOFT_RESET];
s_cnt_clear = apb4.pwdata[`APB4_APU__COMMAND_CLEAR_COUNTERS];
s_microcode_load = apb4.pwdata[`APB4_APU__COMMAND_MICROCODE_LOAD];
end
`APB4_APU__IRQ_STATE: s_irq_clear = s_strobed_write[10:0];
`APB4_APU__IRQ_ENABLE: s_irq_en_d = s_merged_write[10:0];
@@ -535,6 +571,22 @@ module apu_reg (
s_stream_watermark_d = 16'd0;
s_perf_en_d = 1'b0;
s_perf_snapshot_valid_d = 1'b0;
if (resource_reset_apply_i && s_err_stat_q[`APB4_APU__ERROR_VALID] &&
(s_err_stat_q[6:1] == `APB4_APU__ERROR_CODE_RESOURCE_RESET)) begin
s_err_stat_d = s_err_stat_q;
s_err_addr_d = s_err_addr_q;
s_err_detail_d = s_err_detail_q;
s_irq_state_d[`APB4_APU__IRQ_FIRST_ERROR] = 1'b1;
end else if (resource_reset_apply_i && fault_valid_i &&
(fault_code_i == `APB4_APU__ERROR_CODE_RESOURCE_RESET)) begin
s_err_stat_d = 32'd1 | (32'(fault_code_i) << `APB4_APU__ERROR_CODE) |
(32'(fault_stage_i) << `APB4_APU__ERROR_STAGE) |
(32'(fault_resp_i) << `APB4_APU__ERROR_AXI_RESPONSE) |
(32'(fault_index_i) << `APB4_APU__ERROR_DESCRIPTOR_INDEX);
s_err_addr_d = fault_addr_i;
s_err_detail_d = fault_detail_i;
s_irq_state_d[`APB4_APU__IRQ_FIRST_ERROR] = 1'b1;
end
end
end
@@ -697,7 +749,9 @@ module apu_reg (
);
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i || s_soft_reset || resource_reset_apply_i || s_cnt_clear) begin
if (!rst_n_i) begin
s_perf_snapshot_q <= '0;
end else if (s_soft_reset || resource_reset_apply_i || s_cnt_clear) begin
s_perf_snapshot_q <= '0;
end else if (s_write && !s_write_err && (s_offset == `APB4_APU__PERF_CONTROL) &&
s_merged_write[`APB4_APU__PERF_CONTROL_SNAPSHOT]) begin
@@ -708,7 +762,7 @@ module apu_reg (
s_perf_snapshot_q[4] <= perf_dma_read_stalls_i;
s_perf_snapshot_q[5] <= perf_dma_write_stalls_i;
s_perf_snapshot_q[6] <= perf_stream_stalls_i;
s_perf_snapshot_q[7] <= 64'd0;
s_perf_snapshot_q[7] <= perf_sequencer_instr_i;
s_perf_snapshot_q[8] <= 64'd0;
s_perf_snapshot_q[9] <= perf_faults_i;
end
+30 -1
View File
@@ -278,7 +278,36 @@ module apu_ring_scheduler (
end
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i || soft_reset_i) begin
if (!rst_n_i) begin
s_state_q <= Idle;
s_word_index_q <= 6'd0;
s_head_q <= 8'd0;
s_completed_q <= 32'd0;
s_active_q <= 1'b0;
s_ring_err_q <= 1'b0;
s_wrapped_q <= 1'b0;
s_stopped_q <= 1'b0;
s_pending_q <= 8'd0;
s_coalesce_timer_q <= 16'd0;
s_last_index_q <= 8'd0;
s_job_busy_q <= 1'b0;
s_job_done_q <= 1'b0;
s_job_err_q <= 1'b0;
s_job_aborted_q <= 1'b0;
s_job_code_q <= 6'd0;
s_job_stage_q <= 4'd0;
s_job_resp_q <= 2'd0;
s_ring_event_q <= 1'b0;
s_err_event_q <= 1'b0;
s_abort_done_q <= 1'b0;
s_err_code_q <= 6'd0;
s_err_stage_q <= 4'd0;
s_err_resp_q <= 2'd0;
s_err_index_q <= 8'd0;
s_err_addr_q <= 32'd0;
s_abort_pending_q <= 1'b0;
s_ring_en_q <= 1'b0;
end else if (soft_reset_i) begin
s_state_q <= Idle;
s_word_index_q <= 6'd0;
s_head_q <= 8'd0;
+8 -1
View File
@@ -165,7 +165,14 @@ module apu_stream_router #(
);
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i || flush_i) begin
if (!rst_n_i) begin
s_tx_low_armed_q <= 1'b0;
s_rx_high_armed_q <= 1'b1;
s_tx_underrun_q <= 1'b0;
s_rx_overrun_q <= 1'b0;
s_tx_words_q <= 8'd0;
s_rx_words_q <= 8'd0;
end else if (flush_i) begin
s_tx_low_armed_q <= 1'b0;
s_rx_high_armed_q <= 1'b1;
s_tx_underrun_q <= 1'b0;
+4
View File
@@ -125,9 +125,13 @@
/ip/multimedia/dvp_core.sv
/ip/multimedia/dvp_reg.sv
/ip/multimedia/axi4s_dvp.sv
/ip/multimedia/apu_microcode_pkg.sv
/ip/multimedia/apu_dma.sv
/ip/multimedia/apu_ring_scheduler.sv
/ip/multimedia/apu_stream_router.sv
/ip/multimedia/apu_control_store.sv
/ip/multimedia/apu_microcode_loader.sv
/ip/multimedia/apu_codec_sequencer.sv
/ip/multimedia/apu_reg.sv
/ip/multimedia/apb4_apu.sv
/ip/multimedia/jpeg_pkg.sv
+200
View File
@@ -0,0 +1,200 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
module apu_loader_formal_design (
input logic clk_i,
output logic rst_n_i,
output logic f_past_valid,
output logic [ 2:0] scenario,
output logic [ 6:0] cycle,
output logic [ 7:0] status,
output logic lock,
output logic store_write,
output logic [10:0] store_addr,
output logic load_done,
output logic abort_done,
output logic fault_valid,
output logic [ 5:0] fault_code,
output logic [ 3:0] fault_stage,
output logic [ 1:0] fault_resp,
output logic [31:0] fault_addr,
output logic [31:0] fault_detail,
output logic [31:0] actual_crc,
output logic abort_seen,
output logic fault_seen,
output logic load_seen,
output logic store_seen,
output logic [ 1:0] request_count
);
`ifndef APU_LOADER_FORMAL_SCENARIO
`define APU_LOADER_FORMAL_SCENARIO 0
`endif
localparam logic [2:0] FormalScenario = 3'(`APU_LOADER_FORMAL_SCENARIO);
logic [2:0] f_scenario;
logic [6:0] s_cycle_q;
logic s_request_valid;
logic [31:0] s_request_addr, s_request_bytes;
logic s_dma_active_q, s_dma_done_q;
logic [31:0] s_dma_addr_q, s_dma_bytes_q;
logic [ 7:0] s_dma_beat_q;
logic [ 1:0] s_request_count_q;
logic [31:0] s_dma_data;
logic s_dma_last;
logic s_dma_ready;
logic s_store_read, s_store_valid_q;
logic [63:0] s_store_write_data, s_store_word_q, s_store_read_data_q;
logic s_abort_seen_q, s_fault_seen_q, s_load_seen_q, s_store_seen_q;
function automatic logic [31:0] image_word(input logic [7:0] word_i);
logic [31:0] s_word;
begin
s_word = 32'd0;
unique case (word_i)
8'd0: s_word = 32'h4150_4d43;
8'd1: s_word = 32'h0001_0000;
8'd2: s_word = 32'h0000_00c8;
8'd3: s_word = 32'h0000_0001;
8'd4: s_word = 32'h0000_00c0;
8'd7: s_word = 32'h0000_0040;
8'd8: s_word = 32'h0000_0003;
8'd5: s_word = (f_scenario == 3'd1) ? 32'd204 : 32'd0;
8'd11: s_word = (f_scenario == 3'd3) ? 32'h62e4_edd0 : 32'h62e4_edd1;
8'd12: s_word = 32'h5566_7788;
8'd13: s_word = 32'h1122_3344;
8'd20, 8'd24, 8'd28, 8'd29, 8'd36, 8'd37: s_word = 32'h0000_0001;
8'd21: s_word = (f_scenario == 3'd2) ? 32'd0 : 32'h0000_0001;
8'd32: s_word = 32'h0000_0002;
8'd49: s_word = (f_scenario == 3'd4) ? 32'h0500_0000 : 32'h0100_0000;
default: s_word = 32'd0;
endcase
return s_word;
end
endfunction
assign f_scenario = FormalScenario;
assign scenario = f_scenario;
assign cycle = s_cycle_q;
assign request_count = s_request_count_q;
assign abort_seen = s_abort_seen_q;
assign fault_seen = s_fault_seen_q;
assign load_seen = s_load_seen_q;
assign store_seen = s_store_seen_q;
assign s_dma_data = image_word(8'(((s_dma_addr_q - 32'h0000_1000) >> 2) + s_dma_beat_q));
assign s_dma_last = s_dma_beat_q == ((s_dma_bytes_q >> 2) - 1'b1);
apu_microcode_loader #(
.PathStackDepth(2)
) u_dut (
.clk_i,
.rst_n_i,
.start_i (s_cycle_q == 7'd1),
.abort_i ((f_scenario == 3'd5) && (s_cycle_q == 7'd24)),
.resource_reset_i ((f_scenario == 3'd6) && (s_cycle_q == 7'd24)),
.soft_reset_i (1'b0),
.counter_clear_i (1'b0),
.image_addr_i (32'h0000_1000),
.image_size_i (32'd200),
.expected_crc_i (32'h62e4_edd1),
.dma_request_valid_o(s_request_valid),
.dma_request_ready_i(1'b1),
.dma_request_addr_o (s_request_addr),
.dma_request_bytes_o(s_request_bytes),
.dma_data_i (s_dma_data),
.dma_keep_i (4'hf),
.dma_last_i (s_dma_last),
.dma_valid_i (s_dma_active_q),
.dma_ready_o (s_dma_ready),
.dma_done_i (s_dma_done_q),
.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_read_o (s_store_read),
.store_write_o (store_write),
.store_addr_o (store_addr),
.store_data_o (s_store_write_data),
.store_data_i (s_store_read_data_q),
.store_valid_i (s_store_valid_q),
.stat_o (status),
.abi_o (),
.build_id_o (),
.lock_o (lock),
.actual_crc_o (actual_crc),
.load_count_o (),
.entry_pc_o (),
.entry_first_o (),
.entry_last_o (),
.entry_max_loop_o (),
.entry_max_retired_o(),
.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 ()
);
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 <= 7'd0;
s_dma_active_q <= 1'b0;
s_dma_done_q <= 1'b0;
s_dma_addr_q <= 32'd0;
s_dma_bytes_q <= 32'd0;
s_dma_beat_q <= 8'd0;
s_request_count_q <= 2'd0;
s_store_valid_q <= 1'b0;
s_store_word_q <= 64'd0;
s_store_read_data_q <= 64'd0;
s_abort_seen_q <= 1'b0;
s_fault_seen_q <= 1'b0;
s_load_seen_q <= 1'b0;
s_store_seen_q <= 1'b0;
end else begin
if (s_cycle_q != 7'h7f) s_cycle_q <= s_cycle_q + 1'b1;
s_dma_done_q <= 1'b0;
s_store_valid_q <= s_store_read;
if (s_request_valid && !s_dma_active_q) begin
s_dma_active_q <= 1'b1;
s_dma_addr_q <= s_request_addr;
s_dma_bytes_q <= s_request_bytes;
s_dma_beat_q <= 8'd0;
s_request_count_q <= s_request_count_q + 1'b1;
end
if (s_dma_active_q &&
(s_dma_ready || (((f_scenario == 3'd5) || (f_scenario == 3'd6)) &&
(s_cycle_q >= 7'd24)))) begin
if (s_dma_last) begin
s_dma_active_q <= 1'b0;
s_dma_done_q <= 1'b1;
end else begin
s_dma_beat_q <= s_dma_beat_q + 1'b1;
end
end
if (store_write && (store_addr == 11'd0)) begin
s_store_word_q <= s_store_write_data;
s_store_seen_q <= 1'b1;
end
if (s_store_read) begin
s_store_read_data_q <= (store_addr == 11'd0) ? s_store_word_q : 64'd0;
end
if (abort_done) s_abort_seen_q <= 1'b1;
if (fault_valid && (fault_code == 6'd21)) s_fault_seen_q <= 1'b1;
if (load_done) s_load_seen_q <= 1'b1;
end
end
endmodule
`undef APU_LOADER_FORMAL_SCENARIO
@@ -0,0 +1,78 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
module apu_loader_formal;
(* anyseq *) (* gclk *) reg clk_i;
wire rst_n_i, f_past_valid;
wire [2:0] scenario;
wire [1:0] request_count;
wire [6:0] cycle;
wire [7:0] status;
wire lock, store_write, load_done, abort_done, fault_valid;
wire abort_seen, fault_seen, load_seen, store_seen;
wire [10:0] store_addr;
wire [ 5:0] fault_code;
wire [ 3:0] fault_stage;
wire [ 1:0] fault_resp;
wire [31:0] fault_addr, fault_detail, actual_crc;
apu_loader_formal_design u_design (.*);
always @(posedge clk_i) begin
if (rst_n_i) begin
assert (!(status[0] && (|status[7:1])));
assert (request_count <= 2'd3);
if (store_write) begin
assert (status[0]);
assert (request_count == 2'd3);
assert (store_addr[10:0] == store_addr);
end
if (load_done) assert (status[1] && lock);
if (abort_seen || fault_seen) assert (!status[1] && !lock && !load_seen);
if (load_seen) assert (store_seen && status[1] && lock);
if (scenario == 3'd0) begin
assert (!fault_valid || (fault_code != 6'd9));
end else if (scenario == 3'd1) begin
if (fault_valid) begin
assert (status[3] && !lock && !store_seen && (request_count == 2'd1));
assert ((fault_code == 6'd9) && (fault_stage == 4'd1) && (fault_resp == 2'd0));
assert ((fault_addr == 32'h0000_1014) && (fault_detail == 32'd204));
assert (actual_crc == 32'd0);
end
end else if (scenario == 3'd2) begin
if (fault_valid) begin
assert (status[3] && !lock && !store_seen && (request_count == 2'd2));
assert ((fault_code == 6'd9) && (fault_stage == 4'd1) && (fault_resp == 2'd0));
assert ((fault_addr == 32'h0000_1054) && (fault_detail == 32'd0));
assert (actual_crc == 32'd0);
end
end else if (scenario == 3'd3) begin
if (fault_valid) begin
assert (status[6] && !lock && (fault_code == 6'd10));
assert ((fault_stage == 4'd1) && (fault_resp == 2'd0) && (fault_addr == 32'h0000_102c));
end
end else if (scenario == 3'd4) begin
if (fault_valid) begin
assert (status[4] && !lock && (fault_code == 6'd9));
assert ((fault_stage == 4'd1) && (fault_resp == 2'd0) && (fault_addr == 32'h0000_10c0));
assert (fault_detail[7:0] == 8'd3);
end
end else if (scenario == 3'd6) begin
if (fault_valid) begin
assert (!lock && (fault_code == 6'd21) && (fault_stage == 4'd11));
end
end
end
if (f_past_valid && $past(rst_n_i) && $past(status[0]) && !load_done) begin
assert (!status[1]);
end
cover (rst_n_i &&
(((scenario == 3'd0) && load_seen && store_seen && status[1] && lock) ||
((scenario == 3'd1) && fault_valid && status[3]) ||
((scenario == 3'd2) && fault_valid && status[3]) ||
((scenario == 3'd3) && fault_valid && status[6]) ||
((scenario == 3'd4) && fault_valid && status[4]) ||
((scenario == 3'd5) && abort_seen) ||
((scenario == 3'd6) && fault_seen)));
end
endmodule
+139
View File
@@ -0,0 +1,139 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
module apu_sequencer_formal_design (
input logic clk_i,
output logic rst_n_i,
output logic f_past_valid,
output logic [ 1:0] scenario,
output logic [ 5:0] cycle,
output logic [31:0] status,
output logic [31:0] retired,
output logic [31:0] result,
output logic trapped,
output logic trap_event,
output logic abort_done,
output logic trap_seen,
output logic abort_seen,
output logic idle,
output logic fault_valid,
output logic [ 5:0] fault_code,
output logic [ 3:0] fault_stage,
output logic [ 1:0] fault_resp,
output logic [ 7:0] fault_index,
output logic [31:0] fault_addr,
output logic [31:0] fault_detail
);
(* anyconst *)logic [1:0] f_scenario;
logic [5:0] s_cycle_q;
logic s_fetch, s_fetch_pending_q;
logic [10:0] s_fetch_addr, s_fetch_addr_q;
logic [63:0] s_fetch_data;
logic [15:0][31:0] s_gpr;
logic s_trap_seen_q, s_abort_seen_q;
logic [2:0][10:0] s_entry_last;
logic [2:0][23:0] s_entry_max_retired;
assign scenario = f_scenario;
assign cycle = s_cycle_q;
assign result = s_gpr[2];
assign trap_seen = s_trap_seen_q;
assign abort_seen = s_abort_seen_q;
assign s_entry_last = (f_scenario == 2'd3) ? {11'd6, 11'd6, 11'd6} : {11'd3, 11'd3, 11'd3};
assign s_entry_max_retired = (f_scenario == 2'd3) ?
{24'd16, 24'd16, 24'd16} : {24'd8, 24'd8, 24'd8};
always_comb begin
if (f_scenario == 2'd3) begin
unique case (s_fetch_addr_q)
11'd0: s_fetch_data = 64'h1100_0000_0000_0002;
11'd1: s_fetch_data = 64'h0600_0000_0000_0000;
11'd2: s_fetch_data = 64'h0000_0000_0000_0000;
11'd3: s_fetch_data = 64'h0700_0000_0000_0002;
11'd4: s_fetch_data = 64'h0400_0000_0000_0001;
11'd5: s_fetch_data = 64'h0100_0000_0000_0000;
default: s_fetch_data = 64'h0500_0000_0000_0000;
endcase
end else begin
unique case (s_fetch_addr_q)
11'd0: s_fetch_data = 64'h1100_0000_0000_0002;
11'd1: s_fetch_data = 64'h1101_0000_0000_0003;
11'd2: s_fetch_data = 64'h1202_0100_0000_0000;
default: s_fetch_data = 64'h0100_0000_0000_0000;
endcase
end
end
apu_codec_sequencer u_dut (
.clk_i,
.rst_n_i,
.soft_reset_i (1'b0),
.resource_reset_i (1'b0),
.counter_clear_i (1'b0),
.abort_i ((f_scenario == 2'd2) && (s_cycle_q == 6'd8)),
.launch_i (s_cycle_q == 6'd1),
.launch_entry_i (2'd0),
.image_valid_i (1'b1),
.timeout_i ((f_scenario == 2'd1) ? 32'd1 : 32'd16),
.entry_pc_i ({11'd0, 11'd0, 11'd0}),
.entry_first_i ({11'd0, 11'd0, 11'd0}),
.entry_last_i (s_entry_last),
.entry_max_loop_i ({16'd4, 16'd4, 16'd4}),
.entry_max_retired_i (s_entry_max_retired),
.input_exhausted_i (1'b1),
.input_ready_i (1'b0),
.output_ready_i (1'b1),
.kernel_done_i (1'b0),
.transport_idle_success_i(1'b1),
.stall_i (1'b0),
.cause_valid_i (1'b0),
.cause_code_i (6'd0),
.cause_stage_i (4'd0),
.cause_resp_i (2'd0),
.cause_index_i (8'd0),
.cause_addr_i (32'd0),
.cause_detail_i (32'd0),
.fetch_o (s_fetch),
.fetch_addr_o (s_fetch_addr),
.fetch_data_i (s_fetch_data),
.fetch_valid_i (s_fetch_pending_q),
.stat_o (status),
.retired_o (retired),
.gpr_o (s_gpr),
.trapped_o (trapped),
.trap_event_o (trap_event),
.abort_done_o (abort_done),
.fault_valid_o (fault_valid),
.fault_code_o (fault_code),
.fault_stage_o (fault_stage),
.fault_resp_o (fault_resp),
.fault_index_o (fault_index),
.fault_addr_o (fault_addr),
.fault_detail_o (fault_detail),
.perf_retired_o (),
.idle_o (idle)
);
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 <= 6'd0;
s_fetch_pending_q <= 1'b0;
s_fetch_addr_q <= 11'd0;
s_trap_seen_q <= 1'b0;
s_abort_seen_q <= 1'b0;
end else begin
if (s_cycle_q != 6'h3f) s_cycle_q <= s_cycle_q + 1'b1;
s_fetch_pending_q <= s_fetch;
if (s_fetch) s_fetch_addr_q <= s_fetch_addr;
if (trap_event) s_trap_seen_q <= 1'b1;
if (abort_done) s_abort_seen_q <= 1'b1;
end
end
endmodule
@@ -0,0 +1,56 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
module apu_sequencer_formal;
(* anyseq *) (* gclk *) reg clk_i;
wire rst_n_i, f_past_valid;
wire [1:0] scenario;
wire [5:0] cycle;
wire [31:0] status, retired, result;
wire trapped, trap_event, abort_done, trap_seen, abort_seen, idle, fault_valid;
wire [5:0] fault_code;
wire [3:0] fault_stage;
wire [1:0] fault_resp;
wire [7:0] fault_index;
wire [31:0] fault_addr, fault_detail;
apu_sequencer_formal_design u_design (.*);
always @(posedge clk_i) begin
if (rst_n_i) begin
assert (status[31:21] == 11'd0);
assert (status[10:0] <= 11'd6);
assert (retired <= 32'd16);
if (trap_event) begin
assert (trapped && fault_valid);
assert (fault_code == 6'd11);
assert (fault_stage == 4'd11);
assert (fault_resp == 2'd0);
assert (fault_index == 8'd0);
assert (fault_addr == ({21'd0, status[10:0]} << 3));
end
if (cycle >= 6'd36) begin
assert (idle);
if (scenario == 2'd0) begin
assert (!trapped && (retired == 32'd4) && (result == 32'd5));
end else if (scenario == 2'd1) begin
assert (trap_seen && trapped && (retired == 32'd0));
assert (fault_detail[7:0] == 8'd7);
end else if (scenario == 2'd2) begin
assert (abort_seen && !trapped);
end else begin
assert (!trapped && (retired == 32'd9));
end
end
end
if (f_past_valid && $past(rst_n_i) && $past(idle) && (cycle > 6'd2)) begin
assert ($stable(status));
assert ($stable(retired));
assert ($stable(trapped));
end
cover (rst_n_i && idle && (scenario == 2'd0) && (retired == 32'd4));
cover (rst_n_i && trap_seen && (scenario == 2'd1));
cover (rst_n_i && abort_seen && (scenario == 2'd2));
cover (rst_n_i && idle && (scenario == 2'd3) && (retired == 32'd9));
end
endmodule
@@ -24,11 +24,27 @@ MEMORY = ROOT / "rtl/ip/memory"
STORAGE = ROOT / "rtl/ip/storage"
MULTIMEDIA = ROOT / "rtl/ip/multimedia"
TOP = ROOT / "rtl/mini/top"
APU_LOADER_SCENARIOS = {
"apu_loader_success": 0,
"apu_loader_header_range": 1,
"apu_loader_descriptor_range": 2,
"apu_loader_crc": 3,
"apu_loader_control_flow": 4,
"apu_loader_abort": 5,
"apu_loader_resource_reset": 6,
}
def target_defines(target: str) -> list[str]:
if target == "onchip_ram":
return ["+define+PDK_BEHAV", "+define+SYNTHESIS"]
if target == "apu_loader" or target in APU_LOADER_SCENARIOS:
scenario = APU_LOADER_SCENARIOS.get(target, 0)
return [
"+define+SV_ASSRT_DISABLE",
"+define+SYNTHESIS",
f"+define+APU_LOADER_FORMAL_SCENARIO={scenario}",
]
if target == "opipsram":
return ["+define+PDK_BEHAV"]
if target == "sysctrl":
@@ -257,6 +273,19 @@ def source_files(target: str) -> list[Path]:
MULTIMEDIA / "apu_dma.sv",
SCRIPT_DIR / "apu_formal.sv",
]
if target == "apu_loader" or target in APU_LOADER_SCENARIOS:
return [
ROOT / "rtl/tech/tc_sram.sv",
MULTIMEDIA / "apu_microcode_pkg.sv",
MULTIMEDIA / "apu_microcode_loader.sv",
SCRIPT_DIR / "apu_loader_formal.sv",
]
if target == "apu_sequencer":
return [
MULTIMEDIA / "apu_microcode_pkg.sv",
MULTIMEDIA / "apu_codec_sequencer.sv",
SCRIPT_DIR / "apu_sequencer_formal.sv",
]
if target == "gateway_a":
return [
COMMON_RTL / "interface/axi4_if.sv",
@@ -333,6 +362,8 @@ def parse_args() -> argparse.Namespace:
"opipsram",
"dma",
"apu",
*APU_LOADER_SCENARIOS,
"apu_sequencer",
"gateway_a",
"sdio",
),
+14 -1
View File
@@ -22,8 +22,11 @@ def render(
mode: str,
depth: int,
vcd: bool = True,
skip: int | None = None,
) -> str:
options = ["[options]", f"mode {mode}", f"depth {depth}"]
if skip is not None:
options.append(f"skip {skip}")
if not vcd:
options.append("vcd off")
return "\n".join(
@@ -65,6 +68,7 @@ def parse_args() -> argparse.Namespace:
parser.add_argument("--mode", choices=("prove", "bmc", "cover"), required=True)
parser.add_argument("--depth", type=positive_int, required=True)
parser.add_argument("--no-vcd", action="store_false", dest="vcd")
parser.add_argument("--skip", type=positive_int)
parser.add_argument("--output", type=Path, required=True)
return parser.parse_args()
@@ -73,7 +77,16 @@ def main() -> int:
args = parse_args()
atomic_write(
args.output.resolve(),
render(args.top, args.input, args.properties, args.solver, args.mode, args.depth, args.vcd),
render(
args.top,
args.input,
args.properties,
args.solver,
args.mode,
args.depth,
args.vcd,
args.skip,
),
)
return 0
+66 -48
View File
@@ -1,39 +1,46 @@
FORMAL_DIR := $(VARIANT_ROOT)/formal
FORMAL_SBY ?= sby
FORMAL_BITWUZLA ?= $(shell command -v bitwuzla)
FORMAL_SOLVER := bitwuzla
FORMAL_SOLVER_DIR := $(FORMAL_DIR)/bin
FORMAL_SOLVER_WRAPPER := $(FORMAL_SOLVER_DIR)/bitwuzla
FORMAL_DEPTH ?= 20
FORMAL_PLL_RCU_DEPTH ?= 32
FORMAL_WS2812_DEPTH ?= 120
FORMAL_I2C_DEPTH ?= 80
FORMAL_CLINT_DEPTH ?= 32
FORMAL_PSRAM_DEPTH ?= 32
FORMAL_OPIPSRAM_DEPTH ?= 40
FORMAL_OPIPSRAM_BMC_DEPTH ?= 40
FORMAL_DMA_DEPTH ?= 24
FORMAL_DMA_COVER_DEPTH ?= 32
FORMAL_APU_DEPTH ?= 24
FORMAL_GATEWAY_A_DEPTH ?= 20
FORMAL_SDIO_DEPTH ?= 20
FORMAL_SDIO_COVER_DEPTH ?= 24
FORMAL_TIMEOUT ?= 60
FORMAL_WS2812_TIMEOUT ?= 120
FORMAL_I2C_TIMEOUT ?= 300
FORMAL_OPIPSRAM_TIMEOUT ?= 300
FORMAL_OPIPSRAM_BMC_TIMEOUT ?= 600
FORMAL_DMA_TIMEOUT ?= 120
FORMAL_APU_TIMEOUT ?= 120
FORMAL_GATEWAY_A_TIMEOUT ?= 120
FORMAL_SDIO_TIMEOUT ?= 120
FORMAL_TARGET_TIMEOUT = $(if $(filter dma,$*),$(FORMAL_DMA_TIMEOUT),$(if $(filter apu,$*),$(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_COMPACT_COVER_TARGETS := i2c opipsram dma
FORMAL_TARGETS := bus rib_adapter rib2apb sysctrl pll_rcu gpio ws2812 uart i2c timer clint dvp i2s psram onchip_ram opipsram dma apu 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
FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
FORMAL_DIR := $(VARIANT_ROOT)/formal
FORMAL_SBY ?= sby
FORMAL_BITWUZLA ?= $(shell command -v bitwuzla)
FORMAL_SOLVER := bitwuzla
FORMAL_SOLVER_DIR := $(FORMAL_DIR)/bin
FORMAL_SOLVER_WRAPPER := $(FORMAL_SOLVER_DIR)/bitwuzla
FORMAL_DEPTH ?= 20
FORMAL_PLL_RCU_DEPTH ?= 32
FORMAL_WS2812_DEPTH ?= 120
FORMAL_I2C_DEPTH ?= 80
FORMAL_CLINT_DEPTH ?= 32
FORMAL_PSRAM_DEPTH ?= 32
FORMAL_OPIPSRAM_DEPTH ?= 40
FORMAL_OPIPSRAM_BMC_DEPTH ?= 40
FORMAL_DMA_DEPTH ?= 24
FORMAL_DMA_COVER_DEPTH ?= 32
FORMAL_APU_DEPTH ?= 24
FORMAL_APU_LOADER_DEPTH ?= 128
FORMAL_APU_LOADER_COVER_DEPTH ?= 128
FORMAL_APU_SEQUENCER_DEPTH ?= 38
FORMAL_GATEWAY_A_DEPTH ?= 20
FORMAL_SDIO_DEPTH ?= 20
FORMAL_SDIO_COVER_DEPTH ?= 24
FORMAL_TIMEOUT ?= 60
FORMAL_WS2812_TIMEOUT ?= 120
FORMAL_I2C_TIMEOUT ?= 300
FORMAL_OPIPSRAM_TIMEOUT ?= 300
FORMAL_OPIPSRAM_BMC_TIMEOUT ?= 600
FORMAL_DMA_TIMEOUT ?= 120
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_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_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
FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
$(RTL_PATH)/formal/bus_formal_props.sv \
$(RTL_PATH)/formal/rib_adapter_formal.sv \
$(RTL_PATH)/formal/rib_adapter_formal_props.sv \
@@ -69,6 +76,10 @@ 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_loader_formal.sv \
$(RTL_PATH)/formal/apu_loader_formal_props.sv \
$(RTL_PATH)/formal/apu_sequencer_formal.sv \
$(RTL_PATH)/formal/apu_sequencer_formal_props.sv \
$(RTL_PATH)/formal/gateway_a_formal.sv \
$(RTL_PATH)/formal/gateway_a_formal_props.sv \
$(RTL_PATH)/formal/sdio_formal.sv \
@@ -150,6 +161,9 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
$(ROOT_PATH)/rtl/ip/peripheral/dma_req_if.sv \
$(ROOT_PATH)/rtl/ip/peripheral/dma_axi4_master.sv \
$(ROOT_PATH)/rtl/ip/multimedia/apu_dma.sv \
$(ROOT_PATH)/rtl/ip/multimedia/apu_microcode_pkg.sv \
$(ROOT_PATH)/rtl/ip/multimedia/apu_microcode_loader.sv \
$(ROOT_PATH)/rtl/ip/multimedia/apu_codec_sequencer.sv \
$(ROOT_PATH)/rtl/ip/peripheral/dma_core.sv \
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/clkrst/counter.sv \
$(RTL_PATH)/top/rcu.sv \
@@ -169,15 +183,15 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/utils/fifo.sv \
$(ROOT_PATH)/rtl/managed/clusterip/common/rtl/stream/round_robin_arbiter.sv \
$(ROOT_PATH)/scripts/bitwuzla_smt2.py
FORMAL_STAMPS := $(addsuffix /.stamp,$(addprefix $(FORMAL_DIR)/,$(FORMAL_TARGETS)))
FORMAL_INTERMEDIATES := $(foreach target,$(FORMAL_TARGETS), \
FORMAL_STAMPS := $(addsuffix /.stamp,$(addprefix $(FORMAL_DIR)/,$(FORMAL_TARGETS)))
FORMAL_INTERMEDIATES := $(foreach target,$(FORMAL_TARGETS), \
$(FORMAL_DIR)/$(target)/formal.fl $(FORMAL_DIR)/$(target)/design.v \
$(FORMAL_DIR)/$(target)/prove.sby $(FORMAL_DIR)/$(target)/cover.sby \
$(FORMAL_DIR)/$(target)/prove.stamp \
$(FORMAL_DIR)/$(target)/cover.stamp) \
$(FORMAL_DIR)/opipsram/bmc.sby $(FORMAL_DIR)/opipsram/bmc.stamp \
$(FORMAL_SOLVER_WRAPPER)
FORMAL_RESULT := $(META_DIR)/formal.json
FORMAL_RESULT := $(META_DIR)/formal.json
.SECONDARY: $(FORMAL_INTERMEDIATES)
@@ -194,18 +208,18 @@ $(FORMAL_DIR)/%/design.v: $(FORMAL_DIR)/%/formal.fl $(RTL_PATH)/script/convt_sv2
--log $(@D)/sv2v.log --result $(@D)/result-sv2v.json \
-- python3 $(RTL_PATH)/script/convt_sv2v.py -f $< --output $@
$(FORMAL_DIR)/%/prove.sby: $(FORMAL_DIR)/%/design.v $(RTL_PATH)/formal/%_formal_props.sv \
$(FORMAL_DIR)/%/prove.sby: $(FORMAL_DIR)/%/design.v \
$(FORMAL_SBY_GENERATOR)
python3 $(FORMAL_SBY_GENERATOR) --top $*_formal --input $< \
--properties $(RTL_PATH)/formal/$*_formal_props.sv --solver $(FORMAL_SOLVER) \
--mode $(if $(filter dma apu 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,$*),$(FORMAL_APU_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 $@
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 $@
@if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi
$(FORMAL_DIR)/%/cover.sby: $(FORMAL_DIR)/%/design.v $(RTL_PATH)/formal/%_formal_props.sv \
$(FORMAL_DIR)/%/cover.sby: $(FORMAL_DIR)/%/design.v \
$(FORMAL_SBY_GENERATOR)
python3 $(FORMAL_SBY_GENERATOR) --top $*_formal --input $< \
--properties $(RTL_PATH)/formal/$*_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,$*),$(FORMAL_APU_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))))))))))) \
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))))))))))) \
$(if $(filter $(FORMAL_COMPACT_COVER_TARGETS),$*),--no-vcd) --output $@
@if [ "$*" = opipsram ]; then sed -i '/^async2sync/i clk2fflogic' $@; fi
@@ -304,6 +318,10 @@ formal-dma: $(FORMAL_DIR)/dma/.stamp | manifest
formal-apu: $(FORMAL_DIR)/apu/.stamp | manifest
formal-apu-loader: $(addsuffix /.stamp,$(addprefix $(FORMAL_DIR)/,$(FORMAL_APU_LOADER_TARGETS))) | manifest
formal-apu-sequencer: $(FORMAL_DIR)/apu_sequencer/.stamp | manifest
formal-gateway-a: $(FORMAL_DIR)/gateway_a/.stamp | manifest
formal-sdio: $(FORMAL_DIR)/sdio/.stamp | manifest
@@ -314,4 +332,4 @@ formal-doctor:
formal-clean:
python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(FORMAL_DIR)
.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-clint formal-dvp formal-i2s formal-psram formal-onchip-ram formal-opipsram formal-dma formal-apu formal-gateway-a formal-sdio formal-doctor formal-clean
.PHONY: formal formal-bus formal-rib-adapter formal-rib2apb formal-sysctrl formal-pll-rcu formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-clint formal-dvp formal-i2s formal-psram formal-onchip-ram formal-opipsram formal-dma formal-apu formal-apu-loader formal-apu-sequencer formal-gateway-a formal-sdio formal-doctor formal-clean
+4
View File
@@ -135,8 +135,12 @@
"rtl/ip/multimedia": [
"apb4_apu.sv",
"apb4_jpeg.sv",
"apu_codec_sequencer.sv",
"apu_control_store.sv",
"apu_dma.sv",
"apu_define.svh",
"apu_microcode_loader.sv",
"apu_microcode_pkg.sv",
"apu_ring_scheduler.sv",
"apu_reg.sv",
"apu_stream_router.sv",
+5
View File
@@ -4,6 +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.
Scripts are part of the build contract. Prefer existing helpers over ad-hoc
shell behavior, preserve structured JSON results, and keep setup/download
behavior controlled by `dependencies/dependencies.lock.json`.
+266
View File
@@ -0,0 +1,266 @@
"""Reference interpreter for the enabled APU-P3 microcode subset."""
from __future__ import annotations
import argparse
import json
from dataclasses import dataclass, field
from pathlib import Path
from apu_isa import (
ControlOpcode,
Entry,
Instruction,
InstructionClass,
ScalarOpcode,
parse_apumc,
trap_detail,
validate_p3_instruction,
)
class SequencerTrap(RuntimeError):
def __init__(self, reason: int, pc: int, instruction: Instruction, detail: int | None = None):
self.reason = reason
self.pc = pc
self.instruction = instruction
self.detail = trap_detail(reason, pc, instruction) if detail is None else detail
super().__init__(f"APU sequencer trap {reason} at PC {pc}")
@dataclass
class PredicateInputs:
input_exhausted: bool = False
input_ready: bool = False
output_ready: bool = False
kernel_done: bool = False
transport_done: bool = False
@dataclass
class Machine:
instructions: list[Instruction]
entry: Entry
predicate_inputs: PredicateInputs = field(default_factory=PredicateInputs)
timeout: int = 0xFFFF
fetch_no_retirement_cycles: int = 2
stall_cycles: dict[int, int] = field(default_factory=dict)
registers: list[int] = field(default_factory=lambda: [0] * 16)
eq: bool = False
signed_lt: bool = False
unsigned_lt: bool = False
pc: int = 0
retired: int = 0
return_stack: list[int] = field(default_factory=list)
loops: dict[int, tuple[int, int]] = field(default_factory=dict)
watchdog: int = 0
trace: list[dict[str, object]] = field(default_factory=list)
def __post_init__(self) -> None:
self.pc = self.entry.entry_pc
@staticmethod
def _signed(value: int) -> int:
return value if value < 0x80000000 else value - 0x100000000
def _predicate(self, predicate: int) -> bool:
values = (
True,
self.eq,
not self.eq,
self.signed_lt,
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,
self.predicate_inputs.transport_done,
)
if predicate >= len(values):
raise SequencerTrap(1, self.pc, self.instructions[self.pc])
return values[predicate]
def _write(self, register: int, value: int) -> None:
self.registers[register] = value & 0xFFFFFFFF
def _retire(self, instruction: Instruction, predicate_true: bool) -> None:
self.retired += 1
self.watchdog = 0
self.trace.append(
{
"pc": self.pc,
"instruction": f"0x{instruction.encode():016x}",
"predicate": predicate_true,
"retired": self.retired,
}
)
def _no_retirement_cycle(self, instruction: Instruction) -> None:
self.watchdog += 1
if self.timeout <= 0 or self.watchdog >= self.timeout:
raise SequencerTrap(7, self.pc, instruction)
def _saturate(self, value: int, width: int, signed: bool) -> int:
width = 32 if width == 0 else width
if signed:
signed_value = self._signed(value)
maximum = (1 << (width - 1)) - 1
minimum = -(1 << (width - 1))
return min(max(signed_value, minimum), maximum) & 0xFFFFFFFF
return min(value, (1 << width) - 1)
def step(self) -> bool:
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]
try:
validate_p3_instruction(instruction)
except ValueError as error:
raise SequencerTrap(1, self.pc, instruction) from error
if self.retired >= self.entry.max_retired:
raise SequencerTrap(8, self.pc, instruction)
predicate_true = self._predicate(instruction.predicate)
if not predicate_true:
if self.pc == self.entry.last_pc or self.pc == 0x7FF:
raise SequencerTrap(2, self.pc, instruction)
self._retire(instruction, predicate_true)
self.pc += 1
return False
next_pc = self.pc + 1
terminal = False
if instruction.instruction_class == InstructionClass.CONTROL:
opcode = ControlOpcode(instruction.opcode)
if opcode == ControlOpcode.END:
terminal = True
elif opcode == ControlOpcode.TRAP:
raise SequencerTrap(10, self.pc, instruction, instruction.immediate)
elif opcode == ControlOpcode.JUMP_FWD:
next_pc += instruction.immediate
elif opcode == ControlOpcode.CALL_FWD:
if len(self.return_stack) == 4:
raise SequencerTrap(3, self.pc, instruction)
self.return_stack.append(next_pc)
next_pc += instruction.immediate
elif opcode == ControlOpcode.RET:
if not self.return_stack:
raise SequencerTrap(3, self.pc, instruction)
next_pc = self.return_stack.pop()
elif opcode == ControlOpcode.LOOP_SETUP:
slot = instruction.aux & 3
count = self.registers[instruction.src0] & 0xFFFF
if slot in self.loops or count == 0 or count > self.entry.max_loop_count:
raise SequencerTrap(4, self.pc, instruction)
self.loops[slot] = (count, next_pc)
elif opcode == ControlOpcode.LOOP_BACK:
slot = instruction.aux & 3
if slot not in self.loops:
raise SequencerTrap(4, self.pc, instruction)
count, start = self.loops[slot]
target = next_pc - instruction.immediate
if target != start or target < self.entry.first_pc:
raise SequencerTrap(4, self.pc, instruction)
if count > 1:
self.loops[slot] = (count - 1, start)
next_pc = target
else:
del self.loops[slot]
else:
opcode = ScalarOpcode(instruction.opcode)
source0 = self.registers[instruction.src0]
source1 = self.registers[instruction.src1]
if opcode == ScalarOpcode.MOV:
self._write(instruction.dst, source0)
elif opcode == ScalarOpcode.MOVI:
self._write(instruction.dst, instruction.immediate)
elif opcode == ScalarOpcode.ADD:
self._write(instruction.dst, source0 + source1)
elif opcode == ScalarOpcode.SUB:
self._write(instruction.dst, source0 - source1)
elif opcode == ScalarOpcode.AND:
self._write(instruction.dst, source0 & source1)
elif opcode == ScalarOpcode.OR:
self._write(instruction.dst, source0 | source1)
elif opcode == ScalarOpcode.XOR:
self._write(instruction.dst, source0 ^ source1)
elif opcode == ScalarOpcode.SHL:
self._write(instruction.dst, source0 << (source1 & 31))
elif opcode == ScalarOpcode.SHR:
self._write(instruction.dst, source0 >> (source1 & 31))
elif opcode == ScalarOpcode.SAR:
self._write(instruction.dst, self._signed(source0) >> (source1 & 31))
elif opcode == ScalarOpcode.CMP:
self.eq = source0 == source1
self.signed_lt = self._signed(source0) < self._signed(source1)
self.unsigned_lt = source0 < source1
elif opcode in (ScalarOpcode.MIN, ScalarOpcode.MAX):
left = self._signed(source0) if instruction.aux else source0
right = self._signed(source1) if instruction.aux else source1
result = min(left, right) if opcode == ScalarOpcode.MIN else max(left, right)
self._write(instruction.dst, result)
elif opcode == ScalarOpcode.SAT:
self._write(
instruction.dst,
self._saturate(source0, instruction.aux & 0x1F, bool(instruction.aux & 0x20)),
)
self._retire(instruction, predicate_true)
if not terminal:
if next_pc < self.entry.first_pc or next_pc > self.entry.last_pc:
raise SequencerTrap(2, self.pc, instruction)
self.pc = next_pc
return terminal
def run(self) -> dict[str, object]:
while True:
if self.pc < self.entry.first_pc or self.pc > self.entry.last_pc:
raise SequencerTrap(2, self.pc, Instruction(0, 0))
instruction = self.instructions[self.pc]
for _ in range(self.fetch_no_retirement_cycles):
self._no_retirement_cycle(instruction)
remaining_stalls = self.stall_cycles.get(self.pc, 0)
while remaining_stalls > 0:
self._no_retirement_cycle(instruction)
remaining_stalls -= 1
self.stall_cycles[self.pc] = 0
if self.step():
break
return {
"pc": self.pc,
"retired": self.retired,
"registers": self.registers,
"trace": self.trace,
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("bundle", type=Path)
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")
parser.add_argument("--input-ready", action="store_true")
parser.add_argument("--output-ready", action="store_true")
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())
inputs = PredicateInputs(
input_exhausted=args.input_exhausted,
input_ready=args.input_ready,
output_ready=args.output_ready,
kernel_done=args.kernel_done,
transport_done=args.transport_done,
)
print(
json.dumps(
Machine(instructions, entries[args.entry], inputs, args.timeout).run(), sort_keys=True
)
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
+651
View File
@@ -0,0 +1,651 @@
"""Frozen APU microcode V1 encoding and APUMC bundle helpers."""
from __future__ import annotations
import binascii
import struct
from dataclasses import dataclass
from enum import IntEnum
from typing import Iterable
APUMC_MAGIC = 0x41504D43
APUMC_ABI = 0x00010000
APUMC_HEADER_BYTES = 64
APUMC_ENTRY_BYTES = 32
APUMC_ENTRY_COUNT = 3
APUMC_INSTRUCTION_OFFSET = 192
APUMC_MAX_INSTRUCTIONS = 2048
APUMC_P3_CAPABILITY_MASK = 0
class InstructionClass(IntEnum):
CONTROL = 0
SCALAR = 1
BITSTREAM = 2
ENTROPY = 3
LOCAL = 4
KERNEL = 5
TRANSPORT = 6
class ControlOpcode(IntEnum):
NOP = 0
END = 1
TRAP = 2
JUMP_FWD = 3
CALL_FWD = 4
RET = 5
LOOP_SETUP = 6
LOOP_BACK = 7
WAIT = 8
class ScalarOpcode(IntEnum):
MOV = 0
MOVI = 1
ADD = 2
SUB = 3
AND = 4
OR = 5
XOR = 6
SHL = 7
SHR = 8
SAR = 9
CMP = 10
MIN = 11
MAX = 12
SAT = 13
PREDICATES = {
"always": 0,
"eq": 1,
"ne": 2,
"slt": 3,
"sge": 4,
"ult": 5,
"uge": 6,
"input_exhausted": 7,
"input_ready": 8,
"output_ready": 9,
"kernel_done": 10,
"transport_done": 11,
}
CONTROL_OPCODES = {opcode.name.lower(): int(opcode) for opcode in ControlOpcode}
SCALAR_OPCODES = {opcode.name.lower(): int(opcode) for opcode in ScalarOpcode}
DEFERRED_OPCODES = {
"bitstream": ("refill", "peek", "get", "skip", "align", "frame_sync", "crc8", "crc16"),
"entropy": ("huff_symbol", "huff_pair", "huff_quad", "unary", "rice4", "rice5", "sign_restore"),
"local": ("ld32", "st32", "table8", "table16", "table32", "fifo_pop", "fifo_push"),
"kernel": (
"requant",
"stereo",
"imdct6",
"imdct18",
"dct32_poly",
"fixed",
"lpc",
"decorrelate",
"resample",
"pcm_pack",
),
"transport": (
"input_refill",
"output_commit",
"output_stream",
"dma_wait",
"frame_commit",
"job_result",
"event",
),
}
WAIT_SOURCES = ("dma", "kernel", "input_fifo", "output_fifo", "tx_stream", "ring_writeback")
PRIMITIVES = (
"bitstream",
"crc",
"huffman",
"rice",
"local_memory",
"local_fifo",
"requantize",
"stereo",
"imdct6",
"imdct18",
"dct32_poly",
"fixed",
"lpc",
"decorrelate",
"resampler",
"pcm_pack",
"dma_input",
"dma_output",
"stream_output",
"frame_commit",
"event",
)
TRAP_REASONS = (
"reserved",
"illegal",
"pc_range",
"call_stack",
"loop",
"local_range",
"unavailable",
"watchdog",
"retired_budget",
"engine",
"explicit",
)
def abi_manifest() -> dict[str, object]:
"""Return the canonical P3 ABI input consumed by generated tool reports."""
return {
"apumc": {
"magic": APUMC_MAGIC,
"abi": APUMC_ABI,
"header_bytes": APUMC_HEADER_BYTES,
"entry_bytes": APUMC_ENTRY_BYTES,
"entry_count": APUMC_ENTRY_COUNT,
"max_instructions": APUMC_MAX_INSTRUCTIONS,
},
"classes": {item.name.lower(): int(item) for item in InstructionClass},
"control_opcodes": CONTROL_OPCODES,
"scalar_opcodes": SCALAR_OPCODES,
"deferred_opcodes": {
name: {opcode: number for number, opcode in enumerate(opcodes)}
for name, opcodes in DEFERRED_OPCODES.items()
},
"predicates": PREDICATES,
"wait_sources": {name: number for number, name in enumerate(WAIT_SOURCES)},
"primitives": {name: number for number, name in enumerate(PRIMITIVES)},
"trap_reasons": {name: number for number, name in enumerate(TRAP_REASONS)},
"instruction_fields": {
"immediate": 0,
"aux": 32,
"src1": 40,
"src0": 44,
"dst": 48,
"predicate": 52,
"opcode": 56,
"class": 60,
},
"entry_words": {
"format_pc": 0,
"range": 1,
"scratch_base": 2,
"scratch_bytes": 3,
"max_loop": 4,
"max_retired": 5,
"primitives": 6,
"table": 7,
},
"p3_implemented_primitive_mask": APUMC_P3_CAPABILITY_MASK,
}
@dataclass(frozen=True)
class Instruction:
instruction_class: int
opcode: int
predicate: int = 0
dst: int = 0
src0: int = 0
src1: int = 0
aux: int = 0
immediate: int = 0
def encode(self) -> int:
fields = (
(self.instruction_class, 4, 60),
(self.opcode, 4, 56),
(self.predicate, 4, 52),
(self.dst, 4, 48),
(self.src0, 4, 44),
(self.src1, 4, 40),
(self.aux, 8, 32),
(self.immediate, 32, 0),
)
word = 0
for value, width, shift in fields:
if value < 0 or value >= (1 << width):
raise ValueError(f"field value {value} does not fit {width} bits")
word |= value << shift
return word
@classmethod
def decode(cls, word: int) -> Instruction:
if word < 0 or word >= (1 << 64):
raise ValueError("instruction must fit 64 bits")
return cls(
instruction_class=(word >> 60) & 0xF,
opcode=(word >> 56) & 0xF,
predicate=(word >> 52) & 0xF,
dst=(word >> 48) & 0xF,
src0=(word >> 44) & 0xF,
src1=(word >> 40) & 0xF,
aux=(word >> 32) & 0xFF,
immediate=word & 0xFFFFFFFF,
)
@dataclass(frozen=True)
class Entry:
format_id: int
entry_pc: int
first_pc: int
last_pc: int
max_loop_count: int
max_retired: int
scratch_base: int = 0
scratch_bytes: int = 0
primitive_mask: int = 0
table_offset: int = 0
table_bytes: int = 0
def words(self) -> tuple[int, ...]:
return (
self.format_id | (self.entry_pc << 4),
self.first_pc | (self.last_pc << 16),
self.scratch_base,
self.scratch_bytes,
self.max_loop_count,
self.max_retired,
self.primitive_mask,
self.table_offset | (self.table_bytes << 16),
)
def crc32_iso_hdlc(payload: bytes) -> int:
"""Return the reflected ISO-HDLC CRC used by APUMC."""
return binascii.crc32(payload) & 0xFFFFFFFF
def trap_detail(reason: int, pc: int, instruction: Instruction) -> int:
return (
(reason & 0xFF)
| ((pc & 0x7FF) << 8)
| ((instruction.instruction_class & 0xF) << 19)
| ((instruction.opcode & 0xF) << 23)
| ((instruction.aux & 0x1F) << 27)
)
def _require_zero(instruction: Instruction, names: Iterable[str]) -> None:
for name in names:
if getattr(instruction, name) != 0:
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."""
if instruction.predicate > 11:
raise ValueError("predicate is reserved")
if instruction.instruction_class == InstructionClass.CONTROL:
if instruction.opcode > ControlOpcode.WAIT:
raise ValueError("control opcode is unallocated")
opcode = ControlOpcode(instruction.opcode)
if opcode in (ControlOpcode.NOP, ControlOpcode.END):
_require_zero(instruction, ("dst", "src0", "src1", "aux", "immediate"))
elif opcode == ControlOpcode.TRAP:
_require_zero(instruction, ("dst", "src0", "src1", "aux"))
elif opcode in (ControlOpcode.JUMP_FWD, ControlOpcode.CALL_FWD):
_require_zero(instruction, ("dst", "src0", "src1", "aux"))
if not 1 <= instruction.immediate <= 0x7FF:
raise ValueError("forward branch delta must be 1..2047")
elif opcode == ControlOpcode.RET:
_require_zero(instruction, ("dst", "src0", "src1", "aux", "immediate"))
if instruction.predicate != 0:
raise ValueError("RET predicate must be always")
elif opcode == ControlOpcode.LOOP_SETUP:
_require_zero(instruction, ("dst", "src1", "immediate"))
if instruction.predicate != 0 or instruction.aux > 3:
raise ValueError("LOOP_SETUP requires always predicate and slot 0..3")
elif opcode == ControlOpcode.LOOP_BACK:
_require_zero(instruction, ("dst", "src0", "src1"))
if (
instruction.predicate != 0
or instruction.aux > 3
or not 1 <= instruction.immediate <= 0x7FF
):
raise ValueError("LOOP_BACK encoding is invalid")
else:
_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")
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.opcode > ScalarOpcode.SAT:
raise ValueError("scalar opcode is unallocated")
opcode = ScalarOpcode(instruction.opcode)
if opcode == ScalarOpcode.MOV:
_require_zero(instruction, ("src1", "aux", "immediate"))
elif opcode == ScalarOpcode.MOVI:
_require_zero(instruction, ("src0", "src1", "aux"))
elif opcode in (
ScalarOpcode.ADD,
ScalarOpcode.SUB,
ScalarOpcode.AND,
ScalarOpcode.OR,
ScalarOpcode.XOR,
ScalarOpcode.SHL,
ScalarOpcode.SHR,
ScalarOpcode.SAR,
):
_require_zero(instruction, ("aux", "immediate"))
elif opcode == ScalarOpcode.CMP:
_require_zero(instruction, ("dst", "aux", "immediate"))
elif opcode in (ScalarOpcode.MIN, ScalarOpcode.MAX):
_require_zero(instruction, ("immediate",))
if instruction.aux > 1:
raise ValueError("MIN/MAX aux must select unsigned or signed")
elif opcode == ScalarOpcode.SAT:
_require_zero(instruction, ("src1", "immediate"))
if instruction.aux & 0xC0 or (instruction.aux & 0x1F) not in (0, 8, 16, 24):
raise ValueError("SAT width/sign encoding is invalid")
def validate_entry(entry: Entry, instruction_count: int) -> 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):
raise ValueError("entry PC range is invalid")
if not 1 <= entry.max_loop_count <= 0xFFFF:
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(
(
entry.scratch_base,
entry.scratch_bytes,
entry.primitive_mask,
entry.table_offset,
entry.table_bytes,
)
):
raise ValueError("P3 entries cannot use scratch, table, or primitives")
def control_flow_report(instructions: list[Instruction], entry: Entry) -> dict[str, object]:
"""Prove bounded V1 control flow and return its deterministic report."""
loop_pairs: dict[int, int] = {}
active_setup: dict[int, int] = {}
intervals: list[tuple[int, int]] = []
for pc in range(entry.first_pc, entry.last_pc + 1):
instruction = instructions[pc]
if instruction.instruction_class != InstructionClass.CONTROL:
continue
opcode = ControlOpcode(instruction.opcode)
if opcode in (ControlOpcode.JUMP_FWD, ControlOpcode.CALL_FWD):
target = pc + 1 + instruction.immediate
if 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
if slot in active_setup:
raise ValueError(f"entry {entry.format_id} reuses active loop slot {slot}")
active_setup[slot] = pc
elif opcode == ControlOpcode.LOOP_BACK:
slot = instruction.aux & 3
if slot not in active_setup:
raise ValueError(f"entry {entry.format_id} loop at PC {pc} has no setup")
setup = active_setup.pop(slot)
if pc + 1 - instruction.immediate != setup + 1:
raise ValueError(f"entry {entry.format_id} loop at PC {pc} has wrong target")
loop_pairs[pc] = setup + 1
intervals.append((setup + 1, pc))
if active_setup:
raise ValueError(f"entry {entry.format_id} has an unterminated loop setup")
for first_index, first in enumerate(intervals):
for second in intervals[first_index + 1 :]:
crossing = first[0] < second[0] <= first[1] < second[1]
reverse_crossing = second[0] < first[0] <= second[1] < first[1]
if crossing or reverse_crossing:
raise ValueError(f"entry {entry.format_id} has crossing loops")
State = tuple[int, tuple[int, ...], tuple[int | None, ...]]
pending: list[State] = [(entry.entry_pc, (), (None, None, None, None))]
visited: set[State] = set()
edges: set[tuple[int, int, str]] = set()
terminals: set[int] = set()
maximum_call_depth = 0
maximum_pending_states = 1
while pending:
maximum_pending_states = max(maximum_pending_states, len(pending))
pc, returns, loops = pending.pop()
state = (pc, returns, loops)
if state in visited:
continue
visited.add(state)
if pc < entry.first_pc or pc > entry.last_pc:
raise ValueError(f"entry {entry.format_id} can fall outside its program range")
instruction = instructions[pc]
successors: list[State] = []
predicate_may_be_false = instruction.predicate != 0
if predicate_may_be_false:
successors.append((pc + 1, returns, loops))
edges.add((pc, pc + 1, "predicate_false"))
if instruction.instruction_class != InstructionClass.CONTROL:
successors.append((pc + 1, returns, loops))
edges.add((pc, pc + 1, "fallthrough"))
else:
opcode = ControlOpcode(instruction.opcode)
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"))
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))
maximum_call_depth = max(maximum_call_depth, len(returns) + 1)
edges.add((pc, target, "call_true"))
elif opcode == ControlOpcode.RET:
if not returns:
raise ValueError(f"entry {entry.format_id} can return with an empty stack")
successors.append((returns[-1], returns[:-1], loops))
edges.add((pc, returns[-1], "return"))
elif opcode == ControlOpcode.LOOP_SETUP:
slot = instruction.aux & 3
if loops[slot] is not None:
raise ValueError(f"entry {entry.format_id} can reuse active loop slot {slot}")
updated = list(loops)
updated[slot] = pc + 1
successors.append((pc + 1, returns, tuple(updated)))
edges.add((pc, pc + 1, "loop_setup"))
elif opcode == ControlOpcode.LOOP_BACK:
slot = instruction.aux & 3
if loops[slot] != loop_pairs.get(pc):
raise ValueError(f"entry {entry.format_id} has an invalid runtime loop path")
successors.append((loop_pairs[pc], returns, loops))
edges.add((pc, loop_pairs[pc], "loop_repeat"))
updated = list(loops)
updated[slot] = None
successors.append((pc + 1, returns, tuple(updated)))
edges.add((pc, pc + 1, "loop_exit"))
elif opcode == ControlOpcode.NOP:
successors.append((pc + 1, returns, loops))
edges.add((pc, pc + 1, "fallthrough"))
else:
raise ValueError("WAIT requires a primitive unavailable in P3")
pending.extend(successors)
if len(visited) > APUMC_MAX_INSTRUCTIONS * 64:
raise ValueError(f"entry {entry.format_id} control-flow proof exceeded its bound")
if not terminals:
raise ValueError(f"entry {entry.format_id} has no reachable END or TRAP")
return {
"entry": entry.format_id,
"entry_pc": entry.entry_pc,
"first_pc": entry.first_pc,
"last_pc": entry.last_pc,
"reachable_states": len(visited),
"maximum_call_depth": maximum_call_depth,
"maximum_pending_states": maximum_pending_states,
"terminals": sorted(terminals),
"loops": [
{"setup_pc": first - 1, "first_pc": first, "back_pc": last}
for first, last in sorted(intervals)
],
"edges": [
{"source": source, "target": target, "kind": kind}
for source, target, kind in sorted(edges)
],
"proof": "passed",
}
def validate_control_flow(instructions: list[Instruction], entry: Entry) -> None:
"""Prove bounded V1 control flow for one entry descriptor."""
control_flow_report(instructions, entry)
def build_apumc(instructions: list[Instruction], entries: list[Entry], build_id: int = 0) -> bytes:
if not 1 <= len(instructions) <= APUMC_MAX_INSTRUCTIONS:
raise ValueError("instruction count must be 1..2048")
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))
for instruction in instructions:
validate_p3_instruction(instruction)
for entry in entries:
validate_control_flow(instructions, entry)
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
total_bytes = APUMC_HEADER_BYTES + len(payload)
crc = crc32_iso_hdlc(payload)
header = struct.pack(
"<16I",
APUMC_MAGIC,
APUMC_ABI,
total_bytes,
len(instructions),
APUMC_INSTRUCTION_OFFSET,
0,
0,
APUMC_HEADER_BYTES,
APUMC_ENTRY_COUNT,
APUMC_P3_CAPABILITY_MASK,
0,
crc,
build_id & 0xFFFFFFFF,
(build_id >> 32) & 0xFFFFFFFF,
0,
0,
)
return header + payload
def parse_apumc(bundle: bytes) -> tuple[list[Instruction], list[Entry], tuple[int, ...]]:
if len(bundle) < APUMC_HEADER_BYTES:
raise ValueError("bundle is smaller than the APUMC header")
header = struct.unpack_from("<16I", bundle)
if (
header[0] != APUMC_MAGIC
or header[1] != APUMC_ABI
or header[2] != len(bundle)
or len(bundle) < 160
or header[14] != 0
or header[15] != 0
):
raise ValueError("APUMC header is invalid")
if header[8] != APUMC_ENTRY_COUNT:
raise ValueError("APUMC V1 requires exactly three entries")
instruction_count = header[3]
instruction_offset = header[4]
table_offset = header[5]
table_bytes = header[6]
entry_offset = header[7]
instruction_end = instruction_offset + instruction_count * 8
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 (
instruction_offset < APUMC_HEADER_BYTES
or instruction_offset % 64 != 0
or instruction_end > len(bundle)
or entry_offset < APUMC_HEADER_BYTES
or entry_offset % APUMC_ENTRY_BYTES != 0
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
):
raise ValueError("APUMC range or P3 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 (
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")
entries.append(
Entry(
format_id=words[0] & 0xF,
entry_pc=(words[0] >> 4) & 0x7FF,
first_pc=words[1] & 0x7FF,
last_pc=(words[1] >> 16) & 0x7FF,
scratch_base=words[2] & 0x1FFFF,
scratch_bytes=words[3] & 0x1FFFF,
max_loop_count=words[4] & 0xFFFF,
max_retired=words[5] & 0xFFFFFF,
primitive_mask=words[6],
table_offset=words[7] & 0xFFFF,
table_bytes=(words[7] >> 16) & 0xFFFF,
)
)
instructions = [
Instruction.decode(struct.unpack_from("<Q", bundle, instruction_offset + pc * 8)[0])
for pc in range(instruction_count)
]
occupied = bytearray(len(bundle))
occupied[:APUMC_HEADER_BYTES] = bytes([1]) * APUMC_HEADER_BYTES
occupied[entry_offset:entry_end] = bytes([1]) * (entry_end - entry_offset)
occupied[instruction_offset:instruction_end] = bytes([1]) * (
instruction_end - instruction_offset
)
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)
for instruction in instructions:
validate_p3_instruction(instruction)
for entry in entries:
validate_control_flow(instructions, entry)
return instructions, entries, header
+238
View File
@@ -0,0 +1,238 @@
"""Assemble and statically verify frozen APU-P3 microcode bundles."""
from __future__ import annotations
import argparse
import json
import re
from dataclasses import dataclass
from pathlib import Path
from apu_isa import (
CONTROL_OPCODES,
PREDICATES,
SCALAR_OPCODES,
ControlOpcode,
Entry,
Instruction,
InstructionClass,
ScalarOpcode,
abi_manifest,
build_apumc,
control_flow_report,
crc32_iso_hdlc,
)
@dataclass(frozen=True)
class Assembly:
bundle: bytes
symbols: dict[str, int]
instructions: list[Instruction]
entries: list[Entry]
build_id: int
def _number(token: str) -> int:
return int(token, 0)
def _register(token: str) -> int:
match = re.fullmatch(r"[rR](\d+)", token)
if match is None or int(match.group(1)) > 15:
raise ValueError(f"invalid APU register {token}")
return int(match.group(1))
def _tokens(line: str) -> list[str]:
return [token for token in re.split(r"[\s,]+", line.strip()) if token]
def _predicate(tokens: list[str]) -> tuple[list[str], int]:
if tokens and tokens[-1].startswith("if="):
name = tokens[-1][3:].lower()
if name not in PREDICATES:
raise ValueError(f"unknown predicate {name}")
return tokens[:-1], PREDICATES[name]
return tokens, 0
def _encode_instruction(tokens: list[str], pc: int, labels: dict[str, int]) -> Instruction:
tokens, predicate = _predicate(tokens)
mnemonic = tokens[0].lower()
args = tokens[1:]
if mnemonic in CONTROL_OPCODES:
opcode = ControlOpcode(CONTROL_OPCODES[mnemonic])
values = {"predicate": predicate}
if opcode in (ControlOpcode.NOP, ControlOpcode.END, ControlOpcode.RET):
if args:
raise ValueError(f"{mnemonic} takes no operands")
elif opcode == ControlOpcode.TRAP:
if len(args) != 1:
raise ValueError("trap takes one immediate")
values["immediate"] = _number(args[0])
elif opcode in (ControlOpcode.JUMP_FWD, ControlOpcode.CALL_FWD):
if len(args) != 1:
raise ValueError(f"{mnemonic} takes one label")
values["immediate"] = labels[args[0]] - pc - 1
elif opcode == ControlOpcode.LOOP_SETUP:
if len(args) != 2:
raise ValueError("loop_setup takes a slot and count register")
values["aux"] = _number(args[0])
values["src0"] = _register(args[1])
elif opcode == ControlOpcode.LOOP_BACK:
if len(args) != 2:
raise ValueError("loop_back takes a slot and label")
values["aux"] = _number(args[0])
values["immediate"] = pc + 1 - labels[args[1]]
elif opcode == ControlOpcode.WAIT:
if len(args) != 1:
raise ValueError("wait takes one source")
values["aux"] = _number(args[0])
return Instruction(InstructionClass.CONTROL, opcode, **values)
if mnemonic not in SCALAR_OPCODES:
raise ValueError(f"unknown mnemonic {mnemonic}")
opcode = ScalarOpcode(SCALAR_OPCODES[mnemonic])
values = {"predicate": predicate}
if opcode == ScalarOpcode.MOV:
if len(args) != 2:
raise ValueError("mov takes two registers")
values.update(dst=_register(args[0]), src0=_register(args[1]))
elif opcode == ScalarOpcode.MOVI:
if len(args) != 2:
raise ValueError("movi takes one register and one immediate")
values.update(dst=_register(args[0]), immediate=_number(args[1]) & 0xFFFFFFFF)
elif opcode == ScalarOpcode.CMP:
if len(args) != 2:
raise ValueError("cmp takes two registers")
values.update(src0=_register(args[0]), src1=_register(args[1]))
elif opcode in (ScalarOpcode.MIN, ScalarOpcode.MAX):
if len(args) not in (3, 4):
raise ValueError(f"{mnemonic} takes three registers and an optional signed selector")
values.update(
dst=_register(args[0]),
src0=_register(args[1]),
src1=_register(args[2]),
aux=_number(args[3]) if len(args) == 4 else 0,
)
elif opcode == ScalarOpcode.SAT:
if len(args) != 3:
raise ValueError("sat takes a destination, source, and width/sign encoding")
values.update(dst=_register(args[0]), src0=_register(args[1]), aux=_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.SCALAR, opcode, **values)
def assemble(source: str) -> Assembly:
lines = []
labels: dict[str, int] = {}
directives: list[list[str]] = []
pc = 0
for line_number, raw_line in enumerate(source.splitlines(), start=1):
line = raw_line.partition("#")[0].strip()
if not line:
continue
if line.endswith(":"):
label = line[:-1].strip()
if not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", label) or label in labels:
raise ValueError(f"line {line_number}: invalid or duplicate label {label}")
labels[label] = pc
elif line.startswith("."):
directives.append(_tokens(line))
else:
lines.append((line_number, _tokens(line)))
pc += 1
build_id = 0
entry_directives = []
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:
entry_directives.append(directive[1:])
else:
raise ValueError(f"invalid assembler directive {' '.join(directive)}")
if len(entry_directives) != 3:
raise ValueError("exactly three .entry directives are required")
instructions = []
for line_number, tokens in lines:
try:
instructions.append(_encode_instruction(tokens, len(instructions), labels))
except (IndexError, KeyError, ValueError) as error:
raise ValueError(f"line {line_number}: {error}") from error
entries = []
for values in entry_directives:
try:
entries.append(
Entry(
format_id=_number(values[0]),
entry_pc=labels[values[1]],
first_pc=labels[values[2]],
last_pc=labels[values[3]],
max_loop_count=_number(values[4]),
max_retired=_number(values[5]),
)
)
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)
def _artifact_data(assembly: Assembly) -> dict[str, object]:
return {
"abi": "1.0",
"build_id": f"0x{assembly.build_id:016x}",
"bundle_bytes": len(assembly.bundle),
"instruction_count": len(assembly.instructions),
"payload_crc32": f"0x{crc32_iso_hdlc(assembly.bundle[64:]):08x}",
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("source", type=Path)
parser.add_argument("-o", "--output", type=Path, required=True)
parser.add_argument("--symbols", type=Path)
parser.add_argument("--cfg-report", type=Path)
parser.add_argument("--primitive-manifest", type=Path)
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"))
args.output.write_bytes(assembly.bundle)
artifacts = {
args.symbols: assembly.symbols,
args.cfg_report: {
**_artifact_data(assembly),
"entries": [entry.__dict__ for entry in assembly.entries],
"control_flow": [
control_flow_report(assembly.instructions, entry) for entry in assembly.entries
],
},
args.primitive_manifest: {
"implemented_mask": 0,
"required_mask": 0,
"entry_masks": [entry.primitive_mask for entry in assembly.entries],
},
args.trace_input: {
"entries": [entry.entry_pc for entry in assembly.entries],
"instructions": [f"0x{item.encode():016x}" for item in assembly.instructions],
},
args.abi_input_manifest: {**_artifact_data(assembly), "canonical_abi": abi_manifest()},
}
for path, data in artifacts.items():
if path is not None:
path.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n", encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+235
View File
@@ -0,0 +1,235 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`include "apu_define.svh"
module apu_p3_integration_tb;
localparam logic [31:0] ApuBase = 32'h1001_3000;
localparam logic [31:0] ImageBase = 32'h3000_0000;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic [1:0] owner_i = 2'd0;
logic quiesce_i = 1'b0;
logic idle_o, irq_o;
logic [31:0] image [0:4095];
logic read_active_q;
logic [31:0] read_addr_q;
logic [7:0] read_len_q, read_beat_q;
logic [31:0] s_value;
string image_path;
apb4_if apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) axi4 (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_stream_if #(
.DATA_WIDTH(32)
) dma_tx_axis (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_stream_if #(
.DATA_WIDTH(32)
) dma_rx_axis (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_stream_if #(
.DATA_WIDTH(32)
) i2s_tx_axis (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_stream_if #(
.DATA_WIDTH(32)
) i2s_rx_axis (
.aclk (clk_i),
.aresetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
assign axi4.arready = !read_active_q;
assign axi4.rid = 1'b0;
assign axi4.rdata = image[(read_addr_q-ImageBase)>>2];
assign axi4.rresp = 2'd0;
assign axi4.rlast = read_beat_q == read_len_q;
assign axi4.ruser = 1'b0;
assign axi4.rvalid = read_active_q;
assign axi4.awready = 1'b0;
assign axi4.wready = 1'b0;
assign axi4.bid = 1'b0;
assign axi4.bresp = 2'd0;
assign axi4.buser = 1'b0;
assign axi4.bvalid = 1'b0;
assign dma_tx_axis.tdata = 32'd0;
assign dma_tx_axis.tkeep = 4'hf;
assign dma_tx_axis.tstrb = 4'hf;
assign dma_tx_axis.tlast = 1'b0;
assign dma_tx_axis.tid = '0;
assign dma_tx_axis.tdest = '0;
assign dma_tx_axis.tuser = '0;
assign dma_tx_axis.tvalid = 1'b0;
assign dma_rx_axis.tready = 1'b1;
assign i2s_tx_axis.tready = 1'b1;
assign i2s_rx_axis.tdata = 32'd0;
assign i2s_rx_axis.tkeep = 4'hf;
assign i2s_rx_axis.tstrb = 4'hf;
assign i2s_rx_axis.tlast = 1'b0;
assign i2s_rx_axis.tid = '0;
assign i2s_rx_axis.tdest = '0;
assign i2s_rx_axis.tuser = '0;
assign i2s_rx_axis.tvalid = 1'b0;
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
read_active_q <= 1'b0;
read_addr_q <= 32'd0;
read_len_q <= 8'd0;
read_beat_q <= 8'd0;
end else begin
if (axi4.arvalid && axi4.arready) begin
read_active_q <= 1'b1;
read_addr_q <= axi4.araddr;
read_len_q <= axi4.arlen;
read_beat_q <= 8'd0;
end
if (axi4.rvalid && axi4.rready) begin
if (axi4.rlast) begin
read_active_q <= 1'b0;
end else begin
read_addr_q <= read_addr_q + 32'd4;
read_beat_q <= read_beat_q + 1'b1;
end
end
end
end
apb4_apu u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.owner_i (owner_i),
.owner_lock_i (1'b0),
.quiesce_i (quiesce_i),
.resource_reset_i (1'b0),
.bridge_epoch_i (8'd0),
.i2s_tx_underrun_i(1'b0),
.i2s_rx_overrun_i (1'b0),
.apb4 (apb4),
.axi4 (axi4),
.dma_tx_axis (dma_tx_axis),
.dma_rx_axis (dma_rx_axis),
.i2s_tx_axis (i2s_tx_axis),
.i2s_rx_axis (i2s_rx_axis),
.idle_o (idle_o),
.irq_o (irq_o)
);
task automatic apb_write(input logic [11:0] offset_i, input logic [31:0] value_i,
input logic expected_err_i);
begin
@(negedge clk_i);
apb4.paddr = ApuBase + offset_i;
apb4.pprot = 3'd0;
apb4.psel = 1'b1;
apb4.penable = 1'b0;
apb4.pwrite = 1'b1;
apb4.pwdata = value_i;
apb4.pstrb = 4'hf;
@(negedge clk_i);
apb4.penable = 1'b1;
while (!apb4.pready) @(negedge clk_i);
if (apb4.pslverr != expected_err_i) $fatal(1, "APU-P3 APB write response mismatch");
@(negedge clk_i);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
end
endtask
task automatic apb_read(input logic [11:0] offset_i, output logic [31:0] value_o);
begin
@(negedge clk_i);
apb4.paddr = ApuBase + offset_i;
apb4.pprot = 3'd0;
apb4.psel = 1'b1;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pwdata = 32'd0;
apb4.pstrb = 4'd0;
@(negedge clk_i);
apb4.penable = 1'b1;
while (!apb4.pready) @(negedge clk_i);
if (apb4.pslverr) $fatal(1, "APU-P3 APB read failed");
value_o = apb4.prdata;
@(negedge clk_i);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
end
endtask
initial begin
if (!$value$plusargs("IMAGE=%s", image_path)) $fatal(1, "IMAGE plusarg missing");
$readmemh(image_path, image);
apb4.paddr = 32'd0;
apb4.pprot = 3'd0;
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pwdata = 32'd0;
apb4.pstrb = 4'd0;
repeat (4) @(posedge clk_i);
rst_n_i = 1'b1;
repeat (2) @(posedge clk_i);
apb_write(`APB4_APU__READ_BASE, ImageBase, 1'b0);
apb_write(`APB4_APU__READ_LIMIT, ImageBase + image[2] - 1'b1, 1'b0);
apb_write(`APB4_APU__MC_IMAGE_ADDRESS, ImageBase, 1'b0);
apb_write(`APB4_APU__MC_IMAGE_SIZE, image[2], 1'b0);
apb_write(`APB4_APU__MC_EXPECTED_CRC, image[11], 1'b0);
quiesce_i = 1'b1;
apb_write(`APB4_APU__COMMAND, 32'h0000_0010, 1'b0);
for (int unsigned poll = 0; poll < 10000; poll++) begin
apb_read(`APB4_APU__MC_STATUS, s_value);
if (s_value[1]) break;
if (!s_value[0]) $fatal(1, "APU-P3 integrated load failed status=%h", s_value);
end
if (!s_value[1]) $fatal(1, "APU-P3 integrated load timed out");
apb_read(`APB4_APU__MC_ABI, s_value);
if (s_value != 32'h0001_0000) $fatal(1, "APU-P3 MC_ABI mismatch");
apb_read(`APB4_APU__MC_BUILD_ID_LO, s_value);
if (s_value != 32'h5566_7788) $fatal(1, "APU-P3 build ID low mismatch");
apb_read(`APB4_APU__MC_BUILD_ID_HI, s_value);
if (s_value != 32'h1122_3344) $fatal(1, "APU-P3 build ID high mismatch");
apb_read(`APB4_APU__MC_LOCK, s_value);
if (s_value != 32'd1) $fatal(1, "APU-P3 lock mismatch");
apb_read(`APB4_APU__MC_LOAD_COUNT, s_value);
if (s_value != 32'd1) $fatal(1, "APU-P3 load count mismatch");
apb_read(`APB4_APU__IRQ_STATE, s_value);
if (!s_value[`APB4_APU__IRQ_MICROCODE_LOAD_DONE]) $fatal(1, "APU-P3 load IRQ missing");
apb_write(`APB4_APU__COMMAND, 32'h0000_0010, 1'b1);
apb_write(`APB4_APU__STREAM_ROUTE, 32'd1, 1'b1);
$display("APU-P3 APB, DMA, loader integration passed");
$finish;
end
initial begin
repeat (30000) @(posedge clk_i);
$fatal(1, "APU-P3 integration timed out");
end
logic s_unused;
assign s_unused = irq_o ^ ^axi4.awaddr ^ ^axi4.wdata ^ ^axi4.wstrb ^ axi4.wlast;
endmodule
+628
View File
@@ -0,0 +1,628 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
`include "apu_define.svh"
module apu_p3_microcode_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;
logic [31:0] dma_request_addr, dma_request_bytes;
logic [31:0] dma_data;
logic [ 3:0] dma_keep;
logic dma_last, dma_valid, dma_ready, dma_done, dma_error;
logic store_active, store_read, store_write, store_valid;
logic [10:0] store_addr;
logic [63:0] store_write_data, store_read_data;
logic [7:0] mc_status;
logic [31:0] mc_abi, mc_actual_crc, mc_load_count;
logic [63:0] mc_build_id;
logic mc_lock, mc_load_done, mc_abort_done, mc_fault_valid, mc_idle;
logic [5:0] mc_fault_code;
logic [3:0] mc_fault_stage;
logic [1:0] mc_fault_resp;
logic [31:0] mc_fault_addr, mc_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 seq_launch, seq_stall, seq_fetch, seq_fetch_valid, seq_trapped, seq_trap_event;
logic seq_abort_done, seq_fault_valid, seq_idle;
logic [10:0] seq_fetch_addr;
logic [63:0] seq_fetch_data, seq_perf_retired;
logic [31:0] seq_status, seq_retired, seq_timeout;
logic [15:0][31:0] seq_gpr;
logic [ 5:0] seq_fault_code;
logic [ 3:0] seq_fault_stage;
logic [ 1:0] seq_fault_resp;
logic [ 7:0] seq_fault_index;
logic [31:0] seq_fault_addr, seq_fault_detail;
logic [31:0] image [0:4095];
logic transfer_active_q;
logic [11:0] transfer_word_q;
logic [11:0] transfer_beat_q;
logic [31:0] transfer_bytes_q;
logic [31:0] saved_word;
logic [ 3:0] dma_request_count_q;
logic [11:0] store_write_count_q;
string image_path, invalid_cf_image_path, invalid_loop_image_path, diagnostic_image_path;
string lexical_path_image_path, deep_path_image_path;
always #5 clk_i = ~clk_i;
always @(posedge clk_i) begin
if (mc_status[0] && (mc_status[7:1] != 7'd0)) begin
$fatal(1, "APU-P3 loader exposed non-atomic status");
end
end
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;
dma_done <= 1'b0;
dma_request_count_q <= 4'd0;
store_write_count_q <= 12'd0;
end else begin
dma_done <= 1'b0;
if (dma_request_valid && dma_request_ready) begin
dma_request_count_q <= dma_request_count_q + 1'b1;
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;
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 (abort_i || resource_reset_i) begin
transfer_active_q <= 1'b0;
dma_done <= 1'b1;
end
if (store_write) begin
if (dma_request_count_q < 4'd3) begin
$fatal(1, "APU-P3 control-store write preceded descriptor/range admission");
end
store_write_count_q <= store_write_count_q + 1'b1;
end
end
end
apu_microcode_loader 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 (dma_error),
.dma_err_code_i (6'd15),
.dma_err_stage_i (4'd3),
.dma_err_resp_i (2'd2),
.dma_err_addr_i (image_addr_i),
.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),
.stat_o (mc_status),
.abi_o (mc_abi),
.build_id_o (mc_build_id),
.lock_o (mc_lock),
.actual_crc_o (mc_actual_crc),
.load_count_o (mc_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),
.load_done_o (mc_load_done),
.abort_done_o (mc_abort_done),
.fault_valid_o (mc_fault_valid),
.fault_code_o (mc_fault_code),
.fault_stage_o (mc_fault_stage),
.fault_resp_o (mc_fault_resp),
.fault_addr_o (mc_fault_addr),
.fault_detail_o (mc_fault_detail),
.idle_o (mc_idle)
);
apu_control_store u_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 (mc_status[`APB4_APU__MC_STATUS_VALID]),
.fetch_i (seq_fetch),
.fetch_addr_i (seq_fetch_addr),
.fetch_data_o (seq_fetch_data),
.fetch_valid_o (seq_fetch_valid)
);
apu_codec_sequencer 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 (seq_launch),
.launch_entry_i (2'd0),
.image_valid_i (mc_status[`APB4_APU__MC_STATUS_VALID]),
.timeout_i (seq_timeout),
.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),
.input_exhausted_i (1'b1),
.input_ready_i (1'b0),
.output_ready_i (1'b1),
.kernel_done_i (1'b0),
.transport_idle_success_i(1'b1),
.stall_i (seq_stall),
.cause_valid_i (1'b0),
.cause_code_i (6'd0),
.cause_stage_i (4'd0),
.cause_resp_i (2'd0),
.cause_index_i (8'd0),
.cause_addr_i (32'd0),
.cause_detail_i (32'd0),
.fetch_o (seq_fetch),
.fetch_addr_o (seq_fetch_addr),
.fetch_data_i (seq_fetch_data),
.fetch_valid_i (seq_fetch_valid),
.stat_o (seq_status),
.retired_o (seq_retired),
.gpr_o (seq_gpr),
.trapped_o (seq_trapped),
.trap_event_o (seq_trap_event),
.abort_done_o (seq_abort_done),
.fault_valid_o (seq_fault_valid),
.fault_code_o (seq_fault_code),
.fault_stage_o (seq_fault_stage),
.fault_resp_o (seq_fault_resp),
.fault_index_o (seq_fault_index),
.fault_addr_o (seq_fault_addr),
.fault_detail_o (seq_fault_detail),
.perf_retired_o (seq_perf_retired),
.idle_o (seq_idle)
);
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 start_load;
begin
@(negedge clk_i);
start_i = 1'b1;
@(negedge clk_i);
start_i = 1'b0;
end
endtask
task automatic wait_loader_idle;
begin
for (int unsigned cycle = 0; cycle < 10000; cycle++) begin
@(posedge clk_i);
if (mc_idle) return;
end
$display("loader state=%0d entry=%0d pc=%0d paths=%0d stop=%0d/%0d", u_loader.s_state_q,
u_loader.s_scan_entry_q, u_loader.s_scan_pc_q, u_loader.s_path_stack_count_q,
u_loader.s_path_stop_valid_q, u_loader.s_path_stop_pc_q);
$fatal(1, "APU-P3 loader did not become idle");
end
endtask
initial begin
if (!$value$plusargs("IMAGE=%s", image_path)) $fatal(1, "IMAGE plusarg missing");
if (!$value$plusargs("INVALID_CF_IMAGE=%s", invalid_cf_image_path)) begin
$fatal(1, "INVALID_CF_IMAGE plusarg missing");
end
if (!$value$plusargs("INVALID_LOOP_IMAGE=%s", invalid_loop_image_path)) begin
$fatal(1, "INVALID_LOOP_IMAGE plusarg missing");
end
if (!$value$plusargs("DIAGNOSTIC_IMAGE=%s", diagnostic_image_path)) begin
$fatal(1, "DIAGNOSTIC_IMAGE plusarg missing");
end
if (!$value$plusargs("LEXICAL_PATH_IMAGE=%s", lexical_path_image_path)) begin
$fatal(1, "LEXICAL_PATH_IMAGE plusarg missing");
end
if (!$value$plusargs("DEEP_PATH_IMAGE=%s", deep_path_image_path)) begin
$fatal(1, "DEEP_PATH_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;
dma_error = 1'b0;
seq_launch = 1'b0;
seq_stall = 1'b0;
seq_timeout = 32'd16;
image_addr_i = 32'h3000_0000;
image_size_i = image[2];
expected_crc_i = image[11];
hard_reset();
start_load();
wait_loader_idle();
if ((mc_status != 8'h02) || !mc_lock || (mc_abi != 32'h0001_0000) ||
(mc_actual_crc != image[11]) || (mc_load_count != 32'd1) ||
(mc_build_id != 64'h1122_3344_5566_7788)) begin
$display("status=%h lock=%0d abi=%h crc=%h expected=%h count=%h build=%h fault=%0d/%0d/%h/%h",
mc_status, mc_lock, mc_abi, mc_actual_crc, image[11], mc_load_count, mc_build_id,
mc_fault_code, mc_fault_stage, mc_fault_addr, mc_fault_detail);
$fatal(1, "APU-P3 successful publication state mismatch");
end
if ((dma_request_count_q != 4'd3) || (store_write_count_q == 12'd0)) begin
$fatal(1, "APU-P3 staged header/descriptor/payload transfer mismatch");
end
saved_word = u_store.mem[0][31:0];
@(negedge clk_i);
soft_reset_i = 1'b1;
@(negedge clk_i);
soft_reset_i = 1'b0;
@(posedge clk_i);
if (!mc_lock || (mc_status != 8'h02) || (mc_abi != 32'h0001_0000) ||
(u_store.mem[0][31:0] != saved_word)) begin
$fatal(1, "APU-P3 idle soft reset did not preserve the published image");
end
@(negedge clk_i);
seq_launch = 1'b1;
@(negedge clk_i);
seq_launch = 1'b0;
wait (seq_idle);
@(posedge clk_i);
if (seq_trapped || (seq_retired != 32'd36) || (seq_gpr[2] != 32'd9) ||
(seq_gpr[3] != 32'ha5) || (seq_gpr[4] != 32'hffff_fff8) ||
(seq_gpr[5] != 32'hffff_fff8) || (seq_gpr[6] != 32'h66) ||
(seq_gpr[8] != 32'd10) || (seq_gpr[9] != 32'd2) ||
(seq_gpr[10] != 32'hffff_fff8) || (seq_gpr[11] != 32'd5) ||
(seq_gpr[12] != 32'd7) || (seq_gpr[13] != 32'd9) ||
(seq_gpr[14] != 32'd11) || (seq_gpr[15] != 32'd5) ||
(seq_status[18:15] != 4'd1)) begin
$display("seq trapped=%0d retired=%0d r0=%h r1=%h r2=%h r3=%h status=%h", seq_trapped,
seq_retired, seq_gpr[0], seq_gpr[1], seq_gpr[2], seq_gpr[3], seq_status);
$fatal(1, "APU-P3 sequencer execution or END retention mismatch");
end
@(negedge clk_i);
seq_launch = 1'b1;
@(negedge clk_i);
seq_launch = 1'b0;
wait (u_sequencer.s_state_q == 2'd2);
@(negedge clk_i);
abort_i = 1'b1;
@(posedge clk_i);
#1;
abort_i = 1'b0;
if (!seq_abort_done || seq_trapped || (seq_retired != 32'd0) ||
(seq_status[10:0] != 11'd0)) begin
$fatal(1, "APU-P3 sequencer abort retention mismatch");
end
saved_word = u_store.mem[0][31:0];
u_store.mem[0] = 64'h0200_0000_dead_beef;
@(negedge clk_i);
seq_launch = 1'b1;
@(negedge clk_i);
seq_launch = 1'b0;
wait (u_sequencer.s_state_q == 2'd3);
@(negedge clk_i);
abort_i = 1'b1;
@(posedge clk_i);
#1;
if (!seq_trapped || seq_abort_done || !seq_trap_event ||
(seq_fault_detail != 32'hdead_beef) || (seq_retired != 32'd0)) begin
$fatal(1, "APU-P3 explicit trap did not outrank simultaneous abort");
end
@(negedge clk_i);
abort_i = 1'b0;
u_store.mem[0] = {32'h1100_0000, saved_word};
u_store.mem[0] = 64'h0300_0000_0000_07ff;
@(negedge clk_i);
seq_launch = 1'b1;
@(negedge clk_i);
seq_launch = 1'b0;
wait (seq_trap_event);
#1;
if (!seq_trapped || (seq_fault_detail[7:0] != 8'd2) || (seq_retired != 32'd0)) begin
$fatal(1, "APU-P3 branch-overflow trap mismatch");
end
u_store.mem[0] = 64'd0;
seq_timeout = 32'd1;
@(negedge clk_i);
seq_launch = 1'b1;
abort_i = 1'b1;
@(negedge clk_i);
seq_launch = 1'b0;
wait (seq_trap_event);
#1;
abort_i = 1'b0;
if (!seq_trapped || seq_abort_done || (seq_fault_detail[7:0] != 8'd7) ||
(seq_retired != 32'd0) || (seq_status[18:11] != 8'd0)) begin
$fatal(1, "APU-P3 timeout-one fetch watchdog or precedence mismatch");
end
seq_timeout = 32'd16;
@(negedge clk_i);
seq_launch = 1'b1;
@(negedge clk_i);
seq_launch = 1'b0;
wait (u_sequencer.s_state_q == 2'd3);
seq_stall = 1'b1;
wait (seq_trap_event);
#1;
seq_stall = 1'b0;
if (!seq_trapped || (seq_fault_detail[7:0] != 8'd7) || (seq_retired != 32'd0)) begin
$fatal(1, "APU-P3 watchdog trap mismatch");
end
u_store.mem[0] = 64'h1100_0000_0000_0005;
hard_reset();
$readmemh(invalid_cf_image_path, image);
image_size_i = image[2];
expected_crc_i = image[11];
start_load();
wait_loader_idle();
if ((mc_status != 8'h10) || mc_lock || (mc_fault_code != 6'd9) ||
(mc_fault_detail[7:0] != 8'd3) || (mc_fault_addr != image_addr_i + 32'd208)) begin
$fatal(1, "APU-P3 path-sensitive empty-return proof mismatch");
end
hard_reset();
$readmemh(invalid_loop_image_path, image);
image_size_i = image[2];
expected_crc_i = image[11];
start_load();
wait_loader_idle();
if ((mc_status != 8'h10) || mc_lock || (mc_fault_code != 6'd9) ||
(mc_fault_detail[7:0] != 8'd4) || (mc_fault_addr != image_addr_i + 32'd216)) begin
$fatal(1, "APU-P3 path-sensitive skipped-loop-setup proof mismatch");
end
hard_reset();
$readmemh(diagnostic_image_path, image);
image_size_i = image[2];
expected_crc_i = image[11];
start_load();
wait_loader_idle();
if ((mc_status != 8'h10) || mc_lock || (mc_fault_code != 6'd9) ||
(mc_fault_detail[7:0] != 8'd3) || (mc_fault_detail[18:8] != 11'd1) ||
(mc_fault_addr != image_addr_i + 32'd200)) begin
$fatal(1, "APU-P3 path diagnostic did not select the lowest-PC failure");
end
hard_reset();
$readmemh(lexical_path_image_path, image);
image_size_i = image[2];
expected_crc_i = image[11];
start_load();
wait_loader_idle();
if ((mc_status != 8'h10) || mc_lock || (mc_fault_code != 6'd9) ||
(mc_fault_detail[7:0] != 8'd3) || (mc_fault_detail[18:8] != 11'd2) ||
(mc_fault_addr != image_addr_i + 32'd208)) begin
$fatal(1, "APU-P3 lexical/path diagnostic order mismatch");
end
hard_reset();
$readmemh(deep_path_image_path, image);
image_size_i = image[2];
expected_crc_i = image[11];
start_load();
wait_loader_idle();
if ((mc_status != 8'h02) || !mc_lock || (mc_fault_code != 6'd0)) begin
$fatal(1, "APU-P3 valid deep conditional path proof was rejected");
end
hard_reset();
$readmemh(image_path, image);
image_size_i = image[2];
expected_crc_i = image[11];
start_load();
wait_loader_idle();
if ((mc_status != 8'h02) || !mc_lock || (mc_load_count != 32'd1)) begin
$fatal(1, "APU-P3 valid reload after proof rejection failed");
end
@(negedge clk_i);
counter_clear_i = 1'b1;
@(negedge clk_i);
counter_clear_i = 1'b0;
@(posedge clk_i);
if ((mc_load_count != 32'd0) || !mc_lock || (mc_status != 8'h02)) begin
$display("counter clear count=%h lock=%0d status=%h", mc_load_count, mc_lock, mc_status);
$fatal(1, "APU-P3 counter clear did not preserve the published image");
end
hard_reset();
saved_word = image[0];
image[0] = 32'd0;
start_load();
wait_loader_idle();
if ((mc_status != 8'h04) || (mc_fault_code != 6'd9) || (mc_fault_addr != image_addr_i))
$fatal(1, "APU-P3 header diagnostic mismatch");
if ((dma_request_count_q != 4'd1) || (store_write_count_q != 12'd0)) begin
$fatal(1, "APU-P3 invalid header reached payload/control store");
end
image[0] = saved_word;
hard_reset();
saved_word = image[21];
image[21] = 32'd0;
start_load();
wait_loader_idle();
if ((mc_status != 8'h08) || (mc_fault_code != 6'd9) || (mc_actual_crc != 32'd0) ||
(mc_fault_addr != image_addr_i + 32'd84))
$fatal(1, "APU-P3 range diagnostic mismatch");
if (store_write_count_q != 12'd0) begin
$fatal(1, "APU-P3 invalid descriptor mutated the control store");
end
image[21] = saved_word;
hard_reset();
saved_word = image[5];
image[5] = image[2] + 32'd4;
start_load();
wait_loader_idle();
if ((mc_status != 8'h08) || (mc_fault_code != 6'd9) || (mc_actual_crc != 32'd0) ||
(mc_fault_addr != image_addr_i + 32'd20) || (store_write_count_q != 12'd0)) begin
$fatal(1, "APU-P3 zero-byte table offset did not fail as an early range error");
end
image[5] = saved_word;
hard_reset();
saved_word = image[49];
image[49] = image[49] | 32'h0000_1000;
start_load();
wait_loader_idle();
if ((mc_status != 8'h10) || (mc_fault_code != 6'd9) || (mc_actual_crc == 32'd0))
$fatal(1, "APU-P3 control diagnostic mismatch");
image[49] = saved_word;
hard_reset();
saved_word = image[5];
image[5] = 32'h0000_0040;
start_load();
wait_loader_idle();
if ((mc_status != 8'h20) || (mc_actual_crc == 32'd0)) begin
$fatal(1, "APU-P3 table error precedence mismatch");
end
image[5] = saved_word;
hard_reset();
saved_word = image[49];
image[49] = (image[49] & 32'h0fff_ffff) | 32'h2000_0000;
start_load();
wait_loader_idle();
if ((mc_status != 8'h80) || (mc_fault_code != 6'd10)) begin
$fatal(1, "APU-P3 capability diagnostic mismatch");
end
image[49] = saved_word;
hard_reset();
expected_crc_i = image[11] ^ 32'd1;
start_load();
wait_loader_idle();
if ((mc_status != 8'h40) || (mc_fault_detail != mc_actual_crc)) begin
$fatal(1, "APU-P3 CRC diagnostic mismatch");
end
expected_crc_i = image[11];
hard_reset();
start_load();
wait (u_loader.s_state_q == 5'd14);
@(negedge clk_i);
abort_i = 1'b1;
@(posedge clk_i);
#1;
abort_i = 1'b0;
if (!mc_idle || !mc_abort_done || mc_lock || (mc_status != 8'd0)) begin
$fatal(1, "APU-P3 path-stack pop abort mismatch");
end
hard_reset();
start_load();
wait (u_loader.s_state_q == 5'd14);
@(negedge clk_i);
resource_reset_i = 1'b1;
@(posedge clk_i);
#1;
resource_reset_i = 1'b0;
if (!mc_idle || mc_lock || (mc_status != 8'd0) || (mc_fault_code != 6'd21)) begin
$fatal(1, "APU-P3 path-stack pop resource-reset mismatch");
end
hard_reset();
start_load();
repeat (8) @(posedge clk_i);
dma_error = 1'b1;
@(posedge clk_i);
dma_error = 1'b0;
wait_loader_idle();
if ((mc_status != 8'd0) || (mc_actual_crc != 32'd0) ||
(mc_fault_code != 6'd15) || (mc_fault_resp != 2'd2)) begin
$fatal(1, "APU-P3 AXI failure diagnostic mismatch");
end
hard_reset();
start_load();
repeat (8) @(posedge clk_i);
abort_i = 1'b1;
@(posedge clk_i);
abort_i = 1'b0;
wait_loader_idle();
if (mc_lock || (mc_status != 8'd0) || !mc_abort_done) begin
$fatal(1, "APU-P3 abort did not cancel publication");
end
hard_reset();
start_load();
repeat (8) @(posedge clk_i);
resource_reset_i = 1'b1;
@(posedge clk_i);
resource_reset_i = 1'b0;
wait_loader_idle();
if (mc_lock || (mc_status != 8'd0) || (mc_fault_code != 6'd21)) begin
$fatal(1, "APU-P3 resource reset did not cancel publication");
end
$display("APU-P3 loader, control store, and sequencer tests passed");
$finish;
end
initial begin
repeat (20000) @(posedge clk_i);
$fatal(1, "APU-P3 focused test timed out");
end
logic s_unused;
assign s_unused = ^dma_request_addr ^ ^dma_request_bytes ^ mc_load_done ^ mc_fault_valid ^
^mc_fault_stage ^ ^mc_fault_resp ^ ^mc_fault_detail ^ seq_trap_event ^ seq_abort_done ^
seq_fault_valid ^ ^seq_fault_code ^ ^seq_fault_stage ^ ^seq_fault_resp ^
^seq_fault_index ^ ^seq_fault_addr ^ ^seq_fault_detail ^ ^seq_perf_retired;
endmodule
+73 -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_0018, 1'b0);
add_register(`APB4_APU__CAPABILITY1, AccessRo, 32'd0, 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__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);
@@ -461,6 +461,21 @@ module apu_reg_tb;
end
endtask
task automatic seed_performance_state;
begin
hard_reset();
apb_write(`APB4_APU__PERF_CONTROL, 32'h0000_0001, 4'hf, 1'b0);
force u_dut.s_dma_busy = 1'b1;
repeat (3) @(posedge clk_i);
release u_dut.s_dma_busy;
apb_write(`APB4_APU__PERF_CONTROL, 32'h0000_0005, 4'hf, 1'b0);
if ((u_dut.s_active_cycles_q == 64'd0) ||
(u_dut.u_apu_reg.s_perf_snapshot_q[0] == 64'd0)) begin
$fatal(1, "performance reset-timing state was not seeded");
end
end
endtask
initial begin
initialize_register_table();
s_phase = "initial reset matrix";
@@ -542,11 +557,13 @@ module apu_reg_tb;
for (int bit_index = 0; bit_index < 6; bit_index++) begin
if ((bit_index != `APB4_APU__COMMAND_SOFT_RESET) &&
(bit_index != `APB4_APU__COMMAND_ABORT) &&
(bit_index != `APB4_APU__COMMAND_MICROCODE_LOAD) &&
(bit_index != `APB4_APU__COMMAND_CLEAR_COUNTERS)) begin
check_rejected_write(`APB4_APU__COMMAND, 32'd1 << bit_index,
`APB4_APU__ERROR_CODE_UNSUPPORTED);
end
end
check_rejected_write(`APB4_APU__COMMAND, 32'h0000_0010, `APB4_APU__ERROR_CODE_INVALID_CONFIG);
check_rejected_write(`APB4_APU__IRQ_STATE, 32'h0000_0800, `APB4_APU__ERROR_CODE_INVALID_CONFIG);
check_rejected_write(`APB4_APU__IRQ_ENABLE, 32'h0000_0800,
`APB4_APU__ERROR_CODE_INVALID_CONFIG);
@@ -625,6 +642,60 @@ module apu_reg_tb;
resource_reset_i = 1'b0;
check_reset_state(1'b1);
s_phase = "reset timing";
seed_performance_state();
@(negedge clk_i);
force u_dut.u_apu_reg.s_soft_reset = 1'b1;
#1;
if ((u_dut.s_active_cycles_q == 64'd0) || (u_dut.u_apu_reg.s_perf_snapshot_q[0] == 64'd0)) begin
$fatal(1, "soft reset acted asynchronously");
end
@(posedge clk_i);
#1;
if ((u_dut.s_active_cycles_q != 64'd0) || (u_dut.u_apu_reg.s_perf_snapshot_q != '0)) begin
$fatal(1, "soft reset did not clear performance state on PCLK");
end
release u_dut.u_apu_reg.s_soft_reset;
seed_performance_state();
@(negedge clk_i);
force u_dut.s_resource_reset_apply_q = 1'b1;
#1;
if ((u_dut.s_active_cycles_q == 64'd0) || (u_dut.u_apu_reg.s_perf_snapshot_q[0] == 64'd0)) begin
$fatal(1, "resource reset apply acted asynchronously");
end
@(posedge clk_i);
#1;
if ((u_dut.s_active_cycles_q != 64'd0) || (u_dut.u_apu_reg.s_perf_snapshot_q != '0)) begin
$fatal(1, "resource reset apply did not clear performance state on PCLK");
end
release u_dut.s_resource_reset_apply_q;
seed_performance_state();
@(negedge clk_i);
force u_dut.u_apu_reg.s_cnt_clear = 1'b1;
#1;
if ((u_dut.s_active_cycles_q == 64'd0) || (u_dut.u_apu_reg.s_perf_snapshot_q[0] == 64'd0)) begin
$fatal(1, "counter clear acted asynchronously");
end
@(posedge clk_i);
#1;
if ((u_dut.s_active_cycles_q != 64'd0) || (u_dut.u_apu_reg.s_perf_snapshot_q != '0)) begin
$fatal(1, "counter clear did not clear performance state on PCLK");
end
release u_dut.u_apu_reg.s_cnt_clear;
seed_performance_state();
@(negedge clk_i);
rst_n_i = 1'b0;
#1;
if ((u_dut.s_active_cycles_q != 64'd0) || (u_dut.u_apu_reg.s_perf_snapshot_q != '0)) begin
$fatal(1, "hard reset did not clear performance state asynchronously");
end
repeat (2) @(posedge clk_i);
@(negedge clk_i);
rst_n_i = 1'b1;
s_phase = "performance enable";
hard_reset();
force u_dut.s_dma_busy = 1'b1;
+46
View File
@@ -352,6 +352,52 @@ module apu_ring_scheduler_tb;
if (ring_status_o != 32'h0000_0004 || job_status_o != 32'd0) begin
$fatal(1, "ring/job reset status mismatch");
end
s_phase = "reset timing";
ring_tail_i = 8'd1;
ring_enable_i = 1'b1;
hold_dma_request = 1'b1;
pulse_start();
@(posedge clk_i);
#1;
if (!dma_request_valid_o || idle_o) $fatal(1, "ring reset-timing state was not seeded");
@(negedge clk_i);
soft_reset_i = 1'b1;
ring_tail_i = 8'd0;
#1;
if (!dma_request_valid_o || idle_o) $fatal(1, "ring soft reset acted asynchronously");
@(posedge clk_i);
#1;
if (!idle_o || (ring_status_o != 32'h0000_0004) || (job_status_o != 32'd0)) begin
$fatal(1, "ring soft reset did not apply on PCLK");
end
@(negedge clk_i);
soft_reset_i = 1'b0;
ring_enable_i = 1'b0;
hold_dma_request = 1'b0;
repeat (2) @(posedge clk_i);
ring_tail_i = 8'd1;
ring_enable_i = 1'b1;
hold_dma_request = 1'b1;
pulse_start();
@(posedge clk_i);
#1;
if (!dma_request_valid_o || idle_o) $fatal(1, "ring hard-reset state was not seeded");
@(negedge clk_i);
rst_n_i = 1'b0;
ring_tail_i = 8'd0;
#1;
if (!idle_o || (ring_status_o != 32'h0000_0004) || (job_status_o != 32'd0)) begin
$fatal(1, "ring hard reset did not apply asynchronously");
end
repeat (2) @(posedge clk_i);
@(negedge clk_i);
rst_n_i = 1'b1;
ring_enable_i = 1'b0;
hold_dma_request = 1'b0;
repeat (2) @(posedge clk_i);
ring_size_i = 9'd256;
force u_dut.s_head_q = 8'd254;
#1;
+34
View File
@@ -195,6 +195,40 @@ module apu_stream_router_tb;
#1;
if ((status_o != 32'h0000_0014) || !idle_o) $fatal(1, "stream reset status mismatch");
tx_route_apu_i = 1'b1;
tx_underrun_i = 1'b1;
@(posedge clk_i);
@(negedge clk_i);
tx_underrun_i = 1'b0;
#1;
if (!status_o[6]) $fatal(1, "stream reset-timing state was not seeded");
flush_i = 1'b1;
#1;
if (!status_o[6]) $fatal(1, "stream flush acted asynchronously");
@(posedge clk_i);
#1;
if (status_o[6]) $fatal(1, "stream flush did not apply on PCLK");
@(negedge clk_i);
flush_i = 1'b0;
tx_underrun_i = 1'b1;
@(posedge clk_i);
@(negedge clk_i);
tx_underrun_i = 1'b0;
#1;
if (!status_o[6]) $fatal(1, "stream hard-reset state was not seeded");
rst_n_i = 1'b0;
#1;
if (status_o != 32'h0000_0014) begin
$fatal(1, "stream hard reset did not apply asynchronously");
end
repeat (2) @(posedge clk_i);
@(negedge clk_i);
rst_n_i = 1'b1;
tx_route_apu_i = 1'b0;
rx_route_apu_i = 1'b0;
repeat (2) @(posedge clk_i);
dma_tx_axis.tvalid = 1'b1;
#1;
if (!i2s_tx_axis.tvalid || (i2s_tx_axis.tdata != 32'h1122_3344) || !dma_tx_axis.tready)
+443
View File
@@ -3,10 +3,17 @@
from __future__ import annotations
import shutil
import struct
import subprocess
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
from apu_interpreter import Machine, PredicateInputs
from apu_isa import ControlOpcode, Instruction, crc32_iso_hdlc
from apu_mcasm import assemble
ROOT = Path(__file__).resolve().parents[1]
TOPOLOGY = ROOT / "rtl/mini/integration/soc_topology.json"
@@ -36,10 +43,15 @@ def test_apu_p1_apb_register_shell(tmp_path: Path) -> None:
str(common / "interface/axi4_stream_if.sv"),
str(common / "utils/register.sv"),
str(common / "utils/fifo.sv"),
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "rtl/ip/peripheral/dma_axi4_master.sv"),
str(multimedia / "apu_microcode_pkg.sv"),
str(multimedia / "apu_dma.sv"),
str(multimedia / "apu_ring_scheduler.sv"),
str(multimedia / "apu_stream_router.sv"),
str(multimedia / "apu_control_store.sv"),
str(multimedia / "apu_microcode_loader.sv"),
str(multimedia / "apu_codec_sequencer.sv"),
str(multimedia / "apu_reg.sv"),
str(multimedia / "apb4_apu.sv"),
str(ROOT / "tests/rtl/apu_reg_tb.sv"),
@@ -108,10 +120,15 @@ def test_apu_p1_integrated_irq_ownership_topology(tmp_path: Path) -> None:
str(common / "interface/axi4_stream_if.sv"),
str(common / "utils/register.sv"),
str(common / "utils/fifo.sv"),
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "rtl/ip/peripheral/dma_axi4_master.sv"),
str(multimedia / "apu_microcode_pkg.sv"),
str(multimedia / "apu_dma.sv"),
str(multimedia / "apu_ring_scheduler.sv"),
str(multimedia / "apu_stream_router.sv"),
str(multimedia / "apu_control_store.sv"),
str(multimedia / "apu_microcode_loader.sv"),
str(multimedia / "apu_codec_sequencer.sv"),
str(multimedia / "apu_reg.sv"),
str(multimedia / "apb4_apu.sv"),
str(ROOT / "rtl/mini/top/resource_controller.sv"),
@@ -151,6 +168,432 @@ def test_apu_p1_integrated_irq_ownership_topology(tmp_path: Path) -> None:
assert "APU-P1 integrated IRQ ownership topology passed" in result.stdout
def test_apu_p3_loader_control_store_and_sequencer(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
sv2v = shutil.which("sv2v")
if iverilog is None or vvp is None or sv2v is None:
return
source = """
.build_id 0x1122334455667788
.entry 0 start start last 16 64
.entry 1 start start last 16 64
.entry 2 start start last 16 64
start:
movi r0, 5
movi r1, 3
add r2, r0, r1
sub r9, r0, r1
and r10, r0, r1
or r11, r0, r1
xor r12, r0, r1
movi r13, 1
shl r14, r0, r13
shr r15, r14, r13
movi r4, 0xfffffff0
sar r4, r4, r13
cmp r1, r0
movi r3, 0xa5 if=ne
mov r2, r0 if=slt
movi r2, 8 if=sge
movi r2, 9 if=ult
movi r2, 10 if=uge
min r10, r4, r0, 1
max r11, r4, r0, 1
sat r5, r4, 40
movi r12, 7 if=input_exhausted
movi r12, 8 if=input_ready
movi r13, 9 if=output_ready
movi r13, 10 if=kernel_done
movi r14, 11 if=transport_done
movi r7, 2
loop_setup 0, r7
loop_body:
add r8, r8, r0
loop_back 0, loop_body
call_fwd sub if=ne
done:
end
sub:
movi r6, 0x66
last:
ret
"""
assembly = assemble(source)
reference = Machine(
assembly.instructions,
assembly.entries[0],
predicate_inputs=PredicateInputs(
input_exhausted=True,
input_ready=False,
output_ready=True,
kernel_done=False,
transport_done=True,
),
timeout=16,
).run()
assert reference["retired"] == 36
assert reference["registers"] == [
5,
3,
9,
0xA5,
0xFFFFFFF8,
0xFFFFFFF8,
0x66,
2,
10,
2,
0xFFFFFFF8,
5,
7,
9,
11,
5,
]
bundle = assembly.bundle
image = tmp_path / "apu_p3_image.hex"
image.write_text(
"".join(
f"{int.from_bytes(bundle[offset:offset + 4], 'little'):08x}\n"
for offset in range(0, len(bundle), 4)
),
encoding="utf-8",
)
invalid_source = """
.entry 0 start start done 1 16
.entry 1 start start done 1 16
.entry 2 start start done 1 16
start:
nop
nop
nop
done:
end
"""
invalid_bundle = bytearray(assemble(invalid_source).bundle)
invalid_instructions = (
Instruction(0, ControlOpcode.CALL_FWD, predicate=1, immediate=1),
Instruction(0, ControlOpcode.NOP),
Instruction(0, ControlOpcode.RET),
Instruction(0, ControlOpcode.END),
)
for pc, instruction in enumerate(invalid_instructions):
struct.pack_into("<Q", invalid_bundle, 192 + pc * 8, instruction.encode())
struct.pack_into("<I", invalid_bundle, 44, crc32_iso_hdlc(invalid_bundle[64:]))
invalid_image = tmp_path / "apu_p3_invalid_control.hex"
invalid_image.write_text(
"".join(
f"{int.from_bytes(invalid_bundle[offset:offset + 4], 'little'):08x}\n"
for offset in range(0, len(invalid_bundle), 4)
),
encoding="utf-8",
)
invalid_loop_source = """
.entry 0 start start done 1 16
.entry 1 start start done 1 16
.entry 2 start start done 1 16
start:
nop
nop
nop
nop
done:
end
"""
invalid_loop_bundle = bytearray(assemble(invalid_loop_source).bundle)
invalid_loop_instructions = (
Instruction(0, ControlOpcode.JUMP_FWD, predicate=1, immediate=1),
Instruction(0, ControlOpcode.LOOP_SETUP, src0=0, aux=0),
Instruction(0, ControlOpcode.NOP),
Instruction(0, ControlOpcode.LOOP_BACK, aux=0, immediate=2),
Instruction(0, ControlOpcode.END),
)
for pc, instruction in enumerate(invalid_loop_instructions):
struct.pack_into("<Q", invalid_loop_bundle, 192 + pc * 8, instruction.encode())
struct.pack_into("<I", invalid_loop_bundle, 44, crc32_iso_hdlc(invalid_loop_bundle[64:]))
invalid_loop_image = tmp_path / "apu_p3_invalid_loop.hex"
invalid_loop_image.write_text(
"".join(
f"{int.from_bytes(invalid_loop_bundle[offset:offset + 4], 'little'):08x}\n"
for offset in range(0, len(invalid_loop_bundle), 4)
),
encoding="utf-8",
)
diagnostic_source = """
.entry 0 start start last 1 16
.entry 1 start start last 1 16
.entry 2 start start last 1 16
start:
nop
nop
end
nop
last:
end
"""
diagnostic_bundle = bytearray(assemble(diagnostic_source).bundle)
diagnostic_instructions = (
Instruction(0, ControlOpcode.JUMP_FWD, predicate=1, immediate=3),
Instruction(0, ControlOpcode.RET),
Instruction(0, ControlOpcode.END),
Instruction(0, ControlOpcode.NOP),
Instruction(0, ControlOpcode.RET),
)
for pc, instruction in enumerate(diagnostic_instructions):
struct.pack_into("<Q", diagnostic_bundle, 192 + pc * 8, instruction.encode())
struct.pack_into("<I", diagnostic_bundle, 44, crc32_iso_hdlc(diagnostic_bundle[64:]))
diagnostic_image = tmp_path / "apu_p3_diagnostic_order.hex"
diagnostic_image.write_text(
"".join(
f"{int.from_bytes(diagnostic_bundle[offset:offset + 4], 'little'):08x}\n"
for offset in range(0, len(diagnostic_bundle), 4)
),
encoding="utf-8",
)
lexical_source = """
.entry 0 start start last 1 16
.entry 1 start start last 1 16
.entry 2 start start last 1 16
start:
nop
nop
nop
nop
last:
end
"""
lexical_bundle = bytearray(assemble(lexical_source).bundle)
lexical_instructions = (
Instruction(0, ControlOpcode.JUMP_FWD, predicate=1, immediate=1),
Instruction(0, ControlOpcode.NOP),
Instruction(0, ControlOpcode.RET),
Instruction(0, 9),
Instruction(0, ControlOpcode.END),
)
for pc, instruction in enumerate(lexical_instructions):
struct.pack_into("<Q", lexical_bundle, 192 + pc * 8, instruction.encode())
struct.pack_into("<I", lexical_bundle, 44, crc32_iso_hdlc(lexical_bundle[64:]))
lexical_image = tmp_path / "apu_p3_lexical_path_image.hex"
lexical_image.write_text(
"".join(
f"{int.from_bytes(lexical_bundle[offset:offset + 4], 'little'):08x}\n"
for offset in range(0, len(lexical_bundle), 4)
),
encoding="utf-8",
)
deep_lines = [
".entry 0 start start last 1 4096",
".entry 1 start start last 1 4096",
".entry 2 start start last 1 4096",
"start:",
]
for index in range(65):
if index != 0:
deep_lines.append(f"branch_{index}:")
deep_lines.append(f" jump_fwd branch_{index + 1} if=eq")
deep_lines.append(f"fallback_{index}:")
deep_lines.append(" end")
deep_lines.extend(("branch_65:", "last:", " end"))
deep_bundle = assemble("\n".join(deep_lines)).bundle
deep_image = tmp_path / "apu_p3_deep_path_image.hex"
deep_image.write_text(
"".join(
f"{int.from_bytes(deep_bundle[offset:offset + 4], 'little'):08x}\n"
for offset in range(0, len(deep_bundle), 4)
),
encoding="utf-8",
)
common = ROOT / "rtl/managed/clusterip/common/rtl"
multimedia = ROOT / "rtl/ip/multimedia"
source_list = tmp_path / "apu_p3_microcode.fl"
source_list.write_text(
"\n".join(
[
"+define+SV_ASSRT_DISABLE",
f"+incdir+{multimedia}",
f"+incdir+{common}",
str(ROOT / "rtl/tech/tc_sram.sv"),
str(multimedia / "apu_microcode_pkg.sv"),
str(multimedia / "apu_control_store.sv"),
str(multimedia / "apu_microcode_loader.sv"),
str(multimedia / "apu_codec_sequencer.sv"),
str(ROOT / "tests/rtl/apu_p3_microcode_tb.sv"),
"",
]
),
encoding="utf-8",
)
converted = tmp_path / "apu_p3_microcode_tb.v"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/script/convt_sv2v.py"),
"-f",
str(source_list),
"--output",
str(converted),
],
check=True,
)
simulation = tmp_path / "apu_p3_microcode_tb"
subprocess.run(
[
iverilog,
"-g2012",
"-s",
"apu_p3_microcode_tb",
"-o",
str(simulation),
str(converted),
],
check=True,
)
result = subprocess.run(
[
vvp,
str(simulation),
f"+IMAGE={image}",
f"+INVALID_CF_IMAGE={invalid_image}",
f"+INVALID_LOOP_IMAGE={invalid_loop_image}",
f"+DIAGNOSTIC_IMAGE={diagnostic_image}",
f"+LEXICAL_PATH_IMAGE={lexical_image}",
f"+DEEP_PATH_IMAGE={deep_image}",
],
check=True,
capture_output=True,
text=True,
)
assert "APU-P3 loader, control store, and sequencer tests passed" in result.stdout
def test_apu_p3_control_store_profile_mapping() -> None:
source = (ROOT / "rtl/ip/multimedia/apu_control_store.sv").read_text(encoding="utf-8")
assert source.count("tc_sram_1024x32 u_control_") == 4
assert "`ifdef HAVE_SRAM_MACRO" in source
assert "logic [63:0] mem" in source
assert "[0:2047];" in source
assert source.index("logic [63:0] mem") > source.index("`else")
def test_apu_p3_apb_dma_loader_integration(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
sv2v = shutil.which("sv2v")
if iverilog is None or vvp is None or sv2v is None:
return
source = """
.build_id 0x1122334455667788
.entry 0 start start done 16 64
.entry 1 start start done 16 64
.entry 2 start start done 16 64
start:
movi r0, 1
done:
end
"""
bundle = assemble(source).bundle
image = tmp_path / "apu_p3_integration_image.hex"
image.write_text(
"".join(
f"{int.from_bytes(bundle[offset:offset + 4], 'little'):08x}\n"
for offset in range(0, len(bundle), 4)
),
encoding="utf-8",
)
common = ROOT / "rtl/managed/clusterip/common/rtl"
multimedia = ROOT / "rtl/ip/multimedia"
source_list = tmp_path / "apu_p3_integration.fl"
source_list.write_text(
"\n".join(
[
"+define+SV_ASSRT_DISABLE",
f"+incdir+{common}",
f"+incdir+{common / 'interface'}",
f"+incdir+{multimedia}",
str(common / "interface/apb4_if.sv"),
str(common / "interface/axi4_if.sv"),
str(common / "interface/axi4_stream_if.sv"),
str(common / "utils/register.sv"),
str(common / "utils/fifo.sv"),
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "rtl/ip/peripheral/dma_axi4_master.sv"),
str(multimedia / "apu_microcode_pkg.sv"),
str(multimedia / "apu_dma.sv"),
str(multimedia / "apu_ring_scheduler.sv"),
str(multimedia / "apu_stream_router.sv"),
str(multimedia / "apu_control_store.sv"),
str(multimedia / "apu_microcode_loader.sv"),
str(multimedia / "apu_codec_sequencer.sv"),
str(multimedia / "apu_reg.sv"),
str(multimedia / "apb4_apu.sv"),
str(ROOT / "tests/rtl/apu_p3_integration_tb.sv"),
"",
]
),
encoding="utf-8",
)
converted = tmp_path / "apu_p3_integration_tb.v"
subprocess.run(
[
sys.executable,
str(ROOT / "rtl/mini/script/convt_sv2v.py"),
"-f",
str(source_list),
"--output",
str(converted),
],
check=True,
)
simulation = tmp_path / "apu_p3_integration_tb"
subprocess.run(
[
iverilog,
"-g2012",
"-s",
"apu_p3_integration_tb",
"-o",
str(simulation),
str(converted),
],
check=True,
)
result = subprocess.run(
[vvp, str(simulation), f"+IMAGE={image}"],
check=True,
capture_output=True,
text=True,
)
assert "APU-P3 APB, DMA, loader integration passed" in result.stdout
def test_apu_p3_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")
normalized_top = " ".join(top.split())
assert ".launch_i (1'b0)" in normalized_top
assert ".start_i (1'b0)" in normalized_top
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_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 forbidden in ("hazard3", "vexiiriscv", "rv32", "dsp_core"):
assert forbidden not in top.lower()
def test_apu_p2_stream_router(tmp_path: Path) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
+236
View File
@@ -0,0 +1,236 @@
"""APU-P3 assembler, APUMC, and reference-interpreter coverage."""
from __future__ import annotations
import struct
import subprocess
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "scripts"))
from apu_interpreter import Machine, PredicateInputs, SequencerTrap # noqa: E402
from apu_isa import ( # noqa: E402
APUMC_ABI,
APUMC_MAGIC,
ControlOpcode,
Entry,
Instruction,
InstructionClass,
ScalarOpcode,
crc32_iso_hdlc,
control_flow_report,
parse_apumc,
validate_p3_instruction,
)
from apu_mcasm import assemble # noqa: E402
PROGRAM = """
.build_id 0x1122334455667788
.entry 0 wav wav done 16 64
.entry 1 mp3 mp3 done 16 64
.entry 2 flac flac done 16 64
wav:
movi r0, 2
movi r1, 3
add r2, r0, r1
cmp r2, r1
movi r3, 0x55 if=ne
jump_fwd done
mp3:
movi r4, 0xffffffff
sat r5, r4, 40
jump_fwd done
flac:
movi r6, 2
loop_setup 0, r6
body:
add r7, r7, r0
loop_back 0, body
done:
end
"""
def test_apumc_bundle_layout_crc_and_determinism() -> None:
first = assemble(PROGRAM)
second = assemble(PROGRAM)
assert first.bundle == second.bundle
header = struct.unpack_from("<16I", first.bundle)
assert header[0] == APUMC_MAGIC
assert header[1] == APUMC_ABI
assert header[2] == len(first.bundle)
assert header[4] == 192
assert header[7] == 64
assert header[8] == 3
assert header[9] == header[10] == 0
assert header[11] == crc32_iso_hdlc(first.bundle[64:])
assert header[12:14] == (0x55667788, 0x11223344)
assert first.bundle[160:192] == bytes(32)
instructions, entries, parsed_header = parse_apumc(first.bundle)
assert instructions == first.instructions
assert entries == first.entries
assert parsed_header == header
def test_apumc_parser_rejects_reserved_descriptor_and_padding_bits() -> None:
for byte_offset in (64 + 3, 160):
bundle = bytearray(assemble(PROGRAM).bundle)
bundle[byte_offset] ^= 0x80
struct.pack_into("<I", bundle, 44, crc32_iso_hdlc(bundle[64:]))
with pytest.raises(ValueError):
parse_apumc(bundle)
@pytest.mark.parametrize(
"instruction",
[
Instruction(0, 0, immediate=1),
Instruction(0, ControlOpcode.RET, predicate=1),
Instruction(0, ControlOpcode.JUMP_FWD, immediate=0),
Instruction(0, ControlOpcode.WAIT, aux=0),
Instruction(1, 14),
Instruction(1, ScalarOpcode.SAT, aux=7),
Instruction(2, 0),
Instruction(7, 0),
],
)
def test_p3_rejects_reserved_and_deferred_encodings(instruction: Instruction) -> None:
with pytest.raises(ValueError):
validate_p3_instruction(instruction)
def test_reference_interpreter_scalar_predicate_and_terminal_retention() -> None:
assembly = assemble(PROGRAM)
result = Machine(assembly.instructions, assembly.entries[0]).run()
assert result["registers"][2] == 5
assert result["registers"][3] == 0x55
assert result["retired"] == 7
assert result["pc"] == assembly.symbols["done"]
def test_reference_interpreter_call_loop_and_traps() -> None:
source = """
.entry 0 start start end 4 32
.entry 1 start start end 4 32
.entry 2 start start end 4 32
start:
movi r0, 3
loop_setup 0, r0
body:
add r1, r1, r0
loop_back 0, body
call_fwd sub
end
sub:
movi r2, 9
ret
end:
end
"""
assembly = assemble(source)
result = Machine(assembly.instructions, assembly.entries[0]).run()
assert result["registers"][1] == 9
assert result["registers"][2] == 9
trapping = [Instruction(InstructionClass.CONTROL, ControlOpcode.TRAP, immediate=0xA5)]
entry = assembly.entries[0].__class__(0, 0, 0, 0, 1, 1)
with pytest.raises(SequencerTrap) as caught:
Machine(trapping, entry).run()
assert caught.value.reason == 10
assert caught.value.detail == 0xA5
def test_reference_interpreter_external_predicates_and_watchdog() -> None:
instructions = [
Instruction(1, ScalarOpcode.MOVI, predicate=8, dst=0, immediate=0x55),
Instruction(0, ControlOpcode.END),
]
entry = Entry(0, 0, 0, 1, 1, 4)
false_result = Machine(instructions, entry, fetch_no_retirement_cycles=0).run()
true_result = Machine(
instructions,
entry,
predicate_inputs=PredicateInputs(input_ready=True),
fetch_no_retirement_cycles=0,
).run()
assert false_result["registers"][0] == 0
assert true_result["registers"][0] == 0x55
with pytest.raises(SequencerTrap) as caught:
Machine(instructions, entry, timeout=1).run()
assert caught.value.reason == 7
assert caught.value.pc == 0
def test_assembler_rejects_host_execution_inputs_and_bad_contracts() -> None:
with pytest.raises(ValueError, match="exactly three"):
assemble("start:\n end\n")
with pytest.raises(ValueError, match="unknown mnemonic"):
assemble(
".entry 0 x x x 1 1\n.entry 1 x x x 1 1\n.entry 2 x x x 1 1\n"
"x:\n rv32_addi r0, r0, 1\n"
)
@pytest.mark.parametrize(
"body, message",
[
("start:\n jump_fwd start\n", "forward branch delta"),
("start:\n ret\n", "empty stack"),
("start:\n movi r0, 2\n loop_back 0, start\n end\n", "no setup"),
("start:\n nop\n", "outside its program range"),
],
)
def test_assembler_rejects_invalid_control_flow(body: str, message: str) -> None:
source = (
".entry 0 start start last 4 32\n"
".entry 1 start start last 4 32\n"
".entry 2 start start last 4 32\n"
f"{body}"
"last:\n nop\n"
)
with pytest.raises(ValueError, match=message):
assemble(source)
def test_path_sensitive_call_proof_rejects_predicated_empty_return() -> None:
instructions = [
Instruction(0, ControlOpcode.CALL_FWD, predicate=1, immediate=1),
Instruction(0, ControlOpcode.NOP),
Instruction(0, ControlOpcode.RET),
Instruction(0, ControlOpcode.END),
]
entry = Entry(0, 0, 0, 3, 1, 16)
with pytest.raises(ValueError, match="empty stack"):
control_flow_report(instructions, entry)
def test_assembler_cli_emits_all_canonical_artifacts(tmp_path: Path) -> None:
source = tmp_path / "dummy.apus"
source.write_text(PROGRAM, encoding="utf-8")
outputs = {
"-o": tmp_path / "dummy.apumc",
"--symbols": tmp_path / "symbols.json",
"--cfg-report": tmp_path / "cfg.json",
"--primitive-manifest": tmp_path / "primitives.json",
"--trace-input": tmp_path / "trace.json",
"--abi-input-manifest": tmp_path / "abi.json",
}
command = [sys.executable, str(ROOT / "scripts/apu_mcasm.py"), str(source)]
for option, path in outputs.items():
command.extend((option, str(path)))
subprocess.run(command, check=True)
assert outputs["-o"].read_bytes() == assemble(PROGRAM).bundle
for option, path in outputs.items():
assert path.stat().st_size > 0, option
cfg = __import__("json").loads(outputs["--cfg-report"].read_text(encoding="utf-8"))
assert len(cfg["control_flow"]) == 3
assert all(report["proof"] == "passed" for report in cfg["control_flow"])
assert any(report["loops"] for report in cfg["control_flow"])
assert any(report["edges"] for report in cfg["control_flow"])
+69 -1
View File
@@ -1,12 +1,18 @@
"""Keep the handwritten APU-P1 RTL and SDK register ABI synchronized."""
"""Keep the handwritten APU RTL and SDK register ABI synchronized through P3."""
from __future__ import annotations
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "scripts"))
from apu_isa import abi_manifest # noqa: E402
RTL_DEFINE = ROOT / "rtl/ip/multimedia/apu_define.svh"
C_HEADER = ROOT / "crt/include/retrosoc/hal/apu_regs.h"
REGISTER_TESTBENCH = ROOT / "tests/rtl/apu_reg_tb.sv"
@@ -64,3 +70,65 @@ def test_apu_register_matrix_covers_each_offset_once() -> None:
assert len(covered) == len(set(covered))
assert set(covered) == set(expected)
def test_apu_p3_tool_isa_matches_handwritten_rtl_and_c() -> None:
rtl = _rtl_values()
manifest = abi_manifest()
exact = {
"APUMC_MAGIC": manifest["apumc"]["magic"],
"APUMC_ABI": manifest["apumc"]["abi"],
"APUMC_HEADER_BYTES": manifest["apumc"]["header_bytes"],
"APUMC_ENTRY_BYTES": manifest["apumc"]["entry_bytes"],
"APUMC_ENTRY_COUNT": manifest["apumc"]["entry_count"],
"APUMC_MAX_INSTRUCTIONS": manifest["apumc"]["max_instructions"],
}
for name, value in manifest["classes"].items():
exact[f"MC_CLASS_{name.upper()}"] = value
for name, value in manifest["control_opcodes"].items():
exact[f"MC_CONTROL_{name.upper()}"] = value
for name, value in manifest["scalar_opcodes"].items():
exact[f"MC_SCALAR_{name.upper()}"] = value
for class_name, opcodes in manifest["deferred_opcodes"].items():
for name, value in opcodes.items():
exact[f"MC_{class_name.upper()}_{name.upper()}"] = value
predicate_names = {
"always": "ALWAYS",
"eq": "EQ",
"ne": "NE",
"slt": "SIGNED_LT",
"sge": "SIGNED_GE",
"ult": "UNSIGNED_LT",
"uge": "UNSIGNED_GE",
"input_exhausted": "INPUT_EXHAUSTED",
"input_ready": "INPUT_READY",
"output_ready": "OUTPUT_READY",
"kernel_done": "KERNEL_DONE",
"transport_done": "TRANSPORT_DONE",
}
for name, value in manifest["predicates"].items():
exact[f"MC_PRED_{predicate_names[name]}"] = value
for name, value in manifest["wait_sources"].items():
exact[f"MC_WAIT_{name.upper()}"] = value
for name, value in manifest["primitives"].items():
exact[f"MC_PRIMITIVE_{name.upper()}"] = value
for name, value in manifest["instruction_fields"].items():
exact[f"MC_INSTRUCTION_{name.upper()}"] = value
for name, value in manifest["entry_words"].items():
exact[f"APUMC_ENTRY_{name.upper()}"] = value
trap_names = {
"illegal": "ILLEGAL",
"pc_range": "PC_RANGE",
"call_stack": "CALL_STACK",
"loop": "LOOP",
"local_range": "LOCAL_RANGE",
"unavailable": "UNAVAILABLE",
"watchdog": "WATCHDOG",
"retired_budget": "RETIRED_BUDGET",
"engine": "ENGINE",
"explicit": "EXPLICIT",
}
for name, value in manifest["trap_reasons"].items():
if name != "reserved":
exact[f"MC_TRAP_{trap_names[name]}"] = value
assert {name: rtl[name] for name in exact} == exact
+18
View File
@@ -330,6 +330,8 @@ 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_loader_filelist = tmp_path / "apu_loader.fl"
apu_sequencer_filelist = tmp_path / "apu_sequencer.fl"
gateway_a_filelist = tmp_path / "gateway_a.fl"
assert generate_formal_filelist("bus", bus_filelist, memory_map, topology, user_extensions)
assert generate_formal_filelist(
@@ -357,6 +359,12 @@ 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_loader", apu_loader_filelist, memory_map, topology, user_extensions
)
assert generate_formal_filelist(
"apu_sequencer", apu_sequencer_filelist, memory_map, topology, user_extensions
)
assert generate_formal_filelist(
"gateway_a", gateway_a_filelist, memory_map, topology, user_extensions
)
@@ -373,6 +381,8 @@ 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_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)
assert "+define+SV_ASSRT_DISABLE" in bus.defines
assert "+define+PDK_BEHAV" in onchip_ram.defines
@@ -431,6 +441,10 @@ 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_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
assert ROOT / "rtl/mini/formal/apu_sequencer_formal.sv" in apu_sequencer.files
assert ROOT / "rtl/mini/top/hp_axi4_mux3.sv" in gateway_a.files
assert ROOT / "rtl/managed/clusterip/common/rtl/stream/round_robin_arbiter.sv" in gateway_a.files
assert ROOT / "rtl/mini/formal/gateway_a_formal.sv" in gateway_a.files
@@ -467,6 +481,9 @@ def test_sby_config_uses_prove_and_cover_with_bitwuzla(tmp_path: Path) -> None:
compact_cover_config = render_sby_config(
"i2c_formal", design, properties, "bitwuzla", "cover", 80, vcd=False
)
skipped_cover_config = render_sby_config(
"apu_loader_formal", design, properties, "bitwuzla", "cover", 64, skip=60
)
assert "mode prove" in prove_config
assert "mode cover" in cover_config
@@ -476,6 +493,7 @@ def test_sby_config_uses_prove_and_cover_with_bitwuzla(tmp_path: Path) -> None:
assert f"properties.v {properties.resolve()}" in prove_config
assert "vcd off" not in cover_config
assert "vcd off" in compact_cover_config
assert "skip 60" in skipped_cover_config
def test_bitwuzla_wrapper_translates_yosys_legacy_arguments() -> None: