Merge from upstream

This commit is contained in:
Akash Levy 2025-08-21 17:56:55 -07:00
commit e54fa487b8
29 changed files with 1314 additions and 793 deletions

View File

@ -4,7 +4,7 @@ name: Build Wheels for PyPI
on:
workflow_dispatch:
schedule:
- cron: '0 10 * * 0'
- cron: "0 10 * * 0"
jobs:
build_wheels:
@ -54,9 +54,6 @@ jobs:
fetch-depth: 0
submodules: true
persist-credentials: false
- if: ${{ matrix.os.family == 'linux' }}
name: "[Linux] Set up QEMU"
uses: docker/setup-qemu-action@v3
- uses: actions/setup-python@v5
- name: Get Boost Source
shell: bash
@ -68,14 +65,19 @@ jobs:
run: |
mkdir -p ffi
curl -L https://github.com/libffi/libffi/releases/download/v3.4.6/libffi-3.4.6.tar.gz | tar --strip-components=1 -xzC ffi
- if: ${{ matrix.os.family == 'linux' }}
name: "[Linux] Bison 3.8.2"
shell: bash
run: |
mkdir -p bison
curl -L https://ftpmirror.gnu.org/gnu/bison/bison-3.8.2.tar.gz | tar --strip-components=1 -xzC bison
## Software installed by default in GitHub Action Runner VMs:
## https://github.com/actions/runner-images
- if: ${{ matrix.os.family == 'macos' }}
name: "[macOS] Flex/Bison"
run: |
brew install flex bison
echo "PATH=$(brew --prefix flex)/bin:$PATH" >> $GITHUB_ENV
echo "PATH=$(brew --prefix bison)/bin:$PATH" >> $GITHUB_ENV
echo "PATH=$(brew --prefix flex)/bin:$(brew --prefix bison)/bin:$PATH" >> $GITHUB_ENV
- if: ${{ matrix.os.family == 'windows' }}
name: "[Windows] Flex/Bison"
run: |
@ -100,16 +102,20 @@ jobs:
CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28
CIBW_BEFORE_ALL: bash ./.github/workflows/wheels/cibw_before_all.sh
CIBW_ENVIRONMENT: >
OPTFLAGS=-O3
CXXFLAGS=-I./boost/pfx/include
LINKFLAGS=-L./boost/pfx/lib
PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig
makeFlags='BOOST_PYTHON_LIB=./boost/pfx/lib/libboost_python*.a'
PATH="$PWD/bison/src:$PATH"
CIBW_ENVIRONMENT_MACOS: >
OPTFLAGS=-O3
CXXFLAGS=-I./boost/pfx/include
LINKFLAGS=-L./boost/pfx/lib
PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig
MACOSX_DEPLOYMENT_TARGET=11
makeFlags='BOOST_PYTHON_LIB=./boost/pfx/lib/libboost_python*.a CONFIG=clang'
PATH="$PWD/bison/src:$PATH"
CIBW_BEFORE_BUILD: bash ./.github/workflows/wheels/cibw_before_build.sh
CIBW_TEST_COMMAND: python3 {project}/tests/arch/ecp5/add_sub.py
- uses: actions/upload-artifact@v4

View File

@ -20,11 +20,19 @@ import os
import yaml
import platform
import subprocess
from pathlib import Path
__dir__ = os.path.dirname(os.path.abspath(__file__))
__yosys_root__ = Path(__file__).absolute().parents[3]
for source in ["boost", "ffi", "bison"]:
if not (__yosys_root__ / source).is_dir():
print(
"You need to download boost, ffi and bison in a similar manner to wheels.yml first."
)
exit(-1)
workflow = yaml.safe_load(open(os.path.join(os.path.dirname(__dir__), "wheels.yml")))
with open(__yosys_root__ / ".github" / "workflows" / "wheels.yml") as f:
workflow = yaml.safe_load(f)
env = os.environ.copy()
@ -40,5 +48,5 @@ for key, value in cibw_step["env"].items():
continue
env[key] = value
env["CIBW_ARCHS"] = os.getenv("CIBW_ARCHS") or platform.machine()
env["CIBW_ARCHS"] = os.getenv("CIBW_ARCHS", platform.machine())
subprocess.check_call(["cibuildwheel"], env=env)

View File

@ -1,23 +1,37 @@
set -e
set -x
#!/bin/bash
set -e -x
# Build-time dependencies
## Linux Docker Images
if command -v yum &> /dev/null; then
yum install -y flex bison
yum install -y flex # manylinux's bison versions are hopelessly out of date
fi
if command -v apk &> /dev/null; then
apk add flex bison
fi
if ! printf '%s\n' '%require "3.8"' '%%' 'start: ;' | bison -o /dev/null /dev/stdin ; then
(
set -e -x
cd bison
./configure
make clean
make install -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu)
)
fi
## macOS/Windows -- installed in GitHub Action itself, not container
# Build Static FFI (platform-dependent but not Python version dependent)
cd ffi
## Ultimate libyosys.so will be shared, so we need fPIC for the static libraries
CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --prefix=$PWD/pfx
## Without this, SHELL has a space in its path which breaks the makefile
make install -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu)
## Forces static library to be used in all situations
sed -i.bak 's@-L${toolexeclibdir} -lffi@${toolexeclibdir}/libffi.a@' ./pfx/lib/pkgconfig/libffi.pc
# Runtime Dependencies
## Build Static FFI (platform-dependent but not Python version dependent)
(
set -e -x
cd ffi
## Ultimate libyosys.so will be shared, so we need fPIC for the static libraries
CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --prefix=$PWD/pfx
make clean
make install -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu)
## Forces static library to be used in all situations
sed -i.bak 's@-L${toolexeclibdir} -lffi@${toolexeclibdir}/libffi.a@' ./pfx/lib/pkgconfig/libffi.pc
)

1
.gitignore vendored
View File

