bliftax 0.1.1 → 0.2.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 +4 -4
- data/.travis.yml +9 -0
- data/README.md +28 -0
- data/lib/bliftax/implicant.rb +22 -1
- data/lib/bliftax/optimizer.rb +202 -0
- data/lib/bliftax/version.rb +1 -1
- data/lib/bliftax.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 785167c0e657e38b987fd032dacacfeb2b31229e
|
4
|
+
data.tar.gz: 8144130dde9b34a98b76fb67d940cd81ee861d3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 769d3fea118d3ee1c2bac90fa5c727f7eb49f5c8ee2c68cc3e6ff3ec511491d47b1ede6b40ad4dbb988ac739cd8340dcdd677f1d4668b9b66e41b9e291d8c444
|
7
|
+
data.tar.gz: b804198d60ee236c8a3084a879fe6630acf38a319b8d6b9aa7b92563fcc825fe4f13209e20c307e46a316ce46266ccd4541afbd4d82f410e4878ad98df47c4f2
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Bliftax
|
2
2
|
|
3
|
+
[](https://travis-ci.org/NigoroJr/bliftax)
|
4
|
+
|
3
5
|
This is a simple library that parses a
|
4
6
|
[BLIF](https://www.ece.cmu.edu/~ee760/760docs/blif.pdf) file and does
|
5
7
|
operations used in logic optimization algorithms described in Chapter 4.10.2
|
@@ -29,6 +31,10 @@ This gem currently only supports the following declarations for BLIF:
|
|
29
31
|
|
30
32
|
Following is the list of main features of this gem:
|
31
33
|
|
34
|
+
* 2-level logic optimization
|
35
|
+
- getting the prime implicants
|
36
|
+
- getting the essential prime implicants
|
37
|
+
- using branch heuristic
|
32
38
|
* star operators
|
33
39
|
* sharp operators
|
34
40
|
* coverage check (b is covered by a)
|
@@ -37,6 +43,28 @@ Following is the list of main features of this gem:
|
|
37
43
|
|
38
44
|
Here is an example usage of this gem.
|
39
45
|
|
46
|
+
```ruby
|
47
|
+
#!/usr/bin/env ruby
|
48
|
+
|
49
|
+
require 'bliftax'
|
50
|
+
|
51
|
+
abort "Usage: #{$PROGRAM_NAME} <blif file>" if ARGV.empty?
|
52
|
+
|
53
|
+
BLIF_FILE = ARGV.first
|
54
|
+
|
55
|
+
model = Bliftax.new(BLIF_FILE)
|
56
|
+
output = model.dup
|
57
|
+
|
58
|
+
model.gates.each_with_index do |gate, i|
|
59
|
+
final_cover = Bliftax::Optimizer.optimize(gate)
|
60
|
+
output.gates[i].implicants = final_cover.to_a
|
61
|
+
end
|
62
|
+
|
63
|
+
puts output.to_blif
|
64
|
+
```
|
65
|
+
|
66
|
+
Some other ways you can use this gem.
|
67
|
+
|
40
68
|
```ruby
|
41
69
|
require 'bliftax'
|
42
70
|
|
data/lib/bliftax/implicant.rb
CHANGED
@@ -98,6 +98,10 @@ class Bliftax
|
|
98
98
|
# * C = NULL if A_i * B_i = NULL for more than one i
|
99
99
|
# * Otherwise, C_i = A_i * B_i when A_i * B_i != NULL, and C_i = x (don't
|
100
100
|
# care) for the coordinate where A_i * B_i = NULL
|
101
|
+
#
|
102
|
+
# @param rhs [Implicant] the right hand side of the operation.
|
103
|
+
#
|
104
|
+
# @return [Implicant] the result of the star operation.
|
101
105
|
def star(rhs)
|
102
106
|
# Sanity check
|
103
107
|
unless @inputs.size == rhs.inputs.size
|
@@ -141,7 +145,7 @@ class Bliftax
|
|
141
145
|
# * C = NULL if A_i # B_i = EPSILON for all i
|
142
146
|
# * Otherwise, C = union of A_i = B_i' (negated) if A_i = x and B_i != x
|
143
147
|
#
|
144
|
-
# @param rhs [Implicant] the right hand side of the operation
|
148
|
+
# @param rhs [Implicant] the right hand side of the operation.
|
145
149
|
#
|
146
150
|
# @return [Set<Implicant>] Note that the set size could be 1.
|
147
151
|
def sharp(rhs)
|
@@ -301,6 +305,23 @@ class Bliftax
|
|
301
305
|
format '%s %s', @inputs.map(&:bit).join, @output.bit
|
302
306
|
end
|
303
307
|
|
308
|
+
# Creates a dummy implicant with the given inputs.
|
309
|
+
# This is useful when checking if an implicant is equal to what is
|
310
|
+
# expected. The input labels are labeled from 'a' to 'z' then 'A' to 'Z',
|
311
|
+
# and the output label is 'out'.
|
312
|
+
#
|
313
|
+
# @param inputs [String, Array<String>] the input bits.
|
314
|
+
# @param output [String] the output bit.
|
315
|
+
#
|
316
|
+
# @return [Implicant] an implicant with dummy labels and the bits set to
|
317
|
+
# the given bits.
|
318
|
+
def self.make_dummy(inputs, output = '1')
|
319
|
+
labels = ('a'..'z').to_a + ('A'..'Z').to_a
|
320
|
+
in_labels = labels.first(inputs.size)
|
321
|
+
input_str = inputs.is_a?(Array) ? inputs.join : inputs
|
322
|
+
Implicant.new(in_labels, 'out', format('%s %s', input_str, output))
|
323
|
+
end
|
324
|
+
|
304
325
|
# Creates a new NULL Implicant.
|
305
326
|
#
|
306
327
|
# @return [Implicant] a null Implicant
|
@@ -0,0 +1,202 @@
|
|
1
|
+
class Bliftax
|
2
|
+
# A module that does 2-level logic optimization
|
3
|
+
module Optimizer
|
4
|
+
module_function
|
5
|
+
|
6
|
+
# Does 2-level logic optimization to the given gate (a set of implicants).
|
7
|
+
# This is done in the following steps (as described in the book
|
8
|
+
# Fundamentals of Digital Logic with Verilog Design.
|
9
|
+
#
|
10
|
+
# 1. Find all prime implicants by using the star operator until the
|
11
|
+
# resulting set of implicants is the same as the previous round.
|
12
|
+
# 2. Apply the sharp operation to one of the prime implicants against all
|
13
|
+
# other implicants. The operation is cascaded. If the result is not
|
14
|
+
# null, it is an essential prime implicant. Do this for all prime
|
15
|
+
# implicants.
|
16
|
+
# 3. For the non-essential prime implicants, remove the ones that can be
|
17
|
+
# covered by some other implicant but with less cost.
|
18
|
+
# 4. For the remaining implicants, use the branching heuristic to find the
|
19
|
+
# set of prime implicants with the minimum cost.
|
20
|
+
#
|
21
|
+
# @param gate [Bliftax::Gate, Set<Implicant>, Array<Implicant>] the set of
|
22
|
+
# implicants to be optimized.
|
23
|
+
#
|
24
|
+
# @return [Set<Implicant>] the resulting optimized set of implicants that
|
25
|
+
# covers the same minterms.
|
26
|
+
def optimize(gate)
|
27
|
+
implicants = gate.is_a?(Bliftax::Gate) ? gate.implicants : gate
|
28
|
+
primes = get_prime_implicants(implicants)
|
29
|
+
|
30
|
+
essentials = get_essential_implicants(primes)
|
31
|
+
|
32
|
+
# Essential implicants will always be essential
|
33
|
+
primes -= essentials
|
34
|
+
|
35
|
+
# Find minterms that still needs to be covered
|
36
|
+
need_cover = implicants.map(&:minterms).to_set.flatten
|
37
|
+
need_cover -= essentials.map(&:minterms).to_set.flatten
|
38
|
+
|
39
|
+
# Remove implicants with higher cost
|
40
|
+
primes.to_a.permutation(2).each do |a, b|
|
41
|
+
# If b has same coverage but cheaper
|
42
|
+
if a.cost > b.cost && (a.minterms & need_cover) <= b.minterms
|
43
|
+
primes.delete(a)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Branching heuristic
|
48
|
+
to_use = branching(need_cover, primes)
|
49
|
+
|
50
|
+
essentials.union(to_use)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the prime implicants of the given set of implicants.
|
54
|
+
# This method successively applies the star operations to the given
|
55
|
+
# implicants to find the prime implicants.
|
56
|
+
#
|
57
|
+
# @param implicants [Set<Implicant>, Array<Implicant>] the original set of
|
58
|
+
# implicants.
|
59
|
+
#
|
60
|
+
# @return [Set<Implicant>] a set of prime implicants.
|
61
|
+
def get_prime_implicants(implicants)
|
62
|
+
# Use star operator to find prime implicants
|
63
|
+
star_set = Set.new(implicants)
|
64
|
+
prev_set = Set.new
|
65
|
+
|
66
|
+
loop do
|
67
|
+
prev_set = star_set.dup
|
68
|
+
prev_set.to_a.combination(2).each do |a, b|
|
69
|
+
result = a * b
|
70
|
+
star_set.add(result) unless result.null?
|
71
|
+
end
|
72
|
+
|
73
|
+
# Remove redundant implicants
|
74
|
+
union = star_set.union(prev_set)
|
75
|
+
union.to_a.permutation(2).each do |a, b|
|
76
|
+
star_set.delete(b) if a.covers?(b)
|
77
|
+
end
|
78
|
+
|
79
|
+
break if star_set == prev_set
|
80
|
+
end
|
81
|
+
|
82
|
+
star_set
|
83
|
+
end
|
84
|
+
|
85
|
+
# Finds the essential implicants of the given set of implicants.
|
86
|
+
# This method applies the sharp operation to an implicant against each of
|
87
|
+
# the other implicants in the set, and adds the tested implicant if and
|
88
|
+
# only if the result of the cascaded sharp operation is not null.
|
89
|
+
#
|
90
|
+
# @param implicants [Set<Implicant>, Array<Implicant>] the original set of
|
91
|
+
# implicants.
|
92
|
+
#
|
93
|
+
# @return [Set<Implicant>] the essential implicants of the given set of
|
94
|
+
# implicants.
|
95
|
+
def get_essential_implicants(implicants)
|
96
|
+
sharp_set = Set.new
|
97
|
+
|
98
|
+
implicants.each do |a|
|
99
|
+
result_set = Set.new([a])
|
100
|
+
|
101
|
+
implicants.each do |b|
|
102
|
+
next if b == a
|
103
|
+
|
104
|
+
copy = result_set.dup
|
105
|
+
result_set.clear
|
106
|
+
copy.each do |new_a|
|
107
|
+
result_set.add(new_a.sharp(b))
|
108
|
+
end
|
109
|
+
result_set.flatten!
|
110
|
+
result_set.delete_if { |s| s.null? }
|
111
|
+
end
|
112
|
+
|
113
|
+
sharp_set.add(a) unless result_set.empty?
|
114
|
+
end
|
115
|
+
|
116
|
+
sharp_set
|
117
|
+
end
|
118
|
+
|
119
|
+
# Cost heuristic.
|
120
|
+
# If an Implicant is given, the cost of that Implicant is returned.
|
121
|
+
# Otherwise, the sum of the number of implicants and the cost of each
|
122
|
+
# implicant is returned as the cost for that collection.
|
123
|
+
#
|
124
|
+
# @param what [Implicant, #to_a<#cost>] the thing to be evaluated.
|
125
|
+
#
|
126
|
+
# @return the evaluated cost
|
127
|
+
def cost(what)
|
128
|
+
return what.cost if what.is_a?(Bliftax::Implicant)
|
129
|
+
|
130
|
+
cost_sum = what.size
|
131
|
+
cost_sum += what.to_a.reduce(0) { |a, e| a + e.cost }
|
132
|
+
cost_sum
|
133
|
+
end
|
134
|
+
|
135
|
+
# Finds the minimum-cost combination to cover the given minterms.
|
136
|
+
# This method does recursive DFS to search for the minimum-cost
|
137
|
+
# combination, so it may become slow depending on the number of available
|
138
|
+
# options.
|
139
|
+
#
|
140
|
+
# @param to_cover [Set<Integer>] the set of minterms that need to be
|
141
|
+
# covered.
|
142
|
+
# @param options [Set<Implicant>] the set of implicants to choose from.
|
143
|
+
def branching(to_cover, options)
|
144
|
+
to_use = Set.new
|
145
|
+
options.dup.each do |implicant|
|
146
|
+
result = branching_helper(to_cover, options, implicant)
|
147
|
+
if result.include?(implicant)
|
148
|
+
to_use.add(implicant)
|
149
|
+
to_cover -= implicant.minterms
|
150
|
+
options.delete(implicant)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
to_use
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
module_function
|
159
|
+
|
160
|
+
# A helper method for the recursive DFS for branching heuristic.
|
161
|
+
#
|
162
|
+
# @param to_cover [Set<Integer>] minterms that need to be covered.
|
163
|
+
# @param options [Set<Implicant>] the prime implicants that we can choose
|
164
|
+
# from.
|
165
|
+
# @param implicant [Implicant] the implicant that's being decided whether
|
166
|
+
# to include in the final cover or not.
|
167
|
+
# @return the set that results in a better cost when including or not
|
168
|
+
# including the implicant.
|
169
|
+
def branching_helper(to_cover, options, implicant)
|
170
|
+
# Get implicants from options that cover at least one required vertex
|
171
|
+
options = options.select do |o|
|
172
|
+
!(to_cover & o.minterms).empty?
|
173
|
+
end
|
174
|
+
options = options.to_set
|
175
|
+
|
176
|
+
# Base case
|
177
|
+
return Set.new if options.empty?
|
178
|
+
|
179
|
+
# Final cover when using the implicant
|
180
|
+
new_options = options - Set.new([implicant])
|
181
|
+
using_implicant = branching_helper(to_cover - implicant.minterms,
|
182
|
+
new_options,
|
183
|
+
new_options.to_a.first)
|
184
|
+
# Don't forget to add this implicant
|
185
|
+
using_implicant.add(implicant)
|
186
|
+
# Final cover when not using the implicant
|
187
|
+
not_using_implicant = branching_helper(to_cover,
|
188
|
+
new_options,
|
189
|
+
new_options.to_a.first)
|
190
|
+
|
191
|
+
cost_using = cost(using_implicant)
|
192
|
+
cost_not_using = cost(not_using_implicant)
|
193
|
+
|
194
|
+
# Not using this implicant results in as good a coverage but less cost
|
195
|
+
minterms_coverage = not_using_implicant.map(&:minterms).to_set.flatten
|
196
|
+
if cost_not_using < cost_using && minterms_coverage >= to_cover
|
197
|
+
return not_using_implicant
|
198
|
+
end
|
199
|
+
return using_implicant
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
data/lib/bliftax/version.rb
CHANGED
data/lib/bliftax.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bliftax
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Naoki Mizuno
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-10-
|
11
|
+
date: 2015-10-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -73,6 +73,7 @@ files:
|
|
73
73
|
- lib/bliftax/gate.rb
|
74
74
|
- lib/bliftax/implicant.rb
|
75
75
|
- lib/bliftax/implicant/bit.rb
|
76
|
+
- lib/bliftax/optimizer.rb
|
76
77
|
- lib/bliftax/version.rb
|
77
78
|
homepage: https://github.com/NigoroJr/bliftax
|
78
79
|
licenses:
|