Skip to content

Commit

Permalink
added gpio defaults and user id check
Browse files Browse the repository at this point in the history
  • Loading branch information
d-m-bailey committed Aug 21, 2024
1 parent 8fd8ae8 commit 6346abc
Show file tree
Hide file tree
Showing 2 changed files with 326 additions and 0 deletions.
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_SOURCE}"
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 gpio defaults 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 $?

0 comments on commit 6346abc

Please sign in to comment.