mirror of
https://github.com/YosysHQ/yosys.git
synced 2026-10-06 01:53:53 +02:00
kernel: twine damage for utilities that key or search on names
This commit is contained in:
+4
-2
@@ -94,7 +94,9 @@ struct AigMaker
|
||||
int inport(IdString portname, int portbit = 0, bool inverter = false)
|
||||
{
|
||||
if (portbit >= GetSize(cell->getPort(portname))) {
|
||||
if (cell->parameters.count(portname.str() + "_SIGNED") && cell->getParam(portname.str() + "_SIGNED").as_bool())
|
||||
IdString signed_param = cell->module->design->twines.find(
|
||||
cell->module->design->twines.str(portname) + "_SIGNED");
|
||||
if (signed_param != IdString::Null && cell->parameters.count(signed_param) && cell->getParam(signed_param).as_bool())
|
||||
return inport(portname, GetSize(cell->getPort(portname))-1, inverter);
|
||||
return bool_node(inverter);
|
||||
}
|
||||
@@ -266,7 +268,7 @@ struct AigMaker
|
||||
|
||||
Aig::Aig(Cell *cell)
|
||||
{
|
||||
if (cell->type[0] != '$')
|
||||
if (cell->type.isPublic())
|
||||
return;
|
||||
|
||||
AigMaker mk(this, cell);
|
||||
|
||||
+9
-3
@@ -65,10 +65,16 @@ struct CellTypes
|
||||
cell_types[ct.type] = ct;
|
||||
}
|
||||
|
||||
template<typename N, YS_NAME_STRING(N)>
|
||||
void setup_type(const N &type_str, const pool<RTLIL::IdString> &inputs, const pool<RTLIL::IdString> &outputs, bool is_evaluable = false, bool is_combinatorial = false, bool is_synthesizable = false)
|
||||
{
|
||||
setup_type(ID::lookup(type_str), inputs, outputs, is_evaluable, is_combinatorial, is_synthesizable);
|
||||
}
|
||||
|
||||
void setup_module(RTLIL::Module *module)
|
||||
{
|
||||
pool<RTLIL::IdString> inputs, outputs;
|
||||
for (RTLIL::IdString wire_name : module->ports) {
|
||||
pool<IdString> inputs, outputs;
|
||||
for (auto wire_name : module->ports) {
|
||||
RTLIL::Wire *wire = module->wire(wire_name);
|
||||
if (wire->port_input)
|
||||
inputs.insert(wire->name);
|
||||
@@ -361,7 +367,7 @@ struct CellTypes
|
||||
signed1 = false, signed2 = false;
|
||||
}
|
||||
|
||||
#define HANDLE_CELL_TYPE(_t) if (type == ID($##_t)) return const_ ## _t(arg1, arg2, signed1, signed2, result_len);
|
||||
#define HANDLE_CELL_TYPE(_t) if (type == ID::$##_t) return const_ ## _t(arg1, arg2, signed1, signed2, result_len);
|
||||
HANDLE_CELL_TYPE(not)
|
||||
HANDLE_CELL_TYPE(and)
|
||||
HANDLE_CELL_TYPE(or)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/ffinit.h"
|
||||
#include "kernel/twine.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
@@ -181,6 +182,9 @@ struct FfData : FfTypeData {
|
||||
pol_set = false;
|
||||
}
|
||||
|
||||
FfData(Module *module, FfInitVals *initvals, Twine &&name)
|
||||
: FfData(module, initvals, module->design->twines.add(std::move(name))) {}
|
||||
|
||||
FfData(FfInitVals *initvals, Cell *cell_);
|
||||
|
||||
// Returns a FF identical to this one, but only keeping bit indices from the argument.
|
||||
|
||||
+3
-2
@@ -29,7 +29,7 @@ bool FfMergeHelper::is_output_unused(RTLIL::SigSpec sig) {
|
||||
}
|
||||
|
||||
bool FfMergeHelper::find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair<Cell *, int>> &bits) {
|
||||
ff = FfData(module, initvals, NEW_ID);
|
||||
ff = FfData(module, initvals, module->design->twines.add(NEW_ID));
|
||||
sigmap->apply(sig);
|
||||
|
||||
bool found = false;
|
||||
@@ -157,7 +157,7 @@ bool FfMergeHelper::find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pai
|
||||
}
|
||||
|
||||
bool FfMergeHelper::find_input_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair<Cell *, int>> &bits) {
|
||||
ff = FfData(module, initvals, NEW_ID);
|
||||
ff = FfData(module, initvals, module->design->twines.add(NEW_ID));
|
||||
sigmap->apply(sig);
|
||||
|
||||
bool found = false;
|
||||
@@ -303,6 +303,7 @@ void FfMergeHelper::remove_output_ff(const pool<std::pair<Cell *, int>> &bits) {
|
||||
dff_driver.erase((*sigmap)(q[idx]));
|
||||
q[idx] = module->addWire(stringf("$ffmerge_disconnected$%d", autoidx++));
|
||||
cell->setPort(ID::Q, q);
|
||||
initvals->set_init(cell->getPort(ID::Q), (*initvals)(q));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-12
@@ -120,8 +120,8 @@ struct PrintVisitor : DefaultVisitor<std::string> {
|
||||
std::string zero_extend(Node, Node a, int out_width) override { return "zero_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; }
|
||||
std::string sign_extend(Node, Node a, int out_width) override { return "sign_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; }
|
||||
std::string constant(Node, RTLIL::Const const& value) override { return "constant(" + value.as_string() + ")"; }
|
||||
std::string input(Node, IdString name, IdString kind) override { return "input(" + name.str() + ", " + kind.str() + ")"; }
|
||||
std::string state(Node, IdString name, IdString kind) override { return "state(" + name.str() + ", " + kind.str() + ")"; }
|
||||
std::string input(Node self, IdString name, IdString kind) override { return "input(" + self.design->twines.str(name) + ", " + self.design->twines.str(kind) + ")"; }
|
||||
std::string state(Node self, IdString name, IdString kind) override { return "state(" + self.design->twines.str(name) + ", " + self.design->twines.str(kind) + ")"; }
|
||||
std::string default_handler(Node self) override {
|
||||
std::string ret = fn_to_string(self.fn());
|
||||
ret += "(";
|
||||
@@ -395,7 +395,7 @@ public:
|
||||
Node y = neg_if(y_flipped, sign(b));
|
||||
return factory.extend(y, y_width, true);
|
||||
} else
|
||||
log_error("unhandled cell in CellSimplifier %s\n", cellType);
|
||||
log_error("unhandled cell in CellSimplifier %s\n", factory.ir().design->twines.str(cellType).c_str());
|
||||
} else {
|
||||
if(cellType.in(ID($mod), ID($modfloor)))
|
||||
return factory.extend(factory.unsigned_mod(a, b), y_width, false);
|
||||
@@ -458,9 +458,9 @@ public:
|
||||
return factory.value(state);
|
||||
}
|
||||
} else if(cellType == ID($check)) {
|
||||
log_error("The design contains a $check cell `%s'. This is not supported by the functional backend. Call `chformal -lower' to avoid this error.\n", cellName);
|
||||
log_error("The design contains a $check cell `%s'. This is not supported by the functional backend. Call `chformal -lower' to avoid this error.\n", factory.ir().design->twines.str(cellName).c_str());
|
||||
} else {
|
||||
log_error("`%s' cells are not supported by the functional backend\n", cellType);
|
||||
log_error("`%s' cells are not supported by the functional backend\n", factory.ir().design->twines.str(cellType).c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -497,7 +497,7 @@ class FunctionalIRConstruction {
|
||||
for(auto const &[name, sigspec] : cell->connections())
|
||||
if(driver_map.celltypes.cell_output(cell->type, name)) {
|
||||
auto node = factory.create_pending(sigspec.size());
|
||||
factory.suggest_name(node, cell->name.str() + "$" + name.str());
|
||||
factory.suggest_name(node, cell->name.str() + "$" + PooledName(cell->module, name).str());
|
||||
cell_outputs.emplace({cell, name}, node);
|
||||
if(name == port_name)
|
||||
rv = node;
|
||||
@@ -572,7 +572,7 @@ private:
|
||||
const auto &wr = mem->wr_ports[i];
|
||||
if (wr.clk_enable)
|
||||
log_error("Write port %zd of memory %s.%s is clocked. This is not supported by the functional backend. "
|
||||
"Call async2sync or clk2fflogic to avoid this error.\n", i, mem->module, mem->memid.unescape());
|
||||
"Call async2sync or clk2fflogic to avoid this error.\n", i, mem->module, PooledName(mem->module, mem->memid).unescape());
|
||||
Node en = enqueue(driver_map(DriveSpec(wr.en)));
|
||||
Node addr = enqueue(driver_map(DriveSpec(wr.addr)));
|
||||
Node new_data = enqueue(driver_map(DriveSpec(wr.data)));
|
||||
@@ -582,12 +582,12 @@ private:
|
||||
}
|
||||
if (mem->rd_ports.empty())
|
||||
log_error("Memory %s.%s has no read ports. This is not supported by the functional backend. "
|
||||
"Call opt_clean to remove it.", mem->module, mem->memid.unescape());
|
||||
"Call opt_clean to remove it.", mem->module, PooledName(mem->module, mem->memid).unescape());
|
||||
for (size_t i = 0; i < mem->rd_ports.size(); i++) {
|
||||
const auto &rd = mem->rd_ports[i];
|
||||
if (rd.clk_enable)
|
||||
log_error("Read port %zd of memory %s.%s is clocked. This is not supported by the functional backend. "
|
||||
"Call memory_nordff to avoid this error.\n", i, mem->module, mem->memid.unescape());
|
||||
"Call memory_nordff to avoid this error.\n", i, mem->module, PooledName(mem->module, mem->memid).unescape());
|
||||
Node addr = enqueue(driver_map(DriveSpec(rd.addr)));
|
||||
read_results.push_back(factory.memory_read(node, addr));
|
||||
}
|
||||
@@ -695,7 +695,7 @@ public:
|
||||
factory.update_pending(pending, node);
|
||||
} else {
|
||||
DriveSpec driver = driver_map(DriveSpec(port_chunk));
|
||||
check_undriven(driver, port_chunk.cell->name.unescape() + " port " + port_chunk.port.unescape());
|
||||
check_undriven(driver, port_chunk.cell->name.unescape() + " port " + PooledName(port_chunk.cell->module, port_chunk.port).unescape());
|
||||
factory.update_pending(pending, enqueue(driver));
|
||||
}
|
||||
} else {
|
||||
@@ -725,6 +725,7 @@ public:
|
||||
|
||||
IR IR::from_module(Module *module) {
|
||||
IR ir;
|
||||
ir.design = module->design;
|
||||
auto factory = ir.factory();
|
||||
FunctionalIRConstruction ctor(module, factory);
|
||||
ctor.process_queue();
|
||||
@@ -743,7 +744,7 @@ void IR::topological_sort() {
|
||||
{
|
||||
log_warning("Combinational loop:\n");
|
||||
for (int *i = begin; i != end; ++i) {
|
||||
Node node(_graph[*i]);
|
||||
Node node(_graph[*i], design);
|
||||
log("- %s = %s\n", node.name().unescape(), node.to_string());
|
||||
}
|
||||
log("\n");
|
||||
@@ -763,7 +764,7 @@ void IR::topological_sort() {
|
||||
}
|
||||
|
||||
static IdString merge_name(IdString a, IdString b) {
|
||||
if(a[0] == '$' && b[0] == '\\')
|
||||
if(!a.isPublic() && b.isPublic())
|
||||
return b;
|
||||
else
|
||||
return a;
|
||||
|
||||
+34
-28
@@ -159,23 +159,21 @@ namespace Functional {
|
||||
class IRInput {
|
||||
friend class Factory;
|
||||
public:
|
||||
IdString name;
|
||||
IdString kind;
|
||||
PooledName name;
|
||||
PooledName kind;
|
||||
Sort sort;
|
||||
private:
|
||||
IRInput(IR &, IdString name, IdString kind, Sort sort)
|
||||
: name(name), kind(kind), sort(std::move(sort)) {}
|
||||
IRInput(IR &ir, IdString name, IdString kind, Sort sort);
|
||||
};
|
||||
class IROutput {
|
||||
friend class Factory;
|
||||
IR &_ir;
|
||||
public:
|
||||
IdString name;
|
||||
IdString kind;
|
||||
PooledName name;
|
||||
PooledName kind;
|
||||
Sort sort;
|
||||
private:
|
||||
IROutput(IR &ir, IdString name, IdString kind, Sort sort)
|
||||
: _ir(ir), name(name), kind(kind), sort(std::move(sort)) {}
|
||||
IROutput(IR &ir, IdString name, IdString kind, Sort sort);
|
||||
public:
|
||||
Node value() const;
|
||||
bool has_value() const;
|
||||
@@ -185,13 +183,12 @@ namespace Functional {
|
||||
friend class Factory;
|
||||
IR &_ir;
|
||||
public:
|
||||
IdString name;
|
||||
IdString kind;
|
||||
PooledName name;
|
||||
PooledName kind;
|
||||
Sort sort;
|
||||
private:
|
||||
std::variant<RTLIL::Const, MemContents> _initial;
|
||||
IRState(IR &ir, IdString name, IdString kind, Sort sort)
|
||||
: _ir(ir), name(name), kind(kind), sort(std::move(sort)) {}
|
||||
IRState(IR &ir, IdString name, IdString kind, Sort sort);
|
||||
public:
|
||||
Node next_value() const;
|
||||
bool has_next_value() const;
|
||||
@@ -249,6 +246,7 @@ namespace Functional {
|
||||
dict<std::pair<IdString, IdString>, IRState> _states;
|
||||
IR::Graph::Ref mutate(Node n);
|
||||
public:
|
||||
Design *design = nullptr;
|
||||
static IR from_module(Module *module);
|
||||
Factory factory();
|
||||
int size() const { return _graph.size(); }
|
||||
@@ -301,24 +299,25 @@ namespace Functional {
|
||||
friend class IROutput;
|
||||
friend class IRState;
|
||||
IR::Graph::ConstRef _ref;
|
||||
explicit Node(IR::Graph::ConstRef ref) : _ref(ref) { }
|
||||
Node(IR::Graph::ConstRef ref, Design *design) : _ref(ref), design(design) { }
|
||||
explicit operator IR::Graph::ConstRef() { return _ref; }
|
||||
public:
|
||||
Design *design = nullptr;
|
||||
// the node's index. may change if nodes are added or removed
|
||||
int id() const { return _ref.index(); }
|
||||
// a name suggestion for the node, which need not be unique
|
||||
IdString name() const {
|
||||
PooledName name() const {
|
||||
if(_ref.has_sparse_attr())
|
||||
return _ref.sparse_attr();
|
||||
return {design, _ref.sparse_attr()};
|
||||
else
|
||||
return std::string("\\n") + std::to_string(id());
|
||||
return {design, design->twines.add(std::string("\\n") + std::to_string(id()))};
|
||||
}
|
||||
Fn fn() const { return _ref.function().fn(); }
|
||||
Sort sort() const { return _ref.attr().sort; }
|
||||
// returns the width of a bitvector node, errors out for other nodes
|
||||
int width() const { return sort().width(); }
|
||||
size_t arg_count() const { return _ref.size(); }
|
||||
Node arg(int n) const { return Node(_ref.arg(n)); }
|
||||
Node arg(int n) const { return Node(_ref.arg(n), design); }
|
||||
// visit calls the appropriate visitor method depending on the type of the node
|
||||
template<class Visitor> auto visit(Visitor v) const
|
||||
{
|
||||
@@ -364,15 +363,21 @@ namespace Functional {
|
||||
std::string to_string();
|
||||
std::string to_string(std::function<std::string(Node)>);
|
||||
};
|
||||
inline IRInput::IRInput(IR &ir, IdString name, IdString kind, Sort sort)
|
||||
: name(ir.design, name), kind(ir.design, kind), sort(std::move(sort)) {}
|
||||
inline IROutput::IROutput(IR &ir, IdString name, IdString kind, Sort sort)
|
||||
: _ir(ir), name(ir.design, name), kind(ir.design, kind), sort(std::move(sort)) {}
|
||||
inline IRState::IRState(IR &ir, IdString name, IdString kind, Sort sort)
|
||||
: _ir(ir), name(ir.design, name), kind(ir.design, kind), sort(std::move(sort)) {}
|
||||
inline IR::Graph::Ref IR::mutate(Node n) { return _graph[n._ref.index()]; }
|
||||
inline Node IR::operator[](int i) { return Node(_graph[i]); }
|
||||
inline Node IROutput::value() const { return Node(_ir._graph({name, kind, false})); }
|
||||
inline Node IR::operator[](int i) { return Node(_graph[i], design); }
|
||||
inline Node IROutput::value() const { return Node(_ir._graph({name, kind, false}), _ir.design); }
|
||||
inline bool IROutput::has_value() const { return _ir._graph.has_key({name, kind, false}); }
|
||||
inline void IROutput::set_value(Node value) { log_assert(sort == value.sort()); _ir.mutate(value).assign_key({name, kind, false}); }
|
||||
inline Node IRState::next_value() const { return Node(_ir._graph({name, kind, true})); }
|
||||
inline Node IRState::next_value() const { return Node(_ir._graph({name, kind, true}), _ir.design); }
|
||||
inline bool IRState::has_next_value() const { return _ir._graph.has_key({name, kind, true}); }
|
||||
inline void IRState::set_next_value(Node value) { log_assert(sort == value.sort()); _ir.mutate(value).assign_key({name, kind, true}); }
|
||||
inline Node IR::iterator::operator*() { return Node(_ir->_graph[_index]); }
|
||||
inline Node IR::iterator::operator*() { return Node(_ir->_graph[_index], _ir->design); }
|
||||
inline arrow_proxy<Node> IR::iterator::operator->() { return arrow_proxy<Node>(**this); }
|
||||
// AbstractVisitor provides an abstract base class for visitors
|
||||
template<class T> struct AbstractVisitor {
|
||||
@@ -458,7 +463,7 @@ namespace Functional {
|
||||
IR::Graph::Ref ref = _ir._graph.add(std::move(fn), {std::move(sort)});
|
||||
for (auto arg : args)
|
||||
ref.append_arg(IR::Graph::ConstRef(arg));
|
||||
return Node(ref);
|
||||
return Node(ref, _ir.design);
|
||||
}
|
||||
void check_basic_binary(Node const &a, Node const &b) { log_assert(a.sort().is_signal() && a.sort() == b.sort()); }
|
||||
void check_shift(Node const &a, Node const &b) { log_assert(a.sort().is_signal() && b.sort().is_signal() && b.width() == ceil_log2(a.width())); }
|
||||
@@ -552,17 +557,17 @@ namespace Functional {
|
||||
}
|
||||
IRInput &add_input(IdString name, IdString kind, Sort sort) {
|
||||
auto [it, inserted] = _ir._inputs.emplace({name, kind}, IRInput(_ir, name, kind, std::move(sort)));
|
||||
if (!inserted) log_error("input `%s` was re-defined", name);
|
||||
if (!inserted) log_error("input `%s` was re-defined", _ir.design->twines.str(name).c_str());
|
||||
return it->second;
|
||||
}
|
||||
IROutput &add_output(IdString name, IdString kind, Sort sort) {
|
||||
auto [it, inserted] = _ir._outputs.emplace({name, kind}, IROutput(_ir, name, kind, std::move(sort)));
|
||||
if (!inserted) log_error("output `%s` was re-defined", name);
|
||||
if (!inserted) log_error("output `%s` was re-defined", _ir.design->twines.str(name).c_str());
|
||||
return it->second;
|
||||
}
|
||||
IRState &add_state(IdString name, IdString kind, Sort sort) {
|
||||
auto [it, inserted] = _ir._states.emplace({name, kind}, IRState(_ir, name, kind, std::move(sort)));
|
||||
if (!inserted) log_error("state `%s` was re-defined", name);
|
||||
if (!inserted) log_error("state `%s` was re-defined", _ir.design->twines.str(name).c_str());
|
||||
return it->second;
|
||||
}
|
||||
Node value(IRInput const& input) {
|
||||
@@ -574,6 +579,7 @@ namespace Functional {
|
||||
void suggest_name(Node node, IdString name) {
|
||||
_ir.mutate(node).sparse_attr() = name;
|
||||
}
|
||||
YS_NAME_FWD_2ND_POOL(suggest_name, _ir.design->twines)
|
||||
};
|
||||
inline Factory IR::factory() { return Factory(*this); }
|
||||
template<class Id> class Scope {
|
||||
@@ -587,8 +593,8 @@ namespace Functional {
|
||||
void reserve(std::string name) {
|
||||
_used_names.insert(std::move(name));
|
||||
}
|
||||
std::string unique_name(IdString suggestion) {
|
||||
std::string str = suggestion.unescape();
|
||||
template<typename N> std::string unique_name(const N &suggestion) {
|
||||
std::string str = RTLIL::unescape_id(suggestion);
|
||||
for(size_t i = 0; i < str.size(); i++)
|
||||
if(!is_character_legal(str[i], i))
|
||||
str[i] = substitution_character;
|
||||
@@ -604,7 +610,7 @@ namespace Functional {
|
||||
}
|
||||
}
|
||||
}
|
||||
std::string operator()(Id id, IdString suggestion) {
|
||||
template<typename N> std::string operator()(Id id, const N &suggestion) {
|
||||
auto it = _by_id.find(id);
|
||||
if(it != _by_id.end())
|
||||
return it->second;
|
||||
|
||||
+10
-81
@@ -55,9 +55,6 @@ std::chrono::steady_clock::time_point LogManager::get_initial_time() const
|
||||
void (*log_error_atexit)() = nullptr;
|
||||
void (*log_verific_callback)(int msg_type, const char *message_id, LogSourceLocation src, const char *msg) = nullptr;
|
||||
|
||||
// TODO: remove when log_id is removed
|
||||
vector<char*> log_id_cache;
|
||||
|
||||
static bool next_print_log = true;
|
||||
|
||||
FileLogSink::FileLogSink(const std::string &filename, bool line_buffered, bool append)
|
||||
@@ -161,13 +158,6 @@ LogMessage::LogMessage(LogSeverity severity, LogSourceLocation src, std::string_
|
||||
cached_msg = stringf("%s%s%s%s", time_str, loc, prefix, message);
|
||||
}
|
||||
|
||||
static void log_id_cache_clear()
|
||||
{
|
||||
for (auto p : log_id_cache)
|
||||
free(p);
|
||||
log_id_cache.clear();
|
||||
}
|
||||
|
||||
void LogManager::logv_string(LogSeverity severity, LogSourceLocation src, std::string_view prefix, std::string_view format, std::string str_in) {
|
||||
size_t remove_leading = 0;
|
||||
while (format.size() > 1 && format[0] == '\n') {
|
||||
@@ -270,8 +260,6 @@ void LogManager::formatted_header(RTLIL::Design *design, std::string_view format
|
||||
if (hdump.count(header_id) && design != nullptr)
|
||||
for (auto &filename : hdump.at(header_id)) {
|
||||
log("Dumping current design to '%s'.\n", filename);
|
||||
if (yosys_xtrace)
|
||||
IdString::xtrace_db_dump();
|
||||
Pass::call(design, {"dump", "-o", filename});
|
||||
if (yosys_xtrace)
|
||||
log("#X# -- end of dump --\n");
|
||||
@@ -453,7 +441,6 @@ void LogManager::push()
|
||||
void LogManager::pop()
|
||||
{
|
||||
header_count.pop_back();
|
||||
log_id_cache_clear();
|
||||
flush();
|
||||
}
|
||||
|
||||
@@ -558,12 +545,16 @@ void LogManager::reset_stack()
|
||||
{
|
||||
while (header_count.size() > 1)
|
||||
header_count.pop_back();
|
||||
log_id_cache_clear();
|
||||
flush();
|
||||
}
|
||||
|
||||
void log_dump_val_worker(RTLIL::IdString v) {
|
||||
log("%s", v.unescape());
|
||||
if (v == IdString::Null)
|
||||
log("(null)");
|
||||
else if (ID::is_static(v))
|
||||
log("%s", ID::str(v));
|
||||
else
|
||||
log("%s", v.handle_token().c_str());
|
||||
}
|
||||
|
||||
void log_dump_val_worker(RTLIL::SigSpec v) {
|
||||
@@ -577,7 +568,7 @@ void log_dump_val_worker(RTLIL::State v) {
|
||||
std::string log_signal(const RTLIL::SigSpec &sig, bool autoint)
|
||||
{
|
||||
std::stringstream buf;
|
||||
RTLIL_BACKEND::dump_sigspec(buf, sig, autoint);
|
||||
RTLIL_BACKEND::dump_sigspec(buf, sig, autoint, RTLIL_BACKEND::DumpMode::Readable);
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
@@ -589,86 +580,24 @@ std::string log_const(const RTLIL::Const &value, bool autoint)
|
||||
return "\"" + value.decode_string() + "\"";
|
||||
}
|
||||
|
||||
const char *log_id_str(const std::string &str)
|
||||
{
|
||||
log_id_cache.push_back(strdup(str.c_str()));
|
||||
return log_id_cache.back();
|
||||
}
|
||||
|
||||
static const char *log_id_cached(std::string unescaped)
|
||||
{
|
||||
log_id_cache.push_back(strdup(unescaped.c_str()));
|
||||
return log_id_cache.back();
|
||||
}
|
||||
|
||||
const char *log_id(const IdString &str)
|
||||
{
|
||||
if (str == IdString::Null)
|
||||
return log_id_cached(std::string());
|
||||
if (!ID::is_static(str))
|
||||
return log_id_cached(stringf("$twine$%zu", str.untag().raw()));
|
||||
return log_id_cached(RTLIL::unescape_id(ID::str(str)));
|
||||
}
|
||||
|
||||
static const char *log_id_twine(const RTLIL::Design *design, IdString name)
|
||||
{
|
||||
return log_id_cached(RTLIL::unescape_id(design->twines.str(name)));
|
||||
}
|
||||
|
||||
const char *log_id(const RTLIL::Design *design, IdString name)
|
||||
{
|
||||
return log_id_twine(design, name);
|
||||
}
|
||||
|
||||
const char *log_id(const RTLIL::Module *module, IdString name)
|
||||
{
|
||||
return log_id_twine(module->design, name);
|
||||
}
|
||||
|
||||
const char *log_id(const RTLIL::Module *obj, const char *nullstr)
|
||||
{
|
||||
if (nullstr && obj == nullptr) return nullstr;
|
||||
return log_id_twine(obj->design, obj->name);
|
||||
}
|
||||
const char *log_id(const RTLIL::Cell *obj, const char *nullstr)
|
||||
{
|
||||
if (nullstr && obj == nullptr) return nullstr;
|
||||
return log_id_twine(obj->module->design, obj->name);
|
||||
}
|
||||
const char *log_id(const RTLIL::Wire *obj, const char *nullstr)
|
||||
{
|
||||
if (nullstr && obj == nullptr) return nullstr;
|
||||
return log_id_twine(obj->module->design, obj->name);
|
||||
}
|
||||
const char *log_id(const RTLIL::Memory *obj, const char *nullstr)
|
||||
{
|
||||
if (nullstr && obj == nullptr) return nullstr;
|
||||
return log_id_twine(obj->module->design, obj->name);
|
||||
}
|
||||
const char *log_id(const RTLIL::Process *obj, const char *nullstr)
|
||||
{
|
||||
if (nullstr && obj == nullptr) return nullstr;
|
||||
return log_id_twine(obj->module->design, obj->name);
|
||||
}
|
||||
|
||||
void log_module(RTLIL::Module *module, std::string indent)
|
||||
{
|
||||
std::stringstream buf;
|
||||
RTLIL_BACKEND::dump_module(buf, indent, module, module->design, false);
|
||||
RTLIL_BACKEND::dump_module(buf, indent, module, module->design, false, true, false, RTLIL_BACKEND::DumpMode::Readable);
|
||||
log("%s", buf.str());
|
||||
}
|
||||
|
||||
void log_cell(RTLIL::Cell *cell, std::string indent)
|
||||
{
|
||||
std::stringstream buf;
|
||||
RTLIL_BACKEND::dump_cell(buf, indent, cell);
|
||||
RTLIL_BACKEND::dump_cell(buf, indent, cell, cell->module ? cell->module->design : nullptr, RTLIL_BACKEND::DumpMode::Readable);
|
||||
log("%s", buf.str());
|
||||
}
|
||||
|
||||
void log_wire(RTLIL::Wire *wire, std::string indent)
|
||||
{
|
||||
std::stringstream buf;
|
||||
RTLIL_BACKEND::dump_wire(buf, indent, wire);
|
||||
RTLIL_BACKEND::dump_wire(buf, indent, wire, wire->module ? wire->module->design : nullptr, RTLIL_BACKEND::DumpMode::Readable);
|
||||
log("%s", buf.str());
|
||||
}
|
||||
|
||||
|
||||
+1
-19
@@ -590,29 +590,11 @@ void log_backtrace(const char *prefix, int levels);
|
||||
|
||||
std::string log_signal(const RTLIL::SigSpec &sig, bool autoint = true);
|
||||
std::string log_const(const RTLIL::Const &value, bool autoint = true);
|
||||
const char *log_id(const RTLIL::IdString &id);
|
||||
|
||||
template<typename T> static inline const char *log_id(T *obj, const char *nullstr = nullptr) {
|
||||
if (nullstr && obj == nullptr)
|
||||
return nullstr;
|
||||
return log_id(obj->name);
|
||||
}
|
||||
|
||||
void log_module(RTLIL::Module *module, std::string indent = "");
|
||||
void log_cell(RTLIL::Cell *cell, std::string indent = "");
|
||||
void log_wire(RTLIL::Wire *wire, std::string indent = "");
|
||||
|
||||
[[noreturn]]
|
||||
void log_assert_failure(const char *expr, const char *file, int line);
|
||||
#ifndef NDEBUG
|
||||
static inline void log_assert_worker(bool cond, const char *expr, const char *file, int line) {
|
||||
if (!cond) log_assert_failure(expr, file, line);
|
||||
}
|
||||
# define log_assert(_assert_expr_) YOSYS_NAMESPACE_PREFIX log_assert_worker(_assert_expr_, #_assert_expr_, __FILE__, __LINE__)
|
||||
#else
|
||||
# define log_assert(_assert_expr_) do { if (0) { (void)(_assert_expr_); } } while(0)
|
||||
#endif
|
||||
|
||||
[[noreturn]]
|
||||
void log_abort_internal(const char *file, int line);
|
||||
#define log_abort() YOSYS_NAMESPACE_PREFIX log_abort_internal(__FILE__, __LINE__)
|
||||
@@ -713,7 +695,7 @@ static inline void log_dump_val_worker(const char *v) { log("%s", v); }
|
||||
static inline void log_dump_val_worker(std::string v) { log("%s", v); }
|
||||
static inline void log_dump_val_worker(PerformanceTimer p) { log("%f seconds", p.sec()); }
|
||||
static inline void log_dump_args_worker(const char *p) { log_assert(*p == 0); }
|
||||
void log_dump_val_worker(RTLIL::IdString v);
|
||||
void log_dump_val_worker(IdString v);
|
||||
void log_dump_val_worker(RTLIL::SigSpec v);
|
||||
void log_dump_val_worker(RTLIL::State v);
|
||||
|
||||
|
||||
+28
-25
@@ -122,7 +122,7 @@ void Mem::emit() {
|
||||
}
|
||||
if (!cell) {
|
||||
if (memid.empty())
|
||||
memid = NEW_ID;
|
||||
memid = PooledName(module->design, module->design->twines.add(NEW_ID));
|
||||
cell = module->addCell(memid, ID($mem_v2));
|
||||
}
|
||||
cell->type = ID($mem_v2);
|
||||
@@ -291,10 +291,8 @@ void Mem::emit() {
|
||||
}
|
||||
if (!mem) {
|
||||
if (memid.empty())
|
||||
memid = NEW_ID;
|
||||
mem = new RTLIL::Memory;
|
||||
mem->name = memid;
|
||||
module->memories[memid] = mem;
|
||||
memid = PooledName(module->design, module->design->twines.add(NEW_ID));
|
||||
mem = module->addMemory(memid);
|
||||
}
|
||||
mem->width = width;
|
||||
mem->start_offset = start_offset;
|
||||
@@ -349,8 +347,11 @@ void Mem::emit() {
|
||||
bool v2 = !init.en.is_fully_ones();
|
||||
if (!init.cell)
|
||||
init.cell = module->addCell(NEW_ID, v2 ? ID($meminit_v2) : ID($meminit));
|
||||
else
|
||||
else {
|
||||
if (!v2)
|
||||
init.cell->unsetPort(ID::EN);
|
||||
init.cell->type = v2 ? ID($meminit_v2) : ID($meminit);
|
||||
}
|
||||
init.cell->attributes = init.attributes;
|
||||
init.cell->parameters[ID::MEMID] = memid.str();
|
||||
init.cell->parameters[ID::ABITS] = GetSize(init.addr);
|
||||
@@ -361,8 +362,6 @@ void Mem::emit() {
|
||||
init.cell->setPort(ID::DATA, init.data);
|
||||
if (v2)
|
||||
init.cell->setPort(ID::EN, init.en);
|
||||
else
|
||||
init.cell->unsetPort(ID::EN);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -544,9 +543,9 @@ void Mem::check() {
|
||||
namespace {
|
||||
|
||||
struct MemIndex {
|
||||
dict<IdString, pool<Cell *>> rd_ports;
|
||||
dict<IdString, pool<Cell *>> wr_ports;
|
||||
dict<IdString, pool<Cell *>> inits;
|
||||
dict<std::string, pool<Cell *>> rd_ports;
|
||||
dict<std::string, pool<Cell *>> wr_ports;
|
||||
dict<std::string, pool<Cell *>> inits;
|
||||
MemIndex (Module *module) {
|
||||
for (auto cell: module->cells()) {
|
||||
if (cell->type.in(ID($memwr), ID($memwr_v2)))
|
||||
@@ -560,14 +559,15 @@ namespace {
|
||||
};
|
||||
|
||||
Mem mem_from_memory(Module *module, RTLIL::Memory *mem, const MemIndex &index) {
|
||||
std::string memid = mem->name.str();
|
||||
Mem res(module, mem->name, mem->width, mem->start_offset, mem->size);
|
||||
res.packed = false;
|
||||
res.mem = mem;
|
||||
res.attributes = mem->attributes;
|
||||
std::vector<bool> rd_transparent;
|
||||
std::vector<int> wr_portid;
|
||||
if (index.rd_ports.count(mem->name)) {
|
||||
for (auto cell : index.rd_ports.at(mem->name)) {
|
||||
if (index.rd_ports.count(memid)) {
|
||||
for (auto cell : index.rd_ports.at(memid)) {
|
||||
MemRd mrd;
|
||||
bool is_compat = cell->type == ID($memrd);
|
||||
mrd.cell = cell;
|
||||
@@ -609,9 +609,9 @@ namespace {
|
||||
rd_transparent.push_back(transparent);
|
||||
}
|
||||
}
|
||||
if (index.wr_ports.count(mem->name)) {
|
||||
if (index.wr_ports.count(memid)) {
|
||||
std::vector<std::pair<int, MemWr>> ports;
|
||||
for (auto cell : index.wr_ports.at(mem->name)) {
|
||||
for (auto cell : index.wr_ports.at(memid)) {
|
||||
MemWr mwr;
|
||||
bool is_compat = cell->type == ID($memwr);
|
||||
mwr.cell = cell;
|
||||
@@ -654,9 +654,9 @@ namespace {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index.inits.count(mem->name)) {
|
||||
if (index.inits.count(memid)) {
|
||||
std::vector<std::pair<int, MemInit>> inits;
|
||||
for (auto cell : index.inits.at(mem->name)) {
|
||||
for (auto cell : index.inits.at(memid)) {
|
||||
MemInit init;
|
||||
init.cell = cell;
|
||||
init.attributes = cell->attributes;
|
||||
@@ -716,7 +716,7 @@ namespace {
|
||||
}
|
||||
|
||||
Mem mem_from_cell(Cell *cell) {
|
||||
Mem res(cell->module, cell->parameters.at(ID::MEMID).decode_string(),
|
||||
Mem res(cell->module, cell->module->design->twines.add(cell->parameters.at(ID::MEMID).decode_string()),
|
||||
cell->parameters.at(ID::WIDTH).as_int(),
|
||||
cell->parameters.at(ID::OFFSET).as_int(),
|
||||
cell->parameters.at(ID::SIZE).as_int()
|
||||
@@ -887,6 +887,9 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
|
||||
if (!port.clk_enable)
|
||||
return nullptr;
|
||||
|
||||
log_assert(module && module->design);
|
||||
std::string memid_str = memid.str();
|
||||
|
||||
Cell *c;
|
||||
|
||||
// There are two ways to handle rdff extraction when transparency is involved:
|
||||
@@ -923,7 +926,7 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
|
||||
|
||||
if (width)
|
||||
{
|
||||
SigSpec sig_q = module->addWire(stringf("$%s$rdreg[%d]$q", memid, idx), width);
|
||||
SigSpec sig_q = module->addWire(stringf("$%s$rdreg[%d]$q", memid_str, idx), width);
|
||||
SigSpec sig_d;
|
||||
|
||||
int pos = 0;
|
||||
@@ -933,7 +936,7 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
|
||||
port.addr[i] = sig_q[pos++];
|
||||
}
|
||||
|
||||
c = module->addDff(stringf("$%s$rdreg[%d]", memid, idx), port.clk, sig_d, sig_q, port.clk_polarity);
|
||||
c = module->addDff(stringf("$%s$rdreg[%d]", memid_str, idx), port.clk, sig_d, sig_q, port.clk_polarity);
|
||||
} else {
|
||||
c = nullptr;
|
||||
}
|
||||
@@ -942,7 +945,7 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
|
||||
{
|
||||
log_assert(port.arst == State::S0 || port.srst == State::S0);
|
||||
|
||||
SigSpec async_d = module->addWire(stringf("$%s$rdreg[%d]$d", memid, idx), GetSize(port.data));
|
||||
SigSpec async_d = module->addWire(stringf("$%s$rdreg[%d]$d", memid_str, idx), GetSize(port.data));
|
||||
SigSpec sig_d = async_d;
|
||||
|
||||
for (int i = 0; i < GetSize(wr_ports); i++) {
|
||||
@@ -965,7 +968,7 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
|
||||
raddr = port.sub_addr(sub);
|
||||
SigSpec addr_eq;
|
||||
if (raddr != waddr)
|
||||
addr_eq = module->Eq(stringf("$%s$rdtransen[%d][%d][%d]$d", memid, idx, i, sub), raddr, waddr);
|
||||
addr_eq = module->Eq(stringf("$%s$rdtransen[%d][%d][%d]$d", memid_str, idx, i, sub), raddr, waddr);
|
||||
int pos = 0;
|
||||
int ewidth = width << min_wide_log2;
|
||||
int wsub = wide_write ? sub : 0;
|
||||
@@ -978,10 +981,10 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
|
||||
SigSpec other = port.transparency_mask[i] ? wport.data.extract(pos + wsub * width, epos-pos) : Const(State::Sx, epos-pos);
|
||||
SigSpec cond;
|
||||
if (raddr != waddr)
|
||||
cond = module->And(stringf("$%s$rdtransgate[%d][%d][%d][%d]$d", memid, idx, i, sub, pos), wport.en[pos + wsub * width], addr_eq);
|
||||
cond = module->And(stringf("$%s$rdtransgate[%d][%d][%d][%d]$d", memid_str, idx, i, sub, pos), wport.en[pos + wsub * width], addr_eq);
|
||||
else
|
||||
cond = wport.en[pos + wsub * width];
|
||||
SigSpec merged = module->Mux(stringf("$%s$rdtransmux[%d][%d][%d][%d]$d", memid, idx, i, sub, pos), cur, other, cond);
|
||||
SigSpec merged = module->Mux(stringf("$%s$rdtransmux[%d][%d][%d][%d]$d", memid_str, idx, i, sub, pos), cur, other, cond);
|
||||
sig_d.replace(pos + rsub * width, merged);
|
||||
pos = epos;
|
||||
}
|
||||
@@ -989,7 +992,7 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
|
||||
}
|
||||
}
|
||||
|
||||
IdString name = stringf("$%s$rdreg[%d]", memid, idx);
|
||||
IdString name = module->design->twines.add(stringf("$%s$rdreg[%d]", memid_str, idx));
|
||||
FfData ff(module, initvals, name);
|
||||
ff.width = GetSize(port.data);
|
||||
ff.has_clk = true;
|
||||
|
||||
+4
-2
@@ -91,7 +91,7 @@ struct MemInit : RTLIL::AttrObject {
|
||||
|
||||
struct Mem : RTLIL::AttrObject {
|
||||
Module *module;
|
||||
IdString memid;
|
||||
PooledName memid;
|
||||
bool packed;
|
||||
RTLIL::Memory *mem;
|
||||
Cell *cell;
|
||||
@@ -222,7 +222,9 @@ struct Mem : RTLIL::AttrObject {
|
||||
// in the same clock domain.
|
||||
void emulate_read_first(FfInitVals *initvals);
|
||||
|
||||
Mem(Module *module, IdString memid, int width, int start_offset, int size) : module(module), memid(memid), packed(false), mem(nullptr), cell(nullptr), width(width), start_offset(start_offset), size(size) {}
|
||||
Mem(Module *module, IdString memid, int width, int start_offset, int size) : module(module), memid(module->design, memid), packed(false), mem(nullptr), cell(nullptr), width(width), start_offset(start_offset), size(size) {}
|
||||
Mem(Module *module, Twine &&memid, int width, int start_offset, int size)
|
||||
: Mem(module, module->design->twines.add(std::move(memid)), width, start_offset, size) {}
|
||||
};
|
||||
|
||||
// MemContents efficiently represents the contents of a potentially sparse memory by storing only those segments that are actually defined
|
||||
|
||||
+28
-6
@@ -1043,6 +1043,17 @@ RTLIL::Module *RTLIL::Design::module(IdString id) {
|
||||
return modules_.count(id) ? modules_.at(id) : NULL;
|
||||
}
|
||||
|
||||
RTLIL::Module *RTLIL::module_by_name(RTLIL::Design *design, const std::string &name)
|
||||
{
|
||||
if (name.empty())
|
||||
return nullptr;
|
||||
std::string escaped = escape_id(name);
|
||||
for (auto module : design->modules())
|
||||
if (design->twines.name_equal(module->name, escaped))
|
||||
return module;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RTLIL::Module *RTLIL::Design::top_module() const
|
||||
{
|
||||
RTLIL::Module *module = nullptr;
|
||||
@@ -1434,17 +1445,17 @@ size_t RTLIL::Module::count_id(RTLIL::IdString id)
|
||||
return wires_.count(id) + cells_.count(id) + memories.count(id) + processes.count(id);
|
||||
}
|
||||
|
||||
pool<std::string> RTLIL::Module::object_names() const
|
||||
pool<std::string> RTLIL::object_names(const RTLIL::Module *module)
|
||||
{
|
||||
const TwinePool &twines = design->twines;
|
||||
const TwinePool &twines = module->design->twines;
|
||||
pool<std::string> names;
|
||||
for (auto &it : wires_)
|
||||
for (auto &it : module->wires_)
|
||||
names.insert(twines.str(it.first));
|
||||
for (auto &it : cells_)
|
||||
for (auto &it : module->cells_)
|
||||
names.insert(twines.str(it.first));
|
||||
for (auto &it : memories)
|
||||
for (auto &it : module->memories)
|
||||
names.insert(twines.str(it.first));
|
||||
for (auto &it : processes)
|
||||
for (auto &it : module->processes)
|
||||
names.insert(twines.str(it.first));
|
||||
return names;
|
||||
}
|
||||
@@ -3065,6 +3076,17 @@ void RTLIL::copy_attr_dict(dict<IdString, RTLIL::Const> &dst,
|
||||
}
|
||||
}
|
||||
|
||||
RTLIL::Wire *RTLIL::wire_by_name(RTLIL::Module *module, const std::string &name)
|
||||
{
|
||||
if (name.empty())
|
||||
return nullptr;
|
||||
std::string escaped = escape_id(name);
|
||||
for (auto wire : module->wires())
|
||||
if (module->design->twines.name_equal(wire->name, escaped))
|
||||
return wire;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RTLIL::Wire *RTLIL::Module::addWire(RTLIL::IdString name, int width)
|
||||
{
|
||||
log_assert(design);
|
||||
|
||||
@@ -1507,7 +1507,6 @@ public:
|
||||
virtual RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, bool mayfail = false);
|
||||
virtual RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, const dict<RTLIL::IdString, RTLIL::Module*> &interfaces, const dict<RTLIL::IdString, RTLIL::IdString> &modports, bool mayfail = false);
|
||||
virtual size_t count_id(RTLIL::IdString id);
|
||||
pool<std::string> object_names() const;
|
||||
virtual void expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces);
|
||||
virtual bool reprocess_if_necessary(RTLIL::Design *design);
|
||||
|
||||
|
||||
@@ -137,6 +137,10 @@ private:
|
||||
Module *owner();
|
||||
};
|
||||
|
||||
Module *module_by_name(Design *design, const std::string &name);
|
||||
Wire *wire_by_name(Module *module, const std::string &name);
|
||||
pool<std::string> object_names(const Module *module);
|
||||
|
||||
struct PooledName : NameMasqBase<PooledName> {
|
||||
PooledName() = default;
|
||||
explicit PooledName(IdString id) : id_(id) {}
|
||||
|
||||
+16
-6
@@ -100,13 +100,19 @@ static const char *attr_prefix(ScopeinfoAttrs attrs)
|
||||
bool scopeinfo_has_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, RTLIL::IdString id)
|
||||
{
|
||||
log_assert(scopeinfo->type == ID($scopeinfo));
|
||||
return scopeinfo->has_attribute(attr_prefix(attrs) + id.unescape());
|
||||
TwinePool &twines = scopeinfo->module->design->twines;
|
||||
IdString key = twines.find(attr_prefix(attrs) + twines.unescaped_str(id));
|
||||
return key != IdString::Null && scopeinfo->has_attribute(key);
|
||||
}
|
||||
|
||||
RTLIL::Const scopeinfo_get_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, RTLIL::IdString id)
|
||||
{
|
||||
log_assert(scopeinfo->type == ID($scopeinfo));
|
||||
auto found = scopeinfo->attributes.find(attr_prefix(attrs) + id.unescape());
|
||||
TwinePool &twines = scopeinfo->module->design->twines;
|
||||
IdString key = twines.find(attr_prefix(attrs) + twines.unescaped_str(id));
|
||||
if (key == IdString::Null)
|
||||
return RTLIL::Const();
|
||||
auto found = scopeinfo->attributes.find(key);
|
||||
if (found == scopeinfo->attributes.end())
|
||||
return RTLIL::Const();
|
||||
return found->second;
|
||||
@@ -117,11 +123,15 @@ dict<RTLIL::IdString, RTLIL::Const> scopeinfo_attributes(const RTLIL::Cell *scop
|
||||
dict<RTLIL::IdString, RTLIL::Const> attributes;
|
||||
|
||||
const char *prefix = attr_prefix(attrs);
|
||||
int prefix_len = strlen(prefix);
|
||||
size_t prefix_len = strlen(prefix);
|
||||
TwinePool &twines = scopeinfo->module->design->twines;
|
||||
|
||||
for (auto const &entry : scopeinfo->attributes)
|
||||
if (entry.first.begins_with(prefix))
|
||||
attributes.emplace(RTLIL::escape_id(entry.first.c_str() + prefix_len), entry.second);
|
||||
for (auto const &entry : scopeinfo->attributes) {
|
||||
std::string name = twines.str(entry.first);
|
||||
if (name.compare(0, prefix_len, prefix) != 0)
|
||||
continue;
|
||||
attributes.emplace(twines.add(RTLIL::escape_id(name.substr(prefix_len))), entry.second);
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
+17
-12
@@ -219,12 +219,12 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string path_str() {
|
||||
std::string path_str(const RTLIL::Design *design) {
|
||||
std::string result;
|
||||
for (const auto &item : path()) {
|
||||
if (!result.empty())
|
||||
result.push_back(' ');
|
||||
result += RTLIL::unescape_id(item);
|
||||
result += design->twines.unescaped_str(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -328,7 +328,7 @@ struct ModuleItem {
|
||||
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(ptr); return h; }
|
||||
};
|
||||
|
||||
static inline void log_dump_val_worker(typename IdTree<ModuleItem>::Cursor cursor ) { log("%p %s", cursor.target, cursor.scope_name.unescape()); }
|
||||
static inline void log_dump_val_worker(typename IdTree<ModuleItem>::Cursor cursor ) { log("%p ", cursor.target); log_dump_val_worker(cursor.scope_name); }
|
||||
|
||||
template<typename T>
|
||||
static inline void log_dump_val_worker(const typename std::unique_ptr<T> &cursor ) { log("unique %p", cursor.get()); }
|
||||
@@ -336,14 +336,18 @@ static inline void log_dump_val_worker(const typename std::unique_ptr<T> &cursor
|
||||
template<typename O>
|
||||
std::vector<IdString> parse_hdlname(const O* object)
|
||||
{
|
||||
TwinePool &twines = object->design()->twines;
|
||||
IdString name = object->name;
|
||||
std::vector<IdString> path;
|
||||
for (auto const &item : object->get_hdlname_attribute())
|
||||
path.push_back("\\" + item);
|
||||
if (path.empty() && object->name.isPublic())
|
||||
path.push_back(object->name);
|
||||
if (!path.empty() && !(object->name.isPublic() || object->name.begins_with("$paramod") || object->name.begins_with("$abstract"))) {
|
||||
path.push_back(twines.add("\\" + item));
|
||||
bool synthetic = !name.isPublic() && !object->name.begins_with("$paramod")
|
||||
&& !object->name.begins_with("$abstract");
|
||||
if (path.empty() && name.isPublic())
|
||||
path.push_back(name);
|
||||
if (!path.empty() && synthetic) {
|
||||
path.pop_back();
|
||||
path.push_back(object->name);
|
||||
path.push_back(name);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
@@ -351,24 +355,25 @@ std::vector<IdString> parse_hdlname(const O* object)
|
||||
template<typename O>
|
||||
std::pair<std::vector<IdString>, IdString> parse_scopename(const O* object)
|
||||
{
|
||||
TwinePool &twines = object->design()->twines;
|
||||
std::vector<IdString> path;
|
||||
IdString trailing = object->name;
|
||||
if (object->name.isPublic() || object->name.begins_with("$paramod") || object->name.begins_with("$abstract")) {
|
||||
if (trailing.isPublic() || object->name.begins_with("$paramod") || object->name.begins_with("$abstract")) {
|
||||
for (auto const &item : object->get_hdlname_attribute())
|
||||
path.push_back("\\" + item);
|
||||
path.push_back(twines.add("\\" + item));
|
||||
if (!path.empty()) {
|
||||
trailing = path.back();
|
||||
path.pop_back();
|
||||
}
|
||||
} else if (object->has_attribute(ID::hdlname)) {
|
||||
for (auto const &item : object->get_hdlname_attribute())
|
||||
path.push_back("\\" + item);
|
||||
path.push_back(twines.add("\\" + item));
|
||||
if (!path.empty()) {
|
||||
path.pop_back();
|
||||
}
|
||||
} else {
|
||||
for (auto const &item : split_tokens(object->get_string_attribute(ID(scopename)), " "))
|
||||
path.push_back("\\" + item);
|
||||
path.push_back(twines.add("\\" + item));
|
||||
}
|
||||
return {path, trailing};
|
||||
}
|
||||
|
||||
+64
-42
@@ -250,13 +250,15 @@ static int tcl_get_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
|
||||
ERROR("bad usage: expected \"get_attr -mod [-string|-int|-sint|-uint|-bool] <module> <attrname>\""
|
||||
" or \"get_attr [-string|-int|-sint|-uint|-bool] <module> <identifier> <attrname>\"")
|
||||
|
||||
IdString mod_id, obj_id, attr_id;
|
||||
std::string mod_id, obj_id, attr_id;
|
||||
mod_id = RTLIL::escape_id(argv[i++]);
|
||||
if (!mod_flag)
|
||||
obj_id = RTLIL::escape_id(argv[i++]);
|
||||
attr_id = RTLIL::escape_id(argv[i++]);
|
||||
|
||||
RTLIL::Module *mod = yosys_design->module(mod_id);
|
||||
TwineSearch search(&yosys_design->twines);
|
||||
auto mod_twine = search.find(mod_id);
|
||||
RTLIL::Module *mod = yosys_design->module(mod_twine);
|
||||
if (!mod)
|
||||
ERROR("module not found")
|
||||
|
||||
@@ -264,36 +266,39 @@ static int tcl_get_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
|
||||
if (mod_flag) {
|
||||
obj = mod;
|
||||
} else {
|
||||
obj = mod->wire(obj_id);
|
||||
auto obj_twine = search.find(obj_id);
|
||||
obj = mod->wire(obj_twine);
|
||||
if (!obj)
|
||||
obj = mod->memories.at(obj_id, nullptr);
|
||||
obj = mod->memories.at(obj_twine, nullptr);
|
||||
if (!obj)
|
||||
obj = mod->cell(obj_id);
|
||||
obj = mod->cell(obj_twine);
|
||||
if (!obj)
|
||||
obj = mod->processes.at(obj_id, nullptr);
|
||||
obj = mod->processes.at(obj_twine, nullptr);
|
||||
}
|
||||
|
||||
if (!obj)
|
||||
ERROR("object not found")
|
||||
|
||||
IdString attr_twine = search.find(attr_id);
|
||||
|
||||
if (string_flag) {
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(obj->get_string_attribute(attr_id).c_str(), -1));
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(obj->get_string_attribute(attr_twine).c_str(), -1));
|
||||
} else if (int_flag || uint_flag || sint_flag) {
|
||||
if (!obj->has_attribute(attr_id))
|
||||
if (!obj->has_attribute(attr_twine))
|
||||
ERROR("attribute missing (required for -int)");
|
||||
RTLIL::Const &value = obj->attributes.at(attr_id);
|
||||
RTLIL::Const &value = obj->attributes.at(attr_twine);
|
||||
|
||||
mp_int value_mp;
|
||||
if (!const_to_mp_int(value, &value_mp, sint_flag, uint_flag))
|
||||
ERROR("bignum manipulation failed");
|
||||
Tcl_SetObjResult(interp, Tcl_NewBignumObj(&value_mp));
|
||||
} else if (bool_flag) {
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(obj->get_bool_attribute(attr_id)));
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(obj->get_bool_attribute(attr_twine)));
|
||||
} else {
|
||||
if (!obj->has_attribute(attr_id))
|
||||
if (!obj->has_attribute(attr_twine))
|
||||
ERROR("attribute missing (required unless -bool or -string)")
|
||||
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(obj->attributes.at(attr_id).as_string().c_str(), -1));
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(obj->attributes.at(attr_twine).as_string().c_str(), -1));
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
@@ -313,13 +318,15 @@ static int tcl_has_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
|
||||
ERROR("bad usage: expected \"has_attr -mod <module> <attrname>\""
|
||||
" or \"has_attr <module> <identifier> <attrname>\"")
|
||||
|
||||
IdString mod_id, obj_id, attr_id;
|
||||
std::string mod_id, obj_id, attr_id;
|
||||
mod_id = RTLIL::escape_id(argv[i++]);
|
||||
if (!mod_flag)
|
||||
obj_id = RTLIL::escape_id(argv[i++]);
|
||||
attr_id = RTLIL::escape_id(argv[i++]);
|
||||
|
||||
RTLIL::Module *mod = yosys_design->module(mod_id);
|
||||
TwineSearch search(&yosys_design->twines);
|
||||
auto mod_twine = search.find(mod_id);
|
||||
RTLIL::Module *mod = yosys_design->module(mod_twine);
|
||||
if (!mod)
|
||||
ERROR("module not found")
|
||||
|
||||
@@ -327,19 +334,20 @@ static int tcl_has_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
|
||||
if (mod_flag) {
|
||||
obj = mod;
|
||||
} else {
|
||||
obj = mod->wire(obj_id);
|
||||
auto obj_twine = search.find(obj_id);
|
||||
obj = mod->wire(obj_twine);
|
||||
if (!obj)
|
||||
obj = mod->memories.at(obj_id, nullptr);
|
||||
obj = mod->memories.at(obj_twine, nullptr);
|
||||
if (!obj)
|
||||
obj = mod->cell(obj_id);
|
||||
obj = mod->cell(obj_twine);
|
||||
if (!obj)
|
||||
obj = mod->processes.at(obj_id, nullptr);
|
||||
obj = mod->processes.at(obj_twine, nullptr);
|
||||
}
|
||||
|
||||
if (!obj)
|
||||
ERROR("object not found")
|
||||
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(std::to_string(obj->has_attribute(attr_id)).c_str(), -1));
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(std::to_string(obj->has_attribute(search.find(attr_id))).c_str(), -1));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
@@ -366,13 +374,15 @@ static int tcl_set_attr(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj *const
|
||||
" or \"set_attr [-true|-false] <module> <identifier> <attrname>\""
|
||||
" or \"set_attr -mod [-true|-false| <module> <attrname>\"")
|
||||
|
||||
IdString mod_id, obj_id, attr_id;
|
||||
std::string mod_id, obj_id, attr_id;
|
||||
mod_id = RTLIL::escape_id(Tcl_GetString(objv[i++]));
|
||||
if (!mod_flag)
|
||||
obj_id = RTLIL::escape_id(Tcl_GetString(objv[i++]));
|
||||
attr_id = RTLIL::escape_id(Tcl_GetString(objv[i++]));
|
||||
|
||||
RTLIL::Module *mod = yosys_design->module(mod_id);
|
||||
TwineSearch search(&yosys_design->twines);
|
||||
auto mod_twine = search.find(mod_id);
|
||||
RTLIL::Module *mod = yosys_design->module(mod_twine);
|
||||
if (!mod)
|
||||
ERROR("module not found")
|
||||
|
||||
@@ -380,20 +390,23 @@ static int tcl_set_attr(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj *const
|
||||
if (mod_flag) {
|
||||
obj = mod;
|
||||
} else {
|
||||
obj = mod->wire(obj_id);
|
||||
auto obj_twine = search.find(obj_id);
|
||||
obj = mod->wire(obj_twine);
|
||||
if (!obj)
|
||||
obj = mod->memories.at(obj_id, nullptr);
|
||||
obj = mod->memories.at(obj_twine, nullptr);
|
||||
if (!obj)
|
||||
obj = mod->cell(obj_id);
|
||||
obj = mod->cell(obj_twine);
|
||||
if (!obj)
|
||||
obj = mod->processes.at(obj_id, nullptr);
|
||||
obj = mod->processes.at(obj_twine, nullptr);
|
||||
}
|
||||
|
||||
if (!obj)
|
||||
ERROR("object not found")
|
||||
|
||||
IdString attr_twine = yosys_design->twines.add(std::string(attr_id));
|
||||
|
||||
if (string_flag) {
|
||||
obj->set_string_attribute(attr_id, Tcl_GetString(objv[i++]));
|
||||
obj->set_string_attribute(attr_twine, Tcl_GetString(objv[i++]));
|
||||
} else if (sint_flag || uint_flag) {
|
||||
RTLIL::Const const_;
|
||||
mp_int value_mp;
|
||||
@@ -413,15 +426,15 @@ static int tcl_set_attr(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj *const
|
||||
const_.extu(32);
|
||||
}
|
||||
|
||||
obj->attributes[attr_id] = const_;
|
||||
obj->attributes[attr_twine] = const_;
|
||||
} else if (bool_flag) {
|
||||
obj->set_bool_attribute(attr_id, atoi(Tcl_GetString(objv[i++])) != 0);
|
||||
obj->set_bool_attribute(attr_twine, atoi(Tcl_GetString(objv[i++])) != 0);
|
||||
} else if (true_flag) {
|
||||
obj->set_bool_attribute(attr_id, true);
|
||||
obj->set_bool_attribute(attr_twine, true);
|
||||
} else if (false_flag) {
|
||||
obj->set_bool_attribute(attr_id, false);
|
||||
obj->set_bool_attribute(attr_twine, false);
|
||||
} else {
|
||||
obj->attributes[attr_id] = Const::from_string(std::string(Tcl_GetString(objv[i++])));
|
||||
obj->attributes[attr_twine] = Const::from_string(std::string(Tcl_GetString(objv[i++])));
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
@@ -444,23 +457,27 @@ static int tcl_get_param(ClientData, Tcl_Interp *interp, int argc, const char *a
|
||||
(string_flag + int_flag > 1))
|
||||
ERROR("bad usage: expected \"get_param [-string|-int|-sint|-uint] <module> <cellid> <paramname>")
|
||||
|
||||
IdString mod_id, cell_id, param_id;
|
||||
std::string mod_id, cell_id, param_id;
|
||||
mod_id = RTLIL::escape_id(argv[i++]);
|
||||
cell_id = RTLIL::escape_id(argv[i++]);
|
||||
param_id = RTLIL::escape_id(argv[i++]);
|
||||
|
||||
RTLIL::Module *mod = yosys_design->module(mod_id);
|
||||
TwineSearch search(&yosys_design->twines);
|
||||
auto mod_twine = search.find(mod_id);
|
||||
RTLIL::Module *mod = yosys_design->module(mod_twine);
|
||||
if (!mod)
|
||||
ERROR("module not found")
|
||||
|
||||
RTLIL::Cell *cell = mod->cell(cell_id);
|
||||
auto cell_twine = search.find(cell_id);
|
||||
Cell* cell = mod->cell(cell_twine);
|
||||
if (!cell)
|
||||
ERROR("object not found")
|
||||
|
||||
if (!cell->hasParam(param_id))
|
||||
IdString param_twine = search.find(param_id);
|
||||
if (!cell->hasParam(param_twine))
|
||||
ERROR("parameter missing")
|
||||
|
||||
const RTLIL::Const &value = cell->getParam(param_id);
|
||||
const RTLIL::Const &value = cell->getParam(param_twine);
|
||||
|
||||
if (string_flag) {
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(value.decode_string().c_str(), -1));
|
||||
@@ -490,21 +507,26 @@ static int tcl_set_param(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj *cons
|
||||
(string_flag + sint_flag + uint_flag > 1))
|
||||
ERROR("bad usage: expected \"set_param [-string|-sint|-uint] <module> <cellid> <paramname> <value>")
|
||||
|
||||
IdString mod_id, cell_id, param_id;
|
||||
std::string mod_id, cell_id, param_id;
|
||||
mod_id = RTLIL::escape_id(Tcl_GetString(objv[i++]));
|
||||
cell_id = RTLIL::escape_id(Tcl_GetString(objv[i++]));
|
||||
param_id = RTLIL::escape_id(Tcl_GetString(objv[i++]));
|
||||
|
||||
RTLIL::Module *mod = yosys_design->module(mod_id);
|
||||
TwineSearch search(&yosys_design->twines);
|
||||
auto mod_twine = search.find(mod_id);
|
||||
RTLIL::Module *mod = yosys_design->module(mod_twine);
|
||||
if (!mod)
|
||||
ERROR("module not found")
|
||||
|
||||
RTLIL::Cell *cell = mod->cell(cell_id);
|
||||
auto cell_twine = search.find(cell_id);
|
||||
RTLIL::Cell *cell = mod->cell(cell_twine);
|
||||
if (!cell)
|
||||
ERROR("object not found")
|
||||
|
||||
IdString param_twine = yosys_design->twines.add(std::string(param_id));
|
||||
|
||||
if (string_flag) {
|
||||
cell->setParam(param_id, Const(std::string(Tcl_GetString(objv[i++]))));
|
||||
cell->setParam(param_twine, Const(std::string(Tcl_GetString(objv[i++]))));
|
||||
} else if (sint_flag || uint_flag) {
|
||||
RTLIL::Const const_;
|
||||
mp_int value_mp;
|
||||
@@ -524,9 +546,9 @@ static int tcl_set_param(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj *cons
|
||||
const_.extu(32);
|
||||
}
|
||||
|
||||
cell->setParam(param_id, const_);
|
||||
cell->setParam(param_twine, const_);
|
||||
} else {
|
||||
cell->setParam(param_id, Const::from_string(std::string(Tcl_GetString(objv[i++]))));
|
||||
cell->setParam(param_twine, Const::from_string(std::string(Tcl_GetString(objv[i++]))));
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,22 @@ int twine_gc_count;
|
||||
|
||||
Hasher IdString::hash_into(Hasher h) const { h.hash64(value); return h; }
|
||||
|
||||
std::string IdString::handle_token() const {
|
||||
return stringf("%s@%zu", isPublic() ? "$pub" : "$priv", untag().raw());
|
||||
}
|
||||
|
||||
size_t IdString::handle_token_prefix(std::string_view token, bool &is_public) {
|
||||
if (token.substr(0, 5) == "$pub@") {
|
||||
is_public = true;
|
||||
return 5;
|
||||
}
|
||||
if (token.substr(0, 6) == "$priv@") {
|
||||
is_public = false;
|
||||
return 6;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string ID::str(IdString ref) {
|
||||
IdString idx = ref.untag();
|
||||
log_assert(idx.raw() < STATIC_TWINE_END);
|
||||
|
||||
@@ -91,6 +91,9 @@ public:
|
||||
}
|
||||
|
||||
Hasher hash_into(Hasher h) const;
|
||||
|
||||
std::string handle_token() const;
|
||||
static size_t handle_token_prefix(std::string_view token, bool &is_public);
|
||||
};
|
||||
|
||||
constexpr NullIdString::operator IdString() const { return IdString(); }
|
||||
|
||||
+20
-12
@@ -200,7 +200,6 @@ struct shared_str {
|
||||
};
|
||||
|
||||
namespace RTLIL {
|
||||
struct IdString;
|
||||
struct Const;
|
||||
struct SigBit;
|
||||
struct SigSpec;
|
||||
@@ -213,18 +212,16 @@ namespace RTLIL {
|
||||
struct Monitor;
|
||||
struct Selection;
|
||||
struct SigChunk;
|
||||
struct PooledName;
|
||||
enum State : unsigned char;
|
||||
|
||||
typedef std::pair<SigSpec, SigSpec> SigSig;
|
||||
|
||||
namespace ID {}
|
||||
}
|
||||
|
||||
namespace AST {
|
||||
struct AstNode;
|
||||
}
|
||||
|
||||
using RTLIL::IdString;
|
||||
using RTLIL::Const;
|
||||
using RTLIL::SigBit;
|
||||
using RTLIL::SigSpec;
|
||||
@@ -236,6 +233,7 @@ using RTLIL::Design;
|
||||
using RTLIL::State;
|
||||
using RTLIL::SigChunk;
|
||||
using RTLIL::SigSig;
|
||||
using RTLIL::PooledName;
|
||||
|
||||
namespace hashlib {
|
||||
template<> struct hash_ops<RTLIL::Wire*> : hash_obj_ops {};
|
||||
@@ -267,6 +265,17 @@ inline void memhasher() { if (memhasher_active) memhasher_do(); }
|
||||
void yosys_banner();
|
||||
int ceil_log2(int x) YS_ATTRIBUTE(const);
|
||||
|
||||
[[noreturn]]
|
||||
void log_assert_failure(const char *expr, const char *file, int line);
|
||||
#ifndef NDEBUG
|
||||
static inline void log_assert_worker(bool cond, const char *expr, const char *file, int line) {
|
||||
if (!cond) log_assert_failure(expr, file, line);
|
||||
}
|
||||
# define log_assert(_assert_expr_) YOSYS_NAMESPACE_PREFIX log_assert_worker(_assert_expr_, #_assert_expr_, __FILE__, __LINE__)
|
||||
#else
|
||||
# define log_assert(_assert_expr_) do { if (0) { (void)(_assert_expr_); } } while(0)
|
||||
#endif
|
||||
|
||||
template<typename T> int GetSize(const T &obj) { return obj.size(); }
|
||||
inline int GetSize(RTLIL::Wire *wire);
|
||||
|
||||
@@ -298,18 +307,17 @@ extern int yosys_xtrace;
|
||||
extern bool yosys_write_versions;
|
||||
|
||||
const std::string *create_id_prefix(std::string_view file, int line, std::string_view func);
|
||||
RTLIL::IdString new_id_suffix(std::string_view file, int line, std::string_view func, std::string_view suffix);
|
||||
|
||||
#define NEW_ID \
|
||||
YOSYS_NAMESPACE_PREFIX RTLIL::IdString::new_autoidx_with_prefix([](std::string_view func) -> const std::string * { \
|
||||
YOSYS_NAMESPACE_PREFIX Twine{YOSYS_NAMESPACE_PREFIX Twine::AutoSuffix{[](std::string_view func) -> std::string_view { \
|
||||
static std::unique_ptr<const std::string> prefix(YOSYS_NAMESPACE_PREFIX create_id_prefix(__FILE__, __LINE__, func)); \
|
||||
return prefix.get(); \
|
||||
}(__FUNCTION__))
|
||||
return *prefix; \
|
||||
}(__FUNCTION__), std::to_string(YOSYS_NAMESPACE_PREFIX autoidx++)}}
|
||||
#define NEW_ID_SUFFIX(suffix) \
|
||||
YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix)
|
||||
|
||||
namespace ID = RTLIL::ID;
|
||||
|
||||
YOSYS_NAMESPACE_PREFIX Twine{YOSYS_NAMESPACE_PREFIX Twine::AutoSuffix{[](std::string_view func) -> std::string_view { \
|
||||
static std::unique_ptr<const std::string> prefix(YOSYS_NAMESPACE_PREFIX create_id_prefix(__FILE__, __LINE__, func)); \
|
||||
return *prefix; \
|
||||
}(__FUNCTION__), std::string(suffix) + "$" + std::to_string(YOSYS_NAMESPACE_PREFIX autoidx++)}}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
|
||||
Reference in New Issue
Block a user