blifutils 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|