3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-07-15 11:45:41 +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

189
kernel/CMakeLists.txt Normal file
View file

@ -0,0 +1,189 @@
yosys_version_file(version.cc.in version.cc)
configure_file(yosys_config.h.in yosys_config.h @ONLY)
set(cellhelp_sources)
foreach (library simlib simcells)
add_custom_command(
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/cellhelp.py ${CMAKE_SOURCE_DIR}/techlibs/common/${library}.v
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${library}_help.inc
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/cellhelp.py
${CMAKE_SOURCE_DIR}/techlibs/common/${library}.v
> ${CMAKE_CURRENT_BINARY_DIR}/${library}_help.inc
VERBATIM
)
list(APPEND cellhelp_sources ${CMAKE_CURRENT_BINARY_DIR}/${library}_help.inc)
endforeach()
yosys_core(kernel
binding.cc
binding.h
bitpattern.h
calc.cc
cellaigs.cc
cellaigs.h
celledges.cc
celledges.h
celltypes.h
compute_graph.h
consteval.h
constids.inc
cost.cc
cost.h
drivertools.cc
drivertools.h
ff.cc
ff.h
ffinit.h
ffmerge.cc
ffmerge.h
fmt.cc
fmt.h
functional.cc
functional.h
gzip.cc
gzip.h
hashlib.h
io.cc
io.h
json.cc
json.h
log.cc
log.h
$<${YOSYS_ENABLE_VERIFIC}:log_compat.cc>
log_help.cc
log_help.h
macc.h
mem.cc
mem.h
modtools.h
newcelltypes.h
pattern.h
qcsat.cc
qcsat.h
register.cc
register.h
${cellhelp_sources}
rtlil_bufnorm.cc
rtlil.cc
rtlil.h
satgen.cc
satgen.h
scopeinfo.cc
scopeinfo.h
sexpr.cc
sexpr.h
sigtools.h
tclapi.cc
threading.cc
threading.h
timinginfo.h
topo_scc.h
utils.h
version.cc
wallace_tree.h
yosys.cc
yosys_common.h
yosys_config.h
yosys.h
yw.cc
yw.h
INCLUDE_DIRS
${pybind11_INCLUDE_DIR}
LIBRARIES
cxxopts
$<${YOSYS_ENABLE_PLUGINS}:${Dlfcn_LIBRARIES}>
$<${YOSYS_ENABLE_ZLIB}:PkgConfig::zlib>
$<${YOSYS_ENABLE_READLINE}:PkgConfig::readline>
$<${YOSYS_ENABLE_EDITLINE}:PkgConfig::editline>
$<${YOSYS_ENABLE_TCL}:PkgConfig::tcl>
$<${YOSYS_ENABLE_PYTHON}:Python3::Module>
REQUIRES
bigint
ezsat
json11
sha1
PROVIDES
help
echo
license
tcl
shell
history
script
DATA_DIR
include/kernel
DATA_FILES
binding.h
bitpattern.h
cellaigs.h
celledges.h
celltypes.h
newcelltypes.h
consteval.h
constids.inc
cost.h
drivertools.h
ff.h
ffinit.h
ffmerge.h
fmt.h
gzip.h
hashlib.h
io.h
json.h
log.h
macc.h
modtools.h
mem.h
qcsat.h
register.h
rtlil.h
satgen.h
scopeinfo.h
sexpr.h
sigtools.h
threading.h
timinginfo.h
utils.h
yosys.h
yosys_common.h
yw.h
DATA_EXPLICIT
yosys_config.h ${CMAKE_CURRENT_BINARY_DIR}/yosys_config.h
ESSENTIAL
)
set(yosys_cc_definitions
"$<$<BOOL:${YOSYS_ABC_EXECUTABLE}>:ABCEXTERNAL=\"${YOSYS_ABC_EXECUTABLE}\">"
$<$<BOOL:${MSYS}>:YOSYS_WIN32_UNIX_DIR>
)
set_source_files_properties(yosys.cc PROPERTIES
COMPILE_DEFINITIONS "${yosys_cc_definitions}"
)
yosys_core(fstdata
fstdata.cc
fstdata.h
REQUIRES
fst
DATA_DIR
include/kernel
DATA_FILES
fstdata.h
)
if (NOT YOSYS_BUILD_PYTHON_ONLY)
yosys_core(driver
driver.cc
INCLUDE_DIRS
${pybind11_INCLUDE_DIR}
LIBRARIES
$<${YOSYS_ENABLE_READLINE}:PkgConfig::readline>
$<${YOSYS_ENABLE_EDITLINE}:PkgConfig::editline>
$<${YOSYS_ENABLE_TCL}:PkgConfig::tcl>
$<${YOSYS_ENABLE_PYTHON}:Python3::Python>
REQUIRES
essentials
BOOTSTRAP
)
endif()

View file

@ -291,7 +291,7 @@ static RTLIL::Const const_shift_worker(const RTLIL::Const &arg1, const RTLIL::Co
if (pos < 0)
result.set(i, vacant_bits);
else if (pos >= BigInteger(GetSize(arg1)))
result.set(i, sign_ext ? arg1.back() : vacant_bits);
result.set(i, sign_ext && !arg1.empty() ? arg1.back() : vacant_bits);
else
result.set(i, arg1[pos.toInt()]);
}

View file

@ -66,14 +66,7 @@ struct AigMaker
Cell *cell;
idict<AigNode> aig_indices;
int the_true_node;
int the_false_node;
AigMaker(Aig *aig, Cell *cell) : aig(aig), cell(cell)
{
the_true_node = -1;
the_false_node = -1;
}
AigMaker(Aig *aig, Cell *cell) : aig(aig), cell(cell) {}
int node2index(const AigNode &node)
{

100
kernel/cellhelp.py Normal file
View file

@ -0,0 +1,100 @@
#!/usr/bin/env python3
from __future__ import annotations
import fileinput
import json
from pathlib import Path
class SimHelper:
name: str = ""
title: str = ""
ports: str = ""
source: str = ""
desc: list[str]
code: list[str]
group: str = ""
ver: str = "1"
tags: list[str]
def __init__(self) -> None:
self.desc = []
self.tags = []
def __str__(self) -> str:
printed_fields = [
"name", "title", "ports", "source", "desc", "code", "group", "ver",
"tags",
]
# generate C++ struct
val = f"cell_help[{json.dumps(self.name)}] = "
val += "{\n"
for field in printed_fields:
field_val = getattr(self, field)
if isinstance(field_val, list):
field_val = "\n".join(field_val)
field_val = field_val.strip()
val += f' {json.dumps(field_val)},\n'
val += "};\n"
return val
def simcells_reparse(cell: SimHelper):
# cut manual signature
cell.desc = cell.desc[3:]
# code-block truth table
new_desc = []
indent = ""
for line in cell.desc:
if line.startswith("Truth table:"):
indent = " "
new_desc.pop()
new_desc.extend(["::", ""])
new_desc.append(indent + line)
cell.desc = new_desc
# set version
cell.ver = "2a"
simHelper = SimHelper()
for line in fileinput.input():
line = line.rstrip()
# special comments
if line.startswith("//-"):
simHelper.desc.append(line[4:] if len(line) > 4 else "")
elif line.startswith("//* "):
_, key, val = line.split(maxsplit=2)
setattr(simHelper, key, val)
# code parsing
if line.startswith("module "):
clean_line = line[7:].replace("\\", "").replace(";", "")
simHelper.name, simHelper.ports = clean_line.split(maxsplit=1)
simHelper.code = []
short_filename = Path(fileinput.filename()).name
simHelper.source = f'{short_filename}:{fileinput.filelineno()}'
elif not line.startswith("endmodule"):
line = " " + line
try:
simHelper.code.append(line.replace("\t", " "))
except AttributeError:
# no module definition, ignore line
pass
if line.startswith("endmodule"):
short_filename = Path(fileinput.filename()).name
if simHelper.ver == "1" and short_filename == "simcells.v":
# default simcells parsing
simcells_reparse(simHelper)
# check help
if simHelper.desc and simHelper.ver == "1" and short_filename == "simlib.v" and simHelper.desc[1].startswith(' '):
simHelper.desc.pop(1)
# check group
assert simHelper.group, f"techlibs/common/{simHelper.source}: {simHelper.name} cell missing group"
# dump
print(simHelper)
# new
simHelper = SimHelper()

View file

@ -21,6 +21,7 @@
#define CELLTYPES_H
#include "kernel/yosys.h"
#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN
@ -87,22 +88,22 @@ struct CellTypes
{
setup_internals_eval();
setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y}, true);
setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y});
setup_type(ID($assert), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
setup_type(ID($assume), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
setup_type(ID($live), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
setup_type(ID($fair), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
setup_type(ID($cover), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
setup_type(ID($initstate), pool<RTLIL::IdString>(), {ID::Y}, true);
setup_type(ID($anyconst), pool<RTLIL::IdString>(), {ID::Y}, true);
setup_type(ID($anyseq), pool<RTLIL::IdString>(), {ID::Y}, true);
setup_type(ID($allconst), pool<RTLIL::IdString>(), {ID::Y}, true);
setup_type(ID($allseq), pool<RTLIL::IdString>(), {ID::Y}, true);
setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y}, true);
setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, pool<RTLIL::IdString>(), true);
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool<RTLIL::IdString>(), true);
setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool<RTLIL::IdString>(), true);
setup_type(ID($assert), {ID::A, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($assume), {ID::A, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($live), {ID::A, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($fair), {ID::A, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($cover), {ID::A, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($initstate), pool<RTLIL::IdString>(), {ID::Y});
setup_type(ID($anyconst), pool<RTLIL::IdString>(), {ID::Y});
setup_type(ID($anyseq), pool<RTLIL::IdString>(), {ID::Y});
setup_type(ID($allconst), pool<RTLIL::IdString>(), {ID::Y});
setup_type(ID($allseq), pool<RTLIL::IdString>(), {ID::Y});
setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y});
setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, pool<RTLIL::IdString>());
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool<RTLIL::IdString>());
setup_type(ID($specrule), {ID::SRC_EN, ID::DST_EN, ID::SRC, ID::DST}, pool<RTLIL::IdString>());
setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>());
setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>());
setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y});
@ -196,7 +197,7 @@ struct CellTypes
{
setup_stdcells_eval();
setup_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y}, true);
setup_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y});
}
void setup_stdcells_eval()
@ -549,9 +550,6 @@ struct CellTypes
}
};
// initialized by yosys_setup()
extern CellTypes yosys_celltypes;
YOSYS_NAMESPACE_END
#endif

View file

@ -24,6 +24,7 @@
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/macc.h"
#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN
@ -44,9 +45,8 @@ struct ConstEval
ConstEval(RTLIL::Module *module, RTLIL::State defaultval = RTLIL::State::Sm) : module(module), assign_map(module), defaultval(defaultval)
{
CellTypes ct;
ct.setup_internals();
ct.setup_stdcells();
auto ct = NewCellTypes();
ct.static_cell_types = StaticCellTypes::Compat::nomem_noff;
for (auto &it : module->cells_) {
if (!ct.cell_known(it.second->type))

View file

@ -460,9 +460,7 @@ X(EDGE_POL)
X(EFX_ADD)
X(EN)
X(ENPOL)
X(EN_DST)
X(EN_POLARITY)
X(EN_SRC)
X(EQN)
X(F)
X(FDCE)
@ -838,6 +836,7 @@ X(abcgroup)
X(acc_fir)
X(acc_fir_i)
X(add_carry)
X(aiger2_zbuf)
X(allconst)
X(allseq)
X(always_comb)

View file

@ -210,6 +210,6 @@ unsigned int CellCosts::get(RTLIL::Cell *cell)
// TODO: $fsm
// ignored: $pow $memrd $memwr $meminit (and v2 counterparts)
log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(cell->type), GetSize(cell->parameters));
log_warning("Can't determine cost of %s cell (%d parameters).\n", cell->type.unescape(), GetSize(cell->parameters));
return 1;
}

View file

