mirror of https://github.com/YosysHQ/yosys.git
Merge remote-tracking branch 'origin/master' into xc7srl
This commit is contained in:
commit
d8465590ac
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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 ;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue