module BlifUtils::Elaborator

Public Class Methods

elaborate_netlist(ast, quiet: false, default_latch_type: nil, default_latch_clock: nil, default_latch_initial_value: nil) click to toggle source
# File lib/blifutils/elaborator.rb, line 29
def self.elaborate_netlist (ast, quiet: false, default_latch_type: nil, default_latch_clock: nil, default_latch_initial_value: nil)
        modelDeclarations = gather_model_declarations(ast)

        netlist = BlifUtils::Netlist.new
        models = ast.modelList.collect do |modelAst|
                puts "Elaborating model \"#{modelAst.name}\"..." unless quiet
                netlist.add_model(elaborate_model(modelAst, modelDeclarations, quiet: quiet, default_latch_type: default_latch_type, default_latch_clock: default_latch_clock, default_latch_initial_value: default_latch_initial_value))
        end

        return netlist
end

Private Class Methods

elaborate_model(modelAst, modelDeclarations, quiet: false, default_latch_type: nil, default_latch_clock: nil, default_latch_initial_value: nil) click to toggle source
# File lib/blifutils/elaborator.rb, line 97
def self.elaborate_model (modelAst, modelDeclarations, quiet: false, default_latch_type: nil, default_latch_clock: nil, default_latch_initial_value: nil)
        name = modelAst.name
        inputs = []
        outputs = []
        components = []
        nets = []
        clocks = []

        # Create inputs #
        modelAst.header.select{|headerElement| headerElement.class == BlifUtils::AST::ModelHeaderElementInputs}.each do |headerInput|
                headerInput.inputList.each do |inputStr|
                        if inputs.include?(inputStr) then
                                abort "ERROR: In model \"#{name}\": input \"#{inputStr}\" is declared more than once"
                        end
                        inputs << BlifUtils::Netlist::IO.new(inputStr, inputStr)
                end
        end

        # Create outputs #
        modelAst.header.select{|headerElement| headerElement.class == BlifUtils::AST::ModelHeaderElementOutputs}.each do |headerOutput|
                headerOutput.outputList.each do |outputStr|
                        if outputs.include?(outputStr) then
                                abort "ERROR: In model \"#{name}\": output \"#{outputStr}\" is declared more than once"
                        end
                        outputs << BlifUtils::Netlist::IO.new(outputStr, outputStr)
                end
        end

        unless modelAst.commands.index{|commandAst| commandAst.class == BlifUtils::AST::BlackBox}.nil? then
                return BlifUtils::Netlist::Model.new(name, inputs, outputs, components, nets, clocks, true)
        end

        # Create components #
        modelAst.commands.each do |commandAst|
                if commandAst.class == BlifUtils::AST::LogicGate then
                        components << BlifUtils::Netlist::LogicGate.new(commandAst.inputs, commandAst.output, commandAst.single_output_cover_list)
                elsif commandAst.class == BlifUtils::AST::GenericLatch then
                        components << BlifUtils::Netlist::Latch.new(commandAst.input, commandAst.output, commandAst.initValue, commandAst.ctrlType, commandAst.ctrlSig)
                elsif commandAst.class == BlifUtils::AST::ModelReference then
                        modelDeclarationIndex = modelDeclarations.index{|md| md.name == commandAst.modelName}
                        if modelDeclarationIndex.nil? then
                                abort "ERROR: In model \"#{name}\": model \"#{commandAst.modelName}\" is referenced but is not defined in the parsed blif files"
                        end
                        modelDeclaration = modelDeclarations[modelDeclarationIndex]
                        inputFormalAcutalList = []
                        outputFormalAcutalList = []
                        commandAst.formalAcutalList.each do |form_act| 
                                newIO = BlifUtils::Netlist::IO.new(form_act[0], form_act[1])
                                if modelDeclaration.inputs.include?(newIO.name) then
                                        inputFormalAcutalList << newIO
                                elsif modelDeclaration.outputs.include?(newIO.name) then
                                        outputFormalAcutalList << newIO
                                else
                                        abort "ERROR: In model \"#{name}\": model \"#{commandAst.modelName}\" is referenced with formal \"#{newIO.name}\" which is neither an input nor an output of this referenced model"
                                end
                        end
                        components << BlifUtils::Netlist::SubCircuit.new(commandAst.modelName, inputFormalAcutalList, outputFormalAcutalList)
                end
        end

        if default_latch_type then
                components.each do |c|
                        next unless c.isLatch? and c.ctrlType.nil?
                        c.set_type(default_latch_type)
                end
        end

        if default_latch_clock then
                components.each do |c|
                        next unless c.isLatch? and c.ctrlSig.nil?
                        c.set_clock(default_latch_clock)
                end
        end

        if default_latch_initial_value then
                components.each do |c|
                        next unless c.isLatch? and c.initValue.nil?
                        c.set_initial_value(default_latch_initial_value)
                end
        end


        # Create all nets from their drivers #
        netnames = {}
        inputs.each do |iIO|
                newNet = BlifUtils::Netlist::Net.new(iIO.net, :input, [], true, false)
                netnames[newNet.name] = newNet
                nets << newNet
                iIO.net = newNet
        end
        components.reject{|comp| comp.isSubcircuit?}.each do |component|
                newNet = BlifUtils::Netlist::Net.new(component.output, component, [], false, false)
                abort "ERROR: In model \"#{name}\": net \"#{newNet.name}\" has more than one driver" if netnames[newNet.name]
                netnames[newNet.name] = newNet
                nets << newNet
                component.output = newNet
        end
        components.select{|comp| comp.isSubcircuit?}.each do |subcircuit|
                subcircuit.outputFormalAcutalList.each do |outIO|
                        newNet = BlifUtils::Netlist::Net.new(outIO.net, subcircuit, [], false, false)
                        abort "ERROR: In model \"#{name}\": net \"#{newNet.name}\" has more than one driver" if netnames[newNet.name]
                        netnames[newNet.name] = newNet
                        nets << newNet
                        outIO.net = newNet
                end
        end

        # Update nets fanouts #
        outputs.each_with_index do |oIO, i|
                theNet = netnames[oIO.name]
                abort "ERROR: In model \"#{name}\": output \"#{oIO.name}\" has no driver" if theNet.nil?
                theNet.fanouts << BlifUtils::Netlist::Fanout.new(:output, i)
                theNet.isOutput = true
                oIO.net = theNet
        end
        components.select{|comp| comp.isLatch?}.each do |latch|
                theNet = netnames[latch.input]
                abort "ERROR: In model \"#{name}\": input \"#{latch.input}\" from latch \"#{latch.output.name}\" has no driver" if theNet.nil?
                theNet.fanouts << BlifUtils::Netlist::Fanout.new(latch, 0)
                latch.input = theNet
        end
        components.select{|comp| comp.isGate?}.each do |gate|
                gate.inputs.each_with_index do |gin, i|
                        theNet = netnames[gin]
                        abort "ERROR: In model \"#{name}\": input \"#{gin}\" from gate \"#{gate.output.name}\" has no driver" if theNet.nil?
                        theNet.fanouts << BlifUtils::Netlist::Fanout.new(gate, i)
                        gate.inputs[i] = theNet
                end
        end
        components.select{|comp| comp.isSubcircuit?}.each do |subcircuit|
                subcircuit.inputFormalAcutalList.each_with_index do |iIO, i|
                        theNet = netnames[iIO.net]
                        abort "ERROR: In model \"#{name}\": input \"#{iIO}\" (formal \"#{iIO.name}\" from reference model \"#{subcircuit.modelName}\" has no driver" if theNet.nil?
                        theNet.fanouts << BlifUtils::Netlist::Fanout.new(subcircuit, i)
                        iIO.net = theNet
                end
        end

        clocks = components.select{|comp| comp.isLatch?}.collect{|latch| latch.ctrlSig}.reject{|el| el.nil?}.uniq

        # Check that each net has at least one fanout #
        unless quiet then
                nets.each do |net|
                        if net.fanouts.empty? and not(clocks.include?(net.name)) then
                                STDERR.puts "WARNING: In model \"#{name}\": net \"#{net.name}\" has no fanouts"
                        end
                end
        end

        return BlifUtils::Netlist::Model.new(name, inputs, outputs, components, nets, clocks)
end
gather_model_declarations(ast) click to toggle source
# File lib/blifutils/elaborator.rb, line 65
def self.gather_model_declarations (ast)
        modelDeclarations = ast.modelList.collect do |modelAst|
                inputs = []
                modelAst.header.select{|headerElement| headerElement.class == BlifUtils::AST::ModelHeaderElementInputs}.each do |headerInput|
                        headerInput.inputList.each do |inputStr|
                                inputs << inputStr
                        end
                end
                outputs = []
                modelAst.header.select{|headerElement| headerElement.class == BlifUtils::AST::ModelHeaderElementOutputs}.each do |headerOutput|
                        headerOutput.outputList.each do |outputStr|
                                outputs << outputStr
                        end
                end
                BlifUtils::Elaborator::ModelDeclaration.new(modelAst.name, inputs, outputs)
        end

        # Check that each model is defined only once #
        (0 ... (modelDeclarations.length - 1)).each do |i|
                md1name = modelDeclarations[i].name
                ((i+1) ... modelDeclarations.length).each do |j|
                        md2name = modelDeclarations[j].name
                        if md1name == md2name then
                                abort "ERROR: Model \"#{md1name}\" is defined more than once"
                        end
                end
        end

        return modelDeclarations
end