@ -23,6 +23,7 @@
#define CXXOPTS_VECTOR_DELIMITER '\0'
#include "libs/cxxopts/include/cxxopts.hpp"
#include <iostream>
#include <chrono>
#ifdef YOSYS_ENABLE_READLINE
# include <readline/readline.h>
@ -143,19 +144,6 @@ int yosys_history_offset = 0;
std::string yosys_history_file;
#endif
#if defined(__wasm)
extern "C" {
// FIXME: WASI does not currently support exceptions.
void* __cxa_allocate_exception(size_t thrown_size) throw() {
return malloc(thrown_size);
}
bool __cxa_uncaught_exception() throw();
void __cxa_throw(void* thrown_exception, struct std::type_info * tinfo, void (*dest)(void*)) {
std::terminate();
}
}
#endif
void yosys_atexit()
{
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
@ -195,6 +183,7 @@ namespace Yosys {
int main(int argc, char **argv)
{
auto wall_clock_start = std::chrono::steady_clock::now();
std::string frontend_command = "auto";
std::string backend_command = "auto";
std::vector<std::string> vlog_defines;
@ -675,6 +664,7 @@ int main(int argc, char **argv)
#ifdef _WIN32
log("End of script. Logfile hash: %s\n", hash);
(void)wall_clock_start;
#else
std::string meminfo;
std::string stats_divider = ", ";
@ -700,8 +690,11 @@ int main(int argc, char **argv)
meminfo = stringf(", MEM: %.2f MB peak",
ru_buffer.ru_maxrss / (1024.0 * 1024.0));
#endif
log("End of script. Logfile hash: %s%sCPU: user %.2fs system %.2fs%s\n", hash,
stats_divider.c_str(), ru_buffer.ru_utime.tv_sec + 1e-6 * ru_buffer.ru_utime.tv_usec,
double wall_seconds = std::chrono::duration<double>(
std::chrono::steady_clock::now() - wall_clock_start).count();
log("End of script. Logfile hash: %s%stime: %.2fs, user: %.2fs, system: %.2fs%s\n", hash,
stats_divider.c_str(), wall_seconds, ru_buffer.ru_utime.tv_sec + 1e-6 * ru_buffer.ru_utime.tv_usec,
ru_buffer.ru_stime.tv_sec + 1e-6 * ru_buffer.ru_stime.tv_usec, meminfo.c_str());
#endif
log("%s\n", yosys_maybe_version());

View file

@ -866,7 +866,7 @@ DriveSpec DriverMap::operator()(DriveSpec spec)
std::string log_signal(DriveChunkWire const &chunk)
{
const char *id = log_id(chunk.wire->name);
std::string id = chunk.wire->name.unescape();
if (chunk.is_whole())
return id;
if (chunk.width == 1)
@ -877,8 +877,8 @@ std::string log_signal(DriveChunkWire const &chunk)
std::string log_signal(DriveChunkPort const &chunk)
{
const char *cell_id = log_id(chunk.cell->name);
const char *port_id = log_id(chunk.port);
std::string cell_id = chunk.cell->name.unescape();
std::string port_id = chunk.port.unescape();
if (chunk.is_whole())
return stringf("%s <%s>", cell_id, port_id);
if (chunk.width == 1)

View file

@ -25,7 +25,7 @@
#include "kernel/rtlil.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN
@ -1093,10 +1093,10 @@ private:
struct DriverMap
{
CellTypes celltypes;
NewCellTypes celltypes;
DriverMap() { celltypes.setup(); }
DriverMap(Design *design) { celltypes.setup(); celltypes.setup_design(design); }
DriverMap(Design *design) { celltypes.setup(design); }
private:

View file

@ -792,7 +792,7 @@ void FfData::flip_bits(const pool<int> &bits) {
Wire *new_q = module->addWire(NEW_ID4_SUFFIX("new_q"), width); // SILIMATE: Improve the naming
if (has_sr && cell) {
log_warning("Flipping D/Q/init and inserting priority fixup to legalize %s.%s [%s].\n", log_id(module->name), log_id(cell->name), log_id(cell->type));
log_warning("Flipping D/Q/init and inserting priority fixup to legalize %s.%s [%s].\n", module->name.unescape(), cell->name.unescape(), cell->type.unescape());
}
if (is_fine) {

View file

@ -22,6 +22,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/threading.h"
YOSYS_NAMESPACE_BEGIN
@ -35,34 +36,55 @@ struct FfInitVals
sigmap = sigmap_;
initbits.clear();
for (auto wire : module->wires())
if (wire->attributes.count(ID::init))
process_wire(wire);
}
void process_wire(RTLIL::Wire *wire)
{
SigSpec wirebits = (*sigmap)(wire);
Const initval = wire->attributes.at(ID::init);
for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
{
if (wire->attributes.count(ID::init) == 0)
SigBit bit = wirebits[i];
State val = initval[i];
if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
continue;
SigSpec wirebits = (*sigmap)(wire);
Const initval = wire->attributes.at(ID::init);
for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
{
SigBit bit = wirebits[i];
State val = initval[i];
if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
continue;
if (initbits.count(bit)) {
if (initbits.at(bit).first != val)
log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
log_signal(bit), log_signal(SigBit(wire, i)),
log_signal(val), log_signal(initbits.at(bit).first));
continue;
}
initbits[bit] = std::make_pair(val,SigBit(wire,i));
if (initbits.count(bit)) {
if (initbits.at(bit).first != val)
log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
log_signal(bit), log_signal(SigBit(wire, i)),
log_signal(val), log_signal(initbits.at(bit).first));
continue;
}
initbits[bit] = std::make_pair(val,SigBit(wire,i));
}
}
void set_parallel(const SigMapView *sigmap_, ParallelDispatchThreadPool &thread_pool, RTLIL::Module *module)
{
sigmap = sigmap_;
initbits.clear();
const RTLIL::Module *const_module = module;
ParallelDispatchThreadPool::Subpool subpool(thread_pool, ThreadPool::work_pool_size(0, module->wires_size(), 1000));
ShardedVector<RTLIL::Wire*> init_wires(subpool);
subpool.run([const_module, &init_wires](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(const_module->wires_size())) {
RTLIL::Wire *wire = const_module->wire_at(i);
if (wire->attributes.count(ID::init))
init_wires.insert(ctx, wire);
}
});
for (RTLIL::Wire *wire : init_wires)
process_wire(wire);
}
RTLIL::State operator()(RTLIL::SigBit bit) const
{
auto it = initbits.find((*sigmap)(bit));

View file

@ -804,8 +804,10 @@ std::string Fmt::render() const
buf += 'X';
else if (has_z)
buf += 'Z';
else
buf += (part.hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[subvalue.as_int()];
else {
const char *digits = part.hex_upper ? "0123456789ABCDEF" : "0123456789abcdef";
buf += digits[subvalue.as_int()];
}
}
} else if (part.base == 10) {
if (part.show_base)

View file

@ -29,7 +29,7 @@ static std::string file_base_name(std::string const & path)
FstData::FstData(std::string filename) : ctx(nullptr)
{
#if !defined(YOSYS_DISABLE_SPAWN)
#if defined(YOSYS_ENABLE_SPAWN)
std::string filename_trim = file_base_name(filename);
if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".vcd") == 0) {
filename_trim.erase(filename_trim.size()-4);
@ -45,8 +45,7 @@ FstData::FstData(std::string filename) : ctx(nullptr)
ctx = (fstReaderContext *)fstReaderOpen(filename.c_str());
if (!ctx)
log_error("Error opening '%s' as FST file\n", filename);
int scale = (int)fstReaderGetTimescale(ctx);
timescale = pow(10.0, scale);
scale = (int)fstReaderGetTimescale(ctx);
timescale_str = "";
int unit = 0;
int zeros = 0;
@ -88,11 +87,11 @@ static void normalize_brackets(std::string &str)
}
}
fstHandle FstData::getHandle(std::string name) {
fstHandle FstData::getHandle(std::string name) {
normalize_brackets(name);
if (name_to_handle.find(name) != name_to_handle.end())
return name_to_handle[name];
else
else
return 0;
};
@ -253,7 +252,7 @@ void FstData::reconstruct_callback_attimes(uint64_t pnt_time, fstHandle pnt_faci
bool is_clock = false;
if (!all_samples) {
for(auto &s : clk_signals) {
if (s==pnt_facidx) {
if (s==pnt_facidx) {
is_clock=true;
break;
}
@ -386,7 +385,7 @@ std::string FstData::autoScope(Module *topmod) {
// Logging results
if (results.empty()) {
log_warning("Could not auto-discover scope for module '%s'...\n",
log_warning("Could not auto-discover scope for module '%s'...\n",
top.c_str());
return "";
} else {
@ -395,7 +394,7 @@ std::string FstData::autoScope(Module *topmod) {
log(" %s\n", scope.c_str());
}
if (results.size() > 1) {
log_warning("Multiple scopes found for module '%s'. Using the first one.\n",
log_warning("Multiple scopes found for module '%s'. Using the first one.\n",
top.c_str());
}
return results[0];

View file

@ -55,7 +55,7 @@ class FstData
std::string valueOf(fstHandle signal);
fstHandle getHandle(std::string name);
dict<int,fstHandle> getMemoryHandles(std::string name);
double getTimescale() { return timescale; }
int getScale() { return scale; }
const char *getTimescaleString() { return timescale_str.c_str(); }
int getWidth(fstHandle signal);
std::string autoScope(Module *topmod);
@ -72,7 +72,7 @@ private:
uint64_t last_time;
std::map<fstHandle, std::string> past_data;
uint64_t past_time;
double timescale;
int scale; // exponent of 10, e.g. -6 = us, -9 = ns
std::string timescale_str;
uint64_t start_time;
uint64_t end_time;

View file

@ -136,7 +136,7 @@ struct PrintVisitor : DefaultVisitor<std::string> {
std::string Node::to_string()
{
return to_string([](Node n) { return RTLIL::unescape_id(n.name()); });
return to_string([](Node n) { return n.name().unescape(); });
}
std::string Node::to_string(std::function<std::string(Node)> np)
@ -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, log_id(mem->module), log_id(mem->memid));
"Call async2sync or clk2fflogic to avoid this error.\n", i, 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.", log_id(mem->module), log_id(mem->memid));
"Call opt_clean to remove it.", 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, log_id(mem->module), log_id(mem->memid));
"Call memory_nordff to avoid this error.\n", i, mem->module, mem->memid.unescape());
Node addr = enqueue(driver_map(DriveSpec(rd.addr)));
read_results.push_back(factory.memory_read(node, addr));
}
@ -609,7 +609,7 @@ private:
FfData ff(&ff_initvals, cell);
if (!ff.has_gclk)
log_error("The design contains a %s flip-flop at %s. This is not supported by the functional backend. "
"Call async2sync or clk2fflogic to avoid this error.\n", log_id(cell->type), log_id(cell));
"Call async2sync or clk2fflogic to avoid this error.\n", cell->type.unescape(), cell);
auto &state = factory.add_state(ff.name, ID($state), Sort(ff.width));
Node q_value = factory.value(state);
factory.suggest_name(q_value, ff.name);
@ -677,7 +677,7 @@ public:
factory.update_pending(pending, node);
} else {
DriveSpec driver = driver_map(DriveSpec(wire_chunk));
check_undriven(driver, RTLIL::unescape_id(wire_chunk.wire->name));
check_undriven(driver, wire_chunk.wire->name.unescape());
Node node = enqueue(driver);
factory.suggest_name(node, wire_chunk.wire->name);
factory.update_pending(pending, node);
@ -695,7 +695,7 @@ public:
factory.update_pending(pending, node);
} else {
DriveSpec driver = driver_map(DriveSpec(port_chunk));
check_undriven(driver, RTLIL::unescape_id(port_chunk.cell->name) + " port " + RTLIL::unescape_id(port_chunk.port));
check_undriven(driver, port_chunk.cell->name.unescape() + " port " + port_chunk.port.unescape());
factory.update_pending(pending, enqueue(driver));
}
} else {
@ -744,7 +744,7 @@ void IR::topological_sort() {
log_warning("Combinational loop:\n");
for (int *i = begin; i != end; ++i) {
Node node(_graph[*i]);
log("- %s = %s\n", RTLIL::unescape_id(node.name()), node.to_string());
log("- %s = %s\n", node.name().unescape(), node.to_string());
}
log("\n");
scc = true;

View file

@ -588,7 +588,7 @@ namespace Functional {
_used_names.insert(std::move(name));
}
std::string unique_name(IdString suggestion) {
std::string str = RTLIL::unescape_id(suggestion);
std::string str = suggestion.unescape();
for(size_t i = 0; i < str.size(); i++)
if(!is_character_legal(str[i], i))
str[i] = substitution_character;

View file

@ -56,6 +56,12 @@ namespace hashlib {
* instead of pointers.
*/
#if defined(__GNUC__) || defined(__clang__)
# define HASHLIB_ATTRIBUTE_WARN_UNUSED __attribute__((warn_unused))
#else
# define HASHLIB_ATTRIBUTE_WARN_UNUSED
#endif
const int hashtable_size_trigger = 2;
const int hashtable_size_factor = 3;
@ -403,7 +409,7 @@ private:
};
template<typename K, typename T, typename OPS>
class dict {
class HASHLIB_ATTRIBUTE_WARN_UNUSED dict {
struct entry_t
{
std::pair<K, T> udata;
@ -878,7 +884,7 @@ public:
};
template<typename K, typename OPS>
class pool
class HASHLIB_ATTRIBUTE_WARN_UNUSED pool
{
template<typename, int, typename> friend class idict;
@ -1263,7 +1269,7 @@ public:
};
template<typename K, int offset, typename OPS>
class idict
class HASHLIB_ATTRIBUTE_WARN_UNUSED idict
{
pool<K, OPS> database;
@ -1366,7 +1372,7 @@ public:
* i-prefixed methods operate on indices in parents
*/
template<typename K, typename OPS>
class mfp
class HASHLIB_ATTRIBUTE_WARN_UNUSED mfp
{
idict<K, 0, OPS> database;
class AtomicParent {

View file

@ -155,19 +155,13 @@ std::string get_base_tmpdir()
}
#if defined(_WIN32)
# ifdef __MINGW32__
char longpath[MAX_PATH + 1];
char shortpath[MAX_PATH + 1];
# else
WCHAR longpath[MAX_PATH + 1];
TCHAR shortpath[MAX_PATH + 1];
# endif
if (!GetTempPath(MAX_PATH+1, longpath))
if (!GetTempPathA(MAX_PATH+1, longpath))
log_error("GetTempPath() failed.\n");
if (!GetShortPathName(longpath, shortpath, MAX_PATH + 1))
if (!GetShortPathNameA(longpath, shortpath, MAX_PATH + 1))
log_error("GetShortPathName() failed.\n");
for (int i = 0; shortpath[i]; i++)
tmpdir += char(shortpath[i]);
tmpdir += shortpath;
#else
char * var = std::getenv("TMPDIR");
if (var && strlen(var)!=0) {

View file

@ -197,6 +197,28 @@ check_format(std::string_view fmt, int fmt_start, bool *has_escapes, FoundFormat
ensure_no_format_spec(fmt, fmt_start, has_escapes);
}
template <class T>
static auto has_name_member_imp(int)
-> decltype(static_cast<const RTLIL::IdString>(std::declval<T>().name), std::true_type{});
template <class T>
static auto has_name_member_imp(long)
-> std::false_type;
template <class T>
struct has_name_member : decltype(has_name_member_imp<T>(0)){};
template <class T>
static auto ptr_has_name_member_imp(int)
-> decltype(static_cast<const RTLIL::IdString>(std::declval<T>()->name), std::true_type{});
template <class T>
static auto ptr_has_name_member_imp(long)
-> std::false_type;
template <class T>
struct ptr_has_name_member : decltype(ptr_has_name_member_imp<T>(0)){};
// Check that the format string `fmt.substr(fmt_start)` is valid for the given type arguments.
// Fills `specs` with the FoundFormatSpecs found in the format string.
// `int_args_consumed` is the number of int arguments already consumed to satisfy the
@ -245,7 +267,9 @@ constexpr void check_format(std::string_view fmt, int fmt_start, bool *has_escap
if constexpr (!std::is_convertible_v<Arg, const char *> &&
!std::is_convertible_v<Arg, const std::string &> &&
!std::is_convertible_v<Arg, const std::string_view &> &&
!std::is_convertible_v<Arg, const RTLIL::IdString &>) {
!std::is_convertible_v<Arg, const RTLIL::IdString &> &&
!has_name_member<Arg>() &&
!ptr_has_name_member<Arg>()) {
YOSYS_ABORT("Expected type convertible to char *");
}
*specs = found;
@ -343,6 +367,16 @@ inline void format_emit_one(std::string &result, std::string_view fmt, const Fou
format_emit_idstring(result, spec, dynamic_ints, num_dynamic_ints, s);
return;
}
if constexpr (has_name_member<Arg>()) {
const std::string &s = arg.name.unescape();
format_emit_string(result, spec, dynamic_ints, num_dynamic_ints, s);
return;
}
if constexpr (ptr_has_name_member<Arg>()) {
const std::string &s = arg->name.unescape();
format_emit_string(result, spec, dynamic_ints, num_dynamic_ints, s);
return;
}
break;
case CONVSPEC_VOID_PTR:
if constexpr (std::is_convertible_v<Arg, const void *>) {
@ -441,7 +475,8 @@ public:
private:
std::string_view fmt;
bool has_escapes = false;
FoundFormatSpec specs[sizeof...(Args)] = {};
// Making array at least size of one to make MSVC happy and strict to standards
FoundFormatSpec specs[sizeof...(Args) ? sizeof...(Args) : 1] = {};
};
template <typename T> struct WrapType { using type = T; };

View file

@ -25,7 +25,7 @@
# include <sys/time.h>
#endif
#if defined(__linux__) || defined(__FreeBSD__)
#if defined(YOSYS_ENABLE_DLOPEN)
# include <dlfcn.h>
#endif
@ -324,6 +324,14 @@ void log_formatted_file_info(std::string_view filename, int lineno, std::string
log("%s:%d: Info: %s", filename, lineno, str);
}
void log_suppressed() {
if (log_debug_suppressed && !log_make_debug) {
constexpr const char* format = "<suppressed ~%d debug messages>\n";
logv_string(format, stringf(format, log_debug_suppressed));
log_debug_suppressed = 0;
}
}
[[noreturn]]
static void log_error_with_prefix(std::string_view prefix, std::string str)
{
@ -345,7 +353,9 @@ static void log_error_with_prefix(std::string_view prefix, std::string str)
}
log_last_error = std::move(str);
log("%s%s", prefix, log_last_error);
std::string message(prefix);
message += log_last_error;
logv_string("%s%s", message);
log_flush();
log_make_debug = bak_log_make_debug;
@ -355,7 +365,7 @@ static void log_error_with_prefix(std::string_view prefix, std::string str)
item.current_count++;
for (auto &[_, item] : log_expect_prefix_error)
if (std::regex_search(string(prefix) + string(log_last_error), item.pattern))
if (std::regex_search(message, item.pattern))
item.current_count++;
log_check_expected();
@ -461,7 +471,7 @@ void log_pop()
log_flush();
}
#if (defined(__linux__) || defined(__FreeBSD__)) && defined(YOSYS_ENABLE_PLUGINS)
#if defined(YOSYS_ENABLE_DLOPEN)
void log_backtrace(const char *prefix, int levels)
{
if (levels <= 0) return;
@ -576,7 +586,7 @@ void log_flush()
}
void log_dump_val_worker(RTLIL::IdString v) {
log("%s", log_id(v));
log("%s", v.unescape());
}
void log_dump_val_worker(RTLIL::SigSpec v) {
@ -604,7 +614,7 @@ std::string log_const(const RTLIL::Const &value, bool autoint)
const char *log_id(const RTLIL::IdString &str)
{
std::string unescaped = RTLIL::unescape_id(str);
std::string unescaped = str.unescape();
log_id_cache.push_back(strdup(unescaped.c_str()));
return log_id_cache.back();
}

View file

@ -206,12 +206,7 @@ template <typename... Args>
log_formatted_cmd_error(fmt.format(args...));
}
static inline void log_suppressed() {
if (log_debug_suppressed && !log_make_debug) {
log("<suppressed ~%d debug messages>\n", log_debug_suppressed);
log_debug_suppressed = 0;
}
}
void log_suppressed();
struct LogMakeDebugHdl {
bool status = false;

View file

@ -663,15 +663,15 @@ namespace {
auto addr = cell->getPort(ID::ADDR);
auto data = cell->getPort(ID::DATA);
if (!addr.is_fully_const())
log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), log_id(cell));
log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), cell);
if (!data.is_fully_const())
log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell));
log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), cell);
init.addr = addr.as_const();
init.data = data.as_const();
if (cell->type == ID($meminit_v2)) {
auto en = cell->getPort(ID::EN);
if (!en.is_fully_const())
log_error("Non-constant enable %s in memory initialization %s.\n", log_signal(en), log_id(cell));
log_error("Non-constant enable %s in memory initialization %s.\n", log_signal(en), cell);
init.en = en.as_const();
} else {
init.en = RTLIL::Const(State::S1, mem->width);
@ -1056,7 +1056,7 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
if (c)
log("Extracted %s FF from read port %d of %s.%s: %s\n", trans_use_addr ? "addr" : "data",
idx, log_id(module), log_id(memid), log_id(c));
idx, module, memid.unescape(), c);
port.en = State::S1;
port.clk = State::S0;

View file

@ -23,11 +23,28 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN
struct ModIndex : public RTLIL::Monitor
{
struct PointerOrderedSigBit : public RTLIL::SigBit {
PointerOrderedSigBit(SigBit s) {
wire = s.wire;
if (wire)
offset = s.offset;
else
data = s.data;
}
inline bool operator<(const RTLIL::SigBit &other) const {
if (wire == other.wire)
return wire ? (offset < other.offset) : (data < other.data);
if (wire != nullptr && other.wire != nullptr)
return wire < other.wire; // look here
return (wire != nullptr) < (other.wire != nullptr);
}
};
struct PortInfo {
RTLIL::Cell* cell;
RTLIL::IdString port;
@ -77,7 +94,7 @@ struct ModIndex : public RTLIL::Monitor
SigMap sigmap;
RTLIL::Module *module;
std::map<RTLIL::SigBit, SigBitInfo> database;
std::map<PointerOrderedSigBit, SigBitInfo> database;
int auto_reload_counter;
bool auto_reload_module;
@ -94,8 +111,11 @@ struct ModIndex : public RTLIL::Monitor
{
for (int i = 0; i < GetSize(sig); i++) {
RTLIL::SigBit bit = sigmap(sig[i]);
if (bit.wire)
if (bit.wire) {
database[bit].ports.erase(PortInfo(cell, port, i));
if (!database[bit].is_input && !database[bit].is_output && database[bit].ports.empty())
database.erase(bit);
}
}
}
@ -132,11 +152,11 @@ struct ModIndex : public RTLIL::Monitor
}
}
void check()
bool ok()
{
#ifndef NDEBUG
if (auto_reload_module)
return;
return true;
for (auto it : database)
log_assert(it.first == sigmap(it.first));
@ -156,9 +176,15 @@ struct ModIndex : public RTLIL::Monitor
else if (!(it.second == database_bak.at(it.first)))
log("ModuleIndex::check(): Different content for database[%s].\n", log_signal(it.first));
log_assert(database == database_bak);
return false;
}
#endif
return true;
}
void check()
{
log_assert(ok());
}
void notify_connect(RTLIL::Cell *cell, RTLIL::IdString port, const RTLIL::SigSpec &old_sig, const RTLIL::SigSpec &sig) override
@ -294,8 +320,8 @@ struct ModIndex : public RTLIL::Monitor
if (it.second.is_output)
log(" PRIMARY OUTPUT\n");
for (auto &port : it.second.ports)
log(" PORT: %s.%s[%d] (%s)\n", log_id(port.cell),
log_id(port.port), port.offset, log_id(port.cell->type));
log(" PORT: %s.%s[%d] (%s)\n", port.cell,
port.port.unescape(), port.offset, port.cell->type.unescape());
}
}
};
@ -332,7 +358,7 @@ struct ModWalker
RTLIL::Design *design;
RTLIL::Module *module;
CellTypes ct;
NewCellTypes ct;
SigMap sigmap;
dict<RTLIL::SigBit, pool<PortBit>> signal_drivers;

651
kernel/newcelltypes.h Normal file
View file

@ -0,0 +1,651 @@
#ifndef NEWCELLTYPES_H
#define NEWCELLTYPES_H
#include "kernel/rtlil.h"
#include "kernel/yosys.h"
YOSYS_NAMESPACE_BEGIN
/**
* This API is unstable.
* It may change or be removed in future versions and break dependent code.
*/
namespace StaticCellTypes {
// Given by last internal cell type IdString constids.inc, compilation error if too low
constexpr int MAX_CELLS = 300;
// Currently given by _MUX16_, compilation error if too low
constexpr int MAX_PORTS = 20;
struct CellTableBuilder {
struct PortList {
std::array<RTLIL::IdString, MAX_PORTS> ports{};
size_t count = 0;
constexpr PortList() = default;
constexpr PortList(std::initializer_list<RTLIL::IdString> init) {
for (auto p : init) {
ports[count++] = p;
}
}
constexpr auto begin() const { return ports.begin(); }
constexpr auto end() const { return ports.begin() + count; }
constexpr bool contains(RTLIL::IdString port) const {
for (size_t i = 0; i < count; i++) {
if (port == ports[i])
return true;
}
return false;
}
constexpr size_t size() const { return count; }
};
struct Features {
bool is_evaluable = false;
bool is_combinatorial = false;
bool is_synthesizable = false;
bool is_stdcell = false;
bool is_ff = false;
bool is_mem_noff = false;
bool is_anyinit = false;
bool is_tristate = false;
};
struct CellInfo {
RTLIL::IdString type;
PortList inputs, outputs;
Features features;
};
std::array<CellInfo, MAX_CELLS> cells{};
size_t count = 0;
constexpr void setup_type(RTLIL::IdString type, std::initializer_list<RTLIL::IdString> inputs, std::initializer_list<RTLIL::IdString> outputs, const Features& features) {
cells[count++] = {type, PortList(inputs), PortList(outputs), features};
}
constexpr void setup_internals_other()
{
Features features {};
features.is_tristate = true;
setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y}, features);
features = {};
setup_type(ID($assert), {ID::A, ID::EN}, {}, features);
setup_type(ID($assume), {ID::A, ID::EN}, {}, features);
setup_type(ID($live), {ID::A, ID::EN}, {}, features);
setup_type(ID($fair), {ID::A, ID::EN}, {}, features);
setup_type(ID($cover), {ID::A, ID::EN}, {}, features);
setup_type(ID($initstate), {}, {ID::Y}, features);
setup_type(ID($anyconst), {}, {ID::Y}, features);
setup_type(ID($anyseq), {}, {ID::Y}, features);
setup_type(ID($allconst), {}, {ID::Y}, features);
setup_type(ID($allseq), {}, {ID::Y}, features);
setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, {}, features);
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, {}, features);
setup_type(ID($specrule), {ID::SRC_EN, ID::DST_EN, ID::SRC, ID::DST}, {}, features);
setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, {}, features);
setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, {}, features);
setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y}, features);
setup_type(ID($get_tag), {ID::A}, {ID::Y}, features);
setup_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, {}, features);
setup_type(ID($original_tag), {ID::A}, {ID::Y}, features);
setup_type(ID($future_ff), {ID::A}, {ID::Y}, features);
setup_type(ID($scopeinfo), {}, {}, features);
setup_type(ID($input_port), {}, {ID::Y}, features);
setup_type(ID($connect), {ID::A, ID::B}, {}, features);
}
constexpr void setup_internals_eval()
{
Features features {};
features.is_evaluable = true;
std::initializer_list<RTLIL::IdString> unary_ops = {
ID($not), ID($pos), ID($buf), ID($neg),
ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool),
ID($logic_not), ID($slice), ID($lut), ID($sop)
};
std::initializer_list<RTLIL::IdString> binary_ops = {
ID($and), ID($or), ID($xor), ID($xnor),
ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx),
ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt),
ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow),
ID($logic_and), ID($logic_or), ID($concat), ID($macc),
ID($bweqx)
};
for (auto type : unary_ops)
setup_type(type, {ID::A}, {ID::Y}, features);
for (auto type : binary_ops)
setup_type(type, {ID::A, ID::B}, {ID::Y}, features);
for (auto type : {ID($mux), ID($pmux), ID($bwmux)})
setup_type(type, {ID::A, ID::B, ID::S}, {ID::Y}, features);
for (auto type : {ID($bmux), ID($demux)})
setup_type(type, {ID::A, ID::S}, {ID::Y}, features);
setup_type(ID($lcu), {ID::P, ID::G, ID::CI}, {ID::CO}, features);
setup_type(ID($alu), {ID::A, ID::B, ID::CI, ID::BI}, {ID::X, ID::Y, ID::CO}, features);
setup_type(ID($macc_v2), {ID::A, ID::B, ID::C}, {ID::Y}, features);
setup_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}, features);
}
constexpr void setup_internals_ff()
{
Features features {};
features.is_ff = true;
setup_type(ID($sr), {ID::SET, ID::CLR}, {ID::Q}, features);
setup_type(ID($ff), {ID::D}, {ID::Q}, features);
setup_type(ID($dff), {ID::CLK, ID::D}, {ID::Q}, features);
setup_type(ID($dffe), {ID::CLK, ID::EN, ID::D}, {ID::Q}, features);
setup_type(ID($dffsr), {ID::CLK, ID::SET, ID::CLR, ID::D}, {ID::Q}, features);
setup_type(ID($dffsre), {ID::CLK, ID::SET, ID::CLR, ID::D, ID::EN}, {ID::Q}, features);
setup_type(ID($adff), {ID::CLK, ID::ARST, ID::D}, {ID::Q}, features);
setup_type(ID($adffe), {ID::CLK, ID::ARST, ID::D, ID::EN}, {ID::Q}, features);
setup_type(ID($aldff), {ID::CLK, ID::ALOAD, ID::AD, ID::D}, {ID::Q}, features);
setup_type(ID($aldffe), {ID::CLK, ID::ALOAD, ID::AD, ID::D, ID::EN}, {ID::Q}, features);
setup_type(ID($sdff), {ID::CLK, ID::SRST, ID::D}, {ID::Q}, features);
setup_type(ID($sdffe), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}, features);
setup_type(ID($sdffce), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}, features);
setup_type(ID($dlatch), {ID::EN, ID::D}, {ID::Q}, features);
setup_type(ID($adlatch), {ID::EN, ID::D, ID::ARST}, {ID::Q}, features);
setup_type(ID($dlatchsr), {ID::EN, ID::SET, ID::CLR, ID::D}, {ID::Q}, features);
}
constexpr void setup_internals_anyinit()
{
Features features {};
features.is_anyinit = true;
setup_type(ID($anyinit), {ID::D}, {ID::Q}, features);
}
constexpr void setup_internals_mem_noff()
{
Features features {};
features.is_mem_noff = true;
// NOT setup_internals_ff()
setup_type(ID($memrd), {ID::CLK, ID::EN, ID::ADDR}, {ID::DATA}, features);
setup_type(ID($memrd_v2), {ID::CLK, ID::EN, ID::ARST, ID::SRST, ID::ADDR}, {ID::DATA}, features);
setup_type(ID($memwr), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, {}, features);
setup_type(ID($memwr_v2), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, {}, features);
setup_type(ID($meminit), {ID::ADDR, ID::DATA}, {}, features);
setup_type(ID($meminit_v2), {ID::ADDR, ID::DATA, ID::EN}, {}, features);
setup_type(ID($mem), {ID::RD_CLK, ID::RD_EN, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}, features);
setup_type(ID($mem_v2), {ID::RD_CLK, ID::RD_EN, ID::RD_ARST, ID::RD_SRST, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}, features);
// What?
setup_type(ID($fsm), {ID::CLK, ID::ARST, ID::CTRL_IN}, {ID::CTRL_OUT}, features);
}
constexpr void setup_stdcells_tristate()
{
Features features {};
features.is_stdcell = true;
features.is_tristate = true;
setup_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y}, features);
}
constexpr void setup_stdcells_eval()
{
Features features {};
features.is_stdcell = true;
features.is_evaluable = true;
setup_type(ID($_BUF_), {ID::A}, {ID::Y}, features);
setup_type(ID($_NOT_), {ID::A}, {ID::Y}, features);
setup_type(ID($_AND_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_NAND_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_OR_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_NOR_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_XOR_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_XNOR_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_ANDNOT_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_ORNOT_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_MUX_), {ID::A, ID::B, ID::S}, {ID::Y}, features);
setup_type(ID($_NMUX_), {ID::A, ID::B, ID::S}, {ID::Y}, features);
setup_type(ID($_MUX4_), {ID::A, ID::B, ID::C, ID::D, ID::S, ID::T}, {ID::Y}, features);
setup_type(ID($_MUX8_), {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::S, ID::T, ID::U}, {ID::Y}, features);
setup_type(ID($_MUX16_), {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::I, ID::J, ID::K, ID::L, ID::M, ID::N, ID::O, ID::P, ID::S, ID::T, ID::U, ID::V}, {ID::Y}, features);
setup_type(ID($_AOI3_), {ID::A, ID::B, ID::C}, {ID::Y}, features);
setup_type(ID($_OAI3_), {ID::A, ID::B, ID::C}, {ID::Y}, features);
setup_type(ID($_AOI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}, features);
setup_type(ID($_OAI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}, features);
}
constexpr void setup_stdcells_ff() {
Features features {};
features.is_stdcell = true;
features.is_ff = true;
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// setup_type(std::string("$_SR_") + c1 + c2 + "_", {ID::S, ID::R}, {ID::Q}, features);
setup_type(ID($_SR_NN_), {ID::S, ID::R}, {ID::Q}, features);
setup_type(ID($_SR_NP_), {ID::S, ID::R}, {ID::Q}, features);
setup_type(ID($_SR_PN_), {ID::S, ID::R}, {ID::Q}, features);
setup_type(ID($_SR_PP_), {ID::S, ID::R}, {ID::Q}, features);
setup_type(ID($_FF_), {ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// setup_type(std::string("$_DFF_") + c1 + "_", {ID::C, ID::D}, {ID::Q}, features);
setup_type(ID::$_DFF_N_, {ID::C, ID::D}, {ID::Q}, features);
setup_type(ID::$_DFF_P_, {ID::C, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// setup_type(std::string("$_DFFE_") + c1 + c2 + "_", {ID::C, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID::$_DFFE_NN_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID::$_DFFE_NP_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID::$_DFFE_PN_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID::$_DFFE_PP_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// setup_type(std::string("$_DFF_") + c1 + c2 + c3 + "_", {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_NN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_NN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_NP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_NP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_PN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_PN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_PP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_PP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// for (auto c4 : list_np)
// setup_type(std::string("$_DFFE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// setup_type(std::string("$_ALDFF_") + c1 + c2 + "_", {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
setup_type(ID($_ALDFF_NN_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
setup_type(ID($_ALDFF_NP_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
setup_type(ID($_ALDFF_PN_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
setup_type(ID($_ALDFF_PP_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_np)
// setup_type(std::string("$_ALDFFE_") + c1 + c2 + c3 + "_", {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_NNN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_NNP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_NPN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_NPP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_PNN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_PNP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_PPN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_PPP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_np)
// setup_type(std::string("$_DFFSR_") + c1 + c2 + c3 + "_", {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_NNN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_NNP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_NPN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_NPP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_PNN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_PNP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_PPN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_PPP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_np)
// for (auto c4 : list_np)
// setup_type(std::string("$_DFFSRE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NNNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NNNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NNPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NNPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NPNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NPNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NPPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NPPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PNNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PNNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PNPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PNPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PPNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PPNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PPPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PPPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// setup_type(std::string("$_SDFF_") + c1 + c2 + c3 + "_", {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_NN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_NN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_NP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_NP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_PN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_PN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_PP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_PP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// for (auto c4 : list_np)
// setup_type(std::string("$_SDFFE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// for (auto c4 : list_np)
// setup_type(std::string("$_SDFFCE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// setup_type(std::string("$_DLATCH_") + c1 + "_", {ID::E, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_N_), {ID::E, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_P_), {ID::E, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// setup_type(std::string("$_DLATCH_") + c1 + c2 + c3 + "_", {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_NN0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_NN1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_NP0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_NP1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_PN0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_PN1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_PP0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_PP1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_np)
// setup_type(std::string("$_DLATCHSR_") + c1 + c2 + c3 + "_", {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_NNN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_NNP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_NPN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_NPP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_PNN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_PNP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_PPN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_PPP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
}
constexpr CellTableBuilder() {
setup_internals_other();
setup_internals_eval();
setup_internals_ff();
setup_internals_anyinit();
setup_internals_mem_noff();
setup_stdcells_tristate();
setup_stdcells_eval();
setup_stdcells_ff();
}
};
constexpr CellTableBuilder builder{};
struct PortInfo {
struct PortLists {
std::array<CellTableBuilder::PortList, MAX_CELLS> data{};
constexpr CellTableBuilder::PortList operator()(IdString type) const {
return data[type.index_];
}
constexpr CellTableBuilder::PortList& operator[](size_t idx) {
return data[idx];
}
constexpr size_t size() const { return data.size(); }
};
PortLists inputs {};
PortLists outputs {};
constexpr PortInfo() {
for (size_t i = 0; i < builder.count; ++i) {
auto& cell = builder.cells[i];
size_t idx = cell.type.index_;
inputs[idx] = cell.inputs;
outputs[idx] = cell.outputs;
}
}
};
struct Categories {
struct Category {
std::array<bool, MAX_CELLS> data{};
constexpr bool operator()(IdString type) const {
size_t idx = type.index_;
if (idx >= MAX_CELLS)
return false;
return data[idx];
}
constexpr bool operator[](size_t idx) {
return data[idx];
}
constexpr void set_id(IdString type, bool val = true) {
size_t idx = type.index_;
if (idx >= MAX_CELLS)
return; // TODO should be an assert but then it's not constexpr
data[idx] = val;
}
constexpr void set(size_t idx, bool val = true) {
data[idx] = val;
}
constexpr size_t size() const { return data.size(); }
};
Category empty {};
Category is_known {};
Category is_evaluable {};
Category is_combinatorial {};
Category is_synthesizable {};
Category is_stdcell {};
Category is_ff {};
Category is_mem_noff {};
Category is_anyinit {};
Category is_tristate {};
constexpr Categories() {
for (size_t i = 0; i < builder.count; ++i) {
auto& cell = builder.cells[i];
size_t idx = cell.type.index_;
is_known.set(idx);
is_evaluable.set(idx, cell.features.is_evaluable);
is_combinatorial.set(idx, cell.features.is_combinatorial);
is_synthesizable.set(idx, cell.features.is_synthesizable);
is_stdcell.set(idx, cell.features.is_stdcell);
is_ff.set(idx, cell.features.is_ff);
is_mem_noff.set(idx, cell.features.is_mem_noff);
is_anyinit.set(idx, cell.features.is_anyinit);
is_tristate.set(idx, cell.features.is_tristate);
}
}
constexpr static Category join(Category left, Category right) {
Category c {};
for (size_t i = 0; i < MAX_CELLS; ++i) {
c.set(i, left[i] || right[i]);
}
return c;
}
constexpr static Category meet(Category left, Category right) {
Category c {};
for (size_t i = 0; i < MAX_CELLS; ++i) {
c.set(i, left[i] && right[i]);
}
return c;
}
// Sketchy! Make sure to always meet with only the known universe.
// In other words, no modus tollens allowed
constexpr static Category complement(Category arg) {
Category c {};
for (size_t i = 0; i < MAX_CELLS; ++i) {
c.set(i, !arg[i]);
}
return c;
}
};
// Pure
static constexpr PortInfo port_info;
static constexpr Categories categories;
// Legacy
namespace Compat {
static constexpr auto internals_all = Categories::meet(categories.is_known, Categories::complement(categories.is_stdcell));
static constexpr auto mem_ff = Categories::join(categories.is_ff, categories.is_mem_noff);
// old setup_internals + setup_stdcells
static constexpr auto nomem_noff = Categories::meet(categories.is_known, Categories::complement(mem_ff));
static constexpr auto internals_mem_ff = Categories::meet(internals_all, mem_ff);
// old setup_internals
static constexpr auto internals_nomem_noff = Categories::meet(internals_all, nomem_noff);
// old setup_stdcells
static constexpr auto stdcells_nomem_noff = Categories::meet(categories.is_stdcell, nomem_noff);
static constexpr auto stdcells_mem = Categories::meet(categories.is_stdcell, categories.is_mem_noff);
// old setup_internals_eval
// static constexpr auto internals_eval = Categories::meet(internals_all, categories.is_evaluable);
};
namespace {
static_assert(categories.is_evaluable(ID($and)));
static_assert(!categories.is_ff(ID($and)));
static_assert(Categories::join(categories.is_evaluable, categories.is_ff)(ID($and)));
static_assert(Categories::join(categories.is_evaluable, categories.is_ff)(ID($dffsr)));
static_assert(!Categories::join(categories.is_evaluable, categories.is_ff)(ID($anyinit)));
}
};
struct NewCellType {
RTLIL::IdString type;
pool<RTLIL::IdString> inputs, outputs;
bool is_evaluable;
bool is_combinatorial;
bool is_synthesizable;
};
struct NewCellTypes {
struct IdStringHash {
std::size_t operator()(const IdString id) const {
return static_cast<size_t>(id.hash_top().yield());
}
};
StaticCellTypes::Categories::Category static_cell_types = StaticCellTypes::categories.empty;
std::unordered_map<RTLIL::IdString, NewCellType, IdStringHash> custom_cell_types {};
NewCellTypes() {
static_cell_types = StaticCellTypes::categories.empty;
}
NewCellTypes(RTLIL::Design *design) {
static_cell_types = StaticCellTypes::categories.empty;
setup(design);
}
void setup(RTLIL::Design *design = NULL) {
if (design)
setup_design(design);
static_cell_types = StaticCellTypes::categories.is_known;
}
void setup_design(RTLIL::Design *design) {
for (auto module : design->modules())
setup_module(module);
}
void setup_module(RTLIL::Module *module) {
pool<RTLIL::IdString> inputs, outputs;
for (RTLIL::IdString wire_name : module->ports) {
RTLIL::Wire *wire = module->wire(wire_name);
if (wire->port_input)
inputs.insert(wire->name);
if (wire->port_output)
outputs.insert(wire->name);
}
setup_type(module->name, inputs, outputs);
}
void setup_type(RTLIL::IdString type, const pool<RTLIL::IdString> &inputs, const pool<RTLIL::IdString> &outputs, bool is_evaluable = false, bool is_combinatorial = false, bool is_synthesizable = false) {
NewCellType ct = {type, inputs, outputs, is_evaluable, is_combinatorial, is_synthesizable};
custom_cell_types[ct.type] = ct;
}
void clear() {
custom_cell_types.clear();
static_cell_types = StaticCellTypes::categories.empty;
}
bool cell_known(const RTLIL::IdString &type) const {
return static_cell_types(type) || custom_cell_types.count(type) != 0;
}
bool cell_output(const RTLIL::IdString &type, const RTLIL::IdString &port) const
{
if (static_cell_types(type) && StaticCellTypes::port_info.outputs(type).contains(port)) {
return true;
}
auto it = custom_cell_types.find(type);
return it != custom_cell_types.end() && it->second.outputs.count(port) != 0;
}
bool cell_input(const RTLIL::IdString &type, const RTLIL::IdString &port) const
{
if (static_cell_types(type) && StaticCellTypes::port_info.inputs(type).contains(port)) {
return true;
}
auto it = custom_cell_types.find(type);
return it != custom_cell_types.end() && it->second.inputs.count(port) != 0;
}
RTLIL::PortDir cell_port_dir(RTLIL::IdString type, RTLIL::IdString port) const
{
bool is_input, is_output;
if (static_cell_types(type)) {
is_input = StaticCellTypes::port_info.inputs(type).contains(port);
is_output = StaticCellTypes::port_info.outputs(type).contains(port);
} else {
auto it = custom_cell_types.find(type);
if (it == custom_cell_types.end())
return RTLIL::PD_UNKNOWN;
is_input = it->second.inputs.count(port);
is_output = it->second.outputs.count(port);
}
return RTLIL::PortDir(is_input + is_output * 2);
}
bool cell_evaluable(const RTLIL::IdString &type) const
{
return static_cell_types(type) && StaticCellTypes::categories.is_evaluable(type);
}
};
extern NewCellTypes yosys_celltypes;
YOSYS_NAMESPACE_END
#endif

View file

@ -22,11 +22,13 @@
#include "kernel/json.h"
#include "kernel/gzip.h"
#include "kernel/log_help.h"
#include "kernel/newcelltypes.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <filesystem>
YOSYS_NAMESPACE_BEGIN
@ -59,7 +61,7 @@ void try_collect_garbage()
RTLIL::OwningIdString::collect_garbage();
}
Pass::Pass(std::string name, std::string short_help, source_location location) :
Pass::Pass(std::string name, std::string short_help, source_location location) :
pass_name(name), short_help(short_help), location(location)
{
next_queued_pass = first_queued_pass;
@ -216,7 +218,7 @@ void Pass::call(RTLIL::Design *design, std::string command)
return;
if (tok[0] == '!') {
#if !defined(YOSYS_DISABLE_SPAWN)
#if defined(YOSYS_ENABLE_SPAWN)
cmd_buf = command.substr(command.find('!') + 1);
while (!cmd_buf.empty() && (cmd_buf.back() == ' ' || cmd_buf.back() == '\t' ||
cmd_buf.back() == '\r' || cmd_buf.back() == '\n'))
@ -740,8 +742,8 @@ static void log_warning_flags(Pass *pass) {
static struct CellHelpMessages {
dict<string, SimHelper> cell_help;
CellHelpMessages() {
#include "techlibs/common/simlib_help.inc"
#include "techlibs/common/simcells_help.inc"
#include "kernel/simlib_help.inc"
#include "kernel/simcells_help.inc"
cell_help.sort();
}
bool contains(string name) { return cell_help.count(get_cell_name(name)) > 0; }
@ -771,6 +773,10 @@ struct HelpPass : public Pass {
bool raise_error = false;
std::map<string, vector<string>> groups;
// get root path
auto this_path = std::filesystem::path(source_location::current().file_name());
auto source_root = this_path.parent_path().parent_path();
json.name("cmds"); json.begin_object();
// iterate over commands
for (auto &it : pass_register) {
@ -911,10 +917,29 @@ struct HelpPass : public Pass {
}
}
// fix path
string source_file = pass->location.file_name();
bool has_source = source_file.compare("unknown") != 0;
std::filesystem::path source_path;
auto no_source_group = false;
if (has_source) {
source_path = std::filesystem::path(pass->location.file_name());
if (source_path.is_absolute()) {
// using proximate instead of relative means that we
// still get the source path if they aren't relative
auto proximate_path = std::filesystem::proximate(source_path, source_root);
if (proximate_path == std::filesystem::weakly_canonical(proximate_path))
// we're only interested if it's a subpath of our root dir
source_path = proximate_path;
else
// don't try to group external paths
no_source_group = true;
}
source_file = source_path.string();
}
// attempt auto group
if (!cmd_help.has_group()) {
string source_file = pass->location.file_name();
bool has_source = source_file.compare("unknown") != 0;
if (pass->internal_flag)
cmd_help.group = "internal";
else if (source_file.find("backends/") == 0 || (!has_source && name.find("read_") == 0))
@ -922,11 +947,8 @@ struct HelpPass : public Pass {
else if (source_file.find("frontends/") == 0 || (!has_source && name.find("write_") == 0))
cmd_help.group = "frontends";
else if (has_source) {
auto last_slash = source_file.find_last_of('/');
if (last_slash != string::npos) {
auto parent_path = source_file.substr(0, last_slash);
cmd_help.group = parent_path;
}
if (source_path.has_parent_path() && !no_source_group)
cmd_help.group = source_path.parent_path().string();
}
// implicit !has_source
else if (name.find("equiv") == 0)
@ -954,7 +976,7 @@ struct HelpPass : public Pass {
json.value(content.to_json());
json.end_array();
json.entry("group", cmd_help.group);
json.entry("source_file", pass->location.file_name());
json.entry("source_file", source_file);
json.entry("source_line", pass->location.line());
json.entry("source_func", pass->location.function_name());
json.entry("experimental_flag", pass->experimental_flag);
@ -975,16 +997,18 @@ struct HelpPass : public Pass {
json.entry("generator", yosys_maybe_version());
dict<string, vector<string>> groups;
dict<string, pair<SimHelper, CellType>> cells;
dict<string, pair<SimHelper, StaticCellTypes::CellTableBuilder::CellInfo>> cells;
// iterate over cells
bool raise_error = false;
for (auto &it : yosys_celltypes.cell_types) {
auto name = it.first.str();
for (auto it : StaticCellTypes::builder.cells) {
if (!StaticCellTypes::categories.is_known(it.type))
continue;
auto name = it.type.str();
if (cell_help_messages.contains(name)) {
auto cell_help = cell_help_messages.get(name);
groups[cell_help.group].emplace_back(name);
auto cell_pair = pair<SimHelper, CellType>(cell_help, it.second);
auto cell_pair = pair<SimHelper, StaticCellTypes::CellTableBuilder::CellInfo>(cell_help, it);
cells.emplace(name, cell_pair);
} else {
log("ERROR: Missing cell help for cell '%s'.\n", name);
@ -1025,9 +1049,9 @@ struct HelpPass : public Pass {
json.name("outputs"); json.value(outputs);
vector<string> properties;
// CellType properties
if (ct.is_evaluable) properties.push_back("is_evaluable");
if (ct.is_combinatorial) properties.push_back("is_combinatorial");
if (ct.is_synthesizable) properties.push_back("is_synthesizable");
if (ct.features.is_evaluable) properties.push_back("is_evaluable");
if (ct.features.is_combinatorial) properties.push_back("is_combinatorial");
if (ct.features.is_synthesizable) properties.push_back("is_synthesizable");
// SimHelper properties
size_t last = 0; size_t next = 0;
while ((next = ch.tags.find(", ", last)) != string::npos) {

View file

@ -23,29 +23,13 @@
#include "kernel/yosys_common.h"
#include "kernel/yosys.h"
#ifdef YOSYS_ENABLE_HELP_SOURCE
#include <version>
# if __cpp_lib_source_location == 201907L
#include <source_location>
using std::source_location;
#define HAS_SOURCE_LOCATION
# elif defined(__has_include)
# if __has_include(<experimental/source_location>)
#include <experimental/source_location>
using std::experimental::source_location;
#define HAS_SOURCE_LOCATION
# endif
# endif
#endif
#ifndef HAS_SOURCE_LOCATION
struct source_location { // dummy placeholder
int line() const { return 0; }
int column() const { return 0; }
const char* file_name() const { return "unknown"; }
const char* function_name() const { return "unknown"; }
static const source_location current(...) { return source_location(); }
};
#include <version>
#if __cpp_lib_source_location >= 201907L
#include <source_location>
using std::source_location;
#else
#include <experimental/source_location>
using std::experimental::source_location;
#endif
YOSYS_NAMESPACE_BEGIN

View file

@ -19,9 +19,10 @@
#include "kernel/yosys.h"
#include "kernel/macc.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/binding.h"
#include "kernel/sigtools.h"
#include "kernel/threading.h"
#include "frontends/verilog/verilog_frontend.h"
#include "frontends/verilog/preproc.h"
#include "backends/rtlil/rtlil_backend.h"
@ -144,9 +145,17 @@ static constexpr bool check_well_known_id_order()
// and in sorted ascii order, as required by the ID macro.
static_assert(check_well_known_id_order());
constexpr int STATIC_ID_END = static_cast<int>(RTLIL::StaticId::STATIC_ID_END);
struct IdStringCollector {
IdStringCollector(std::vector<MonotonicFlag> &live_ids)
: live_ids(live_ids) {}
void trace(IdString id) {
live.insert(id.index_);
if (id.index_ >= STATIC_ID_END)
live_ids[id.index_ - STATIC_ID_END].set();
else if (id.index_ < 0)
live_autoidx_ids.push_back(id.index_);
}
template <typename T> void trace(const T* v) {
trace(*v);
@ -180,10 +189,6 @@ struct IdStringCollector {
trace(element);
}
void trace(const RTLIL::Design &design) {
trace_values(design.modules_);
trace(design.selection_vars);
}
void trace(const RTLIL::Selection &selection_var) {
trace(selection_var.selected_modules);
trace(selection_var.selected_members);
@ -192,15 +197,6 @@ struct IdStringCollector {
trace_keys(named.attributes);
trace(named.name);
}
void trace(const RTLIL::Module &module) {
trace_named(module);
trace_values(module.wires_);
trace_values(module.cells_);
trace(module.avail_parameters);
trace_keys(module.parameter_default_values);
trace_values(module.memories);
trace_values(module.processes);
}
void trace(const RTLIL::Wire &wire) {
trace_named(wire);
if (wire.known_driver())
@ -236,7 +232,8 @@ struct IdStringCollector {
trace(action.memid);
}
std::unordered_set<int> live;
std::vector<MonotonicFlag> &live_ids;
std::vector<int> live_autoidx_ids;
};
int64_t RTLIL::OwningIdString::gc_ns;
@ -245,20 +242,55 @@ int RTLIL::OwningIdString::gc_count;
void RTLIL::OwningIdString::collect_garbage()
{
int64_t start = PerformanceTimer::query();
IdStringCollector collector;
for (auto &[idx, design] : *RTLIL::Design::get_all_designs()) {
collector.trace(*design);
}
int size = GetSize(global_id_storage_);
for (int i = static_cast<int>(StaticId::STATIC_ID_END); i < size; ++i) {
RTLIL::IdString::Storage &storage = global_id_storage_.at(i);
if (storage.buf == nullptr)
continue;
if (collector.live.find(i) != collector.live.end())
continue;
if (global_refcount_storage_.find(i) != global_refcount_storage_.end())
continue;
int pool_size = 0;
for (auto &[idx, design] : *RTLIL::Design::get_all_designs())
for (RTLIL::Module *module : design->modules())
pool_size = std::max(pool_size, ThreadPool::work_pool_size(0, module->cells_size(), 1000));
ParallelDispatchThreadPool thread_pool(pool_size);
int size = GetSize(global_id_storage_);
std::vector<MonotonicFlag> live_ids(size - STATIC_ID_END);
std::vector<IdStringCollector> collectors;
int num_threads = thread_pool.num_threads();
collectors.reserve(num_threads);
for (int i = 0; i < num_threads; ++i)
collectors.emplace_back(live_ids);
for (auto &[idx, design] : *RTLIL::Design::get_all_designs()) {
for (RTLIL::Module *module : design->modules()) {
collectors[0].trace_named(*module);
ParallelDispatchThreadPool::Subpool subpool(thread_pool, ThreadPool::work_pool_size(0, module->cells_size(), 1000));
subpool.run([&collectors, module](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(module->cells_size()))
collectors[ctx.thread_num].trace(module->cell_at(i));
for (int i : ctx.item_range(module->wires_size()))
collectors[ctx.thread_num].trace(module->wire_at(i));
});
collectors[0].trace(module->avail_parameters);
collectors[0].trace_keys(module->parameter_default_values);
collectors[0].trace_values(module->memories);
collectors[0].trace_values(module->processes);
}
collectors[0].trace(design->selection_vars);
}
ShardedVector<int> free_ids(thread_pool);
thread_pool.run([&live_ids, size, &free_ids](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(size - STATIC_ID_END)) {
int index = i + STATIC_ID_END;
RTLIL::IdString::Storage &storage = global_id_storage_.at(index);
if (storage.buf == nullptr)
continue;
if (live_ids[i].load())
continue;
if (global_refcount_storage_.find(index) != global_refcount_storage_.end())
continue;
free_ids.insert(ctx, index);
}
});
for (int i : free_ids) {
RTLIL::IdString::Storage &storage = global_id_storage_.at(i);
if (yosys_xtrace) {
log("#X# Removed IdString '%s' with index %d.\n", storage.buf, i);
log_backtrace("-X- ", yosys_xtrace-1);
@ -270,8 +302,13 @@ void RTLIL::OwningIdString::collect_garbage()
global_free_idx_list_.push_back(i);
}
std::unordered_set<int> live_autoidx_ids;
for (IdStringCollector &collector : collectors)
for (int id : collector.live_autoidx_ids)
live_autoidx_ids.insert(id);
for (auto it = global_autoidx_id_storage_.begin(); it != global_autoidx_id_storage_.end();) {
if (collector.live.find(it->first) != collector.live.end()) {
if (live_autoidx_ids.find(it->first) != live_autoidx_ids.end()) {
++it;
continue;
}
@ -290,159 +327,17 @@ void RTLIL::OwningIdString::collect_garbage()
dict<std::string, std::string> RTLIL::constpad;
static const pool<IdString> &builtin_ff_cell_types_internal() {
static const pool<IdString> res = {
ID($sr),
ID($ff),
ID($dff),
ID($dffe),
ID($dffsr),
ID($dffsre),
ID($adff),
ID($adffe),
ID($aldff),
ID($aldffe),
ID($sdff),
ID($sdffe),
ID($sdffce),
ID($dlatch),
ID($adlatch),
ID($dlatchsr),
ID($_DFFE_NN_),
ID($_DFFE_NP_),
ID($_DFFE_PN_),
ID($_DFFE_PP_),
ID($_DFFSR_NNN_),
ID($_DFFSR_NNP_),
ID($_DFFSR_NPN_),
ID($_DFFSR_NPP_),
ID($_DFFSR_PNN_),
ID($_DFFSR_PNP_),
ID($_DFFSR_PPN_),
ID($_DFFSR_PPP_),
ID($_DFFSRE_NNNN_),
ID($_DFFSRE_NNNP_),
ID($_DFFSRE_NNPN_),
ID($_DFFSRE_NNPP_),
ID($_DFFSRE_NPNN_),
ID($_DFFSRE_NPNP_),
ID($_DFFSRE_NPPN_),
ID($_DFFSRE_NPPP_),
ID($_DFFSRE_PNNN_),
ID($_DFFSRE_PNNP_),
ID($_DFFSRE_PNPN_),
ID($_DFFSRE_PNPP_),
ID($_DFFSRE_PPNN_),
ID($_DFFSRE_PPNP_),
ID($_DFFSRE_PPPN_),
ID($_DFFSRE_PPPP_),
ID($_DFF_N_),
ID($_DFF_P_),
ID($_DFF_NN0_),
ID($_DFF_NN1_),
ID($_DFF_NP0_),
ID($_DFF_NP1_),
ID($_DFF_PN0_),
ID($_DFF_PN1_),
ID($_DFF_PP0_),
ID($_DFF_PP1_),
ID($_DFFE_NN0N_),
ID($_DFFE_NN0P_),
ID($_DFFE_NN1N_),
ID($_DFFE_NN1P_),
ID($_DFFE_NP0N_),
ID($_DFFE_NP0P_),
ID($_DFFE_NP1N_),
ID($_DFFE_NP1P_),
ID($_DFFE_PN0N_),
ID($_DFFE_PN0P_),
ID($_DFFE_PN1N_),
ID($_DFFE_PN1P_),
ID($_DFFE_PP0N_),
ID($_DFFE_PP0P_),
ID($_DFFE_PP1N_),
ID($_DFFE_PP1P_),
ID($_ALDFF_NN_),
ID($_ALDFF_NP_),
ID($_ALDFF_PN_),
ID($_ALDFF_PP_),
ID($_ALDFFE_NNN_),
ID($_ALDFFE_NNP_),
ID($_ALDFFE_NPN_),
ID($_ALDFFE_NPP_),
ID($_ALDFFE_PNN_),
ID($_ALDFFE_PNP_),
ID($_ALDFFE_PPN_),
ID($_ALDFFE_PPP_),
ID($_SDFF_NN0_),
ID($_SDFF_NN1_),
ID($_SDFF_NP0_),
ID($_SDFF_NP1_),
ID($_SDFF_PN0_),
ID($_SDFF_PN1_),
ID($_SDFF_PP0_),
ID($_SDFF_PP1_),
ID($_SDFFE_NN0N_),
ID($_SDFFE_NN0P_),
ID($_SDFFE_NN1N_),
ID($_SDFFE_NN1P_),
ID($_SDFFE_NP0N_),
ID($_SDFFE_NP0P_),
ID($_SDFFE_NP1N_),
ID($_SDFFE_NP1P_),
ID($_SDFFE_PN0N_),
ID($_SDFFE_PN0P_),
ID($_SDFFE_PN1N_),
ID($_SDFFE_PN1P_),
ID($_SDFFE_PP0N_),
ID($_SDFFE_PP0P_),
ID($_SDFFE_PP1N_),
ID($_SDFFE_PP1P_),
ID($_SDFFCE_NN0N_),
ID($_SDFFCE_NN0P_),
ID($_SDFFCE_NN1N_),
ID($_SDFFCE_NN1P_),
ID($_SDFFCE_NP0N_),
ID($_SDFFCE_NP0P_),
ID($_SDFFCE_NP1N_),
ID($_SDFFCE_NP1P_),
ID($_SDFFCE_PN0N_),
ID($_SDFFCE_PN0P_),
ID($_SDFFCE_PN1N_),
ID($_SDFFCE_PN1P_),
ID($_SDFFCE_PP0N_),
ID($_SDFFCE_PP0P_),
ID($_SDFFCE_PP1N_),
ID($_SDFFCE_PP1P_),
ID($_SR_NN_),
ID($_SR_NP_),
ID($_SR_PN_),
ID($_SR_PP_),
ID($_DLATCH_N_),
ID($_DLATCH_P_),
ID($_DLATCH_NN0_),
ID($_DLATCH_NN1_),
ID($_DLATCH_NP0_),
ID($_DLATCH_NP1_),
ID($_DLATCH_PN0_),
ID($_DLATCH_PN1_),
ID($_DLATCH_PP0_),
ID($_DLATCH_PP1_),
ID($_DLATCHSR_NNN_),
ID($_DLATCHSR_NNP_),
ID($_DLATCHSR_NPN_),
ID($_DLATCHSR_NPP_),
ID($_DLATCHSR_PNN_),
ID($_DLATCHSR_PNP_),
ID($_DLATCHSR_PPN_),
ID($_DLATCHSR_PPP_),
ID($_FF_),
};
return res;
}
const pool<IdString> &RTLIL::builtin_ff_cell_types() {
return builtin_ff_cell_types_internal();
static const pool<IdString> res = []() {
pool<IdString> r;
for (size_t i = 0; i < StaticCellTypes::builder.count; i++) {
auto &cell = StaticCellTypes::builder.cells[i];
if (cell.features.is_ff)
r.insert(cell.type);
}
return r;
}();
return res;
}
#define check(condition) log_assert(condition && "malformed Const union")
@ -1333,7 +1228,7 @@ void RTLIL::Design::add(RTLIL::Module *module)
mon->notify_module_add(module);
if (yosys_xtrace) {
log("#X# New Module: %s\n", log_id(module));
log("#X# New Module: %s\n", module);
log_backtrace("-X- ", yosys_xtrace-1);
}
}
@ -1361,7 +1256,7 @@ RTLIL::Module *RTLIL::Design::addModule(RTLIL::IdString name)
mon->notify_module_add(module);
if (yosys_xtrace) {
log("#X# New Module: %s\n", log_id(module));
log("#X# New Module: %s\n", module);
log_backtrace("-X- ", yosys_xtrace-1);
}
@ -1439,7 +1334,7 @@ void RTLIL::Design::remove(RTLIL::Module *module)
mon->notify_module_del(module);
if (yosys_xtrace) {
log("#X# Remove Module: %s\n", log_id(module));
log("#X# Remove Module: %s\n", module);
log_backtrace("-X- ", yosys_xtrace-1);
}
@ -1470,15 +1365,21 @@ void RTLIL::Design::sort_modules()
modules_.sort(sort_by_id_str());
}
void check_module(RTLIL::Module *module, ParallelDispatchThreadPool &thread_pool);
void RTLIL::Design::check()
{
#ifndef NDEBUG
log_assert(!selection_stack.empty());
int pool_size = 0;
for (auto &it : modules_)
pool_size = std::max(pool_size, ThreadPool::work_pool_size(0, it.second->cells_size(), 1000));
ParallelDispatchThreadPool thread_pool(pool_size);
for (auto &it : modules_) {
log_assert(this == it.second->design);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
it.second->check();
check_module(it.second, thread_pool);
}
#endif
}
@ -1575,22 +1476,22 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_modules(RTLIL::SelectPartial
switch (boxes)
{
case RTLIL::SB_UNBOXED_WARN:
log_warning("Ignoring boxed module %s.\n", log_id(it.first));
log_warning("Ignoring boxed module %s.\n", it.first.unescape());
break;
case RTLIL::SB_EXCL_BB_WARN:
log_warning("Ignoring blackbox module %s.\n", log_id(it.first));
log_warning("Ignoring blackbox module %s.\n", it.first.unescape());
break;
case RTLIL::SB_UNBOXED_ERR:
log_error("Unsupported boxed module %s.\n", log_id(it.first));
log_error("Unsupported boxed module %s.\n", it.first.unescape());
break;
case RTLIL::SB_EXCL_BB_ERR:
log_error("Unsupported blackbox module %s.\n", log_id(it.first));
log_error("Unsupported blackbox module %s.\n", it.first.unescape());
break;
case RTLIL::SB_UNBOXED_CMDERR:
log_cmd_error("Unsupported boxed module %s.\n", log_id(it.first));
log_cmd_error("Unsupported boxed module %s.\n", it.first.unescape());
break;
case RTLIL::SB_EXCL_BB_CMDERR:
log_cmd_error("Unsupported blackbox module %s.\n", log_id(it.first));
log_cmd_error("Unsupported blackbox module %s.\n", it.first.unescape());
break;
default:
break;
@ -1599,13 +1500,13 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_modules(RTLIL::SelectPartial
switch(partials)
{
case RTLIL::SELECT_WHOLE_WARN:
log_warning("Ignoring partially selected module %s.\n", log_id(it.first));
log_warning("Ignoring partially selected module %s.\n", it.first.unescape());
break;
case RTLIL::SELECT_WHOLE_ERR:
log_error("Unsupported partially selected module %s.\n", log_id(it.first));
log_error("Unsupported partially selected module %s.\n", it.first.unescape());
break;
case RTLIL::SELECT_WHOLE_CMDERR:
log_cmd_error("Unsupported partially selected module %s.\n", log_id(it.first));
log_cmd_error("Unsupported partially selected module %s.\n", it.first.unescape());
break;
default:
break;
@ -1682,7 +1583,7 @@ void RTLIL::Module::makeblackbox()
void RTLIL::Module::expand_interfaces(RTLIL::Design *, const dict<RTLIL::IdString, RTLIL::Module *> &)
{
log_error("Class doesn't support expand_interfaces (module: `%s')!\n", id2cstr(name));
log_error("Class doesn't support expand_interfaces (module: `%s')!\n", name.unescape());
}
bool RTLIL::Module::reprocess_if_necessary(RTLIL::Design *)
@ -1694,7 +1595,7 @@ RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, const dict<RTLIL::IdString
{
if (mayfail)
return RTLIL::IdString();
log_error("Module `%s' is used with parameters but is not parametric!\n", id2cstr(name));
log_error("Module `%s' is used with parameters but is not parametric!\n", name.unescape());
}
@ -1702,7 +1603,7 @@ RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, const dict<RTLIL::IdString
{
if (mayfail)
return RTLIL::IdString();
log_error("Module `%s' is used with parameters but is not parametric!\n", id2cstr(name));
log_error("Module `%s' is used with parameters but is not parametric!\n", name.unescape());
}
size_t RTLIL::Module::count_id(RTLIL::IdString id)
@ -1714,11 +1615,11 @@ size_t RTLIL::Module::count_id(RTLIL::IdString id)
namespace {
struct InternalCellChecker
{
RTLIL::Module *module;
const RTLIL::Module *module;
RTLIL::Cell *cell;
pool<RTLIL::IdString> expected_params, expected_ports;
InternalCellChecker(RTLIL::Module *module, RTLIL::Cell *cell) : module(module), cell(cell) { }
InternalCellChecker(const RTLIL::Module *module, RTLIL::Cell *cell) : module(module), cell(cell) { }
void error(int linenr)
{
@ -2703,88 +2604,96 @@ void RTLIL::Module::sort()
it.second->attributes.sort(sort_by_id_str());
}
void RTLIL::Module::check()
void check_module(RTLIL::Module *module, ParallelDispatchThreadPool &thread_pool)
{
#ifndef NDEBUG
std::vector<bool> ports_declared;
for (auto &it : wires_) {
log_assert(this == it.second->module);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(it.second->width >= 0);
log_assert(it.second->port_id >= 0);
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
if (it.second->port_id) {
log_assert(GetSize(ports) >= it.second->port_id);
log_assert(ports.at(it.second->port_id-1) == it.first);
log_assert(it.second->port_input || it.second->port_output);
if (GetSize(ports_declared) < it.second->port_id)
ports_declared.resize(it.second->port_id);
log_assert(ports_declared[it.second->port_id-1] == false);
ports_declared[it.second->port_id-1] = true;
} else
log_assert(!it.second->port_input && !it.second->port_output);
}
for (auto port_declared : ports_declared)
log_assert(port_declared == true);
log_assert(GetSize(ports) == GetSize(ports_declared));
ParallelDispatchThreadPool::Subpool subpool(thread_pool, ThreadPool::work_pool_size(0, module->cells_size(), 1000));
const RTLIL::Module *const_module = module;
for (auto &it : memories) {
pool<std::string> memory_strings;
for (auto &it : module->memories) {
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(it.second->width >= 0);
log_assert(it.second->size >= 0);
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
memory_strings.insert(it.second->name.str());
}
pool<IdString> packed_memids;
std::vector<MonotonicFlag> ports_declared(GetSize(module->ports));
ShardedVector<std::string> memids(subpool);
subpool.run([const_module, &ports_declared, &memory_strings, &memids](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(const_module->cells_size())) {
auto it = *const_module->cells_.element(i);
log_assert(const_module == it.second->module);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(!it.second->type.empty());
for (auto &it2 : it.second->connections()) {
log_assert(!it2.first.empty());
it2.second.check(const_module);
}
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
for (auto &it2 : it.second->parameters)
log_assert(!it2.first.empty());
InternalCellChecker checker(const_module, it.second);
checker.check();
if (it.second->has_memid()) {
log_assert(memory_strings.count(it.second->parameters.at(ID::MEMID).decode_string()));
} else if (it.second->is_mem_cell()) {
std::string memid = it.second->parameters.at(ID::MEMID).decode_string();
log_assert(!memory_strings.count(memid));
memids.insert(ctx, std::move(memid));
}
auto cell_mod = const_module->design->module(it.first);
if (cell_mod != nullptr) {
// assertion check below to make sure that there are no
// cases where a cell has a blackbox attribute since
// that is deprecated
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
log_assert(!it.second->get_blackbox_attribute());
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
}
for (auto &it : cells_) {
log_assert(this == it.second->module);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(!it.second->type.empty());
for (auto &it2 : it.second->connections()) {
log_assert(!it2.first.empty());
it2.second.check(this);
for (int i : ctx.item_range(const_module->wires_size())) {
auto it = *const_module->wires_.element(i);
log_assert(const_module == it.second->module);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(it.second->width >= 0);
log_assert(it.second->port_id >= 0);
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
if (it.second->port_id) {
log_assert(GetSize(const_module->ports) >= it.second->port_id);
log_assert(const_module->ports.at(it.second->port_id-1) == it.first);
log_assert(it.second->port_input || it.second->port_output);
log_assert(it.second->port_id <= GetSize(ports_declared));
bool previously_declared = ports_declared[it.second->port_id-1].set_and_return_old();
log_assert(previously_declared == false);
} else
log_assert(!it.second->port_input && !it.second->port_output);
}
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
for (auto &it2 : it.second->parameters)
log_assert(!it2.first.empty());
InternalCellChecker checker(this, it.second);
checker.check();
if (it.second->has_memid()) {
log_assert(memories.count(it.second->parameters.at(ID::MEMID).decode_string()));
} else if (it.second->is_mem_cell()) {
IdString memid = it.second->parameters.at(ID::MEMID).decode_string();
log_assert(!memories.count(memid));
log_assert(!packed_memids.count(memid));
packed_memids.insert(memid);
}
auto cell_mod = design->module(it.first);
if (cell_mod != nullptr) {
// assertion check below to make sure that there are no
// cases where a cell has a blackbox attribute since
// that is deprecated
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
log_assert(!it.second->get_blackbox_attribute());
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
}
});
for (const MonotonicFlag &port_declared : ports_declared)
log_assert(port_declared.load() == true);
pool<std::string> memids_pool;
for (std::string &memid : memids)
log_assert(memids_pool.insert(memid).second);
for (auto &it : processes) {
for (auto &it : module->processes) {
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(it.second->root_case.compare.empty());
std::vector<CaseRule*> all_cases = {&it.second->root_case};
std::vector<RTLIL::CaseRule*> all_cases = {&it.second->root_case};
for (size_t i = 0; i < all_cases.size(); i++) {
for (auto &switch_it : all_cases[i]->switches) {
for (auto &case_it : switch_it->cases) {
@ -2797,34 +2706,41 @@ void RTLIL::Module::check()
}
for (auto &sync_it : it.second->syncs) {
switch (sync_it->type) {
case SyncType::ST0:
case SyncType::ST1:
case SyncType::STp:
case SyncType::STn:
case SyncType::STe:
case RTLIL::SyncType::ST0:
case RTLIL::SyncType::ST1:
case RTLIL::SyncType::STp:
case RTLIL::SyncType::STn:
case RTLIL::SyncType::STe:
log_assert(!sync_it->signal.empty());
break;
case SyncType::STa:
case SyncType::STg:
case SyncType::STi:
case RTLIL::SyncType::STa:
case RTLIL::SyncType::STg:
case RTLIL::SyncType::STi:
log_assert(sync_it->signal.empty());
break;
}
}
}
for (auto &it : connections_) {
for (auto &it : module->connections_) {
log_assert(it.first.size() == it.second.size());
log_assert(!it.first.has_const());
it.first.check(this);
it.second.check(this);
it.first.check(module);
it.second.check(module);
}
for (auto &it : attributes)
for (auto &it : module->attributes)
log_assert(!it.first.empty());
#endif
}
void RTLIL::Module::check()
{
int pool_size = ThreadPool::work_pool_size(0, cells_size(), 1000);
ParallelDispatchThreadPool thread_pool(pool_size);
check_module(this, thread_pool);
}
void RTLIL::Module::optimize()
{
}
@ -2893,14 +2809,14 @@ bool RTLIL::Module::has_processes() const
bool RTLIL::Module::has_memories_warn() const
{
if (!memories.empty())
log_warning("Ignoring module %s because it contains memories (run 'memory' command first).\n", log_id(this));
log_warning("Ignoring module %s because it contains memories (run 'memory' command first).\n", this);
return !memories.empty();
}
bool RTLIL::Module::has_processes_warn() const
{
if (!processes.empty())
log_warning("Ignoring module %s because it contains processes (run 'proc' command first).\n", log_id(this));
log_warning("Ignoring module %s because it contains processes (run 'proc' command first).\n", this);
return !processes.empty();
}
@ -3206,7 +3122,7 @@ void RTLIL::Module::connect(const RTLIL::SigSig &conn)
}
if (yosys_xtrace) {
log("#X# Connect (SigSig) in %s: %s = %s (%d bits)\n", log_id(this), log_signal(conn.first), log_signal(conn.second), GetSize(conn.first));
log("#X# Connect (SigSig) in %s: %s = %s (%d bits)\n", this, log_signal(conn.first), log_signal(conn.second), GetSize(conn.first));
log_backtrace("-X- ", yosys_xtrace-1);
}
@ -3229,7 +3145,7 @@ void RTLIL::Module::new_connections(const std::vector<RTLIL::SigSig> &new_conn)
mon->notify_connect(this, new_conn);
if (yosys_xtrace) {
log("#X# New connections vector in %s:\n", log_id(this));
log("#X# New connections vector in %s:\n", this);
for (auto &conn: new_conn)
log("#X# %s = %s (%d bits)\n", log_signal(conn.first), log_signal(conn.second), GetSize(conn.first));
log_backtrace("-X- ", yosys_xtrace-1);
@ -4702,7 +4618,7 @@ bool RTLIL::Cell::is_mem_cell() const
}
bool RTLIL::Cell::is_builtin_ff() const {
return builtin_ff_cell_types_internal().count(type) > 0;
return StaticCellTypes::categories.is_ff(type);
}
RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit)
@ -5166,31 +5082,35 @@ void RTLIL::SigSpec::remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *othe
other->unpack();
}
bool modified = false;
bool other_modified = false;
for (int i = GetSize(bits_) - 1; i >= 0; i--)
{
if (bits_[i].wire == NULL) continue;
// Convert pattern to pool for O(1) lookup, avoiding O(n*m) chunk iteration
pool<SigBit> pattern_bits;
pattern_bits.reserve(pattern.size());
for (auto &bit : pattern)
if (bit.wire != NULL)
pattern_bits.insert(bit);
for (auto &pattern_chunk : pattern.chunks())
if (bits_[i].wire == pattern_chunk.wire &&
bits_[i].offset >= pattern_chunk.offset &&
bits_[i].offset < pattern_chunk.offset + pattern_chunk.width) {
modified = true;
bits_.erase(bits_.begin() + i);
if (other != NULL) {
other_modified = true;
other->bits_.erase(other->bits_.begin() + i);
}
break;
// Compact in-place to avoid O(n^2) erase operations
size_t write_idx = 0;
for (size_t read_idx = 0; read_idx < bits_.size(); read_idx++)
{
if (!(bits_[read_idx].wire != NULL && pattern_bits.count(bits_[read_idx]))) {
if (write_idx != read_idx) {
bits_[write_idx] = bits_[read_idx];
if (other != NULL)
other->bits_[write_idx] = other->bits_[read_idx];
}
write_idx++;
}
}
bool modified = (write_idx < bits_.size());
if (modified) {
bits_.resize(write_idx);
hash_.clear();
try_repack();
}
if (other_modified) {
if (other != NULL && modified) {
other->bits_.resize(write_idx);
other->hash_.clear();
other->try_repack();
}
@ -5217,24 +5137,27 @@ void RTLIL::SigSpec::remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec
other->unpack();
}
bool modified = false;
bool other_modified = false;
for (int i = GetSize(bits_) - 1; i >= 0; i--) {
if (bits_[i].wire != NULL && pattern.count(bits_[i])) {
modified = true;
bits_.erase(bits_.begin() + i);
if (other != NULL) {
other_modified = true;
other->bits_.erase(other->bits_.begin() + i);
// Avoid O(n^2) complexity by compacting in-place
size_t write_idx = 0;
for (size_t read_idx = 0; read_idx < bits_.size(); read_idx++) {
if (!(bits_[read_idx].wire != NULL && pattern.count(bits_[read_idx]))) {
if (write_idx != read_idx) {
bits_[write_idx] = bits_[read_idx];
if (other != NULL)
other->bits_[write_idx] = other->bits_[read_idx];
}
write_idx++;
}
}
bool modified = (write_idx < bits_.size());
if (modified) {
bits_.resize(write_idx);
hash_.clear();
try_repack();
}
if (other_modified) {
if (other != NULL && modified) {
other->bits_.resize(write_idx);
other->hash_.clear();
other->try_repack();
}
@ -5250,24 +5173,27 @@ void RTLIL::SigSpec::remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigS
other->unpack();
}
bool modified = false;
bool other_modified = false;
for (int i = GetSize(bits_) - 1; i >= 0; i--) {
if (bits_[i].wire != NULL && pattern.count(bits_[i])) {
modified = true;
bits_.erase(bits_.begin() + i);
if (other != NULL) {
other_modified = true;
other->bits_.erase(other->bits_.begin() + i);
// Avoid O(n^2) complexity by compacting in-place
size_t write_idx = 0;
for (size_t read_idx = 0; read_idx < bits_.size(); read_idx++) {
if (!(bits_[read_idx].wire != NULL && pattern.count(bits_[read_idx]))) {
if (write_idx != read_idx) {
bits_[write_idx] = bits_[read_idx];
if (other != NULL)
other->bits_[write_idx] = other->bits_[read_idx];
}
write_idx++;
}
}
bool modified = (write_idx < bits_.size());
if (modified) {
bits_.resize(write_idx);
hash_.clear();
try_repack();
}
if (other_modified) {
if (other != NULL && modified) {
other->bits_.resize(write_idx);
other->hash_.clear();
other->try_repack();
}
@ -5438,26 +5364,32 @@ RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const
log_assert(length >= 0);
log_assert(offset + length <= size());
SigSpec extracted;
Chunks cs = chunks();
auto it = cs.begin();
for (; offset; offset -= it->width, ++it) {
if (offset < it->width) {
int chunk_length = min(it->width - offset, length);
extracted.append(it->extract(offset, chunk_length));
length -= chunk_length;
++it;
break;
}
}
for (; length; length -= it->width, ++it) {
if (length >= it->width) {
extracted.append(*it);
std::vector<SigBit> extracted;
SigBit first;
bool is_packing = true;
for (int i = offset; i < offset + length; i++) {
bool was_packing_before = is_packing;
SigBit bit = (*this)[i];
if (i == offset) {
first = bit;
if (!bit.wire)
is_packing = false;
} else {
extracted.append(it->extract(0, length));
break;
if (bit.wire != first.wire)
is_packing = false;
if (bit.wire)
if (bit.offset != first.offset + (i - offset))
is_packing = false;
}
if (was_packing_before && !is_packing)
for (int j = offset; j < i; j++)
extracted.push_back((*this)[j]);
if (!is_packing)
extracted.push_back((*this)[i]);
}
if (is_packing)
return SigChunk(first.wire, first.offset, length);
return extracted;
}
@ -5562,7 +5494,7 @@ RTLIL::SigSpec RTLIL::SigSpec::repeat(int num) const
}
#ifndef NDEBUG
void RTLIL::SigSpec::check(Module *mod) const
void RTLIL::SigSpec::check(const Module *mod) const
{
if (rep_ == CHUNK)
{

View file

@ -275,6 +275,17 @@ struct RTLIL::IdString
*out += std::to_string(-index_);
}
std::string unescape() const {
if (index_ < 0) {
// Must start with "$auto$" so no unescaping required.
return str();
}
std::string_view str = global_id_storage_.at(index_).str_view();
if (str.size() < 2 || str[0] != '\\' || str[1] == '$' || str[1] == '\\' || (str[1] >= '0' && str[1] <= '9'))
return std::string(str);
return std::string(str.substr(1));
}
class Substrings {
std::string_view first_;
int suffix_number;
@ -737,6 +748,7 @@ template <> struct IDMacroHelper<-1> {
namespace RTLIL {
extern dict<std::string, std::string> constpad;
[[deprecated("use StaticCellTypes::categories.is_ff() instead")]]
const pool<IdString> &builtin_ff_cell_types();
static inline std::string escape_id(const std::string &str) {
@ -758,7 +770,7 @@ namespace RTLIL {
}
static inline std::string unescape_id(RTLIL::IdString str) {
return unescape_id(str.str());
return str.unescape();
}
static inline const char *id2cstr(RTLIL::IdString str) {
@ -1394,6 +1406,8 @@ struct RTLIL::SigSpecConstIterator
struct RTLIL::SigSpec
{
private:
friend class SigSpecRepTest;
FRIEND_TEST(SigSpecRepTest, Extract);
enum Representation : char {
CHUNK,
BITS,
@ -1758,9 +1772,9 @@ public:
}
#ifndef NDEBUG
void check(Module *mod = nullptr) const;
void check(const Module *mod = nullptr) const;
#else
void check(Module *mod = nullptr) const { (void)mod; }
void check(const Module *mod = nullptr) const { (void)mod; }
#endif
};

View file

@ -146,7 +146,7 @@ void RTLIL::Module::bufNormalize()
// already enqueued or becomes reachable when denormalizing $buf or
// $connect cells.
auto enqueue_cell_port = [&](Cell *cell, IdString port) {
xlog("processing cell port %s.%s\n", log_id(cell), log_id(port));
xlog("processing cell port %s.%s\n", cell, port.unescape());
// An empty cell type means the cell got removed
if (cell->type.empty())
@ -270,7 +270,7 @@ void RTLIL::Module::bufNormalize()
// normalized mode).
while (wire_queue_pos < GetSize(wire_queue_entries)) {
auto wire = wire_queue_entries[wire_queue_pos++];
xlog("processing wire %s\n", log_id(wire));
xlog("processing wire %s\n", wire);
if (wire->driverCell_) {
Cell *cell = wire->driverCell_;
@ -287,7 +287,7 @@ void RTLIL::Module::bufNormalize()
log_assert(connect_cell->type == ID($connect));
SigSpec const &sig_a = connect_cell->getPort(ID::A);
SigSpec const &sig_b = connect_cell->getPort(ID::B);
xlog("found $connect cell %s: %s <-> %s\n", log_id(connect_cell), log_signal(sig_a), log_signal(sig_b));
xlog("found $connect cell %s: %s <-> %s\n", connect_cell, log_signal(sig_a), log_signal(sig_b));
for (auto &side : {sig_a, sig_b})
for (auto chunk : side.chunks())
if (chunk.wire)
@ -452,7 +452,7 @@ void RTLIL::Module::bufNormalize()
}
if (wire->driverCell_ == nullptr) {
xlog("wire %s drivers %s\n", log_id(wire), log_signal(wire_drivers));
xlog("wire %s drivers %s\n", wire, log_signal(wire_drivers));
addBuf(NEW_ID, wire_drivers, wire);
}
}
@ -541,7 +541,7 @@ void RTLIL::Cell::unsetPort(RTLIL::IdString portname)
mon->notify_connect(this, conn_it->first, conn_it->second, signal);
if (yosys_xtrace) {
log("#X# Unconnect %s.%s.%s\n", log_id(this->module), log_id(this), log_id(portname));
log("#X# Unconnect %s.%s.%s\n", this->module, this, portname.unescape());
log_backtrace("-X- ", yosys_xtrace-1);
}
@ -601,7 +601,7 @@ void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal)
mon->notify_connect(this, conn_it->first, conn_it->second, signal);
if (yosys_xtrace) {
log("#X# Connect %s.%s.%s = %s (%d)\n", log_id(this->module), log_id(this), log_id(portname), log_signal(signal), GetSize(signal));
log("#X# Connect %s.%s.%s = %s (%d)\n", this->module, this, portname.unescape(), log_signal(signal), GetSize(signal));
log_backtrace("-X- ", yosys_xtrace-1);
}

View file

@ -19,6 +19,7 @@
#include "kernel/satgen.h"
#include "kernel/ff.h"
#include "kernel/yosys_common.h"
USING_YOSYS_NAMESPACE
@ -1378,7 +1379,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
return true;
}
if (cell->type == ID($scopeinfo))
if (cell->type == ID($scopeinfo) || cell->type == ID($input_port))
{
return true;
}
@ -1387,3 +1388,22 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
// .. and all sequential cells with asynchronous inputs
return false;
}
namespace Yosys {
void report_missing_model(bool warn_only, RTLIL::Cell* cell)
{
std::string s;
if (cell->is_builtin_ff())
s = stringf("No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first.\n", cell, cell->type.unescape());
else
s = stringf("No SAT model available for cell %s (%s).\n", cell, cell->type.unescape());
if (warn_only) {
log_formatted_warning_noprefix(s);
} else {
log_formatted_error(s);
}
}
}

View file

@ -102,7 +102,7 @@ struct SatGen
else
vec.push_back(bit == (undef_mode ? RTLIL::State::Sx : RTLIL::State::S1) ? ez->CONST_TRUE : ez->CONST_FALSE);
} else {
std::string wire_name = RTLIL::unescape_id(bit.wire->name);
std::string wire_name = bit.wire->name.unescape();
std::string name = pf +
(bit.wire->width == 1 ? wire_name : stringf("%s [%d]", wire_name, bit.offset));
vec.push_back(ez->frozen_literal(name));
@ -293,6 +293,8 @@ struct SatGen
bool importCell(RTLIL::Cell *cell, int timestep = -1);
};
void report_missing_model(bool warn_only, RTLIL::Cell* cell);
YOSYS_NAMESPACE_END
#endif

View file

@ -100,13 +100,13 @@ 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) + RTLIL::unescape_id(id));
return scopeinfo->has_attribute(attr_prefix(attrs) + id.unescape());
}
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) + RTLIL::unescape_id(id));
auto found = scopeinfo->attributes.find(attr_prefix(attrs) + id.unescape());
if (found == scopeinfo->attributes.end())
return RTLIL::Const();
return found->second;

View file

@ -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, log_id(cursor.scope_name)); }
static inline void log_dump_val_worker(typename IdTree<ModuleItem>::Cursor cursor ) { log("%p %s", cursor.target, cursor.scope_name.unescape()); }
template<typename T>
static inline void log_dump_val_worker(const typename std::unique_ptr<T> &cursor ) { log("unique %p", cursor.get()); }

View file

@ -206,13 +206,16 @@ bool mp_int_to_const(mp_int *a, Const &b, bool is_signed)
return false;
if (negative) {
mp_neg(a, a);
mp_sub_d(a, 1, a);
if (mp_neg(a, a) != MP_OKAY)
return false;
if (mp_sub_d(a, 1, a) != MP_OKAY)
return false;
}
std::vector<unsigned char> buf;
buf.resize(mp_unsigned_bin_size(a));
mp_to_unsigned_bin(a, buf.data());
if (mp_to_unsigned_bin(a, buf.data()) != MP_OKAY)
return false;
Const::Builder b_bits(mp_count_bits(a) + is_signed);
for (int i = 0; i < mp_count_bits(a);) {
@ -279,7 +282,7 @@ static int tcl_get_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
ERROR("object not found")
if (string_flag) {
Tcl_SetResult(interp, (char *) obj->get_string_attribute(attr_id).c_str(), TCL_VOLATILE);
Tcl_SetObjResult(interp, Tcl_NewStringObj(obj->get_string_attribute(attr_id).c_str(), -1));
} else if (int_flag || uint_flag || sint_flag) {
if (!obj->has_attribute(attr_id))
ERROR("attribute missing (required for -int)");
@ -295,7 +298,7 @@ static int tcl_get_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
if (!obj->has_attribute(attr_id))
ERROR("attribute missing (required unless -bool or -string)")
Tcl_SetResult(interp, (char *) obj->attributes.at(attr_id).as_string().c_str(), TCL_VOLATILE);
Tcl_SetObjResult(interp, Tcl_NewStringObj(obj->attributes.at(attr_id).as_string().c_str(), -1));
}
return TCL_OK;
@ -341,7 +344,7 @@ static int tcl_has_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
if (!obj)
ERROR("object not found")
Tcl_SetResult(interp, (char *) std::to_string(obj->has_attribute(attr_id)).c_str(), TCL_VOLATILE);
Tcl_SetObjResult(interp, Tcl_NewStringObj(std::to_string(obj->has_attribute(attr_id)).c_str(), -1));
return TCL_OK;
}
@ -465,14 +468,14 @@ static int tcl_get_param(ClientData, Tcl_Interp *interp, int argc, const char *a
const RTLIL::Const &value = cell->getParam(param_id);
if (string_flag) {
Tcl_SetResult(interp, (char *) value.decode_string().c_str(), TCL_VOLATILE);
Tcl_SetObjResult(interp, Tcl_NewStringObj(value.decode_string().c_str(), -1));
} else if (int_flag || uint_flag || sint_flag) {
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 {
Tcl_SetResult(interp, (char *) value.as_string().c_str(), TCL_VOLATILE);
Tcl_SetObjResult(interp, Tcl_NewStringObj(value.as_string().c_str(), -1));
}
return TCL_OK;
}

View file

@ -17,6 +17,20 @@ static int get_max_threads()
return max_threads;
}
static int init_work_units_per_thread_override()
{
const char *v = getenv("YOSYS_WORK_UNITS_PER_THREAD");
if (v == nullptr)
return 0;
return atoi(v);
}
static int get_work_units_per_thread_override()
{
static int work_units_per_thread = init_work_units_per_thread_override();
return work_units_per_thread;
}
void DeferredLogs::flush()
{
for (auto &m : logs)
@ -31,21 +45,32 @@ int ThreadPool::pool_size(int reserved_cores, int max_worker_threads)
#ifdef YOSYS_ENABLE_THREADS
int available_threads = std::min<int>(std::thread::hardware_concurrency(), get_max_threads());
int num_threads = std::min(available_threads - reserved_cores, max_worker_threads);
return std::max(0, num_threads);
return std::max(0, num_threads);
#else
return 0;
(void)reserved_cores;
(void)max_worker_threads;
(void)get_max_threads();
return 0;
#endif
}
int ThreadPool::work_pool_size(int reserved_cores, int work_units, int work_units_per_thread)
{
int work_units_per_thread_override = get_work_units_per_thread_override();
if (work_units_per_thread_override > 0)
work_units_per_thread = work_units_per_thread_override;
return pool_size(reserved_cores, work_units / work_units_per_thread);
}
ThreadPool::ThreadPool(int pool_size, std::function<void(int)> b)
: body(std::move(b))
{
#ifdef YOSYS_ENABLE_THREADS
threads.reserve(pool_size);
for (int i = 0; i < pool_size; i++)
threads.emplace_back([i, this]{ body(i); });
threads.reserve(pool_size);
for (int i = 0; i < pool_size; i++)
threads.emplace_back([i, this]{ body(i); });
#else
log_assert(pool_size == 0);
(void)pool_size;
#endif
}
@ -57,4 +82,74 @@ ThreadPool::~ThreadPool()
#endif
}
IntRange item_range_for_worker(int num_items, int thread_num, int num_threads)
{
if (num_threads <= 1) {
return {0, num_items};
}
int items_per_thread = num_items / num_threads;
int extra_items = num_items % num_threads;
// The first `extra_items` threads get one extra item.
int start = thread_num * items_per_thread + std::min(thread_num, extra_items);
int end = (thread_num + 1) * items_per_thread + std::min(thread_num + 1, extra_items);
return {start, end};
}
ParallelDispatchThreadPool::ParallelDispatchThreadPool(int pool_size)
#ifdef YOSYS_ENABLE_THREADS
: num_worker_threads_(std::max(1, pool_size) - 1)
#else
: num_worker_threads_(0)
#endif
{
main_to_workers_signal.resize(num_worker_threads_, 0);
// Don't start the threads until we've constructed all our data members.
thread_pool = std::make_unique<ThreadPool>(num_worker_threads_, [this](int thread_num){
run_worker(thread_num);
});
}
ParallelDispatchThreadPool::~ParallelDispatchThreadPool()
{
if (num_worker_threads_ == 0)
return;
current_work = nullptr;
num_active_worker_threads_.store(num_worker_threads_, std::memory_order_relaxed);
signal_workers_start();
wait_for_workers_done();
}
void ParallelDispatchThreadPool::run(std::function<void(const RunCtx &)> work, int max_threads)
{
Multithreading multithreading;
int num_active_worker_threads = num_threads(max_threads) - 1;
if (num_active_worker_threads == 0) {
work({{0}, 1});
return;
}
num_active_worker_threads_.store(num_active_worker_threads, std::memory_order_relaxed);
current_work = &work;
signal_workers_start();
work({{0}, num_active_worker_threads + 1});
wait_for_workers_done();
}
void ParallelDispatchThreadPool::run_worker(int thread_num)
{
#ifdef YOSYS_ENABLE_THREADS
while (true)
{
worker_wait_for_start(thread_num);
if (current_work == nullptr)
break;
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
(*current_work)({{thread_num + 1}, num_active_worker_threads + 1});
signal_worker_done();
}
signal_worker_done();
#else
(void)current_work;
#endif
}
YOSYS_NAMESPACE_END

View file

@ -1,19 +1,47 @@
#include <deque>
#include "kernel/yosys_common.h"
#include "kernel/log.h"
#include "kernel/utils.h"
#ifdef YOSYS_ENABLE_THREADS
#include <condition_variable>
#include <mutex>
#include <thread>
#endif
#include "kernel/yosys_common.h"
#include "kernel/log.h"
#ifndef YOSYS_THREADING_H
#define YOSYS_THREADING_H
YOSYS_NAMESPACE_BEGIN
// Redirect to no-op to avoid dependence on <mutex>
// and <condition_variable> in single-threaded builds
#ifdef YOSYS_ENABLE_THREADS
using Mutex = std::mutex;
using CondVar = std::condition_variable;
using UniqueLock = std::unique_lock<Mutex>;
using LockGuard = std::lock_guard<Mutex>;
#else
struct Mutex {
void lock() {}
void unlock() {}
bool try_lock() { return true; }
};
struct CondVar {
template <class L> void wait(L &) {}
template <class L, class P> void wait(L &, P) {}
void notify_one() {}
void notify_all() {}
};
struct UniqueLock {
UniqueLock(Mutex &) {}
};
struct LockGuard {
LockGuard(Mutex &) {}
};
#endif
// Concurrent queue implementation. Not fast, but simple.
// Multi-producer, multi-consumer, optionally bounded.
// When YOSYS_ENABLE_THREADS is not defined, this is just a non-thread-safe non-blocking deque.
@ -26,26 +54,20 @@ public:
// Push an element into the queue. If it's at capacity, block until there is room.
void push_back(T t)
{
#ifdef YOSYS_ENABLE_THREADS
std::unique_lock<std::mutex> lock(mutex);
UniqueLock lock(mutex);
not_full_condition.wait(lock, [this] { return static_cast<int>(contents.size()) < capacity; });
if (contents.empty())
not_empty_condition.notify_one();
#endif
log_assert(!closed);
contents.push_back(std::move(t));
#ifdef YOSYS_ENABLE_THREADS
if (static_cast<int>(contents.size()) < capacity)
not_full_condition.notify_one();
#endif
}
// Signal that no more elements will be produced. `pop_front()` will return nullopt.
void close()
{
#ifdef YOSYS_ENABLE_THREADS
std::unique_lock<std::mutex> lock(mutex);
UniqueLock lock(mutex);
not_empty_condition.notify_all();
#endif
closed = true;
}
// Pop an element from the queue. Blocks until an element is available
@ -61,39 +83,28 @@ public:
return pop_front_internal(false);
}
private:
#ifdef YOSYS_ENABLE_THREADS
std::optional<T> pop_front_internal(bool wait)
{
std::unique_lock<std::mutex> lock(mutex);
UniqueLock lock(mutex);
if (wait) {
not_empty_condition.wait(lock, [this] { return !contents.empty() || closed; });
}
#else
std::optional<T> pop_front_internal(bool)
{
#endif
if (contents.empty())
return std::nullopt;
#ifdef YOSYS_ENABLE_THREADS
if (static_cast<int>(contents.size()) == capacity)
not_full_condition.notify_one();
#endif
T result = std::move(contents.front());
contents.pop_front();
#ifdef YOSYS_ENABLE_THREADS
if (!contents.empty())
not_empty_condition.notify_one();
#endif
return std::move(result);
}
#ifdef YOSYS_ENABLE_THREADS
std::mutex mutex;
Mutex mutex;
// Signals one waiter thread when the queue changes and is not full.
std::condition_variable not_full_condition;
CondVar not_full_condition;
// Signals one waiter thread when the queue changes and is not empty.
std::condition_variable not_empty_condition;
#endif
CondVar not_empty_condition;
std::deque<T> contents;
int capacity;
bool closed = false;
@ -131,6 +142,11 @@ public:
// The result may be 0.
static int pool_size(int reserved_cores, int max_worker_threads);
// Computes the number of worker threads to use, by dividing work_units among threads.
// For testing purposes you can set YOSYS_WORK_UNITS_PER_THREAD to override `work_units_per_thread`.
// The result may be 0.
static int work_pool_size(int reserved_cores, int work_units, int work_units_per_thread);
// Create a pool of threads running the given closure (parameterized by thread number).
// `pool_size` must be the result of a `pool_size()` call.
ThreadPool(int pool_size, std::function<void(int)> b);
@ -154,20 +170,150 @@ private:
#endif
};
// Divides some number of items into `num_threads` subranges and returns the
// `thread_num`'th subrange. If `num_threads` is zero, returns the whole range.
IntRange item_range_for_worker(int num_items, int thread_num, int num_threads);
// A type that encapsulates the index of a thread in some list of threads. Useful for
// stronger typechecking and code readability.
struct ThreadIndex {
int thread_num;
};
// A set of threads with a `run()` API that runs a closure on all of the threads
// and wait for all those closures to complete. This is a convenient way to implement
// parallel algorithms that use barrier synchronization.
class ParallelDispatchThreadPool
{
public:
// Create a pool of threads running the given closure (parameterized by thread number).
// `pool_size` must be the result of a `pool_size()` call.
// `pool_size` can be zero, which we treat as 1.
ParallelDispatchThreadPool(int pool_size);
~ParallelDispatchThreadPool();
// For each thread running a closure, a `RunCtx` is passed to the closure. Currently
// it contains the thread index and the total number of threads. It can be passed
// directly to any APIs requiring a `ThreadIndex`.
struct RunCtx : public ThreadIndex {
int num_threads;
IntRange item_range(int num_items) const {
return item_range_for_worker(num_items, thread_num, num_threads);
}
};
// Sometimes we only want to activate a subset of the threads in the pool. This
// class provides a way to do that. It provides the same `num_threads()`
// and `run()` APIs as a `ParallelDispatchThreadPool`.
class Subpool {
public:
Subpool(ParallelDispatchThreadPool &parent, int max_threads)
: parent(parent), max_threads(max_threads) {}
// Returns the number of threads that will be used when calling `run()`.
int num_threads() const {
return parent.num_threads(max_threads);
}
void run(std::function<void(const RunCtx &)> work) {
parent.run(std::move(work), max_threads);
}
ParallelDispatchThreadPool &thread_pool() { return parent; }
private:
ParallelDispatchThreadPool &parent;
int max_threads;
};
// Run the `work` function in parallel on each thread in the pool (parameterized by
// thread number). Waits for all work functions to complete. Only one `run()` can be
// active at a time.
// Uses no more than `max_threads` threads (but at least one).
void run(std::function<void(const RunCtx &)> work) {
run(std::move(work), INT_MAX);
}
// Returns the number of threads that will be used when calling `run()`.
int num_threads() const {
return num_threads(INT_MAX);
}
private:
friend class Subpool;
void run(std::function<void(const RunCtx &)> work, int max_threads);
int num_threads(int max_threads) const {
return std::min(num_worker_threads_ + 1, std::max(1, max_threads));
}
void run_worker(int thread_num);
std::function<void(const RunCtx &)> *current_work = nullptr;
// Keeps a correct count even when threads are exiting.
int num_worker_threads_;
// The count of active worker threads for the current `run()`.
// This is only written by the main thread, and only written when
// no other worker threads are running (i.e. all worker threads have
// passed the increment of `done_workers` in `signal_worker_done()`
// and not passed the release of the lock in `worker_wait_for_start()`.
// Although there can't be any races, we still need to make it atomic
// to prevent the compiler reordering accesses so the above invariant
// is maintained.
std::atomic<int> num_active_worker_threads_ = 0;
// Not especially efficient for large numbers of threads. Worker wakeup could scale
// better by conceptually organising workers into a tree and having workers wake
// up their children.
Mutex main_to_workers_signal_mutex;
CondVar main_to_workers_signal_cv;
std::vector<uint8_t> main_to_workers_signal;
void signal_workers_start() {
UniqueLock lock(main_to_workers_signal_mutex);
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
std::fill(main_to_workers_signal.begin(), main_to_workers_signal.begin() + num_active_worker_threads, 1);
// When `num_active_worker_threads_` is small compared to `num_worker_threads_`, we have a "thundering herd"
// problem here. Fixing that would add complexity so don't worry about it for now.
main_to_workers_signal_cv.notify_all();
}
void worker_wait_for_start(int thread_num) {
UniqueLock lock(main_to_workers_signal_mutex);
main_to_workers_signal_cv.wait(lock, [this, thread_num] { return main_to_workers_signal[thread_num] > 0; });
main_to_workers_signal[thread_num] = 0;
}
std::atomic<int> done_workers = 0;
Mutex workers_to_main_signal_mutex;
CondVar workers_to_main_signal_cv;
void signal_worker_done() {
// Must read `num_active_worker_threads_` before we increment `d`! Otherwise
// it is possible we would increment `d`, and then another worker signals the
// main thread that all workers are done, and the main thread writes to
// `num_active_worker_threads_` before we check it.
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
int d = done_workers.fetch_add(1, std::memory_order_release);
if (d + 1 == num_active_worker_threads) {
UniqueLock lock(workers_to_main_signal_mutex);
workers_to_main_signal_cv.notify_all();
}
}
void wait_for_workers_done() {
UniqueLock lock(workers_to_main_signal_mutex);
workers_to_main_signal_cv.wait(lock, [this] {
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
return done_workers.load(std::memory_order_acquire) == num_active_worker_threads;
});
done_workers.store(0, std::memory_order_relaxed);
}
// Ensure `thread_pool` is destroyed before any other members,
// forcing all threads to be joined before destroying the
// members (e.g. workers_to_main_signal_mutex) they might be using.
std::unique_ptr<ThreadPool> thread_pool;
};
template <class T>
class ConcurrentStack
{
public:
void push_back(T &&t) {
#ifdef YOSYS_ENABLE_THREADS
std::lock_guard<std::mutex> lock(mutex);
#endif
LockGuard lock(mutex);
contents.push_back(std::move(t));
}
std::optional<T> try_pop_back() {
#ifdef YOSYS_ENABLE_THREADS
std::lock_guard<std::mutex> lock(mutex);
#endif
LockGuard lock(mutex);
if (contents.empty())
return std::nullopt;
T result = std::move(contents.back());
@ -175,12 +321,387 @@ public:
return result;
}
private:
#ifdef YOSYS_ENABLE_THREADS
std::mutex mutex;
#endif
Mutex mutex;
std::vector<T> contents;
};
// A vector that is sharded into buckets, one per thread. This lets multiple threads write
// efficiently to the vector without synchronization overhead. After all writers have
// finished writing, the vector can be iterated over. The iteration order is deterministic:
// all the elements written by thread 0 in the order it inserted them, followed by all elements
// written by thread 1, etc.
template <typename T>
class ShardedVector {
public:
ShardedVector(const ParallelDispatchThreadPool &thread_pool) {
init(thread_pool.num_threads());
}
ShardedVector(const ParallelDispatchThreadPool::Subpool &thread_pool) {
init(thread_pool.num_threads());
}
// Insert a value, passing the `ThreadIndex` of the writer thread.
// Parallel inserts with different `ThreadIndex` values are fine.
// Inserts must not run concurrently with any other methods (e.g.
// iteration or `empty()`.)
void insert(const ThreadIndex &thread, T value) {
buckets[thread.thread_num].emplace_back(std::move(value));
}
bool empty() const {
for (const std::vector<T> &bucket : buckets)
if (!bucket.empty())
return false;
return true;
}
using Buckets = std::vector<std::vector<T>>;
class iterator {
public:
iterator(typename Buckets::iterator bucket_it, typename Buckets::iterator bucket_end)
: bucket_it(std::move(bucket_it)), bucket_end(std::move(bucket_end)) {
if (bucket_it != bucket_end)
inner_it = bucket_it->begin();
normalize();
}
T& operator*() const { return *inner_it.value(); }
iterator &operator++() {
++*inner_it;
normalize();
return *this;
}
bool operator!=(const iterator &other) const {
return bucket_it != other.bucket_it || inner_it != other.inner_it;
}
private:
void normalize() {
if (bucket_it == bucket_end)
return;
while (inner_it == bucket_it->end()) {
++bucket_it;
if (bucket_it == bucket_end) {
inner_it.reset();
return;
}
inner_it = bucket_it->begin();
}
}
std::optional<typename std::vector<T>::iterator> inner_it;
typename Buckets::iterator bucket_it;
typename Buckets::iterator bucket_end;
};
iterator begin() { return iterator(buckets.begin(), buckets.end()); }
iterator end() { return iterator(buckets.end(), buckets.end()); }
private:
void init(int num_threads) {
buckets.resize(num_threads);
}
Buckets buckets;
};
// This collision handler for `ShardedHashtable` resolves collisions by keeping
// the current value and discarding the other. This is correct when all values with the
// same key are interchangeable, i.e. when the hashtable is being used as a set instead
// of a map.
template <typename V>
struct SetCollisionHandler {
void operator()(typename V::Accumulated &, typename V::Accumulated &) const {}
};
// A hashtable that can be efficiently built in parallel and then looked up concurrently.
// `V` is the type of elements that will be added to the hashtable. It must have a
// member type `Accumulated` representing the combination of multiple `V` elements. This
// can be the same as `V`, but for example `V` could contain a Wire* and `V::Accumulated`
// could contain a `pool<Wire*>`. `KeyEquality` is a class containing an `operator()` that
// returns true of two `V` elements have equal keys.
// `CollisionHandler` is used to reduce two `V::Accumulated` values into a single value.
//
// To use this, first construct a `Builder` and fill it in (in parallel), then construct
// a `ShardedHashtable` from the `Builder`.
template <typename V, typename KeyEquality, typename CollisionHandler>
class ShardedHashtable {
public:
// A combination of a `V` and its hash value.
struct Value {
Value(V value, unsigned int hash) : value(std::move(value)), hash(hash) {}
Value(Value &&) = default;
Value(const Value &) = delete;
Value &operator=(const Value &) = delete;
V value;
unsigned int hash;
};
// A combination of a `V::Accumulated` and its hash value.
struct AccumulatedValue {
AccumulatedValue(typename V::Accumulated value, unsigned int hash) : value(std::move(value)), hash(hash) {}
AccumulatedValue(AccumulatedValue &&) = default;
#if defined(_MSC_VER)
AccumulatedValue(const AccumulatedValue &) {
log_error("Copy constructor called on AccumulatedValue");
}
AccumulatedValue &operator=(const AccumulatedValue &) {
log_error("Copy assignment called on AccumulatedValue");
return *this;
}
#else
AccumulatedValue(const AccumulatedValue &) = delete;
AccumulatedValue &operator=(const AccumulatedValue &) = delete;
#endif
typename V::Accumulated value;
unsigned int hash;
};
// A class containing an `operator()` that returns true of two `AccumulatedValue`
// elements have equal keys.
// Required to insert `AccumulatedValue`s into an `std::unordered_set`.
struct AccumulatedValueEquality {
KeyEquality inner;
AccumulatedValueEquality(const KeyEquality &inner) : inner(inner) {}
bool operator()(const AccumulatedValue &v1, const AccumulatedValue &v2) const {
return inner(v1.value, v2.value);
}
};
// A class containing an `operator()` that returns the hash value of an `AccumulatedValue`.
// Required to insert `AccumulatedValue`s into an `std::unordered_set`.
struct AccumulatedValueHashOp {
size_t operator()(const AccumulatedValue &v) const {
return static_cast<size_t>(v.hash);
}
};
using Shard = std::unordered_set<AccumulatedValue, AccumulatedValueHashOp, AccumulatedValueEquality>;
// First construct one of these. Then populate it in parallel by calling `insert()` from many threads.
// Then do another parallel phase calling `process()` from many threads.
class Builder {
public:
Builder(const ParallelDispatchThreadPool &thread_pool, KeyEquality equality = KeyEquality(), CollisionHandler collision_handler = CollisionHandler())
: collision_handler(std::move(collision_handler)) {
init(thread_pool.num_threads(), std::move(equality));
}
Builder(const ParallelDispatchThreadPool::Subpool &thread_pool, KeyEquality equality = KeyEquality(), CollisionHandler collision_handler = CollisionHandler())
: collision_handler(std::move(collision_handler)) {
init(thread_pool.num_threads(), std::move(equality));
}
// First call `insert` to insert all elements. All inserts must finish
// before calling any `process()`.
void insert(const ThreadIndex &thread, Value v) {
// You might think that for the single-threaded case, we can optimize by
// inserting directly into the `std::unordered_set` here. But that slows things down
// a lot and I never got around to figuring out why.
std::vector<std::vector<Value>> &buckets = all_buckets[thread.thread_num];
size_t bucket = static_cast<size_t>(v.hash) % buckets.size();
buckets[bucket].emplace_back(std::move(v));
}
// Then call `process` for each thread. All `process()`s must finish before using
// the `Builder` to construct a `ShardedHashtable`.
void process(const ThreadIndex &thread) {
int size = 0;
for (std::vector<std::vector<Value>> &buckets : all_buckets)
size += GetSize(buckets[thread.thread_num]);
Shard &shard = shards[thread.thread_num];
shard.reserve(size);
for (std::vector<std::vector<Value>> &buckets : all_buckets) {
for (Value &value : buckets[thread.thread_num])
accumulate(value, shard);
// Free as much memory as we can during the parallel phase.
std::vector<Value>().swap(buckets[thread.thread_num]);
}
}
private:
friend class ShardedHashtable<V, KeyEquality, CollisionHandler>;
void accumulate(Value &value, Shard &shard) {
// With C++20 we could make this more efficient using heterogenous lookup
AccumulatedValue accumulated_value{std::move(value.value), value.hash};
auto [it, inserted] = shard.insert(std::move(accumulated_value));
if (!inserted)
collision_handler(const_cast<typename V::Accumulated &>(it->value), accumulated_value.value);
}
void init(int num_threads, KeyEquality equality) {
all_buckets.resize(num_threads);
for (std::vector<std::vector<Value>> &buckets : all_buckets)
buckets.resize(num_threads);
for (int i = 0; i < num_threads; ++i)
shards.emplace_back(0, AccumulatedValueHashOp(), AccumulatedValueEquality(equality));
}
const CollisionHandler collision_handler;
// A num_threads x num_threads matrix of buckets.
// In the first phase, each thread i gemerates elements and writes them to
// bucket [i][j] where j = hash(element) % num_threads.
// In the second phase, thread i reads from bucket [j][i] for all j, collecting
// all elements where i = hash(element) % num_threads.
std::vector<std::vector<std::vector<Value>>> all_buckets;
std::vector<Shard> shards;
};
// Then finally construct the hashtable:
ShardedHashtable(Builder &builder) : shards(std::move(builder.shards)) {
// Check that all necessary 'process()' calls were made.
for (std::vector<std::vector<Value>> &buckets : builder.all_buckets)
for (std::vector<Value> &bucket : buckets)
log_assert(bucket.empty());
// Free memory.
std::vector<std::vector<std::vector<Value>>>().swap(builder.all_buckets);
}
ShardedHashtable(ShardedHashtable &&other) = default;
ShardedHashtable() {}
ShardedHashtable &operator=(ShardedHashtable &&other) = default;
// Look up by `AccumulatedValue`. If we switch to C++20 then we could use
// heterogenous lookup to support looking up by `Value` here. Returns nullptr
// if the key is not found.
const typename V::Accumulated *find(const AccumulatedValue &v) const {
size_t num_shards = shards.size();
if (num_shards == 0)
return nullptr;
size_t shard = static_cast<size_t>(v.hash) % num_shards;
auto it = shards[shard].find(v);
if (it == shards[shard].end())
return nullptr;
return &it->value;
}
// Insert an element into the table. The caller is responsible for ensuring this does not
// happen concurrently with any other method calls.
void insert(AccumulatedValue v) {
size_t num_shards = shards.size();
if (num_shards == 0)
return;
size_t shard = static_cast<size_t>(v.hash) % num_shards;
shards[shard].insert(v);
}
// Call this for each shard to implement parallel destruction. For very large `ShardedHashtable`s,
// deleting all elements of all shards on a single thread can be a performance bottleneck.
void clear(const ThreadIndex &shard) {
AccumulatedValueEquality equality = shards[shard.thread_num].key_eq();
shards[shard.thread_num] = Shard(0, AccumulatedValueHashOp(), equality);
}
private:
std::vector<Shard> shards;
};
// A concurrent work-queue that can share batches of work across threads.
// Uses a naive implementation of work-stealing.
template <typename T>
class ConcurrentWorkQueue {
public:
// Create a queue that supports the given number of threads and
// groups work into `batch_size` units.
ConcurrentWorkQueue(int num_threads, int batch_size = 100)
: batch_size(batch_size), thread_states(num_threads) {}
int num_threads() const { return GetSize(thread_states); }
// Push some work to do. Pushes and pops with the same `thread` must
// not happen concurrently.
void push(const ThreadIndex &thread, T work) {
ThreadState &thread_state = thread_states[thread.thread_num];
thread_state.next_batch.emplace_back(std::move(work));
if (GetSize(thread_state.next_batch) < batch_size)
return;
bool was_empty;
{
UniqueLock lock(thread_state.batches_lock);
was_empty = thread_state.batches.empty();
thread_state.batches.push_back(std::move(thread_state.next_batch));
}
if (was_empty) {
UniqueLock lock(waiters_lock);
if (num_waiters > 0) {
waiters_cv.notify_one();
}
}
}
// Grab some work to do.
// If all threads enter `pop_batch()`, then instead of deadlocking the
// queue will return no work. That is the only case in which it will
// return no work.
std::vector<T> pop_batch(const ThreadIndex &thread) {
ThreadState &thread_state = thread_states[thread.thread_num];
if (!thread_state.next_batch.empty())
return std::move(thread_state.next_batch);
// Empty our own work queue first.
{
UniqueLock lock(thread_state.batches_lock);
if (!thread_state.batches.empty()) {
std::vector<T> batch = std::move(thread_state.batches.back());
thread_state.batches.pop_back();
return batch;
}
}
// From here on in this function, our work queue is empty.
while (true) {
std::vector<T> batch = try_steal(thread);
if (!batch.empty()) {
return std::move(batch);
}
// Termination: if all threads run out of work, then all of
// them will eventually enter this loop and there will be no further
// notifications on waiters_cv, so all will eventually increment
// num_waiters and wait, so num_waiters == num_threads()
// will become true. In single-threaded builds, num_threads() is 1,
// so we always terminate on the first iteration.
UniqueLock lock(waiters_lock);
++num_waiters;
if (num_waiters == num_threads()) {
waiters_cv.notify_all();
return {};
}
// As above, it's possible that we'll wait here even when there
// are work batches posted by other threads. That's OK.
waiters_cv.wait(lock);
if (num_waiters == num_threads())
return {};
--num_waiters;
}
}
private:
std::vector<T> try_steal(const ThreadIndex &thread) {
for (int i = 1; i < num_threads(); i++) {
int other_thread_num = (thread.thread_num + i) % num_threads();
ThreadState &other_thread_state = thread_states[other_thread_num];
UniqueLock lock(other_thread_state.batches_lock);
if (!other_thread_state.batches.empty()) {
std::vector<T> batch = std::move(other_thread_state.batches.front());
other_thread_state.batches.pop_front();
return batch;
}
}
return {};
}
int batch_size;
struct ThreadState {
// Entirely thread-local.
std::vector<T> next_batch;
Mutex batches_lock;
// Only the associated thread ever adds to this, and only at the back.
// Other threads can remove elements from the front.
std::deque<std::vector<T>> batches;
};
std::vector<ThreadState> thread_states;
Mutex waiters_lock;
CondVar waiters_cv;
// Number of threads waiting for work. Their queues are empty.
int num_waiters = 0;
};
// A monotonic flag. Starts false, and can be set to true in a thread-safe way.
// Once `load()` returns true, it will always return true.
// Uses relaxed atomics so there are no memory ordering guarantees. Do not use this
// to guard access to shared memory.
class MonotonicFlag {
public:
MonotonicFlag() : value(false) {}
bool load() const { return value.load(std::memory_order_relaxed); }
void set() { value.store(true, std::memory_order_relaxed); }
bool set_and_return_old() {
return value.exchange(true, std::memory_order_relaxed);
}
private:
std::atomic<bool> value;
};
YOSYS_NAMESPACE_END
#endif // YOSYS_THREADING_H

View file

@ -105,21 +105,21 @@ struct TimingInfo
auto dst = cell->getPort(ID::DST);
for (const auto &c : src.chunks())
if (!c.wire || !c.wire->port_input)
log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src));
log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", module, cell, log_signal(src));
for (const auto &c : dst.chunks())
if (!c.wire || !c.wire->port_output)
log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module output.\n", log_id(module), log_id(cell), log_signal(dst));
log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module output.\n", module, cell, log_signal(dst));
int rise_max = cell->getParam(ID::T_RISE_MAX).as_int();
int fall_max = cell->getParam(ID::T_FALL_MAX).as_int();
int max = std::max(rise_max,fall_max);
if (max < 0)
log_error("Module '%s' contains specify cell '%s' with T_{RISE,FALL}_MAX < 0.\n", log_id(module), log_id(cell));
log_error("Module '%s' contains specify cell '%s' with T_{RISE,FALL}_MAX < 0.\n", module, cell);
if (cell->getParam(ID::FULL).as_bool()) {
for (const auto &s : src)
for (const auto &d : dst) {
auto r = t.comb.insert(BitBit(s,d));
if (!r.second)
log_error("Module '%s' contains multiple specify cells for SRC '%s' and DST '%s'.\n", log_id(module), log_signal(s), log_signal(d));
log_error("Module '%s' contains multiple specify cells for SRC '%s' and DST '%s'.\n", module, log_signal(s), log_signal(d));
r.first->second = max;
}
}
@ -130,7 +130,7 @@ struct TimingInfo
const auto &d = dst[i];
auto r = t.comb.insert(BitBit(s,d));
if (!r.second)
log_error("Module '%s' contains multiple specify cells for SRC '%s' and DST '%s'.\n", log_id(module), log_signal(s), log_signal(d));
log_error("Module '%s' contains multiple specify cells for SRC '%s' and DST '%s'.\n", module, log_signal(s), log_signal(d));
r.first->second = max;
}
}
@ -139,15 +139,15 @@ struct TimingInfo
auto src = cell->getPort(ID::SRC).as_bit();
auto dst = cell->getPort(ID::DST);
if (!src.wire || !src.wire->port_input)
log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src));
log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", module, cell, log_signal(src));
for (const auto &c : dst.chunks())
if (!c.wire->port_output)
log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module output.\n", log_id(module), log_id(cell), log_signal(dst));
log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module output.\n", module, cell, log_signal(dst));
int rise_max = cell->getParam(ID::T_RISE_MAX).as_int();
int fall_max = cell->getParam(ID::T_FALL_MAX).as_int();
int max = std::max(rise_max,fall_max);
if (max < 0) {
log_warning("Module '%s' contains specify cell '%s' with T_{RISE,FALL}_MAX < 0 which is currently unsupported. Clamping to 0.\n", log_id(module), log_id(cell));
log_warning("Module '%s' contains specify cell '%s' with T_{RISE,FALL}_MAX < 0 which is currently unsupported. Clamping to 0.\n", module, cell);
max = 0;
}
for (const auto &d : dst) {
@ -167,12 +167,12 @@ struct TimingInfo
auto dst = cell->getPort(ID::DST).as_bit();
for (const auto &c : src.chunks())
if (!c.wire || !c.wire->port_input)
log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src));
log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", module, cell, log_signal(src));
if (!dst.wire || !dst.wire->port_input)
log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(dst));
log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module input.\n", module, cell, log_signal(dst));
int max = cell->getParam(ID::T_LIMIT_MAX).as_int();
if (max < 0) {
log_warning("Module '%s' contains specify cell '%s' with T_LIMIT_MAX < 0 which is currently unsupported. Clamping to 0.\n", log_id(module), log_id(cell));
log_warning("Module '%s' contains specify cell '%s' with T_LIMIT_MAX < 0 which is currently unsupported. Clamping to 0.\n", module, cell);
max = 0;
}
for (const auto &s : src) {

View file

@ -125,20 +125,22 @@ public:
};
// ------------------------------------------------
// A simple class for topological sorting
// ------------------------------------------------
// ---------------------------------------------------
// Best-effort topological sorting with loop detection
// ---------------------------------------------------
template <typename T, typename C = std::less<T>> class TopoSort
{
public:
static_assert(!(std::is_pointer<T>::value && std::is_same<C, std::less<T>>::value),
"std::less is run-to-run unstable for pointers");
public:
// We use this ordering of the edges in the adjacency matrix for
// exact compatibility with an older implementation.
struct IndirectCmp {
IndirectCmp(const std::vector<T> &nodes) : node_cmp_(), nodes_(nodes) {}
IndirectCmp(const std::vector<T> &nodes) : node_cmp_(), nodes_(nodes) {}
bool operator()(int a, int b) const
{
log_assert(static_cast<size_t>(a) < nodes_.size());
log_assert(static_cast<size_t>(a) < nodes_.size());
log_assert(static_cast<size_t>(b) < nodes_.size());
return node_cmp_(nodes_[a], nodes_[b]);
}
@ -147,7 +149,9 @@ template <typename T, typename C = std::less<T>> class TopoSort
};
bool analyze_loops;
// The stability doesn't rely on std::less of T, so pointers are safe
std::map<T, int, C> node_to_index;
// edges[i] is the set of nodes with an edge into node i
std::vector<std::set<int, IndirectCmp>> edges;
std::vector<T> sorted;
std::set<std::vector<T>> loops;
@ -160,10 +164,10 @@ template <typename T, typename C = std::less<T>> class TopoSort
int node(T n)
{
auto rv = node_to_index.emplace(n, static_cast<int>(nodes.size()));
if (rv.second) {
nodes.push_back(n);
edges.push_back(std::set<int, IndirectCmp>(indirect_cmp));
auto rv = node_to_index.emplace(n, static_cast<int>(nodes.size()));
if (rv.second) {
nodes.push_back(n);
edges.push_back(std::set<int, IndirectCmp>(indirect_cmp));
}
return rv.first->second;
}
@ -183,13 +187,14 @@ template <typename T, typename C = std::less<T>> class TopoSort
sorted.clear();
found_loops = false;
std::vector<bool> marked_cells(edges.size(), false);
std::vector<bool> active_cells(edges.size(), false);
std::vector<int> active_stack;
std::vector<bool> node_is_sorted(edges.size(), false);
std::vector<bool> node_is_on_stack(edges.size(), false);
// Only used with analyze_loops
std::vector<int> stack;
sorted.reserve(edges.size());
for (const auto &it : node_to_index)
sort_worker(it.second, marked_cells, active_cells, active_stack);
sort_worker(it.second, node_is_sorted, node_is_on_stack, stack);
log_assert(GetSize(sorted) == GetSize(nodes));
@ -211,19 +216,20 @@ template <typename T, typename C = std::less<T>> class TopoSort
return database;
}
private:
private:
bool found_loops;
std::vector<T> nodes;
const IndirectCmp indirect_cmp;
void sort_worker(const int root_index, std::vector<bool> &marked_cells, std::vector<bool> &active_cells, std::vector<int> &active_stack)
void sort_worker(const int root_index, std::vector<bool> &node_is_sorted, std::vector<bool> &node_is_on_stack, std::vector<int> &stack)
{
if (active_cells[root_index]) {
if (node_is_on_stack[root_index]) {
// We've been here before, meaning we have a loop
found_loops = true;
if (analyze_loops) {
std::vector<T> loop;
for (int i = GetSize(active_stack) - 1; i >= 0; i--) {
const int index = active_stack[i];
for (int i = GetSize(stack) - 1; i >= 0; i--) {
const int index = stack[i];
loop.push_back(nodes[index]);
if (index == root_index)
break;
@ -233,23 +239,24 @@ template <typename T, typename C = std::less<T>> class TopoSort
return;
}
if (marked_cells[root_index])
// We're done if we've already sorted this subgraph
if (node_is_sorted[root_index])
return;
if (!edges[root_index].empty()) {
if (analyze_loops)
active_stack.push_back(root_index);
active_cells[root_index] = true;
stack.push_back(root_index);
node_is_on_stack[root_index] = true;
for (int left_n : edges[root_index])
sort_worker(left_n, marked_cells, active_cells, active_stack);
sort_worker(left_n, node_is_sorted, node_is_on_stack, stack);
if (analyze_loops)
active_stack.pop_back();
active_cells[root_index] = false;
stack.pop_back();
node_is_on_stack[root_index] = false;
}
marked_cells[root_index] = true;
node_is_sorted[root_index] = true;
sorted.push_back(nodes[root_index]);
}
};
@ -299,6 +306,24 @@ auto reversed(const T& container) {
return reverse_view{container};
}
// A range of integers [start_, end_) that can be iterated over with a
// C++ range-based for loop.
struct IntRange {
int start_;
int end_;
struct Int {
int v;
int operator*() const { return v; }
Int &operator++() { ++v; return *this; }
bool operator!=(const Int &other) const { return v != other.v; }
};
Int begin() const { return {start_}; }
Int end() const { return {end_}; }
bool operator==(const IntRange &other) const { return start_ == other.start_ && end_ == other.end_; }
bool operator!=(const IntRange &other) const { return !(*this == other); }
};
YOSYS_NAMESPACE_END
#endif

4
kernel/version.cc.in Normal file
View file

@ -0,0 +1,4 @@
namespace Yosys {
const char *yosys_version_str = "@YOSYS_BUILD_INFO@";
const char *yosys_git_hash_str = "@YOSYS_CHECKOUT_INFO@";
}

112
kernel/wallace_tree.h Normal file
View file

@ -0,0 +1,112 @@
/**
* Wallace tree utilities for multi-operand addition using carry-save adders
*
* Terminology:
* - compressor: $fa viewed as reducing 3 inputs to 2 outputs (sum + shifted carry) (3:2 compressor)
* - level: A stage of parallel compression operations
* - depth: Maximum number of 3:2 compressor levels from any input to a signal
*
* References:
* - "Binary Adder Architectures for Cell-Based VLSI and their Synthesis" (https://iis-people.ee.ethz.ch/~zimmi/publications/adder_arch.pdf)
* - "A Suggestion for a Fast Multiplier" (https://www.ece.ucdavis.edu/~vojin/CLASSES/EEC280/Web-page/papers/Arithmetic/Wallace_mult.pdf)
*/
#ifndef WALLACE_TREE_H
#define WALLACE_TREE_H
#include "kernel/sigtools.h"
#include "kernel/yosys.h"
YOSYS_NAMESPACE_BEGIN
inline std::pair<SigSpec, SigSpec> emit_fa(Module *module, SigSpec a, SigSpec b, SigSpec c, int width)
{
SigSpec sum = module->addWire(NEW_ID, width);
SigSpec cout = module->addWire(NEW_ID, width);
module->addFa(NEW_ID, a, b, c, cout, sum);
SigSpec carry;
carry.append(State::S0);
carry.append(cout.extract(0, width - 1));
return {sum, carry};
}
/**
* wallace_reduce_scheduled() - Reduce multiple operands to two using a Wallace tree
* @module: The Yosys module to which the compressors will be added
* @sigs: Vector of input signals (operands) to be reduced
* @width: Target bit-width to which all operands will be zero-extended
* @compressor_count: Optional pointer to return the number of $fa cells emitted
*
* Return: The final two reduced operands, that are to be fed into an adder
*/
inline std::pair<SigSpec, SigSpec> wallace_reduce_scheduled(Module *module, std::vector<SigSpec> &sigs, int width, int *compressor_count = nullptr)
{
struct DepthSig {
SigSpec sig;
int depth;
};
for (auto &s : sigs)
s.extend_u0(width);
std::vector<DepthSig> operands;
operands.reserve(sigs.size());
for (auto &s : sigs)
operands.push_back({s, 0});
// Number of $fa's emitted
if (compressor_count)
*compressor_count = 0;
// Only compress operands ready at current level
for (int level = 0; operands.size() > 2; level++) {
// Partition operands into ready and waiting
std::vector<DepthSig> ready, waiting;
for (auto &op : operands) {
if (op.depth <= level)
ready.push_back(op);
else
waiting.push_back(op);
}
if (ready.size() < 3)
continue;
// Apply compressors to ready operands
std::vector<DepthSig> compressed;
size_t i = 0;
while (i + 2 < ready.size()) {
auto [sum, carry] = emit_fa(module, ready[i].sig, ready[i + 1].sig, ready[i + 2].sig, width);
int new_depth = std::max({ready[i].depth, ready[i + 1].depth, ready[i + 2].depth}) + 1;
compressed.push_back({sum, new_depth});
compressed.push_back({carry, new_depth});
if (compressor_count)
(*compressor_count)++;
i += 3;
}
// Uncompressed operands pass through to next level
for (; i < ready.size(); i++)
compressed.push_back(ready[i]);
// Merge compressed with waiting operands
for (auto &op : waiting)
compressed.push_back(op);
operands = std::move(compressed);
}
if (operands.size() == 0)
return {SigSpec(State::S0, width), SigSpec(State::S0, width)};
else if (operands.size() == 1)
return {operands[0].sig, SigSpec(State::S0, width)};
else {
log_assert(operands.size() == 2);
log(" Wallace tree depth: %d levels of $fa + 1 final $add\n", std::max(operands[0].depth, operands[1].depth));
return {operands[0].sig, operands[1].sig};
}
}
YOSYS_NAMESPACE_END
#endif

View file

@ -18,8 +18,8 @@
*/
#include "kernel/yosys.h"
#include "kernel/celltypes.h"
#include "kernel/log.h"
#include "kernel/newcelltypes.h"
#include "libs/backward-cpp/backward.hpp"
@ -59,7 +59,7 @@ namespace py = pybind11;
# include <dirent.h>
# include <sys/types.h>
# include <sys/stat.h>
# if !defined(YOSYS_DISABLE_SPAWN)
# if defined(YOSYS_ENABLE_SPAWN)
# include <sys/wait.h>
# endif
#endif
@ -94,7 +94,7 @@ const char* yosys_maybe_version() {
}
RTLIL::Design *yosys_design = NULL;
CellTypes yosys_celltypes;
NewCellTypes yosys_celltypes;
#ifdef YOSYS_ENABLE_TCL
Tcl_Interp *yosys_tcl_interp = NULL;
@ -181,7 +181,7 @@ void yosys_banner()
log(" %s\n", yosys_maybe_version());
}
#if !defined(YOSYS_DISABLE_SPAWN)
#if defined(YOSYS_ENABLE_SPAWN)
int run_command(const std::string &command, std::function<void(const std::string&)> process_line)
{
if (!process_line)
@ -230,6 +230,7 @@ PYBIND11_MODULE(pyosys, m) {
// This should not affect using wheels as the dylib has to actually be called
// libyosys_dummy.so for this function to be interacted with at all.
PYBIND11_MODULE(libyosys_dummy, _) {
(void)_;
throw py::import_error("Change your import from 'import libyosys' to 'from pyosys import libyosys'.");
}
#endif
@ -265,7 +266,7 @@ void yosys_setup()
Pass::init_register();
yosys_design = new RTLIL::Design;
yosys_celltypes.setup();
yosys_celltypes.static_cell_types = StaticCellTypes::categories.is_known;
log_push();
}
@ -294,8 +295,6 @@ void yosys_shutdown()
log_errfile = NULL;
log_files.clear();
yosys_celltypes.clear();
#ifdef YOSYS_ENABLE_TCL
if (yosys_tcl_interp != NULL) {
if (!Tcl_InterpDeleted(yosys_tcl_interp)) {
@ -476,17 +475,30 @@ struct TclPass : public Pass {
#endif
#if defined(__linux__) || defined(__CYGWIN__)
#if defined(__linux__) || defined(__CYGWIN__) || defined(__gnu_hurd__)
std::string proc_self_dirname()
{
char path[PATH_MAX];
ssize_t buflen = readlink("/proc/self/exe", path, sizeof(path));
std::string path(4096, '\0');
ssize_t buflen = -1;
// Double until sucess, while avoiding endless loop. Give up
// when symlink is longer than 4096*(2^30) = 4398046511104
// bytes.
for (int tries = 30; 0 < tries; tries--) {
buflen = readlink("/proc/self/exe", path.data(), path.size());
if (buflen < (ssize_t)path.size())
break;
else
path.resize(path.size() * 2);
}
if (buflen < 0) {
log_error("readlink(\"/proc/self/exe\") failed: %s\n", strerror(errno));
path.resize(0);
} else {
while (buflen > 0 && path[buflen-1] != '/')
buflen--;
path.resize(buflen);
}
while (buflen > 0 && path[buflen-1] != '/')
buflen--;
return std::string(path, buflen);
return path;
}
#elif defined(__FreeBSD__) || defined(__NetBSD__)
std::string proc_self_dirname()
@ -529,25 +541,17 @@ std::string proc_self_dirname()
std::string proc_self_dirname()
{
int i = 0;
# ifdef __MINGW32__
char longpath[MAX_PATH + 1];
char shortpath[MAX_PATH + 1];
# else
WCHAR longpath[MAX_PATH + 1];
TCHAR shortpath[MAX_PATH + 1];
# endif
if (!GetModuleFileName(0, longpath, MAX_PATH+1))
if (!GetModuleFileNameA(0, longpath, MAX_PATH+1))
log_error("GetModuleFileName() failed.\n");
if (!GetShortPathName(longpath, shortpath, MAX_PATH+1))
if (!GetShortPathNameA(longpath, shortpath, MAX_PATH+1))
log_error("GetShortPathName() failed.\n");
while (shortpath[i] != 0)
i++;
while (i > 0 && shortpath[i-1] != '/' && shortpath[i-1] != '\\')
shortpath[--i] = 0;
std::string path;
for (i = 0; shortpath[i]; i++)
path += char(shortpath[i]);
return path;
return shortpath;
}
#elif defined(EMSCRIPTEN) || defined(__wasm)
std::string proc_self_dirname()
@ -628,13 +632,11 @@ void init_share_dirname()
yosys_share_dirname = proc_share_path;
return;
}
# ifdef YOSYS_DATDIR
proc_share_path = YOSYS_DATDIR "/";
if (check_directory_exists(proc_share_path, true)) {
yosys_share_dirname = proc_share_path;
return;
}
# endif
# endif
}
#endif
@ -676,11 +678,7 @@ std::string proc_share_dirname()
std::string proc_program_prefix()
{
std::string program_prefix;
#ifdef YOSYS_PROGRAM_PREFIX
program_prefix = YOSYS_PROGRAM_PREFIX;
#endif
return program_prefix;
return YOSYS_PROGRAM_PREFIX;
}
bool fgetline(FILE *f, std::string &buffer)
@ -940,28 +938,28 @@ static char *readline_obj_generator(const char *text, int state)
if (design->selected_active_module.empty())
{
for (auto mod : design->modules())
if (RTLIL::unescape_id(mod->name).compare(0, len, text) == 0)
obj_names.push_back(strdup(log_id(mod->name)));
if (mod->name.unescape().compare(0, len, text) == 0)
obj_names.push_back(strdup(mod->name.unescape().c_str()));
}
else if (design->module(design->selected_active_module) != nullptr)
{
RTLIL::Module *module = design->module(design->selected_active_module);
for (auto w : module->wires())
if (RTLIL::unescape_id(w->name).compare(0, len, text) == 0)
obj_names.push_back(strdup(log_id(w->name)));
if (w->name.unescape().compare(0, len, text) == 0)
obj_names.push_back(strdup(w->name.unescape().c_str()));
for (auto &it : module->memories)
if (RTLIL::unescape_id(it.first).compare(0, len, text) == 0)
obj_names.push_back(strdup(log_id(it.first)));
if (it.first.unescape().compare(0, len, text) == 0)
obj_names.push_back(strdup(it.first.unescape().c_str()));
for (auto cell : module->cells())
if (RTLIL::unescape_id(cell->name).compare(0, len, text) == 0)
obj_names.push_back(strdup(log_id(cell->name)));
if (cell->name.unescape().compare(0, len, text) == 0)
obj_names.push_back(strdup(cell->name.unescape().c_str()));
for (auto &it : module->processes)
if (RTLIL::unescape_id(it.first).compare(0, len, text) == 0)
obj_names.push_back(strdup(log_id(it.first)));
if (it.first.unescape().compare(0, len, text) == 0)
obj_names.push_back(strdup(it.first.unescape().c_str()));
}
std::sort(obj_names.begin(), obj_names.end());
@ -1166,7 +1164,7 @@ struct ScriptCmdPass : public Pass {
if (!mod->selected(w))
continue;
if (!c.second.is_fully_const())
log_error("RHS of selected wire %s.%s is not constant.\n", log_id(mod), log_id(w));
log_error("RHS of selected wire %s.%s is not constant.\n", mod, w);
auto v = c.second.as_const();
Pass::call_on_module(design, mod, v.decode_string());
}

View file

@ -60,6 +60,8 @@
defines the Yosys Makefile would set for your build configuration.
#endif
#include "kernel/yosys_config.h"
#define FRIEND_TEST(test_case_name, test_name) \
friend class test_case_name##_##test_name##_Test
@ -91,6 +93,8 @@
# undef CONST
// `wingdi.h` defines a TRANSPARENT macro that conflicts with X(TRANSPARENT) entry in kernel/constids.inc
# undef TRANSPARENT
// `wingdi.h` defines an ERROR macro that conflicts with `ERROR()` macro in kernel/tclapi.cc
# undef ERROR
#endif
#ifndef PATH_MAX
@ -120,10 +124,10 @@
# define YS_MAYBE_UNUSED
#endif
#if __cplusplus >= 201703L
#if __cplusplus >= 202002L
# define YS_FALLTHROUGH [[fallthrough]];
#else
# error "C++17 or later compatible compiler is required"
# error "C++20 or later compatible compiler is required"
#endif
#if defined(__has_cpp_attribute) && __has_cpp_attribute(gnu::cold)

21
kernel/yosys_config.h.in Normal file
View file

@ -0,0 +1,21 @@
#ifndef YOSYS_CONFIG_H
#define YOSYS_CONFIG_H
// Installation parameters
#define YOSYS_PROGRAM_PREFIX "@YOSYS_PROGRAM_PREFIX@"
#define YOSYS_DATDIR "@YOSYS_INSTALL_DATADIR@"
// Feature toggles
#cmakedefine YOSYS_ENABLE_GLOB
#cmakedefine YOSYS_ENABLE_SPAWN
#cmakedefine YOSYS_ENABLE_THREADS
#cmakedefine YOSYS_ENABLE_DLOPEN
#cmakedefine YOSYS_ENABLE_ZLIB
#cmakedefine YOSYS_ENABLE_PLUGINS
#cmakedefine YOSYS_ENABLE_READLINE
#cmakedefine YOSYS_ENABLE_EDITLINE
#cmakedefine YOSYS_ENABLE_TCL
#cmakedefine YOSYS_ENABLE_PYTHON
#cmakedefine YOSYS_ENABLE_VERIFIC
#endif