TMDS Part 2

MIT Fall 2024

The questions below are due on Wednesday October 02, 2024; 11:59:00 PM.
 
You are not logged in.

Please Log In for full access to the web site.
Note that this link will take you to an external site (https://shimmer.mit.edu) to authenticate, and then you will be redirected back to this page.

The General Algorithm

In the previous page you wrote the first part of TMDS. A module which decides, given a byte of information, whether to encode it using an XOR or XNOR sliding scheme. This minimized transitions in any given byte of data at the expense of one additional bit...the output was 9 bits (for an 8 bit input).

In this section, you'll perform the second part of TMDS encoding. You will convert the 9 bit output from stage 1 into a 10 bit output with the goal of keeping the long-term running ratio of 1’s to 0’s to be as close as possible to 50/50 in order to minimize any building up a DC voltage offset on the transmission line. The idea behind this is pretty simple:

If you decide you need to invert the bits, you invert the lower 8 bits, keep bit 9 as-is, and add a 1 as the tenth bit. If you decide you do not need to invert the bits, you keep the lower 9 bits the same and instead make bit 10 a 0.

On the receiver side then, all that would need to be done would be to look at that top-most bit and determine whether or not you need to invert the lower 8 before moving on.

Referring back to the HDMI specification, the decision about whether to invert or not is encompassed in the latter (more complicated) part of the encoding algorithm:

tmds_module

Part 2 of the TMDS Algorithm

This second part of TMDS is inherently stateful, meaning its output on a given timestep will be based on what its previous inputs and outputs are (the running tally) referred to in the algorithmic flowchart above as the variable cnt(t-1) and cnt(t). When this number is positive it means there has been an excess of 1's in recent history. If it is less than 0, it means there has been an excess of 0's recently.

But what is that algorithm actually doing intuitively? Well a running tally must be kept of how many 1's or 0's have been sent. Conventionally a number that can be both positive and negative is used here where a postive value means a net excess of 1's have been sent and a negative value means a net excess of 0's have been sent. Ideally you keep this tally as close to 0 as possible and you are always striving to "make things better" and trend to zero.

  • If the tally is 4, you should choose to invert/not invert your bits so that you put more 0's on the line.
  • If the tally is -5, ou should choose to invert/not invert your bits so that you put more 1's on the line.

Because a worst case situation would be: you start with a tally of 0, and you either put ten 1's on the line or ten 0's on the line. You will need a variable that can represent the integers from -10 to 10. This can be accomplished by a 5-bit number that we treat as a two's complement signed number.

Let's do an example:

  1. Let's say the first part of the TMDS has already been calculated and the output is 9'b1_0000_0101. Let's also say that going into this, the running tally is -2, currently represented in binary as 5'b11110.
  2. According to the algorithm, you neither have a running tally of 0 nor do you have an equal number of 1's or 0's in your lower 8 bits of the previous output. So you proceed down the "False" path of the first decision
  3. At the next decision, you determine that you do indeed have a tally less than 0 and more 0's than 1's. As a result, you don't want to make the situation with 0's even worse, so you're going to invert the bits. You follow the "True" path as a result.
  4. Following this path, you set your top bit to be 1, keep your XOR/XNOR bit untouched q_m[8], and invert the lower 8 bits to make your message which will therefore be: 10'b11_1111_1010.
  5. Before finishing, however you update your tally by adding 2 and the net number of ones in the new message (6) to it. You do this because your 10 bit message consisted of eight 1's and two 0's. So take your tally 5'b11110 and add 5'b00110 to it. The result is 5'b00100 which is +4 what would be expected (started with a tally of -2...added 6...now we're at +4).

You might be thinking that we really need our tally variable to be "signed", but one of the reasons two's complement is so nice is that it is built into the modular nature of binary numbers as is. Signed numbers in Verilog can sometimes be more trouble than they're worth (we'll get to them eventually don't worry). For this problem we're going to recommend you avoid doing that. For your tally variable, just make a five bit logic as is:

logic [4:0] tally;

Then when it comes time to check if it is positive or negative, all you need to do is actually check the value of the msb. If it is 1, that means it is negative. If it is 0, that means it is positive. Then everything else you do can actually just be regular (adding and subtracting all "just work").

Another Thing:

In addition to handling the algorithm as specified above, the full encoder also needs to consider several other signals:

  • rst_in: If in reset, the system should reset its tally to 0 and should set the output to be all 0's.
  • ve_in: Video Enable In. When this is 1, the system works as described and shown in the diagram above. When it is 0, however, this should set your running tally to be 0. In addition, if ve_in is 0, the output of the module should take on one of four values based on what control_in is:
    • 2'b00: 10'b1101010100
    • 2'b01: 10'b0010101011
    • 2'b10: 10'b0101010100
    • 2'b11: 10'b1010101011

These four control signals are used to convey blanking and sync period information and in practice only the TMDS encoder in charge of the blue channel will be fed anything other than 2'b00.

A starting skeleton of tmds_encoder is provided below!

`timescale 1ns / 1ps
`default_nettype none // prevents system from inferring an undeclared logic (good practice)

module tmds_encoder(
  input wire clk_in,
  input wire rst_in,
  input wire [7:0] data_in,  // video data (red, green or blue)
  input wire [1:0] control_in, //for blue set to {vs,hs}, else will be 0
  input wire ve_in,  // video data enable, to choose between control or video signal
  output logic [9:0] tmds_out
);

  logic [8:0] q_m;

  tm_choice mtm(
    .data_in(data_in),
    .qm_out(q_m));

  //your code here.

endmodule

`default_nettype wire

As far as timing goes: you cannot exceed one clock cycle of latency for the full calculation. This is part of the reason why we wanted the tm_choice module to be combinational only. At the same time, you cannot hope to do this logic purely combinationally since there is state involved, so design your system to perform its calculations in one clock cycle.

To help in debugging, we've provided a skeleton of a testbench as well as a reference waveform of a properly working tmds_encoder output with the proper outputs to a sequence of inputs. Look at the input waves to see the input values, and look 1 clock cycle later at the output wave to see the proper outputs for those values.

Like with some of our other modules, the checker below is only going to be so thorough. I did write this one special so it does a cycle-to-cycle comparison of your system's output to a verified version and will report back to you details of your failure at the first spot of failure, but you will need to rely on your testbenching and waveforms to get your system working perfectly.

This module is very picky so the test cases are very picky. Even being off by one single cycle may result in this module not working, so the test bench reflects that.

module tmds_encoder
Submit your tested TMDS encoder module here, including all stateful differential logic and control outputs. We'll run it online with a working version of tm_choice.
Code Skeleton
  No file selected

TMDS is working now (hopefully). Time to put it on the board! Checkoff 01.