mirror of
https://github.com/YosysHQ/yosys
synced 2025-05-08 00:05:48 +00:00
Merge branch 'YosysHQ:main' into pyosys_copy_abc
This commit is contained in:
commit
0abe8bfee8
94 changed files with 2967 additions and 270 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -7,6 +7,7 @@
|
|||
*.gcno
|
||||
*~
|
||||
__pycache__
|
||||
/.cache
|
||||
/.cproject
|
||||
/.project
|
||||
/.settings
|
||||
|
@ -15,6 +16,7 @@ __pycache__
|
|||
/qtcreator.config
|
||||
/qtcreator.creator
|
||||
/qtcreator.creator.user
|
||||
/compile_commands.json
|
||||
/coverage.info
|
||||
/coverage_html
|
||||
/Makefile.conf
|
||||
|
|
19
CHANGELOG
19
CHANGELOG
|
@ -2,9 +2,26 @@
|
|||
List of major changes and improvements between releases
|
||||
=======================================================
|
||||
|
||||
Yosys 0.50 .. Yosys 0.51-dev
|
||||
Yosys 0.51 .. Yosys 0.52-dev
|
||||
--------------------------
|
||||
|
||||
Yosys 0.50 .. Yosys 0.51
|
||||
--------------------------
|
||||
* New commands and options
|
||||
- Added "abstract" pass to allow reducing and never increasing
|
||||
the constraints on a circuit's behavior in a formal verification setting.
|
||||
|
||||
* Various
|
||||
- "splitcells" pass now splits "aldff" cells.
|
||||
- FunctionalIR documentation
|
||||
|
||||
* QuickLogic support
|
||||
- Added IOFF inference for qlf_k6n10f
|
||||
|
||||
* Intel support
|
||||
- Fixed RAM and DSP support.
|
||||
- Overall performance improvement for "synth_intel".
|
||||
|
||||
Yosys 0.49 .. Yosys 0.50
|
||||
--------------------------
|
||||
* Various
|
||||
|
|
73
CONTRIBUTING.md
Normal file
73
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,73 @@
|
|||
# Introduction
|
||||
|
||||
Thanks for thinking about contributing to the Yosys project. If this is your
|
||||
first time contributing to an open source project, please take a look at the
|
||||
following guide:
|
||||
https://opensource.guide/how-to-contribute/#orienting-yourself-to-a-new-project.
|
||||
|
||||
Information about the Yosys coding style is available on our Read the Docs:
|
||||
https://yosys.readthedocs.io/en/latest/yosys_internals/extending_yosys/contributing.html.
|
||||
|
||||
# Using the issue tracker
|
||||
|
||||
The [issue tracker](https://github.com/YosysHQ/yosys/issues) is used for
|
||||
tracking bugs or other problems with Yosys or its documentation. It is also the
|
||||
place to go for requesting new features.
|
||||
When [creating a new issue](https://github.com/YosysHQ/yosys/issues/new/choose),
|
||||
we have a few templates available. Please make use of these! It will make it
|
||||
much easier for someone to respond and help.
|
||||
|
||||
### Bug reports
|
||||
|
||||
Before you submit an issue, please have a search of the existing issues in case
|
||||
one already exists. Making sure that you have a minimal, complete and
|
||||
verifiable example (MVCE) is a great way to quickly check an existing issue
|
||||
against a new one. Stack overflow has a guide on [how to create an
|
||||
MVCE](https://stackoverflow.com/help/minimal-reproducible-example). The
|
||||
[`bugpoint`
|
||||
command](https://yosyshq.readthedocs.io/projects/yosys/en/latest/cmd/bugpoint.html)
|
||||
in Yosys can be helpful for this process.
|
||||
|
||||
|
||||
# Using pull requests
|
||||
|
||||
If you are working on something to add to Yosys, or fix something that isn't
|
||||
working quite right, make a [PR](https://github.com/YosysHQ/yosys/pulls)! An
|
||||
open PR, even as a draft, tells everyone that you're working on it and they
|
||||
don't have to. It can also be a useful way to solicit feedback on in-progress
|
||||
changes. See below to find the best way to [ask us
|
||||
questions](#asking-questions).
|
||||
|
||||
In general, all changes to the code are done as a PR, with [Continuous
|
||||
Integration (CI)](https://github.com/YosysHQ/yosys/actions) tools that
|
||||
automatically run the full suite of tests compiling and running Yosys. Please
|
||||
make use of this! If you're adding a feature: add a test! Not only does it
|
||||
verify that your feature is working as expected, but it can also be a handy way
|
||||
for people to see how the feature is used. If you're fixing a bug: add a test!
|
||||
If you can, do this first; it's okay if the test starts off failing - you
|
||||
already know there is a bug. CI also helps to make sure that your changes still
|
||||
work under a range of compilers, settings, and targets.
|
||||
|
||||
|
||||
### Labels
|
||||
|
||||
We use [labels](https://github.com/YosysHQ/yosys/labels) to help categorise
|
||||
issues and PRs. If a label seems relevant to your work, please do add it; this
|
||||
also includes the labels beggining with 'status-'. The 'merge-' labels are used
|
||||
by maintainers for tracking and communicating which PRs are ready and pending
|
||||
merge; please do not use these labels if you are not a maintainer.
|
||||
|
||||
|
||||
# Asking questions
|
||||
|
||||
If you have a question about how to use Yosys, please ask on our [discussions
|
||||
page](https://github.com/YosysHQ/yosys/discussions) or in our [community
|
||||
slack](https://join.slack.com/t/yosyshq/shared_invite/zt-1aopkns2q-EiQ97BeQDt_pwvE41sGSuA).
|
||||
The slack is also a great place to ask questions about developing or
|
||||
contributing to Yosys.
|
||||
|
||||
We have open dev 'jour fixe' (JF) meetings where developers from YosysHQ and the
|
||||
community come together to discuss open issues and PRs. This is also a good
|
||||
place to talk to us about how to implement larger PRs. Please join the
|
||||
community slack if you would like to join the next meeting, the link is
|
||||
available in the description of the #devel-discuss channel.
|
55
Makefile
55
Makefile
|
@ -123,8 +123,8 @@ ifneq ($(shell :; command -v brew),)
|
|||
BREW_PREFIX := $(shell brew --prefix)/opt
|
||||
$(info $$BREW_PREFIX is [${BREW_PREFIX}])
|
||||
ifeq ($(ENABLE_PYOSYS),1)
|
||||
CXXFLAGS += -I$(BREW_PREFIX)/boost/include/boost
|
||||
LINKFLAGS += -L$(BREW_PREFIX)/boost/lib
|
||||
CXXFLAGS += -I$(BREW_PREFIX)/boost/include
|
||||
LINKFLAGS += -L$(BREW_PREFIX)/boost/lib -L$(BREW_PREFIX)/boost-python3/lib
|
||||
endif
|
||||
CXXFLAGS += -I$(BREW_PREFIX)/readline/include
|
||||
LINKFLAGS += -L$(BREW_PREFIX)/readline/lib
|
||||
|
@ -153,7 +153,14 @@ ifeq ($(OS), Haiku)
|
|||
CXXFLAGS += -D_DEFAULT_SOURCE
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.50+1
|
||||
YOSYS_VER := 0.51+17
|
||||
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
|
||||
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1)
|
||||
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2)
|
||||
CXXFLAGS += -DYOSYS_VER=\\"$(YOSYS_VER)\\" \
|
||||
-DYOSYS_MAJOR=$(YOSYS_MAJOR) \
|
||||
-DYOSYS_MINOR=$(YOSYS_MINOR) \
|
||||
-DYOSYS_COMMIT=$(YOSYS_COMMIT)
|
||||
|
||||
# Note: We arrange for .gitcommit to contain the (short) commit hash in
|
||||
# tarballs generated with git-archive(1) using .gitattributes. The git repo
|
||||
|
@ -169,7 +176,7 @@ endif
|
|||
OBJS = kernel/version_$(GIT_REV).o
|
||||
|
||||
bumpversion:
|
||||
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline b5170e1.. | wc -l`/;" Makefile
|
||||
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline c4b5190.. | wc -l`/;" Makefile
|
||||
|
||||
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q)
|
||||
|
||||
|
@ -330,8 +337,13 @@ TARGETS += libyosys.so
|
|||
endif
|
||||
|
||||
ifeq ($(ENABLE_PYOSYS),1)
|
||||
# python-config --ldflags includes -l and -L, but LINKFLAGS is only -L
|
||||
LINKFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags))
|
||||
LIBS += $(shell $(PYTHON_CONFIG) --libs)
|
||||
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
|
||||
|
||||
# Detect name of boost_python library. Some distros use boost_python-py<version>, other boost_python<version>, some only use the major version number, some a concatenation of major and minor version numbers
|
||||
CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)")
|
||||
CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(LINKFLAGS) $(LIBS) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)")
|
||||
BOOST_PYTHON_LIB ?= $(shell \
|
||||
$(call CHECK_BOOST_PYTHON,boost_python-py$(subst .,,$(PYTHON_VERSION))) || \
|
||||
$(call CHECK_BOOST_PYTHON,boost_python-py$(PYTHON_MAJOR_VERSION)) || \
|
||||
|
@ -343,11 +355,7 @@ ifeq ($(BOOST_PYTHON_LIB),)
|
|||
$(error BOOST_PYTHON_LIB could not be detected. Please define manually)
|
||||
endif
|
||||
|
||||
LIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||
# python-config --ldflags includes LIBS for some reason
|
||||
LINKFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags))
|
||||
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
|
||||
|
||||
LIBS += $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||
PY_WRAPPER_FILE = kernel/python_wrappers
|
||||
OBJS += $(PY_WRAPPER_FILE).o
|
||||
PY_GEN_SCRIPT= py_wrap_generator
|
||||
|
@ -651,6 +659,9 @@ OBJS += libs/fst/fastlz.o
|
|||
OBJS += libs/fst/lz4.o
|
||||
endif
|
||||
|
||||
techlibs/%_pm.h: passes/pmgen/pmgen.py techlibs/%.pmg
|
||||
$(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p $(notdir $*) $(filter-out $<,$^)
|
||||
|
||||
ifneq ($(SMALL),1)
|
||||
|
||||
OBJS += libs/subcircuit/subcircuit.o
|
||||
|
@ -775,6 +786,15 @@ check-git-abc:
|
|||
elif [ -f "$(YOSYS_SRC)/abc/.gitcommit" ] && ! grep -q '\$$Format:%[hH]\$$' "$(YOSYS_SRC)/abc/.gitcommit"; then \
|
||||
echo "'abc' comes from a tarball. Continuing."; \
|
||||
exit 0; \
|
||||
elif git -C "$(YOSYS_SRC)" submodule status abc 2>/dev/null | grep -q '^+'; then \
|
||||
echo "'abc' submodule does not match expected commit."; \
|
||||
echo "Run 'git submodule update' to check out the correct version."; \
|
||||
echo "Note: If testing a different version of abc, call 'git commit abc' in the Yosys source directory to update the expected commit."; \
|
||||
exit 1; \
|
||||
elif git -C "$(YOSYS_SRC)" submodule status abc 2>/dev/null | grep -q '^U'; then \
|
||||
echo "'abc' submodule has merge conflicts."; \
|
||||
echo "Please resolve merge conflicts before continuing."; \
|
||||
exit 1; \
|
||||
elif [ -f "$(YOSYS_SRC)/abc/.gitcommit" ] && grep -q '\$$Format:%[hH]\$$' "$(YOSYS_SRC)/abc/.gitcommit"; then \
|
||||
echo "Error: 'abc' is not configured as a git submodule."; \
|
||||
echo "To resolve this:"; \
|
||||
|
@ -1001,6 +1021,18 @@ docs/source/cell/word_add.rst: $(TARGETS) $(EXTRA_TARGETS)
|
|||
docs/source/generated/cells.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS)
|
||||
$(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cells-json $@'
|
||||
|
||||
docs/source/generated/%.cc: backends/%.cc
|
||||
$(Q) mkdir -p $(@D)
|
||||
$(Q) cp $< $@
|
||||
|
||||
# diff returns exit code 1 if the files are different, but it's not an error
|
||||
docs/source/generated/functional/rosette.diff: backends/functional/smtlib.cc backends/functional/smtlib_rosette.cc
|
||||
$(Q) mkdir -p $(@D)
|
||||
$(Q) diff -U 20 $^ > $@ || exit 0
|
||||
|
||||
PHONY: docs/gen/functional_ir
|
||||
docs/gen/functional_ir: docs/source/generated/functional/smtlib.cc docs/source/generated/functional/rosette.diff
|
||||
|
||||
PHONY: docs/gen docs/usage docs/reqs
|
||||
docs/gen: $(TARGETS)
|
||||
$(Q) $(MAKE) -C docs gen
|
||||
|
@ -1036,7 +1068,7 @@ docs/reqs:
|
|||
$(Q) $(MAKE) -C docs reqs
|
||||
|
||||
.PHONY: docs/prep
|
||||
docs/prep: docs/source/cmd/abc.rst docs/source/generated/cells.json docs/gen docs/usage
|
||||
docs/prep: docs/source/cmd/abc.rst docs/source/generated/cells.json docs/gen docs/usage docs/gen/functional_ir
|
||||
|
||||
DOC_TARGET ?= html
|
||||
docs: docs/prep
|
||||
|
@ -1057,6 +1089,7 @@ clean:
|
|||
rm -rf vloghtb/Makefile vloghtb/refdat vloghtb/rtl vloghtb/scripts vloghtb/spec vloghtb/check_yosys vloghtb/vloghammer_tb.tar.bz2 vloghtb/temp vloghtb/log_test_*
|
||||
rm -f tests/svinterfaces/*.log_stdout tests/svinterfaces/*.log_stderr tests/svinterfaces/dut_result.txt tests/svinterfaces/reference_result.txt tests/svinterfaces/a.out tests/svinterfaces/*_syn.v tests/svinterfaces/*.diff
|
||||
rm -f tests/tools/cmp_tbdata
|
||||
rm -f $(addsuffix /run-test.mk,$(MK_TEST_DIRS))
|
||||
-$(MAKE) -C docs clean
|
||||
rm -rf docs/source/cmd docs/util/__pycache__
|
||||
|
||||
|
|
2
abc
2
abc
|
@ -1 +1 @@
|
|||
Subproject commit cac8f99eaa220a5e3db5caeb87cef0a975c953a2
|
||||
Subproject commit f2d68d590fa6f8fc32295a2edd79afc0d14a1414
|
|
@ -53,6 +53,8 @@ struct XAigerWriter
|
|||
dict<SigBit, float> arrival_times;
|
||||
|
||||
vector<pair<int, int>> aig_gates;
|
||||
vector<SigBit> bit2aig_stack;
|
||||
int next_loop_check = 1024;
|
||||
vector<int> aig_outputs;
|
||||
int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0;
|
||||
|
||||
|
@ -76,6 +78,24 @@ struct XAigerWriter
|
|||
return it->second;
|
||||
}
|
||||
|
||||
if (GetSize(bit2aig_stack)== next_loop_check) {
|
||||
for (int i = 0; i < next_loop_check; ++i)
|
||||
{
|
||||
SigBit report_bit = bit2aig_stack[i];
|
||||
if (report_bit != bit)
|
||||
continue;
|
||||
for (int j = i; j < next_loop_check; ++j) {
|
||||
report_bit = bit2aig_stack[j];
|
||||
if (report_bit.is_wire() && report_bit.wire->name.isPublic())
|
||||
break;
|
||||
}
|
||||
log_error("Found combinatorial logic loop while processing signal %s.\n", log_signal(report_bit));
|
||||
}
|
||||
next_loop_check *= 2;
|
||||
}
|
||||
|
||||
bit2aig_stack.push_back(bit);
|
||||
|
||||
// NB: Cannot use iterator returned from aig_map.insert()
|
||||
// since this function is called recursively
|
||||
|
||||
|
@ -93,6 +113,8 @@ struct XAigerWriter
|
|||
a = bit2aig(alias_map.at(bit));
|
||||
}
|
||||
|
||||
bit2aig_stack.pop_back();
|
||||
|
||||
if (bit == State::Sx || bit == State::Sz) {
|
||||
log_debug("Design contains 'x' or 'z' bits. Treating as 1'b0.\n");
|
||||
a = aig_map.at(State::S0);
|
||||
|
|
|
@ -616,7 +616,7 @@ std::string escape_c_string(const std::string &input)
|
|||
output.push_back('\\');
|
||||
output.push_back(c);
|
||||
} else {
|
||||
char l = c & 0x3, m = (c >> 3) & 0x3, h = (c >> 6) & 0x3;
|
||||
char l = c & 0x7, m = (c >> 3) & 0x7, h = (c >> 6) & 0x3;
|
||||
output.append("\\");
|
||||
output.push_back('0' + h);
|
||||
output.push_back('0' + m);
|
||||
|
|
|
@ -210,14 +210,8 @@ struct SmtrModule {
|
|||
state_struct.insert(state->name, state->sort);
|
||||
}
|
||||
|
||||
void write(std::ostream &out)
|
||||
{
|
||||
SExprWriter w(out);
|
||||
|
||||
input_struct.write_definition(w);
|
||||
output_struct.write_definition(w);
|
||||
state_struct.write_definition(w);
|
||||
|
||||
void write_eval(SExprWriter &w)
|
||||
{
|
||||
w.push();
|
||||
w.open(list("define", list(name, "inputs", "state")));
|
||||
auto inlined = [&](Functional::Node n) {
|
||||
|
@ -240,7 +234,10 @@ struct SmtrModule {
|
|||
output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); });
|
||||
state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); });
|
||||
w.pop();
|
||||
}
|
||||
|
||||
void write_initial(SExprWriter &w)
|
||||
{
|
||||
w.push();
|
||||
auto initial = name + "_initial";
|
||||
w.open(list("define", initial));
|
||||
|
@ -259,6 +256,18 @@ struct SmtrModule {
|
|||
}
|
||||
w.pop();
|
||||
}
|
||||
|
||||
void write(std::ostream &out)
|
||||
{
|
||||
SExprWriter w(out);
|
||||
|
||||
input_struct.write_definition(w);
|
||||
output_struct.write_definition(w);
|
||||
state_struct.write_definition(w);
|
||||
|
||||
write_eval(w);
|
||||
write_initial(w);
|
||||
}
|
||||
};
|
||||
|
||||
struct FunctionalSmtrBackend : public Backend {
|
||||
|
@ -267,7 +276,7 @@ struct FunctionalSmtrBackend : public Backend {
|
|||
void help() override {
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" write_functional_rosette [options] [selection] [filename]\n");
|
||||
log(" write_functional_rosette [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Functional Rosette Backend.\n");
|
||||
log("\n");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Coarse arithmetics
|
||||
------------------
|
||||
|
||||
.. todo:: Add information about `$alu`, `$fa`, and `$lcu` cells.
|
||||
.. todo:: Add information about `$alu`, `$fa`, `$macc_v2`, and `$lcu` cells.
|
||||
|
||||
The `$macc` cell type represents a generalized multiply and accumulate
|
||||
operation. The cell is purely combinational. It outputs the result of summing up
|
||||
|
|
|
@ -67,7 +67,7 @@ show -color maroon3 @new_cells -notitle -format dot -prefix rdata_memrdv2 o:rdat
|
|||
# ========================================================
|
||||
|
||||
alumacc
|
||||
select -set new_cells t:$alu t:$macc
|
||||
select -set new_cells t:$alu t:$macc_v2
|
||||
show -color maroon3 @new_cells -notitle -format dot -prefix rdata_alumacc o:rdata %ci*
|
||||
|
||||
# ========================================================
|
||||
|
|
44
docs/source/code_examples/functional/dummy.cc
Normal file
44
docs/source/code_examples/functional/dummy.cc
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "kernel/functional.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct FunctionalDummyBackend : public Backend {
|
||||
FunctionalDummyBackend() : Backend("functional_dummy", "dump generated Functional IR") {}
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
// backend pass boiler plate
|
||||
log_header(design, "Executing dummy functional backend.\n");
|
||||
|
||||
size_t argidx = 1;
|
||||
extra_args(f, filename, args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
log("Processing module `%s`.\n", module->name.c_str());
|
||||
|
||||
// convert module to FunctionalIR
|
||||
auto ir = Functional::IR::from_module(module);
|
||||
*f << "module " << module->name.c_str() << "\n";
|
||||
|
||||
// write node functions
|
||||
for (auto node : ir)
|
||||
*f << " assign " << id2cstr(node.name())
|
||||
<< " = " << node.to_string() << "\n";
|
||||
*f << "\n";
|
||||
|
||||
// write outputs and next state
|
||||
for (auto output : ir.outputs())
|
||||
*f << " " << id2cstr(output->kind)
|
||||
<< " " << id2cstr(output->name)
|
||||
<< " = " << id2cstr(output->value().name()) << "\n";
|
||||
for (auto state : ir.states())
|
||||
*f << " " << id2cstr(state->kind)
|
||||
<< " " << id2cstr(state->name)
|
||||
<< " = " << id2cstr(state->next_value().name()) << "\n";
|
||||
}
|
||||
}
|
||||
} FunctionalDummyBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -6,7 +6,7 @@ import os
|
|||
project = 'YosysHQ Yosys'
|
||||
author = 'YosysHQ GmbH'
|
||||
copyright ='2025 YosysHQ GmbH'
|
||||
yosys_ver = "0.50"
|
||||
yosys_ver = "0.51"
|
||||
|
||||
# select HTML theme
|
||||
html_theme = 'furo-ys'
|
||||
|
@ -93,6 +93,9 @@ bibtex_bibfiles = ['literature.bib']
|
|||
latex_elements = {
|
||||
'releasename': 'Version',
|
||||
'preamble': r'''
|
||||
\pdfinfoomitdate 1
|
||||
\pdfsuppressptexinfo 1
|
||||
\pdftrailerid{}
|
||||
\usepackage{lmodern}
|
||||
\usepackage{comment}
|
||||
|
||||
|
|
|
@ -523,7 +523,7 @@ That brings us to the fourth and final part for the iCE40 synthesis flow:
|
|||
:name: synth_coarse4
|
||||
|
||||
Where before each type of arithmetic operation had its own cell, e.g. `$add`, we
|
||||
now want to extract these into `$alu` and `$macc` cells which can help identify
|
||||
now want to extract these into `$alu` and `$macc_v2` cells which can help identify
|
||||
opportunities for reusing logic. We do this by running `alumacc`, which we can
|
||||
see produce the following changes in our example design:
|
||||
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
Contributing to Yosys
|
||||
=====================
|
||||
|
||||
.. note::
|
||||
|
||||
For information on making a pull request on github, refer to our
|
||||
|CONTRIBUTING|_ file.
|
||||
|
||||
.. |CONTRIBUTING| replace:: :file:`CONTRIBUTING.md`
|
||||
.. _CONTRIBUTING: https://github.com/YosysHQ/yosys/CONTRIBUTING.md
|
||||
|
||||
Coding Style
|
||||
------------
|
||||
|
||||
|
|
|
@ -1,94 +1,524 @@
|
|||
Writing a new backend using FunctionalIR
|
||||
===========================================
|
||||
========================================
|
||||
|
||||
To simplify the writing of backends for functional languages or similar targets, Yosys provides an alternative intermediate representation called FunctionalIR which maps more directly on those targets.
|
||||
What is FunctionalIR
|
||||
--------------------
|
||||
|
||||
FunctionalIR represents the design as a function ``(inputs, current_state) -> (outputs, next_state)``.
|
||||
This function is broken down into a series of assignments to variables.
|
||||
Each assignment is a simple operation, such as an addition.
|
||||
Complex operations are broken up into multiple steps.
|
||||
For example, an RTLIL addition will be translated into a sign/zero extension of the inputs, followed by an addition.
|
||||
To simplify the writing of backends for functional languages or similar targets,
|
||||
Yosys provides an alternative intermediate representation called FunctionalIR
|
||||
which maps more directly on those targets.
|
||||
|
||||
Like SSA form, each variable is assigned to exactly once.
|
||||
We can thus treat variables and assignments as equivalent and, since this is a graph-like representation, those variables are also called "nodes".
|
||||
Unlike RTLIL's cells and wires representation, this representation is strictly ordered (topologically sorted) with definitions preceding their use.
|
||||
FunctionalIR represents the design as a function ``(inputs, current_state) ->
|
||||
(outputs, next_state)``. This function is broken down into a series of
|
||||
assignments to variables. Each assignment is a simple operation, such as an
|
||||
addition. Complex operations are broken up into multiple steps. For example, an
|
||||
RTLIL addition will be translated into a sign/zero extension of the inputs,
|
||||
followed by an addition.
|
||||
|
||||
Every node has a "sort" (the FunctionalIR term for what might otherwise be called a "type"). The sorts available are
|
||||
Like SSA form, each variable is assigned to exactly once. We can thus treat
|
||||
variables and assignments as equivalent and, since this is a graph-like
|
||||
representation, those variables are also called "nodes". Unlike RTLIL's cells
|
||||
and wires representation, this representation is strictly ordered (topologically
|
||||
sorted) with definitions preceding their use.
|
||||
|
||||
Every node has a "sort" (the FunctionalIR term for what might otherwise be
|
||||
called a "type"). The sorts available are
|
||||
|
||||
- ``bit[n]`` for an ``n``-bit bitvector, and
|
||||
- ``memory[n,m]`` for an immutable array of ``2**n`` values of sort ``bit[m]``.
|
||||
|
||||
In terms of actual code, Yosys provides a class ``Functional::IR`` that represents a design in FunctionalIR.
|
||||
``Functional::IR::from_module`` generates an instance from an RTLIL module.
|
||||
The entire design is stored as a whole in an internal data structure.
|
||||
To access the design, the ``Functional::Node`` class provides a reference to a particular node in the design.
|
||||
The ``Functional::IR`` class supports the syntax ``for(auto node : ir)`` to iterate over every node.
|
||||
In terms of actual code, Yosys provides a class ``Functional::IR`` that
|
||||
represents a design in FunctionalIR. ``Functional::IR::from_module`` generates
|
||||
an instance from an RTLIL module. The entire design is stored as a whole in an
|
||||
internal data structure. To access the design, the ``Functional::Node`` class
|
||||
provides a reference to a particular node in the design. The ``Functional::IR``
|
||||
class supports the syntax ``for(auto node : ir)`` to iterate over every node.
|
||||
|
||||
``Functional::IR`` also keeps track of inputs, outputs and states.
|
||||
By a "state" we mean a pair of a "current state" input and a "next state" output.
|
||||
One such pair is created for every register and for every memory.
|
||||
Every input, output and state has a name (equal to their name in RTLIL), a sort and a kind.
|
||||
The kind field usually remains as the default value ``$input``, ``$output`` or ``$state``, however some RTLIL cells such as ``$assert`` or ``$anyseq`` generate auxiliary inputs/outputs/states that are given a different kind to distinguish them from ordinary RTLIL inputs/outputs/states.
|
||||
``Functional::IR`` also keeps track of inputs, outputs and states. By a "state"
|
||||
we mean a pair of a "current state" input and a "next state" output. One such
|
||||
pair is created for every register and for every memory. Every input, output and
|
||||
state has a name (equal to their name in RTLIL), a sort and a kind. The kind
|
||||
field usually remains as the default value ``$input``, ``$output`` or
|
||||
``$state``, however some RTLIL cells such as ``$assert`` or ``$anyseq`` generate
|
||||
auxiliary inputs/outputs/states that are given a different kind to distinguish
|
||||
them from ordinary RTLIL inputs/outputs/states.
|
||||
|
||||
- To access an individual input/output/state, use ``ir.input(name, kind)``, ``ir.output(name, kind)`` or ``ir.state(name, kind)``. ``kind`` defaults to the default kind.
|
||||
- To iterate over all inputs/outputs/states of a certain kind, methods ``ir.inputs``, ``ir.outputs``, ``ir.states`` are provided. Their argument defaults to the default kinds mentioned.
|
||||
- To iterate over inputs/outputs/states of any kind, use ``ir.all_inputs``, ``ir.all_outputs`` and ``ir.all_states``.
|
||||
- Outputs have a node that indicate the value of the output, this can be retrieved via ``output.value()``.
|
||||
- States have a node that indicate the next value of the state, this can be retrieved via ``state.next_value()``.
|
||||
They also have an initial value that is accessed as either ``state.initial_value_signal()`` or ``state.initial_value_memory()``, depending on their sort.
|
||||
- To access an individual input/output/state, use ``ir.input(name, kind)``,
|
||||
``ir.output(name, kind)`` or ``ir.state(name, kind)``. ``kind`` defaults to
|
||||
the default kind.
|
||||
- To iterate over all inputs/outputs/states of a certain kind, methods
|
||||
``ir.inputs``, ``ir.outputs``, ``ir.states`` are provided. Their argument
|
||||
defaults to the default kinds mentioned.
|
||||
- To iterate over inputs/outputs/states of any kind, use ``ir.all_inputs``,
|
||||
``ir.all_outputs`` and ``ir.all_states``.
|
||||
- Outputs have a node that indicate the value of the output, this can be
|
||||
retrieved via ``output.value()``.
|
||||
- States have a node that indicate the next value of the state, this can be
|
||||
retrieved via ``state.next_value()``. They also have an initial value that is
|
||||
accessed as either ``state.initial_value_signal()`` or
|
||||
``state.initial_value_memory()``, depending on their sort.
|
||||
|
||||
Each node has a "function", which defines its operation (for a complete list of functions and a specification of their operation, see ``functional.h``).
|
||||
Functions are represented as an enum ``Functional::Fn`` and the function field can be accessed as ``node.fn()``.
|
||||
Since the most common operation is a switch over the function that also accesses the arguments, the ``Node`` class provides a method ``visit`` that implements the visitor pattern.
|
||||
For example, for an addition node ``node`` with arguments ``n1`` and ``n2``, ``node.visit(visitor)`` would call ``visitor.add(node, n1, n2)``.
|
||||
Thus typically one would implement a class with a method for every function.
|
||||
Visitors should inherit from either ``Functional::AbstractVisitor<ReturnType>`` or ``Functional::DefaultVisitor<ReturnType>``.
|
||||
The former will produce a compiler error if a case is unhandled, the latter will call ``default_handler(node)`` instead.
|
||||
Visitor methods should be marked as ``override`` to provide compiler errors if the arguments are wrong.
|
||||
Each node has a "function", which defines its operation (for a complete list of
|
||||
functions and a specification of their operation, see ``functional.h``).
|
||||
Functions are represented as an enum ``Functional::Fn`` and the function field
|
||||
can be accessed as ``node.fn()``. Since the most common operation is a switch
|
||||
over the function that also accesses the arguments, the ``Node`` class provides
|
||||
a method ``visit`` that implements the visitor pattern. For example, for an
|
||||
addition node ``node`` with arguments ``n1`` and ``n2``, ``node.visit(visitor)``
|
||||
would call ``visitor.add(node, n1, n2)``. Thus typically one would implement a
|
||||
class with a method for every function. Visitors should inherit from either
|
||||
``Functional::AbstractVisitor<ReturnType>`` or
|
||||
``Functional::DefaultVisitor<ReturnType>``. The former will produce a compiler
|
||||
error if a case is unhandled, the latter will call ``default_handler(node)``
|
||||
instead. Visitor methods should be marked as ``override`` to provide compiler
|
||||
errors if the arguments are wrong.
|
||||
|
||||
Utility classes
|
||||
-----------------
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
``functional.h`` also provides utility classes that are independent of the main FunctionalIR representation but are likely to be useful for backends.
|
||||
``functional.h`` also provides utility classes that are independent of the main
|
||||
FunctionalIR representation but are likely to be useful for backends.
|
||||
|
||||
``Functional::Writer`` provides a simple formatting class that wraps a ``std::ostream`` and provides the following methods:
|
||||
``Functional::Writer`` provides a simple formatting class that wraps a
|
||||
``std::ostream`` and provides the following methods:
|
||||
|
||||
- ``writer << value`` wraps ``os << value``.
|
||||
- ``writer.print(fmt, value0, value1, value2, ...)`` replaces ``{0}``, ``{1}``, ``{2}``, etc in the string ``fmt`` with ``value0``, ``value1``, ``value2``, resp.
|
||||
Each value is formatted using ``os << value``.
|
||||
It is also possible to write ``{}`` to refer to one past the last index, i.e. ``{1} {} {} {7} {}`` is equivalent to ``{1} {2} {3} {7} {8}``.
|
||||
- ``writer.print_with(fn, fmt, value0, value1, value2, ...)`` functions much the same as ``print`` but it uses ``os << fn(value)`` to print each value and falls back to ``os << value`` if ``fn(value)`` is not legal.
|
||||
- ``writer.print(fmt, value0, value1, value2, ...)`` replaces ``{0}``, ``{1}``,
|
||||
``{2}``, etc in the string ``fmt`` with ``value0``, ``value1``, ``value2``,
|
||||
resp. Each value is formatted using ``os << value``. It is also possible to
|
||||
write ``{}`` to refer to one past the last index, i.e. ``{1} {} {} {7} {}`` is
|
||||
equivalent to ``{1} {2} {3} {7} {8}``.
|
||||
- ``writer.print_with(fn, fmt, value0, value1, value2, ...)`` functions much the
|
||||
same as ``print`` but it uses ``os << fn(value)`` to print each value and
|
||||
falls back to ``os << value`` if ``fn(value)`` is not legal.
|
||||
|
||||
``Functional::Scope`` keeps track of variable names in a target language.
|
||||
It is used to translate between different sets of legal characters and to avoid accidentally re-defining identifiers.
|
||||
Users should derive a class from ``Scope`` and supply the following:
|
||||
``Functional::Scope`` keeps track of variable names in a target language. It is
|
||||
used to translate between different sets of legal characters and to avoid
|
||||
accidentally re-defining identifiers. Users should derive a class from ``Scope``
|
||||
and supply the following:
|
||||
|
||||
- ``Scope<Id>`` takes a template argument that specifies a type that's used to uniquely distinguish variables.
|
||||
Typically this would be ``int`` (if variables are used for ``Functional::IR`` nodes) or ``IdString``.
|
||||
- The derived class should provide a constructor that calls ``reserve`` for every reserved word in the target language.
|
||||
- A method ``bool is_legal_character(char c, int index)`` has to be provided that returns ``true`` iff ``c`` is legal in an identifier at position ``index``.
|
||||
- ``Scope<Id>`` takes a template argument that specifies a type that's used to
|
||||
uniquely distinguish variables. Typically this would be ``int`` (if variables
|
||||
are used for ``Functional::IR`` nodes) or ``IdString``.
|
||||
- The derived class should provide a constructor that calls ``reserve`` for
|
||||
every reserved word in the target language.
|
||||
- A method ``bool is_character_legal(char c, int index)`` has to be provided
|
||||
that returns ``true`` iff ``c`` is legal in an identifier at position
|
||||
``index``.
|
||||
|
||||
Given an instance ``scope`` of the derived class, the following methods are then available:
|
||||
Given an instance ``scope`` of the derived class, the following methods are then
|
||||
available:
|
||||
|
||||
- ``scope.reserve(std::string name)`` marks the given name as being in-use
|
||||
- ``scope.unique_name(IdString suggestion)`` generates a previously unused name and attempts to make it similar to ``suggestion``.
|
||||
- ``scope(Id id, IdString suggestion)`` functions similar to ``unique_name``, except that multiple calls with the same ``id`` are guaranteed to retrieve the same name (independent of ``suggestion``).
|
||||
- ``scope.unique_name(IdString suggestion)`` generates a previously unused name
|
||||
and attempts to make it similar to ``suggestion``.
|
||||
- ``scope(Id id, IdString suggestion)`` functions similar to ``unique_name``,
|
||||
except that multiple calls with the same ``id`` are guaranteed to retrieve the
|
||||
same name (independent of ``suggestion``).
|
||||
|
||||
``sexpr.h`` provides classes that represent and pretty-print s-expressions.
|
||||
S-expressions can be constructed with ``SExpr::list``, for example ``SExpr expr = SExpr::list("add", "x", SExpr::list("mul", "y", "z"))`` represents ``(add x (mul y z))``
|
||||
(by adding ``using SExprUtil::list`` to the top of the file, ``list`` can be used as shorthand for ``SExpr::list``).
|
||||
For prettyprinting, ``SExprWriter`` wraps an ``std::ostream`` and provides the following methods:
|
||||
S-expressions can be constructed with ``SExpr::list``, for example ``SExpr expr
|
||||
= SExpr::list("add", "x", SExpr::list("mul", "y", "z"))`` represents ``(add x
|
||||
(mul y z))`` (by adding ``using SExprUtil::list`` to the top of the file,
|
||||
``list`` can be used as shorthand for ``SExpr::list``). For prettyprinting,
|
||||
``SExprWriter`` wraps an ``std::ostream`` and provides the following methods:
|
||||
|
||||
- ``writer << sexpr`` writes the provided expression to the output, breaking long lines and adding appropriate indentation.
|
||||
- ``writer.open(sexpr)`` is similar to ``writer << sexpr`` but will omit the last closing parenthesis.
|
||||
Further arguments can then be added separately with ``<<`` or ``open``.
|
||||
This allows for printing large s-expressions without needing to construct the whole expression in memory first.
|
||||
- ``writer.open(sexpr, false)`` is similar to ``writer.open(sexpr)`` but further arguments will not be indented.
|
||||
This is used to avoid unlimited indentation on structures with unlimited nesting.
|
||||
- ``writer << sexpr`` writes the provided expression to the output, breaking
|
||||
long lines and adding appropriate indentation.
|
||||
- ``writer.open(sexpr)`` is similar to ``writer << sexpr`` but will omit the
|
||||
last closing parenthesis. Further arguments can then be added separately with
|
||||
``<<`` or ``open``. This allows for printing large s-expressions without
|
||||
needing to construct the whole expression in memory first.
|
||||
- ``writer.open(sexpr, false)`` is similar to ``writer.open(sexpr)`` but further
|
||||
arguments will not be indented. This is used to avoid unlimited indentation on
|
||||
structures with unlimited nesting.
|
||||
- ``writer.close(n = 1)`` closes the last ``n`` open s-expressions.
|
||||
- ``writer.push()`` and ``writer.pop()`` are used to automatically close s-expressions.
|
||||
``writer.pop()`` closes all s-expressions opened since the last call to ``writer.push()``.
|
||||
- ``writer.push()`` and ``writer.pop()`` are used to automatically close
|
||||
s-expressions. ``writer.pop()`` closes all s-expressions opened since the last
|
||||
call to ``writer.push()``.
|
||||
- ``writer.comment(string)`` writes a comment on a separate-line.
|
||||
``writer.comment(string, true)`` appends a comment to the last printed s-expression.
|
||||
- ``writer.flush()`` flushes any buffering and should be called before any direct access to the underlying ``std::ostream``. It does not close unclosed parentheses.
|
||||
``writer.comment(string, true)`` appends a comment to the last printed
|
||||
s-expression.
|
||||
- ``writer.flush()`` flushes any buffering and should be called before any
|
||||
direct access to the underlying ``std::ostream``. It does not close unclosed
|
||||
parentheses.
|
||||
- The destructor calls ``flush`` but also closes all unclosed parentheses.
|
||||
|
||||
.. _minimal backend:
|
||||
|
||||
Example: A minimal functional backend
|
||||
-------------------------------------
|
||||
|
||||
At its most basic, there are three steps we need to accomplish for a minimal
|
||||
functional backend. First, we need to convert our design into FunctionalIR.
|
||||
This is most easily done by calling the ``Functional::IR::from_module()`` static
|
||||
method with our top-level module, or iterating over and converting each of the
|
||||
modules in our design. Second, we need to handle each of the
|
||||
``Functional::Node``\ s in our design. Iterating over the ``Functional::IR``
|
||||
includes reading the module inputs and current state, but not writing the
|
||||
results. So our final step is to handle the outputs and next state.
|
||||
|
||||
In order to add an output command to Yosys, we implement the ``Yosys::Backend``
|
||||
class and provide an instance of it:
|
||||
|
||||
.. literalinclude:: /code_examples/functional/dummy.cc
|
||||
:language: c++
|
||||
:caption: Example source code for a minimal functional backend, ``dummy.cc``
|
||||
|
||||
Because we are using the ``Backend`` class, our ``"functional_dummy"`` is
|
||||
registered as the ``write_functional_dummy`` command. The ``execute`` method is
|
||||
the part that runs when the user calls the command, handling any options,
|
||||
preparing the output file for writing, and iterating over selected modules in
|
||||
the design. Since we don't have any options here, we set ``argidx = 1`` and
|
||||
call the ``extra_args()`` method. This method will read the command arguments,
|
||||
raising an error if there are any unexpected ones. It will also assign the
|
||||
pointer ``f`` to the output file, or stdout if none is given.
|
||||
|
||||
.. note::
|
||||
|
||||
For more on adding new commands to Yosys and how they work, refer to
|
||||
:doc:`/yosys_internals/extending_yosys/extensions`.
|
||||
|
||||
For this minimal example all we are doing is printing out each node. The
|
||||
``node.name()`` method returns an ``RTLIL::IdString``, which we convert for
|
||||
printing with ``id2cstr()``. Then, to print the function of the node, we use
|
||||
``node.to_string()`` which gives us a string of the form ``function(args)``. The
|
||||
``function`` part is the result of ``Functional::IR::fn_to_string(node.fn())``;
|
||||
while ``args`` is the zero or more arguments passed to the function, most
|
||||
commonly the name of another node. Behind the scenes, the ``node.to_string()``
|
||||
method actually wraps ``node.visit(visitor)`` with a private visitor whose
|
||||
return type is ``std::string``.
|
||||
|
||||
Finally we iterate over the module's outputs and states, using
|
||||
``Functional::IROutput::value()`` and ``Functional::IRState::next_value()``
|
||||
respectively in order to get the results of the transfer function.
|
||||
|
||||
Example: Adapting SMT-LIB backend for Rosette
|
||||
---------------------------------------------
|
||||
|
||||
This section will introduce the SMT-LIB functional backend
|
||||
(`write_functional_smt2`) and what changes are needed to work with another
|
||||
s-expression target, `Rosette`_ (`write_functional_rosette`).
|
||||
|
||||
.. _Rosette: http://emina.github.io/rosette/
|
||||
|
||||
Overview
|
||||
~~~~~~~~
|
||||
|
||||
Rosette is a solver-aided programming language that extends `Racket`_ with
|
||||
language constructs for program synthesis, verification, and more. To verify
|
||||
or synthesize code, Rosette compiles it to logical constraints solved with
|
||||
off-the-shelf `SMT`_ solvers.
|
||||
|
||||
-- https://emina.github.io/rosette/
|
||||
|
||||
.. _Racket: http://racket-lang.org/
|
||||
.. _SMT: http://smtlib.cs.uiowa.edu/
|
||||
|
||||
Rosette, being backed by SMT solvers and written with s-expressions, uses code
|
||||
very similar to the `write_functional_smt2` output. As a result, the SMT-LIB
|
||||
functional backend can be used as a starting point for implementing a Rosette
|
||||
backend.
|
||||
|
||||
Full code listings for the initial SMT-LIB backend and the converted Rosette
|
||||
backend are included in the Yosys source repository under
|
||||
:file:`backends/functional` as ``smtlib.cc`` and ``smtlib_rosette.cc``
|
||||
respectively. Note that the Rosette language is an extension of the Racket
|
||||
language; this guide tends to refer to Racket when talking about the underlying
|
||||
semantics/syntax of the language.
|
||||
|
||||
The major changes from the SMT-LIB backend are as follows:
|
||||
|
||||
- all of the ``Smt`` prefixes in names are replaced with ``Smtr`` to mean
|
||||
``smtlib_rosette``;
|
||||
- syntax is adjusted for Racket;
|
||||
- data structures for input/output/state are changed from using
|
||||
``declare-datatype`` with statically typed fields, to using ``struct`` with no
|
||||
static typing;
|
||||
- the transfer function also loses its static typing;
|
||||
- sign/zero extension in Rosette use the output width instead of the number of
|
||||
extra bits, gaining static typing;
|
||||
- the single scope is traded for a global scope with local scope for each
|
||||
struct;
|
||||
- initial state is provided as a constant value instead of a set of assertions;
|
||||
- and the ``-provides`` option is introduced to more easily use generated code
|
||||
within Rosette based applications.
|
||||
|
||||
Scope
|
||||
~~~~~
|
||||
|
||||
Our first addition to the `minimal backend`_ above is that for both SMT-LIB and
|
||||
Rosette backends, we are now targetting real languages which bring with them
|
||||
their own sets of constraints with what we can use as identifiers. This is
|
||||
where the ``Functional::Scope`` class described above comes in; by using this
|
||||
class we can safely rename our identifiers in the generated output without
|
||||
worrying about collisions or illegal names/characters.
|
||||
|
||||
In the SMT-LIB version, the ``SmtScope`` class implements ``Scope<int>``;
|
||||
provides a constructor that iterates over a list of reserved keywords, calling
|
||||
``reserve`` on each; and defines the ``is_character_legal`` method to reject any
|
||||
characters which are not allowed in SMT-LIB variable names to then be replaced
|
||||
with underscores in the output. To use this scope we create an instance of it,
|
||||
and call the ``Scope::unique_name()`` method to generate a unique and legal name
|
||||
for each of our identifiers.
|
||||
|
||||
In the Rosette version we update the list of legal ascii characters in the
|
||||
``is_character_legal`` method to only those allowed in Racket variable names.
|
||||
|
||||
.. literalinclude:: /generated/functional/rosette.diff
|
||||
:language: diff
|
||||
:caption: diff of ``Scope`` class
|
||||
:start-at: -struct SmtScope : public Functional::Scope<int> {
|
||||
:end-at: };
|
||||
|
||||
For the reserved keywords we trade the SMT-LIB specification for Racket to
|
||||
prevent parts of our design from accidentally being treated as Racket code. We
|
||||
also no longer need to reserve ``pair``, ``first``, and ``second``. In
|
||||
`write_functional_smt2` these are used for combining the ``(inputs,
|
||||
current_state)`` and ``(outputs, next_state)`` into a single variable. Racket
|
||||
provides this functionality natively with ``cons``, which we will see later.
|
||||
|
||||
.. inlined diff for skipping the actual lists
|
||||
.. code-block:: diff
|
||||
:caption: diff of ``reserved_keywords`` list
|
||||
|
||||
const char *reserved_keywords[] = {
|
||||
- // reserved keywords from the smtlib spec
|
||||
- ...
|
||||
+ // reserved keywords from the racket spec
|
||||
+ ...
|
||||
|
||||
// reserved for our own purposes
|
||||
- "pair", "Pair", "first", "second",
|
||||
- "inputs", "state",
|
||||
+ "inputs", "state", "name",
|
||||
nullptr
|
||||
};
|
||||
|
||||
.. note:: We skip over the actual list of reserved keywords from both the smtlib
|
||||
and racket specifications to save on space in this document.
|
||||
|
||||
Sort
|
||||
~~~~
|
||||
|
||||
Next up in `write_functional_smt2` we see the ``Sort`` class. This is a wrapper
|
||||
for the ``Functional::Sort`` class, providing the additional functionality of
|
||||
mapping variable declarations to s-expressions with the ``to_sexpr()`` method.
|
||||
The main change from ``SmtSort`` to ``SmtrSort`` is a syntactical one with
|
||||
signals represented as ``bitvector``\ s, and memories as ``list``\ s of signals.
|
||||
|
||||
.. literalinclude:: /generated/functional/rosette.diff
|
||||
:language: diff
|
||||
:caption: diff of ``Sort`` wrapper
|
||||
:start-at: SExpr to_sexpr() const {
|
||||
:end-before: };
|
||||
|
||||
Struct
|
||||
~~~~~~
|
||||
|
||||
As we saw in the `minimal backend`_ above, the ``Functional::IR`` class tracks
|
||||
the set of inputs, the set of outputs, and the set of "state" variables. The
|
||||
SMT-LIB backend maps each of these sets into its own ``SmtStruct``, with each
|
||||
variable getting a corresponding field in the struct and a specified `Sort`_.
|
||||
`write_functional_smt2` then defines each of these structs as a new
|
||||
``datatype``, with each element being strongly-typed.
|
||||
|
||||
In Rosette, rather than defining new datatypes for our structs, we use the
|
||||
native ``struct``. We also only declare each field by name because Racket
|
||||
provides less static typing. For ease of use, we provide the expected type for
|
||||
each field as comments.
|
||||
|
||||
.. literalinclude:: /generated/functional/rosette.diff
|
||||
:language: diff
|
||||
:caption: diff of ``write_definition`` method
|
||||
:start-at: void write_definition
|
||||
:end-before: template<typename Fn> void write_value
|
||||
|
||||
Each field is added to the ``SmtStruct`` with the ``insert`` method, which also
|
||||
reserves a unique name (or accessor) within the `Scope`_. These accessors
|
||||
combine the struct name and field name and are globally unique, being used in
|
||||
the ``access`` method for reading values from the input/current state.
|
||||
|
||||
.. literalinclude:: /generated/functional/smtlib.cc
|
||||
:language: c++
|
||||
:caption: ``Struct::access()`` method
|
||||
:start-at: SExpr access(
|
||||
:end-before: };
|
||||
|
||||
In Rosette, struct fields are accessed as ``<struct_name>-<field_name>`` so
|
||||
including the struct name in the field name would be redundant. For
|
||||
`write_functional_rosette` we instead choose to make field names unique only
|
||||
within the struct, while accessors are unique across the whole module. We thus
|
||||
modify the class constructor and ``insert`` method to support this; providing
|
||||
one scope that is local to the struct (``local_scope``) and one which is shared
|
||||
across the whole module (``global_scope``), leaving the ``access`` method
|
||||
unchanged.
|
||||
|
||||
.. literalinclude:: /generated/functional/rosette.diff
|
||||
:language: diff
|
||||
:caption: diff of struct constructor
|
||||
:start-at: SmtStruct(std::string name, SmtScope &scope)
|
||||
:end-before: void write_definition
|
||||
|
||||
Finally, ``SmtStruct`` also provides a ``write_value`` template method which
|
||||
calls a provided function on each element in the struct. This is used later for
|
||||
assigning values to the output/next state pair. The only change here is to
|
||||
remove the check for zero-argument constructors since this is not necessary with
|
||||
Rosette ``struct``\ s.
|
||||
|
||||
.. literalinclude:: /generated/functional/rosette.diff
|
||||
:language: diff
|
||||
:caption: diff of ``write_value`` method
|
||||
:start-at: template<typename Fn> void write_value
|
||||
:end-before: SExpr access
|
||||
|
||||
PrintVisitor
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Remember in the `minimal backend`_ we converted nodes into strings for writing
|
||||
using the ``node.to_string()`` method, which wrapped ``node.visit()`` with a
|
||||
private visitor. We now want a custom visitor which can convert nodes into
|
||||
s-expressions. This is where the ``PrintVisitor`` comes in, implementing the
|
||||
abstract ``Functional::AbstractVisitor`` class with a return type of ``SExpr``.
|
||||
For most functions, the Rosette output is very similar to the corresponding
|
||||
SMT-LIB function with minor adjustments for syntax.
|
||||
|
||||
.. literalinclude:: /generated/functional/rosette.diff
|
||||
:language: diff
|
||||
:caption: portion of ``Functional::AbstractVisitor`` implementation diff showing similarities
|
||||
:start-at: SExpr logical_shift_left
|
||||
:end-at: "list-set-bv"
|
||||
|
||||
However there are some differences in the two formats with regards to how
|
||||
booleans are handled, with Rosette providing built-in functions for conversion.
|
||||
|
||||
.. literalinclude:: /generated/functional/rosette.diff
|
||||
:language: diff
|
||||
:caption: portion of ``Functional::AbstractVisitor`` implementation diff showing differences
|
||||
:start-at: SExpr from_bool
|
||||
:end-before: SExpr extract
|
||||
|
||||
Of note here is the rare instance of the Rosette implementation *gaining* static
|
||||
typing rather than losing it. Where SMT_LIB calls zero/sign extension with the
|
||||
number of extra bits needed (given by ``out_width - a.width()``), Rosette
|
||||
instead specifies the type of the output (given by ``list("bitvector",
|
||||
out_width)``).
|
||||
|
||||
.. literalinclude:: /generated/functional/rosette.diff
|
||||
:language: diff
|
||||
:caption: zero/sign extension implementation diff
|
||||
:start-after: SExpr buf(
|
||||
:end-before: SExpr concat(
|
||||
:lines: 2-3, 5-6
|
||||
|
||||
.. note:: Be sure to check the source code for the full list of differences here.
|
||||
|
||||
Module
|
||||
~~~~~~
|
||||
|
||||
With most of the supporting classes out of the way, we now reach our three main
|
||||
steps from the `minimal backend`_. These are all handled by the ``SmtModule``
|
||||
class, with the mapping from RTLIL module to FunctionalIR happening in the
|
||||
constructor. Each of the three ``SmtStruct``\ s; inputs, outputs, and state;
|
||||
are also created in the constructor, with each value in the corresponding lists
|
||||
in the IR being ``insert``\ ed.
|
||||
|
||||
.. literalinclude:: /generated/functional/smtlib.cc
|
||||
:language: c++
|
||||
:caption: ``SmtModule`` constructor
|
||||
:start-at: SmtModule(Module
|
||||
:end-at: }
|
||||
|
||||
Since Racket uses the ``-`` to access struct fields, the ``SmtrModule`` instead
|
||||
uses an underscore for the name of the initial state.
|
||||
|
||||
.. literalinclude:: /generated/functional/rosette.diff
|
||||
:language: diff
|
||||
:caption: diff of ``Module`` constructor
|
||||
:start-at: scope.reserve(name
|
||||
:end-before: for (auto input
|
||||
|
||||
The ``write`` method is then responsible for writing the FunctionalIR to the
|
||||
output file, formatted for the corresponding backend. ``SmtModule::write()``
|
||||
breaks the output file down into four parts: defining the three structs,
|
||||
declaring the ``pair`` datatype, defining the transfer function ``(inputs,
|
||||
current_state) -> (outputs, next_state)`` with ``write_eval``, and declaring the
|
||||
initial state with ``write_initial``. The only change for the ``SmtrModule`` is
|
||||
that the ``pair`` declaration isn't needed.
|
||||
|
||||
.. literalinclude:: /generated/functional/rosette.diff
|
||||
:language: diff
|
||||
:caption: diff of ``Module::write()`` method
|
||||
:start-at: void write(std::ostream &out)
|
||||
:end-at: }
|
||||
|
||||
The ``write_eval`` method is where the FunctionalIR nodes, outputs, and next
|
||||
state are handled. Just as with the `minimal backend`_, we iterate over the
|
||||
nodes with ``for(auto n : ir)``, and then use the ``Struct::write_value()``
|
||||
method for the ``output_struct`` and ``state_struct`` to iterate over the
|
||||
outputs and next state respectively.
|
||||
|
||||
.. literalinclude:: /generated/functional/smtlib.cc
|
||||
:language: c++
|
||||
:caption: iterating over FunctionalIR nodes in ``SmtModule::write_eval()``
|
||||
:start-at: for(auto n : ir)
|
||||
:end-at: }
|
||||
|
||||
The main differences between our two backends here are syntactical. First we
|
||||
change the ``define-fun`` for the Racket style ``define`` which drops the
|
||||
explicitly typed inputs/outputs. And then we change the final result from a
|
||||
``pair`` to the native ``cons`` which acts in much the same way, returning both
|
||||
the ``outputs`` and the ``next_state`` in a single variable.
|
||||
|
||||
.. literalinclude:: /generated/functional/rosette.diff
|
||||
:language: diff
|
||||
:caption: diff of ``Module::write_eval()`` transfer function declaration
|
||||
:start-at: w.open(list("define-fun"
|
||||
:end-at: w.open(list("define"
|
||||
|
||||
.. literalinclude:: /generated/functional/rosette.diff
|
||||
:language: diff
|
||||
:caption: diff of output/next state handling ``Module::write_eval()``
|
||||
:start-at: w.open(list("pair"
|
||||
:end-at: w.pop();
|
||||
|
||||
For the ``write_initial`` method, the SMT-LIB backend uses ``declare-const`` and
|
||||
``assert``\ s which must always hold true. For Rosette we instead define the
|
||||
initial state as any other variable that can be used by external code. This
|
||||
variable, ``[name]_initial``, can then be used in the ``[name]`` function call;
|
||||
allowing the Rosette code to be used in the generation of the ``next_state``,
|
||||
whereas the SMT-LIB code can only verify that a given ``next_state`` is correct.
|
||||
|
||||
.. literalinclude:: /generated/functional/rosette.diff
|
||||
:language: diff
|
||||
:caption: diff of ``Module::write_initial()`` method
|
||||
:start-at: void write_initial
|
||||
:end-before: void write
|
||||
|
||||
Backend
|
||||
~~~~~~~
|
||||
|
||||
The final part is the ``Backend`` itself, with much of the same boiler plate as
|
||||
the `minimal backend`_. The main difference is that we use the `Module`_ to
|
||||
perform the actual processing.
|
||||
|
||||
.. literalinclude:: /generated/functional/smtlib.cc
|
||||
:language: c++
|
||||
:caption: The ``FunctionalSmtBackend``
|
||||
:start-at: struct FunctionalSmtBackend
|
||||
:end-at: } FunctionalSmtBackend;
|
||||
|
||||
There are two additions here for Rosette. The first is that the output file
|
||||
needs to start with the ``#lang`` definition which tells the
|
||||
compiler/interpreter that we want to use the Rosette language module. The
|
||||
second is that the `write_functional_rosette` command takes an optional
|
||||
argument, ``-provides``. If this argument is given, then the output file gets
|
||||
an additional line declaring that everything in the file should be exported for
|
||||
use; allowing the file to be treated as a Racket package with structs and
|
||||
mapping function available for use externally.
|
||||
|
||||
.. literalinclude:: /generated/functional/rosette.diff
|
||||
:language: diff
|
||||
:caption: relevant portion of diff of ``Backend::execute()`` method
|
||||
:start-at: lang rosette/safe
|
||||
:end-before: for (auto module
|
||||
|
|
|
@ -67,9 +67,10 @@ static ffi_fptr resolve_fn (std::string symbol_name)
|
|||
AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args)
|
||||
{
|
||||
AST::AstNode *newNode = nullptr;
|
||||
union { double f64; float f32; int32_t i32; void *ptr; } value_store [args.size() + 1];
|
||||
ffi_type *types [args.size() + 1];
|
||||
void *values [args.size() + 1];
|
||||
union value { double f64; float f32; int32_t i32; void *ptr; };
|
||||
std::vector<value> value_store(args.size() + 1);
|
||||
std::vector<ffi_type *> types(args.size() + 1);
|
||||
std::vector<void *> values(args.size() + 1);
|
||||
ffi_cif cif;
|
||||
int status;
|
||||
|
||||
|
@ -118,10 +119,10 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname,
|
|||
log_error("invalid rtype '%s'.\n", rtype.c_str());
|
||||
}
|
||||
|
||||
if ((status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, args.size(), types[args.size()], types)) != FFI_OK)
|
||||
if ((status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, args.size(), types[args.size()], types.data())) != FFI_OK)
|
||||
log_error("ffi_prep_cif failed: status %d.\n", status);
|
||||
|
||||
ffi_call(&cif, resolve_fn(fname.c_str()), values[args.size()], values);
|
||||
ffi_call(&cif, resolve_fn(fname.c_str()), values[args.size()], values.data());
|
||||
|
||||
if (rtype == "real") {
|
||||
newNode = new AstNode(AST_REALVALUE);
|
||||
|
|
|
@ -348,7 +348,7 @@ static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool fla
|
|||
RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? ID($_OR_) : ID($_AND_));
|
||||
enable_gate->setPort(ID::A, enable_sig);
|
||||
enable_gate->setPort(ID::B, clear_enable);
|
||||
enable_gate->setPort(ID::Y, data_sig = module->addWire(NEW_ID));
|
||||
enable_gate->setPort(ID::Y, enable_sig = module->addWire(NEW_ID));
|
||||
}
|
||||
|
||||
if (preset_sig.size() == 1)
|
||||
|
@ -376,7 +376,7 @@ static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool fla
|
|||
RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? ID($_OR_) : ID($_AND_));
|
||||
enable_gate->setPort(ID::A, enable_sig);
|
||||
enable_gate->setPort(ID::B, preset_enable);
|
||||
enable_gate->setPort(ID::Y, data_sig = module->addWire(NEW_ID));
|
||||
enable_gate->setPort(ID::Y, enable_sig = module->addWire(NEW_ID));
|
||||
}
|
||||
|
||||
cell = module->addCell(NEW_ID, stringf("$_DLATCH_%c_", enable_polarity ? 'P' : 'N'));
|
||||
|
|
|
@ -4298,7 +4298,7 @@ struct ReadPass : public Pass {
|
|||
log("\n");
|
||||
log(" read {-f|-F} <command-file>\n");
|
||||
log("\n");
|
||||
log("Load and execute the specified command file. (Requires Verific.)\n");
|
||||
log("Load and execute the specified command file.\n");
|
||||
log("Check verific command for more information about supported commands in file.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
|
@ -4412,10 +4412,14 @@ struct ReadPass : public Pass {
|
|||
if (args[1] == "-f" || args[1] == "-F") {
|
||||
if (use_verific) {
|
||||
args[0] = "verific";
|
||||
Pass::call(design, args);
|
||||
} else {
|
||||
cmd_error(args, 1, "This version of Yosys is built without Verific support.\n");
|
||||
#if !defined(__wasm)
|
||||
args[0] = "read_verilog_file_list";
|
||||
#else
|
||||
cmd_error(args, 1, "Command files are not supported on this platform.\n");
|
||||
#endif
|
||||
}
|
||||
Pass::call(design, args);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#if !defined(__wasm)
|
||||
#include <filesystem>
|
||||
#endif
|
||||
|
||||
#include "verilog_frontend.h"
|
||||
#include "preproc.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
@ -672,6 +676,89 @@ struct VerilogDefines : public Pass {
|
|||
}
|
||||
} VerilogDefines;
|
||||
|
||||
#if !defined(__wasm)
|
||||
|
||||
static void parse_file_list(const std::string &file_list_path, RTLIL::Design *design, bool relative_to_file_list_path)
|
||||
{
|
||||
std::ifstream flist(file_list_path);
|
||||
if (!flist.is_open()) {
|
||||
log_error("Verilog file list file does not exist");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::filesystem::path file_list_parent_dir = std::filesystem::path(file_list_path).parent_path();
|
||||
|
||||
std::string v_file_name;
|
||||
while (std::getline(flist, v_file_name)) {
|
||||
if (v_file_name.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::filesystem::path verilog_file_path;
|
||||
if (relative_to_file_list_path) {
|
||||
verilog_file_path = file_list_parent_dir / v_file_name;
|
||||
} else {
|
||||
verilog_file_path = std::filesystem::current_path() / v_file_name;
|
||||
}
|
||||
|
||||
bool is_sv = (verilog_file_path.extension() == ".sv");
|
||||
|
||||
std::vector<std::string> read_verilog_cmd = {"read_verilog", "-defer"};
|
||||
if (is_sv) {
|
||||
read_verilog_cmd.push_back("-sv");
|
||||
}
|
||||
read_verilog_cmd.push_back(verilog_file_path.string());
|
||||
Pass::call(design, read_verilog_cmd);
|
||||
}
|
||||
|
||||
flist.close();
|
||||
}
|
||||
|
||||
struct VerilogFileList : public Pass {
|
||||
VerilogFileList() : Pass("read_verilog_file_list", "Parse a Verilog file list") {}
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" read_verilog_file_list [options]\n");
|
||||
log("\n");
|
||||
log("Parse a Verilog file list, and pass the list of Verilog files to read_verilog\n");
|
||||
log("command\n");
|
||||
log("\n");
|
||||
log(" -F file_list_path\n");
|
||||
log(" File list file contains list of Verilog files to be parsed, any path is\n");
|
||||
log(" treated relative to the file list file\n");
|
||||
log("\n");
|
||||
log(" -f file_list_path\n");
|
||||
log(" File list file contains list of Verilog files to be parsed, any path is\n");
|
||||
log(" treated relative to current working directroy\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-F" && argidx + 1 < args.size()) {
|
||||
std::string file_list_path = args[++argidx];
|
||||
parse_file_list(file_list_path, design, true);
|
||||
continue;
|
||||
}
|
||||
if (arg == "-f" && argidx + 1 < args.size()) {
|
||||
std::string file_list_path = args[++argidx];
|
||||
parse_file_list(file_list_path, design, false);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
extra_args(args, argidx, design);
|
||||
}
|
||||
} VerilogFilelist;
|
||||
|
||||
#endif
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
// the yyerror function used by bison to report parser errors
|
||||
|
|
|
@ -453,7 +453,7 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL
|
|||
}
|
||||
|
||||
// FIXME: $mul $div $mod $divfloor $modfloor $slice $concat
|
||||
// FIXME: $lut $sop $alu $lcu $macc $fa
|
||||
// FIXME: $lut $sop $alu $lcu $macc $macc_v2 $fa
|
||||
// FIXME: $mul $div $mod $divfloor $modfloor $pow $slice $concat $bweqx
|
||||
// FIXME: $lut $sop $alu $lcu $macc $fa $logic_and $logic_or $bwmux
|
||||
|
||||
|
|
|
@ -144,6 +144,7 @@ struct CellTypes
|
|||
|
||||
setup_type(ID($lcu), {ID::P, ID::G, ID::CI}, {ID::CO}, true);
|
||||
setup_type(ID($alu), {ID::A, ID::B, ID::CI, ID::BI}, {ID::X, ID::Y, ID::CO}, true);
|
||||
setup_type(ID($macc_v2), {ID::A, ID::B, ID::C}, {ID::Y}, true);
|
||||
setup_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -310,7 +310,7 @@ struct ConstEval
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (cell->type == ID($macc))
|
||||
else if (cell->type.in(ID($macc), ID($macc_v2)))
|
||||
{
|
||||
Macc macc;
|
||||
macc.from_cell(cell);
|
||||
|
|
|
@ -276,3 +276,11 @@ X(Y)
|
|||
X(Y_WIDTH)
|
||||
X(area)
|
||||
X(capacitance)
|
||||
X(NPRODUCTS)
|
||||
X(NADDENDS)
|
||||
X(PRODUCT_NEGATED)
|
||||
X(ADDEND_NEGATED)
|
||||
X(A_WIDTHS)
|
||||
X(B_WIDTHS)
|
||||
X(C_WIDTHS)
|
||||
X(C_SIGNED)
|
||||
|
|
|
@ -634,10 +634,11 @@ std::string escape_cxx_string(const std::string &input)
|
|||
output.push_back('\\');
|
||||
output.push_back(c);
|
||||
} else {
|
||||
char l = c & 0xf, h = (c >> 4) & 0xf;
|
||||
output.append("\\x");
|
||||
output.push_back((h < 10 ? '0' + h : 'a' + h - 10));
|
||||
output.push_back((l < 10 ? '0' + l : 'a' + l - 10));
|
||||
char l = c & 0x7, m = (c >> 3) & 0x7, h = (c >> 6) & 0x3;
|
||||
output.push_back('\\');
|
||||
output.push_back('0' + h);
|
||||
output.push_back('0' + m);
|
||||
output.push_back('0' + l);
|
||||
}
|
||||
}
|
||||
output.push_back('"');
|
||||
|
|
160
kernel/macc.h
160
kernel/macc.h
|
@ -82,7 +82,7 @@ struct Macc
|
|||
new_ports.swap(ports);
|
||||
}
|
||||
|
||||
void from_cell(RTLIL::Cell *cell)
|
||||
void from_cell_v1(RTLIL::Cell *cell)
|
||||
{
|
||||
RTLIL::SigSpec port_a = cell->getPort(ID::A);
|
||||
|
||||
|
@ -136,52 +136,128 @@ struct Macc
|
|||
log_assert(port_a_cursor == GetSize(port_a));
|
||||
}
|
||||
|
||||
void to_cell(RTLIL::Cell *cell) const
|
||||
void from_cell(RTLIL::Cell *cell)
|
||||
{
|
||||
RTLIL::SigSpec port_a;
|
||||
std::vector<RTLIL::State> config_bits;
|
||||
int max_size = 0, num_bits = 0;
|
||||
if (cell->type == ID($macc)) {
|
||||
from_cell_v1(cell);
|
||||
return;
|
||||
}
|
||||
log_assert(cell->type == ID($macc_v2));
|
||||
|
||||
for (auto &port : ports) {
|
||||
max_size = max(max_size, GetSize(port.in_a));
|
||||
max_size = max(max_size, GetSize(port.in_b));
|
||||
RTLIL::SigSpec port_a = cell->getPort(ID::A);
|
||||
RTLIL::SigSpec port_b = cell->getPort(ID::B);
|
||||
RTLIL::SigSpec port_c = cell->getPort(ID::C);
|
||||
|
||||
ports.clear();
|
||||
|
||||
int nproducts = cell->getParam(ID::NPRODUCTS).as_int();
|
||||
const Const &product_neg = cell->getParam(ID::PRODUCT_NEGATED);
|
||||
const Const &a_widths = cell->getParam(ID::A_WIDTHS);
|
||||
const Const &b_widths = cell->getParam(ID::B_WIDTHS);
|
||||
const Const &a_signed = cell->getParam(ID::A_SIGNED);
|
||||
const Const &b_signed = cell->getParam(ID::B_SIGNED);
|
||||
int ai = 0, bi = 0;
|
||||
for (int i = 0; i < nproducts; i++) {
|
||||
port_t term;
|
||||
|
||||
log_assert(a_signed[i] == b_signed[i]);
|
||||
term.is_signed = (a_signed[i] == State::S1);
|
||||
int a_width = a_widths.extract(16 * i, 16).as_int(false);
|
||||
int b_width = b_widths.extract(16 * i, 16).as_int(false);
|
||||
|
||||
term.in_a = port_a.extract(ai, a_width);
|
||||
ai += a_width;
|
||||
term.in_b = port_b.extract(bi, b_width);
|
||||
bi += b_width;
|
||||
term.do_subtract = (product_neg[i] == State::S1);
|
||||
|
||||
ports.push_back(term);
|
||||
}
|
||||
log_assert(port_a.size() == ai);
|
||||
log_assert(port_b.size() == bi);
|
||||
|
||||
int naddends = cell->getParam(ID::NADDENDS).as_int();
|
||||
const Const &addend_neg = cell->getParam(ID::ADDEND_NEGATED);
|
||||
const Const &c_widths = cell->getParam(ID::C_WIDTHS);
|
||||
const Const &c_signed = cell->getParam(ID::C_SIGNED);
|
||||
int ci = 0;
|
||||
for (int i = 0; i < naddends; i++) {
|
||||
port_t term;
|
||||
|
||||
term.is_signed = (c_signed[i] == State::S1);
|
||||
int c_width = c_widths.extract(16 * i, 16).as_int(false);
|
||||
|
||||
term.in_a = port_c.extract(ci, c_width);
|
||||
ci += c_width;
|
||||
term.do_subtract = (addend_neg[i] == State::S1);
|
||||
|
||||
ports.push_back(term);
|
||||
}
|
||||
log_assert(port_c.size() == ci);
|
||||
}
|
||||
|
||||
void to_cell(RTLIL::Cell *cell)
|
||||
{
|
||||
cell->type = ID($macc_v2);
|
||||
|
||||
int nproducts = 0, naddends = 0;
|
||||
Const a_signed, b_signed, a_widths, b_widths, product_negated;
|
||||
Const c_signed, c_widths, addend_negated;
|
||||
SigSpec a, b, c;
|
||||
|
||||
for (int i = 0; i < (int) ports.size(); i++) {
|
||||
SigSpec term_a = ports[i].in_a, term_b = ports[i].in_b;
|
||||
|
||||
if (term_b.empty()) {
|
||||
// addend
|
||||
c_widths.append(Const(term_a.size(), 16));
|
||||
c_signed.append(ports[i].is_signed ? RTLIL::S1 : RTLIL::S0);
|
||||
addend_negated.append(ports[i].do_subtract ? RTLIL::S1 : RTLIL::S0);
|
||||
c.append(term_a);
|
||||
naddends++;
|
||||
} else {
|
||||
// product
|
||||
a_widths.append(Const(term_a.size(), 16));
|
||||
b_widths.append(Const(term_b.size(), 16));
|
||||
a_signed.append(ports[i].is_signed ? RTLIL::S1 : RTLIL::S0);
|
||||
b_signed.append(ports[i].is_signed ? RTLIL::S1 : RTLIL::S0);
|
||||
product_negated.append(ports[i].do_subtract ? RTLIL::S1 : RTLIL::S0);
|
||||
a.append(term_a);
|
||||
b.append(term_b);
|
||||
nproducts++;
|
||||
}
|
||||
}
|
||||
|
||||
while (max_size)
|
||||
num_bits++, max_size /= 2;
|
||||
if (a_signed.empty())
|
||||
a_signed = {RTLIL::Sx};
|
||||
if (b_signed.empty())
|
||||
b_signed = {RTLIL::Sx};
|
||||
if (c_signed.empty())
|
||||
c_signed = {RTLIL::Sx};
|
||||
if (a_widths.empty())
|
||||
a_widths = {RTLIL::Sx};
|
||||
if (b_widths.empty())
|
||||
b_widths = {RTLIL::Sx};
|
||||
if (c_widths.empty())
|
||||
c_widths = {RTLIL::Sx};
|
||||
if (product_negated.empty())
|
||||
product_negated = {RTLIL::Sx};
|
||||
if (addend_negated.empty())
|
||||
addend_negated = {RTLIL::Sx};
|
||||
|
||||
log_assert(num_bits < 16);
|
||||
config_bits.push_back(num_bits & 1 ? State::S1 : State::S0);
|
||||
config_bits.push_back(num_bits & 2 ? State::S1 : State::S0);
|
||||
config_bits.push_back(num_bits & 4 ? State::S1 : State::S0);
|
||||
config_bits.push_back(num_bits & 8 ? State::S1 : State::S0);
|
||||
|
||||
for (auto &port : ports)
|
||||
{
|
||||
if (GetSize(port.in_a) == 0)
|
||||
continue;
|
||||
|
||||
config_bits.push_back(port.is_signed ? State::S1 : State::S0);
|
||||
config_bits.push_back(port.do_subtract ? State::S1 : State::S0);
|
||||
|
||||
int size_a = GetSize(port.in_a);
|
||||
for (int i = 0; i < num_bits; i++)
|
||||
config_bits.push_back(size_a & (1 << i) ? State::S1 : State::S0);
|
||||
|
||||
int size_b = GetSize(port.in_b);
|
||||
for (int i = 0; i < num_bits; i++)
|
||||
config_bits.push_back(size_b & (1 << i) ? State::S1 : State::S0);
|
||||
|
||||
port_a.append(port.in_a);
|
||||
port_a.append(port.in_b);
|
||||
}
|
||||
|
||||
cell->setPort(ID::A, port_a);
|
||||
cell->setPort(ID::B, {});
|
||||
cell->setParam(ID::CONFIG, config_bits);
|
||||
cell->setParam(ID::CONFIG_WIDTH, GetSize(config_bits));
|
||||
cell->setParam(ID::A_WIDTH, GetSize(port_a));
|
||||
cell->setParam(ID::B_WIDTH, 0);
|
||||
cell->setParam(ID::NPRODUCTS, nproducts);
|
||||
cell->setParam(ID::PRODUCT_NEGATED, product_negated);
|
||||
cell->setParam(ID::NADDENDS, naddends);
|
||||
cell->setParam(ID::ADDEND_NEGATED, addend_negated);
|
||||
cell->setParam(ID::A_SIGNED, a_signed);
|
||||
cell->setParam(ID::B_SIGNED, b_signed);
|
||||
cell->setParam(ID::C_SIGNED, c_signed);
|
||||
cell->setParam(ID::A_WIDTHS, a_widths);
|
||||
cell->setParam(ID::B_WIDTHS, b_widths);
|
||||
cell->setParam(ID::C_WIDTHS, c_widths);
|
||||
cell->setPort(ID::A, a);
|
||||
cell->setPort(ID::B, b);
|
||||
cell->setPort(ID::C, c);
|
||||
}
|
||||
|
||||
bool eval(RTLIL::Const &result) const
|
||||
|
|
|
@ -540,6 +540,12 @@ void RTLIL::Const::bitvectorize() const {
|
|||
}
|
||||
}
|
||||
|
||||
void RTLIL::Const::append(const RTLIL::Const &other) {
|
||||
bitvectorize();
|
||||
bitvectype& bv = get_bits();
|
||||
bv.insert(bv.end(), other.begin(), other.end());
|
||||
}
|
||||
|
||||
RTLIL::State RTLIL::Const::const_iterator::operator*() const {
|
||||
if (auto bv = parent.get_if_bits())
|
||||
return (*bv)[idx];
|
||||
|
@ -1461,6 +1467,40 @@ namespace {
|
|||
return;
|
||||
}
|
||||
|
||||
if (cell->type == ID($macc_v2)) {
|
||||
if (param(ID::NPRODUCTS) < 0)
|
||||
error(__LINE__);
|
||||
if (param(ID::NADDENDS) < 0)
|
||||
error(__LINE__);
|
||||
param_bits(ID::PRODUCT_NEGATED, max(param(ID::NPRODUCTS), 1));
|
||||
param_bits(ID::ADDEND_NEGATED, max(param(ID::NADDENDS), 1));
|
||||
param_bits(ID::A_SIGNED, max(param(ID::NPRODUCTS), 1));
|
||||
param_bits(ID::B_SIGNED, max(param(ID::NPRODUCTS), 1));
|
||||
param_bits(ID::C_SIGNED, max(param(ID::NADDENDS), 1));
|
||||
if (cell->getParam(ID::A_SIGNED) != cell->getParam(ID::B_SIGNED))
|
||||
error(__LINE__);
|
||||
param_bits(ID::A_WIDTHS, max(param(ID::NPRODUCTS) * 16, 1));
|
||||
param_bits(ID::B_WIDTHS, max(param(ID::NPRODUCTS) * 16, 1));
|
||||
param_bits(ID::C_WIDTHS, max(param(ID::NADDENDS) * 16, 1));
|
||||
const Const &a_width = cell->getParam(ID::A_WIDTHS);
|
||||
const Const &b_width = cell->getParam(ID::B_WIDTHS);
|
||||
const Const &c_width = cell->getParam(ID::C_WIDTHS);
|
||||
int a_width_sum = 0, b_width_sum = 0, c_width_sum = 0;
|
||||
for (int i = 0; i < param(ID::NPRODUCTS); i++) {
|
||||
a_width_sum += a_width.extract(16 * i, 16).as_int(false);
|
||||
b_width_sum += b_width.extract(16 * i, 16).as_int(false);
|
||||
}
|
||||
for (int i = 0; i < param(ID::NADDENDS); i++) {
|
||||
c_width_sum += c_width.extract(16 * i, 16).as_int(false);
|
||||
}
|
||||
port(ID::A, a_width_sum);
|
||||
port(ID::B, b_width_sum);
|
||||
port(ID::C, c_width_sum);
|
||||
port(ID::Y, param(ID::Y_WIDTH));
|
||||
check_expected();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell->type == ID($logic_not)) {
|
||||
param_bool(ID::A_SIGNED);
|
||||
port(ID::A, param(ID::A_WIDTH));
|
||||
|
@ -4093,6 +4133,11 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed)
|
|||
return;
|
||||
}
|
||||
|
||||
if (type == ID($macc_v2)) {
|
||||
parameters[ID::Y_WIDTH] = GetSize(connections_[ID::Y]);
|
||||
return;
|
||||
}
|
||||
|
||||
bool signedness_ab = !type.in(ID($slice), ID($concat), ID($macc));
|
||||
|
||||
if (connections_.count(ID::A)) {
|
||||
|
|
|
@ -738,6 +738,8 @@ public:
|
|||
bool empty() const;
|
||||
void bitvectorize() const;
|
||||
|
||||
void append(const RTLIL::Const &other);
|
||||
|
||||
class const_iterator {
|
||||
private:
|
||||
const Const& parent;
|
||||
|
@ -1673,6 +1675,19 @@ public:
|
|||
RTLIL::Cell *driverCell() const { log_assert(driverCell_); return driverCell_; };
|
||||
RTLIL::IdString driverPort() const { log_assert(driverCell_); return driverPort_; };
|
||||
|
||||
int from_hdl_index(int hdl_index) {
|
||||
int zero_index = hdl_index - start_offset;
|
||||
int rtlil_index = upto ? width - 1 - zero_index : zero_index;
|
||||
return rtlil_index >= 0 && rtlil_index < width ? rtlil_index : INT_MIN;
|
||||
}
|
||||
|
||||
int to_hdl_index(int rtlil_index) {
|
||||
if (rtlil_index < 0 || rtlil_index >= width)
|
||||
return INT_MIN;
|
||||
int zero_index = upto ? width - 1 - rtlil_index : rtlil_index;
|
||||
return zero_index + start_offset;
|
||||
}
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
static std::map<unsigned int, RTLIL::Wire*> *get_all_wires(void);
|
||||
#endif
|
||||
|
|
|
@ -740,7 +740,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (cell->type == ID($macc))
|
||||
if (cell->type.in(ID($macc), ID($macc_v2)))
|
||||
{
|
||||
std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
|
||||
std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
|
||||
|
|
|
@ -413,8 +413,11 @@ std::string make_temp_dir(std::string template_str)
|
|||
# endif
|
||||
|
||||
char *p = strdup(template_str.c_str());
|
||||
log_assert(p);
|
||||
char *res = mkdtemp(p);
|
||||
log_assert(res != NULL);
|
||||
if (!res)
|
||||
log_error("mkdtemp failed for '%s': %s [Error %d]\n",
|
||||
p, strerror(errno), errno);
|
||||
template_str = p;
|
||||
free(p);
|
||||
|
||||
|
@ -754,9 +757,10 @@ struct TclPass : public Pass {
|
|||
log("If any arguments are specified, these arguments are provided to the script via\n");
|
||||
log("the standard $argc and $argv variables.\n");
|
||||
log("\n");
|
||||
log("Note, tcl will not recieve the output of any yosys command. If the output\n");
|
||||
log("Note, tcl will not receive the output of any yosys command. If the output\n");
|
||||
log("of the tcl commands are needed, use the yosys command 'tee -s result.string'\n");
|
||||
log("to redirect yosys's output to the 'result.string' scratchpad value.\n");
|
||||
log("The 'result.string' value is then used as the tcl output value of the command.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *) override {
|
||||
|
|
10
libs/fst/00_PATCH_wx_len_overread.patch
Normal file
10
libs/fst/00_PATCH_wx_len_overread.patch
Normal file
|
@ -0,0 +1,10 @@
|
|||
--- fstapi.cc
|
||||
+++ fstapi.cc
|
||||
@@ -6072,6 +6072,7 @@ for(;;)
|
||||
}
|
||||
|
||||
wx_len = snprintf(wx_buf, 32, "r%.16g", d);
|
||||
+ if (wx_len > 32 || wx_len < 0) wx_len = 32;
|
||||
fstWritex(xc, wx_buf, wx_len);
|
||||
}
|
||||
}
|
|
@ -18,3 +18,4 @@ sed -i -e 's,"fastlz.c","fastlz.cc",' *.cc *.h
|
|||
patch -p0 < 00_PATCH_win_zlib.patch
|
||||
patch -p0 < 00_PATCH_win_io.patch
|
||||
patch -p1 < 00_PATCH_strict_alignment.patch
|
||||
patch -p0 < 00_PATCH_wx_len_overread.patch
|
||||
|
|
|
@ -3907,16 +3907,18 @@ while (value)
|
|||
static int fstVcdIDForFwrite(char *buf, unsigned int value)
|
||||
{
|
||||
char *pnt = buf;
|
||||
int len = 0;
|
||||
|
||||
/* zero is illegal for a value...it is assumed they start at one */
|
||||
while (value)
|
||||
while (value && len < 14)
|
||||
{
|
||||
value--;
|
||||
++len;
|
||||
*(pnt++) = (char)('!' + value % 94);
|
||||
value = value / 94;
|
||||
}
|
||||
|
||||
return(pnt - buf);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
|
@ -6070,6 +6072,7 @@ for(;;)
|
|||
}
|
||||
|
||||
wx_len = snprintf(wx_buf, 32, "r%.16g", d);
|
||||
if (wx_len > 32 || wx_len < 0) wx_len = 32;
|
||||
fstWritex(xc, wx_buf, wx_len);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,3 +53,4 @@ OBJS += passes/cmds/example_dt.o
|
|||
OBJS += passes/cmds/portarcs.o
|
||||
OBJS += passes/cmds/wrapcell.o
|
||||
OBJS += passes/cmds/setenv.o
|
||||
OBJS += passes/cmds/abstract.o
|
||||
|
|
491
passes/cmds/abstract.cc
Normal file
491
passes/cmds/abstract.cc
Normal file
|
@ -0,0 +1,491 @@
|
|||
#include "kernel/yosys.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/ff.h"
|
||||
#include "kernel/ffinit.h"
|
||||
#include <variant>
|
||||
#include <charconv>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct EnableLogic {
|
||||
SigBit bit;
|
||||
bool pol;
|
||||
};
|
||||
|
||||
enum SliceIndices {
|
||||
RtlilSlice,
|
||||
HdlSlice,
|
||||
};
|
||||
|
||||
struct Slice {
|
||||
SliceIndices indices;
|
||||
int first;
|
||||
int last;
|
||||
|
||||
Slice(SliceIndices indices, const std::string &slice) :
|
||||
indices(indices)
|
||||
{
|
||||
if (slice.empty())
|
||||
syntax_error(slice);
|
||||
auto sep = slice.find(':');
|
||||
const char *first_begin, *first_end, *last_begin, *last_end;
|
||||
if (sep == std::string::npos) {
|
||||
first_begin = last_begin = slice.c_str();
|
||||
first_end = last_end = slice.c_str() + slice.length();
|
||||
} else {
|
||||
first_begin = slice.c_str();
|
||||
first_end = first_begin + sep;
|
||||
|
||||
last_begin = first_end + 1;
|
||||
last_end = slice.c_str() + slice.length();
|
||||
}
|
||||
first = parse_index(first_begin, first_end, slice);
|
||||
last = parse_index(last_begin, last_end, slice);
|
||||
}
|
||||
|
||||
static int parse_index(const char *begin, const char *end, const std::string &slice) {
|
||||
int value;
|
||||
auto result = std::from_chars(begin, end, value, 10);
|
||||
if (result.ptr != end || result.ptr == begin)
|
||||
syntax_error(slice);
|
||||
return value;
|
||||
}
|
||||
|
||||
static void syntax_error(const std::string &slice) {
|
||||
log_cmd_error("Invalid slice '%s', expected '<first>:<last>' or '<single>'", slice.c_str());
|
||||
}
|
||||
|
||||
std::string to_string() const {
|
||||
const char *option = indices == RtlilSlice ? "-rtlilslice" : "-slice";
|
||||
if (first == last)
|
||||
return stringf("%s %d", option, first);
|
||||
else
|
||||
return stringf("%s %d:%d", option, first, last);
|
||||
}
|
||||
|
||||
int wire_offset(RTLIL::Wire *wire, int index) const {
|
||||
int rtl_offset = indices == RtlilSlice ? index : wire->from_hdl_index(index);
|
||||
if (rtl_offset < 0 || rtl_offset >= wire->width) {
|
||||
log_error("Slice %s is out of bounds for wire %s in module %s", to_string().c_str(), log_id(wire), log_id(wire->module));
|
||||
}
|
||||
return rtl_offset;
|
||||
}
|
||||
|
||||
std::pair<int, int> wire_range(RTLIL::Wire *wire) const {
|
||||
int rtl_first = wire_offset(wire, first);
|
||||
int rtl_last = wire_offset(wire, last);
|
||||
if (rtl_first > rtl_last)
|
||||
std::swap(rtl_first, rtl_last);
|
||||
return {rtl_first, rtl_last + 1};
|
||||
}
|
||||
};
|
||||
|
||||
void emit_mux_anyseq(Module* mod, const SigSpec& mux_input, const SigSpec& mux_output, EnableLogic enable) {
|
||||
auto anyseq = mod->Anyseq(NEW_ID, mux_input.size());
|
||||
if (enable.bit == (enable.pol ? State::S1 : State::S0)) {
|
||||
mod->connect(mux_output, anyseq);
|
||||
}
|
||||
SigSpec mux_a, mux_b;
|
||||
if (enable.pol) {
|
||||
mux_a = mux_input;
|
||||
mux_b = anyseq;
|
||||
} else {
|
||||
mux_a = anyseq;
|
||||
mux_b = mux_input;
|
||||
}
|
||||
(void)mod->addMux(NEW_ID,
|
||||
mux_a,
|
||||
mux_b,
|
||||
enable.bit,
|
||||
mux_output);
|
||||
}
|
||||
|
||||
bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set<int> offsets, EnableLogic enable) {
|
||||
Wire* abstracted = ff.module->addWire(NEW_ID, offsets.size());
|
||||
SigSpec mux_input;
|
||||
int abstracted_idx = 0;
|
||||
for (int d_idx = 0; d_idx < ff.width; d_idx++) {
|
||||
if (offsets.count(d_idx)) {
|
||||
mux_input.append(port_sig[d_idx]);
|
||||
port_sig[d_idx].wire = abstracted;
|
||||
port_sig[d_idx].offset = abstracted_idx;
|
||||
log_assert(abstracted_idx < abstracted->width);
|
||||
abstracted_idx++;
|
||||
}
|
||||
}
|
||||
emit_mux_anyseq(ff.module, mux_input, abstracted, enable);
|
||||
(void)ff.emit();
|
||||
return true;
|
||||
}
|
||||
|
||||
using SelReason=std::variant<Wire*, Cell*>;
|
||||
|
||||
dict<SigBit, std::vector<SelReason>> gather_selected_reps(Module* mod, const std::vector<Slice> &slices, SigMap& sigmap) {
|
||||
dict<SigBit, std::vector<SelReason>> selected_reps;
|
||||
|
||||
if (slices.empty()) {
|
||||
// Collect reps for all wire bits of selected wires
|
||||
for (auto wire : mod->selected_wires())
|
||||
for (auto bit : sigmap(wire))
|
||||
selected_reps.insert(bit).first->second.push_back(wire);
|
||||
|
||||
// Collect reps for all output wire bits of selected cells
|
||||
for (auto cell : mod->selected_cells())
|
||||
for (auto conn : cell->connections())
|
||||
if (cell->output(conn.first))
|
||||
for (auto bit : conn.second.bits())
|
||||
selected_reps.insert(sigmap(bit)).first->second.push_back(cell);
|
||||
} else {
|
||||
if (mod->selected_wires().size() != 1 || !mod->selected_cells().empty())
|
||||
log_error("Slices are only supported for single-wire selections\n");
|
||||
|
||||
auto wire = mod->selected_wires()[0];
|
||||
|
||||
for (auto slice : slices) {
|
||||
auto [begin, end] = slice.wire_range(wire);
|
||||
for (int i = begin; i < end; i++) {
|
||||
selected_reps.insert(sigmap(SigBit(wire, i))).first->second.push_back(wire);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return selected_reps;
|
||||
}
|
||||
|
||||
void explain_selections(const std::vector<SelReason>& reasons) {
|
||||
for (std::variant<Wire*, Cell*> reason : reasons) {
|
||||
if (Cell** cell_reason = std::get_if<Cell*>(&reason))
|
||||
log_debug("\tcell %s\n", (*cell_reason)->name.c_str());
|
||||
else if (Wire** wire_reason = std::get_if<Wire*>(&reason))
|
||||
log_debug("\twire %s\n", (*wire_reason)->name.c_str());
|
||||
else
|
||||
log_assert(false && "insane reason variant\n");
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int abstract_state(Module* mod, EnableLogic enable, const std::vector<Slice> &slices) {
|
||||
CellTypes ct;
|
||||
ct.setup_internals_ff();
|
||||
SigMap sigmap(mod);
|
||||
dict<SigBit, std::vector<SelReason>> selected_reps = gather_selected_reps(mod, slices, sigmap);
|
||||
|
||||
unsigned int changed = 0;
|
||||
std::vector<FfData> ffs;
|
||||
// Abstract flop inputs if they're driving a selected output rep
|
||||
for (auto cell : mod->cells()) {
|
||||
if (!ct.cell_types.count(cell->type))
|
||||
continue;
|
||||
FfData ff(nullptr, cell);
|
||||
if (ff.has_sr)
|
||||
log_cmd_error("SR not supported\n");
|
||||
ffs.push_back(ff);
|
||||
}
|
||||
for (auto ff : ffs) {
|
||||
// A bit inefficient
|
||||
std::set<int> offsets_to_abstract;
|
||||
for (int i = 0; i < GetSize(ff.sig_q); i++) {
|
||||
SigBit bit = ff.sig_q[i];
|
||||
if (selected_reps.count(sigmap(bit))) {
|
||||
log_debug("Abstracting state for %s.Q[%i] in module %s due to selections:\n", log_id(ff.cell), i, log_id(mod));
|
||||
explain_selections(selected_reps.at(sigmap(bit)));
|
||||
offsets_to_abstract.insert(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (offsets_to_abstract.empty())
|
||||
continue;
|
||||
|
||||
// Normalize to simpler FF
|
||||
ff.unmap_ce();
|
||||
ff.unmap_srst();
|
||||
if (ff.has_arst)
|
||||
ff.arst_to_aload();
|
||||
|
||||
bool cell_changed = false;
|
||||
|
||||
if (ff.has_aload)
|
||||
cell_changed = abstract_state_port(ff, ff.sig_ad, offsets_to_abstract, enable);
|
||||
cell_changed |= abstract_state_port(ff, ff.sig_d, offsets_to_abstract, enable);
|
||||
changed += cell_changed;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool abstract_value_cell_port(Module* mod, Cell* cell, std::set<int> offsets, IdString port_name, EnableLogic enable) {
|
||||
Wire* to_abstract = mod->addWire(NEW_ID, offsets.size());
|
||||
SigSpec mux_input;
|
||||
SigSpec mux_output;
|
||||
const SigSpec& old_port = cell->getPort(port_name);
|
||||
SigSpec new_port = old_port;
|
||||
int to_abstract_idx = 0;
|
||||
for (int port_idx = 0; port_idx < old_port.size(); port_idx++) {
|
||||
if (offsets.count(port_idx)) {
|
||||
mux_output.append(old_port[port_idx]);
|
||||
SigBit in_bit {to_abstract, to_abstract_idx};
|
||||
new_port.replace(port_idx, in_bit);
|
||||
mux_input.append(in_bit);
|
||||
log_assert(to_abstract_idx < to_abstract->width);
|
||||
to_abstract_idx++;
|
||||
}
|
||||
}
|
||||
cell->setPort(port_name, new_port);
|
||||
emit_mux_anyseq(mod, mux_input, mux_output, enable);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool abstract_value_mod_port(Module* mod, Wire* wire, std::set<int> offsets, EnableLogic enable) {
|
||||
Wire* to_abstract = mod->addWire(NEW_ID, wire);
|
||||
to_abstract->port_input = true;
|
||||
to_abstract->port_id = wire->port_id;
|
||||
wire->port_input = false;
|
||||
wire->port_id = 0;
|
||||
mod->swap_names(wire, to_abstract);
|
||||
SigSpec mux_input;
|
||||
SigSpec mux_output;
|
||||
SigSpec direct_lhs;
|
||||
SigSpec direct_rhs;
|
||||
for (int port_idx = 0; port_idx < wire->width; port_idx++) {
|
||||
if (offsets.count(port_idx)) {
|
||||
mux_output.append(SigBit(wire, port_idx));
|
||||
mux_input.append(SigBit(to_abstract, port_idx));
|
||||
} else {
|
||||
direct_lhs.append(SigBit(wire, port_idx));
|
||||
direct_rhs.append(SigBit(to_abstract, port_idx));
|
||||
}
|
||||
}
|
||||
mod->connections_.push_back(SigSig(direct_lhs, direct_rhs));
|
||||
emit_mux_anyseq(mod, mux_input, mux_output, enable);
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int abstract_value(Module* mod, EnableLogic enable, const std::vector<Slice> &slices) {
|
||||
SigMap sigmap(mod);
|
||||
dict<SigBit, std::vector<SelReason>> selected_reps = gather_selected_reps(mod, slices, sigmap);
|
||||
unsigned int changed = 0;
|
||||
std::vector<Cell*> cells_snapshot = mod->cells();
|
||||
for (auto cell : cells_snapshot) {
|
||||
for (auto conn : cell->connections())
|
||||
if (cell->output(conn.first)) {
|
||||
std::set<int> offsets_to_abstract;
|
||||
for (int i = 0; i < conn.second.size(); i++) {
|
||||
if (selected_reps.count(sigmap(conn.second[i]))) {
|
||||
log_debug("Abstracting value for %s.%s[%i] in module %s due to selections:\n",
|
||||
log_id(cell), log_id(conn.first), i, log_id(mod));
|
||||
explain_selections(selected_reps.at(sigmap(conn.second[i])));
|
||||
offsets_to_abstract.insert(i);
|
||||
}
|
||||
}
|
||||
if (offsets_to_abstract.empty())
|
||||
continue;
|
||||
|
||||
changed += abstract_value_cell_port(mod, cell, offsets_to_abstract, conn.first, enable);
|
||||
}
|
||||
}
|
||||
std::vector<Wire*> wires_snapshot = mod->wires();
|
||||
for (auto wire : wires_snapshot)
|
||||
if (wire->port_input) {
|
||||
std::set<int> offsets_to_abstract;
|
||||
for (auto bit : SigSpec(wire))
|
||||
if (selected_reps.count(sigmap(bit))) {
|
||||
log_debug("Abstracting value for module input port bit %s in module %s due to selections:\n",
|
||||
log_signal(bit), log_id(mod));
|
||||
explain_selections(selected_reps.at(sigmap(bit)));
|
||||
offsets_to_abstract.insert(bit.offset);
|
||||
}
|
||||
if (offsets_to_abstract.empty())
|
||||
continue;
|
||||
|
||||
changed += abstract_value_mod_port(mod, wire, offsets_to_abstract, enable);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
unsigned int abstract_init(Module* mod, const std::vector<Slice> &slices) {
|
||||
unsigned int changed = 0;
|
||||
FfInitVals initvals;
|
||||
SigMap sigmap(mod);
|
||||
dict<SigBit, std::vector<SelReason>> selected_reps = gather_selected_reps(mod, slices, sigmap);
|
||||
initvals.set(&sigmap, mod);
|
||||
for (auto bit : selected_reps) {
|
||||
log_debug("Removing init bit on %s due to selections:\n", log_signal(bit.first));
|
||||
explain_selections(bit.second);
|
||||
initvals.remove_init(bit.first);
|
||||
changed++;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
struct AbstractPass : public Pass {
|
||||
AbstractPass() : Pass("abstract", "replace signals with abstract values during formal verification") { }
|
||||
void help() override {
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" abstract [mode] [options] [selection]\n");
|
||||
log("\n");
|
||||
log("Perform abstraction of signals within the design. Abstraction replaces a signal\n");
|
||||
log("with an unconstrained abstract value that can take an arbitrary concrete value\n");
|
||||
log("during formal verification. The mode and options control when a signal should\n");
|
||||
log("be abstracted and how it should affect FFs present in the design.\n");
|
||||
log("\n");
|
||||
log("Modes:");
|
||||
log("\n");
|
||||
log(" -state\n");
|
||||
log(" The selected FFs will be modified to load a new abstract value on every\n");
|
||||
log(" active clock edge, async reset or async load. This is independent of any\n");
|
||||
log(" clock enable that may be present on the FF cell. Conditional abstraction\n");
|
||||
log(" is supported with the -enable/-enabeln options. If present, the condition\n");
|
||||
log(" is sampled at the same time as the FF would smaple the correspnding data\n");
|
||||
log(" or async-data input whose value will be replaced with an abstract value.\n");
|
||||
log("\n");
|
||||
log(" The selection can be used to specify which state bits to abstract. For\n");
|
||||
log(" each selected wire, any state bits that the wire is driven by will be\n");
|
||||
log(" abstracted. For a selected FF cell, all of its state is abstracted.\n");
|
||||
log(" Individual bits of a single wire can be abtracted using the -slice and\n");
|
||||
log(" -rtlilslice options.\n");
|
||||
log("\n");
|
||||
log(" -init\n");
|
||||
log(" The selected FFs will be modified to have an abstract initial value.\n");
|
||||
log(" The -enable/-enablen options are not supported in this mode.\n");
|
||||
log(" \n");
|
||||
log(" The selection is used in the same way as it is for the -state mode.\n");
|
||||
log("\n");
|
||||
log(" -value\n");
|
||||
log(" The drivers of the selected signals will be replaced with an abstract\n");
|
||||
log(" value. In this mode, the abstract value can change at any time and is\n");
|
||||
log(" not synchronized to any clock or other signal. Conditional abstraction\n");
|
||||
log(" is supported with the -enable/-enablen options. The condition will\n");
|
||||
log(" combinationally select between the original driver and the abstract\n");
|
||||
log(" value.\n");
|
||||
log("\n");
|
||||
log(" The selection can be used to specify which output bits of which drivers\n");
|
||||
log(" to abtract. For a selected cell, all its output bits will be abstracted.\n");
|
||||
log(" For a selected wire, every output bit that is driving the wire will be\n");
|
||||
log(" abstracted. Individual bits of a single wire can be abstracted using the\n");
|
||||
log(" -slice and -rtlilslice options.\n");
|
||||
log("\n");
|
||||
log(" -enable <wire-name>\n");
|
||||
log(" -enablen <wire-name>\n");
|
||||
log(" Perform conditional abstraction with a named single bit wire as\n");
|
||||
log(" condition. For -enable the wire is used as an active-high condition and\n");
|
||||
log(" for -enablen as an active-low condition. See the description of the\n");
|
||||
log(" -state and -value modes for details on how the condition affects the\n");
|
||||
log(" abstractions performed by either mode. This option is not supported in\n");
|
||||
log(" the -init mode.\n");
|
||||
log("\n");
|
||||
log(" -slice <lhs>:<rhs>\n");
|
||||
log(" -slice <index>\n");
|
||||
log(" -rtlilslice <lhs>:<rhs>\n");
|
||||
log(" -rtlilslice <index>\n");
|
||||
log(" Limit the abstraction to a slice of a single selected wire. The targeted\n");
|
||||
log(" bits of the wire can be given as an inclusive range of indices or as a\n");
|
||||
log(" single index. When using the -slice option, the indices are interpreted\n");
|
||||
log(" following the source level declaration of the wire. This means the\n");
|
||||
log(" -slice option will respect declarations with a non-zero-based index range\n");
|
||||
log(" or with reversed bitorder. The -rtlilslice options will always use\n");
|
||||
log(" zero-based indexing where index 0 corresponds to the least significant\n");
|
||||
log(" bit of the wire.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
|
||||
log_header(design, "Executing ABSTRACT pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
enum Mode {
|
||||
None,
|
||||
State,
|
||||
Initial,
|
||||
Value,
|
||||
};
|
||||
Mode mode = Mode::None;
|
||||
enum Enable {
|
||||
Always = -1,
|
||||
ActiveLow = false, // ensuring we can use bool(enable)
|
||||
ActiveHigh = true,
|
||||
};
|
||||
Enable enable = Enable::Always;
|
||||
std::string enable_name;
|
||||
std::vector<Slice> slices;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-state") {
|
||||
mode = State;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-init") {
|
||||
mode = Initial;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-value") {
|
||||
mode = Value;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-enable" && argidx + 1 < args.size()) {
|
||||
if (enable != Enable::Always)
|
||||
log_cmd_error("Multiple enable condition are not supported\n");
|
||||
enable_name = args[++argidx];
|
||||
enable = Enable::ActiveHigh;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-enablen" && argidx + 1 < args.size()) {
|
||||
if (enable != Enable::Always)
|
||||
log_cmd_error("Multiple enable condition are not supported\n");
|
||||
enable_name = args[++argidx];
|
||||
enable = Enable::ActiveLow;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-slice" && argidx + 1 < args.size()) {
|
||||
slices.emplace_back(SliceIndices::HdlSlice, args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
if (arg == "-rtlilslice" && argidx + 1 < args.size()) {
|
||||
slices.emplace_back(SliceIndices::RtlilSlice, args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (enable != Enable::Always) {
|
||||
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");
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
if (mode == State)
|
||||
changed += abstract_state(mod, enable_logic, slices);
|
||||
else
|
||||
changed += abstract_value(mod, enable_logic, slices);
|
||||
}
|
||||
if (mode == State)
|
||||
log("Abstracted %d stateful cells.\n", changed);
|
||||
else
|
||||
log("Abstracted %d driver ports.\n", changed);
|
||||
} else if (mode == Initial) {
|
||||
for (auto mod : design->selected_modules()) {
|
||||
changed += abstract_init(mod, slices);
|
||||
}
|
||||
log("Abstracted %d init bits.\n", changed);
|
||||
} else {
|
||||
log_cmd_error("No mode selected, see help message\n");
|
||||
}
|
||||
}
|
||||
} AbstractPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -103,8 +103,7 @@ struct SplitcellsWorker
|
|||
|
||||
auto slice_signal = [&](SigSpec old_sig) -> SigSpec {
|
||||
SigSpec new_sig;
|
||||
for (int i = 0; i < GetSize(old_sig); i += GetSize(outsig)) {
|
||||
int offset = i+slice_lsb;
|
||||
for (int offset = slice_lsb; offset < GetSize(old_sig); offset += GetSize(outsig)) {
|
||||
int length = std::min(GetSize(old_sig)-offset, slice_msb-slice_lsb+1);
|
||||
new_sig.append(old_sig.extract(offset, length));
|
||||
}
|
||||
|
@ -134,7 +133,7 @@ struct SplitcellsWorker
|
|||
return GetSize(slices)-1;
|
||||
}
|
||||
|
||||
if (cell->type.in("$ff", "$dff", "$dffe", "$dffsr", "$dffsre", "$adff", "$adffe", "$aldffe",
|
||||
if (cell->type.in("$ff", "$dff", "$dffe", "$dffsr", "$dffsre", "$adff", "$adffe", "$aldff", "$aldffe",
|
||||
"$sdff", "$sdffce", "$sdffe", "$dlatch", "$dlatchsr", "$adlatch"))
|
||||
{
|
||||
auto splitports = {ID::D, ID::Q, ID::AD, ID::SET, ID::CLR};
|
||||
|
|
1
passes/opt/.gitignore
vendored
Normal file
1
passes/opt/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/peepopt*_pm.h
|
|
@ -22,4 +22,18 @@ OBJS += passes/opt/opt_lut_ins.o
|
|||
OBJS += passes/opt/opt_ffinv.o
|
||||
OBJS += passes/opt/pmux2shiftx.o
|
||||
OBJS += passes/opt/muxpack.o
|
||||
|
||||
OBJS += passes/opt/peepopt.o
|
||||
GENFILES += passes/opt/peepopt_pm.h
|
||||
passes/opt/peepopt.o: passes/opt/peepopt_pm.h
|
||||
$(eval $(call add_extra_objs,passes/opt/peepopt_pm.h))
|
||||
|
||||
PEEPOPT_PATTERN = passes/opt/peepopt_shiftmul_right.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_shiftmul_left.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_shiftadd.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_muldiv.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_formal_clockgateff.pmg
|
||||
|
||||
passes/opt/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
|
||||
$(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^)
|
||||
endif
|
||||
|
|
|
@ -601,7 +601,7 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool
|
|||
|
||||
std::vector<RTLIL::Cell*> delcells;
|
||||
for (auto cell : module->cells())
|
||||
if (cell->type.in(ID($pos), ID($_BUF_)) && !cell->has_keep_attr()) {
|
||||
if (cell->type.in(ID($pos), ID($_BUF_), ID($buf)) && !cell->has_keep_attr()) {
|
||||
bool is_signed = cell->type == ID($pos) && cell->getParam(ID::A_SIGNED).as_bool();
|
||||
RTLIL::SigSpec a = cell->getPort(ID::A);
|
||||
RTLIL::SigSpec y = cell->getPort(ID::Y);
|
||||
|
|
|
@ -1315,6 +1315,10 @@ skip_fine_alu:
|
|||
RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
|
||||
RTLIL::SigSpec sig_y(cell->type == ID($shiftx) ? RTLIL::State::Sx : RTLIL::State::S0, cell->getParam(ID::Y_WIDTH).as_int());
|
||||
|
||||
// Limit indexing to the size of a, which is behaviourally identical (result is all 0)
|
||||
// and avoids integer overflow of i + shift_bits when e.g. ID::B == INT_MAX
|
||||
shift_bits = min(shift_bits, GetSize(sig_a));
|
||||
|
||||
if (cell->type != ID($shiftx) && GetSize(sig_a) < GetSize(sig_y))
|
||||
sig_a.extend_u0(GetSize(sig_y), cell->getParam(ID::A_SIGNED).as_bool());
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ bool did_something;
|
|||
// scratchpad configurations for pmgen
|
||||
int shiftadd_max_ratio;
|
||||
|
||||
#include "passes/pmgen/peepopt_pm.h"
|
||||
#include "passes/opt/peepopt_pm.h"
|
||||
|
||||
struct PeepoptPass : public Pass {
|
||||
PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { }
|
|
@ -23,6 +23,7 @@
|
|||
#include "kernel/modtools.h"
|
||||
#include "kernel/utils.h"
|
||||
#include "kernel/macc.h"
|
||||
#include <iterator>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
@ -33,6 +34,7 @@ typedef std::pair<RTLIL::SigSpec, RTLIL::Const> ssc_pair_t;
|
|||
struct ShareWorkerConfig
|
||||
{
|
||||
int limit;
|
||||
size_t pattern_limit;
|
||||
bool opt_force;
|
||||
bool opt_aggressive;
|
||||
bool opt_fast;
|
||||
|
@ -87,7 +89,7 @@ struct ShareWorker
|
|||
queue_bits.clear();
|
||||
|
||||
for (auto &pbit : portbits) {
|
||||
if (pbit.cell->type == ID($mux) || pbit.cell->type == ID($pmux)) {
|
||||
if ((pbit.cell->type == ID($mux) || pbit.cell->type == ID($pmux)) && visited_cells.count(pbit.cell) == 0) {
|
||||
pool<RTLIL::SigBit> bits = modwalker.sigmap(pbit.cell->getPort(ID::S)).to_sigbit_pool();
|
||||
terminal_bits.insert(bits.begin(), bits.end());
|
||||
queue_bits.insert(bits.begin(), bits.end());
|
||||
|
@ -853,6 +855,23 @@ struct ShareWorker
|
|||
optimize_activation_patterns(patterns);
|
||||
}
|
||||
|
||||
template<typename Iterator>
|
||||
bool insert_capped(pool<ssc_pair_t>& cache, Iterator begin_pattern, Iterator end_pattern)
|
||||
{
|
||||
if (cache.size() + std::distance(begin_pattern, end_pattern) > config.pattern_limit) {
|
||||
cache.clear();
|
||||
cache.insert(ssc_pair_t());
|
||||
return false;
|
||||
} else {
|
||||
cache.insert(begin_pattern, end_pattern);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool insert_capped(pool<ssc_pair_t>& cache, ssc_pair_t pattern)
|
||||
{
|
||||
return insert_capped(cache, &pattern, &pattern + 1);
|
||||
}
|
||||
|
||||
const pool<ssc_pair_t> &find_cell_activation_patterns(RTLIL::Cell *cell, const char *indent)
|
||||
{
|
||||
if (recursion_state.count(cell)) {
|
||||
|
@ -909,20 +928,29 @@ struct ShareWorker
|
|||
for (int i = 0; i < GetSize(sig_s); i++)
|
||||
p.first.append(sig_s[i]), p.second.bits().push_back(RTLIL::State::S0);
|
||||
if (sort_check_activation_pattern(p))
|
||||
activation_patterns_cache[cell].insert(p);
|
||||
if (!insert_capped(activation_patterns_cache[cell], p)) {
|
||||
recursion_state.erase(cell);
|
||||
return activation_patterns_cache[cell];
|
||||
}
|
||||
}
|
||||
|
||||
for (int idx : used_in_b_parts)
|
||||
for (auto p : c_patterns) {
|
||||
p.first.append(sig_s[idx]), p.second.bits().push_back(RTLIL::State::S1);
|
||||
if (sort_check_activation_pattern(p))
|
||||
activation_patterns_cache[cell].insert(p);
|
||||
if (!insert_capped(activation_patterns_cache[cell], p)) {
|
||||
recursion_state.erase(cell);
|
||||
return activation_patterns_cache[cell];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto c : driven_cells) {
|
||||
const pool<ssc_pair_t> &c_patterns = find_cell_activation_patterns(c, indent);
|
||||
activation_patterns_cache[cell].insert(c_patterns.begin(), c_patterns.end());
|
||||
if (!insert_capped(activation_patterns_cache[cell], c_patterns.begin(), c_patterns.end())) {
|
||||
recursion_state.erase(cell);
|
||||
return activation_patterns_cache[cell];
|
||||
}
|
||||
}
|
||||
|
||||
log_assert(recursion_state.count(cell) != 0);
|
||||
|
@ -1438,12 +1466,18 @@ struct SharePass : public Pass {
|
|||
log(" -limit N\n");
|
||||
log(" Only perform the first N merges, then stop. This is useful for debugging.\n");
|
||||
log("\n");
|
||||
log(" -pattern-limit N\n");
|
||||
log(" Only analyze up to N activation patterns per cell, otherwise assume active.\n");
|
||||
log(" N is 1000 by default. Higher values may merge more resources at the cost of\n");
|
||||
log(" more runtime and memory consumption.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
ShareWorkerConfig config;
|
||||
|
||||
config.limit = -1;
|
||||
config.pattern_limit = design->scratchpad_get_int("share.pattern_limit", 1000);
|
||||
config.opt_force = false;
|
||||
config.opt_aggressive = false;
|
||||
config.opt_fast = false;
|
||||
|
@ -1508,6 +1542,10 @@ struct SharePass : public Pass {
|
|||
config.limit = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-pattern-limit" && argidx+1 < args.size()) {
|
||||
config.pattern_limit = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
|
|
@ -1,70 +1,10 @@
|
|||
%_pm.h: passes/pmgen/pmgen.py %.pmg
|
||||
$(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p $(subst _pm.h,,$(notdir $@)) $(filter-out $<,$^)
|
||||
passes/pmgen/%_pm.h: passes/pmgen/pmgen.py passes/pmgen/%.pmg
|
||||
$(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p $(notdir $*) $(filter-out $<,$^)
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
OBJS += passes/pmgen/test_pmgen.o
|
||||
GENFILES += passes/pmgen/test_pmgen_pm.h
|
||||
passes/pmgen/test_pmgen.o: passes/pmgen/test_pmgen_pm.h passes/pmgen/ice40_dsp_pm.h passes/pmgen/peepopt_pm.h passes/pmgen/xilinx_srl_pm.h
|
||||
passes/pmgen/test_pmgen.o: passes/pmgen/test_pmgen_pm.h techlibs/ice40/ice40_dsp_pm.h techlibs/xilinx/xilinx_srl_pm.h
|
||||
$(eval $(call add_extra_objs,passes/pmgen/test_pmgen_pm.h))
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
OBJS += passes/pmgen/ice40_dsp.o
|
||||
GENFILES += passes/pmgen/ice40_dsp_pm.h
|
||||
passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
|
||||
$(eval $(call add_extra_objs,passes/pmgen/ice40_dsp_pm.h))
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
OBJS += passes/pmgen/ice40_wrapcarry.o
|
||||
GENFILES += passes/pmgen/ice40_wrapcarry_pm.h
|
||||
passes/pmgen/ice40_wrapcarry.o: passes/pmgen/ice40_wrapcarry_pm.h
|
||||
$(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h))
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
OBJS += passes/pmgen/xilinx_dsp.o
|
||||
GENFILES += passes/pmgen/xilinx_dsp_pm.h
|
||||
GENFILES += passes/pmgen/xilinx_dsp48a_pm.h
|
||||
GENFILES += passes/pmgen/xilinx_dsp_CREG_pm.h
|
||||
GENFILES += passes/pmgen/xilinx_dsp_cascade_pm.h
|
||||
passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp48a_pm.h passes/pmgen/xilinx_dsp_CREG_pm.h passes/pmgen/xilinx_dsp_cascade_pm.h
|
||||
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_pm.h))
|
||||
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp48a_pm.h))
|
||||
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_CREG_pm.h))
|
||||
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h))
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
OBJS += passes/pmgen/microchip_dsp.o
|
||||
GENFILES += passes/pmgen/microchip_dsp_pm.h
|
||||
GENFILES += passes/pmgen/microchip_dsp_CREG_pm.h
|
||||
GENFILES += passes/pmgen/microchip_dsp_cascade_pm.h
|
||||
passes/pmgen/microchip_dsp.o: passes/pmgen/microchip_dsp_pm.h passes/pmgen/microchip_dsp_CREG_pm.h passes/pmgen/microchip_dsp_cascade_pm.h
|
||||
$(eval $(call add_extra_objs,passes/pmgen/microchip_dsp_pm.h))
|
||||
$(eval $(call add_extra_objs,passes/pmgen/microchip_dsp_CREG_pm.h))
|
||||
$(eval $(call add_extra_objs,passes/pmgen/microchip_dsp_cascade_pm.h))
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
OBJS += passes/pmgen/peepopt.o
|
||||
GENFILES += passes/pmgen/peepopt_pm.h
|
||||
passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h
|
||||
$(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h))
|
||||
|
||||
PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul_right.pmg
|
||||
PEEPOPT_PATTERN += passes/pmgen/peepopt_shiftmul_left.pmg
|
||||
PEEPOPT_PATTERN += passes/pmgen/peepopt_shiftadd.pmg
|
||||
PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
|
||||
PEEPOPT_PATTERN += passes/pmgen/peepopt_formal_clockgateff.pmg
|
||||
|
||||
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
|
||||
$(P) mkdir -p passes/pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^)
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
OBJS += passes/pmgen/xilinx_srl.o
|
||||
GENFILES += passes/pmgen/xilinx_srl_pm.h
|
||||
passes/pmgen/xilinx_srl.o: passes/pmgen/xilinx_srl_pm.h
|
||||
$(eval $(call add_extra_objs,passes/pmgen/xilinx_srl_pm.h))
|
||||
|
|
|
@ -22,7 +22,7 @@ list of cells from that module:
|
|||
foobar_pm pm(module, module->selected_cells());
|
||||
|
||||
The caller must make sure that none of the cells in the 2nd argument are
|
||||
deleted for as long as the patter matcher instance is used.
|
||||
deleted for as long as the pattern matcher instance is used.
|
||||
|
||||
At any time it is possible to disable cells, preventing them from showing
|
||||
up in any future matches:
|
||||
|
|
|
@ -24,8 +24,8 @@ USING_YOSYS_NAMESPACE
|
|||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#include "passes/pmgen/test_pmgen_pm.h"
|
||||
#include "passes/pmgen/ice40_dsp_pm.h"
|
||||
#include "passes/pmgen/xilinx_srl_pm.h"
|
||||
#include "techlibs/ice40/ice40_dsp_pm.h"
|
||||
#include "techlibs/xilinx/xilinx_srl_pm.h"
|
||||
|
||||
#include "generate.h"
|
||||
|
||||
|
|
|
@ -218,7 +218,7 @@ struct BoothPassWorker {
|
|||
|
||||
log_assert(cell->getParam(ID::A_SIGNED).as_bool() == cell->getParam(ID::B_SIGNED).as_bool());
|
||||
is_signed = cell->getParam(ID::A_SIGNED).as_bool();
|
||||
} else if (cell->type == ID($macc)) {
|
||||
} else if (cell->type.in(ID($macc), ID($macc_v2))) {
|
||||
Macc macc;
|
||||
macc.from_cell(cell);
|
||||
|
||||
|
|
|
@ -403,7 +403,7 @@ struct MaccmapPass : public Pass {
|
|||
|
||||
for (auto mod : design->selected_modules())
|
||||
for (auto cell : mod->selected_cells())
|
||||
if (cell->type == ID($macc)) {
|
||||
if (cell->type.in(ID($macc), ID($macc_v2))) {
|
||||
log("Mapping %s.%s (%s).\n", log_id(mod), log_id(cell), log_id(cell->type));
|
||||
maccmap(mod, cell, unmap_mode);
|
||||
mod->remove(cell);
|
||||
|
|
|
@ -42,6 +42,14 @@ void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell)
|
|||
}
|
||||
}
|
||||
|
||||
void simplemap_buf(RTLIL::Module *module, RTLIL::Cell *cell)
|
||||
{
|
||||
RTLIL::SigSpec sig_a = cell->getPort(ID::A);
|
||||
RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
|
||||
|
||||
module->connect(RTLIL::SigSig(sig_y, sig_a));
|
||||
}
|
||||
|
||||
void simplemap_pos(RTLIL::Module *module, RTLIL::Cell *cell)
|
||||
{
|
||||
RTLIL::SigSpec sig_a = cell->getPort(ID::A);
|
||||
|
@ -411,6 +419,7 @@ void simplemap_get_mappers(dict<IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)>
|
|||
{
|
||||
mappers[ID($not)] = simplemap_not;
|
||||
mappers[ID($pos)] = simplemap_pos;
|
||||
mappers[ID($buf)] = simplemap_buf;
|
||||
mappers[ID($and)] = simplemap_bitop;
|
||||
mappers[ID($or)] = simplemap_bitop;
|
||||
mappers[ID($xor)] = simplemap_bitop;
|
||||
|
|
|
@ -554,8 +554,8 @@ struct TechmapWorker
|
|||
|
||||
if (extmapper_name == "maccmap") {
|
||||
log("Creating %s with maccmap.\n", log_id(extmapper_module));
|
||||
if (extmapper_cell->type != ID($macc))
|
||||
log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(extmapper_cell->type));
|
||||
if (!extmapper_cell->type.in(ID($macc), ID($macc_v2)))
|
||||
log_error("The maccmap mapper can only map $macc/$macc_v2 (not %s) cells!\n", log_id(extmapper_cell->type));
|
||||
maccmap(extmapper_module, extmapper_cell);
|
||||
extmapper_module->remove(extmapper_cell);
|
||||
}
|
||||
|
@ -600,8 +600,8 @@ struct TechmapWorker
|
|||
}
|
||||
|
||||
if (extmapper_name == "maccmap") {
|
||||
if (cell->type != ID($macc))
|
||||
log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(cell->type));
|
||||
if (!cell->type.in(ID($macc), ID($macc_v2)))
|
||||
log_error("The maccmap mapper can only map $macc/$macc_v2 (not %s) cells!\n", log_id(cell->type));
|
||||
maccmap(module, cell);
|
||||
}
|
||||
|
||||
|
|
1
techlibs/.gitignore
vendored
1
techlibs/.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
blackbox.v
|
||||
*_pm.h
|
||||
|
|
|
@ -1207,6 +1207,120 @@ end
|
|||
|
||||
endmodule
|
||||
|
||||
// --------------------------------------------------------
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
//-
|
||||
//- $macc_v2 (A, B, C, Y)
|
||||
//* group arith
|
||||
//-
|
||||
//- Multiply and add.
|
||||
//- This cell represents a generic fused multiply-add operation, it supersedes the
|
||||
//- earlier $macc cell.
|
||||
//-
|
||||
module \$macc_v2 (A, B, C, Y);
|
||||
|
||||
parameter NPRODUCTS = 0;
|
||||
parameter NADDENDS = 0;
|
||||
parameter A_WIDTHS = 16'h0000;
|
||||
parameter B_WIDTHS = 16'h0000;
|
||||
parameter C_WIDTHS = 16'h0000;
|
||||
parameter Y_WIDTH = 0;
|
||||
|
||||
parameter PRODUCT_NEGATED = 1'bx;
|
||||
parameter ADDEND_NEGATED = 1'bx;
|
||||
parameter A_SIGNED = 1'bx;
|
||||
parameter B_SIGNED = 1'bx;
|
||||
parameter C_SIGNED = 1'bx;
|
||||
|
||||
function integer sum_widths1;
|
||||
input [(16*NPRODUCTS)-1:0] widths;
|
||||
integer i;
|
||||
begin
|
||||
sum_widths1 = 0;
|
||||
for (i = 0; i < NPRODUCTS; i++) begin
|
||||
sum_widths1 = sum_widths1 + widths[16*i+:16];
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
|
||||
function integer sum_widths2;
|
||||
input [(16*NADDENDS)-1:0] widths;
|
||||
integer i;
|
||||
begin
|
||||
sum_widths2 = 0;
|
||||
for (i = 0; i < NADDENDS; i++) begin
|
||||
sum_widths2 = sum_widths2 + widths[16*i+:16];
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
|
||||
input [sum_widths1(A_WIDTHS)-1:0] A; // concatenation of LHS factors
|
||||
input [sum_widths1(B_WIDTHS)-1:0] B; // concatenation of RHS factors
|
||||
input [sum_widths2(C_WIDTHS)-1:0] C; // concatenation of summands
|
||||
output reg [Y_WIDTH-1:0] Y; // output sum
|
||||
|
||||
integer i, j, ai, bi, ci, aw, bw, cw;
|
||||
reg [Y_WIDTH-1:0] product;
|
||||
reg [Y_WIDTH-1:0] addend, oper_a, oper_b;
|
||||
|
||||
always @* begin
|
||||
Y = 0;
|
||||
ai = 0;
|
||||
bi = 0;
|
||||
for (i = 0; i < NPRODUCTS; i = i+1)
|
||||
begin
|
||||
aw = A_WIDTHS[16*i+:16];
|
||||
bw = B_WIDTHS[16*i+:16];
|
||||
|
||||
oper_a = 0;
|
||||
oper_b = 0;
|
||||
for (j = 0; j < Y_WIDTH && j < aw; j = j + 1)
|
||||
oper_a[j] = A[ai + j];
|
||||
for (j = 0; j < Y_WIDTH && j < bw; j = j + 1)
|
||||
oper_b[j] = B[bi + j];
|
||||
// A_SIGNED[i] == B_SIGNED[i] as RTLIL invariant
|
||||
if (A_SIGNED[i] && B_SIGNED[i]) begin
|
||||
for (j = aw; j > 0 && j < Y_WIDTH; j = j + 1)
|
||||
oper_a[j] = oper_a[j - 1];
|
||||
for (j = bw; j > 0 && j < Y_WIDTH; j = j + 1)
|
||||
oper_b[j] = oper_b[j - 1];
|
||||
end
|
||||
|
||||
product = oper_a * oper_b;
|
||||
|
||||
if (PRODUCT_NEGATED[i])
|
||||
Y = Y - product;
|
||||
else
|
||||
Y = Y + product;
|
||||
|
||||
ai = ai + aw;
|
||||
bi = bi + bw;
|
||||
end
|
||||
|
||||
ci = 0;
|
||||
for (i = 0; i < NADDENDS; i = i+1)
|
||||
begin
|
||||
cw = C_WIDTHS[16*i+:16];
|
||||
|
||||
addend = 0;
|
||||
for (j = 0; j < Y_WIDTH && j < cw; j = j + 1)
|
||||
addend[j] = C[ci + j];
|
||||
if (C_SIGNED[i]) begin
|
||||
for (j = cw; j > 0 && j < Y_WIDTH; j = j + 1)
|
||||
addend[j] = addend[j - 1];
|
||||
end
|
||||
|
||||
if (ADDEND_NEGATED[i])
|
||||
Y = Y - addend;
|
||||
else
|
||||
Y = Y + addend;
|
||||
|
||||
ci = ci + cw;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
// --------------------------------------------------------
|
||||
//* ver 2
|
||||
//* title Divider
|
||||
|
|
|
@ -59,7 +59,7 @@ module _90_simplemap_compare_ops;
|
|||
endmodule
|
||||
|
||||
(* techmap_simplemap *)
|
||||
(* techmap_celltype = "$pos $slice $concat $mux $tribuf $bmux $bwmux $bweqx" *)
|
||||
(* techmap_celltype = "$buf $pos $slice $concat $mux $tribuf $bmux $bwmux $bweqx" *)
|
||||
module _90_simplemap_various;
|
||||
endmodule
|
||||
|
||||
|
@ -290,7 +290,7 @@ module _90_alu (A, B, CI, BI, X, Y, CO);
|
|||
endmodule
|
||||
|
||||
(* techmap_maccmap *)
|
||||
(* techmap_celltype = "$macc" *)
|
||||
(* techmap_celltype = "$macc $macc_v2" *)
|
||||
module _90_macc;
|
||||
endmodule
|
||||
|
||||
|
|
|
@ -1005,7 +1005,7 @@ always @* begin
|
|||
C = I0;
|
||||
end
|
||||
MULT: begin
|
||||
S = I0 & I1;
|
||||
S = (I0 & I1) ^ I3;
|
||||
C = I0 & I1;
|
||||
end
|
||||
endcase
|
||||
|
|
|
@ -14,3 +14,13 @@ $(eval $(call add_share_file,share/ice40,techlibs/ice40/spram.txt))
|
|||
$(eval $(call add_share_file,share/ice40,techlibs/ice40/spram_map.v))
|
||||
$(eval $(call add_share_file,share/ice40,techlibs/ice40/dsp_map.v))
|
||||
$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc9_model.v))
|
||||
|
||||
OBJS += techlibs/ice40/ice40_dsp.o
|
||||
GENFILES += techlibs/ice40/ice40_dsp_pm.h
|
||||
techlibs/ice40/ice40_dsp.o: techlibs/ice40/ice40_dsp_pm.h
|
||||
$(eval $(call add_extra_objs,techlibs/ice40/ice40_dsp_pm.h))
|
||||
|
||||
OBJS += techlibs/ice40/ice40_wrapcarry.o
|
||||
GENFILES += techlibs/ice40/ice40_wrapcarry_pm.h
|
||||
techlibs/ice40/ice40_wrapcarry.o: techlibs/ice40/ice40_wrapcarry_pm.h
|
||||
$(eval $(call add_extra_objs,techlibs/ice40/ice40_wrapcarry_pm.h))
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#include "passes/pmgen/ice40_dsp_pm.h"
|
||||
#include "techlibs/ice40/ice40_dsp_pm.h"
|
||||
|
||||
void create_ice40_dsp(ice40_dsp_pm &pm)
|
||||
{
|
|
@ -23,7 +23,7 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#include "passes/pmgen/ice40_wrapcarry_pm.h"
|
||||
#include "techlibs/ice40/ice40_wrapcarry_pm.h"
|
||||
|
||||
void create_ice40_wrapcarry(ice40_wrapcarry_pm &pm)
|
||||
{
|
|
@ -63,10 +63,10 @@ module \$__M9K_ALTSYNCRAM_SINGLEPORT_FULL (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1A
|
|||
.width_byteena_a (1), // Forced value
|
||||
.numwords_b ( NUMWORDS ),
|
||||
.numwords_a ( NUMWORDS ),
|
||||
.widthad_b ( CFG_DBITS ),
|
||||
.width_b ( CFG_ABITS ),
|
||||
.widthad_a ( CFG_DBITS ),
|
||||
.width_a ( CFG_ABITS )
|
||||
.widthad_b ( CFG_ABITS ),
|
||||
.width_b ( CFG_DBITS ),
|
||||
.widthad_a ( CFG_ABITS ),
|
||||
.width_a ( CFG_DBITS )
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.data_a(B1DATA),
|
||||
.address_a(B1ADDR),
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
* Copyright (C) 2024 Richard Herveille <richard.herveille@roalogic.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -289,4 +290,56 @@ module fiftyfivenm_pll
|
|||
output vcooverrange;
|
||||
output vcounderrange;
|
||||
|
||||
endmodule // cycloneive_pll
|
||||
endmodule // max10_pll
|
||||
|
||||
|
||||
/* MAX10 MULT clearbox model */
|
||||
(* blackbox *)
|
||||
module fiftyfivenm_mac_mult (
|
||||
dataa,
|
||||
datab,
|
||||
dataout,
|
||||
signa,
|
||||
signb,
|
||||
|
||||
aclr,
|
||||
clk,
|
||||
ena
|
||||
);
|
||||
parameter dataa_clock = "none";
|
||||
parameter dataa_width = 18;
|
||||
parameter datab_clock = "none";
|
||||
parameter datab_width = 18;
|
||||
parameter signa_clock = "none";
|
||||
parameter signb_clock = "none";
|
||||
parameter lpm_type = "fiftyfivenm_mac_mult";
|
||||
|
||||
input [dataa_width -1:0] dataa;
|
||||
input [datab_width -1:0] datab;
|
||||
output [(dataa_width+datab_width)-1:0] dataout;
|
||||
input signa;
|
||||
input signb;
|
||||
input aclr;
|
||||
input clk;
|
||||
input ena;
|
||||
endmodule //fiftyfivenm_mac_mult
|
||||
|
||||
module fiftyfivenm_mac_out (
|
||||
dataa,
|
||||
dataout,
|
||||
|
||||
aclr,
|
||||
clk,
|
||||
ena
|
||||
);
|
||||
|
||||
parameter dataa_width = 38;
|
||||
parameter output_clock = "none";
|
||||
parameter lpm_type = "fiftyfivenm_mac_out";
|
||||
|
||||
input [dataa_width-1:0] dataa;
|
||||
output [dataa_width-1:0] dataout;
|
||||
input aclr;
|
||||
input clk;
|
||||
input ena;
|
||||
endmodule //fiftyfivenm_mac_out
|
||||
|
|
73
techlibs/intel/max10/dsp_map.v
Normal file
73
techlibs/intel/max10/dsp_map.v
Normal file
|
@ -0,0 +1,73 @@
|
|||
module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y);
|
||||
parameter A_SIGNED = 0;
|
||||
parameter B_SIGNED = 0;
|
||||
parameter A_WIDTH = 0;
|
||||
parameter B_WIDTH = 0;
|
||||
parameter Y_WIDTH = 0;
|
||||
|
||||
wire [A_WIDTH+B_WIDTH-1:0] mult_result;
|
||||
|
||||
fiftyfivenm_mac_mult #(
|
||||
.dataa_clock ("none"),
|
||||
.datab_clock ("none"),
|
||||
.signa_clock ("none"),
|
||||
.signb_clock ("none"),
|
||||
.dataa_width (A_WIDTH),
|
||||
.datab_width (B_WIDTH),
|
||||
.lpm_type ("fiftyfivenm_mac_mult")
|
||||
) _TECHMAP_REPLACE_mac_mult (
|
||||
//Data path
|
||||
.dataa ( A ),
|
||||
.datab ( B ),
|
||||
.dataout( mult_result ),
|
||||
.signa ( A_SIGNED != 0 ? 1'b1 : 1'b0),
|
||||
.signb ( B_SIGNED != 0 ? 1'b1 : 1'b0)
|
||||
);
|
||||
|
||||
fiftyfivenm_mac_out #(
|
||||
.dataa_width (A_WIDTH + B_WIDTH),
|
||||
.output_clock ("none"),
|
||||
.lpm_type ("fiftyfivenm_mac_out")
|
||||
) _TECHMAP_REPLACE_mac_out (
|
||||
.dataa (mult_result),
|
||||
.dataout (Y)
|
||||
);
|
||||
endmodule
|
||||
|
||||
|
||||
module \$__MUL9X9 (input [8:0] A, input [8:0] B, output [17:0] Y);
|
||||
parameter A_SIGNED = 0;
|
||||
parameter B_SIGNED = 0;
|
||||
parameter A_WIDTH = 0;
|
||||
parameter B_WIDTH = 0;
|
||||
parameter Y_WIDTH = 0;
|
||||
|
||||
wire [A_WIDTH+B_WIDTH-1:0] mult_result;
|
||||
|
||||
fiftyfivenm_mac_mult #(
|
||||
.dataa_clock ("none"),
|
||||
.datab_clock ("none"),
|
||||
.signa_clock ("none"),
|
||||
.signb_clock ("none"),
|
||||
.dataa_width (A_WIDTH),
|
||||
.datab_width (B_WIDTH),
|
||||
.lpm_type ("fiftyfivenm_mac_mult")
|
||||
) _TECHMAP_REPLACE_mac_mult (
|
||||
//Data path
|
||||
.dataa ( A ),
|
||||
.datab ( B ),
|
||||
.dataout( mult_result ),
|
||||
.signa ( A_SIGNED != 0 ? 1'b1 : 1'b0),
|
||||
.signb ( B_SIGNED != 0 ? 1'b1 : 1'b0)
|
||||
);
|
||||
|
||||
fiftyfivenm_mac_out #(
|
||||
.dataa_width (A_WIDTH + B_WIDTH),
|
||||
.output_clock ("none"),
|
||||
.lpm_type ("fiftyfivenm_mac_out")
|
||||
) _TECHMAP_REPLACE_mac_out (
|
||||
.dataa (mult_result),
|
||||
.dataout (Y)
|
||||
);
|
||||
endmodule
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
* Copyright (C) 2024 Richard Herveille <richard.herveille@roalogic.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -62,12 +63,19 @@ struct SynthIntelPass : public ScriptPass {
|
|||
log(" from label is synonymous to 'begin', and empty to label is\n");
|
||||
log(" synonymous to the end of the command list.\n");
|
||||
log("\n");
|
||||
log(" -dff\n");
|
||||
log(" pass DFFs to ABC to perform sequential logic optimisations\n");
|
||||
log(" (EXPERIMENTAL)\n");
|
||||
log("\n");
|
||||
log(" -iopads\n");
|
||||
log(" use IO pad cells in output netlist\n");
|
||||
log("\n");
|
||||
log(" -nobram\n");
|
||||
log(" do not use block RAM cells in output netlist\n");
|
||||
log("\n");
|
||||
log(" -nodsp\n");
|
||||
log(" do not map multipliers to MUL18/MUL9 cells\n");
|
||||
log("\n");
|
||||
log(" -noflatten\n");
|
||||
log(" do not flatten design before synthesis\n");
|
||||
log("\n");
|
||||
|
@ -80,7 +88,7 @@ struct SynthIntelPass : public ScriptPass {
|
|||
}
|
||||
|
||||
string top_opt, family_opt, vout_file, blif_file;
|
||||
bool retime, flatten, nobram, iopads;
|
||||
bool retime, flatten, nobram, dff, nodsp, iopads;
|
||||
|
||||
void clear_flags() override
|
||||
{
|
||||
|
@ -91,6 +99,8 @@ struct SynthIntelPass : public ScriptPass {
|
|||
retime = false;
|
||||
flatten = true;
|
||||
nobram = false;
|
||||
dff = false;
|
||||
nodsp = false;
|
||||
iopads = false;
|
||||
}
|
||||
|
||||
|
@ -130,6 +140,14 @@ struct SynthIntelPass : public ScriptPass {
|
|||
iopads = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-dff") {
|
||||
dff = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nodsp") {
|
||||
nodsp = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nobram") {
|
||||
nobram = true;
|
||||
continue;
|
||||
|
@ -178,15 +196,42 @@ struct SynthIntelPass : public ScriptPass {
|
|||
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
|
||||
}
|
||||
|
||||
if (flatten && check_label("flatten", "(unless -noflatten)")) {
|
||||
run("proc");
|
||||
run("flatten");
|
||||
run("tribuf -logic");
|
||||
run("deminout");
|
||||
}
|
||||
|
||||
if (check_label("coarse")) {
|
||||
run("synth -run coarse");
|
||||
run("proc");
|
||||
if (flatten || help_mode)
|
||||
run("flatten", "(skip if -noflatten)");
|
||||
run("tribuf -logic");
|
||||
run("deminout");
|
||||
run("opt_expr");
|
||||
run("opt_clean");
|
||||
run("check");
|
||||
run("opt -nodffe -nosdff");
|
||||
run("fsm");
|
||||
run("opt");
|
||||
run("wreduce");
|
||||
run("peepopt");
|
||||
run("opt_clean");
|
||||
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
|
||||
run("opt_expr");
|
||||
run("opt_clean");
|
||||
|
||||
if (help_mode) {
|
||||
run("techmap -map +mul2dsp.v [...]", "(unless -nodsp)");
|
||||
} else if (!nodsp) {
|
||||
run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=10 -D DSP_B_MINWIDTH=4 -D DSP_NAME=$__MUL18X18");
|
||||
run("chtype -set $mul t:$__soft_mul");
|
||||
run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=4 -D DSP_B_MINWIDTH=10 -D DSP_NAME=$__MUL18X18");
|
||||
run("chtype -set $mul t:$__soft_mul");
|
||||
run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=9 -D DSP_B_MAXWIDTH=9 -D DSP_A_MINWIDTH=4 -D DSP_B_MINWIDTH=4 -D DSP_NAME=$__MUL9X9");
|
||||
run("chtype -set $mul t:$__soft_mul");
|
||||
run("alumacc");
|
||||
run(stringf("techmap -map +/intel/%s/dsp_map.v", family_opt.c_str()));
|
||||
} else {
|
||||
run("alumacc");
|
||||
}
|
||||
run("opt");
|
||||
run("memory -nomap");
|
||||
run("opt_clean");
|
||||
}
|
||||
|
||||
if (!nobram && check_label("map_bram", "(skip if -nobram)")) {
|
||||
|
@ -219,7 +264,10 @@ struct SynthIntelPass : public ScriptPass {
|
|||
}
|
||||
|
||||
if (check_label("map_luts")) {
|
||||
run("abc -lut 4" + string(retime ? " -dff" : ""));
|
||||
run("abc9 -lut 4 -W 300" + string(dff ? " -dff" : ""));
|
||||
run("clean");
|
||||
run("opt -fast");
|
||||
run("autoname");
|
||||
run("clean");
|
||||
}
|
||||
|
||||
|
|
|
@ -29,3 +29,12 @@ $(eval $(call add_share_file,share/microchip,techlibs/microchip/LSRAM_map.v))
|
|||
$(eval $(call add_share_file,share/microchip,techlibs/microchip/LSRAM.txt))
|
||||
$(eval $(call add_share_file,share/microchip,techlibs/microchip/uSRAM_map.v))
|
||||
$(eval $(call add_share_file,share/microchip,techlibs/microchip/uSRAM.txt))
|
||||
|
||||
OBJS += techlibs/microchip/microchip_dsp.o
|
||||
GENFILES += techlibs/microchip/microchip_dsp_pm.h
|
||||
GENFILES += techlibs/microchip/microchip_dsp_CREG_pm.h
|
||||
GENFILES += techlibs/microchip/microchip_dsp_cascade_pm.h
|
||||
techlibs/microchip/microchip_dsp.o: techlibs/microchip/microchip_dsp_pm.h techlibs/microchip/microchip_dsp_CREG_pm.h techlibs/microchip/microchip_dsp_cascade_pm.h
|
||||
$(eval $(call add_extra_objs,techlibs/microchip/microchip_dsp_pm.h))
|
||||
$(eval $(call add_extra_objs,techlibs/microchip/microchip_dsp_CREG_pm.h))
|
||||
$(eval $(call add_extra_objs,techlibs/microchip/microchip_dsp_cascade_pm.h))
|
||||
|
|
|
@ -23,9 +23,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#include "passes/pmgen/microchip_dsp_CREG_pm.h"
|
||||
#include "passes/pmgen/microchip_dsp_cascade_pm.h"
|
||||
#include "passes/pmgen/microchip_dsp_pm.h"
|
||||
#include "techlibs/microchip/microchip_dsp_CREG_pm.h"
|
||||
#include "techlibs/microchip/microchip_dsp_cascade_pm.h"
|
||||
#include "techlibs/microchip/microchip_dsp_pm.h"
|
||||
|
||||
void microchip_dsp_pack(microchip_dsp_pm &pm)
|
||||
{
|
|
@ -6,6 +6,7 @@ OBJS += techlibs/quicklogic/ql_bram_merge.o
|
|||
OBJS += techlibs/quicklogic/ql_bram_types.o
|
||||
OBJS += techlibs/quicklogic/ql_dsp_simd.o
|
||||
OBJS += techlibs/quicklogic/ql_dsp_io_regs.o
|
||||
OBJS += techlibs/quicklogic/ql_ioff.o
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
|
@ -40,4 +41,4 @@ $(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf
|
|||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/dsp_final_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/TDP18K_FIFO.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/ufifo_ctl.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/sram1024x18_mem.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/sram1024x18_mem.v))
|
||||
|
|
125
techlibs/quicklogic/ql_ioff.cc
Normal file
125
techlibs/quicklogic/ql_ioff.cc
Normal file
|
@ -0,0 +1,125 @@
|
|||
#include "kernel/log.h"
|
||||
#include "kernel/modtools.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/rtlil.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct QlIoffPass : public Pass {
|
||||
QlIoffPass() : Pass("ql_ioff", "Infer I/O FFs for qlf_k6n10f architecture") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" ql_ioff [selection]\n");
|
||||
log("\n");
|
||||
log("This pass promotes qlf_k6n10f registers directly connected to a top-level I/O\n");
|
||||
log("port to I/O FFs.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string>, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing QL_IOFF pass.\n");
|
||||
|
||||
ModWalker modwalker(design);
|
||||
Module *module = design->top_module();
|
||||
if (!module)
|
||||
return;
|
||||
modwalker.setup(module);
|
||||
pool<RTLIL::Cell *> input_ffs;
|
||||
dict<RTLIL::Wire *, std::vector<Cell*>> output_ffs;
|
||||
dict<SigBit, pool<SigBit>> output_bit_aliases;
|
||||
|
||||
for (Wire* wire : module->wires())
|
||||
if (wire->port_output) {
|
||||
output_ffs[wire].resize(wire->width, nullptr);
|
||||
for (SigBit bit : SigSpec(wire))
|
||||
output_bit_aliases[modwalker.sigmap(bit)].insert(bit);
|
||||
}
|
||||
|
||||
for (auto cell : module->selected_cells()) {
|
||||
if (cell->type.in(ID(dffsre), ID(sdffsre))) {
|
||||
log_debug("Checking cell %s.\n", cell->name.c_str());
|
||||
bool e_const = cell->getPort(ID::E).is_fully_ones();
|
||||
bool r_const = cell->getPort(ID::R).is_fully_ones();
|
||||
bool s_const = cell->getPort(ID::S).is_fully_ones();
|
||||
|
||||
if (!(e_const && r_const && s_const)) {
|
||||
log_debug("not promoting: E, R, or S is used\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
SigSpec d = cell->getPort(ID::D);
|
||||
log_assert(GetSize(d) == 1);
|
||||
if (modwalker.has_inputs(d)) {
|
||||
log_debug("Cell %s is potentially eligible for promotion to input IOFF.\n", cell->name.c_str());
|
||||
// check that d_sig has no other consumers
|
||||
pool<ModWalker::PortBit> portbits;
|
||||
modwalker.get_consumers(portbits, d);
|
||||
if (GetSize(portbits) > 1) {
|
||||
log_debug("not promoting: D has other consumers\n");
|
||||
continue;
|
||||
}
|
||||
input_ffs.insert(cell);
|
||||
continue; // prefer input FFs over output FFs
|
||||
}
|
||||
|
||||
SigSpec q = cell->getPort(ID::Q);
|
||||
log_assert(GetSize(q) == 1);
|
||||
if (modwalker.has_outputs(q) && !modwalker.has_consumers(q)) {
|
||||
log_debug("Cell %s is potentially eligible for promotion to output IOFF.\n", cell->name.c_str());
|
||||
for (SigBit bit : output_bit_aliases[modwalker.sigmap(q)]) {
|
||||
log_assert(bit.is_wire());
|
||||
output_ffs[bit.wire][bit.offset] = cell;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto cell : input_ffs) {
|
||||
log("Promoting register %s to input IOFF.\n", log_signal(cell->getPort(ID::Q)));
|
||||
cell->type = ID(dff);
|
||||
cell->unsetPort(ID::E);
|
||||
cell->unsetPort(ID::R);
|
||||
cell->unsetPort(ID::S);
|
||||
}
|
||||
for (auto & [old_port_output, ioff_cells] : output_ffs) {
|
||||
if (std::any_of(ioff_cells.begin(), ioff_cells.end(), [](Cell * c) { return c != nullptr; }))
|
||||
{
|
||||
// create replacement output wire
|
||||
RTLIL::Wire* new_port_output = module->addWire(NEW_ID, old_port_output->width);
|
||||
new_port_output->start_offset = old_port_output->start_offset;
|
||||
module->swap_names(old_port_output, new_port_output);
|
||||
std::swap(old_port_output->port_id, new_port_output->port_id);
|
||||
std::swap(old_port_output->port_input, new_port_output->port_input);
|
||||
std::swap(old_port_output->port_output, new_port_output->port_output);
|
||||
std::swap(old_port_output->upto, new_port_output->upto);
|
||||
std::swap(old_port_output->is_signed, new_port_output->is_signed);
|
||||
std::swap(old_port_output->attributes, new_port_output->attributes);
|
||||
|
||||
// create new output FFs
|
||||
SigSpec sig_o(old_port_output);
|
||||
SigSpec sig_n(new_port_output);
|
||||
for (int i = 0; i < new_port_output->width; i++) {
|
||||
if (ioff_cells[i]) {
|
||||
log("Promoting %s to output IOFF.\n", log_signal(sig_n[i]));
|
||||
|
||||
RTLIL::Cell *new_cell = module->addCell(NEW_ID, ID(dff));
|
||||
new_cell->setPort(ID::C, ioff_cells[i]->getPort(ID::C));
|
||||
new_cell->setPort(ID::D, ioff_cells[i]->getPort(ID::D));
|
||||
new_cell->setPort(ID::Q, sig_n[i]);
|
||||
new_cell->set_bool_attribute(ID::keep);
|
||||
} else {
|
||||
module->connect(sig_n[i], sig_o[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} QlIoffPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -78,7 +78,7 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
}
|
||||
|
||||
string top_opt, blif_file, edif_file, family, currmodule, verilog_file, lib_path;
|
||||
bool abc9, inferAdder, nobram, bramTypes, dsp;
|
||||
bool abc9, inferAdder, nobram, bramTypes, dsp, ioff;
|
||||
|
||||
void clear_flags() override
|
||||
{
|
||||
|
@ -94,6 +94,7 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
bramTypes = false;
|
||||
lib_path = "+/quicklogic/";
|
||||
dsp = true;
|
||||
ioff = true;
|
||||
}
|
||||
|
||||
void set_scratchpad_defaults(RTLIL::Design *design) {
|
||||
|
@ -158,6 +159,10 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
dsp = false;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-noioff") {
|
||||
ioff = false;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
@ -328,6 +333,13 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
run("clean");
|
||||
run("opt_lut");
|
||||
}
|
||||
|
||||
if (check_label("iomap", "(for qlf_k6n10f, skip if -noioff)") && (family == "qlf_k6n10f" || help_mode)) {
|
||||
if (ioff || help_mode) {
|
||||
run("ql_ioff");
|
||||
run("opt_clean");
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("check")) {
|
||||
run("autoname");
|
||||
|
|
|
@ -46,3 +46,19 @@ $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_dsp_map.v))
|
|||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xcu_dsp_map.v))
|
||||
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc9_model.v))
|
||||
|
||||
OBJS += techlibs/xilinx/xilinx_dsp.o
|
||||
GENFILES += techlibs/xilinx/xilinx_dsp_pm.h
|
||||
GENFILES += techlibs/xilinx/xilinx_dsp48a_pm.h
|
||||
GENFILES += techlibs/xilinx/xilinx_dsp_CREG_pm.h
|
||||
GENFILES += techlibs/xilinx/xilinx_dsp_cascade_pm.h
|
||||
techlibs/xilinx/xilinx_dsp.o: techlibs/xilinx/xilinx_dsp_pm.h techlibs/xilinx/xilinx_dsp48a_pm.h techlibs/xilinx/xilinx_dsp_CREG_pm.h techlibs/xilinx/xilinx_dsp_cascade_pm.h
|
||||
$(eval $(call add_extra_objs,techlibs/xilinx/xilinx_dsp_pm.h))
|
||||
$(eval $(call add_extra_objs,techlibs/xilinx/xilinx_dsp48a_pm.h))
|
||||
$(eval $(call add_extra_objs,techlibs/xilinx/xilinx_dsp_CREG_pm.h))
|
||||
$(eval $(call add_extra_objs,techlibs/xilinx/xilinx_dsp_cascade_pm.h))
|
||||
|
||||
OBJS += techlibs/xilinx/xilinx_srl.o
|
||||
GENFILES += techlibs/xilinx/xilinx_srl_pm.h
|
||||
techlibs/xilinx/xilinx_srl.o: techlibs/xilinx/xilinx_srl_pm.h
|
||||
$(eval $(call add_extra_objs,techlibs/xilinx/xilinx_srl_pm.h))
|
||||
|
|
|
@ -25,10 +25,10 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#include "passes/pmgen/xilinx_dsp_pm.h"
|
||||
#include "passes/pmgen/xilinx_dsp48a_pm.h"
|
||||
#include "passes/pmgen/xilinx_dsp_CREG_pm.h"
|
||||
#include "passes/pmgen/xilinx_dsp_cascade_pm.h"
|
||||
#include "techlibs/xilinx/xilinx_dsp_pm.h"
|
||||
#include "techlibs/xilinx/xilinx_dsp48a_pm.h"
|
||||
#include "techlibs/xilinx/xilinx_dsp_CREG_pm.h"
|
||||
#include "techlibs/xilinx/xilinx_dsp_cascade_pm.h"
|
||||
|
||||
static Cell* addDsp(Module *module) {
|
||||
Cell *cell = module->addCell(NEW_ID, ID(DSP48E1));
|
|
@ -24,7 +24,7 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#include "passes/pmgen/xilinx_srl_pm.h"
|
||||
#include "techlibs/xilinx/xilinx_srl_pm.h"
|
||||
|
||||
void run_fixed(xilinx_srl_pm &pm)
|
||||
{
|
61
tests/alumacc/basic.ys
Normal file
61
tests/alumacc/basic.ys
Normal file
|
@ -0,0 +1,61 @@
|
|||
read_verilog <<EOF
|
||||
module gate(input signed [2:0] a1, input signed [2:0] b1,
|
||||
input [1:0] a2, input [3:0] b2, input c, input d, output signed [3:0] y);
|
||||
wire signed [3:0] ab1;
|
||||
assign ab1 = a1 * b1;
|
||||
assign y = ab1 + a2*b2 + c + d + 1;
|
||||
endmodule
|
||||
EOF
|
||||
prep
|
||||
equiv_opt -assert alumacc
|
||||
design -load postopt
|
||||
stat
|
||||
design -save save
|
||||
equiv_opt -assert maccmap
|
||||
design -load save
|
||||
equiv_opt -assert maccmap -unmap
|
||||
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module gate(input signed [2:0] a1, input signed [1:0] b1, output signed [3:0] y);
|
||||
assign y = a1 * b1;
|
||||
endmodule
|
||||
EOF
|
||||
prep
|
||||
equiv_opt -assert alumacc
|
||||
design -load postopt
|
||||
stat
|
||||
design -save save
|
||||
equiv_opt -assert maccmap
|
||||
design -load save
|
||||
equiv_opt -assert maccmap -unmap
|
||||
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module gate(input [2:0] a, input [1:0] b, output [3:0] y);
|
||||
assign y = a * b;
|
||||
endmodule
|
||||
EOF
|
||||
prep
|
||||
equiv_opt -assert alumacc
|
||||
design -load postopt
|
||||
stat
|
||||
design -save save
|
||||
equiv_opt -assert maccmap
|
||||
design -load save
|
||||
equiv_opt -assert maccmap -unmap
|
||||
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module gate(input [2:0] a, input [1:0] b, input [1:0] c, output [3:0] y);
|
||||
assign y = a * b - c;
|
||||
endmodule
|
||||
EOF
|
||||
prep
|
||||
equiv_opt -assert alumacc
|
||||
design -load postopt
|
||||
stat
|
||||
design -save save
|
||||
equiv_opt -assert maccmap
|
||||
design -load save
|
||||
equiv_opt -assert maccmap -unmap
|
19
tests/alumacc/macc_infer_n_unmap.ys
Normal file
19
tests/alumacc/macc_infer_n_unmap.ys
Normal file
|
@ -0,0 +1,19 @@
|
|||
read_verilog <<EOF
|
||||
module gate(input signed [2:0] a1, input signed [2:0] b1,
|
||||
input [1:0] a2, input [3:0] b2, input c, input d, output signed [3:0] y);
|
||||
wire signed [3:0] ab1;
|
||||
assign ab1 = a1 * b1;
|
||||
assign y = ab1 + a2*b2 + c + d + 1;
|
||||
endmodule
|
||||
EOF
|
||||
prep
|
||||
|
||||
design -save gold
|
||||
alumacc
|
||||
opt_clean
|
||||
select -assert-count 1 t:$macc_v2
|
||||
maccmap -unmap
|
||||
design -copy-from gold -as gold gate
|
||||
equiv_make gold gate equiv
|
||||
equiv_induct equiv
|
||||
equiv_status -assert equiv
|
|
@ -2,7 +2,7 @@ read_verilog ../../common/counter.v
|
|||
hierarchy -top top
|
||||
proc
|
||||
flatten
|
||||
equiv_opt -assert -multiclock -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f # equivalency check
|
||||
equiv_opt -assert -multiclock -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f -noioff # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd top # Constrain all select calls below inside the top module
|
||||
select -assert-count 4 t:$lut
|
||||
|
|
|
@ -5,7 +5,7 @@ design -save read
|
|||
|
||||
hierarchy -top my_dff
|
||||
proc
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f # equivalency check
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f -noioff # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd my_dff # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:sdffsre
|
||||
|
@ -14,7 +14,7 @@ select -assert-none t:sdffsre %% t:* %D
|
|||
design -load read
|
||||
hierarchy -top my_dffe
|
||||
proc
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f # equivalency check
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f -noioff # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd my_dffe # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:sdffsre
|
||||
|
|
209
tests/arch/quicklogic/qlf_k6n10f/ioff.ys
Normal file
209
tests/arch/quicklogic/qlf_k6n10f/ioff.ys
Normal file
|
@ -0,0 +1,209 @@
|
|||
# test: acceptable for output IOFF promotion
|
||||
read_verilog <<EOF
|
||||
module top (input clk, input a, output reg o);
|
||||
always @(posedge clk) begin
|
||||
o <= ~a;
|
||||
end
|
||||
endmodule
|
||||
EOF
|
||||
synth_quicklogic -family qlf_k6n10f -top top
|
||||
select -assert-count 1 t:dff
|
||||
|
||||
design -reset
|
||||
# test: acceptable for output IOFF promotion
|
||||
read_verilog <<EOF
|
||||
module top (input clk, input [3:0] a, output reg [3:0] o);
|
||||
always @(posedge clk) begin
|
||||
o <= ~a;
|
||||
end
|
||||
endmodule
|
||||
EOF
|
||||
synth_quicklogic -family qlf_k6n10f -top top
|
||||
select -assert-count 4 t:dff
|
||||
|
||||
design -reset
|
||||
# test: acceptable for output IOFF promotion; duplicate output FF
|
||||
read_verilog <<EOF
|
||||
module top (input clk, input [3:0] a, output [3:0] o, output [3:0] p);
|
||||
reg [3:0] r;
|
||||
always @(posedge clk) begin
|
||||
r <= ~a;
|
||||
end
|
||||
assign o = r;
|
||||
assign p = r;
|
||||
endmodule
|
||||
EOF
|
||||
synth_quicklogic -family qlf_k6n10f -top top
|
||||
select -assert-count 8 t:dff
|
||||
|
||||
design -reset
|
||||
# test: acceptable for input IOFF promotion
|
||||
read_verilog <<EOF
|
||||
module top (input clk, input a, output o);
|
||||
reg r;
|
||||
always @(posedge clk) begin
|
||||
r <= a;
|
||||
end
|
||||
assign o = ~r;
|
||||
endmodule
|
||||
EOF
|
||||
synth_quicklogic -family qlf_k6n10f -top top
|
||||
select -assert-count 1 t:dff
|
||||
|
||||
design -reset
|
||||
# test: acceptable for input IOFF promotion
|
||||
read_verilog <<EOF
|
||||
module top (input clk, input [3:0] a, output [3:0] o);
|
||||
reg [3:0] r;
|
||||
always @(posedge clk) begin
|
||||
r <= a;
|
||||
end
|
||||
assign o = ~r;
|
||||
endmodule
|
||||
EOF
|
||||
synth_quicklogic -family qlf_k6n10f -top top
|
||||
select -assert-count 4 t:dff
|
||||
|
||||
design -reset
|
||||
# test: acceptable for either IOFF promotion
|
||||
read_verilog <<EOF
|
||||
module top (input clk, input a, output reg o);
|
||||
always @(posedge clk) begin
|
||||
o <= a;
|
||||
end
|
||||
endmodule
|
||||
EOF
|
||||
synth_quicklogic -family qlf_k6n10f -top top
|
||||
select -assert-count 1 t:dff
|
||||
|
||||
design -reset
|
||||
# test: not acceptable for output IOFF promotion: output signal is used
|
||||
read_verilog <<EOF
|
||||
module top (input clk, input a, output reg o);
|
||||
always @(posedge clk) begin
|
||||
o <= ~a | o;
|
||||
end
|
||||
endmodule
|
||||
EOF
|
||||
synth_quicklogic -family qlf_k6n10f -top top
|
||||
select -assert-count 0 t:dff
|
||||
|
||||
design -reset
|
||||
# test: not acceptable for output IOFF promotion: output signal is used
|
||||
read_verilog <<EOF
|
||||
module top (input clk, input [3:0] a, output reg [3:0] o);
|
||||
always @(posedge clk) begin
|
||||
o <= ~a | o;
|
||||
end
|
||||
endmodule
|
||||
EOF
|
||||
synth_quicklogic -family qlf_k6n10f -top top
|
||||
select -assert-count 0 t:dff
|
||||
|
||||
design -reset
|
||||
# test: not acceptable for input IOFF promotion: input signal is used
|
||||
read_verilog <<EOF
|
||||
module top (input clk, input a, output o, p);
|
||||
reg r;
|
||||
always @(posedge clk) begin
|
||||
r <= a;
|
||||
end
|
||||
assign o = ~r;
|
||||
assign p = ~a;
|
||||
endmodule
|
||||
EOF
|
||||
synth_quicklogic -family qlf_k6n10f -top top
|
||||
select -assert-count 0 t:dff
|
||||
|
||||
design -reset
|
||||
# test: not acceptable for input IOFF promotion: input signal is used
|
||||
read_verilog <<EOF
|
||||
module top (input clk, input [3:0] a, output [3:0] o, output [3:0] p);
|
||||
reg [3:0] r;
|
||||
always @(posedge clk) begin
|
||||
r <= a;
|
||||
end
|
||||
assign o = ~r;
|
||||
assign p = ~a;
|
||||
endmodule
|
||||
EOF
|
||||
synth_quicklogic -family qlf_k6n10f -top top
|
||||
select -assert-count 0 t:dff
|
||||
|
||||
design -reset
|
||||
# test: not acceptable for IOFF promotion: FF has reset
|
||||
read_verilog <<EOF
|
||||
module top (input clk, input rst, input a, output reg o);
|
||||
always @(posedge clk) begin
|
||||
if (rst)
|
||||
o <= 1'b0;
|
||||
else
|
||||
o <= a;
|
||||
end
|
||||
endmodule
|
||||
EOF
|
||||
synth_quicklogic -family qlf_k6n10f -top top
|
||||
select -assert-count 0 t:dff
|
||||
|
||||
design -reset
|
||||
# test: not acceptable for IOFF promotion: FF has reset
|
||||
read_verilog <<EOF
|
||||
module top (input clk, input rst, input [3:0] a, output reg [3:0] o);
|
||||
always @(posedge clk) begin
|
||||
if (rst)
|
||||
o <= 4'b0;
|
||||
else
|
||||
o <= a;
|
||||
end
|
||||
endmodule
|
||||
EOF
|
||||
synth_quicklogic -family qlf_k6n10f -top top
|
||||
select -assert-count 0 t:dff
|
||||
|
||||
design -reset
|
||||
# test: not acceptable for IOFF promotion: FF has enable
|
||||
read_verilog <<EOF
|
||||
module top (input clk, input en, input a, output reg o);
|
||||
always @(posedge clk) begin
|
||||
if (en)
|
||||
o <= a;
|
||||
end
|
||||
endmodule
|
||||
EOF
|
||||
synth_quicklogic -family qlf_k6n10f -top top
|
||||
select -assert-count 0 t:dff
|
||||
|
||||
design -reset
|
||||
# test: not acceptable for IOFF promotion: FF has enable
|
||||
read_verilog <<EOF
|
||||
module top (input clk, input en, input [3:0] a, output reg [3:0] o);
|
||||
always @(posedge clk) begin
|
||||
if (en)
|
||||
o <= a;
|
||||
end
|
||||
endmodule
|
||||
EOF
|
||||
synth_quicklogic -family qlf_k6n10f -top top
|
||||
select -assert-count 0 t:dff
|
||||
|
||||
design -reset
|
||||
# test: duplicate registers driving multiple output ports
|
||||
read_verilog <<EOF
|
||||
module top (
|
||||
input clk,
|
||||
input en,
|
||||
input [3:0] a,
|
||||
output reg [3:0] o_1,
|
||||
output wire [3:0] o_2
|
||||
);
|
||||
always @(posedge clk) begin
|
||||
o_1[1:0] <= ~a[1:0];
|
||||
if (en)
|
||||
o_1[2] <= a[2];
|
||||
end
|
||||
always @(*) o_1[3] = a[3];
|
||||
assign o_2 = o_1;
|
||||
endmodule
|
||||
EOF
|
||||
synth_quicklogic -family qlf_k6n10f -top top
|
||||
select -assert-count 4 t:dff
|
9
tests/opt/opt_expr_shr_int_max.ys
Normal file
9
tests/opt/opt_expr_shr_int_max.ys
Normal file
|
@ -0,0 +1,9 @@
|
|||
read_verilog << EOF
|
||||
module uut_00034(b, y);
|
||||
input signed [30:0] b;
|
||||
output [11:0] y = b >> ~31'b0; // shift by INT_MAX
|
||||
endmodule
|
||||
EOF
|
||||
|
||||
# This should succeed, even with UBSAN halt_on_error
|
||||
opt_expr
|
13
tests/techmap/buf.ys
Normal file
13
tests/techmap/buf.ys
Normal file
|
@ -0,0 +1,13 @@
|
|||
read_verilog -icells <<EOF
|
||||
module top(input wire [2:0] a, output wire [2:0] y);
|
||||
\$buf #(.WIDTH(3)) b(.A(a), .Y(y));
|
||||
endmodule
|
||||
EOF
|
||||
design -save save
|
||||
|
||||
opt_clean
|
||||
select -assert-none t:$buf
|
||||
|
||||
design -load save
|
||||
techmap
|
||||
select -assert-none t:$buf
|
|
@ -5,7 +5,22 @@ YOSYS_NAMESPACE_BEGIN
|
|||
|
||||
namespace RTLIL {
|
||||
|
||||
class KernelRtlilTest : public testing::Test {};
|
||||
struct SafePrintToStringParamName {
|
||||
template <class ParamType>
|
||||
std::string operator()(const testing::TestParamInfo<ParamType>& info) const {
|
||||
std::string name = testing::PrintToString(info.param);
|
||||
for (auto &c : name)
|
||||
if (!('0' <= c && c <= '9') && !('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z') ) c = '_';
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
class KernelRtlilTest : public testing::Test {
|
||||
protected:
|
||||
KernelRtlilTest() {
|
||||
if (log_files.empty()) log_files.emplace_back(stdout);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(KernelRtlilTest, ConstAssignCompare)
|
||||
{
|
||||
|
@ -74,6 +89,83 @@ namespace RTLIL {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
class WireRtlVsHdlIndexConversionTest :
|
||||
public KernelRtlilTest,
|
||||
public testing::WithParamInterface<std::tuple<bool, int, int>>
|
||||
{};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
WireRtlVsHdlIndexConversionInstance,
|
||||
WireRtlVsHdlIndexConversionTest,
|
||||
testing::Values(
|
||||
std::make_tuple(false, 0, 10),
|
||||
std::make_tuple(true, 0, 10),
|
||||
std::make_tuple(false, 4, 10),
|
||||
std::make_tuple(true, 4, 10),
|
||||
std::make_tuple(false, -4, 10),
|
||||
std::make_tuple(true, -4, 10),
|
||||
std::make_tuple(false, 0, 1),
|
||||
std::make_tuple(true, 0, 1),
|
||||
std::make_tuple(false, 4, 1),
|
||||
std::make_tuple(true, 4, 1),
|
||||
std::make_tuple(false, -4, 1),
|
||||
std::make_tuple(true, -4, 1)
|
||||
),
|
||||
SafePrintToStringParamName()
|
||||
);
|
||||
|
||||
TEST_P(WireRtlVsHdlIndexConversionTest, WireRtlVsHdlIndexConversion) {
|
||||
std::unique_ptr<Module> mod = std::make_unique<Module>();
|
||||
Wire *wire = mod->addWire(ID(test), 10);
|
||||
|
||||
auto [upto, start_offset, width] = GetParam();
|
||||
|
||||
wire->upto = upto;
|
||||
wire->start_offset = start_offset;
|
||||
wire->width = width;
|
||||
|
||||
int smallest = INT_MAX;
|
||||
int largest = INT_MIN;
|
||||
|
||||
for (int i = 0; i < wire->width; i++) {
|
||||
int j = wire->to_hdl_index(i);
|
||||
smallest = std::min(smallest, j);
|
||||
largest = std::max(largest, j);
|
||||
EXPECT_EQ(
|
||||
std::make_pair(wire->from_hdl_index(j), j),
|
||||
std::make_pair(i, wire->to_hdl_index(i))
|
||||
);
|
||||
}
|
||||
|
||||
EXPECT_EQ(smallest, start_offset);
|
||||
EXPECT_EQ(largest, start_offset + wire->width - 1);
|
||||
|
||||
for (int i = 1; i < wire->width; i++) {
|
||||
EXPECT_EQ(
|
||||
wire->to_hdl_index(i) - wire->to_hdl_index(i - 1),
|
||||
upto ? -1 : 1
|
||||
);
|
||||
}
|
||||
|
||||
for (int j = smallest; j < largest; j++) {
|
||||
int i = wire->from_hdl_index(j);
|
||||
EXPECT_EQ(
|
||||
std::make_pair(wire->from_hdl_index(j), j),
|
||||
std::make_pair(i, wire->to_hdl_index(i))
|
||||
);
|
||||
}
|
||||
|
||||
for (int i = -10; i < 0; i++)
|
||||
EXPECT_EQ(wire->to_hdl_index(i), INT_MIN);
|
||||
for (int i = wire->width; i < wire->width + 10; i++)
|
||||
EXPECT_EQ(wire->to_hdl_index(i), INT_MIN);
|
||||
for (int j = smallest - 10; j < smallest; j++)
|
||||
EXPECT_EQ(wire->from_hdl_index(j), INT_MIN);
|
||||
for (int j = largest + 1; j < largest + 11; j++)
|
||||
EXPECT_EQ(wire->from_hdl_index(j), INT_MIN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
121
tests/various/abstract_init.ys
Normal file
121
tests/various/abstract_init.ys
Normal file
|
@ -0,0 +1,121 @@
|
|||
design -reset
|
||||
read_verilog <<EOT
|
||||
module foo (CLK, Q, QQQ);
|
||||
input CLK;
|
||||
output reg QQQ;
|
||||
output reg Q = 1'b1;
|
||||
assign QQQ = Q;
|
||||
always @(posedge CLK)
|
||||
Q <= ~Q;
|
||||
endmodule
|
||||
EOT
|
||||
|
||||
proc
|
||||
opt_expr
|
||||
opt_dff
|
||||
select -assert-count 1 w:Q a:init %i
|
||||
abstract -init w:QQQ
|
||||
check -assert
|
||||
select -assert-count 0 w:Q a:init %i
|
||||
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module foo (CLK, Q, QQQ);
|
||||
input CLK;
|
||||
output reg QQQ;
|
||||
output reg [1:0] Q = 1'b1;
|
||||
assign QQQ = Q;
|
||||
always @(posedge CLK)
|
||||
Q <= ~Q;
|
||||
endmodule
|
||||
EOT
|
||||
|
||||
proc
|
||||
opt_expr
|
||||
opt_dff
|
||||
select -assert-count 1 w:Q a:init=2'b01 %i
|
||||
abstract -init w:QQQ
|
||||
check -assert
|
||||
select -assert-count 1 w:Q a:init=2'b0x %i
|
||||
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module foo (CLK, Q);
|
||||
input CLK;
|
||||
// downto
|
||||
output reg [1:0] Q = 1'b1;
|
||||
always @(posedge CLK)
|
||||
Q <= ~Q;
|
||||
endmodule
|
||||
EOT
|
||||
|
||||
proc
|
||||
opt_expr
|
||||
opt_dff
|
||||
design -save basic
|
||||
select -assert-count 1 w:Q a:init=2'b01 %i
|
||||
abstract -init -slice 0 w:Q
|
||||
check -assert
|
||||
select -assert-count 1 w:Q a:init=2'b0x %i
|
||||
design -load basic
|
||||
select -assert-count 1 w:Q a:init=2'b01 %i
|
||||
abstract -init -slice 0:1 w:Q
|
||||
check -assert
|
||||
select -assert-count 0 w:Q a:init %i
|
||||
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module foo (CLK, Q);
|
||||
input CLK;
|
||||
// downto
|
||||
output reg [1:0] Q = 1'b1;
|
||||
always @(posedge CLK)
|
||||
Q <= ~Q;
|
||||
endmodule
|
||||
EOT
|
||||
|
||||
proc
|
||||
opt_expr
|
||||
opt_dff
|
||||
select -assert-count 1 w:Q a:init=2'b01 %i
|
||||
abstract -init -rtlilslice 0 w:Q
|
||||
check -assert
|
||||
select -assert-count 1 w:Q a:init=2'b0x %i
|
||||
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module foo (CLK, Q);
|
||||
input CLK;
|
||||
// upto
|
||||
output reg [0:1] Q = 1'b1;
|
||||
always @(posedge CLK)
|
||||
Q <= ~Q;
|
||||
endmodule
|
||||
EOT
|
||||
|
||||
proc
|
||||
opt_expr
|
||||
opt_dff
|
||||
select -assert-count 1 w:Q a:init=2'b01 %i
|
||||
abstract -init -slice 0 w:Q
|
||||
check -assert
|
||||
select -assert-count 1 w:Q a:init=2'bx1 %i
|
||||
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module foo (CLK, Q);
|
||||
input CLK;
|
||||
// upto
|
||||
output reg [0:1] Q = 1'b1;
|
||||
always @(posedge CLK)
|
||||
Q <= ~Q;
|
||||
endmodule
|
||||
EOT
|
||||
|
||||
proc
|
||||
opt_expr
|
||||
opt_dff
|
||||
select -assert-count 1 w:Q a:init=2'b01 %i
|
||||
abstract -init -rtlilslice 0 w:Q
|
||||
check -assert
|
||||
select -assert-count 1 w:Q a:init=2'b0x %i
|
178
tests/various/abstract_state.ys
Normal file
178
tests/various/abstract_state.ys
Normal file
|
@ -0,0 +1,178 @@
|
|||
read_verilog <<EOT
|
||||
|
||||
module half_clock (CLK, Q, magic);
|
||||
input CLK;
|
||||
output reg Q;
|
||||
input magic;
|
||||
always @(posedge CLK)
|
||||
Q <= ~Q;
|
||||
endmodule
|
||||
|
||||
EOT
|
||||
proc
|
||||
design -save half_clock
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# An empty selection causes no change
|
||||
select -none
|
||||
|
||||
logger -expect log "Abstracted 0 stateful cells" 1
|
||||
abstract -state -enablen magic
|
||||
logger -check-expected
|
||||
|
||||
logger -expect log "Abstracted 0 init bits" 1
|
||||
abstract -init
|
||||
logger -check-expected
|
||||
|
||||
logger -expect log "Abstracted 0 driver ports" 1
|
||||
abstract -value -enablen magic
|
||||
logger -check-expected
|
||||
|
||||
select -clear
|
||||
# -----------------------------------------------------------------------------
|
||||
design -load half_clock
|
||||
# Basic -state test
|
||||
abstract -state -enablen magic
|
||||
check -assert
|
||||
# Connections to dff D input port
|
||||
select -set conn_to_d t:$dff %x:+[D] t:$dff %d
|
||||
# The D input port is fed with a mux
|
||||
select -set mux @conn_to_d %ci t:$mux %i
|
||||
select -assert-count 1 @mux
|
||||
# The S input port is fed with the magic wire
|
||||
select -assert-count 1 @mux %x:+[S] w:magic %i
|
||||
# The A input port is fed with an anyseq
|
||||
select -assert-count 1 @mux %x:+[A] %ci t:$anyseq %i
|
||||
# The B input port is fed with the negated Q
|
||||
select -set not @mux %x:+[B] %ci t:$not %i
|
||||
select -assert-count 1 @not %x:+[A] o:Q %i
|
||||
|
||||
design -load half_clock
|
||||
# Same thing, inverted polarity
|
||||
abstract -state -enable magic
|
||||
check -assert
|
||||
select -set conn_to_d t:$dff %x:+[D] t:$dff %d
|
||||
select -set mux @conn_to_d %ci t:$mux %i
|
||||
select -assert-count 1 @mux
|
||||
select -assert-count 1 @mux %x:+[S] w:magic %i
|
||||
# so we get swapped A and B
|
||||
select -assert-count 1 @mux %x:+[B] %ci t:$anyseq %i
|
||||
select -set not @mux %x:+[A] %ci t:$not %i
|
||||
select -assert-count 1 @not %x:+[A] o:Q %i
|
||||
# -----------------------------------------------------------------------------
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module wide_flop_no_q (CLK, DDD, QQQ, magic);
|
||||
input CLK;
|
||||
input [2:0] DDD;
|
||||
output reg [2:0] QQQ;
|
||||
input magic;
|
||||
always @(posedge CLK)
|
||||
QQQ <= DDD;
|
||||
endmodule
|
||||
EOT
|
||||
proc
|
||||
opt_expr
|
||||
opt_dff
|
||||
dump
|
||||
abstract -state -enablen magic -slice 0 w:QQQ
|
||||
check -assert
|
||||
# Connections to dff D input port
|
||||
select -set conn_to_d t:$dff %x:+[D] t:$dff %d
|
||||
# The D input port is partially fed with a mux
|
||||
select -set mux @conn_to_d %ci t:$mux %i
|
||||
select -assert-count 1 @mux
|
||||
# and also the DDD input
|
||||
select -assert-count 1 @conn_to_d w:DDD %i
|
||||
# The S input port is fed with the magic wire
|
||||
select -assert-count 1 @mux %x:+[S] w:magic %i
|
||||
# The A input port is fed with an anyseq
|
||||
select -assert-count 1 @mux %x:+[A] %ci t:$anyseq %i
|
||||
# The B input port is fed with DDD
|
||||
select -assert-count 1 @mux %x:+[B] %ci w:DDD %i
|
||||
# -----------------------------------------------------------------------------
|
||||
# Selecting wire Q connected to bit 0 of QQQ is the same as specifying
|
||||
# QQQ with the -slice 0 argument
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module wide_flop (CLK, DDD, QQQ, Q, magic);
|
||||
input CLK;
|
||||
input [2:0] DDD;
|
||||
output reg [2:0] QQQ;
|
||||
output reg Q;
|
||||
input magic;
|
||||
always @(posedge CLK)
|
||||
QQQ <= DDD;
|
||||
assign Q = QQQ[0];
|
||||
endmodule
|
||||
EOT
|
||||
proc
|
||||
design -save wide_flop
|
||||
# Test that abstracting an output slice muxes an input slice
|
||||
abstract -state -enablen magic w:Q
|
||||
check -assert
|
||||
# Same testing as previous case
|
||||
select -set conn_to_d t:$dff %x:+[D] t:$dff %d
|
||||
select -set mux @conn_to_d %ci t:$mux %i
|
||||
select -assert-count 1 @mux
|
||||
select -assert-count 1 @conn_to_d w:DDD %i
|
||||
select -assert-count 1 @mux %x:+[S] w:magic %i
|
||||
select -assert-count 1 @mux %x:+[A] %ci t:$anyseq %i
|
||||
select -assert-count 1 @mux %x:+[B] %ci w:DDD %i
|
||||
# -----------------------------------------------------------------------------
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module half_clock_en (CLK, E, Q, magic);
|
||||
input CLK;
|
||||
input E;
|
||||
output reg Q;
|
||||
input magic;
|
||||
always @(posedge CLK)
|
||||
if (E)
|
||||
Q <= ~Q;
|
||||
endmodule
|
||||
EOT
|
||||
proc
|
||||
opt_expr
|
||||
opt_dff
|
||||
design -save half_clock_en
|
||||
# Test that abstracting a $dffe unmaps the enable
|
||||
select -assert-count 1 t:$dffe
|
||||
abstract -state -enablen magic
|
||||
check -assert
|
||||
select -assert-count 0 t:$dffe
|
||||
select -assert-count 1 t:$dff
|
||||
# -----------------------------------------------------------------------------
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module top (CLK, E, Q, Q_EN);
|
||||
input CLK;
|
||||
input E;
|
||||
output reg Q;
|
||||
output reg Q_EN;
|
||||
half_clock uut (CLK, Q, 1'b0);
|
||||
half_clock_en uut_en (CLK, E, Q_EN, 1'b0);
|
||||
endmodule
|
||||
EOT
|
||||
proc
|
||||
design -import half_clock
|
||||
design -import half_clock_en
|
||||
hierarchy -check -top top
|
||||
# Test when the abstraction is disabled (enable is inactive),
|
||||
# the equivalence is preserved
|
||||
rename top top_gold
|
||||
design -save gold
|
||||
abstract -state -enable magic half_clock half_clock_en
|
||||
flatten
|
||||
rename top_gold top_gate
|
||||
design -save gate
|
||||
design -load gold
|
||||
flatten
|
||||
design -import gate -as top_gate
|
||||
|
||||
equiv_make top_gold top_gate equiv
|
||||
equiv_induct equiv
|
||||
equiv_status -assert equiv
|
||||
# The reader may verify that this model checking is functional
|
||||
# by changing -enable to -enablen in the abstract pass invocation above
|
||||
# -----------------------------------------------------------------------------
|
95
tests/various/abstract_value.ys
Normal file
95
tests/various/abstract_value.ys
Normal file
|
@ -0,0 +1,95 @@
|
|||
read_verilog <<EOT
|
||||
module split_output (A, B, Y, magic);
|
||||
input [1:0] A;
|
||||
input [1:0] B;
|
||||
output [1:0] Y;
|
||||
input magic;
|
||||
wire W;
|
||||
assign Y = A + B;
|
||||
assign W = Y[0]; // <--- look here
|
||||
endmodule
|
||||
EOT
|
||||
proc
|
||||
design -save split_output
|
||||
# Basic -value test
|
||||
abstract -value -enable magic w:W
|
||||
check -assert
|
||||
# Connections to $add Y output port
|
||||
select -set conn_to_y t:$add %x:+[Y] t:$add %d
|
||||
# The $add Y output port feeds partially into a mux
|
||||
select -set mux @conn_to_y %ci t:$mux %i
|
||||
select -assert-count 1 @mux
|
||||
# and also the Y module output
|
||||
select -assert-count 1 @conn_to_y %a o:Y %i
|
||||
# The S input port is fed with the magic wire
|
||||
select -assert-count 1 @mux %x:+[S] w:magic %i
|
||||
# The B input port is fed with an anyseq
|
||||
select -assert-count 1 @mux %x:+[B] %ci t:$anyseq %i
|
||||
# The Y output port feeds into the Y module output
|
||||
select -assert-count 1 @mux %x:+[Y] %co o:Y %i
|
||||
# -----------------------------------------------------------------------------
|
||||
# Same thing, but we use -slice instead of wire W
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module split_output_no_w (A, B, Y, magic);
|
||||
input [1:0] A;
|
||||
input [1:0] B;
|
||||
output [1:0] Y;
|
||||
input magic;
|
||||
assign Y = A + B;
|
||||
endmodule
|
||||
EOT
|
||||
proc
|
||||
# Same test as the previous case
|
||||
abstract -value -enable magic -slice 0 w:Y
|
||||
check -assert
|
||||
select -set conn_to_y t:$add %x:+[Y] t:$add %d
|
||||
select -set mux @conn_to_y %ci t:$mux %i
|
||||
select -assert-count 1 @mux
|
||||
select -assert-count 1 @conn_to_y %a o:Y %i
|
||||
select -assert-count 1 @mux %x:+[S] w:magic %i
|
||||
select -assert-count 1 @mux %x:+[B] %ci t:$anyseq %i
|
||||
select -assert-count 1 @mux %x:+[Y] %co o:Y %i
|
||||
# -----------------------------------------------------------------------------
|
||||
design -reset
|
||||
read_verilog <<EOT
|
||||
module split_input (A, B, Y, magic);
|
||||
input [1:0] A;
|
||||
input [1:0] B;
|
||||
output [1:0] Y;
|
||||
input magic;
|
||||
wire W;
|
||||
assign Y = A + B;
|
||||
assign W = A[0]; // <--- look here
|
||||
endmodule
|
||||
EOT
|
||||
proc
|
||||
design -save split_input
|
||||
# The mux goes on an input this time
|
||||
abstract -value -enable magic w:W
|
||||
check -assert
|
||||
# Connections to add A input port
|
||||
select -set conn_to_a t:$add %x:+[A] t:$add %d
|
||||
# The B input port is partially fed with a mux
|
||||
select -set mux @conn_to_a %ci t:$mux %i
|
||||
select -assert-count 1 @mux
|
||||
# and also the A input
|
||||
select -assert-count 1 @conn_to_a %a w:A %i
|
||||
# The S input port is fed with the magic wire
|
||||
select -assert-count 1 @mux %x:+[S] w:magic %i
|
||||
# The A input port is fed with the module input A
|
||||
select -assert-count 1 @mux %x:+[A] %ci i:A %i
|
||||
# The B input port is fed with an anyseq
|
||||
select -assert-count 1 @mux %x:+[B] %ci t:$anyseq %i
|
||||
# -----------------------------------------------------------------------------
|
||||
# All wires selected, excluding magic -> muxes on inputs and outputs
|
||||
design -load split_output
|
||||
select -assert-count 0 t:$mux
|
||||
abstract -value -enable magic w:* w:magic %d
|
||||
select -assert-count 3 t:$mux
|
||||
# All cells selected -> muxes on outputs only
|
||||
design -load split_output
|
||||
select -assert-count 0 t:$mux
|
||||
abstract -value -enable magic t:*
|
||||
select -assert-count 1 t:$mux
|
||||
# -----------------------------------------------------------------------------
|
44
tests/various/bug4909.ys
Normal file
44
tests/various/bug4909.ys
Normal file
|
@ -0,0 +1,44 @@
|
|||
read_rtlil << EOF
|
||||
autoidx 20
|
||||
attribute \src "3510.v:2.1-26.10"
|
||||
attribute \cells_not_processed 1
|
||||
attribute \tamara_triplicate 1
|
||||
module \top
|
||||
attribute \src "3510.v:14.3-17.8"
|
||||
wire width 4 $0\reg5[3:0]
|
||||
attribute $bugpoint 1
|
||||
wire width 4 $auto$bugpoint.cc:258:simplify_something$12
|
||||
wire $delete_wire$14
|
||||
attribute \src "3510.v:13.19-13.59"
|
||||
wire width 4 $xnor$3510.v:13$1_Y
|
||||
attribute \src "3510.v:11.23-11.27"
|
||||
wire width 4 \reg5
|
||||
attribute \src "3510.v:8.24-8.29"
|
||||
wire width 3 \wire4
|
||||
attribute \src "3510.v:3.33-3.34"
|
||||
wire width 12 output 1 \y
|
||||
attribute \src "3510.v:13.19-13.59"
|
||||
cell $xnor $xnor$3510.v:13$1
|
||||
parameter \A_SIGNED 0
|
||||
parameter \A_WIDTH 3
|
||||
parameter \B_SIGNED 0
|
||||
parameter \B_WIDTH 4
|
||||
parameter \Y_WIDTH 4
|
||||
connect \A 3'x
|
||||
connect \B $auto$bugpoint.cc:258:simplify_something$12
|
||||
connect \Y $xnor$3510.v:13$1_Y
|
||||
end
|
||||
attribute \src "3510.v:14.3-17.8"
|
||||
process $proc$3510.v:14$2
|
||||
assign $0\reg5[3:0] { \wire4 [2] \wire4 }
|
||||
sync posedge $delete_wire$14
|
||||
update \reg5 $0\reg5[3:0]
|
||||
end
|
||||
connect \y [4:0] { \reg5 1'0 }
|
||||
connect \wire4 $xnor$3510.v:13$1_Y [2:0]
|
||||
end
|
||||
EOF
|
||||
|
||||
prep
|
||||
splitcells
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue