|   JHDL Home Page   |   Configurable Computing Lab   |   User's Manual (TOC)   | Search

Importing External Designs into JHDL

JHDL was designed to interact with other CAD tools and systems. Specifically, provision has been made to import EDIF files created by other tools into JHDL for use in JHDL designs. This section describes this functionality and consists of two parts:

The EDIF Parser

The EdifParser makes it possible for you to use an already existent EDIF file as a JHDL module. The EDIFParser will basically:
  1. Take the EDIF file and read it.
  2. Identify all the cells and elements in the EDIF file and collect information about those cells and elements.
  3. Translate those cells and elements in the EDIF file and translate them into JHDL cells.
  4. Finally it will build the actual JHDL cell so you can look at it using the JAB, simulate it, test it, etc.
To better explain how to use the EdifParser to bring in external designs into JHDL lets look at an example.

A 4-Bit Adder Example

To begin with, assume that you have an EDIF file out on disk (add.edn) which you know contains the following interface:

         (interface 
	   (port (array (rename a "a[3:0]") 4) (direction INPUT))
	   (port (array (rename b "b[3:0]") 4) (direction INPUT))
	   (port (array (rename q "q[3:0]") 4) (direction OUTPUT))
	 )

That is, it contains a 4-bit input named "a", a 4-bit input named "b", and a 4-bit output named "q". Further, assume that the EDIF is for a Xilinx Virtex design.

Here is a JHDL design which will read in the EDIF and treat it like any other JHDL module:

import byucc.jhdl.base.*;
import byucc.jhdl.Logic.*;
import byucc.jhdl.Xilinx.*;
import byucc.jhdl.Xilinx.Virtex.*;
import byucc.jhdl.parsers.edif.sablecc.*;

public class add_wrapper extends Logic {

  public static CellInterface[] cell_interface = {
    in( "a", 4),    
    in( "b", 4),    
    out( "q", 4),
  };

  Wire a; 
  Wire b; 
  Wire q;

  public add_wrapper(Node parent,
    Wire a,    
    Wire b,    
    Wire q
    ) {

    super(parent);
    this.a = connect( "a", a);
    this.b = connect( "b", b);
    this.q = connect( "q", q);

   EdifPortInterface[] portWires = {
   EdifPortInterface.addPort("a", a),
   EdifPortInterface.addPort("b", b),
   EdifPortInterface.addPort("q", q),
    };

    Cell cel = EdifParser.parse(this, "add.edn", portWires, "Virtex");
  } // end constructor
} // end class

Things of interest include the following:

  1. You must import the EDIF parsers package as shown on line 5 of the testbench code.
  2. You create a EdifPortInterface array to feed into the parse routine. These will be wired up to the ports of the EDIF design. The parser contains checking to ensure you pass in the right number of ports and wires and that all the various wire and port widths match up.
  3. As the parser attempts to recognize cells in the EDIF file, it needs to know the technology library to look in. The 4th parameter in the parse() call is the technology. The only strings currently allowed are "Virtex" and "XC4000".
Try the following:
  1. Copy the above code into a file called add_wrapper.java.
  2. Download the following EDIF file: add.edn into the same directory where you have your add_wrapper.java file.
  3. Compile the file and execute it using the jab:

       javac add_wrapper.java
       java jab add_wrapper 
  4. Now try testing some values. Do the following:
    1. Click on the add_wrapper icon in the Circuit Browser until the name add_wrapper is highlighted in color.
    2. Click on all the boxes under the "Tr" column on the right side of the Circuit Browser. This will bring up the waveform viewer.
    3. Try some values for a and b : Click in the command box at the bottom of the window in the waveform viewer and type:

       
           put a 3  
           put b 4 
      

      Then click the cycle button and the window looks like this:

That's it! This mechanism can be used anywhere in any JHDL design to import an EDIF design and treat it otherwise like a JHDL module.

OK, so maybe there is more...

Automatically Generating a JHDL Wrapper for an EDIF File

