dakies/OSS_Verilog huggingface dataset:
module align_mux #(
  parameter DATA_PATH_WIDTH = 4
) (
  input clk,
  input [1:0] align,
  input [DATA_PATH_WIDTH*8-1:0] in_data,
  input [DATA_PATH_WIDTH-1:0] in_charisk,
  output reg [DATA_PATH_WIDTH*8-1:0] out_data,
  output reg [DATA_PATH_WIDTH-1:0] out_charisk
);

reg [DATA_PATH_WIDTH*8-1:0] in_data_d1;
reg [DATA_PATH_WIDTH-1:0] in_charisk_d1;

always @(posedge clk) begin
  in_data_d1 <= in_data;
  in_charisk_d1 <= in_charisk;
end

always @(*) begin
  case (align)
  'h0: out_data = in_data_d1;
  'h1: out_data = {in_data[7:0],in_data_d1[31:8]};
  'h2: out_data = {in_data[15:0],in_data_d1[31:16]};
  'h3: out_data = {in_data[23:0],in_data_d1[31:24]};
  endcase

  case (align)
  'h0: out_charisk = in_charisk_d1;
  'h1: out_charisk = {in_charisk[0:0],in_charisk_d1[3:1]};
  'h2: out_charisk = {in_charisk[1:0],in_charisk_d1[3:2]};
  'h3: out_charisk = {in_charisk[2:0],in_charisk_d1[3:3]};
  endcase
end

endmodule
module jesd204_lane_latency_monitor #(
  parameter NUM_LANES = 1
) (
  input clk,
  input reset,

  input [NUM_LANES-1:0] lane_ready,
  input [NUM_LANES*2-1:0] lane_frame_align,

  output [14*NUM_LANES-1:0] lane_latency,
  output [NUM_LANES-1:0] lane_latency_ready
);

reg [11:0] beat_counter;

reg [11:0] lane_latency_mem[0:NUM_LANES-1];
reg [NUM_LANES-1:0] lane_captured = 'h00;

