Module: ABC

Defined in:
ext/ruby_abc.c,
lib/ruby_abc.rb,
ext/ruby_abc.c

Overview

ABC module wrapps the command line interface of the Berkeley abc program. abc is a system for sequential synthesis and verification, developped at the University of California, Berkeley. Documentation on ABC can be found at [people.eecs.berkeley.edu/~alanmi/abc/](https://people.eecs.berkeley.edu/~alanmi/abc/)

The abc program is a command line interface: the user issue commands and adjust them according to the results he gets back. In order to automate this process, the ABC module allows to retreive some information on the logic network in ruby, such as the number of nodes or its logic level. The command adjustment can then be done in ruby instead of manually.

abc commands are issued with the ABC::run_command module method, thus the abc documentation is not covered in this API documentation. However, available commands can be retreived with ABC::run_command(‘help’), and the documentation of a given command can be retreived with ABC::run_command(‘command_name -h’).

Constant Summary collapse

VERSION =
'0.0.6'

Class Method Summary collapse

Class Method Details

.echo_commandsObject

echo_commands -> true or false

Return if each command received is echoed back.

ABC.echo_commands #=> true


164
165
166
167
168
169
# File 'ext/ruby_abc.c', line 164

static VALUE rubyabc_get_echo_commands(VALUE self)
{
	if (rubyabc_echo_commands)
		return Qtrue;
	return Qfalse;
}

.echo_commands=(b) ⇒ Object

echo_commands= (boolean)

Set whether each command is echoed or not.

ABC.echo_commands = false


179
180
181
182
183
184
185
186
187
188
189
# File 'ext/ruby_abc.c', line 179

static VALUE rubyabc_set_echo_commands(VALUE self, VALUE b)
{
	if (b == Qtrue)
		rubyabc_echo_commands = 1;
	else if (b == Qfalse || b == Qnil)
		rubyabc_echo_commands = 0;
	else
		rb_raise(rb_eTypeError, "expecting true or false");

	return Qnil;
}

.map(k, optimize_area = false, verbose: true) ⇒ Object

call-seq: map (lut_size, optimize_for_area = false, verbose: true) -> nb_nodes

Map the logic network to nodes with at most lut_size inputs. If optimize_for_area is true, minimize the number of nodes instead of minimizing the logic level of the network.

This method loops the command:

"choice2; if -K #{lut_size}#{optimize_for_area ? ' -a':''}; ps"

and stops when the number of nodes stop decreasing.

ABC.map(4)


87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/ruby_abc.rb', line 87

def self.map (k, optimize_area = false, verbose: true)
	map_cmd = "choice2; if -K #{k}#{optimize_area ? ' -a':''}#{verbose ? '; ps':''}"

	n_nodes = -1
	loop do
		ABC::run_command(map_cmd)
		new_n_nodes = ABC::nb_nodes
		break if new_n_nodes >= n_nodes and n_nodes > 0
		n_nodes = new_n_nodes
	end
	return ABC::nb_nodes
end

.method_missing(name, *args) ⇒ Object

call-seq: method_missing(method, *args)

The method_missing method is implemented so that commands can be issued to abc without using ABC::run_command. Methods arguments are concatenated as strings to the command.

ABC.read '-m', filename
# equivalent to
ABC.run_command "read -m #{filename}"


112
113
114
115
116
# File 'lib/ruby_abc.rb', line 112

def self.method_missing (name, *args)
	cmd = name.to_s
	cmd += ' ' + args.join(' ') if args
	ABC::run_command cmd
end

.nb_andsObject

nb_ands -> integer or nil

Return the number of AND gate in the network. If no network have been loaded or the then network is not in AIG form, return nil.

ABC.nb_ands #=> 264


16
17
18
19
20
21
22
23
24
# File 'ext/ruby_abc.c', line 16

static VALUE rubyabc_n_ands(VALUE self)
{
	int res = rubyabc_c_n_ands();

	if (res < 0)
		return Qnil;

	return INT2NUM(res);
}

.nb_latchesObject

nb_latches -> integer or nil

Return the number of latches in the network. If no network have been loaded, return nil.

ABC.nb_latches #=> 17


92
93
94
95
96
97
98
99
100
# File 'ext/ruby_abc.c', line 92

static VALUE rubyabc_n_latches(VALUE self)
{
	int res = rubyabc_c_n_latches();

	if (res < 0)
		return Qnil;

	return INT2NUM(res);
}

.nb_levelsObject

nb_levels -> integer or nil

Return the number of logic levels. This is the number of combinatorial logic between two latches or primary input/output. If no network have been loaded, return nil.

ABC.nb_latches #=> 8


113
114
115
116
117
118
119
120
121
# File 'ext/ruby_abc.c', line 113

static VALUE rubyabc_n_levels(VALUE self)
{
	int res = rubyabc_c_n_levels();

	if (res < 0)
		return Qnil;

	return INT2NUM(res);
}

.nb_nodesObject

nb_nodes -> integer or nil

Return the number combinatorial logic nodes in the network. If no network have been loaded or the then network is in AIG form, return nil.

ABC.nb_nodes #=> 114


35
36
37
38
39
40
41
42
43
# File 'ext/ruby_abc.c', line 35

static VALUE rubyabc_n_nodes(VALUE self)
{
	int res = rubyabc_c_n_nodes();

	if (res < 0)
		return Qnil;

	return INT2NUM(res);
}

.nb_primary_inputsObject

nb_primary_inputs -> integer or nil

Return the number of primary inputs of the network. If no network have been loaded, return nil.

ABC.nb_primary_inputs #=> 7


54
55
56
57
58
59
60
61
62
# File 'ext/ruby_abc.c', line 54

static VALUE rubyabc_n_pis(VALUE self)
{
	int res = rubyabc_c_n_pis();

	if (res < 0)
		return Qnil;

	return INT2NUM(res);
}

.nb_primary_outputsObject

nb_primary_outputs -> integer or nil

Return the number of primary outputs of the network. If no network have been loaded, return nil.

ABC.nb_primary_outputs #=> 6


73
74
75
76
77
78
79
80
81
# File 'ext/ruby_abc.c', line 73

static VALUE rubyabc_n_pos(VALUE self)
{
	int res = rubyabc_c_n_pos();

	if (res < 0)
		return Qnil;

	return INT2NUM(res);
}

.optimize(verbose: true) ⇒ Object

call-seq: optimize (verbose: true) -> nb_ands

Combinatorial synthesis of the network. This method loops the command “resyn; resyn2, resyn3, print_stats” and stops when the number of AND gates stop decreasing. At the end, the logic network is in AIG form.

“Fast and efficient synthesis is achieved by DAG-aware rewriting of the AIG. i Rewriting is performed using a library of pre-computed four-input AIGs (command rewrite; standard alias rw), or collapsing and refactoring of logic cones with 10-20 inputs (command refactor; standard alias rf). It can be experimentally shown that iterating these two transformations and interleaving them with AIG balancing (command balance; standard alias b) substantially reduces the AIG size and tends to reduce the number of AIG levels.”

ABC.optimize


61
62
63
64
65
66
67
68
69
70
71
# File 'lib/ruby_abc.rb', line 61

def self.optimize (verbose: true)
	cmd = 'resyn; resyn2; resyn3' + (verbose ? '; ps' : '')
	n_ands = ABC::nb_ands
	loop do
		ABC::run_command(cmd)
		new_n_ands = ABC::nb_ands
		break unless new_n_ands < n_ands
		n_ands = new_n_ands
	end 
	return ABC::nb_ands
end

call-seq: print_stats

Print out network statistics.

ABC.print_stats
# Inputs: ..... 7
# Outputs: .... 6
# Nodes: .... 114
# Latches: ... 17
# Levels: .... 18
#=> nil


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/ruby_abc.rb', line 23

def self.print_stats
	n_pis     = ABC::nb_primary_inputs
	n_pos     = ABC::nb_primary_outputs
	n_nodes   = ABC::nb_nodes
	n_latches = ABC::nb_latches
	n_levels  = ABC::nb_levels
	n_ands    = ABC::nb_ands

	arr = [n_pis, n_pos, n_nodes, n_latches, n_levels, n_ands].reject{|v| v.nil?}
	return nil if arr.empty?
	max_len = arr.collect{|v| v.to_s.length}.max + 1

	puts "Inputs: ...#{(' ' + n_pis.to_s).rjust(max_len, '.')}"     if n_pis
	puts "Outputs: ..#{(' ' + n_pos.to_s).rjust(max_len, '.')}"     if n_pos
	puts "Nodes: ....#{(' ' + n_nodes.to_s).rjust(max_len, '.')}"   if n_nodes
	puts "Latches: ..#{(' ' + n_latches.to_s).rjust(max_len, '.')}" if n_latches
	puts "And gates: #{(' ' + n_ands.to_s).rjust(max_len, '.')}"    if n_ands
	puts "Levels: ...#{(' ' + n_levels.to_s).rjust(max_len, '.')}"  if n_levels
	return nil
end

.run_command(cmd) ⇒ Object

run_command (string)

Run the command string in ABC.

If ABC::echo_commands has been set to true, string will be echoed on STDOUT before it is given to abc.

ABC.run_command 'read netlist.blif; strash; resyn; if -K 4; write out.blif'
# To see available ABC commands:
ABC.run_command 'help'

# To get help on a particular command, use `-h' after the command name:
ABC.run_command 'csweep -h'


138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'ext/ruby_abc.c', line 138

static VALUE rubyabc_run_command(VALUE self, VALUE cmd)
{
	VALUE printed_str;

	Check_Type(cmd, T_STRING);


	if (rubyabc_echo_commands) {
		printed_str = rb_sprintf("Command: \"%"PRIsVALUE"\"", cmd);
		rb_io_puts(1, &printed_str, rb_stdout);
	}

	if (rubyabc_c_run_command(StringValueCStr(cmd)))
		return Qfalse;

	return Qtrue;
}

.synthesis(input: nil, output: nil, zero: false, sweep: false, retime: false, lcorr: false, area: false, lut_map: nil, help: nil, verbose: true) ⇒ Object

call-seq: synthesis (input: nil, output: nil, zero: false, sweep: false, retime: false, lcorr: false, area: false, lut_map: nil, verbose: true, help: nil)

This method is an atempt to automate the logic synthesis process (combinatorial and sequential) according to some parameters.

Keyword parameters are:

  • input: (string) Input netilist file name

  • output: (string) Output netlist file name

  • zero: (boolean) Set latches initial value to zero (netlist functionality is keept by adding inverters around latches with initial value 1)

  • sweep: (boolean) Sweep logic network to remove dangling nodes

  • retime: (boolean) Retime the netlist (reduce logic level, but increase the number of latches)

  • lcorr: (boolean) Computes latch correspondence using 1-step induction

  • area: (boolean) Optimize for area (minimize number of nodes) instead of speed (minimize number of logic levels)

  • lut_map: (integer) Map the netlist to K-input LUTs. Optimize for area if area: == true

ABC.synthesis(input: 'generic.blif', output: 'maped.blif', lcorr: true, lut_map: 4, area: true)


137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/ruby_abc.rb', line 137

def self.synthesis(input: nil, output: nil, zero: false, sweep: false, retime: false, lcorr: false, area: false, lut_map: nil, help: nil, verbose: true)
	if help then
		puts <<EOS
ABC::synthesis keyword arguments:
input:   (string)  Input netilist file name
output:  (string)  Output netlist file name
zero:    (boolean) Set latches initial value to zero (netlist functionality is keept by adding inverters around latches with initial value 1)
sweep:   (boolean) Sweep logic network to remove dangling nodes
retime:  (boolean) Retime the netlist (reduce logic level, but increase the number of latches)
lcorr:   (boolean) Computes latch correspondence using 1-step induction
area:    (boolean) Optimize for area (minimize number of nodes) instead of speed (minimize number of logic levels)
lut_map: (integer) Map the netlist to K-input LUTs. Optimize for area if area: == true
EOS
		return
	end

	if input then
		raise "Expecting a string for :input argument" unless input.kind_of?(String)
		ABC::read input
	end

	puts "---- Input netlist ----"
	ABC::print_stats
	puts "-----------------------"

	ABC::ps if verbose

	if sweep then
		ABC::sweep
		ABC::ps
	end

	if zero and ABC::nb_latches > 0 then
		ABC::run_command 'strash; zero'
	else
		ABC::run_command 'strash'
	end
	ABC::ps if verbose

	ABC::lcorr if (lcorr and ABC::nb_latches > 0)

	if sweep then
		ABC::ssweep
		ABC::csweep
		ABC::ps if verbose
	end

	ABC::optimize(verbose: verbose)

	if retime and ABC::nb_latches > 0 then
		level = ABC::nb_levels
		ABC::run_command('retime; strash')
		ABC::ps if verbose
		if ABC::nb_levels >= level then
			ABC::run_command('dretime; strash')
			ABC::ps if verbose
		end
		ABC::optimize(verbose: verbose)
	end

	ABC::zero if zero

	if lut_map then
		raise "Expecting a strictly positive integer for argument :lut_map" unless (lut_map.kind_of?(Integer) and lut_map > 0)
		ABC::map(lut_map, not(not(area)), verbose: verbose)
	end

	if sweep then
		ABC::run_command 'sop; sweep; resyn; resyn2; resyn3; scleanup; resyn; scleanup; scleanup; scleanup; strash; ssweep; csweep'
		ABC::ps if verbose
		ABC::optimize(verbose: verbose)
		if lut_map then
			raise "Expecting a strictly positive integer for argument :lut_map" unless (lut_map.kind_of?(Integer) and lut_map > 0)
			ABC::map(lut_map, not(not(area)), verbose: verbose)
		end
	end

	if (lcorr and ABC::nb_latches > 0) then
		ABC::run_command 'strash; lcorr'
		if lut_map then
			raise "Expecting a strictly positive integer for argument :lut_map" unless (lut_map.kind_of?(Integer) and lut_map > 0)
			ABC::map(lut_map, not(not(area)), verbose: verbose)
		end
	end

	puts "---- output netlist ----"
	ABC::print_stats
	puts "------------------------"

	if output then
		raise "Expecting a string for :output argument" unless output.kind_of?(String)
		puts "Writing output netlist to file \"#{output}\""
		if input then
			ABC.write_hie input, output
		else
			ABC.write output
		end
	end
end