blifutils 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,377 @@
1
+
2
+ BlifUtils
3
+ =========
4
+
5
+ BlifUtils is a library to handle BLIF netlists in Ruby.
6
+ The Berkeley Logic Interchange Format (BLIF) allows to describe logic-level
7
+ hierarchical circuits in textual form. Its specification can be found
8
+ [here](https://www.ece.cmu.edu/~ee760/760docs/blif.pdf). The BLIF format is
9
+ used by programs such as [ABC](http://people.eecs.berkeley.edu/~alanmi/abc/)
10
+ (logic synthesis) or [VPR](http://www.eecg.toronto.edu/~vaughn/papers/fpl97.pdf)
11
+ (placement and routing).
12
+
13
+ BlifUtils can read and write BLIF files,
14
+ elaborate internal representations of the netlists, analyze it, flattent modules,
15
+ write the modules as VHDL entities, and generate C++ code for fast
16
+ netlist simulation.
17
+
18
+
19
+ Installation
20
+ ------------
21
+
22
+ To install BlifUtils from the git repository:
23
+
24
+ ```lang-none
25
+ $ git clone https://github.com/TheotimeBollengier/blifutils
26
+ $ cd blifutils
27
+ $ gem build blifutils.gemspec
28
+ $ gem install blifutils.<version>.gem
29
+ ```
30
+
31
+ Alternatively, the blifutils gem is also hosted on RubyGems
32
+ ([https://rubygems.org/gems/blifutils](https://rubygems.org/gems/blifutils)).
33
+ So you can simply type:
34
+
35
+ ```lang-none
36
+ $ gem install blifutils
37
+ ```
38
+
39
+ BlifUtils uses the [RLTK](https://rubygems.org/gems/rltk) gem
40
+ ([https://github.com/chriswailes/RLTK](https://github.com/chriswailes/RLTK)).
41
+
42
+
43
+ Example
44
+ -------
45
+
46
+ ```ruby
47
+ require 'blifutils'
48
+
49
+ ## Parse input file and sub-referenced files ##
50
+ netlist = BlifUtils::read('sqrt8.blif')
51
+
52
+ netlist.models.collect { |model| model.name }
53
+ #=> ["sqrt8",
54
+ # "sqrt8_PO",
55
+ # "sqrt8_PO_output",
56
+ # "sqrt8_PO_sqrtr",
57
+ # "sqrt8_PO_work",
58
+ # "sqrt8_PC",
59
+ # "sqrt8_PC_done",
60
+ # "sqrt8_PC_state",
61
+ # "sqrt8_PC_counter"]
62
+
63
+ puts netlist.get_model_by_name('sqrt8_PC').analyze
64
+ #=> "+----------------------------------------+
65
+ # | Model "sqrt8_PC" analysis |
66
+ # +----------------------------------------+
67
+ # Model "sqrt8_PC"
68
+ # Inputs: 2
69
+ # Outputs: 3
70
+ # Nets: 7
71
+ # Edges: 13
72
+ # Nodes: 3
73
+ # Latches: 0
74
+ # Logic gates: 0
75
+ # Sub circuits: 3
76
+ # Sub circuits repartition:
77
+ # sqrt8_PC_done: 1
78
+ # sqrt8_PC_state: 1
79
+ # sqrt8_PC_counter: 1"
80
+
81
+ sqrt8pc_flattened_model = netlist.flatten('sqrt8_PC')
82
+
83
+ sqrt8pc_flattened_model.analyze
84
+ #=> "+----------------------------------------+
85
+ # | Model "sqrt8_PC" analysis |
86
+ # +----------------------------------------+
87
+ # Model "sqrt8_PC"
88
+ # Inputs: 2
89
+ # Outputs: 3
90
+ # Nets: 21
91
+ # Edges: 38
92
+ # Nodes: 19
93
+ # Latches: 5
94
+ # Logic gates: 14
95
+ # Sub circuits: 0
96
+ # Gates repartition:
97
+ # 1 input: 7 50.0%
98
+ # 2 inputs: 3 21.4%
99
+ # 4 inputs: 3 21.4%
100
+ # 5 inputs: 1 7.1%
101
+ # Buffers: 3"
102
+
103
+ ## Analyze the logic level of the module
104
+ ## that is to say the maximum number of logic functions between two latches or IOs.
105
+ sqrt8pc_flattened_model.level #=> 4
106
+
107
+ sqrt8pc_flattened_model.to_blif
108
+ #=> ".model sqrt8_PC
109
+ # .inputs _clk_ go[0]
110
+ # .outputs done[0] state[1] state[0]
111
+ # .latch n1 n0 re _clk_ 0
112
+ # .latch n6 n2 re _clk_ 0
113
+ # .latch n7 n3 re _clk_ 0
114
+ # .latch nD nA re _clk_ 0
115
+ # .latch nE nB re _clk_ 0
116
+ # .names n2 n3 n1
117
+ # 11 1
118
+ # .names n8 n9 n4
119
+ # 1- 1
120
+ # -1 1
121
+ # .names n4 n5
122
+ # 0 1
123
+ # .names go[0] n2 n3 n2 n6
124
+ # 10-- 1
125
+ # -01- 1
126
+ # 0001 1
127
+ # .names go[0] n2 n3 n5 n3 n7
128
+ # -10-- 1
129
+ # -011- 1
130
+ # 000-1 1
131
+ # .names nA n8
132
+ # 0 1
133
+ # .names nB n9
134
+ # 0 1
135
+ # .names nB nA nC
136
+ # 10 1
137
+ # 01 1
138
+ # .names n2 n3 nA nF nD
139
+ # 1-1- 1
140
+ # 01-1 1
141
+ # .names n2 n3 nB nC nE
142
+ # 1-1- 1
143
+ # 01-1 1
144
+ # .names nA nF
145
+ # 0 1
146
+ # .names n0 done[0]
147
+ # 1 1
148
+ # .names n3 state[1]
149
+ # 1 1
150
+ # .names n2 state[0]
151
+ # 1 1
152
+ # .end"
153
+
154
+ sqrt8pc_flattened_model.to_vhdl
155
+ #=> "library IEEE;
156
+ # use IEEE.STD_LOGIC_1164.ALL;
157
+ #
158
+ #
159
+ # entity SQRT8_PC is
160
+ # port ( clk_in : in std_ulogic;
161
+ # go_0_in : in std_ulogic;
162
+ # done_0_out : out std_ulogic;
163
+ # state_1_out : out std_ulogic;
164
+ # state_0_out : out std_ulogic);
165
+ # end SQRT8_PC;
166
+ #
167
+ #
168
+ # architecture blif of SQRT8_PC is
169
+ #
170
+ # signal clk : std_ulogic;
171
+ # signal go_0 : std_ulogic;
172
+ # signal n0 : std_ulogic := '0';
173
+ # signal n1 : std_ulogic;
174
+ # signal n2 : std_ulogic := '0';
175
+ # signal n3 : std_ulogic := '0';
176
+ # signal n4 : std_ulogic;
177
+ # signal n5 : std_ulogic;
178
+ # signal n6 : std_ulogic;
179
+ # signal n7 : std_ulogic;
180
+ # signal n8 : std_ulogic;
181
+ # signal n9 : std_ulogic;
182
+ # signal nA : std_ulogic := '0';
183
+ # signal nB : std_ulogic := '0';
184
+ # signal nC : std_ulogic;
185
+ # signal nD : std_ulogic;
186
+ # signal nE : std_ulogic;
187
+ # signal nF : std_ulogic;
188
+ # signal done_0 : std_ulogic;
189
+ # signal state_1 : std_ulogic;
190
+ # signal state_0 : std_ulogic;
191
+ #
192
+ # begin
193
+ #
194
+ # clk <= clk_in;
195
+ # go_0 <= go_0_in;
196
+ #
197
+ # done_0_out <= done_0;
198
+ # state_1_out <= state_1;
199
+ # state_0_out <= state_0;
200
+ #
201
+ #
202
+ # process(clk)
203
+ # begin
204
+ # if rising_edge(clk) then
205
+ # n0 <= n1;
206
+ # n2 <= n6;
207
+ # n3 <= n7;
208
+ # nA <= nD;
209
+ # nB <= nE;
210
+ # end if;
211
+ # end process;
212
+ #
213
+ # n1 <= (n2 and n3);
214
+ # n4 <= (n8) or
215
+ # (n9);
216
+ # n5 <= (not(n4));
217
+ # n6 <= (go_0 and not(n2)) or
218
+ # (not(n2) and n3) or
219
+ # (not(go_0) and not(n2) and not(n3) and n2);
220
+ # n7 <= (n2 and not(n3)) or
221
+ # (not(n2) and n3 and n5) or
222
+ # (not(go_0) and not(n2) and not(n3) and n3);
223
+ # n8 <= (not(nA));
224
+ # n9 <= (not(nB));
225
+ # nC <= (nB and not(nA)) or
226
+ # (not(nB) and nA);
227
+ # nD <= (n2 and nA) or
228
+ # (not(n2) and n3 and nF);
229
+ # nE <= (n2 and nB) or
230
+ # (not(n2) and n3 and nC);
231
+ # nF <= (not(nA));
232
+ # done_0 <= (n0);
233
+ # state_1 <= (n3);
234
+ # state_0 <= (n2);
235
+ #
236
+ # end blif;
237
+
238
+ netlist.create_simulation_file_for_model('sqrt8')
239
+ # Creates files sqrt8_cpp_header.hh sqrt8_cpp_sim.cc
240
+ # and compiles to sqrt8_cpp_sim.o
241
+ ```
242
+
243
+ You can then write a C++ testbench:
244
+
245
+ ```C++
246
+ #include <cmath>
247
+ #include "sqrt8_cpp_header.hh"
248
+
249
+ int main(void)
250
+ {
251
+ uint64_t in, out, golden;
252
+
253
+ Sqrt8SimulationClass *dut = new Sqrt8SimulationClass();
254
+
255
+ dut->reset();
256
+
257
+ for (in = 0; in < 256; in++) {
258
+ iterations++;
259
+
260
+ dut->INPUT_VECTOR_radicand->setValue(in);
261
+ dut->INPUT_NET_go->setValue(1);
262
+ dut->cycle();
263
+ dut->INPUT_NET_go->setValue(0);
264
+ dut->cycle();
265
+
266
+ while (dut->OUTPUT_NET_done->getValue() != 1) {
267
+ dut->propagate();
268
+ dut->clock();
269
+ dut->propagate();
270
+ // equivalent to 'dut->cycle()'
271
+ }
272
+
273
+ out = dut->OUTPUT_VECTOR_squareRoot->getValue(NULL);
274
+ golden = (int)sqrt(in);
275
+ if (out != golden) {
276
+ std::cerr << "sqrt(" << in << ") => " << out << " (should be " << golden << ")" << std::endl;
277
+ }
278
+ }
279
+
280
+ delete dut;
281
+
282
+ return 0;
283
+ }
284
+ ```
285
+
286
+ Compile and execute this testbench:
287
+
288
+ ```lang-none
289
+ $ g++ -W -Wall -I. -o simu testbench.cc sqrt8_cpp_sim.o
290
+ $ ./simu
291
+ ```
292
+
293
+ ---
294
+
295
+ The `examples/zpu/` directory contains the necessary files to simulate
296
+ a ZPU ZPU processor [https://github.com/zylin/zpu](https://github.com/zylin/zpu)
297
+ executing a hello-world.
298
+
299
+ ```lang-none
300
+ $ cd examples/zpu
301
+ $ ruby simulate_zpu.rb
302
+
303
+ Parsing file "zpu_mem16.blif"...
304
+ Elaborating model "zpu_mem16"...
305
+ WARNING: In model "zpu_mem16": net "_clk_" has no fanouts
306
+ ------------------------------------------------------------
307
+ Model collection contains 1 models
308
+ +----------------------------------------+
309
+ | Model "zpu_mem16" analysis |
310
+ +----------------------------------------+
311
+ Model "zpu_mem16"
312
+ Inputs: 34
313
+ Outputs: 50
314
+ Nets: 1006
315
+ Edges: 4039
316
+ Nodes: 972
317
+ Latches: 156
318
+ Logic gates: 816
319
+ Sub circuits: 0
320
+ Gates repartition:
321
+ 1 input: 51 6.3%
322
+ 2 inputs: 33 4.0%
323
+ 3 inputs: 95 11.6%
324
+ 4 inputs: 108 13.2%
325
+ 5 inputs: 175 21.4%
326
+ 6 inputs: 354 43.4%
327
+ Buffers: 51
328
+ ------------------------------------------------------------
329
+ Generating graph from model components...
330
+ Extracting connected subgraphs... 87 subgraphs found
331
+ Checking that there are no cycles in subgraphs... Ok
332
+ Layering subgraphs...
333
+ Maximum number of layers: 33
334
+ Writing static schedule for component simulation...
335
+ Written C++ simulation model in file "zpu_mem16_cpp_sim.cc"
336
+ Compiling model...
337
+ g++ -c -W -Wall -O3 -std=c++11 zpu_mem16_cpp_sim.cc -o zpu_mem16_cpp_sim.o
338
+ Written C++ model simulation header in file "zpu_mem16_cpp_header.hh"
339
+ Now you can write your testbench in a C++ file as 'testbench.cc' including '#include "zpu_mem16_cpp_header.hh"', then run:
340
+ g++ -W -Wall -O3 zpu_mem16_cpp_sim.o testbench.cc
341
+ -- Compiling testbench -------------------------------------
342
+ Executing: g++ -W -Wall -I. -o zpu_simulation testbench_zpu.cc zpu_mem16_cpp_sim.o
343
+ You can now execute the testbench with "./zpu_simulation <zpu_compiled_program>"
344
+ -- Executing simulation ------------------------------------
345
+ Executing: ./zpu_simulation zpu_helloworld.bin
346
+ Read 3284 bytes from file 'zpu_helloworld.bin' (memory filled 10.0%)
347
+ Starting simulation
348
+ Hello, world!
349
+ What you see is a processor simulated at a logic netlist level, executing a helloworld.
350
+ Simulation ended
351
+ 80877 clock cycles in 1.271 s
352
+ Simulation average clock speed: 63637 Hzxecutable
353
+ ```
354
+
355
+
356
+ Executable
357
+ ----------
358
+
359
+ To use BlifUtils directly from a terminal or a Makefile,
360
+ this gem also include the `blifutils` executable script, which uses command line arguments.
361
+
362
+ ```lang-none
363
+ $ blifutils --help
364
+ Usage: blifutils [options] -i file1 file2 ...
365
+ -i, --input FILES Input blif files
366
+ -o, --output [FILE] Output blif to FILE
367
+ -s, --simulation Create C++ simulation files
368
+ -v, --vhdl Create a vhdl file
369
+ -f, --flatten Flatten the model hierarchy in a single model
370
+ -m, --model NAME Name of the model to process
371
+ -p, --print-models Print model names
372
+ -a, --analyze [level] Print analysis, with optional level analysis (level)
373
+ -A, --analyze_with_graphviz Print analysis, with level analysis, wirting graphvis files
374
+ -q, --quiet Don't print messages
375
+ -h, --help Display this help
376
+ ```
377
+
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ##
4
+ # Copyright (C) 2017 Théotime bollengier <theotime.bollengier@gmail.com>
5
+ #
6
+ # This file is part of Blifutils.
7
+ #
8
+ # Blifutils is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # Blifutils is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with Blifutils. If not, see <http://www.gnu.org/licenses/>.
20
+ #
21
+
22
+ require 'blifutils'
23
+ require 'optparse'
24
+
25
+
26
+ options = {}
27
+ inputFiles = []
28
+
29
+ optparse = OptionParser.new do |opts|
30
+ # Set a banner, displayed at the top
31
+ # of the help screen.
32
+ opts.banner = "Usage: #{$0} [options] -i file1 file2 ..."
33
+
34
+ opts.on('-i', '--input FILES', 'Input blif files') do |file|
35
+ inputFiles << file
36
+ end
37
+
38
+ options[:blif] = false
39
+ options[:outputBlifName] = nil
40
+ opts.on('-o', '--output [FILE]', 'Output blif to FILE') do |file|
41
+ options[:blif] = true
42
+ options[:outputBlifName] = file
43
+ end
44
+
45
+ options[:simulation] = false
46
+ opts.on('-s', '--simulation', "Create C++ simulation files") do
47
+ options[:simulation] = true
48
+ end
49
+
50
+ options[:vhdl] = false
51
+ opts.on('-v', '--vhdl', "Create a vhdl file") do
52
+ options[:vhdl] = true
53
+ end
54
+
55
+ options[:flatten] = false
56
+ opts.on('-f', '--flatten', 'Flatten the model hierarchy in a single model') do
57
+ options[:flatten] = true
58
+ end
59
+
60
+ options[:model] = nil
61
+ opts.on('-m', '--model NAME', 'Name of the model to process') do |mod|
62
+ options[:model] = mod
63
+ end
64
+
65
+ options[:printModels] = false
66
+ opts.on('-p', '--print-models', 'Print model names') do
67
+ options[:printModels] = true
68
+ end
69
+
70
+ options[:analyze] = false
71
+ options[:analyzeLevel] = false
72
+ opts.on('-a', '--analyze [level]', [:level], 'Print analysis, with optional level analysis (level)') do |ana|
73
+ options[:analyze] = true
74
+ options[:analyzeLevel] = true if ana == :level
75
+ end
76
+
77
+ options[:analyzeLevelWithGV] = false
78
+ opts.on('-A', '--analyze_with_graphviz', 'Print analysis, with level analysis, wirting graphvis files') do
79
+ options[:analyze] = true
80
+ options[:analyzeLevel] = true
81
+ options[:analyzeLevelWithGV] = true
82
+ end
83
+
84
+
85
+ options[:quiet] = false
86
+ opts.on('-q', '--quiet', "Don't print messages") do
87
+ options[:quiet] = true
88
+ end
89
+
90
+ # This displays the help screen, all programs are
91
+ # assumed to have this option.
92
+ opts.on( '-h', '--help', 'Display this help' ) do
93
+ puts opts
94
+ exit
95
+ end
96
+ end
97
+
98
+ optparse.parse!
99
+
100
+ ARGV.each{|f| inputFiles << f}
101
+
102
+
103
+
104
+ ## Read blif inputs and add models to the netlist ##
105
+ netlist = BlifUtils::Netlist.new
106
+ inputFiles.each do |iFile|
107
+ elNetlist = BlifUtils::Elaborator.elaborate_netlist(BlifUtils::Parser.parse(iFile, quiet: options[:quiet]), quiet: options[:quiet])
108
+ elNetlist.models.each{|model| netlist.add_model(model)}
109
+ end
110
+
111
+
112
+ ## Print model names ##
113
+ if options[:printModels] then
114
+ puts "List of models:"
115
+ netlist.model_names.each{|modName| puts " #{modName}"}
116
+ end
117
+
118
+
119
+ ## Flatening model ##
120
+ if options[:flatten] then
121
+ flattenedmodel = netlist.flatten(options[:model], true, quiet: options[:quiet])
122
+ blackboxes = netlist.models.select{|m| m.is_blackbox?}
123
+ netlist.clear
124
+ netlist.add_model(flattenedmodel)
125
+ blackboxes.each{|blbx| netlist.add_model(blbx)}
126
+ end
127
+
128
+
129
+ ## Output blif ##
130
+ if options[:blif] and netlist.length > 0 then
131
+ unless options[:outputBlifName].nil? then
132
+ netlistFileName = options[:outputBlifName]
133
+ else
134
+ if not(File.exist?(netlist.first_model.name + '.blif')) then
135
+ netlistFileName = netlist.first_model.name + '.blif'
136
+ elsif inputFiles.length == 1 then
137
+ netlistFileName = File.basename(inputFiles[0], '.*') + '.blif'
138
+ else
139
+ netlistFileName = 'output.blif'
140
+ end
141
+ end
142
+
143
+ netlist.update_clocks()
144
+ blifStr = netlist.models.collect{|model| model.to_blif}.join("\n")
145
+
146
+ File.write(netlistFileName, blifStr)
147
+ puts "Blif models written to file \"#{netlistFileName}\"." unless options[:quiet]
148
+ end
149
+
150
+
151
+ ## Print analysis ##
152
+ if options[:analyze] then
153
+ unless options[:model].nil? then
154
+ model = netlist.get_model_by_name(options[:model])
155
+ if model.nil?
156
+ STDERR.puts "Cannot find a model named \"#{options[:model]}\""
157
+ STDERR.puts "Available models are:"
158
+ netlist.model_names.each{|modelname| STDERR.puts " #{modelname}"}
159
+ abort
160
+ end
161
+ puts model.analyze
162
+ if model.is_self_contained? and options[:analyzeLevel] then
163
+ model.level_analysis(withOutputGraphviz: options[:analyzeLevelWithGV], quiet: options[:quiet])
164
+ end
165
+ else
166
+ netlist.models.each do |model|
167
+ puts model.analyze
168
+ if model.is_self_contained? and options[:analyzeLevel] then
169
+ model.level_analysis(withOutputGraphviz: options[:analyzeLevelWithGV], quiet: options[:quiet])
170
+ end
171
+ end
172
+ end
173
+ end
174
+
175
+
176
+ if options[:simulation] then
177
+ netlist.create_simulation_file_for_model(options[:model], quiet: options[:quiet])
178
+ end
179
+
180
+
181
+ if options[:vhdl] then
182
+ netlist.create_vhdl_files(options[:model])
183
+ end
184
+