Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Addition of layout port check - checks pin position, shape, and label. #296

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions check_manager/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from checks.gpio_defines_check import gpio_defines_check
from checks.license_check import license_check
from checks.xor_check import xor_check
from checks.port_check import port_check
from checks.lvs_check.lvs import run_lvs
from checks.oeb_check.oeb import run_oeb
from checks.pdn_check.pdn import run_pdn
Expand Down Expand Up @@ -543,6 +544,33 @@ def run(self):
logging.warning("{{XOR CHECK FAILED}} The GDS file has non-conforming geometries.")
return self.result

class Port(CheckManager):
__ref__ = 'port'
__surname__ = 'Port'
__supported_pdks__ = ['gf180mcuC', 'gf180mcuD', 'sky130A', 'sky130B']
__supported_type__ = ['analog', 'digital', 'openframe', 'mini']

def __init__(self, precheck_config, project_config):
super().__init__(precheck_config, project_config)

def run(self):
if 'gf180mcu' in self.precheck_config['pdk_path'].stem:
gds_golden_wrapper_file_path = Path(__file__).parent.parent / "_default_content/gds/user_project_wrapper_empty_gf180mcu.gds"
elif self.project_config['type'] == "mini":
gds_golden_wrapper_file_path = Path(__file__).parent.parent / "_default_content/gds/user_project_wrapper_mini4_empty.gds"
else:
gds_golden_wrapper_file_path = self.precheck_config['caravel_root'] / f"gds/{self.project_config['golden_wrapper']}.gds"

self.result = port_check.layout_port_check(self.precheck_config['input_directory'],
self.precheck_config['output_directory'],
gds_golden_wrapper_file_path,
self.project_config,
self.precheck_config['pdk_path'])
if self.result:
logging.info("{{PORT CHECK PASSED}} The GDS file has no port violations.")
else:
logging.warning("{{PORT CHECK FAILED}} The GDS file has incorrect ports.")
return self.result

class PDNMulti(CheckManager):
__ref__ = 'pdnmulti'
Expand Down Expand Up @@ -595,6 +623,7 @@ def run(self):
(PDNMulti.__ref__, PDNMulti),
(MetalCheck.__ref__, MetalCheck),
(XOR.__ref__, XOR),
(Port.__ref__, Port),
(MagicDRC.__ref__, MagicDRC),
(KlayoutFEOL.__ref__, KlayoutFEOL),
(KlayoutBEOL.__ref__, KlayoutBEOL),
Expand All @@ -616,6 +645,7 @@ def run(self):
(GpioDefines.__ref__, GpioDefines),
(MetalCheck.__ref__, MetalCheck),
(XOR.__ref__, XOR),
(Port.__ref__, Port),
(MagicDRC.__ref__, MagicDRC),
(KlayoutFEOL.__ref__, KlayoutFEOL),
(KlayoutBEOL.__ref__, KlayoutBEOL),
Expand Down
Empty file added checks/port_check/__init__.py
Empty file.
127 changes: 127 additions & 0 deletions checks/port_check/layout_ports.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/usr/bin/ruby
# usage: layout_ports.rb <layoutFile> <cellName>
#
# Runs klayout (in batch) to get the ports of a cell in a layout file.
# Script starts as regular ruby, then exec's via klayout passing self to it.
# (klayout requirement is this script-name *must* end in .rb).
#
in_klayout=$in_klayout
if in_klayout.to_s.empty?
this_script = $0
layoutFile = ARGV[0]
cellName = ARGV[1]
portLayers = ARGV[2]

if layoutFile == "--version" || layoutFile == "-v"
# these options don't prevent klayout from initializing ~/.klayout unfortunately...
exec "klayout -nc -rx -zz -v"
end

if ARGV.length != 3
puts "ERROR, must give two arguments, usage: layout_ports.rb <layoutFile> <cellName> <portLayers>"
puts " It's an error if <cellName> unbound (referenced by others, not defined)."
puts " But that's the only unbound checked, no other cells checked or reported."
puts "Exit-status: 0 on success; 1 on I/O or usage error; 2 unbound. See also gdsAllcells.rb"
exit 1
end


# construct command from our script arguments, replace self with klayout...
exec "klayout -nc -zz -rx \
-rd in_klayout=1 \
-rd file=#{layoutFile} \
-rd topcell=#{cellName} \
-rd ports=\"#{portLayers}\" \
-r #{this_script}"
end

#
# to just read a layout in batch (no useful info printed):
# klayout -d 40 -z xyz.gds >& klayout.read.log
#
# -d : debug level, no details during GDS-reading however, try 20 or 40 or (timing too:) 21 or 41
# -z/-zz : -z pseudo-batch mode, still needs X-DISPLAY connection; -zz true batch
# -nc : don't use/update configuration file
# -rx : disable built-in macros, stuff not needed for batch usually
# -rd : define variables the script can reference
#

layoutFile = $file
cellName = $topcell
portLayers = $ports.split(" ")

if layoutFile == ""
STDERR.puts "ERROR: missing layoutFile argument, usage: layout_ports.rb <layoutFile> <cellName> <portLayers>"
exit 1
elsif cellName == ""
STDERR.puts "ERROR: missing cellName argument, usage: layout_ports.rb <layoutFile> <cellName> <portLayers>"
exit 1
elsif portLayers.empty?
STDERR.puts "ERROR: missing port layer list argument, usage: layout_ports.rb <layoutFile> <cellName> <portLayers>"
exit 1
end

include RBA

begin
puts "Reading file #{layoutFile} for cell #{cellName}."
puts "Looking for ports on #{portLayers}."
layout = Layout.new
layout.read(layoutFile)
dbu = layout.dbu
puts "dbu: #{dbu}"

errs = 0

# does not catch case where cell.bbox -> "()"
if ! layout.has_cell?(cellName)
STDERR.puts "ERROR: layout does not have the cell #{cellName}"
STDOUT.flush
STDERR.flush
Kernel.exit! 1
end

cell = layout.cell(cellName)
if cell.to_s.empty?
STDERR.puts "ERROR: couldn't open the cell #{cellName}"
STDOUT.flush
STDERR.flush
Kernel.exit! 1
end

puts "cell #{cellName}"
portLayers.each do |layer_type_it|
port_layer_info = LayerInfo.from_string(layer_type_it)
my_layer_index = cell.layout.find_layer(port_layer_info)
if my_layer_index
puts "Found #{port_layer_info}"
my_shapes = cell.shapes(my_layer_index)
my_shapes.each do |shape_it|
if shape_it.is_text?
puts "text: #{shape_it.text_pos} #{layer_type_it} #{shape_it.text_string}"
elsif shape_it.is_box?
puts "box: #{shape_it.box_p1} #{shape_it.box_p2} #{layer_type_it}"
else
puts "Unrecognized shape #{shape_it.type}"
end
end
end
end

end

puts "Done."

# reserve status=1 for I/O errors
if errs > 0
errs = errs + 1
end
# don't roll-over exit-status to/past zero
if errs > 255
errs = 255
end

# exit doesn't work to set status; exit! requires explicit buffered-IO flush.
STDOUT.flush
STDERR.flush
Kernel.exit! errs
Loading
Loading