![]() |
|
|
PAGE SUMMARY |
---|
XNF (Xilinx Netlist Format) is a netlist file format generated by certain Xilinx programs such as Synopsis/FPGA Express. This netlist file is a description of how Xiling FPGA primitives, or pseudo-primitives, are wired up for a particular circuit. We can easily take a design written in VHDL and generate XNF files.
XNF files by themselves don't do us much good in the world of JHDL. XNF is a Xilinx-specific, proprietary format. (Whereas, JHDL has been designed to be relatively platform-unspecific.) It is also a format that Xilinx has essentially abandoned (i.e. it is not being supported anymore) even though Xilinx software still generates it. So we have sufficient motivation to avoid directly using XNF files. Therefore, we have created a parser that takes an .xnf file and generates JHDL components based on the netlist description found in that file. This JHDL circuit can then be integrated within any other JHDL circuit that you may wish to include with it.
This tutorial will describe how to use the XNF to JHDL parser. The parser is simple to use. There are two easy steps:
To set up your JHDL file to use the XNFParser, you need to import the XNFParser class. Just include the following at the top of your XNF file:
import byucc.jhdl.parsers.xnf.XNFParser;
In addition, if you are using the XNFPortInterface class, you need to import that as well:
import byucc.jhdl.parsers.xnf.XNFPortInterface;
Then again, Java, in all of its coolness, will allow you to just import an entire package, so you don't have to import both classes individually:
import byucc.jhdl.parsers.xnf.*;
However, if you import the entire package, be aware that there is a public class in the xnf package called Node. There is also a public class called Node in the byucc.jhdl.base package. That means that if you also import the whole byucc.jhdl.base package, then you must specify which Node class you are using if you happen to use either one. Importing only the few specific classes that you need will help you to avoid these types of conflicts when you compile your .java files.
At the end of an .xnf file, you will find several lines similar to the following:
SIG, A, PIN=A SIG, B, PIN=B SIG, CLK, PIN=CLK SIG, O, PIN=O EOF
Each line that starts with SIG tells you what wires are on the top level of the XNF circuitry. That is, there may be intermediate wires connecting the components in the middle of the circuit, but these wires are going to be the only inputs and outputs to the overall circuit. You should know your design well enough to tell which of these wires are inputs and which are outputs so that you can properly use them in the circuit outside of the XNF-defined circuit.
Since these wires will connect the XNF-defined circuit to the rest of your circuit, the XNFParser will not attempt to create these wires. That is because these wires may be the outputs or inputs of some other components in your circuit; also, it would be unecessarily tricky to have the parser create these wires, and then give them to you to wire up to your circuit. (That would require you to do a lot of extra work that you really don't want to do, e.g. extracting Wire objects from something like a hashtable.)
Therefore, you must create these wires yourself. Then you must prepare them in such a way that the XNFParser can use them. The reason for this is that the XNFParser must associate a name with each wire that you give it to hook up to its circuit so that it knows where to make those connections.
NOTE(1): In considering the naming of wires, you need not worry about case-sensitivity; the XNFParser will convert all of the wire names to lower-case when doing name comparisons.
NOTE(2): As we discuss the names of JHDL Wire instances, we are not referring to the Java variable names that refer to the instances. For example you might make the following declaration of a wire: Wire MyAInput = wire(1, "a");. In this case, the name of the Wire is actually "a," NOT "MyAInput", because MyAInput is just the Java variable name that points to the instance of Wire that has the name "a."
There are three options for supplying wires to the XNFParser:
If every wire that you give to the XNFParser already has the exact name (disregarding case) that the XNF file shows, then the XNFParser will be able to use the getUserName method of the JHDL Wire class to determine which wire it is. So, if you are sure that each wire has the name corresponding to the XNF file's wire names, then you can simply give the XNFParser an array of Wire (Wire[]) that contains the wires for your .xnf file. The instances of Wire in the array can appear in any order (i.e. they need not be in the same order as they appear in the .xnf file).
Here's how your would set up your array of Wire if your wires were a, b, clk, and o (this assumes that you have imported byucc.jhdl.base.*, or at least byucc.jhdl.base.Wire):
Wire[] MyXNFWires = { a, b, clk, o };
If you are not sure that each wire that you will pass to the XNFParser will have the same name as the top-level wires in the .xnf file, you can use two arrays: a String[] and a Wire[]. This could be handy if a wire to be connected to the XNF-defined circuit is the output of some other important component of your overall circuit, and you called that wire "ImportantOutputWire" so that you could find it more easily in JAB's Schematic Viewer or Circuit Browser. (Likewise, an output from the XNF-defined circuit could be the input to some other important component, e.g. "ImportantInput".)
The XNFParser will associate the name in the first element of the array of String with the first element in the array of Wire. Likewise for the second element of each array, and so on. Naturally, the two arrays should have the same size, or you will most likely end up with an ArrayIndexOutOfBoundsException, since the XNFParser does not do this kind of error checking (for greater simplicity and speed).
Here's an example of how you might make your two arrays:
String[] MyXNFWireNames = { "a", "b", "clk", "o" }; Wire[] MyXNFWires = { MyAWire, BWire, AClockWire, OutputWire };
Another option available to you is to use the XNFPortInterface class. This class is really simple. It has two public member variables: a String and a Wire. This makes it an adequate class to group together a Wire and a String in order to associate the Wire with the String for its name.
You can do something similar to the following to set up your array of XNFPortInterface:
XNFPortInterface[] MyXNFPorts = { new XNFPortInterface("a", a), new XNFPortInterface("b", b), new XNFPortInterface("clk", clk), new XNFPortInterface("o", o) };
Sometimes you will see SIG lines in XNF files that look like the following:
SIG, even, PIN=even SIG, data<6>, PIN=data<6> SIG, data<5>, PIN=data<5> SIG, data<4>, PIN=data<4> SIG, data<3>, PIN=data<3> SIG, data<2>, PIN=data<2> SIG, data<1>, PIN=data<1> SIG, data<0>, PIN=data<0> SIG, parity_value, PIN=parity_value EOF
These SIG lines include several references to wires named "data" which also have a particular index associated with them. This is because all of the wires in the XNF files are atomic (i.e. there are no bus wires--wires of width greater than one). But the original design from which the XNF file was generated obviously had just one 7-bit wire named data; the Xilinx software simply broke the bus wire down into individual wires referenced with indices. To more closely resemble the original design, the XNFParser attempts to merge wires of the same name into a single bus wire. That means that you should provide the XNFParser a single multi-bit wire instead of several single-bit wires. For an example of this see MultipleXNF.java.
To invoke the XNFParser, simply use the parseXNF
method of the XNFParser class. parseXNF
is a
static public method of the XNFParser class. It is also
overloaded, that is, there are several different versions of it
to accomodate different users' needs. Here are the different
versions of the the parseXNF
method:
public static void parseXNF(Cell parent, Object[] ports, String fileName) public static void parseXNF(Cell parent, Object[] ports, String fileName, boolean pushHierarchy) public static void parseXNF(Cell parent, String[] wireNames, Wire[] xnfWires, String fileName) public static void parseXNF(Cell parent, String[] wireNames, Wire[] xnfWires, String fileName, boolean pushHierarchy)
Here's a description of the parameters used in these methods:
parent | This is going to be the parent of the components built by
the XNFParser. Typically this parameter will just be
this |
---|---|
ports | This is an array of Object. This array must actually be either an array of Wire or an array of XNFPortInterface. This array would be the array that your set up for either method one or three as described in the "Setting up the Wires" section above. If you use an array of any type of Object besides Wire or XNFPortInterface, or if your array contains a combination of Wire and XNFPortInterface, then the XNFParser will not work correctly. (Most likely it will simply die.) |
fileName | This is the name of the .xnf file that the XNFParser will parse. It's OK if you omit the .xnf extension, but the XNFParser still expects the actual file name to contain that extension, otherwise, the XNFParser will fail. |
pushHierarchy | If this boolean is true, then the XNFParser will parse the
XNF file and then create another level of hierarchy in which
the XNF-defined circuit will be placed. To do that, it
creates a new Cell that is the child of parent,
which will be the parent of the XNF-defined circuitry. What
this means to you, is that in JAB's Circuit Browser and
Schematic Viewer, you will see a Cell enclosing the XNF
circuitry in its own little package. If you want to
actually see that circuitry, you will have to click into
that Cell as well. If you use a version of
parseXNF that does not have this parameter,
then the XNFParser will not create a new level of
hierarchy. |
wireNames | This array of String is the array that tells the XNFParser which wires are which in the xnfWires array. This array is what you would have set up had you used method two described in "Setting up the Wires" above. |
xnfWires | This array of Wire contains the wires that the XNFParser will connect to the top-level wires of the XNF-defined circuit. The names of the wires should be defined in wireNames as described in the section "Setting up the Wires" above. |
To use the parseXNF
method, simply do something
similar to the following:
XNFParser.parseXNF(this, myWireArray, "myfilename.xnf");
Included here are some simple examples of how to use the XNFParser.
SimpleXNF.java
test1.xnf |
These files show the simplest case of how to use the XNFParser. |
---|---|
SimpleXNFWPush.java
test1.xnf |
These files are the same as the previous example, except that the call to parseXNF also includes the boolean to tell the parser to create another level of hierarchy. |
MultipleXNF.java
test1.xnf parity.xnf |
These files show how to integrate more than one .xnf file into one design. This example shows the use of two different versions of the parseXNF method, including using the XNFPortInterface class, two arrays, and pushHierarchy. |
For questions about the XNFParser, <SEND EMAIL TO AUTHOR>
Table of Contents
Here is one example of how to check for multiple cells driving the same wire in a circuit:
import byucc.jhdl.DRC.*;
import byucc.jhdl.DRC.Rules.*;
...
MultiplePutsCircuit my_MultiplePutsCircuit = new MultiplePutsCircuit(this,
i, explicit_clock, o);
DesignRuleChecker.checkRule(this, my_MultiplePutsCircuit, new MultipleDrivers());
MultiplePutsCircuit is a circuit that has a problem with multiple puts. MultipleDrivers is a design rule that checks for multiple puts.
This will report to stdout if 2 or more cells (that are not tri-state buffers) are driving the same wire in a circuit. In the theoretical situation where the designer would like to drive the same wire with two or more non-tri-state buffer cells, he would specify whether or not he would like to apply this rule to his circuit.
Quick-start Example Explained in a Nutshell
The my_MultiplePutsCircuit object is a circuit with 2 different cells both driving the same wire. DesignRuleChecker is referenced statically, and the MultipleDrivers class is an extension of the DesignRule class. It implements the sortCells() method and the doCheck() method. The sortCells() method checks every descendent of the instance of MultiplePutsCircuit for output ports, and it keeps a list of wires connected to each output port. The doCheck() method compares all the sources of these wires with each other and fails if there are two or more source cells for any given wire and at least one of those source cells is not a tri-state buffer. In this case, since there are two fdce() components driving the same wire, the rule reports a failure.
Quick-start Example with Multiple Rules
If you wish to apply multiple rules in one run, place an instance of all rules in a Vector object and use the Vector object as the parameter replacing the DesignRule object:
import java.util.Vector;
import byucc.jhdl.DRC.*;
import byucc.jhdl.DRC.Rules.*;
...
MultiplePutsCircuit my_MultiplePutsCircuit = new MultiplePutsCircuit(this,
i, explicit_clock, o);
Vector rules = new Vector();
rules.add(new MultipleDrivers());
rules.add(new BufgDR());
rules.add(new IOBufs());
DesignRuleChecker.checkRule(this, my_MultiplePutsCircuit, rules);
The quick-start example is one way to run design rules against your circuit. It uses static methods to perform the operation. The static methods in the DesignRuleChecker class basically do what it described here, using the default settings for the checker (none of the variables in any of the design rules or the design rule checker are set). If you want more control over the checking process, you should follow the instructions given here.
To use the Design Rule Checker, make an instance of the DesignRuleChecker class. Then, successively call the registerDesignRule(DesignRule dr) method for each design rule you would like applied to your circuit. Set any variables in the DesignRuleChecker or the individual rules to your liking (more often then not, you will not have any to set). Now call the doCheck() method.
To get a String containing the results of the test(s), call
getReport().
There are more utilities provided to manage design rules and violations
(including a graphical violations browser). These utilities are covered
in detail the API documentation for the DesignRuleChecker.
The Graphical Design Rule Browser
To view the results of a rule check graphically, call DesignRuleChecker.checkRule() with an extra parameter "true":
import byucc.jhdl.DRC.*;
import byucc.jhdl.DRC.Rules.*;
...
MultiplePutsCircuit my_MultiplePutsCircuit = new MultiplePutsCircuit(this,
i, explicit_clock, o);
DesignRuleChecker.checkRule(this, my_MultiplePutsCircuit, new MultipleDrivers(),
true);
This will launch a frame and display a tree of all violations to the rule.
You may also launch the DesignRuleBrowser independently from the command line with this command:
java byucc.jhdl.DRC.DesignRuleBrowser
You will be able to specify the full package name for a class containing a circuit you have designed. All available rules will be listed, and you can choose which rules you wish to apply to your circuit. After you run the check, the browser provides you with a list of registered violations.
To write your own design rule, extend the DesignRule class and implement the sortCells() and doCheck() methods. You can start from Template.java, which is a fully-implemented design rule that can be compiled and run through the DesignRuleChecker on any circuit:
package byucc.jhdl.DRC.Rules;
import byucc.jhdl.DRC.*;
import byucc.jhdl.base.*;
public class Template extends DesignRule {
public Template() {
name="";
description="";
oneLineDescription="";
}
/* This method will be called several times by the DesignRuleChecker
* class, once for the top-level cell and once for
each of the
* descendents of the top-level cell.
*/
public void sortCell(Cell cl) {
}
/* This method returns true if the collection of cells conforms
to the
* design rule specification, false otherwise.
*/
public boolean doCheck() {
failureReport="";
return true;
}
}
As you can see, this rule is not particularly useful. The user may fill in the code to make this rule do what he would like.
If your design rule checks for parts that are unique to any given platform (i.e., XC4000 or Virtex), then you should make your rule abstract. The methods that identify parts unique to any given platform should be abstract. You should then write the code used to identify the parts in question in classes that extend your abstract rule.
These classes are the ones that you will use as the design rule for your checker when you are ready to check your circuit. Any rule pulled from that location (byucc.jhdl.DRC.Rules) should function on any circuit, regardless of the platform. If a rule requires platform-specific checks, then the rule in byucc.jhdl.DRC.Rules should be abstract, and the implementation should exist in a package such as byucc.jhdl.DRC.Rules.Virtex or byucc.jhdl.DRC.Rules.XC4000.
Emphasizing the point - there should be no rules that refer to any specific architecture in the byucc.jhdl.DRC.Rules package.
| JHDL Home Page | Configurable Computing Lab | Prev (General Issues) | Next (JAB) | |
JHDL 0.3.45
Copyright (c) 1998-2003 Brigham Young University. All rights reserved.
Last updated on 11 May 2006