Added files/images directory and modified drafts

The file/images directory is meant for images used for posts. They will
not be application cached (which is what one would want as otherwise the
application cache would become too large).

Signed-off-by: Collin J. Doering <collin.doering@rekahsoft.ca>
This commit is contained in:
Collin J. Doering 2015-07-03 02:55:17 -04:00
parent 9976b7a719
commit b4d1523cc6
4 changed files with 480 additions and 13 deletions

View File

@ -11,3 +11,389 @@ TODO
<!--more-->
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 <http://www.gnu.org/licenses/>.
*/
/**
* File: church.js
* Author: Collin J. Doering <collin.doering@rekahsoft.ca>
* 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));
}
```
<!--
``` {.javascript .code-term .numberLines include="../files/source/church.js"}
```
Find a way to include source files without having to copy-paste them into the article.
See: http://stackoverflow.com/questions/21584396/pandoc-include-files-filter-in-haskell
-->

View File

@ -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].
<!--more-->
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/

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

BIN
files/images/gtkwave.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB