bliftax 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +82 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/bliftax.gemspec +32 -0
- data/lib/bliftax/gate.rb +70 -0
- data/lib/bliftax/implicant/bit.rb +54 -0
- data/lib/bliftax/implicant.rb +344 -0
- data/lib/bliftax/version.rb +3 -0
- data/lib/bliftax.rb +191 -0
- metadata +103 -0
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
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
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
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
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
|
data/lib/bliftax/gate.rb
ADDED
@@ -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
|
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:
|