| Charles Curley - Software Engineer, Writer
| << | < | > | >> | Blog | Linked In Profile
+ Larger Font | - Smaller Font
Charles Curley

Valid XHTML 1.0! Valid CSS!


Register

A Register Decoding Program

Programmers close to the silicon, such as device driver writers, have to debug their programs. To do that, it is useful to print out the contents of a register bit field by bit field. This program makes debugging close to the silicon much easier. This is the only muti-file program I have here, and shows a simple makefile.

This program is designed to be portable. It compiles under the GNU C compiler on Linux. Programmers may readily add registers as needed, and define new chips.

K&R style was used to allow compilation on HP-UX where the user does not have the ANSI C compiler. In fact the first pass was done on a K&R style compiler. When I re-compiled on GCC, a number of subtle design flaws showed up that the K&R compiler had missed. So I got an abject lesson in the advantages of using a good compiler and the advantages of ANSI C over K&R C.

If you are not comfortable with C's pointer to structure syntax, you are going to have problems reading the code. There is indirection galore in order to simplify adding new registers and new chips. On the other tentacle, this is probably a very good example for teaching students how to use pointers to structures, because the program is otherwise very simple and short.

Each register is defined in a structure. For each bit field, there is an entry with three fields: the name, the size in bits, and a flag indicating whether the program should show the bit field or not. This last is useful for monster registers where the user is interested in only a few bit fields.

A chip is defined as a set of registers. A structure keeps a list of the register structures in the chip. This allows the code to walk through the chip definition without the user having to modify the source code at all.

Note that all structures are null terminated, allowing walks through structures of indeterminate length. This allows the user to define 32 bit registers (as shown here), 16 or 8 bit registers, by simply changing the #define REGSIZE in register.h.

register.c

/* a quick & dirty register tool for the DEC 21143 100/10BT chip.

   To use, give as arguments a register name followed by a hexidecimal
   value. The program will list bit field by bit field the values in each
   one.

   Begun 4 11 96 c^2 */

#include "register.h"
#include <stdlib.h>		/* So we can use strtol() */

#ifndef NULL
#define NULL 0
#endif

extern struct reg *registers[];	/* Defined in the register file. */

/* Brain dead table lookup for the masks necessary to select each
   field. */
unsigned int masque[] = { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023,
		     2047, 4095, 8191, 16384, 32768, 65535 };

int inventory(src)
     char *src;
{
  int found = -1;
  int field = 0;
  int size = 0;
  int i=0;

/*   printf("Input to the inventory routine is: %s\n", src); */

  printf("The registers I recognize are:\n\t");

  while(registers[i] != (struct reg*)NULL )
    {
      printf("%s ", registers[i]->name);

      field = strcmp(registers[i]->name, src);
      if(!field)
	{
	  found = i;
	}
/*       printf("Found = %d. ", found); */
/*       printf("Field = %d.\t", field); */

      i++;
    }

  printf("\n");

  return (found);
}
/* end scan for desired register */

main(argc, argv)
     int argc;
     char ** argv;
{
  int i = 0;			/* Generic counter. Greatly recycled. */
  int field;			/* Current field within the current register */
  int found =-1;		/* When we find the desired register, we
				   keep its number here. */
  int size;			/* The size of the current register */
  long reg;			/* The register value from the cmd line. */
  char *garbage;		/* So we can ignore the pointer from strtol */
  int mask;			/* Holds the mask for this size bit field */
  int len = 0;			/* Records the longest string length in the
				   register. */
  int padding;			/* The count for padding leading spaces */

  /* Argument checking goes here: argc must equal 3. Otherwise print syntax
     message & error out. */

  if (argc != 3)
    {
      printf( "chipname regname value\n");
      printf( "\tWhere chipname is the name of the chip to be examined,\n");
      printf( "\tregname is the name of a register in which you are\n");
      printf( "\tinterested, and value is a hexidecimal value to be\n");
      printf( "\tdecoded by the bitfields. ");
      printf( "Version 1.1 13 November 1996 C^2\n");
      inventory(" ");
      exit(-2);
    }

  /* Now we do a walk through the register structures to do a simple
     integrity check. Bomb out if any register doesn't have the right
     number of bits defined. */

  while(registers[i] != (struct reg*)NULL )
    {
      field = 0;
      size = 0;

      while (registers[i]->fields[field].size != 0)
	{
	  size += registers[i]->fields[field].size;
	  field++;
	}

      if (size != REGSIZE)
	{
	  printf("\aOops! Size of register %s is %d!!\n",
		 registers[i]->name, size);
	  exit(-1);
	}

      i++;
    } /* end integrity check */


  /* Now we scan for the requested register. */
  found = inventory(argv[1]);

  if (found == -1)
    {
      printf("\aOops! %s is not a register I recognize!\n", argv[1]);
      exit(-1);
    }

  /* Now that we have located our register, scan for string sizes. */
  field = 0;

  while (registers[found]->fields[field].size != 0)
    {
      /* If the showme flag is set, print this line. */
      if (registers[found]->fields[field].showme)
	{
	  i = strlen(registers[found]->fields[field].name);
/* 	  printf("The length is %d\n", i); */
	  if ( i > len )
	    {
	      len = i;
	    }
	}

      field++;			/* Go to it. */
    }
  len++;
/*   printf("len = %d\n", len); */

  /* end scanning for string sizes */

  /*   printf("The input register string is %s.\n", argv[2]); */

  /* Get the second argument in to a variable in hexidecimal */
  reg = strtol(argv[2], &garbage, 16);

  printf("The input value is %lx.\n", reg);


  /* walk through the selected register & print out the values */
  field = 0;

  printf ("Starting with the least significant bit, the contents of register %s are:\n", registers[found]->name);

  while (registers[found]->fields[field].size != 0)
    {
      size = registers[found]->fields[field].size;
      mask = masque[size];
      /*       printf("Size is %d, mask is %4x. ", size, mask); */

      /* If the showme flag is set, print this line. */
      if (registers[found]->fields[field].showme)
	{

	  /* print leading spaces */
	  padding = len - strlen(registers[found]->fields[field].name);
	  while (padding--)	/* Is there a better way to do this? */
	    {
	      printf(" ");
	    }

	  printf("%s: ", registers[found]->fields[field].name);

	  /* Single bit bitfields are describe as set or reset. Multi-bit
	     fields are described as the hex value within that field, right
	     justified. */

	  if ( size == 1 )
	    {
	      printf ( "%s\n", reg & mask ? "set": "reset" );
	    } else {
		     printf ( "0x%lx in a field of %d (mask %x)\n", reg &
			     mask, size, mask );
		   }
	}

      reg >>= size;		/* Bring the next field into view. */
      field++;			/* Go to it. */
    }

}

