Moved stand-alone libs to libs/ directory and added libs/subcircuit

This commit is contained in:
Clifford Wolf 2013-02-27 09:32:19 +01:00
parent 4f0c2862a0
commit a321a5c412
39 changed files with 2776 additions and 9 deletions

View File

@ -3,8 +3,13 @@ CONFIG := clang-debug
# CONFIG := gcc-debug
# CONFIG := release
OBJS = kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/sha1.o kernel/calc.o kernel/select.o kernel/show.o
OBJS += bigint/BigIntegerAlgorithms.o bigint/BigInteger.o bigint/BigIntegerUtils.o bigint/BigUnsigned.o bigint/BigUnsignedInABase.o
OBJS = kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/select.o kernel/show.o
OBJS += libs/bigint/BigIntegerAlgorithms.o libs/bigint/BigInteger.o libs/bigint/BigIntegerUtils.o
OBJS += libs/bigint/BigUnsigned.o libs/bigint/BigUnsignedInABase.o
OBJS += libs/sha1/sha1.o
OBJS += libs/subcircuit/subcircuit.o
GENFILES =
TARGETS = yosys

View File

@ -27,7 +27,7 @@
*/
#include "kernel/log.h"
#include "kernel/sha1.h"
#include "libs/sha1/sha1.h"
#include "ast.h"
#include <sstream>

View File

@ -27,7 +27,7 @@
*/
#include "kernel/log.h"
#include "kernel/sha1.h"
#include "libs/sha1/sha1.h"
#include "ast.h"
#include <sstream>

View File

@ -27,7 +27,7 @@
*/
#include "kernel/log.h"
#include "kernel/sha1.h"
#include "libs/sha1/sha1.h"
#include "ast.h"
#include <sstream>

View File

@ -29,7 +29,7 @@
#include "verilog_frontend.h"
#include "kernel/register.h"
#include "kernel/log.h"
#include "kernel/sha1.h"
#include "libs/sha1/sha1.h"
#include <sstream>
#include <stdarg.h>
#include <assert.h>

View File

@ -18,7 +18,7 @@
*/
#include "kernel/rtlil.h"
#include "bigint/BigIntegerLibrary.hh"
#include "libs/bigint/BigIntegerLibrary.hh"
#include <assert.h>
static BigInteger const2big(const RTLIL::Const &val, bool as_signed, int &undef_bit_pos)

52
libs/subcircuit/Makefile Normal file
View File

@ -0,0 +1,52 @@
CONFIG := clang-debug
# CONFIG := gcc-debug
# CONFIG := profile
# CONFIG := release
CC = clang
CXX = clang
CXXFLAGS = -MD -Wall -Wextra -ggdb
LDLIBS = -lstdc++
ifeq ($(CONFIG),clang-debug)
CXXFLAGS += -std=c++11 -O0
endif
ifeq ($(CONFIG),gcc-debug)
CC = gcc
CXX = gcc
CXXFLAGS += -std=gnu++0x -O0
endif
ifeq ($(CONFIG),profile)
CC = gcc
CXX = gcc
CXXFLAGS += -std=gnu++0x -Os -DNDEBUG
endif
ifeq ($(CONFIG),release)
CC = gcc
CXX = gcc
CXXFLAGS += -std=gnu++0x -march=native -O3 -DNDEBUG
endif
all: demo scshell
demo: demo.o subcircuit.o
scshell: scshell.o subcircuit.o
test: scshell
./scshell < test_macc22.txt
perl test_perm.pl | ./scshell
splrun test_shorts.spl | ./scshell
splrun test_large.spl | ./scshell
clean:
rm -f demo scshell *.o *.d
.PHONY: all test clean
-include *.d

440
libs/subcircuit/README Normal file
View File

@ -0,0 +1,440 @@
**************************************************************************
* *
* The SubCircuit C++11 library *
* *
* An implementation of a modified Ullmann Subgraph Isomorphism Algorithm *
* for coarse grain logic networks. by Clifford Wolf *
* *
**************************************************************************
============
Introduction
============
This is a library that implements a modified Ullmann Subgraph Isomorphism
Algorithm with additional features aimed at working with coarse grain logic
networks.
A simple command line tool that exposes the features of the library is also
included.
Under-Construction Warning
--------------------------
This work is under constructions. It is likely that they are bugs in the
library that need fixing. Feel free to contact me at clifford@clifford.at
if you have found a bug.
C++11 Warning
-------------
This project is written in C++11. Use appropriate compiler switches to compile
it. Tested with clang version 3.0 and option -std=c++11. Also tested with gcc
version 4.6.3 and option -std=c++0x.
========
Features
========
The input is two graphs (needle and haystack) that represent coarse grain
logic networks. The algorithm identifies all subgraphs of haystack that are
isomorphic to needle.
The following additional features over the regular Ullmann Subgraph Isomorphism
Algorithm are provided by the library.
* The graphs are attributed hypergraphs capable of representing netlists:
- Nodes represent the logic cells:
- Nodes have types and only match compatible types
- Nodes have ports with variable bit-width
- Hyperedges represent the signals:
- Each hyperedge connects one to many bits on ports on nodes
- Callback functions for advanced attributes and compatibility rules:
Any set of node-node compatibility rules and edge-edge
compatibility rules can be implemented by providing
the necessary callback functions.
* The algorithm is very efficient when all or many bits of one port are
connected to bits of the same other port. This is usually the case
in coarse grain logic networks. But the algorithm does not add any
restrictions in this area; it is just optimized for this scenario.
* The algorithm can be configured to allow larger ports in needle cells to
match smaller ports in haystack cells in certain situations. This way it
is possible to e.g. have a 32-bit adder cell in the needle match a
16-bit adder cell in the haystack.
* The algorithm can be configured to perform port-swapping on certain
ports on certain cell types to match commutative operations properly.
This is, however, not implemented very efficiently when a larger number
of permutations is possible on a cell type. Therefore it is recommended
to only use swap groups with only a few members and a few such groups
on one cell type type.
Also note, that the algorithm can not resolve complex dependencies
between the port swappings of different cells. Therefore it is
recommended to only use port swapping on input pins of commutative
operations, where such complex dependencies can not emerge.
* The algorithm can be configured to distinguish between internal signals
of the needle and externally visible signals. The needle will only
match a subgraph of the haystack if that subgraph does not expose the
internal signal to nodes in the haystack outside the matching subgraph.
* The algorithm can recognize a subcircuit even if some or all of its
inputs and/or outputs are shorted together.
* Explicit fast support for constant signals without extra nodes for
constant drivers.
* Support for finding only non-overlapping matches.
* The public API of the library is using std::string identifiers for
nodes, node types and ports. Internally the costly part of the
algorithm is only using integer values, thus speeding up the
algorithm without exposing complex internal encodings to the caller.
=================
API Documentation
=================
This section gives a brief overview of the API. For a working example, have a
look at the demo.cc example program in this directory.
Setting up graphs
-----------------
Instanciate the SubCircuit::Graph class and use the methods of this class to
set up the circuit.
SubCircuit::Graph myGraph;
For each node in the circuit call the createNode() method. Specify the
identifier for the node and also the type of function implemented by the node.
Then call createPort() for each port of this node.
E.g. the following code adds a node "myAdder" of type "add" with three 32 bit
wide ports "A", "B" and "Y". Note that SubCircuit does not care which port is
an input and which port is an output. The last (and optional) argument to
createPort() specifies the minimum number of bits required for this port in the
haystack (this field is only used in the needle graph). So in this example the
node would e.g. also match any adder with a bit width smaller 32.
myGraph.createNode("myAdder", "add");
myGraph.createPort("myAdder", "A", 32, 1);
myGraph.createPort("myAdder", "B", 32, 1);
myGraph.createPort("myAdder", "Y", 32, 1);
The createConnection() method can be used to connect the nodes. It internally
creates a hypergraph. So the following code does not only connect cell1.Y with
cell2.A and cell3.A but also implicitly cell2.A with cell3.A.
myGraph.createConnection("cell1", "Y", "cell2", "A");
myGraph.createConnection("cell1", "Y", "cell3", "A");
Redundent calls to createConnection() are ignored. As long as the method is
called after the relevant nodes and ports are created, the order in which the
createConnection() calls are performed is irrelevant.
The createConnection() method can also be used to connect single bit signals.
In this case the start bit for both ports must be provided as well as an
optional width (which defaults to 1). E.g. the following calls can be used to
connect the 32 bit port cell4.Y to the 32 bit port cell5.A with a one bit left
rotate shift,
myGraph.createConnection("cell4", "Y", 0, "cell5", "A", 1, 31);
myGraph.createConnection("cell4", "Y", 31, "cell5", "A", 0);
The method createConstant() can be used to add a constant driver to a signal.
The signal value is encoded as one char by bit, allowing for multi-valued
logic matching. The follwoing command sets the lowest bit of cell6.A to a
logic 1:
myGraph.createConnection("cell6", "A", 0, '1');
It is also possible to set an entire port to a integer value, using the
encodings '0' and '1' for the binary digits:
myGraph.createConnection("cell6", "A", 42);
The method markExtern() can be used to mark a signal as externally visible. In
a needle graph this means, this signal may match a signal in the haystack that
is used outside the matching subgraph. In a haystack graph this means, this
signal is used outside the haystack graph. I.e. an internal signal of the
needle won't match an external signal of the haystack regardless where the
signal is used in the haystack.
In some application one may disable this extern/intern checks. This can easily
be achieved by marking all signals in the needle as extern. This can be done
using the Graph::markAllExtern() method.
Setting up and running solvers
------------------------------
To actually run the subgraph isomorphism algorithm, an instance of
SubCircuit::Solver must be created.
SubCircuit::Solver mySolver;
The addGraph() method can be used to register graphs with the solver:
mySolver.addGraph("graph1", myGraph);
mySolver.addGraph("graph2", myOtherGraph);
Usually nodes in the needle and the haystack must have the same type identifier
to match each other. Additionally pairs of compatible needle and haystack node
pairs can be registered using the addCompatibleTypes() method:
mySolver.addCompatibleTypes("alu", "add");
mySolver.addCompatibleTypes("alu", "sub");
mySolver.addCompatibleTypes("alu", "and");
mySolver.addCompatibleTypes("alu", "or");
mySolver.addCompatibleTypes("alu", "xor");
Note that nodes in needle and haystack must also use the same naming convention
for their ports in order to be considered compatible by the algorithm.
Similarly the method addCompatibleConstants() can be used the specify which
constant values in the needle should match which constant value in the haystack.
Equal values always do match.
mySolver.addCompatibleConstants('x', '0');
mySolver.addCompatibleConstants('x', '1');
Some cells implement commutative operations that don't care if their input
operands are swapped. For this cell types it is possible to register groups
of swappable ports. Let's consider a cell "macc23" that implements the
function Y = (A * B) + (C * D * E):
mySolver.addSwappablePorts("macc23", "A", "B");
mySolver.addSwappablePorts("macc23", "C", "D", "E");
Sometimes the rules for port swapping are a more complicated and the swapping
of one port is related to the swapping of another port. Let's consider a cell
"macc22" that implements the function Y = (A * B) + (C * D):
mySolver.addSwappablePorts("macc22", "A", "B");
mySolver.addSwappablePorts("macc22", "C", "D");
std::map<std::string, std::string> portMapping;
portMapping["A"] = "C";
portMapping["B"] = "D";
portMapping["C"] = "A";
portMapping["D"] = "B";
mySolver.addSwappablePortsPermutation("macc22", portMapping);
I.e. the method mySolver.addSwappablePortsPermutation() can be used to register
additional permutations for a node type of which one or none is applied on top
of the permutations yielded by the permutations generated by the swap groups.
Note that two solutions that differ only in the applied port swapping are not
reported as separate solutions. Instead only one of them is selected (in most
cases the one with less port swapping as it is usually identified first).
Once everything has been set up, the solve() method can be used to actually
search for isomorphic subgraphs. The first argument to solve() is an
std::vector<SubCircuit::Solver::Result> objects to which all found solutions
are appended. The second argument is the identifier under which the needle
graph has been registered and the third argument is the identifier under which
the haystack graph has been registered:
std::vector<SubCircuit::Solver::Result> results;
mySolver.solve(results, "graph1", "graph2");
The SubCircuit::Solver::Result object is a simple data structure that contains
the mappings between needle and haystack nodes, port mappings after the port
swapping and some additional metadata. See "subcircuit.h" and "demo.cc" for
details.
The solve() method has a third optional boolean argument. If it is set to
false, solve will not return any solutions that contain haystack nodes that
have been part of a previously found solution. This way it is e.g. easy
to implement a greedy macro cell matching algorithm:
std::vector<SubCircuit::Solver::Result> results;
mySolver.solve(results, "macroCell1", "circuit", false);
mySolver.solve(results, "macroCell2", "circuit", false);
mySolver.solve(results, "macroCell3", "circuit", false);
After this code has been executed, the results vector contains all
non-overlapping matches of the three macrocells. The method
clearOverlapHistory() can be used to reset the internal state used
for this feature. The default value for the third argument to solve()
is true (allow overlapping).
The solve() method also has a fourth optional integer argument. If it is set to
a positive integer, this integer specifies the maximum number of solutions to
be appended to the results vector, i.e. to terminate the algorithm early when
the set number of matches is found. When this fourth argument is negative or
omitted all matches are found and appended.
An alternative version of the solve() method supports an additional argument
after they haystack graph identifier that specifies initial mappings for
the algorithm. In the following example only the haystack nodes cell_1 and
cell_2 are considered as mappings for the needle node cell_A:
std::map<std::string, std::set<std::string>> initialMappings;
initialMappings["cell_A"].insert("cell_1");
initialMappings["cell_A"].insert("cell_2");
std::vector<SubCircuit::Solver::Result> results;
mySolver.solve(results, "graph1", "graph2", initialMappings);
The clearConfig() method can be used to clear all data registered using
addCompatibleTypes(), addCompatibleConstants(), addSwappablePorts() and
addSwappablePortsPermutation() but retaining the graphs and the overlap state.
Using user callback function
----------------------------
For more complex tasks it is possible to derive a class from SubCircuit::Solver
that overloads one or more of the following virtual methods. The userData
arguments to the following methods are void pointers that can be passed as
third argument to Graph::createNode() and are simly passed thru to the user
callback functions together with the node id whenever a node is referenced.
bool userCompareNodes(needleGraphId, needleNodeId, needleUserData, haystackGraphId, haystackNodeId, haystackUserData):
Perform additional checks on a pair of nodes (one from the needle, one
from the haystack) to determine if the nodes are compatible. The default
implementation always returns true.
bool userCompareEdge(needleGraphId, needleFromNodeId, needleFromUserData, needleToNodeId, needleToUserData,
haystackGraphId, haystackFromNodeId, haystackFromUserData, haystackToNodeId, haystackToUserData):
Perform additional checks on a pair of a pair of adjacent nodes (one
adjacent pair from the needle and one adjacent pair from the haystack)
to determine wheter this edge from the needle is compatible with
that edge from the haystack. The default implementation always
returns true.
bool userCheckSolution(result):
Perform additional checks on a solution before appending it to the
results vector. When this function returns false, the solution is
ignored. The default implementation always returns true.
Debugging
---------
For debugging purposes the SubCircuit::Solver class implements a setVerbose()
method. When called once, all further calls to the solve() method cause the
algorithm to dump out a lot of debug information to stdout.
In conjunction with setVerbose() one can also overload the userAnnotateEdge()
method in order to add additional information about the edges to the debug
output.
===================
Shell Documentation
===================
This package also contains a small command-line tool called "scshell" that can
be used for experimentation with the algorithm. This program reads a series of
commands from stdin and reports its findings to stdout on exit.
$ ./scshell < test_macc22.txt
...
Match #3: (macc22 in macc4x2)
add_1 -> add_2 A:B B:A Y:Y
mul_1 -> mul_4 A:A B:B Y:Y
mul_2 -> mul_3 A:A B:B Y:Y
The following commands can be used in scshell to specify graphs:
graph <graph_name>
...
endgraph
Used to specify a graph with the given name. Only the commands
"node", "connect" and "extern" may be used within the graph ...
endgraph block.
node <node_name> [<port_name> [<bits> [<min_bits>]]]+
Used to create a node and ports. This command is a direct frontend
to the Graph::createNode() and Graph::createPort() methods.
connect <from_node> <from_port> <to_node> <to_port>
connect <from_node> <from_port> <from_bit> <to_node> <to_port> <to_bit>
connect <from_node> <from_port> <from_bit> <to_node> <to_port> <to_bit> <width>
Used to connect the nodes in the graph via Graph::createConnection().
constant <node> <port> [<bit>] <value>
Call Graph::createConstant().
extern <node> [<port> [<bit>]]+
Mark signals as extern via Graph::markExtern().
allextern
Mark all signals as extern via Graph::markAllExtern().
The following commands can be used in scshell outside a graph ... endgraph block:
compatible <needle_type> <haystack_type>
Call Solver::addCompatibleTypes().
constcompat <needle_value> <haystack_value>
Call Solver::addCompatibleConstants().
swapgroup <needle_type> <port>+
Call Solver::addSwappablePorts().
swapperm <needle_type> <ports>+ : <ports>+
Call Solver::addSwappablePortsPermutation(). Both port lists must
have the same length and the second one must be a permutation of the
first one.
initmap <needle_node> <haystack_node>+
Add an entry to the initial mappings for the next solve command.
This mappings are automatically reset after the solve command.
solve <needle_graph> <haystack_graph> [<allow_overlap> [<max_solutions>]]
Call Solver::solve(). The <allow_overlap> must be "1" or "true"
for true and "0" or "false" for false.
expect <number>
Print all results so far since the last call to expect. Expect
<number> results and exit with error code 1 if a different number
of results have been found.
clearoverlap
Call Solver::clearOverlapHistory().
clearconfig
Call Solver::clearConfig().
verbose
Call Solver::setVerbose().

