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,251 @@
|
|
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/parser'
|
21
|
+
require 'blifutils/lexer'
|
22
|
+
require 'blifutils/ast'
|
23
|
+
|
24
|
+
|
25
|
+
module BlifUtils
|
26
|
+
|
27
|
+
module BlifUtils::Language
|
28
|
+
|
29
|
+
class Parser < RLTK::Parser
|
30
|
+
|
31
|
+
production(:toplevel, 'model_list') do |arr|
|
32
|
+
BlifUtils::AST::TranslationUnit.new(arr)
|
33
|
+
end
|
34
|
+
|
35
|
+
list(:model_list, [:model, :file_reference])
|
36
|
+
|
37
|
+
nonempty_list(:identifier_list, :IDENTIFIER)
|
38
|
+
|
39
|
+
production(:model, 'MODEL .IDENTIFIER .model_header .commands END') do |name, header, commands|
|
40
|
+
BlifUtils::AST::Model.new(name, header, commands)
|
41
|
+
end
|
42
|
+
|
43
|
+
list(:model_header, :model_header_element)
|
44
|
+
|
45
|
+
production(:model_header_element) do
|
46
|
+
clause('INPUTS .identifier_list') do |arr|
|
47
|
+
BlifUtils::AST::ModelHeaderElementInputs.new(arr)
|
48
|
+
end
|
49
|
+
clause('OUTPUTS .identifier_list') do |arr|
|
50
|
+
BlifUtils::AST::ModelHeaderElementOutputs.new(arr)
|
51
|
+
end
|
52
|
+
clause('CLOCK .identifier_list') do |arr|
|
53
|
+
BlifUtils::AST::ModelHeaderElementClock.new(arr)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
list(:commands, :command)
|
58
|
+
|
59
|
+
production(:command) do
|
60
|
+
clause('NAMES .identifier_list .cover_list') do |idl, cl|
|
61
|
+
icl = cl.collect do |s|
|
62
|
+
broken = s.split(/\s+/)
|
63
|
+
if broken.length == 1 then
|
64
|
+
[[], s.to_i]
|
65
|
+
else
|
66
|
+
[broken.first.split('').collect{|char|
|
67
|
+
case char
|
68
|
+
when '0' then 0
|
69
|
+
when '1' then 1
|
70
|
+
else 2
|
71
|
+
end
|
72
|
+
}, broken.last.to_i]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
BlifUtils::AST::LogicGate.new(idl, icl)
|
76
|
+
end
|
77
|
+
clause('LATCH .generic_latch') { |gl| gl }
|
78
|
+
clause('SUBCKT .IDENTIFIER .formal_actual_list') do |name, fal|
|
79
|
+
BlifUtils::AST::ModelReference.new(name, fal)
|
80
|
+
end
|
81
|
+
clause('SEARCH .IDENTIFIER') { |fn| BlifUtils::AST::SubfileReference.new(fn) }
|
82
|
+
clause('BLACKBOX') { |_| BlifUtils::AST::BlackBox.new }
|
83
|
+
end
|
84
|
+
|
85
|
+
production(:generic_latch) do
|
86
|
+
clause('IDENTIFIER IDENTIFIER LATCHTYPE IDENTIFIER LATCHINITVAL') do |input, output, type, control, init|
|
87
|
+
BlifUtils::AST::GenericLatch.new(input, output, ctrlType: type, ctrlSig: ((control =~ /^NIL$/) ? nil : control), initValue: init)
|
88
|
+
end
|
89
|
+
clause('IDENTIFIER IDENTIFIER LATCHTYPE IDENTIFIER') do |input, output, type, control|
|
90
|
+
BlifUtils::AST::GenericLatch.new(input, output, ctrlType: type, ctrlSig: ((control =~ /^NIL$/) ? nil : control))
|
91
|
+
end
|
92
|
+
clause('IDENTIFIER IDENTIFIER LATCHINITVAL') do |input, output, init|
|
93
|
+
BlifUtils::AST::GenericLatch.new(input, output, initValue: init)
|
94
|
+
end
|
95
|
+
clause('IDENTIFIER IDENTIFIER') do |input, output|
|
96
|
+
BlifUtils::AST::GenericLatch.new(input, output)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
list(:cover_list, :COVER)
|
101
|
+
|
102
|
+
nonempty_list(:formal_actual_list, :formal_actual)
|
103
|
+
|
104
|
+
production(:formal_actual, '.IDENTIFIER EQUAL .IDENTIFIER') { |formal, actual| [formal, actual] }
|
105
|
+
|
106
|
+
production(:file_reference, 'SEARCH .IDENTIFIER') { |fn| BlifUtils::AST::SubfileReference.new(fn) }
|
107
|
+
finalize()
|
108
|
+
|
109
|
+
end # BlifUtils::Language::Parser
|
110
|
+
|
111
|
+
end # BlifUtils::Language
|
112
|
+
|
113
|
+
|
114
|
+
class Parser
|
115
|
+
|
116
|
+
def self.parse (fileName, quiet: false)
|
117
|
+
processedFileNames = []
|
118
|
+
ast = self.parse_recursive(File.expand_path(fileName), processedFileNames, quiet)
|
119
|
+
return ast
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
def self.parse_string (str, quiet: false)
|
124
|
+
lexems = BlifUtils::Language::Lexer::lex(str)
|
125
|
+
|
126
|
+
begin
|
127
|
+
ast = BlifUtils::Language::Parser::parse(lexems)
|
128
|
+
rescue RLTK::NotInLanguage => e
|
129
|
+
print_parse_error(e.current, 'String not in grammar.')
|
130
|
+
rescue RLTK::BadToken => e
|
131
|
+
print_parse_error(e.faultyToken, "Unexpected token: \"#{e.faultyToken.type.to_s}\". Token not present in grammar definition.")
|
132
|
+
end
|
133
|
+
|
134
|
+
# Delete file references from the AST
|
135
|
+
ast.modelList.delete_if do |elem|
|
136
|
+
if elem.kind_of?(BlifUtils::AST::SubfileReference) then
|
137
|
+
STDERR.puts "WARNING: Ignoring \".search #{elem.fileName}\"" unless quiet
|
138
|
+
true
|
139
|
+
else
|
140
|
+
false
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
ast.modelList.each do |model|
|
145
|
+
model.commands.delete_if do |com|
|
146
|
+
if com.kind_of?(BlifUtils::AST::SubfileReference) then
|
147
|
+
STDERR.puts "WARNING: Ignoring \".search #{com.fileName}\"" unless quiet
|
148
|
+
true
|
149
|
+
else
|
150
|
+
false
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
return ast
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
|
162
|
+
def self.print_parse_error (token, errorString)
|
163
|
+
unless token.position.nil? then
|
164
|
+
fileName = token.position.file_name
|
165
|
+
line = token.position.line_number
|
166
|
+
column = token.position.line_offset
|
167
|
+
|
168
|
+
STDERR.puts "ERROR: Parse error at line #{line}, column #{column+1}, from file \"#{fileName}\":\n#{errorString}"
|
169
|
+
str = File.read(fileName).lines.to_a[line-1].gsub(/\t/,' ')
|
170
|
+
STDERR.puts (line.to_s + ': ') + str
|
171
|
+
abort ' '*(line.to_s.length + 2) + ('~'*column + '^')
|
172
|
+
else
|
173
|
+
STDERR.puts "Parse error:"
|
174
|
+
abort errorString
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
def self.parse_file (fileName, quiet = false)
|
180
|
+
puts "Parsing file \"#{fileName}\"..." unless quiet
|
181
|
+
lexems = BlifUtils::Language::Lexer::lex_file(fileName)
|
182
|
+
|
183
|
+
begin
|
184
|
+
ast = BlifUtils::Language::Parser::parse(lexems)
|
185
|
+
rescue RLTK::NotInLanguage => e
|
186
|
+
print_parse_error(e.current, 'String not in grammar.')
|
187
|
+
rescue RLTK::BadToken => e
|
188
|
+
print_parse_error(e.faultyToken, "Unexpected token: \"#{e.faultyToken.type.to_s}\". Token not present in grammar definition.")
|
189
|
+
end
|
190
|
+
|
191
|
+
return ast
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
def self.parse_recursive (fileName, processedFileNames, quiet = false)
|
196
|
+
return BlifUtils::AST::TranslationUnit.new if processedFileNames.include?(fileName)
|
197
|
+
processedFileNames << fileName
|
198
|
+
|
199
|
+
# Parse the file
|
200
|
+
ast = self.parse_file(fileName, quiet)
|
201
|
+
|
202
|
+
# Gather new file to parse
|
203
|
+
newFileToParseList = []
|
204
|
+
ast.modelList.each do |element|
|
205
|
+
if element.kind_of?(BlifUtils::AST::SubfileReference) then # file reference outside a model
|
206
|
+
newFileToParseList << element.fileName
|
207
|
+
else # it is a Model
|
208
|
+
element.commands.select{|elm| elm.kind_of?(BlifUtils::AST::SubfileReference)}.each do |com|
|
209
|
+
newFileToParseList << com.fileName
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Delete file references from the AST
|
215
|
+
ast.modelList.delete_if{|elem| elem.kind_of?(BlifUtils::AST::SubfileReference)}
|
216
|
+
ast.modelList.each do |model|
|
217
|
+
model.commands.delete_if{|com| com.kind_of?(BlifUtils::AST::SubfileReference)}
|
218
|
+
end
|
219
|
+
|
220
|
+
# Get absolute path of new file to parse
|
221
|
+
dirname = File.dirname(fileName)
|
222
|
+
newFileToParseList.collect!{|fn| File.expand_path(fn, dirname)}
|
223
|
+
|
224
|
+
# Parse new files
|
225
|
+
newFileToParseList.each do |newFileName|
|
226
|
+
newAst = self.parse_recursive(newFileName, processedFileNames, quiet)
|
227
|
+
newAst.modelList.each do |newModel|
|
228
|
+
if ast.modelList.collect{|model| model.name}.include?(newModel.name) then
|
229
|
+
abort "ERROR: Model \"#{newModel.name}\" is redefined"
|
230
|
+
end
|
231
|
+
ast.modelList << newModel
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
return ast
|
236
|
+
end
|
237
|
+
|
238
|
+
end # BlifUtils::Parser
|
239
|
+
|
240
|
+
end # BlifUtils
|
241
|
+
|
242
|
+
|
243
|
+
|
244
|
+
|
245
|
+
if __FILE__ == $0 then
|
246
|
+
|
247
|
+
abort "Usage: #{__FILE__} <file_to_parse>" unless ARGV.length == 1
|
248
|
+
|
249
|
+
puts BlifUtils::Parser::parse(ARGV[0]).pretty_print
|
250
|
+
end
|
251
|
+
|
@@ -0,0 +1,342 @@
|
|
1
|
+
##
|
2
|
+
# Copyright (C) 2017 Théotime bollengier <theotime.bollengier@gmail.com>
|
3
|
+
#
|
4
|
+
# This file is part of Blifutils.
|
5
|
+
#
|
6
|
+
# Blifutils is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Blifutils is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with Blifutils. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
|
20
|
+
require 'blifutils/netlist'
|
21
|
+
require 'blifutils/layering'
|
22
|
+
|
23
|
+
|
24
|
+
module BlifUtils
|
25
|
+
|
26
|
+
class Netlist
|
27
|
+
|
28
|
+
class LogicGate
|
29
|
+
|
30
|
+
def get_simulation_table
|
31
|
+
table = get_LookUpTable()
|
32
|
+
tableLength = table.length
|
33
|
+
|
34
|
+
uint32table = []
|
35
|
+
pos = 0
|
36
|
+
begin
|
37
|
+
tableCont = 0
|
38
|
+
(0 ... 32).each do |j|
|
39
|
+
tagada = pos + j
|
40
|
+
break if tagada >= tableLength
|
41
|
+
if table[tagada] == 1 then
|
42
|
+
tableCont |= (1 << j)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
pos += 32
|
46
|
+
uint32table << tableCont
|
47
|
+
end while pos < tableLength
|
48
|
+
|
49
|
+
return uint32table.collect{|num| num.to_s}.join(', ')
|
50
|
+
end
|
51
|
+
|
52
|
+
end # BlifUtils::Netlist::LogicGate
|
53
|
+
|
54
|
+
|
55
|
+
def create_simulation_file_for_model (modelName = nil, quiet: false)
|
56
|
+
modelName = first_model.name if modelName.nil?
|
57
|
+
dedel = get_model_by_name(modelName)
|
58
|
+
if dedel.nil?
|
59
|
+
abort "ERROR: Model \"#{modelName}\" not found."
|
60
|
+
end
|
61
|
+
if dedel.is_self_contained? then
|
62
|
+
model = dedel
|
63
|
+
else
|
64
|
+
model = flatten(modelName, false, quiet: quiet)
|
65
|
+
end
|
66
|
+
|
67
|
+
className = (model.name + '_simulation_class').gsub('_',' ').split.collect{|word| word.capitalize}.join
|
68
|
+
gateArray = model.simulation_components_to_schedule_stack(withOutputGraphviz: false, quiet: quiet) # This array does not contain constants
|
69
|
+
latchArray = model.components.select{|comp| comp.isLatch?}
|
70
|
+
nbGates = gateArray.length
|
71
|
+
nbLatches = latchArray.length
|
72
|
+
nbNets = model.nets.length
|
73
|
+
|
74
|
+
# Find inputs #
|
75
|
+
simInputs = {} # {name -> [[net, index], ... ], ... }
|
76
|
+
model.inputs.each do |iIO|
|
77
|
+
iname = iIO.name
|
78
|
+
match = iname.match(/(.*?)((\[(\d+)\])|(_(\d+)_))/)
|
79
|
+
if match.nil? then
|
80
|
+
simInputs[iname] = [] if simInputs[iname].nil?
|
81
|
+
simInputs[iname] << [iIO.net, 0]
|
82
|
+
else
|
83
|
+
unless match[4].nil? then
|
84
|
+
indNum = match[4].to_i
|
85
|
+
end
|
86
|
+
unless match[6].nil? then
|
87
|
+
indNum = match[6].to_i
|
88
|
+
end
|
89
|
+
simInputs[match[1]] = [] if simInputs[match[1]].nil?
|
90
|
+
simInputs[match[1]] << [iIO.net, indNum]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
simVectorInputs = [] # [[name, [net0, net1, ... ]], ... ]
|
94
|
+
simInputs.each do |vectName, net_index_array|
|
95
|
+
net_index_array.sort!{|net_indexA, net_indexB| net_indexA[1] <=> net_indexB[1]}
|
96
|
+
simVectorInputs << [vectName, net_index_array.collect{|net_index| net_index[0]}]
|
97
|
+
end
|
98
|
+
|
99
|
+
# Find outputs #
|
100
|
+
simOutputs = {} # {name -> [[net, index], ... ], ... }
|
101
|
+
model.outputs.each do |iIO|
|
102
|
+
oname = iIO.name
|
103
|
+
match = oname.match(/(.*?)((\[(\d+)\])|(_(\d+)_))/)
|
104
|
+
if match.nil? then
|
105
|
+
simOutputs[oname] = [] if simOutputs[oname].nil?
|
106
|
+
simOutputs[oname] << [iIO.net, 0]
|
107
|
+
else
|
108
|
+
unless match[4].nil? then
|
109
|
+
indNum = match[4].to_i
|
110
|
+
end
|
111
|
+
unless match[6].nil? then
|
112
|
+
indNum = match[6].to_i
|
113
|
+
end
|
114
|
+
simOutputs[match[1]] = [] if simOutputs[match[1]].nil?
|
115
|
+
simOutputs[match[1]] << [iIO.net, indNum]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
simVectorOutputs = [] # [[name, [net0, net1, ... ]], ... ]
|
119
|
+
simOutputs.each do |vectName, net_index_array|
|
120
|
+
net_index_array.sort!{|net_indexA, net_indexB| net_indexA[1] <=> net_indexB[1]}
|
121
|
+
simVectorOutputs << [vectName, net_index_array.collect{|net_index| net_index[0]}]
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
str = "/#{'*'*78}/\n\n\n"
|
126
|
+
|
127
|
+
str += "class #{className} : public Model\n{\n"
|
128
|
+
str += "\tprivate:\n\n"
|
129
|
+
str += "\t\tstatic const unsigned int nbNets = #{nbNets};\n"
|
130
|
+
str += "\t\tstatic const unsigned int nbLatches = #{nbLatches};\n"
|
131
|
+
str += "\t\tstatic const unsigned int nbGates = #{nbGates};\n\n"
|
132
|
+
str += "\t\tNet *nets[nbNets];\n"
|
133
|
+
str += "\t\tLatch *latches[nbLatches];\n"
|
134
|
+
str += "\t\tGate *gates[nbGates];\n\n"
|
135
|
+
str += "\t\tbool gateChanged[nbGates];\n\n"
|
136
|
+
str += "\tpublic:\n\n"
|
137
|
+
str += "\t\t#{className}();\n"
|
138
|
+
str += "\t\t~#{className}();\n\n"
|
139
|
+
simInputs.each do |key, val|
|
140
|
+
val.each do |net_index|
|
141
|
+
ind = model.nets.index(net_index[0])
|
142
|
+
next if ind.nil?
|
143
|
+
str += "\t\tNet *INPUT_NET_#{key}#{if val.length > 1 then "_#{net_index[1]}" end};\n"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
str += "\n"
|
147
|
+
simOutputs.each do |key, val|
|
148
|
+
val.each do |net_index|
|
149
|
+
str += "\t\tNet *OUTPUT_NET_#{key}#{if val.length > 1 then "_#{net_index[1]}" end};\n"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
unless simVectorInputs.empty? then
|
153
|
+
str += "\n"
|
154
|
+
simVectorInputs.each do |simVectInput|
|
155
|
+
next if simVectInput[1].collect{|net| model.nets.index(net)}.include?(nil)
|
156
|
+
str += "\t\tBitVector *INPUT_VECTOR_#{simVectInput[0]};\n"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
unless simVectorOutputs.empty? then
|
160
|
+
str += "\n"
|
161
|
+
simVectorOutputs.each do |simVectOutput|
|
162
|
+
str += "\t\tBitVector *OUTPUT_VECTOR_#{simVectOutput[0]};\n"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
str += "\n\tprivate:\n\n\t\tvoid setConstants();\n"
|
166
|
+
str += "};\n\n"
|
167
|
+
|
168
|
+
str += "#{className}::#{className}() :\n"
|
169
|
+
str += "\tModel(nbNets, nbLatches, nbGates)\n"
|
170
|
+
str += "{\n"
|
171
|
+
model.nets.each_with_index do |net, i|
|
172
|
+
fanouts = []
|
173
|
+
net.fanouts.each do |fanout|
|
174
|
+
index = gateArray.index(fanout.target)
|
175
|
+
fanouts << index unless index.nil?
|
176
|
+
end
|
177
|
+
if fanouts.empty? then
|
178
|
+
str += "\tnets[#{i}] = new Net(NULL, 0, gateChanged);\n"
|
179
|
+
else
|
180
|
+
str += "\tnets[#{i}] = new Net(new int[#{fanouts.length}] {#{fanouts.collect{|ind| ind.to_s}.join(', ')}}, #{fanouts.length}, gateChanged);\n"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
str += "\n"
|
184
|
+
latchArray.each_with_index do |latch, i|
|
185
|
+
str += "\tlatches[#{i}] = new Latch(nets[#{model.nets.index(latch.input)}], nets[#{model.nets.index(latch.output)}], #{latch.initValue != 1 ? '0' : '1'});\n"
|
186
|
+
end
|
187
|
+
str += "\n"
|
188
|
+
gateArray.each_with_index do |gate, i|
|
189
|
+
str += "\tgates[#{i}] = new Gate(new Net*[#{gate.inputs.length}]{#{gate.inputs.collect{|net| "nets[#{model.nets.index(net)}]"}.join(', ')}}, #{gate.inputs.length}, nets[#{model.nets.index(gate.output)}], new uint32_t[#{((2**gate.inputs.length)/32.0).ceil}]{#{gate.get_simulation_table}});\n"
|
190
|
+
end
|
191
|
+
str += "\n"
|
192
|
+
str += "\tfor (unsigned int i(0); i < nbGates; i++) {\n\t\tgateChanged[i] = false;\n\t}\n"
|
193
|
+
str += "\n"
|
194
|
+
simInputs.each do |key, val|
|
195
|
+
val.each do |net_index|
|
196
|
+
ind = model.nets.index(net_index[0])
|
197
|
+
next if ind.nil?
|
198
|
+
str += "\tINPUT_NET_#{key}#{if val.length > 1 then "_#{net_index[1]}" end} = nets[#{ind}];\n"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
str += "\n"
|
202
|
+
simOutputs.each do |key, val|
|
203
|
+
val.each do |net_index|
|
204
|
+
str += "\tOUTPUT_NET_#{key}#{if val.length > 1 then "_#{net_index[1]}" end} = nets[#{model.nets.index(net_index[0])}];\n"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
unless simVectorInputs.empty? then
|
208
|
+
str += "\n"
|
209
|
+
simVectorInputs.each do |simVectInput|
|
210
|
+
next if simVectInput[1].collect{|net| model.nets.index(net)}.include?(nil)
|
211
|
+
str += "\tINPUT_VECTOR_#{simVectInput[0]} = new BitVector(new Net*[#{simVectInput[1].length}]{#{simVectInput[1].collect{|net| "nets[#{model.nets.index(net)}]"}.join(', ')}}, #{simVectInput[1].length});\n"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
unless simVectorOutputs.empty? then
|
215
|
+
str += "\n"
|
216
|
+
simVectorOutputs.each do |simVectOutput|
|
217
|
+
str += "\tOUTPUT_VECTOR_#{simVectOutput[0]} = new BitVector(new Net*[#{simVectOutput[1].length}]{#{simVectOutput[1].collect{|net| "nets[#{model.nets.index(net)}]"}.join(', ')}}, #{simVectOutput[1].length});\n"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
str += "\n"
|
221
|
+
str += "\tModel::setNets(nets);\n"
|
222
|
+
str += "\tModel::setLatches(latches);\n"
|
223
|
+
str += "\tModel::setGates(gates);\n"
|
224
|
+
str += "\tModel::setChanges(gateChanged);\n"
|
225
|
+
str += "}\n\n\n"
|
226
|
+
|
227
|
+
str += "#{className}::~#{className}()\n"
|
228
|
+
str += "{\n"
|
229
|
+
str += "\tunsigned int i;\n\n"
|
230
|
+
if nbNets > 0 then
|
231
|
+
str += "\tfor (i = 0; i < nbNets; i++) {\n"
|
232
|
+
str += "\t\tdelete nets[i];\n"
|
233
|
+
str += "\t}\n\n"
|
234
|
+
end
|
235
|
+
if nbLatches > 0 then
|
236
|
+
str += "\tfor (i = 0; i < nbLatches; i++) {\n"
|
237
|
+
str += "\t\tdelete latches[i];\n"
|
238
|
+
str += "\t}\n\n"
|
239
|
+
end
|
240
|
+
if nbGates > 0 then
|
241
|
+
str += "\tfor (i = 0; i < nbGates; i++) {\n"
|
242
|
+
str += "\t\tdelete gates[i];\n"
|
243
|
+
str += "\t}\n"
|
244
|
+
end
|
245
|
+
unless simVectorInputs.empty? then
|
246
|
+
str += "\n"
|
247
|
+
simVectorInputs.each do |simVectInput|
|
248
|
+
next if simVectInput[1].collect{|net| model.nets.index(net)}.include?(nil)
|
249
|
+
str += "\tdelete INPUT_VECTOR_#{simVectInput[0]};\n"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
unless simVectorOutputs.empty? then
|
253
|
+
str += "\n"
|
254
|
+
simVectorOutputs.each do |simVectOutput|
|
255
|
+
str += "\tdelete OUTPUT_VECTOR_#{simVectOutput[0]};\n"
|
256
|
+
end
|
257
|
+
end
|
258
|
+
str += "}\n\n\n"
|
259
|
+
str += "void #{className}::setConstants()\n{\n"
|
260
|
+
model.components.select{|comp| comp.isGate? and comp.is_constant?}.each do |cstGate|
|
261
|
+
if cstGate.singleOutputCoverList.empty? or cstGate.singleOutputCoverList[0][1] == 0 then
|
262
|
+
str += "\tnets[#{model.nets.index(cstGate.output)}]->setValue(0);\n"
|
263
|
+
else
|
264
|
+
str += "\tnets[#{model.nets.index(cstGate.output)}]->setValue(1);\n"
|
265
|
+
end
|
266
|
+
if cstGate.singleOutputCoverList.length > 1 and cstGate.singleOutputCoverList.collect{|ina_o| ina_o[1]}.uniq.length > 1 then
|
267
|
+
abort "ERROR: Bad constant definition in gate \"#{cstGate.output.name}\""
|
268
|
+
end
|
269
|
+
end
|
270
|
+
str += "}\n\n"
|
271
|
+
|
272
|
+
|
273
|
+
outFileName = model.name + '_cpp_sim.cc'
|
274
|
+
File.write(outFileName, File.read(File.join(File.dirname(File.expand_path(__FILE__)), '..', '..', 'share', 'blimulator_cpp_classes.cc')) + str)
|
275
|
+
puts "Written C++ simulation model in file \"#{outFileName}\"" unless quiet
|
276
|
+
|
277
|
+
compileLine = "g++ -c -W -Wall -O3 -std=c++11 #{outFileName} -o #{File.basename(outFileName, '.cc')}.o"
|
278
|
+
puts "Compiling model...\n#{compileLine}" unless quiet
|
279
|
+
case system(compileLine)
|
280
|
+
when nil then
|
281
|
+
abort "ERROR: No g++ compiler found"
|
282
|
+
when false then
|
283
|
+
abort "An error occured during compilation"
|
284
|
+
end
|
285
|
+
|
286
|
+
|
287
|
+
## Header ##
|
288
|
+
hstr = "class #{className} : public Model\n{\n"
|
289
|
+
hstr += "\tprivate:\n\n"
|
290
|
+
hstr += "\t\tstatic const unsigned int nbNets = #{nbNets};\n"
|
291
|
+
hstr += "\t\tstatic const unsigned int nbLatches = #{nbLatches};\n"
|
292
|
+
hstr += "\t\tstatic const unsigned int nbGates = #{nbGates};\n\n"
|
293
|
+
hstr += "\t\tNet *nets[nbNets];\n"
|
294
|
+
hstr += "\t\tLatch *latches[nbLatches];\n"
|
295
|
+
hstr += "\t\tGate *gates[nbGates];\n\n"
|
296
|
+
hstr += "\t\tbool gateChanged[nbGates];\n\n"
|
297
|
+
hstr += "\tpublic:\n\n"
|
298
|
+
hstr += "\t\t#{className}();\n"
|
299
|
+
hstr += "\t\t~#{className}();\n\n"
|
300
|
+
simInputs.each do |key, val|
|
301
|
+
val.each do |net_index|
|
302
|
+
ind = model.nets.index(net_index[0])
|
303
|
+
next if ind.nil?
|
304
|
+
hstr += "\t\tNet *INPUT_NET_#{key}#{if val.length > 1 then "_#{net_index[1]}" end};\n"
|
305
|
+
end
|
306
|
+
end
|
307
|
+
hstr += "\n"
|
308
|
+
simOutputs.each do |key, val|
|
309
|
+
val.each do |net_index|
|
310
|
+
hstr += "\t\tNet *OUTPUT_NET_#{key}#{if val.length > 1 then "_#{net_index[1]}" end};\n"
|
311
|
+
end
|
312
|
+
end
|
313
|
+
unless simVectorInputs.empty? then
|
314
|
+
hstr += "\n"
|
315
|
+
simVectorInputs.each do |simVectInput|
|
316
|
+
next if simVectInput[1].collect{|net| model.nets.index(net)}.include?(nil)
|
317
|
+
hstr += "\t\tBitVector *INPUT_VECTOR_#{simVectInput[0]};\n"
|
318
|
+
end
|
319
|
+
end
|
320
|
+
unless simVectorOutputs.empty? then
|
321
|
+
hstr += "\n"
|
322
|
+
simVectorOutputs.each do |simVectOutput|
|
323
|
+
hstr += "\t\tBitVector *OUTPUT_VECTOR_#{simVectOutput[0]};\n"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
hstr += "\n\tprivate:\n\n\t\tvoid setConstants();\n"
|
327
|
+
hstr += "};\n\n#endif /* #{model.name.upcase}_SIMULATION_HEADER_H */\n"
|
328
|
+
|
329
|
+
hhstr = "#ifndef #{model.name.upcase}_SIMULATION_HEADER_H\n#define #{model.name.upcase}_SIMULATION_HEADER_H\n\n"
|
330
|
+
outHeadername = model.name + '_cpp_header.hh'
|
331
|
+
File.write(outHeadername, hhstr + File.read(File.join(File.dirname(File.expand_path(__FILE__)), '..', '..', 'share', 'blimulator_cpp_classes.hh')) + hstr)
|
332
|
+
|
333
|
+
puts "Written C++ model simulation header in file \"#{outHeadername}\"" unless quiet
|
334
|
+
puts "Now you can write your testbench in a C++ file as 'testbench.cc' including '#include \"#{outHeadername}\"', then run:" unless quiet
|
335
|
+
puts "g++ -W -Wall -O3 #{File.basename(outFileName, '.cc')}.o testbench.cc" unless quiet
|
336
|
+
end
|
337
|
+
|
338
|
+
|
339
|
+
end # BlifUtils::Netlist
|
340
|
+
|
341
|
+
end # BlifUtils
|
342
|
+
|