diff --git a/.github/workflows/floogen.yml b/.github/workflows/floogen.yml new file mode 100644 index 00000000..cbaa9ea3 --- /dev/null +++ b/.github/workflows/floogen.yml @@ -0,0 +1,84 @@ +# Copyright 2024 ETH Zurich and University of Bologna. +# Solderpad Hardware License, Version 0.51, see LICENSE for details. +# SPDX-License-Identifier: SHL-0.51 + +# Author: Tim Fischer + +name: floogen + +on: [push, pull_request] + +jobs: + + unit-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install . + - name: Run unit tests + run: | + python -m pytest -v + + ##################### + # Generate SV files # + ##################### + gen-nocs: + runs-on: ubuntu-latest + strategy: + matrix: + examples: ["single_cluster", "occamy_mesh", "occamy_tree"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + - name: Install floogen + run: | + python -m pip install . + - name: Generate NoCs + run: | + floogen -c floogen/examples/${{ matrix.examples }}.yml -o generated --no-format + - name: Format generated NoCs + uses: chipsalliance/verible-formatter-action@main + with: + files: + ./generated/${{ matrix.examples }}_floo_noc.sv + github_token: ${{ secrets.GITHUB_TOKEN }} + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.examples }} + path: generated/${{ matrix.examples }}_floo_noc.sv + if-no-files-found: error + retention-days: 1 + + ################# + # Lint SV files # + ################# + lint-nocs: + runs-on: ubuntu-latest + needs: gen-nocs + steps: + - uses: actions/checkout@v4 + - name: Download artifact + uses: actions/download-artifact@v4 + with: + path: generated + - name: Lint SV files + uses: chipsalliance/verible-linter-action@main + with: + config_file: '' + paths: + ./generated + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 75a59719..b2d220ed 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -16,21 +16,28 @@ jobs: check-clean: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.10' cache: 'pip' - - run: pip install -r requirements.txt + - name: Install dependencies and package + run: | + python -m pip install --upgrade pip + pip install . - name: Install bender uses: pulp-platform/pulp-actions/bender-install@v2 - - name: Install verible + - name: Make clean sources run: | - curl -Ls -o verible.tar.gz https://github.com/chipsalliance/verible/releases/download/v0.0-3313-gddcea377/verible-v0.0-3313-gddcea377-Ubuntu-22.04-jammy-x86_64.tar.gz - mkdir verible && tar -xzf verible.tar.gz -C verible --strip-components 1 - - name: Check clean make targets + make clean sources FLOOGEN_ARGS="--no-format" + - name: Format with verible + uses: chipsalliance/verible-formatter-action@main + with: + files: + ./hw/**/*.sv + github_token: ${{ secrets.GITHUB_TOKEN }} + - name: Check clean run: | - make clean sources VERIBLE_FMT=verible/bin/verible-verilog-format git status && test -z "$(git status --porcelain)" ############### @@ -39,22 +46,29 @@ jobs: check-stale: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.10' cache: 'pip' - - run: pip install -r requirements.txt + - name: Install dependencies and package + run: | + python -m pip install --upgrade pip + pip install . - name: Install bender uses: pulp-platform/pulp-actions/bender-install@v2 - - name: Install verible + - name: Make clean sources run: | - curl -Ls -o verible.tar.gz https://github.com/chipsalliance/verible/releases/download/v0.0-3313-gddcea377/verible-v0.0-3313-gddcea377-Ubuntu-22.04-jammy-x86_64.tar.gz - mkdir verible && tar -xzf verible.tar.gz -C verible --strip-components 1 - - name: Check clean makefile + make -B sources FLOOGEN_ARGS="--no-format" + - name: Format with verible + uses: chipsalliance/verible-formatter-action@main + with: + files: + ./hw/**/*.sv + github_token: ${{ secrets.GITHUB_TOKEN }} + - name: Check clean run: | - make -B sources VERIBLE_FMT=verible/bin/verible-verilog-format - git status && test -z "$(git status --porcelain)" + git status && test -z "$(git status --porcelain)" ##################### # Bender up-to-date # @@ -68,19 +82,40 @@ jobs: ################ # Verible Lint # ################ - lint: + verible-lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: chipsalliance/verible-linter-action@main with: + config_file: '' paths: | - ./src + ./hw extra_args: "--waiver_files util/verible.waiver" github_token: ${{ secrets.GITHUB_TOKEN }} fail_on_error: true reviewdog_reporter: github-check + ########## + # PyLint # + ########## + pylint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + cache: 'pip' + - name: Install dependencies and package + run: | + python -m pip install --upgrade pip + pip install . + - name: Analysing the code with pylint + run: | + pylint $(git ls-files '*.py') + ################# # Check License # ################# diff --git a/.gitignore b/.gitignore index eb486c65..d2472fc5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,85 @@ +# Temporary and binary files +*~ +*.py[cod] +*.so +*.cfg +!.isort.cfg +!setup.cfg +*.orig +*.log +*.pot +__pycache__/* +.cache/* +.*.swp +*/.ipynb_checkpoints/* +.DS_Store + +# Project files +.ropeproject +.project +.pydevproject +.settings +.idea +.vscode +.dvt +tags + +# Package files +*.egg +*.eggs/ +.installed.cfg +*.egg-info + +# Unittest and coverage +htmlcov/* +.coverage +.coverage.* +.tox +junit*.xml +coverage.xml +.pytest_cache/ + +# Build and docs folder/files +build/* +dist/* +sdist/* +docs/api/* +docs/_rst/* +docs/_build/* +cover/* +MANIFEST + +# Per-project virtualenvs +.venv*/ +.conda*/ +.python-version + +# bender .bender -*modelsim.ini -*transcript -*work +Bender.lock +Bender.local +deps + +# Auto generated sources +generated +*.png +hw/test/jobs + +# QuestaSim +work +work-vsim +modelsim.ini +transcript vsim.wlf scripts/compile_vsim.tcl -working_dir/* -compile.log -Bender.lock -.dvt -dvt_build.log -.vscode + +# Verible verible* # spyglass scripts/spyglass/* -test/jobs +# Misc *.csv **/*.log +reviewdog diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dd0d3a9c..e7286e1f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,14 +7,15 @@ variables: VSIM: questa-2023.4 vsim BENDER: bender + PYTHON: /home/fischeti/micromamba/envs/floo/bin/python stages: - - sources + - init - build - run collect-bender-sources: - stage: sources + stage: init script: - $BENDER checkout artifacts: @@ -22,12 +23,24 @@ collect-bender-sources: - .bender/ - Bender.lock +python-virtualenv: + stage: init + script: + - $PYTHON -m venv .venv + - source .venv/bin/activate + - pip install . + artifacts: + paths: + - .venv/ + compile-vsim: stage: build script: + - source .venv/bin/activate - make compile-sim | tee compile.log 2>&1 - '! grep "Error: " compile.log' needs: + - python-virtualenv - collect-bender-sources artifacts: paths: diff --git a/Bender.yml b/Bender.yml index 58324b59..62475804 100644 --- a/Bender.yml +++ b/Bender.yml @@ -15,62 +15,61 @@ dependencies: idma: { git: "https://github.com/pulp-platform/iDMA.git", version: 0.5.1 } export_include_dirs: - - include + - hw/include sources: # Level 0 - - src/floo_pkg.sv - - src/floo_axi_pkg.sv - - src/floo_narrow_wide_pkg.sv + - hw/floo_pkg.sv + - hw/floo_axi_pkg.sv + - hw/floo_narrow_wide_pkg.sv # Level 1 - - src/floo_cut.sv - - src/floo_fifo.sv - - src/floo_cdc.sv - - src/floo_route_select.sv - - src/floo_route_comp.sv - - src/floo_vc_arbiter.sv - - src/floo_wormhole_arbiter.sv - - src/floo_simple_rob.sv - - src/floo_rob.sv - - src/floo_rob_wrapper.sv - - src/floo_meta_buffer.sv + - hw/floo_cut.sv + - hw/floo_fifo.sv + - hw/floo_cdc.sv + - hw/floo_route_select.sv + - hw/floo_route_comp.sv + - hw/floo_vc_arbiter.sv + - hw/floo_wormhole_arbiter.sv + - hw/floo_simple_rob.sv + - hw/floo_rob.sv + - hw/floo_rob_wrapper.sv + - hw/floo_meta_buffer.sv # Level 2 - - src/floo_axi_chimney.sv - - src/floo_narrow_wide_chimney.sv - - src/floo_router.sv - - src/floo_narrow_wide_router.sv + - hw/floo_axi_chimney.sv + - hw/floo_narrow_wide_chimney.sv + - hw/floo_router.sv + - hw/floo_narrow_wide_router.sv - target: test include_dirs: - - test + - hw/test files: # Level 0 - - test/floo_test_pkg.sv + - hw/test/floo_test_pkg.sv # Level 1 - - test/axi_channel_compare.sv - - test/floo_axi_test_node.sv - - test/floo_axi_rand_slave.sv - - test/floo_dma_test_node.sv - - test/axi_reorder_compare.sv - - test/axi_reorder_remap_compare.sv - - test/axi_bw_monitor.sv - - test/floo_hbm_model.sv + - hw/test/floo_axi_test_node.sv + - hw/test/floo_axi_rand_slave.sv + - hw/test/floo_dma_test_node.sv + - hw/test/axi_reorder_compare.sv + - hw/test/axi_reorder_remap_compare.sv + - hw/test/axi_bw_monitor.sv + - hw/test/floo_hbm_model.sv # Level 2 - - test/tb_floo_axi_chimney.sv - - test/tb_floo_narrow_wide_chimney.sv - - test/tb_floo_router.sv - - test/tb_floo_rob.sv - - test/tb_floo_dma_chimney.sv - - test/tb_floo_dma_nw_chimney.sv - - test/tb_floo_dma_mesh.sv + - hw/tb/tb_floo_axi_chimney.sv + - hw/tb/tb_floo_narrow_wide_chimney.sv + - hw/tb/tb_floo_router.sv + - hw/tb/tb_floo_rob.sv + - hw/tb/tb_floo_dma_chimney.sv + - hw/tb/tb_floo_dma_nw_chimney.sv + - hw/tb/tb_floo_dma_mesh.sv - target: any(synthesis,spyglass) files: # Level 0 - - test/floo_test_pkg.sv + - hw/test/floo_test_pkg.sv # Level 1 - - src/synth/floo_synth_axi_chimney.sv - - src/synth/floo_synth_narrow_wide_chimney.sv - - src/synth/floo_synth_router.sv - - src/synth/floo_synth_narrow_wide_router.sv - - src/synth/floo_synth_endpoint.sv + - hw/synth/floo_synth_axi_chimney.sv + - hw/synth/floo_synth_narrow_wide_chimney.sv + - hw/synth/floo_synth_router.sv + - hw/synth/floo_synth_narrow_wide_router.sv + - hw/synth/floo_synth_endpoint.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index b8bf3f53..45e33dbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Added +- Added NoC generation framework called `floogen`. Also added documentation for `floogen` in the `docs` folder. - Added Chimney Parameters `EnMgrPort` and `EnSbrPort` to properly parametrize Manager resp. Subordinate-only instances of a chimney - Added `XYRouteOpt` parameter to router to enable/disable routing optimizations when using `XYRouting` ### Changed +- the exported include folder of the `floo` package is moved to `hw/include`. +- The `LICENSE` file was updated to reflect that the project uses the `Solderpad Hardware License Version 2.1` for all `hw` files and the `Apache License 2.0` for software related files. +- The directory was restructured to accomodate the new `floogen` framework. The `src` was renamed to `hw`, which contains only SystemVerilog code. Test modules and testbenches were also moved to `hw/test` and `hw/tb` respectively. The same holds true for wave files, which are now located in `hw/tb/wave`. +- The SV packages `floo_axi_pkg` and `floo_narrow_wide_pkg` are now generated by `floogen`. The configuration files were moved to the `floogen/examples` folder, and were aligned with the new `floogen` configuration format, that is written in `YAML` instead of `hjson`. +- Reworked the python dependencies to use `pyproject.toml` instead of `requirements.txt`. Furthermore, the python requirement was bumped to `3.10` due to `floogen` (which makes heavy use of the newer `match` syntax) - Removed `xy_id_i` ports from AXI chimneys in favor of a generic `id_i` port for both `IdTable` and `XYRouting` - Changed auto-generated package configuration schema. The `header` field is replaced in favor of a `routing` field that better represents the information needed for routing. - `XYRouting` now also supports a routing table similar to the `IdTable` routing table. Before the destination was determined based on a couple of bits in the address. This however did not allow for a lot of flexibility and requires a larger addres width. @@ -21,6 +27,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed missing backpressure in the `NoRoB` version of the reorder buffer, which could lead to overflow of counters +### Removed + +- `axi_channel_compare` was removed in favor of `axi_chan_compare` from the `axi` repository. +- Removed flit generation script `flit_gen.py` including configuration files, since this is now integrated into `floogen` (in conjunction with the `--only-pkg` flag) + ## [0.2.1] - 2023-10-13 ### Changed diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE b/LICENSE-SHL similarity index 100% rename from LICENSE rename to LICENSE-SHL diff --git a/Makefile b/Makefile index a999c434..ad81ad01 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ clean: clean-sim clean-spyglass clean-jobs clean-sources ############ BENDER ?= bender -VSIM ?= questa-2022.3 vsim +VSIM ?= questa-2023.4 vsim SPYGLASS ?= sg_shell VERIBLE_FMT ?= verible-verilog-format @@ -42,7 +42,6 @@ VSIM_TB_DUT ?= floo_noc_router_test VSIM_FLAGS += -64 VSIM_FLAGS += -t 1ps VSIM_FLAGS += -sv_seed 0 -VSIM_FLAGS += -voptargs=+acc # Set the job name and directory if specified ifdef JOB_NAME @@ -58,30 +57,38 @@ endif # Automatically open the waveform if a wave.tcl file is present VSIM_FLAGS_GUI += -do "log -r /*" -ifneq ("$(wildcard test/$(VSIM_TB_DUT).wave.tcl)","") - VSIM_FLAGS_GUI += -do "source test/$(VSIM_TB_DUT).wave.tcl" +VSIM_FLAGS_GUI += -voptargs=+acc +ifneq ("$(wildcard hw/tb/wave/$(VSIM_TB_DUT).wave.tcl)","") + VSIM_FLAGS_GUI += -do "source hw/tb/wave/$(VSIM_TB_DUT).wave.tcl" endif -################### -# Flit Generation # -################### +########### +# FlooGen # +########### -FLIT_OUT_DIR ?= src -FLIT_CFG_DIR ?= util -FLIT_CFG ?= $(shell find $(FLIT_CFG_DIR) -name "*.hjson") -FLIT_SRC ?= $(patsubst $(FLIT_CFG_DIR)/%_cfg.hjson,$(FLIT_OUT_DIR)/floo_%_pkg.sv,$(FLIT_CFG)) -FLIT_GEN ?= util/flit_gen.py -FLIT_TPL ?= util/floo_flit_pkg.sv.mako +FLOOGEN ?= floogen -.PHONY: sources clean-sources +FLOOGEN_OUT_DIR ?= $(MKFILE_DIR)generated +FLOOGEN_PKG_OUT_DIR ?= $(MKFILE_DIR)hw +FLOOGEN_CFG_DIR ?= $(MKFILE_DIR)floogen/examples +FLOOGEN_TPL_DIR ?= $(MKFILE_DIR)floogen/templates -sources: $(FLIT_SRC) -$(FLIT_OUT_DIR)/floo_%_pkg.sv: $(FLIT_CFG_DIR)/%_cfg.hjson $(FLIT_GEN) $(FLIT_TPL) - $(FLIT_GEN) -c $< > $@ - $(VERIBLE_FMT) --inplace --try_wrap_long_lines $@ +FLOOGEN_PKG_CFG ?= $(shell find $(FLOOGEN_CFG_DIR) -name "*_pkg.yml") +FLOOGEN_PKG_SRC ?= $(patsubst $(FLOOGEN_CFG_DIR)/%_pkg.yml,$(FLOOGEN_PKG_OUT_DIR)/floo_%_pkg.sv,$(FLOOGEN_PKG_CFG)) +FLOOGEN_TPL ?= $(shell find $(FLOOGEN_TPL_DIR) -name "*.mako") + +.PHONY: install-floogen sources clean-sources + +install-floogen: + @which $(FLOOGEN) > /dev/null || (echo "Installing floogen..." && pip install .) + +sources: install-floogen $(FLOOGEN_PKG_SRC) +$(FLOOGEN_PKG_OUT_DIR)/floo_%_pkg.sv: $(FLOOGEN_CFG_DIR)/%_pkg.yml $(FLOOGEN_TPL) + $(FLOOGEN) -c $< --only-pkg --pkg-outdir $(FLOOGEN_PKG_OUT_DIR) $(FLOOGEN_ARGS) clean-sources: - rm -f $(FLIT_SRC) + rm -rf $(FLOOGEN_OUT_DIR) + rm -f $(FLOOGEN_PKG_SRC) ###################### # Traffic Generation # @@ -91,12 +98,12 @@ TRAFFIC_GEN ?= util/gen_jobs.py TRAFFIC_TB ?= dma_mesh TRAFFIC_TYPE ?= random TRAFFIC_RW ?= read -TRAFFIC_OUTDIR ?= test/jobs +TRAFFIC_OUTDIR ?= hw/test/jobs .PHONY: jobs clean-jobs jobs: $(TRAFFIC_GEN) mkdir -p $(TRAFFIC_OUTDIR) - $(TRAFFIC_GEN) --out_dir $(TRAFFIC_OUTDIR) --tb $(TRAFFIC_TB) --type $(TRAFFIC_TYPE) --rw $(TRAFFIC_RW) + $(TRAFFIC_GEN) --out_dir $(TRAFFIC_OUTDIR) --tb $(TRAFFIC_TB) --traffic_type $(TRAFFIC_TYPE) --rw $(TRAFFIC_RW) clean-jobs: rm -rf $(TRAFFIC_OUTDIR) diff --git a/README.md b/README.md index d87e22a1..2e308a3e 100644 --- a/README.md +++ b/README.md @@ -9,20 +9,20 @@ Logo -This repository provides modules for the FlooNoC, a Network-on-Chip (NoC) which is part of the [PULP (Parallel Ultra-Low Power) Platform](https://pulp-platform.org/). The repository includes Network Interface IPs (named chimneys), Routers and further NoC components to build a complete NoC. FlooNoC mainly supports [AXI4+ATOPs](https://github.com/pulp-platform/axi/tree/master), but can be easily extended to other On-Chip protocols. Arbitrary topologies are supported with several routing algorithms. FlooNoC is designed to be scalable and modular, and can be easily extended with new components.

