`timescale 1ns / 1ps `default_nettype none // Project F: Display TMDS Encoder for DVI // (C)2019 Will Green, Open source hardware released under the MIT License // Learn more at https://projectf.io module tmds_encoder_dvi( input wire i_clk, // clock input wire i_rst, // reset (active high) input wire [7:0] i_data, // colour data input wire [1:0] i_ctrl, // control data input wire i_de, // display enable (active high) output reg [9:0] o_tmds // encoded TMDS data ); // select basic encoding based on the ones in the input data wire [3:0] d_ones = {3'b0,i_data[0]} + {3'b0,i_data[1]} + {3'b0,i_data[2]} + {3'b0,i_data[3]} + {3'b0,i_data[4]} + {3'b0,i_data[5]} + {3'b0,i_data[6]} + {3'b0,i_data[7]}; wire use_xnor = (d_ones > 4'd4) || ((d_ones == 4'd4) && (i_data[0] == 0)); // encode colour data with xor/xnor /* verilator lint_off UNOPTFLAT */ wire [8:0] enc_qm; assign enc_qm[0] = i_data[0]; assign enc_qm[1] = (use_xnor) ? (enc_qm[0] ~^ i_data[1]) : (enc_qm[0] ^ i_data[1]); assign enc_qm[2] = (use_xnor) ? (enc_qm[1] ~^ i_data[2]) : (enc_qm[1] ^ i_data[2]); assign enc_qm[3] = (use_xnor) ? (enc_qm[2] ~^ i_data[3]) : (enc_qm[2] ^ i_data[3]); assign enc_qm[4] = (use_xnor) ? (enc_qm[3] ~^ i_data[4]) : (enc_qm[3] ^ i_data[4]); assign enc_qm[5] = (use_xnor) ? (enc_qm[4] ~^ i_data[5]) : (enc_qm[4] ^ i_data[5]); assign enc_qm[6] = (use_xnor) ? (enc_qm[5] ~^ i_data[6]) : (enc_qm[5] ^ i_data[6]); assign enc_qm[7] = (use_xnor) ? (enc_qm[6] ~^ i_data[7]) : (enc_qm[6] ^ i_data[7]); assign enc_qm[8] = (use_xnor) ? 0 : 1; /* verilator lint_on UNOPTFLAT */ // disparity in encoded data for DC balancing: needs to cover -8 to +8 wire signed [4:0] ones = {4'b0,enc_qm[0]} + {4'b0,enc_qm[1]} + {4'b0,enc_qm[2]} + {4'b0,enc_qm[3]} + {4'b0,enc_qm[4]} + {4'b0,enc_qm[5]} + {4'b0,enc_qm[6]} + {4'b0,enc_qm[7]}; wire signed [4:0] zeros = 5'b01000 - ones; wire signed [4:0] balance = ones - zeros; // record ongoing DC bias reg signed [4:0] bias; always @ (posedge i_clk) begin if (i_rst) begin o_tmds <= 10'b1101010100; // equivalent to ctrl 2'b00 bias <= 5'sb00000; end else if (i_de == 0) // send control data in blanking interval begin case (i_ctrl) // ctrl sequences (always have 7 transitions) 2'b00: o_tmds <= 10'b1101010100; 2'b01: o_tmds <= 10'b0010101011; 2'b10: o_tmds <= 10'b0101010100; default: o_tmds <= 10'b1010101011; endcase bias <= 5'sb00000; end else // send pixel colour data (at most 5 transitions) begin if (bias == 0 || balance == 0) // no prior bias or disparity begin if (enc_qm[8] == 0) begin $display("\t%d %b %d, %d, A1", i_data, enc_qm, ones, bias); o_tmds[9:0] <= {2'b10, ~enc_qm[7:0]}; bias <= bias - balance; end else begin $display("\t%d %b %d, %d, A0", i_data, enc_qm, ones, bias); o_tmds[9:0] <= {2'b01, enc_qm[7:0]}; bias <= bias + balance; end end else if ((bias > 0 && balance > 0) || (bias < 0 && balance < 0)) begin $display("\t%d %b %d, %d, B1", i_data, enc_qm, ones, bias); o_tmds[9:0] <= {1'b1, enc_qm[8], ~enc_qm[7:0]}; bias <= bias + {3'b0, enc_qm[8], 1'b0} - balance; end else begin $display("\t%d %b %d, %d, B0", i_data, enc_qm, ones, bias); o_tmds[9:0] <= {1'b0, enc_qm[8], enc_qm[7:0]}; bias <= bias - {3'b0, ~enc_qm[8], 1'b0} + balance; end end end endmodule