diff --git a/drafts/church-encoding-in-javascript.markdown b/drafts/church-encoding-in-javascript.markdown index beadd8c..9ac3e69 100644 --- a/drafts/church-encoding-in-javascript.markdown +++ b/drafts/church-encoding-in-javascript.markdown @@ -11,3 +11,389 @@ TODO TODO + +``` {.javascript .code-term .numberLines} +/** + * (C) Copyright Collin J. Doering 2015 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +/** + * File: church.js + * Author: Collin J. Doering + * Date: Feb 2, 2015 + */ + +var church = (function () { + var spec, + t = function (x) { + var ret = function (y) { + return x; + }; + return ret; + }, + f = function (x) { + var ret = function (y) { + return y; + }; + return ret; + }, + church_zero = function (f) { + var ret = function (x) { + return x; + }; + return ret; + }, + nil; + + // ------------------------------ + // Church Boolean implementation + // ------------------------------ + + function make_bool (b) { + // b ? return t : return f + if (b) { + return t; + } else { + return f; + } + } + + function unchurch_bool (b) { + return b(true)(false); + } + + function and (x) { + var ret = function (y) { + return x(y)(x); + }; + return ret; + } + + function or (x) { + var ret = function (y) { + return x(x)(y); + }; + return ret; + } + + function not (x) { + var ret = function (y) { + var aret = function (z) { + return x(z)(y); + }; + return aret; + }; + return ret; + } + + function xor (x) { + var ret = function (y) { + return x(not(y))(y); + }; + return ret; + } + + function church_if (p) { + var ret = function (a) { + var aret = function (b) { + return p(a)(b); + }; + return aret; + }; + return ret; + } + + // ----------------------- + // Church natural numbers + // ----------------------- + + function succ (n) { + var ret = function (f) { + var aret = function (x) { + return f(n(f)(x)); + }; + return aret; + }; + return ret; + } + + function add (n) { + var ret = function (m) { + var aret = function (f) { + var bret = function (x) { + return n(f)(m(f)(x)); + }; + return bret; + }; + return aret; + }; + return ret; + } + + function mult (n) { + var ret = function (m) { + var aret = function (f) { + var bret = function (x) { + return n(m(f))(x); + }; + return bret; + }; + return aret; + }; + return ret; + } + + function expt (n) { + var ret = function (m) { + var aret = function (f) { + var bret = function (x) { + return (m(n))(f)(x); + }; + return bret; + }; + return aret; + }; + return ret; + } + + function isZero (n) { + var ret = n(function (x) { + return f; + })(t); + return ret; + } + + function make_nat (n) { + var i, ret = church_zero.bind({}); + for (i = 0; i < n; i += 1) { + ret = succ(ret); + } + return ret; + } + + function unchurch_nat (n) { + var ret = function (i) { + var aret = function (m) { + i += 1; + return i; + }; + return aret; + }, i = 0; + return n(ret(i))(0); + } + + // ------------- + // Church Pairs + // ------------- + + function make_pair (a) { + var ret = function (b) { + var aret = function (f) { + return f(a)(b); + }; + return aret; + }; + return ret; + } + + function fst (p) { + return p(t); + } + + function snd (p) { + return p(f); + } + + function unchurch_pair (p) { + return [fst(p), snd(p)]; + } + + // ------------- + // Church Lists + // ------------- + + function make_list () {} + + function unchurch_list (xs) {} + + nil = make_pair(t)(t); + isNil = fst; + + function cons (h) { + var ret = function (t) { + return make_pair(f)(make_pair(h)(t)); + }; + return ret; + } + + function head (l) { + return fst(snd(l)); + } + + function tail (l) { + return snd(snd(l)); + } + + // ----------------- + // The Y Combinator + // ----------------- + // * This doesn't work as javascript is strictly evaluated + // ----------------- + + function fix (g) { + var f = function (x) { + return g(x(x)); + }; + return f(f); + } + + // Setup specification object + spec = { + "if": church_if, + fix: fix, + bool: { + make: make_bool, + toNative: unchurch_bool, + t: t, + f: f, + not: not, + and: and, + or: or, + xor: xor + }, + nat: { + make: make_nat, + toNative: unchurch_nat, + zero: church_zero, + succ: succ, + add: add, + mult: mult, + expt: expt, + isZero: isZero + }, + pair: { + make: make_pair, + toNative: unchurch_pair, + fst: fst, + snd: snd + }, + list: { + make: make_list, + toNative: unchurch_list, + nil: nil, + isNil: isNil, + cons: cons, + head: head, + tail: tail + } + }; + return spec; +})(); + +// ------------------------- + +function unchurch_church_nat_test (lim) { + var i, + lim = lim || 12; + for (i = 0; i <= lim; i += 1) { + if (i !== church.nat.toNative(church.nat.make(i))) { + console.log('Failed church.nat.toNative(church.nat.make(' + i + '))'); + return false; + } + } + console.log('Created church nats from 1 to ' + lim + ' successfully.'); + return true; +} + +function uncurry (f) { + var ret = function (x, y) { + return f(x)(y); + }; + return ret; +} + +function matrix_test (f, g, n, tp) { + var i, j, + t = tp || 100, + name = n || "unnamed"; + for (i = 0; i <= t; i += 1) { + for (j = 0; j <= t; j += 1) { + if (f(i,j) !== g(i,j)) { + console.log('Failed matrix test for ' + name + ' with inputs ' + i + ' and ' + j + '.'); + return false; + } + } + } + console.log('Successfully completed test for ' + name + '.'); + return true; +} + +unchurch_church_nat_test(100); + +matrix_test(function (a, b) { + return a + b; +}, function (a, b) { + return church.nat.toNative(church.nat.add(church.nat.make(a))(church.nat.make(b))); +}, "Church addition"); + +// matrix_test(function (a, b) { +// return a - b; +// }, function (a, b) { +// return church.nat.toNative(minus(church.nat.make(a))(church.nat.make(b))); +// }); + +matrix_test(function (a, b) { + return a * b; +}, function (a, b) { + return church.nat.toNative(church.nat.mult(church.nat.make(a))(church.nat.make(b))); +}, "Church multiplication"); + +// very slow for some reason? (not that this implementation will ever be fast) +matrix_test(function (a, b) { + return Math.pow(a, b); +}, function (a, b) { + return church.nat.toNative(church.nat.expt(church.nat.make(a))(church.nat.make(b))); +}, "Church exponentiation", 7); + + +// ------------------------------------------------------- +// Factorial function written similar to how the y-combinator works +function factorial (x) { + var g = function (h) { + var f = function (n) { + if (n < 2) { + return 1; + } else { + return n * h(h)(n - 1); + } + }; + return f; + }; + return g(g)(x); +} + +var i; +for (i = 0; i < 10; i += 1) { + console.log(factorial(i)); +} +``` + + diff --git a/drafts/computer-from-scratch.markdown b/drafts/computer-from-scratch.markdown index e7a2cf3..7145cc6 100644 --- a/drafts/computer-from-scratch.markdown +++ b/drafts/computer-from-scratch.markdown @@ -11,23 +11,104 @@ Recently I have had the pleasure of completing the course *Nand to Tetris* on thought it would be possible for me to understand computers all the way down to the hardware level (past assembly that is); *Nand to Tetris* has shown me otherwise! It has also inspired me to pursue learning a real world hardware description language and attempt to implement the -*Hack* system on an FPGA. In this article I will describe how the process is coming and the +*Hack* system on an [FPGA](https://en.wikipedia.org/wiki/Field-programmable_gate_array) (Field +Programmable Gate Array). In this article I will describe how the process is coming and the what remains to be completed. +For the impatient, the repository is located [here][hack-git] and is documented +[here][hack-docs]. + After implementing the *Hack Computer System* in the hardware description language described by -the course, I went on to implement everything in [VHDL](https://en.wikipedia.org/wiki/VHDL), -with the end goal of being able to realize the design on a -[FPGA](https://en.wikipedia.org/wiki/Field-programmable_gate_array) (Field Programmable Gate -Array). I also needed a set of test benches to verify correctness of the design through -simulation. Initially I set out to find a completely open source solution. To my dismay, I -found that FPGA's are incredibly proprietary. Namely, the process of *synthesis* cannot be done -using open source tools, though there exists a few projects which are reverse engineering the -bit file format of various FPGA's in order to develop a open solution to synthesis, they are -not yet ready for use. So, synthesis needs to be done with proprietary tools. +the course, I wanted to explorer further. So after doing a little research, I found there were +two main industry strength hardware description languages, +[VHDL](https://en.wikipedia.org/wiki/VHDL) and +[Verilog](https://en.wikipedia.org/wiki/Verilog). I then set out to find open source +implementations of these languages. Happily I found [GHDL](http://ghdl.free.fr/) and +[Icarus Verilog](http://iverilog.icarus.com/) respectively. After briefly looking over both +languages and their open source implementations, I decided I would use VHDL because I'm a fan +of type systems and it is similar to the hardware description language given by the *Nand to +Tetris* course. + +Now both *Icarus Verilog* and *GHDL* are unable to do *synthesis*. For those of you who are +unaware of what *synthesis* is, it refers to the process of taking a hardware description and +converting it into a format a FPGA can use. This is generally a bit file for JTAG programming +or a bin file for writing to the FPGA board's internal memory. To my dismay, FPGA's are +incredibly proprietary! Namely, the *synthesis* process can only be done using proprietary +tools. So initially I decided I would forgo thinking about how exactly I would get my +implementation on a FPGA but instead get the components of the system designed and verified +though simulation, which can be completed using all open source tools. Now, there is a +unsynthesizable subset of both VHDL and Verilog, so care needs to be taken to ensure that the +design doesn't utilize these parts as the end goal is to implement the *Hack Computer System* +on a FPGA. An exception to this is when a entity/module will only be used in simulation (Eg. +Reading a text file and emulating a ROM that contains the machine language instructions +specified in the file). + +Following the materials given by the course, I then went on to implement the *Hack Computer +System* in VHDL. Currently, the project [resides here][hack-git] and has some +[accompanying documentation][hack-docs] which explain how things work, how to build the +project, and how to run a simulation. Please refer to the [documentation][hack-docs] for +complete instructions on building and using the [hack][hack-git] project and simulator. + +Before going into detail on some of the design choices I made, I'd like to give an example of +setting up the simulator and running the default simulation of the top most unit, +`computer_tb`, which computes the first 25 Fibonacci numbers and puts them in RAM address' `0x3` +through `0x28`. + +``` {.bash .code-term} +$ git clone http://git.rekahsoft.ca/hack +$ cd hack/src +$ ghdl -i --workdir=work *.vhdl +$ ghdl -m --workdir=work computer_tb.vhdl +$ ghdl -r computer_tb --stop-time=750ns --vcd=wave/vcd/computer-fib.vcd +$ vcd2fst wave/vcd/computer-fib.vcd wave/vcd/computer-fib.fst +``` + +First, clone the sources, then import and build them with `ghdl`. Finally run the simulation +using the default input program and output a vcd file to `src/wave/vcd/computer-fib.vcd` which +is then converted to a `fst` file using `vcd2fst`. Now once the simulation has complete the +*vcd* or *fst* file can be opened with a wave form viewer. I recommend and have only tested +with [GtkWave][]. To open the dump file open [GtkWave][] and go to `File -> Open New Tab` (or +`C-t`) and select the output file (in our case `src/wave/vcd/computer-fib.fst`). I have +provided pre-saved "views" for `computer_tb` (as well as the other entities within the project) +so that the signals of interest for the entity in question don't need to be repopulated into +the signals panel each time [GtkWave][] is opened. To open the [GtkWave][] save file go to +`File -> Read Save File` (or `C-o`) and select the appropriate save file. In our case we will +open `src/wave/gtkw/computer-fib.gtkw`. + +Note running this simulation will take some time (likely longer then an hour) and will consume +lots of RAM and CPU and generate a large vcd dump (> 20 GB). By changing the length of time the +simulation runs for one can limit the size of the vcd dump, but the usage of the CPU and RAM +will remain the same. The large vcd dump file is unavoidable (as far as I know) because *GHDL* +records data for all signals in the design and there's no way to filter its output until after +the simulation has run (using external tools). + +![[GtkWave][] displaying a simulation dump from *computer_tb* running + *src/asm/Fib.hack*](files/images/gtkwave.png) + +Using a +[generic property](http://www.doulos.com/knowhow/fpga/Setting_Generics_Parameters_for_Synthesis/) +`program_file`, a user is able to pass a text file containing *Hack Machine Language* to the +`computer_tb` unit using the `-g` switch to *GHDL* like so: +`-gprogram_file=path/to/program.hack`. If none is specified it defaults to +[src/asm/Fib.hack](http://git.rekahsoft.ca/hack/tree/src/asm/Fib.hack) (the corresponding *Hack +assembly* form is provided [here](http://git.rekahsoft.ca/hack/tree/src/asm/Fib.asm)). The +contents of the given `program_file` are used as the ROM data for the duration of the +simulation. + +Unfortunately specifying generic properties when ghdl is invoked is only implemented in very +recent versions of *GHDL* (later then 2015-03-07; see +[ticket](http://sourceforge.net/p/ghdl-updates/tickets/37/?limit=25)). If you are running an +older version of *GHDL* then you must modify `src/computer_tb.vhdl` to specify the program you +want to run in the simulation. See the [documentation][hack-docs] for details. + +![[GtkWave][] zoomed in to view approximately *100 ns* of signal + output](files/images/gtkwave-closeup.png) -which is -taking the hardware description (either VHDL or Verilog) and convert it to a bit file (a format -the FPGA can use). +TODO: talk about current state of project (deficiencies in regards to screen and keyboard memory maps) + +[hack-git]: http://git.rekahsoft.ca/hack/ +[hack-docs]: http://git.rekahsoft.ca/hack/about +[GtkWave]: http://gtkwave.sourceforge.net/ diff --git a/files/images/gtkwave-closeup.png b/files/images/gtkwave-closeup.png new file mode 100644 index 0000000..38d5c10 Binary files /dev/null and b/files/images/gtkwave-closeup.png differ diff --git a/files/images/gtkwave.png b/files/images/gtkwave.png new file mode 100644 index 0000000..a811471 Binary files /dev/null and b/files/images/gtkwave.png differ