Skip to content

Instantly share code, notes, and snippets.

@kulp
Last active June 26, 2021 10:45
Show Gist options
  • Save kulp/50a9f357c2375f7e599583bca489a5f0 to your computer and use it in GitHub Desktop.
Save kulp/50a9f357c2375f7e599583bca489a5f0 to your computer and use it in GitHub Desktop.
Generate TMDS codes (for DVI / HDMI)
#!/usr/bin/env perl
# Generate TMDS codes (for DVI / HDMI)
# Takes decimal numbers 0-255 on stdin and emits ASCII binary numbers on stdout.
# Emits 462 unique codes where the standard expects 460; the user should reject
# the two zero-transition codes it produces.
use strict;
use warnings;
# LSb first
sub unbitarray ($$) { unpack "S", pack "b$_[0]", join "", @_[0 .. $#_] }
sub bitarray ($$) { split //, unpack "b$_[0]", pack "S", $_[1] }
sub ones { scalar grep /1/, @_ }
sub gen9
{
my @D = @_;
my @q;
my $N1 = ones @D;
push @q, $D[0];
if ($N1 > 4 or ($N1 == 4 and $D[0] == 0)) {
push @q, 1-($q[-1] ^ int($D[$_])) for 1 .. 7;
push @q, 0; # XNOR is 0
} else {
push @q, ($q[-1] ^ int($D[$_])) for 1 .. 7;
push @q, 1; # XOR is 1
}
return @q;
}
sub gen10
{
# data enable, two bits of control (e.g., vsync, hsync), ones-count, q_m
my ($de, $cc, $count, $q) = @_;
if ($de) {
if ($$count == 0 or ones @$q == 4) {
# balanced
my $off = 8 - 2 * ones @$q;
if ($q->[8] == 0) {
$$count -= $off;
} else {
$$count += $off;
}
return ((map { $q->[8] ? $_ : 1-$_ } @$q[0 .. 7]), $q->[8], 1-$q->[8]);
} else {
# unbalanced
# TODO update $$count
my $flip = 0+($$count > 0 && (ones @$q) > 4 or
$$count < 0 && (ones @$q) < 4);
return ((map $flip^$_, @$q[0 .. 7]), $q->[8], $flip);
}
} else {
$$count = 0;
# TODO check bit-endianness of control words
# spec is unclear as to whether `q[0:9]` and `q[9:0]` are different
my @arr = (qw(0 0 1 0 1 0 1 0 1), 1-$cc->[1]);
return reverse ($cc->[0] ? map 1-$_, @arr : @arr);
}
}
for my $D (<>) {
chomp $D;
for my $count (-5, 5) {
my @q = gen10 1, [ 0, 0 ], \$count, [ gen9 bitarray 8, $D ];
print((join "", reverse @q), "\n");
}
}
#!/usr/bin/env perl
# Decode TMDS codes (for DVI / HDMI)
# Takes ASCII binary numbers on stdin and emits decimal numbers 0-255 on stdout.
use strict;
use warnings;
while (<>) {
chomp;
my $out = 0;
# MSB on left of string
my ($inv, $mode, @q) = split //;
@q = reverse @q; # put MSB at highest index
if ($inv) {
@q = map 1-$_, @q;
}
my $lsb = $out = $q[0];
for my $i (1 .. 7) {
my $bit = int($q[$i]) ^ int($q[$i-1]) ^ int($mode) ^ 1;
$out = $bit . $out;
}
print(unpack("C", pack "B8", $out), "\n");
}
#!/usr/bin/env bash
# Provides some functions for generating TMDS codes.
# Run it like this: `$0 count-codes` or `$0 dc-balanced-pairs`.
# Fail early and loudly.
set -euo pipefail
here=$(dirname $0)
unique-codes ()
{
seq 0 255 |
$here/tmds.pl |
sort --unique |
# I infer that ten zeros and ten ones are invalid codes, though I have not
# seen this explicitly stated anywhere yet.
grep -v -E -e '^0{10}' -e '^1{10}'
}
count-codes ()
{
unique-codes |
$here/untmds.pl |
wc -l
}
find-dc-balanced ()
{
grep -vE -e '(1.*){6}' -e '(0.*){6}'
}
dc-balanced-pairs ()
{
paste \
<(unique-codes | find-dc-balanced | ./untmds.pl) \
<(unique-codes | find-dc-balanced | tr 01 10 | ./untmds.pl) |
while read a b
do
echo -e "$a\t$b\t$(( a - b ))"
done |
sort -gk3
}
"$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment