It has been a while since I wrote the last post on this project. In the meantime, I could dedicate all my limited free time to work on the implementation. It took a long time, but I did make significant progress. The calculator is pretty much completed.
In the heart of it is a small, custom (soft) CPU.
I created a very simple, carefully crafted, instruction set for it. At the same time, I wrote a Python-based assembler and verification microcode. The instructions took shape as I was continuously optimizing them for minimal gate use, code density, and likely frequency of use.
Next, I started writing functional microcode (in the assembly language of that custom CPU). That task itself took good 3-4 months. Some operations required me to write separate software simulators; for example, a seemingly simple “number input” heuristic that included dealing with the exponents could be best developed and debugged using a functionally equivalent C++ code and then “hand-ported” to assembly.
Once all basic calculator operations have been written, they were tested and cross-checked using 7000+ test vectors (the values included those specifically selected to hit various corner cases and then some random tests). The microcode closely followed the algorithms that we already developed and refined in the “Practical Numerical Methods” post. This time, I developed a verification method that would run each test through Verilator (running the Verilog implementation) and compare it to the output of a C++ model, which itself was compared with the standard C library (double precision) golden values. My goal was 13-14 digits of precision and it was largely reached (a detailed accuracy characterization is outside the scope of this brief post).
When I started looking at the higher-order functions that could be derived from the basic ones (either through one of the many equivalence formulas or by a need to implement a recursive or an iterative process), it became obvious that the most effective solution would be to implement a scripting language and simply “script” those functions. Hence, I devised a simple scripting language and implemented its interpreter in assembly. The interpreter became part of the calculator firmware.
That additional capacity (which itself required writing a scripting compiler in Python and a set of tests for it), facilitated a very rapid implementation of the rest of the functions. All remaining functions could now be quickly added. In fact, the scripting language proved so effective that I was tempted to implement more functions than originally planned, and in the end, I simply run out of the available keys to map them.
Best of all, I was not even close to using all the available resources of a rather older and smaller Altera Cyclone II FPGA. The complete calculator is using only about 30% of LE’s (~1,400 LEs) and 55% of the embedded memory bits. The calculator implementation does a lot given that relatively small usage.
I do want to describe the implementation in more detail and publish the cleaned-up code, but that might take a while. In the meantime, you can see a demo:
The video above shows a “version A” of the board which has a rather large separate keypad attached to the stock FPGA board. In the meantime, I have designed and manufactured (through JLCPCB) “version B” of the board which is completely self-contained and has a LiPo battery. It consists of the main board and a more compact keypad.
Here is another link, this one is to the Qt-compiled WebAssembly which may or may not work for you – it should work on a desktop PC due to the screen size requirement and the processing speed – it may not work well on a phone:
Open the “Calculator.html” in your web browser and the WebAssembly code will compile and run verilated HDL code (using the framework described in one of my previous articles). Be patient as it runs really slow, with most operations taking tens of seconds.
If you are still reading this, you are likely the kind of person who would also enjoy a WebAssembly version with the debug console. Among the other commands, “l” will list the microcode (keep pressing Enter after it); “c” will continue executing.
Hope it works for you, and hope you find it interesting!
This project is part of the Homebuilt CPUs WebRing