An easy way to create the wrapper module shown above for importing EDIF is to let the tools automagically generate one for you. Execute the following:
     
     java byucc.jhdl.parsers.EDIF.sablecc.BuildWrapper -t Virtex add.edn

and it will parse "add.edn" and create the file "add_wrapper.java" shown below which you can use instead of writing your own wrapper by hand:

// Automatically generated EDIF wrapper...
import byucc.jhdl.base.*;
import byucc.jhdl.Logic.*;
import byucc.jhdl.Xilinx.*;
import byucc.jhdl.Xilinx.Virtex.*;
import byucc.jhdl.parsers.edif.sablecc.*;

public class add_wrapper extends Logic {

  public static CellInterface[] cell_interface = {
    in( "a", 4),
    in( "b", 4),
    out( "q", 4),
  };

  Wire a;
  Wire b;
  Wire q;

  public add_wrapper(Node parent,
    Wire a,
    Wire b,
    Wire q
    ) {

    super(parent);
    this.a = connect( "a", a);
    this.b = connect( "b", b);
    this.q = connect( "q", q);

    // Changes to make below if there is a clock port called "clk" on the imported EDIF and you want
    //   to wire it to the default global clock:
    //      EdifPortInterface.addPort("clk", getDefaultClock());

    // Alternatively, if your wrapper has a port called "clk" and you want
    //   to hook it to a clock driver of your own making:
    // Put these two lines in:
    //   Wire myclk = wire(1, "myclk");
    //   clockDriver("clk", "myclk", "01", "clockDriver");
    // Now, change the EdifPortInterface.addPort() below to be:
    //      EdifPortInterface.addPort("clk", myclk);

    EdifPortInterface[] portWires = {
     EdifPortInterface.addPort("a", a),
     EdifPortInterface.addPort("b", b),
     EdifPortInterface.addPort("q", q),
    };

    Cell cel = EdifParser.parse(this, "add.edn", portWires, "Virtex");


  } // end constructor

} // end class

Notice also that when you executed the command

     
     java byucc.jhdl.parsers.edif.sablecc.BuildWrapper -t Virtex add.edn

you got something like this:

There are a few things to be mention about this:
  1. If you look at the add.edn file, you will see in line 5 the following:

      (status
        (written
          (timeStamp 2000 6 29 15 2 7)
          (program "BYU-CC's JHDL-EDIF netlister by Peter Bellows and Eric Blake"
            (version "JHDL.RELEASE.BRENT.6.28"))))
    
    

    When you parse this EDIF file, the parser gives you a WARNING saying that the element (status) of the EDIF file has been ignored by the parser. It also tells you that if you want to see the full description of this element as it appears in the EDIF file you only need to add the following line to your add_wrapper.java file:

        
         byucc.jhdl.JHDLOutput.getJHDLOutput("EDIF_ignore_loud"); 
    

    Try adding this line of code to your add_wrapper.java file right before the call to the EDIF parser:

           ......
             byucc.jhdl.JHDLOutput.getJHDLOutput("edif_ignore_loud");
             Cell cel = EdifParser.parse(this, "add.edn", portWires, "Virtex");
           ......
    

    Now compile and execute the file:

     
         javac add_wrapper.java
         java jab add_wrapper
    

    By having added that line of code now you have enabled the JHDLOutput and the warning given by the parser will now look something like this:

  2. The purpose of this warning is for you to know what the parser is ignoring and decide whether or not that is something you need in order for your design to work properly.

Don't Forget the Clock Port!

JHDL designs can be done in a way that the clock is hidden - it is implied in every cell which needs it. If you want to parse in such a design that JHDL generated, make no mistake about it - the CLK port really is there (check the EDIF file). In this case, you must hook a wire to it.

In the following automatically generated wrapper, you will see that the tool saw that the EDIF had a clock port and so created a clock port on the wrapper's interface as well as created a PortInterface entry for it:

...
public class ramtest_wrapper extends Logic {

  public static CellInterface[] cell_interface = {
    in( "a", 4),
    in( "clk", 1),
    in( "d", 1),
    out( "q", 1),
    in( "we", 1),
  };

