I had also to deal with the problem.

The first solution, i see, is a new descrition langage parsed by a
lex/yacc C programme. This programme generate VHDL constant file, C
header, asm header. But lack a documentation output (html, doxygen
like or, pdf, datasheet like).

The second solution was a pure perl data structure : no parsing was
needed. It's much easier to generate new kind of file : C, VHDL, HTML.

This problème is generic. A good XML dtd could help to have a clean
database and some tools to generate what ever header or documentation
you need but it's a project by it-self.

I have heard about some definition for "core" that should include
register definition, to better generate "SOC plateform". So maybe a
standard exist in this domain.

Regards,
Nicolas

2008/11/7 Mark Marshall <[EMAIL PROTECTED]>:
> Hi.
>
> One of things that has bitten me before is when the different areas of a
>  project start to use there own header files of magic numbers, or worse
> still they just write the magic numbers straight into the code.  This causes
> real pain once a project gets to a certain size, and gives even more grief
> when we get to the second version.
>
> To try to solve this I propose adding the attached patch.  It contains a
> plain text file that contains a small machine readable description of every
> register.  There is also a small piece of perl that will process this text
> file and generate source files for C, assembler and verilog.
>
> As an extension I will eventually add support to generate HTML and other
> documentation formats directly from the source script.
>
> What do people think?  Can I just check this in, and if I do will people use
> it?  Would people like it done differently?  (I'm really not keen to go to
> XML input files, by the way).
>
> I would also propose adding the file oga1_defs.v (that's a bad name) and
> including it in all the places where it's needed.  It took me about 2 weeks
> to realise that there were two different things called TARGET_*. I spent
> another day screaming that the values of TARGET_ are not consistent amongst
> everything in the XP10 (the _syn.v files have drifted from the others).
>
> MM
>
> Index: registers.txt
> ===================================================================
> --- registers.txt       (revision 0)
> +++ registers.txt       (revision 0)
> @@ -0,0 +1,280 @@
> +# This file describes all of the registers used in OGD1.  From it we
> +# generate all other files that describe the registers, that means
> +# verilog, assembler, C and html files.
> +#
> +# The format is fairly simple.  It consits of a number of blocks, each
> +# block contains a number of registers.  I have assumed that each
> +# block is implimented by one piece of verilog (or the address
> +# decoding is).
> +#
> +# The tags are:
> +#
> +# REG_BLOCK <name>
> +#   This starts a new register block.  If there is only one instance
> +#   of this block then the name descibes both the name of the block
> +#   and the name of the instance.
> +#
> +# REG_BLOCK_ENDS
> +#   This ends a register block.
> +#
> +# REG_BLOCK_AT <address>
> +# REG_BLOCK_AT <name> <address>
> +#   This command gives the global address of the block or block
> +#   instance.  The first format is used when there is only one instance
> +#   of this block.  The second format is used when there are multiple
> +#   instances.  Each instance is then named.
> +#
> +# REG_VWIDTH <width>
> +#   This gives the bit width of the verilog address decoder attached
> +#   to this block.
> +#
> +# REG_VSHIFT <shift>
> +#   This gives the number of LSBs to shift off when we generate the
> +#   verilog.  This is useful when the verilog is decoding uint32s.
> +#
> +# REG_TYPE
> +#   Starts a register type.  A register type describes the contents of
> +#   a register, this is most useful when multiple registers have the
> +#   same contents.  Types are global.
> +#
> +# REG_TYPE_ENDS
> +#   Close a preceeding type.
> +#
> +# REG <offset> <flag> <name>
> +# REG <offset> <flag> <name> <type>
> +# REG <offset> <flag> <name> ...
> +#   The REG command describes a register.  The offset is the address
> +#   of the register within the block.  The flag can be R, W or RW.
> +#   The first format describes a simple register containing an
> +#   unsigned int.  The second format describes a register of the named
> +#   type.  The third format indicates that the following lines
> +#   describe the contents of the register.
> +#
> +# REG_ENDS
> +#   This closes the "..." format of REG definition.
> +#
> +# REG_BIT <bit> <flag> <name>
> +#   This defines a bit within a register.  It can only occure within a
> +#   REG_TYPE or REG "..." section.
> +#
> +# REG_BITS <lsb-bit> <msb-bit> <flag> <name>
> +#   This defines a bit range within a register.  It can only occure
> +#   within a REG_TYPE or REG "..." section.
> +#
> +# REG_ENUM <lsb-bit> <msb-bit> <flag> [ <value> <name> ]
> +#   This is used to give names to the values that a register can be
> +#   set to.  It can only occure within a REG_TYPE or REG "..."
> +#   section.
> +#
> +# REG_DOC
> +#   This allows documentation to be added to a given section.  All of
> +#   the lines that follow are part of the documentation string until
> +#   there is a line containing just a dot.
> +
> +REG_BLOCK PCI_CONFIG
> +
> +REG_BLOCK_AT 0
> +REG_VWIDTH 6
> +REG_VSHIFT 2
> +
> +REG_DOC
> +These are the definitions of the PCI Configuration structure.  The
> +entries between 0 and 0x40 are standardised, and come from the PCI
> +specification.  The entries after 0x40 are OGP specific.
> +.
> +
> +REG 0x0000 R  VENDOR_ID
> +REG 0x0002 R  DEVICE_ID
> +REG 0x0004 RW COMMAND
> +REG 0x0006 RW CONTROL
> +REG 0x0008 R  CLASS_REVISION
> +REG 0x000A R  CLASS
> +REG 0x000C RW LATENCY
> +REG 0x0010 RW BAR0
> +REG 0x0014 RW BAR1
> +REG 0x0018 RW BAR2
> +REG 0x001C RW BAR3
> +REG 0x0020 RW BAR4
> +REG 0x0024 RW BAR5
> +REG 0x002C R  SUBSYSTEM_SUBVENDOR
> +REG 0x0030 RW EPROM
> +REG 0x0034 R  CAPS_POINTER
> +REG 0x003C RW FLAGS
> +
> +REG 0x0040 RW MEM_DECODE ...
> +REG_BIT 0 RW BAR0_EN
> +REG_BIT 1 RW BAR1_EN
> +REG_BIT 2 RW BAR2_EN
> +REG_BIT 3 RW EPROM_EN
> +REG_BIT 4 RW VGA_EN
> +REG_BIT 5 RW MDA_EN
> +REG_BIT 6 RW CGA_EN
> +REG_BIT 7 RW VMEM_EN
> +REG_ENDS
> +
> +REG_BLOCK_ENDS
> +
> +
> +# Memory map:
> +#
> +# 0x0000-0x03ff: Reserved for XP10 register space
> +# 0x0400-0x040f: Memory arbiter
> +# 0x0410-0x041f: Memory controller
> +# 0x0420-0x07ff: unused
> +# 0x0800-0x0bff: video controller 0
> +# 0x0c00-0x0fff: video controller 1
> +# 0x1000-0xffff: unused
> +
> +
> +REG_BLOCK XP10
> +
> +REG_BLOCK_AT 0x0000
> +REG_VWIDTH 6
> +REG_VSHIFT 2
> +
> +REG_TYPE       SERIAL_DATA_LINE
> +REG_DOC
> +This type defines the bits used to control the lines of the four
> +serial interaces (Top / Bottom, DDC / I2C).
> +.
> +REG_BIT 0 W OUTPUT
> +REG_BIT 1 W OUTPUT_ENABLE
> +REG_ENUM 0 1 W [ 0 HIZ 2 LO 3 HI 1 BAD ]
> +REG_BIT 0 R STATUS
> +REG_TYPE_ENDS
> +
> +REG 0x0000 RW DDC_CLOCK_TOP    SERIAL_DATA_LINE
> +REG 0x0004 RW DDC_CLOCK_BOTTOM SERIAL_DATA_LINE
> +REG 0x0008 RW DDC_DATA_TOP     SERIAL_DATA_LINE
> +REG 0x000C RW DDC_DATA_BOTTOM  SERIAL_DATA_LINE
> +REG 0x0010 RW I2C_SDA_TOP      SERIAL_DATA_LINE
> +REG 0x0014 RW I2C_SDA_BOTTOM   SERIAL_DATA_LINE
> +REG 0x0018 RW I2C_SCL_TOP      SERIAL_DATA_LINE
> +REG 0x001C RW I2C_SCL_BOTTOM   SERIAL_DATA_LINE
> +
> +REG 0x0020 W VIDEO_RESET ...
> +REG_DOC
> +Is VIDEO_RESET active high or active low?
> +.
> +REG_ENDS
> +
> +REG 0x0024  W RESET_INTERRUPT
> +
> +REG 0x0024 R  INTERRUPT_FLAGS ...
> +REG_BIT 0 R HOTPLUG_TOP
> +REG_BIT 1 R HOTPLUG_BOTTOM
> +REG_BIT 2 R BRIDGE
> +REG_ENDS
> +
> +REG 0x0028 W  DAC_POWER_ON
> +
> +REG 0x0040  W BPROM_SI
> +REG 0x0040 R  BPROM_SO
> +REG 0x0044  W BPROM_CE_BAR
> +REG 0x0048  W BPROM_SCK
> +REG 0x004C  W BPROM_REG_SEL
> +
> +REG 0x0050  W CPROM_SI
> +REG 0x0050 R  CPROM_SO
> +REG 0x0054  W CPROM_CE_BAR
> +REG 0x0058  W CPROM_SCK
> +REG 0x005C  W CPROM_REG_SEL
> +
> +REG 0x0060  W XCONFIG_DIN
> +REG 0x0064  W XCONFIG_CCLK
> +REG 0x0068  W XCONFIG_PROGRAM
> +REG 0x006C  W XCONFIG_SEL
> +
> +REG 0x0070 R  XCONFIG_INITN
> +REG 0x0074 R  XCONFIG_DONE
> +
> +REG 0x0070  W HQ_CONTROL ...
> +REG_BITS 0 9 W LOAD_ADDRESS
> +REG_BIT 10 W BYPASS_EN
> +REG_BIT 11 W RUN_EN
> +REG_ENDS
> +
> +REG 0x0074  W HQ_DATA
> +
> +REG 0x0078 RW CACHE_ENABLE
> +
> +REG 0x007C RW TEST_REG
> +
> +REG_BLOCK_ENDS
> +
> +
> +REG_BLOCK MEMARB
> +
> +REG_BLOCK_AT 0x0400
> +REG_VWIDTH 2
> +REG_VSHIFT 2
> +
> +REG 0x0000  W LMR_REGISTER
> +REG 0x0008  W LMR_RESET
> +REG 0x000C  W LMR_REFRESH_CTRL ...
> +REG_BITS 0 10 W REFRESH_DELAY
> +REG_BIT  11 W REFRESH_ENABLE
> +REG_BIT  12 W MEM_CKE
> +REG_ENDS
> +
> +REG_BLOCK_ENDS
> +
> +
> +REG_BLOCK MEMCTL
> +
> +REG_BLOCK_AT 0x0410
> +REG_VWIDTH 4
> +REG_VSHIFT 2
> +
> +REG 0x0000  W R2W_WAIT
> +REG 0x0004  W W2R_WAIT
> +REG 0x0008  W ACT2RW_WAIT
> +REG 0x000C  W R2PRE_WAIT
> +REG 0x0010  W W2PRE_WAIT
> +REG 0x0014  W PRE2ACT_WAIT
> +REG 0x0018  W ACT2PRE_WAIT
> +REG 0x001C  W REFRESH2ACT_WAIT
> +REG 0x0020  W CAS_LATENCY
> +
> +REG_BLOCK_ENDS
> +
> +
> +
> +REG_BLOCK VM
> +
> +REG_BLOCK_AT VM0 0x0800
> +REG_BLOCK_AT VM1 0x0C00
> +REG_VWIDTH 9
> +REG_VSHIFT 2
> +
> +REG 0x0000  W PROGRAM_MEMORY
> +
> +REG_BLOCK_ENDS
> +
> +
> +REG_BLOCK VC
> +
> +REG_BLOCK_AT VC0 0x0A00
> +REG_BLOCK_AT VC1 0x0E00
> +REG_VWIDTH 4
> +REG_VSHIFT 2
> +
> +REG 0x0000  W PIXEL_X_START
> +REG 0x0004  W PIXEL_Y_START
> +REG 0x0008  W CLEAR_INT
> +REG 0x000C  W PIXEL_INFO ...
> +REG_BITS 0 2 W PIXEL_DEPTH
> +REG_BIT  3   W PIXEL_RATE
> +REG_ENDS
> +REG 0x0010  W CPU_RESET_B
> +REG 0x0014  W INTERRUPT_ENABLE
> +REG 0x0018  W VIDEO_ENABLE
> +REG 0x0020  W PC_START
> +REG 0x0024  W DIVIDERS ...
> +REG_BITS  0 2 W DIVISOR1
> +REG_BITS  3 8 W DIVISOR0
> +REG_BIT   9   W BASE_CLOCK
> +REG_BIT  10   W OE
> +REG_ENDS
> +
> +REG_BLOCK_ENDS
> Index: mk_registers.pl
> ===================================================================
> --- mk_registers.pl     (revision 0)
> +++ mk_registers.pl     (revision 0)
> @@ -0,0 +1,517 @@
> +#!/usr/bin/perl -w
> +
> +use strict;
> +use Pod::Usage;
> +use Getopt::Long;
> +
> +my $output_filename;
> +my $output_type = "c";
> +
> +GetOptions(
> +    "help" => sub { pod2usage(0); },
> +    "output=s" => \$output_filename,
> +    "type=s" => \$output_type
> +) or pod2usage("Bad options.");
> +$#ARGV == 0 or pod2usage("Wrong number of arguments.");
> +
> +my %types;
> +my %regs;
> +my %blocks;
> +
> +
> +sub error(@)
> +{
> +    print @_;
> +    exit(1);
> +}
> +
> +sub parse_input_file($)
> +{
> +  # These are defined when we are in the middle of a multiline type.
> +  my $block;
> +  my $type;
> +  my $reg;
> +
> +  while (<>) {
> +    if (/^REG_BLOCK\s+(\S+)\s*$/) {
> +      my ($name) = ($1);
> +      #printf(" %s\n", $addr);
> +      $block = $name;
> +      my %bh = (
> +               name => $name,
> +               vwidth => 16,
> +               copies => [ ]
> +              );
> +      $blocks{$block} = \%bh;
> +    } elsif (/^REG_BLOCK_ENDS/) {
> +      error("REG_BLOCK_ENDS must follow a REG_BLOCK") if
> (!defined($block));
> +      error("No REG_ENDS after REG ...") if (defined $reg);
> +      error("No REG_TYPE_ENDS after REG_TYPE") if (defined $type);
> +      undef $block;
> +    } elsif (/^REG_BLOCK_AT\s+(\S+)\s*$/) {
> +      error("REG_BLOCK_AT must be in a REG_BLOCK") if (!defined($block));
> +      my ($addr) = ($1);
> +      $addr = hex($addr) if ($addr =~ s/0x//);
> +      $blocks{$block}->{addr} = $addr;
> +    } elsif (/^REG_BLOCK_AT\s+(\S+)\s+(\S+)/) {
> +      error("REG_BLOCK_AT must be in a REG_BLOCK") if (!defined($block));
> +      my ($name, $addr) = ($1, $2);
> +      $addr = hex($addr) if ($addr =~ s/0x//);
> +      push @{$blocks{$block}->{copies}}, [ $name, $addr ];
> +    } elsif (/^REG_VWIDTH\s+(\S+)/) {
> +      $blocks{$block}->{vwidth} = $1;
> +    } elsif (/^REG_VSHIFT\s+(\S+)/) {
> +      $blocks{$block}->{vshift} = $1;
> +    } elsif (/^REG_ENUM\s+(\S+)/) {
> +      # TODO
> +    } elsif (/^REG_TYPE\s+(\S+)/) {
> +      $type = $1;
> +      my %th = (
> +               name => $type,
> +               doc => "",
> +               bits => []
> +              );
> +      $types{$type} = \%th;
> +    } elsif (/^REG_TYPE_ENDS/) {
> +      error("REG_TYPE_ENDS must follow a REG_TYPE") if (!defined($type));
> +      undef $type;
> +    } elsif (/^REG\s+(\S+)\s+(\S+)\s+(\S+)(\s+(\S+))?/) {
> +      my ($name, $addr, $flags, $extra) = ($3, $1, $2, $5);
> +      error("No REG_ENDS after REG ...") if (defined $reg);
> +      error("No REG_TYPE_ENDS after REG_TYPE") if (defined $type);
> +      $addr = hex($addr) if ($addr =~ s/0x//);
> +      #printf("m %s %d\n", $extra, $addr) if defined($extra);
> +      my %rh = (
> +               name => $name,
> +               doc => "",
> +               flags => $flags,
> +               addr => $addr,
> +               block => $block,
> +               bits => []
> +              );
> +      $blocks{$block}->{regs}{$name} = \%rh;
> +      if (defined $extra and $extra eq "...") {
> +       $reg = $name;
> +      } elsif (defined $extra) {
> +       $blocks{$block}->{regs}{$name}->{type} = $extra;
> +      }
> +    } elsif (/^REG_ENDS/) {
> +      error("REG_ENDS must follow a REG ...") if (!defined($reg));
> +      undef $reg;
> +    } elsif (/^REG_DOC/) {
> +      error("REG_DOC must follow a REG or be in a REG_TYPE or REG_BLOCK")
> +       if (!defined($type) && !defined($reg) && !defined($block));
> +      my $doc = "";
> +      while (<>) {
> +       last if (/^\.\s*$/);
> +       $doc .= $_;
> +      }
> +      $types{$type}->{doc} = $doc if (defined $type);
> +      $blocks{$block}->{regs}{$reg}->{doc} = $doc if (defined $reg);
> +      $blocks{$block}->{doc} .= $doc if (defined $block and not (defined
> $type or defined $reg));
> +    } elsif (/^REG_BIT\s+(\S+)\s+(\S+)\s+(\S+)/) {
> +      my @bitpos = ($1, $2, $3);
> +      error("REG_BIT must follow a REG or be in a REG_TYPE")
> +       if (!defined($type) && !defined($reg));
> +      push @{$types{$type}->{bits}}, [ @bitpos ] if (defined $type);
> +      push @{$blocks{$block}->{regs}{$reg}->{bits}}, [ @bitpos ] if
> (defined $reg);
> +    } elsif (/^REG_BITS\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/) {
> +      my @bitpos = ($1, $3, $4, $2);
> +      error("REG_BITS must follow a REG or be in a REG_TYPE")
> +       if (!defined($type) && !defined($reg));
> +      push @{$types{$type}->{bits}}, [ @bitpos ] if (defined $type);
> +      push @{$blocks{$block}->{regs}{$reg}->{bits}}, [ @bitpos ] if
> (defined $reg);
> +    } elsif (/^\s*#/) {
> +    } elsif (/^\s*$/) {
> +    } else {
> +      error("Bad line $_");
> +    }
> +  }
> +}
> +
> +parse_input_file($ARGV[0]);
> +
> +open(INC, "> $output_filename") or
> +  die "Can't open output file $output_filename";
> +
> +sub print_bits_asm($@)
> +{
> +  my ($name, @list) = @_;
> +
> +  for my $b (@list) {
> +    my $nm = sprintf("%s_%s_", $name, @{$b}[2]);
> +    if (@{$b} == 3) {
> +      printf(INC "%-40s EQU 0x%08x ; %2s\n",
> +            $nm."MASK", 1 << @{$b}[0], @{$b}[1]);
> +      printf(INC "%-40s EQU %10u\n",
> +            $nm."POSN", @{$b}[0]);
> +    } elsif (@{$b} == 4) {
> +      my $mm = ((1 << (@{$b}[3] + 1)) - 1) & ~ ((1 << @{$b}[0]) - 1);
> +      printf(INC "%-40s EQU 0x%08x ; %2s\n",
> +            $nm."MASK", $mm, @{$b}[1]);
> +      printf(INC "%-40s EQU %10u\n",
> +            $nm."LSB_POSN", @{$b}[0]);
> +      printf(INC "%-40s EQU %10u\n",
> +            $nm."MSB_POSN", @{$b}[3]);
> +    }
> +  }
> +}
> +
> +sub print_bits_c($@)
> +{
> +  my ($name, @list) = @_;
> +
> +  for my $b (@list) {
> +    my $nm = sprintf("%s_%s_", $name, @{$b}[2]);
> +    if (@{$b} == 3) {
> +      printf(INC "#define %-40s 0x%08x /* %2s */\n",
> +            $nm."MASK", 1 << @{$b}[0], @{$b}[1]);
> +      printf(INC "#define %-40s %u\n",
> +            $nm."POSN", @{$b}[0]);
> +    } elsif (@{$b} == 4) {
> +      my $mm = ((1 << (@{$b}[3] + 1)) - 1) & ~ ((1 << @{$b}[0]) - 1);
> +      printf(INC "#define %-40s 0x%08x /* %2s */\n",
> +            $nm."MASK", $mm, @{$b}[1]);
> +      printf(INC "#define %-40s %u\n",
> +            $nm."LSB_POSN", @{$b}[0]);
> +      printf(INC "#define %-40s %u\n",
> +            $nm."MSB_POSN", @{$b}[3]);
> +    }
> +  }
> +
> +  my $i = 0;
> +  for my $b (@list) {
> +    next if (@{$b}[1] eq "R");
> +    $i++;
> +  }
> +  return if ($i == 0);
> +
> +  printf(INC "#define %s(", $name);
> +  $i = 0;
> +  for my $b (@list) {
> +    next if (@{$b}[1] eq "R");
> +    my $p = lc(sprintf("%s", @{$b}[2]));
> +    printf(INC "%s%s", ($i == 0) ? "" : ", ", $p);
> +    $i++;
> +  }
> +  printf(INC ") \\\n    (");
> +  $i = 0;
> +  for my $b (@list) {
> +    next if (@{$b}[1] eq "R");
> +    my $p = lc(sprintf("%s", @{$b}[2]));
> +    my $nm = sprintf("%s_%s_", $name, @{$b}[2]);
> +    printf(INC " | \\\n     ") if ($i != 0);
> +    if (@{$b} == 3) {
> +      printf(INC "((%s) ? %sMASK : 0)", $p, $nm);
> +    } else {
> +      printf(INC "(((%s) << %sLSB_POSN) \\\n         & %sMASK)", $p, $nm,
> $nm);
> +    }
> +    $i++;
> +  }
> +  printf(INC ")\n");
> +}
> +
> +sub print_bits_v($@)
> +{
> +  my ($name, @list) = @_;
> +
> +  for my $b (@list) {
> +    my $nm = sprintf("%s_%s_", $name, @{$b}[2]);
> +    if (@{$b} == 3) {
> +      printf(INC "parameter %-40s = 32'h%08x;\n",
> +            $nm."MASK", 1 << @{$b}[0]);
> +      printf(INC "parameter %-40s = 5'd%u;\n", $nm."POSN", @{$b}[0]);
> +    } elsif (@{$b} == 4) {
> +      my $mm = ((1 << (@{$b}[3] + 1)) - 1) & ~ ((1 << @{$b}[0]) - 1);
> +      printf(INC "parameter %-40s = 32'h%08x;\n", $nm."MASK", $mm);
> +      printf(INC "parameter %-40s = 5'd%u;\n", $nm."LSB_POSN", @{$b}[0]);
> +      printf(INC "parameter %-40s = 5'd%u;\n", $nm."MSB_POSN", @{$b}[3]);
> +    }
> +  }
> +}
> +
> +sub comment($)
> +{
> +  my ($text) = @_;
> +
> +  return unless defined $text;
> +  return if $text eq "";
> +
> +  if ($output_type eq "c") {
> +    if ($text =~ /\n.*\n/) {
> +      printf(INC "/*\n * %s\n */\n",
> +            join("\n * ",
> +                 split('\n', $text)));
> +    } else {
> +      chomp($text);
> +      printf(INC "/* %s */\n", $text);
> +    }
> +  } elsif ($output_type eq "v") {
> +    if ($text =~ /\n.*\n/) {
> +      printf(INC "// %s\n//\n",
> +            join("\n// ",
> +                 split('\n', $text)));
> +    } else {
> +      chomp($text);
> +      printf(INC "// %s\n", $text);
> +    }
> +  } elsif ($output_type eq "x") {
> +    if ($text =~ /\n.*\n/) {
> +      printf(INC ";; %s\n\n",
> +            join("\n;; ",
> +                 split('\n', $text)));
> +    } else {
> +      chomp($text);
> +      printf(INC ";; %s\n", $text);
> +    }
> +  }
> +}
> +
> +sub dump_regs_c($$$)
> +{
> +  my ($block, $copy, $base) = @_;
> +
> +  comment($blocks{$block}->{doc});
> +
> +  for my $t (sort { $blocks{$block}->{regs}{$a}->{addr} <=>
> +                     $blocks{$block}->{regs}{$b}->{addr} }
> +            keys %{$blocks{$block}->{regs}}) {
> +    my $name = sprintf("%s_%s",
> +                      $copy, $blocks{$block}->{regs}{$t}->{name});
> +
> +    comment($blocks{$block}->{regs}{$t}->{doc});
> +
> +    printf(INC "#define %-40s     0x%04X /* %2s */\n",
> +          $name,
> +          $blocks{$block}->{regs}{$t}->{addr} + $base,
> +          $blocks{$block}->{regs}{$t}->{flags});
> +  }
> +}
> +
> +sub dump_regs_c2($$$)
> +{
> +  my ($block, $copy, $base) = @_;
> +
> +  for my $t (sort { $blocks{$block}->{regs}{$a}->{addr} <=>
> +                     $blocks{$block}->{regs}{$b}->{addr} }
> +            keys %{$blocks{$block}->{regs}}) {
> +    my $name = sprintf("%s_%s",
> +                      $copy, $blocks{$block}->{regs}{$t}->{name});
> +
> +    if (scalar @{$blocks{$block}->{regs}{$t}->{bits}}) {
> +      printf(INC "\n/* Bits for register %s */\n", $name);
> +
> +      print_bits_c($blocks{$block}->{regs}{$t}->{name},
> +                  @{$blocks{$block}->{regs}{$t}->{bits}});
> +    }
> +  }
> +}
> +
> +sub dump_regs_asm($$$)
> +{
> +  my ($block, $copy, $base) = @_;
> +
> +  comment($blocks{$block}->{doc});
> +
> +  for my $t (sort { $blocks{$block}->{regs}{$a}->{addr} <=>
> +                     $blocks{$block}->{regs}{$b}->{addr} }
> +            keys %{$blocks{$block}->{regs}}) {
> +    my $name = sprintf("%s_%s",
> +                      $copy, $blocks{$block}->{regs}{$t}->{name});
> +
> +    comment($blocks{$block}->{regs}{$t}->{doc});
> +
> +    printf(INC "%-40s EQU     0x%04X ; %2s\n",
> +          $name,
> +          $blocks{$block}->{regs}{$t}->{addr} + $base,
> +          $blocks{$block}->{regs}{$t}->{flags});
> +  }
> +}
> +
> +sub dump_regs_asm2($$$)
> +{
> +  my ($block, $copy, $base) = @_;
> +
> +  comment($blocks{$block}->{doc});
> +
> +  for my $t (sort { $blocks{$block}->{regs}{$a}->{addr} <=>
> +                     $blocks{$block}->{regs}{$b}->{addr} }
> +            keys %{$blocks{$block}->{regs}}) {
> +    my $name = sprintf("%s_%s",
> +                      $copy, $blocks{$block}->{regs}{$t}->{name});
> +
> +    comment($blocks{$block}->{regs}{$t}->{doc});
> +
> +    if (scalar @{$blocks{$block}->{regs}{$t}->{bits}}) {
> +      printf(INC "; Bits for register %s\n", $name);
> +
> +      print_bits_asm($blocks{$block}->{regs}{$t}->{name},
> +                    @{$blocks{$block}->{regs}{$t}->{bits}});
> +    }
> +  }
> +}
> +
> +sub dump_regs_v
> +{
> +  my ($block, $copy, $base) = @_;
> +
> +  my $am = (1 << $blocks{$block}->{vwidth}) - 1;
> +  my $hd = ($blocks{$block}->{vwidth} + 3) / 4;
> +  my $sh = $blocks{$block}->{vshift};
> +  $sh = 0 unless defined ($sh);
> +
> +  comment($blocks{$block}->{doc});
> +
> +  for my $t (sort { $blocks{$block}->{regs}{$a}->{addr} <=>
> +                     $blocks{$block}->{regs}{$b}->{addr} }
> +            keys %{$blocks{$block}->{regs}} )
> +  {
> +    my $name = sprintf("%s_%s",
> +                      $block, $blocks{$block}->{regs}{$t}->{name});
> +
> +    comment($blocks{$block}->{regs}{$t}->{doc});
> +
> +    printf(INC "parameter %-40s = %d'h%0*x;\n",
> +            $name, $blocks{$block}->{vwidth}, $hd,
> +          (($blocks{$block}->{regs}{$t}->{addr} + $base) >> $sh) & $am);
> +
> +    print_bits_v($blocks{$block}->{regs}{$t}->{name},
> +                @{$blocks{$block}->{regs}{$t}->{bits}});
> +  }
> +}
> +
> +sub get_addr($)
> +{
> +  my ($bl) = @_;
> +
> +  return $blocks{$bl}->{addr}
> +    if (defined($blocks{$bl}->{addr}));
> +
> +  for my $c (@{$blocks{$bl}->{copies}}) {
> +    return @{$c}[1];
> +  }
> +
> +  return 0;
> +}
> +
> +
> +if ($output_type eq "c") {
> +
> +  my $fn = uc($output_filename);
> +  $fn =~ s/\./_/;
> +
> +  printf(INC "\n#ifndef %s__\n#define %s__\n\n", $fn, $fn);
> +
> +  for my $t (keys %types) {
> +    printf(INC "\n/* TYPE %s */\n\n", $t);
> +    comment($types{$t}->{doc});
> +    print_bits_c($t, @{$types{$t}->{bits}});
> +  }
> +
> +  for my $bl (sort { get_addr($a) <=> get_addr($b) } keys %blocks) {
> +    printf(INC "\n/* BLOCK %s */\n\n", $bl);
> +
> +    # For the C we dump one set of info per instance
> +    for my $c (@{$blocks{$bl}->{copies}}) {
> +      dump_regs_c($bl, @{$c}[0], @{$c}[1]);
> +    }
> +    if (defined($blocks{$bl}->{addr})) {
> +      dump_regs_c($bl, $bl, $blocks{$bl}->{addr});
> +    }
> +    dump_regs_c2($bl, $bl, $blocks{$bl}->{addr});
> +  }
> +
> +  printf(INC "\n#endif /* %s__ */\n\n", $fn);
> +
> +} elsif ($output_type eq "x") {
> +
> +  for my $t (keys %types) {
> +    printf(INC "\n; TYPE %s\n\n", $t);
> +    comment($types{$t}->{doc});
> +    print_bits_asm($t, @{$types{$t}->{bits}});
> +  }
> +
> +  for my $bl (sort { get_addr($a) <=> get_addr($b) } keys %blocks) {
> +    printf(INC "\n; BLOCK %s\n\n", $bl);
> +
> +    # For the ASM we dump one set of info per instance
> +    for my $c (@{$blocks{$bl}->{copies}}) {
> +      dump_regs_asm($bl, @{$c}[0], @{$c}[1]);
> +    }
> +    if (defined($blocks{$bl}->{addr})) {
> +      dump_regs_asm($bl, $bl, $blocks{$bl}->{addr});
> +    }
> +    dump_regs_asm2($bl, $bl, $blocks{$bl}->{addr});
> +  }
> +
> +} elsif ($output_type eq "v") {
> +
> +  for my $t (keys %types) {
> +    printf(INC "\n// TYPE %s\n\n", $t);
> +    comment($types{$t}->{doc});
> +    print_bits_v($t, @{$types{$t}->{bits}});
> +  }
> +
> +  for my $bl (sort { get_addr($a) <=> get_addr($b) } keys %blocks) {
> +    printf(INC "\n// BLOCK %s\n\n", $bl);
> +
> +    # Dump one set of info per block for VERILOG
> +    dump_regs_v($bl, $bl, 0);
> +  }
> +
> +} else {
> +  error("Bad type \"$output_type\"");
> +}
> +
> +exit(0);
> +
> +__END__
> +
> +=head1 NAME
> +
> +mk_registers.pl - Make register definitions
> +
> +=head1 SYNOPSIS
> +
> +  mk_registers.pl --help
> +
> +  mk_registers.pl [OPTIONS] input.txt
> +
> +=head2 Options
> +
> +=over 8
> +
> +=item --output <filename>
> +
> +Specify the output file name.
> +
> +=item --type <type_string>
> +
> +Specify the output file type.  <type_string> can be one of
> +"c", "v", "x" or "html".
> +
> +"c" means that the output file will be a C header file.
> +
> +"v" means that the output file will be a verilog file.
> +
> +"x" means that the output file will be a .inc file suitable
> +for the nasm assembler.
> +
> +"html" is not yet supported, but will eventually produce
> +register documentation.
> +
> +=back
> +
> +=head1 DESCRIPTION
> +
> +This program is used to process a textual description of the registers
> +used within the Open Graphics Hardware.  From this textual descritpion
> +it will produce C include files, Assembler include files, verilog and
> +HTML documentation..
> +
> +This is useful, because it means that there is one source of
> +information about register from which all copies are made.
> +
> +=head1 BUGS
> +
> +Maybe?
> Index: Makefile
> ===================================================================
> --- Makefile    (revision 0)
> +++ Makefile    (revision 0)
> @@ -0,0 +1,17 @@
> +
> +all:: ogd_reg_defines.inc ogd_regs.v ogd_reg_defines.h
> +
> +ogd_reg_defines.inc: registers.txt
> +       perl mk_registers.pl --output=$@ --type=x $<
> +
> +ogd_regs.v: registers.txt
> +       perl mk_registers.pl --output=$@ --type=v $<
> +
> +ogd_reg_defines.h: registers.txt
> +       perl mk_registers.pl --output=$@ --type=c $<
> +
> +
> +ogd_reg_defines.inc ogd_regs.v ogd_reg_defines.h: mk_registers.pl
> +
> +clean:
> +       rm -f ogd_reg_defines.inc ogd_regs.v ogd_reg_defines.h
>
>
> // These are all on the XP10?
>
> parameter PCI_TARGET_CFG   = 0;
> parameter PCI_TARGET_ENG   = 1;
> parameter PCI_TARGET_MEM   = 2;
> parameter PCI_TARGET_VMEM  = 3;
> parameter PCI_TARGET_IO    = 4;
> parameter PCI_TARGET_PROM  = 5;
>
> parameter PCI_TARGET_MAX   = 5;
>
>
> // Bridge Targets
> //
> // The bridge target is passed as a one-hot encoding on the 4-bit
> // 'flags' bus when the 'ADDR' command is active.
> parameter BR_TARGET_ENG  = 2'd0;
> parameter BR_TARGET_MEM  = 2'd1;
>
>
> // Bridge Commands
> //
> parameter BR_CMD_IDLE   = 2'd0;
> parameter BR_CMD_ADDR   = 2'd1;
> parameter BR_CMD_RCOUNT = 2'd2;
> parameter BR_CMD_WRITE  = 2'd3;
>
> _______________________________________________
> Open-graphics mailing list
> [email protected]
> http://lists.duskglow.com/mailman/listinfo/open-graphics
> List service provided by Duskglow Consulting, LLC (www.duskglow.com)
>
_______________________________________________
Open-graphics mailing list
[email protected]
http://lists.duskglow.com/mailman/listinfo/open-graphics
List service provided by Duskglow Consulting, LLC (www.duskglow.com)

Reply via email to