bliftax 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 629ed1deeccb6405f838c9867ca6a1f83872c2d6
4
+ data.tar.gz: f6d15136e5044fbf828e0ebf143dc43b7c8247e5
5
+ SHA512:
6
+ metadata.gz: 26642f82d331d02108f63e891805f80066addea67c88f726f5b21ef67a544feebc72b77ecdcff557dc9efe6ea2b270abb3de6f863b433ca8936200f4e7a97607
7
+ data.tar.gz: 7e93b46f18ec3a43880f9eb001726de378d8adfcc2cf0c5594510b7636f524ed4c8a92e71d6dc022f9d5a3bae3756a6436f464eb342f118c8fb34ab6e9b4897c
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.swp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ before_install: gem install bundler -v 1.10.4
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in blif.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Naoki Mizuno
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # Bliftax
2
+
3
+ This is a simple library that parses a
4
+ [BLIF](https://www.ece.cmu.edu/~ee760/760docs/blif.pdf) file and does
5
+ operations used in logic optimization algorithms described in Chapter 4.10.2
6
+ in the book _Fundamentals of Digital Logic with Verilog Design_ by Stephen
7
+ Brown and Zvonko Vranesic.
8
+
9
+ As usual, thanks to the [Faker](https://github.com/stympy/faker) gem for
10
+ partially naming this gem.
11
+
12
+ ## Installation
13
+
14
+ $ bundle install
15
+ $ rake install
16
+
17
+ ## Usage
18
+
19
+ This gem currently only supports the following declarations for BLIF:
20
+
21
+ ```
22
+ .model
23
+ .inputs
24
+ .outputs
25
+ .names
26
+ .latch
27
+ .clock
28
+ ```
29
+
30
+ Following is the list of main features of this gem:
31
+
32
+ * star operators
33
+ * sharp operators
34
+ * coverage check (b is covered by a)
35
+ * finding minterms that an implicant covers
36
+ * finding the cost of an implicant
37
+
38
+ Here is an example usage of this gem.
39
+
40
+ ```ruby
41
+ require 'bliftax'
42
+
43
+ model = Bliftax.new('path/to/blif_file')
44
+ model.gates.each do |gate|
45
+ next if gate.implicants.size < 2
46
+
47
+ starred = gate[0] * gate[1]
48
+ sharped = gate[0].sharp(gate[1])
49
+ end
50
+
51
+ model.gates.each do |gate|
52
+ gate.implicants.combination(2).each do |a, b|
53
+ c = a * b
54
+ covered = c.covers?(a)
55
+ end
56
+ end
57
+ ```
58
+
59
+ ## Development
60
+
61
+ After checking out the repo, run `bin/setup` to install dependencies. Then,
62
+ run `rake rspec` to run the tests. You can also run `bin/console` for an
63
+ interactive prompt that will allow you to experiment.
64
+
65
+ To install this gem onto your local machine, run `bundle exec rake install`.
66
+ To release a new version, update the version number in `version.rb`, and then
67
+ run `bundle exec rake release`, which will create a git tag for the version,
68
+ push git commits and tags, and push the `.gem` file to
69
+ [rubygems.org](https://rubygems.org).
70
+
71
+ ## Contributing
72
+
73
+ Bug reports and pull requests are welcome on GitHub at
74
+ https://github.com/NigoroJr/bliftax.
75
+
76
+ ## License
77
+
78
+ The gem is available as open source under the terms of the [MIT
79
+ License](http://opensource.org/licenses/MIT).
80
+
81
+ ## Author
82
+ Naoki Mizuno
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "bliftax"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
data/bliftax.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bliftax/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "bliftax"
8
+ spec.version = Bliftax::VERSION
9
+ spec.authors = ["Naoki Mizuno"]
10
+ spec.email = ["nigorojr@gmail.com"]
11
+
12
+ spec.summary = 'A library that parses BLIF files and do logic optimization'
13
+ spec.homepage = 'https://github.com/NigoroJr/bliftax'
14
+ spec.license = "MIT"
15
+
16
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
17
+ # delete this section to allow pushing this gem to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
20
+ else
21
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
22
+ end
23
+
24
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_development_dependency "bundler", "~> 1.10"
30
+ spec.add_development_dependency "rake", "~> 10.0"
31
+ spec.add_development_dependency "rspec"
32
+ end
@@ -0,0 +1,70 @@
1
+ require 'bliftax/implicant'
2
+
3
+ class Bliftax
4
+ # Represents one logic gate in the BLIF file
5
+ class Gate
6
+
7
+ attr_reader :input_labels, :output_label
8
+ attr_accessor :implicants
9
+
10
+ # Initializes this gate with the labels for inputs and output.
11
+ #
12
+ # @param labels [Array] the label names for the inputs and output. The
13
+ # last element in the Array is the output label.
14
+ def initialize(labels)
15
+ (*@input_labels, @output_label) = labels
16
+ @implicants = []
17
+ end
18
+
19
+ # Adds an Implicant to this gate.
20
+ #
21
+ # @param implicant [Implicant, String, Array<Implicant, String>] the
22
+ # implicant to add. If a String is passed, it is considered to be a
23
+ # two-token string with the first being the input bits and the second
24
+ # being the output bit. For example, '010 1' represents the three inputs
25
+ # being 0, 1, 0 and the output being 1 in this case. If an Array is
26
+ # given, it will add all of the implicants.
27
+ def add_implicant(implicant)
28
+ case implicant
29
+ when Implicant
30
+ @implicants << implicant
31
+ when String
32
+ @implicants << Implicant.new(@input_labels, @output_label, implicant)
33
+ when Array
34
+ # Recursive call
35
+ implicant.each { |i| add(i) }
36
+ end
37
+ end
38
+ alias_method :<<, :add_implicant
39
+
40
+ # Returns the specified implicant.
41
+ #
42
+ # @param index [Integer] the index of the implicant.
43
+ #
44
+ # @return [Implicant] the specified implicant.
45
+ def [](index)
46
+ @implicants[index]
47
+ end
48
+
49
+ # Returns the size of the inputs.
50
+ #
51
+ # @return [Integer] the size of the inputs.
52
+ def input_size
53
+ @input_labels.size
54
+ end
55
+
56
+ # Returns a string representation of this gate in BLIF format.
57
+ #
58
+ # @return [String] this gate in BLIF format
59
+ def to_blif
60
+ str = format(".names %s %s\n",
61
+ @input_labels.join(SPACE),
62
+ @output_label)
63
+ @implicants.each do |implicant|
64
+ str += format("%s\n", implicant.to_blif)
65
+ end
66
+
67
+ str
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,54 @@
1
+ class Bliftax
2
+ class Implicant
3
+ # A class that represents one bit of an implicant
4
+ class Bit
5
+ ON = '1'
6
+ OFF = '0'
7
+ # Don't care
8
+ DC = '-'
9
+ EPSILON = 'E'
10
+ NULL = 'N'
11
+
12
+ attr_accessor :label, :bit, :type
13
+
14
+ INPUT = :input
15
+ OUTPUT = :output
16
+
17
+ # Creates a new Bit instance.
18
+ #
19
+ # @param label [String] the label corresponding to this bit.
20
+ # @param bit [String] the bit (can be 1, 0, -, E, or N).
21
+ # @param type [Symbol] the type of this bit (input or output).
22
+ def initialize(label, bit, type)
23
+ fail 'Label has to be a String' unless label.is_a?(String)
24
+ fail 'Bit has to be a String' unless bit.is_a?(String)
25
+
26
+ @label = label
27
+ @bit = bit
28
+ @type = type
29
+ end
30
+
31
+ # Checks for equality.
32
+ #
33
+ # @param other [Object] whatever to compare against
34
+ def ==(other)
35
+ @bit == other.bit && @type == other.type
36
+ end
37
+ alias_method :eql?, :==
38
+
39
+ # Returns the hash value of this instance.
40
+ #
41
+ # @return [Integer] the hash value of this instance.
42
+ def hash
43
+ [@bit, @type].hash
44
+ end
45
+
46
+ # Return the string version of this bit.
47
+ #
48
+ # @return [String] string representation of this bit.
49
+ def to_s
50
+ format '%-6s %-8s %s', @type.to_s.upcase, @label, @bit
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,344 @@
1
+ require 'set'
2
+ require 'bliftax/implicant/bit'
3
+
4
+ class Bliftax
5
+ # A class that represents an implicant of a gate.
6
+ class Implicant
7
+ # Look-up tables for the star and sharp operators
8
+ # +---+---+---+---+
9
+ # |A/B| 0 | 1 | - |
10
+ # +---+---+---+---+
11
+ # | 0 | | | |
12
+ # +---+---+---+---+
13
+ # | 1 | | | |
14
+ # +---+---+---+---+
15
+ # | - | | | |
16
+ # +---+---+---+---+
17
+ #
18
+ # Use it like: STAR_TABLE[a.bit][b.bit]
19
+ STAR_TABLE = {
20
+ Bit::OFF => {
21
+ Bit::OFF => Bit::OFF,
22
+ Bit::ON => Bit::NULL,
23
+ Bit::DC => Bit::OFF
24
+ },
25
+ Bit::ON => {
26
+ Bit::OFF => Bit::NULL,
27
+ Bit::ON => Bit::ON,
28
+ Bit::DC => Bit::ON
29
+ },
30
+ Bit::DC => {
31
+ Bit::OFF => Bit::OFF,
32
+ Bit::ON => Bit::ON,
33
+ Bit::DC => Bit::DC
34
+ }
35
+ }
36
+ SHARP_TABLE = {
37
+ Bit::OFF => {
38
+ Bit::OFF => Bit::EPSILON,
39
+ Bit::ON => Bit::NULL,
40
+ Bit::DC => Bit::EPSILON
41
+ },
42
+ Bit::ON => {
43
+ Bit::OFF => Bit::NULL,
44
+ Bit::ON => Bit::EPSILON,
45
+ Bit::DC => Bit::EPSILON
46
+ },
47
+ Bit::DC => {
48
+ Bit::OFF => Bit::ON,
49
+ Bit::ON => Bit::OFF,
50
+ Bit::DC => Bit::EPSILON
51
+ }
52
+ }
53
+
54
+ attr_reader :inputs, :output
55
+
56
+ def initialize(input_labels, output_label, str, is_null = false)
57
+ @is_null = is_null
58
+
59
+ return if str.empty?
60
+
61
+ (input_bits, output_bit) = parse_bits(str)
62
+
63
+ fail 'Input bit size mismatch' if input_labels.size != input_bits.size
64
+
65
+ @inputs = []
66
+ input_labels.each_with_index do |label, i|
67
+ @inputs << Bit.new(label, input_bits.at(i), Bit::INPUT)
68
+ end
69
+
70
+ @output = Bit.new(output_label, output_bit, Bit::OUTPUT)
71
+ end
72
+
73
+ # The Star-operator.
74
+ # As described in Chapter 4.10 in the Fundamentals of Digital Logic with
75
+ # Verilog Design,
76
+ #
77
+ # A * B = C
78
+ #
79
+ # where
80
+ #
81
+ # * C = NULL if A_i * B_i = NULL for more than one i
82
+ # * Otherwise, C_i = A_i * B_i when A_i * B_i != NULL, and C_i = x (don't
83
+ # care) for the coordinate where A_i * B_i = NULL
84
+ def star(rhs)
85
+ # Sanity check
86
+ unless @inputs.size == rhs.inputs.size
87
+ fail 'Two operands must have equal size for *-operator'
88
+ end
89
+ unless bits_valid?(@inputs.map(&:bit))
90
+ fail 'Bad bit in LHS operand of star operator'
91
+ end
92
+ unless bits_valid?(rhs.inputs.map(&:bit))
93
+ fail 'Bad bit in RHS operand of star operator'
94
+ end
95
+
96
+ result_bits = []
97
+ @inputs.zip(rhs.inputs).each do |a, b|
98
+ result_bits << STAR_TABLE[a.bit][b.bit]
99
+ end
100
+
101
+ result_is_null = result_bits.count(Bit::NULL) > 1
102
+ unless result_is_null
103
+ # C_i = DC if A_i * B_i == NULL
104
+ result_bits.map! { |b| b == Bit::NULL ? Bit::DC : b }
105
+ end
106
+
107
+ # Construct a Implicant instance
108
+ Implicant.new(@inputs.map(&:label),
109
+ @output.label,
110
+ bit_str(result_bits),
111
+ result_is_null)
112
+ end
113
+ alias_method :*, :star
114
+
115
+ # The Sharp-operator.
116
+ # Described in Chapter 4.10.1 in the Fundamentals of Digital Logic with
117
+ # Verilog Design.
118
+ #
119
+ # A # B = C
120
+ #
121
+ # where
122
+ #
123
+ # * C = A if A_i # B_i = NULL for some i
124
+ # * C = NULL if A_i # B_i = EPSILON for all i
125
+ # * Otherwise, C = union of A_i = B_i' (negated) if A_i = x and B_i != x
126
+ #
127
+ # @param rhs [Implicant] the right hand side of the operation
128
+ #
129
+ # @return [Set<Implicant>] Note that the set size could be 1.
130
+ def sharp(rhs)
131
+ unless @inputs.size == rhs.inputs.size
132
+ fail 'Two operands must have equal size for sharp operator'
133
+ end
134
+ unless bits_valid?(@inputs.map(&:bit))
135
+ fail 'Bad bit in LHS operand of sharp operator'
136
+ end
137
+ unless bits_valid?(rhs.inputs.map(&:bit))
138
+ fail 'Bad bit in RHS operand of sharp operator'
139
+ end
140
+
141
+ result_bits = []
142
+ @inputs.zip(rhs.inputs).each do |a, b|
143
+ result_bits << SHARP_TABLE[a.bit][b.bit]
144
+ end
145
+
146
+ # C = A if A_i # B_i = NULL for some i
147
+ return Set.new([self]) if result_bits.any? { |bit| bit == Bit::NULL }
148
+
149
+ # C = NULL if A_i # B_i = EPSILON for all i
150
+ result_is_null = result_bits.all? { |bit| bit == Bit::EPSILON }
151
+ return Set.new([Implicant.make_null]) if result_is_null
152
+
153
+ # Set of Implicant to return
154
+ result_set = Set.new
155
+
156
+ @inputs.zip(rhs.inputs).each_with_index do |(a, b), i|
157
+ # Check for A_i == DC and B_i != DC
158
+ if a.bit == Bit::DC && b.bit != Bit::DC
159
+ copy = @inputs.map(&:bit)
160
+ copy[i] = b.bit == Bit::ON ? Bit::OFF : Bit::ON
161
+ in_labels = @inputs.map(&:label)
162
+ result = Implicant.new(in_labels, @output.label, bit_str(copy))
163
+ result_set.add(result)
164
+ end
165
+ end
166
+
167
+ result_set
168
+ end
169
+
170
+ # Finds the possible minterms that this implicant can represent.
171
+ # For example, a bit string of 101 can only represent m_5, but 10- can
172
+ # represent m_4 and m_5.
173
+ #
174
+ # @return [Set<Integer>] a set of minterms.
175
+ def minterms
176
+ set = Set.new
177
+
178
+ bits = @inputs.map(&:bit)
179
+ num_dc = bits.count(Bit::DC)
180
+ str = bits.join
181
+ # There are 2**num_dc combinations
182
+ (2**num_dc).times do |i|
183
+ str_copy = str.dup
184
+
185
+ # Convert to binary form and pad with 0
186
+ cnt_str = i.to_s(2).rjust(num_dc, '0')
187
+ cnt_str.split('').each do |b|
188
+ str_copy = str_copy.sub(Bit::DC, b)
189
+ end
190
+ set.add(str_copy.to_i(2))
191
+ end
192
+
193
+ set
194
+ end
195
+
196
+ # Returns the cost of this implicant.
197
+ # A cost refers to the size of the cube, meaning how many don't-cares this
198
+ # implicant has. For example, an implicant '1-00' has a cost of 3, whereas
199
+ # '1--0' has a cost of 2.
200
+ #
201
+ # @return [Integer] the cost of this implicant.
202
+ def cost
203
+ @inputs.size - @inputs.map(&:bit).count(Bit::DC)
204
+ end
205
+
206
+ # Checks if this implicant covers the given implicant.
207
+ # Implicant a covers b if all bit combinations that b can describe can
208
+ # also be described by a.
209
+ #
210
+ # @param other [Implicant] the implicant to be compared against.
211
+ #
212
+ # @return [true, false] true if this covers the given implicant, false
213
+ # otherwise.
214
+ def covers?(other)
215
+ fail 'Argument must be an Implicant' unless other.is_a?(Implicant)
216
+
217
+ @inputs.zip(other.inputs).each do |a, b|
218
+ return false unless a.bit == b.bit || a.bit == Bit::DC
219
+ end
220
+
221
+ true
222
+ end
223
+
224
+ def null?
225
+ @is_null
226
+ end
227
+
228
+ # Checks for equality.
229
+ #
230
+ # @param other [Object] whatever to compare against
231
+ def ==(other)
232
+ @inputs == other.inputs && @output == other.output
233
+ end
234
+ alias_method :eql?, :==
235
+
236
+ # Returns the hash value of this instance.
237
+ #
238
+ # @return [Integer] the hash value of this instance.
239
+ def hash
240
+ [@inputs, @output].hash
241
+ end
242
+
243
+ # Return the string version of this implicant.
244
+ #
245
+ # @param show [:input, :output, :labels, Array<:input, :output, :labels>]
246
+ # what to show. If Array is used, corresponding information is appended.
247
+ #
248
+ # @return [String] string representation of this implicant.
249
+ def to_s(show = [:input])
250
+ show = [show] unless show.is_a?(Array)
251
+
252
+ tokens = []
253
+
254
+ if show.include?(:labels)
255
+ # Only show the labels
256
+ if show == [:labels]
257
+ return [@inputs.map(&:label), @output.label].join(Bliftax::SPACE)
258
+ end
259
+
260
+ if show.include?(:input)
261
+ tokens << 'INPUTS'
262
+ @inputs.each do |b|
263
+ tokens << format('%-8s %s', b.label, b.bit)
264
+ end
265
+ end
266
+ if show.include?(:output)
267
+ tokens << 'OUTPUT'
268
+ tokens << format('%-8s %s', @output.label, @output.bit)
269
+ end
270
+
271
+ return tokens.join("\n") + "\n"
272
+ end
273
+
274
+ tokens << @inputs.map(&:bit).join if show.include?(:input)
275
+ tokens << @output.bit if show.include?(:output)
276
+ tokens.join(Bliftax::SPACE)
277
+ end
278
+
279
+ # Returns the string representation of this implicant.
280
+ #
281
+ # @return [String] the string representation of this implicant in BLIF
282
+ # format.
283
+ def to_blif
284
+ format '%s %s', @inputs.map(&:bit).join, @output.bit
285
+ end
286
+
287
+ # Creates a new NULL Implicant.
288
+ #
289
+ # @return [Implicant] a null Implicant
290
+ def self.make_null
291
+ Implicant.new([], Bliftax::EMPTY, Bliftax::EMPTY, true)
292
+ end
293
+
294
+ private
295
+
296
+ # Parses the bit string and returns the bits for inputs and outputs.
297
+ #
298
+ # @param str [String] the string representation of the input and output
299
+ # bits. Input and output bits must be separated by a whitespace.
300
+ #
301
+ # @return [Array] contains exactly two elements, the first being the input
302
+ # bits in String and the second being the output bit in String.
303
+ #
304
+ # @example Parse a String representation of bits.
305
+ # parse_bits('010 1')
306
+ # # => [['0', '1', '0'], '1']
307
+ def parse_bits(str)
308
+ is_single = str.split(' ').size == 1
309
+
310
+ # Constant gate (likely vcc or gnd)
311
+ if is_single
312
+ input_bits = []
313
+ output_bit = str
314
+ else
315
+ # Parse strings that look like "0001 1"
316
+ (input_bits, output_bit) = str.split(' ')
317
+ input_bits = input_bits.split('')
318
+ end
319
+
320
+ [input_bits, output_bit]
321
+ end
322
+
323
+ # Checks whether all the bits are either 1, 0, or DC.
324
+ #
325
+ # @param bits [Array<String>]
326
+ #
327
+ # @return [true, false] true if all the bits are 1, 0, or DC.
328
+ def bits_valid?(bits)
329
+ bits.all? do |bit|
330
+ [Bit::ON, Bit::OFF, Bit::DC].any? { |b| bit == b }
331
+ end
332
+ end
333
+
334
+ # Returns the BLIF-style representation of this implicant.
335
+ # This method assumes that the output bit is 1 because BLIF only states
336
+ # combinations that result in output of 1.
337
+ #
338
+ # @return [String] representation of the bits in this implicant. The
339
+ # output bit is separated from the input bits by one space.
340
+ def bit_str(bits)
341
+ format '%s 1', bits.join
342
+ end
343
+ end
344
+ end
@@ -0,0 +1,3 @@
1
+ class Bliftax
2
+ VERSION = '0.1.0'
3
+ end
data/lib/bliftax.rb ADDED
@@ -0,0 +1,191 @@
1
+ require 'bliftax/gate'
2
+ require 'bliftax/implicant'
3
+ require 'bliftax/version'
4
+
5
+ # A class that parses a BLIF file.
6
+ class Bliftax
7
+ attr_accessor :name, :inputs, :outputs, :latches, :clocks, :gates
8
+
9
+ # Declarations for BLIF
10
+ MODEL = '.model'
11
+ INPUTS = '.inputs'
12
+ OUTPUTS = '.outputs'
13
+ NAMES = '.names'
14
+ LATCH = '.latch'
15
+ CLOCK = '.clock'
16
+ END_MODEL = '.end'
17
+
18
+ # One space
19
+ SPACE = ' '
20
+ # Empty string
21
+ EMPTY = ''
22
+
23
+ # Initializes the object.
24
+ #
25
+ # @param str [String] filename or text to parse
26
+ def initialize(str = nil)
27
+ @latches = []
28
+ @clocks = []
29
+ @gates = []
30
+
31
+ parse(str) unless str.nil?
32
+ end
33
+
34
+ # Parses a BLIF file.
35
+ #
36
+ # @param str [String] filename or the text to parse.
37
+ #
38
+ # @return [Bliftax] the parsed BLIF file.
39
+ def parse(str)
40
+ fh = File.exist?(str) ? File.open(str) : StringIO.new(str)
41
+ lines = read_continuous_lines(fh)
42
+
43
+ lines.each_with_index do |line, i|
44
+ (decl, *following) = line.split(SPACE)
45
+
46
+ case decl
47
+ when MODEL
48
+ @name = following.first
49
+ when INPUTS
50
+ @inputs = following
51
+ when OUTPUTS
52
+ @outputs = following
53
+ when NAMES
54
+ @gates << parse_gate(following, lines, i + 1)
55
+ when LATCH
56
+ @latches << following
57
+ when CLOCK
58
+ @clocks << following
59
+ when END_MODEL
60
+ break
61
+ when /^[^\.]/
62
+ next
63
+ else
64
+ fail "Unknown decl encountered: I don't understand `#{decl}' :("
65
+ end
66
+ end
67
+
68
+ self
69
+ end
70
+
71
+ # Parse one gate from the input.
72
+ #
73
+ # @param labels [Array<String>] labels of the inputs and output.
74
+ # @param lines [Array<String>] all the lines in the current BLIF file.
75
+ # @param i [Integer] index of in lines to start from.
76
+ #
77
+ # @return [Gate] the gate that was parsed.
78
+ def parse_gate(labels, lines, i)
79
+ gate = Gate.new(labels)
80
+
81
+ loop do
82
+ # If no truth table exists, then that's all 0
83
+ bit_str = lines[i].start_with?('.') ? Implicant::Bit::OFF : lines[i]
84
+ gate.add_implicant(bit_str)
85
+
86
+ i += 1
87
+
88
+ break if lines[i].nil? || lines[i].empty? || lines[i].start_with?('.')
89
+ end
90
+
91
+ gate
92
+ end
93
+
94
+ # Returns a string representation of this gate in BLIF format.
95
+ #
96
+ # @return [String] this gate in BLIF format
97
+ def to_blif
98
+ in_labels = @inputs.join(SPACE)
99
+ out_labels = @outputs.join(SPACE)
100
+ str = <<-EOF
101
+ .model #{@name}
102
+ .inputs #{in_labels}
103
+ .outputs #{out_labels}
104
+ EOF
105
+
106
+ @gates.each do |gate|
107
+ str << gate.to_blif
108
+ end
109
+
110
+ @latches.each do |l|
111
+ str << format(".latch %s\n", l.join(SPACE))
112
+ end
113
+
114
+ @clocks.each do |c|
115
+ str << format(".clock %s\n", c.join(SPACE))
116
+ end
117
+
118
+ str << ".end\n"
119
+ end
120
+
121
+ # Duplicates this object.
122
+ #
123
+ # @return the deep copy of this object.
124
+ def dup
125
+ copy = Bliftax.new
126
+ copy.name = @name.dup
127
+ copy.inputs = @inputs.dup
128
+ copy.outputs = @outputs.dup
129
+ copy.latches = @latches.dup
130
+ copy.clocks = @clocks.dup
131
+ copy.gates = @gates.dup
132
+ copy
133
+ end
134
+
135
+ private
136
+
137
+ # Reads in the BLIF file creating an Array with each element being the
138
+ # logical line (removing comments and joining lines that end with
139
+ # backslash with the next line).
140
+ #
141
+ # @param fh [IO] File handle
142
+ #
143
+ # @return [Array] each element being the logical lines with comments and
144
+ # backslashes removed. Lines ending with a backslash gets joined with the
145
+ # next line.
146
+ def read_continuous_lines(fh)
147
+ # Read in the entire BLIF file into an array so that
148
+ # we can "peek" into the future.
149
+ lines = []
150
+ fh.each_line do |line|
151
+ line.chomp!
152
+
153
+ # Skip comment lines
154
+ next if line.start_with?('#')
155
+ next if line.strip.empty?
156
+
157
+ continuous_lines = []
158
+ loop do
159
+ line = strip_comments(line)
160
+ continuous_lines << strip_trailing_backslash(line)
161
+
162
+ # If line ends with a backslash, there's more to come
163
+ break unless line.end_with?('\\')
164
+
165
+ line = fh.gets.chomp
166
+ end
167
+
168
+ lines << continuous_lines.join(SPACE).strip
169
+ end
170
+
171
+ lines
172
+ end
173
+
174
+ # Removes the trailing comments from a string.
175
+ #
176
+ # @param str [String] the original string
177
+ #
178
+ # @return [String] string with the trailing whitespace and comments removed
179
+ def strip_comments(str)
180
+ str.sub(/\s*\#.*$/, EMPTY).strip
181
+ end
182
+
183
+ # Removes the trailing backslash from a string.
184
+ #
185
+ # @param str [String] the original string
186
+ #
187
+ # @return [String] string with the trailing backslash removed
188
+ def strip_trailing_backslash(str)
189
+ str.sub(/\\/, EMPTY)
190
+ end
191
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bliftax
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Naoki Mizuno
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-10-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description:
56
+ email:
57
+ - nigorojr@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - bin/console
70
+ - bin/setup
71
+ - bliftax.gemspec
72
+ - lib/bliftax.rb
73
+ - lib/bliftax/gate.rb
74
+ - lib/bliftax/implicant.rb
75
+ - lib/bliftax/implicant/bit.rb
76
+ - lib/bliftax/version.rb
77
+ homepage: https://github.com/NigoroJr/bliftax
78
+ licenses:
79
+ - MIT
80
+ metadata:
81
+ allowed_push_host: https://rubygems.org
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 2.4.5
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: A library that parses BLIF files and do logic optimization
102
+ test_files: []
103
+ has_rdoc: