Download from GitHub

TOOLS FOR OPEN RECONFIGURABLE COMPUTING

Examples

Refer to Getting Started for information on how to build the following examples:

torc/examples/ArchitectureExample.cpp

The architecture API example demonstrates basic use of the device database. It begins by initializing a DirectoryTree object with the invocation path of the executable. The location of the executable will serve as the reference point for all other directories, and in particular for the "devices" directory with all of its device databases.

A DeviceDesignator object is initialized for convenience with a particular Virtex6 device, package, and speed grade (the package and speed grade being optional), which it parses. Functions in the DeviceDesignator class allow access to the family, device, package, and speed grade.

The device database object is then constructed, using the designator object to determine which device and package to load. The appropriate database file is read from disk, and is used to populate all wiring, logic, and package information for the device.

Once the device database is initialized, the code looks up a particular logic site, and then looks up a particular site pin. That pin is returned as a "tilewire," a numeric combination of a wire index and a tile index, and is written to the output. The arcs that can connect this wire to other wires are output as well.


#include "torc/Architecture.hpp" #include "torc/Common.hpp" #include <iostream> using namespace torc::common; using namespace torc::architecture; using namespace torc::architecture::xilinx; int main(int argc, char* argv[]) { // construct and initialize the device database (void) argc; DirectoryTree directoryTree(argv[0]); DeviceDesignator designator("xc6vlx75tff484-1"); DDB ddb(designator); // look up a site output and convert it to a tilewire const Sites& sites = ddb.getSites(); SiteIndex index = sites.findSiteIndex("SLICE_X0Y119"); const Site& site = sites.getSite(index); Tilewire pinTilewire = site.getPinTilewire("A"); std::cout << ddb << pinTilewire << std::endl; // look up arcs that connect from this tilewire ArcVector sinks; ddb.expandSegmentSinks(pinTilewire, sinks); ArcVector::const_iterator pos = sinks.begin(); ArcVector::const_iterator end = sinks.end(); while(pos < end) std::cout << "\t" << *pos++ << std::endl; return 0; }

The tilewire is a lightweight encapsulation of an integer that lacks any knowledge of the current database. Its annotated output is made possible because the code first inserted the database object ddb into the output stream ("std::cout << ddb"). This insertion only needs to be done one time per stream. Without this insertion, the tilewire output would simply be the combination of its tile and wire indexes, for example "189@186".

WARNING: Need to check segment packing.
Reading device xc6vlx75t ("./torc/devices/xc6vlx75t.db")...
        Database 1.0.0 build 2, Vendor "Release 12.2 - xdl M.63c (lin64)"
        Reading family Virtex6 ("./torc/devices/Virtex6.db")...
        Reading 96 tile types...
        Reading wire info for 96 tile types...
        Reading 3 speed grades (-3, -2, -1) ... 
        Reading 2 packages (ff484, ff784) ...
        Reading tile map for 22733 tiles (127 rows x 179 columns)...
        Reading 22733 tiles...
        Reading 38074 segments...
        1673499 total segments
        Reading irregular arcs for 22733 tiles...
        Reading 51 site types...
        Reading 210 primitive pin maps...
        Reading 22509 sites...
Read 2566212 bytes from Virtex6
Read 14450208 bytes from xc6vlx75t
CLBLM_M_A@[1,7] CLBLM "CLBLM_X1Y119" (189@186) OUTPUT
        CLBLM_M_A@[1,7] CLBLM "CLBLM_X1Y119" (189@186) OUTPUT >> CLBLM_LOGIC_OUTS12@[1,7] CLBLM "CLBLM_X1Y119" (121@186)
        CLBLM_M_A@[1,7] CLBLM "CLBLM_X1Y119" (189@186) OUTPUT >> CLBLM_M_AMUX@[1,7] CLBLM "CLBLM_X1Y119" (197@186) OUTPUT

torc/examples/BitstreamExample.cpp

The bitstream API example reads a bitstream file specified on the command line, determined the correct architecture, reads and parses its packets into a new Bitstream object (which is itself a vector of packets), and writes those packets to the standard output.

#include "torc/Bitstream.hpp"
#include "torc/Common.hpp"
#include <fstream>
#include <iostream>

using namespace torc::common;
using namespace torc::bitstream;

