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.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +377 -0
- data/bin/blifutils +184 -0
- data/examples/zpu/compile_zpu_program/Makefile +114 -0
- data/examples/zpu/compile_zpu_program/README.md +10 -0
- data/examples/zpu/compile_zpu_program/main.c +68 -0
- data/examples/zpu/simulate_zpu.rb +23 -0
- data/examples/zpu/testbench_zpu.cc +132 -0
- data/examples/zpu/zpu_helloworld.bin +0 -0
- data/examples/zpu/zpu_mem16.blif +3519 -0
- data/examples/zpu/zpu_mem16.piccolo +351 -0
- data/lib/blifutils.rb +31 -0
- data/lib/blifutils/ast.rb +185 -0
- data/lib/blifutils/blif_to_vhdl.rb +180 -0
- data/lib/blifutils/elaborator.rb +257 -0
- data/lib/blifutils/layering.rb +406 -0
- data/lib/blifutils/level_analyzer.rb +143 -0
- data/lib/blifutils/lexer.rb +133 -0
- data/lib/blifutils/netlist.rb +808 -0
- data/lib/blifutils/parser.rb +251 -0
- data/lib/blifutils/simulator_generator.rb +342 -0
- data/share/blimulator_cpp_classes.cc +446 -0
- data/share/blimulator_cpp_classes.hh +136 -0
- data/test/sqrt8.blif +40 -0
- data/test/sqrt8.piccolo +132 -0
- data/test/sqrt8_PC.blif +43 -0
- data/test/sqrt8_PC_counter.blif +61 -0
- data/test/sqrt8_PC_done.blif +49 -0
- data/test/sqrt8_PC_state.blif +68 -0
- data/test/sqrt8_PO.blif +43 -0
- data/test/sqrt8_PO_output.blif +67 -0
- data/test/sqrt8_PO_sqrtr.blif +66 -0
- data/test/sqrt8_PO_work.blif +227 -0
- data/test/test_blifutils.rb +79 -0
- data/test/testbench_sqrt8.cc +48 -0
- metadata +102 -0
@@ -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
|
+
|