It has been a while since I wrote the last post about this project. In the meantime, I was able to dedicate all my free time to work on the implementation. As I was making really good progress, the calculator started taking shape. My excitement grew and I just could not stop the work to write about it.
First, the custom CPU was born.
A very simple, carefully selected, instruction set took form and was given a breath of life by a Python-based assembler and the verification microcode. Instructions were given meaning and shape, and various forms were coming in and out of existence as I would optimize the encodings for minimal gate use, code density, and likely frequency of use.
Next, I started writing functional microcode. That itself took good 3-4 months. Some operations needed separate 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, cross-checked using 7000+ test vectors (values 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 functions that are derived from the basic ones (either through one of the many equivalence formulas or by a need to implement a process), it became obvious that the most effective solution would be to implement a scripting language and simply "script" those functions by the means of a programmable processor. Hence, I wrote a scripting language and implemented its interpreter in assembly.
That additional capacity (which itself required writing a scripting compiler and a set of tests for it), facilitated a very rapid implementation of the rest of the functions. In fact, pretty much any existing calculating function could now be quickly added.
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; I feel it does "amazingly a lot" with it.
In a nutshell, that's it. 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:
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, 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!