507 lines
18 KiB
JavaScript
507 lines
18 KiB
JavaScript
|
/*
|
||
|
# Primary Developers
|
||
|
1) James H-J Yeh, Ph.D.
|
||
|
2) Satvik Ramaprasad
|
||
|
|
||
|
refer verilog_documentation.md
|
||
|
*/
|
||
|
|
||
|
function generateVerilog() {
|
||
|
var data = verilog.exportVerilog();
|
||
|
console.log(data);
|
||
|
download(projectName + ".v", data);
|
||
|
}
|
||
|
|
||
|
verilog = {
|
||
|
// Entry point to verilog generation
|
||
|
// scope = undefined means export all circuits
|
||
|
exportVerilog:function(scope = undefined){
|
||
|
var dependencyList = {};
|
||
|
|
||
|
// Reset Verilog Element State
|
||
|
for (var i = 0; i < circuitElementList.length; i++) {
|
||
|
if (window[circuitElementList[i]].resetVerilog) {
|
||
|
window[circuitElementList[i]].resetVerilog();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// List of devices under test for which testbench needs to be created
|
||
|
var DUTs = [];
|
||
|
var SubCircuitIds = new Set();
|
||
|
|
||
|
// Generate SubCircuit Dependency Graph
|
||
|
for (id in scopeList) {
|
||
|
dependencyList[id] = scopeList[id].getDependencies();
|
||
|
for (var i = 0; i < scopeList[id].SubCircuit.length; i++) {
|
||
|
SubCircuitIds.add(scopeList[id].SubCircuit[i].id);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for(id in scopeList) {
|
||
|
if(!SubCircuitIds.has(id))
|
||
|
DUTs.push(scopeList[id]);
|
||
|
}
|
||
|
|
||
|
// DFS on SubCircuit Dependency Graph
|
||
|
var visited = {};
|
||
|
var elementTypesUsed = {};
|
||
|
var output = "";
|
||
|
if(scope) {
|
||
|
// generate verilog only for scope
|
||
|
output += this.exportVerilogScope(scope.id,visited,dependencyList, elementTypesUsed);
|
||
|
}
|
||
|
else {
|
||
|
// generate verilog for everything
|
||
|
for (id in scopeList) {
|
||
|
output += this.exportVerilogScope(id,visited,dependencyList, elementTypesUsed);
|
||
|
}
|
||
|
}
|
||
|
// Add Circuit Element - Module Specific Verilog Code
|
||
|
for(var element in elementTypesUsed) {
|
||
|
// If element has custom verilog
|
||
|
if (window[element].moduleVerilog) {
|
||
|
output += window[element].moduleVerilog();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var report = this.generateReport(elementTypesUsed) + "\n";
|
||
|
var testbench = this.generateTestBenchCode(DUTs);
|
||
|
|
||
|
return report + testbench + output;
|
||
|
},
|
||
|
generateReport: function(elementTypesUsed) {
|
||
|
var output = "";
|
||
|
output += "/**\n"
|
||
|
output += " * This is an autogenerated netlist code from CircuitVerse. Verilog Code can be\n"
|
||
|
output += " * tested on https://www.edaplayground.com/ using Icarus Verilog 0.9.7. This is an\n"
|
||
|
output += " * experimental module and some manual changes make need to be done in order for\n"
|
||
|
output += " * this to work.\n"
|
||
|
output += " *\n"
|
||
|
output += " * If you have any ideas/suggestions or bug fixes, raise an issue\n"
|
||
|
output += " * on https://github.com/CircuitVerse/CircuitVerse/issues/new/choose\n"
|
||
|
output += " */\n"
|
||
|
output += "\n";
|
||
|
output += '/*\n';
|
||
|
output += sp(1) + "Element Usage Report\n";
|
||
|
for(var elem in elementTypesUsed) {
|
||
|
if(elem == "Node")continue;
|
||
|
output += `${sp(2)}${elem} - ${elementTypesUsed[elem]} times\n`;
|
||
|
}
|
||
|
output += '*/\n';
|
||
|
output += "\n";
|
||
|
var instructions = "";
|
||
|
output += '/*\n';
|
||
|
output += sp(1) + "Usage Instructions and Tips\n";
|
||
|
instructions += sp(2) + "Labels - Ensure unique label names and avoid using verilog keywords\n";
|
||
|
instructions += sp(2) + "Warnings - Connect all optional inputs to remove warnings\n";
|
||
|
for(var elem in elementTypesUsed) {
|
||
|
// If element has custom instructions
|
||
|
if (window[elem].verilogInstructions) {
|
||
|
instructions += indent(2, window[elem].verilogInstructions());
|
||
|
}
|
||
|
}
|
||
|
output += instructions
|
||
|
output += '*/\n';
|
||
|
return output;
|
||
|
},
|
||
|
generateTestBenchCode: function(DUTs) {
|
||
|
if(DUTs.length == 0)return "";
|
||
|
var output = "// Sample Testbench Code - Uncomment to use\n";
|
||
|
|
||
|
output += "\n/*\n";
|
||
|
output += "module TestBench();\n";
|
||
|
var registers = {};
|
||
|
var wires = {};
|
||
|
for(var i = 1; i <= 32; i++)
|
||
|
registers[i] = new Set();
|
||
|
for(var i = 1; i <= 32; i++)
|
||
|
wires[i] = new Set();
|
||
|
|
||
|
var clocks = new Set();
|
||
|
var inputs = new Set();
|
||
|
var outputs = new Set();
|
||
|
var deviceInstantiations = "";
|
||
|
for(var i = 0; i < DUTs.length; i++) {
|
||
|
var DUT = DUTs[i];
|
||
|
for(var j = 0;j < DUT.Input.length; j++){
|
||
|
var inp = DUT.Input[j];
|
||
|
registers[inp.bitWidth].add(inp.label);
|
||
|
inputs.add(inp.label);
|
||
|
}
|
||
|
for(var j = 0;j < DUT.Output.length; j++){
|
||
|
var out = DUT.Output[j];
|
||
|
wires[out.bitWidth].add(out.label);
|
||
|
outputs.add(out.label);
|
||
|
}
|
||
|
for(var j=0;j<DUT.Clock.length;j++){
|
||
|
var inp = DUT.Clock[j];
|
||
|
registers[1].add(inp.label);
|
||
|
clocks.add(inp.label);
|
||
|
}
|
||
|
var circuitName = this.sanitizeLabel(DUT.name);
|
||
|
var dutHeader = this.generateHeaderHelper(DUT);
|
||
|
deviceInstantiations += `${sp(1)}${circuitName} DUT${i}${dutHeader}\n`;
|
||
|
}
|
||
|
output += "\n"
|
||
|
// Generate Reg Initialization Code
|
||
|
for (var bitWidth = 1; bitWidth<= 32; bitWidth++){
|
||
|
if(registers[bitWidth].size == 0)
|
||
|
continue;
|
||
|
var regArray = [...registers[bitWidth]];
|
||
|
if(bitWidth == 1)
|
||
|
output += `${sp(1)}reg ${regArray.join(", ")};\n`;
|
||
|
else
|
||
|
output += `${sp(1)}reg [${bitWidth-1}:0] ${regArray.join(", ")};\n`;
|
||
|
}
|
||
|
output += "\n"
|
||
|
// Generate Wire Initialization Code
|
||
|
for (var bitWidth = 1; bitWidth<= 32; bitWidth++){
|
||
|
if(wires[bitWidth].size == 0)
|
||
|
continue;
|
||
|
var wireArray = [...wires[bitWidth]];
|
||
|
if(bitWidth == 1)
|
||
|
output += `${sp(1)}wire ${wireArray.join(", ")};\n`;
|
||
|
else
|
||
|
output += `${sp(1)}wire [${bitWidth-1}:0] ${wireArray.join(", ")};\n`;
|
||
|
}
|
||
|
output += "\n"
|
||
|
|
||
|
output += deviceInstantiations;
|
||
|
|
||
|
if(clocks.size) {
|
||
|
output += `${sp(1)}always begin\n`;
|
||
|
output += `${sp(2)}#10\n`;
|
||
|
for(var clk of clocks)
|
||
|
output += `${sp(2)}${clk} = 0;\n`;
|
||
|
output += `${sp(2)}#10\n`;
|
||
|
for(var clk of clocks)
|
||
|
output += `${sp(2)}${clk} = 1;\n`;
|
||
|
output += `${sp(1)}end\n`;
|
||
|
output += '\n';
|
||
|
}
|
||
|
|
||
|
output += `${sp(1)}initial begin\n`;
|
||
|
|
||
|
// Reset inputs to 0
|
||
|
for(var inp of inputs){
|
||
|
output += `${sp(2)}${inp} = 0;\n`;
|
||
|
}
|
||
|
output += '\n';
|
||
|
output += `${sp(2)}#15\n`;
|
||
|
for(var out of outputs) {
|
||
|
output += `${sp(2)}$display("${out} = %b", ${out});\n`;
|
||
|
}
|
||
|
output += '\n';
|
||
|
output += `${sp(2)}#10\n`;
|
||
|
for(var out of outputs) {
|
||
|
output += `${sp(2)}$display("${out} = %b", ${out});\n`;
|
||
|
}
|
||
|
output += '\n';
|
||
|
output += `${sp(2)}$finish;\n\n`;
|
||
|
output += `${sp(1)}end\n`;
|
||
|
|
||
|
output += "endmodule\n";
|
||
|
|
||
|
output += "\n*/\n";
|
||
|
|
||
|
return output;
|
||
|
},
|
||
|
// Recursive DFS function
|
||
|
exportVerilogScope: function(id,visited,dependencyList, elementTypesUsed){
|
||
|
// Already Visited
|
||
|
if (visited[id]) return "";
|
||
|
// Mark as Visited
|
||
|
visited[id] = true;
|
||
|
|
||
|
var output = "";
|
||
|
// DFS on dependencies
|
||
|
for (var i = 0; i < dependencyList[id].length; i++)
|
||
|
output += this.exportVerilogScope(dependencyList[id][i],visited,dependencyList, elementTypesUsed)+"\n";
|
||
|
|
||
|
var scope = scopeList[id];
|
||
|
// Initialize labels for all elements
|
||
|
this.resetLabels(scope);
|
||
|
this.setLabels(scope);
|
||
|
|
||
|
output += this.generateHeader(scope);
|
||
|
output += this.generateOutputList(scope); // generate output first to be consistent
|
||
|
output += this.generateInputList(scope);
|
||
|
|
||
|
// Note: processGraph function populates scope.verilogWireList
|
||
|
var res = this.processGraph(scope, elementTypesUsed);
|
||
|
|
||
|
// Generate Wire Initialization Code
|
||
|
for (var bitWidth = 1; bitWidth<= 32; bitWidth++){
|
||
|
var wireList = scope.verilogWireList[bitWidth];
|
||
|
// Hack for splitter
|
||
|
wireList = wireList.filter(x => !x.includes("["))
|
||
|
if(wireList.length == 0)
|
||
|
continue;
|
||
|
if(bitWidth == 1)
|
||
|
output += " wire " + wireList.join(", ") + ";\n";
|
||
|
else
|
||
|
output += " wire [" +(bitWidth-1)+":0] " + wireList.join(", ") + ";\n";
|
||
|
}
|
||
|
|
||
|
// Append Wire connections and module instantiations
|
||
|
output += res;
|
||
|
|
||
|
// Append footer
|
||
|
output += "endmodule\n";
|
||
|
|
||
|
return output;
|
||
|
},
|
||
|
// Performs DFS on the graph and generates netlist of wires and connections
|
||
|
processGraph: function(scope, elementTypesUsed){
|
||
|
// Initializations
|
||
|
var res = "";
|
||
|
scope.stack = [];
|
||
|
scope.verilogWireList = [];
|
||
|
for(var i = 0; i <= 32; i++)
|
||
|
scope.verilogWireList.push(new Array());
|
||
|
|
||
|
var verilogResolvedSet = new Set();
|
||
|
|
||
|
// Start DFS from inputs
|
||
|
for (var i = 0; i < inputList.length; i++) {
|
||
|
for (var j = 0; j < scope[inputList[i]].length; j++) {
|
||
|
scope.stack.push(scope[inputList[i]][j]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Iterative DFS on circuit graph
|
||
|
while (scope.stack.length) {
|
||
|
if (errorDetected) return;
|
||
|
var elem = scope.stack.pop();
|
||
|
|
||
|
if(verilogResolvedSet.has(elem))
|
||
|
continue;
|
||
|
|
||
|
// Process verilog creates variable names and adds elements to DFS stack
|
||
|
elem.processVerilog();
|
||
|
|
||
|
// Record usage of element type
|
||
|
if(elem.objectType != "Node") {
|
||
|
if(elementTypesUsed[elem.objectType])elementTypesUsed[elem.objectType]++;
|
||
|
else elementTypesUsed[elem.objectType] = 1;
|
||
|
}
|
||
|
|
||
|
if(elem.objectType!="Node" && elem.objectType!="Input" && elem.objectType!="Clock") {
|
||
|
verilogResolvedSet.add(elem);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Generate connection verilog code and module instantiations
|
||
|
for(var elem of verilogResolvedSet) {
|
||
|
res += " " + elem.generateVerilog() + "\n";
|
||
|
}
|
||
|
return res;
|
||
|
},
|
||
|
|
||
|
resetLabels: function(scope){
|
||
|
for(var i=0;i<scope.allNodes.length;i++){
|
||
|
scope.allNodes[i].verilogLabel="";
|
||
|
}
|
||
|
},
|
||
|
// Sets labels for all Circuit Elements elements
|
||
|
setLabels: function(scope=globalScope){
|
||
|
/**
|
||
|
* Sets a name for each element. If element is already labeled,
|
||
|
* the element is used directly, otherwise an automated label is provided
|
||
|
* sanitizeLabel is a helper function to escape white spaces
|
||
|
*/
|
||
|
for(var i=0;i<scope.Input.length;i++){
|
||
|
if(scope.Input[i].label=="")
|
||
|
scope.Input[i].label="inp_"+i;
|
||
|
else
|
||
|
scope.Input[i].label=this.sanitizeLabel(scope.Input[i].label)
|
||
|
// copy label to node
|
||
|
scope.Input[i].output1.verilogLabel = scope.Input[i].label;
|
||
|
}
|
||
|
for(var i=0;i<scope.ConstantVal.length;i++){
|
||
|
if(scope.ConstantVal[i].label=="")
|
||
|
scope.ConstantVal[i].label="const_"+i;
|
||
|
else
|
||
|
scope.ConstantVal[i].label=this.sanitizeLabel(scope.ConstantVal[i].label)
|
||
|
// copy label to node
|
||
|
scope.ConstantVal[i].output1.verilogLabel=scope.ConstantVal[i].label;
|
||
|
}
|
||
|
|
||
|
// copy label to clock
|
||
|
for(var i = 0; i < scope.Clock.length; i++) {
|
||
|
if (scope.Clock[i].label == "")
|
||
|
scope.Clock[i].label = "clk_"+i;
|
||
|
else
|
||
|
scope.Clock[i].label=this.sanitizeLabel(scope.Clock[i].label);
|
||
|
scope.Clock[i].output1.verilogLabel = scope.Clock[i].label;
|
||
|
}
|
||
|
|
||
|
for(var i=0;i<scope.Output.length;i++){
|
||
|
if(scope.Output[i].label=="")
|
||
|
scope.Output[i].label="out_"+i;
|
||
|
else
|
||
|
scope.Output[i].label=this.sanitizeLabel(scope.Output[i].label)
|
||
|
}
|
||
|
for(var i=0;i<scope.SubCircuit.length;i++){
|
||
|
if(scope.SubCircuit[i].label=="")
|
||
|
scope.SubCircuit[i].label=scope.SubCircuit[i].data.name+"_"+i;
|
||
|
else
|
||
|
scope.SubCircuit[i].label=this.sanitizeLabel(scope.SubCircuit[i].label)
|
||
|
}
|
||
|
for(var i=0;i<moduleList.length;i++){
|
||
|
var m = moduleList[i];
|
||
|
for(var j=0;j<scope[m].length;j++){
|
||
|
scope[m][j].verilogLabel = this.sanitizeLabel(scope[m][j].label) || (scope[m][j].verilogName()+"_"+j);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
generateHeader:function(scope=globalScope){
|
||
|
// Example: module HalfAdder (a,b,s,c);
|
||
|
var res="\nmodule " + this.sanitizeLabel(scope.name);
|
||
|
res += this.generateHeaderHelper(scope);
|
||
|
return res;
|
||
|
},
|
||
|
generateHeaderHelper:function(scope=globalScope) {
|
||
|
// Example: (a,b,s,c);
|
||
|
var res="(";
|
||
|
var pins = [];
|
||
|
for(var i=0;i<scope.Output.length;i++){
|
||
|
pins.push(scope.Output[i].label);
|
||
|
}
|
||
|
for(var i=0;i<scope.Clock.length;i++){
|
||
|
pins.push(scope.Clock[i].label);
|
||
|
}
|
||
|
for(var i=0;i<scope.Input.length;i++){
|
||
|
pins.push(scope.Input[i].label);
|
||
|
}
|
||
|
res += pins.join(", ");
|
||
|
res += ");\n";
|
||
|
return res;
|
||
|
},
|
||
|
generateInputList:function(scope=globalScope){
|
||
|
var inputs={}
|
||
|
for(var i = 1; i <= 32; i++)
|
||
|
inputs[i] = [];
|
||
|
|
||
|
for(var i=0;i<scope.Input.length;i++){
|
||
|
inputs[scope.Input[i].bitWidth].push(scope.Input[i].label);
|
||
|
}
|
||
|
|
||
|
for(var i=0;i<scope.Clock.length;i++){
|
||
|
inputs[scope.Clock[i].bitWidth].push(scope.Clock[i].label);
|
||
|
}
|
||
|
|
||
|
var res="";
|
||
|
for (bitWidth in inputs){
|
||
|
if(inputs[bitWidth].length == 0) continue;
|
||
|
if(bitWidth==1)
|
||
|
res+=" input "+ inputs[1].join(", ") + ";\n";
|
||
|
else
|
||
|
res+=" input ["+(bitWidth-1)+":0] "+ inputs[bitWidth].join(", ") + ";\n";
|
||
|
}
|
||
|
|
||
|
return res;
|
||
|
},
|
||
|
generateOutputList:function(scope=globalScope){
|
||
|
// Example 1: output s,cout;
|
||
|
var outputs={}
|
||
|
for(var i=0;i<scope.Output.length;i++){
|
||
|
if(outputs[scope.Output[i].bitWidth])
|
||
|
outputs[scope.Output[i].bitWidth].push(scope.Output[i].label);
|
||
|
else
|
||
|
outputs[scope.Output[i].bitWidth] = [scope.Output[i].label];
|
||
|
}
|
||
|
var res="";
|
||
|
for (bitWidth in outputs){
|
||
|
if(bitWidth==1)
|
||
|
res+=" output "+ outputs[1].join(", ") + ";\n";
|
||
|
else
|
||
|
res+=" output ["+(bitWidth-1)+":0] "+ outputs[bitWidth].join(", ") + ";\n";
|
||
|
}
|
||
|
|
||
|
return res;
|
||
|
},
|
||
|
sanitizeLabel: function(name){
|
||
|
// return name.replace(/ Inverse/g, "_inv").replace(/ /g , "_");
|
||
|
var temp = name;
|
||
|
//if there is a space anywhere but the last place
|
||
|
//replace spaces by "_"
|
||
|
//last space is required for escaped id
|
||
|
if (temp.search(/ /g) < temp.length-1 && temp.search(/ /g) >= 0) {
|
||
|
temp = temp.replace(/ Inverse/g, "_inv");
|
||
|
temp = temp.replace(/ /g , "_");
|
||
|
}
|
||
|
//if first character is not \ already
|
||
|
if (temp.substring(0,1).search(/\\/g) < 0) {
|
||
|
//if there are non-alphanum_ character, or first character is num, add \
|
||
|
if (temp.search(/[\W]/g) > -1 || temp.substring(0,1).search(/[0-9]/g) > -1)
|
||
|
temp = "\\" + temp + " ";
|
||
|
}
|
||
|
return temp;
|
||
|
},
|
||
|
/*
|
||
|
sanitizeLabel: function(name){
|
||
|
// Replace spaces by "_"
|
||
|
name = name.replace(/ /g , "_");
|
||
|
// Replace Hyphens by "_"
|
||
|
name = name.replace(/-/g , "_");
|
||
|
// Replace Colons by "_"
|
||
|
name = name.replace(/:/g , "_");
|
||
|
// replace ~ with inv_
|
||
|
name = name.replace(/~/g , "inv_");
|
||
|
// Shorten Inverse to inv
|
||
|
name = name.replace(/Inverse/g , "inv");
|
||
|
|
||
|
// If first character is a number
|
||
|
if(name.substring(0, 1).search(/[0-9]/g) > -1) {
|
||
|
name = "w_" + name;
|
||
|
}
|
||
|
|
||
|
// if first character is not \ already
|
||
|
if (name[0] != '\\') {
|
||
|
//if there are non-alphanum_ character, add \
|
||
|
if (name.search(/[\W]/g) > -1)
|
||
|
name = "\\" + name;
|
||
|
}
|
||
|
return name;
|
||
|
},
|
||
|
*/
|
||
|
generateNodeName: function(node, currentCount, totalCount) {
|
||
|
if(node.verilogLabel) return node.verilogLabel;
|
||
|
var parentVerilogLabel = node.parent.verilogLabel;
|
||
|
var nodeName;
|
||
|
if(node.label) {
|
||
|
nodeName = verilog.sanitizeLabel(node.label);
|
||
|
}
|
||
|
else {
|
||
|
nodeName = (totalCount > 1) ? "out_" + currentCount: "out";
|
||
|
}
|
||
|
if (parentVerilogLabel.substring(0,1).search(/\\/g) < 0)
|
||
|
return (parentVerilogLabel) + "_" + nodeName;
|
||
|
else
|
||
|
return (parentVerilogLabel.substring(0,parentVerilogLabel.length-1)) + "_" + nodeName + " ";
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Helper function to generate spaces for indentation
|
||
|
*/
|
||
|
function sp(indentation) {
|
||
|
return " ".repeat(indentation * 2);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Helper function to indent paragraph
|
||
|
*/
|
||
|
function indent(indentation, string) {
|
||
|
var result = string.split('\n');
|
||
|
if(result[result.length - 1] == '') {
|
||
|
result.pop();
|
||
|
result = result.map(x => sp(indentation) + x).join("\n");
|
||
|
result += '\n';
|
||
|
return result;
|
||
|
}
|
||
|
return result.map(x => sp(indentation) + x).join("\n");
|
||
|
}
|