3
0
Fork 0
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:
Mohamed Gaber 2026-06-09 16:22:51 +03:00
commit e58125b605
No known key found for this signature in database
834 changed files with 25281 additions and 8780 deletions

10
frontends/CMakeLists.txt Normal file
View 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)

View file

@ -0,0 +1,6 @@
yosys_frontend(aigerparse
aigerparse.cc
aigerparse.h
REQUIRES
opt_clean
)

View file

@ -1,3 +0,0 @@
OBJS += frontends/aiger/aigerparse.o

View file

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

View file

@ -0,0 +1,3 @@
yosys_frontend(xaiger2
xaiger.cc
)

View file

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

View file

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

View 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
)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,8 @@
yosys_frontend(blif
blifparse.cc
blifparse.h
DATA_DIR
include/frontends/blif
DATA_FILES
blifparse.h
)

View file

@ -1,3 +0,0 @@
OBJS += frontends/blif/blifparse.o

View file

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

View file

@ -0,0 +1,3 @@
yosys_frontend(json
jsonparse.cc
)

View file

@ -1,3 +0,0 @@
OBJS += frontends/json/jsonparse.o

View file

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

View file

@ -0,0 +1,5 @@
yosys_frontend(liberty
liberty.cc
REQUIRES
libparse
)

View file

@ -1,3 +0,0 @@
OBJS += frontends/liberty/liberty.o

View file

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

View file

@ -0,0 +1,8 @@
yosys_frontend(rpc
rpc_frontend.cc
REQUIRES
json11
sha1
ENABLE_IF
YOSYS_ENABLE_SPAWN
)

View file

@ -1,3 +0,0 @@
ifeq ($(DISABLE_SPAWN),0)
OBJS += frontends/rpc/rpc_frontend.o
endif

View file

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

View file

@ -0,0 +1,3 @@
yosys_frontend(rtlil
rtlil_frontend.cc
)

View file

@ -1 +0,0 @@
OBJS += frontends/rtlil/rtlil_frontend.o

View file

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

View 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}
)

View file

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

View file

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

View file

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

View 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
)

View file

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

View file

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