mirror of
https://github.com/YosysHQ/yosys
synced 2026-02-12 20:04:11 +00:00
Merge remote-tracking branch 'origin/main' into support-parameter-default-values-in-json-frontend-and-verilog-backend
This commit is contained in:
commit
5b7d436851
1758 changed files with 182731 additions and 54865 deletions
|
|
@ -284,7 +284,7 @@ end_of_header:
|
|||
if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size()))
|
||||
log_error("Line %u has invalid symbol position!\n", line_count);
|
||||
|
||||
RTLIL::IdString escaped_s = stringf("\\%s", s.c_str());
|
||||
RTLIL::IdString escaped_s = stringf("\\%s", s);
|
||||
RTLIL::Wire* wire;
|
||||
if (c == 'i') wire = inputs[l1];
|
||||
else if (c == 'l') wire = latches[l1];
|
||||
|
|
@ -448,7 +448,7 @@ void AigerReader::parse_xaiger()
|
|||
bool success = ce.eval(o);
|
||||
log_assert(success);
|
||||
log_assert(o.wire == nullptr);
|
||||
lut_mask[gray] = o.data;
|
||||
lut_mask.set(gray, o.data);
|
||||
}
|
||||
RTLIL::Cell *output_cell = module->cell(stringf("$and$aiger%d$%d", aiger_autoidx, rootNodeID));
|
||||
log_assert(output_cell);
|
||||
|
|
@ -476,7 +476,7 @@ void AigerReader::parse_xaiger()
|
|||
else if (c == 'n') {
|
||||
parse_xaiger_literal(f);
|
||||
f >> s;
|
||||
log_debug("n: '%s'\n", s.c_str());
|
||||
log_debug("n: '%s'\n", s);
|
||||
}
|
||||
else if (c == 'h') {
|
||||
f.ignore(sizeof(uint32_t));
|
||||
|
|
@ -590,6 +590,7 @@ void AigerReader::parse_aiger_ascii()
|
|||
for (unsigned i = 0; i < O; ++i, ++line_count) {
|
||||
if (!(f >> l1))
|
||||
log_error("Line %u cannot be interpreted as an output!\n", line_count);
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
log_debug2("%d is an output\n", l1);
|
||||
RTLIL::Wire *wire = module->addWire(stringf("$o%0*d", digits, i));
|
||||
|
|
@ -597,20 +598,18 @@ void AigerReader::parse_aiger_ascii()
|
|||
module->connect(wire, createWireIfNotExists(module, l1));
|
||||
outputs.push_back(wire);
|
||||
}
|
||||
//std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// Parse bad properties
|
||||
for (unsigned i = 0; i < B; ++i, ++line_count) {
|
||||
if (!(f >> l1))
|
||||
log_error("Line %u cannot be interpreted as a bad state property!\n", line_count);
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
log_debug2("%d is a bad state property\n", l1);
|
||||
RTLIL::Wire *wire = createWireIfNotExists(module, l1);
|
||||
wire->port_output = true;
|
||||
bad_properties.push_back(wire);
|
||||
}
|
||||
//if (B > 0)
|
||||
// std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// TODO: Parse invariant constraints
|
||||
for (unsigned i = 0; i < C; ++i, ++line_count)
|
||||
|
|
@ -628,6 +627,7 @@ void AigerReader::parse_aiger_ascii()
|
|||
for (unsigned i = 0; i < A; ++i) {
|
||||
if (!(f >> l1 >> l2 >> l3))
|
||||
log_error("Line %u cannot be interpreted as an AND!\n", line_count);
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
log_debug2("%d %d %d is an AND\n", l1, l2, l3);
|
||||
log_assert(!(l1 & 1));
|
||||
|
|
@ -636,7 +636,6 @@ void AigerReader::parse_aiger_ascii()
|
|||
RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
|
||||
module->addAndGate("$and" + o_wire->name.str(), i1_wire, i2_wire, o_wire);
|
||||
}
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
}
|
||||
|
||||
static unsigned parse_next_delta_literal(std::istream &f, unsigned ref)
|
||||
|
|
@ -715,6 +714,7 @@ void AigerReader::parse_aiger_binary()
|
|||
for (unsigned i = 0; i < O; ++i, ++line_count) {
|
||||
if (!(f >> l1))
|
||||
log_error("Line %u cannot be interpreted as an output!\n", line_count);
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
log_debug2("%d is an output\n", l1);
|
||||
RTLIL::Wire *wire = module->addWire(stringf("$o%0*d", digits, i));
|
||||
|
|
@ -722,20 +722,18 @@ void AigerReader::parse_aiger_binary()
|
|||
module->connect(wire, createWireIfNotExists(module, l1));
|
||||
outputs.push_back(wire);
|
||||
}
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// Parse bad properties
|
||||
for (unsigned i = 0; i < B; ++i, ++line_count) {
|
||||
if (!(f >> l1))
|
||||
log_error("Line %u cannot be interpreted as a bad state property!\n", line_count);
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
log_debug2("%d is a bad state property\n", l1);
|
||||
RTLIL::Wire *wire = createWireIfNotExists(module, l1);
|
||||
wire->port_output = true;
|
||||
bad_properties.push_back(wire);
|
||||
}
|
||||
if (B > 0)
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// TODO: Parse invariant constraints
|
||||
for (unsigned i = 0; i < C; ++i, ++line_count)
|
||||
|
|
@ -832,7 +830,7 @@ void AigerReader::post_process()
|
|||
log_debug(" -> %s\n", log_id(escaped_s));
|
||||
}
|
||||
else {
|
||||
RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s.c_str(), index);
|
||||
RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s, index);
|
||||
existing = module->wire(indexed_name);
|
||||
if (!existing)
|
||||
module->rename(wire, indexed_name);
|
||||
|
|
@ -879,7 +877,7 @@ void AigerReader::post_process()
|
|||
log_debug(" -> %s\n", log_id(escaped_s));
|
||||
}
|
||||
else {
|
||||
RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s.c_str(), index);
|
||||
RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s, index);
|
||||
existing = module->wire(indexed_name);
|
||||
if (!existing)
|
||||
module->rename(wire, indexed_name);
|
||||
|
|
@ -911,7 +909,7 @@ void AigerReader::post_process()
|
|||
module->rename(cell, escaped_s);
|
||||
}
|
||||
else
|
||||
log_error("Symbol type '%s' not recognised.\n", type.c_str());
|
||||
log_error("Symbol type '%s' not recognised.\n", type);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -924,7 +922,7 @@ void AigerReader::post_process()
|
|||
|
||||
RTLIL::Wire *wire = module->wire(name);
|
||||
if (wire)
|
||||
module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", name.c_str(), 0)));
|
||||
module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", name, 0)));
|
||||
|
||||
// Do not make ports with a mix of input/output into
|
||||
// wide ports
|
||||
|
|
@ -944,7 +942,7 @@ void AigerReader::post_process()
|
|||
wire->port_output = port_output;
|
||||
|
||||
for (int i = min; i <= max; i++) {
|
||||
RTLIL::IdString other_name = stringf("%s[%d]", name.c_str(), i);
|
||||
RTLIL::IdString other_name = stringf("%s[%d]", name, i);
|
||||
RTLIL::Wire *other_wire = module->wire(other_name);
|
||||
if (other_wire) {
|
||||
other_wire->port_input = false;
|
||||
|
|
@ -973,9 +971,9 @@ void AigerReader::post_process()
|
|||
if (cell->type != ID($lut)) continue;
|
||||
auto y_port = cell->getPort(ID::Y).as_bit();
|
||||
if (y_port.wire->width == 1)
|
||||
module->rename(cell, stringf("$lut%s", y_port.wire->name.c_str()));
|
||||
module->rename(cell, stringf("$lut%s", y_port.wire->name));
|
||||
else
|
||||
module->rename(cell, stringf("$lut%s[%d]", y_port.wire->name.c_str(), y_port.offset));
|
||||
module->rename(cell, stringf("$lut%s[%d]", y_port.wire->name, y_port.offset));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
2
frontends/aiger2/Makefile.inc
Normal file
2
frontends/aiger2/Makefile.inc
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
OBJS += frontends/aiger2/xaiger.o
|
||||
470
frontends/aiger2/xaiger.cc
Normal file
470
frontends/aiger2/xaiger.cc
Normal file
|
|
@ -0,0 +1,470 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 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/register.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
uint32_t read_be32(std::istream &f) {
|
||||
return ((uint32_t) f.get() << 24) |
|
||||
((uint32_t) f.get() << 16) |
|
||||
((uint32_t) f.get() << 8) | (uint32_t) f.get();
|
||||
}
|
||||
|
||||
IdString read_idstring(std::istream &f)
|
||||
{
|
||||
std::string str;
|
||||
std::getline(f, str, '\0');
|
||||
if (!f.good())
|
||||
log_error("failed to read string\n");
|
||||
return RTLIL::escape_id(str);
|
||||
}
|
||||
|
||||
struct Xaiger2Frontend : public Frontend {
|
||||
Xaiger2Frontend() : Frontend("xaiger2", "(experimental) read XAIGER file")
|
||||
{
|
||||
experimental();
|
||||
}
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" read_xaiger2 -sc_mapping [options] <filename>\n");
|
||||
log("\n");
|
||||
log("Read a standard cell mapping from a XAIGER file into an existing module.\n");
|
||||
log("\n");
|
||||
log(" -module_name <name>\n");
|
||||
log(" name of the target module\n");
|
||||
log("\n");
|
||||
log(" -map2 <filename>\n");
|
||||
log(" read file with symbol information\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void read_sc_mapping(std::istream *&f, std::string filename, std::vector<std::string> args, Design *design)
|
||||
{
|
||||
IdString module_name;
|
||||
std::string map_filename;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 2; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-module_name" && argidx + 1 < args.size()) {
|
||||
module_name = RTLIL::escape_id(args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
if (arg == "-map2" && argidx + 1 < args.size()) {
|
||||
map_filename = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx, true);
|
||||
|
||||
if (map_filename.empty())
|
||||
log_error("A '-map2' argument required\n");
|
||||
if (module_name.empty())
|
||||
log_error("A '-module_name' argument required\n");
|
||||
|
||||
Module *module = design->module(module_name);
|
||||
if (!module)
|
||||
log_error("Module '%s' not found\n", log_id(module_name));
|
||||
|
||||
std::ifstream map_file;
|
||||
map_file.open(map_filename);
|
||||
if (!map_file)
|
||||
log_error("Failed to open map file '%s'\n", map_filename);
|
||||
|
||||
unsigned int M, I, L, O, A;
|
||||
std::string header;
|
||||
if (!(*f >> header >> M >> I >> L >> O >> A) || header != "aig")
|
||||
log_error("Bad header\n");
|
||||
std::string line;
|
||||
std::getline(*f, line);
|
||||
log_debug("M=%u I=%u L=%u O=%u A=%u\n", M, I, L, O, A);
|
||||
|
||||
if (L != 0)
|
||||
log_error("Latches unsupported\n");
|
||||
if (I + L + A != M)
|
||||
log_error("Inconsistent header\n");
|
||||
|
||||
std::vector<int> outputs;
|
||||
for (int i = 0; i < (int) O; i++) {
|
||||
int po;
|
||||
*f >> po;
|
||||
int c = f->get();
|
||||
log_assert(c == '\n');
|
||||
outputs.push_back(po);
|
||||
}
|
||||
|
||||
std::vector<std::pair<Cell *, Module *>> boxes;
|
||||
std::vector<bool> retained_boxes;
|
||||
std::vector<SigBit> bits(2 + 2*M, RTLIL::Sm);
|
||||
bits[0] = RTLIL::S0;
|
||||
bits[1] = RTLIL::S1;
|
||||
|
||||
std::string type;
|
||||
while (map_file >> type) {
|
||||
if (type == "pi") {
|
||||
int pi_idx;
|
||||
int woffset;
|
||||
std::string name;
|
||||
if (!(map_file >> pi_idx >> woffset >> name))
|
||||
log_error("Bad map file (1)\n");
|
||||
int lit = (2 * pi_idx) + 2;
|
||||
if (lit < 0 || lit >= (int) bits.size())
|
||||
log_error("Bad map file (2)\n");
|
||||
Wire *w = module->wire(name);
|
||||
if (!w || woffset < 0 || woffset >= w->width)
|
||||
log_error("Map file references non-existent signal bit %s[%d]\n",
|
||||
name.c_str(), woffset);
|
||||
bits[lit] = SigBit(w, woffset);
|
||||
} else if (type == "box") {
|
||||
int box_seq;
|
||||
std::string name;
|
||||
if (!(map_file >> box_seq >> name))
|
||||
log_error("Bad map file (20)\n");
|
||||
if (box_seq < 0)
|
||||
log_error("Bad map file (21)\n");
|
||||
|
||||
Cell *box = module->cell(RTLIL::escape_id(name));
|
||||
if (!box)
|
||||
log_error("Map file references non-existent box %s\n",
|
||||
name.c_str());
|
||||
|
||||
Module *def = design->module(box->type);
|
||||
if (def && !box->parameters.empty()) {
|
||||
// TODO: This is potentially costly even if a cached derivation exists
|
||||
def = design->module(def->derive(design, box->parameters));
|
||||
log_assert(def);
|
||||
}
|
||||
|
||||
if (!def)
|
||||
log_error("Bad map file (22)\n");
|
||||
|
||||
if (box_seq >= (int) boxes.size()) {
|
||||
boxes.resize(box_seq + 1);
|
||||
retained_boxes.resize(box_seq + 1);
|
||||
}
|
||||
boxes[box_seq] = std::make_pair(box, def);
|
||||
} else {
|
||||
std::string scratch;
|
||||
std::getline(map_file, scratch);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int) A; i++) {
|
||||
while (f->get() & 0x80 && !f->eof());
|
||||
while (f->get() & 0x80 && !f->eof());
|
||||
}
|
||||
|
||||
if (f->get() != 'c')
|
||||
log_error("Missing 'c' ahead of extensions\n");
|
||||
if (f->peek() == '\n')
|
||||
f->get();
|
||||
auto extensions_start = f->tellg();
|
||||
|
||||
log_debug("reading 'h' (first pass)\n");
|
||||
for (int c = f->get(); c != EOF; c = f->get()) {
|
||||
if (c == 'h') {
|
||||
uint32_t len, ci_num, co_num, pi_num, po_num, no_boxes;
|
||||
len = read_be32(*f);
|
||||
read_be32(*f);
|
||||
ci_num = read_be32(*f);
|
||||
co_num = read_be32(*f);
|
||||
pi_num = read_be32(*f);
|
||||
po_num = read_be32(*f);
|
||||
no_boxes = read_be32(*f);
|
||||
|
||||
log_debug("len=%u ci_num=%u co_num=%u pi_num=%u po_nun=%u no_boxes=%u\n",
|
||||
len, ci_num, co_num, pi_num, po_num, no_boxes);
|
||||
|
||||
int ci_counter = 0;
|
||||
for (uint32_t i = 0; i < no_boxes; i++) {
|
||||
/* unused box_inputs = */ read_be32(*f);
|
||||
YS_MAYBE_UNUSED auto box_outputs = read_be32(*f);
|
||||
/* unused box_id = */ read_be32(*f);
|
||||
auto box_seq = read_be32(*f);
|
||||
|
||||
log_assert(box_seq < boxes.size());
|
||||
|
||||
auto [cell, def] = boxes[box_seq];
|
||||
log_assert(cell && def);
|
||||
retained_boxes[box_seq] = true;
|
||||
|
||||
int box_ci_idx = 0;
|
||||
for (auto port_id : def->ports) {
|
||||
Wire *port = def->wire(port_id);
|
||||
if (port->port_output) {
|
||||
if (!cell->hasPort(port_id) || cell->getPort(port_id).size() != port->width)
|
||||
log_error("Malformed design (1)\n");
|
||||
|
||||
SigSpec &conn = cell->connections_[port_id];
|
||||
for (int j = 0; j < port->width; j++) {
|
||||
if (conn[j].wire && conn[j].wire->port_output)
|
||||
conn[j] = module->addWire(module->uniquify(
|
||||
stringf("$box$%s$%s$%d",
|
||||
cell->name.isPublic() ? cell->name.c_str() + 1 : cell->name.c_str(),
|
||||
port_id.isPublic() ? port_id.c_str() + 1 : port_id.c_str(),
|
||||
j)));
|
||||
|
||||
bits[2*(pi_num + ci_counter + box_ci_idx++) + 2] = conn[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_assert(box_ci_idx == (int) box_outputs);
|
||||
ci_counter += box_ci_idx;
|
||||
}
|
||||
log_assert(pi_num + ci_counter == ci_num);
|
||||
} else if (c == '\n') {
|
||||
break;
|
||||
} else if (c == 'c') {
|
||||
break;
|
||||
} else {
|
||||
uint32_t len = read_be32(*f);
|
||||
f->ignore(len);
|
||||
log_debug(" section '%c' (%d): ignoring %d bytes\n", c, c, len);
|
||||
}
|
||||
}
|
||||
|
||||
log_debug("reading 'M' (second pass)\n");
|
||||
|
||||
f->seekg(extensions_start);
|
||||
bool read_mapping = false;
|
||||
uint32_t no_cells, no_instances;
|
||||
for (int c = f->get(); c != EOF; c = f->get()) {
|
||||
if (c == 'M') {
|
||||
uint32_t len = read_be32(*f);
|
||||
read_mapping = true;
|
||||
|
||||
no_cells = read_be32(*f);
|
||||
no_instances = read_be32(*f);
|
||||
|
||||
log_debug("M: len=%u no_cells=%u no_instances=%u\n", len, no_cells, no_instances);
|
||||
|
||||
struct MappingCell {
|
||||
RTLIL::IdString type;
|
||||
RTLIL::IdString out;
|
||||
std::vector<RTLIL::IdString> ins;
|
||||
};
|
||||
std::vector<MappingCell> cells;
|
||||
cells.resize(no_cells);
|
||||
|
||||
for (unsigned i = 0; i < no_cells; ++i) {
|
||||
auto &cell = cells[i];
|
||||
cell.type = read_idstring(*f);
|
||||
cell.out = read_idstring(*f);
|
||||
uint32_t nins = read_be32(*f);
|
||||
for (uint32_t j = 0; j < nins; j++)
|
||||
cell.ins.push_back(read_idstring(*f));
|
||||
log_debug("M: Cell %s (out %s, ins", log_id(cell.type), log_id(cell.out));
|
||||
for (auto in : cell.ins)
|
||||
log_debug(" %s", log_id(in));
|
||||
log_debug(")\n");
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < no_instances; ++i) {
|
||||
uint32_t cell_id = read_be32(*f);
|
||||
uint32_t out_lit = read_be32(*f);
|
||||
|
||||
log_assert(out_lit < bits.size());
|
||||
log_assert(bits[out_lit] == RTLIL::Sm);
|
||||
log_assert(cell_id < cells.size());
|
||||
auto &cell = cells[cell_id];
|
||||
Cell *instance = module->addCell(module->uniquify(stringf("$sc%d", out_lit)), cell.type);
|
||||
auto out_w = module->addWire(module->uniquify(stringf("$lit%d", out_lit)));
|
||||
instance->setPort(cell.out, out_w);
|
||||
bits[out_lit] = out_w;
|
||||
for (auto in : cell.ins) {
|
||||
uint32_t in_lit = read_be32(*f);
|
||||
log_assert(out_lit < bits.size());
|
||||
log_assert(bits[in_lit] != RTLIL::Sm);
|
||||
instance->setPort(in, bits[in_lit]);
|
||||
}
|
||||
}
|
||||
} else if (c == '\n') {
|
||||
break;
|
||||
} else if (c == 'c') {
|
||||
break;
|
||||
} else {
|
||||
uint32_t len = read_be32(*f);
|
||||
f->ignore(len);
|
||||
log_debug(" section '%c' (%d): ignoring %d bytes\n", c, c, len);
|
||||
}
|
||||
}
|
||||
|
||||
if (!read_mapping)
|
||||
log_error("Missing mapping (no 'M' section)\n");
|
||||
|
||||
log("Read %d instances with cell library of size %d.\n",
|
||||
no_instances, no_cells);
|
||||
|
||||
f->seekg(extensions_start);
|
||||
log_debug("reading 'h' (second pass)\n");
|
||||
int co_counter = 0;
|
||||
for (int c = f->get(); c != EOF; c = f->get()) {
|
||||
if (c == 'h') {
|
||||
uint32_t len, ci_num, co_num, pi_num, po_num, no_boxes;
|
||||
len = read_be32(*f);
|
||||
read_be32(*f);
|
||||
ci_num = read_be32(*f);
|
||||
co_num = read_be32(*f);
|
||||
pi_num = read_be32(*f);
|
||||
po_num = read_be32(*f);
|
||||
no_boxes = read_be32(*f);
|
||||
|
||||
log_debug("len=%u ci_num=%u co_num=%u pi_num=%u po_nun=%u no_boxes=%u\n",
|
||||
len, ci_num, co_num, pi_num, po_num, no_boxes);
|
||||
|
||||
for (uint32_t i = 0; i < no_boxes; i++) {
|
||||
YS_MAYBE_UNUSED auto box_inputs = read_be32(*f);
|
||||
/* unused box_outputs = */ read_be32(*f);
|
||||
/* unused box_id = */ read_be32(*f);
|
||||
auto box_seq = read_be32(*f);
|
||||
|
||||
log_assert(box_seq < boxes.size());
|
||||
|
||||
auto [cell, def] = boxes[box_seq];
|
||||
log_assert(cell && def);
|
||||
|
||||
int box_co_idx = 0;
|
||||
for (auto port_id : def->ports) {
|
||||
Wire *port = def->wire(port_id);
|
||||
SigSpec conn;
|
||||
if (port->port_input) {
|
||||
if (!cell->hasPort(port_id) || cell->getPort(port_id).size() != port->width)
|
||||
log_error("Malformed design (2)\n");
|
||||
|
||||
SigSpec conn;
|
||||
for (int j = 0; j < port->width; j++) {
|
||||
log_assert(co_counter + box_co_idx < (int) outputs.size());
|
||||
int lit = outputs[co_counter + box_co_idx++];
|
||||
log_assert(lit >= 0 && lit < (int) bits.size());
|
||||
SigBit bit = bits[lit];
|
||||
if (bit == RTLIL::Sm)
|
||||
log_error("Malformed mapping (1)\n");
|
||||
conn.append(bit);
|
||||
}
|
||||
cell->setPort(port_id, conn);
|
||||
}
|
||||
}
|
||||
|
||||
log_assert(box_co_idx == (int) box_inputs);
|
||||
co_counter += box_co_idx;
|
||||
}
|
||||
log_assert(po_num + co_counter == co_num);
|
||||
} else if (c == '\n') {
|
||||
break;
|
||||
} else if (c == 'c') {
|
||||
break;
|
||||
} else {
|
||||
uint32_t len = read_be32(*f);
|
||||
f->ignore(len);
|
||||
log_debug(" section '%c' (%d): ignoring %d bytes\n", c, c, len);
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
std::string scratch;
|
||||
std::getline(*f, scratch);
|
||||
if (f->eof())
|
||||
break;
|
||||
log_assert(!f->fail());
|
||||
log("input file: %s\n", scratch);
|
||||
}
|
||||
|
||||
log_debug("co_counter=%d\n", co_counter);
|
||||
|
||||
// TODO: seek without close/open
|
||||
map_file.close();
|
||||
map_file.open(map_filename);
|
||||
while (map_file >> type) {
|
||||
if (type == "po") {
|
||||
int po_idx;
|
||||
int woffset;
|
||||
std::string name;
|
||||
if (!(map_file >> po_idx >> woffset >> name))
|
||||
log_error("Bad map file (3)\n");
|
||||
po_idx += co_counter;
|
||||
if (po_idx < 0 || po_idx >= (int) outputs.size())
|
||||
log_error("Bad map file (4)\n");
|
||||
int lit = outputs[po_idx];
|
||||
if (lit < 0 || lit >= (int) bits.size())
|
||||
log_error("Bad map file (5)\n");
|
||||
if (bits[lit] == RTLIL::Sm)
|
||||
log_error("Bad map file (6)\n");
|
||||
Wire *w = module->wire(name);
|
||||
if (!w || woffset < 0 || woffset >= w->width)
|
||||
log_error("Map file references non-existent signal bit %s[%d]\n",
|
||||
name.c_str(), woffset);
|
||||
module->connect(SigBit(w, woffset), bits[lit]);
|
||||
} else if (type == "pseudopo") {
|
||||
int po_idx;
|
||||
int poffset;
|
||||
std::string box_name;
|
||||
std::string box_port;
|
||||
if (!(map_file >> po_idx >> poffset >> box_name >> box_port))
|
||||
log_error("Bad map file (7)\n");
|
||||
po_idx += co_counter;
|
||||
if (po_idx < 0 || po_idx >= (int) outputs.size())
|
||||
log_error("Bad map file (8)\n");
|
||||
int lit = outputs[po_idx];
|
||||
if (lit < 0 || lit >= (int) bits.size())
|
||||
log_error("Bad map file (9)\n");
|
||||
if (bits[lit] == RTLIL::Sm)
|
||||
log_error("Bad map file (10)\n");
|
||||
Cell *cell = module->cell(box_name);
|
||||
if (!cell || !cell->hasPort(box_port))
|
||||
log_error("Map file references non-existent box port %s/%s\n",
|
||||
box_name.c_str(), box_port.c_str());
|
||||
SigSpec &port = cell->connections_[box_port];
|
||||
if (poffset < 0 || poffset >= port.size())
|
||||
log_error("Map file references non-existent box port bit %s/%s[%d]\n",
|
||||
box_name.c_str(), box_port.c_str(), poffset);
|
||||
port[poffset] = bits[lit];
|
||||
} else {
|
||||
std::string scratch;
|
||||
std::getline(map_file, scratch);
|
||||
}
|
||||
}
|
||||
|
||||
int box_seq = 0;
|
||||
for (auto [cell, def] : boxes) {
|
||||
if (!retained_boxes[box_seq++])
|
||||
module->remove(cell);
|
||||
}
|
||||
}
|
||||
|
||||
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, Design *design) override
|
||||
{
|
||||
log_header(design, "Executing XAIGER2 frontend.\n");
|
||||
|
||||
if (args.size() > 1 && args[1] == "-sc_mapping") {
|
||||
read_sc_mapping(f, filename, args, design);
|
||||
return;
|
||||
}
|
||||
|
||||
log_cmd_error("Mode '-sc_mapping' must be selected\n");
|
||||
}
|
||||
} Xaiger2Frontend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -17,12 +17,9 @@
|
|||
*
|
||||
* ---
|
||||
*
|
||||
* This is the AST frontend library.
|
||||
*
|
||||
* The AST frontend library is not a frontend on it's own but provides a
|
||||
* generic abstract syntax tree (AST) abstraction for HDL code and can be
|
||||
* used by HDL frontends. See "ast.h" for an overview of the API and the
|
||||
* Verilog frontend for an usage example.
|
||||
* The AST frontend library is not a frontend on its own but provides an
|
||||
* abstract syntax tree (AST) abstraction for the open source Verilog frontend
|
||||
* at frontends/verilog.
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
@ -30,6 +27,8 @@
|
|||
#define AST_H
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/fmt.h"
|
||||
#include "frontends/verilog/verilog_location.h"
|
||||
#include <stdint.h>
|
||||
#include <set>
|
||||
|
||||
|
|
@ -155,6 +154,7 @@ namespace AST
|
|||
AST_MODPORT,
|
||||
AST_MODPORTMEMBER,
|
||||
AST_PACKAGE,
|
||||
AST_IMPORT,
|
||||
|
||||
AST_WIRETYPE,
|
||||
AST_TYPEDEF,
|
||||
|
|
@ -164,12 +164,7 @@ namespace AST
|
|||
AST_BIND
|
||||
};
|
||||
|
||||
struct AstSrcLocType {
|
||||
unsigned int first_line, last_line;
|
||||
unsigned int first_column, last_column;
|
||||
AstSrcLocType() : first_line(0), last_line(0), first_column(0), last_column(0) {}
|
||||
AstSrcLocType(int _first_line, int _first_column, int _last_line, int _last_column) : first_line(_first_line), last_line(_last_line), first_column(_first_column), last_column(_last_column) {}
|
||||
};
|
||||
using AstSrcLocType = Location;
|
||||
|
||||
// convert an node type to a string (e.g. for debug output)
|
||||
std::string type2str(AstNodeType type);
|
||||
|
|
@ -179,16 +174,16 @@ namespace AST
|
|||
{
|
||||
// for dict<> and pool<>
|
||||
unsigned int hashidx_;
|
||||
unsigned int hash() const { return hashidx_; }
|
||||
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
|
||||
|
||||
// this nodes type
|
||||
AstNodeType type;
|
||||
|
||||
// the list of child nodes for this node
|
||||
std::vector<AstNode*> children;
|
||||
std::vector<std::unique_ptr<AstNode>> children;
|
||||
|
||||
// the list of attributes assigned to this node
|
||||
std::map<RTLIL::IdString, AstNode*> attributes;
|
||||
std::map<RTLIL::IdString, std::unique_ptr<AstNode>> attributes;
|
||||
bool get_bool_attribute(RTLIL::IdString id);
|
||||
|
||||
// node content - most of it is unused in most node types
|
||||
|
|
@ -201,12 +196,20 @@ namespace AST
|
|||
// set for IDs typed to an enumeration, not used
|
||||
bool is_enum;
|
||||
|
||||
// if this is a multirange memory then this vector contains offset and length of each dimension
|
||||
std::vector<int> multirange_dimensions;
|
||||
std::vector<bool> multirange_swapped; // true if range is swapped, not used for structs
|
||||
// Declared range for array dimension.
|
||||
struct dimension_t {
|
||||
int range_right; // lsb in [msb:lsb]
|
||||
int range_width; // msb - lsb + 1
|
||||
bool range_swapped; // if the declared msb < lsb, msb and lsb above are swapped
|
||||
};
|
||||
// Packed and unpacked dimensions for arrays.
|
||||
// Unpacked dimensions go first, to follow the order of indexing.
|
||||
std::vector<dimension_t> dimensions;
|
||||
// Number of unpacked dimensions.
|
||||
int unpacked_dimensions;
|
||||
|
||||
// this is set by simplify and used during RTLIL generation
|
||||
AstNode *id2ast;
|
||||
AstNode* id2ast;
|
||||
|
||||
// this is used by simplify to detect if basic analysis has been performed already on the node
|
||||
bool basic_prep;
|
||||
|
|
@ -217,13 +220,19 @@ namespace AST
|
|||
// this is the original sourcecode location that resulted in this AST node
|
||||
// it is automatically set by the constructor using AST::current_filename and
|
||||
// the AST::get_line_num() callback function.
|
||||
std::string filename;
|
||||
AstSrcLocType location;
|
||||
|
||||
// are we embedded in an lvalue, param?
|
||||
// (see fixup_hierarchy_flags)
|
||||
bool in_lvalue;
|
||||
bool in_param;
|
||||
bool in_lvalue_from_above;
|
||||
bool in_param_from_above;
|
||||
|
||||
// creating and deleting nodes
|
||||
AstNode(AstNodeType type = AST_NONE, AstNode *child1 = nullptr, AstNode *child2 = nullptr, AstNode *child3 = nullptr, AstNode *child4 = nullptr);
|
||||
AstNode *clone() const;
|
||||
void cloneInto(AstNode *other) const;
|
||||
AstNode(AstSrcLocType loc, AstNodeType type = AST_NONE, std::unique_ptr<AstNode> child1 = nullptr, std::unique_ptr<AstNode> child2 = nullptr, std::unique_ptr<AstNode> child3 = nullptr, std::unique_ptr<AstNode> child4 = nullptr);
|
||||
std::unique_ptr<AstNode> clone() const;
|
||||
void cloneInto(AstNode &other) const;
|
||||
void delete_children();
|
||||
~AstNode();
|
||||
|
||||
|
|
@ -250,16 +259,16 @@ namespace AST
|
|||
|
||||
// simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc.
|
||||
// it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL()
|
||||
bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param);
|
||||
bool simplify(bool const_fold, int stage, int width_hint, bool sign_hint);
|
||||
void replace_result_wire_name_in_function(const std::string &from, const std::string &to);
|
||||
AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init);
|
||||
std::unique_ptr<AstNode> readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init);
|
||||
void expand_genblock(const std::string &prefix);
|
||||
void label_genblks(std::set<std::string>& existing, int &counter);
|
||||
void mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places,
|
||||
dict<AstNode*, uint32_t> &mem2reg_flags, dict<AstNode*, uint32_t> &proc_flags, uint32_t &status_flags);
|
||||
bool mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block);
|
||||
bool mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block, AstNode* async_block);
|
||||
bool mem2reg_check(pool<AstNode*> &mem2reg_set);
|
||||
void mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes);
|
||||
void mem2reg_remove(pool<AstNode*> &mem2reg_set);
|
||||
void meminfo(int &mem_width, int &mem_size, int &addr_bits);
|
||||
bool detect_latch(const std::string &var);
|
||||
const RTLIL::Module* lookup_cell_module();
|
||||
|
|
@ -275,9 +284,11 @@ namespace AST
|
|||
};
|
||||
bool has_const_only_constructs();
|
||||
bool replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall, bool must_succeed);
|
||||
AstNode *eval_const_function(AstNode *fcall, bool must_succeed);
|
||||
std::unique_ptr<AstNode> eval_const_function(AstNode *fcall, bool must_succeed);
|
||||
bool is_simple_const_expr();
|
||||
std::string process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint);
|
||||
|
||||
// helper for parsing format strings
|
||||
Fmt processFormat(int stage, bool sformat_like, int default_base = 10, size_t first_arg_at = 0, bool may_fail = false);
|
||||
|
||||
bool is_recursive_function() const;
|
||||
std::pair<AstNode*, AstNode*> get_tern_choice();
|
||||
|
|
@ -290,26 +301,30 @@ namespace AST
|
|||
std::vector<RTLIL::Binding *> genBindings() const;
|
||||
|
||||
// used by genRTLIL() for detecting expression width and sign
|
||||
void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL);
|
||||
void detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real = NULL);
|
||||
void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = nullptr);
|
||||
void detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real = nullptr);
|
||||
|
||||
// create RTLIL code for this AST node
|
||||
// for expressions the resulting signal vector is returned
|
||||
// all generated cell instances, etc. are written to the RTLIL::Module pointed to by AST_INTERNAL::current_module
|
||||
RTLIL::SigSpec genRTLIL(int width_hint = -1, bool sign_hint = false);
|
||||
RTLIL::SigSpec genWidthRTLIL(int width, bool sgn, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr = NULL);
|
||||
RTLIL::SigSpec genWidthRTLIL(int width, bool sgn, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr = nullptr);
|
||||
|
||||
// compare AST nodes
|
||||
bool operator==(const AstNode &other) const;
|
||||
bool operator!=(const AstNode &other) const;
|
||||
bool contains(const AstNode *other) const;
|
||||
AstNode operator=(AstNode) = delete;
|
||||
|
||||
// helper functions for creating AST nodes for constants
|
||||
static AstNode *mkconst_int(uint32_t v, bool is_signed, int width = 32);
|
||||
static AstNode *mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed, bool is_unsized);
|
||||
static AstNode *mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed);
|
||||
static AstNode *mkconst_str(const std::vector<RTLIL::State> &v);
|
||||
static AstNode *mkconst_str(const std::string &str);
|
||||
static std::unique_ptr<AstNode> mkconst_int(AstSrcLocType loc, uint32_t v, bool is_signed, int width = 32);
|
||||
static std::unique_ptr<AstNode> mkconst_bits(AstSrcLocType loc, const std::vector<RTLIL::State> &v, bool is_signed, bool is_unsized);
|
||||
static std::unique_ptr<AstNode> mkconst_bits(AstSrcLocType loc, const std::vector<RTLIL::State> &v, bool is_signed);
|
||||
static std::unique_ptr<AstNode> mkconst_str(AstSrcLocType loc, const std::vector<RTLIL::State> &v);
|
||||
static std::unique_ptr<AstNode> mkconst_str(AstSrcLocType loc, const std::string &str);
|
||||
|
||||
// helper function to create an AST node for a temporary register
|
||||
std::unique_ptr<AstNode> mktemp_logic(AstSrcLocType loc, const std::string &name, AstNode *mod, bool nosync, int range_left, int range_right, bool is_signed);
|
||||
|
||||
// helper function for creating sign-extended const objects
|
||||
RTLIL::Const bitsAsConst(int width, bool is_signed);
|
||||
|
|
@ -335,21 +350,54 @@ namespace AST
|
|||
|
||||
// Helper for looking up identifiers which are prefixed with the current module name
|
||||
std::string try_pop_module_prefix() const;
|
||||
|
||||
// helper to clone the node with some of its subexpressions replaced with zero (this is used
|
||||
// to evaluate widths of dynamic ranges)
|
||||
std::unique_ptr<AstNode> clone_at_zero();
|
||||
|
||||
void set_attribute(RTLIL::IdString key, std::unique_ptr<AstNode> node)
|
||||
{
|
||||
node->set_in_param_flag(true);
|
||||
attributes[key] = std::move(node);
|
||||
}
|
||||
|
||||
// helper to set in_lvalue/in_param flags from the hierarchy context (the actual flag
|
||||
// can be overridden based on the intrinsic properties of this node, i.e. based on its type)
|
||||
void set_in_lvalue_flag(bool flag, bool no_descend = false);
|
||||
void set_in_param_flag(bool flag, bool no_descend = false);
|
||||
|
||||
// fix up the hierarchy flags (in_lvalue/in_param) of this node and its children
|
||||
//
|
||||
// to keep the flags in sync, fixup_hierarchy_flags(true) needs to be called once after
|
||||
// parsing the AST to walk the full tree, then plain fixup_hierarchy_flags() performs
|
||||
// localized fixups after modifying children/attributes of a particular node
|
||||
void fixup_hierarchy_flags(bool force_descend = false);
|
||||
|
||||
// helpers for indexing
|
||||
std::unique_ptr<AstNode> make_index_range(AstNode *node, bool unpacked_range = false);
|
||||
AstNode *get_struct_member() const;
|
||||
|
||||
// helper to print errors from simplify/genrtlil code
|
||||
[[noreturn]] void formatted_input_error(std::string str) const;
|
||||
template <typename... Args>
|
||||
[[noreturn]] void input_error(FmtString<TypeIdentity<Args>...> fmt, const Args &... args) const
|
||||
{
|
||||
formatted_input_error(fmt.format(args...));
|
||||
}
|
||||
};
|
||||
|
||||
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
|
||||
void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit,
|
||||
void process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit,
|
||||
bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire);
|
||||
|
||||
// parametric modules are supported directly by the AST library
|
||||
// therefore we need our own derivate of RTLIL::Module with overloaded virtual functions
|
||||
struct AstModule : RTLIL::Module {
|
||||
AstNode *ast;
|
||||
std::unique_ptr<AstNode> ast;
|
||||
bool nolatches, nomeminit, nomem2reg, mem2reg, noblackbox, lib, nowb, noopt, icells, pwires, autowire;
|
||||
~AstModule() override;
|
||||
RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, bool mayfail) override;
|
||||
RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, const dict<RTLIL::IdString, RTLIL::Module*> &interfaces, const dict<RTLIL::IdString, RTLIL::IdString> &modports, bool mayfail) override;
|
||||
std::string derive_common(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, AstNode **new_ast_out, bool quiet = false);
|
||||
std::string derive_common(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, std::unique_ptr<AstNode>* new_ast_out, bool quiet = false);
|
||||
void expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces) override;
|
||||
bool reprocess_if_necessary(RTLIL::Design *design) override;
|
||||
RTLIL::Module *clone() const override;
|
||||
|
|
@ -359,16 +407,19 @@ namespace AST
|
|||
// this must be set by the language frontend before parsing the sources
|
||||
// the AstNode constructor then uses current_filename and get_line_num()
|
||||
// to initialize the filename and linenum properties of new nodes
|
||||
extern std::string current_filename;
|
||||
extern void (*set_line_num)(int);
|
||||
extern int (*get_line_num)();
|
||||
// extern std::string current_filename;
|
||||
// also set by the language frontend to control some AST processing
|
||||
extern bool sv_mode_but_global_and_used_for_literally_one_condition;
|
||||
|
||||
// for stats
|
||||
unsigned long long astnode_count();
|
||||
|
||||
// set set_line_num and get_line_num to internal dummy functions (done by simplify() and AstModule::derive
|
||||
// to control the filename and linenum properties of new nodes not generated by a frontend parser)
|
||||
void use_internal_line_num();
|
||||
|
||||
// call a DPI function
|
||||
AstNode *dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args);
|
||||
std::unique_ptr<AstNode> dpi_call(AstSrcLocType loc, const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<std::unique_ptr<AstNode>> &args);
|
||||
|
||||
// Helper functions related to handling SystemVerilog interfaces
|
||||
std::pair<std::string,std::string> split_modport_from_type(std::string name_type);
|
||||
|
|
@ -378,9 +429,6 @@ namespace AST
|
|||
// Helper for setting the src attribute.
|
||||
void set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast);
|
||||
|
||||
// struct helper exposed from simplify for genrtlil
|
||||
AstNode *make_struct_member_range(AstNode *node, AstNode *member_node);
|
||||
|
||||
// generate standard $paramod... derived module name; parameters should be
|
||||
// in the order they are declared in the instantiated module
|
||||
std::string derived_module_name(std::string stripped_name, const std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> ¶meters);
|
||||
|
|
@ -393,7 +441,7 @@ namespace AST
|
|||
namespace AST_INTERNAL
|
||||
{
|
||||
// internal state variables
|
||||
extern bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
|
||||
extern bool flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
|
||||
extern bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_pwires, flag_autowire;
|
||||
extern AST::AstNode *current_ast, *current_ast_mod;
|
||||
extern std::map<std::string, AST::AstNode*> current_scope;
|
||||
|
|
@ -417,7 +465,7 @@ namespace AST_INTERNAL
|
|||
process_and_replace_module(RTLIL::Design *design,
|
||||
RTLIL::Module *old_module,
|
||||
AST::AstNode *new_ast,
|
||||
AST::AstNode *original_ast = nullptr);
|
||||
std::unique_ptr<AST::AstNode> original_ast = nullptr);
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ static ffi_fptr resolve_fn (std::string symbol_name)
|
|||
plugin_name = loaded_plugin_aliases.at(plugin_name);
|
||||
|
||||
if (loaded_plugins.count(plugin_name) == 0)
|
||||
log_error("unable to resolve '%s': can't find plugin `%s'\n", symbol_name.c_str(), plugin_name.c_str());
|
||||
log_error("unable to resolve '%s': can't find plugin `%s'\n", symbol_name, plugin_name);
|
||||
|
||||
void *symbol = dlsym(loaded_plugins.at(plugin_name), real_symbol_name.c_str());
|
||||
|
||||
|
|
@ -61,44 +61,45 @@ static ffi_fptr resolve_fn (std::string symbol_name)
|
|||
if (symbol != nullptr)
|
||||
return (ffi_fptr) symbol;
|
||||
|
||||
log_error("unable to resolve '%s'.\n", symbol_name.c_str());
|
||||
log_error("unable to resolve '%s'.\n", symbol_name);
|
||||
}
|
||||
|
||||
AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args)
|
||||
std::unique_ptr<AST::AstNode> AST::dpi_call(AstSrcLocType loc, const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<std::unique_ptr<AST::AstNode>> &args)
|
||||
{
|
||||
AST::AstNode *newNode = nullptr;
|
||||
union { double f64; float f32; int32_t i32; void *ptr; } value_store [args.size() + 1];
|
||||
ffi_type *types [args.size() + 1];
|
||||
void *values [args.size() + 1];
|
||||
std::unique_ptr<AST::AstNode> newNode = nullptr;
|
||||
union value { double f64; float f32; int32_t i32; void *ptr; };
|
||||
std::vector<value> value_store(args.size() + 1);
|
||||
std::vector<ffi_type *> types(args.size() + 1);
|
||||
std::vector<void *> values(args.size() + 1);
|
||||
ffi_cif cif;
|
||||
int status;
|
||||
|
||||
log("Calling DPI function `%s' and returning `%s':\n", fname.c_str(), rtype.c_str());
|
||||
log("Calling DPI function `%s' and returning `%s':\n", fname, rtype);
|
||||
|
||||
log_assert(GetSize(args) == GetSize(argtypes));
|
||||
for (int i = 0; i < GetSize(args); i++) {
|
||||
if (argtypes[i] == "real") {
|
||||
log(" arg %d (%s): %f\n", i, argtypes[i].c_str(), args[i]->asReal(args[i]->is_signed));
|
||||
log(" arg %d (%s): %f\n", i, argtypes[i], args[i]->asReal(args[i]->is_signed));
|
||||
value_store[i].f64 = args[i]->asReal(args[i]->is_signed);
|
||||
values[i] = &value_store[i].f64;
|
||||
types[i] = &ffi_type_double;
|
||||
} else if (argtypes[i] == "shortreal") {
|
||||
log(" arg %d (%s): %f\n", i, argtypes[i].c_str(), args[i]->asReal(args[i]->is_signed));
|
||||
log(" arg %d (%s): %f\n", i, argtypes[i], args[i]->asReal(args[i]->is_signed));
|
||||
value_store[i].f32 = args[i]->asReal(args[i]->is_signed);
|
||||
values[i] = &value_store[i].f32;
|
||||
types[i] = &ffi_type_double;
|
||||
} else if (argtypes[i] == "integer") {
|
||||
log(" arg %d (%s): %lld\n", i, argtypes[i].c_str(), (long long)args[i]->asInt(args[i]->is_signed));
|
||||
log(" arg %d (%s): %lld\n", i, argtypes[i], (long long)args[i]->asInt(args[i]->is_signed));
|
||||
value_store[i].i32 = args[i]->asInt(args[i]->is_signed);
|
||||
values[i] = &value_store[i].i32;
|
||||
types[i] = &ffi_type_sint32;
|
||||
} else if (argtypes[i] == "chandle") {
|
||||
log(" arg %d (%s): %llx\n", i, argtypes[i].c_str(), (unsigned long long)args[i]->asInt(false));
|
||||
log(" arg %d (%s): %llx\n", i, argtypes[i], (unsigned long long)args[i]->asInt(false));
|
||||
value_store[i].ptr = (void *)args[i]->asInt(args[i]->is_signed);
|
||||
values[i] = &value_store[i].ptr;
|
||||
types[i] = &ffi_type_pointer;
|
||||
} else {
|
||||
log_error("invalid argtype '%s' for argument %d.\n", argtypes[i].c_str(), i);
|
||||
log_error("invalid argtype '%s' for argument %d.\n", argtypes[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -115,20 +116,20 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname,
|
|||
types[args.size()] = &ffi_type_pointer;
|
||||
values[args.size()] = &value_store[args.size()].ptr;
|
||||
} else {
|
||||
log_error("invalid rtype '%s'.\n", rtype.c_str());
|
||||
log_error("invalid rtype '%s'.\n", rtype);
|
||||
}
|
||||
|
||||
if ((status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, args.size(), types[args.size()], types)) != FFI_OK)
|
||||
if ((status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, args.size(), types[args.size()], types.data())) != FFI_OK)
|
||||
log_error("ffi_prep_cif failed: status %d.\n", status);
|
||||
|
||||
ffi_call(&cif, resolve_fn(fname.c_str()), values[args.size()], values);
|
||||
ffi_call(&cif, resolve_fn(fname.c_str()), values[args.size()], values.data());
|
||||
|
||||
if (rtype == "real") {
|
||||
newNode = new AstNode(AST_REALVALUE);
|
||||
newNode = std::make_unique<AstNode>(loc, AST_REALVALUE);
|
||||
newNode->realvalue = value_store[args.size()].f64;
|
||||
log(" return realvalue: %g\n", newNode->asReal(true));
|
||||
} else if (rtype == "shortreal") {
|
||||
newNode = new AstNode(AST_REALVALUE);
|
||||
newNode = std::make_unique<AstNode>(loc, AST_REALVALUE);
|
||||
newNode->realvalue = value_store[args.size()].f32;
|
||||
log(" return realvalue: %g\n", newNode->asReal(true));
|
||||
} else if (rtype == "chandle") {
|
||||
|
|
@ -136,10 +137,10 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname,
|
|||
std::vector<RTLIL::State> bits(64);
|
||||
for (int i = 0; i < 64; i++)
|
||||
bits.at(i) = (rawval & (1ULL << i)) ? RTLIL::State::S1 : RTLIL::State::S0;
|
||||
newNode = AstNode::mkconst_bits(bits, false);
|
||||
newNode = AstNode::mkconst_bits(loc, bits, false);
|
||||
log(" return chandle: %llx\n", (unsigned long long)newNode->asInt(false));
|
||||
} else {
|
||||
newNode = AstNode::mkconst_int(value_store[args.size()].i32, false);
|
||||
newNode = AstNode::mkconst_int(loc, value_store[args.size()].i32, false);
|
||||
log(" return integer: %lld\n", (long long)newNode->asInt(true));
|
||||
}
|
||||
|
||||
|
|
@ -152,9 +153,9 @@ YOSYS_NAMESPACE_END
|
|||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
AST::AstNode *AST::dpi_call(const std::string&, const std::string &fname, const std::vector<std::string>&, const std::vector<AstNode*>&)
|
||||
std::unique_ptr<AST::AstNode> AST::dpi_call(AstSrcLocType, const std::string&, const std::string &fname, const std::vector<std::string>&, const std::vector<std::unique_ptr<AST::AstNode>>&)
|
||||
{
|
||||
log_error("Can't call DPI function `%s': this version of yosys is built without plugin support\n", fname.c_str());
|
||||
log_error("Can't call DPI function `%s': this version of yosys is built without plugin support\n", fname);
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
const int lut_input_plane_limit = 12;
|
||||
|
||||
static bool read_next_line(char *&buffer, size_t &buffer_size, int &line_count, std::istream &f)
|
||||
{
|
||||
string strbuf;
|
||||
|
|
@ -147,7 +149,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
|||
if (buffer[0] == '.')
|
||||
{
|
||||
if (lutptr) {
|
||||
for (auto &bit : lutptr->bits)
|
||||
for (auto bit : *lutptr)
|
||||
if (bit == RTLIL::State::Sx)
|
||||
bit = lut_default_state;
|
||||
lutptr = NULL;
|
||||
|
|
@ -243,7 +245,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
|||
if (undef_wire != nullptr)
|
||||
module->rename(undef_wire, stringf("$undef$%d", ++blif_maxnum));
|
||||
|
||||
autoidx = std::max(autoidx, blif_maxnum+1);
|
||||
autoidx.ensure_at_least(blif_maxnum+1);
|
||||
blif_maxnum = 0;
|
||||
}
|
||||
|
||||
|
|
@ -254,6 +256,16 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, ".area") || !strcmp(cmd, ".delay") || !strcmp(cmd, ".wire_load_slope") || !strcmp(cmd, ".wire") ||
|
||||
!strcmp(cmd, ".input_arrival") || !strcmp(cmd, ".default_input_arrival") || !strcmp(cmd, ".output_required") ||
|
||||
!strcmp(cmd, ".default_output_required") || !strcmp(cmd, ".input_drive") || !strcmp(cmd, ".default_input_drive") ||
|
||||
!strcmp(cmd, ".max_input_load") || !strcmp(cmd, ".default_max_input_load") || !strcmp(cmd, ".output_load") ||
|
||||
!strcmp(cmd, ".default_output_load"))
|
||||
{
|
||||
log_warning("Blif delay constraints (%s) are not supported.", cmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs"))
|
||||
{
|
||||
char *p;
|
||||
|
|
@ -309,9 +321,10 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
|||
const_v = Const(str);
|
||||
} else {
|
||||
int n = strlen(v);
|
||||
const_v.bits.resize(n);
|
||||
Const::Builder const_v_builder(n);
|
||||
for (int i = 0; i < n; i++)
|
||||
const_v.bits[i] = v[n-i-1] != '0' ? State::S1 : State::S0;
|
||||
const_v_builder.push_back(v[n-i-1] != '0' ? State::S1 : State::S0);
|
||||
const_v = const_v_builder.build();
|
||||
}
|
||||
if (!strcmp(cmd, ".attr")) {
|
||||
if (obj_attributes == nullptr) {
|
||||
|
|
@ -350,17 +363,17 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
|||
goto no_latch_clock;
|
||||
|
||||
if (!strcmp(edge, "re"))
|
||||
cell = module->addDff(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q));
|
||||
cell = module->addDffGate(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q));
|
||||
else if (!strcmp(edge, "fe"))
|
||||
cell = module->addDff(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q), false);
|
||||
cell = module->addDffGate(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q), false);
|
||||
else if (!strcmp(edge, "ah"))
|
||||
cell = module->addDlatch(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q));
|
||||
cell = module->addDlatchGate(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q));
|
||||
else if (!strcmp(edge, "al"))
|
||||
cell = module->addDlatch(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q), false);
|
||||
cell = module->addDlatchGate(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q), false);
|
||||
else {
|
||||
no_latch_clock:
|
||||
if (dff_name.empty()) {
|
||||
cell = module->addFf(NEW_ID, blif_wire(d), blif_wire(q));
|
||||
cell = module->addFfGate(NEW_ID, blif_wire(d), blif_wire(q));
|
||||
} else {
|
||||
cell = module->addCell(NEW_ID, dff_name);
|
||||
cell->setPort(ID::D, blif_wire(d));
|
||||
|
|
@ -513,6 +526,11 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
|||
sopmode = -1;
|
||||
lastcell = sopcell;
|
||||
}
|
||||
else if (input_sig.size() > lut_input_plane_limit)
|
||||
{
|
||||
err_reason = stringf("names' input plane must have fewer than %d signals.", lut_input_plane_limit + 1);
|
||||
goto error_with_reason;
|
||||
}
|
||||
else
|
||||
{
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, ID($lut));
|
||||
|
|
@ -546,21 +564,23 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
|||
log_assert(sopcell->parameters[ID::WIDTH].as_int() == input_len);
|
||||
sopcell->parameters[ID::DEPTH] = sopcell->parameters[ID::DEPTH].as_int() + 1;
|
||||
|
||||
Const::Builder table_bits_builder(input_len * 2);
|
||||
for (int i = 0; i < input_len; i++)
|
||||
switch (input[i]) {
|
||||
case '0':
|
||||
sopcell->parameters[ID::TABLE].bits.push_back(State::S1);
|
||||
sopcell->parameters[ID::TABLE].bits.push_back(State::S0);
|
||||
table_bits_builder.push_back(State::S1);
|
||||
table_bits_builder.push_back(State::S0);
|
||||
break;
|
||||
case '1':
|
||||
sopcell->parameters[ID::TABLE].bits.push_back(State::S0);
|
||||
sopcell->parameters[ID::TABLE].bits.push_back(State::S1);
|
||||
table_bits_builder.push_back(State::S0);
|
||||
table_bits_builder.push_back(State::S1);
|
||||
break;
|
||||
default:
|
||||
sopcell->parameters[ID::TABLE].bits.push_back(State::S0);
|
||||
sopcell->parameters[ID::TABLE].bits.push_back(State::S0);
|
||||
table_bits_builder.push_back(State::S0);
|
||||
table_bits_builder.push_back(State::S0);
|
||||
break;
|
||||
}
|
||||
sopcell->parameters[ID::TABLE].append(table_bits_builder.build());
|
||||
|
||||
if (sopmode == -1) {
|
||||
sopmode = (*output == '1');
|
||||
|
|
@ -576,7 +596,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
|||
|
||||
if (lutptr)
|
||||
{
|
||||
if (input_len > 12)
|
||||
if (input_len > lut_input_plane_limit)
|
||||
goto error;
|
||||
|
||||
for (int i = 0; i < (1 << input_len); i++) {
|
||||
|
|
@ -588,7 +608,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
|||
goto try_next_value;
|
||||
}
|
||||
}
|
||||
lutptr->bits.at(i) = !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1;
|
||||
lutptr->set(i, !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1);
|
||||
try_next_value:;
|
||||
}
|
||||
|
||||
|
|
@ -601,7 +621,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
|||
error:
|
||||
log_error("Syntax error in line %d!\n", line_count);
|
||||
error_with_reason:
|
||||
log_error("Syntax error in line %d: %s\n", line_count, err_reason.c_str());
|
||||
log_error("Syntax error in line %d: %s\n", line_count, err_reason);
|
||||
}
|
||||
|
||||
struct BlifFrontend : public Frontend {
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ Const json_parse_attr_param_value(JsonNode *node)
|
|||
}
|
||||
} else
|
||||
if (node->type == 'N') {
|
||||
value = Const(node->data_number, 32);
|
||||
value = Const(node->data_number);
|
||||
if (node->data_number < 0)
|
||||
value.flags |= RTLIL::CONST_FLAG_SIGNED;
|
||||
} else
|
||||
|
|
@ -289,7 +289,7 @@ void json_parse_attr_param(dict<IdString, Const> &results, JsonNode *node)
|
|||
|
||||
void json_import(Design *design, string &modname, JsonNode *node)
|
||||
{
|
||||
log("Importing module %s from JSON tree.\n", modname.c_str());
|
||||
log("Importing module %s from JSON tree.\n", modname);
|
||||
|
||||
Module *module = new RTLIL::Module;
|
||||
module->name = RTLIL::escape_id(modname.c_str());
|
||||
|
|
@ -370,7 +370,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
port_wire->port_input = true;
|
||||
port_wire->port_output = true;
|
||||
} else
|
||||
log_error("JSON port node '%s' has invalid '%s' direction attribute.\n", log_id(port_name), port_direction_node->data_string.c_str());
|
||||
log_error("JSON port node '%s' has invalid '%s' direction attribute.\n", log_id(port_name), port_direction_node->data_string);
|
||||
|
||||
port_wire->port_id = port_id;
|
||||
|
||||
|
|
|
|||
|
|
@ -40,60 +40,25 @@ static RTLIL::SigSpec parse_func_identifier(RTLIL::Module *module, const char *&
|
|||
expr[id_len] == '_' || expr[id_len] == '[' || expr[id_len] == ']') id_len++;
|
||||
|
||||
if (id_len == 0)
|
||||
log_error("Expected identifier at `%s'.\n", expr);
|
||||
log_error("Expected identifier at `%s' in %s.\n", expr, RTLIL::unescape_id(module->name));
|
||||
|
||||
if (id_len == 1 && (*expr == '0' || *expr == '1'))
|
||||
return *(expr++) == '0' ? RTLIL::State::S0 : RTLIL::State::S1;
|
||||
|
||||
std::string id = RTLIL::escape_id(std::string(expr, id_len));
|
||||
if (!module->wires_.count(id))
|
||||
log_error("Can't resolve wire name %s.\n", RTLIL::unescape_id(id).c_str());
|
||||
log_error("Can't resolve wire name %s in %s.\n", RTLIL::unescape_id(id), RTLIL::unescape_id(module->name));
|
||||
|
||||
expr += id_len;
|
||||
return module->wires_.at(id);
|
||||
}
|
||||
|
||||
static RTLIL::SigSpec create_inv_cell(RTLIL::Module *module, RTLIL::SigSpec A)
|
||||
{
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, ID($_NOT_));
|
||||
cell->setPort(ID::A, A);
|
||||
cell->setPort(ID::Y, module->addWire(NEW_ID));
|
||||
return cell->getPort(ID::Y);
|
||||
}
|
||||
|
||||
static RTLIL::SigSpec create_xor_cell(RTLIL::Module *module, RTLIL::SigSpec A, RTLIL::SigSpec B)
|
||||
{
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, ID($_XOR_));
|
||||
cell->setPort(ID::A, A);
|
||||
cell->setPort(ID::B, B);
|
||||
cell->setPort(ID::Y, module->addWire(NEW_ID));
|
||||
return cell->getPort(ID::Y);
|
||||
}
|
||||
|
||||
static RTLIL::SigSpec create_and_cell(RTLIL::Module *module, RTLIL::SigSpec A, RTLIL::SigSpec B)
|
||||
{
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, ID($_AND_));
|
||||
cell->setPort(ID::A, A);
|
||||
cell->setPort(ID::B, B);
|
||||
cell->setPort(ID::Y, module->addWire(NEW_ID));
|
||||
return cell->getPort(ID::Y);
|
||||
}
|
||||
|
||||
static RTLIL::SigSpec create_or_cell(RTLIL::Module *module, RTLIL::SigSpec A, RTLIL::SigSpec B)
|
||||
{
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, ID($_OR_));
|
||||
cell->setPort(ID::A, A);
|
||||
cell->setPort(ID::B, B);
|
||||
cell->setPort(ID::Y, module->addWire(NEW_ID));
|
||||
return cell->getPort(ID::Y);
|
||||
}
|
||||
|
||||
static bool parse_func_reduce(RTLIL::Module *module, std::vector<token_t> &stack, token_t next_token)
|
||||
{
|
||||
int top = int(stack.size())-1;
|
||||
|
||||
if (0 <= top-1 && stack[top].type == 0 && stack[top-1].type == '!') {
|
||||
token_t t = token_t(0, create_inv_cell(module, stack[top].sig));
|
||||
token_t t = token_t(0, module->NotGate(NEW_ID, stack[top].sig));
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.push_back(t);
|
||||
|
|
@ -101,7 +66,7 @@ static bool parse_func_reduce(RTLIL::Module *module, std::vector<token_t> &stack
|
|||
}
|
||||
|
||||
if (0 <= top-1 && stack[top].type == '\'' && stack[top-1].type == 0) {
|
||||
token_t t = token_t(0, create_inv_cell(module, stack[top-1].sig));
|
||||
token_t t = token_t(0, module->NotGate(NEW_ID, stack[top-1].sig));
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.push_back(t);
|
||||
|
|
@ -116,7 +81,7 @@ static bool parse_func_reduce(RTLIL::Module *module, std::vector<token_t> &stack
|
|||
}
|
||||
|
||||
if (0 <= top-2 && stack[top-2].type == 1 && stack[top-1].type == '^' && stack[top].type == 1) {
|
||||
token_t t = token_t(1, create_xor_cell(module, stack[top-2].sig, stack[top].sig));
|
||||
token_t t = token_t(1, module->XorGate(NEW_ID, stack[top-2].sig, stack[top].sig));
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
|
|
@ -132,7 +97,7 @@ static bool parse_func_reduce(RTLIL::Module *module, std::vector<token_t> &stack
|
|||
}
|
||||
|
||||
if (0 <= top-1 && stack[top-1].type == 2 && stack[top].type == 2) {
|
||||
token_t t = token_t(2, create_and_cell(module, stack[top-1].sig, stack[top].sig));
|
||||
token_t t = token_t(2, module->AndGate(NEW_ID, stack[top-1].sig, stack[top].sig));
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.push_back(t);
|
||||
|
|
@ -140,7 +105,7 @@ static bool parse_func_reduce(RTLIL::Module *module, std::vector<token_t> &stack
|
|||
}
|
||||
|
||||
if (0 <= top-2 && stack[top-2].type == 2 && (stack[top-1].type == '*' || stack[top-1].type == '&') && stack[top].type == 2) {
|
||||
token_t t = token_t(2, create_and_cell(module, stack[top-2].sig, stack[top].sig));
|
||||
token_t t = token_t(2, module->AndGate(NEW_ID, stack[top-2].sig, stack[top].sig));
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
|
|
@ -156,7 +121,7 @@ static bool parse_func_reduce(RTLIL::Module *module, std::vector<token_t> &stack
|
|||
}
|
||||
|
||||
if (0 <= top-2 && stack[top-2].type == 3 && (stack[top-1].type == '+' || stack[top-1].type == '|') && stack[top].type == 3) {
|
||||
token_t t = token_t(3, create_or_cell(module, stack[top-2].sig, stack[top].sig));
|
||||
token_t t = token_t(3, module->OrGate(NEW_ID, stack[top-2].sig, stack[top].sig));
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
|
|
@ -209,16 +174,40 @@ static RTLIL::SigSpec parse_func_expr(RTLIL::Module *module, const char *expr)
|
|||
#endif
|
||||
|
||||
if (stack.size() != 1 || stack.back().type != 3)
|
||||
log_error("Parser error in function expr `%s'.\n", orig_expr);
|
||||
log_error("Parser error in function expr `%s'in %s.\n", orig_expr, RTLIL::unescape_id(module->name));
|
||||
|
||||
return stack.back().sig;
|
||||
}
|
||||
|
||||
static void create_ff(RTLIL::Module *module, LibertyAst *node)
|
||||
static RTLIL::SigSpec create_tristate(RTLIL::Module *module, RTLIL::SigSpec func, const char *three_state_expr)
|
||||
{
|
||||
RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0))));
|
||||
RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1))));
|
||||
RTLIL::SigSpec three_state = parse_func_expr(module, three_state_expr);
|
||||
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, ID($tribuf));
|
||||
cell->setParam(ID::WIDTH, GetSize(func));
|
||||
cell->setPort(ID::A, func);
|
||||
cell->setPort(ID::EN, module->NotGate(NEW_ID, three_state));
|
||||
cell->setPort(ID::Y, module->addWire(NEW_ID));
|
||||
return cell->getPort(ID::Y);
|
||||
}
|
||||
|
||||
static void create_latch_ff_wires(RTLIL::Module *module, const LibertyAst *node)
|
||||
{
|
||||
module->addWire(RTLIL::escape_id(node->args.at(0)));
|
||||
module->addWire(RTLIL::escape_id(node->args.at(1)));
|
||||
}
|
||||
|
||||
static std::pair<RTLIL::SigSpec, RTLIL::SigSpec> find_latch_ff_wires(RTLIL::Module *module, const LibertyAst *node)
|
||||
{
|
||||
auto* iq_wire = module->wire(RTLIL::escape_id(node->args.at(0)));
|
||||
auto* iqn_wire = module->wire(RTLIL::escape_id(node->args.at(1)));
|
||||
log_assert(iq_wire && iqn_wire);
|
||||
return std::make_pair(iq_wire, iqn_wire);
|
||||
}
|
||||
|
||||
static void create_ff(RTLIL::Module *module, const LibertyAst *node)
|
||||
{
|
||||
auto [iq_sig, iqn_sig] = find_latch_ff_wires(module, node);
|
||||
RTLIL::SigSpec clk_sig, data_sig, clear_sig, preset_sig;
|
||||
bool clk_polarity = true, clear_polarity = true, preset_polarity = true;
|
||||
|
||||
|
|
@ -234,7 +223,7 @@ static void create_ff(RTLIL::Module *module, LibertyAst *node)
|
|||
}
|
||||
|
||||
if (clk_sig.size() == 0 || data_sig.size() == 0)
|
||||
log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", log_id(module->name));
|
||||
log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", RTLIL::unescape_id(module->name));
|
||||
|
||||
for (bool rerun_invert_rollback = true; rerun_invert_rollback;)
|
||||
{
|
||||
|
|
@ -291,11 +280,9 @@ static void create_ff(RTLIL::Module *module, LibertyAst *node)
|
|||
log_assert(!cell->type.empty());
|
||||
}
|
||||
|
||||
static bool create_latch(RTLIL::Module *module, LibertyAst *node, bool flag_ignore_miss_data_latch)
|
||||
static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool flag_ignore_miss_data_latch)
|
||||
{
|
||||
RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0))));
|
||||
RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1))));
|
||||
|
||||
auto [iq_sig, iqn_sig] = find_latch_ff_wires(module, node);
|
||||
RTLIL::SigSpec enable_sig, data_sig, clear_sig, preset_sig;
|
||||
bool enable_polarity = true, clear_polarity = true, preset_polarity = true;
|
||||
|
||||
|
|
@ -312,9 +299,9 @@ static bool create_latch(RTLIL::Module *module, LibertyAst *node, bool flag_igno
|
|||
|
||||
if (enable_sig.size() == 0 || data_sig.size() == 0) {
|
||||
if (!flag_ignore_miss_data_latch)
|
||||
log_error("Latch cell %s has no data_in and/or enable attribute.\n", log_id(module->name));
|
||||
log_error("Latch cell %s has no data_in and/or enable attribute.\n", RTLIL::unescape_id(module->name));
|
||||
else
|
||||
log("Ignored latch cell %s with no data_in and/or enable attribute.\n", log_id(module->name));
|
||||
log("Ignored latch cell %s with no data_in and/or enable attribute.\n", RTLIL::unescape_id(module->name));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -371,7 +358,7 @@ static bool create_latch(RTLIL::Module *module, LibertyAst *node, bool flag_igno
|
|||
RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? ID($_OR_) : ID($_AND_));
|
||||
enable_gate->setPort(ID::A, enable_sig);
|
||||
enable_gate->setPort(ID::B, clear_enable);
|
||||
enable_gate->setPort(ID::Y, data_sig = module->addWire(NEW_ID));
|
||||
enable_gate->setPort(ID::Y, enable_sig = module->addWire(NEW_ID));
|
||||
}
|
||||
|
||||
if (preset_sig.size() == 1)
|
||||
|
|
@ -399,7 +386,7 @@ static bool create_latch(RTLIL::Module *module, LibertyAst *node, bool flag_igno
|
|||
RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? ID($_OR_) : ID($_AND_));
|
||||
enable_gate->setPort(ID::A, enable_sig);
|
||||
enable_gate->setPort(ID::B, preset_enable);
|
||||
enable_gate->setPort(ID::Y, data_sig = module->addWire(NEW_ID));
|
||||
enable_gate->setPort(ID::Y, enable_sig = module->addWire(NEW_ID));
|
||||
}
|
||||
|
||||
cell = module->addCell(NEW_ID, stringf("$_DLATCH_%c_", enable_polarity ? 'P' : 'N'));
|
||||
|
|
@ -410,7 +397,7 @@ static bool create_latch(RTLIL::Module *module, LibertyAst *node, bool flag_igno
|
|||
return true;
|
||||
}
|
||||
|
||||
void parse_type_map(std::map<std::string, std::tuple<int, int, bool>> &type_map, LibertyAst *ast)
|
||||
void parse_type_map(std::map<std::string, std::tuple<int, int, bool>> &type_map, const LibertyAst *ast)
|
||||
{
|
||||
for (auto type_node : ast->children)
|
||||
{
|
||||
|
|
@ -464,6 +451,9 @@ struct LibertyFrontend : public Frontend {
|
|||
log(" -lib\n");
|
||||
log(" only create empty blackbox modules\n");
|
||||
log("\n");
|
||||
log(" -wb\n");
|
||||
log(" mark imported cells as whiteboxes\n");
|
||||
log("\n");
|
||||
log(" -nooverwrite\n");
|
||||
log(" ignore re-definitions of modules. (the default behavior is to\n");
|
||||
log(" create an error message if the existing module is not a blackbox\n");
|
||||
|
|
@ -482,22 +472,29 @@ struct LibertyFrontend : public Frontend {
|
|||
log(" -ignore_miss_data_latch\n");
|
||||
log(" ignore latches with missing data and/or enable pins\n");
|
||||
log("\n");
|
||||
log(" -ignore_buses\n");
|
||||
log(" ignore cells with bus interfaces (wide ports)\n");
|
||||
log("\n");
|
||||
log(" -setattr <attribute_name>\n");
|
||||
log(" set the specified attribute (to the value 1) on all loaded modules\n");
|
||||
log("\n");
|
||||
log(" -unit_delay\n");
|
||||
log(" import combinational timing arcs under the unit delay model\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
bool flag_lib = false;
|
||||
bool flag_wb = false;
|
||||
bool flag_nooverwrite = false;
|
||||
bool flag_overwrite = false;
|
||||
bool flag_ignore_miss_func = false;
|
||||
bool flag_ignore_miss_dir = false;
|
||||
bool flag_ignore_miss_data_latch = false;
|
||||
bool flag_ignore_buses = false;
|
||||
bool flag_unit_delay = false;
|
||||
std::vector<std::string> attributes;
|
||||
|
||||
log_header(design, "Executing Liberty frontend.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
|
|
@ -505,6 +502,10 @@ struct LibertyFrontend : public Frontend {
|
|||
flag_lib = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-wb") {
|
||||
flag_wb = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-ignore_redef" || arg == "-nooverwrite") {
|
||||
flag_nooverwrite = true;
|
||||
flag_overwrite = false;
|
||||
|
|
@ -527,15 +528,28 @@ struct LibertyFrontend : public Frontend {
|
|||
flag_ignore_miss_data_latch = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-ignore_buses") {
|
||||
flag_ignore_buses = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-setattr" && argidx+1 < args.size()) {
|
||||
attributes.push_back(RTLIL::escape_id(args[++argidx]));
|
||||
continue;
|
||||
}
|
||||
if (arg == "-unit_delay") {
|
||||
flag_unit_delay = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
LibertyParser parser(*f);
|
||||
if (flag_wb && flag_lib)
|
||||
log_error("-wb and -lib cannot be specified together!\n");
|
||||
|
||||
log_header(design, "Executing Liberty frontend: %s\n", filename);
|
||||
|
||||
LibertyParser parser(*f, filename);
|
||||
int cell_count = 0;
|
||||
|
||||
std::map<std::string, std::tuple<int, int, bool>> global_type_map;
|
||||
|
|
@ -546,46 +560,41 @@ struct LibertyFrontend : public Frontend {
|
|||
if (cell->id != "cell" || cell->args.size() != 1)
|
||||
continue;
|
||||
|
||||
std::string cell_name = RTLIL::escape_id(cell->args.at(0));
|
||||
|
||||
if (design->has(cell_name)) {
|
||||
Module *existing_mod = design->module(cell_name);
|
||||
if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute(ID::blackbox)) {
|
||||
log_error("Re-definition of cell/module %s!\n", log_id(cell_name));
|
||||
} else if (flag_nooverwrite) {
|
||||
log("Ignoring re-definition of module %s.\n", log_id(cell_name));
|
||||
continue;
|
||||
} else {
|
||||
log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", log_id(cell_name));
|
||||
design->remove(existing_mod);
|
||||
}
|
||||
}
|
||||
|
||||
// log("Processing cell type %s.\n", RTLIL::unescape_id(cell_name).c_str());
|
||||
// log("Processing cell type %s.\n", RTLIL::unescape_id(cell_name));
|
||||
|
||||
std::map<std::string, std::tuple<int, int, bool>> type_map = global_type_map;
|
||||
parse_type_map(type_map, cell);
|
||||
|
||||
RTLIL::Module *module = new RTLIL::Module;
|
||||
std::string cell_name = RTLIL::escape_id(cell->args.at(0));
|
||||
module->name = cell_name;
|
||||
|
||||
if (flag_lib)
|
||||
module->set_bool_attribute(ID::blackbox);
|
||||
|
||||
if (flag_wb)
|
||||
module->set_bool_attribute(ID::whitebox);
|
||||
|
||||
const LibertyAst *area = cell->find("area");
|
||||
if (area)
|
||||
module->attributes[ID::area] = area->value;
|
||||
|
||||
for (auto &attr : attributes)
|
||||
module->attributes[attr] = 1;
|
||||
|
||||
bool simple_comb_cell = true, has_outputs = false;
|
||||
|
||||
for (auto node : cell->children)
|
||||
{
|
||||
if (node->id == "pin" && node->args.size() == 1) {
|
||||
LibertyAst *dir = node->find("direction");
|
||||
const LibertyAst *dir = node->find("direction");
|
||||
if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal"))
|
||||
{
|
||||
if (!flag_ignore_miss_dir)
|
||||
{
|
||||
log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0).c_str(), log_id(module->name));
|
||||
log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0), RTLIL::unescape_id(module->name));
|
||||
} else {
|
||||
log("Ignoring cell %s with missing or invalid direction for pin %s.\n", log_id(module->name), node->args.at(0).c_str());
|
||||
log("Ignoring cell %s with missing or invalid direction for pin %s.\n", RTLIL::unescape_id(module->name), node->args.at(0));
|
||||
delete module;
|
||||
goto skip_cell;
|
||||
}
|
||||
|
|
@ -596,28 +605,36 @@ struct LibertyFrontend : public Frontend {
|
|||
|
||||
if (node->id == "bus" && node->args.size() == 1)
|
||||
{
|
||||
if (!flag_lib)
|
||||
log_error("Error in cell %s: bus interfaces are only supported in -lib mode.\n", log_id(cell_name));
|
||||
if (flag_ignore_buses) {
|
||||
log("Ignoring cell %s with a bus interface %s.\n", RTLIL::unescape_id(module->name), node->args.at(0));
|
||||
delete module;
|
||||
goto skip_cell;
|
||||
}
|
||||
|
||||
LibertyAst *dir = node->find("direction");
|
||||
if (!flag_lib)
|
||||
log_error("Error in cell %s: bus interfaces are only supported in -lib mode.\n", RTLIL::unescape_id(cell_name));
|
||||
|
||||
const LibertyAst *dir = node->find("direction");
|
||||
|
||||
if (dir == nullptr) {
|
||||
LibertyAst *pin = node->find("pin");
|
||||
const LibertyAst *pin = node->find("pin");
|
||||
if (pin != nullptr)
|
||||
dir = pin->find("direction");
|
||||
}
|
||||
|
||||
if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal"))
|
||||
log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0).c_str(), log_id(module->name));
|
||||
log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0), RTLIL::unescape_id(module->name));
|
||||
|
||||
simple_comb_cell = false;
|
||||
|
||||
if (dir->value == "internal")
|
||||
continue;
|
||||
|
||||
LibertyAst *bus_type_node = node->find("bus_type");
|
||||
const LibertyAst *bus_type_node = node->find("bus_type");
|
||||
|
||||
if (!bus_type_node || !type_map.count(bus_type_node->value))
|
||||
log_error("Unknown or unsupported type for bus interface %s on cell %s.\n",
|
||||
node->args.at(0).c_str(), log_id(cell_name));
|
||||
node->args.at(0).c_str(), RTLIL::unescape_id(cell_name));
|
||||
|
||||
int bus_type_width = std::get<0>(type_map.at(bus_type_node->value));
|
||||
int bus_type_offset = std::get<1>(type_map.at(bus_type_node->value));
|
||||
|
|
@ -639,6 +656,13 @@ struct LibertyFrontend : public Frontend {
|
|||
{
|
||||
// some liberty files do not put ff/latch at the beginning of a cell
|
||||
// try to find "ff" or "latch" and create FF/latch _before_ processing all other nodes
|
||||
// but first, in case of balloon retention cells, we need all ff/latch output wires
|
||||
// defined before we add ff/latch cells
|
||||
for (auto node : cell->children)
|
||||
{
|
||||
if ((node->id == "ff" && node->args.size() == 2) || (node->id == "latch" && node->args.size() == 2))
|
||||
create_latch_ff_wires(module, node);
|
||||
}
|
||||
for (auto node : cell->children)
|
||||
{
|
||||
if (node->id == "ff" && node->args.size() == 2)
|
||||
|
|
@ -655,12 +679,20 @@ struct LibertyFrontend : public Frontend {
|
|||
{
|
||||
if (node->id == "pin" && node->args.size() == 1)
|
||||
{
|
||||
LibertyAst *dir = node->find("direction");
|
||||
const LibertyAst *dir = node->find("direction");
|
||||
|
||||
if (dir->value == "internal" || dir->value == "inout")
|
||||
simple_comb_cell = false;
|
||||
|
||||
if (flag_lib && dir->value == "internal")
|
||||
continue;
|
||||
|
||||
RTLIL::Wire *wire = module->wires_.at(RTLIL::escape_id(node->args.at(0)));
|
||||
log_assert(wire);
|
||||
|
||||
const LibertyAst *capacitance = node->find("capacitance");
|
||||
if (capacitance)
|
||||
wire->attributes[ID::capacitance] = capacitance->value;
|
||||
|
||||
if (dir && dir->value == "inout") {
|
||||
wire->port_input = true;
|
||||
|
|
@ -672,27 +704,84 @@ struct LibertyFrontend : public Frontend {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (dir && dir->value == "output")
|
||||
if (dir && dir->value == "output") {
|
||||
has_outputs = true;
|
||||
wire->port_output = true;
|
||||
}
|
||||
|
||||
if (flag_lib)
|
||||
continue;
|
||||
|
||||
LibertyAst *func = node->find("function");
|
||||
const LibertyAst *func = node->find("function");
|
||||
if (func == NULL)
|
||||
{
|
||||
if (!flag_ignore_miss_func)
|
||||
{
|
||||
log_error("Missing function on output %s of cell %s.\n", log_id(wire->name), log_id(module->name));
|
||||
} else {
|
||||
log("Ignoring cell %s with missing function on output %s.\n", log_id(module->name), log_id(wire->name));
|
||||
delete module;
|
||||
goto skip_cell;
|
||||
if (dir->value != "inout") { // allow inout with missing function, can be used for power pins
|
||||
if (!flag_ignore_miss_func)
|
||||
{
|
||||
log_error("Missing function on output %s of cell %s.\n", RTLIL::unescape_id(wire->name), RTLIL::unescape_id(module->name));
|
||||
} else {
|
||||
log("Ignoring cell %s with missing function on output %s.\n", RTLIL::unescape_id(module->name), RTLIL::unescape_id(wire->name));
|
||||
delete module;
|
||||
goto skip_cell;
|
||||
}
|
||||
}
|
||||
simple_comb_cell = false;
|
||||
} else {
|
||||
RTLIL::SigSpec out_sig = parse_func_expr(module, func->value.c_str());
|
||||
const LibertyAst *three_state = node->find("three_state");
|
||||
if (three_state) {
|
||||
out_sig = create_tristate(module, out_sig, three_state->value.c_str());
|
||||
simple_comb_cell = false;
|
||||
}
|
||||
module->connect(RTLIL::SigSig(wire, out_sig));
|
||||
}
|
||||
}
|
||||
|
||||
if (node->id == "ff" || node->id == "ff_bank" ||
|
||||
node->id == "latch" || node->id == "latch_bank" ||
|
||||
node->id == "statetable")
|
||||
simple_comb_cell = false;
|
||||
}
|
||||
|
||||
if (simple_comb_cell && has_outputs) {
|
||||
module->set_bool_attribute(ID::abc9_box);
|
||||
|
||||
if (flag_unit_delay) {
|
||||
for (auto wi : module->wires())
|
||||
if (wi->port_input) {
|
||||
for (auto wo : module->wires())
|
||||
if (wo->port_output) {
|
||||
RTLIL::Cell *spec = module->addCell(NEW_ID, ID($specify2));
|
||||
spec->setParam(ID::SRC_WIDTH, wi->width);
|
||||
spec->setParam(ID::DST_WIDTH, wo->width);
|
||||
spec->setParam(ID::T_FALL_MAX, 1000);
|
||||
spec->setParam(ID::T_FALL_TYP, 1000);
|
||||
spec->setParam(ID::T_FALL_MIN, 1000);
|
||||
spec->setParam(ID::T_RISE_MAX, 1000);
|
||||
spec->setParam(ID::T_RISE_TYP, 1000);
|
||||
spec->setParam(ID::T_RISE_MIN, 1000);
|
||||
spec->setParam(ID::SRC_DST_POL, false);
|
||||
spec->setParam(ID::SRC_DST_PEN, false);
|
||||
spec->setParam(ID::FULL, true);
|
||||
spec->setPort(ID::EN, Const(1, 1));
|
||||
spec->setPort(ID::SRC, wi);
|
||||
spec->setPort(ID::DST, wo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RTLIL::SigSpec out_sig = parse_func_expr(module, func->value.c_str());
|
||||
module->connect(RTLIL::SigSig(wire, out_sig));
|
||||
if (design->has(cell_name)) {
|
||||
Module *existing_mod = design->module(cell_name);
|
||||
if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute(ID::blackbox)) {
|
||||
log_error("Re-definition of cell/module %s!\n", RTLIL::unescape_id(cell_name));
|
||||
} else if (flag_nooverwrite) {
|
||||
log("Ignoring re-definition of module %s.\n", RTLIL::unescape_id(cell_name));
|
||||
delete module;
|
||||
goto skip_cell;
|
||||
} else {
|
||||
log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", RTLIL::unescape_id(cell_name));
|
||||
design->remove(existing_mod);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,17 +83,17 @@ struct RpcServer {
|
|||
std::string request;
|
||||
json_request.dump(request);
|
||||
request += '\n';
|
||||
log_debug("RPC frontend request: %s", request.c_str());
|
||||
log_debug("RPC frontend request: %s", request);
|
||||
write(request);
|
||||
|
||||
std::string response = read();
|
||||
log_debug("RPC frontend response: %s", response.c_str());
|
||||
log_debug("RPC frontend response: %s", response);
|
||||
std::string error;
|
||||
Json json_response = Json::parse(response, error);
|
||||
if (json_response.is_null())
|
||||
log_cmd_error("parsing JSON failed: %s\n", error.c_str());
|
||||
log_cmd_error("parsing JSON failed: %s\n", error);
|
||||
if (json_response["error"].is_string())
|
||||
log_cmd_error("RPC frontend returned an error: %s\n", json_response["error"].string_value().c_str());
|
||||
log_cmd_error("RPC frontend returned an error: %s\n", json_response["error"].string_value());
|
||||
return json_response;
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +111,7 @@ struct RpcServer {
|
|||
}
|
||||
} else is_valid = false;
|
||||
if (!is_valid)
|
||||
log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump().c_str());
|
||||
log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump());
|
||||
return modules;
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ struct RpcServer {
|
|||
source = response["source"].string_value();
|
||||
else is_valid = false;
|
||||
if (!is_valid)
|
||||
log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump().c_str());
|
||||
log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump());
|
||||
return std::make_pair(frontend, source);
|
||||
}
|
||||
};
|
||||
|
|
@ -163,12 +163,12 @@ struct RpcModule : RTLIL::Module {
|
|||
stripped_name = stripped_name.substr(9);
|
||||
log_assert(stripped_name[0] == '\\');
|
||||
|
||||
log_header(design, "Executing RPC frontend `%s' for module `%s'.\n", server->name.c_str(), stripped_name.c_str());
|
||||
log_header(design, "Executing RPC frontend `%s' for module `%s'.\n", server->name, stripped_name);
|
||||
|
||||
std::string parameter_info;
|
||||
for (auto ¶m : parameters) {
|
||||
log("Parameter %s = %s\n", param.first.c_str(), log_signal(RTLIL::SigSpec(param.second)));
|
||||
parameter_info += stringf("%s=%s", param.first.c_str(), log_signal(RTLIL::SigSpec(param.second)));
|
||||
log("Parameter %s = %s\n", param.first, log_signal(RTLIL::SigSpec(param.second)));
|
||||
parameter_info += stringf("%s=%s", param.first, log_signal(RTLIL::SigSpec(param.second)));
|
||||
}
|
||||
|
||||
std::string derived_name;
|
||||
|
|
@ -180,7 +180,7 @@ struct RpcModule : RTLIL::Module {
|
|||
derived_name = "$paramod" + stripped_name + parameter_info;
|
||||
|
||||
if (design->has(derived_name)) {
|
||||
log("Found cached RTLIL representation for module `%s'.\n", derived_name.c_str());
|
||||
log("Found cached RTLIL representation for module `%s'.\n", derived_name);
|
||||
} else {
|
||||
std::string command, input;
|
||||
std::tie(command, input) = server->derive_module(stripped_name.substr(1), parameters);
|
||||
|
|
@ -202,7 +202,7 @@ struct RpcModule : RTLIL::Module {
|
|||
}
|
||||
}
|
||||
if (!found_derived_top)
|
||||
log_cmd_error("RPC frontend did not return requested module `%s`!\n", stripped_name.c_str());
|
||||
log_cmd_error("RPC frontend did not return requested module `%s`!\n", stripped_name);
|
||||
|
||||
for (auto module : derived_design->modules())
|
||||
for (auto cell : module->cells())
|
||||
|
|
@ -256,7 +256,7 @@ struct HandleRpcServer : RpcServer {
|
|||
do {
|
||||
DWORD data_written;
|
||||
if (!WriteFile(hsend, &data[offset], data.length() - offset, &data_written, /*lpOverlapped=*/NULL))
|
||||
log_cmd_error("WriteFile failed: %s\n", get_last_error_str().c_str());
|
||||
log_cmd_error("WriteFile failed: %s\n", get_last_error_str());
|
||||
offset += data_written;
|
||||
} while(offset < (ssize_t)data.length());
|
||||
}
|
||||
|
|
@ -268,7 +268,7 @@ struct HandleRpcServer : RpcServer {
|
|||
data.resize(data.length() + 1024);
|
||||
DWORD data_read;
|
||||
if (!ReadFile(hrecv, &data[offset], data.length() - offset, &data_read, /*lpOverlapped=*/NULL))
|
||||
log_cmd_error("ReadFile failed: %s\n", get_last_error_str().c_str());
|
||||
log_cmd_error("ReadFile failed: %s\n", get_last_error_str());
|
||||
offset += data_read;
|
||||
data.resize(offset);
|
||||
size_t term_pos = data.find('\n', offset);
|
||||
|
|
@ -383,12 +383,12 @@ struct RpcFrontend : public Pass {
|
|||
log(" request for the module <module-name> to be derived for a specific set of\n");
|
||||
log(" parameters. <param-name> starts with \\ for named parameters, and with $\n");
|
||||
log(" for unnamed parameters, which are numbered starting at 1.<param-value>\n");
|
||||
log(" for integer parameters is always specified as a binary string of unlimited\n");
|
||||
log(" precision. the <source> returned by the frontend is hygienically parsed\n");
|
||||
log(" by a built-in Yosys <frontend>, allowing the RPC frontend to return any\n");
|
||||
log(" convenient representation of the module. the derived module is cached,\n");
|
||||
log(" so the response should be the same whenever the same set of parameters\n");
|
||||
log(" is provided.\n");
|
||||
log(" for integer parameters is always specified as a binary string of\n");
|
||||
log(" unlimited precision. the <source> returned by the frontend is\n");
|
||||
log(" hygienically parsedby a built-in Yosys <frontend>, allowing the RPC\n");
|
||||
log(" frontend to return anyconvenient representation of the module. the\n");
|
||||
log(" derived module is cached,so the response should be the same whenever the\n");
|
||||
log(" same set of parameters is provided.\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
|
|
@ -437,7 +437,7 @@ struct RpcFrontend : public Pass {
|
|||
|
||||
command_path_len_w = SearchPathW(/*lpPath=*/NULL, /*lpFileName=*/command_w.c_str(), /*lpExtension=*/L".exe", /*nBufferLength=*/0, /*lpBuffer=*/NULL, /*lpFilePart=*/NULL);
|
||||
if (command_path_len_w == 0) {
|
||||
log_error("SearchPathW failed: %s\n", get_last_error_str().c_str());
|
||||
log_error("SearchPathW failed: %s\n", get_last_error_str());
|
||||
goto cleanup_exec;
|
||||
}
|
||||
command_path_w.resize(command_path_len_w - 1);
|
||||
|
|
@ -448,19 +448,19 @@ struct RpcFrontend : public Pass {
|
|||
pipe_attr.bInheritHandle = TRUE;
|
||||
pipe_attr.lpSecurityDescriptor = NULL;
|
||||
if (!CreatePipe(&send_r, &send_w, &pipe_attr, /*nSize=*/0)) {
|
||||
log_error("CreatePipe failed: %s\n", get_last_error_str().c_str());
|
||||
log_error("CreatePipe failed: %s\n", get_last_error_str());
|
||||
goto cleanup_exec;
|
||||
}
|
||||
if (!SetHandleInformation(send_w, HANDLE_FLAG_INHERIT, 0)) {
|
||||
log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str());
|
||||
log_error("SetHandleInformation failed: %s\n", get_last_error_str());
|
||||
goto cleanup_exec;
|
||||
}
|
||||
if (!CreatePipe(&recv_r, &recv_w, &pipe_attr, /*nSize=*/0)) {
|
||||
log_error("CreatePipe failed: %s\n", get_last_error_str().c_str());
|
||||
log_error("CreatePipe failed: %s\n", get_last_error_str());
|
||||
goto cleanup_exec;
|
||||
}
|
||||
if (!SetHandleInformation(recv_r, HANDLE_FLAG_INHERIT, 0)) {
|
||||
log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str());
|
||||
log_error("SetHandleInformation failed: %s\n", get_last_error_str());
|
||||
goto cleanup_exec;
|
||||
}
|
||||
|
||||
|
|
@ -470,7 +470,7 @@ struct RpcFrontend : public Pass {
|
|||
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
startup_info.dwFlags |= STARTF_USESTDHANDLES;
|
||||
if (!CreateProcessW(/*lpApplicationName=*/command_path_w.c_str(), /*lpCommandLine=*/&command_line_w[0], /*lpProcessAttributes=*/NULL, /*lpThreadAttributes=*/NULL, /*bInheritHandles=*/TRUE, /*dwCreationFlags=*/0, /*lpEnvironment=*/NULL, /*lpCurrentDirectory=*/NULL, &startup_info, &proc_info)) {
|
||||
log_error("CreateProcessW failed: %s\n", get_last_error_str().c_str());
|
||||
log_error("CreateProcessW failed: %s\n", get_last_error_str());
|
||||
goto cleanup_exec;
|
||||
}
|
||||
CloseHandle(proc_info.hProcess);
|
||||
|
|
@ -550,7 +550,7 @@ cleanup_exec:
|
|||
|
||||
h = CreateFileW(path_w.c_str(), GENERIC_READ|GENERIC_WRITE, /*dwShareMode=*/0, /*lpSecurityAttributes=*/NULL, /*dwCreationDisposition=*/OPEN_EXISTING, /*dwFlagsAndAttributes=*/0, /*hTemplateFile=*/NULL);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
log_error("CreateFileW failed: %s\n", get_last_error_str().c_str());
|
||||
log_error("CreateFileW failed: %s\n", get_last_error_str());
|
||||
goto cleanup_path;
|
||||
}
|
||||
|
||||
|
|
@ -586,7 +586,7 @@ cleanup_path:
|
|||
log_cmd_error("Failed to connect to RPC frontend.\n");
|
||||
|
||||
for (auto &module_name : server->get_module_names()) {
|
||||
log("Linking module `%s'.\n", module_name.c_str());
|
||||
log("Linking module `%s'.\n", module_name);
|
||||
RpcModule *module = new RpcModule;
|
||||
module->name = "$abstract\\" + module_name;
|
||||
module->server = server;
|
||||
|
|
|
|||
|
|
@ -1,19 +1 @@
|
|||
|
||||
GENFILES += frontends/rtlil/rtlil_parser.tab.cc
|
||||
GENFILES += frontends/rtlil/rtlil_parser.tab.hh
|
||||
GENFILES += frontends/rtlil/rtlil_parser.output
|
||||
GENFILES += frontends/rtlil/rtlil_lexer.cc
|
||||
|
||||
frontends/rtlil/rtlil_parser.tab.cc: frontends/rtlil/rtlil_parser.y
|
||||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) $(BISON) -o $@ -d -r all -b frontends/rtlil/rtlil_parser $<
|
||||
|
||||
frontends/rtlil/rtlil_parser.tab.hh: frontends/rtlil/rtlil_parser.tab.cc
|
||||
|
||||
frontends/rtlil/rtlil_lexer.cc: frontends/rtlil/rtlil_lexer.l
|
||||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) flex -o frontends/rtlil/rtlil_lexer.cc $<
|
||||
|
||||
OBJS += frontends/rtlil/rtlil_parser.tab.o frontends/rtlil/rtlil_lexer.o
|
||||
OBJS += frontends/rtlil/rtlil_frontend.o
|
||||
|
||||
|
|
|
|||
|
|
@ -17,22 +17,771 @@
|
|||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward frontend for the RTLIL text
|
||||
* representation.
|
||||
* A handwritten recursive-descent parser for the RTLIL text representation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "rtlil_frontend.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/log.h"
|
||||
|
||||
void rtlil_frontend_yyerror(char const *s)
|
||||
{
|
||||
YOSYS_NAMESPACE_PREFIX log_error("Parser error in line %d: %s\n", rtlil_frontend_yyget_lineno(), s);
|
||||
}
|
||||
#include "kernel/utils.h"
|
||||
#include <charconv>
|
||||
#include <deque>
|
||||
#include <optional>
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
struct RTLILFrontendWorker {
|
||||
// Forbid constants of more than 1 Gb.
|
||||
// This will help us not explode on malicious RTLIL.
|
||||
static constexpr int MAX_CONST_WIDTH = 1024 * 1024 * 1024;
|
||||
|
||||
std::istream *f = nullptr;
|
||||
RTLIL::Design *design;
|
||||
bool flag_nooverwrite = false;
|
||||
bool flag_overwrite = false;
|
||||
bool flag_lib = false;
|
||||
|
||||
int line_num;
|
||||
std::string line_buf;
|
||||
// Substring of line_buf. Always newline-terminated, thus never empty.
|
||||
std::string_view line;
|
||||
|
||||
RTLIL::Module *current_module;
|
||||
dict<RTLIL::IdString, RTLIL::Const> attrbuf;
|
||||
std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack;
|
||||
std::vector<RTLIL::CaseRule*> case_stack;
|
||||
|
||||
template <typename... Args>
|
||||
[[noreturn]]
|
||||
void error(FmtString<TypeIdentity<Args>...> fmt, const Args &... args)
|
||||
{
|
||||
log_error("Parser error in line %d: %s\n", line_num, fmt.format(args...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void warning(FmtString<TypeIdentity<Args>...> fmt, const Args &... args)
|
||||
{
|
||||
log_warning("In line %d: %s\n", line_num, fmt.format(args...));
|
||||
}
|
||||
|
||||
// May return an empty line if the stream is not good().
|
||||
void advance_to_next_nonempty_line()
|
||||
{
|
||||
if (!f->good()) {
|
||||
line = "\n";
|
||||
return;
|
||||
}
|
||||
while (true) {
|
||||
std::getline(*f, line_buf);
|
||||
line_num++;
|
||||
if (line_buf.empty() || line_buf[line_buf.size() - 1] != '\n')
|
||||
line_buf += '\n';
|
||||
line = line_buf;
|
||||
consume_whitespace_and_comments();
|
||||
if (line[0] != '\n' || !f->good())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void consume_whitespace_and_comments()
|
||||
{
|
||||
while (true) {
|
||||
switch (line[0]) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
line = line.substr(1);
|
||||
break;
|
||||
case '#':
|
||||
line = "\n";
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool try_parse_keyword(std::string_view keyword)
|
||||
{
|
||||
int keyword_size = keyword.size();
|
||||
if (keyword != line.substr(0, keyword_size))
|
||||
return false;
|
||||
// This index is safe because `line` is always newline-terminated
|
||||
// and `keyword` never contains a newline.
|
||||
char ch = line[keyword_size];
|
||||
if (ch >= 'a' && ch <= 'z')
|
||||
return false;
|
||||
line = line.substr(keyword_size);
|
||||
consume_whitespace_and_comments();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string error_token()
|
||||
{
|
||||
std::string result;
|
||||
for (char ch : line) {
|
||||
if (ch == '\n' || ch == ' ' || ch == '\t')
|
||||
break;
|
||||
result += ch;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void expect_keyword(std::string_view keyword)
|
||||
{
|
||||
if (!try_parse_keyword(keyword))
|
||||
error("Expected token `%s', got `%s'.", keyword, error_token());
|
||||
}
|
||||
|
||||
bool try_parse_char(char ch)
|
||||
{
|
||||
if (line[0] != ch)
|
||||
return false;
|
||||
line = line.substr(1);
|
||||
consume_whitespace_and_comments();
|
||||
return true;
|
||||
}
|
||||
|
||||
void expect_char(char ch)
|
||||
{
|
||||
if (!try_parse_char(ch))
|
||||
error("Expected `%c', got `%s'.", ch, error_token());
|
||||
}
|
||||
|
||||
bool try_parse_eol()
|
||||
{
|
||||
if (line[0] != '\n')
|
||||
return false;
|
||||
advance_to_next_nonempty_line();
|
||||
return true;
|
||||
}
|
||||
|
||||
void expect_eol()
|
||||
{
|
||||
if (!try_parse_eol())
|
||||
error("Expected EOL, got `%s'.", error_token());
|
||||
}
|
||||
|
||||
std::optional<RTLIL::IdString> try_parse_id()
|
||||
{
|
||||
char ch = line[0];
|
||||
if (ch != '\\' && ch != '$')
|
||||
return std::nullopt;
|
||||
int idx = 1;
|
||||
while (true) {
|
||||
ch = line[idx];
|
||||
if (ch <= ' ' && (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'))
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
IdString result(line.substr(0, idx));
|
||||
line = line.substr(idx);
|
||||
consume_whitespace_and_comments();
|
||||
return result;
|
||||
}
|
||||
|
||||
RTLIL::IdString parse_id()
|
||||
{
|
||||
std::optional<RTLIL::IdString> id = try_parse_id();
|
||||
if (!id.has_value())
|
||||
error("Expected ID, got `%s'.", error_token());
|
||||
return std::move(*id);
|
||||
}
|
||||
|
||||
long long parse_integer()
|
||||
{
|
||||
long long result = parse_integer_alone();
|
||||
consume_whitespace_and_comments();
|
||||
return result;
|
||||
}
|
||||
|
||||
long long parse_integer_alone()
|
||||
{
|
||||
int idx = 0;
|
||||
if (line[idx] == '-')
|
||||
++idx;
|
||||
while (true) {
|
||||
char ch = line[idx];
|
||||
if (ch < '0' || ch > '9')
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
long long result;
|
||||
if (std::from_chars(line.data(), line.data() + idx, result, 10).ec != std::errc{})
|
||||
error("Invalid integer `%s'.", error_token());
|
||||
line = line.substr(idx);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string parse_string()
|
||||
{
|
||||
if (line[0] != '\"')
|
||||
error("Expected string, got `%s'.", error_token());
|
||||
std::string str;
|
||||
int idx = 1;
|
||||
while (true) {
|
||||
int start_idx = idx;
|
||||
char ch;
|
||||
while (true) {
|
||||
ch = line[idx];
|
||||
if (ch == '"' || ch == '\n' || ch == '\\' || ch == 0)
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
str.append(line.data() + start_idx, line.data() + idx);
|
||||
++idx;
|
||||
if (ch == '"')
|
||||
break;
|
||||
if (ch == 0)
|
||||
error("Null byte in string literal: `%s'.", line);
|
||||
if (ch == '\n')
|
||||
error("Unterminated string literal: `%s'.", line);
|
||||
ch = line[idx++];
|
||||
if (ch == 'n') {
|
||||
ch = '\n';
|
||||
} else if (ch == 't') {
|
||||
ch = '\t';
|
||||
} else if (ch >= '0' && ch <= '7') {
|
||||
int v = ch - '0';
|
||||
char next_ch = line[idx + 1];
|
||||
if (next_ch >= '0' && next_ch <= '7') {
|
||||
++idx;
|
||||
v = v*8 + (next_ch - '0');
|
||||
next_ch = line[idx + 1];
|
||||
if (next_ch >= '0' && next_ch <= '7') {
|
||||
++idx;
|
||||
v = v*8 + (next_ch - '0');
|
||||
}
|
||||
}
|
||||
ch = v;
|
||||
}
|
||||
str += ch;
|
||||
}
|
||||
line = line.substr(idx);
|
||||
consume_whitespace_and_comments();
|
||||
return str;
|
||||
}
|
||||
|
||||
RTLIL::Const parse_const()
|
||||
{
|
||||
if (line[0] == '"')
|
||||
return RTLIL::Const(parse_string());
|
||||
|
||||
bool negative_value = line[0] == '-';
|
||||
long long width = parse_integer_alone();
|
||||
// Can't test value<0 here because we need to stop parsing after '-0'
|
||||
if (negative_value || line[0] != '\'') {
|
||||
if (width < INT_MIN || width > INT_MAX)
|
||||
error("Integer %lld out of range before `%s'.", width, error_token());
|
||||
consume_whitespace_and_comments();
|
||||
return RTLIL::Const(width);
|
||||
}
|
||||
|
||||
int idx = 1;
|
||||
bool is_signed = line[1] == 's';
|
||||
if (is_signed)
|
||||
++idx;
|
||||
|
||||
std::vector<RTLIL::State> bits;
|
||||
if (width > MAX_CONST_WIDTH)
|
||||
error("Constant width %lld out of range before `%s`.", width, error_token());
|
||||
bits.reserve(width);
|
||||
while (true) {
|
||||
RTLIL::State bit;
|
||||
switch (line[idx]) {
|
||||
case '0': bit = RTLIL::S0; break;
|
||||
case '1': bit = RTLIL::S1; break;
|
||||
case 'x': bit = RTLIL::Sx; break;
|
||||
case 'z': bit = RTLIL::Sz; break;
|
||||
case 'm': bit = RTLIL::Sm; break;
|
||||
case '-': bit = RTLIL::Sa; break;
|
||||
default: goto done;
|
||||
}
|
||||
bits.push_back(bit);
|
||||
++idx;
|
||||
}
|
||||
done:
|
||||
std::reverse(bits.begin(), bits.end());
|
||||
|
||||
if (GetSize(bits) > width)
|
||||
bits.resize(width);
|
||||
else if (GetSize(bits) < width) {
|
||||
RTLIL::State extbit = RTLIL::Sx;
|
||||
if (!bits.empty()) {
|
||||
extbit = bits.back();
|
||||
if (extbit == RTLIL::S1)
|
||||
extbit = RTLIL::S0;
|
||||
}
|
||||
bits.resize(width, extbit);
|
||||
}
|
||||
|
||||
RTLIL::Const val(std::move(bits));
|
||||
if (is_signed)
|
||||
val.flags |= RTLIL::CONST_FLAG_SIGNED;
|
||||
line = line.substr(idx);
|
||||
consume_whitespace_and_comments();
|
||||
return val;
|
||||
}
|
||||
|
||||
RTLIL::SigSpec parse_sigspec()
|
||||
{
|
||||
RTLIL::SigSpec sig;
|
||||
|
||||
if (try_parse_char('{')) {
|
||||
std::vector<SigSpec> parts;
|
||||
while (!try_parse_char('}'))
|
||||
parts.push_back(parse_sigspec());
|
||||
for (auto it = parts.rbegin(); it != parts.rend(); ++it)
|
||||
sig.append(std::move(*it));
|
||||
} else {
|
||||
// We could add a special path for parsing IdStrings that must already exist,
|
||||
// as here.
|
||||
// We don't need to addref/release in this case.
|
||||
std::optional<RTLIL::IdString> id = try_parse_id();
|
||||
if (id.has_value()) {
|
||||
RTLIL::Wire *wire = current_module->wire(*id);
|
||||
if (wire == nullptr)
|
||||
error("Wire `%s' not found.", *id);
|
||||
sig = RTLIL::SigSpec(wire);
|
||||
} else {
|
||||
sig = RTLIL::SigSpec(parse_const());
|
||||
}
|
||||
}
|
||||
|
||||
while (try_parse_char('[')) {
|
||||
int left = parse_integer();
|
||||
if (left >= sig.size() || left < 0)
|
||||
error("bit index %d out of range", left);
|
||||
if (try_parse_char(':')) {
|
||||
int right = parse_integer();
|
||||
if (right < 0)
|
||||
error("bit index %d out of range", right);
|
||||
if (left < right)
|
||||
error("invalid slice [%d:%d]", left, right);
|
||||
sig = sig.extract(right, left-right+1);
|
||||
} else {
|
||||
sig = sig.extract(left);
|
||||
}
|
||||
expect_char(']');
|
||||
}
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
void parse_module()
|
||||
{
|
||||
RTLIL::IdString module_name = parse_id();
|
||||
expect_eol();
|
||||
|
||||
bool delete_current_module = false;
|
||||
if (design->has(module_name)) {
|
||||
RTLIL::Module *existing_mod = design->module(module_name);
|
||||
if (!flag_overwrite && (flag_lib || (attrbuf.count(ID::blackbox) && attrbuf.at(ID::blackbox).as_bool()))) {
|
||||
log("Ignoring blackbox re-definition of module %s.\n", module_name);
|
||||
delete_current_module = true;
|
||||
} else if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute(ID::blackbox)) {
|
||||
error("RTLIL error: redefinition of module %s.", module_name);
|
||||
} else if (flag_nooverwrite) {
|
||||
log("Ignoring re-definition of module %s.\n", module_name);
|
||||
delete_current_module = true;
|
||||
} else {
|
||||
log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", module_name);
|
||||
design->remove(existing_mod);
|
||||
}
|
||||
}
|
||||
|
||||
current_module = new RTLIL::Module;
|
||||
current_module->name = std::move(module_name);
|
||||
current_module->attributes = std::move(attrbuf);
|
||||
if (!delete_current_module)
|
||||
design->add(current_module);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (try_parse_keyword("attribute")) {
|
||||
parse_attribute();
|
||||
continue;
|
||||
}
|
||||
if (try_parse_keyword("parameter")) {
|
||||
parse_parameter();
|
||||
continue;
|
||||
}
|
||||
if (try_parse_keyword("connect")) {
|
||||
parse_connect();
|
||||
continue;
|
||||
}
|
||||
if (try_parse_keyword("wire")) {
|
||||
parse_wire();
|
||||
continue;
|
||||
}
|
||||
if (try_parse_keyword("cell")) {
|
||||
parse_cell();
|
||||
continue;
|
||||
}
|
||||
if (try_parse_keyword("memory")) {
|
||||
parse_memory();
|
||||
continue;
|
||||
}
|
||||
if (try_parse_keyword("process")) {
|
||||
parse_process();
|
||||
continue;
|
||||
}
|
||||
if (try_parse_keyword("end")) {
|
||||
expect_eol();
|
||||
break;
|
||||
}
|
||||
error("Unexpected token in module body: %s", error_token());
|
||||
}
|
||||
|
||||
if (attrbuf.size() != 0)
|
||||
error("dangling attribute");
|
||||
current_module->fixup_ports();
|
||||
if (delete_current_module)
|
||||
delete current_module;
|
||||
else if (flag_lib)
|
||||
current_module->makeblackbox();
|
||||
current_module = nullptr;
|
||||
}
|
||||
|
||||
void parse_attribute()
|
||||
{
|
||||
RTLIL::IdString id = parse_id();
|
||||
RTLIL::Const c = parse_const();
|
||||
attrbuf.insert({std::move(id), std::move(c)});
|
||||
expect_eol();
|
||||
}
|
||||
|
||||
void parse_parameter()
|
||||
{
|
||||
RTLIL::IdString id = parse_id();
|
||||
current_module->avail_parameters(id);
|
||||
if (try_parse_eol())
|
||||
return;
|
||||
RTLIL::Const c = parse_const();
|
||||
current_module->parameter_default_values.insert({std::move(id), std::move(c)});
|
||||
expect_eol();
|
||||
}
|
||||
|
||||
void parse_wire()
|
||||
{
|
||||
RTLIL::Wire *wire;
|
||||
int width = 1;
|
||||
int start_offset = 0;
|
||||
int port_id = 0;
|
||||
bool port_input = false;
|
||||
bool port_output = false;
|
||||
bool upto = false;
|
||||
bool is_signed = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::optional<RTLIL::IdString> id = try_parse_id();
|
||||
if (id.has_value()) {
|
||||
if (current_module->wire(*id) != nullptr)
|
||||
error("RTLIL error: redefinition of wire %s.", *id);
|
||||
wire = current_module->addWire(std::move(*id));
|
||||
break;
|
||||
}
|
||||
if (try_parse_keyword("width"))
|
||||
width = parse_integer();
|
||||
else if (try_parse_keyword("upto"))
|
||||
upto = true;
|
||||
else if (try_parse_keyword("signed"))
|
||||
is_signed = true;
|
||||
else if (try_parse_keyword("offset"))
|
||||
start_offset = parse_integer();
|
||||
else if (try_parse_keyword("input")) {
|
||||
port_id = parse_integer();
|
||||
port_input = true;
|
||||
} else if (try_parse_keyword("output")) {
|
||||
port_id = parse_integer();
|
||||
port_output = true;
|
||||
} else if (try_parse_keyword("inout")) {
|
||||
port_id = parse_integer();
|
||||
port_input = true;
|
||||
port_output = true;
|
||||
} else if (try_parse_eol())
|
||||
error("Missing wire ID");
|
||||
else
|
||||
error("Unexpected wire option: %s", error_token());
|
||||
}
|
||||
|
||||
wire->attributes = std::move(attrbuf);
|
||||
wire->width = width;
|
||||
wire->upto = upto;
|
||||
wire->start_offset = start_offset;
|
||||
wire->is_signed = is_signed;
|
||||
wire->port_id = port_id;
|
||||
wire->port_input = port_input;
|
||||
wire->port_output = port_output;
|
||||
expect_eol();
|
||||
}
|
||||
|
||||
void parse_memory()
|
||||
{
|
||||
RTLIL::Memory *memory = new RTLIL::Memory;
|
||||
memory->attributes = std::move(attrbuf);
|
||||
|
||||
int width = 1;
|
||||
int start_offset = 0;
|
||||
int size = 0;
|
||||
while (true)
|
||||
{
|
||||
std::optional<RTLIL::IdString> id = try_parse_id();
|
||||
if (id.has_value()) {
|
||||
if (current_module->memories.count(*id) != 0)
|
||||
error("RTLIL error: redefinition of memory %s.", *id);
|
||||
memory->name = std::move(*id);
|
||||
break;
|
||||
}
|
||||
if (try_parse_keyword("width"))
|
||||
width = parse_integer();
|
||||
else if (try_parse_keyword("size"))
|
||||
size = parse_integer();
|
||||
else if (try_parse_keyword("offset"))
|
||||
start_offset = parse_integer();
|
||||
else if (try_parse_eol())
|
||||
error("Missing memory ID");
|
||||
else
|
||||
error("Unexpected memory option: %s", error_token());
|
||||
}
|
||||
memory->width = width;
|
||||
memory->start_offset = start_offset;
|
||||
memory->size = size;
|
||||
current_module->memories.insert({memory->name, memory});
|
||||
expect_eol();
|
||||
}
|
||||
|
||||
void parse_cell()
|
||||
{
|
||||
RTLIL::IdString cell_type = parse_id();
|
||||
RTLIL::IdString cell_name = parse_id();
|
||||
expect_eol();
|
||||
|
||||
if (current_module->cell(cell_name) != nullptr)
|
||||
error("RTLIL error: redefinition of cell %s.", cell_name);
|
||||
RTLIL::Cell *cell = current_module->addCell(cell_name, cell_type);
|
||||
cell->attributes = std::move(attrbuf);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (try_parse_keyword("parameter")) {
|
||||
bool is_signed = false;
|
||||
bool is_real = false;
|
||||
bool is_unsized = false;
|
||||
if (try_parse_keyword("signed")) {
|
||||
is_signed = true;
|
||||
} else if (try_parse_keyword("real")) {
|
||||
is_real = true;
|
||||
} else if (try_parse_keyword("unsized")) {
|
||||
is_unsized = true;
|
||||
}
|
||||
RTLIL::IdString param_name = parse_id();
|
||||
RTLIL::Const val = parse_const();
|
||||
if (is_signed)
|
||||
val.flags |= RTLIL::CONST_FLAG_SIGNED;
|
||||
if (is_real)
|
||||
val.flags |= RTLIL::CONST_FLAG_REAL;
|
||||
if (is_unsized)
|
||||
val.flags |= RTLIL::CONST_FLAG_UNSIZED;
|
||||
cell->parameters.insert({std::move(param_name), std::move(val)});
|
||||
expect_eol();
|
||||
} else if (try_parse_keyword("connect")) {
|
||||
RTLIL::IdString port_name = parse_id();
|
||||
if (cell->hasPort(port_name))
|
||||
error("RTLIL error: redefinition of cell port %s.", port_name);
|
||||
cell->setPort(std::move(port_name), parse_sigspec());
|
||||
expect_eol();
|
||||
} else if (try_parse_keyword("end")) {
|
||||
expect_eol();
|
||||
break;
|
||||
} else {
|
||||
error("Unexpected token in cell body: %s", error_token());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parse_connect()
|
||||
{
|
||||
if (attrbuf.size() != 0)
|
||||
error("dangling attribute");
|
||||
RTLIL::SigSpec s1 = parse_sigspec();
|
||||
RTLIL::SigSpec s2 = parse_sigspec();
|
||||
current_module->connect(std::move(s1), std::move(s2));
|
||||
expect_eol();
|
||||
}
|
||||
|
||||
void parse_case_body(RTLIL::CaseRule *current_case)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (try_parse_keyword("attribute"))
|
||||
parse_attribute();
|
||||
else if (try_parse_keyword("switch"))
|
||||
parse_switch();
|
||||
else if (try_parse_keyword("assign")) {
|
||||
if (attrbuf.size() != 0)
|
||||
error("dangling attribute");
|
||||
// See https://github.com/YosysHQ/yosys/pull/4765 for discussion on this
|
||||
// warning
|
||||
if (!switch_stack.back()->empty())
|
||||
warning("case rule assign statements after switch statements may cause unexpected behaviour. "
|
||||
"The assign statement is reordered to come before all switch statements.");
|
||||
RTLIL::SigSpec s1 = parse_sigspec();
|
||||
RTLIL::SigSpec s2 = parse_sigspec();
|
||||
current_case->actions.push_back(RTLIL::SigSig(std::move(s1), std::move(s2)));
|
||||
expect_eol();
|
||||
} else
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void parse_switch()
|
||||
{
|
||||
RTLIL::SwitchRule *rule = new RTLIL::SwitchRule;
|
||||
rule->signal = parse_sigspec();
|
||||
rule->attributes = std::move(attrbuf);
|
||||
switch_stack.back()->push_back(rule);
|
||||
expect_eol();
|
||||
|
||||
while (true) {
|
||||
if (try_parse_keyword("attribute")) {
|
||||
parse_attribute();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (try_parse_keyword("end")) {
|
||||
expect_eol();
|
||||
break;
|
||||
}
|
||||
|
||||
expect_keyword("case");
|
||||
RTLIL::CaseRule *case_rule = new RTLIL::CaseRule;
|
||||
case_rule->attributes = std::move(attrbuf);
|
||||
rule->cases.push_back(case_rule);
|
||||
switch_stack.push_back(&case_rule->switches);
|
||||
case_stack.push_back(case_rule);
|
||||
|
||||
if (!try_parse_eol()) {
|
||||
while (true) {
|
||||
case_rule->compare.push_back(parse_sigspec());
|
||||
if (try_parse_eol())
|
||||
break;
|
||||
expect_char(',');
|
||||
}
|
||||
}
|
||||
|
||||
parse_case_body(case_rule);
|
||||
|
||||
switch_stack.pop_back();
|
||||
case_stack.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void parse_process()
|
||||
{
|
||||
RTLIL::IdString proc_name = parse_id();
|
||||
expect_eol();
|
||||
|
||||
if (current_module->processes.count(proc_name) != 0)
|
||||
error("RTLIL error: redefinition of process %s.", proc_name);
|
||||
RTLIL::Process *proc = current_module->addProcess(std::move(proc_name));
|
||||
proc->attributes = std::move(attrbuf);
|
||||
|
||||
switch_stack.clear();
|
||||
switch_stack.push_back(&proc->root_case.switches);
|
||||
case_stack.clear();
|
||||
case_stack.push_back(&proc->root_case);
|
||||
|
||||
parse_case_body(&proc->root_case);
|
||||
|
||||
while (try_parse_keyword("sync"))
|
||||
{
|
||||
RTLIL::SyncRule *rule = new RTLIL::SyncRule;
|
||||
|
||||
if (try_parse_keyword("low")) rule->type = RTLIL::ST0;
|
||||
else if (try_parse_keyword("high")) rule->type = RTLIL::ST1;
|
||||
else if (try_parse_keyword("posedge")) rule->type = RTLIL::STp;
|
||||
else if (try_parse_keyword("negedge")) rule->type = RTLIL::STn;
|
||||
else if (try_parse_keyword("edge")) rule->type = RTLIL::STe;
|
||||
else if (try_parse_keyword("always")) rule->type = RTLIL::STa;
|
||||
else if (try_parse_keyword("global")) rule->type = RTLIL::STg;
|
||||
else if (try_parse_keyword("init")) rule->type = RTLIL::STi;
|
||||
else error("Unexpected sync type: %s", error_token());
|
||||
|
||||
if (rule->type != RTLIL::STa && rule->type != RTLIL::STg && rule->type != RTLIL::STi)
|
||||
rule->signal = parse_sigspec();
|
||||
proc->syncs.push_back(rule);
|
||||
expect_eol();
|
||||
|
||||
bool attributes_in_update_list = false;
|
||||
while (true)
|
||||
{
|
||||
if (try_parse_keyword("update")) {
|
||||
RTLIL::SigSpec s1 = parse_sigspec();
|
||||
RTLIL::SigSpec s2 = parse_sigspec();
|
||||
rule->actions.push_back(RTLIL::SigSig(std::move(s1), std::move(s2)));
|
||||
expect_eol();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (try_parse_keyword("attribute")) {
|
||||
attributes_in_update_list = true;
|
||||
parse_attribute();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!try_parse_keyword("memwr"))
|
||||
break;
|
||||
|
||||
RTLIL::MemWriteAction act;
|
||||
act.attributes = std::move(attrbuf);
|
||||
act.memid = parse_id();
|
||||
act.address = parse_sigspec();
|
||||
act.data = parse_sigspec();
|
||||
act.enable = parse_sigspec();
|
||||
act.priority_mask = parse_const();
|
||||
rule->mem_write_actions.push_back(std::move(act));
|
||||
expect_eol();
|
||||
}
|
||||
// The old parser allowed dangling attributes before a "sync" to carry through
|
||||
// the "sync", so we will too, for now.
|
||||
if (attributes_in_update_list && attrbuf.size() > 0)
|
||||
error("dangling attribute");
|
||||
}
|
||||
|
||||
expect_keyword("end");
|
||||
expect_eol();
|
||||
}
|
||||
|
||||
RTLILFrontendWorker(RTLIL::Design *design) : design(design) {}
|
||||
|
||||
void parse(std::istream *f)
|
||||
{
|
||||
this->f = f;
|
||||
line_num = 0;
|
||||
advance_to_next_nonempty_line();
|
||||
while (f->good())
|
||||
{
|
||||
if (try_parse_keyword("attribute")) {
|
||||
parse_attribute();
|
||||
continue;
|
||||
}
|
||||
if (try_parse_keyword("module")) {
|
||||
parse_module();
|
||||
continue;
|
||||
}
|
||||
if (try_parse_keyword("autoidx")) {
|
||||
autoidx = std::max<int>(autoidx, parse_integer());
|
||||
expect_eol();
|
||||
continue;
|
||||
}
|
||||
error("Unexpected token: %s", error_token());
|
||||
}
|
||||
if (attrbuf.size() != 0)
|
||||
error("dangling attribute");
|
||||
}
|
||||
};
|
||||
|
||||
struct RTLILFrontend : public Frontend {
|
||||
RTLILFrontend() : Frontend("rtlil", "read modules from RTLIL file") { }
|
||||
void help() override
|
||||
|
|
@ -58,9 +807,7 @@ struct RTLILFrontend : public Frontend {
|
|||
}
|
||||
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
RTLIL_FRONTEND::flag_nooverwrite = false;
|
||||
RTLIL_FRONTEND::flag_overwrite = false;
|
||||
RTLIL_FRONTEND::flag_lib = false;
|
||||
RTLILFrontendWorker worker(design);
|
||||
|
||||
log_header(design, "Executing RTLIL frontend.\n");
|
||||
|
||||
|
|
@ -68,48 +815,27 @@ struct RTLILFrontend : public Frontend {
|
|||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-nooverwrite") {
|
||||
RTLIL_FRONTEND::flag_nooverwrite = true;
|
||||
RTLIL_FRONTEND::flag_overwrite = false;
|
||||
worker.flag_nooverwrite = true;
|
||||
worker.flag_overwrite = false;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-overwrite") {
|
||||
RTLIL_FRONTEND::flag_nooverwrite = false;
|
||||
RTLIL_FRONTEND::flag_overwrite = true;
|
||||
worker.flag_nooverwrite = false;
|
||||
worker.flag_overwrite = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-lib") {
|
||||
RTLIL_FRONTEND::flag_lib = true;
|
||||
worker.flag_lib = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
log("Input filename: %s\n", filename.c_str());
|
||||
log("Input filename: %s\n", filename);
|
||||
|
||||
RTLIL_FRONTEND::lexin = f;
|
||||
RTLIL_FRONTEND::current_design = design;
|
||||
rtlil_frontend_yydebug = false;
|
||||
rtlil_frontend_yyrestart(NULL);
|
||||
rtlil_frontend_yyparse();
|
||||
rtlil_frontend_yylex_destroy();
|
||||
worker.parse(f);
|
||||
}
|
||||
} RTLILFrontend;
|
||||
|
||||
struct IlangFrontend : public Frontend {
|
||||
IlangFrontend() : Frontend("ilang", "(deprecated) alias of read_rtlil") { }
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log("See `help read_rtlil`.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
RTLILFrontend.execute(f, filename, args, design);
|
||||
}
|
||||
} IlangFrontend;
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
|
|
|
|||
|
|
@ -1,150 +0,0 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward frontend for the RTLIL text
|
||||
* representation.
|
||||
*
|
||||
*/
|
||||
|
||||
%{
|
||||
|
||||
#ifdef __clang__
|
||||
// bison generates code using the 'register' storage class specifier
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-register"
|
||||
#endif
|
||||
|
||||
#include <cstdlib>
|
||||
#include "frontends/rtlil/rtlil_frontend.h"
|
||||
#include "rtlil_parser.tab.hh"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
||||
#define YY_INPUT(buf,result,max_size) \
|
||||
result = readsome(*RTLIL_FRONTEND::lexin, buf, max_size)
|
||||
|
||||
%}
|
||||
|
||||
%option yylineno
|
||||
%option noyywrap
|
||||
%option nounput
|
||||
%option prefix="rtlil_frontend_yy"
|
||||
|
||||
%x STRING
|
||||
|
||||
%%
|
||||
|
||||
"autoidx" { return TOK_AUTOIDX; }
|
||||
"module" { return TOK_MODULE; }
|
||||
"attribute" { return TOK_ATTRIBUTE; }
|
||||
"parameter" { return TOK_PARAMETER; }
|
||||
"signed" { return TOK_SIGNED; }
|
||||
"real" { return TOK_REAL; }
|
||||
"wire" { return TOK_WIRE; }
|
||||
"memory" { return TOK_MEMORY; }
|
||||
"width" { return TOK_WIDTH; }
|
||||
"upto" { return TOK_UPTO; }
|
||||
"offset" { return TOK_OFFSET; }
|
||||
"size" { return TOK_SIZE; }
|
||||
"input" { return TOK_INPUT; }
|
||||
"output" { return TOK_OUTPUT; }
|
||||
"inout" { return TOK_INOUT; }
|
||||
"cell" { return TOK_CELL; }
|
||||
"connect" { return TOK_CONNECT; }
|
||||
"switch" { return TOK_SWITCH; }
|
||||
"case" { return TOK_CASE; }
|
||||
"assign" { return TOK_ASSIGN; }
|
||||
"sync" { return TOK_SYNC; }
|
||||
"low" { return TOK_LOW; }
|
||||
"high" { return TOK_HIGH; }
|
||||
"posedge" { return TOK_POSEDGE; }
|
||||
"negedge" { return TOK_NEGEDGE; }
|
||||
"edge" { return TOK_EDGE; }
|
||||
"always" { return TOK_ALWAYS; }
|
||||
"global" { return TOK_GLOBAL; }
|
||||
"init" { return TOK_INIT; }
|
||||
"update" { return TOK_UPDATE; }
|
||||
"memwr" { return TOK_MEMWR; }
|
||||
"process" { return TOK_PROCESS; }
|
||||
"end" { return TOK_END; }
|
||||
|
||||
[a-z]+ { return TOK_INVALID; }
|
||||
|
||||
"\\"[^ \t\r\n]+ { rtlil_frontend_yylval.string = strdup(yytext); return TOK_ID; }
|
||||
"$"[^ \t\r\n]+ { rtlil_frontend_yylval.string = strdup(yytext); return TOK_ID; }
|
||||
|
||||
[0-9]+'[01xzm-]* { rtlil_frontend_yylval.string = strdup(yytext); return TOK_VALUE; }
|
||||
-?[0-9]+ {
|
||||
char *end = nullptr;
|
||||
errno = 0;
|
||||
long value = strtol(yytext, &end, 10);
|
||||
log_assert(end == yytext + strlen(yytext));
|
||||
if (errno == ERANGE)
|
||||
return TOK_INVALID; // literal out of range of long
|
||||
if (value < INT_MIN || value > INT_MAX)
|
||||
return TOK_INVALID; // literal out of range of int (relevant mostly for LP64 platforms)
|
||||
rtlil_frontend_yylval.integer = value;
|
||||
return TOK_INT;
|
||||
}
|
||||
|
||||
\" { BEGIN(STRING); }
|
||||
<STRING>\\. { yymore(); }
|
||||
<STRING>\" {
|
||||
BEGIN(0);
|
||||
char *yystr = strdup(yytext);
|
||||
yystr[strlen(yytext) - 1] = 0;
|
||||
int i = 0, j = 0;
|
||||
while (yystr[i]) {
|
||||
if (yystr[i] == '\\' && yystr[i + 1]) {
|
||||
i++;
|
||||
if (yystr[i] == 'n')
|
||||
yystr[i] = '\n';
|
||||
else if (yystr[i] == 't')
|
||||
yystr[i] = '\t';
|
||||
else if ('0' <= yystr[i] && yystr[i] <= '7') {
|
||||
yystr[i] = yystr[i] - '0';
|
||||
if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
|
||||
yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
|
||||
i++;
|
||||
}
|
||||
if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
|
||||
yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
yystr[j++] = yystr[i++];
|
||||
}
|
||||
yystr[j] = 0;
|
||||
rtlil_frontend_yylval.string = yystr;
|
||||
return TOK_STRING;
|
||||
}
|
||||
<STRING>. { yymore(); }
|
||||
|
||||
"#"[^\n]* /* ignore comments */
|
||||
[ \t] /* ignore non-newline whitespaces */
|
||||
[\r\n]+ { return TOK_EOL; }
|
||||
|
||||
. { return *yytext; }
|
||||
|
||||
%%
|
||||
|
||||
// this is a hack to avoid the 'yyinput defined but not used' error msgs
|
||||
void *rtlil_frontend_avoid_input_warnings() {
|
||||
return (void*)&yyinput;
|
||||
}
|
||||
|
|
@ -1,501 +0,0 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward frontend for the RTLIL text
|
||||
* representation.
|
||||
*
|
||||
*/
|
||||
|
||||
%require "3.0"
|
||||
|
||||
%{
|
||||
#include <list>
|
||||
#include "frontends/rtlil/rtlil_frontend.h"
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
namespace RTLIL_FRONTEND {
|
||||
std::istream *lexin;
|
||||
RTLIL::Design *current_design;
|
||||
RTLIL::Module *current_module;
|
||||
RTLIL::Wire *current_wire;
|
||||
RTLIL::Memory *current_memory;
|
||||
RTLIL::Cell *current_cell;
|
||||
RTLIL::Process *current_process;
|
||||
std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack;
|
||||
std::vector<RTLIL::CaseRule*> case_stack;
|
||||
dict<RTLIL::IdString, RTLIL::Const> attrbuf;
|
||||
bool flag_nooverwrite, flag_overwrite, flag_lib;
|
||||
bool delete_current_module;
|
||||
}
|
||||
using namespace RTLIL_FRONTEND;
|
||||
YOSYS_NAMESPACE_END
|
||||
USING_YOSYS_NAMESPACE
|
||||
%}
|
||||
|
||||
%define api.prefix {rtlil_frontend_yy}
|
||||
|
||||
/* The union is defined in the header, so we need to provide all the
|
||||
* includes it requires
|
||||
*/
|
||||
%code requires {
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "frontends/rtlil/rtlil_frontend.h"
|
||||
}
|
||||
|
||||
%union {
|
||||
char *string;
|
||||
int integer;
|
||||
YOSYS_NAMESPACE_PREFIX RTLIL::Const *data;
|
||||
YOSYS_NAMESPACE_PREFIX RTLIL::SigSpec *sigspec;
|
||||
std::vector<YOSYS_NAMESPACE_PREFIX RTLIL::SigSpec> *rsigspec;
|
||||
}
|
||||
|
||||
%token <string> TOK_ID TOK_VALUE TOK_STRING
|
||||
%token <integer> TOK_INT
|
||||
%token TOK_AUTOIDX TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT
|
||||
%token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC
|
||||
%token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS TOK_GLOBAL TOK_INIT
|
||||
%token TOK_UPDATE TOK_MEMWR TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET
|
||||
%token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_REAL TOK_UPTO
|
||||
|
||||
%type <rsigspec> sigspec_list_reversed
|
||||
%type <sigspec> sigspec sigspec_list
|
||||
%type <integer> sync_type
|
||||
%type <data> constant
|
||||
|
||||
%expect 0
|
||||
%debug
|
||||
|
||||
%%
|
||||
|
||||
input:
|
||||
optional_eol {
|
||||
attrbuf.clear();
|
||||
} design {
|
||||
if (attrbuf.size() != 0)
|
||||
rtlil_frontend_yyerror("dangling attribute");
|
||||
};
|
||||
|
||||
EOL:
|
||||
optional_eol TOK_EOL;
|
||||
|
||||
optional_eol:
|
||||
optional_eol TOK_EOL | /* empty */;
|
||||
|
||||
design:
|
||||
design module |
|
||||
design attr_stmt |
|
||||
design autoidx_stmt |
|
||||
/* empty */;
|
||||
|
||||
module:
|
||||
TOK_MODULE TOK_ID EOL {
|
||||
delete_current_module = false;
|
||||
if (current_design->has($2)) {
|
||||
RTLIL::Module *existing_mod = current_design->module($2);
|
||||
if (!flag_overwrite && (flag_lib || (attrbuf.count(ID::blackbox) && attrbuf.at(ID::blackbox).as_bool()))) {
|
||||
log("Ignoring blackbox re-definition of module %s.\n", $2);
|
||||
delete_current_module = true;
|
||||
} else if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute(ID::blackbox)) {
|
||||
rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of module %s.", $2).c_str());
|
||||
} else if (flag_nooverwrite) {
|
||||
log("Ignoring re-definition of module %s.\n", $2);
|
||||
delete_current_module = true;
|
||||
} else {
|
||||
log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", $2);
|
||||
current_design->remove(existing_mod);
|
||||
}
|
||||
}
|
||||
current_module = new RTLIL::Module;
|
||||
current_module->name = $2;
|
||||
current_module->attributes = attrbuf;
|
||||
if (!delete_current_module)
|
||||
current_design->add(current_module);
|
||||
attrbuf.clear();
|
||||
free($2);
|
||||
} module_body TOK_END {
|
||||
if (attrbuf.size() != 0)
|
||||
rtlil_frontend_yyerror("dangling attribute");
|
||||
current_module->fixup_ports();
|
||||
if (delete_current_module)
|
||||
delete current_module;
|
||||
else if (flag_lib)
|
||||
current_module->makeblackbox();
|
||||
current_module = nullptr;
|
||||
} EOL;
|
||||
|
||||
module_body:
|
||||
module_body module_stmt |
|
||||
/* empty */;
|
||||
|
||||
module_stmt:
|
||||
param_stmt | param_defval_stmt | attr_stmt | wire_stmt | memory_stmt | cell_stmt | proc_stmt | conn_stmt;
|
||||
|
||||
param_stmt:
|
||||
TOK_PARAMETER TOK_ID EOL {
|
||||
current_module->avail_parameters($2);
|
||||
free($2);
|
||||
};
|
||||
|
||||
param_defval_stmt:
|
||||
TOK_PARAMETER TOK_ID constant EOL {
|
||||
current_module->avail_parameters($2);
|
||||
current_module->parameter_default_values[$2] = *$3;
|
||||
delete $3;
|
||||
free($2);
|
||||
};
|
||||
|
||||
attr_stmt:
|
||||
TOK_ATTRIBUTE TOK_ID constant EOL {
|
||||
attrbuf[$2] = *$3;
|
||||
delete $3;
|
||||
free($2);
|
||||
};
|
||||
|
||||
autoidx_stmt:
|
||||
TOK_AUTOIDX TOK_INT EOL {
|
||||
autoidx = max(autoidx, $2);
|
||||
};
|
||||
|
||||
wire_stmt:
|
||||
TOK_WIRE {
|
||||
current_wire = current_module->addWire("$__rtlil_frontend_tmp__");
|
||||
current_wire->attributes = attrbuf;
|
||||
attrbuf.clear();
|
||||
} wire_options TOK_ID EOL {
|
||||
if (current_module->wire($4) != nullptr)
|
||||
rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of wire %s.", $4).c_str());
|
||||
current_module->rename(current_wire, $4);
|
||||
free($4);
|
||||
};
|
||||
|
||||
wire_options:
|
||||
wire_options TOK_WIDTH TOK_INT {
|
||||
current_wire->width = $3;
|
||||
} |
|
||||
wire_options TOK_WIDTH TOK_INVALID {
|
||||
rtlil_frontend_yyerror("RTLIL error: invalid wire width");
|
||||
} |
|
||||
wire_options TOK_UPTO {
|
||||
current_wire->upto = true;
|
||||
} |
|
||||
wire_options TOK_SIGNED {
|
||||
current_wire->is_signed = true;
|
||||
} |
|
||||
wire_options TOK_OFFSET TOK_INT {
|
||||
current_wire->start_offset = $3;
|
||||
} |
|
||||
wire_options TOK_INPUT TOK_INT {
|
||||
current_wire->port_id = $3;
|
||||
current_wire->port_input = true;
|
||||
current_wire->port_output = false;
|
||||
} |
|
||||
wire_options TOK_OUTPUT TOK_INT {
|
||||
current_wire->port_id = $3;
|
||||
current_wire->port_input = false;
|
||||
current_wire->port_output = true;
|
||||
} |
|
||||
wire_options TOK_INOUT TOK_INT {
|
||||
current_wire->port_id = $3;
|
||||
current_wire->port_input = true;
|
||||
current_wire->port_output = true;
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
memory_stmt:
|
||||
TOK_MEMORY {
|
||||
current_memory = new RTLIL::Memory;
|
||||
current_memory->attributes = attrbuf;
|
||||
attrbuf.clear();
|
||||
} memory_options TOK_ID EOL {
|
||||
if (current_module->memories.count($4) != 0)
|
||||
rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of memory %s.", $4).c_str());
|
||||
current_memory->name = $4;
|
||||
current_module->memories[$4] = current_memory;
|
||||
free($4);
|
||||
};
|
||||
|
||||
memory_options:
|
||||
memory_options TOK_WIDTH TOK_INT {
|
||||
current_memory->width = $3;
|
||||
} |
|
||||
memory_options TOK_SIZE TOK_INT {
|
||||
current_memory->size = $3;
|
||||
} |
|
||||
memory_options TOK_OFFSET TOK_INT {
|
||||
current_memory->start_offset = $3;
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
cell_stmt:
|
||||
TOK_CELL TOK_ID TOK_ID EOL {
|
||||
if (current_module->cell($3) != nullptr)
|
||||
rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of cell %s.", $3).c_str());
|
||||
current_cell = current_module->addCell($3, $2);
|
||||
current_cell->attributes = attrbuf;
|
||||
attrbuf.clear();
|
||||
free($2);
|
||||
free($3);
|
||||
} cell_body TOK_END EOL;
|
||||
|
||||
cell_body:
|
||||
cell_body TOK_PARAMETER TOK_ID constant EOL {
|
||||
current_cell->parameters[$3] = *$4;
|
||||
free($3);
|
||||
delete $4;
|
||||
} |
|
||||
cell_body TOK_PARAMETER TOK_SIGNED TOK_ID constant EOL {
|
||||
current_cell->parameters[$4] = *$5;
|
||||
current_cell->parameters[$4].flags |= RTLIL::CONST_FLAG_SIGNED;
|
||||
free($4);
|
||||
delete $5;
|
||||
} |
|
||||
cell_body TOK_PARAMETER TOK_REAL TOK_ID constant EOL {
|
||||
current_cell->parameters[$4] = *$5;
|
||||
current_cell->parameters[$4].flags |= RTLIL::CONST_FLAG_REAL;
|
||||
free($4);
|
||||
delete $5;
|
||||
} |
|
||||
cell_body TOK_CONNECT TOK_ID sigspec EOL {
|
||||
if (current_cell->hasPort($3))
|
||||
rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of cell port %s.", $3).c_str());
|
||||
current_cell->setPort($3, *$4);
|
||||
delete $4;
|
||||
free($3);
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
proc_stmt:
|
||||
TOK_PROCESS TOK_ID EOL {
|
||||
if (current_module->processes.count($2) != 0)
|
||||
rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of process %s.", $2).c_str());
|
||||
current_process = current_module->addProcess($2);
|
||||
current_process->attributes = attrbuf;
|
||||
switch_stack.clear();
|
||||
switch_stack.push_back(¤t_process->root_case.switches);
|
||||
case_stack.clear();
|
||||
case_stack.push_back(¤t_process->root_case);
|
||||
attrbuf.clear();
|
||||
free($2);
|
||||
} case_body sync_list TOK_END EOL;
|
||||
|
||||
switch_stmt:
|
||||
TOK_SWITCH sigspec EOL {
|
||||
RTLIL::SwitchRule *rule = new RTLIL::SwitchRule;
|
||||
rule->signal = *$2;
|
||||
rule->attributes = attrbuf;
|
||||
switch_stack.back()->push_back(rule);
|
||||
attrbuf.clear();
|
||||
delete $2;
|
||||
} attr_list switch_body TOK_END EOL;
|
||||
|
||||
attr_list:
|
||||
/* empty */ |
|
||||
attr_list attr_stmt;
|
||||
|
||||
switch_body:
|
||||
switch_body TOK_CASE {
|
||||
RTLIL::CaseRule *rule = new RTLIL::CaseRule;
|
||||
rule->attributes = attrbuf;
|
||||
switch_stack.back()->back()->cases.push_back(rule);
|
||||
switch_stack.push_back(&rule->switches);
|
||||
case_stack.push_back(rule);
|
||||
attrbuf.clear();
|
||||
} compare_list EOL case_body {
|
||||
switch_stack.pop_back();
|
||||
case_stack.pop_back();
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
compare_list:
|
||||
sigspec {
|
||||
case_stack.back()->compare.push_back(*$1);
|
||||
delete $1;
|
||||
} |
|
||||
compare_list ',' sigspec {
|
||||
case_stack.back()->compare.push_back(*$3);
|
||||
delete $3;
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
case_body:
|
||||
case_body attr_stmt |
|
||||
case_body switch_stmt |
|
||||
case_body assign_stmt |
|
||||
/* empty */;
|
||||
|
||||
assign_stmt:
|
||||
TOK_ASSIGN sigspec sigspec EOL {
|
||||
if (attrbuf.size() != 0)
|
||||
rtlil_frontend_yyerror("dangling attribute");
|
||||
case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3));
|
||||
delete $2;
|
||||
delete $3;
|
||||
};
|
||||
|
||||
sync_list:
|
||||
sync_list TOK_SYNC sync_type sigspec EOL {
|
||||
RTLIL::SyncRule *rule = new RTLIL::SyncRule;
|
||||
rule->type = RTLIL::SyncType($3);
|
||||
rule->signal = *$4;
|
||||
current_process->syncs.push_back(rule);
|
||||
delete $4;
|
||||
} update_list |
|
||||
sync_list TOK_SYNC TOK_ALWAYS EOL {
|
||||
RTLIL::SyncRule *rule = new RTLIL::SyncRule;
|
||||
rule->type = RTLIL::SyncType::STa;
|
||||
rule->signal = RTLIL::SigSpec();
|
||||
current_process->syncs.push_back(rule);
|
||||
} update_list |
|
||||
sync_list TOK_SYNC TOK_GLOBAL EOL {
|
||||
RTLIL::SyncRule *rule = new RTLIL::SyncRule;
|
||||
rule->type = RTLIL::SyncType::STg;
|
||||
rule->signal = RTLIL::SigSpec();
|
||||
current_process->syncs.push_back(rule);
|
||||
} update_list |
|
||||
sync_list TOK_SYNC TOK_INIT EOL {
|
||||
RTLIL::SyncRule *rule = new RTLIL::SyncRule;
|
||||
rule->type = RTLIL::SyncType::STi;
|
||||
rule->signal = RTLIL::SigSpec();
|
||||
current_process->syncs.push_back(rule);
|
||||
} update_list |
|
||||
/* empty */;
|
||||
|
||||
sync_type:
|
||||
TOK_LOW { $$ = RTLIL::ST0; } |
|
||||
TOK_HIGH { $$ = RTLIL::ST1; } |
|
||||
TOK_POSEDGE { $$ = RTLIL::STp; } |
|
||||
TOK_NEGEDGE { $$ = RTLIL::STn; } |
|
||||
TOK_EDGE { $$ = RTLIL::STe; };
|
||||
|
||||
update_list:
|
||||
update_list TOK_UPDATE sigspec sigspec EOL {
|
||||
current_process->syncs.back()->actions.push_back(RTLIL::SigSig(*$3, *$4));
|
||||
delete $3;
|
||||
delete $4;
|
||||
} |
|
||||
update_list attr_list TOK_MEMWR TOK_ID sigspec sigspec sigspec constant EOL {
|
||||
RTLIL::MemWriteAction act;
|
||||
act.attributes = attrbuf;
|
||||
act.memid = $4;
|
||||
act.address = *$5;
|
||||
act.data = *$6;
|
||||
act.enable = *$7;
|
||||
act.priority_mask = *$8;
|
||||
current_process->syncs.back()->mem_write_actions.push_back(std::move(act));
|
||||
attrbuf.clear();
|
||||
free($4);
|
||||
delete $5;
|
||||
delete $6;
|
||||
delete $7;
|
||||
delete $8;
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
constant:
|
||||
TOK_VALUE {
|
||||
char *ep;
|
||||
int width = strtol($1, &ep, 10);
|
||||
std::list<RTLIL::State> bits;
|
||||
while (*(++ep) != 0) {
|
||||
RTLIL::State bit = RTLIL::Sx;
|
||||
switch (*ep) {
|
||||
case '0': bit = RTLIL::S0; break;
|
||||
case '1': bit = RTLIL::S1; break;
|
||||
case 'x': bit = RTLIL::Sx; break;
|
||||
case 'z': bit = RTLIL::Sz; break;
|
||||
case '-': bit = RTLIL::Sa; break;
|
||||
case 'm': bit = RTLIL::Sm; break;
|
||||
}
|
||||
bits.push_front(bit);
|
||||
}
|
||||
if (bits.size() == 0)
|
||||
bits.push_back(RTLIL::Sx);
|
||||
while ((int)bits.size() < width) {
|
||||
RTLIL::State bit = bits.back();
|
||||
if (bit == RTLIL::S1)
|
||||
bit = RTLIL::S0;
|
||||
bits.push_back(bit);
|
||||
}
|
||||
while ((int)bits.size() > width)
|
||||
bits.pop_back();
|
||||
$$ = new RTLIL::Const;
|
||||
for (auto it = bits.begin(); it != bits.end(); it++)
|
||||
$$->bits.push_back(*it);
|
||||
free($1);
|
||||
} |
|
||||
TOK_INT {
|
||||
$$ = new RTLIL::Const($1, 32);
|
||||
} |
|
||||
TOK_STRING {
|
||||
$$ = new RTLIL::Const($1);
|
||||
free($1);
|
||||
};
|
||||
|
||||
sigspec:
|
||||
constant {
|
||||
$$ = new RTLIL::SigSpec(*$1);
|
||||
delete $1;
|
||||
} |
|
||||
TOK_ID {
|
||||
if (current_module->wire($1) == nullptr)
|
||||
rtlil_frontend_yyerror(stringf("RTLIL error: wire %s not found", $1).c_str());
|
||||
$$ = new RTLIL::SigSpec(current_module->wire($1));
|
||||
free($1);
|
||||
} |
|
||||
sigspec '[' TOK_INT ']' {
|
||||
if ($3 >= $1->size() || $3 < 0)
|
||||
rtlil_frontend_yyerror("bit index out of range");
|
||||
$$ = new RTLIL::SigSpec($1->extract($3));
|
||||
delete $1;
|
||||
} |
|
||||
sigspec '[' TOK_INT ':' TOK_INT ']' {
|
||||
if ($3 >= $1->size() || $3 < 0 || $3 < $5)
|
||||
rtlil_frontend_yyerror("invalid slice");
|
||||
$$ = new RTLIL::SigSpec($1->extract($5, $3 - $5 + 1));
|
||||
delete $1;
|
||||
} |
|
||||
'{' sigspec_list '}' {
|
||||
$$ = $2;
|
||||
};
|
||||
|
||||
sigspec_list_reversed:
|
||||
sigspec_list_reversed sigspec {
|
||||
$$->push_back(*$2);
|
||||
delete $2;
|
||||
} |
|
||||
/* empty */ {
|
||||
$$ = new std::vector<RTLIL::SigSpec>;
|
||||
};
|
||||
|
||||
sigspec_list: sigspec_list_reversed {
|
||||
$$ = new RTLIL::SigSpec;
|
||||
for (auto it = $1->rbegin(); it != $1->rend(); it++)
|
||||
$$->append(*it);
|
||||
delete $1;
|
||||
};
|
||||
|
||||
conn_stmt:
|
||||
TOK_CONNECT sigspec sigspec EOL {
|
||||
if (attrbuf.size() != 0)
|
||||
rtlil_frontend_yyerror("dangling attribute");
|
||||
current_module->connect(*$2, *$3);
|
||||
delete $2;
|
||||
delete $3;
|
||||
};
|
||||
|
|
@ -10,10 +10,11 @@ EXTRA_TARGETS += share/verific
|
|||
share/verific:
|
||||
$(P) rm -rf share/verific.new
|
||||
$(Q) mkdir -p share/verific.new
|
||||
ifneq ($(DISABLE_VERIFIC_VHDL),1)
|
||||
ifeq ($(ENABLE_VERIFIC_VHDL),1)
|
||||
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1987/. share/verific.new/vhdl_vdbs_1987
|
||||
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1993/. share/verific.new/vhdl_vdbs_1993
|
||||
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2008/. share/verific.new/vhdl_vdbs_2008
|
||||
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2019/. share/verific.new/vhdl_vdbs_2019
|
||||
endif
|
||||
$(Q) chmod -R a+rX share/verific.new
|
||||
$(Q) mv share/verific.new share/verific
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
This directory contains Verific bindings for Yosys.
|
||||
|
||||
Use Tabby CAD Suite from YosysHQ if you need Yosys+Verifc.
|
||||
Use Tabby CAD Suite from YosysHQ if you need Yosys+Verific.
|
||||
https://www.yosyshq.com/
|
||||
|
||||
Contact YosysHQ at contact@yosyshq.com for free evaluation
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -26,7 +26,7 @@ YOSYS_NAMESPACE_BEGIN
|
|||
extern int verific_verbose;
|
||||
|
||||
extern bool verific_import_pending;
|
||||
extern void verific_import(Design *design, const std::map<std::string,std::string> ¶meters, std::string top = std::string());
|
||||
extern std::string verific_import(Design *design, const std::map<std::string,std::string> ¶meters, std::string top = std::string());
|
||||
|
||||
extern pool<int> verific_sva_prims;
|
||||
|
||||
|
|
@ -71,23 +71,27 @@ struct VerificImporter
|
|||
|
||||
std::map<Verific::Net*, RTLIL::SigBit> net_map;
|
||||
std::map<Verific::Net*, Verific::Net*> sva_posedge_map;
|
||||
pool<Verific::Net*, hash_ptr_ops> any_all_nets;
|
||||
pool<Verific::Net*> any_all_nets;
|
||||
|
||||
bool mode_gates, mode_keep, mode_nosva, mode_names, mode_verific;
|
||||
bool mode_gates, mode_keep, mode_nosva, mode_sva_continue, mode_names, mode_verific;
|
||||
bool mode_autocover, mode_fullinit;
|
||||
|
||||
VerificImporter(bool mode_gates, bool mode_keep, bool mode_nosva, bool mode_names, bool mode_verific, bool mode_autocover, bool mode_fullinit);
|
||||
int num_sva_continue = 0;
|
||||
|
||||
VerificImporter(bool mode_gates, bool mode_keep, bool mode_nosva, bool mode_sva_continue, bool mode_names, bool mode_verific, bool mode_autocover, bool mode_fullinit);
|
||||
|
||||
RTLIL::SigBit net_map_at(Verific::Net *net);
|
||||
|
||||
RTLIL::IdString new_verific_id(Verific::DesignObj *obj);
|
||||
void import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, Verific::DesignObj *obj, Verific::Netlist *nl = nullptr);
|
||||
void import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, Verific::DesignObj *obj, Verific::Netlist *nl = nullptr, int wire_width_hint = -1);
|
||||
|
||||
RTLIL::SigBit netToSigBit(Verific::Net *net);
|
||||
RTLIL::SigSpec operatorInput(Verific::Instance *inst);
|
||||
RTLIL::SigSpec operatorInput1(Verific::Instance *inst);
|
||||
RTLIL::SigSpec operatorInput2(Verific::Instance *inst);
|
||||
RTLIL::SigSpec operatorInport(Verific::Instance *inst, const char *portname);
|
||||
RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool<Verific::Net*, hash_ptr_ops> *any_all_nets = nullptr);
|
||||
RTLIL::SigSpec operatorInportCase(Verific::Instance *inst, const char *portname);
|
||||
RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool<Verific::Net*> *any_all_nets = nullptr);
|
||||
|
||||
bool import_netlist_instance_gates(Verific::Instance *inst, RTLIL::IdString inst_name);
|
||||
bool import_netlist_instance_cells(Verific::Instance *inst, RTLIL::IdString inst_name);
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ USING_YOSYS_NAMESPACE
|
|||
using namespace Verific;
|
||||
#endif
|
||||
|
||||
#ifdef VERIFIC_SYSTEMVERILOG_SUPPORT
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
// Non-deterministic FSM
|
||||
|
|
@ -123,6 +124,7 @@ struct SvaFsm
|
|||
{
|
||||
Module *module;
|
||||
VerificClocking clocking;
|
||||
std::function<void (std::string)> parser_error;
|
||||
|
||||
SigBit trigger_sig = State::S1, disable_sig;
|
||||
SigBit throughout_sig = State::S1;
|
||||
|
|
@ -147,6 +149,7 @@ struct SvaFsm
|
|||
module = clking.module;
|
||||
clocking = clking;
|
||||
trigger_sig = trig;
|
||||
parser_error = [](std::string msg){ log_error("%s", msg); };
|
||||
|
||||
startNode = createNode();
|
||||
acceptNode = createNode();
|
||||
|
|
@ -474,18 +477,18 @@ struct SvaFsm
|
|||
dump();
|
||||
log(" ctrl signal: %s\n", log_signal(dnode.ctrl));
|
||||
}
|
||||
log_error("SVA DFSM state ctrl signal has %d (>%d) bits. Stopping to prevent exponential design size explosion.\n",
|
||||
GetSize(dnode.ctrl), verific_sva_fsm_limit);
|
||||
parser_error(stringf("SVA DFSM state ctrl signal has %d (>%d) bits. Stopping to prevent exponential design size explosion.\n",
|
||||
GetSize(dnode.ctrl), verific_sva_fsm_limit));
|
||||
}
|
||||
|
||||
for (int i = 0; i < (1 << GetSize(dnode.ctrl)); i++)
|
||||
for (unsigned long long i = 0; i < (1ull << GetSize(dnode.ctrl)); i++)
|
||||
{
|
||||
Const ctrl_val(i, GetSize(dnode.ctrl));
|
||||
pool<SigBit> ctrl_bits;
|
||||
|
||||
for (int i = 0; i < GetSize(dnode.ctrl); i++)
|
||||
if (ctrl_val[i] == State::S1)
|
||||
ctrl_bits.insert(dnode.ctrl[i]);
|
||||
for (int j = 0; j < GetSize(dnode.ctrl); j++)
|
||||
if (ctrl_val[j] == State::S1)
|
||||
ctrl_bits.insert(dnode.ctrl[j]);
|
||||
|
||||
vector<int> new_state;
|
||||
bool accept = false, cond = false;
|
||||
|
|
@ -574,7 +577,7 @@ struct SvaFsm
|
|||
|
||||
if (delta_pos >= 0 && i_within_j && j_within_i) {
|
||||
did_something = true;
|
||||
values[i][delta_pos] = State::Sa;
|
||||
values[i].set(delta_pos, State::Sa);
|
||||
values[j] = values.back();
|
||||
values.pop_back();
|
||||
goto next_pair;
|
||||
|
|
@ -1022,20 +1025,20 @@ struct VerificSvaImporter
|
|||
|
||||
[[noreturn]] void parser_error(std::string errmsg)
|
||||
{
|
||||
if (!importer->mode_keep)
|
||||
log_error("%s", errmsg.c_str());
|
||||
log_warning("%s", errmsg.c_str());
|
||||
if (!importer->mode_keep && !importer->mode_sva_continue)
|
||||
log_error("%s", errmsg);
|
||||
log_warning("%s", errmsg);
|
||||
throw ParserErrorException();
|
||||
}
|
||||
|
||||
[[noreturn]] void parser_error(std::string errmsg, linefile_type loc)
|
||||
{
|
||||
parser_error(stringf("%s at %s:%d.\n", errmsg.c_str(), LineFile::GetFileName(loc), LineFile::GetLineNo(loc)));
|
||||
parser_error(stringf("%s at %s:%d.\n", errmsg, LineFile::GetFileName(loc), LineFile::GetLineNo(loc)));
|
||||
}
|
||||
|
||||
[[noreturn]] void parser_error(std::string errmsg, Instance *inst)
|
||||
{
|
||||
parser_error(stringf("%s at %s (%s)", errmsg.c_str(), inst->View()->Owner()->Name(), inst->Name()), inst->Linefile());
|
||||
parser_error(stringf("%s at %s (%s)", errmsg, inst->View()->Owner()->Name(), inst->Name()), inst->Linefile());
|
||||
}
|
||||
|
||||
[[noreturn]] void parser_error(Instance *inst)
|
||||
|
|
@ -1050,7 +1053,7 @@ struct VerificSvaImporter
|
|||
msg.c_str(), inst->View()->Owner()->Name(), inst->Name()), inst->Linefile());
|
||||
}
|
||||
|
||||
dict<Net*, bool, hash_ptr_ops> check_expression_cache;
|
||||
dict<Net*, bool> check_expression_cache;
|
||||
|
||||
bool check_expression(Net *net, bool raise_error = false)
|
||||
{
|
||||
|
|
@ -1259,6 +1262,7 @@ struct VerificSvaImporter
|
|||
if (inst->Type() == PRIM_SVA_FIRST_MATCH)
|
||||
{
|
||||
SvaFsm match_fsm(clocking);
|
||||
match_fsm.parser_error = [&](std::string msg) { this->parser_error(msg); };
|
||||
match_fsm.createLink(parse_sequence(match_fsm, match_fsm.createStartNode(), inst->GetInput()), match_fsm.acceptNode);
|
||||
|
||||
int node = fsm.createNode();
|
||||
|
|
@ -1425,12 +1429,15 @@ struct VerificSvaImporter
|
|||
if (inst->Type() == PRIM_SVA_SEQ_AND || inst->Type() == PRIM_SVA_AND)
|
||||
{
|
||||
SvaFsm fsm1(clocking);
|
||||
fsm1.parser_error = [&](std::string msg) { this->parser_error(msg); };
|
||||
fsm1.createLink(parse_sequence(fsm1, fsm1.createStartNode(), inst->GetInput1()), fsm1.acceptNode);
|
||||
|
||||
SvaFsm fsm2(clocking);
|
||||
fsm2.parser_error = [&](std::string msg) { this->parser_error(msg); };
|
||||
fsm2.createLink(parse_sequence(fsm2, fsm2.createStartNode(), inst->GetInput2()), fsm2.acceptNode);
|
||||
|
||||
SvaFsm combined_fsm(clocking);
|
||||
combined_fsm.parser_error = [&](std::string msg) { this->parser_error(msg); };
|
||||
fsm1.getDFsm(combined_fsm, combined_fsm.createStartNode(), -1, combined_fsm.acceptNode);
|
||||
fsm2.getDFsm(combined_fsm, combined_fsm.createStartNode(), -1, combined_fsm.acceptNode);
|
||||
|
||||
|
|
@ -1455,6 +1462,7 @@ struct VerificSvaImporter
|
|||
if (inst->Type() == PRIM_SVA_INTERSECT || inst->Type() == PRIM_SVA_WITHIN)
|
||||
{
|
||||
SvaFsm intersect_fsm(clocking);
|
||||
intersect_fsm.parser_error = [&](std::string msg) { this->parser_error(msg); };
|
||||
|
||||
if (inst->Type() == PRIM_SVA_INTERSECT)
|
||||
{
|
||||
|
|
@ -1561,6 +1569,7 @@ struct VerificSvaImporter
|
|||
int node;
|
||||
|
||||
SvaFsm antecedent_fsm(clocking, trig);
|
||||
antecedent_fsm.parser_error = [&](std::string msg) { this->parser_error(msg); };
|
||||
node = parse_sequence(antecedent_fsm, antecedent_fsm.createStartNode(), antecedent_net);
|
||||
if (inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION) {
|
||||
int next_node = antecedent_fsm.createNode();
|
||||
|
|
@ -1598,24 +1607,33 @@ struct VerificSvaImporter
|
|||
|
||||
if (inst == nullptr)
|
||||
{
|
||||
log_assert(trig == State::S1);
|
||||
|
||||
if (accept_p != nullptr)
|
||||
*accept_p = importer->net_map_at(net);
|
||||
if (reject_p != nullptr)
|
||||
*reject_p = module->Not(NEW_ID, importer->net_map_at(net));
|
||||
if (trig != State::S1) {
|
||||
if (accept_p != nullptr)
|
||||
*accept_p = module->And(NEW_ID, trig, importer->net_map_at(net));
|
||||
if (reject_p != nullptr)
|
||||
*reject_p = module->And(NEW_ID, trig, module->Not(NEW_ID, importer->net_map_at(net)));
|
||||
} else {
|
||||
if (accept_p != nullptr)
|
||||
*accept_p = importer->net_map_at(net);
|
||||
if (reject_p != nullptr)
|
||||
*reject_p = module->Not(NEW_ID, importer->net_map_at(net));
|
||||
}
|
||||
}
|
||||
else
|
||||
if (inst->Type() == PRIM_SVA_OVERLAPPED_IMPLICATION ||
|
||||
inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION)
|
||||
inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION ||
|
||||
(mode_cover && (
|
||||
inst->Type() == PRIM_SVA_OVERLAPPED_FOLLOWED_BY ||
|
||||
inst->Type() == PRIM_SVA_NON_OVERLAPPED_FOLLOWED_BY)))
|
||||
{
|
||||
Net *antecedent_net = inst->GetInput1();
|
||||
Net *consequent_net = inst->GetInput2();
|
||||
int node;
|
||||
|
||||
SvaFsm antecedent_fsm(clocking, trig);
|
||||
antecedent_fsm.parser_error = [&](std::string msg) { this->parser_error(msg); };
|
||||
node = parse_sequence(antecedent_fsm, antecedent_fsm.createStartNode(), antecedent_net);
|
||||
if (inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION) {
|
||||
if (inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION || inst->Type() == PRIM_SVA_NON_OVERLAPPED_FOLLOWED_BY) {
|
||||
int next_node = antecedent_fsm.createNode();
|
||||
antecedent_fsm.createEdge(node, next_node);
|
||||
node = next_node;
|
||||
|
|
@ -1668,6 +1686,7 @@ struct VerificSvaImporter
|
|||
}
|
||||
|
||||
SvaFsm consequent_fsm(clocking, antecedent_match);
|
||||
consequent_fsm.parser_error = [&](std::string msg) { this->parser_error(msg); };
|
||||
node = parse_sequence(consequent_fsm, consequent_fsm.createStartNode(), consequent_net);
|
||||
consequent_fsm.createLink(node, consequent_fsm.acceptNode);
|
||||
|
||||
|
|
@ -1687,6 +1706,7 @@ struct VerificSvaImporter
|
|||
}
|
||||
|
||||
SvaFsm fsm(clocking, trig);
|
||||
fsm.parser_error = [&](std::string msg) { this->parser_error(msg); };
|
||||
int node = parse_sequence(fsm, fsm.createStartNode(), net);
|
||||
fsm.createLink(node, fsm.acceptNode);
|
||||
|
||||
|
|
@ -1701,30 +1721,34 @@ struct VerificSvaImporter
|
|||
|
||||
void import()
|
||||
{
|
||||
try
|
||||
{
|
||||
module = importer->module;
|
||||
netlist = root->Owner();
|
||||
module = importer->module;
|
||||
netlist = root->Owner();
|
||||
|
||||
if (verific_verbose)
|
||||
log(" importing SVA property at root cell %s (%s) at %s:%d.\n", root->Name(), root->View()->Owner()->Name(),
|
||||
LineFile::GetFileName(root->Linefile()), LineFile::GetLineNo(root->Linefile()));
|
||||
int initial_cell_count = GetSize(module->cells_);
|
||||
int initial_wire_count = GetSize(module->wires_);
|
||||
int initial_connection_count = GetSize(module->connections_);
|
||||
|
||||
bool is_user_declared = root->IsUserDeclared();
|
||||
if (verific_verbose)
|
||||
log(" importing SVA property at root cell %s (%s) at %s:%d.\n", root->Name(), root->View()->Owner()->Name(),
|
||||
LineFile::GetFileName(root->Linefile()), LineFile::GetLineNo(root->Linefile()));
|
||||
|
||||
// FIXME
|
||||
if (!is_user_declared) {
|
||||
const char *name = root->Name();
|
||||
for (int i = 0; name[i]; i++) {
|
||||
if (i ? (name[i] < '0' || name[i] > '9') : (name[i] != 'i')) {
|
||||
is_user_declared = true;
|
||||
break;
|
||||
}
|
||||
bool is_user_declared = root->IsUserDeclared();
|
||||
|
||||
// FIXME
|
||||
if (!is_user_declared) {
|
||||
const char *name = root->Name();
|
||||
for (int i = 0; name[i]; i++) {
|
||||
if (i ? (name[i] < '0' || name[i] > '9') : (name[i] != 'i')) {
|
||||
is_user_declared = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RTLIL::IdString root_name = module->uniquify(importer->mode_names || is_user_declared ? RTLIL::escape_id(root->Name()) : NEW_ID);
|
||||
RTLIL::IdString root_name = module->uniquify(importer->mode_names || is_user_declared ? RTLIL::escape_id(root->Name()) : NEW_ID);
|
||||
|
||||
try
|
||||
{
|
||||
// parse SVA sequence into trigger signal
|
||||
|
||||
clocking = VerificClocking(importer, root->GetInput(), true);
|
||||
|
|
@ -1777,7 +1801,7 @@ struct VerificSvaImporter
|
|||
if (mode_assert) c = module->addLive(root_name, sig_a_q, sig_en_q);
|
||||
if (mode_assume) c = module->addFair(root_name, sig_a_q, sig_en_q);
|
||||
|
||||
importer->import_attributes(c->attributes, root);
|
||||
if (c) importer->import_attributes(c->attributes, root);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -1822,11 +1846,44 @@ struct VerificSvaImporter
|
|||
if (mode_assume) c = module->addAssume(root_name, sig_a_q, sig_en_q);
|
||||
if (mode_cover) c = module->addCover(root_name, sig_a_q, sig_en_q);
|
||||
|
||||
importer->import_attributes(c->attributes, root);
|
||||
if (c) {
|
||||
c->set_bool_attribute(ID(keep));
|
||||
importer->import_attributes(c->attributes, root);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ParserErrorException)
|
||||
{
|
||||
if (importer->mode_sva_continue) {
|
||||
|
||||
std::vector<Cell *> remove_cells;
|
||||
pool<Wire *> remove_wires;
|
||||
|
||||
for (int i = 0, end = GetSize(module->cells_) - initial_cell_count; i != end; ++i)
|
||||
remove_cells.push_back(module->cells_.element(i)->second);
|
||||
|
||||
for (int i = 0, end = GetSize(module->wires_) - initial_wire_count; i != end; ++i)
|
||||
remove_wires.emplace(module->wires_.element(i)->second);
|
||||
|
||||
for (auto cell : remove_cells)
|
||||
module->remove(cell);
|
||||
module->remove(remove_wires);
|
||||
|
||||
module->connections_.resize(initial_connection_count);
|
||||
|
||||
RTLIL::Cell *c = nullptr;
|
||||
|
||||
if (mode_assert) c = module->addAssert(root_name, State::Sx, State::Sx);
|
||||
if (mode_assume) c = module->addAssume(root_name, State::Sx, State::Sx);
|
||||
if (mode_cover) c = module->addCover(root_name, State::Sx, State::Sx);
|
||||
|
||||
if (c) {
|
||||
importer->import_attributes(c->attributes, root);
|
||||
c->set_bool_attribute(ID(unsupported_sva));
|
||||
}
|
||||
|
||||
importer->num_sva_continue++;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1873,5 +1930,8 @@ bool verific_is_sva_net(VerificImporter *importer, Verific::Net *net)
|
|||
worker.importer = importer;
|
||||
return worker.net_to_ast_driver(net) != nullptr;
|
||||
}
|
||||
|
||||
#else
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
pool<int> verific_sva_prims = {};
|
||||
#endif
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ frontends/verilog/verilog_parser.tab.cc: frontends/verilog/verilog_parser.y
|
|||
|
||||
frontends/verilog/verilog_parser.tab.hh: frontends/verilog/verilog_parser.tab.cc
|
||||
|
||||
frontends/verilog/verilog_frontend.o: frontends/verilog/verilog_parser.tab.hh
|
||||
frontends/verilog/preproc.o: frontends/verilog/verilog_parser.tab.hh
|
||||
|
||||
frontends/verilog/verilog_lexer.h: frontends/verilog/verilog_parser.tab.hh
|
||||
frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l frontends/verilog/verilog_parser.tab.cc
|
||||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) flex -o frontends/verilog/verilog_lexer.cc $<
|
||||
|
|
@ -20,5 +24,6 @@ OBJS += frontends/verilog/verilog_parser.tab.o
|
|||
OBJS += frontends/verilog/verilog_lexer.o
|
||||
OBJS += frontends/verilog/preproc.o
|
||||
OBJS += frontends/verilog/verilog_frontend.o
|
||||
OBJS += frontends/verilog/verilog_error.o
|
||||
OBJS += frontends/verilog/const2ast.o
|
||||
|
||||
|
|
|
|||
|
|
@ -42,14 +42,23 @@
|
|||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
using namespace AST;
|
||||
using namespace VERILOG_FRONTEND;
|
||||
|
||||
void ConstParser::log_maybe_loc_error(std::string msg) {
|
||||
log_file_error(*loc.begin.filename, loc.begin.line, "%s", msg);
|
||||
}
|
||||
|
||||
void ConstParser::log_maybe_loc_warn(std::string msg) {
|
||||
log_file_warning(*loc.begin.filename, loc.begin.line, "%s", msg);
|
||||
}
|
||||
|
||||
// divide an arbitrary length decimal number by two and return the rest
|
||||
static int my_decimal_div_by_two(std::vector<uint8_t> &digits)
|
||||
int ConstParser::my_decimal_div_by_two(std::vector<uint8_t> &digits)
|
||||
{
|
||||
int carry = 0;
|
||||
for (size_t i = 0; i < digits.size(); i++) {
|
||||
if (digits[i] >= 10)
|
||||
log_file_error(current_filename, get_line_num(), "Invalid use of [a-fxz?] in decimal constant.\n");
|
||||
log_maybe_loc_error("Invalid use of [a-fxz?] in decimal constant.\n");
|
||||
digits[i] += carry * 10;
|
||||
carry = digits[i] % 2;
|
||||
digits[i] /= 2;
|
||||
|
|
@ -60,7 +69,7 @@ static int my_decimal_div_by_two(std::vector<uint8_t> &digits)
|
|||
}
|
||||
|
||||
// find the number of significant bits in a binary number (not including the sign bit)
|
||||
static int my_ilog2(int x)
|
||||
int ConstParser::my_ilog2(int x)
|
||||
{
|
||||
int ret = 0;
|
||||
while (x != 0 && x != -1) {
|
||||
|
|
@ -71,7 +80,7 @@ static int my_ilog2(int x)
|
|||
}
|
||||
|
||||
// parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?')
|
||||
static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type, bool is_unsized)
|
||||
void ConstParser::my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type, bool is_unsized)
|
||||
{
|
||||
// all digits in string (MSB at index 0)
|
||||
std::vector<uint8_t> digits;
|
||||
|
|
@ -102,8 +111,8 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le
|
|||
int bits_per_digit = my_ilog2(base-1);
|
||||
for (auto it = digits.rbegin(), e = digits.rend(); it != e; it++) {
|
||||
if (*it > (base-1) && *it < 0xf0)
|
||||
log_file_error(current_filename, get_line_num(), "Digit larger than %d used in in base-%d constant.\n",
|
||||
base-1, base);
|
||||
log_maybe_loc_error(stringf("Digit larger than %d used in in base-%d constant.\n",
|
||||
base-1, base));
|
||||
for (int i = 0; i < bits_per_digit; i++) {
|
||||
int bitmask = 1 << i;
|
||||
if (*it == 0xf0)
|
||||
|
|
@ -126,7 +135,7 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le
|
|||
}
|
||||
|
||||
if (is_unsized && (len > len_in_bits))
|
||||
log_file_error(current_filename, get_line_num(), "Unsized constant must have width of 1 bit, but have %d bits!\n", len);
|
||||
log_maybe_loc_error(stringf("Unsized constant must have width of 1 bit, but have %d bits!\n", len));
|
||||
|
||||
for (len = len - 1; len >= 0; len--)
|
||||
if (data[len] == State::S1)
|
||||
|
|
@ -140,21 +149,19 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le
|
|||
}
|
||||
|
||||
if (len_in_bits == 0)
|
||||
log_file_error(current_filename, get_line_num(), "Illegal integer constant size of zero (IEEE 1800-2012, 5.7).\n");
|
||||
log_maybe_loc_error("Illegal integer constant size of zero (IEEE 1800-2012, 5.7).\n");
|
||||
|
||||
if (len > len_in_bits)
|
||||
log_warning("Literal has a width of %d bit, but value requires %d bit. (%s:%d)\n",
|
||||
len_in_bits, len, current_filename.c_str(), get_line_num());
|
||||
log_maybe_loc_warn(stringf("Literal has a width of %d bit, but value requires %d bit.\n",
|
||||
len_in_bits, len));
|
||||
}
|
||||
|
||||
// convert the Verilog code for a constant to an AST node
|
||||
AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn_z)
|
||||
std::unique_ptr<AstNode> ConstParser::const2ast(std::string code, char case_type, bool warn_z)
|
||||
{
|
||||
if (warn_z) {
|
||||
AstNode *ret = const2ast(code, case_type);
|
||||
auto ret = const2ast(code, case_type);
|
||||
if (ret != nullptr && std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end())
|
||||
log_warning("Yosys has only limited support for tri-state logic at the moment. (%s:%d)\n",
|
||||
current_filename.c_str(), get_line_num());
|
||||
log_maybe_loc_warn("Yosys has only limited support for tri-state logic at the moment.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +179,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
|
|||
ch = ch >> 1;
|
||||
}
|
||||
}
|
||||
AstNode *ast = AstNode::mkconst_bits(data, false);
|
||||
auto ast = AstNode::mkconst_bits(loc, data, false);
|
||||
ast->str = code;
|
||||
return ast;
|
||||
}
|
||||
|
|
@ -191,7 +198,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
|
|||
my_strtobin(data, str, -1, 10, case_type, false);
|
||||
if (data.back() == State::S1)
|
||||
data.push_back(State::S0);
|
||||
return AstNode::mkconst_bits(data, true);
|
||||
return AstNode::mkconst_bits(loc, data, true);
|
||||
}
|
||||
|
||||
// unsized constant
|
||||
|
|
@ -239,10 +246,11 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
|
|||
if (is_signed && data.back() == State::S1)
|
||||
data.push_back(State::S0);
|
||||
}
|
||||
return AstNode::mkconst_bits(data, is_signed, is_unsized);
|
||||
return AstNode::mkconst_bits(loc, data, is_signed, is_unsized);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
#include "preproc.h"
|
||||
#include "verilog_frontend.h"
|
||||
#include "frontends/verilog/verilog_parser.tab.hh"
|
||||
#include "kernel/log.h"
|
||||
#include <assert.h>
|
||||
#include <stack>
|
||||
|
|
@ -241,7 +242,7 @@ struct arg_map_t
|
|||
void add_arg(const std::string &name, const char *default_value)
|
||||
{
|
||||
if (find(name)) {
|
||||
log_error("Duplicate macro arguments with name `%s'.\n", name.c_str());
|
||||
log_error("Duplicate macro arguments with name `%s'.\n", name);
|
||||
}
|
||||
|
||||
name_to_pos[name] = args.size();
|
||||
|
|
@ -264,7 +265,7 @@ struct arg_map_t
|
|||
// (something like macro_foobar_arg2). This doesn't include the leading backtick.
|
||||
static std::string str_token(const std::string ¯o_name, int pos)
|
||||
{
|
||||
return stringf("macro_%s_arg%d", macro_name.c_str(), pos);
|
||||
return stringf("macro_%s_arg%d", macro_name, pos);
|
||||
}
|
||||
|
||||
// Return definitions for the macro arguments (so that substituting in the macro body and
|
||||
|
|
@ -740,7 +741,7 @@ read_define(const std::string &filename,
|
|||
defines_map.add(name, value, (state == 2) ? &args : nullptr);
|
||||
global_defines_cache.add(name, value, (state == 2) ? &args : nullptr);
|
||||
} else {
|
||||
log_file_error(filename, 0, "Invalid name for macro definition: >>%s<<.\n", name.c_str());
|
||||
log_file_error(filename, 0, "Invalid name for macro definition: >>%s<<.\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -749,7 +750,9 @@ frontend_verilog_preproc(std::istream &f,
|
|||
std::string filename,
|
||||
const define_map_t &pre_defines,
|
||||
define_map_t &global_defines_cache,
|
||||
const std::list<std::string> &include_dirs)
|
||||
const std::list<std::string> &include_dirs,
|
||||
ParseState &parse_state,
|
||||
ParseMode &parse_mode)
|
||||
{
|
||||
define_map_t defines;
|
||||
defines.merge(pre_defines);
|
||||
|
|
@ -786,14 +789,14 @@ frontend_verilog_preproc(std::istream &f,
|
|||
else if (ifdef_pass_level > 0)
|
||||
ifdef_pass_level--;
|
||||
else
|
||||
log_error("Found %s outside of macro conditional branch!\n", tok.c_str());
|
||||
log_error("Found %s outside of macro conditional branch!\n", tok);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tok == "`else") {
|
||||
if (ifdef_fail_level == 0) {
|
||||
if (ifdef_pass_level == 0)
|
||||
log_error("Found %s outside of macro conditional branch!\n", tok.c_str());
|
||||
log_error("Found %s outside of macro conditional branch!\n", tok);
|
||||
ifdef_pass_level--;
|
||||
ifdef_fail_level = 1;
|
||||
ifdef_already_satisfied = true;
|
||||
|
|
@ -810,7 +813,7 @@ frontend_verilog_preproc(std::istream &f,
|
|||
std::string name = next_token(true);
|
||||
if (ifdef_fail_level == 0) {
|
||||
if (ifdef_pass_level == 0)
|
||||
log_error("Found %s outside of macro conditional branch!\n", tok.c_str());
|
||||
log_error("Found %s outside of macro conditional branch!\n", tok);
|
||||
ifdef_pass_level--;
|
||||
ifdef_fail_level = 1;
|
||||
ifdef_already_satisfied = true;
|
||||
|
|
@ -892,11 +895,7 @@ frontend_verilog_preproc(std::istream &f,
|
|||
// if the include file was not found, it is not given with an absolute path, and the
|
||||
// currently read file is given with a path, then try again relative to its directory
|
||||
ff.clear();
|
||||
#ifdef _WIN32
|
||||
fixed_fn = filename.substr(0, filename.find_last_of("/\\")+1) + fn;
|
||||
#else
|
||||
fixed_fn = filename.substr(0, filename.rfind('/')+1) + fn;
|
||||
#endif
|
||||
fixed_fn = parent_from_file_path(filename) + fn;
|
||||
ff.open(fixed_fn);
|
||||
}
|
||||
if (ff.fail() && fn.size() > 0 && fn_relative) {
|
||||
|
|
@ -961,6 +960,11 @@ frontend_verilog_preproc(std::istream &f,
|
|||
}
|
||||
|
||||
if (tok == "`resetall") {
|
||||
parse_state.default_nettype_wire = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tok == "`undefineall" && parse_mode.sv) {
|
||||
defines.clear();
|
||||
global_defines_cache.clear();
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,11 @@ YOSYS_NAMESPACE_BEGIN
|
|||
struct define_body_t;
|
||||
struct arg_map_t;
|
||||
|
||||
namespace VERILOG_FRONTEND {
|
||||
struct ParseState;
|
||||
struct ParseMode;
|
||||
};
|
||||
|
||||
struct define_map_t
|
||||
{
|
||||
define_map_t();
|
||||
|
|
@ -71,7 +76,9 @@ frontend_verilog_preproc(std::istream &f,
|
|||
std::string filename,
|
||||
const define_map_t &pre_defines,
|
||||
define_map_t &global_defines_cache,
|
||||
const std::list<std::string> &include_dirs);
|
||||
const std::list<std::string> &include_dirs,
|
||||
VERILOG_FRONTEND::ParseState &parse_state,
|
||||
VERILOG_FRONTEND::ParseMode &parse_mode);
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
|
|
|
|||
|
|
@ -17,35 +17,29 @@
|
|||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward frontend for the RTLIL text
|
||||
* representation.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RTLIL_FRONTEND_H
|
||||
#define RTLIL_FRONTEND_H
|
||||
#include "kernel/yosys_common.h"
|
||||
#include "frontends/verilog/verilog_error.h"
|
||||
#include "frontends/verilog/verilog_location.h"
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
/**
|
||||
* Legacy behavior is to only track lines. Now we have columns too, but we don't
|
||||
* report them in errors.
|
||||
* TODO: report columns, too
|
||||
*/
|
||||
|
||||
namespace RTLIL_FRONTEND {
|
||||
extern std::istream *lexin;
|
||||
extern RTLIL::Design *current_design;
|
||||
extern bool flag_nooverwrite;
|
||||
extern bool flag_overwrite;
|
||||
extern bool flag_lib;
|
||||
[[noreturn]]
|
||||
void VERILOG_FRONTEND::formatted_err_at_loc(Location loc, std::string str)
|
||||
{
|
||||
YOSYS_NAMESPACE_PREFIX log_file_error(loc.begin.filename ? *(loc.begin.filename) : "UNKNOWN", loc.begin.line,
|
||||
"%s\n", std::move(str));
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
extern int rtlil_frontend_yydebug;
|
||||
int rtlil_frontend_yylex(void);
|
||||
void rtlil_frontend_yyerror(char const *s);
|
||||
void rtlil_frontend_yyrestart(FILE *f);
|
||||
int rtlil_frontend_yyparse(void);
|
||||
int rtlil_frontend_yylex_destroy(void);
|
||||
int rtlil_frontend_yyget_lineno(void);
|
||||
|
||||
#endif
|
||||
|
||||
void VERILOG_FRONTEND::formatted_warn_at_loc(Location loc, std::string str)
|
||||
{
|
||||
YOSYS_NAMESPACE_PREFIX log_file_warning(loc.begin.filename ? *(loc.begin.filename) : "UNKNOWN", loc.begin.line,
|
||||
"%s\n", std::move(str));
|
||||
}
|
||||
31
frontends/verilog/verilog_error.h
Normal file
31
frontends/verilog/verilog_error.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef VERILOG_ERROR_H
|
||||
#define VERILOG_ERROR_H
|
||||
|
||||
#include "kernel/yosys_common.h"
|
||||
#include "frontends/ast/ast.h"
|
||||
#include "frontends/verilog/verilog_location.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
namespace VERILOG_FRONTEND
|
||||
{
|
||||
[[noreturn]]
|
||||
void formatted_err_at_loc(Location loc, std::string str);
|
||||
template <typename... Args>
|
||||
[[noreturn]]
|
||||
void err_at_loc(Location loc, FmtString<TypeIdentity<Args>...> fmt, const Args &... args)
|
||||
{
|
||||
formatted_err_at_loc(std::move(loc), fmt.format(args...));
|
||||
}
|
||||
|
||||
void formatted_warn_at_loc(Location loc, std::string str);
|
||||
template <typename... Args>
|
||||
void warn_at_loc(Location loc, FmtString<TypeIdentity<Args>...> fmt, const Args &... args)
|
||||
{
|
||||
formatted_warn_at_loc(std::move(loc), fmt.format(args...));
|
||||
}
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
@ -26,11 +26,19 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#if !defined(__wasm)
|
||||
#include <filesystem>
|
||||
#endif
|
||||
|
||||
#include "verilog_frontend.h"
|
||||
#include "verilog_lexer.h"
|
||||
#include "verilog_error.h"
|
||||
#include "verilog_location.h"
|
||||
#include "preproc.h"
|
||||
#include "kernel/yosys.h"
|
||||
#include "libs/sha1/sha1.h"
|
||||
#include <stdarg.h>
|
||||
#include <list>
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
using namespace VERILOG_FRONTEND;
|
||||
|
|
@ -42,13 +50,13 @@ static std::list<std::vector<std::string>> verilog_defaults_stack;
|
|||
|
||||
static void error_on_dpi_function(AST::AstNode *node)
|
||||
{
|
||||
if (node->type == AST::AST_DPI_FUNCTION)
|
||||
log_file_error(node->filename, node->location.first_line, "Found DPI function %s.\n", node->str.c_str());
|
||||
for (auto child : node->children)
|
||||
error_on_dpi_function(child);
|
||||
if (node->type == AST::AST_DPI_FUNCTION)
|
||||
err_at_loc(node->location, "Found DPI function %s.\n", node->str);
|
||||
for (auto& child : node->children)
|
||||
error_on_dpi_function(child.get());
|
||||
}
|
||||
|
||||
static void add_package_types(dict<std::string, AST::AstNode *> &user_types, std::vector<AST::AstNode *> &package_list)
|
||||
static void add_package_types(dict<std::string, AST::AstNode *> &user_types, std::vector<std::unique_ptr<AST::AstNode>> &package_list)
|
||||
{
|
||||
// prime the parser's user type lookup table with the package qualified names
|
||||
// of typedefed names in the packages seen so far.
|
||||
|
|
@ -57,7 +65,7 @@ static void add_package_types(dict<std::string, AST::AstNode *> &user_types, std
|
|||
for (const auto &node: pkg->children) {
|
||||
if (node->type == AST::AST_TYPEDEF) {
|
||||
std::string s = pkg->str + "::" + node->str.substr(1);
|
||||
user_types[s] = node;
|
||||
user_types[s] = node.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -100,6 +108,10 @@ struct VerilogFrontend : public Frontend {
|
|||
log(" -assert-assumes\n");
|
||||
log(" treat all assume() statements like assert() statements\n");
|
||||
log("\n");
|
||||
log(" -nodisplay\n");
|
||||
log(" suppress output from display system tasks ($display et. al).\n");
|
||||
log(" This does not affect the output from a later 'sim' command.\n");
|
||||
log("\n");
|
||||
log(" -debug\n");
|
||||
log(" alias for -dump_ast1 -dump_ast2 -dump_vlog1 -dump_vlog2 -yydebug\n");
|
||||
log("\n");
|
||||
|
|
@ -217,6 +229,10 @@ struct VerilogFrontend : public Frontend {
|
|||
log(" add 'dir' to the directories which are used when searching include\n");
|
||||
log(" files\n");
|
||||
log("\n");
|
||||
log(" -relativeshare\n");
|
||||
log(" use paths relative to share directory for source locations\n");
|
||||
log(" where possible (experimental).\n");
|
||||
log("\n");
|
||||
log("The command 'verilog_defaults' can be used to register default options for\n");
|
||||
log("subsequent calls to 'read_verilog'.\n");
|
||||
log("\n");
|
||||
|
|
@ -235,12 +251,15 @@ struct VerilogFrontend : public Frontend {
|
|||
}
|
||||
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
bool flag_nodisplay = false;
|
||||
bool flag_dump_ast1 = false;
|
||||
bool flag_dump_ast2 = false;
|
||||
bool flag_no_dump_ptr = false;
|
||||
bool flag_dump_vlog1 = false;
|
||||
bool flag_dump_vlog2 = false;
|
||||
bool flag_dump_rtlil = false;
|
||||
bool flag_debug_lexer = false;
|
||||
bool flag_debug_parser = false;
|
||||
bool flag_nolatches = false;
|
||||
bool flag_nomeminit = false;
|
||||
bool flag_nomem2reg = false;
|
||||
|
|
@ -257,19 +276,25 @@ struct VerilogFrontend : public Frontend {
|
|||
bool flag_noblackbox = false;
|
||||
bool flag_nowb = false;
|
||||
bool flag_nosynthesis = false;
|
||||
bool flag_yydebug = false;
|
||||
bool flag_relative_share = false;
|
||||
define_map_t defines_map;
|
||||
|
||||
std::list<std::string> include_dirs;
|
||||
std::list<std::string> attributes;
|
||||
|
||||
frontend_verilog_yydebug = false;
|
||||
sv_mode = false;
|
||||
formal_mode = false;
|
||||
norestrict_mode = false;
|
||||
assume_asserts_mode = false;
|
||||
lib_mode = false;
|
||||
specify_mode = false;
|
||||
default_nettype_wire = true;
|
||||
ParseMode parse_mode = {};
|
||||
ParseState parse_state = {};
|
||||
parse_mode.sv = false;
|
||||
parse_mode.formal = false;
|
||||
parse_mode.noassert = false;
|
||||
parse_mode.noassume = false;
|
||||
parse_mode.norestrict = false;
|
||||
parse_mode.assume_asserts = false;
|
||||
parse_mode.assert_assumes = false;
|
||||
parse_mode.lib = false;
|
||||
parse_mode.specify = false;
|
||||
parse_state.default_nettype_wire = true;
|
||||
|
||||
args.insert(args.begin()+1, verilog_defaults.begin(), verilog_defaults.end());
|
||||
|
||||
|
|
@ -277,11 +302,11 @@ struct VerilogFrontend : public Frontend {
|
|||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-sv") {
|
||||
sv_mode = true;
|
||||
parse_mode.sv = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-formal") {
|
||||
formal_mode = true;
|
||||
parse_mode.formal = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-nosynthesis") {
|
||||
|
|
@ -289,23 +314,27 @@ struct VerilogFrontend : public Frontend {
|
|||
continue;
|
||||
}
|
||||
if (arg == "-noassert") {
|
||||
noassert_mode = true;
|
||||
parse_mode.noassert = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-noassume") {
|
||||
noassume_mode = true;
|
||||
parse_mode.noassume = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-norestrict") {
|
||||
norestrict_mode = true;
|
||||
parse_mode.norestrict = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-assume-asserts") {
|
||||
assume_asserts_mode = true;
|
||||
parse_mode.assume_asserts = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-assert-assumes") {
|
||||
assert_assumes_mode = true;
|
||||
parse_mode.assert_assumes = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-nodisplay") {
|
||||
flag_nodisplay = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-debug") {
|
||||
|
|
@ -313,7 +342,8 @@ struct VerilogFrontend : public Frontend {
|
|||
flag_dump_ast2 = true;
|
||||
flag_dump_vlog1 = true;
|
||||
flag_dump_vlog2 = true;
|
||||
frontend_verilog_yydebug = true;
|
||||
flag_debug_lexer = true;
|
||||
flag_debug_parser = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-dump_ast1") {
|
||||
|
|
@ -341,7 +371,9 @@ struct VerilogFrontend : public Frontend {
|
|||
continue;
|
||||
}
|
||||
if (arg == "-yydebug") {
|
||||
frontend_verilog_yydebug = true;
|
||||
flag_yydebug = true;
|
||||
flag_debug_lexer = true;
|
||||
flag_debug_parser = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-nolatches") {
|
||||
|
|
@ -377,7 +409,7 @@ struct VerilogFrontend : public Frontend {
|
|||
continue;
|
||||
}
|
||||
if (arg == "-lib") {
|
||||
lib_mode = true;
|
||||
parse_mode.lib = true;
|
||||
defines_map.add("BLACKBOX", "");
|
||||
continue;
|
||||
}
|
||||
|
|
@ -386,7 +418,7 @@ struct VerilogFrontend : public Frontend {
|
|||
continue;
|
||||
}
|
||||
if (arg == "-specify") {
|
||||
specify_mode = true;
|
||||
parse_mode.specify = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-noopt") {
|
||||
|
|
@ -416,13 +448,18 @@ struct VerilogFrontend : public Frontend {
|
|||
continue;
|
||||
}
|
||||
if (arg == "-noautowire") {
|
||||
default_nettype_wire = false;
|
||||
parse_state.default_nettype_wire = false;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-setattr" && argidx+1 < args.size()) {
|
||||
attributes.push_back(RTLIL::escape_id(args[++argidx]));
|
||||
continue;
|
||||
}
|
||||
if (arg == "-relativeshare") {
|
||||
flag_relative_share = true;
|
||||
log_experimental("read_verilog -relativeshare");
|
||||
continue;
|
||||
}
|
||||
if (arg == "-D" && argidx+1 < args.size()) {
|
||||
std::string name = args[++argidx], value;
|
||||
size_t equal = name.find('=');
|
||||
|
|
@ -453,76 +490,89 @@ struct VerilogFrontend : public Frontend {
|
|||
break;
|
||||
}
|
||||
|
||||
if (formal_mode || !flag_nosynthesis)
|
||||
defines_map.add(formal_mode ? "FORMAL" : "SYNTHESIS", "1");
|
||||
if (parse_mode.formal || !flag_nosynthesis)
|
||||
defines_map.add(parse_mode.formal ? "FORMAL" : "SYNTHESIS", "1");
|
||||
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
log_header(design, "Executing Verilog-2005 frontend: %s\n", filename.c_str());
|
||||
log_header(design, "Executing Verilog-2005 frontend: %s\n", filename);
|
||||
|
||||
log("Parsing %s%s input from `%s' to AST representation.\n",
|
||||
formal_mode ? "formal " : "", sv_mode ? "SystemVerilog" : "Verilog", filename.c_str());
|
||||
parse_mode.formal ? "formal " : "", parse_mode.sv ? "SystemVerilog" : "Verilog", filename.c_str());
|
||||
|
||||
AST::current_filename = filename;
|
||||
AST::set_line_num = &frontend_verilog_yyset_lineno;
|
||||
AST::get_line_num = &frontend_verilog_yyget_lineno;
|
||||
|
||||
current_ast = new AST::AstNode(AST::AST_DESIGN);
|
||||
|
||||
lexin = f;
|
||||
log("verilog frontend filename %s\n", filename.c_str());
|
||||
if (flag_relative_share) {
|
||||
auto share_path = proc_share_dirname();
|
||||
if (filename.substr(0, share_path.length()) == share_path)
|
||||
filename = std::string("+/") + filename.substr(share_path.length());
|
||||
log("new filename %s\n", filename.c_str());
|
||||
}
|
||||
AST::sv_mode_but_global_and_used_for_literally_one_condition = parse_mode.sv;
|
||||
std::string code_after_preproc;
|
||||
|
||||
parse_state.lexin = f;
|
||||
if (!flag_nopp) {
|
||||
code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, *design->verilog_defines, include_dirs);
|
||||
code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, *design->verilog_defines, include_dirs, parse_state, parse_mode);
|
||||
if (flag_ppdump)
|
||||
log("-- Verilog code after preprocessor --\n%s-- END OF DUMP --\n", code_after_preproc.c_str());
|
||||
lexin = new std::istringstream(code_after_preproc);
|
||||
log("-- Verilog code after preprocessor --\n%s-- END OF DUMP --\n", code_after_preproc);
|
||||
parse_state.lexin = new std::istringstream(code_after_preproc);
|
||||
}
|
||||
|
||||
auto filename_shared = std::make_shared<std::string>(filename);
|
||||
auto top_loc = Location();
|
||||
top_loc.begin.filename = filename_shared;
|
||||
parse_state.current_ast = new AST::AstNode(top_loc, AST::AST_DESIGN);
|
||||
VerilogLexer lexer(&parse_state, &parse_mode, filename_shared);
|
||||
frontend_verilog_yy::parser parser(&lexer, &parse_state, &parse_mode);
|
||||
lexer.set_debug(flag_debug_lexer);
|
||||
parser.set_debug_level(flag_debug_parser ? 1 : 0);
|
||||
|
||||
// make package typedefs available to parser
|
||||
add_package_types(pkg_user_types, design->verilog_packages);
|
||||
add_package_types(parse_state.pkg_user_types, design->verilog_packages);
|
||||
|
||||
UserTypeMap global_types_map;
|
||||
for (auto def : design->verilog_globals) {
|
||||
for (auto& def : design->verilog_globals) {
|
||||
if (def->type == AST::AST_TYPEDEF) {
|
||||
global_types_map[def->str] = def;
|
||||
global_types_map[def->str] = def.get();
|
||||
}
|
||||
}
|
||||
|
||||
log_assert(user_type_stack.empty());
|
||||
log_assert(parse_state.user_type_stack.empty());
|
||||
// use previous global typedefs as bottom level of user type stack
|
||||
user_type_stack.push_back(std::move(global_types_map));
|
||||
parse_state.user_type_stack.push_back(std::move(global_types_map));
|
||||
// add a new empty type map to allow overriding existing global definitions
|
||||
user_type_stack.push_back(UserTypeMap());
|
||||
parse_state.user_type_stack.push_back(UserTypeMap());
|
||||
|
||||
frontend_verilog_yyset_lineno(1);
|
||||
frontend_verilog_yyrestart(NULL);
|
||||
frontend_verilog_yyparse();
|
||||
frontend_verilog_yylex_destroy();
|
||||
if (flag_yydebug) {
|
||||
lexer.set_debug(true);
|
||||
parser.set_debug_level(1);
|
||||
}
|
||||
parser.parse();
|
||||
// frontend_verilog_yyset_lineno(1);
|
||||
|
||||
for (auto &child : current_ast->children) {
|
||||
for (auto &child : parse_state.current_ast->children) {
|
||||
if (child->type == AST::AST_MODULE)
|
||||
for (auto &attr : attributes)
|
||||
if (child->attributes.count(attr) == 0)
|
||||
child->attributes[attr] = AST::AstNode::mkconst_int(1, false);
|
||||
child->attributes[attr] = AST::AstNode::mkconst_int(top_loc, 1, false);
|
||||
}
|
||||
|
||||
if (flag_nodpi)
|
||||
error_on_dpi_function(current_ast);
|
||||
error_on_dpi_function(parse_state.current_ast);
|
||||
|
||||
AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches,
|
||||
flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, lib_mode, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire);
|
||||
AST::process(design, parse_state.current_ast, flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches,
|
||||
flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, parse_mode.lib, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_nooverwrite, flag_overwrite, flag_defer, parse_state.default_nettype_wire);
|
||||
|
||||
|
||||
if (!flag_nopp)
|
||||
delete lexin;
|
||||
delete parse_state.lexin;
|
||||
|
||||
// only the previous and new global type maps remain
|
||||
log_assert(user_type_stack.size() == 2);
|
||||
user_type_stack.clear();
|
||||
log_assert(parse_state.user_type_stack.size() == 2);
|
||||
parse_state.user_type_stack.clear();
|
||||
|
||||
delete current_ast;
|
||||
current_ast = NULL;
|
||||
delete parse_state.current_ast;
|
||||
parse_state.current_ast = NULL;
|
||||
|
||||
log("Successfully finished Verilog frontend.\n");
|
||||
}
|
||||
|
|
@ -660,19 +710,87 @@ struct VerilogDefines : public Pass {
|
|||
}
|
||||
} VerilogDefines;
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
#if !defined(__wasm)
|
||||
|
||||
// the yyerror function used by bison to report parser errors
|
||||
void frontend_verilog_yyerror(char const *fmt, ...)
|
||||
static void parse_file_list(const std::string &file_list_path, RTLIL::Design *design, bool relative_to_file_list_path)
|
||||
{
|
||||
va_list ap;
|
||||
char buffer[1024];
|
||||
char *p = buffer;
|
||||
va_start(ap, fmt);
|
||||
p += vsnprintf(p, buffer + sizeof(buffer) - p, fmt, ap);
|
||||
va_end(ap);
|
||||
p += snprintf(p, buffer + sizeof(buffer) - p, "\n");
|
||||
YOSYS_NAMESPACE_PREFIX log_file_error(YOSYS_NAMESPACE_PREFIX AST::current_filename, frontend_verilog_yyget_lineno(),
|
||||
"%s", buffer);
|
||||
exit(1);
|
||||
std::ifstream flist(file_list_path);
|
||||
if (!flist.is_open()) {
|
||||
log_error("Verilog file list file does not exist");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::filesystem::path file_list_parent_dir = std::filesystem::path(file_list_path).parent_path();
|
||||
|
||||
std::string v_file_name;
|
||||
while (std::getline(flist, v_file_name)) {
|
||||
if (v_file_name.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::filesystem::path verilog_file_path;
|
||||
if (relative_to_file_list_path) {
|
||||
verilog_file_path = file_list_parent_dir / v_file_name;
|
||||
} else {
|
||||
verilog_file_path = std::filesystem::current_path() / v_file_name;
|
||||
}
|
||||
|
||||
bool is_sv = (verilog_file_path.extension() == ".sv");
|
||||
|
||||
std::vector<std::string> read_verilog_cmd = {"read_verilog", "-defer"};
|
||||
if (is_sv) {
|
||||
read_verilog_cmd.push_back("-sv");
|
||||
}
|
||||
read_verilog_cmd.push_back(verilog_file_path.string());
|
||||
Pass::call(design, read_verilog_cmd);
|
||||
}
|
||||
|
||||
flist.close();
|
||||
}
|
||||
|
||||
struct VerilogFileList : public Pass {
|
||||
VerilogFileList() : Pass("read_verilog_file_list", "parse a Verilog file list") {}
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" read_verilog_file_list [options]\n");
|
||||
log("\n");
|
||||
log("Parse a Verilog file list, and pass the list of Verilog files to read_verilog\n");
|
||||
log("command\n");
|
||||
log("\n");
|
||||
log(" -F file_list_path\n");
|
||||
log(" File list file contains list of Verilog files to be parsed, any path is\n");
|
||||
log(" treated relative to the file list file\n");
|
||||
log("\n");
|
||||
log(" -f file_list_path\n");
|
||||
log(" File list file contains list of Verilog files to be parsed, any path is\n");
|
||||
log(" treated relative to current working directroy\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-F" && argidx + 1 < args.size()) {
|
||||
std::string file_list_path = args[++argidx];
|
||||
parse_file_list(file_list_path, design, true);
|
||||
continue;
|
||||
}
|
||||
if (arg == "-f" && argidx + 1 < args.size()) {
|
||||
std::string file_list_path = args[++argidx];
|
||||
parse_file_list(file_list_path, design, false);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
extra_args(args, argidx, design, false);
|
||||
}
|
||||
} VerilogFilelist;
|
||||
|
||||
#endif
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -31,70 +31,33 @@
|
|||
|
||||
#include "kernel/yosys.h"
|
||||
#include "frontends/ast/ast.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <list>
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
namespace VERILOG_FRONTEND
|
||||
{
|
||||
// this variable is set to a new AST_DESIGN node and then filled with the AST by the bison parser
|
||||
extern struct AST::AstNode *current_ast;
|
||||
/* Ephemeral context class */
|
||||
struct ConstParser {
|
||||
AST::AstSrcLocType loc;
|
||||
private:
|
||||
void log_maybe_loc_error(std::string msg);
|
||||
void log_maybe_loc_warn(std::string msg);
|
||||
// divide an arbitrary length decimal number by two and return the rest
|
||||
int my_decimal_div_by_two(std::vector<uint8_t> &digits);
|
||||
// find the number of significant bits in a binary number (not including the sign bit)
|
||||
int my_ilog2(int x);
|
||||
// parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?')
|
||||
void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type, bool is_unsized);
|
||||
public:
|
||||
// convert the Verilog code for a constant to an AST node
|
||||
std::unique_ptr<AST::AstNode> const2ast(std::string code, char case_type = 0, bool warn_z = false);
|
||||
|
||||
// this function converts a Verilog constant to an AST_CONSTANT node
|
||||
AST::AstNode *const2ast(std::string code, char case_type = 0, bool warn_z = false);
|
||||
|
||||
// names of locally typedef'ed types in a stack
|
||||
typedef std::map<std::string, AST::AstNode*> UserTypeMap;
|
||||
extern std::vector<UserTypeMap> user_type_stack;
|
||||
|
||||
// names of package typedef'ed types
|
||||
extern dict<std::string, AST::AstNode*> pkg_user_types;
|
||||
|
||||
// state of `default_nettype
|
||||
extern bool default_nettype_wire;
|
||||
|
||||
// running in SystemVerilog mode
|
||||
extern bool sv_mode;
|
||||
|
||||
// running in -formal mode
|
||||
extern bool formal_mode;
|
||||
|
||||
// running in -noassert mode
|
||||
extern bool noassert_mode;
|
||||
|
||||
// running in -noassume mode
|
||||
extern bool noassume_mode;
|
||||
|
||||
// running in -norestrict mode
|
||||
extern bool norestrict_mode;
|
||||
|
||||
// running in -assume-asserts mode
|
||||
extern bool assume_asserts_mode;
|
||||
|
||||
// running in -assert-assumes mode
|
||||
extern bool assert_assumes_mode;
|
||||
|
||||
// running in -lib mode
|
||||
extern bool lib_mode;
|
||||
|
||||
// running in -specify mode
|
||||
extern bool specify_mode;
|
||||
|
||||
// lexer input stream
|
||||
extern std::istream *lexin;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
// the usual bison/flex stuff
|
||||
extern int frontend_verilog_yydebug;
|
||||
void frontend_verilog_yyerror(char const *fmt, ...);
|
||||
void frontend_verilog_yyrestart(FILE *f);
|
||||
int frontend_verilog_yyparse(void);
|
||||
int frontend_verilog_yylex_destroy(void);
|
||||
int frontend_verilog_yyget_lineno(void);
|
||||
void frontend_verilog_yyset_lineno (int);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
48
frontends/verilog/verilog_lexer.h
Normal file
48
frontends/verilog/verilog_lexer.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef VERILOG_LEXER_H
|
||||
#define VERILOG_LEXER_H
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "frontends/ast/ast.h"
|
||||
#include "frontends/verilog/verilog_parser.tab.hh"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#if ! defined(yyFlexLexerOnce)
|
||||
#define yyFlexLexer frontend_verilog_yyFlexLexer
|
||||
#include <FlexLexer.h>
|
||||
#endif
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
namespace VERILOG_FRONTEND {
|
||||
using parser = frontend_verilog_yy::parser;
|
||||
class VerilogLexer : public frontend_verilog_yyFlexLexer {
|
||||
ParseState* extra;
|
||||
ParseMode* mode;
|
||||
parser::location_type out_loc;
|
||||
public:
|
||||
VerilogLexer(ParseState* e, ParseMode* m, std::shared_ptr<string> filename) : frontend_verilog_yyFlexLexer(e->lexin), extra(e), mode(m) {
|
||||
out_loc.begin.filename = filename;
|
||||
}
|
||||
~VerilogLexer() override {}
|
||||
// autogenerated body due to YY_DECL
|
||||
parser::symbol_type nextToken();
|
||||
// get rid of override virtual function warning
|
||||
using FlexLexer::yylex;
|
||||
parser::symbol_type terminate() {
|
||||
return parser::make_FRONTEND_VERILOG_YYEOF(out_loc);
|
||||
}
|
||||
private:
|
||||
std::shared_ptr<std::string> current_filename;
|
||||
std::vector<std::shared_ptr<std::string>> fn_stack;
|
||||
std::vector<int> ln_stack;
|
||||
int LexerInput(char* buf, int max_size) override {
|
||||
return readsome(*extra->lexin, buf, max_size);
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
@ -32,6 +32,13 @@
|
|||
*
|
||||
*/
|
||||
|
||||
%option c++
|
||||
%option yyclass="VerilogLexer"
|
||||
%option noyywrap
|
||||
%option nounput
|
||||
%option yylineno
|
||||
%option prefix="frontend_verilog_yy"
|
||||
|
||||
%{
|
||||
|
||||
#ifdef __clang__
|
||||
|
|
@ -41,88 +48,234 @@
|
|||
#pragma clang diagnostic ignored "-Wmisleading-indentation"
|
||||
#endif
|
||||
|
||||
#include "kernel/log.h"
|
||||
#include "frontends/verilog/verilog_frontend.h"
|
||||
#include "frontends/verilog/verilog_lexer.h"
|
||||
#include "frontends/ast/ast.h"
|
||||
#include "verilog_parser.tab.hh"
|
||||
#include "frontends/verilog/verilog_location.h"
|
||||
#include "kernel/log.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
using namespace AST;
|
||||
using namespace VERILOG_FRONTEND;
|
||||
|
||||
#define YYSTYPE FRONTEND_VERILOG_YYSTYPE
|
||||
#define YYLTYPE FRONTEND_VERILOG_YYLTYPE
|
||||
using parser = frontend_verilog_yy::parser;
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
namespace VERILOG_FRONTEND {
|
||||
std::vector<std::string> fn_stack;
|
||||
std::vector<int> ln_stack;
|
||||
YYLTYPE real_location;
|
||||
YYLTYPE old_location;
|
||||
}
|
||||
#undef YY_DECL
|
||||
#define YY_DECL parser::symbol_type VerilogLexer::nextToken()
|
||||
|
||||
#undef yyterminate
|
||||
#define yyterminate() return terminate()
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#define SV_KEYWORD(_tok) \
|
||||
if (sv_mode) return _tok; \
|
||||
if (mode->sv) return _tok; \
|
||||
log("Lexer warning: The SystemVerilog keyword `%s' (at %s:%d) is not "\
|
||||
"recognized unless read_verilog is called with -sv!\n", yytext, \
|
||||
AST::current_filename.c_str(), frontend_verilog_yyget_lineno()); \
|
||||
yylval->string = new std::string(std::string("\\") + yytext); \
|
||||
return TOK_ID;
|
||||
"recognized unless read_verilog is called with -sv!\n", YYText(), \
|
||||
current_filename->c_str(), yylineno); \
|
||||
string_t val = std::make_unique<std::string>(std::string("\\") + YYText()); \
|
||||
return parser::make_TOK_ID(std::move(val), out_loc);
|
||||
|
||||
#define NON_KEYWORD() \
|
||||
yylval->string = new std::string(std::string("\\") + yytext); \
|
||||
return TOK_ID;
|
||||
string_t val = std::make_unique<std::string>(std::string("\\") + YYText()); \
|
||||
return parser::make_TOK_ID(std::move(val), out_loc);
|
||||
|
||||
#define YY_INPUT(buf,result,max_size) \
|
||||
result = readsome(*VERILOG_FRONTEND::lexin, buf, max_size)
|
||||
|
||||
#define YY_USER_ACTION \
|
||||
old_location = real_location; \
|
||||
real_location.first_line = real_location.last_line; \
|
||||
real_location.first_column = real_location.last_column; \
|
||||
for(int i = 0; yytext[i] != '\0'; ++i){ \
|
||||
if(yytext[i] == '\n') { \
|
||||
real_location.last_line++; \
|
||||
real_location.last_column = 1; \
|
||||
} \
|
||||
else { \
|
||||
real_location.last_column++; \
|
||||
} \
|
||||
} \
|
||||
(*yylloc) = real_location;
|
||||
out_loc.step(); \
|
||||
for(int i = 0; YYText()[i] != '\0'; ++i){ \
|
||||
if(YYText()[i] == '\n') { \
|
||||
out_loc.lines(); \
|
||||
} \
|
||||
else { \
|
||||
out_loc.columns(); \
|
||||
} \
|
||||
} \
|
||||
out_loc.begin.filename = current_filename; \
|
||||
out_loc.end.filename = current_filename;
|
||||
|
||||
#define YY_BREAK \
|
||||
(*yylloc) = old_location; \
|
||||
break;
|
||||
|
||||
#undef YY_BUF_SIZE
|
||||
#define YY_BUF_SIZE 65536
|
||||
|
||||
extern int frontend_verilog_yylex(YYSTYPE *yylval_param, YYLTYPE *yyloc_param);
|
||||
|
||||
static bool isUserType(std::string &s)
|
||||
static bool isUserType(ParseState* extra, std::string &s)
|
||||
{
|
||||
// check current scope then outer scopes for a name
|
||||
for (auto it = user_type_stack.rbegin(); it != user_type_stack.rend(); ++it) {
|
||||
for (auto it = extra->user_type_stack.rbegin(); it != extra->user_type_stack.rend(); ++it) {
|
||||
if (it->count(s) > 0) {
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
parser::symbol_type char_tok(char c, parser::location_type loc) {
|
||||
switch (c) {
|
||||
case '!': return parser::make_TOK_EXCL(loc);
|
||||
case '#': return parser::make_TOK_HASH(loc);
|
||||
case '%': return parser::make_TOK_PERC(loc);
|
||||
case '&': return parser::make_TOK_AMP(loc);
|
||||
case '(': return parser::make_TOK_LPAREN(loc);
|
||||
case ')': return parser::make_TOK_RPAREN(loc);
|
||||
case '*': return parser::make_TOK_ASTER(loc);
|
||||
case '+': return parser::make_TOK_PLUS(loc);
|
||||
case ',': return parser::make_TOK_COMMA(loc);
|
||||
case '-': return parser::make_TOK_MINUS(loc);
|
||||
case '.': return parser::make_TOK_DOT(loc);
|
||||
case '/': return parser::make_TOK_SLASH(loc);
|
||||
case ':': return parser::make_TOK_COL(loc);
|
||||
case ';': return parser::make_TOK_SEMICOL(loc);
|
||||
case '<': return parser::make_TOK_LT(loc);
|
||||
case '=': return parser::make_TOK_EQ(loc);
|
||||
case '>': return parser::make_TOK_GT(loc);
|
||||
case '?': return parser::make_TOK_QUE(loc);
|
||||
case '@': return parser::make_TOK_AT(loc);
|
||||
case '[': return parser::make_TOK_LBRA(loc);
|
||||
case ']': return parser::make_TOK_RBRA(loc);
|
||||
case '^': return parser::make_TOK_CARET(loc);
|
||||
case '_': return parser::make_TOK_UNDER(loc);
|
||||
case '{': return parser::make_TOK_LCURL(loc);
|
||||
case '|': return parser::make_TOK_PIPE(loc);
|
||||
case '}': return parser::make_TOK_RCURL(loc);
|
||||
case '~': return parser::make_TOK_TILDE(loc);
|
||||
case 'n': return parser::make_TOK_n(loc);
|
||||
case 'p': return parser::make_TOK_p(loc);
|
||||
case 'x': return parser::make_TOK_x(loc);
|
||||
case 'z': return parser::make_TOK_z(loc);
|
||||
case 0: return parser::make_FRONTEND_VERILOG_YYEOF(loc);
|
||||
default:
|
||||
return parser::make_ch_t(c, loc);
|
||||
}
|
||||
}
|
||||
static bool is_hex_dig(char c, int *val, parser::location_type loc)
|
||||
{
|
||||
if ('0' <= c && c <= '9') {
|
||||
*val = c - '0';
|
||||
return true;
|
||||
} else if ('a' <= c && c <= 'f') {
|
||||
*val = c - 'a' + 0xA;
|
||||
return true;
|
||||
} else if ('A' <= c && c <= 'F') {
|
||||
*val = c - 'A' + 0xA;
|
||||
return true;
|
||||
} else if (c == 'x' || c == 'X' || c == 'z' || c == 'Z' || c == '?') {
|
||||
log_file_warning(*loc.begin.filename, loc.begin.line, "'%c' not a valid digit in hex escape sequence.\n", c);
|
||||
*val = 0; // not semantically valid in hex escape...
|
||||
return true; // ...but still processed as part of hex token
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_oct_dig(char c, int *val, parser::location_type loc)
|
||||
{
|
||||
if ('0' <= c && c <= '7') {
|
||||
*val = c - '0';
|
||||
return true;
|
||||
} else if (c == 'x' || c == 'X' || c == 'z' || c == 'Z' || c == '?') {
|
||||
log_file_warning(*loc.begin.filename, loc.begin.line, "'%c' not a valid digit in octal escape sequence.\n", c);
|
||||
*val = 0; // not semantically valid in octal escape...
|
||||
return true; // ...but still processed as part of octal token
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static parser::symbol_type process_str(char *str, int len, bool triple, parser::location_type loc)
|
||||
{
|
||||
char *in, *out; // Overwrite input buffer: flex manual states "Actions
|
||||
// are free to modify 'yytext' except for lengthening it".
|
||||
|
||||
for (in = str, out = str; in < str + len; in++)
|
||||
switch (*in) {
|
||||
case '\n':
|
||||
case '\r':
|
||||
if (in + 1 < str + len && (in[1] ^ *in) == ('\n' ^ '\r'))
|
||||
in++;
|
||||
if (!triple)
|
||||
log_file_warning(*loc.begin.filename, loc.begin.line, "Multi-line string literals should be triple-quoted or escaped.\n");
|
||||
*out++ = '\n';
|
||||
break;
|
||||
case '\\':
|
||||
in++;
|
||||
log_assert(in < str + len);
|
||||
switch (*in) {
|
||||
case 'a':
|
||||
*out++ = '\a';
|
||||
break;
|
||||
case 'f':
|
||||
*out++ = '\f';
|
||||
break;
|
||||
case 'n':
|
||||
*out++ = '\n';
|
||||
break;
|
||||
case 'r': /* not part of IEEE-1800 2023, but seems
|
||||
like a good idea to support it anyway */
|
||||
*out++ = '\r';
|
||||
break;
|
||||
case 't':
|
||||
*out++ = '\t';
|
||||
break;
|
||||
case 'v':
|
||||
*out++ = '\v';
|
||||
break;
|
||||
case 'x':
|
||||
int val;
|
||||
if (in + 1 < str + len && is_hex_dig(in[1], &val, loc)) {
|
||||
*out = val;
|
||||
in++;
|
||||
if (in + 1 < str + len && is_hex_dig(in[1], &val, loc)) {
|
||||
*out = *out * 0x10 + val;
|
||||
in++;
|
||||
}
|
||||
out++;
|
||||
} else
|
||||
log_file_warning(*loc.begin.filename, loc.begin.line, "ignoring invalid hex escape.\n");
|
||||
break;
|
||||
case '\\':
|
||||
*out++ = '\\';
|
||||
break;
|
||||
case '"':
|
||||
*out++ = '"';
|
||||
break;
|
||||
case '\n':
|
||||
case '\r':
|
||||
if (in + 1 < str + len && (in[1] ^ *in) == ('\n' ^ '\r'))
|
||||
in++;
|
||||
break;
|
||||
default:
|
||||
if ('0' <= *in && *in <= '7') {
|
||||
int val;
|
||||
|
||||
*out = *in - '0';
|
||||
if (in + 1 < str + len && is_oct_dig(in[1], &val, loc)) {
|
||||
*out = *out * 010 + val;
|
||||
in++;
|
||||
if (in + 1 < str + len && is_oct_dig(in[1], &val, loc)) {
|
||||
if (*out >= 040)
|
||||
log_file_warning(*loc.begin.filename, loc.begin.line, "octal escape exceeds \\377\n");
|
||||
*out = *out * 010 + val;
|
||||
in++;
|
||||
}
|
||||
}
|
||||
out++;
|
||||
} else
|
||||
*out++ = *in;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*out++ = *in;
|
||||
}
|
||||
|
||||
return parser::make_TOK_STRING(std::make_unique<std::string>(str, out - str), loc);
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%option yylineno
|
||||
%option noyywrap
|
||||
%option nounput
|
||||
%option bison-locations
|
||||
%option bison-bridge
|
||||
%option prefix="frontend_verilog_yy"
|
||||
|
||||
%x COMMENT
|
||||
%x STRING
|
||||
%x SYNOPSYS_TRANSLATE_OFF
|
||||
%x SYNOPSYS_FLAGS
|
||||
%x IMPORT_DPI
|
||||
|
|
@ -134,47 +287,46 @@ FIXED_POINT_NUMBER_NO_DEC [0-9][0-9_]*[eE][-+]?[0-9_]+
|
|||
TIME_SCALE_SUFFIX [munpf]?s
|
||||
|
||||
%%
|
||||
|
||||
// Initialise comment_caller to something to avoid a "maybe undefined"
|
||||
// warning from GCC.
|
||||
int comment_caller = INITIAL;
|
||||
|
||||
<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`file_push "[^\n]* {
|
||||
fn_stack.push_back(current_filename);
|
||||
ln_stack.push_back(frontend_verilog_yyget_lineno());
|
||||
current_filename = yytext+11;
|
||||
if (!current_filename.empty() && current_filename.front() == '"')
|
||||
current_filename = current_filename.substr(1);
|
||||
if (!current_filename.empty() && current_filename.back() == '"')
|
||||
current_filename = current_filename.substr(0, current_filename.size()-1);
|
||||
frontend_verilog_yyset_lineno(0);
|
||||
yylloc->first_line = yylloc->last_line = 0;
|
||||
real_location.first_line = real_location.last_line = 0;
|
||||
ln_stack.push_back(yylineno);
|
||||
std::string filename = YYText()+11;
|
||||
if (!filename.empty() && filename.front() == '"')
|
||||
filename = filename.substr(1);
|
||||
if (!filename.empty() && filename.back() == '"')
|
||||
filename = filename.substr(0, filename.size()-1);
|
||||
current_filename = std::make_shared<std::string>(filename);
|
||||
yylineno = (0);
|
||||
out_loc.begin.line = out_loc.end.line = 0;
|
||||
}
|
||||
|
||||
<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`file_pop"[^\n]*\n {
|
||||
current_filename = fn_stack.back();
|
||||
fn_stack.pop_back();
|
||||
frontend_verilog_yyset_lineno(ln_stack.back());
|
||||
yylloc->first_line = yylloc->last_line = ln_stack.back();
|
||||
real_location.first_line = real_location.last_line = ln_stack.back();
|
||||
yylineno = (ln_stack.back());
|
||||
out_loc.begin.line = out_loc.end.line = ln_stack.back();
|
||||
ln_stack.pop_back();
|
||||
}
|
||||
|
||||
<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`line"[ \t]+[^ \t\r\n]+[ \t]+\"[^ \r\n]+\"[^\r\n]*\n {
|
||||
char *p = yytext + 5;
|
||||
const char *p = YYText() + 5;
|
||||
while (*p == ' ' || *p == '\t') p++;
|
||||
frontend_verilog_yyset_lineno(atoi(p));
|
||||
yylloc->first_line = yylloc->last_line = atoi(p);
|
||||
real_location.first_line = real_location.last_line = atoi(p);
|
||||
yylineno = (atoi(p));
|
||||
out_loc.begin.line = out_loc.end.line = atoi(p);
|
||||
while (*p && *p != ' ' && *p != '\t') p++;
|
||||
while (*p == ' ' || *p == '\t') p++;
|
||||
char *q = *p ? p + 1 : p;
|
||||
const char *q = *p ? p + 1 : p;
|
||||
while (*q && *q != '"') q++;
|
||||
current_filename = std::string(p).substr(1, q-p-1);
|
||||
current_filename = std::make_shared<std::string>(std::string(p).substr(1, q-p-1));
|
||||
}
|
||||
|
||||
"`file_notfound "[^\n]* {
|
||||
log_error("Can't open include file `%s'!\n", yytext + 15);
|
||||
log_error("Can't open include file `%s'!\n", YYText() + 15);
|
||||
}
|
||||
|
||||
"`timescale"[ \t]+[^ \t\r\n/]+[ \t]*"/"[ \t]*[^ \t\r\n]* /* ignore timescale directive */
|
||||
|
|
@ -183,260 +335,224 @@ TIME_SCALE_SUFFIX [munpf]?s
|
|||
"`endcelldefine"[^\n]* /* ignore `endcelldefine */
|
||||
|
||||
"`default_nettype"[ \t]+[^ \t\r\n/]+ {
|
||||
char *p = yytext;
|
||||
const char *p = YYText();
|
||||
while (*p != 0 && *p != ' ' && *p != '\t') p++;
|
||||
while (*p == ' ' || *p == '\t') p++;
|
||||
if (!strcmp(p, "none"))
|
||||
VERILOG_FRONTEND::default_nettype_wire = false;
|
||||
extra->default_nettype_wire = false;
|
||||
else if (!strcmp(p, "wire"))
|
||||
VERILOG_FRONTEND::default_nettype_wire = true;
|
||||
extra->default_nettype_wire = true;
|
||||
else
|
||||
frontend_verilog_yyerror("Unsupported default nettype: %s", p);
|
||||
err_at_loc(out_loc, "Unsupported default nettype: %s", p);
|
||||
}
|
||||
|
||||
"`protect"[^\n]* /* ignore `protect*/
|
||||
"`endprotect"[^\n]* /* ignore `endprotect*/
|
||||
|
||||
"`"[a-zA-Z_$][a-zA-Z0-9_$]* {
|
||||
frontend_verilog_yyerror("Unimplemented compiler directive or undefined macro %s.", yytext);
|
||||
err_at_loc(out_loc, "Unimplemented compiler directive or undefined macro %s.", YYText());
|
||||
}
|
||||
|
||||
"module" { return TOK_MODULE; }
|
||||
"endmodule" { return TOK_ENDMODULE; }
|
||||
"function" { return TOK_FUNCTION; }
|
||||
"endfunction" { return TOK_ENDFUNCTION; }
|
||||
"task" { return TOK_TASK; }
|
||||
"endtask" { return TOK_ENDTASK; }
|
||||
"specify" { return specify_mode ? TOK_SPECIFY : TOK_IGNORED_SPECIFY; }
|
||||
"endspecify" { return TOK_ENDSPECIFY; }
|
||||
"specparam" { return TOK_SPECPARAM; }
|
||||
"package" { SV_KEYWORD(TOK_PACKAGE); }
|
||||
"endpackage" { SV_KEYWORD(TOK_ENDPACKAGE); }
|
||||
"interface" { SV_KEYWORD(TOK_INTERFACE); }
|
||||
"endinterface" { SV_KEYWORD(TOK_ENDINTERFACE); }
|
||||
"modport" { SV_KEYWORD(TOK_MODPORT); }
|
||||
"parameter" { return TOK_PARAMETER; }
|
||||
"localparam" { return TOK_LOCALPARAM; }
|
||||
"defparam" { return TOK_DEFPARAM; }
|
||||
"assign" { return TOK_ASSIGN; }
|
||||
"always" { return TOK_ALWAYS; }
|
||||
"initial" { return TOK_INITIAL; }
|
||||
"begin" { return TOK_BEGIN; }
|
||||
"end" { return TOK_END; }
|
||||
"if" { return TOK_IF; }
|
||||
"else" { return TOK_ELSE; }
|
||||
"for" { return TOK_FOR; }
|
||||
"posedge" { return TOK_POSEDGE; }
|
||||
"negedge" { return TOK_NEGEDGE; }
|
||||
"or" { return TOK_OR; }
|
||||
"case" { return TOK_CASE; }
|
||||
"casex" { return TOK_CASEX; }
|
||||
"casez" { return TOK_CASEZ; }
|
||||
"endcase" { return TOK_ENDCASE; }
|
||||
"default" { return TOK_DEFAULT; }
|
||||
"generate" { return TOK_GENERATE; }
|
||||
"endgenerate" { return TOK_ENDGENERATE; }
|
||||
"while" { return TOK_WHILE; }
|
||||
"repeat" { return TOK_REPEAT; }
|
||||
"automatic" { return TOK_AUTOMATIC; }
|
||||
"module" { return parser::make_TOK_MODULE(out_loc); }
|
||||
"endmodule" { return parser::make_TOK_ENDMODULE(out_loc); }
|
||||
"function" { return parser::make_TOK_FUNCTION(out_loc); }
|
||||
"endfunction" { return parser::make_TOK_ENDFUNCTION(out_loc); }
|
||||
"task" { return parser::make_TOK_TASK(out_loc); }
|
||||
"endtask" { return parser::make_TOK_ENDTASK(out_loc); }
|
||||
"specify" { return mode->specify ? parser::make_TOK_SPECIFY(out_loc) : parser::make_TOK_IGNORED_SPECIFY(out_loc); }
|
||||
"endspecify" { return parser::make_TOK_ENDSPECIFY(out_loc); }
|
||||
"specparam" { return parser::make_TOK_SPECPARAM(out_loc); }
|
||||
"package" { SV_KEYWORD(parser::make_TOK_PACKAGE(out_loc)); }
|
||||
"endpackage" { SV_KEYWORD(parser::make_TOK_ENDPACKAGE(out_loc)); }
|
||||
"import" { SV_KEYWORD(parser::make_TOK_IMPORT(out_loc)); }
|
||||
"interface" { SV_KEYWORD(parser::make_TOK_INTERFACE(out_loc)); }
|
||||
"endinterface" { SV_KEYWORD(parser::make_TOK_ENDINTERFACE(out_loc)); }
|
||||
"modport" { SV_KEYWORD(parser::make_TOK_MODPORT(out_loc)); }
|
||||
"parameter" { return parser::make_TOK_PARAMETER(out_loc); }
|
||||
"localparam" { return parser::make_TOK_LOCALPARAM(out_loc); }
|
||||
"defparam" { return parser::make_TOK_DEFPARAM(out_loc); }
|
||||
"assign" { return parser::make_TOK_ASSIGN(out_loc); }
|
||||
"always" { return parser::make_TOK_ALWAYS(out_loc); }
|
||||
"initial" { return parser::make_TOK_INITIAL(out_loc); }
|
||||
"begin" { return parser::make_TOK_BEGIN(out_loc); }
|
||||
"end" { return parser::make_TOK_END(out_loc); }
|
||||
"if" { return parser::make_TOK_IF(out_loc); }
|
||||
"ifnone" { return parser::make_TOK_IFNONE(out_loc); }
|
||||
"else" { return parser::make_TOK_ELSE(out_loc); }
|
||||
"for" { return parser::make_TOK_FOR(out_loc); }
|
||||
"posedge" { return parser::make_TOK_POSEDGE(out_loc); }
|
||||
"negedge" { return parser::make_TOK_NEGEDGE(out_loc); }
|
||||
"or" { return parser::make_TOK_OR(out_loc); }
|
||||
"case" { return parser::make_TOK_CASE(out_loc); }
|
||||
"casex" { return parser::make_TOK_CASEX(out_loc); }
|
||||
"casez" { return parser::make_TOK_CASEZ(out_loc); }
|
||||
"endcase" { return parser::make_TOK_ENDCASE(out_loc); }
|
||||
"default" { return parser::make_TOK_DEFAULT(out_loc); }
|
||||
"generate" { return parser::make_TOK_GENERATE(out_loc); }
|
||||
"endgenerate" { return parser::make_TOK_ENDGENERATE(out_loc); }
|
||||
"while" { return parser::make_TOK_WHILE(out_loc); }
|
||||
"repeat" { return parser::make_TOK_REPEAT(out_loc); }
|
||||
"automatic" { return parser::make_TOK_AUTOMATIC(out_loc); }
|
||||
|
||||
"unique" { SV_KEYWORD(TOK_UNIQUE); }
|
||||
"unique0" { SV_KEYWORD(TOK_UNIQUE0); }
|
||||
"priority" { SV_KEYWORD(TOK_PRIORITY); }
|
||||
"unique" { SV_KEYWORD(parser::make_TOK_UNIQUE(out_loc)); }
|
||||
"unique0" { SV_KEYWORD(parser::make_TOK_UNIQUE0(out_loc)); }
|
||||
"priority" { SV_KEYWORD(parser::make_TOK_PRIORITY(out_loc)); }
|
||||
|
||||
"always_comb" { SV_KEYWORD(TOK_ALWAYS_COMB); }
|
||||
"always_ff" { SV_KEYWORD(TOK_ALWAYS_FF); }
|
||||
"always_latch" { SV_KEYWORD(TOK_ALWAYS_LATCH); }
|
||||
"always_comb" { SV_KEYWORD(parser::make_TOK_ALWAYS_COMB(out_loc)); }
|
||||
"always_ff" { SV_KEYWORD(parser::make_TOK_ALWAYS_FF(out_loc)); }
|
||||
"always_latch" { SV_KEYWORD(parser::make_TOK_ALWAYS_LATCH(out_loc)); }
|
||||
|
||||
/* use special token for labels on assert, assume, cover, and restrict because it's insanley complex
|
||||
to fix parsing of cells otherwise. (the current cell parser forces a reduce very early to update some
|
||||
global state.. its a mess) */
|
||||
[a-zA-Z_$][a-zA-Z0-9_$]*/[ \t\r\n]*:[ \t\r\n]*(assert|assume|cover|restrict)[^a-zA-Z0-9_$\.] {
|
||||
if (!strcmp(yytext, "default"))
|
||||
return TOK_DEFAULT;
|
||||
yylval->string = new std::string(std::string("\\") + yytext);
|
||||
return TOK_SVA_LABEL;
|
||||
if (!strcmp(YYText(), "default"))
|
||||
return parser::make_TOK_DEFAULT(out_loc);
|
||||
string_t val = std::make_unique<std::string>(std::string("\\") + YYText());
|
||||
return parser::make_TOK_SVA_LABEL(std::move(val), out_loc);
|
||||
}
|
||||
|
||||
"assert" { if (formal_mode) return TOK_ASSERT; SV_KEYWORD(TOK_ASSERT); }
|
||||
"assume" { if (formal_mode) return TOK_ASSUME; SV_KEYWORD(TOK_ASSUME); }
|
||||
"cover" { if (formal_mode) return TOK_COVER; SV_KEYWORD(TOK_COVER); }
|
||||
"restrict" { if (formal_mode) return TOK_RESTRICT; SV_KEYWORD(TOK_RESTRICT); }
|
||||
"property" { if (formal_mode) return TOK_PROPERTY; SV_KEYWORD(TOK_PROPERTY); }
|
||||
"rand" { if (formal_mode) return TOK_RAND; SV_KEYWORD(TOK_RAND); }
|
||||
"const" { if (formal_mode) return TOK_CONST; SV_KEYWORD(TOK_CONST); }
|
||||
"checker" { if (formal_mode) return TOK_CHECKER; SV_KEYWORD(TOK_CHECKER); }
|
||||
"endchecker" { if (formal_mode) return TOK_ENDCHECKER; SV_KEYWORD(TOK_ENDCHECKER); }
|
||||
"bind" { if (formal_mode) return TOK_BIND; SV_KEYWORD(TOK_BIND); }
|
||||
"final" { SV_KEYWORD(TOK_FINAL); }
|
||||
"logic" { SV_KEYWORD(TOK_LOGIC); }
|
||||
"var" { SV_KEYWORD(TOK_VAR); }
|
||||
"bit" { SV_KEYWORD(TOK_LOGIC); }
|
||||
"int" { SV_KEYWORD(TOK_INT); }
|
||||
"byte" { SV_KEYWORD(TOK_BYTE); }
|
||||
"shortint" { SV_KEYWORD(TOK_SHORTINT); }
|
||||
"longint" { SV_KEYWORD(TOK_LONGINT); }
|
||||
"assert" { if (mode->formal) return parser::make_TOK_ASSERT(out_loc); SV_KEYWORD(parser::make_TOK_ASSERT(out_loc)); }
|
||||
"assume" { if (mode->formal) return parser::make_TOK_ASSUME(out_loc); SV_KEYWORD(parser::make_TOK_ASSUME(out_loc)); }
|
||||
"cover" { if (mode->formal) return parser::make_TOK_COVER(out_loc); SV_KEYWORD(parser::make_TOK_COVER(out_loc)); }
|
||||
"restrict" { if (mode->formal) return parser::make_TOK_RESTRICT(out_loc); SV_KEYWORD(parser::make_TOK_RESTRICT(out_loc)); }
|
||||
"property" { if (mode->formal) return parser::make_TOK_PROPERTY(out_loc); SV_KEYWORD(parser::make_TOK_PROPERTY(out_loc)); }
|
||||
"rand" { if (mode->formal) return parser::make_TOK_RAND(out_loc); SV_KEYWORD(parser::make_TOK_RAND(out_loc)); }
|
||||
"const" { if (mode->formal) return parser::make_TOK_CONST(out_loc); SV_KEYWORD(parser::make_TOK_CONST(out_loc)); }
|
||||
"checker" { if (mode->formal) return parser::make_TOK_CHECKER(out_loc); SV_KEYWORD(parser::make_TOK_CHECKER(out_loc)); }
|
||||
"endchecker" { if (mode->formal) return parser::make_TOK_ENDCHECKER(out_loc); SV_KEYWORD(parser::make_TOK_ENDCHECKER(out_loc)); }
|
||||
"bind" { if (mode->formal) return parser::make_TOK_BIND(out_loc); SV_KEYWORD(parser::make_TOK_BIND(out_loc)); }
|
||||
"final" { SV_KEYWORD(parser::make_TOK_FINAL(out_loc)); }
|
||||
"logic" { SV_KEYWORD(parser::make_TOK_LOGIC(out_loc)); }
|
||||
"var" { SV_KEYWORD(parser::make_TOK_VAR(out_loc)); }
|
||||
"bit" { SV_KEYWORD(parser::make_TOK_LOGIC(out_loc)); }
|
||||
"int" { SV_KEYWORD(parser::make_TOK_INT(out_loc)); }
|
||||
"byte" { SV_KEYWORD(parser::make_TOK_BYTE(out_loc)); }
|
||||
"shortint" { SV_KEYWORD(parser::make_TOK_SHORTINT(out_loc)); }
|
||||
"longint" { SV_KEYWORD(parser::make_TOK_LONGINT(out_loc)); }
|
||||
"void" { SV_KEYWORD(parser::make_TOK_VOID(out_loc)); }
|
||||
|
||||
"eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); }
|
||||
"s_eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); }
|
||||
"eventually" { if (mode->formal) return parser::make_TOK_EVENTUALLY(out_loc); SV_KEYWORD(parser::make_TOK_EVENTUALLY(out_loc)); }
|
||||
"s_eventually" { if (mode->formal) return parser::make_TOK_EVENTUALLY(out_loc); SV_KEYWORD(parser::make_TOK_EVENTUALLY(out_loc)); }
|
||||
|
||||
"input" { return TOK_INPUT; }
|
||||
"output" { return TOK_OUTPUT; }
|
||||
"inout" { return TOK_INOUT; }
|
||||
"wire" { return TOK_WIRE; }
|
||||
"tri" { return TOK_WIRE; }
|
||||
"wor" { return TOK_WOR; }
|
||||
"trior" { return TOK_WOR; }
|
||||
"wand" { return TOK_WAND; }
|
||||
"triand" { return TOK_WAND; }
|
||||
"reg" { return TOK_REG; }
|
||||
"integer" { return TOK_INTEGER; }
|
||||
"signed" { return TOK_SIGNED; }
|
||||
"unsigned" { SV_KEYWORD(TOK_UNSIGNED); }
|
||||
"genvar" { return TOK_GENVAR; }
|
||||
"real" { return TOK_REAL; }
|
||||
"input" { return parser::make_TOK_INPUT(out_loc); }
|
||||
"output" { return parser::make_TOK_OUTPUT(out_loc); }
|
||||
"inout" { return parser::make_TOK_INOUT(out_loc); }
|
||||
"wire" { return parser::make_TOK_WIRE(out_loc); }
|
||||
"tri" { return parser::make_TOK_WIRE(out_loc); }
|
||||
"wor" { return parser::make_TOK_WOR(out_loc); }
|
||||
"trior" { return parser::make_TOK_WOR(out_loc); }
|
||||
"wand" { return parser::make_TOK_WAND(out_loc); }
|
||||
"triand" { return parser::make_TOK_WAND(out_loc); }
|
||||
"reg" { return parser::make_TOK_REG(out_loc); }
|
||||
"integer" { return parser::make_TOK_INTEGER(out_loc); }
|
||||
"signed" { return parser::make_TOK_SIGNED(out_loc); }
|
||||
"unsigned" { SV_KEYWORD(parser::make_TOK_UNSIGNED(out_loc)); }
|
||||
"genvar" { return parser::make_TOK_GENVAR(out_loc); }
|
||||
"real" { return parser::make_TOK_REAL(out_loc); }
|
||||
|
||||
"enum" { SV_KEYWORD(TOK_ENUM); }
|
||||
"typedef" { SV_KEYWORD(TOK_TYPEDEF); }
|
||||
"struct" { SV_KEYWORD(TOK_STRUCT); }
|
||||
"union" { SV_KEYWORD(TOK_UNION); }
|
||||
"packed" { SV_KEYWORD(TOK_PACKED); }
|
||||
"enum" { SV_KEYWORD(parser::make_TOK_ENUM(out_loc)); }
|
||||
"typedef" { SV_KEYWORD(parser::make_TOK_TYPEDEF(out_loc)); }
|
||||
"struct" { SV_KEYWORD(parser::make_TOK_STRUCT(out_loc)); }
|
||||
"union" { SV_KEYWORD(parser::make_TOK_UNION(out_loc)); }
|
||||
"packed" { SV_KEYWORD(parser::make_TOK_PACKED(out_loc)); }
|
||||
|
||||
{UNSIGNED_NUMBER} {
|
||||
yylval->string = new std::string(yytext);
|
||||
return TOK_CONSTVAL;
|
||||
string_t val = std::make_unique<std::string>(YYText());
|
||||
return parser::make_TOK_CONSTVAL(std::move(val), out_loc);
|
||||
}
|
||||
|
||||
\'[01zxZX] {
|
||||
yylval->string = new std::string(yytext);
|
||||
return TOK_UNBASED_UNSIZED_CONSTVAL;
|
||||
string_t val = std::make_unique<std::string>(YYText());
|
||||
return parser::make_TOK_UNBASED_UNSIZED_CONSTVAL(std::move(val), out_loc);
|
||||
}
|
||||
|
||||
\'[sS]?[bodhBODH] {
|
||||
BEGIN(BASED_CONST);
|
||||
yylval->string = new std::string(yytext);
|
||||
return TOK_BASE;
|
||||
string_t val = std::make_unique<std::string>(YYText());
|
||||
return parser::make_TOK_BASE(std::move(val), out_loc);
|
||||
}
|
||||
|
||||
<BASED_CONST>[0-9a-fA-FzxZX?][0-9a-fA-FzxZX?_]* {
|
||||
BEGIN(0);
|
||||
yylval->string = new std::string(yytext);
|
||||
return TOK_BASED_CONSTVAL;
|
||||
string_t val = std::make_unique<std::string>(YYText());
|
||||
return parser::make_TOK_BASED_CONSTVAL(std::move(val), out_loc);
|
||||
}
|
||||
|
||||
{FIXED_POINT_NUMBER_DEC} {
|
||||
yylval->string = new std::string(yytext);
|
||||
return TOK_REALVAL;
|
||||
string_t val = std::make_unique<std::string>(YYText());
|
||||
return parser::make_TOK_REALVAL(std::move(val), out_loc);
|
||||
}
|
||||
|
||||
{FIXED_POINT_NUMBER_NO_DEC} {
|
||||
yylval->string = new std::string(yytext);
|
||||
return TOK_REALVAL;
|
||||
string_t val = std::make_unique<std::string>(YYText());
|
||||
return parser::make_TOK_REALVAL(std::move(val), out_loc);
|
||||
}
|
||||
|
||||
\" { BEGIN(STRING); }
|
||||
<STRING>\\. { yymore(); real_location = old_location; }
|
||||
<STRING>\" {
|
||||
BEGIN(0);
|
||||
char *yystr = strdup(yytext);
|
||||
yystr[strlen(yytext) - 1] = 0;
|
||||
int i = 0, j = 0;
|
||||
while (yystr[i]) {
|
||||
if (yystr[i] == '\\' && yystr[i + 1]) {
|
||||
i++;
|
||||
if (yystr[i] == 'a')
|
||||
yystr[i] = '\a';
|
||||
else if (yystr[i] == 'f')
|
||||
yystr[i] = '\f';
|
||||
else if (yystr[i] == 'n')
|
||||
yystr[i] = '\n';
|
||||
else if (yystr[i] == 'r')
|
||||
yystr[i] = '\r';
|
||||
else if (yystr[i] == 't')
|
||||
yystr[i] = '\t';
|
||||
else if (yystr[i] == 'v')
|
||||
yystr[i] = '\v';
|
||||
else if ('0' <= yystr[i] && yystr[i] <= '7') {
|
||||
yystr[i] = yystr[i] - '0';
|
||||
if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
|
||||
yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
|
||||
i++;
|
||||
}
|
||||
if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
|
||||
yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
yystr[j++] = yystr[i++];
|
||||
}
|
||||
yystr[j] = 0;
|
||||
yylval->string = new std::string(yystr, j);
|
||||
free(yystr);
|
||||
return TOK_STRING;
|
||||
}
|
||||
<STRING>. { yymore(); real_location = old_location; }
|
||||
\"([^\\"]|\\.|\\\n)*\" { return process_str(yytext + 1, yyleng - 2, false, out_loc); }
|
||||
|
||||
and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1 {
|
||||
yylval->string = new std::string(yytext);
|
||||
return TOK_PRIMITIVE;
|
||||
\"{3}(\"{0,2}([^\\"]|\\.|\\\n))*\"{3} { return process_str(yytext + 3, yyleng - 6, true, out_loc); }
|
||||
|
||||
and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1|tran {
|
||||
auto val = std::make_unique<std::string>(YYText());
|
||||
return parser::make_TOK_PRIMITIVE(std::move(val), out_loc);
|
||||
}
|
||||
|
||||
supply0 { return TOK_SUPPLY0; }
|
||||
supply1 { return TOK_SUPPLY1; }
|
||||
supply0 { return parser::make_TOK_SUPPLY0(out_loc); }
|
||||
supply1 { return parser::make_TOK_SUPPLY1(out_loc); }
|
||||
|
||||
"$"(display|write|strobe|monitor|time|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) {
|
||||
yylval->string = new std::string(yytext);
|
||||
return TOK_ID;
|
||||
"$"(display[bho]?|write[bho]?|strobe|monitor|time|realtime|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) {
|
||||
auto val = std::make_unique<std::string>(YYText());
|
||||
return parser::make_TOK_ID(std::move(val), out_loc);
|
||||
}
|
||||
|
||||
"$"(setup|hold|setuphold|removal|recovery|recrem|skew|timeskew|fullskew|nochange) {
|
||||
if (!specify_mode) REJECT;
|
||||
yylval->string = new std::string(yytext);
|
||||
return TOK_ID;
|
||||
if (!mode->specify) REJECT;
|
||||
auto val = std::make_unique<std::string>(YYText());
|
||||
return parser::make_TOK_ID(std::move(val), out_loc);
|
||||
}
|
||||
|
||||
"$"(info|warning|error|fatal) {
|
||||
yylval->string = new std::string(yytext);
|
||||
return TOK_MSG_TASKS;
|
||||
auto val = std::make_unique<std::string>(YYText());
|
||||
return parser::make_TOK_MSG_TASKS(std::move(val), out_loc);
|
||||
}
|
||||
|
||||
"$signed" { return TOK_TO_SIGNED; }
|
||||
"$unsigned" { return TOK_TO_UNSIGNED; }
|
||||
"$signed" { return parser::make_TOK_TO_SIGNED(out_loc); }
|
||||
"$unsigned" { return parser::make_TOK_TO_UNSIGNED(out_loc); }
|
||||
|
||||
[a-zA-Z_][a-zA-Z0-9_]*::[a-zA-Z_$][a-zA-Z0-9_$]* {
|
||||
// package qualifier
|
||||
auto s = std::string("\\") + yytext;
|
||||
if (pkg_user_types.count(s) > 0) {
|
||||
auto s = std::string("\\") + YYText();
|
||||
if (extra->pkg_user_types.count(s) > 0) {
|
||||
// package qualified typedefed name
|
||||
yylval->string = new std::string(s);
|
||||
return TOK_PKG_USER_TYPE;
|
||||
auto val = std::make_unique<std::string>(s);
|
||||
return parser::make_TOK_PKG_USER_TYPE(std::move(val), out_loc);
|
||||
}
|
||||
else {
|
||||
// backup before :: just return first part
|
||||
size_t len = strchr(yytext, ':') - yytext;
|
||||
size_t len = strchr(YYText(), ':') - YYText();
|
||||
yyless(len);
|
||||
yylval->string = new std::string(std::string("\\") + yytext);
|
||||
return TOK_ID;
|
||||
auto val = std::make_unique<std::string>(std::string("\\") + YYText());
|
||||
return parser::make_TOK_ID(std::move(val), out_loc);
|
||||
}
|
||||
}
|
||||
|
||||
[a-zA-Z_$][a-zA-Z0-9_$]* {
|
||||
auto s = std::string("\\") + yytext;
|
||||
if (isUserType(s)) {
|
||||
auto s = std::string("\\") + YYText();
|
||||
if (isUserType(extra, s)) {
|
||||
// previously typedefed name
|
||||
yylval->string = new std::string(s);
|
||||
return TOK_USER_TYPE;
|
||||
auto val = std::make_unique<std::string>(s);
|
||||
return parser::make_TOK_USER_TYPE(std::move(val), out_loc);
|
||||
}
|
||||
else {
|
||||
yylval->string = new std::string(std::string("\\") + yytext);
|
||||
return TOK_ID;
|
||||
auto val = std::make_unique<std::string>(std::string("\\") + YYText());
|
||||
return parser::make_TOK_ID(std::move(val), out_loc);
|
||||
}
|
||||
}
|
||||
|
||||
[a-zA-Z_$][a-zA-Z0-9_$\.]* {
|
||||
yylval->string = new std::string(std::string("\\") + yytext);
|
||||
return TOK_ID;
|
||||
auto val = std::make_unique<std::string>(std::string("\\") + YYText());
|
||||
return parser::make_TOK_ID(std::move(val), out_loc);
|
||||
}
|
||||
|
||||
"/*"[ \t]*(synopsys|synthesis)[ \t]*translate_off[ \t]*"*/" {
|
||||
|
|
@ -472,7 +588,7 @@ supply1 { return TOK_SUPPLY1; }
|
|||
);
|
||||
printed_warning = true;
|
||||
}
|
||||
return TOK_SYNOPSYS_FULL_CASE;
|
||||
return parser::make_TOK_SYNOPSYS_FULL_CASE(out_loc);
|
||||
}
|
||||
<SYNOPSYS_FLAGS>parallel_case {
|
||||
static bool printed_warning = false;
|
||||
|
|
@ -486,119 +602,115 @@ supply1 { return TOK_SUPPLY1; }
|
|||
);
|
||||
printed_warning = true;
|
||||
}
|
||||
return TOK_SYNOPSYS_PARALLEL_CASE;
|
||||
return parser::make_TOK_SYNOPSYS_PARALLEL_CASE(out_loc);
|
||||
}
|
||||
<SYNOPSYS_FLAGS>. /* ignore everything else */
|
||||
<SYNOPSYS_FLAGS>"*/" { BEGIN(0); }
|
||||
|
||||
import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ {
|
||||
BEGIN(IMPORT_DPI);
|
||||
return TOK_DPI_FUNCTION;
|
||||
return parser::make_TOK_DPI_FUNCTION(out_loc);
|
||||
}
|
||||
|
||||
<IMPORT_DPI>[a-zA-Z_$][a-zA-Z0-9_$]* {
|
||||
yylval->string = new std::string(std::string("\\") + yytext);
|
||||
return TOK_ID;
|
||||
auto val = std::make_unique<std::string>(std::string("\\") + YYText());
|
||||
return parser::make_TOK_ID(std::move(val), out_loc);
|
||||
}
|
||||
|
||||
<IMPORT_DPI>[ \t\r\n] /* ignore whitespaces */
|
||||
|
||||
<IMPORT_DPI>";" {
|
||||
BEGIN(0);
|
||||
return *yytext;
|
||||
return char_tok(*YYText(), out_loc);
|
||||
}
|
||||
|
||||
<IMPORT_DPI>. {
|
||||
return *yytext;
|
||||
return char_tok(*YYText(), out_loc);
|
||||
}
|
||||
|
||||
"\\"[^ \t\r\n]+ {
|
||||
yylval->string = new std::string(yytext);
|
||||
return TOK_ID;
|
||||
auto val = std::make_unique<std::string>(YYText());
|
||||
return parser::make_TOK_ID(std::move(val), out_loc);
|
||||
}
|
||||
|
||||
"(*" { return ATTR_BEGIN; }
|
||||
"*)" { return ATTR_END; }
|
||||
"(*" { return parser::make_ATTR_BEGIN(out_loc); }
|
||||
"*)" { return parser::make_ATTR_END(out_loc); }
|
||||
|
||||
"{*" { return DEFATTR_BEGIN; }
|
||||
"*}" { return DEFATTR_END; }
|
||||
"{*" { return parser::make_DEFATTR_BEGIN(out_loc); }
|
||||
"*}" { return parser::make_DEFATTR_END(out_loc); }
|
||||
|
||||
"**" { return OP_POW; }
|
||||
"||" { return OP_LOR; }
|
||||
"&&" { return OP_LAND; }
|
||||
"==" { return OP_EQ; }
|
||||
"!=" { return OP_NE; }
|
||||
"<=" { return OP_LE; }
|
||||
">=" { return OP_GE; }
|
||||
"**" { return parser::make_OP_POW(out_loc); }
|
||||
"||" { return parser::make_OP_LOR(out_loc); }
|
||||
"&&" { return parser::make_OP_LAND(out_loc); }
|
||||
"==" { return parser::make_OP_EQ(out_loc); }
|
||||
"!=" { return parser::make_OP_NE(out_loc); }
|
||||
"<=" { return parser::make_OP_LE(out_loc); }
|
||||
">=" { return parser::make_OP_GE(out_loc); }
|
||||
|
||||
"===" { return OP_EQX; }
|
||||
"!==" { return OP_NEX; }
|
||||
"===" { return parser::make_OP_EQX(out_loc); }
|
||||
"!==" { return parser::make_OP_NEX(out_loc); }
|
||||
|
||||
"~&" { return OP_NAND; }
|
||||
"~|" { return OP_NOR; }
|
||||
"~^" { return OP_XNOR; }
|
||||
"^~" { return OP_XNOR; }
|
||||
"~&" { return parser::make_OP_NAND(out_loc); }
|
||||
"~|" { return parser::make_OP_NOR(out_loc); }
|
||||
"~^" { return parser::make_OP_XNOR(out_loc); }
|
||||
"^~" { return parser::make_OP_XNOR(out_loc); }
|
||||
|
||||
"<<" { return OP_SHL; }
|
||||
">>" { return OP_SHR; }
|
||||
"<<<" { return OP_SSHL; }
|
||||
">>>" { return OP_SSHR; }
|
||||
"<<" { return parser::make_OP_SHL(out_loc); }
|
||||
">>" { return parser::make_OP_SHR(out_loc); }
|
||||
"<<<" { return parser::make_OP_SSHL(out_loc); }
|
||||
">>>" { return parser::make_OP_SSHR(out_loc); }
|
||||
|
||||
"'" { return OP_CAST; }
|
||||
"'" { return parser::make_OP_CAST(out_loc); }
|
||||
|
||||
"::" { return TOK_PACKAGESEP; }
|
||||
"++" { return TOK_INCREMENT; }
|
||||
"--" { return TOK_DECREMENT; }
|
||||
"::" { return parser::make_TOK_PACKAGESEP(out_loc); }
|
||||
"++" { return parser::make_TOK_INCREMENT(out_loc); }
|
||||
"--" { return parser::make_TOK_DECREMENT(out_loc); }
|
||||
|
||||
"+:" { return TOK_POS_INDEXED; }
|
||||
"-:" { return TOK_NEG_INDEXED; }
|
||||
"+:" { return parser::make_TOK_POS_INDEXED(out_loc); }
|
||||
"-:" { return parser::make_TOK_NEG_INDEXED(out_loc); }
|
||||
|
||||
".*" { return TOK_WILDCARD_CONNECT; }
|
||||
".*" { return parser::make_TOK_WILDCARD_CONNECT(out_loc); }
|
||||
|
||||
"|=" { SV_KEYWORD(TOK_BIT_OR_ASSIGN); }
|
||||
"&=" { SV_KEYWORD(TOK_BIT_AND_ASSIGN); }
|
||||
"+=" { SV_KEYWORD(TOK_ADD_ASSIGN); }
|
||||
"-=" { SV_KEYWORD(TOK_SUB_ASSIGN); }
|
||||
"^=" { SV_KEYWORD(TOK_BIT_XOR_ASSIGN); }
|
||||
"/=" { SV_KEYWORD(TOK_DIV_ASSIGN); }
|
||||
"%=" { SV_KEYWORD(TOK_MOD_ASSIGN); }
|
||||
"*=" { SV_KEYWORD(TOK_MUL_ASSIGN); }
|
||||
"<<=" { SV_KEYWORD(TOK_SHL_ASSIGN); }
|
||||
">>=" { SV_KEYWORD(TOK_SHR_ASSIGN); }
|
||||
"<<<=" { SV_KEYWORD(TOK_SSHL_ASSIGN); }
|
||||
">>>=" { SV_KEYWORD(TOK_SSHR_ASSIGN); }
|
||||
"|=" { SV_KEYWORD(parser::make_TOK_BIT_OR_ASSIGN(out_loc)); }
|
||||
"&=" { SV_KEYWORD(parser::make_TOK_BIT_AND_ASSIGN(out_loc)); }
|
||||
"+=" { SV_KEYWORD(parser::make_TOK_ADD_ASSIGN(out_loc)); }
|
||||
"-=" { SV_KEYWORD(parser::make_TOK_SUB_ASSIGN(out_loc)); }
|
||||
"^=" { SV_KEYWORD(parser::make_TOK_BIT_XOR_ASSIGN(out_loc)); }
|
||||
"/=" { SV_KEYWORD(parser::make_TOK_DIV_ASSIGN(out_loc)); }
|
||||
"%=" { SV_KEYWORD(parser::make_TOK_MOD_ASSIGN(out_loc)); }
|
||||
"*=" { SV_KEYWORD(parser::make_TOK_MUL_ASSIGN(out_loc)); }
|
||||
"<<=" { SV_KEYWORD(parser::make_TOK_SHL_ASSIGN(out_loc)); }
|
||||
">>=" { SV_KEYWORD(parser::make_TOK_SHR_ASSIGN(out_loc)); }
|
||||
"<<<=" { SV_KEYWORD(parser::make_TOK_SSHL_ASSIGN(out_loc)); }
|
||||
">>>=" { SV_KEYWORD(parser::make_TOK_SSHR_ASSIGN(out_loc)); }
|
||||
|
||||
[-+]?[=*]> {
|
||||
if (!specify_mode) REJECT;
|
||||
yylval->string = new std::string(yytext);
|
||||
return TOK_SPECIFY_OPER;
|
||||
if (!mode->specify) REJECT;
|
||||
auto val = std::make_unique<std::string>(YYText());
|
||||
return parser::make_TOK_SPECIFY_OPER(std::move(val), out_loc);
|
||||
}
|
||||
|
||||
"&&&" {
|
||||
if (!specify_mode) return TOK_IGNORED_SPECIFY_AND;
|
||||
return TOK_SPECIFY_AND;
|
||||
if (!mode->specify) return parser::make_TOK_IGNORED_SPECIFY_AND(out_loc);
|
||||
return parser::make_TOK_SPECIFY_AND(out_loc);
|
||||
}
|
||||
|
||||
{UNSIGNED_NUMBER}{TIME_SCALE_SUFFIX} { return TOK_TIME_SCALE; }
|
||||
{FIXED_POINT_NUMBER_DEC}{TIME_SCALE_SUFFIX} { return TOK_TIME_SCALE; }
|
||||
{FIXED_POINT_NUMBER_NO_DEC}{TIME_SCALE_SUFFIX} { return TOK_TIME_SCALE; }
|
||||
{UNSIGNED_NUMBER}{TIME_SCALE_SUFFIX} { return parser::make_TOK_TIME_SCALE(out_loc); }
|
||||
{FIXED_POINT_NUMBER_DEC}{TIME_SCALE_SUFFIX} { return parser::make_TOK_TIME_SCALE(out_loc); }
|
||||
{FIXED_POINT_NUMBER_NO_DEC}{TIME_SCALE_SUFFIX} { return parser::make_TOK_TIME_SCALE(out_loc); }
|
||||
|
||||
<INITIAL,BASED_CONST>"/*" { comment_caller=YY_START; BEGIN(COMMENT); }
|
||||
<COMMENT>. /* ignore comment body */
|
||||
<COMMENT>\n /* ignore comment body */
|
||||
<COMMENT>"*/" { BEGIN(comment_caller); }
|
||||
|
||||
|
||||
<INITIAL,BASED_CONST>[ \t\r\n] /* ignore whitespaces */
|
||||
<INITIAL,BASED_CONST>\\[\r\n] /* ignore continuation sequence */
|
||||
<INITIAL,BASED_CONST>"//"[^\r\n]* /* ignore one-line comments */
|
||||
|
||||
<INITIAL>. { return *yytext; }
|
||||
<*>. { BEGIN(0); return *yytext; }
|
||||
<INITIAL>. { return char_tok(*YYText(), out_loc); }
|
||||
<*>. { BEGIN(0); return char_tok(*YYText(), out_loc); }
|
||||
|
||||
%%
|
||||
|
||||
// this is a hack to avoid the 'yyinput defined but not used' error msgs
|
||||
void *frontend_verilog_avoid_input_warnings() {
|
||||
return (void*)&yyinput;
|
||||
}
|
||||
|
||||
|
|
|
|||
97
frontends/verilog/verilog_location.h
Normal file
97
frontends/verilog/verilog_location.h
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#ifndef VERILOG_LOCATION_H
|
||||
#define VERILOG_LOCATION_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
/**
|
||||
* Provide frontend-wide location tracking like what bison generates
|
||||
* but using shared_ptr for filename
|
||||
*/
|
||||
|
||||
struct Position {
|
||||
std::shared_ptr<std::string> filename;
|
||||
int line;
|
||||
int column;
|
||||
|
||||
Position(std::shared_ptr<std::string> filename, int line = 1, int column = 1)
|
||||
: filename(filename), line(line), column(column) {}
|
||||
Position() = default;
|
||||
Position(const Position& other) = default;
|
||||
Position& operator=(const Position& other) = default;
|
||||
|
||||
void advance() { ++column; }
|
||||
void columns(int count = 1) {
|
||||
column += count;
|
||||
}
|
||||
|
||||
void lines(int count = 1) {
|
||||
line += count;
|
||||
column = 1;
|
||||
}
|
||||
std::string to_string() const {
|
||||
std::ostringstream oss;
|
||||
if (filename && !filename->empty()) {
|
||||
oss << *filename << ":";
|
||||
}
|
||||
oss << line << ":" << column;
|
||||
return oss.str();
|
||||
}
|
||||
};
|
||||
|
||||
struct Location {
|
||||
Position begin;
|
||||
Position end;
|
||||
|
||||
Location() = default;
|
||||
Location(const Position& b, const Position& e)
|
||||
: begin(b), end(e) {}
|
||||
Location(const Location& other) = default;
|
||||
Location& operator=(const Location& other) = default;
|
||||
|
||||
void step() { begin = end; }
|
||||
|
||||
void columns(int count = 1) {
|
||||
end.columns(count);
|
||||
}
|
||||
|
||||
void lines(int count = 1) {
|
||||
end.lines(count);
|
||||
}
|
||||
std::string to_string() const {
|
||||
std::ostringstream oss;
|
||||
bool same_file = (!begin.filename && !end.filename) ||
|
||||
(begin.filename && end.filename &&
|
||||
*begin.filename == *end.filename);
|
||||
|
||||
if (same_file) {
|
||||
if (begin.filename && !begin.filename->empty())
|
||||
oss << *begin.filename << ":";
|
||||
|
||||
if (begin.line == end.line) {
|
||||
if (begin.column == end.column) {
|
||||
oss << begin.line << ":" << begin.column;
|
||||
} else {
|
||||
oss << begin.line << ":" << begin.column
|
||||
<< "-" << end.column;
|
||||
}
|
||||
} else {
|
||||
oss << begin.line << ":" << begin.column
|
||||
<< "-" << end.line << ":" << end.column;
|
||||
}
|
||||
} else {
|
||||
oss << begin.to_string() << "-" << end.to_string();
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
};
|
||||
|
||||
static inline std::ostream& operator<<(std::ostream& os, const Location& loc) {
|
||||
return os << loc.to_string();
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue