Files
retroSoC/rtl/ip/multimedia/npu_core.sv
T
maksyuki 3ac9e73ab9 feat: refactor tests for mailbox and environment validation
- Updated mailbox purpose tests to reflect new write order and sequence handling.
- Added tests to ensure Linux-ready values match LP contract and constants match Linux helper.
- Introduced tests to verify that offset changes do not hide stale Linux helpers.
- Enhanced environment tests to report current locked launcher and runtime versions.
- Added checks for SBT launcher presence in environment labels.
- Improved full diagram tests to validate storage geometry and RSA vector configurations.
- Expanded register tests to ensure crypto interrupt registers publish all events and lifecycle annotations preserve security boundaries.
- Modified script tools tests to include Vexii generation validation and updated workflow assertions.
2026-09-30 10:51:04 +08:00

867 lines
36 KiB
Systemverilog

// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// SPDX-License-Identifier: MulanPSL-2.0
// NPU HP-domain job execution core. With ExecutionReady=1'b0 this is exactly
// the Phase 2 lifecycle endpoint: it terminates the launch, result, and
// snapshot mailboxes of npu_control_cdc, participates in the epoch
// reconciliation handshake, answers idle snapshots with a zero counter bank,
// sinks the (shell-unreachable) launch, and produces no result. With
// ExecutionReady=1'b1 it is the Phase 3 job controller: an accepted launch
// latches {JOB_BASE, JOB_ID, TIMEOUT_CYCLES, JOB_COUNT}, resets the private
// DMA and the per-job counters, and runs npu_job_decoder (fetch + ABI-1.0
// validation) feeding npu_scheduler (transport) record by record until every
// descriptor retires (DONE), the first fault drains (ERROR), software abort
// or resource quiesce cancels (ABORTED), or a reset/flush epoch recovery
// cancels (RESET_CANCELLED, published after the fresh epoch is
// re-acknowledged). The npu_dma engine itself lives in apb4_npu (the Phase 2
// control-CDC testbench instantiates this module without an AXI fabric, so
// the engine interface is a discrete signal boundary); the P2 branch never
// references the P3 ports.
//
// Lifecycle invariants (both configurations): an epoch restart forces the
// endpoint to a safe state before re-acknowledging (at P3 the drain completes
// first: no new work, accepted transport obligations retire, presented but
// unaccepted VALID stays stable); the quiesce acknowledge is a registered
// round trip and at P3 additionally requires the terminal result latched for
// delivery; a flushed or cancelled job never reports DONE; busy_o/draining_o
// are truthful. The first-fault record follows the spec's deterministic
// same-cycle priority AXI_PROTOCOL > AXI_READ > AXI_WRITE > LOCAL_STATE >
// ARITHMETIC > DESCRIPTOR > UNSUPPORTED > RANGE > NO_PROGRESS; reset
// cancellation never overwrites a captured earlier fault.
`include "npu_define.svh"
module npu_core #(
parameter int unsigned LaunchPayloadWidth = `APB4_NPU__LAUNCH_PAYLOAD_WIDTH,
parameter int unsigned ResultPayloadWidth = `APB4_NPU__RESULT_PAYLOAD_WIDTH,
parameter int unsigned SnapshotPayloadWidth = `APB4_NPU__SNAPSHOT_PAYLOAD_WIDTH,
parameter int unsigned EpochWidth = `APB4_NPU__EPOCH_WIDTH,
// Phase 3 execution compile switch: the PCLK shell keeps START rejected at
// P3, so apb4_npu instantiates the live job controller while the software
// launch path stays closed; the P2 shell testbench elaborates the default
// (ExecutionReady=0) endpoint.
parameter bit ExecutionReady = 1'b0
) (
// verilog_format: off -- preserve the HP lifecycle boundary columns
input logic clk_hp_i,
input logic rst_hp_n_i,
input logic launch_valid_i,
output logic launch_ready_o,
input logic [LaunchPayloadWidth-1:0] launch_data_i,
output logic result_valid_o,
input logic result_ready_i,
output logic [ResultPayloadWidth-1:0] result_data_o,
input logic snapshot_req_valid_i,
output logic snapshot_req_ready_o,
output logic snapshot_resp_valid_o,
input logic snapshot_resp_ready_i,
output logic [SnapshotPayloadWidth-1:0] snapshot_resp_data_o,
input logic [EpochWidth-1:0] epoch_req_i,
output logic [EpochWidth-1:0] epoch_ack_o,
input logic quiesce_req_i,
output logic quiesce_ack_o,
output logic busy_o,
output logic draining_o,
input logic block_new_i,
output logic clock_pause_ack_o,
input logic flush_i,
output logic flush_busy_o,
output logic pause_active_o,
output logic idle_o,
// Phase 3: software abort pulse (HP domain, toggle-synchronized in
// apb4_npu) and the discrete npu_dma engine interface
input logic abort_i,
output logic dma_clear_o,
output logic dma_read_req_valid_o,
input logic dma_read_req_ready_i,
output logic [31:0] dma_read_addr_o,
output logic [31:0] dma_read_bytes_o,
input logic dma_read_data_valid_i,
output logic dma_read_data_ready_o,
input logic [63:0] dma_read_data_i,
input logic [ 7:0] dma_read_keep_i,
input logic dma_read_last_i,
output logic dma_write_req_valid_o,
input logic dma_write_req_ready_i,
output logic [31:0] dma_write_addr_o,
output logic [31:0] dma_write_bytes_o,
output logic dma_write_data_valid_o,
input logic dma_write_data_ready_i,
output logic [63:0] dma_write_data_o,
output logic [ 7:0] dma_write_keep_o,
output logic dma_write_last_o,
input logic dma_write_done_i,
input logic dma_busy_i,
input logic dma_read_busy_i,
input logic dma_write_busy_i,
input logic dma_pause_ack_i,
input logic dma_fault_i,
input logic [ 3:0] dma_fault_code_i,
input logic [31:0] dma_fault_addr_i,
input logic [ 1:0] dma_fault_resp_i,
input logic [63:0] dma_read_bytes_i,
input logic [63:0] dma_write_bytes_i,
input logic [63:0] dma_stall_cycles_i,
input logic dma_read_cmd_err_i,
input logic dma_write_cmd_err_i
// verilog_format: on
);
typedef enum logic [0:0] {
SnapIdle,
SnapRespond
} snap_state_e;
`ifndef SYNTHESIS
initial begin
if ((EpochWidth < 2) || (LaunchPayloadWidth < 1) || (ResultPayloadWidth < 1) ||
(SnapshotPayloadWidth < 1)) begin
$fatal(1, "npu_core: invalid payload or epoch geometry");
end
end
`endif
if (!ExecutionReady) begin : gen_p2_endpoint
// Phase 2 protocol-complete sinks and constant sources: a launch is sunk
// (unreachable), no terminal result exists, the idle snapshot returns the
// zero bank with a zero job token, and no AXI activity ever needs
// draining. The P3 engine interface is tied off and its inputs are
// unread by construction.
snap_state_e s_state_d, s_state_q;
logic [EpochWidth-1:0] s_epoch_ack_d, s_epoch_ack_q;
logic s_quiesce_ack_d, s_quiesce_ack_q;
logic s_epoch_restart;
logic s_unused;
assign launch_ready_o = 1'b1;
assign result_valid_o = 1'b0;
assign result_data_o = '0;
assign busy_o = 1'b0;
assign draining_o = 1'b0;
assign flush_busy_o = 1'b0;
assign snapshot_resp_data_o = '0;
// With zero accepted AXI obligations and no internal work, the
// clock-pause acknowledge is immediate and the HP idle contribution
// follows the platform formula !block_new_i || clock_pause_ack_o.
assign clock_pause_ack_o = block_new_i;
assign pause_active_o = block_new_i && clock_pause_ack_o;
assign idle_o = !block_new_i || clock_pause_ack_o;
assign epoch_ack_o = s_epoch_ack_q;
assign quiesce_ack_o = s_quiesce_ack_q;
assign s_epoch_restart = (epoch_req_i != s_epoch_ack_q);
assign s_unused = ^{launch_data_i, result_ready_i, flush_i, 1'b0};
assign dma_clear_o = 1'b0;
assign dma_read_req_valid_o = 1'b0;
assign dma_read_addr_o = '0;
assign dma_read_bytes_o = '0;
assign dma_read_data_ready_o = 1'b0;
assign dma_write_req_valid_o = 1'b0;
assign dma_write_addr_o = '0;
assign dma_write_bytes_o = '0;
assign dma_write_data_valid_o = 1'b0;
assign dma_write_data_o = '0;
assign dma_write_keep_o = '0;
assign dma_write_last_o = 1'b0;
always_comb begin
s_state_d = s_state_q;
s_epoch_ack_d = s_epoch_ack_q;
s_quiesce_ack_d = quiesce_req_i;
snapshot_req_ready_o = 1'b0;
snapshot_resp_valid_o = 1'b0;
unique case (s_state_q)
SnapIdle: begin
snapshot_req_ready_o = 1'b1;
if (snapshot_req_valid_i) begin
s_state_d = SnapRespond;
end
end
SnapRespond: begin
snapshot_resp_valid_o = 1'b1;
if (snapshot_resp_ready_i) begin
s_state_d = SnapIdle;
end
end
default: s_state_d = SnapIdle;
endcase
// A fresh epoch restarts the endpoint bookkeeping before it is
// acknowledged: any in-flight mailbox item is being flushed by the link
// reset held for the whole reconciliation, so the endpoint returns to
// SnapIdle first and only then publishes the new epoch.
if (s_epoch_restart) begin
s_state_d = SnapIdle;
s_epoch_ack_d = epoch_req_i;
end
end
always_ff @(posedge clk_hp_i or negedge rst_hp_n_i) begin
if (!rst_hp_n_i) begin
s_state_q <= SnapIdle;
end else begin
s_state_q <= s_state_d;
end
end
dffr #(
.DATA_WIDTH(EpochWidth)
) u_epoch_ack (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.dat_i (s_epoch_ack_d),
.dat_o (s_epoch_ack_q)
);
dffr #(
.DATA_WIDTH(1)
) u_quiesce_ack (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.dat_i (s_quiesce_ack_d),
.dat_o (s_quiesce_ack_q)
);
`ifndef SV_ASSRT_DISABLE
logic s_resp_wait_q;
logic s_epoch_restart_q;
always_ff @(posedge clk_hp_i or negedge rst_hp_n_i) begin
if (!rst_hp_n_i) begin
s_resp_wait_q <= 1'b0;
s_epoch_restart_q <= 1'b0;
end else begin
s_resp_wait_q <= snapshot_resp_valid_o && !snapshot_resp_ready_i;
s_epoch_restart_q <= s_epoch_restart;
// The snapshot response holds valid and its payload stable until the
// mailbox accepts it; only an epoch restart may withdraw it.
if (s_resp_wait_q) begin
assert ((snapshot_resp_valid_o && $stable(snapshot_resp_data_o)) || s_epoch_restart_q);
end
// No terminal result can ever appear at Phase 2.
assert (!result_valid_o);
end
end
`endif
end else begin : gen_p3_core
// -----------------------------------------------------------------
// Phase 3 job controller
// -----------------------------------------------------------------
typedef enum logic [2:0] {
CoreIdle,
CoreRun,
CoreDrainFault,
CoreDrainAbort,
CoreDrainReset,
CorePublish
} core_state_e;
core_state_e s_state_d, s_state_q;
snap_state_e s_snap_state_d, s_snap_state_q;
logic [EpochWidth-1:0] s_epoch_ack_d, s_epoch_ack_q;
logic s_quiesce_ack_d, s_quiesce_ack_q;
logic s_epoch_restart;
// job context
logic [31:0] s_job_base_q;
logic [31:0] s_job_id_q;
logic [31:0] s_timeout_q;
logic [15:0] s_job_count_q;
logic [31:0] s_term_job_id_q;
// first-fault record
logic s_fault_seen_d, s_fault_seen_q;
logic [3:0] s_fault_code_d, s_fault_code_q;
logic [31:0] s_fault_addr_d, s_fault_addr_q;
logic [31:0] s_fault_info_d, s_fault_info_q;
logic [31:0] s_fault_desc_d, s_fault_desc_q;
// terminal record
logic [2:0] s_res_code_d, s_res_code_q;
logic [2:0] s_res_irq_d, s_res_irq_q;
logic [31:0] s_res_completed_d, s_res_completed_q;
// snapshot capture
logic [SnapshotPayloadWidth-1:0] s_snap_payload_d, s_snap_payload_q;
logic s_launch_accept;
logic s_job_busy;
logic s_dec_busy;
logic s_sch_busy;
logic s_sch_drained;
logic s_drained;
logic s_compose_terminal;
logic s_stop;
logic s_clear;
// decoder wires
logic s_dec_job_start;
logic s_dec_read_req_valid;
logic s_dec_read_req_ready;
logic [31:0] s_dec_read_addr;
logic [31:0] s_dec_read_bytes;
logic s_dec_read_data_valid;
logic s_dec_read_data_ready;
logic s_dec_pause_ok;
logic s_dec_all_done;
logic s_all_done_q;
logic s_dec_progress;
logic s_dec_fault_req;
logic [ 3:0] s_dec_fault_code;
logic [31:0] s_dec_fault_addr;
logic [31:0] s_dec_fault_info;
logic [31:0] s_dec_fault_desc;
logic [15:0] s_dec_cur_index;
logic s_dec_retired;
// record wires decoder -> scheduler
logic s_rec_valid;
logic s_rec_ready;
logic [ 3:0] s_rec_opcode;
logic [12:0] s_rec_h, s_rec_w, s_rec_cin, s_rec_cout, s_rec_oh, s_rec_ow;
logic [31:0] s_rec_input0_base, s_rec_output_base;
logic [31:0] s_rec_weight_base, s_rec_param_base;
logic [31:0] s_rec_irow, s_rec_orow;
logic [7:0] s_rec_kh, s_rec_kw, s_rec_sh, s_rec_sw;
logic [7:0] s_rec_pad_top, s_rec_pad_left, s_rec_in0_zero;
logic [7:0] s_rec_tile_h, s_rec_tile_w;
logic [15:0] s_rec_k_slice;
logic [31:0] s_rec_input1_base, s_rec_input1_row_bytes;
logic [7:0] s_rec_in1_zero, s_rec_out_zero, s_rec_act_min, s_rec_act_max;
logic [31:0] s_rec_weight_bytes, s_rec_param_bytes;
logic [ 15:0] s_rec_desc_index;
// scheduler wires
logic s_sch_launch;
logic s_sch_retired;
logic [ 31:0] s_sch_completed;
logic s_sch_read_req_valid;
logic s_sch_read_req_ready;
logic [ 31:0] s_sch_read_addr;
logic [ 31:0] s_sch_read_bytes;
logic s_sch_read_data_valid;
logic s_sch_read_data_ready;
logic s_sch_write_req_valid;
logic [ 31:0] s_sch_write_addr;
logic [ 31:0] s_sch_write_bytes;
logic s_sch_write_data_valid;
logic [ 63:0] s_sch_write_data;
logic [ 7:0] s_sch_write_keep;
logic s_sch_write_last;
logic s_sch_pause_ok;
logic s_sch_terminal;
logic [639:0] s_sch_counters;
logic [639:0] s_sch_term_counters;
logic s_sch_fault_req;
logic [ 3:0] s_sch_fault_code;
logic [ 31:0] s_sch_fault_addr;
logic [ 31:0] s_sch_fault_info;
logic [ 31:0] s_sch_fault_desc;
// fault arbitration
logic s_evt_dec;
logic s_evt_sch;
logic s_evt_cmd_err;
logic [ 3:0] s_evt_code;
logic [ 31:0] s_evt_addr;
logic [ 31:0] s_evt_info;
logic [ 31:0] s_evt_desc;
logic s_evt_any;
// Deterministic same-cycle first-fault priority: lower rank wins.
// verilog_format: off -- preserve macro case labels that Verible misclassifies as item tokens
function automatic logic [3:0] fault_rank(input logic [3:0] code_i);
logic [3:0] rank;
begin
unique case (code_i)
`APB4_NPU__FAULT_CODE_AXI_PROTOCOL: rank = 4'd0;
`APB4_NPU__FAULT_CODE_AXI_READ: rank = 4'd1;
`APB4_NPU__FAULT_CODE_AXI_WRITE: rank = 4'd2;
`APB4_NPU__FAULT_CODE_LOCAL_STATE: rank = 4'd3;
`APB4_NPU__FAULT_CODE_ARITHMETIC: rank = 4'd4;
`APB4_NPU__FAULT_CODE_DESCRIPTOR: rank = 4'd5;
`APB4_NPU__FAULT_CODE_UNSUPPORTED: rank = 4'd6;
`APB4_NPU__FAULT_CODE_RANGE: rank = 4'd7;
`APB4_NPU__FAULT_CODE_NO_PROGRESS: rank = 4'd8;
default: rank = 4'd9;
endcase
return rank;
end
endfunction
// verilog_format: on
assign s_evt_dec = s_dec_fault_req;
assign s_evt_sch = s_sch_fault_req;
// A rejected DMA command means a scheduler/decoder programming bug.
assign s_evt_cmd_err = (dma_read_cmd_err_i || dma_write_cmd_err_i) && s_job_busy &&
!dma_fault_i && !s_fault_seen_q;
always_comb begin
s_evt_any = s_evt_dec || s_evt_sch || s_evt_cmd_err;
s_evt_code = `APB4_NPU__FAULT_CODE_LOCAL_STATE;
s_evt_addr = 32'd0;
s_evt_info = {16'd0, 8'd255, 4'd0, 2'd0, 2'd0};
s_evt_desc = 32'hffff_ffff;
if (s_evt_dec) begin
s_evt_code = s_dec_fault_code;
s_evt_addr = s_dec_fault_addr;
s_evt_info = s_dec_fault_info;
s_evt_desc = s_dec_fault_desc;
end
if (s_evt_sch && (!s_evt_dec || (fault_rank(
s_sch_fault_code
) <= fault_rank(
s_dec_fault_code
)))) begin
s_evt_code = s_sch_fault_code;
s_evt_addr = s_sch_fault_addr;
s_evt_info = s_sch_fault_info;
// A scheduler fault while it is idle (watchdog during the fetch
// phase) belongs to the descriptor the decoder holds.
s_evt_desc = s_dec_busy ? {16'd0, s_dec_cur_index} : s_sch_fault_desc;
end
// verilog_format: off -- preserve macro argument tokens that Verible misclassifies
if (s_evt_cmd_err && (!s_evt_dec && !s_evt_sch ||
(fault_rank(`APB4_NPU__FAULT_CODE_LOCAL_STATE) <=
fault_rank(s_evt_code)))) begin
s_evt_code = `APB4_NPU__FAULT_CODE_LOCAL_STATE;
s_evt_addr = 32'd0;
s_evt_info = {16'd0, 8'd255, 4'd0, 2'd0, 2'd0};
s_evt_desc = s_dec_busy ? {16'd0, s_dec_cur_index} : s_sch_fault_desc;
end
// verilog_format: on
end
assign s_job_busy = (s_state_q != CoreIdle);
assign s_drained = !s_dec_busy && s_sch_drained && !dma_busy_i;
assign s_epoch_restart = (epoch_req_i != s_epoch_ack_q);
assign s_launch_accept = launch_valid_i && launch_ready_o;
assign s_stop = (s_state_q == CoreDrainFault) || (s_state_q == CoreDrainAbort) ||
(s_state_q == CoreDrainReset) || flush_i;
assign s_clear = flush_i ||
((s_state_q == CorePublish) && result_valid_o && result_ready_i) ||
((s_state_q == CoreDrainReset) && s_drained);
assign s_compose_terminal = ((s_state_q == CoreRun) && s_all_done_q) ||
(((s_state_q == CoreDrainFault) || (s_state_q == CoreDrainAbort) ||
(s_state_q == CoreDrainReset)) && s_drained);
// -------------------------------------------------------------
// Mailbox-facing outputs
// -------------------------------------------------------------
assign launch_ready_o = (s_state_q == CoreIdle) && !s_epoch_restart && !quiesce_req_i &&
!flush_i;
assign busy_o = s_job_busy;
assign draining_o = (s_state_q == CoreDrainFault) || (s_state_q == CoreDrainAbort) ||
(s_state_q == CoreDrainReset);
assign flush_busy_o = 1'b0;
assign idle_o = !block_new_i || clock_pause_ack_o;
assign clock_pause_ack_o = block_new_i && dma_pause_ack_i && s_dec_pause_ok && s_sch_pause_ok;
assign pause_active_o = block_new_i && clock_pause_ack_o;
assign epoch_ack_o = s_epoch_ack_q;
assign quiesce_ack_o = s_quiesce_ack_q;
assign dma_clear_o = flush_i || s_launch_accept;
// Read-channel ownership alternates fetch (decoder) and execute
// (scheduler) phases, which never overlap.
assign dma_read_req_valid_o = s_sch_busy ? s_sch_read_req_valid : s_dec_read_req_valid;
assign dma_read_addr_o = s_sch_busy ? s_sch_read_addr : s_dec_read_addr;
assign dma_read_bytes_o = s_sch_busy ? s_sch_read_bytes : s_dec_read_bytes;
assign s_dec_read_req_ready = !s_sch_busy && dma_read_req_ready_i;
assign s_sch_read_req_ready = s_sch_busy && dma_read_req_ready_i;
assign s_dec_read_data_valid = !s_sch_busy && dma_read_data_valid_i;
assign s_sch_read_data_valid = s_sch_busy && dma_read_data_valid_i;
assign dma_read_data_ready_o = s_sch_busy ? s_sch_read_data_ready : s_dec_read_data_ready;
assign dma_write_req_valid_o = s_sch_write_req_valid;
assign dma_write_addr_o = s_sch_write_addr;
assign dma_write_bytes_o = s_sch_write_bytes;
assign dma_write_data_valid_o = s_sch_write_data_valid;
assign dma_write_data_o = s_sch_write_data;
assign dma_write_keep_o = s_sch_write_keep;
assign dma_write_last_o = s_sch_write_last;
assign s_dec_job_start = s_launch_accept;
assign s_sch_launch = s_launch_accept;
assign s_dec_retired = s_sch_retired;
assign s_sch_terminal = s_compose_terminal;
// -------------------------------------------------------------
// Core FSM
// -------------------------------------------------------------
always_comb begin
s_state_d = s_state_q;
s_epoch_ack_d = s_epoch_ack_q;
s_quiesce_ack_d = s_quiesce_ack_q;
s_snap_state_d = s_snap_state_q;
s_snap_payload_d = s_snap_payload_q;
s_fault_seen_d = s_fault_seen_q;
s_fault_code_d = s_fault_code_q;
s_fault_addr_d = s_fault_addr_q;
s_fault_info_d = s_fault_info_q;
s_fault_desc_d = s_fault_desc_q;
s_res_code_d = s_res_code_q;
s_res_irq_d = s_res_irq_q;
s_res_completed_d = s_res_completed_q;
snapshot_req_ready_o = 1'b0;
snapshot_resp_valid_o = 1'b0;
// snapshot endpoint: atomic capture of the live or retained bank
unique case (s_snap_state_q)
SnapIdle: begin
snapshot_req_ready_o = 1'b1;
if (snapshot_req_valid_i) begin
s_snap_payload_d = s_job_busy ? {s_sch_counters, s_job_id_q} :
{s_sch_term_counters, s_term_job_id_q};
s_snap_state_d = SnapRespond;
end
end
SnapRespond: begin
snapshot_resp_valid_o = 1'b1;
if (snapshot_resp_ready_i) begin
s_snap_state_d = SnapIdle;
end
end
default: s_snap_state_d = SnapIdle;
endcase
// first-fault capture (also during abort/reset drains: a fault during
// cancellation converts the terminal record to ERROR)
if (s_evt_any && !s_fault_seen_q &&
((s_state_q == CoreRun) || (s_state_q == CoreDrainAbort) ||
(s_state_q == CoreDrainReset))) begin
s_fault_seen_d = 1'b1;
s_fault_code_d = s_evt_code;
s_fault_addr_d = s_evt_addr;
s_fault_info_d = s_evt_info;
s_fault_desc_d = s_evt_desc;
end
unique case (s_state_q)
CoreIdle: begin
if (s_launch_accept) begin
s_state_d = CoreRun;
end
end
CoreRun: begin
if (s_evt_any && !s_fault_seen_q) begin
s_state_d = CoreDrainFault;
end else if (s_all_done_q) begin
s_res_code_d = 3'(`APB4_NPU__RESULT_CODE_DONE);
s_res_irq_d = 3'b001;
s_res_completed_d = {16'd0, s_job_count_q};
s_fault_code_d = 4'(`APB4_NPU__FAULT_CODE_NONE);
s_fault_desc_d = 32'hffff_ffff;
s_fault_addr_d = 32'd0;
s_fault_info_d = 32'd0;
s_state_d = CorePublish;
end else if (abort_i) begin
s_state_d = CoreDrainAbort;
end else if (quiesce_req_i) begin
s_state_d = CoreDrainAbort;
end
end
CoreDrainFault, CoreDrainAbort: begin
if (s_drained) begin
if (s_fault_seen_q) begin
s_res_code_d = 3'(`APB4_NPU__RESULT_CODE_ERROR);
s_res_irq_d = 3'b010;
end else begin
s_res_code_d = 3'(`APB4_NPU__RESULT_CODE_ABORTED);
s_res_irq_d = 3'b100;
s_fault_code_d = 4'(`APB4_NPU__FAULT_CODE_NONE);
s_fault_desc_d = 32'hffff_ffff;
s_fault_addr_d = 32'd0;
s_fault_info_d = 32'd0;
end
s_res_completed_d = s_sch_completed;
s_state_d = CorePublish;
end
end
CoreDrainReset: begin
if (s_drained) begin
// stop/drain completed: re-acknowledge the fresh epoch, then
// deliver the retained cancellation record
s_epoch_ack_d = epoch_req_i;
if (s_fault_seen_q) begin
s_res_code_d = 3'(`APB4_NPU__RESULT_CODE_ERROR);
s_res_irq_d = 3'b010;
end else begin
s_res_code_d = 3'(`APB4_NPU__RESULT_CODE_RESET_CANCELLED);
s_res_irq_d = 3'b010;
s_fault_code_d = 4'(`APB4_NPU__FAULT_CODE_RESET_CANCELLED);
s_fault_desc_d = 32'hffff_ffff;
s_fault_addr_d = 32'd0;
s_fault_info_d = 32'd0;
end
s_res_completed_d = s_sch_completed;
s_state_d = CorePublish;
end
end
CorePublish: begin
if (result_ready_i) begin
s_state_d = CoreIdle;
end
end
default: s_state_d = CoreIdle;
endcase
// Quiesce acknowledge: idle endpoint answers immediately; an active one
// answers once the terminal result is latched in the result mailbox.
if (quiesce_req_i) begin
s_quiesce_ack_d = (s_state_q == CoreIdle) || (s_state_q == CorePublish);
end else begin
s_quiesce_ack_d = 1'b0;
end
// Epoch restart (link loss, resource reset, coordinated flush): force a
// safe state; idle is re-acknowledged immediately, an active or
// terminating job is cancelled and reconciled through the drain.
if (s_epoch_restart) begin
s_snap_state_d = SnapIdle;
if (s_state_q == CoreIdle) begin
s_epoch_ack_d = epoch_req_i;
end else begin
s_state_d = CoreDrainReset;
end
end
// Coordinated fabric flush cancels transport immediately; the epoch
// restart that follows reconciles the endpoint.
if (flush_i && (s_state_q != CoreIdle)) begin
s_state_d = CoreDrainReset;
end
end
assign result_valid_o = (s_state_q == CorePublish);
assign result_data_o = {
s_res_irq_q,
s_fault_code_q,
s_res_code_q,
s_fault_info_q,
s_fault_addr_q,
s_fault_desc_q,
s_res_completed_q,
s_job_id_q
};
assign snapshot_resp_data_o = s_snap_payload_q;
always_ff @(posedge clk_hp_i or negedge rst_hp_n_i) begin
if (!rst_hp_n_i) begin
s_state_q <= CoreIdle;
s_snap_state_q <= SnapIdle;
s_job_base_q <= '0;
s_job_id_q <= '0;
s_timeout_q <= '0;
s_job_count_q <= '0;
s_term_job_id_q <= '0;
s_fault_seen_q <= 1'b0;
s_fault_code_q <= '0;
s_fault_addr_q <= '0;
s_fault_info_q <= '0;
s_fault_desc_q <= `APB4_NPU__FAULT_DESCRIPTOR_RESET;
s_res_code_q <= '0;
s_res_irq_q <= '0;
s_res_completed_q <= '0;
s_snap_payload_q <= '0;
s_all_done_q <= 1'b0;
end else begin
s_state_q <= s_state_d;
s_snap_state_q <= s_snap_state_d;
s_snap_payload_q <= s_snap_payload_d;
s_fault_seen_q <= s_fault_seen_d;
s_fault_code_q <= s_fault_code_d;
s_fault_addr_q <= s_fault_addr_d;
s_fault_info_q <= s_fault_info_d;
s_fault_desc_q <= s_fault_desc_d;
s_res_code_q <= s_res_code_d;
s_res_irq_q <= s_res_irq_d;
s_res_completed_q <= s_res_completed_d;
if (s_launch_accept) begin
s_job_base_q <= launch_data_i[`APB4_NPU__LAUNCH_JOB_BASE+:32];
s_job_id_q <= launch_data_i[`APB4_NPU__LAUNCH_JOB_ID+:32];
s_timeout_q <= launch_data_i[`APB4_NPU__LAUNCH_TIMEOUT_CYCLES+:32];
s_job_count_q <= launch_data_i[`APB4_NPU__LAUNCH_JOB_COUNT+:16];
s_fault_seen_q <= 1'b0;
s_fault_desc_q <= `APB4_NPU__FAULT_DESCRIPTOR_RESET;
end
if (s_compose_terminal) begin
s_term_job_id_q <= s_job_id_q;
end
if ((s_state_q == CoreIdle) || (s_state_q == CorePublish)) begin
s_all_done_q <= 1'b0;
end else if (s_dec_all_done) begin
s_all_done_q <= 1'b1;
end
if (s_state_q == CoreIdle) begin
s_fault_seen_q <= 1'b0;
end
end
end
dffr #(
.DATA_WIDTH(EpochWidth)
) u_epoch_ack (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.dat_i (s_epoch_ack_d),
.dat_o (s_epoch_ack_q)
);
dffr #(
.DATA_WIDTH(1)
) u_quiesce_ack (
.clk_i (clk_hp_i),
.rst_n_i(rst_hp_n_i),
.dat_i (s_quiesce_ack_d),
.dat_o (s_quiesce_ack_q)
);
npu_job_decoder u_job_decoder (
.clk_hp_i (clk_hp_i),
.rst_hp_n_i (rst_hp_n_i),
.clear_i (s_stop),
.block_new_i (block_new_i),
.pause_ok_o (s_dec_pause_ok),
.job_start_i (s_dec_job_start),
.job_base_i (launch_data_i[`APB4_NPU__LAUNCH_JOB_BASE+:32]),
.job_count_i (launch_data_i[`APB4_NPU__LAUNCH_JOB_COUNT+:16]),
.read_req_valid_o (s_dec_read_req_valid),
.read_req_ready_i (s_dec_read_req_ready),
.read_addr_o (s_dec_read_addr),
.read_bytes_o (s_dec_read_bytes),
.read_data_valid_i (s_dec_read_data_valid),
.read_data_ready_o (s_dec_read_data_ready),
.read_data_i (dma_read_data_i),
.read_last_i (dma_read_last_i),
.dma_read_busy_i (dma_read_busy_i),
.dma_fault_i (dma_fault_i),
.dma_fault_code_i (dma_fault_code_i),
.dma_fault_addr_i (dma_fault_addr_i),
.dma_fault_resp_i (dma_fault_resp_i),
.rec_valid_o (s_rec_valid),
.rec_ready_i (s_rec_ready),
.rec_opcode_o (s_rec_opcode),
.rec_h_o (s_rec_h),
.rec_w_o (s_rec_w),
.rec_cin_o (s_rec_cin),
.rec_cout_o (s_rec_cout),
.rec_oh_o (s_rec_oh),
.rec_ow_o (s_rec_ow),
.rec_input0_base_o (s_rec_input0_base),
.rec_output_base_o (s_rec_output_base),
.rec_weight_base_o (s_rec_weight_base),
.rec_param_base_o (s_rec_param_base),
.rec_input0_row_bytes_o(s_rec_irow),
.rec_output_row_bytes_o(s_rec_orow),
.rec_kh_o (s_rec_kh),
.rec_kw_o (s_rec_kw),
.rec_sh_o (s_rec_sh),
.rec_sw_o (s_rec_sw),
.rec_pad_top_o (s_rec_pad_top),
.rec_pad_left_o (s_rec_pad_left),
.rec_input0_zero_o (s_rec_in0_zero),
.rec_tile_h_o (s_rec_tile_h),
.rec_tile_w_o (s_rec_tile_w),
.rec_k_slice_o (s_rec_k_slice),
.rec_input1_base_o (s_rec_input1_base),
.rec_input1_row_bytes_o(s_rec_input1_row_bytes),
.rec_input1_zero_o (s_rec_in1_zero),
.rec_output_zero_o (s_rec_out_zero),
.rec_act_min_o (s_rec_act_min),
.rec_act_max_o (s_rec_act_max),
.rec_weight_bytes_o (s_rec_weight_bytes),
.rec_param_bytes_o (s_rec_param_bytes),
.rec_desc_index_o (s_rec_desc_index),
.retired_i (s_dec_retired),
.all_done_o (s_dec_all_done),
.busy_o (s_dec_busy),
.cur_desc_index_o (s_dec_cur_index),
.progress_o (s_dec_progress),
.fault_req_o (s_dec_fault_req),
.fault_code_o (s_dec_fault_code),
.fault_addr_o (s_dec_fault_addr),
.fault_info_o (s_dec_fault_info),
.fault_desc_o (s_dec_fault_desc)
);
npu_scheduler u_scheduler (
.clk_hp_i (clk_hp_i),
.rst_hp_n_i (rst_hp_n_i),
.clear_i (s_clear),
.stop_i (s_stop),
.block_new_i (block_new_i),
.pause_ok_o (s_sch_pause_ok),
.launch_i (s_sch_launch),
.timeout_cycles_i (launch_data_i[`APB4_NPU__LAUNCH_TIMEOUT_CYCLES+:32]),
.job_active_i (s_state_q == CoreRun),
.pause_active_i (pause_active_o),
.rec_valid_i (s_rec_valid),
.rec_ready_o (s_rec_ready),
.rec_opcode_i (s_rec_opcode),
.rec_h_i (s_rec_h),
.rec_w_i (s_rec_w),
.rec_cin_i (s_rec_cin),
.rec_cout_i (s_rec_cout),
.rec_oh_i (s_rec_oh),
.rec_ow_i (s_rec_ow),
.rec_input0_base_i (s_rec_input0_base),
.rec_output_base_i (s_rec_output_base),
.rec_weight_base_i (s_rec_weight_base),
.rec_param_base_i (s_rec_param_base),
.rec_input0_row_bytes_i(s_rec_irow),
.rec_output_row_bytes_i(s_rec_orow),
.rec_kh_i (s_rec_kh),
.rec_kw_i (s_rec_kw),
.rec_sh_i (s_rec_sh),
.rec_sw_i (s_rec_sw),
.rec_pad_top_i (s_rec_pad_top),
.rec_pad_left_i (s_rec_pad_left),
.rec_input0_zero_i (s_rec_in0_zero),
.rec_tile_h_i (s_rec_tile_h),
.rec_tile_w_i (s_rec_tile_w),
.rec_k_slice_i (s_rec_k_slice),
.rec_input1_base_i (s_rec_input1_base),
.rec_input1_row_bytes_i(s_rec_input1_row_bytes),
.rec_input1_zero_i (s_rec_in1_zero),
.rec_output_zero_i (s_rec_out_zero),
.rec_act_min_i (s_rec_act_min),
.rec_act_max_i (s_rec_act_max),
.rec_weight_bytes_i (s_rec_weight_bytes),
.rec_param_bytes_i (s_rec_param_bytes),
.rec_desc_index_i (s_rec_desc_index),
.retired_o (s_sch_retired),
.completed_o (s_sch_completed),
.read_req_valid_o (s_sch_read_req_valid),
.read_req_ready_i (s_sch_read_req_ready),
.read_addr_o (s_sch_read_addr),
.read_bytes_o (s_sch_read_bytes),
.read_data_valid_i (s_sch_read_data_valid),
.read_data_ready_o (s_sch_read_data_ready),
.read_data_i (dma_read_data_i),
.read_keep_i (dma_read_keep_i),
.read_last_i (dma_read_last_i),
.write_req_valid_o (s_sch_write_req_valid),
.write_req_ready_i (dma_write_req_ready_i),
.write_addr_o (s_sch_write_addr),
.write_bytes_o (s_sch_write_bytes),
.write_data_valid_o (s_sch_write_data_valid),
.write_data_ready_i (dma_write_data_ready_i),
.write_data_o (s_sch_write_data),
.write_keep_o (s_sch_write_keep),
.write_last_o (s_sch_write_last),
.write_done_i (dma_write_done_i),
.dma_read_busy_i (dma_read_busy_i),
.dma_write_busy_i (dma_write_busy_i),
.dma_pause_ack_i (dma_pause_ack_i),
.dma_fault_i (dma_fault_i),
.dma_fault_code_i (dma_fault_code_i),
.dma_fault_addr_i (dma_fault_addr_i),
.dma_fault_resp_i (dma_fault_resp_i),
.dma_read_bytes_i (dma_read_bytes_i),
.dma_write_bytes_i (dma_write_bytes_i),
.dma_stall_cycles_i (dma_stall_cycles_i),
.progress_i (s_dec_progress),
.terminal_i (s_sch_terminal),
.counters_o (s_sch_counters),
.term_counters_o (s_sch_term_counters),
.fault_req_o (s_sch_fault_req),
.fault_code_o (s_sch_fault_code),
.fault_addr_o (s_sch_fault_addr),
.fault_info_o (s_sch_fault_info),
.fault_desc_o (s_sch_fault_desc),
.busy_o (s_sch_busy),
.drained_o (s_sch_drained)
);
end
endmodule