UNPKG

reqack

Version:

elastic logic circuit tools

632 lines (562 loc) 21.1 kB
'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'); };