// ============================================================================
// Top Module: ulink_rx
// Integrates training, descrambling, and frame reception, outputs 512-bit block writes to SRAM
// ============================================================================
module ulink_rx #(
    parameter FIFO_DEPTH       = 64,          // Input FIFO depth
    parameter [31:0] SCRAMBLER_SEED = 32'hFFFFFFFF // Fixed seed for descrambler (all lanes share)
)(
   input  logic         clk
  ,input  logic         rst_n                // Active-low reset
  // 4-lane serial input
  ,input  logic [3  :0] serial_in
  // Training parameters
  ,input  logic [19 :0] patn_count
  ,input  logic [2  :0] tap_step
  ,input  logic [2  :0] tap_adj_mask       // Delay adjustment mask
  ,input  logic [0  :0] tap_force          // Delay force to tap_step
  ,input  logic         force_train
  ,input  logic         descram_en
  ,input  logic         always_on
  ,input  logic         prefilling
  ,output logic         prefill_start
  // Link status outputs
  ,output logic         link_down
  ,output logic         train_ready
  ,output logic [2  :0] delay_tap
  ,output logic         tap_adj_req
  // SRAM write interface (each block is 512 bits)
  ,output logic [12 :0] wr_addr
  ,output logic [511:0] wr_data
  ,output logic         wr_en
  ,output logic [63 :0] byte_mask
  // CRC error indication (can be used for external monitoring or to restart training)
  ,output logic         crc_error
  ,output logic         frame_done
  ,output logic [31 :0] train_status
  ,output logic [31 :0] frame_status
);

// Internal connection signals
//logic         train_ready;        // Training ready (not used, but can be exported)
logic         data_valid;           // Data valid pulse from training module
logic [127:0] data_int;             // 128-bit data from training module
logic [127:0] descrambled_data;     // Descrambled data
logic         descrambled_valid;    // Descrambled data valid
logic [0  :0] train_en;             // Training enable
logic [3  :0] descram_valid;

// ========================================================================
// prefill module instantiation
// ========================================================================
prefill                                    #(
   .DELAY                                   ( 20'd10000                   ) 
) U_prefill                                 (
   .clk                                     ( clk                         )
  ,.rst_n                                   ( rst_n                       )
  ,.always_on                               ( always_on                   )
  ,.prefilling                              ( prefilling                  )
  ,.prefill_start                           ( prefill_start               )
  ,.prefill_done                            ( train_en                    )
);


// ========================================================================
// Training module instantiation
// ========================================================================
ulink_rx_train u_train                      (
   .clk                                     ( clk                         )
  ,.rst_n                                   ( rst_n                       )
  ,.serial_in                               ( serial_in                   )
  ,.patn_count                              ( patn_count                  )
  ,.frame_error                             ( crc_error                   )       // Frame error feedback to restart training
  ,.frame_done                              ( frame_done                  )
  ,.tap_step                                ( tap_step                    )
  ,.tap_adj_mask                            ( tap_adj_mask                )
  ,.tap_force                               ( tap_force                   )
  ,.force_train                             ( force_train                 )
  ,.train_en                                ( train_en                    )
  ,.link_down                               ( link_down                   )
  ,.delay_tap                               ( delay_tap                   )
  ,.tap_adj_req                             ( tap_adj_req                 )
  ,.train_ready                             ( train_ready                 )
  ,.descram_valid                           ( descram_valid               )
  ,.data_valid                              ( data_valid                  )
  ,.data_out                                ( data_int                    )
  ,.train_status                            ( train_status                )
);


// ========================================================================
// Descrambler module instantiation (128-bit, all lanes share the same seed)
// ========================================================================
ulink_descrambler_128                      #(
    .SEED                                   ( SCRAMBLER_SEED              )
) u_descrambler                             (                             
   .clk                                     ( clk                         )
  ,.rst_n                                   ( rst_n                       )
  ,.data_in                                 ( data_int                    )
  ,.valid_in                                ( descram_valid               )
  ,.enable                                  ( descram_en                  )               // Always enable descrambling
  ,.data_out                                ( descrambled_data            )
  ,.valid_out                               ( descrambled_valid           )
);

