// NPU Instruction Set Architecture — 32-bit RISC-style ISA
module npu_decoder #(parameter DW=16, AW=20)(
    input clk, rst_n,
    input [31:0] instr, input instr_valid,
    // Decoded fields
    output reg [6:0]  opcode,     // [31:25]
    output reg [4:0]  rd,         // [24:20] destination register
    output reg [4:0]  rs1,        // [19:15] source register 1
    output reg [4:0]  rs2,        // [14:10] source register 2
    output reg [4:0]  rs3,        // [9:5] source register 3 (for MAC)
    output reg [4:0]  func,       // [4:0] function code
    // Immediate value
    output reg signed [AW-1:0] imm,
    // Control signals
    output reg        reg_we,     // Register file write enable
    output reg [2:0]  alu_op,     // ALU operation select
    output reg        mem_en,     // Memory access enable
    output reg        mem_wr,     // Memory write (1) / read (0)
    output reg        sa_en,      // Systolic array enable
    output reg        is_imm      // Immediate operand flag
);
    // Opcode definitions
    localparam OP_NOP    = 7'd0;
    localparam OP_LOAD   = 7'd1;  // LOAD rd, imm(addr)
    localparam OP_STORE  = 7'd2;  // STORE rs1, imm(addr)
    localparam OP_ADD    = 7'd3;  // ADD rd, rs1, rs2
    localparam OP_MAC    = 7'd4;  // MAC rd, rs1, rs2, rs3
    localparam OP_MATMUL = 7'd5;  // MATMUL rd, rs1, rs2 (systolic array)
    localparam OP_QUANT  = 7'd6;  // QUANT rd, rs1, imm(scale_idx)
    localparam OP_CONV   = 7'd7;  // CONV rd, rs1, rs2, func(kernel_size)
    localparam OP_RELU   = 7'd8;  // ReLU rd, rs1
    localparam OP_POOL   = 7'd9;  // POOL rd, rs1, func(type)
    localparam OP_SMAX   = 7'd10; // SOFTMAX rd, rs1, imm(len)
    localparam OP_BRANCH = 7'd11; // BRANCH rs1, rs2, imm(offset)
    localparam OP_HALT   = 7'd63;

    always_ff @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin opcode<=0; rd<=0; rs1<=0; rs2<=0; rs3<=0; func<=0; imm<=0;
            reg_we<=0; alu_op<=0; mem_en<=0; mem_wr<=0; sa_en<=0; is_imm<=0; end
        else if(instr_valid) begin
            opcode <= instr[31:25];
            rd      <= instr[24:20];
            rs1     <= instr[19:15];
            rs2     <= instr[14:10];
            rs3     <= instr[9:5];
            func    <= instr[4:0];
            imm     <= {{(AW-12){instr[19]}}, instr[19:8]}; // Sign-extended 12-bit imm
            // Default control signals
            reg_we <= 0; mem_en <= 0; mem_wr <= 0; sa_en <= 0; is_imm <= 0;
            case(instr[31:25])
              OP_NOP:    begin end
              OP_LOAD:   begin reg_we<=1; mem_en<=1; mem_wr<=0; end
              OP_STORE:  begin mem_en<=1; mem_wr<=1; end
              OP_ADD:    begin reg_we<=1; alu_op<=3'd0; end
              OP_MAC:    begin reg_we<=1; alu_op<=3'd1; end
              OP_MATMUL: begin reg_we<=1; sa_en<=1; end
              OP_QUANT:  begin reg_we<=1; alu_op<=3'd2; is_imm<=1; end
              OP_CONV:   begin reg_we<=1; sa_en<=1; end
              OP_RELU:   begin reg_we<=1; alu_op<=3'd3; end
              OP_POOL:   begin reg_we<=1; alu_op<=3'd4; end
              OP_SMAX:   begin reg_we<=1; alu_op<=3'd5; end
              OP_BRANCH: begin end
              OP_HALT:   begin end
              default:   begin end
            endcase
        end
    end
endmodule

// NPU Register File: 32 general-purpose registers
module npu_regfile #(parameter DW=16, NR=32)(
    input clk, rst_n,
    input [4:0] raddr1, raddr2, raddr3,
    output reg signed [DW-1:0] rdata1, rdata2, rdata3,
    input [4:0] waddr, input signed [DW-1:0] wdata, input we
);
    reg signed [DW-1:0] regs [0:NR-1];
    always_ff @(posedge clk) begin
        if(we && waddr>0) regs[waddr] <= wdata;
    end
    always_comb begin rdata1=regs[raddr1]; rdata2=regs[raddr2]; rdata3=regs[raddr3]; end
endmodule