Files
retroSoC/rtl/ip/multimedia/apu_codec_sequencer.sv
T
maksyuki 67281e224f 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.
2026-09-04 11:56:05 +08:00

549 lines
24 KiB
Systemverilog

// 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