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

configuration path

parent ff11ebb6
require_relative 'argen/version.rb'
require_relative 'argen/architecture.rb'
require_relative 'argen/arch_reader.rb'
require_relative 'argen/high_level_arch_generator/argen_arch_writer.rb'
require_relative 'argen/high_level_arch_generator/yavag_routing_graph.rb'
require_relative 'argen/high_level_arch_generator/vpr_architecture_writer.rb'
require_relative 'argen/high_level_arch_generator/yavag_route_parser.rb'
require_relative 'argen/hdl_arch_writer.rb'
require_relative 'argen/vpr_placement_reader.rb'
require_relative 'argen/vpr_route.rb'
require_relative 'argen/vpr_route_ast.rb'
require_relative 'argen/vpr_route_node_extensions.rb'
require_relative 'argen/vpr_route_parser.rb'
require_relative 'argen/vpr_route_reader.rb'
require_relative 'argen/netlist_reader.rb'
require_relative 'argen/vpr_packing_reader.rb'
require_relative 'argen/par_loader.rb'
require_relative 'argen/bitstream_extractor.rb'
require_relative 'argen/timing_analysis.rb'
require_relative 'argen/VirtBitgen.rb'
require_relative 'argen/VirtSta.rb'
require_relative 'argen/resource_usage_analyzer.rb'
require_relative 'argen/constraint_io_locations.rb'
require_relative 'argen/rewrite_placement_with_IO_locations.rb'
require_relative 'argen/config_shuffling.rb'
require_relative 'argen/graphics/place_and_route_viewer.rb'
require_relative 'argen/graphics/state_viewer.rb'
require_relative 'argen/graphics/resources_to_svg.rb'
......@@ -7,40 +7,40 @@ require_relative 'par_loader.rb'
module ArGen
module Architecture
class Arch
def extract_bitstream_wrapped_for_SPI_wrapper(ioLocationReportFile: options[:ioloc])
vFpgaCoreInputNames = Array.new(@vFpgaNbIoPins)
vFpgaCoreOutputNames = Array.new(@vFpgaNbIoPins)
update_IOs_position()
## Input pads ##
@ios.select{|io| io.vFpgaInputName != nil and io.outputPin.used and io.vFpgaInputName != '_clk_'}.each do |io|
vFpgaCoreInputNames[io.vFpgaIoPinNumber] = io.vFpgaInputName
end
## Output pads ##
@ios.select{|io| io.vFpgaOutputName != nil and io.inputPin.used}.each do |io|
vFpgaCoreOutputNames[io.vFpgaIoPinNumber] = io.vFpgaOutputName
end
output_state_arr = Array.new(@vFpgaNbIoPins){'0'}
iolocationreportOutFile = File.open(ioLocationReportFile, 'w') if ioLocationReportFile
maxSigNameLen = ([vFpgaCoreInputNames, vFpgaCoreOutputNames].flatten.reject{|e| e.nil?}.collect{|s| s.length} + [11]).max if ioLocationReportFile
iolocationreportOutFile.puts "┏━━━━━━━━━━━┳━━━━━┳#{'━'*(2+maxSigNameLen)}┓" if ioLocationReportFile
iolocationreportOutFile.puts "┃ Direction ┃ PIN ┃#{'Signal Name'.center(2+maxSigNameLen)}┃" if ioLocationReportFile
iolocationreportOutFile.puts "┡━━━━━━━━━━━╇━━━━━╇#{'━'*(2+maxSigNameLen)}┩" if ioLocationReportFile
pins = []
@vFpgaNbIoPins.times do |i|
next unless vFpgaCoreInputNames[i] or vFpgaCoreOutputNames[i]
el = {input: vFpgaCoreInputNames[i], output: vFpgaCoreOutputNames[i], dir: (vFpgaCoreOutputNames[i] ? 'Output ' : 'Input Z'), number: i}
pins << el
end
pins.each_with_index do |el, i|
output_state_arr[el[:number]] = '1' if el[:output]
if ioLocationReportFile then
......@@ -51,37 +51,37 @@ module ArGen
end
STDERR.puts "WARNING: Physical pin #{el[:number]} is used at the same time as an input and an output.\n Note that the pin state is NOT high impedence." if el[:input] and el[:output]
end
iolocationreportOutFile.puts "└───────────┴─────┴#{'─'*(2+[11, maxSigNameLen].max)}┘" if ioLocationReportFile
iolocationreportOutFile.close if ioLocationReportFile
puts "IO location report written to file \"#{ioLocationReportFile}\"" if ioLocationReportFile
outmap = output_state_arr.reverse.join
nVTPR = (get_longest_path_length_fast(quiet: true) - 1).to_s(2).rjust(8, '0')
return nVTPR + outmap + extract_bitstream()
end # Architecture::Arch#extract_bitstream_wrapped_for_SPI_wrapper
def extract_bitstream_wrapped (constraintFile: nil, ioLocationReportFile: nil, withReorderingConfig: true)
vFpgaCoreInputNames = Array.new(@vFpgaNbIoPins){nil}
vFpgaCoreOutputNames = Array.new(@vFpgaNbIoPins){nil}
update_IOs_position()
## Input pads ##
@ios.select{|io| io.vFpgaInputName != nil and io.outputPin.used}.each do |io|
vFpgaCoreInputNames[io.vFpgaIoPinNumber] = io.vFpgaInputName
end
## Output pads ##
@ios.select{|io| io.vFpgaOutputName != nil and io.inputPin.used}.each do |io|
vFpgaCoreOutputNames[io.vFpgaIoPinNumber] = io.vFpgaOutputName
end
vFpgaWrappedInputNames = Array.new(@vFpgaNbIoPins)
vFpgaWrappedOutputNames = Array.new(@vFpgaNbIoPins)
if constraintFile then
begin
content = File.read(constraintFile).gsub(/#.*?$/,'').gsub(/^\s+/,'').gsub(/\n+/, "\n").gsub(/\A\n+/,'')
......@@ -93,13 +93,13 @@ module ArGen
else
puts "No IO location constraint file was provided"
end
if source == :fromFile then
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 \"#{constraintFile}\":\n \"#{line.chomp}\"" if match.nil?
sigName = match[1]
if match[2] then
fromMsbBit = match[3].to_i
if match[4] then
......@@ -111,14 +111,14 @@ module ArGen
fromMsbBit = nil
fromLsbBit = nil
end
toMsbBit = match[6].to_i
if match[7] then
toLsbBit = match[8].to_i
else
toLsbBit = toMsbBit
end
if toLsbBit > toMsbBit then
tmp = toLsbBit
toLsbBit = toMsbBit
......@@ -126,7 +126,7 @@ module ArGen
end
abort "ERROR: In file \"#{constraintFile}\": index cannot be negative\n \"#{line.chomp}\"" if toLsbBit < 0
abort "ERROR: In file \"#{constraintFile}\": IO constraint location not in the architecture (#{@vFpgaNbIoPins} inputs and #{@vFpgaNbIoPins} outputs)\n \"#{line.chomp}\"" if toLsbBit >= @vFpgaNbIoPins
if fromMsbBit.nil? then
fromMsbBit = toMsbBit - toLsbBit
end
......@@ -139,11 +139,11 @@ module ArGen
fromMsbBit = tmp
end
abort "ERROR: In file \"#{constraintFile}\": index cannot be negative\n \"#{line.chomp}\"" if fromLsbBit < 0
if (fromMsbBit - fromLsbBit) != (toMsbBit - toLsbBit) then
abort "ERROR: In file \"#{constraintFile}\": bitwidths mismatch (#{fromMsbBit + 1 - fromLsbBit} and #{toMsbBit + 1 - toLsbBit})\n \"#{line.chomp}\""
end
if vFpgaCoreInputNames.reject{|el| el.nil?}.collect{|name| name.gsub(/\[\s*\d+\s*\]/,'')}.include?(sigName) then
arr = vFpgaWrappedInputNames
coreArr = vFpgaCoreInputNames
......@@ -155,7 +155,7 @@ module ArGen
else
abort "ERROR: In file \"#{constraintFile}\": cannot find signal \"#{sigName}\" in the placed and routed netlist.\n \"#{line.chomp}\""
end
toBit = toLsbBit
fromLsbBit.upto(fromMsbBit) do |fromBit|
putSig = sigName + '[' + fromBit.to_s + ']'
......@@ -183,8 +183,8 @@ module ArGen
vFpgaWrappedOutputNames[pos] = name
end
end
if ioLocationReportFile then
iolocationreportOutFile = File.open(ioLocationReportFile, 'w')
iolocationreportOutFile.puts "IO mapping:"
......@@ -199,10 +199,10 @@ module ArGen
iolocationreportOutFile.close
puts "IO location report written to file \"#{ioLocationReportFile}\""
end
wrappedIoReorderingConfigForOneIoSize = Math.log2(@vFpgaNbIoPins + 1).ceil
wrappedIoReorderingConfigSize = wrappedIoReorderingConfigForOneIoSize * @vFpgaNbIoPins * 2
wrappedIoReorderingConfig = '1' * wrappedIoReorderingConfigSize
posStart = -wrappedIoReorderingConfigForOneIoSize
vFpgaWrappedOutputNames.reverse.each_with_index do |name, i|
......@@ -219,78 +219,78 @@ module ArGen
posEnd = posStart + wrappedIoReorderingConfigForOneIoSize
wrappedIoReorderingConfig[posStart...posEnd] = corePos.to_s(2).rjust(wrappedIoReorderingConfigForOneIoSize, '0')
end
if withReorderingConfig then
return extract_bitstream() + wrappedIoReorderingConfig
else
return extract_bitstream()
end
end # extract_bitstream_wrapped
end # Arch
def self.format_bit_string_to_binary_bitstream (bitString, outputFileName)
bitString = bitString.gsub(/#.*?$/,'').gsub(/[^(0|1)]/,'')
blength = bitString.length
totalLength = 32*((blength/32.0).ceil)
padding = totalLength - blength
paddedBitString = bitString + '0'*padding
array = paddedBitString.scan(/.{1,32}/).reverse
#binString = array.collect{|word| word.to_i(2)}.pack('L>*')
binString = array.collect{|word| word.to_i(2)}.pack('L<*')
File.binwrite(outputFileName, binString)
end
def self.format_bit_string_to_hexa_bit_string (bitString, outputFileName)
bitString = bitString.gsub(/#.*?$/,'').gsub(/[^(0|1)]/,'')
blength = bitString.length
totalLength = 32*((blength/32.0).ceil)
padding = totalLength - blength
paddedBitString = bitString + '0'*padding
array = paddedBitString.scan(/.{1,32}/).reverse
hexString = array.collect{|word| word.to_i(2).to_s(16).rjust(8,'0')}.join("\n") + "\n"
File.write(outputFileName, hexString)
end
def self.format_bit_string_to_binary_bitstream_for_SPI_wrapper (bitString, outputFileName)
bitString = bitString.gsub(/#.*?$/,'').gsub(/[^(0|1)]/,'')
blength = bitString.length
totalLength = 32*((blength/32.0).ceil)
padding = totalLength - blength
paddedBitString = bitString + '0'*padding
array = paddedBitString.scan(/.{1,8}/).reverse
binString = array.collect{|byte| byte.reverse.to_i(2)}.pack('C*')
File.binwrite(outputFileName, binString)
end
def self.format_bit_string_to_hexa_bit_string_for_SPI_wrapper (bitString, outputFileName)
bitString = bitString.gsub(/#.*?$/,'').gsub(/[^(0|1)]/,'')
blength = bitString.length
totalLength = 32*((blength/32.0).ceil)
padding = totalLength - blength
paddedBitString = bitString + '0'*padding
array = paddedBitString.scan(/.{1,8}/).collect{|s| s.reverse}.reverse.join.scan(/.{1,32}/).collect{|ws| [ws[24..31], ws[16..23], ws[8..15], ws[0..7]].join}
hexString = array.collect{|word| word.to_i(2).to_s(16).rjust(8,'0')}.join("\n") + "\n"
File.write(outputFileName, hexString)
end
# def self.read_bit_string_as_binary_bitstream_for_SPI_wrapper (inputFileName)
# File.binread(inputFileName).unpack('C*').collect{|byte| byte.to_s(2).rjust(8, '0').reverse}.reverse.join
# end
......
......@@ -5,18 +5,18 @@ require_relative 'architecture.rb'
module ArGen
class ArchReader
def self.parse (fileName)
begin
sexpArray = Sexpistol.new.parse_string(File.read(fileName).gsub(/#.*$/,''))
rescue Exception => e
abort "ERROR: Cannot read file \"#{fileName}\":\n #{e.message}"
end
return parse_sexp_arch(sexpArray[0])
end
def self.inspect_res (element, tab = 1)
space = " "*tab
if element.class == Hash then
......@@ -42,30 +42,30 @@ module ArGen
return element.to_s
end
end
private
def self.parse_sexp_arch (array)
abort "ERROR: Expecting \"architecture\"" unless array[0] == :architecture
abort "ERROR: Expecting an architecture name" unless array[1].class == String
name = array[1]
abort "ERROR: Expecting \"withConfigPreloading\"" unless array[2][0] == :withConfigPreloading
abort "ERROR: Expecting \"true\" or \"false\" in \"withConfigPreloading\"" unless array[2][1] == :true or array[2][1] == :false
withConfigPreloading = ((array[2][1] == :true) ? true : false)
abort "ERROR: Expecting \"withSnapshotRegister\"" unless array[3][0] == :withSnapshotRegister
abort "ERROR: Expecting \"true\" or \"false\" in \"withSnapshotRegister\"" unless array[3][1] == :true or array[3][1] == :false
withSnapshotRegister = ((array[3][1] == :true) ? true : false)
abort "ERROR: Expecting \"withVTPR\"" unless array[4][0] == :withVTPR
abort "ERROR: Expecting \"true\" or \"false\" in \"withVTPR\"" unless array[4][1] == :true or array[4][1] == :false
withVTPR = ((array[4][1] == :true) ? true : false)
abort "ERROR: Expecting \"routeChannelWidth\"" unless array[5][0] == :routeChannelWidth
abort "ERROR: Expecting channel width in \"routeChannelWidth\"" unless array[5][1].kind_of?(Integer)
w = array[5][1]
......@@ -79,8 +79,8 @@ module ArGen
sbxesArray = array[9][1..-1]
abort "ERROR: Expecting \"switchBoxLayout\"" unless array[10][0] == :switchBoxLayout
sbxLayoutArray = array[10][1..-1]
## CLBs ##
clbs = {}
clbsArray.each do |clbArr|
......@@ -97,11 +97,11 @@ module ArGen
abort "ERROR: Expecting \"K\" in \"clb\" in \"clbs\"" unless clbArr[4][0] == :K
abort "ERROR: Expecting the CLB's BLE's LUT's number of input in \"K\" in \"clb\" in \"clbs\"" unless clbArr[4][1].kind_of?(Integer)
clbK = clbArr[4][1]
## CLB Routing ##
abort "ERROR: Expecting \"clbRouting\" in \"clb\" in \"clbs\"" unless clbArr[5][0] == :clbRouting
interfacesArray = clbArr[5][1..-1]
clbRoutings = {}
interfacesArray.each do |interfaceArray|
## Interface name ##
......@@ -109,7 +109,7 @@ module ArGen
abort "ERROR: Expecting the interface name in \"interface\" in \"clbRouting\" in CLB \"#{clbName}\" in \"clbs\"" unless interfaceArray[1].class == String
interfaceName = interfaceArray[1]
abort "ERROR: In \"interface\" in \"clbRouting\" in CLB \"#{clbName}\" in \"clbs\": an interface name shall not have a space or a dot (#{interfaceName})" unless (/\s|\./ =~ interfaceName).nil?
## Inputs ##
clbInputs = Array.new(clbI)
abort "ERROR: Expecting \"inputs\" in interface \"#{interfaceName}\" in \"clbRouting\" in CLB \"#{clbName}\" in \"clbs\"" unless interfaceArray[2][0] == :inputs
......@@ -131,7 +131,7 @@ module ArGen
end
clbInputs[clbInputIndex] = [clbInputOrientation] + connectToArr
end
## Outputs ##
clbOutputs = Array.new(clbN)
abort "ERROR: Expecting \"outputs\" in interface \"#{interfaceName}\" in \"clbRouting\" in \"clb\" in \"clbs\"" unless interfaceArray[3][0] == :outputs
......@@ -153,19 +153,19 @@ module ArGen
end
clbOutputs[clbOutputOutdex] = [clbOutputOrientation] + connectToArr
end
abort "ERROR: Interface \"#{interfaceName}\" is defined twice in CLB \"#{clbName}\"" unless clbRoutings[interfaceName].nil?
clbRoutings[interfaceName] = {name: interfaceName, inputs: clbInputs, outputs: clbOutputs}
end
abort "ERROR: \"clb\" must have at most 6 elements: a name, \"N\", \"I\", \"K\" and \"clbRouting\"" if clbArr.length > 6
abort "ERROR: CLB \"#{clbName}\" is defined twice" unless clbs[clbName].nil?
clbs[clbName] = {name: clbName, N: clbN, I: clbI, K: clbK, interfaces: clbRoutings}
end
#clbs.each{|name, clbdef| puts clbdef.keys.collect{|param| "#{param}: \%{#{param}}"}.join("\n") % clbdef}
## IO blocks ##
ioBlocks = {}
ioBlocksArray.each do |ioArr|
......@@ -174,7 +174,7 @@ module ArGen
ioBlockName = ioArr[1]
abort "ERROR: Expecting the number of IO in IO block \"#{ioBlockName}\"" unless ioArr[2].kind_of?(Integer)
numIos = ioArr[2]
## IOs ##
abort "ERROR: Expecting \"ios\" in IO block \"#{ioBlockName}\"" unless ioArr[3][0] == :ios
iosArr = ioArr[3][1..-1]
......@@ -186,39 +186,39 @@ module ArGen
ioIndex = ioDefArr[1]
abort "ERROR: IO index (#{ioIndex}) is superior to the number of IOs (#{numIos}) in IO block \"#{ioBlockName}\"" unless ioIndex < numIos
abort "ERROR: IO index (#{ioIndex}) is defined twice in IO block \"#{ioBlockName}\"" unless ios[ioIndex].nil?
abort "ERROR: Expecting \"inputConnectTo\" in IO #{ioIndex} of IO block \"#{ioBlockName}\"" unless ioDefArr[2][0] == :inputConnectTo
ioInputs = ioDefArr[2][1..-1]
ioInputs.each do |ictnum|
abort "ERROR: Expecting an input wire index in \"inputConnectTo\" in IO #{ioIndex} of IO block \"#{ioBlockName}\"" unless ictnum.kind_of?(Integer) and ictnum >= 0
abort "ERROR: Input wire index (#{ictnum}) is greater than the routing channel width (#{w}) in IO #{ioIndex} of IO block \"#{ioBlockName}\"" unless ictnum < w
end
abort "ERROR: Expecting \"outputConnectTo\" in IO #{ioIndex} of IO block \"#{ioBlockName}\"" unless ioDefArr[3][0] == :outputConnectTo
ioOutputs = ioDefArr[3][1..-1]
ioOutputs.each do |octnum|
abort "ERROR: Expecting an output wire index in \"outputConnectTo\" in IO #{ioIndex} of IO block \"#{ioBlockName}\"" unless octnum.kind_of?(Integer) and octnum >= 0
abort "ERROR: Output wire index (#{octnum}) is greater than the routing channel width (#{w}) in IO #{ioIndex} of IO block \"#{ioBlockName}\"" unless octnum < w
end
ios[ioIndex] = {inputConnections: ioInputs, outputConnections: ioOutputs}
end
abort "ERROR: IO block \"#{ioBlockName}\" is defined twice" unless ioBlocks[ioBlockName].nil?
ioBlocks[ioBlockName] = {name: ioBlockName, nbIOs: numIos, ios: ios}
end
#ioBlocks.each{|name, ioBlock| puts ioBlock.keys.collect{|param| "#{param}: \%{#{param}}"}.join("\n") % ioBlock}
## Layout ##
abort "ERROR: Expecting \"size\" in \"layout\"" unless layoutArray[1][0] == :size
abort "ERROR: Expecting width and height in \"size\" in \"layout\"" unless layoutArray[1][1].kind_of?(Integer) and layoutArray[1][2].kind_of?(Integer)
layoutWidth = layoutArray[1][1]
layoutHeight = layoutArray[1][2]
layout = Array.new(layoutWidth+2){Array.new(layoutHeight+2)}
if layoutArray.length == 3 and layoutArray[2] == :auto then
### IOs, expect 'top', 'right', 'left', 'bottom' and 'default' if one of those is missing ###
topIo = ioBlocks['top']
bottomIo = ioBlocks['bottom']
......@@ -228,19 +228,19 @@ module ArGen
if (topIo.nil? or bottomIo.nil? or leftIo.nil? or rightIo.nil?) and defaultIo.nil? then
abort "ERROR: In automatic layout: If any of the IO blocks \"top\", \"bottom\", \"left\" and \"right\" is not defined then you must define a \"default\" IO block"
end
if defaultIo then
(0...layoutWidth).each{|x| layout[1 + x][layoutHeight + 1] = 'default'}
(0...layoutWidth).each{|x| layout[1 + x][0] = 'default'}
(0...layoutHeight).each{|y| layout[layoutWidth + 1][1 + y] = 'default'}
(0...layoutHeight).each{|y| layout[0][1 + y] = 'default'}
end
(0...layoutWidth).each{|x| layout[1 + x][layoutHeight + 1] = 'top'} if topIo
(0...layoutWidth).each{|x| layout[1 + x][0] = 'bottom'} if bottomIo
(0...layoutHeight).each{|y| layout[layoutWidth + 1][1 + y] = 'right'} if rightIo
(0...layoutHeight).each{|y| layout[0][1 + y] = 'left'} if leftIo
dfltClb = clbs['default']
abort "ERROR: In automatic layout: No CLB named \"default\" was previously defined" if dfltClb.nil?
dfltClbInt = dfltClb[:interfaces]['default']
......@@ -253,41 +253,41 @@ module ArGen
bottomClbInt = dfltClb[:interfaces]['bottom']
rightClbInt = dfltClb[:interfaces]['right']
topClbInt = dfltClb[:interfaces]['top']
(1..layoutWidth).each do |x|
(1..layoutHeight).each do |y|
layout[x][y] = 'default.default'
end
end
if leftClbInt then
(1..layoutHeight).each{|y| layout[1][y] = 'default.left'}
end
if rightClbInt then
(1..layoutHeight).each{|y| layout[layoutWidth][y] = 'default.right'}
end
if bottomClbInt then
(1..layoutWidth).each{|x| layout[x][1] = 'default.bottom'}
end
if topClbInt then
(1..layoutWidth).each{|x| layout[x][layoutHeight] = 'default.top'}
end
if bottomLeftClbInt then
layout[1][1] = 'default.bottom-left'
end
if topLeftClbInt then
layout[1][layoutHeight] = 'default.top-left'
end
if bottomRightClbInt then
layout[layoutWidth][1] = 'default.bottom-right'
end
if topRightClbInt then
layout[layoutWidth][layoutHeight] = 'default.top-right'
end
......@@ -300,7 +300,7 @@ module ArGen
#(0...(layoutWidth+2)).each do |i|
# layout[i] = Array.new(layoutHeight + 2)
#end
## Bottom IOs ##
x = 1
(((layoutWidth + 2)*layoutHeight + layoutWidth) ... expectedArrayLength).each do |i|
......@@ -359,7 +359,7 @@ module ArGen
end
end
end
#puts '-'*80
#(0...(layoutHeight+2)).reverse_each do |y|
# (0...(layoutWidth+2)).each do |x|
......@@ -367,7 +367,7 @@ module ArGen
# end
# puts
#end
## Switch box connection definition ##
sbxes = {}
sbxesArray.each do |sbxArr|
......@@ -375,13 +375,13 @@ module ArGen
abort "ERROR: Expecting a switch box name in \"switchBox\" in \"switchBoxes\"" unless sbxArr[1].class == String
sbxName = sbxArr[1]
connArr = sbxArr[2..-1]
muxes = []
if connArr.length == 1 and connArr[0] == :auto then
abort "ERROR: In \"switchBoxes\": switch box \"#{sbxName}\" is used as 'auto', but automatic connexion works only with \"left\", \"right\" and \"bottom\"" if sbxName != 'left' and sbxName != 'right' and sbxName != 'bottom'
topSwBx = sbxes['top']
abort "ERROR: In \"switchBoxes\": switch box \"#{sbxName}\" is used as 'auto', but to use automatic connexion you must have previously defined a \"top\" switch box" if topSwBx.nil?