134
libs/subcircuit/demo.cc Normal file
View File

@ -0,0 +1,134 @@
#include "subcircuit.h"
#include <stdio.h>
#define VERBOSE
int main()
{
SubCircuit::Graph needle, haystack;
// create needle graph
needle.createNode("mul_1", "product");
needle.createPort("mul_1", "A", 4);
needle.createPort("mul_1", "B", 4);
needle.createPort("mul_1", "Y", 4);
needle.markExtern("mul_1", "A");
needle.markExtern("mul_1", "B");
needle.createNode("mul_2", "product");
needle.createPort("mul_2", "A", 4);
needle.createPort("mul_2", "B", 4);
needle.createPort("mul_2", "Y", 4);
needle.markExtern("mul_2", "A");
needle.markExtern("mul_2", "B");
needle.createNode("add_1", "sum");
needle.createPort("add_1", "A", 4);
needle.createPort("add_1", "B", 4);
needle.createPort("add_1", "Y", 4);
needle.markExtern("add_1", "Y");
needle.createConnection("mul_1", "Y", "add_1", "A");
needle.createConnection("mul_2", "Y", "add_1", "B");
#ifdef VERBOSE
printf("\n");
needle.print();
#endif
// create haystack graph
#if 0
for (int i = 0; i < 4; i++) {
char id[100];
snprintf(id, 100, "mul_%d", i);
haystack.createNode(id, "mul");
haystack.createPort(id, "A", 4);
haystack.createPort(id, "B", 4);
haystack.createPort(id, "Y", 4);
haystack.markExtern(id, "A");
haystack.markExtern(id, "B");
}
for (int i = 0; i < 3; i++) {
char id[100];
snprintf(id, 100, "add_%d", i);
haystack.createNode(id, "add");
haystack.createPort(id, "A", 4);
haystack.createPort(id, "B", 4);
haystack.createPort(id, "Y", 4);
}
haystack.createConnection("mul_0", "Y", "add_0", "A");
haystack.createConnection("mul_1", "Y", "add_0", "B");
haystack.createConnection("mul_2", "Y", "add_1", "A");
haystack.createConnection("mul_3", "Y", "add_1", "B");
haystack.createConnection("add_0", "Y", "add_2", "A");
haystack.createConnection("add_1", "Y", "add_2", "B");
haystack.markExtern("add_2", "Y");
#else
std::vector<std::string> cellIds;
srand48(12345);
for (int i = 0; i < 45; i++) {
char id[100];
snprintf(id, 100, "cell_%02d", i);
haystack.createNode(id, i < 30 ? "mul" : "add");
haystack.createPort(id, "A", 4);
haystack.createPort(id, "B", 4);
haystack.createPort(id, "Y", 4);
cellIds.push_back(id);
}
for (int i = 0; i < int(cellIds.size()); i++) {
if (lrand48() % (i < 20 ? 3 : 2) != 0)
continue;
const std::string &id = cellIds[i];
const std::string &id_left = cellIds[lrand48() % cellIds.size()];
const std::string &id_right = cellIds[lrand48() % cellIds.size()];
haystack.createConnection(id_left, "Y", id, "A");
haystack.createConnection(id_right, "Y", id, "B");
}
#endif
#ifdef VERBOSE
printf("\n");
haystack.print();
#endif
// search needle in haystack
SubCircuit::Solver solver;
std::vector<SubCircuit::Solver::Result> results;
#ifdef VERBOSE
solver.setVerbose();
#endif
solver.addCompatibleTypes("product", "mul");
solver.addCompatibleTypes("sum", "add");
solver.addSwappablePorts("product", "A", "B");
solver.addSwappablePorts("sum", "A", "B");
solver.addGraph("needle", needle);
solver.addGraph("haystack", haystack);
solver.solve(results, "needle", "haystack");
for (int i = 0; i < int(results.size()); i++) {
printf("\nMatch #%d: (%s in %s)\n", i, results[i].needleGraphId.c_str(), results[i].haystackGraphId.c_str());
for (const auto &it : results[i].mappings) {
printf(" %s -> %s", it.first.c_str(), it.second.haystackNodeId.c_str());
for (const auto &it2 : it.second.portMapping)
printf(" %s:%s", it2.first.c_str(), it2.second.c_str());
printf("\n");
}
}
printf("\n");
return 0;
}

235
libs/subcircuit/scshell.cc Normal file
View File

@ -0,0 +1,235 @@
#include "subcircuit.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
std::vector<std::string> readLine()
{
char buffer[4096];
std::vector<std::string> tokenList;
while (tokenList.size() == 0 && fgets(buffer, sizeof(buffer), stdin) != NULL) {
for (char *p = buffer; char *tok = strtok(p, " \t\r\n"); p = NULL) {
if (p != NULL && tok[0] == '#')
break;
tokenList.push_back(tok);
}
}
return tokenList;
}
int main()
{
std::string graphId;
SubCircuit::Graph *graph = NULL;
SubCircuit::Solver solver;
std::map<std::string, std::set<std::string>> initialMappings;
std::vector<SubCircuit::Solver::Result> results;
std::vector<std::string> cmdBuffer;
bool lastCommandExpect = false;
while (1)
{
cmdBuffer = readLine();
if (cmdBuffer.empty())
break;
printf(graph == NULL || cmdBuffer[0] == "endgraph" ? ">" : "> ");
for (const auto &tok : cmdBuffer)
printf(" %s", tok.c_str());
printf("\n");
lastCommandExpect = false;
if (graph != NULL)
{
if (cmdBuffer[0] == "node" && cmdBuffer.size() >= 3) {
graph->createNode(cmdBuffer[1], cmdBuffer[2]);
for (int i = 3; i < int(cmdBuffer.size()); i++) {
std::string portId = cmdBuffer[i];
int width = 1, minWidth = -1;
if (i+1 < int(cmdBuffer.size()) && '0' <= cmdBuffer[i+1][0] && cmdBuffer[i+1][0] <= '9')
width = atoi(cmdBuffer[++i].c_str());
if (i+1 < int(cmdBuffer.size()) && '0' <= cmdBuffer[i+1][0] && cmdBuffer[i+1][0] <= '9')
minWidth = atoi(cmdBuffer[++i].c_str());
graph->createPort(cmdBuffer[1], portId, width, minWidth);
}
continue;
}
if (cmdBuffer[0] == "connect" && cmdBuffer.size() == 5) {
graph->createConnection(cmdBuffer[1], cmdBuffer[2], cmdBuffer[3], cmdBuffer[4]);
continue;
}
if (cmdBuffer[0] == "connect" && cmdBuffer.size() == 7) {
graph->createConnection(cmdBuffer[1], cmdBuffer[2], atoi(cmdBuffer[3].c_str()), cmdBuffer[4], cmdBuffer[5], atoi(cmdBuffer[6].c_str()));
continue;
}
if (cmdBuffer[0] == "connect" && cmdBuffer.size() == 8) {
graph->createConnection(cmdBuffer[1], cmdBuffer[2], atoi(cmdBuffer[3].c_str()), cmdBuffer[4], cmdBuffer[5], atoi(cmdBuffer[6].c_str()), atoi(cmdBuffer[7].c_str()));
continue;
}
if (cmdBuffer[0] == "constant" && cmdBuffer.size() == 5) {
int constValue = cmdBuffer[4].size() > 1 && cmdBuffer[4][0] == '#' ? atoi(cmdBuffer[4].c_str()+1) : cmdBuffer[4][0];
graph->createConstant(cmdBuffer[1], cmdBuffer[2], atoi(cmdBuffer[3].c_str()), constValue);
continue;
}
if (cmdBuffer[0] == "constant" && cmdBuffer.size() == 4) {
graph->createConstant(cmdBuffer[1], cmdBuffer[2], atoi(cmdBuffer[3].c_str()));
continue;
}
if (cmdBuffer[0] == "extern" && cmdBuffer.size() >= 3) {
for (int i = 2; i < int(cmdBuffer.size()); i++) {
std::string portId = cmdBuffer[i];
int bit = -1;
if (i+1 < int(cmdBuffer.size()) && '0' <= cmdBuffer[i+1][0] && cmdBuffer[i+1][0] <= '9')
bit = atoi(cmdBuffer[++i].c_str());
graph->markExtern(cmdBuffer[1], portId, bit);
}
continue;
}
if (cmdBuffer[0] == "allextern" && cmdBuffer.size() == 1) {
graph->markAllExtern();
continue;
}
if (cmdBuffer[0] == "endgraph" && cmdBuffer.size() == 1) {
solver.addGraph(graphId, *graph);
delete graph;
graph = NULL;
continue;
}
}
else
{
if (cmdBuffer[0] == "graph" && cmdBuffer.size() == 2) {
graph = new SubCircuit::Graph;
graphId = cmdBuffer[1];
continue;
}
if (cmdBuffer[0] == "compatible" && cmdBuffer.size() == 3) {
solver.addCompatibleTypes(cmdBuffer[1], cmdBuffer[2]);
continue;
}
if (cmdBuffer[0] == "constcompat" && cmdBuffer.size() == 3) {
int needleConstValue = cmdBuffer[1].size() > 1 && cmdBuffer[1][0] == '#' ? atoi(cmdBuffer[1].c_str()+1) : cmdBuffer[1][0];
int haystackConstValue = cmdBuffer[2].size() > 1 && cmdBuffer[2][0] == '#' ? atoi(cmdBuffer[2].c_str()+1) : cmdBuffer[2][0];
solver.addCompatibleConstants(needleConstValue, haystackConstValue);
continue;
}
if (cmdBuffer[0] == "swapgroup" && cmdBuffer.size() >= 4) {
std::set<std::string> ports;
for (int i = 2; i < int(cmdBuffer.size()); i++)
ports.insert(cmdBuffer[i]);
solver.addSwappablePorts(cmdBuffer[1], ports);
continue;
}
if (cmdBuffer[0] == "swapperm" && cmdBuffer.size() >= 4 && cmdBuffer.size() % 2 == 1 && cmdBuffer[cmdBuffer.size()/2 + 1] == ":") {
std::map<std::string, std::string> portMapping;
int n = (cmdBuffer.size()-3) / 2;
for (int i = 0; i < n; i++)
portMapping[cmdBuffer[i+2]] = cmdBuffer[i+3+n];
solver.addSwappablePortsPermutation(cmdBuffer[1], portMapping);
continue;
}
if (cmdBuffer[0] == "initmap" && cmdBuffer.size() >= 4) {
for (int i = 2; i < int(cmdBuffer.size()); i++)
initialMappings[cmdBuffer[1]].insert(cmdBuffer[i]);
continue;
}
if (cmdBuffer[0] == "solve" && 3 <= cmdBuffer.size() && cmdBuffer.size() <= 5) {
bool allowOverlap = true;
int maxSolutions = -1;
if (cmdBuffer.size() >= 4)
allowOverlap = cmdBuffer[3] == "true" || atoi(cmdBuffer[3].c_str()) ? true : false;
if (cmdBuffer.size() >= 5)
maxSolutions = atoi(cmdBuffer[4].c_str());
solver.solve(results, cmdBuffer[1], cmdBuffer[2], initialMappings, allowOverlap, maxSolutions);
initialMappings.clear();
continue;
}
if (cmdBuffer[0] == "clearoverlap" && cmdBuffer.size() == 1) {
solver.clearOverlapHistory();
continue;
}
if (cmdBuffer[0] == "clearconfig" && cmdBuffer.size() == 1) {
solver.clearConfig();
continue;
}
if (cmdBuffer[0] == "verbose" && cmdBuffer.size() == 1) {
solver.setVerbose();
continue;
}
if (cmdBuffer[0] == "expect" && cmdBuffer.size() == 2) {
int expected = atoi(cmdBuffer[1].c_str());
printf("\n-- Expected %d, Got %d --\n", expected, int(results.size()));
for (int i = 0; i < int(results.size()); i++) {
printf("\nMatch #%d: (%s in %s)\n", i, results[i].needleGraphId.c_str(), results[i].haystackGraphId.c_str());
for (const auto &it : results[i].mappings) {
printf(" %s -> %s", it.first.c_str(), it.second.haystackNodeId.c_str());
for (const auto &it2 : it.second.portMapping)
printf(" %s:%s", it2.first.c_str(), it2.second.c_str());
printf("\n");
}
}
printf("\n");
if (expected != int(results.size())) {
printf("^^ expected %d, Got %d ^^\n\n", expected, int(results.size()));
printf(" +----------------+\n");
printf(" | \\|/ ____ \\|/ |\n");
printf(" | \"@'/ ,. \\`@\" |\n");
printf(" | /_| \\__/ |_\\ |\n");
printf(" | \\__U_/ |\n");
printf(" | | | |\n");
printf(" +----------------+\n\n");
return 1;
}
results.clear();
lastCommandExpect = true;
continue;
}
}
printf("Invalid input command!\n");
return 1;
}
if (graph)
delete graph;
if (!lastCommandExpect) {
printf("\n-- Got %d --\n", int(results.size()));
for (int i = 0; i < int(results.size()); i++) {
printf("\nMatch #%d: (%s in %s)\n", i, results[i].needleGraphId.c_str(), results[i].haystackGraphId.c_str());
for (const auto &it : results[i].mappings) {
printf(" %s -> %s", it.first.c_str(), it.second.haystackNodeId.c_str());
for (const auto &it2 : it.second.portMapping)
printf(" %s:%s", it2.first.c_str(), it2.second.c_str());
printf("\n");
}
}
} else
printf("PASSED.\n");
printf("\n");
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,139 @@
/*
* SubCircuit -- An implementation of the Ullmann Subgraph Isomorphism
* algorithm for coarse grain logic networks
*
* Copyright (C) 2013 Clifford Wolf <clifford@clifford.at>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef SUBCIRCUIT_H
#define SUBCIRCUIT_H
#include <string>
#include <vector>
#include <set>
#include <map>
namespace SubCircuit
{
class SolverWorker;
class Graph
{
protected:
struct BitRef {
int nodeIdx, portIdx, bitIdx;
BitRef(int nodeIdx = -1, int portIdx = -1, int bitIdx = -1) : nodeIdx(nodeIdx), portIdx(portIdx), bitIdx(bitIdx) { };
bool operator < (const BitRef &other) const;
};
struct Edge {
std::set<BitRef> portBits;
int constValue;
bool isExtern;
Edge() : constValue(0), isExtern(false) { };
};
struct PortBit {
int edgeIdx;
PortBit() : edgeIdx(-1) { };
};
struct Port {
std::string portId;
int minWidth;
std::vector<PortBit> bits;
Port() : minWidth(-1) { };
};
struct Node {
std::string nodeId, typeId;
std::map<std::string, int> portMap;
std::vector<Port> ports;
void *userData;
Node() : userData(NULL) { };
};
bool allExtern;
std::map<std::string, int> nodeMap;
std::vector<Node> nodes;
std::vector<Edge> edges;
public:
Graph() : allExtern(false) { };
void createNode(std::string nodeId, std::string typeId, void *userData = NULL);
void createPort(std::string nodeId, std::string portId, int width = 1, int minWidth = -1);
void createConnection(std::string fromNodeId, std::string fromPortId, int fromBit, std::string toNodeId, std::string toPortId, int toBit, int width = 1);
void createConnection(std::string fromNodeId, std::string fromPortId, std::string toNodeId, std::string toPortId);
void createConstant(std::string toNodeId, std::string toPortId, int toBit, int constValue);
void createConstant(std::string toNodeId, std::string toPortId, int constValue);
void markExtern(std::string nodeId, std::string portId, int bit = -1);
void markAllExtern();
void print();
friend class SolverWorker;
};
class Solver
{
public:
struct ResultNodeMapping {
std::string needleNodeId, haystackNodeId;
void *needleUserData, *haystackUserData;
std::map<std::string, std::string> portMapping;
};
struct Result {
std::string needleGraphId, haystackGraphId;
std::map<std::string, ResultNodeMapping> mappings;
};
private:
SolverWorker *worker;
protected:
virtual bool userCompareNodes(const std::string &needleGraphId, const std::string &needleNodeId, void *needleUserData,
const std::string &haystackGraphId, const std::string &haystackNodeId, void *haystackUserData);
virtual std::string userAnnotateEdge(const std::string &graphId, const std::string &fromNodeId, void *fromUserData, const std::string &toNodeId, void *toUserData);
virtual bool userCompareEdge(const std::string &needleGraphId, const std::string &needleFromNodeId, void *needleFromUserData, const std::string &needleToNodeId, void *needleToUserData,
const std::string &haystackGraphId, const std::string &haystackFromNodeId, void *haystackFromUserData, const std::string &haystackToNodeId, void *haystackToUserData);
virtual bool userCheckSolution(const Result &result);
friend class SolverWorker;
public:
Solver();
~Solver();
void setVerbose();
void addGraph(std::string graphId, const Graph &graph);
void addCompatibleTypes(std::string needleTypeId, std::string haystackTypeId);
void addCompatibleConstants(int needleConstant, int haystackConstant);
void addSwappablePorts(std::string needleTypeId, std::string portId1, std::string portId2, std::string portId3 = std::string(), std::string portId4 = std::string());
void addSwappablePorts(std::string needleTypeId, std::set<std::string> ports);
void addSwappablePortsPermutation(std::string needleTypeId, std::map<std::string, std::string> portMapping);
void solve(std::vector<Result> &results, std::string needleGraphId, std::string haystackGraphId, bool allowOverlap = true, int maxSolutions = -1);
void solve(std::vector<Result> &results, std::string needleGraphId, std::string haystackGraphId,
const std::map<std::string, std::set<std::string>> &initialMapping, bool allowOverlap = true, int maxSolutions = -1);
void clearOverlapHistory();
void clearConfig();
};
}
#endif /* SUBCIRCUIT_H */

View File

@ -0,0 +1,221 @@
#!/usr/bin/env splrun
var idx = 0;
var count_nand = 0;
var count_nor = 0;
function makeNAND(net, id)
{
count_nand++;
net["${id}_VDD"] = "${id}_pa S";
net["${id}_VSS"] = "${id}_nb S";
net["${id}_A"] = "${id}_pa G";
net["${id}_B"] = "${id}_pb G";
net["${id}_Y"] = "${id}_pb D";
return <:>
: node ${id}_pa pmos S 1 D 1 G 1
: node ${id}_pb pmos S 1 D 1 G 1
: node ${id}_na nmos S 1 D 1 G 1
: node ${id}_nb nmos S 1 D 1 G 1
: connect ${id}_pa S ${id}_pb S
: connect ${id}_pa D ${id}_pb D
: connect ${id}_pa D ${id}_na D
: connect ${id}_na S ${id}_nb D
: connect ${id}_pa G ${id}_na G
: connect ${id}_pb G ${id}_nb G
</>;
}
function makeNOR(net, id)
{
count_nor++;
net["${id}_VDD"] = "${id}_pa S";
net["${id}_VSS"] = "${id}_nb S";
net["${id}_A"] = "${id}_pa G";
net["${id}_B"] = "${id}_pb G";
net["${id}_Y"] = "${id}_pb D";
return <:>
: node ${id}_pa pmos S 1 D 1 G 1
: node ${id}_pb pmos S 1 D 1 G 1
: node ${id}_na nmos S 1 D 1 G 1
: node ${id}_nb nmos S 1 D 1 G 1
: connect ${id}_pa D ${id}_pb S
: connect ${id}_pb D ${id}_na D
: connect ${id}_pb D ${id}_nb D
: connect ${id}_na S ${id}_nb S
: connect ${id}_pa G ${id}_na G
: connect ${id}_pb G ${id}_nb G
</>;
}
function makeGraph(seed, gates, primaryInputs, primaryOutputs)
{
srand(seed);
var code = "";
var net, vdd, vss, outputs;
var unusedOutpus;
for (var i = 0; i < gates; i++)
{
var id = fmt("G%d", idx++);
if (rand(2) == 0)
code ~= makeNAND(net, id);
else
code ~= makeNOR(net, id);
if (i == 0) {
vdd = net["${id}_VDD"];
vss = net["${id}_VSS"];
} else {
code ~= <:>
: connect $vdd ${net["${id}_VDD"]}
: connect $vss ${net["${id}_VSS"]}
</>;
}
var inIdx1 = rand((elementsof outputs) + 1);
if (inIdx1 < elementsof outputs) {
code ~= " connect ${outputs[inIdx1]} ${net["${id}_A"]}\n";
delete unusedOutpus[outputs[inIdx1]];
} else
push primaryInputs, net["${id}_A"];
var inIdx2 = rand((elementsof outputs) + 1);
if (inIdx2 < elementsof outputs) {
code ~= " connect ${outputs[inIdx2]} ${net["${id}_B"]}\n";
delete unusedOutpus[outputs[inIdx2]];
} else
push primaryInputs, net["${id}_B"];
unusedOutpus[net["${id}_Y"]] = 1;
push outputs, net["${id}_Y"];
}
foreach netDecl (unusedOutpus)
push primaryOutputs, netDecl;
return code;
}
function makeConnections(fromNets, toNets)
{
var code = "";
foreach[] toNet (toNets) {
var fromNet = fromNets[rand(elementsof fromNets)];
code != " connect $fromNet $toNet\n";
}
return code;
}
var numNodes;
write(<:>
: graph nand
<?spl var net = []; ?>
${makeNAND(net, "nand")}
: extern ${net["nand_VDD"]}
: extern ${net["nand_VSS"]}
: extern ${net["nand_A"]}
: extern ${net["nand_B"]}
: extern ${net["nand_Y"]}
: endgraph
:
: graph nor
${makeNOR(net, "nor")}
: extern ${net["nor_VDD"]}
: extern ${net["nor_VSS"]}
: extern ${net["nor_A"]}
: extern ${net["nor_B"]}
: extern ${net["nor_Y"]}
: endgraph
:
: graph needle_1
<?spl var ports; ?>
${makeGraph(1, 100, ports, ports)}
<?spl numNodes["needle_1"] = idx*4; ?>
<spl:foreach var="[]net" list="ports">
: extern $net
</spl:foreach>
: endgraph
:
: graph needle_2
<?spl var ports; ?>
${makeGraph(2, 200, ports, ports)}
<?spl numNodes["needle_2"] = idx*4; ?>
<spl:foreach var="[]net" list="ports">
: extern $net
</spl:foreach>
: endgraph
:
: graph needle_3
<?spl var ports; ?>
${makeGraph(3, 300, ports, ports)}
<?spl numNodes["needle_3"] = idx*4; ?>
<spl:foreach var="[]net" list="ports">
: extern $net
</spl:foreach>
: endgraph
:
: graph haystack
<?spl count_nand=0; count_nor=0; ?>
<?spl var inPorts1, outPorts1; ?>
${makeGraph(1, 100, inPorts1, outPorts1)}
<?spl var inPorts2, outPorts2; ?>
${makeGraph(2, 200, inPorts2, outPorts2)}
<?spl var inPorts3, outPorts3; ?>
${makeGraph(3, 300, inPorts3, outPorts3)}
<?spl var inPorts4, outPorts4; ?>
${makeGraph(2, 200, inPorts4, outPorts4)}
<?spl var inPorts5, outPorts5; ?>
${makeGraph(1, 100, inPorts5, outPorts5)}
<?spl numNodes["haystack"] = idx*4; ?>
${makeConnections(outPorts1, inPorts2)}
${makeConnections(outPorts2, inPorts3)}
${makeConnections(outPorts3, inPorts4)}
${makeConnections(outPorts4, inPorts5)}
: endgraph
:
: solve nand haystack false
: expect $count_nand
: clearoverlap
:
: solve nor haystack false
: expect $count_nor
: clearoverlap
:
: solve needle_1 haystack false
: expect 2
:
: solve needle_2 haystack false
: expect 2
:
: solve needle_3 haystack false
: expect 1
</>);
numNodes["haystack"] -= numNodes["needle_3"];
numNodes["needle_3"] -= numNodes["needle_2"];
numNodes["needle_2"] -= numNodes["needle_1"];
write(<:>
:
: # Needle_1: ${numNodes["needle_1"]} transistors
: # Needle_2: ${numNodes["needle_2"]} transistors
: # Needle_3: ${numNodes["needle_3"]} transistors
: # Haystack: ${numNodes["haystack"]} transistors
</>);

View File

@ -0,0 +1,48 @@
# verbose
graph macc22
node mul_1 mul A 32 B 32 Y 32
node mul_2 mul A 32 B 32 Y 32
node add_1 add A 32 B 32 Y 32
connect mul_1 Y add_1 A
connect mul_2 Y add_1 B
extern mul_1 A B
extern mul_2 A B
extern add_1 Y
endgraph
graph macc4x2
node mul_1 mul A 32 B 32 Y 32
node mul_2 mul A 32 B 32 Y 32
node mul_3 mul A 32 B 32 Y 32
node mul_4 mul A 32 B 32 Y 32
node add_1 add A 32 B 32 Y 32
node add_2 add A 32 B 32 Y 32
node add_3 add A 32 B 32 Y 32
connect mul_1 Y add_1 A
connect mul_2 Y add_1 B
connect mul_3 Y add_2 A
connect mul_4 Y add_2 B
connect add_1 Y add_3 A
connect add_2 Y add_3 B
extern mul_1 A B
extern mul_2 A B
extern mul_3 A B
extern mul_4 A B
extern add_3 Y
endgraph
solve macc22 macc4x2
expect 2
swapgroup mul A B
solve macc22 macc4x2
expect 2
swapperm add A B : B A
solve macc22 macc4x2
expect 4

View File

@ -0,0 +1,108 @@
#!/usr/bin/perl -w
use strict;
# let "macc" implement a function like Y = (A*B) + (C*D)
#
# the following permutations of the input pins exist:
#
# g01 | A B C D | match
# g02 | A B D C | match
# g03 | A C B D | not
# g04 | A C D B | not
# g05 | A D B C | not
# g06 | A D C B | not
# g07 | B A C D | match
# g08 | B A D C | match
# g09 | B C A D | not
# g10 | B C D A | not
# g11 | B D A C | not
# g12 | B D C A | not
# g13 | C A B D | not
# g14 | C A D B | not
# g15 | C B A D | not
# g16 | C B D A | not
# g17 | C D A B | match
# g18 | C D B A | match
# g19 | D A B C | not
# g20 | D A C B | not
# g21 | D B A C | not
# g22 | D B C A | not
# g23 | D C A B | match
# g24 | D C B A | match
my @matches = qw/g01 g02 g07 g08 g17 g18 g23 g24/;
my @non_matches = qw/g03 g04 g05 g06 g09 g10 g11 g12 g13 g14 g15 g16 g19 g20 g21 g22/;
print "\n";
for my $i (0..3) {
for my $j (0..2) {
for my $k (0..1) {
my @t = qw/A B C D/;
print "# ";
print splice(@t,$i,1),splice(@t,$j,1),splice(@t,$k,1),$t[0];
print "\n";
}}}
print "\n";
my $iter = 1;
for my $i (0..3) {
for my $j (0..2) {
for my $k (0..1) {
my @t = qw/A B C D/;
printf "graph g%02d\n", $iter++;
printf " node input input A 32 1 B 32 1 C 32 1 D 32 1\n";
printf " node macc macc A 32 1 B 32 1 C 32 1 D 32 1\n";
printf " connect input A macc %s\n", splice(@t,$i,1);
printf " connect input B macc %s\n", splice(@t,$j,1);
printf " connect input C macc %s\n", splice(@t,$k,1);
printf " connect input D macc %s\n", splice(@t,0,1);
printf "endgraph\n";
printf "\n";
}}}
$iter = 1;
printf "graph gXL\n";
for my $i (0..3) {
for my $j (0..2) {
for my $k (0..1) {
my $id = sprintf "_%02d", $iter++;
my @t = qw/A B C D/;
printf " node input$id input A 16 B 16 C 16 D 16\n";
printf " node macc$id macc A 16 B 16 C 16 D 16\n";
printf " connect input$id A macc$id %s\n", splice(@t,$i,1);
printf " connect input$id B macc$id %s\n", splice(@t,$j,1);
printf " connect input$id C macc$id %s\n", splice(@t,$k,1);
printf " connect input$id D macc$id %s\n", splice(@t,0,1);
}}}
printf "endgraph\n";
printf "\n";
printf "swapgroup macc A B\n";
printf "swapgroup macc C D\n";
printf "swapperm macc A B C D : C D A B\n";
for my $i (@matches) {
for my $j (@non_matches) {
printf "solve %s %s\n", $i, $j;
}}
printf "expect 0\n\n";
for my $i (@matches) {
for my $j (@matches) {
printf "solve %s %s\n", $i, $j;
}}
printf "expect %d\n\n", @matches*@matches;
printf "solve g01 gXL false\n";
printf "expect 8\n";
printf "solve g03 gXL false\n";
printf "expect 8\n";
printf "solve g04 gXL false\n";
printf "expect 8\n";

View File

@ -0,0 +1,121 @@
#!/usr/bin/env splrun
//
// Test procedure for matching Gates with shorted inputs, as suggested in
// "SubCircuit Extraction with SubGraph Isomorphism. Zong Ling, Ph. D. IBM
// Almaden Research Center -- EDA Shape Processing zling@us.ibm.com.":
//
// Four NAND gates and a NOR gate. One NAND gate (G1) has no shorted inputs,
// one (G2) has an input shorted to VSS, one (G3) has an input shorted to VDD,
// and one (G4) has both inputs shorted together. Th last gate (G5) is a NOR
// gate.
var net;
function makeNAND(id)
{
net["${id}_VDD"] = "${id}_pa S";
net["${id}_VSS"] = "${id}_nb S";
net["${id}_A"] = "${id}_pa G";
net["${id}_B"] = "${id}_pb G";
net["${id}_Y"] = "${id}_pb D";
return <:>
: node ${id}_pa pmos S 1 D 1 G 1
: node ${id}_pb pmos S 1 D 1 G 1
: node ${id}_na nmos S 1 D 1 G 1
: node ${id}_nb nmos S 1 D 1 G 1
: connect ${id}_pa S ${id}_pb S
: connect ${id}_pa D ${id}_pb D
: connect ${id}_pa D ${id}_na D
: connect ${id}_na S ${id}_nb D
: connect ${id}_pa G ${id}_na G
: connect ${id}_pb G ${id}_nb G
</>;
}
function makeNOR(id)
{
net["${id}_VDD"] = "${id}_pa S";
net["${id}_VSS"] = "${id}_nb S";
net["${id}_A"] = "${id}_pa G";
net["${id}_B"] = "${id}_pb G";
net["${id}_Y"] = "${id}_pb D";
return <:>
: node ${id}_pa pmos S 1 D 1 G 1
: node ${id}_pb pmos S 1 D 1 G 1
: node ${id}_na nmos S 1 D 1 G 1
: node ${id}_nb nmos S 1 D 1 G 1
: connect ${id}_pa D ${id}_pb S
: connect ${id}_pb D ${id}_na D
: connect ${id}_pb D ${id}_nb D
: connect ${id}_na S ${id}_nb S
: connect ${id}_pa G ${id}_na G
: connect ${id}_pb G ${id}_nb G
</>;
}
write(<:>
: graph nand
: ${ makeNAND("G0") }
: extern ${net["G0_VDD"]}
: extern ${net["G0_VSS"]}
: extern ${net["G0_A"]}
: extern ${net["G0_B"]}
: extern ${net["G0_Y"]}
: endgraph
:
: graph nor
: ${ makeNOR("G0") }
: extern ${net["G0_VDD"]}
: extern ${net["G0_VSS"]}
: extern ${net["G0_A"]}
: extern ${net["G0_B"]}
: extern ${net["G0_Y"]}
: endgraph
:
: graph haystack
: ${ makeNAND("G1") }
: ${ makeNAND("G2") }
: ${ makeNAND("G3") }
: ${ makeNAND("G4") }
${ makeNOR("G5") }
:
: node vdd vsupply V 1
: connect vdd V ${net["G1_VDD"]}
: connect vdd V ${net["G2_VDD"]}
: connect vdd V ${net["G3_VDD"]}
: connect vdd V ${net["G4_VDD"]}
: connect vdd V ${net["G5_VDD"]}
:
: node vss vsupply V 1
: connect vss V ${net["G1_VSS"]}
: connect vss V ${net["G2_VSS"]}
: connect vss V ${net["G3_VSS"]}
: connect vss V ${net["G4_VSS"]}
: connect vss V ${net["G5_VSS"]}
:
: connect ${net["G2_A"]} ${net["G1_A"]}
: connect ${net["G2_B"]} ${net["G2_VSS"]}
:
: connect ${net["G3_A"]} ${net["G1_VDD"]}
: connect ${net["G3_B"]} ${net["G2_Y"]}
:
: connect ${net["G4_A"]} ${net["G1_Y"]}
: connect ${net["G4_B"]} ${net["G1_Y"]}
:
: connect ${net["G5_A"]} ${net["G3_Y"]}
: connect ${net["G5_B"]} ${net["G4_Y"]}
: endgraph
:
: solve nand haystack false
: clearoverlap
: expect 4
:
: solve nor haystack false
: clearoverlap
: expect 1
</>);

View File

@ -21,8 +21,8 @@
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/log.h"
#include "kernel/sha1.h"
#include "kernel/celltypes.h"
#include "libs/sha1/sha1.h"
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>

View File

@ -21,8 +21,8 @@
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/log.h"
#include "kernel/sha1.h"
#include "kernel/celltypes.h"
#include "libs/sha1/sha1.h"
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>