@ -73,6 +73,7 @@ __pycache__
/boost
/ffi
/doxygen
/bison
/venv
/*.whl
/*.egg-info

View File

@ -175,7 +175,7 @@ ifeq ($(OS), Haiku)
CXXFLAGS += -D_DEFAULT_SOURCE
endif
YOSYS_VER := 0.56+105
YOSYS_VER := 0.56+165
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2)
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'.' -f3)
@ -376,7 +376,7 @@ BOOST_PYTHON_LIB ?= $(shell \
# Inside CentOS 7
ifeq (${PLATFORM},centos7)
BOOST_PYTHON_LIB = -L/opt/boost/lib -lboost_python38 -lboost_system -lboost_filesystem
BOOST_PYTHON_LIB = -L/opt/boost/lib -lboost_python38
CXXFLAGS += -I/opt/boost/include
endif
@ -384,7 +384,7 @@ ifeq ($(BOOST_PYTHON_LIB),)
$(error BOOST_PYTHON_LIB could not be detected. Please define manually)
endif
LIBS += $(BOOST_PYTHON_LIB)
LIBS += $(BOOST_PYTHON_LIB) -lboost_filesystem
PY_WRAPPER_FILE = kernel/python_wrappers
OBJS += $(PY_WRAPPER_FILE).o
PY_GEN_SCRIPT= py_wrap_generator

View File

@ -129,7 +129,7 @@ struct BtorWorker
std::replace(src.begin(), src.end(), ' ', '_');
if (srcsymbols.count(src) || module->count_id("\\" + src)) {
for (int i = 1;; i++) {
string s = stringf("%s-%d", src.c_str(), i);
string s = stringf("%s-%d", src, i);
if (!srcsymbols.count(s) && !module->count_id("\\" + s)) {
src = s;
break;
@ -192,7 +192,7 @@ struct BtorWorker
void btorf_push(const string &id)
{
if (verbose) {
f << indent << stringf(" ; begin %s\n", id.c_str());
f << indent << stringf(" ; begin %s\n", id);
indent += " ";
}
}
@ -201,7 +201,7 @@ struct BtorWorker
{
if (verbose) {
indent = indent.substr(4);
f << indent << stringf(" ; end %s\n", id.c_str());
f << indent << stringf(" ; end %s\n", id);
}
}

View File

@ -253,7 +253,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream
const std::string extmoduleFileinfo = getFileinfo(cell);
// Emit extmodule header.
f << stringf(" extmodule %s: %s\n", exported_name.c_str(), extmoduleFileinfo.c_str());
f << stringf(" extmodule %s: %s\n", exported_name, extmoduleFileinfo);
// Emit extmodule ports.
for (auto wire : mod_instance->wires())
@ -280,7 +280,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream
// Emit extmodule "defname" field. This is the name of the verilog blackbox
// that is used when verilog is emitted, so we use the name of mod_instance
// here.
f << stringf("%sdefname = %s\n", indent.c_str(), blackbox_name.c_str());
f << stringf("%sdefname = %s\n", indent, blackbox_name);
// Emit extmodule generic parameters.
for (const auto &p : cell->parameters)
@ -301,7 +301,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream
param_name.end()
);
f << stringf("%sparameter %s = %s\n", indent.c_str(), param_name.c_str(), param_value.c_str());
f << stringf("%sparameter %s = %s\n", indent, param_name, param_value);
}
f << "\n";
@ -417,7 +417,7 @@ struct FirrtlWorker
else
{
string wire_id = make_id(chunk.wire->name);
new_expr = stringf("bits(%s, %d, %d)", wire_id.c_str(), chunk.offset + chunk.width - 1, chunk.offset);
new_expr = stringf("bits(%s, %d, %d)", wire_id, chunk.offset + chunk.width - 1, chunk.offset);
}
if (expr.empty())
@ -477,7 +477,7 @@ struct FirrtlWorker
instanceOf;
std::string cellFileinfo = getFileinfo(cell);
wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceName.c_str(), cellFileinfo.c_str()));
wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent, cell_name, cell_name_comment, instanceName, cellFileinfo));
for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
if (it->second.size() > 0) {
@ -518,7 +518,7 @@ struct FirrtlWorker
// as part of the coalesced subfield assignments for this wire.
register_reverse_wire_map(sourceExpr, *sinkSig);
} else {
wire_exprs.push_back(stringf("\n%s%s <= %s %s", indent.c_str(), sinkExpr.c_str(), sourceExpr.c_str(), cellFileinfo.c_str()));
wire_exprs.push_back(stringf("\n%s%s <= %s %s", indent, sinkExpr, sourceExpr, cellFileinfo));
}
}
}
@ -535,7 +535,7 @@ struct FirrtlWorker
int max_shift_width_bits = FIRRTL_MAX_DSH_WIDTH_ERROR - 1;
string max_shift_string = stringf("UInt<%d>(%d)", max_shift_width_bits, (1<<max_shift_width_bits) - 1);
// Deal with the difference in semantics between FIRRTL and verilog
result = stringf("mux(gt(%s, %s), %s, bits(%s, %d, 0))", b_expr.c_str(), max_shift_string.c_str(), max_shift_string.c_str(), b_expr.c_str(), max_shift_width_bits - 1);
result = stringf("mux(gt(%s, %s), %s, bits(%s, %d, 0))", b_expr, max_shift_string, max_shift_string, b_expr, max_shift_width_bits - 1);
}
return result;
}
@ -543,7 +543,7 @@ struct FirrtlWorker
void emit_module()
{
std::string moduleFileinfo = getFileinfo(module);
f << stringf(" module %s: %s\n", make_id(module->name), moduleFileinfo.c_str());
f << stringf(" module %s: %s\n", make_id(module->name), moduleFileinfo);
vector<string> port_decls, wire_decls, mem_exprs, cell_exprs, wire_exprs;
std::vector<Mem> memories = Mem::get_all_memories(module);
@ -602,7 +602,7 @@ struct FirrtlWorker
if (cell->type.in(ID($not), ID($logic_not), ID($_NOT_), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_bool), ID($reduce_xnor)))
{
string a_expr = make_expr(cell->getPort(ID::A));
wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str()));
wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent, y_id, y_width, cellFileinfo));
if (a_signed) {
a_expr = "asSInt(" + a_expr + ")";
@ -610,7 +610,7 @@ struct FirrtlWorker
// Don't use the results of logical operations (a single bit) to control padding
if (!(cell->type.in(ID($eq), ID($eqx), ID($gt), ID($ge), ID($lt), ID($le), ID($ne), ID($nex), ID($reduce_bool), ID($logic_not)) && y_width == 1) ) {
a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
a_expr = stringf("pad(%s, %d)", a_expr, y_width);
}
// Assume the FIRRTL width is a single bit.
@ -622,27 +622,27 @@ struct FirrtlWorker
firrtl_width = a_width;
} else if (cell->type == ID($logic_not)) {
primop = "eq";
a_expr = stringf("%s, UInt(0)", a_expr.c_str());
a_expr = stringf("%s, UInt(0)", a_expr);
}
else if (cell->type == ID($reduce_and)) primop = "andr";
else if (cell->type == ID($reduce_or)) primop = "orr";
else if (cell->type == ID($reduce_xor)) primop = "xorr";
else if (cell->type == ID($reduce_xnor)) {
primop = "not";
a_expr = stringf("xorr(%s)", a_expr.c_str());
a_expr = stringf("xorr(%s)", a_expr);
}
else if (cell->type == ID($reduce_bool)) {
primop = "neq";
// Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand.
a_expr = stringf("%s, %cInt<%d>(0)", a_expr.c_str(), a_signed ? 'S' : 'U', a_width);
a_expr = stringf("%s, %cInt<%d>(0)", a_expr, a_signed ? 'S' : 'U', a_width);
}
string expr = stringf("%s(%s)", primop.c_str(), a_expr.c_str());
string expr = stringf("%s(%s)", primop, a_expr);
if ((firrtl_is_signed && !always_uint))
expr = stringf("asUInt(%s)", expr.c_str());
expr = stringf("asUInt(%s)", expr);
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
@ -654,13 +654,13 @@ struct FirrtlWorker
string a_expr = make_expr(cell->getPort(ID::A));
string b_expr = make_expr(cell->getPort(ID::B));
std::string cellFileinfo = getFileinfo(cell);
wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str()));
wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent, y_id, y_width, cellFileinfo));
if (a_signed) {
a_expr = "asSInt(" + a_expr + ")";
// Expand the "A" operand to the result width
if (a_width < y_width) {
a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
a_expr = stringf("pad(%s, %d)", a_expr, y_width);
a_width = y_width;
}
}
@ -670,7 +670,7 @@ struct FirrtlWorker
b_expr = "asSInt(" + b_expr + ")";
// Expand the "B" operand to the result width
if (b_width < y_width) {
b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width);
b_expr = stringf("pad(%s, %d)", b_expr, y_width);
b_width = y_width;
}
}
@ -680,11 +680,11 @@ struct FirrtlWorker
if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($_XOR_), ID($xnor), ID($and), ID($_AND_), ID($or), ID($_OR_)))
{
if (a_width < y_width) {
a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
a_expr = stringf("pad(%s, %d)", a_expr, y_width);
a_width = y_width;
}
if (b_width < y_width) {
b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width);
b_expr = stringf("pad(%s, %d)", b_expr, y_width);
b_width = y_width;
}
}
@ -856,23 +856,23 @@ struct FirrtlWorker
string expr;
// Deal with $xnor == ~^ (not xor)
if (primop == "xnor") {
expr = stringf("not(xor(%s, %s))", a_expr.c_str(), b_expr.c_str());
expr = stringf("not(xor(%s, %s))", a_expr, b_expr);
} else {
expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str());
expr = stringf("%s(%s, %s)", primop, a_expr, b_expr);
}
// Deal with FIRRTL's "shift widens" semantics, or the need to widen the FIRRTL result.
// If the operation is signed, the FIRRTL width will be 1 one bit larger.
if (extract_y_bits) {
expr = stringf("bits(%s, %d, 0)", expr.c_str(), y_width - 1);
expr = stringf("bits(%s, %d, 0)", expr, y_width - 1);
} else if (firrtl_is_signed && (firrtl_width + 1) < y_width) {
expr = stringf("pad(%s, %d)", expr.c_str(), y_width);
expr = stringf("pad(%s, %d)", expr, y_width);
}
if ((firrtl_is_signed && !always_uint))
expr = stringf("asUInt(%s)", expr.c_str());
expr = stringf("asUInt(%s)", expr);
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
@ -887,9 +887,9 @@ struct FirrtlWorker
string s_expr = make_expr(cell->getPort(ID::S));
wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), width, cellFileinfo.c_str()));
string expr = stringf("mux(%s, %s, %s)", s_expr.c_str(), b_expr.c_str(), a_expr.c_str());
string expr = stringf("mux(%s, %s, %s)", s_expr, b_expr, a_expr);
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
@ -911,9 +911,9 @@ struct FirrtlWorker
string expr = make_expr(cell->getPort(ID::D));
string clk_expr = "asClock(" + make_expr(cell->getPort(ID::CLK)) + ")";
wire_decls.push_back(stringf("%sreg %s: UInt<%d>, %s %s\n", indent.c_str(), y_id.c_str(), width, clk_expr.c_str(), cellFileinfo.c_str()));
wire_decls.push_back(stringf("%sreg %s: UInt<%d>, %s %s\n", indent, y_id, width, clk_expr, cellFileinfo));
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo));
register_reverse_wire_map(y_id, cell->getPort(ID::Q));
continue;
@ -934,7 +934,7 @@ struct FirrtlWorker
int b_sign = cell->parameters.at(ID::B_WIDTH).as_int() - 1;
b_expr = stringf("validif(not(bits(%s, %d, %d)), %s)", b_string, b_sign, b_sign, b_string);
}
string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_expr.c_str());
string expr = stringf("dshr(%s, %s)", a_expr, b_expr);
cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str()));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
@ -973,7 +973,7 @@ struct FirrtlWorker
// Verilog appears to treat the result as signed, so if the result is wider than "A",
// we need to pad.
if (a_width < y_width) {
a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
a_expr = stringf("pad(%s, %d)", a_expr, y_width);
}
wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width));
cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), a_expr.c_str()));

View File

@ -172,7 +172,7 @@ struct IntersynthBackend : public Backend {
if (sig.size() != 0) {
conntypes_code.insert(stringf("conntype b%d %d 2 %d\n", sig.size(), sig.size(), sig.size()));
celltype_code += stringf(" b%d %s%s", sig.size(), ct.cell_output(cell->type, port.first) ? "*" : "", log_id(port.first));
node_code += stringf(" %s %s", log_id(port.first), netname(conntypes_code, celltypes_code, constcells_code, sig).c_str());
node_code += stringf(" %s %s", log_id(port.first), netname(conntypes_code, celltypes_code, constcells_code, sig));
}
}
for (auto &param : cell->parameters) {
@ -199,13 +199,13 @@ struct IntersynthBackend : public Backend {
if (!flag_notypes) {
*f << stringf("### Connection Types\n");
for (auto code : conntypes_code)
*f << stringf("%s", code.c_str());
*f << stringf("%s", code);
*f << stringf("\n### Cell Types\n");
for (auto code : celltypes_code)
*f << stringf("%s", code.c_str());
*f << stringf("%s", code);
}
*f << stringf("\n### Netlists\n");
*f << stringf("%s", netlists_code.c_str());
*f << stringf("%s", netlists_code);
for (auto lib : libs)
delete lib;

View File

@ -218,8 +218,8 @@ struct SimplecWorker
s[i] -= 'a' - 'A';
util_declarations.push_back("");
util_declarations.push_back(stringf("#ifndef %s", s.c_str()));
util_declarations.push_back(stringf("#define %s", s.c_str()));
util_declarations.push_back(stringf("#ifndef %s", s));
util_declarations.push_back(stringf("#define %s", s));
}
string util_get_bit(const string &signame, int n, int idx)
@ -232,33 +232,33 @@ struct SimplecWorker
if (generated_utils.count(util_name) == 0)
{
util_ifdef_guard(util_name);
util_declarations.push_back(stringf("static inline bool %s(const %s *sig)", util_name.c_str(), sigtype(n).c_str()));
util_declarations.push_back(stringf("static inline bool %s(const %s *sig)", util_name, sigtype(n)));
util_declarations.push_back(stringf("{"));
int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize;
string value_name = stringf("value_%d_%d", std::min(n-1, (word_idx+1)*max_uintsize-1), word_idx*max_uintsize);
util_declarations.push_back(stringf(" return (sig->%s >> %d) & 1;", value_name.c_str(), word_offset));
util_declarations.push_back(stringf(" return (sig->%s >> %d) & 1;", value_name, word_offset));
util_declarations.push_back(stringf("}"));
util_declarations.push_back(stringf("#endif"));
generated_utils.insert(util_name);
}
return stringf("%s(&%s)", util_name.c_str(), signame.c_str());
return stringf("%s(&%s)", util_name, signame);
}
string util_set_bit(const string &signame, int n, int idx, const string &expr)
{
if (n == 1 && idx == 0)
return stringf(" %s.value_0_0 = %s;", signame.c_str(), expr.c_str());
return stringf(" %s.value_0_0 = %s;", signame, expr);
string util_name = stringf("yosys_simplec_set_bit_%d_of_%d", idx, n);
if (generated_utils.count(util_name) == 0)
{
util_ifdef_guard(util_name);
util_declarations.push_back(stringf("static inline void %s(%s *sig, bool value)", util_name.c_str(), sigtype(n).c_str()));
util_declarations.push_back(stringf("static inline void %s(%s *sig, bool value)", util_name, sigtype(n)));
util_declarations.push_back(stringf("{"));
int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize;
@ -266,9 +266,9 @@ struct SimplecWorker
#if 0
util_declarations.push_back(stringf(" if (value)"));
util_declarations.push_back(stringf(" sig->%s |= 1UL << %d;", value_name.c_str(), word_offset));
util_declarations.push_back(stringf(" sig->%s |= 1UL << %d;", value_name, word_offset));
util_declarations.push_back(stringf(" else"));
util_declarations.push_back(stringf(" sig->%s &= ~(1UL << %d);", value_name.c_str(), word_offset));
util_declarations.push_back(stringf(" sig->%s &= ~(1UL << %d);", value_name, word_offset));
#else
util_declarations.push_back(stringf(" sig->%s = (sig->%s & ~((uint%d_t)1 << %d)) | ((uint%d_t)value << %d);",
value_name.c_str(), value_name.c_str(), max_uintsize, word_offset, max_uintsize, word_offset));
@ -279,7 +279,7 @@ struct SimplecWorker
generated_utils.insert(util_name);
}
return stringf(" %s(&%s, %s);", util_name.c_str(), signame.c_str(), expr.c_str());
return stringf(" %s(&%s, %s);", util_name, signame, expr);
}
void create_module_struct(Module *mod)
@ -339,38 +339,38 @@ struct SimplecWorker
for (int i = 0; i < GetSize(topo.sorted); i++)
topoidx[mod->cell(topo.sorted[i])] = i;
string ifdef_name = stringf("yosys_simplec_%s_state_t", cid(mod->name).c_str());
string ifdef_name = stringf("yosys_simplec_%s_state_t", cid(mod->name));
for (int i = 0; i < GetSize(ifdef_name); i++)
if ('a' <= ifdef_name[i] && ifdef_name[i] <= 'z')
ifdef_name[i] -= 'a' - 'A';
struct_declarations.push_back("");
struct_declarations.push_back(stringf("#ifndef %s", ifdef_name.c_str()));
struct_declarations.push_back(stringf("#define %s", ifdef_name.c_str()));
struct_declarations.push_back(stringf("struct %s_state_t", cid(mod->name).c_str()));
struct_declarations.push_back(stringf("#ifndef %s", ifdef_name));
struct_declarations.push_back(stringf("#define %s", ifdef_name));
struct_declarations.push_back(stringf("struct %s_state_t", cid(mod->name)));
struct_declarations.push_back("{");
struct_declarations.push_back(" // Input Ports");
for (Wire *w : mod->wires())
if (w->port_input)
struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w)));
struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), log_id(w)));
struct_declarations.push_back("");
struct_declarations.push_back(" // Output Ports");
for (Wire *w : mod->wires())
if (!w->port_input && w->port_output)
struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w)));
struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), log_id(w)));
struct_declarations.push_back("");
struct_declarations.push_back(" // Internal Wires");
for (Wire *w : mod->wires())
if (!w->port_input && !w->port_output)
struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w)));
struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), log_id(w)));
for (Cell *c : mod->cells())
if (design->module(c->type))
struct_declarations.push_back(stringf(" struct %s_state_t %s; // %s", cid(c->type).c_str(), cid(c->name).c_str(), log_id(c)));
struct_declarations.push_back(stringf(" struct %s_state_t %s; // %s", cid(c->type), cid(c->name), log_id(c)));
struct_declarations.push_back(stringf("};"));
struct_declarations.push_back("#endif");
@ -407,14 +407,14 @@ struct SimplecWorker
string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0";
string expr;
if (cell->type == ID($_AND_)) expr = stringf("%s & %s", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_NAND_)) expr = stringf("!(%s & %s)", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_OR_)) expr = stringf("%s | %s", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_NOR_)) expr = stringf("!(%s | %s)", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_XOR_)) expr = stringf("%s ^ %s", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_XNOR_)) expr = stringf("!(%s ^ %s)", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_ANDNOT_)) expr = stringf("%s & (!%s)", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_ORNOT_)) expr = stringf("%s | (!%s)", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_AND_)) expr = stringf("%s & %s", a_expr, b_expr);
if (cell->type == ID($_NAND_)) expr = stringf("!(%s & %s)", a_expr, b_expr);
if (cell->type == ID($_OR_)) expr = stringf("%s | %s", a_expr, b_expr);
if (cell->type == ID($_NOR_)) expr = stringf("!(%s | %s)", a_expr, b_expr);
if (cell->type == ID($_XOR_)) expr = stringf("%s ^ %s", a_expr, b_expr);
if (cell->type == ID($_XNOR_)) expr = stringf("!(%s ^ %s)", a_expr, b_expr);
if (cell->type == ID($_ANDNOT_)) expr = stringf("%s & (!%s)", a_expr, b_expr);
if (cell->type == ID($_ORNOT_)) expr = stringf("%s | (!%s)", a_expr, b_expr);
log_assert(y.wire);
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
@ -436,8 +436,8 @@ struct SimplecWorker
string c_expr = c.wire ? util_get_bit(work->prefix + cid(c.wire->name), c.wire->width, c.offset) : c.data ? "1" : "0";
string expr;
if (cell->type == ID($_AOI3_)) expr = stringf("!((%s & %s) | %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str());
if (cell->type == ID($_OAI3_)) expr = stringf("!((%s | %s) & %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str());
if (cell->type == ID($_AOI3_)) expr = stringf("!((%s & %s) | %s)", a_expr, b_expr, c_expr);
if (cell->type == ID($_OAI3_)) expr = stringf("!((%s | %s) & %s)", a_expr, b_expr, c_expr);
log_assert(y.wire);
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
@ -461,8 +461,8 @@ struct SimplecWorker
string d_expr = d.wire ? util_get_bit(work->prefix + cid(d.wire->name), d.wire->width, d.offset) : d.data ? "1" : "0";
string expr;
if (cell->type == ID($_AOI4_)) expr = stringf("!((%s & %s) | (%s & %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str());
if (cell->type == ID($_OAI4_)) expr = stringf("!((%s | %s) & (%s | %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str());
if (cell->type == ID($_AOI4_)) expr = stringf("!((%s & %s) | (%s & %s))", a_expr, b_expr, c_expr, d_expr);
if (cell->type == ID($_OAI4_)) expr = stringf("!((%s | %s) & (%s | %s))", a_expr, b_expr, c_expr, d_expr);
log_assert(y.wire);
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
@ -484,9 +484,9 @@ struct SimplecWorker
string s_expr = s.wire ? util_get_bit(work->prefix + cid(s.wire->name), s.wire->width, s.offset) : s.data ? "1" : "0";
// casts to bool are a workaround for CBMC bug (https://github.com/diffblue/cbmc/issues/933)
string expr = stringf("%s ? %s(bool)%s : %s(bool)%s", s_expr.c_str(),
cell->type == ID($_NMUX_) ? "!" : "", b_expr.c_str(),
cell->type == ID($_NMUX_) ? "!" : "", a_expr.c_str());
string expr = stringf("%s ? %s(bool)%s : %s(bool)%s", s_expr,
cell->type == ID($_NMUX_) ? "!" : "", b_expr,
cell->type == ID($_NMUX_) ? "!" : "", a_expr);
log_assert(y.wire);
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
@ -518,7 +518,7 @@ struct SimplecWorker
continue;
if (verbose)
log(" Propagating %s.%s[%d:%d].\n", work->log_prefix.c_str(), log_id(chunk.wire), chunk.offset+chunk.width-1, chunk.offset);
funct_declarations.push_back(stringf(" // Updated signal in %s: %s", work->log_prefix.c_str(), log_signal(chunk)));
funct_declarations.push_back(stringf(" // Updated signal in %s: %s", work->log_prefix, log_signal(chunk)));
}
for (SigBit bit : dirtysig)
@ -636,7 +636,7 @@ struct SimplecWorker
reactivated_cells.clear();
funct_declarations.push_back("");
funct_declarations.push_back(stringf("static void %s(struct %s_state_t *state)", func_name.c_str(), cid(work->module->name).c_str()));
funct_declarations.push_back(stringf("static void %s(struct %s_state_t *state)", func_name, cid(work->module->name)));
funct_declarations.push_back("{");
for (auto &line : preamble)
funct_declarations.push_back(line);

View File

@ -51,16 +51,16 @@ static void print_spice_net(std::ostream &f, RTLIL::SigBit s, std::string &neg,
if (s.wire->port_id)
use_inames = true;
if (s.wire->width > 1)
f << stringf(" %s.%d", spice_id2str(s.wire->name, use_inames, inums).c_str(), s.offset);
f << stringf(" %s.%d", spice_id2str(s.wire->name, use_inames, inums), s.offset);
else
f << stringf(" %s", spice_id2str(s.wire->name, use_inames, inums).c_str());
f << stringf(" %s", spice_id2str(s.wire->name, use_inames, inums));
} else {
if (s == RTLIL::State::S0)
f << stringf(" %s", neg.c_str());
f << stringf(" %s", neg);
else if (s == RTLIL::State::S1)
f << stringf(" %s", pos.c_str());
f << stringf(" %s", pos);
else
f << stringf(" %s%d", ncpf.c_str(), nc_counter++);
f << stringf(" %s%d", ncpf, nc_counter++);
}
}
@ -119,7 +119,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De
}
}
f << stringf(" %s\n", spice_id2str(cell->type).c_str());
f << stringf(" %s\n", spice_id2str(cell->type));
}
for (auto &conn : module->connections())
@ -127,7 +127,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De
f << (buf == "DC" ? stringf("V%d", conn_counter++) : stringf("X%d", cell_counter++));
print_spice_net(f, conn.second.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums);
print_spice_net(f, conn.first.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums);
f << (buf == "DC" ? " DC 0\n" : stringf(" %s\n", buf.c_str()));
f << (buf == "DC" ? " DC 0\n" : stringf(" %s\n", buf));
}
}
@ -242,18 +242,18 @@ struct SpiceBackend : public Backend {
ports.at(wire->port_id-1) = wire;
}
*f << stringf(".SUBCKT %s", spice_id2str(module->name).c_str());
*f << stringf(".SUBCKT %s", spice_id2str(module->name));
for (RTLIL::Wire *wire : ports) {
log_assert(wire != NULL);
if (wire->width > 1) {
for (int i = 0; i < wire->width; i++)
*f << stringf(" %s.%d", spice_id2str(wire->name).c_str(), big_endian ? wire->width - 1 - i : i);
*f << stringf(" %s.%d", spice_id2str(wire->name), big_endian ? wire->width - 1 - i : i);
} else
*f << stringf(" %s", spice_id2str(wire->name).c_str());
*f << stringf(" %s", spice_id2str(wire->name));
}
*f << stringf("\n");
print_spice_module(*f, module, design, neg, pos, buf, ncpf, big_endian, use_inames);
*f << stringf(".ENDS %s\n\n", spice_id2str(module->name).c_str());
*f << stringf(".ENDS %s\n\n", spice_id2str(module->name));
}
if (!top_module_name.empty()) {

View File

@ -5,6 +5,7 @@
#include <string>
#include <stack>
#include <string>
#include <sstream>
/**
* Provide frontend-wide location tracking like what bison generates

View File

@ -33,7 +33,7 @@
*
*/
%require "3.8"
%require "3.6"
%language "c++"
%define api.value.type variant
%define api.prefix {frontend_verilog_yy}

View File

@ -27,10 +27,10 @@ YOSYS_NAMESPACE_BEGIN
struct FfInitVals
{
const SigMap *sigmap;
const SigMapView *sigmap;
dict<SigBit, std::pair<State,SigBit>> initbits;
void set(const SigMap *sigmap_, RTLIL::Module *module)
void set(const SigMapView *sigmap_, RTLIL::Module *module)
{
sigmap = sigmap_;
initbits.clear();
@ -126,7 +126,7 @@ struct FfInitVals
initbits.clear();
}
FfInitVals (const SigMap *sigmap, RTLIL::Module *module)
FfInitVals (const SigMapView *sigmap, RTLIL::Module *module)
{
set(sigmap, module);
}

View File

@ -58,7 +58,7 @@ YOSYS_NAMESPACE_BEGIN
struct FfMergeHelper
{
const SigMap *sigmap;
const SigMapView *sigmap;
RTLIL::Module *module;
FfInitVals *initvals;

View File

@ -12,6 +12,7 @@
#ifndef HASHLIB_H
#define HASHLIB_H
#include <array>
#include <stdexcept>
#include <algorithm>
#include <set>
@ -101,7 +102,7 @@ private:
uint32_t hash = ((a << 5) + a) ^ b;
return hash;
}
public:
public:
void hash32(uint32_t i) {
state = djb2_xor(i, state);
state = mkhash_xorshift(fudge ^ state);
@ -128,6 +129,7 @@ private:
*this = hash_ops<T>::hash_into(t, *this);
}
[[deprecated]]
void commutative_eat(hash_t t) {
state ^= t;
}
@ -357,6 +359,29 @@ template<typename K, int offset = 0, typename OPS = hash_ops<K>> class idict;
template<typename K, typename OPS = hash_ops<K>> class pool;
template<typename K, typename OPS = hash_ops<K>> class mfp;
// Computes the hash value of an unordered set of elements.
// See https://www.preprints.org/manuscript/201710.0192/v1/download.
// This is the Sum(4) algorithm from that paper, which has good collision resistance,
// much better than Sum(1) or Xor(1) (and somewhat better than Xor(4)).
class commutative_hash {
public:
commutative_hash() {
buckets.fill(0);
}
void eat(Hasher h) {
Hasher::hash_t v = h.yield();
size_t index = v & (buckets.size() - 1);
buckets[index] += v;
}
[[nodiscard]] Hasher hash_into(Hasher h) const {
for (auto b : buckets)
h.eat(b);
return h;
}
private:
std::array<Hasher::hash_t, 4> buckets;
};
template<typename K, typename T, typename OPS>
class dict {
struct entry_t
@ -802,14 +827,14 @@ public:
}
[[nodiscard]] Hasher hash_into(Hasher h) const {
commutative_hash comm;
for (auto &it : entries) {
Hasher entry_hash;
entry_hash.eat(it.udata.first);
entry_hash.eat(it.udata.second);
h.commutative_eat(entry_hash.yield());
comm.eat(entry_hash);
}
h.eat(entries.size());
return h;
return comm.hash_into(h);
}
void reserve(size_t n) { entries.reserve(n); }
@ -1190,11 +1215,11 @@ public:
}
[[nodiscard]] Hasher hash_into(Hasher h) const {
commutative_hash comm;
for (auto &it : entries) {
h.commutative_eat(ops.hash(it.udata).yield());
comm.eat(ops.hash(it.udata));
}
h.eat(entries.size());
return h;
return comm.hash_into(h);
}
void reserve(size_t n) { entries.reserve(n); }
@ -1359,7 +1384,8 @@ public:
return p;
}
// Merge sets if the given indices belong to different sets
// Merge sets if the given indices belong to different sets.
// Makes ifind(j) the root of the merged set.
void imerge(int i, int j)
{
i = ifind(i);

View File

@ -412,6 +412,70 @@ static std::string string_view_stringf(std::string_view spec, ...)
return result;
}
static int spec_parameter_size(std::string_view spec)
{
// Every valid spec starts with '%' which means the code below
// won't look before the spec start.
switch (spec[spec.size() - 1]) {
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X':
switch (spec[spec.size() - 2]) {
case 'h':
if (spec[spec.size() - 3] == 'h')
return sizeof(char);
return sizeof(short);
case 'l':
if (spec[spec.size() - 3] == 'l')
return sizeof(long long);
return sizeof(long);
case 'L':
case 'q':
return sizeof(long long);
case 'j':
return sizeof(intmax_t);
case 'z':
case 'Z':
return sizeof(size_t);
case 't':
return sizeof(ptrdiff_t);
}
return sizeof(int);
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
case 'a':
case 'A':
if (spec[spec.size() - 2] == 'L')
return sizeof(long double);
if (spec[spec.size() - 2] == 'l' && spec[spec.size() - 3] == 'l')
return sizeof(long double);
return sizeof(double);
case 'c':
if (spec[spec.size() - 2] == 'l') {
return sizeof(wchar_t);
}
return sizeof(unsigned char);
case 'C':
return sizeof(wchar_t);
case 's':
case 'p':
case 'S':
case 'n':
return sizeof(void *);
case 'm':
return sizeof(int);
default:
return -1;
}
}
template <typename Arg>
static void format_emit_stringf(std::string &result, std::string_view spec, int *dynamic_ints,
DynamicIntCount num_dynamic_ints, Arg arg)
@ -439,7 +503,13 @@ void format_emit_long_long(std::string &result, std::string_view spec, int *dyna
result += std::to_string(static_cast<int>(arg));
return;
}
format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg);
if (spec_parameter_size(spec) <= 4) {
// On some platforms (Wasm) we must ensure that the arg is properly aligned
// after the dynamic `int` parameters.
format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, (int)arg);
} else {
format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg);
}
}
void format_emit_unsigned_long_long(std::string &result, std::string_view spec, int *dynamic_ints,
@ -454,7 +524,13 @@ void format_emit_unsigned_long_long(std::string &result, std::string_view spec,
result += static_cast<char>(arg);
return;
}
format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg);
if (spec_parameter_size(spec) <= 4) {
// On some platforms (Wasm) we must ensure that the arg is properly aligned
// after the dynamic `int` parameters.
format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, (unsigned int)arg);
} else {
format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg);
}
}
void format_emit_double(std::string &result, std::string_view spec, int *dynamic_ints,

View File

@ -237,6 +237,42 @@ using sort_by_name_id_guard = typename std::enable_if<std::is_same<T,RTLIL::Cell
template<typename T>
class SigSet<T, sort_by_name_id_guard<T>> : public SigSet<T, RTLIL::sort_by_name_id<typename std::remove_pointer<T>::type>> {};
struct SigMapView
{
mfp<SigBit> database;
// Modify bit to its representative
void apply(RTLIL::SigBit &bit) const
{
bit = database.find(bit);
}
void apply(RTLIL::SigSpec &sig) const
{
for (auto &bit : sig)
apply(bit);
}
RTLIL::SigBit operator()(RTLIL::SigBit bit) const
{
apply(bit);
return bit;
}
RTLIL::SigSpec operator()(RTLIL::SigSpec sig) const
{
apply(sig);
return sig;
}
RTLIL::SigSpec operator()(RTLIL::Wire *wire) const
{
SigSpec sig(wire);
apply(sig);
return sig;
}
};
/**
* SigMap wraps a union-find "database"
* to map SigBits of a module to canonical representative SigBits.
@ -244,10 +280,8 @@ class SigSet<T, sort_by_name_id_guard<T>> : public SigSet<T, RTLIL::sort_by_name
* If a SigBit has a const state (impl: bit.wire is nullptr),
* it's promoted to a representative.
*/
struct SigMap
struct SigMap final : public SigMapView
{
mfp<SigBit> database;
SigMap(RTLIL::Module *module = NULL)
{
if (module != NULL)
@ -320,37 +354,6 @@ struct SigMap
inline void add(Wire *wire) { return add(RTLIL::SigSpec(wire)); }
// Modify bit to its representative
void apply(RTLIL::SigBit &bit) const
{
bit = database.find(bit);
}
void apply(RTLIL::SigSpec &sig) const
{
for (auto &bit : sig)
apply(bit);
}
RTLIL::SigBit operator()(RTLIL::SigBit bit) const
{
apply(bit);
return bit;
}
RTLIL::SigSpec operator()(RTLIL::SigSpec sig) const
{
apply(sig);
return sig;
}
RTLIL::SigSpec operator()(RTLIL::Wire *wire) const
{
SigSpec sig(wire);
apply(sig);
return sig;
}
// All non-const bits
RTLIL::SigSpec allbits() const
{
@ -362,6 +365,107 @@ struct SigMap
}
};
/**
* SiValgMap wraps a union-find "database" to map SigBits of a module to
* canonical representative SigBits plus some optional Val value associated with the bits.
* Val has a commutative, associative, idempotent operator|=, a default constructor
* which constructs an identity element, and a copy constructor.
* SigBits that are connected share a set in the underlying database;
* the associated value is the "sum" of all the values associated with the contributing bits.
* If any of the SigBits in a set are a constant, the canonical SigBit is a constant.
*/
template <class Val>
struct SigValMap final : public SigMapView
{
dict<SigBit, Val> values;
void swap(SigValMap<Val> &other)
{
database.swap(other.database);
values.swap(other.values);
}
void clear()
{
database.clear();
values.clear();
}
// Rebuild SigMap for all connections in module
void set(RTLIL::Module *module)
{
int bitcount = 0;
for (auto &it : module->connections())
bitcount += it.first.size();
database.clear();
values.clear();
database.reserve(bitcount);
for (auto &it : module->connections())
add(it.first, it.second);
}
// Add connections from "from" to "to", bit-by-bit.
void add(const RTLIL::SigSpec& from, const RTLIL::SigSpec& to)
{
log_assert(GetSize(from) == GetSize(to));
for (int i = 0; i < GetSize(from); i++)
{
int bfi = database.lookup(from[i]);
int bti = database.lookup(to[i]);
if (bfi == bti) {
continue;
}
const RTLIL::SigBit &bf = database[bfi];
const RTLIL::SigBit &bt = database[bti];
if (bf.wire == nullptr) {
// bf is constant so make it the canonical representative.
database.imerge(bti, bfi);
merge_value(bt, bf);
} else {
// Make bt the canonical representative.
database.imerge(bfi, bti);
merge_value(bf, bt);
}
}
}
void addVal(const RTLIL::SigBit &bit, const Val &val)
{
values[database.find(bit)] |= val;
}
void addVal(const RTLIL::SigSpec &sig, const Val &val)
{
for (const auto &bit : sig)
addVal(bit, val);
}
Val apply_and_get_value(RTLIL::SigBit &bit) const
{
bit = database.find(bit);
auto it = values.find(bit);
return it == values.end() ? Val() : it->second;
}
private:
void merge_value(const RTLIL::SigBit &from, const RTLIL::SigBit &to)
{
auto it = values.find(from);
if (it == values.end()) {
return;
}
// values[to] could resize the underlying `entries` so
// finish using `it` first.
Val v = it->second;
values.erase(it);
values[to] |= v;
}
};
YOSYS_NAMESPACE_END
#endif /* SIGTOOLS_H */

View File

@ -20,6 +20,7 @@
#ifndef YOSYS_COMMON_H
#define YOSYS_COMMON_H
#include <array>
#include <map>
#include <set>
#include <tuple>

View File

@ -373,6 +373,12 @@ struct AbstractPass : public Pass {
log(" abstractions performed by either mode. This option is not supported in\n");
log(" the -init mode.\n");
log("\n");
log(" -initstates <n>\n");
log(" Perform conditional abstraction for the first <n> time steps. See the\n");
log(" description of the -state and -value modes for details on how the\n");
log(" condition affects the abstractions performed by either mode. This option\n");
log(" is not supported in the -init mode.\n");
log("\n");
log(" -slice <lhs>:<rhs>\n");
log(" -slice <index>\n");
log(" -rtlilslice <lhs>:<rhs>\n");
@ -402,8 +408,10 @@ struct AbstractPass : public Pass {
Always = -1,
ActiveLow = false, // ensuring we can use bool(enable)
ActiveHigh = true,
Initstates = 2,
};
Enable enable = Enable::Always;
int initstates = 0;
std::string enable_name;
std::vector<Slice> slices;
for (argidx = 1; argidx < args.size(); argidx++)
@ -435,6 +443,13 @@ struct AbstractPass : public Pass {
enable = Enable::ActiveLow;
continue;
}
if (arg == "-initstates" && argidx + 1 < args.size()) {
if (enable != Enable::Always)
log_cmd_error("Multiple enable condition are not supported\n");
initstates = atoi(args[++argidx].c_str());
enable = Enable::Initstates;
continue;
}
if (arg == "-slice" && argidx + 1 < args.size()) {
slices.emplace_back(SliceIndices::HdlSlice, args[++argidx]);
continue;
@ -451,22 +466,50 @@ struct AbstractPass : public Pass {
if (mode == Mode::Initial)
log_cmd_error("Conditional initial value abstraction is not supported\n");
if (enable_name.empty())
log_cmd_error("Unspecified enable wire\n");
switch (enable) {
case Enable::Always:
log_assert(false);
case Enable::ActiveLow:
case Enable::ActiveHigh: {
if (enable_name.empty())
log_cmd_error("Unspecified enable wire\n");
} break;
case Enable::Initstates: {
if (initstates <= 0)
log_cmd_error("Number of initial time steps must be positive\n");
} break;
}
}
unsigned int changed = 0;
if ((mode == State) || (mode == Value)) {
for (auto mod : design->selected_modules()) {
EnableLogic enable_logic = { State::S1, true };
if (enable != Enable::Always) {
Wire *enable_wire = mod->wire("\\" + enable_name);
if (!enable_wire)
log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str());
if (GetSize(enable_wire) != 1)
log_cmd_error("Enable wire %s must have width 1 but has width %d in module %s\n",
enable_name.c_str(), GetSize(enable_wire), mod->name.c_str());
enable_logic = { enable_wire, enable == Enable::ActiveHigh };
EnableLogic enable_logic;
switch (enable) {
case Enable::Always: {
enable_logic = { State::S1, true };
} break;
case Enable::ActiveLow:
case Enable::ActiveHigh: {
Wire *enable_wire = mod->wire("\\" + enable_name);
if (!enable_wire)
log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str());
if (GetSize(enable_wire) != 1)
log_cmd_error("Enable wire %s must have width 1 but has width %d in module %s\n",
enable_name.c_str(), GetSize(enable_wire), mod->name.c_str());
enable_logic = { enable_wire, enable == Enable::ActiveHigh };
} break;
case Enable::Initstates: {
SigBit in_init_states = mod->Initstate(NEW_ID);
for (int i = 1; i < initstates; i++) {
Wire *in_init_states_q = mod->addWire(NEW_ID);
mod->addFf(NEW_ID, in_init_states, in_init_states_q);
in_init_states_q->attributes[ID::init] = State::S1;
in_init_states = in_init_states_q;
}
enable_logic = { in_init_states, true };
} break;
}
if (mode == State)
changed += abstract_state(mod, enable_logic, slices);

View File

@ -195,16 +195,23 @@ struct CheckPass : public Pass {
// in port widths are those for us to check.
if (!cell->type.in(
ID($add), ID($sub),
ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx)))
ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx),
ID($pmux), ID($bmux)))
return false;
int in_widths = 0, out_widths = 0;
for (auto &conn : cell->connections()) {
if (cell->input(conn.first))
in_widths += conn.second.size();
if (cell->output(conn.first))
out_widths += conn.second.size();
if (cell->type.in(ID($pmux), ID($bmux))) {
// We're skipping inputs A and B, since each of their bits contributes only one edge
in_widths = GetSize(cell->getPort(ID::S));
out_widths = GetSize(cell->getPort(ID::Y));
} else {
for (auto &conn : cell->connections()) {
if (cell->input(conn.first))
in_widths += conn.second.size();
if (cell->output(conn.first))
out_widths += conn.second.size();
}
}
const int threshold = 1024;

View File

@ -598,6 +598,8 @@ struct RenamePass : public Pass {
for (auto &it : new_cell_names)
module->rename(it.first, it.second);
module->fixup_ports();
}
}
else

View File

@ -44,20 +44,16 @@ struct OptMergeWorker
CellTypes ct;
int total_count;
static vector<pair<SigBit, SigSpec>> sorted_pmux_in(const dict<RTLIL::IdString, RTLIL::SigSpec> &conn)
static Hasher hash_pmux_in(const SigSpec& sig_s, const SigSpec& sig_b, Hasher h)
{
SigSpec sig_s = conn.at(ID::S);
SigSpec sig_b = conn.at(ID::B);
int s_width = GetSize(sig_s);
int width = GetSize(sig_b) / s_width;
vector<pair<SigBit, SigSpec>> sb_pairs;
hashlib::commutative_hash comm;
for (int i = 0; i < s_width; i++)
sb_pairs.push_back(pair<SigBit, SigSpec>(sig_s[i], sig_b.extract(i*width, width)));
comm.eat(hash_ops<std::pair<SigBit, SigSpec>>::hash({sig_s[i], sig_b.extract(i*width, width)}));
std::sort(sb_pairs.begin(), sb_pairs.end());
return sb_pairs;
return comm.hash_into(h);
}
static void sort_pmux_conn(dict<RTLIL::IdString, RTLIL::SigSpec> &conn)
@ -89,12 +85,10 @@ struct OptMergeWorker
// (builtin || stdcell) && (unary || binary) && symmetrical
if (cell->type.in(ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($mul),
ID($logic_and), ID($logic_or), ID($_AND_), ID($_OR_), ID($_XOR_))) {
std::array<RTLIL::SigSpec, 2> inputs = {
assign_map(cell->getPort(ID::A)),
assign_map(cell->getPort(ID::B))
};
std::sort(inputs.begin(), inputs.end());
h = hash_ops<std::array<RTLIL::SigSpec, 2>>::hash_into(inputs, h);
hashlib::commutative_hash comm;
comm.eat(hash_ops<RTLIL::SigSpec>::hash(assign_map(cell->getPort(ID::A))));
comm.eat(hash_ops<RTLIL::SigSpec>::hash(assign_map(cell->getPort(ID::B))));
h = comm.hash_into(h);
} else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) {
SigSpec a = assign_map(cell->getPort(ID::A));
a.sort();
@ -104,44 +98,31 @@ struct OptMergeWorker
a.sort_and_unify();
h = a.hash_into(h);
} else if (cell->type == ID($pmux)) {
dict<RTLIL::IdString, RTLIL::SigSpec> conn = cell->connections();
assign_map.apply(conn.at(ID::A));
assign_map.apply(conn.at(ID::B));
assign_map.apply(conn.at(ID::S));
for (const auto& [s_bit, b_chunk] : sorted_pmux_in(conn)) {
h = s_bit.hash_into(h);
h = b_chunk.hash_into(h);
}
SigSpec sig_s = assign_map(cell->getPort(ID::S));
SigSpec sig_b = assign_map(cell->getPort(ID::B));
h = hash_pmux_in(sig_s, sig_b, h);
h = assign_map(cell->getPort(ID::A)).hash_into(h);
} else {
std::vector<std::pair<IdString, SigSpec>> conns;
for (const auto& conn : cell->connections()) {
conns.push_back(conn);
hashlib::commutative_hash comm;
for (const auto& [port, sig] : cell->connections()) {
if (cell->output(port))
continue;
comm.eat(hash_ops<std::pair<IdString, SigSpec>>::hash({port, assign_map(sig)}));
}
std::sort(conns.begin(), conns.end());
for (const auto& [port, sig] : conns) {
if (!cell->output(port)) {
h = port.hash_into(h);
h = assign_map(sig).hash_into(h);
}
}
h = comm.hash_into(h);
if (RTLIL::builtin_ff_cell_types().count(cell->type))
h = initvals(cell->getPort(ID::Q)).hash_into(h);
}
return h;
}
static Hasher hash_cell_parameters(const RTLIL::Cell *cell, Hasher h)
{
using Paramvec = std::vector<std::pair<IdString, Const>>;
Paramvec params;
hashlib::commutative_hash comm;
for (const auto& param : cell->parameters) {
params.push_back(param);
comm.eat(hash_ops<std::pair<IdString, Const>>::hash(param));
}
std::sort(params.begin(), params.end());
return hash_ops<Paramvec>::hash_into(params, h);
return comm.hash_into(h);
}
Hasher hash_cell_function(const RTLIL::Cell *cell, Hasher h) const
@ -227,7 +208,7 @@ struct OptMergeWorker
}
OptMergeWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all, bool mode_keepdc) :
design(design), module(module), assign_map(module), mode_share_all(mode_share_all)
design(design), module(module), mode_share_all(mode_share_all)
{
total_count = 0;
ct.setup_internals();

View File

@ -23,6 +23,8 @@
#include "kernel/celltypes.h"
#include <stdlib.h>
#include <stdio.h>
#include <unordered_map>
#include <unordered_set>
#include <set>
USING_YOSYS_NAMESPACE
@ -291,14 +293,14 @@ struct OptMuxtreeWorker
// database of known inactive signals
// the payload is a reference counter used to manage the
// list. when it is non-zero the signal in known to be inactive
vector<int> known_inactive;
std::unordered_map<int, int> known_inactive;
// database of known active signals
vector<int> known_active;
std::unordered_map<int, int> known_active;
// this is just used to keep track of visited muxes in order to prohibit
// endless recursion in mux loops
vector<bool> visited_muxes;
std::unordered_set<int> visited_muxes;
};
void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx, bool do_replace_known, bool do_enable_ports, int abort_count)
@ -315,17 +317,18 @@ struct OptMuxtreeWorker
if (i == port_idx)
continue;
if (muxinfo.ports[i].ctrl_sig >= 0)
knowledge.known_inactive.at(muxinfo.ports[i].ctrl_sig)++;
++knowledge.known_inactive[muxinfo.ports[i].ctrl_sig];
}
if (port_idx < GetSize(muxinfo.ports)-1 && !muxinfo.ports[port_idx].const_activated)
knowledge.known_active.at(muxinfo.ports[port_idx].ctrl_sig)++;
++knowledge.known_active[muxinfo.ports[port_idx].ctrl_sig];
vector<int> parent_muxes;
for (int m : muxinfo.ports[port_idx].input_muxes) {
if (knowledge.visited_muxes[m])
auto it = knowledge.visited_muxes.find(m);
if (it != knowledge.visited_muxes.end())
continue;
knowledge.visited_muxes[m] = true;
knowledge.visited_muxes.insert(it, m);
parent_muxes.push_back(m);
}
for (int m : parent_muxes) {
@ -344,16 +347,24 @@ struct OptMuxtreeWorker
return;
}
for (int m : parent_muxes)
knowledge.visited_muxes[m] = false;
knowledge.visited_muxes.erase(m);
if (port_idx < GetSize(muxinfo.ports)-1 && !muxinfo.ports[port_idx].const_activated)
knowledge.known_active.at(muxinfo.ports[port_idx].ctrl_sig)--;
if (port_idx < GetSize(muxinfo.ports)-1 && !muxinfo.ports[port_idx].const_activated) {
auto it = knowledge.known_active.find(muxinfo.ports[port_idx].ctrl_sig);
if (it != knowledge.known_active.end())
if (--it->second == 0)
knowledge.known_active.erase(it);
}
for (int i = 0; i < GetSize(muxinfo.ports); i++) {
if (i == port_idx)
continue;
if (muxinfo.ports[i].ctrl_sig >= 0)
knowledge.known_inactive.at(muxinfo.ports[i].ctrl_sig)--;
if (muxinfo.ports[i].ctrl_sig >= 0) {
auto it = knowledge.known_inactive.find(muxinfo.ports[i].ctrl_sig);
if (it != knowledge.known_inactive.end())
if (--it->second == 0)
knowledge.known_inactive.erase(it);
}
}
}
@ -373,11 +384,11 @@ struct OptMuxtreeWorker
vector<int> bits = sig2bits(sig, false);
for (int i = 0; i < GetSize(bits); i++) {
if (bits[i] >= 0) {
if (knowledge.known_inactive.at(bits[i])) {
if (knowledge.known_inactive.count(bits[i]) > 0) {
sig[i] = State::S0;
did_something = true;
} else
if (knowledge.known_active.at(bits[i])) {
if (knowledge.known_active.count(bits[i]) > 0) {
sig[i] = State::S1;
did_something = true;
}
@ -435,7 +446,7 @@ struct OptMuxtreeWorker
portinfo_t &portinfo = muxinfo.ports[port_idx];
if (portinfo.const_deactivated)
continue;
if (knowledge.known_active.at(portinfo.ctrl_sig)) {
if (knowledge.known_active.count(portinfo.ctrl_sig) > 0) {
eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count);
return;
}
@ -449,7 +460,7 @@ struct OptMuxtreeWorker
if (portinfo.const_deactivated)
continue;
if (port_idx < GetSize(muxinfo.ports)-1)
if (knowledge.known_inactive.at(portinfo.ctrl_sig))
if (knowledge.known_inactive.count(portinfo.ctrl_sig) > 0)
continue;
eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count);
@ -462,10 +473,7 @@ struct OptMuxtreeWorker
{
log_assert(glob_abort_cnt > 0);
knowledge_t knowledge;
knowledge.known_inactive.resize(GetSize(bit2info));
knowledge.known_active.resize(GetSize(bit2info));
knowledge.visited_muxes.resize(GetSize(mux2info));
knowledge.visited_muxes[mux_idx] = true;
knowledge.visited_muxes.insert(mux_idx);
eval_mux(knowledge, mux_idx, true, root_enable_muxes.at(mux_idx), 3);
}
};

File diff suppressed because it is too large Load Diff

View File

@ -51,16 +51,27 @@ class libyosys_so_ext(Extension):
]
def custom_build(self, bext: build_ext):
make_flags_split = shlex.split(os.getenv("makeFlags", ""))
# abc linking takes a lot of memory, best get it out of the way first
bext.spawn(
[
"make",
f"-j{os.cpu_count() or 1}",
"yosys-abc",
*make_flags_split,
*self.args,
]
)
# build libyosys and share with abc out of the way
bext.spawn(
[
"make",
f"-j{os.cpu_count() or 1}",
self.name,
"yosys-abc",
"share",
*make_flags_split,
*self.args,
]
+ shlex.split(os.getenv("makeFlags", ""))
+ self.args
)
build_path = os.path.dirname(os.path.dirname(bext.get_ext_fullpath(self.name)))
pyosys_path = os.path.join(build_path, "pyosys")
@ -87,6 +98,7 @@ class libyosys_so_ext(Extension):
shutil.copytree("share", share_target)
class custom_build_ext(build_ext):
def build_extension(self, ext) -> None:
if not hasattr(ext, "custom_build"):
@ -102,8 +114,8 @@ setup(
long_description=open(os.path.join(__dir__, "README.md")).read(),
long_description_content_type="text/markdown",
install_requires=["wheel", "setuptools"],
license="MIT",
classifiers=[
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Intended Audience :: Developers",
"Operating System :: POSIX :: Linux",

View File

@ -0,0 +1,67 @@
#include <gtest/gtest.h>
#include "kernel/yosys_common.h"
#include <unordered_set>
YOSYS_NAMESPACE_BEGIN
static Hasher hash(int x)
{
Hasher h;
h.eat(x);
return h;
}
TEST(CommutativeTest, basic)
{
hashlib::commutative_hash comm1;
comm1.eat(hash(1));
comm1.eat(hash(2));
hashlib::commutative_hash comm2;
comm2.eat(hash(2));
comm2.eat(hash(1));
EXPECT_EQ(comm1.hash_into(Hasher()).yield(), comm2.hash_into(Hasher()).yield());
}
TEST(PoolHashTest, collisions)
{
uint64_t collisions = 0;
std::unordered_set<Hasher::hash_t> hashes;
for (int i = 0; i < 1000; ++i) {
for (int j = i + 1; j < 1000; ++j) {
pool<int> p1;
p1.insert(i);
p1.insert(j);
auto h = p1.hash_into(Hasher()).yield();
if (!hashes.insert(h).second) {
++collisions;
}
}
}
std::cout << "pool<int> collisions: " << collisions << std::endl;
EXPECT_LT(collisions, 10'000);
}
TEST(PoolHashTest, subset_collisions)
{
uint64_t collisions = 0;
std::unordered_set<Hasher::hash_t> hashes;
for (int i = 0; i < 1000 * 1000; ++i) {
pool<int> p1;
for (int b = 0; i >> b; ++b) {
if ((i >> b) & 1) {
p1.insert(b);
}
}
auto h = p1.hash_into(Hasher()).yield();
if (!hashes.insert(h).second) {
++collisions;
}
}
std::cout << "pool<int> subset collisions: " << collisions << std::endl;
EXPECT_LT(collisions, 100);
}
YOSYS_NAMESPACE_END

View File

@ -69,4 +69,14 @@ TEST(KernelStringfTest, dynamicWidthAndPrecision)
EXPECT_EQ(stringf("%*.*f", 8, 4, 1.0), " 1.0000");
}
TEST(KernelStringfTest, dynamicPrecisionInt)
{
EXPECT_EQ(stringf("%.*d", 4, 7), "0007");
}
TEST(KernelStringfTest, dynamicWidthAndPrecisionInt)
{
EXPECT_EQ(stringf("%*.*d", 8, 4, 7), " 0007");
}
YOSYS_NAMESPACE_END

View File

@ -0,0 +1,30 @@
read_verilog <<EOT
module half_clock (CLK, Q, magic);
input CLK;
output reg Q = 0;
input magic;
always @(posedge CLK)
Q <= ~Q;
endmodule
EOT
proc
design -save half_clock
sat -set-init-undef -enable_undef -verify -seq 5 -set-at 1 Q 0
sat -set-init-undef -enable_undef -falsify -seq 5 -set-at 1 Q 0 -set-at 2 Q 0
sat -set-init-undef -enable_undef -falsify -seq 5 -set-at 2 Q 0 -set-at 3 Q 0
abstract -state -initstates 1 */Q
sat -set-init-undef -enable_undef -verify -seq 5 -set-at 1 Q 0 -set-at 2 Q 0
sat -set-init-undef -enable_undef -falsify -seq 5 -set-at 2 Q 0 -set-at 3 Q 0
design -load half_clock
sat -set-init-undef -enable_undef -falsify -seq 5 -set-at 1 Q 0 -set-at 2 Q 0
sat -set-init-undef -enable_undef -falsify -seq 5 -set-at 2 Q 0 -set-at 3 Q 0
sat -set-init-undef -enable_undef -falsify -seq 5 -set-at 3 Q 0 -set-at 4 Q 0
abstract -state -initstates 2 */Q
sat -set-init-undef -enable_undef -verify -seq 5 -set-at 1 Q 0 -set-at 2 Q 0
sat -set-init-undef -enable_undef -verify -seq 5 -set-at 1 Q 0 -set-at 2 Q 0 -set-at 3 Q 0
sat -set-init-undef -enable_undef -falsify -seq 5 -set-at 3 Q 0 -set-at 4 Q 0

View File

@ -39,3 +39,18 @@ select -assert-count 1 w:d__1
select -assert-count 1 w:_e
select -assert-count 1 w:wire_
select -assert-count 1 w:$add$<<EOF:*$1_Y
# Ports are updated during rename
design -reset
read_verilog << EOT
module top(output \$e );
submod \a$ (\$e );
endmodule
module submod(output \a[0] );
assign \a[0] = 0;
endmodule
EOT
rename -unescape
check