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