mirror of https://github.com/efabless/caravel.git
Added ckeck-gpio-id target to check oas file gpio defaults and id.
Development does not create oas files, so not tested on development data.
This commit is contained in:
parent
357352e145
commit
54eba30e01
4
Makefile
4
Makefile
|
@ -903,6 +903,10 @@ check-python:
|
|||
@$(PYTHON_BIN) -c "import sys; assert sys.version_info >= (3, 6), 'Python version less than 3.6'"
|
||||
@echo "Python >=3.6 found."
|
||||
|
||||
.PHONY: check-gpio-id
|
||||
check-gpio-id: check-uid
|
||||
$(CARAVEL_ROOT)/scripts/run_gpio-id_check caravel_$$USER_ID tapeout/outputs/oas/caravel_$$USER_ID.oas*
|
||||
|
||||
.PHONY: clean-openlane
|
||||
clean-openlane:
|
||||
rm -rf $(OPENLANE_ROOT)
|
||||
|
|
|
@ -0,0 +1,322 @@
|
|||
#! /bin/bash
|
||||
# run_hier_check: Checks layout hierarchy against verilog
|
||||
|
||||
# Copyright 2024 D. Mitch Bailey cvc at shuharisystem dot com
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Overview:
|
||||
# 1. Extract gds/oas hierachy.
|
||||
# 2. Compare expected values to layout values
|
||||
#
|
||||
# Uses WORK_ROOT and LOG_ROOT if set.
|
||||
|
||||
# Use case
|
||||
# run_gpio-id_check top_layout layout_file
|
||||
if [[ $# -ne 2 ]]; then
|
||||
echo "usage: run_gpio-id_check top_layout oas_file|gds_file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# define location of expected values. Missing files do not generate fatal errors but do cause mismatches.
|
||||
CHIP_ID_SOURCE=tapeout/logs/set_user_id.log
|
||||
GPIO_DEFAULT_SOURCE=mpw_precheck/outputs/reports/gpio_defines.report
|
||||
|
||||
export TOP_LAYOUT=$1
|
||||
export LAYOUT_FILE=$2
|
||||
|
||||
if [[ ! -f $LAYOUT_FILE ]]; then
|
||||
echo "Error: missing file $LAYOUT_FILE"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "WORK_ROOT : ${WORK_ROOT:=$(pwd)/$TOP_LAYOUT}"
|
||||
echo "LOG_ROOT : ${LOG_ROOT:=$WORK_ROOT}"
|
||||
export LOG_ROOT WORK_ROOT
|
||||
|
||||
mkdir -p $LOG_ROOT
|
||||
mkdir -p $WORK_ROOT
|
||||
rm -f $LOG_ROOT/gpio-id.log
|
||||
|
||||
log_file=$WORK_ROOT/gpio-id.log
|
||||
layout_cell_file=$WORK_ROOT/layout.cells
|
||||
rm -f $log_file $layout_cell_file
|
||||
|
||||
date "+BEGIN: %c" >$log_file
|
||||
start_time=$SECONDS
|
||||
|
||||
echo "Step 1. extracting $TOP_LAYOUT layout hierarchy from $LAYOUT_FILE..."
|
||||
if [[ $LAYOUT_FILE == *.gz ]]; then
|
||||
CAT=zcat
|
||||
BASE_LAYOUT=${LAYOUT_FILE%.gz}
|
||||
else
|
||||
CAT=cat
|
||||
BASE_LAYOUT=$LAYOUT_FILE
|
||||
fi
|
||||
EXT=${BASE_LAYOUT##*.}
|
||||
if [[ "$EXT" == "txt" ]]; then
|
||||
TEXT_FILE=$BASE_LAYOUT
|
||||
elif [[ "$EXT" == "gds" || "$EXT" == "oas" ]]; then
|
||||
TEXT_FILE=$WORK_ROOT/layout.txt
|
||||
ID_TEXT_FILE=$WORK_ROOT/user_id_programming.txt
|
||||
rm -f $TEXT_FILE $TEXT_FILE.gz $ID_TEXT_FILE
|
||||
cat >$WORK_ROOT/gds2txt.py <<-EOF
|
||||
import pya
|
||||
|
||||
app = pya.Application.instance()
|
||||
opt = pya.SaveLayoutOptions()
|
||||
layout_view = pya.Layout()
|
||||
|
||||
input_layout = "$LAYOUT_FILE"
|
||||
output = "$TEXT_FILE"
|
||||
id_output = "$ID_TEXT_FILE"
|
||||
# Setting the name of the output file and setting the substitution character
|
||||
print("[INFO] Changing from " + input_layout + "\n to " + output)
|
||||
opt.set_format_from_filename(output)
|
||||
opt.oasis_substitution_char=''
|
||||
|
||||
# Reading the input file and writing it to the output file name
|
||||
layout_view.read(input_layout)
|
||||
for cell_it in layout_view.each_cell():
|
||||
if cell_it.name.endswith("$TOP_LAYOUT"):
|
||||
myTopIndex = layout_view.cell(cell_it.name).cell_index()
|
||||
if cell_it.name.endswith("user_id_programming"):
|
||||
myUserIdIndex = layout_view.cell(cell_it.name).cell_index()
|
||||
try:
|
||||
if myTopIndex and myUserIdIndex: # stop searching once both have been found
|
||||
break
|
||||
except NameError: # continue if either is not defined
|
||||
continue
|
||||
|
||||
try:
|
||||
opt.select_cell(myTopIndex)
|
||||
opt.add_layer(0, pya.LayerInfo())
|
||||
layout_view.write(output, opt)
|
||||
except NameError: # $TOP_LAYOUT not found
|
||||
print("[ERROR] Could not find $TOP_LAYOUT in $LAYOUT_FILE")
|
||||
|
||||
try:
|
||||
opt.select_cell(myUserIdIndex)
|
||||
mcon_layer = layout_view.find_layer(pya.LayerInfo(67, 44)) # logical mcon layer
|
||||
opt.add_layer(mcon_layer, pya.LayerInfo())
|
||||
layout_view.write(id_output, opt)
|
||||
except NameError as myError: # user_id_programming not found
|
||||
print("[ERROR] Could not find user_id_programming in $LAYOUT_FILE")
|
||||
|
||||
app.exit(0)
|
||||
EOF
|
||||
klayout -b -rm $WORK_ROOT/gds2txt.py |
|
||||
tee -a $log_file
|
||||
gzip -f $TEXT_FILE
|
||||
CAT=zcat
|
||||
fi
|
||||
|
||||
echo "Step 2. comparing expected values to layout values..."
|
||||
if [[ -f $CHIP_ID_SOURCE ]]; then
|
||||
echo "Reading chip id from $CHIP_ID_SOURCE" |
|
||||
tee -a $log_file
|
||||
else
|
||||
printf "\n** Missing $CHIP_ID_SOURCE **\n" |
|
||||
tee -a $log_file
|
||||
CHIP_ID_SOURCE=
|
||||
fi
|
||||
if [[ -f $GPIO_DEFAULT_SOURCE ]]; then
|
||||
echo "Reading gpio defaults from $GPIO_DEFAULT_SOURCE" |
|
||||
tee -a $log_file
|
||||
else
|
||||
printf "\n** Missing $GPIO_DEFAULT_SOURCE **\n" |
|
||||
tee -a $log_file
|
||||
GPIO_DEFAULT_SOURCE=
|
||||
fi
|
||||
cat $CHIP_ID_SOURCE $GPIO_DEFAULT_SOURCE <($CAT $TEXT_FILE) <(cat $ID_TEXT_FILE) |
|
||||
awk '
|
||||
BEGIN {
|
||||
# Set constant defaults that are not in the gpio_defines.report
|
||||
default_gpio[0] = "1803";
|
||||
default_gpio[1] = "1803";
|
||||
default_gpio[2] = "0403";
|
||||
default_gpio[3] = "0801";
|
||||
default_gpio[4] = "0403";
|
||||
# Set user id programming bit constants
|
||||
bitXY["14405:9265"] = "0@0"; bitXY["13485:9265"] = "1@0";
|
||||
bitXY["16245:9265"] = "0@1"; bitXY["15325:9265"] = "1@1";
|
||||
bitXY["10265:20145"] = "0@2"; bitXY["9345:20145"] = "1@2";
|
||||
bitXY["7965:9265"] = "0@3"; bitXY["7045:9265"] = "1@3";
|
||||
bitXY["28205:9265"] = "0@4"; bitXY["27285:9265"] = "1@4";
|
||||
bitXY["21765:25585"] = "0@5"; bitXY["20845:25585"] = "1@5";
|
||||
bitXY["7965:20145"] = "0@6"; bitXY["7045:20145"] = "1@6";
|
||||
bitXY["20385:9265"] = "0@7"; bitXY["19465:9265"] = "1@7";
|
||||
bitXY["17165:17765"] = "0@8"; bitXY["16245:17765"] = "1@8";
|
||||
bitXY["25445:11985"] = "0@9"; bitXY["24525:11985"] = "1@9";
|
||||
bitXY["22225:20145"] = "0@10"; bitXY["21305:20145"] = "1@10";
|
||||
bitXY["13025:9265"] = "0@11"; bitXY["12105:9265"] = "1@11";
|
||||
bitXY["23605:23205"] = "0@12"; bitXY["22685:23205"] = "1@12";
|
||||
bitXY["24065:11985"] = "0@13"; bitXY["23145:11985"] = "1@13";
|
||||
bitXY["13485:17765"] = "0@14"; bitXY["12565:17765"] = "1@14";
|
||||
bitXY["23145:6885"] = "0@15"; bitXY["22225:6885"] = "1@15";
|
||||
bitXY["24065:17765"] = "0@16"; bitXY["23145:17765"] = "1@16";
|
||||
bitXY["8425:17765"] = "0@17"; bitXY["7505:17765"] = "1@17";
|
||||
bitXY["23605:20145"] = "0@18"; bitXY["22685:20145"] = "1@18";
|
||||
bitXY["10725:23205"] = "0@19"; bitXY["9805:23205"] = "1@19";
|
||||
bitXY["14865:6885"] = "0@20"; bitXY["13945:6885"] = "1@20";
|
||||
bitXY["18085:23205"] = "0@21"; bitXY["17165:23205"] = "1@21";
|
||||
bitXY["21305:17765"] = "0@22"; bitXY["20385:17765"] = "1@22";
|
||||
bitXY["26365:25585"] = "0@23"; bitXY["25445:25585"] = "1@23";
|
||||
bitXY["9805:17765"] = "0@24"; bitXY["8885:17765"] = "1@24";
|
||||
bitXY["15785:17765"] = "0@25"; bitXY["14865:17765"] = "1@25";
|
||||
bitXY["26365:17765"] = "0@26"; bitXY["25445:17765"] = "1@26";
|
||||
bitXY["8425:6885"] = "0@27"; bitXY["7505:6885"] = "1@27";
|
||||
bitXY["10725:9265"] = "0@28"; bitXY["9805:9265"] = "1@28";
|
||||
bitXY["27745:20145"] = "0@29"; bitXY["26825:20145"] = "1@29";
|
||||
bitXY["16245:23205"] = "0@30"; bitXY["15325:23205"] = "1@30";
|
||||
bitXY["7965:14705"] = "0@31"; bitXY["7045:14705"] = "1@31";
|
||||
# binary to hexadecimal conversion constants
|
||||
HEX["0000"] = "0"; HEX["0001"] = "1"; HEX["0010"] = "2"; HEX["0011"] = "3";
|
||||
HEX["0100"] = "4"; HEX["0101"] = "5"; HEX["0110"] = "6"; HEX["0111"] = "7";
|
||||
HEX["1000"] = "8"; HEX["1001"] = "9"; HEX["1010"] = "A"; HEX["1011"] = "B";
|
||||
HEX["1100"] = "C"; HEX["1101"] = "D"; HEX["1110"] = "E"; HEX["1111"] = "F";
|
||||
}
|
||||
/Setting Project Chip ID to:/ {
|
||||
expected_user_id = toupper($NF);
|
||||
}
|
||||
/^USER_CONFIG_GPIO_.*_INIT/ {
|
||||
# Remember expected default_gpio
|
||||
gpio = gensub(/USER_CONFIG_GPIO_(.*)_INIT.*/, "\\1", "g") + 0; # extract gpio number
|
||||
default_gpio[gpio] = gensub(/13.h/, "", "g", $2); # remove width and base prefix
|
||||
}
|
||||
/^STRNAMR/ {
|
||||
get_user_id_text = 0; # reset user id search flag
|
||||
get_user_id_bits = 0; # reset user id bits search flag
|
||||
}
|
||||
/^STRNAME/ && $2 ~ /user_id_textblock/ {
|
||||
get_user_id_text = 1; # in user_id_textblock, so set user id search flag
|
||||
}
|
||||
/^SNAME/ && get_user_id_text == 1 && $2 ~ /alpha_/ {
|
||||
# the user_id_textblock contains alpha_? cells where ? is the display character
|
||||
# these cells have an instance name property alphaX_x where x is a position 7-0 from left to right
|
||||
cell = $2;
|
||||
# including ENDEL in condition prevents infinite loop with unexpected formats
|
||||
while ( ! ( /PROPATTR 61/ || /ENDEL/ ) ) {
|
||||
getline;
|
||||
}
|
||||
getline;
|
||||
if ( ! /PROPVALUE/ ) next; # skip if unexpected format
|
||||
pos = gensub(/.*alphaX_([0-9]).*/, "\\1", "g") + 0; # extract the position from the instance name property
|
||||
text[pos] = gensub(/.*alpha_(.).*/, "\\1", "g", cell); # extract the text character from the cell name
|
||||
}
|
||||
/^STRNAME/ && $2 ~ /user_id_programming/ {
|
||||
get_user_id_bits = 1; # in user_id_programming, so set user id bits search flag
|
||||
}
|
||||
/BOUNDARY/ && get_user_id_bits == 1 {
|
||||
# BOUNDARY elements instantiate the id programming bits
|
||||
# including ENDEL in condition prevents infinite loop with unexpected formats
|
||||
while ( ! ( /^XY/ || /ENDEL/ ) ) {
|
||||
getline;
|
||||
}
|
||||
xy = $2 $3;
|
||||
items = split(bitXY[xy], bit_data, /@/);
|
||||
if ( items == 2 ) {
|
||||
bit[bit_data[2]+0] = bit_data[1];
|
||||
}
|
||||
}
|
||||
/^SNAME/ && $2 ~ /gpio_defaults_block/ {
|
||||
# from the cell name gpio_defaults_block_xxxx, xxxx is the 4 byte hex code for the gpio defaults.
|
||||
# from the instance name property (61) gpio_defaults_block_x, x is the gpio number
|
||||
cell = $2;
|
||||
# including ENDEL in condition prevents infinite loop with unexpected formats
|
||||
while ( ! ( /PROPATTR 61/ || /ENDEL/ ) ) {
|
||||
getline;
|
||||
}
|
||||
getline;
|
||||
if ( ! /PROPVALUE/ ) next; # skip if unexpected format
|
||||
gpio = gensub(/.*gpio_defaults_block_([0-9]+).*/, "\\1", "g") + 0; # extracct the gpio number from the instance name property
|
||||
layout[gpio] = gensub(/.*gpio_defaults_block_(....).*/, "\\1", "g", cell); # extract the layout gpio default from the cell name
|
||||
}
|
||||
function BinaryToHex(binary_number) {
|
||||
# converts arbitrary length binary string to hexadecimal number string
|
||||
binary_digits = length(binary_number);
|
||||
while ( binary_digits % 4 != 0 ) { # pad binary number with leading zeros until length is multiple of 4
|
||||
binary_number = "0" binary_number;
|
||||
binary_digits = length(binary_number);
|
||||
}
|
||||
hex_number = "";
|
||||
for ( bit_it = binary_digits - 3; bit_it > 0; bit_it -= 4 ) { # awk strings start from index 1
|
||||
nibble = substr(binary_number, bit_it, 4);
|
||||
if ( nibble in HEX ) {
|
||||
hex_number = HEX[nibble] hex_number;
|
||||
} else {
|
||||
hex_number = "X" hex_number;
|
||||
}
|
||||
}
|
||||
return hex_number;
|
||||
}
|
||||
END {
|
||||
layout_text = "";
|
||||
# concatenate the id characters in order
|
||||
for ( position_it = 7; position_it >= 0; position_it--) {
|
||||
layout_text = layout_text text[position_it];
|
||||
}
|
||||
mismatch = 0;
|
||||
# for unexpected values, print "*" at the end of the line. Also set mismatch flag.
|
||||
check = ( expected_user_id == layout_text ) ? "" : "*";
|
||||
mismatch = mismatch || ( check == "*" );
|
||||
printf "\nChip ID text: expected %s found %s %s\n\n", expected_user_id, layout_text, check;
|
||||
|
||||
id_bits = "";
|
||||
reversed_bits = "";
|
||||
for ( position_it = 31; position_it >= 0; position_it--) {
|
||||
if ( position_it in bit ) { # use actual value if found, otherwise x.
|
||||
myBit = bit[position_it];
|
||||
} else {
|
||||
myBit = "x";
|
||||
}
|
||||
id_bits = id_bits myBit;
|
||||
reversed_bits = myBit reversed_bits;
|
||||
}
|
||||
layout_id = BinaryToHex(id_bits);
|
||||
reversed_id = BinaryToHex(reversed_bits);
|
||||
# Reversed ids are flagged "<" but not reported as an error
|
||||
check = ( expected_user_id == layout_id ) ? "" : ( expected_user_id == reversed_id ) ? "<" : "*";
|
||||
mismatch = mismatch || ( check == "*" );
|
||||
printf "Chip ID bits: expected %s found(reversed) %s(%s) %s\n\n", expected_user_id, layout_id, reversed_id, check;
|
||||
|
||||
print "GPIO default check";
|
||||
print "gpio expected found";
|
||||
for ( gpio_it = 0; gpio_it <= 37; gpio_it++) {
|
||||
if ( gpio_it in layout ) {
|
||||
check = ( default_gpio[gpio_it] == layout[gpio_it] ) ? "" : "*";
|
||||
mismatch = mismatch || ( check == "*" );
|
||||
printf "%4d %8s %5s %s\n", gpio_it, default_gpio[gpio_it], layout[gpio_it], check;
|
||||
}
|
||||
}
|
||||
if ( mismatch ) {
|
||||
print "\n** Unexpected values **";
|
||||
} else {
|
||||
print "\nExpected values match";
|
||||
}
|
||||
}' - |
|
||||
tee -a $log_file
|
||||
|
||||
date "+END: %c" >>$log_file
|
||||
runtime=$((SECONDS - start_time))
|
||||
hours=$((runtime / 3600))
|
||||
minutes=$(((runtime % 3600) / 60))
|
||||
seconds=$(((runtime % 3600) % 60))
|
||||
printf "Runtime: %d:%02d:%02d (hh:mm:ss)\n" $hours $minutes $seconds >>$log_file
|
||||
|
||||
if [[ $WORK_ROOT != $LOG_ROOT ]]; then
|
||||
cp $log_file $LOG_ROOT/.
|
||||
fi
|
||||
|
||||
grep -q "Expected values" $log_file
|
||||
exit $?
|
Loading…
Reference in New Issue