├── .gitignore
├── screenshot.png
├── README.md
├── lib
├── index.js
├── to-string2_16.js
├── expr.js
├── simplify.js
├── construct.js
├── is-xor4.js
├── d.js
├── single.js
├── fpga-gates.js
├── lut.js
├── lut4cache.js
└── fpga.js
├── demo
├── svg.js
├── dropzone.js
├── icedrom.js
└── arizona.js
├── .travis.yml
├── LICENSE
├── package.json
└── test
└── lut4.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .nyc_output
2 | node_modules
3 | package-lock.json
4 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drom/icedrom/HEAD/screenshot.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/drom/icedrom)
2 | # icedrom
3 |
4 | FPGA content visualized
5 |
6 | 
7 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fpga = require('./fpga');
4 | var lut = require('./lut');
5 | var pkg = require('../package.json');
6 |
7 | exports.fpga = fpga;
8 | exports.lut = lut;
9 | exports.version = pkg.version;
10 |
--------------------------------------------------------------------------------
/demo/svg.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function svg (body) {
4 | var opt = body[1];
5 | return ['svg', {
6 | xmlns: 'http://www.w3.org/2000/svg',
7 | width: opt.w || 512,
8 | height: opt.h || 512,
9 | viewBox: [0, 0, opt.w || 512, opt.h || 512].join(' ')
10 | }, body ];
11 | }
12 |
13 | module.exports = svg;
14 |
--------------------------------------------------------------------------------
/lib/to-string2_16.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function toString2_16 (num) {
4 | var res = '0000000000000000';
5 | if (typeof num === 'string') {
6 | num = parseInt(num, 10);
7 | }
8 | res += num.toString(2);
9 | res = res.slice(-16);
10 | res = res.slice(0,4) +
11 | '_' + res.slice(4,8) +
12 | '_' + res.slice(8,12) +
13 | '_' + res.slice(12,16);
14 | return res;
15 | }
16 |
17 | module.exports = toString2_16;
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | os:
2 | - linux
3 | - osx
4 | sudo: false
5 | env:
6 | matrix:
7 | - TRAVIS_NODE_VERSION="13"
8 | - TRAVIS_NODE_VERSION="12"
9 | - TRAVIS_NODE_VERSION="10"
10 | - TRAVIS_NODE_VERSION="8"
11 | install:
12 | - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION
13 | - npm --version
14 | - npm install
15 | script: npm test
16 |
--------------------------------------------------------------------------------
/lib/expr.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function expr (desc, first, last) {
4 | var keys = Object.keys(desc);
5 | var res = keys.map(function (key) {
6 | return '(' +
7 | desc[key].map(function (sig) {
8 | if (typeof sig === 'string') {
9 | return sig;
10 | } else {
11 | return sig.join('');
12 | }
13 | }).join(first) +
14 | ')';
15 | });
16 | return res.join(last);
17 | }
18 |
19 | module.exports = expr;
20 |
--------------------------------------------------------------------------------
/lib/simplify.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function simplify (data, terms) {
4 | var masks = Object.keys(terms);
5 |
6 | while (masks.some(function (mask, idx) {
7 | var sum = 0;
8 | masks.forEach(function (m1, i) {
9 | if (i !== idx) {
10 | sum = sum | m1;
11 | }
12 | });
13 | if (sum == data) {
14 | // console.log('removed: ' + mask);
15 | masks.splice(idx, 1);
16 | delete terms[mask];
17 | return true;
18 | }
19 | }));
20 | return terms;
21 | }
22 |
23 | module.exports = simplify;
24 |
--------------------------------------------------------------------------------
/lib/construct.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function construct (data, groups) {
4 | var res = {};
5 | var tmp = 0;
6 | groups.some(function (g) {
7 | return Object.keys(g).some(function (key) {
8 | if ((data & key) == key) {
9 | if ((tmp | key) != tmp) {
10 | res[key] = g[key];
11 | tmp = tmp | key;
12 | if (tmp == data) {
13 | return true;
14 | }
15 | }
16 | }
17 | });
18 | });
19 | return res;
20 | }
21 |
22 | module.exports = construct;
23 |
--------------------------------------------------------------------------------
/demo/dropzone.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var tspan = require('tspan');
4 |
5 | function dropzone (label) {
6 | var res = ['g', { w: 640, h: 128, draggable: true },
7 | ['rect', {
8 | x: 0, y: 0,
9 | width: 640, height: 128,
10 | stroke: '#000',
11 | fill: 'none',
12 | 'stroke-width': '1px'
13 | }]
14 | ];
15 | label.forEach(function (t, y) {
16 | var ts = tspan.parse(t);
17 | res.push(
18 | ['text', { x: 320, y: 20 * y, 'text-anchor': 'middle'}]
19 | .concat(ts)
20 | );
21 | });
22 | return res;
23 |
24 | }
25 |
26 | module.exports = dropzone;
27 |
--------------------------------------------------------------------------------
/lib/is-xor4.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function isXor4 (data) {
4 | var i0s = [0x0000, 0x5555, 0xaaaa],
5 | i1s = [0x0000, 0x3333, 0xcccc],
6 | i2s = [0x0000, 0x0f0f, 0xf0f0],
7 | i3s = [0x0000, 0x00ff, 0xff00];
8 |
9 | var i0, i1, i2, i3;
10 | for (i0 = 0; i0 < 3; i0++) {
11 | for (i1 = 0; i1 < 3; i1++) {
12 | for (i2 = 0; i2 < 3; i2++) {
13 | for (i3 = 0; i3 < 3; i3++) {
14 | if ((i0s[i0] ^ i1s[i1] ^ i2s[i2] ^ i3s[i3]) === data) {
15 | return [i0, i1, i2, i3];
16 | }
17 | }
18 | }
19 | }
20 | }
21 | }
22 |
23 | module.exports = isXor4;
24 |
--------------------------------------------------------------------------------
/demo/icedrom.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var stringify = require('onml/lib/stringify'),
4 | arizona = require('./arizona'),
5 | dropzone = require('./dropzone'),
6 | lib = require('../lib'),
7 | svg = require('./svg');
8 |
9 | var icedrom = document.getElementById('icedrom');
10 |
11 | icedrom.innerHTML = stringify(svg(dropzone([
12 | '',
13 | 'yosys -q -p "synth_ice40 -blif $PRJ.blif" $PRJ.v',
14 | 'arachne-pnr $PRJ.blif -d 8k -P tq144:4k --post-place-blif $PRJ.post.blif',
15 | 'yosys -q -o $PRJ.post.json $PRJ.post.blif',
16 | '',
17 | 'Drop $PRJ.post.json here'
18 | ])));
19 |
20 | arizona()
21 | .listen(icedrom)
22 | .onfile(function (data, name) {
23 | var mySVG = svg(
24 | lib.fpga({
25 | fname: name,
26 | body: JSON.parse(data)
27 | })
28 | );
29 | icedrom.innerHTML = stringify(mySVG);
30 | });
31 |
--------------------------------------------------------------------------------
/lib/d.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | origin: 'M32.5,16.5',
5 |
6 | mux01: 'h-4 z m-4,0 v-8 z m0,-8 h-6 z m-6,4 l-6,4 v-16 l6,4 z m10,8',
7 | mux02: 'h-4 z m-4,0 v-4 z m0,-4 h-6 z m-6,8 l-6,4 v-24 l6,4 z m10,-4',
8 |
9 | and10: 'm-16,-10 h5 c6,0 11,4 11,10 0,6 -5,10 -11,10 h-5 z',
10 | and8: 'm-12,-8 h4 c4,0 8,4 8,8 0,4 -4,8 -8,8 h-5 z',
11 | and6: 'q0 6,-8 6 v-12 q8 0,8 6 z',
12 |
13 | or10: 'm-18,-10' +
14 | 'h4 c6,0 12,5 14,10 -2,5 -8,10 -14,10 h-4' +
15 | 'c2.5,-5 2.5,-15 0,-20 z',
16 |
17 | or6: 'q-2 6,-8 6 q2 -6,0 -12 q6 0,8 6 z ',
18 |
19 | xor10: 'm-21,-10 c1,3 2,6 2,10 0,4 -1,7 -2,10' +
20 | 'm3,-20' +
21 | 'h4 c6,0 12,5 14,10 -2,5 -8,10 -14,10 h-4' +
22 | 'c1,-3 2,-6 2,-10 0,-4 -1,-7 -2,-10 z',
23 |
24 | xor6: 'q-2 6,-6 6 q2 -6,0 -12 q4 0,6 6 z m-9 -6 q2 6,0 12 z m9 6',
25 |
26 | circle: 'm4,0 c0 1.1,-0.9 2,-2 2 s -2 -0.9,-2 -2 s 0.9 -2,2 -2 s 2 0.9,2 2 z',
27 | buf: 'l-12,8 v-16 z'
28 | };
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016-2020 Aliaksei Chapyzhenka
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "icedrom",
3 | "version": "0.1.0",
4 | "description": "FPGA schematic rendering",
5 | "main": "lib/",
6 | "scripts": {
7 | "test": "eslint lib test && nyc mocha test",
8 | "build": "browserify demo/icedrom.js > build/app.js",
9 | "unpkg": "browserify --standalone icedrom lib/index.js > build/icedrom.js",
10 | "prepublish": "npm run test && mkdir -p build && npm run unpkg"
11 | },
12 | "files": [
13 | "build/icedrom.js",
14 | "lib/*"
15 | ],
16 | "unpkg": "build/icedrom.js",
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/drom/icedrom.git"
20 | },
21 | "keywords": [
22 | "FPGA"
23 | ],
24 | "author": "Aliaksei Chapyzhenka",
25 | "license": "MIT",
26 | "bugs": {
27 | "url": "https://github.com/drom/icedrom/issues"
28 | },
29 | "homepage": "https://github.com/drom/icedrom#readme",
30 | "devDependencies": {
31 | "@drom/eslint-config": "^0.10.0",
32 | "browserify": "^16.5.0",
33 | "chai": "^4.2.0",
34 | "eslint": "^6.8.0",
35 | "nyc": "^15.0.0",
36 | "esprima": "^4.0.1",
37 | "mocha": "^7.1.0",
38 | "onml": "^1.2.0",
39 | "tspan": "^0.3.6"
40 | },
41 | "eslintConfig": {
42 | "extends": "@drom/eslint-config/eslint4/node4"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/lib/single.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var d = require('./d'),
4 | expr = require('./expr');
5 |
6 | var wires = {
7 | i0: 'M8.5 4.5 v12 h24',
8 | i1: 'M8.5 12.5 v4 h24',
9 | i2: 'M8.5 20.5 v-4 h24',
10 | i3: 'M8.5 28.5 v-12 h24'
11 | };
12 |
13 | function constant (val) {
14 | return ['text', {
15 | x: 24, y: 20,
16 | 'text-anchor': 'middle'
17 | }, val];
18 | }
19 |
20 | function single (desc) {
21 | var term, res;
22 | var keys = Object.keys(desc);
23 |
24 | if (keys.length === 0) {
25 | return constant(0);
26 | }
27 |
28 | if (keys.length === 1) {
29 | term = desc[keys[0]];
30 |
31 | if (typeof term === 'string') { // TODO check for value?
32 | return constant(1);
33 | } else
34 |
35 | if (term.length === 1) {
36 | res = ['g', { class: 'gate' },
37 | ['title', {}, expr(desc, ' & ', ' | ')],
38 | ['path', {
39 | d: wires[
40 | (typeof term[0] === 'string') ?
41 | term[0] :
42 | term[0][1]
43 | ] || '',
44 | fill: 'none'
45 | }]
46 | ];
47 | if (typeof term[0] !== 'string') {
48 | res.push(['path', {
49 | d: 'M24.5 16.5' + d.buf + d.circle
50 | }]);
51 | } else {
52 | res.push(['rect', {
53 | x: 8, y: 0, width: 24, height: 32,
54 | 'fill-opacity': 0.1, stroke: 'none'
55 | }]);
56 | }
57 | return res;
58 | }
59 | }
60 | }
61 |
62 | module.exports = single;
63 |
--------------------------------------------------------------------------------
/test/lut4.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var lut4 = require('../lib/lut'),
4 | lut4cache = require('../lib/lut4cache'),
5 | fs = require('fs'),
6 | svg = require('../demo/svg'),
7 | onml = require('onml');
8 |
9 | function toString2_16 (num) {
10 | var res = '0000000000000000';
11 | res += num.toString(2);
12 | res = res.slice(-16);
13 | res = res.slice(0,4) +
14 | '_' + res.slice(4,8) +
15 | '_' + res.slice(8,12) +
16 | '_' + res.slice(12,16);
17 | return res;
18 | }
19 |
20 | var inits = [
21 | 0, // 0
22 | 0xffff, // 1
23 | 0xaaaa, // i3
24 | 0x5555, // ~i3
25 | 0xcccc, // i2
26 | 0x3333, // ~i2
27 | 0xf0f0, // i1
28 | 0x0f0f, // ~i1
29 | 0xff00, // i0
30 | 0x00ff, // ~i0
31 |
32 | 0x8000, // and4
33 | 0xfffe, // or4
34 |
35 | 0x8080, // and3
36 | 0xfefe, // or3
37 |
38 | 1, // nor4 TODO
39 | 2,
40 | 3,
41 | 4,
42 | 5,
43 | 6,
44 | 7,
45 | 8,
46 | 4096, //
47 | 112,
48 | 32423,
49 | 234,
50 | 143, // 0, 1, 2
51 |
52 | 27030, // XOR
53 |
54 | 0x4eee, // 0b0100 1110 1100 1100,
55 | 63743,
56 | 61168,
57 | 15530,
58 | 0xcaca, // mux
59 | 0x4f4f,
60 | 43808,
61 | 52426,
62 | 0xf800
63 | ];
64 |
65 | describe('lut4', function () {
66 | it('inits', function (done) {
67 | var lut = lut4();
68 | var res = ['g', {
69 | w: 320,
70 | h: 32 * inits.length
71 | },
72 | ['style', { type: 'text/css' },
73 | ''
80 | ]
81 | ];
82 | var cache = lut4cache();
83 | inits.forEach(function (data, i) {
84 | if (cache[data] === undefined) {
85 | cache[data] = lut(data);
86 | }
87 | res.push(['g', {
88 | transform: 'translate(32,' + (32 * i) + ')'
89 | },
90 | cache[data],
91 | ['text', { x: 96, y: 24 }, toString2_16(data)]
92 | ]);
93 | });
94 | fs.writeFile('test.svg', onml.s(svg(res)), done);
95 | });
96 | });
97 |
98 | /* eslint no-console:1 */
99 | /* eslint-env mocha */
100 |
--------------------------------------------------------------------------------
/lib/fpga-gates.js:
--------------------------------------------------------------------------------
1 |
2 | 'use strict';
3 | var d = require('./d'),
4 | expr = require('./expr'),
5 | single = require('./single');
6 |
7 | var pinInvert = {
8 | i0: ' M8.5 4.5h4' + d.circle,
9 | i1: ' M8.5 12.5h4' + d.circle,
10 | i2: ' M8.5 20.5h4' + d.circle,
11 | i3: ' M8.5 28.5h4' + d.circle
12 | };
13 |
14 | var pin = {
15 | i0: ' M8.5 4.5h8v2',
16 | i1: ' M8.5 12.5h8',
17 | i2: ' M8.5 20.5h8',
18 | i3: ' M8.5 28.5h8v-2'
19 | };
20 |
21 | function inverters (term) {
22 | var res = '';
23 | if (typeof term === 'object') {
24 | term.forEach(function (inp) {
25 | if (typeof inp === 'string') {
26 | if (pin[inp]) {
27 | res += pin[inp];
28 | }
29 | } else {
30 | if (pinInvert[inp[1]]) {
31 | res += pinInvert[inp[1]];
32 | }
33 | }
34 | });
35 | }
36 | return res;
37 | }
38 |
39 | function and (desc) {
40 | var terms = Object.keys(desc);
41 | if (terms.length === 1) {
42 | return ['path', {
43 | d: 'M32.5 16.5' + d.and10 + inverters(desc[terms[0]]),
44 | class: 'gate'
45 | },
46 | ['title', {}, expr(desc, ' & ', ' | ')]
47 | ];
48 | }
49 | }
50 |
51 | function or (desc) {
52 | var terms = Object.keys(desc);
53 | if (terms.length === 1) {
54 | return ['path', {
55 | d: 'M32.5 16.5' + d.or10 + inverters(desc[terms[0]]),
56 | class: 'gate'
57 | },
58 | ['title', {}, expr(desc, ' | ', ' & ')]
59 | ];
60 | }
61 | }
62 |
63 | function xorer (desc) {
64 | // console.log(desc);
65 | var dd = 'M32.5 16.5' + d.xor10;
66 | desc.forEach(function (e, i) {
67 | if (e === 1) {
68 | dd += pin['i' + i];
69 | } else
70 | if (e === 2) {
71 | dd += pinInvert['i' + i];
72 | }
73 | });
74 | return ['path', { d: dd, class: 'gate' },
75 | ['title', {}, desc.join(',')]
76 | ];
77 | }
78 |
79 | function blackbox (sumOfProducts, productOfSums) {
80 | return ['rect', {
81 | x: 8.5, y: 2.5, width: 20, height: 28,
82 | class: 'bbox'
83 | }, ['title', {},
84 | expr(sumOfProducts, ' & ', ' | ') +
85 | ' \n ' +
86 | expr(productOfSums, ' | ', ' & ')
87 | ]
88 | ];
89 | }
90 |
91 | function gates () {
92 | return {
93 | and: and,
94 | or: or,
95 | xorer: xorer,
96 | single: single,
97 | blackbox: blackbox
98 | };
99 | }
100 |
101 | module.exports = gates;
102 |
--------------------------------------------------------------------------------
/demo/arizona.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function arizona () {
4 |
5 | var onLoadCall;
6 |
7 | function onLoad (f) {
8 | var myReader = new FileReader();
9 | 'onabort onerror onload onloadstart onloadend onloadprogress'
10 | .split(' ')
11 | .forEach(function (e) {
12 | myReader[e] = function (p) {
13 | console.log(p);
14 | if (p.type === 'loadend') {
15 | onLoadCall(p.currentTarget.result, f.name);
16 | }
17 | };
18 | });
19 | console.log(myReader.readAsText(f));
20 | }
21 |
22 | var lis = {
23 | dragover: function (event) { event.preventDefault(); },
24 | dragenter: null,
25 | dragleave: null,
26 | drop: function(event) {
27 | var i;
28 | event.preventDefault();
29 | var dt = event.dataTransfer;
30 | if (dt.items) {
31 | // Use DataTransferItemList interface to access the file(s)
32 | for (i = 0; i < dt.items.length; i++) {
33 | if (dt.items[i].kind === 'file') {
34 | var f = dt.items[i].getAsFile();
35 | console.log('[1] file[' + i + '].name = ' + f.name);
36 | onLoad(f);
37 | }
38 | }
39 | } else {
40 | // Use DataTransfer interface to access the file(s)
41 | for (i = 0; i < dt.files.length; i++) {
42 | console.log('[2] file[' + i + '].name=' + dt.files[i].name);
43 | console.log(dt.files[i]);
44 | onLoad(dt.files[i]);
45 | }
46 | }
47 | }
48 | };
49 |
50 | var self = {
51 | listen: function (el) {
52 |
53 | var inp = document.createElement('input');
54 | inp.setAttribute('type', 'file');
55 | el.insertBefore(inp, el.childNodes[0]);
56 | // el.appendChild(inp);
57 | inp.addEventListener('change', function () {
58 | console.log(onLoad(inp.files[0]));
59 | }, false);
60 |
61 | Object.keys(lis).forEach(function (key) {
62 | var cb;
63 | if (typeof lis[key] === 'function') {
64 | cb = function (event) {
65 | lis[key](event);
66 | console.log(key);
67 | };
68 | } else {
69 | cb = function (/*event*/) {
70 | console.log(key);
71 | };
72 | }
73 | // document.addEventListener(key, cb, false);
74 | el.addEventListener(key, cb, false);
75 | });
76 |
77 | return self;
78 | },
79 | onfile: function (cb) {
80 | onLoadCall = cb;
81 | return self;
82 | }
83 | };
84 | return self;
85 | }
86 |
87 | module.exports = arizona;
88 | /* eslint no-console:0 */
89 |
--------------------------------------------------------------------------------
/lib/lut.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var gates = require('./fpga-gates')(),
4 | simplify = require('./simplify'),
5 | construct = require('./construct'),
6 | isXor4 = require('./is-xor4');
7 |
8 | function toString2_16 (data) {
9 | return (
10 | '0000000000000000' +
11 | parseInt(data, 10).toString(2)
12 | ).slice(-16);
13 | }
14 |
15 | function invertInputs (desc) {
16 | var res = {};
17 | Object.keys(desc).forEach(function (key) {
18 | res[key] = desc[key].map(function (term) {
19 | if (typeof term === 'string') {
20 | return ['~', term];
21 | } else {
22 | return term[1];
23 | }
24 | });
25 | });
26 | return res;
27 | }
28 |
29 | function lut4 () {
30 |
31 | var g0 = {
32 | 0xffff: '1'
33 | };
34 |
35 | var singles = [
36 | [0xaaaa, 'i0'],
37 | [0xcccc, 'i1'],
38 | [0xf0f0, 'i2'],
39 | [0xff00, 'i3']
40 | // [0xff00, 'C'],
41 | // [0xf0f0, 'D'],
42 | // [0xcccc, 'A'],
43 | // [0xaaaa, 'B']
44 | ];
45 |
46 | var arr = [];
47 | singles.forEach(function (e) {
48 | arr.push(e);
49 | arr.push([~e[0] & 0xffff, ['~', e[1]]]);
50 | });
51 |
52 | var g1 = {};
53 | arr.forEach(function (a) {
54 | var idx = a[0];
55 | if (idx && !g1[idx]) {
56 | g1[idx] = [a[1]];
57 | }
58 | });
59 |
60 | var g2 = {};
61 | arr.forEach(function (a) {
62 | arr.forEach(function (b) {
63 | var idx = a[0] & b[0];
64 | if (idx && !g1[idx] && !g2[idx]) {
65 | g2[idx] = [a[1], b[1]];
66 | }
67 | });
68 | });
69 |
70 | var g3 = {};
71 | arr.forEach(function (a) {
72 | arr.forEach(function (b) {
73 | arr.forEach(function (c) {
74 | var idx = a[0] & b[0] & c[0];
75 | if (idx && !g1[idx] && !g2[idx] && !g3[idx]) {
76 | g3[idx] = [a[1], b[1], c[1]];
77 | }
78 | });
79 | });
80 | });
81 |
82 | var g4 = {};
83 | arr.forEach(function (a) {
84 | arr.forEach(function (b) {
85 | arr.forEach(function (c) {
86 | arr.forEach(function (d) {
87 | var idx = a[0] & b[0] & c[0] & d[0];
88 | if (idx && !g1[idx] && !g2[idx] && !g3[idx] && !g4[idx]) {
89 | g4[idx] = [a[1], b[1], c[1], d[1]];
90 | }
91 | });
92 | });
93 | });
94 | });
95 | var groups = [g0, g1, g2, g3, g4];
96 |
97 | return function (data) {
98 | console.log(toString2_16(data));
99 |
100 | var sumOfProducts = construct(data, groups);
101 | sumOfProducts = simplify(data, sumOfProducts);
102 | console.log(JSON.stringify(sumOfProducts, null, 4));
103 |
104 | var resSingle = gates.single(sumOfProducts);
105 | if (resSingle) {
106 | return resSingle;
107 | }
108 |
109 | var resAnd = gates.and(sumOfProducts);
110 | if (resAnd) {
111 | return resAnd;
112 | }
113 |
114 | var productOfSums = construct(~data & 0xffff, groups);
115 | productOfSums = simplify(~data & 0xffff, productOfSums);
116 | productOfSums = invertInputs(productOfSums);
117 | console.log(JSON.stringify(productOfSums, null, 4));
118 |
119 | var resOr = gates.or(productOfSums);
120 | if (resOr) {
121 | return resOr;
122 | }
123 |
124 | var xorer = isXor4(data);
125 | if (xorer) {
126 | return gates.xorer(xorer);
127 | }
128 |
129 | console.log('BLACKBOX!');
130 |
131 | return gates.blackbox(sumOfProducts, productOfSums);
132 | };
133 | }
134 |
135 | module.exports = lut4;
136 | /* eslint no-console:1 */
137 |
--------------------------------------------------------------------------------
/lib/lut4cache.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var d = require('./d');
4 |
5 | // var xor = 'm -21,-10 c1,3 2,6 2,10 m0,0 c0,4 -1,7 -2,10 m3,-20 4,0 c6,0 12,5 14,10 -2,5 -8,10 -14,10 l-4,0 c1,-3 2,-6 2,-10 0,-4 -1,-7 -2,-10 z';
6 | // var circle = ' M 4,0 C 4,1.1 3.1,2 2,2 0.9,2 0,1.1 0,0 c 0,-1.1 0.9,-2 2,-2 1.1,0 2,0.9 2,2 z';
7 | // var circle = 'm 4 0 c 0 1.1,-0.9 2,-2 2 s -2 -0.9,-2 -2 s 0.9 -2,2 -2 s 2 0.9,2 2 z';
8 | // var and = 'm -16,-10 5,0 c 6,0 11,4 11,10 0,6 -5,10 -11,10 l -5,0 z';
9 | // var buf = 'l-12,8 v-16 z';
10 | // var or = 'm -18,-10 4,0 c 6,0 12,5 14,10 -2,5 -8,10 -14,10 l -4,0 c 2.5,-5 2.5,-15 0,-20 z';
11 | //
12 | // var inputs = {
13 | // i0t1: 'M0 4 h12 v4 h4',
14 | //
15 | // i1t1: 'M0 12 h4 v-4 h12',
16 | // i1t2: 'M0 12 h16',
17 | //
18 | // i2t1: 'M0 20 h4 v-12 h12',
19 | // i2t3: 'M0 20 h8 v-4 h8',
20 | //
21 | // i3t3: 'M0 28 h12 v-12 h4',
22 | // i3t4: 'M0 28 h12 v-8 h4'
23 | // };
24 | //
25 | // function group (body) {
26 | // var res = ['g', { transform: 'translate(0.5,0.5)' }];
27 | // body.forEach(function (e) {
28 | // res.push(e);
29 | // });
30 | // return res;
31 | // }
32 |
33 | function lut4cache () {
34 |
35 | var cache = {};
36 |
37 | cache[0xcaca] = ['path', {
38 | d: d.origin +
39 | 'm-24 -12 h8 z m24 12' +
40 | 'm-24 -4 h8 z m24 4 ' +
41 | 'm-24 4 h10 z m10 0 v-5 z m14 -4 ' +
42 | d.mux01,
43 | class: 'gate'
44 | },
45 | ['title', {}, 'i2 ? i1 : i0']
46 | ];
47 |
48 | cache[0x3caa] = ['path', {
49 | d: d.origin +
50 | 'm-24 -12 h8 z m24 12' +
51 | 'm-24 12 h10 z m10 0 v-5 z m14 -12' +
52 | d.mux02 +
53 | 'm-16,0' + d.xor6,
54 | class: 'gate'
55 | },
56 | ['title', {}, 'i3 ? (i1 ^ i2) : i0']
57 | ];
58 |
59 | cache[0xccca] = ['path', {
60 | d: d.origin +
61 | 'm-24 -12 h8 z m24 12' +
62 | 'm-24 -4 h8 z m24 4 ' +
63 | 'm-16 8 h2 z m2 0 v-9 z m14 -8' +
64 | d.mux01 +
65 | 'm-16 4' + d.or6,
66 | class: 'gate'
67 | },
68 | ['title', {}, '(i2 | i3) ? i1 : i0']
69 | ];
70 |
71 | cache[0xf800] = ['path', {
72 | d: d.origin +
73 | 'm-24 4 h4 z m4 0 v-4 z m0 -4 h4 z m20 0' +
74 | 'm-24 12 h12 z m12 0 v-8 z m0 -8 h4 z m12 -4' +
75 | d.and6 + ' m-8 -4' + d.or6 + 'm-8 -4' + d.and6,
76 | class: 'gate'
77 | },
78 | ['title', {}, '((i0 & i1) | i2) & i3']
79 | ];
80 |
81 |
82 | // cache[0xffff] = group([
83 | // ['text', { x: 24, y: 20, 'text-anchor': 'middle' }, '1']
84 | // ]);
85 | //
86 | // cache[0x0ff0] = group([
87 | // ['path', {
88 | // d: inputs.i2t1 + inputs.i3t3,
89 | // fill: 'none', stroke: '#000'
90 | // }],
91 | // ['path', {
92 | // d: 'M32 16' + xor,
93 | // fill: '#ffb', stroke: '#000'
94 | // }]
95 | // ]);
96 | //
97 | // cache[0x3c3c] = group([
98 | // ['path', {
99 | // d: inputs.i1t1 + inputs.i2t3,
100 | // fill: 'none', stroke: '#000'
101 | // }],
102 | // ['path', {
103 | // d: 'M32 16' + xor,
104 | // fill: '#ffb', stroke: '#000'
105 | // }]
106 | // ]);
107 | //
108 | // cache[0x5555] = group([
109 | // ['path', {
110 | // d: 'M0,4 h12 v8 h12',
111 | // fill: 'none', stroke: '#000'
112 | // }],
113 | // ['path', {
114 | // d: 'M32 16' + buf + circle,
115 | // fill: '#ffb', stroke: '#000'
116 | // }]
117 | // ]);
118 | //
119 | // cache[0xaaaa] = group([
120 | // ['path', {
121 | // d: 'M0,4 h12 v8 h12',
122 | // fill: 'none', stroke: '#000'
123 | // }],
124 | // ['path', {
125 | // d: 'M32 16' + buf,
126 | // fill: '#ffb', stroke: '#000'
127 | // }]
128 | // ]);
129 | //
130 | // var arr16 = Array.apply(null, Array(16));
131 | //
132 | // arr16.forEach(function (e, i) {
133 | // var tt = 1 << i;
134 | // var res = ['g', {},
135 | // ['path', { d: 'M32 16' + and, fill: '#ffb', stroke: '#000' },
136 | // ['title', {}, tt]
137 | // ]
138 | // ];
139 | // [1, 2, 4, 8].forEach(function (mask, maski) {
140 | // if (!(i & mask)) {
141 | // res.push(['path', {
142 | // d: 'M12 ' + (4 + 8 * maski) + circle,
143 | // fill: '#ffb', stroke: '#000'
144 | // }]);
145 | // }
146 | // });
147 | // cache[tt] = res;
148 | // });
149 | //
150 | // arr16.forEach(function (e, i) {
151 | // var tt = 0xffff ^ (1 << i);
152 | // var res = ['g', {},
153 | // ['path', { d: 'M32 16' + or, fill: '#ffb', stroke: '#000' },
154 | // ['title', {}, tt]
155 | // ]
156 | // ];
157 | // [1, 2, 4, 8].forEach(function (mask, maski) {
158 | // if (!(i & mask)) {
159 | // res.push(['path', {
160 | // d: 'M12 ' + (4 + 8 * maski) + circle,
161 | // fill: '#ffb', stroke: '#000'
162 | // }]);
163 | // }
164 | // });
165 | // cache[tt] = res;
166 | // });
167 |
168 | return cache;
169 | }
170 |
171 | module.exports = lut4cache;
172 |
--------------------------------------------------------------------------------
/lib/fpga.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var lut4 = require('./lut')();
4 | var lut4cache = require('./lut4cache')();
5 | var toString2_16 = require('./to-string2_16');
6 | var lut4count = {};
7 | var blackboxes = {};
8 |
9 | function lutSimplify (data, connections) {
10 | var mask0 = [
11 | 0x5555,
12 | 0x3333,
13 | 0x0f0f,
14 | 0x00ff
15 | ];
16 | var shift = [
17 | 1,
18 | 2,
19 | 4,
20 | 8
21 | ];
22 |
23 | 'I0 I1 I2 I3'.split(' ').forEach(function (inp, idx) {
24 | if (connections[inp][0] === '0') {
25 | // console.log(toString2_16(data), idx);
26 | data = (data & mask0[idx]) |
27 | ((data & mask0[idx]) << shift[idx]);
28 | // console.log(toString2_16(data));
29 | }
30 | });
31 | return data;
32 | }
33 |
34 | function runningSum (sum, e, idx, arr) {
35 | sum += e;
36 | arr[idx] = sum;
37 | return sum;
38 | }
39 |
40 | function fpga (params) {
41 | var modules = params.body.modules;
42 | var moduleNames = Object.keys(modules);
43 | var cells = modules[moduleNames[0]].cells;
44 |
45 | var res = ['g', {},
46 | ['style', { type: 'text/css' },
47 | ''
54 | ]
55 | ];
56 |
57 | var colV = Array.apply(null, Array(34)).map(function () { return 1; });
58 | var rowV = Array.apply(null, Array(34)).map(function () { return 1; });
59 |
60 | Object.keys(cells).forEach(function (key) {
61 | var cell = cells[key];
62 | var loc1 = cell.attributes.loc.split(',');
63 | var col = parseInt(loc1[0], 10);
64 | var loc2 = loc1[1].split('/');
65 | var row = parseInt(loc2[0]);
66 | // var lc = parseInt(loc2[1]);
67 | colV[col] = 4;
68 | rowV[row] = 16;
69 | });
70 |
71 | colV.reduce(runningSum, 0);
72 | rowV.reduce(runningSum, 0);
73 |
74 | colV.forEach(function (col, x) {
75 | rowV.forEach(function (row, y) {
76 | res.push(['rect', {
77 | x: 16 * col,
78 | y: 16 * row,
79 | width: 16,
80 | height: 16,
81 | stroke: 'none',
82 | opacity: 0.2,
83 | fill: ((x + y) & 1) ? '#fff' : '#000'
84 | }, ['title', {}, ((x + 1) + ',' + (y + 1))]]);
85 | });
86 | });
87 |
88 | var drivers = {};
89 |
90 | Object.keys(cells).forEach(function (key) {
91 | var cell = cells[key];
92 | var loc1 = cell.attributes.loc.split(',');
93 | var col = parseInt(loc1[0], 10);
94 | var loc2 = loc1[1].split('/');
95 | var row = parseInt(loc2[0]);
96 | var lc = parseInt(loc2[1]);
97 | var connections;
98 | var connectionNames;
99 |
100 | if (cell.connections) {
101 | connections = cell.connections;
102 | connectionNames = Object.keys(connections);
103 | '^O-48-16 ^COUT-4-32 ^D_IN_0-48-16 ^D_IN_1-48-24 ^RDATA-48-16 ^GLOBAL_BUFFER_OUTPUT-48-16'
104 | .split(' ')
105 | .map(function (e) {
106 | var arr = e.split('-');
107 | return { name: arr[0], x: parseInt(arr[1], 10), y: parseInt(arr[2], 10) };
108 | })
109 | .forEach(function (pin) {
110 | var driver;
111 | var x, y;
112 | connectionNames.forEach(function (name) {
113 | if (
114 | name.match(pin.name) &&
115 | connections[name].length
116 | ) {
117 | x = 16 * (colV[col] - 4) + pin.x;
118 | y = 16 * (rowV[row] - 16) + 32 * lc + pin.y;
119 | driver = connections[name][0];
120 | drivers[driver] = { x: x, y: y };
121 | }
122 | });
123 | });
124 | }
125 | });
126 |
127 | // console.log(drivers);
128 |
129 | Object.keys(cells).forEach(function (key) {
130 | var cell = cells[key];
131 | var loc1 = cell.attributes.loc.split(',');
132 | var col = parseInt(loc1[0], 10);
133 | var loc2 = loc1[1].split('/');
134 | var row = parseInt(loc2[0]);
135 | var lc = parseInt(loc2[1]);
136 | var connections;
137 | var connectionNames;
138 |
139 | var wireGroup = ['g', {
140 | transform: 'translate(0.5,0.5)',
141 | 'stroke-linecap': 'round',
142 | fill: 'none', stroke: '#777'
143 | }];
144 |
145 | if (cell.connections) {
146 | connections = cell.connections;
147 | connectionNames = Object.keys(connections);
148 | '^I0-0-4 ^I1-0-12 ^I2-0-20 ^I3-0-28 ^D_OUT_0-0-16 ^D_OUT_1-0-24 ^WDATA-0-16 ^WADDR-0-64 ^RADDR-0-128 ^WE-0-192 ^RE-0-208 ^WCLKE-0-224 ^RCLKE-0-240 ^USER_SIGNAL_TO_GLOBAL_BUFFER-0-16'
149 | .split(' ')
150 | .map(function (e) {
151 | var arr = e.split('-');
152 | return { name: arr[0], x: parseInt(arr[1], 10), y: parseInt(arr[2], 10) };
153 | })
154 | .forEach(function (pin) {
155 | var x, y;
156 | connectionNames.forEach(function (name) {
157 | if (
158 | name.match(pin.name) &&
159 | connections[name].length &&
160 | (typeof connections[name][0] === 'number')
161 | ) {
162 | var wireNumber = connections[name][0];
163 | x = 16 * (colV[col] - 4) + pin.x;
164 | y = (16 * (rowV[row] - 16) + (lc * 32)) + pin.y;
165 | var groupO = ['path', {
166 | d: 'M' + drivers[wireNumber].x +
167 | ' ' + drivers[wireNumber].y +
168 | ' L ' + x + ' ' + y
169 | }];
170 | wireGroup.push(groupO);
171 | }
172 | });
173 | });
174 |
175 | res.push(wireGroup);
176 | }
177 | });
178 |
179 |
180 | var LUT_INIT;
181 |
182 | Object.keys(cells).forEach(function (key) {
183 | var cell = cells[key];
184 | var loc1 = cell.attributes.loc.split(',');
185 | var col = parseInt(loc1[0], 10);
186 | var loc2 = loc1[1].split('/');
187 | var row = parseInt(loc2[0]);
188 | var lc = parseInt(loc2[1]);
189 | var group = ['g', {
190 | transform: 'translate(' +
191 | (16 * (colV[col] - 4)) + ',' +
192 | (16 * (rowV[row] - 16) + (lc * 32)) + ')'
193 | }];
194 | res.push(group);
195 |
196 | if (cell.type === 'SB_GB') {
197 | group.push(['rect', {
198 | x: 2, y: 2, width: 44, height: 28,
199 | stroke: 'none', fill: '#f51'
200 | }, ['title', {}, cell.attributes.loc]
201 | ]);
202 | }
203 |
204 | if (cell.type === 'SB_IO') {
205 | group.push(['g', {},
206 | ['rect', {
207 | x: 2, y: 2, width: 44, height: 28,
208 | stroke: 'none', fill: '#1e5'
209 | },
210 | ['title', {}, cell.attributes.loc]
211 | ],
212 | ['text', { x: 24, y: 20, 'text-anchor': 'middle'}, cell.connections.PACKAGE_PIN[0]]
213 | ]);
214 | }
215 |
216 | if (cell.type === 'SB_RAM40_4K') {
217 | group.push(['rect', {
218 | x: 2, y: 2, width: 44, height: 252,
219 | stroke: 'none', fill: '#d3d'
220 | }, ['title', {}, cell.attributes.loc]
221 | ]);
222 | }
223 |
224 | if (cell.type === 'ICESTORM_LC') {
225 | 'I0 I1 I2 I3'
226 | .split(' ')
227 | .forEach(function (pin, pindx) {
228 | if (typeof cell.connections[pin][0] === 'number') {
229 | group.push(['path', {
230 | d: 'M0.5 ' + (4.5 + 8 * pindx) + ' h8',
231 | fill: 'none', stroke: '#000'
232 | }]);
233 | }
234 | });
235 |
236 | group.push(['path', {
237 | d: 'M28.5 16.5 h20',
238 | fill: 'none', stroke: '#000'
239 | }]);
240 |
241 | if (cell.parameters.CARRY_ENABLE) {
242 | group.push(['rect', {
243 | x: -4, y: 24,
244 | width: 8, height: 8,
245 | stroke: 'none', fill: '#333'
246 | }]);
247 | }
248 |
249 | LUT_INIT = cell.parameters.LUT_INIT;
250 | if (LUT_INIT) {
251 | LUT_INIT = lutSimplify(LUT_INIT, cell.connections);
252 | console.log(LUT_INIT);
253 | if (lut4cache[LUT_INIT] === undefined) {
254 | lut4cache[LUT_INIT] = lut4(LUT_INIT);
255 | if (lut4cache[LUT_INIT][0] === 'rect') {
256 | blackboxes[LUT_INIT] = true;
257 | }
258 | }
259 | if (lut4count[LUT_INIT] === undefined) {
260 | lut4count[LUT_INIT] = 1;
261 | } else {
262 | lut4count[LUT_INIT] += 1;
263 | }
264 | group.push(lut4cache[LUT_INIT]);
265 | }
266 |
267 | if (cell.parameters.DFF_ENABLE) {
268 | group.push(['rect', {
269 | x: 34, y: 2, width: 12, height: 28,
270 | stroke: 'none', fill: '#15f'
271 | }]);
272 | }
273 |
274 | }
275 |
276 | });
277 |
278 | res[1] = { w: 16 * colV[colV.length - 1], h: 16 * rowV[rowV.length - 1] };
279 |
280 | Object.keys(blackboxes).forEach(function (key) {
281 | if (lut4count[key] > 0) {
282 | console.log(key + ' = ' + toString2_16(key) + ': ' + lut4count[key]);
283 | }
284 | });
285 |
286 | return res;
287 | }
288 |
289 | module.exports = fpga;
290 | /* eslint no-console:1 */
291 |
--------------------------------------------------------------------------------