![]() |
|
|
Technology specific module generators are documented under Logic Descriptions - level 4.
The list below identifies all of the old modgen modules. They do have some optimized circuitry, especially for XC4000, so they may still used - they are found in the contrib directory. The updated modules now exist in Logic.Modules for technology-independent modules. Also, each technology has its own Modules directory.
As a result of this, anyone can add module generators to their design environment - they are simply parameterized circuit designs similar to the parameterized adder example from the Describing Circuits section of the JHDL documentation.
As an example of how to use a predefined module, consider the code required to instance an upcounter from the modgen library:
new upcnt(this, clk_en, // Clock enable load, // If asserted, load the value stored in load_data load_data, // Value to be loaded when load is asserted out); // The output of the up counter
The API Documentation for upcnt can be consulted to determine what kinds of parameters can be passed into its constructor to instance various forms of the up counter.
In addition to the modules found in the module generator, much similar functionality is found in the Logic class. In case you don't find what you are looking for in the module generator library, consult the Logic Class.
In reality, all module generators could have been wrapped into Logic. The advantage of Logic is that you need not import anything to use it - you simply extend it and obtain access to its methods for instancing hardware functionality. The advantage of having a separate library of module generator classes (which you import) is that you can call any static methods they provide. This will be shown in the following section.
To determine the word widths needed in your design you could try one of the following:
Each of the datapath-type modules (addsub, multiply, CORDIC) provides just this functionality. Each contains a static compute() method which can be called from arbitrary Java code and thus used in accuracy studies. Each compute() routine (documented in the module generator API), allows the specification of word widths and other parameters such as signed/unsigned to ensure that the result it gives back is bit-level identical to the hardware module it represents, albeit without any timing provided.
Step 1. Define an interface for multiplication, such as:
interface Mult { double doit (double a, double b); }
This interface code will allow your Java code to easily switch between a double-precision multiply (based on the "*" operator) and that provided by the compute() method in the arrayMult module.
Step 2. Write a double-precision multiplier class which implements this interface:
public class DoubleMult implements Mult { public double doit (double a, double b) { double c=a*b; return c; } }
This provides the correct double-precision answer for multiplication. You will use this later to compare against the limited precision module you will implement in the next step.
Step 3. Write a fixed-point class which implements the same interface. It will do this via the following steps (which you can identify in the code example below):
import java.lang.Math; import byucc.jhdl.modgen.*; public final class FixedMult implements Mult { /* These parameters are used below in converting double values to fixed point value.*/ double input1_mult, input2_mult, de_divisor; int width1, width2; boolean signed; /* This constructor sets up the interface between the double inputs and integer inputs to the compute methods */ public FixedMult (int input1_width, int input_bits_before_decimal1, int input2_width, int input_bits_before_decimal2, boolean is_signed) { signed = is_signed; width1 = input1_width; width2 = input2_width; /* These multipliers put the needed bits above the decimal for conversion to ints */ input1_mult = Math.pow(2.0,(double)(input1_width-input_bits_before_decimal1)); input2_mult=Math.pow(2.0,(double)(input2_width-input_bits_before_decimal2)); // The divisor converts the final int back into a double de_divisor = input1_mult * input2_mult; } // Here is the routine that does the work public double doit(double a, double b) { long my_a, my_b; long output; long round; // Convert doubles to ints my_a = (long)(a * input1_mult); my_b = (long)(b * input2_mult); // Do the computation in fixed point by calling the compute() routine if (signed) output = arrayMult.compute(my_a, width1, my_b, width2, width1+width2, 1); else output = arrayMult.compute(my_a, width1, my_b, width2, width1+width2, 0); // Convert the result back to a double and return it double result = (double)(output / de_divisor); return result; } }
A program can now easily be written which performs accuracy studies. Because of the interface created, all the multipliers you create will be of type Mult - both fixed point and floating point. For example, the double-precision module is created with this code:
Mult my_mult = new DoubleMult();
An 8x8 multiplier to compare to this is created by:
Mult my_fixed_mult = new FixedMult(8,0,8,0,false);
Then, in your accuracy study code, the call to perform the multiply does not change, but remains as:
my_mult.doit(input1,input2);
or
my_fixed_mult.doit(input1,input2);
Using this mechanism, our design style for some signal processing applications has been as follows:
Using this approach, the results from #1 vs. #2 should be identical
and easily checked. Similarly for #3 vs. #4.
| JHDL Home Page | Configurable Computing Lab | Prev (TBone) | |
JHDL 0.3.45
Copyright (c) 1998-2003 Brigham Young University. All rights reserved.
Last updated on 11 May 2006