// ========================================================================
// Frame receiver module instantiation
// ========================================================================
ulink_frame_receiver                       #(
    .FIFO_DEPTH                             ( FIFO_DEPTH                  )
) u_frame                                   (                             
   .clk                                     ( clk                         )
  ,.rst_n                                   ( rst_n                       )
  ,.data_in                                 ( descrambled_data            )
  ,.valid_in                                ( descrambled_valid           )
  ,.wr_addr                                 ( wr_addr                     )
  ,.wr_data                                 ( wr_data                     )
  ,.wr_en                                   ( wr_en                       )
  ,.byte_mask                               ( byte_mask                   )
  ,.crc_error                               ( crc_error                   )
  ,.frame_done                              ( frame_done                  )
  ,.frame_status                            ( frame_status                )
);

endmodule


// ============================================================================
// Sub-module definitions (can be placed in separate files as needed)
// ============================================================================

// ----------------------------------------------------------------------------
// 32-bit Descrambler (used inside the 128-bit descrambler)
// ----------------------------------------------------------------------------
module ulink_descrambler_32 #(
    parameter [31:0] SEED = 32'hFFFFFFFF
) (
   input  logic         clk
  ,input  logic         rst_n
  ,input  logic [31:0]  data_in
  ,input  logic         valid_in
  ,input  logic         enable
  ,output logic [31:0]  data_out
  ,output logic         valid_out
);
    logic [31:0] lfsr, next_lfsr;
    wire feedback = lfsr[31] ^ lfsr[30] ^ lfsr[29] ^ lfsr[28];
    assign next_lfsr = {lfsr[30:0], feedback};

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            lfsr <= SEED;
        else if (valid_in && enable)
            lfsr <= next_lfsr;
    end

    logic [31:0] descrambled;
    assign descrambled = valid_in && enable ? (data_in ^ lfsr) : data_in;

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_out <= 32'h0;
            valid_out <= 1'b0;
        end else begin
            data_out <= descrambled;
            valid_out <= valid_in;
        end
    end
endmodule


// ----------------------------------------------------------------------------
// 128-bit Descrambler (composed of four 32-bit descramblers)
// ----------------------------------------------------------------------------
module ulink_descrambler_128 #(
    parameter [31:0] SEED = 32'hFFFFFFFF
) (
   input  logic         clk
  ,input  logic         rst_n
  ,input  logic [127:0] data_in
  ,input  logic [3  :0] valid_in
  ,input  logic         enable
  ,output logic [127:0] data_out
  ,output logic         valid_out
);
    logic [31:0] lane0_out, lane1_out, lane2_out, lane3_out;
    logic        valid_out0, valid_out1, valid_out2, valid_out3;

    ulink_descrambler_32 #(.SEED(SEED)) u0 (
        .clk      (clk),
        .rst_n    (rst_n),
        .data_in  (data_in[31:0]),
        .valid_in (valid_in[0]),
        .enable   (enable),
        .data_out (lane0_out),
        .valid_out(valid_out0)
    );
    ulink_descrambler_32 #(.SEED(SEED)) u1 (
        .clk      (clk),
        .rst_n    (rst_n),
        .data_in  (data_in[63:32]),
        .valid_in (valid_in[1]),
        .enable   (enable),
        .data_out (lane1_out),
        .valid_out(valid_out1)
    );
    ulink_descrambler_32 #(.SEED(SEED)) u2 (
        .clk      (clk),
        .rst_n    (rst_n),
        .data_in  (data_in[95:64]),
        .valid_in (valid_in[2]),
        .enable   (enable),
        .data_out (lane2_out),
        .valid_out(valid_out2)
    );
    ulink_descrambler_32 #(.SEED(SEED)) u3 (
        .clk      (clk),
        .rst_n    (rst_n),
        .data_in  (data_in[127:96]),
        .valid_in (valid_in[3]),
        .enable   (enable),
        .data_out (lane3_out),
        .valid_out(valid_out3)
    );

    assign data_out = {lane3_out, lane2_out, lane1_out, lane0_out};
    assign valid_out = valid_out1;  // All lanes are synchronized
