mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
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:
33 files changed
+6171
-100
No files matched your search
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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",
|
||||
),
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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())
|
||||
@@ -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
|
||||
@@ -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())
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"])
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user