blifutils 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+