use XML::Simple; use FileHandle; use Getopt::Std; use Data::Dumper; use Storable qw( dclone ); use strict; use vars qw( $opt_n $opt_S $opt_I $opt_h $opt_d $opt_v $opt_r $opt_o $opt_t $opt_p); use vars qw( @ORIG_ARGS ); use vars qw( $debug ); $debug = 0; @ORIG_ARGS = @ARGV; getopts('n:S:I:hdvropt'); # n - Additional namepaces separated by commas # S - source file destination path # I - include file destination path # h - help # d - dump internal representation, then quit # v - validate register, then quit # p - include pretty print routines, e.g. toString() # t - not to make the register a BL_ThinFieldObj-derived class main(); #################################################################### #################################################################### #################################################################### sub print_debug { if( $debug ){ print @_; } } #################################################################### #################################################################### #################################################################### sub main { if( defined $opt_h ){ $0 =~ s#.*/##; print "usage: $0 [-h|d|v] [-I include path] [-S source path] [-n csv namespace list] \n"; exit 0; } for my $xml_file ( @ARGV ){ gen_regs( $xml_file ); } return 0; } #################################################################### #################################################################### #################################################################### sub get_xml_hash { my ( $xml_file, $leaf ) = @_; my $xs = XML::Simple->new(); my $xml_hash = $xs->XMLin( $xml_file ); # If the register has only one entry, fix it up. # This is a consequence of using the XML::Simple library. if( defined $xml_hash->{register}->{field}->{name} ){ # copy the single field to the single field's name. Then remove # all of the other key's using fix_hash. $xml_hash->{register}->{field}->{$xml_hash->{register}->{field}->{name}} = dclone( $xml_hash->{register}->{field} ); fix_hash( $xml_hash->{register} ); } if ( defined $xml_hash->{register}->{inherits} ){ my $parent_xml_hash; my $parent_file = $xml_hash->{register}->{inherits}; #$parent_file = $opt_S . "/" . $parent_file if defined $opt_S; $parent_xml_hash = get_xml_hash( $parent_file, 0 ); $xml_hash = merge_parent_child( $parent_xml_hash, $xml_hash, $leaf ); $xml_hash->{register}->{inheritance_info}->{other_ancestor} = 1; $xml_hash->{register}->{inheritance_info}->{ancestor_xml} = $parent_file; } elsif ( $leaf ) { for my $field_name ( keys (%{$xml_hash->{register}->{field}}) ){ $xml_hash->{register}->{field}->{$field_name}->{generate_data} = 1; } } if( $leaf && defined $xml_hash->{register}->{inheritance_info}->{other_ancestor} ){ my $inh_xml_file = $xml_hash->{register}->{inheritance_info}->{ancestor_xml}; my $inh_include_file = $inh_xml_file; #$inh_include_file =~ s/\.xml$/.h/; my $inh_class_name; if( $inh_xml_file =~ m#\b(\w[\w\d]*)\.xml$# ){ $inh_class_name = $1; $inh_include_file = $1.".h"; } else { die "$0: couldn't extract class name from \"$inh_xml_file\"\n"; } $xml_hash->{register}->{inheritance_info}->{include_file} = $inh_include_file; $xml_hash->{register}->{inheritance_info}->{ancestor_class} = $inh_class_name; } return $xml_hash; } #################################################################### #################################################################### #################################################################### sub location_match { my ( $p, $start_o, $end_o ) = @_; for my $field ( keys (%{$p->{field}}) ){ return $field if ( $p->{field}->{$field}->{start_offset} == $start_o && $p->{field}->{$field}->{end_offset} == $end_o ); } return -1; } #################################################################### #################################################################### #################################################################### sub merge_parent_child { my ( $parent_h, $child_h, $leaf ) = @_; my ( $p, $c ) = ( $parent_h->{register}, $child_h->{register} ); print_debug "Merging $p->{class_name} into $c->{class_name}\n"; my $old_class_name = $p->{class_name}; my $new_class_name = $c->{class_name}; # top-level informational tags $p->{class_name} = $c->{class_name}; $p->{name} = $c->{name} if defined $c->{name}; $p->{submodule} = $c->{submodule} if defined $c->{submodule}; $p->{priv} = $c->{priv} if defined $c->{priv}; $p->{comment} .= $c->{comment} if defined $c->{comment}; $p->{base_address} .= $c->{base_address} if defined $c->{base_address}; $p->{count} .= $c->{count} if defined $c->{count}; $p->{stride} .= $c->{stride} if defined $c->{stride}; delete $p->{public}; $p->{public} .= $c->{public} if defined $c->{public}; delete $p->{protected}; $p->{protected} .= $c->{protected} if defined $c->{protected}; delete $p->{private}; $p->{private} .= $c->{private} if defined $c->{private}; # copy any new aggregations that are in the child if( defined $c->{aggregate} ){ for my $agg ( keys (%{$c->{aggregate}}) ){ $p->{aggregate}->{$agg} = dclone( $c->{aggregate}->{$agg} ); } } # don't generate any fields by default. for my $field ( keys (%{$p->{field}}) ){ $p->{field}->{$field}->{generate_data} = 0; } for my $field ( keys (%{$c->{field}}) ){ my $field_name = location_match( $p, $c->{field}->{$field}->{start_offset}, $c->{field}->{$field}->{end_offset} ); # there's a field in the parent that matches the child's location if( $field_name != -1 ){ print_debug "Merging field '$field_name'\n"; if( defined $c->{field}->{$field}->{comment} ){ print_debug " Merged comment from $old_class_name.$field_name to $new_class_name.$field\n"; $p->{field}->{$field_name}->{comment} = $c->{field}->{$field}->{comment}; } if( defined $c->{field}->{$field}->{base_address} ){ print_debug " Merged base_address from $old_class_name.$field_name to $new_class_name.$field\n"; $p->{field}->{$field_name}->{base_address} = $c->{field}->{$field}->{base_address}; } if( defined $c->{field}->{$field}->{count} ){ print_debug " Merged count from $old_class_name.$field_name to $new_class_name.$field\n"; $p->{field}->{$field_name}->{count} = $c->{field}->{$field}->{count}; } if( defined $c->{field}->{$field}->{stride} ){ print_debug " Merged stride from $old_class_name.$field_name to $new_class_name.$field\n"; $p->{field}->{$field_name}->{stride} = $c->{field}->{$field}->{stride}; } if( defined $c->{field}->{$field}->{protection} ){ print_debug " Merged protection from $old_class_name.$field_name to $new_class_name.$field "; print_debug "(old = $p->{field}->{$field_name}->{protection}, new = $c->{field}->{$field}->{protection})\n"; $p->{field}->{$field_name}->{protection} = $c->{field}->{$field}->{protection}; } if( defined $c->{field}->{$field}->{field_type} ){ print_debug " Merged field_type from $old_class_name.$field_name to $new_class_name.$field "; print_debug "(old=$p->{field}->{$field_name}->{field_type} new=$c->{field}->{$field}->{field_type})\n"; $p->{field}->{$field_name}->{field_type} = $c->{field}->{$field}->{field_type} ; } if( defined $c->{field}->{$field}->{initial_value} ){ print_debug " Merged intial_value from $old_class_name.$field_name to $new_class_name.$field "; print_debug "(old=$p->{field}->{$field_name}->{initial_value} new=$c->{field}->{$field}->{initial_value})\n"; $p->{field}->{$field_name}->{initial_value} = $c->{field}->{$field}->{initial_value} ; } if( defined $c->{field}->{$field}->{format} ){ print_debug "Merged format from $old_class_name.$field_name to $new_class_name.$field\n"; $p->{field}->{$field_name}->{format} = dclone( $c->{field}->{$field}->{format} ); } # If this statement is true, then the field has been renamed in the child # Since we've merged everything into the parent, then we'll need clone the # parent's field (containing the merged data) and assign it to the new name. # Then we can remove copy under the old field name if( $field ne $field_name ){ $p->{field}->{$field} = dclone( $p->{field}->{$field_name} ); delete( $p->{field}->{$field_name} ); $p->{field}->{$field}->{generate_data} = $leaf; } } else { print_debug "Adding new field '$field' from child\n"; # no field matches child's location, so we add it. $p->{field}->{$field} = dclone( $c->{field}->{$field} ); $p->{field}->{$field}->{generate_data} = $leaf; } } return $parent_h; } #################################################################### #################################################################### #################################################################### sub gen_regs { my( $xml_file ) = @_; my $xml_hash = get_xml_hash( $xml_file, 1 ); if( defined $opt_d ){ print Dumper( $xml_hash ); my $register_ref = $xml_hash->{register}; print Dumper( $register_ref ); exit 0; } my $register_ref = $xml_hash->{register}; if( defined $register_ref->{name} ){ # single register gen_cpp( $xml_file, $register_ref->{name}, $register_ref ); } else { foreach my $reg_name ( keys (%{$register_ref}) ){ $register_ref->{$reg_name}->{name} = $reg_name; gen_cpp( $xml_file, $reg_name, $register_ref->{$reg_name} ); } } } #################################################################### #################################################################### #################################################################### sub gen_cpp { my ( $xml_file, $name, $register_ref ) = @_; my( $h_file_name, $cc_file_name ); calculate_sizes( $register_ref ); if( !validate( $register_ref ) ){ exit 1; } return if defined $opt_v; my $include_path = "."; my $source_path = "."; $include_path = $opt_I if defined $opt_I; $source_path = $opt_S if defined $opt_S; if( ! -d $include_path ){ system( "mkdir -p $include_path" ); } if( ! -d $source_path ){ system( "mkdir -p $source_path" ); } $h_file_name = xlate_path( $register_ref->{class_name}, $include_path, "h" ); $cc_file_name = xlate_path( $register_ref->{class_name}, $source_path, "cc" ); open DOT_H_FILE, ">$h_file_name" or die "couldn't create $h_file_name"; open DOT_CC_FILE, ">$cc_file_name" or die "couldn't create $cc_file_name" if !$opt_t; print "$xml_file -> $h_file_name\n"; write_include_file( *DOT_H_FILE, $xml_file, $register_ref, $register_ref->{name} ); if( !$opt_t ) { print "$xml_file -> $cc_file_name\n"; write_impl_file ( *DOT_CC_FILE, $xml_file, $register_ref, $register_ref->{name} ); } } #################################################################### #################################################################### #################################################################### sub calculate_sizes { my ( $reg_ref ) = @_; for my $field_name ( keys (%{$reg_ref->{field}} ) ){ $reg_ref->{field}->{$field_name}->{size} = $reg_ref->{field}->{$field_name}->{end_offset} - $reg_ref->{field}->{$field_name}->{start_offset} + 1; } } #################################################################### #################################################################### #################################################################### sub xlate_path { my ( $file, $path, $suffix ) = @_; my $new_file = $file; if( $path ne "" ){ $new_file = $path . "/" . $new_file; } return $new_file . "." . $suffix; } #################################################################### #################################################################### #################################################################### sub get_size { my( $reg_hash ) = @_; my $bits; for my $field_name ( keys (%{$reg_hash->{field}} ) ){ $bits += $reg_hash->{field}->{$field_name}->{size}; } return $bits; } #################################################################### #################################################################### #################################################################### sub get_native_type { my ( $nr_bits ) = @_; my %sz_hash = ( 8 => "uint8_t", 16 => "uint16_t", 32 => "uint32_t", 64 => "uint64_t" ); for my $k ( sort { $a <=> $b } keys %sz_hash ){ return ( $sz_hash{$k}, $k ) if $nr_bits <= $k; } print "ERROR: size $nr_bits cannot be represented in an integral type\n"; return ("ERROR", "ERROR"); } #################################################################### #################################################################### #################################################################### sub build_aggregate_include_string { my ( $reg_ref ) = @_; my $agg_ref = $reg_ref->{aggregate}; my $field_ref = $reg_ref->{field}; my $str; for my $agg ( keys (%{$agg_ref} ) ){ my $size = 0; for my $field ( keys (%{$agg_ref->{$agg}->{field}}) ){ $size += $field_ref->{$field}->{end_offset} - $field_ref->{$field}->{start_offset} + 1; } my ( $native_type, $bits ) = get_native_type( $size ); $str .= <{aggregate}; my $field_ref = $reg_ref->{field}; my $str; for my $agg ( keys (%{$agg_ref} ) ){ my $size = 0; my %field_data; for my $field ( keys (%{$agg_ref->{$agg}->{field}}) ){ my $sz = $field_ref->{$field}->{end_offset} - $field_ref->{$field}->{start_offset} + 1; $size += $sz; $field_data{$agg_ref->{$agg}->{field}->{$field}->{'pos'}}->{name} = $field; $field_data{$agg_ref->{$agg}->{field}->{$field}->{'pos'}}->{size} = $sz; } my ( $native_type, $bits ) = get_native_type( $size ); my ( $get_str, $set_str ) = ( "", "" ); my $offset = 0; for my $pos ( sort { $a <=> $b } keys %field_data ){ my $var_name = uc $field_data{$pos}->{name}; my $mask = (2 ** $field_data{$pos}->{size}) - 1; $mask = sprintf "0x0%lx", $mask; $get_str .= "(get$var_name() << $offset) |\n "; $set_str .= " set$var_name((____d >> $offset) & $mask);\n"; $offset += $field_data{$pos}->{size}; } $get_str .= "0"; $str .= <{field}} ) ){ $register_size += $reg_ref->{field}->{$field_name}->{size}; if( $reg_ref->{field}->{$field_name}->{field_type} eq "NORMAL" ){ push @tmpList, $field_name; } $fields{ $reg_ref->{field}->{$field_name}->{start_offset}} = $field_name; } while( ($fn = pop @tmpList) ){ my $ha_field_mask = "(~uint64_t(0) >> (64 - $reg_ref->{field}->{$fn}->{size}))"; my $ha_field_shft = "$reg_ref->{field}->{$fn}->{start_offset}"; if( defined $reg_ref->{field}->{$fn}->{initial_value} ){ $constructor_init .= " | (($reg_ref->{field}->{$fn}->{initial_value} & $ha_field_mask) << $ha_field_shft)\n"; } if( $reg_ref->{field}->{$fn}->{field_type} eq "NORMAL" ){ if( $reg_ref->{field}->{$fn}->{protection} eq "RW" ){ $constructor_mask .= " | ($ha_field_mask << $ha_field_shft)\n"; } if( $reg_ref->{field}->{$fn}->{protection} eq "RW1C" ){ $constructor_rw1c .= " | ($ha_field_mask << $ha_field_shft)\n"; } $constructor_bits .= " | ($ha_field_mask << $ha_field_shft)\n"; } } return ( $constructor_mask, $constructor_init, $constructor_bits, $constructor_rw1c ); } #################################################################### #################################################################### #################################################################### sub write_include_file { my( @save_args ) = @_; my( $file_handle, $xml_file, $reg_ref, $reg_name ) = @_; my $sz = 0; my $reg_name_uc = uc $reg_name; my $nr_fields = 0; my $class_name = $reg_ref->{class_name}; my $class_name_ref = "${class_name}&"; my $class_name_const_ref = "const ${class_name}&"; my ( $get_string, $set_string ); my $field_type_struct; my ( $extra_namespace_header, $extra_namespace_footer ); my $field_type_enum = "enum { "; my $field_string_array = "static const FieldObject::FieldInfo fieldData[] = {"; my %fields; my $agg_string; my $base_address; my $count; my $stride; my $public; my $protected; my $private; my $pretty_print_includes; my ( $constructor_mask, $constructor_init, $constructor_bits, $constructor_rw1c ) = &calc_constructors($reg_ref); my $constructor_list = "$constructor_mask, $constructor_init, $constructor_bits, $constructor_rw1c"; if (defined $reg_ref->{base_address}) { $base_address = "static const uint64_t BaseAddress = $reg_ref->{base_address};"; } if (defined $reg_ref->{count}) { $count = "static const uint64_t Count = $reg_ref->{count};"; } if (defined $reg_ref->{stride}) { $stride = "static const uint64_t Stride = $reg_ref->{stride};"; } if (defined $reg_ref->{public}) { $public = "$reg_ref->{public}"; } if (defined $reg_ref->{protected}) { $protected = "$reg_ref->{protected}"; } if (defined $reg_ref->{private}) { $private = "$reg_ref->{private}"; } $agg_string = build_aggregate_include_string( $reg_ref ) if defined $reg_ref->{aggregate}; # Get count of total register size and field count for my $field_name ( keys (%{$reg_ref->{field}} ) ){ # maps field start_offset to field name $fields{$reg_ref->{field}->{$field_name}->{start_offset}} = $field_name; $sz += $reg_ref->{field}->{$field_name}->{size}; ++$nr_fields; } my $field_count = $nr_fields; for my $o ( reverse sort { $a <=> $b } keys %fields ){ $field_count--; my $field_name = $fields{$o}; $field_type_enum .= uc $field_name; $field_type_enum .= ","; if($reg_ref->{field}->{$field_name}->{field_type} eq "NORMAL" ){ my ($data_type, $foo) = get_native_type( $reg_ref->{field}->{$field_name}->{size} ); # $field_type_struct .= " $data_type ". lc $field_name . ";\n" if $reg_ref->{field}->{$field_name}->{generate_data}; $get_string .= create_get_fn_decl( $reg_name, $field_name, $reg_ref->{field}->{$field_name}->{size}, $reg_ref->{field}->{$field_name}->{start_offset} ); $set_string .= create_set_fn_decl( $reg_name, $field_name, $reg_ref->{field}->{$field_name}->{size}, $reg_ref->{field}->{$field_name}->{start_offset}, $reg_ref->{field}->{$field_name}->{protection}); } $set_string .= create_bitsize_decl( $reg_name, $field_name, $reg_ref->{field}->{$field_name}->{size}, $reg_ref->{field}->{$field_name}->{start_offset}, $reg_ref->{field}->{$field_name}->{protection}); } $field_type_enum =~ s/,\s*$//; # chop off the comma from the last entry $field_type_enum .= " }"; my $include_guard = "RIESLING_" . uc $class_name . "_H"; my $comment_str = get_comment_string( $0, $xml_file ); foreach my $ns ( split /,/, $opt_n ){ $extra_namespace_header .= "namespace $ns {\n"; $extra_namespace_footer .= "} /* namespace $ns */\n"; } ## ## Frontend registration handling ## my $extra_ancestors; my $reg_declarations; my $to_string_fn = "std::string toString() const;"; my $set_native_fn = "void setNative( uint64_t value ) { FieldObject::setNative(value); }"; $set_native_fn = "void setNative( uint64_t value ) { BL_ThinFieldObj<$constructor_list>::setNative(value); }" if $opt_t; my $set_nativeHW_fn = "void setNativeHW( uint64_t value ) { FieldObject::setNativeHW(value); }"; $set_nativeHW_fn = "void setNativeHW( uint64_t value ) { BL_ThinFieldObj<$constructor_list>::setNativeHW(value); }" if $opt_t; my $get_native_fn = "uint64_t getNative() const { return FieldObject::getNative(); }"; $get_native_fn = "uint64_t getNative() const { return BL_ThinFieldObj<$constructor_list>::getNative(); }" if $opt_t; my $default_ctor = "$class_name();"; my $ancestor_include = $opt_t ? '#include "BL_ThinFieldObj.h"' : '#include "FieldObject.h"'; my $ancestor_class = "FieldObject"; $ancestor_class = "BL_ThinFieldObj<$constructor_list>" if $opt_t; if( defined $reg_ref->{inheritance_info}->{other_ancestor} ){ $ancestor_include = '#include "' . $reg_ref->{inheritance_info}->{include_file} . '"'; $ancestor_class = $reg_ref->{inheritance_info}->{ancestor_class}; # TODO This should really find out of RegisteredObject was already inherited by its # ancestor $extra_ancestors = ""; # base class aready has these so no need to duplicate/confuse people $set_native_fn = ""; $set_nativeHW_fn = ""; $get_native_fn = ""; } $pretty_print_includes = "#include \n#include \n" if $ opt_p; if ($opt_t) { print $file_handle < #include $extra_namespace_header EOHFILE1 ; } ################################# ## Produce html for doxygen ################################# print $file_handle "/**\n
\n";
print_field_table_doc( $file_handle, $reg_name_uc, \%fields, $reg_ref ); 
print $file_handle "\n
\n*/\n"; print $file_handle <{class_name} . ".h"; if( defined $reg_ref->{submodule} ){ $ifile_name = $reg_ref->{submodule} . "/" . $ifile_name; } my $class_name = $reg_ref->{class_name}; my $class_name_ref = "${class_name}&"; my $class_name_const_ref = "const ${class_name}&"; my $class_scope = $class_name . '::'; my $equal_string; my ( $get_string, $set_string ); my $printing_string; my $str_string; my @tmpList; my $fn; my ( $copy_constructor_string, $constructor_string ); my ( $constructor_mask, $constructor_init, $constructor_bits, $constructor_rw1c ) = &calc_constructors($reg_ref); my ( $get_native_string, $set_native_string, $set_nativeHW_string ); my $register_size = 0; my %fields; my $agg_string; $agg_string = build_aggregate_impl_string( $reg_ref, $class_name ) if defined $reg_ref->{aggregate}; my $field_string_array = "const FieldObject::FieldInfo ${class_scope}fieldData[] = {"; for my $field_name ( keys (%{$reg_ref->{field}} ) ){ $register_size += $reg_ref->{field}->{$field_name}->{size}; if( $reg_ref->{field}->{$field_name}->{field_type} eq "NORMAL" ){ push @tmpList, $field_name; } $fields{ $reg_ref->{field}->{$field_name}->{start_offset}} = $field_name; } for my $o ( reverse sort { $a <=> $b } keys %fields ){ my $field_name = $fields{$o}; if ( $reg_ref->{field}->{$field_name}->{protection} eq "RWH") { $field_string_array .= "\n { FieldObject::RO, "; } else { $field_string_array .= "\n { FieldObject::$reg_ref->{field}->{$field_name}->{protection}, "; } $field_string_array .= " $reg_ref->{field}->{$field_name}->{start_offset},"; $field_string_array .= " $reg_ref->{field}->{$field_name}->{size},"; $field_string_array .= " FieldObject::$reg_ref->{field}->{$field_name}->{field_type},"; $field_string_array .= " \"$field_name\" }"; $field_string_array .= ","; if( $reg_ref->{field}->{$field_name}->{field_type} eq "NORMAL" ){ # build impl for getNative method $get_native_string .= " |\n" if $get_native_string ne ""; $get_native_string .= " ( (0ULL |" . lc $field_name . ") << " . $reg_ref->{field}->{$field_name}->{start_offset} . ")"; # build impl for setNative method if( $reg_ref->{field}->{$field_name}->{protection} eq "RW" ){ my $mask = "(~0ULL >> (64 - $reg_ref->{field}->{$field_name}->{size}))"; $set_native_string .= " " . lc $field_name . " = (____d >> $reg_ref->{field}->{$field_name}->{start_offset}) & $mask;\n"; } elsif($reg_ref->{field}->{$field_name}->{protection} eq "RW1C"){ my $mask = "(~0ULL >> (64 - $reg_ref->{field}->{$field_name}->{size}))"; $set_native_string .= " " . lc $field_name . " &= ~((____d >> $reg_ref->{field}->{$field_name}->{start_offset}) & $mask);\n"; } # build impl for setNativeHW method if( $reg_ref->{field}->{$field_name}->{protection} ne "RO" ){ my $mask = "(~0ULL >> (64 - $reg_ref->{field}->{$field_name}->{size}))"; $set_nativeHW_string .= " " . lc $field_name . " = (____d >> $reg_ref->{field}->{$field_name}->{start_offset}) & $mask;\n"; } } } $field_string_array =~ s/,\s*$//; # chop off the comma from the last entry $field_string_array .= " }"; my ( $get_case_body, $set_case_body ); while( ($fn = pop @tmpList) ){ my $lc_fn = lc $fn; my $uc_fn = uc $fn; $equal_string .= " $lc_fn == rhs.$lc_fn"; $equal_string .= " &&\n" if @tmpList; $printing_string .= " \"" . uc( $fn ) . "=\" << get$uc_fn() << \" \""; $printing_string .= " <<\n" if @tmpList; $get_case_body .= < using namespace std; using namespace Riesling; $using_ns EO_CCFILE if ! $opt_t; if (! $opt_t) { print $file_handle <= NR_FIELDS ){ RIESLING_THROW_INVALID_ARGUMENT( "Invalid field specifier." ); } return ${class_scope}fieldData[ field ].size; } uint32_t ${class_scope}getOffset( uint_t field ) const { if( field >= NR_FIELDS ){ RIESLING_THROW_INVALID_ARGUMENT( "Invalid field specifier." ); } return ${class_scope}fieldData[ field ].offset; } FieldObject::Protection ${class_scope}getProtection( uint_t field ) const { if( field >= NR_FIELDS ){ RIESLING_THROW_INVALID_ARGUMENT( "Invalid field specifier." ); } return ${class_scope}fieldData[ field ].prot; } const char* ${class_scope}getName() const { return "$reg_ref->{name}"; } const char* ${class_scope}getName( uint_t field ) const { if( field >= NR_FIELDS ){ RIESLING_THROW_INVALID_ARGUMENT( "Invalid field specifier." ); } return ${class_scope}fieldData[ field ].name; } EO_CCFILE7 ; } print $file_handle < $b } keys %{$field_ref} ){ my ($FMT_offset,$FMT_size,$FMT_name, $FMT_description); my $field_name = $field_ref->{$o}; format COMMENT_INIT = @>> @>> @<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $FMT_offset,$FMT_size,$FMT_name, $FMT_description ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~ $FMT_description . ( $FMT_description, @comments ) = split /\\n/, $reg_ref->{field}->{$field_name}->{comment}; $FMT_offset = $reg_ref->{field}->{$field_name}->{start_offset}; $FMT_size = $reg_ref->{field}->{$field_name}->{size}; $FMT_name = $field_name; $file_handle->format_name( "COMMENT_INIT" ); write $file_handle; while( @comments ){ my $FMT_cont = shift @comments; format COMMENT_CONT = ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~ $FMT_cont . $file_handle->format_name( "COMMENT_CONT" ); write $file_handle; } } $~ = $orig_name; } #################################################################### #################################################################### #################################################################### sub fix_hash { my ( $reg_ref ) = @_; delete( $reg_ref->{field}->{field_type}); delete( $reg_ref->{field}->{name} ); delete( $reg_ref->{field}->{format} ); delete( $reg_ref->{field}->{comment} ); delete( $reg_ref->{field}->{start_offset} ); delete( $reg_ref->{field}->{end_offset} ); delete( $reg_ref->{field}->{protection} ); delete( $reg_ref->{field}->{size} ); delete( $reg_ref->{field}->{initial_value} ); } #################################################################### #################################################################### #################################################################### sub validate { my( $reg_ref ) = @_; my (%names, @bits); my $size = 0; my $max_offset = 0; for my $field_name ( keys (%{$reg_ref->{field}} ) ){ $size += $reg_ref->{field}->{$field_name}->{size}; $max_offset = $reg_ref->{field}->{$field_name}->{end_offset} if $max_offset < $reg_ref->{field}->{$field_name}->{end_offset}; } if( $size != $max_offset + 1 ){ print STDERR "VALIDATION ERROR: Max end_offset is $max_offset but is size is $size\n."; return 0; } for( my $i = 0; $i < $size; $i++ ){ $bits[$i] = 0; } for my $field_name ( keys (%{$reg_ref->{field}} ) ){ # Duplicated name check if( defined $names{ $field_name } ){ print STDERR "VALIDATION ERROR: duplicate field: $field_name\n"; return 0; } else { $names{$field_name} = 1; } if( $field_name =~ /^\W/ ){ print STDERR "VALIDATION ERROR: $field_name must start with a letter or and underscore.\n"; return 0; } if( $field_name =~ /([^\w\d])/ ){ print STDERR "VALIDATION ERROR: $field_name cannot contain the character $1.\n"; return 0; } # Check for offsets that are negative or too large. my $offset = $reg_ref->{field}->{$field_name}->{start_offset}; if( $offset < 0 ){ print STDERR "VALIDATION ERROR: $field_name.offset is negative\n"; return 0; } if( $offset >= $size ){ print STDERR "VALIDATION ERROR: $field_name.offset = $offset is greater than size = $size\n"; return 0; } my $field_size = $reg_ref->{field}->{$field_name}->{size}; # Check for correct size if( $field_size < 1 ){ print STDERR "VALIDATION ERROR: $field_name.size is less than one.\n"; return 0; } if( $offset + $field_size - 1 >= $size ){ print STDERR "VALIDATION ERROR: $field_name.offset ($offset) + $field_name.size ($field_size) is \ greater than size ($size)\n"; return 0; } # Check for overlapping bits for( my $i = $offset; $i < ($offset + $field_size); $i++ ){ if( $bits[$i] != 0 ){ print STDERR "VALIDATION ERROR: $field_name overlaps with $bits[$i] at bit $i\n"; return 0; } else { $bits[$i] = $field_name; } } # Initial value check. my $initial_value = $reg_ref->{field}->{$field_name}->{initial_value}; my $max_size = ( 2 ** $size ) - 1; if( $initial_value > $max_size ){ print STDERR "WARNING: $field_name.intial_value is greater than maximum representable value for\n"; print STDERR " $field_name. This value will be truncated.\n"; $reg_ref->{field}->{$field_name}->{initial_value} &= $max_size; } my $prot = $reg_ref->{field}->{$field_name}->{protection}; if( $prot ne "RO" && $prot ne "RW" && $prot ne "WO" && $prot ne "RWH" && $prot ne "RW1C"){ # RWH is used to indicate a field that is read-only by software, # but is writable by hardware, we need this to create setNativeHW, # to be used by (N2) testbench to force a value into an otherwise # read-only field. print STDERR "VALIDATION ERROR: $field_name.protection is not RO, RW, or WO [$prot]\n"; return 0; } if ($prot eq "RWH"){ print STDERR "IMPLEMENTATION CHANGE: RWH behaviour is available through setNativeHW which treats RO fields as RW\n"; } my $type = $reg_ref->{field}->{$field_name}->{field_type}; if( $type ne "NORMAL" && $type ne "DONT_CARE" && $type ne "ZERO" ){ print STDERR "VALIDATION ERROR: $field_name.field_type is not NORMAL, DONT_CARE, or ZERO.\n"; return 0; } my $comment = $reg_ref->{field}->{$field_name}->{comment}; if( $type ne "NORMAL" && $comment != /\w/ ){ print STDERR "VALIDATION WARNING: $field_name.comment appears to be blank\n"; } } my @uncounted_bits; for( my $i = 0; $i < $size; $i++ ){ if( $bits[$i] eq "0" ){ push @uncounted_bits, $i; } } if( @uncounted_bits > 0 ){ print STDERR "VALIDATION ERROR: Bits @uncounted_bits are not part of any field.\n"; return 0; } return 1; } __END__ #################################################################### #################################################################### #################################################################### # POD documentation =head1 NAME xml2reg - Build a C++ implementation of a register from an XML specification. =head1 SYNOPSYS xml2reg [-hdrv] [ -n namespaces] [ -I path] [ -S path ] xml file ... =head1 DESCRIPTION xml2reg takes an XML file containing a specification for a register as input and generates the corresponding C++ implemenation of that specification. All xml2reg-generated registers implement the FieldObject interface. xml2reg documents the C++ files with appropriate doxygen-compatible comments. =head1 OPTIONS =item B<-h> Print a usage summary. =item B<-d> Dump out the internal represenation (parse tree) of the XML file. This option is useful for developers who wish to modify xml2reg. =item B<-v> Validate the XML file and then exit without generating any C++ output. This option is used to ensure the correctness of the your XML register file specification. xml2reg performs a number of sanity checks on your XML file. When an error in your XML file is discovered, xml2reg will report the error to stderr an then exit with a nonzero status. Some problems with your XML will generate warnings. A warning will be reported to stderr, but will not cause xml2reg to exit. xml2reg will exit with zero status if your file has no errors but does generate warnings. =item B<-n> I Enclose the C++ implemenation in additional namespaces. By default, the C++ code generated by xml2reg is placed in the Riesling namespace. This option is used to specify additional enclosing namespaces. The namespaces are specified as a comma-separated list of namespaces with no intervening whitespace. The the namespaces should be specified from in outermost to innermost order. =item B<-r> Generate code to make this register accessble from the object registry. Classes generated with this can be instantiated and their instances manipulated Riesling's frontend. =item B<-I> I Specify the directory to which the generated header file (.h file) should be written. =item B<-S> I Specify the directory to which the generated implemenation file (.cc file) should be written. =head1 EXAMPLES xml2reg myreg.xml Generate C++ code for a single xml file. The resulting C++ files will be names myreg.h and myreg.cc. The files will be written to the current working directory. xml2reg *.xml Process all the XML files in the current directory. A .h and .cc file will be generated for each XML file processed. xml2reg -n Mmu -I /src/Riesling/include/mmu -S /src/Riesling/src/mmu MmuConfigReg.xml Generates C++ code for the XML file MmuConfigReg.xml. The the class is enclosed in the Mmu namespace. The .h file is written to /src/Riesling/include/mmu/MmuConfigReg.h. The .cc file is written to /src/Riesling/include/mmu/MmuConfigReg.cc. =head1 SEE ALSO XML::Simple, FileHandle, process_xml(1) =head1 BUGS Errors parsing the XML file will be reported by XML::Simple, and these errors can be cryptic. =head1 NOTES See the document I for a complete discussion of the XML specification.