bliftax 0.1.0

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 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: