3
0
Fork 0
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:
Gus Smith 2026-01-06 09:39:59 -08:00
commit 5b7d436851
1758 changed files with 182731 additions and 54865 deletions

View file

@ -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));
}
}

View file

@ -0,0 +1,2 @@
OBJS += frontends/aiger2/xaiger.o

470
frontends/aiger2/xaiger.cc Normal file
View 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

View file

@ -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> &parameters, bool mayfail) override;
RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, 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> &parameters, AstNode **new_ast_out, bool quiet = false);
std::string derive_common(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, 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>> &parameters);
@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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 &param : 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;

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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(&current_process->root_case.switches);
case_stack.clear();
case_stack.push_back(&current_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;
};

View file

@ -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

View file

@ -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

View file

@ -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> &parameters, std::string top = std::string());
extern std::string verific_import(Design *design, const std::map<std::string,std::string> &parameters, 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);

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 &macro_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;

View file

@ -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

View file

@ -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));
}

View 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

View file

@ -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

View file

@ -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

View 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

View file

@ -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;
}

View 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