register.h

/* This file defines the data structure used to house information about a
   given register. Define each register in its own header, using this
   structure, then link 'em in in the source program. That way you can
   re-use the program for different chips.

   Since this isn't ANSI, we can't assume that uninitialized values are
   set to 0. *So* *initialize* *them*, or at least the first one. */

#define NAMES 50
#define REGSIZE 32		/* The number of bits in a register */
#define FINIS { "", 0}		/* Mandatory final entry!!! */
#define SHOW 1			/* Show this field */
#define NOSHOW 0		/* Don't show this field */

struct fld {
  char name[NAMES];		/* The name of the current bit field */
  int size;			/* The size of the field in bits. */
  /* Begin bit fields to describe the curent entry. */
  unsigned showme:1;		/* Tell the program to show this entry or
				   not */
};

struct reg {
  char name[NAMES];		/* Name of the register, so we can print
				   it out. */
  struct fld fields[REGSIZE+1];	/* A list of the fields for this
				   register. 33 so we can always have the
				   mandatory empty last field. */
};

dec21143.c

/*	CSR0 and CSR5 for the DEC 21143, for the register program.	*/

#include "register.h"

#define NULL 0

struct reg csr0 = {
	"csr0",
	"Software Reset", 1, SHOW,
	"Bus Arbitration", 1, SHOW,
	"Descriptor Skip Length", 6, SHOW,
	"Big/little Endian", 1, SHOW,
	"Programmable Burst Length", 8, SHOW,
	"Cache Alignment", 3, SHOW,
	"Transmit Automatice Polling", 1, SHOW,
	"Descriptor Byte Ordering Mode", 1, SHOW,
	"Read Multiple Enable", 1, SHOW,
	"Reserved, should be 1,", 1, NOSHOW,
	"Read Line Enable", 1, SHOW,
	"Reserved, should be 1,", 7, NOSHOW,
	FINIS,		/* Mandatory final entry!!! */
};

struct reg csr5 = {
	"csr5",
	"Transmit Interrupt", 1, SHOW,
	"Transmit Process Stopped", 1, SHOW,
	"Transmit Buffer Unavailable", 1, SHOW,
	"Transmit Jabber Timeout", 1, SHOW,
	"Link Pass/Autonegotiate Done", 1, SHOW,
	"Transmit Underflow", 1, SHOW,
	"Receive Interrupt", 1, SHOW,
	"Reveive Buffer Unavailable", 1, SHOW,
	"Receive Process Stopped", 1, SHOW,
	"Receive Watchdog Timeout", 1, SHOW,
	"Early Transmit Interrupt", 1, SHOW,
	"General Purpose Timer Expired", 1, SHOW,
	"Link Fail", 1, SHOW,
	"Fatal Bus Error", 1, SHOW,
	"Early Receive Interrupt", 1, SHOW,
	"Abnormal Interrupt Summary", 1, SHOW,
	"Normal Interrupt Summary", 1, SHOW,
	"Receive Process State", 3, SHOW,
	"Transmission Process State", 3, SHOW,
	"Error Bits", 3, SHOW,
	"General Purpose Port Interrupt", 1, SHOW,
	"Link Changed", 1, SHOW,
	"Reserved, should be 1,", 4, SHOW,
	FINIS,		/* Mandatory final entry!!! */
};

/* As you define chips, put each one in its own file. Then you can
   re-cycle the core code. Add an entry for each register in the chip
   above. Then add it to the registers array.

   Enter the initialization data into each structure starting with the
   least significant bit. */

struct reg *registers[] =	/* Always leave room for the null
				   terminator */
{
  &csr0,
  &csr5,
  (struct reg*)NULL		/* Always the last entry!! */
};

makefile

# makefile for registers program(s).

# As you define multiple chips, make each one a dependency for all, and
# recycle the register code. That way you can have multiple programs to
# describe multiple chips. Also add the executable to the clean target.

all:	dec21143

dec21143:	register.o dec21143.o
	cc register.o dec21143.o -o dec21143

dec21143.o:	dec21143.c register.h
	cc -c dec21143.c

register.o:	register.c register.h
	cc -c register.c

clean:
	rm -f reg.shar
	rm -f *.o
	rm -f dec21143
	rm -f *~			# remove emacs backups

shar: clean
	shar * > reg.shar

Copyright © 1996 through 2011 by Charles Curley
Last Modified: 28 Oct, 2011
100% Microsoft-free web site.