// AI Accelerator Top-Level Module
module accel_top #(
    parameter DATA_W = 16,
    parameter ADDR_W = 16,
    parameter NUM_PE  = 4,
    parameter MEM_DEPTH = 1024
)(
    input  wire                  clk,
    input  wire                  rst_n,
    input  wire                  cmd_valid,
    input  wire [7:0]           cmd_opcode,
    input  wire [ADDR_W-1:0]    cmd_addr,
    input  wire [DATA_W-1:0]    cmd_data,
    output wire                  cmd_ready,
    output wire [ADDR_W-1:0]    mem_addr,
    output wire                  mem_we,
    output wire [DATA_W-1:0]    mem_wdata,
    input  wire [DATA_W-1:0]    mem_rdata,
    output wire                  result_valid,
    output wire [DATA_W-1:0]    result_data,
    output wire                  done
);
    localparam S_IDLE=0, S_DECODE=1, S_LOAD=2, S_COMPUTE=3, S_STORE=4, S_FINISH=5;
    reg [2:0] state, nxt;
    reg [ADDR_W-1:0] pc;
    always_ff @(posedge clk or negedge rst_n)
        if (!rst_n) begin state<=S_IDLE; pc<='0; end
        else begin state<=nxt; pc<=pc+1; end
    always_comb begin nxt=state;
        case(state)
          S_IDLE:    nxt=cmd_valid?S_DECODE:S_IDLE;
          S_DECODE:  case(cmd_opcode) 8'h01:nxt=S_LOAD; 8'h02:nxt=S_COMPUTE; 8'h03:nxt=S_STORE; default:nxt=S_IDLE; endcase
          S_LOAD:    nxt=(pc>=MEM_DEPTH)?S_DECODE:S_LOAD;
          S_COMPUTE: nxt=(pc>=MEM_DEPTH)?S_STORE:S_COMPUTE;
          S_STORE:   nxt=S_FINISH;
          S_FINISH:  nxt=S_IDLE;
        endcase
    end
    wire [DATA_W-1:0] pe_out[0:NUM_PE-1];
    genvar i; generate for(i=0;i<NUM_PE;i++) begin:gp
        pe_unit #(.W(DATA_W)) u_pe(.clk(clk),.rst_n(rst_n),.a(mem_rdata),.b(cmd_data),.op(cmd_opcode[1:0]),.y(pe_out[i]));
    end endgenerate
    assign result_data=pe_out[0]; assign result_valid=(state==S_STORE);
    assign done=(state==S_FINISH); assign cmd_ready=(state==S_IDLE);
    assign mem_addr=pc; assign mem_we=(state==S_STORE); assign mem_wdata=result_data;
endmodule

module pe_unit #(parameter W=16)(
    input clk, rst_n, input signed [W-1:0] a,b, input [1:0] op, output reg signed [W-1:0] y
);
    always_ff @(posedge clk or negedge rst_n)
        if(!rst_n) y<='0; else case(op) 2'b00:y<=a+b; 2'b01:y<=a-b; 2'b10:y<=a&b; 2'b11:y<=a|b; endcase
endmodule