diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml index e4bc8ec58..60fe481e7 100644 --- a/.github/actions/setup-build-env/action.yml +++ b/.github/actions/setup-build-env/action.yml @@ -1,14 +1,65 @@ name: Build environment setup description: Configure build env for Yosys builds + +inputs: + runs-on: + required: true + type: string + get-build-deps: + description: 'Install Yosys build dependencies' + default: false + required: false + type: boolean + get-docs-deps: + description: 'Install Yosys docs dependencies' + default: false + required: false + type: boolean + get-test-deps: + description: 'Install Yosys test dependencies' + default: false + required: false + type: boolean + get-iverilog: + description: 'Install iverilog' + default: false + required: false + type: boolean + runs: using: composite steps: - - name: Install Linux Dependencies + # if updating common/build/docs dependencies, make sure to update README.md + # and docs/source/getting_started/installation.rst to match. + - name: Linux common dependencies if: runner.os == 'Linux' - shell: bash - run: | - sudo apt-get update - sudo apt-get install gperf build-essential bison flex libfl-dev libreadline-dev gawk tcl-dev libffi-dev git graphviz xdot pkg-config python3 libboost-system-dev libboost-python-dev libboost-filesystem-dev zlib1g-dev libbz2-dev libgtest-dev + uses: awalsh128/cache-apt-pkgs-action@v1.6.0 + with: + packages: gawk git make python3 + version: ${{ inputs.runs-on }}-commonys + + - name: Linux build dependencies + if: runner.os == 'Linux' && inputs.get-build-deps == 'true' + uses: awalsh128/cache-apt-pkgs-action@v1.6.0 + with: + packages: bison clang flex libffi-dev libfl-dev libreadline-dev pkg-config tcl-dev zlib1g-dev + version: ${{ inputs.runs-on }}-buildys + + - name: Linux docs dependencies + if: runner.os == 'Linux' && inputs.get-docs-deps == 'true' + uses: awalsh128/cache-apt-pkgs-action@v1.6.0 + with: + packages: graphviz xdot + version: ${{ inputs.runs-on }}-docsys + + # if updating test dependencies, make sure to update + # docs/source/yosys_internals/extending_yosys/test_suites.rst to match. + - name: Linux test dependencies + if: runner.os == 'Linux' && inputs.get-test-deps == 'true' + uses: awalsh128/cache-apt-pkgs-action@v1.6.0 + with: + packages: libgtest-dev + version: ${{ inputs.runs-on }}-testys - name: Install macOS Dependencies if: runner.os == 'macOS' @@ -32,3 +83,9 @@ runs: echo "$(brew --prefix bison)/bin" >> $GITHUB_PATH echo "$(brew --prefix flex)/bin" >> $GITHUB_PATH echo "procs=$(sysctl -n hw.ncpu)" >> $GITHUB_ENV + + - name: Setup iverilog + if: inputs.get-iverilog == 'true' + uses: ./.github/actions/setup-iverilog + with: + runs-on: ${{ inputs.runs-on }} diff --git a/.github/actions/setup-iverilog/action.yml b/.github/actions/setup-iverilog/action.yml new file mode 100644 index 000000000..0acb582e3 --- /dev/null +++ b/.github/actions/setup-iverilog/action.yml @@ -0,0 +1,70 @@ +name: iverilog setup +description: Cached build and install of iverilog + +inputs: + runs-on: + required: true + type: string + +runs: + using: composite + steps: + - name: iverilog Linux deps + if: steps.restore-iverilog.outputs.cache-hit != 'true' && runner.os == 'Linux' + uses: awalsh128/cache-apt-pkgs-action@v1.6.0 + with: + packages: autoconf gperf make gcc g++ bison flex libbz2-dev + version: ${{ inputs.runs-on }}-iverilog + + - name: iverilog macOS deps + if: steps.restore-iverilog.outputs.cache-hit != 'true' && runner.os == 'macOS' + shell: bash + run: | + brew install autoconf + + - name: Get iverilog + id: get-iverilog + shell: bash + run: | + git clone https://github.com/steveicarus/iverilog.git + cd iverilog + echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - name: Get vcd2fst + shell: bash + run: | + git clone https://github.com/mmicko/libwave.git + mkdir -p ${{ github.workspace }}/.local/ + cd libwave + cmake . -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/.local + make -j$procs + make install + + - uses: actions/cache/restore@v4 + id: restore-iverilog + with: + path: .local/ + key: ${{ inputs.runs-on }}-${{ steps.get-iverilog.outputs.IVERILOG_GIT }} + + - name: Build iverilog + if: steps.restore-iverilog.outputs.cache-hit != 'true' + shell: bash + run: | + mkdir -p ${{ github.workspace }}/.local/ + cd iverilog + autoconf + CC=gcc CXX=g++ ./configure --prefix=${{ github.workspace }}/.local + make -j$procs + make install + + - name: Check iverilog + shell: bash + run: | + iverilog -V + + - uses: actions/cache/save@v4 + id: save-iverilog + if: steps.restore-iverilog.outputs.cache-hit != 'true' + with: + path: .local/ + key: ${{ steps.restore-iverilog.outputs.cache-primary-key }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 95595924a..4bca5a8a5 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -10,15 +10,18 @@ jobs: name: Analyze runs-on: ubuntu-latest steps: - - name: Install deps - run: sudo apt-get install bison flex libfl-dev libreadline-dev tcl-dev libffi-dev - - name: Checkout repository uses: actions/checkout@v4 with: submodules: true persist-credentials: false + - name: Setup environment + uses: ./.github/actions/setup-build-env + with: + runs-on: ubuntu-latest + get-build-deps: true + - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: diff --git a/.github/workflows/extra-builds.yml b/.github/workflows/extra-builds.yml index 503145e97..b22a399db 100644 --- a/.github/workflows/extra-builds.yml +++ b/.github/workflows/extra-builds.yml @@ -73,8 +73,8 @@ jobs: persist-credentials: false - name: Build run: | - WASI_SDK=wasi-sdk-19.0 - WASI_SDK_URL=https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz + WASI_SDK=wasi-sdk-27.0-x86_64-linux + WASI_SDK_URL=https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-27/wasi-sdk-27.0-x86_64-linux.tar.gz if ! [ -d ${WASI_SDK} ]; then curl -L ${WASI_SDK_URL} | tar xzf -; fi FLEX_VER=2.6.4 diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index 2d4a7a6c8..8c1a3bbd2 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -60,6 +60,9 @@ jobs: - name: Setup environment uses: ./.github/actions/setup-build-env + with: + runs-on: ${{ matrix.os }} + get-build-deps: true - name: Build shell: bash @@ -105,48 +108,10 @@ jobs: - name: Setup environment uses: ./.github/actions/setup-build-env - - - name: Get iverilog - id: get-iverilog - shell: bash - run: | - git clone https://github.com/steveicarus/iverilog.git - cd iverilog - echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - - - name: Get vcd2fst - shell: bash - run: | - git clone https://github.com/mmicko/libwave.git - mkdir -p ${{ github.workspace }}/.local/ - cd libwave - cmake . -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/.local - make -j$procs - make install - - - name: Cache iverilog - id: cache-iverilog - uses: actions/cache@v4 with: - path: .local/ - key: ${{ matrix.os }}-${{ steps.get-iverilog.outputs.IVERILOG_GIT }} - - - name: iverilog macOS deps - if: steps.cache-iverilog.outputs.cache-hit != 'true' && runner.os == 'macOS' - shell: bash - run: | - brew install autoconf - - - name: Build iverilog - if: steps.cache-iverilog.outputs.cache-hit != 'true' - shell: bash - run: | - mkdir -p ${{ github.workspace }}/.local/ - cd iverilog - autoconf - CC=gcc CXX=g++ ./configure --prefix=${{ github.workspace }}/.local - make -j$procs - make install + runs-on: ${{ matrix.os }} + get-test-deps: true + get-iverilog: true - name: Download build artifact uses: actions/download-artifact@v4 @@ -191,6 +156,8 @@ jobs: - name: Setup environment uses: ./.github/actions/setup-build-env + with: + runs-on: ${{ matrix.os }} - name: Download build artifact uses: actions/download-artifact@v4 @@ -229,6 +196,10 @@ jobs: - name: Setup environment uses: ./.github/actions/setup-build-env + with: + runs-on: ${{ matrix.os }} + get-build-deps: true + get-docs-deps: true - name: Download build artifact uses: actions/download-artifact@v4 diff --git a/.github/workflows/test-compile.yml b/.github/workflows/test-compile.yml index 7c18c7ba0..31c8bccf6 100644 --- a/.github/workflows/test-compile.yml +++ b/.github/workflows/test-compile.yml @@ -45,7 +45,7 @@ jobs: - 'gcc-14' include: # macOS x86 - - os: macos-13 + - os: macos-15-intel compiler: 'clang-19' # macOS arm - os: macos-latest @@ -60,6 +60,9 @@ jobs: - name: Setup environment uses: ./.github/actions/setup-build-env + with: + runs-on: ${{ matrix.os }} + get-build-deps: true - name: Setup Cpp uses: aminya/setup-cpp@v1 diff --git a/.github/workflows/test-sanitizers.yml b/.github/workflows/test-sanitizers.yml index 9c0f6d746..4c8e3ec51 100644 --- a/.github/workflows/test-sanitizers.yml +++ b/.github/workflows/test-sanitizers.yml @@ -44,53 +44,11 @@ jobs: - name: Setup environment uses: ./.github/actions/setup-build-env - - - name: Get iverilog - id: get-iverilog - shell: bash - run: | - git clone https://github.com/steveicarus/iverilog.git - cd iverilog - echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - - - name: Get vcd2fst - shell: bash - run: | - git clone https://github.com/mmicko/libwave.git - mkdir -p ${{ github.workspace }}/.local/ - cd libwave - cmake . -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/.local - make -j$procs - make install - - - name: Cache iverilog - id: cache-iverilog - uses: actions/cache@v4 with: - path: .local/ - key: ${{ matrix.os }}-${{ steps.get-iverilog.outputs.IVERILOG_GIT }} - - - name: iverilog macOS deps - if: steps.cache-iverilog.outputs.cache-hit != 'true' && runner.os == 'macOS' - shell: bash - run: | - brew install autoconf - - - name: Build iverilog - if: steps.cache-iverilog.outputs.cache-hit != 'true' - shell: bash - run: | - mkdir -p ${{ github.workspace }}/.local/ - cd iverilog - autoconf - CC=gcc CXX=g++ ./configure --prefix=${{ github.workspace }}/.local - make -j$procs - make install - - - name: Check iverilog - shell: bash - run: | - iverilog -V + runs-on: ${{ matrix.os }} + get-build-deps: true + get-test-deps: true + get-iverilog: true - name: Build shell: bash diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index b6c9a51ac..8e055f526 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -25,15 +25,15 @@ jobs: archs: "aarch64", }, { - name: "macOS 13", + name: "macOS 15 x64", family: "macos", - runner: "macos-13", + runner: "macos-15-intel", archs: "x86_64", }, { - name: "macOS 14", + name: "macOS 15 arm64", family: "macos", - runner: "macos-14", + runner: "macos-15", archs: "arm64", }, ## Windows is disabled because of an issue with compiling FFI as @@ -59,7 +59,7 @@ jobs: shell: bash run: | mkdir -p ffi - curl -L https://github.com/libffi/libffi/releases/download/v3.4.6/libffi-3.4.6.tar.gz | tar --strip-components=1 -xzC ffi + curl -L https://github.com/libffi/libffi/releases/download/v3.4.8/libffi-3.4.8.tar.gz | tar --strip-components=1 -xzC ffi - if: ${{ matrix.os.family == 'linux' }} name: "[Linux] Bison 3.8.2" shell: bash @@ -114,7 +114,7 @@ jobs: path: ./wheelhouse/*.whl upload_wheels: name: Upload Wheels - if: github.repository == 'YosysHQ/Yosys' + if: (github.repository == 'YosysHQ/Yosys') && (github.event_name == 'workflow_dispatch') runs-on: ubuntu-latest # Specifying a GitHub environment is optional, but strongly encouraged environment: pypi diff --git a/.github/workflows/wheels/_run_cibw_linux.py b/.github/workflows/wheels/_run_cibw_linux.py index fb4e0b839..1e8a0f497 100644 --- a/.github/workflows/wheels/_run_cibw_linux.py +++ b/.github/workflows/wheels/_run_cibw_linux.py @@ -24,10 +24,10 @@ from pathlib import Path __yosys_root__ = Path(__file__).absolute().parents[3] -for source in ["boost", "ffi", "bison"]: +for source in ["ffi", "bison"]: if not (__yosys_root__ / source).is_dir(): print( - "You need to download boost, ffi and bison in a similar manner to wheels.yml first." + "You need to download ffi and bison in a similar manner to wheels.yml first." ) exit(-1) diff --git a/.gitignore b/.gitignore index 2367bccb3..a8b04ac45 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,6 @@ # pyosys /kernel/*.pyh /kernel/python_wrappers.cc -/boost /ffi /bison /venv diff --git a/Brewfile b/Brewfile index c90434e62..917f1bdb4 100644 --- a/Brewfile +++ b/Brewfile @@ -6,9 +6,9 @@ brew "git" brew "graphviz" brew "pkg-config" brew "python3" +brew "uv" brew "xdot" brew "bash" -brew "boost-python3" brew "llvm@20" brew "lld" brew "googletest" diff --git a/CHANGELOG b/CHANGELOG index 8ceefee41..6cefcc3ac 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,9 +2,27 @@ List of major changes and improvements between releases ======================================================= -Yosys 0.58 .. Yosys 0.59-dev +Yosys 0.59 .. Yosys 0.60-dev -------------------------- +Yosys 0.58 .. Yosys 0.59 +-------------------------- + * Various + - Pyosys is rewritten using pybind11. + - alumacc: merge independent of sign. + - write_btor: Include $assert and $assume cells in -ywmap output. + - RTLIL parser rewritten for efficiency. + - Wildcards enabled for Liberty file consuming. + - timeest: Add top ports launching/sampling. + + * New commands and options + - Added "-apply_derived_type" option to "box_derive" pass. + - Added "-publish_icells" option to "chtype" pass. + - Added "-width" option to "sim" pass. + - Added "sort" pass for sorting the design objects. + - Merged "synth_ecp5" and "synth_nexus" into "synth_lattice" pass. + - Added "-strict-gw5a-dffs" and "-setundef" options to "synth_gowin" pass. + Yosys 0.57 .. Yosys 0.58 -------------------------- * Various diff --git a/Makefile b/Makefile index 108c92b2a..09dcd0639 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,7 @@ ENABLE_HELP_SOURCE := 0 # python wrappers ENABLE_PYOSYS := 0 +PYOSYS_USE_UV := 1 # other configuration flags ENABLE_GCOV := 0 @@ -94,14 +95,14 @@ TARGETS = $(PROGRAM_PREFIX)yosys$(EXE) $(PROGRAM_PREFIX)yosys-config PRETTY = 1 SMALL = 0 -# Unit test -UNITESTPATH := tests/unit - all: top-all YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST))) VPATH := $(YOSYS_SRC) +# Unit test +UNITESTPATH := $(YOSYS_SRC)/tests/unit + export CXXSTD ?= c++17 CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -MP -D_YOSYS_ -fPIC -I$(PREFIX)/include LIBS := $(LIBS) -lstdc++ -lm @@ -160,7 +161,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.58+89 +YOSYS_VER := 0.59+0 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) @@ -183,7 +184,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: - sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 157aabb.. | wc -l`/;" Makefile + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 03eb220.. | wc -l`/;" Makefile ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) @@ -282,12 +283,11 @@ ifeq ($(WASI_SDK),) CXX = clang++ AR = llvm-ar RANLIB = llvm-ranlib -WASIFLAGS := -target wasm32-wasi --sysroot $(WASI_SYSROOT) $(WASIFLAGS) +WASIFLAGS := -target wasm32-wasi $(WASIFLAGS) else CXX = $(WASI_SDK)/bin/clang++ AR = $(WASI_SDK)/bin/ar RANLIB = $(WASI_SDK)/bin/ranlib -WASIFLAGS := --sysroot $(WASI_SDK)/share/wasi-sysroot $(WASIFLAGS) endif CXXFLAGS := $(WASIFLAGS) -std=$(CXXSTD) $(OPT_LEVEL) -D_WASI_EMULATED_PROCESS_CLOCKS $(filter-out -fPIC,$(CXXFLAGS)) LINKFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LINKFLAGS)) @@ -352,16 +352,22 @@ PYTHON_OBJECTS = pyosys/wrappers.o kernel/drivers.o kernel/yosys.o passes/cmds/p ifeq ($(ENABLE_PYOSYS),1) # python-config --ldflags includes -l and -L, but LINKFLAGS is only -L + +UV_ENV := +ifeq ($(PYOSYS_USE_UV),1) +UV_ENV := uv run --no-project --with 'pybind11>3,<4' --with 'cxxheaderparser' +endif + LINKFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags)) LIBS += $(shell $(PYTHON_CONFIG) --libs) EXE_LIBS += $(filter-out $(LIBS),$(shell $(PYTHON_CONFIG_FOR_EXE) --libs)) -PYBIND11_INCLUDE ?= $(shell $(PYTHON_EXECUTABLE) -m pybind11 --includes) +PYBIND11_INCLUDE ?= $(shell $(UV_ENV) $(PYTHON_EXECUTABLE) -m pybind11 --includes) CXXFLAGS += -I$(PYBIND11_INCLUDE) -DYOSYS_ENABLE_PYTHON CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DYOSYS_ENABLE_PYTHON OBJS += $(PY_WRAPPER_FILE).o -PY_GEN_SCRIPT = pyosys/generator.py -PY_WRAP_INCLUDES := $(shell $(PYTHON_EXECUTABLE) $(PY_GEN_SCRIPT) --print-includes) +PY_GEN_SCRIPT = $(YOSYS_SRC)/pyosys/generator.py +PY_WRAP_INCLUDES := $(shell $(UV_ENV) $(PYTHON_EXECUTABLE) $(PY_GEN_SCRIPT) --print-includes) endif # ENABLE_PYOSYS ifeq ($(ENABLE_READLINE),1) @@ -777,7 +783,7 @@ endif ifeq ($(ENABLE_PYOSYS),1) $(PY_WRAPPER_FILE).cc: $(PY_GEN_SCRIPT) pyosys/wrappers_tpl.cc $(PY_WRAP_INCLUDES) pyosys/hashlib.h $(Q) mkdir -p $(dir $@) - $(P) $(PYTHON_EXECUTABLE) $(PY_GEN_SCRIPT) $(PY_WRAPPER_FILE).cc + $(P) $(UV_ENV) $(PYTHON_EXECUTABLE) $(PY_GEN_SCRIPT) $(PY_WRAPPER_FILE).cc endif %.o: %.cpp @@ -1129,7 +1135,7 @@ DOC_TARGET ?= html docs: docs/prep $(Q) $(MAKE) -C docs $(DOC_TARGET) -clean: clean-py +clean: clean-py clean-unit-test rm -rf share rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS) rm -f kernel/version_*.o kernel/version_*.cc @@ -1144,7 +1150,7 @@ clean: clean-py 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 + -$(MAKE) -C $(YOSYS_SRC)/docs clean rm -rf docs/util/__pycache__ rm -f libyosys.so @@ -1156,7 +1162,7 @@ clean-py: rm -rf kernel/*.pyh clean-abc: - $(MAKE) -C abc DEP= clean + $(MAKE) -C $(YOSYS_SRC)/abc DEP= clean rm -f $(PROGRAM_PREFIX)yosys-abc$(EXE) $(PROGRAM_PREFIX)yosys-libabc.a abc/abc-[0-9a-f]* abc/libabc-[0-9a-f]*.a .git-abc-submodule-hash mrproper: clean diff --git a/README.md b/README.md index 6118d6079..427d59c9e 100644 --- a/README.md +++ b/README.md @@ -83,10 +83,10 @@ Xdot (graphviz) is used by the ``show`` command in yosys to display schematics. For example on Ubuntu Linux 22.04 LTS the following commands will install all prerequisites for building yosys: - $ sudo apt-get install build-essential clang lld bison flex libfl-dev \ - libreadline-dev gawk tcl-dev libffi-dev git \ - graphviz xdot pkg-config python3 libboost-system-dev \ - libboost-python-dev libboost-filesystem-dev zlib1g-dev + $ sudo apt-get install gawk git make python3 lld bison clang flex \ + libffi-dev libfl-dev libreadline-dev pkg-config tcl-dev zlib1g-dev \ + graphviz xdot + $ curl -LsSf https://astral.sh/uv/install.sh | sh The environment variable `CXX` can be used to control the C++ compiler used, or run one of the following to override it: diff --git a/abc b/abc index fa186342b..1c5ed1ce3 160000 --- a/abc +++ b/abc @@ -1 +1 @@ -Subproject commit fa186342baefea06e7c2aa13fe51f338ffc84912 +Subproject commit 1c5ed1ce378cc04beac30bb31abc4c37c8467042 diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 48710aff8..d575b5879 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -637,20 +637,6 @@ std::string escape_cxx_string(const std::string &input) return output; } -std::string basename(const std::string &filepath) -{ -#ifdef _WIN32 - const std::string dir_seps = "\\/"; -#else - const std::string dir_seps = "/"; -#endif - size_t sep_pos = filepath.find_last_of(dir_seps); - if (sep_pos != std::string::npos) - return filepath.substr(sep_pos + 1); - else - return filepath; -} - template std::string get_hdl_name(T *object) { @@ -2858,7 +2844,7 @@ struct CxxrtlWorker { } if (split_intf) - f << "#include \"" << basename(intf_filename) << "\"\n"; + f << "#include \"" << name_from_file_path(intf_filename) << "\"\n"; else f << "#include \n"; f << "\n"; diff --git a/backends/rtlil/rtlil_backend.cc b/backends/rtlil/rtlil_backend.cc index d607be837..057edc584 100644 --- a/backends/rtlil/rtlil_backend.cc +++ b/backends/rtlil/rtlil_backend.cc @@ -121,7 +121,8 @@ void RTLIL_BACKEND::dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, boo dump_sigchunk(f, sig.as_chunk(), autoint); } else { f << stringf("{ "); - for (const auto& chunk : reversed(sig.chunks())) { + auto chunks = sig.chunks(); + for (const auto& chunk : reversed(chunks)) { dump_sigchunk(f, chunk, false); f << stringf(" "); } diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index c747aa901..faeb2cd0b 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -382,8 +382,9 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) dump_sigchunk(f, sig.as_chunk()); } else { f << stringf("{ "); - for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) { - if (it != sig.chunks().rbegin()) + auto chunks = sig.chunks(); + for (auto it = chunks.rbegin(); it != chunks.rend(); ++it) { + if (it != chunks.rbegin()) f << stringf(", "); dump_sigchunk(f, *it, true); } diff --git a/docs/source/cmd/index_techlibs_ecp5.rst b/docs/source/cmd/index_techlibs_ecp5.rst deleted file mode 100644 index 29fd309cf..000000000 --- a/docs/source/cmd/index_techlibs_ecp5.rst +++ /dev/null @@ -1,5 +0,0 @@ -ECP5 ------------------- - -.. autocmdgroup:: techlibs/ecp5 - :members: diff --git a/docs/source/cmd/index_techlibs_lattice_nexus.rst b/docs/source/cmd/index_techlibs_lattice_nexus.rst deleted file mode 100644 index d5ac4184c..000000000 --- a/docs/source/cmd/index_techlibs_lattice_nexus.rst +++ /dev/null @@ -1,5 +0,0 @@ -Lattice Nexus ------------------- - -.. autocmdgroup:: techlibs/nexus - :members: diff --git a/docs/source/conf.py b/docs/source/conf.py index 49f8f5eab..fb106bddb 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,7 +6,7 @@ import os project = 'YosysHQ Yosys' author = 'YosysHQ GmbH' copyright ='2025 YosysHQ GmbH' -yosys_ver = "0.58" +yosys_ver = "0.59" # select HTML theme html_theme = 'furo-ys' @@ -50,7 +50,7 @@ rst_prolog = """ :language: yoscrypt """ -extensions = ['sphinx.ext.autosectionlabel', 'sphinxcontrib.bibtex'] +extensions = ['sphinx.ext.autosectionlabel', 'sphinxcontrib.bibtex', 'sphinx_inline_tabs'] if os.getenv("READTHEDOCS"): # Use rtds_action if we are building on read the docs and have a github token env var diff --git a/docs/source/getting_started/installation.rst b/docs/source/getting_started/installation.rst index 0ac85d199..43b996353 100644 --- a/docs/source/getting_started/installation.rst +++ b/docs/source/getting_started/installation.rst @@ -93,60 +93,66 @@ tools: readline, libffi, Tcl and zlib; are optional but enabled by default (see :makevar:`ENABLE_*` settings in Makefile). Graphviz and Xdot are used by the `show` command to display schematics. -Installing all prerequisites for Ubuntu 22.04: +Installing all prerequisites: -.. code:: console - - sudo apt-get install gperf build-essential clang lld bison flex libfl-dev \ - libreadline-dev gawk tcl-dev libffi-dev git \ - graphviz xdot pkg-config python3 libboost-system-dev \ - libboost-python-dev libboost-filesystem-dev zlib1g-dev - -Installing all prerequisites for macOS 13 (with Homebrew): - -.. code:: console - - brew tap Homebrew/bundle && brew bundle - -or MacPorts: - -.. code:: console - - sudo port install bison flex readline gawk libffi graphviz \ - pkgconfig python311 boost zlib tcl - -On FreeBSD use the following command to install all prerequisites: - -.. code:: console - - pkg install bison flex readline gawk libffi graphviz \ - pkgconf python311 tcl-wrapper boost-libs - -.. note:: On FreeBSD system use gmake instead of make. To run tests use: - ``MAKE=gmake CXX=cxx CC=cc gmake test`` - -For Cygwin use the following command to install all prerequisites, or select these additional packages: - -.. code:: console - - setup-x86_64.exe -q --packages=bison,flex,gcc-core,gcc-g++,git,libffi-devel,libreadline-devel,make,pkg-config,python3,tcl-devel,boost-build,zlib-devel - -.. warning:: - - As of this writing, Cygwin only supports up to Python 3.9.16 while the - minimum required version of Python is 3.11. This means that Cygwin is not - compatible with many of the Python-based frontends. While this does not - currently prevent Yosys itself from working, no guarantees are made for - continued support. You may also need to specify `CXXSTD=gnu++17` to resolve - missing `strdup` function when using gcc. It is instead recommended to use - Windows Subsystem for Linux (WSL) and follow the instructions for Ubuntu. - -.. - For MSYS2 (MINGW64): +.. tab:: Ubuntu 22.04 .. code:: console - pacman -S bison flex mingw-w64-x86_64-gcc git libffi-devel libreadline-devel make pkg-config python3 tcl-devel mingw-w64-x86_64-boost zlib-devel + sudo apt-get install gawk git make python3 lld bison clang flex \ + libffi-dev libfl-dev libreadline-dev pkg-config tcl-dev zlib1g-dev \ + graphviz xdot + curl -LsSf https://astral.sh/uv/install.sh | sh + +.. tab:: macOS 13 (with Homebrew) + + .. code:: console + + brew tap Homebrew/bundle && brew bundle + +.. tab:: MacPorts + + .. code:: console + + sudo port install bison flex readline gawk libffi graphviz \ + pkgconfig python311 zlib tcl + +.. tab:: FreeBSD + + .. code:: console + + pkg install bison flex readline gawk libffi graphviz \ + pkgconf python311 tcl-wrapper + + .. note:: On FreeBSD system use gmake instead of make. To run tests use: + ``MAKE=gmake CXX=cxx CC=cc gmake test`` + +.. tab:: Cygwin + + Use the following command to install all prerequisites, or select these + additional packages: + + .. code:: console + + setup-x86_64.exe -q --packages=bison,flex,gcc-core,gcc-g++,git,libffi-devel,libreadline-devel,make,pkg-config,python3,tcl-devel,zlib-devel + + .. warning:: + + As of this writing, Cygwin only supports up to Python 3.9.16 while the + minimum required version of Python is 3.11. This means that Cygwin is not + compatible with many of the Python-based frontends. While this does not + currently prevent Yosys itself from working, no guarantees are made for + continued support. You may also need to specify ``CXXSTD=gnu++17`` to + resolve missing ``strdup`` function when using gcc. It is instead + recommended to use Windows Subsystem for Linux (WSL) and follow the + instructions for Ubuntu. + +.. + tab:: MSYS2 (MINGW64) + + .. code:: console + + pacman -S bison flex mingw-w64-x86_64-gcc git libffi-devel libreadline-devel make pkg-config python3 tcl-devel zlib-devel Not that I can get this to work; it's failing during ld with what looks like math library issues: ``multiple definition of `tanh'`` and @@ -215,7 +221,7 @@ Running the build system From the root ``yosys`` directory, call the following commands: .. code:: console - + make sudo make install @@ -228,7 +234,7 @@ To use a separate (out-of-tree) build directory, provide a path to the Makefile. Out-of-tree builds require a clean source tree. -.. seealso:: +.. seealso:: Refer to :doc:`/yosys_internals/extending_yosys/test_suites` for details on testing Yosys once compiled. diff --git a/docs/source/requirements.txt b/docs/source/requirements.txt index 203205169..403bfb1c6 100644 --- a/docs/source/requirements.txt +++ b/docs/source/requirements.txt @@ -1,3 +1,4 @@ furo-ys @ git+https://github.com/YosysHQ/furo-ys sphinxcontrib-bibtex rtds-action +sphinx-inline-tabs diff --git a/docs/source/using_yosys/pyosys.rst b/docs/source/using_yosys/pyosys.rst index 8aa7fd4fe..09b572e05 100644 --- a/docs/source/using_yosys/pyosys.rst +++ b/docs/source/using_yosys/pyosys.rst @@ -28,6 +28,14 @@ methods: ``yosys -y ./my_pyosys_script.py`` + Do note this requires some build-time dependencies to be available to Python, + namely, ``pybind11`` and ``cxxheaderparser``. By default, the required + ``uv`` package will be used to create an ephemeral environment with the + correct versions of the tools installed. + + You can force use of your current Python environment by passing the Makefile + flag ``PYOSYS_USE_UV=0``. + 2. Installing the Pyosys wheels On macOS and GNU/Linux you can install pre-built wheels of Yosys using diff --git a/docs/source/using_yosys/verilog.rst b/docs/source/using_yosys/verilog.rst index 92f223e49..a557360b7 100644 --- a/docs/source/using_yosys/verilog.rst +++ b/docs/source/using_yosys/verilog.rst @@ -9,7 +9,7 @@ Yosys and there are currently no plans to add support for them: - Non-synthesizable language features as defined in - IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002 + IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002 - The ``tri``, ``triand`` and ``trior`` net types @@ -356,21 +356,29 @@ from SystemVerilog: files being read into the same design afterwards. - typedefs are supported (including inside packages) - - type casts are currently not supported + + - type casts are currently not supported - enums are supported (including inside packages) - - but are currently not strongly typed + + - but are currently not strongly typed - packed structs and unions are supported - - arrays of packed structs/unions are currently not supported - - structure literals are currently not supported + + - arrays of packed structs/unions are currently not supported + - structure literals are currently not supported - multidimensional arrays are supported - - array assignment of unpacked arrays is currently not supported - - array literals are currently not supported -- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether - ports are inputs or outputs are supported. + - array assignment of unpacked arrays is currently not supported + - array literals are currently not supported + +- SystemVerilog interfaces (SVIs), including modports for specifying whether + ports are inputs or outputs, are partially supported. + + - interfaces must be provided as *named* arguments, not positional arguments. + i.e. ``foo bar(.intf(intf0), .x(x));`` is supported but ``foo bar(intf0, + x);`` is not. - Assignments within expressions are supported. diff --git a/docs/source/yosys_internals/extending_yosys/test_suites.rst b/docs/source/yosys_internals/extending_yosys/test_suites.rst index 3e5f45b94..81a79e77f 100644 --- a/docs/source/yosys_internals/extending_yosys/test_suites.rst +++ b/docs/source/yosys_internals/extending_yosys/test_suites.rst @@ -1,7 +1,72 @@ Testing Yosys ============= -.. TODO:: more about the included test suite and how to add tests +.. todo:: adding tests (makefile-tests vs seed-tests) + +Running the included test suite +------------------------------- + +The Yosys source comes with a test suite to avoid regressions and keep +everything working as expected. Tests can be run by calling ``make test`` from +the root Yosys directory. + +Functional tests +~~~~~~~~~~~~~~~~ + +Testing functional backends (see +:doc:`/yosys_internals/extending_yosys/functional_ir`) has a few requirements in +addition to those listed in :ref:`getting_started/installation:Build +prerequisites`: + +.. tab:: Ubuntu + + .. code:: console + + sudo apt-get install racket + raco pkg install rosette + pip install pytest-xdist pytest-xdist-gnumake + +.. tab:: macOS + + .. code:: console + + brew install racket + raco pkg install rosette + pip install pytest-xdist pytest-xdist-gnumake + +If you don't have one of the :ref:`getting_started/installation:CAD suite(s)` +installed, you should also install Z3 `following their +instructions `_. + +Then, set the :makevar:`ENABLE_FUNCTIONAL_TESTS` make variable when calling +``make test`` and the functional tests will be run as well. + +Unit tests +~~~~~~~~~~ + +Running the unit tests requires the following additional packages: + +.. tab:: Ubuntu + + .. code:: console + + sudo apt-get install libgtest-dev + +.. tab:: macOS + + No additional requirements. + +Unit tests can be run with ``make unit-test``. + +Docs tests +~~~~~~~~~~ + +There are some additional tests for checking examples included in the +documentation, which can be run by calling ``make test`` from the +:file:`yosys/docs` sub-directory (or ``make -C docs test`` from the root). This +also includes checking some macro commands to ensure that descriptions of them +are kept up to date, and is mostly intended for CI. + Automatic testing ----------------- @@ -14,8 +79,6 @@ compiler versions. For up to date information, including OS versions, refer to .. _Yosys Git repo: https://github.com/YosysHQ/yosys .. _the git actions page: https://github.com/YosysHQ/yosys/actions -.. todo:: are unit tests currently working - .. How to add a unit test ---------------------- diff --git a/docs/source/yosys_internals/formats/rtlil_rep.rst b/docs/source/yosys_internals/formats/rtlil_rep.rst index b0cbfe3a5..dbd69c7e4 100644 --- a/docs/source/yosys_internals/formats/rtlil_rep.rst +++ b/docs/source/yosys_internals/formats/rtlil_rep.rst @@ -194,17 +194,18 @@ RTLIL::SigSpec A "signal" is everything that can be applied to a cell port. I.e. -- | Any constant value of arbitrary bit-width +- | A bit from a wire (``RTLIL::SigBit``) + | 1em For example: ``mywire[24]`` + +- | A range of bits from a wire (wire variant of ``RTLIL::SigChunk``) + | 1em For example: ``mywire, mywire[15:8]`` + +- | Any constant value of arbitrary bit-width (``std::vector>`` variant of ``RTLIL::SigChunk``) | 1em For example: ``1337, 16'b0000010100111001, 1'b1, 1'bx`` -- | All bits of a wire or a selection of bits from a wire - | 1em For example: ``mywire, mywire[24], mywire[15:8]`` - -- | Concatenations of the above - | 1em For example: ``{16'd1337, mywire[15:8]}`` - -The ``RTLIL::SigSpec`` data type is used to represent signals. The -``RTLIL::Cell`` object contains one ``RTLIL::SigSpec`` for each cell port. +The ``RTLIL::SigSpec`` data type is used to represent signals. +It contains a single ``RTLIL::SigChunk`` or a vector of ``RTLIL::SigBit``. +The ``RTLIL::Cell`` object contains one ``RTLIL::SigSpec`` for each cell port. In addition, connections between wires are represented using a pair of ``RTLIL::SigSpec`` objects. Such pairs are needed in different locations. diff --git a/flake.nix b/flake.nix index 19ba59f17..1993b803b 100644 --- a/flake.nix +++ b/flake.nix @@ -41,7 +41,7 @@ packages.default = yosys; defaultPackage = yosys; devShell = pkgs.mkShell { - buildInputs = with pkgs; [ clang llvmPackages.bintools gcc bison flex libffi tcl readline python3 zlib git gtest abc-verifier verilog boost python3Packages.boost ]; + buildInputs = with pkgs; [ clang llvmPackages.bintools gcc bison flex libffi tcl readline python3 zlib git gtest abc-verifier verilog ]; }; } ); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 81018e137..308406591 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -30,6 +30,7 @@ #include "libs/sha1/sha1.h" #include "frontends/verilog/verilog_frontend.h" #include "ast.h" +#include "kernel/io.h" #include #include @@ -888,6 +889,52 @@ static void check_auto_nosync(AstNode *node) check_auto_nosync(child.get()); } +class PackageImporter { + std::set import_items; + bool is_wildcard; + const AstNode* node; +public: + PackageImporter(const AstNode* n, const AstNode* child) : node(n) { + is_wildcard = child->children.empty(); + // For specific imports, collect the list of items to import + if (!is_wildcard) { + for (auto& item : child->children) { + import_items.insert(item->str); + } + } + } + + void import(std::map& scope, AstNode* to_import) const { + // Check if this is a specific import and if this item should be imported + if (!is_wildcard && import_items.count(to_import->str) == 0) + return; + + if (to_import->type == AST_PARAMETER || to_import->type == AST_LOCALPARAM || + to_import->type == AST_TYPEDEF || to_import->type == AST_FUNCTION || + to_import->type == AST_TASK || to_import->type == AST_ENUM) { + // For wildcard imports, check if item already exists (from specific import) + if (is_wildcard && scope.count(to_import->str) > 0) + return; + scope[to_import->str] = to_import; + } + if (to_import->type == AST_ENUM) { + for (auto& enode : to_import->children) { + log_assert(enode->type==AST_ENUM_ITEM); + // Check if this enum item should be imported + if (!is_wildcard && import_items.count(enode->str) == 0) + continue; + // For wildcard imports, check if item already exists (from specific import) + if (is_wildcard && scope.count(enode->str) > 0) + continue; + if (scope.count(enode->str) == 0) + scope[enode->str] = enode.get(); + else + node->input_error("enum item %s already exists in current scope\n", enode->str); + } + } + } +}; + // convert the AST into a simpler AST that has all parameters substituted by their // values, unrolled for-loops, expanded generate blocks, etc. when this function // is done with an AST it can be converted into RTLIL using genRTLIL(). @@ -1122,22 +1169,10 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } if (package_node) { - // Import all names from the package into current scope + PackageImporter importer(this, child); + // Import names from the package into current scope for (auto& pkg_child : package_node->children) { - if (pkg_child->type == AST_PARAMETER || pkg_child->type == AST_LOCALPARAM || - pkg_child->type == AST_TYPEDEF || pkg_child->type == AST_FUNCTION || - pkg_child->type == AST_TASK || pkg_child->type == AST_ENUM) { - current_scope[pkg_child->str] = pkg_child.get(); - } - if (pkg_child->type == AST_ENUM) { - for (auto& enode : pkg_child->children) { - log_assert(enode->type==AST_ENUM_ITEM); - if (current_scope.count(enode->str) == 0) - current_scope[enode->str] = enode.get(); - else - input_error("enum item %s already exists in current scope\n", enode->str); - } - } + importer.import(current_scope, pkg_child.get()); } // Remove the import node since it's been processed children.erase(children.begin() + i); @@ -4474,12 +4509,7 @@ std::unique_ptr AstNode::readmem(bool is_readmemh, std::string mem_file std::ifstream f; f.open(mem_filename.c_str()); if (f.fail()) { -#ifdef _WIN32 - char slash = '\\'; -#else - char slash = '/'; -#endif - std::string path = location.begin.filename->substr(0, location.begin.filename->find_last_of(slash)+1); + std::string path = parent_from_file_path(*location.begin.filename); f.open(path + mem_filename.c_str()); yosys_input_files.insert(path + mem_filename); } else { diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index ff8932dac..5790e92f0 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -1576,6 +1576,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma SetIter si ; Port *port ; FOREACH_PORT_OF_PORTBUS(portbus, si, port) { + wire->port_id = nl->IndexOf(port) + 1; import_attributes(wire->attributes, port->GetNet(), nl, portbus->Size()); break; } diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index 1858edc97..7675bab62 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -895,11 +895,7 @@ frontend_verilog_preproc(std::istream &f, // if the include file was not found, it is not given with an absolute path, and the // currently read file is given with a path, then try again relative to its directory ff.clear(); -#ifdef _WIN32 - fixed_fn = filename.substr(0, filename.find_last_of("/\\")+1) + fn; -#else - fixed_fn = filename.substr(0, filename.rfind('/')+1) + fn; -#endif + fixed_fn = parent_from_file_path(filename) + fn; ff.open(fixed_fn); } if (ff.fail() && fn.size() > 0 && fn_relative) { diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index ef8427679..684727d5b 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -829,11 +829,31 @@ package_body_stmt: typedef_decl | localparam_decl | param_decl | task_func_decl; import_stmt: - TOK_IMPORT hierarchical_id TOK_PACKAGESEP TOK_ASTER TOK_SEMICOL { - // Create an import node to track package imports + TOK_IMPORT TOK_ID TOK_PACKAGESEP TOK_ASTER TOK_SEMICOL { + // Create an import node to track wildcard package imports auto import_node = std::make_unique(@$, AST_IMPORT); import_node->str = *$2; extra->ast_stack.back()->children.push_back(std::move(import_node)); + } | + TOK_IMPORT TOK_ID TOK_PACKAGESEP { + // Start a specific import: create and push the AST_IMPORT node + AstNode* import_node = extra->pushChild(std::make_unique(@$, AST_IMPORT)); + import_node->str = *$2; + } import_item_list TOK_SEMICOL { + // Done collecting specific items, pop the AST_IMPORT node + extra->ast_stack.pop_back(); + }; + +import_item_list: + import_item | + import_item_list TOK_COMMA import_item ; + +import_item: + TOK_ID { + // Append this specific import name under the current AST_IMPORT + auto item_node = std::make_unique(@$, AST_NONE); + item_node->str = *$1; + extra->ast_stack.back()->children.push_back(std::move(item_node)); }; interface: diff --git a/kernel/hashlib.h b/kernel/hashlib.h index ee27c85d6..ca600231a 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -1021,7 +1021,7 @@ protected: if (hashtable.empty()) { entries.emplace_back(std::forward(rvalue), -1); do_rehash(); - hash = do_hash(rvalue); + hash = do_hash(entries.back().udata); } else { entries.emplace_back(std::forward(rvalue), hashtable[hash]); hashtable[hash] = entries.size() - 1; diff --git a/kernel/io.cc b/kernel/io.cc index 9e9eb9fb0..e9801f63e 100644 --- a/kernel/io.cc +++ b/kernel/io.cc @@ -2,6 +2,7 @@ #include "kernel/log.h" #include #include +#include #if !defined(WIN32) #include @@ -391,6 +392,24 @@ void append_globbed(std::vector& paths, std::string pattern) copy(globbed.begin(), globbed.end(), back_inserter(paths)); } +std::string name_from_file_path(std::string path) { + return std::filesystem::path(path).filename().string(); +} + +// Includes OS_PATH_SEP at the end if present +std::string parent_from_file_path(std::string path) { + auto parent = std::filesystem::path(path).parent_path(); + if (parent.empty()) { + return ""; + } + // Add trailing separator to match original behavior + std::string result = parent.string(); + if (!result.empty() && result.back() != std::filesystem::path::preferred_separator) { + result += std::filesystem::path::preferred_separator; + } + return result; +} + void format_emit_unescaped(std::string &result, std::string_view fmt) { result.reserve(result.size() + fmt.size()); diff --git a/kernel/io.h b/kernel/io.h index b3922bef0..171f47a80 100644 --- a/kernel/io.h +++ b/kernel/io.h @@ -470,6 +470,8 @@ void remove_directory(std::string dirname); bool create_directory(const std::string& dirname); std::string escape_filename_spaces(const std::string& filename); void append_globbed(std::vector& paths, std::string pattern); +std::string name_from_file_path(std::string path); +std::string parent_from_file_path(std::string path); YOSYS_NAMESPACE_END diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index aa4743a87..a58487216 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -2657,10 +2657,9 @@ void RTLIL::Module::cloneInto(RTLIL::Module *new_mod) const RTLIL::Module *mod; void operator()(RTLIL::SigSpec &sig) { - sig.pack(); - for (auto &c : sig.chunks_) - if (c.wire != NULL) - c.wire = mod->wires_.at(c.wire->name); + sig.rewrite_wires([this](RTLIL::Wire *&wire) { + wire = mod->wires_.at(wire->name); + }); } }; @@ -2808,12 +2807,10 @@ void RTLIL::Module::remove(const pool &wires) const pool *wires_p; void operator()(RTLIL::SigSpec &sig) { - sig.pack(); - for (auto &c : sig.chunks_) - if (c.wire != NULL && wires_p->count(c.wire)) { - c.wire = module->addWire(stringf("$delete_wire$%d", autoidx++), c.width); - c.offset = 0; - } + sig.rewrite_wires([this](RTLIL::Wire *&wire) { + if (wires_p->count(wire)) + wire = module->addWire(stringf("$delete_wire$%d", autoidx++), wire->width); + }); } void operator()(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) { @@ -3088,6 +3085,14 @@ RTLIL::Cell *RTLIL::Module::addCell(RTLIL::IdString name, const RTLIL::Cell *oth return cell; } +RTLIL::Memory *RTLIL::Module::addMemory(RTLIL::IdString name) +{ + RTLIL::Memory *mem = new RTLIL::Memory; + mem->name = std::move(name); + memories[mem->name] = mem; + return mem; +} + RTLIL::Memory *RTLIL::Module::addMemory(RTLIL::IdString name, const RTLIL::Memory *other) { RTLIL::Memory *mem = new RTLIL::Memory; @@ -4451,9 +4456,7 @@ RTLIL::SigSpec::SigSpec(std::initializer_list parts) { cover("kernel.rtlil.sigspec.init.list"); - width_ = 0; - hash_ = 0; - + init_empty_bits(); log_assert(parts.size() > 0); auto ie = parts.begin(); auto it = ie + parts.size() - 1; @@ -4466,12 +4469,11 @@ RTLIL::SigSpec::SigSpec(const RTLIL::Const &value) cover("kernel.rtlil.sigspec.init.const"); if (GetSize(value) != 0) { - chunks_.emplace_back(value); - width_ = chunks_.back().width; + rep_ = CHUNK; + new (&chunk_) RTLIL::SigChunk(value); } else { - width_ = 0; + init_empty_bits(); } - hash_ = 0; check(); } @@ -4480,12 +4482,11 @@ RTLIL::SigSpec::SigSpec(RTLIL::Const &&value) cover("kernel.rtlil.sigspec.init.const.move"); if (GetSize(value) != 0) { - chunks_.emplace_back(std::move(value)); - width_ = chunks_.back().width; + rep_ = CHUNK; + new (&chunk_) RTLIL::SigChunk(value); } else { - width_ = 0; + init_empty_bits(); } - hash_ = 0; check(); } @@ -4494,12 +4495,11 @@ RTLIL::SigSpec::SigSpec(const RTLIL::SigChunk &chunk) cover("kernel.rtlil.sigspec.init.chunk"); if (chunk.width != 0) { - chunks_.emplace_back(chunk); - width_ = chunks_.back().width; + rep_ = CHUNK; + new (&chunk_) RTLIL::SigChunk(chunk); } else { - width_ = 0; + init_empty_bits(); } - hash_ = 0; check(); } @@ -4508,12 +4508,11 @@ RTLIL::SigSpec::SigSpec(RTLIL::SigChunk &&chunk) cover("kernel.rtlil.sigspec.init.chunk.move"); if (chunk.width != 0) { - chunks_.emplace_back(std::move(chunk)); - width_ = chunks_.back().width; + rep_ = CHUNK; + new (&chunk_) RTLIL::SigChunk(chunk); } else { - width_ = 0; + init_empty_bits(); } - hash_ = 0; check(); } @@ -4522,12 +4521,11 @@ RTLIL::SigSpec::SigSpec(RTLIL::Wire *wire) cover("kernel.rtlil.sigspec.init.wire"); if (wire->width != 0) { - chunks_.emplace_back(wire); - width_ = chunks_.back().width; + rep_ = CHUNK; + new (&chunk_) RTLIL::SigChunk(wire); } else { - width_ = 0; + init_empty_bits(); } - hash_ = 0; check(); } @@ -4536,12 +4534,11 @@ RTLIL::SigSpec::SigSpec(RTLIL::Wire *wire, int offset, int width) cover("kernel.rtlil.sigspec.init.wire_part"); if (width != 0) { - chunks_.emplace_back(wire, offset, width); - width_ = chunks_.back().width; + rep_ = CHUNK; + new (&chunk_) RTLIL::SigChunk(wire, offset, width); } else { - width_ = 0; + init_empty_bits(); } - hash_ = 0; check(); } @@ -4550,12 +4547,11 @@ RTLIL::SigSpec::SigSpec(const std::string &str) cover("kernel.rtlil.sigspec.init.str"); if (str.size() != 0) { - chunks_.emplace_back(str); - width_ = chunks_.back().width; + rep_ = CHUNK; + new (&chunk_) RTLIL::SigChunk(str); } else { - width_ = 0; + init_empty_bits(); } - hash_ = 0; check(); } @@ -4563,10 +4559,11 @@ RTLIL::SigSpec::SigSpec(int val, int width) { cover("kernel.rtlil.sigspec.init.int"); - if (width != 0) - chunks_.emplace_back(val, width); - width_ = width; - hash_ = 0; + if (width != 0) { + rep_ = CHUNK; + new (&chunk_) RTLIL::SigChunk(val, width); + } else + init_empty_bits(); check(); } @@ -4574,10 +4571,11 @@ RTLIL::SigSpec::SigSpec(RTLIL::State bit, int width) { cover("kernel.rtlil.sigspec.init.state"); - if (width != 0) - chunks_.emplace_back(bit, width); - width_ = width; - hash_ = 0; + if (width != 0) { + rep_ = CHUNK; + new (&chunk_) RTLIL::SigChunk(bit, width); + } else + init_empty_bits(); check(); } @@ -4586,14 +4584,20 @@ RTLIL::SigSpec::SigSpec(const RTLIL::SigBit &bit, int width) cover("kernel.rtlil.sigspec.init.bit"); if (width != 0) { - if (bit.wire == NULL) - chunks_.emplace_back(bit.data, width); - else + if (bit.wire == NULL) { + rep_ = CHUNK; + new (&chunk_) RTLIL::SigChunk(bit.data, width); + } else if (width == 1) { + rep_ = CHUNK; + new (&chunk_) RTLIL::SigChunk(bit); + } else { + init_empty_bits(); + bits_.reserve(width); for (int i = 0; i < width; i++) - chunks_.push_back(bit); - } - width_ = width; - hash_ = 0; + bits_.push_back(bit); + } + } else + init_empty_bits(); check(); } @@ -4601,8 +4605,7 @@ RTLIL::SigSpec::SigSpec(const std::vector &chunks) { cover("kernel.rtlil.sigspec.init.stdvec_chunks"); - width_ = 0; - hash_ = 0; + init_empty_bits(); for (const auto &c : chunks) append(c); check(); @@ -4612,8 +4615,7 @@ RTLIL::SigSpec::SigSpec(const std::vector &bits) { cover("kernel.rtlil.sigspec.init.stdvec_bits"); - width_ = 0; - hash_ = 0; + init_empty_bits(); for (const auto &bit : bits) append(bit); check(); @@ -4623,8 +4625,7 @@ RTLIL::SigSpec::SigSpec(const pool &bits) { cover("kernel.rtlil.sigspec.init.pool_bits"); - width_ = 0; - hash_ = 0; + init_empty_bits(); for (const auto &bit : bits) append(bit); check(); @@ -4634,8 +4635,7 @@ RTLIL::SigSpec::SigSpec(const std::set &bits) { cover("kernel.rtlil.sigspec.init.stdset_bits"); - width_ = 0; - hash_ = 0; + init_empty_bits(); for (const auto &bit : bits) append(bit); check(); @@ -4645,79 +4645,92 @@ RTLIL::SigSpec::SigSpec(bool bit) { cover("kernel.rtlil.sigspec.init.bool"); - width_ = 0; - hash_ = 0; - append(SigBit(bit)); + rep_ = CHUNK; + new (&chunk_) RTLIL::SigChunk(bit ? RTLIL::S1 : RTLIL::S0); check(); } -void RTLIL::SigSpec::pack() const +void RTLIL::SigSpec::Chunks::const_iterator::next_chunk_bits() { - RTLIL::SigSpec *that = (RTLIL::SigSpec*)this; - - if (that->bits_.empty()) + int bits_size = GetSize(spec.bits_); + if (bit_index >= bits_size) return; - - cover("kernel.rtlil.sigspec.convert.pack"); - log_assert(that->chunks_.empty()); - - std::vector old_bits; - old_bits.swap(that->bits_); - - RTLIL::SigChunk *last = NULL; - int last_end_offset = 0; - - for (auto &bit : old_bits) { - if (last && bit.wire == last->wire) { - if (bit.wire == NULL) { - last->data.push_back(bit.data); - last->width++; - continue; - } else if (last_end_offset == bit.offset) { - last_end_offset++; - last->width++; - continue; - } + int i = bit_index; + const SigBit &bit = spec.bits_[i++]; + chunk.wire = bit.wire; + chunk.data.clear(); + if (bit.is_wire()) { + chunk.offset = bit.offset; + while (i < bits_size && spec.bits_[i].wire == bit.wire && + spec.bits_[i].offset == bit.offset + i - bit_index) + ++i; + } else { + chunk.offset = 0; + chunk.data.push_back(bit.data); + while (i < bits_size && !spec.bits_[i].is_wire()) { + chunk.data.push_back(spec.bits_[i].data); + ++i; } - that->chunks_.push_back(bit); - last = &that->chunks_.back(); - last_end_offset = bit.offset + 1; } - - check(); + chunk.width = i - bit_index; } -void RTLIL::SigSpec::unpack() const +void RTLIL::SigSpec::unpack() { - RTLIL::SigSpec *that = (RTLIL::SigSpec*)this; - - if (that->chunks_.empty()) + if (rep_ == BITS) return; cover("kernel.rtlil.sigspec.convert.unpack"); - log_assert(that->bits_.empty()); - that->bits_.reserve(that->width_); - for (auto &c : that->chunks_) - for (int i = 0; i < c.width; i++) - that->bits_.emplace_back(c, i); + std::vector bits; + bits.reserve(chunk_.width); + for (int i = 0; i < chunk_.width; i++) + bits.emplace_back(chunk_, i); - that->chunks_.clear(); - that->hash_ = 0; + chunk_.~SigChunk(); + rep_ = BITS; + new (&bits_) std::vector(std::move(bits)); } -void RTLIL::SigSpec::updhash() const +void RTLIL::SigSpec::try_repack() { - RTLIL::SigSpec *that = (RTLIL::SigSpec*)this; - - if (that->hash_ != 0) + if (rep_ != BITS) return; + cover("kernel.rtlil.sigspec.convert.try_repack"); + + int bits_size = GetSize(bits_); + if (bits_size == 0) + return; + if (bits_[0].is_wire()) { + for (int i = 1; i < bits_size; i++) + if (bits_[0].wire != bits_[i].wire || bits_[0].offset + i != bits_[i].offset) + return; + SigChunk chunk(bits_[0].wire, bits_[0].offset, bits_size); + bits_.~vector(); + rep_ = CHUNK; + new (&chunk_) SigChunk(std::move(chunk)); + return; + } + std::vector bits; + bits.reserve(bits_size); + bits.push_back(bits_[0].data); + for (int i = 1; i < bits_size; i++) { + if (bits_[i].is_wire()) + return; + bits.push_back(bits_[i].data); + } + bits_.~vector(); + rep_ = CHUNK; + new (&chunk_) SigChunk(std::move(bits)); +} + +Hasher::hash_t RTLIL::SigSpec::updhash() const +{ cover("kernel.rtlil.sigspec.hash"); - that->pack(); Hasher h; - for (auto &c : that->chunks_) + for (auto &c : chunks()) if (c.wire == NULL) { for (auto &v : c.data) h.eat(v); @@ -4726,9 +4739,11 @@ void RTLIL::SigSpec::updhash() const h.eat(c.offset); h.eat(c.width); } - that->hash_ = h.yield(); - if (that->hash_ == 0) - that->hash_ = 1; + Hasher::hash_t result = h.yield(); + if (result == 0) + result = 1; + hash_.set(result); + return result; } void RTLIL::SigSpec::sort() @@ -4736,6 +4751,8 @@ void RTLIL::SigSpec::sort() unpack(); cover("kernel.rtlil.sigspec.sort"); std::sort(bits_.begin(), bits_.end()); + hash_.clear(); + try_repack(); } void RTLIL::SigSpec::sort_and_unify() @@ -4752,6 +4769,8 @@ void RTLIL::SigSpec::sort_and_unify() unique_bits.erase(last, unique_bits.end()); *this = unique_bits; + hash_.clear(); + try_repack(); } void RTLIL::SigSpec::replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with) @@ -4762,28 +4781,30 @@ void RTLIL::SigSpec::replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec void RTLIL::SigSpec::replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with, RTLIL::SigSpec *other) const { log_assert(other != NULL); - log_assert(width_ == other->width_); - log_assert(pattern.width_ == with.width_); - - pattern.unpack(); - with.unpack(); - unpack(); - other->unpack(); + log_assert(size() == other->size()); + log_assert(pattern.size() == with.size()); dict pattern_to_with; - for (int i = 0; i < GetSize(pattern.bits_); i++) { - if (pattern.bits_[i].wire != NULL) { - pattern_to_with.emplace(pattern.bits_[i], i); + int pattern_size = pattern.size(); + for (int i = 0; i < pattern_size; i++) { + SigBit pattern_bit = pattern[i]; + if (pattern_bit.wire != NULL) { + pattern_to_with.emplace(pattern_bit, i); } } - for (int j = 0; j < GetSize(bits_); j++) { - auto it = pattern_to_with.find(bits_[j]); + int this_size = size(); + bool other_modified = false; + for (int j = 0; j < this_size; j++) { + auto it = pattern_to_with.find((*this)[j]); if (it != pattern_to_with.end()) { - other->bits_[j] = with.bits_[it->second]; + other_modified = true; + (*other)[j] = with[it->second]; } } + if (other_modified) + other->try_repack(); other->check(); } @@ -4797,18 +4818,22 @@ void RTLIL::SigSpec::replace(const dict &rules, RT cover("kernel.rtlil.sigspec.replace_dict"); log_assert(other != NULL); - log_assert(width_ == other->width_); + log_assert(size() == other->size()); if (rules.empty()) return; - unpack(); - other->unpack(); - for (int i = 0; i < GetSize(bits_); i++) { - auto it = rules.find(bits_[i]); - if (it != rules.end()) - other->bits_[i] = it->second; + int this_size = size(); + bool other_modified = false; + for (int i = 0; i < this_size; i++) { + auto it = rules.find((*this)[i]); + if (it != rules.end()) { + other_modified = true; + (*other)[i] = it->second; + } } + if (other_modified) + other->try_repack(); other->check(); } @@ -4822,18 +4847,22 @@ void RTLIL::SigSpec::replace(const std::map &rules cover("kernel.rtlil.sigspec.replace_map"); log_assert(other != NULL); - log_assert(width_ == other->width_); + log_assert(size() == other->size()); if (rules.empty()) return; - unpack(); - other->unpack(); - for (int i = 0; i < GetSize(bits_); i++) { - auto it = rules.find(bits_[i]); - if (it != rules.end()) - other->bits_[i] = it->second; + int this_size = size(); + bool other_modified = false; + for (int i = 0; i < this_size; i++) { + auto it = rules.find((*this)[i]); + if (it != rules.end()) { + other_modified = true; + (*other)[i] = it->second; + } } + if (other_modified) + other->try_repack(); other->check(); } @@ -4857,10 +4886,12 @@ void RTLIL::SigSpec::remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *othe unpack(); if (other != NULL) { - log_assert(width_ == other->width_); + log_assert(size() == other->size()); other->unpack(); } + bool modified = false; + bool other_modified = false; for (int i = GetSize(bits_) - 1; i >= 0; i--) { if (bits_[i].wire == NULL) continue; @@ -4869,16 +4900,24 @@ void RTLIL::SigSpec::remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *othe if (bits_[i].wire == pattern_chunk.wire && bits_[i].offset >= pattern_chunk.offset && bits_[i].offset < pattern_chunk.offset + pattern_chunk.width) { + modified = true; bits_.erase(bits_.begin() + i); - width_--; if (other != NULL) { + other_modified = true; other->bits_.erase(other->bits_.begin() + i); - other->width_--; } break; } } + if (modified) { + hash_.clear(); + try_repack(); + } + if (other_modified) { + other->hash_.clear(); + other->try_repack(); + } check(); } @@ -4903,21 +4942,31 @@ void RTLIL::SigSpec::remove2(const pool &pattern, RTLIL::SigSpec unpack(); if (other != NULL) { - log_assert(width_ == other->width_); + log_assert(size() == other->size()); other->unpack(); } + bool modified = false; + bool other_modified = false; for (int i = GetSize(bits_) - 1; i >= 0; i--) { if (bits_[i].wire != NULL && pattern.count(bits_[i])) { + modified = true; bits_.erase(bits_.begin() + i); - width_--; if (other != NULL) { + other_modified = true; other->bits_.erase(other->bits_.begin() + i); - other->width_--; } } } + if (modified) { + hash_.clear(); + try_repack(); + } + if (other_modified) { + other->hash_.clear(); + other->try_repack(); + } check(); } @@ -4931,21 +4980,31 @@ void RTLIL::SigSpec::remove2(const std::set &pattern, RTLIL::SigS unpack(); if (other != NULL) { - log_assert(width_ == other->width_); + log_assert(size() == other->size()); other->unpack(); } + bool modified = false; + bool other_modified = false; for (int i = GetSize(bits_) - 1; i >= 0; i--) { if (bits_[i].wire != NULL && pattern.count(bits_[i])) { + modified = true; bits_.erase(bits_.begin() + i); - width_--; if (other != NULL) { + other_modified = true; other->bits_.erase(other->bits_.begin() + i); - other->width_--; } } } + if (modified) { + hash_.clear(); + try_repack(); + } + if (other_modified) { + other->hash_.clear(); + other->try_repack(); + } check(); } @@ -4959,21 +5018,31 @@ void RTLIL::SigSpec::remove2(const pool &pattern, RTLIL::SigSpec * unpack(); if (other != NULL) { - log_assert(width_ == other->width_); + log_assert(size() == other->size()); other->unpack(); } + bool modified = false; + bool other_modified = false; for (int i = GetSize(bits_) - 1; i >= 0; i--) { if (bits_[i].wire != NULL && pattern.count(bits_[i].wire)) { + modified = true; bits_.erase(bits_.begin() + i); - width_--; if (other != NULL) { + other_modified = true; other->bits_.erase(other->bits_.begin() + i); - other->width_--; } } } + if (modified) { + hash_.clear(); + try_repack(); + } + if (other_modified) { + other->hash_.clear(); + other->try_repack(); + } check(); } @@ -4984,7 +5053,7 @@ RTLIL::SigSpec RTLIL::SigSpec::extract(const RTLIL::SigSpec &pattern, const RTLI else cover("kernel.rtlil.sigspec.extract"); - log_assert(other == NULL || width_ == other->width_); + log_assert(other == NULL || size() == other->size()); RTLIL::SigSpec ret; std::vector bits_match = to_sigbit_vector(); @@ -4992,19 +5061,22 @@ RTLIL::SigSpec RTLIL::SigSpec::extract(const RTLIL::SigSpec &pattern, const RTLI for (auto& pattern_chunk : pattern.chunks()) { if (other) { std::vector bits_other = other->to_sigbit_vector(); - for (int i = 0; i < width_; i++) - if (bits_match[i].wire && - bits_match[i].wire == pattern_chunk.wire && - bits_match[i].offset >= pattern_chunk.offset && - bits_match[i].offset < pattern_chunk.offset + pattern_chunk.width) + int i = 0; + for (const RTLIL::SigBit &bit : bits_match) { + if (bit.wire && + bit.wire == pattern_chunk.wire && + bit.offset >= pattern_chunk.offset && + bit.offset < pattern_chunk.offset + pattern_chunk.width) ret.append(bits_other[i]); + ++i; + } } else { - for (int i = 0; i < width_; i++) - if (bits_match[i].wire && - bits_match[i].wire == pattern_chunk.wire && - bits_match[i].offset >= pattern_chunk.offset && - bits_match[i].offset < pattern_chunk.offset + pattern_chunk.width) - ret.append(bits_match[i]); + for (const RTLIL::SigBit &bit : bits_match) + if (bit.wire && + bit.wire == pattern_chunk.wire && + bit.offset >= pattern_chunk.offset && + bit.offset < pattern_chunk.offset + pattern_chunk.width) + ret.append(bit); } } @@ -5019,20 +5091,23 @@ RTLIL::SigSpec RTLIL::SigSpec::extract(const pool &pattern, const else cover("kernel.rtlil.sigspec.extract"); - log_assert(other == NULL || width_ == other->width_); + log_assert(other == NULL || size() == other->size()); std::vector bits_match = to_sigbit_vector(); RTLIL::SigSpec ret; if (other) { std::vector bits_other = other->to_sigbit_vector(); - for (int i = 0; i < width_; i++) - if (bits_match[i].wire && pattern.count(bits_match[i])) + int i = 0; + for (const RTLIL::SigBit &bit : bits_match) { + if (bit.wire && pattern.count(bit)) ret.append(bits_other[i]); + ++i; + } } else { - for (int i = 0; i < width_; i++) - if (bits_match[i].wire && pattern.count(bits_match[i])) - ret.append(bits_match[i]); + for (const RTLIL::SigBit &bit : bits_match) + if (bit.wire && pattern.count(bit)) + ret.append(bit); } ret.check(); @@ -5043,56 +5118,52 @@ void RTLIL::SigSpec::replace(int offset, const RTLIL::SigSpec &with) { cover("kernel.rtlil.sigspec.replace_pos"); + if (with.size() == 0) + return; + unpack(); - with.unpack(); log_assert(offset >= 0); - log_assert(with.width_ >= 0); - log_assert(offset+with.width_ <= width_); + log_assert(with.size() >= 0); + log_assert(offset+with.size() <= size()); - for (int i = 0; i < with.width_; i++) - bits_.at(offset + i) = with.bits_.at(i); + int i = 0; + for (const RTLIL::SigBit &bit : with.bits()) { + bits_.at(offset + i) = bit; + ++i; + } + hash_.clear(); + try_repack(); check(); } void RTLIL::SigSpec::remove_const() { - if (packed()) + if (rep_ == CHUNK) { cover("kernel.rtlil.sigspec.remove_const.packed"); - std::vector new_chunks; - new_chunks.reserve(GetSize(chunks_)); - - width_ = 0; - for (auto &chunk : chunks_) - if (chunk.wire != NULL) { - if (!new_chunks.empty() && - new_chunks.back().wire == chunk.wire && - new_chunks.back().offset + new_chunks.back().width == chunk.offset) { - new_chunks.back().width += chunk.width; - } else { - new_chunks.push_back(chunk); - } - width_ += chunk.width; - } - - chunks_.swap(new_chunks); + if (chunk_.wire == NULL) { + chunk_.~SigChunk(); + init_empty_bits(); + hash_.clear(); + } } else { cover("kernel.rtlil.sigspec.remove_const.unpacked"); std::vector new_bits; - new_bits.reserve(width_); - + new_bits.reserve(bits_.size()); for (auto &bit : bits_) if (bit.wire != NULL) new_bits.push_back(bit); - - bits_.swap(new_bits); - width_ = bits_.size(); + if (GetSize(new_bits) != GetSize(bits_)) { + bits_.swap(new_bits); + hash_.clear(); + try_repack(); + } } check(); @@ -5102,15 +5173,19 @@ void RTLIL::SigSpec::remove(int offset, int length) { cover("kernel.rtlil.sigspec.remove_pos"); + if (length == 0) + return; + unpack(); log_assert(offset >= 0); log_assert(length >= 0); - log_assert(offset + length <= width_); + log_assert(offset + length <= size()); bits_.erase(bits_.begin() + offset, bits_.begin() + offset + length); - width_ = bits_.size(); + hash_.clear(); + try_repack(); check(); } @@ -5118,106 +5193,113 @@ RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const { log_assert(offset >= 0); log_assert(length >= 0); - log_assert(offset + length <= width_); + log_assert(offset + length <= size()); cover("kernel.rtlil.sigspec.extract_pos"); - if (packed()) { - SigSpec extracted; - extracted.width_ = length; - - auto it = chunks_.begin(); - for (; offset; offset -= it->width, it++) { - if (offset < it->width) { - int chunk_length = min(it->width - offset, length); - extracted.chunks_.emplace_back(it->extract(offset, chunk_length)); - length -= chunk_length; - it++; - break; - } + SigSpec extracted; + Chunks cs = chunks(); + auto it = cs.begin(); + for (; offset; offset -= it->width, ++it) { + if (offset < it->width) { + int chunk_length = min(it->width - offset, length); + extracted.append(it->extract(offset, chunk_length)); + length -= chunk_length; + ++it; + break; } - for (; length; length -= it->width, it++) { - if (length >= it->width) { - extracted.chunks_.emplace_back(*it); - } else { - extracted.chunks_.emplace_back(it->extract(0, length)); - break; - } - } - - return extracted; - } else { - return std::vector(bits_.begin() + offset, bits_.begin() + offset + length); } + for (; length; length -= it->width, ++it) { + if (length >= it->width) { + extracted.append(*it); + } else { + extracted.append(it->extract(0, length)); + break; + } + } + return extracted; +} + +void RTLIL::SigSpec::rewrite_wires(std::function rewrite) +{ + if (rep_ == CHUNK) { + if (chunk_.wire != nullptr) + rewrite(chunk_.wire); + return; + } + + std::vector new_bits; + for (const RTLIL::SigChunk &chunk : chunks()) { + RTLIL::SigChunk c = chunk; + if (c.wire != nullptr) + rewrite(c.wire); + for (int i = 0; i < c.width; i++) + new_bits.emplace_back(c, i); + } + bits_ = std::move(new_bits); + hash_.clear(); } void RTLIL::SigSpec::append(const RTLIL::SigSpec &signal) { - if (signal.width_ == 0) + if (signal.size() == 0) return; - if (width_ == 0) { + if (size() == 0) { *this = signal; return; } cover("kernel.rtlil.sigspec.append"); - if (packed() != signal.packed()) { - pack(); - signal.pack(); + hash_.clear(); + if (rep_ == CHUNK && signal.rep_ == CHUNK && chunk_.wire == signal.chunk_.wire) { + if (chunk_.wire == NULL) { + chunk_.data.insert(chunk_.data.end(), signal.chunk_.data.begin(), signal.chunk_.data.end()); + chunk_.width = GetSize(chunk_.data); + return; + } + if (chunk_.offset + chunk_.width == signal.chunk_.offset) { + chunk_.width += signal.chunk_.width; + return; + } } - if (packed()) - for (auto &other_c : signal.chunks_) - { - auto &my_last_c = chunks_.back(); - if (my_last_c.wire == NULL && other_c.wire == NULL) { - auto &this_data = my_last_c.data; - auto &other_data = other_c.data; - this_data.insert(this_data.end(), other_data.begin(), other_data.end()); - my_last_c.width += other_c.width; - } else - if (my_last_c.wire == other_c.wire && my_last_c.offset + my_last_c.width == other_c.offset) { - my_last_c.width += other_c.width; - } else - chunks_.push_back(other_c); - } - else - bits_.insert(bits_.end(), signal.bits_.begin(), signal.bits_.end()); - - width_ += signal.width_; + unpack(); + for (const SigBit &bit : signal.bits()) + bits_.push_back(bit); check(); } void RTLIL::SigSpec::append(const RTLIL::SigBit &bit) { - if (packed()) - { + hash_.clear(); + + if (size() == 0) { + destroy(); + rep_ = CHUNK; + new (&chunk_) RTLIL::SigChunk(bit); + return; + } + + if (rep_ == CHUNK && chunk_.wire == bit.wire) { cover("kernel.rtlil.sigspec.append_bit.packed"); - if (chunks_.size() == 0) - chunks_.push_back(bit); - else - if (bit.wire == NULL) - if (chunks_.back().wire == NULL) { - chunks_.back().data.push_back(bit.data); - chunks_.back().width++; - } else - chunks_.push_back(bit); - else - if (chunks_.back().wire == bit.wire && chunks_.back().offset + chunks_.back().width == bit.offset) - chunks_.back().width++; - else - chunks_.push_back(bit); - } - else - { - cover("kernel.rtlil.sigspec.append_bit.unpacked"); - bits_.push_back(bit); + if (chunk_.wire == NULL) { + chunk_.data.push_back(bit.data); + chunk_.width++; + return; + } + if (chunk_.offset + chunk_.width == bit.offset) { + chunk_.width++; + return; + } } - width_++; + unpack(); + + cover("kernel.rtlil.sigspec.append_bit.unpacked"); + bits_.push_back(bit); check(); } @@ -5225,19 +5307,16 @@ void RTLIL::SigSpec::extend_u0(int width, bool is_signed) { cover("kernel.rtlil.sigspec.extend_u0"); - pack(); + if (size() > width) + remove(width, size() - width); - if (width_ > width) - remove(width, width_ - width); - - if (width_ < width) { - RTLIL::SigBit padding = width_ > 0 ? (*this)[width_ - 1] : RTLIL::State::Sx; + if (size() < width) { + RTLIL::SigBit padding = size() > 0 ? (*this)[size() - 1] : RTLIL::State::Sx; if (!is_signed) padding = RTLIL::State::S0; - while (width_ < width) + while (size() < width) append(padding); } - } RTLIL::SigSpec RTLIL::SigSpec::repeat(int num) const @@ -5253,50 +5332,36 @@ RTLIL::SigSpec RTLIL::SigSpec::repeat(int num) const #ifndef NDEBUG void RTLIL::SigSpec::check(Module *mod) const { - if (width_ > 64) - { - cover("kernel.rtlil.sigspec.check.skip"); - } - else if (packed()) + if (rep_ == CHUNK) { cover("kernel.rtlil.sigspec.check.packed"); - int w = 0; - for (size_t i = 0; i < chunks_.size(); i++) { - const RTLIL::SigChunk &chunk = chunks_[i]; - log_assert(chunk.width != 0); - if (chunk.wire == NULL) { - if (i > 0) - log_assert(chunks_[i-1].wire != NULL); - log_assert(chunk.offset == 0); - log_assert(chunk.data.size() == (size_t)chunk.width); - } else { - if (i > 0 && chunks_[i-1].wire == chunk.wire) - log_assert(chunk.offset != chunks_[i-1].offset + chunks_[i-1].width); - log_assert(chunk.offset >= 0); - log_assert(chunk.width >= 0); - log_assert(chunk.offset + chunk.width <= chunk.wire->width); - log_assert(chunk.data.size() == 0); - if (mod != nullptr) - log_assert(chunk.wire->module == mod); - } - w += chunk.width; + log_assert(chunk_.width != 0); + if (chunk_.wire == NULL) { + log_assert(chunk_.offset == 0); + log_assert(chunk_.data.size() == (size_t)chunk_.width); + } else { + log_assert(chunk_.offset >= 0); + log_assert(chunk_.width >= 0); + log_assert(chunk_.offset + chunk_.width <= chunk_.wire->width); + log_assert(chunk_.data.size() == 0); + if (mod != nullptr) + log_assert(chunk_.wire->module == mod); } - log_assert(w == width_); - log_assert(bits_.empty()); + } + else if (size() > 64) + { + cover("kernel.rtlil.sigspec.check.skip"); } else { cover("kernel.rtlil.sigspec.check.unpacked"); if (mod != nullptr) { - for (size_t i = 0; i < bits_.size(); i++) - if (bits_[i].wire != nullptr) - log_assert(bits_[i].wire->module == mod); + for (const RTLIL::SigBit &bit : bits_) + if (bit.wire != nullptr) + log_assert(bit.wire->module == mod); } - - log_assert(width_ == GetSize(bits_)); - log_assert(chunks_.empty()); } } #endif @@ -5308,26 +5373,15 @@ bool RTLIL::SigSpec::operator <(const RTLIL::SigSpec &other) const if (this == &other) return false; - if (width_ != other.width_) - return width_ < other.width_; + if (size() != other.size()) + return size() < other.size(); - pack(); - other.pack(); - - if (chunks_.size() != other.chunks_.size()) - return chunks_.size() < other.chunks_.size(); - - updhash(); - other.updhash(); - - if (hash_ != other.hash_) - return hash_ < other.hash_; - - for (size_t i = 0; i < chunks_.size(); i++) - if (chunks_[i] != other.chunks_[i]) { - cover("kernel.rtlil.sigspec.comp_lt.hash_collision"); - return chunks_[i] < other.chunks_[i]; - } + auto other_it = other.chunks().begin(); + for (const SigChunk &c : chunks()) { + if (c != *other_it) + return c < *other_it; + ++other_it; + } cover("kernel.rtlil.sigspec.comp_lt.equal"); return false; @@ -5340,32 +5394,15 @@ bool RTLIL::SigSpec::operator ==(const RTLIL::SigSpec &other) const if (this == &other) return true; - if (width_ != other.width_) + if (size() != other.size()) return false; - // Without this, SigSpec() == SigSpec(State::S0, 0) will fail - // since the RHS will contain one SigChunk of width 0 causing - // the size check below to fail - if (width_ == 0) - return true; - - pack(); - other.pack(); - - if (chunks_.size() != other.chunks_.size()) - return false; - - updhash(); - other.updhash(); - - if (hash_ != other.hash_) - return false; - - for (size_t i = 0; i < chunks_.size(); i++) - if (chunks_[i] != other.chunks_[i]) { - cover("kernel.rtlil.sigspec.comp_eq.hash_collision"); + auto other_it = other.chunks().begin(); + for (const SigChunk &c : chunks()) { + if (c != *other_it) return false; - } + ++other_it; + } cover("kernel.rtlil.sigspec.comp_eq.equal"); return true; @@ -5375,22 +5412,28 @@ bool RTLIL::SigSpec::is_wire() const { cover("kernel.rtlil.sigspec.is_wire"); - pack(); - return GetSize(chunks_) == 1 && chunks_[0].wire && chunks_[0].wire->width == width_; + Chunks cs = chunks(); + auto it = cs.begin(); + if (it == cs.end()) + return false; + const RTLIL::SigChunk &chunk = *it; + return chunk.wire && chunk.wire->width == size() && ++it == cs.end(); } bool RTLIL::SigSpec::is_chunk() const { cover("kernel.rtlil.sigspec.is_chunk"); - pack(); - return GetSize(chunks_) == 1; + Chunks cs = chunks(); + auto it = cs.begin(); + if (it == cs.end()) + return false; + return ++it == cs.end(); } bool RTLIL::SigSpec::known_driver() const { - pack(); - for (auto &chunk : chunks_) + for (auto &chunk : chunks()) if (chunk.is_wire() && !chunk.wire->known_driver()) return false; return true; @@ -5400,9 +5443,8 @@ bool RTLIL::SigSpec::is_fully_const() const { cover("kernel.rtlil.sigspec.is_fully_const"); - pack(); - for (auto it = chunks_.begin(); it != chunks_.end(); it++) - if (it->width > 0 && it->wire != NULL) + for (auto &chunk : chunks()) + if (chunk.width > 0 && chunk.wire != NULL) return false; return true; } @@ -5411,12 +5453,11 @@ bool RTLIL::SigSpec::is_fully_zero() const { cover("kernel.rtlil.sigspec.is_fully_zero"); - pack(); - for (auto it = chunks_.begin(); it != chunks_.end(); it++) { - if (it->width > 0 && it->wire != NULL) + for (auto &chunk : chunks()) { + if (chunk.width > 0 && chunk.wire != NULL) return false; - for (size_t i = 0; i < it->data.size(); i++) - if (it->data[i] != RTLIL::State::S0) + for (RTLIL::State d : chunk.data) + if (d != RTLIL::State::S0) return false; } return true; @@ -5426,12 +5467,11 @@ bool RTLIL::SigSpec::is_fully_ones() const { cover("kernel.rtlil.sigspec.is_fully_ones"); - pack(); - for (auto it = chunks_.begin(); it != chunks_.end(); it++) { - if (it->width > 0 && it->wire != NULL) + for (auto &chunk : chunks()) { + if (chunk.width > 0 && chunk.wire != NULL) return false; - for (size_t i = 0; i < it->data.size(); i++) - if (it->data[i] != RTLIL::State::S1) + for (RTLIL::State d : chunk.data) + if (d != RTLIL::State::S1) return false; } return true; @@ -5441,12 +5481,11 @@ bool RTLIL::SigSpec::is_fully_def() const { cover("kernel.rtlil.sigspec.is_fully_def"); - pack(); - for (auto it = chunks_.begin(); it != chunks_.end(); it++) { - if (it->width > 0 && it->wire != NULL) + for (auto &chunk : chunks()) { + if (chunk.width > 0 && chunk.wire != NULL) return false; - for (size_t i = 0; i < it->data.size(); i++) - if (it->data[i] != RTLIL::State::S0 && it->data[i] != RTLIL::State::S1) + for (RTLIL::State d : chunk.data) + if (d != RTLIL::State::S0 && d != RTLIL::State::S1) return false; } return true; @@ -5456,12 +5495,11 @@ bool RTLIL::SigSpec::is_fully_undef() const { cover("kernel.rtlil.sigspec.is_fully_undef"); - pack(); - for (auto it = chunks_.begin(); it != chunks_.end(); it++) { - if (it->width > 0 && it->wire != NULL) + for (auto &chunk : chunks()) { + if (chunk.width > 0 && chunk.wire != NULL) return false; - for (size_t i = 0; i < it->data.size(); i++) - if (it->data[i] != RTLIL::State::Sx && it->data[i] != RTLIL::State::Sz) + for (RTLIL::State d : chunk.data) + if (d != RTLIL::State::Sx && d != RTLIL::State::Sz) return false; } return true; @@ -5471,9 +5509,8 @@ bool RTLIL::SigSpec::has_const() const { cover("kernel.rtlil.sigspec.has_const"); - pack(); - for (auto it = chunks_.begin(); it != chunks_.end(); it++) - if (it->width > 0 && it->wire == NULL) + for (auto &chunk : chunks()) + if (chunk.width > 0 && chunk.wire == NULL) return true; return false; } @@ -5482,9 +5519,8 @@ bool RTLIL::SigSpec::has_const(State state) const { cover("kernel.rtlil.sigspec.has_const"); - pack(); - for (auto it = chunks_.begin(); it != chunks_.end(); it++) - if (it->width > 0 && it->wire == NULL && std::find(it->data.begin(), it->data.end(), state) != it->data.end()) + for (auto &chunk : chunks()) + if (chunk.width > 0 && chunk.wire == NULL && std::find(chunk.data.begin(), chunk.data.end(), state) != chunk.data.end()) return true; return false; } @@ -5494,11 +5530,10 @@ bool RTLIL::SigSpec::has_marked_bits() const { cover("kernel.rtlil.sigspec.has_marked_bits"); - pack(); - for (auto it = chunks_.begin(); it != chunks_.end(); it++) - if (it->width > 0 && it->wire == NULL) { - for (size_t i = 0; i < it->data.size(); i++) - if (it->data[i] == RTLIL::State::Sm) + for (auto &chunk : chunks()) + if (chunk.width > 0 && chunk.wire == NULL) { + for (RTLIL::State d : chunk.data) + if (d == RTLIL::State::Sm) return true; } return false; @@ -5508,12 +5543,8 @@ bool RTLIL::SigSpec::is_onehot(int *pos) const { cover("kernel.rtlil.sigspec.is_onehot"); - pack(); - if (!is_fully_const()) - return false; - log_assert(GetSize(chunks_) <= 1); - if (width_) - return RTLIL::Const(chunks_[0].data).is_onehot(pos); + if (std::optional c = try_as_const()) + return c->is_onehot(pos); return false; } @@ -5521,74 +5552,58 @@ bool RTLIL::SigSpec::as_bool() const { cover("kernel.rtlil.sigspec.as_bool"); - pack(); - log_assert(is_fully_const() && GetSize(chunks_) <= 1); - if (width_) - return RTLIL::Const(chunks_[0].data).as_bool(); - return false; + std::optional c = try_as_const(); + log_assert(c.has_value()); + return c->as_bool(); } int RTLIL::SigSpec::as_int(bool is_signed) const { cover("kernel.rtlil.sigspec.as_int"); - pack(); - log_assert(is_fully_const() && GetSize(chunks_) <= 1); - if (width_) - return RTLIL::Const(chunks_[0].data).as_int(is_signed); - return 0; + std::optional c = try_as_const(); + log_assert(c.has_value()); + return c->as_int(is_signed); } bool RTLIL::SigSpec::convertible_to_int(bool is_signed) const { cover("kernel.rtlil.sigspec.convertible_to_int"); - pack(); - if (!is_fully_const()) + std::optional c = try_as_const(); + if (!c.has_value()) return false; - - if (empty()) - return true; - - return RTLIL::Const(chunks_[0].data).convertible_to_int(is_signed); + return c->convertible_to_int(is_signed); } std::optional RTLIL::SigSpec::try_as_int(bool is_signed) const { cover("kernel.rtlil.sigspec.try_as_int"); - pack(); - if (!is_fully_const()) + std::optional c = try_as_const(); + if (!c.has_value()) return std::nullopt; - - if (empty()) - return 0; - - return RTLIL::Const(chunks_[0].data).try_as_int(is_signed); + return c->try_as_int(is_signed); } int RTLIL::SigSpec::as_int_saturating(bool is_signed) const { cover("kernel.rtlil.sigspec.try_as_int"); - pack(); - log_assert(is_fully_const() && GetSize(chunks_) <= 1); - - if (empty()) - return 0; - - return RTLIL::Const(chunks_[0].data).as_int_saturating(is_signed); + std::optional c = try_as_const(); + log_assert(c.has_value()); + return c->as_int_saturating(is_signed); } std::string RTLIL::SigSpec::as_string() const { cover("kernel.rtlil.sigspec.as_string"); - pack(); std::string str; str.reserve(size()); - for (size_t i = chunks_.size(); i > 0; i--) { - const RTLIL::SigChunk &chunk = chunks_[i-1]; + std::vector chunks = *this; + for (size_t i = chunks.size(); i > 0; i--) { + const RTLIL::SigChunk &chunk = chunks[i-1]; if (chunk.wire != NULL) str.append(chunk.width, '?'); else @@ -5597,67 +5612,82 @@ std::string RTLIL::SigSpec::as_string() const return str; } +std::optional RTLIL::SigSpec::try_as_const() const +{ + cover("kernel.rtlil.sigspec.as_const"); + + Chunks cs = chunks(); + auto it = cs.begin(); + if (it == cs.end()) + return RTLIL::Const(); + SigChunk chunk = *it; + if (chunk.wire != NULL || ++it != cs.end()) + return std::nullopt; + return RTLIL::Const(std::move(chunk.data)); +} + RTLIL::Const RTLIL::SigSpec::as_const() const { cover("kernel.rtlil.sigspec.as_const"); - pack(); - log_assert(is_fully_const() && GetSize(chunks_) <= 1); - if (width_) - return chunks_[0].data; - return RTLIL::Const(); + std::optional c = try_as_const(); + log_assert(c.has_value()); + return *c; } RTLIL::Wire *RTLIL::SigSpec::as_wire() const { cover("kernel.rtlil.sigspec.as_wire"); - pack(); - log_assert(is_wire()); - return chunks_[0].wire; + Chunks cs = chunks(); + auto it = cs.begin(); + log_assert(it != cs.end()); + RTLIL::SigChunk chunk = *it; + log_assert(++it == cs.end() && chunk.wire && chunk.wire->width == size()); + return chunk.wire; } RTLIL::SigChunk RTLIL::SigSpec::as_chunk() const { cover("kernel.rtlil.sigspec.as_chunk"); - pack(); - log_assert(is_chunk()); - return chunks_[0]; + Chunks cs = chunks(); + auto it = cs.begin(); + log_assert(it != cs.end()); + RTLIL::SigChunk chunk = *it; + log_assert(++it == cs.end()); + return chunk; } RTLIL::SigBit RTLIL::SigSpec::as_bit() const { cover("kernel.rtlil.sigspec.as_bit"); - - log_assert(width_ == 1); - if (packed()) - return RTLIL::SigBit(*chunks_.begin()); - else - return bits_[0]; + return RTLIL::SigBit(*this); } bool RTLIL::SigSpec::match(const char* pattern) const { cover("kernel.rtlil.sigspec.match"); - unpack(); - log_assert(int(strlen(pattern)) == GetSize(bits_)); + int pattern_len = strlen(pattern); + log_assert(pattern_len == size()); - for (auto it = bits_.rbegin(); it != bits_.rend(); it++, pattern++) { - if (*pattern == ' ') + for (int i = 0; i < pattern_len; ++i) { + char ch = pattern[i]; + if (ch == ' ') continue; - if (*pattern == '*') { - if (*it != State::Sz && *it != State::Sx) + RTLIL::SigBit bit = (*this)[pattern_len - 1 - i]; + if (ch == '*') { + if (bit != State::Sz && bit != State::Sx) return false; continue; } - if (*pattern == '0') { - if (*it != State::S0) + if (ch == '0') { + if (bit != State::S0) return false; } else - if (*pattern == '1') { - if (*it != State::S1) + if (ch == '1') { + if (bit != State::S1) return false; } else log_abort(); @@ -5670,9 +5700,8 @@ std::set RTLIL::SigSpec::to_sigbit_set() const { cover("kernel.rtlil.sigspec.to_sigbit_set"); - pack(); std::set sigbits; - for (auto &c : chunks_) + for (auto &c : chunks()) for (int i = 0; i < c.width; i++) sigbits.insert(RTLIL::SigBit(c, i)); return sigbits; @@ -5682,10 +5711,9 @@ pool RTLIL::SigSpec::to_sigbit_pool() const { cover("kernel.rtlil.sigspec.to_sigbit_pool"); - pack(); pool sigbits; sigbits.reserve(size()); - for (auto &c : chunks_) + for (auto &c : chunks()) for (int i = 0; i < c.width; i++) sigbits.insert(RTLIL::SigBit(c, i)); return sigbits; @@ -5695,22 +5723,23 @@ std::vector RTLIL::SigSpec::to_sigbit_vector() const { cover("kernel.rtlil.sigspec.to_sigbit_vector"); - unpack(); - return bits_; + std::vector result; + result.reserve(size()); + for (SigBit bit : *this) + result.push_back(bit); + return result; } std::map RTLIL::SigSpec::to_sigbit_map(const RTLIL::SigSpec &other) const { cover("kernel.rtlil.sigspec.to_sigbit_map"); - unpack(); - other.unpack(); - - log_assert(width_ == other.width_); + int this_size = size(); + log_assert(this_size == other.size()); std::map new_map; - for (int i = 0; i < width_; i++) - new_map[bits_[i]] = other.bits_[i]; + for (int i = 0; i < this_size; i++) + new_map[(*this)[i]] = other[i]; return new_map; } @@ -5719,15 +5748,13 @@ dict RTLIL::SigSpec::to_sigbit_dict(const RTLIL::S { cover("kernel.rtlil.sigspec.to_sigbit_dict"); - unpack(); - other.unpack(); - - log_assert(width_ == other.width_); + int this_size = size(); + log_assert(this_size == other.size()); dict new_map; - new_map.reserve(size()); - for (int i = 0; i < width_; i++) - new_map[bits_[i]] = other.bits_[i]; + new_map.reserve(this_size); + for (int i = 0; i < this_size; i++) + new_map[(*this)[i]] = other[i]; return new_map; } @@ -5852,21 +5879,21 @@ bool RTLIL::SigSpec::parse_rhs(const RTLIL::SigSpec &lhs, RTLIL::SigSpec &sig, R { if (str == "0") { cover("kernel.rtlil.sigspec.parse.rhs_zeros"); - sig = RTLIL::SigSpec(RTLIL::State::S0, lhs.width_); + sig = RTLIL::SigSpec(RTLIL::State::S0, lhs.size()); return true; } if (str == "~0") { cover("kernel.rtlil.sigspec.parse.rhs_ones"); - sig = RTLIL::SigSpec(RTLIL::State::S1, lhs.width_); + sig = RTLIL::SigSpec(RTLIL::State::S1, lhs.size()); return true; } - if (lhs.chunks_.size() == 1) { + if (lhs.is_chunk()) { char *p = (char*)str.c_str(), *endptr; long int val = strtol(p, &endptr, 10); if (endptr && endptr != p && *endptr == 0) { - sig = RTLIL::SigSpec(val, lhs.width_); + sig = RTLIL::SigSpec(val, lhs.size()); cover("kernel.rtlil.sigspec.parse.rhs_dec"); return true; } @@ -5874,11 +5901,19 @@ bool RTLIL::SigSpec::parse_rhs(const RTLIL::SigSpec &lhs, RTLIL::SigSpec &sig, R if (!parse(sig, module, str)) return false; - if (sig.width_ > lhs.width_) - sig.remove(lhs.width_, sig.width_ - lhs.width_); + if (sig.size() > lhs.size()) + sig.remove(lhs.size(), sig.size() - lhs.size()); return true; } +RTLIL::SigSpec::operator std::vector() const +{ + std::vector result; + for (const RTLIL::SigChunk &c : chunks()) + result.push_back(c); + return result; +} + RTLIL::CaseRule::~CaseRule() { for (auto it = switches.begin(); it != switches.end(); it++) diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 394c6f25d..79cc73e45 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -569,6 +569,7 @@ template <> struct IDMacroHelper<-1> { } }; +#undef ID #define ID(_id) \ YOSYS_NAMESPACE_PREFIX IDMacroHelper< \ YOSYS_NAMESPACE_PREFIX lookup_well_known_id(#_id) \ @@ -1222,9 +1223,10 @@ struct RTLIL::SigSpecConstIterator typedef RTLIL::SigBit& reference; const RTLIL::SigSpec *sig_p; + RTLIL::SigBit bit; int index; - inline const RTLIL::SigBit &operator*() const; + inline const RTLIL::SigBit &operator*(); inline bool operator!=(const RTLIL::SigSpecConstIterator &other) const { return index != other.index; } inline bool operator==(const RTLIL::SigSpecIterator &other) const { return index == other.index; } inline void operator++() { index++; } @@ -1233,33 +1235,84 @@ struct RTLIL::SigSpecConstIterator struct RTLIL::SigSpec { private: - int width_; - Hasher::hash_t hash_; - std::vector chunks_; // LSB at index 0 - std::vector bits_; // LSB at index 0 + enum Representation : char { + CHUNK, + BITS, + }; + // An AtomicHash is either clear or a nonzero integer. + struct AtomicHash { + // Create an initially clear value. + AtomicHash() : atomic_(0) {} + AtomicHash(const AtomicHash &rhs) : atomic_(rhs.load()) {} + AtomicHash &operator=(const AtomicHash &rhs) { store(rhs.load()); return *this; } + // Read the hash. Returns nullopt if the hash is clear. + std::optional read() const { + Hasher::hash_t value = load(); + if (value == 0) + return std::nullopt; + return value; + } + // Set the hash. If the value is already set, then the new value must + // equal the current value. + void set(Hasher::hash_t value) const { + log_assert(value != 0); + Hasher::hash_t old = const_cast&>(atomic_) + .exchange(value, std::memory_order_relaxed); + log_assert(old == 0 || old == value); + } + void clear() { store(0); } + private: + int load() const { return atomic_.load(std::memory_order_relaxed); } + void store(Hasher::hash_t value) const { + const_cast&>(atomic_).store(value, std::memory_order_relaxed); + } - void pack() const; - void unpack() const; - void updhash() const; + std::atomic atomic_; + }; - inline bool packed() const { - return bits_.empty(); + Representation rep_; + AtomicHash hash_; + union { + RTLIL::SigChunk chunk_; + std::vector bits_; // LSB at index 0 + }; + + void init_empty_bits() { + rep_ = BITS; + new (&bits_) std::vector; } - inline void inline_unpack() const { - if (!chunks_.empty()) + void unpack(); + inline void inline_unpack() { + if (rep_ == CHUNK) unpack(); } + void try_repack(); - // Only used by Module::remove(const pool &wires) - // but cannot be more specific as it isn't yet declared - friend struct RTLIL::Module; + Hasher::hash_t updhash() const; + void destroy() { + if (rep_ == CHUNK) + chunk_.~SigChunk(); + else + bits_.~vector(); + } + friend struct Chunks; public: - SigSpec() : width_(0), hash_(0) {} + SigSpec() { init_empty_bits(); } SigSpec(std::initializer_list parts); - SigSpec(const SigSpec &value) = default; - SigSpec(SigSpec &&value) = default; + SigSpec(const SigSpec &value) : rep_(value.rep_), hash_(value.hash_) { + if (value.rep_ == CHUNK) + new (&chunk_) RTLIL::SigChunk(value.chunk_); + else + new (&bits_) std::vector(value.bits_); + } + SigSpec(SigSpec &&value) : rep_(value.rep_), hash_(value.hash_) { + if (value.rep_ == CHUNK) + new (&chunk_) RTLIL::SigChunk(std::move(value.chunk_)); + else + new (&bits_) std::vector(std::move(value.bits_)); + } SigSpec(const RTLIL::Const &value); SigSpec(RTLIL::Const &&value); SigSpec(const RTLIL::SigChunk &chunk); @@ -1275,24 +1328,138 @@ public: SigSpec(const pool &bits); SigSpec(const std::set &bits); explicit SigSpec(bool bit); + ~SigSpec() { + destroy(); + } - SigSpec &operator=(const SigSpec &rhs) = default; - SigSpec &operator=(SigSpec &&rhs) = default; + SigSpec &operator=(const SigSpec &rhs) { + destroy(); + rep_ = rhs.rep_; + hash_ = rhs.hash_; + if (rep_ == CHUNK) + new (&chunk_) RTLIL::SigChunk(rhs.chunk_); + else + new (&bits_) std::vector(rhs.bits_); + return *this; + } + SigSpec &operator=(SigSpec &&rhs) { + destroy(); + rep_ = rhs.rep_; + hash_ = rhs.hash_; + if (rep_ == CHUNK) + new (&chunk_) RTLIL::SigChunk(std::move(rhs.chunk_)); + else + new (&bits_) std::vector(std::move(rhs.bits_)); + return *this; + } - inline const std::vector &chunks() const { pack(); return chunks_; } - inline const std::vector &bits() const { inline_unpack(); return bits_; } + // SigSpec::Chunks holds one reconstructed chunk at a time + // to provide the SigSpec::chunks() read-only chunks view + // since vector SigSpec::chunks_ has been removed + struct Chunks { + Chunks(const SigSpec &spec) : spec(spec) {} + struct const_iterator { + using iterator_category = std::forward_iterator_tag; + using value_type = const SigChunk &; + using difference_type = std::ptrdiff_t; + using pointer = const SigChunk *; + using reference = const SigChunk &; - inline int size() const { return width_; } - inline bool empty() const { return width_ == 0; } + const SigSpec &spec; + int bit_index; + SigChunk chunk; - inline RTLIL::SigBit &operator[](int index) { inline_unpack(); return bits_.at(index); } - inline const RTLIL::SigBit &operator[](int index) const { inline_unpack(); return bits_.at(index); } + const_iterator(const SigSpec &spec) : spec(spec) { + bit_index = 0; + if (spec.rep_ == BITS) + next_chunk_bits(); + } + enum End { END }; + const_iterator(const SigSpec &spec, End) : spec(spec) { + bit_index = spec.size(); + } + void next_chunk_bits(); + + const SigChunk &operator*() { + if (spec.rep_ == CHUNK) + return spec.chunk_; + return chunk; + }; + const SigChunk *operator->() { return &**this; } + const_iterator &operator++() { + bit_index += (**this).width; + if (spec.rep_ == BITS) + next_chunk_bits(); + return *this; + } + bool operator==(const const_iterator &rhs) const { return bit_index == rhs.bit_index; } + bool operator!=(const const_iterator &rhs) const { return !(*this == rhs); } + }; + const_iterator begin() const { return const_iterator(spec); } + const_iterator end() const { + const_iterator it(spec, const_iterator::END); + return it; + } + // Later we should deprecate these and remove their in-tree calls, + // so we can eventually remove chunk_vector. + std::vector::const_reverse_iterator rbegin() { + ensure_chunk_vector(); + return chunk_vector.rbegin(); + } + std::vector::const_reverse_iterator rend() { + ensure_chunk_vector(); + return chunk_vector.rend(); + } + int size() { + ensure_chunk_vector(); + return chunk_vector.size(); + } + int size() const { + return std::distance(begin(), end()); + } + const SigChunk &at(int index) { + ensure_chunk_vector(); + return chunk_vector.at(index); + } + operator const std::vector&() { + ensure_chunk_vector(); + return chunk_vector; + } + private: + void ensure_chunk_vector() { + if (spec.size() > 0 && chunk_vector.empty()) { + for (const RTLIL::SigChunk &c : *this) + chunk_vector.push_back(c); + } + } + const SigSpec &spec; + std::vector chunk_vector; + }; + friend struct Chunks::const_iterator; + + inline Chunks chunks() const { return {*this}; } + inline const SigSpec &bits() const { return *this; } + + inline int size() const { return rep_ == CHUNK ? chunk_.width : GetSize(bits_); } + inline bool empty() const { return size() == 0; }; + + inline RTLIL::SigBit &operator[](int index) { inline_unpack(); hash_.clear(); return bits_.at(index); } + inline RTLIL::SigBit operator[](int index) const { + if (rep_ == CHUNK) { + if (index < 0 || index >= chunk_.width) + throw std::out_of_range("SigSpec::operator[]"); + if (chunk_.wire) + return RTLIL::SigBit(chunk_.wire, chunk_.offset + index); + return RTLIL::SigBit(chunk_.data[index]); + } + return bits_.at(index); + } inline RTLIL::SigSpecIterator begin() { RTLIL::SigSpecIterator it; it.sig_p = this; it.index = 0; return it; } - inline RTLIL::SigSpecIterator end() { RTLIL::SigSpecIterator it; it.sig_p = this; it.index = width_; return it; } + inline RTLIL::SigSpecIterator end() { RTLIL::SigSpecIterator it; it.sig_p = this; it.index = size(); return it; } inline RTLIL::SigSpecConstIterator begin() const { RTLIL::SigSpecConstIterator it; it.sig_p = this; it.index = 0; return it; } - inline RTLIL::SigSpecConstIterator end() const { RTLIL::SigSpecConstIterator it; it.sig_p = this; it.index = width_; return it; } + inline RTLIL::SigSpecConstIterator end() const { RTLIL::SigSpecConstIterator it; it.sig_p = this; it.index = size(); return it; } void sort(); void sort_and_unify(); @@ -1324,10 +1491,14 @@ public: RTLIL::SigSpec extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other = NULL) const; RTLIL::SigSpec extract(const pool &pattern, const RTLIL::SigSpec *other = NULL) const; RTLIL::SigSpec extract(int offset, int length = 1) const; - RTLIL::SigSpec extract_end(int offset) const { return extract(offset, width_ - offset); } + RTLIL::SigSpec extract_end(int offset) const { return extract(offset, size() - offset); } - RTLIL::SigBit lsb() const { log_assert(width_); return (*this)[0]; }; - RTLIL::SigBit msb() const { log_assert(width_); return (*this)[width_ - 1]; }; + void rewrite_wires(std::function rewrite); + + RTLIL::SigBit lsb() const { log_assert(size()); return (*this)[0]; }; + RTLIL::SigBit msb() const { log_assert(size()); return (*this)[size() - 1]; }; + RTLIL::SigBit front() const { return (*this)[0]; } + RTLIL::SigBit back() const { return (*this)[size() - 1]; } void append(const RTLIL::SigSpec &signal); inline void append(Wire *wire) { append(RTLIL::SigSpec(wire)); } @@ -1350,7 +1521,7 @@ public: bool is_wire() const; bool is_chunk() const; - inline bool is_bit() const { return width_ == 1; } + inline bool is_bit() const { return size() == 1; } bool known_driver() const; @@ -1387,6 +1558,9 @@ public: int as_int_saturating(bool is_signed = false) const; std::string as_string() const; + // Returns std::nullopt if there are any non-constant bits. Returns an empty + // Const if this has zero width. + std::optional try_as_const() const; RTLIL::Const as_const() const; RTLIL::Wire *as_wire() const; RTLIL::SigChunk as_chunk() const; @@ -1404,11 +1578,19 @@ public: static bool parse_sel(RTLIL::SigSpec &sig, RTLIL::Design *design, RTLIL::Module *module, std::string str); static bool parse_rhs(const RTLIL::SigSpec &lhs, RTLIL::SigSpec &sig, RTLIL::Module *module, std::string str); - operator std::vector() const { return chunks(); } - operator std::vector() const { return bits(); } - const RTLIL::SigBit &at(int offset, const RTLIL::SigBit &defval) { return offset < width_ ? (*this)[offset] : defval; } + operator std::vector() const; + operator std::vector() const { return to_sigbit_vector(); } + const RTLIL::SigBit &at(int offset, const RTLIL::SigBit &defval) { return offset < size() ? (*this)[offset] : defval; } - [[nodiscard]] Hasher hash_into(Hasher h) const { if (!hash_) updhash(); h.eat(hash_); return h; } + [[nodiscard]] Hasher hash_into(Hasher h) const { + Hasher::hash_t val; + if (std::optional current = hash_.read()) + val = *current; + else + val = updhash(); + h.eat(val); + return h; + } #ifndef NDEBUG void check(Module *mod = nullptr) const; @@ -1827,6 +2009,7 @@ public: RTLIL::Cell *addCell(RTLIL::IdString name, RTLIL::IdString type); RTLIL::Cell *addCell(RTLIL::IdString name, const RTLIL::Cell *other); + RTLIL::Memory *addMemory(RTLIL::IdString name); RTLIL::Memory *addMemory(RTLIL::IdString name, const RTLIL::Memory *other); RTLIL::Process *addProcess(RTLIL::IdString name); @@ -2312,13 +2495,15 @@ inline RTLIL::SigBit &RTLIL::SigSpecIterator::operator*() const { return (*sig_p)[index]; } -inline const RTLIL::SigBit &RTLIL::SigSpecConstIterator::operator*() const { - return (*sig_p)[index]; +inline const RTLIL::SigBit &RTLIL::SigSpecConstIterator::operator*() { + bit = (*sig_p)[index]; + return bit; } inline RTLIL::SigBit::SigBit(const RTLIL::SigSpec &sig) { - log_assert(sig.size() == 1 && sig.chunks().size() == 1); - *this = SigBit(sig.chunks().front()); + log_assert(sig.size() == 1); + auto it = sig.chunks().begin(); + *this = SigBit(*it); } template diff --git a/kernel/utils.h b/kernel/utils.h index 5c739aceb..e90ba09d8 100644 --- a/kernel/utils.h +++ b/kernel/utils.h @@ -277,14 +277,26 @@ inline int ceil_log2(int x) #endif } +template +auto reversed(T& container) { + struct reverse_view { + reverse_view(T& container) : container(container) {} + auto begin() const { return container.rbegin(); } + auto end() const { return container.rend(); } + T& container; + }; + return reverse_view{container}; +} + template auto reversed(const T& container) { - struct reverse_view { - const T& cont; - auto begin() const { return cont.rbegin(); } - auto end() const { return cont.rend(); } - }; - return reverse_view{container}; + struct reverse_view { + reverse_view(const T& container) : container(container) {} + auto begin() const { return container.rbegin(); } + auto end() const { return container.rend(); } + const T& container; + }; + return reverse_view{container}; } YOSYS_NAMESPACE_END diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index d0d9c0f85..14a251c41 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -278,11 +278,12 @@ struct ShowWorker std::vector label_pieces; int bitpos = sig.size()-1; - for (int rep, chunk_idx = ((int) sig.chunks().size()) - 1; chunk_idx >= 0; chunk_idx -= rep) { - const RTLIL::SigChunk &c = sig.chunks().at(chunk_idx); + RTLIL::SigSpec::Chunks sig_chunks = sig.chunks(); + for (int rep, chunk_idx = ((int) sig_chunks.size()) - 1; chunk_idx >= 0; chunk_idx -= rep) { + const RTLIL::SigChunk &c = sig_chunks.at(chunk_idx); // Find the number of times this chunk is repeating - for (rep = 1; chunk_idx - rep >= 0 && c == sig.chunks().at(chunk_idx - rep); rep++); + for (rep = 1; chunk_idx - rep >= 0 && c == sig_chunks.at(chunk_idx - rep); rep++); int cl, cr; cl = c.offset + c.width - 1; diff --git a/passes/cmds/timeest.cc b/passes/cmds/timeest.cc index ce9c628f4..1caa1ddaf 100644 --- a/passes/cmds/timeest.cc +++ b/passes/cmds/timeest.cc @@ -39,7 +39,8 @@ const arrivalint INF_PAST = std::numeric_limits::min(); struct EstimateSta { SigMap sigmap; Module *m; - SigBit clk; + std::optional clk; + bool top_port_endpoints = false; dict>, Aig> aigs; dict cell_aigs; @@ -73,15 +74,18 @@ struct EstimateSta { } // TODO: ignores clock polarity - EstimateSta(Module *m, SigBit clk) - : sigmap(m), m(m), clk(clk) + EstimateSta(Module *m, std::optional clk, bool top_port_endpoints) + : sigmap(m), m(m), clk(clk), top_port_endpoints(top_port_endpoints) { - sigmap.apply(clk); + if (clk.has_value()) + sigmap.apply(*clk); } void run() { - log("Domain %s\n", log_signal(clk)); + log("\nModule %s\n", log_id(m)); + if (clk.has_value()) + log("Domain %s\n", log_signal(*clk)); // first, we collect launch and sample points and convert the combinational logic to AIG std::vector combinational; @@ -151,6 +155,22 @@ struct EstimateSta { } } + // add top module port launching/sampling, if requested + if (top_port_endpoints) { + SigSpec all_inputs, all_outputs; + for (auto port_id : m->ports) { + Wire *port = m->wire(port_id); + if (port->port_input && !port->port_output) { + all_inputs.append(port); + } else if (port->port_output && !port->port_input) { + all_outputs.append(port); + } else if (port->port_output && port->port_input) { + log_warning("Ignoring bi-directional port %s\n", log_id(port)); + } + } + add_seq(nullptr, all_inputs, all_outputs); + } + // now we toposort the combinational logic // each toposort node is either a SigBit or a pair of Cell * / AigNode * @@ -360,7 +380,7 @@ struct TimeestPass : Pass { log("\n"); log(" timeest [-clk ] [options] [selection]\n"); log("\n"); - log("Estimate the critical path in clock domain by counting AIG nodes.\n"); + log("Estimate the critical path by counting AIG nodes.\n"); log("\n"); log(" -all_paths\n"); log(" Print or select nodes from all critical paths instead of focusing on\n"); @@ -374,7 +394,8 @@ struct TimeestPass : Pass { { log_header(d, "Executing TIMEEST pass. (estimate timing)\n"); - std::string clk; + std::string clk_name; + bool clk_domain_specified = false; bool all_paths = false; bool select = false; size_t argidx; @@ -388,26 +409,30 @@ struct TimeestPass : Pass { continue; } if (args[argidx] == "-clk" && argidx + 1 < args.size()) { - clk = args[++argidx]; + clk_domain_specified = true; + clk_name = args[++argidx]; continue; } break; } extra_args(args, argidx, d); - if (clk.empty()) - log_cmd_error("No -clk argument provided\n"); - if (select && d->selected_modules().size() > 1) log_cmd_error("The -select option operates on a single selected module\n"); for (auto m : d->selected_modules()) { - if (!m->wire(RTLIL::escape_id(clk))) { - log_warning("No domain '%s' in module %s\n", clk, log_id(m)); - continue; + std::optional clk; + + if (clk_domain_specified) { + if (!m->wire(RTLIL::escape_id(clk_name))) { + log_warning("No domain '%s' in module %s\n", clk_name.c_str(), log_id(m)); + continue; + } + + clk = SigBit(m->wire(RTLIL::escape_id(clk_name)), 0); } - EstimateSta sta(m, SigBit(m->wire(RTLIL::escape_id(clk)), 0)); + EstimateSta sta(m, clk, /*top_port_endpoints=*/ !clk_domain_specified); sta.all_paths = all_paths; sta.select = select; sta.run(); diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index ea68add18..416997bee 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -156,6 +156,21 @@ std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) { return basicType; } +// Try to read an IdString as a numbered connection name ("$123" or similar), +// writing the result to dst. If the string isn't of the right format, ignore +// dst and return false. +bool read_id_num(RTLIL::IdString str, int *dst) +{ + log_assert(dst); + + const char *c_str = str.c_str(); + if (c_str[0] != '$' || !('0' <= c_str[1] && c_str[1] <= '9')) + return false; + + *dst = atoi(c_str + 1); + return true; +} + // A helper struct for expanding a module's interface connections in expand_module struct IFExpander { @@ -283,15 +298,42 @@ struct IFExpander RTLIL::IdString conn_name, const RTLIL::SigSpec &conn_signals) { - // Check if the connection is present as an interface in the sub-module's port list - const RTLIL::Wire *wire = submodule.wire(conn_name); - if (!wire || !wire->get_bool_attribute(ID::is_interface)) + // Does the connection look like an interface + if ( + conn_signals.size() != 1 || + conn_signals[0].wire == nullptr || + conn_signals[0].wire->get_bool_attribute(ID::is_interface) == false || + conn_signals[0].wire->name.str().find("$dummywireforinterface") != 0 + ) return; + // Check if the connection is present as an interface in the sub-module's port list + int id; + if (read_id_num(conn_name, &id)) { + /* Interface expansion is incompatible with positional arguments + * during expansion, the port list gets each interface signal + * inserted after the interface itself which means that the argument + * positions in the parent module no longer match. + * + * Supporting this would require expanding the interfaces in the + * parent module, renumbering the arguments to match, and then + * iterating over the ports list to find the matching interface + * (refactoring on_interface to accept different conn_names on the + * parent and child). + */ + log_error("Unable to connect `%s' to submodule `%s' with positional interface argument `%s'!\n", + module.name, + submodule.name, + conn_signals[0].wire->name.str().substr(23) + ); + } else { + // Lookup connection by name + const RTLIL::Wire *wire = submodule.wire(conn_name); + if (!wire || !wire->get_bool_attribute(ID::is_interface)) + return; + } // If the connection looks like an interface, handle it. - const auto &bits = conn_signals; - if (bits.size() == 1 && bits[0].wire->get_bool_attribute(ID::is_interface)) - on_interface(submodule, conn_name, conn_signals); + on_interface(submodule, conn_name, conn_signals); } // Iterate over the connections in a cell, tracking any interface @@ -376,21 +418,6 @@ RTLIL::Module *get_module(RTLIL::Design &design, return nullptr; } -// Try to read an IdString as a numbered connection name ("$123" or similar), -// writing the result to dst. If the string isn't of the right format, ignore -// dst and return false. -bool read_id_num(RTLIL::IdString str, int *dst) -{ - log_assert(dst); - - const char *c_str = str.c_str(); - if (c_str[0] != '$' || !('0' <= c_str[1] && c_str[1] <= '9')) - return false; - - *dst = atoi(c_str + 1); - return true; -} - // Check that the connections on the cell match those that are defined // on the type: each named connection should match the name of a port // and each positional connection should have an index smaller than diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index c5c653317..e2894aee3 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -117,15 +117,6 @@ struct gate_t std::string bit_str; }; -bool map_mux4; -bool map_mux8; -bool map_mux16; - -bool markgroups; - -pool enabled_gates; -bool cmos_cost; - struct AbcConfig { std::string global_tempdir_name; @@ -146,6 +137,12 @@ struct AbcConfig bool show_tempdir = false; bool sop_mode = false; bool abc_dress = false; + bool map_mux4 = false; + bool map_mux8 = false; + bool map_mux16 = false; + bool markgroups = false; + pool enabled_gates; + bool cmos_cost = false; }; struct AbcSigVal { @@ -1382,7 +1379,7 @@ void emit_global_input_files(const AbcConfig &config) fprintf(f, "%d %d.00 1.00\n", i+1, config.lut_costs.at(i)); fclose(f); } else { - auto &cell_cost = cmos_cost ? CellCosts::cmos_gate_cost() : CellCosts::default_gate_cost(); + auto &cell_cost = config.cmos_cost ? CellCosts::cmos_gate_cost() : CellCosts::default_gate_cost(); std::string buffer = stringf("%s/stdcells.genlib", config.global_tempdir_name.c_str()); FILE *f = fopen(buffer.c_str(), "wt"); @@ -1392,39 +1389,39 @@ void emit_global_input_files(const AbcConfig &config) fprintf(f, "GATE ONE 1 Y=CONST1;\n"); fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", cell_cost.at(ID($_BUF_))); fprintf(f, "GATE NOT %d Y=!A; PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_NOT_))); - if (enabled_gates.count("AND")) + if (config.enabled_gates.count("AND")) fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", cell_cost.at(ID($_AND_))); - if (enabled_gates.count("NAND")) + if (config.enabled_gates.count("NAND")) fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_NAND_))); - if (enabled_gates.count("OR")) + if (config.enabled_gates.count("OR")) fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", cell_cost.at(ID($_OR_))); - if (enabled_gates.count("NOR")) + if (config.enabled_gates.count("NOR")) fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_NOR_))); - if (enabled_gates.count("XOR")) + if (config.enabled_gates.count("XOR")) fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_XOR_))); - if (enabled_gates.count("XNOR")) + if (config.enabled_gates.count("XNOR")) fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_XNOR_))); - if (enabled_gates.count("ANDNOT")) + if (config.enabled_gates.count("ANDNOT")) fprintf(f, "GATE ANDNOT %d Y=A*!B; PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_ANDNOT_))); - if (enabled_gates.count("ORNOT")) + if (config.enabled_gates.count("ORNOT")) fprintf(f, "GATE ORNOT %d Y=A+!B; PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_ORNOT_))); - if (enabled_gates.count("AOI3")) + if (config.enabled_gates.count("AOI3")) fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_AOI3_))); - if (enabled_gates.count("OAI3")) + if (config.enabled_gates.count("OAI3")) fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_OAI3_))); - if (enabled_gates.count("AOI4")) + if (config.enabled_gates.count("AOI4")) fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_AOI4_))); - if (enabled_gates.count("OAI4")) + if (config.enabled_gates.count("OAI4")) fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_OAI4_))); - if (enabled_gates.count("MUX")) + if (config.enabled_gates.count("MUX")) fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_MUX_))); - if (enabled_gates.count("NMUX")) + if (config.enabled_gates.count("NMUX")) fprintf(f, "GATE NMUX %d Y=!((A*B)+(S*B)+(!S*A)); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_NMUX_))); - if (map_mux4) + if (config.map_mux4) fprintf(f, "GATE MUX4 %d Y=(!S*!T*A)+(S*!T*B)+(!S*T*C)+(S*T*D); PIN * UNKNOWN 1 999 1 0 1 0\n", 2*cell_cost.at(ID($_MUX_))); - if (map_mux8) + if (config.map_mux8) fprintf(f, "GATE MUX8 %d Y=(!S*!T*!U*A)+(S*!T*!U*B)+(!S*T*!U*C)+(S*T*!U*D)+(!S*!T*U*E)+(S*!T*U*F)+(!S*T*U*G)+(S*T*U*H); PIN * UNKNOWN 1 999 1 0 1 0\n", 4*cell_cost.at(ID($_MUX_))); - if (map_mux16) + if (config.map_mux16) fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*cell_cost.at(ID($_MUX_))); fclose(f); } @@ -1456,6 +1453,7 @@ void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL RTLIL::Module *mapped_mod = mapped_design->module(ID(netlist)); if (mapped_mod == nullptr) log_error("ABC output file does not contain a module `netlist'.\n"); + bool markgroups = run_abc.config.markgroups; for (auto w : mapped_mod->wires()) { RTLIL::Wire *orig_wire = nullptr; RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire)); @@ -1998,9 +1996,9 @@ struct AbcPass : public Pass { lut_arg = design->scratchpad_get_string("abc.lut", lut_arg); luts_arg = design->scratchpad_get_string("abc.luts", luts_arg); config.sop_mode = design->scratchpad_get_bool("abc.sop", false); - map_mux4 = design->scratchpad_get_bool("abc.mux4", map_mux4); - map_mux8 = design->scratchpad_get_bool("abc.mux8", map_mux8); - map_mux16 = design->scratchpad_get_bool("abc.mux16", map_mux16); + config.map_mux4 = design->scratchpad_get_bool("abc.mux4", false); + config.map_mux8 = design->scratchpad_get_bool("abc.mux8", false); + config.map_mux16 = design->scratchpad_get_bool("abc.mux16", false); config.abc_dress = design->scratchpad_get_bool("abc.dress", false); g_arg = design->scratchpad_get_string("abc.g", g_arg); @@ -2014,7 +2012,7 @@ struct AbcPass : public Pass { config.keepff = design->scratchpad_get_bool("abc.keepff", false); config.cleanup = !design->scratchpad_get_bool("abc.nocleanup", false); config.show_tempdir = design->scratchpad_get_bool("abc.showtmp", false); - markgroups = design->scratchpad_get_bool("abc.markgroups", markgroups); + config.markgroups = design->scratchpad_get_bool("abc.markgroups", false); if (config.cleanup) config.global_tempdir_name = get_base_tmpdir() + "/"; @@ -2094,15 +2092,15 @@ struct AbcPass : public Pass { continue; } if (arg == "-mux4") { - map_mux4 = true; + config.map_mux4 = true; continue; } if (arg == "-mux8") { - map_mux8 = true; + config.map_mux8 = true; continue; } if (arg == "-mux16") { - map_mux16 = true; + config.map_mux16 = true; continue; } if (arg == "-dress") { @@ -2143,7 +2141,7 @@ struct AbcPass : public Pass { continue; } if (arg == "-markgroups") { - markgroups = true; + config.markgroups = true; continue; } break; @@ -2236,14 +2234,14 @@ struct AbcPass : public Pass { } if (g == "cmos2") { if (!remove_gates) - cmos_cost = true; + config.cmos_cost = true; gate_list.push_back("NAND"); gate_list.push_back("NOR"); goto ok_alias; } if (g == "cmos3") { if (!remove_gates) - cmos_cost = true; + config.cmos_cost = true; gate_list.push_back("NAND"); gate_list.push_back("NOR"); gate_list.push_back("AOI3"); @@ -2252,7 +2250,7 @@ struct AbcPass : public Pass { } if (g == "cmos4") { if (!remove_gates) - cmos_cost = true; + config.cmos_cost = true; gate_list.push_back("NAND"); gate_list.push_back("NOR"); gate_list.push_back("AOI3"); @@ -2263,7 +2261,7 @@ struct AbcPass : public Pass { } if (g == "cmos") { if (!remove_gates) - cmos_cost = true; + config.cmos_cost = true; gate_list.push_back("NAND"); gate_list.push_back("NOR"); gate_list.push_back("AOI3"); @@ -2322,9 +2320,9 @@ struct AbcPass : public Pass { ok_alias: for (auto gate : gate_list) { if (remove_gates) - enabled_gates.erase(gate); + config.enabled_gates.erase(gate); else - enabled_gates.insert(gate); + config.enabled_gates.insert(gate); } } } @@ -2334,21 +2332,21 @@ struct AbcPass : public Pass { if (!config.constr_file.empty() && (config.liberty_files.empty() && config.genlib_files.empty())) log_cmd_error("Got -constr but no -liberty/-genlib!\n"); - if (enabled_gates.empty()) { - enabled_gates.insert("AND"); - enabled_gates.insert("NAND"); - enabled_gates.insert("OR"); - enabled_gates.insert("NOR"); - enabled_gates.insert("XOR"); - enabled_gates.insert("XNOR"); - enabled_gates.insert("ANDNOT"); - enabled_gates.insert("ORNOT"); - // enabled_gates.insert("AOI3"); - // enabled_gates.insert("OAI3"); - // enabled_gates.insert("AOI4"); - // enabled_gates.insert("OAI4"); - enabled_gates.insert("MUX"); - // enabled_gates.insert("NMUX"); + if (config.enabled_gates.empty()) { + config.enabled_gates.insert("AND"); + config.enabled_gates.insert("NAND"); + config.enabled_gates.insert("OR"); + config.enabled_gates.insert("NOR"); + config.enabled_gates.insert("XOR"); + config.enabled_gates.insert("XNOR"); + config.enabled_gates.insert("ANDNOT"); + config.enabled_gates.insert("ORNOT"); + // config.enabled_gates.insert("AOI3"); + // config.enabled_gates.insert("OAI3"); + // config.enabled_gates.insert("AOI4"); + // config.enabled_gates.insert("OAI4"); + config.enabled_gates.insert("MUX"); + // config.enabled_gates.insert("NMUX"); } emit_global_input_files(config); diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc index 373d5d15e..8d3869ece 100644 --- a/passes/techmap/abc9_ops.cc +++ b/passes/techmap/abc9_ops.cc @@ -1428,13 +1428,13 @@ void reintegrate(RTLIL::Module *module, bool dff_mode) // Copy connections (and rename) from mapped_mod to module for (auto conn : mapped_mod->connections()) { if (!conn.first.is_fully_const()) { - auto chunks = conn.first.chunks(); + std::vector chunks = conn.first.chunks(); for (auto &c : chunks) c.wire = module->wires_.at(remap_name(c.wire->name)); conn.first = std::move(chunks); } if (!conn.second.is_fully_const()) { - auto chunks = conn.second.chunks(); + std::vector chunks = conn.second.chunks(); for (auto &c : chunks) if (c.wire) c.wire = module->wires_.at(remap_name(c.wire->name)); diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index f2bd16082..e8fc6fc12 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -127,7 +127,7 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std return false; } - auto pin_names = pool{}; + auto pin_names = std::unordered_set{}; tree.get_pin_names(pin_names); // from the `ff` block, we know the flop output signal name for loopback. @@ -156,7 +156,7 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std auto pins = std::vector(pin_names.begin(), pin_names.end()); int lut = 0; for (int n = 0; n < 8; n++) { - auto values = dict{}; + auto values = std::unordered_map{}; values.insert(std::make_pair(pins[0], (n & 1) == 1)); values.insert(std::make_pair(pins[1], (n & 2) == 2)); values.insert(std::make_pair(ff_output, (n & 4) == 4)); @@ -271,6 +271,13 @@ static void find_cell(std::vector cells, IdString cell_type, continue; if (!parse_next_state(cell, ff->find("next_state"), cell_next_pin, cell_next_pol, cell_enable_pin, cell_enable_pol) || (has_enable && (cell_enable_pin.empty() || cell_enable_pol != enapol))) continue; + + if (has_reset && !cell_next_pol) { + // next_state is negated + // we later propagate this inversion to the output, + // which requires the negation of the reset value + rstval = !rstval; + } if (has_reset && rstval == false) { if (!parse_pin(cell, ff->find("clear"), cell_rst_pin, cell_rst_pol) || cell_rst_pol != rstpol) continue; diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 7d4fd77ad..a9ae75c01 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -25,9 +25,22 @@ #include #include #include +#include -#ifndef FILTERLIB +#ifdef FILTERLIB +#undef log_assert +void log_assert(bool cond) { + if (!cond) + fprintf(stderr, "Unspecified assertion failed\n"); +} +void warn(std::string str) { + std::cerr << str; +} +#else #include "kernel/log.h" +void warn(std::string str) { + Yosys::log_formatted_warning("", str); +} #endif using namespace Yosys; @@ -110,7 +123,9 @@ int LibertyInputStream::peek_cold(size_t offset) if (!extend_buffer_at_least(offset + 1)) return EOF; } - +#ifdef log_assert + log_assert(buf_pos + offset < buffer.size()); +#endif return buffer[buf_pos + offset]; } @@ -162,13 +177,15 @@ void LibertyAst::dump(FILE *f, sieve &blacklist, sieve &whitelist, std::string i fprintf(f, " ;\n"); } -#ifndef FILTERLIB - // binary operators excluding ' ' -bool LibertyExpression::is_nice_binop(char c) { +bool LibertyExpression::char_is_nice_binop(char c) { return c == '*' || c == '&' || c == '^' || c == '+' || c == '|'; } +bool LibertyExpression::is_binop() { + return kind == AND || kind == OR || kind == XOR; +} + // https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { if (s.empty()) @@ -177,7 +194,7 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { char c = s.peek(); auto lhs = LibertyExpression{}; - while (isspace(c)) { + while (isspace(c) || c == '"') { if (s.empty()) return lhs; s.next(); @@ -191,7 +208,9 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { s.next(); lhs = parse(s); if (s.peek() != ')') { - log_warning("expected ')' instead of '%c' while parsing Liberty expression '%s'\n", s.peek(), s.full_expr()); + std::stringstream ss; + ss << "expected ')' instead of " << s.peek() << " while parsing Liberty expression '" << s.full_expr() << "'\n"; + warn(ss.str()); return lhs; } s.next(); @@ -200,10 +219,11 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { lhs.kind = Kind::NOT; lhs.children.push_back(parse(s, 7)); } else { - log_warning("unrecognised character '%c' while parsing Liberty expression '%s'\n", c, s.full_expr()); + std::stringstream ss; + ss << "unrecognised character " << c << " while parsing Liberty expression " << s.full_expr() << "\n"; + warn(ss.str()); return lhs; } - while (true) { if (s.empty()) break; @@ -246,9 +266,10 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { s.next(); c = s.peek(); } - if (is_nice_binop(c)) { + if (char_is_nice_binop(c) || c == ')' || c == '\'' || c == '\"') { // We found a real binop, so this space wasn't an AND // and we just discard it as meaningless whitespace + // Tail operators also imply this isn't an AND continue; } } else { @@ -286,7 +307,7 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) { return lhs; } -void LibertyExpression::get_pin_names(pool& names) { +void LibertyExpression::get_pin_names(std::unordered_set& names) { if (kind == Kind::PIN) { names.insert(name); } else { @@ -295,7 +316,7 @@ void LibertyExpression::get_pin_names(pool& names) { } } -bool LibertyExpression::eval(dict& values) { +bool LibertyExpression::eval(std::unordered_map& values) { bool result = false; switch (kind) { case Kind::AND: @@ -324,7 +345,7 @@ bool LibertyExpression::eval(dict& values) { return false; } -std::string LibertyExpression::str(int indent) +std::string LibertyExpression::sexpr_str(int indent) { std::string prefix; switch (kind) { @@ -355,16 +376,55 @@ std::string LibertyExpression::str(int indent) if (!first) { prefix += "\n" + std::string(indent + add_indent, ' '); } - prefix += child.str(indent + add_indent); + prefix += child.sexpr_str(indent + add_indent); first = false; } prefix += ")"; return prefix; } -#endif +std::string LibertyExpression::vlog_str() +{ + std::string prefix; + if (kind != PIN) + prefix += "("; + if (is_binop()) { + log_assert(children.size() == 2); + prefix += children[0].vlog_str(); + switch (kind) { + case AND: + prefix += "&"; + break; + case OR: + prefix += "|"; + break; + case XOR: + prefix += "^"; + break; + default: + log_assert(false); + } + prefix += children[1].vlog_str(); + } else { + switch (kind) { + case NOT: + log_assert(children.size() == 1); + prefix += "~"; + prefix += children[0].vlog_str(); + break; + case PIN: + prefix += name; + break; + default: + log_assert(false); + } + } + if (kind != PIN) + prefix += ")"; + return prefix; +} -int LibertyParser::lexer(std::string &str) +int LibertyParser::lexer_inner(std::string &str) { int c; @@ -390,11 +450,9 @@ int LibertyParser::lexer(std::string &str) if (str == "+" || str == "-") { /* Single operator is not an identifier */ - // fprintf(stderr, "LEX: char >>%s<<\n", str.c_str()); return str[0]; } else { - // fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str()); return 'v'; } } @@ -406,20 +464,19 @@ int LibertyParser::lexer(std::string &str) while (true) { c = f.peek(i); line += (c == '\n'); - if (c != '"') + if (c != '"' && c != EOF) i += 1; else break; } str.clear(); -#ifdef FILTERLIB - f.unget(); - str.append(f.buffered_data(), f.buffered_data() + i + 2); - f.consume(i + 2); -#else str.append(f.buffered_data(), f.buffered_data() + i); - f.consume(i + 1); + // Usage in filterlib is expected to retain quotes + // but yosys expects to get unquoted +#ifdef FILTERLIB + str = "\"" + str + "\""; #endif + f.consume(i + 1); return 'v'; } @@ -442,13 +499,12 @@ int LibertyParser::lexer(std::string &str) return lexer(str); } f.unget(); - // fprintf(stderr, "LEX: char >>/<<\n"); return '/'; // a single '/' charater. } // check for a backslash if (c == '\\') { - c = f.get(); + c = f.get(); if (c == '\r') c = f.get(); if (c == '\n') { @@ -467,14 +523,22 @@ int LibertyParser::lexer(std::string &str) // anything else, such as ';' will get passed // through as literal items. - - // if (c >= 32 && c < 255) - // fprintf(stderr, "LEX: char >>%c<<\n", c); - // else - // fprintf(stderr, "LEX: char %d\n", c); return c; } +int LibertyParser::lexer(std::string &str) +{ + int ret = lexer_inner(str); + // if (ret >= 32 && ret < 255) { + // fprintf(stdout, "LEX: ret >>%c<<\n", ret); + // } else if (ret == 'v') { + // fprintf(stdout, "LEX: ret v str %s\n", str.c_str()); + // } else { + // fprintf(stdout, "LEX: ret %d\n", ret); + // } + return ret; +} + void LibertyParser::report_unexpected_token(int tok) { std::string eReport; @@ -545,6 +609,25 @@ void LibertyParser::parse_vector_range(int tok) } } +// Consume into out_str any string-ish tokens, seperated with spaces +// to cope with abuse of the underdefined spec by real world PDKs +// enabled by proprietary implementations. +// Sorry. +int LibertyParser::consume_wrecked_str(int tok, std::string& out_str) { + std::string str = ""; + while (tok != ';' && tok != EOF && tok != 'n') { + out_str += " "; + if (tok == 'v') + out_str += str; + else + out_str += tok; + tok = lexer(str); + } + if (tok == EOF) + error("wrecked string EOF"); + return tok; +} + LibertyAst *LibertyParser::parse(bool top_level) { std::string str; @@ -591,7 +674,14 @@ LibertyAst *LibertyParser::parse(bool top_level) if (tok == '[') { parse_vector_range(tok); tok = lexer(str); + } else { + // Hack for when an expression string is unquoted + tok = consume_wrecked_str(tok, ast->value); } + } else if (tok == '(') { + // Hack for when an expression string is unquoted and starts with + // parentheses + tok = consume_wrecked_str(tok, ast->value); } while (tok == '+' || tok == '-' || tok == '*' || tok == '/' || tok == '!') { ast->value += tok; @@ -601,7 +691,7 @@ LibertyAst *LibertyParser::parse(bool top_level) ast->value += str; tok = lexer(str); } - + // In a liberty file, all key : value pairs should end in ';' // However, there are some liberty files in the wild that // just have a newline. We'll be kind and accept a newline @@ -621,11 +711,11 @@ LibertyAst *LibertyParser::parse(bool top_level) continue; if (tok == ')') break; - + if (tok == '[') { parse_vector_range(tok); - continue; + continue; } if (tok == 'n') continue; @@ -727,42 +817,13 @@ const LibertyAst *find_non_null(const LibertyAst *node, const char *name) std::string func2vl(std::string str) { - for (size_t pos = str.find_first_of("\" \t"); pos != std::string::npos; pos = str.find_first_of("\" \t")) { - char c_left = pos > 0 ? str[pos-1] : ' '; - char c_right = pos+1 < str.size() ? str[pos+1] : ' '; - if (std::string("\" \t*+").find(c_left) != std::string::npos) - str.erase(pos, 1); - else if (std::string("\" \t*+").find(c_right) != std::string::npos) - str.erase(pos, 1); - else - str[pos] = '*'; - } - - std::vector group_start; - for (size_t pos = 0; pos < str.size(); pos++) { - if (str[pos] == '(') - group_start.push_back(pos); - if (str[pos] == ')' && group_start.size() > 0) { - if (pos+1 < str.size() && str[pos+1] == '\'') { - std::string group = str.substr(group_start.back(), pos-group_start.back()+1); - str[group_start.back()] = '~'; - str.replace(group_start.back()+1, group.size(), group); - pos++; - } - group_start.pop_back(); - } - if (str[pos] == '\'' && pos > 0) { - size_t start = str.find_last_of("()'*+^&| ", pos-1)+1; - std::string group = str.substr(start, pos-start); - str[start] = '~'; - str.replace(start+1, group.size(), group); - } - if (str[pos] == '*') - str[pos] = '&'; - if (str[pos] == '+') - str[pos] = '|'; - } + auto helper = LibertyExpression::Lexer(str); + return LibertyExpression::parse(helper).vlog_str(); +} +std::string vlog_identifier(std::string str) +{ + str.erase(std::remove(str.begin(), str.end(), '\"'), str.end()); return str; } @@ -772,11 +833,13 @@ void event2vl(const LibertyAst *ast, std::string &edge, std::string &expr) expr.clear(); if (ast != NULL) { - expr = func2vl(ast->value); - if (expr.size() > 0 && expr[0] == '~') - edge = "negedge " + expr.substr(1); + auto helper = LibertyExpression::Lexer(ast->value); + auto parsed = LibertyExpression::parse(helper); + expr = parsed.vlog_str(); + if (parsed.kind == LibertyExpression::Kind::NOT) + edge = "negedge " + parsed.children[0].vlog_str(); else - edge = "posedge " + expr; + edge = "posedge " + parsed.vlog_str(); } } @@ -806,13 +869,13 @@ void gen_verilogsim_cell(const LibertyAst *ast) return; CHECK_NV(ast->args.size(), == 1); - printf("module %s (", ast->args[0].c_str()); + printf("module %s (", vlog_identifier(ast->args[0]).c_str()); bool first = true; for (auto child : ast->children) { if (child->id != "pin") continue; CHECK_NV(child->args.size(), == 1); - printf("%s%s", first ? "" : ", ", child->args[0].c_str()); + printf("%s%s", first ? "" : ", ", vlog_identifier(child->args[0]).c_str()); first = false; } printf(");\n"); @@ -823,7 +886,7 @@ void gen_verilogsim_cell(const LibertyAst *ast) printf(" reg "); first = true; for (auto arg : child->args) { - printf("%s%s", first ? "" : ", ", arg.c_str()); + printf("%s%s", first ? "" : ", ", vlog_identifier(arg).c_str()); first = false; } printf(";\n"); @@ -835,9 +898,10 @@ void gen_verilogsim_cell(const LibertyAst *ast) CHECK_NV(child->args.size(), == 1); const LibertyAst *dir = find_non_null(child, "direction"); const LibertyAst *func = child->find("function"); - printf(" %s %s;\n", dir->value.c_str(), child->args[0].c_str()); + std::string var = vlog_identifier(child->args[0]); + printf(" %s %s;\n", dir->value.c_str(), var.c_str()); if (func != NULL) - printf(" assign %s = %s; // %s\n", child->args[0].c_str(), func2vl(func->value).c_str(), func->value.c_str()); + printf(" assign %s = %s; // %s\n", var.c_str(), func2vl(func->value).c_str(), func->value.c_str()); } for (auto child : ast->children) @@ -845,8 +909,8 @@ void gen_verilogsim_cell(const LibertyAst *ast) if (child->id != "ff" || child->args.size() != 2) continue; - std::string iq_var = child->args[0]; - std::string iqn_var = child->args[1]; + std::string iq_var = vlog_identifier(child->args[0]); + std::string iqn_var = vlog_identifier(child->args[1]); std::string clock_edge, clock_expr; event2vl(child->find("clocked_on"), clock_edge, clock_expr); @@ -909,8 +973,8 @@ void gen_verilogsim_cell(const LibertyAst *ast) if (child->id != "latch" || child->args.size() != 2) continue; - std::string iq_var = child->args[0]; - std::string iqn_var = child->args[1]; + std::string iq_var = vlog_identifier(child->args[0]); + std::string iqn_var = vlog_identifier(child->args[1]); std::string enable_edge, enable_expr; event2vl(child->find("enable"), enable_edge, enable_expr); diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 44b5d3d47..674484dad 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -63,7 +63,7 @@ namespace Yosys } std::string pin() { - auto length = s.find_first_of("\t()'!^*& +|"); + auto length = s.find_first_of("\t()'!^*& +|\""); if (length == std::string::npos) { // nothing found so use size of s length = s.size(); @@ -91,11 +91,13 @@ namespace Yosys LibertyExpression() : kind(Kind::EMPTY) {} static LibertyExpression parse(Lexer &s, int min_prio = 0); - void get_pin_names(pool& names); - bool eval(dict& values); - std::string str(int indent = 0); + void get_pin_names(std::unordered_set& names); + bool eval(std::unordered_map& values); + std::string sexpr_str(int indent = 0); + std::string vlog_str(); private: - static bool is_nice_binop(char c); + static bool char_is_nice_binop(char c); + bool is_binop(); }; class LibertyInputStream { @@ -170,10 +172,12 @@ namespace Yosys 'n': newline anything else is a single character. */ + int lexer_inner(std::string &str); int lexer(std::string &str); void report_unexpected_token(int tok); void parse_vector_range(int tok); + int consume_wrecked_str(int tok, std::string& out_str); LibertyAst *parse(bool top_level); void error() const; void error(const std::string &str) const; diff --git a/pyosys/generator.py b/pyosys/generator.py index 15b40a79e..0883a7ba4 100644 --- a/pyosys/generator.py +++ b/pyosys/generator.py @@ -56,6 +56,7 @@ from cxxheaderparser.types import ( Variable, Array, FundamentalSpecifier, + FunctionType, ) __file_dir__ = Path(__file__).absolute().parent @@ -177,11 +178,11 @@ pyosys_headers = [ denylist=frozenset({"bits", "bitvectorize"}), ), PyosysClass("AttrObject", denylist=frozenset({"get_blackbox_attribute"})), - PyosysClass("NamedObject", denylist=frozenset({"get_blackbox_attribute"})), + PyosysClass("NamedObject"), PyosysClass("Selection"), # PyosysClass("Monitor"), # Virtual methods, manually bridged - PyosysClass("CaseRule", denylist=frozenset({"get_blackbox_attribute"})), - PyosysClass("SwitchRule", denylist=frozenset({"get_blackbox_attribute"})), + PyosysClass("CaseRule"), + PyosysClass("SwitchRule"), PyosysClass("SyncRule"), PyosysClass( "Process", @@ -191,7 +192,7 @@ pyosys_headers = [ ), PyosysClass("SigChunk"), PyosysClass("SigBit", hash_expr="s"), - PyosysClass("SigSpec", hash_expr="s"), + PyosysClass("SigSpec", hash_expr="s", denylist={"chunks"}), PyosysClass( "Cell", ref_only=True, @@ -219,7 +220,7 @@ pyosys_headers = [ ), PyosysClass( "Design", - string_expr="s.hashidx_", + string_expr="std::to_string(s.hashidx_)", hash_expr="s", denylist=frozenset({"selected_whole_modules"}), # deprecated ), @@ -241,13 +242,17 @@ class PyosysType: @classmethod def from_type(Self, type_obj, drop_const=False) -> "PyosysType": - const = type_obj.const and not drop_const + const = hasattr(type_obj, "const") and type_obj.const and not drop_const if isinstance(type_obj, Pointer): ptr_to = Self.from_type(type_obj.ptr_to) return Self("ptr", (ptr_to,), const) elif isinstance(type_obj, Reference): ref_to = Self.from_type(type_obj.ref_to) return Self("ref", (ref_to,), const) + elif isinstance(type_obj, FunctionType): + ret_type = Self.from_type(type_obj.return_type) + param_types = (Self.from_type(p.type) for p in type_obj.parameters) + return Self("fn", (ret_type, *param_types), False) assert isinstance( type_obj, Type ), f"unexpected c++ type object of type {type(type_obj)}" @@ -270,6 +275,16 @@ class PyosysType: if title == "Dict": key, value = self.specialization return f"{key.generate_identifier()}To{value.generate_identifier()}{title}" + elif title == "Fn": + identifier = self.specialization[0].generate_identifier() + if identifier == "Void": + identifier = "" + else: + identifier += "From" + identifier += "And".join( + p.generate_identifier() for p in self.specialization[1:] + ) + return identifier return ( "".join(spec.generate_identifier() for spec in self.specialization) + title @@ -283,6 +298,9 @@ class PyosysType: return const_prefix + f"{self.specialization[0].generate_cpp_name()} *" elif self.base == "ref": return const_prefix + f"{self.specialization[0].generate_cpp_name()} &" + elif self.base == "fn": + param_cpp_names = (s.generate_cpp_name() for s in self.specialization[1:]) + return f"{self.specialization[0].generate_cpp_name()}({','.join(param_cpp_names)})" else: return ( const_prefix @@ -301,7 +319,7 @@ class PyosysWrapperGenerator(object): self.f = wrapper_stream self.f_inc = header_stream self.found_containers: Dict[PyosysType, Any] = {} - self.class_registry: Dict[str, ClassScope] = {} + self.class_registry: Dict[str, Tuple[ClassScope, PyosysClass]] = {} # entry point def generate(self): @@ -380,7 +398,7 @@ class PyosysWrapperGenerator(object): if isinstance(type_info, Reference): return PyosysWrapperGenerator.find_containers(containers, type_info.ref_to) if not isinstance(type_info, Type): - return () + return {} segments = type_info.typename.segments containers_found = {} for segment in segments: @@ -411,19 +429,23 @@ class PyosysWrapperGenerator(object): def get_parameter_types(function: Function) -> str: return ", ".join(p.type.format() for p in function.parameters) - def register_containers(self, target: Union[Function, Field, Variable]): + def register_containers(self, target: Union[Function, Field, Variable]) -> bool: supported = ("dict", "idict", "pool", "set", "vector") + found = False if isinstance(target, Function): - self.found_containers.update( - self.find_containers(supported, target.return_type) - ) + return_type_containers = self.find_containers(supported, target.return_type) + found = found or len(return_type_containers) + self.found_containers.update(return_type_containers) for parameter in target.parameters: - self.found_containers.update( - self.find_containers(supported, parameter.type) - ) + parameter_containers = self.find_containers(supported, parameter.type) + found = found or len(parameter_containers) + self.found_containers.update(parameter_containers) else: - self.found_containers.update(self.find_containers(supported, target.type)) + variable_containers = self.find_containers(supported, target.type) + found = found or len(variable_containers) + self.found_containers.update(variable_containers) + return found # processors def get_overload_cast( @@ -470,9 +492,9 @@ class PyosysWrapperGenerator(object): def_args = [f'"{python_function_basename}"'] def_args.append(self.get_overload_cast(function, class_basename)) - for parameter in function.parameters: - # ASSUMPTION: there are no unnamed parameters in the yosys codebase - parameter_arg = f'py::arg("{parameter.name}")' + for i, parameter in enumerate(function.parameters): + name = parameter.name or f"arg{i}" + parameter_arg = f'py::arg("{name}")' if parameter.default is not None: parameter_arg += f" = {parameter.default.format()}" def_args.append(parameter_arg) @@ -525,8 +547,12 @@ class PyosysWrapperGenerator(object): if function.static: definition_fn = "def_static" + definition_args = self.get_definition_args( + function, metadata.name, python_name_override + ) + print( - f"\t\t\t.{definition_fn}({', '.join(self.get_definition_args(function, metadata.name, python_name_override))})", + f"\t\t\t.{definition_fn}({', '.join(definition_args)})", file=self.f, ) @@ -565,7 +591,7 @@ class PyosysWrapperGenerator(object): # care return - self.register_containers(field) + has_containers = self.register_containers(field) definition_fn = f"def_{'readonly' if field.type.const else 'readwrite'}" if field.static: @@ -573,8 +599,13 @@ class PyosysWrapperGenerator(object): field_python_basename = keyword_aliases.get(field.name, field.name) + def_args = [ + f'"{field_python_basename}"', + f"&{metadata.name}::{field.name}", + ] + def_args.append("py::return_value_policy::copy") print( - f'\t\t\t.{definition_fn}("{field_python_basename}", &{metadata.name}::{field.name})', + f"\t\t\t.{definition_fn}({', '.join(def_args)})", file=self.f, ) @@ -603,16 +634,20 @@ class PyosysWrapperGenerator(object): ) def process_class_members( - self, metadata: PyosysClass, cls: ClassScope, basename: str + self, + metadata: PyosysClass, + base_metadata: PyosysClass, + cls: ClassScope, + basename: str, ): for method in cls.methods: - if method.name.segments[-1].name in metadata.denylist: + if method.name.segments[-1].name in base_metadata.denylist: continue self.process_method(metadata, method) visited_anonymous_unions = set() for field_ in cls.fields: - if field_.name in metadata.denylist: + if field_.name in base_metadata.denylist: continue self.process_field(metadata, field_) @@ -627,6 +662,16 @@ class PyosysWrapperGenerator(object): for subfield in subclass.fields: self.process_field(metadata, subfield) + for base in cls.class_decl.bases: + if base.access != "public": + continue + name = base.typename.segments[-1].format() + if processed := self.class_registry.get(name): + base_scope, base_metadata = processed + self.process_class_members( + metadata, base_metadata, base_scope, basename + ) + def process_class( self, metadata: PyosysClass, @@ -638,7 +683,7 @@ class PyosysWrapperGenerator(object): segment.format() for segment in pqname.segments ] basename = full_path.pop() - self.class_registry[basename] = cls + self.class_registry[basename] = (cls, metadata) declaration_namespace = "::".join(full_path) tpl_args = [basename] @@ -649,19 +694,17 @@ class PyosysWrapperGenerator(object): file=self.f, ) - self.process_class_members(metadata, cls, basename) - for base in cls.class_decl.bases: - if base.access != "public": - continue - name = base.typename.segments[-1].format() - if base_scope := self.class_registry.get(name): - self.process_class_members(metadata, base_scope, basename) + self.process_class_members(metadata, metadata, cls, basename) if expr := metadata.string_expr: print( f'\t\t.def("__str__", [](const {basename} &s) {{ return {expr}; }})', file=self.f, ) + print( + f'\t\t.def("__repr__", [](const {basename} &s) {{ std::stringstream ss; ss << "<{basename} " << {expr} << ">"; return ss.str(); }})', + file=self.f, + ) if expr := metadata.hash_expr: print( diff --git a/pyosys/wrappers_tpl.cc b/pyosys/wrappers_tpl.cc index 5e77fef21..aa257e1b6 100644 --- a/pyosys/wrappers_tpl.cc +++ b/pyosys/wrappers_tpl.cc @@ -21,6 +21,12 @@ // #include #include +#include + +// duplicates for LSPs +#include "kernel/register.h" +#include "kernel/yosys_common.h" + #include "pyosys/hashlib.h" namespace py = pybind11; @@ -28,7 +34,7 @@ namespace py = pybind11; USING_YOSYS_NAMESPACE using std::set; -using std::regex; +using std::function; using std::ostream; using namespace RTLIL; diff --git a/pyproject.toml b/pyproject.toml index d5882084c..8893137d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ requires = [ "setuptools>=42", "pybind11>=3,<4", - "cxxheaderparser", + "cxxheaderparser" ] build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index 59c1c4b33..8b786cb32 100644 --- a/setup.py +++ b/setup.py @@ -51,6 +51,7 @@ class libyosys_so_ext(Extension): "ENABLE_TCL=0", "ENABLE_READLINE=0", "ENABLE_EDITLINE=0", + "PYOSYS_USE_UV=0", # + install requires takes its role when building wheels # Always compile and include ABC in wheel "ABCEXTERNAL=", # Show compile commands diff --git a/techlibs/gatemate/Makefile.inc b/techlibs/gatemate/Makefile.inc index aeb318cc9..8a258aec8 100644 --- a/techlibs/gatemate/Makefile.inc +++ b/techlibs/gatemate/Makefile.inc @@ -2,6 +2,9 @@ OBJS += techlibs/gatemate/synth_gatemate.o OBJS += techlibs/gatemate/gatemate_foldinv.o +GENFILES += techlibs/gatemate/lut_tree_cells.genlib +GENFILES += techlibs/gatemate/lut_tree_map.v + $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/reg_map.v)) $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/mux_map.v)) $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/lut_map.v)) @@ -28,3 +31,4 @@ techlibs/gatemate/lut_tree_map.v: techlibs/gatemate/lut_tree_lib.mk $(eval $(call add_gen_share_file,share/gatemate,techlibs/gatemate/lut_tree_cells.genlib)) $(eval $(call add_gen_share_file,share/gatemate,techlibs/gatemate/lut_tree_map.v)) + diff --git a/techlibs/gowin/adc.v b/techlibs/gowin/adc.v new file mode 100644 index 000000000..a5bb13bde --- /dev/null +++ b/techlibs/gowin/adc.v @@ -0,0 +1,260 @@ +/********ADC***********/ +//ADC for LRC,GW5AT-138K +module ADCLRC ( +//Voltage signal source, /mV + input ADCINBK2A, //input from bank2 IO, adc_in_b is the reference + input ADCINBK2B, //input from bank2 IO, adc_in_b is the reference + input ADCINBK3A, //input from bank3 IO, adc_in_b is the reference + input ADCINBK3B, //input from bank3 IO, adc_in_b is the reference + input ADCINBK4A, //input from bank4 IO, adc_in_b is the reference + input ADCINBK4B, //input from bank4 IO, adc_in_b is the reference + input ADCINBK5A, //input from bank5 IO, adc_in_b is the reference + input ADCINBK5B, //input from bank5 IO, adc_in_b is the reference + +//control signal + input [2:0] VSENCTL, //Input source selection (ciu), from 0 to 7: adcv, adct, vdd09_0, vdd09_1, vdd18_0, vdd18_1, vdd33_0, vdd33_1 + + input [9:0] FSCAL_VALUE, // temperature mode 510~948, typical value 730; Voltage mode 452~840, typical value 653 + input [11:0] OFFSET_VALUE, //signed number, temperature mode - 1560~- 760, typical value - 1180; Voltage mode - 410~410, typical value 0 + + input ADCEN, //Enable signal, active high + input CLK, //clk input,1/2 shared + input DRSTN, //0/1 shared, Digital part reset signal, active low + input ADCREQI, //Measurement request signal, valid rising edge, asynchronous signal + + //output + output ADCRDY, //The measurement completion signal, active high + output [13:0] ADCVALUE //The measurement result output, signed number. In voltage mode,/2048 is the actual measured value; In temperature mode,/4 is the actual measured value + +); + + parameter DYN_BKEN = "FALSE";//"FALSE","TRUE"."TRUE",BUF_BK2_EN[0]&BUF_BK3_EN[0]=1 +//Input source selection,1/2 shared + parameter BUF_SERDES_Q1_EN = 3'b000; //[1:0] does not support "11" + parameter BUF_BK2_EN = 6'b000000; //[3:0] Only one bit can be 1 at the same time + parameter BUF_BK3_EN = 6'b000000; //[3:0] Only one bit can be 1 at the same time + parameter BUF_BK4_EN = 6'b000000; //[3:0] Only one bit can be 1 at the same time + parameter BUF_BK5_EN = 6'b000000; //[3:0] Only one bit can be 1 at the same time + parameter BUF_BK10_EN = 5'b00000; //[3:1] Only one bit can be 1 at the same time + parameter BUF_BK11_EN = 5'b00000; //[3:1] Only one bit can be 1 at the same time +//Analog terminal option + parameter CLK_SEL = 1'b0; //Clock source selection. 0,PIOCLK_SEL,1,ciu_clk + parameter PIOCLK_SEL = 1'b0; //Clock source selection. 1/2 shared,0,mck_adc_clk_osc,1,io_clk + parameter VSEN_CTL = 3'b000; //Input source selection + parameter VSEN_CTL_SEL = 1'b0; //vsen_ctl source selection,0,VSEN_CTL,1,ciu_vsen_ctl + parameter ADC_MODE = 1'b0; //Mode selection + parameter DIV_CTL = 2'd0; //clock division,0:/1,1:/2,2:/4,3:/8,Clock after frequency division, 500kHz~8MHz + + //Digital terminal options + parameter SAMPLE_CNT_SEL = 3'd4; //total samples configuration, 0~4:64, 128, 256, 512, 1024 sampling points, and the other values are 2048 sampling points.The total number of samples shall be greater than 7 * sampling period, i.e. SAMPLR_CNT_SEL >= RATE_CHANGE_CTRL-1 + parameter RATE_CHANGE_CTRL = 3'd4; //Sampling period configuration, 0~4:4、8、16、32、64,other values are 128 + + +endmodule + +//ADCULC,GW5AT-138K +module ADCULC ( + + //Voltage signal source, /mV + input ADCINBK6A, //input from bank6 IO, adc_in_b is the reference + input ADCINBK6B, //input from bank6 IO, adc_in_b is the reference + input ADCINBK7A, //input from bank7 IO, adc_in_b is the reference + input ADCINBK7B, //input from bank7 IO, adc_in_b is the reference + + //control signal + input [2:0] VSENCTL, //Input source selection(cib),0~7: vtest, vdd09_0, vdd09_1, vdd09_2, vdd18_0, vdd18_1, reserved, vdd33 + + input [9:0] FSCAL_VALUE, // temperature mode 510~948, typical value 730; Voltage mode 452~840, typical value 653 + input [11:0] OFFSET_VALUE, //signed number, temperature mode - 1560~- 760, typical value - 1180; Voltage mode - 410~410, typical value 0 + + input ADCEN, //Enable signal, active high + input CLK, //clk input + input DRSTN, //0/1 shared, Digital part reset signal, active low + input ADCREQI, //Measurement request signal, valid rising edge, asynchronous signal + output ADCRDY, //The measurement completion signal, active high + output [13:0] ADCVALUE //The measurement result output, signed number. In voltage mode,/2048 is the actual measured value; In temperature mode,/4 is the actual measured value + +); + parameter DYN_BKEN = "FALSE";//"FALSE","TRUE"."TRUE",BUF_BK6_EN[0]&BUF_BK7_EN[0]=1 + +//Input source selection + parameter BUF_VCC_EN = 1'b0; //ulc + parameter BUF_VCCM_EN = 1'b0; //ulc + parameter BUF_MIPI_M0_EN = 3'b000; //ulc,[1:0] Only one bit can be 1 at the same time + parameter BUF_MIPI_M1_EN = 3'b000; //ulc,[1:0] Only one bit can be 1 at the same time + parameter BUF_SERDES_Q0_EN = 3'b000; //ulc,[1:0] Only one bit can be 1 at the same time + parameter BUF_BK6_EN = 6'b000000; //bk6,[3:0] Only one bit can be 1 at the same time + parameter BUF_BK7_EN = 6'b000000; //bk7,[3:0] Only one bit can be 1 at the same time +//Analog terminal option + parameter CLK_SEL = 1'b0; //Clock source selection. 0,PIOCLK_SEL,1,ciu_clk + parameter PIOCLK_SEL = 1'b0; //Clock source selection. 1/2 shared,0,mck_adc_clk_osc,1,io_clk + parameter VSEN_CTL = 3'b000; //Input source selection + parameter VSEN_CTL_SEL = 1'b0; //vsen_ctl source selection,0,VSEN_CTL,1,ciu_vsen_ctl + parameter ADC_MODE = 1'b0; //Mode selection + parameter DIV_CTL = 2'd0; //clock division,0:/1,1:/2,2:/4,3:/8,Clock after frequency division, 500kHz~8MHz + +//Digital terminal options + parameter SAMPLE_CNT_SEL = 3'd4; //total samples configuration, 0~4:64, 128, 256, 512, 1024 sampling points, and the other values are 2048 sampling points.The total number of samples shall be greater than 7 * sampling period, i.e. SAMPLR_CNT_SEL >= RATE_CHANGE_CTRL-1 + parameter RATE_CHANGE_CTRL = 3'd4; //Sampling period configuration, 0~4:4、8、16、32、64,other values are 128 + +endmodule + + +//ADC,GW5A-25 +module ADC ( + + //control signal + input CLK, //clk input + input [2:0] VSENCTL, //Input source selection (ciu), from 0 to 7: glo_left,glo_right,loc_left,vtest,vcc_rt,vccc_rt,vccm_rt,vccx_buf + input ADCMODE, //Mode selection,0:temperature mode,1:voltage mode + input DRSTN, //Digital part reset signal, active low + input ADCREQI, //Measurement request signal, valid rising edge, asynchronous signal + output ADCRDY, //The measurement completion signal, active high + output [13:0] ADCVALUE, //The measurement result output, signed number. In voltage mode,/2048 is the actual measured value; In temperature mode,/4 is the actual measured value + //mdrp + input MDRP_CLK, //mdrp clock + input [7:0] MDRP_WDATA, //mdrp write data + input MDRP_A_INC, //mdrp self-increased address + input [1:0] MDRP_OPCODE, //mdrp opcode + output [7:0] MDRP_RDATA, //mdrp read data + input ADCEN //Enable signal, active high + +); + + //Analog terminal option + parameter CLK_SEL = 1'b0; //时钟源选择,0:osc(2.5MHz),1:CLK + parameter DIV_CTL = 2'd0; //clock division,0:/1,1:/2,2:/4,3:/8,Clock after frequency division, 500kHz~8MHz + + //Input source selection + parameter BUF_EN = 12'b000000000000; // + parameter BUF_BK0_VREF_EN = 1'b0; // + parameter BUF_BK1_VREF_EN = 1'b0; // + parameter BUF_BK2_VREF_EN = 1'b0; // + parameter BUF_BK3_VREF_EN = 1'b0; // + parameter BUF_BK4_VREF_EN = 1'b0; // + parameter BUF_BK5_VREF_EN = 1'b0; // + parameter BUF_BK6_VREF_EN = 1'b0; // + parameter BUF_BK7_VREF_EN = 1'b0; // + + //Digital terminal options + parameter CSR_ADC_MODE = 1'b1; // Mode selection + parameter CSR_VSEN_CTRL = 3'd0; // signal source:vccx/vccio_*/vcc_reg -> 7, signal source:vcc_ext -> 4, others -> 0 + parameter CSR_SAMPLE_CNT_SEL = 3'd4; // total samples configuration, 0~4:64, 128, 256, 512, 1024 sampling points, and the other values are 2048 sampling points.The total number of samples shall be greater than 7 * sampling period, i.e. SAMPLR_CNT_SEL >= RATE_CHANGE_CTRL-1 + parameter CSR_RATE_CHANGE_CTRL = 3'd4; // Sampling period configuration, 0~4:4、8、16、32、64,other values are 128 + parameter CSR_FSCAL = 10'd730; // Parameter 1: temperature mode 510~948, typical value 730; Voltage mode 452~840, typical value 653 + parameter CSR_OFFSET = -12'd1180; // Parameter 2, signed number, temperature mode - 1560~- 760, typical value - 1180; Voltage mode - 410~410, typical value 0 + + +endmodule + + +//ADC_SAR,integrated saradc and adc functions. +module ADC_SAR ( +//`ifdef ADC + input ADCMODE, //Mode selection + input [2:0] VSENCTL, //Input source selection + input CLK, //clk input + //Digital + output ADCENO, //Enable signal, active high + input DRSTN, //Digital part reset signal, active low + input ADCREQI, //Measurement request signal + output ADCRDY, //The measurement completion signal, active high + output [13:0] ADCVALUE, //The measurement result output + input MDRP_CLK, //mdrp clock + input [7:0] MDRP_WDATA, //mdrp write data + input MDRP_A_INC, //mdrp self-increased address + input [1:0] MDRP_OPCODE,//mdrp opcode + output [7:0] MDRP_RDATA, //mdrp read data + //Analog + output ADC1BIT, //Analog data output + output ADCCLKO, //Analog clock output + input ADCENI, //fabric adc enable input +//`endif +// SAR +//`ifdef SARADC + output [12:0] ADCBIT, //Measurement result output + output CLKO, //output clk + output EOC, //The measurement completion signal + input CLKI, //fabric input clk + input [6:0] CHEN, //channel select + input RSTN, //resetn,active low + input SOC //Measurement request signal +//`endif +); + + parameter BUF_EN = 29'b0; // signal source selecor switch +// Δ-Σ +//`ifdef ADC + parameter CLK_SEL = 1'b1; // clk source select + parameter DIV_CTL = 2'd0; // clock division.0:/1,1:/2,2:/4,3:/8 + parameter ADC_EN_SEL = 1'b0; // adc_en source select + parameter PHASE_SEL = 1'b0; // adc internal data phase select + + //Digital terminal options + parameter CSR_ADC_MODE = 1'b1; // Mode selection + parameter CSR_VSEN_CTRL = 3'd0; // signal source:vccx/vccio_*/vcc_reg -> 7, signal source:vcc_ext -> 4, others -> 0 + parameter CSR_SAMPLE_CNT_SEL = 3'd4; // total samples configuration, 0~4:64, 128, 256, 512, 1024 sampling points, and the other values are 2048 sampling points.The total number of samples shall be greater than 7 * sampling period, i.e. SAMPLR_CNT_SEL >= RATE_CHANGE_CTRL-1 + parameter CSR_RATE_CHANGE_CTRL = 3'd4; // Sampling period configuration, 0~4:4、8、16、32、64,other values are 128 + parameter CSR_FSCAL = 10'd730; // Parameter 1: temperature mode 510~948, typical value 730; Voltage mode 452~840, typical value 653 + parameter CSR_OFFSET = -12'd1180; // Parameter 2, signed number, temperature mode - 1560~- 760, typical value - 1180; Voltage mode - 410~410, typical value 0 + +//`endif +// SAR +//`ifdef SARADC + parameter ADC_CLK_DIV = 2'b00; // clock division.00:/1,01:/2,10:/4,11:/8 + parameter ADC_CLKDIV_EN = 1'b0; // clock division enable + parameter CLK_SRC_SEL = 1'b1; // source clock sel + parameter VREF_BUF_EN = 1'b1; // BGR vref buffer enable + parameter COUNT_LEN = 5'b10100; // ADC counter length + parameter DAC_SAMPLE_END = 5'b10010; // DAC sample end point + parameter DAC_SAMPLE_START = 5'b01101; // DAC sample start point + parameter SH_SAMPLE_END = 5'b01011; // SH sample start point + parameter SH_SAMPLE_START = 5'b00001; // SH sample end point + parameter AUTO_CHOP_EN = 1'b0; // auto chop + parameter CHOP_CLK_DIV = 4'b0; // chop clock divider + +//`endif + +endmodule + + +//ADCA,15k +module ADCA ( +// analog + input ADCMODE, //Mode selection + input [2:0] VSENCTL, //Input source selection 0-7: vglo_left, vglo_right, vcc2, vccc3, vccb4, vcc5, vccm6, vccc7 + input CLK, //clk input + input ADCENI, //fabric adc enable input + input PWRON_DYN, //power enable. 1:on 0:off + // digital + input DRSTN, //Digital part reset signal, active low + input ADCREQI, //Measurement request signal + output ADCRDY, //The measurement completion signal, active high + output [13:0] ADCVALUE, //The measurement result output + input MDRP_CLK, //mdrp clock + input [7:0] MDRP_WDATA, //mdrp write data + input MDRP_A_INC, //mdrp self-increased address + input [1:0] MDRP_OPCODE,//mdrp opcode + output [7:0] MDRP_RDATA //mdrp read data +); + + parameter BUF_EN = 20'b0; // signal source selecor switch //[8:0] one-hot; [19:15] one-hot. BUF_EN[6] must be 0 + parameter CLK_SEL = 1'b1; // clk source select + parameter DIV_CTL = 2'd0; // clock division + parameter ADC_EN_SEL = 1'b0; // adc_en source select + parameter PHASE_SEL = 1'b0; // adc internal data phase select + + parameter PWRON_SEL = 1'b0; // power enbale source select. 0:PWRON 1:PWRON_DYN + parameter PWRON = 1'b1; // as PWRON_DYN + parameter LDO_MODE = 1'b0; // LDO mode 0:on(2.5/3.3V) 1:off(1.8V) + + //Digital terminal options + parameter CSR_ADC_MODE = 1'b1; // Mode selection + parameter CSR_VSEN_CTRL = 3'd0; // signal source:vccx/vccio_*/vcc_reg -> 7, signal source:vcc_ext -> 4, others -> 0 + parameter CSR_SAMPLE_CNT_SEL = 3'd4; // total samples configuration, 0~4:64, 128, 256, 512, 1024 sampling points, and the other values are 2048 sampling points.The total number of samples shall be greater than 7 * sampling period, i.e. SAMPLR_CNT_SEL >= RATE_CHANGE_CTRL-1 + parameter CSR_RATE_CHANGE_CTRL = 3'd4; // Sampling period configuration, 0~4:4、8、16、32、64,other values are 128 + parameter CSR_FSCAL = 10'd730; // Parameter 1: temperature mode 510~948, typical value 730; Voltage mode 452~840, typical value 653 + parameter CSR_OFFSET = -12'd1180; // Parameter 2, signed number, temperature mode - 1560~- 760, typical value - 1180; Voltage mode - 410~410, typical value 0 + +endmodule + diff --git a/techlibs/gowin/cells_xtra.py b/techlibs/gowin/cells_xtra.py index 01416268a..7e8253a6b 100644 --- a/techlibs/gowin/cells_xtra.py +++ b/techlibs/gowin/cells_xtra.py @@ -32,6 +32,8 @@ _skip = { # These are already described, no need to extract them from the vendor 'DLNC', 'DLNCE', 'DLNP', 'DLNPE', 'rSDP', 'rSDPX9', 'rROM', 'rROMX9', 'TLVDS_OEN_BK', 'DLL', 'DCC', 'I3C', 'IODELAYA', 'IODELAYC', 'IODELAYB', 'SPMI', 'PLLO', 'DCCG', 'MIPI_DPHY_RX', 'CLKDIVG', 'PWRGRD', 'FLASH96KA', + # ADCs are in a separate file + 'ADCLRC', 'ADCULC', 'ADC', 'ADC_SAR', 'ADCA', } def xtract_cells_decl(dir, fout): fname = os.path.join(dir, 'prim_sim.v') @@ -94,3 +96,11 @@ if __name__ == '__main__': fout.write('// Created by cells_xtra.py\n') fout.write('\n') xtract_cells_decl(dir, fout) + if family == 'gw5a': + fout.write('\n') + fout.write('// Added from adc.v\n') + fout.write('\n') + with open(f'adc.v', 'r') as fin: + for l in fin: + fout.write(l); + diff --git a/techlibs/gowin/cells_xtra_gw5a.v b/techlibs/gowin/cells_xtra_gw5a.v index b4df1f33e..b2dc06236 100644 --- a/techlibs/gowin/cells_xtra_gw5a.v +++ b/techlibs/gowin/cells_xtra_gw5a.v @@ -1720,89 +1720,6 @@ input SPIAD; input LOAD; endmodule -module ADCLRC (...); - parameter DYN_BKEN = "FALSE"; - parameter BUF_SERDES_Q1_EN = 3'b000; - parameter BUF_BK2_EN = 6'b000000; - parameter BUF_BK3_EN = 6'b000000; - parameter BUF_BK4_EN = 6'b000000; - parameter BUF_BK5_EN = 6'b000000; - parameter BUF_BK10_EN = 5'b00000; - parameter BUF_BK11_EN = 5'b00000; - parameter CLK_SEL = 1'b0; - parameter PIOCLK_SEL = 1'b0; - parameter VSEN_CTL = 3'b000; - parameter VSEN_CTL_SEL = 1'b0; - parameter ADC_MODE = 1'b0; - parameter DIV_CTL = 2'd0; - parameter SAMPLE_CNT_SEL = 3'd4; - parameter RATE_CHANGE_CTRL = 3'd4; -endmodule - -module ADCULC (...); - parameter DYN_BKEN = "FALSE"; - parameter BUF_VCC_EN = 1'b0; - parameter BUF_VCCM_EN = 1'b0; - parameter BUF_MIPI_M0_EN = 3'b000; - parameter BUF_MIPI_M1_EN = 3'b000; - parameter BUF_SERDES_Q0_EN = 3'b000; - parameter BUF_BK6_EN = 6'b000000; - parameter BUF_BK7_EN = 6'b000000; - parameter CLK_SEL = 1'b0; - parameter PIOCLK_SEL = 1'b0; - parameter VSEN_CTL = 3'b000; - parameter VSEN_CTL_SEL = 1'b0; - parameter ADC_MODE = 1'b0; - parameter DIV_CTL = 2'd0; - parameter SAMPLE_CNT_SEL = 3'd4; - parameter RATE_CHANGE_CTRL = 3'd4; -endmodule - -module ADC (...); - parameter CLK_SEL = 1'b0; - parameter DIV_CTL = 2'd0; - parameter BUF_EN = 12'b000000000000; - parameter BUF_BK0_VREF_EN = 1'b0; - parameter BUF_BK1_VREF_EN = 1'b0; - parameter BUF_BK2_VREF_EN = 1'b0; - parameter BUF_BK3_VREF_EN = 1'b0; - parameter BUF_BK4_VREF_EN = 1'b0; - parameter BUF_BK5_VREF_EN = 1'b0; - parameter BUF_BK6_VREF_EN = 1'b0; - parameter BUF_BK7_VREF_EN = 1'b0; - parameter CSR_ADC_MODE = 1'b1; - parameter CSR_VSEN_CTRL = 3'd0; - parameter CSR_SAMPLE_CNT_SEL = 3'd4; - parameter CSR_RATE_CHANGE_CTRL = 3'd4; - parameter CSR_FSCAL = 10'd730; - parameter CSR_OFFSET = -12'd1180; -endmodule - -module ADC_SAR (...); - parameter BUF_EN = 29'b0; - parameter CLK_SEL = 1'b1; - parameter DIV_CTL = 2'd2; - parameter ADC_EN_SEL = 1'b0; - parameter PHASE_SEL = 1'b0; - parameter CSR_ADC_MODE = 1'b1; - parameter CSR_VSEN_CTRL = 3'd0; - parameter CSR_SAMPLE_CNT_SEL = 3'd4; - parameter CSR_RATE_CHANGE_CTRL = 3'd4; - parameter CSR_FSCAL = 10'd730; - parameter CSR_OFFSET = -12'd1180; - parameter ADC_CLK_DIV = 2'b00; - parameter ADC_CLKDIV_EN = 1'b0; - parameter CLK_SRC_SEL = 1'b1; - parameter VREF_BUF_EN = 1'b1; - parameter COUNT_LEN = 5'b10100; - parameter DAC_SAMPLE_END = 5'b10010; - parameter DAC_SAMPLE_START = 5'b01101; - parameter SH_SAMPLE_END = 5'b01011; - parameter SH_SAMPLE_START = 5'b00001; - parameter AUTO_CHOP_EN = 1'b0; - parameter CHOP_CLK_DIV = 4'b0; -endmodule - module LICD (...); parameter STAGE_NUM = 2'b00; parameter ENCDEC_NUM = 2'b00; @@ -2575,3 +2492,266 @@ parameter RD_PNTR = 3'b000; parameter DQS_MODE = "X1"; parameter HWL = "false"; endmodule + +// Added form adc.v + +/********ADC***********/ +//ADC for LRC,GW5AT-138K +module ADCLRC ( +//Voltage signal source, /mV + input ADCINBK2A, //input from bank2 IO, adc_in_b is the reference + input ADCINBK2B, //input from bank2 IO, adc_in_b is the reference + input ADCINBK3A, //input from bank3 IO, adc_in_b is the reference + input ADCINBK3B, //input from bank3 IO, adc_in_b is the reference + input ADCINBK4A, //input from bank4 IO, adc_in_b is the reference + input ADCINBK4B, //input from bank4 IO, adc_in_b is the reference + input ADCINBK5A, //input from bank5 IO, adc_in_b is the reference + input ADCINBK5B, //input from bank5 IO, adc_in_b is the reference + +//control signal + input [2:0] VSENCTL, //Input source selection (ciu), from 0 to 7: adcv, adct, vdd09_0, vdd09_1, vdd18_0, vdd18_1, vdd33_0, vdd33_1 + + input [9:0] FSCAL_VALUE, // temperature mode 510~948, typical value 730; Voltage mode 452~840, typical value 653 + input [11:0] OFFSET_VALUE, //signed number, temperature mode - 1560~- 760, typical value - 1180; Voltage mode - 410~410, typical value 0 + + input ADCEN, //Enable signal, active high + input CLK, //clk input,1/2 shared + input DRSTN, //0/1 shared, Digital part reset signal, active low + input ADCREQI, //Measurement request signal, valid rising edge, asynchronous signal + + //output + output ADCRDY, //The measurement completion signal, active high + output [13:0] ADCVALUE //The measurement result output, signed number. In voltage mode,/2048 is the actual measured value; In temperature mode,/4 is the actual measured value + +); + + parameter DYN_BKEN = "FALSE";//"FALSE","TRUE"."TRUE",BUF_BK2_EN[0]&BUF_BK3_EN[0]=1 +//Input source selection,1/2 shared + parameter BUF_SERDES_Q1_EN = 3'b000; //[1:0] does not support "11" + parameter BUF_BK2_EN = 6'b000000; //[3:0] Only one bit can be 1 at the same time + parameter BUF_BK3_EN = 6'b000000; //[3:0] Only one bit can be 1 at the same time + parameter BUF_BK4_EN = 6'b000000; //[3:0] Only one bit can be 1 at the same time + parameter BUF_BK5_EN = 6'b000000; //[3:0] Only one bit can be 1 at the same time + parameter BUF_BK10_EN = 5'b00000; //[3:1] Only one bit can be 1 at the same time + parameter BUF_BK11_EN = 5'b00000; //[3:1] Only one bit can be 1 at the same time +//Analog terminal option + parameter CLK_SEL = 1'b0; //Clock source selection. 0,PIOCLK_SEL,1,ciu_clk + parameter PIOCLK_SEL = 1'b0; //Clock source selection. 1/2 shared,0,mck_adc_clk_osc,1,io_clk + parameter VSEN_CTL = 3'b000; //Input source selection + parameter VSEN_CTL_SEL = 1'b0; //vsen_ctl source selection,0,VSEN_CTL,1,ciu_vsen_ctl + parameter ADC_MODE = 1'b0; //Mode selection + parameter DIV_CTL = 2'd0; //clock division,0:/1,1:/2,2:/4,3:/8,Clock after frequency division, 500kHz~8MHz + + //Digital terminal options + parameter SAMPLE_CNT_SEL = 3'd4; //total samples configuration, 0~4:64, 128, 256, 512, 1024 sampling points, and the other values are 2048 sampling points.The total number of samples shall be greater than 7 * sampling period, i.e. SAMPLR_CNT_SEL >= RATE_CHANGE_CTRL-1 + parameter RATE_CHANGE_CTRL = 3'd4; //Sampling period configuration, 0~4:4、8、16、32、64,other values are 128 + + +endmodule + +//ADCULC,GW5AT-138K +module ADCULC ( + + //Voltage signal source, /mV + input ADCINBK6A, //input from bank6 IO, adc_in_b is the reference + input ADCINBK6B, //input from bank6 IO, adc_in_b is the reference + input ADCINBK7A, //input from bank7 IO, adc_in_b is the reference + input ADCINBK7B, //input from bank7 IO, adc_in_b is the reference + + //control signal + input [2:0] VSENCTL, //Input source selection(cib),0~7: vtest, vdd09_0, vdd09_1, vdd09_2, vdd18_0, vdd18_1, reserved, vdd33 + + input [9:0] FSCAL_VALUE, // temperature mode 510~948, typical value 730; Voltage mode 452~840, typical value 653 + input [11:0] OFFSET_VALUE, //signed number, temperature mode - 1560~- 760, typical value - 1180; Voltage mode - 410~410, typical value 0 + + input ADCEN, //Enable signal, active high + input CLK, //clk input + input DRSTN, //0/1 shared, Digital part reset signal, active low + input ADCREQI, //Measurement request signal, valid rising edge, asynchronous signal + output ADCRDY, //The measurement completion signal, active high + output [13:0] ADCVALUE //The measurement result output, signed number. In voltage mode,/2048 is the actual measured value; In temperature mode,/4 is the actual measured value + +); + parameter DYN_BKEN = "FALSE";//"FALSE","TRUE"."TRUE",BUF_BK6_EN[0]&BUF_BK7_EN[0]=1 + +//Input source selection + parameter BUF_VCC_EN = 1'b0; //ulc + parameter BUF_VCCM_EN = 1'b0; //ulc + parameter BUF_MIPI_M0_EN = 3'b000; //ulc,[1:0] Only one bit can be 1 at the same time + parameter BUF_MIPI_M1_EN = 3'b000; //ulc,[1:0] Only one bit can be 1 at the same time + parameter BUF_SERDES_Q0_EN = 3'b000; //ulc,[1:0] Only one bit can be 1 at the same time + parameter BUF_BK6_EN = 6'b000000; //bk6,[3:0] Only one bit can be 1 at the same time + parameter BUF_BK7_EN = 6'b000000; //bk7,[3:0] Only one bit can be 1 at the same time +//Analog terminal option + parameter CLK_SEL = 1'b0; //Clock source selection. 0,PIOCLK_SEL,1,ciu_clk + parameter PIOCLK_SEL = 1'b0; //Clock source selection. 1/2 shared,0,mck_adc_clk_osc,1,io_clk + parameter VSEN_CTL = 3'b000; //Input source selection + parameter VSEN_CTL_SEL = 1'b0; //vsen_ctl source selection,0,VSEN_CTL,1,ciu_vsen_ctl + parameter ADC_MODE = 1'b0; //Mode selection + parameter DIV_CTL = 2'd0; //clock division,0:/1,1:/2,2:/4,3:/8,Clock after frequency division, 500kHz~8MHz + +//Digital terminal options + parameter SAMPLE_CNT_SEL = 3'd4; //total samples configuration, 0~4:64, 128, 256, 512, 1024 sampling points, and the other values are 2048 sampling points.The total number of samples shall be greater than 7 * sampling period, i.e. SAMPLR_CNT_SEL >= RATE_CHANGE_CTRL-1 + parameter RATE_CHANGE_CTRL = 3'd4; //Sampling period configuration, 0~4:4、8、16、32、64,other values are 128 + +endmodule + + +//ADC,GW5A-25 +module ADC ( + + //control signal + input CLK, //clk input + input [2:0] VSENCTL, //Input source selection (ciu), from 0 to 7: glo_left,glo_right,loc_left,vtest,vcc_rt,vccc_rt,vccm_rt,vccx_buf + input ADCMODE, //Mode selection,0:temperature mode,1:voltage mode + input DRSTN, //Digital part reset signal, active low + input ADCREQI, //Measurement request signal, valid rising edge, asynchronous signal + output ADCRDY, //The measurement completion signal, active high + output [13:0] ADCVALUE, //The measurement result output, signed number. In voltage mode,/2048 is the actual measured value; In temperature mode,/4 is the actual measured value + //mdrp + input MDRP_CLK, //mdrp clock + input [7:0] MDRP_WDATA, //mdrp write data + input MDRP_A_INC, //mdrp self-increased address + input [1:0] MDRP_OPCODE, //mdrp opcode + output [7:0] MDRP_RDATA, //mdrp read data + input ADCEN //Enable signal, active high + +); + + //Analog terminal option + parameter CLK_SEL = 1'b0; //时钟源选择,0:osc(2.5MHz),1:CLK + parameter DIV_CTL = 2'd0; //clock division,0:/1,1:/2,2:/4,3:/8,Clock after frequency division, 500kHz~8MHz + + //Input source selection + parameter BUF_EN = 12'b000000000000; // + parameter BUF_BK0_VREF_EN = 1'b0; // + parameter BUF_BK1_VREF_EN = 1'b0; // + parameter BUF_BK2_VREF_EN = 1'b0; // + parameter BUF_BK3_VREF_EN = 1'b0; // + parameter BUF_BK4_VREF_EN = 1'b0; // + parameter BUF_BK5_VREF_EN = 1'b0; // + parameter BUF_BK6_VREF_EN = 1'b0; // + parameter BUF_BK7_VREF_EN = 1'b0; // + + //Digital terminal options + parameter CSR_ADC_MODE = 1'b1; // Mode selection + parameter CSR_VSEN_CTRL = 3'd0; // signal source:vccx/vccio_*/vcc_reg -> 7, signal source:vcc_ext -> 4, others -> 0 + parameter CSR_SAMPLE_CNT_SEL = 3'd4; // total samples configuration, 0~4:64, 128, 256, 512, 1024 sampling points, and the other values are 2048 sampling points.The total number of samples shall be greater than 7 * sampling period, i.e. SAMPLR_CNT_SEL >= RATE_CHANGE_CTRL-1 + parameter CSR_RATE_CHANGE_CTRL = 3'd4; // Sampling period configuration, 0~4:4、8、16、32、64,other values are 128 + parameter CSR_FSCAL = 10'd730; // Parameter 1: temperature mode 510~948, typical value 730; Voltage mode 452~840, typical value 653 + parameter CSR_OFFSET = -12'd1180; // Parameter 2, signed number, temperature mode - 1560~- 760, typical value - 1180; Voltage mode - 410~410, typical value 0 + + +endmodule + + +//ADC_SAR,integrated saradc and adc functions. +module ADC_SAR ( +//`ifdef ADC + input ADCMODE, //Mode selection + input [2:0] VSENCTL, //Input source selection + input CLK, //clk input + //Digital + output ADCENO, //Enable signal, active high + input DRSTN, //Digital part reset signal, active low + input ADCREQI, //Measurement request signal + output ADCRDY, //The measurement completion signal, active high + output [13:0] ADCVALUE, //The measurement result output + input MDRP_CLK, //mdrp clock + input [7:0] MDRP_WDATA, //mdrp write data + input MDRP_A_INC, //mdrp self-increased address + input [1:0] MDRP_OPCODE,//mdrp opcode + output [7:0] MDRP_RDATA, //mdrp read data + //Analog + output ADC1BIT, //Analog data output + output ADCCLKO, //Analog clock output + input ADCENI, //fabric adc enable input +//`endif +// SAR +//`ifdef SARADC + output [12:0] ADCBIT, //Measurement result output + output CLKO, //output clk + output EOC, //The measurement completion signal + input CLKI, //fabric input clk + input [6:0] CHEN, //channel select + input RSTN, //resetn,active low + input SOC //Measurement request signal +//`endif +); + + parameter BUF_EN = 29'b0; // signal source selecor switch +// Δ-Σ +//`ifdef ADC + parameter CLK_SEL = 1'b1; // clk source select + parameter DIV_CTL = 2'd0; // clock division.0:/1,1:/2,2:/4,3:/8 + parameter ADC_EN_SEL = 1'b0; // adc_en source select + parameter PHASE_SEL = 1'b0; // adc internal data phase select + + //Digital terminal options + parameter CSR_ADC_MODE = 1'b1; // Mode selection + parameter CSR_VSEN_CTRL = 3'd0; // signal source:vccx/vccio_*/vcc_reg -> 7, signal source:vcc_ext -> 4, others -> 0 + parameter CSR_SAMPLE_CNT_SEL = 3'd4; // total samples configuration, 0~4:64, 128, 256, 512, 1024 sampling points, and the other values are 2048 sampling points.The total number of samples shall be greater than 7 * sampling period, i.e. SAMPLR_CNT_SEL >= RATE_CHANGE_CTRL-1 + parameter CSR_RATE_CHANGE_CTRL = 3'd4; // Sampling period configuration, 0~4:4、8、16、32、64,other values are 128 + parameter CSR_FSCAL = 10'd730; // Parameter 1: temperature mode 510~948, typical value 730; Voltage mode 452~840, typical value 653 + parameter CSR_OFFSET = -12'd1180; // Parameter 2, signed number, temperature mode - 1560~- 760, typical value - 1180; Voltage mode - 410~410, typical value 0 + +//`endif +// SAR +//`ifdef SARADC + parameter ADC_CLK_DIV = 2'b00; // clock division.00:/1,01:/2,10:/4,11:/8 + parameter ADC_CLKDIV_EN = 1'b0; // clock division enable + parameter CLK_SRC_SEL = 1'b1; // source clock sel + parameter VREF_BUF_EN = 1'b1; // BGR vref buffer enable + parameter COUNT_LEN = 5'b10100; // ADC counter length + parameter DAC_SAMPLE_END = 5'b10010; // DAC sample end point + parameter DAC_SAMPLE_START = 5'b01101; // DAC sample start point + parameter SH_SAMPLE_END = 5'b01011; // SH sample start point + parameter SH_SAMPLE_START = 5'b00001; // SH sample end point + parameter AUTO_CHOP_EN = 1'b0; // auto chop + parameter CHOP_CLK_DIV = 4'b0; // chop clock divider + +//`endif + +endmodule + + +//ADCA,15k +module ADCA ( +// analog + input ADCMODE, //Mode selection + input [2:0] VSENCTL, //Input source selection 0-7: vglo_left, vglo_right, vcc2, vccc3, vccb4, vcc5, vccm6, vccc7 + input CLK, //clk input + input ADCENI, //fabric adc enable input + input PWRON_DYN, //power enable. 1:on 0:off + // digital + input DRSTN, //Digital part reset signal, active low + input ADCREQI, //Measurement request signal + output ADCRDY, //The measurement completion signal, active high + output [13:0] ADCVALUE, //The measurement result output + input MDRP_CLK, //mdrp clock + input [7:0] MDRP_WDATA, //mdrp write data + input MDRP_A_INC, //mdrp self-increased address + input [1:0] MDRP_OPCODE,//mdrp opcode + output [7:0] MDRP_RDATA //mdrp read data +); + + parameter BUF_EN = 20'b0; // signal source selecor switch //[8:0] one-hot; [19:15] one-hot. BUF_EN[6] must be 0 + parameter CLK_SEL = 1'b1; // clk source select + parameter DIV_CTL = 2'd0; // clock division + parameter ADC_EN_SEL = 1'b0; // adc_en source select + parameter PHASE_SEL = 1'b0; // adc internal data phase select + + parameter PWRON_SEL = 1'b0; // power enbale source select. 0:PWRON 1:PWRON_DYN + parameter PWRON = 1'b1; // as PWRON_DYN + parameter LDO_MODE = 1'b0; // LDO mode 0:on(2.5/3.3V) 1:off(1.8V) + + //Digital terminal options + parameter CSR_ADC_MODE = 1'b1; // Mode selection + parameter CSR_VSEN_CTRL = 3'd0; // signal source:vccx/vccio_*/vcc_reg -> 7, signal source:vcc_ext -> 4, others -> 0 + parameter CSR_SAMPLE_CNT_SEL = 3'd4; // total samples configuration, 0~4:64, 128, 256, 512, 1024 sampling points, and the other values are 2048 sampling points.The total number of samples shall be greater than 7 * sampling period, i.e. SAMPLR_CNT_SEL >= RATE_CHANGE_CTRL-1 + parameter CSR_RATE_CHANGE_CTRL = 3'd4; // Sampling period configuration, 0~4:4、8、16、32、64,other values are 128 + parameter CSR_FSCAL = 10'd730; // Parameter 1: temperature mode 510~948, typical value 730; Voltage mode 452~840, typical value 653 + parameter CSR_OFFSET = -12'd1180; // Parameter 2, signed number, temperature mode - 1560~- 760, typical value - 1180; Voltage mode - 410~410, typical value 0 + +endmodule + diff --git a/tests/liberty/XNOR2X1.lib.verilogsim.ok b/tests/liberty/XNOR2X1.lib.verilogsim.ok index 89e55e8b8..6c9ec26dd 100644 --- a/tests/liberty/XNOR2X1.lib.verilogsim.ok +++ b/tests/liberty/XNOR2X1.lib.verilogsim.ok @@ -2,5 +2,5 @@ module XNOR2X1 (B, A, Y); input B; input A; output Y; - assign Y = !(B&!A|!B&A); // "!(B&!A|!B&A)" + assign Y = (~((B&(~A))|((~B)&A))); // "!(B&!A|!B&A)" endmodule diff --git a/tests/liberty/dff.lib b/tests/liberty/dff.lib index 61f5966f5..b5df36587 100644 --- a/tests/liberty/dff.lib +++ b/tests/liberty/dff.lib @@ -5,7 +5,7 @@ library(dff) { area : 1; ff("IQ", "IQN") { next_state : "(D)"; - clocked_on : "CLK"; + clocked_on : (CLK); } pin(D) { direction : input; @@ -15,7 +15,7 @@ library(dff) { } pin(Q) { direction: output; - function : "IQ"; + function : IQ; } } diff --git a/tests/liberty/dff.lib.filtered.ok b/tests/liberty/dff.lib.filtered.ok index b7dcb96be..2c2804ca1 100644 --- a/tests/liberty/dff.lib.filtered.ok +++ b/tests/liberty/dff.lib.filtered.ok @@ -3,7 +3,7 @@ library(dff) { area : 1 ; ff("IQ", "IQN") { next_state : "(D)" ; - clocked_on : "CLK" ; + clocked_on : ( CLK ) ; } pin(D) { direction : input ; @@ -13,7 +13,7 @@ library(dff) { } pin(Q) { direction : output ; - function : "IQ" ; + function : IQ ; } } } diff --git a/tests/liberty/dff.lib.verilogsim.ok b/tests/liberty/dff.lib.verilogsim.ok index 46441d0fc..e560df539 100644 --- a/tests/liberty/dff.lib.verilogsim.ok +++ b/tests/liberty/dff.lib.verilogsim.ok @@ -1,12 +1,12 @@ module dff (D, CLK, Q); - reg "IQ", "IQN"; + reg IQ, IQN; input D; input CLK; output Q; - assign Q = IQ; // "IQ" + assign Q = IQ; // IQ always @(posedge CLK) begin // "(D)" - "IQ" <= (D); - "IQN" <= ~((D)); + IQ <= D; + IQN <= ~(D); end endmodule diff --git a/tests/liberty/normal.lib.verilogsim.ok b/tests/liberty/normal.lib.verilogsim.ok index 85aed5f4e..92efbf8aa 100644 --- a/tests/liberty/normal.lib.verilogsim.ok +++ b/tests/liberty/normal.lib.verilogsim.ok @@ -1,13 +1,13 @@ module inv (A, Y); input A; output Y; - assign Y = ~A; // "A'" + assign Y = (~A); // "A'" endmodule module tri_inv (A, S, Z); input A; input S; output Z; - assign Z = ~A; // "A'" + assign Z = (~A); // "A'" endmodule module buffer (A, Y); input A; @@ -18,29 +18,29 @@ module nand2 (A, B, Y); input A; input B; output Y; - assign Y = ~(A&B); // "(A * B)'" + assign Y = (~(A&B)); // "(A * B)'" endmodule module nor2 (A, B, Y); input A; input B; output Y; - assign Y = ~(A|B); // "(A + B)'" + assign Y = (~(A|B)); // "(A + B)'" endmodule module xor2 (A, B, Y); input A; input B; output Y; - assign Y = (A&~B)|(~A&B); // "(A *B') + (A' * B)" + assign Y = ((A&(~B))|((~A)&B)); // "(A *B') + (A' * B)" endmodule module imux2 (A, B, S, Y); input A; input B; input S; output Y; - assign Y = ~(&(A&S)|(B&~S)&); // "( (A * S) + (B * S') )'" + assign Y = (~((A&S)|(B&(~S)))); // "( (A * S) + (B * S') )'" endmodule module dff (D, CLK, RESET, PRESET, Q, QN); - reg "IQ", "IQN"; + reg IQ, IQN; input D; input CLK; input RESET; @@ -51,26 +51,26 @@ module dff (D, CLK, RESET, PRESET, Q, QN); assign QN = IQN; // "IQN" always @(posedge CLK, posedge RESET, posedge PRESET) begin if ((RESET) && (PRESET)) begin - "IQ" <= 0; - "IQN" <= 0; + IQ <= 0; + IQN <= 0; end else if (RESET) begin - "IQ" <= 0; - "IQN" <= 1; + IQ <= 0; + IQN <= 1; end else if (PRESET) begin - "IQ" <= 1; - "IQN" <= 0; + IQ <= 1; + IQN <= 0; end else begin // "D" - "IQ" <= D; - "IQN" <= ~(D); + IQ <= D; + IQN <= ~(D); end end endmodule module latch (D, G, Q, QN); - reg "IQ", "IQN"; + reg IQ, IQN; input D; input G; output Q; @@ -79,8 +79,8 @@ module latch (D, G, Q, QN); assign QN = IQN; // "IQN" always @* begin if (G) begin - "IQ" <= D; - "IQN" <= ~(D); + IQ <= D; + IQN <= ~(D); end end endmodule @@ -89,14 +89,14 @@ module aoi211 (A, B, C, Y); input B; input C; output Y; - assign Y = ~((A&B)|C); // "((A * B) + C)'" + assign Y = (~((A&B)|C)); // "((A * B) + C)'" endmodule module oai211 (A, B, C, Y); input A; input B; input C; output Y; - assign Y = ~((A|B)&C); // "((A + B) * C)'" + assign Y = (~((A|B)&C)); // "((A + B) * C)'" endmodule module halfadder (A, B, C, Y); input A; @@ -104,7 +104,7 @@ module halfadder (A, B, C, Y); output C; assign C = (A&B); // "(A * B)" output Y; - assign Y = (A&~B)|(~A&B); // "(A *B') + (A' * B)" + assign Y = ((A&(~B))|((~A)&B)); // "(A *B') + (A' * B)" endmodule module fulladder (A, B, CI, CO, Y); input A; diff --git a/tests/liberty/unquoted.lib b/tests/liberty/unquoted.lib new file mode 100644 index 000000000..c2bf538d2 --- /dev/null +++ b/tests/liberty/unquoted.lib @@ -0,0 +1,60 @@ +library(dff_unquoted) { + cell (dff1) { + area : 1; + ff("IQ", "IQN") { + next_state : !D; + clocked_on : (CLK); + } + pin(D) { + direction : input; + } + pin(CLK) { + direction : input; + } + pin(Q) { + direction: output; + function : IQ; + } + } + cell (dff2) { + area : 1; + ff(IQ, IQN) { + next_state : D'; + clocked_on : CLK; + } + pin(D) { + direction : input; + } + pin(CLK) { + direction : input; + } + pin(Q) { + direction: output; + function : "IQ"; + } + } + cell (dffe) { + area : 6; + ff("IQ", "IQN") { + next_state : (D&EN) | (IQ&!EN); + clocked_on : !CLK; + } + pin(D) { + direction : input; + } + pin(EN) { + direction : input; + } + pin(CLK) { + direction : input; + } + pin(Q) { + direction: output; + function : "IQ"; + } + pin(QN) { + direction: output; + function : "IQN"; + } + } +} diff --git a/tests/liberty/unquoted.lib.filtered.ok b/tests/liberty/unquoted.lib.filtered.ok new file mode 100644 index 000000000..0ffc157b4 --- /dev/null +++ b/tests/liberty/unquoted.lib.filtered.ok @@ -0,0 +1,60 @@ +library(dff_unquoted) { + cell(dff1) { + area : 1 ; + ff("IQ", "IQN") { + next_state : !D ; + clocked_on : ( CLK ) ; + } + pin(D) { + direction : input ; + } + pin(CLK) { + direction : input ; + } + pin(Q) { + direction : output ; + function : IQ ; + } + } + cell(dff2) { + area : 1 ; + ff(IQ, IQN) { + next_state : D ' ; + clocked_on : CLK ; + } + pin(D) { + direction : input ; + } + pin(CLK) { + direction : input ; + } + pin(Q) { + direction : output ; + function : "IQ" ; + } + } + cell(dffe) { + area : 6 ; + ff("IQ", "IQN") { + next_state : ( D & EN ) | ( IQ & ! EN ) ; + clocked_on : !CLK ; + } + pin(D) { + direction : input ; + } + pin(EN) { + direction : input ; + } + pin(CLK) { + direction : input ; + } + pin(Q) { + direction : output ; + function : "IQ" ; + } + pin(QN) { + direction : output ; + function : "IQN" ; + } + } +} diff --git a/tests/liberty/unquoted.lib.verilogsim.ok b/tests/liberty/unquoted.lib.verilogsim.ok new file mode 100644 index 000000000..2a2f1d173 --- /dev/null +++ b/tests/liberty/unquoted.lib.verilogsim.ok @@ -0,0 +1,39 @@ +module dff1 (D, CLK, Q); + reg IQ, IQN; + input D; + input CLK; + output Q; + assign Q = IQ; // IQ + always @(posedge CLK) begin + // !D + IQ <= (~D); + IQN <= ~((~D)); + end +endmodule +module dff2 (D, CLK, Q); + reg IQ, IQN; + input D; + input CLK; + output Q; + assign Q = IQ; // "IQ" + always @(posedge CLK) begin + // D ' + IQ <= (~D); + IQN <= ~((~D)); + end +endmodule +module dffe (D, EN, CLK, Q, QN); + reg IQ, IQN; + input D; + input EN; + input CLK; + output Q; + assign Q = IQ; // "IQ" + output QN; + assign QN = IQN; // "IQN" + always @(negedge CLK) begin + // ( D & EN ) | ( IQ & ! EN ) + IQ <= ((D&EN)|(IQ&(~EN))); + IQN <= ~(((D&EN)|(IQ&(~EN)))); + end +endmodule diff --git a/tests/pyosys/test_idstring_lifetime.py b/tests/pyosys/test_idstring_lifetime.py new file mode 100644 index 000000000..4e6984474 --- /dev/null +++ b/tests/pyosys/test_idstring_lifetime.py @@ -0,0 +1,28 @@ + +from pyosys import libyosys as ys +from pathlib import Path + +__file_dir__ = Path(__file__).absolute().parent + +d = ys.Design() +ys.run_pass(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d) +ys.run_pass("hierarchy -top spm", d) + +external_idstring_holder_0 = None +external_idstring_holder_1 = None + +def get_top_module_idstring(): + global external_idstring_holder_0, external_idstring_holder_1 + d = ys.Design() + ys.run_pass(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d) + ys.run_pass("hierarchy -top spm", d) + external_idstring_holder_0 = d.top_module().name + for cell in d.top_module().cells_: + print(f"TARGETED: {cell}", flush=True) + external_idstring_holder_1 = cell + break + # d deallocates + +get_top_module_idstring() +print(external_idstring_holder_0, flush=True) +print(external_idstring_holder_1, flush=True) diff --git a/tests/pyosys/test_indirect_inheritance.py b/tests/pyosys/test_indirect_inheritance.py new file mode 100644 index 000000000..e29737e32 --- /dev/null +++ b/tests/pyosys/test_indirect_inheritance.py @@ -0,0 +1,15 @@ + +from pyosys import libyosys as ys +from pathlib import Path + +__file_dir__ = Path(__file__).absolute().parent + + +d = ys.Design() +ys.run_pass(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d) +ys.run_pass("hierarchy -top spm", d) + +for idstr, cell in d.top_module().cells_.items(): + cell.set_bool_attribute("\\set") + print(cell.attributes) + break diff --git a/tests/pyosys/test_monitor.py b/tests/pyosys/test_monitor.py index 2eefdad60..f9e435faa 100644 --- a/tests/pyosys/test_monitor.py +++ b/tests/pyosys/test_monitor.py @@ -14,7 +14,7 @@ class Monitor(ys.Monitor): self.mods.append(mod.name.str()) m = Monitor() -d.monitors.add(m) +d.monitors = [m] ys.run_pass(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d) ys.run_pass("hierarchy -top spm", d) diff --git a/tests/svinterfaces/positional_args.ys b/tests/svinterfaces/positional_args.ys new file mode 100644 index 000000000..6017a1c25 --- /dev/null +++ b/tests/svinterfaces/positional_args.ys @@ -0,0 +1,33 @@ +read_verilog -sv << EOF +interface simple_if; + logic receiver; + logic driver; +endinterface + +module driver_mod(simple_if intf, input in); + assign intf.driver = in; +endmodule + +module receiver_mod(simple_if intf); + assign intf.receiver = intf.driver; +endmodule + +module top( + input logic [1:0] inputs, + output logic [1:0] outputs +); + simple_if intf0(); + simple_if intf1(); + + driver_mod d0(intf0, inputs[0]); + driver_mod d1(intf1, inputs[1]); + + receiver_mod r0(intf0); + receiver_mod r1(intf1); + + assign outputs = {intf0.receiver, intf1.receiver}; +endmodule +EOF + +logger -expect error "Unable to connect.* with positional interface" 1 +hierarchy -top top diff --git a/tests/svinterfaces/run-test.sh b/tests/svinterfaces/run-test.sh index afa222766..71bdcd67a 100755 --- a/tests/svinterfaces/run-test.sh +++ b/tests/svinterfaces/run-test.sh @@ -5,3 +5,4 @@ ./run_simple.sh load_and_derive ./run_simple.sh resolve_types +./run_simple.sh positional_args diff --git a/tests/techmap/abc_state.ys b/tests/techmap/abc_state.ys new file mode 100644 index 000000000..bc2a744f5 --- /dev/null +++ b/tests/techmap/abc_state.ys @@ -0,0 +1,26 @@ +read_verilog < expressions, std::string expected) { for (const auto& e : expressions) { auto helper = LibertyExpression::Lexer(e); - auto tree_s = LibertyExpression::parse(helper).str(); + auto tree_s = LibertyExpression::parse(helper).sexpr_str(); EXPECT_EQ(tree_s, expected); } } @@ -82,6 +82,11 @@ namespace RTLIL { }, "(and (pin \"x\")\n" " (not (pin \"y\")))" ); + checkAll({ + "( D & EN )", + }, "(and (pin \"D\")\n" + " (pin \"EN\"))" + ); } } diff --git a/tests/various/timeest.ys b/tests/various/timeest.ys new file mode 100644 index 000000000..3859de5e1 --- /dev/null +++ b/tests/various/timeest.ys @@ -0,0 +1,12 @@ +read_verilog < $include +$yosys $test $source + +# include local to cwd +mkdir -p $subdir +cp $source $subdir +$yosys $test $subdir/$source + +# include local to source +mv $include $subdir +$yosys $test $subdir/$source + +# include local to source, and source is given as an absolute path +$yosys $test $(pwd)/$subdir/$source diff --git a/tests/verilog/local_include.v b/tests/verilog/local_include.v new file mode 100644 index 000000000..a677e888e --- /dev/null +++ b/tests/verilog/local_include.v @@ -0,0 +1,4 @@ +`include "temp_foo.v" +module top (input x, output y); + foo bar (.a(x), .b(y)); +endmodule diff --git a/tests/verilog/package_import_specific.sv b/tests/verilog/package_import_specific.sv new file mode 100644 index 000000000..97004ed56 --- /dev/null +++ b/tests/verilog/package_import_specific.sv @@ -0,0 +1,14 @@ +package package_import_specific; + + localparam integer + DATA_WIDTH = 8, + ADDR_WIDTH = 4; + + localparam logic [2:0] + IDLE = 3'b000, + START = 3'b001, + DATA = 3'b010, + STOP = 3'b100, + DONE = 3'b101; + +endpackage diff --git a/tests/verilog/package_import_specific.ys b/tests/verilog/package_import_specific.ys new file mode 100644 index 000000000..8b183aac4 --- /dev/null +++ b/tests/verilog/package_import_specific.ys @@ -0,0 +1,5 @@ +read_verilog -sv package_import_specific.sv +read_verilog -sv package_import_specific_module.sv +hierarchy -check +proc +opt -full diff --git a/tests/verilog/package_import_specific_module.sv b/tests/verilog/package_import_specific_module.sv new file mode 100644 index 000000000..a568af4d2 --- /dev/null +++ b/tests/verilog/package_import_specific_module.sv @@ -0,0 +1,16 @@ +import package_import_specific::DATA_WIDTH; +import package_import_specific::IDLE; + +module package_import_specific_module; + logic [DATA_WIDTH-1:0] data; + logic [3:0] addr; + logic [2:0] state; + + always_comb begin + case (state) + IDLE: data = 8'h00; + default: data = 8'hFF; + endcase + end + +endmodule