blifutils 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,180 @@
1
+ ##
2
+ # Copyright (C) 2017 Théotime bollengier <theotime.bollengier@gmail.com>
3
+ #
4
+ # This file is part of Blifutils.
5
+ #
6
+ # Blifutils is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Blifutils is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Blifutils. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+
20
+ require 'blifutils/netlist'
21
+
22
+
23
+ module BlifUtils
24
+
25
+ class Netlist
26
+
27
+ def create_vhdl_files (topLevelModuleName = nil)
28
+ if topLevelModuleName then
29
+ tLModel = get_model_by_name(topLevelModuleName)
30
+ else
31
+ tLModel = first_model
32
+ end
33
+
34
+ abort "ERROR: create_vhdl_file(#{topLevelModuleName}): cannot find top level model" if tLModel.nil?
35
+
36
+ update_clocks()
37
+
38
+ models.each do |model|
39
+ model.create_vhdl_file(model == tLModel)
40
+ end
41
+ end
42
+
43
+
44
+ class Model
45
+
46
+ def create_vhdl_file (topLevel = false)
47
+ return if @isBlackBox
48
+ fileName = @name + '.vhd'
49
+ file = File.open(fileName, 'w')
50
+
51
+ to_vhdl(toplevel: topLevel, stream: file)
52
+
53
+ file.close
54
+ STDERR.puts "File \"#{fileName}\" written."
55
+ end
56
+
57
+
58
+ def to_vhdl (topLevel: false, stream: "")
59
+ iNames = @inputs.collect{|io| io.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'')}
60
+ oNames = @outputs.collect{|io| io.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'')}
61
+ just = [([0] + iNames.collect{|name| name.length}).max + 3, ([0] + oNames.collect{|name| name.length}).max + 4].max
62
+ entityStr = iNames.collect{|name| "#{(name + '_in').ljust(just)} : in std_ulogic;"} +
63
+ oNames.collect{|name| "#{(name + '_out').ljust(just)} : out std_ulogic;"}
64
+
65
+ stream << "\nlibrary IEEE;\nuse IEEE.STD_LOGIC_1164.ALL;\n\n\nentity #{@name.upcase} is\n\tport ( "
66
+ stream << entityStr.join("\n\t ").chop
67
+ stream << ");\nend #{name.upcase};\n\n\n"
68
+ stream << "architecture blif of #{name.upcase} is\n\n"
69
+
70
+ just = @nets.collect{|net| net.name.length}.max
71
+
72
+ @clocks.reject{|clkname| @nets.collect{|net| net.name}.include?(clkname)}.each do |name|
73
+ stream << "\tsignal #{name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'').ljust(just)} : std_ulogic;\n"
74
+ end
75
+ @nets.each do |net|
76
+ name = net.name
77
+ stream << "\tsignal #{name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'').ljust(just)} : std_ulogic#{if net.driver.kind_of?(BlifUtils::Netlist::Latch) and net.driver.initValue <= 1 then " := '#{net.driver.initValue}'" end};\n"
78
+ end
79
+
80
+ stream << "\nbegin\n\n"
81
+
82
+ @inputs.each do |io|
83
+ stream << "\t#{io.net.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'')} <= #{io.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'') + '_in'};\n"
84
+ end
85
+ stream <<("\n") unless @inputs.empty?
86
+ @outputs.each do |io|
87
+ stream << "\t#{io.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'') + '_out'} <= #{io.net.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'')};\n"
88
+ end
89
+ stream <<("\n") unless @outputs.empty?
90
+
91
+ stream <<("\n")
92
+
93
+ latches = @components.select{|comp| comp.kind_of?(BlifUtils::Netlist::Latch)}
94
+ unless latches.empty? then
95
+ clks = latches.collect{|latch| latch.ctrlSig}.reject{|el| el.nil?}.collect{|ctrlsig| if ctrlsig.kind_of?(String) then ctrlsig else ctrlsig.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'') end}.uniq
96
+
97
+ clks.each do |clkname|
98
+ stream << "\tprocess(#{clkname.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'')})\n\tbegin\n\t\tif rising_edge(#{clkname.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'')}) then\n"
99
+ latches.select{|latch| latch.ctrlSig != nil and (latch.ctrlSig == clkname or latch.ctrlSig.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'') == clkname)}.each do |latch|
100
+ stream << "\t\t\t#{latch.output.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'')} <= #{latch.input.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'')};\n"
101
+ end
102
+ stream << "\t\tend if;\n\tend process;\n"
103
+ end
104
+
105
+ if clks.empty? then
106
+ stream << "\n\tprocess(_clk)\n\tbegin\n\t\tif rising_edge(_clk) then\n"
107
+ latches.select{|latch| latch.ctrlSig.nil?}.each do |latch|
108
+ stream << "\n\t\t\t#{latch.output.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'')} <= #{latch.input.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'')};\n"
109
+ end
110
+ stream << "\t\tend if;\n\tend process;\n"
111
+ end
112
+ stream <<("\n")
113
+ end
114
+
115
+ gates = @components.select{|comp| comp.kind_of?(BlifUtils::Netlist::LogicGate)}
116
+ gates.each do |gate|
117
+ next if gate.is_constant?
118
+ oname = gate.output.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'')
119
+ inames = gate.inputs.collect{|net| net.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'')}
120
+ stream << "\t#{oname} <= "
121
+ polarity = gate.singleOutputCoverList.collect{|inputs_output| inputs_output[1]}.uniq
122
+ if polarity.length != 1 or (polarity[0] != 0 and polarity[0] != 1) then
123
+ abort "ERROR: Output cover list of gate \"#{oname}\" contains '1' and '0' as output!"
124
+ end
125
+ stream <<("not(") if polarity[0] == 0
126
+ socvlst = gate.singleOutputCoverList.collect { |cvlst|
127
+ cvlstArr = []
128
+ cvlst[0].each_with_index { |val, i|
129
+ if val == 1 then
130
+ cvlstArr << inames[i]
131
+ elsif val == 0 then
132
+ cvlstArr << "not(#{inames[i]})"
133
+ #else
134
+ # cvlstArr << "'1'"
135
+ end
136
+ }
137
+ '(' + cvlstArr.join(' and ') + ')'
138
+ }.join(" or\n\t#{' '*oname.length} ")
139
+ stream << socvlst
140
+ stream <<(")") if polarity[0] == 0
141
+ stream <<(";\n")
142
+ end
143
+ stream <<("\n") unless gates.empty?
144
+
145
+ constants = gates.select{|gate| gate.is_constant?}
146
+ constants.each do |cstGate|
147
+ oname = cstGate.output.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'')
148
+ if cstGate.singleOutputCoverList.empty? or cstGate.singleOutputCoverList[0][2] == 0 then
149
+ stream << "\t#{oname} <= '0';\n"
150
+ else
151
+ stream << "\t#{oname} <= '1';\n"
152
+ end
153
+ end
154
+ stream <<("\n") unless constants.empty?
155
+
156
+ @components.select{|comp| comp.kind_of?(BlifUtils::Netlist::SubCircuit)}.each_with_index do |subckt, i|
157
+ stream << "\tCMPINST#{i}: entity work.#{subckt.modelName.upcase}\n\tport map ( "
158
+
159
+ iNames = subckt.inputFormalAcutalList.collect{|io| io.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'') + '_in'}
160
+ oNames = subckt.outputFormalAcutalList.collect{|io| io.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'') + '_out'}
161
+ just = ([0] + iNames.collect{|name| name.length} + oNames.collect{|name| name.length}).max
162
+
163
+ portmapStr = subckt.inputFormalAcutalList.collect{|io| "#{(io.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'') + '_in').ljust(just)} => #{io.net.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'')},"} +
164
+ subckt.outputFormalAcutalList.collect{|io| "#{(io.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'') + '_out').ljust(just)} => #{io.net.name.gsub(/(\[|\])/, '_').gsub(/_$/,'').gsub(/^_/,'')},"}
165
+
166
+ stream << portmapStr.join("\n\t ").chop
167
+ stream << ");\n\n"
168
+ end
169
+
170
+ stream << "end blif;\n\n"
171
+
172
+ return stream
173
+ end
174
+
175
+ end # BlifUtils::Netlist::Model
176
+
177
+ end # BlifUtils::Netlist
178
+
179
+ end # BlifUtils
180
+
@@ -0,0 +1,257 @@
1
+ ##
2
+ # Copyright (C) 2017 Théotime bollengier <theotime.bollengier@gmail.com>
3
+ #
4
+ # This file is part of Blifutils.
5
+ #
6
+ # Blifutils is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Blifutils is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Blifutils. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+
20
+ require 'blifutils/parser.rb'
21
+ require 'blifutils/ast.rb'
22
+ require 'blifutils/netlist.rb'
23
+
24
+
25
+ module BlifUtils
26
+
27
+ module Elaborator
28
+
29
+ def self.elaborate_netlist (ast, quiet: false)
30
+ modelDeclarations = gather_model_declarations(ast)
31
+
32
+ netlist = BlifUtils::Netlist.new
33
+ models = ast.modelList.collect do |modelAst|
34
+ puts "Elaborating model \"#{modelAst.name}\"..." unless quiet
35
+ netlist.add_model(elaborate_model(modelAst, modelDeclarations, quiet: quiet))
36
+ end
37
+
38
+ return netlist
39
+ end # BlifUtils::Elaborator::elaborate_netlist
40
+
41
+
42
+ private
43
+
44
+
45
+ class ModelDeclaration
46
+ attr_reader :name
47
+ attr_reader :inputs #[String, ... ]
48
+ attr_reader :outputs #[String, ... ]
49
+
50
+ def initialize (name, inputs, outputs)
51
+ @name = name
52
+ @inputs = inputs
53
+ @outputs = outputs
54
+ end
55
+
56
+ def to_s
57
+ str = "Model \"#{@name}\"\n"
58
+ str += " Inputs: #{@inputs.join(', ')}\n"
59
+ str += " Outputs: #{@outputs.join(', ')}\n"
60
+ return str
61
+ end
62
+ end # BlifUtils::Elaborator::ModelDeclaration
63
+
64
+
65
+ def self.gather_model_declarations (ast)
66
+ modelDeclarations = ast.modelList.collect do |modelAst|
67
+ inputs = []
68
+ modelAst.header.select{|headerElement| headerElement.class == BlifUtils::AST::ModelHeaderElementInputs}.each do |headerInput|
69
+ headerInput.inputList.each do |inputStr|
70
+ inputs << inputStr
71
+ end
72
+ end
73
+ outputs = []
74
+ modelAst.header.select{|headerElement| headerElement.class == BlifUtils::AST::ModelHeaderElementOutputs}.each do |headerOutput|
75
+ headerOutput.outputList.each do |outputStr|
76
+ outputs << outputStr
77
+ end
78
+ end
79
+ BlifUtils::Elaborator::ModelDeclaration.new(modelAst.name, inputs, outputs)
80
+ end
81
+
82
+ # Check that each model is defined only once #
83
+ (0 ... (modelDeclarations.length - 1)).each do |i|
84
+ md1name = modelDeclarations[i].name
85
+ ((i+1) ... modelDeclarations.length).each do |j|
86
+ md2name = modelDeclarations[j].name
87
+ if md1name == md2name then
88
+ abort "ERROR: Model \"#{md1name}\" is defined more than once"
89
+ end
90
+ end
91
+ end
92
+
93
+ return modelDeclarations
94
+ end # BlifUtils::Elaborator::gather_model_declarations
95
+
96
+
97
+ def self.elaborate_model (modelAst, modelDeclarations, quiet: false)
98
+ name = modelAst.name
99
+ inputs = []
100
+ outputs = []
101
+ components = []
102
+ nets = []
103
+ clocks = []
104
+
105
+ # Create inputs #
106
+ modelAst.header.select{|headerElement| headerElement.class == BlifUtils::AST::ModelHeaderElementInputs}.each do |headerInput|
107
+ headerInput.inputList.each do |inputStr|
108
+ if inputs.include?(inputStr) then
109
+ abort "ERROR: In model \"#{name}\": input \"#{inputStr}\" is declared more than once"
110
+ end
111
+ inputs << BlifUtils::Netlist::IO.new(inputStr, inputStr)
112
+ end
113
+ end
114
+
115
+ # Create outputs #
116
+ modelAst.header.select{|headerElement| headerElement.class == BlifUtils::AST::ModelHeaderElementOutputs}.each do |headerOutput|
117
+ headerOutput.outputList.each do |outputStr|
118
+ if outputs.include?(outputStr) then
119
+ abort "ERROR: In model \"#{name}\": output \"#{outputStr}\" is declared more than once"
120
+ end
121
+ outputs << BlifUtils::Netlist::IO.new(outputStr, outputStr)
122
+ end
123
+ end
124
+
125
+ unless modelAst.commands.index{|commandAst| commandAst.class == BlifUtils::AST::BlackBox}.nil? then
126
+ return BlifUtils::Netlist::Model.new(name, inputs, outputs, components, nets, clocks, true)
127
+ end
128
+
129
+ # Create components #
130
+ modelAst.commands.each do |commandAst|
131
+ if commandAst.class == BlifUtils::AST::LogicGate then
132
+ components << BlifUtils::Netlist::LogicGate.new(commandAst.inputs, commandAst.output, commandAst.single_output_cover_list)
133
+ elsif commandAst.class == BlifUtils::AST::GenericLatch then
134
+ components << BlifUtils::Netlist::Latch.new(commandAst.input, commandAst.output, commandAst.initValue, commandAst.ctrlType, commandAst.ctrlSig)
135
+ elsif commandAst.class == BlifUtils::AST::ModelReference then
136
+ modelDeclarationIndex = modelDeclarations.index{|md| md.name == commandAst.modelName}
137
+ if modelDeclarationIndex.nil? then
138
+ abort "ERROR: In model \"#{name}\": model \"#{commandAst.modelName}\" is referenced but is not defined in the parsed blif files"
139
+ end
140
+ modelDeclaration = modelDeclarations[modelDeclarationIndex]
141
+ inputFormalAcutalList = []
142
+ outputFormalAcutalList = []
143
+ commandAst.formalAcutalList.each do |form_act|
144
+ newIO = BlifUtils::Netlist::IO.new(form_act[0], form_act[1])
145
+ if modelDeclaration.inputs.include?(newIO.name) then
146
+ inputFormalAcutalList << newIO
147
+ elsif modelDeclaration.outputs.include?(newIO.name) then
148
+ outputFormalAcutalList << newIO
149
+ else
150
+ 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"
151
+ end
152
+ end
153
+ components << BlifUtils::Netlist::SubCircuit.new(commandAst.modelName, inputFormalAcutalList, outputFormalAcutalList)
154
+ end
155
+ end
156
+
157
+ # Create all nets from their drivers #
158
+ inputs.each do |iIO|
159
+ newNet = BlifUtils::Netlist::Net.new(iIO.net, :input, [], true, false)
160
+ nets << newNet
161
+ iIO.net = newNet
162
+ end
163
+ components.reject{|comp| comp.isSubcircuit?}.each do |component|
164
+ newNet = BlifUtils::Netlist::Net.new(component.output, component, [], false, false)
165
+ if nets.collect{|net| net.name}.include?(newNet.name) then
166
+ abort "ERROR: In model \"#{name}\": net \"#{newNet.name}\" has more than one driver"
167
+ end
168
+ nets << newNet
169
+ component.output = newNet
170
+ end
171
+ components.select{|comp| comp.isSubcircuit?}.each do |subcircuit|
172
+ subcircuit.outputFormalAcutalList.each do |outIO|
173
+ newNet = BlifUtils::Netlist::Net.new(outIO.net, subcircuit, [], false, false)
174
+ if nets.collect{|net| net.name}.include?(newNet.name) then
175
+ abort "ERROR: In model \"#{name}\": net \"#{newNet.name}\" has more than one driver"
176
+ end
177
+ nets << newNet
178
+ outIO.net = newNet
179
+ end
180
+ end
181
+
182
+ # Update nets fanouts #
183
+ outputs.each_with_index do |oIO, i|
184
+ index = nets.index{|net| net.name == oIO.name}
185
+ if index.nil? then
186
+ abort "ERROR: In model \"#{name}\": output \"#{oIO.name}\" has no driver"
187
+ end
188
+ nets[index].fanouts << BlifUtils::Netlist::Fanout.new(:output, i)
189
+ nets[index].isOutput = true
190
+ oIO.net = nets[index]
191
+ end
192
+ components.select{|comp| comp.isLatch?}.each do |latch|
193
+ index = nets.index{|net| net.name == latch.input}
194
+ if index.nil? then
195
+ abort "ERROR: In model \"#{name}\": input \"#{latch.input}\" from latch \"#{latch.output.name}\" has no driver"
196
+ end
197
+ nets[index].fanouts << BlifUtils::Netlist::Fanout.new(latch, 0)
198
+ latch.input = nets[index]
199
+ end
200
+ components.select{|comp| comp.isGate?}.each do |gate|
201
+ gate.inputs.each_with_index do |gin, i|
202
+ index = nets.index{|net| net.name == gin}
203
+ if index.nil? then
204
+ abort "ERROR: In model \"#{name}\": input \"#{gin}\" from gate \"#{gate.output.name}\" has no driver"
205
+ end
206
+ nets[index].fanouts << BlifUtils::Netlist::Fanout.new(gate, i)
207
+ gate.inputs[i] = nets[index]
208
+ end
209
+ end
210
+ components.select{|comp| comp.isSubcircuit?}.each do |subcircuit|
211
+ subcircuit.inputFormalAcutalList.each_with_index do |iIO, i|
212
+ index = nets.index{|net| net.name == iIO.net}
213
+ if index.nil? then
214
+ abort "ERROR: In model \"#{name}\": input \"#{iIO}\" (formal \"#{iIO.name}\" from reference model \"#{subcircuit.modelName}\" has no driver"
215
+ end
216
+ nets[index].fanouts << BlifUtils::Netlist::Fanout.new(subcircuit, i)
217
+ iIO.net = nets[index]
218
+ end
219
+ end
220
+
221
+ clocks = components.select{|comp| comp.isLatch?}.collect{|latch| latch.ctrlSig}.reject{|el| el.nil?}.uniq
222
+
223
+ # Check that each net has at least one fanout #
224
+ nets.each do |net|
225
+ if net.fanouts.empty? and not(clocks.include?(net.name)) then
226
+ STDERR.puts "WARNING: In model \"#{name}\": net \"#{net.name}\" has no fanouts" unless quiet
227
+ end
228
+ end
229
+
230
+ return BlifUtils::Netlist::Model.new(name, inputs, outputs, components, nets, clocks)
231
+ end # BlifUtils::Elaborator::elaborate_model
232
+
233
+ end # BlifUtils::Elaborator
234
+
235
+
236
+ def self.read(fileName, quiet: false)
237
+ ast = BlifUtils::Parser.parse(fileName, quiet: quiet)
238
+ netlist = BlifUtils::Elaborator.elaborate_netlist(ast, quiet: quiet)
239
+ return netlist
240
+ end # BlifUtils::read
241
+
242
+ end # BlifUtils
243
+
244
+
245
+
246
+ if __FILE__ == $0
247
+ if ARGV.length == 0
248
+ puts "You must provide the file to process as argument!"
249
+ exit
250
+ end
251
+
252
+ ast = BlifUtils::Parser.parse(ARGV[0])
253
+
254
+ netlist = BlifUtils::Elaborator.elaborate_netlist(ast)
255
+ puts netlist.analyze
256
+ end
257
+
@@ -0,0 +1,406 @@
1
+ ##
2
+ # Copyright (C) 2017 Théotime bollengier <theotime.bollengier@gmail.com>
3
+ #
4
+ # This file is part of Blifutils.
5
+ #
6
+ # Blifutils is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Blifutils is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Blifutils. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+
20
+ require 'blifutils/netlist'
21
+
22
+
23
+ module BlifUtils
24
+
25
+ module NetlistGraph
26
+
27
+ class Graph
28
+ attr_accessor :vertices
29
+ attr_reader :fromModel
30
+
31
+ def initialize (vertices = [], model = nil)
32
+ @vertices = vertices
33
+ @fromModel = model
34
+ check unless @vertices.empty?
35
+ end
36
+
37
+
38
+ def get_graph_without_input_output_reg_cst_modinst
39
+ newGraph = clone
40
+ newGraph.vertices.delete_if do |vertice|
41
+ vertice.component == :input or
42
+ vertice.component == :output or
43
+ vertice.component.class == BlifUtils::Netlist::SubCircuit or
44
+ vertice.component.class == BlifUtils::Netlist::Latch or
45
+ (vertice.component.class == BlifUtils::Netlist::LogicGate and vertice.component.is_constant?)
46
+ end
47
+ newGraph.vertices.each do |vertice|
48
+ vertice.remove_input_output_reg_cst_modinst_references
49
+ end
50
+ return newGraph
51
+ end
52
+
53
+
54
+ def clone
55
+ vertices = @vertices.collect{|vertice| vertice.clone}
56
+ # Update successors and predecessors references to cloned vertices
57
+ vertices.each do |vertice|
58
+ (0 ... vertice.successors.length).each do |i|
59
+ next if vertice.successors[i] == :output
60
+ successorVertice = vertices.select{|vever| vever.component == vertice.successors[i].component}
61
+ if successorVertice.empty? then
62
+ abort "ERROR: While cloning netlist graph: successor #{vertice.successors[i].component} of component #{vertice.component} has no reference in the graph."
63
+ end
64
+ if successorVertice.length > 1 then
65
+ abort "ERROR: While cloning netlist graph: successor #{vertice.successors[i].component} of component #{vertice.component} has more than one reference in the graph."
66
+ end
67
+ vertice.successors[i] = successorVertice[0]
68
+ end
69
+ (0 ... vertice.predecessors.length).each do |i|
70
+ next if vertice.predecessors[i] == :input
71
+ predecessorVertice = vertices.select{|vever| vever.component == vertice.predecessors[i].component}
72
+ if predecessorVertice.empty? then
73
+ abort "ERROR: While cloning netlist graph: predecessor #{vertice.predecessors[i].component} of component #{vertice.component} has no reference in the graph."
74
+ end
75
+ if predecessorVertice.length > 1 then
76
+ abort "ERROR: While cloning netlist graph: predecessor #{vertice.predecessors[i].component} of component #{vertice.component} has more than one reference in the graph."
77
+ end
78
+ vertice.predecessors[i] = predecessorVertice[0]
79
+ end
80
+ end
81
+ newGraph = BlifUtils::NetlistGraph::Graph.new(vertices, @fromModel)
82
+ return newGraph
83
+ end
84
+
85
+
86
+ def self.create_from_model (model)
87
+ vertices = BlifUtils::NetlistGraph::Vertice.get_vertices_from_model(model)
88
+ ###vertices.each{|ver| puts "#{ver.component} #{ver.component.class.name} #{ver.component.label}"}
89
+ # Update successors and predecessors references to components by references to Vertices
90
+ vertices.each do |vertice|
91
+ (0 ... vertice.successors.length).each do |i|
92
+ next if vertice.successors[i] == :output
93
+ successorVertice = vertices.select{|vever| vever.component == vertice.successors[i]}
94
+ if successorVertice.empty? then
95
+ abort "ERROR: While elaborating netlist graph: successor #{vertice.successors[i]} of component #{vertice.component} has no reference in the graph."
96
+ end
97
+ if successorVertice.length > 1 then
98
+ abort "ERROR: While elaborating netlist graph: successor #{vertice.successors[i]} of component #{vertice.component} has more than one reference in the graph."
99
+ end
100
+ vertice.successors[i] = successorVertice[0]
101
+ end
102
+ (0 ... vertice.predecessors.length).each do |i|
103
+ next if vertice.predecessors[i] == :input
104
+ predecessorVertice = vertices.select{|vever| vever.component == vertice.predecessors[i]}
105
+ if predecessorVertice.empty? then
106
+ abort "ERROR: While elaborating netlist graph: predecessor #{vertice.predecessors[i]} of component #{vertice.component} has no reference in the graph."
107
+ end
108
+ if predecessorVertice.length > 1 then
109
+ abort "ERROR: While elaborating netlist graph: predecessor #{vertice.predecessors[i]} of component #{vertice.component} has more than one reference in the graph."
110
+ end
111
+ vertice.predecessors[i] = predecessorVertice[0]
112
+ end
113
+ end
114
+ newGraph = BlifUtils::NetlistGraph::Graph.new(vertices, model)
115
+ return newGraph
116
+ end
117
+
118
+
119
+ def to_graphviz
120
+ @vertices.each_with_index{|vert, i| vert.id = i}
121
+ str = "digraph #{@fromModel.nil? ? '' : @fromModel.name} {\n"
122
+ @vertices.each do |vertice|
123
+ str += "\t#{vertice.id} [label=\"#{vertice.to_s}\""
124
+ if vertice.component == :input or
125
+ vertice.component == :output or
126
+ vertice.component.class == BlifUtils::Netlist::Latch or
127
+ (vertice.component.class == BlifUtils::Netlist::LogicGate and vertice.component.is_constant?) then
128
+ str += ",shape=box"
129
+ end
130
+ str += "];\n"
131
+ end
132
+ @vertices.each do |vertice|
133
+ vertice.successors.each do |successor|
134
+ next if successor.class == Symbol
135
+ str += "\t#{vertice.id} -> "
136
+ str += "#{successor.id};\n"
137
+ end
138
+ if vertice.successors.empty? and vertice.predecessors.empty? then
139
+ str += "\t#{vertice.id};\n"
140
+ end
141
+ end
142
+ str += "}\n"
143
+ return str
144
+ end
145
+
146
+
147
+ def check
148
+ # Check that each component is in only one vertice
149
+ allComponents = @vertices.collect{|vertice| vertice.component}
150
+ allComponents.each do |component|
151
+ if allComponents.select{|compo| compo == component}.length > 1 then
152
+ abort "ERROR: Checking graph: component #{component} has more than one corresponding vertice."
153
+ end
154
+ end
155
+
156
+ @vertices.each do |vertice|
157
+ # Check that each successor has the current vertice as predecessor
158
+ vertice.successors.each do |successor|
159
+ next if successor == :output
160
+ predecessorVertices = successor.predecessors.select{|prede| prede == vertice}
161
+ if predecessorVertices.empty? then
162
+ abort "ERROR: While elaborating netlist graph: successor #{successor.component} of component #{vertice.component} has no reference to component #{vertice.component} as predecessor."
163
+ end
164
+ if predecessorVertices.length > 1 then
165
+ abort "ERROR: While elaborating netlist graph: successor #{successor.component} of component #{vertice.component} has more than one reference to component #{vertice.component} as predecessor."
166
+ end
167
+ end
168
+
169
+ # Check that each predecessor has the current vertice as successor
170
+ vertice.predecessors.each do |predecessor|
171
+ next if predecessor == :input
172
+ successorVertices = predecessor.successors.select{|succe| succe == vertice}
173
+ if successorVertices.empty? then
174
+ abort "ERROR: While elaborating netlist graph: predecessor #{predecessor.component} of component #{vertice.component} has no reference to component #{vertice.component} as successor."
175
+ end
176
+ if successorVertices.length > 1 then
177
+ abort "ERROR: While elaborating netlist graph: predecessor #{predecessor.component} of component #{vertice.component} has more than one reference to component #{vertice.component} as successor."
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+
184
+ def get_connected_subgraphs
185
+ dags = []
186
+
187
+ verticePool = @vertices.collect{|vert| vert}
188
+
189
+ until verticePool.empty? do
190
+ newDAGvertices = []
191
+ # Pick up a vertice
192
+ BlifUtils::NetlistGraph::Graph.get_connected_vertices_recursive(verticePool[0], newDAGvertices, verticePool)
193
+ dags << BlifUtils::NetlistGraph::Graph.new(newDAGvertices, @fromModel)
194
+ end
195
+
196
+ return dags
197
+ end
198
+
199
+
200
+ def is_acyclic?
201
+ # If a directed graph is acyclic, it has at least a node with no successors,
202
+ # if there is no such node, the graph cannot be acyclic.
203
+ # If we remove a node with no successors, the graph is still acyclic as it leaves new nodes without successors
204
+
205
+ # We make a copy of the graph as we will modigy it and its nodes
206
+ graph = self.clone
207
+
208
+ until graph.vertices.empty? do
209
+ # Find a leaf, e.g. a node with no successors
210
+ leafs = graph.vertices.select{|vertice| vertice.successors.empty?}
211
+ return false if leafs.empty?
212
+ # Remove the leaf from the graph
213
+ leaf = leafs[0]
214
+ graph.vertices.delete(leaf)
215
+ leaf.predecessors.each do |predecessor|
216
+ predecessor.successors.delete(leaf)
217
+ end
218
+ end
219
+
220
+ return true
221
+ end
222
+
223
+
224
+ def assign_layers_to_vertices
225
+ @vertices.each{|vertice| vertice.layer = nil}
226
+ v_remainder_set = @vertices.collect{|vert| vert}
227
+ u_new_set = []
228
+ u_set_length = 0
229
+ z_set = []
230
+ currentLayer = 1
231
+ while u_set_length != @vertices.length do
232
+ selectedVertice = nil
233
+ v_remainder_set.each do |vertice|
234
+ unless vertice.successors.collect{|suc| suc.layer != nil and suc.layer < currentLayer}.include?(false)
235
+ selectedVertice = vertice
236
+ break
237
+ end
238
+ end
239
+ if selectedVertice.nil? then
240
+ currentLayer += 1
241
+ z_set += u_new_set
242
+ u_new_set = []
243
+ else
244
+ selectedVertice.layer = currentLayer
245
+ u_set_length += 1
246
+ u_new_set << selectedVertice
247
+ v_remainder_set.delete(selectedVertice)
248
+ end
249
+ end
250
+ end
251
+
252
+
253
+ private
254
+
255
+
256
+ def self.get_connected_vertices_recursive (vertice, newDAGvertices, verticePool)
257
+ return if newDAGvertices.include?(vertice)
258
+ newDAGvertices << vertice
259
+ raise 'Mah! Que passa ?' unless verticePool.include?(vertice)
260
+ verticePool.delete(vertice)
261
+ vertice.predecessors.each do |predecessor|
262
+ BlifUtils::NetlistGraph::Graph.get_connected_vertices_recursive(predecessor, newDAGvertices, verticePool)
263
+ end
264
+ vertice.successors.each do |successor|
265
+ BlifUtils::NetlistGraph::Graph.get_connected_vertices_recursive(successor, newDAGvertices, verticePool)
266
+ end
267
+ end
268
+
269
+ end # BlifUtils::NetlistGraph::Graph
270
+
271
+
272
+ class Vertice
273
+ attr_accessor :component
274
+ attr_accessor :successors
275
+ attr_accessor :predecessors
276
+ attr_accessor :layer
277
+ attr_accessor :id
278
+
279
+ def initialize
280
+ @component = nil
281
+ @successors = []
282
+ @predecessors = []
283
+ @layer = nil
284
+ @id = -1
285
+ end
286
+
287
+
288
+ def clone
289
+ newVertice = BlifUtils::NetlistGraph::Vertice.new
290
+ newVertice.component = @component
291
+ newVertice.layer = @layer
292
+ newVertice.successors = @successors.collect{|suc| suc}
293
+ newVertice.predecessors = @predecessors.collect{|pred| pred}
294
+ newVertice.id = @id
295
+ return newVertice
296
+ end
297
+
298
+
299
+ def remove_input_output_reg_cst_modinst_references
300
+ @successors.delete_if do |successor|
301
+ successor == :output or
302
+ successor.component.class == BlifUtils::Netlist::Latch
303
+ end
304
+ @predecessors.delete_if do |predecessor|
305
+ predecessor == :input or
306
+ predecessor.component.class == BlifUtils::Netlist::Latch or
307
+ (predecessor.component.class == BlifUtils::Netlist::LogicGate and predecessor.component.is_constant?)
308
+ end
309
+ end
310
+
311
+
312
+ def to_s
313
+ return "#{@component.class.name.split('::')[-1]} (#{@component.output.name})#{@layer.nil? ? '' : " [L#{@layer}]"}"
314
+ end
315
+
316
+
317
+ def self.create_from_model_component (component, model)
318
+ newVertice = BlifUtils::NetlistGraph::Vertice.new
319
+
320
+ newVertice.component = component
321
+ component.inputs.each do |net|
322
+ driverCompo = net.driver
323
+ newVertice.predecessors << driverCompo unless newVertice.predecessors.include?(driverCompo)
324
+ end
325
+
326
+ component.output.fanouts.each do |fanout|
327
+ fanoutCompo = fanout.target
328
+ newVertice.successors << fanoutCompo unless newVertice.successors.include?(fanoutCompo)
329
+ end
330
+
331
+ return newVertice
332
+ end
333
+
334
+
335
+ def self.get_vertices_from_model (model)
336
+ vertices = model.components.collect{|component| self.create_from_model_component(component, model)}
337
+ return vertices
338
+ end
339
+
340
+ end # BlifUtils::NetlistGraph::Vertice
341
+
342
+ end # BlifUtils::NetlistGraph
343
+
344
+
345
+ class Netlist
346
+
347
+ class Model
348
+
349
+ def simulation_components_to_schedule_stack (withOutputGraphviz: false, quiet: false)
350
+ unless is_self_contained? then
351
+ raise "#{self.class.name}##{__method__.to_s}() requires that the model has no model reference in it. You must flatten the model before."
352
+ end
353
+ puts "Generating graph from model components..." unless quiet
354
+ graphFull = BlifUtils::NetlistGraph::Graph.create_from_model(self)
355
+ print "Extracting connected subgraphs... " unless quiet
356
+ graphDAGs = graphFull.get_graph_without_input_output_reg_cst_modinst
357
+ dags = graphDAGs.get_connected_subgraphs
358
+ puts "#{dags.length} subgraph#{dags.length > 1 ? 's' : ''} found" unless quiet
359
+
360
+ print "Checking that there are no cycles in subgraphs... " unless quiet
361
+ # Check that all subgraphs are acyclic
362
+ dags.each_with_index do |dag, i|
363
+ unless dag.is_acyclic? then
364
+ str = "\nERROR: There is a combinatorial loop.\n (See cycle in file \"#{@name}_graph_DAG_#{i}.gv\")\n This subgraph includes components:\n"
365
+ dag.vertices.each do |vertice|
366
+ str += " Component #{vertice.to_s}\n"
367
+ end
368
+
369
+ abort str
370
+ end
371
+ end
372
+ puts "Ok" unless quiet
373
+
374
+ # Do graph layering
375
+ puts "Layering subgraphs..." unless quiet
376
+ dags.each_with_index do |dag, i|
377
+ dag.assign_layers_to_vertices
378
+ File.write("#{@name}_graph_DAG_#{i}.gv", dag.to_graphviz) if withOutputGraphviz
379
+ end
380
+
381
+ File.write("#{@name}_graph_subgraphs.gv", graphDAGs.to_graphviz) if withOutputGraphviz
382
+ graphDAGs.vertices.each do |vertice|
383
+ ind = graphFull.vertices.index{|vert| vert.component == vertice.component}
384
+ graphFull.vertices[ind].layer = vertice.layer unless ind.nil?
385
+ end
386
+ File.write("#{@name}_graph_full.gv", graphFull.to_graphviz) if withOutputGraphviz
387
+ puts "Maximum number of layers: #{dags.collect{|dag| dag.vertices.collect{|vertice| vertice.layer}.reject{|l| l.nil?}.max}.reject{|m| m.nil?}.max}" unless quiet
388
+
389
+ puts "Writing static schedule for component simulation..." unless quiet
390
+ componentSchedulingStack = []
391
+ dags.each do |dag|
392
+ dag.vertices.sort{|verta, vertb| vertb.layer <=> verta.layer}.each{|vert| componentSchedulingStack << vert.component}
393
+ end
394
+ unless componentSchedulingStack.index{|comp| comp.class != BlifUtils::Netlist::LogicGate}.nil? then
395
+ raise "merde"
396
+ end
397
+
398
+ return componentSchedulingStack
399
+ end
400
+
401
+ end # BlifUtils::Netlist::Model
402
+
403
+ end # BlifUtils::Netlist
404
+
405
+ end # BlifUtils
406
+