Untitled

Anonymous
plain_text
02/13/2026 1:30 PM
70.7 KB
11
Indexable
`timescale 1ns/1ps

module top_layer2_all(
    input  wire clk,
    input  wire rst,
    output wire done_all,



 
    

    // === debug: padding ===
    output wire        dbg_pad_we,
    output wire [9:0]  dbg_pad_addr,
    output wire [7:0]  dbg_pad_din,

   

    output wire        dbg_pp_bram_we,
    output wire [7:0]  dbg_pp_bram_addr,
    output wire [7:0]  dbg_pp_bram_din,

    // === pipeline debug (exported by CRP) ===
    output wire        dbg_s5_v,
    output wire [5:0]  dbg_s5_tile,
    output wire [1:0]  dbg_s5_sub,
    output wire [7:0]  dbg_s5_pix_relu,

    output wire        dbg_s3_v,
    output wire [5:0]  dbg_s3_tile,
    output wire [1:0]  dbg_s3_sub,
    output wire signed [31:0] dbg_s3_sum32,

    output wire [7:0] dbg_pool_acc_max,
    output wire [5:0] dbg_pool_acc_tile,
    output wire [3:0] dbg_pool_acc_seen,

    // === debug: stg1 read check ===
    output wire        dbg_stg1_v,
    output wire [5:0]  dbg_stg1_tile,
    output wire [1:0]  dbg_stg1_sub,
    output wire [8:0]  dbg_stg1_mask9,
   
    
    
    
    // ---- flatten write to BRAM (真實寫入) ----
    output wire        dbg_flat_wr_we,
    output wire [7:0]  dbg_flat_wr_addr,

    
    output wire        dbg_mask_wr_we,
    output wire [7:0]  dbg_mask_wr_addr,
    output wire [8:0]  dbg_mask_wr_data,
    
    // ---- CRP read from BRAM (實際讀取) ----
    output wire [7:0]  dbg_flat_rd_addr,
    
    
    output wire [7:0]  dbg_mask_rd_addr,
    output wire [8:0]  dbg_mask_rd_data,
    
    
    // === debug: CRP issue (addr actually sent to BRAM portB) ===
    output wire        dbg_issue_v,
    output wire [5:0]  dbg_issue_tile,
    output wire [1:0]  dbg_issue_sub,
    output wire [7:0]  dbg_issue_addr,
    
    // === debug: BRAM PortB raw taps (what BRAM really sees/returns) ===
    output wire [7:0]  dbg_flat_b_addr,
  
    output wire [7:0]  dbg_mask_b_addr,
    output wire [8:0]  dbg_mask_b_dout

    

    
);

    // =============================================================
    // (A) Flatten -> flat72/mask write (USE top_module2 pending/hold)
    // =============================================================
    wire        flat_we;
    wire [7:0]  flat_waddr;
    wire [71:0] flat_wdata;

    wire        mask_we;
    wire [7:0]  mask_waddr;
    wire [8:0]  mask_wdata;

    wire flatten_all_done;

    top_module2 flatten_u (
        .clk(clk),
        .rst(rst),
        .data_out(),

        .flat_we(flat_we),
        .flat_waddr(flat_waddr),
        .flat_wdata(flat_wdata),

        .mask_we(mask_we),
        .mask_waddr(mask_waddr),
        .mask_wdata(mask_wdata),

        .flatten_all_done(flatten_all_done),

        .dbg_vld(),
        .dbg_patch(),
        .dbg_elem(),
        .dbg_data()
    );

    // =============================================================
    // (B) Flat72 / Mask BRAM : Simple Dual Port
    //   Port A = write by Flatten (stable whole cycle -> SAFE)
    //   Port B = read  by CRP
    // =============================================================
    wire [7:0]  conv_c0_f0_flat_addr;
    wire [71:0] flat72_doutb;
    // -------------------------------------------------
    // Extra register slice for BRAM write inputs
    // (improves setup/hold into RAMB36/RAMB18)
    // -------------------------------------------------
    // ===== flat =====
reg        flat_we_r0, flat_we_r1;
reg [7:0]  flat_waddr_r;
reg [71:0] flat_wdata_r;

// ===== mask =====
reg        mask_we_r0, mask_we_r1;
reg [7:0]  mask_waddr_r;
reg [8:0]  mask_wdata_r;

always @(posedge clk or posedge rst) begin
  if (rst) begin
    flat_we_r0   <= 1'b0;
    flat_we_r1   <= 1'b0;
    flat_waddr_r <= 8'd0;
    flat_wdata_r <= 72'd0;

    mask_we_r0   <= 1'b0;
    mask_we_r1   <= 1'b0;
    mask_waddr_r <= 8'd0;
    mask_wdata_r <= 9'd0;
  end else begin
    // addr/data 立刻鎖住
    flat_waddr_r <= flat_waddr;
    flat_wdata_r <= flat_wdata;

    mask_waddr_r <= mask_waddr;
    mask_wdata_r <= mask_wdata;

    // WE 延 2 拍(比 addr/data 慢)
    flat_we_r0 <= flat_we;
    flat_we_r1 <= flat_we_r0;

    mask_we_r0 <= mask_we;
    mask_we_r1 <= mask_we_r0;
  end
end

    
    

    localparam ST_FLAT = 2'd0,
               ST_CRP  = 2'd1,
               ST_PAD  = 2'd2,
               ST_DONE = 2'd3;
               
    reg [1:0] stage, stage_d;
               




   blk_mem_gen_flat72_256 bram_flat72 (
    .clka (clk),
    .ena (1'b1),
   .wea   (flat_we_r1),
    .addra (flat_waddr_r),
    .dina  (flat_wdata_r),
    
    .clkb (clk),
    .enb (1'b1),      //stage == ST_CRP
    .addrb (conv_c0_f0_flat_addr),
    .doutb (flat72_doutb)
   );

    wire [7:0]  conv_c0_f0_mask_addr;
    wire [8:0]  mask_doutb;

   blk_mem_gen_mask2 bram_mask (
    .clka (clk),
    .ena (1'b1),
    .wea   (mask_we_r1),
    .addra (mask_waddr_r),
    .dina  (mask_wdata_r),
    
    .clkb (clk),
    .enb (1'b1),                   // stage == ST_CRP
    .addrb (conv_c0_f0_mask_addr),
    .doutb (mask_doutb)
   );

    wire [71:0] conv_c0_f0_flat_dout = flat72_doutb;
    wire [8:0]  conv_c0_f0_mask_dout = mask_doutb;

    // =============================================================
    // (C) Track: last patch(255) actually written into BOTH BRAMs
    // =============================================================
    reg last_flat_written, last_mask_written;

  


    // =============================================================
    // (D) Weight BRAM (filter0)
    // =============================================================
    wire [3:0]        conv_f0_w_addr;
    wire signed [7:0] conv_f0_w_dout;

    blk_mem_gen_weight_f0 bram_w_f0 (
        .clka  (clk),
        .ena   (1'b1),
        .addra (conv_f0_w_addr),
        .douta (conv_f0_w_dout)
    );

    // =============================================================
    // (E) Stage FSM: FLAT -> CRP -> PAD -> DONE
    // =============================================================

               
                          
               


    always @(posedge clk or posedge rst) begin
        if (rst) stage_d <= ST_FLAT;
        else     stage_d <= stage;
    end
    wire stage_sw = (stage != stage_d);

    // ---- flat_done latch + drain ----
    reg       flat_done_lat;
    reg [3:0] flat_drain;

    always @(posedge clk or posedge rst) begin
        if (rst) begin
            flat_done_lat <= 1'b0;
            flat_drain    <= 4'd0;
        end else begin
            // clear when re-enter FLAT stage
            if ((stage == ST_FLAT) && stage_sw) begin
                flat_done_lat <= 1'b0;
                flat_drain    <= 4'd0;
                // also clear last_written here if you want strict per-run:
               
            end

            if ((stage == ST_FLAT) && flatten_all_done) begin
                flat_done_lat <= 1'b1;
                flat_drain    <= 4'd8; // just a small safety margin
            end

            if (flat_done_lat && (flat_drain != 0))
                flat_drain <= flat_drain - 1'b1;
        end
    end
    
                 
  always @(posedge clk or posedge rst) begin
      if (rst) begin
        last_flat_written <= 1'b0;
        last_mask_written <= 1'b0;
      end else begin
        if ((stage == ST_FLAT) && stage_sw) begin
          last_flat_written <= 1'b0;
          last_mask_written <= 1'b0;
        end else begin
           if (flat_we_r1 && (flat_waddr_r == 8'd255))
              last_flat_written <= 1'b1;
            
            if (mask_we_r1 && (mask_waddr_r == 8'd255))
              last_mask_written <= 1'b1;

        end
      end
    end   
               
               
               
               
    
    

    wire flat_ready_for_crp =
        flat_done_lat && (flat_drain == 0) &&
        last_flat_written && last_mask_written;

    // =============================================================
    // (F) CRP pipeline: Conv+ReLU+Pool (pool writes only)
    // =============================================================
    wire        pool_f0_we;
    wire [5:0]  pool_f0_addr;
    wire [7:0]  pool_f0_din;
    wire        crp_f0_done;

    // CRP start: pulse 1 cycle when entering ST_CRP
    reg crp_start_p;
    always @(posedge clk or posedge rst) begin
        if (rst) crp_start_p <= 1'b0;
        else begin
            crp_start_p <= 1'b0;
            if ((stage == ST_CRP) && stage_sw)
                crp_start_p <= 1'b1;
        end
    end
    wire crp_start = crp_start_p;

    crp_tilepipe_c0_f0 #(
        .READ_LAT_D  (2),   // <<< 必須跟 blk_mem_gen_flat72/mask 的 PortB read latency 一致
        .READ_LAT_W  (2),
        .SHIFT_CONST (31)
    ) crp_u_c0_f0 (
        .clk(clk),
        .rst(rst),
        .start(crp_start),
        .done(crp_f0_done),

        .conv_c0_flat_addr(conv_c0_f0_flat_addr),
        .conv_c0_flat_dout(conv_c0_f0_flat_dout),
        .conv_c0_mask_addr(conv_c0_f0_mask_addr),
        .conv_c0_mask_dout(conv_c0_f0_mask_dout),

        .conv_f0_w_addr(conv_f0_w_addr),
        .conv_f0_w_dout(conv_f0_w_dout),

        .pool_f0_we(pool_f0_we),
        .pool_f0_addr(pool_f0_addr),
        .pool_f0_din(pool_f0_din),

        // debug from CRP (避免 hierarchy 參考)
        .dbg_s5_v(dbg_s5_v),
        .dbg_s5_tile(dbg_s5_tile),
        .dbg_s5_sub(dbg_s5_sub),
        .dbg_s5_pix_relu(dbg_s5_pix_relu),

        .dbg_s3_v(dbg_s3_v),
        .dbg_s3_tile(dbg_s3_tile),
        .dbg_s3_sub(dbg_s3_sub),
        .dbg_s3_sum32(dbg_s3_sum32),

        .dbg_pool_acc_max(dbg_pool_acc_max),
        .dbg_pool_acc_tile(dbg_pool_acc_tile),
        .dbg_pool_acc_seen(dbg_pool_acc_seen),

        .dbg_stg1_v(dbg_stg1_v),
        .dbg_stg1_tile(dbg_stg1_tile),
        .dbg_stg1_sub(dbg_stg1_sub),
        .dbg_stg1_mask9(dbg_stg1_mask9),
        .dbg_stg1_flat72(dbg_stg1_flat72),
        .dbg_issue_v   (dbg_issue_v),
        .dbg_issue_tile(dbg_issue_tile),
        .dbg_issue_sub (dbg_issue_sub),
        .dbg_issue_addr(dbg_issue_addr)
    
    );

    // =============================================================
    // (G) Pooling2 BRAM: Simple Dual Port
    //   Port A = write by CRP (pool_f0_* are stable for 1 cycle)
    //   Port B = read  by Padding
    // =============================================================
  // =============================================================
    // (G) Pooling2 BRAM write commit stage (capture -> next-cycle write)
    // =============================================================
    reg        pp_pend_q;
    reg        pp_we_q;
    reg [5:0]  pp_addr_hold;
    reg [7:0]  pp_din_hold;
    
    always @(posedge clk or posedge rst) begin
      if (rst) begin
        pp_pend_q   <= 1'b0;
        pp_we_q     <= 1'b0;
        pp_addr_hold<= 6'd0;
        pp_din_hold <= 8'd0;
      end else begin
        // default: no write
        pp_we_q   <= pp_pend_q;
        pp_pend_q <= 1'b0;
    
        // capture request (from CRP)
        if (pool_f0_we) begin
          pp_addr_hold <= pool_f0_addr;  // 6-bit
          pp_din_hold  <= pool_f0_din;
          pp_pend_q    <= 1'b1;
        end
      end
    end
    
    wire        pool2_wea   = pp_we_q;
    wire [5:0]  pool2_addra = pp_addr_hold;
    wire [7:0]  pool2_dina  = pp_din_hold;
    
    // ---- Pooling2 BRAM PortB read for padding ----
    wire [9:0] padding_addr_in;
    wire [7:0] bram_pooling2_doutb;
    wire [7:0] padding_data_in = bram_pooling2_doutb;

    
    blk_mem_gen_pooling2 bram_pooling2 (
        .clka  (clk),
        .ena   (1'b1),
        .wea   (pool2_wea),
        .addra (pool2_addra),
        .dina  (pool2_dina),
    
        .clkb  (clk),
        .enb   (1'b1),        //stage == ST_PAD
        .addrb (padding_addr_in[5:0]),
        .doutb (bram_pooling2_doutb)
    );
    
    
    // =============================================================
    // (H) Padding: 8x8 -> 10x10
    //   start pulse once when entering ST_PAD
    // =============================================================
    reg pad_started;
    always @(posedge clk or posedge rst) begin
        if (rst) pad_started <= 1'b0;
        else begin
            if (stage != ST_PAD) pad_started <= 1'b0;
            else if (!pad_started) pad_started <= 1'b1;
        end
    end
    wire padding_start = (stage == ST_PAD) && !pad_started;

    wire [9:0] padding_addr_out;
    wire [7:0] padding_data_out;
    wire       padding_we_out;
    wire       padding_done;

    padding_8x8_top #(
        .RD_TOTAL_LAT(3)
    ) padding_u (
        .clk(clk),
        .rst(rst),
        .start(padding_start),
        .done(padding_done),

        .addr_in(padding_addr_in),
        .data_in(padding_data_in),

        .addr_out(padding_addr_out),
        .data_out(padding_data_out),
        .we_out(padding_we_out)
    );

    blk_mem_gen_out_padding_10x10 bram_padding_out (
        .clka  (clk),
        .ena   (1'b1),
        .wea   (padding_we_out),
        .addra (padding_addr_out[6:0]),
        .dina  (padding_data_out),
        .douta ()
    );

    // padding done edge
    reg padding_done_d;
    always @(posedge clk or posedge rst) begin
        if (rst) padding_done_d <= 1'b0;
        else     padding_done_d <= padding_done;
    end
    wire padding_done_p = padding_done & ~padding_done_d;

    // =============================================================
    // (I) Stage transition
    // =============================================================
    always @(posedge clk or posedge rst) begin
        if (rst) stage <= ST_FLAT;
        else begin
            case (stage)
                ST_FLAT: if (flat_ready_for_crp) stage <= ST_CRP;
                ST_CRP : if (crp_f0_done)        stage <= ST_PAD;  // 若你還要 drain,可再加 cnt
                ST_PAD : if (padding_done_p)     stage <= ST_DONE;
                ST_DONE: stage <= ST_DONE;
            endcase
        end
    end

    assign done_all = (stage == ST_DONE);

    // =============================================================
    // (J) Debug outputs
    // =============================================================
   

    

  

    assign dbg_pad_we   = padding_we_out;
    assign dbg_pad_addr = padding_addr_out;
    assign dbg_pad_din  = padding_data_out;

 
    assign dbg_pp_bram_we   = pool2_wea;
    assign dbg_pp_bram_addr = {2'b00, pool2_addra};
    assign dbg_pp_bram_din  = pool2_dina;

   assign dbg_flat_wr_we   = flat_we_r1;
    assign dbg_flat_wr_addr = flat_waddr_r;
    
    assign dbg_mask_wr_we   = mask_we_r1;
    assign dbg_mask_wr_addr = mask_waddr_r;
    assign dbg_mask_wr_data = mask_wdata_r;
    
    assign dbg_flat_rd_addr = conv_c0_f0_flat_addr;
   
    
    assign dbg_mask_rd_addr = conv_c0_f0_mask_addr;
    assign dbg_mask_rd_data = mask_doutb;
    
    assign dbg_flat_b_addr = conv_c0_f0_flat_addr;
    
    assign dbg_mask_b_addr = conv_c0_f0_mask_addr;
    assign dbg_mask_b_dout = mask_doutb;
/*
   reg dbg_mask_we_reg;
    always @(posedge clk) begin
        dbg_mask_we_reg <= mask_we;
    end
    assign dbg_mask_wr_we = dbg_mask_we_reg;
    */
  

    
  

endmodule




module top_module2 (
    input  wire        clk,
    input  wire        rst,
    output wire [10:0] data_out,          // debug 用

    // flat BRAM:每 patch 寫一次
    output wire        flat_we,
    output wire [7:0]  flat_waddr,        // 0..255
    output wire [71:0] flat_wdata,

    // mask BRAM:每 patch 寫一次 (維持 9-bit)
    output wire        mask_we,
    output wire [7:0]  mask_waddr,        // 0..255
    output wire [8:0]  mask_wdata,

    output wire        flatten_all_done,
    
    
    output wire        dbg_vld,
    output wire [15:0] dbg_patch,
    output wire [3:0]  dbg_elem,
    output wire [7:0]  dbg_data

);

    // === Wires and Regs ===
    wire [8:0] bram_addr;
    wire [7:0]  bram_data_out;
    wire [7:0]  data_out_internal;
    wire        done;
    wire        start;
    wire [8:0] base_addr;
    wire [15:0] patch_index;
    wire        valid;

    // ---- mask ----
    reg  [3:0]  elem_idx;          // 0..8
    reg  [8:0]  mask_temp;
    wire        is_nz = (data_out_internal != 8'd0);
    reg  [8:0]  mask_next;

    wire        last_elem       = valid && (elem_idx == 4'd8);
    wire [7:0]  write_addr_mask = patch_index[7:0];
    
    reg last_elem_d;
    wire last_elem_p = last_elem & ~last_elem_d;
    
    
    
    always @(posedge clk or posedge rst) begin
      if (rst) last_elem_d <= 1'b0;
      else     last_elem_d <= last_elem;
    end
    


    assign flatten_all_done = (patch_index == 16'd255) && done;

    // --- input BRAM (18x18) ---
    blk_mem_gen_out_padding bram_transform (
        .clka (clk),
        .ena  (1'b1),
        .addra(bram_addr),
        .douta(bram_data_out)
    );

    input_transformation2 #(
        .BRAM_READ_LAT(4)
    ) reader (
        .clk      (clk),
        .rst      (rst),
        .start    (start),
        .base_addr(base_addr),
        .bram_data(bram_data_out),
        .bram_addr(bram_addr),
        .data_out (data_out_internal),
        .done     (done),
        .valid    (valid)
    );

    controller2 patch_ctr (
        .clk        (clk),
        .rst        (rst),
        .done       (done),
        .base_addr  (base_addr),
        .start      (start),
        .patch_index(patch_index)
    );

    assign data_out = {3'b000, data_out_internal};

    // ===============================
    // NEW: patch buffer for 72-bit write
    // ===============================
    reg [7:0] patch_buf [0:8];   // 收 9 筆
    reg clear_buf;

    integer k;

    // 每拍 valid 把當前 element 放到對應位置
   always @(posedge clk or posedge rst) begin
  if (rst) begin
    for (k = 0; k < 9; k = k + 1)
      patch_buf[k] <= 8'd0;
    clear_buf <= 1'b0;
  end else begin
    // 如果上一拍要求清,這拍先清掉(然後把旗標放回 0)
    if (clear_buf) begin
      for (k = 0; k < 9; k = k + 1)
        patch_buf[k] <= 8'd0;
      clear_buf <= 1'b0;
    end

    // 收資料
    if (valid) begin
      patch_buf[elem_idx] <= data_out_internal;

      // 收到 elem 8 → 下一拍清
      if (elem_idx == 4'd8)
        clear_buf <= 1'b1;
    end
  end
end

    reg        dbg_vld_reg;
    reg [15:0] dbg_patch_reg;
    reg [3:0]  dbg_elem_reg;
    reg [7:0]  dbg_data_reg;

    // 2. 在 Clock Edge 更新它們
    always @(posedge clk) begin
        if (rst) begin
            dbg_vld_reg   <= 1'b0;
            dbg_patch_reg <= 16'd0;
            dbg_elem_reg  <= 4'd0;
            dbg_data_reg  <= 8'd0;
        end else begin
            dbg_vld_reg   <= valid;
            dbg_patch_reg <= patch_index;
            dbg_elem_reg  <= elem_idx;
            dbg_data_reg  <= data_out_internal;
        end
    end

    // 3. 輸出連接到暫存器
    assign dbg_vld   = dbg_vld_reg;
    assign dbg_patch = dbg_patch_reg;
    assign dbg_elem  = dbg_elem_reg;
    assign dbg_data  = dbg_data_reg;


    // 72-bit 打包:byte i 放在 [i*8 +: 8]
   wire [71:0] flat_pack_curr;
assign flat_pack_curr = {
    patch_buf[8], patch_buf[7], patch_buf[6], patch_buf[5], patch_buf[4],
    patch_buf[3], patch_buf[2], patch_buf[1], patch_buf[0]
};

reg [71:0] flat_pack_next;
always @(*) begin
    flat_pack_next = flat_pack_curr;
    // last_elem 這拍 patch_buf[elem_idx] 還沒被 nonblocking 更新,所以手動塞 data_out_internal
    case (elem_idx)
        4'd0: flat_pack_next[ 7: 0] = data_out_internal;
        4'd1: flat_pack_next[15: 8] = data_out_internal;
        4'd2: flat_pack_next[23:16] = data_out_internal;
        4'd3: flat_pack_next[31:24] = data_out_internal;
        4'd4: flat_pack_next[39:32] = data_out_internal;
        4'd5: flat_pack_next[47:40] = data_out_internal;
        4'd6: flat_pack_next[55:48] = data_out_internal;
        4'd7: flat_pack_next[63:56] = data_out_internal;
        4'd8: flat_pack_next[71:64] = data_out_internal;
        default: flat_pack_next = flat_pack_curr;
    endcase
end

//assign flat_wdata = (last_elem) ? flat_pack_next : flat_pack_curr;


    // mask BRAM write:每個 patch 一次(維持你的寫法)
    //assign mask_we    = last_elem;
   // assign mask_waddr = write_addr_mask;
   // assign mask_wdata = mask_next;
   
   // -------------------------------
// BRAM write commit stage (flat + mask)
//   capture on last_elem, commit next cycle
// -------------------------------
// -------------------------------
// BRAM write commit stage (flat + mask)
//   capture on last_elem, COMMIT on NEXT cycle (pending)
// -------------------------------
// -------------------------------
// BRAM write commit stage (flat + mask)
//   capture on last_elem_p, commit on NEXT cycle
// -------------------------------

reg [1:0] we_cnt;   // 0=不寫,2/1=保持寫入


reg [7:0]  flat_waddr_hold;
reg [71:0] flat_wdata_hold;

reg [7:0]  mask_waddr_hold;   // 注意:你 mask addr 其實是 8-bit 就夠
reg [8:0]  mask_wdata_hold;

assign flat_we    = (we_cnt != 0);
assign flat_waddr = flat_waddr_hold;
assign flat_wdata = flat_wdata_hold;

assign mask_we    = (we_cnt != 0);
assign mask_waddr = mask_waddr_hold;
assign mask_wdata = mask_wdata_hold;


always @(posedge clk or posedge rst) begin
  if (rst) begin
    we_cnt <= 2'd0;

    flat_waddr_hold <= 8'd0;
    flat_wdata_hold <= 72'd0;
    mask_waddr_hold <= 8'd0;
    mask_wdata_hold <= 9'd0;

  end else begin
    // 預設:如果正在寫,倒數
    if (we_cnt != 0)
      we_cnt <= we_cnt - 1'b1;

    // 捕捉資料:在 last_elem_p 當拍把 addr/data 鎖住
    // 然後把 WE 拉寬兩拍(接下來兩個 clock 都會寫)
    if (last_elem_p) begin
      flat_waddr_hold <= patch_index[7:0];
      flat_wdata_hold <= flat_pack_next;

      mask_waddr_hold <= patch_index[7:0];
      mask_wdata_hold <= mask_next;

      we_cnt <= 2'd2;  // ★關鍵:WE 拉寬兩拍
    end
  end
end

       

    always @(*) begin
        mask_next = mask_temp;
        mask_next[elem_idx] = is_nz;
    end

    // mask_temp & elem_idx
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            elem_idx  <= 4'd0;
            mask_temp <= 9'd0;
        end else if (valid) begin
            mask_temp[elem_idx] <= is_nz;

            if (elem_idx == 4'd8) begin
                elem_idx  <= 4'd0;
                mask_temp <= 9'd0;
            end else begin
                elem_idx <= elem_idx + 1'b1;
            end
        end
    end

endmodule









// ==================== INPUT TRANSFORMATION (latency-safe, pipeline align) ====================
// ==================== INPUT TRANSFORMATION (latency-safe, with warm-up) ====================
module input_transformation2 #(
    parameter integer BRAM_READ_LAT = 4   // 請設成和 blk_mem_gen_out_flatten 的 read latency 一樣
)(
    input  wire        clk,
    input  wire        rst,
    input  wire        start,
    input  wire [8:0] base_addr,
    input  wire [7:0]  bram_data,
    
    // 這指令告訴 Vivado:這個訊號負載太重了,請自動複製暫存器!
    // 這樣邏輯時序完全不變,但電路推力變強

   (* max_fanout = 1 *) output reg  [8:0] bram_addr,
   (* max_fanout = 1 *)output reg  [7:0]  data_out,
   (* max_fanout = 1 *)output reg         done,      // 每個 patch 出完 9 筆時拉 1 拍
   (* max_fanout = 1 *)output reg         valid      // 每次輸出一筆有效 data 時拉 1 拍
);
    // -------------------------------
    // 固定 offset:3x3 window
    // -------------------------------
    reg [10:0] offsets[0:8];
    initial begin
        offsets[0] = 0;
        offsets[1] = 1;
        offsets[2] = 2;
        offsets[3] = 18;
        offsets[4] = 19;
        offsets[5] = 20;
        offsets[6] = 36;
        offsets[7] = 37;
        offsets[8] = 38;
    end

    localparam integer LAT = (BRAM_READ_LAT < 1) ? 1 : BRAM_READ_LAT;

    // 狀態機
    localparam S_IDLE   = 2'd0;
    localparam S_WAIT   = 2'd1;
    localparam S_CAPTURE= 2'd2;

    reg [1:0] state;

    reg [3:0] idx;          // 要讀哪一個 offset(0..8)
    reg [3:0] elem_cnt;     // 已經輸出了幾個真正的 element(0..8)
    reg [3:0] wait_cnt;     // 等待 BRAM read latency 用
    reg       warmup;       // 0 = 還沒丟掉第一筆;1 = 已經丟掉,開始算真正的 9 筆

    // 主邏輯:每個 start 代表一個 patch
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            state    <= S_IDLE;
            bram_addr<= 11'd0;
            data_out <= 8'd0;
            done     <= 1'b0;
            valid    <= 1'b0;
            idx      <= 4'd0;
            elem_cnt <= 4'd0;
            wait_cnt <= 4'd0;
            warmup   <= 1'b0;
        end else begin
            // 預設不拉 valid / done
            valid <= 1'b0;
            done  <= 1'b0;

            case (state)
                // 等 controller 丟 start
                S_IDLE: begin
                    if (start) begin
                        idx      <= 4'd0;
                        elem_cnt <= 4'd0;
                        warmup   <= 1'b0;

                        // 先發第一個 address(用 offsets[0])
                        bram_addr<= base_addr + offsets[0];
                        wait_cnt <= LAT;
                        state    <= S_WAIT;
                    end
                end

                // 等 BRAM latency
                S_WAIT: begin
                    if (wait_cnt != 0)
                        wait_cnt <= wait_cnt - 1'b1;
                    else
                        state <= S_CAPTURE;
                end

                // 收到一筆 BRAM output
                S_CAPTURE: begin
                    if (!warmup) begin
                        // 第一次 capture:當作 warm-up,不產生 valid,不算 elem_cnt
                        // 只把 warmup 拉起來,然後重新發「真正的第 0 筆」的 address
                        warmup   <= 1'b1;
                        idx      <= 4'd0;
                        bram_addr<= base_addr + offsets[0];
                        wait_cnt <= LAT;
                        state    <= S_WAIT;
                    end else begin
                        // 正式的 9 筆 data 都走這裡
                        data_out <= bram_data;
                        valid    <= 1'b1;    // 這一拍 data_out 有效

                        if (elem_cnt == 4'd8) begin
                            // 第 9 筆輸出完成 → 這個 patch 結束
                            done   <= 1'b1;  // 告訴 controller1:這個 patch ok 了
                            state  <= S_IDLE;
                        end else begin
                            // 還有下一筆
                            elem_cnt <= elem_cnt + 1'b1;
                            idx      <= idx + 1'b1;
                            bram_addr<= base_addr + offsets[idx + 1'b1];
                            wait_cnt <= LAT;
                            state    <= S_WAIT;
                        end
                    end
                end

                default: state <= S_IDLE;
            endcase
        end
    end

endmodule


// ==================== CONTROLLER (run-once then halt) ====================
module controller2 (
    input  wire        clk,
    input  wire        rst,
    input  wire        done,
    output reg  [8:0] base_addr,
    output reg         start,
    output reg  [15:0] patch_index
);

    reg [5:0] row, col;
    reg [1:0] state;

    localparam IDLE = 2'b00,
               READ = 2'b01,
               WAIT = 2'b10,
               HALT = 2'b11;   // ★ 新增

    wire last_col   = (col == 6'd15);
    wire last_row   = (row == 6'd15);
    wire last_patch = last_row && last_col;

    always @(posedge clk or posedge rst) begin
        if (rst) begin
            state       <= IDLE;
            row         <= 6'd0;
            col         <= 6'd0;
            base_addr   <= 11'd0;
            start       <= 1'b0;
            patch_index <= 16'd0;
        end else begin
            case (state)
                IDLE: begin
                    // ★ 只在 IDLE 打 1 拍 start
                    base_addr <= row * 9'd18 + col;
                    start     <= 1'b1;
                    state     <= READ;
                end

                READ: begin
                    // ★ 進 READ 立刻把 start 拉回 0(你原本就有)
                    start <= 1'b0;

                    if (done) begin
                        // ★ 關鍵:最後一個 patch 在這裡就直接停
                        if (last_patch) begin
                            patch_index <= 16'd255;  // optional
                            state       <= HALT;     // ★ 不再回 IDLE
                        end else begin
                            state <= WAIT;
                        end
                    end
                end

                WAIT: begin
                    // 下一個 patch 的 row/col/patch_index 更新
                    if (!last_col) begin
                        col <= col + 1'b1;
                    end else begin
                        col <= 6'd0;
                        row <= row + 1'b1;
                    end
                    patch_index <= patch_index + 1'b1;
                    state <= IDLE;
                end

                HALT: begin
                    // ★ 永遠不再發 start
                    start <= 1'b0;
                    state <= HALT;
                end
            endcase
        end
    end
endmodule





module crp_tilepipe_c0_f0 #(
    parameter  READ_LAT_D   = 2,   // flat/mask BRAM read latency
    parameter  READ_LAT_W   = 2,   // weight BRAM read latency
    parameter  SHIFT_CONST  = 31,

    parameter signed [31:0] BIAS_CONST  = 32'sd3598,
    parameter signed [31:0] SCALE_CONST = 32'sd10093451
)(
    input  wire clk,
    input  wire rst,
    input  wire start,               // 建議 pulse:進入 CONV stage 那拍
    output reg  done,                // 最後一筆 pool write 時 pulse

    // ===== Flatten outputs (read-only) =====
    output reg  [7:0]  conv_c0_flat_addr,
    input  wire [71:0] conv_c0_flat_dout,   // dout = addr from 2 cycles ago
    output reg  [7:0]  conv_c0_mask_addr,
    input  wire [8:0]  conv_c0_mask_dout,

    // ===== Weight (for filter0) =====
    output reg  [3:0]        conv_f0_w_addr,
    input  wire signed [7:0] conv_f0_w_dout, // dout = addr from 2 cycles ago

    // ===== Pool output (filter0) =====
    output wire        pool_f0_we,
    output wire  [5:0] pool_f0_addr,   // 0..63
    output wire  [7:0] pool_f0_din,
  
  
  
      // ===== DEBUG taps =====
    output wire        dbg_stg1_v,
    output wire [5:0]  dbg_stg1_tile,
    output wire [1:0]  dbg_stg1_sub,
    output wire [8:0]  dbg_stg1_mask9,
    output wire [71:0] dbg_stg1_flat72,
    
    
    output wire        dbg_s5_v,
    output wire [5:0]  dbg_s5_tile,
    output wire [1:0]  dbg_s5_sub,
    output wire [7:0]  dbg_s5_pix_relu,

    output wire        dbg_s3_v,
    output wire [5:0]  dbg_s3_tile,
    output wire [1:0]  dbg_s3_sub,
    output wire signed [31:0] dbg_s3_sum32,

    output wire [7:0]  dbg_pool_acc_max,
    output wire [5:0]  dbg_pool_acc_tile,
    output wire [3:0]  dbg_pool_acc_seen,
    
    
    output wire        dbg_issue_v,
    output wire [5:0]  dbg_issue_tile,
    output wire [1:0]  dbg_issue_sub,
    output wire [7:0]  dbg_issue_addr



);


  

    // =========================================================
    // 0) Weight preload (9 weights)
    // =========================================================
    reg signed [7:0] conv_f0_w0,conv_f0_w1,conv_f0_w2,conv_f0_w3,conv_f0_w4,
                     conv_f0_w5,conv_f0_w6,conv_f0_w7,conv_f0_w8;
    reg              conv_f0_weights_ready;
    
    // ---- NEW: registered commit stage for BRAM write ----
    reg        pool_commit_we;
    reg [7:0]  pool_commit_addr;
    reg [7:0]  pool_commit_din;
    

    localparam W_IDLE = 3'd0,
           W_ISS  = 3'd1,
           W_WT   = 3'd2,
           W_CAP  = 3'd3,
           W_DONE = 3'd4;

    reg [2:0]  wst;
    reg [3:0]  widx;
    reg [1:0]  wwait;

    always @(posedge clk or posedge rst) begin
       if (rst) begin
        wst   <= W_IDLE;
        widx  <= 4'd0;
        wwait <= 2'd0;
    
        conv_f0_w_addr <= 4'd0;
        conv_f0_weights_ready <= 1'b0;
    
        conv_f0_w0 <= 8'sd0;
        conv_f0_w1 <= 8'sd0;
        conv_f0_w2 <= 8'sd0;
        conv_f0_w3 <= 8'sd0;
        conv_f0_w4 <= 8'sd0;
        conv_f0_w5 <= 8'sd0;
        conv_f0_w6 <= 8'sd0;
        conv_f0_w7 <= 8'sd0;
        conv_f0_w8 <= 8'sd0;
    end  else begin
            if (start) begin
                wst <= W_ISS;
                widx <= 0;
                conv_f0_weights_ready <= 1'b0;
            end

            case (wst)
                W_IDLE: begin end
                W_ISS: begin
                    conv_f0_w_addr <= widx;
                    wwait <= (READ_LAT_W < 1) ? 1 : READ_LAT_W[1:0];
                    wst <= W_WT;
                end
                W_WT: begin
                    if (wwait != 0) wwait <= wwait - 1'b1;
                    else wst <= W_CAP;
                end
                W_CAP: begin
                    case (widx)
                        0: conv_f0_w0 <= conv_f0_w_dout;
                        1: conv_f0_w1 <= conv_f0_w_dout;
                        2: conv_f0_w2 <= conv_f0_w_dout;
                        3: conv_f0_w3 <= conv_f0_w_dout;
                        4: conv_f0_w4 <= conv_f0_w_dout;
                        5: conv_f0_w5 <= conv_f0_w_dout;
                        6: conv_f0_w6 <= conv_f0_w_dout;
                        7: conv_f0_w7 <= conv_f0_w_dout;
                        8: conv_f0_w8 <= conv_f0_w_dout;
                    endcase

                    if (widx == 8) begin
                        conv_f0_weights_ready <= 1'b1;
                        wst <= W_DONE;
                    end else begin
                        widx <= widx + 1'b1;
                        wst <= W_ISS;
                    end
                end
                W_DONE: begin end
            endcase
        end
    end
    
    
    

    // =========================================================
    // 1) ISSUE: tile-based patch order
    //    tile_id 0..63, sub_id 0..3 => patch = base + {0,1,16,17}
    // =========================================================
    
    reg [5:0]  issue_tile_id;   // 0..63
    reg [1:0]  issue_sub_id;    // 0..3

    wire [2:0] issue_tile_r = issue_tile_id[5:3]; // 0..7
    wire [2:0] issue_tile_c = issue_tile_id[2:0]; // 0..7

    // base = (tile_r*32) + (tile_c*2)
    wire [9:0] issue_base_patch = ({issue_tile_r,5'b0}) + ({issue_tile_c,1'b0});

    reg  [9:0] issue_patch_addr;
    always @(*) begin
        case (issue_sub_id)
            2'd0: issue_patch_addr = issue_base_patch + 10'd0;
            2'd1: issue_patch_addr = issue_base_patch + 10'd1;
            2'd2: issue_patch_addr = issue_base_patch + 10'd16;
            2'd3: issue_patch_addr = issue_base_patch + 10'd17;
        endcase
    end
    
    
   

   
    
   
    reg        issue_run;
   
    reg [1:0]  prime_cnt;         // 2->1->0
    reg [5:0]  issue_tile_hold;   // ★當拍送出去的 tag 鎖住
    reg [1:0]  issue_sub_hold;
    
    wire issuing   = issue_run && conv_f0_weights_ready;
    wire issue_fire = issuing && (prime_cnt == 2'd0);   // ★只有 prime 完才算正式 issue
    
   
   assign dbg_issue_v    = issue_fire;
    assign dbg_issue_tile = issue_tile_id;
    assign dbg_issue_sub  = issue_sub_id;
    assign dbg_issue_addr = issue_patch_addr[7:0];
    
    
    always @(posedge clk or posedge rst) begin
      if (rst) begin
        issue_run        <= 1'b0;
        issue_tile_id    <= 6'd0;
        issue_sub_id     <= 2'd0;
        prime_cnt        <= 2'd0;
    
        conv_c0_flat_addr <= 8'd0;
        conv_c0_mask_addr <= 8'd0;
    
        issue_tile_hold  <= 6'd0;
        issue_sub_hold   <= 2'd0;
      end else begin
        if (start) begin
          issue_run     <= 1'b1;
          issue_tile_id <= 6'd0;
          issue_sub_id  <= 2'd0;
          prime_cnt     <= 2'd2;      // ★重送 addr0 兩拍
        end
    
        if (issuing) begin
          // ★先鎖住當拍 tag(非常重要)
          issue_tile_hold <= issue_tile_id;
          issue_sub_hold  <= issue_sub_id;
    
          // ★每拍都送 addr 到 BRAM(prime 期間也送)
          conv_c0_flat_addr <= issue_patch_addr[7:0];
          conv_c0_mask_addr <= issue_patch_addr[7:0];
    
          if (prime_cnt != 0) begin
            prime_cnt <= prime_cnt - 1'b1;   // prime 不前進 tag
          end else begin
            // ★正式前進 tile/sub
            if (issue_sub_id == 2'd3) begin
              issue_sub_id <= 2'd0;
              if (issue_tile_id == 6'd63) begin
                issue_run <= 1'b0;
              end else begin
                issue_tile_id <= issue_tile_id + 1'b1;
              end
            end else begin
              issue_sub_id <= issue_sub_id + 1'b1;
            end
          end
        end
      end
    end
    
    
    
    

    // =========================================================
    // 2) ALIGN: BRAM latency=2 => shift tag 2 cycles 對齊 dout
    //    tag = {tile_id, sub_id}
    // =========================================================
   reg [READ_LAT_D:0] stg0_vld_sh;
    reg [5:0]          stg0_tile_sh [0:READ_LAT_D];
    reg [1:0]          stg0_sub_sh  [0:READ_LAT_D];
    
    integer k;
    always @(posedge clk or posedge rst) begin
      if (rst) begin
        stg0_vld_sh <= {(READ_LAT_D+1){1'b0}};
        for (k=0; k<=READ_LAT_D; k=k+1) begin
          stg0_tile_sh[k] <= 6'd0;
          stg0_sub_sh[k]  <= 2'd0;
        end
      end else begin
       stg0_vld_sh <= {stg0_vld_sh[READ_LAT_D-1:0], issue_fire};

        // ★重點:tag 用「當拍的」issue_tile_id/sub_id(只在 issue_fire 時更新)
        if (issue_fire) begin
          stg0_tile_sh[0] <= issue_tile_id;
          stg0_sub_sh[0]  <= issue_sub_id;
        end
        
    
        for (k=1; k<=READ_LAT_D; k=k+1) begin
          stg0_tile_sh[k] <= stg0_tile_sh[k-1];
          stg0_sub_sh[k]  <= stg0_sub_sh[k-1];
        end
      end
    end
    

    // =========================================================
    // 3) STAGE1: latch flat/mask (aligned)
    // =========================================================
    reg        stg1_v;
    reg [5:0]  stg1_tile_id;
    reg [1:0]  stg1_sub_id;
    reg [71:0] stg1_flat72;
    reg [8:0]  stg1_mask9;

    always @(posedge clk or posedge rst) begin
         if (rst) begin
        stg1_v       <= 1'b0;
        stg1_tile_id <= 6'd0;
        stg1_sub_id  <= 2'd0;
        stg1_flat72  <= 72'd0;
        stg1_mask9   <= 9'd0;
    end else begin
            stg1_v       <= stg0_vld_sh[READ_LAT_D];
            stg1_tile_id <= stg0_tile_sh[READ_LAT_D];
            stg1_sub_id  <= stg0_sub_sh[READ_LAT_D];

            if (stg0_vld_sh[READ_LAT_D]) begin
                stg1_flat72 <= conv_c0_flat_dout;
                stg1_mask9  <= conv_c0_mask_dout;
            end
        end
    end

    // unpack x
    wire signed [7:0] stg1_x0 = stg1_flat72[ 7: 0];
    wire signed [7:0] stg1_x1 = stg1_flat72[15: 8];
    wire signed [7:0] stg1_x2 = stg1_flat72[23:16];
    wire signed [7:0] stg1_x3 = stg1_flat72[31:24];
    wire signed [7:0] stg1_x4 = stg1_flat72[39:32];
    wire signed [7:0] stg1_x5 = stg1_flat72[47:40];
    wire signed [7:0] stg1_x6 = stg1_flat72[55:48];
    wire signed [7:0] stg1_x7 = stg1_flat72[63:56];
    wire signed [7:0] stg1_x8 = stg1_flat72[71:64];




    assign dbg_stg1_v      = stg1_v;
    assign dbg_stg1_tile   = stg1_tile_id;
    assign dbg_stg1_sub    = stg1_sub_id;
    assign dbg_stg1_mask9  = stg1_mask9;
    assign dbg_stg1_flat72 = stg1_flat72;

    // =========================================================
    // 4) STAGE2: MUL (masked)
    // =========================================================
    reg        stg2_v;
    reg [5:0]  stg2_tile_id;
    reg [1:0]  stg2_sub_id;
    reg signed [31:0] stg2_p[0:8];

    integer m;
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            stg2_v <= 1'b0;
            stg2_tile_id <= 6'd0;
            stg2_sub_id  <= 2'd0;
           for (m = 0; m < 9; m = m + 1)
            stg2_p[m] <= 32'sd0;   
        end else begin
            stg2_v       <= stg1_v;
            stg2_tile_id <= stg1_tile_id;
            stg2_sub_id  <= stg1_sub_id;

            if (stg1_v) begin
                stg2_p[0] <= stg1_mask9[0] ? ($signed(stg1_x0) * $signed(conv_f0_w0)) : 32'sd0;
                stg2_p[1] <= stg1_mask9[1] ? ($signed(stg1_x1) * $signed(conv_f0_w1)) : 32'sd0;
                stg2_p[2] <= stg1_mask9[2] ? ($signed(stg1_x2) * $signed(conv_f0_w2)) : 32'sd0;
                stg2_p[3] <= stg1_mask9[3] ? ($signed(stg1_x3) * $signed(conv_f0_w3)) : 32'sd0;
                stg2_p[4] <= stg1_mask9[4] ? ($signed(stg1_x4) * $signed(conv_f0_w4)) : 32'sd0;
                stg2_p[5] <= stg1_mask9[5] ? ($signed(stg1_x5) * $signed(conv_f0_w5)) : 32'sd0;
                stg2_p[6] <= stg1_mask9[6] ? ($signed(stg1_x6) * $signed(conv_f0_w6)) : 32'sd0;
                stg2_p[7] <= stg1_mask9[7] ? ($signed(stg1_x7) * $signed(conv_f0_w7)) : 32'sd0;
                stg2_p[8] <= stg1_mask9[8] ? ($signed(stg1_x8) * $signed(conv_f0_w8)) : 32'sd0;
            end
        end
    end

    // =========================================================
    // 5) STAGE3: ACC + BIAS
    // =========================================================
    reg        stg3_v;
    reg [5:0]  stg3_tile_id;
    reg [1:0]  stg3_sub_id;
    reg signed [31:0] stg3_sum32;

    always @(posedge clk or posedge rst) begin
        if (rst) begin
            stg3_v <= 1'b0;
            stg3_tile_id <= 6'd0;
            stg3_sub_id  <= 2'd0;
            stg3_sum32   <= 32'sd0;
        end else begin
            stg3_v       <= stg2_v;
            stg3_tile_id <= stg2_tile_id;
            stg3_sub_id  <= stg2_sub_id;

            if (stg2_v) begin
                stg3_sum32 <= (stg2_p[0]+stg2_p[1]+stg2_p[2]+stg2_p[3]+stg2_p[4]+
                              stg2_p[5]+stg2_p[6]+stg2_p[7]+stg2_p[8]) + BIAS_CONST;
            end
        end
    end
     
    // =========================================================
    // 6) STAGE4: SCALE MUL
    // =========================================================
    reg        stg4_v;
    reg [5:0]  stg4_tile_id;
    reg [1:0]  stg4_sub_id;
    reg signed [63:0] stg4_prod64;

    always @(posedge clk or posedge rst) begin
        if (rst) begin
            stg4_v <= 1'b0;
            stg4_tile_id <= 6'd0;
            stg4_sub_id  <= 2'd0;
            stg4_prod64  <= 64'sd0;
        end else begin
            stg4_v       <= stg3_v;
            stg4_tile_id <= stg3_tile_id;
            stg4_sub_id  <= stg3_sub_id;

            if (stg3_v) begin
                stg4_prod64 <= $signed(stg3_sum32) * $signed(SCALE_CONST);
            end
        end
    end

    // =========================================================
    // 7) STAGE5: SHIFT + ReLU => pixel
    // =========================================================
    reg        stg5_v;
    reg [5:0]  stg5_tile_id;
    reg [1:0]  stg5_sub_id;
    reg [7:0]  stg5_pix_relu;

    wire signed [63:0] stg5_shifted64 = $signed(stg4_prod64) >>> SHIFT_CONST;
    wire signed [7:0]  stg5_out8_s    = stg5_shifted64[7:0];

    always @(posedge clk or posedge rst) begin
        if (rst) begin
            stg5_v <= 1'b0;
            stg5_tile_id <= 6'd0;
            stg5_sub_id  <= 2'd0;
            stg5_pix_relu<= 8'sd0;
        end else begin
            stg5_v       <= stg4_v;
            stg5_tile_id <= stg4_tile_id;
            stg5_sub_id  <= stg4_sub_id;

            if (stg4_v) begin
                stg5_pix_relu <= stg5_out8_s[7] ? 8'd0 : stg5_out8_s; // ReLU
            end
        end
    end
    
    
    

    // =========================================================
    // 8) POOL reducer (collect 4 sub pixels per tile)
    // =========================================================
    function [7:0] max2;
        input [7:0] a,b;
        begin max2 = (a>b)?a:b; end
    endfunction
    
    
    // ---- pending write hold regs ----
    reg       pool_pending;
    reg [7:0] pool_addr_hold;
    reg [7:0] pool_din_hold;
    
    
    

    // =========================================================
    // 8.0) BRAM write outputs are fully registered (commit stage)
    // =========================================================
  assign pool_f0_we   = pool_pending;
 assign pool_f0_addr = pool_addr_hold[5:0]; // 你 top 端用 6-bit
 assign pool_f0_din  = pool_din_hold;

      assign dbg_s5_v        = stg5_v;
    assign dbg_s5_tile     = stg5_tile_id;
    assign dbg_s5_sub      = stg5_sub_id;
    assign dbg_s5_pix_relu = stg5_pix_relu;
    
    assign dbg_s3_v     = stg3_v;
    assign dbg_s3_tile  = stg3_tile_id;
    assign dbg_s3_sub   = stg3_sub_id;
    assign dbg_s3_sum32 = stg3_sum32;
    
   
    

    reg [7:0] pool_acc_max;
    reg [5:0] pool_acc_tile_id;
    reg [3:0] pool_acc_seen; // bitmask for sub_id 0..3

    always @(posedge clk or posedge rst) begin
        if (rst) begin
            //pool_f0_we   <= 1'b0;
           // pool_f0_addr <= 8'd0;
            //pool_f0_din  <= 8'd0;
            done         <= 1'b0;
            pool_pending <= 1'b0;
          pool_addr_hold <= 8'd0;
          pool_din_hold  <= 8'd0;

            pool_acc_max     <= 8'd0;
            pool_acc_tile_id <= 6'd0;
            pool_acc_seen    <= 4'b0000;
        end else begin
            pool_commit_we <= 1'b0;
            done       <= 1'b0;
            if (pool_pending) pool_pending <= 1'b0; // 只維持一拍
            if (start) begin
                pool_acc_max     <= 8'd0;
                pool_acc_tile_id <= 6'd0;
                pool_acc_seen    <= 4'b0000;
            end

            if (stg5_v) begin
                // new tile? (正常情況 stg5_tile_id 會依序,但我仍用 tile_id 做保護)
                if (pool_acc_seen == 4'b0000) begin
                    pool_acc_tile_id <= stg5_tile_id;
                    pool_acc_max     <= stg5_pix_relu;
                    pool_acc_seen    <= (4'b0001 << stg5_sub_id);
                end else if (stg5_tile_id == pool_acc_tile_id) begin
                    pool_acc_max  <= max2(pool_acc_max, stg5_pix_relu);
                    pool_acc_seen <= pool_acc_seen | (4'b0001 << stg5_sub_id);
                end else begin
                    // 若你未來改成 out-of-order,這裡就是你插 queue 的地方
                    // 目前先假設 in-order,不處理跨 tile 亂序
                    pool_acc_tile_id <= stg5_tile_id;
                    pool_acc_max     <= stg5_pix_relu;
                    pool_acc_seen    <= (4'b0001 << stg5_sub_id);
                end

                // 收滿 4 個 sub pixel => write
               if ((pool_acc_seen | (4'b0001 << stg5_sub_id)) == 4'b1111) begin
                    pool_addr_hold <= {2'b00, pool_acc_tile_id};
                    pool_din_hold  <= max2(pool_acc_max, stg5_pix_relu);
                    pool_pending   <= 1'b1;
                
                    if (pool_acc_tile_id == 6'd63) begin
                        done <= 1'b1;   // 這個 done 仍然在「收滿那拍」pulse
                    end
                
                    pool_acc_seen <= 4'b0000;
                end

            end
        end
    end
    
    
    
     assign dbg_pool_acc_max  = pool_acc_max;
    assign dbg_pool_acc_tile = pool_acc_tile_id;
    assign dbg_pool_acc_seen = pool_acc_seen;
    
        

endmodule



module padding_8x8_top #(
    parameter IN_W  = 8,
    parameter IN_H  = 8,
    parameter OUT_W = IN_W + 2,   // 10
    parameter OUT_H = IN_H + 2,   // 10

    // ★總延遲:從「addr_in 被外部(你的top仲裁/BRAM)吃到」到「data_in 對應該addr」有效
    // 你目前常見是:p2_a_r 1拍 + BRAM 2拍 => 3
    parameter integer RD_TOTAL_LAT = 3
)(
    input  wire clk,
    input  wire rst,
    input  wire start,
    output reg  done,

    output reg  [9:0] addr_in,
    input  wire [7:0] data_in,

    output reg  [9:0] addr_out,
    output reg  [7:0] data_out,
    output reg        we_out
);

    // (x,y) on 10x10
    reg [5:0] x, y;
    
    reg run;


    // 0..63 for inner 8x8
    reg [9:0] in_cnt;

    // latch for incoming data
    reg [7:0] din_lat;

    // latch current "is inner?"
    reg is_inner_lat;

    // wait counter
    reg [7:0] wait_cnt;

    localparam S_ISSUE = 2'd0;
    localparam S_WAIT  = 2'd1;
    localparam S_LATCH = 2'd2;
    localparam S_WRITE = 2'd3;

    reg [1:0] state;

    wire is_inner_now = (x != 0 && x != OUT_W-1 && y != 0 && y != OUT_H-1);

    // next x,y helper
    wire last_x = (x == OUT_W-1);
    wire last_y = (y == OUT_H-1);

    always @(posedge clk or posedge rst) begin
        if (rst) begin
            x <= 0; y <= 0;
            in_cnt <= 0;
            run <= 1'b0;

            addr_in  <= 0;
            addr_out <= 0;
            data_out <= 0;
            we_out   <= 0;
            done     <= 0;

            din_lat      <= 0;
            is_inner_lat <= 0;
            wait_cnt     <= 0;
            state        <= S_ISSUE;
        end else begin
            we_out <= 1'b0;
            // latch start pulse -> keep running until done
            if (start) run <= 1'b1;
            if (done)  run <= 1'b0;

            if (!done && run) begin
                case (state)
                    // -------------------------------------------------
                    // ISSUE: 固定住本次要讀的 addr_in(若是inner)
                    // -------------------------------------------------
                    S_ISSUE: begin
                        is_inner_lat <= is_inner_now;

                        if (is_inner_now) begin
                            addr_in  <= in_cnt;                // ★把本次要讀的 in_cnt 發出去
                            wait_cnt <= (RD_TOTAL_LAT < 1) ? 1 : RD_TOTAL_LAT;
                            state    <= S_WAIT;
                        end else begin
                            // border:不需要讀 input,直接走寫出
                            state <= S_WRITE;
                        end
                    end

                    // -------------------------------------------------
                    // WAIT: 等到 data_in 對應 addr_in 穩定
                    // -------------------------------------------------
                    S_WAIT: begin
                        if (wait_cnt > 1)
                            wait_cnt <= wait_cnt - 1'b1;
                        else
                            state <= S_LATCH;
                    end

                    // -------------------------------------------------
                    // LATCH: 先把 data_in 鎖進暫存器,下一拍再寫
                    // -------------------------------------------------
                    S_LATCH: begin
                        din_lat <= data_in;
                        state   <= S_WRITE;
                    end

                    // -------------------------------------------------
                    // WRITE: 寫 output(inner用 din_lat,border=0)
                    // -------------------------------------------------
                    S_WRITE: begin
                        we_out   <= 1'b1;
                        addr_out <= y * OUT_W + x;

                        if (!is_inner_lat)
                            data_out <= 8'd0;
                        else begin
                            data_out <= din_lat;
                            in_cnt   <= in_cnt + 1'b1;
                        end

                        // advance x,y
                        if (last_x) begin
                            x <= 0;
                            if (last_y) begin
                                done <= 1'b1;
                            end else begin
                                y <= y + 1'b1;
                            end
                        end else begin
                            x <= x + 1'b1;
                        end

                        state <= S_ISSUE;
                    end
                endcase
            end
        end
    end

endmodule


Editor is loading...
Leave a Comment