blifutils 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,143 @@
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.rb'
21
+ require 'blifutils/layering.rb'
22
+
23
+
24
+ module BlifUtils
25
+
26
+ class Netlist
27
+
28
+ class Model
29
+
30
+ ##
31
+ # Returns the logic level of the circuit, nil if the model includes subcircuits, or false if the model contains combinatorial loops
32
+ def level
33
+ return nil unless self.is_self_contained?
34
+
35
+ graph = BlifUtils::NetlistGraph::Graph::create_from_model(self)
36
+
37
+ start_vertices = graph.vertices.select{|v| v.component.kind_of?(BlifUtils::Netlist::Latch) or v.component.output.isOutput}.uniq
38
+
39
+ res = catch(:combinatorial_loop_found) do
40
+ theMax = 0
41
+ visited_vertices = []
42
+ start_vertices.each do |start|
43
+ follow_combinatorial_path(graph, start, visited_vertices)
44
+ theMax = start.layer if start.layer > theMax
45
+ end
46
+ theMax
47
+ end
48
+
49
+ return false unless res
50
+ return res
51
+ end
52
+
53
+
54
+ private
55
+
56
+
57
+ def follow_combinatorial_path (graph, vertice, visited_vertices)
58
+ return unless vertice.layer.nil?
59
+ throw :combinatorial_loop_found if visited_vertices.include?(vertice)
60
+ visited_vertices << vertice
61
+
62
+ if vertice.component.kind_of?(BlifUtils::Netlist::Latch) then
63
+ my_layer = 0
64
+ else
65
+ my_layer = 1
66
+ end
67
+
68
+ the_max = 0
69
+
70
+ vertice.predecessors.each do |svert|
71
+ next if (svert == :input or svert == :output or svert.component.kind_of?(BlifUtils::Netlist::Latch))
72
+ follow_combinatorial_path(graph, svert, visited_vertices) if svert.layer.nil?
73
+ the_max = [the_max, svert.layer].max
74
+ end
75
+
76
+ vertice.layer = my_layer + the_max
77
+ end
78
+
79
+
80
+ public
81
+
82
+
83
+ def level_analysis (withOutputGraphviz: false, quiet: false)
84
+ return unless is_self_contained?
85
+
86
+ puts "Generating graph from model components..." unless quiet
87
+ graphFull = BlifUtils::NetlistGraph::Graph.create_from_model(self)
88
+ print "Extracting connected subgraphs... " unless quiet
89
+ graphDAGs = graphFull.get_graph_without_input_output_reg_cst_modinst
90
+ dags = graphDAGs.get_connected_subgraphs
91
+ puts "#{dags.length} subgraph#{dags.length > 1 ? 's' : ''} found"
92
+
93
+ print "Checking that there are no cycles in subgraphs...\n" unless quiet
94
+ # Check that all subgraphs are acyclic
95
+ dags.each_with_index do |dag, i|
96
+ unless dag.is_acyclic? then
97
+ str = "\nERROR: There is a combinatorial loop.\n This subgraph includes components:\n"
98
+ dag.vertices.each do |vertice|
99
+ str += " Component #{vertice.to_s}\n"
100
+ end
101
+ abort str
102
+ end
103
+ end
104
+ puts "No combinatorial loops found"
105
+
106
+ # Do graph layering
107
+ unless quiet then
108
+ print "Layering subgraphs...\n" unless withOutputGraphviz
109
+ end
110
+ maxDagSize = 0
111
+ maxDagLevel = 0
112
+ dags.each_with_index do |dag, i|
113
+ dag.assign_layers_to_vertices
114
+ dagSize = dag.vertices.length
115
+ dagLevel = dag.vertices.collect{|vertice| vertice.layer}.reject{|l| l.nil?}.max
116
+ maxDagSize = dagSize if dagSize != nil and maxDagSize < dagSize
117
+ maxDagLevel = dagLevel if dagLevel != nil and maxDagLevel < dagLevel
118
+ if withOutputGraphviz then
119
+ File.write("#{@name}_graph_DAG_#{i}.gv", dag.to_graphviz)
120
+ puts "Graph #{i.to_s.rjust(2)}: level #{dagLevel.to_s.rjust(2)}, size #{dagSize.to_s.rjust(2)}"
121
+ end
122
+
123
+ end
124
+
125
+ if withOutputGraphviz then
126
+ File.write("#{@name}_graph_subgraphs.gv", graphDAGs.to_graphviz)
127
+ graphDAGs.vertices.each do |vertice|
128
+ ind = graphFull.vertices.index{|vert| vert.component == vertice.component}
129
+ graphFull.vertices[ind].layer = vertice.layer unless ind.nil?
130
+ end
131
+ File.write("#{@name}_graph_full.gv", graphFull.to_graphviz)
132
+ end
133
+
134
+ puts "Maximum number of layers: #{maxDagLevel}"
135
+ puts "Maximum number of gate per subgraph: #{maxDagSize}"
136
+ end # BlifUtils::Netlist::Model::level_analysis
137
+
138
+ end # BlifUtils::Netlist::Model
139
+
140
+ end # BlifUtils::Netlist
141
+
142
+ end # BlifUtils
143
+
@@ -0,0 +1,133 @@
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 'rltk/lexer'
21
+
22
+
23
+ module BlifUtils
24
+
25
+ module BlifUtils::Language
26
+
27
+ class Lexer < RLTK::Lexer
28
+ # Comment rule
29
+ rule(/#.*(?=\r?\n)/)
30
+
31
+ # Ignore kiss state machine
32
+ rule(/\.start_kiss/) { push_state :kiss }
33
+ rule(/\.end_kiss/, :kiss) { pop_state }
34
+ rule(/./, :kiss)
35
+
36
+ # Skip line concatenation, new lines and whitespaces
37
+ rule(/\\\r?\n/)
38
+ rule(/[\r\n\f\v]+\s*/)
39
+ rule(/[ \t\r]/)
40
+
41
+ # Command rules
42
+ rule(/\.model/) { :MODEL }
43
+ rule(/\.end/) { :END }
44
+ rule(/\.inputs/) { :INPUTS }
45
+ rule(/\.outputs/) { :OUTPUTS }
46
+ rule(/\.clock/) { :CLOCK }
47
+ rule(/\.blackbox/) { :BLACKBOX }
48
+ rule(/\.names/) { :NAMES }
49
+ rule(/\.latch/) { push_state(:latch); :LATCH }
50
+ rule(/\.subckt/) { :SUBCKT }
51
+ rule(/\.search/) { :SEARCH }
52
+
53
+ # The equal tocken for formal/actual lists
54
+ rule(/=/) { :EQUAL }
55
+
56
+ # Latch specific tockens
57
+ rule(/(fe)|(re)|(ah)|(al)|(as)/, :latch) { |s| [:LATCHTYPE, s.to_sym] }
58
+ rule(/[0123]/, :latch) { |s| [:LATCHINITVAL, s.to_i] }
59
+ rule(/[^\s=]+/, :latch) { |s| [:IDENTIFIER, s] }
60
+ rule(/#.*(?=\r?\n)/, :latch)
61
+ rule(/\\\r?\n/, :latch)
62
+ rule(/[ \t\r]/, :latch)
63
+ rule(/[\r\n\f\v]+\s*/, :latch) { pop_state }
64
+
65
+ # Names cover
66
+ rule(/([01-]+\s+)?[01][ \t]*(?=\r?\n)/) { |s| [:COVER, s.sub(/^\s+/,'').sub(/\s+$/,'')] }
67
+
68
+ # Ignored commands
69
+ rule(/\.area\.*\r?\n/)
70
+ rule(/\.cycle\.*\r?\n/)
71
+ rule(/\.clock_event\.*\r?\n/)
72
+ rule(/\.delay\.*\r?\n/)
73
+ rule(/\.default_input_arrival\.*\r?\n/)
74
+ rule(/\.default_input_drive\.*\r?\n/)
75
+ rule(/\.default_max_input_load\.*\r?\n/)
76
+ rule(/\.default_output_load\.*\r?\n/)
77
+ rule(/\.default_output_required\.*\r?\n/)
78
+ rule(/\.exdc\.*\r?\n/)
79
+ rule(/\.gate\.*\r?\n/)
80
+ rule(/\.input_arrival\.*\r?\n/)
81
+ rule(/\.input_drive\.*\r?\n/)
82
+ rule(/\.max_input_load\.*\r?\n/)
83
+ rule(/\.mlatch\.*\r?\n/)
84
+ rule(/\.output_required\.*\r?\n/)
85
+ rule(/\.output_load\.*\r?\n/)
86
+ rule(/\.wire_load_slope\.*\r?\n/)
87
+ rule(/\.wire\.*\r?\n/)
88
+
89
+ # Identifier
90
+ rule(/[^\s=]+/) { |s| [:IDENTIFIER, s] }
91
+
92
+ end # BlifUtils::Language::Lexer
93
+
94
+ end # BlifUtils::Language
95
+
96
+ end # BlifUtils
97
+
98
+
99
+
100
+ if $0 == __FILE__ then
101
+ ts = Time.now
102
+ tokens = BlifUtils::Language::Lexer::lex_file(ARGV[0])
103
+ te = Time.now
104
+
105
+ tokens.each do |el|
106
+ if el.value then
107
+ puts "#{el.type.to_s} -> \"#{el.value}\""
108
+ else
109
+ puts "#{el.type.to_s}"
110
+ end
111
+ end
112
+
113
+ puts '-'*80
114
+ puts "Lexing time: #{te - ts} s"
115
+ puts '-'*80
116
+
117
+ puts "MODEL #{tokens.count{|t| t.type == :MODEL}}"
118
+ puts "END #{tokens.count{|t| t.type == :END}}"
119
+ puts "INPUTS #{tokens.count{|t| t.type == :INPUTS}}"
120
+ puts "OUTPUTS #{tokens.count{|t| t.type == :OUTPUTS}}"
121
+ puts "CLOCK #{tokens.count{|t| t.type == :CLOCK}}"
122
+ puts "BLACKBOX #{tokens.count{|t| t.type == :BLACKBOX}}"
123
+ puts "NAMES #{tokens.count{|t| t.type == :NAMES}}"
124
+ puts "LATCH #{tokens.count{|t| t.type == :LATCH}}"
125
+ puts "LATCHTYPE #{tokens.count{|t| t.type == :LATCHTYPE}}"
126
+ puts "LATCHINITVAL #{tokens.count{|t| t.type == :LATCHINITVAL}}"
127
+ puts "SUBCKT #{tokens.count{|t| t.type == :SUBCKT}}"
128
+ puts "SEARCH #{tokens.count{|t| t.type == :SEARCH}}"
129
+ puts "EQUAL #{tokens.count{|t| t.type == :EQUAL}}"
130
+ puts "COVER #{tokens.count{|t| t.type == :COVER}}"
131
+ puts "IDENTIFIER #{tokens.count{|t| t.type == :IDENTIFIER}}"
132
+ end
133
+
@@ -0,0 +1,808 @@
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
+ module BlifUtils
21
+
22
+ class Netlist
23
+
24
+ class IO
25
+ attr_reader :name
26
+ attr_accessor :net
27
+
28
+ def initialize (name, net)
29
+ @name = name
30
+ @net = net
31
+ end
32
+
33
+ def to_s
34
+ return "#{if @net.isInput then 'Input' elsif @net.isOutput then 'Output' else 'IO' end} #{@net.name} / #{@name}"
35
+ end
36
+ end # BlifUtils::Netlist::IO
37
+
38
+
39
+ class Component
40
+ def isGate?
41
+ return self.kind_of?(BlifUtils::Netlist::LogicGate)
42
+ end
43
+
44
+ def isLatch?
45
+ return self.kind_of?(BlifUtils::Netlist::Latch)
46
+ end
47
+
48
+ def isSubcircuit?
49
+ return self.kind_of?(BlifUtils::Netlist::SubCircuit)
50
+ end
51
+ end # BlifUtils::Netlist::Component
52
+
53
+
54
+ class LogicGate < Component
55
+ attr_accessor :inputs # [Net, ... ] inputs output
56
+ attr_accessor :output # Net | |
57
+ attr_reader :singleOutputCoverList # [[[val, ...], val], ... ] val is 0, 1 or 2 for -
58
+
59
+ def initialize (inputs, output, single_output_cover_list)
60
+ @inputs = inputs
61
+ @output = output
62
+ @singleOutputCoverList = single_output_cover_list
63
+ end
64
+
65
+
66
+ def input_bit_width
67
+ return @inputs.length
68
+ end
69
+
70
+
71
+ def to_blif
72
+ str = ".names #{@inputs.collect{|net| net.name}.join(' ')} #{@output.name}\n"
73
+ @singleOutputCoverList.each do |inputs_output|
74
+ str += "#{inputs_output[0].collect{|strbit| case strbit when 0 then '0' when 1 then '1' else '-' end}.join}#{unless inputs_output[0].empty? then ' ' end}#{inputs_output[1]}\n"
75
+ end
76
+ return str
77
+ end
78
+
79
+
80
+ def is_buffer?
81
+ return (@inputs.length == 1 and @singleOutputCoverList.length == 1 and @singleOutputCoverList[0][0][0] == 1 and @singleOutputCoverList[0][1] == 1)
82
+ end
83
+
84
+
85
+ def is_constant?
86
+ return (@inputs.length == 0)
87
+ end
88
+
89
+
90
+ def to_s
91
+ return "Logic Gate \"#{@output.name}\""
92
+ end
93
+
94
+
95
+ def get_LookUpTable
96
+ outputCovered = @singleOutputCoverList.collect{|inputs_output| inputs_output[1]}.uniq
97
+ if outputCovered.length != 1 then
98
+ abort "ERROR: Output cover list of gate \"#{@output.name}\" covers 1 and 0:\n#{@singleOutputCoverList.collect{|ins_out| " #{ins_out[0].collect{|ins| ins.to_s}.join.gsub('2','-')} #{ins_out[1]}\n"}.join}"
99
+ end
100
+
101
+ statedVal = outputCovered[0]
102
+ if statedVal == 0 then
103
+ defaultVal = 1
104
+ else
105
+ defaultVal = 0
106
+ end
107
+
108
+ tableLength = 2**(@inputs.length)
109
+ #table = (0 ... tableLength).collect{defaultVal}
110
+ table = Array.new(tableLength){defaultVal}
111
+
112
+ statedIndexes = []
113
+ @singleOutputCoverList.each{|inputs_output| statedIndexes += expand_input_cover_list(inputs_output[0])}
114
+ statedIndexes.uniq.each{|ind| table[ind] = statedVal}
115
+
116
+ return table
117
+ end
118
+
119
+
120
+ private
121
+
122
+
123
+ def expand_input_cover_list (icl)
124
+ # icl is an array of 0, 1 and 2 for '-' #
125
+
126
+ expanded = []
127
+
128
+ index = icl.index(2)
129
+ if index.nil? then
130
+ expanded << icl.reverse.collect{|n| n.to_s}.join.to_i(2)
131
+ else
132
+ newIcl1 = icl.collect{|n| n}
133
+ newIcl2 = icl.collect{|n| n}
134
+ newIcl1[index] = 0
135
+ newIcl2[index] = 1
136
+ expanded += expand_input_cover_list(newIcl1)
137
+ expanded += expand_input_cover_list(newIcl2)
138
+ end
139
+
140
+ return expanded
141
+ end
142
+
143
+ end # BlifUtils::Netlist::LogicGate
144
+
145
+
146
+ class Latch < Component
147
+ attr_accessor :input # Net
148
+ attr_accessor :output # Net
149
+ attr_reader :initValue # 0, 1, 2 for don't care and 3 for unknown
150
+ attr_reader :ctrlType # :fe, :re, :ah, :al, :as
151
+ attr_reader :ctrlSig # Net
152
+
153
+ def initialize (input, output, initValue, ctrlType = :re, ctrlSig = nil)
154
+ @input = input
155
+ @output = output
156
+ @initValue = initValue
157
+ @ctrlType = ctrlType
158
+ @ctrlSig = ctrlSig
159
+ end
160
+
161
+
162
+ def to_blif
163
+ return ".latch #{@input.name} #{@output.name} #{@ctrlType} #{@ctrlSig.nil? ? 'NIL' : @ctrlSig} #{@initValue}\n"
164
+ end
165
+
166
+
167
+ def inputs
168
+ return [@input]
169
+ end
170
+
171
+
172
+ def name
173
+ return @output.name
174
+ end
175
+
176
+
177
+ def to_s
178
+ return "Latch \"#{@output.name}\""
179
+ end
180
+
181
+ end # BlifUtils::Netlist::Latch
182
+
183
+
184
+ class SubCircuit < Component
185
+ attr_reader :modelName # String
186
+ attr_accessor :inputFormalAcutalList # [IO, ... ]
187
+ attr_accessor :outputFormalAcutalList # [IO, ... ]
188
+
189
+ def initialize (modelName, inputFormalAcutalList, outputFormalAcutalList)
190
+ @modelName = modelName
191
+ @inputFormalAcutalList = inputFormalAcutalList
192
+ @outputFormalAcutalList = outputFormalAcutalList
193
+ end
194
+
195
+
196
+ def to_blif
197
+ str = ''
198
+ tmpstr = ".subckt #{@modelName}"
199
+ @inputFormalAcutalList.collect{|io| " #{io.name}=#{io.net.name}"}.each do |fa|
200
+ if tmpstr.length + fa.length + 1 > 80 then
201
+ tmpstr += " \\\n"
202
+ str += tmpstr
203
+ tmpstr = ''
204
+ end
205
+ tmpstr += fa
206
+ end
207
+ @outputFormalAcutalList.collect{|io| " #{io.name}=#{io.net.name}"}.each do |fa|
208
+ if tmpstr.length + fa.length + 1 > 80 then
209
+ tmpstr += " \\\n"
210
+ str += tmpstr
211
+ tmpstr = ''
212
+ end
213
+ tmpstr += fa
214
+ end
215
+ str += tmpstr + "\n"
216
+ return str
217
+ end
218
+
219
+
220
+ def to_s
221
+ return "Sub Circuit \"#{@modelName}\""
222
+ end
223
+
224
+ end # BlifUtils::Netlist::SubCircuit
225
+
226
+
227
+ class Fanout
228
+ attr_accessor :target # Component
229
+ attr_accessor :index # Integer
230
+
231
+ def initialize (target, index)
232
+ @target = target
233
+ @index = index
234
+ end
235
+ end # BlifUtils::Netlist::Fanout
236
+
237
+
238
+ class Net
239
+ attr_accessor :name # String
240
+ attr_accessor :driver # Component
241
+ attr_accessor :fanouts # [Fanout, ... ]
242
+ attr_accessor :isInput # true/false
243
+ attr_accessor :isOutput # true/false
244
+
245
+ def initialize (name, driver, fanouts, isInput, isOutput)
246
+ @name = name
247
+ @driver = driver
248
+ @fanouts = fanouts
249
+ @isInput = isInput
250
+ @isOutput = isOutput
251
+ end
252
+
253
+ ## To prevent inspect recurtion ##
254
+ def inspect
255
+ return "#<BlifUtils::Netlist::Net:#{object_id} @name=#{@name.inspect}>"
256
+ end
257
+
258
+ def to_s
259
+ return @name
260
+ end
261
+
262
+
263
+ def isIO?
264
+ return (@isInput or @isOutput)
265
+ end
266
+
267
+ end # BlifUtils::Netlist::Net
268
+
269
+
270
+ class Model
271
+ attr_reader :name # String
272
+ attr_reader :inputs # [IO, ... ]
273
+ attr_reader :outputs # [IO, ... ]
274
+ attr_accessor :components # [Component, ... ]
275
+ attr_accessor :nets # [Net, ... ]
276
+ attr_accessor :clocks # [Net, ... ]
277
+ attr_reader :isBlackBox
278
+
279
+ def initialize (name, inputs, outputs, components, nets, clocks, isBlackBox = false)
280
+ @name = name
281
+ @inputs = inputs
282
+ @outputs = outputs
283
+ @components = components
284
+ @nets = nets
285
+ @isBlackBox = isBlackBox
286
+ @clocks = clocks
287
+ end
288
+
289
+
290
+
291
+ ## To prevent 36548 lines of output ##
292
+ def inspect
293
+ return "#<BlifUtils::Netlist::Model:#{object_id} @name=#{@name.inspect}>"
294
+ end
295
+
296
+
297
+ def is_blackbox?
298
+ return not(not(@isBlackBox))
299
+ end
300
+
301
+
302
+ def analyze
303
+ bannerTitle = " #{@isBlackBox ? 'Black box' : 'Model'} \"#{@name}\" analysis "
304
+ bannerSize = [40, bannerTitle.length].max
305
+ str = '+' + ''.ljust(bannerSize,'-') + "+\n"
306
+ str += '|' + bannerTitle.center(bannerSize) + "|\n"
307
+ str += '+' + ''.ljust(bannerSize,'-') + "+\n"
308
+ str += "#{@isBlackBox ? 'Black box' : 'Model'} \"#{@name}\"\n"
309
+ str += " Inputs: #{@inputs.length}\n"
310
+ str += " Outputs: #{@outputs.length}\n"
311
+ return str if @isBlackBox
312
+ str += " Nets: #{@nets.length}\n"
313
+ str += " Edges: #{@nets.collect{|net| net.fanouts.length}.inject(:+)}\n"
314
+ str += " Nodes: #{@components.length}\n"
315
+ str += " Latches: #{@components.select{|comp| comp.isLatch?}.length}\n"
316
+ gates = @components.select{|comp| comp.isGate?}
317
+ nbGates = gates.length
318
+ str += " Logic gates: #{nbGates}\n"
319
+ subcircuits = @components.select{|comp| comp.isSubcircuit?}
320
+ nbSubcircuits = subcircuits.length
321
+ str += " Sub circuits: #{nbSubcircuits}\n"
322
+
323
+ if nbGates > 0 then
324
+ str += " Gates repartition:\n"
325
+ repartition = Hash.new(0)
326
+ gates.each{|gate| repartition[gate.inputs.length] += 1}
327
+ Hash[repartition.sort].each do |key, val|
328
+ str += " #{key.to_s.rjust(2)} input#{key > 1 ? 's:' : ': '} #{val.to_s.rjust(4)} #{(val*100/(nbGates.to_f)).round(1).to_s.rjust(5)}%\n"
329
+ end
330
+
331
+ nbBuffers = gates.select{|gate| gate.is_buffer?}.length
332
+ nbConstants = gates.select{|gate| gate.is_constant?}.length
333
+ str += " Buffers: #{nbBuffers}\n" if nbBuffers > 0
334
+ str += " Constants: #{nbConstants}\n" if nbConstants > 0
335
+ end
336
+
337
+ if nbSubcircuits > 0 then
338
+ str += " Sub circuits repartition:\n"
339
+ repartition = Hash.new(0)
340
+ subcircuits.each{|subckt| repartition[subckt.modelName] += 1}
341
+ repartition.sort_by{|key, val| val}.each do |key_val|
342
+ str += " #{key_val[0]}: #{key_val[1]}\n"
343
+ end
344
+ end
345
+
346
+ return str
347
+ end
348
+
349
+
350
+ def analyze_to_hash
351
+
352
+ res = {}
353
+ res[:name] = String.new(@name)
354
+ res[:nb_inputs] = @inputs.length
355
+ res[:nb_outputs] = @outputs.length
356
+ res[:is_blackbox] = @isBlackBox
357
+ return res if @isBlackBox
358
+ res[:nb_nets] = @nets.length
359
+ res[:nb_edges] = @nets.collect{|net| net.fanouts.length}.inject(:+)
360
+ res[:nb_nodes] = @components.length
361
+ res[:nb_latches] = @components.count{|comp| comp.isLatch?}
362
+ gates = @components.select{|comp| comp.isGate?}
363
+ res[:nb_gates] = gates.length
364
+ res[:nb_subckt] = @components.count{|comp| comp.isSubcircuit?}
365
+
366
+ if res[:nb_gates] > 0 then
367
+ repartition = {}
368
+ gates.collect{|g| g.inputs.length}.uniq.sort.each{|n| repartition[n] = 0}
369
+ gates.each{|gate| repartition[gate.inputs.length] += 1}
370
+ gh = {}
371
+ gh[:gates_per_nb_inputs] = repartition
372
+ gh[:nb_buffers] = gates.count{|gate| gate.is_buffer?}
373
+ gh[:nb_constants] = gates.count{|gate| gate.is_constant?}
374
+ res[:gates] = gh
375
+ end
376
+
377
+ if res[:nb_subckt] > 0 then
378
+ repartition = Hash.new(0)
379
+ subcircuits.each{|subckt| repartition[subckt.modelName] += 1}
380
+ res[:subckts] = repartition
381
+ end
382
+
383
+ return res
384
+ end
385
+
386
+
387
+ def is_self_contained?
388
+ return @components.index{|comp| comp.class == BlifUtils::Netlist::SubCircuit}.nil?
389
+ end
390
+
391
+
392
+ def to_blif
393
+ str = ".model #{@name}\n"
394
+ if @isBlackBox then
395
+ tmpstr = ".inputs"
396
+ unless @inputs.empty? then
397
+ @inputs.collect{|io| io.name}.each do |iname|
398
+ if tmpstr.length + iname.length + 3 > 80 then
399
+ tmpstr += " \\\n"
400
+ str += tmpstr
401
+ tmpstr = ''
402
+ end
403
+ tmpstr += ' ' + iname
404
+ end
405
+ str += tmpstr + "\n"
406
+ end
407
+ tmpstr = ".outputs"
408
+ unless @inputs.empty? then
409
+ @outputs.collect{|io| io.name}.each do |iname|
410
+ if tmpstr.length + iname.length + 3 > 80 then
411
+ tmpstr += " \\\n"
412
+ str += tmpstr
413
+ tmpstr = ''
414
+ end
415
+ tmpstr += ' ' + iname
416
+ end
417
+ str += tmpstr + "\n"
418
+ end
419
+ str += ".blackbox\n.end\n"
420
+ return str
421
+ end
422
+ tmpstr = ".inputs"
423
+ unless @inputs.empty? then
424
+ @inputs.collect{|io| io.net.name}.each do |iname|
425
+ if tmpstr.length + iname.length + 3 > 80 then
426
+ tmpstr += " \\\n"
427
+ str += tmpstr
428
+ tmpstr = ''
429
+ end
430
+ tmpstr += ' ' + iname
431
+ end
432
+ str += tmpstr + "\n"
433
+ end
434
+ tmpstr = ".outputs"
435
+ unless @inputs.empty? then
436
+ @outputs.collect{|io| io.net.name}.each do |iname|
437
+ if tmpstr.length + iname.length + 3 > 80 then
438
+ tmpstr += " \\\n"
439
+ str += tmpstr
440
+ tmpstr = ''
441
+ end
442
+ tmpstr += ' ' + iname
443
+ end
444
+ str += tmpstr + "\n"
445
+ end
446
+ #str += "\n"
447
+ @components.select{|comp| comp.isSubcircuit?}.each{|subckt| str += subckt.to_blif}
448
+ #str += "\n"
449
+ @components.select{|comp| comp.isLatch?}.each{|latch| str += latch.to_blif}
450
+ #str += "\n"
451
+ @components.select{|comp| comp.isGate?}.each{|gate| str += gate.to_blif}
452
+ str += ".end\n"
453
+ return str
454
+ end
455
+
456
+
457
+ def clone
458
+ ## stack level too deep (SystemStackError) if self is too big... =(
459
+ return Marshal.load(Marshal.dump(self))
460
+ end
461
+
462
+
463
+ def rename_nets
464
+ i = 0
465
+ @nets.each do |net|
466
+ unless net.isInput then
467
+ net.name = "n#{i.to_s(16).upcase}"
468
+ i += 1
469
+ end
470
+ end
471
+ end
472
+
473
+
474
+ def add_output_buffers
475
+ @outputs.each_with_index do |oIO, i|
476
+ newNet = BlifUtils::Netlist::Net.new(oIO.name, nil, [BlifUtils::Netlist::Fanout.new(:output, i)], false, true)
477
+ newBuffer = BlifUtils::Netlist::LogicGate.new([oIO.net], newNet, [[[1], 1]])
478
+ newNet.driver = newBuffer
479
+ fanoutIndexToDelete = oIO.net.fanouts.index{|fanout| fanout.target == :output and fanout.index == i}
480
+ raise "Cannot find actual fanout to delete for output" if fanoutIndexToDelete.nil?
481
+ oIO.net.fanouts[fanoutIndexToDelete].target = newBuffer
482
+ oIO.net.fanouts[fanoutIndexToDelete].index = 0
483
+ oIO.net.isOutput = false
484
+ oIO.net = newNet
485
+ @components << newBuffer
486
+ @nets << newNet
487
+ end
488
+ end
489
+
490
+
491
+ def remove_buffers
492
+ buffers = @components.select{|comp| comp.isGate?}.select{|gate| gate.is_buffer?}
493
+
494
+ buffers.each do |buffer|
495
+ netA = buffer.inputs[0]
496
+ netB = buffer.output
497
+
498
+ netAbufferFanoutIndex = netA.fanouts.index{|fanout| fanout.target == buffer and fanout.index == 0}
499
+ if netAbufferFanoutIndex.nil? then
500
+ raise "Cannot find buffer fanout in net"
501
+ end
502
+ netA.fanouts.delete_at(netAbufferFanoutIndex)
503
+ netB.fanouts.each do |fanout|
504
+ if fanout.target.class == BlifUtils::Netlist::LogicGate then
505
+ fanout.target.inputs[fanout.index] = netA
506
+ elsif fanout.target.class == BlifUtils::Netlist::Latch then
507
+ fanout.target.input = netA
508
+ elsif fanout.target.class == BlifUtils::Netlist::SubCircuit then
509
+ fanout.target.inputFormalAcutalList[fanout.index].net = netA
510
+ elsif fanout.target == :output then
511
+ @outputs[fanout.index].net = netA
512
+ else
513
+ raise "WTF?"
514
+ end
515
+ netA.fanouts << fanout
516
+ end
517
+ if netB.isOutput then
518
+ netA.isOutput = true
519
+ end
520
+ @nets.delete(netB)
521
+ @components.delete(buffer)
522
+ end
523
+
524
+ buffers = @components.select{|comp| comp.isGate?}.select{|gate| gate.is_buffer?}
525
+ end
526
+
527
+ end # BlifUtils::Netlist::Model
528
+
529
+
530
+ def initialize
531
+ @models = []
532
+ end
533
+
534
+
535
+ def models
536
+ return @models
537
+ end
538
+
539
+
540
+ def first_model
541
+ return @models[0]
542
+ end
543
+
544
+
545
+ def length
546
+ return @models.length
547
+ end
548
+
549
+
550
+ def model_names
551
+ return @models.collect{|mod| mod.name}
552
+ end
553
+
554
+
555
+ def add_model (model)
556
+ if include?(model.name) then
557
+ abort "ERROR: Model \"#{model.name}\" is already defined in the model collection"
558
+ end
559
+ @models << model
560
+ self
561
+ end
562
+
563
+
564
+ def add_model_to_front (model)
565
+ if include?(model.name) then
566
+ abort "ERROR: Model \"#{model.name}\" is already defined in the model collection".ligth_red
567
+ end
568
+ @models.unshift(model)
569
+ self
570
+ end
571
+
572
+
573
+ def include? (model)
574
+ if model.class == String then
575
+ return model_names.include?(model)
576
+ elsif model.class == BlifUtils::Netlist::Model then
577
+ return @models.include?(model)
578
+ end
579
+ end
580
+
581
+
582
+ def remove_model (model)
583
+ if model.class == String then
584
+ @models.delete_if{|momo| momo.name == model}
585
+ elsif model.class == BlifUtils::Netlist::Model then
586
+ @models.delete(model)
587
+ end
588
+ self
589
+ end
590
+
591
+
592
+ def get_model_by_name (name)
593
+ return @models.select{|mod| mod.name == name}[0]
594
+ end
595
+
596
+
597
+ def analyze
598
+ str = "Model collection contains #{length} models\n"
599
+ @models.each{|model| str += model.analyze}
600
+ return str
601
+ end
602
+
603
+
604
+ def remove_unused_models
605
+ used_models = []
606
+ find_used_models_recursive(used_models, first_model)
607
+ model_names.each do |modName|
608
+ unless used_models.include?(modName) then
609
+ remove_model(modName)
610
+ end
611
+ end
612
+ self
613
+ end
614
+
615
+
616
+ def to_blif
617
+ return @models.collect{|mod| mod.to_blif}.join("\n")
618
+ end
619
+
620
+
621
+ def flatten (modelName = nil, withOutputBuffers = true, quiet: false)
622
+ modelName = first_model.name if modelName.nil?
623
+ dedel = get_model_by_name(modelName)
624
+ if dedel.nil?
625
+ abort "ERROR: Model \"#{modelName}\" not found."
626
+ end
627
+ if dedel.is_self_contained? then
628
+ return dedel.clone
629
+ end
630
+ ####################################
631
+ update_clocks()
632
+ ####################################
633
+ flattenedModel = flatten_model_recursive(modelName, [], quiet: quiet)
634
+ flattenedModel.remove_buffers
635
+ flattenedModel.rename_nets
636
+ flattenedModel.add_output_buffers if withOutputBuffers
637
+ return flattenedModel
638
+ end
639
+
640
+
641
+ def clear
642
+ @models = []
643
+ self
644
+ end
645
+
646
+
647
+ def update_clocks
648
+ @models.each do |model|
649
+ update_clocks_for_model_recursive(model)
650
+ end
651
+ self
652
+ end
653
+
654
+
655
+ private
656
+
657
+
658
+ def update_clocks_for_model_recursive (model)
659
+ childrenClocks = []
660
+ model.components.select{|comp| comp.isSubcircuit?}.each do |subckt|
661
+ referencedModel = get_model_by_name(subckt.modelName)
662
+ if referencedModel.nil? then
663
+ STDERR.puts "WARNING: update_clocks(): Model \"#{subckt.modelName}\" referenced from model \"#{model.name}\" is not is the model collection,\n cannot determine if it uses any clock."
664
+ next
665
+ end
666
+ if referencedModel.isBlackBox then
667
+ STDERR.puts "WARNING: update_clocks(): Model \"#{subckt.modelName}\" referenced from model \"#{model.name}\" is a black box,\n cannot determine if it uses any clock."
668
+ next
669
+ end
670
+ update_clocks_for_model_recursive(referencedModel)
671
+ childrenClocks += referencedModel.clocks
672
+ end
673
+ childrenClocks.uniq!
674
+ childrenClocks.each do |clkname|
675
+ model.clocks << clkname unless model.clocks.include?(clkname)
676
+ end
677
+ model.clocks.each do |clkname|
678
+ next if model.inputs.collect{|io| io.name}.include?(clkname)
679
+ newClkNet = BlifUtils::Netlist::Net.new(clkname, nil, [], true, false)
680
+ newClkIo = BlifUtils::Netlist::IO.new(clkname, newClkNet)
681
+ model.inputs.unshift(newClkIo)
682
+ end
683
+
684
+ model.components.select{|comp| comp.isSubcircuit?}.each do |subckt|
685
+ referencedModel = get_model_by_name(subckt.modelName)
686
+ next if referencedModel.nil? or referencedModel.isBlackBox
687
+ referencedModel.clocks.each do |clkname|
688
+ unless (subckt.inputFormalAcutalList.collect{|io| io.name} + subckt.outputFormalAcutalList.collect{|io| io.name}).include?(clkname) then
689
+ actualClkNet = model.inputs.find{|io| io.name == clkname}.net
690
+ actualClkNet.fanouts << BlifUtils::Netlist::Fanout.new(subckt, 0)
691
+ newIo = BlifUtils::Netlist::IO.new(clkname, actualClkNet)
692
+
693
+ subckt.inputFormalAcutalList.each_with_index do |io, i|
694
+ net = io.net
695
+ fanout = net.fanouts.find{|fnt| fnt.target == subckt and fnt.index == i}
696
+ raise "Trouve pas le fanout de l'IO net:#{io.net.name} name:#{io.name} de la reference:#{subckt.modelName} depuis:#{model.name}" if fanout.nil?
697
+ fanout.index += 1
698
+ end
699
+
700
+ subckt.inputFormalAcutalList.unshift(newIo)
701
+ end
702
+ end
703
+ end
704
+ end
705
+
706
+
707
+ def find_used_models_recursive (used_models, model)
708
+ return if used_models.include?(model.name)
709
+ used_models << model.name
710
+ model.components.select{|comp| comp.isSubcircuit?}.collect{|subc| subc.modelName}.uniq.each do |modname|
711
+ find_used_models_recursive(used_models, get_model_by_name(modname))
712
+ end
713
+ end
714
+
715
+
716
+ def flatten_model_recursive (modelName, parentModelList, quiet: false)
717
+ puts "Flattening model \"#{modelName}\"" unless quiet
718
+ # Retreive the model to be flattened
719
+ originalModToFlatten = get_model_by_name(modelName)
720
+ if originalModToFlatten.nil? then
721
+ errStr = "ERROR: The model collection does not contains a model named \"#{modelName}\"."
722
+ unless parentModelList.empty? then
723
+ instancierFileName = get_model_by_name(parentModelList[-1][0]).originFileName
724
+ errStr += "\n Model \"#{modelName}\" is referenced in model \"#{parentModelList[-1]}\""
725
+ end
726
+ abort errStr
727
+ end
728
+ if originalModToFlatten.isBlackBox then
729
+ abort "ERROR: Cannot flatten black box \"#{originalModToFlatten.name}\""
730
+ end
731
+
732
+ # Check that there is no recursive instanciation of the same model (would get infinite loop)
733
+ if parentModelList.include?(modelName) then
734
+ errStr = "ERROR: Recursive reference of model \"#{modelName}\".\n Reference stack:\n"
735
+ parentModelList.each{|pmn| errStr += " #{pmn}\n"}
736
+ abort errStr
737
+ end
738
+
739
+ # Clone it to get a new object copy to work with
740
+ currentModel = originalModToFlatten.clone
741
+
742
+ # Loop on each sub circuits in the current model
743
+ currentModel.components.select{|comp| comp.isSubcircuit?}.each do |subckt|
744
+ next if get_model_by_name(subckt.modelName).isBlackBox
745
+
746
+ # Get a flatten clone of the referenced model
747
+ instanciatedModel = flatten_model_recursive(subckt.modelName, parentModelList + [currentModel.name], quiet: quiet)
748
+
749
+ # Merge the inputs #
750
+ instanciatedModel.inputs.each do |fio|
751
+ # Find the IO aio whose formal corresponds to fio #
752
+ actualIOindex = subckt.inputFormalAcutalList.index{|iaio| iaio.name == fio.name}
753
+ if actualIOindex.nil? then
754
+ abort "ERROR: In model \"#{currentModel.name}\": in reference to model \"#{instanciatedModel.name}\": input \"#{fio.name}\" is not driven."
755
+ end
756
+ aio = subckt.inputFormalAcutalList[actualIOindex]
757
+ # aio.net -> fio.net
758
+ newBuffer = BlifUtils::Netlist::LogicGate.new([aio.net], fio.net, [[[1], 1]])
759
+ aFanoutIndexToDelete = aio.net.fanouts.index{|fanout| fanout.target == subckt and fanout.index == actualIOindex}
760
+ #####################################################################################
761
+ raise "Cannot find actual fanout to delete for input" if aFanoutIndexToDelete.nil?
762
+ aio.net.fanouts[aFanoutIndexToDelete].target = newBuffer
763
+ aio.net.fanouts[aFanoutIndexToDelete].index = 0
764
+ fio.net.driver = newBuffer
765
+ fio.net.isInput = false
766
+ currentModel.components << newBuffer
767
+ #####################################################################################
768
+ #unless aFanoutIndexToDelete.nil? then
769
+ # aio.net.fanouts[aFanoutIndexToDelete].target = newBuffer
770
+ # aio.net.fanouts[aFanoutIndexToDelete].index = 0
771
+ # fio.net.driver = newBuffer
772
+ # fio.net.isInput = false
773
+ # currentModel.components << newBuffer
774
+ #end
775
+ #####################################################################################
776
+ end
777
+
778
+ # Merge the outputs #
779
+ instanciatedModel.outputs.each_with_index do |fio, oind|
780
+ # Find the IO aio whose formal corresponds to fio #
781
+ actualIOindex = subckt.outputFormalAcutalList.index{|iaio| iaio.name == fio.name}
782
+ if actualIOindex.nil? then
783
+ abort "ERROR: In model \"#{currentModel.name}\": in reference to model \"#{instanciatedModel.name}\": output \"#{fio.name}\" has no fanout."
784
+ end
785
+ aio = subckt.outputFormalAcutalList[actualIOindex]
786
+ # fio.net -> aio.net
787
+ newBuffer = BlifUtils::Netlist::LogicGate.new([fio.net], aio.net, [[[1], 1]])
788
+ fFanoutIndexToDelete = fio.net.fanouts.index{|fanout| fanout.target == :output and fanout.index == oind}
789
+ raise "Cannot find actual fanout to delete for output" if fFanoutIndexToDelete.nil?
790
+ fio.net.fanouts[fFanoutIndexToDelete].target = newBuffer
791
+ fio.net.fanouts[fFanoutIndexToDelete].index = 0
792
+ aio.net.driver = newBuffer
793
+ fio.net.isOutput = false
794
+ currentModel.components << newBuffer
795
+ end
796
+
797
+ currentModel.components.delete(subckt)
798
+ currentModel.components += instanciatedModel.components
799
+ currentModel.nets += instanciatedModel.nets
800
+ end
801
+
802
+ return currentModel
803
+ end
804
+
805
+ end # BlifUtils::Netlist
806
+
807
+ end # BlifUtils
808
+