Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / cpus / vonk / n2 / lib / ras / bin / xml2reg
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] <xml file names>\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 .= <<EOTEXT
$native_type get${agg}();
void set${agg}( $native_type );
EOTEXT
;
}
return $str;
}
####################################################################
####################################################################
####################################################################
sub build_aggregate_impl_string
{
my ( $reg_ref, $class_name ) = @_;
my $agg_ref = $reg_ref->{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 .= <<EOTEXT
$native_type ${class_name}::get${agg}()
{
return $get_str;
}
void ${class_name}::set${agg}( $native_type ____d )
{
$set_str
}
EOTEXT
;
}
return $str;
}
####################################################################
####################################################################
####################################################################
sub calc_constructors
{
my( $reg_ref ) = @_;
my ( $constructor_mask, $constructor_init, $constructor_bits,
$constructor_rw1c ) = ( "0\n", "0\n", "0\n", "0\n" );
my $register_size = 0;
my @tmpList;
my $fn;
my %fields;
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;
}
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 <iostream>\n#include <string>\n" if $ opt_p;
if ($opt_t) {
print $file_handle <<EOHFILE0
#ifndef $include_guard
#define $include_guard
$comment_str
$pretty_print_includes
#include "BL_ThinFieldObj.h"
$ancestor_include
$extra_namespace_header
EOHFILE0
;
}
else {
print $file_handle <<EOHFILE1
#ifndef $include_guard
#define $include_guard
$comment_str
#include "DataTypes.h"
$ancestor_include
#include "StdMacros.h"
#include "Interface/RegisteredObject.h"
#include <iostream>
#include <string>
$extra_namespace_header
EOHFILE1
;
}
#################################
## Produce html for doxygen
#################################
print $file_handle "/**\n<pre>\n";
print_field_table_doc( $file_handle, $reg_name_uc, \%fields, $reg_ref );
print $file_handle "\n</pre>\n*/\n";
print $file_handle <<EOHFILE1
class $class_name : public $ancestor_class $extra_ancestors {
EOHFILE1
;
print $file_handle <<EOHFILE4
public:
$base_address
$count
$stride
$public
EOHFILE4
;
if (! $opt_t) {
print $file_handle <<EOHFILE5
/**
* An enumeration that represent each accessable field. Use these types
* as inputs for accessors and mutators for fields.
*/
typedef $field_type_enum FieldT;
EOHFILE5
;
}
print $file_handle <<EOHFILE6
$default_ctor
$class_name( $class_name_const_ref obj );
~$class_name();
EOHFILE6
if ! $opt_t;
print $file_handle <<EOHFILE60
$class_name_const_ref operator=( $class_name_const_ref obj ) { return ($class_name_const_ref)$ancestor_class ::operator=(obj); }
bool operator==( $class_name_const_ref obj ) const { return $ancestor_class ::operator==(obj); }
/**
* Get a copy of the register's native representation
*
* \@param data A pointer to a chunk of memory large enough
* to hold the register's native representaion
* \@see getSize()
*/
$get_native_fn
$set_native_fn
$set_nativeHW_fn
EOHFILE60
;
if (! $opt_t) {
print $file_handle <<EOHFILE7
/**
* Return the register's size in bits
*
* \@return The register size in bits
* \@see ${class_name}::FieldT
*/
uint32_t getSize() const;
/**
* Return the field's size in bits
*
* \@param field The number of this field
* \@return The field's size in bits
* \@see ${class_name}::FieldT
*/
virtual uint32_t getSize( uint_t field ) const;
/**
* Return the field's offset in the register
*
* \@param field The number of this field
* \@return The field's offset
* \@see ${class_name}::FieldT
*/
virtual uint32_t getOffset( uint_t field ) const;
/**
* Return the field's protection. That is, can the field be
* read, written, or both.
*
* \@param field The number of this field
* \@return The field's protection
* \@see FieldObject::ProtectionT
* \@see ${class_name}::FieldT
*/
FieldObject::Protection getProtection( uint_t field ) const;
/**
* Return a string representation of the field name
*
* \@param field The number of this field
* \@return The name of the field
* \@see ${class_name}::FieldT
*/
virtual const char* getName() const;
/**
* Return a string representation of the field name
*
* \@param field The number of this field
* \@return The name of the field
* \@see ${class_name}::FieldT
*/
virtual const char* getName( uint_t field ) const;
EOHFILE7
;
}
print $file_handle <<EOHFILE80
$reg_declarations
$get_string
$set_string
$agg_string
EOHFILE80
;
if ( $opt_t ) {
my( $file_handle, $xml_file, $reg_ref, $reg_name ) = @save_args;
write_impl_file( $file_handle, $xml_file, $reg_ref, $reg_name );
}
else {
print $file_handle <<EOHFILE8
/**
* Return the string representaton of this register.
*
*/
$to_string_fn
/**
* Print this Register to an output stream
*
* \@param os The output stream to which the register should be
* printed. Omitting this parameter will print the std::cout
*/
virtual void print( std::ostream& os = std::cout ) const;
EOHFILE8
;
}
print $file_handle <<EOHFILE85
protected:
$protected
EOHFILE85
;
if (! $opt_t) {
print $file_handle <<EOHFILE9
//! How many fields the register has.
static const int NR_FIELDS = $nr_fields;
static const FieldObject::FieldInfo fieldData[];
EOHFILE9
;
}
print $file_handle <<EOHFILE10
private:
};
$extra_namespace_footer
#endif /* $include_guard */
EOHFILE10
;
print $file_handle "\n";
}
####################################################################
####################################################################
####################################################################
sub write_impl_file
{
my( $file_handle, $xml_file, $reg_ref, $reg_name ) = @_;
my $ifile_name = $reg_ref->{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 .=
<<EO_GET_CASE
case $uc_fn:
return get$uc_fn();
break;
EO_GET_CASE
;
$set_case_body .=
<<EO_SET_CASE
case $uc_fn:
set$uc_fn( value );
break;
EO_SET_CASE
;
}
my $comment_str = get_comment_string( $0, $xml_file );
# construct using declarations for each additional namespace
my $using_ns;
my $extra_ns_accum = "Riesling";
foreach my $ns ( split /,/, $opt_n ){
$extra_ns_accum .= "::$ns";
$using_ns .= "using namespace $extra_ns_accum;\n";
}
print $file_handle <<EO_CCFILE
$comment_str
#include "$ifile_name"
#include <sstream>
using namespace std;
using namespace Riesling;
$using_ns
EO_CCFILE
if ! $opt_t;
if (! $opt_t) {
print $file_handle <<EO_CCFILE1
const int ${class_scope}NR_FIELDS;
$field_string_array;
EO_CCFILE1
;
}
if ($opt_t) {
print $file_handle <<EO_CCFILE6
$class_name()
{
data = get_init();
}
~$class_name()
{}
$class_name( const $class_name& obj )
{
data = obj.data;
}
EO_CCFILE6
}
else {
print $file_handle <<EO_CCFILE6
${class_scope}$class_name()
{
mask = $constructor_mask ;
init = $constructor_init ;
bits = $constructor_bits ;
rw1c = $constructor_rw1c ;
data = init;
}
${class_scope}~$class_name()
{}
${class_scope}$class_name( const $class_name& obj )
{
data = obj.data;
mask = obj.mask;
rw1c = obj.rw1c;
init = obj.init;
bits = obj.bits;
}
EO_CCFILE6
;
}
if (! $opt_t) {
print $file_handle <<EO_CCFILE7
$get_string
$set_string
uint32_t ${class_scope}getSize() const
{
return $register_size;
}
uint32_t ${class_scope}getSize( uint_t field ) const
{
if( field >= 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 <<EO_CCFILE8
std::string ${class_scope}toString() const
{
std::ostringstream os;
os << std::hex << $printing_string;
return os.str();
}
void ${class_scope}print( std::ostream& os ) const
{
os << toString();
}
EO_CCFILE8
if $opt_p;
print $file_handle "$agg_string\n;";
}
####################################################################
####################################################################
####################################################################
sub create_set_fn_decl
{
my ( $class_name, $field_name, $field_size, $field_start, $prot) = @_;
my ( $sz_type, $sz ) = get_native_type( $field_size );
my $field_name_uc = uc $field_name;
my $field_name_lc = lc $field_name;
my $rvalue = "(value << $field_start)";
my $mask = "uint64_t field_mask = (~uint64_t(0) >> (64 - $field_size)) << $field_start;";
$rvalue = "(data & ~field_mask) | ((value << $field_start) & field_mask)";
## This is a little painful. Long time ago Riesling decided to make itself public, very public
## and that means that all registers are visible and accessable, including the set field methods.
## This is a grieving mistake. It should have been hidden, awell, now we have a problem ...
## If someone sets a field of a register and internally there is some side effect associated with that
## then we need to call a function or method that handles the side effect ... thank god for pointers
## to functions or methods ha style. All because the rshell allows for statements like
## $hpstate.HPRIV=1 ... and that needs a call ... and I don't know what the jshell does.
my $decl_string = "void set$field_name_uc( uint64_t value ) { $mask data=$rvalue; }\n";
$decl_string .= "void set${field_name_uc}_Notify( uint64_t value ) { $mask call->call(this,$rvalue); }" if ! $opt_t;
if( $opt_o ){
$decl_string = "virtual $decl_string";
}
<<EODECL
//! set the value of $class_name.$field_name_uc
$decl_string
EODECL
;
}
####################################################################
####################################################################
####################################################################
sub create_get_fn_decl
{
my ( $class_name, $field_name, $field_size, $field_start ) = @_;
my ( $sz_type, $sz ) = get_native_type( $field_size );
my $field_name_uc = uc $field_name;
my $field_name_lc = lc $field_name;
my $decl_string = "uint64_t get$field_name_uc( ) const { return (data >> $field_start) & (~uint64_t(0) >> (64 - $field_size));}";
if( $opt_o ){
$decl_string = "virtual $decl_string";
}
<<EODECL
//! return the value of $class_name.$field_name_uc
$decl_string
EODECL
;
}
####################################################################
####################################################################
####################################################################
sub create_bitsize_decl
{
my ( $class_name, $field_name, $field_size, $field_start ) = @_;
my ( $sz_type, $sz ) = get_native_type( $field_size );
my $field_name_uc = uc $field_name;
my $field_name_lc = lc $field_name;
my $decl_string = "static const bitSize$field_name_uc = $field_size;";
<<EODECL
//! return the value of $class_name.$field_name_uc
$decl_string
EODECL
;
}
####################################################################
####################################################################
####################################################################
sub get_comment_string
{
my ( $generator, $source_file ) = @_;
my $today = `date`;
chomp $today;
my $id_str = `id`;
$id_str =~ /uid=(\d+)/;
my( $user_login_name,$passwd,$uid,$gid,$quota,$comment,$user_full_name, @foo ) = getpwuid( $1 );
my $short_gen = $generator;
$short_gen =~ s#.*/##;
my $pwd = `pwd`;
chomp $pwd;
$source_file = resolve_path( $pwd, $source_file );
<<EOCOMMENT
/************************************************************************
**
**
** ****** MACHINE-GENERATED FILE ******
** ****** DO NOT EDIT BY HAND ******
**
** Generated from $source_file
** using $generator
** File created on $today by $user_full_name ($user_login_name)
**
** Command Line Used to Generate File:
** $short_gen @ORIG_ARGS
**
** Copyright (C) 2002, Sun Microsystems, Inc.
**
** Sun considers its source code as an unpublished, proprietary
** trade secret and it is available only under strict license provisions.
** This copyright notice is placed here only to protect Sun in the event
** the source is deemed a published work. Disassembly, decompilation,
** or other means of reducing the object code to human readable form
** is prohibited by the license agreement under which this code is
** provided to the user or company in possession of this copy.
*************************************************************************/
EOCOMMENT
;
}
####################################################################
####################################################################
####################################################################
sub resolve_path
{
my ( $pwd, $path ) = @_;
my $result;
while( $path =~ s#\.\./## ){
$pwd =~ s#/[^/]+$##;
}
if( $path !~ m#^/# && $pwd !~ m#/$# ){
$pwd .= '/';
}
return $pwd . $path;
}
####################################################################
####################################################################
####################################################################
sub get_register_documentation_string
{
#my() = @_;
<<EOCOMMENT
EOCOMMENT
;
}
####################################################################
####################################################################
####################################################################
sub print_field_table_doc
{
my ( $file_handle, $reg_name, $field_ref, $reg_ref ) = @_;
#################################
## Produce the table of register fields.
#################################
my @comments;
my $orig_name = $~;
print $file_handle <<EOHEAD
$reg_name Field Specification
Offset Size Name Description
------------------------------------------------------------------------------------------------
EOHEAD
;
for my $o ( sort { $a <=> $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<namespace list>
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<path>
Specify the directory to which the generated header file (.h file) should be written.
=item B<-S> I<path>
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<Riesling Register Specification> for a complete discussion
of the XML specification.