Merge remote-tracking branch 'origin/master' into xc7srl

This commit is contained in:
Eddie Hung 2019-04-03 03:36:11 -07:00
commit d8465590ac
14 changed files with 526 additions and 73 deletions

View File

@ -315,6 +315,9 @@ Verilog Attributes and non-standard features
- The ``dynports'' attribute is used by the Verilog front-end to mark modules
that have ports with a width that depends on a parameter.
- The ``hdlname'' attribute is used by some passes to document the original
(HDL) name of a module when renaming a module.
- The ``keep`` attribute on cells and wires is used to mark objects that should
never be removed by the optimizer. This is used for example for cells that
have hidden connections that are not part of the netlist, such as IO pads.

View File

@ -106,6 +106,95 @@ struct FirrtlWorker
RTLIL::Design *design;
std::string indent;
// Define read/write ports and memories.
// We'll collect their definitions and emit the corresponding FIRRTL definitions at the appropriate point in module construction.
// For the moment, we don't handle $readmemh or $readmemb.
// These will be part of a subsequent PR.
struct read_port {
string name;
bool clk_enable;
bool clk_parity;
bool transparent;
RTLIL::SigSpec clk;
RTLIL::SigSpec ena;
RTLIL::SigSpec addr;
read_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr) : name(name), clk_enable(clk_enable), clk_parity(clk_parity), transparent(transparent), clk(clk), ena(ena), addr(addr) {
// Current (3/13/2019) conventions:
// generate a constant 0 for clock and a constant 1 for enable if they are undefined.
if (!clk.is_fully_def())
this->clk = SigSpec(RTLIL::Const(0, 1));
if (!ena.is_fully_def())
this->ena = SigSpec(RTLIL::Const(1, 1));
}
string gen_read(const char * indent) {
string addr_expr = make_expr(addr);
string ena_expr = make_expr(ena);
string clk_expr = make_expr(clk);
string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str());
string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str());
string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str());
return addr_str + ena_str + clk_str;
}
};
struct write_port : read_port {
RTLIL::SigSpec mask;
write_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr, RTLIL::SigSpec mask) : read_port(name, clk_enable, clk_parity, transparent, clk, ena, addr), mask(mask) {
if (!clk.is_fully_def())
this->clk = SigSpec(RTLIL::Const(0));
if (!ena.is_fully_def())
this->ena = SigSpec(RTLIL::Const(0));
if (!mask.is_fully_def())
this->ena = SigSpec(RTLIL::Const(1));
}
string gen_read(const char * /* indent */) {
log_error("gen_read called on write_port: %s\n", name.c_str());
return stringf("gen_read called on write_port: %s\n", name.c_str());
}
string gen_write(const char * indent) {
string addr_expr = make_expr(addr);
string ena_expr = make_expr(ena);
string clk_expr = make_expr(clk);
string mask_expr = make_expr(mask);
string mask_str = stringf("%s%s.mask <= %s\n", indent, name.c_str(), mask_expr.c_str());
string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str());
string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str());
string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str());
return addr_str + ena_str + clk_str + mask_str;
}
};
/* Memories defined within this module. */
struct memory {
string name; // memory name
int abits; // number of address bits
int size; // size (in units) of the memory
int width; // size (in bits) of each element
int read_latency;
int write_latency;
vector<read_port> read_ports;
vector<write_port> write_ports;
std::string init_file;
std::string init_file_srcFileSpec;
memory(string name, int abits, int size, int width) : name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") {}
memory() : read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){}
void add_memory_read_port(read_port &rp) {
read_ports.push_back(rp);
}
void add_memory_write_port(write_port &wp) {
write_ports.push_back(wp);
}
void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) {
this->init_file = init_file;
this->init_file_srcFileSpec = init_file_srcFileSpec;
}
};
dict<string, memory> memories;
void register_memory(memory &m)
{
memories[m.name] = m;
}
void register_reverse_wire_map(string id, SigSpec sig)
{
for (int i = 0; i < GetSize(sig); i++)
@ -116,7 +205,7 @@ struct FirrtlWorker
{
}
string make_expr(const SigSpec &sig)
static string make_expr(const SigSpec &sig)
{
string expr;
@ -515,6 +604,7 @@ struct FirrtlWorker
int abits = cell->parameters.at("\\ABITS").as_int();
int width = cell->parameters.at("\\WIDTH").as_int();
int size = cell->parameters.at("\\SIZE").as_int();
memory m(mem_id, abits, size, width);
int rd_ports = cell->parameters.at("\\RD_PORTS").as_int();
int wr_ports = cell->parameters.at("\\WR_PORTS").as_int();
@ -531,33 +621,24 @@ struct FirrtlWorker
if (offset != 0)
log_error("Memory with nonzero offset: %s.%s\n", log_id(module), log_id(cell));
cell_exprs.push_back(stringf(" mem %s:\n", mem_id.c_str()));
cell_exprs.push_back(stringf(" data-type => UInt<%d>\n", width));
cell_exprs.push_back(stringf(" depth => %d\n", size));
for (int i = 0; i < rd_ports; i++)
cell_exprs.push_back(stringf(" reader => r%d\n", i));
for (int i = 0; i < wr_ports; i++)
cell_exprs.push_back(stringf(" writer => w%d\n", i));
cell_exprs.push_back(stringf(" read-latency => 0\n"));
cell_exprs.push_back(stringf(" write-latency => 1\n"));
cell_exprs.push_back(stringf(" read-under-write => undefined\n"));
for (int i = 0; i < rd_ports; i++)
{
if (rd_clk_enable[i] != State::S0)
log_error("Clocked read port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
SigSpec addr_sig = cell->getPort("\\RD_ADDR").extract(i*abits, abits);
SigSpec data_sig = cell->getPort("\\RD_DATA").extract(i*width, width);
string addr_expr = make_expr(cell->getPort("\\RD_ADDR").extract(i*abits, abits));
cell_exprs.push_back(stringf(" %s.r%d.addr <= %s\n", mem_id.c_str(), i, addr_expr.c_str()));
cell_exprs.push_back(stringf(" %s.r%d.en <= UInt<1>(1)\n", mem_id.c_str(), i));
cell_exprs.push_back(stringf(" %s.r%d.clk <= asClock(UInt<1>(0))\n", mem_id.c_str(), i));
register_reverse_wire_map(stringf("%s.r%d.data", mem_id.c_str(), i), data_sig);
string addr_expr = make_expr(addr_sig);
string name(stringf("%s.r%d", m.name.c_str(), i));
bool clk_enable = false;
bool clk_parity = true;
bool transparency = false;
SigSpec ena_sig = RTLIL::SigSpec(RTLIL::State::S1, 1);
SigSpec clk_sig = RTLIL::SigSpec(RTLIL::State::S0, 1);
read_port rp(name, clk_enable, clk_parity, transparency, clk_sig, ena_sig, addr_sig);
m.add_memory_read_port(rp);
cell_exprs.push_back(rp.gen_read(indent.c_str()));
register_reverse_wire_map(stringf("%s.data", name.c_str()), data_sig);
}
for (int i = 0; i < wr_ports; i++)
@ -568,9 +649,16 @@ struct FirrtlWorker
if (wr_clk_polarity[i] != State::S1)
log_error("Negedge write port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
string addr_expr = make_expr(cell->getPort("\\WR_ADDR").extract(i*abits, abits));
string data_expr = make_expr(cell->getPort("\\WR_DATA").extract(i*width, width));
string clk_expr = make_expr(cell->getPort("\\WR_CLK").extract(i));
string name(stringf("%s.w%d", m.name.c_str(), i));
bool clk_enable = true;
bool clk_parity = true;
bool transparency = false;
SigSpec addr_sig =cell->getPort("\\WR_ADDR").extract(i*abits, abits);
string addr_expr = make_expr(addr_sig);
SigSpec data_sig =cell->getPort("\\WR_DATA").extract(i*width, width);
string data_expr = make_expr(data_sig);
SigSpec clk_sig = cell->getPort("\\WR_CLK").extract(i);
string clk_expr = make_expr(clk_sig);
SigSpec wen_sig = cell->getPort("\\WR_EN").extract(i*width, width);
string wen_expr = make_expr(wen_sig[0]);
@ -579,13 +667,50 @@ struct FirrtlWorker
if (wen_sig[0] != wen_sig[i])
log_error("Complex write enable on port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
cell_exprs.push_back(stringf(" %s.w%d.addr <= %s\n", mem_id.c_str(), i, addr_expr.c_str()));
cell_exprs.push_back(stringf(" %s.w%d.data <= %s\n", mem_id.c_str(), i, data_expr.c_str()));
cell_exprs.push_back(stringf(" %s.w%d.en <= %s\n", mem_id.c_str(), i, wen_expr.c_str()));
cell_exprs.push_back(stringf(" %s.w%d.mask <= UInt<1>(1)\n", mem_id.c_str(), i));
cell_exprs.push_back(stringf(" %s.w%d.clk <= asClock(%s)\n", mem_id.c_str(), i, clk_expr.c_str()));
SigSpec mask_sig = RTLIL::SigSpec(RTLIL::State::S1, 1);
write_port wp(name, clk_enable, clk_parity, transparency, clk_sig, wen_sig[0], addr_sig, mask_sig);
m.add_memory_write_port(wp);
cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), name.c_str(), data_expr.c_str()));
cell_exprs.push_back(wp.gen_write(indent.c_str()));
}
register_memory(m);
continue;
}
if (cell->type.in("$memwr", "$memrd", "$meminit"))
{
std::string cell_type = fid(cell->type);
std::string mem_id = make_id(cell->parameters["\\MEMID"].decode_string());
memory *mp = nullptr;
if (cell->type == "$meminit" ) {
log_error("$meminit (%s.%s.%s) currently unsupported\n", log_id(module), log_id(cell), mem_id.c_str());
} else {
// It's a $memwr or $memrd. Remember the read/write port parameters for the eventual FIRRTL memory definition.
auto addrSig = cell->getPort("\\ADDR");
auto dataSig = cell->getPort("\\DATA");
auto enableSig = cell->getPort("\\EN");
auto clockSig = cell->getPort("\\CLK");
Const clk_enable = cell->parameters.at("\\CLK_ENABLE");
Const clk_polarity = cell->parameters.at("\\CLK_POLARITY");
mp = &memories.at(mem_id);
int portNum = 0;
bool transparency = false;
string data_expr = make_expr(dataSig);
if (cell->type.in("$memwr")) {
portNum = (int) mp->write_ports.size();
write_port wp(stringf("%s.w%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig, dataSig);
mp->add_memory_write_port(wp);
cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), wp.name.c_str(), data_expr.c_str()));
cell_exprs.push_back(wp.gen_write(indent.c_str()));
} else if (cell->type.in("$memrd")) {
portNum = (int) mp->read_ports.size();
read_port rp(stringf("%s.r%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig);
mp->add_memory_read_port(rp);
cell_exprs.push_back(rp.gen_read(indent.c_str()));
register_reverse_wire_map(stringf("%s.data", rp.name.c_str()), dataSig);
}
}
continue;
}
@ -763,6 +888,24 @@ struct FirrtlWorker
f << stringf("\n");
// If we have any memory definitions, output them.
for (auto kv : memories) {
memory m = kv.second;
f << stringf(" mem %s:\n", m.name.c_str());
f << stringf(" data-type => UInt<%d>\n", m.width);
f << stringf(" depth => %d\n", m.size);
for (int i = 0; i < (int) m.read_ports.size(); i += 1) {
f << stringf(" reader => r%d\n", i);
}
for (int i = 0; i < (int) m.write_ports.size(); i += 1) {
f << stringf(" writer => w%d\n", i);
}
f << stringf(" read-latency => %d\n", m.read_latency);
f << stringf(" write-latency => %d\n", m.write_latency);
f << stringf(" read-under-write => undefined\n");
}
f << stringf("\n");
for (auto str : cell_exprs)
f << str;

View File

@ -1619,30 +1619,35 @@ struct VerificExtNets
int portname_cnt = 0;
// a map from Net to the same Net one level up in the design hierarchy
std::map<Net*, Net*> net_level_up;
std::map<Net*, Net*> net_level_up_drive_up;
std::map<Net*, Net*> net_level_up_drive_down;
Net *get_net_level_up(Net *net)
Net *route_up(Net *net, bool drive_up, Net *final_net = nullptr)
{
auto &net_level_up = drive_up ? net_level_up_drive_up : net_level_up_drive_down;
if (net_level_up.count(net) == 0)
{
Netlist *nl = net->Owner();
// Simply return if Netlist is not unique
if (nl->NumOfRefs() != 1)
return net;
log_assert(nl->NumOfRefs() == 1);
Instance *up_inst = (Instance*)nl->GetReferences()->GetLast();
Netlist *up_nl = up_inst->Owner();
// create new Port
string name = stringf("___extnets_%d", portname_cnt++);
Port *new_port = new Port(name.c_str(), DIR_OUT);
Port *new_port = new Port(name.c_str(), drive_up ? DIR_OUT : DIR_IN);
nl->Add(new_port);
net->Connect(new_port);
// create new Net in up Netlist
Net *new_net = new Net(name.c_str());
up_nl->Add(new_net);
Net *new_net = final_net;
if (new_net == nullptr || new_net->Owner() != up_nl) {
new_net = new Net(name.c_str());
up_nl->Add(new_net);
}
up_inst->Connect(new_port, new_net);
net_level_up[net] = new_net;
@ -1651,6 +1656,39 @@ struct VerificExtNets
return net_level_up.at(net);
}
Net *route_up(Net *net, bool drive_up, Netlist *dest, Net *final_net = nullptr)
{
while (net->Owner() != dest)
net = route_up(net, drive_up, final_net);
if (final_net != nullptr)
log_assert(net == final_net);
return net;
}
Netlist *find_common_ancestor(Netlist *A, Netlist *B)
{
std::set<Netlist*> ancestors_of_A;
Netlist *cursor = A;
while (1) {
ancestors_of_A.insert(cursor);
if (cursor->NumOfRefs() != 1)
break;
cursor = ((Instance*)cursor->GetReferences()->GetLast())->Owner();
}
cursor = B;
while (1) {
if (ancestors_of_A.count(cursor))
return cursor;
if (cursor->NumOfRefs() != 1)
break;
cursor = ((Instance*)cursor->GetReferences()->GetLast())->Owner();
}
log_error("No common ancestor found between %s and %s.\n", get_full_netlist_name(A).c_str(), get_full_netlist_name(B).c_str());
}
void run(Netlist *nl)
{
MapIter mi, mi2;
@ -1674,19 +1712,37 @@ struct VerificExtNets
if (verific_verbose)
log("Fixing external net reference on port %s.%s.%s:\n", get_full_netlist_name(nl).c_str(), inst->Name(), port->Name());
while (net->IsExternalTo(nl))
{
Net *newnet = get_net_level_up(net);
if (newnet == net) break;
Netlist *ext_nl = net->Owner();
if (verific_verbose)
log(" external net owner: %s\n", get_full_netlist_name(ext_nl).c_str());
Netlist *ca_nl = find_common_ancestor(nl, ext_nl);
if (verific_verbose)
log(" common ancestor: %s\n", get_full_netlist_name(ca_nl).c_str());
Net *ca_net = route_up(net, !port->IsOutput(), ca_nl);
Net *new_net = ca_net;
if (ca_nl != nl)
{
if (verific_verbose)
log(" external net: %s.%s\n", get_full_netlist_name(net->Owner()).c_str(), net->Name());
net = newnet;
log(" net in common ancestor: %s\n", ca_net->Name());
string name = stringf("___extnets_%d", portname_cnt++);
new_net = new Net(name.c_str());
nl->Add(new_net);
Net *n = route_up(new_net, port->IsOutput(), ca_nl, ca_net);
log_assert(n == ca_net);
}
if (verific_verbose)
log(" final net: %s.%s%s\n", get_full_netlist_name(net->Owner()).c_str(), net->Name(), net->IsExternalTo(nl) ? " (external)" : "");
todo_connect.push_back(tuple<Instance*, Port*, Net*>(inst, port, net));
log(" new local net: %s\n", new_net->Name());
log_assert(!new_net->IsExternalTo(nl));
todo_connect.push_back(tuple<Instance*, Port*, Net*>(inst, port, new_net));
}
for (auto it : todo_connect) {
@ -2330,21 +2386,43 @@ struct ReadPass : public Pass {
log("\n");
log("Add directory to global Verilog/SystemVerilog include directories.\n");
log("\n");
log("\n");
log(" read -verific\n");
log(" read -noverific\n");
log("\n");
log("Subsequent calls to 'read' will either use or not use Verific. Calling 'read'\n");
log("with -verific will result in an error on Yosys binaries that are built without\n");
log("Verific support. The default is to use Verific if it is available.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
#ifdef YOSYS_ENABLE_VERIFIC
static bool verific_available = !check_noverific_env();
#else
static bool verific_available = false;
#endif
static bool use_verific = verific_available;
if (args.size() < 2 || args[1][0] != '-')
log_cmd_error("Missing mode parameter.\n");
if (args[1] == "-verific" || args[1] == "-noverific") {
if (args.size() != 2)
log_cmd_error("Additional arguments to -verific/-noverific.\n");
if (args[1] == "-verific") {
if (!verific_available)
log_cmd_error("This version of Yosys is built without Verific support.\n");
use_verific = true;
} else {
use_verific = false;
}
return;
}
if (args.size() < 3)
log_cmd_error("Missing file name parameter.\n");
#ifdef YOSYS_ENABLE_VERIFIC
bool use_verific = !check_noverific_env();
#else
bool use_verific = false;
#endif
if (args[1] == "-vlog95" || args[1] == "-vlog2k") {
if (use_verific) {
args[0] = "verific";

View File

@ -14,6 +14,8 @@ frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l
$(Q) mkdir -p $(dir $@)
$(P) flex -o frontends/verilog/verilog_lexer.cc $<
frontends/verilog/verilog_parser.tab.o: CXXFLAGS += -DYYMAXDEPTH=100000
OBJS += frontends/verilog/verilog_parser.tab.o
OBJS += frontends/verilog/verilog_lexer.o
OBJS += frontends/verilog/preproc.o

View File

@ -24,7 +24,7 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name)
static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name, bool flag_output)
{
from_name = RTLIL::escape_id(from_name);
to_name = RTLIL::escape_id(to_name);
@ -37,13 +37,18 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std::
Wire *w = it.second;
log("Renaming wire %s to %s in module %s.\n", log_id(w), log_id(to_name), log_id(module));
module->rename(w, to_name);
if (w->port_id)
if (w->port_id || flag_output) {
if (flag_output)
w->port_output = true;
module->fixup_ports();
}
return;
}
for (auto &it : module->cells_)
if (it.first == from_name) {
if (flag_output)
log_cmd_error("Called with -output but the specified object is a cell.\n");
log("Renaming cell %s to %s in module %s.\n", log_id(it.second), log_id(to_name), log_id(module));
module->rename(it.second, to_name);
return;
@ -108,15 +113,26 @@ struct RenamePass : public Pass {
log("Rename the specified object. Note that selection patterns are not supported\n");
log("by this command.\n");
log("\n");
log("\n");
log("\n");
log(" rename -output old_name new_name\n");
log("\n");
log("Like above, but also make the wire an output. This will fail if the object is\n");
log("not a wire.\n");
log("\n");
log("\n");
log(" rename -src [selection]\n");
log("\n");
log("Assign names auto-generated from the src attribute to all selected wires and\n");
log("cells with private names.\n");
log("\n");
log("\n");
log(" rename -wire [selection]\n");
log("\n");
log("Assign auto-generated names based on the wires they drive to all selected\n");
log("cells with private names. Ignores cells driving privatly named wires.\n");
log("\n");
log("\n");
log(" rename -enumerate [-pattern <pattern>] [selection]\n");
log("\n");
log("Assign short auto-generated names to all selected wires and cells with private\n");
@ -124,11 +140,13 @@ struct RenamePass : public Pass {
log("The character %% in the pattern is replaced with a integer number. The default\n");
log("pattern is '_%%_'.\n");
log("\n");
log("\n");
log(" rename -hide [selection]\n");
log("\n");
log("Assign private names (the ones with $-prefix) to all selected wires and cells\n");
log("with public names. This ignores all selected ports.\n");
log("\n");
log("\n");
log(" rename -top new_name\n");
log("\n");
log("Rename top module.\n");
@ -142,6 +160,7 @@ struct RenamePass : public Pass {
bool flag_enumerate = false;
bool flag_hide = false;
bool flag_top = false;
bool flag_output = false;
bool got_mode = false;
size_t argidx;
@ -153,6 +172,11 @@ struct RenamePass : public Pass {
got_mode = true;
continue;
}
if (arg == "-output" && !got_mode) {
flag_output = true;
got_mode = true;
continue;
}
if (arg == "-wire" && !got_mode) {
flag_wire = true;
got_mode = true;
@ -322,10 +346,12 @@ struct RenamePass : public Pass {
if (!design->selected_active_module.empty())
{
if (design->modules_.count(design->selected_active_module) > 0)
rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name);
rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name, flag_output);
}
else
{
if (flag_output)
log_cmd_error("Mode -output requires that there is an active module selected.\n");
for (auto &mod : design->modules_) {
if (mod.first == from_name || RTLIL::unescape_id(mod.first) == from_name) {
to_name = RTLIL::escape_id(to_name);

View File

@ -87,6 +87,8 @@ struct UniquifyPass : public Pass {
smod->name = newname;
cell->type = newname;
smod->set_bool_attribute("\\unique");
if (smod->attributes.count("\\hdlname") == 0)
smod->attributes["\\hdlname"] = string(log_id(tmod->name));
design->add(smod);
did_something = true;

View File

@ -641,6 +641,7 @@ grow_read_ports:;
pi.sig_data = SigSpec();
pi.sig_en = SigSpec();
pi.make_outreg = false;
pi.make_transp = false;
}
new_portinfos.push_back(pi);
if (pi.dupidx == dup_count-1) {
@ -956,6 +957,8 @@ grow_read_ports:;
SigSpec addr_ok_q = addr_ok;
if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) {
addr_ok_q = module->addWire(NEW_ID);
if (!pi.sig_en.empty())
addr_ok = module->Mux(NEW_ID, addr_ok_q, addr_ok, pi.sig_en);
module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol);
}

View File

@ -340,6 +340,7 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d
// evaluate in reverse order to give the first entry the top priority
RTLIL::SigSpec initial_val = result;
RTLIL::Cell *last_mux_cell = NULL;
bool shiftx = initial_val.is_fully_undef();
for (size_t i = 0; i < sw->cases.size(); i++) {
int case_idx = sw->cases.size() - i - 1;
RTLIL::CaseRule *cs2 = sw->cases[case_idx];
@ -348,6 +349,33 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d
append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, ifxmode);
else
result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, ifxmode);
// Ignore output values which are entirely don't care
if (shiftx && !value.is_fully_undef()) {
// Keep checking if case condition is the same as the current case index
if (cs2->compare.size() == 1 && cs2->compare.front().is_fully_const())
shiftx = (cs2->compare.front().as_int() == case_idx);
else
shiftx = false;
}
}
// Transform into a $shiftx where possible
if (shiftx && last_mux_cell->type == "$pmux") {
// Create bit-blasted $shiftx-es that shifts by the address line used in the case statement
auto pmux_b_port = last_mux_cell->getPort("\\B");
auto pmux_y_port = last_mux_cell->getPort("\\Y");
int width = last_mux_cell->getParam("\\WIDTH").as_int();
for (int i = 0; i < width; ++i) {
RTLIL::SigSpec a_port;
// Because we went in reverse order above, un-reverse $pmux's B port here
for (int j = pmux_b_port.size()/width-1; j >= 0; --j)
a_port.append(pmux_b_port.extract(j*width+i, 1));
// Create a $shiftx that shifts by the address line used in the case statement
mod->addShiftx(NEW_ID, a_port, sw->signal, pmux_y_port.extract(i, 1));
}
// Disconnect $pmux by replacing its output port with a floating wire
last_mux_cell->setPort("\\Y", mod->addWire(NEW_ID, width));
}
}

View File

@ -33,20 +33,24 @@ struct CutpointPass : public Pass {
log("\n");
log("This command adds formal cut points to the design.\n");
log("\n");
log(" -undef\n");
log(" set cupoint nets to undef (x). the default behavior is to create a\n");
log(" $anyseq cell and drive the cutpoint net from that\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
// bool flag_noinit = false;
bool flag_undef = false;
log_header(design, "Executing CUTPOINT pass.\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
// if (args[argidx] == "-noinit") {
// flag_noinit = true;
// continue;
// }
if (args[argidx] == "-undef") {
flag_undef = true;
continue;
}
break;
}
extra_args(args, argidx, design);
@ -63,7 +67,7 @@ struct CutpointPass : public Pass {
if (wire->port_output)
output_wires.push_back(wire);
for (auto wire : output_wires)
module->connect(wire, module->Anyseq(NEW_ID, GetSize(wire)));
module->connect(wire, flag_undef ? Const(State::Sx, GetSize(wire)) : module->Anyseq(NEW_ID, GetSize(wire)));
continue;
}
@ -76,7 +80,7 @@ struct CutpointPass : public Pass {
log("Removing cell %s.%s, making all cell outputs cutpoints.\n", log_id(module), log_id(cell));
for (auto &conn : cell->connections()) {
if (cell->output(conn.first))
module->connect(conn.second, module->Anyseq(NEW_ID, GetSize(conn.second)));
module->connect(conn.second, flag_undef ? Const(State::Sx, GetSize(conn.second)) : module->Anyseq(NEW_ID, GetSize(conn.second)));
}
module->remove(cell);
}
@ -86,7 +90,7 @@ struct CutpointPass : public Pass {
log("Making output wire %s.%s a cutpoint.\n", log_id(module), log_id(wire));
Wire *new_wire = module->addWire(NEW_ID, wire);
module->swap_names(wire, new_wire);
module->connect(new_wire, module->Anyseq(NEW_ID, GetSize(new_wire)));
module->connect(new_wire, flag_undef ? Const(State::Sx, GetSize(new_wire)) : module->Anyseq(NEW_ID, GetSize(new_wire)));
wire->port_id = 0;
wire->port_input = false;
wire->port_output = false;
@ -142,7 +146,7 @@ struct CutpointPass : public Pass {
rhs.append(SigBit(new_wire, i));
}
if (GetSize(lhs))
module->connect(lhs, rhs);
module->connect(lhs, rhs);
module->swap_names(wire, new_wire);
wire->port_id = 0;
wire->port_input = false;
@ -154,7 +158,7 @@ struct CutpointPass : public Pass {
for (auto chunk : sig.chunks()) {
SigSpec s(chunk);
module->connect(s, module->Anyseq(NEW_ID, GetSize(s)));
module->connect(s, flag_undef ? Const(State::Sx, GetSize(s)) : module->Anyseq(NEW_ID, GetSize(s)));
}
}
}

View File

@ -155,11 +155,13 @@ int LibertyParser::lexer(std::string &str)
// check for a backslash
if (c == '\\') {
c = f.get();
c = f.get();
if (c == '\r')
c = f.get();
if (c == '\n')
if (c == '\n') {
line++;
return lexer(str);
}
f.unget();
return '\\';
}
@ -186,14 +188,39 @@ LibertyAst *LibertyParser::parse()
int tok = lexer(str);
while (tok == 'n')
// there are liberty files in the wild that
// have superfluous ';' at the end of
// a { ... }. We simply ignore a ';' here.
// and get to the next statement.
while ((tok == 'n') || (tok == ';'))
tok = lexer(str);
if (tok == '}' || tok < 0)
return NULL;
if (tok != 'v')
error();
if (tok != 'v') {
std::string eReport;
switch(tok)
{
case 'n':
error("Unexpected newline.");
break;
case '[':
case ']':
case '}':
case '{':
case '\"':
case ':':
eReport = "Unexpected '";
eReport += static_cast<char>(tok);
eReport += "'.";
error(eReport);
break;
default:
error();
}
}
LibertyAst *ast = new LibertyAst;
ast->id = str;
@ -282,8 +309,28 @@ LibertyAst *LibertyParser::parse()
}
continue;
}
if (tok != 'v')
error();
if (tok != 'v') {
std::string eReport;
switch(tok)
{
case 'n':
error("Unexpected newline.");
break;
case '[':
case ']':
case '}':
case '{':
case '\"':
case ':':
eReport = "Unexpected '";
eReport += static_cast<char>(tok);
eReport += "'.";
error(eReport);
break;
default:
error();
}
}
ast->args.push_back(arg);
}
continue;

View File

@ -142,8 +142,7 @@ library(supergate) {
}
/* D-type flip-flop with asynchronous reset and preset */
cell (dff)
{
cell (dff) {
area : 6;
ff("IQ", "IQN") {
next_state : "D";

View File

@ -0,0 +1,48 @@
/********************************************/
/* */
/* Supergate cell library for Bench marking */
/* */
/* Symbiotic EDA GmbH / Moseley Instruments */
/* Niels A. Moseley */
/* */
/* Process: none */
/* */
/* Date : 25-03-2019 */
/* Version: 1.0 */
/* */
/********************************************/
library(processdefs) {
technology (cmos);
revision : 1.0;
time_unit : "1ps";
pulling_resistance_unit : "1kohm";
voltage_unit : "1V";
current_unit : "1uA";
capacitive_load_unit(1,ff);
default_inout_pin_cap : 7.0;
default_input_pin_cap : 7.0;
default_output_pin_cap : 0.0;
default_fanout_load : 1.0;
default_wire_load_capacitance : 0.1;
default_wire_load_resistance : 1.0e-3;
default_wire_load_area : 0.0;
nom_process : 1.0;
nom_temperature : 25.0;
nom_voltage : 1.2;
delay_model : generic_cmos;
define_cell_area(bond_pads,pad_slots)
input_voltage(cmos) {
vil : 0.3 * VDD ;
vih : 0.7 * VDD ;
vimin : -0.5 ;
vimax : VDD + 0.5 ;
}
}

View File

@ -0,0 +1,48 @@
/*
Test case for https://www.reddit.com/r/yosys/comments/b5texg/yosys_fails_to_parse_apparentlycorrect_liberty/
fall_constraint (SETUP_HOLD) formatting.
*/
library(supergate) {
technology (cmos);
revision : 1.0;
cell (DFF) {
cell_footprint : dff;
area : 50;
pin(D) {
direction : input;
capacitance : 0.002;
timing() {
related_pin : "CK";
timing_type : setup_rising;
fall_constraint (SETUP_HOLD) { values ("0.4000, 0.3000, 0.2000, 0.1000, 0.0000", \
"0.4000, 0.3000, 0.2000, 0.1000, 0.000", \
"0.5000, 0.4000, 0.3000, 0.2000, 0.0000", \
"0.7000, 0.6000, 0.5000, 0.4000, 0.2000", \
"1.0000, 1.0000, 0.9000, 0.8000, 0.6000"); };
}
}
pin(CK) {
direction : input;
clock : true;
capacitance : 0.00290;
}
ff(IQ,IQN) {
clocked_on : "CK";
next_state : "D";
}
pin(Q) {
direction : output;
capacitance : 0.003;
max_capacitance : 0.3;
}
cell_leakage_power : 0.3;
}
}

22
tests/sva/extnets.sv Normal file
View File

@ -0,0 +1,22 @@
module top(input i, output o);
A A();
B B();
assign A.i = i;
assign o = B.o;
always @* assert(o == i);
endmodule
module A;
wire i, y;
`ifdef FAIL
assign B.x = i;
`else
assign B.x = !i;
`endif
assign y = !B.y;
endmodule
module B;
wire x, y, o;
assign y = x, o = A.y;
endmodule