  ...

    // Changes to make below if there is a clock port called "clk" on the imported EDIF and you want
    //   to wire it to the default global clock:
    //       EdifPortInterface.addPort("clk", getDefaultClock());

    // Alternatively, if your wrapper has a port called "clk" and you want
    //   to hook it to a clock driver of your own making:
    // Put these two lines in:
    //   Wire myclk = wire(1, "myclk");
    //   clockDriver("clk", "myclk", "01", "clockDriver");
    // Now, change the EdifPortInterface.addPort() below to be:
    //       EdifPortInterface.addPort("clk", myclk);

   EdifPortInterface[] portWires = {
     EdifPortInterface.addPort("a", a),
     EdifPortInterface.addPort("clk", clk),
     EdifPortInterface.addPort("d", d),
     EdifPortInterface.addPort("q", q),
     EdifPortInterface.addPort("we", we),
    };

    Cell cel = EdifParser.parse(this, "ramtest.edn", portWires, "Virtex");


  } // end constructor

} // end class

However, you will see that it also suggested how you might hook a clock to that port, depending on what you wanted to do. The first suggestion simply hooks the clock up to the global default clock. The second lets you insert a ClockDriver and specify the clock's waveform.

As an alternative to doing this in the wrapper, you could write a testbench to directly import the EDIF. Below is a complete testbench to load the EDIF for a CLB RAM test circuit and read all 16 locations. When executed in JAB, one can see the successive values of the memory coming out on the "q" port.

import byucc.jhdl.Logic.*;
import byucc.jhdl.Xilinx.*;
import byucc.jhdl.base.*;
import byucc.jhdl.Xilinx.Virtex.*;
import byucc.jhdl.parsers.edif.sablecc.*;

public class tbe_ramtest extends Logic implements TestBench {
  public static void main(String argv[]) {
    HWSystem hw = new HWSystem();
    tbe_ramtest tb = new tbe_ramtest(hw);
    hw.cycle(iterations);
  }

  // Here are the addresses
  static final int[] a_data = {
    0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0,
  };

  Wire d;
  Wire we;
  Wire a;
  Wire q;

  public void reset() {
    count = 0;
    d.put(this, 0);
    we.put(this, 0);
    a.put(this, 0);
  }

  static final int iterations = 20;
  int count = 0;

  public void clock() {
    d.put(this, 0);
    we.put(this, 0);
    a.put(this, a_data[count%iterations]);
    count++;
  }

  public tbe_ramtest(Node parent) {
    super(parent);
    VirtexTechMapper tm = new VirtexTechMapper( true );
    setDefaultTechMapper( tm );
    d  = wire(1, "d");
    we = wire(1, "we");
    a  = wire(4, "a");
    q  = wire(1, "q");

    EdifPortInterface[] ports = {
      EdifPortInterface.addPort("d", d),
      EdifPortInterface.addPort("we", we),
      EdifPortInterface.addPort("a", a),
      EdifPortInterface.addPort("q", q),
      EdifPortInterface.addPort("clk", getDefaultClock()),
    };

    Cell my_ramtest = EdifParser.parse(this, "ramtest.edn", ports, "Virtex");
  }
}

Try the following

  1. Copy the above code into a file called tbe_ramtest_.java.
  2. Download the following EDIF file: ramtest.edn into the same directory where you have your tbe_ramtest.java file.
  3. Compile executing the above code
         javac tbe_ramtest.java
         java jab tbe_ramtest
    

  4. Click on the ramtest icon in the Circuit Browser until the name ramtest is highlighted in color.
  5. Click on all the boxes under the "Tr" column on the right side of the Circuit Browser. This will bring up the waveform viewer.
  6. Using the waveform viewer type
     
         cycle 20 
    

    in the command line and you will see something like this:

Dealing With Designs With Pads

In general, you will want to import designs that have a top level interface and insert them into your JHDL design. However, the EDIF parser has the ability to deal with designs with pads.

Building Wrappers For Designs With Pads

