UNPKG

reqack

Version:

elastic logic circuit tools

1,606 lines (1,408 loc) 957 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.reqack = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ 'use strict'; const data = p => ` // edge:${p.id} EB1.5 wire en${p.id}_0, en${p.id}_1, sel${p.id}; reg [${p.width - 1}:0] dat${p.id}_r0, dat${p.id}_r1; always @(posedge clk) if (en${p.id}_0) dat${p.id}_r0 <= ${p.t.data}; always @(posedge clk) if (en${p.id}_1) dat${p.id}_r1 <= ${p.t.data}; assign ${p.i.data} = sel${p.id} ? dat${p.id}_r1 : dat${p.id}_r0; `; const ctrl = p => ` // edge:${p.id} EB1.5 wire ${p.i.ready}, ${p.i.valid}; eb15_ctrl uctrl_${p.id} ( .t_0_req(${p.t.valid}), .t_0_ack(${p.t.ready}), .i_0_req(${p.i.valid}), .i_0_ack(${p.i.ready}), .en0(en${p.id}_0), .en1(en${p.id}_1), .sel(sel${p.id}), .clk(clk), .reset_n(reset_n) ); `; module.exports = { ctrl: ctrl, data: data, ctrl2data: p => { let res = {}; res[`en${p.id}_0`] = 1; res[`en${p.id}_1`] = 1; res[`sel${p.id}`] = 1; return res; } }; },{}],2:[function(require,module,exports){ 'use strict'; const data = p => ` // edge:${p.id} EB1.7 wire en${p.id}_0, en${p.id}_1, sel${p.id}; reg [${p.width - 1}:0] dat${p.id}_r0, dat${p.id}_r1; wire [${p.width - 1}:0] dat${p.id}_r0_nxt; assign dat${p.id}_r0_nxt = sel${p.id} ? dat${p.id}_r1 : ${p.t.data}; always @(posedge clk) if (en${p.id}_0) dat${p.id}_r0 <= dat${p.id}_r0_nxt; always @(posedge clk) if (en${p.id}_1) dat${p.id}_r1 <= ${p.t.data}; assign dat${p.id} = dat${p.id}_r0; `; const ctrl = p => ` // edge:${p.id} EB1.7 wire ${p.i.ready}, ${p.i.valid}; eb17_ctrl uctrl_${p.id} ( .t_0_req(${p.t.valid}), .t_0_ack(${p.t.ready}), .i_0_req(${p.i.valid}), .i_0_ack(${p.i.ready}), .en0(en${p.id}_0), .en1(en${p.id}_1), .sel(sel${p.id}), .clk(clk), .reset_n(reset_n) ); `; module.exports = { ctrl: ctrl, data: data, ctrl2data: p => { let res = {}; res[`en${p.id}_0`] = 1; res[`en${p.id}_1`] = 1; res[`sel${p.id}`] = 1; return res; } }; },{}],3:[function(require,module,exports){ 'use strict'; const data = p => { const depthlog2 = Math.ceil(Math.log2(p.capacity)); return ` // edge:${p.id} EB_FIFO Depth=${p.capacity} reg [${p.width - 1}:0] mem${p.id} [${p.capacity - 1}:0]; reg [${p.width - 1}:0] dat${p.id}_r0; wire ren${p.id}, wen${p.id}; wire [${depthlog2 - 1}:0] wr_ptr${p.id}, rd_ptr${p.id}; always @(posedge clk) if (ren${p.id} && wen${p.id} && (wr_ptr${p.id} == rd_ptr${p.id})) dat${p.id}_r0 <= ${p.t.data}; else if(ren${p.id}) dat${p.id}_r0 <= mem${p.id}[rd_ptr${p.id}]; always @(posedge clk) if (wen${p.id}) mem${p.id}[wr_ptr${p.id}] <= ${p.t.data}; assign ${p.i.data} = dat${p.id}_r0; `; }; const ctrl = p => { const depthlog2 = Math.ceil(Math.log2(p.capacity)); return ` // edge:${p.id} EB_FIFO wire ${p.i.ready}, ${p.i.valid}; eb_fifo_ctrl #( .DEPTHMO(${depthlog2}'d${p.capacity - 1}), .DEPTHLOG2MO(${depthlog2 - 1}) ) uctrl_${p.id} ( .t_0_req(${p.t.valid}), .t_0_ack(${p.t.ready}), .i_0_req(${p.i.valid}), .i_0_ack(${p.i.ready}), .wr_ptr(wr_ptr${p.id}), .rd_ptr(rd_ptr${p.id}), .wen(wen${p.id}), .ren(ren${p.id}), .clk(clk), .reset_n(reset_n) ); `; }; module.exports = { ctrl: ctrl, data: data, ctrl2data: p => { const ptrWidth = Math.ceil(Math.log2(p.capacity)); let res = {}; res[`wr_ptr${p.id}`] = ptrWidth; res[`rd_ptr${p.id}`] = ptrWidth; res[`wen${p.id}`] = 1; res[`ren${p.id}`] = 1; return res; } }; },{}],4:[function(require,module,exports){ 'use strict'; module.exports = arr => arr.map(e => e + ';').join('\n'); },{}],5:[function(require,module,exports){ 'use strict'; const dagre = require('dagre'); const stringify = require('onml/lib/stringify.js'); const getNodeType = require('./get-node-type.js'); const margin = 4; const sNode = {w: 8, h: 20}; // width per character const sSock = {w: 32, h: 16}; const sBuffer = {w: 32, h: 8}; const isTarget = n => n.from.length === 0; const isInitiator = n => n.to.length === 0; const t = (x, y) => ({transform: 'translate(' + (x || 0) + ',' + (y || 0) + ')'}); const findGlobalIndexOfEdge = (g, edge) => { let index; g.edges.some((e, ei) => { if (edge === e) { index = ei; return true; } }); return index; }; const ci2g = (ci, opt) => { const topDown = opt.topDown || false; // else leftRight const g = new dagre.graphlib.Graph(); g.setGraph({rankdir: topDown ? 'TD' : 'LR', nodesep: 4, ranksep: 32}); // Default to assigning a new object as a label for each new edge. g.setDefaultEdgeLabel(function() { return {}; }); ci.edges.map((e, ei) => { const label = e.label || {}; const eWidth = (label.width || '').toString(); const capacity = (label.capacity || '').toString(); if (e.label && e.label.capacity > 0) { g.setNode('edge:' + ei, { type: 'buffer', label: 'edge:' + ei, width: topDown ? 24 : 0, height: topDown ? 0 : 24, eWidth: eWidth, capacity: capacity }); } else { g.setNode('edge:' + ei, { type: 'wire', label: 'edge:' + ei, width: topDown ? 24 : 0, // 20 * eWidth.length + 8, height: topDown ? 0 : 24, eWidth: eWidth }); } }); ci.nodes.map((n, ni) => { if (isTarget(n)) { g.setNode('node:' + ni, { label: n.label || ni, width: (getNodeType(n).length + 1) * sNode.w, // sSock.w, height: sSock.h, type: 'target' }); } else if (isInitiator(n)) { g.setNode('node:' + ni, { label: n.label || ni, width: (getNodeType(n).length + 1) * sNode.w, // sSock.w , height: sSock.h, type: 'initiator' }); } else { g.setNode('node:' + ni, { label: ni.toString(), width: (getNodeType(n).length + 1) * sNode.w, height: sNode.h, type: getNodeType(n) }); } n.to.map(e => { g.setEdge('node:' + ni, 'edge:' + findGlobalIndexOfEdge(ci, e)); }); n.from.map(e => { g.setEdge('edge:' + findGlobalIndexOfEdge(ci, e), 'node:' + ni); }); }); dagre.layout(g); return g; }; const render = (pg, opt) => { const topDown = opt.topDown || false; // else leftRight const w = pg.graph().width + 2 * margin + 1; const h = pg.graph().height + 2 * margin + 1; return ['svg', { xmlns: 'http://www.w3.org/2000/svg', width: w, height: h // viewBox: [0, 0, w, h].join(' ') }, ['style', {}, ` text { fill: black; stroke: none; font-family: Roboto,Helvetica; font-size: 11pt; } .center { alignment-baseline: middle; text-anchor: middle; } `], ['g', t(margin + 0.5, margin + 0.5)] .concat(pg.edges().map((e) => { const desc = pg.edge(e); return ['g', { fill: 'none', stroke: 'black' }, ['path', { d: 'M' + desc.points.map(point => point.x + ' ' + point.y ).join('L') }]]; })) .concat(pg.nodes().map((n) => { const desc = pg.node(n); if (desc.type === 'wire') { return ['g', t(desc.x, desc.y), ['circle', {r: 3, fill: '#000'}], // ['rect', { // x: -desc.width / 2, width: desc.width, // y: -desc.height / 2, height: desc.height, // fill: 'hsla(120, 0%, 50%, 0.05)' // }], ['text', {'text-anchor': 'end', x: -2, y: -2}, desc.eWidth] ]; } if (desc.type === 'buffer') { return ['g', t(desc.x, desc.y), ['rect', topDown ? { x: -sBuffer.w / 2, width: sBuffer.w, y: -sBuffer.h, height: sBuffer.h, fill: '#ddd', stroke: 'black' } : { x: -sBuffer.h, width: sBuffer.h, y: -sBuffer.w / 2, height: sBuffer.w, fill: '#ddd', stroke: 'black' }], ['text', topDown ? { x: -2, y: -(sBuffer.h + 2), 'text-anchor': 'end' } : { x: -(sBuffer.h + 2), y: -2, 'text-anchor': 'end' }, desc.eWidth], ['text', topDown ? { x: 2, y: -(sBuffer.h + 2), 'text-anchor': 'start' } : { x: -(sBuffer.h + 2), y: 2, 'text-anchor': 'end', 'alignment-baseline': 'hanging' }, desc.capacity] ]; } if (desc.type === 'target' || desc.type === 'initiator') { return ['g', //t(desc.x - (desc.width >> 1), desc.y - 8), ['path', { d: `M${desc.x - (desc.width >> 1)} ${desc.y - 8}l${desc.width - 8} 0l8 8l-8 8l-${desc.width - 8} 0z`, fill: '#fff', stroke: '#000' }], ['text', {class: 'center', x: desc.x, y: desc.y}, desc.label] ]; } return ['g', t(desc.x, desc.y), ['rect', { x: -desc.width / 2, y: -desc.height / 2, width: desc.width, height: desc.height, rx: Math.min(desc.width, desc.height) / 2, ry: Math.min(desc.width, desc.height) / 2, fill: '#fff', stroke: '#000' }], ['text', {class: 'center'}, desc.type] ]; })) ]; }; module.exports = (ci, opt) => { opt = opt || {}; return stringify( render( ci2g(ci, opt), opt ), 2 ); }; },{"./get-node-type.js":11,"dagre":20,"onml/lib/stringify.js":318}],6:[function(require,module,exports){ 'use strict'; function nId (i) { return 'n' + i.toString(); } function eId (i) { return 'e' + i.toString(); } function edgeWidth (e) { if (e.label) { switch (typeof e.label) { case 'number': return e.label; case 'object': if (typeof e.label.width === 'number') { return e.label.width; } } } } module.exports = function (g) { let res = [ 'digraph g {', 'graph [fontname=helvetica margin=0.02 width=0 height=0];', 'node [fontname=helvetica margin=0.04 width=0 height=0];', 'edge [fontname=helvetica margin=0.02 width=0 height=0];' ]; const nodes = {}; const edges = {}; res = res.concat(g.nodes.map(function (n, i) { const key = nId(i); nodes[key] = n; const l = n.label; return key + ((l === undefined) ? '' : ' [label="' + l + '"];'); })); const nodeKeys = Object.keys(nodes); res = res.concat(g.edges.map(function (e, i) { const key = eId(i); edges[key] = e; const w = edgeWidth(e); let shape = 'none'; if (e.label.capacity === 1) { shape = 'box'; } else if (e.label.capacity > 1) { shape = 'box3d'; } let label = w || ''; label += (e.label.capacity > 1) ? (',C:' + e.label.capacity) : ''; label = (label === '') ? '' : '; label="' + label + '"'; return key + ' [shape=' + shape + label + '];'; })); const edgeKeys = Object.keys(edges); res = res.concat(g.nodes.map(function (n, i) { const nKey = nId(i); const res = []; n.to.forEach(function (e, ni) { edgeKeys.some(function (eKey) { if (edges[eKey] === e) { const label = 'label="' + ni + '"'; const taillabel = e.taillabel ? ' taillabel="' + e.taillabel + '"' : ''; res.push(nKey + ' -> ' + eKey + ' [' + label + taillabel + '];'); e.targets.forEach(function (nn) { nodeKeys.some(function (nnKey) { if (nodes[nnKey] === nn.node) { const label1 = 'label="' + nn.index + '"'; const headlabel = nn.headlabel ? ' headlabel="' + nn.headlabel + '"' : ''; res.push(' ' + eKey + ' -> ' + nnKey + ' [' + label1 + headlabel + '];'); return true; } }); }); return true; } }); }); return res.join('\n'); })); res.push('}'); return res.join('\n'); }; /* eslint no-console: 1 */ },{}],7:[function(require,module,exports){ 'use strict'; function gLabel (g) { return g.label || 'g'; } const descriptor = (ni, preffix, root, width) => ({ data: preffix + root + '_dat', valid: preffix + root + '_req', ready: preffix + root + '_ack', width: width, length: 16 }); module.exports = function (g, name) { const targets = g.nodes.reduce((res, n, ni) => { const root = n.label || ni; return res.concat((n.from.length === 0) ? [ descriptor(ni, 't_', root, n.to[0].label.width) ] : []); }, []); const initiators = g.nodes.reduce((res, n, ni) => { const root = n.label || ni; return res.concat((n.to.length === 0) ? [ descriptor(ni, 'i_', root, n.from[0].label.width) ] : []); }, []); const obj = { top: gLabel(g), topFile: name + '.v', clk: 'clk', 'reset_n': 'reset_n', targets: targets, initiators: initiators }; return 'module.exports = ' + JSON.stringify(obj, null, 4) + ';'; }; },{}],8:[function(require,module,exports){ 'use strict'; // const operators = require('./operators.js'); const rpn = require('./rpn.js'); const macroVerilog = require('./macro-verilog.js'); const vectorDim = require('./vector-dim.js'); const mimo = require('./mimo.js'); const getNodeType = require('./get-node-type.js'); const indent = ' '; // const enPrefix = id => 'en' + id; const datPrefix = id => 'dat' + id; const reqPrefix = id => 'req' + id; const ackPrefix = id => 'ack' + id; const datSuffix = id => id + '_dat'; const reqSuffix = id => id + '_req'; const ackSuffix = id => id + '_ack'; const assign = (lhs, rhs) => ['assign ' + lhs + ' = ' + rhs + ';']; const instantiation = (p) => { const head = p.modName + ' ' + p.instName + ' (\n'; const body = p.bindings.map(bind => indent + '.' + bind[0] + '(' + bind[1] + ')' ).join(',\n'); const foot = ');\n'; return head + body + foot; }; // function edgeIndex (g, edge) { // let index; // g.edges.some((e, i) => { // if (edge === e) { // index = i; // return true; // } // }); // return index; // } function gLabel (g) { return g.label || 'g'; } function vport (desc) { return Object.keys(desc).map((key, i, arr) => { const val = desc[key]; const type = (val < 0) ? 'output ' : 'input '; const comma = (i === (arr.length - 1)) ? '' : ','; return indent + type + vectorDim(val) + key + comma; }); } function vlogic (desc) { return Object.keys(desc).map(key => { const val = desc[key]; const type = 'wire '; return type + vectorDim(val) + key + ';'; }); } function wires (desc) { return Object.keys(desc).map(key => 'wire ' + vectorDim(key) + desc[key].join(', ') + ';'); } function edgeWidth (e) { if (e.label) { switch (typeof e.label) { case 'number': return e.label; case 'object': if (typeof e.label.width === 'number') { return e.label.width; } } } } function pbind (desc) { return Object.keys(desc).map(function (key, i, arr) { const comma = (i === (arr.length - 1)) ? '' : ','; const val = (typeof desc[key] === 'number') ? key : desc[key]; return ' .' + key + '(' + val + ')' + comma; }); } const ioReducer = (g, ocb) => g.nodes.reduce((res, n, ni) => { const nname = n.label || ni; if (n.from.length === 0) { // target node const ewidth = edgeWidth(n.to[0]); return ocb.target(res, nname, ewidth); } if (n.to.length === 0) { // initiator node const ewidth = edgeWidth(n.from[0]); return ocb.initiator(res, nname, ewidth); } return res; }, {clk: 1, 'reset_n': 1}); const ctrlInstance = (g, macros) => { const glabel = gLabel(g); const io = ioReducer(g, { target: (res, nname) => { res[reqSuffix('t_' + nname)] = 1; res[ackSuffix('t_' + nname)] = 1; return res; }, initiator: (res, nname) => { res[reqSuffix('i_' + nname)] = 1; res[ackSuffix('i_' + nname)] = 1; return res; } }); g.edges.reduce((eres, e, ei) => Object.assign( eres, macroVerilog.eb.ctrl2data({ capacity: e.label.capacity, id: ei }) ), io); g.nodes.reduce((nres, n, ni) => { const label = n.label; if (label && macros[label] && macros[label].ctrl2data) { const ctrl2data = macros[label].ctrl2data(getNodeSockets(g, n, ni)); ctrl2data.map(sig => { nres[sig[1]] = sig[1]; }); } return nres; }, io); return [glabel + '_ctrl uctrl ('] .concat(pbind(io)) .concat([');']); }; function dport (g) { return vport(ioReducer(g, { target: (res, nname, ewidth) => { res[datSuffix('t_' + nname)] = ewidth; res[reqSuffix('t_' + nname)] = 1; res[ackSuffix('t_' + nname)] = -1; return res; }, initiator: (res, nname, ewidth) => { res[datSuffix('i_' + nname)] = -ewidth; res[reqSuffix('i_' + nname)] = -1; res[ackSuffix('i_' + nname)] = 1; return res; } })); } const cForks = g => g.edges.reduce((res, e, ei) => ( res .concat(macroVerilog.eb.ctrl({ id: ei, capacity: e.label.capacity, t: { valid: 'req' + ei, ready: 'ack' + ei }, i: { valid: 'req' + ei + 'm', ready: 'ack' + ei + 'm' } })) .concat(macroVerilog.fork.ctrl({id: ei, targets: e.targets})) ), []); const cTargets = g => g.nodes.reduce((res, n, ni) => { const nname = 't_' + (n.label || ni); return res.concat((n.from.length === 0) ? ( ['// node:' + nname + ' target'] .concat(n.to.reduce((eres, e) => { const idx = findGlobalIndexOfEdge(g, e); return eres .concat(assign(reqPrefix(idx), reqSuffix(nname))) .concat(assign(ackSuffix(nname), ackPrefix(idx))); }, [])) ) : []); }, []); const cInitiators = g => g.nodes.reduce((res, n, ni) => { const nname = 'i_' + (n.label || ni); return res.concat((n.to.length === 0) ? ( ['// node:' + ni + ' initiator'] .concat(n.from.reduce((eres, efrom) => { const idx = findGlobalIndexOfEdge(g, efrom); const nindex = findIndexOfNode(g, efrom.targets, n); return eres .concat(assign( reqSuffix(nname), reqPrefix(idx + '_' + nindex) )) .concat(assign( ackPrefix(idx + '_' + nindex), ackSuffix(nname) )); }, [])) ) : []); }, []); function findIndexOfNode (g, nodes, node) { let index; nodes.some((n, ni) => { if (node === n.node) { index = ni; return true; } }); return index; } const perTargetLink = (g, n) => (e, ei) => { const tindex = findIndexOfNode(g, e.targets, n); const target = e.targets[tindex] || {}; const pname = target.headlabel || ei; return Object.assign({ port: 't_' + datSuffix(pname), wire: datPrefix(findGlobalIndexOfEdge(g, e)) // width: e.label.width }, e.label); }; const perInitiatorLink = g => (e, ei) => (Object.assign({ port: 'i_' + datSuffix(e.taillabel || ei), wire: datPrefix(findGlobalIndexOfEdge(g, e) + (e.label.capacity ? '_nxt' : '')) // width: e.label.width }, e.label)); const getNodeSockets = (g, n, ni) => ({ t: n.from.map(perTargetLink(g, n)), i: n.to.map(perInitiatorLink(g)), id: ni }); // function findGlobalIndexOfNode (g, node) { // let index; // g.nodes.some((n, ni) => { // if (node === n) { // index = ni; // return true; // } // }); // return index; // } function findGlobalIndexOfEdge (g, edge) { let index; g.edges.some((e, ei) => { if (edge === e) { index = ei; return true; } }); return index; } const clogic = g => wires({1: g.edges.reduce((eres, e, ei) => { eres = eres.concat([ reqPrefix(ei), ackPrefix(ei) ]); e.targets.forEach((t, ti) => { eres = eres.concat([ ackPrefix(ei + '_' + ti), reqPrefix(ei + '_' + ti) ]); }); return eres; }, [])}); const dlogic = g => wires(g.edges.reduce((eres, e, ei) => { let ename = datPrefix(ei); const w = e.label.width; const res = eres[w] || []; eres[w] = res.concat((e.label.capacity >= 1) ? [ename, ename + '_nxt'] : [ename]); return eres; }, {})); const dff = g => ['// per edge'] .concat( g.edges.reduce((eres, e, ei) => ( eres.concat([macroVerilog.eb.data({ id: ei, capacity: e.label.capacity, width: e.label.width, t: {data: 'dat' + ei + '_nxt'}, i: {data: 'dat' + ei} })]) ), []) ); module.exports = function (g, macros) { // data const perTargetPort = (n, ni) => { const e = n.to[0]; const ename = datPrefix(findGlobalIndexOfEdge(g, e) + (e.label.capacity ? '_nxt' : '')); const nname = datSuffix(n.label || ni); return assign(ename, 't_' + nname) + ` // node:${ni} is target port`; }; const perInitiatorPort = (n, ni) => { const e = n.from[0]; const ename = datPrefix(findGlobalIndexOfEdge(g, e)); const nname = datSuffix(n.label || ni); return assign('i_' + nname, ename) + ` // node:${ni} is initiator port`; }; const perConcatNode = (n, ni) => { const lhs = n.to .map(e => datPrefix(findGlobalIndexOfEdge(g, e) + (e.label.capacity ? '_nxt' : ''))) .reverse() .join(', '); const rhs = n.from .map(e => datPrefix(findGlobalIndexOfEdge(g, e))) .reverse() .join(', '); return assign('{' + lhs + '}', '{' + rhs + '}') + ` // node:${ni} { }`; }; const perOperatorNode = (n, ni, label) => { const statements = n.to.map(e => { const ename = datPrefix(findGlobalIndexOfEdge(g, e) + (e.label.capacity ? '_nxt' : '')); const expr = rpn(label, n.from.map(te => ({ name: datPrefix(findGlobalIndexOfEdge(g, te)), width: te.label.width }))); return assign(ename, expr); }); return `${statements.join('\n')} // node:${ni} operator ${label}`; }; const standardDataPathInstance = (label, macros) => { const descriptor = macros[label] || {}; if (descriptor.data) { return descriptor.data; } const ctrl2data = descriptor.ctrl2data || (() => []); const parameters = descriptor.parameters; return p => { const paramInterface = parameters ? ( ' #(\n' + Object.keys(parameters).map(param => indent + '.' + param + '(NODE' + p.id + '_' + param + ')') .join(',\n') + '\n)' ) : ''; const ctrl2dataWires = ctrl2data(p); const extra = Object.keys(ctrl2dataWires).map(sig => [ctrl2dataWires[sig][0], ctrl2dataWires[sig][1]]); const localLogic = ctrl2dataWires.reduce((res, sig) => { res[sig[1]] = sig[2]; return res; }, {}); const targets = p.t.map(b => [b.port, b.wire]); const initiators = p.i.map(b => [b.port, b.wire]); const ports = targets .concat(initiators) .concat(extra) .concat([['clk', 'clk'], ['reset_n', 'reset_n']]); return ( vlogic(localLogic).join('\n') + '\n' + datSuffix(label) + '_' + targets.length + '_' + initiators.length + paramInterface + ' unode' + p.id + ' (\n' + ports.map(pair => ` .${pair[0]}(${pair[1]})`).join(',\n') + '\n);' ); }; }; const perMacroNode = (n, ni, label) => { const dataPathGen = standardDataPathInstance(label, macros); const insert = dataPathGen(getNodeSockets(g, n, ni)); const inserta = (Array.isArray(insert)) ? insert.join('\n') : insert.toString(); return `// node:${ni} macro ${label} ${inserta}`; }; const perNode = (n, ni) => { const label = n.label; if (n.from.length === 0) { return perTargetPort(n, ni); } if (n.to.length === 0) { return perInitiatorPort(n, ni); } if (label && macros[label]) { return perMacroNode(n, ni, label); } if (typeof label === 'string') { return perOperatorNode(n, ni, label); } // if (label && operators[label]) { // return perOperatorNode(n, ni, label); // } // if (n.to.length === 1 && n.from.length === 1) { return perConcatNode(n, ni); // } // return `// node:${ni} with strange label ${label}`; }; const dcombNodes = () => ['// per node'] .concat( g.nodes.reduce((nres, n, ni) => { return nres.concat(perNode(n, ni)); }, []) ); // ctrl const cport = () => { const io = ioReducer(g, { target: (res, nname) => { res[reqSuffix('t_' + nname)] = 1; res[ackSuffix('t_' + nname)] = -1; return res; }, initiator: (res, nname) => { res[reqSuffix('i_' + nname)] = -1; res[ackSuffix('i_' + nname)] = 1; return res; } }); g.edges.reduce((eres, e, ei) => { const ctrl2data = macroVerilog.eb.ctrl2data({ capacity: e.label.capacity, id: ei }); Object.keys(ctrl2data).map(key => { eres[key] = -ctrl2data[key]; }); return eres; }, io); g.nodes.reduce((nres, n, ni) => { const label = n.label; if (label && macros[label] && macros[label].ctrl2data) { const ctrl2data = macros[label].ctrl2data(getNodeSockets(g, n, ni)); ctrl2data.map(sig => { nres[sig[1]] = -sig[2]; }); } return nres; }, io); return vport(io); }; const cJoins = () => g.nodes.reduce((res, n, ni) => { if (n.from.length > 0 && n.to.length > 0) { // not IO node const label = getNodeType(n); const ml = macros[label] || {}; if (ml.ctrl) { return res.concat(ml.ctrl({ id: ni, t: n.from.map(e => findGlobalIndexOfEdge(g, e) + '_' + findIndexOfNode(g, e.targets, n) ), i: n.to.map(e => findGlobalIndexOfEdge(g, e) ) })); } if (ml.ctrl2data) { // need custom controller res = res.concat(['// node:' + ni + ' custom controller ' + label]); const bindTargets = n.from.reduce((eres, e, ei) => { const tindex = findIndexOfNode(g, e.targets, n); const target = e.targets[tindex] || {}; const pname = target.headlabel || ei; const ename = findGlobalIndexOfEdge(g, e) + '_' + 0; return eres.concat([ [reqSuffix('t_' + pname), reqPrefix(ename)], [ackSuffix('t_' + pname), ackPrefix(ename)] ]); }, []); const bindInitiators = n.to.reduce((eres, e, ei) => eres.concat([ [reqSuffix('i_' + (e.taillabel || ei)), reqPrefix(findGlobalIndexOfEdge(g, e))], [ackSuffix('i_' + (e.taillabel || ei)), ackPrefix(findGlobalIndexOfEdge(g, e))] ]), []); // t: n.from.map(e => ({ // name: datPrefix(findGlobalIndexOfEdge(g, e)) // })), // i: n.to.map(e => ({ // name: datPrefix(findGlobalIndexOfEdge(g, e) + (e.label.capacity ? '_nxt' : '')) // })), const extra = (ml.ctrl2data || (() => []))(getNodeSockets(g, n, ni)); return res.concat(instantiation({ modName: label + '_ctrl_' + n.from.length + '_' + n.to.length, instName: 'unode' + ni, bindings: bindTargets .concat(bindInitiators) .concat(extra) .concat([ ['clk', 'clk'], ['reset_n', 'reset_n'] ]) })); // return res.concat(macros[label].ctrl({ // id: ni // })); } res = res.concat(['// node:' + ni + ' join ' + label]); res = res.concat(mimo({ t: n.from.map(efrom => { const i = findGlobalIndexOfEdge(g, efrom); const j = findIndexOfNode(g, efrom.targets, n); return { req: reqPrefix(i + '_' + j), ack: ackPrefix(i + '_' + j) }; }), i: n.to.map(eto => { const i = findGlobalIndexOfEdge(g, eto); return { req: reqPrefix(i), ack: ackPrefix(i) }; }) })); // // req path // n.to.forEach(eto => { // res = res.concat(assign( // reqPrefix(findGlobalIndexOfEdge(g, eto)), // n.from.map(efrom => // reqPrefix( // findGlobalIndexOfEdge(g, efrom) + '_' + findIndexOfNode(g, efrom.targets, n) // ) // ).join(' & ') // )); // }); // // n.to.forEach(to => { // const iEdgeTo = edgeIndex(g, to); // n.from.forEach((from, ifrom) => { // const idFrom = edgeIndex(g, from) + '_' + findIndexOfNode(g, from.targets, n); // const rest = n.from.reduce((res2, from2, ifrom2) => { // if (ifrom !== ifrom2) { // res2 = res2.concat([ // reqPrefix(edgeIndex(g, from2) + '_' + findIndexOfNode(g, from2.targets, n)) // ]); // } // return res2; // }, [ackPrefix(iEdgeTo)]).join(' & '); // res = res.concat(assign( // ackPrefix(idFrom), // rest // )); // }); // }); } return res; }, []); function topModule () { const glabel = gLabel(g); const parameters = g.nodes.reduce((res, n, ni) => { if (macros[n.label] && macros[n.label].parameters) { const params = macros[n.label].parameters; Object.keys(params).map(param => { res = res.concat([ indent + 'parameter NODE' + ni + '_' + param + ' = ' + JSON.stringify(params[param](ni)) ]); }); } return res; }, []).join(',\n'); const paramInterface = parameters.length ? ' #(\n' + parameters + '\n)' : ''; return ['module ' + glabel + paramInterface + ' ('] .concat([indent + '// per node (target / initiator)']) .concat(dport(g)) .concat(');') .concat(dlogic(g)) // .concat(dcomb(g)) .concat(dcombNodes()) .concat(dff(g)) .concat(ctrlInstance(g, macros)) .concat('endmodule // ' + glabel + '\n'); } function ctrlModule () { const glabel = gLabel(g); return ['module ' + glabel + '_ctrl ('] .concat([indent + '// per node (target / initiator)']) .concat(cport()) .concat(');') .concat(clogic(g)) .concat(cTargets(g)) .concat(cForks(g)) .concat(cJoins()) .concat(cInitiators(g)) // .concat(cBuffers(g)); .concat('endmodule // ' + glabel + '_ctrl\n'); } return topModule() .concat(ctrlModule()) .join('\n'); }; },{"./get-node-type.js":11,"./macro-verilog.js":14,"./mimo.js":15,"./rpn.js":18,"./vector-dim.js":19}],9:[function(require,module,exports){ 'use strict'; /* Graph, Directed, Hypergraph, F-edges */ const genConnector = (gState, nState, eState) => function perTarget (nn, headlabel) { if (nn === undefined) { nn = genNode(gState)(); } const nnState = nn.state; const nnFrom = nnState.from; eState.targets.push({ node: nnState, index: nnFrom.length, headlabel: headlabel }); nnFrom.push(eState); return perTarget; }; const genEdge = (gState, nState) => (label, taillabel) => { if (label === undefined) { label = {}; } const eState = { source: { node: nState, index: nState.to.length }, targets: [], label: label, taillabel: taillabel, root: gState }; nState.to.push(eState); gState.edges.push(eState); const res = genConnector(gState, nState, eState); res.state = eState; return res; }; const genNode = gState => function (label) { const nState = { from: [], to: [], label: label, root: gState }; gState.nodes.push(nState); const res = genEdge(gState, nState); res.state = nState; const alen = arguments.length; if (alen > 1) { for (let i = 1; i < alen; i++) { const arg = arguments[i]; if (Array.isArray(arg)) { arg.map(function (subArg) { subArg(res); }); } else { arg(res); } } } return res; }; module.exports = function (label) { const gState = {nodes: [], edges: [], label: label}; const res = genNode(gState); res.nodes = gState.nodes; res.edges = gState.edges; res.label = gState.label; return res; }; },{}],10:[function(require,module,exports){ 'use strict'; const express = require('./express.js'); const forkCtrl = p => { let res = []; res = res.concat((p.targets.length > 1) ? 'reg ' + p.targets.map((e, ei) => `ack${p.id}_${ei}_r`).join(', ') : [] ); res = res.concat((p.targets.length > 1) ? 'wire ' + p.targets.map((e, ei) => `ack${p.id}_${ei}_s`).join(', ') : [] ); res = res.concat((p.targets.length > 1) ? p.targets.map((e, ei) => `assign req${p.id}_${ei} = req${p.id}m & ~ack${p.id}_${ei}_r`) : [`assign req${p.id}_0 = req${p.id}m`] ); res = res.concat((p.targets.length > 1) ? p.targets.map((e, ei) => `assign ack${p.id}_${ei}_s = ack${p.id}_${ei} | ~req${p.id}_${ei}`) : [] ); const ackm = (p.targets.length > 1) ? p.targets.map((e, ei) => `ack${p.id}_${ei}_s`).join(' & ') : `ack${p.id}_0`; res = res.concat(`assign ack${p.id}m = ${ackm}`); res = res.concat((p.targets.length > 1) ? p.targets.map((e, ei) => `always @(posedge clk or negedge reset_n) if (~reset_n) ack${p.id}_${ei}_r <= 1'b0; else ack${p.id}_${ei}_r <= ack${p.id}_${ei}_s & ~ack${p.id}m`) : [] ); return `// edge:${p.id} fork ${express(res)}`; }; module.exports = forkCtrl; },{"./express.js":4}],11:[function(require,module,exports){ 'use strict'; module.exports = n => (typeof n.label === 'object') ? n.label.name || '{ }' : (n.label || '').toString(); },{}],12:[function(require,module,exports){ 'use strict'; const esprima = require('esprima'); const escodegen = require('escodegen'); const estraverse = require('estraverse'); const fhyper = require('./fhyper'); const ops = { '+': {name: 'add', fn: (a, b) => a + b}, '-': {name: 'sub', fn: (a, b) => a - b} }; function opGen (g, lut) { const res = {}; Object.keys(lut).forEach(function (key) { const e = lut[key]; res[e.name] = function (a, b) { if (typeof a === 'number' && typeof b === 'number') { return e.fn(a, b); } const aEdge = (typeof a === 'function') ? a : g(a)({}); const bEdge = (typeof b === 'function') ? b : g(b)({}); const resNode = g(e.name); aEdge(resNode); bEdge(resNode); return resNode({}); }; }); // console.log(res); return res; } function hls (fn) { const fnText = fn.toString(); // console.log(fnText); const ast = esprima.parse(fnText); // console.log(JSON.stringify(ast, null, 4)); const newAst = estraverse.replace(ast, { leave: function (node) { if (node.type === 'BinaryExpression') { return { type: 'CallExpression', callee: { type: 'Identifier', name: ops[node.operator].name }, arguments: [node.left, node.right] }; } } }); // console.log(JSON.stringify(newAst, null, 4)); const newFnText = escodegen.generate(newAst); // console.log(newFnText); const _g = fhyper(); const {add, sub} = opGen(_g, ops); let _res; eval('_res = ' + newFnText); return _res; } module.exports = hls; /* eslint no-unused-vars: 0 */ },{"./fhyper":9,"escodegen":50,"esprima":54,"estraverse":55}],13:[function(require,module,exports){ 'use strict'; const fhyper = require('./fhyper'); const fhyperVerilog = require('./fhyper-verilog'); const fhyperDot = require('./fhyper-dot'); const fhyperDagre = require('./fhyper-dagre'); const fhyperManifest = require('./fhyper-manifest'); const nodeForkCtrl = require('./node-fork-ctrl'); const nodeMacros = require('./node-macros'); const hls = require('./hls'); const dagre = require('dagre'); exports.circuit = fhyper; exports.verilog = fhyperVerilog; exports.dot = fhyperDot; exports.svg = fhyperDagre; exports.manifest = fhyperManifest; exports.macros = nodeMacros; exports.ctrl = {fork: nodeForkCtrl}; exports.hls = hls; exports.dagre = dagre; },{"./fhyper":9,"./fhyper-dagre":5,"./fhyper-dot":6,"./fhyper-manifest":7,"./fhyper-verilog":8,"./hls":12,"./node-fork-ctrl":16,"./node-macros":17,"dagre":20}],14:[function(require,module,exports){ 'use strict'; const eb15 = require('./eb15'); const eb17 = require('./eb17'); const ebfifo = require('./ebfifo'); const forkCtrl = require('./fork-ctrl'); const fork = { ctrl: forkCtrl, data: () => '', ctrl2data: () => ({}) }; const join = { ctrl: () => '', data: () => '', ctrl2data: () => ({}) }; const eb0 = { data: () => [], ctrl: p => `// edge:${p.id} EB0 wire ${p.i.ready}, ${p.i.valid}; assign ${p.i.valid} = ${p.t.valid}; assign ${p.t.ready} = ${p.i.ready}; `, ctrl2data: () => ({}) }; const eb1 = { data: p => `wire en${p.id}; // edge:${p.id} EB1 reg [${p.width - 1}:0] ${p.i.data}_r; always @(posedge clk) if (en${p.id}) ${p.i.data}_r <= ${p.t.data}; assign ${p.i.data} = ${p.i.data}_r; `, ctrl: p => `// edge:${p.id} EB1 wire ${p.i.ready}; reg ${p.i.valid}; assign en${p.id} = ${p.t.valid} & ${p.t.ready}; assign ${p.t.ready} = ~${p.i.valid} | ${p.i.ready}; always @(posedge clk or negedge reset_n) if (~reset_n) ${p.i.valid} <= 1'b0; else ${p.i.valid} <= ~${p.t.ready} | ${p.t.valid}; `, ctrl2data: p => { let res = {}; res[`en${p.id}`] = 1; return res; } }; const eb = { ctrl: p => (p.capacity === 1) ? eb1.ctrl(p) : (p.capacity === 1.5) ? eb15.ctrl(p) : (p.capacity === 1.7) ? eb17.ctrl(p) : (p.capacity >= 2) ? ebfifo.ctrl(p) : eb0.ctrl(p), data: p => (p.capacity === 1) ? eb1.data(p) : (p.capacity === 1.5) ? eb15.data(p) : (p.capacity === 1.7) ? eb17.data(p) : (p.capacity >= 2) ? ebfifo.data(p) : eb0.data(p), ctrl2data: p => (p.capacity === 1) ? eb1.ctrl2data(p) : (p.capacity === 1.5) ? eb15.ctrl2data(p) : (p.capacity === 1.7) ? eb17.ctrl2data(p) : (p.capacity >= 2) ? ebfifo.ctrl2data(p) : eb0.ctrl2data(p) }; module.exports = { fork: fork, join: join, eb0: eb0, eb1: eb1, eb15: eb15, ebfifo: ebfifo, eb: eb, ctrl: { fork: forkCtrl } }; },{"./eb15":1,"./eb17":2,"./ebfifo":3,"./fork-ctrl":10}],15:[function(require,module,exports){ 'use strict'; // Multiple Inputs Multiple Outputs (MIMO) controller const vectorDim = require('./vector-dim'); const concat = list => '{' + list.reverse().join(', ') + '}'; const op = o => nodes => nodes.join(o); const and = op(' & '); const or = op(' | '); const repeat = (val, count) => '{' + count + '{' + val + '}}'; const genDeclaration = (res, type) => list => res.push(Object.keys(list).map(dim => type + vectorDim(dim) + list[dim].join(', ') + ';')); const genBody = () => { let state = []; return { comment: function () { let res = '// '; for(let i = 0; i < arguments.length; i++) { res += arguments[i].toString(); } state.push(res); }, wire: genDeclaration(state, 'wire '), reg: genDeclaration(state, 'reg '), assign: (lhs, rhs) => state.push('assign ' + lhs + ' = ' + rhs + ';'), ff: (lhs, rhs, len) => state.push( 'always @(posedge clk or negedge reset_n) if (~reset_n) ' + lhs + ' <= ' + len + '\'b0; else ' + lhs + ' <= ' + rhs + ';' ), toString: () => state.join('\n') }; }; const mimo = u => { const tLen = u.t.length; const iLen = u.i.length; if (tLen < 1) { throw new Error('no targets sockets'); } if (iLen < 1) { throw new Error('no initiator sockets'); } const join = tLen > 1; const fork = iLen > 1; const _ = genBody(); _.comment('join:' + tLen + ', fork:' + iLen); let req = (!join && fork) ? u.t[0].req : u.i[0].req; let ack = (join && !fork) ? u.i[0].ack : u.t[0].ack; if (join && fork) { req += '_q'; ack += '_m'; _.wire({1: [req, ack]}); } if (join || !fork) { _.assign(req, and(u.t.map(e => e.req))); } if (!join && !fork) { _.assign(ack, u.i[0].ack); } if (fork) { const ackR = u.i[0].ack + '_r'; const reqC = u.i[0].req + '_c'; const ackS = u.i[0].ack + '_s'; _.reg({[iLen]: [ackR]}); _.wire({[iLen]: [reqC, ackS]}); _.assign( reqC, and([ '~' + ackR, repeat(req, iLen) ]) ); _.assign( concat(u.i.map(e => e.req)), reqC ); _.assign( ackS, or([ concat(u.i.map(e => e.ack)), '~' + reqC ]) ); _.assign(ack, '&' + ackS); _.ff( ackR, and([ ackS, '~' + repeat(ack, iLen) ]), iLen ); } if (join) u.t.map((rhs, i) => _.assign(rhs.ack, and( u.t.reduce((a, lhs, j) => a.concat((i !== j) ? [lhs.req] : []), [ack])))); return _.toString(); }; module.exports = mimo; },{"./vector-dim":19}],16:[function(require,module,exports){ 'use strict'; // fork controller generator for nodes const nodeForkCtrl = p => { // console.log(p); const t = p.t[0]; return [`// node:${p.id} fork controller`] .concat( 'reg ' + p.i.map(e => `req${e}_reg`).join(', ') ) .concat(p.i.map(e => `assign req${e} = req${t} & ~req${e}_reg` )) .concat( 'wire ' + p.i.map(e => `ack${e}_g`).join(', ') ) .concat(p.i.map(e => `assign ack${e}_g = ack${e} | ~req${e}` )) .concat(`assign ack${t} = ` + p.i.map(e => `ack${e}_g` ).join(' & ')) .concat(p.i.map(e => `always @(posedge clk or negedge reset_n) if (~reset_n) req${e}_reg <= 1'b0; else req${e}_reg <= ack${e}_g & ~ack${t}` )) .concat('') .join(';\n'); }; module.exports = nodeForkCtrl; },{}],17:[function(require,module,exports){ 'use strict'; const nodeForkCtrl = require('./node-fork-ctrl'); const deconcat = { data: p => { const width = p.i.reduce((res, sig) => (res + sig.width), 0); return `assign {${ p.i.reverse().map(sig => sig.wire).join(', ') }} = ${p.t[0].wire}[${width - 1}:0];`; }, ctrl: nodeForkCtrl }; module.exports = { deconcat: deconcat }; },{"./node-fork-ctrl":16}],18:[function(require,module,exports){ 'use strict'; const binOps = '+ - / % & | ^ << >> > >= < <= == != && ||' .split(/\s+/) .reduce((res, e) => { res[e] = e; return res; }, { 'u*' : '*' }); const signedBinOps = { '*': '*' }; const unOps = { '!': '!', '~': '~' }; const rpn = (seq, arr) => { if (typeof seq === 'string') { seq = seq.split(/\s+/); } // if (typeof arr === 'string') { // arr = arr.split(/\s+/); // } arr = arr.map(e => e.name); for (let j = 0; j < 100; j++) { for (let i = 0; i < seq.length; i++) { const cmd = seq[i]; if (unOps[cmd]) { arr.push(cmd + arr.pop()); } else if (signedBinOps[cmd]) {