int main(int argc, char* argv[]) {

    // we need an input bitstream
    if(argc != 2) {
        std::cout << "Usage: " << argv[0] << " bitstream.bit" << std::endl;
        exit(-1);
    }

    // read the bitstream
    boost::filesystem::path bitstreamPath = argv[1];
    BitstreamSharedPtr bitstreamPtr = Factory::newBitstreamPtr(bitstreamPath);
    // write the bitstream digest to the console
    std::cout << *bitstreamPtr << std::endl;

    return 0;
}

After the bitstream has been read and parsed, the bitstream object is "inserted" into the standard output. An output helper class formats the output as a sequence of bitstream packets, complete with symbolic bit field constants.

Design VirtexUnitTest.reference.ncd (v50bg256) @ 2011/01/26 11:51:59: 69900 bytes (17475 words)
    00000058: DUMMY
    0000005c: SYNC
    00000060: TYPE1 WRITE CMD RCRC
    00000068: TYPE1 WRITE FLR: 0000000b
    00000070: TYPE1 WRITE COR: 00803f2d (DonePipe:No, DriveDone:No, Capture:Continuous, ConfigRate:[UNKNOWN 2], 
StartupClk:Cclk, LOCK_WAIT:1, SHUTDOWN:0, DONE_cycle:4, LCK_cycle:NoWait, GTS_cycle:5, GWE_cycle:6, GSR_cycle:6) 00000078: TYPE1 WRITE MASK: 00000000 (Security:Protected, Persist:Protected, GTS_USER_B:Protected) 00000080: TYPE1 WRITE CMD SWITCH 00000088: TYPE1 WRITE FAR: 00000000 00000090: TYPE1 WRITE CMD WCFG 00000098: TYPE1 WRITE FDRI: 00000000 words 0000009c: TYPE2 WRITE FDRI: 00003e04 words 0000f8b0: TYPE1 WRITE FAR: 02000000 0000f8b8: TYPE1 WRITE FDRI: 0000030c words 000104ec: TYPE1 WRITE FAR: 02020000 000104f4: TYPE1 WRITE FDRI: 00000300 words 000110f8: TYPE1 WRITE CRC: 000018fa 00011100: TYPE1 WRITE CMD LFRM 00011108: TYPE1 WRITE FDRI: 0000000c words 0001113c: TYPE1 WRITE CMD START 00011144: TYPE1 WRITE CTL: 00000000 (Security:None, Persist:No, GTS_USER_B:IoDisabled) 0001114c: TYPE1 WRITE CRC: 0000e15a 00011154: [UNKNOWN TYPE 0] 00011158: [UNKNOWN TYPE 0] 0001115c: [UNKNOWN TYPE 0] 00011160: [UNKNOWN TYPE 0]

Bitfield constants can also be initialized symbolically for convenience, as is done in torc/bitstream/VirtexBitstreamUnitTest.cpp.

torc/examples/GenericExample.cpp

The generic API example reads an EDIF file, updates a property, and writes the file back out. Like the other examples, this one too begins by initializing the DirectoryTree. The code then specifies the two files it will work with, the first one already existing in the regression directory, and the second one to be used to output the modified design.

The code opens the reference file and imports the EDIF design into its object model. It then looks up a particular library, cell, and view, to find an instance of interest. The INIT property for that instance is changed from its previous value of "8" to "6", effectively turning this 2-input LUT from an AND gate into an XOR gate. The code then opens the output file and lets the exporter write out the resulting EDIF.


#include "torc/Generic.hpp" #include "torc/Common.hpp" #include <fstream> #include <boost/regex.hpp> using namespace std; using namespace torc::generic; int main(int argc, char* argv[]) { // build the file paths (void) argc; torc::common::DirectoryTree directoryTree(argv[0]); boost::filesystem::path referencePath = torc::common::DirectoryTree::getExecutablePath() / "regression" / "GenericExample.reference.edf"; boost::filesystem::path generatedPath = torc::common::DirectoryTree::getExecutablePath() / "regression" / "GenericExample.generated.edf"; // import the EDIF design string inFileName = referencePath.string(); fstream fileStream(inFileName.c_str()); ObjectFactorySharedPtr factoryPtr(new ObjectFactory()); EdifImporter importer(factoryPtr); importer(fileStream, inFileName); // look up an instance of interest RootSharedPtr rootPtr = importer.getRootPtr(); InstanceSharedPtr instancePtr = rootPtr->findLibrary("work")->findCell("and") ->findView("verilog")->findInstance("oZ0"); // change the INIT property (LUT mask) to XOR PropertySharedPtr initPropertyPtr = instancePtr->getProperty("INIT"); string originalMask = initPropertyPtr->getValue().get<Value::String>(); std::cout << "The original LUT mask was \"" << originalMask << "\"." << std::endl; Value xorMask(Value::eValueTypeString, string("6")); initPropertyPtr->setValue(xorMask); // export the EDIF design string outFileName = generatedPath.string(); fstream edifExport(outFileName.c_str(), ios_base::out); EdifExporter exporter(edifExport); exporter(rootPtr); return 0; }

