When is a UART not a UART?

Tunneling debugging information over JTAG – 2020-03-04

One of my primary platforms for FPGA tinkering is the Turbo Chameleon 64 cartridge – which comes in two flavours: the original V1 hardware which features a Cyclone III FPGA and the V2 hardware which has a very similar Cyclone 10LP FPGA (basically the same thing in a newer package).

While this cartridge is intended as an expansion for the venerable Commodore 64 8-bit computer from the 1980s, it can nonetheless run other more general-purpose cores, so most of my projects have Chameleon64 targets. The one downside of this hardware is the lack of general purpose IOs. It has no built-in serial port, and nowhere really convenient to attach a USB-serial dongle either. It’s possible to misuse the IEC port for this purpose, but then I need to remember to disable it before distributing a finished core (I doubt a 1541 disk drive would appreciate having RS232 data spewed at it). There’s also a USB debugging protocol built into the cartridge, which I haven’t yet explored – mostly because so many of my projects can be built for multiple platforms, I’m reluctant to put a large amount of effort into supporting features only available on one of them.

I discovered the other day, however, that it’s possible to tunnel a UART-style connection over JTAG.

Since I always use the trusty USB Blaster (either external or built in the board) to upload designs while developing, I was keen to explore this.

Unfortunately, it appears that the low level interface in question is completely undocumented – it’s clear that it’s intended to be used only with the higher-level NIOS2 and QSYS.

Luckily the internet came to the rescue, and with the help of some example code I found in a forum, I now have a VHDL module which acts as a drop-in replacement for the simple_uart module I’ve been using so far. This will be in the RTL/Peripherals directory of my EightThirtyTwoDemos repo.

The undocumented interface itself looks like this:

component alt_jtag_atlantic is
generic (
INSTANCE_ID : integer;
LOG2_RXFIFO_DEPTH : integer;
LOG2_TXFIFO_DEPTH : integer;
SLD_AUTO_INSTANCE_INDEX : string
);
port (
clk : in std_logic;
rst_n : in std_logic;
r_dat : in std_logic_vector(7 downto 0); -- data from FPGA
r_val : in std_logic; -- data valid
r_ena : out std_logic; -- can accept data
t_dat : out std_logic_vector(7 downto 0); -- data to FPGA
t_dav : in std_logic; -- ready to receive more data
t_ena : out std_logic; -- tx data valid
t_pause : out std_logic -- ???
);
end component alt_jtag_atlantic

This component creates a virtual JTAG node which the nios2-terminal program (part of the Quartus distribution) can talk to. You can still use signaltap while it’s connected, but it’s necessary to break the connection in order to reprogram the FPGA – for obvious reasons.

I’ve plumbed in the alt_jtag_atlantic component based on these signal descriptions, but I’m not 100% sure they’re fully correct, because while it works correctly on every platform I’ve tested on so far (DE2, DE10Lite, Chameleon64, Chameleon64v2 and a cheap EBay Cyclone IV board) on all but the Chameleon64 v1 the link is significantly slower than I expected. On the Chameleon V1, however, it’s the same speed or faster than actual RS232 @ 115,200 baud. I’m not yet sure why. I believe there are further generics related to the FIFOs, but I don’t know what they’re called, so can’t yet experiment with them!

It’s also possible to write custom software to talk to the virtual JTAG node. Again, the interface for doing this (libjtag_atlantic) is undocumented, but again the internet comes to the rescue – in this case via a downloadable archive in a random forum thread which can be found here.

2 thoughts on “When is a UART not a UART?

Leave a Reply

Your email address will not be published.