forked from efabless/caravel
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added gpio defaults and user id check
- Loading branch information
1 parent
8fd8ae8
commit 6346abc
Showing
2 changed files
with
326 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 $? |