This example generates little console output, but its effects can be seen with the following diff command:

diff regression/GenericExample.reference.edf regression/GenericExample.generated.edf

The expected output is:

47c47
< (property INIT (string "8"))
---
> (property INIT (string "6"))

torc/examples/PhysicalExample.cpp

The physical API example reads in an XDL file that must be specified by the user, looks up the design root, and simply writes the design back out. (A few XDL files are available in the regression directory.)

Because the input file is specified on the command line, there is no need to create and initialize a DirectoryTree object in this example. The code opens the input file, and then imports all of the XDL into its object mode. The root of that object model is obtained from the importer, and can be used to traverse or manipulate anything in the design.

Even though no changes are made to the design in this example, the code is written back out with a different extension.


#include "torc/Physical.hpp" #include <fstream> /// \brief Standard main() function. int main(int argc, char* argv[]) { // import the XDL design if(argc < 2) return 0; std::string inFileName(argv[1]); std::fstream fileStream(inFileName.c_str()); if(!fileStream.good()) return -1; torc::physical::XdlImporter importer; importer(fileStream, inFileName); // look up the design (and do something with it ...) torc::physical::DesignSharedPtr designPtr = importer.getDesignPtr(); // export the XDL design std::string outFileName = boost::filesystem::path(inFileName).replace_extension().string() + ".mod.xdl"; std::fstream xdlExport(outFileName.c_str(), std::ios_base::out); torc::physical::XdlExporter fileExporter(xdlExport); fileExporter(designPtr); return 0; }

Note that there is a subclass of torc::physical::XdlImporter named torc::architecture::XdlImporter. The architecture-aware importer identifies the design device, initializes the corresponding device database, and populates all wire usage information. Code like that in the example above, based on torc::architecture::XdlImporter, could serve as the basis for a stand-alone XDL router or placer.

This example generates no console output. Modifications to the design are left as an exercise to the reader.

torc/examples/EdifObfuscator.cpp

The EDIF obfuscator example reads an EDIF file specified by the user, and replaces all possible names and identifiers with MD5 hashes. Names and identifiers that are not replaced include those in any vendor libraries, as well as those belonging to the design's top-level ports.

The sample output below shows a simple EDIF design after obfuscation. The vendor VIRTEX library is not obfuscated, nor are the top-level ports of the design (the "and" cell in this case).

(edif (rename and "AND")
(edifVersion 2 0 0)
(edifLevel 0)
(keywordMap (keywordLevel 0))
(library VIRTEX
(ediflevel 0) (technology (numberDefinition))
(cell LUT2
(cellType GENERIC)
(view PRIM
(viewType NETLIST)
(interface
(port I0
(direction INPUT)
)
(port I1
(direction INPUT)
)
(port O
(direction OUTPUT)
)
)
)
)
)
(library work
(ediflevel 0) (technology (numberDefinition))
(cell
(rename and "AND")
(cellType GENERIC)
(view verilog
(viewType NETLIST)
(interface
(port i0
(direction INPUT)
)
(port i1
(direction INPUT)
)
(port o
(direction OUTPUT)
)
)
(contents
(instance icfcd208495d565ef66e7dff9f98764da
(viewRef PRIM (cellRef LUT2 (libraryRef VIRTEX)))
(property INIT (string ))
)
(net ic4ca4238a0b923820dcc509a6f75849b
(joined
(portRef i0)
(portRef I0 (instanceRef icfcd208495d565ef66e7dff9f98764da))
)
)
(net ic81e728d9d4c2f636f067f89cc14862c
(joined
(portRef i1)
(portRef I1 (instanceRef icfcd208495d565ef66e7dff9f98764da))
)
)
(net ieccbc87e4b5ce2fe28308fd9f2a7baf3
(joined
(portRef o)
(portRef O (instanceRef icfcd208495d565ef66e7dff9f98764da))
)
)
)
)
)
)
(design (rename and "AND") (cellRef and (libraryRef work))
(property PART (string "xc5vlx30ff324-1")
(owner "Xilinx"))
)
)