+This repository provides modules for the FlooNoC, a Network-on-Chip (NoC) which is part of the [PULP (Parallel Ultra-Low Power) Platform](https://pulp-platform.org/). The repository includes Network Interface IPs (named chimneys), Routers and further NoC components to build a complete NoC. FlooNoC mainly supports [AXI4+ATOPs](https://github.com/pulp-platform/axi/tree/master), but can be easily extended to other On-Chip protocols. Arbitrary topologies are supported with several routing algorithms. FlooNoC is designed to be scalable and modular, and can be easily extended with new components. Additionally, FlooNoC provides a generation framework for creating customized NoC configurations.
[![CI status](https://github.com/pulp-platform/FlooNoC/actions/workflows/gitlab-ci.yml/badge.svg?branch=main)](https://github.com/pulp-platform/FlooNoC/actions/workflows/gitlab-ci.yml?query=branch%3Amain) [![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/pulp-platform/FlooNoC?color=blue&label=current&sort=semver)](CHANGELOG.md) +![License](https://img.shields.io/badge/license-Apache--2.0-green) ![License](https://img.shields.io/badge/license-SHL--0.51-green) [Design Principles](#-design-principles) • [Getting started](#-getting-started) • [List of IPs](#-list-of-ips) • -[Configuration](#-configuration) • +[Generation](#%EF%B8%8F-generation) • [License](#-license) -
## 💡 Design Principles @@ -42,7 +42,7 @@ The names of the IPs are inspired by the [Harry Potter](https://en.wikipedia.org > In use for centuries, the Floo Network, while somewhat uncomfortable, has many advantages. Firstly, unlike broomsticks, the Network can be used without fear of breaking the International Statute of Secrecy. Secondly, unlike Apparition, there is little to no danger of serious injury. Thirdly, it can be used to transport children, the elderly and the infirm." ## 🔐 License -Unless specified otherwise in the respective file headers, all code checked into this repository is made available under a permissive license. All hardware sources and tool scripts are licensed under the Solderpad Hardware License 0.51 (see [`LICENSE`](LICENSE)) +All code checked into this repository is made available under a permissive license. All software sources are licensed under the Apache License 2.0 (see [`LICENSE-APACHE`](LICENSE-APACHE)), and all hardware sources in the `hw` folder are licensed under the Solderpad Hardware License 0.51 (see [`LICENSE-SHL`](LICENSE-SHL)). ## 📚 Publication If you use FlooNoC in your research, please cite the following paper: @@ -68,7 +68,7 @@ If you use FlooNoC in your research, please cite the following paper: ### Pre-requisites -FlooNoC uses [bender](https://github.com/pulp-platform/bender) to manage its dependencies and to automatically generate compilation scripts. Further `Python >= 3.8` is required with the packages listed in `requirements.txt`. +FlooNoC uses [bender](https://github.com/pulp-platform/bender) to manage its dependencies and to automatically generate compilation scripts. Further `Python >= 3.10` is required to install the generation framework. ### Simulation Currently, we do not provide any open-source simulation setup. Internally, the FlooNoC was tested using QuestaSim, which can be launched with the following command: @@ -104,76 +104,126 @@ This repository includes the following NoC IPs: ### Routers | Name | Description | Doc | | --- | --- | --- | -| [floo_router](src/floo_router.sv) | A simple router with configurable number of ports, physical and virtual channels, and input/output buffers | | -| [floo_narrow_wide_router](src/floo_narrow_wide_router.sv) | Wrapper of a multi-link router for narrow and wide links | | +| [floo_router](hw/floo_router.sv) | A simple router with configurable number of ports, physical and virtual channels, and input/output buffers | | +| [floo_narrow_wide_router](hw/floo_narrow_wide_router.sv) | Wrapper of a multi-link router for narrow and wide links | | ### Network Interfaces | Name | Description | Doc | | --- | --- | --- | -| [floo_axi_chimney](src/floo_axi_chimney.sv) | A bidirectional network interface for connecting AXI4 Buses to the NoC | | -| [floo_narrow_wide_chimney](src/floo_narrow_wide_chimney.sv) | A bidirectional network interface for connecting narrow & wide AXI Buses to the multi-link NoC | | +| [floo_axi_chimney](hw/floo_axi_chimney.sv) | A bidirectional network interface for connecting AXI4 Buses to the NoC | | +| [floo_narrow_wide_chimney](hw/floo_narrow_wide_chimney.sv) | A bidirectional network interface for connecting narrow & wide AXI Buses to the multi-link NoC | | ### Topologies | Name | Description | Doc | | --- | --- | --- | -| [floo_mesh](src/floo_mesh.sv) | A mesh topology with configurable number of rows and columns | | -| [floo_mesh_ruche](src/floo_mesh_ruche.sv) | A mesh topology with ruche channels and a configurable number of rows and columns | | +| [floo_mesh](hw/floo_mesh.sv) | A mesh topology with configurable number of rows and columns | | +| [floo_mesh_ruche](hw/floo_mesh_ruche.sv) | A mesh topology with ruche channels and a configurable number of rows and columns | | ### Common IPs | Name | Description | Doc | | --- | --- | --- | -| [floo_fifo](src/floo_fifo.sv) | A FIFO buffer with configurable depth | | -| [floo_cut](src/floo_cut.sv) | Elastic buffers for cuting timing paths | | -| [floo_cdc](src/floo_cdc.sv) | A Clock-Domain-Crossing (CDC) module implemented with a gray-counter based FIFO. | | -| [floo_wormhole_arbiter](src/floo_wormhole_arbiter.sv) | A wormhole arbiter | | -| [floo_vc_arbiter](src/floo_vc_arbiter.sv) | A virtual channel arbiter | | -| [floo_rob](src/floo_rob.sv) | A table-based Reorder Buffer | | -| [floo_simple_rob](src/floo_simple_rob.sv) | A simplistic low-complexity Reorder Buffer | | -| [floo_rob_wrapper](src/floo_simple_rob.sv) | A wrapper of all available types of RoBs including RoB-less version | | +| [floo_fifo](hw/floo_fifo.sv) | A FIFO buffer with configurable depth | | +| [floo_cut](hw/floo_cut.sv) | Elastic buffers for cuting timing paths | | +| [floo_cdc](hw/floo_cdc.sv) | A Clock-Domain-Crossing (CDC) module implemented with a gray-counter based FIFO. | | +| [floo_wormhole_arbiter](hw/floo_wormhole_arbiter.sv) | A wormhole arbiter | | +| [floo_vc_arbiter](hw/floo_vc_arbiter.sv) | A virtual channel arbiter | | +| [floo_route_comp](hw/floo_route_comp.sv) | A helper module to compute the packet destination | | +| [floo_rob](hw/floo_rob.sv) | A table-based Reorder Buffer | | +| [floo_simple_rob](hw/floo_simple_rob.sv) | A simplistic low-complexity Reorder Buffer | | +| [floo_rob_wrapper](hw/floo_simple_rob.sv) | A wrapper of all available types of RoBs including RoB-less version | | ### Verification IPs | Name | Description | Doc | | --- | --- | --- | -| [axi_bw_monitor](test/axi_bw_monitor.sv) | A AXI4 Bus Monitor for measuring the throughput and latency of the AXI4 Bus | | -| [axi_reorder_compare](test/axi_reorder_compare.sv) | A AXI4 Bus Monitor for verifying the order of AXI transactions with the same ID | | -| [floo_axi_rand_slave](test/floo_axi_rand_slave.sv) | A AXI4 Bus Multi-Slave generating random AXI respones with configurable response time | | -| [floo_axi_test_node](test/floo_axi_test_node.sv) | A AXI4 Bus Master-Slave Node for generating random AXI transactions | | -| [floo_dma_test_node](test/floo_dma_test_node.sv) | An endpoint node with a DMA master port and a Simulation Memory Slave port | | -| [floo_hbm_model](test/floo_hbm_model.sv) | A very simple model of the HBM memory controller with configurable delay | | - -## 🎛️ Configuration - -The data structs for the flits and the links are auto-generated and can be configured in `util/*cfg.hjson`. The size of the links is automatically determined to fit the largest message going over the link into a single flit, in order to avoid any serialization. - -The AXI channels(s) needs to be configured in `util/*cfg.hjson`. The following example shows the configuration for a single AXI channel with 64-bit data width, 32-bit address width, 3-bit ID width, and 1-bit user width (beware that ID width can be different for input and output channels). - -``` - axi_channels: [ - {name: 'axi', direction: 'input', params: {dw: 64, aw: 32, iw: 3, uw: 1 }} - ] +| [axi_bw_monitor](hw/test/axi_bw_monitor.sv) | A AXI4 Bus Monitor for measuring the throughput and latency of the AXI4 Bus | | +| [axi_reorder_compare](hw/test/axi_reorder_compare.sv) | A AXI4 Bus Monitor for verifying the order of AXI transactions with the same ID | | +| [floo_axi_rand_slave](hw/test/floo_axi_rand_slave.sv) | A AXI4 Bus Multi-Slave generating random AXI respones with configurable response time | | +| [floo_axi_test_node](hw/test/floo_axi_test_node.sv) | A AXI4 Bus Master-Slave Node for generating random AXI transactions | | +| [floo_dma_test_node](hw/test/floo_dma_test_node.sv) | An endpoint node with a DMA master port and a Simulation Memory Slave port | | +| [floo_hbm_model](hw/test/floo_hbm_model.sv) | A very simple model of the HBM memory controller with configurable delay | | + +## 🛠️ Generation + +FlooNoC comes with a generation framework called `floogen`. It allows to create complex network configurations with a simple configuration file. + +### Capabilities + +`floogen` has a graph-based internal representation of the network configuration. This allows to easily add new features and capabilities to the generation framework. The following list shows the a couple of the current capabilities of `floogen`: + +- **Validation**: The configuration is validated before the generation to ensure that the configuration is valid. For instance, the configuration is checked for invalid user input, overlapping address ranges +- **Routing**: XY-Routing and ID-Table routing are supported. `floogen` automatically generates the routing tables for the routers, as well as the address map for the network interfaces. +- **Package Generation**: `floogen` automatically generates a SystemVerilog package with all the needed types and constants for the network configuration. +- **Top Module Generation**: `floogen` automatically generates a top module that contains all router and network interfaces. The interfaces of the top module are AXI4 interfaces for all the enpdoints specified in the configuration. + +### Example + +The following example shows the configuration for a simple mesh topology with 4x4 routers and 4x4 chimneys with XY-Routing. + +```yaml + name: example_system + description: "Example of a configuration file" + + routing: + route_algo: "XY" + use_id_table: true + + protocols: + - name: "example_axi" + type: "AXI4" + direction: "manager" + data_width: 64 + addr_width: 32 + id_width: 3 + user_width: 1 + - name: "example_axi" + type: "AXI4" + direction: "subordinate" + data_width: 64 + addr_width: 32 + id_width: 3 + user_width: 1 + + endpoints: + - name: "cluster" + array: [4, 4] + addr_range: + base: 0x1000_0000 + size: 0x0004_0000 + mgr_port_protocol: + - "example_axi" + sbr_port_protocol: + - "example_axi" + + routers: + - name: "router" + array: [4, 4] + + connections: + - src: "cluster" + dst: "router" + src_range: + - [0, 3] + - [0, 3] + dst_range: + - [0, 3] + - [0, 3] + bidirectional: true ``` -Multiple physical links can be declared and the mapping of the AXI channels to the physical link can be configured in `util/*cfg.json`. The following example shows the configuration for two physical channels, one for requests and one for responses. The mapping of the AXI channels to the physical link is done by specifying the AXI channels in the `map` field. -``` - channel_mapping: { - req: {axi: ['aw', 'w', 'ar']} - rsp: {axi: ['b', 'r']} - } -``` +### Usage -FlooNoC does not send any header and tail flits to avoid serilization overhead. Instead additional needed routing information is sent in parallel and needs to be specified in the `routing` field. Examples for the different routing algorithms can be found in `util/*cfg.hjson`. The following example shows the configuration for a XY routing algorithm with 3-bit X and Y coordinates, 36-bit address offset, and 8-bit RoB index. +To install `floogen` run the following command: +```sh +pip install . ``` - routing: { - route_algo: XYRouting - num_x_bits: 3 - num_y_bits: 3 - addr_offset_bits: 36 - rob_idx_bits: 8 - } -``` -Finally, the package source files can be generated with: + +which allows you to use `floogen` with the following command: ```sh -make sources +floogen -c -o ``` + +### Configuration + +The example configuration above shows the basic structure of a configuration file. A more detailed description of the configuration file can be found in the [documentation](docs/floogen.md). diff --git a/docs/floogen.md b/docs/floogen.md new file mode 100644 index 00000000..6abb96fd --- /dev/null +++ b/docs/floogen.md @@ -0,0 +1,165 @@ +# FlooNoC generation framework + +## Introduction + +`floogen` is a framework for generating [FlooNoC](https://github.com/pulp-platform/FlooNoC) networks on chip. It allows to generate SystemVerilog RTL code for a given network configuration given by a configuration file written in YAML format. This document describes the configuration file format and how write your own configuration file to generate a FlooNoC network. + +## Configuration file format +The following is an example of a configuration file for a 4x4 mesh network: + +```yaml + name: example_system + description: "Example of a configuration file" + + routing: + route_algo: "XY" + use_id_table: true + + protocols: + - name: "example_axi" + type: "AXI4" + direction: "manager" + data_width: 64 + addr_width: 32 + id_width: 3 + user_width: 1 + - name: "example_axi" + type: "AXI4" + direction: "subordinate" + data_width: 64 + addr_width: 32 + id_width: 3 + user_width: 1 + + endpoints: + - name: "cluster" + array: [4, 4] + addr_range: + base: 0x1000_0000 + size: 0x0004_0000 + mgr_port_protocol: + - "example_axi" + sbr_port_protocol: + - "example_axi" + + routers: + - name: "router" + array: [4, 4] + + connections: + - src: "cluster" + dst: "router" + src_range: + - [0, 3] + - [0, 3] + dst_range: + - [0, 3] + - [0, 3] + bidirectional: true +``` + +but let's go step by step. + +### System description + +The first part of the configuration file is the system description. It is composed by the following fields: + +- `name`: name of the system. This will be used in the generated RTL code to name the top module and the top-level signals. +- `description`: a short description of the system. This is optional and not used to generate any kind of code + +### Routing + +The routing section describes the routing algorithm to be used in the generated network. It is composed by the following fields: + +- `route_algo`: the routing algorithm to be used. Currently XY-Routing (`XY`) and table-based (`ID`) routing are supported +- `use_id_table`: if set to `true`, a system address table will be generated in the package which is used to translate system addresses to endpoint IDs or coordinates. This is always used in table-based routing and can be used in XY-Routing to translate system addresses to XY-coordinates. If XY-Routing is used and this field is set to `false`, the XY coordinates are automatically derived as address offsets. This is much simpler but requires that the system addresses are contiguous and the address width is large enough to address all endpoints. + +The following fields are usually auto-generated by the framework, but can be overwritten if only the package is generated: +- `addr_offset_bits`: The offset bits to determine the endpoint ID or XY coordinates from the system address. For instance, if you want to use the 4 MSB bits of a 32-bit address to determine the endpoint ID, this field should be set to 28. This field is only used if `use_id_table` is set to `false`. +- `num_id_bits`: The number of bits used to encode the endpoint ID. This field is only used if `use_id_table` is set to `false`. +- `num_x_bits`: The number of bits used to encode the X-coordinate. This field is only used if `use_id_table` is set to `false`. +- `num_y_bits`: The number of bits used to encode the Y-coordinate. This field is only used if `use_id_table` is set to `false`. + +### Protocols + +The protocols section describes the protocols used in the network. It is composed by a list of protocols, each one described by the following fields: + +- `name`: name of the protocol. This will be used as a reference in the framework and in the generated RTL code to name the protocol module and the protocol signals. If the narrow-wide channels are used, they need to be named `narrow` and `wide` respectively. +- `type`: Currently only `AXI4` is supported +- `direction`: the direction of the protocol. It can be either `manager` or `subordinate`. If an endpoint is both manager and subordinate, two protocols need to be defined. +- `data_width`: the data width of the protocol +- `addr_width`: the address width of the protocol +- `id_width`: the ID width of the protocol. Endpoints with different ID widths for the `manager` and `subordinate` protocols are supported. +- `user_width`: the user width of the protocol + +### Endpoints + +The endpoints section describes the endpoints of the network. It is composed by a list of endpoints, each one described by the following fields: + +- `name`: name of the endpoint. This will be used as a reference in the framework and in the generated RTL code to name the endpoint module and the endpoint signals. +- `array`: the array of the endpoint. It can either be a 1D array (e.g. `[4]`) or a 2D array (e.g. `[4, 4]`). In the latter case, the first element of the array is the number of rows and the second element is the number of columns. +- `adddr_range`: Every endpoint that is a subordinate needs an address range. This field describes the address range of the endpoint. See the [Address Map](#address-map) section for more details. +- `mgr_port_protocol`: the list of protocols used by the manager port of the endpoint (if any). The names defined in the protocols section need to be used here. +- `sbr_port_protocol`: the list of protocols used by the subordinate port of the endpoint (if any). The names defined in the protocols section need to be used here. +- `id_offset`: the ID offset of the endpoint. This is used to offset the ID of the endpoint. For instance, if the ID offset is 1, the ID of the first endpoint will be 1, the ID of the second endpoint will be 2, and so on. This is useful when the ID 0 is reserved for a special endpoint (e.g. DRAM). The `id_offset` can either be a single value (e.g. `1`) in case of `ID` routing or coordinates (e.g. `x: 1, y: 1`) in case of `XY` routing. This is useful in meshes where the coordinate of the first endpoint is not `(0, 0)`, but rather `(1, 1)`. The `id_offset` is also used in `XY` routing to determine the location of the endpoint in the network and how to connect it to the routers. + +### Address Map + +Each endpoint that is a subordinate needs an address range, which can be defined in different ways: +- `start` and `end`: the start and end address of the endpoint. The size of the address range is automatically computed by the framework. +- `start` and `size`: the start address and the size of the address range. The end address of the endpoint is automatically computed by the framework. + +For arrays, it usually makes sense to define a `base` address with an offset for each element of the array like this: +- `base`: the base address of the endpoint. +- `size`: the size of the address range. This is the offset added to the address range of the next element of the array. +- `idx`: the index of the endpoint in the array. This is optional and is usually automatically computed by the framework, but could be specified. + +### Routers + +The routers section describes the routers of the network. It is composed by a list of routers, each one described by the following fields: + +- `name`: name of the router. This will be used as a reference in the framework and in the generated RTL code to name the router module and the router signals. +- `array`: the array of the router. It can either be a 1D array (e.g. `[4]`) or a 2D array (e.g. `[4, 4]`). In the latter case, the first element of the array is the number of rows and the second element is the number of columns. +- `tree`: a router structure as a tree. This is mutually exclusive with the `array` field. It is a list of the number of children each level has. For instance `[1, 6, 4]` means that one root router is created that has 6 children routers, which each have 4 children routers (in total, there will be `1 + 6 + 4*6 = 31` routers). This is useful to create a tree structure of routers. +- `auto_connect`: If the `auto_connect` field is set to `true`, the framework will automatically connect the routers to each other. This is useful for meshes and trees. If the `auto_connect` field is set to `false`, the framework will not connect the routers to each other. This is useful for custom topologies. In this case, the connections need to be defined in the `connections` section. + +### Connections + +The connections section describes the connections between the endpoints and the routers. It is composed by a list of connections, each one described by the following fields: + +- `src`: the source of the connection. It can either be an endpoint or a router. The reference name of the endpoint or router needs to be used here. +- `dst`: the destination of the connection. It can either be an endpoint or a router. The reference name of the endpoint or router needs to be used here. + +In case of array endpoints or routers, one of the following fields needs to be specified: +- `src_range` and/or `dst_range`: the range of the source and destination. This is a list of ranges. Each range is a list of two elements: the start and end index of the range. The first element of the list is the range of the first dimension of the array and the second element is the range of the second dimension of the array (if any). For instance, if the source is a 4x4 array and the source range is `[[0, 3], [0, 3]]`, the connection will be made to all the endpoints of the array. If the source is a 4x4 array and the source range is `[[0, 3], [0, 0]]`, the connection will be made to the first row of the array. +- `src_idx` and/or `dst_idx`: the actual index of a single endpoint or router. This is a list of indices. Each index is a list of two elements: the index of the first dimension of the array and the index of the second dimension of the array (if any). For instance, if the source is a 4x4 array and the source index is `[0, 0]`, the connection will be made to the first endpoint of the array. If the source is a 4x4 array and the source index is `[0, 1]`, the connection will be made to the second endpoint of the array. + +In case of tree routers, you can also specify the following fields: +`src_lvl` or `dst_lvl`: the level of the source or destination. This is the level of the tree where the router is located. The root router is at level 0, the children routers are at level 1, and so on. This is useful to connect endpoints to the root router or to connect endpoints to the children routers. + +## Running floogen + +`floogen` is a Python package that can be installed with `pip`: + +```bash +pip install . +``` + +Once installed, you can run `floogen` with the following command: + +```bash +floogen -c -o +``` + +where `` is the configuration file and `` is the output directory where the generated RTL code will be placed. + + +## Additonal arguments + +Apart from the configuration file, `floogen` supports additional options to customize the generated RTL code. The following options are supported: + +- `--outdir`: the output directory where the generated RTL code will be placed. This is equivalent to the `-o` option. If it is not specified, the output is printed to stdout. +- `--only-pkg`: only generate the package. This is useful if you want to test single IPs without generating a whole network. +- `--pkg-outdir`: the output directory where the generated package will be placed. By default, the package in the `hw` folder is overwitten, since it is also the once that is used by `bender` for compiling the IPs. If you want to keep the original package, you can specify a different output directory here. +- `--no-format`: do not format the generated RTL code. By default, the generated RTL code is formatted with verible format, for which the `verible-verilog-format` binary needs to be installed. If this option is set, the generated RTL code is not formatted. +- `--visualize`: visualize the generated network. It will create a plot of the graph of the network. If the `--outdir` option is specified, the plot is saved in the output directory. Otherwise, it is shown in a window. This is mainly intended for a quick check of the generated network, not a tool for debugging. diff --git a/floo_noc.core b/floo_noc.core index ca4feb51..208d2d97 100644 --- a/floo_noc.core +++ b/floo_noc.core @@ -4,67 +4,74 @@ CAPI=2: name: '::floo_noc:' filesets: - test: + files_rtl: file_type: systemVerilogSource files: - - test/floo_test_pkg.sv - - test/axi_channel_compare.sv - - test/floo_axi_test_node.sv - - test/floo_axi_rand_slave.sv - - test/floo_dma_test_node.sv - - test/axi_reorder_compare.sv - - test/axi_reorder_remap_compare.sv - - test/axi_bw_monitor.sv - - test/floo_hbm_model.sv - - test/tb_floo_axi_chimney.sv - - test/tb_floo_narrow_wide_chimney.sv - - test/tb_floo_router.sv - - test/tb_floo_rob.sv - - test/tb_floo_dma_chimney.sv - - test/tb_floo_dma_nw_chimney.sv - - test/tb_floo_dma_mesh.sv + - hw/floo_pkg.sv + - hw/floo_axi_pkg.sv + - hw/floo_narrow_wide_pkg.sv + - hw/floo_cut.sv + - hw/floo_fifo.sv + - hw/floo_cdc.sv + - hw/floo_route_select.sv + - hw/floo_route_comp.sv + - hw/floo_vc_arbiter.sv + - hw/floo_wormhole_arbiter.sv + - hw/floo_simple_rob.sv + - hw/floo_rob.sv + - hw/floo_rob_wrapper.sv + - hw/floo_meta_buffer.sv + - hw/floo_axi_chimney.sv + - hw/floo_narrow_wide_chimney.sv + - hw/floo_router.sv + - hw/floo_narrow_wide_router.sv + - hw/include/floo_noc/typedef.svh: + is_include_file: true + include_path: hw/include depend: - pulp-platform.org::common_cells:1.32.0 - pulp-platform.org::common_verification:0.2.3 - pulp-platform.org::axi:0.39.1 - ::idma:0.5.1 - spyglass_or_synthesis: + test: file_type: systemVerilogSource files: - - test/floo_test_pkg.sv - - src/synth/floo_synth_axi_chimney.sv - - src/synth/floo_synth_narrow_wide_chimney.sv - - src/synth/floo_synth_router.sv - - src/synth/floo_synth_narrow_wide_router.sv - - src/synth/floo_synth_endpoint.sv + - hw/test/floo_test_pkg.sv + - hw/test/axi_channel_compare.sv + - hw/test/floo_axi_test_node.sv + - hw/test/floo_axi_rand_slave.sv + - hw/test/floo_dma_test_node.sv + - hw/test/axi_reorder_compare.sv + - hw/test/axi_reorder_remap_compare.sv + - hw/test/axi_bw_monitor.sv + - hw/test/floo_hbm_model.sv + - hw/tb/tb_floo_axi_chimney.sv + - hw/tb/tb_floo_narrow_wide_chimney.sv + - hw/tb/tb_floo_router.sv + - hw/tb/tb_floo_rob.sv + - hw/tb/tb_floo_dma_chimney.sv + - hw/tb/tb_floo_dma_nw_chimney.sv + - hw/tb/tb_floo_dma_mesh.sv + - hw/test/include/axi_print_txns.svh: + is_include_file: true + include_path: hw/test/include + - hw/test/include/tb_tasks.svh: + is_include_file: true + include_path: hw/test/include depend: - pulp-platform.org::common_cells:1.32.0 - pulp-platform.org::common_verification:0.2.3 - pulp-platform.org::axi:0.39.1 - ::idma:0.5.1 - files_rtl: + spyglass_or_synthesis: file_type: systemVerilogSource files: - - src/floo_axi_pkg.sv - - src/floo_narrow_wide_pkg.sv - - src/floo_pkg.sv - - src/floo_cut.sv - - src/floo_fifo.sv - - src/floo_cdc.sv - - src/floo_route_select.sv - - src/floo_vc_arbiter.sv - - src/floo_wormhole_arbiter.sv - - src/floo_simple_rob.sv - - src/floo_rob.sv - - src/floo_rob_wrapper.sv - - src/floo_meta_buffer.sv - - src/floo_axi_chimney.sv - - src/floo_narrow_wide_chimney.sv - - src/floo_router.sv - - src/floo_narrow_wide_router.sv - - include/floo_noc/typedef.svh: - is_include_file: true - include_path: include + - hw/test/floo_test_pkg.sv + - hw/synth/floo_synth_axi_chimney.sv + - hw/synth/floo_synth_narrow_wide_chimney.sv + - hw/synth/floo_synth_router.sv + - hw/synth/floo_synth_narrow_wide_router.sv + - hw/synth/floo_synth_endpoint.sv depend: - pulp-platform.org::common_cells:1.32.0 - pulp-platform.org::common_verification:0.2.3 diff --git a/floogen/__init__.py b/floogen/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/floogen/config_parser.py b/floogen/config_parser.py new file mode 100644 index 00000000..7114330c --- /dev/null +++ b/floogen/config_parser.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Author: Tim Fischer + +from pathlib import Path +import logging +from typing import List, Union, Tuple, Mapping, TypeVar + +from pydantic import ValidationError, BaseModel + +from ruamel.yaml.comments import CommentedMap +from ruamel.yaml import YAMLError +import ruamel.yaml + +import click + +logger = logging.getLogger("padrick.ConfigParser") + + +def get_error_context(config_file: Path, line, column, context_before=4, context_after=4): + """Retrieves the context surrounding an error in a configuration file.""" + lines_to_return = [] + with config_file.open() as file: + for line_idx, l in enumerate(file.readlines()): + if line_idx + 1 == line: + lines_to_return.append(l) + lines_to_return.append(click.style(column * " " + "^\n", blink=True, fg="yellow")) + elif line_idx + 1 >= line - context_before and line_idx + 1 <= line + context_after: + lines_to_return.append(l) + return "".join(lines_to_return) + + +def get_human_readable_error_path(config_data: dict, error_location: List[Union[str, int]]): + """Transforms a list of path segments into a human readable string.""" + transformed_path_segments = [] + node = config_data + for path_segment in error_location: + try: + node = node[path_segment] + except KeyError: + transformed_path_segments.append(path_segment) + break + if isinstance(path_segment, int): + transformed_path_segments.append(node.get("name", path_segment)) + else: + transformed_path_segments.append(path_segment) + return "->".join(transformed_path_segments) + + +def get_file_location( + config_data: CommentedMap, error_location: List[Union[str, int]] +) -> Tuple[Tuple[int, int], Mapping]: + """Retrieves the location of an error in a configuration file.""" + node = config_data + location = (node.lc.line + 1, node.lc.col) + subtree = node + for path_segment in error_location: + try: + location = (node.lc.data[path_segment][0] + 1, node.lc.data[path_segment][1]) + node = node[path_segment] + if isinstance(node, Mapping): + subtree = node + except KeyError: + break + return location, subtree + + +T = TypeVar("T", bound=BaseModel) + + +def parse_config(cls: T, config_file: Path) -> Union[T, None]: + """Parses a configuration file and returns a validated model.""" + with config_file.open() as file: + try: + yaml = ruamel.yaml.YAML(typ="rt") + # enable support for !include directives (see pyyaml-include package) + # if not include_base_dir: + # include_base_dir = config_file.parent + # if not ignore_includes: + # include_constructor = YamlIncludeConstructor(base_dir=str(include_base_dir)) + # else: + # include_constructor = IgnoreIncludeConstructor + # yaml.register_class(include_constructor) + config_data = yaml.load(file) + # config_data = ruamel.yaml.load(file, Loader=ruamel.yaml.RoundTripLoader) + except YAMLError as e: + logger.error("Error while parsing config_file:\n %s", e) + return None + try: + model = cls.model_validate(config_data) + return model + except ValidationError as e: + logger.error( + "Encountered %s validation errors while parsing the configuration file:", + len(e.errors()), + ) + for error in e.errors(): + if error["type"] == "extra": + error[ + "msg" + ] = f'Unknown field {error["loc"][-1]}. \ + Did you mispell the field name?' + if error["type"] == "missing": + error["msg"] = f'Missing field \'{error["loc"][-1]}\'' + else: + error["msg"] = f'{error["msg"]} (field \'{error["loc"][-1]}\')' + # error_path = get_human_readable_error_path(config_data, error["loc"]) + (line, column), _ = get_file_location(config_data, error["loc"]) + error_context = get_error_context(config_file, line, column, context_after=10) + logger.error("Line %s, Column %s:", line, column) + logger.error("...\n%s\n...", error_context) + logger.error("Error: %s", error["msg"]) + return None diff --git a/floogen/examples/axi_pkg.yml b/floogen/examples/axi_pkg.yml new file mode 100644 index 00000000..9ab6e5bd --- /dev/null +++ b/floogen/examples/axi_pkg.yml @@ -0,0 +1,35 @@ +# Copyright 2024 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "narrow" +description: "flit configuration for narrow-only AXI4 interfaces" + +routing: + route_algo: "XY" + use_id_table: false + addr_offset_bits: 16 + num_x_bits: 3 + num_y_bits: 3 + +protocols: + - name: "axi" + type: "AXI4" + direction: "manager" + data_width: 64 + addr_width: 32 + id_width: 3 + user_width: 1 + - name: "axi" + type: "AXI4" + direction: "subordinate" + data_width: 64 + addr_width: 32 + id_width: 3 + user_width: 1 + +endpoints: [] + +routers: [] + +connections: [] diff --git a/floogen/examples/narrow_wide_pkg.yml b/floogen/examples/narrow_wide_pkg.yml new file mode 100644 index 00000000..c8c24f77 --- /dev/null +++ b/floogen/examples/narrow_wide_pkg.yml @@ -0,0 +1,49 @@ +# Copyright 2024 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "narrow_wide" +description: "flit configuration for narrow and wide AXI4 interfaces" + +routing: + route_algo: "XY" + use_id_table: false + addr_offset_bits: 16 + num_x_bits: 3 + num_y_bits: 3 + +protocols: + - name: "narrow" + type: "AXI4" + direction: "manager" + data_width: 64 + addr_width: 48 + id_width: 4 + user_width: 1 + - name: "narrow" + type: "AXI4" + direction: "subordinate" + data_width: 64 + addr_width: 48 + id_width: 2 + user_width: 1 + - name: "wide" + type: "AXI4" + direction: "manager" + data_width: 512 + addr_width: 48 + id_width: 3 + user_width: 1 + - name: "wide" + type: "AXI4" + direction: "subordinate" + data_width: 512 + addr_width: 48 + id_width: 1 + user_width: 1 + +endpoints: [] + +routers: [] + +connections: [] diff --git a/floogen/examples/occamy_mesh.yml b/floogen/examples/occamy_mesh.yml new file mode 100644 index 00000000..e1537393 --- /dev/null +++ b/floogen/examples/occamy_mesh.yml @@ -0,0 +1,139 @@ +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: occamy_mesh +description: "Occamy mesh configuration for FlooGen" + +routing: + route_algo: "XY" + use_id_table: true + +protocols: + - name: "narrow" + type: "AXI4" + direction: "manager" + data_width: 64 + addr_width: 48 + id_width: 4 + user_width: 1 + - name: "narrow" + type: "AXI4" + direction: "subordinate" + data_width: 64 + addr_width: 48 + id_width: 2 + user_width: 1 + - name: "wide" + type: "AXI4" + direction: "manager" + data_width: 512 + addr_width: 48 + id_width: 3 + user_width: 1 + - name: "wide" + type: "AXI4" + direction: "subordinate" + data_width: 512 + addr_width: 48 + id_width: 1 + user_width: 1 + +endpoints: + - name: "cluster" + array: [3, 8] + addr_range: + base: 0x0000_1000_0000 + size: 0x0000_0004_0000 + mgr_port_protocol: + - "narrow" + - "wide" + sbr_port_protocol: + - "narrow" + - "wide" + id_offset: + x: 0 + y: 0 + - name: "hbm" + array: [1, 8] + addr_range: + base: 0x0000_8000_0000 + size: 0x0000_4000_0000 + sbr_port_protocol: + - "narrow" + - "wide" + id_offset: + x: -1 + y: 0 + - name: "serial_link" + array: [3, 1] + addr_range: + base: 0x0100_0000_0000 + size: 0x0010_000_0000 + mgr_port_protocol: + - "narrow" + - "wide" + sbr_port_protocol: + - "narrow" + - "wide" + id_offset: + x: 0 + y: -1 + - name: "cva6" + mgr_port_protocol: + - "narrow" + id_offset: + x: 0 + y: 8 + - name: "peripherals" + addr_range: + start: 0x0000_0000_0000 + end: 0x0000_0fff_ffff + id_offset: + x: 1 + y: 8 + mgr_port_protocol: + - "narrow" + sbr_port_protocol: + - "narrow" + +routers: + - name: "router" + array: [3, 8] + +connections: + - src: "cluster" + dst: "router" + src_range: + - [0, 2] + - [0, 7] + dst_range: + - [0, 2] + - [0, 7] + bidirectional: true + - src: "hbm" + dst: "router" + src_range: + - [0, 0] + - [0, 7] + dst_range: + - [0, 0] + - [0, 7] + bidirectional: true + - src: "serial_link" + dst: "router" + src_range: + - [0, 2] + - [0, 0] + dst_range: + - [0, 2] + - [0, 0] + bidirectional: true + - src: "cva6" + dst: "router" + dst_idx: [0, 7] + bidirectional: true + - src: "peripherals" + dst: "router" + dst_idx: [1, 7] + bidirectional: true diff --git a/floogen/examples/occamy_tree.yml b/floogen/examples/occamy_tree.yml new file mode 100644 index 00000000..a5b902af --- /dev/null +++ b/floogen/examples/occamy_tree.yml @@ -0,0 +1,109 @@ +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: occamy_tree +description: "Occamy configuration for FlooGen" + +routing: + route_algo: "ID" + use_id_table: true + + +protocols: + - name: "narrow" + type: "AXI4" + direction: "manager" + data_width: 64 + addr_width: 48 + id_width: 4 + user_width: 1 + - name: "narrow" + type: "AXI4" + direction: "subordinate" + data_width: 64 + addr_width: 48 + id_width: 2 + user_width: 1 + - name: "wide" + type: "AXI4" + direction: "manager" + data_width: 512 + addr_width: 48 + id_width: 3 + user_width: 1 + - name: "wide" + type: "AXI4" + direction: "subordinate" + data_width: 512 + addr_width: 48 + id_width: 1 + user_width: 1 + +endpoints: + - name: "cluster" + array: [6, 4] + addr_range: + base: 0x0000_1000_0000 + size: 0x0000_0004_0000 + mgr_port_protocol: + - "narrow" + - "wide" + sbr_port_protocol: + - "narrow" + - "wide" + - name: "hbm" + array: [8] + addr_range: + base: 0x0000_8000_0000 + size: 0x0000_4000_0000 + sbr_port_protocol: + - "narrow" + - "wide" + - name: "serial_link" + addr_range: + start: 0x0100_0000_0000 + end: 0x01ff_ffff_ffff + mgr_port_protocol: + - "narrow" + - "wide" + sbr_port_protocol: + - "narrow" + - "wide" + - name: "peripherals" + addr_range: + start: 0x0000_0000_0000 + end: 0x0000_0fff_ffff + mgr_port_protocol: + - "narrow" + sbr_port_protocol: + - "narrow" + +routers: + - name: "router" + tree: [1, 6] # Number of routers per level + +connections: + - src: "cluster" + dst: "router" + src_range: + - [0, 5] + - [0, 3] + dst_lvl: 1 + allow_multi: true + bidirectional: true + - src: "router" + dst: "hbm" + dst_range: + - [0, 7] + src_lvl: 0 + allow_multi: true + bidirectional: true + - src: "router" + dst: "serial_link" + src_lvl: 0 + bidirectional: true + - src: "router" + dst: "peripherals" + src_lvl: 0 + bidirectional: true diff --git a/floogen/examples/single_cluster.yml b/floogen/examples/single_cluster.yml new file mode 100644 index 00000000..51d2daa9 --- /dev/null +++ b/floogen/examples/single_cluster.yml @@ -0,0 +1,100 @@ +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: single_cluster +description: "Single Cluster Configuration for FlooGen" + +routing: + route_algo: "ID" + use_id_table: true + +protocols: + - name: "narrow" + type: "AXI4" + direction: "manager" + data_width: 64 + addr_width: 48 + id_width: 4 + user_width: 1 + - name: "narrow" + type: "AXI4" + direction: "subordinate" + data_width: 64 + addr_width: 48 + id_width: 2 + user_width: 1 + - name: "wide" + type: "AXI4" + direction: "manager" + data_width: 512 + addr_width: 48 + id_width: 3 + user_width: 1 + - name: "wide" + type: "AXI4" + direction: "subordinate" + data_width: 512 + addr_width: 48 + id_width: 1 + user_width: 1 + +endpoints: + - name: "cluster" + addr_range: + base: 0x0000_1000_0000 + size: 0x0000_0004_0000 + mgr_port_protocol: + - "narrow" + - "wide" + sbr_port_protocol: + - "narrow" + - "wide" + - name: "hbm" + addr_range: + base: 0x0010_0000_0000 + size: 0x0000_4000_0000 + sbr_port_protocol: + - "narrow" + - "wide" + - name: "serial_link" + addr_range: + base: 0x0100_0000_0000 + size: 0x0010_000_0000 + mgr_port_protocol: + - "narrow" + - "wide" + sbr_port_protocol: + - "narrow" + - "wide" + - name: "cva6" + mgr_port_protocol: + - "narrow" + - name: "peripherals" + addr_range: + start: 0x0000_0000_0000 + end: 0x0000_0fff_ffff + mgr_port_protocol: + - "narrow" + sbr_port_protocol: + - "narrow" + +routers: + - name: "router" + +connections: + - src: "cluster" + dst: "router" + bidirectional: true + - src: "hbm" + dst: "router" + bidirectional: true + - src: "serial_link" + dst: "router" + bidirectional: true + - src: "cva6" + dst: "router" + bidirectional: true + - src: "peripherals" + dst: "router" + bidirectional: true diff --git a/floogen/floo_gen.py b/floogen/floo_gen.py new file mode 100755 index 00000000..bfaee851 --- /dev/null +++ b/floogen/floo_gen.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Author: Tim Fischer + +import argparse +import pathlib + +from floogen.config_parser import parse_config +from floogen.model.network import Network +from floogen.utils import verible_format + + +def parse_args(): + """Parse the command line arguments.""" + parser = argparse.ArgumentParser(description="FlooGen: A Network-on-Chip Generator") + parser.add_argument( + "-c", "--config", type=pathlib.Path, required=True, help="Path to the configuration file." + ) + parser.add_argument( + "-o", + "--outdir", + type=pathlib.Path, + required=False, + help="Path to the output directory of the generated output.", + ) + parser.add_argument( + "--pkg-outdir", + dest="pkg_outdir", + type=pathlib.Path, + required=False, + default=pathlib.Path(__file__).parent.parent / "hw", + help="Path to the output directory of the generated output.", + ) + parser.add_argument( + "--only-pkg", + dest="only_pkg", + action="store_true", + help="Only generate the package file." + ) + parser.add_argument( + "--no-format", + dest="no_format", + action="store_true", + help="Do not format the output.", + ) + parser.add_argument("--visualize", action="store_true", help="Visualize the network graph.") + args = parser.parse_args() + return args + + +def main(): + """Generates the network.""" + args = parse_args() + network = parse_config(Network, args.config) + + + if not args.only_pkg: + network.create_network() + network.compile_network() + network.gen_routing_info() + + # Visualize the network graph + if args.visualize: + if args.outdir: + network.visualize(filename=args.outdir / (network.name + ".pdf")) + else: + network.visualize(savefig=False) + + # Generate the network description + rendered_top = network.render_network() + if not args.no_format: + rendered_top = verible_format(rendered_top) + # Write the network description to file or print it to stdout + if args.outdir: + args.outdir.mkdir(parents=True, exist_ok=True) + top_file_name = args.outdir / (network.name + "_floo_noc.sv") + with open(top_file_name, "w+", encoding="utf-8") as top_file: + top_file.write(rendered_top) + else: + print(rendered_top) + + axi_type, rendered_pkg = network.render_link_cfg() + if not args.no_format: + rendered_pkg = verible_format(rendered_pkg) + # Write the link configuration to file or print it to stdout + if args.pkg_outdir: + cfg_file_name = args.pkg_outdir / (f"floo_{axi_type}_pkg.sv") + with open(cfg_file_name, "w+", encoding="utf-8") as cfg_file: + cfg_file.write(rendered_pkg) + else: + print(rendered_pkg) + + +if __name__ == "__main__": + main() diff --git a/floogen/model/__init__.py b/floogen/model/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/floogen/model/connection.py b/floogen/model/connection.py new file mode 100644 index 00000000..59ab7e18 --- /dev/null +++ b/floogen/model/connection.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Author: Tim Fischer + +from typing import Optional, List, Tuple, Dict +from pydantic import BaseModel, field_validator, model_validator + + +class ConnectionDesc(BaseModel): + """Connection class to describe a connection between routers and endpoints.""" + + description: Optional[str] = "" + src: str + dst: str + src_range: Optional[List[Tuple[int, int]]] = None + dst_range: Optional[List[Tuple[int, int]]] = None + src_idx: Optional[List[int]] = None + dst_idx: Optional[List[int]] = None + src_lvl: Optional[int] = None + dst_lvl: Optional[int] = None + coord_offset: Optional[Dict] = None + allow_multi: Optional[bool] = False + bidirectional: Optional[bool] = False + + @field_validator("src_idx", "dst_idx", mode="before") + @classmethod + def int_to_list(cls, v): + """Convert int to list.""" + if isinstance(v, int): + return [v] + return v + + @model_validator(mode="after") + def check_indexing(self): + """Check if the indexing is valid.""" + if self.src_idx and self.src_lvl: + raise ValueError("src_idx and src_lvl are mutually exclusive") + if self.dst_idx and self.dst_lvl: + raise ValueError("dst_idx and dst_lvl are mutually exclusive") + return self diff --git a/floogen/model/endpoint.py b/floogen/model/endpoint.py new file mode 100644 index 00000000..e0436529 --- /dev/null +++ b/floogen/model/endpoint.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Author: Tim Fischer +from typing import Optional, List, Union, Tuple +from pydantic import BaseModel, field_validator, model_validator + +from floogen.model.routing import AddrRange, Id, Coord +from floogen.model.protocol import Protocols + + +class EndpointDesc(BaseModel): + """ + Endpoint class to describe an endpoint with adress ranges and configuration parameters. + """ + + name: str + description: Optional[str] = "" + array: Optional[Union[Tuple[int], Tuple[int, int]]] = None + addr_range: Optional[AddrRange] = None + id_offset: Optional[Id] = None + mgr_port_protocol: Optional[List[str]] = None + sbr_port_protocol: Optional[List[str]] = None + + @field_validator("array", mode="before") + @classmethod + def int_to_tuple(cls, v): + """Convert int to tuple.""" + if isinstance(v, int): + return (v,) + return v + + @field_validator("id_offset", mode="before") + @classmethod + def dict_to_coord_obj(cls, v): + """Convert dict to Coord object.""" + match v: + case None: + return None + case {"x": x, "y": y}: + return Coord(x=x, y=y) + + @model_validator(mode="after") + def check_addr_range(self): + """Check if the address range is valid.""" + # Manager-only endpoints do not need an address range + match (self.sbr_port_protocol, self.addr_range): + case (None, _): + return self + case (_, None): + raise ValueError("Endpoint is a Subordinate and requires an address range") + return self + + def is_sbr(self) -> bool: + """Return true if the endpoint is a subordinate.""" + return self.sbr_port_protocol is not None + + def is_mgr(self) -> bool: + """Return true if the endpoint is a manager.""" + return self.mgr_port_protocol is not None + + def get_ni_name(self, name: str) -> str: + """Return the name of the NI.""" + return name.replace(self.name, f"{self.name}_ni") + + def render_ports(self): + """Render the ports of the endpoint.""" + + +class Endpoint(EndpointDesc): + """Endpoint class to describe an endpoint with adress ranges and configuration parameters.""" + + mgr_ports: List[Protocols] = [] + sbr_ports: List[Protocols] = [] + + @classmethod + def from_desc(cls, desc: EndpointDesc, + mgr_ports: List[Protocols], + sbr_ports: List[Protocols]): + """Create an endpoint from a description.""" + return cls(**desc.model_dump(), mgr_ports=mgr_ports, sbr_ports=sbr_ports) + + def render_ports(self): + """Render the ports of the endpoint.""" + ports = [] + for port in self.mgr_ports: + ports += port.render_port() + for port in self.sbr_ports: + ports += port.render_port() + return ports diff --git a/floogen/model/graph.py b/floogen/model/graph.py new file mode 100644 index 00000000..c3a115cc --- /dev/null +++ b/floogen/model/graph.py @@ -0,0 +1,264 @@ +#!/usr/bin/env python3 +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Author: Tim Fischer + +from typing import List, Tuple + +import networkx as nx + + +class Graph(nx.DiGraph): # pylint: disable=too-many-public-methods + """Network graph class.""" + + def __init__(self): + """Initialize the graph.""" + super().__init__() + self._node_idx = 0 + + def add_node(self, node_for_adding: str, **attr): + """Add a node to the graph.""" + if self.has_node(node_for_adding): + raise ValueError(f"Node {node_for_adding} already exists in the graph.") + assert "type" in attr, "Node type not provided" + if "obj" not in attr: + attr["obj"] = None + super().add_node(node_for_adding, **attr) + + def add_edge(self, u_of_edge: str, v_of_edge: str, **attr): + """Add an edge to the graph.""" + if self.has_edge(u_of_edge, v_of_edge): + raise ValueError( + f"Edge ({u_of_edge}, {v_of_edge}) already exists in the graph." + ) + assert "type" in attr, "Edge type not provided" + if "obj" not in attr: + attr["obj"] = None + super().add_edge(u_of_edge, v_of_edge, **attr) + + def add_edge_bidir(self, u_of_edge: str, v_of_edge: str, **attr): + """Add a bidirectional edge to the graph.""" + self.add_edge(u_of_edge, v_of_edge, **attr) + self.add_edge(v_of_edge, u_of_edge, **attr) # pylint: disable=arguments-out-of-order + + def get_node_obj(self, node): + """Return the node object.""" + return self.nodes[node]["obj"] + + def set_node_obj(self, node, obj): + """Set the node object.""" + self.nodes[node]["obj"] = obj + + def get_node_arr_idx(self, node): + """Return the node array index.""" + return self.nodes[node]["arr_idx"] + + def get_node_lvl(self, node): + """Return the node level.""" + return self.nodes[node]["lvl"] + + def get_edge_obj(self, edge): + """Return the edge object.""" + return self.edges[edge]["obj"] + + def set_edge_obj(self, edge, obj): + """Set the edge object.""" + self.edges[edge]["obj"] = obj + + def is_rt_node(self, node): + """Return whether the node is a router node.""" + return self.nodes[node]["type"] == "router" + + def is_ep_node(self, node): + """Return whether the node is an endpoint node.""" + return self.nodes[node]["type"] == "endpoint" + + def is_ni_node(self, node): + """Return whether the node is an ni node.""" + return self.nodes[node]["type"] == "network_interface" + + def is_prot_edge(self, edge): + """Return whether the edge is a protocol edge.""" + return self.edges[edge]["type"] == "protocol" + + def is_link_edge(self, edge): + """Return whether the edge is a link edge.""" + return self.edges[edge]["type"] == "link" + + def get_nodes(self, filters=None, with_name=False): + """Filter the nodes from the graph.""" + nodes = self.nodes + if filters is not None: + for flt in filters: + nodes = list(filter(flt, nodes)) + if with_name: + return [(node, self.get_node_obj(node)) for node in nodes] + return [self.get_node_obj(node) for node in nodes] + + def get_edges(self, filters=None, with_name=False): + """Filter the edges from the graph.""" + edges = self.edges + if filters is not None: + for flt in filters: + edges = list(filter(flt, edges)) + if with_name: + return [(edge, self.get_edge_obj(edge)) for edge in edges] + return [self.get_edge_obj(edge) for edge in edges] + + def get_edges_from(self, node, filters=None, with_name=False): + """Return the outgoing edges from the node.""" + if filters is None: + filters = [] + filters.append(lambda e: e[0] == node) + return self.get_edges(filters=filters, with_name=with_name) + + def get_edges_to(self, node, filters=None, with_name=False): + """Return the incoming edges to the node.""" + if filters is None: + filters = [] + filters.append(lambda e: e[1] == node) + return self.get_edges(filters=filters, with_name=with_name) + + def get_edges_of(self, node, filters=None, with_name=False): + """Return the edges of the node.""" + if filters is None: + filters = [] + filters.append(lambda e: node in e) + return self.get_edges(filters=filters, with_name=with_name) + + def get_ni_nodes(self, with_name=False): + """Return the ni nodes.""" + return self.get_nodes(filters=[self.is_ni_node], with_name=with_name) + + def get_rt_nodes(self, with_name=False): + """Return the router nodes.""" + return self.get_nodes(filters=[self.is_rt_node], with_name=with_name) + + def get_ep_nodes(self, with_name=False): + """Return the endpoint nodes.""" + return self.get_nodes(filters=[self.is_ep_node], with_name=with_name) + + def get_prot_edges(self, with_name=False): + """Return the protocol edges.""" + return self.get_edges(filters=[self.is_prot_edge], with_name=with_name) + + def get_link_edges(self, with_name=False): + """Return the link edges.""" + return self.get_edges(filters=[self.is_link_edge], with_name=with_name) + + def get_nodes_from_range(self, node: str, rng: List[Tuple[int]]): + """Return the nodes from the range.""" + nodes = [] + match rng: + # 2D range + case [(start1, end1), (start2, end2)]: + for x in range(start1, end1 + 1): + for y in range(start2, end2 + 1): + node_name = f"{node}_{x}_{y}" + if self.has_node(node_name): + nodes.append(node_name) + else: + raise ValueError(f"Node {node_name} does not exist") + # 1D range + case [(start, end)]: + for i in range(start, end + 1): + node_name = f"{node}_{i}" + if self.has_node(node_name): + nodes.append(node_name) + else: + raise ValueError(f"Node {node_name} does not exist") + case _: + raise NotImplementedError(f"Unsupported range {rng}") + return nodes + + def get_nodes_from_idx(self, node: str, idx: List[int]): + """Return the nodes from the index.""" + node_name = f"{node}_{'_'.join([str(i) for i in idx])}" + if self.has_node(node_name): + return [node_name] + raise ValueError(f"Node {node_name} does not exist") + + def get_nodes_from_lvl(self, node: str, lvl: int): + """Return the nodes from the level.""" + nodes = self.get_nodes( + filters=[lambda n: n.startswith(node), lambda n: self.nodes[n]["lvl"] == lvl], + with_name=True, + ) + return [name for name, _ in nodes] + + def add_nodes_as_tree( + self, + parent: str, + tree: List[int], + node_type: str, + edge_type: str, + lvl: int=0, + node_obj=None, + edge_obj=None, + connect=True, + ): # pylint: disable=too-many-arguments + """Add nodes as a tree.""" + if lvl == len(tree): + return + for i in range(tree[lvl]): + node = f"{parent}_{i}" + self.add_node(node, type=node_type, lvl=lvl, obj=node_obj) + if connect and lvl > 0: + self.add_edge(parent, node, type=edge_type, obj=edge_obj) + self.add_edge(node, parent, type=edge_type, obj=edge_obj) + self.add_nodes_as_tree( + node, tree, node_type, edge_type, lvl + 1, node_obj, edge_obj, connect + ) + + def add_nodes_as_array( + self, + name: str, + array: Tuple[int], + node_type: str, + edge_type: str = "", + node_obj=None, + edge_obj=None, + connect=True, + ): # pylint: disable=too-many-arguments + """Add nodes as an array.""" + match array: + case [n]: + for i in range(n): + node = f"{name}_{i}" + self.add_node(node, type=node_type, arr_idx=(i,), obj=node_obj) + if i > 0 and connect: + self.add_edge(node, f"{name}_{i-1}", type=edge_type, obj=edge_obj) + self.add_edge(f"{name}_{i-1}", node, type=edge_type, obj=edge_obj) + case [n, m]: + for i in range(n): + for j in range(m): + node = f"{name}_{i}_{j}" + self.add_node(node, type=node_type, arr_idx=(i, j), obj=node_obj) + if i > 0 and connect: + self.add_edge( + node, f"{name}_{i-1}_{j}", type=edge_type, obj=edge_obj + ) + self.add_edge( + f"{name}_{i-1}_{j}", node, type=edge_type, obj=edge_obj + ) + if j > 0 and connect: + self.add_edge( + node, f"{name}_{i}_{j-1}", type=edge_type, obj=edge_obj + ) + self.add_edge( + f"{name}_{i}_{j-1}", node, type=edge_type, obj=edge_obj + ) + case _: + raise NotImplementedError(f"Unsupported array {array}") + + def create_unique_ep_id(self, node) -> int: + """Return the endpoint id.""" + ep_nodes = [name for name, _ in self.get_ep_nodes(with_name=True)] + return sorted(ep_nodes).index(node) + + + def get_node_id(self, node): + """Return the node id.""" + return self.nodes[node]["id"] diff --git a/floogen/model/link.py b/floogen/model/link.py new file mode 100644 index 00000000..eb7dfa9f --- /dev/null +++ b/floogen/model/link.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python3 +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Author: Tim Fischer + +from typing import ClassVar, List, Union, Dict, Optional, NamedTuple +from abc import ABC, abstractmethod +from pydantic import BaseModel + +from floogen.utils import snake_to_camel, sv_struct_typedef + + +class Link(BaseModel, ABC): + """ + Link class to describe a link with instantiation template and + configuration parameters. + """ + + description: str = "" + source: Union[str, List[str]] + dest: Union[str, List[str]] + source_type: str + dest_type: str + is_bidirectional: bool = False + is_array: bool = False + array: list = None + + channel_mapping: ClassVar[Dict] = {} + + @abstractmethod + def declare(self): + """Declare the link in the generated code.""" + + @abstractmethod + def render_ports(self): + """Declare the ports of the link.""" + + @classmethod + def render_enum_decl(cls): + """Render the enum declaration of the link.""" + string = "typedef enum {" + i = 0 + for _, mapping in cls.channel_mapping.items(): + for ch_type, axi_chs in mapping.items(): + for axi_ch in axi_chs: + name = f"{ch_type}_{axi_ch}" + string += f"{snake_to_camel(name)} = {i},\n" + i += 1 + string += f"NumAxiChannels = {i}\n}} axi_ch_e;\n" + return string + + @classmethod + def get_inverted_mapping(cls): + """Return the mapping from axi to physical channels.""" + mappings = {} + for phys_ch, ch_types in cls.channel_mapping.items(): + for ch_type, axi_chs in ch_types.items(): + for axi_ch in axi_chs: + mappings.setdefault(ch_type, {})[axi_ch] = phys_ch + return mappings + + @classmethod + def calc_link_sizes(cls, protocols): + """Infer the link sizes from the network.""" + link_sizes = {} + for phys_ch, axi_chs in cls.channel_mapping.items(): + # Get all protocols that use this channel + prots = [ + p + for p in protocols + if p.name in axi_chs and p.direction == "manager" + ] + # Get only the exact AXI channels that are used by the link + used_axi_chs = [axi_chs[p.name] for p in prots] + # Get the sizes of the AXI channels + axi_ch_sizes = [p.get_axi_channel_sizes() for p in prots] + link_message_sizes = [] + for used_axi_ch, axi_ch_size in zip(used_axi_chs, axi_ch_sizes): + link_message_sizes += [axi_ch_size[ch] for ch in used_axi_ch] + # Get the maximum size of the link + link_sizes[phys_ch] = max(link_message_sizes) + return link_sizes + + @classmethod + def render_flit(cls, protocols): + """Render the flit of the protocol.""" + string = "" + inv_mapping = cls.get_inverted_mapping() + link_sizes = cls.calc_link_sizes(protocols) + for p in protocols: + if p.direction == "manager": + for axi_ch, size in p.get_axi_channel_sizes().items(): + phys_ch = inv_mapping[p.name][axi_ch] + phys_ch_size = link_sizes[phys_ch] + rsvd_size = phys_ch_size - size + struct_dict = { + "hdr": "hdr_t", + axi_ch: f"{p.full_name()}_{axi_ch}_chan_t", + } + if phys_ch_size - size > 0: + struct_dict["rsvd"] = f"logic[{rsvd_size-1}:0]" + string += sv_struct_typedef(f"floo_{p.name}_{axi_ch}_flit_t", struct_dict) + + for phys_ch, size in link_sizes.items(): + struct_dict = { + "hdr": "hdr_t", + "rsvd": f"logic[{size-1}:0]", + } + string += sv_struct_typedef(f"floo_{phys_ch}_generic_flit_t", struct_dict) + return string + + @classmethod + def render_channels(cls) -> str: + """Render the channels of the protocol.""" + string = "" + for phys_ch, axi_chs in cls.channel_mapping.items(): + struct_dict = {} + for axi_name, axi_chs in axi_chs.items(): + for axi_ch in axi_chs: + struct_dict[axi_name + '_' + axi_ch] = f"floo_{axi_name}_{axi_ch}_flit_t" + struct_dict["generic"] = f"floo_{phys_ch}_generic_flit_t" + string += sv_struct_typedef(f"floo_{phys_ch}_chan_t", struct_dict, union=True) + return string + + @classmethod + def render_link_typedefs(cls) -> str: + """Render the typedefs of the protocol.""" + string = "" + for phys_ch in cls.channel_mapping: + struct_dict = {"valid": "logic", "ready": "logic", phys_ch: f"floo_{phys_ch}_chan_t"} + string += sv_struct_typedef(f"floo_{phys_ch}_t", struct_dict) + return string + + +class XYLinks(NamedTuple): + """Class to describe the directed links of a router.""" + + EJECT: Optional[Link] = None + EAST: Optional[Link] = None + NORTH: Optional[Link] = None + SOUTH: Optional[Link] = None + WEST: Optional[Link] = None + + +class NarrowWideLink(Link): + """Link class to describe a NarrowWidelink.""" + + channel_mapping: ClassVar[Dict] = { + "req": {"narrow": ["aw", "w", "ar"], "wide": ["aw", "ar"]}, + "rsp": {"narrow": ["b", "r"], "wide": ["b"]}, + "wide": {"wide": ["w", "r"]}, + } + + req_type: ClassVar[str] = "floo_req_t" + rsp_type: ClassVar[str] = "floo_rsp_t" + wide_type: ClassVar[str] = "floo_wide_t" + + def req_name(self): + """Return the narrow request name.""" + return f"{self.source}_to_{self.dest}_req" + + def rsp_name(self): + """Return the narrow response name.""" + return f"{self.dest}_to_{self.source}_rsp" + + def wide_name(self, is_reversed=False): + """Return the wide name.""" + if is_reversed: + return f"{self.dest}_to_{self.source}_wide" + return f"{self.source}_to_{self.dest}_wide" + + @classmethod + def get_axi_chs(cls): + """Return all the AXI channels.""" + channels = [] + for axi_chs in cls.channel_mapping.values(): + for key, values in axi_chs.items(): + for v in values: + channels.append(f"{key}_{v}") + return channels + + @classmethod + def get_mapping(cls): + """Return the mapping of the link.""" + return cls.channel_mapping + + + + def declare(self): + """Declare the link in the generated code.""" + string = f"{self.req_type} {self.req_name()};\n" + string += f"{self.rsp_type} {self.rsp_name()};\n" + string += f"{self.wide_type} {self.wide_name()};\n" + if not self.is_bidirectional: + string += f"{self.wide_type} {self.wide_name(is_reversed=True)};\n" + return string + "\n" + + def render_ports(self, direction="input"): + """Declare the ports of the link.""" + reverse_direction = "output" if direction == "input" else "input" + ports = [] + ports.append(f"{direction} {self.req_type} {self.req_name()}") + ports.append(f"{reverse_direction} {self.rsp_type} {self.rsp_name()}") + ports.append(f"{direction} {self.wide_type} {self.wide_name()}") + if not self.is_bidirectional: + ports.append( + f"{reverse_direction} {self.wide_type} \ + {self.wide_name(is_reversed=True)}" + ) + return ports + + +class NarrowLink(Link): + """ + Link class to describe a link with instantiation template and + configuration parameters. + """ + + channel_mapping: ClassVar[Dict] = { + "req": {"axi": ["aw", "w", "ar"]}, + "rsp": {"axi": ["b", "r"]}, + } + + def declare(self): + """Declare the link in the generated code.""" + string = "" + string += f"req_t {self.source}_to_{self.dest}_req;\n" + string += f"rsp_t {self.source}_to_{self.dest}_rsp;\n\n" + return string + + def render_ports(self): + """Declare the ports of the link.""" + raise NotImplementedError diff --git a/floogen/model/network.py b/floogen/model/network.py new file mode 100644 index 00000000..9c77c5d8 --- /dev/null +++ b/floogen/model/network.py @@ -0,0 +1,595 @@ +#!/usr/bin/env python3 +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Author: Tim Fischer + +import pathlib +from typing import Optional, List, ClassVar +from importlib import resources + +import networkx as nx +import matplotlib.pyplot as plt +from mako.lookup import Template +from pydantic import BaseModel, ConfigDict, field_validator, model_validator + +from floogen.model.routing import Routing, RouteAlgo, RoutingRule, RoutingTable +from floogen.model.routing import Coord, SimpleId, AddrRange +from floogen.model.graph import Graph +from floogen.model.endpoint import EndpointDesc, Endpoint +from floogen.model.router import RouterDesc, NarrowWideRouter, NarrowWideXYRouter +from floogen.model.connection import ConnectionDesc +from floogen.model.link import NarrowWideLink, XYLinks, NarrowLink +from floogen.model.network_interface import NarrowWideAxiNI +from floogen.model.protocol import AXI4, AXI4Bus +from floogen.utils import clog2 + + +class Network(BaseModel): # pylint: disable=too-many-public-methods + """ + Network class to describe a network with routers and endpoints. + """ + + model_config = ConfigDict(arbitrary_types_allowed=True) + + with resources.path("floogen.templates", "floo_noc_top.sv.mako") as _tpl_path: + tpl: ClassVar = Template(filename=str(_tpl_path)) + + with resources.path("floogen.templates", "floo_flit_pkg.sv.mako") as _tpl_path: + tpl_pkg: ClassVar = Template(filename=str(_tpl_path)) + + name: str + description: Optional[str] + protocols: List[AXI4] + endpoints: List[EndpointDesc] + routers: List[RouterDesc] + connections: List[ConnectionDesc] + graph: Optional[Graph] = None + routing: Routing + + def create_network(self): + """Initialize the network as a graph.""" + self.graph = Graph() + self.create_routers() + self.create_endpoints() + self.create_connections() + + def compile_network(self): + """Compile the network.""" + self.compile_ids() + self.compile_links() + self.compile_endpoints() + self.compile_nis() + self.compile_routers() + + @field_validator("endpoints") + @classmethod + def validate_endpoints(cls, endpoints): + """Check that endpoint names are unique.""" + names = set() + for ep in endpoints: + if ep.name in names: + raise ValueError("Endpoint names must be unique") + names.add(ep.name) + return endpoints + + @field_validator("routers") + @classmethod + def validate_routers(cls, routers): + """Check that router ids are unique.""" + names = set() + for rt in routers: + if rt.name in names: + raise ValueError("router names must be unique") + names.add(rt.name) + return routers + + @field_validator("protocols") + @classmethod + def validate_addr_width(cls, protocols): + """Check that all protocols have the same address width.""" + addr_widths = [prot.addr_width for prot in protocols] + if len(set(addr_widths)) != 1: + raise ValueError("All protocols must have the same address width") + return protocols + + @model_validator(mode="after") + def set_addr_width(self): + """Set the address width of the network.""" + self.routing.addr_width = self.protocols[0].addr_width + return self + + def create_routers(self): + """Create the routers in the network.""" + + for rt_desc in self.routers: + # handle single router + match (rt_desc.array, rt_desc.tree): + # Single router case + case (None, None): + self.graph.add_node(rt_desc.name, obj=rt_desc, type="router") + # 2D mesh case + case ((m, n), None): + self.graph.add_nodes_as_array( + name=rt_desc.name, + array=(m, n), + node_type="router", + edge_type="link", + node_obj=rt_desc, + connect=rt_desc.auto_connect, + ) + # rt.id = Coord(x=x, y=y) + # tree case + case (None, tree_list): + self.graph.add_nodes_as_tree( + parent=rt_desc.name, + tree=tree_list, + node_type="router", + edge_type="link", + node_obj=rt_desc, + connect=rt_desc.auto_connect, + ) + # Invalid case + case (_, _): + raise ValueError("Invalid router description") + + def create_endpoints(self): + """Create the endpoints in the network.""" + + for ep_desc in self.endpoints: + # handle single endpoint + match ep_desc.array: + # Single endpoint case + case None: + self.graph.add_node(ep_desc.name, obj=ep_desc, type="endpoint") + self.graph.add_node(f"{ep_desc.name}_ni", obj=ep_desc, type="network_interface") + if ep_desc.is_sbr(): + self.graph.add_edge(f"{ep_desc.name}_ni", ep_desc.name, type="protocol") + if ep_desc.is_mgr(): + self.graph.add_edge(ep_desc.name, f"{ep_desc.name}_ni", type="protocol") + # 1D array case + case (n,): + self.graph.add_nodes_as_array( + name=ep_desc.name, + array=(n,), + node_type="endpoint", + node_obj=ep_desc, + connect=False, + ) + self.graph.add_nodes_as_array( + name=f"{ep_desc.name}_ni", + array=(n,), + node_type="network_interface", + node_obj=ep_desc, + connect=False, + ) + ep_nodes = self.graph.get_nodes_from_range(ep_desc.name, [(0, n - 1)]) + ni_nodes = self.graph.get_nodes_from_range(f"{ep_desc.name}_ni", [(0, n - 1)]) + if ep_desc.is_sbr(): + for ep_node, ni_node in zip(ep_nodes, ni_nodes): + self.graph.add_edge(ni_node, ep_node, type="protocol") + if ep_desc.is_mgr(): + for ep_node, ni_node in zip(ep_nodes, ni_nodes): + self.graph.add_edge(ep_node, ni_node, type="protocol") + # 2D array case + case (m, n): + self.graph.add_nodes_as_array( + name=ep_desc.name, + array=(m, n), + node_type="endpoint", + node_obj=ep_desc, + connect=False, + ) + self.graph.add_nodes_as_array( + name=f"{ep_desc.name}_ni", + array=(m, n), + node_type="network_interface", + node_obj=ep_desc, + connect=False, + ) + ep_nodes = self.graph.get_nodes_from_range( + ep_desc.name, [(0, m - 1), (0, n - 1)] + ) + ni_nodes = self.graph.get_nodes_from_range( + f"{ep_desc.name}_ni", [(0, m - 1), (0, n - 1)] + ) + if ep_desc.is_sbr(): + for ep_node, ni_node in zip(ep_nodes, ni_nodes): + self.graph.add_edge(ni_node, ep_node, type="protocol") + if ep_desc.is_mgr(): + for ep_node, ni_node in zip(ep_nodes, ni_nodes): + self.graph.add_edge(ep_node, ni_node, type="protocol") + # Invalid case + case _: + raise ValueError("Invalid endpoint description") + + def create_connections(self): + """Initialize the connections in the network.""" + for con in self.connections: + # Get the source nodes + match (con.src_idx, con.src_range, con.src_lvl): + # Explicit node case + case (None, None, None): + srcs = [con.src] + # Get node from index + case (idx, None, None): + srcs = self.graph.get_nodes_from_idx(con.src, idx) + # Get node from range + case (None, rng, None): + srcs = self.graph.get_nodes_from_range(con.src, rng) + # Get node from level + case (None, None, lvl): + srcs = self.graph.get_nodes_from_lvl(con.src, lvl) + # Invalid case + case (_, _, _): + raise ValueError("src_idx, src_range and src_lvl are mutually exclusive") + + # Get the destination nodes + match (con.dst_idx, con.dst_range, con.dst_lvl): + # Explicit node case + case (None, None, None): + dsts = [con.dst] + # Get node from index + case (idx, None, None): + dsts = self.graph.get_nodes_from_idx(con.dst, idx) + # Get node from range + case (None, rng, None): + dsts = self.graph.get_nodes_from_range(con.dst, rng) + # Get node from level + case (None, None, lvl): + dsts = self.graph.get_nodes_from_lvl(con.dst, lvl) + # Invalid case + case (_, _, _): + raise ValueError("dst_idx, dst_range and dst_lvl are mutually exclusive") + + def get_ni_of_ep(ep): + """Get the network interface of an endpoint.""" + if self.graph.is_ep_node(ep): + return self.graph.get_node_obj(ep).get_ni_name(ep) + return ep + + srcs = [get_ni_of_ep(src) for src in srcs] + dsts = [get_ni_of_ep(dst) for dst in dsts] + + # Add edges between the nodes + match (len(srcs), len(dsts), con.allow_multi): + # Normal case where srcs and dsts have the same length + case (n_srcs, n_dsts, _) if n_srcs == n_dsts: + pass + # Multi connection case where srcs is dividable by dsts + case (n_srcs, n_dsts, True) if n_srcs % n_dsts == 0 and n_srcs > n_dsts: + num_multi_con = n_srcs // n_dsts + # Duplicate dsts + dsts = [dst for dst in dsts for _ in range(num_multi_con)] + # Multi connection case where dsts is dividable by srcs + case (n_srcs, n_dsts, True) if n_dsts % n_srcs == 0 and n_dsts > n_srcs: + num_multi_con = n_dsts // n_srcs + # Duplicate srcs + srcs = [src for src in srcs for _ in range(num_multi_con)] + case (_, _, False): + raise ValueError( + "srcs and dsts must have the same length \ + or `allow_multi` must be `True` and lengths \ + must be dividable by each other" + ) + + # Add edges between the nodes + for src, dst in zip(srcs, dsts): + self.graph.add_edge(src, dst, type="link") + if con.bidirectional: + self.graph.add_edge(dst, src, type="link") + + def compile_ids(self): + """Infer the id type from the network.""" + match self.routing.route_algo: + case RouteAlgo.XY: + for node_name, node in self.graph.get_nodes(with_name=True): + if "arr_idx" in self.graph.nodes[node_name]: + x, y = self.graph.get_node_arr_idx(node_name) + else: + x, y = 0, 0 + node_id = Coord(x=x, y=y) + if node.id_offset is not None: + node_id += node.id_offset + self.graph.nodes[node_name]["id"] = node_id + case RouteAlgo.ID: + for ep_name, ep in self.graph.get_ep_nodes(with_name=True): + node_id = SimpleId(id=self.graph.create_unique_ep_id(ep_name)) + if ep.id_offset is not None: + node_id += ep.id_offset + ni_name = ep.get_ni_name(ep_name) + self.graph.nodes[ep_name]["id"] = node_id + self.graph.nodes[ni_name]["id"] = node_id + + def compile_links(self): + """Infer the link type from the network.""" + for edge, _ in self.graph.get_link_edges(with_name=True): + # Check if link is bidirectional + is_bidirectional = self.graph.has_edge(edge[1], edge[0]) + if not is_bidirectional: + raise NotImplementedError( + "Only bidirectional links are \ + supported yet inside the network" + ) + link = { + "source": edge[0], + "dest": edge[1], + "source_type": self.graph.nodes[edge[0]]["type"], + "dest_type": self.graph.nodes[edge[1]]["type"], + "is_bidirectional": is_bidirectional, + } + self.graph.set_edge_obj(edge, NarrowWideLink(**link)) + + def compile_routers(self): + """Infer the router type from the network.""" + for rt_name, _ in self.graph.get_rt_nodes(with_name=True): + match self.routing.route_algo: + case RouteAlgo.XY: + rt_id = self.graph.get_node_id(rt_name) + incoming, outgoing = {}, {} + for edge in self.graph.get_edges_to(rt_name): + neighbor_id = self.graph.get_node_id(edge.source) + incoming_dir = str(Coord.get_dir(rt_id, neighbor_id)) + if incoming_dir in incoming: + raise ValueError("Incoming direction is already defined") + incoming[incoming_dir] = edge + for edge in self.graph.get_edges_from(rt_name): + neighbor_id = self.graph.get_node_id(edge.dest) + outgoing_dir = str(Coord.get_dir(rt_id, neighbor_id)) + if outgoing_dir in outgoing: + raise ValueError("Outgoing direction is already defined") + outgoing[outgoing_dir] = edge + router_dict = { + "name": rt_name, + "incoming": XYLinks(**incoming), + "outgoing": XYLinks(**outgoing), + "id": rt_id, + } + self.graph.set_node_obj(rt_name, NarrowWideXYRouter(**router_dict)) + + case RouteAlgo.ID: + router_dict = { + "name": rt_name, + "incoming": self.graph.get_edges_to(rt_name), + "outgoing": self.graph.get_edges_from(rt_name), + "degree": len(set(self.graph.neighbors(rt_name))), + "routing": self.routing.model_copy(), + } + self.graph.set_node_obj(rt_name, NarrowWideRouter(**router_dict)) + + def compile_endpoints(self): + """Infer the endpoint type from the network.""" + for ep_name, ep in self.graph.get_ep_nodes(with_name=True): + mgr_ports = [] + sbr_ports = [] + if ep.is_mgr(): + for i in ep.mgr_port_protocol: + prot = {} + protocol = [ + p for p in self.protocols if p.name == i and p.direction == "manager" + ][0] + prot["base_name"] = f"{ep.name}_{protocol.name}" + prot["source"] = ep_name + prot["dest"] = ep.get_ni_name(ep_name) + if ep.array is not None: + prot["array"] = ep.array + prot["arr_idx"] = self.graph.get_node_arr_idx(ep_name) + protocol = AXI4Bus(**prot, **protocol.__dict__) + mgr_ports.append(protocol) + self.graph.set_edge_obj((prot["source"], prot["dest"]), mgr_ports) + if ep.is_sbr(): + for i in ep.sbr_port_protocol: + protocol = [ + p for p in self.protocols if p.name == i and p.direction == "subordinate" + ][0] + prot = {} + prot["base_name"] = f"{ep.name}_{protocol.name}" + prot["source"] = ep.get_ni_name(ep_name) + prot["dest"] = ep_name + if ep.array is not None: + prot["array"] = ep.array + prot["arr_idx"] = self.graph.get_node_arr_idx(ep_name) + protocol = AXI4Bus(**prot, **protocol.__dict__) + sbr_ports.append(protocol) + self.graph.set_edge_obj((prot["source"], prot["dest"]), sbr_ports) + # Add endpoint object to the node + self.graph.set_node_obj( + ep_name, Endpoint(mgr_ports=mgr_ports, sbr_ports=sbr_ports, **ep.__dict__) + ) + + def compile_nis(self): + """Compile the endpoints in the network.""" + + for ni_name, ep_desc in self.graph.get_ni_nodes(with_name=True): + ni_dict = { + "name": f"{ni_name}", + "endpoint": ep_desc, + "routing": self.routing, + "addr_range": ep_desc.addr_range.model_copy() if ep_desc.addr_range else None, + "id": self.graph.get_node_id(ni_name).model_copy(), + } + + assert ep_desc + + match ep_desc.array: + # Single endpoint case + case None: + pass + + # 1D array case + case (_,): + if self.routing.route_algo == RouteAlgo.XY: + raise ValueError("Use 2D arrays for XY routing") + node_idx = self.graph.get_node_arr_idx(ni_name)[0] + ni_dict["arr_idx"] = SimpleId(id=node_idx) + ni_dict["addr_range"] = ep_desc.addr_range.model_copy().set_idx(node_idx) + + # 2D array case + case (m, _): + x, y = self.graph.get_node_arr_idx(ni_name) + idx = y * m + x + ni_dict["arr_idx"] = Coord(x=x, y=y) + ni_dict["addr_range"] = ep_desc.addr_range.model_copy().set_idx(idx) + + # Invalid case + case _: + raise ValueError("Invalid endpoint array description") + + sbr_prot_edges = self.graph.get_edges_from(ni_name, filters=[self.graph.is_prot_edge]) + mgr_prot_edges = self.graph.get_edges_to(ni_name, filters=[self.graph.is_prot_edge]) + for protocols in sbr_prot_edges: + for prot in protocols: + match prot.name: + case "narrow": + ni_dict["sbr_narrow_port"] = prot + case "wide": + ni_dict["sbr_wide_port"] = prot + + for protocols in mgr_prot_edges: + for prot in protocols: + match prot.name: + case "narrow": + ni_dict["mgr_narrow_port"] = prot + case "wide": + ni_dict["mgr_wide_port"] = prot + + ni_dict["mgr_link"] = self.graph.get_edges_from( + ni_name, filters=[self.graph.is_link_edge] + )[0] + ni_dict["sbr_link"] = self.graph.get_edges_to( + ni_name, filters=[self.graph.is_link_edge] + )[0] + + self.graph.set_node_obj(ni_name, NarrowWideAxiNI(**ni_dict)) + + def gen_routing_info(self): + """Generate the routing table for the network.""" + self.routing.num_endpoints = len(self.graph.get_ni_nodes()) + self.routing.num_id_bits = clog2(len(self.graph.get_ni_nodes())) + match self.routing.route_algo: + case RouteAlgo.XY: + self.gen_xy_routing_info() + case RouteAlgo.ID: + self.gen_routing_tables() + case _: + raise NotImplementedError("Only XY and IdTable routing is supported yet") + + if self.routing.use_id_table: + self.gen_address_table() + + def gen_routing_tables(self): + """Generate the routing table for the network.""" + for router in self.graph.get_rt_nodes(): + routing_table = [] + ni_sbr_nodes = [ni for ni in self.graph.get_ni_nodes() if ni.is_sbr()] + for ni in ni_sbr_nodes: + shortest_path = nx.shortest_path(self.graph, router.name, ni.name) + out_edge = (router.name, shortest_path[1]) + out_link = self.graph.get_edge_obj(out_edge) + out_idx = router.outgoing.index(out_link) + dest = SimpleId(id=out_idx) + addr_range = AddrRange(start=ni.id.id, size=1) + routing_table.append(RoutingRule(dest=dest, addr_range=addr_range)) + + # Add routing table to the router + routing_table = RoutingTable(rules=routing_table) + routing_table.trim() + router.routing = self.routing.model_copy() + router.routing.table = routing_table + + def gen_xy_routing_info(self): + """Generate the XY routing info for the network.""" + ni_nodes = self.graph.get_ni_nodes() + ni_sbr_nodes = [ni for ni in ni_nodes if ni.is_sbr()] + min_x = min(ni.id.x for ni in ni_nodes) + min_y = min(ni.id.y for ni in ni_nodes) + max_x = max(ni.id.x for ni in ni_nodes) + max_y = max(ni.id.y for ni in ni_nodes) + max_address = max(ni.addr_range.end for ni in ni_sbr_nodes) + self.routing.num_x_bits = clog2(max_x - min_x) + self.routing.num_y_bits = clog2(max_y - min_y) + self.routing.addr_offset_bits = clog2(max_address) + self.routing.id_offset = Coord(x=min_x, y=min_y) + for ni in self.graph.get_ni_nodes(): + ni.routing = self.routing.model_copy() + + def gen_address_table(self): + """Generate the address table for the network.""" + addr_table = [] + ni_sbr_nodes = [ni for ni in self.graph.get_ni_nodes() if ni.is_sbr()] + for ni in ni_sbr_nodes: + dest = ni.id + addr_range = ni.addr_range + addr_rule = RoutingRule(dest=dest, addr_range=addr_range) + addr_table.append(addr_rule) + self.routing.table = RoutingTable(rules=addr_table) + for ni in self.graph.get_ni_nodes(): + ni.routing.table = self.routing.table.model_copy() + + def render_ports(self): + """Render the ports in the generated code.""" + ports, declared_ports = [], [] + for ep in self.graph.get_ep_nodes(): + if ep.name in declared_ports: + continue + ports += ep.render_ports() + declared_ports.append(ep.name) + port_string = ",\n ".join(ports) + "\n" + return port_string + + def render_prots(self): + """Render the protocols in the generated code.""" + string = "" + for prot_list in self.graph.get_prot_edges(): + for prot in prot_list: + string += prot.declare() + return string + + def render_links(self): + """Render the links in the generated code.""" + string = "" + for link in self.graph.get_link_edges(): + string += link.declare() + return string + + def render_routers(self): + """Render the routers in the generated code.""" + string = "" + for rt in self.graph.get_rt_nodes(): + string += rt.render() + return string + + def render_nis(self): + """Render the network interfaces in the generated code.""" + string = "" + for ni in self.graph.get_ni_nodes(): + string += ni.render(noc=self) + return string + + def render_network(self): + """Render the network in the generated code.""" + return self.tpl.render(noc=self) + + def render_link_cfg(self): + """Render the link configuration file""" + prot_names = [prot.name for prot in self.protocols] + if "wide" in prot_names and "narrow" in prot_names: + axi_type, link_type = "narrow_wide", NarrowWideLink + else: + axi_type, link_type = "axi", NarrowLink + + return axi_type, self.tpl_pkg.render( + name=axi_type, noc=self, link=link_type + ) + + def visualize(self, savefig=True, filename: pathlib.Path = "network.png"): + """Visualize the network graph.""" + ni_nodes = [name for name, _ in self.graph.get_ni_nodes(with_name=True)] + router_nodes = [name for name, _ in self.graph.get_rt_nodes(with_name=True)] + filtered_graph = self.graph.subgraph(ni_nodes + router_nodes) + nx.draw(filtered_graph, with_labels=True) + if savefig: + plt.savefig(filename) + else: + plt.show() diff --git a/floogen/model/network_interface.py b/floogen/model/network_interface.py new file mode 100644 index 00000000..d4a1fd2c --- /dev/null +++ b/floogen/model/network_interface.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Author: Tim Fischer + +from typing import Optional, ClassVar +from importlib import resources + +from pydantic import BaseModel +from mako.lookup import Template + +from floogen.model.routing import Id, AddrRange, Routing +from floogen.model.protocol import AXI4 +from floogen.model.link import NarrowWideLink +from floogen.model.endpoint import EndpointDesc + + +class NetworkInterface(BaseModel): + """NetworkInterface class to describe a network interface.""" + + name: str + endpoint: EndpointDesc + description: str = "" + routing: Routing + id: Optional[Id] = None + arr_idx: Optional[Id] = None + addr_range: Optional[AddrRange] = None + + def is_sbr(self) -> bool: + """Return true if the network interface is a subordinate.""" + return self.endpoint.is_sbr() + + def is_mgr(self) -> bool: + """Return true if the network interface is a manager.""" + return self.endpoint.is_mgr() + + +class NarrowWideAxiNI(NetworkInterface): + """ " NarrowWideNI class to describe a narrow-wide network interface.""" + + with resources.path("floogen.templates", "floo_narrow_wide_chimney.sv.mako") as _tpl_path: + tpl: ClassVar = Template(filename=str(_tpl_path)) + + mgr_narrow_port: Optional[AXI4] = None + sbr_narrow_port: Optional[AXI4] = None + mgr_wide_port: Optional[AXI4] = None + sbr_wide_port: Optional[AXI4] = None + mgr_link: NarrowWideLink + sbr_link: NarrowWideLink + + def render(self, **kwargs) -> str: + """Render the network interface.""" + return self.tpl.render(ni=self, **kwargs) diff --git a/floogen/model/protocol.py b/floogen/model/protocol.py new file mode 100644 index 00000000..2c7e15d3 --- /dev/null +++ b/floogen/model/protocol.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Author: Tim Fischer + +from typing import Optional, List, TypeVar, Union +from typing_extensions import Annotated +from pydantic import BaseModel, StringConstraints, model_validator + +from floogen.utils import short_dir, snake_to_camel, sv_param_decl, sv_typedef + +class ProtocolDesc(BaseModel): + """Protocol class to describe a protocol.""" + + name: str + description: Optional[str] = "" + type: str + direction: Annotated[str, StringConstraints(pattern=r"manager|subordinate")] + svdirection: str + + @model_validator(mode="before") + def set_svdirection(self): + """Set the SystemVerilog direction.""" + self["svdirection"] = "input" if self["direction"] == "manager" else "output" + return self + + +class AXI4(ProtocolDesc): + """AXI4 protocol class.""" + + data_width: int + addr_width: int + id_width: int + user_width: int + + def get_axi_channel_sizes(self) -> dict: # pylint: disable=too-many-locals + """Return the sizes of each of the AXI channels.""" + + burst = 2 + resp = 2 + cache = 4 + prot = 3 + qos = 4 + region = 4 + length = 8 + size = 3 + atop = 6 + last = 1 + lock = 1 + + # Variable widths + iw = self.id_width + aw = self.addr_width + dw = self.data_width + uw = self.user_width + + axi_ch_size = {} + axi_ch_size["aw"] = ( + iw + aw + length + size + burst + lock + cache + prot + qos + region + atop + uw + ) + axi_ch_size["w"] = dw + dw // 8 + last + uw + axi_ch_size["b"] = iw + resp + uw + axi_ch_size["ar"] = ( + iw + aw + length + size + burst + lock + cache + prot + qos + region + uw + ) + axi_ch_size["r"] = iw + dw + resp + last + uw + + return axi_ch_size + + def full_name(self) -> str: + """Return the name of the protocol.""" + if "axi" in self.name: + return f"{self.name}_{short_dir(self.svdirection)}" + return f"axi_{self.name}_{short_dir(self.svdirection)}" + + def render_params(self) -> str: + """Render the parameters of the protocol.""" + cfull_name = snake_to_camel(self.full_name()) + string = sv_param_decl(cfull_name + "AddrWidth", self.addr_width) + string += sv_param_decl(cfull_name + "DataWidth", self.data_width) + string += sv_param_decl(cfull_name + "IdWidth", self.id_width) + string += sv_param_decl(cfull_name + "UserWidth", self.user_width) + return string + "\n" + + def render_typedefs(self) -> str: + """Render the typedefs of the protocol.""" + full_name = self.full_name() + string = sv_typedef(full_name + "_addr_t", array_size=self.addr_width) + string += sv_typedef(full_name + "_data_t", array_size=self.data_width) + string += sv_typedef(full_name + "_strb_t", array_size=self.data_width // 8) + string += sv_typedef(full_name + "_id_t", array_size=self.id_width) + string += sv_typedef(full_name + "_user_t", array_size=self.user_width) + string += f"`AXI_TYPEDEF_ALL_CT({full_name}, \ + {full_name}_req_t, \ + {full_name}_rsp_t, \ + {full_name}_addr_t, \ + {full_name}_id_t, \ + {full_name}_data_t, \ + {full_name}_strb_t, \ + {full_name}_user_t)\n\n" + return string + + +class AXI4Bus(AXI4): + """AXI4 bus protocol class.""" + + base_name: str + source: Union[str, List[str]] + dest: Union[str, List[str]] + array: Optional[List[int]] = None + arr_idx: Optional[List[int]] = None + is_declared: bool = False + subtype: str = "" + type_prefix: str = "axi" + + def _invert_dir(self): + """Invert the direction of the protocol.""" + return "input" if self.svdirection == "output" else "output" + + def _type_name(self): + """Return the type name of the protocol.""" + return f"{self.type_prefix}_{self.name}_{short_dir(self.svdirection)}" + + def _array_to_sv_array(self): + """Convert the array to a SystemVerilog array.""" + if self.array is not None: + return "".join([f"[{i-1}:0]" if i != 1 else "" for i in self.array]) + return "" + + def _idx_to_sv_idx(self): + """Convert the array to a SystemVerilog array.""" + if self.arr_idx is not None: + string = "" + for idx, val in zip(self.arr_idx, self.array): + if val != 1: + string += f"[{idx}]" + return string + return "" + + def typedef(self) -> str: + """Return the typedef of the protocol.""" + return f"`AXI_TYPEDEF_ALL_CT({self._type_name()}, \ + {self._type_name()}_req_t, \ + {self._type_name()}_rsp_t, \ + logic[{self.addr_width-1}:0], \ + logic[{self.id_width-1}:0], \ + logic[{self.data_width-1}:0], \ + logic[{self.data_width//8-1}:0], \ + logic[{self.user_width-1}:0])" + + def req_type(self) -> str: + """Return the request type of the protocol.""" + return f"{self._type_name()}_req_t" + + def rsp_type(self) -> str: + """Return the response type of the protocol.""" + return f"{self._type_name()}_rsp_t" + + def req_name(self, port=False, idx=False) -> str: + """Return the request name of the protocol.""" + idx = self._idx_to_sv_idx() if idx else "" + if port: + return f"{self.base_name}_req_{self.svdirection[0]}{idx}" + return f"{self.source}_to_{self.dest}_req" + + def rsp_name(self, port=False, idx=False) -> str: + """Return the response name of the protocol.""" + idx = self._idx_to_sv_idx() if idx else "" + if port: + return f"{self.base_name}_rsp_{self._invert_dir()[0]}{idx}" + return f"{self.dest}_to_{self.source}_rsp" + + def declare(self) -> str: + """Declare the protocol.""" + string = f"{self.req_type()} {self.req_name()};\n" + string += f"{self.rsp_type()} {self.rsp_name()};\n" + return string + "\n" + + def render_port(self) -> List[str]: + """Render the port of the protocol.""" + rev_direction = self._invert_dir() + ports = [] + ports.append( + f"{self.svdirection} {self.req_type()} \ + {self._array_to_sv_array()} {self.req_name(port=True)}" + ) + ports.append( + f"{rev_direction} {self.rsp_type()} \ + {self._array_to_sv_array()} {self.rsp_name(port=True)}" + ) + return ports + + +Protocols = TypeVar("Protocols", bound=ProtocolDesc) diff --git a/floogen/model/router.py b/floogen/model/router.py new file mode 100644 index 00000000..a6d94a40 --- /dev/null +++ b/floogen/model/router.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Author: Tim Fischer + + +from typing import Optional, List, ClassVar, Tuple, Union +from importlib import resources +from abc import ABC, abstractmethod + +from pydantic import BaseModel, field_validator +from mako.lookup import Template + +from floogen.model.routing import Routing, Id, Coord +from floogen.model.link import Link, XYLinks + + +class RouterDesc(BaseModel): + """Router class to describe (arrays of) a router""" + + name: str + array: Optional[Union[Tuple[int], Tuple[int, int]]] = None + tree: Optional[List[int]] = None + id_offset: Optional[Id] = None + auto_connect: Optional[bool] = True + + @field_validator("array", mode="before") + @classmethod + def array_int_to_tuple(cls, v): + """Convert array to tuple if it is an int""" + if isinstance(v, int): + return (v,) + return v + + @field_validator("tree", mode="before") + @classmethod + def tree_int_to_tuple(cls, v): + """Convert tree to tuple if it is an int""" + if isinstance(v, int): + return (v,) + return v + + +class Router(BaseModel, ABC): + """Abstract router class of an actual router""" + + name: str + incoming: List[Link] + outgoing: List[Link] + degree: int + routing: Routing + + @abstractmethod + def render(self): + """Declare the router in the generated code.""" + + +class XYRouter(BaseModel, ABC): + """Abstract router class of an actual router""" + + name: str + incoming: XYLinks + outgoing: XYLinks + degree: int = 5 # XY router always has 5 links + id: Coord + + @abstractmethod + def render(self): + """Declare the router in the generated code.""" + + +class NarrowWideRouter(Router): + """Router class to describe a narrow-wide router""" + + with resources.path("floogen.templates", "floo_narrow_wide_router.sv.mako") as _tpl_path: + _tpl: ClassVar = Template(filename=str(_tpl_path)) + + def render(self): + """Declare the router in the generated code.""" + return self._tpl.render(router=self) + "\n" + + +class NarrowWideXYRouter(XYRouter): + """Router class to describe a narrow-wide router""" + + with resources.path("floogen.templates", "floo_narrow_wide_xy_router.sv.mako") as _tpl_path: + _tpl: ClassVar = Template(filename=str(_tpl_path)) + + def render(self): + """Declare the router in the generated code.""" + return self._tpl.render(router=self) + "\n" diff --git a/floogen/model/routing.py b/floogen/model/routing.py new file mode 100644 index 00000000..1306ad35 --- /dev/null +++ b/floogen/model/routing.py @@ -0,0 +1,393 @@ +#!/usr/bin/env python3 +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Author: Tim Fischer + +from enum import Enum +from typing import Optional, List +from abc import ABC, abstractmethod + +from pydantic import BaseModel, Field, ConfigDict, model_validator, field_validator + +from floogen.utils import ( + cdiv, + sv_param_decl, + sv_typedef, + sv_struct_typedef, + bool_to_sv, + snake_to_camel, +) + + +class RouteAlgo(Enum): + """Routing algorithm enum.""" + + XY = "XYRouting" + YX = "YXRouting" + ID = "IdTable" + + def __str__(self): + return f"{self.name}" + + +class XYDirections(Enum): + """XY directions enum.""" + + EJECT = "Eject" + NORTH = "North" + EAST = "East" + SOUTH = "South" + WEST = "West" + + def __str__(self): + return self.name + + +class Id(BaseModel, ABC): + """ID class.""" + + @abstractmethod + def render(self): + """Declare the Id generated code.""" + + @classmethod + def __get_validators__(cls): + yield cls.validate + + @classmethod + def validate(cls, value): + if not issubclass(value, Id): + raise ValueError("Invalid Object") + return value + + +class SimpleId(Id): + """ID class.""" + + id: int + + def __hash__(self): + return hash(self.id) + + def __add__(self, other): + """Add the ID.""" + return self.id + other.id + + def __sub__(self, other): + """Subtract the ID.""" + return self.id - other.id + + @field_validator("id") + @classmethod + def validate_id(cls, v): + """Validate the ID.""" + if v < 0: + raise ValueError("ID must be positive") + return v + + def render(self, as_index=False): + """Render the SystemVerilog ID.""" + if not as_index: + return f"{self.id}" + return f"[{self.id}]" + + +class Coord(Id): + """2D coordinate class.""" + + x: int + y: int + + def __hash__(self): + return hash((self.x, self.y)) + + def __add__(self, other): + return Coord(x=self.x + other.x, y=self.y + other.y) + + def __sub__(self, other): + return Coord(x=self.x - other.x, y=self.y - other.y) + + def render(self, as_index=False): + """Render the SystemVerilog coordinate.""" + if not as_index: + return f"'{{x: {self.x}, y: {self.y}}}" + return f"[{self.x}][{self.y}]" + + @staticmethod + def get_dir(node, neighbor) -> XYDirections: + """Get the direction from node to neighbor.""" + if node == neighbor: + return XYDirections.EJECT + if node.x == neighbor.x: + if node.y > neighbor.y: + return XYDirections.SOUTH + return XYDirections.NORTH + if node.y == neighbor.y: + if node.x > neighbor.x: + return XYDirections.WEST + return XYDirections.EAST + raise ValueError("Invalid neighbor") + + +class AddrRange(BaseModel): + """Address range class.""" + + start: int = Field(ge=0) + end: int = Field(ge=0) + size: int + base: Optional[int] = None + idx: Optional[int] = None + + def __str__(self): + return f"[{self.start:X}:{self.end:X}]" + + @model_validator(mode="before") + def validate_input(self): + """Validate the address range.""" + if not isinstance(self, dict): + raise ValueError("Invalid address range specification") + addr_dict = {k: v for k, v in self.items() if v is not None} + match addr_dict: + case {"size": size, "base": base, "idx": idx}: + addr_dict["start"] = base + size * idx + addr_dict["end"] = addr_dict["start"] + size + case {"size": size, "base": base}: + addr_dict["start"] = base + addr_dict["end"] = base + size + case {"start": start, "end": end, "size": size}: + if end - start != size: + raise ValueError("Invalid address range specification") + case {"start": start, "end": end}: + addr_dict["size"] = end - start + case {"start": start, "size": size}: + addr_dict["end"] = start + size + case _: + raise ValueError("Invalid address range specification") + return addr_dict + + @model_validator(mode="after") + def validate_output(self): + """Validate the address range.""" + if self.start >= self.end: + raise ValueError("Invalid address range") + return self + + def set_idx(self, idx): + """Update the address range with the given index.""" + self.idx = idx + if self.base is not None: + self.start = self.base + self.size * idx + self.end = self.start + self.size + else: + raise ValueError("Address range base not set") + return self + + +class RoutingRule(BaseModel): + """Routing rule class.""" + + model_config = ConfigDict(arbitrary_types_allowed=True) + + dest: Id + addr_range: AddrRange + + def __str__(self): + return f"{self.addr_range} -> {self.dest}" + + def __lt__(self, other): + return self.addr_range.start < other.addr_range.start + + def render(self, aw=None): + """Render the SystemVerilog routing rule.""" + if aw is not None: + return ( + f"'{{idx: {self.dest.render()}, " + f"start_addr: {aw}'h{self.addr_range.start:0{cdiv(aw,4)}x}, " + f"end_addr: {aw}'h{self.addr_range.end:0{cdiv(aw,4)}x}}}" + ) + return ( + f"'{{idx: {self.dest.render()}, " + f"start_addr: {self.addr_range.start}, " + f"end_addr: {self.addr_range.end}}}" + ) + + +class RoutingTable(BaseModel): + """Routing table class.""" + + rules: List[RoutingRule] + + def __str__(self): + return f"{self.rules}" + + def __len__(self): + return len(self.rules) + + @model_validator(mode="after") + def check_no_overlapping_ranges(self): + """Check if there are no overlapping ranges.""" + rules = sorted(self.rules) + for i in range(len(rules) - 1): + if rules[i].addr_range.end > rules[i + 1].addr_range.start: + raise ValueError( + f"Overlapping ranges: {rules[i].addr_range} and {rules[i+1].addr_range}\n \ + {self.pprint()}" + ) + return rules + + def trim(self): + """Optimize the routing table.""" + # Separate the rules by destination + rules_by_dest = {} + for rule in self.rules: + rules_by_dest.setdefault(rule.dest, []).append(rule) + + # Sort the rules by start address + for dest, ranges in rules_by_dest.items(): + rules_by_dest[dest] = sorted(ranges) + + # Merge the rules if the end of one range is the start of the next + for dest, ranges in rules_by_dest.items(): + i = 0 + while i < len(ranges) - 1: + if ranges[i].addr_range.end == ranges[i + 1].addr_range.start: + ranges[i].addr_range.end = ranges[i + 1].addr_range.end + ranges[i].addr_range.size = ( + ranges[i].addr_range.end - ranges[i].addr_range.start + ) + del ranges[i + 1] + else: + i += 1 + + # Combine the rules into a single table again + self.rules = [] + for dest, ranges in rules_by_dest.items(): + for rule in ranges: + self.rules.append(rule) + + # Validate the routing table + self.model_validate(self) + + def render(self, name, aw=None, id_offset=None): + """Render the SystemVerilog routing table.""" + string = "" + rules = self.rules.copy() + if id_offset is not None: + for rule in rules: + rule.dest -= id_offset + # typedef of the address rule + addr_type = f"logic [{aw-1}:0]" if aw is not None else "id_t" + rule_type_dict = { + "idx": "id_t", + "start_addr": addr_type, + "end_addr": addr_type, + } + string += sv_struct_typedef(f"{name}_rule_t", rule_type_dict) + # size and numbers of rules (of the table) + string += sv_param_decl(f"{snake_to_camel(name)}NumIDs", len(rules)) + string += sv_param_decl(f"{snake_to_camel(name)}NumRules", len(rules)) + "\n" + rules_str = "" + if not rules: + string += sv_param_decl( + f"{snake_to_camel(name)}", + value="'{default: 0}", + dtype=f"{name}_rule_t", + ) + return string + for rule in rules: + rules_str += f"{rule.render(aw)},\n" + rules_str = rules_str[:-2] + string += sv_param_decl( + f"{snake_to_camel(name)}", + value="'{\n" + rules_str + "\n}", + dtype=f"{name}_rule_t", + array_size=len(rules), + ) + return string + + def pprint(self): + """Pretty print the routing table.""" + for rule in self.rules: + print(rule) + + +class Routing(BaseModel): + """Routing Description class.""" + + model_config = ConfigDict(arbitrary_types_allowed=True) + + route_algo: RouteAlgo + use_id_table: bool = True + table: Optional[RoutingTable] = None + addr_offset_bits: Optional[int] = None + id_offset: Optional[Id] = None + num_endpoints: Optional[int] = None + num_id_bits: Optional[int] = None + num_x_bits: Optional[int] = None + num_y_bits: Optional[int] = None + addr_width: Optional[int] = None + rob_idx_bits: int = 4 + + @field_validator("route_algo", mode="before") + @classmethod + def validate_route_algo(cls, v): + """Validate the routing algorithm.""" + if isinstance(v, str): + v = RouteAlgo[v] + return v + + def render_param_decl(self) -> str: + """Render the SystemVerilog parameter declaration.""" + string = "" + string += sv_param_decl("RouteAlgo", self.route_algo.value, dtype="route_algo_e") + string += sv_param_decl("UseIdTable", bool_to_sv(self.use_id_table), dtype="bit") + match (self.route_algo): + case RouteAlgo.XY: + string += sv_param_decl("NumXBits", self.num_x_bits) + string += sv_param_decl("NumYBits", self.num_y_bits) + case RouteAlgo.ID: + string += sv_param_decl("NumIdBits", self.num_id_bits) + case _: + pass + + if self.route_algo == RouteAlgo.XY: + string += sv_param_decl("XYAddrOffsetX", self.addr_offset_bits) + string += sv_param_decl("XYAddrOffsetY", self.addr_offset_bits + self.num_x_bits) + else: + string += sv_param_decl("XYAddrOffsetX", 0) + string += sv_param_decl("XYAddrOffsetY", 0) + if self.route_algo == RouteAlgo.ID and not self.use_id_table: + string += sv_param_decl("IdAddrOffset", self.addr_offset_bits) + else: + string += sv_param_decl("IdAddrOffset", 0) + return string + + def render_typedefs(self) -> str: + """Render the SystemVerilog typedefs.""" + string = "" + string += sv_typedef("rob_idx_t", array_size=self.rob_idx_bits) + match self.route_algo: + case RouteAlgo.XY: + string += sv_typedef("x_bits_t", array_size=self.num_x_bits) + string += sv_typedef("y_bits_t", array_size=self.num_y_bits) + string += sv_struct_typedef("id_t", {"x": "x_bits_t", "y": "y_bits_t"}) + case RouteAlgo.ID: + string += sv_typedef("id_t", array_size=self.num_id_bits) + case _: + pass + return string + + def render_flit_header(self) -> str: + """Render the SystemVerilog flit header.""" + header_fields = { + "rob_req": "logic", + "rob_idx": "rob_idx_t", + "dst_id": "id_t", + "src_id": "id_t", + "last": "logic", + "atop": "logic", + "axi_ch": "axi_ch_e", + } + return sv_struct_typedef("hdr_t", header_fields) diff --git a/floogen/templates/floo_flit_pkg.sv.mako b/floogen/templates/floo_flit_pkg.sv.mako new file mode 100644 index 00000000..b1f021e8 --- /dev/null +++ b/floogen/templates/floo_flit_pkg.sv.mako @@ -0,0 +1,66 @@ +// Copyright 2022 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// This file is auto-generated. Do not edit! Edit the template file instead + +`include "axi/typedef.svh" + +package floo_${name}_pkg; + + import floo_pkg::*; + + //////////////////////// + // AXI Parameters // + //////////////////////// + + ${link.render_enum_decl()} + +% for prot in noc.protocols: + ${prot.render_params()} +% endfor +% for prot in noc.protocols: + ${prot.render_typedefs()} +% endfor + + ///////////////////////// + // Header Typedefs // + ///////////////////////// + + ${noc.routing.render_param_decl()} + + ${noc.routing.render_typedefs()} + + ${noc.routing.render_flit_header()} + + ///////////////////// + // Address Map // + ///////////////////// + +% if noc.routing.use_id_table: + ${noc.routing.table.render(name="addr_map", aw=noc.routing.addr_width, id_offset=noc.routing.id_offset)} +% else: + typedef logic addr_map_rule_t; + localparam int unsigned AddrMapNumIDs = 0; + localparam int unsigned AddrMapNumRules = 0; + localparam addr_map_rule_t AddrMap = '0; +% endif + + //////////////////////// + // Flits Typedefs // + //////////////////////// + + ${link.render_flit(noc.protocols)} + + ////////////////////////// + // Channel Typedefs // + ////////////////////////// + + ${link.render_channels()} + + /////////////////////// + // Link Typedefs // + /////////////////////// + + ${link.render_link_typedefs()} +endpackage diff --git a/floogen/templates/floo_narrow_wide_chimney.sv.mako b/floogen/templates/floo_narrow_wide_chimney.sv.mako new file mode 100644 index 00000000..ec762fe1 --- /dev/null +++ b/floogen/templates/floo_narrow_wide_chimney.sv.mako @@ -0,0 +1,71 @@ +<%def name="int2hex(val, width)"> + <% return f"{width}\'h{val:0{width//4}x}" %> +\ +<% actual_xy_id = ni.id - ni.routing.id_offset if ni.routing.id_offset is not None else ni.id %>\ + +floo_narrow_wide_chimney #( +% if ni.sbr_narrow_port is None: + .EnNarrowSbrPort(1'b0), +% else: + .EnNarrowSbrPort(1'b1), +% endif +% if ni.mgr_narrow_port is None: + .EnNarrowMgrPort(1'b0), +% else: + .EnNarrowMgrPort(1'b1), +% endif +% if ni.sbr_wide_port is None: + .EnWideSbrPort(1'b0), +% else: + .EnWideSbrPort(1'b1), +% endif +% if ni.mgr_wide_port is None: + .EnWideMgrPort(1'b0) +% else: + .EnWideMgrPort(1'b1) +% endif +) ${ni.name} ( + .clk_i, + .rst_ni, + .test_enable_i, + .sram_cfg_i ( '0 ), +% if ni.mgr_narrow_port is not None: + .axi_narrow_in_req_i ( ${ni.mgr_narrow_port.req_name(port=True, idx=True)} ), + .axi_narrow_in_rsp_o ( ${ni.mgr_narrow_port.rsp_name(port=True, idx=True)} ), +% else: + .axi_narrow_in_req_i ( '0 ), + .axi_narrow_in_rsp_o ( ), +% endif +% if ni.sbr_narrow_port is not None: + .axi_narrow_out_req_o ( ${ni.sbr_narrow_port.req_name(port=True, idx=True)} ), + .axi_narrow_out_rsp_i ( ${ni.sbr_narrow_port.rsp_name(port=True, idx=True)} ), +% else: + .axi_narrow_out_req_o ( ), + .axi_narrow_out_rsp_i ( '0 ), +% endif +% if ni.mgr_wide_port is not None: + .axi_wide_in_req_i ( ${ni.mgr_wide_port.req_name(port=True, idx=True)} ), + .axi_wide_in_rsp_o ( ${ni.mgr_wide_port.rsp_name(port=True, idx=True)} ), +% else: + .axi_wide_in_req_i ( '0 ), + .axi_wide_in_rsp_o ( ), +% endif +% if ni.sbr_wide_port is not None: + .axi_wide_out_req_o ( ${ni.sbr_wide_port.req_name(port=True, idx=True)} ), + .axi_wide_out_rsp_i ( ${ni.sbr_wide_port.rsp_name(port=True, idx=True)} ), +% else: + .axi_wide_out_req_o ( ), + .axi_wide_out_rsp_i ( '0 ), +% endif +% if ni.routing.route_algo.value == 'XYRouting': + .id_i ( ${actual_xy_id.render()} ), +% else: + .id_i ( ${ni.id.render()} ), +% endif + .floo_req_o ( ${ni.mgr_link.req_name()} ), + .floo_rsp_i ( ${ni.mgr_link.rsp_name()} ), + .floo_wide_o ( ${ni.mgr_link.wide_name()} ), + .floo_req_i ( ${ni.sbr_link.req_name()} ), + .floo_rsp_o ( ${ni.sbr_link.rsp_name()} ), + .floo_wide_i ( ${ni.sbr_link.wide_name()} ) +); diff --git a/floogen/templates/floo_narrow_wide_router.sv.mako b/floogen/templates/floo_narrow_wide_router.sv.mako new file mode 100644 index 00000000..2cfe8957 --- /dev/null +++ b/floogen/templates/floo_narrow_wide_router.sv.mako @@ -0,0 +1,59 @@ +<% def camelcase(s): + return ''.join(x.capitalize() or '_' for x in s.split('_')) +%>\ +${router.routing.table.render(name=router.name + "_table")} + +${router.incoming[0].req_type} [${len(router.incoming)-1}:0] ${router.name}_req_in; +${router.incoming[0].rsp_type} [${len(router.incoming)-1}:0] ${router.name}_rsp_out; +${router.outgoing[0].req_type} [${len(router.outgoing)-1}:0] ${router.name}_req_out; +${router.outgoing[0].rsp_type} [${len(router.outgoing)-1}:0] ${router.name}_rsp_in; +${router.incoming[0].wide_type} [${len(router.incoming)-1}:0] ${router.name}_wide_in; +${router.outgoing[0].wide_type} [${len(router.outgoing)-1}:0] ${router.name}_wide_out; + +% for i, link in enumerate(router.incoming): + assign ${router.name}_req_in[${i}] = ${link.req_name()}; +% endfor + +% for i, link in enumerate(router.incoming): + assign ${link.rsp_name()} = ${router.name}_rsp_out[${i}]; +% endfor + +% for i, link in enumerate(router.outgoing): + assign ${link.req_name()} = ${router.name}_req_out[${i}]; +% endfor + +% for i, link in enumerate(router.outgoing): + assign ${router.name}_rsp_in[${i}] = ${link.rsp_name()}; +% endfor + +% for i, link in enumerate(router.incoming): + assign ${router.name}_wide_in[${i}] = ${link.wide_name()}; +% endfor + +% for i, link in enumerate(router.outgoing): + assign ${link.wide_name()} = ${router.name}_wide_out[${i}]; +% endfor + +floo_narrow_wide_router #( + .NumRoutes (${router.degree}), + .NumInputs (${len(router.incoming)}), + .NumOutputs (${len(router.outgoing)}), + .ChannelFifoDepth (2), + .OutputFifoDepth (2), + .RouteAlgo (IdTable), + .id_t(id_t), + .NumAddrRules (${len(router.routing.table.rules)}), + .addr_rule_t (${router.name}_table_rule_t) +) ${router.name} ( + .clk_i, + .rst_ni, + .test_enable_i, + .id_i ('0), + .id_route_map_i (${camelcase(router.name + "_table")}), + .floo_req_i (${router.name}_req_in), + .floo_rsp_o (${router.name}_rsp_out), + .floo_req_o (${router.name}_req_out), + .floo_rsp_i (${router.name}_rsp_in), + .floo_wide_i (${router.name}_wide_in), + .floo_wide_o (${router.name}_wide_out) +); diff --git a/floogen/templates/floo_narrow_wide_xy_router.sv.mako b/floogen/templates/floo_narrow_wide_xy_router.sv.mako new file mode 100644 index 00000000..971bd1ce --- /dev/null +++ b/floogen/templates/floo_narrow_wide_xy_router.sv.mako @@ -0,0 +1,60 @@ +<% def camelcase(s): + return ''.join(x.capitalize() or '_' for x in s.split('_')) +%>\ + +${router.incoming[0].req_type} [NumDirections-1:0] ${router.name}_req_in; +${router.incoming[0].rsp_type} [NumDirections-1:0] ${router.name}_rsp_out; +${router.outgoing[0].req_type} [NumDirections-1:0] ${router.name}_req_out; +${router.outgoing[0].rsp_type} [NumDirections-1:0] ${router.name}_rsp_in; +${router.incoming[0].wide_type} [NumDirections-1:0] ${router.name}_wide_in; +${router.outgoing[0].wide_type} [NumDirections-1:0] ${router.name}_wide_out; + +% for dir, link in router.incoming._asdict().items(): + assign ${router.name}_req_in[${camelcase(dir)}] = ${"'0" if link is None else link.req_name()}; +% endfor + +% for dir, link in router.incoming._asdict().items(): + % if link is not None: + assign ${link.rsp_name()} = ${router.name}_rsp_out[${camelcase(dir)}]; + % endif +% endfor + +% for dir, link in router.outgoing._asdict().items(): + % if link is not None: + assign ${link.req_name()} = ${router.name}_req_out[${camelcase(dir)}]; + % endif +% endfor + +% for dir, link in router.outgoing._asdict().items(): + assign ${router.name}_rsp_in[${camelcase(dir)}] = ${"'0" if link is None else link.rsp_name()}; +% endfor + +% for dir, link in router.incoming._asdict().items(): + assign ${router.name}_wide_in[${camelcase(dir)}] = ${"'0" if link is None else link.wide_name()}; +% endfor + +% for dir, link in router.outgoing._asdict().items(): + % if link is not None: + assign ${link.wide_name()} = ${router.name}_wide_out[${camelcase(dir)}]; + % endif +% endfor + +floo_narrow_wide_router #( + .NumRoutes (NumDirections), + .ChannelFifoDepth (2), + .OutputFifoDepth (2), + .RouteAlgo (XYRouting), + .id_t(id_t) +) ${router.name} ( + .clk_i, + .rst_ni, + .test_enable_i, + .id_i (${router.id.render()}), + .id_route_map_i ('0), + .floo_req_i (${router.name}_req_in), + .floo_rsp_o (${router.name}_rsp_out), + .floo_req_o (${router.name}_req_out), + .floo_rsp_i (${router.name}_rsp_in), + .floo_wide_i (${router.name}_wide_in), + .floo_wide_o (${router.name}_wide_out) +); diff --git a/floogen/templates/floo_noc_top.sv.mako b/floogen/templates/floo_noc_top.sv.mako new file mode 100644 index 00000000..95eb9726 --- /dev/null +++ b/floogen/templates/floo_noc_top.sv.mako @@ -0,0 +1,24 @@ +<%! + import datetime +%>\ +// Copyright ${datetime.datetime.now().year} ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// AUTOMATICALLY GENERATED! DO NOT EDIT! + +module ${noc.name}_floo_noc + import floo_pkg::*; + import floo_narrow_wide_pkg::*; +( + input logic clk_i, + input logic rst_ni, + input logic test_enable_i, + ${noc.render_ports()} +); + +${noc.render_links()} +${noc.render_nis()} +${noc.render_routers()} + +endmodule diff --git a/floogen/tests/__init__.py b/floogen/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/floogen/tests/address_test.py b/floogen/tests/address_test.py new file mode 100644 index 00000000..670c2a40 --- /dev/null +++ b/floogen/tests/address_test.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Author: Tim Fischer + +import pytest +from floogen.model.routing import AddrRange, RoutingRule, RoutingTable, SimpleId + + +def test_addr_range_creation1(): + """Test the creation of an AddrRange object.""" + addr_range = AddrRange(start=0, end=50) + assert addr_range.start == 0 + assert addr_range.end == 50 + assert addr_range.size == 50 + assert addr_range.base is None + assert addr_range.idx is None + + +def test_addr_range_creation2(): + """Test the creation of an AddrRange object.""" + addr_range = AddrRange(start=50, size=100) + assert addr_range.start == 50 + assert addr_range.end == 150 + assert addr_range.size == 100 + assert addr_range.base is None + assert addr_range.idx is None + + +def test_addr_range_creation3(): + """Test the creation of an AddrRange object.""" + addr_range = AddrRange(base=50, size=100, idx=5) + assert addr_range.start == 550 + assert addr_range.end == 650 + assert addr_range.size == 100 + assert addr_range.base == 50 + assert addr_range.idx == 5 + + +def test_addr_range_creation4(): + """Test the creation of an AddrRange object.""" + addr_range = AddrRange(base=50, size=100) + assert addr_range.start == 50 + assert addr_range.end == 150 + assert addr_range.size == 100 + assert addr_range.base == 50 + assert addr_range.idx is None + + +def test_addr_range_set_idx(): + """Test the set_idx method of an AddrRange object.""" + addr_range = AddrRange(base=50, size=100) + addr_range.set_idx(1) + assert addr_range.start == 150 + assert addr_range.end == 250 + assert addr_range.size == 100 + assert addr_range.base == 50 + assert addr_range.idx == 1 + + +def test_invalid_addr_range(): + """Test the validation of an AddrRange object.""" + with pytest.raises(ValueError): + AddrRange(start=100, end=0, size=100) + + with pytest.raises(ValueError): + AddrRange(start=0, end=100, size=0) + + with pytest.raises(ValueError): + addr_range = AddrRange(start=0, end=100, size=100) + addr_range.set_idx(2) + +def test_routing_table_len(): + """Test the length of a RoutingTable object.""" + rule1 = RoutingRule(addr_range=AddrRange(start=0, end=10), dest=SimpleId(id=1)) + rule2 = RoutingRule(addr_range=AddrRange(start=11, end=20), dest=SimpleId(id=2)) + routing_table = RoutingTable(rules=[rule1, rule2]) + assert len(routing_table) == 2 + + +def test_check_no_overlapping_ranges(): + """Test the check_no_overlapping_ranges method of a RoutingTable object.""" + rule1 = RoutingRule(addr_range=AddrRange(start=0, end=10), dest=SimpleId(id=1)) + rule2 = RoutingRule(addr_range=AddrRange(start=5, end=15), dest=SimpleId(id=2)) + with pytest.raises(ValueError): + RoutingTable(rules=[rule1, rule2]) + + +def test_trim(): + """Test the trim method of a RoutingTable object.""" + rule1 = RoutingRule(addr_range=AddrRange(start=0, end=10), dest=SimpleId(id=1)) + rule2 = RoutingRule(addr_range=AddrRange(start=10, end=20), dest=SimpleId(id=1)) + rule3 = RoutingRule(addr_range=AddrRange(start=20, end=30), dest=SimpleId(id=2)) + rule4 = RoutingRule(addr_range=AddrRange(start=31, end=40), dest=SimpleId(id=2)) + routing_table = RoutingTable(rules=[rule1, rule2, rule3, rule4]) + routing_table.trim() + expected_rules = [ + RoutingRule(addr_range=AddrRange(start=0, end=20), dest=SimpleId(id=1)), + RoutingRule(addr_range=AddrRange(start=20, end=30), dest=SimpleId(id=2)), + RoutingRule(addr_range=AddrRange(start=31, end=40), dest=SimpleId(id=2)), + ] + assert routing_table.rules == expected_rules diff --git a/floogen/tests/graph_test.py b/floogen/tests/graph_test.py new file mode 100644 index 00000000..324ee7c5 --- /dev/null +++ b/floogen/tests/graph_test.py @@ -0,0 +1,363 @@ +#!/usr/bin/env python3 +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Author: Tim Fischer + +import pytest +from floogen.model.graph import Graph + + +@pytest.fixture(name="graph") +def setup_graph(): + """Return a Graph object.""" + return Graph() + + +def test_add_node(graph): + """Add a router node to the graph.""" + graph.add_node("A", type="router") + assert graph.has_node("A") + assert graph.get_node_obj("A") is None + + +def test_add_edge(graph): + """Add a one-directinal link edge to the graph.""" + graph.add_node("A", type="router") + graph.add_node("B", type="endpoint") + graph.add_edge("A", "B", type="link") + assert graph.has_edge("A", "B") + assert graph.get_edge_obj(("A", "B")) is None + + +def test_add_edge_bidir(graph): + """Add a bidirectional link edge to the graph.""" + graph.add_node("A", type="router") + graph.add_node("B", type="endpoint") + graph.add_edge_bidir("A", "B", type="link") + assert graph.has_edge("A", "B") + assert graph.has_edge("B", "A") + + +def test_get_node_obj(graph): + """Add a router node to the graph and get its object.""" + graph.add_node("A", type="router", obj="Router A") + assert graph.get_node_obj("A") == "Router A" + + +def test_set_node_obj(graph): + """Add a router node to the graph and set its object.""" + graph.add_node("A", type="router") + graph.set_node_obj("A", "Router A") + assert graph.get_node_obj("A") == "Router A" + + +def test_get_node_arr_idx(graph): + """Add a router node to the graph and get its array index.""" + graph.add_node("A", type="router", arr_idx=(0, 1)) + assert graph.get_node_arr_idx("A") == (0, 1) + + +def test_get_node_lvl(graph): + """Add a router node to the graph and get its level.""" + graph.add_node("A", type="router", lvl=2) + assert graph.get_node_lvl("A") == 2 + + +def test_get_edge_obj(graph): + """Add a one-directional link edge to the graph and get its object.""" + graph.add_node("A", type="router") + graph.add_node("B", type="endpoint") + graph.add_edge("A", "B", type="link", obj="Link AB") + assert graph.get_edge_obj(("A", "B")) == "Link AB" + + +def test_set_edge_obj(graph): + """Add a one-directional link edge to the graph and set its object.""" + graph.add_node("A", type="router") + graph.add_node("B", type="endpoint") + graph.add_edge("A", "B", type="link") + graph.set_edge_obj(("A", "B"), "Link AB") + assert graph.get_edge_obj(("A", "B")) == "Link AB" + + +def test_is_rt_node(graph): + """Add a router node to the graph and check if it is a router node.""" + graph.add_node("A", type="router") + assert graph.is_rt_node("A") + + +def test_is_ep_node(graph): + """Add an endpoint node to the graph and check if it is an endpoint node.""" + graph.add_node("A", type="endpoint") + assert graph.is_ep_node("A") + + +def test_is_ni_node(graph): + """Add a network interface node to the graph and check if it is a network interface node.""" + graph.add_node("A", type="network_interface") + assert graph.is_ni_node("A") + + +def test_is_prot_edge(graph): + """Add a protocol edge to the graph and check if it is a protocol edge.""" + graph.add_node("A", type="router") + graph.add_node("B", type="endpoint") + graph.add_edge("A", "B", type="protocol") + assert graph.is_prot_edge(("A", "B")) + + +def test_is_link_edge(graph): + """Add a link edge to the graph and check if it is a link edge.""" + graph.add_node("A", type="router") + graph.add_node("B", type="endpoint") + graph.add_edge("A", "B", type="link") + assert graph.is_link_edge(("A", "B")) + + +def test_get_nodes(graph): + """Add a router node and an endpoint node to the graph and get all nodes.""" + graph.add_node("A", type="router", obj="Router A") + graph.add_node("B", type="endpoint", obj="Endpoint B") + nodes = graph.get_nodes(with_name=True) + assert nodes == [("A", "Router A"), ("B", "Endpoint B")] + + +def test_get_edges(graph): + """Add a one-directional link edge to the graph and get all edges.""" + graph.add_node("A", type="router") + graph.add_node("B", type="endpoint") + graph.add_edge("A", "B", type="link", obj="Link AB") + edges = graph.get_edges(with_name=True) + assert edges == [(("A", "B"), "Link AB")] + + +def test_get_edges_from(graph): + """Get all outgoing edges.""" + graph.add_node("A", type="router") + graph.add_node("B", type="endpoint") + graph.add_node("C", type="endpoint") + graph.add_node("D", type="router") + graph.add_edge("A", "B", type="link") + graph.add_edge("A", "C", type="link") + graph.add_edge("D", "A", type="link") + edges = graph.get_edges_from("A", with_name=True) + assert edges == [(("A", "B"), None), (("A", "C"), None)] + + +def test_get_edges_to(graph): + """Get all incoming edges.""" + graph.add_node("A", type="router") + graph.add_node("B", type="endpoint") + graph.add_node("C", type="endpoint") + graph.add_edge("A", "B", type="link") + graph.add_edge("C", "A", type="link") + edges = graph.get_edges_to("A", with_name=True) + assert edges == [(("C", "A"), None)] + + +def test_get_edges_of(graph): + """Get all edges of a node.""" + graph.add_node("A", type="router") + graph.add_node("B", type="endpoint") + graph.add_node("C", type="endpoint") + graph.add_node("D", type="router") + graph.add_edge("A", "B", type="link") + graph.add_edge("C", "A", type="link") + graph.add_edge("D", "A", type="link") + edges = graph.get_edges_of("A", with_name=True) + assert edges == [(("A", "B"), None), (("C", "A"), None), (("D", "A"), None)] + + +def test_get_ni_nodes(graph): + """Test getting all network interfaces""" + graph.add_node("A", type="network_interface", obj="NI A") + graph.add_node("B", type="router", obj="Router B") + graph.add_node("C", type="network_interface", obj="NI C") + ni_nodes = graph.get_ni_nodes(with_name=True) + assert ni_nodes == [("A", "NI A"), ("C", "NI C")] + + +def test_get_rt_nodes(graph): + """Test getting all routers""" + graph.add_node("A", type="router", obj="Router A") + graph.add_node("B", type="endpoint", obj="Endpoint B") + graph.add_node("C", type="router", obj="Router C") + rt_nodes = graph.get_rt_nodes(with_name=True) + assert rt_nodes == [("A", "Router A"), ("C", "Router C")] + + +def test_get_ep_nodes(graph): + """Test getting all endpoints""" + graph.add_node("A", type="router", obj="Router A") + graph.add_node("B", type="endpoint", obj="Endpoint B") + graph.add_node("C", type="endpoint", obj="Endpoint C") + ep_nodes = graph.get_ep_nodes(with_name=True) + assert ep_nodes == [("B", "Endpoint B"), ("C", "Endpoint C")] + + +def test_get_prot_edges(graph): + """Test getting all protocol edges""" + graph.add_node("A", type="endpoint") + graph.add_node("B", type="network_interface") + graph.add_node("C", type="router") + graph.add_edge("A", "B", type="protocol", obj="Protocol AB") + graph.add_edge("B", "C", type="link", obj="Link BC") + prot_edges = graph.get_prot_edges(with_name=True) + assert prot_edges == [(("A", "B"), "Protocol AB")] + + +def test_get_link_edges(graph): + """Test getting all link edges""" + graph.add_node("A", type="endpoint") + graph.add_node("B", type="network_interface") + graph.add_node("C", type="router") + graph.add_edge("A", "B", type="protocol", obj="Protocol AB") + graph.add_edge("B", "C", type="link", obj="Link BC") + link_edges = graph.get_link_edges(with_name=True) + assert link_edges == [(("B", "C"), "Link BC")] + + +def test_get_nodes_from_range1(graph): + """Test getting all nodes from a range""" + graph.add_node("A_0_0", type="router") + graph.add_node("A_0_1", type="router") + graph.add_node("A_1_0", type="router") + graph.add_node("A_1_1", type="router") + nodes = graph.get_nodes_from_range("A", [(0, 1), (0, 1)]) + assert nodes == ["A_0_0", "A_0_1", "A_1_0", "A_1_1"] + + +def test_get_nodes_from_range2(graph): + """Test getting all nodes from a range""" + graph.add_node("A_0_0", type="router") + graph.add_node("A_0_1", type="router") + graph.add_node("A_1_0", type="router") + graph.add_node("A_1_1", type="router") + nodes = graph.get_nodes_from_range("A", [(0, 1), (0, 0)]) + assert nodes == ["A_0_0", "A_1_0"] + + +def test_get_nodes_from_range3(graph): + """Test getting all nodes from a range""" + graph.add_node("A_0", type="router") + graph.add_node("A_1", type="router") + graph.add_node("A_2", type="router") + graph.add_node("A_3", type="router") + nodes = graph.get_nodes_from_range("A", [(0, 3)]) + assert nodes == ["A_0", "A_1", "A_2", "A_3"] + + +def test_get_nodes_from_idx(graph): + """Test getting all nodes from an index""" + graph.add_node("A_0_0", type="router") + graph.add_node("A_0_1", type="router") + graph.add_node("A_1_0", type="router") + graph.add_node("A_1_1", type="router") + nodes = graph.get_nodes_from_idx("A", [1, 0]) + assert nodes == ["A_1_0"] + + +def test_get_nodes_from_lvl(graph): + """Test getting all nodes from a level""" + graph.add_node("A", type="router", lvl=0) + graph.add_node("A_1", type="router", lvl=1) + graph.add_node("A_2", type="router", lvl=1) + nodes = graph.get_nodes_from_lvl("A", 1) + assert nodes == ["A_1", "A_2"] + + +def test_add_nodes_as_tree(graph): + """Test adding nodes as a tree""" + graph.add_nodes_as_tree("A", [1, 2, 2], "router", "link") + + def assert_node(node, lvl): + assert graph.has_node(node) + assert graph.is_rt_node(node) + assert graph.get_node_lvl(node) == lvl + + def assert_edge(edge): + assert graph.has_edge(*edge) + assert graph.is_link_edge(edge) + assert graph.has_edge(*edge[::-1]) + assert graph.is_link_edge(edge[::-1]) + + assert_node("A_0", 0) + assert_node("A_0_0", 1) + assert_node("A_0_1", 1) + assert_node("A_0_0_0", 2) + assert_node("A_0_0_1", 2) + assert_node("A_0_1_0", 2) + assert_node("A_0_1_1", 2) + assert_edge(("A_0", "A_0_0")) + assert_edge(("A_0", "A_0_1")) + assert_edge(("A_0_0", "A_0_0_0")) + assert_edge(("A_0_0", "A_0_0_1")) + assert_edge(("A_0_1", "A_0_1_0")) + assert_edge(("A_0_1", "A_0_1_1")) + + +def test_add_nodes_as_array1(graph): + """Test adding nodes as an array""" + + def assert_node(node, idx): + assert graph.has_node(node) + assert graph.is_rt_node(node) + assert graph.get_node_arr_idx(node) == idx + def assert_edge(edge): + assert graph.has_edge(*edge) + assert graph.is_link_edge(edge) + assert graph.has_edge(*edge[::-1]) + assert graph.is_link_edge(edge[::-1]) + + graph.add_nodes_as_array("A", [2], "router", "link") + assert_node("A_0", (0,)) + assert_node("A_1", (1,)) + assert_edge(("A_0", "A_1")) + + +def test_add_nodes_as_array2(graph): + """Test adding nodes as an array""" + + def assert_node(node, idx): + assert graph.has_node(node) + assert graph.is_rt_node(node) + assert graph.get_node_arr_idx(node) == idx + + def assert_edge(edge): + assert graph.has_edge(*edge) + assert graph.is_link_edge(edge) + + graph.add_nodes_as_array("A", [2, 2], "router", "link") + assert_node("A_0_0", (0, 0)) + assert_node("A_0_1", (0, 1)) + assert_node("A_1_0", (1, 0)) + assert_node("A_1_1", (1, 1)) + assert_edge(("A_0_0", "A_0_1")) + assert_edge(("A_0_0", "A_1_0")) + assert_edge(("A_0_1", "A_1_1")) + assert_edge(("A_1_0", "A_1_1")) + + +def test_create_unique_ep_id(graph): + """Test creating a unique endpoint id""" + graph.add_node("A", type="endpoint") + graph.add_node("A_0", type="endpoint") + graph.add_node("B", type="endpoint") + graph.add_node("C", type="endpoint") + graph.add_node("R", type="router") + graph.add_node("S", type="endpoint") + assert graph.create_unique_ep_id("A") == 0 + assert graph.create_unique_ep_id("A_0") == 1 + assert graph.create_unique_ep_id("B") == 2 + assert graph.create_unique_ep_id("C") == 3 + assert graph.create_unique_ep_id("S") == 4 + with pytest.raises(ValueError): + graph.create_unique_ep_id("R") + + +def test_get_node_id(graph): + """Test getting the id of a node""" + graph.add_node("A", type="router", id=1) + assert graph.get_node_id("A") == 1 diff --git a/floogen/utils.py b/floogen/utils.py new file mode 100644 index 00000000..485e6bbb --- /dev/null +++ b/floogen/utils.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Author: Tim Fischer + +import math +import shutil +import subprocess +from typing import Union + + +def cdiv(x, y) -> int: + """Returns the ceiling of x/y.""" + return -(-x // y) + + +def clog2(x) -> int: + """Returns the ceiling of log2(x).""" + return math.ceil(math.log2(x)) + + +def camel_to_snake(name: str) -> str: + """Converts a camel case string to snake case.""" + return "".join(["_" + i.lower() if i.isupper() else i for i in name]).lstrip("_") + + +def snake_to_camel(name: str) -> str: + """Converts a snake case string to camel case.""" + return "".join([i.capitalize() for i in name.split("_")]) + + +def short_dir(direction: str) -> str: + """Returns the short direction string.""" + return "in" if direction == "input" else "out" + +def bool_to_sv(value: bool) -> str: + """Converts a boolean to a SystemVerilog string.""" + return "1'b1" if value else "1'b0" + + +def sv_param_decl( + name: str, + value: Union[int, str], + ptype: str = "localparam", + dtype: str = "int unsigned", + array_size: int = None, +) -> str: + """Declare a SystemVerilog parameter.""" + assert ptype in ["localparam", "parameter"] + assert array_size is None or isinstance(array_size, int) + if array_size is None: + return f"{ptype} {dtype} {name} = {value};\n" + return f"{ptype} {dtype}[{array_size-1}:0] {name} = {value};\n" + + +def sv_typedef(name: str, dtype: str = "logic", array_size: int = None) -> str: + """Declare a SystemVerilog typedef.""" + assert array_size is None or isinstance(array_size, int) + if array_size is None: + return f"typedef {dtype} {name};\n" + return f"typedef {dtype}[{array_size-1}:0] {name};\n" + + +def sv_struct_typedef(name: str, fields: dict, union=False) -> str: + """Declare a SystemVerilog struct typedef.""" + if union: + typedef = "typedef union packed {\n" + else: + typedef = "typedef struct packed {\n" + for field, dtype in fields.items(): + typedef += f" {dtype} {field};\n" + typedef += f"}} {name};\n\n" + return typedef + +def verible_format(string: str) -> str: + """Format the string using verible-verilog-format.""" + if shutil.which("verible-verilog-format") is None: + raise RuntimeError( + "verible-verilog-format not found. Please install it to use the --format option." + ) + # Format the output using verible-verilog-format, by piping it into the stdin + # of the formatter and capturing the stdout + return subprocess.run( + ["verible-verilog-format", "-"], + input=string, + capture_output=True, + text=True, + check=True, + ).stdout diff --git a/src/floo_axi_chimney.sv b/hw/floo_axi_chimney.sv similarity index 94% rename from src/floo_axi_chimney.sv rename to hw/floo_axi_chimney.sv index c2de29cd..baa63616 100644 --- a/src/floo_axi_chimney.sv +++ b/hw/floo_axi_chimney.sv @@ -24,21 +24,6 @@ module floo_axi_chimney /// Every atomic transactions needs to have a unique ID /// and one ID is reserved for non-atomic transactions parameter int unsigned MaxAtomicTxns = 1, - /// Routing Algorithm - parameter route_algo_e RouteAlgo = IdTable, - /// Whether to look up the coordinates in a table or - /// directly read them from the request address - parameter bit UseIdTable = 1'b0, - /// X Coordinate address offset for XY routing - parameter int unsigned XYAddrOffsetX = 0, - /// Y Coordinate address offset for XY routing - parameter int unsigned XYAddrOffsetY = 0, - /// ID address offset for ID routing - parameter int unsigned IdAddrOffset = 0, - /// Number of Endpoints in the system, only used for Table based routing - parameter int unsigned NumIDs = 0, - /// Number of rules in the routing table, only used for Table based routing - parameter int unsigned NumRules = 0, /// ID address offset for ID routing parameter int unsigned MaxTxns = 32, /// Maximum number of outstanding requests per ID @@ -51,10 +36,6 @@ module floo_axi_chimney parameter bit CutAx = 1'b0, /// Cut timing paths of incoming responses parameter bit CutRsp = 1'b1, - /// Type of Coordinates/Id - parameter type id_t = logic, - /// Only used for IDRouting - parameter type id_rule_t = logic, /// Type for implementation inputs and outputs parameter type sram_cfg_t = logic ) ( @@ -69,8 +50,6 @@ module floo_axi_chimney input axi_out_rsp_t axi_out_rsp_i, /// Coordinates/ID of the current tile input id_t id_i, - /// Routing table - input id_rule_t[NumRules-1:0] id_map_i, /// Output to NoC output floo_req_t floo_req_o, output floo_rsp_t floo_rsp_o, @@ -370,20 +349,20 @@ module floo_axi_chimney assign addr_to_decode[ArReq] = axi_ar_queue.addr; floo_route_comp #( - .RouteAlgo ( RouteAlgo ), - .UseIdTable ( UseIdTable ), - .XYAddrOffsetX ( XYAddrOffsetX ), - .XYAddrOffsetY ( XYAddrOffsetY ), - .IdAddrOffset ( IdAddrOffset ), - .NumIDs ( NumIDs ), - .NumRules ( NumRules ), - .id_t ( id_t ), - .id_rule_t ( id_rule_t ), - .addr_t ( axi_in_addr_t ) + .RouteAlgo ( RouteAlgo ), + .UseIdTable ( UseIdTable ), + .XYAddrOffsetX ( XYAddrOffsetX ), + .XYAddrOffsetY ( XYAddrOffsetY ), + .IdAddrOffset ( IdAddrOffset ), + .NumIDs ( AddrMapNumIDs ), + .NumRules ( AddrMapNumRules ), + .AddrMap ( AddrMap ), + .id_t ( id_t ), + .id_rule_t ( addr_map_rule_t ), + .addr_t ( axi_in_addr_t ) ) i_floo_narrow_route_comp [NumAddrDecoders-1:0] ( .clk_i, .rst_ni, - .id_map_i, .addr_i ( addr_to_decode ), .id_o ( decoded_id ) ); diff --git a/src/floo_axi_pkg.sv b/hw/floo_axi_pkg.sv similarity index 66% rename from src/floo_axi_pkg.sv rename to hw/floo_axi_pkg.sv index d929c4cd..d7b9ebb1 100644 --- a/src/floo_axi_pkg.sv +++ b/hw/floo_axi_pkg.sv @@ -4,7 +4,6 @@ // // This file is auto-generated. Do not edit! Edit the template file instead - `include "axi/typedef.svh" package floo_axi_pkg; @@ -24,84 +23,85 @@ package floo_axi_pkg; NumAxiChannels = 5 } axi_ch_e; + localparam int unsigned AxiInAddrWidth = 32; localparam int unsigned AxiInDataWidth = 64; localparam int unsigned AxiInIdWidth = 3; localparam int unsigned AxiInUserWidth = 1; + localparam int unsigned AxiOutAddrWidth = 32; localparam int unsigned AxiOutDataWidth = 64; localparam int unsigned AxiOutIdWidth = 3; localparam int unsigned AxiOutUserWidth = 1; - typedef logic [AxiInAddrWidth-1:0] axi_in_addr_t; - typedef logic [AxiInDataWidth-1:0] axi_in_data_t; - typedef logic [AxiInDataWidth/8-1:0] axi_in_strb_t; - typedef logic [AxiInIdWidth-1:0] axi_in_id_t; - typedef logic [AxiInUserWidth-1:0] axi_in_user_t; - - typedef logic [AxiOutAddrWidth-1:0] axi_out_addr_t; - typedef logic [AxiOutDataWidth-1:0] axi_out_data_t; - typedef logic [AxiOutDataWidth/8-1:0] axi_out_strb_t; - typedef logic [AxiOutIdWidth-1:0] axi_out_id_t; - typedef logic [AxiOutUserWidth-1:0] axi_out_user_t; + typedef logic [31:0] axi_in_addr_t; + typedef logic [63:0] axi_in_data_t; + typedef logic [7:0] axi_in_strb_t; + typedef logic [2:0] axi_in_id_t; + typedef logic [0:0] axi_in_user_t; `AXI_TYPEDEF_ALL_CT(axi_in, axi_in_req_t, axi_in_rsp_t, axi_in_addr_t, axi_in_id_t, axi_in_data_t, axi_in_strb_t, axi_in_user_t) + + + typedef logic [31:0] axi_out_addr_t; + typedef logic [63:0] axi_out_data_t; + typedef logic [7:0] axi_out_strb_t; + typedef logic [2:0] axi_out_id_t; + typedef logic [0:0] axi_out_user_t; `AXI_TYPEDEF_ALL_CT(axi_out, axi_out_req_t, axi_out_rsp_t, axi_out_addr_t, axi_out_id_t, axi_out_data_t, axi_out_strb_t, axi_out_user_t) + + ///////////////////////// // Header Typedefs // ///////////////////////// localparam route_algo_e RouteAlgo = XYRouting; - typedef logic [8:0] rob_idx_t; - + localparam bit UseIdTable = 1'b0; localparam int unsigned NumXBits = 3; localparam int unsigned NumYBits = 3; - localparam int unsigned XAddrOffset = 16; - localparam int unsigned YAddrOffset = 19; + localparam int unsigned XYAddrOffsetX = 16; + localparam int unsigned XYAddrOffsetY = 19; + localparam int unsigned IdAddrOffset = 0; + + typedef logic [3:0] rob_idx_t; + typedef logic [2:0] x_bits_t; + typedef logic [2:0] y_bits_t; typedef struct packed { - logic [NumXBits-1:0] x; - logic [NumYBits-1:0] y; - } xy_id_t; - - function automatic logic [NumXBits-1:0] get_x_coord(logic [31:0] addr); - return addr[XAddrOffset+:NumXBits]; - endfunction - - function automatic logic [NumYBits-1:0] get_y_coord(logic [31:0] addr); - return addr[YAddrOffset+:NumYBits]; - endfunction - - function automatic xy_id_t get_xy_id(logic [31:0] addr); - xy_id_t id; - id.x = get_x_coord(addr); - id.y = get_y_coord(addr); - return id; - endfunction - - function automatic logic [31:0] get_base_addr(xy_id_t id); - logic [31:0] addr; - addr = id.x << XAddrOffset + id.y << YAddrOffset; - return addr; - endfunction + x_bits_t x; + y_bits_t y; + } id_t; + + typedef struct packed { logic rob_req; rob_idx_t rob_idx; - xy_id_t dst_id; - xy_id_t src_id; + id_t dst_id; + id_t src_id; logic last; logic atop; axi_ch_e axi_ch; } hdr_t; - //////////////////////////// - // AXI Flits Typedefs // - //////////////////////////// + + + ///////////////////// + // Address Map // + ///////////////////// + + typedef logic addr_map_rule_t; + localparam int unsigned AddrMapNumIDs = 0; + localparam int unsigned AddrMapNumRules = 0; + localparam addr_map_rule_t AddrMap = '0; + + //////////////////////// + // Flits Typedefs // + //////////////////////// typedef struct packed { hdr_t hdr; @@ -131,11 +131,6 @@ package floo_axi_pkg; axi_in_r_chan_t r; } floo_axi_r_flit_t; - - //////////////////////////////// - // Generic Flits Typedefs // - //////////////////////////////// - typedef struct packed { hdr_t hdr; logic [73:0] rsvd; @@ -147,6 +142,7 @@ package floo_axi_pkg; } floo_rsp_generic_flit_t; + ////////////////////////// // Channel Typedefs // ////////////////////////// @@ -164,6 +160,8 @@ package floo_axi_pkg; floo_rsp_generic_flit_t generic; } floo_rsp_chan_t; + + /////////////////////// // Link Typedefs // /////////////////////// @@ -180,5 +178,5 @@ package floo_axi_pkg; floo_rsp_chan_t rsp; } floo_rsp_t; -endpackage +endpackage diff --git a/src/floo_cdc.sv b/hw/floo_cdc.sv similarity index 100% rename from src/floo_cdc.sv rename to hw/floo_cdc.sv diff --git a/src/floo_cut.sv b/hw/floo_cut.sv similarity index 100% rename from src/floo_cut.sv rename to hw/floo_cut.sv diff --git a/src/floo_fifo.sv b/hw/floo_fifo.sv similarity index 100% rename from src/floo_fifo.sv rename to hw/floo_fifo.sv diff --git a/src/floo_mesh.sv b/hw/floo_mesh.sv similarity index 100% rename from src/floo_mesh.sv rename to hw/floo_mesh.sv diff --git a/src/floo_mesh_ruche.sv b/hw/floo_mesh_ruche.sv similarity index 100% rename from src/floo_mesh_ruche.sv rename to hw/floo_mesh_ruche.sv diff --git a/src/floo_meta_buffer.sv b/hw/floo_meta_buffer.sv similarity index 100% rename from src/floo_meta_buffer.sv rename to hw/floo_meta_buffer.sv diff --git a/src/floo_narrow_wide_chimney.sv b/hw/floo_narrow_wide_chimney.sv similarity index 95% rename from src/floo_narrow_wide_chimney.sv rename to hw/floo_narrow_wide_chimney.sv index 30751e7d..99f0d9a0 100644 --- a/src/floo_narrow_wide_chimney.sv +++ b/hw/floo_narrow_wide_chimney.sv @@ -29,21 +29,6 @@ module floo_narrow_wide_chimney /// Every atomic transactions needs to have a unique ID /// and one ID is reserved for non-atomic transactions parameter int unsigned MaxAtomicTxns = 1, - /// Routing Algorithm - parameter route_algo_e RouteAlgo = IdTable, - /// Whether to look up the coordinates in a table or - /// directly read them from the request address with an offset - parameter bit UseIdTable = 1'b0, - /// X Coordinate address offset for XY routing - parameter int unsigned XYAddrOffsetX = 0, - /// Y Coordinate address offset for XY routing - parameter int unsigned XYAddrOffsetY = 0, - /// ID address offset for ID routing - parameter int unsigned IdAddrOffset = 0, - /// Number of Endpoints in the system, only used for Table based routing - parameter int unsigned NumIDs = 0, - /// Number of rules in the routing table, only used for Table based routing - parameter int unsigned NumRules = 0, /// Number of maximum oustanding requests on the narrow network parameter int unsigned NarrowMaxTxns = 32, /// Number of maximum oustanding requests on the wide network @@ -64,10 +49,6 @@ module floo_narrow_wide_chimney parameter bit CutAx = 1'b1, /// Cut timing paths of incoming responses from the NoC parameter bit CutRsp = 1'b1, - /// Type of Coordinates/Id - parameter type id_t = logic, - /// Only used for IDRouting - parameter type id_rule_t = logic, /// Type for implementation inputs and outputs parameter type sram_cfg_t = logic ) ( @@ -86,8 +67,6 @@ module floo_narrow_wide_chimney input axi_wide_out_rsp_t axi_wide_out_rsp_i, /// Coordinates/ID of the current tile input id_t id_i, - /// Routing table - input id_rule_t[NumRules-1:0] id_map_i, /// Output to NoC output floo_req_t floo_req_o, output floo_rsp_t floo_rsp_o, @@ -243,7 +222,7 @@ module floo_narrow_wide_chimney end else begin : gen_narrow_err_slv_port axi_err_slv #( - .AxiIdWidth ( NarrowInIdWidth ), + .AxiIdWidth ( AxiNarrowInIdWidth ), .ATOPs ( AtopSupport ), .axi_req_t ( axi_narrow_in_req_t ), .axi_resp_t ( axi_narrow_in_rsp_t ) @@ -302,7 +281,7 @@ module floo_narrow_wide_chimney end end else begin : gen_wide_err_slv_port axi_err_slv #( - .AxiIdWidth ( WideInIdWidth ), + .AxiIdWidth ( AxiWideInIdWidth ), .ATOPs ( AtopSupport ), .axi_req_t ( axi_wide_in_req_t ), .axi_resp_t ( axi_wide_in_rsp_t ) @@ -629,20 +608,20 @@ module floo_narrow_wide_chimney assign addr_to_decode[WideArReq] = axi_wide_ar_queue.addr; floo_route_comp #( - .RouteAlgo ( RouteAlgo ), - .UseIdTable ( UseIdTable ), - .XYAddrOffsetX ( XYAddrOffsetX ), - .XYAddrOffsetY ( XYAddrOffsetY ), - .IdAddrOffset ( IdAddrOffset ), - .NumIDs ( NumIDs ), - .NumRules ( NumRules ), - .id_t ( id_t ), - .id_rule_t ( id_rule_t ), - .addr_t ( addr_t ) + .RouteAlgo ( RouteAlgo ), + .UseIdTable ( UseIdTable ), + .XYAddrOffsetX ( XYAddrOffsetX ), + .XYAddrOffsetY ( XYAddrOffsetY ), + .IdAddrOffset ( IdAddrOffset ), + .NumIDs ( AddrMapNumIDs ), + .NumRules ( AddrMapNumRules ), + .AddrMap ( AddrMap ), + .id_t ( id_t ), + .id_rule_t ( addr_map_rule_t ), + .addr_t ( addr_t ) ) i_floo_narrow_route_comp [NumAddrDecoders-1:0] ( .clk_i, .rst_ni, - .id_map_i, .addr_i ( addr_to_decode ), .id_o ( decoded_id ) ); @@ -1046,8 +1025,8 @@ module floo_narrow_wide_chimney .AtopSupport ( AtopSupport ), .MaxAtomicTxns ( MaxAtomicTxns ), .buf_t ( narrow_id_out_buf_t ), - .IdInWidth ( NarrowInIdWidth ), - .IdOutWidth ( NarrowOutIdWidth ), + .IdInWidth ( AxiNarrowInIdWidth ), + .IdOutWidth ( AxiNarrowOutIdWidth ), .axi_req_t ( axi_narrow_in_req_t ), .axi_rsp_t ( axi_narrow_in_rsp_t ) ) i_narrow_meta_buffer ( @@ -1065,7 +1044,7 @@ module floo_narrow_wide_chimney ); end else begin : gen_no_narrow_mgr_port axi_err_slv #( - .AxiIdWidth ( NarrowInIdWidth ), + .AxiIdWidth ( AxiNarrowInIdWidth ), .ATOPs ( AtopSupport ), .axi_req_t ( axi_narrow_in_req_t ), .axi_resp_t ( axi_narrow_in_rsp_t ) @@ -1087,8 +1066,8 @@ module floo_narrow_wide_chimney .AtopSupport ( 1'b1 ), .MaxAtomicTxns ( MaxAtomicTxns ), .buf_t ( wide_id_out_buf_t ), - .IdInWidth ( WideInIdWidth ), - .IdOutWidth ( WideOutIdWidth ), + .IdInWidth ( AxiWideInIdWidth ), + .IdOutWidth ( AxiWideOutIdWidth ), .axi_req_t ( axi_wide_in_req_t ), .axi_rsp_t ( axi_wide_in_rsp_t ) ) i_wide_meta_buffer ( @@ -1106,7 +1085,7 @@ module floo_narrow_wide_chimney ); end else begin : gen_no_wide_mgr_port axi_err_slv #( - .AxiIdWidth ( WideInIdWidth ), + .AxiIdWidth ( AxiWideInIdWidth ), .ATOPs ( 1'b1 ), .axi_req_t ( axi_wide_in_req_t ), .axi_resp_t ( axi_wide_in_rsp_t ) @@ -1133,16 +1112,16 @@ module floo_narrow_wide_chimney // Multiple outstanding atomics need to use different IDs // Non-atomic transactions all use the same ID - `ASSERT_INIT(ToSmallIdWidth, 1 + AtopSupport * MaxAtomicTxns <= 2**NarrowOutIdWidth) + `ASSERT_INIT(ToSmallIdWidth, 1 + AtopSupport * MaxAtomicTxns <= 2**AxiNarrowOutIdWidth) // If Network Interface has no subordinate port, make sure that `RoBType` is `NoRoB` `ASSERT_INIT(NoNarrowSbrPortRobType, EnNarrowSbrPort || (NarrowRoBType == NoRoB)) `ASSERT_INIT(NoWideSbrPortRobType, EnWideSbrPort || (WideRoBType == NoRoB)) // Check that all addresses have the same width - `ASSERT_INIT(SameAddrWidth1, NarrowInAddrWidth == NarrowOutAddrWidth) - `ASSERT_INIT(SameAddrWidth2, WideInAddrWidth == NarrowOutAddrWidth) - `ASSERT_INIT(SameAddrWidth3, WideInAddrWidth == WideOutAddrWidth) + `ASSERT_INIT(SameAddrWidth1, AxiNarrowInAddrWidth == AxiNarrowOutAddrWidth) + `ASSERT_INIT(SameAddrWidth2, AxiWideInAddrWidth == AxiNarrowOutAddrWidth) + `ASSERT_INIT(SameAddrWidth3, AxiWideInAddrWidth == AxiWideOutAddrWidth) // Data and valid signals must be stable/asserted when ready is low `ASSERT(NarrowReqOutStableValid, floo_req_o.valid && diff --git a/src/floo_narrow_wide_pkg.sv b/hw/floo_narrow_wide_pkg.sv similarity index 57% rename from src/floo_narrow_wide_pkg.sv rename to hw/floo_narrow_wide_pkg.sv index ba0caf6f..a13fdd3b 100644 --- a/src/floo_narrow_wide_pkg.sv +++ b/hw/floo_narrow_wide_pkg.sv @@ -4,7 +4,6 @@ // // This file is auto-generated. Do not edit! Edit the template file instead - `include "axi/typedef.svh" package floo_narrow_wide_pkg; @@ -29,113 +28,118 @@ package floo_narrow_wide_pkg; NumAxiChannels = 10 } axi_ch_e; - localparam int unsigned NarrowInAddrWidth = 48; - localparam int unsigned NarrowInDataWidth = 64; - localparam int unsigned NarrowInIdWidth = 4; - localparam int unsigned NarrowInUserWidth = 5; - - localparam int unsigned NarrowOutAddrWidth = 48; - localparam int unsigned NarrowOutDataWidth = 64; - localparam int unsigned NarrowOutIdWidth = 2; - localparam int unsigned NarrowOutUserWidth = 5; - - localparam int unsigned WideInAddrWidth = 48; - localparam int unsigned WideInDataWidth = 512; - localparam int unsigned WideInIdWidth = 3; - localparam int unsigned WideInUserWidth = 1; - - localparam int unsigned WideOutAddrWidth = 48; - localparam int unsigned WideOutDataWidth = 512; - localparam int unsigned WideOutIdWidth = 1; - localparam int unsigned WideOutUserWidth = 1; - - typedef logic [NarrowInAddrWidth-1:0] axi_narrow_in_addr_t; - typedef logic [NarrowInDataWidth-1:0] axi_narrow_in_data_t; - typedef logic [NarrowInDataWidth/8-1:0] axi_narrow_in_strb_t; - typedef logic [NarrowInIdWidth-1:0] axi_narrow_in_id_t; - typedef logic [NarrowInUserWidth-1:0] axi_narrow_in_user_t; - - typedef logic [NarrowOutAddrWidth-1:0] axi_narrow_out_addr_t; - typedef logic [NarrowOutDataWidth-1:0] axi_narrow_out_data_t; - typedef logic [NarrowOutDataWidth/8-1:0] axi_narrow_out_strb_t; - typedef logic [NarrowOutIdWidth-1:0] axi_narrow_out_id_t; - typedef logic [NarrowOutUserWidth-1:0] axi_narrow_out_user_t; - - typedef logic [WideInAddrWidth-1:0] axi_wide_in_addr_t; - typedef logic [WideInDataWidth-1:0] axi_wide_in_data_t; - typedef logic [WideInDataWidth/8-1:0] axi_wide_in_strb_t; - typedef logic [WideInIdWidth-1:0] axi_wide_in_id_t; - typedef logic [WideInUserWidth-1:0] axi_wide_in_user_t; - - typedef logic [WideOutAddrWidth-1:0] axi_wide_out_addr_t; - typedef logic [WideOutDataWidth-1:0] axi_wide_out_data_t; - typedef logic [WideOutDataWidth/8-1:0] axi_wide_out_strb_t; - typedef logic [WideOutIdWidth-1:0] axi_wide_out_id_t; - typedef logic [WideOutUserWidth-1:0] axi_wide_out_user_t; + localparam int unsigned AxiNarrowInAddrWidth = 48; + localparam int unsigned AxiNarrowInDataWidth = 64; + localparam int unsigned AxiNarrowInIdWidth = 4; + localparam int unsigned AxiNarrowInUserWidth = 1; + + + localparam int unsigned AxiNarrowOutAddrWidth = 48; + localparam int unsigned AxiNarrowOutDataWidth = 64; + localparam int unsigned AxiNarrowOutIdWidth = 2; + localparam int unsigned AxiNarrowOutUserWidth = 1; + + + localparam int unsigned AxiWideInAddrWidth = 48; + localparam int unsigned AxiWideInDataWidth = 512; + localparam int unsigned AxiWideInIdWidth = 3; + localparam int unsigned AxiWideInUserWidth = 1; + + + localparam int unsigned AxiWideOutAddrWidth = 48; + localparam int unsigned AxiWideOutDataWidth = 512; + localparam int unsigned AxiWideOutIdWidth = 1; + localparam int unsigned AxiWideOutUserWidth = 1; + + + typedef logic [47:0] axi_narrow_in_addr_t; + typedef logic [63:0] axi_narrow_in_data_t; + typedef logic [7:0] axi_narrow_in_strb_t; + typedef logic [3:0] axi_narrow_in_id_t; + typedef logic [0:0] axi_narrow_in_user_t; `AXI_TYPEDEF_ALL_CT(axi_narrow_in, axi_narrow_in_req_t, axi_narrow_in_rsp_t, axi_narrow_in_addr_t, axi_narrow_in_id_t, axi_narrow_in_data_t, axi_narrow_in_strb_t, axi_narrow_in_user_t) + + + typedef logic [47:0] axi_narrow_out_addr_t; + typedef logic [63:0] axi_narrow_out_data_t; + typedef logic [7:0] axi_narrow_out_strb_t; + typedef logic [1:0] axi_narrow_out_id_t; + typedef logic [0:0] axi_narrow_out_user_t; `AXI_TYPEDEF_ALL_CT(axi_narrow_out, axi_narrow_out_req_t, axi_narrow_out_rsp_t, axi_narrow_out_addr_t, axi_narrow_out_id_t, axi_narrow_out_data_t, axi_narrow_out_strb_t, axi_narrow_out_user_t) + + + typedef logic [47:0] axi_wide_in_addr_t; + typedef logic [511:0] axi_wide_in_data_t; + typedef logic [63:0] axi_wide_in_strb_t; + typedef logic [2:0] axi_wide_in_id_t; + typedef logic [0:0] axi_wide_in_user_t; `AXI_TYPEDEF_ALL_CT(axi_wide_in, axi_wide_in_req_t, axi_wide_in_rsp_t, axi_wide_in_addr_t, axi_wide_in_id_t, axi_wide_in_data_t, axi_wide_in_strb_t, axi_wide_in_user_t) + + + typedef logic [47:0] axi_wide_out_addr_t; + typedef logic [511:0] axi_wide_out_data_t; + typedef logic [63:0] axi_wide_out_strb_t; + typedef logic [0:0] axi_wide_out_id_t; + typedef logic [0:0] axi_wide_out_user_t; `AXI_TYPEDEF_ALL_CT(axi_wide_out, axi_wide_out_req_t, axi_wide_out_rsp_t, axi_wide_out_addr_t, axi_wide_out_id_t, axi_wide_out_data_t, axi_wide_out_strb_t, axi_wide_out_user_t) + + ///////////////////////// // Header Typedefs // ///////////////////////// localparam route_algo_e RouteAlgo = XYRouting; - typedef logic [8:0] rob_idx_t; - + localparam bit UseIdTable = 1'b0; localparam int unsigned NumXBits = 3; localparam int unsigned NumYBits = 3; - localparam int unsigned XAddrOffset = 36; - localparam int unsigned YAddrOffset = 39; + localparam int unsigned XYAddrOffsetX = 16; + localparam int unsigned XYAddrOffsetY = 19; + localparam int unsigned IdAddrOffset = 0; + + typedef logic [3:0] rob_idx_t; + typedef logic [2:0] x_bits_t; + typedef logic [2:0] y_bits_t; typedef struct packed { - logic [NumXBits-1:0] x; - logic [NumYBits-1:0] y; - } xy_id_t; - - function automatic logic [NumXBits-1:0] get_x_coord(logic [47:0] addr); - return addr[XAddrOffset+:NumXBits]; - endfunction - - function automatic logic [NumYBits-1:0] get_y_coord(logic [47:0] addr); - return addr[YAddrOffset+:NumYBits]; - endfunction - - function automatic xy_id_t get_xy_id(logic [47:0] addr); - xy_id_t id; - id.x = get_x_coord(addr); - id.y = get_y_coord(addr); - return id; - endfunction - - function automatic logic [47:0] get_base_addr(xy_id_t id); - logic [47:0] addr; - addr = id.x << XAddrOffset + id.y << YAddrOffset; - return addr; - endfunction + x_bits_t x; + y_bits_t y; + } id_t; + + typedef struct packed { logic rob_req; rob_idx_t rob_idx; - xy_id_t dst_id; - xy_id_t src_id; + id_t dst_id; + id_t src_id; logic last; logic atop; axi_ch_e axi_ch; } hdr_t; - //////////////////////////// - // AXI Flits Typedefs // - //////////////////////////// + + + ///////////////////// + // Address Map // + ///////////////////// + + typedef logic addr_map_rule_t; + localparam int unsigned AddrMapNumIDs = 0; + localparam int unsigned AddrMapNumRules = 0; + localparam addr_map_rule_t AddrMap = '0; + + //////////////////////// + // Flits Typedefs // + //////////////////////// typedef struct packed { hdr_t hdr; @@ -168,7 +172,7 @@ package floo_narrow_wide_pkg; typedef struct packed { hdr_t hdr; axi_wide_in_aw_chan_t aw; - logic [4:0] rsvd; + logic [0:0] rsvd; } floo_wide_aw_flit_t; typedef struct packed { @@ -179,13 +183,13 @@ package floo_narrow_wide_pkg; typedef struct packed { hdr_t hdr; axi_wide_in_b_chan_t b; - logic [69:0] rsvd; + logic [65:0] rsvd; } floo_wide_b_flit_t; typedef struct packed { hdr_t hdr; axi_wide_in_ar_chan_t ar; - logic [10:0] rsvd; + logic [6:0] rsvd; } floo_wide_ar_flit_t; typedef struct packed { @@ -194,19 +198,14 @@ package floo_narrow_wide_pkg; logic [58:0] rsvd; } floo_wide_r_flit_t; - - //////////////////////////////// - // Generic Flits Typedefs // - //////////////////////////////// - typedef struct packed { hdr_t hdr; - logic [91:0] rsvd; + logic [87:0] rsvd; } floo_req_generic_flit_t; typedef struct packed { hdr_t hdr; - logic [75:0] rsvd; + logic [71:0] rsvd; } floo_rsp_generic_flit_t; typedef struct packed { @@ -215,6 +214,7 @@ package floo_narrow_wide_pkg; } floo_wide_generic_flit_t; + ////////////////////////// // Channel Typedefs // ////////////////////////// @@ -241,6 +241,8 @@ package floo_narrow_wide_pkg; floo_wide_generic_flit_t generic; } floo_wide_chan_t; + + /////////////////////// // Link Typedefs // /////////////////////// @@ -263,5 +265,5 @@ package floo_narrow_wide_pkg; floo_wide_chan_t wide; } floo_wide_t; -endpackage +endpackage diff --git a/src/floo_narrow_wide_router.sv b/hw/floo_narrow_wide_router.sv similarity index 100% rename from src/floo_narrow_wide_router.sv rename to hw/floo_narrow_wide_router.sv diff --git a/src/floo_pkg.sv b/hw/floo_pkg.sv similarity index 100% rename from src/floo_pkg.sv rename to hw/floo_pkg.sv diff --git a/src/floo_rob.sv b/hw/floo_rob.sv similarity index 100% rename from src/floo_rob.sv rename to hw/floo_rob.sv diff --git a/src/floo_rob_wrapper.sv b/hw/floo_rob_wrapper.sv similarity index 100% rename from src/floo_rob_wrapper.sv rename to hw/floo_rob_wrapper.sv diff --git a/src/floo_route_comp.sv b/hw/floo_route_comp.sv similarity index 93% rename from src/floo_route_comp.sv rename to hw/floo_route_comp.sv index 8da50dee..a0c5924d 100644 --- a/src/floo_route_comp.sv +++ b/hw/floo_route_comp.sv @@ -29,13 +29,14 @@ module floo_route_comp parameter type id_t = logic, /// The type of the rules parameter type id_rule_t = logic, + /// The address map to use for the address decoder + parameter id_rule_t [NumRules-1:0] AddrMap = '{default: '0}, /// The address type parameter type addr_t = logic ) ( input logic clk_i, input logic rst_ni, input addr_t addr_i, - input id_rule_t [NumRules-1:0] id_map_i, output id_t id_o ); @@ -51,7 +52,7 @@ module floo_route_comp .idx_t ( id_t ) ) i_addr_dst_decode ( .addr_i ( addr_i ), - .addr_map_i ( id_map_i ), + .addr_map_i ( AddrMap ), .idx_o ( id_o ), .dec_valid_o ( ), .dec_error_o ( dec_error ), diff --git a/src/floo_route_select.sv b/hw/floo_route_select.sv similarity index 100% rename from src/floo_route_select.sv rename to hw/floo_route_select.sv diff --git a/src/floo_router.sv b/hw/floo_router.sv similarity index 100% rename from src/floo_router.sv rename to hw/floo_router.sv diff --git a/src/floo_simple_rob.sv b/hw/floo_simple_rob.sv similarity index 100% rename from src/floo_simple_rob.sv rename to hw/floo_simple_rob.sv diff --git a/src/floo_vc_arbiter.sv b/hw/floo_vc_arbiter.sv similarity index 100% rename from src/floo_vc_arbiter.sv rename to hw/floo_vc_arbiter.sv diff --git a/src/floo_wormhole_arbiter.sv b/hw/floo_wormhole_arbiter.sv similarity index 100% rename from src/floo_wormhole_arbiter.sv rename to hw/floo_wormhole_arbiter.sv diff --git a/include/floo_noc/typedef.svh b/hw/include/floo_noc/typedef.svh similarity index 100% rename from include/floo_noc/typedef.svh rename to hw/include/floo_noc/typedef.svh diff --git a/src/synth/floo_synth_axi_chimney.sv b/hw/synth/floo_synth_axi_chimney.sv similarity index 100% rename from src/synth/floo_synth_axi_chimney.sv rename to hw/synth/floo_synth_axi_chimney.sv diff --git a/src/synth/floo_synth_endpoint.sv b/hw/synth/floo_synth_endpoint.sv similarity index 100% rename from src/synth/floo_synth_endpoint.sv rename to hw/synth/floo_synth_endpoint.sv diff --git a/src/synth/floo_synth_mesh.sv b/hw/synth/floo_synth_mesh.sv similarity index 100% rename from src/synth/floo_synth_mesh.sv rename to hw/synth/floo_synth_mesh.sv diff --git a/src/synth/floo_synth_mesh_ruche.sv b/hw/synth/floo_synth_mesh_ruche.sv similarity index 100% rename from src/synth/floo_synth_mesh_ruche.sv rename to hw/synth/floo_synth_mesh_ruche.sv diff --git a/src/synth/floo_synth_narrow_wide_chimney.sv b/hw/synth/floo_synth_narrow_wide_chimney.sv similarity index 100% rename from src/synth/floo_synth_narrow_wide_chimney.sv rename to hw/synth/floo_synth_narrow_wide_chimney.sv diff --git a/src/synth/floo_synth_narrow_wide_router.sv b/hw/synth/floo_synth_narrow_wide_router.sv similarity index 100% rename from src/synth/floo_synth_narrow_wide_router.sv rename to hw/synth/floo_synth_narrow_wide_router.sv diff --git a/src/synth/floo_synth_router.sv b/hw/synth/floo_synth_router.sv similarity index 97% rename from src/synth/floo_synth_router.sv rename to hw/synth/floo_synth_router.sv index 57caef9d..1277720c 100644 --- a/src/synth/floo_synth_router.sv +++ b/hw/synth/floo_synth_router.sv @@ -13,7 +13,7 @@ module floo_synth_router input logic rst_ni, input logic test_enable_i, - input xy_id_t xy_id_i, + input id_t xy_id_i, input floo_req_t [NumRoutes-1:0] req_i, input floo_rsp_t [NumRoutes-1:0] rsp_i, @@ -51,7 +51,7 @@ module floo_synth_router .ChannelFifoDepth ( 2 ), .RouteAlgo ( XYRouting ), .IdWidth ( 4 ), - .id_t ( xy_id_t ), + .id_t ( id_t ), .NumAddrRules ( 1 ) ) i_req_floo_router ( .clk_i, diff --git a/src/synth/floo_synth_router_simple.sv b/hw/synth/floo_synth_router_simple.sv similarity index 90% rename from src/synth/floo_synth_router_simple.sv rename to hw/synth/floo_synth_router_simple.sv index 13d90854..ff208f88 100644 --- a/src/synth/floo_synth_router_simple.sv +++ b/hw/synth/floo_synth_router_simple.sv @@ -6,14 +6,14 @@ module floo_synth_router_simple import floo_pkg::*; - import floo_axi_flit_pkg::*; - import floo_param_pkg::*; + import floo_axi_pkg::*; + import floo_test_pkg::*; #( parameter int unsigned DataWidth = 32, parameter type data_t = logic [DataWidth-1:0], parameter type flit_t = struct packed { data_t data; - xy_id_t id; + id_t id; logic last; } ) ( @@ -21,7 +21,7 @@ module floo_synth_router_simple input logic rst_ni, input logic test_enable_i, - input xy_id_t xy_id_i, + input id_t xy_id_i, input flit_t [NumRoutes-1:0] data_i, output flit_t [NumRoutes-1:0] data_o, @@ -40,7 +40,7 @@ module floo_synth_router_simple .OutputFifoDepth ( 0 ), .RouteAlgo ( XYRouting ), .IdWidth ( 4 ), - .id_t ( xy_id_t ), + .id_t ( id_t ), .NumAddrRules ( 1 ) ) i_req_floo_router ( .clk_i, diff --git a/test/tb_floo_axi_chimney.sv b/hw/tb/tb_floo_axi_chimney.sv similarity index 93% rename from test/tb_floo_axi_chimney.sv rename to hw/tb/tb_floo_axi_chimney.sv index ae7a8a06..8450121b 100644 --- a/test/tb_floo_axi_chimney.sv +++ b/hw/tb/tb_floo_axi_chimney.sv @@ -17,14 +17,14 @@ module tb_floo_axi_chimney; localparam time ApplTime = 2ns; localparam time TestTime = 8ns; - localparam NumReads0 = 1000; - localparam NumWrites0 = 1000; - localparam NumReads1 = 1000; - localparam NumWrites1 = 1000; + localparam int unsigned NumReads0 = 1000; + localparam int unsigned NumWrites0 = 1000; + localparam int unsigned NumReads1 = 1000; + localparam int unsigned NumWrites1 = 1000; localparam bit AtopSupport = 1'b1; - localparam NumTargets = 2; + localparam int unsigned NumTargets = 2; localparam int unsigned ReorderBufferSize = 64; localparam int unsigned MaxTxns = 32; @@ -120,7 +120,6 @@ module tb_floo_axi_chimney; floo_axi_chimney #( .AtopSupport ( AtopSupport ), .MaxAtomicTxns ( 4 ), - .RouteAlgo ( floo_pkg::IdTable ), .MaxTxns ( MaxTxns ), .MaxTxnsPerId ( MaxTxnsPerId ), .ReorderBufferSize ( ReorderBufferSize ) @@ -143,7 +142,6 @@ module tb_floo_axi_chimney; floo_axi_chimney #( .AtopSupport ( AtopSupport ), .MaxAtomicTxns ( 4 ), - .RouteAlgo ( floo_pkg::IdTable ), .MaxTxns ( MaxTxns ), .MaxTxnsPerId ( MaxTxnsPerId ), .ReorderBufferSize ( ReorderBufferSize ) @@ -216,11 +214,13 @@ module tb_floo_axi_chimney; .rsp_t ( axi_in_rsp_t ), .AxiIdWidth ( AxiInIdWidth ) ) i_axi_bw_monitor ( - .clk_i ( clk ), - .en_i ( rst_n ), - .end_of_sim_i ( &end_of_sim ), - .req_i ( node_man_req[0] ), - .rsp_i ( node_man_rsp[0] ) + .clk_i ( clk ), + .en_i ( rst_n ), + .end_of_sim_i ( &end_of_sim ), + .req_i ( node_man_req[0] ), + .rsp_i ( node_man_rsp[0] ), + .ar_in_flight_o ( ), + .aw_in_flight_o ( ) ); initial begin diff --git a/test/tb_floo_dma_chimney.sv b/hw/tb/tb_floo_dma_chimney.sv similarity index 89% rename from test/tb_floo_dma_chimney.sv rename to hw/tb/tb_floo_dma_chimney.sv index 1d4c9709..20e72713 100644 --- a/test/tb_floo_dma_chimney.sv +++ b/hw/tb/tb_floo_dma_chimney.sv @@ -17,7 +17,7 @@ module tb_floo_dma_chimney; localparam time ApplTime = 0ns; localparam time TestTime = 10ns; - localparam NumTargets = 2; + localparam int unsigned NumTargets = 2; localparam int unsigned ReorderBufferSize = 128; localparam int unsigned MaxTxns = 32; @@ -83,7 +83,7 @@ module tb_floo_dma_chimney; .end_of_sim_o ( end_of_sim[0] ) ); - axi_channel_compare #( + axi_chan_compare #( .aw_chan_t ( axi_in_aw_chan_t ), .w_chan_t ( axi_in_w_chan_t ), .b_chan_t ( axi_in_b_chan_t ), @@ -91,8 +91,9 @@ module tb_floo_dma_chimney; .r_chan_t ( axi_in_r_chan_t ), .req_t ( axi_in_req_t ), .resp_t ( axi_in_rsp_t ) - ) i_axi_channel_compare_0 ( - .clk_i ( clk ), + ) i_axi_chan_compare_0 ( + .clk_a_i ( clk ), + .clk_b_i ( clk ), .axi_a_req ( node_man_req[0] ), .axi_a_res ( node_man_rsp[0] ), .axi_b_req ( sub_req_id_mapped[1] ), @@ -145,16 +146,17 @@ module tb_floo_dma_chimney; .floo_rsp_i ( chimney_rsp[0] ) ); - axi_channel_compare #( + axi_chan_compare #( .aw_chan_t ( axi_in_aw_chan_t ), .w_chan_t ( axi_in_w_chan_t ), .b_chan_t ( axi_in_b_chan_t ), .ar_chan_t ( axi_in_ar_chan_t ), .r_chan_t ( axi_in_r_chan_t ), .req_t ( axi_in_req_t ), - .resp_t ( axi_in_rsp_t ) - ) i_axi_channel_compare_1 ( - .clk_i(clk), + .resp_t ( axi_in_rsp_t ) + ) i_axi_chan_compare_1 ( + .clk_a_i ( clk ), + .clk_b_i ( clk ), .axi_a_req ( node_man_req[1] ), .axi_a_res ( node_man_rsp[1] ), .axi_b_req ( sub_req_id_mapped[0] ), @@ -172,7 +174,7 @@ module tb_floo_dma_chimney; .MemBaseAddr ( MemBaseAddr ), .MemSize ( MemSize ), .axi_req_t ( axi_in_req_t ), - .axi_rsp_t ( axi_in_rsp_t ), + .axi_rsp_t ( axi_in_rsp_t ), .JobId ( 1 ) ) i_floo_dma_test_node_1 ( .clk_i ( clk ), @@ -186,14 +188,16 @@ module tb_floo_dma_chimney; axi_bw_monitor #( .req_t ( axi_in_req_t ), - .rsp_t ( axi_in_rsp_t ), + .rsp_t ( axi_in_rsp_t ), .AxiIdWidth ( AxiInIdWidth ) ) i_axi_bw_monitor ( - .clk_i ( clk ), - .en_i ( rst_n ), - .end_of_sim_i ( &end_of_sim ), - .req_i ( node_man_req[0] ), - .rsp_i ( node_man_rsp[0] ) + .clk_i ( clk ), + .en_i ( rst_n ), + .end_of_sim_i ( &end_of_sim ), + .req_i ( node_man_req[0] ), + .rsp_i ( node_man_rsp[0] ), + .ar_in_flight_o ( ), + .aw_in_flight_o ( ) ); initial begin diff --git a/test/tb_floo_dma_mesh.sv b/hw/tb/tb_floo_dma_mesh.sv similarity index 83% rename from test/tb_floo_dma_mesh.sv rename to hw/tb/tb_floo_dma_mesh.sv index a53c2b8d..c706f9e9 100644 --- a/test/tb_floo_dma_mesh.sv +++ b/hw/tb/tb_floo_dma_mesh.sv @@ -5,6 +5,7 @@ // Author: Tim Fischer `include "floo_noc/typedef.svh" +`include "common_cells/assertions.svh" module tb_floo_dma_mesh; @@ -19,27 +20,23 @@ module tb_floo_dma_mesh; localparam int unsigned NumY = 4; localparam int unsigned NumMax = (NumX > NumY) ? NumX : NumY; - `FLOO_NOC_TYPEDEF_XY_ID_T(xy_id_t, NumX+2, NumY+2) - - localparam int unsigned HBMChannels = NumY; - localparam int unsigned HBMSize = 32'h10000; // 64KB localparam int unsigned HBMLatency = 100; - localparam int unsigned MemSize = HBMSize; + localparam axi_narrow_in_addr_t HBMSize = 48'h10000; // 64KB + localparam axi_narrow_in_addr_t MemSize = HBMSize; + + `ASSERT_INIT(NotEnoughXBits, $clog2(NumX + 2) <= $bits(x_bits_t)) + `ASSERT_INIT(NotEnoughYBits, $clog2(NumY + 2) <= $bits(y_bits_t)) + `ASSERT_INIT(NotEnoughAddrOffset, $clog2(HBMSize) <= XYAddrOffsetX) // Narrow Wide Chimney parameters localparam bit CutAx = 1'b1; localparam bit CutRsp = 1'b0; - localparam bit NarrowRoBSimple = 1'b1; localparam int unsigned NarrowMaxTxnsPerId = 4; localparam int unsigned NarrowReorderBufferSize = 32'd256; - localparam bit WideRoBSimple = 1'b0; localparam int unsigned WideMaxTxnsPerId = 32; localparam int unsigned WideReorderBufferSize = 32'd64; localparam int unsigned NarrowMaxTxns = 32; localparam int unsigned WideMaxTxns = 32; - localparam route_algo_e RouteAlgo = XYRouting; - localparam int unsigned XYAddrOffsetX = $clog2(HBMSize); - localparam int unsigned XYAddrOffsetY = $clog2(HBMSize) + $clog2(NumX+1); localparam int unsigned ChannelFifoDepth = 2; localparam int unsigned OutputFifoDepth = 32; @@ -104,13 +101,12 @@ module tb_floo_dma_mesh; floo_hbm_model #( .TA ( ApplTime ), .TT ( TestTime ), - .TCK ( CyclTime ), .Latency ( HBMLatency ), .NumChannels ( 1 ), - .MemSize ( HBMSize ), - .DataWidth ( WideOutDataWidth ), - .UserWidth ( WideOutUserWidth ), - .IdWidth ( WideOutIdWidth ), + .AddrWidth ( AxiWideOutAddrWidth ), + .DataWidth ( AxiWideOutDataWidth ), + .UserWidth ( AxiWideOutUserWidth ), + .IdWidth ( AxiWideOutIdWidth ), .axi_req_t ( axi_wide_out_req_t ), .axi_rsp_t ( axi_wide_out_rsp_t ), .aw_chan_t ( axi_wide_out_aw_chan_t ), @@ -128,13 +124,12 @@ module tb_floo_dma_mesh; floo_hbm_model #( .TA ( ApplTime ), .TT ( TestTime ), - .TCK ( CyclTime ), .Latency ( HBMLatency ), .NumChannels ( 1 ), - .MemSize ( HBMSize ), - .DataWidth ( NarrowOutDataWidth ), - .UserWidth ( NarrowOutUserWidth ), - .IdWidth ( NarrowOutIdWidth ), + .AddrWidth ( AxiNarrowOutAddrWidth ), + .DataWidth ( AxiNarrowOutDataWidth ), + .UserWidth ( AxiNarrowOutUserWidth ), + .IdWidth ( AxiNarrowOutIdWidth ), .axi_req_t ( axi_narrow_out_req_t ), .axi_rsp_t ( axi_narrow_out_rsp_t ), .aw_chan_t ( axi_narrow_out_aw_chan_t ), @@ -156,7 +151,7 @@ module tb_floo_dma_mesh; floo_req_t [NumChimneys-1:0] req_hbm_in, req_hbm_out; floo_rsp_t [NumChimneys-1:0] rsp_hbm_in, rsp_hbm_out; floo_wide_t [NumChimneys-1:0] wide_hbm_in, wide_hbm_out; - xy_id_t [NumChimneys-1:0] xy_id_hbm; + id_t [NumChimneys-1:0] xy_id_hbm; if (i == North) begin : gen_north_hbm_chimneys for (genvar j = 0; j < NumChimneys; j++) begin : gen_hbm_chimney_xy_id @@ -182,7 +177,7 @@ module tb_floo_dma_mesh; end else if (i == East) begin : gen_east_hbm_chimneys for (genvar j = 0; j < NumChimneys; j++) begin : gen_hbm_chimney_xy_id - assign xy_id_hbm[j] = '{x: NumX, y: j+1}; + assign xy_id_hbm[j] = '{x: NumX+1, y: j+1}; end assign req_hbm_in = req_hor_pos[NumX]; assign rsp_hbm_in = rsp_hor_pos[NumX]; @@ -204,23 +199,18 @@ module tb_floo_dma_mesh; end floo_narrow_wide_chimney #( - .RouteAlgo ( RouteAlgo ), - .XYAddrOffsetX ( XYAddrOffsetX ), - .XYAddrOffsetY ( XYAddrOffsetY ), .NarrowMaxTxns ( NarrowMaxTxns ), .WideMaxTxns ( WideMaxTxns ), .NarrowReorderBufferSize ( NarrowReorderBufferSize ), .WideReorderBufferSize ( WideReorderBufferSize ), .CutAx ( CutAx ), - .CutRsp ( CutRsp ), - .id_t ( xy_id_t ) + .CutRsp ( CutRsp ) ) i_hbm_chimney [NumChimneys-1:0] ( .clk_i ( clk ), .rst_ni ( rst_n ), .sram_cfg_i ( '0 ), .test_enable_i ( 1'b0 ), .id_i ( xy_id_hbm ), - .id_map_i ( '0 ), .axi_narrow_in_req_i ( '0 ), .axi_narrow_in_rsp_o ( ), .axi_narrow_out_req_o ( narrow_hbm_req[i] ), @@ -246,26 +236,26 @@ module tb_floo_dma_mesh; for (genvar x = 0; x < NumX; x++) begin : gen_x for (genvar y = 0; y < NumX; y++) begin : gen_y - xy_id_t current_id; - localparam string narrow_dma_name = $sformatf("narrow_dma_%0d_%0d", x, y); - localparam string wide_dma_name = $sformatf("wide_dma_%0d_%0d", x, y); + id_t current_id; + localparam string NarrowDmaName = $sformatf("narrow_dma_%0d_%0d", x, y); + localparam string WideDmaName = $sformatf("wide_dma_%0d_%0d", x, y); floo_req_t [NumDirections-1:0] req_out, req_in; floo_rsp_t [NumDirections-1:0] rsp_out, rsp_in; floo_wide_t [NumDirections-1:0] wide_out, wide_in; - localparam int unsigned index = y * NumX + x+1; - localparam MemBaseAddr = (x+1) << XYAddrOffsetX | (y+1) << XYAddrOffsetY; + localparam int unsigned Index = y * NumX + x+1; + localparam logic [AxiNarrowInAddrWidth-1:0] MemBaseAddr = + (x+1) << XYAddrOffsetX | (y+1) << XYAddrOffsetY; assign current_id = '{x: x+1, y: y+1}; floo_dma_test_node #( .TA ( ApplTime ), .TT ( TestTime ), - .TCK ( CyclTime ), - .DataWidth ( NarrowInDataWidth ), - .AddrWidth ( NarrowInAddrWidth ), - .UserWidth ( NarrowInUserWidth ), - .AxiIdInWidth ( NarrowOutIdWidth ), - .AxiIdOutWidth ( NarrowInIdWidth ), + .DataWidth ( AxiNarrowInDataWidth ), + .AddrWidth ( AxiNarrowInAddrWidth ), + .UserWidth ( AxiNarrowInUserWidth ), + .AxiIdInWidth ( AxiNarrowOutIdWidth ), + .AxiIdOutWidth ( AxiNarrowInIdWidth ), .MemBaseAddr ( MemBaseAddr ), .MemSize ( MemSize ), .NumAxInFlight ( 2*NarrowMaxTxnsPerId ), @@ -273,7 +263,7 @@ module tb_floo_dma_mesh; .axi_in_rsp_t ( axi_narrow_out_rsp_t ), .axi_out_req_t ( axi_narrow_in_req_t ), .axi_out_rsp_t ( axi_narrow_in_rsp_t ), - .JobId ( 100 + index ) + .JobId ( 100 + Index ) ) i_narrow_dma_node ( .clk_i ( clk ), .rst_ni ( rst_n ), @@ -287,12 +277,11 @@ module tb_floo_dma_mesh; floo_dma_test_node #( .TA ( ApplTime ), .TT ( TestTime ), - .TCK ( CyclTime ), - .DataWidth ( WideInDataWidth ), - .AddrWidth ( WideInAddrWidth ), - .UserWidth ( WideInUserWidth ), - .AxiIdInWidth ( WideOutIdWidth ), - .AxiIdOutWidth ( WideInIdWidth ), + .DataWidth ( AxiWideInDataWidth ), + .AddrWidth ( AxiWideInAddrWidth ), + .UserWidth ( AxiWideInUserWidth ), + .AxiIdInWidth ( AxiWideOutIdWidth ), + .AxiIdOutWidth ( AxiWideInIdWidth ), .MemBaseAddr ( MemBaseAddr ), .MemSize ( MemSize ), .NumAxInFlight ( 2*WideMaxTxnsPerId ), @@ -300,7 +289,7 @@ module tb_floo_dma_mesh; .axi_in_rsp_t ( axi_wide_out_rsp_t ), .axi_out_req_t ( axi_wide_in_req_t ), .axi_out_rsp_t ( axi_wide_in_rsp_t ), - .JobId ( index ) + .JobId ( Index ) ) i_wide_dma_node ( .clk_i ( clk ), .rst_ni ( rst_n ), @@ -314,8 +303,8 @@ module tb_floo_dma_mesh; axi_bw_monitor #( .req_t ( axi_narrow_in_req_t ), .rsp_t ( axi_narrow_in_rsp_t ), - .AxiIdWidth ( NarrowInIdWidth ), - .name ( narrow_dma_name ) + .AxiIdWidth ( AxiNarrowInIdWidth ), + .Name ( NarrowDmaName ) ) i_axi_narrow_bw_monitor ( .clk_i ( clk ), .en_i ( rst_n ), @@ -329,8 +318,8 @@ module tb_floo_dma_mesh; axi_bw_monitor #( .req_t ( axi_wide_in_req_t ), .rsp_t ( axi_wide_in_rsp_t ), - .AxiIdWidth ( WideInIdWidth ), - .name ( wide_dma_name ) + .AxiIdWidth ( AxiWideInIdWidth ), + .Name ( WideDmaName ) ) i_axi_wide_bw_monitor ( .clk_i ( clk ), .en_i ( rst_n ), @@ -342,23 +331,18 @@ module tb_floo_dma_mesh; ); floo_narrow_wide_chimney #( - .RouteAlgo ( RouteAlgo ), - .XYAddrOffsetX ( XYAddrOffsetX ), - .XYAddrOffsetY ( XYAddrOffsetY ), .NarrowMaxTxns ( NarrowMaxTxns ), .WideMaxTxns ( WideMaxTxns ), .NarrowReorderBufferSize ( NarrowReorderBufferSize ), .WideReorderBufferSize ( WideReorderBufferSize ), .CutAx ( CutAx ), - .CutRsp ( CutRsp ), - .id_t ( xy_id_t ) + .CutRsp ( CutRsp ) ) i_dma_chimney ( .clk_i ( clk ), .rst_ni ( rst_n ), .sram_cfg_i ( '0 ), .test_enable_i ( 1'b0 ), .id_i ( current_id ), - .id_map_i ( '0 ), .axi_narrow_in_req_i ( narrow_man_req[x][y] ), .axi_narrow_in_rsp_o ( narrow_man_rsp[x][y] ), .axi_narrow_out_req_o ( narrow_sub_req[x][y] ), @@ -381,7 +365,7 @@ module tb_floo_dma_mesh; .OutputFifoDepth ( OutputFifoDepth ), .RouteAlgo ( RouteAlgo ), .XYRouteOpt ( 1'b0 ), - .id_t ( xy_id_t ) + .id_t ( id_t ) ) i_router ( .clk_i ( clk ), .rst_ni ( rst_n ), diff --git a/test/tb_floo_dma_nw_chimney.sv b/hw/tb/tb_floo_dma_nw_chimney.sv similarity index 85% rename from test/tb_floo_dma_nw_chimney.sv rename to hw/tb/tb_floo_dma_nw_chimney.sv index e04520ae..df36dd6e 100644 --- a/test/tb_floo_dma_nw_chimney.sv +++ b/hw/tb/tb_floo_dma_nw_chimney.sv @@ -17,7 +17,7 @@ module tb_floo_dma_nw_chimney; localparam time ApplTime = 2ns; localparam time TestTime = 8ns; - localparam NumTargets = 2; + localparam int unsigned NumTargets = 2; localparam int unsigned ReorderBufferSize = 128; localparam int unsigned MaxTxns = 32; @@ -62,8 +62,8 @@ module tb_floo_dma_nw_chimney; ); typedef struct packed { - logic [NarrowInAddrWidth-1:0] start_addr; - logic [NarrowInAddrWidth-1:0] end_addr; + logic [AxiNarrowInAddrWidth-1:0] start_addr; + logic [AxiNarrowInAddrWidth-1:0] end_addr; } node_addr_region_t; localparam int unsigned MemBaseAddr = 32'h0000_0000; @@ -73,11 +73,11 @@ module tb_floo_dma_nw_chimney; .TA ( ApplTime ), .TT ( TestTime ), .TCK ( CyclTime ), - .DataWidth ( NarrowInDataWidth ), - .AddrWidth ( NarrowInAddrWidth ), - .UserWidth ( NarrowInUserWidth ), - .AxiIdInWidth ( NarrowOutIdWidth ), - .AxiIdOutWidth ( NarrowInIdWidth ), + .DataWidth ( AxiNarrowInDataWidth ), + .AddrWidth ( AxiNarrowInAddrWidth ), + .UserWidth ( AxiNarrowInUserWidth ), + .AxiIdInWidth ( AxiNarrowOutIdWidth ), + .AxiIdOutWidth ( AxiNarrowInIdWidth ), .MemBaseAddr ( MemBaseAddr ), .MemSize ( MemSize ), .axi_in_req_t ( axi_narrow_out_req_t ), @@ -99,11 +99,11 @@ module tb_floo_dma_nw_chimney; .TA ( ApplTime ), .TT ( TestTime ), .TCK ( CyclTime ), - .DataWidth ( WideInDataWidth ), - .AddrWidth ( WideInAddrWidth ), - .UserWidth ( WideInUserWidth ), - .AxiIdInWidth ( WideOutIdWidth ), - .AxiIdOutWidth ( WideInIdWidth ), + .DataWidth ( AxiWideInDataWidth ), + .AddrWidth ( AxiWideInAddrWidth ), + .UserWidth ( AxiWideInUserWidth ), + .AxiIdInWidth ( AxiWideOutIdWidth ), + .AxiIdOutWidth ( AxiWideInIdWidth ), .MemBaseAddr ( MemBaseAddr ), .MemSize ( MemSize ), .axi_in_req_t ( axi_wide_out_req_t ), @@ -121,7 +121,7 @@ module tb_floo_dma_nw_chimney; .end_of_sim_o ( end_of_sim[1] ) ); - axi_channel_compare #( + axi_chan_compare #( .aw_chan_t ( axi_narrow_in_aw_chan_t ), .w_chan_t ( axi_narrow_in_w_chan_t ), .b_chan_t ( axi_narrow_in_b_chan_t ), @@ -130,14 +130,15 @@ module tb_floo_dma_nw_chimney; .req_t ( axi_narrow_in_req_t ), .resp_t ( axi_narrow_in_rsp_t ) ) i_narrow_channel_compare_0 ( - .clk_i ( clk ), + .clk_a_i ( clk ), + .clk_b_i ( clk ), .axi_a_req ( narrow_man_req[0] ), .axi_a_res ( narrow_man_rsp[0] ), .axi_b_req ( narrow_sub_req_id_mapped[1] ), .axi_b_res ( narrow_sub_rsp_id_mapped[1] ) ); - axi_channel_compare #( + axi_chan_compare #( .aw_chan_t ( axi_wide_in_aw_chan_t ), .w_chan_t ( axi_wide_in_w_chan_t ), .b_chan_t ( axi_wide_in_b_chan_t ), @@ -146,7 +147,8 @@ module tb_floo_dma_nw_chimney; .req_t ( axi_wide_in_req_t ), .resp_t ( axi_wide_in_rsp_t ) ) i_wide_channel_compare_0 ( - .clk_i ( clk ), + .clk_a_i ( clk ), + .clk_b_i ( clk ), .axi_a_req ( wide_man_req[0] ), .axi_a_res ( wide_man_rsp[0] ), .axi_b_req ( wide_sub_req_id_mapped[1] ), @@ -260,7 +262,7 @@ module tb_floo_dma_nw_chimney; .floo_wide_i ( chimney_wide_cut[0] ) ); - axi_channel_compare #( + axi_chan_compare #( .aw_chan_t ( axi_narrow_in_aw_chan_t ), .w_chan_t ( axi_narrow_in_w_chan_t ), .b_chan_t ( axi_narrow_in_b_chan_t ), @@ -269,14 +271,15 @@ module tb_floo_dma_nw_chimney; .req_t ( axi_narrow_in_req_t ), .resp_t ( axi_narrow_in_rsp_t ) ) i_narrow_channel_compare_1 ( - .clk_i ( clk ), + .clk_a_i ( clk ), + .clk_b_i ( clk ), .axi_a_req ( narrow_man_req[1] ), .axi_a_res ( narrow_man_rsp[1] ), .axi_b_req ( narrow_sub_req_id_mapped[0] ), .axi_b_res ( narrow_sub_rsp_id_mapped[0] ) ); - axi_channel_compare #( + axi_chan_compare #( .aw_chan_t ( axi_wide_in_aw_chan_t ), .w_chan_t ( axi_wide_in_w_chan_t ), .b_chan_t ( axi_wide_in_b_chan_t ), @@ -285,7 +288,8 @@ module tb_floo_dma_nw_chimney; .req_t ( axi_wide_in_req_t ), .resp_t ( axi_wide_in_rsp_t ) ) i_wide_channel_compare_1 ( - .clk_i ( clk ), + .clk_a_i ( clk ), + .clk_b_i ( clk ), .axi_a_req ( wide_man_req[1] ), .axi_a_res ( wide_man_rsp[1] ), .axi_b_req ( wide_sub_req_id_mapped[0] ), @@ -296,11 +300,11 @@ module tb_floo_dma_nw_chimney; .TA ( ApplTime ), .TT ( TestTime ), .TCK ( CyclTime ), - .DataWidth ( NarrowInDataWidth ), - .AddrWidth ( NarrowInAddrWidth ), - .UserWidth ( NarrowInUserWidth ), - .AxiIdInWidth ( NarrowOutIdWidth ), - .AxiIdOutWidth ( NarrowInIdWidth ), + .DataWidth ( AxiNarrowInDataWidth ), + .AddrWidth ( AxiNarrowInAddrWidth ), + .UserWidth ( AxiNarrowInUserWidth ), + .AxiIdInWidth ( AxiNarrowOutIdWidth ), + .AxiIdOutWidth ( AxiNarrowInIdWidth ), .MemBaseAddr ( MemBaseAddr ), .MemSize ( MemSize ), .axi_in_req_t ( axi_narrow_out_req_t ), @@ -322,11 +326,11 @@ module tb_floo_dma_nw_chimney; .TA ( ApplTime ), .TT ( TestTime ), .TCK ( CyclTime ), - .DataWidth ( WideInDataWidth ), - .AddrWidth ( WideInAddrWidth ), - .UserWidth ( WideInUserWidth ), - .AxiIdInWidth ( WideOutIdWidth ), - .AxiIdOutWidth ( WideInIdWidth ), + .DataWidth ( AxiWideInDataWidth ), + .AddrWidth ( AxiWideInAddrWidth ), + .UserWidth ( AxiWideInUserWidth ), + .AxiIdInWidth ( AxiWideOutIdWidth ), + .AxiIdOutWidth ( AxiWideInIdWidth ), .MemBaseAddr ( MemBaseAddr ), .MemSize ( MemSize ), .axi_in_req_t ( axi_wide_out_req_t ), @@ -347,8 +351,8 @@ module tb_floo_dma_nw_chimney; axi_bw_monitor #( .req_t ( axi_narrow_in_req_t ), .rsp_t ( axi_narrow_in_rsp_t ), - .AxiIdWidth ( NarrowInIdWidth ), - .name ( "narrow 0" ) + .AxiIdWidth ( AxiNarrowInIdWidth ), + .Name ( "narrow 0" ) ) i_axi_narrow_bw_monitor_0 ( .clk_i ( clk ), .en_i ( rst_n ), @@ -360,8 +364,8 @@ module tb_floo_dma_nw_chimney; axi_bw_monitor #( .req_t ( axi_narrow_in_req_t ), .rsp_t ( axi_narrow_in_rsp_t ), - .AxiIdWidth ( NarrowInIdWidth ), - .name ( "narrow 1" ) + .AxiIdWidth ( AxiNarrowInIdWidth ), + .Name ( "narrow 1" ) ) i_axi_narrow_bw_monitor_1 ( .clk_i ( clk ), .en_i ( rst_n ), @@ -373,28 +377,32 @@ module tb_floo_dma_nw_chimney; axi_bw_monitor #( .req_t ( axi_wide_in_req_t ), .rsp_t ( axi_wide_in_rsp_t ), - .AxiIdWidth ( WideInIdWidth ), - .name ( "wide 0" ) + .AxiIdWidth ( AxiWideInIdWidth ), + .Name ( "wide 0" ) ) i_axi_wide_bw_monitor_0 ( - .clk_i ( clk ), - .en_i ( rst_n ), - .end_of_sim_i ( &end_of_sim ), - .req_i ( wide_man_req[0] ), - .rsp_i ( wide_man_rsp[0] ) - ); + .clk_i ( clk ), + .en_i ( rst_n ), + .end_of_sim_i ( &end_of_sim ), + .req_i ( wide_man_req[0] ), + .rsp_i ( wide_man_rsp[0] ), + .ar_in_flight_o ( ), + .aw_in_flight_o ( ) + ); axi_bw_monitor #( .req_t ( axi_wide_in_req_t ), .rsp_t ( axi_wide_in_rsp_t ), - .AxiIdWidth ( WideInIdWidth ), - .name ( "wide 1" ) + .AxiIdWidth ( AxiWideInIdWidth ), + .Name ( "wide 1" ) ) i_axi_wide_bw_monitor_1 ( - .clk_i ( clk ), - .en_i ( rst_n ), - .end_of_sim_i ( &end_of_sim ), - .req_i ( wide_man_req[1] ), - .rsp_i ( wide_man_rsp[1] ) - ); + .clk_i ( clk ), + .en_i ( rst_n ), + .end_of_sim_i ( &end_of_sim ), + .req_i ( wide_man_req[1] ), + .rsp_i ( wide_man_rsp[1] ), + .ar_in_flight_o ( ), + .aw_in_flight_o ( ) + ); initial begin wait(&end_of_sim); diff --git a/test/tb_floo_narrow_wide_chimney.sv b/hw/tb/tb_floo_narrow_wide_chimney.sv similarity index 84% rename from test/tb_floo_narrow_wide_chimney.sv rename to hw/tb/tb_floo_narrow_wide_chimney.sv index 176970d2..24dd1aab 100644 --- a/test/tb_floo_narrow_wide_chimney.sv +++ b/hw/tb/tb_floo_narrow_wide_chimney.sv @@ -17,14 +17,14 @@ module tb_floo_narrow_wide_chimney; localparam time ApplTime = 2ns; localparam time TestTime = 8ns; - localparam NarrowNumReads = 1000; - localparam NarrowNumWrites = 1000; - localparam WideNumReads = 1000; - localparam WideNumWrites = 1000; + localparam int unsigned NarrowNumReads = 1000; + localparam int unsigned NarrowNumWrites = 1000; + localparam int unsigned WideNumReads = 1000; + localparam int unsigned WideNumWrites = 1000; localparam bit AtopSupport = 1'b1; - localparam NumTargets = 2; + localparam int unsigned NumTargets = 2; localparam int unsigned ReorderBufferSize = 128; localparam int unsigned MaxTxns = 32; @@ -69,8 +69,8 @@ module tb_floo_narrow_wide_chimney; ); typedef struct packed { - logic [NarrowInAddrWidth-1:0] start_addr; - logic [NarrowInAddrWidth-1:0] end_addr; + logic [AxiNarrowInAddrWidth-1:0] start_addr; + logic [AxiNarrowInAddrWidth-1:0] end_addr; } node_addr_region_t; localparam int unsigned NumAddrRegions = 1; @@ -80,8 +80,8 @@ module tb_floo_narrow_wide_chimney; typedef struct packed { int unsigned idx; - logic [NarrowInAddrWidth-1:0] start_addr; - logic [NarrowInAddrWidth-1:0] end_addr; + logic [AxiNarrowInAddrWidth-1:0] start_addr; + logic [AxiNarrowInAddrWidth-1:0] end_addr; } node_addr_region_id_t; node_addr_region_id_t [NumTargets-1:0] node_addr_regions; @@ -91,11 +91,11 @@ module tb_floo_narrow_wide_chimney; }; floo_axi_test_node #( - .AxiAddrWidth ( NarrowInAddrWidth ), - .AxiDataWidth ( NarrowInDataWidth ), - .AxiIdInWidth ( NarrowOutIdWidth ), - .AxiIdOutWidth ( NarrowInIdWidth ), - .AxiUserWidth ( NarrowInUserWidth ), + .AxiAddrWidth ( AxiNarrowInAddrWidth ), + .AxiDataWidth ( AxiNarrowInDataWidth ), + .AxiIdInWidth ( AxiNarrowOutIdWidth ), + .AxiIdOutWidth ( AxiNarrowInIdWidth ), + .AxiUserWidth ( AxiNarrowInUserWidth ), .mst_req_t ( axi_narrow_in_req_t ), .mst_rsp_t ( axi_narrow_in_rsp_t ), .slv_req_t ( axi_narrow_out_req_t ), @@ -120,11 +120,11 @@ module tb_floo_narrow_wide_chimney; ); floo_axi_test_node #( - .AxiAddrWidth ( WideInAddrWidth ), - .AxiDataWidth ( WideInDataWidth ), - .AxiIdInWidth ( WideOutIdWidth ), - .AxiIdOutWidth ( WideInIdWidth ), - .AxiUserWidth ( WideInUserWidth ), + .AxiAddrWidth ( AxiWideInAddrWidth ), + .AxiDataWidth ( AxiWideInDataWidth ), + .AxiIdInWidth ( AxiWideOutIdWidth ), + .AxiIdOutWidth ( AxiWideInIdWidth ), + .AxiUserWidth ( AxiWideInUserWidth ), .mst_req_t ( axi_wide_in_req_t ), .mst_rsp_t ( axi_wide_in_rsp_t ), .slv_req_t ( axi_wide_out_req_t ), @@ -149,8 +149,8 @@ module tb_floo_narrow_wide_chimney; ); axi_reorder_remap_compare #( - .AxiInIdWidth ( NarrowInIdWidth ), - .AxiOutIdWidth ( NarrowOutIdWidth ), + .AxiInIdWidth ( AxiNarrowInIdWidth ), + .AxiOutIdWidth ( AxiNarrowOutIdWidth ), .aw_chan_t ( axi_narrow_in_aw_chan_t ), .w_chan_t ( axi_narrow_in_w_chan_t ), .b_chan_t ( axi_narrow_in_b_chan_t ), @@ -167,7 +167,8 @@ module tb_floo_narrow_wide_chimney; .end_of_sim_o ( end_of_sim[2] ) ); - axi_channel_compare #( + axi_chan_compare #( + .IgnoreId ( 1'b1 ), .aw_chan_t ( axi_wide_in_aw_chan_t ), .w_chan_t ( axi_wide_in_w_chan_t ), .b_chan_t ( axi_wide_in_b_chan_t ), @@ -176,7 +177,8 @@ module tb_floo_narrow_wide_chimney; .req_t ( axi_wide_in_req_t ), .resp_t ( axi_wide_in_rsp_t ) ) i_wide_channel_compare_0 ( - .clk_i ( clk ), + .clk_a_i ( clk ), + .clk_b_i ( clk ), .axi_a_req ( wide_man_req[0] ), .axi_a_res ( wide_man_rsp[0] ), .axi_b_req ( wide_sub_req_id_mapped[1] ), @@ -186,10 +188,6 @@ module tb_floo_narrow_wide_chimney; floo_narrow_wide_chimney #( .AtopSupport ( AtopSupport ), .MaxAtomicTxns ( 1 ), - .RouteAlgo ( floo_pkg::IdTable ), - .NumIDs ( NumTargets ), - .NumRules ( NumTargets ), - .id_rule_t ( node_addr_region_id_t ), .NarrowMaxTxns ( MaxTxns ), .NarrowMaxTxnsPerId ( MaxTxnsPerId ), .NarrowReorderBufferSize ( ReorderBufferSize ), @@ -197,7 +195,7 @@ module tb_floo_narrow_wide_chimney; .WideMaxTxnsPerId ( MaxTxnsPerId ), .WideReorderBufferSize ( ReorderBufferSize ), .CutAx ( 1'b0 ), - .CutRsp ( 1'b0 ) + .CutRsp ( 1'b1 ) ) i_floo_narrow_wide_chimney_0 ( .clk_i ( clk ), .rst_ni ( rst_n ), @@ -212,7 +210,6 @@ module tb_floo_narrow_wide_chimney; .axi_wide_out_req_o ( wide_sub_req[0] ), .axi_wide_out_rsp_i ( wide_sub_rsp[0] ), .id_i ( '0 ), - .id_map_i ( node_addr_regions ), .floo_req_o ( chimney_req[0] ), .floo_rsp_o ( chimney_rsp[0] ), .floo_wide_o ( chimney_wide[0] ), @@ -224,10 +221,6 @@ module tb_floo_narrow_wide_chimney; floo_narrow_wide_chimney #( .AtopSupport ( AtopSupport ), .MaxAtomicTxns ( 1 ), - .RouteAlgo ( floo_pkg::IdTable ), - .NumIDs ( NumTargets ), - .NumRules ( NumTargets ), - .id_rule_t ( node_addr_region_id_t ), .NarrowMaxTxns ( MaxTxns ), .NarrowMaxTxnsPerId ( MaxTxnsPerId ), .NarrowReorderBufferSize ( ReorderBufferSize ), @@ -235,7 +228,7 @@ module tb_floo_narrow_wide_chimney; .WideMaxTxnsPerId ( MaxTxnsPerId ), .WideReorderBufferSize ( ReorderBufferSize ), .CutAx ( 1'b0 ), - .CutRsp ( 1'b0 ) + .CutRsp ( 1'b1 ) ) i_floo_narrow_wide_chimney_1 ( .clk_i ( clk ), .rst_ni ( rst_n ), @@ -250,7 +243,6 @@ module tb_floo_narrow_wide_chimney; .axi_wide_out_req_o ( wide_sub_req[1] ), .axi_wide_out_rsp_i ( wide_sub_rsp[1] ), .id_i ( '0 ), - .id_map_i ( node_addr_regions ), .floo_req_o ( chimney_req[1] ), .floo_rsp_o ( chimney_rsp[1] ), .floo_wide_o ( chimney_wide[1] ), @@ -260,8 +252,8 @@ module tb_floo_narrow_wide_chimney; ); axi_reorder_remap_compare #( - .AxiInIdWidth ( NarrowInIdWidth ), - .AxiOutIdWidth ( NarrowOutIdWidth ), + .AxiInIdWidth ( AxiNarrowInIdWidth ), + .AxiOutIdWidth ( AxiNarrowOutIdWidth ), .aw_chan_t ( axi_narrow_in_aw_chan_t ), .w_chan_t ( axi_narrow_in_w_chan_t ), .b_chan_t ( axi_narrow_in_b_chan_t ), @@ -278,7 +270,8 @@ module tb_floo_narrow_wide_chimney; .end_of_sim_o ( end_of_sim[3] ) ); - axi_channel_compare #( + axi_chan_compare #( + .IgnoreId ( 1'b1 ), .aw_chan_t ( axi_wide_in_aw_chan_t ), .w_chan_t ( axi_wide_in_w_chan_t ), .b_chan_t ( axi_wide_in_b_chan_t ), @@ -287,7 +280,8 @@ module tb_floo_narrow_wide_chimney; .req_t ( axi_wide_in_req_t ), .resp_t ( axi_wide_in_rsp_t ) ) i_wide_channel_compare_1 ( - .clk_i ( clk ), + .clk_a_i ( clk ), + .clk_b_i ( clk ), .axi_a_req ( wide_man_req[1] ), .axi_a_res ( wide_man_rsp[1] ), .axi_b_req ( wide_sub_req_id_mapped[0] ), @@ -295,11 +289,11 @@ module tb_floo_narrow_wide_chimney; ); floo_axi_test_node #( - .AxiAddrWidth ( NarrowInAddrWidth ), - .AxiDataWidth ( NarrowInDataWidth ), - .AxiIdOutWidth ( NarrowInIdWidth ), - .AxiIdInWidth ( NarrowOutIdWidth ), - .AxiUserWidth ( NarrowInUserWidth ), + .AxiAddrWidth ( AxiNarrowInAddrWidth ), + .AxiDataWidth ( AxiNarrowInDataWidth ), + .AxiIdOutWidth ( AxiNarrowInIdWidth ), + .AxiIdInWidth ( AxiNarrowOutIdWidth ), + .AxiUserWidth ( AxiNarrowInUserWidth ), .mst_req_t ( axi_narrow_in_req_t ), .mst_rsp_t ( axi_narrow_in_rsp_t ), .slv_req_t ( axi_narrow_out_req_t ), @@ -324,11 +318,11 @@ module tb_floo_narrow_wide_chimney; ); floo_axi_test_node #( - .AxiAddrWidth ( WideInAddrWidth ), - .AxiDataWidth ( WideInDataWidth ), - .AxiIdInWidth ( WideOutIdWidth ), - .AxiIdOutWidth ( WideInIdWidth ), - .AxiUserWidth ( WideInUserWidth ), + .AxiAddrWidth ( AxiWideInAddrWidth ), + .AxiDataWidth ( AxiWideInDataWidth ), + .AxiIdInWidth ( AxiWideOutIdWidth ), + .AxiIdOutWidth ( AxiWideInIdWidth ), + .AxiUserWidth ( AxiWideInUserWidth ), .mst_req_t ( axi_wide_in_req_t ), .mst_rsp_t ( axi_wide_in_rsp_t ), .slv_req_t ( axi_wide_out_req_t ), diff --git a/test/tb_floo_rob.sv b/hw/tb/tb_floo_rob.sv similarity index 89% rename from test/tb_floo_rob.sv rename to hw/tb/tb_floo_rob.sv index 53f25fbe..6fb96497 100644 --- a/test/tb_floo_rob.sv +++ b/hw/tb/tb_floo_rob.sv @@ -18,11 +18,8 @@ module tb_floo_rob; localparam time ApplTime = 2ns; localparam time TestTime = 8ns; - localparam NumReads = 1000; - localparam NumWrites = 1000; - - localparam int unsigned NumX = 4; - localparam int unsigned NumY = 4; + localparam int unsigned NumReads = 1000; + localparam int unsigned NumWrites = 1000; localparam int unsigned ReorderBufferSize = 64; localparam int unsigned MaxTxns = 32; @@ -84,10 +81,8 @@ module tb_floo_rob; // Local Master // //////////////////// - `FLOO_NOC_TYPEDEF_XY_ID_T(xy_id_t, NumX, NumY) - - xy_id_t [NumDirections-1:0] xy_id; - assign xy_id[Eject] = '{x: 2'd1, y: 2'd1}; + id_t [NumDirections-1:0] xy_id; + assign xy_id[Eject] = '{x: 3'd1, y: 3'd1}; typedef struct packed { int unsigned idx; @@ -110,9 +105,9 @@ module tb_floo_rob; .AxiIdOutWidth ( AxiInIdWidth ), .AxiUserWidth ( AxiInUserWidth ), .mst_req_t ( axi_in_req_t ), - .mst_rsp_t ( axi_in_rsp_t ), + .mst_rsp_t ( axi_in_rsp_t ), .slv_req_t ( axi_out_req_t ), - .slv_rsp_t ( axi_out_rsp_t ), + .slv_rsp_t ( axi_out_rsp_t ), .ApplTime ( ApplTime ), .TestTime ( TestTime ), .AxiMaxBurstLen ( 4 ), @@ -132,13 +127,9 @@ module tb_floo_rob; ); floo_axi_chimney #( - .XYAddrOffsetX ( 32'd16 ), - .XYAddrOffsetY ( 32'd20 ), - .RouteAlgo ( XYRouting ), .MaxTxns ( MaxTxns ), .MaxTxnsPerId ( MaxTxnsPerId ), - .ReorderBufferSize ( ReorderBufferSize ), - .id_t ( xy_id_t ) + .ReorderBufferSize ( ReorderBufferSize ) ) i_floo_axi_chimney ( .clk_i ( clk ), .rst_ni ( rst_n ), @@ -162,7 +153,7 @@ module tb_floo_rob; .flit_t ( floo_req_generic_flit_t ), .ChannelFifoDepth ( 4 ), .RouteAlgo ( XYRouting ), - .id_t ( xy_id_t ) + .id_t ( id_t ) ) i_floo_req_router ( .clk_i ( clk ), .rst_ni ( rst_n ), @@ -184,7 +175,7 @@ module tb_floo_rob; .flit_t ( floo_rsp_generic_flit_t ), .ChannelFifoDepth ( 4 ), .RouteAlgo ( XYRouting ), - .id_t ( xy_id_t ) + .id_t ( id_t ) ) i_floo_rsp_router ( .clk_i ( clk ), .rst_ni ( rst_n ), @@ -199,28 +190,25 @@ module tb_floo_rob; .data_o ( chimney_rsp_in_chan ) ); - localparam slave_type_e SlaveType[NumDirections-1] = '{FastSlave, FastSlave, SlowSlave, MixedSlave}; + localparam slave_type_e SlaveType[NumDirections-1] = '{ + FastSlave, FastSlave, SlowSlave, MixedSlave}; for (genvar i = North; i <= West; i++) begin : gen_slaves if (i == North) begin : gen_north - assign xy_id[i] = '{x: 2'd1, y: 2'd2}; + assign xy_id[i] = '{x: 3'd1, y: 3'd2}; end else if (i == South) begin : gen_south - assign xy_id[i] = '{x: 2'd1, y: 2'd0}; + assign xy_id[i] = '{x: 3'd1, y: 3'd0}; end else if (i == East) begin : gen_east - assign xy_id[i] = '{x: 2'd2, y: 2'd1}; + assign xy_id[i] = '{x: 3'd2, y: 3'd1}; end else if (i == West) begin : gen_west - assign xy_id[i] = '{x: 2'd0, y: 2'd1}; + assign xy_id[i] = '{x: 3'd0, y: 3'd1}; end floo_axi_chimney #( - .XYAddrOffsetX ( 32'd16 ), - .XYAddrOffsetY ( 32'd20 ), - .RouteAlgo ( XYRouting ), .MaxTxns ( MaxTxns ), .MaxTxnsPerId ( MaxTxnsPerId ), - .ReorderBufferSize ( ReorderBufferSize ), - .id_t ( xy_id_t ) + .ReorderBufferSize ( ReorderBufferSize ) ) i_floo_axi_chimney ( .clk_i ( clk ), .rst_ni ( rst_n ), diff --git a/test/tb_floo_router.sv b/hw/tb/tb_floo_router.sv similarity index 84% rename from test/tb_floo_router.sv rename to hw/tb/tb_floo_router.sv index 29688ab7..8c8f219d 100644 --- a/test/tb_floo_router.sv +++ b/hw/tb/tb_floo_router.sv @@ -22,15 +22,15 @@ module tb_floo_router; localparam time ApplTime = 2ns; localparam time TestTime = 8ns; - task cycle_start(); + task automatic cycle_start(); #ApplTime; endtask : cycle_start - task cycle_end(); + task automatic cycle_end(); #TestTime; endtask : cycle_end - task started_cycle_end(); + task automatic started_cycle_end(); #(TestTime-ApplTime); endtask : started_cycle_end @@ -65,7 +65,7 @@ module tb_floo_router; this.id = in_id; endfunction - constraint data_mod_id { + constraint data_mod_id_c { data % NumPorts == id; } endclass @@ -79,12 +79,12 @@ module tb_floo_router; this.source = in_source; endfunction - constraint id_in_range { + constraint id_in_range_c { id < NumPorts; id != source; } - constraint len_is_reasonable { + constraint len_is_reasonable_c { len <= 32; len > 0; } @@ -92,10 +92,10 @@ module tb_floo_router; endclass // Source Port Virtual Channel - floo_req_generic_flit_t stimuli_queue[NumPorts-1:0][NumVirtChannels-1:0] [$]; + floo_req_generic_flit_t stimuli_queue[NumPorts][NumVirtChannels][$]; // Destination Virtual Channel Source Port - floo_req_generic_flit_t golden_queue [NumPorts-1:0][NumVirtChannels-1:0][NumPorts-1:0][$]; + floo_req_generic_flit_t golden_queue [NumPorts][NumVirtChannels][NumPorts][$]; function automatic void generate_stimuli(); for (int port = 0; port < NumPorts; port++) begin @@ -106,14 +106,14 @@ module tb_floo_router; automatic stimuli_t stimuli = new(port); // Active Constraints - stimuli.id_in_range.constraint_mode(1); - stimuli.len_is_reasonable.constraint_mode(1); + stimuli.id_in_range_c.constraint_mode(1); + stimuli.len_is_reasonable_c.constraint_mode(1); // Randomize if (stimuli.randomize()) begin for (int j = 0; j < stimuli.len; j++) begin automatic rand_data_t rand_data = new(port); - rand_data.data_mod_id.constraint_mode(1); + rand_data.data_mod_id_c.constraint_mode(1); if (rand_data.randomize()) begin automatic floo_req_generic_flit_t next_flit = '0; next_flit.rsvd = rand_data.data; @@ -177,7 +177,7 @@ module tb_floo_router; floo_req_generic_flit_t [NumPorts-1:0][NumVirtChannels-1:0] delayed_data_in; for (genvar port = 0; port < NumPorts; port++) begin : gen_in_delay - for (genvar virt_channel = 0; virt_channel < NumVirtChannels; virt_channel++) begin : gen_in_vc_delay + for (genvar vc = 0; vc < NumVirtChannels; vc++) begin : gen_in_vc_delay stream_delay #( .StallRandom ( 1'b1 ), .FixedDelay ( 1 ), @@ -187,13 +187,13 @@ module tb_floo_router; .clk_i (clk), .rst_ni (rst_n), - .payload_i( pre_data_in [port][virt_channel] ), - .ready_o ( pre_ready_in[port][virt_channel] ), - .valid_i ( pre_valid_in[port][virt_channel] ), + .payload_i( pre_data_in [port][vc] ), + .ready_o ( pre_ready_in[port][vc] ), + .valid_i ( pre_valid_in[port][vc] ), - .payload_o( delayed_data_in [port][virt_channel] ), - .ready_i ( delayed_ready_in[port][virt_channel] ), - .valid_o ( delayed_valid_in[port][virt_channel] ) + .payload_o( delayed_data_in [port][vc] ), + .ready_i ( delayed_ready_in[port][vc] ), + .valid_o ( delayed_valid_in[port][vc] ) ); end end @@ -253,7 +253,7 @@ module tb_floo_router; floo_req_generic_flit_t [NumPorts-1:0][NumVirtChannels-1:0] delayed_data_out; for (genvar port = 0; port < NumPorts; port++) begin : gen_out_delay - for (genvar virt_channel = 0; virt_channel < NumVirtChannels; virt_channel++) begin : gen_out_vc_delay + for (genvar vc = 0; vc < NumVirtChannels; vc++) begin : gen_out_vc_delay fall_through_register #( .T ( floo_req_generic_flit_t ) @@ -263,13 +263,13 @@ module tb_floo_router; .clr_i (1'b0), .testmode_i(1'b0), - .valid_i ( valid_out [port][virt_channel] ), - .ready_o ( ready_out [port][virt_channel] ), + .valid_i ( valid_out [port][vc] ), + .ready_o ( ready_out [port][vc] ), .data_i ( data_out [port] ), - .valid_o ( fall_valid_out[port][virt_channel] ), - .ready_i ( fall_ready_out[port][virt_channel] ), - .data_o ( fall_data_out [port][virt_channel] ) + .valid_o ( fall_valid_out[port][vc] ), + .ready_i ( fall_ready_out[port][vc] ), + .data_o ( fall_data_out [port][vc] ) ); stream_delay #( @@ -281,13 +281,13 @@ module tb_floo_router; .clk_i (clk), .rst_ni (rst_n), - .payload_i( fall_data_out [port][virt_channel] ), - .ready_o ( fall_ready_out[port][virt_channel] ), - .valid_i ( fall_valid_out[port][virt_channel] ), + .payload_i( fall_data_out [port][vc] ), + .ready_o ( fall_ready_out[port][vc] ), + .valid_i ( fall_valid_out[port][vc] ), - .payload_o( delayed_data_out [port][virt_channel] ), - .ready_i ( delayed_ready_out[port][virt_channel] ), - .valid_o ( delayed_valid_out[port][virt_channel] ) + .payload_o( delayed_data_out [port][vc] ), + .ready_i ( delayed_ready_out[port][vc] ), + .valid_o ( delayed_valid_out[port][vc] ) ); end end @@ -297,7 +297,7 @@ module tb_floo_router; ***********************/ // Destination Virtual Channel - floo_req_generic_flit_t result_queue[NumPorts-1:0][NumVirtChannels-1:0][$]; + floo_req_generic_flit_t result_queue[NumPorts][NumVirtChannels][$]; task automatic collect_result(int unsigned port, int unsigned virt_channel); @@ -344,7 +344,8 @@ module tb_floo_router; end if (result.rsvd != golden.rsvd) begin - $error("ERROR! Mismatch for port %d channel %d (from %d, target port %d).\n This was the data: %x, %x", port, virt_channel, result.hdr.src_id, result.hdr.dst_id, result.rsvd, golden.rsvd); + $error("ERROR! Mismatch for port %d channel %d (from %d, target port %d)", + port, virt_channel, result.hdr.src_id, result.hdr.dst_id); end all_golden_size = 0; diff --git a/test/tb_floo_axi_chimney.wave.tcl b/hw/tb/wave/tb_floo_axi_chimney.wave.tcl similarity index 100% rename from test/tb_floo_axi_chimney.wave.tcl rename to hw/tb/wave/tb_floo_axi_chimney.wave.tcl diff --git a/test/tb_floo_dma_mesh.wave.tcl b/hw/tb/wave/tb_floo_dma_mesh.wave.tcl similarity index 100% rename from test/tb_floo_dma_mesh.wave.tcl rename to hw/tb/wave/tb_floo_dma_mesh.wave.tcl diff --git a/test/tb_floo_dma_nw_chimney.wave.tcl b/hw/tb/wave/tb_floo_dma_nw_chimney.wave.tcl similarity index 100% rename from test/tb_floo_dma_nw_chimney.wave.tcl rename to hw/tb/wave/tb_floo_dma_nw_chimney.wave.tcl diff --git a/test/tb_floo_narrow_wide_chimney.wave.tcl b/hw/tb/wave/tb_floo_narrow_wide_chimney.wave.tcl similarity index 96% rename from test/tb_floo_narrow_wide_chimney.wave.tcl rename to hw/tb/wave/tb_floo_narrow_wide_chimney.wave.tcl index ab71a439..c4686b7e 100644 --- a/test/tb_floo_narrow_wide_chimney.wave.tcl +++ b/hw/tb/wave/tb_floo_narrow_wide_chimney.wave.tcl @@ -60,13 +60,13 @@ for {set i 0} {$i < 2} {incr i} { add wave -noupdate -expand -group $group_name -group NarrowB_RoB tb_floo_narrow_wide_chimney/i_floo_narrow_wide_chimney_${i}/i_narrow_b_rob/* add wave -noupdate -expand -group $group_name -group WideB_RoB tb_floo_narrow_wide_chimney/i_floo_narrow_wide_chimney_${i}/i_wide_b_rob/* - add wave -noupdate -expand -group $group_name -group NarrowMetaBuffer tb_floo_narrow_wide_chimney/i_floo_narrow_wide_chimney_${i}/i_narrow_meta_buffer/* + add wave -noupdate -expand -group $group_name -group NarrowMetaBuffer tb_floo_narrow_wide_chimney/i_floo_narrow_wide_chimney_${i}/gen_narrow_mgr_port/i_narrow_meta_buffer/* try { - add wave -noupdate -expand -group $group_name -group NarrowMetaBuffer tb_floo_narrow_wide_chimney/i_floo_narrow_wide_chimney_${i}/i_narrow_meta_buffer/gen_atop_support/* + add wave -noupdate -expand -group $group_name -group NarrowMetaBuffer tb_floo_narrow_wide_chimney/i_floo_narrow_wide_chimney_${i}/gen_narrow_mgr_port/i_narrow_meta_buffer/gen_atop_support/* } - add wave -noupdate -expand -group $group_name -group WideMetaBuffer tb_floo_narrow_wide_chimney/i_floo_narrow_wide_chimney_${i}/i_wide_meta_buffer/* + add wave -noupdate -expand -group $group_name -group WideMetaBuffer tb_floo_narrow_wide_chimney/i_floo_narrow_wide_chimney_${i}/gen_wide_mgr_port/i_wide_meta_buffer/* try { - add wave -noupdate -expand -group $group_name -group WideMetaBuffer tb_floo_narrow_wide_chimney/i_floo_narrow_wide_chimney_${i}/i_wide_meta_buffer/gen_atop_support/* + add wave -noupdate -expand -group $group_name -group WideMetaBuffer tb_floo_narrow_wide_chimney/i_floo_narrow_wide_chimney_${i}/gen_wide_mgr_port/i_wide_meta_buffer/gen_atop_support/* } } diff --git a/test/tb_floo_rob.wave.tcl b/hw/tb/wave/tb_floo_rob.wave.tcl similarity index 100% rename from test/tb_floo_rob.wave.tcl rename to hw/tb/wave/tb_floo_rob.wave.tcl diff --git a/test/axi_bw_monitor.sv b/hw/test/axi_bw_monitor.sv similarity index 94% rename from test/axi_bw_monitor.sv rename to hw/test/axi_bw_monitor.sv index bac7ace7..eaffdace 100644 --- a/test/axi_bw_monitor.sv +++ b/hw/test/axi_bw_monitor.sv @@ -11,7 +11,7 @@ module axi_bw_monitor #( parameter type rsp_t = logic, parameter int unsigned AxiIdWidth = 4, parameter int unsigned NumAxiIds = 2**AxiIdWidth, - parameter string name = "" + parameter string Name = "" ) ( input logic clk_i, input logic en_i, @@ -143,8 +143,10 @@ module axi_bw_monitor #( read_util = real'(r_cnt) * 100 / real'(cycle_cnt); write_util = real'(w_cnt) * 100 / real'(cycle_cnt); - $display("[Monitor %s][Read] Latency: %0.2f +- %0.2f, BW: %0.2f Bits/cycle, Util: %0.2f%%", name, read_latency_mean, read_latency_stddev, read_bw, read_util); - $display("[Monitor %s][Write] Latency: %0.2f +- %0.2f, BW: %0.2f Bits/cycle, Util: %0.2f%%", name, write_latency_mean, write_latency_stddev, write_bw, write_util); + $display("[Monitor %s][Read] Latency: %0.2f +- %0.2f, BW: %0.2f Bits/cycle, Util: %0.2f%%", + Name, read_latency_mean, read_latency_stddev, read_bw, read_util); + $display("[Monitor %s][Write] Latency: %0.2f +- %0.2f, BW: %0.2f Bits/cycle, Util: %0.2f%%", + Name, write_latency_mean, write_latency_stddev, write_bw, write_util); end endmodule diff --git a/test/axi_reorder_compare.sv b/hw/test/axi_reorder_compare.sv similarity index 93% rename from test/axi_reorder_compare.sv rename to hw/test/axi_reorder_compare.sv index 0f0d1b7c..c8fbf106 100644 --- a/test/axi_reorder_compare.sv +++ b/hw/test/axi_reorder_compare.sv @@ -128,24 +128,21 @@ module axi_reorder_compare #( int unsigned num_rsp; } out_rsp_t; - aw_chan_t aw_queue [NumSlaves-1:0][$]; - w_chan_t w_queue [NumSlaves-1:0][$]; - ar_chan_t ar_queue [NumSlaves-1:0][$]; - b_chan_t b_queue [NumSlaves-1:0][NumAxiIds-1:0][$]; - r_chan_t r_queue [NumSlaves-1:0][NumAxiIds-1:0][$]; + aw_chan_t aw_queue [NumSlaves][$]; + w_chan_t w_queue [NumSlaves][$]; + ar_chan_t ar_queue [NumSlaves][$]; + b_chan_t b_queue [NumSlaves][NumAxiIds][$]; + r_chan_t r_queue [NumSlaves][NumAxiIds][$]; - out_rsp_t r_out_rsp_queue[NumAxiIds-1:0][$]; - out_rsp_t b_out_rsp_queue[NumAxiIds-1:0][$]; + out_rsp_t r_out_rsp_queue[NumAxiIds][$]; + out_rsp_t b_out_rsp_queue[NumAxiIds][$]; - id_t aw_id_queue [NumSlaves-1:0][$]; - id_t ar_id_queue [NumSlaves-1:0][$]; + id_t aw_id_queue [NumSlaves][$]; + id_t ar_id_queue [NumSlaves][$]; typedef logic [$clog2(NumSlaves)-1:0] slv_id_t; slv_id_t w_slv_idx[$]; slv_id_t aw_slv_idx, ar_slv_idx; - slv_id_t aw_slv_id[NumAxiIds-1:0][$]; - slv_id_t ar_slv_id[NumAxiIds-1:0][$]; - slv_id_t w_slv_id[NumAxiIds-1:0][$]; addr_decode #( .NoIndices ( NumAddrRegions ), @@ -193,11 +190,14 @@ module axi_reorder_compare #( end if (mon_mst_req_i.ar_valid && mon_mst_rsp_i.ar_ready) begin ar_queue[ar_slv_idx].push_back(mon_mst_req_i.ar); - r_out_rsp_queue[mon_mst_req_i.ar.id].push_back('{slv_id: ar_slv_idx, num_rsp: mon_mst_req_i.ar.len}); - if (Verbose) $info("Issued AR: id=%0d, len=%0d", mon_mst_req_i.ar.id, mon_mst_req_i.ar.len+1); + r_out_rsp_queue[mon_mst_req_i.ar.id].push_back( + '{slv_id: ar_slv_idx, num_rsp: mon_mst_req_i.ar.len}); + if (Verbose) $info("Issued AR: id=%0d, len=%0d", + mon_mst_req_i.ar.id, mon_mst_req_i.ar.len+1); end end + // verilog_lint: waive-start always-ff-non-blocking for (genvar i = 0; i < NumSlaves; i++) begin : gen_slv_step_2 always_ff @(posedge clk_i) begin : step_2 if (mon_slv_req_i[i].aw_valid && mon_slv_rsp_i[i].aw_ready) begin @@ -251,7 +251,9 @@ module axi_reorder_compare #( end end end + // verilog_lint: waive-stop always-ff-non-blocking + // verilog_lint: waive-start always-ff-non-blocking for (genvar i = 0; i < NumSlaves; i++) begin : gen_slv_step_3 always_ff @(posedge clk_i) begin : slv_step_3 if (mon_slv_rsp_i[i].b_valid && mon_slv_req_i[i].b_ready) begin @@ -267,11 +269,14 @@ module axi_reorder_compare #( r.id = ar_id_queue[i][0]; if (r.last) void'(ar_id_queue[i].pop_front()); r_queue[i][r.id].push_back(r); - if (Verbose) $info("Slave[%0d] Issued R: id=%0d, data=%0x, last=%0b", i, r.id, r.data, r.last); + if (Verbose) $info("Slave[%0d] Issued R: id=%0d, data=%0x, last=%0b", + i, r.id, r.data, r.last); end end end + // verilog_lint: waive-stop always-ff-non-blocking + // verilog_lint: waive-start always-ff-non-blocking always_ff @(posedge clk_i) begin : step_4 if (mon_mst_rsp_i.b_valid && mon_mst_req_i.b_ready) begin automatic b_chan_t b_exp, b_act; @@ -290,7 +295,8 @@ module axi_reorder_compare #( end else begin // This should always be true for B if (b_out_rsp_queue[b_id][0].num_rsp == 0) begin - if (b_out_rsp_queue[b_id].size() == 0) $error("B: id=%0d out response queue is empty!", b_id); + if (b_out_rsp_queue[b_id].size() == 0) + $error("B: id=%0d out response queue is empty!", b_id); void'(b_out_rsp_queue[b_id].pop_front()); end else begin b_out_rsp_queue[b_id][0].num_rsp--; @@ -322,6 +328,7 @@ module axi_reorder_compare #( end end end + // verilog_lint: waive-stop always-ff-non-blocking logic [NumSlaves-1:0] aw_queue_empty; logic [NumSlaves-1:0] w_queue_empty; diff --git a/test/axi_reorder_remap_compare.sv b/hw/test/axi_reorder_remap_compare.sv similarity index 85% rename from test/axi_reorder_remap_compare.sv rename to hw/test/axi_reorder_remap_compare.sv index 2b0cbd12..b64e0d0c 100644 --- a/test/axi_reorder_remap_compare.sv +++ b/hw/test/axi_reorder_remap_compare.sv @@ -31,17 +31,17 @@ module axi_reorder_remap_compare #( output logic end_of_sim_o ); -`include "axi_print_txns.svh" +`include "include/axi_print_txns.svh" -aw_chan_t aw_queue_sent [NumAxiInIds-1:0][$]; -aw_chan_t aw_queue_recv [NumAxiOutIds-1:0][$]; +aw_chan_t aw_queue_sent [NumAxiInIds][$]; +aw_chan_t aw_queue_recv [NumAxiOutIds][$]; aw_chan_t aw_w_queue_sent [$]; aw_chan_t aw_w_queue_recv [$]; -w_chan_t w_queue [NumAxiInIds-1:0][$]; -ar_chan_t ar_queue_sent [NumAxiInIds-1:0][$]; -ar_chan_t ar_queue_recv [NumAxiOutIds-1:0][$]; -b_chan_t b_queue [NumAxiInIds-1:0][$]; -r_chan_t r_queue [NumAxiInIds-1:0][$]; +w_chan_t w_queue [NumAxiInIds][$]; +ar_chan_t ar_queue_sent [NumAxiInIds][$]; +ar_chan_t ar_queue_recv [NumAxiOutIds][$]; +b_chan_t b_queue [NumAxiInIds][$]; +r_chan_t r_queue [NumAxiInIds][$]; always_ff @(posedge clk_i) begin : send_ax if (mon_mst_req_i.aw_valid && mon_mst_rsp_i.aw_ready) begin @@ -51,13 +51,14 @@ always_ff @(posedge clk_i) begin : send_ax if (mon_mst_req_i.w_valid && mon_mst_rsp_i.w_ready) begin if (aw_w_queue_sent.size() == 0) $error("No AW for W"); w_queue[aw_w_queue_sent[0].id].push_back(mon_mst_req_i.w); - if (mon_mst_req_i.w.last) aw_w_queue_sent.pop_front(); + if (mon_mst_req_i.w.last) void'(aw_w_queue_sent.pop_front()); end if (mon_mst_req_i.ar_valid && mon_mst_rsp_i.ar_ready) begin ar_queue_sent[mon_mst_req_i.ar.id].push_back(mon_mst_req_i.ar); end end +// verilog_lint: waive-start always-ff-non-blocking always_ff @(posedge clk_i) begin : recv_ax if (mon_slv_req_i.aw_valid && mon_slv_rsp_i.aw_ready) begin automatic aw_chan_t mst_aw, slv_aw; @@ -82,7 +83,7 @@ always_ff @(posedge clk_i) begin : recv_ax end aw_w_queue_recv.push_back(aw_queue_sent[i][0]); // Remove from sent queue - aw_queue_sent[i].pop_front(); + void'(aw_queue_sent[i].pop_front()); match = 1; break; end @@ -100,7 +101,7 @@ always_ff @(posedge clk_i) begin : recv_ax slv_w = mon_slv_req_i.w; if (mst_w !== slv_w) $error("W does not match"); // Check if last - if (mon_slv_req_i.w.last) aw_w_queue_recv.pop_front(); + if (mon_slv_req_i.w.last) void'(aw_w_queue_recv.pop_front()); end if (mon_slv_req_i.ar_valid && mon_slv_rsp_i.ar_ready) begin automatic ar_chan_t mst_ar, slv_ar; @@ -117,7 +118,7 @@ always_ff @(posedge clk_i) begin : recv_ax // If so, remap ID and push to recv queue ar_queue_recv[mon_slv_req_i.ar.id].push_back(ar_queue_sent[i][0]); // Remove from sent queue - ar_queue_sent[i].pop_front(); + void'(ar_queue_sent[i].pop_front()); match = 1; break; end @@ -125,7 +126,9 @@ always_ff @(posedge clk_i) begin : recv_ax if (!match) $error("No AR for AR"); end end +// verilog_lint: waive-stop always-ff-non-blocking +// verilog_lint: waive-start always-ff-non-blocking always_ff @(posedge clk_i) begin : send_rsp if (mon_slv_rsp_i.b_valid && mon_slv_req_i.b_ready) begin // Check if AW in queue @@ -133,7 +136,7 @@ always_ff @(posedge clk_i) begin : send_rsp // Enqueue B in queue of original ID b_queue[aw_queue_recv[mon_slv_rsp_i.b.id][0].id].push_back(mon_slv_rsp_i.b); // Finish AW if last (always the case for B) - aw_queue_recv[mon_slv_rsp_i.b.id].pop_front(); + void'(aw_queue_recv[mon_slv_rsp_i.b.id].pop_front()); end if (mon_slv_rsp_i.r_valid && mon_slv_req_i.r_ready) begin // Check if AR @@ -141,10 +144,12 @@ always_ff @(posedge clk_i) begin : send_rsp // Enqueue R in queue of original ID r_queue[ar_queue_recv[mon_slv_rsp_i.r.id][0].id].push_back(mon_slv_rsp_i.r); // Finish AR - if (mon_slv_rsp_i.r.last) ar_queue_recv[mon_slv_rsp_i.r.id].pop_front(); + if (mon_slv_rsp_i.r.last) void'(ar_queue_recv[mon_slv_rsp_i.r.id].pop_front()); end end +// verilog_lint: waive-stop always-ff-non-blocking +// verilog_lint: waive-start always-ff-non-blocking always_ff @(posedge clk_i) begin : recv_rsp if (mon_mst_rsp_i.b_valid && mon_mst_req_i.b_ready) begin automatic b_chan_t mst_b, slv_b; @@ -169,6 +174,7 @@ always_ff @(posedge clk_i) begin : recv_rsp if (mst_r !== slv_r) $error("R does not match"); end end +// verilog_lint: waive-stop always-ff-non-blocking logic [NumAxiInIds-1:0] aw_queue_sent_empty; logic [NumAxiOutIds-1:0] aw_queue_recv_empty; @@ -183,7 +189,7 @@ logic [NumAxiInIds-1:0] r_queue_empty; assign aw_w_queue_sent_empty = (aw_w_queue_sent.size() == 0); assign aw_w_queue_recv_empty = (aw_w_queue_recv.size() == 0); -for (genvar i = 0; i < NumAxiInIds; i++) begin : aw_queue_sent_empty_gen +for (genvar i = 0; i < NumAxiInIds; i++) begin : gen_aw_queue_sent_empty assign aw_queue_sent_empty[i] = (aw_queue_sent[i].size() == 0); assign w_queue_empty[i] = (w_queue[i].size() == 0); assign ar_queue_sent_empty[i] = (ar_queue_sent[i].size() == 0); @@ -191,7 +197,7 @@ for (genvar i = 0; i < NumAxiInIds; i++) begin : aw_queue_sent_empty_gen assign r_queue_empty[i] = (r_queue[i].size() == 0); end -for (genvar i = 0; i < NumAxiOutIds; i++) begin : aw_queue_recv_empty_gen +for (genvar i = 0; i < NumAxiOutIds; i++) begin : gen_aw_queue_recv_empty assign aw_queue_recv_empty[i] = (aw_queue_recv[i].size() == 0); assign ar_queue_recv_empty[i] = (ar_queue_recv[i].size() == 0); end diff --git a/test/floo_axi_rand_slave.sv b/hw/test/floo_axi_rand_slave.sv similarity index 95% rename from test/floo_axi_rand_slave.sv rename to hw/test/floo_axi_rand_slave.sv index ace579c2..1746273f 100644 --- a/test/floo_axi_rand_slave.sv +++ b/hw/test/floo_axi_rand_slave.sv @@ -182,7 +182,7 @@ module floo_axi_rand_slave axi_rand_fast_slave_t axi_rand_fast_slave[NumSlaves]; if (SlaveType == SlowSlave) begin : gen_slow_slaves - for (genvar i = 0; i < NumSlaves; i++) begin + for (genvar i = 0; i < NumSlaves; i++) begin : gen_slow_slaves initial begin axi_rand_slow_slave[i] = new( slave_dv[i] ); axi_rand_slow_slave[i].reset(); @@ -191,7 +191,7 @@ module floo_axi_rand_slave end end end else if (SlaveType == FastSlave) begin : gen_fast_slaves - for (genvar i = 0; i < NumSlaves; i++) begin + for (genvar i = 0; i < NumSlaves; i++) begin : gen_fast_slaves initial begin axi_rand_fast_slave[i] = new( slave_dv[i] ); axi_rand_fast_slave[i].reset(); @@ -200,15 +200,15 @@ module floo_axi_rand_slave end end end else if (SlaveType == MixedSlave) begin : gen_mixed_slaves - for (genvar i = 0; i < NumSlaves; i++) begin - if (i % 2 == 0) begin + for (genvar i = 0; i < NumSlaves; i++) begin : gen_mixed_slaves + if (i % 2 == 0) begin : gen_slow_slaves initial begin axi_rand_slow_slave[i] = new( slave_dv[i] ); axi_rand_slow_slave[i].reset(); @(posedge rst_ni) axi_rand_slow_slave[i].run(); end - end else begin + end else begin : gen_fast_slaves initial begin axi_rand_fast_slave[i] = new( slave_dv[i] ); axi_rand_fast_slave[i].reset(); diff --git a/test/floo_axi_test_node.sv b/hw/test/floo_axi_test_node.sv similarity index 96% rename from test/floo_axi_test_node.sv rename to hw/test/floo_axi_test_node.sv index 114621a7..72adf292 100644 --- a/test/floo_axi_test_node.sv +++ b/hw/test/floo_axi_test_node.sv @@ -104,7 +104,9 @@ module floo_axi_test_node #( end_of_sim = 1'b0; for (int i = 0; i < NumAddrRegions; i++) begin - axi_rand_master.add_memory_region(AddrRegions[i].start_addr, AddrRegions[i].end_addr, axi_pkg::DEVICE_NONBUFFERABLE); + axi_rand_master.add_memory_region(AddrRegions[i].start_addr, + AddrRegions[i].end_addr, + axi_pkg::DEVICE_NONBUFFERABLE); end axi_rand_master.reset(); diff --git a/test/floo_dma_test_node.sv b/hw/test/floo_dma_test_node.sv similarity index 99% rename from test/floo_dma_test_node.sv rename to hw/test/floo_dma_test_node.sv index 16b56cd7..fb86e381 100644 --- a/test/floo_dma_test_node.sv +++ b/hw/test/floo_dma_test_node.sv @@ -13,7 +13,6 @@ module floo_dma_test_node #( parameter time TA = 1ns, parameter time TT = 9ns, - parameter time TCK = 10ns, parameter int unsigned BufferDepth = 16, parameter int unsigned NumAxInFlight = 16, parameter int unsigned DataWidth = 32, @@ -336,7 +335,7 @@ module floo_dma_test_node #( if ($value$plusargs("JOB_DIR=%s", job_dir)) begin job_dir = $sformatf("%s", job_dir); end else begin - job_dir = "test/jobs"; + job_dir = "hw/test/jobs"; end if (JobId != -1) begin job_file = $sformatf("%s/%s_%0d.txt", job_dir, job_name, JobId); diff --git a/test/floo_hbm_model.sv b/hw/test/floo_hbm_model.sv similarity index 93% rename from test/floo_hbm_model.sv rename to hw/test/floo_hbm_model.sv index e12b692f..eda38622 100644 --- a/test/floo_hbm_model.sv +++ b/hw/test/floo_hbm_model.sv @@ -10,11 +10,9 @@ module floo_hbm_model #( parameter time TA = 1ns, parameter time TT = 9ns, - parameter time TCK = 10ns, parameter int unsigned Latency = 100, parameter int unsigned NumChannels = 1, - parameter int unsigned MemSize = 32'h10000, - parameter int unsigned AddrWidth = $clog2(MemSize), + parameter int unsigned AddrWidth = 32, parameter int unsigned DataWidth = 32, parameter int unsigned UserWidth = 1, parameter int unsigned IdWidth = 2, @@ -89,7 +87,7 @@ module floo_hbm_model #( ); end - for (genvar i = 0; i < NumChannels; i++) begin + for (genvar i = 0; i < NumChannels; i++) begin : gen_rand_slaves initial begin axi_rand_slave[i] = new( slave_dv[i] ); axi_rand_slave[i].reset(); diff --git a/test/floo_test_pkg.sv b/hw/test/floo_test_pkg.sv similarity index 100% rename from test/floo_test_pkg.sv rename to hw/test/floo_test_pkg.sv diff --git a/test/axi_print_txns.svh b/hw/test/include/axi_print_txns.svh similarity index 100% rename from test/axi_print_txns.svh rename to hw/test/include/axi_print_txns.svh diff --git a/test/include/tb_tasks.svh b/hw/test/include/tb_tasks.svh similarity index 100% rename from test/include/tb_tasks.svh rename to hw/test/include/tb_tasks.svh diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..2beeeb83 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,69 @@ +# Copyright 2023 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +[project] +name = "floogen" +version = "0.1.0" +authors = [ + { name="Tim Fischer", email="fischeti@iis.ee.ethz.ch" }, +] +description = "A Network Generator for FlooNoC" +readme = "README.md" +requires-python = ">=3.10" +classifiers = [ + "Programming Language :: Python :: 3.10", + "License :: OSI Approved :: Apache License", + "Operating System :: OS Independent", +] + +dependencies = [ + "pydantic", + "networkx", + "matplotlib", + "mako", + "ruamel.yaml", + "hjson", + "jsonref", + "click", + "pylint", + "pytest" +] + +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project.urls] +Homepage = "https://github.com/pulp-platform/FlooNoC" + +[project.scripts] +floogen = "floogen.floo_gen:main" + +[tool.setuptools] +packages = [ + "floogen", + "floogen.model", + "floogen.templates", + "floogen.tests", +] + +[tool.setuptools.package-data] +"floogen.templates" = ["*.mako"] + +[tool.pytest.ini_options] +testpaths = ["floogen/tests"] + +[tool.pylint.format] +max-line-length = 100 +jobs = 4 +suggestion-mode = 'yes' + +[tool.pylint.disable] +disable = [ + "C0114", # Missing module docstring + "R0801" # Similar lines in 2 files, TODO: Remove this once FlooGen is integrated into FlooNoC +] + +[tool.black] +line-length = 100 diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 017dd85b..00000000 --- a/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright 2023 ETH Zurich and University of Bologna. -# Solderpad Hardware License, Version 0.51, see LICENSE for details. -# SPDX-License-Identifier: SHL-0.51 -hjson -jsonref -mako diff --git a/test/axi_channel_compare.sv b/test/axi_channel_compare.sv deleted file mode 100644 index 0798f1c5..00000000 --- a/test/axi_channel_compare.sv +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2022 ETH Zurich and University of Bologna. -// Solderpad Hardware License, Version 0.51, see LICENSE for details. -// SPDX-License-Identifier: SHL-0.51 - -// Authors: -// - Thomas Benz -// - Paul Scheffler -// - Tim Fischer - -module axi_channel_compare #( - parameter type aw_chan_t = logic, - parameter type w_chan_t = logic, - parameter type b_chan_t = logic, - parameter type ar_chan_t = logic, - parameter type r_chan_t = logic, - parameter type req_t = logic, - parameter type resp_t = logic -)( - input logic clk_i, - input req_t axi_a_req, - input resp_t axi_a_res, - input req_t axi_b_req, - input resp_t axi_b_res -); - - function automatic void print_aw ( - input aw_chan_t aw_expected, - input aw_chan_t aw_received - ); - // verilog_lint: waive-start line-length - $display("AW | expected | received "); - $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); - $display("id: | %64d | %64d", aw_expected.id, aw_received.id); - $display("addr: | %64x | %64x", aw_expected.addr, aw_received.addr); - $display("len: | %64d | %64d", aw_expected.len, aw_received.len); - $display("size: | %64d | %64d", aw_expected.size, aw_received.size); - $display("burst: | %64d | %64d", aw_expected.burst, aw_received.burst); - $display("lock: | %64d | %64d", aw_expected.lock, aw_received.lock); - $display("cache: | %64d | %64d", aw_expected.cache, aw_received.cache); - $display("prot: | %64d | %64d", aw_expected.prot, aw_received.prot); - $display("qos: | %64d | %64d", aw_expected.qos, aw_received.qos); - $display("region: | %64d | %64d", aw_expected.region, aw_received.region); - $display("user: | %64d | %64d", aw_expected.user, aw_received.user); - $display("atop: | %64d | %64d", aw_expected.atop, aw_received.atop); - $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); - // verilog_lint: waive-stop line-length - endfunction - - function automatic void print_ar ( - input ar_chan_t ar_expected, - input ar_chan_t ar_received - ); - // verilog_lint: waive-start line-length - $display("AR | expected | received "); - $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); - $display("id: | %64d | %64d", ar_expected.id, ar_received.id); - $display("addr: | %64x | %64x", ar_expected.addr, ar_received.addr); - $display("len: | %64d | %64d", ar_expected.len, ar_received.len); - $display("size: | %64d | %64d", ar_expected.size, ar_received.size); - $display("burst: | %64d | %64d", ar_expected.burst, ar_received.burst); - $display("lock: | %64d | %64d", ar_expected.lock, ar_received.lock); - $display("cache: | %64d | %64d", ar_expected.cache, ar_received.cache); - $display("prot: | %64d | %64d", ar_expected.prot, ar_received.prot); - $display("qos: | %64d | %64d", ar_expected.qos, ar_received.qos); - $display("region: | %64d | %64d", ar_expected.region, ar_received.region); - $display("user: | %64d | %64d", ar_expected.user, ar_received.user); - $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); - // verilog_lint: waive-stop line-length - endfunction - - function automatic void print_w ( - input w_chan_t w_expected, - input w_chan_t w_received - ); - // verilog_lint: waive-start line-length - $display("W | expected | received "); - $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); - $display("data: | %64x | %64x", w_expected.data, w_received.data); - $display("strb: | %64d | %64d", w_expected.strb, w_received.strb); - $display("last: | %64d | %64d", w_expected.last, w_received.last); - $display("user: | %64d | %64d", w_expected.user, w_received.user); - $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); - // verilog_lint: waive-stop line-length - endfunction - - function automatic void print_b ( - input b_chan_t b_expected, - input b_chan_t b_received - ); - // verilog_lint: waive-start line-length - $display("B | expected | received "); - $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); - $display("id: | %64d | %64d", b_expected.id, b_received.id); - $display("resp: | %64d | %64d", b_expected.resp, b_received.resp); - $display("user: | %64d | %64d", b_expected.user, b_received.user); - $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); - // verilog_lint: waive-stop line-length - endfunction - - function automatic void print_r ( - input r_chan_t r_expected, - input r_chan_t r_received - ); - // verilog_lint: waive-start line-length - $display("R | expected | received "); - $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); - $display("id: | %64d | %64d", r_expected.id, r_received.id); - $display("data: | %64x | %64x", r_expected.data, r_received.data); - $display("resp: | %64d | %64d", r_expected.resp, r_received.resp); - $display("last: | %64d | %64d", r_expected.last, r_received.last); - $display("user: | %64d | %64d", r_expected.user, r_received.user); - $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); - // verilog_lint: waive-stop line-length - endfunction - - // queues - aw_chan_t aw_queue [$]; - w_chan_t w_queue [$]; - b_chan_t b_queue [$]; - ar_chan_t ar_queue [$]; - r_chan_t r_queue [$]; - - // requests generated at axi A: enqueue elements - always_ff @(posedge clk_i) begin : proc_enqueue_a - // aw - if (axi_a_req.aw_valid & axi_a_res.aw_ready) - aw_queue.push_back(axi_a_req.aw); - // w - if (axi_a_req.w_valid & axi_a_res.w_ready) - w_queue.push_back(axi_a_req.w); - // ar - if (axi_a_req.ar_valid & axi_a_res.ar_ready) - ar_queue.push_back(axi_a_req.ar); - end - - // responses generated at axi B: enqueue elements - always_ff @(posedge clk_i) begin : proc_enqueue_b - // b - if (axi_b_res.b_valid & axi_b_req.b_ready) - b_queue.push_back(axi_b_res.b); - // r - if (axi_b_res.r_valid & axi_b_req.r_ready) - r_queue.push_back(axi_b_res.r); - end - - // requests arriving at axi B from A: dequeue elements and check - always_ff @(posedge clk_i) begin : proc_dequeue_and_check_b - // aw - if (axi_b_req.aw_valid & axi_b_res.aw_ready) begin - automatic aw_chan_t aw_exp, aw_act; - if (aw_queue.size() == 0) $error("AW queue is empty!"); - aw_exp = aw_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking - aw_act = axi_b_req.aw; - // Ignore ID - aw_exp.id = 'X; - aw_act.id = 'X; - if (aw_exp !== aw_act) begin - $error("AW mismatch!"); - print_aw(aw_exp, aw_act); - end - end - // w - if (axi_b_req.w_valid & axi_b_res.w_ready) begin - automatic w_chan_t w_exp, w_act; - if (w_queue.size() == 0) $error("W queue is empty!"); - w_exp = w_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking - w_act = axi_b_req.w; - if (w_exp !== w_act) begin - $error("W mismatch!"); - print_w(w_exp, w_act); - end - end - // ar - if (axi_b_req.ar_valid & axi_b_res.ar_ready) begin - automatic ar_chan_t ar_exp, ar_act; - if (ar_queue.size() == 0) $error("AR queue is empty!"); - ar_exp = ar_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking - ar_act = axi_b_req.ar; - // Ignore ID - ar_exp.id = 'X; - ar_act.id = 'X; - if (ar_exp !== ar_act) begin - $error("AR mismatch!"); - print_ar(ar_exp, ar_act); - end - end - end - - // responses arriving at axi A from B: dequeue elements and check - always_ff @(posedge clk_i) begin : proc_dequeue_and_check_a - // b - if (axi_a_res.b_valid & axi_a_req.b_ready) begin - automatic b_chan_t b_exp, b_act; - if (b_queue.size() == 0) $error("B queue is empty!"); - b_exp = b_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking - b_act = axi_a_res.b; - // Ignore ID - b_exp.id = 'X; - b_act.id = 'X; - if (b_exp !== b_act) begin - $error("B mismatch!"); - print_b(b_exp, b_act); - end - end - // r - if (axi_a_res.r_valid & axi_a_req.r_ready) begin - automatic r_chan_t r_exp, r_act; - if (r_queue.size() == 0) $error("R queue is empty!"); - r_exp = r_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking - r_act = axi_a_res.r; - // Ignore ID - r_exp.id = 'X; - r_act.id = 'X; - if (r_exp !== r_act) begin - $error("R mismatch!"); - print_r(r_exp, r_act); - end - end - end - -endmodule : axi_channel_compare diff --git a/util/axi_cfg.hjson b/util/axi_cfg.hjson deleted file mode 100644 index 0c834b07..00000000 --- a/util/axi_cfg.hjson +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2022 ETH Zurich and University of Bologna. -// Licensed under the Apache License, Version 2.0, see LICENSE for details. -// SPDX-License-Identifier: Apache-2.0 -// -// Tim Fischer - -{ - name: "axi", - protocols: [ - {name: 'axi', direction: 'input', params: {dw: 64, aw: 32, iw: 3, uw: 1 }} - {name: 'axi', direction: 'output', params: {dw: 64, aw: 32, iw: 3, uw: 1 }} - ] - channel_mapping: { - req: {axi: ['aw', 'w', 'ar']} - rsp: {axi: ['b', 'r']} - }, - routing: { - route_algo: XYRouting - num_x_bits: 3 - num_y_bits: 3 - addr_offset_bits: 16 - rob_idx_bits: 8 - } -} diff --git a/util/flit_gen.py b/util/flit_gen.py deleted file mode 100755 index ef09dbc4..00000000 --- a/util/flit_gen.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2022 ETH Zurich and University of Bologna. -# Licensed under the Apache License, Version 2.0, see LICENSE for details. -# SPDX-License-Identifier: Apache-2.0 -# -# Tim Fischer - -import argparse -import hjson -import pathlib -import math -from jsonref import JsonRef -from mako.lookup import TemplateLookup - -AXI_CHANNELS = ["aw", "w", "b", "ar", "r"] - -templates = TemplateLookup(directories=[pathlib.Path(__file__).parent], - output_encoding="utf-8") - - -def clog2(x: int) -> int: - """Compute the ceil of the log2 of x.""" - return int(math.ceil(math.log(x, 2))) - - -def get_axi_chs(channel_mapping: dict, **kwargs) -> list: - """Return all the AXI channels.""" - channels = [] - for axi_chs in channel_mapping.values(): - for key, values in axi_chs.items(): - for v in values: - channels.append(f"{key}_{v}") - return channels - - -def get_inverted_mapping(channel_mapping: dict, **kwargs) -> dict: - """Return the mapping of the link.""" - mappings = {} - for phys_ch, ch_types in channel_mapping.items(): - for ch_type, axi_chs in ch_types.items(): - for axi_ch in axi_chs: - mappings.setdefault(ch_type, {})[axi_ch] = phys_ch - return mappings - - -def get_axi_channel_sizes(aw: int, dw: int, iw: int, uw: int) -> dict: - """Compute the AXI channel size in bits.""" - - # Constant widths - burst = 2 - resp = 2 - cache = 4 - prot = 3 - qos = 4 - region = 4 - len = 8 - size = 3 - atop = 6 - last = 1 - lock = 1 - - if isinstance(iw, dict): - iw = max(iw.values()) - - axi_ch_size = {} - axi_ch_size["aw"] = iw + aw + len + size + burst + lock + cache + \ - prot + qos + region + atop + uw - axi_ch_size["w"] = dw + dw//8 + last + uw - axi_ch_size["b"] = iw + resp + uw - axi_ch_size["ar"] = iw + aw + len + size + burst + lock + cache + \ - prot + qos + region + uw - axi_ch_size["r"] = iw + dw + resp + last + uw - - return axi_ch_size - - -def get_link_sizes(channel_mapping: dict, protocols: list, **kwargs) -> dict: - """Infer the link sizes AXI channels and the mapping.""" - link_sizes = {} - for phys_ch, axi_chs in channel_mapping.items(): - # Get all protocols that use this channel - used_protocols = [p for p in protocols if p['name'] in axi_chs and p['direction'] == 'input'] - # Get only the exact AXI channels that are used by the link - used_axi_chs = [axi_chs[p['name']] for p in used_protocols] - # Get the sizes of the AXI channels - axi_ch_sizes = [get_axi_channel_sizes(**p['params']) for p in used_protocols] - link_message_sizes = [] - for used_axi_ch, axi_ch_size in zip(used_axi_chs, axi_ch_sizes): - link_message_sizes += [axi_ch_size[ch] for ch in used_axi_ch] - # Get the maximum size of the link - link_sizes[phys_ch] = max(link_message_sizes) - return link_sizes - - -def main(): - """Generate a flit packet package.""" - - parser = argparse.ArgumentParser( - description="Generate flit files for a given configuration") - parser.add_argument("--config", "-c", type=pathlib.Path, required=True, help="Path to the config file") - - args = parser.parse_args() - - # Read HJSON description of System. - with open(args.config, "r") as f: - cfg = JsonRef.replace_refs(hjson.load(f)) - - kwargs = cfg - kwargs['axi_channels'] = get_axi_chs(**kwargs) - kwargs['inv_map'] = get_inverted_mapping(**kwargs) - kwargs['get_axi_channel_sizes'] = get_axi_channel_sizes - kwargs['link_sizes'] = get_link_sizes(**kwargs) - - tpl = templates.get_template("floo_flit_pkg.sv.mako") - print(tpl.render_unicode(**kwargs)) - - -if __name__ == "__main__": - main() diff --git a/util/floo_flit_pkg.sv.mako b/util/floo_flit_pkg.sv.mako deleted file mode 100644 index 124bf4af..00000000 --- a/util/floo_flit_pkg.sv.mako +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2022 ETH Zurich and University of Bologna. -// Solderpad Hardware License, Version 0.51, see LICENSE for details. -// SPDX-License-Identifier: SHL-0.51 -// -// This file is auto-generated. Do not edit! Edit the template file instead - -<% def list2array(values: list): - return '{' + ', '.join([str(a) for a in values]) + '}' -%> \ -<% def camelcase(word: str): - return ''.join([w.capitalize() for w in word.split('_')]) -%> \ -<% def clog2(value: int): - import math - return int(math.ceil(math.log(value, 2))) -%> \ -<%def zero_pad(values: list, length: int): - return values + [0] * (length - len(values)) -%> \ -<%def short_dir(direction: str): - return 'in' if direction == 'input' else 'out' -%> \ -<%def prot_full_name(name: str, direction: str, prefix: str="axi_", **kwargs): - prefix = '' if name in prefix else prefix - return prefix + name + '_' + short_dir(direction) -%> \ - -`include "axi/typedef.svh" - -package floo_${name}_pkg; - - import floo_pkg::*; - - //////////////////////// - // AXI Parameters // - //////////////////////// - -<% i = 0 %> \ - typedef enum { -% for phys_ch, mapping in channel_mapping.items(): - % for ch_type, axi_chs in mapping.items(): - % for axi_ch in axi_chs: - ${camelcase(f"{ch_type}_{axi_ch}")} = ${i}, - <% i += 1 %> \ - % endfor - % endfor -% endfor - NumAxiChannels = ${i} - } axi_ch_e; - -% for prot in protocols: - localparam int unsigned ${camelcase(prot_full_name(**prot, prefix=''))}AddrWidth = ${prot['params']['aw']}; - localparam int unsigned ${camelcase(prot_full_name(**prot, prefix=''))}DataWidth = ${prot['params']['dw']}; - localparam int unsigned ${camelcase(prot_full_name(**prot, prefix=''))}IdWidth = ${prot['params']['iw']}; - localparam int unsigned ${camelcase(prot_full_name(**prot, prefix=''))}UserWidth = ${prot['params']['uw']}; - -% endfor -% for prot in protocols: - typedef logic [${camelcase(prot_full_name(**prot, prefix=''))}AddrWidth-1:0] ${prot_full_name(**prot)}_addr_t; - typedef logic [${camelcase(prot_full_name(**prot, prefix=''))}DataWidth-1:0] ${prot_full_name(**prot)}_data_t; - typedef logic [${camelcase(prot_full_name(**prot, prefix=''))}DataWidth/8-1:0] ${prot_full_name(**prot)}_strb_t; - typedef logic [${camelcase(prot_full_name(**prot, prefix=''))}IdWidth-1:0] ${prot_full_name(**prot)}_id_t; - typedef logic [${camelcase(prot_full_name(**prot, prefix=''))}UserWidth-1:0] ${prot_full_name(**prot)}_user_t; - -% endfor -% for prot in protocols: - `AXI_TYPEDEF_ALL_CT(${prot_full_name(**prot)}, ${prot_full_name(**prot)}_req_t, ${prot_full_name(**prot)}_rsp_t, ${prot_full_name(**prot)}_addr_t, ${prot_full_name(**prot)}_id_t, ${prot_full_name(**prot)}_data_t, ${prot_full_name(**prot)}_strb_t, ${prot_full_name(**prot)}_user_t) -% endfor - - ///////////////////////// - // Header Typedefs // - ///////////////////////// - - localparam route_algo_e RouteAlgo = ${routing['route_algo']}; - typedef logic [${routing['rob_idx_bits']}:0] rob_idx_t; - -% if routing['route_algo'] == 'XYRouting': - localparam int unsigned NumXBits = ${routing['num_x_bits']}; - localparam int unsigned NumYBits = ${routing['num_y_bits']}; - localparam int unsigned XAddrOffset = ${routing['addr_offset_bits']}; - localparam int unsigned YAddrOffset = ${routing['addr_offset_bits'] + routing['num_x_bits']}; - - typedef struct packed { - logic [NumXBits-1:0] x; - logic [NumYBits-1:0] y; - } xy_id_t; - - function automatic logic [NumXBits-1:0] get_x_coord(logic [${protocols[0]['params']['aw']-1}:0] addr); - return addr[XAddrOffset +: NumXBits]; - endfunction - - function automatic logic [NumYBits-1:0] get_y_coord(logic [${protocols[0]['params']['aw']-1}:0] addr); - return addr[YAddrOffset +: NumYBits]; - endfunction - - function automatic xy_id_t get_xy_id(logic [${protocols[0]['params']['aw']-1}:0] addr); - xy_id_t id; - id.x = get_x_coord(addr); - id.y = get_y_coord(addr); - return id; - endfunction - - function automatic logic [${protocols[0]['params']['aw']-1}:0] get_base_addr(xy_id_t id); - logic [${protocols[0]['params']['aw']-1}:0] addr; - addr = id.x << XAddrOffset + id.y << YAddrOffset; - return addr; - endfunction -% elif routing['route_algo'] == 'IdTable': - typedef logic [${routing['num_id_bits']-1}:0] id_t; -% endif - - typedef struct packed { - logic rob_req; - rob_idx_t rob_idx; -% if routing['route_algo'] == 'XYRouting': - xy_id_t dst_id; - xy_id_t src_id; -% elif routing['route_algo'] == 'IdTable': - id_t dst_id; - id_t src_id; -% endif - logic last; - logic atop; - axi_ch_e axi_ch; - } hdr_t; - - //////////////////////////// - // AXI Flits Typedefs // - //////////////////////////// - - % for prot in protocols: - % if prot['direction'] == 'input': - % for axi_ch, size in get_axi_channel_sizes(**prot['params']).items(): -<% phys_ch = inv_map[prot['name']][axi_ch] %>\ -<% phys_ch_size = link_sizes[phys_ch] %>\ -<% rsvd_space = phys_ch_size - size %>\ - typedef struct packed { - hdr_t hdr; - ${prot_full_name(**prot)}_${axi_ch}_chan_t ${axi_ch}; - % if rsvd_space > 0: - logic [${rsvd_space-1}:0] rsvd; - % endif - } floo_${prot['name']}_${axi_ch}_flit_t; - - % endfor - % endif - % endfor - - //////////////////////////////// - // Generic Flits Typedefs // - //////////////////////////////// - - % for phys_ch in channel_mapping: - typedef struct packed { - hdr_t hdr; - logic [${link_sizes[phys_ch]-1}:0] rsvd; - } floo_${phys_ch}_generic_flit_t; - - % endfor - - ////////////////////////// - // Channel Typedefs // - ////////////////////////// - - % for phys_ch, mapping in channel_mapping.items(): - typedef union packed { - % for ch_type, axi_chs in mapping.items(): - % for axi_ch in axi_chs: - floo_${ch_type}_${axi_ch}_flit_t ${ch_type}_${axi_ch}; - % endfor - % endfor - floo_${phys_ch}_generic_flit_t generic; - } floo_${phys_ch}_chan_t; - - % endfor - /////////////////////// - // Link Typedefs // - /////////////////////// - - % for phys_ch in channel_mapping: - typedef struct packed { - logic valid; - logic ready; - floo_${phys_ch}_chan_t ${phys_ch}; - } floo_${phys_ch}_t; - - % endfor -endpackage diff --git a/util/gen_jobs.py b/util/gen_jobs.py index 09ad097f..b6c659b7 100755 --- a/util/gen_jobs.py +++ b/util/gen_jobs.py @@ -1,11 +1,10 @@ #!/usr/bin/env python3 -# Copyright 2023 ETH Zurich and University of Bologna. -# Solderpad Hardware License, Version 0.51, see LICENSE for details. -# SPDX-License-Identifier: SHL-0.51 +# Copyright 2024 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 # # Tim Fischer -"""Randomly generates job files.""" import random import argparse import os @@ -14,7 +13,7 @@ NUM_X = 4 NUM_Y = 4 -data_widths = {'wide': 512, 'narrow': 64} +data_widths = {"wide": 512, "narrow": 64} def clog2(x: int): @@ -24,18 +23,21 @@ def clog2(x: int): def get_xy_base_addr(x: int, y: int): """Get the address of a tile in the mesh.""" - assert (x <= NUM_X and y <= NUM_Y) - return (x + 2**clog2(NUM_X+1)*y)*MEM_SIZE - - -def gen_job_str(length: int, - src_addr: int, - dst_addr: int, - max_src_burst_size: int = 256, - max_dst_burst_size: int = 256, - r_aw_decouple: bool = False, - r_w_decouple: bool = False, - num_errors: int = 0): + assert x <= NUM_X+1 and y <= NUM_Y+1 + return (x + 2 ** clog2(NUM_X + 2) * y) * MEM_SIZE + + +def gen_job_str( + length: int, + src_addr: int, + dst_addr: int, + max_src_burst_size: int = 256, + max_dst_burst_size: int = 256, + r_aw_decouple: bool = False, + r_w_decouple: bool = False, + num_errors: int = 0, +): + # pylint: disable=too-many-arguments """Generate a single job.""" job_str = "" job_str += f"{int(length)}\n" @@ -49,92 +51,99 @@ def gen_job_str(length: int, return job_str -def emit_jobs(jobs, out_dir, name, id): +def emit_jobs(jobs, out_dir, name, idx): + """Emit jobs to file.""" # Generate directory if it does not exist if not os.path.exists(out_dir): os.makedirs(out_dir) - with open(f'{out_dir}/{name}_{id}.txt', 'w', encoding='utf-8') as job_file: + with open(f"{out_dir}/{name}_{idx}.txt", "w", encoding="utf-8") as job_file: job_file.write(jobs) job_file.close() -def gen_chimney2chimney_traffic(narrow_burst_length: int = 16, - num_narrow_bursts: int = 16, - rw: str = 'write', - bidir: bool = False, - out_dir: str = 'jobs', - **kwargs): +def gen_chimney2chimney_traffic( + narrow_burst_length: int = 16, + num_narrow_bursts: int = 16, + rw: str = "write", + bidir: bool = False, + out_dir: str = "jobs" +): """Generate Chimney to Chimney traffic.""" num_masters = 2 for i in range(num_masters): jobs = "" if bidir or i == 0: - for j in range(num_narrow_bursts): - length = narrow_burst_length*data_widths['narrow']/8 - assert (length <= MEM_SIZE) - src_addr = 0 if rw == 'write' else MEM_SIZE - dst_addr = MEM_SIZE if rw == 'write' else 0 + for _ in range(num_narrow_bursts): + length = narrow_burst_length * data_widths["narrow"] / 8 + assert length <= MEM_SIZE + src_addr = 0 if rw == "write" else MEM_SIZE + dst_addr = MEM_SIZE if rw == "write" else 0 job_str = gen_job_str(length, src_addr, dst_addr) jobs += job_str - emit_jobs(jobs, out_dir, 'chimney2chimney', i) - - -def gen_nw_chimney2chimney_traffic(narrow_burst_length: int, - wide_burst_length: int, - num_narrow_bursts: int, - num_wide_bursts: int, - rw: str, - bidir: bool, - out_dir: str, - **kwargs): + emit_jobs(jobs, out_dir, "chimney2chimney", i) + + +def gen_nw_chimney2chimney_traffic( + narrow_burst_length: int, + wide_burst_length: int, + num_narrow_bursts: int, + num_wide_bursts: int, + rw: str, + bidir: bool, + out_dir: str +): + # pylint: disable=too-many-arguments """Generate Narrow Wide Chimney to Chimney traffic.""" num_masters = 2 for i in range(num_masters): wide_jobs = "" narrow_jobs = "" - wide_length = wide_burst_length*data_widths['wide']/8 - narrow_length = narrow_burst_length*data_widths['narrow']/8 - assert (wide_length <= MEM_SIZE and narrow_length <= MEM_SIZE) - src_addr = 0 if rw == 'write' else MEM_SIZE - dst_addr = MEM_SIZE if rw == 'write' else 0 + wide_length = wide_burst_length * data_widths["wide"] / 8 + narrow_length = narrow_burst_length * data_widths["narrow"] / 8 + assert wide_length <= MEM_SIZE and narrow_length <= MEM_SIZE + src_addr = 0 if rw == "write" else MEM_SIZE + dst_addr = MEM_SIZE if rw == "write" else 0 if bidir or i == 0: - for j in range(num_wide_bursts): + for _ in range(num_wide_bursts): wide_jobs += gen_job_str(wide_length, src_addr, dst_addr) - for j in range(num_narrow_bursts): + for _ in range(num_narrow_bursts): narrow_jobs += gen_job_str(narrow_length, src_addr, dst_addr) - emit_jobs(wide_jobs, out_dir, 'nw_chimney2chimney', i) - emit_jobs(narrow_jobs, out_dir, 'nw_chimney2chimney', i+100) - - -def gen_mesh_traffic(narrow_burst_length: int, - wide_burst_length: int, - num_narrow_bursts: int, - num_wide_bursts: int, - rw: str, - type: str, - out_dir: str, - **kwargs): + emit_jobs(wide_jobs, out_dir, "nw_chimney2chimney", i) + emit_jobs(narrow_jobs, out_dir, "nw_chimney2chimney", i + 100) + + +def gen_mesh_traffic( + narrow_burst_length: int, + wide_burst_length: int, + num_narrow_bursts: int, + num_wide_bursts: int, + rw: str, + traffic_type: str, + out_dir: str, + **_kwargs +): + # pylint: disable=too-many-arguments, too-many-locals """Generate Mesh traffic.""" - for x in range(1, NUM_X+1): - for y in range(1, NUM_Y+1): + for x in range(1, NUM_X + 1): + for y in range(1, NUM_Y + 1): wide_jobs = "" narrow_jobs = "" - wide_length = wide_burst_length*data_widths['wide']/8 - narrow_length = narrow_burst_length*data_widths['narrow']/8 - assert (wide_length <= MEM_SIZE and narrow_length <= MEM_SIZE) - if type == 'hbm': + wide_length = wide_burst_length * data_widths["wide"] / 8 + narrow_length = narrow_burst_length * data_widths["narrow"] / 8 + assert wide_length <= MEM_SIZE and narrow_length <= MEM_SIZE + if traffic_type == "hbm": # Tile x=0 are the HBM channels # Each core read from the channel of its y coordinate hbm_addr = get_xy_base_addr(0, y) local_addr = get_xy_base_addr(x, y) - src_addr = hbm_addr if rw == 'read' else local_addr - dst_addr = local_addr if rw == 'read' else hbm_addr - elif type == 'random': + src_addr = hbm_addr if rw == "read" else local_addr + dst_addr = local_addr if rw == "read" else hbm_addr + elif traffic_type == "random": local_addr = get_xy_base_addr(x, y) ext_addr = get_xy_base_addr(random.randint(1, NUM_X), random.randint(1, NUM_Y)) - src_addr = ext_addr if rw == 'read' else local_addr - dst_addr = local_addr if rw == 'read' else ext_addr - elif type == 'onehop': + src_addr = ext_addr if rw == "read" else local_addr + dst_addr = local_addr if rw == "read" else ext_addr + elif traffic_type == "onehop": if not (x == 1 and y == 1): wide_length = 0 narrow_length = 0 @@ -142,44 +151,44 @@ def gen_mesh_traffic(narrow_burst_length: int, dst_addr = 0 else: local_addr = get_xy_base_addr(x, y) - ext_addr = get_xy_base_addr(x, y+1) - src_addr = ext_addr if rw == 'read' else local_addr - dst_addr = local_addr if rw == 'read' else ext_addr + ext_addr = get_xy_base_addr(x, y + 1) + src_addr = ext_addr if rw == "read" else local_addr + dst_addr = local_addr if rw == "read" else ext_addr else: - raise ValueError(f'Unknown traffic type: {type}') - for j in range(num_wide_bursts): + raise ValueError(f"Unknown traffic type: {traffic_type}") + for _ in range(num_wide_bursts): wide_jobs += gen_job_str(wide_length, src_addr, dst_addr) - for j in range(num_narrow_bursts): + for _ in range(num_narrow_bursts): narrow_jobs += gen_job_str(narrow_length, src_addr, dst_addr) - emit_jobs(wide_jobs, out_dir, 'mesh', x + (y-1)*NUM_X) - emit_jobs(narrow_jobs, out_dir, 'mesh', x + (y-1)*NUM_X + 100) + emit_jobs(wide_jobs, out_dir, "mesh", x + (y - 1) * NUM_X) + emit_jobs(narrow_jobs, out_dir, "mesh", x + (y - 1) * NUM_X + 100) def main(): - + """Main function.""" # parse arguments parser = argparse.ArgumentParser() - parser.add_argument('--out_dir', type=str, default='test/jobs') - parser.add_argument('--num_narrow_bursts', type=int, default=10) - parser.add_argument('--num_wide_bursts', type=int, default=100) - parser.add_argument('--narrow_burst_length', type=int, default=1) - parser.add_argument('--wide_burst_length', type=int, default=16) - parser.add_argument('--bidir', action='store_true') - parser.add_argument('--tb', type=str, default='dma_mesh') - parser.add_argument('--type', type=str, default='random') - parser.add_argument('--rw', type=str, default='read') + parser.add_argument("--out_dir", type=str, default="test/jobs") + parser.add_argument("--num_narrow_bursts", type=int, default=10) + parser.add_argument("--num_wide_bursts", type=int, default=100) + parser.add_argument("--narrow_burst_length", type=int, default=1) + parser.add_argument("--wide_burst_length", type=int, default=16) + parser.add_argument("--bidir", action="store_true") + parser.add_argument("--tb", type=str, default="dma_mesh") + parser.add_argument("--traffic_type", type=str, default="random") + parser.add_argument("--rw", type=str, default="read") args = parser.parse_args() kwargs = vars(args) - if args.tb == 'chimney2chimney': + if args.tb == "chimney2chimney": gen_chimney2chimney_traffic(**kwargs) - elif args.tb == 'nw_chimney2chimney': + elif args.tb == "nw_chimney2chimney": gen_nw_chimney2chimney_traffic(**kwargs) - elif args.tb == 'dma_mesh': + elif args.tb == "dma_mesh": gen_mesh_traffic(**kwargs) else: - raise ValueError(f'Unknown testbench: {args.tb}') + raise ValueError(f"Unknown testbench: {args.tb}") if __name__ == "__main__": diff --git a/util/measurements.sh b/util/measurements.sh index 64297d40..875e9129 100755 --- a/util/measurements.sh +++ b/util/measurements.sh @@ -1,9 +1,9 @@ #!/bin/bash -# Copyright 2023 ETH Zurich and University of Bologna. -# Solderpad Hardware License, Version 0.51, see LICENSE for details. -# SPDX-License-Identifier: SHL-0.51 +# Copyright 2024 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 # -# Author: Tim Fischer +# Tim Fischer types=("nw" "wo") measurement_type=("bw" "lat") diff --git a/util/narrow_wide_cfg.hjson b/util/narrow_wide_cfg.hjson deleted file mode 100644 index fc537fb1..00000000 --- a/util/narrow_wide_cfg.hjson +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2022 ETH Zurich and University of Bologna. -// Licensed under the Apache License, Version 2.0, see LICENSE for details. -// SPDX-License-Identifier: Apache-2.0 -// -// Tim Fischer - -{ - name: "narrow_wide", - protocols: [ - {name: 'narrow', direction: 'input', params: {dw: 64, aw: 48, iw: 4, uw: 5 }} - {name: 'narrow', direction: 'output', params: {dw: 64, aw: 48, iw: 2, uw: 5 }} - {name: 'wide', direction: 'input', params: {dw: 512, aw: 48, iw: 3, uw: 1 }} - {name: 'wide', direction: 'output', params: {dw: 512, aw: 48, iw: 1, uw: 1 }} - ] - channel_mapping: { - req: { - narrow: ['aw', 'w', 'ar'] - wide: ['aw', 'ar'] - } - rsp: { - narrow: ['b', 'r'] - wide: ['b'] - } - wide: { - wide: ['w', 'r'] - } - } - routing: { - route_algo: XYRouting - num_x_bits: 3 - num_y_bits: 3 - addr_offset_bits: 36 - rob_idx_bits: 8 - } -} diff --git a/util/verible.waiver b/util/verible.waiver index d507ddeb..01c51875 100644 --- a/util/verible.waiver +++ b/util/verible.waiver @@ -1,6 +1,6 @@ -# Copyright 2023 ETH Zurich and University of Bologna. -# Solderpad Hardware License, Version 0.51, see LICENSE for details. -# SPDX-License-Identifier: SHL-0.51 +# Copyright 2024 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 # Waive rule for struct typedef in synthesis wrappers -waive --rule=typedef-structs-unions --line=14 --location="src/synth/floo_synth_router_simple.sv" +waive --rule=typedef-structs-unions --line=14 --location="hw/synth/floo_synth_router_simple.sv"