Giter Site home page Giter Site logo

hdl-js's People

Contributors

dmitrysoshnikov avatar gfosco avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

hdl-js's Issues

Implement HDL code generator

The code generator is an inverse procedure for parsing: having an AST, we need to generate HDL code.

This will be used to generate HDL specs from gate instances, and exporting a gate built in the UI interface to an HDL file.

const hdl = require('hdl-js');
const ast = hdl.parseFile('./examples/And.hdl');

const {generator} = hdl;

// Gives a code close to the original ./examples/And.hdl
const hdlCode = generator.fromAST(ast);

console.log(hdlCode);

/*

Output:

CHIP And {

  IN a, b;
  OUT out;

  PARTS:

  Nand(a=a, b=b, out=n);
  Nand(a=n, b=n, out=out);
}

*/

The generated code should be readable and pretty-printed with 2 spaces indentation.

Example of a very similar generator in the regexp-tree project: https://github.com/DmitrySoshnikov/regexp-tree/blob/master/src/generator/index.js Basically need to handle each AST node type, and return string representation of it.

Test code Error

I run the test under windows and find an error. What is the reason?

F:\hdl-js>node bin/hdl-js -g examples/MipsAlu16.hdl -e '[{a: 2, b: 3, op: 2}]' -f dec -c a,b,out
evalmachine.:1
('[{a:)
^^^^^^

SyntaxError: Invalid or unexpected token
at createScript (vm.js:80:10)
at Object.runInNewContext (vm.js:135:10)
at parseInputData (F:\hdl-js\dist\bin\hdl-js-cli.js:117:19)
at loadAndProcessData (F:\hdl-js\dist\bin\hdl-js-cli.js:110:10)
at main (F:\hdl-js\dist\bin\hdl-js-cli.js:520:16)
at Object. (F:\hdl-js\bin\hdl-js:5:34)
at Module._compile (module.js:653:30)
at Object.Module._extensions..js (module.js:664:10)
at Module.load (module.js:566:32)
at tryModuleLoad (module.js:506:12)

Emulator: expose Pin 'change' event

When a Pin instance changes its value, we should emit 'change' event (for this Pin class should extend EventEmitter).

Example:

const a = new Pin({name: 'a', size: 16});

a.on('change', (newValue, oldValue) => {
  console.log(`Pin "a" changed value from ${oldValue} to ${newValue}.`);
});

a.setValue(255);

/*

Result:

Pin "a" changed value from 0 to 255.

*/

[Emulator] Implement built-in gates

The following gates have to be implemented as internal chips:

Very basic:

Basic chips:

ALU:

Memory chips:

Computer

  • Screen (video/screen memory) (81de2b9)
  • Keyboard (memory to map keyboard) (6583e4a)
  • Memory (abstract memory block: logical partitioning by addresses: Screen, Keyboard, etc)
  • CPU (main central processing unit chip)

[Emulator] Implement MIPS ALU

This is a specified ALU based on the MIPS architecture. See design doc here:
ALU_AppB.pdf.

  • HDL implementation in examples
  • Built-in JS implementation
    • MipsAlu.js
    • MipsAlu16.js

1-bit ALU:

IN a, b, na, nb, cin, less, op[2];
  • a - 1-bit input

  • b - 1-bit input

  • na - negate a?

  • nb - negate b?

  • less - special bit for the "less than operation" (propagated directly, calculated in the 16-bit ALU)

  • cin - carry in (from previous operation)

  • op[2] - 2-bits opcode

    • 00 - AND
    • 01 - OR
    • 10 - AND (done with a full-adder, carry-in, carry-out)
    • 11 - propagate less input directly
OUT out, cout, set;
  • out - result output
  • cout - carry out from the full adder
  • set - adder result, used to set less value for the LSB in 16-bit ALU

16-bit ALU

A 16-bit ALU consists of 16 1-Bit ALUs.

The inputs (semantics is the same as in 1-bit ALU above):

IN a[16], b[16], na, nb, cin, op[2];

Outputs:

OUT out[16], overflow, zero;
  • overflow - whether there was an overflow in math (add/sub) operation
  • zero - whether the out result is 0 (used for comparisons)

Gates scripting: pin width error

Hello.

I'm taking the nand2tetris course and I'm using hdl-js' gate scripting feature to execute tests more conveniently from the terminal.

I'm having the following error with multi-way 16-bit chips, Mux4Way16 and Mux8Way16:

Script error: TypeError: Pin "Mux1": value 4660 doesn't match pin's width. Max allowed is 1 (size 1).
    at Pin.setValue (/home/projects/hdl-js/node_modules/hdl-js/dist/emulator/hardware/Pin.js:112:15)
    at eval (/home/projects/hdl-js/node_modules/hdl-js/dist/emulator/hardware/Pin.js:330:18)
    at Pin.listener (/home/projects/hdl-js/node_modules/hdl-js/dist/emulator/hardware/Pin.js:217:16)
    at EventEmitter.emit (https://hdl-js.w.staticblitz.com/blitz.1f021b18268b32e6c6b2095e039ac8c9f88b0d52.js:6:155573)
    at Pin.setValue (/home/projects/hdl-js/node_modules/hdl-js/dist/emulator/hardware/Pin.js:116:12)
    at Mux16._eval [as _originalEval] (/home/projects/hdl-js/node_modules/hdl-js/dist/emulator/hardware/builtin-gates/Mux16.js:104:31)
    at Mux16._evalEmit (/home/projects/hdl-js/node_modules/hdl-js/dist/emulator/hardware/Gate.js:428:12)
    at GateClass._eval [as _originalEval] (/home/projects/hdl-js/node_modules/hdl-js/dist/emulator/hardware/CompositeGate.js:105:16)
    at GateClass._evalEmit (/home/projects/hdl-js/node_modules/hdl-js/dist/emulator/hardware/Gate.js:428:12)
    at GateClass._eval [as _originalEval] (/home/projects/hdl-js/node_modules/hdl-js/dist/emulator/hardware/CompositeGate.js:105:16)

I'm not having this problem on nand2tetris' Hardware Simulator 2.5:

Mux4Way16 Mux8Way16
Screen Shot 2022-08-14 at 19 21 27 Screen Shot 2022-08-14 at 19 21 45

I reproduced the error on StackBlitz.

Thanks in advance.

CLI: expose execOnData as an option

Gates in the emulator support execOnData method which accepts a table with input data (or the full input truth table), executes the gate on this dat, and returns resulting (actual) truth table. If the output pins are passed, returns also conflicting data, if actual values differ from the passed ones.

We should expose it as --exec-on-data (-e) option.

./bin/hdl-js -g And -e '[{"a": 1, "b": 0}]'

Output:

Truth table for the input:

┌───┬───┬─────┐
│ a │ b │ out │
├───┼───┼─────┤
│ 1 │ 0 │  0  │
└───┴───┴─────┘

Passing expected output pins (invalid in this case):

./bin/hdl-js -g And -e '[{"a": 1, "b": 0, "out": 1}]'

Result:

Found conflicts in:

  - row 0, pins: out

┌───┬───┬─────┐
│ a │ b │ out │
├───┼───┼─────┤
│ 1 │ 0 │  0  │
└───┴───┴─────┘

The 0 should be colored in red.

CLI: implement truth table printer

The --table (-t) option should print a truth table of a gate, applying to all possible inputs.

./bin/hdl-js --file examples/And.hdl --table

Result:

Truth table for "And":

┌───┬───┬─────┐
│ a │ b │ out │
├───┼───┼─────┤
│ 0 │ 0 │  0  │
├───┼───┼─────┤
│ 0 │ 1 │  0  │
├───┼───┼─────┤
│ 1 │ 0 │  0  │
├───┼───┼─────┤
│ 1 │ 1 │  1  │
└───┴───┴─────┘

CLI: Add --columns option

When the --columns options is passed (comma-separated list), only this whitelist of truth table columns is shown.

All columns in the result table (default):

./bin/hdl-js -g examples/And.hdl -e '[{a: 1, b: 1}]'

Truth table for data:

┌───┬───┬───┬─────┐
│ a │ b │ n │ out │
├───┼───┼───┼─────┤
│ 1 │ 1 │ 0 │  1  │
└───┴───┴───┴─────┘

Only whitelist:

./bin/hdl-js -g examples/And.hdl -e '[{a: 1, b: 1}]' --columns a,b,out

Truth table for data:

┌───┬───┬─────┐
│ a │ b │ out │
├───┼───┼─────┤
│ 1 │ 1 │  1  │
└───┴───┴─────┘

Help with design of low-level HDL language

FPGA world suffers a lot from fragmentation - some tools produce Verilog, some VHDL, some - only subsets of them, creating low-level LLVM-like alternative will help everyone, so HDL implementations will opt only for generating this low-level HDL and routing/synthesizers accept it. LLVM or WebAssembly - you can see how many languages and targets are supported now by both. With more open source tools for FPGA this is more feasible now than ever. Most of the people suggest to adapt FIRRTL for this. Please check the discussion and provide a feedback if you have any. There is a good paper on FIRRTL design and its reusability across different tools and frameworks.

See f4pga/ideas#19

[Emulator] Support scripting for gates testing

The --exec-on-data already allows accepting testing data, and validating the outputs. In addition we can provide an ability to do scripted testing of the pins, and manually call some events (like tick, tock, etc) in the scripts. This can be compatible with the nand2tetris script testing.

TODO:

Example testing And:

load And.hdl,
output-file And.out,
compare-to And.cmp,
output-list a%B3.1.3 b%B3.1.3 out%B3.1.3;

set a 0,
set b 0,
eval,
output;

set a 0,
set b 1,
eval,
output;

set a 1,
set b 0,
eval,
output;

set a 1,
set b 1,
eval,
output;

Output:

|   a   |   b   |  out  |
|   0   |   0   |   0   |
|   0   |   1   |   0   |
|   1   |   0   |   0   |
|   1   |   1   |   1   |

[Parser] Support for-loop

Currently multi-bit pins have to be set manually (see Add16 example). It'd be great to support for-loop in HDL.

CHIP And16 {

  IN a[16], b[16];
  OUT out[16];

  PARTS:
  for i = 0..15 {
    And(a=a[i], b=b[i], out=out[i]);
  }
}

CLI: unify --gate option for both BuiltIn and Composite gates from HDL files

Currently built-in gates are handled by the --gate (-g) option, while custom gates from HDL files -- via --file (-f) option. We can unify, and use only --gate option for both, and reserve -f to future --format option.

Built-in:

./bin/hdl-js --gate And --describe

Custom:

./bin/hdl-js --gate examples/And.hdl --describe

[CLI] --exec-on-data: accept file name with JSON-like structure for data

Currently one have to pass the data for the --exec-on-data (-e) directly in the command line. We can extend it to accept file names as well which contain data in the extended JSON-notation.

Via direct data (currently supported):

./bin/hdl-js -g And -e '[{a: 1, b: 0}]'

Truth table for data:

┌───┬───┬─────┐
│ a │ b │ out │
├───┼───┼─────┤
│ 1 │ 0 │  0  │
└───┴───┴─────┘

File ~/my-data.dat:

[
  {a: 1, b: 0},
]

Via filename (need to add support for):

./bin/hdl-js -g And -e ~/my-data.dat

... same output ...

[Parser] Support 0 and 1 constants

Currently constants have to be passed as logical false, and true in the ChipCall argument values. We need to support also 0, and 1 aliases.

Current:

MyChip(a=false, b=true);

Add also support for:

MyChip(a=0, b=1);

Emulator: allow creating pins from spec data

Currently from Node one have to manually create Pin instances before passing to a gate constructor:

const and = new And({
  inputPins: [
    new Pin({name: 'a'}),
    new Pin({name: 'b'}),
  ],

  outputPins: [
    new Pin({name: 'out'}),
  ],
});

For convenience gate's constructor can automatically create Pin instances:

const and = new And({
  inputPins: ['a', 'b'],
  outputPins: ['out'],
});

For pin bus:

const and16 = new And({
  inputPins: [
    {name: 'a', size: 16},
    {name: 'b', size: 16},
  ],
  outputPins: [
    {name: 'out', size: 16},
  ],
});

[Emulator] HDLClassFactory: handle BUILTIN directive

The BUILTIN directive allows overriding the gate implementation with loading a built-in gate for it.

Example:

CHIP And {

  IN  a, b;
  OUT out;

  BUILTIN And;
}

In this case the "custom" And chip described in the HDL-file doesn't contain any PARTS implementation, and loads instead the built-in (canonical) And gate as a backend for its implementation.

Another example:

CHIP Mux {
  IN a, b, sel;
  OUT out;

  PARTS:

  Not(in=sel, out=nel);
  And(a=a, b=nel, out=A);
  And(a=b, b=sel, out=B);
  Or(a=A, b=B, out=out);

  BUILTIN And, Or;
}

In this case the And, and Or gates are explicitly marked as built-in, and hence will be loaded from the built-ins directory. However, the Not gate will be searched first in the local directory for the Not.hdl file (and only if it's not found, the built-in gate will be searched).

The later use case allows faster debugging, when one builds a chip, and in order to exclude potential issue in some custom gate implementation (e.g. And.hdl), they choose to specify it as a built-in. Once the code is debugged, a developer can switched again to the custom And.hdl, removing And from BUILTIN.

CLI: implement --run command

The --run (-r) command can be useful for "oscillating" (sequential/clocked) chips. When passing --exec-on-data, instead of showing the whole output table, we can show only one row at a (clock) time -- this will make it easier to observe changing over time values (e.g. changing values and control bits of the Bit, Register, or PC gates).

Implementation detail:

  • Show one row at a time (erasing the current row in the terminal, and showing a new row on the same line)
  • Run it in a 1 second timeout (emulating the clock-rate)

[Emulator] Implement ALU chip

This issue is extracted from #3.

  • Built-in implementation in JS (ALU.js) (8905bbb)

ALU design will affect the resulting format of the CPU instructions. Initially we can build the ALU from the nand2tetris course (named as ALU). The spec is as follows:

/**
 * The ALU (Arithmetic Logic Unit).
 * Computes one of the following functions:
 * x+y, x-y, y-x, 0, 1, -1, x, y, -x, -y, !x, !y,
 * x+1, y+1, x-1, y-1, x&y, x|y on two 16-bit inputs,
 * according to 6 input bits denoted zx,nx,zy,ny,f,no.
 * In addition, the ALU computes two 1-bit outputs:
 * if the ALU output == 0, zr is set to 1; otherwise zr is set to 0;
 * if the ALU output < 0, ng is set to 1; otherwise ng is set to 0.
 */

// Implementation: the ALU logic manipulates the x and y inputs
// and operates on the resulting values, as follows:
// if (zx == 1) set x = 0        // 16-bit constant
// if (nx == 1) set x = !x       // bitwise not
// if (zy == 1) set y = 0        // 16-bit constant
// if (ny == 1) set y = !y       // bitwise not
// if (f == 1)  set out = x + y  // integer 2's complement addition
// if (f == 0)  set out = x & y  // bitwise and
// if (no == 1) set out = !out   // bitwise not
// if (out == 0) set zr = 1
// if (out < 0) set ng = 1

CHIP ALUN2T {
    IN
        x[16], y[16],  // 16-bit inputs
        zx, // zero the x input?
        nx, // negate the x input?
        zy, // zero the y input?
        ny, // negate the y input?
        f,  // compute out = x + y (if 1) or x & y (if 0)
        no; // negate the out output?

    OUT
        out[16], // 16-bit output
        zr, // 1 if (out == 0), 0 otherwise
        ng; // 1 if (out < 0),  0 otherwise

  BUIlTIN ALU;
}

The implementation ALUN2T.js should be in the directory of all other builtin gates.

See also #21 for another ALU based on MIPS.

Parser: support bus ranges

To make a sub-bus, one could assign: Add16(a[0...7]=lsb, ...). The parser needs to support a[0...7] ranges. See also #1.

Table printer: implement output data format (binary, hex, decimal)

Currently we show in the tables data only in binary format, e.g. 1111111111111111:

./bin/hdl-js -g And16 -d

We should implement --format (-f) CLI option (and passing format parameter to printTruthTable or to the TablePrinter instance), and output data in either binary, hex (FFFF), or decimal (-1 for signed, 65535 for unsigned) formats.

Emulator: use single Pin class, merge PinBus into Pin

Currently we use Pin for single "wire" pin (with values 0, and 1), and PinBus for multiple wires in a bus, to handle sized values (such as 0101, etc).

We can treat Pin as special bus with size 1, and use the single Pin class, which handles generic buses, of sizes from 1 to 16.

// Default size 1, 'a'
new Pin({name: 'a'});

// Size 1, 'a'
new Pin({name: 'a', size: 1});

// Size 16, 'a[16]'
new Pin({name: 'a', size: 16});

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.