always @(posedge clk) begin
  if (reset == 1'b1) begin
    beat_counter <= 'h0;
  end else if (beat_counter != 'hfff) begin
    beat_counter <= beat_counter + 1'b1;
  end
end

generate
genvar i;

for (i = 0; i < NUM_LANES; i = i + 1) begin: gen_lane
  always @(posedge clk) begin
    if (reset == 1'b1) begin
      lane_latency_mem[i] <= 'h00;
      lane_captured[i] <= 1'b0;
    end else if (lane_ready[i] == 1'b1 && lane_captured[i] == 1'b0) begin
      lane_latency_mem[i] <= beat_counter;
      lane_captured[i] <= 1'b1;
    end
  end

  assign lane_latency[i*14+13:i*14] = {lane_latency_mem[i],lane_frame_align[i*2+1:i*2]};
  assign lane_latency_ready[i] = lane_captured[i];
end
endgenerate

endmodule
module error_monitor #(
  parameter EVENT_WIDTH = 16,
  parameter CNT_WIDTH = 32
) (
  input clk,
  input reset,
  input active,
  input [EVENT_WIDTH-1:0] error_event,
  input [EVENT_WIDTH-1:0] error_event_mask,
  output reg [CNT_WIDTH-1:0] status_err_cnt = 'h0
);

localparam EVENT_WIDTH_LOG = $clog2(EVENT_WIDTH);

reg  [EVENT_WIDTH-1:0] err;

function [EVENT_WIDTH_LOG-1:0] num_set_bits;
input [EVENT_WIDTH-1:0] x;
integer j;
begin
  num_set_bits = 0;
  for (j = 0; j < EVENT_WIDTH; j = j + 1) begin
    num_set_bits = num_set_bits + x[j];
  end
end
endfunction

always @(posedge clk) begin
  if (active == 1'b1) begin
    err <= (~error_event_mask) & error_event;
  end else begin
    err <= {EVENT_WIDTH{1'b0}};
  end
end

always @(posedge clk) begin
  if (reset == 1'b1) begin
    status_err_cnt <= 'h0;
  end else if (~&status_err_cnt[CNT_WIDTH-1:EVENT_WIDTH_LOG]) begin
    status_err_cnt <= status_err_cnt + num_set_bits(err);
  end
end

endmodule
module jesd204_rx_ctrl #(
  parameter NUM_LANES = 1,
  parameter NUM_LINKS = 1
) (
  input clk,
  input reset,

  input [NUM_LANES-1:0] cfg_lanes_disable,
  input [NUM_LINKS-1:0] cfg_links_disable,

  input phy_ready,

  output phy_en_char_align,

  output [NUM_LANES-1:0] cgs_reset,
  input [NUM_LANES-1:0] cgs_ready,

  output [NUM_LANES-1:0] ifs_reset,

  input lmfc_edge,

  output [NUM_LINKS-1:0] sync,
  output reg latency_monitor_reset,

  output [1:0] status_state,
  output event_data_phase
);

localparam STATE_RESET = 0;
localparam STATE_WAIT_FOR_PHY = 1;
localparam STATE_CGS = 2;
localparam STATE_SYNCHRONIZED = 3;

reg [2:0] state = STATE_RESET;
reg [2:0] next_state = STATE_RESET;

reg [NUM_LANES-1:0] cgs_rst = {NUM_LANES{1'b1}};
reg [NUM_LANES-1:0] ifs_rst = {NUM_LANES{1'b1}};
reg [NUM_LINKS-1:0] sync_n = {NUM_LINKS{1'b1}};
reg en_align = 1'b0;
reg state_good = 1'b0;

reg [7:0] good_counter = 'h00;

wire [7:0] good_cnt_limit_s;
wire       good_cnt_limit_reached_s;

assign cgs_reset = cgs_rst;
assign ifs_reset = ifs_rst;
assign sync = sync_n;
assign phy_en_char_align = en_align;

assign status_state = state;

always @(posedge clk) begin
  case (state)
  STATE_RESET: begin
    cgs_rst <= {NUM_LANES{1'b1}};
    ifs_rst <= {NUM_LANES{1'b1}};
    sync_n <= {NUM_LINKS{1'b1}};
    latency_monitor_reset <= 1'b1;
  end
  STATE_CGS: begin
    sync_n <= cfg_links_disable;
    cgs_rst <= cfg_lanes_disable;
  end
  STATE_SYNCHRONIZED: begin
    if (lmfc_edge == 1'b1) begin
      sync_n <= {NUM_LINKS{1'b1}};
      ifs_rst <= cfg_lanes_disable;
      latency_monitor_reset <= 1'b0;
    end
  end
  endcase
end

always @(*) begin
  case (state)
  STATE_RESET: state_good = 1'b1;
  STATE_WAIT_FOR_PHY: state_good = phy_ready;
  STATE_CGS: state_good = &(cgs_ready | cfg_lanes_disable);
  STATE_SYNCHRONIZED: state_good = 1'b1;
  default: state_good = 1'b0;
  endcase
end

assign good_cnt_limit_s = (state == STATE_CGS) ? 'hff : 'h7;
assign good_cnt_limit_reached_s = good_counter == good_cnt_limit_s;

always @(posedge clk) begin
  if (reset) begin
    good_counter <= 'h00;
  end else if (state_good == 1'b1) begin
    if (good_cnt_limit_reached_s) begin
      good_counter <= 'h00;
    end else begin
      good_counter <= good_counter + 1'b1;
    end
  end else begin
    good_counter <= 'h00;
  end
end

always @(posedge clk) begin
  case (state)
  STATE_CGS: en_align <= 1'b1;
  default: en_align <= 1'b0;
  endcase
end

always @(*) begin
  case (state)
  STATE_RESET: next_state = STATE_WAIT_FOR_PHY;
  STATE_WAIT_FOR_PHY: next_state = STATE_CGS;
  STATE_CGS: next_state = STATE_SYNCHRONIZED;
  default: next_state = state_good ? state : STATE_RESET;
  endcase
end

always @(posedge clk) begin
  if (reset == 1'b1) begin
    state <= STATE_RESET;
  end else begin
    if (good_cnt_limit_reached_s) begin
      state <= next_state;
    end
  end
end

assign event_data_phase = state == STATE_CGS &&
                          next_state == STATE_SYNCHRONIZED &&
                          good_cnt_limit_reached_s;

endmodule
module jesd204_rx_cgs #(
  parameter DATA_PATH_WIDTH = 4
) (
  input clk,
  input reset,

  input [DATA_PATH_WIDTH-1:0] char_is_cgs,
  input [DATA_PATH_WIDTH-1:0] char_is_error,

  output ready,

  output [1:0] status_state
);

localparam CGS_STATE_INIT = 2'b00;
localparam CGS_STATE_CHECK = 2'b01;
localparam CGS_STATE_DATA = 2'b10;

reg [1:0] state = CGS_STATE_INIT;
reg rdy = 1'b0;
reg [1:0] beat_error_count = 'h00;

wire beat_is_cgs = &char_is_cgs;
wire beat_has_error = |char_is_error;
wire beat_is_all_error = &char_is_error;

assign ready = rdy;
assign status_state = state;

always @(posedge clk) begin
  if (state == CGS_STATE_INIT) begin
    beat_error_count <= 'h00;
  end else begin
    if (beat_has_error == 1'b1) begin
      beat_error_count <= beat_error_count + 1'b1;
    end else begin
      beat_error_count <= 'h00;
    end
  end
end

always @(posedge clk) begin
  if (reset == 1'b1) begin
    state <= CGS_STATE_INIT;
  end else begin
    case (state)
    CGS_STATE_INIT: begin
      if (beat_is_cgs == 1'b1) begin
        state <= CGS_STATE_CHECK;
      end
    end
    CGS_STATE_CHECK: begin
      if (beat_has_error == 1'b1) begin
        if (beat_error_count == 'h3 ||
            beat_is_all_error == 1'b1) begin
          state <= CGS_STATE_INIT;
        end
      end else begin
        state <= CGS_STATE_DATA;
      end
    end
    CGS_STATE_DATA: begin
      if (beat_has_error == 1'b1) begin
        state <= CGS_STATE_CHECK;
      end
    end
    endcase
  end
end

always @(posedge clk) begin
  case (state)
  CGS_STATE_INIT: rdy <= 1'b0;
  CGS_STATE_DATA: rdy <= 1'b1;
  default: rdy <= rdy;
  endcase
end

endmodule
module elastic_buffer #(
  parameter WIDTH = 32,
  parameter SIZE = 256
) (
  input clk,
  input reset,

  input [WIDTH-1:0] wr_data,

  output reg [WIDTH-1:0] rd_data,

  input ready_n,
  input do_release_n
);

localparam ADDR_WIDTH = SIZE > 128 ? 7 :
  SIZE > 64 ? 6 :
  SIZE > 32 ? 5 :
  SIZE > 16 ? 4 :
  SIZE > 8 ? 3 :
  SIZE > 4 ? 2 :
  SIZE > 2 ? 1 : 0;

reg [ADDR_WIDTH:0] wr_addr = 'h00;
reg [ADDR_WIDTH:0] rd_addr = 'h00;
reg [WIDTH-1:0] mem[0:SIZE - 1];

always @(posedge clk) begin
  if (ready_n == 1'b1) begin
    wr_addr <= 'h00;
  end else begin
    mem[wr_addr] <= wr_data;
    wr_addr <= wr_addr + 1'b1;
  end
end

always @(posedge clk) begin
  if (do_release_n == 1'b1) begin
    rd_addr <= 'h00;
  end else begin
    rd_addr <= rd_addr + 1'b1;
    rd_data <= mem[rd_addr];
  end
end

endmodule
module jesd204_rx_ctrl_64b #(
  parameter NUM_LANES = 1
) (
  input clk,
  input reset,

  input [NUM_LANES-1:0] cfg_lanes_disable,

  input [NUM_LANES-1:0] phy_block_sync,

  input [NUM_LANES-1:0] emb_lock,

  output all_emb_lock,
  input buffer_release_n,

  output [1:0] status_state,
  output reg event_unexpected_lane_state_error
);


localparam STATE_RESET = 2'b00;
localparam STATE_WAIT_BS = 2'b01;
localparam STATE_BLOCK_SYNC = 2'b10;
localparam STATE_DATA = 2'b11;

reg [1:0] state = STATE_RESET;
reg [1:0] next_state;
reg [5:0] good_cnt;
reg rst_good_cnt;

wire [NUM_LANES-1:0] phy_block_sync_masked;
wire [NUM_LANES-1:0] emb_lock_masked;
wire all_block_sync;

assign phy_block_sync_masked = phy_block_sync | cfg_lanes_disable;
assign emb_lock_masked = emb_lock | cfg_lanes_disable;

assign all_block_sync = &phy_block_sync_masked;
assign all_emb_lock = &emb_lock_masked;

always @(*) begin
  next_state = state;
  rst_good_cnt = 1'b1;
  event_unexpected_lane_state_error = 1'b0;
  case (state)
    STATE_RESET:
      next_state = STATE_WAIT_BS;
    STATE_WAIT_BS:
      if (all_block_sync) begin
        rst_good_cnt = 1'b0;
        if (&good_cnt) begin
          next_state = STATE_BLOCK_SYNC;
        end
      end
    STATE_BLOCK_SYNC:
      if (~all_block_sync) begin
        next_state = STATE_WAIT_BS;
      end else if (all_emb_lock & ~buffer_release_n) begin
        rst_good_cnt = 1'b0;
        if (&good_cnt) begin
          next_state = STATE_DATA;
        end
      end
    STATE_DATA:
      if (~all_block_sync) begin
        next_state = STATE_WAIT_BS;
        event_unexpected_lane_state_error = 1'b1;
      end else if (~all_emb_lock | buffer_release_n) begin
        next_state = STATE_BLOCK_SYNC;
        event_unexpected_lane_state_error = 1'b1;
      end
  endcase
end

// Wait n consecutive valid cycles before jumping into next state
always @(posedge clk) begin
  if (reset || rst_good_cnt) begin
    good_cnt <= 'h0;
  end else begin
    good_cnt <= good_cnt + 1;
  end
end

always @(posedge clk) begin
  if (reset == 1'b1) begin
    state <= STATE_RESET;
  end else begin
    state <= next_state;
  end
end

assign status_state = state;

endmodule
module jesd204_up_rx_lane (
  input up_clk,
  input up_reset_synchronizer,

  input up_rreq,
  input [2:0] up_raddr,
  output reg [31:0] up_rdata,

  input [1:0] up_status_cgs_state,
  input [31:0] up_status_err_statistics_cnt,
  input [2:0] up_status_emb_state,
  input [7:0] up_status_lane_frame_align_err_cnt,

  input core_clk,
  input core_reset,

  input core_ilas_config_valid,
  input [1:0] core_ilas_config_addr,
  input [31:0] core_ilas_config_data,

  input core_status_ifs_ready,
  input [13:0] core_status_latency
);

wire [1:0] up_status_ctrl_state;

wire up_status_ifs_ready;
reg [13:0] up_status_latency = 'h00;

wire [31:0] up_ilas_rdata;
wire up_ilas_ready;

sync_bits #(
  .NUM_OF_BITS(1)
) i_cdc_status_ready (
  .in_bits({
    core_status_ifs_ready
  }),
  .out_clk(up_clk),
  .out_resetn(1'b1),
  .out_bits({
    up_status_ifs_ready
  })
);

always @(posedge up_clk) begin
  if (up_reset_synchronizer == 1'b1) begin
    up_status_latency <= 'h00;
  end else begin
    if (up_status_ifs_ready == 1'b1) begin
      up_status_latency <= core_status_latency;
    end
  end
end

always @(*) begin
  if (up_raddr[2] == 1'b1) begin
    if (up_ilas_ready == 1'b1) begin
      up_rdata = up_ilas_rdata;
    end else begin
      up_rdata = 'h00;
    end
  end else begin
    case (up_raddr[1:0])
    2'b00: up_rdata = {
      /* 11-31 */ 21'h0, /* Reserved for future use */
      /* 08-10 */ up_status_emb_state,
      /* 06-07 */ 2'h00,
      /*    05 */ up_ilas_ready,
      /*    04 */ up_status_ifs_ready,
      /* 02-03 */ 2'b00, /* Reserved for future extensions of cgs_state */
      /* 00-01 */ up_status_cgs_state
    };
    2'b01: up_rdata = {
      /* 14-31 */ 18'h00, /* Reserved for future use */
      /* 00-13 */ up_status_latency
    };
    2'b10: up_rdata = {
      /* 00-31 */ up_status_err_statistics_cnt
    };
    2'b11: up_rdata = {
      /* 08-31 */ 24'h0, /* Reserved for future use */
      /* 00-07 */ up_status_lane_frame_align_err_cnt
    };
    default: up_rdata = 'h00;
    endcase
  end
end

jesd204_up_ilas_mem i_ilas_mem (
  .up_clk(up_clk),

  .up_rreq(up_rreq),
  .up_raddr(up_raddr[1:0]),
  .up_rdata(up_ilas_rdata),
  .up_ilas_ready(up_ilas_ready),

  .core_clk(core_clk),
  .core_reset(core_reset),

  .core_ilas_config_valid(core_ilas_config_valid),
  .core_ilas_config_addr(core_ilas_config_addr),
  .core_ilas_config_data(core_ilas_config_data)
);

endmodule
module jesd204_up_ilas_mem (
  input up_clk,

  input up_rreq,
  input [1:0] up_raddr,
  output reg [31:0] up_rdata,

  input core_clk,
  input core_reset,

  input core_ilas_config_valid,
  input [1:0] core_ilas_config_addr,
  input [31:0] core_ilas_config_data,

  output up_ilas_ready
);

reg [31:0] mem[0:3];
reg core_ilas_captured = 1'b0;

sync_bits i_cdc_ilas_ready (
  .in_bits(core_ilas_captured),
  .out_resetn(1'b1),
  .out_clk(up_clk),
  .out_bits(up_ilas_ready)
);

always @(posedge core_clk) begin
  if (core_reset == 1'b1) begin
    core_ilas_captured <= 1'b0;
  end else begin
    if (core_ilas_config_valid == 1'b1 && core_ilas_config_addr == 'h3) begin
      core_ilas_captured <= 1'b1;
    end
  end
end

always @(posedge up_clk) begin
  if (up_rreq == 1'b1) begin
    up_rdata <= mem[up_raddr];
  end
end

always @(posedge core_clk) begin
  if (core_ilas_config_valid == 1'b1) begin
    mem[core_ilas_config_addr] <= core_ilas_config_data;
  end
end

/*
 * Shift register with variable tap for accessing the stored data.
 *
 * This has slightly better utilization on Xilinx based platforms than the dual
 * port RAM approach, but there is no equivalent primitive on Intel resulting
 * in increased utilization since it needs to be implemented used registers and
 * muxes.
 *
 * We might make this a device dependent configuration option at some point.

reg [3:0] mem[0:31];

generate
genvar i;
for (i = 0; i < 32; i = i + 1) begin: gen_ilas_mem
  assign up_rdata[i] = mem[i][~up_raddr];

  always @(posedge core_clk) begin
    if (core_ilas_config_valid == 1'b1) begin
      mem[i] <= {mem[i][2:0],core_ilas_config_data[i]};
    end
  end
end
endgenerate
*/

endmodule
module jesd204_soft_pcs_tx #(
  parameter NUM_LANES = 1,
  parameter DATA_PATH_WIDTH = 4,
  parameter INVERT_OUTPUTS = 0
) (
   input clk,
   input reset,

   input [NUM_LANES*DATA_PATH_WIDTH*8-1:0] char,
   input [NUM_LANES*DATA_PATH_WIDTH-1:0] charisk,

   output reg [NUM_LANES*DATA_PATH_WIDTH*10-1:0] data
);

reg [NUM_LANES-1:0] disparity = 'h00;

wire [DATA_PATH_WIDTH:0] disparity_chain[0:NUM_LANES-1];
wire [NUM_LANES*DATA_PATH_WIDTH*10-1:0] data_s;

always @(posedge clk) begin
  data <= INVERT_OUTPUTS ? ~data_s : data_s;
end

generate
genvar lane;
genvar i;
for (lane = 0; lane < NUM_LANES; lane = lane + 1) begin: gen_lane
  assign disparity_chain[lane][0] = disparity[lane];

  always @(posedge clk) begin
    if (reset == 1'b1) begin
      disparity[lane] <= 1'b0;
    end else begin
      disparity[lane] <= disparity_chain[lane][DATA_PATH_WIDTH];
    end
  end

  for (i = 0; i < DATA_PATH_WIDTH; i = i + 1) begin: gen_dpw
    localparam j = DATA_PATH_WIDTH * lane + i;

    jesd204_8b10b_encoder i_enc (
      .in_char(char[j*8+:8]),
      .in_charisk(charisk[j]),
      .out_char(data_s[j*10+:10]),

      .in_disparity(disparity_chain[lane][i]),
      .out_disparity(disparity_chain[lane][i+1])
    );
  end
end
endgenerate

endmodule
module up_tpl_common #(

  // parameters
  parameter COMMON_ID = 2'h0,  // Offset of regmap
  parameter NUM_PROFILES = 1   // Number of JESD profiles
  )(

  input [NUM_PROFILES*8-1: 0] jesd_m,
  input [NUM_PROFILES*8-1: 0] jesd_l,
  input [NUM_PROFILES*8-1: 0] jesd_s,
  input [NUM_PROFILES*8-1: 0] jesd_f,
  input [NUM_PROFILES*8-1: 0] jesd_n,
  input [NUM_PROFILES*8-1: 0] jesd_np,

  output reg [$clog2(NUM_PROFILES):0] up_profile_sel = 'h0,

  // bus interface

  input               up_rstn,
  input               up_clk,
  input               up_wreq,
  input       [10:0]  up_waddr,
  input       [31:0]  up_wdata,
  output              up_wack,
  input               up_rreq,
  input       [10:0]  up_raddr,
  output      [31:0]  up_rdata,
  output              up_rack
);

  // internal registers
  reg             up_rack_int = 'd0;
  reg             up_wack_int = 'd0;
  reg     [31:0]  up_rdata_int = 'd0;

  // internal signals
  wire            up_wreq_s;
  wire            up_rreq_s;
  reg     [31:0]  up_rdata_jesd_params;

  // decode block select

  assign up_wreq_s = (up_waddr[10:7] == {COMMON_ID,1'b1}) ? up_wreq : 1'b0;
  assign up_rreq_s = (up_raddr[10:7] == {COMMON_ID,1'b1}) ? up_rreq : 1'b0;

  // processor write interface

  assign up_wack = up_wack_int;

  always @(negedge up_rstn or posedge up_clk) begin
    if (up_rstn == 0) begin
      up_wack_int <= 'd0;
      up_profile_sel <= 'd0;
   end else begin
      up_wack_int <= up_wreq_s;
      if ((up_wreq_s == 1'b1) && (up_waddr[6:0] == 7'h00)) begin
        up_profile_sel <= up_wdata[$clog2(NUM_PROFILES):0];
      end
    end
  end

  // processor read interface

  assign up_rack = up_rack_int;
  assign up_rdata = up_rdata_int;

  always @(negedge up_rstn or posedge up_clk) begin
    if (up_rstn == 0) begin
      up_rack_int <= 'd0;
      up_rdata_int <= 'd0;
    end else begin
      up_rack_int <= up_rreq_s;
      if (up_rreq_s == 1'b1) begin
        case (up_raddr[6:0])
          7'h00: up_rdata_int <= up_profile_sel;
          7'h01: up_rdata_int <= NUM_PROFILES;
         default: up_rdata_int <= up_rdata_jesd_params;
        endcase
      end else begin
        up_rdata_int <= 32'd0;
      end
    end
  end

  integer i;
  always @(*) begin
    for (i=0; i<NUM_PROFILES; i = i + 1) begin:jesd_param
      up_rdata_jesd_params = 0;
      if (up_rreq_s == 1'b1) begin
        if (up_raddr[6:0] == 7'h10 + {i[5:0],1'b0}) begin
          up_rdata_jesd_params = {jesd_f[i*8+:8], jesd_s[i*8+:8],
                                  jesd_l[i*8+:8], jesd_m[i*8+:8]};
        end
        if (up_raddr[6:0] == 7'h11 + {i[5:0],1'b0}) begin
          up_rdata_jesd_params = {16'b0,jesd_np[i*8+:8], jesd_n[i*8+:8]};
        end
      end
    end
  end

endmodule
module jesd204_tx_lane #(
  parameter DATA_PATH_WIDTH = 4
) (
  input clk,

  input [DATA_PATH_WIDTH-1:0] eof,
  input eomf,

  input cgs_enable,

  input [DATA_PATH_WIDTH*8-1:0] ilas_data,
  input [DATA_PATH_WIDTH-1:0] ilas_charisk,

  input [DATA_PATH_WIDTH*8-1:0] tx_data,
  input tx_ready,

  output reg [DATA_PATH_WIDTH*8-1:0] phy_data,
  output reg [DATA_PATH_WIDTH-1:0] phy_charisk,

  input cfg_disable_scrambler
);

wire [DATA_PATH_WIDTH*8-1:0] scrambled_data;
wire [7:0] scrambled_char[0:DATA_PATH_WIDTH-1];
reg [7:0] char_align[0:DATA_PATH_WIDTH-1];

jesd204_scrambler #(
  .WIDTH(DATA_PATH_WIDTH*8),
  .DESCRAMBLE(0)
) i_scrambler (
  .clk(clk),
  .reset(~tx_ready),
  .enable(~cfg_disable_scrambler),
  .data_in(tx_data),
  .data_out(scrambled_data)
);

generate
genvar i;

for (i = 0; i < DATA_PATH_WIDTH; i = i + 1) begin: gen_char
  assign scrambled_char[i] = scrambled_data[i*8+7:i*8];

  always @(*) begin
    if (i == DATA_PATH_WIDTH-1 && eomf == 1'b1) begin
      char_align[i] = 8'h7c; // /A/
    end else begin
      char_align[i] = 8'hfc; // /F/
    end
  end

  always @(posedge clk) begin
    if (cgs_enable == 1'b1) begin
      phy_charisk[i] <= 1'b1;
    end else if (eof[i] == 1'b1 && scrambled_char[i] == char_align[i]) begin
      phy_charisk[i] <= 1'b1;
    end else begin
      phy_charisk[i] <= ilas_charisk[i];
    end
  end
end

endgenerate

always @(posedge clk) begin
  if (cgs_enable == 1'b1) begin
    phy_data <= {DATA_PATH_WIDTH{8'hbc}};
  end else begin
    phy_data <= (tx_ready) ? scrambled_data : ilas_data;
  end
end

endmodule
module jesd204_tx_lane_64b (
  input clk,
  input reset,

  input [63:0] tx_data,
  input tx_ready,

  output reg [63:0] phy_data,
  output     [1:0] phy_header,

  input lmc_edge,
  input lmc_quarter_edge,
  input eoemb,

  // Scrambling mandatory in 64bxxb, keep this for debugging purposes
  input cfg_disable_scrambler,
  input [1:0] cfg_header_mode,
  input cfg_lane_disable

);

reg [63:0] scrambled_data;
reg lmc_edge_d1 = 'b0;
reg lmc_edge_d2 = 'b0;
reg lmc_quarter_edge_d1 = 'b0;
reg lmc_quarter_edge_d2 = 'b0;
reg tx_ready_d1 = 'b0;

wire [63:0] tx_data_msb_s;
wire [63:0] scrambled_data_r;
wire [11:0] crc12;

/* Reorder octets MSB first */
genvar i;
generate
  for (i = 0; i < 64; i = i + 8) begin: g_link_data
    assign tx_data_msb_s[i+:8] = tx_data[64-1-i-:8];
  end
endgenerate

jesd204_scrambler_64b #(
  .WIDTH(64),
  .DESCRAMBLE(0)
) i_scrambler (
  .clk(clk),
  .reset(reset),
  .enable(~cfg_disable_scrambler),
  .data_in(tx_data_msb_s),
  .data_out(scrambled_data_r)
);

always @(posedge clk) begin
  lmc_edge_d1 <= lmc_edge;
  lmc_edge_d2 <= lmc_edge_d1;
  lmc_quarter_edge_d1 <= lmc_quarter_edge;
  lmc_quarter_edge_d2 <= lmc_quarter_edge_d1;
end

always @(posedge clk) begin
  scrambled_data <= scrambled_data_r;
  phy_data <= scrambled_data;
end

always @(posedge clk) begin
  tx_ready_d1 <= tx_ready;
end

jesd204_crc12 i_crc12 (
  .clk(clk),
  .reset(~tx_ready_d1),
  .init(lmc_edge_d2),
  .data_in(scrambled_data),
  .crc12(crc12)
);

jesd204_tx_header i_header_gen (
  .clk(clk),
  .reset(~tx_ready | cfg_lane_disable),

  .cfg_header_mode(cfg_header_mode),

  .lmc_edge(lmc_edge_d2),
  .lmc_quarter_edge(lmc_quarter_edge_d2),

  // Header content to be sent must be valid during lmc_edge
  .eoemb(eoemb),
  .crc3(3'b0),
  .crc12(crc12),
  .fec(26'b0),
  .cmd(19'b0),

  .header(phy_header)

);


endmodule
module jesd204_tx_header (
  input clk,
  input reset,

  input [1:0]  cfg_header_mode,

  input        lmc_edge,
  input        lmc_quarter_edge,

  // Header content to be sent must be valid during lmc_edge
  input        eoemb,
  input [2:0]  crc3,
  input [11:0] crc12,
  input [25:0] fec,
  input [18:0] cmd,

  output [1:0] header

);

  reg header_bit;
  reg [31:0] sync_word = 'h0;


  always @(posedge clk) begin
    if (reset) begin
      sync_word <= 'h0;
    end else if (lmc_edge) begin
      case (cfg_header_mode)
        // CRC-12
        2'b00 : sync_word <= {crc12[11:9],1'b1,crc12[8:6],1'b1,
                               crc12[5:3],1'b1,crc12[2:0],1'b1,
                                 cmd[6:4],1'b1,cmd[3],1'b1,eoemb,1'b1,
                                 cmd[2:0],5'b00001};
        // CRC-3
        2'b01 : sync_word <= {  crc3[2:0],1'b1,cmd[6:4],1'b1,
                                   3'b000,1'b1,cmd[3:1],1'b1,
                                   3'b000,1'b1,cmd[0],1'b1,eoemb,1'b1,
                                   3'b000,5'b00001};
        // FEC
        2'b10 : sync_word <= { fec[25:18],
                               fec[17:10],
                                 fec[9:4],eoemb,fec[3],
                                 fec[2:0],5'b00001};
        // Stand alone command
        2'b11 : sync_word <= { cmd[18:16],1'b1,cmd[15:13],1'b1,
                               cmd[12:10],1'b1,cmd[9:7],1'b1,
                                 cmd[6:4],1'b1,cmd[3],1'b1,eoemb,1'b1,
                                 cmd[2:0],5'b00001};
      endcase
    end else begin
      if (lmc_quarter_edge && cfg_header_mode == 2'b01) begin
        sync_word <= {crc3[2],crc3[1],crc3[0],sync_word[27:0],1'b0};
      end else begin
        sync_word <= {sync_word[30:0],1'b0};
      end
    end
  end

  assign header = {~sync_word[31],sync_word[31]};

endmodule
module rx_ctrl_tb;
  parameter VCD_FILE = "rx_ctrl_tb.vcd";

  `include "tb_base.v"

  integer phy_reset_counter = 'h00;
  integer align_counter = 'h00;
  integer cgs_counter = 'h00;

  reg phy_ready = 1'b0;
  reg aligned = 1'b0;
  reg cgs_ready = 1'b0;

  wire en_align;
  wire cgs_reset;

  always @(posedge clk) begin
    if (reset == 1'b1) begin
      phy_reset_counter <= 'h00;
      phy_ready <= 1'b0;
    end else begin
      if (phy_reset_counter == 'h7) begin
        phy_ready <= 1'b1;
      end else begin
        phy_reset_counter <= phy_reset_counter + 1'b1;
      end
    end
  end

  always @(posedge clk) begin
    if (reset == 1'b1) begin
      aligned <= 1'b0;
      align_counter <= 'h00;
    end else if (phy_ready == 1'b1) begin
      if (en_align == 1'b1) begin
        if (align_counter == 'h20) begin
          aligned <= 1'b1;
        end else begin
          align_counter <= align_counter + 1;
        end
      end
    end
  end


  always @(posedge clk or posedge cgs_reset) begin
    if (cgs_reset == 1'b1) begin
      cgs_counter <= 'h00;
      cgs_ready <= 1'b0;
    end else begin
      if (cgs_counter == 'h20) begin
        cgs_ready <= 1'b1;
      end else begin
        cgs_counter <= cgs_counter + 1;
      end
    end
  end

  jesd204_rx_ctrl i_rx_ctrl (
    .clk(clk),
    .reset(reset),
    .phy_ready(phy_ready),
    .phy_en_char_align(en_align),
    .cgs_reset(cgs_reset),
    .cgs_ready(cgs_ready)
  );

endmodule
module scrambler_64b_tb;
  parameter VCD_FILE = "scrambler_64b_tb.vcd";

  `include "tb_base.v"
  reg failed_t1 = 1'b0;
  reg failed_t2 = 1'b0;

  // Test scrambler against descrambler
  //
  // Descrambled data should match the input of the scrambler.
  reg [63:0] data_in = 'h0;
  reg [63:0] data_out_expected;
  wire [63:0] data_scrambled;
  wire [63:0] data_out;

  always @(posedge clk) begin
    if (reset == 1'b1) begin
      data_in <= 'h0001020304050607;
    end else begin
      data_in <= data_in + {8{8'h08}};
    end
  end


  jesd204_scrambler_64b #(
    .DESCRAMBLE(0)
  ) i_scrambler (
    .clk(clk),
    .reset(reset),
    .enable(1'b1),
    .data_in(data_in),
    .data_out(data_scrambled)
  );

  jesd204_scrambler_64b #(
    .DESCRAMBLE(1)
  ) i_descrambler (
    .clk(clk),
    .reset(reset),
    .enable(1'b1),
    .data_in(data_scrambled),
    .data_out(data_out)
  );
  always @(posedge clk) begin
    if (data_in != data_out && failed_t1 == 1'b0) begin
      failed_t1 <= 1'b1;
    end
  end

  // Test descrambler against reference data
  //
  // Check if descrambler can synchronize and a stream captured from
  // a scrambler output. The descrambled data stream should match the input of
  // the scrambler.
  reg [63:0] descrambler_data_in = 'h0;
  reg [63:0] data_ref = 'h0;
  wire[63:0] descrambler_data_out;

  integer i;
  reg [63:0] scrambler_64b_input [0:993];
  reg [63:0] scrambler_64b_output [0:993];
  reg t2_enable = 1'b0;

  initial begin
    $readmemh("scrambler_64b_input.txt", scrambler_64b_input);   // Input to a scrambler
    $readmemh("scrambler_64b_output.txt", scrambler_64b_output); // Output of a scrambler
    @(negedge reset);
    @(posedge clk);
    for (i=0;i<994;i=i+1) begin
      @(posedge clk);
      descrambler_data_in <= scrambler_64b_output[i];
      data_ref <= scrambler_64b_input[i];
      if (i==1) t2_enable <= 1'b1;
      if (i==993) t2_enable <= 1'b0;
    end
  end

  jesd204_scrambler_64b #(
    .DESCRAMBLE(1)
  ) i_descrambler2 (
    .clk(clk),
    .reset(reset),
    .enable(1'b1),
    .data_in(descrambler_data_in),
    .data_out(descrambler_data_out)
  );

  always @(posedge clk) begin
    if (data_ref != descrambler_data_out && failed_t2 == 1'b0 && t2_enable) begin
      failed_t2 <= 1'b1;
    end
  end

  always @(posedge clk) begin
    failed <= failed_t1 || failed_t2;
  end


endmodule
module crc12_tb;
  parameter VCD_FILE = "crc12_tb.vcd";

  `define TIMEOUT 400

  `include "tb_base.v"

  reg [63:0] data_in = 'h0;
  reg [11:0] ref_crc12 = 'h0;
  wire [11:0] crc12;
  reg test_en = 1'b1;
  reg init = 1'b0;

  jesd204_crc12 DUT (
    .clk (clk),
    .reset (1'b0),
    .init (init),
    .data_in (data_in),
    .crc12 (crc12)
  );

  // Test against dataset from the standard
  //  - Test contiguous input stream with init phase
  initial begin
    @(negedge reset);
    @(posedge clk);
    repeat (3) begin
      @(posedge clk) data_in <= 'h80_01_02_03_05_05_04_23;
      init <= 1'b1;
      @(posedge clk) data_in <= 'h0e_43_80_c2_0b_50_81_cd;
      init <= 1'b0;
      @(posedge clk) data_in <= 'h04_e7_83_92_5a_3c_aa_51;
      @(posedge clk) data_in <= 'h05_4d_87_d9_31_3d_11_51;
    end
    @(posedge clk);
    @(posedge clk);
    test_en <= 1'b0;
  end

   initial begin
    @(negedge reset);
    @(posedge clk);
    @(posedge clk);
    repeat(3) begin
      @(posedge clk) ref_crc12 <= 'hd00;
      @(posedge clk) ref_crc12 <= 'h11c;
      @(posedge clk) ref_crc12 <= 'hfea;
      @(posedge clk) ref_crc12 <= 'h5fe;
    end
  end


  always @(posedge clk) begin
    if (ref_crc12 != crc12 && failed == 1'b0 && test_en) begin
      failed <= 1'b1;
    end
  end

endmodule
module rx_cgs_tb;
  parameter VCD_FILE = "rx_cgs_tb.vcd";

  `define TIMEOUT 1000
  `include "tb_base.v"

  reg [3:0] char_is_error = 4'b0000;
  reg [3:0] char_is_cgs = 4'b1111;

  integer counter = 'h00;
  wire ready;
  reg ready_prev = 1'b0;
/*
  always @(posedge clk) begin
    if ($random % 2 == 0)
      char_is_error <= 4'b1111;
    else
      char_is_error <= 4'b0000;
  end
*/
  always @(posedge clk) begin
    counter <= counter + 1;
    if (counter == 'h20) begin
      char_is_cgs <= 4'b0001;
    end else if (counter > 'h20) begin
      char_is_cgs <= 4'b0000;
    end
  end

  jesd204_rx_cgs i_rx_cgs (
    .clk(clk),
    .reset(reset),
    .char_is_cgs(char_is_cgs),
    .char_is_error(char_is_error),

    .ready(ready)
  );

  reg lost_sync = 1'b0;

  always @(posedge clk) begin
    ready_prev <= ready;
    if (ready_prev == 1'b1 && ready == 1'b0) begin
      lost_sync <= 1'b1;
    end
    failed <= lost_sync | ~ready;
  end

endmodule
module scrambler_tb;
  parameter VCD_FILE = "scrambler_tb.vcd";

  `include "tb_base.v"

  reg [31:0] data_in;
  reg [31:0] data_out_expected;
  wire [31:0] data_scrambled;
  wire [31:0] data_out;

  always @(posedge clk) begin
    if (reset == 1'b1) begin
      data_in <= 'h03020100;
    end else begin
      data_in <= data_in + {4{8'h04}};
    end
  end

  jesd204_scrambler #(
    .DESCRAMBLE(0)
  ) i_scrambler (
    .clk(clk),
    .reset(reset),
    .enable(1'b1),
    .data_in(data_in),
    .data_out(data_scrambled)
  );

  jesd204_scrambler #(
    .DESCRAMBLE(1)
  ) i_descrambler (
    .clk(clk),
    .reset(reset),
    .enable(1'b1),
    .data_in(data_scrambled),
    .data_out(data_out)
  );

  always @(posedge clk) begin
    if (data_in != data_out && failed == 1'b0) begin
      failed <= 1'b1;
    end
  end

endmodule
module rx_lane_tb;
  parameter VCD_FILE = "rx_lane_tb.vcd";

  `include "tb_base.v"

  reg [31:0] data = {4{{3'd5,5'd28}}};
  reg [3:0] disperr = 4'b0000;
  reg [3:0] notintable = 4'b0000;
  reg [3:0] charisk = 4'b1111;

  wire ilas_config_valid;
  wire [1:0] ilas_config_addr;
  wire [4*8-1:0] ilas_config_data;
  wire [31:0] status_err_statistics_cnt;

  wire [4*8-1:0] rx_data;

  wire [1:0] status_cgs_state;
  wire status_ifs_ready;
  wire [1:0] status_frame_align;

  integer counter = 'h00;
  wire [31:0] counter2 = (counter - 'h20) * 4;

  always @(posedge clk) begin
    if ($urandom % 400 == 0)
      disperr <= 4'b1111;
    else if ($urandom % 400 == 1)
      disperr <= 4'b0001;
    else if ($urandom % 400 == 2)
      disperr <= 4'b0011;
    else if ($urandom % 400 == 3)
      disperr <= 4'b0111;
    else
      disperr <= 4'b0000;
  end

  always @(posedge clk) begin
    if ($random % 500 == 0)
      notintable <= 4'b1111;
    else
      notintable <= 4'b0000;
  end

  always @(posedge clk) begin
    counter <= counter + 1;
    if (counter == 'h20) begin
      charisk <= 'h0001;
      data[31:8] <= {{24'h020100}};
    end else if (counter == 'h21) begin
      charisk <= 'h0000;
      data[31:0] <= {{32'h06050403}};
    end else if (counter > 'h21) begin
      data <= (counter2 + 'h2) << 24 |
              (counter2 + 'h1) << 16 |
          (counter2 + 'h0) << 8 |
          (counter2 - 'h1);
      charisk <= 4'b0000;
    end
  end

  reg buffer_release_n = 1'b0;
  wire buffer_ready_n;

  always @(posedge clk) begin
    buffer_release_n <= buffer_ready_n;
  end

  jesd204_rx_lane i_rx_lane (
    .clk(clk),
    .reset(1'b0),

    .phy_data(data),
    .phy_charisk(charisk),
    .phy_disperr(disperr),
    .phy_notintable(notintable),

    .cgs_reset(reset),
    .cgs_ready(),

    .ifs_reset(1'b0),

    .rx_data(rx_data),

    .buffer_release_n(buffer_release_n),
    .buffer_ready_n(buffer_ready_n),

    .cfg_disable_scrambler(1'b0),
    .ilas_config_valid(ilas_config_valid),
    .ilas_config_addr(ilas_config_addr),
    .ilas_config_data(ilas_config_data),

    .ctrl_err_statistics_reset(1'b0),
    .ctrl_err_statistics_mask(3'h7),
    .status_err_statistics_cnt(status_err_statistics_cnt),

    .status_cgs_state(status_cgs_state),
    .status_ifs_ready(status_ifs_ready),
    .status_frame_align(status_frame_align)
  );

endmodule
module soft_pcs_8b10b_table_tb;
  parameter VCD_FILE = "soft_pcs_8b10b_table_tb.vcd";

  `include "tb_base.v"

  // Build a table of all valid 8b10b words using the encoder and then check
  // for every possible 10-bit value that the decoder correctly reports the
  // not-in-table flag

  reg [1023:0] valid_table = 'h00;

  reg [8:0] counter = 10'h00;

  reg [7:0] encoder_char = 8'h00;
  reg encoder_charisk = 1'b0;
  reg encoder_disparity = 1'b0;
  wire [9:0] encoder_raw;

  wire [7:0] decoder_char;
  wire decoder_notintable;
  wire decoder_charisk;
  reg [9:0] decoder_raw = 10'h00;

  reg build_k28 = 1'b0;
  reg build_table = 1'b1;

  reg decoder_disparity = 1'b0;
  wire decoder_disparity_s;

  always @(posedge clk) begin
    counter <= counter + 1'b1;

    if (counter == 'h1ff) begin
      build_k28 <= 1'b1;
    end else if (counter == 'h9 && build_k28 == 1'b1) begin
      build_table <= 1'b0;
    end
  end

  always @(*) begin
    encoder_disparity <= counter[0];
    if (build_k28 == 1'b0) begin
      encoder_char <= counter[8:1];
      encoder_charisk <= 1'b0;
    end else begin
      case (counter[8:1])
      0: encoder_char <= {3'd0,5'd28};
      1: encoder_char <= {3'd3,5'd28};
      2: encoder_char <= {3'd4,5'd28};
      3: encoder_char <= {3'd5,5'd28};
      4: encoder_char <= {3'd7,5'd28};
      endcase
      encoder_charisk <= 1'b1;
    end
  end

  jesd204_8b10b_encoder i_enc (
    .in_char(encoder_char),
    .in_charisk(encoder_charisk),
    .in_disparity(encoder_disparity),
    .out_char(encoder_raw)
  );

  always @(posedge clk) begin
    if (build_table == 1'b1) begin
      valid_table[encoder_raw] <= 1'b1;
    end
  end

  always @(posedge clk) begin
    if (build_table == 1'b0) begin
      decoder_disparity <= ~decoder_disparity;
      if (decoder_disparity == 1'b1 && decoder_raw != 'h3ff) begin
        decoder_raw <= decoder_raw + 1'b1;
      end
    end
  end

  always @(posedge clk) begin
  end

  jesd204_8b10b_decoder i_dec (
    .in_char(build_table ? encoder_raw : decoder_raw),
    .out_char(decoder_char),
    .out_notintable(decoder_notintable),
    .out_charisk(decoder_charisk),

    .in_disparity(decoder_disparity),
    .out_disparity(decoder_disparity_s)
  );

  wire decoder_should_be_in_table = valid_table[decoder_raw];

  always @(posedge clk) begin
    if (build_table == 1'b0) begin
      if (decoder_notintable == decoder_should_be_in_table) begin
        $display("%b.%b, %d", decoder_raw[9:6], decoder_raw[5:0],
          decoder_should_be_in_table);
        failed <= 1'b1;
      end
     end
  end

endmodule
module soft_pcs_pattern_align_tb;
  parameter VCD_FILE = "soft_pcs_pattern_align_tb.vcd";

  localparam [9:0] PATTERN_P = 10'b1010000011;
  localparam [9:0] PATTERN_N = 10'b0101111100;

  `define TIMEOUT 1000000
  `include "tb_base.v"

  integer counter = 0;

  reg [3:0] bitshift = 4'd0;
  reg [9:0] comma_unaligned;
  reg comma_sync = 1'b0;
  wire [9:0] comma_aligned;
  reg [9:0] comma = PATTERN_P;

  wire comma_match = comma == comma_aligned;

  always @(posedge clk) begin
    if (counter == 63) begin
      if (comma_sync != 1'b1) begin
        failed <= 1'b1;
      end

      comma_sync <= 1'b0;
      counter <= 0;
      bitshift <= {$random} % 10;
      comma <= {$random} % 2 ? PATTERN_P : PATTERN_N;
    end else begin
      counter <= counter + 1'b1;

      // Takes two clock cycles for the new value to propagate
      if (counter > 1) begin
        // Shouldn't loose sync after it gained it
        if (comma_match == 1'b1) begin
          comma_sync <= 1'b1;
        end else if (comma_sync == 1'b1) begin
          failed <= 1'b1;
        end
      end
    end
  end

  always @(*) begin
    case (bitshift)
    4'd0: comma_unaligned <= comma[9:0];
    4'd1: comma_unaligned <= {comma[0],comma[9:1]};
    4'd2: comma_unaligned <= {comma[1:0],comma[9:2]};
    4'd3: comma_unaligned <= {comma[2:0],comma[9:3]};
    4'd4: comma_unaligned <= {comma[3:0],comma[9:4]};
    4'd5: comma_unaligned <= {comma[4:0],comma[9:5]};
    4'd6: comma_unaligned <= {comma[5:0],comma[9:6]};
    4'd7: comma_unaligned <= {comma[6:0],comma[9:7]};
    4'd8: comma_unaligned <= {comma[7:0],comma[9:8]};
    default: comma_unaligned <= {comma[8:0],comma[9]};
    endcase
  end

  jesd204_pattern_align #(
    .DATA_PATH_WIDTH(1)
  ) i_pa (
    .clk(clk),
    .reset(1'b0),
    .patternalign_en(1'b1),

    .in_data(comma_unaligned),
    .out_data(comma_aligned)
  );

endmodule
module soft_pcs_8b10b_sequence_tb;
  parameter VCD_FILE = "soft_pcs_8b10b_sequence_tb.vcd";

  `include "tb_base.v"

  // Send a random sequence of characters to the decoder and make sure the
  // decoder produces the same character sequence and no disparity or
  // not-in-table errors

  wire [9:0] raw_data;

  reg [7:0] encoder_char = 'h00;
  reg encoder_charisk = 1'b0;

  wire [7:0] decoder_char;
  wire decoder_charisk;
  wire decoder_notintable;
  wire decoder_disperr;

  reg encoder_disparity = 1'b0;
  wire encoder_disparity_s;
  reg decoder_disparity = 1'b0;
  wire decoder_disparity_s;

  integer x;

  reg inject_enc_disparity_error = 1'b0;

/*
  always @(posedge clk) begin
    if ({$random} % 256 == 0) begin
      inject_enc_disparity_error <= 1'b1;
    end else begin
      inject_enc_disparity_error <= 1'b0;
    end
  end
*/

  always @(posedge clk) begin
    x = {$random} % (256 + 5);
    if (x < 256) begin
      encoder_char <= x & 8'hff;
      encoder_charisk <= 1'b0;
    end else begin
      case (x - 256)
      0: encoder_char <= {3'd0,5'd28};
      1: encoder_char <= {3'd3,5'd28};
      2: encoder_char <= {3'd4,5'd28};
      3: encoder_char <= {3'd5,5'd28};
      4: encoder_char <= {3'd7,5'd28};
      endcase
      encoder_charisk <= 1'b1;
    end
    encoder_disparity <= encoder_disparity_s ^ inject_enc_disparity_error;
    decoder_disparity <= decoder_disparity_s;
  end

  jesd204_8b10b_encoder i_enc (
    .in_char(encoder_char),
    .in_charisk(encoder_charisk),
    .out_char(raw_data),

    .in_disparity(encoder_disparity),
    .out_disparity(encoder_disparity_s)
  );

  jesd204_8b10b_decoder i_dec (
    .in_char(raw_data),
    .out_char(decoder_char),
    .out_notintable(decoder_notintable),
    .out_disperr(decoder_disperr),
    .out_charisk(decoder_charisk),

    .in_disparity(decoder_disparity),
    .out_disparity(decoder_disparity_s)
  );

   wire char_mismatch = encoder_char != decoder_char;
   wire charisk_mismatch = encoder_charisk != decoder_charisk;

   always @(posedge clk) begin
     if (char_mismatch == 1'b1 || charisk_mismatch == 1'b1 ||
         decoder_notintable == 1'b1 || decoder_disperr == 1'b1) begin
       failed <= 1'b1;
     end
   end

endmodule
module jesd204_up_sysref #(
  parameter DATA_PATH_WIDTH = 2
) (
  input up_clk,
  input up_reset,

  input core_clk,

  input [11:0] up_raddr,
  output reg [31:0] up_rdata,

  input up_wreq,
  input [11:0] up_waddr,
  input [31:0] up_wdata,

  input up_cfg_is_writeable,

  output reg up_cfg_sysref_oneshot,
  output reg [7:0] up_cfg_lmfc_offset,
  output reg up_cfg_sysref_disable,

  input core_event_sysref_alignment_error,
  input core_event_sysref_edge
);

reg [1:0] up_sysref_status;
reg [1:0] up_sysref_status_clear;
wire [1:0] up_sysref_event;

sync_event #(
  .NUM_OF_EVENTS(2)
) i_cdc_sysref_event (
  .in_clk(core_clk),
  .in_event({
    core_event_sysref_alignment_error,
    core_event_sysref_edge
  }),
  .out_clk(up_clk),
  .out_event(up_sysref_event)
);

always @(posedge up_clk) begin
  if (up_reset == 1'b1) begin
    up_sysref_status <= 2'b00;
  end else begin
    up_sysref_status <= (up_sysref_status & ~up_sysref_status_clear) | up_sysref_event;
  end
end

always @(*) begin
  case (up_raddr)
  /* JESD SYSREF configuraton */
  12'h040: up_rdata = {
    /* 02-31 */ 30'h00, /* Reserved for future use */
    /*    01 */ up_cfg_sysref_oneshot,
    /*    00 */ up_cfg_sysref_disable
  };
  12'h041: up_rdata = {
    /* 10-31 */ 22'h00, /* Reserved for future use */
    /* 02-09 */ up_cfg_lmfc_offset,
    /* 00-01 */ 2'b00 /* data path alignment for cfg_lmfc_offset */
  };
  12'h042: up_rdata = {
    /* 02-31 */ 30'h00,
    /* 00-01 */ up_sysref_status
  };
  default: up_rdata = 32'h00000000;
  endcase
end

always @(posedge up_clk) begin
  if (up_reset == 1'b1) begin
    up_cfg_sysref_oneshot <= 1'b0;
    up_cfg_lmfc_offset <= 'h00;
    up_cfg_sysref_disable <= 1'b0;
  end else if (up_wreq == 1'b1 && up_cfg_is_writeable == 1'b1) begin
    case (up_waddr)
    /* JESD SYSREF configuraton */
    12'h040: begin
      up_cfg_sysref_oneshot <= up_wdata[1];
      up_cfg_sysref_disable <= up_wdata[0];
    end
    12'h041: begin
      /* Aligned to data path width */
      up_cfg_lmfc_offset <= up_wdata[9:DATA_PATH_WIDTH];
    end
    endcase
  end
end

always @(*) begin
  if (up_wreq == 1'b1 && up_waddr == 12'h042) begin
    up_sysref_status_clear = up_wdata[1:0];
  end else begin
    up_sysref_status_clear = 2'b00;
  end
end

endmodule
module jesd204_soft_pcs_rx #(
  parameter NUM_LANES = 1,
  parameter DATA_PATH_WIDTH = 4,
  parameter REGISTER_INPUTS = 0,
  parameter INVERT_INPUTS = 0
) (
   input clk,
   input reset,

   input patternalign_en,

   input [NUM_LANES*DATA_PATH_WIDTH*10-1:0] data,

   output reg [NUM_LANES*DATA_PATH_WIDTH*8-1:0] char,
   output reg [NUM_LANES*DATA_PATH_WIDTH-1:0] charisk,
   output reg [NUM_LANES*DATA_PATH_WIDTH-1:0] notintable,
   output reg [NUM_LANES*DATA_PATH_WIDTH-1:0] disperr
);

localparam LANE_DATA_WIDTH = DATA_PATH_WIDTH * 10;

wire [NUM_LANES*LANE_DATA_WIDTH-1:0] data_aligned;

wire [NUM_LANES*DATA_PATH_WIDTH*8-1:0] char_s;
wire [NUM_LANES*DATA_PATH_WIDTH-1:0] charisk_s;
wire [NUM_LANES*DATA_PATH_WIDTH-1:0] notintable_s;
wire [NUM_LANES*DATA_PATH_WIDTH-1:0] disperr_s;

reg [NUM_LANES-1:0] disparity = {NUM_LANES{1'b0}};
wire [DATA_PATH_WIDTH:0] disparity_chain[0:NUM_LANES-1];

wire [NUM_LANES*DATA_PATH_WIDTH*10-1:0] data_s;
wire patternalign_en_s;

always @(posedge clk) begin
  char <= char_s;
  charisk <= charisk_s;
  notintable <= notintable_s;
  disperr <= disperr_s;
end

generate
genvar lane;
genvar i;
if (REGISTER_INPUTS > 0) begin
  reg                                     patternalign_en_r;
  reg [NUM_LANES*DATA_PATH_WIDTH*10-1:0]  data_r;
  always @(posedge clk) begin
    patternalign_en_r <= patternalign_en;
    data_r  <= data;
  end
  assign patternalign_en_s = patternalign_en_r;
  assign data_s = data_r;
end else begin
  assign patternalign_en_s = patternalign_en;
  assign data_s = data;
end

for (lane = 0; lane < NUM_LANES; lane = lane + 1) begin: gen_lane

  jesd204_pattern_align #(
    .DATA_PATH_WIDTH(DATA_PATH_WIDTH)
  ) i_pattern_align (
    .clk(clk),
    .reset(reset),

    .patternalign_en(patternalign_en_s),
    .in_data(data_s[LANE_DATA_WIDTH*lane+:LANE_DATA_WIDTH]),
    .out_data(data_aligned[LANE_DATA_WIDTH*lane+:LANE_DATA_WIDTH])
  );

  assign disparity_chain[lane][0] = disparity[lane];

  always @(posedge clk) begin
    if (reset == 1'b1) begin
      disparity[lane] <= 1'b0;
    end else begin
      disparity[lane] <= disparity_chain[lane][DATA_PATH_WIDTH];
    end
  end

  for (i = 0; i < DATA_PATH_WIDTH; i = i + 1) begin: gen_dpw
    localparam j = DATA_PATH_WIDTH * lane + i;
    wire [9:0] in_char;
    if (REGISTER_INPUTS > 1) begin
      reg [9:0] in_char_r = 10'b0;
      always @(posedge clk) begin
        in_char_r <= INVERT_INPUTS ? ~data_aligned[j*10+:10] :
                                      data_aligned[j*10+:10];
      end
      assign in_char = in_char_r;
    end else begin
      assign in_char = INVERT_INPUTS ? ~data_aligned[j*10+:10] :
                                        data_aligned[j*10+:10];
    end

    jesd204_8b10b_decoder i_dec (
      .in_char(in_char),
      .out_char(char_s[j*8+:8]),
      .out_charisk(charisk_s[j]),
      .out_notintable(notintable_s[j]),
      .out_disperr(disperr_s[j]),

      .in_disparity(disparity_chain[lane][i]),
      .out_disparity(disparity_chain[lane][i+1])
    );
  end
end
endgenerate

endmodule
module jesd204_pattern_align #(
  parameter DATA_PATH_WIDTH = 4
) (
  input clk,
  input reset,

  input patternalign_en,

  input [DATA_PATH_WIDTH*10-1:0] in_data,
  output [DATA_PATH_WIDTH*10-1:0] out_data
);

localparam [9:0] PATTERN_P = 10'b1010000011;
localparam [9:0] PATTERN_N = 10'b0101111100;

reg [1:0] match_counter = 2'h0;
reg pattern_sync = 1'b0;
reg pattern_match = 1'b0;

reg [3:0] align = 4'h0;
reg [1:0] cooldown = 2'h3;

reg [8:0] data_d1 = 9'h00;
reg [DATA_PATH_WIDTH*10+2:0] aligned_data_stage1;
reg [DATA_PATH_WIDTH*10-1:0] aligned_data_stage2;
wire [(DATA_PATH_WIDTH+1)*10-2:0] full_data;

assign full_data = {in_data,data_d1};
assign out_data = aligned_data_stage2;

/* 2-stage cascade of 3:1 and 4:1 muxes */

integer i;

always @(*) begin
  for (i = 0; i < DATA_PATH_WIDTH*10+3; i = i + 1) begin
    if (i < DATA_PATH_WIDTH*10+1) begin
      case (align[3:2])
      2'b00: aligned_data_stage1[i] = full_data[i];
      2'b01: aligned_data_stage1[i] = full_data[i+4];
      2'b10: aligned_data_stage1[i] = full_data[i+8];
      default: aligned_data_stage1[i] = 1'b0;
      endcase
    end else begin
      case (align[2])
      1'b0: aligned_data_stage1[i] = full_data[i];
      default: aligned_data_stage1[i] = full_data[i+4];
      endcase
    end
  end

  aligned_data_stage2 = aligned_data_stage1[align[1:0]+:DATA_PATH_WIDTH*10];
end

always @(posedge clk) begin
  data_d1 <= in_data[DATA_PATH_WIDTH*10-9+:9];
end

always @(posedge clk) begin
  if (out_data[9:0] == PATTERN_P || out_data[9:0] == PATTERN_N) begin
    pattern_match <= 1'b1;
  end else begin
    pattern_match <= 1'b0;
  end
end

always @(posedge clk) begin
  if (reset == 1'b1) begin
    cooldown <= 2'h3;
    align <= 4'h0;
  end else begin
    if (cooldown != 2'h0) begin
      cooldown <= cooldown - 1'b1;
    end else if (patternalign_en == 1'b1 && pattern_sync == 1'b0 &&
                 pattern_match == 1'b0) begin
      cooldown <= 2'h3;
      if (align == 'h9) begin
        align <= 4'h0;
      end else begin
        align <= align + 1'b1;
      end
    end
  end
end

always @(posedge clk) begin
  if (reset == 1'b1) begin
    pattern_sync <= 1'b0;
    match_counter <= 2'h0;
  end else begin
    if (match_counter == 2'h0) begin
      pattern_sync <= 1'b0;
    end else if (match_counter == 2'h3) begin
      pattern_sync <= 1'b1;
    end

    if (pattern_match == 1'b1) begin
      if (match_counter != 2'h3) begin
        match_counter <= match_counter + 1'b1;
      end
    end else begin
      if (match_counter != 2'h0) begin
        match_counter <= match_counter - 1'b1;
      end
    end
  end
end

endmodule
module pipeline_stage #(
  parameter REGISTERED = 1,
  parameter WIDTH = 1
) (
  input clk,
  input [WIDTH-1:0] in,
  output [WIDTH-1:0] out
);

generate if (REGISTERED == 0) begin

  assign out = in;

end else begin

  (* shreg_extract = "no" *)  reg [REGISTERED*WIDTH-1:0] in_dly;

  always @(posedge clk) in_dly <= {in_dly,in};

  assign out = in_dly[REGISTERED*WIDTH-1 -: WIDTH];

end endgenerate

endmodule