HDMI Round 1

HDMI Round 1

by ferris

Continuing with my hardware endeavors, this week I worked on trying to get an HDMI signal out of my FPGA.

While generating a simple HDMI signal is pretty, well, simple, one of the things that's made it difficult for me is the fact that on my board, the FPGA doesn't have a direct connection to the pins in the HDMI header. Instead, there's a transmitter chip you must go through instead. While this makes most parts of actually transmitting a signal easier, a big problem is that the chip must be configured before it will actually transmit anything.

Now, configuration of the chip is really "just setting a few registers" and you're good to go. However, in this particular package, the registers can only be set over an i2c bus. So before I even get anywhere near generating a video signal, I have to wrap my head around this first. And yes, I am aware that there are some open-source i2c implementations lurking around the net, but what fun would that be? Naturally, I had to give this a go myself :)

So, the first step was setting up a simple project and playing with the timing. This wasn't too bad actually with the help of kaze, the build tool I started a couple weeks ago (and worked on a bit more here and there since). It was pretty time consuming, though, as I'm still pretty new to all of this stuff - so setting up the right state machines with the right inputs/outputs, getting the right timing, and not writing a mountain of code is a lot to manage. I think I'm getting the hang of it, though.

The first part was figuring out how to organize the various components I would be working with. I obviously needed an i2c implementation, but I also pulled in the UART transmitter I wrote a few weeks ago so that I could get some kind of logging out of the device while working with it. This is super important, as it can't really be underestimated how blind you are when doing this stuff on the actual hardware - especially when you don't own an oscilloscope!

The next milestone was when I finally got the chip to "ACK" when I was trying to write bytes over the i2c bus. This took a bit of work, as I had a lot of small timing issues at first, and was also sending the wrong number of bits, haha. The biggest part of this, though, was the fact that I was sending bytes to the wrong i2c slave address! This was pretty crappy actually; let me explain why:

  • i2c slave addresses are 7 bits long (or they can be 10 bits, but not in this particular case)
  • apparently it's a convention to write them as 8 bit values, where the UPPER 7 bits are the actual address, and the LSB is 0, which is very much backwards to how you'd normally write them; as just a simple 7 bit value. It does make sense, however, seeing as the address is transmitted over i2c MSB first down to LSB, and the 8th bit over the wire is a read/write bit. But I never would've guessed that myself. I ended up finding this information in a forum post where someone was also failing to address the same chip, and this article.

But yeah, at this point I could actually write/read bytes on the bus, which was a step in the right direction.

The next part is actually sending/receiving meaningful bytes on the bus. An i2c bus is really just a transport layer; the idea is you're supposed to implement your own protocol on top of it. So, that was the next step.

The actual protocol the chip uses is described in the Hardware User's Guide, section 6.6. The protocol required finer-grained control over the bus than I had originally implemented, so I had to spend a lot of time refactoring my code and exposing the right hw interface between my components. The nice part about this, though, was that the different i2c protocol parts were already solved, so this was mostly a matter of testing a lot in the simulator and just trying to match the spec.

Some hours later, I finally got a successful register read from the device. In this case, I was reading bit 6 of register 0x42, which on this device tells me if a receiver is hooked up to the hdmi port. And it works :D !! Of course, this was not without issues, haha. A lot of that time was spent not realizing I had forgotten to hook up some of my components to actually get at the bytes being read from the i2c bus, and making the simple mistake of comparing the entire register's value to a constant, instead of only the bits that were valid, as the rest are basically undefined. Once I got that working, though, I could plug/unplug the monitor and reset the hw and verify the register bits were correct. Success!!

So, not much visible to show yet other than some debugging output and timing diagrams from the simulator. But, this is huge for me - actually getting down and dirty interfacing other pieces of hardware is SO cool. I've written drivers before, esp. on oldschool platforms for demoscene stuff, but I had never done it in HW, haha. Fun stuff!! :D

As always, you can follow the project on my github.

Until next week :D

Last Edited on Mon Nov 23 2015 03:01:28 GMT-0500 (EST)