reqack
Version:
elastic logic circuit tools
1,606 lines (1,408 loc) • 957 kB
JavaScript
(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]) {