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

added gpio defaults and user id check #2

Open
wants to merge 3 commits into
base: tapeout-lvs
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
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1459,6 +1459,10 @@ README.rst: README.src.rst docs/source/getting-started.rst docs/source/tool-vers
-e's@.. warning::@**WARNING:**@g' \
> openlane/README.rst

.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)
Expand Down
322 changes: 322 additions & 0 deletions scripts/run_gpio-id_check
Original file line number Diff line number Diff line change
@@ -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