| #!/usr/bin/perl -w |
| # SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause |
| # Copyright (C) 2019--2020 Intel Corporation |
| |
| use Getopt::Long qw(:config no_ignore_case); |
| use File::Basename; |
| |
| my $ccsregs = "ccs-regs.asc"; |
| my $header; |
| my $regarray; |
| my $limitc; |
| my $limith; |
| my $kernel; |
| my $help; |
| |
| GetOptions("ccsregs|c=s" => \$ccsregs, |
| "header|e=s" => \$header, |
| "regarray|r=s" => \$regarray, |
| "limitc|l=s" => \$limitc, |
| "limith|L=s" => \$limith, |
| "kernel|k" => \$kernel, |
| "help|h" => \$help) or die "can't parse options"; |
| |
| $help = 1 if ! defined $header || ! defined $limitc || ! defined $limith; |
| |
| if (defined $help) { |
| print <<EOH |
| $0 - Create CCS register definitions for C |
| |
| usage: $0 -c ccs-regs.asc -e header -r regarray -l limit-c -L limit-header [-k] |
| |
| -c ccs register file |
| -e header file name |
| -r register description array file name |
| -l limit and capability array file name |
| -L limit and capability header file name |
| -k generate files for kernel space consumption |
| EOH |
| ; |
| exit 0; |
| } |
| |
| my $lh_hdr = ! defined $kernel |
| ? '#include "ccs-os.h"' . "\n" |
| : "#include <linux/bits.h>\n#include <linux/types.h>\n"; |
| my $uint32_t = ! defined $kernel ? 'uint32_t' : 'u32'; |
| my $uint16_t = ! defined $kernel ? 'uint16_t' : 'u16'; |
| |
| open(my $R, "< $ccsregs") or die "can't open $ccsregs"; |
| |
| open(my $H, "> $header") or die "can't open $header"; |
| my $A; |
| if (defined $regarray) { |
| open($A, "> $regarray") or die "can't open $regarray"; |
| } |
| open(my $LC, "> $limitc") or die "can't open $limitc"; |
| open(my $LH, "> $limith") or die "can't open $limith"; |
| |
| my %this; |
| |
| sub is_limit_reg($) { |
| my $addr = hex $_[0]; |
| |
| return 0 if $addr < 0x40; # weed out status registers |
| return 0 if $addr >= 0x100 && $addr < 0xfff; # weed out configuration registers |
| |
| return 1; |
| } |
| |
| my $uc_header = basename uc $header; |
| $uc_header =~ s/[^A-Z0-9]/_/g; |
| |
| my $copyright = "/* Copyright (C) 2019--2020 Intel Corporation */\n"; |
| my $license = "SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause"; |
| |
| for my $fh ($A, $LC) { |
| print $fh "// $license\n$copyright\n" if defined $fh; |
| } |
| |
| for my $fh ($H, $LH) { |
| print $fh "/* $license */\n$copyright\n"; |
| } |
| |
| sub bit_def($) { |
| my $bit = shift @_; |
| |
| return "BIT($bit)" if defined $kernel; |
| return "(1U << $bit)" if $bit =~ /^[a-zA-Z0-9_]+$/; |
| return "(1U << ($bit))"; |
| } |
| |
| print $H <<EOF |
| #ifndef __${uc_header}__ |
| #define __${uc_header}__ |
| |
| EOF |
| ; |
| |
| print $H "#include <linux/bits.h>\n\n" if defined $kernel; |
| |
| print $H <<EOF |
| #define CCS_FL_BASE 16 |
| EOF |
| ; |
| |
| print $H "#define CCS_FL_16BIT " . bit_def("CCS_FL_BASE") . "\n"; |
| print $H "#define CCS_FL_32BIT " . bit_def("CCS_FL_BASE + 1") . "\n"; |
| print $H "#define CCS_FL_FLOAT_IREAL " . bit_def("CCS_FL_BASE + 2") . "\n"; |
| print $H "#define CCS_FL_IREAL " . bit_def("CCS_FL_BASE + 3") . "\n"; |
| |
| print $H <<EOF |
| #define CCS_R_ADDR(r) ((r) & 0xffff) |
| |
| EOF |
| ; |
| |
| print $A <<EOF |
| #include <stdint.h> |
| #include <stdio.h> |
| #include "ccs-extra.h" |
| #include "ccs-regs.h" |
| |
| EOF |
| if defined $A; |
| |
| my $uc_limith = basename uc $limith; |
| $uc_limith =~ s/[^A-Z0-9]/_/g; |
| |
| print $LH <<EOF |
| #ifndef __${uc_limith}__ |
| #define __${uc_limith}__ |
| |
| $lh_hdr |
| struct ccs_limit { |
| $uint32_t reg; |
| $uint16_t size; |
| $uint16_t flags; |
| const char *name; |
| }; |
| |
| EOF |
| ; |
| print $LH "#define CCS_L_FL_SAME_REG " . bit_def(0) . "\n\n"; |
| |
| print $LH <<EOF |
| extern const struct ccs_limit ccs_limits[]; |
| |
| EOF |
| ; |
| |
| print $LC <<EOF |
| #include "ccs-limits.h" |
| #include "ccs-regs.h" |
| |
| const struct ccs_limit ccs_limits[] = { |
| EOF |
| ; |
| |
| my $limitcount = 0; |
| my $argdescs; |
| my $reglist = "const struct ccs_reg_desc ccs_reg_desc[] = {\n"; |
| |
| sub name_split($$) { |
| my ($name, $addr) = @_; |
| my $args; |
| |
| $name =~ /([^\(]+?)(\(.*)/; |
| ($name, $args) = ($1, $2); |
| $args = [split /,\s*/, $args]; |
| foreach my $t (@$args) { |
| $t =~ s/[\(\)]//g; |
| $t =~ s/\//\\\//g; |
| } |
| |
| return ($name, $addr, $args); |
| } |
| |
| sub tabconv($) { |
| $_ = shift; |
| |
| my @l = split "\n", $_; |
| |
| map { |
| s/ {8,8}/\t/g; |
| s/\t\K +//; |
| } @l; |
| |
| return (join "\n", @l) . "\n"; |
| } |
| |
| sub elem_size(@) { |
| my @flags = @_; |
| |
| return 2 if grep /^16$/, @flags; |
| return 4 if grep /^32$/, @flags; |
| return 1; |
| } |
| |
| sub arr_size($) { |
| my $this = $_[0]; |
| my $size = $this->{elsize}; |
| my $h = $this->{argparams}; |
| |
| foreach my $arg (@{$this->{args}}) { |
| my $apref = $h->{$arg}; |
| |
| $size *= $apref->{max} - $apref->{min} + 1; |
| } |
| |
| return $size; |
| } |
| |
| sub print_args($$$) { |
| my ($this, $postfix, $is_same_reg) = @_; |
| my ($args, $argparams, $name) = |
| ($this->{args}, $this->{argparams}, $this->{name}); |
| my $varname = "ccs_reg_arg_" . (lc $name) . $postfix; |
| my @mins; |
| my @sorted_args = @{$this->{sorted_args}}; |
| my $lim_arg; |
| my $size = arr_size($this); |
| |
| $argdescs .= "static const struct ccs_reg_arg " . $varname . "[] = {\n"; |
| |
| foreach my $sorted_arg (@sorted_args) { |
| push @mins, $argparams->{$sorted_arg}->{min}; |
| } |
| |
| foreach my $sorted_arg (@sorted_args) { |
| my $h = $argparams->{$sorted_arg}; |
| |
| $argdescs .= "\t{ \"$sorted_arg\", $h->{min}, $h->{max}, $h->{elsize} },\n"; |
| |
| $lim_arg .= defined $lim_arg ? ", $h->{min}" : "$h->{min}"; |
| } |
| |
| $argdescs .= "};\n\n"; |
| |
| $reglist .= "\t{ CCS_R_" . (uc $name) . "(" . (join ",", (@mins)) . |
| "), $size, sizeof($varname) / sizeof(*$varname)," . |
| " \"" . (lc $name) . "\", $varname },\n"; |
| |
| print $LC tabconv sprintf "\t{ CCS_R_" . (uc $name) . "($lim_arg), " . |
| $size . ", " . ($is_same_reg ? "CCS_L_FL_SAME_REG" : "0") . |
| ", \"$name" . (defined $this->{discontig} ? " $lim_arg" : "") . "\" },\n" |
| if is_limit_reg $this->{base_addr}; |
| } |
| |
| my $hdr_data; |
| |
| while (<$R>) { |
| chop; |
| s/^\s*//; |
| next if /^[#;]/ || /^$/; |
| if (s/^-\s*//) { |
| if (s/^b\s*//) { |
| my ($bit, $addr) = split /\t+/; |
| $bit = uc $bit; |
| $hdr_data .= sprintf "#define %-62s %s", "CCS_" . (uc ${this{name}}) ."_$bit", bit_def($addr) . "\n"; |
| } elsif (s/^f\s*//) { |
| s/[,\.-]/_/g; |
| my @a = split /\s+/; |
| my ($msb, $lsb, $this_field) = reverse @a; |
| @a = ( { "name" => "SHIFT", "addr" => $lsb, "fmt" => "%uU", }, |
| { "name" => "MASK", "addr" => (1 << ($msb + 1)) - 1 - ((1 << $lsb) - 1), "fmt" => "0x%" . join(".", ($this{"elsize"} >> 2) x 2) . "x" } ); |
| $this{"field"} = $this_field; |
| foreach my $ar (@a) { |
| #print $ar->{fmt}."\n"; |
| $hdr_data .= sprintf "#define %-62s " . $ar->{"fmt"} . "\n", "CCS_" . (uc $this{"name"}) . (defined $this_field ? "_" . uc $this_field : "") . "_" . $ar->{"name"}, $ar->{"addr"} . "\n"; |
| } |
| } elsif (s/^e\s*//) { |
| s/[,\.-]/_/g; |
| my ($enum, $addr) = split /\s+/; |
| $enum = uc $enum; |
| $hdr_data .= sprintf "#define %-62s %s", "CCS_" . (uc ${this{name}}) . (defined $this{"field"} ? "_" . uc $this{"field"} : "") ."_$enum", $addr . ($addr =~ /0x/i ? "" : "U") . "\n"; |
| } elsif (s/^l\s*//) { |
| my ($arg, $min, $max, $elsize, @discontig) = split /\s+/; |
| my $size; |
| |
| foreach my $num ($min, $max) { |
| $num = hex $num if $num =~ /0x/i; |
| } |
| |
| $hdr_data .= sprintf "#define %-62s %s", "CCS_LIM_" . (uc ${this{name}} . "_MIN_$arg"), $min . ($min =~ /0x/i ? "" : "U") . "\n"; |
| $hdr_data .= sprintf "#define %-62s %s", "CCS_LIM_" . (uc ${this{name}} . "_MAX_$arg"), $max . ($max =~ /0x/i ? "" : "U") . "\n"; |
| |
| my $h = $this{argparams}; |
| |
| $h->{$arg} = { "min" => $min, |
| "max" => $max, |
| "elsize" => $elsize =~ /^0x/ ? hex $elsize : $elsize, |
| "discontig" => \@discontig }; |
| |
| $this{discontig} = $arg if @discontig; |
| |
| next if $#{$this{args}} + 1 != scalar keys %{$this{argparams}}; |
| |
| my $reg_formula = "($this{addr}"; |
| my $lim_formula; |
| |
| foreach my $arg (@{$this{args}}) { |
| my $d = $h->{$arg}->{discontig}; |
| my $times = $h->{$arg}->{elsize} != 1 ? |
| " * " . $h->{$arg}->{elsize} : ""; |
| |
| if (@$d) { |
| my ($lim, $offset) = split /,/, $d->[0]; |
| |
| $reg_formula .= " + (($arg) < $lim ? ($arg)$times : $offset + (($arg) - $lim)$times)"; |
| } else { |
| $reg_formula .= " + ($arg)$times"; |
| } |
| |
| $lim_formula .= (defined $lim_formula ? " + " : "") . "($arg)$times"; |
| } |
| |
| $reg_formula .= ")\n"; |
| $lim_formula =~ s/^\(([a-z0-9]+)\)$/$1/i; |
| |
| print $H tabconv sprintf("#define %-62s %s", "CCS_R_" . (uc $this{name}) . |
| $this{arglist}, $reg_formula); |
| |
| print $H tabconv $hdr_data; |
| undef $hdr_data; |
| |
| # Sort arguments in descending order by size |
| @{$this{sorted_args}} = sort { |
| $h->{$a}->{elsize} <= $h->{$b}->{elsize} |
| } @{$this{args}}; |
| |
| if (defined $this{discontig}) { |
| my $da = $this{argparams}->{$this{discontig}}; |
| my ($first_discontig) = split /,/, $da->{discontig}->[0]; |
| my $max = $da->{max}; |
| |
| $da->{max} = $first_discontig - 1; |
| print_args(\%this, "", 0); |
| |
| $da->{min} = $da->{max} + 1; |
| $da->{max} = $max; |
| print_args(\%this, $first_discontig, 1); |
| } else { |
| print_args(\%this, "", 0); |
| } |
| |
| next unless is_limit_reg $this{base_addr}; |
| |
| print $LH tabconv sprintf "#define %-63s%s\n", |
| "CCS_L_" . (uc $this{name}) . "_OFFSET(" . |
| (join ", ", @{$this{args}}) . ")", "($lim_formula)"; |
| } |
| |
| if (! @{$this{args}}) { |
| print $H tabconv($hdr_data); |
| undef $hdr_data; |
| } |
| |
| next; |
| } |
| |
| my ($name, $addr, @flags) = split /\t+/, $_; |
| my $args = []; |
| |
| my $sp; |
| |
| ($name, $addr, $args) = name_split($name, $addr) if /\(.*\)/; |
| |
| $name =~ s/[,\.-]/_/g; |
| |
| my $flagstring = ""; |
| my $size = elem_size(@flags); |
| $flagstring .= "| CCS_FL_16BIT " if $size eq "2"; |
| $flagstring .= "| CCS_FL_32BIT " if $size eq "4"; |
| $flagstring .= "| CCS_FL_FLOAT_IREAL " if grep /^float_ireal$/, @flags; |
| $flagstring .= "| CCS_FL_IREAL " if grep /^ireal$/, @flags; |
| $flagstring =~ s/^\| //; |
| $flagstring =~ s/ $//; |
| $flagstring = "($flagstring)" if $flagstring =~ /\|/; |
| my $base_addr = $addr; |
| $addr = "($addr | $flagstring)" if $flagstring ne ""; |
| |
| my $arglist = @$args ? "(" . (join ", ", @$args) . ")" : ""; |
| $hdr_data .= sprintf "#define %-62s %s\n", "CCS_R_" . (uc $name), $addr |
| if !@$args; |
| |
| $name =~ s/\(.*//; |
| |
| %this = ( name => $name, |
| addr => $addr, |
| base_addr => $base_addr, |
| argparams => {}, |
| args => $args, |
| arglist => $arglist, |
| elsize => $size, |
| ); |
| |
| if (!@$args) { |
| $reglist .= "\t{ CCS_R_" . (uc $name) . ", 1, 0, \"" . (lc $name) . "\", NULL },\n"; |
| print $H tabconv $hdr_data; |
| undef $hdr_data; |
| |
| print $LC tabconv sprintf "\t{ CCS_R_" . (uc $name) . ", " . |
| $this{elsize} . ", 0, \"$name\" },\n" |
| if is_limit_reg $this{base_addr}; |
| } |
| |
| print $LH tabconv sprintf "#define %-63s%s\n", |
| "CCS_L_" . (uc $this{name}), $limitcount++ |
| if is_limit_reg $this{base_addr}; |
| } |
| |
| if (defined $A) { |
| print $A $argdescs, $reglist; |
| |
| print $A "\t{ 0 }\n"; |
| |
| print $A "};\n"; |
| } |
| |
| print $H "\n#endif /* __${uc_header}__ */\n"; |
| |
| print $LH tabconv sprintf "#define %-63s%s\n", "CCS_L_LAST", $limitcount; |
| |
| print $LH "\n#endif /* __${uc_limith}__ */\n"; |
| |
| print $LC "\t{ 0 } /* Guardian */\n"; |
| print $LC "};\n"; |
| |
| close($R); |
| close($H); |
| close($A) if defined $A; |
| close($LC); |
| close($LH); |