The BuildWrapper mechanism described above will detect that no top-level ports exist on a design. In this case it will do the following: In common cases this will correctly remove the pad ring from the design. However, there are cases where it will fail. Specifically, if a clock signal goes through a CLKDLL and then into a BUFG, the output of the CLKDLL will be the input port. If the signal went through an IBUF before going into the CLKDLL, the output of the IBUF will also be n input port. Thus, there will be cases where you may want to modify the wrapper after it is generated. However, the goal is for this to get you close to the final design you want.

Actually Importing Designs With Pads

If you attempt to parse an EDIF file and construct the JHDL circuit for it and the EDIF file contains pads, the tool will attempt something similar to above. It will throw away all pads and try to create a top level interface based on the combination of buf's it sees. In this case, however, once it does this it will write out a new EDIF file without the pads, print a warning message that it has done so and suggest you import that EDIF file instead, and exit.

An Example of Importing a Circuit With Pads

Let's assume that the RAMTEST circuit shown above was created by some CAD tool but with pads. Let's assume it is in a file called "ramtest_pads.edn".

Step 1 - Build A Wrapper For It

By executing:
    
     java byucc.jhdl.parsers.edif.sablecc.BuildWrapper ramtest_pads.edn

The file "ramtest_pads_wrapper.java" will be created. This design has a clock port on it. There will be a comment in "ramtest_pads_wrapper.java" showing how you might want to modify the design to add a clock driver to the circuit in case it was going to be your top-level design. You should read the comment to understand how to do that.

Step 2 - Import the JHDL

Compile and execute "ramtest_pads_wrapper.java":
    
     javac ramtest_pads_wrapper.java 
     java jab ramtest_pads_wrapper

This will:

Step 3 - Modify the Wrapper and Import the JHDL Again

After modifying the wrapper file to parse this newly generated EDIF file, compile it and execute it:
        ... modify the wrapper ... 
     javac ramtest_pads_wrapper.java 
     java jab ramtest_pads_wrapper

NOTE: if you used an automatically-generated wrapper you will see that it has many 1-bit ports as in: "a_0_", "a_1_", etc. (this is what happens when pads are thrown away). However, the EDIF parser on import will always merge these into a wide port by looking at the indices. Thus, when you modify the wrapper you will want to replace all these by a single port and wire named "a". Otherwise, you will get an error.

Other Information - Wire Widths

