projects/scripts: Add gtwizard scripts

main
LIacob106 2021-11-24 14:18:32 +00:00 committed by LIacob106
parent 62dc310794
commit 86d754ae85
2 changed files with 1466 additions and 0 deletions

935
projects/scripts/gtwiz_parser.pl Executable file
View File

@ -0,0 +1,935 @@
#!/usr/bin/perl -w
################################################################################
#
# This script is meant to be used together with gtwizard_generator.tcl
# It parses and provides as output a list of unique parameters to a provided GT wizard configuration
# Must be called from the project folder, where the IP instance is (*.gen/sources_1/ip)
#
################################################################################
use strict;
use warnings;
# use bigint;
################################################################################
## Check the status of the required modules
my @modules = qw (
Cwd
Data::Dumper
);
for(@modules) {
eval "use $_";
if ($@) {
warn "Module not found : $_ \n Please install it!" if $@;
}
}
sub print_hash {
my ($hash_ref) = @_;
foreach my $key (keys %$hash_ref) {
print " $key : $$hash_ref{$key}\n ";
}
}
################################################################################
## SUBROUTINES
################################################################################
# Search for the COMMON and CHANNEL attributes from the generated files
# Required parameters : $_ - file name
################################################################################
sub get_attribute_name {
my ($pfile) = @_;
my $myname = (caller(0))[3];
# Try to open the file
open(READFILE, "<$pfile") or die print "@[$myname] Can not open file called $pfile for reading.\n";
my $xcvr_params_str = join('', <READFILE>);
my @xcvr_params;
# remove comments //anything
my $comment_regex = qr/^\s*\/{2}.*\n/mp;
my $subst = '';
$xcvr_params_str = $xcvr_params_str=~s/$comment_regex/$subst/rg;
# return the attribute block from the *_[COMMON|CHANNEL] instance
# Ultrascale/Ultrascale+ version
# if ($xcvr_params_str =~ m/#\(((\n(.*\),\n){2,}).*\)\n)/) {
# Version that works for both 7 series and Ultrascale/Ultrascale+
# This regex searches for the IP instance in the verilog code. In 7 series there are spaces at the begining of the line, while for Ultrascale there are not. After that searches for the name of the instance followed by spaces (0 or more) and a #. The next characters are maybe a \n followed by spaces and (. Going forward, it accepts \n followed by anything as long as it ends with '),'. Or a space followed by anything. The '?' is there to make the regex be non-greedy. The group after ( is searched for at least 2 times and must be followed by a ')\n'. This regex matches . with \n becouse of the /m at the end.
my $regex = qr/^\s*[A-Z1-9_]*\s*#\n?\s*\((((\n(.*\),\n)|(\s.*?)){2,}).*\)\n)/mp;
if ($xcvr_params_str =~ /$regex/ ) {
@xcvr_params = split ("\n", $1);
# the first element is not valid
splice(@xcvr_params, 0, 1);
} else {
print "@[$myname] No match in $pfile, check regexp!\n";
}
close(READFILE) or die print "@[$myname] Can not close file called $pfile.\n";
# parse out the attribute names
foreach my $i (0..$#xcvr_params) {
# remove leading white space
$xcvr_params[$i] =~ s/^\s+//;
# remove trailing text, keep just the first word
$xcvr_params[$i] =~ s/\s+.*$//;
# remove leading dot character
$xcvr_params[$i] =~ s/^\.//;
}
return @xcvr_params;
}
################################################################################
# Mine the COMMON and CHANNEL attributes into a list
# Required parameters : $1 - file name
# $2 - transceiver type (e.g. GTHE4_COMMON)
# $3 - parameter name list - reference array
################################################################################
sub get_attribute_value {
my ($pfile, $xcvr_type, $param_list) = @_;
my $myname = (caller(0))[3];
my %params;
my $path = Cwd::cwd();
# Try to open the file
open(READFILE, "<$pfile") or die print "@[$myname] Can not open file called $pfile for reading at $path\n";
my $xcvr_params_str = join('', <READFILE>);
# remove comments //anything
my $comment_regex = qr/^\s*\/{2}.*\n/mp;
my $subst = '';
$xcvr_params_str = $xcvr_params_str=~s/$comment_regex/$subst/rg;
foreach my $param (@$param_list) {
if ((uc($xcvr_type) eq "GTXE2_GT") or (uc($xcvr_type) eq "GTXE2_COMMON")) {
my $regex = qr/^\s*[A-Z1-9_]*\s*#\n?\s*\((((\n(.*\),\n)|(\s.*?)){2,}).*\)\n)/mp;
if ($xcvr_params_str =~ /$regex/ ) {
my $new_xcvr_params_str = $1;
if ($new_xcvr_params_str =~ m/($param.*\n)/i) {
my $value = $1;
# remove the leading attribute name and \s and '('
$value =~ s/^.*\(//;
# remove the trailing '),\n'
$value =~ s/\).*$//;
# save the attribute into the hash
chomp($value);
$params{$param} = $value;
# print "$value -> ";
# print "$params{$param}\n"
} else {
print "@[$myname] WARNING: Can not find $param\n";
}
}
} elsif ($xcvr_params_str =~ m/($xcvr_type\_$param.*\n)/i) {
my $value = $1;
$value =~ s/${xcvr_type}_//;
# remove the leading attribute name and \s and '('
$value =~ s/^.*\(//;
# remove the trailing '),\n'
$value =~ s/\).*$//;
# save the attribute into the hash
chomp($value);
$params{$param} = $value;
} else {
print "@[$myname] WARNING: Can not find $xcvr_type\_$param\n";
}
}
close(READFILE) or die print "@[$myname] Can not close file called $pfile.\n";
return %params;
}
################################################################################
# Save the attribute hash into a file
# Required parameters : $1 - file name
# $2 - parameter hash - reference
################################################################################
sub save_to_file {
my ($pfile, $params_ref) = @_;
my $myname = (caller(0))[3];
# Try to open file for write
open(WRITEFILE, ">$pfile") or die print "@[$myname] Can not open file called $pfile for writing.\n";
foreach my $key (sort keys %$params_ref){
print WRITEFILE ".$key ($$params_ref{$key}),\n";
}
close(WRITEFILE) or die print "@[$myname] Can not close file called $pfile.\n";
}
################################################################################
# Update util_adxcvr_xc[m|h] with the generated attributes
# Required parameters : $1 - file name and location of the util_adxcvr_xcm.v
# $2 - GT type, valid values {GTXE2, GTHE3, GTHE4}
# $3 - generated parameter hash - reference
################################################################################
sub xcvr_diff {
my ($pfile, $gt_type, $params_ref) = @_;
my $myname = (caller(0))[3];
# Try to open file for read
if (not open(READFILE, "<$pfile")) {
print "WARNING@[$myname] : Can not update $pfile ...\n";
return 0;
}
my @File = "";
while (my $fline = <READFILE>) {
push(@File, $fline);
}
# Find the GT's attributes location in the file
my $gt_paramblock_start = 0;
my $gt_paramblock_end = 0;
for my $i (0 .. $#File) {
if ($File[$i] =~ m/\b$gt_type/i) {
$gt_paramblock_start = $i;
}
if ($File[$i] =~ m/\bi_$gt_type/i) {
$gt_paramblock_end = $i;
}
}
close(READFILE) or die print "@[$myname] Can not close file called $pfile.\n";
## Update the attributes with the generated values
for my $i ($gt_paramblock_start..$gt_paramblock_end) {
if ($File[$i] =~ m/\.\w*/) { ## it match a word which starts with a dot
my $param = $File[$i];
$param =~ s/^\s*\.//;
$param =~ s/\s*\(.*$//;
chomp($param);
if (exists $$params_ref{$param}) {
$File[$i] =~ s/\(.*?\)/\($$params_ref{$param}\)/;
} else {
## print "@[$myname] WARNING : Parameter $param does not exist in the generated GT instance!\n"
}
}
}
# Try to open file for write
open(WRITEFILE, ">$pfile") or die print "@[$myname] Can not open file called $pfile for writing.\n";
foreach my $line (@File){
print WRITEFILE $line;
}
close(WRITEFILE) or die print "@[$myname] Can not close file called $pfile.\n";
## save the diff between the current and updated XCVR files
my $file_name = $pfile;
$file_name =~ s/^.*\///;
$file_name =~ s/\.v$//;
my $check_git = `git status`;
if ($check_git =~ m/On branch/i) {
system "git diff $pfile > $file_name.diff";
system "git checkout -- $pfile";
} else {
print "WARNING: ADI's util_xcvr's can not be updated, because the current direcotry is NOT an HDL repository!\n";
}
}
################################################################################
# Parse out the default value of a GT instance
# Required parameters : $1 - file name and location of the util_adxcvr_xc[m|h].v
# $2 - GT type, valid values {GTXE2, GTHE3, GTHE4}
################################################################################
sub xcvr_default {
my ($pfile, $gt_type) = @_;
my $myname = (caller(0))[3];
my %parameters;
my %default_attributes;
# Try to open file for read
if (not open(READFILE, "<$pfile")) {
print "WARNING@[$myname] : Can not update $pfile ...\n";
return 0;
}
# Save file into an array, also save all parameters into a hash
my @File = "";
while (my $fline = <READFILE>) {
push(@File, $fline);
if ($fline =~ m/\bparameter\b/i) {
my @param = split '=', $fline;
(my $param_name) = $param[0] =~ m/\b\w*\s+$/g;
$param_name =~ s/^\s+|\s+$//g;
my $param_value = $param[1];
$param_value =~ s/,.*$//;
$param_value =~ s/^\s+|\s+$//g;
$parameters{$param_name} = $param_value;
}
}
# Find the GT's attributes location in the file
my $gt_paramblock_start = 0;
my $gt_paramblock_end = 0;
for my $i (0 .. $#File) {
if ($File[$i] =~ m/\b$gt_type/i) {
$gt_paramblock_start = $i;
}
if ($File[$i] =~ m/\bi_$gt_type/i) {
$gt_paramblock_end = $i;
}
}
close(READFILE) or die print "@[$myname] Can not close file called $pfile.\n";
## Find all the attribute and save it into a hash
for my $i ($gt_paramblock_start..$gt_paramblock_end) {
if ($File[$i] =~ m/\.\w*/) { ## it match a word which starts with a dot
my $param = $File[$i];
my $value = $File[$i];
# parse out the parameter name
$param =~ s/^\s*\.//;
$param =~ s/\s*\(.*$//;
chomp($param);
# parse out the value name
$value =~ s/^.*\(//;
$value =~ s/\).*$//;
chomp($value);
## if it's a module parameter switch to its value
if (exists $parameters{$value}) {
$value = $parameters{$value};
}
## convert to hex only if it's a binary number, leave it as it is otherwise
my $old_value = $value;
$value =~ s/^.*b//;
if ($value =~ /^[0-1]+$/) {
$value = sprintf('0x%X', oct("0b$value"));
$default_attributes{$param} = $value;
} else {
$default_attributes{$param} = $old_value;
}
}
}
return \%default_attributes;
}
################################################################################
# Generate the required DRP sequence for software
# Required parameters : $1 - util_xcvr diff file
# $2 - parameter hash - reference
# $3 - target file name
################################################################################
sub gen_drp_cmd {
my ($pfile_rd, $xcvr_params_ref, $pfile_wr) = @_;
my $myname = (caller(0))[3];
#my $t3 = Text::SimpleTable::AutoWidth->new( captions => [qw/ DRP_register Value_binary Value_hex /] );
my %drp_access;
# Try to open file for read
open(READFILE, "<$pfile_rd") or die print "@[$myname] Can not open file called $pfile_rd for reading.\n";
my @File = "";
while (my $fline = <READFILE>) {
if ($fline =~ m/^\+\s+/) {
push(@File, $fline);
}
}
close(READFILE) or die print "@[$myname] Can not close file called $pfile_rd.\n";
# Try to open file for write
open(WRITEFILE, ">$pfile_wr") or die print "@[$myname] Can not open file called $pfile_wr for writing.\n";
# Parse the differences and check if the attribute is valid
for my $i (0 .. $#File) {
my $param_name = $File[$i];
#print "$param_name\n";
$param_name =~ s/^\+\s*\.//;
$param_name =~ s/\s+.*$//;
chomp($param_name);
#print "$param_name\n\n";
my $param_value = $File[$i];
$param_value =~ s/^.*\(//;
$param_value =~ s/\).*$//;
chomp($param_value);
#print "$param_value\n";
## ignore all attributes which are related to unused features and
## double check attribute validity
if ($param_name =~ /^(?!ES_)(?!PCIE_)(?!TXPI_)(?!TX_PI_BIASSET)/) {
if (exists $$xcvr_params_ref{$param_name}) {
my $param_value_hex = $param_value;
$param_value_hex =~ s/^.*'b//;
## convert to hex only if it's a binary number, leave it as it is otherwise
if ($param_value_hex =~ /^[0-1]+$/) {
$param_value_hex = sprintf('0x%X', oct("0b$param_value_hex"));
}
#$t3->row($param_name, $param_value, $param_value_hex);
print WRITEFILE "$param_name - $param_value - $param_value_hex\n";
$drp_access{$param_name} = $param_value_hex;
} else {
if ($param_name ne "") {
print "@[$myname] WARNING : Parameter $param_name does not exist from $pfile_rd!\n"
}
}
}
}
#print WRITEFILE $t3->draw;
print WRITEFILE "\n";
close(WRITEFILE) or die print "@[$myname] Can not close file called $pfile_wr.\n";
return \%drp_access;
}
################################################################################
# Parse all the PLL dividers into a file
# Required parameters : $1 - name of the file
# $2 - generated common parameter hash - reference
# $3 - generated channel parameter hash - reference
#
# Return value - a hash with the PLL dividers for further processing
#
################################################################################
sub parse_pll_dividers {
my ($pfile, $cm_params_ref, $ch_params_ref) = @_;
my $myname = (caller(0))[3];
## Hash for storing all the PLL dividers
my %pll_dividers = (
"QPLL0_FBDIV" => 0,
"QPLL0_FBDIV_G3" => 0,
"QPLL0_REFCLK_DIV" => 0,
"QPLL0CLKOUT_RATE" => 0,
"QPLL1_FBDIV" => 0,
"QPLL1_FBDIV_G3" => 0,
"QPLL1_REFCLK_DIV" => 0,
"QPLL1CLKOUT_RATE" => 0,
"CPLL_FBDIV" => 0,
"CPLL_FBDIV_45" => 0,
"CPLL_REFCLK_DIV" => 0,
"RXOUT_DIV" => 0,
"TXOUT_DIV" => 0,
"RX_CLK25_DIV" => 0,
"TX_CLK25_DIV" => 0
);
my %used_pll_dividers;
# Update the PLL divider hash
foreach my $pll_dividers_key (keys %pll_dividers) {
if (exists $$cm_params_ref{$pll_dividers_key}) {
$used_pll_dividers{$pll_dividers_key} = $$cm_params_ref{$pll_dividers_key};
}
if (exists $$ch_params_ref{$pll_dividers_key}) {
$used_pll_dividers{$pll_dividers_key} = $$ch_params_ref{$pll_dividers_key};
}
}
# Try to open file for write
open(WRITEFILE, ">$pfile") or die print "@[$myname] Can not open file called $pfile for writing.\n";
foreach my $pll_dividers_key (keys %used_pll_dividers) {
print WRITEFILE "$pll_dividers_key = $used_pll_dividers{$pll_dividers_key}\n";
}
close(WRITEFILE) or die print "@[$myname] Can not close file called $pfile.\n";
return \%used_pll_dividers;
}
################################################################################
# Parse the generated file returned by the GT Wizard of a given configuration
# and return all the GT attribute and its values
# Required parameters : $1 - XCVR type
# $2 - GT Instance type (expected values: 'CHANNEL' or 'COMMON')
# $3 - the path to util_adxcvr_xc[m|h]
#
################################################################################
sub parse_gt_attribute {
my $myname = (caller(0))[3];
my ($xcvr_type, $xcvr_ins_type, $hdl_path) = @_;
## arrays and hashes for the attributes
my @xcvr_attribute_names;
my %xcvr_attribute;
if (uc($xcvr_type) eq "GTXE2"){
## common and gt
my $fp_xcvr_wrapper = `find . -iname '*$xcvr_type*_$xcvr_ins_type.v' -print0 -quit`;
#chomp($fp_xcvr_wrapper);
## hdl's repo util_adxcvr files -- we need this for comparison and default values
$xcvr_ins_type = lc($xcvr_ins_type);
my $xcvr_path;
if ($xcvr_ins_type eq "gt") {
$xcvr_path = "${hdl_path}/library/xilinx/util_adxcvr/util_adxcvr_xch.v";
} elsif ($xcvr_ins_type eq "common") {
$xcvr_path = "${hdl_path}/library/xilinx/util_adxcvr/util_adxcvr_xcm.v";
} else {
die print "@[$myname] Invalid instance type. Expected values are GT or COMMON.\n";
}
if (($fp_xcvr_wrapper eq "") && ($xcvr_ins_type eq "common")) {
## print "NOTE : QPLL is not used in this configuration ...\n";
} else {
# extract all the attributes
# TODO: get name from the fp_xcvr_wrapper file
my $ls = `pwd`;
@xcvr_attribute_names = get_attribute_name ($fp_xcvr_wrapper);
%xcvr_attribute = get_attribute_value ($fp_xcvr_wrapper, "$xcvr_type\_$xcvr_ins_type", \@xcvr_attribute_names);
# save into a file
save_to_file("$xcvr_ins_type.txt", \%xcvr_attribute);
# create a git diff for comparison
xcvr_diff($xcvr_path, $xcvr_type, \%xcvr_attribute);
}
} else{
## find the COMMON and CHANNEL instances
my $fp_xcvr = `find . -iname '*$xcvr_type\_$xcvr_ins_type.v' -print0 -quit`;
my $fp_xcvr_wrapper = `find . -iname '*gt*_$xcvr_ins_type\_wrapper.v' -print0 -quit`;
## hdl's repo util_adxcvr files -- we need this for comparison and default values
$xcvr_ins_type = lc($xcvr_ins_type);
my $xcvr_path;
if ($xcvr_ins_type eq "channel") {
$xcvr_path = "${hdl_path}/library/xilinx/util_adxcvr/util_adxcvr_xch.v";
} elsif ($xcvr_ins_type eq "common") {
$xcvr_path = "${hdl_path}/library/xilinx/util_adxcvr/util_adxcvr_xcm.v";
} else {
die print "@[$myname] Invalid instance type. Expected values are CHANNEL or COMMON.\n";
}
if (($fp_xcvr eq "") && ($xcvr_ins_type eq "common")) {
## print "NOTE : QPLL is not used in this configuration ...\n";
} else {
# extract all the attributes
@xcvr_attribute_names = get_attribute_name ($fp_xcvr);
%xcvr_attribute = get_attribute_value ($fp_xcvr_wrapper, "$xcvr_type\_$xcvr_ins_type", \@xcvr_attribute_names);
# save into a file
save_to_file("$xcvr_ins_type.txt", \%xcvr_attribute);
# create a git diff for comparison
xcvr_diff($xcvr_path, $xcvr_type, \%xcvr_attribute);
}
}
return \%xcvr_attribute;
}
################################################################################
# Parse the generated file returned by the GT Wizard of a given configuration
# Required parameters : $1 - XCVR type
# $2 - the path to the HDL repository
#
# Return value - a hash with the DRP registers for further processing
#
################################################################################
sub parse_gt {
my ($xcvr_type, $hdl_path) = @_;
my %gt_drp_access;
my $xcvr_common;
my $xcvr_channel;
## hash references for the attribute hash
if (uc($xcvr_type) eq "GTXE2"){
## common and gt
$xcvr_common = parse_gt_attribute($xcvr_type, "common", $hdl_path);
$xcvr_channel = parse_gt_attribute($xcvr_type, "gt", $hdl_path);
} else{
$xcvr_common = parse_gt_attribute($xcvr_type, "common", $hdl_path);
$xcvr_channel = parse_gt_attribute($xcvr_type, "channel", $hdl_path);
}
## create the drp hash with info related to common and channel attributes and
## PLL dividers
if (keys %{ $xcvr_common }) {
$gt_drp_access{'common'} = gen_drp_cmd("util_adxcvr_xcm.diff", $xcvr_common, "common_drp_access.txt");
}
$gt_drp_access{'channel'} = gen_drp_cmd("util_adxcvr_xch.diff", $xcvr_channel, "channel_drp_access.txt");
$gt_drp_access{'pll_dividers'} = parse_pll_dividers("pll_div.txt", $xcvr_common, $xcvr_channel);
## prune out all the PLL dividers from the common/channel hash
foreach my $key (keys %{ $gt_drp_access{'pll_dividers'}}) {
if (exists $gt_drp_access{'channel'}{$key}) {
delete $gt_drp_access{'channel'}{$key};
}
if (exists $gt_drp_access{'common'}{$key}) {
delete $gt_drp_access{'common'}{$key};
}
}
return \%gt_drp_access;
}
################################################################################
# Print a table in a CSV supported format
# Required parameters : $1 - DRP hash reference
# $1 - File name
#
# Return value - None
#
################################################################################
sub print_table {
my ($gt_drp_ref, $gt_defcommon_ref, $gt_defchannel_ref, $file_name) = @_;
my @gt_configs;
my @gt_attributes;
my %gt_drp_buf;
## save all configuration into an array
foreach my $config_key (keys %{ $gt_drp_ref }) {
push(@gt_configs, $config_key);
}
## save all attributes into an array
foreach my $config_key (keys %{ $gt_drp_ref }) {
foreach my $type_key (keys %{ $$gt_drp_ref{$config_key} }) {
if ($type_key ne 'pll_dividers') {
foreach my $attribute_key (keys %{ $$gt_drp_ref{$config_key}{$type_key} }) {
my $exist = 0;
foreach (@gt_attributes) {
if ($_ eq $attribute_key) {
$exist = 1;
last;
}
}
if (not $exist) {
push (@gt_attributes, $attribute_key);
}
}
}
}
}
## open file
open(MYTABLE, ">$file_name.csv") or die print "Can not open file called $file_name.csv for writing.\n";
print MYTABLE "Attribute_names,\t";
print MYTABLE "Default_value,\t";
## print the header
foreach my $config_key (keys %{ $gt_drp_ref }) {
print MYTABLE "$config_key,\t";
}
print MYTABLE "\n";
## populate the CSV table
for my $i (0 .. $#gt_attributes) {
print MYTABLE "$gt_attributes[$i],\t";
my $default_value = "";
if (exists $$gt_defcommon_ref{$gt_attributes[$i]}) {
$default_value = $$gt_defcommon_ref{$gt_attributes[$i]};
## convert the default value to a 0xhhhh format
$default_value = lc($default_value);
$default_value =~ s/^\d*\'h/0x/gi;
print MYTABLE "$default_value,\t";
} elsif (exists $$gt_defchannel_ref{$gt_attributes[$i]}) {
$default_value = $$gt_defchannel_ref{$gt_attributes[$i]};
## convert the default value to a 0xhhhh format
$default_value = lc($default_value);
$default_value =~ s/^\d*\'h/0x/gi;
print MYTABLE "$default_value,\t";
} else {
print MYTABLE ",\t";
}
for my $j (0 .. $#gt_configs) {
my $is_exist = 0;
my $normalized;
my $value;
foreach my $type_key (keys %{ $$gt_drp_ref{$gt_configs[$j]}}) {
if ((exists $$gt_drp_ref{$gt_configs[$j]}{$type_key}{$gt_attributes[$i]}) and ($$gt_drp_ref{$gt_configs[$j]}{$type_key}{$gt_attributes[$i]})) {
$value = $$gt_drp_ref{$gt_configs[$j]}{$type_key}{$gt_attributes[$i]};
$normalized = $value;
$normalized =~ s/^\d*\'h/0x/gi;
# print "$normalized : $default_value\n";
#if (oct(lc($value)) != oct(lc($default_value))) {
#if ((lc($$gt_drp_ref{$gt_configs[$j]}{$type_key}{$gt_attributes[$i]})) eq ($default_value)) {
if ($normalized ne $default_value){
print MYTABLE "$normalized,\t";
} else {
print MYTABLE ",\t";
}
$is_exist = 1;
last;
}
}
## if the current attribute does not have a value in the current configuration
if ($is_exist == 0) {
print MYTABLE ",\t";
}
}
print MYTABLE "\n";
}
close(MYTABLE) or die print "Can not close file called $file_name.csv.\n";
}
################################################################################
# Prune out all the entries which are present in every configuration with the
# same value.
# Required parameters : $1 - GT DRP hash generated by the parser
#
################################################################################
sub prune_drp_access {
my ($gt_drp_ref) = @_;
my %gt_generic_drp;
my $is_generic;
## select a random configuration
my @gt_drp_ref_keys = keys %$gt_drp_ref;
my $ref_conf = $gt_drp_ref_keys[rand @gt_drp_ref_keys];
foreach my $gt_ref_conf_type_key (keys %{ $$gt_drp_ref{$ref_conf} }) {
foreach my $gt_ref_conf_attribute_key (keys %{ $$gt_drp_ref{$ref_conf}{$gt_ref_conf_type_key} }) {
$is_generic = 1;
foreach my $gt_drp_config_key (keys %{ $gt_drp_ref }) {
if ($gt_drp_config_key ne $ref_conf) {
if (exists $$gt_drp_ref{$gt_drp_config_key}{$gt_ref_conf_type_key}{$gt_ref_conf_attribute_key}) {
if ($$gt_drp_ref{$gt_drp_config_key}{$gt_ref_conf_type_key}{$gt_ref_conf_attribute_key} eq '') {
delete $$gt_drp_ref{$gt_drp_config_key}{$gt_ref_conf_type_key}{$gt_ref_conf_attribute_key};
}
## check if a given attribute value is the same in all the configurations
elsif ($$gt_drp_ref{$ref_conf}{$gt_ref_conf_type_key}{$gt_ref_conf_attribute_key} ne
$$gt_drp_ref{$gt_drp_config_key}{$gt_ref_conf_type_key}{$gt_ref_conf_attribute_key}) {
$is_generic = 0;
}
} else {
## if it does not exists, can not be generic
$is_generic = 0;
}
}
}
if ($is_generic) {
# Store it into another hash
$gt_generic_drp{'gt_global'}{$gt_ref_conf_type_key}{$gt_ref_conf_attribute_key} = $$gt_drp_ref{$ref_conf}{$gt_ref_conf_type_key}{$gt_ref_conf_attribute_key};
## Delete the attribute from the \%gt_drp_ref
foreach my $gt_drp_config_key (keys %{ $gt_drp_ref }) {
delete $$gt_drp_ref{$gt_drp_config_key}{$gt_ref_conf_type_key}{$gt_ref_conf_attribute_key};
}
}
}
}
return \%gt_generic_drp;
}
################################################################################
# Generate a value distribution hash for each variable
#
# E.g. attribute_name -> %existing_values -> @configuration
#
# Required parameters : $1 - GT DRP hash generated by the parser
#
################################################################################
sub drp_value_distribution {
my ($gt_drp_ref) = @_;
my %gt_drp_dist;
foreach my $config_key (keys %{ $gt_drp_ref }) {
foreach my $type_key (keys %{ $$gt_drp_ref{$config_key} }) {
foreach my $attribute_key (keys %{ $$gt_drp_ref{$config_key}{$type_key} }) {
my $value = $$gt_drp_ref{$config_key}{$type_key}{$attribute_key};
## store a new configuration name to a (attribute,value) pair
push (@{ $gt_drp_dist{$attribute_key}{$value} }, $config_key);
}
}
}
## sort the arrays with the GT configuration to improve readability
foreach my $attribute_key (keys %gt_drp_dist) {
foreach my $value (keys %{ $gt_drp_dist{$attribute_key} }) {
my @sorted = sort @{ $gt_drp_dist{$attribute_key}{$value} };
@{ $gt_drp_dist{$attribute_key}{$value} } = @sorted;
}
}
return \%gt_drp_dist;
}
################################################################################
# Generate a VCO frequency distribution hash for each variable
#
# E.g. attribute_name -> %VCO_freq -> @attribute_value
#
# Required parameters : $1 - GT DRP hash generated by the parser
#
################################################################################
sub uniq {
my %seen;
grep !$seen{$_}++, @_;
}
sub drp_vcof_distribution {
my ($gt_drp_ref, $gt_default_ref) = @_;
my %gt_drp_dist;
foreach my $config_key (keys %{ $gt_drp_ref }) {
foreach my $type_key (keys %{ $$gt_drp_ref{$config_key} }) {
if ($type_key ne 'pll_dividers') {
foreach my $attribute_key (keys %{ $$gt_drp_ref{$config_key}{$type_key} }) {
my @words = split /_/, $config_key;
my $lane_rate;
if ($words[4]) {
$lane_rate = "$words[2].$words[3]";
} else {
$lane_rate = $words[2];
}
if ($$gt_drp_ref{$config_key}{'pll_dividers'}{'RXOUT_DIV'}) {
my $vcof = ($lane_rate * $$gt_drp_ref{$config_key}{'pll_dividers'}{'RXOUT_DIV'}) / 2;
my $value = $$gt_drp_ref{$config_key}{$type_key}{$attribute_key};
# my %conf_with_value;
# $conf_with_lane_ratevalue{$value} = $config_key;
## store a new configuration name to a (attribute,value) pair
## push (@{ $gt_drp_dist{$attribute_key}{$vcof} }, $value);
## push (@{ $gt_drp_dist{$attribute_key}{$vcof} }, %conf_with_value);
# print "$vcof\n";
push (@{ $gt_drp_dist{$attribute_key}{$vcof}{$value} }, $config_key);
}
}
}
}
}
## sort the arrays with the GT configuration to improve readability
foreach my $attribute_key (keys %gt_drp_dist) {
# foreach my $vcof (keys %{ $gt_drp_dist{$attribute_key} }) {
# my @sorted = sort @{ $gt_drp_dist{$attribute_key}{$vcof} };
# my @uniq = uniq(@sorted);
# @{ $gt_drp_dist{$attribute_key}{$vcof} } = @uniq;
# }
## add the default value to each attribute
if (exists $$gt_default_ref{'common'}{$attribute_key}) {
$gt_drp_dist{$attribute_key}{'default'} = $$gt_default_ref{'common'}{$attribute_key}
}
if (exists $$gt_default_ref{'channel'}{$attribute_key}) {
$gt_drp_dist{$attribute_key}{'default'} = $$gt_default_ref{'channel'}{$attribute_key}
}
}
return \%gt_drp_dist;
}
################################################################################
# MAIN program
################################################################################
## Hash for storing all the DRP register values
my %gt_drp;
my %gt_default;
## Valid transceiver types are: GTHE3, GTHE4, GTYE3, GTYE4
my $xcvr_type = $ARGV[0];
## Check the ADI_HDL_DIR environment variable, exist if it does not exist
my $hdl_path = "";
if (defined $ENV{ADI_HDL_DIR}) {
$hdl_path = $ENV{ADI_HDL_DIR};
} else {
# print `pwd`;
my $curr_dir = `pwd`;
my $new_str = chop($curr_dir);
my $relative_path = "/../../../../../..";
$hdl_path = $curr_dir.$relative_path;
# print "ERROR: ADI_HDL_DIR is undefined! Please define this environment variable.\n";
# exit;
}
## Currently only GTXE2, GTHE3, GTYE3, GTHE4 and GTYE4 is supported
print "NOTE: To parse the generated GT IP, change directory to *.gen/sources_1/ip/ ...\n";
if (not defined $xcvr_type) {
die "ERROR Need to define an XCVR type. (GTXE2, GTHE3, GTHE4, GTYE3, GTYE4)";
} elsif ((uc($xcvr_type) ne "GTHE3") and (uc($xcvr_type) ne "GTHE4") and (uc($xcvr_type) ne "GTYE3") and (uc($xcvr_type) ne "GTYE4") and (uc($xcvr_type) ne "GTXE2")) {
die "ERROR Wrong XCVR type, should be GTXE2, GTHE3, GTHE4, GTYE3, GTYE4";
}
## save the default values into $gt_drp->'default'
$gt_default{'common'} = xcvr_default("$hdl_path/library/xilinx/util_adxcvr/util_adxcvr_xcm.v", $xcvr_type);
$gt_default{'channel'} = xcvr_default("$hdl_path/library/xilinx/util_adxcvr/util_adxcvr_xch.v", $xcvr_type);
## iterate through all the configurations
my @gt_config_list = `ls`;
foreach my $gt_config (@gt_config_list) {
chomp($gt_config);
$gt_config =~ s/^\s+|\s+$//g;
if (-d $gt_config) {
chdir $gt_config;
$gt_drp{$gt_config} = parse_gt($xcvr_type, $hdl_path);
chdir "../";
}
}
## prune the DRP access which is generic for all the configuration
my $gt_generic_drp = prune_drp_access(\%gt_drp);
open(WRITEFILE, ">$xcvr_type\_cfng.txt") or die print "Can not open file called $xcvr_type\_cfng.txt for writing.\n";
print WRITEFILE "========================================================================\n";
print WRITEFILE "Unique configuration/attribute values\n";
print WRITEFILE "The following attribute/value pairs are unique for one or more configuration\n";
print WRITEFILE "========================================================================\n";
print WRITEFILE Dumper(%gt_drp);
print WRITEFILE "========================================================================\n";
print WRITEFILE "Common configuration/attribute values\n";
print WRITEFILE "The following attribute/value pairs are the same for each configuration \n";
print WRITEFILE "========================================================================\n";
print WRITEFILE Dumper(%{ $gt_generic_drp });
close(WRITEFILE) or die print "Can not close file called daq2_gthe4.txt.\n";
print_table(\%gt_drp, $gt_default{'common'}, $gt_default{'channel'}, "table_unique");
print_table($gt_generic_drp, $gt_default{'common'}, $gt_default{'channel'}, "table_common");
# create a hash table which reflects the value distribution of each DRP attribute
# in function of lane rates and reference clock
my $gt_drp_var_dist = drp_value_distribution(\%gt_drp);
open(WRITEFILE, ">$xcvr_type\_var_dist.txt") or die print "Can not open file called $xcvr_type\_var_dist.txt for writing.\n";
print WRITEFILE "========================================================================\n";
print WRITEFILE "Value distribution of each DRP attribute in function of lane rates\n";
print WRITEFILE "========================================================================\n";
print WRITEFILE Dumper(%{ $gt_drp_var_dist });
close(WRITEFILE) or die print "Can not close file called $xcvr_type\_var_dist.txt.\n";
# create a hash table which reflects the value distribution of each DRP attribute
# in function of VCO frequency
my $gt_drp_vco_dist = drp_vcof_distribution (\%gt_drp, \%gt_default);
open(WRITEFILE, ">$xcvr_type\_vco_dist.txt") or die print "Can not open file called $xcvr_type\_vco_dist.txt for writing.\n";
print WRITEFILE "========================================================================\n";
print WRITEFILE "Value distribution of each DRP attribute in function of VCO frequency\n";
print WRITEFILE "========================================================================\n";
print WRITEFILE Dumper(%{ $gt_drp_vco_dist });
close(WRITEFILE) or die print "Can not close file called $xcvr_type\_vco_dist.txt.\n";

View File

@ -0,0 +1,531 @@
###############################################################################
# CPLL - generate reference clocks for a given Lane Rate
#
# Attributes: NOTE lane rate should be define in kHz
#
# Return: the result will be in millihertz
# E.g. set value_MHz [format "%.7f" [expr $value / 1e9]]
#
###############################################################################
proc cpll_ref_clk_gen { lane_rate } {
#
# fLineRate = (REF_CLK * (FBDIV * FBDIV_45) / REFCLK_DIV) / OUT_DIV
#
set out_div_l { 1 2 4 8 }
set refclk_div_l { 1 2 }
set fbdiv_l { 1 2 3 4 5 }
set fbdiv_45_l { 4 5 }
set pllclkout_l ""
set ref_clk_l ""
## Calculate the VCO
for {set i 0} {$i < [llength $out_div_l]} {incr i} {
## to get the right resolution which is liked by the Wizard we have to upscale
## the value of the Lane Rate
set pll_out [expr [expr [expr $lane_rate * 1e6] * [lindex $out_div_l $i]] / 2]
if { ($pll_out >= [expr 2 * 1e12]) && ($pll_out <= [expr 6.25 * 1e12]) } {
lappend pllclkout_l $pll_out
}
}
## Calculate the reference clock
for {set i 0 } {$i < [llength $pllclkout_l]} {incr i} {
for {set j 0} {$j < [llength $refclk_div_l]} {incr j} {
for {set k 0} {$k < [llength $fbdiv_l]} {incr k} {
for {set l 0} {$l < [llength $fbdiv_45_l]} {incr l} {
set ref_clk [expr [expr [lindex $pllclkout_l $i] * [lindex $refclk_div_l $j]] / [expr [lindex $fbdiv_l $k] * [lindex $fbdiv_45_l $l]]]
if {[lsearch $ref_clk_l [expr $ref_clk / 1e9]] < 0} {
## Apperantly the Wizard support a maximum 800 MHz reference clock
if { $ref_clk <= [expr 8 * 1e11] } {
lappend ref_clk_l [expr $ref_clk / 1e9]
}
}
}
}
}
}
return [lsort -real $ref_clk_l]
}
###############################################################################
# QPLL - generate reference clocks for a given Lane Rate - does not support
# fractional-N
#
# Attributes: lane rate should be define in kHz
#
# Return: the result will be in millihertz
# E.g. set value_MHz [format "%.7f" [expr $value / 1e9]]
#
###############################################################################
proc qpll_ref_clk_gen { gt_type lane_rate } {
#
# fLineRate = ((REF_CLK * FBDIV) / (REFCLK_DIV * 2)) * 2 / OUT_DIV
#
## OUT_DIV has a wider range in case of GTYs
if {[string equal $gt_type GTYE3] || [string equal $gt_type GTYE4]} {
set out_div_l { 1 2 4 8 16 32}
} else {
set out_div_l { 1 2 4 8 16 }
}
set refclk_div_l { 1 2 3 4 }
## NOTE: At GTYE3|4 the range is 16-160
set fbdiv_l { 16 20 32 40 60 64 66 75 80 84 90 96 100 112 120 125 150 160 }
set pllclkout_l ""
set ref_clk_l ""
## Calculate the VCO
for {set i 0} {$i < [llength $out_div_l]} {incr i} {
## to get the right resolution which is accepted by the Wizard we have to upscale
## the value of the Lane Rate
set pll_out [expr [expr [expr $lane_rate * 1e6] * [lindex $out_div_l $i]] / 2]
if { ($pll_out >= [expr 4.9 * 1e12]) && ($pll_out <= [expr 8.1875 * 1e12]) } {
lappend pllclkout_l $pll_out
}
}
## Calculate the reference clock
for {set i 0 } {$i < [llength $pllclkout_l]} {incr i} {
for {set j 0} {$j < [llength $refclk_div_l]} {incr j} {
for {set k 0} {$k < [llength $fbdiv_l]} {incr k} {
set ref_clk [expr [expr [expr [lindex $pllclkout_l $i] * [lindex $refclk_div_l $j]] * 2] / [lindex $fbdiv_l $k]]
if {[lsearch $ref_clk_l [expr $ref_clk / 1e9]] < 0} {
## Apperantly the Wizard support a maximum 800 MHz reference clock
if { $ref_clk <= [expr 8 * 1e11] } {
lappend ref_clk_l [expr $ref_clk / 1e9]
}
}
}
}
}
return [lsort -real $ref_clk_l]
}
###############################################################################
##
## This script can be used to generate multiple GTHE[3|4] PHY with the GT wizard
## for further examination.
## The script should be used in tandem with the gtwiz_parser.pl and must be called
## inside a Ultrascale or Ultrascale+ project.
##
###############################################################################
proc ad_gth_generator { {lane_rate_l {}} {pll_type {}} {ref_clk_l {}} } {
set prj_name [get_property NAME [current_project]]
## Currently supporting both GTH and GTY's, expected boards are ZCU102/KCU105/VCU108/VCU118
#TODO: define this switch to be generic and support all Ultrascale/Ultrascale+ devices
set board [string range [get_property PART [current_project]] 0 6]
switch $board {
"xc7z045" {
set gt_type GTXE2
# set channel_enable [list X0Y15 X0Y14 X0Y13 X0Y12]
set ref_clk "clk1"
# set ref_clk_source [list X0Y15 $ref_clk X0Y14 $ref_clk X0Y13 $ref_clk X0Y12 $ref_clk]
}
"xc7k325" {
set gt_type GTXE2
# set channel_enable [list X0Y15 X0Y14 X0Y13 X0Y12]
set ref_clk "clk1"
# set ref_clk_source [list X0Y15 $ref_clk X0Y14 $ref_clk X0Y13 $ref_clk X0Y12 $ref_clk]
}
"xc7z020" {
set gt_type GTXE2
# set channel_enable [list X0Y15 X0Y14 X0Y13 X0Y12]
set ref_clk "clk1"
# set ref_clk_source [list X0Y15 $ref_clk X0Y14 $ref_clk X0Y13 $ref_clk X0Y12 $ref_clk]
}
"xc7vx48" {
set gt_type GTXE2
# set channel_enable [list X0Y15 X0Y14 X0Y13 X0Y12]
set ref_clk "clk1"
# set ref_clk_source [list X0Y15 $ref_clk X0Y14 $ref_clk X0Y13 $ref_clk X0Y12 $ref_clk]
}
"xc7vx69" {
# set gt_type GTXE2
# set channel_enable [list X0Y15 X0Y14 X0Y13 X0Y12]
# set ref_clk "clk1"
# set ref_clk_source [list X0Y15 $ref_clk X0Y14 $ref_clk X0Y13 $ref_clk X0Y12 $ref_clk]
puts "ERROR ad_gth_generator: Unsupported device."
return 1
}
"xcku040" {
set gt_type GTHE3
set channel_enable [list X0Y16 X0Y17 X0Y18 X0Y19]
set ref_clk "clk1"
set ref_clk_source [list X0Y16 $ref_clk X0Y17 $ref_clk X0Y18 $ref_clk X0Y19 $ref_clk]
}
"xcvu095" {
set gt_type GTYE3
set channel_enable [list X0Y16 X0Y17 X0Y18 X0Y19]
set ref_clk "clk1"
set ref_clk_source [list X0Y16 $ref_clk X0Y17 $ref_clk X0Y18 $ref_clk X0Y19 $ref_clk]
}
"xczu9eg" {
set gt_type GTHE4
set channel_enable [list X0Y15 X0Y14 X0Y13 X0Y12]
set ref_clk "clk1"
set ref_clk_source [list X0Y15 $ref_clk X0Y14 $ref_clk X0Y13 $ref_clk X0Y12 $ref_clk]
}
"xcvu9p-" {
set gt_type GTYE4
set channel_enable [list X0Y15 X0Y14 X0Y13 X0Y12]
set ref_clk "clk1"
set ref_clk_source [list X0Y15 $ref_clk X0Y14 $ref_clk X0Y13 $ref_clk X0Y12 $ref_clk]
}
"xcvu37p" {
set gt_type GTYE4
# ??
set channel_enable [list X0Y15 X0Y14 X0Y13 X0Y12]
set ref_clk "clk1"
# ??
set ref_clk_source [list X0Y15 $ref_clk X0Y14 $ref_clk X0Y13 $ref_clk X0Y12 $ref_clk]
}
default {
puts "ERROR ad_gth_generator: Unsupported device."
return 1
}
}
## define the PLL's min and max range in GHz
set pll_min_range ""
set pll_max_range ""
switch $pll_type {
CPLL {
set pll_min_range 0.5
set pll_max_range 12.5
}
QPLL {
if {[string equal $gt_type GTXE2]} {
set pll_min_range 0.5
set pll_max_range 10.3125
} else {
puts "Invalid PLL type -- $pll_type"
return 1
}
}
QPLL0 {
set pll_min_range 0.6125
set pll_max_range 16.375
}
QPLL1 {
set pll_min_range 0.5
set pll_max_range 13.0
}
default {
puts "Invalid PLL type -- $pll_type"
return 1
}
}
set inst_num 0
foreach lane_rate $lane_rate_l {
## make sure that the lane rate is inside the valid range
if { ($lane_rate < $pll_min_range) || ($lane_rate > $pll_max_range)} {
## TODO: remove the lane rate from the list so that it is not dragged though the parsing script
## remove the invalid lane rate from the list
## set lane_rate_l [lreplace $lane_rate_l [lsearch $lane_rate] [lsearch $lane_rate]]
puts "NOTE: $lane_rate is out of $pll_type range. Skipping it..."
continue
} else {
## generate the reference clock list for each lane rate
if { [llength $ref_clk_l] == 0 } {
switch -glob $pll_type {
CPLL {
set ref_clk_l [cpll_ref_clk_gen [expr $lane_rate * 1e6]]
}
QPLL* {
set ref_clk_l [qpll_ref_clk_gen $gt_type [expr $lane_rate * 1e6]]
}
default {
Something went wrong with $pll_type
}
}
puts "NOTE: [llength $ref_clk_l] different reference clock were generated. \n $ref_clk_l"
} else {
## use the reference clock
}
foreach ref_clk $ref_clk_l {
set lane_rate_txt [string replace $lane_rate [string first . $lane_rate] [string first . $lane_rate] "_"]
set ref_clk_txt [lindex [split $ref_clk "."] 0]
## format the ref_clk to be wizard compatible - precision must be max
## 7 digit with no trailing zeros
set ref_clk [format "%.7f" $ref_clk]
set ref_clk_float [split $ref_clk .]
if {[lindex $ref_clk_float 1] == 0} {
set ref_clk [lindex $ref_clk_float 0]
} else {
scan [lindex $ref_clk_float 1] "%1d%1d%1d%1d%1d%1d%1d" a b c d e f g
set float_l [list $a $b $c $d $e $f $g]
while {[lindex $float_l [expr [llength $float_l] - 1]] == 0} {
set l_index [expr [llength $float_l] - 1]
set float_l [lreplace $float_l $l_index $l_index]
}
set ref_clk [lindex $ref_clk_float 0].[join $float_l ""]
}
set ip_name "$gt_type\_$pll_type\_$lane_rate_txt\_$ref_clk_txt"
incr inst_num
## check if it is 7series or ultrascale/ultrascale+
if {[string equal $gt_type GTXE2]} {
## 7 series
## create a GT instance with the wizard, if already exist skip it
if {[lsearch [get_ips] $ip_name] == -1} {
set float_clk [format "%.3f" [expr {$ref_clk}]]
create_ip -name gtwizard -vendor xilinx.com -library ip -version 3.6 -module_name $ip_name
set_property -dict [list \
CONFIG.identical_protocol_file {JESD204} \
CONFIG.identical_val_tx_reference_clock $float_clk \
CONFIG.identical_val_rx_reference_clock $float_clk \
CONFIG.gt0_val_rx_reference_clock $float_clk \
CONFIG.gt0_val_tx_reference_clock $float_clk \
CONFIG.identical_val_tx_line_rate $lane_rate \
CONFIG.identical_val_rx_line_rate $lane_rate \
CONFIG.gt0_val_rx_line_rate $lane_rate \
CONFIG.gt0_val_tx_line_rate $lane_rate \
CONFIG.gt_val_tx_pll $pll_type \
CONFIG.gt0_usesharedlogic {1} \
CONFIG.advanced_clocking {true} \
CONFIG.gt_val_drp {false} \
CONFIG.gt0_val_drp_clock {100} \
CONFIG.gt_val_drp_clock {60} \
CONFIG.gt0_val_tx_refclk {REFCLK1_Q0} \
CONFIG.gt0_val_rx_refclk {REFCLK1_Q0} \
CONFIG.gt1_val_tx_refclk {REFCLK1_Q0} \
CONFIG.gt1_val_rx_refclk {REFCLK1_Q0} \
CONFIG.gt2_val_tx_refclk {REFCLK1_Q0} \
CONFIG.gt2_val_rx_refclk {REFCLK1_Q0} \
CONFIG.gt3_val_tx_refclk {REFCLK1_Q0} \
CONFIG.gt3_val_rx_refclk {REFCLK1_Q0} \
CONFIG.gt4_val_tx_refclk {REFCLK1_Q1} \
CONFIG.gt4_val_rx_refclk {REFCLK1_Q1} \
CONFIG.gt5_val_tx_refclk {REFCLK1_Q1} \
CONFIG.gt5_val_rx_refclk {REFCLK1_Q1} \
CONFIG.gt6_val_tx_refclk {REFCLK1_Q1} \
CONFIG.gt6_val_rx_refclk {REFCLK1_Q1} \
CONFIG.gt7_val_tx_refclk {REFCLK1_Q1} \
CONFIG.gt7_val_rx_refclk {REFCLK1_Q1} \
CONFIG.gt8_val_tx_refclk {REFCLK1_Q2} \
CONFIG.gt8_val_rx_refclk {REFCLK1_Q2} \
CONFIG.gt9_val_tx_refclk {REFCLK1_Q2} \
CONFIG.gt9_val_rx_refclk {REFCLK1_Q2} \
CONFIG.gt10_val_tx_refclk {REFCLK1_Q2} \
CONFIG.gt10_val_rx_refclk {REFCLK1_Q2} \
CONFIG.gt11_val_tx_refclk {REFCLK1_Q2} \
CONFIG.gt11_val_rx_refclk {REFCLK1_Q2} \
CONFIG.gt12_val_tx_refclk {REFCLK1_Q3} \
CONFIG.gt12_val_rx_refclk {REFCLK1_Q3} \
CONFIG.gt13_val_tx_refclk {REFCLK1_Q3} \
CONFIG.gt13_val_rx_refclk {REFCLK1_Q3} \
CONFIG.gt14_val_tx_refclk {REFCLK1_Q3} \
CONFIG.gt14_val_rx_refclk {REFCLK1_Q3} \
CONFIG.gt15_val_tx_refclk {REFCLK1_Q3} \
CONFIG.gt15_val_rx_refclk {REFCLK1_Q3} \
CONFIG.gt0_val_rxusrclk {RXOUTCLK} \
CONFIG.gt0_val_decoding {8B/10B} \
CONFIG.gt0_val_encoding {8B/10B} \
CONFIG.gt0_val_tx_data_width {32} \
CONFIG.gt0_val_tx_int_datawidth {40} \
CONFIG.gt0_val_rx_data_width {32} \
CONFIG.gt0_val_rx_int_datawidth {40} \
CONFIG.gt0_val_port_rxchariscomma {true} \
CONFIG.gt0_val_port_rxcharisk {true} \
CONFIG.gt0_val_align_pcomma_value {0101111100} \
CONFIG.gt0_val_align_mcomma_value {1010000011} \
CONFIG.gt0_val_align_comma_enable {1111111111} \
] [get_ips $ip_name]
puts "\n IP generated \n"
## generate output products and run synthesis
generate_target all [get_files \
$prj_name.srcs/sources_1/ip/$ip_name/$ip_name.xci]
catch { config_ip_cache -export [get_ips -all $ip_name] }
export_ip_user_files -of_objects [get_files $prj_name.srcs/sources_1/ip/$ip_name/$ip_name.xci] -no_script -sync -force -quiet
create_ip_run [get_files -of_objects [get_fileset sources_1] $prj_name.srcs/sources_1/ip/$ip_name/$ip_name.xci] -force
## Synthesis is optional - the generated files already contains all the
## attributes
## launch_runs -jobs 8 "$ip_name\_synth_1"
} else {
puts "NOTE: $ip_name does already exist."
}
} else {
## Ultrascale/Ultrascale+
## create a GT instance with the wizard, if already exist skip it
if {[lsearch [get_ips] $ip_name] == -1} {
create_ip -name gtwizard_ultrascale -vendor xilinx.com -library ip -version 1.7 -module_name $ip_name
## TODO: double check if this configuration is OK for JESD204B
if {[string equal $gt_type GTYE3] || [string equal $gt_type GTYE4]} {
set gt_preset GTY-JESD204
} else {
set gt_preset GTH-JESD204
}
set_property -dict [list \
CONFIG.preset $gt_preset \
CONFIG.CHANNEL_ENABLE $channel_enable \
CONFIG.TX_MASTER_CHANNEL [lindex $channel_enable 0] \
CONFIG.RX_MASTER_CHANNEL [lindex $channel_enable 0] \
CONFIG.TX_REFCLK_SOURCE $ref_clk_source \
CONFIG.RX_REFCLK_SOURCE $ref_clk_source \
CONFIG.TX_LINE_RATE $lane_rate \
CONFIG.TX_PLL_TYPE $pll_type \
CONFIG.TX_REFCLK_FREQUENCY $ref_clk \
CONFIG.TX_DATA_ENCODING {8B10B} \
CONFIG.TX_INT_DATA_WIDTH {40} \
CONFIG.RX_LINE_RATE $lane_rate \
CONFIG.RX_PLL_TYPE $pll_type \
CONFIG.RX_REFCLK_FREQUENCY $ref_clk \
CONFIG.RX_DATA_DECODING {8B10B} \
CONFIG.RX_INT_DATA_WIDTH {40} \
CONFIG.RX_EQ_MODE {LPM} \
CONFIG.RX_COMMA_P_ENABLE {true} \
CONFIG.RX_COMMA_M_ENABLE {true} \
CONFIG.RX_COMMA_MASK {1111111111} \
] [get_ips $ip_name]
puts "\n IP generated \n"
## generate output products and run synthesis
generate_target all [get_files \
$prj_name.srcs/sources_1/ip/$ip_name/$ip_name.xci]
catch { config_ip_cache -export [get_ips -all $ip_name] }
export_ip_user_files -of_objects [get_files $prj_name.srcs/sources_1/ip/$ip_name/$ip_name.xci] -no_script -sync -force -quiet
create_ip_run [get_files -of_objects [get_fileset sources_1] $prj_name.srcs/sources_1/ip/$ip_name/$ip_name.xci] -force
## Synthesis is optional - the generated files already contains all the
## attributes
## launch_runs -jobs 8 "$ip_name\_synth_1"
} else {
puts "NOTE: $ip_name does already exist."
}
}
}
}
}
}
###############################################################################
##
## This script combines the previous one with the gtwiz_parser.pl, as they are meant to work.
## It generates a PHY configuration with GT wizard, that is parsed afterwards.
## That results in a list of parameters that are specific to the configuration, but different from the default one.
## This output should then overwrite the existing instance in the system_bd.tcl.
## The output products can be found at <project_name>.gen/sources_1/ip (*_cfng.txt)
## It must be called inside a Ultrascale or Ultrascale+ project.
## Wiki documentation: wiki.analog.com/resources/fpga/docs/xgt_wizard
##
###############################################################################
proc get_diff_params { {lane_rate_l {}} {pll_type {}} {ref_clk_l {}} {keep_ip "true"} } {
## Get the gt_type for the board
set board [string range [get_property PART [current_project]] 0 6]
switch $board {
"xc7z045" {
set gt_type GTXE2
}
"xc7k325" {
set gt_type GTXE2
}
"xc7z020" {
set gt_type GTXE2
}
"xc7vx48" {
set gt_type GTXE2
}
"xc7vx69" {
#set gt_type GTHE2
puts "ERROR ad_gth_generator: Unsupported device."
return 1
}
"xcku040" {
set gt_type GTHE3
}
"xcvu095" {
set gt_type GTYE3
}
"xczu9eg" {
set gt_type GTHE4
}
"xcvu9p-" {
set gt_type GTYE4
}
"xcvu37p" {
set gt_type GTYE4
}
default {
puts "ERROR ad_gth_generator: Unsupported device."
return 1
}
}
set current_dir [pwd]
set project_name [get_property NAME [current_project]]
## Generate configurations
ad_gth_generator $lane_rate_l $pll_type $ref_clk_l
## Call parser script gtwiz_parser.pl
cd $project_name\.gen/sources_1/ip
# exec $::env(ADI_HDL_DIR)/projects/scripts/gtwiz_parser.pl $gt_type
# catch exception for the next line. If it catches something, come back to $current_dir
if { [catch { exec ../../../../../scripts/gtwiz_parser.pl $gt_type } e] } {
cd $current_dir
puts "Some error has occured: \n$e";
} else {
exec ../../../../../scripts/gtwiz_parser.pl $gt_type
cd $current_dir
## if keep_ip not true, remove from the project the generated IPs and delete them
if {$keep_ip ne "true"} {
foreach lane_rate $lane_rate_l {
foreach ref_clk $ref_clk_l {
set lane_rate_txt [string replace $lane_rate [string first . $lane_rate] [string first . $lane_rate] "_"]
set ref_clk_txt [lindex [split $ref_clk "."] 0]
## Get the paths to generated IP so that it can be removed
set src_path $current_dir/$project_name\.srcs/sources_1/ip
set gen_path $current_dir/$project_name\.gen/sources_1/ip
set ip_name [eval exec ls $src_path | grep $gt_type\_$pll_type\_$lane_rate_txt\_$ref_clk_txt]
set ip_path_src $src_path\/$ip_name
set ip_path_gen $gen_path\/$ip_name
set xci_file $ip_path_src/$ip_name\.xci
## Remove the generated IP after the differences were written
export_ip_user_files -of_objects [get_files $xci_file] -no_script -reset -force -quiet
remove_files -fileset $ip_name $xci_file
file delete -force $ip_path_src
file delete -force $ip_path_gen
}
}
} else {
puts "\ngenerated files can be find at $project_name\.gen/sources_1/ip"
}
puts "\nconfiguration file for the tranciever is $project_name\.gen/sources_1/ip/$gt_type\_cfng.txt"
}
}