This document's purpose is to set forth guidelines and standards that should be followed when writing a module generator for JHDL. These standards will help ease the task of maintaining the current library of module generators (the byucc.jhdl.modgen package) and serve as a tutorial to anyone new to the JHDL module generator scene. Thus this document will contain some very basic explanation to help those starting out as well as discussion and standards on how to implement the things that are common to most module generators. As with any standard, there will be exceptions to the rule because of different circumstances from what the standard was designed for, but module generator designers should do there best to follow every applicable standard to make it easier for those who come after. The following is an overview of the topics covered in this document:
Module Generator Basics
What is a Module Generator?
A module generator is a design that will build itself according to
the configuration parameters that are passed into it. For
example, the NBitAdder in the JHDL Documentation Getting Started
section is an example of a very simple module generator. Besides
the adder size, other parameters could also be passed into the
NBitAdder such as a registered parameter or a parameter that
indicates if overflow logic should be added to the adder. The
ideal module generator would build the most efficiently
constructed and placed circuit for the parameters given.
Obviously this ideal is not always possible because the the most
efficient construction of a circuit can be highly dependant on the
application. Although, a very high level module generator could
also have a parameter that indicates a certain configuration or
placement directive to the module generator. FPGA
implementation-independence is another module generator ideal,
although this ideal is difficult to acheive. For a little more
introduction to the module generators in JHDL see the Modgen Module
Generators section of the Users Manual in the JHDL
documentation.
Generic width input/output wires
The NBitAdder example from the JHDL documentation shows one way to
implement generic wire widths in JHDL by passing the desired
widths in as integer parameters to the constructor. It's
implementation is seen below:
public class NBitAdder extends Logic {
public static CellInterface[] cell_interface = {
in("a", "width"),
in("b", "width"),
out("sum", "width+1"),
param("width", INTEGER)
};
public NBitAdder(Node parent, Wire a, Wire b, Wire sum,
int width, String name) {
super(parent, name);
bind("width", width);
connect("a", a);
connect("b", b);
connect("sum", sum);
. . .
. . .
. . .
}
}
While this method works, it adds a parameter into the constructor
for every generic width that is needed in the circuit. The
following more efficient way of handling generic wire widths uses
the Wire class's method getWidth() to get the width of one of the input
wires. This approach has the advantage that the constructor has
one or more less parameters. Seen here is a different constructor
whose CellInterface is the same as example above:
public NBitAdder(Node parent, Wire a, Wire b, Wire sum,
String name) {
super(parent, name);
int width = a.getWidth();
bind("width", width);
connect("a", a);
connect("b", b);
connect("sum", sum);
. . .
. . .
. . .
}
null in for
this parameter. You can do this by testing the wire to see if
it's null and if it is, removing the port with the
removePort(String port) call instead of connecting
it. Ideally, if you know that an optional wire is null you should
optimize as much logic as possible (i.e. if one of many outputs is
passed a null, eliminate any logic that creates that output that
is not needed for other signals). Note that depending on the
module configuration you may require that a certain input or
output wire be null (i.e. the IntDivide module generator requires
that if the divider rounds the quotient that the remainder output
parameter be passed a null). You can enforce this requirement as
explained in the next section.
//exception used for invalid parameters into constructor
private class arrayMultException extends ModgenException {
protected arrayMultException(String gripe) {
super(gripe); } }
Here's an example from the constructor of arrayMult for a simple
error check on the width of x and y by using the error check (the
arrayMult module generator doesn't accept inputs of widths less
than 3 bits):
if (width<3||height<3){
throw new arrayMultException
("Width of input wires x and y must be 3 bits or greater."); }
For convenience multiple constructors can be used to help mantain backwards compatibility and to allow different configurations of a module generator. One must be VERY careful with multiple constructors when returning true for the method cellInterfaceDeterminesUniqueNetlistStructure() because making sure that every parameter is bound correctly can be tricky.
One other issue regarding multiple constructors: each module generator should have the ability for the user to specify or not specify an instance name. This should be accomplished with two constructors for each configuration as in the following example showing the use of two constructors for the arrayMult module generator:
public arrayMult(Node parent, Wire x, Wire y, Wire clk_en,
boolean signed, int pipedepth, Wire pout) {
this(parent, x, y, clk_en, signed, pipedepth, pout, null);
}
public arrayMult(Node parent, Wire x, Wire y, Wire clk_en,
boolean signed, int pipedepth, Wire pout,
String instanceName) {
super(parent, instanceName);
...(connect ports, do error checking and build circuit)
TechMapper defaultTechMapper = Logic.getDefaultTechMapper();
if (defaultTechMapper instanceof VirtexTechMapper) {
set flags here so that a Virtex optimized module is built
}
else if (defaultTechMapper instanceof XC4000TechMapper) {
set flags here so that a XC4000 optimized module is built
}
else {
set flags so a generic Logic module is built
}
Placement
As mentioned in the What is a Module Generator section,
placement is one of the challenges of writing a generic module
generator. It is strongly recomended that when placing that you
use relative place calls so that the placement is more readable
and more portable to new technologies. It is difficult for the
module generator designer to know what the end user will be more
interested in, less area or more speed. A good general
assumption is that the end user will be interested in both and
that you should come up with a good trade-off between area and
speed. Although, if it is practical or plausable, you may give
a little control to the end user over what the exact
configuration of the circuit will be.
A good general rule is to make your design as square as possible so that it can easily be placed with other circuits by the end user. Remember that the origin will be translated to the upper left-hand corner of the bounding box of cell automatically.
Behavioral Modeling
To speed up run time, a module generator, wherever possible and
practical, should have a behavioral model that models the
module's behavior in software. The basic information on
behavioral modeling is found in the User's Manual of the JHDL
Documentation under the section Behavioral
Modeling in JHDL. Remember the following as you write your
behavioral model:
public void clock() { out.put(this, in.get(this)); }
public void propagate() { out.put(this, in.get(this)); }
In the code above, the clock method will behave like a
register while the propagate method will behave like a buffer.
This implied register is important to remember when you model
pipelining structure because there is one less register that
you need to model with an array.
Automatic Test Benches
One of the great advantages about JHDL testbenches written in
java is that automatic and comprehensive tests can be written by
simply using the resources available from the Java programming
language. An automatic testbench is a testbench that checks its
own results and a comprehensive testbench is one that tests all
feasable configurations of a module and runs comprehensive (as
much as practical) test vectors on that module. Ideally we
would like an automatic and comprehensive testbench for every
module generator in the byucc.jhdl.modgen package. This would
allow a verification of the functionality of every module before
a JHDL release and more especially it helps those who modify or
maintain your code (including yourself) verify that their
changes have not broken anything. An automatic test bench
should do the following:
Documentation
Documentation for the module generators should be done using
javadoc comments and according to the document
Documentation Standards for Module Generators. This
document should walk you through the basics of documentation
with javadoc. Note that the only class from your module
generator that needs to be documented in this prescribed way is
the top-level class. Remember to make sure that the following are
documented:
Adding Your Module to the byucc.jhdl.modgen package
For those who are designing for the byucc.jhdl.modgen package,
when your module generator is ready to be added to the modgen
package you should perform the following steps (note that a
module should not be added to this package until it has been
thoroughly tested and documented). If your module will reside
actually in the modgen package then you should make your top
level class part of the modgen package by adding the statement
(without the quotes) "package byucc.jhdl.modgen;" to the first
line of this file and place that file in the
fpga/byucc/jhdl/modgen directory in the CVS repository (see the
CVS Tutorial
on the internal lab web page and/or ask Carl if you are
unfamiliar with CVS).
Any unique helper classes that your top-level file calls and any automatic testbenches should reside in its their own subpackage of modgen. For example the modules that the Cordic module generator calls are contained in the byucc.jhdl.modgen.CordicPack package, implying that they are found in the directory CordicPack found in the modgen directory. Thus you should create a directory in modgen with a name that as well as possible uniquely identifies the purpose of the files contained therein with the word Pack added on the end (no underscores please). Remember that once you've selected a name where your submodules will reside and have made the files part of that package, you will need to import that submodules package in the top-level file in that lives in the modgen package. Once you've put all your files in this new directory and are ready to add this module to the repository do a CVS add followed by a commit on this directory and all the files within it . Add the class files you've added to the modgen Makefile as well as any helper directories you've added. Also when adding a new helper directory make sure there is a Makefile in play (you probably want to copy another helper directory's Makefile) and then you need to make sure that that directory is included in JHDL.jar by modifying the byuc.jhdl Makefile under the MODGEN_CLASSES variable (see Wes if you need more help regarding Makefiles). For modules that don't use any helper classes (like the accum module generator), please make the automatic testbench associated with this module a part of the byucc.jhdl.modgen.modtest package while importing the byucc.jhdl.modgen package and add it to the appropriate directory (fpga/byucc/jhdl/modgen/modtest) in the CVS tree.
For those of you whose top-level module(s) will reside in a
seperate directory off the modgen tree it is a good idea to
follow the same conventions as if your module was in the modgen
directory because although it may not be so now, you may in the
future share the directory with another designer and following
the above conventions will help maintain organization in the
directory structure. The msd directory structure, which is off
the modgen package, is a good example of what someone has done in
this case.
modgen Package Naming Conventions
Class names should follow Java naming conventions. That is
that the first letter is capitalized as well as any new words
are also capitalized (no underscores). For example, the
modgen class Cordic follows Java conventions but the class
arrayMult does not. If modules already commonly in use do not
follow this convention, their class names should be left
unchanged to assure backwards compatibility.