Friday, January 10, 2020

Testing the Verilog RAM-based VFD driver

Over several evenings I wrote the Verilog to drive my VFD from a RAM-based i4002 RAM emulation. The comparison between this and the previous one is apples and bananas, which is to say they're not very similar. But I did get it working.

This evening I finished debugging in the simulator and loaded the bitstream into the FPGA through the JTAG. The test is set up to increment the content of the emulated WR register twice a second.

Although most people reading this blog wouldn't have trouble picturing a counter incrementing twice a second here's a video anyway, showing the carry from the low order digit to the higher digits as it counts from 1990 to 2012.

 

Why bother making the WR register increment?

The tools are designed to optimize the results of the synthesis, so if you're not careful when setting up a test large portions of your design may simply disappear. This can make what would really be a more complicated design appear smaller than it will in actual use. For example, I inferred a dual-port distributed RAM in my Verilog with values set in "initial" statements. Without the incrementer, the tools recognized that I wasn't writing to the primary port, reclassified it as a ROM, and completely removed all the write logic and half the LUTs used to implement the RAM. Something similar happened with my flip-flop based design when I removed the Digilent I/O Expansion module.

Even with the incrementer logic, this test uses only 149 out of 5,720 LUTs, and only 54 for the VFD driver itself. Only 6 of the 149 LUTs are used as dual-port RAMs and the rest perform logic functions; I have no idea how this came out to six. The "Technology Schematic" display in ISE shows only four RAM32X1D blocks. Each column of a 20x4 array should occupy two LUTs -- one for each of the two output ports -- so I expected it to use 8. Or, if the 64x1 LUT is split into a pair of 32x1 RAMs, I'd expect 4. Six is just confusing, unless it somehow split the four "status characters" from the 16 "main characters" and saved two LUTs.

For a size comparison, the best I can do is to compare this with the FF-based implementation that included the Digilent I/O expansion module. As noted in Packing worms into a can, that implementation required 331 LUTs, with the Digilent module using 205 of them. The emulation of the WR register was part of the Digilent module, and it's hard to separate one from the other. But even with the extra incrementer logic, the RAM-based implementation is far smaller.

Another measure is how features like leading zero suppression were handled. In the flip-flop based implementation I handled this by having logic for each digit that considered whether the digit's value was zero, if its neighbor to the left was suppressed, and the position of the decimal point. This logic was instantiated once per digit, and there was a big 13x4 mux to select which digit's value was sent to the 7-segment decoder.

In the RAM-based implementation, each digit's value comes out of the RAM one digit at a time. The digits are now sent to the VFD from high-order digit to low-order, or left to right. There is only one instantiation of the zero-suppression logic, and the suppression state of the higher-order digit is saved in a single flip-flop. The hardest part turned out to be figuring out how to scan the four highest-order digits that I can't display. To do this I decoupled the RAM address from the digit being refreshed, and the upper four digits are now checked rapidly while the 13th "digit" position with the Memory/Negative/Overflow indicators is driven.



Unfortunately, I don't think I'm done with the VFD driver. My VFD can only display 12 digits, while the Busicom 141-PF software handles 14 digits. To make things simpler I decided to start by displaying only the lower 12 digits of the WR register. That's fine if it's handling smaller integers. But what happens if you calculate the square root of 2? The documentation says this will print as 1.4142135623730. If I display the lowest 12 digits my VFD will show 142135623730. That's not great, and the same problem may arise when calculating something simpler like 1÷3.

I'd thought I might be able to do something with the muxes to display the first non-zero digit but I hadn't given much thought to how it would work. Now that I'm scanning from left to right and the RAM address isn't tightly coupled to the digit position being driven, this may be a lot simpler to handle. I still don't know exactly how I'll do it, but I'll figure that out eventually.

No comments:

Post a Comment