module jesd204_8b10b_encoder (
  input in_disparity,
  input [7:0] in_char,
  input in_charisk,

  output [9:0] out_char,
  output out_disparity
);

/*
 * Only supports the subset of 8b10b that is used by JESD204.
 * If non-supported control characters are supplied the output is undefined.
 */

reg [5:0] data6b;
reg [5:0] out6b;
reg may_invert6b;
reg disparity6b;
reg [3:0] data4b;
reg [3:0] out4b;
reg may_invert4b;
reg disparity4b;
wire disparity4b_in;
reg alt7;

always @(*) begin
  if (in_charisk == 1'b1) begin
    // Assume K28.x
    data6b = 6'b000011;
    may_invert6b = 1'b1;
    disparity6b = 1'b1;
  end else begin
    case (in_char[4:0])
    5'd00: begin
      data6b = 6'b000110;
      may_invert6b = 1'b1;
      disparity6b = 1'b1;
    end
    5'd01: begin
      data6b = 6'b010001;
      may_invert6b = 1'b1;
      disparity6b = 1'b1;
    end
    5'd02: begin
      data6b = 6'b010010;
      may_invert6b = 1'b1;
      disparity6b = 1'b1;
    end
    5'd03: begin
      data6b = 6'b100011;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd04: begin
      data6b = 6'b010100;
      may_invert6b = 1'b1;
      disparity6b = 1'b1;
    end
    5'd05: begin
      data6b = 6'b100101;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd06: begin
      data6b = 6'b100110;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd07: begin
      data6b = 6'b111000;
      may_invert6b = 1'b1;
      disparity6b = 1'b0;
    end
    5'd08: begin
      data6b = 6'b011000;
      may_invert6b = 1'b1;
      disparity6b = 1'b1;
    end
    5'd09: begin
      data6b = 6'b101001;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd10: begin
      data6b = 6'b101010;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd11: begin
      data6b = 6'b001011;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd12: begin
      data6b = 6'b101100;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd13: begin
      data6b = 6'b001101;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd14: begin
      data6b = 6'b001110;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd15: begin
      data6b = 6'b000101;
      may_invert6b = 1'b1;
      disparity6b = 1'b1;
    end
    5'd16: begin
      data6b = 6'b001001;
      may_invert6b = 1'b1;
      disparity6b = 1'b1;
    end
    5'd17: begin
      data6b = 6'b110001;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd18: begin
      data6b = 6'b110010;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd19: begin
      data6b = 6'b010011;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd20: begin
      data6b = 6'b110100;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd21: begin
      data6b = 6'b010101;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd22: begin
      data6b = 6'b010110;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd23: begin
      data6b = 6'b101000;
      may_invert6b = 1'b1;
      disparity6b = 1'b1;
    end
    5'd24: begin
      data6b = 6'b001100;
      may_invert6b = 1'b1;
      disparity6b = 1'b1;
    end
    5'd25: begin
      data6b = 6'b011001;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd26: begin
      data6b = 6'b011010;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd27: begin
      data6b = 6'b100100;
      may_invert6b = 1'b1;
      disparity6b = 1'b1;
    end
    5'd28: begin
      data6b = 6'b011100;
      may_invert6b = 1'b0;
      disparity6b = 1'b0;
    end
    5'd29: begin
      data6b = 6'b100010;
      may_invert6b = 1'b1;
      disparity6b = 1'b1;
    end
    5'd30: begin
      data6b = 6'b100001;
      may_invert6b = 1'b1;
      disparity6b = 1'b1;
    end
    default: begin
      data6b = 6'b001010;
      may_invert6b = 1'b1;
      disparity6b = 1'b1;
    end
    endcase
  end
end

always @(*) begin
  if (in_charisk == 1'b1) begin
    alt7 = 1'b1;
  end else begin
//    case (in_char[4:0])
//    5'd11, 5'd13, 5'd14: alt7 = in_disparity;
//    5'd17, 5'd18, 5'd20: alt7 = ~in_disparity;
//    default: alt7 = 1'b0;
//    endcase

      // Slightly better packing
      case ({may_invert6b,data6b[5:4]})
      3'b000: alt7 = in_disparity;
      3'b011: alt7 = ~in_disparity;
      default: alt7 = 1'b0;
      endcase
  end
end

always @(*) begin
  case (in_char[7:5])
  3'd0: begin
    data4b = 4'b0010;
    may_invert4b = 1'b1;
    disparity4b = 1'b1;
  end
  3'd1: begin
    data4b = 4'b1001;
    may_invert4b = in_charisk;
    disparity4b = 1'b0;
  end
  3'd2: begin
    data4b = 4'b1010;
    may_invert4b = in_charisk;
    disparity4b = 1'b0;
  end
  3'd3: begin
    data4b = 4'b1100;
    may_invert4b = 1'b1;
    disparity4b = 1'b0;
  end
  3'd4: begin
    data4b = 4'b0100;
    may_invert4b = 1'b1;
    disparity4b = 1'b1;
  end
  3'd5: begin
    data4b = 4'b0101;
    may_invert4b = in_charisk;
    disparity4b = 1'b0;
  end
  3'd6: begin
    data4b = 4'b0110;
    may_invert4b = in_charisk;
    disparity4b = 1'b0;
  end
  default: begin
    if (alt7 == 1'b1) begin
      data4b = 4'b0001;
    end else begin
      data4b = 4'b1000;
    end
    may_invert4b = 1'b1;
    disparity4b = 1'b1;
  end
  endcase
end

assign disparity4b_in = in_disparity ^ disparity6b;
assign out_disparity = disparity4b_in ^ disparity4b;

always @(*) begin
  if (in_disparity == 1'b0 && may_invert6b == 1'b1) begin
    out6b = ~data6b;
  end else begin
    out6b = data6b;
  end
  if (disparity4b_in == 1'b0 && may_invert4b == 1'b1) begin
    out4b = ~data4b;
  end else begin
    out4b = data4b;
  end
end

assign out_char = {out4b,out6b};

endmodule