endmodule


// ----------------------------------------------------------------------------
// CRC32 Module (used in frame receiver)
// ----------------------------------------------------------------------------
module crc32 #(
   parameter POLY = 32'h04C11DB7
  ,parameter INIT = 32'hFFFFFFFF
)(
   input  logic        clk
  ,input  logic        rst_n
  ,input  logic        clear
  ,input  logic [31:0] data
  ,input  logic        valid
  ,output logic [31:0] crc
);
    logic [31:0] crc_reg, crc_next;
    integer i;
    logic data_bit;

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            crc_reg <= INIT;
        else if (clear)
            crc_reg <= INIT;
        else if (valid)
            crc_reg <= crc_next;
    end

    always_comb begin
        crc_next = crc_reg;
        for (i = 0; i < 32; i++) begin
            data_bit = crc_next[31] ^ data[31-i];
            crc_next = {crc_next[30:0], 1'b0};
            if (data_bit) crc_next = crc_next ^ POLY;
        end
    end
    assign crc = crc_reg;
endmodule


// ----------------------------------------------------------------------------
// Frame Receiver Module (uses syn_fwft_fifo)
// ----------------------------------------------------------------------------
module ulink_frame_receiver #(
    parameter FIFO_DEPTH = 64
)(
   input  logic         clk
  ,input  logic         rst_n
  ,input  logic [127:0] data_in
  ,input  logic         valid_in
  ,output logic [12:0]  wr_addr
  ,output logic [511:0] wr_data
  ,output logic         wr_en
  ,output logic [63:0]  byte_mask
  ,output logic [31:0]  frame_status
  ,output logic         crc_error
  ,output logic         frame_done
);
// Frame header constant
localparam FRAME_HEADER = 32'hBCBCBCBC;



	
// FIFO instantiation
logic fifo_rd_en;
logic [127:0] fifo_dout;
logic fifo_empty;

