mirror of
https://github.com/YosysHQ/yosys
synced 2025-05-13 02:34:44 +00:00
Updates
This commit is contained in:
commit
6be73e5c2e
12 changed files with 691 additions and 39 deletions
2
Makefile
2
Makefile
|
@ -163,7 +163,7 @@ ifeq ($(OS), Haiku)
|
|||
CXXFLAGS += -D_DEFAULT_SOURCE
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.47+22
|
||||
YOSYS_VER := 0.47+61
|
||||
|
||||
# Note: We arrange for .gitcommit to contain the (short) commit hash in
|
||||
# tarballs generated with git-archive(1) using .gitattributes. The git repo
|
||||
|
|
|
@ -2497,7 +2497,7 @@ struct CxxrtlWorker {
|
|||
// Alias of a member wire
|
||||
const RTLIL::Wire *aliasee = debug_wire_type.sig_subst.as_wire();
|
||||
f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)) << ", ";
|
||||
dump_debug_attrs(aliasee);
|
||||
dump_debug_attrs(wire);
|
||||
f << ", ";
|
||||
// If the aliasee is an outline, then the alias must be an outline, too; otherwise downstream
|
||||
// tooling has no way to find out about the outline.
|
||||
|
|
|
@ -4306,7 +4306,7 @@ struct VerificPass : public Pass {
|
|||
}
|
||||
if (v[0] == '"') {
|
||||
std::string s = v.substr(1, GetSize(v)-2);
|
||||
RuntimeFlags::SetStringVar(k.c_str(), v.c_str());
|
||||
RuntimeFlags::SetStringVar(k.c_str(), s.c_str());
|
||||
goto check_error;
|
||||
}
|
||||
char *endptr;
|
||||
|
|
|
@ -37,6 +37,14 @@ struct TimingInfo
|
|||
bool operator==(const NameBit& nb) const { return nb.name == name && nb.offset == offset; }
|
||||
bool operator!=(const NameBit& nb) const { return !operator==(nb); }
|
||||
unsigned int hash() const { return mkhash_add(name.hash(), offset); }
|
||||
std::optional<SigBit> get_connection(RTLIL::Cell *cell) {
|
||||
if (!cell->hasPort(name))
|
||||
return {};
|
||||
auto &port = cell->getPort(name);
|
||||
if (offset >= port.size())
|
||||
return {};
|
||||
return port[offset];
|
||||
}
|
||||
};
|
||||
struct BitBit
|
||||
{
|
||||
|
|
|
@ -52,6 +52,7 @@ OBJS += passes/cmds/dft_tag.o
|
|||
OBJS += passes/cmds/future.o
|
||||
OBJS += passes/cmds/box_derive.o
|
||||
OBJS += passes/cmds/example_dt.o
|
||||
OBJS += passes/cmds/portarcs.o
|
||||
OBJS += passes/cmds/activity.o
|
||||
OBJS += passes/cmds/splitnetlist.o
|
||||
OBJS += passes/cmds/reconstructbusses.o
|
||||
OBJS += passes/cmds/reconstructbusses.o
|
||||
|
|
|
@ -184,7 +184,7 @@ private:
|
|||
std::vector<RTLIL::SigSig> connections(module->connections());
|
||||
|
||||
for(auto &cell : module->cells().to_vector()) {
|
||||
if (!cell->type.in({ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_MUX_), ID($_NMUX_), ID($_NOT_), ID($anyconst), ID($allconst), ID($assume), ID($assert)}) && module->design->module(cell->type) == nullptr) {
|
||||
if (!cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_MUX_), ID($_NMUX_), ID($_NOT_), ID($anyconst), ID($allconst), ID($assume), ID($assert)) && module->design->module(cell->type) == nullptr) {
|
||||
log_cmd_error("Unsupported cell type \"%s\" found. Run `techmap` first.\n", cell->type.c_str());
|
||||
}
|
||||
if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_))) {
|
||||
|
|
310
passes/cmds/portarcs.cc
Normal file
310
passes/cmds/portarcs.cc
Normal file
|
@ -0,0 +1,310 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2024 Martin Povišer <povik@cutebit.org>
|
||||
*
|
||||
* 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/timinginfo.h"
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/utils.h"
|
||||
#include "kernel/celltypes.h"
|
||||
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
||||
static RTLIL::SigBit canonical_bit(RTLIL::SigBit bit)
|
||||
{
|
||||
RTLIL::Wire *w;
|
||||
while ((w = bit.wire) != NULL && !w->port_input &&
|
||||
w->driverCell()->type.in(ID($buf), ID($_BUF_))) {
|
||||
bit = w->driverCell()->getPort(ID::A)[bit.offset];
|
||||
}
|
||||
return bit;
|
||||
}
|
||||
|
||||
struct PortarcsPass : Pass {
|
||||
PortarcsPass() : Pass("portarcs", "derive port arcs for propagation delay") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" portarcs [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This command characterizes the combinational content of selected modules and\n");
|
||||
log("derives timing arcs going from module inputs to module outputs representing the\n");
|
||||
log("propagation delay of the module.\n");
|
||||
log("\n");
|
||||
log(" -draw\n");
|
||||
log(" plot the computed delay table to the terminal\n");
|
||||
log("\n");
|
||||
log(" -icells\n");
|
||||
log(" assign unit delay to gates from the internal Yosys cell library\n");
|
||||
log("\n");
|
||||
log(" -write\n");
|
||||
log(" write the computed arcs back into the module as $specify2 instances\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *d) override
|
||||
{
|
||||
log_header(d, "Executing PORTARCS pass. (derive propagation arcs)\n");
|
||||
|
||||
size_t argidx;
|
||||
bool icells_mode = false, write_mode = false, draw_mode = false;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-icells")
|
||||
icells_mode = true;
|
||||
else if (args[argidx] == "-write")
|
||||
write_mode = true;
|
||||
else if (args[argidx] == "-draw")
|
||||
draw_mode = true;
|
||||
else
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, d);
|
||||
|
||||
d->bufNormalize(true);
|
||||
TimingInfo tinfo(d);
|
||||
|
||||
if (icells_mode) {
|
||||
CellTypes ct;
|
||||
ct.setup_stdcells_eval();
|
||||
for (auto [id, type] : ct.cell_types) {
|
||||
auto &tdata = tinfo.data[id];
|
||||
tdata.has_inputs = true;
|
||||
for (auto inp : type.inputs)
|
||||
for (auto out : type.outputs)
|
||||
tdata.comb[TimingInfo::BitBit({inp, 0}, {out, 0})] = 1000;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto m : d->selected_whole_modules_warn()) {
|
||||
bool ambiguous_ports = false;
|
||||
SigSpec inputs, outputs;
|
||||
for (auto port : m->ports) {
|
||||
Wire *w = m->wire(port);
|
||||
log_assert(w->port_input || w->port_output);
|
||||
if (w->port_input && w->port_output) {
|
||||
log_warning("Module '%s' with ambiguous direction on port %s ignored.\n",
|
||||
log_id(m), log_id(w));
|
||||
ambiguous_ports = true;
|
||||
break;
|
||||
}
|
||||
if (w->port_input)
|
||||
inputs.append(w);
|
||||
else
|
||||
outputs.append(w);
|
||||
}
|
||||
if (ambiguous_ports)
|
||||
continue;
|
||||
|
||||
SigSpec ordering;
|
||||
{
|
||||
TopoSort<SigBit> sort;
|
||||
|
||||
for (auto cell : m->cells())
|
||||
if (cell->type != ID($buf)) {
|
||||
auto tdata = tinfo.find(cell->type);
|
||||
if (tdata == tinfo.end())
|
||||
log_cmd_error("Missing timing data for module '%s'.\n", log_id(cell->type));
|
||||
for (auto [edge, delay] : tdata->second.comb) {
|
||||
auto from = edge.first.get_connection(cell);
|
||||
auto to = edge.second.get_connection(cell);
|
||||
if (from && to) {
|
||||
auto from_c = canonical_bit(from.value());
|
||||
if (from_c.wire)
|
||||
sort.edge(from_c, to.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sort.sort())
|
||||
log_error("Failed to sort instances in module %s.\n", log_id(m));
|
||||
|
||||
ordering = sort.sorted;
|
||||
}
|
||||
|
||||
dict<SigBit, int*> annotations;
|
||||
std::vector<std::unique_ptr<int[]>> allocated;
|
||||
std::vector<int*> recycling;
|
||||
|
||||
auto alloc_for_bit = [&](SigBit bit) {
|
||||
if (!recycling.empty()) {
|
||||
annotations[bit] = recycling.back();
|
||||
recycling.pop_back();
|
||||
} else {
|
||||
int *p = new int[std::max(1, inputs.size())];
|
||||
allocated.emplace_back(p);
|
||||
annotations[bit] = p;
|
||||
}
|
||||
};
|
||||
|
||||
for (auto bit : outputs) {
|
||||
SigBit bit_c = canonical_bit(bit);
|
||||
alloc_for_bit(bit_c);
|
||||
|
||||
// consistency check
|
||||
annotations.at(bit_c)[0] = (intptr_t) bit_c.wire;
|
||||
}
|
||||
|
||||
for (int i = ordering.size() - 1; i >= 0; i--) {
|
||||
SigBit bit = ordering[i];
|
||||
|
||||
if (!bit.wire->port_input) {
|
||||
auto cell = bit.wire->driverCell();
|
||||
auto tdata = tinfo.find(cell->type);
|
||||
log_assert(tdata != tinfo.end());
|
||||
for (auto [edge, delay] : tdata->second.comb) {
|
||||
auto from = edge.first.get_connection(cell);
|
||||
auto to = edge.second.get_connection(cell);
|
||||
if (from && to && to.value() == bit) {
|
||||
auto from_c = canonical_bit(from.value());
|
||||
if (from_c.wire) {
|
||||
if (!annotations.count(from_c)) {
|
||||
alloc_for_bit(from_c);
|
||||
|
||||
// consistency check
|
||||
annotations.at(from_c)[0] = (intptr_t) from_c.wire;
|
||||
} else {
|
||||
// consistency check
|
||||
log_assert(annotations.at(from_c)[0] == ((int) (intptr_t) from_c.wire));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (annotations.count(bit)) {
|
||||
// consistency check
|
||||
log_assert(annotations.at(bit)[0] == ((int) (intptr_t) bit.wire));
|
||||
|
||||
recycling.push_back(annotations.at(ordering[i]));
|
||||
}
|
||||
}
|
||||
log_debug("Allocated %lux%d\n", allocated.size(), inputs.size());
|
||||
|
||||
for (auto bit : outputs) {
|
||||
int *p = annotations.at(canonical_bit(bit));
|
||||
for (int i = 0; i < inputs.size(); i++)
|
||||
p[i] = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ordering.size(); i++) {
|
||||
SigBit bit = ordering[i];
|
||||
int *p = annotations.at(bit);
|
||||
if (bit.wire->port_input) {
|
||||
for (int j = 0; j < inputs.size(); j++)
|
||||
p[j] = (j == i) ? 0 : -1;
|
||||
} else {
|
||||
for (int j = 0; j < inputs.size(); j++)
|
||||
p[j] = -1;
|
||||
|
||||
auto cell = ordering[i].wire->driverCell();
|
||||
auto tdata = tinfo.find(cell->type);
|
||||
log_assert(tdata != tinfo.end());
|
||||
for (auto [edge, delay] : tdata->second.comb) {
|
||||
auto from = edge.first.get_connection(cell);
|
||||
auto to = edge.second.get_connection(cell);
|
||||
if (from && to && to.value() == ordering[i]) {
|
||||
auto from_c = canonical_bit(from.value());
|
||||
if (from_c.wire) {
|
||||
int *q = annotations.at(from_c);
|
||||
for (int j = 0; j < inputs.size(); j++)
|
||||
if (q[j] >= 0)
|
||||
p[j] = std::max(p[j], q[j] + delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (draw_mode) {
|
||||
auto bit_str = [](SigBit bit) {
|
||||
return stringf("%s%d", RTLIL::unescape_id(bit.wire->name.str()).c_str(), bit.offset);
|
||||
};
|
||||
|
||||
std::vector<std::string> headings;
|
||||
int top_length = 0;
|
||||
for (auto bit : inputs) {
|
||||
headings.push_back(bit_str(bit));
|
||||
top_length = std::max(top_length, (int) headings.back().size());
|
||||
}
|
||||
|
||||
int max_delay = 0;
|
||||
for (auto bit : outputs) {
|
||||
int *p = annotations.at(canonical_bit(bit));
|
||||
for (auto i = 0; i < inputs.size(); i++)
|
||||
if (p[i] > max_delay)
|
||||
max_delay = p[i];
|
||||
}
|
||||
|
||||
log("Delay legend:\n\n");
|
||||
log(" ");
|
||||
for (int i = 0; i < 24; i++)
|
||||
log("\033[48;5;%dm ", 232+i);
|
||||
log("\033[0m\n");
|
||||
log(" |%22s|\n", "");
|
||||
log(" 0%22s%d\n", "", max_delay);
|
||||
log("\n");
|
||||
for (int k = top_length - 1; k >= 0; k--) {
|
||||
log(" %10s ", "");
|
||||
for (auto &h : headings)
|
||||
log("%c", (k < (int) h.size()) ? h[k] : ' ');
|
||||
log("\n");
|
||||
}
|
||||
log("\n");
|
||||
|
||||
for (auto bit : outputs) {
|
||||
log(" %10s ", bit_str(bit).c_str());
|
||||
int *p = annotations.at(canonical_bit(bit));
|
||||
for (auto i = 0; i < inputs.size(); i++)
|
||||
log("\033[48;5;%dm ", 232 + ((std::max(p[i], 0) * 24) - 1) / max_delay);
|
||||
log("\033[0m\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (write_mode) {
|
||||
for (auto bit : outputs) {
|
||||
int *p = annotations.at(canonical_bit(bit));
|
||||
for (auto i = 0; i < inputs.size(); i++) {
|
||||
if (p[i] >= 0) {
|
||||
Cell *spec = m->addCell(NEW_ID, ID($specify2));
|
||||
spec->setParam(ID::SRC_WIDTH, 1);
|
||||
spec->setParam(ID::DST_WIDTH, 1);
|
||||
spec->setParam(ID::T_FALL_MAX, p[i]);
|
||||
spec->setParam(ID::T_FALL_TYP, p[i]);
|
||||
spec->setParam(ID::T_FALL_MIN, p[i]);
|
||||
spec->setParam(ID::T_RISE_MAX, p[i]);
|
||||
spec->setParam(ID::T_RISE_TYP, p[i]);
|
||||
spec->setParam(ID::T_RISE_MIN, p[i]);
|
||||
spec->setParam(ID::SRC_DST_POL, false);
|
||||
spec->setParam(ID::SRC_DST_PEN, false);
|
||||
spec->setParam(ID::FULL, false);
|
||||
spec->setPort(ID::EN, Const(1, 1));
|
||||
spec->setPort(ID::SRC, inputs[i]);
|
||||
spec->setPort(ID::DST, bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
d->bufNormalize(false);
|
||||
}
|
||||
} PortarcsPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -404,6 +404,17 @@ struct BufnormPass : public Pass {
|
|||
|
||||
pool<Cell*> added_buffers;
|
||||
|
||||
const auto lookup_mapping = [&mapped_bits](const SigBit bit, bool default_sx = false)
|
||||
{
|
||||
if (!bit.is_wire())
|
||||
return bit;
|
||||
|
||||
if (default_sx)
|
||||
return mapped_bits.at(bit, State::Sx);
|
||||
|
||||
return mapped_bits.at(bit);
|
||||
};
|
||||
|
||||
auto make_buffer_f = [&](const IdString &type, const SigSpec &src, const SigSpec &dst)
|
||||
{
|
||||
auto it = old_buffers.find(pair<IdString, SigSpec>(type, dst));
|
||||
|
@ -438,12 +449,8 @@ struct BufnormPass : public Pass {
|
|||
bool chain_this_wire = chain_this_wire_f(wire);
|
||||
|
||||
SigSpec keysig = sigmap(wire), insig = wire, outsig = wire;
|
||||
for (int i = 0; i < GetSize(insig); i++) {
|
||||
if (keysig[i].is_wire())
|
||||
insig[i] = mapped_bits.at(keysig[i], State::Sx);
|
||||
else
|
||||
insig[i] = keysig[i];
|
||||
}
|
||||
for (int i = 0; i < GetSize(insig); i++)
|
||||
insig[i] = lookup_mapping(keysig[i], true);
|
||||
|
||||
if (chain_this_wire) {
|
||||
for (int i = 0; i < GetSize(outsig); i++)
|
||||
|
@ -491,7 +498,7 @@ struct BufnormPass : public Pass {
|
|||
|
||||
SigSpec newsig = conn.second;
|
||||
for (auto &bit : newsig)
|
||||
bit = mapped_bits[sigmap(bit)];
|
||||
bit = lookup_mapping(sigmap(bit));
|
||||
|
||||
if (conn.second != newsig) {
|
||||
log(" fixing input signal on cell %s port %s: %s\n",
|
||||
|
|
|
@ -66,6 +66,11 @@ static void logmap_all()
|
|||
logmap(ID($_DFF_PP0_));
|
||||
logmap(ID($_DFF_PP1_));
|
||||
|
||||
logmap(ID($_DFFE_NN_));
|
||||
logmap(ID($_DFFE_NP_));
|
||||
logmap(ID($_DFFE_PN_));
|
||||
logmap(ID($_DFFE_PP_));
|
||||
|
||||
logmap(ID($_DFFSR_NNN_));
|
||||
logmap(ID($_DFFSR_NNP_));
|
||||
logmap(ID($_DFFSR_NPN_));
|
||||
|
@ -76,6 +81,115 @@ static void logmap_all()
|
|||
logmap(ID($_DFFSR_PPP_));
|
||||
}
|
||||
|
||||
static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std::string &data_name, bool &data_not_inverted, std::string &enable_name, bool &enable_not_inverted)
|
||||
{
|
||||
static pool<std::string> warned_cells{};
|
||||
|
||||
if (cell == nullptr || attr == nullptr || attr->value.empty())
|
||||
return false;
|
||||
|
||||
auto expr = attr->value;
|
||||
auto cell_name = cell->args[0];
|
||||
|
||||
for (size_t pos = expr.find_first_of("\" \t"); pos != std::string::npos; pos = expr.find_first_of("\" \t"))
|
||||
expr.erase(pos, 1);
|
||||
|
||||
// if this isn't an enable flop, the next_state variable is usually just the input pin name.
|
||||
if (expr[expr.size()-1] == '\'') {
|
||||
data_name = expr.substr(0, expr.size()-1);
|
||||
data_not_inverted = false;
|
||||
} else if (expr[0] == '!') {
|
||||
data_name = expr.substr(1, expr.size()-1);
|
||||
data_not_inverted = false;
|
||||
} else {
|
||||
data_name = expr;
|
||||
data_not_inverted = true;
|
||||
}
|
||||
|
||||
for (auto child : cell->children)
|
||||
if (child->id == "pin" && child->args.size() == 1 && child->args[0] == data_name)
|
||||
return true;
|
||||
|
||||
// the next_state variable isn't just a pin name; perhaps this is an enable?
|
||||
auto helper = LibertyExpression::Lexer(expr);
|
||||
auto tree = LibertyExpression::parse(helper);
|
||||
|
||||
if (tree.kind == LibertyExpression::Kind::EMPTY) {
|
||||
if (!warned_cells.count(cell_name)) {
|
||||
log_warning("Invalid expression '%s' in next_state attribute of cell '%s' - skipping.\n", expr.c_str(), cell_name.c_str());
|
||||
warned_cells.insert(cell_name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto pin_names = pool<std::string>{};
|
||||
tree.get_pin_names(pin_names);
|
||||
|
||||
// from the `ff` block, we know the flop output signal name for loopback.
|
||||
auto ff = cell->find("ff");
|
||||
if (ff == nullptr || ff->args.size() != 2)
|
||||
return false;
|
||||
auto ff_output = ff->args.at(0);
|
||||
|
||||
// This test is redundant with the one in enable_pin, but we're in a
|
||||
// position that gives better diagnostics here.
|
||||
if (!pin_names.count(ff_output)) {
|
||||
if (!warned_cells.count(cell_name)) {
|
||||
log_warning("Inference failed on expression '%s' in next_state attribute of cell '%s' because it does not contain ff output '%s' - skipping.\n", expr.c_str(), cell_name.c_str(), ff_output.c_str());
|
||||
warned_cells.insert(cell_name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
data_not_inverted = true;
|
||||
data_name = "";
|
||||
enable_not_inverted = true;
|
||||
enable_name = "";
|
||||
|
||||
if (pin_names.size() == 3 && pin_names.count(ff_output)) {
|
||||
pin_names.erase(ff_output);
|
||||
auto pins = std::vector<std::string>(pin_names.begin(), pin_names.end());
|
||||
int lut = 0;
|
||||
for (int n = 0; n < 8; n++) {
|
||||
auto values = dict<std::string, bool>{};
|
||||
values.insert(std::make_pair(pins[0], (n & 1) == 1));
|
||||
values.insert(std::make_pair(pins[1], (n & 2) == 2));
|
||||
values.insert(std::make_pair(ff_output, (n & 4) == 4));
|
||||
if (tree.eval(values))
|
||||
lut |= 1 << n;
|
||||
}
|
||||
// the ff output Q is in a known bit location, so we now just have to compare the LUT mask to known values to find the enable pin and polarity.
|
||||
if (lut == 0xD8) {
|
||||
data_name = pins[1];
|
||||
enable_name = pins[0];
|
||||
return true;
|
||||
}
|
||||
if (lut == 0xB8) {
|
||||
data_name = pins[0];
|
||||
enable_name = pins[1];
|
||||
return true;
|
||||
}
|
||||
enable_not_inverted = false;
|
||||
if (lut == 0xE4) {
|
||||
data_name = pins[1];
|
||||
enable_name = pins[0];
|
||||
return true;
|
||||
}
|
||||
if (lut == 0xE2) {
|
||||
data_name = pins[0];
|
||||
enable_name = pins[1];
|
||||
return true;
|
||||
}
|
||||
// this does not match an enable flop.
|
||||
}
|
||||
|
||||
if (!warned_cells.count(cell_name)) {
|
||||
log_warning("Inference failed on expression '%s' in next_state attribute of cell '%s' because it does not evaluate to an enable flop - skipping.\n", expr.c_str(), cell_name.c_str());
|
||||
warned_cells.insert(cell_name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool parse_pin(const LibertyAst *cell, const LibertyAst *attr, std::string &pin_name, bool &pin_pol)
|
||||
{
|
||||
if (cell == nullptr || attr == nullptr || attr->value.empty())
|
||||
|
@ -115,7 +229,7 @@ static bool parse_pin(const LibertyAst *cell, const LibertyAst *attr, std::strin
|
|||
return false;
|
||||
}
|
||||
|
||||
static void find_cell(const LibertyAst *ast, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval, std::vector<std::string> &dont_use_cells)
|
||||
static void find_cell(const LibertyAst *ast, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval, bool has_enable, bool enapol, std::vector<std::string> &dont_use_cells)
|
||||
{
|
||||
const LibertyAst *best_cell = nullptr;
|
||||
std::map<std::string, char> best_cell_ports;
|
||||
|
@ -151,12 +265,12 @@ static void find_cell(const LibertyAst *ast, IdString cell_type, bool clkpol, bo
|
|||
if (ff == nullptr)
|
||||
continue;
|
||||
|
||||
std::string cell_clk_pin, cell_rst_pin, cell_next_pin;
|
||||
bool cell_clk_pol, cell_rst_pol, cell_next_pol;
|
||||
std::string cell_clk_pin, cell_rst_pin, cell_next_pin, cell_enable_pin;
|
||||
bool cell_clk_pol, cell_rst_pol, cell_next_pol, cell_enable_pol;
|
||||
|
||||
if (!parse_pin(cell, ff->find("clocked_on"), cell_clk_pin, cell_clk_pol) || cell_clk_pol != clkpol)
|
||||
continue;
|
||||
if (!parse_pin(cell, ff->find("next_state"), cell_next_pin, cell_next_pol))
|
||||
if (!parse_next_state(cell, ff->find("next_state"), cell_next_pin, cell_next_pol, cell_enable_pin, cell_enable_pol) || (has_enable && (cell_enable_pin.empty() || cell_enable_pol != enapol)))
|
||||
continue;
|
||||
if (has_reset && rstval == false) {
|
||||
if (!parse_pin(cell, ff->find("clear"), cell_rst_pin, cell_rst_pol) || cell_rst_pol != rstpol)
|
||||
|
@ -171,6 +285,8 @@ static void find_cell(const LibertyAst *ast, IdString cell_type, bool clkpol, bo
|
|||
this_cell_ports[cell_clk_pin] = 'C';
|
||||
if (has_reset)
|
||||
this_cell_ports[cell_rst_pin] = 'R';
|
||||
if (has_enable)
|
||||
this_cell_ports[cell_enable_pin] = 'E';
|
||||
this_cell_ports[cell_next_pin] = 'D';
|
||||
|
||||
double area = 0;
|
||||
|
@ -239,7 +355,7 @@ static void find_cell(const LibertyAst *ast, IdString cell_type, bool clkpol, bo
|
|||
}
|
||||
}
|
||||
|
||||
static void find_cell_sr(const LibertyAst *ast, IdString cell_type, bool clkpol, bool setpol, bool clrpol, std::vector<std::string> &dont_use_cells)
|
||||
static void find_cell_sr(const LibertyAst *ast, IdString cell_type, bool clkpol, bool setpol, bool clrpol, bool has_enable, bool enapol, std::vector<std::string> &dont_use_cells)
|
||||
{
|
||||
const LibertyAst *best_cell = nullptr;
|
||||
std::map<std::string, char> best_cell_ports;
|
||||
|
@ -247,6 +363,8 @@ static void find_cell_sr(const LibertyAst *ast, IdString cell_type, bool clkpol,
|
|||
bool best_cell_noninv = false;
|
||||
double best_cell_area = 0;
|
||||
|
||||
log_assert(!enapol && "set/reset cell with enable is unimplemented due to lack of cells for testing");
|
||||
|
||||
if (ast->id != "library")
|
||||
log_error("Format error in liberty file.\n");
|
||||
|
||||
|
@ -275,12 +393,12 @@ static void find_cell_sr(const LibertyAst *ast, IdString cell_type, bool clkpol,
|
|||
if (ff == nullptr)
|
||||
continue;
|
||||
|
||||
std::string cell_clk_pin, cell_set_pin, cell_clr_pin, cell_next_pin;
|
||||
bool cell_clk_pol, cell_set_pol, cell_clr_pol, cell_next_pol;
|
||||
std::string cell_clk_pin, cell_set_pin, cell_clr_pin, cell_next_pin, cell_enable_pin;
|
||||
bool cell_clk_pol, cell_set_pol, cell_clr_pol, cell_next_pol, cell_enable_pol;
|
||||
|
||||
if (!parse_pin(cell, ff->find("clocked_on"), cell_clk_pin, cell_clk_pol) || cell_clk_pol != clkpol)
|
||||
continue;
|
||||
if (!parse_pin(cell, ff->find("next_state"), cell_next_pin, cell_next_pol))
|
||||
if (!parse_next_state(cell, ff->find("next_state"), cell_next_pin, cell_next_pol, cell_enable_pin, cell_enable_pol))
|
||||
continue;
|
||||
if (!parse_pin(cell, ff->find("preset"), cell_set_pin, cell_set_pol) || cell_set_pol != setpol)
|
||||
continue;
|
||||
|
@ -291,6 +409,8 @@ static void find_cell_sr(const LibertyAst *ast, IdString cell_type, bool clkpol,
|
|||
this_cell_ports[cell_clk_pin] = 'C';
|
||||
this_cell_ports[cell_set_pin] = 'S';
|
||||
this_cell_ports[cell_clr_pin] = 'R';
|
||||
if (has_enable)
|
||||
this_cell_ports[cell_enable_pin] = 'E';
|
||||
this_cell_ports[cell_next_pin] = 'D';
|
||||
|
||||
double area = 0;
|
||||
|
@ -526,26 +646,31 @@ struct DfflibmapPass : public Pass {
|
|||
LibertyParser libparser(f);
|
||||
f.close();
|
||||
|
||||
find_cell(libparser.ast, ID($_DFF_N_), false, false, false, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_P_), true, false, false, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_N_), false, false, false, false, false, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_P_), true, false, false, false, false, false, dont_use_cells);
|
||||
|
||||
find_cell(libparser.ast, ID($_DFF_NN0_), false, true, false, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_NN1_), false, true, false, true, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_NP0_), false, true, true, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_NP1_), false, true, true, true, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_PN0_), true, true, false, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_PN1_), true, true, false, true, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_PP0_), true, true, true, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_PP1_), true, true, true, true, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_NN0_), false, true, false, false, false, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_NN1_), false, true, false, true, false, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_NP0_), false, true, true, false, false, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_NP1_), false, true, true, true, false, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_PN0_), true, true, false, false, false, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_PN1_), true, true, false, true, false, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_PP0_), true, true, true, false, false, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFF_PP1_), true, true, true, true, false, false, dont_use_cells);
|
||||
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_NNN_), false, false, false, dont_use_cells);
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_NNP_), false, false, true, dont_use_cells);
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_NPN_), false, true, false, dont_use_cells);
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_NPP_), false, true, true, dont_use_cells);
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_PNN_), true, false, false, dont_use_cells);
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_PNP_), true, false, true, dont_use_cells);
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_PPN_), true, true, false, dont_use_cells);
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_PPP_), true, true, true, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFFE_NN_), false, false, false, false, true, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFFE_NP_), false, false, false, false, true, true, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFFE_PN_), true, false, false, false, true, false, dont_use_cells);
|
||||
find_cell(libparser.ast, ID($_DFFE_PP_), true, false, false, false, true, true, dont_use_cells);
|
||||
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_NNN_), false, false, false, false, false, dont_use_cells);
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_NNP_), false, false, true, false, false, dont_use_cells);
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_NPN_), false, true, false, false, false, dont_use_cells);
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_NPP_), false, true, true, false, false, dont_use_cells);
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_PNN_), true, false, false, false, false, dont_use_cells);
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_PNP_), true, false, true, false, false, dont_use_cells);
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_PPN_), true, true, false, false, false, dont_use_cells);
|
||||
find_cell_sr(libparser.ast, ID($_DFFSR_PPP_), true, true, true, false, false, dont_use_cells);
|
||||
|
||||
log(" final dff cell mappings:\n");
|
||||
logmap_all();
|
||||
|
|
|
@ -80,6 +80,152 @@ void LibertyAst::dump(FILE *f, sieve &blacklist, sieve &whitelist, std::string i
|
|||
fprintf(f, " ;\n");
|
||||
}
|
||||
|
||||
#ifndef FILTERLIB
|
||||
// https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html
|
||||
LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
|
||||
if (s.empty())
|
||||
return LibertyExpression{};
|
||||
|
||||
char c = s.peek();
|
||||
auto lhs = LibertyExpression{};
|
||||
|
||||
while (isspace(c)) {
|
||||
if (s.empty())
|
||||
return lhs;
|
||||
s.next();
|
||||
c = s.peek();
|
||||
}
|
||||
|
||||
if (isalpha(c)) { // pin
|
||||
lhs.kind = Kind::PIN;
|
||||
lhs.name = s.pin();
|
||||
} else if (c == '(') { // parens
|
||||
s.next();
|
||||
lhs = parse(s);
|
||||
if (s.peek() != ')') {
|
||||
log_warning("expected ')' instead of '%c' while parsing Liberty expression '%s'\n", s.peek(), s.full_expr().c_str());
|
||||
return lhs;
|
||||
}
|
||||
s.next();
|
||||
} else if (c == '!') { // prefix NOT
|
||||
s.next();
|
||||
lhs.kind = Kind::NOT;
|
||||
lhs.children.push_back(parse(s, 7));
|
||||
} else {
|
||||
log_warning("unrecognised character '%c' while parsing Liberty expression '%s'\n", c, s.full_expr().c_str());
|
||||
return lhs;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (s.empty())
|
||||
break;
|
||||
|
||||
c = s.peek();
|
||||
|
||||
while (isspace(c)) {
|
||||
if (s.empty())
|
||||
return lhs;
|
||||
s.next();
|
||||
c = s.peek();
|
||||
}
|
||||
|
||||
if (c == '\'') { // postfix NOT
|
||||
if (min_prio > 7)
|
||||
break;
|
||||
s.next();
|
||||
|
||||
auto n = LibertyExpression{};
|
||||
n.kind = Kind::NOT;
|
||||
n.children.push_back(lhs);
|
||||
lhs = std::move(n);
|
||||
|
||||
continue;
|
||||
} else if (c == '^') { // infix XOR
|
||||
if (min_prio > 5)
|
||||
break;
|
||||
s.next();
|
||||
|
||||
auto rhs = parse(s, 6);
|
||||
auto n = LibertyExpression{};
|
||||
n.kind = Kind::XOR;
|
||||
n.children.push_back(lhs);
|
||||
n.children.push_back(rhs);
|
||||
lhs = std::move(n);
|
||||
|
||||
continue;
|
||||
} else if (c == '&' || c == '*') { // infix AND
|
||||
// technically space should be considered infix AND. it seems rare in practice.
|
||||
if (min_prio > 3)
|
||||
break;
|
||||
s.next();
|
||||
|
||||
auto rhs = parse(s, 4);
|
||||
auto n = LibertyExpression{};
|
||||
n.kind = Kind::AND;
|
||||
n.children.push_back(lhs);
|
||||
n.children.push_back(rhs);
|
||||
lhs = std::move(n);
|
||||
|
||||
continue;
|
||||
} else if (c == '+' || c == '|') { // infix OR
|
||||
if (min_prio > 1)
|
||||
break;
|
||||
s.next();
|
||||
|
||||
auto rhs = parse(s, 2);
|
||||
auto n = LibertyExpression{};
|
||||
n.kind = Kind::OR;
|
||||
n.children.push_back(lhs);
|
||||
n.children.push_back(rhs);
|
||||
lhs = std::move(n);
|
||||
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
void LibertyExpression::get_pin_names(pool<std::string>& names) {
|
||||
if (kind == Kind::PIN) {
|
||||
names.insert(name);
|
||||
} else {
|
||||
for (auto& child : children)
|
||||
child.get_pin_names(names);
|
||||
}
|
||||
}
|
||||
|
||||
bool LibertyExpression::eval(dict<std::string, bool>& values) {
|
||||
bool result = false;
|
||||
switch (kind) {
|
||||
case Kind::AND:
|
||||
result = true;
|
||||
for (auto& child : children)
|
||||
result &= child.eval(values);
|
||||
return result;
|
||||
case Kind::OR:
|
||||
result = false;
|
||||
for (auto& child : children)
|
||||
result |= child.eval(values);
|
||||
return result;
|
||||
case Kind::NOT:
|
||||
log_assert(children.size() == 1);
|
||||
return !children[0].eval(values);
|
||||
case Kind::XOR:
|
||||
result = false;
|
||||
for (auto& child : children)
|
||||
result ^= child.eval(values);
|
||||
return result;
|
||||
case Kind::PIN:
|
||||
return values.at(name);
|
||||
case Kind::EMPTY:
|
||||
log_assert(false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
int LibertyParser::lexer(std::string &str)
|
||||
{
|
||||
int c;
|
||||
|
@ -115,12 +261,19 @@ int LibertyParser::lexer(std::string &str)
|
|||
// maybe it's a string?
|
||||
if (c == '"') {
|
||||
str = "";
|
||||
#ifdef FILTERLIB
|
||||
str += c;
|
||||
#endif
|
||||
while (1) {
|
||||
c = f.get();
|
||||
if (c == '\n')
|
||||
line++;
|
||||
if (c == '"')
|
||||
if (c == '"') {
|
||||
#ifdef FILTERLIB
|
||||
str += c;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
str += c;
|
||||
}
|
||||
// fprintf(stderr, "LEX: string >>%s<<\n", str.c_str());
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#ifndef LIBPARSE_H
|
||||
#define LIBPARSE_H
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -39,6 +40,52 @@ namespace Yosys
|
|||
void dump(FILE *f, sieve &blacklist, sieve &whitelist, std::string indent = "", std::string path = "", bool path_ok = false) const;
|
||||
};
|
||||
|
||||
struct LibertyExpression
|
||||
{
|
||||
struct Lexer {
|
||||
std::string s, expr;
|
||||
|
||||
Lexer(std::string s) : s{s}, expr{s} {}
|
||||
|
||||
bool empty() { return s.empty();}
|
||||
char peek() { return s[0]; }
|
||||
std::string full_expr() { return expr; }
|
||||
|
||||
char next() {
|
||||
char c = s[0];
|
||||
s = s.substr(1, s.size());
|
||||
return c;
|
||||
}
|
||||
|
||||
std::string pin() {
|
||||
auto length = s.find_first_of("\t()'!^*& +|");
|
||||
auto pin = s.substr(0, length);
|
||||
s = s.substr(length, s.size());
|
||||
return pin;
|
||||
}
|
||||
};
|
||||
|
||||
enum Kind {
|
||||
AND,
|
||||
OR,
|
||||
NOT,
|
||||
XOR,
|
||||
// the standard specifies constants, but they're probably rare in practice.
|
||||
PIN,
|
||||
EMPTY
|
||||
};
|
||||
|
||||
Kind kind;
|
||||
std::string name;
|
||||
std::vector<LibertyExpression> children;
|
||||
|
||||
LibertyExpression() : kind(Kind::EMPTY) {}
|
||||
|
||||
static LibertyExpression parse(Lexer &s, int min_prio = 0);
|
||||
void get_pin_names(pool<std::string>& names);
|
||||
bool eval(dict<std::string, bool>& values);
|
||||
};
|
||||
|
||||
class LibertyParser
|
||||
{
|
||||
private:
|
||||
|
|
|
@ -301,6 +301,7 @@ struct SynthGateMatePass : public ScriptPass
|
|||
}
|
||||
run("muxcover " + muxcover_args);
|
||||
run("opt -full");
|
||||
run("simplemap");
|
||||
run("techmap -map +/gatemate/mux_map.v");
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue