IBM 1410 FPGA: From USB Serial to UDP over Ethernet

Seeing that some of the problems that I had identified with the Tape Adapter Unit (TAU) implementation could be or might be traceable back to the fact that I was using a 115,200 bps serial over USB to communicate between the 1410 FPGA implementation and the PC side support program, I decided to look at alternatives.

  • I2C would be too slow, and I would need some kind of intermediary like a PIC or Raspberry PI to talk I2C and then something else, like TCP/IP or UDP to talk to the PC side.
  • SPI, at 10Mbps would probably be fast enough, but also would need an intermediary.
  • I could use an embedded soft processor, like a MicroBlaze, on the FPGA. That would probably work, but would take up a lot of resources on the FPGA, and I was concerned that the Xilinx XC7A100T might not have enough for all of the things I might want to do, especially with a TCP or UDP stack added in. I did do a little playing with Microblaze a few years back, and found the development process somewhat cumbersome: it would considerably lengthen all of the place and route everytime I made a change.
  • Understandably there do not seem to be any direct TCP implementations in VHDL or Verilog that I could find.
  • I found three different implementations of UDP, two in VHDL and one in Verilog that I decided to test.

Two of the UDP implementations were on opencores.org. They were old, apparently done as a research project, and it was not apparent how to configure it to work on my hardware, so I quickly abandoned those. Plus, it took opencores.org three months to grant my request for a user ID.

Next I looked at the UDP implementation from Alex Forencich. At the time, it was located at https://github.com/alexforencich/verilog-ethernet, however, that one has apparently been superceded by one at https://github.com/fpganinja/taxi — however this latter one seems to no longer include a UDP stack. This UDP implementation looked promising, however there were a couple of issues for me. Firstly, it used a generation process using cocotb. When I tried to do that, it did not work well for me. So, I ended up doing what it would have done to figure out what files I actually needed.

The second issue with this UDP implementation from Alex Forencich was that it was not directly complatible with the physical ethernet interface (PHY) on my Nexys4 development board. It is set up to interface with an MII layer, however, my Nexys4 PHY uses an RMII layer. I tried a couple of approaches to resolve this issue. The first was to see if the Ethernet MAC at https://github.com/chasep255/Nexys-4-DDR-Ethernet-Mac would work. It provides an AXI interface to the Ethernet MAC on the Nexys4 board, but also uses RMII. There was something important I cleaned from this latter project however: how to build a test bench to send and receive Ethernet packets, which I used quite a bit.

In the end I found a free MII to RMII v2.0 interface layer IP from Xilinx. While not available in the latest versions of Vivado, it was still there in my older versions of Vivado, and while not supported by Xilinx, it works fine in Vivado 2023.1, and I would expect it will continue to function OK in later releases as well. And, worst case I could also do a development board upgrade to something that has an Ethernet interface using MII.

It took me quite a bit of futzing around to get Alex Forencich’s UDP layer working as it was in Verilog, and I need to interface to it with VHDL. Also, the example/sample logic just does a UDP echo, and it took some time and testing to figure out how to separate out sending and receiving UDP packets.

Once I had it working, it was not tremendously difficult to integrate it into my 1410 FPGA logic. I chose to continue to use a stream UART-like interface that is as close as possible to what I had for an actual UART, so that only minimal changes were required in my existing state machines. I did need to expand the intermediary output related FIFOs from 8 bits to 9 in the logic, however, to carry a “flush flag” so that, for example, when the 1410 needed to send a UDP request for a tape operation, it could force the UDP layer to send the packet even though it was not full. Everything else remained essentially the same as when I was using a serial port.

I now use the UDP implementation for TAU related packets going both ways, to and from the FPGA, for lamp data going from the FPGA to the PC and for sending memory images (“core loads”) from the PC to the FPGA. I did find that I have to throttle the lamps some and not update them as fast as I’d like – the speed of the C# code and Windows updates are a limiting factor. I even found that on tape writes I had to add what kind of corresponds to a tape stop in the inter-record gap, to add a delay when writing so that it does not overwhelm the PC (which leads to lost packet(s) and a program error). I also added some delay when the PC is sending data back to the FPGA for tape reads so that it does not overwhelm the FPGA UDP stack.

With those changes, it can now reliable write and read tape records, though some tape related problems when running diagnostic T020 remain unchanged:

  • Error 17, caused by a test of the ability to do an erase operation to write a long interrecord gap over a bad stretch of tape.
  • Errors 39, 41 and tape I/O operations resulting in Wrong Length Record Status. The problem relates to what happens when writing a record that goes to the end of core storage (with no Group Mark + Word Mark) and reading records that go to the end of core storage.
  • In one of the later tests a read tape to end of core is executed, in odd parity. The tape data is in odd parity as well. However, the operation generates a data check, and core storage from 39990 to 39998 (for a 9 character record) are all asterisks from what the 1410 sees as an invalid parity.
  • On Channel 2 / F Channel, errors 20, 23 and 70 which seem to relate to problems when a tape mark is encountered after writing one and backspacing over it.
  • T020 starts by trying an overlapped write on each tape drive. If it finds one that is ready, the result is a 1410 error stop, I think on an “R” I/O status branch instruction, with only 1 byte transferred to the PC.

Leave a Reply