mirror of https://github.com/YosysHQ/yosys.git
Merge branch 'YosysHQ:main' into main
This commit is contained in:
commit
e332ba807d
2
Makefile
2
Makefile
|
|
@ -177,7 +177,7 @@ ifeq ($(OS), Haiku)
|
|||
CXXFLAGS += -D_DEFAULT_SOURCE
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.60+70
|
||||
YOSYS_VER := 0.60+88
|
||||
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
|
||||
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2)
|
||||
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'.' -f3)
|
||||
|
|
|
|||
2
abc
2
abc
|
|
@ -1 +1 @@
|
|||
Subproject commit ef74590ebd78b3b707eeba56d8284faf018affa6
|
||||
Subproject commit 799ba632239b2a4db2bacda81de4e6efdc486b0c
|
||||
|
|
@ -40,6 +40,7 @@ struct RTLILFrontendWorker {
|
|||
bool flag_nooverwrite = false;
|
||||
bool flag_overwrite = false;
|
||||
bool flag_lib = false;
|
||||
bool flag_legalize = false;
|
||||
|
||||
int line_num;
|
||||
std::string line_buf;
|
||||
|
|
@ -322,6 +323,17 @@ struct RTLILFrontendWorker {
|
|||
return val;
|
||||
}
|
||||
|
||||
RTLIL::Wire *legalize_wire(RTLIL::IdString id)
|
||||
{
|
||||
int wires_size = current_module->wires_size();
|
||||
if (wires_size == 0)
|
||||
error("No wires found for legalization");
|
||||
int hash = hash_ops<RTLIL::IdString>::hash(id).yield();
|
||||
RTLIL::Wire *wire = current_module->wire_at(abs(hash % wires_size));
|
||||
log("Legalizing wire `%s' to `%s'.\n", log_id(id), log_id(wire->name));
|
||||
return wire;
|
||||
}
|
||||
|
||||
RTLIL::SigSpec parse_sigspec()
|
||||
{
|
||||
RTLIL::SigSpec sig;
|
||||
|
|
@ -339,8 +351,12 @@ struct RTLILFrontendWorker {
|
|||
std::optional<RTLIL::IdString> id = try_parse_id();
|
||||
if (id.has_value()) {
|
||||
RTLIL::Wire *wire = current_module->wire(*id);
|
||||
if (wire == nullptr)
|
||||
error("Wire `%s' not found.", *id);
|
||||
if (wire == nullptr) {
|
||||
if (flag_legalize)
|
||||
wire = legalize_wire(*id);
|
||||
else
|
||||
error("Wire `%s' not found.", *id);
|
||||
}
|
||||
sig = RTLIL::SigSpec(wire);
|
||||
} else {
|
||||
sig = RTLIL::SigSpec(parse_const());
|
||||
|
|
@ -349,17 +365,44 @@ struct RTLILFrontendWorker {
|
|||
|
||||
while (try_parse_char('[')) {
|
||||
int left = parse_integer();
|
||||
if (left >= sig.size() || left < 0)
|
||||
error("bit index %d out of range", left);
|
||||
if (left >= sig.size() || left < 0) {
|
||||
if (flag_legalize) {
|
||||
int legalized;
|
||||
if (sig.size() == 0)
|
||||
legalized = 0;
|
||||
else
|
||||
legalized = std::max(0, std::min(left, sig.size() - 1));
|
||||
log("Legalizing bit index %d to %d.\n", left, legalized);
|
||||
left = legalized;
|
||||
} else {
|
||||
error("bit index %d out of range", left);
|
||||
}
|
||||
}
|
||||
if (try_parse_char(':')) {
|
||||
int right = parse_integer();
|
||||
if (right < 0)
|
||||
error("bit index %d out of range", right);
|
||||
if (left < right)
|
||||
error("invalid slice [%d:%d]", left, right);
|
||||
sig = sig.extract(right, left-right+1);
|
||||
if (right < 0) {
|
||||
if (flag_legalize) {
|
||||
log("Legalizing bit index %d to %d.\n", right, 0);
|
||||
right = 0;
|
||||
} else
|
||||
error("bit index %d out of range", right);
|
||||
}
|
||||
if (left < right) {
|
||||
if (flag_legalize) {
|
||||
log("Legalizing bit index %d to %d.\n", left, right);
|
||||
left = right;
|
||||
} else
|
||||
error("invalid slice [%d:%d]", left, right);
|
||||
}
|
||||
if (flag_legalize && left >= sig.size())
|
||||
log("Legalizing slice %d:%d by igoring it\n", left, right);
|
||||
else
|
||||
sig = sig.extract(right, left - right + 1);
|
||||
} else {
|
||||
sig = sig.extract(left);
|
||||
if (flag_legalize && left >= sig.size())
|
||||
log("Legalizing slice %d by igoring it\n", left);
|
||||
else
|
||||
sig = sig.extract(left);
|
||||
}
|
||||
expect_char(']');
|
||||
}
|
||||
|
|
@ -476,8 +519,14 @@ struct RTLILFrontendWorker {
|
|||
{
|
||||
std::optional<RTLIL::IdString> id = try_parse_id();
|
||||
if (id.has_value()) {
|
||||
if (current_module->wire(*id) != nullptr)
|
||||
error("RTLIL error: redefinition of wire %s.", *id);
|
||||
if (current_module->wire(*id) != nullptr) {
|
||||
if (flag_legalize) {
|
||||
log("Legalizing redefinition of wire %s.\n", *id);
|
||||
pool<RTLIL::Wire*> wires = {current_module->wire(*id)};
|
||||
current_module->remove(wires);
|
||||
} else
|
||||
error("RTLIL error: redefinition of wire %s.", *id);
|
||||
}
|
||||
wire = current_module->addWire(std::move(*id));
|
||||
break;
|
||||
}
|
||||
|
|
@ -528,8 +577,13 @@ struct RTLILFrontendWorker {
|
|||
{
|
||||
std::optional<RTLIL::IdString> id = try_parse_id();
|
||||
if (id.has_value()) {
|
||||
if (current_module->memories.count(*id) != 0)
|
||||
error("RTLIL error: redefinition of memory %s.", *id);
|
||||
if (current_module->memories.count(*id) != 0) {
|
||||
if (flag_legalize) {
|
||||
log("Legalizing redefinition of memory %s.\n", *id);
|
||||
current_module->remove(current_module->memories.at(*id));
|
||||
} else
|
||||
error("RTLIL error: redefinition of memory %s.", *id);
|
||||
}
|
||||
memory->name = std::move(*id);
|
||||
break;
|
||||
}
|
||||
|
|
@ -551,14 +605,36 @@ struct RTLILFrontendWorker {
|
|||
expect_eol();
|
||||
}
|
||||
|
||||
void legalize_width_parameter(RTLIL::Cell *cell, RTLIL::IdString port_name)
|
||||
{
|
||||
std::string width_param_name = port_name.str() + "_WIDTH";
|
||||
if (cell->parameters.count(width_param_name) == 0)
|
||||
return;
|
||||
RTLIL::Const ¶m = cell->parameters.at(width_param_name);
|
||||
if (param.as_int() != 0)
|
||||
return;
|
||||
cell->parameters[width_param_name] = RTLIL::Const(cell->getPort(port_name).size());
|
||||
}
|
||||
|
||||
void parse_cell()
|
||||
{
|
||||
RTLIL::IdString cell_type = parse_id();
|
||||
RTLIL::IdString cell_name = parse_id();
|
||||
expect_eol();
|
||||
|
||||
if (current_module->cell(cell_name) != nullptr)
|
||||
error("RTLIL error: redefinition of cell %s.", cell_name);
|
||||
if (current_module->cell(cell_name) != nullptr) {
|
||||
if (flag_legalize) {
|
||||
RTLIL::IdString new_name;
|
||||
int suffix = 1;
|
||||
do {
|
||||
new_name = RTLIL::IdString(cell_name.str() + "_" + std::to_string(suffix));
|
||||
++suffix;
|
||||
} while (current_module->cell(new_name) != nullptr);
|
||||
log("Legalizing redefinition of cell %s by renaming to %s.\n", cell_name, new_name);
|
||||
cell_name = new_name;
|
||||
} else
|
||||
error("RTLIL error: redefinition of cell %s.", cell_name);
|
||||
}
|
||||
RTLIL::Cell *cell = current_module->addCell(cell_name, cell_type);
|
||||
cell->attributes = std::move(attrbuf);
|
||||
|
||||
|
|
@ -587,9 +663,15 @@ struct RTLILFrontendWorker {
|
|||
expect_eol();
|
||||
} else if (try_parse_keyword("connect")) {
|
||||
RTLIL::IdString port_name = parse_id();
|
||||
if (cell->hasPort(port_name))
|
||||
error("RTLIL error: redefinition of cell port %s.", port_name);
|
||||
if (cell->hasPort(port_name)) {
|
||||
if (flag_legalize)
|
||||
log("Legalizing redefinition of cell port %s.", port_name);
|
||||
else
|
||||
error("RTLIL error: redefinition of cell port %s.", port_name);
|
||||
}
|
||||
cell->setPort(std::move(port_name), parse_sigspec());
|
||||
if (flag_legalize)
|
||||
legalize_width_parameter(cell, port_name);
|
||||
expect_eol();
|
||||
} else if (try_parse_keyword("end")) {
|
||||
expect_eol();
|
||||
|
|
@ -606,6 +688,11 @@ struct RTLILFrontendWorker {
|
|||
error("dangling attribute");
|
||||
RTLIL::SigSpec s1 = parse_sigspec();
|
||||
RTLIL::SigSpec s2 = parse_sigspec();
|
||||
if (flag_legalize) {
|
||||
int min_size = std::min(s1.size(), s2.size());
|
||||
s1 = s1.extract(0, min_size);
|
||||
s2 = s2.extract(0, min_size);
|
||||
}
|
||||
current_module->connect(std::move(s1), std::move(s2));
|
||||
expect_eol();
|
||||
}
|
||||
|
|
@ -682,8 +769,13 @@ struct RTLILFrontendWorker {
|
|||
RTLIL::IdString proc_name = parse_id();
|
||||
expect_eol();
|
||||
|
||||
if (current_module->processes.count(proc_name) != 0)
|
||||
error("RTLIL error: redefinition of process %s.", proc_name);
|
||||
if (current_module->processes.count(proc_name) != 0) {
|
||||
if (flag_legalize) {
|
||||
log("Legalizing redefinition of process %s.\n", proc_name);
|
||||
current_module->remove(current_module->processes.at(proc_name));
|
||||
} else
|
||||
error("RTLIL error: redefinition of process %s.", proc_name);
|
||||
}
|
||||
RTLIL::Process *proc = current_module->addProcess(std::move(proc_name));
|
||||
proc->attributes = std::move(attrbuf);
|
||||
|
||||
|
|
@ -804,6 +896,11 @@ struct RTLILFrontend : public Frontend {
|
|||
log(" -lib\n");
|
||||
log(" only create empty blackbox modules\n");
|
||||
log("\n");
|
||||
log(" -legalize\n");
|
||||
log(" prevent semantic errors (e.g. reference to unknown wire, redefinition of wire/cell)\n");
|
||||
log(" by deterministically rewriting the input into something valid. Useful when using\n");
|
||||
log(" fuzzing to generate random but valid RTLIL.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
|
|
@ -828,6 +925,10 @@ struct RTLILFrontend : public Frontend {
|
|||
worker.flag_lib = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-legalize") {
|
||||
worker.flag_legalize = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
|
|
|||
|
|
@ -3047,6 +3047,13 @@ void RTLIL::Module::remove(RTLIL::Cell *cell)
|
|||
}
|
||||
}
|
||||
|
||||
void RTLIL::Module::remove(RTLIL::Memory *memory)
|
||||
{
|
||||
log_assert(memories.count(memory->name) != 0);
|
||||
memories.erase(memory->name);
|
||||
delete memory;
|
||||
}
|
||||
|
||||
void RTLIL::Module::remove(RTLIL::Process *process)
|
||||
{
|
||||
log_assert(processes.count(process->name) != 0);
|
||||
|
|
|
|||
|
|
@ -223,8 +223,8 @@ struct RTLIL::IdString
|
|||
|
||||
constexpr inline IdString() : index_(0) { }
|
||||
inline IdString(const char *str) : index_(insert(std::string_view(str))) { }
|
||||
constexpr inline IdString(const IdString &str) : index_(str.index_) { }
|
||||
inline IdString(IdString &&str) : index_(str.index_) { str.index_ = 0; }
|
||||
constexpr IdString(const IdString &str) = default;
|
||||
IdString(IdString &&str) = default;
|
||||
inline IdString(const std::string &str) : index_(insert(std::string_view(str))) { }
|
||||
inline IdString(std::string_view str) : index_(insert(str)) { }
|
||||
constexpr inline IdString(StaticId id) : index_(static_cast<short>(id)) {}
|
||||
|
|
@ -2148,6 +2148,8 @@ public:
|
|||
}
|
||||
|
||||
RTLIL::ObjRange<RTLIL::Wire*> wires() { return RTLIL::ObjRange<RTLIL::Wire*>(&wires_, &refcount_wires_); }
|
||||
int wires_size() const { return wires_.size(); }
|
||||
RTLIL::Wire* wire_at(int index) const { return wires_.element(index)->second; }
|
||||
RTLIL::ObjRange<RTLIL::Cell*> cells() { return RTLIL::ObjRange<RTLIL::Cell*>(&cells_, &refcount_cells_); }
|
||||
|
||||
void add(RTLIL::Binding *binding);
|
||||
|
|
@ -2155,6 +2157,7 @@ public:
|
|||
// Removing wires is expensive. If you have to remove wires, remove them all at once.
|
||||
void remove(const pool<RTLIL::Wire*> &wires);
|
||||
void remove(RTLIL::Cell *cell);
|
||||
void remove(RTLIL::Memory *memory);
|
||||
void remove(RTLIL::Process *process);
|
||||
|
||||
void rename(RTLIL::Wire *wire, RTLIL::IdString new_name);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ endif
|
|||
OBJS += passes/cmds/add.o
|
||||
OBJS += passes/cmds/delete.o
|
||||
OBJS += passes/cmds/design.o
|
||||
OBJS += passes/cmds/design_equal.o
|
||||
OBJS += passes/cmds/select.o
|
||||
OBJS += passes/cmds/show.o
|
||||
OBJS += passes/cmds/viz.o
|
||||
|
|
|
|||
|
|
@ -0,0 +1,352 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
*
|
||||
* 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/rtlil.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
class ModuleComparator
|
||||
{
|
||||
RTLIL::Module *mod_a;
|
||||
RTLIL::Module *mod_b;
|
||||
|
||||
public:
|
||||
ModuleComparator(RTLIL::Module *mod_a, RTLIL::Module *mod_b) : mod_a(mod_a), mod_b(mod_b) {}
|
||||
|
||||
bool compare_sigbit(const RTLIL::SigBit &a, const RTLIL::SigBit &b)
|
||||
{
|
||||
if (a.wire == nullptr && b.wire == nullptr)
|
||||
return a.data == b.data;
|
||||
if (a.wire != nullptr && b.wire != nullptr)
|
||||
return a.wire->name == b.wire->name && a.offset == b.offset;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool compare_sigspec(const RTLIL::SigSpec &a, const RTLIL::SigSpec &b)
|
||||
{
|
||||
if (a.size() != b.size()) return false;
|
||||
auto it_a = a.begin(), it_b = b.begin();
|
||||
for (; it_a != a.end(); ++it_a, ++it_b) {
|
||||
if (!compare_sigbit(*it_a, *it_b)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string compare_attributes(const RTLIL::AttrObject *a, const RTLIL::AttrObject *b)
|
||||
{
|
||||
for (const auto &it : a->attributes) {
|
||||
if (b->attributes.count(it.first) == 0)
|
||||
return "missing attribute " + std::string(log_id(it.first)) + " in second design";
|
||||
if (it.second != b->attributes.at(it.first))
|
||||
return "attribute " + std::string(log_id(it.first)) + " mismatch: " + log_const(it.second) + " != " + log_const(b->attributes.at(it.first));
|
||||
}
|
||||
for (const auto &it : b->attributes)
|
||||
if (a->attributes.count(it.first) == 0)
|
||||
return "missing attribute " + std::string(log_id(it.first)) + " in first design";
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string compare_wires(const RTLIL::Wire *a, const RTLIL::Wire *b)
|
||||
{
|
||||
if (a->name != b->name)
|
||||
return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
|
||||
if (a->width != b->width)
|
||||
return "width mismatch: " + std::to_string(a->width) + " != " + std::to_string(b->width);
|
||||
if (a->start_offset != b->start_offset)
|
||||
return "start_offset mismatch: " + std::to_string(a->start_offset) + " != " + std::to_string(b->start_offset);
|
||||
if (a->port_id != b->port_id)
|
||||
return "port_id mismatch: " + std::to_string(a->port_id) + " != " + std::to_string(b->port_id);
|
||||
if (a->port_input != b->port_input)
|
||||
return "port_input mismatch: " + std::to_string(a->port_input) + " != " + std::to_string(b->port_input);
|
||||
if (a->port_output != b->port_output)
|
||||
return "port_output mismatch: " + std::to_string(a->port_output) + " != " + std::to_string(b->port_output);
|
||||
if (a->upto != b->upto)
|
||||
return "upto mismatch: " + std::to_string(a->upto) + " != " + std::to_string(b->upto);
|
||||
if (a->is_signed != b->is_signed)
|
||||
return "is_signed mismatch: " + std::to_string(a->is_signed) + " != " + std::to_string(b->is_signed);
|
||||
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
|
||||
return mismatch;
|
||||
return "";
|
||||
}
|
||||
|
||||
void check_wires()
|
||||
{
|
||||
for (const auto &it : mod_a->wires_) {
|
||||
if (mod_b->wires_.count(it.first) == 0)
|
||||
log_error("Module %s missing wire %s in second design.\n", log_id(mod_a->name), log_id(it.first));
|
||||
if (std::string mismatch = compare_wires(it.second, mod_b->wires_.at(it.first)); !mismatch.empty())
|
||||
log_error("Module %s wire %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch);
|
||||
}
|
||||
for (const auto &it : mod_b->wires_)
|
||||
if (mod_a->wires_.count(it.first) == 0)
|
||||
log_error("Module %s missing wire %s in first design.\n", log_id(mod_b->name), log_id(it.first));
|
||||
}
|
||||
|
||||
std::string compare_memories(const RTLIL::Memory *a, const RTLIL::Memory *b)
|
||||
{
|
||||
if (a->name != b->name)
|
||||
return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
|
||||
if (a->width != b->width)
|
||||
return "width mismatch: " + std::to_string(a->width) + " != " + std::to_string(b->width);
|
||||
if (a->start_offset != b->start_offset)
|
||||
return "start_offset mismatch: " + std::to_string(a->start_offset) + " != " + std::to_string(b->start_offset);
|
||||
if (a->size != b->size)
|
||||
return "size mismatch: " + std::to_string(a->size) + " != " + std::to_string(b->size);
|
||||
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
|
||||
return mismatch;
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string compare_cells(const RTLIL::Cell *a, const RTLIL::Cell *b)
|
||||
{
|
||||
if (a->name != b->name)
|
||||
return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
|
||||
if (a->type != b->type)
|
||||
return "type mismatch: " + std::string(log_id(a->type)) + " != " + log_id(b->type);
|
||||
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
|
||||
return mismatch;
|
||||
|
||||
for (const auto &it : a->parameters) {
|
||||
if (b->parameters.count(it.first) == 0)
|
||||
return "parameter mismatch: missing parameter " + std::string(log_id(it.first)) + " in second design";
|
||||
if (it.second != b->parameters.at(it.first))
|
||||
return "parameter mismatch: " + std::string(log_id(it.first)) + " mismatch: " + log_const(it.second) + " != " + log_const(b->parameters.at(it.first));
|
||||
}
|
||||
for (const auto &it : b->parameters)
|
||||
if (a->parameters.count(it.first) == 0)
|
||||
return "parameter mismatch: missing parameter " + std::string(log_id(it.first)) + " in first design";
|
||||
|
||||
for (const auto &it : a->connections()) {
|
||||
if (b->connections().count(it.first) == 0)
|
||||
return "connection mismatch: missing connection " + std::string(log_id(it.first)) + " in second design";
|
||||
if (!compare_sigspec(it.second, b->connections().at(it.first)))
|
||||
return "connection " + std::string(log_id(it.first)) + " mismatch: " + log_signal(it.second) + " != " + log_signal(b->connections().at(it.first));
|
||||
}
|
||||
for (const auto &it : b->connections())
|
||||
if (a->connections().count(it.first) == 0)
|
||||
return "connection mismatch: missing connection " + std::string(log_id(it.first)) + " in first design";
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void check_cells()
|
||||
{
|
||||
for (const auto &it : mod_a->cells_) {
|
||||
if (mod_b->cells_.count(it.first) == 0)
|
||||
log_error("Module %s missing cell %s in second design.\n", log_id(mod_a->name), log_id(it.first));
|
||||
if (std::string mismatch = compare_cells(it.second, mod_b->cells_.at(it.first)); !mismatch.empty())
|
||||
log_error("Module %s cell %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch);
|
||||
}
|
||||
for (const auto &it : mod_b->cells_)
|
||||
if (mod_a->cells_.count(it.first) == 0)
|
||||
log_error("Module %s missing cell %s in first design.\n", log_id(mod_b->name), log_id(it.first));
|
||||
}
|
||||
|
||||
void check_memories()
|
||||
{
|
||||
for (const auto &it : mod_a->memories) {
|
||||
if (mod_b->memories.count(it.first) == 0)
|
||||
log_error("Module %s missing memory %s in second design.\n", log_id(mod_a->name), log_id(it.first));
|
||||
if (std::string mismatch = compare_memories(it.second, mod_b->memories.at(it.first)); !mismatch.empty())
|
||||
log_error("Module %s memory %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch);
|
||||
}
|
||||
for (const auto &it : mod_b->memories)
|
||||
if (mod_a->memories.count(it.first) == 0)
|
||||
log_error("Module %s missing memory %s in first design.\n", log_id(mod_b->name), log_id(it.first));
|
||||
}
|
||||
|
||||
std::string compare_case_rules(const RTLIL::CaseRule *a, const RTLIL::CaseRule *b)
|
||||
{
|
||||
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty()) return mismatch;
|
||||
|
||||
if (a->compare.size() != b->compare.size())
|
||||
return "compare size mismatch: " + std::to_string(a->compare.size()) + " != " + std::to_string(b->compare.size());
|
||||
for (size_t i = 0; i < a->compare.size(); i++)
|
||||
if (!compare_sigspec(a->compare[i], b->compare[i]))
|
||||
return "compare " + std::to_string(i) + " mismatch: " + log_signal(a->compare[i]) + " != " + log_signal(b->compare[i]);
|
||||
|
||||
if (a->actions.size() != b->actions.size())
|
||||
return "actions size mismatch: " + std::to_string(a->actions.size()) + " != " + std::to_string(b->actions.size());
|
||||
for (size_t i = 0; i < a->actions.size(); i++) {
|
||||
if (!compare_sigspec(a->actions[i].first, b->actions[i].first))
|
||||
return "action " + std::to_string(i) + " first mismatch: " + log_signal(a->actions[i].first) + " != " + log_signal(b->actions[i].first);
|
||||
if (!compare_sigspec(a->actions[i].second, b->actions[i].second))
|
||||
return "action " + std::to_string(i) + " second mismatch: " + log_signal(a->actions[i].second) + " != " + log_signal(b->actions[i].second);
|
||||
}
|
||||
|
||||
if (a->switches.size() != b->switches.size())
|
||||
return "switches size mismatch: " + std::to_string(a->switches.size()) + " != " + std::to_string(b->switches.size());
|
||||
for (size_t i = 0; i < a->switches.size(); i++)
|
||||
if (std::string mismatch = compare_switch_rules(a->switches[i], b->switches[i]); !mismatch.empty())
|
||||
return "switch " + std::to_string(i) + " " + mismatch;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string compare_switch_rules(const RTLIL::SwitchRule *a, const RTLIL::SwitchRule *b)
|
||||
{
|
||||
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
|
||||
return mismatch;
|
||||
if (!compare_sigspec(a->signal, b->signal))
|
||||
return "signal mismatch: " + log_signal(a->signal) + " != " + log_signal(b->signal);
|
||||
|
||||
if (a->cases.size() != b->cases.size())
|
||||
return "cases size mismatch: " + std::to_string(a->cases.size()) + " != " + std::to_string(b->cases.size());
|
||||
for (size_t i = 0; i < a->cases.size(); i++)
|
||||
if (std::string mismatch = compare_case_rules(a->cases[i], b->cases[i]); !mismatch.empty())
|
||||
return "case " + std::to_string(i) + " " + mismatch;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string compare_sync_rules(const RTLIL::SyncRule *a, const RTLIL::SyncRule *b)
|
||||
{
|
||||
if (a->type != b->type)
|
||||
return "type mismatch: " + std::to_string(a->type) + " != " + std::to_string(b->type);
|
||||
if (!compare_sigspec(a->signal, b->signal))
|
||||
return "signal mismatch: " + log_signal(a->signal) + " != " + log_signal(b->signal);
|
||||
if (a->actions.size() != b->actions.size())
|
||||
return "actions size mismatch: " + std::to_string(a->actions.size()) + " != " + std::to_string(b->actions.size());
|
||||
for (size_t i = 0; i < a->actions.size(); i++) {
|
||||
if (!compare_sigspec(a->actions[i].first, b->actions[i].first))
|
||||
return "action " + std::to_string(i) + " first mismatch: " + log_signal(a->actions[i].first) + " != " + log_signal(b->actions[i].first);
|
||||
if (!compare_sigspec(a->actions[i].second, b->actions[i].second))
|
||||
return "action " + std::to_string(i) + " second mismatch: " + log_signal(a->actions[i].second) + " != " + log_signal(b->actions[i].second);
|
||||
}
|
||||
if (a->mem_write_actions.size() != b->mem_write_actions.size())
|
||||
return "mem_write_actions size mismatch: " + std::to_string(a->mem_write_actions.size()) + " != " + std::to_string(b->mem_write_actions.size());
|
||||
for (size_t i = 0; i < a->mem_write_actions.size(); i++) {
|
||||
const auto &ma = a->mem_write_actions[i];
|
||||
const auto &mb = b->mem_write_actions[i];
|
||||
if (ma.memid != mb.memid)
|
||||
return "mem_write_actions " + std::to_string(i) + " memid mismatch: " + log_id(ma.memid) + " != " + log_id(mb.memid);
|
||||
if (!compare_sigspec(ma.address, mb.address))
|
||||
return "mem_write_actions " + std::to_string(i) + " address mismatch: " + log_signal(ma.address) + " != " + log_signal(mb.address);
|
||||
if (!compare_sigspec(ma.data, mb.data))
|
||||
return "mem_write_actions " + std::to_string(i) + " data mismatch: " + log_signal(ma.data) + " != " + log_signal(mb.data);
|
||||
if (!compare_sigspec(ma.enable, mb.enable))
|
||||
return "mem_write_actions " + std::to_string(i) + " enable mismatch: " + log_signal(ma.enable) + " != " + log_signal(mb.enable);
|
||||
if (ma.priority_mask != mb.priority_mask)
|
||||
return "mem_write_actions " + std::to_string(i) + " priority_mask mismatch: " + log_const(ma.priority_mask) + " != " + log_const(mb.priority_mask);
|
||||
if (std::string mismatch = compare_attributes(&ma, &mb); !mismatch.empty())
|
||||
return "mem_write_actions " + std::to_string(i) + " " + mismatch;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string compare_processes(const RTLIL::Process *a, const RTLIL::Process *b)
|
||||
{
|
||||
if (a->name != b->name) return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
|
||||
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
|
||||
return mismatch;
|
||||
if (std::string mismatch = compare_case_rules(&a->root_case, &b->root_case); !mismatch.empty())
|
||||
return "case rule " + mismatch;
|
||||
if (a->syncs.size() != b->syncs.size())
|
||||
return "sync count mismatch: " + std::to_string(a->syncs.size()) + " != " + std::to_string(b->syncs.size());
|
||||
for (size_t i = 0; i < a->syncs.size(); i++)
|
||||
if (std::string mismatch = compare_sync_rules(a->syncs[i], b->syncs[i]); !mismatch.empty())
|
||||
return "sync " + std::to_string(i) + " " + mismatch;
|
||||
return "";
|
||||
}
|
||||
|
||||
void check_processes()
|
||||
{
|
||||
for (auto &it : mod_a->processes) {
|
||||
if (mod_b->processes.count(it.first) == 0)
|
||||
log_error("Module %s missing process %s in second design.\n", log_id(mod_a->name), log_id(it.first));
|
||||
if (std::string mismatch = compare_processes(it.second, mod_b->processes.at(it.first)); !mismatch.empty())
|
||||
log_error("Module %s process %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch.c_str());
|
||||
}
|
||||
for (auto &it : mod_b->processes)
|
||||
if (mod_a->processes.count(it.first) == 0)
|
||||
log_error("Module %s missing process %s in first design.\n", log_id(mod_b->name), log_id(it.first));
|
||||
}
|
||||
|
||||
void check_connections()
|
||||
{
|
||||
const auto &conns_a = mod_a->connections();
|
||||
const auto &conns_b = mod_b->connections();
|
||||
if (conns_a.size() != conns_b.size()) {
|
||||
log_error("Module %s connection count differs: %zu != %zu\n", log_id(mod_a->name), conns_a.size(), conns_b.size());
|
||||
} else {
|
||||
for (size_t i = 0; i < conns_a.size(); i++) {
|
||||
if (!compare_sigspec(conns_a[i].first, conns_b[i].first))
|
||||
log_error("Module %s connection %zu LHS %s != %s.\n", log_id(mod_a->name), i, log_signal(conns_a[i].first), log_signal(conns_b[i].first));
|
||||
if (!compare_sigspec(conns_a[i].second, conns_b[i].second))
|
||||
log_error("Module %s connection %zu RHS %s != %s.\n", log_id(mod_a->name), i, log_signal(conns_a[i].second), log_signal(conns_b[i].second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check()
|
||||
{
|
||||
if (mod_a->name != mod_b->name)
|
||||
log_error("Modules have different names: %s != %s\n", log_id(mod_a->name), log_id(mod_b->name));
|
||||
if (std::string mismatch = compare_attributes(mod_a, mod_b); !mismatch.empty())
|
||||
log_error("Module %s %s.\n", log_id(mod_a->name), mismatch);
|
||||
check_wires();
|
||||
check_cells();
|
||||
check_memories();
|
||||
check_connections();
|
||||
check_processes();
|
||||
}
|
||||
};
|
||||
|
||||
struct DesignEqualPass : public Pass {
|
||||
DesignEqualPass() : Pass("design_equal", "check if two designs are the same") { }
|
||||
void help() override
|
||||
{
|
||||
log("\n");
|
||||
log(" design_equal <name>\n");
|
||||
log("\n");
|
||||
log("Compare the current design with the design previously saved under the given\n");
|
||||
log("name. Abort with an error if the designs are different.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
if (args.size() != 2)
|
||||
log_cmd_error("Missing argument.\n");
|
||||
|
||||
std::string check_name = args[1];
|
||||
if (saved_designs.count(check_name) == 0)
|
||||
log_cmd_error("No saved design '%s' found!\n", check_name.c_str());
|
||||
|
||||
RTLIL::Design *other = saved_designs.at(check_name);
|
||||
|
||||
for (auto &it : design->modules_) {
|
||||
RTLIL::Module *mod = it.second;
|
||||
if (!other->has(mod->name))
|
||||
log_error("Second design missing module %s.\n", log_id(mod->name));
|
||||
|
||||
ModuleComparator cmp(mod, other->module(mod->name));
|
||||
cmp.check();
|
||||
}
|
||||
for (auto &it : other->modules_) {
|
||||
RTLIL::Module *mod = it.second;
|
||||
if (!design->has(mod->name))
|
||||
log_error("First design missing module %s.\n", log_id(mod->name));
|
||||
}
|
||||
|
||||
log("Designs are identical.\n");
|
||||
}
|
||||
} DesignEqualPass;
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
@ -289,6 +289,7 @@ struct OptMergeWorker
|
|||
CellPtrHash,
|
||||
CellPtrEqual> known_cells (0, CellPtrHash(*this), CellPtrEqual(*this));
|
||||
|
||||
std::vector<RTLIL::SigSig> redirects;
|
||||
for (auto cell : cells)
|
||||
{
|
||||
auto [cell_in_map, inserted] = known_cells.insert(cell);
|
||||
|
|
@ -310,12 +311,7 @@ struct OptMergeWorker
|
|||
RTLIL::SigSpec other_sig = other_cell->getPort(it.first);
|
||||
log_debug(" Redirecting output %s: %s = %s\n", it.first,
|
||||
log_signal(it.second), log_signal(other_sig));
|
||||
Const init = initvals(other_sig);
|
||||
initvals.remove_init(it.second);
|
||||
initvals.remove_init(other_sig);
|
||||
module->connect(RTLIL::SigSig(it.second, other_sig));
|
||||
assign_map.add(it.second, other_sig);
|
||||
initvals.set_init(other_sig, init);
|
||||
redirects.push_back(RTLIL::SigSig(it.second, std::move(other_sig)));
|
||||
}
|
||||
}
|
||||
log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type, cell->name, module->name);
|
||||
|
|
@ -323,6 +319,14 @@ struct OptMergeWorker
|
|||
total_count++;
|
||||
}
|
||||
}
|
||||
for (const RTLIL::SigSig &redirect : redirects) {
|
||||
module->connect(redirect);
|
||||
Const init = initvals(redirect.second);
|
||||
initvals.remove_init(redirect.first);
|
||||
initvals.remove_init(redirect.second);
|
||||
assign_map.add(redirect.first, redirect.second);
|
||||
initvals.set_init(redirect.second, init);
|
||||
}
|
||||
}
|
||||
|
||||
log_suppressed();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ ram block $__GOWIN_SP_ {
|
|||
abits 14;
|
||||
widths 1 2 4 9 18 36 per_port;
|
||||
cost 128;
|
||||
byte 9;
|
||||
init no_undef;
|
||||
port srsw "A" {
|
||||
clock posedge;
|
||||
|
|
@ -24,6 +25,7 @@ ram block $__GOWIN_SP_ {
|
|||
rdwr old;
|
||||
}
|
||||
}
|
||||
wrbe_separate;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -31,6 +33,7 @@ ram block $__GOWIN_DP_ {
|
|||
abits 14;
|
||||
widths 1 2 4 9 18 per_port;
|
||||
cost 128;
|
||||
byte 9;
|
||||
init no_undef;
|
||||
port srsw "A" "B" {
|
||||
clock posedge;
|
||||
|
|
@ -53,6 +56,7 @@ ram block $__GOWIN_DP_ {
|
|||
rdwr old;
|
||||
}
|
||||
}
|
||||
wrbe_separate;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -60,6 +64,7 @@ ram block $__GOWIN_SDP_ {
|
|||
abits 14;
|
||||
widths 1 2 4 9 18 36 per_port;
|
||||
cost 128;
|
||||
byte 9;
|
||||
init no_undef;
|
||||
port sr "R" {
|
||||
clock posedge;
|
||||
|
|
@ -75,5 +80,6 @@ ram block $__GOWIN_SDP_ {
|
|||
port sw "W" {
|
||||
clock posedge;
|
||||
clken;
|
||||
wrbe_separate;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
`define x8_width(width) (width / 9 * 8 + width % 9)
|
||||
`define x8_rd_data(data) {1'bx, data[31:24], 1'bx, data[23:16], 1'bx, data[15:8], 1'bx, data[7:0]}
|
||||
`define x8_wr_data(data) {data[34:27], data[25:18], data[16:9], data[7:0]}
|
||||
`define addrbe_always(width, addr) (width < 18 ? addr : width == 18 ? {addr[13:4], 4'b0011} : {addr[13:5], 5'b01111})
|
||||
`define addrbe(width, addr, w_be) (width < 18 ? addr : width == 18 ? {addr[13:4], 2'b00, w_be[1:0]} : {addr[13:5], 1'b0, w_be[3:0]})
|
||||
|
||||
|
||||
`define INIT(func) \
|
||||
|
|
@ -90,6 +90,7 @@ parameter OPTION_RESET_MODE = "SYNC";
|
|||
|
||||
parameter PORT_A_WIDTH = 36;
|
||||
parameter PORT_A_OPTION_WRITE_MODE = 0;
|
||||
parameter PORT_A_WR_BE_WIDTH = 4;
|
||||
|
||||
input PORT_A_CLK;
|
||||
input PORT_A_CLK_EN;
|
||||
|
|
@ -97,13 +98,14 @@ input PORT_A_WR_EN;
|
|||
input PORT_A_RD_SRST;
|
||||
input PORT_A_RD_ARST;
|
||||
input [13:0] PORT_A_ADDR;
|
||||
input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE;
|
||||
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
|
||||
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
|
||||
|
||||
`DEF_FUNCS
|
||||
|
||||
wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST;
|
||||
wire [13:0] AD = `addrbe_always(PORT_A_WIDTH, PORT_A_ADDR);
|
||||
wire [13:0] AD = `addrbe(PORT_A_WIDTH, PORT_A_ADDR, PORT_A_WR_BE);
|
||||
|
||||
generate
|
||||
|
||||
|
|
@ -173,9 +175,11 @@ parameter OPTION_RESET_MODE = "SYNC";
|
|||
|
||||
parameter PORT_A_WIDTH = 18;
|
||||
parameter PORT_A_OPTION_WRITE_MODE = 0;
|
||||
parameter PORT_A_WR_BE_WIDTH = 4;
|
||||
|
||||
parameter PORT_B_WIDTH = 18;
|
||||
parameter PORT_B_OPTION_WRITE_MODE = 0;
|
||||
parameter PORT_B_WR_BE_WIDTH = 4;
|
||||
|
||||
input PORT_A_CLK;
|
||||
input PORT_A_CLK_EN;
|
||||
|
|
@ -183,6 +187,7 @@ input PORT_A_WR_EN;
|
|||
input PORT_A_RD_SRST;
|
||||
input PORT_A_RD_ARST;
|
||||
input [13:0] PORT_A_ADDR;
|
||||
input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE;
|
||||
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
|
||||
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
|
||||
|
||||
|
|
@ -192,6 +197,7 @@ input PORT_B_WR_EN;
|
|||
input PORT_B_RD_SRST;
|
||||
input PORT_B_RD_ARST;
|
||||
input [13:0] PORT_B_ADDR;
|
||||
input [PORT_B_WR_BE_WIDTH-1:0] PORT_B_WR_BE;
|
||||
input [PORT_A_WIDTH-1:0] PORT_B_WR_DATA;
|
||||
output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA;
|
||||
|
||||
|
|
@ -199,8 +205,8 @@ output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA;
|
|||
|
||||
wire RSTA = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST;
|
||||
wire RSTB = OPTION_RESET_MODE == "SYNC" ? PORT_B_RD_SRST : PORT_B_RD_ARST;
|
||||
wire [13:0] ADA = `addrbe_always(PORT_A_WIDTH, PORT_A_ADDR);
|
||||
wire [13:0] ADB = `addrbe_always(PORT_B_WIDTH, PORT_B_ADDR);
|
||||
wire [13:0] ADA = `addrbe(PORT_A_WIDTH, PORT_A_ADDR, PORT_B_WR_BE);
|
||||
wire [13:0] ADB = `addrbe(PORT_B_WIDTH, PORT_B_ADDR, PORT_B_WR_BE);
|
||||
|
||||
generate
|
||||
|
||||
|
|
@ -306,6 +312,7 @@ parameter OPTION_RESET_MODE = "SYNC";
|
|||
|
||||
parameter PORT_R_WIDTH = 18;
|
||||
parameter PORT_W_WIDTH = 18;
|
||||
parameter PORT_W_WR_BE_WIDTH = 4;
|
||||
|
||||
input PORT_R_CLK;
|
||||
input PORT_R_CLK_EN;
|
||||
|
|
@ -318,12 +325,13 @@ input PORT_W_CLK;
|
|||
input PORT_W_CLK_EN;
|
||||
input PORT_W_WR_EN;
|
||||
input [13:0] PORT_W_ADDR;
|
||||
input [PORT_W_WR_BE_WIDTH-1:0] PORT_W_WR_BE;
|
||||
input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA;
|
||||
|
||||
`DEF_FUNCS
|
||||
|
||||
wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_R_RD_SRST : PORT_R_RD_ARST;
|
||||
wire [13:0] ADW = `addrbe_always(PORT_W_WIDTH, PORT_W_ADDR);
|
||||
wire [13:0] ADW = `addrbe(PORT_W_WIDTH, PORT_W_ADDR, PORT_W_WR_BE);
|
||||
wire WRE = PORT_W_CLK_EN & PORT_W_WR_EN;
|
||||
|
||||
generate
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
`define x8_width(width) (width / 9 * 8 + width % 9)
|
||||
`define x8_rd_data(data) {1'bx, data[31:24], 1'bx, data[23:16], 1'bx, data[15:8], 1'bx, data[7:0]}
|
||||
`define x8_wr_data(data) {data[34:27], data[25:18], data[16:9], data[7:0]}
|
||||
`define addrbe_always(width, addr) (width < 18 ? addr : width == 18 ? {addr[13:4], 4'b0011} : {addr[13:5], 5'b01111})
|
||||
`define addrbe(width, addr, w_be) (width < 18 ? addr : width == 18 ? {addr[13:4], 2'b00, w_be[1:0]} : {addr[13:5], 1'b0, w_be[3:0]})
|
||||
|
||||
|
||||
`define INIT(func) \
|
||||
|
|
@ -90,6 +90,7 @@ parameter OPTION_RESET_MODE = "SYNC";
|
|||
|
||||
parameter PORT_A_WIDTH = 36;
|
||||
parameter PORT_A_OPTION_WRITE_MODE = 0;
|
||||
parameter PORT_A_WR_BE_WIDTH = 4;
|
||||
|
||||
input PORT_A_CLK;
|
||||
input PORT_A_CLK_EN;
|
||||
|
|
@ -97,13 +98,14 @@ input PORT_A_WR_EN;
|
|||
input PORT_A_RD_SRST;
|
||||
input PORT_A_RD_ARST;
|
||||
input [13:0] PORT_A_ADDR;
|
||||
input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE;
|
||||
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
|
||||
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
|
||||
|
||||
`DEF_FUNCS
|
||||
|
||||
wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST;
|
||||
wire [13:0] AD = `addrbe_always(PORT_A_WIDTH, PORT_A_ADDR);
|
||||
wire [13:0] AD = `addrbe(PORT_A_WIDTH, PORT_A_ADDR, PORT_A_WR_BE);
|
||||
|
||||
generate
|
||||
|
||||
|
|
@ -173,9 +175,11 @@ parameter OPTION_RESET_MODE = "SYNC";
|
|||
|
||||
parameter PORT_A_WIDTH = 18;
|
||||
parameter PORT_A_OPTION_WRITE_MODE = 0;
|
||||
parameter PORT_A_WR_BE_WIDTH = 4;
|
||||
|
||||
parameter PORT_B_WIDTH = 18;
|
||||
parameter PORT_B_OPTION_WRITE_MODE = 0;
|
||||
parameter PORT_B_WR_BE_WIDTH = 4;
|
||||
|
||||
input PORT_A_CLK;
|
||||
input PORT_A_CLK_EN;
|
||||
|
|
@ -183,6 +187,7 @@ input PORT_A_WR_EN;
|
|||
input PORT_A_RD_SRST;
|
||||
input PORT_A_RD_ARST;
|
||||
input [13:0] PORT_A_ADDR;
|
||||
input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE;
|
||||
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
|
||||
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
|
||||
|
||||
|
|
@ -192,6 +197,7 @@ input PORT_B_WR_EN;
|
|||
input PORT_B_RD_SRST;
|
||||
input PORT_B_RD_ARST;
|
||||
input [13:0] PORT_B_ADDR;
|
||||
input [PORT_B_WR_BE_WIDTH-1:0] PORT_B_WR_BE;
|
||||
input [PORT_A_WIDTH-1:0] PORT_B_WR_DATA;
|
||||
output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA;
|
||||
|
||||
|
|
@ -199,8 +205,8 @@ output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA;
|
|||
|
||||
wire RSTA = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST;
|
||||
wire RSTB = OPTION_RESET_MODE == "SYNC" ? PORT_B_RD_SRST : PORT_B_RD_ARST;
|
||||
wire [13:0] ADA = `addrbe_always(PORT_A_WIDTH, PORT_A_ADDR);
|
||||
wire [13:0] ADB = `addrbe_always(PORT_B_WIDTH, PORT_B_ADDR);
|
||||
wire [13:0] ADA = `addrbe(PORT_A_WIDTH, PORT_A_ADDR, PORT_B_WR_BE);
|
||||
wire [13:0] ADB = `addrbe(PORT_B_WIDTH, PORT_B_ADDR, PORT_B_WR_BE);
|
||||
|
||||
generate
|
||||
|
||||
|
|
@ -306,6 +312,7 @@ parameter OPTION_RESET_MODE = "SYNC";
|
|||
|
||||
parameter PORT_R_WIDTH = 18;
|
||||
parameter PORT_W_WIDTH = 18;
|
||||
parameter PORT_W_WR_BE_WIDTH = 4;
|
||||
|
||||
input PORT_R_CLK;
|
||||
input PORT_R_CLK_EN;
|
||||
|
|
@ -318,12 +325,13 @@ input PORT_W_CLK;
|
|||
input PORT_W_CLK_EN;
|
||||
input PORT_W_WR_EN;
|
||||
input [13:0] PORT_W_ADDR;
|
||||
input [PORT_W_WR_BE_WIDTH-1:0] PORT_W_WR_BE;
|
||||
input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA;
|
||||
|
||||
`DEF_FUNCS
|
||||
|
||||
wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_R_RD_SRST : PORT_R_RD_ARST;
|
||||
wire [13:0] ADW = `addrbe_always(PORT_W_WIDTH, PORT_W_ADDR);
|
||||
wire [13:0] ADW = `addrbe(PORT_W_WIDTH, PORT_W_ADDR, PORT_W_WR_BE);
|
||||
wire WRE = PORT_W_CLK_EN & PORT_W_WR_EN;
|
||||
|
||||
generate
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
read_verilog -sv <<EOT
|
||||
module opt_expr_or_test(input [3:0] i, input [7:0] j, output [8:0] o);
|
||||
wire[8:0] a = 8'b0;
|
||||
wire[8:0] a;
|
||||
initial begin
|
||||
a = 8'b0;
|
||||
a |= i;
|
||||
a |= j;
|
||||
end
|
||||
assign o = a;
|
||||
assign o = a;
|
||||
endmodule
|
||||
EOT
|
||||
proc
|
||||
|
|
@ -17,12 +18,13 @@ select -assert-count 1 t:$or r:A_WIDTH=4 r:B_WIDTH=4 r:Y_WIDTH=4 %i %i %i
|
|||
design -reset
|
||||
read_verilog -sv <<EOT
|
||||
module opt_expr_add_test(input [3:0] i, input [7:0] j, output [8:0] o);
|
||||
wire[8:0] a = 8'b0;
|
||||
wire[8:0] a;
|
||||
initial begin
|
||||
a += i;
|
||||
a += j;
|
||||
a = 8'b0;
|
||||
a += i;
|
||||
a += j;
|
||||
end
|
||||
assign o = a;
|
||||
assign o = a;
|
||||
endmodule
|
||||
EOT
|
||||
proc
|
||||
|
|
@ -34,12 +36,13 @@ select -assert-count 1 t:$add r:A_WIDTH=9 r:B_WIDTH=8 r:Y_WIDTH=9 %i %i %i
|
|||
design -reset
|
||||
read_verilog -sv <<EOT
|
||||
module opt_expr_xor_test(input [3:0] i, input [7:0] j, output [8:0] o);
|
||||
wire[8:0] a = 8'b0;
|
||||
wire[8:0] a;
|
||||
initial begin
|
||||
a ^= i;
|
||||
a ^= j;
|
||||
a = 8'b0;
|
||||
a ^= i;
|
||||
a ^= j;
|
||||
end
|
||||
assign o = a;
|
||||
assign o = a;
|
||||
endmodule
|
||||
EOT
|
||||
proc
|
||||
|
|
@ -51,12 +54,13 @@ select -assert-count 1 t:$xor r:A_WIDTH=4 r:B_WIDTH=4 r:Y_WIDTH=4 %i %i %i
|
|||
design -reset
|
||||
read_verilog -sv <<EOT
|
||||
module opt_expr_sub_test(input [3:0] i, input [7:0] j, output [8:0] o);
|
||||
wire[8:0] a = 8'b0;
|
||||
wire[8:0] a;
|
||||
initial begin
|
||||
a -= i;
|
||||
a -= j;
|
||||
a = 8'b0;
|
||||
a -= i;
|
||||
a -= j;
|
||||
end
|
||||
assign o = a;
|
||||
assign o = a;
|
||||
endmodule
|
||||
EOT
|
||||
proc
|
||||
|
|
@ -68,12 +72,13 @@ select -assert-count 1 t:$sub r:A_WIDTH=9 r:B_WIDTH=8 r:Y_WIDTH=9 %i %i %i
|
|||
design -reset
|
||||
read_verilog -sv <<EOT
|
||||
module opt_expr_and_test(input [3:0] i, input [7:0] j, output [8:0] o);
|
||||
wire[8:0] a = 8'b11111111;
|
||||
wire[8:0] a;
|
||||
initial begin
|
||||
a &= i;
|
||||
a &= j;
|
||||
a = 8'b11111111;
|
||||
a &= i;
|
||||
a &= j;
|
||||
end
|
||||
assign o = a;
|
||||
assign o = a;
|
||||
endmodule
|
||||
EOT
|
||||
proc
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
Suppose you're making significant changes to a pass that should not change
|
||||
the pass's output in any way. It might be useful to run a large number of
|
||||
automatically generated tests to try to find bugs where the output has
|
||||
changed. This document describes how to do that.
|
||||
|
||||
Basically we're going to use [AFL++](https://github.com/AFLplusplus/AFLplusplus) with the
|
||||
[Grammar-Mutator](https://github.com/AFLplusplus/Grammar-Mutator) plugin to generate
|
||||
RTLIL testcases. For each testcase, we run a Yosys script that applies both the old and new
|
||||
implementation of the pass to the same design and compares the results. Testcase
|
||||
generation is coverage-guided, i.e. the fuzzer will try to find testcases that exercise all
|
||||
code in the old and new implementation of the pass (and in the RTLIL parser).
|
||||
|
||||
## Setup
|
||||
|
||||
These instructions clone tools into subdirectories of your home directory. They assume
|
||||
you have a Yosys checkout under `$HOME/yosys`, and that you're testing the `opt_merge` pass.
|
||||
They have been tested with AFL++ revision 68b492b2c7725816068718ef9437b72b40e67519 and Grammar-Mutator revision 05d8f537f8d656f0754e7ad5dcc653c42cb4f8ff.
|
||||
|
||||
Clone and build AFL++ and Grammar-Mutator:
|
||||
```
|
||||
cd $HOME
|
||||
git clone https://github.com/AFLplusplus/AFLplusplus.git
|
||||
git -C AFLplusplus checkout stable
|
||||
git clone https://github.com/AFLplusplus/Grammar-Mutator.git
|
||||
git -C Grammar-Mutator checkout stable
|
||||
```
|
||||
|
||||
Check that `rtlil-fuzz-grammar.json` generates RTLIL constructs relevant to your pass.
|
||||
Currently it's quite simple and generates a limited set of cells and wires; you may need to
|
||||
extend it to generate different kinds of cells and other RTLIL constructs (e.g. `proc`).
|
||||
|
||||
Build AFL++ and Grammar-Mutator:
|
||||
```
|
||||
make -C $HOME/AFLplusplus -j all
|
||||
make -C $HOME/Grammar-Mutator -j GRAMMAR_FILE=$HOME/yosys/tests/tools/rtlil-fuzz-grammar.json
|
||||
```
|
||||
|
||||
Create a Yosys commit that adds the old version of your pass as a new command, e.g. copy
|
||||
`opt_merge.cc` into `old_opt_merge.cc` and change the name of the command to `old_opt_merge`.
|
||||
[Here's](https://github.com/YosysHQ/yosys/commit/827cd8c998f3e455b14ac990a3159030ddc19b21) an example.
|
||||
|
||||
You may also need to patch in [this commit](https://github.com/YosysHQ/yosys/commit/121c52f514c4ca282b4e6b3b14f71184f3849ddf) to work around a bug involving `std::reverse` on
|
||||
empty vectors in the RTLIL parser when building with fuzzing instrumentation.
|
||||
I think this is a clang++ bug so hopefully it will get fixed eventually and that patch will not be
|
||||
necessary.
|
||||
|
||||
Rebuild Yosys with the AFL++ compiler wrapper. This assumes your config builds Yosys with clang++.
|
||||
```
|
||||
(cd $HOME/yosys; patch -lp1 << EOF)
|
||||
diff --git a/Makefile b/Makefile
|
||||
index 9c361294d..c9a98f74c 100644
|
||||
--- a/Makefile
|
||||
+++ b/Makefile
|
||||
@@ -238,7 +238,7 @@
|
||||
LTOFLAGS := $(GCC_LTO)
|
||||
|
||||
ifeq ($(CONFIG),clang)
|
||||
-CXX = clang++
|
||||
+CXX = $(HOME)/AFLplusplus/afl-c++
|
||||
CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL)
|
||||
ifeq ($(ENABLE_LTO),1)
|
||||
LINKFLAGS += -fuse-ld=lld
|
||||
EOF
|
||||
make -C yosys clean && make -C yosys -j
|
||||
```
|
||||
|
||||
You probably need to configure coredumps to work normally instead of going through some OS service:
|
||||
```
|
||||
echo core | sudo tee /proc/sys/kernel/core_pattern
|
||||
```
|
||||
|
||||
## Running the fuzzer
|
||||
|
||||
Generate some initial testcases using Grammar-Mutator:
|
||||
```
|
||||
(cd $HOME/Grammar-Mutator; rm -rf seeds trees; ./grammar_generator-rtlil 100 1000 ./seeds ./trees)
|
||||
```
|
||||
|
||||
Now run AFL++.
|
||||
```
|
||||
(cd $HOME/Grammar-Mutator; \
|
||||
AFL_CUSTOM_MUTATOR_LIBRARY=./libgrammarmutator-rtlil.so \
|
||||
AFL_CUSTOM_MUTATOR_ONLY=1 \
|
||||
AFL_BENCH_UNTIL_CRASH=1 \
|
||||
YOSYS_WORK_UNITS_PER_THREAD=1 \
|
||||
YOSYS_ABORT_ON_LOG_ERROR=1 \
|
||||
$HOME/AFLplusplus/afl-fuzz -t 5000 -m none -i seeds -o out -- \
|
||||
$HOME/yosys/yosys -p 'read_rtlil -legalize @@; design -save init; old_opt_merge; design -save old; design -load init; opt_merge; design_equal old' \
|
||||
)
|
||||
```
|
||||
This will run the fuzzer until the first crash (including any pass output mismatches) and then stop.
|
||||
Or if you're lucky, the fuzzer will run indefinitely. This uses very little parallelism; if it doesn't find any errors right away, you can increase the test throughput by running AFL++ in parallel using the instructions [here](https://aflplus.plus/docs/parallel_fuzzing).
|
||||
|
||||
## Working with fuzz test failures
|
||||
|
||||
Any failing testcases will be dropped in `$HOME/Grammar-Mutator/out/default/crashes`.
|
||||
Run `yosys -p 'read_rtlil -legalize ... ; dump'` to get the testcase as legalized RTLIL.
|
||||
|
||||
## Notes on generating semantically valid RTLIL
|
||||
|
||||
`Grammar-Mutator` generates RTLIL files according to the context-free grammar in `rtlil-fuzz-grammar.json`.
|
||||
However, the testcases must also be semantically valid, e.g. references to wires should only refer to
|
||||
wires that actually exist. These constraints cannot reasonably be expresed in a CFG. Therefore we
|
||||
have added a `-legalize` option to the `read_rtlil` command. When `-legalize` is set, when `read_rtlil`
|
||||
detects a failed semantic check, instead of erroring out it emits a warning and patches the incoming RTLIL
|
||||
to make it valid.
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
{
|
||||
"<MODULE>": [
|
||||
[
|
||||
"module \\test\n",
|
||||
"<WIRE>", "<WIRES>",
|
||||
"<CELLS>",
|
||||
"<CONNECTS>",
|
||||
"end\n"
|
||||
]
|
||||
],
|
||||
"<WIRE>": [ [ " wire width ", "<WIDTH>", " ", "<WIRE_MODE>", " ", "<WIRE_ID>", "\n" ] ],
|
||||
"<WIDTH>": [ [ "1" ], [ "2" ], [ "3" ], [ "4" ], [ "32" ], [ "128" ] ],
|
||||
"<WIRE_MODE>": [ [ "input ", "<PORT_ID>" ], [ "output ", "<PORT_ID>" ], [ "inout ", "<PORT_ID>" ], [] ],
|
||||
"<CELL>": [
|
||||
[
|
||||
" cell $not ", "<CELL_ID>", "\n",
|
||||
" parameter \\A_SIGNED 0\n",
|
||||
" parameter \\A_WIDTH 0\n",
|
||||
" parameter \\Y_WIDTH 0\n",
|
||||
" connect \\A ", "<SIGSPEC>", "\n",
|
||||
" connect \\Y ", "<SIGSPEC>", "\n",
|
||||
" end\n"
|
||||
],
|
||||
[
|
||||
" cell $and ", "<CELL_ID>", "\n",
|
||||
" parameter \\A_SIGNED 0\n",
|
||||
" parameter \\B_SIGNED 0\n",
|
||||
" parameter \\A_WIDTH 0\n",
|
||||
" parameter \\B_WIDTH 0\n",
|
||||
" parameter \\Y_WIDTH 0\n",
|
||||
" connect \\A ", "<SIGSPEC>", "\n",
|
||||
" connect \\B ", "<SIGSPEC>", "\n",
|
||||
" connect \\Y ", "<SIGSPEC>", "\n",
|
||||
" end\n"
|
||||
],
|
||||
[
|
||||
" cell $or ", "<CELL_ID>", "\n",
|
||||
" parameter \\A_SIGNED 0\n",
|
||||
" parameter \\B_SIGNED 0\n",
|
||||
" parameter \\A_WIDTH 0\n",
|
||||
" parameter \\B_WIDTH 0\n",
|
||||
" parameter \\Y_WIDTH 0\n",
|
||||
" connect \\A ", "<SIGSPEC>", "\n",
|
||||
" connect \\B ", "<SIGSPEC>", "\n",
|
||||
" connect \\Y ", "<SIGSPEC>", "\n",
|
||||
" end\n"
|
||||
],
|
||||
[
|
||||
" cell $xor ", "<CELL_ID>", "\n",
|
||||
" parameter \\A_SIGNED 0\n",
|
||||
" parameter \\B_SIGNED 0\n",
|
||||
" parameter \\A_WIDTH 0\n",
|
||||
" parameter \\B_WIDTH 0\n",
|
||||
" parameter \\Y_WIDTH 0\n",
|
||||
" connect \\A ", "<SIGSPEC>", "\n",
|
||||
" connect \\B ", "<SIGSPEC>", "\n",
|
||||
" connect \\Y ", "<SIGSPEC>", "\n",
|
||||
" end\n"
|
||||
],
|
||||
[
|
||||
" cell ", "<BLACKBOX_CELL>", " ", "<CELL_ID>", "\n",
|
||||
" connect \\A ", "<SIGSPEC>", "\n",
|
||||
" connect \\Y ", "<SIGSPEC>", "\n",
|
||||
" end\n"
|
||||
],
|
||||
[
|
||||
" cell ", "<BLACKBOX_CELL>", " ", "<CELL_ID>", "\n",
|
||||
" connect \\A ", "<SIGSPEC>", "\n",
|
||||
" connect \\B ", "<SIGSPEC>", "\n",
|
||||
" connect \\Y ", "<SIGSPEC>", "\n",
|
||||
" end\n"
|
||||
]
|
||||
],
|
||||
"<WIRE_ID>": [ [ "\\wire_a" ], [ "\\wire_b" ], [ "\\wire_c" ], [ "\\wire_d" ], [ "\\wire_e" ], [ "\\wire_f" ], [ "\\wire_g" ], [ "\\wire_h" ], [ "\\wire_i" ], [ "\\wire_j" ] ],
|
||||
"<CELL_ID>": [ [ "\\cell_a" ], [ "\\cell_b" ], [ "\\cell_c" ], [ "\\cell_d" ], [ "\\cell_e" ], [ "\\cell_f" ], [ "\\cell_g" ], [ "\\cell_h" ], [ "\\cell_i" ], [ "\\cell_j" ] ],
|
||||
"<BLACKBOX_CELL>": [ [ "\\bb1" ], [ "\\bb2" ] ],
|
||||
"<SIGSPEC>": [
|
||||
[ "<WIRE_ID>", " " ],
|
||||
[ "{", "<SIGSPEC>", " ", "<SIGSPECS>", "}" ],
|
||||
[ "<CONST>" ],
|
||||
[ "<SIGSPEC>", "[", "<BIT_OFFSET>", "]" ],
|
||||
[ "<SIGSPEC>", "[", "<BIT_OFFSET>", ":", "<BIT_OFFSET>", "]" ]
|
||||
],
|
||||
"<CONST>": [
|
||||
[ "0'", "<BITS>" ],
|
||||
[ "1'", "<BITS>" ],
|
||||
[ "2'", "<BITS>" ],
|
||||
[ "3'", "<BITS>" ],
|
||||
[ "4'", "<BITS>" ],
|
||||
[ "31'", "<BITS>" ],
|
||||
[ "32'", "<BITS>" ],
|
||||
[ "128'", "<BITS>" ]
|
||||
],
|
||||
"<BIT>": [ [ "0" ], [ "1" ], [ "x" ], [ "z" ], [ "-" ], [ "m" ] ],
|
||||
"<BIT_OFFSET>": [ "0", "1", "2", "3", "31", "32" ],
|
||||
"<PORT_ID>": [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" ],
|
||||
"<CONNECT>": [ [ " connect ", "<SIGSPEC>", " ", "<SIGSPEC>", "\n" ] ],
|
||||
|
||||
"<WIRES>": [ [ ], [ "<WIRE>", "<WIRES>" ] ],
|
||||
"<CELLS>": [ [ ], [ "<CELL>", "<CELLS>" ] ],
|
||||
"<BITS>": [ [ ], [ "<BIT>", "<BITS>" ] ],
|
||||
"<CONNECTS>": [ [ ], [ "<CONNECT>", "<CONNECTS>" ] ],
|
||||
"<SIGSPECS>": [ [ ], [ "<SIGSPEC>", " ", "<SIGSPECS>" ] ]
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
logger -expect error "Second design missing module top_renamed" 1
|
||||
|
||||
read_rtlil <<EOT
|
||||
module \top
|
||||
wire width 1 input 1 \a
|
||||
wire width 1 output 2 \y
|
||||
connect \y \a
|
||||
end
|
||||
EOT
|
||||
|
||||
design -save golden
|
||||
|
||||
design -reset
|
||||
read_rtlil <<EOT
|
||||
module \top_renamed
|
||||
wire width 1 input 1 \a
|
||||
wire width 1 output 2 \y
|
||||
connect \y \a
|
||||
end
|
||||
EOT
|
||||
|
||||
design_equal golden
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
read_rtlil <<EOT
|
||||
module \top
|
||||
wire width 1 input 1 \a
|
||||
wire width 1 output 2 \y
|
||||
connect \y \a
|
||||
end
|
||||
EOT
|
||||
|
||||
design -save golden
|
||||
design_equal golden
|
||||
|
||||
design -save copy
|
||||
design_equal copy
|
||||
|
||||
design -load golden
|
||||
design_equal golden
|
||||
design_equal copy
|
||||
Loading…
Reference in New Issue