mirror of https://github.com/YosysHQ/yosys.git
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
81abb2517c
|
@ -36,6 +36,8 @@ echo
|
|||
|
||||
##########################################################################
|
||||
|
||||
./yosys tests/simple/fiedler-cooley.v
|
||||
|
||||
echo
|
||||
echo 'Testing...' && echo -en 'travis_fold:start:script.test\\r'
|
||||
echo
|
||||
|
|
|
@ -6,48 +6,15 @@ source .travis/common.sh
|
|||
|
||||
##########################################################################
|
||||
|
||||
# Fixing Travis's git clone
|
||||
echo
|
||||
echo 'Fixing git setup...' && echo -en 'travis_fold:start:before_install.git\\r'
|
||||
echo
|
||||
git fetch --unshallow && git fetch --tags
|
||||
|
||||
# For pull requests, we get more info about the git source.
|
||||
if [ z"$TRAVIS_PULL_REQUEST_SLUG" != z ]; then
|
||||
echo "- Fetching from pull request source"
|
||||
git remote add source https://github.com/$TRAVIS_PULL_REQUEST_SLUG.git
|
||||
git fetch source && git fetch --tags
|
||||
|
||||
echo "- Fetching the actual pull request"
|
||||
git fetch origin pull/$TRAVIS_PULL_REQUEST/head:pull-$TRAVIS_PULL_REQUEST-head
|
||||
git fetch origin pull/$TRAVIS_PULL_REQUEST/merge:pull-$TRAVIS_PULL_REQUEST-merge
|
||||
|
||||
git log -n 5 --graph pull-$TRAVIS_PULL_REQUEST-merge
|
||||
fi
|
||||
|
||||
# For building branches we need to fix the "detached head" state.
|
||||
if [ z"$TRAVIS_BRANCH" != z ]; then
|
||||
TRAVIS_COMMIT_ACTUAL=$(git log --pretty=format:'%H' -n 1)
|
||||
echo "- Fixing detached head (current $TRAVIS_COMMIT_ACTUAL -> $TRAVIS_COMMIT)"
|
||||
git remote -v
|
||||
git branch -v
|
||||
if [ x"$(git show-ref -s HEAD)" = x"$TRAVIS_COMMIT" ]; then
|
||||
echo "Checked out at $TRAVIS_COMMIT"
|
||||
else
|
||||
if [ z"$TRAVIS_PULL_REQUEST_SLUG" != z ]; then
|
||||
git fetch source $TRAVIS_COMMIT || echo "Unable to fetch $TRAVIS_COMMIT from source"
|
||||
fi
|
||||
git fetch origin $TRAVIS_COMMIT || echo "Unable to fetch $TRAVIS_COMMIT from origin"
|
||||
fi
|
||||
git branch -D $TRAVIS_BRANCH || true
|
||||
git checkout $TRAVIS_COMMIT -b $TRAVIS_BRANCH
|
||||
git branch -v
|
||||
fi
|
||||
|
||||
# Output status information.
|
||||
git status
|
||||
git describe --tags
|
||||
git log -n 5 --graph
|
||||
(
|
||||
set +e
|
||||
set -x
|
||||
git status
|
||||
git branch -v
|
||||
git log -n 5 --graph
|
||||
git log --format=oneline -n 20 --graph
|
||||
)
|
||||
echo
|
||||
echo -en 'travis_fold:end:before_install.git\\r'
|
||||
echo
|
||||
|
|
2
Makefile
2
Makefile
|
@ -100,7 +100,7 @@ LDFLAGS += -rdynamic
|
|||
LDLIBS += -lrt
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.8+$(shell cd $(YOSYS_SRC) && test -e .git && { git log --author=clifford@clifford.at --oneline 4d4665b.. | wc -l; })
|
||||
YOSYS_VER := 0.8+$(shell cd $(YOSYS_SRC) && test -e .git && { git log --author=clifford@clifford.at --oneline 4d4665b.. 2> /dev/null | wc -l; })
|
||||
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
|
||||
OBJS = kernel/version_$(GIT_REV).o
|
||||
|
||||
|
|
|
@ -23,7 +23,11 @@
|
|||
#include "kernel/celltypes.h"
|
||||
#include "kernel/cellaigs.h"
|
||||
#include "kernel/log.h"
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
@ -37,6 +41,7 @@ static const FDirection FD_NODIRECTION = 0x0;
|
|||
static const FDirection FD_IN = 0x1;
|
||||
static const FDirection FD_OUT = 0x2;
|
||||
static const FDirection FD_INOUT = 0x3;
|
||||
static const int FIRRTL_MAX_DSH_WIDTH_ERROR = 20; // For historic reasons, this is actually one greater than the maximum allowed shift width
|
||||
|
||||
// Get a port direction with respect to a specific module.
|
||||
FDirection getPortFDirection(IdString id, Module *module)
|
||||
|
@ -173,6 +178,26 @@ struct FirrtlWorker
|
|||
void process_instance(RTLIL::Cell *cell, vector<string> &wire_exprs)
|
||||
{
|
||||
std::string cell_type = fid(cell->type);
|
||||
std::string instanceOf;
|
||||
// If this is a parameterized module, its parent module is encoded in the cell type
|
||||
if (cell->type.substr(0, 8) == "$paramod")
|
||||
{
|
||||
std::string::iterator it;
|
||||
for (it = cell_type.begin(); it < cell_type.end(); it++)
|
||||
{
|
||||
switch (*it) {
|
||||
case '\\': /* FALL_THROUGH */
|
||||
case '=': /* FALL_THROUGH */
|
||||
case '\'': /* FALL_THROUGH */
|
||||
case '$': instanceOf.append("_"); break;
|
||||
default: instanceOf.append(1, *it); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
instanceOf = cell_type;
|
||||
}
|
||||
|
||||
std::string cell_name = cellname(cell);
|
||||
std::string cell_name_comment;
|
||||
|
@ -182,7 +207,13 @@ struct FirrtlWorker
|
|||
cell_name_comment = "";
|
||||
// Find the module corresponding to this instance.
|
||||
auto instModule = design->module(cell->type);
|
||||
wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), cell_type.c_str()));
|
||||
// If there is no instance for this, just return.
|
||||
if (instModule == NULL)
|
||||
{
|
||||
log_warning("No instance for %s.%s\n", cell_type.c_str(), cell_name.c_str());
|
||||
return;
|
||||
}
|
||||
wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceOf.c_str()));
|
||||
|
||||
for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
|
||||
if (it->second.size() > 0) {
|
||||
|
@ -194,20 +225,20 @@ struct FirrtlWorker
|
|||
std::string source, sink;
|
||||
switch (dir) {
|
||||
case FD_INOUT:
|
||||
log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", log_id(cell_type), log_signal(it->second));
|
||||
log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", cell_type.c_str(), log_signal(it->second));
|
||||
case FD_OUT:
|
||||
source = firstName;
|
||||
sink = secondName;
|
||||
break;
|
||||
case FD_NODIRECTION:
|
||||
log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", log_id(cell_type), log_signal(it->second));
|
||||
log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", cell_type.c_str(), log_signal(it->second));
|
||||
/* FALL_THROUGH */
|
||||
case FD_IN:
|
||||
source = secondName;
|
||||
sink = firstName;
|
||||
break;
|
||||
default:
|
||||
log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", log_id(cell_type), log_signal(it->second), dir);
|
||||
log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", cell_type.c_str(), log_signal(it->second), dir);
|
||||
break;
|
||||
}
|
||||
wire_exprs.push_back(stringf("\n%s%s <= %s", indent.c_str(), sink.c_str(), source.c_str()));
|
||||
|
@ -217,6 +248,20 @@ struct FirrtlWorker
|
|||
|
||||
}
|
||||
|
||||
// Given an expression for a shift amount, and a maximum width,
|
||||
// generate the FIRRTL expression for equivalent dynamic shift taking into account FIRRTL shift semantics.
|
||||
std::string gen_dshl(const string b_expr, const int b_padded_width)
|
||||
{
|
||||
string result = b_expr;
|
||||
if (b_padded_width >= FIRRTL_MAX_DSH_WIDTH_ERROR) {
|
||||
int max_shift_width_bits = FIRRTL_MAX_DSH_WIDTH_ERROR - 1;
|
||||
string max_shift_string = stringf("UInt<%d>(%d)", max_shift_width_bits, (1<<max_shift_width_bits) - 1);
|
||||
// Deal with the difference in semantics between FIRRTL and verilog
|
||||
result = stringf("mux(gt(%s, %s), %s, bits(%s, %d, 0))", b_expr.c_str(), max_shift_string.c_str(), max_shift_string.c_str(), b_expr.c_str(), max_shift_width_bits - 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
f << stringf(" module %s:\n", make_id(module->name));
|
||||
|
@ -225,6 +270,12 @@ struct FirrtlWorker
|
|||
for (auto wire : module->wires())
|
||||
{
|
||||
const auto wireName = make_id(wire->name);
|
||||
// If a wire has initial data, issue a warning since FIRRTL doesn't currently support it.
|
||||
if (wire->attributes.count("\\init")) {
|
||||
log_warning("Initial value (%s) for (%s.%s) not supported\n",
|
||||
wire->attributes.at("\\init").as_string().c_str(),
|
||||
log_id(module), log_id(wire));
|
||||
}
|
||||
if (wire->port_id)
|
||||
{
|
||||
if (wire->port_input && wire->port_output)
|
||||
|
@ -240,7 +291,8 @@ struct FirrtlWorker
|
|||
|
||||
for (auto cell : module->cells())
|
||||
{
|
||||
// Is this cell is a module instance?
|
||||
bool extract_y_bits = false; // Assume no extraction of final bits will be required.
|
||||
// Is this cell is a module instance?
|
||||
if (cell->type[0] != '$')
|
||||
{
|
||||
process_instance(cell, wire_exprs);
|
||||
|
@ -264,21 +316,21 @@ struct FirrtlWorker
|
|||
}
|
||||
|
||||
string primop;
|
||||
bool always_uint = false;
|
||||
bool always_uint = false;
|
||||
if (cell->type == "$not") primop = "not";
|
||||
if (cell->type == "$neg") primop = "neg";
|
||||
if (cell->type == "$logic_not") {
|
||||
else if (cell->type == "$neg") primop = "neg";
|
||||
else if (cell->type == "$logic_not") {
|
||||
primop = "eq";
|
||||
a_expr = stringf("%s, UInt(0)", a_expr.c_str());
|
||||
}
|
||||
if (cell->type == "$reduce_and") primop = "andr";
|
||||
if (cell->type == "$reduce_or") primop = "orr";
|
||||
if (cell->type == "$reduce_xor") primop = "xorr";
|
||||
if (cell->type == "$reduce_xnor") {
|
||||
else if (cell->type == "$reduce_and") primop = "andr";
|
||||
else if (cell->type == "$reduce_or") primop = "orr";
|
||||
else if (cell->type == "$reduce_xor") primop = "xorr";
|
||||
else if (cell->type == "$reduce_xnor") {
|
||||
primop = "not";
|
||||
a_expr = stringf("xorr(%s)", a_expr.c_str());
|
||||
}
|
||||
if (cell->type == "$reduce_bool") {
|
||||
else if (cell->type == "$reduce_bool") {
|
||||
primop = "neq";
|
||||
// Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand.
|
||||
bool a_signed = cell->parameters.at("\\A_SIGNED").as_bool();
|
||||
|
@ -297,14 +349,15 @@ struct FirrtlWorker
|
|||
continue;
|
||||
}
|
||||
if (cell->type.in("$add", "$sub", "$mul", "$div", "$mod", "$xor", "$and", "$or", "$eq", "$eqx",
|
||||
"$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl",
|
||||
"$logic_and", "$logic_or"))
|
||||
"$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl",
|
||||
"$logic_and", "$logic_or"))
|
||||
{
|
||||
string y_id = make_id(cell->name);
|
||||
bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool();
|
||||
int y_width = cell->parameters.at("\\Y_WIDTH").as_int();
|
||||
string a_expr = make_expr(cell->getPort("\\A"));
|
||||
string b_expr = make_expr(cell->getPort("\\B"));
|
||||
int b_padded_width = cell->parameters.at("\\B_WIDTH").as_int();
|
||||
wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
|
||||
|
||||
if (cell->parameters.at("\\A_SIGNED").as_bool()) {
|
||||
|
@ -315,67 +368,93 @@ struct FirrtlWorker
|
|||
if (cell->parameters.at("\\B_SIGNED").as_bool()) {
|
||||
b_expr = "asSInt(" + b_expr + ")";
|
||||
}
|
||||
b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width);
|
||||
if (b_padded_width < y_width) {
|
||||
auto b_sig = cell->getPort("\\B");
|
||||
b_padded_width = y_width;
|
||||
}
|
||||
}
|
||||
|
||||
a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
|
||||
auto a_sig = cell->getPort("\\A");
|
||||
|
||||
if (cell->parameters.at("\\A_SIGNED").as_bool() & (cell->type == "$shr")) {
|
||||
a_expr = "asUInt(" + a_expr + ")";
|
||||
}
|
||||
|
||||
string primop;
|
||||
bool always_uint = false;
|
||||
bool always_uint = false;
|
||||
if (cell->type == "$add") primop = "add";
|
||||
if (cell->type == "$sub") primop = "sub";
|
||||
if (cell->type == "$mul") primop = "mul";
|
||||
if (cell->type == "$div") primop = "div";
|
||||
if (cell->type == "$mod") primop = "rem";
|
||||
if (cell->type == "$and") {
|
||||
else if (cell->type == "$sub") primop = "sub";
|
||||
else if (cell->type == "$mul") primop = "mul";
|
||||
else if (cell->type == "$div") primop = "div";
|
||||
else if (cell->type == "$mod") primop = "rem";
|
||||
else if (cell->type == "$and") {
|
||||
primop = "and";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$or" ) {
|
||||
else if (cell->type == "$or" ) {
|
||||
primop = "or";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$xor") {
|
||||
else if (cell->type == "$xor") {
|
||||
primop = "xor";
|
||||
always_uint = true;
|
||||
}
|
||||
if ((cell->type == "$eq") | (cell->type == "$eqx")) {
|
||||
else if ((cell->type == "$eq") | (cell->type == "$eqx")) {
|
||||
primop = "eq";
|
||||
always_uint = true;
|
||||
}
|
||||
if ((cell->type == "$ne") | (cell->type == "$nex")) {
|
||||
else if ((cell->type == "$ne") | (cell->type == "$nex")) {
|
||||
primop = "neq";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$gt") {
|
||||
else if (cell->type == "$gt") {
|
||||
primop = "gt";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$ge") {
|
||||
else if (cell->type == "$ge") {
|
||||
primop = "geq";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$lt") {
|
||||
else if (cell->type == "$lt") {
|
||||
primop = "lt";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$le") {
|
||||
else if (cell->type == "$le") {
|
||||
primop = "leq";
|
||||
always_uint = true;
|
||||
}
|
||||
if ((cell->type == "$shl") | (cell->type == "$sshl")) primop = "dshl";
|
||||
if ((cell->type == "$shr") | (cell->type == "$sshr")) primop = "dshr";
|
||||
if ((cell->type == "$logic_and")) {
|
||||
else if ((cell->type == "$shl") | (cell->type == "$sshl")) {
|
||||
// FIRRTL will widen the result (y) by the amount of the shift.
|
||||
// We'll need to offset this by extracting the un-widened portion as Verilog would do.
|
||||
extract_y_bits = true;
|
||||
// Is the shift amount constant?
|
||||
auto b_sig = cell->getPort("\\B");
|
||||
if (b_sig.is_fully_const()) {
|
||||
primop = "shl";
|
||||
} else {
|
||||
primop = "dshl";
|
||||
// Convert from FIRRTL left shift semantics.
|
||||
b_expr = gen_dshl(b_expr, b_padded_width);
|
||||
}
|
||||
}
|
||||
else if ((cell->type == "$shr") | (cell->type == "$sshr")) {
|
||||
// We don't need to extract a specific range of bits.
|
||||
extract_y_bits = false;
|
||||
// Is the shift amount constant?
|
||||
auto b_sig = cell->getPort("\\B");
|
||||
if (b_sig.is_fully_const()) {
|
||||
primop = "shr";
|
||||
} else {
|
||||
primop = "dshr";
|
||||
}
|
||||
}
|
||||
else if ((cell->type == "$logic_and")) {
|
||||
primop = "and";
|
||||
a_expr = "neq(" + a_expr + ", UInt(0))";
|
||||
b_expr = "neq(" + b_expr + ", UInt(0))";
|
||||
always_uint = true;
|
||||
}
|
||||
if ((cell->type == "$logic_or")) {
|
||||
else if ((cell->type == "$logic_or")) {
|
||||
primop = "or";
|
||||
a_expr = "neq(" + a_expr + ", UInt(0))";
|
||||
b_expr = "neq(" + b_expr + ", UInt(0))";
|
||||
|
@ -388,6 +467,11 @@ struct FirrtlWorker
|
|||
|
||||
string expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str());
|
||||
|
||||
// Deal with FIRRTL's "shift widens" semantics
|
||||
if (extract_y_bits) {
|
||||
expr = stringf("bits(%s, %d, 0)", expr.c_str(), y_width - 1);
|
||||
}
|
||||
|
||||
if ((is_signed && !always_uint) || cell->type.in("$sub"))
|
||||
expr = stringf("asUInt(%s)", expr.c_str());
|
||||
|
||||
|
@ -513,7 +597,65 @@ struct FirrtlWorker
|
|||
continue;
|
||||
}
|
||||
|
||||
log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
|
||||
// This may be a parameterized module - paramod.
|
||||
if (cell->type.substr(0, 8) == "$paramod")
|
||||
{
|
||||
process_instance(cell, wire_exprs);
|
||||
continue;
|
||||
}
|
||||
if (cell->type == "$shiftx") {
|
||||
// assign y = a[b +: y_width];
|
||||
// We'll extract the correct bits as part of the primop.
|
||||
|
||||
string y_id = make_id(cell->name);
|
||||
int y_width = cell->parameters.at("\\Y_WIDTH").as_int();
|
||||
string a_expr = make_expr(cell->getPort("\\A"));
|
||||
// Get the initial bit selector
|
||||
string b_expr = make_expr(cell->getPort("\\B"));
|
||||
wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
|
||||
|
||||
if (cell->getParam("\\B_SIGNED").as_bool()) {
|
||||
// Use validif to constrain the selection (test the sign bit)
|
||||
auto b_string = b_expr.c_str();
|
||||
int b_sign = cell->parameters.at("\\B_WIDTH").as_int() - 1;
|
||||
b_expr = stringf("validif(not(bits(%s, %d, %d)), %s)", b_string, b_sign, b_sign, b_string);
|
||||
}
|
||||
string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_expr.c_str());
|
||||
|
||||
cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
|
||||
register_reverse_wire_map(y_id, cell->getPort("\\Y"));
|
||||
continue;
|
||||
}
|
||||
if (cell->type == "$shift") {
|
||||
// assign y = a >> b;
|
||||
// where b may be negative
|
||||
|
||||
string y_id = make_id(cell->name);
|
||||
int y_width = cell->parameters.at("\\Y_WIDTH").as_int();
|
||||
string a_expr = make_expr(cell->getPort("\\A"));
|
||||
string b_expr = make_expr(cell->getPort("\\B"));
|
||||
auto b_string = b_expr.c_str();
|
||||
int b_padded_width = cell->parameters.at("\\B_WIDTH").as_int();
|
||||
string expr;
|
||||
wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
|
||||
|
||||
if (cell->getParam("\\B_SIGNED").as_bool()) {
|
||||
// We generate a left or right shift based on the sign of b.
|
||||
std::string dshl = stringf("bits(dshl(%s, %s), 0, %d)", a_expr.c_str(), gen_dshl(b_expr, b_padded_width).c_str(), y_width);
|
||||
std::string dshr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string);
|
||||
expr = stringf("mux(%s < 0, %s, %s)",
|
||||
b_string,
|
||||
dshl.c_str(),
|
||||
dshr.c_str()
|
||||
);
|
||||
} else {
|
||||
expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string);
|
||||
}
|
||||
cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
|
||||
register_reverse_wire_map(y_id, cell->getPort("\\Y"));
|
||||
continue;
|
||||
}
|
||||
log_warning("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
|
||||
}
|
||||
|
||||
for (auto conn : module->connections())
|
||||
|
@ -629,38 +771,53 @@ struct FirrtlBackend : public Backend {
|
|||
log(" write_firrtl [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Write a FIRRTL netlist of the current design.\n");
|
||||
log("The following commands are executed by this command:\n");
|
||||
log(" pmuxtree\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-aig") {
|
||||
// aig_mode = true;
|
||||
// continue;
|
||||
// }
|
||||
break;
|
||||
size_t argidx = args.size(); // We aren't expecting any arguments.
|
||||
|
||||
// If we weren't explicitly passed a filename, use the last argument (if it isn't a flag).
|
||||
if (filename == "") {
|
||||
if (argidx > 0 && args[argidx - 1][0] != '-') {
|
||||
// extra_args and friends need to see this argument.
|
||||
argidx -= 1;
|
||||
filename = args[argidx];
|
||||
}
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
if (!design->full_selection())
|
||||
log_cmd_error("This command only operates on fully selected designs!\n");
|
||||
|
||||
log_header(design, "Executing FIRRTL backend.\n");
|
||||
log_push();
|
||||
|
||||
Module *top = design->top_module();
|
||||
|
||||
if (top == nullptr)
|
||||
log_error("No top module found!\n");
|
||||
Pass::call(design, stringf("pmuxtree"));
|
||||
|
||||
namecache.clear();
|
||||
autoid_counter = 0;
|
||||
|
||||
// Get the top module, or a reasonable facsimile - we need something for the circuit name.
|
||||
Module *top = design->top_module();
|
||||
Module *last = nullptr;
|
||||
// Generate module and wire names.
|
||||
for (auto module : design->modules()) {
|
||||
make_id(module->name);
|
||||
last = module;
|
||||
if (top == nullptr && module->get_bool_attribute("\\top")) {
|
||||
top = module;
|
||||
}
|
||||
for (auto wire : module->wires())
|
||||
if (wire->port_id)
|
||||
make_id(wire->name);
|
||||
}
|
||||
|
||||
if (top == nullptr)
|
||||
top = last;
|
||||
|
||||
*f << stringf("circuit %s:\n", make_id(top->name));
|
||||
|
||||
for (auto module : design->modules())
|
||||
|
|
|
@ -1310,6 +1310,15 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
}
|
||||
}
|
||||
|
||||
if (reg_ct.count(cell->type) && cell->hasPort("\\Q")) {
|
||||
std::stringstream ss;
|
||||
dump_reg_init(ss, cell->getPort("\\Q"));
|
||||
if (!ss.str().empty()) {
|
||||
f << stringf("%sinitial %s.Q", indent.c_str(), cell_name.c_str());
|
||||
f << ss.str();
|
||||
f << ";\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
|
||||
|
|
|
@ -644,7 +644,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
|
||||
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
|
||||
log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
|
||||
this_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
|
||||
this_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
|
||||
delete left_at_zero_ast;
|
||||
delete right_at_zero_ast;
|
||||
} else
|
||||
|
@ -792,7 +792,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
// everything should have been handled above -> print error if not.
|
||||
default:
|
||||
for (auto f : log_files)
|
||||
current_ast->dumpAst(f, "verilog-ast> ");
|
||||
current_ast_mod->dumpAst(f, "verilog-ast> ");
|
||||
log_file_error(filename, linenum, "Don't know how to detect sign and width for %s node!\n", type2str(type).c_str());
|
||||
}
|
||||
|
||||
|
@ -1034,7 +1034,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
|
||||
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
|
||||
log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
|
||||
int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
|
||||
int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
|
||||
AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ?
|
||||
children[0]->children[1]->clone() : children[0]->children[0]->clone());
|
||||
fake_ast->children[0]->delete_children();
|
||||
|
@ -1565,7 +1565,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
// everything should have been handled above -> print error if not.
|
||||
default:
|
||||
for (auto f : log_files)
|
||||
current_ast->dumpAst(f, "verilog-ast> ");
|
||||
current_ast_mod->dumpAst(f, "verilog-ast> ");
|
||||
type_name = type2str(type);
|
||||
log_file_error(filename, linenum, "Don't know how to generate RTLIL code for %s node!\n", type_name.c_str());
|
||||
}
|
||||
|
|
|
@ -642,6 +642,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
// (iterate by index as e.g. auto wires can add new children in the process)
|
||||
for (size_t i = 0; i < children.size(); i++) {
|
||||
bool did_something_here = true;
|
||||
bool backup_flag_autowire = flag_autowire;
|
||||
if ((type == AST_GENFOR || type == AST_FOR) && i >= 3)
|
||||
break;
|
||||
if ((type == AST_GENIF || type == AST_GENCASE) && i >= 1)
|
||||
|
@ -652,6 +653,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
break;
|
||||
if (type == AST_PREFIX && i >= 1)
|
||||
break;
|
||||
if (type == AST_DEFPARAM && i == 0)
|
||||
flag_autowire = true;
|
||||
while (did_something_here && i < children.size()) {
|
||||
bool const_fold_here = const_fold, in_lvalue_here = in_lvalue;
|
||||
int width_hint_here = width_hint;
|
||||
|
@ -686,6 +689,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
children.erase(children.begin() + (i--));
|
||||
did_something = true;
|
||||
}
|
||||
flag_autowire = backup_flag_autowire;
|
||||
}
|
||||
for (auto &attr : attributes) {
|
||||
while (attr.second->simplify(true, false, false, stage, -1, false, true))
|
||||
|
@ -934,12 +938,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
}
|
||||
}
|
||||
if (current_scope.count(str) == 0) {
|
||||
// log_warning("Creating auto-wire `%s' in module `%s'.\n", str.c_str(), current_ast_mod->str.c_str());
|
||||
AstNode *auto_wire = new AstNode(AST_AUTOWIRE);
|
||||
auto_wire->str = str;
|
||||
current_ast_mod->children.push_back(auto_wire);
|
||||
current_scope[str] = auto_wire;
|
||||
did_something = true;
|
||||
if (flag_autowire) {
|
||||
AstNode *auto_wire = new AstNode(AST_AUTOWIRE);
|
||||
auto_wire->str = str;
|
||||
current_ast_mod->children.push_back(auto_wire);
|
||||
current_scope[str] = auto_wire;
|
||||
did_something = true;
|
||||
} else {
|
||||
log_file_error(filename, linenum, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str());
|
||||
}
|
||||
}
|
||||
if (id2ast != current_scope[str]) {
|
||||
id2ast = current_scope[str];
|
||||
|
@ -1689,7 +1696,7 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
|
||||
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
|
||||
log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
|
||||
int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
|
||||
int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
|
||||
|
||||
assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
|
||||
new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone()));
|
||||
|
@ -1778,7 +1785,7 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
|
||||
if (str == "\\$past")
|
||||
{
|
||||
if (width_hint <= 0)
|
||||
if (width_hint < 0)
|
||||
goto replace_fcall_later;
|
||||
|
||||
int num_steps = 1;
|
||||
|
|
|
@ -1920,6 +1920,10 @@ struct VerificPass : public Pass {
|
|||
// WARNING: instantiating unknown module 'XYZ' (VERI-1063)
|
||||
Message::SetMessageType("VERI-1063", VERIFIC_ERROR);
|
||||
|
||||
#ifndef DB_PRESERVE_INITIAL_VALUE
|
||||
# warning Verific was built without DB_PRESERVE_INITIAL_VALUE.
|
||||
#endif
|
||||
|
||||
set_verific_global_flags = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -179,6 +179,7 @@ int main(int argc, char **argv)
|
|||
{
|
||||
std::string frontend_command = "auto";
|
||||
std::string backend_command = "auto";
|
||||
std::vector<std::string> vlog_defines;
|
||||
std::vector<std::string> passes_commands;
|
||||
std::vector<std::string> plugin_filenames;
|
||||
std::string output_filename = "";
|
||||
|
@ -268,7 +269,10 @@ int main(int argc, char **argv)
|
|||
printf(" -A\n");
|
||||
printf(" will call abort() at the end of the script. for debugging\n");
|
||||
printf("\n");
|
||||
printf(" -D <header_id>[:<filename>]\n");
|
||||
printf(" -D <macro>[=<value>]\n");
|
||||
printf(" set the specified Verilog define (via \"read -define\")\n");
|
||||
printf("\n");
|
||||
printf(" -P <header_id>[:<filename>]\n");
|
||||
printf(" dump the design when printing the specified log header to a file.\n");
|
||||
printf(" yosys_dump_<header_id>.il is used as filename if none is specified.\n");
|
||||
printf(" Use 'ALL' as <header_id> to dump at every header.\n");
|
||||
|
@ -307,7 +311,7 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "MXAQTVSm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:E:")) != -1)
|
||||
while ((opt = getopt(argc, argv, "MXAQTVSm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
|
@ -408,6 +412,9 @@ int main(int argc, char **argv)
|
|||
std::regex_constants::egrep));
|
||||
break;
|
||||
case 'D':
|
||||
vlog_defines.push_back(optarg);
|
||||
break;
|
||||
case 'P':
|
||||
{
|
||||
auto args = split_tokens(optarg, ":");
|
||||
if (!args.empty() && args[0] == "ALL") {
|
||||
|
@ -473,6 +480,13 @@ int main(int argc, char **argv)
|
|||
shell(yosys_design);
|
||||
}
|
||||
|
||||
if (!vlog_defines.empty()) {
|
||||
std::string vdef_cmd = "read -define";
|
||||
for (auto vdef : vlog_defines)
|
||||
vdef_cmd += " " + vdef;
|
||||
run_pass(vdef_cmd);
|
||||
}
|
||||
|
||||
while (optind < argc)
|
||||
run_frontend(argv[optind++], frontend_command, output_filename == "-" ? &backend_command : NULL);
|
||||
|
||||
|
|
|
@ -2410,6 +2410,9 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed)
|
|||
if (connections_.count("\\Y"))
|
||||
parameters["\\Y_WIDTH"] = GetSize(connections_["\\Y"]);
|
||||
|
||||
if (connections_.count("\\Q"))
|
||||
parameters["\\WIDTH"] = GetSize(connections_["\\Q"]);
|
||||
|
||||
check();
|
||||
}
|
||||
|
||||
|
|
|
@ -143,6 +143,9 @@ struct SetundefPass : public Pass {
|
|||
log(" -init\n");
|
||||
log(" also create/update init values for flip-flops\n");
|
||||
log("\n");
|
||||
log(" -params\n");
|
||||
log(" replace undef in cell parameters\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
|
@ -150,6 +153,7 @@ struct SetundefPass : public Pass {
|
|||
bool undriven_mode = false;
|
||||
bool expose_mode = false;
|
||||
bool init_mode = false;
|
||||
bool params_mode = false;
|
||||
SetundefWorker worker;
|
||||
|
||||
log_header(design, "Executing SETUNDEF pass (replace undef values with defined constants).\n");
|
||||
|
@ -199,6 +203,10 @@ struct SetundefPass : public Pass {
|
|||
init_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-params") {
|
||||
params_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) {
|
||||
got_value = true;
|
||||
worker.next_bit_mode = MODE_RANDOM;
|
||||
|
@ -228,6 +236,18 @@ struct SetundefPass : public Pass {
|
|||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
if (params_mode)
|
||||
{
|
||||
for (auto *cell : module->selected_cells()) {
|
||||
for (auto ¶meter : cell->parameters) {
|
||||
for (auto &bit : parameter.second.bits) {
|
||||
if (bit > RTLIL::State::S1)
|
||||
bit = worker.next_bit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (undriven_mode)
|
||||
{
|
||||
if (!module->processes.empty())
|
||||
|
|
|
@ -140,6 +140,23 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
|
|||
}
|
||||
}
|
||||
|
||||
// Return the "basic" type for an array item.
|
||||
std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) {
|
||||
std::string basicType = celltype;
|
||||
if (celltype.substr(0, 7) == "$array:") {
|
||||
int pos_idx = celltype.find_first_of(':');
|
||||
int pos_num = celltype.find_first_of(':', pos_idx + 1);
|
||||
int pos_type = celltype.find_first_of(':', pos_num + 1);
|
||||
basicType = celltype.substr(pos_type + 1);
|
||||
if (pos != nullptr) {
|
||||
pos[0] = pos_idx;
|
||||
pos[1] = pos_num;
|
||||
pos[2] = pos_type;
|
||||
}
|
||||
}
|
||||
return basicType;
|
||||
}
|
||||
|
||||
bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector<std::string> &libdirs)
|
||||
{
|
||||
bool did_something = false;
|
||||
|
@ -178,9 +195,11 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
|
|||
std::vector<RTLIL::SigSpec> connections_to_add_signal;
|
||||
|
||||
if (cell->type.substr(0, 7) == "$array:") {
|
||||
int pos_idx = cell->type.str().find_first_of(':');
|
||||
int pos_num = cell->type.str().find_first_of(':', pos_idx + 1);
|
||||
int pos_type = cell->type.str().find_first_of(':', pos_num + 1);
|
||||
int pos[3];
|
||||
basic_cell_type(cell->type.str(), pos);
|
||||
int pos_idx = pos[0];
|
||||
int pos_num = pos[1];
|
||||
int pos_type = pos[2];
|
||||
int idx = atoi(cell->type.str().substr(pos_idx + 1, pos_num).c_str());
|
||||
int num = atoi(cell->type.str().substr(pos_num + 1, pos_type).c_str());
|
||||
array_cells[cell] = std::pair<int, int>(idx, num);
|
||||
|
@ -439,10 +458,7 @@ void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::
|
|||
for (auto cell : mod->cells()) {
|
||||
std::string celltype = cell->type.str();
|
||||
if (celltype.substr(0, 7) == "$array:") {
|
||||
int pos_idx = celltype.find_first_of(':');
|
||||
int pos_num = celltype.find_first_of(':', pos_idx + 1);
|
||||
int pos_type = celltype.find_first_of(':', pos_num + 1);
|
||||
celltype = celltype.substr(pos_type + 1);
|
||||
celltype = basic_cell_type(celltype);
|
||||
}
|
||||
if (design->module(celltype))
|
||||
hierarchy_worker(design, used, design->module(celltype), indent+4);
|
||||
|
@ -502,9 +518,19 @@ int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db)
|
|||
if (db.count(module) == 0) {
|
||||
int score = 0;
|
||||
db[module] = 0;
|
||||
for (auto cell : module->cells())
|
||||
if (design->module(cell->type))
|
||||
score = max(score, find_top_mod_score(design, design->module(cell->type), db) + 1);
|
||||
for (auto cell : module->cells()) {
|
||||
std::string celltype = cell->type.str();
|
||||
// Is this an array instance
|
||||
if (celltype.substr(0, 7) == "$array:") {
|
||||
celltype = basic_cell_type(celltype);
|
||||
}
|
||||
// Is this cell a module instance?
|
||||
auto instModule = design->module(celltype);
|
||||
// If there is no instance for this, issue a warning.
|
||||
if (instModule != nullptr) {
|
||||
score = max(score, find_top_mod_score(design, instModule, db) + 1);
|
||||
}
|
||||
}
|
||||
db[module] = score;
|
||||
}
|
||||
return db.at(module);
|
||||
|
|
|
@ -174,8 +174,6 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
|
|||
cell->unsetParam("\\CLR_POLARITY");
|
||||
cell->unsetPort("\\SET");
|
||||
cell->unsetPort("\\CLR");
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -186,11 +184,12 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
|
|||
cell->unsetParam("\\CLR_POLARITY");
|
||||
cell->unsetPort("\\SET");
|
||||
cell->unsetPort("\\CLR");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
||||
if (!hasreset)
|
||||
{
|
||||
IdString new_type;
|
||||
|
||||
|
@ -207,8 +206,10 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
|
|||
cell->unsetPort("\\S");
|
||||
cell->unsetPort("\\R");
|
||||
|
||||
return did_something;
|
||||
return true;
|
||||
}
|
||||
|
||||
return did_something;
|
||||
}
|
||||
|
||||
bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
|
||||
|
|
|
@ -38,7 +38,8 @@ struct WreduceConfig
|
|||
"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
|
||||
"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
|
||||
"$add", "$sub", "$mul", // "$div", "$mod", "$pow",
|
||||
"$mux", "$pmux"
|
||||
"$mux", "$pmux",
|
||||
"$dff", "$adff"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -134,6 +135,78 @@ struct WreduceWorker
|
|||
module->connect(sig_y.extract(n_kept, n_removed), sig_removed);
|
||||
}
|
||||
|
||||
void run_cell_dff(Cell *cell)
|
||||
{
|
||||
// Reduce size of FF if inputs are just sign/zero extended or output bit is not used
|
||||
|
||||
SigSpec sig_d = mi.sigmap(cell->getPort("\\D"));
|
||||
SigSpec sig_q = mi.sigmap(cell->getPort("\\Q"));
|
||||
|
||||
int width_before = GetSize(sig_q);
|
||||
|
||||
if (width_before == 0)
|
||||
return;
|
||||
|
||||
bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0;
|
||||
bool sign_ext = !zero_ext;
|
||||
|
||||
for (int i = GetSize(sig_q)-1; i >= 0; i--)
|
||||
{
|
||||
if (zero_ext && sig_d[i] == State::S0) {
|
||||
module->connect(sig_q[i], State::S0);
|
||||
sig_d.remove(i);
|
||||
sig_q.remove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1]) {
|
||||
module->connect(sig_q[i], sig_q[i-1]);
|
||||
sig_d.remove(i);
|
||||
sig_q.remove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto info = mi.query(sig_q[i]);
|
||||
if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
|
||||
sig_d.remove(i);
|
||||
sig_q.remove(i);
|
||||
zero_ext = false;
|
||||
sign_ext = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (width_before == GetSize(sig_q))
|
||||
return;
|
||||
|
||||
if (GetSize(sig_q) == 0) {
|
||||
log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type));
|
||||
return;
|
||||
}
|
||||
|
||||
log("Removed top %d bits (of %d) from mux cell %s.%s (%s).\n", width_before - GetSize(sig_q), width_before,
|
||||
log_id(module), log_id(cell), log_id(cell->type));
|
||||
|
||||
for (auto bit : sig_d)
|
||||
work_queue_bits.insert(bit);
|
||||
|
||||
for (auto bit : sig_q)
|
||||
work_queue_bits.insert(bit);
|
||||
|
||||
// Narrow ARST_VALUE parameter to new size.
|
||||
if (cell->parameters.count("\\ARST_VALUE")) {
|
||||
Const arst_value = cell->getParam("\\ARST_VALUE");
|
||||
arst_value.bits.resize(GetSize(sig_q));
|
||||
cell->setParam("\\ARST_VALUE", arst_value);
|
||||
}
|
||||
|
||||
cell->setPort("\\D", sig_d);
|
||||
cell->setPort("\\Q", sig_q);
|
||||
cell->fixup_parameters();
|
||||
}
|
||||
|
||||
void run_reduce_inport(Cell *cell, char port, int max_port_size, bool &port_signed, bool &did_something)
|
||||
{
|
||||
port_signed = cell->getParam(stringf("\\%c_SIGNED", port)).as_bool();
|
||||
|
@ -176,6 +249,9 @@ struct WreduceWorker
|
|||
if (cell->type.in("$mux", "$pmux"))
|
||||
return run_cell_mux(cell);
|
||||
|
||||
if (cell->type.in("$dff", "$adff"))
|
||||
return run_cell_dff(cell);
|
||||
|
||||
SigSpec sig = mi.sigmap(cell->getPort("\\Y"));
|
||||
|
||||
if (sig.has_const())
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
/ice40_dsp_pm.h
|
|
@ -0,0 +1,8 @@
|
|||
OBJS += passes/pmgen/ice40_dsp.o
|
||||
|
||||
passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
|
||||
EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h
|
||||
.SECONDARY: passes/pmgen/ice40_dsp_pm.h
|
||||
|
||||
passes/pmgen/ice40_dsp_pm.h: passes/pmgen/ice40_dsp.pmg passes/pmgen/pmgen.py
|
||||
$(P) cd passes/pmgen && python3 pmgen.py ice40_dsp
|
|
@ -0,0 +1,224 @@
|
|||
Pattern Matcher Generator
|
||||
=========================
|
||||
|
||||
The program `pmgen.py` reads a `.pmg` (Pattern Matcher Generator) file and
|
||||
writes a header-only C++ library that implements that pattern matcher.
|
||||
|
||||
The "patterns" in this context are subgraphs in a Yosys RTLIL netlist.
|
||||
|
||||
The algorithm used in the generated pattern matcher is a simple recursive
|
||||
search with backtracking. It is left to the author of the `.pmg` file to
|
||||
determine an efficient cell order for the search that allows for maximum
|
||||
use of indices and early backtracking.
|
||||
|
||||
|
||||
API of Generated Matcher
|
||||
========================
|
||||
|
||||
When `pmgen.py` reads a `foobar.pmg` file, it writes `foobar_pm.h` containing
|
||||
a class `foobar_pm`. That class is instanciated with an RTLIL module and a
|
||||
list of cells from that module:
|
||||
|
||||
foobar_pm pm(module, module->selected_cells());
|
||||
|
||||
The caller must make sure that none of the cells in the 2nd argument are
|
||||
deleted for as long as the patter matcher instance is used.
|
||||
|
||||
At any time it is possible to disable cells, preventing them from showing
|
||||
up in any future matches:
|
||||
|
||||
pm.blacklist(some_cell);
|
||||
|
||||
The `.run(callback_function)` method searches for all matches and calls the
|
||||
callback function for each found match:
|
||||
|
||||
pm.run([&](){
|
||||
log("found matching 'foo' cell: %s\n", log_id(pm.st.foo));
|
||||
log(" with 'bar' cell: %s\n", log_id(pm.st.bar));
|
||||
});
|
||||
|
||||
The `.pmg` file declares matcher state variables that are accessible via the
|
||||
`.st.<state_name>` members. (The `.st` member is of type `foobar_pm::state_t`.)
|
||||
|
||||
Similarly the `.pmg` file declares user data variables that become members of
|
||||
`.ud`, a struct of type `foobar_pm::udata_t`.
|
||||
|
||||
|
||||
The .pmg File Format
|
||||
====================
|
||||
|
||||
The `.pmg` file format is a simple line-based file format. For the most part
|
||||
lines consist of whitespace-separated tokens.
|
||||
|
||||
Lines in `.pmg` files starting with `//` are comments.
|
||||
|
||||
Declaring state variables
|
||||
-------------------------
|
||||
|
||||
One or more state variables can be declared using the `state` statement,
|
||||
followed by a C++ type in angle brackets, followed by a whitespace separated
|
||||
list of variable names. For example:
|
||||
|
||||
state <bool> flag1 flag2 happy big
|
||||
state <SigSpec> sigA sigB sigY
|
||||
|
||||
State variables are automatically managed by the generated backtracking algorithm
|
||||
and saved and restored as needed.
|
||||
|
||||
They are automatically initialized to the default constructed value of their type
|
||||
when `.run(callback_function)` is called.
|
||||
|
||||
Declaring udata variables
|
||||
-------------------------
|
||||
|
||||
Udata (user-data) variables can be used for example to configure the matcher or
|
||||
the callback function used to perform actions on found matches.
|
||||
|
||||
There is no automatic management of udata variables. For this reason it is
|
||||
recommended that the user-supplied matcher code treats them as read-only
|
||||
variables.
|
||||
|
||||
They are declared like state variables, just using the `udata` statement:
|
||||
|
||||
udata <int> min_data_width max_data_width
|
||||
udata <IdString> data_port_name
|
||||
|
||||
They are atomatically initialzed to the default constructed value of their type
|
||||
when ther pattern matcher object is constructed.
|
||||
|
||||
Embedded C++ code
|
||||
-----------------
|
||||
|
||||
Many statements in a `.pmg` file contain C++ code. However, there are some
|
||||
slight additions to regular C++/Yosys/RTLIL code that make it a bit easier to
|
||||
write matchers:
|
||||
|
||||
- Identifiers starting with a dollar sign or backslash are automatically
|
||||
converted to special IdString variables that are initialized when the
|
||||
matcher object is constructed.
|
||||
|
||||
- The `port(<cell>, <portname>)` function is a handy alias for
|
||||
`sigmap(<cell>->getPort(<portname>))`.
|
||||
|
||||
- Similarly `param(<cell>, <paramname>)` looks up a parameter on a cell.
|
||||
|
||||
- The function `nusers(<sigspec>)` returns the number of different cells
|
||||
connected to any of the given signal bits, plus one if any of the signal
|
||||
bits is also a primary input or primary output.
|
||||
|
||||
- In `code..endcode` blocks there exist `accept`, `reject`, and `branch`
|
||||
statements.
|
||||
|
||||
- In `index` statements there is a special `===` operator for the index
|
||||
lookup.
|
||||
|
||||
Matching cells
|
||||
--------------
|
||||
|
||||
Cells are matched using `match..endmatch` blocks. For example:
|
||||
|
||||
match mul
|
||||
if ff
|
||||
select mul->type == $mul
|
||||
select nusers(port(mul, \Y) == 2
|
||||
index <SigSpec> port(mul, \Y) === port(ff, \D)
|
||||
filter some_weird_function(mul) < other_weird_function(ff)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
A `match` block starts with `match <statevar>` and implicitly generates
|
||||
a state variable `<statevar>` of type `RTLIL::Cell*`.
|
||||
|
||||
All statements in the match block are optional. (An empty match block
|
||||
would simply match each and every cell in the module.)
|
||||
|
||||
The `if <expression>` statement makes the match block conditional. If
|
||||
`<expression>` evaluates to `false` then the match block will be ignored
|
||||
and the corresponding state variable is set to `nullptr`. In our example
|
||||
we only try to match the `mul` cell if the `ff` state variable points
|
||||
to a cell. (Presumably `ff` is provided by a prior `match` block.)
|
||||
|
||||
The `select` lines are evaluated once for each cell when the matcher is
|
||||
initialized. A `match` block will only consider cells for which all `select`
|
||||
expressions evaluated to `true`. Note that the state variable corresponding to
|
||||
the match (in the example `mul`) is the only state variable that may be used
|
||||
`select` lines.
|
||||
|
||||
Index lines are using the `index <type> expr1 === expr2` syntax. `expr1` is
|
||||
evaluated during matcher initialization and the same restrictions apply as for
|
||||
`select` expressions. `expr2` is evaluated when the match is calulated. It is a
|
||||
function of any state variables assigned to by previous blocks. Both expression
|
||||
are converted to the given type and compared for equality. Only cells for which
|
||||
all `index` statements in the block pass are considered by the match.
|
||||
|
||||
Note that `select` and `index` are fast operations. Thus `select` and `index`
|
||||
should be used whenever possible to create efficient matchers.
|
||||
|
||||
Finally, `filter <expression>` narrows down the remaining list of cells. For
|
||||
performance reasons `filter` statements should only be used for things that
|
||||
can't be done using `select` and `index`.
|
||||
|
||||
The `optional` statement marks optional matches. I.e. the matcher will also
|
||||
explore the case where `mul` is set to `nullptr`. Without the `optional`
|
||||
statement a match may only be assigned nullptr when one of the `if` expressions
|
||||
evaluates to `false`.
|
||||
|
||||
Additional code
|
||||
---------------
|
||||
|
||||
Interleaved with `match..endmatch` blocks there may be `code..endcode` blocks.
|
||||
Such a block starts with the keyword `code` followed by a list of state variables
|
||||
that the block may modify. For example:
|
||||
|
||||
code addAB sigS
|
||||
if (addA) {
|
||||
addAB = addA;
|
||||
sigS = port(addA, \B);
|
||||
}
|
||||
if (addB) {
|
||||
addAB = addB;
|
||||
sigS = port(addB, \A);
|
||||
}
|
||||
endcode
|
||||
|
||||
The special keyword `reject` can be used to reject the current state and
|
||||
backtrack. For example:
|
||||
|
||||
code
|
||||
if (ffA && ffB) {
|
||||
if (port(ffA, \CLK) != port(ffB, \CLK))
|
||||
reject;
|
||||
if (param(ffA, \CLK_POLARITY) != param(ffB, \CLK_POLARITY))
|
||||
reject;
|
||||
}
|
||||
endcode
|
||||
|
||||
Similarly, the special keyword `accept` can be used to accept the current
|
||||
state. (`accept` will not backtrack. This means it continues with the current
|
||||
branch and may accept a larger match later.)
|
||||
|
||||
The special keyword `branch` can be used to explore different cases. Note that
|
||||
each code block has an implicit `branch` at the end. So most use-cases of the
|
||||
`branch` keyword need to end the block with `reject` to avoid the implicit
|
||||
branch at the end. For example:
|
||||
|
||||
state <int> mode
|
||||
|
||||
code mode
|
||||
for (mode = 0; mode < 8; mode++)
|
||||
branch;
|
||||
reject;
|
||||
endcode
|
||||
|
||||
But in some cases it is more natural to utilize the implicit branch statement:
|
||||
|
||||
state <IdString> portAB
|
||||
|
||||
code portAB
|
||||
portAB = \A;
|
||||
branch;
|
||||
portAB = \B;
|
||||
endcode
|
||||
|
||||
There is an implicit `code..endcode` block at the end of each `.pgm` file
|
||||
that just accepts everything that gets all the way there.
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "passes/pmgen/ice40_dsp_pm.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
void create_ice40_dsp(ice40_dsp_pm &pm)
|
||||
{
|
||||
#if 0
|
||||
log("\n");
|
||||
log("ffA: %s\n", log_id(pm.st.ffA, "--"));
|
||||
log("ffB: %s\n", log_id(pm.st.ffB, "--"));
|
||||
log("mul: %s\n", log_id(pm.st.mul, "--"));
|
||||
log("ffY: %s\n", log_id(pm.st.ffY, "--"));
|
||||
log("addAB: %s\n", log_id(pm.st.addAB, "--"));
|
||||
log("muxAB: %s\n", log_id(pm.st.muxAB, "--"));
|
||||
log("ffS: %s\n", log_id(pm.st.ffS, "--"));
|
||||
#endif
|
||||
|
||||
log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul));
|
||||
|
||||
if (GetSize(pm.st.sigA) > 16) {
|
||||
log(" input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA));
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSize(pm.st.sigB) > 16) {
|
||||
log(" input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB));
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSize(pm.st.sigS) > 32) {
|
||||
log(" accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS));
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSize(pm.st.sigY) > 32) {
|
||||
log(" output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY));
|
||||
return;
|
||||
}
|
||||
|
||||
bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool();
|
||||
|
||||
if (mul_signed) {
|
||||
log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
log(" replacing $mul with SB_MAC16 cell.\n");
|
||||
|
||||
Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
|
||||
pm.module->swap_names(cell, pm.st.mul);
|
||||
|
||||
// SB_MAC16 Input Interface
|
||||
|
||||
SigSpec A = pm.st.sigA;
|
||||
A.extend_u0(16, mul_signed);
|
||||
|
||||
SigSpec B = pm.st.sigB;
|
||||
B.extend_u0(16, mul_signed);
|
||||
|
||||
SigSpec CD;
|
||||
if (pm.st.muxA)
|
||||
CD = pm.st.muxA->getPort("\\B");
|
||||
if (pm.st.muxB)
|
||||
CD = pm.st.muxB->getPort("\\A");
|
||||
CD.extend_u0(32, mul_signed);
|
||||
|
||||
cell->setPort("\\A", A);
|
||||
cell->setPort("\\B", B);
|
||||
cell->setPort("\\C", CD.extract(0, 16));
|
||||
cell->setPort("\\D", CD.extract(16, 16));
|
||||
|
||||
cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0);
|
||||
cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0);
|
||||
|
||||
cell->setPort("\\AHOLD", State::S0);
|
||||
cell->setPort("\\BHOLD", State::S0);
|
||||
cell->setPort("\\CHOLD", State::S0);
|
||||
cell->setPort("\\DHOLD", State::S0);
|
||||
|
||||
cell->setPort("\\IRSTTOP", State::S0);
|
||||
cell->setPort("\\IRSTBOT", State::S0);
|
||||
|
||||
if (pm.st.clock_vld)
|
||||
{
|
||||
cell->setPort("\\CLK", pm.st.clock);
|
||||
cell->setPort("\\CE", State::S1);
|
||||
cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1);
|
||||
|
||||
log(" clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge");
|
||||
|
||||
if (pm.st.ffA)
|
||||
log(" ffA:%s", log_id(pm.st.ffA));
|
||||
|
||||
if (pm.st.ffB)
|
||||
log(" ffB:%s", log_id(pm.st.ffB));
|
||||
|
||||
if (pm.st.ffY)
|
||||
log(" ffY:%s", log_id(pm.st.ffY));
|
||||
|
||||
if (pm.st.ffS)
|
||||
log(" ffS:%s", log_id(pm.st.ffS));
|
||||
|
||||
log("\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
cell->setPort("\\CLK", State::S0);
|
||||
cell->setPort("\\CE", State::S0);
|
||||
cell->setParam("\\NEG_TRIGGER", State::S0);
|
||||
}
|
||||
|
||||
// SB_MAC16 Cascade Interface
|
||||
|
||||
cell->setPort("\\SIGNEXTIN", State::Sx);
|
||||
cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID));
|
||||
|
||||
cell->setPort("\\CI", State::Sx);
|
||||
cell->setPort("\\CO", pm.module->addWire(NEW_ID));
|
||||
|
||||
cell->setPort("\\ACCUMCI", State::Sx);
|
||||
cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID));
|
||||
|
||||
// SB_MAC16 Output Interface
|
||||
|
||||
SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY;
|
||||
if (GetSize(O) < 32)
|
||||
O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
|
||||
|
||||
cell->setPort("\\O", O);
|
||||
|
||||
if (pm.st.addAB) {
|
||||
log(" accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type));
|
||||
cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
|
||||
cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
|
||||
} else {
|
||||
cell->setPort("\\ADDSUBTOP", State::S0);
|
||||
cell->setPort("\\ADDSUBBOT", State::S0);
|
||||
}
|
||||
|
||||
cell->setPort("\\ORSTTOP", State::S0);
|
||||
cell->setPort("\\ORSTBOT", State::S0);
|
||||
|
||||
cell->setPort("\\OHOLDTOP", State::S0);
|
||||
cell->setPort("\\OHOLDBOT", State::S0);
|
||||
|
||||
SigSpec acc_reset = State::S0;
|
||||
if (pm.st.muxA)
|
||||
acc_reset = pm.st.muxA->getPort("\\S");
|
||||
if (pm.st.muxB)
|
||||
acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S"));
|
||||
|
||||
cell->setPort("\\OLOADTOP", acc_reset);
|
||||
cell->setPort("\\OLOADBOT", acc_reset);
|
||||
|
||||
// SB_MAC16 Remaining Parameters
|
||||
|
||||
cell->setParam("\\C_REG", State::S0);
|
||||
cell->setParam("\\D_REG", State::S0);
|
||||
|
||||
cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
|
||||
cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
|
||||
cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0);
|
||||
cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
|
||||
|
||||
cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
|
||||
cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
|
||||
cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
|
||||
cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
|
||||
|
||||
cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
|
||||
cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
|
||||
cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
|
||||
cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
|
||||
|
||||
cell->setParam("\\MODE_8x8", State::S0);
|
||||
cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
|
||||
cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
|
||||
|
||||
pm.autoremove(pm.st.mul);
|
||||
pm.autoremove(pm.st.ffY);
|
||||
pm.autoremove(pm.st.ffS);
|
||||
}
|
||||
|
||||
struct Ice40DspPass : public Pass {
|
||||
Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" ice40_dsp [options] [selection]\n");
|
||||
log("\n");
|
||||
log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing ICE40_DSP pass (map multipliers).\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-singleton") {
|
||||
// singleton_mode = true;
|
||||
// continue;
|
||||
// }
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp);
|
||||
}
|
||||
} Ice40DspPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,160 @@
|
|||
state <SigBit> clock
|
||||
state <bool> clock_pol clock_vld
|
||||
state <SigSpec> sigA sigB sigY sigS
|
||||
state <Cell*> addAB muxAB
|
||||
|
||||
match mul
|
||||
select mul->type.in($mul)
|
||||
select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
|
||||
select GetSize(mul->getPort(\Y)) > 10
|
||||
endmatch
|
||||
|
||||
match ffA
|
||||
select ffA->type.in($dff)
|
||||
// select nusers(port(ffA, \Q)) == 2
|
||||
index <SigSpec> port(ffA, \Q) === port(mul, \A)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code sigA clock clock_pol clock_vld
|
||||
sigA = port(mul, \A);
|
||||
|
||||
if (ffA) {
|
||||
sigA = port(ffA, \D);
|
||||
|
||||
clock = port(ffA, \CLK).as_bit();
|
||||
clock_pol = param(ffA, \CLK_POLARITY).as_bool();
|
||||
clock_vld = true;
|
||||
}
|
||||
endcode
|
||||
|
||||
match ffB
|
||||
select ffB->type.in($dff)
|
||||
// select nusers(port(ffB, \Q)) == 2
|
||||
index <SigSpec> port(ffB, \Q) === port(mul, \B)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code sigB clock clock_pol clock_vld
|
||||
sigB = port(mul, \B);
|
||||
|
||||
if (ffB) {
|
||||
sigB = port(ffB, \D);
|
||||
SigBit c = port(ffB, \CLK).as_bit();
|
||||
bool cp = param(ffB, \CLK_POLARITY).as_bool();
|
||||
|
||||
if (clock_vld && (c != clock || cp != clock_pol))
|
||||
reject;
|
||||
|
||||
clock = c;
|
||||
clock_pol = cp;
|
||||
clock_vld = true;
|
||||
}
|
||||
endcode
|
||||
|
||||
match ffY
|
||||
select ffY->type.in($dff)
|
||||
select nusers(port(ffY, \D)) == 2
|
||||
index <SigSpec> port(ffY, \D) === port(mul, \Y)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code sigY clock clock_pol clock_vld
|
||||
sigY = port(mul, \Y);
|
||||
|
||||
if (ffY) {
|
||||
sigY = port(ffY, \Q);
|
||||
SigBit c = port(ffY, \CLK).as_bit();
|
||||
bool cp = param(ffY, \CLK_POLARITY).as_bool();
|
||||
|
||||
if (clock_vld && (c != clock || cp != clock_pol))
|
||||
reject;
|
||||
|
||||
clock = c;
|
||||
clock_pol = cp;
|
||||
clock_vld = true;
|
||||
}
|
||||
endcode
|
||||
|
||||
match addA
|
||||
select addA->type.in($add)
|
||||
select nusers(port(addA, \A)) == 2
|
||||
index <SigSpec> port(addA, \A) === sigY
|
||||
optional
|
||||
endmatch
|
||||
|
||||
match addB
|
||||
if !addA
|
||||
select addB->type.in($add, $sub)
|
||||
select nusers(port(addB, \B)) == 2
|
||||
index <SigSpec> port(addB, \B) === sigY
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code addAB sigS
|
||||
if (addA) {
|
||||
addAB = addA;
|
||||
sigS = port(addA, \B);
|
||||
}
|
||||
if (addB) {
|
||||
addAB = addB;
|
||||
sigS = port(addB, \A);
|
||||
}
|
||||
if (addAB) {
|
||||
int natural_mul_width = GetSize(sigA) + GetSize(sigB);
|
||||
int actual_mul_width = GetSize(sigY);
|
||||
int actual_acc_width = GetSize(sigS);
|
||||
|
||||
if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
|
||||
reject;
|
||||
if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool()))
|
||||
reject;
|
||||
}
|
||||
endcode
|
||||
|
||||
match muxA
|
||||
if addAB
|
||||
select muxA->type.in($mux)
|
||||
select nusers(port(muxA, \A)) == 2
|
||||
index <SigSpec> port(muxA, \A) === port(addAB, \Y)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
match muxB
|
||||
if addAB
|
||||
if !muxA
|
||||
select muxB->type.in($mux)
|
||||
select nusers(port(muxB, \B)) == 2
|
||||
index <SigSpec> port(muxB, \B) === port(addAB, \Y)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code muxAB
|
||||
muxAB = addAB;
|
||||
if (muxA)
|
||||
muxAB = muxA;
|
||||
if (muxB)
|
||||
muxAB = muxB;
|
||||
endcode
|
||||
|
||||
match ffS
|
||||
if muxAB
|
||||
select ffS->type.in($dff)
|
||||
select nusers(port(ffS, \D)) == 2
|
||||
index <SigSpec> port(ffS, \D) === port(muxAB, \Y)
|
||||
index <SigSpec> port(ffS, \Q) === sigS
|
||||
endmatch
|
||||
|
||||
code clock clock_pol clock_vld
|
||||
if (ffS) {
|
||||
SigBit c = port(ffS, \CLK).as_bit();
|
||||
bool cp = param(ffS, \CLK_POLARITY).as_bool();
|
||||
|
||||
if (clock_vld && (c != clock || cp != clock_pol))
|
||||
reject;
|
||||
|
||||
clock = c;
|
||||
clock_pol = cp;
|
||||
clock_vld = true;
|
||||
}
|
||||
endcode
|
|
@ -0,0 +1,482 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
import sys
|
||||
import pprint
|
||||
|
||||
pp = pprint.PrettyPrinter(indent=4)
|
||||
|
||||
prefix = sys.argv[1]
|
||||
|
||||
state_types = dict()
|
||||
udata_types = dict()
|
||||
blocks = list()
|
||||
ids = dict()
|
||||
|
||||
def rewrite_cpp(s):
|
||||
t = list()
|
||||
i = 0
|
||||
while i < len(s):
|
||||
if s[i] in ("'", '"') and i + 1 < len(s):
|
||||
j = i + 1
|
||||
while j + 1 < len(s) and s[j] != s[i]:
|
||||
if s[j] == '\\' and j + 1 < len(s):
|
||||
j += 1
|
||||
j += 1
|
||||
t.append(s[i:j+1])
|
||||
i = j + 1
|
||||
continue
|
||||
|
||||
if s[i] in ('$', '\\') and i + 1 < len(s):
|
||||
j = i + 1
|
||||
while True:
|
||||
if j == len(s):
|
||||
j -= 1
|
||||
break
|
||||
if ord('a') <= ord(s[j]) <= ord('z'):
|
||||
j += 1
|
||||
continue
|
||||
if ord('A') <= ord(s[j]) <= ord('Z'):
|
||||
j += 1
|
||||
continue
|
||||
if ord('0') <= ord(s[j]) <= ord('9'):
|
||||
j += 1
|
||||
continue
|
||||
if s[j] == '_':
|
||||
j += 1
|
||||
continue
|
||||
j -= 1
|
||||
break
|
||||
|
||||
n = s[i:j+1]
|
||||
i = j + 1
|
||||
|
||||
if n[0] == '$':
|
||||
v = "id_d_" + n[1:]
|
||||
else:
|
||||
v = "id_b_" + n[1:]
|
||||
|
||||
if v not in ids:
|
||||
ids[v] = n
|
||||
else:
|
||||
assert ids[v] == n
|
||||
|
||||
t.append(v)
|
||||
continue
|
||||
|
||||
if s[i] == "\t":
|
||||
t.append(" ")
|
||||
else:
|
||||
t.append(s[i])
|
||||
|
||||
i += 1
|
||||
|
||||
return "".join(t)
|
||||
|
||||
with open("%s.pmg" % prefix, "r") as f:
|
||||
while True:
|
||||
line = f.readline()
|
||||
if line == "": break
|
||||
line = line.strip()
|
||||
|
||||
cmd = line.split()
|
||||
if len(cmd) == 0 or cmd[0].startswith("//"): continue
|
||||
cmd = cmd[0]
|
||||
|
||||
if cmd == "state":
|
||||
m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
|
||||
assert m
|
||||
type_str = m.group(1)
|
||||
states_str = m.group(2)
|
||||
for s in re.split(r"\s+", states_str):
|
||||
assert s not in state_types
|
||||
state_types[s] = type_str
|
||||
continue
|
||||
|
||||
if cmd == "udata":
|
||||
m = re.match(r"^udata\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
|
||||
assert m
|
||||
type_str = m.group(1)
|
||||
udatas_str = m.group(2)
|
||||
for s in re.split(r"\s+", udatas_str):
|
||||
assert s not in udata_types
|
||||
udata_types[s] = type_str
|
||||
continue
|
||||
|
||||
if cmd == "match":
|
||||
block = dict()
|
||||
block["type"] = "match"
|
||||
|
||||
line = line.split()
|
||||
assert len(line) == 2
|
||||
assert line[1] not in state_types
|
||||
block["cell"] = line[1]
|
||||
state_types[line[1]] = "Cell*";
|
||||
|
||||
block["if"] = list()
|
||||
block["select"] = list()
|
||||
block["index"] = list()
|
||||
block["filter"] = list()
|
||||
block["optional"] = False
|
||||
|
||||
while True:
|
||||
l = f.readline()
|
||||
assert l != ""
|
||||
a = l.split()
|
||||
if len(a) == 0 or a[0].startswith("//"): continue
|
||||
if a[0] == "endmatch": break
|
||||
|
||||
if a[0] == "if":
|
||||
b = l.lstrip()[2:]
|
||||
block["if"].append(rewrite_cpp(b.strip()))
|
||||
continue
|
||||
|
||||
if a[0] == "select":
|
||||
b = l.lstrip()[6:]
|
||||
block["select"].append(rewrite_cpp(b.strip()))
|
||||
continue
|
||||
|
||||
if a[0] == "index":
|
||||
m = re.match(r"^\s*index\s+<(.*?)>\s+(.*?)\s*===\s*(.*?)\s*$", l)
|
||||
assert m
|
||||
block["index"].append((m.group(1), rewrite_cpp(m.group(2)), rewrite_cpp(m.group(3))))
|
||||
continue
|
||||
|
||||
if a[0] == "filter":
|
||||
b = l.lstrip()[6:]
|
||||
block["filter"].append(rewrite_cpp(b.strip()))
|
||||
continue
|
||||
|
||||
if a[0] == "optional":
|
||||
block["optional"] = True
|
||||
continue
|
||||
|
||||
assert False
|
||||
|
||||
blocks.append(block)
|
||||
|
||||
if cmd == "code":
|
||||
block = dict()
|
||||
block["type"] = "code"
|
||||
block["code"] = list()
|
||||
block["states"] = set()
|
||||
|
||||
for s in line.split()[1:]:
|
||||
assert s in state_types
|
||||
block["states"].add(s)
|
||||
|
||||
while True:
|
||||
l = f.readline()
|
||||
assert l != ""
|
||||
a = l.split()
|
||||
if len(a) == 0: continue
|
||||
if a[0] == "endcode": break
|
||||
|
||||
block["code"].append(rewrite_cpp(l.rstrip()))
|
||||
|
||||
blocks.append(block)
|
||||
|
||||
with open("%s_pm.h" % prefix, "w") as f:
|
||||
print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f)
|
||||
print("", file=f)
|
||||
|
||||
print("#include \"kernel/yosys.h\"", file=f)
|
||||
print("#include \"kernel/sigtools.h\"", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print("YOSYS_NAMESPACE_BEGIN", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print("struct {}_pm {{".format(prefix), file=f)
|
||||
print(" Module *module;", file=f)
|
||||
print(" SigMap sigmap;", file=f)
|
||||
print(" std::function<void()> on_accept;".format(prefix), file=f)
|
||||
print("", file=f)
|
||||
|
||||
for index in range(len(blocks)):
|
||||
block = blocks[index]
|
||||
if block["type"] == "match":
|
||||
index_types = list()
|
||||
for entry in block["index"]:
|
||||
index_types.append(entry[0])
|
||||
print(" typedef std::tuple<{}> index_{}_key_type;".format(", ".join(index_types), index), file=f)
|
||||
print(" dict<index_{}_key_type, vector<Cell*>> index_{};".format(index, index), file=f)
|
||||
print(" dict<SigBit, pool<Cell*>> sigusers;", file=f)
|
||||
print(" pool<Cell*> blacklist_cells;", file=f)
|
||||
print(" pool<Cell*> autoremove_cells;", file=f)
|
||||
print(" bool blacklist_dirty;", file=f)
|
||||
print(" int rollback;", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" struct state_t {", file=f)
|
||||
for s, t in sorted(state_types.items()):
|
||||
print(" {} {};".format(t, s), file=f)
|
||||
print(" } st;", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" struct udata_t {", file=f)
|
||||
for s, t in sorted(udata_types.items()):
|
||||
print(" {} {};".format(t, s), file=f)
|
||||
print(" } ud;", file=f)
|
||||
print("", file=f)
|
||||
|
||||
for v, n in sorted(ids.items()):
|
||||
if n[0] == "\\":
|
||||
print(" IdString {}{{\"\\{}\"}};".format(v, n), file=f)
|
||||
else:
|
||||
print(" IdString {}{{\"{}\"}};".format(v, n), file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" void add_siguser(const SigSpec &sig, Cell *cell) {", file=f)
|
||||
print(" for (auto bit : sigmap(sig)) {", file=f)
|
||||
print(" if (bit.wire == nullptr) continue;", file=f)
|
||||
print(" if (sigusers.count(bit) == 0 && bit.wire->port_id)", file=f)
|
||||
print(" sigusers[bit].insert(nullptr);", file=f)
|
||||
print(" sigusers[bit].insert(cell);", file=f)
|
||||
print(" }", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" void blacklist(Cell *cell) {", file=f)
|
||||
print(" if (cell != nullptr) {", file=f)
|
||||
print(" if (blacklist_cells.insert(cell).second)", file=f)
|
||||
print(" blacklist_dirty = true;", file=f)
|
||||
print(" }", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" void autoremove(Cell *cell) {", file=f)
|
||||
print(" if (cell != nullptr) {", file=f)
|
||||
print(" if (blacklist_cells.insert(cell).second)", file=f)
|
||||
print(" blacklist_dirty = true;", file=f)
|
||||
print(" autoremove_cells.insert(cell);", file=f)
|
||||
print(" }", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" void check_blacklist() {", file=f)
|
||||
print(" if (!blacklist_dirty)", file=f)
|
||||
print(" return;", file=f)
|
||||
print(" blacklist_dirty = false;", file=f)
|
||||
for index in range(len(blocks)):
|
||||
block = blocks[index]
|
||||
if block["type"] == "match":
|
||||
print(" if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f)
|
||||
print(" rollback = {};".format(index+1), file=f)
|
||||
print(" return;", file=f)
|
||||
print(" }", file=f)
|
||||
print(" rollback = 0;", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" SigSpec port(Cell *cell, IdString portname) {", file=f)
|
||||
print(" return sigmap(cell->getPort(portname));", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" Const param(Cell *cell, IdString paramname) {", file=f)
|
||||
print(" return cell->getParam(paramname);", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" int nusers(const SigSpec &sig) {", file=f)
|
||||
print(" pool<Cell*> users;", file=f)
|
||||
print(" for (auto bit : sigmap(sig))", file=f)
|
||||
print(" for (auto user : sigusers[bit])", file=f)
|
||||
print(" users.insert(user);", file=f)
|
||||
print(" return GetSize(users);", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f)
|
||||
print(" module(module), sigmap(module) {", file=f)
|
||||
for s, t in sorted(udata_types.items()):
|
||||
if t.endswith("*"):
|
||||
print(" ud.{} = nullptr;".format(s), file=f)
|
||||
else:
|
||||
print(" ud.{} = {}();".format(s, t), file=f)
|
||||
print(" for (auto cell : module->cells()) {", file=f)
|
||||
print(" for (auto &conn : cell->connections())", file=f)
|
||||
print(" add_siguser(conn.second, cell);", file=f)
|
||||
print(" }", file=f)
|
||||
print(" for (auto cell : cells) {", file=f)
|
||||
|
||||
for index in range(len(blocks)):
|
||||
block = blocks[index]
|
||||
if block["type"] == "match":
|
||||
print(" do {", file=f)
|
||||
print(" Cell *{} = cell;".format(block["cell"]), file=f)
|
||||
for expr in block["select"]:
|
||||
print(" if (!({})) break;".format(expr), file=f)
|
||||
print(" index_{}_key_type key;".format(index), file=f)
|
||||
for field, entry in enumerate(block["index"]):
|
||||
print(" std::get<{}>(key) = {};".format(field, entry[1]), file=f)
|
||||
print(" index_{}[key].push_back(cell);".format(index), file=f)
|
||||
print(" } while (0);", file=f)
|
||||
|
||||
print(" }", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" ~{}_pm() {{".format(prefix), file=f)
|
||||
print(" for (auto cell : autoremove_cells)", file=f)
|
||||
print(" module->remove(cell);", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" void run(std::function<void()> on_accept_f) {", file=f)
|
||||
print(" on_accept = on_accept_f;", file=f)
|
||||
print(" rollback = 0;", file=f)
|
||||
print(" blacklist_dirty = false;", file=f)
|
||||
for s, t in sorted(state_types.items()):
|
||||
if t.endswith("*"):
|
||||
print(" st.{} = nullptr;".format(s), file=f)
|
||||
else:
|
||||
print(" st.{} = {}();".format(s, t), file=f)
|
||||
print(" block_0();", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f)
|
||||
print(" run([&](){on_accept_f(*this);});", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
for index in range(len(blocks)):
|
||||
block = blocks[index]
|
||||
|
||||
print(" void block_{}() {{".format(index), file=f)
|
||||
|
||||
const_st = set()
|
||||
nonconst_st = set()
|
||||
restore_st = set()
|
||||
|
||||
for i in range(index):
|
||||
if blocks[i]["type"] == "code":
|
||||
for s in blocks[i]["states"]:
|
||||
const_st.add(s)
|
||||
elif blocks[i]["type"] == "match":
|
||||
const_st.add(blocks[i]["cell"])
|
||||
else:
|
||||
assert False
|
||||
|
||||
if block["type"] == "code":
|
||||
for s in block["states"]:
|
||||
if s in const_st:
|
||||
const_st.remove(s)
|
||||
restore_st.add(s)
|
||||
nonconst_st.add(s)
|
||||
elif block["type"] == "match":
|
||||
s = block["cell"]
|
||||
assert s not in const_st
|
||||
nonconst_st.add(s)
|
||||
else:
|
||||
assert False
|
||||
|
||||
for s in sorted(const_st):
|
||||
t = state_types[s]
|
||||
if t.endswith("*"):
|
||||
print(" {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
|
||||
else:
|
||||
print(" const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
|
||||
|
||||
for s in sorted(nonconst_st):
|
||||
t = state_types[s]
|
||||
print(" {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
|
||||
|
||||
if len(restore_st):
|
||||
print("", file=f)
|
||||
for s in sorted(restore_st):
|
||||
t = state_types[s]
|
||||
print(" {} backup_{} = {};".format(t, s, s), file=f)
|
||||
|
||||
if block["type"] == "code":
|
||||
print("", file=f)
|
||||
print(" do {", file=f)
|
||||
print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f)
|
||||
print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f)
|
||||
print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
|
||||
|
||||
for line in block["code"]:
|
||||
print(" " + line, file=f)
|
||||
|
||||
print("", file=f)
|
||||
print(" block_{}();".format(index+1), file=f)
|
||||
print("#undef reject", file=f)
|
||||
print("#undef accept", file=f)
|
||||
print("#undef branch", file=f)
|
||||
print(" } while (0);", file=f)
|
||||
print("", file=f)
|
||||
print("rollback_label:", file=f)
|
||||
print(" YS_ATTRIBUTE(unused);", file=f)
|
||||
|
||||
if len(restore_st) or len(nonconst_st):
|
||||
print("", file=f)
|
||||
for s in sorted(restore_st):
|
||||
t = state_types[s]
|
||||
print(" {} = backup_{};".format(s, s), file=f)
|
||||
for s in sorted(nonconst_st):
|
||||
if s not in restore_st:
|
||||
t = state_types[s]
|
||||
if t.endswith("*"):
|
||||
print(" {} = nullptr;".format(s), file=f)
|
||||
else:
|
||||
print(" {} = {}();".format(s, t), file=f)
|
||||
|
||||
elif block["type"] == "match":
|
||||
assert len(restore_st) == 0
|
||||
|
||||
if len(block["if"]):
|
||||
for expr in block["if"]:
|
||||
print("", file=f)
|
||||
print(" if (!({})) {{".format(expr), file=f)
|
||||
print(" {} = nullptr;".format(block["cell"]), file=f)
|
||||
print(" block_{}();".format(index+1), file=f)
|
||||
print(" return;", file=f)
|
||||
print(" }", file=f)
|
||||
|
||||
print("", file=f)
|
||||
print(" index_{}_key_type key;".format(index), file=f)
|
||||
for field, entry in enumerate(block["index"]):
|
||||
print(" std::get<{}>(key) = {};".format(field, entry[2]), file=f)
|
||||
print(" const vector<Cell*> &cells = index_{}[key];".format(index), file=f)
|
||||
|
||||
print("", file=f)
|
||||
print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f)
|
||||
print(" {} = cells[idx];".format(block["cell"]), file=f)
|
||||
print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f)
|
||||
for expr in block["filter"]:
|
||||
print(" if (!({})) continue;".format(expr), file=f)
|
||||
print(" block_{}();".format(index+1), file=f)
|
||||
print(" if (rollback) {", file=f)
|
||||
print(" if (rollback != {}) {{".format(index+1), file=f)
|
||||
print(" {} = nullptr;".format(block["cell"]), file=f)
|
||||
print(" return;", file=f)
|
||||
print(" }", file=f)
|
||||
print(" rollback = 0;", file=f)
|
||||
print(" }", file=f)
|
||||
print(" }", file=f)
|
||||
|
||||
print("", file=f)
|
||||
print(" {} = nullptr;".format(block["cell"]), file=f)
|
||||
|
||||
if block["optional"]:
|
||||
print(" block_{}();".format(index+1), file=f)
|
||||
|
||||
else:
|
||||
assert False
|
||||
|
||||
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" void block_{}() {{".format(len(blocks)), file=f)
|
||||
print(" on_accept();", file=f)
|
||||
print(" check_blacklist();", file=f)
|
||||
print(" }", file=f)
|
||||
print("};", file=f)
|
||||
|
||||
print("", file=f)
|
||||
print("YOSYS_NAMESPACE_END", file=f)
|
||||
|
||||
# pp.pprint(blocks)
|
|
@ -327,8 +327,26 @@ void extract_cell(RTLIL::Cell *cell, bool keepff)
|
|||
}
|
||||
}
|
||||
|
||||
std::string remap_name(RTLIL::IdString abc_name)
|
||||
std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr)
|
||||
{
|
||||
std::string abc_sname = abc_name.substr(1);
|
||||
if (abc_sname.substr(0, 5) == "ys__n") {
|
||||
int sid = std::stoi(abc_sname.substr(5));
|
||||
bool inv = abc_sname.back() == 'v';
|
||||
for (auto sig : signal_list) {
|
||||
if (sig.id == sid && sig.bit.wire != nullptr) {
|
||||
std::stringstream sstr;
|
||||
sstr << "$abc$" << map_autoidx << "$" << sig.bit.wire->name.substr(1);
|
||||
if (sig.bit.wire->width != 1)
|
||||
sstr << "[" << sig.bit.offset << "]";
|
||||
if (inv)
|
||||
sstr << "_inv";
|
||||
if (orig_wire != nullptr)
|
||||
*orig_wire = sig.bit.wire;
|
||||
return sstr.str();
|
||||
}
|
||||
}
|
||||
}
|
||||
std::stringstream sstr;
|
||||
sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1);
|
||||
return sstr.str();
|
||||
|
@ -353,12 +371,12 @@ void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edges, std:
|
|||
}
|
||||
|
||||
for (auto n : nodes)
|
||||
fprintf(f, " n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit),
|
||||
fprintf(f, " ys__n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit),
|
||||
n, in_counts[n], workpool.count(n) ? ", shape=box" : "");
|
||||
|
||||
for (auto &e : edges)
|
||||
for (auto n : e.second)
|
||||
fprintf(f, " n%d -> n%d;\n", e.first, n);
|
||||
fprintf(f, " ys__n%d -> ys__n%d;\n", e.first, n);
|
||||
|
||||
fprintf(f, "}\n");
|
||||
}
|
||||
|
@ -624,7 +642,7 @@ struct abc_output_filter
|
|||
void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file,
|
||||
std::string liberty_file, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,
|
||||
bool keepff, std::string delay_target, std::string sop_inputs, std::string sop_products, std::string lutin_shared, bool fast_mode,
|
||||
const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode)
|
||||
const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode, bool abc_dress)
|
||||
{
|
||||
module = current_module;
|
||||
map_autoidx = autoidx++;
|
||||
|
@ -728,7 +746,8 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
|
|||
|
||||
for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos))
|
||||
abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3);
|
||||
|
||||
if (abc_dress)
|
||||
abc_script += "; dress";
|
||||
abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str());
|
||||
abc_script = add_echos_to_abc_cmd(abc_script);
|
||||
|
||||
|
@ -784,7 +803,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
|
|||
for (auto &si : signal_list) {
|
||||
if (!si.is_port || si.type != G(NONE))
|
||||
continue;
|
||||
fprintf(f, " n%d", si.id);
|
||||
fprintf(f, " ys__n%d", si.id);
|
||||
pi_map[count_input++] = log_signal(si.bit);
|
||||
}
|
||||
if (count_input == 0)
|
||||
|
@ -796,17 +815,17 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
|
|||
for (auto &si : signal_list) {
|
||||
if (!si.is_port || si.type == G(NONE))
|
||||
continue;
|
||||
fprintf(f, " n%d", si.id);
|
||||
fprintf(f, " ys__n%d", si.id);
|
||||
po_map[count_output++] = log_signal(si.bit);
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
|
||||
for (auto &si : signal_list)
|
||||
fprintf(f, "# n%-5d %s\n", si.id, log_signal(si.bit));
|
||||
fprintf(f, "# ys__n%-5d %s\n", si.id, log_signal(si.bit));
|
||||
|
||||
for (auto &si : signal_list) {
|
||||
if (si.bit.wire == NULL) {
|
||||
fprintf(f, ".names n%d\n", si.id);
|
||||
fprintf(f, ".names ys__n%d\n", si.id);
|
||||
if (si.bit == RTLIL::State::S1)
|
||||
fprintf(f, "1\n");
|
||||
}
|
||||
|
@ -815,68 +834,68 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
|
|||
int count_gates = 0;
|
||||
for (auto &si : signal_list) {
|
||||
if (si.type == G(BUF)) {
|
||||
fprintf(f, ".names n%d n%d\n", si.in1, si.id);
|
||||
fprintf(f, ".names ys__n%d ys__n%d\n", si.in1, si.id);
|
||||
fprintf(f, "1 1\n");
|
||||
} else if (si.type == G(NOT)) {
|
||||
fprintf(f, ".names n%d n%d\n", si.in1, si.id);
|
||||
fprintf(f, ".names ys__n%d ys__n%d\n", si.in1, si.id);
|
||||
fprintf(f, "0 1\n");
|
||||
} else if (si.type == G(AND)) {
|
||||
fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, "11 1\n");
|
||||
} else if (si.type == G(NAND)) {
|
||||
fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, "0- 1\n");
|
||||
fprintf(f, "-0 1\n");
|
||||
} else if (si.type == G(OR)) {
|
||||
fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, "-1 1\n");
|
||||
fprintf(f, "1- 1\n");
|
||||
} else if (si.type == G(NOR)) {
|
||||
fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, "00 1\n");
|
||||
} else if (si.type == G(XOR)) {
|
||||
fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, "01 1\n");
|
||||
fprintf(f, "10 1\n");
|
||||
} else if (si.type == G(XNOR)) {
|
||||
fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, "00 1\n");
|
||||
fprintf(f, "11 1\n");
|
||||
} else if (si.type == G(ANDNOT)) {
|
||||
fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, "10 1\n");
|
||||
} else if (si.type == G(ORNOT)) {
|
||||
fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
|
||||
fprintf(f, "1- 1\n");
|
||||
fprintf(f, "-0 1\n");
|
||||
} else if (si.type == G(MUX)) {
|
||||
fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id);
|
||||
fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);
|
||||
fprintf(f, "1-0 1\n");
|
||||
fprintf(f, "-11 1\n");
|
||||
} else if (si.type == G(AOI3)) {
|
||||
fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id);
|
||||
fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);
|
||||
fprintf(f, "-00 1\n");
|
||||
fprintf(f, "0-0 1\n");
|
||||
} else if (si.type == G(OAI3)) {
|
||||
fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id);
|
||||
fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);
|
||||
fprintf(f, "00- 1\n");
|
||||
fprintf(f, "--0 1\n");
|
||||
} else if (si.type == G(AOI4)) {
|
||||
fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
|
||||
fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
|
||||
fprintf(f, "-0-0 1\n");
|
||||
fprintf(f, "-00- 1\n");
|
||||
fprintf(f, "0--0 1\n");
|
||||
fprintf(f, "0-0- 1\n");
|
||||
} else if (si.type == G(OAI4)) {
|
||||
fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
|
||||
fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
|
||||
fprintf(f, "00-- 1\n");
|
||||
fprintf(f, "--00 1\n");
|
||||
} else if (si.type == G(FF)) {
|
||||
if (si.init == State::S0 || si.init == State::S1) {
|
||||
fprintf(f, ".latch n%d n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0);
|
||||
fprintf(f, ".latch ys__n%d ys__n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0);
|
||||
recover_init = true;
|
||||
} else
|
||||
fprintf(f, ".latch n%d n%d 2\n", si.in1, si.id);
|
||||
fprintf(f, ".latch ys__n%d ys__n%d 2\n", si.in1, si.id);
|
||||
} else if (si.type != G(NONE))
|
||||
log_abort();
|
||||
if (si.type != G(NONE))
|
||||
|
@ -889,7 +908,6 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
|
|||
log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n",
|
||||
count_gates, GetSize(signal_list), count_input, count_output);
|
||||
log_push();
|
||||
|
||||
if (count_output > 0)
|
||||
{
|
||||
log_header(design, "Executing ABC.\n");
|
||||
|
@ -988,7 +1006,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
|
|||
log_error("ABC output file does not contain a module `netlist'.\n");
|
||||
for (auto &it : mapped_mod->wires_) {
|
||||
RTLIL::Wire *w = it.second;
|
||||
RTLIL::Wire *wire = module->addWire(remap_name(w->name));
|
||||
RTLIL::Wire *orig_wire = nullptr;
|
||||
RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire));
|
||||
if (orig_wire != nullptr && orig_wire->attributes.count("\\src"))
|
||||
wire->attributes["\\src"] = orig_wire->attributes["\\src"];
|
||||
if (markgroups) wire->attributes["\\abcgroup"] = map_autoidx;
|
||||
design->select(module, wire);
|
||||
}
|
||||
|
@ -1213,7 +1234,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
|
|||
for (auto &si : signal_list)
|
||||
if (si.is_port) {
|
||||
char buffer[100];
|
||||
snprintf(buffer, 100, "\\n%d", si.id);
|
||||
snprintf(buffer, 100, "\\ys__n%d", si.id);
|
||||
RTLIL::SigSig conn;
|
||||
if (si.type != G(NONE)) {
|
||||
conn.first = si.bit;
|
||||
|
@ -1407,6 +1428,11 @@ struct AbcPass : public Pass {
|
|||
log(" this attribute is a unique integer for each ABC process started. This\n");
|
||||
log(" is useful for debugging the partitioning of clock domains.\n");
|
||||
log("\n");
|
||||
log(" -dress\n");
|
||||
log(" run the 'dress' command after all other ABC commands. This aims to\n");
|
||||
log(" preserve naming by an equivalence check between the original and post-ABC\n");
|
||||
log(" netlists (experimental).\n");
|
||||
log("\n");
|
||||
log("When neither -liberty nor -lut is used, the Yosys standard cell library is\n");
|
||||
log("loaded into ABC before the ABC script is executed.\n");
|
||||
log("\n");
|
||||
|
@ -1441,6 +1467,7 @@ struct AbcPass : public Pass {
|
|||
std::string delay_target, sop_inputs, sop_products, lutin_shared = "-S 1";
|
||||
bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true;
|
||||
bool show_tempdir = false, sop_mode = false;
|
||||
bool abc_dress = false;
|
||||
vector<int> lut_costs;
|
||||
markgroups = false;
|
||||
|
||||
|
@ -1555,6 +1582,10 @@ struct AbcPass : public Pass {
|
|||
map_mux16 = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-dress") {
|
||||
abc_dress = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-g" && argidx+1 < args.size()) {
|
||||
for (auto g : split_tokens(args[++argidx], ",")) {
|
||||
vector<string> gate_list;
|
||||
|
@ -1704,7 +1735,7 @@ struct AbcPass : public Pass {
|
|||
|
||||
if (!dff_mode || !clk_str.empty()) {
|
||||
abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff,
|
||||
delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode);
|
||||
delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode, abc_dress);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1849,7 +1880,7 @@ struct AbcPass : public Pass {
|
|||
en_polarity = std::get<2>(it.first);
|
||||
en_sig = assign_map(std::get<3>(it.first));
|
||||
abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$",
|
||||
keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode);
|
||||
keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode, abc_dress);
|
||||
assign_map.set(mod);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -268,9 +268,9 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
}
|
||||
run("techmap -map +/ecp5/latches_map.v");
|
||||
if (nomux)
|
||||
run("abc -lut 4");
|
||||
run("abc -lut 4 -dress");
|
||||
else
|
||||
run("abc -lut 4:7");
|
||||
run("abc -lut 4:7 -dress");
|
||||
run("clean");
|
||||
}
|
||||
|
||||
|
|
|
@ -886,59 +886,6 @@ module SB_WARMBOOT (
|
|||
);
|
||||
endmodule
|
||||
|
||||
// UltraPlus feature cells
|
||||
(* blackbox *)
|
||||
module SB_MAC16 (
|
||||
input CLK,
|
||||
input CE,
|
||||
input [15:0] C,
|
||||
input [15:0] A,
|
||||
input [15:0] B,
|
||||
input [15:0] D,
|
||||
input AHOLD,
|
||||
input BHOLD,
|
||||
input CHOLD,
|
||||
input DHOLD,
|
||||
input IRSTTOP,
|
||||
input IRSTBOT,
|
||||
input ORSTTOP,
|
||||
input ORSTBOT,
|
||||
input OLOADTOP,
|
||||
input OLOADBOT,
|
||||
input ADDSUBTOP,
|
||||
input ADDSUBBOT,
|
||||
input OHOLDTOP,
|
||||
input OHOLDBOT,
|
||||
input CI,
|
||||
input ACCUMCI,
|
||||
input SIGNEXTIN,
|
||||
output [31:0] O,
|
||||
output CO,
|
||||
output ACCUMCO,
|
||||
output SIGNEXTOUT
|
||||
);
|
||||
parameter NEG_TRIGGER = 1'b0;
|
||||
parameter C_REG = 1'b0;
|
||||
parameter A_REG = 1'b0;
|
||||
parameter B_REG = 1'b0;
|
||||
parameter D_REG = 1'b0;
|
||||
parameter TOP_8x8_MULT_REG = 1'b0;
|
||||
parameter BOT_8x8_MULT_REG = 1'b0;
|
||||
parameter PIPELINE_16x16_MULT_REG1 = 1'b0;
|
||||
parameter PIPELINE_16x16_MULT_REG2 = 1'b0;
|
||||
parameter TOPOUTPUT_SELECT = 2'b00;
|
||||
parameter TOPADDSUB_LOWERINPUT = 2'b00;
|
||||
parameter TOPADDSUB_UPPERINPUT = 1'b0;
|
||||
parameter TOPADDSUB_CARRYSELECT = 2'b00;
|
||||
parameter BOTOUTPUT_SELECT = 2'b00;
|
||||
parameter BOTADDSUB_LOWERINPUT = 2'b00;
|
||||
parameter BOTADDSUB_UPPERINPUT = 1'b0;
|
||||
parameter BOTADDSUB_CARRYSELECT = 2'b00;
|
||||
parameter MODE_8x8 = 1'b0;
|
||||
parameter A_SIGNED = 1'b0;
|
||||
parameter B_SIGNED = 1'b0;
|
||||
endmodule
|
||||
|
||||
module SB_SPRAM256KA (
|
||||
input [13:0] ADDRESS,
|
||||
input [15:0] DATAIN,
|
||||
|
@ -1273,3 +1220,171 @@ module SB_IO_OD (
|
|||
endgenerate
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
module SB_MAC16 (
|
||||
input CLK, CE,
|
||||
input [15:0] C, A, B, D,
|
||||
input AHOLD, BHOLD, CHOLD, DHOLD,
|
||||
input IRSTTOP, IRSTBOT,
|
||||
input ORSTTOP, ORSTBOT,
|
||||
input OLOADTOP, OLOADBOT,
|
||||
input ADDSUBTOP, ADDSUBBOT,
|
||||
input OHOLDTOP, OHOLDBOT,
|
||||
input CI, ACCUMCI, SIGNEXTIN,
|
||||
output [31:0] O,
|
||||
output CO, ACCUMCO, SIGNEXTOUT
|
||||
);
|
||||
parameter [0:0] NEG_TRIGGER = 0;
|
||||
parameter [0:0] C_REG = 0;
|
||||
parameter [0:0] A_REG = 0;
|
||||
parameter [0:0] B_REG = 0;
|
||||
parameter [0:0] D_REG = 0;
|
||||
parameter [0:0] TOP_8x8_MULT_REG = 0;
|
||||
parameter [0:0] BOT_8x8_MULT_REG = 0;
|
||||
parameter [0:0] PIPELINE_16x16_MULT_REG1 = 0;
|
||||
parameter [0:0] PIPELINE_16x16_MULT_REG2 = 0;
|
||||
parameter [1:0] TOPOUTPUT_SELECT = 0;
|
||||
parameter [1:0] TOPADDSUB_LOWERINPUT = 0;
|
||||
parameter [0:0] TOPADDSUB_UPPERINPUT = 0;
|
||||
parameter [1:0] TOPADDSUB_CARRYSELECT = 0;
|
||||
parameter [1:0] BOTOUTPUT_SELECT = 0;
|
||||
parameter [1:0] BOTADDSUB_LOWERINPUT = 0;
|
||||
parameter [0:0] BOTADDSUB_UPPERINPUT = 0;
|
||||
parameter [1:0] BOTADDSUB_CARRYSELECT = 0;
|
||||
parameter [0:0] MODE_8x8 = 0;
|
||||
parameter [0:0] A_SIGNED = 0;
|
||||
parameter [0:0] B_SIGNED = 0;
|
||||
|
||||
wire clock = CLK ^ NEG_TRIGGER;
|
||||
|
||||
// internal wires, compare Figure on page 133 of ICE Technology Library 3.0 and Fig 2 on page 2 of Lattice TN1295-DSP
|
||||
// http://www.latticesemi.com/~/media/LatticeSemi/Documents/TechnicalBriefs/SBTICETechnologyLibrary201608.pdf
|
||||
// https://www.latticesemi.com/-/media/LatticeSemi/Documents/ApplicationNotes/AD/DSPFunctionUsageGuideforICE40Devices.ashx
|
||||
wire [15:0] iA, iB, iC, iD;
|
||||
wire [15:0] iF, iJ, iK, iG;
|
||||
wire [31:0] iL, iH;
|
||||
wire [15:0] iW, iX, iP, iQ;
|
||||
wire [15:0] iY, iZ, iR, iS;
|
||||
wire HCI, LCI, LCO;
|
||||
|
||||
// Regs C and A
|
||||
reg [15:0] rC, rA;
|
||||
always @(posedge clock, posedge IRSTTOP) begin
|
||||
if (IRSTTOP) begin
|
||||
rC <= 0;
|
||||
rA <= 0;
|
||||
end else if (CE) begin
|
||||
if (!CHOLD) rC <= C;
|
||||
if (!AHOLD) rA <= A;
|
||||
end
|
||||
end
|
||||
assign iC = C_REG ? rC : C;
|
||||
assign iA = A_REG ? rA : A;
|
||||
|
||||
// Regs B and D
|
||||
reg [15:0] rB, rD;
|
||||
always @(posedge clock, posedge IRSTBOT) begin
|
||||
if (IRSTBOT) begin
|
||||
rB <= 0;
|
||||
rD <= 0;
|
||||
end else if (CE) begin
|
||||
if (!BHOLD) rB <= B;
|
||||
if (!DHOLD) rD <= D;
|
||||
end
|
||||
end
|
||||
assign iB = B_REG ? rB : B;
|
||||
assign iD = D_REG ? rD : D;
|
||||
|
||||
// Multiplier Stage
|
||||
wire [15:0] p_Ah_Bh, p_Al_Bh, p_Ah_Bl, p_Al_Bl;
|
||||
wire [15:0] Ah, Al, Bh, Bl;
|
||||
assign Ah = {A_SIGNED ? {8{iA[15]}} : 8'b0, iA[15: 8]};
|
||||
assign Al = {A_SIGNED ? {8{iA[ 7]}} : 8'b0, iA[ 7: 0]};
|
||||
assign Bh = {B_SIGNED ? {8{iB[15]}} : 8'b0, iB[15: 8]};
|
||||
assign Bl = {B_SIGNED ? {8{iB[ 7]}} : 8'b0, iB[ 7: 0]};
|
||||
assign p_Ah_Bh = Ah * Bh;
|
||||
assign p_Al_Bh = Al * Bh;
|
||||
assign p_Ah_Bl = Ah * Bl;
|
||||
assign p_Al_Bl = Al * Bl;
|
||||
|
||||
// Regs F and J
|
||||
reg [15:0] rF, rJ;
|
||||
always @(posedge clock, posedge IRSTTOP) begin
|
||||
if (IRSTTOP) begin
|
||||
rF <= 0;
|
||||
rJ <= 0;
|
||||
end else if (CE) begin
|
||||
rF <= p_Ah_Bh;
|
||||
if (!MODE_8x8) rJ <= p_Al_Bh;
|
||||
end
|
||||
end
|
||||
assign iF = TOP_8x8_MULT_REG ? rF : p_Ah_Bh;
|
||||
assign iJ = PIPELINE_16x16_MULT_REG1 ? rJ : p_Al_Bh;
|
||||
|
||||
// Regs K and G
|
||||
reg [15:0] rK, rG;
|
||||
always @(posedge clock, posedge IRSTBOT) begin
|
||||
if (IRSTBOT) begin
|
||||
rK <= 0;
|
||||
rG <= 0;
|
||||
end else if (CE) begin
|
||||
if (!MODE_8x8) rK <= p_Ah_Bl;
|
||||
rG <= p_Al_Bl;
|
||||
end
|
||||
end
|
||||
assign iK = PIPELINE_16x16_MULT_REG1 ? rK : p_Ah_Bl;
|
||||
assign iG = BOT_8x8_MULT_REG ? rG : p_Al_Bl;
|
||||
|
||||
// Adder Stage
|
||||
assign iL = iG + (iK << 8) + (iJ << 8) + (iF << 16);
|
||||
|
||||
// Reg H
|
||||
reg [31:0] rH;
|
||||
always @(posedge clock, posedge IRSTBOT) begin
|
||||
if (IRSTBOT) begin
|
||||
rH <= 0;
|
||||
end else if (CE) begin
|
||||
if (!MODE_8x8) rH <= iL;
|
||||
end
|
||||
end
|
||||
assign iH = PIPELINE_16x16_MULT_REG2 ? rH : iL;
|
||||
|
||||
// Hi Output Stage
|
||||
wire [15:0] XW, Oh;
|
||||
reg [15:0] rQ;
|
||||
assign iW = TOPADDSUB_UPPERINPUT ? iC : iQ;
|
||||
assign iX = (TOPADDSUB_LOWERINPUT == 0) ? iA : (TOPADDSUB_LOWERINPUT == 1) ? iF : (TOPADDSUB_LOWERINPUT == 2) ? iH[31:16] : {16{iZ[15]}};
|
||||
assign {ACCUMCO, XW} = iX + (iW ^ {16{ADDSUBTOP}}) + HCI;
|
||||
assign CO = ACCUMCO ^ ADDSUBTOP;
|
||||
assign iP = OLOADTOP ? iC : XW ^ {16{ADDSUBTOP}};
|
||||
always @(posedge clock, posedge ORSTTOP) begin
|
||||
if (ORSTTOP) begin
|
||||
rQ <= 0;
|
||||
end else if (CE) begin
|
||||
if (!OHOLDTOP) rQ <= iP;
|
||||
end
|
||||
end
|
||||
assign iQ = rQ;
|
||||
assign Oh = (TOPOUTPUT_SELECT == 0) ? iP : (TOPOUTPUT_SELECT == 1) ? iQ : (TOPOUTPUT_SELECT == 2) ? iF : iH[31:16];
|
||||
assign HCI = (TOPADDSUB_CARRYSELECT == 0) ? 1'b0 : (TOPADDSUB_CARRYSELECT == 1) ? 1'b1 : (TOPADDSUB_CARRYSELECT == 2) ? LCO : LCO ^ ADDSUBBOT;
|
||||
assign SIGNEXTOUT = iX[15];
|
||||
|
||||
// Lo Output Stage
|
||||
wire [15:0] YZ, Ol;
|
||||
reg [15:0] rS;
|
||||
assign iY = BOTADDSUB_UPPERINPUT ? iD : iS;
|
||||
assign iZ = (BOTADDSUB_LOWERINPUT == 0) ? iB : (BOTADDSUB_LOWERINPUT == 1) ? iG : (BOTADDSUB_LOWERINPUT == 2) ? iH[15:0] : {16{SIGNEXTIN}};
|
||||
assign {LCO, YZ} = iZ + (iY ^ {16{ADDSUBBOT}}) + LCI;
|
||||
assign iR = OLOADBOT ? iD : YZ ^ {16{ADDSUBBOT}};
|
||||
always @(posedge clock, posedge ORSTBOT) begin
|
||||
if (ORSTBOT) begin
|
||||
rS <= 0;
|
||||
end else if (CE) begin
|
||||
if (!OHOLDBOT) rS <= iR;
|
||||
end
|
||||
end
|
||||
assign iS = rS;
|
||||
assign Ol = (BOTOUTPUT_SELECT == 0) ? iR : (BOTOUTPUT_SELECT == 1) ? iS : (BOTOUTPUT_SELECT == 2) ? iG : iH[15:0];
|
||||
assign LCI = (BOTADDSUB_CARRYSELECT == 0) ? 1'b0 : (BOTADDSUB_CARRYSELECT == 1) ? 1'b1 : (BOTADDSUB_CARRYSELECT == 2) ? ACCUMCI : CI;
|
||||
assign O = {Oh, Ol};
|
||||
endmodule
|
||||
|
|
|
@ -79,6 +79,9 @@ struct SynthIce40Pass : public ScriptPass
|
|||
log(" -nobram\n");
|
||||
log(" do not use SB_RAM40_4K* cells in output netlist\n");
|
||||
log("\n");
|
||||
log(" -dsp\n");
|
||||
log(" use iCE40 UltraPlus DSP cells for large arithmetic\n");
|
||||
log("\n");
|
||||
log(" -noabc\n");
|
||||
log(" use built-in Yosys LUT techmapping instead of abc\n");
|
||||
log("\n");
|
||||
|
@ -96,7 +99,7 @@ struct SynthIce40Pass : public ScriptPass
|
|||
}
|
||||
|
||||
string top_opt, blif_file, edif_file, json_file;
|
||||
bool nocarry, nodffe, nobram, flatten, retime, relut, noabc, abc2, vpr;
|
||||
bool nocarry, nodffe, nobram, dsp, flatten, retime, relut, noabc, abc2, vpr;
|
||||
int min_ce_use;
|
||||
|
||||
void clear_flags() YS_OVERRIDE
|
||||
|
@ -109,6 +112,7 @@ struct SynthIce40Pass : public ScriptPass
|
|||
nodffe = false;
|
||||
min_ce_use = -1;
|
||||
nobram = false;
|
||||
dsp = false;
|
||||
flatten = true;
|
||||
retime = false;
|
||||
relut = false;
|
||||
|
@ -181,6 +185,10 @@ struct SynthIce40Pass : public ScriptPass
|
|||
nobram = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-dsp") {
|
||||
dsp = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-noabc") {
|
||||
noabc = true;
|
||||
continue;
|
||||
|
@ -214,11 +222,11 @@ struct SynthIce40Pass : public ScriptPass
|
|||
{
|
||||
run("read_verilog -lib +/ice40/cells_sim.v");
|
||||
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
|
||||
run("proc");
|
||||
}
|
||||
|
||||
if (flatten && check_label("flatten", "(unless -noflatten)"))
|
||||
{
|
||||
run("proc");
|
||||
run("flatten");
|
||||
run("tribuf -logic");
|
||||
run("deminout");
|
||||
|
@ -226,7 +234,23 @@ struct SynthIce40Pass : public ScriptPass
|
|||
|
||||
if (check_label("coarse"))
|
||||
{
|
||||
run("synth -lut 4 -run coarse");
|
||||
run("opt_expr");
|
||||
run("opt_clean");
|
||||
run("check");
|
||||
run("opt");
|
||||
run("wreduce");
|
||||
run("share");
|
||||
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
|
||||
run("opt_expr");
|
||||
run("opt_clean");
|
||||
if (help_mode || dsp)
|
||||
run("ice40_dsp", "(if -dsp)");
|
||||
run("alumacc");
|
||||
run("opt");
|
||||
run("fsm");
|
||||
run("opt -fast");
|
||||
run("memory -nomap");
|
||||
run("opt_clean");
|
||||
}
|
||||
|
||||
if (!nobram && check_label("bram", "(skip if -nobram)"))
|
||||
|
@ -282,7 +306,7 @@ struct SynthIce40Pass : public ScriptPass
|
|||
run("techmap -map +/gate2lut.v -D LUT_WIDTH=4", "(only if -noabc)");
|
||||
}
|
||||
if (!noabc) {
|
||||
run("abc -lut 4", "(skip if -noabc)");
|
||||
run("abc -dress -lut 4", "(skip if -noabc)");
|
||||
}
|
||||
run("clean");
|
||||
if (relut || help_mode) {
|
||||
|
|
|
@ -1,2 +1,11 @@
|
|||
test_ffs_[01][01][01][01][01]_*
|
||||
test_bram_[0-9]*
|
||||
/test_ffs_[01][01][01][01][01]_*
|
||||
/test_bram_[0-9]*
|
||||
/test_dsp_model
|
||||
/test_dsp_model.vcd
|
||||
/test_dsp_model_ref.v
|
||||
/test_dsp_model_uut.v
|
||||
/test_dsp_map
|
||||
/test_dsp_map.vcd
|
||||
/test_dsp_map_tb.v
|
||||
/test_dsp_map_top.v
|
||||
/test_dsp_map_syn.v
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
#!/bin/bash
|
||||
set -ex
|
||||
|
||||
for iter in {1..100}
|
||||
do
|
||||
SZA=$(( 3 + $RANDOM % 13 ))
|
||||
SZB=$(( 3 + $RANDOM % 13 ))
|
||||
SZO=$(( 3 + $RANDOM % 29 ))
|
||||
|
||||
C0=clk$(( $RANDOM & 1))
|
||||
C1=clk$(( $RANDOM & 1))
|
||||
C2=clk$(( $RANDOM & 1))
|
||||
C3=clk$(( $RANDOM & 1))
|
||||
|
||||
E0=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge )
|
||||
E1=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge )
|
||||
E2=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge )
|
||||
E3=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge )
|
||||
|
||||
SP=$( test $(( $RANDOM & 1 )) -eq 0 && echo S || echo P )
|
||||
|
||||
RC=$( test $(( $RANDOM & 1 )) -eq 0 && echo "reset" || echo "!reset" )
|
||||
RV="32'h$( echo $RANDOM | md5sum | cut -c1-8 )"
|
||||
|
||||
cat > test_dsp_map_top.v << EOT
|
||||
module top (
|
||||
input clk0, clk1, reset,
|
||||
input [$SZA:0] A,
|
||||
input [$SZB:0] B,
|
||||
output [$SZO:0] O
|
||||
);
|
||||
reg [15:0] AA, BB;
|
||||
reg [31:0] P, S;
|
||||
|
||||
always @($E0 $C0) AA <= A;
|
||||
always @($E1 $C1) BB <= B;
|
||||
always @($E2 $C2) P <= AA * BB;
|
||||
always @($E3 $C3) S <= $RC ? $RV : S + P;
|
||||
assign O = $SP;
|
||||
endmodule
|
||||
EOT
|
||||
|
||||
cat > test_dsp_map_tb.v << EOT
|
||||
\`timescale 1ns / 1ps
|
||||
module testbench;
|
||||
reg clk1, clk0, reset;
|
||||
reg [$SZA:0] A;
|
||||
reg [$SZB:0] B;
|
||||
|
||||
wire [$SZO:0] O_top, O_syn;
|
||||
|
||||
top top_inst (.clk0(clk0), .clk1(clk1), .reset(reset), .A(A), .B(B), .O(O_top));
|
||||
syn syn_inst (.clk0(clk0), .clk1(clk1), .reset(reset), .A(A), .B(B), .O(O_syn));
|
||||
|
||||
initial begin
|
||||
// \$dumpfile("test_dsp_map.vcd");
|
||||
// \$dumpvars(0, testbench);
|
||||
|
||||
#2;
|
||||
clk0 = 0;
|
||||
clk1 = 0;
|
||||
reset = 1;
|
||||
reset = $RC;
|
||||
A = 0;
|
||||
B = 0;
|
||||
|
||||
repeat (3) begin
|
||||
#2; clk0 = ~clk0;
|
||||
#2; clk0 = ~clk0;
|
||||
#2; clk1 = ~clk1;
|
||||
#2; clk1 = ~clk1;
|
||||
end
|
||||
|
||||
repeat (100) begin
|
||||
#2;
|
||||
A = \$urandom;
|
||||
B = \$urandom;
|
||||
reset = \$urandom & \$urandom & \$urandom & \$urandom;
|
||||
if (\$urandom & 1) begin
|
||||
#2; clk0 = ~clk0;
|
||||
#2; clk0 = ~clk0;
|
||||
end else begin
|
||||
#2; clk1 = ~clk1;
|
||||
#2; clk1 = ~clk1;
|
||||
end
|
||||
#2;
|
||||
if (O_top !== O_syn) begin
|
||||
\$display("ERROR: O_top=%b O_syn=%b", O_top, O_syn);
|
||||
\$stop;
|
||||
end
|
||||
// \$display("OK O_top=O_syn=%b", O_top);
|
||||
end
|
||||
|
||||
\$display("Test passed.");
|
||||
\$finish;
|
||||
end
|
||||
endmodule
|
||||
EOT
|
||||
|
||||
../../../yosys -p 'read_verilog test_dsp_map_top.v; synth_ice40 -dsp; rename top syn; write_verilog test_dsp_map_syn.v'
|
||||
iverilog -o test_dsp_map -s testbench test_dsp_map_tb.v test_dsp_map_top.v test_dsp_map_syn.v ../cells_sim.v
|
||||
vvp -N test_dsp_map
|
||||
done
|
||||
|
||||
: ""
|
||||
: "#### All tests passed. ####"
|
||||
: ""
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
set -ex
|
||||
sed 's/SB_MAC16/SB_MAC16_UUT/; /SB_MAC16_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp_model_uut.v
|
||||
cat /opt/lscc/iCEcube2.2017.01/verilog/sb_ice_syn.v > test_dsp_model_ref.v
|
||||
for tb in testbench \
|
||||
testbench_comb_8x8_A testbench_comb_8x8_B testbench_comb_16x16 \
|
||||
testbench_seq_16x16_A testbench_seq_16x16_B
|
||||
do
|
||||
iverilog -s $tb -o test_dsp_model test_dsp_model.v test_dsp_model_uut.v test_dsp_model_ref.v
|
||||
vvp -N ./test_dsp_model
|
||||
done
|
|
@ -0,0 +1,342 @@
|
|||
`timescale 1ns / 1ps
|
||||
|
||||
module testbench;
|
||||
parameter [0:0] NEG_TRIGGER = 0;
|
||||
parameter [0:0] C_REG = 0;
|
||||
parameter [0:0] A_REG = 0;
|
||||
parameter [0:0] B_REG = 0;
|
||||
parameter [0:0] D_REG = 0;
|
||||
parameter [0:0] TOP_8x8_MULT_REG = 0;
|
||||
parameter [0:0] BOT_8x8_MULT_REG = 0;
|
||||
parameter [0:0] PIPELINE_16x16_MULT_REG1 = 0;
|
||||
parameter [0:0] PIPELINE_16x16_MULT_REG2 = 0;
|
||||
parameter [1:0] TOPOUTPUT_SELECT = 0;
|
||||
parameter [1:0] TOPADDSUB_LOWERINPUT = 0;
|
||||
parameter [0:0] TOPADDSUB_UPPERINPUT = 1;
|
||||
parameter [1:0] TOPADDSUB_CARRYSELECT = 0;
|
||||
parameter [1:0] BOTOUTPUT_SELECT = 0;
|
||||
parameter [1:0] BOTADDSUB_LOWERINPUT = 0;
|
||||
parameter [0:0] BOTADDSUB_UPPERINPUT = 1;
|
||||
parameter [1:0] BOTADDSUB_CARRYSELECT = 0;
|
||||
parameter [0:0] MODE_8x8 = 0;
|
||||
parameter [0:0] A_SIGNED = 0;
|
||||
parameter [0:0] B_SIGNED = 0;
|
||||
|
||||
reg CLK, CE;
|
||||
reg [15:0] C, A, B, D;
|
||||
reg AHOLD, BHOLD, CHOLD, DHOLD;
|
||||
reg IRSTTOP, IRSTBOT;
|
||||
reg ORSTTOP, ORSTBOT;
|
||||
reg OLOADTOP, OLOADBOT;
|
||||
reg ADDSUBTOP, ADDSUBBOT;
|
||||
reg OHOLDTOP, OHOLDBOT;
|
||||
reg CI, ACCUMCI, SIGNEXTIN;
|
||||
|
||||
output [31:0] REF_O, UUT_O;
|
||||
output REF_CO, REF_ACCUMCO, REF_SIGNEXTOUT;
|
||||
output UUT_CO, UUT_ACCUMCO, UUT_SIGNEXTOUT;
|
||||
|
||||
integer errcount = 0;
|
||||
|
||||
task clkcycle;
|
||||
begin
|
||||
#5;
|
||||
CLK = ~CLK;
|
||||
#10;
|
||||
CLK = ~CLK;
|
||||
#2;
|
||||
if (REF_O !== UUT_O) begin
|
||||
$display("ERROR at %1t: REF_O=%b UUT_O=%b DIFF=%b", $time, REF_O, UUT_O, REF_O ^ UUT_O);
|
||||
errcount = errcount + 1;
|
||||
end
|
||||
if (REF_CO !== UUT_CO) begin
|
||||
$display("ERROR at %1t: REF_CO=%b UUT_CO=%b", $time, REF_CO, UUT_CO);
|
||||
errcount = errcount + 1;
|
||||
end
|
||||
if (REF_ACCUMCO !== UUT_ACCUMCO) begin
|
||||
$display("ERROR at %1t: REF_ACCUMCO=%b UUT_ACCUMCO=%b", $time, REF_ACCUMCO, UUT_ACCUMCO);
|
||||
errcount = errcount + 1;
|
||||
end
|
||||
if (REF_SIGNEXTOUT !== UUT_SIGNEXTOUT) begin
|
||||
$display("ERROR at %1t: REF_SIGNEXTOUT=%b UUT_SIGNEXTOUT=%b", $time, REF_SIGNEXTOUT, UUT_SIGNEXTOUT);
|
||||
errcount = errcount + 1;
|
||||
end
|
||||
#3;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
$dumpfile("test_dsp_model.vcd");
|
||||
$dumpvars(0, testbench);
|
||||
|
||||
#2;
|
||||
CLK = NEG_TRIGGER;
|
||||
CE = 1;
|
||||
{C, A, B, D} = 0;
|
||||
{AHOLD, BHOLD, CHOLD, DHOLD} = 0;
|
||||
{OLOADTOP, OLOADBOT} = 0;
|
||||
{ADDSUBTOP, ADDSUBBOT} = 0;
|
||||
{OHOLDTOP, OHOLDBOT} = 0;
|
||||
{CI, ACCUMCI, SIGNEXTIN} = 0;
|
||||
|
||||
{IRSTTOP, IRSTBOT} = ~0;
|
||||
{ORSTTOP, ORSTBOT} = ~0;
|
||||
|
||||
#3;
|
||||
{IRSTTOP, IRSTBOT} = 0;
|
||||
{ORSTTOP, ORSTBOT} = 0;
|
||||
|
||||
repeat (300) begin
|
||||
clkcycle;
|
||||
|
||||
A = $urandom;
|
||||
B = $urandom;
|
||||
C = $urandom;
|
||||
D = $urandom;
|
||||
|
||||
{AHOLD, BHOLD, CHOLD, DHOLD} = $urandom & $urandom & $urandom;
|
||||
{OLOADTOP, OLOADBOT} = $urandom & $urandom & $urandom;
|
||||
{ADDSUBTOP, ADDSUBBOT} = $urandom & $urandom & $urandom;
|
||||
{OHOLDTOP, OHOLDBOT} = $urandom & $urandom & $urandom;
|
||||
{CI, ACCUMCI, SIGNEXTIN} = $urandom & $urandom & $urandom;
|
||||
|
||||
{IRSTTOP, IRSTBOT} = $urandom & $urandom & $urandom;
|
||||
{ORSTTOP, ORSTBOT} = $urandom & $urandom & $urandom;
|
||||
end
|
||||
|
||||
if (errcount == 0) begin
|
||||
$display("All tests passed.");
|
||||
$finish;
|
||||
end else begin
|
||||
$display("Caught %1d errors.", errcount);
|
||||
$stop;
|
||||
end
|
||||
end
|
||||
|
||||
SB_MAC16 #(
|
||||
.NEG_TRIGGER (NEG_TRIGGER ),
|
||||
.C_REG (C_REG ),
|
||||
.A_REG (A_REG ),
|
||||
.B_REG (B_REG ),
|
||||
.D_REG (D_REG ),
|
||||
.TOP_8x8_MULT_REG (TOP_8x8_MULT_REG ),
|
||||
.BOT_8x8_MULT_REG (BOT_8x8_MULT_REG ),
|
||||
.PIPELINE_16x16_MULT_REG1 (PIPELINE_16x16_MULT_REG1),
|
||||
.PIPELINE_16x16_MULT_REG2 (PIPELINE_16x16_MULT_REG2),
|
||||
.TOPOUTPUT_SELECT (TOPOUTPUT_SELECT ),
|
||||
.TOPADDSUB_LOWERINPUT (TOPADDSUB_LOWERINPUT ),
|
||||
.TOPADDSUB_UPPERINPUT (TOPADDSUB_UPPERINPUT ),
|
||||
.TOPADDSUB_CARRYSELECT (TOPADDSUB_CARRYSELECT ),
|
||||
.BOTOUTPUT_SELECT (BOTOUTPUT_SELECT ),
|
||||
.BOTADDSUB_LOWERINPUT (BOTADDSUB_LOWERINPUT ),
|
||||
.BOTADDSUB_UPPERINPUT (BOTADDSUB_UPPERINPUT ),
|
||||
.BOTADDSUB_CARRYSELECT (BOTADDSUB_CARRYSELECT ),
|
||||
.MODE_8x8 (MODE_8x8 ),
|
||||
.A_SIGNED (A_SIGNED ),
|
||||
.B_SIGNED (B_SIGNED )
|
||||
) ref (
|
||||
.CLK (CLK ),
|
||||
.CE (CE ),
|
||||
.C (C ),
|
||||
.A (A ),
|
||||
.B (B ),
|
||||
.D (D ),
|
||||
.AHOLD (AHOLD ),
|
||||
.BHOLD (BHOLD ),
|
||||
.CHOLD (CHOLD ),
|
||||
.DHOLD (DHOLD ),
|
||||
.IRSTTOP (IRSTTOP ),
|
||||
.IRSTBOT (IRSTBOT ),
|
||||
.ORSTTOP (ORSTTOP ),
|
||||
.ORSTBOT (ORSTBOT ),
|
||||
.OLOADTOP (OLOADTOP ),
|
||||
.OLOADBOT (OLOADBOT ),
|
||||
.ADDSUBTOP (ADDSUBTOP ),
|
||||
.ADDSUBBOT (ADDSUBBOT ),
|
||||
.OHOLDTOP (OHOLDTOP ),
|
||||
.OHOLDBOT (OHOLDBOT ),
|
||||
.CI (CI ),
|
||||
.ACCUMCI (ACCUMCI ),
|
||||
.SIGNEXTIN (SIGNEXTIN ),
|
||||
.O (REF_O ),
|
||||
.CO (REF_CO ),
|
||||
.ACCUMCO (REF_ACCUMCO ),
|
||||
.SIGNEXTOUT (REF_SIGNEXTOUT)
|
||||
);
|
||||
|
||||
SB_MAC16_UUT #(
|
||||
.NEG_TRIGGER (NEG_TRIGGER ),
|
||||
.C_REG (C_REG ),
|
||||
.A_REG (A_REG ),
|
||||
.B_REG (B_REG ),
|
||||
.D_REG (D_REG ),
|
||||
.TOP_8x8_MULT_REG (TOP_8x8_MULT_REG ),
|
||||
.BOT_8x8_MULT_REG (BOT_8x8_MULT_REG ),
|
||||
.PIPELINE_16x16_MULT_REG1 (PIPELINE_16x16_MULT_REG1),
|
||||
.PIPELINE_16x16_MULT_REG2 (PIPELINE_16x16_MULT_REG2),
|
||||
.TOPOUTPUT_SELECT (TOPOUTPUT_SELECT ),
|
||||
.TOPADDSUB_LOWERINPUT (TOPADDSUB_LOWERINPUT ),
|
||||
.TOPADDSUB_UPPERINPUT (TOPADDSUB_UPPERINPUT ),
|
||||
.TOPADDSUB_CARRYSELECT (TOPADDSUB_CARRYSELECT ),
|
||||
.BOTOUTPUT_SELECT (BOTOUTPUT_SELECT ),
|
||||
.BOTADDSUB_LOWERINPUT (BOTADDSUB_LOWERINPUT ),
|
||||
.BOTADDSUB_UPPERINPUT (BOTADDSUB_UPPERINPUT ),
|
||||
.BOTADDSUB_CARRYSELECT (BOTADDSUB_CARRYSELECT ),
|
||||
.MODE_8x8 (MODE_8x8 ),
|
||||
.A_SIGNED (A_SIGNED ),
|
||||
.B_SIGNED (B_SIGNED )
|
||||
) uut (
|
||||
.CLK (CLK ),
|
||||
.CE (CE ),
|
||||
.C (C ),
|
||||
.A (A ),
|
||||
.B (B ),
|
||||
.D (D ),
|
||||
.AHOLD (AHOLD ),
|
||||
.BHOLD (BHOLD ),
|
||||
.CHOLD (CHOLD ),
|
||||
.DHOLD (DHOLD ),
|
||||
.IRSTTOP (IRSTTOP ),
|
||||
.IRSTBOT (IRSTBOT ),
|
||||
.ORSTTOP (ORSTTOP ),
|
||||
.ORSTBOT (ORSTBOT ),
|
||||
.OLOADTOP (OLOADTOP ),
|
||||
.OLOADBOT (OLOADBOT ),
|
||||
.ADDSUBTOP (ADDSUBTOP ),
|
||||
.ADDSUBBOT (ADDSUBBOT ),
|
||||
.OHOLDTOP (OHOLDTOP ),
|
||||
.OHOLDBOT (OHOLDBOT ),
|
||||
.CI (CI ),
|
||||
.ACCUMCI (ACCUMCI ),
|
||||
.SIGNEXTIN (SIGNEXTIN ),
|
||||
.O (UUT_O ),
|
||||
.CO (UUT_CO ),
|
||||
.ACCUMCO (UUT_ACCUMCO ),
|
||||
.SIGNEXTOUT (UUT_SIGNEXTOUT)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module testbench_comb_8x8_A;
|
||||
testbench #(
|
||||
.NEG_TRIGGER (0),
|
||||
.C_REG (0),
|
||||
.A_REG (0),
|
||||
.B_REG (0),
|
||||
.D_REG (0),
|
||||
.TOP_8x8_MULT_REG (0),
|
||||
.BOT_8x8_MULT_REG (0),
|
||||
.PIPELINE_16x16_MULT_REG1 (0),
|
||||
.PIPELINE_16x16_MULT_REG2 (0),
|
||||
.TOPOUTPUT_SELECT (2), // 0=P, 1=Q, 2=8x8, 3=16x16
|
||||
.TOPADDSUB_LOWERINPUT (0), // 0=A, 1=8x8, 2=16x16, 3=S-EXT
|
||||
.TOPADDSUB_UPPERINPUT (0), // 0=Q, 1=C
|
||||
.TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI
|
||||
.BOTOUTPUT_SELECT (2), // 0=R, 1=S, 2=8x8, 3=16x16
|
||||
.BOTADDSUB_LOWERINPUT (0), // 0=B, 1=8x8, 2=16x16, 3=S-EXT
|
||||
.BOTADDSUB_UPPERINPUT (0), // 0=S, 1=D
|
||||
.BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI
|
||||
.MODE_8x8 (0),
|
||||
.A_SIGNED (0),
|
||||
.B_SIGNED (0)
|
||||
) testbench ();
|
||||
endmodule
|
||||
|
||||
module testbench_comb_8x8_B;
|
||||
testbench #(
|
||||
.NEG_TRIGGER (0),
|
||||
.C_REG (0),
|
||||
.A_REG (0),
|
||||
.B_REG (0),
|
||||
.D_REG (0),
|
||||
.TOP_8x8_MULT_REG (0),
|
||||
.BOT_8x8_MULT_REG (0),
|
||||
.PIPELINE_16x16_MULT_REG1 (0),
|
||||
.PIPELINE_16x16_MULT_REG2 (0),
|
||||
.TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16
|
||||
.TOPADDSUB_LOWERINPUT (1), // 0=A, 1=8x8, 2=16x16, 3=S-EXT
|
||||
.TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C
|
||||
.TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI
|
||||
.BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16
|
||||
.BOTADDSUB_LOWERINPUT (1), // 0=B, 1=8x8, 2=16x16, 3=S-EXT
|
||||
.BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D
|
||||
.BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI
|
||||
.MODE_8x8 (0),
|
||||
.A_SIGNED (0),
|
||||
.B_SIGNED (0)
|
||||
) testbench ();
|
||||
endmodule
|
||||
|
||||
module testbench_comb_16x16;
|
||||
testbench #(
|
||||
.NEG_TRIGGER (0),
|
||||
.C_REG (0),
|
||||
.A_REG (0),
|
||||
.B_REG (0),
|
||||
.D_REG (0),
|
||||
.TOP_8x8_MULT_REG (0),
|
||||
.BOT_8x8_MULT_REG (0),
|
||||
.PIPELINE_16x16_MULT_REG1 (0),
|
||||
.PIPELINE_16x16_MULT_REG2 (0),
|
||||
.TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16
|
||||
.TOPADDSUB_LOWERINPUT (2), // 0=A, 1=8x8, 2=16x16, 3=S-EXT
|
||||
.TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C
|
||||
.TOPADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI
|
||||
.BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16
|
||||
.BOTADDSUB_LOWERINPUT (2), // 0=B, 1=8x8, 2=16x16, 3=S-EXT
|
||||
.BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D
|
||||
.BOTADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI
|
||||
.MODE_8x8 (0),
|
||||
.A_SIGNED (0),
|
||||
.B_SIGNED (0)
|
||||
) testbench ();
|
||||
endmodule
|
||||
|
||||
module testbench_seq_16x16_A;
|
||||
testbench #(
|
||||
.NEG_TRIGGER (0),
|
||||
.C_REG (1),
|
||||
.A_REG (1),
|
||||
.B_REG (1),
|
||||
.D_REG (1),
|
||||
.TOP_8x8_MULT_REG (1),
|
||||
.BOT_8x8_MULT_REG (1),
|
||||
.PIPELINE_16x16_MULT_REG1 (1),
|
||||
.PIPELINE_16x16_MULT_REG2 (1),
|
||||
.TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16
|
||||
.TOPADDSUB_LOWERINPUT (2), // 0=A, 1=8x8, 2=16x16, 3=S-EXT
|
||||
.TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C
|
||||
.TOPADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI
|
||||
.BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16
|
||||
.BOTADDSUB_LOWERINPUT (2), // 0=B, 1=8x8, 2=16x16, 3=S-EXT
|
||||
.BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D
|
||||
.BOTADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI
|
||||
.MODE_8x8 (0),
|
||||
.A_SIGNED (0),
|
||||
.B_SIGNED (0)
|
||||
) testbench ();
|
||||
endmodule
|
||||
|
||||
module testbench_seq_16x16_B;
|
||||
testbench #(
|
||||
.NEG_TRIGGER (0),
|
||||
.C_REG (1),
|
||||
.A_REG (1),
|
||||
.B_REG (1),
|
||||
.D_REG (1),
|
||||
.TOP_8x8_MULT_REG (1),
|
||||
.BOT_8x8_MULT_REG (1),
|
||||
.PIPELINE_16x16_MULT_REG1 (1),
|
||||
.PIPELINE_16x16_MULT_REG2 (0),
|
||||
.TOPOUTPUT_SELECT (1), // 0=P, 1=Q, 2=8x8, 3=16x16
|
||||
.TOPADDSUB_LOWERINPUT (2), // 0=A, 1=8x8, 2=16x16, 3=S-EXT
|
||||
.TOPADDSUB_UPPERINPUT (0), // 0=Q, 1=C
|
||||
.TOPADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI
|
||||
.BOTOUTPUT_SELECT (1), // 0=R, 1=S, 2=8x8, 3=16x16
|
||||
.BOTADDSUB_LOWERINPUT (2), // 0=B, 1=8x8, 2=16x16, 3=S-EXT
|
||||
.BOTADDSUB_UPPERINPUT (0), // 0=S, 1=D
|
||||
.BOTADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI
|
||||
.MODE_8x8 (0),
|
||||
.A_SIGNED (0),
|
||||
.B_SIGNED (0)
|
||||
) testbench ();
|
||||
endmodule
|
|
@ -11,4 +11,4 @@ do
|
|||
done
|
||||
shift "$((OPTIND-1))"
|
||||
|
||||
exec ${MAKE:-make} -f ../tools/autotest.mk $seed EXTRA_FLAGS="-e" *.v
|
||||
exec ${MAKE:-make} -f ../tools/autotest.mk $seed EXTRA_FLAGS+="-e" *.v
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# This file contains the names of verilog files to exclude from verilog to FIRRTL regression tests due to known failures.
|
||||
code_hdl_models_arbiter.v error: reg rst; cannot be driven by primitives or continuous assignment.
|
||||
code_hdl_models_clk_div_45.v yosys issue: 2nd PMUXTREE pass yields: ERROR: Negative edge clock on FF clk_div_45.$procdff$49.
|
||||
code_hdl_models_d_ff_gates.v combinational loop
|
||||
code_hdl_models_d_latch_gates.v combinational loop
|
||||
code_hdl_models_dff_async_reset.v $adff
|
||||
code_hdl_models_tff_async_reset.v $adff
|
||||
code_hdl_models_uart.v $adff
|
||||
code_specman_switch_fabric.v subfield assignment (bits() <= ...)
|
||||
code_tidbits_asyn_reset.v $adff
|
||||
code_tidbits_reg_seq_example.v $adff
|
||||
code_verilog_tutorial_always_example.v empty module
|
||||
code_verilog_tutorial_escape_id.v make_id issues (name begins with a digit)
|
||||
code_verilog_tutorial_explicit.v firrtl backend bug (empty module)
|
||||
code_verilog_tutorial_first_counter.v error: reg rst; cannot be driven by primitives or continuous assignment.
|
||||
code_verilog_tutorial_fsm_full.v error: reg reset; cannot be driven by primitives or continuous assignment.
|
||||
code_verilog_tutorial_if_else.v empty module (everything is under 'always @ (posedge clk)')
|
||||
[code_verilog_tutorial_n_out_primitive.v empty module
|
||||
code_verilog_tutorial_parallel_if.v empty module (everything is under 'always @ (posedge clk)')
|
||||
code_verilog_tutorial_simple_function.v empty module (no hardware)
|
||||
code_verilog_tutorial_simple_if.v empty module (everything is under 'always @ (posedge clk)')
|
||||
code_verilog_tutorial_task_global.v empty module (everything is under 'always @ (posedge clk)')
|
||||
code_verilog_tutorial_v2k_reg.v empty module
|
||||
code_verilog_tutorial_which_clock.v $adff
|
|
@ -0,0 +1,21 @@
|
|||
module top(
|
||||
input clk,
|
||||
input rst,
|
||||
input [2:0] a,
|
||||
output [1:0] b
|
||||
);
|
||||
reg [2:0] b_reg;
|
||||
initial begin
|
||||
b_reg <= 3'b0;
|
||||
end
|
||||
|
||||
assign b = b_reg[1:0];
|
||||
always @(posedge clk or posedge rst) begin
|
||||
if(rst) begin
|
||||
b_reg <= 3'b0;
|
||||
end else begin
|
||||
b_reg <= a;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
read_verilog opt_ff.v
|
||||
synth_ice40
|
||||
ice40_unlut
|
|
@ -0,0 +1,42 @@
|
|||
module dff0_test(n1, n1_inv, clk);
|
||||
input clk;
|
||||
output n1;
|
||||
reg n1 = 32'd0;
|
||||
output n1_inv;
|
||||
always @(posedge clk)
|
||||
n1 <= n1_inv;
|
||||
assign n1_inv = ~n1;
|
||||
endmodule
|
||||
|
||||
module dff1_test(n1, n1_inv, clk);
|
||||
input clk;
|
||||
(* init = 32'd1 *)
|
||||
output n1;
|
||||
reg n1 = 32'd1;
|
||||
output n1_inv;
|
||||
always @(posedge clk)
|
||||
n1 <= n1_inv;
|
||||
assign n1_inv = ~n1;
|
||||
endmodule
|
||||
|
||||
module dff0a_test(n1, n1_inv, clk);
|
||||
input clk;
|
||||
(* init = 32'd0 *) // Must be consistent with reg initialiser below
|
||||
output n1;
|
||||
reg n1 = 32'd0;
|
||||
output n1_inv;
|
||||
always @(posedge clk)
|
||||
n1 <= n1_inv;
|
||||
assign n1_inv = ~n1;
|
||||
endmodule
|
||||
|
||||
module dff1a_test(n1, n1_inv, clk);
|
||||
input clk;
|
||||
(* init = 32'd1 *) // Must be consistent with reg initialiser below
|
||||
output n1;
|
||||
reg n1 = 32'd1;
|
||||
output n1_inv;
|
||||
always @(posedge clk)
|
||||
n1 <= n1_inv;
|
||||
assign n1_inv = ~n1;
|
||||
endmodule
|
|
@ -1,3 +1,5 @@
|
|||
`default_nettype none
|
||||
|
||||
module hierdefparam_top(input [7:0] A, output [7:0] Y);
|
||||
generate begin:foo
|
||||
hierdefparam_a mod_a(.A(A), .Y(Y));
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# This file contains the names of verilog files to exclude from verilog to FIRRTL regression tests due to known failures.
|
||||
arraycells.v inst id[0] of
|
||||
dff_different_styles.v
|
||||
generate.v combinational loop
|
||||
hierdefparam.v inst id[0] of
|
||||
i2c_master_tests.v $adff
|
||||
macros.v drops modules
|
||||
mem2reg.v drops modules
|
||||
mem_arst.v $adff
|
||||
memory.v $adff
|
||||
multiplier.v inst id[0] of
|
||||
muxtree.v drops modules
|
||||
omsp_dbg_uart.v $adff
|
||||
operators.v $pow
|
||||
paramods.v subfield assignment (bits() <= ...)
|
||||
partsel.v drops modules
|
||||
process.v drops modules
|
||||
realexpr.v drops modules
|
||||
scopes.v original verilog issues ( -x where x isn't declared signed)
|
||||
sincos.v $adff
|
||||
specify.v no code (empty module generates error
|
||||
subbytes.v $adff
|
||||
task_func.v drops modules
|
||||
values.v combinational loop
|
||||
vloghammer.v combinational loop
|
||||
wreduce.v original verilog issues ( -x where x isn't declared signed)
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
EXTRA_FLAGS=
|
||||
SEED=
|
||||
|
||||
# Don't bother defining default values for SEED and EXTRA_FLAGS.
|
||||
# Their "natural" default values should be sufficient,
|
||||
# and they may be overridden in the environment.
|
||||
ifneq ($(strip $(SEED)),)
|
||||
SEEDOPT=-S$(SEED)
|
||||
endif
|
||||
|
|
|
@ -17,12 +17,18 @@ scriptfiles=""
|
|||
scriptopt=""
|
||||
toolsdir="$(cd $(dirname $0); pwd)"
|
||||
warn_iverilog_git=false
|
||||
# The following are used in verilog to firrtl regression tests.
|
||||
# Typically these will be passed as environment variables:
|
||||
#EXTRA_FLAGS="--firrtl2verilog 'java -cp /.../firrtl/utils/bin/firrtl.jar firrtl.Driver'"
|
||||
# The tests are skipped if firrtl2verilog is the empty string (the default).
|
||||
firrtl2verilog=""
|
||||
xfirrtl="../xfirrtl"
|
||||
|
||||
if [ ! -f $toolsdir/cmp_tbdata -o $toolsdir/cmp_tbdata.c -nt $toolsdir/cmp_tbdata ]; then
|
||||
( set -ex; ${CC:-gcc} -Wall -o $toolsdir/cmp_tbdata $toolsdir/cmp_tbdata.c; ) || exit 1
|
||||
fi
|
||||
|
||||
while getopts xmGl:wkjvref:s:p:n:S:I: opt; do
|
||||
while getopts xmGl:wkjvref:s:p:n:S:I:-: opt; do
|
||||
case "$opt" in
|
||||
x)
|
||||
use_xsim=true ;;
|
||||
|
@ -59,8 +65,24 @@ while getopts xmGl:wkjvref:s:p:n:S:I: opt; do
|
|||
include_opts="$include_opts -I $OPTARG"
|
||||
xinclude_opts="$xinclude_opts -i $OPTARG"
|
||||
minclude_opts="$minclude_opts +incdir+$OPTARG" ;;
|
||||
-)
|
||||
case "${OPTARG}" in
|
||||
xfirrtl)
|
||||
xfirrtl="${!OPTIND}"
|
||||
OPTIND=$(( $OPTIND + 1 ))
|
||||
;;
|
||||
firrtl2verilog)
|
||||
firrtl2verilog="${!OPTIND}"
|
||||
OPTIND=$(( $OPTIND + 1 ))
|
||||
;;
|
||||
*)
|
||||
if [ "$OPTERR" == 1 ] && [ "${optspec:0:1}" != ":" ]; then
|
||||
echo "Unknown option --${OPTARG}" >&2
|
||||
fi
|
||||
;;
|
||||
esac;;
|
||||
*)
|
||||
echo "Usage: $0 [-x|-m] [-G] [-w] [-k] [-j] [-v] [-r] [-e] [-l libs] [-f frontend] [-s script] [-p cmdstring] [-n iters] [-S seed] [-I incdir] verilog-files\n" >&2
|
||||
echo "Usage: $0 [-x|-m] [-G] [-w] [-k] [-j] [-v] [-r] [-e] [-l libs] [-f frontend] [-s script] [-p cmdstring] [-n iters] [-S seed] [-I incdir] [--xfirrtl FIRRTL test exclude file] [--firrtl2verilog command to generate verilog from firrtl] verilog-files\n" >&2
|
||||
exit 1
|
||||
esac
|
||||
done
|
||||
|
@ -109,6 +131,8 @@ do
|
|||
fn=$(basename $fn)
|
||||
bn=$(basename $bn)
|
||||
|
||||
rm -f ${bn}_ref.fir
|
||||
|
||||
egrep -v '^\s*`timescale' ../$fn > ${bn}_ref.v
|
||||
|
||||
if [ ! -f ../${bn}_tb.v ]; then
|
||||
|
@ -148,6 +172,13 @@ do
|
|||
else
|
||||
test_passes -f "$frontend $include_opts" -p "hierarchy; proc; opt; memory; opt; fsm; opt -full -fine" ${bn}_ref.v
|
||||
test_passes -f "$frontend $include_opts" -p "hierarchy; synth -run coarse; techmap; opt; abc -dff" ${bn}_ref.v
|
||||
if [ -n "$firrtl2verilog" ]; then
|
||||
if test -z "$xfirrtl" || ! grep "$fn" "$xfirrtl" ; then
|
||||
"$toolsdir"/../../yosys -b "firrtl" -o ${bn}_ref.fir -f "$frontend $include_opts" -p "prep -nordff; proc; opt; memory; opt; fsm; opt -full -fine; pmuxtree" ${bn}_ref.v
|
||||
$firrtl2verilog -i ${bn}_ref.fir -o ${bn}_ref.fir.v -X verilog
|
||||
test_passes -f "$frontend $include_opts" -p "hierarchy; proc; opt; memory; opt; fsm; opt -full -fine" ${bn}_ref.fir.v
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
touch ../${bn}.log
|
||||
}
|
||||
|
@ -160,14 +191,18 @@ do
|
|||
( set -ex; body; ) > ${bn}.err 2>&1
|
||||
fi
|
||||
|
||||
did_firrtl=""
|
||||
if [ -f ${bn}.out/${bn}_ref.fir ]; then
|
||||
did_firrtl="+FIRRTL "
|
||||
fi
|
||||
if [ -f ${bn}.log ]; then
|
||||
mv ${bn}.err ${bn}.log
|
||||
echo "${status_prefix}-> ok"
|
||||
echo "${status_prefix}${did_firrtl}-> ok"
|
||||
elif [ -f ${bn}.skip ]; then
|
||||
mv ${bn}.err ${bn}.skip
|
||||
echo "${status_prefix}-> skip"
|
||||
else
|
||||
echo "${status_prefix}-> ERROR!"
|
||||
echo "${status_prefix}${did_firrtl}-> ERROR!"
|
||||
if $warn_iverilog_git; then
|
||||
echo "Note: Make sure that 'iverilog' is an up-to-date git checkout of Icarus Verilog."
|
||||
fi
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/env bash
|
||||
# Simple test of hierarchy -auto-top.
|
||||
|
||||
set -e
|
||||
|
||||
echo -n " TOP first - "
|
||||
../../yosys -s - <<- EOY | grep "Automatically selected TOP as design top module"
|
||||
read_verilog << EOV
|
||||
module TOP(a, y);
|
||||
input a;
|
||||
output [31:0] y;
|
||||
|
||||
aoi12 p [31:0] (a, y);
|
||||
endmodule
|
||||
|
||||
module aoi12(a, y);
|
||||
input a;
|
||||
output y;
|
||||
assign y = ~a;
|
||||
endmodule
|
||||
EOV
|
||||
hierarchy -auto-top
|
||||
EOY
|
||||
|
||||
echo -n " TOP last - "
|
||||
../../yosys -s - <<- EOY | grep "Automatically selected TOP as design top module"
|
||||
read_verilog << EOV
|
||||
module aoi12(a, y);
|
||||
input a;
|
||||
output y;
|
||||
assign y = ~a;
|
||||
endmodule
|
||||
|
||||
module TOP(a, y);
|
||||
input a;
|
||||
output [31:0] y;
|
||||
|
||||
aoi12 foo (a, y);
|
||||
endmodule
|
||||
EOV
|
||||
hierarchy -auto-top
|
||||
EOY
|
||||
|
||||
echo -n " no explicit top - "
|
||||
../../yosys -s - <<- EOY | grep "Automatically selected noTop as design top module."
|
||||
read_verilog << EOV
|
||||
module aoi12(a, y);
|
||||
input a;
|
||||
output y;
|
||||
assign y = ~a;
|
||||
endmodule
|
||||
|
||||
module noTop(a, y);
|
||||
input a;
|
||||
output [31:0] y;
|
||||
endmodule
|
||||
EOV
|
||||
hierarchy -auto-top
|
||||
EOY
|
|
@ -1,6 +1,14 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
for x in *.ys; do
|
||||
echo "Running $x.."
|
||||
../../yosys -ql ${x%.ys}.log $x
|
||||
done
|
||||
# Run any .sh files in this directory (with the exception of the file - run-test.sh
|
||||
shell_tests=$(echo *.sh | sed -e 's/run-test.sh//')
|
||||
if [ "$shell_tests" ]; then
|
||||
for s in $shell_tests; do
|
||||
echo "Running $s.."
|
||||
bash $s
|
||||
done
|
||||
fi
|
||||
|
|
Loading…
Reference in New Issue