//syn_fwft_fifo
syn_fwft_fifo                              #(
   .width                                   ( 128        )
  ,.depth                                   ( FIFO_DEPTH )
  ) u_fifo                                  (
   .clk                                     ( clk        )
  ,.rst                                     ( ~rst_n     )
  ,.clr                                     ( 1'b0       )
  ,.wr_en                                   ( valid_in   )
  ,.din                                     ( data_in    )
  ,.full                                    (            )
  ,.almost_full                             (            )
  ,.prog_full                               (            )
  ,.rd_en                                   (fifo_rd_en  )
  ,.dout                                    (fifo_dout   )
  ,.empty                                   (fifo_empty  )
  ,.almost_empty                            (            )
  ,.prog_empty                              (            )
  ,.cnt                                     (            )
);

// Split 128-bit data into four 32-bit words
typedef enum logic [2:0] { S_IDLE, S_WORD0, S_WORD1, S_WORD2, S_WORD3 } word_state_t;
word_state_t word_state_c, word_state_n;

// ------------------------------------------------------
// -- word state machine
// ------------------------------------------------------

//jump conditions
wire idle2word0   = (word_state_c == S_IDLE   ) && !fifo_empty;
wire word02word1  = (word_state_c == S_WORD0  ) ;
wire word12word2  = (word_state_c == S_WORD1  ) ;
wire word22word3  = (word_state_c == S_WORD2  ) ;
wire word32idle   = (word_state_c == S_WORD3  ) ;


//state_n
assign word_state_n = ((word_state_c == S_IDLE   ) && idle2word0  ) ? S_WORD0   :
                      ((word_state_c == S_WORD0  ) && word02word1 ) ? S_WORD1   :
                      ((word_state_c == S_WORD1  ) && word12word2 ) ? S_WORD2   :
                      ((word_state_c == S_WORD2  ) && word22word3 ) ? S_WORD3   :
                      ((word_state_c == S_WORD3  ) && word32idle  ) ? S_IDLE    :
                        word_state_c ;
//word_state_c
sirv_gnrl_edffr #(.T(word_state_t)) word_state_c_dffr (word_state_n,word_state_c,clk, rst_n);


//fifo_rd_en
assign fifo_rd_en = idle2word0;

wire [127:0] current_fifo_data;

sirv_gnrl_dfflr #(128) current_fifo_data_dfflr (fifo_rd_en, fifo_dout, current_fifo_data, clk, rst_n);

wire [31:0] current_word_w = (word_state_c == S_WORD0) ? current_fifo_data[31:0]   : 
                             (word_state_c == S_WORD1) ? current_fifo_data[63:32]  :
						     (word_state_c == S_WORD2) ? current_fifo_data[95:64]  :
						     (word_state_c == S_WORD3) ? current_fifo_data[127:96] :
						     32'h0;
wire        word_valid_w   = (word_state_c != S_IDLE);

wire [31:0] current_word; 

sirv_gnrl_dfflr #(32) current_word_dfflr (word_valid_w, current_word_w, current_word, clk, rst_n);

wire        word_valid;

sirv_gnrl_dffr #(1) word_valid_dffr (word_valid_w, word_valid, clk, rst_n);

// ------------------------------------------------------
// -- Frame parsing FSM
// ------------------------------------------------------	
typedef enum logic [2:0] { ST_IDLE, ST_HEAD, ST_DATA, ST_CRC } state_t;
state_t state_c, state_n;

//data_len
wire [15:0] data_len;
sirv_gnrl_dfflr #(16) data_len_dfflr ((state_c == ST_HEAD  ), current_word[15:0], data_len, clk, rst_n);
//data_cnt contrl
wire [15:0] data_cnt;
wire add_data_cnt = (state_c == ST_DATA  ) && word_valid;
wire end_data_cnt = add_data_cnt && (state_c == ST_DATA  ) && (data_cnt == data_len -1);

wire [15:0] data_cnt_n = end_data_cnt ? 5'h0               :
                         add_data_cnt ? data_cnt + 1'b1    :
                         data_cnt                          ;
							  
sirv_gnrl_dffr #(16) data_cnt_dffr (data_cnt_n, data_cnt, clk, rst_n);	


//jump conditions
wire idle2head = (state_c == ST_IDLE ) && (word_valid && current_word == FRAME_HEADER);
wire head2data = (state_c == ST_HEAD ) && word_valid;
wire data2crc  = (state_c == ST_DATA ) && end_data_cnt;
wire crc2idle  = (state_c == ST_CRC  ) && word_valid;

//state_n
assign state_n = ((state_c == ST_IDLE  ) && idle2head ) ? ST_HEAD   :
                 ((state_c == ST_HEAD  ) && head2data ) ? ST_DATA   :
                 ((state_c == ST_DATA  ) && data2crc  ) ? ST_CRC    :
                 ((state_c == ST_CRC   ) && crc2idle  ) ? ST_IDLE   :
                   state_c ;
//state_c
sirv_gnrl_edffr #(.T(state_t)) state_c_dffr (state_n,state_c,clk, rst_n);
                               

//cur_block_offset
wire [3:0] cur_block_offset;

wire add_cur_block_offset = (state_c == ST_DATA  ) && word_valid;
wire end_cur_block_offset = (state_c == ST_DATA  ) && add_cur_block_offset && (cur_block_offset == 16-1);;

wire [3:0] cur_block_offset_n = end_cur_block_offset   ? 5'h0                 :
                                (state_c == ST_HEAD  ) ? current_word[19:16]  :
                                add_cur_block_offset   ? cur_block_offset + 1'b1    :
                                cur_block_offset                              ;
							  
sirv_gnrl_dffr #(4) cur_block_offset_dffr (cur_block_offset_n, cur_block_offset, clk, rst_n);

//block_done
wire block_done;

sirv_gnrl_dffr #(1) block_done_dffr ((state_c == ST_DATA) && ((end_data_cnt && !end_cur_block_offset) || end_cur_block_offset), block_done, clk, rst_n);

//base_addr
wire [15:0] base_addr;
sirv_gnrl_dfflr #(16) base_addr_dfflr ((state_c == ST_HEAD  ), current_word[31:16], base_addr, clk, rst_n);

//cur_block_addr
wire [12:0] cur_block_addr;
wire [12:0] cur_block_addr_w = end_cur_block_offset ? cur_block_addr + 1'b1    : 
                               (state_c == ST_HEAD) ? current_word[31:16] >> 4 :
							   crc2idle             ? 13'd0                    :
							   cur_block_addr;

sirv_gnrl_dffr #(13) cur_block_addr_dffr (cur_block_addr_w, cur_block_addr, clk, rst_n);
							   
//cur_block_data
wire [31:0] cur_block_data [15:0]; 


genvar i;
generate
    for (i = 0; i < 16; i++) begin : gen_block_data
        wire load_sel = (state_c == ST_DATA) && (cur_block_offset == i);
		sirv_gnrl_dfflr #(32) cur_block_data_dfflr (load_sel, current_word, cur_block_data[i], clk, rst_n);
    end
endgenerate


//cur_block_mask
wire [63:0] cur_block_mask;

wire clear_mask = (state_c == ST_HEAD) || block_done;

genvar j;
generate
  for (j = 0; j < 16; j++) begin : gen_mask
    wire load_sel_mask = (state_c == ST_DATA) && (cur_block_offset == j);
    wire load_en = load_sel_mask || clear_mask;
    wire [3:0] dnxt = load_sel_mask ? 4'b1111 :
	                  clear_mask    ? 4'b0000 : 
					  4'b1111;
    sirv_gnrl_dfflr #(4) cur_block_mask_dfflr (load_en, dnxt, cur_block_mask[j*4 +: 4], clk, rst_n);
  end
endgenerate


 
//SRAM Write Ouput Contrl

//wr_addr
wire [12:0] cur_block_addr_r;
sirv_gnrl_dffr #(13) cur_block_addr_r_dffr (cur_block_addr, cur_block_addr_r, clk, rst_n);
sirv_gnrl_dfflr #(13) wr_addr_dfflr (block_done, cur_block_addr_r, wr_addr, clk, rst_n);	 

//wr_data
wire [511:0] cur_block_data_packed;
genvar k;
generate
  for (k = 0; k < 16; k++) begin : pack_cur_block_data
      assign cur_block_data_packed[32*k +: 32] = cur_block_data[k];
  end	
endgenerate

sirv_gnrl_dfflr #(512) wr_data_dfflr (block_done, cur_block_data_packed, wr_data, clk, rst_n);	


//byte_mask
sirv_gnrl_dfflr #(64) byte_mask_dfflr (block_done, cur_block_mask, byte_mask, clk, rst_n);

//wr_en
sirv_gnrl_dffr #(1) wr_en_dffr (block_done, wr_en, clk, rst_n);	    

//frame_done
sirv_gnrl_dffr #(1) frame_done_dffr (crc2idle, frame_done, clk, rst_n);

// ------------------------------------------------------
// -- CRC32 instance
// ------------------------------------------------------
wire crc_clear, crc_valid;
wire [31:0] crc_out;
crc32 u_crc32 (
   .clk       ( clk          )
  ,.rst_n     ( rst_n        )
  ,.clear     ( crc_clear    )
  ,.data      ( current_word )
  ,.valid     ( crc_valid    )
  ,.crc       ( crc_out      )
);

//CRC Contrl

//crc_clear
sirv_gnrl_dffr #(1) crc_clear_dffr (crc2idle, crc_clear, clk, rst_n);

//crc_valid
//sirv_gnrl_dffr #(1) crc_valid_dffr ((state_c == ST_HEAD) || (state_c == ST_DATA) && word_valid, crc_valid, clk, rst_n);
assign crc_valid = (state_c == ST_HEAD) || (state_c == ST_DATA) && word_valid;
//crc_error
sirv_gnrl_dffr #(1) crc_error_dffr (crc2idle && (current_word != crc_out), crc_error, clk, rst_n);			

wire [31:0] frame_status_w;

assign frame_status_w[2 : 0] = state_c;
assign frame_status_w[15: 3] = base_addr[15:3];
assign frame_status_w[31:16] = data_len;

sirv_gnrl_dffr #(32) frame_status_dffr (frame_status_w, frame_status, clk, rst_n);					
endmodule
// ----------------------------------------------------------------------------
// Training module (provided in user's file, placeholder here)
// Note: To avoid duplication, the definition of ulink_rx_train is omitted.
// In actual use, include the original file content here or via `include.
// ----------------------------------------------------------------------------
// Assume ulink_rx_train module exists, identical to the user-provided code.

module ulink_rx_train (
   input  logic         clk
  ,input  logic         rst_n
  ,input  logic [3  :0] serial_in          // 4-lane serial input
  ,input  logic [19 :0] patn_count         // Training count preset value
  ,input  logic         frame_error        // Frame error indication (from frame module)
  ,input  logic         frame_done         // Frame done indication (from frame module)
  ,input  logic [2  :0] tap_step           // Delay adjustment step value
  ,input  logic [2  :0] tap_adj_mask       // Delay adjustment mask
  ,input  logic [0  :0] tap_force          // Delay force to tap_step
  ,input  logic         force_train        // Force Training
  ,input  logic [0  :0] train_en           // Training enable
  ,output logic         link_down          // Link down indication
  ,output logic [2  :0] delay_tap          // Delay adjustment value
  ,output logic         tap_adj_req
  ,output logic         train_ready        // Training complete, link ready
  ,output logic [127:0] data_out           // Latched parallel data
  ,output logic         data_valid
  ,output logic [3  :0] descram_valid
  ,output logic [31 :0] train_status
);

// 4 32-bit shift registers, corresponding to 4 input lanes
wire [31:0] lane0_reg, lane1_reg, lane2_reg, lane3_reg;

sirv_gnrl_dffr #(32) lane0_reg_dffr ({lane0_reg[30:0], serial_in[0]}, lane0_reg, clk, rst_n); 
sirv_gnrl_dffr #(32) lane1_reg_dffr ({lane1_reg[30:0], serial_in[1]}, lane1_reg, clk, rst_n); 
sirv_gnrl_dffr #(32) lane2_reg_dffr ({lane2_reg[30:0], serial_in[2]}, lane2_reg, clk, rst_n); 
sirv_gnrl_dffr #(32) lane3_reg_dffr ({lane3_reg[30:0], serial_in[3]}, lane3_reg, clk, rst_n);  


// State definition
typedef enum logic [1:0] {
    SM_DOWN,   // Link down
    SM_MATCH,  // Match training pattern
    SM_EXIT,   // Wait for exit pattern
    SM_READY   // Link ready
} state_t;

state_t state_c, state_n;

localparam TRAINING_PATN = 32'h68666E6C;  // "hfnl"
localparam TRAINING_EXIT = 32'h65786974;  // "exit" 

// Bit counter control
wire  [4:0] bit_counter;

wire add_bit_conuter = ((state_c == SM_MATCH) || (state_c == SM_EXIT));
wire end_bit_conuter = add_bit_conuter && (bit_counter == 32-1);

wire [4:0] counter_m1 = end_bit_conuter ? 5'h0               :
                        add_bit_conuter ? bit_counter + 1'b1 :
                        bit_counter                          ;
							  
sirv_gnrl_dffr #(5) bit_counter_dffr (counter_m1, bit_counter, clk, rst_n);							  


// Combinational signals: all four lanes match simultaneously
wire match_en     =   (lane0_reg == TRAINING_PATN) &&
                      (lane1_reg == TRAINING_PATN) &&
                      (lane2_reg == TRAINING_PATN) &&
                      (lane3_reg == TRAINING_PATN);
wire match_exit   =   (lane0_reg == TRAINING_EXIT) &&
                      (lane1_reg == TRAINING_EXIT) &&
                      (lane2_reg == TRAINING_EXIT) &&
                      (lane3_reg == TRAINING_EXIT);
wire match_failed =   !(match_en | match_exit) & end_bit_conuter;

// Match counter
wire [19:0] match_counter_c;


wire add_match_counter = (state_c == SM_MATCH) && end_bit_conuter;

wire end_match_counter = add_match_counter && (match_counter_c == patn_count-2);

wire [19:0] match_counter_n = end_match_counter ? 20'h0                  :
                              add_match_counter ? match_counter_c + 1'b1 :
                              match_counter_c                            ;
							  
sirv_gnrl_dffr #(20) match_counter_c_dffr (match_counter_n, match_counter_c, clk, rst_n);								  
// ------------------------------------------------------
// -- state machine
// ------------------------------------------------------

//jump conditions
wire down2match = (state_c == SM_DOWN  ) && match_en && train_en;
wire match2down = (state_c == SM_MATCH ) && match_failed;
wire match2exit = (state_c == SM_MATCH ) && end_match_counter;
wire exit2down  = (state_c == SM_EXIT  ) && match_failed;
wire exit2ready = (state_c == SM_EXIT  ) && match_exit;
wire ready2down = (state_c == SM_READY ) && frame_error;


//state_n
assign  state_n = ((state_c == SM_DOWN  ) && down2match                  ) ? SM_MATCH  :
                  ((state_c == SM_MATCH ) && (match2down || force_train )) ? SM_DOWN   :
                  ((state_c == SM_MATCH ) && match2exit                  ) ? SM_EXIT   :
                  ((state_c == SM_EXIT  ) && (exit2down  || force_train )) ? SM_DOWN   :
                  ((state_c == SM_EXIT  ) && exit2ready                  ) ? SM_READY  :
                  ((state_c == SM_READY ) && (ready2down || force_train )) ? SM_DOWN   :
                    state_c ;
//state_c
sirv_gnrl_edffr #(.T(state_t)) state_c_dffr (state_n,state_c,clk, rst_n);
// Delay adjustment (step controlled by tap_step)

wire tap_adj_req_w = (tap_adj_mask[0] && match2down) || 
                     (tap_adj_mask[1] && exit2down ) || 
				     (tap_adj_mask[2] && ready2down) ;

wire  tap_adj_enable = tap_adj_req_w || tap_force;
				   
wire [2:0] delay_tap_w = tap_force ? tap_step :
                         delay_tap + tap_step ;	
						 
sirv_gnrl_dfflr #(3) delay_tap_dfflr (tap_adj_enable, delay_tap_w, delay_tap, clk, rst_n);


sirv_gnrl_dffr #(1) tap_adj_req_dffr (tap_adj_req_w, tap_adj_req, clk, rst_n); 
  
// Link status outputs

sirv_gnrl_dffr #(1) link_down_dffr ((state_c == SM_DOWN), link_down, clk, rst_n);  
sirv_gnrl_dffr #(1) train_ready_dffr ((state_c == SM_READY), train_ready, clk, rst_n);  


// ======================
// Data output 
// ======================
wire [127:0] data_buf;
sirv_gnrl_dffr #(128) data_buf_dffr ({lane3_reg, lane2_reg, lane1_reg, lane0_reg}, data_buf, clk, rst_n); 

// Frame header constant
localparam FRAME_HEADER = 32'hBCBCBCBC;

// ==============================================================================================================
// Data input control: In SM_READY state, latch data every 32 cycles and generate a valid pulse
// ==============================================================================================================

//match_head
wire match_head_start;
wire match_head = (data_buf[31:0] == FRAME_HEADER);
wire match_head_start_w = match_head && train_ready ? 1'b1 :
                          link_down                 ? 1'b0 :
						  frame_done                ? 1'b0 :
						  match_head_start;
	
sirv_gnrl_dffr #(1) match_head_start_dffr (match_head_start_w, match_head_start, clk, rst_n);
// data counter control
wire [4:0] data_counter;

wire add_data_conuter = match_head_start;
wire end_data_conuter = (add_data_conuter && (data_counter == 32-1) || frame_done);

wire [4:0] data_counter_n = end_data_conuter ? 5'h0                :
                            add_data_conuter ? data_counter + 1'b1 :
                            data_counter                           ;
							  
sirv_gnrl_dffr #(5) data_counter_dffr (data_counter_n, data_counter, clk, rst_n);		

//valid_int
wire data_valid_w = (state_c == SM_READY) && (match_head || (end_data_conuter && (data_counter == 32-1)));
sirv_gnrl_dffr #(1) valid_int_dffr (data_valid_w, data_valid, clk, rst_n);		

//descram_valid
wire [3:0] descram_valid_w;
assign descram_valid_w[3:1] = {3{data_valid_w}};
assign descram_valid_w[0] = (state_c == SM_READY) && (end_data_conuter && (data_counter == 32-1));
sirv_gnrl_dffr #(4) descram_valid_dffr (descram_valid_w, descram_valid, clk, rst_n);

//data_in reg
wire data_in_reg;
sirv_gnrl_dffr #(128) data_in_reg_dffr (data_buf, data_out, clk, rst_n);	

//train_status
wire [31:0] train_status_w;

assign train_status_w[1 : 0] = state_c;
assign train_status_w[2    ] = match_en;
assign train_status_w[3    ] = match_exit;
assign train_status_w[4    ] = match_failed;
assign train_status_w[5    ] = match_head;
assign train_status_w[6    ] = match_head_start;
assign train_status_w[11: 7] = bit_counter;
assign train_status_w[31:12] = match_counter_c;

sirv_gnrl_dffr #(32) train_status_dffr (train_status_w, train_status, clk, rst_n);

endmodule

// ----------------------------------------------------------------------------
// prefill Module (used in frame receiver)
// ----------------------------------------------------------------------------
module prefill #(
   parameter DELAY = 20'd10000
)(
   input  logic        clk
  ,input  logic        rst_n
  ,input  logic        always_on
  ,input  logic        prefilling
  ,output logic        prefill_start
  ,output logic        prefill_done
);

// delay counter control
wire [19:0] delay_counter;

wire add_delay_counter = ~(delay_counter == DELAY);
wire end_delay_counter = add_delay_counter && (delay_counter == DELAY-1);

wire [19:0] delay_counter_n = end_delay_counter ? 20'h0                :
                              add_delay_counter ? delay_counter + 1'b1 :
                              delay_counter                           ;
							  
sirv_gnrl_dffr #(20) delay_counter_dffr (delay_counter_n, delay_counter, clk, rst_n);

//prefill_start
sirv_gnrl_dffr #(1) prefill_start_dffr (end_delay_counter, prefill_start, clk, rst_n);

//prefilling_falling
wire [1:0] prefilling_r;
sirv_gnrl_dffr #(2) prefilling_r_dffr ({prefilling_r[0],prefilling}, prefilling_r, clk, rst_n);

wire prefilling_falling = !prefilling_r[0] && prefilling_r[1];
//prefill_done
sirv_gnrl_dfflr #(1) prefill_done_dfflr (prefilling_falling || always_on, 1'b1, prefill_done, clk, rst_n);


endmodule

