Commit 0458d5c2 authored by Théotime BOLLENGIER's avatar Théotime BOLLENGIER
Browse files

constraint io locations

parent ea950c8a
require 'nokogiri'
require 'colorize'
require_relative 'architecture.rb'
module ArGen
class PackingIO
attr_accessor :number, :input_io, :output_io, :pos_X, :pos_Y, :subblk
def initialize (pos_X, pos_Y, subblk)
@number = nil
@input_io = nil
@output_io = nil
@pos_X = pos_X
@pos_Y = pos_Y
@subblk = subblk
end
def to_s
return "IO (#{@pos_X},#{@pos_Y})[#{@subblk}] #{@number}\n input: #{@input_io}\n output: #{@output_io}"
end
def <=> (other)
res = @pos_Y <=> other.pos_Y
return res if res != 0
res = @pos_X <=> other.pos_X
return res if res != 0
return @subblk <=> other.subblk
end
def name
if @output_io then
return @output_io.name
else
return @input_io.name
end
end
def to_xml_node (doc)
io_block = Nokogiri::XML::Node.new 'block', doc
io_block['name'] = name
io_block['instance'] = "io[#{@number}]"
io_block['mode'] = "io"
inputs = Nokogiri::XML::Node.new 'inputs', doc
ports = Nokogiri::XML::Node.new 'port', doc
ports['name'] = 'outpad'
ports.content = @output_io.nil? ? 'open ' : "#{@output_io.signame} "
inputs.add_child(ports)
io_block.add_child(inputs)
outputs = Nokogiri::XML::Node.new 'outputs', doc
ports = Nokogiri::XML::Node.new 'port', doc
ports['name'] = 'inpad'
ports.content = @input_io.nil? ? 'open ' : 'inpad[0].inpad[0]->inpad '
outputs.add_child(ports)
io_block.add_child(outputs)
clocks = Nokogiri::XML::Node.new 'clocks', doc
ports = Nokogiri::XML::Node.new 'port', doc
ports['name'] = 'clock'
ports.content = 'open '
clocks.add_child(ports)
io_block.add_child(clocks)
blk_inpad = Nokogiri::XML::Node.new 'block', doc
if @input_io.nil? then
blk_inpad['name'] = 'open'
else
blk_inpad['name'] = @input_io.name
blk_inpad.add_child(Nokogiri::XML::Node.new 'inputs', doc)
outputs = Nokogiri::XML::Node.new 'outputs', doc
ports = Nokogiri::XML::Node.new 'port', doc
ports['name'] = 'inpad'
ports.content = "#{@input_io.name} "
outputs.add_child(ports)
blk_inpad.add_child(outputs)
blk_inpad.add_child(Nokogiri::XML::Node.new 'clocks', doc)
end
blk_inpad['instance'] = 'inpad[0]'
io_block.add_child(blk_inpad)
blk_outpad = Nokogiri::XML::Node.new 'block', doc
if @output_io.nil? then
blk_outpad['name'] = 'open'
else
blk_outpad['name'] = @output_io.name
inputs = Nokogiri::XML::Node.new 'inputs', doc
ports = Nokogiri::XML::Node.new 'port', doc
ports['name'] = 'outpad'
ports.content = "io.outpad[0]->outpad "
inputs.add_child(ports)
blk_outpad.add_child(inputs)
blk_outpad.add_child(Nokogiri::XML::Node.new 'outputs', doc)
blk_outpad.add_child(Nokogiri::XML::Node.new 'clocks', doc)
end
blk_outpad['instance'] = 'outpad[0]'
io_block.add_child(blk_outpad)
return io_block
end
end
class IOFromPacking
attr_accessor :name, :signame, :direction, :io_index, :pos_X, :pos_Y, :subblk
def initialize
@name = nil
@signame = nil
@direction = nil
@io_index = nil
@pox_X = nil
@pox_Y = nil
@subblk = nil
end
def to_s
str = ''
str += "OUTPUT".light_yellow.bold if @direction == :output
str += "INPUT".light_green.bold if @direction == :input
str += " name[#{@name.light_cyan}]"
str += " signal[#{@signame.light_blue}]"
str += " @#{@io_index}" if @io_index
str += " (#{@pos_X},#{@pos_Y})[#{@subblk}]" unless @pos_X.nil? or @pos_Y.nil? or @subblk.nil?
return str
end
end
class ConstrainedIO
attr_accessor :name, :index
def initialize (name = nil, index = nil)
@name = name
@index = index
end
def to_s
return "ArGen::ConstrainedIO: \"#{@name}\" @#{@index}"
end
end
module Architecture
class Arch
def repack_vpr_packing_according_to_constraints (constraint:, packing_in:, packing_out:, placement_out:)
constrained_ios = read_io_location_constraint_file(constraint)
#constrained_ios.each{|cio| puts cio}
#puts
packed_ios = extract_inputs_from_vpr_packing_file(packing_in)
#packed_ios.each{|pio| puts pio}
#puts
match_constrained_IOs_with_packing_IOs(constrained_ios, packed_ios)
#packed_ios.each{|pio| puts pio}
#puts
find_packed_IO_location_in_the_matrix(packed_ios)
#packed_ios.each{|pio| puts pio}
#puts
new_packing_ios = fill_packing_IOs(packed_ios)
#new_packing_ios.each{|pio| puts pio}
#puts
write_new_packing_file(new_packing_ios, packing_in, packing_out)
write_io_placement_file(new_packing_ios, placement_out)
end # ArGen::Architecture::Arch#repack_vpr_packing_according_to_constraints
private
def write_io_placement_file (new_packing_ios, placement_out)
name_str_len = new_packing_ios.collect{|p| p.name.to_s.length}.max
pos_x_str_len = new_packing_ios.collect{|p| p.pos_X.to_s.length}.max
pos_y_str_len = new_packing_ios.collect{|p| p.pos_Y.to_s.length}.max
subblk_str_len = new_packing_ios.collect{|p| p.subblk.to_s.length}.max
fout = File.open(placement_out, 'w')
new_packing_ios.each do |p|
fout.puts "#{p.name.to_s.ljust(name_str_len)} #{p.pos_X.to_s.rjust(pos_x_str_len)} #{p.pos_Y.to_s.rjust(pos_y_str_len)} #{p.subblk.to_s.rjust(subblk_str_len)} ##{p.number}"
end
fout.close
end # ArGen::Architecture::Arch#write_io_placement_file
def write_new_packing_file (new_packing_ios, packing_in, packing_out)
doc = File.open(packing_in) { |f| Nokogiri::XML(f){|config| config.strict} }
doc.search("/block/block[@mode='io']").remove
i = 0
doc.xpath("/block/block").each do |block_node|
instance_name = block_node['instance']
next if instance_name.nil?
m = instance_name.match(/^(.+?)\[\d+\]$/)
next if m.nil?
block_node['instance'] = m[1] + "[#{i}]"
i += 1
end
new_packing_ios.each{|pio| pio.number = i; i += 1}
root = doc.xpath('/block').first
new_packing_ios.each do |pio|
root.add_child(pio.to_xml_node(doc))
end
doc.xpath('//text()').each{|t| t.content = t.content.gsub(/^\s+/,'').gsub(/\s+$/,'')}
doc.xpath('//text()').each{|t| t.content = t.content.gsub(/\s\s+/,' ')}
doc.xpath('//text()').each{|t| t.remove if t.content =~ /^[\n\r\t]+$/}
doc.xpath('//text()').each{|t| t.remove if t.content.empty?}
File.write(packing_out, doc.serialize(indent_text: "\t", indent: 1, save_with: Nokogiri::XML::Node::SaveOptions::NO_DECLARATION | Nokogiri::XML::Node::SaveOptions::FORMAT).gsub('&gt;','>'))
end # ArGen::Architecture::Arch#write_new_packing_file
def fill_packing_IOs (packed_ios)
locations = packed_ios.collect{|pio| [pio.pos_X, pio.pos_Y, pio.subblk]}.uniq
new_packing_ios = locations.collect{|xys| ArGen::PackingIO.new(*xys)}.sort
packed_ios.each do |pio|
pckio = new_packing_ios.find{|pkio| pkio.pos_X == pio.pos_X and pkio.pos_Y == pio.pos_Y and pkio.subblk == pio.subblk}
if pio.direction == :output then
abort "#{pckio}\nalready has an output!, cannot put #{pio}!" unless pckio.output_io.nil?
pckio.output_io = pio
elsif pio.direction == :input then
abort "#{pckio}\nalready has an input!, cannot put #{pio}!" unless pckio.input_io.nil?
pckio.input_io = pio
end
end
new_packing_ios.reject!{|pkio| pkio.input_io.nil? and pkio.output_io.nil?}
return new_packing_ios
end # ArGen::Architecture::Arch#fill_packing_IOs
def find_packed_IO_location_in_the_matrix (packed_ios)
update_IOs_position()
packed_ios.each do |pio|
archIO = @ios.find{|io| io.vFpgaIoPinNumber == pio.io_index}
abort "No IO with index #{pio.io_index} found in the architecture.\nIt is requiered for #{pio}" unless archIO
pio.pos_X = archIO.locX
pio.pos_Y = archIO.locY
pio.subblk = archIO.index
end
end # Architecture::Arch#find_packed_IO_location_in_the_matrix
def match_constrained_IOs_with_packing_IOs (constrained_ios, packing_ios)
packing_io_by_name = {}
packing_ios.each do |pio|
name = pio.name.sub(/^out:/, '')
abort "IO \"#{pio}\" is not uniq!" unless packing_io_by_name[name].nil?
packing_io_by_name[name] = pio
end
constrained_ios.each do |cio|
pio = packing_io_by_name[cio.name]
abort "Cannot find IO \"#{cio.name}\" (from constraint file) among the IOs from the packing file!" if pio.nil?
pio.io_index = cio.index
end
## set arbitrary index to unconstrained IOs ##
free_input_indexes = (0 ... @vFpgaNbIoPins).collect{|i| i} - packing_ios.select{|pio| pio.direction == :input}.collect{|pio| pio.io_index}.reject(&:nil?)
free_output_indexes = (0 ... @vFpgaNbIoPins).collect{|i| i} - packing_ios.select{|pio| pio.direction == :output}.collect{|pio| pio.io_index}.reject(&:nil?)
packing_ios.select{|pio| pio.direction == :input and pio.io_index.nil?}.each{|pio| pio.io_index = free_input_indexes.pop}
packing_ios.select{|pio| pio.direction == :output and pio.io_index.nil?}.each{|pio| pio.io_index = free_output_indexes.pop}
abort "Not enough IO locations!" if packing_ios.collect(&:io_index).include?(nil)
end # Architecture::Arch#match_constrained_IOs_with_packing_IOs
def extract_inputs_from_vpr_packing_file (file_name)
doc = File.open(file_name) { |f| Nokogiri::XML(f) }
#netlist_name = doc.xpath('/block[1]').first['name']
#puts netlist_name.bold
inputs = doc.xpath('/block/inputs').collect{|n| n.text.split(/[ \t\n\r]+/)}.flatten.reject{|s| s.length == 0}
#puts "Inputs: ".light_green.bold
#puts inputs.inspect.gsub(/[\[\]"]/, '')
outputs = doc.xpath('/block/outputs').collect{|n| n.text.split(/[ \t\n\r]+/)}.flatten.reject{|s| s.length == 0}
#puts "Outputs: ".light_yellow.bold
#puts outputs.inspect.gsub(/[\[\]"]/, '')
ios = doc.xpath("/block/block[@mode='io']")
pios = []
ios.each do |ionode|
# Circuit input #
unless ionode.xpath("outputs/port").first.text =~ /\s*open\s*/i then
npio = ArGen::IOFromPacking.new
npio.direction = :input
npio.name = ionode.xpath("block[@instance='inpad[0]']/outputs/port").first.text.gsub(/\s+/, '')
npio.signame = npio.name
pios << npio
end
# Circuit output #
unless ionode.xpath("inputs/port").first.text =~ /\s*open\s*/i then
npio = ArGen::IOFromPacking.new
npio.direction = :output
npio.signame = ionode.xpath("inputs/port").first.text.gsub(/\s+/, '')
npio.name = ionode.xpath("block[@instance='outpad[0]']").first['name']
pios << npio
end
end
pin = pios.select{|io| io.direction == :input}
pout = pios.select{|io| io.direction == :output}
inputs.each do |n|
abort "Input #{n} not found!".light_red unless pin.index{|p| p.name == n}
end
outputs.each do |n|
abort "Output #{n} not found!".light_red unless pout.index{|p| p.name == n}
end
return pios
end # Architecture::Arch#extract_inputs_from_vpr_packing_file
def read_io_location_constraint_file (file_name)
begin
content = File.read(file_name).gsub(/#.*?$/,'').gsub(/^\s+/,'').gsub(/\n+/, "\n").gsub(/\A\n+/,'')
rescue Exception => e
abort "Cannot open file \"#{file_name}\": #{e.message}"
end
constrained_ios = []
content.lines.each do |line|
match = line.match(/^\s*(\w+)\s*(\[\s*(\d+)\s*(:\s*(\d+)\s*)?\])?\s+(\d+)\s*(:\s*(\d+)\s*)?$/)
abort "ERROR: Parsing error in file \"#{file_name}\":\n \"#{line.chomp}\"" if match.nil?
sigName = match[1]
if match[2] then
fromMsbBit = match[3].to_i
if match[4] then
fromLsbBit = match[5].to_i
else
fromLsbBit = fromMsbBit
end
else
fromMsbBit = nil
fromLsbBit = nil
end
toMsbBit = match[6].to_i
if match[7] then
toLsbBit = match[8].to_i
else
toLsbBit = toMsbBit
end
toLsbBit, toMsbBit = [toMsbBit, toLsbBit] if toLsbBit > toMsbBit
if fromMsbBit.nil? then
fromMsbBit = toMsbBit - toLsbBit
end
if fromLsbBit.nil? then
fromLsbBit = 0
end
fromLsbBit, fromMsbBit = [fromMsbBit, fromLsbBit] if fromLsbBit > fromMsbBit
abort "ERROR: In file \"#{file_name}\": index cannot be negative\n \"#{line.chomp}\"" if fromLsbBit < 0
if (fromMsbBit - fromLsbBit) != (toMsbBit - toLsbBit) then
abort "ERROR: In file \"#{file_name}\": bitwidths mismatch (#{fromMsbBit + 1 - fromLsbBit} and #{toMsbBit + 1 - toLsbBit})\n \"#{line.chomp}\""
end
from_index = fromLsbBit
to_index = toLsbBit
(fromLsbBit .. fromMsbBit).each do |i|
constrained_ios << ArGen::ConstrainedIO.new(sigName + "[#{from_index}]", to_index)
from_index += 1
to_index += 1
end
end
return constrained_ios
end # Architecture::Arch#read_io_location_constraint_file
end # Architecture::Arch
end # Architecture
end
require 'optparse'
require_relative '../../lib/argen/version.rb'
require_relative '../../lib/argen/arch_reader.rb'
require_relative '../../lib/argen/constraint_io_locations.rb'
$copyright = "Copyright (C) 2017-2018,2020 ENSTA Bretagne"
$authors = [
["Théotime Bollengier", "<theotime.bollengier@ensta-bretagne.fr>"]
]
$program_name = File.basename(__FILE__, '.*')
$program_version = ArGen::VERSION
$description = "This program modifies VPR output packing files to precisely constraint IO locations."
$description_string = "\nWelcome to #{$program_name} #{$program_version}, a component of the ArGen framework.\n#{$description}\n#{$copyright}\n#{$authors.collect.with_index{|a, i| "#{(i == 0) ? "Author#{($authors.length > 1) ? 's':''}: " : " #{($authors.length > 1) ? ' ':''} "}#{a.first} #{a.last}\n"}.join}"
STDOUT.sync = true
STDERR.sync = true
options = {}
optparse = OptionParser.new do |opts|
opts.banner = "Usage: #{File.basename($0)} [options]"
options[:arch] = nil
opts.on('--arch FILE', 'Input ArGen architecture description file') do |s|
options[:arch] = s
end
options[:packing_in] = nil
opts.on('--packing_in FILE', 'Input packing file') do |s|
options[:packing_in] = s
end
options[:packing_out] = nil
opts.on('--packing_out FILE', 'Output packing file') do |s|
options[:packing_out] = s
end
options[:placement_out] = nil
opts.on('--placement_out FILE', 'Output IO placement file') do |s|
options[:placement_out] = s
end
options[:constraint] = nil
opts.on('--constraint FILE', "Input IO location constraint file") do |s|
options[:constraint] = s
end
opts.on('-h', '--help', 'Display this help') do
puts opts
puts $description_string
exit
end
end
begin
optparse.parse!
rescue Exception => e
if e.class == SystemExit then
exit 0
else
abort e.message + "\n" + optparse.to_s
end
end
STDERR.puts "WARNING: Unused commandline arguments: #{ARGV.join(' ')}" unless ARGV.empty?
if options[:arch].nil? then
STDERR.puts "ERROR: you must specify an ArGen architecture input file!"
abort optparse.to_s
end
if options[:packing_in].nil? then
STDERR.puts "ERROR: you must specify an VPR packing input file!"
abort optparse.to_s
end
if options[:packing_out].nil? then
STDERR.puts "ERROR: you must specify an packing output file!"
abort optparse.to_s
end
if options[:placement_out].nil? then
STDERR.puts "ERROR: you must specify an IO placement output file!"
abort optparse.to_s
end
if options[:constraint].nil? then
STDERR.puts "ERROR: you must specify an IO constraint input file!"
abort optparse.to_s
end
arch = ArGen::Architecture::Arch.new(ArGen::ArchReader.parse(options[:arch]))
arch.repack_vpr_packing_according_to_constraints(
constraint: options[:constraint],
packing_in: options[:packing_in],
packing_out: options[:packing_out],
placement_out: options[:placement_out]
)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment