/* # 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 !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= 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"); }