mirror of
https://github.com/YosysHQ/yosys
synced 2026-07-19 13:45:48 +00:00
Merge remote-tracking branch 'upstream/main' into silimate
This commit is contained in:
commit
e58125b605
834 changed files with 25281 additions and 8780 deletions
10
frontends/CMakeLists.txt
Normal file
10
frontends/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
add_subdirectory(aiger)
|
||||
add_subdirectory(aiger2)
|
||||
add_subdirectory(ast)
|
||||
add_subdirectory(blif)
|
||||
add_subdirectory(json)
|
||||
add_subdirectory(liberty)
|
||||
add_subdirectory(rpc)
|
||||
add_subdirectory(rtlil)
|
||||
add_subdirectory(verific)
|
||||
add_subdirectory(verilog)
|
||||
6
frontends/aiger/CMakeLists.txt
Normal file
6
frontends/aiger/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
yosys_frontend(aigerparse
|
||||
aigerparse.cc
|
||||
aigerparse.h
|
||||
REQUIRES
|
||||
opt_clean
|
||||
)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
|
||||
OBJS += frontends/aiger/aigerparse.o
|
||||
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "aigerparse.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
|
@ -224,7 +224,7 @@ AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString
|
|||
module = new RTLIL::Module;
|
||||
module->name = module_name;
|
||||
if (design->module(module->name))
|
||||
log_error("Duplicate definition of module %s!\n", log_id(module->name));
|
||||
log_error("Duplicate definition of module %s!\n", module->name.unescape());
|
||||
}
|
||||
|
||||
void AigerReader::parse_aiger()
|
||||
|
|
@ -286,10 +286,15 @@ end_of_header:
|
|||
|
||||
RTLIL::IdString escaped_s = stringf("\\%s", s);
|
||||
RTLIL::Wire* wire;
|
||||
if (c == 'i') wire = inputs[l1];
|
||||
else if (c == 'l') wire = latches[l1];
|
||||
else if (c == 'o') {
|
||||
if (c == 'i') {
|
||||
log_assert(l1 < inputs.size());
|
||||
wire = inputs[l1];
|
||||
} else if (c == 'l') {
|
||||
log_assert(l1 < latches.size());
|
||||
wire = latches[l1];
|
||||
} else if (c == 'o') {
|
||||
wire = module->wire(escaped_s);
|
||||
log_assert(l1 < outputs.size());
|
||||
if (wire) {
|
||||
// Could have been renamed by a latch
|
||||
module->swap_names(wire, outputs[l1]);
|
||||
|
|
@ -297,9 +302,9 @@ end_of_header:
|
|||
goto next;
|
||||
}
|
||||
wire = outputs[l1];
|
||||
}
|
||||
else if (c == 'b') wire = bad_properties[l1];
|
||||
else log_abort();
|
||||
} else if (c == 'b') {
|
||||
wire = bad_properties[l1];
|
||||
} else log_abort();
|
||||
|
||||
module->rename(wire, escaped_s);
|
||||
}
|
||||
|
|
@ -652,6 +657,9 @@ void AigerReader::parse_aiger_binary()
|
|||
unsigned l1, l2, l3;
|
||||
std::string line;
|
||||
|
||||
if (M != I + L + A)
|
||||
log_error("Binary AIGER input is malformed: maximum variable index M is %u, but number of inputs, latches and AND gates adds up to %u.\n", M, I + L + A);
|
||||
|
||||
// Parse inputs
|
||||
int digits = decimal_digits(I);
|
||||
for (unsigned i = 1; i <= I; ++i) {
|
||||
|
|
@ -813,7 +821,7 @@ void AigerReader::post_process()
|
|||
RTLIL::Wire* wire = inputs[variable];
|
||||
log_assert(wire);
|
||||
log_assert(wire->port_input);
|
||||
log_debug("Renaming input %s", log_id(wire));
|
||||
log_debug("Renaming input %s", wire);
|
||||
|
||||
RTLIL::Wire *existing = nullptr;
|
||||
if (index == 0) {
|
||||
|
|
@ -827,7 +835,7 @@ void AigerReader::post_process()
|
|||
wire->port_input = false;
|
||||
module->connect(wire, existing);
|
||||
}
|
||||
log_debug(" -> %s\n", log_id(escaped_s));
|
||||
log_debug(" -> %s\n", escaped_s.unescape());
|
||||
}
|
||||
else {
|
||||
RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s, index);
|
||||
|
|
@ -838,7 +846,7 @@ void AigerReader::post_process()
|
|||
module->connect(wire, existing);
|
||||
wire->port_input = false;
|
||||
}
|
||||
log_debug(" -> %s\n", log_id(indexed_name));
|
||||
log_debug(" -> %s\n", indexed_name.unescape());
|
||||
}
|
||||
|
||||
if (wideports && !existing) {
|
||||
|
|
@ -858,7 +866,7 @@ void AigerReader::post_process()
|
|||
RTLIL::Wire* wire = outputs[variable + co_count];
|
||||
log_assert(wire);
|
||||
log_assert(wire->port_output);
|
||||
log_debug("Renaming output %s", log_id(wire));
|
||||
log_debug("Renaming output %s", wire);
|
||||
|
||||
RTLIL::Wire *existing;
|
||||
if (index == 0) {
|
||||
|
|
@ -874,7 +882,7 @@ void AigerReader::post_process()
|
|||
module->connect(wire, existing);
|
||||
wire = existing;
|
||||
}
|
||||
log_debug(" -> %s\n", log_id(escaped_s));
|
||||
log_debug(" -> %s\n", escaped_s.unescape());
|
||||
}
|
||||
else {
|
||||
RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s, index);
|
||||
|
|
@ -886,7 +894,7 @@ void AigerReader::post_process()
|
|||
existing->port_output = true;
|
||||
module->connect(wire, existing);
|
||||
}
|
||||
log_debug(" -> %s\n", log_id(indexed_name));
|
||||
log_debug(" -> %s\n", indexed_name.unescape());
|
||||
}
|
||||
|
||||
if (wideports && !existing) {
|
||||
|
|
@ -904,7 +912,7 @@ void AigerReader::post_process()
|
|||
else if (type == "box") {
|
||||
RTLIL::Cell* cell = module->cell(stringf("$box%d", variable));
|
||||
if (!cell)
|
||||
log_debug("Box %d (%s) no longer exists.\n", variable, log_id(escaped_s));
|
||||
log_debug("Box %d (%s) no longer exists.\n", variable, escaped_s.unescape());
|
||||
else
|
||||
module->rename(cell, escaped_s);
|
||||
}
|
||||
|
|
|
|||
3
frontends/aiger2/CMakeLists.txt
Normal file
3
frontends/aiger2/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
yosys_frontend(xaiger2
|
||||
xaiger.cc
|
||||
)
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
|
||||
OBJS += frontends/aiger2/xaiger.o
|
||||
|
|
@ -24,7 +24,7 @@ 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() << 16) |
|
||||
((uint32_t) f.get() << 8) | (uint32_t) f.get();
|
||||
}
|
||||
|
||||
|
|
@ -80,13 +80,13 @@ struct Xaiger2Frontend : public Frontend {
|
|||
extra_args(f, filename, args, argidx, true);
|
||||
|
||||
if (map_filename.empty())
|
||||
log_error("A '-map2' argument required\n");
|
||||
log_error("A '-map2' argument is required\n");
|
||||
if (module_name.empty())
|
||||
log_error("A '-module_name' argument required\n");
|
||||
log_error("A '-module_name' argument is required\n");
|
||||
|
||||
Module *module = design->module(module_name);
|
||||
if (!module)
|
||||
log_error("Module '%s' not found\n", log_id(module_name));
|
||||
log_error("Module '%s' not found\n", module_name.unescape());
|
||||
|
||||
std::ifstream map_file;
|
||||
map_file.open(map_filename);
|
||||
|
|
@ -128,10 +128,10 @@ struct Xaiger2Frontend : public Frontend {
|
|||
int woffset;
|
||||
std::string name;
|
||||
if (!(map_file >> pi_idx >> woffset >> name))
|
||||
log_error("Bad map file (1)\n");
|
||||
log_error("Bad map file: couldn't read 'pi' line\n");
|
||||
int lit = (2 * pi_idx) + 2;
|
||||
if (lit < 0 || lit >= (int) bits.size())
|
||||
log_error("Bad map file (2)\n");
|
||||
log_error("Bad map file: primary input literal out of range\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",
|
||||
|
|
@ -141,9 +141,9 @@ struct Xaiger2Frontend : public Frontend {
|
|||
int box_seq;
|
||||
std::string name;
|
||||
if (!(map_file >> box_seq >> name))
|
||||
log_error("Bad map file (20)\n");
|
||||
log_error("Bad map file: couldn't read 'box' line\n");
|
||||
if (box_seq < 0)
|
||||
log_error("Bad map file (21)\n");
|
||||
log_error("Bad map file: box out of range\n");
|
||||
|
||||
Cell *box = module->cell(RTLIL::escape_id(name));
|
||||
if (!box)
|
||||
|
|
@ -158,7 +158,7 @@ struct Xaiger2Frontend : public Frontend {
|
|||
}
|
||||
|
||||
if (!def)
|
||||
log_error("Bad map file (22)\n");
|
||||
log_error("Bad map file: no module found for box type '%s'\n", box->type.unescape());
|
||||
|
||||
if (box_seq >= (int) boxes.size()) {
|
||||
boxes.resize(box_seq + 1);
|
||||
|
|
@ -276,9 +276,9 @@ struct Xaiger2Frontend : public Frontend {
|
|||
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));
|
||||
log_debug("M: Cell %s (out %s, ins", cell.type.unescape(), cell.out.unescape());
|
||||
for (auto in : cell.ins)
|
||||
log_debug(" %s", log_id(in));
|
||||
log_debug(" %s", in.unescape());
|
||||
log_debug(")\n");
|
||||
}
|
||||
|
||||
|
|
@ -403,15 +403,15 @@ struct Xaiger2Frontend : public Frontend {
|
|||
int woffset;
|
||||
std::string name;
|
||||
if (!(map_file >> po_idx >> woffset >> name))
|
||||
log_error("Bad map file (3)\n");
|
||||
log_error("Bad map file: couldn't read 'po' line\n");
|
||||
po_idx += co_counter;
|
||||
if (po_idx < 0 || po_idx >= (int) outputs.size())
|
||||
log_error("Bad map file (4)\n");
|
||||
log_error("Bad map file: primary output index out of range\n");
|
||||
int lit = outputs[po_idx];
|
||||
if (lit < 0 || lit >= (int) bits.size())
|
||||
log_error("Bad map file (5)\n");
|
||||
log_error("Bad map file: primary output literal out of range\n");
|
||||
if (bits[lit] == RTLIL::Sm)
|
||||
log_error("Bad map file (6)\n");
|
||||
log_error("Bad map file: primary output literal is a marker\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",
|
||||
|
|
@ -423,15 +423,15 @@ struct Xaiger2Frontend : public Frontend {
|
|||
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");
|
||||
log_error("Bad map file: couldn't read 'pseudopo' line\n");
|
||||
po_idx += co_counter;
|
||||
if (po_idx < 0 || po_idx >= (int) outputs.size())
|
||||
log_error("Bad map file (8)\n");
|
||||
log_error("Bad map file: pseudo primary output index out of range\n");
|
||||
int lit = outputs[po_idx];
|
||||
if (lit < 0 || lit >= (int) bits.size())
|
||||
log_error("Bad map file (9)\n");
|
||||
log_error("Bad map file: pseudo primary output literal out of range\n");
|
||||
if (bits[lit] == RTLIL::Sm)
|
||||
log_error("Bad map file (10)\n");
|
||||
log_error("Bad map file: pseudo primary output literal is a marker\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",
|
||||
|
|
|
|||
13
frontends/ast/CMakeLists.txt
Normal file
13
frontends/ast/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
yosys_core(ast
|
||||
ast_binding.cc
|
||||
ast_binding.h
|
||||
ast.cc
|
||||
ast.h
|
||||
dpicall.cc
|
||||
genrtlil.cc
|
||||
simplify.cc
|
||||
LIBRARIES
|
||||
$<${YOSYS_ENABLE_LIBFFI}:${Dlfcn_LIBRARIES};PkgConfig::libffi>
|
||||
REQUIRES
|
||||
sha1
|
||||
)
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
|
||||
OBJS += frontends/ast/ast.o
|
||||
OBJS += frontends/ast/simplify.o
|
||||
OBJS += frontends/ast/genrtlil.o
|
||||
OBJS += frontends/ast/dpicall.o
|
||||
OBJS += frontends/ast/ast_binding.o
|
||||
|
||||
|
|
@ -100,6 +100,7 @@ std::string AST::type2str(AstNodeType type)
|
|||
X(AST_CAST_SIZE)
|
||||
X(AST_CONCAT)
|
||||
X(AST_REPLICATE)
|
||||
X(AST_ASSIGN_PATTERN)
|
||||
X(AST_BIT_NOT)
|
||||
X(AST_BIT_AND)
|
||||
X(AST_BIT_OR)
|
||||
|
|
@ -696,6 +697,16 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
|
|||
fprintf(f, "}}");
|
||||
break;
|
||||
|
||||
case AST_ASSIGN_PATTERN:
|
||||
fprintf(f, "'{");
|
||||
for (int i = 0; i < GetSize(children); i++) {
|
||||
if (i != 0)
|
||||
fprintf(f, ", ");
|
||||
children[i]->dumpVlog(f, "");
|
||||
}
|
||||
fprintf(f, "}");
|
||||
break;
|
||||
|
||||
if (0) { case AST_BIT_NOT: txt = "~"; }
|
||||
if (0) { case AST_REDUCE_AND: txt = "&"; }
|
||||
if (0) { case AST_REDUCE_OR: txt = "|"; }
|
||||
|
|
@ -1533,7 +1544,7 @@ void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule
|
|||
for (auto w : intfmodule->wires()){
|
||||
auto loc = module_ast->location;
|
||||
auto wire = std::make_unique<AstNode>(loc, AST_WIRE, std::make_unique<AstNode>(loc, AST_RANGE, AstNode::mkconst_int(loc, w->width -1, true), AstNode::mkconst_int(loc, 0, true)));
|
||||
std::string origname = log_id(w->name);
|
||||
std::string origname = w->name.unescape();
|
||||
std::string newname = intfname + "." + origname;
|
||||
wire->str = newname;
|
||||
if (modport != NULL) {
|
||||
|
|
@ -1573,7 +1584,7 @@ bool AstModule::reprocess_if_necessary(RTLIL::Design *design)
|
|||
continue;
|
||||
if (design->module(modname) || design->module("$abstract" + modname)) {
|
||||
log("Reprocessing module %s because instantiated module %s has become available.\n",
|
||||
log_id(name), log_id(modname));
|
||||
name.unescape(), RTLIL::unescape_id(modname));
|
||||
loadconfig();
|
||||
process_and_replace_module(design, this, ast.get(), NULL);
|
||||
return true;
|
||||
|
|
@ -1595,7 +1606,7 @@ void AstModule::expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdStr
|
|||
RTLIL::Module *intfmodule = intf.second;
|
||||
for (auto w : intfmodule->wires()){
|
||||
auto wire = std::make_unique<AstNode>(loc, AST_WIRE, std::make_unique<AstNode>(loc, AST_RANGE, AstNode::mkconst_int(loc, w->width -1, true), AstNode::mkconst_int(loc, 0, true)));
|
||||
std::string newname = log_id(w->name);
|
||||
std::string newname = w->name.unescape();
|
||||
newname = intfname + "." + newname;
|
||||
wire->str = newname;
|
||||
new_ast->children.push_back(std::move(wire));
|
||||
|
|
@ -1668,7 +1679,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict<RTLIL::IdStr
|
|||
|
||||
bool has_interfaces = false;
|
||||
for(auto &intf : interfaces) {
|
||||
interf_info += log_id(intf.second->name);
|
||||
interf_info += intf.second->name.unescape();
|
||||
has_interfaces = true;
|
||||
}
|
||||
|
||||
|
|
@ -1724,7 +1735,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict<RTLIL::IdStr
|
|||
new_subcell->set_bool_attribute(ID::is_interface);
|
||||
}
|
||||
else {
|
||||
log_error("No port with matching name found (%s) in %s. Stopping\n", log_id(intf.first), modname);
|
||||
log_error("No port with matching name found (%s) in %s. Stopping\n", intf.first, modname);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ namespace AST
|
|||
AST_CAST_SIZE,
|
||||
AST_CONCAT,
|
||||
AST_REPLICATE,
|
||||
AST_ASSIGN_PATTERN,
|
||||
AST_BIT_NOT,
|
||||
AST_BIT_AND,
|
||||
AST_BIT_OR,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include "ast.h"
|
||||
|
||||
#ifdef YOSYS_ENABLE_PLUGINS
|
||||
#ifdef YOSYS_ENABLE_LIBFFI
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <ffi.h>
|
||||
|
|
@ -149,7 +149,7 @@ std::unique_ptr<AST::AstNode> AST::dpi_call(AstSrcLocType loc, const std::string
|
|||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#else /* YOSYS_ENABLE_PLUGINS */
|
||||
#else /* YOSYS_ENABLE_LIBFFI */
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
|
|
@ -160,5 +160,5 @@ std::unique_ptr<AST::AstNode> AST::dpi_call(AstSrcLocType, const std::string&, c
|
|||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif /* YOSYS_ENABLE_PLUGINS */
|
||||
#endif /* YOSYS_ENABLE_LIBFFI */
|
||||
|
||||
|
|
|
|||
|
|
@ -342,6 +342,9 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
// The most recently assigned $print or $check cell \PRIORITY.
|
||||
int last_effect_priority;
|
||||
|
||||
// Track which signals have been assigned in current_case to avoid unnecessary removeSignalFromCaseTree calls
|
||||
pool<RTLIL::SigBit> current_case_assigned_bits;
|
||||
|
||||
ProcessGenerator(std::unique_ptr<AstNode> a, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(std::move(a)), initSyncSignals(initSyncSignalsArg), last_effect_priority(0)
|
||||
{
|
||||
// rewrite lookahead references
|
||||
|
|
@ -418,6 +421,10 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
subst_rvalue_map = subst_lvalue_from.to_sigbit_dict(RTLIL::SigSpec(RTLIL::State::Sx, GetSize(subst_lvalue_from)));
|
||||
} else {
|
||||
addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from);
|
||||
// Track initial assignments
|
||||
for (auto &bit : subst_lvalue_to)
|
||||
if (bit.wire != NULL)
|
||||
current_case_assigned_bits.insert(bit);
|
||||
}
|
||||
|
||||
// process the AST
|
||||
|
|
@ -545,14 +552,42 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
// e.g. when the last statement in the code "a = 23; if (b) a = 42; a = 0;" is processed this
|
||||
// function is called to clean up the first two assignments as they are overwritten by
|
||||
// the third assignment.
|
||||
void removeSignalFromCaseTree(const RTLIL::SigSpec &pattern, RTLIL::CaseRule *cs)
|
||||
void removeSignalFromCaseTree(const pool<RTLIL::SigBit> &pattern_bits, RTLIL::CaseRule *cs)
|
||||
{
|
||||
for (auto it = cs->actions.begin(); it != cs->actions.end(); it++)
|
||||
it->first.remove2(pattern, &it->second);
|
||||
// Optimization: check actions in reverse order and stop early if we've found all pattern bits
|
||||
pool<RTLIL::SigBit> remaining_bits = pattern_bits;
|
||||
|
||||
for (auto it = cs->actions.rbegin(); it != cs->actions.rend(); ++it) {
|
||||
bool has_pattern = false;
|
||||
for (auto &bit : it->first) {
|
||||
if (bit.wire != NULL && remaining_bits.count(bit)) {
|
||||
has_pattern = true;
|
||||
remaining_bits.erase(bit);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_pattern) {
|
||||
it->first.remove2(pattern_bits, &it->second);
|
||||
}
|
||||
|
||||
// Early exit if we've processed all bits in pattern
|
||||
if (remaining_bits.empty())
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto it = cs->switches.begin(); it != cs->switches.end(); it++)
|
||||
for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++)
|
||||
removeSignalFromCaseTree(pattern, *it2);
|
||||
removeSignalFromCaseTree(pattern_bits, *it2);
|
||||
}
|
||||
|
||||
void removeSignalFromCaseTree(const RTLIL::SigSpec &pattern, RTLIL::CaseRule *cs)
|
||||
{
|
||||
pool<RTLIL::SigBit> pattern_bits;
|
||||
pattern_bits.reserve(pattern.size());
|
||||
for (auto &bit : pattern)
|
||||
if (bit.wire != NULL)
|
||||
pattern_bits.insert(bit);
|
||||
removeSignalFromCaseTree(pattern_bits, cs);
|
||||
}
|
||||
|
||||
// add an assignment (aka "action") but split it up in chunks. this way huge assignments
|
||||
|
|
@ -611,7 +646,23 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
subst_rvalue_map.set(unmapped_lvalue[i], rvalue[i]);
|
||||
}
|
||||
|
||||
removeSignalFromCaseTree(lvalue, current_case);
|
||||
// Check if any bits in lvalue have been assigned before in current_case
|
||||
bool has_overlap = false;
|
||||
for (auto &bit : lvalue) {
|
||||
if (bit.wire != NULL && current_case_assigned_bits.count(bit)) {
|
||||
has_overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_overlap)
|
||||
removeSignalFromCaseTree(lvalue, current_case);
|
||||
|
||||
// Track newly assigned bits
|
||||
for (auto &bit : lvalue)
|
||||
if (bit.wire != NULL)
|
||||
current_case_assigned_bits.insert(bit);
|
||||
|
||||
remove_unwanted_lvalue_bits(lvalue, rvalue);
|
||||
current_case->actions.push_back(RTLIL::SigSig(lvalue, rvalue));
|
||||
}
|
||||
|
|
@ -658,9 +709,15 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
|
||||
RTLIL::CaseRule *backup_case = current_case;
|
||||
current_case = new RTLIL::CaseRule;
|
||||
pool<RTLIL::SigBit> backup_assigned_bits = std::move(current_case_assigned_bits);
|
||||
current_case_assigned_bits.clear();
|
||||
set_src_attr(current_case, child.get());
|
||||
last_generated_case = current_case;
|
||||
addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue);
|
||||
// Track temp assignments
|
||||
for (auto &bit : this_case_eq_ltemp)
|
||||
if (bit.wire != NULL)
|
||||
current_case_assigned_bits.insert(bit);
|
||||
for (auto& node : child->children) {
|
||||
if (node->type == AST_DEFAULT)
|
||||
default_case = current_case;
|
||||
|
|
@ -674,6 +731,7 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
else
|
||||
log_assert(current_case->compare.size() == 0);
|
||||
current_case = backup_case;
|
||||
current_case_assigned_bits = std::move(backup_assigned_bits);
|
||||
|
||||
subst_lvalue_map.restore();
|
||||
subst_rvalue_map.restore();
|
||||
|
|
@ -702,8 +760,24 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
subst_rvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]);
|
||||
|
||||
this_case_eq_lvalue.replace(subst_lvalue_map.stdmap());
|
||||
removeSignalFromCaseTree(this_case_eq_lvalue, current_case);
|
||||
|
||||
// Check if any bits in lvalue have been assigned before in current_case
|
||||
bool has_overlap = false;
|
||||
for (auto &bit : this_case_eq_lvalue) {
|
||||
if (bit.wire != NULL && current_case_assigned_bits.count(bit)) {
|
||||
has_overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_overlap)
|
||||
removeSignalFromCaseTree(this_case_eq_lvalue, current_case);
|
||||
|
||||
addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp);
|
||||
// Track newly assigned bits
|
||||
for (auto &bit : this_case_eq_lvalue)
|
||||
if (bit.wire != NULL)
|
||||
current_case_assigned_bits.insert(bit);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -712,7 +786,7 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
break;
|
||||
|
||||
case AST_ASSIGN:
|
||||
ast->input_error("Found continous assignment in always/initial block!\n");
|
||||
ast->input_error("Found continuous assignment in always/initial block!\n");
|
||||
break;
|
||||
|
||||
case AST_PARAMETER:
|
||||
|
|
@ -1126,6 +1200,15 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
sign_hint = false;
|
||||
break;
|
||||
|
||||
case AST_ASSIGN_PATTERN:
|
||||
for (auto& child : children) {
|
||||
sub_width_hint = 0;
|
||||
sub_sign_hint = true;
|
||||
child->detectSignWidthWorker(sub_width_hint, sub_sign_hint);
|
||||
}
|
||||
sign_hint = false;
|
||||
break;
|
||||
|
||||
case AST_NEG:
|
||||
case AST_BIT_NOT:
|
||||
case AST_POS:
|
||||
|
|
@ -1738,6 +1821,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
return sig;
|
||||
}
|
||||
|
||||
case AST_ASSIGN_PATTERN:
|
||||
input_error("Assignment pattern is only supported for whole unpacked array assignments.\n");
|
||||
|
||||
// generate cells for unary operations: $not, $pos, $neg
|
||||
if (0) { case AST_BIT_NOT: type_name = ID($not); }
|
||||
if (0) { case AST_POS: type_name = ID($pos); }
|
||||
|
|
@ -2099,10 +2185,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
const auto* value = child->children[0].get();
|
||||
if (value->type == AST_REALVALUE)
|
||||
log_file_warning(*location.begin.filename, location.begin.line, "Replacing floating point parameter %s.%s = %f with string.\n",
|
||||
log_id(cell), log_id(paraname), value->realvalue);
|
||||
cell, paraname.unescape(), value->realvalue);
|
||||
else if (value->type != AST_CONSTANT)
|
||||
input_error("Parameter %s.%s with non-constant value!\n",
|
||||
log_id(cell), log_id(paraname));
|
||||
cell, paraname.unescape());
|
||||
cell->parameters[paraname] = value->asParaConst();
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,11 @@ void AstNode::fixup_hierarchy_flags(bool force_descend)
|
|||
children[0]->set_in_param_flag(true, force_descend);
|
||||
break;
|
||||
|
||||
case AST_ASSIGN_PATTERN:
|
||||
for (auto& child : children)
|
||||
child->set_in_param_flag(in_param, force_descend);
|
||||
break;
|
||||
|
||||
case AST_GENFOR:
|
||||
case AST_FOR:
|
||||
for (auto& child : children) {
|
||||
|
|
@ -269,6 +274,100 @@ static int add_dimension(AstNode *node, AstNode *rnode)
|
|||
node->input_error("Unpacked array in packed struct/union member %s\n", node->str);
|
||||
}
|
||||
|
||||
// Check if node is an unexpanded array reference (AST_IDENTIFIER -> AST_MEMORY without indexing)
|
||||
static bool is_unexpanded_array_ref(AstNode *node)
|
||||
{
|
||||
if (node->type != AST_IDENTIFIER)
|
||||
return false;
|
||||
if (node->id2ast == nullptr || node->id2ast->type != AST_MEMORY)
|
||||
return false;
|
||||
// No indexing children = whole array reference
|
||||
return node->children.empty();
|
||||
}
|
||||
|
||||
// Check if two memories have compatible unpacked dimensions for array assignment
|
||||
static bool arrays_have_compatible_dims(AstNode *mem_a, AstNode *mem_b)
|
||||
{
|
||||
if (mem_a->unpacked_dimensions != mem_b->unpacked_dimensions)
|
||||
return false;
|
||||
for (int i = 0; i < mem_a->unpacked_dimensions; i++) {
|
||||
if (mem_a->dimensions[i].range_width != mem_b->dimensions[i].range_width)
|
||||
return false;
|
||||
}
|
||||
// Also check packed dimensions (element width)
|
||||
int a_width, a_size, a_bits;
|
||||
int b_width, b_size, b_bits;
|
||||
mem_a->meminfo(a_width, a_size, a_bits);
|
||||
mem_b->meminfo(b_width, b_size, b_bits);
|
||||
return a_width == b_width;
|
||||
}
|
||||
|
||||
// Check if mem_b matches mem_a's unpacked dimensions starting at first_dim.
|
||||
static bool arrays_have_compatible_dims_from(AstNode *mem_a, int first_dim, AstNode *mem_b)
|
||||
{
|
||||
if (mem_b->unpacked_dimensions != mem_a->unpacked_dimensions - first_dim)
|
||||
return false;
|
||||
for (int i = 0; i < mem_b->unpacked_dimensions; i++) {
|
||||
if (mem_a->dimensions[first_dim + i].range_width != mem_b->dimensions[i].range_width)
|
||||
return false;
|
||||
}
|
||||
// Also check packed dimensions (element width)
|
||||
int a_width, a_size, a_bits;
|
||||
int b_width, b_size, b_bits;
|
||||
mem_a->meminfo(a_width, a_size, a_bits);
|
||||
mem_b->meminfo(b_width, b_size, b_bits);
|
||||
return a_width == b_width;
|
||||
}
|
||||
|
||||
// Convert per-dimension element positions to declared index values.
|
||||
// Position 0 is the first declared element for each unpacked dimension.
|
||||
static std::vector<int> array_indices_from_position(AstNode *mem, const std::vector<int> &position)
|
||||
{
|
||||
int num_dims = mem->unpacked_dimensions;
|
||||
log_assert(GetSize(position) == num_dims);
|
||||
|
||||
std::vector<int> indices(num_dims);
|
||||
for (int d = 0; d < num_dims; d++) {
|
||||
int low = mem->dimensions[d].range_right;
|
||||
int high = low + mem->dimensions[d].range_width - 1;
|
||||
indices[d] = mem->dimensions[d].range_swapped ? (low + position[d]) : (high - position[d]);
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
|
||||
// Generate all element positions for a multi-dimensional unpacked array and
|
||||
// call callback once for each combination.
|
||||
static void foreach_array_position(AstNode *mem, std::function<void(const std::vector<int>&)> callback)
|
||||
{
|
||||
int num_dims = mem->unpacked_dimensions;
|
||||
if (num_dims == 0) {
|
||||
callback({});
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<int> position(num_dims, 0);
|
||||
std::vector<int> sizes(num_dims);
|
||||
|
||||
for (int d = 0; d < num_dims; d++)
|
||||
sizes[d] = mem->dimensions[d].range_width;
|
||||
|
||||
// Iterate through all position combinations (rightmost dimension fastest).
|
||||
while (true) {
|
||||
callback(position);
|
||||
|
||||
int d = num_dims - 1;
|
||||
while (d >= 0) {
|
||||
position[d]++;
|
||||
if (position[d] < sizes[d])
|
||||
break;
|
||||
position[d] = 0;
|
||||
d--;
|
||||
}
|
||||
if (d < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int size_packed_struct(AstNode *snode, int base_offset)
|
||||
{
|
||||
// Struct members will be laid out in the structure contiguously from left to right.
|
||||
|
|
@ -1393,7 +1492,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
|
|||
const RTLIL::Wire *ref = module->wire(port_name);
|
||||
if (ref == nullptr)
|
||||
input_error("Cell instance refers to port %s which does not exist in module %s!.\n",
|
||||
log_id(port_name), log_id(module->name));
|
||||
port_name.unescape(), module->name.unescape());
|
||||
|
||||
// select the argument, if present
|
||||
log_assert(child->children.size() <= 1);
|
||||
|
|
@ -1653,6 +1752,12 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
|
|||
children_are_self_determined = true;
|
||||
break;
|
||||
|
||||
case AST_ASSIGN_PATTERN:
|
||||
// Assignment pattern elements are context-determined by the target element type.
|
||||
// Keep child width context intact until whole-array assignment expansion creates scalar assignments.
|
||||
detect_width_simple = true;
|
||||
break;
|
||||
|
||||
case AST_NEG:
|
||||
case AST_BIT_NOT:
|
||||
case AST_POS:
|
||||
|
|
@ -3145,7 +3250,7 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
if (stage > 1 && type == AST_IDENTIFIER && id2ast != nullptr && id2ast->type == AST_MEMORY && !in_lvalue &&
|
||||
children.size() == 1 && children[0]->type == AST_RANGE && children[0]->children.size() == 1) {
|
||||
if (integer < (unsigned)id2ast->unpacked_dimensions)
|
||||
input_error("Insufficient number of array indices for %s.\n", log_id(str));
|
||||
input_error("Insufficient number of array indices for %s.\n", RTLIL::unescape_id(str));
|
||||
newNode = std::make_unique<AstNode>(location, AST_MEMRD, children[0]->children[0]->clone());
|
||||
newNode->str = str;
|
||||
newNode->id2ast = id2ast;
|
||||
|
|
@ -3200,6 +3305,217 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
}
|
||||
}
|
||||
|
||||
// Expand array assignment: arr_out = arr_in OR arr_out = cond ? arr_a : arr_b OR arr_out = '{a, b}
|
||||
// Supports multi-dimensional unpacked arrays
|
||||
if ((type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE || type == AST_ASSIGN) &&
|
||||
is_unexpanded_array_ref(children[0].get()))
|
||||
{
|
||||
AstNode *lhs = children[0].get();
|
||||
AstNode *rhs = children[1].get();
|
||||
AstNode *lhs_mem = lhs->id2ast;
|
||||
|
||||
// Case 1: Direct array assignment (b = a)
|
||||
bool is_direct_assign = is_unexpanded_array_ref(rhs);
|
||||
|
||||
// Case 2: Ternary array assignment (out = sel ? a : b)
|
||||
bool is_ternary_assign = (rhs->type == AST_TERNARY &&
|
||||
is_unexpanded_array_ref(rhs->children[1].get()) &&
|
||||
is_unexpanded_array_ref(rhs->children[2].get()));
|
||||
|
||||
// Case 3: Positional assignment pattern (out = '{a, b})
|
||||
bool is_pattern_assign = rhs->type == AST_ASSIGN_PATTERN;
|
||||
|
||||
if (is_direct_assign || is_ternary_assign || is_pattern_assign)
|
||||
{
|
||||
AstNode *direct_rhs_mem = nullptr;
|
||||
AstNode *true_mem = nullptr;
|
||||
AstNode *false_mem = nullptr;
|
||||
|
||||
int num_dims = lhs_mem->unpacked_dimensions;
|
||||
int total_elements = 1;
|
||||
for (int d = 0; d < num_dims; d++)
|
||||
total_elements *= lhs_mem->dimensions[d].range_width;
|
||||
int element_width, mem_size, addr_bits;
|
||||
lhs_mem->meminfo(element_width, mem_size, addr_bits);
|
||||
bool pattern_is_flat = false;
|
||||
|
||||
// Helper to add indices to an array identifier clone.
|
||||
auto add_indices_to_id = [&](std::unique_ptr<AstNode> id, const std::vector<int>& indices) {
|
||||
int indexed_dims = GetSize(indices);
|
||||
if (indexed_dims == 1) {
|
||||
// Single dimension: use AST_RANGE
|
||||
id->children.push_back(std::make_unique<AstNode>(location, AST_RANGE,
|
||||
mkconst_int(location, indices[0], true)));
|
||||
} else {
|
||||
// Multiple dimensions: use AST_MULTIRANGE
|
||||
auto multirange = std::make_unique<AstNode>(location, AST_MULTIRANGE);
|
||||
for (int idx : indices) {
|
||||
multirange->children.push_back(std::make_unique<AstNode>(location, AST_RANGE,
|
||||
mkconst_int(location, idx, true)));
|
||||
}
|
||||
id->children.push_back(std::move(multirange));
|
||||
}
|
||||
id->integer = indexed_dims;
|
||||
// Reset basic_prep so multirange gets resolved during subsequent simplify passes
|
||||
id->basic_prep = false;
|
||||
return id;
|
||||
};
|
||||
|
||||
auto add_position_to_id = [&](std::unique_ptr<AstNode> id, AstNode *mem, const std::vector<int>& position) {
|
||||
return add_indices_to_id(std::move(id), array_indices_from_position(mem, position));
|
||||
};
|
||||
|
||||
// Validate nested assignment pattern shape against unpacked dimensions.
|
||||
std::function<void(AstNode*, int)> validate_pattern_shape = [&](AstNode *pattern, int dim) {
|
||||
log_assert(pattern->type == AST_ASSIGN_PATTERN);
|
||||
|
||||
int expected = lhs_mem->dimensions[dim].range_width;
|
||||
if (GetSize(pattern->children) != expected)
|
||||
input_error("Assignment pattern element count mismatch at dimension %d: got %d, expected %d\n",
|
||||
dim + 1, GetSize(pattern->children), expected);
|
||||
|
||||
if (dim + 1 == num_dims)
|
||||
return;
|
||||
|
||||
for (auto& child : pattern->children) {
|
||||
if (child->type == AST_ASSIGN_PATTERN) {
|
||||
validate_pattern_shape(child.get(), dim + 1);
|
||||
} else if (is_unexpanded_array_ref(child.get()) &&
|
||||
arrays_have_compatible_dims_from(lhs_mem, dim + 1, child->id2ast)) {
|
||||
continue;
|
||||
} else {
|
||||
input_error("Nested assignment pattern or compatible array expression required for dimension %d\n", dim + 2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Select the assignment pattern element for an unpacked array position.
|
||||
auto pattern_element_at_position = [&](const std::vector<int>& position, int flat_index) {
|
||||
if (pattern_is_flat)
|
||||
return rhs->children[flat_index]->clone();
|
||||
|
||||
AstNode *pattern = rhs;
|
||||
for (int d = 0; d < num_dims; d++) {
|
||||
log_assert(pattern->type == AST_ASSIGN_PATTERN);
|
||||
AstNode *element = pattern->children[position[d]].get();
|
||||
|
||||
if (d + 1 == num_dims)
|
||||
return element->clone();
|
||||
|
||||
if (element->type == AST_ASSIGN_PATTERN) {
|
||||
pattern = element;
|
||||
} else {
|
||||
std::vector<int> subposition(position.begin() + d + 1, position.end());
|
||||
return add_position_to_id(element->clone(), element->id2ast, subposition);
|
||||
}
|
||||
}
|
||||
log_abort();
|
||||
};
|
||||
|
||||
// Validate array compatibility
|
||||
if (is_direct_assign) {
|
||||
direct_rhs_mem = rhs->id2ast;
|
||||
if (!arrays_have_compatible_dims(lhs_mem, direct_rhs_mem))
|
||||
input_error("Array dimension mismatch in assignment\n");
|
||||
} else if (is_ternary_assign) {
|
||||
true_mem = rhs->children[1]->id2ast;
|
||||
false_mem = rhs->children[2]->id2ast;
|
||||
if (!arrays_have_compatible_dims(lhs_mem, true_mem) ||
|
||||
!arrays_have_compatible_dims(lhs_mem, false_mem))
|
||||
input_error("Array dimension mismatch in ternary expression\n");
|
||||
} else {
|
||||
if (num_dims > 1 && GetSize(rhs->children) == lhs_mem->dimensions[0].range_width) {
|
||||
validate_pattern_shape(rhs, 0);
|
||||
} else if (num_dims == 1 && GetSize(rhs->children) == total_elements) {
|
||||
pattern_is_flat = true;
|
||||
} else {
|
||||
if (num_dims > 1 && GetSize(rhs->children) == lhs_mem->dimensions[0].range_width)
|
||||
validate_pattern_shape(rhs, 0);
|
||||
int expected = num_dims > 1 ? lhs_mem->dimensions[0].range_width : total_elements;
|
||||
input_error("Assignment pattern element count mismatch: got %d, expected %d\n", GetSize(rhs->children), expected);
|
||||
}
|
||||
}
|
||||
|
||||
// Warn if array assignment expansion is large.
|
||||
if (total_elements > 10000)
|
||||
log_warning("Expanding array assignment with %d elements at %s, this may be slow.\n",
|
||||
total_elements, location.to_string().c_str());
|
||||
|
||||
// Collect all assignments
|
||||
std::vector<std::unique_ptr<AstNode>> assignments;
|
||||
std::vector<std::unique_ptr<AstNode>> pattern_temp_assignments;
|
||||
|
||||
foreach_array_position(lhs_mem, [&](const std::vector<int>& position) {
|
||||
auto lhs_idx = add_position_to_id(lhs->clone(), lhs_mem, position);
|
||||
|
||||
std::unique_ptr<AstNode> rhs_expr;
|
||||
if (is_direct_assign) {
|
||||
rhs_expr = add_position_to_id(rhs->clone(), direct_rhs_mem, position);
|
||||
} else if (is_ternary_assign) {
|
||||
// Ternary case
|
||||
AstNode *cond = rhs->children[0].get();
|
||||
AstNode *true_val = rhs->children[1].get();
|
||||
AstNode *false_val = rhs->children[2].get();
|
||||
|
||||
auto true_idx = add_position_to_id(true_val->clone(), true_mem, position);
|
||||
auto false_idx = add_position_to_id(false_val->clone(), false_mem, position);
|
||||
|
||||
rhs_expr = std::make_unique<AstNode>(location, AST_TERNARY,
|
||||
cond->clone(), std::move(true_idx), std::move(false_idx));
|
||||
} else {
|
||||
auto pattern_rhs = pattern_element_at_position(position, GetSize(assignments));
|
||||
|
||||
if (type == AST_ASSIGN_EQ) {
|
||||
auto wire_tmp_owned = std::make_unique<AstNode>(location, AST_WIRE,
|
||||
std::make_unique<AstNode>(location, AST_RANGE,
|
||||
mkconst_int(location, element_width - 1, true),
|
||||
mkconst_int(location, 0, true)));
|
||||
auto wire_tmp = wire_tmp_owned.get();
|
||||
wire_tmp->str = stringf("$assignpattern$%s:%d$%d",
|
||||
RTLIL::encode_filename(*location.begin.filename), location.begin.line, autoidx++);
|
||||
current_scope[wire_tmp->str] = wire_tmp;
|
||||
current_ast_mod->children.push_back(std::move(wire_tmp_owned));
|
||||
wire_tmp->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false));
|
||||
while (wire_tmp->simplify(true, 1, -1, false)) { }
|
||||
wire_tmp->is_logic = true;
|
||||
wire_tmp->is_signed = lhs_mem->is_signed;
|
||||
|
||||
auto tmp_id = std::make_unique<AstNode>(location, AST_IDENTIFIER);
|
||||
tmp_id->str = wire_tmp->str;
|
||||
pattern_temp_assignments.push_back(std::make_unique<AstNode>(location, AST_ASSIGN_EQ,
|
||||
tmp_id->clone(), std::move(pattern_rhs)));
|
||||
rhs_expr = std::move(tmp_id);
|
||||
} else {
|
||||
rhs_expr = std::move(pattern_rhs);
|
||||
}
|
||||
}
|
||||
|
||||
auto assign = std::make_unique<AstNode>(location, type,
|
||||
std::move(lhs_idx), std::move(rhs_expr));
|
||||
assign->was_checked = true;
|
||||
assignments.push_back(std::move(assign));
|
||||
});
|
||||
|
||||
// For continuous assignments, add to module; for procedural, use block
|
||||
if (type == AST_ASSIGN) {
|
||||
// Add all but last to module
|
||||
for (size_t i = 0; i + 1 < assignments.size(); i++)
|
||||
current_ast_mod->children.push_back(std::move(assignments[i]));
|
||||
// Last one replaces current node
|
||||
newNode = std::move(assignments.back());
|
||||
} else {
|
||||
// Wrap in AST_BLOCK for procedural
|
||||
newNode = std::make_unique<AstNode>(location, AST_BLOCK);
|
||||
for (auto& assign : pattern_temp_assignments)
|
||||
newNode->children.push_back(std::move(assign));
|
||||
for (auto& assign : assignments)
|
||||
newNode->children.push_back(std::move(assign));
|
||||
}
|
||||
|
||||
goto apply_newNode;
|
||||
}
|
||||
}
|
||||
|
||||
// assignment with memory in left-hand side expression -> replace with memory write port
|
||||
if (stage > 1 && (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_IDENTIFIER &&
|
||||
children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && children[0]->id2ast->children.size() >= 2 &&
|
||||
|
|
@ -3207,7 +3523,7 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
(children[0]->children.size() == 1 || children[0]->children.size() == 2) && children[0]->children[0]->type == AST_RANGE)
|
||||
{
|
||||
if (children[0]->integer < (unsigned)children[0]->id2ast->unpacked_dimensions)
|
||||
input_error("Insufficient number of array indices for %s.\n", log_id(str));
|
||||
input_error("Insufficient number of array indices for %s.\n", RTLIL::unescape_id(str));
|
||||
|
||||
std::stringstream sstr;
|
||||
sstr << "$memwr$" << children[0]->str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++);
|
||||
|
|
@ -4458,6 +4774,8 @@ replace_fcall_later:;
|
|||
tmp_bits.insert(tmp_bits.end(), children.at(1)->bits.begin(), children.at(1)->bits.end());
|
||||
newNode = children.at(1)->is_string ? mkconst_str(location, tmp_bits) : mkconst_bits(location, tmp_bits, false);
|
||||
break;
|
||||
case AST_ASSIGN_PATTERN:
|
||||
goto not_const;
|
||||
default:
|
||||
not_const:
|
||||
break;
|
||||
|
|
@ -4955,7 +5273,7 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg
|
|||
AstNode *mem = id2ast;
|
||||
|
||||
if (integer < (unsigned)mem->unpacked_dimensions)
|
||||
input_error("Insufficient number of array indices for %s.\n", log_id(str));
|
||||
input_error("Insufficient number of array indices for %s.\n", RTLIL::unescape_id(str));
|
||||
|
||||
// flag if used after blocking assignment (in same proc)
|
||||
if ((proc_flags[mem] & AstNode::MEM2REG_FL_EQ1) && !(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_EQ2)) {
|
||||
|
|
|
|||
8
frontends/blif/CMakeLists.txt
Normal file
8
frontends/blif/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
yosys_frontend(blif
|
||||
blifparse.cc
|
||||
blifparse.h
|
||||
DATA_DIR
|
||||
include/frontends/blif
|
||||
DATA_FILES
|
||||
blifparse.h
|
||||
)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
|
||||
OBJS += frontends/blif/blifparse.o
|
||||
|
||||
|
|
@ -175,7 +175,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
|||
obj_attributes = &module->attributes;
|
||||
obj_parameters = nullptr;
|
||||
if (design->module(module->name))
|
||||
log_error("Duplicate definition of module %s in line %d!\n", log_id(module->name), line_count);
|
||||
log_error("Duplicate definition of module %s in line %d!\n", module->name.unescape(), line_count);
|
||||
design->add(module);
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
3
frontends/json/CMakeLists.txt
Normal file
3
frontends/json/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
yosys_frontend(json
|
||||
jsonparse.cc
|
||||
)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
|
||||
OBJS += frontends/json/jsonparse.o
|
||||
|
||||
|
|
@ -295,7 +295,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
module->name = RTLIL::escape_id(modname.c_str());
|
||||
|
||||
if (design->module(module->name))
|
||||
log_error("Re-definition of module %s.\n", log_id(module->name));
|
||||
log_error("Re-definition of module %s.\n", module->name.unescape());
|
||||
|
||||
design->add(module);
|
||||
|
||||
|
|
@ -320,22 +320,22 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
JsonNode *port_node = ports_node->data_dict.at(ports_node->data_dict_keys[port_id-1]);
|
||||
|
||||
if (port_node->type != 'D')
|
||||
log_error("JSON port node '%s' is not a dictionary.\n", log_id(port_name));
|
||||
log_error("JSON port node '%s' is not a dictionary.\n", port_name.unescape());
|
||||
|
||||
if (port_node->data_dict.count("direction") == 0)
|
||||
log_error("JSON port node '%s' has no direction attribute.\n", log_id(port_name));
|
||||
log_error("JSON port node '%s' has no direction attribute.\n", port_name.unescape());
|
||||
|
||||
if (port_node->data_dict.count("bits") == 0)
|
||||
log_error("JSON port node '%s' has no bits attribute.\n", log_id(port_name));
|
||||
log_error("JSON port node '%s' has no bits attribute.\n", port_name.unescape());
|
||||
|
||||
JsonNode *port_direction_node = port_node->data_dict.at("direction");
|
||||
JsonNode *port_bits_node = port_node->data_dict.at("bits");
|
||||
|
||||
if (port_direction_node->type != 'S')
|
||||
log_error("JSON port node '%s' has non-string direction attribute.\n", log_id(port_name));
|
||||
log_error("JSON port node '%s' has non-string direction attribute.\n", port_name.unescape());
|
||||
|
||||
if (port_bits_node->type != 'A')
|
||||
log_error("JSON port node '%s' has non-array bits attribute.\n", log_id(port_name));
|
||||
log_error("JSON port node '%s' has non-array bits attribute.\n", port_name.unescape());
|
||||
|
||||
Wire *port_wire = module->wire(port_name);
|
||||
|
||||
|
|
@ -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);
|
||||
log_error("JSON port node '%s' has invalid '%s' direction attribute.\n", port_name.unescape(), port_direction_node->data_string);
|
||||
|
||||
port_wire->port_id = port_id;
|
||||
|
||||
|
|
@ -390,7 +390,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
module->connect(sigbit, State::Sz);
|
||||
else
|
||||
log_error("JSON port node '%s' has invalid '%s' bit string value on bit %d.\n",
|
||||
log_id(port_name), bitval_node->data_string.c_str(), i);
|
||||
port_name.unescape(), bitval_node->data_string.c_str(), i);
|
||||
} else
|
||||
if (bitval_node->type == 'N') {
|
||||
int bitidx = bitval_node->data_number;
|
||||
|
|
@ -405,7 +405,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
signal_bits[bitidx] = sigbit;
|
||||
}
|
||||
} else
|
||||
log_error("JSON port node '%s' has invalid bit value on bit %d.\n", log_id(port_name), i);
|
||||
log_error("JSON port node '%s' has invalid bit value on bit %d.\n", port_name.unescape(), i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -425,15 +425,15 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
JsonNode *net_node = net.second;
|
||||
|
||||
if (net_node->type != 'D')
|
||||
log_error("JSON netname node '%s' is not a dictionary.\n", log_id(net_name));
|
||||
log_error("JSON netname node '%s' is not a dictionary.\n", net_name.unescape());
|
||||
|
||||
if (net_node->data_dict.count("bits") == 0)
|
||||
log_error("JSON netname node '%s' has no bits attribute.\n", log_id(net_name));
|
||||
log_error("JSON netname node '%s' has no bits attribute.\n", net_name.unescape());
|
||||
|
||||
JsonNode *bits_node = net_node->data_dict.at("bits");
|
||||
|
||||
if (bits_node->type != 'A')
|
||||
log_error("JSON netname node '%s' has non-array bits attribute.\n", log_id(net_name));
|
||||
log_error("JSON netname node '%s' has non-array bits attribute.\n", net_name.unescape());
|
||||
|
||||
Wire *wire = module->wire(net_name);
|
||||
|
||||
|
|
@ -468,7 +468,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
module->connect(sigbit, State::Sz);
|
||||
else
|
||||
log_error("JSON netname node '%s' has invalid '%s' bit string value on bit %d.\n",
|
||||
log_id(net_name), bitval_node->data_string.c_str(), i);
|
||||
net_name.unescape(), bitval_node->data_string.c_str(), i);
|
||||
} else
|
||||
if (bitval_node->type == 'N') {
|
||||
int bitidx = bitval_node->data_number;
|
||||
|
|
@ -479,7 +479,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
signal_bits[bitidx] = sigbit;
|
||||
}
|
||||
} else
|
||||
log_error("JSON netname node '%s' has invalid bit value on bit %d.\n", log_id(net_name), i);
|
||||
log_error("JSON netname node '%s' has invalid bit value on bit %d.\n", net_name.unescape(), i);
|
||||
}
|
||||
|
||||
if (net_node->data_dict.count("attributes"))
|
||||
|
|
@ -500,27 +500,27 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
JsonNode *cell_node = cell_node_it.second;
|
||||
|
||||
if (cell_node->type != 'D')
|
||||
log_error("JSON cells node '%s' is not a dictionary.\n", log_id(cell_name));
|
||||
log_error("JSON cells node '%s' is not a dictionary.\n", cell_name.unescape());
|
||||
|
||||
if (cell_node->data_dict.count("type") == 0)
|
||||
log_error("JSON cells node '%s' has no type attribute.\n", log_id(cell_name));
|
||||
log_error("JSON cells node '%s' has no type attribute.\n", cell_name.unescape());
|
||||
|
||||
JsonNode *type_node = cell_node->data_dict.at("type");
|
||||
|
||||
if (type_node->type != 'S')
|
||||
log_error("JSON cells node '%s' has a non-string type.\n", log_id(cell_name));
|
||||
log_error("JSON cells node '%s' has a non-string type.\n", cell_name.unescape());
|
||||
|
||||
IdString cell_type = RTLIL::escape_id(type_node->data_string.c_str());
|
||||
|
||||
Cell *cell = module->addCell(cell_name, cell_type);
|
||||
|
||||
if (cell_node->data_dict.count("connections") == 0)
|
||||
log_error("JSON cells node '%s' has no connections attribute.\n", log_id(cell_name));
|
||||
log_error("JSON cells node '%s' has no connections attribute.\n", cell_name.unescape());
|
||||
|
||||
JsonNode *connections_node = cell_node->data_dict.at("connections");
|
||||
|
||||
if (connections_node->type != 'D')
|
||||
log_error("JSON cells node '%s' has non-dictionary connections attribute.\n", log_id(cell_name));
|
||||
log_error("JSON cells node '%s' has non-dictionary connections attribute.\n", cell_name.unescape());
|
||||
|
||||
for (auto &conn_it : connections_node->data_dict)
|
||||
{
|
||||
|
|
@ -528,7 +528,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
JsonNode *conn_node = conn_it.second;
|
||||
|
||||
if (conn_node->type != 'A')
|
||||
log_error("JSON cells node '%s' connection '%s' is not an array.\n", log_id(cell_name), log_id(conn_name));
|
||||
log_error("JSON cells node '%s' connection '%s' is not an array.\n", cell_name.unescape(), conn_name.unescape());
|
||||
|
||||
SigSpec sig;
|
||||
|
||||
|
|
@ -547,7 +547,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
sig.append(State::Sz);
|
||||
else
|
||||
log_error("JSON cells node '%s' connection '%s' has invalid '%s' bit string value on bit %d.\n",
|
||||
log_id(cell_name), log_id(conn_name), bitval_node->data_string.c_str(), i);
|
||||
cell_name.unescape(), conn_name.unescape(), bitval_node->data_string.c_str(), i);
|
||||
} else
|
||||
if (bitval_node->type == 'N') {
|
||||
int bitidx = bitval_node->data_number;
|
||||
|
|
@ -556,7 +556,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
sig.append(signal_bits.at(bitidx));
|
||||
} else
|
||||
log_error("JSON cells node '%s' connection '%s' has invalid bit value on bit %d.\n",
|
||||
log_id(cell_name), log_id(conn_name), i);
|
||||
cell_name.unescape(), conn_name.unescape(), i);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -587,20 +587,20 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
mem->name = memory_name;
|
||||
|
||||
if (memory_node->type != 'D')
|
||||
log_error("JSON memory node '%s' is not a dictionary.\n", log_id(memory_name));
|
||||
log_error("JSON memory node '%s' is not a dictionary.\n", memory_name.unescape());
|
||||
|
||||
if (memory_node->data_dict.count("width") == 0)
|
||||
log_error("JSON memory node '%s' has no width attribute.\n", log_id(memory_name));
|
||||
log_error("JSON memory node '%s' has no width attribute.\n", memory_name.unescape());
|
||||
JsonNode *width_node = memory_node->data_dict.at("width");
|
||||
if (width_node->type != 'N')
|
||||
log_error("JSON memory node '%s' has a non-number width.\n", log_id(memory_name));
|
||||
log_error("JSON memory node '%s' has a non-number width.\n", memory_name.unescape());
|
||||
mem->width = width_node->data_number;
|
||||
|
||||
if (memory_node->data_dict.count("size") == 0)
|
||||
log_error("JSON memory node '%s' has no size attribute.\n", log_id(memory_name));
|
||||
log_error("JSON memory node '%s' has no size attribute.\n", memory_name.unescape());
|
||||
JsonNode *size_node = memory_node->data_dict.at("size");
|
||||
if (size_node->type != 'N')
|
||||
log_error("JSON memory node '%s' has a non-number size.\n", log_id(memory_name));
|
||||
log_error("JSON memory node '%s' has a non-number size.\n", memory_name.unescape());
|
||||
mem->size = size_node->data_number;
|
||||
|
||||
mem->start_offset = 0;
|
||||
|
|
|
|||
5
frontends/liberty/CMakeLists.txt
Normal file
5
frontends/liberty/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
yosys_frontend(liberty
|
||||
liberty.cc
|
||||
REQUIRES
|
||||
libparse
|
||||
)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
|
||||
OBJS += frontends/liberty/liberty.o
|
||||
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
#include "passes/techmap/libparse.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include <array>
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
|
|
@ -40,14 +41,14 @@ 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' in %s.\n", expr, RTLIL::unescape_id(module->name));
|
||||
log_error("Expected identifier at `%s' in %s.\n", expr, module);
|
||||
|
||||
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 in %s.\n", RTLIL::unescape_id(id), RTLIL::unescape_id(module->name));
|
||||
log_error("Can't resolve wire name %s in %s.\n", RTLIL::unescape_id(id), module);
|
||||
|
||||
expr += id_len;
|
||||
return module->wires_.at(id);
|
||||
|
|
@ -174,7 +175,7 @@ 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'in %s.\n", orig_expr, RTLIL::unescape_id(module->name));
|
||||
log_error("Parser error in function expr `%s'in %s.\n", orig_expr, module);
|
||||
|
||||
return stack.back().sig;
|
||||
}
|
||||
|
|
@ -210,7 +211,10 @@ 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;
|
||||
const std::string name = module->name.unescape();
|
||||
|
||||
std::optional<char> clear_preset_var1;
|
||||
std::optional<char> clear_preset_var2;
|
||||
for (auto child : node->children) {
|
||||
if (child->id == "clocked_on")
|
||||
clk_sig = parse_func_expr(module, child->value.c_str());
|
||||
|
|
@ -220,10 +224,18 @@ static void create_ff(RTLIL::Module *module, const LibertyAst *node)
|
|||
clear_sig = parse_func_expr(module, child->value.c_str());
|
||||
if (child->id == "preset")
|
||||
preset_sig = parse_func_expr(module, child->value.c_str());
|
||||
|
||||
for (auto& [id, var] : {pair{"clear_preset_var1", &clear_preset_var1}, {"clear_preset_var2", &clear_preset_var2}})
|
||||
if (child->id == id) {
|
||||
if (child->value.size() != 1)
|
||||
log_error("Unexpected length of clear_preset_var* value %s in FF cell %s\n", child->value, name);
|
||||
*var = child->value[0];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (clk_sig.size() == 0 || data_sig.size() == 0)
|
||||
log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", RTLIL::unescape_id(module->name));
|
||||
log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", name);
|
||||
|
||||
for (bool rerun_invert_rollback = true; rerun_invert_rollback;)
|
||||
{
|
||||
|
|
@ -248,36 +260,64 @@ static void create_ff(RTLIL::Module *module, const LibertyAst *node)
|
|||
}
|
||||
}
|
||||
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, ID($_NOT_));
|
||||
cell->setPort(ID::A, iq_sig);
|
||||
cell->setPort(ID::Y, iqn_sig);
|
||||
for (auto& [out_sig, cp_var, neg] : {tuple{iq_sig, clear_preset_var1, false}, {iqn_sig, clear_preset_var2, true}}) {
|
||||
SigSpec q_sig = out_sig;
|
||||
if (neg) {
|
||||
q_sig = module->addWire(NEW_ID, out_sig.as_wire());
|
||||
module->addNotGate(NEW_ID, q_sig, out_sig);
|
||||
}
|
||||
|
||||
cell = module->addCell(NEW_ID, "");
|
||||
cell->setPort(ID::D, data_sig);
|
||||
cell->setPort(ID::Q, iq_sig);
|
||||
cell->setPort(ID::C, clk_sig);
|
||||
RTLIL::Cell* cell = module->addCell(NEW_ID, "");
|
||||
cell->setPort(ID::D, data_sig);
|
||||
cell->setPort(ID::Q, q_sig);
|
||||
cell->setPort(ID::C, clk_sig);
|
||||
|
||||
if (clear_sig.size() == 0 && preset_sig.size() == 0) {
|
||||
cell->type = stringf("$_DFF_%c_", clk_polarity ? 'P' : 'N');
|
||||
if (clear_sig.size() == 0 && preset_sig.size() == 0) {
|
||||
cell->type = stringf("$_DFF_%c_", clk_polarity ? 'P' : 'N');
|
||||
}
|
||||
|
||||
if (clear_sig.size() == 1 && preset_sig.size() == 0) {
|
||||
cell->type = stringf("$_DFF_%c%c0_", clk_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N');
|
||||
cell->setPort(ID::R, clear_sig);
|
||||
}
|
||||
|
||||
if (clear_sig.size() == 0 && preset_sig.size() == 1) {
|
||||
cell->type = stringf("$_DFF_%c%c1_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N');
|
||||
cell->setPort(ID::R, preset_sig);
|
||||
}
|
||||
|
||||
if (clear_sig.size() == 1 && preset_sig.size() == 1) {
|
||||
cell->type = stringf("$_DFFSR_%c%c%c_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N');
|
||||
|
||||
SigBit s_sig = preset_sig;
|
||||
SigBit r_sig = clear_sig;
|
||||
if (cp_var && *cp_var != 'X') {
|
||||
// Either set or reset dominates
|
||||
bool set_dominates;
|
||||
if (*cp_var == 'L') {
|
||||
set_dominates = neg;
|
||||
} else if (*cp_var == 'H') {
|
||||
set_dominates = !neg;
|
||||
} else {
|
||||
log_error("FF cell %s has unsupported clear&preset behavior \'%c\'.\n", name, *cp_var);
|
||||
}
|
||||
log_debug("cell %s variable %d cp_var %c set dominates? %d\n", name, (int)neg + 1, *cp_var, set_dominates);
|
||||
// S&R priority is well-defined now
|
||||
if (set_dominates) {
|
||||
r_sig = module->AndnotGate(NEW_ID, r_sig, s_sig);
|
||||
} else {
|
||||
s_sig = module->AndnotGate(NEW_ID, s_sig, r_sig);
|
||||
}
|
||||
} else {
|
||||
log_debug("cell %s variable %d undef c&p behavior\n", name, (int)neg + 1);
|
||||
}
|
||||
|
||||
cell->setPort(ID::S, s_sig);
|
||||
cell->setPort(ID::R, r_sig);
|
||||
}
|
||||
|
||||
log_assert(!cell->type.empty());
|
||||
}
|
||||
|
||||
if (clear_sig.size() == 1 && preset_sig.size() == 0) {
|
||||
cell->type = stringf("$_DFF_%c%c0_", clk_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N');
|
||||
cell->setPort(ID::R, clear_sig);
|
||||
}
|
||||
|
||||
if (clear_sig.size() == 0 && preset_sig.size() == 1) {
|
||||
cell->type = stringf("$_DFF_%c%c1_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N');
|
||||
cell->setPort(ID::R, preset_sig);
|
||||
}
|
||||
|
||||
if (clear_sig.size() == 1 && preset_sig.size() == 1) {
|
||||
cell->type = stringf("$_DFFSR_%c%c%c_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N');
|
||||
cell->setPort(ID::S, preset_sig);
|
||||
cell->setPort(ID::R, clear_sig);
|
||||
}
|
||||
|
||||
log_assert(!cell->type.empty());
|
||||
}
|
||||
|
||||
static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool flag_ignore_miss_data_latch)
|
||||
|
|
@ -299,9 +339,9 @@ static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool fla
|
|||
|
||||
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", RTLIL::unescape_id(module->name));
|
||||
log_error("Latch cell %s has no data_in and/or enable attribute.\n", module);
|
||||
else
|
||||
log("Ignored latch cell %s with no data_in and/or enable attribute.\n", RTLIL::unescape_id(module->name));
|
||||
log("Ignored latch cell %s with no data_in and/or enable attribute.\n", module);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -592,9 +632,9 @@ struct LibertyFrontend : public Frontend {
|
|||
{
|
||||
if (!flag_ignore_miss_dir)
|
||||
{
|
||||
log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0), RTLIL::unescape_id(module->name));
|
||||
log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0), module);
|
||||
} else {
|
||||
log("Ignoring cell %s with missing or invalid direction for pin %s.\n", RTLIL::unescape_id(module->name), node->args.at(0));
|
||||
log("Ignoring cell %s with missing or invalid direction for pin %s.\n", module, node->args.at(0));
|
||||
delete module;
|
||||
goto skip_cell;
|
||||
}
|
||||
|
|
@ -606,7 +646,7 @@ struct LibertyFrontend : public Frontend {
|
|||
if (node->id == "bus" && node->args.size() == 1)
|
||||
{
|
||||
if (flag_ignore_buses) {
|
||||
log("Ignoring cell %s with a bus interface %s.\n", RTLIL::unescape_id(module->name), node->args.at(0));
|
||||
log("Ignoring cell %s with a bus interface %s.\n", module, node->args.at(0));
|
||||
delete module;
|
||||
goto skip_cell;
|
||||
}
|
||||
|
|
@ -623,7 +663,7 @@ struct LibertyFrontend : public Frontend {
|
|||
}
|
||||
|
||||
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), RTLIL::unescape_id(module->name));
|
||||
log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0), module);
|
||||
|
||||
simple_comb_cell = false;
|
||||
|
||||
|
|
@ -718,9 +758,9 @@ struct LibertyFrontend : public Frontend {
|
|||
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));
|
||||
log_error("Missing function on output %s of cell %s.\n", wire, module);
|
||||
} else {
|
||||
log("Ignoring cell %s with missing function on output %s.\n", RTLIL::unescape_id(module->name), RTLIL::unescape_id(wire->name));
|
||||
log("Ignoring cell %s with missing function on output %s.\n", module, wire);
|
||||
delete module;
|
||||
goto skip_cell;
|
||||
}
|
||||
|
|
@ -797,3 +837,4 @@ skip_cell:;
|
|||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
|
||||
|
|
|
|||
8
frontends/rpc/CMakeLists.txt
Normal file
8
frontends/rpc/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
yosys_frontend(rpc
|
||||
rpc_frontend.cc
|
||||
REQUIRES
|
||||
json11
|
||||
sha1
|
||||
ENABLE_IF
|
||||
YOSYS_ENABLE_SPAWN
|
||||
)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
ifeq ($(DISABLE_SPAWN),0)
|
||||
OBJS += frontends/rpc/rpc_frontend.o
|
||||
endif
|
||||
|
|
@ -212,7 +212,7 @@ struct RpcModule : RTLIL::Module {
|
|||
for (auto module : derived_design->modules_) {
|
||||
std::string mangled_name = name_mangling[module.first.str()];
|
||||
|
||||
log("Importing `%s' as `%s'.\n", log_id(module.first), log_id(mangled_name));
|
||||
log("Importing `%s' as `%s'.\n", module.first.unescape(), mangled_name);
|
||||
|
||||
module.second->name = mangled_name;
|
||||
module.second->design = design;
|
||||
|
|
|
|||
3
frontends/rtlil/CMakeLists.txt
Normal file
3
frontends/rtlil/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
yosys_frontend(rtlil
|
||||
rtlil_frontend.cc
|
||||
)
|
||||
|
|
@ -1 +0,0 @@
|
|||
OBJS += frontends/rtlil/rtlil_frontend.o
|
||||
|
|
@ -286,6 +286,7 @@ struct RTLILFrontendWorker {
|
|||
if (width > MAX_CONST_WIDTH)
|
||||
error("Constant width %lld out of range before `%s`.", width, error_token());
|
||||
bits.reserve(width);
|
||||
int start_idx = idx;
|
||||
while (true) {
|
||||
RTLIL::State bit;
|
||||
switch (line[idx]) {
|
||||
|
|
@ -300,8 +301,9 @@ struct RTLILFrontendWorker {
|
|||
bits.push_back(bit);
|
||||
++idx;
|
||||
}
|
||||
done:
|
||||
std::reverse(bits.begin(), bits.end());
|
||||
done:
|
||||
if (start_idx < idx)
|
||||
std::reverse(bits.begin(), bits.end());
|
||||
|
||||
if (GetSize(bits) > width)
|
||||
bits.resize(width);
|
||||
|
|
@ -330,7 +332,7 @@ struct RTLILFrontendWorker {
|
|||
error("No wires found for legalization");
|
||||
int hash = hash_ops<RTLIL::IdString>::hash(id).yield();
|
||||
RTLIL::Wire *wire = current_module->wire_at(abs(hash % wires_size));
|
||||
log("Legalizing wire `%s' to `%s'.\n", log_id(id), log_id(wire->name));
|
||||
log("Legalizing wire `%s' to `%s'.\n", id.unescape(), wire->name.unescape());
|
||||
return wire;
|
||||
}
|
||||
|
||||
|
|
|
|||
70
frontends/verific/CMakeLists.txt
Normal file
70
frontends/verific/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
if (NOT YOSYS_ENABLE_VERIFIC)
|
||||
# Stub interface library.
|
||||
add_library(verific INTERFACE)
|
||||
set(verific_data_files)
|
||||
else()
|
||||
if (NOT YOSYS_VERIFIC_COMPONENTS)
|
||||
get_verific_components(YOSYS_VERIFIC_COMPONENTS)
|
||||
endif()
|
||||
|
||||
if (NOT YOSYS_VERIFIC_FEATURES)
|
||||
foreach (component ${YOSYS_VERIFIC_COMPONENTS})
|
||||
if (component MATCHES "^(hier_tree|vhdl|edif|extensions)$")
|
||||
list(APPEND YOSYS_VERIFIC_FEATURES ${component})
|
||||
elseif (component STREQUAL "verilog")
|
||||
list(APPEND YOSYS_VERIFIC_FEATURES systemverilog)
|
||||
elseif (component STREQUAL "synlib")
|
||||
list(APPEND YOSYS_VERIFIC_FEATURES liberty)
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
message(STATUS "Verific library components: ${YOSYS_VERIFIC_COMPONENTS}")
|
||||
message(STATUS "Verific frontend features: ${YOSYS_VERIFIC_FEATURES}")
|
||||
|
||||
get_verific_options(verific_include_dirs verific_libraries ${YOSYS_VERIFIC_COMPONENTS})
|
||||
add_library(verific INTERFACE)
|
||||
target_include_directories(verific INTERFACE
|
||||
${verific_include_dirs}
|
||||
)
|
||||
target_link_libraries(verific INTERFACE
|
||||
$<LINK_LIBRARY:WHOLE_ARCHIVE,${verific_libraries}>
|
||||
PkgConfig::zlib
|
||||
)
|
||||
|
||||
set(verific_data_files)
|
||||
if ("vhdl" IN_LIST YOSYS_VERIFIC_FEATURES)
|
||||
foreach (vdb_std 1987 1993 2008 2019)
|
||||
set(vdb_std_root ${YOSYS_VERIFIC_DIR}/vhdl_packages/vdbs_${vdb_std})
|
||||
file(GLOB_RECURSE vdb_files RELATIVE ${vdb_std_root} ${vdb_std_root}/*)
|
||||
foreach (vdb_file ${vdb_files})
|
||||
list(APPEND verific_data_files
|
||||
verific/vhdl_vdbs_${vdb_std}/${vdb_file}
|
||||
${YOSYS_VERIFIC_DIR}/vhdl_packages/vdbs_${vdb_std}/${vdb_file}
|
||||
)
|
||||
endforeach()
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
yosys_frontend(verific
|
||||
verific.cc
|
||||
verific.h
|
||||
$<${YOSYS_ENABLE_VERIFIC}:verificsva.cc>
|
||||
DEFINITIONS
|
||||
$<$<IN_LIST:hier_tree,${YOSYS_VERIFIC_FEATURES}>:VERIFIC_HIER_TREE_SUPPORT>
|
||||
$<$<IN_LIST:systemverilog,${YOSYS_VERIFIC_FEATURES}>:VERIFIC_SYSTEMVERILOG_SUPPORT>
|
||||
$<$<IN_LIST:vhdl,${YOSYS_VERIFIC_FEATURES}>:VERIFIC_VHDL_SUPPORT>
|
||||
$<$<IN_LIST:edif,${YOSYS_VERIFIC_FEATURES}>:VERIFIC_EDIF_SUPPORT>
|
||||
$<$<IN_LIST:liberty,${YOSYS_VERIFIC_FEATURES}>:VERIFIC_LIBERTY_SUPPORT>
|
||||
$<$<IN_LIST:extensions,${YOSYS_VERIFIC_FEATURES}>:YOSYSHQ_VERIFIC_EXTENSIONS>
|
||||
LIBRARIES
|
||||
verific
|
||||
REQUIRES
|
||||
sha1
|
||||
read_verilog
|
||||
PROVIDES
|
||||
read
|
||||
DATA_EXPLICIT
|
||||
${verific_data_files}
|
||||
)
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
|
||||
OBJS += frontends/verific/verific.o
|
||||
|
||||
ifeq ($(ENABLE_VERIFIC),1)
|
||||
|
||||
OBJS += frontends/verific/verificsva.o
|
||||
|
||||
EXTRA_TARGETS += share/verific
|
||||
|
||||
share/verific:
|
||||
$(P) rm -rf share/verific.new
|
||||
$(Q) mkdir -p share/verific.new
|
||||
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
|
||||
|
||||
endif
|
||||
|
||||
|
|
@ -1515,13 +1515,13 @@ void VerificImporter::merge_past_ffs_clock(pool<RTLIL::Cell*> &candidates, SigBi
|
|||
RTLIL::Cell *new_ff = module->addDff(NEW_ID, clock, sig_d, sig_q, clock_pol);
|
||||
|
||||
if (verific_verbose)
|
||||
log(" merging single-bit past_ffs into new %d-bit ff %s.\n", GetSize(sig_d), log_id(new_ff));
|
||||
log(" merging single-bit past_ffs into new %d-bit ff %s.\n", GetSize(sig_d), new_ff);
|
||||
|
||||
for (int i = 0; i < GetSize(sig_d); i++)
|
||||
for (auto old_ff : dbits_db[sig_d[i]])
|
||||
{
|
||||
if (verific_verbose)
|
||||
log(" replacing old ff %s on bit %d.\n", log_id(old_ff), i);
|
||||
log(" replacing old ff %s on bit %d.\n", old_ff, i);
|
||||
|
||||
SigBit old_q = old_ff->getPort(ID::Q);
|
||||
SigBit new_q = sig_q[i];
|
||||
|
|
@ -1567,6 +1567,64 @@ static std::string sha1_if_contain_spaces(std::string str)
|
|||
return str;
|
||||
}
|
||||
|
||||
void VerificImporter::recurse_mem_dimensions(RTLIL::Module *module, RTLIL::Memory *memory, Net *net, const char *&ascii_initdata, int max_bits_in_addr, const RTLIL::SigSpec &prefix, TypeRange *typeRange) {
|
||||
if (typeRange == nullptr)
|
||||
typeRange = net->GetOrigTypeRange();
|
||||
|
||||
auto *nextRange = typeRange->GetNext();
|
||||
auto left = typeRange->LeftRangeBound();
|
||||
auto right = typeRange->RightRangeBound();
|
||||
bool is_up = left < right;
|
||||
for (auto i = left; is_up ? i <= right : i >= right; is_up ? i++ : i--) {
|
||||
// TODO verific can do u64
|
||||
auto max_bits = max_bits_in_addr - prefix.size();
|
||||
auto next_sig = SigSpec(Const(i, typeRange->NumBits()));
|
||||
auto extra_bits = next_sig.size() - max_bits;
|
||||
if (extra_bits > 0) {
|
||||
next_sig = next_sig.extract_end(extra_bits);
|
||||
auto extra_inc = (1 << extra_bits) - 1;
|
||||
i = is_up ? i + extra_inc : i - extra_inc;
|
||||
}
|
||||
next_sig.append(prefix);
|
||||
if (nextRange != nullptr && extra_bits < 0) {
|
||||
recurse_mem_dimensions(module, memory, net, ascii_initdata, max_bits_in_addr, next_sig, nextRange);
|
||||
} else {
|
||||
if (next_sig.size() != max_bits_in_addr) {
|
||||
// TODO verific can do u64
|
||||
log_error("Expected %d bits for addr but got %d!\n", max_bits_in_addr, next_sig.size());
|
||||
}
|
||||
Const initval = Const(State::Sx, memory->width);
|
||||
bool initval_valid = false;
|
||||
if (ascii_initdata) {
|
||||
for (int bit_idx = memory->width-1; bit_idx >= 0; bit_idx--) {
|
||||
if (*ascii_initdata == 0)
|
||||
break;
|
||||
if (*ascii_initdata == '0' || *ascii_initdata == '1') {
|
||||
initval.set(bit_idx, (*ascii_initdata == '0') ? State::S0 : State::S1);
|
||||
initval_valid = true;
|
||||
}
|
||||
ascii_initdata++;
|
||||
}
|
||||
}
|
||||
if (!next_sig.convertible_to_int())
|
||||
log_error("Address %s on RAM for identifier '%s' too wide!\n", log_signal(next_sig), net->Name());
|
||||
auto next_idx = next_sig.as_int();
|
||||
if (initval_valid) {
|
||||
RTLIL::Cell *cell = module->addCell(new_verific_id(net), ID($meminit));
|
||||
cell->parameters[ID::WORDS] = 1;
|
||||
cell->setPort(ID::ADDR, next_idx);
|
||||
cell->setPort(ID::DATA, initval);
|
||||
cell->parameters[ID::MEMID] = RTLIL::Const(memory->name.str());
|
||||
cell->parameters[ID::ABITS] = 32;
|
||||
cell->parameters[ID::WIDTH] = memory->width;
|
||||
cell->parameters[ID::PRIORITY] = RTLIL::Const(autoidx-1);
|
||||
}
|
||||
memory->start_offset = min(memory->start_offset, next_idx);
|
||||
memory->size = max(memory->size, next_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::map<std::string,Netlist*> &nl_todo, bool norename)
|
||||
{
|
||||
std::string netlist_name = nl->GetAtt(" \\top") || is_blackbox(nl) ? nl->CellBaseName() : nl->Owner()->Name();
|
||||
|
|
@ -1617,12 +1675,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
uniquify_cache.clear();
|
||||
|
||||
if (is_blackbox(nl)) {
|
||||
log("Importing blackbox module %s.\n", RTLIL::id2cstr(module->name));
|
||||
log_flush();
|
||||
log("Importing blackbox module %s.\n", module);
|
||||
module->set_bool_attribute(ID::blackbox);
|
||||
} else {
|
||||
log("Importing module %s.\n", RTLIL::id2cstr(module->name));
|
||||
log_flush();
|
||||
log("Importing module %s.\n", module);
|
||||
}
|
||||
import_attributes(module->attributes, nl, nl);
|
||||
if (module->name.isPublic())
|
||||
|
|
@ -1766,22 +1822,33 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
module->memories[memory->name] = memory;
|
||||
import_attributes(memory->attributes, net, nl);
|
||||
|
||||
uint64_t number_of_bits = net->Size();
|
||||
uint64_t bits_in_word = number_of_bits;
|
||||
int number_of_bits = net->Size();
|
||||
int min_bits_in_word = number_of_bits;
|
||||
int max_bits_in_addr = 0;
|
||||
|
||||
// get the size of each memory access
|
||||
FOREACH_PORTREF_OF_NET(net, si, pr) {
|
||||
if (pr->GetInst()->Type() == OPER_READ_PORT) {
|
||||
bits_in_word = min<uint64_t>(bits_in_word, pr->GetInst()->OutputSize());
|
||||
continue;
|
||||
auto *inst = pr->GetInst();
|
||||
int bits_in_word;
|
||||
if (inst->Type() == OPER_READ_PORT)
|
||||
bits_in_word = inst->OutputSize();
|
||||
else if (inst->Type() == OPER_WRITE_PORT || inst->Type() == OPER_CLOCKED_WRITE_PORT)
|
||||
bits_in_word = inst->Input2Size();
|
||||
else
|
||||
log_error("%sVerific RamNet %s is connected to unsupported instance type %s (%s).\n", announce_src_location(inst),
|
||||
net->Name(), inst->View()->Owner()->Name(), inst->Name());
|
||||
|
||||
if (bits_in_word < min_bits_in_word) {
|
||||
min_bits_in_word = bits_in_word;
|
||||
max_bits_in_addr = inst->Input1Size();
|
||||
}
|
||||
if (pr->GetInst()->Type() == OPER_WRITE_PORT || pr->GetInst()->Type() == OPER_CLOCKED_WRITE_PORT) {
|
||||
bits_in_word = min<uint64_t>(bits_in_word, pr->GetInst()->Input2Size());
|
||||
continue;
|
||||
}
|
||||
log_error("%sVerific RamNet %s is connected to unsupported instance type %s (%s).\n", announce_src_location(pr->GetInst()),
|
||||
net->Name(), pr->GetInst()->View()->Owner()->Name(), pr->GetInst()->Name());
|
||||
}
|
||||
memory->width = bits_in_word;
|
||||
memory->size = number_of_bits / bits_in_word;
|
||||
|
||||
int number_of_words = number_of_bits / min_bits_in_word;
|
||||
|
||||
memory->width = min_bits_in_word;
|
||||
memory->size = 0;
|
||||
memory->start_offset = INT_MAX;
|
||||
|
||||
const char *ascii_initdata = net->GetWideInitialValue();
|
||||
if (ascii_initdata) {
|
||||
|
|
@ -1793,32 +1860,26 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
log_assert(*ascii_initdata == 'b');
|
||||
ascii_initdata++;
|
||||
}
|
||||
for (int word_idx = 0; word_idx < memory->size; word_idx++) {
|
||||
Const initval = Const(State::Sx, memory->width);
|
||||
bool initval_valid = false;
|
||||
for (int bit_idx = memory->width-1; bit_idx >= 0; bit_idx--) {
|
||||
if (*ascii_initdata == 0)
|
||||
break;
|
||||
if (*ascii_initdata == '0' || *ascii_initdata == '1') {
|
||||
initval.set(bit_idx, (*ascii_initdata == '0') ? State::S0 : State::S1);
|
||||
initval_valid = true;
|
||||
}
|
||||
ascii_initdata++;
|
||||
}
|
||||
if (initval_valid) {
|
||||
RTLIL::Cell *cell = module->addCell(new_verific_id(net), ID($meminit));
|
||||
cell->parameters[ID::WORDS] = 1;
|
||||
if (net->GetOrigTypeRange()->LeftRangeBound() < net->GetOrigTypeRange()->RightRangeBound())
|
||||
cell->setPort(ID::ADDR, word_idx);
|
||||
else
|
||||
cell->setPort(ID::ADDR, memory->size - word_idx - 1);
|
||||
cell->setPort(ID::DATA, initval);
|
||||
cell->parameters[ID::MEMID] = RTLIL::Const(memory->name.str());
|
||||
cell->parameters[ID::ABITS] = 32;
|
||||
cell->parameters[ID::WIDTH] = memory->width;
|
||||
cell->parameters[ID::PRIORITY] = RTLIL::Const(autoidx-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// process initdata and fixup min/max address
|
||||
auto prefix = SigSpec();
|
||||
recurse_mem_dimensions(module, memory, net, ascii_initdata, max_bits_in_addr, prefix);
|
||||
|
||||
auto min_idx = memory->start_offset;
|
||||
auto max_idx = memory->size;
|
||||
memory->size = max_idx - min_idx + 1;
|
||||
|
||||
// sanity check we haven't shrunk the memory
|
||||
if (memory->size < number_of_words)
|
||||
log_error("Expected memory of size %d words, but got %d for address range %d to %d (inclusive)\n", number_of_words, memory->size, min_idx, max_idx);
|
||||
|
||||
// warn on oversize memories
|
||||
// TODO consider using a minimum ratio?
|
||||
if (memory->size > number_of_words) {
|
||||
float ratio = memory->size / (float)number_of_words;
|
||||
log_warning("RAM for identifier '%s' may be up to %.0f%% oversize due to addressing\n", net->Name(), (ratio-1)*100);
|
||||
log_debug("Expected memory of size %d words, but got %d for address range %d to %d (inclusive)\n", number_of_words, memory->size, min_idx, max_idx);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1872,7 +1933,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
RTLIL::IdString wire_name = new_verific_id(net);
|
||||
|
||||
if (verific_verbose)
|
||||
log(" importing net %s as %s.\n", net->Name(), log_id(wire_name));
|
||||
log(" importing net %s as %s.\n", net->Name(), wire_name.unescape());
|
||||
|
||||
RTLIL::Wire *wire = module->addWire(wire_name);
|
||||
import_attributes(wire->attributes, net, nl, 1);
|
||||
|
|
@ -1896,7 +1957,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
RTLIL::IdString wire_name = new_verific_id(netbus);
|
||||
|
||||
if (verific_verbose)
|
||||
log(" importing netbus %s as %s.\n", netbus->Name(), log_id(wire_name));
|
||||
log(" importing netbus %s as %s.\n", netbus->Name(), wire_name.unescape());
|
||||
|
||||
RTLIL::Wire *wire = module->addWire(wire_name, netbus->Size());
|
||||
wire->start_offset = min(netbus->LeftIndex(), netbus->RightIndex());
|
||||
|
|
@ -2021,16 +2082,15 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
pool<Instance*> sva_assumes;
|
||||
pool<Instance*> sva_covers;
|
||||
pool<Instance*> sva_triggers;
|
||||
#endif
|
||||
|
||||
pool<RTLIL::Cell*> past_ffs;
|
||||
#endif
|
||||
|
||||
FOREACH_INSTANCE_OF_NETLIST(nl, mi, inst)
|
||||
{
|
||||
RTLIL::IdString inst_name = new_verific_id(inst);
|
||||
|
||||
if (verific_verbose)
|
||||
log(" importing cell %s (%s) as %s.\n", inst->Name(), inst->View()->Owner()->Name(), log_id(inst_name));
|
||||
log(" importing cell %s (%s) as %s.\n", inst->Name(), inst->View()->Owner()->Name(), inst_name.unescape());
|
||||
|
||||
if (mode_verific)
|
||||
goto import_verific_cells;
|
||||
|
|
@ -2396,7 +2456,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
|
||||
for (auto &it : cell_port_conns) {
|
||||
if (verific_verbose)
|
||||
log(" .%s(%s)\n", log_id(it.first), log_signal(it.second));
|
||||
log(" .%s(%s)\n", it.first.unescape(), log_signal(it.second));
|
||||
cell->setPort(it.first, it.second);
|
||||
}
|
||||
}
|
||||
|
|
@ -3020,6 +3080,7 @@ std::set<std::string> import_tops(const char* work, std::map<std::string,Netlist
|
|||
{
|
||||
std::set<std::string> top_mod_names;
|
||||
Array *netlists = nullptr;
|
||||
(void)top;
|
||||
|
||||
#ifdef VERIFIC_VHDL_SUPPORT
|
||||
VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work, 1);
|
||||
|
|
|
|||
|
|
@ -70,6 +70,9 @@ struct VerificClocking {
|
|||
|
||||
struct VerificImporter
|
||||
{
|
||||
private:
|
||||
void recurse_mem_dimensions(RTLIL::Module *module, RTLIL::Memory *memory, Verific::Net *net, const char *&ascii_initdata, int max_bits_in_addr, const RTLIL::SigSpec &prefix, Verific::TypeRange *typeRange = nullptr);
|
||||
public:
|
||||
RTLIL::Module *module;
|
||||
Verific::Netlist *netlist;
|
||||
|
||||
|
|
|
|||
43
frontends/verilog/CMakeLists.txt
Normal file
43
frontends/verilog/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
if (NOT FLEX_INCLUDE_DIRS)
|
||||
set(FLEX_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/flex)
|
||||
endif()
|
||||
|
||||
flex_target(verilog_lexer
|
||||
verilog_lexer.l
|
||||
verilog_lexer.cc
|
||||
)
|
||||
bison_target(verilog_parser
|
||||
verilog_parser.y
|
||||
verilog_parser.tab.cc
|
||||
# (requires CMake 4.0)
|
||||
# OPTIONS
|
||||
# -Wall -Werror
|
||||
)
|
||||
yosys_frontend(verilog
|
||||
const2ast.cc
|
||||
preproc.cc
|
||||
preproc.h
|
||||
verilog_error.cc
|
||||
verilog_error.h
|
||||
verilog_frontend.cc
|
||||
verilog_frontend.h
|
||||
verilog_lexer.h
|
||||
verilog_location.h
|
||||
${FLEX_verilog_lexer_OUTPUTS}
|
||||
${BISON_verilog_parser_OUTPUTS}
|
||||
INCLUDE_DIRS
|
||||
${FLEX_INCLUDE_DIRS}
|
||||
REQUIRES
|
||||
sha1
|
||||
ast
|
||||
PROVIDES
|
||||
verilog_defaults
|
||||
verilog_defines
|
||||
read_verilog_file_list
|
||||
ESSENTIAL
|
||||
)
|
||||
set_source_files_properties(
|
||||
${BISON_verilog_parser_OUTPUT_SOURCE}
|
||||
PROPERTIES
|
||||
COMPILE_DEFINITIONS -DYYMAXDEPTH=10000000
|
||||
)
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
|
||||
GENFILES += frontends/verilog/verilog_parser.tab.cc
|
||||
GENFILES += frontends/verilog/verilog_parser.tab.hh
|
||||
GENFILES += frontends/verilog/verilog_parser.output
|
||||
GENFILES += frontends/verilog/verilog_lexer.cc
|
||||
|
||||
frontends/verilog/verilog_parser.tab.cc: frontends/verilog/verilog_parser.y
|
||||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) $(BISON) -Wall -Werror -o $@ -d -r all -b frontends/verilog/verilog_parser $<
|
||||
|
||||
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 $<
|
||||
|
||||
frontends/verilog/verilog_parser.tab.o: CXXFLAGS += -DYYMAXDEPTH=10000000
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
int current_function_or_task_port_id;
|
||||
std::vector<char> case_type_stack;
|
||||
bool do_not_require_port_stubs;
|
||||
bool current_wire_rand, current_wire_const;
|
||||
bool current_wire_rand, current_wire_const, current_wire_automatic;
|
||||
bool current_modport_input, current_modport_output;
|
||||
bool default_nettype_wire = true;
|
||||
std::istream* lexin;
|
||||
|
|
@ -546,7 +546,7 @@
|
|||
%token TOK_z "'z'"
|
||||
|
||||
%type <ast_t> range range_or_multirange non_opt_range non_opt_multirange
|
||||
%type <ast_t> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list non_io_wire_type io_wire_type
|
||||
%type <ast_t> wire_type expr basic_expr concat_list assignment_pattern_list rvalue lvalue lvalue_concat_list non_io_wire_type io_wire_type
|
||||
%type <string_t> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id integral_number
|
||||
%type <string_t> type_name
|
||||
%type <ast_t> opt_enum_init enum_type struct_type enum_struct_type func_return_type typedef_base_type
|
||||
|
|
@ -958,14 +958,18 @@ delay:
|
|||
non_opt_delay | %empty;
|
||||
|
||||
io_wire_type:
|
||||
{ extra->astbuf3 = std::make_unique<AstNode>(@$, AST_WIRE); extra->current_wire_rand = false; extra->current_wire_const = false; }
|
||||
{ extra->astbuf3 = std::make_unique<AstNode>(@$, AST_WIRE); extra->current_wire_rand = false; extra->current_wire_const = false; extra->current_wire_automatic = false; }
|
||||
wire_type_token_io wire_type_const_rand opt_wire_type_token wire_type_signedness
|
||||
{ $$ = std::move(extra->astbuf3); SET_RULE_LOC(@$, @2, @$); };
|
||||
|
||||
non_io_wire_type:
|
||||
{ extra->astbuf3 = std::make_unique<AstNode>(@$, AST_WIRE); extra->current_wire_rand = false; extra->current_wire_const = false; }
|
||||
wire_type_const_rand wire_type_token wire_type_signedness
|
||||
{ $$ = std::move(extra->astbuf3); SET_RULE_LOC(@$, @2, @$); };
|
||||
{ extra->astbuf3 = std::make_unique<AstNode>(@$, AST_WIRE); extra->current_wire_rand = false; extra->current_wire_const = false; extra->current_wire_automatic = false; }
|
||||
opt_lifetime wire_type_const_rand wire_type_token wire_type_signedness
|
||||
{
|
||||
if (extra->current_wire_automatic)
|
||||
extra->astbuf3->set_attribute(ID::nosync, AstNode::mkconst_int(extra->astbuf3->location, 1, false));
|
||||
$$ = std::move(extra->astbuf3); SET_RULE_LOC(@$, @2, @$);
|
||||
};
|
||||
|
||||
wire_type:
|
||||
io_wire_type { $$ = std::move($1); } |
|
||||
|
|
@ -1253,6 +1257,10 @@ opt_automatic:
|
|||
TOK_AUTOMATIC |
|
||||
%empty;
|
||||
|
||||
opt_lifetime:
|
||||
TOK_AUTOMATIC { extra->current_wire_automatic = true; } |
|
||||
%empty;
|
||||
|
||||
task_func_args_opt:
|
||||
TOK_LPAREN TOK_RPAREN | %empty | TOK_LPAREN {
|
||||
extra->albuf = nullptr;
|
||||
|
|
@ -3341,6 +3349,11 @@ basic_expr:
|
|||
TOK_LCURL concat_list TOK_RCURL {
|
||||
$$ = std::move($2);
|
||||
} |
|
||||
OP_CAST TOK_LCURL assignment_pattern_list optional_comma TOK_RCURL {
|
||||
if (!mode->sv)
|
||||
err_at_loc(@1, "Assignment patterns are only supported in SystemVerilog mode.");
|
||||
$$ = std::move($3);
|
||||
} |
|
||||
TOK_LCURL expr TOK_LCURL concat_list TOK_RCURL TOK_RCURL {
|
||||
$$ = std::make_unique<AstNode>(@$, AST_REPLICATE, std::move($2), std::move($4));
|
||||
} |
|
||||
|
|
@ -3572,6 +3585,16 @@ concat_list:
|
|||
$$->children.push_back(std::move($1));
|
||||
};
|
||||
|
||||
assignment_pattern_list:
|
||||
expr {
|
||||
$$ = std::make_unique<AstNode>(@$, AST_ASSIGN_PATTERN);
|
||||
$$->children.push_back(std::move($1));
|
||||
} |
|
||||
assignment_pattern_list TOK_COMMA expr {
|
||||
$$ = std::move($1);
|
||||
$$->children.push_back(std::move($3));
|
||||
};
|
||||
|
||||
integral_number:
|
||||
TOK_CONSTVAL { $$ = std::move($1); } |
|
||||
TOK_UNBASED_UNSIZED_CONSTVAL { $$ = std::move($1); } |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue