// Edge AI SoC — NPU + CPU + DMA + Peripherals
module edge_soc #(
    parameter DW=16, AW=20, NPU_PE=16, SRAM_SIZE=65536
)(
    input clk, rst_n,
    // CPU interface (simplified)
    input [31:0] cpu_instr, input cpu_instr_valid,
    input [AW-1:0] cpu_data_addr, input [15:0] cpu_data_in, input cpu_data_we, input cpu_data_en,
    output reg [15:0] cpu_data_out,
    // NPU interface
    output reg npu_start, input npu_done,
    output reg [7:0] npu_cmd, output reg [AW-1:0] npu_param0, npu_param1,
    input [15:0] npu_result,
    // DMA
    output reg [31:0] dma_src_addr, dma_dst_addr,
    output reg [15:0] dma_len, output reg dma_start, input dma_done,
    // SRAM
    output reg [AW-1:0] sram_addr, output reg sram_we, output reg [15:0] sram_wdata,
    input [15:0] sram_rdata,
    // Interrupt
    output reg irq_npu_done, output reg irq_dma_done
);
    // SoC FSM
    localparam S_IDLE=0, S_CPU_EXEC=1, S_NPU_WAIT=2, S_DMA_WAIT=3, S_RESP=4;
    reg [2:0] soc_state;
    reg [7:0] npu_cmd_reg;
    always_ff @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin soc_state<=S_IDLE; npu_start<=0; dma_start<=0; irq_npu_done<=0; irq_dma_done<=0; end
        else begin irq_npu_done<=0; irq_dma_done<=0;
            case(soc_state)
              S_IDLE: if(cpu_instr_valid) begin
                    case(cpu_instr[31:28])
                      4'h1: begin // NPU command
                        npu_start<=1; npu_cmd<=cpu_instr[27:20]; npu_param0<=cpu_instr[19:0]; soc_state<=S_NPU_WAIT;
                      end
                      4'h2: begin // DMA command
                        dma_start<=1; dma_src_addr<=cpu_instr[27:12]; dma_len<=cpu_instr[11:0]; soc_state<=S_DMA_WAIT;
                      end
                      4'h3: begin // SRAM access
                        sram_addr<=cpu_data_addr; sram_we<=cpu_data_we; sram_wdata<=cpu_data_in;
                        if(!cpu_data_we) cpu_data_out<=sram_rdata;
                      end
                      default: begin end
                    endcase
                end
              S_NPU_WAIT: begin npu_start<=0; if(npu_done) begin irq_npu_done<=1; soc_state<=S_IDLE; end end
              S_DMA_WAIT: begin dma_start<=0; if(dma_done) begin irq_dma_done<=1; soc_state<=S_IDLE; end end
              default: soc_state<=S_IDLE;
            endcase
        end
    end
endmodule

// Peripheral: UART for debug output
module uart_tx #(parameter CLK_HZ=200000000, BAUD=115200)(
    input clk, rst_n, input [7:0] tx_data, input tx_valid, output reg tx_ready, output reg tx_pin
);
    localparam DIV = CLK_HZ / BAUD;
    reg [15:0] cnt; reg [3:0] bit_cnt; reg [9:0] shift_reg;
    always_ff @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin cnt<=0; bit_cnt<=0; tx_ready<=1; tx_pin<=1; shift_reg<=0; end
        else if(tx_valid && tx_ready) begin shift_reg<={1'b1,tx_data,1'b0}; bit_cnt<=10; tx_ready<=0; cnt<=0; end
        else if(!tx_ready) begin
            if(cnt>=DIV-1) begin cnt<=0; tx_pin<=shift_reg[0]; shift_reg<=shift_reg>>1; bit_cnt<=bit_cnt-1;
                if(bit_cnt<=1) tx_ready<=1; end
            else cnt<=cnt+1;
        end
    end
endmodule