Skip to content

Instantly share code, notes, and snippets.

@a3f
Created May 31, 2021
Embed
What would you like to do?
EZchip clock driver generator
#!/usr/bin/env perl
# StarFive software release has a few thousand lines of macro soup
# apparently generated from HDL. This script generates common clk
# framework-like-code out of it
# Usage: ./scripts/ezchip-clk-parse.pl clkgen_ctrl_macro.h
use strict;
use warnings;
my %clks;
while (<>) {
if (/^#define\s+clk_(.+)_ctrl_REG_ADDR\s+CLKGEN_BASE_ADDR \+ (.+)$/) {
$clks{$1} = { offset => $2 };
$clks{$1}{parents} = {};
$clks{$1}{type} = '';
}
if (/^#define\s+_SWITCH_CLOCK_clk_(.+)_SOURCE_(.+)_\s*\{\s*\\/) {
$clks{$1}{type} = 'mux';
my $remainder = '';
my $src = $2;
while (<>) {
$remainder .= $_;
s/(\|=.*)\);/$1<<0/;
last if /\}/;
}
if ($remainder =~
/\s*uint32_t\s+_ezchip_macro_read_value_\s*=\s*MA_INW\(clk_(.+)_ctrl_REG_ADDR\);\s*\\
\s*_ezchip_macro_read_value_\s*&=\s*~.+;\s*\\
\s*_ezchip_macro_read_value_\s*\|=\s*\((.+)&(.+)\)<<(.+);\s*\\
\s*MA_OUTW\(clk_(.+)_ctrl_REG_ADDR,_ezchip_macro_read_value_\);\s*\\
\}/s) {
my $value = hex($2);
$clks{$1}{parents}{$value} = { name => $src, mask => hex($3), shift => $4 };
} else {
die "couldn't parse $remainder\n";
}
}
if (/^#define\s+_ENABLE_CLOCK_clk_(.+)_\s*{\s*\\/) {
$clks{$1}{type} .= 'gate';
my $remainder = '';
while (<>) {
$remainder .= $_;
s/(\|=.*)\);/$1<<0/;
last if /\}/;
}
if ($remainder =~
/\s*uint32_t\s+_ezchip_macro_read_value_\s*=\s*MA_INW\(clk_(.+)_ctrl_REG_ADDR\);\s*\\
\s*_ezchip_macro_read_value_\s*&=\s*~.+;\s*\\
\s*_ezchip_macro_read_value_\s*\|=\s*\((.+)&(.+)\)<<(.+);\s*\\
\s*MA_OUTW\(clk_(.+)_ctrl_REG_ADDR,_ezchip_macro_read_value_\);\s*\\
\}/s) {
my $value = hex($2);
$clks{$1}{mask} = hex($3);
$clks{$1}{shift} = $4;
} else {
die "couldn't parse $remainder\n";
}
}
if (/^#define\s+_SET_CLOCK_clk_(.+)_POLARITY_\s*{\s*\\/) {
$clks{$1}{type} .= 'inverter';
my $remainder = '';
while (<>) {
$remainder .= $_;
s/(\|=.*)\);/$1<<0/;
last if /\}/;
}
if ($remainder =~
/\s*uint32_t\s+_ezchip_macro_read_value_\s*=\s*MA_INW\(clk_(.+)_ctrl_REG_ADDR\);\s*\\
\s*_ezchip_macro_read_value_\s*&=\s*~.+;\s*\\
\s*_ezchip_macro_read_value_\s*\|=\s*\((.+)&(.+)\)<<(.+);\s*\\
\s*MA_OUTW\(clk_(.+)_ctrl_REG_ADDR,_ezchip_macro_read_value_\);\s*\\
\}/s) {
my $value = hex($2);
$clks{$1}{mask} = hex($3);
$clks{$1}{shift} = $4;
} else {
die "couldn't parse $remainder\n";
}
}
if (/^#define\s+_DIVIDE_CLOCK_clk_(.+)_\s*\(div\)\s*{\s*\\/) {
$clks{$1}{type} .= 'divider';
my $remainder = '';
while (<>) {
s/(\|=.*)\);/$1\)<<0;/;
$remainder .= $_;
last if /\}/;
}
if ($remainder =~
/\s*uint32_t\s+_ezchip_macro_read_value_\s*=\s*MA_INW\(clk_(.+)_ctrl_REG_ADDR\);\s*\\
\s*_ezchip_macro_read_value_\s*&=\s*~.+;\s*\\
\s*_ezchip_macro_read_value_\s*\|=\s*\((.+)&(.+)\)<<(.+);\s*\\
\s*MA_OUTW\(clk_(.+)_ctrl_REG_ADDR,_ezchip_macro_read_value_\);\s*\\
\}/s) {
$clks{$1}{mask} = hex($3);
$clks{$1}{shift} = $4;
} else {
die "couldn't parse $remainder\n";
}
}
}
my @sorted_clk_names = sort { hex $clks{$a}{offset} <=> hex $clks{$b}{offset} } keys %clks;
my $i = 0;
for my $name (@sorted_clk_names) {
printf "#define EZCHIP_CLK_%s\t\t%u\n", uc($name), ++$i
}
print "\n\n\n";
for my $name (@sorted_clk_names) {
my $clk = $clks{$name};
my $max = 1;
my $last = 0;
my $parents = '';
my %shifts;
for my $parent (values(%{$clk->{parents}})) {
$max = $parent->{mask} > $max ? $parent->{mask} + 1 : $max;
$shifts{$parent->{shift}} = 1;
}
my $shifts = keys(%shifts);
next if $shifts == 0;
print "static const char \*${name}_sels\[$max\] = {\n";
die "// Invalid #shifts = $shifts ^" if $shifts != 1;
$clk->{shift} = @{[keys(%shifts)]}[0];
$clk->{mask} = $max;
for (my $i = 0; $i < $max; $i++) {
my $parent = %{$clk->{parents}}{$i};
print "\t[$i] = ";
if (not defined $parent) {
print "\"dummy\",\n";
} else {
printf "\"%s\",\n", $parent->{name};
}
}
print "}\n\n";
}
print "\n\n\n";
sub log2 { log(shift) / log(2) }
for my $name (@sorted_clk_names) {
my $clk = $clks{$name};
my $offset = $clk->{offset};
my $shift = $clk->{shift} // 0;
my $width = defined $clk->{mask} ? log2($clk->{mask} + 1) : 'UNKNOWN';
my $parent = "";
my $type = $clk->{type} // 'FIXME';
my @extra;
$type =~ s/gatedivider/gated_divider/;
my $idx = "EZCHIP_CLK_" . uc($name);
print "\tclks[EZCHIP_CLK_" . uc($name). qq{]\t\t= ezchip_clk_$type("$name",\t\t"$parent",\t\tbase + $offset, $shift, $width};
if ($clk->{type} eq 'mux') {
print ", ${name}_sels, ARRAY_SIZE(${name}_sels));";
} else {
}
print qq{);\n};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment