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)