// (C) 2001-2025 Altera Corporation. All rights reserved.
// Your use of Altera Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Altera Program License Subscription 
// Agreement, Altera IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Altera and sold by 
// Altera or its authorized distributors.  Please refer to the applicable 
// agreement for further details.


`default_nettype none
module intel_vvp_cvo_align
#(
  parameter DEVICE_FAMILY                           = "Arria 10", // -- Cyclone 10 GX -- Arria 10 -- Stratix 10 -- Agilex
  parameter C_RAM_BLOCK_TYPE                        = "M20K",
  parameter C_FIFO_DEPTH                            = 256,
  parameter C_INPUT_IS_ASYNC                        = 1,

  parameter C_PIXELS_IN_PARALLEL                    = 1, // at this stage, we assume the core input and output have the same number of PIP. As an extension, we could compose a core with a PIP converter.
  parameter C_NUMBER_OF_COLOR_PLANES                = 3,
  parameter C_BPS                                   = 8

)
(

//////////////////////////////////////////////////////
// video input bus
// axi4-s vvp lite/full
vid_clk,
vid_rst,
axi4s_vid_in_tuser,
axi4s_vid_in_tdata,
axi4s_vid_in_tvalid,
axi4s_vid_in_tready,
axi4s_vid_in_tlast,

//////////////////////////////////////////////////////
// video output bus
// axi4-s vvp lite/full
axi4s_vid_aligned_tdata,

//////////////////////////////////////////////////////
// fr timing-only input bus
// axi4-s vvp fr
fr_clk,
fr_rst,
axi4s_timing_in_tuser,
axi4s_timing_in_tdata,
axi4s_timing_in_tvalid,
axi4s_timing_in_tready,
axi4s_timing_in_tlast,
timing_phase,

//////////////////////////////////////////////////////
// Status signals
//
r_input_locked,
input_is_full,
input_stall_error,
input_size_mismatch,

locked_count,
stall_count,
size_err_count


);

import intel_vvp_common_pkg::*;

// calculate localparams needed for the interface.
localparam C_AXIS_VID_IN_PIXEL_BITS   = C_NUMBER_OF_COLOR_PLANES*C_BPS;
localparam C_AXIS_VID_IN_PIXEL_BYTES  = (C_NUMBER_OF_COLOR_PLANES*C_BPS+7)/8;
localparam C_AXIS_VID_IN_WIDTH        = C_AXIS_VID_IN_PIXEL_BYTES*C_PIXELS_IN_PARALLEL*8;
localparam C_AXIS_VID_IN_TUSER_WIDTH  = vvp_max(VVP_USER_KEEP_BITS, C_AXIS_VID_IN_PIXEL_BYTES*C_PIXELS_IN_PARALLEL);

localparam C_VVP_PIXEL_BYTES          = vvp_max(VVP_USER_KEEP_BITS, C_AXIS_VID_IN_PIXEL_BYTES);
localparam C_VVP_TUSER_WIDTH          = C_VVP_PIXEL_BYTES*C_PIXELS_IN_PARALLEL;
localparam C_VVP_TDATA_WIDTH          = C_VVP_TUSER_WIDTH*8;

localparam C_AXIS_FR_PIXEL_BYTES      = ((C_NUMBER_OF_COLOR_PLANES+1)*C_BPS+7)/8;
localparam C_AXIS_FR_WIDTH            = C_AXIS_FR_PIXEL_BYTES*C_PIXELS_IN_PARALLEL*8;
localparam C_AXIS_FR_TUSER_WIDTH      = C_AXIS_FR_PIXEL_BYTES*C_PIXELS_IN_PARALLEL;
// internal localparams
localparam C_AXIS_FR_BITS   = (C_NUMBER_OF_COLOR_PLANES + 1) * C_BPS;

localparam C_BIT_FIELD          = C_AXIS_FR_BITS - 1;
localparam C_BIT_V              = C_AXIS_FR_BITS - 2;
localparam C_BIT_H              = C_AXIS_FR_BITS - 3;
localparam C_BIT_DE             = C_AXIS_FR_BITS - 4;
localparam C_BIT_SYNC_MODE      = C_AXIS_FR_BITS - 5;
localparam C_AXIS_FR_TOP_BIT    = C_AXIS_FR_PIXEL_BYTES*(C_PIXELS_IN_PARALLEL-1)*8+C_AXIS_FR_BITS;
localparam C_BIT_TOP_FIELD      = C_AXIS_FR_TOP_BIT - 1;
localparam C_BIT_TOP_V          = C_AXIS_FR_TOP_BIT - 2;
localparam C_BIT_TOP_H          = C_AXIS_FR_TOP_BIT - 3;
localparam C_BIT_TOP_DE         = C_AXIS_FR_TOP_BIT - 4;
localparam C_BIT_TOP_SYNC_MODE  = C_AXIS_FR_TOP_BIT - 5;


//////////////////////////////////////////////////////
// main video input bus
// axi4-s vvp lite/full
input  logic                                vid_clk;
input  logic                                vid_rst;
input  logic [C_VVP_TUSER_WIDTH-1 : 0]      axi4s_vid_in_tuser;
input  logic [C_VVP_TDATA_WIDTH-1 : 0]      axi4s_vid_in_tdata;
input  logic                                axi4s_vid_in_tvalid;
output logic                                axi4s_vid_in_tready;
input  logic                                axi4s_vid_in_tlast;

output logic [C_AXIS_VID_IN_WIDTH-1 : 0]    axi4s_vid_aligned_tdata;

//////////////////////////////////////////////////////
// fr timing-only input bus
// axi4-s vvp fr
input  logic                                fr_clk;
input  logic                                fr_rst;
input  logic [C_AXIS_FR_TUSER_WIDTH-1:0]    axi4s_timing_in_tuser;
input  logic [C_AXIS_FR_WIDTH-1 : 0]        axi4s_timing_in_tdata;
input  logic                                axi4s_timing_in_tvalid;
input  logic                                axi4s_timing_in_tready;
input  logic                                axi4s_timing_in_tlast;
input  logic [3:0]                          timing_phase;

//////////////////////////////////////////////////////
// Status signals
//
output logic                                r_input_locked;
output logic                                input_is_full;
output logic                                input_stall_error;
output logic                                input_size_mismatch;

output logic [31:0]                         locked_count;
output logic [31:0]                         stall_count;
output logic [31:0]                         size_err_count;

///////////////////////////////////////////////////////
// Internal un-aligned pixel bus
//
logic [C_AXIS_VID_IN_TUSER_WIDTH-1:0]axi4s_vid_tuser;
logic [C_AXIS_VID_IN_WIDTH-1 : 0]    axi4s_vid_tdata;
logic                                axi4s_vid_tvalid;
logic                                axi4s_vid_tready;
logic                                axi4s_vid_tlast;


///////////////////////////////////////////////////////
// Internal signals
//
logic input_locked;
logic dump_ctrl_pkt;
logic start_of_pixels;
logic start_of_full_cc;
logic [C_PIXELS_IN_PARALLEL-1:0] axi4s_timing_de;
logic [C_PIXELS_IN_PARALLEL-1:0] r1_axi4s_timing_de;
logic [C_PIXELS_IN_PARALLEL-1:0] r2_axi4s_timing_de;
logic                            axi4s_timing_any_de;
logic                            r1_axi4s_timing_any_de;
logic                            r2_axi4s_timing_any_de;

logic                            do_locked_count;
logic                            do_stall_count;
logic                            do_size_err_count;
logic                            r_do_locked_count;
logic                            r_do_stall_count;
logic                            r_do_size_err_count;
logic                            input_locking;

//////////////////////////////////////////////////////
// Strip padding from lite/full bus that gets added when BPS=8 and NUMBER_OF_COLOR_PLANES=1
logic [C_AXIS_VID_IN_WIDTH-1 : 0]     axi4s_vid_stripped_tdata;
logic [C_AXIS_VID_IN_TUSER_WIDTH-1:0] axi4s_vid_stripped_tuser;

generate
  if (C_AXIS_VID_IN_PIXEL_BYTES < C_VVP_PIXEL_BYTES)
  begin:g_strip_padding
    always_comb
    begin
      integer i;
      for (i=0;i<C_PIXELS_IN_PARALLEL;i=i+1) begin
        axi4s_vid_stripped_tdata[C_AXIS_VID_IN_PIXEL_BITS*i+:C_AXIS_VID_IN_PIXEL_BITS] = axi4s_vid_in_tdata[8*C_VVP_PIXEL_BYTES*i+:C_AXIS_VID_IN_PIXEL_BITS];
      end
      axi4s_vid_stripped_tuser = axi4s_vid_in_tuser[C_AXIS_VID_IN_TUSER_WIDTH-1:0];
    end
  end
  else begin
    assign axi4s_vid_stripped_tdata = axi4s_vid_in_tdata;
    assign axi4s_vid_stripped_tuser = axi4s_vid_in_tuser;
  end
endgenerate


always_comb
begin
  integer i;
  for (i=0;i<C_PIXELS_IN_PARALLEL;i=i+1)
    axi4s_timing_de[i] = axi4s_timing_in_tdata[C_AXIS_FR_PIXEL_BYTES*i*8+C_BIT_DE];
end
assign axi4s_timing_any_de = |axi4s_timing_de;
assign start_of_pixels  = (axi4s_vid_tuser[1:0] == 2'b01) ? 1'b1 : 1'b0;
assign start_of_full_cc = (axi4s_vid_tuser[1:0] == 2'b10) ? 1'b1 : 1'b0;
//////////////////////////////////////////////////
// decouple vid input
//

generate
  if (C_FIFO_DEPTH > 0)
  begin:g_cdc_vid_fifo

    // instance CDC fifo
    intel_vvp_axi_streaming_fifo
    #(
      .P_ASYNC              ( C_INPUT_IS_ASYNC    ),
      .P_FAMILY             ( DEVICE_FAMILY       ),
      .P_AXIS_TDATA_WIDTH   ( C_AXIS_VID_IN_WIDTH ),
      .P_DEPTH              ( C_FIFO_DEPTH        ),
      .P_INPUT_REG          ( 1                   ),
      .P_OUTPUT_REG         ( 1                   )
    ) i_cdc_fifo
    (
      .wr_clk             ( vid_clk                                    ),
      .wr_rst             ( vid_rst                                    ),
      .axi4s_in_tuser     ( axi4s_vid_stripped_tuser                   ),
      .axi4s_in_tdata     ( axi4s_vid_stripped_tdata                   ),
      .axi4s_in_tvalid    ( axi4s_vid_in_tvalid                        ),
      .axi4s_in_tready    ( axi4s_vid_in_tready                        ),
      .axi4s_in_tlast     ( axi4s_vid_in_tlast                         ),

      .rd_clk             ( fr_clk                                  ),
      .rd_rst             ( fr_rst                                  ),
      .axi4s_out_tuser    ( axi4s_vid_tuser                         ),
      .axi4s_out_tdata    ( axi4s_vid_tdata                         ),
      .axi4s_out_tvalid   ( axi4s_vid_tvalid                        ),
      .axi4s_out_tready   ( axi4s_vid_tready                        ),
      .axi4s_out_tlast    ( axi4s_vid_tlast                         )
    );

 
  end
  else
  begin:g_vid_input_regs
    logic [C_AXIS_VID_IN_TUSER_WIDTH-1:0] axi4s_vid_shim_tuser;

    // instance register stage
    intel_vvp_axi_pipeline_stage #(
      .DATA_WIDTH           ( C_AXIS_VID_IN_WIDTH      )
    ) i_inp_shim (  
      .clk                  ( fr_clk     ),
      .rst                  ( fr_rst     ),

      .axi_st_din_tuser     ( axi4s_vid_stripped_tuser ),
      .axi_st_din_tdata     ( axi4s_vid_stripped_tdata ),
      .axi_st_din_tvalid    ( axi4s_vid_in_tvalid      ),
      .axi_st_din_tready    ( axi4s_vid_in_tready      ),
      .axi_st_din_tlast     ( axi4s_vid_in_tlast       ),
                                                      
      .axi_st_dout_tuser    ( axi4s_vid_shim_tuser     ),
      .axi_st_dout_tdata    ( axi4s_vid_tdata          ),
      .axi_st_dout_tvalid   ( axi4s_vid_tvalid         ),
      .axi_st_dout_tready   ( axi4s_vid_tready         ),
      .axi_st_dout_tlast    ( axi4s_vid_tlast          )
    );

    // only need to pass VVP_USER_KEEP_BITS of tuser
    always_comb
    begin
      axi4s_vid_tuser = {C_AXIS_VID_IN_TUSER_WIDTH{1'b0}};
      axi4s_vid_tuser[VVP_USER_KEEP_BITS-1:0] = axi4s_vid_shim_tuser[VVP_USER_KEEP_BITS-1:0];
    end
  end
endgenerate


//////////////////////////////////////////////////////
// register the timing fr data so we can find an edge!
//
logic [C_AXIS_FR_WIDTH-1 : 0]        r_axi4s_timing_in_tdata;
always_ff @(posedge fr_clk)
  r_axi4s_timing_in_tdata  <= axi4s_timing_in_tdata;
  
logic r1_axi4s_vid_tlast;
logic r2_axi4s_vid_tlast;
always_ff @(posedge fr_clk)
begin
  if (fr_rst)
  begin
    r1_axi4s_vid_tlast <= 1'b0;
    r2_axi4s_vid_tlast <= 1'b0;
    
    r1_axi4s_timing_de <= 1'b0;
    r2_axi4s_timing_de <= 1'b0;
    
    r1_axi4s_timing_any_de <= 1'b0;
    r2_axi4s_timing_any_de <= 1'b0;
  
  end
  else
  begin
    if (axi4s_timing_in_tready && axi4s_timing_in_tvalid)
    begin
      r1_axi4s_vid_tlast <= axi4s_vid_tlast & axi4s_vid_tvalid;
      r2_axi4s_vid_tlast <= r1_axi4s_vid_tlast;

      r1_axi4s_timing_de <= axi4s_timing_de;
      r2_axi4s_timing_de <= r1_axi4s_timing_de;

      r1_axi4s_timing_any_de <= axi4s_timing_any_de;
      r2_axi4s_timing_any_de <= r1_axi4s_timing_any_de;
    end
  end
end
//////////////////////////////////////////////////////
// Find the First Active Pixel
// The tUser for the fr data is at pixel (1,1), which 
// is not the first active pixel!
//
logic axi4s_timing_in_had_vtiming;
always_ff @(posedge fr_clk)
  if (axi4s_timing_in_tuser[0])
    axi4s_timing_in_had_vtiming <= 1'b1;
  else if (axi4s_timing_in_tdata[C_BIT_DE]) 
    axi4s_timing_in_had_vtiming <= 1'b0;


logic axi4s_timing_fap;
assign axi4s_timing_fap = axi4s_timing_in_had_vtiming & axi4s_timing_in_tdata[C_BIT_TOP_DE] & !axi4s_timing_in_tdata[C_BIT_V] & !r_axi4s_timing_in_tdata[C_BIT_TOP_DE]; // First Active Pixel

//////////////////////////////////////////////////////
// Drive the video tReady signal
//
assign axi4s_vid_tready = (!input_locked && !(start_of_pixels && axi4s_vid_tvalid)) ||
                          (start_of_pixels && axi4s_vid_tvalid && axi4s_timing_fap) ||
                          (input_locked && axi4s_timing_in_tready && axi4s_timing_in_tdata[C_BIT_DE] && !r1_axi4s_vid_tlast && !r2_axi4s_vid_tlast) ||
                          (input_locked && axi4s_timing_in_tready && axi4s_timing_in_tdata[C_BIT_TOP_DE]) ||
                          (input_locked && (start_of_full_cc && axi4s_vid_tvalid)) ||
                          (dump_ctrl_pkt && !(start_of_pixels && axi4s_vid_tvalid));

//////////////////////////////////////////////////////
// Drive the locked and error signals
//
always_ff @(posedge fr_clk)
begin:a_make_locked
  if (fr_rst)
  begin
    r_input_locked      <= 1'b0;
    input_stall_error   <= 1'b0;
    input_size_mismatch <= 1'b0;
    input_is_full       <= 1'b0;
    dump_ctrl_pkt       <= 1'b0;
    do_locked_count     <= 1'b0;
    do_stall_count      <= 1'b0;
    do_size_err_count   <= 1'b0;
    
  end
  else
  begin
    do_locked_count     <= 1'b0;
    do_stall_count      <= 1'b0;
    do_size_err_count   <= 1'b0;

    if (input_locked && axi4s_vid_tvalid && start_of_full_cc && axi4s_vid_tready) // must be start of a "Full" control packet
    begin
      input_is_full     <= 1'b1;
      dump_ctrl_pkt     <= 1'b1;
    end
    else if (input_locked && axi4s_vid_tvalid && start_of_pixels && axi4s_vid_tready) // start of pixel data. Could be "lite" or "full"
    begin
      input_is_full     <= dump_ctrl_pkt; // if we weren't dumping a ctrl packet, then this can't be a vvp full stream!
      dump_ctrl_pkt     <= 1'b0;
    end
      
    if (axi4s_vid_tready && axi4s_timing_in_tready && input_locking)
    begin
      r_input_locked      <= 1'b1;
      input_stall_error   <= 1'b0;
      input_size_mismatch <= 1'b0;
      do_locked_count     <= 1'b1;
    end
    else if (axi4s_timing_in_tready && axi4s_timing_any_de && !(axi4s_vid_tvalid || r1_axi4s_vid_tlast) )
    begin
      r_input_locked      <= 1'b0;
      input_stall_error   <= 1'b1;
      input_size_mismatch <= 1'b0;
      do_stall_count      <= 1'b1;
    end
    else if (input_locked && ((r1_axi4s_timing_de[0] & ~axi4s_timing_de[0]) && !(r1_axi4s_vid_tlast || r2_axi4s_vid_tlast )))
    begin
      r_input_locked      <= 1'b0;
      input_stall_error   <= 1'b0;
      input_size_mismatch <= 1'b1;
      do_size_err_count   <= 1'b1;
    end
    else if (input_locked && r2_axi4s_timing_de[0] && r1_axi4s_timing_de[0] && axi4s_timing_de[0] && r2_axi4s_vid_tlast )
    begin
      r_input_locked      <= 1'b0;
      input_stall_error   <= 1'b0;
      input_size_mismatch <= 1'b1;
      do_size_err_count   <= 1'b1;
    end
    else if (input_locked && axi4s_timing_in_tready && axi4s_timing_in_tvalid && axi4s_vid_tready && axi4s_vid_tvalid && (axi4s_timing_fap != start_of_pixels))
    begin
      r_input_locked      <= 1'b0;
      input_stall_error   <= 1'b0;
      input_size_mismatch <= 1'b1;
      do_size_err_count   <= 1'b1;
    end
  end
end

assign input_locking = (axi4s_vid_tvalid && start_of_pixels && axi4s_timing_in_tvalid && axi4s_timing_fap);
assign input_locked = r_input_locked || input_locking;


//////////////////////////////////////////////////////
// Count errors
//
always_ff @(posedge fr_clk)
begin:a_count_errs
  if (fr_rst)
  begin
    locked_count        <= 32'b0;
    stall_count         <= 32'b0;
    size_err_count      <= 32'b0;
    r_do_locked_count   <= 1'b0;
    r_do_stall_count    <= 1'b0;
    r_do_size_err_count <= 1'b0;
  end
  else
  begin
    r_do_locked_count   <= do_locked_count  ; 
    r_do_stall_count    <= do_stall_count   ; 
    r_do_size_err_count <= do_size_err_count; 
  
    if (do_locked_count && !r_do_locked_count)
    begin
      locked_count <= locked_count + 1'b1;
    end
    
    if (do_stall_count && !r_do_stall_count)
    begin
      stall_count <= stall_count + 1'b1;
    end
    
    if (do_size_err_count && !r_do_size_err_count)
    begin
      size_err_count  <= size_err_count + 1'b1;
    end
    
    
  end
end
 
//////////////////////////////////////////////////////
// Barrel shift the output
//


if (C_PIXELS_IN_PARALLEL > 1)
begin:barrel_shift
  localparam C_AXIS_BARREL_WIDTH  = C_AXIS_VID_IN_PIXEL_BYTES*((C_PIXELS_IN_PARALLEL*2)-1)*8;
  localparam C_AXIS_BARREL_BOT    = C_AXIS_BARREL_WIDTH - C_AXIS_VID_IN_WIDTH;
  localparam C_AXIS_PIXEL_WIDTH   = C_AXIS_VID_IN_PIXEL_BYTES * 8;
  logic [C_AXIS_BARREL_WIDTH-1:0]tdata_barrel_stage1;
  logic [C_AXIS_BARREL_WIDTH-1:0]tdata_barrel_stage2;
  logic [C_AXIS_BARREL_WIDTH-1:0]tdata_barrel_stage3;
  logic [C_AXIS_BARREL_WIDTH-1:0]tdata_barrel_stage4;
  logic [C_AXIS_BARREL_WIDTH-1:0]tdata_barrel_stage5;
  logic [3:0]                    timing_phase_stage1;
  logic [3:0]                    timing_phase_stage2;
  logic [3:0]                    timing_phase_stage3;

  always_ff @(posedge fr_clk)
  begin:a_do_barrel_shift

    tdata_barrel_stage1   <= {axi4s_vid_tdata , tdata_barrel_stage1[C_AXIS_BARREL_WIDTH-1 : C_AXIS_VID_IN_WIDTH]};
    tdata_barrel_stage2   <= tdata_barrel_stage1;
    tdata_barrel_stage3   <= tdata_barrel_stage2;
    tdata_barrel_stage4   <= tdata_barrel_stage3;
    tdata_barrel_stage5   <= tdata_barrel_stage4;
    timing_phase_stage1   <= timing_phase;
    timing_phase_stage2   <= timing_phase_stage1;
    timing_phase_stage3   <= timing_phase_stage2;

    if (timing_phase[2])
      tdata_barrel_stage2 <= tdata_barrel_stage1 << (4*C_AXIS_PIXEL_WIDTH);
    if (timing_phase_stage1[1])
      tdata_barrel_stage3 <= tdata_barrel_stage2 << (2*C_AXIS_PIXEL_WIDTH);
    if (timing_phase_stage2[0])
      tdata_barrel_stage4 <= tdata_barrel_stage3 << (1*C_AXIS_PIXEL_WIDTH);
      
  end

  assign axi4s_vid_aligned_tdata = tdata_barrel_stage5[C_AXIS_BARREL_WIDTH-1 : C_AXIS_BARREL_BOT];
end
else
  always_ff @(posedge fr_clk)
  begin:a_clock_data_out
    axi4s_vid_aligned_tdata <= axi4s_vid_tdata;
  end
  
  
endmodule
`default_nettype wire
`ifdef QUESTA_INTEL_OEM
`pragma questa_oem_00 "6vv+NLMwKGCERPrXQet892N4C1uCcErmxLgPyMYhdyaOVp6v3RudhNcA/7XKciBkOPUl2PgJ7/ZUbVxqHQKKzrSdlNXKta3IsnCAq/J9MkOatE4scrQhqz7jz78naosjfxAIOYUnLEA1oR4TKsq17eekHtg6YxLDUNA4tyS3nu/F6+GYYnJsFdi8wNo4AExJNjRD7CqV+JQu8Wz0b9258XsQQ2oy9clPj5jgEPEBfOwjZEv4Yl3yws8gUQqfe3JgCXfb+jIX+y6qzmVl/Z8BDgmgysv2dwh7ARuZ9ut5TEhjJysl22x+pQGQRN8qXAtgwWks5fbTUw7u9r8Ka+ZEOXkl3vpuHFWdxIxjJxyvrdu5K7l4rbXaAICpaphspHwf94V3fkcZ8SJ8cmd3XK8PRcBHKTQGG5Q9kJ1RuK+72x9ghSKYkyFBn1+O5Zp2BUuiLlYVnjdL6xNTfX7q0zwZnAY4eMg5GVEJtp/XjMIa0vZr2dvvSgC2oKS9fB0xJKA4ri1gHM3RPgwa4ftQ6GrP5jnrRxQ8xCOCM6si0LrPyxsAa6FEEf1Zo9mHaPZ8MehrljJUD6At3pFiydIxiGX1c/OVH05p4wDVTqvq+Zst8qbv/LQxrp7Z/BlkrbXPfrx68mNmGB/I6d3icv65QDvKH/QJ28NaJqvhi0fwwFVbI4pJ8QMWUGk6arS5Pvy7rfZbJBlBtf6C5GKlkkuk064JiCJWN3VXmWZ/fYZL+txW2748xobX5Lbxoz2oGuZE579+PB9FSOlBMCG+3gMIXiHLb9LHAY264vyyXHCf7YyNwEtl4YQWogx5yi5uasvAg7cbxYp8i9LVIC1P1YKYw8WtIOMqZI84UXV7MV0qxWcwHTFh9/LBQw3ywsUL4QIR0r9Xjz0dU34upjr0QToFN3zdE2LDBu3v01htJJTkBj/0Hnq1eyuQ/fMr5LWpU1EHM7AqNY/SdLtoza/J3WhcijqHVzGZV7j0ETyqQD4OS3Zz0KLj6v7LwWuurW+kVFeSfQfI"
`endif