The widths of the wires must match the widths in the EDIF file. The width of the ports from the EDIF netlist is determined by the bus reconstruction algorithm. To reconstruct the buses, the EDIFParser looks at the names of the ports. Moving from the rear of the name, the parser looks for a number to indicate its position in the bus. For example, port in2 has a 2 at the end of the name. This port will be placed in a multi-width port called in with an index of 2. Any port that does not end in an integer will not be reconstructed and remain a single bit port. To prevent invalid ports from being created, the parser counts the number of ports placed in a multibit port. If this count does not match the index of the largest element of the port, each element is created as a single bit port. For example, an EDIF file has a declaration with the following interface. In addition the EDIF parser can reconstruct buses based on bus delimiters such as (, {, or [. The ports in[0] and in[1] will create a 2 bit port in.

    (interface
     (port in0 (direction INPUT))
     (port in1 (direction INPUT))
     (port in2 (direction INPUT))
     (port in3 (direction INPUT))
     (port out0 (direction OUTPUT))
     (port out3 (direction OUTPUT))
    )

the ports in0 - in3 will be reconstructed into a 4 bit port called in. The other 2 ports will attempt to reconstruct a 2 bit port out. However, since the number of ports is less than the greatest port index, the ports will remain single bit ports with their given names. The final reconstructed port interface for this EDIF section is shown in the following JHDL code.

  
    public static CellInterface[] cell_interface{
     in( "in", 4 );
     out( "out0", 1);
     out( "out3", 1);
   }

Specifying a Specific Subcell

If the desired cell to be parsed is not actually the top-level cell of the EDIF file, you may specify that particular cell. To specify a subcell of the EDIF file, use the overloaded parse method of the EdifParser for that purpose. This method is just like the other parse method mentioned at the top of this page, except that the name of the subcell comes after the name of the EDIF file.

For example:

 
    Cell cel = EdifParser.parse(this, "add.edn", "subcellname", portWires, "Virtex"); 

This will parse the file add.edn and generate a JHDL cell representation of the file. Then it will search through the children of the top-level cell until it finds one with the name "subcellname"; the EdifParser will then return that subcell. If the EdifParser cannot find a cell by that name, a NullPointerException is thrown.

This feature can be used with the BuildWrapper class as well. Simply use the -sub switch followd by the name of the subcell to be targetted.

The JHDL Netlister

The JHDL Netlister is a tool that enables a user to netlist the components of a design into machine-generated JHDL code. The cell hierarchy is read, starting with the top-level cell, and the top cell and each child cell are written out into a separate .java file. The entire hierarchy is netlisted in this fashion, with one cell per .java file. The cell's name is used as the filename for each .java file by default. Any wires needed to connect the cells together are also netlisted in their respective parent cells.

For example, a common scenario is to use a schematic capture tool to design a cell. The CAD tool usually has an option to netlist the design to an EDIF file, targeted to a certain library such as Xilinx XC4000 or Virtex. The user may then use the JHDL Netlister to translate the EDIF file to JHDL files, compile the JHDL, and then use the cells by instantiating the cells in JHDL source code that the user has written. In this manner, the JHDL Netlister can be used to import designs from other CAD tools to JHDL.

Syntax

A java wrapper program enables the user to netlist into JHDL from an EDIF file at the command line. To run the JHDL netlister, type


      java byucc.jhdl.netlisters.jhdl.BuildJHDL < -t target > my_EDIF_file.edf

where 'target' is the architecture that the EDIF file was synthesized against. Current valid options for 'target' are:

      XC4000
      Virtex
      Virtex2

For example, to netlist an EDIF file called div3by3_100.edf, which was targeted to Virtex, the user should type:

     java byucc.jhdl.netlisters.jhdl.BuildJHDL -t Virtex div3by3_100.edf

Note: if a target architecture is not specified, the current default is Virtex.

Note about large EDIF files: It is likely that for large EDIF files, java will run out of heap memory. To avoid this error, the user should specify on the command line for java that increases java's memory limit. For example:

For Java 1.1.x:

     java -mx128M byucc.jhdl.netlisters.jhdl.BuildJHDL -t Virtex div3by3_100.edf

For Java 1.2.x or 1.3.x:

     java -Xmx128M byucc.jhdl.netlisters.jhdl.BuildJHDL -t Virtex div3by3_100.edf

These examples show running the JHDL Netlister with a maximum heap memory size of 128 megabytes.

Advanced

It is possible to use the JHDL netlister in the same way that other netlisters are used from a JHDL testbench. The syntax is very similar to other netlisting functions. Please read the documentation on Test Benches for more information on this topic. The syntax for netlisting to JHDL from a testbench looks like this:

------- tb_MyCell.java -------- 
 
        Cell my_cell;                        // Cell to be netlisted 
 
        ... 
 
        public tb_MyCell( ... ){             // testbench constructor 
            JHDLNetlistWriter my_writer = new JHDLNetlistWriter( my_cell, "my_cell.java" );     //Instantiate the netlister 
            my_writer.writeTree();                                                              // actually output JHDL 
 
        ... 
 
------- tb_MyCell.java -------

Known Bugs

The JHDL netlister is still under development. It has been tested with small and medium-sized designs and has netlisted accurately. However, on large designs with many wires, a naming conflict sometimes appears that creates multiple instances of a wire, or references a wire in a JHDL cell constructor call that has not been instantiated. In this case, the user must fix the naming discrepancy by hand in the generated source code.

The JHDL code that is generated is decidedly ugly. Eventually, the code will hopefully be more human-readable.



|   JHDL Home Page   |   Configurable Computing Lab   |   Prev (FSM Generators)   |   Next (Logic Revisited)   |


JHDL 0.3.45