├── .github └── workflows │ ├── gitlab-ci.yml │ └── lint.yml ├── .gitignore ├── .gitlab-ci.yml ├── Bender.yml ├── CHANGELOG.md ├── CODEOWNERS ├── LICENSE ├── Makefile ├── README.md ├── doc ├── README.md ├── channel_allocator.md ├── constraints.md ├── data_link_layer.md ├── fig │ ├── constraint.svg │ ├── network_layer.svg │ ├── phy-protocol.svg │ └── serial_link.svg ├── network_layer.md └── physical_layer.md ├── requirements.txt ├── src ├── axis │ ├── include │ │ └── axis │ │ │ ├── assign.svh │ │ │ └── typedef.svh │ └── src │ │ ├── axis_intf.sv │ │ └── axis_test.sv ├── channel_allocator │ ├── channel_despread_sfr.sv │ ├── channel_spread_sfr.sv │ ├── serial_link_channel_allocator.sv │ ├── stream_chopper.sv │ ├── stream_dechopper.sv │ └── stream_dechopper.sv.bak ├── regs │ ├── serial_link.hjson │ ├── serial_link_reg_pkg.sv │ ├── serial_link_reg_top.sv │ ├── serial_link_single_channel.hjson │ ├── serial_link_single_channel_reg_pkg.sv │ ├── serial_link_single_channel_reg_top.sv │ └── serial_link_single_channel_sdr.hjson ├── serial_link.sv ├── serial_link_data_link.sv ├── serial_link_network.sv ├── serial_link_occamy_wrapper.sv ├── serial_link_physical.sv ├── serial_link_pkg.sv └── serial_link_synth_wrapper.sv ├── test ├── axi_channel_compare.sv ├── tb_axi_serial_link.sv ├── tb_ch_calib_serial_link.sv ├── tb_channel_allocator.sv ├── tb_stream_chopper.sv └── tb_stream_chopper_dechopper.sv └── util ├── check-license ├── licence-checker.hjson ├── lowrisc_misc-linters ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── licence-checker │ ├── README.md │ └── licence-checker.py └── requirements.txt ├── serial_link_wave.tcl └── verible.waiver /.github/workflows/gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 ETH Zurich and University of Bologna. 2 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | # Author: Tim Fischer 6 | 7 | name: gitlab-ci 8 | 9 | on: [ push, pull_request ] 10 | 11 | jobs: 12 | 13 | check: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - 17 | name: Mirror and check 18 | uses: pulp-platform/pulp-actions/gitlab-ci@v2 19 | # Skip on forks or pull requests from forks due to missing secrets. 20 | if: > 21 | github.repository == 'pulp-platform/serial_link' && 22 | (github.event_name != 'pull_request' || 23 | github.event.pull_request.head.repo.full_name == github.repository) 24 | with: 25 | domain: iis-git.ee.ethz.ch 26 | repo: github-mirror/serial_link 27 | token: ${{ secrets.GITLAB_TOKEN }} 28 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 ETH Zurich and University of Bologna. 2 | # Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | # SPDX-License-Identifier: SHL-0.51 4 | 5 | # Author: Tim Fischer 6 | 7 | name: lint 8 | 9 | on: [push, pull_request] 10 | 11 | jobs: 12 | 13 | ############### 14 | # Check Clean # 15 | ############### 16 | check-clean: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v3 20 | - uses: actions/setup-python@v4 21 | with: 22 | python-version: '3.9' 23 | cache: 'pip' 24 | - run: pip install -r requirements.txt 25 | - name: Install bender 26 | uses: pulp-platform/pulp-actions/bender-install@v2 27 | - name: Check clean make targets 28 | run: | 29 | make -B update-regs 30 | make clean 31 | git status && test -z "$(git status --porcelain)" 32 | 33 | ############### 34 | # Check Stale # 35 | ############### 36 | check-stale: 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@v3 40 | - uses: actions/setup-python@v4 41 | with: 42 | python-version: '3.9' 43 | cache: 'pip' 44 | - run: pip install -r requirements.txt 45 | - name: Install bender 46 | uses: pulp-platform/pulp-actions/bender-install@v2 47 | - name: Check clean makefile 48 | run: | 49 | make -B update-regs 50 | git status && test -z "$(git status --porcelain)" 51 | 52 | ##################### 53 | # Bender up-to-date # 54 | ##################### 55 | bender-up-to-date: 56 | runs-on: ubuntu-latest 57 | steps: 58 | - name: Check Bender up-to-date 59 | uses: pulp-platform/pulp-actions/bender-up-to-date@v2 60 | 61 | ################ 62 | # Verible Lint # 63 | ################ 64 | lint: 65 | runs-on: ubuntu-latest 66 | steps: 67 | - uses: actions/checkout@v3 68 | - uses: chipsalliance/verible-linter-action@main 69 | with: 70 | paths: | 71 | ./src 72 | exclude_paths: | 73 | ./src/regs 74 | extra_args: "--waiver_files util/verible.waiver" 75 | github_token: ${{ secrets.GITHUB_TOKEN }} 76 | fail_on_error: true 77 | reviewdog_reporter: github-check 78 | 79 | ################# 80 | # Check License # 81 | ################# 82 | lint-license: 83 | runs-on: ubuntu-latest 84 | steps: 85 | - name: lint license 86 | uses: pulp-platform/pulp-actions/lint-license@v2 87 | with: 88 | license: | 89 | Copyright (\d{4}(-\d{4})?\s)?(ETH Zurich and University of Bologna|lowRISC contributors). 90 | (Solderpad Hardware License, Version 0.51|Licensed under the Apache License, Version 2.0), see LICENSE for details. 91 | SPDX-License-Identifier: (SHL-0.51|Apache-2.0) 92 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bender 2 | *.ini 3 | transcript 4 | *.wlf 5 | vsim.log 6 | *.vstf 7 | *.log 8 | scripts/compile_vsim.tcl 9 | work* 10 | !.github/workflows 11 | gf12 12 | Bender.lock 13 | tsmc65-test 14 | tsmc65 15 | bender 16 | AN.DB 17 | bin 18 | scripts/compile_vcs.sh 19 | ucli.key 20 | vc_hdrs.h 21 | scripts/compile_vcs.log 22 | .dvt 23 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 ETH Zurich and University of Bologna. 2 | # Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | # SPDX-License-Identifier: SHL-0.51 4 | 5 | # Author: Tim Fischer 6 | 7 | variables: 8 | VSIM: 'questa-2021.3-kgf vsim' 9 | VCS: 'vcs-2020.12 vcs' 10 | VLOGAN: 'vcs-2020.12 vlogan' 11 | 12 | stages: 13 | - build 14 | - test 15 | 16 | build_vsim: 17 | stage: build 18 | script: 19 | - make clean 20 | - make compile_questa VSIM="${VSIM}" WORK=work-vsim 21 | artifacts: 22 | expire_in: 1 week 23 | paths: 24 | - work-vsim 25 | 26 | build_vsim_single_channel: 27 | stage: build 28 | script: 29 | - make clean 30 | - make compile_questa VSIM="${VSIM}" SINGLE_CHANNEL=1 WORK=work-vsim-single-channel 31 | artifacts: 32 | expire_in: 1 week 33 | paths: 34 | - work-vsim-single-channel 35 | 36 | build_vcs_axi: 37 | stage: build 38 | script: 39 | - make clean 40 | - make bin/serial_link_axi.vcs TB_DUT=tb_axi_serial_link VCS="${VCS}" WORK=work-vcs 41 | artifacts: 42 | expire_in: 1 week 43 | paths: 44 | - bin/serial_link_axi.vcs* 45 | 46 | build_vcs_ch_calib: 47 | stage: build 48 | script: 49 | - make clean 50 | - make bin/serial_link_ch_calib.vcs TB_DUT=tb_ch_calib_serial_link VCS="${VCS}" WORK=work-vcs 51 | artifacts: 52 | expire_in: 1 week 53 | paths: 54 | - bin/serial_link_ch_calib.vcs* 55 | 56 | build_vcs_single_channel: 57 | stage: build 58 | script: 59 | - make clean 60 | - make bin/serial_link_single_channel.vcs TB_DUT=tb_axi_serial_link VCS="${VCS}" SINGLE_CHANNEL=1 WORK=work-vcs-single-channel 61 | artifacts: 62 | expire_in: 1 week 63 | paths: 64 | - bin/serial_link_single_channel.vcs* 65 | 66 | run_vsim_axi: 67 | stage: test 68 | needs: 69 | - build_vsim 70 | script: 71 | - echo "Running AXI test with R/W [$NUM_READS_1,$NUM_WRITES_1] <-> [$NUM_READS_2,$NUM_WRITES_2]" 72 | - make run TB_DUT=tb_axi_serial_link WORK=work-vsim RUN_ARGS="+NUM_READS_1=$NUM_READS_1 +NUM_WRITES_1=$NUM_WRITES_1 +NUM_READS_2=$NUM_READS_2 +NUM_WRITES_2=$NUM_WRITES_2" 73 | variables: 74 | TEST_LENGTH: 100 75 | parallel: 76 | matrix: 77 | - NUM_READS_1: [0, $TEST_LENGTH] 78 | NUM_WRITES_1: [0, $TEST_LENGTH] 79 | NUM_READS_2: [0, $TEST_LENGTH] 80 | NUM_WRITES_2: [0, $TEST_LENGTH] 81 | timeout: 15 minutes 82 | 83 | run_vsim_ch_calib: 84 | stage: test 85 | needs: 86 | - build_vsim 87 | script: 88 | - echo "Running Channel Calibration test with [$NUM_FAULTS, $NUM_FAULTS]" 89 | - make run TB_DUT=tb_ch_calib_serial_link WORK=work-vsim RUN_ARGS="+NUM_FAULTS_1=$NUM_FAULTS +NUM_FAULTS_2=$NUM_FAULTS" 90 | parallel: 91 | matrix: 92 | - NUM_FAULTS: [0, 3, 7] 93 | timeout: 15 minutes 94 | 95 | run_vsim_single_channel: 96 | stage: test 97 | needs: 98 | - build_vsim_single_channel 99 | script: 100 | - echo "Running test with single channel instance" 101 | - make run TB_DUT=tb_axi_serial_link WORK=work-vsim-single-channel 102 | timeout: 15 minutes 103 | 104 | run_vcs_axi: 105 | stage: test 106 | needs: 107 | - build_vcs_axi 108 | script: 109 | - echo "Running AXI test with R/W [$NUM_READS_1,$NUM_WRITES_1] <-> [$NUM_READS_2,$NUM_WRITES_2]" 110 | - bin/serial_link_axi.vcs +permissive -exitstatus +NUM_READS_1=$NUM_READS_1 +NUM_WRITES_1=$NUM_WRITES_1 +NUM_READS_2=$NUM_READS_2 +NUM_WRITES_2=$NUM_WRITES_2 +permissive-off 111 | variables: 112 | TEST_LENGTH: 100 113 | parallel: 114 | matrix: 115 | - NUM_READS_1: [0, $TEST_LENGTH] 116 | NUM_WRITES_1: [0, $TEST_LENGTH] 117 | NUM_READS_2: [0, $TEST_LENGTH] 118 | NUM_WRITES_2: [0, $TEST_LENGTH] 119 | timeout: 15 minutes 120 | 121 | run_vcs_ch_calib: 122 | stage: test 123 | needs: 124 | - build_vcs_ch_calib 125 | script: 126 | - echo "Running Channel Calibration test with [$NUM_FAULTS, $NUM_FAULTS]" 127 | - bin/serial_link_ch_calib.vcs +permissive -exitstatus +NUM_FAULTS_1=$NUM_FAULTS +NUM_FAULTS_2=$NUM_FAULTS +permissive-off 128 | parallel: 129 | matrix: 130 | # TODO: Check why CI fails with 7 faults in VCS 131 | # - NUM_FAULTS: [0, 3, 7] 132 | - NUM_FAULTS: [0, 3] 133 | timeout: 15 minutes 134 | 135 | run_vcs_single_channel: 136 | stage: test 137 | needs: 138 | - build_vcs_single_channel 139 | script: 140 | - echo "Running test with single channel instance" 141 | - bin/serial_link_single_channel.vcs 142 | timeout: 15 minutes 143 | -------------------------------------------------------------------------------- /Bender.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 ETH Zurich and University of Bologna. 2 | # Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | # SPDX-License-Identifier: SHL-0.51 4 | 5 | package: 6 | name: serial_link 7 | authors: 8 | - "Tim Fischer " 9 | - "Manuel Eggimann " 10 | - "Thomas Benz " 11 | - "Paul Scheffler " 12 | 13 | dependencies: 14 | common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.28.0 } 15 | axi: { git: "https://github.com/pulp-platform/axi.git", version: 0.38.0 } 16 | register_interface: { git: "https://github.com/pulp-platform/register_interface.git", version: 0.3.8 } 17 | 18 | export_include_dirs: 19 | - src/axis/include 20 | 21 | sources: 22 | files: 23 | 24 | # Configuration Registers 25 | - src/regs/serial_link_reg_pkg.sv 26 | - src/regs/serial_link_reg_top.sv 27 | - src/regs/serial_link_single_channel_reg_pkg.sv 28 | - src/regs/serial_link_single_channel_reg_top.sv 29 | 30 | # Parametrization 31 | - src/serial_link_pkg.sv 32 | 33 | # Abstraction Layers 34 | - src/channel_allocator/stream_chopper.sv 35 | - src/channel_allocator/stream_dechopper.sv 36 | - src/channel_allocator/channel_despread_sfr.sv 37 | - src/channel_allocator/channel_spread_sfr.sv 38 | - src/channel_allocator/serial_link_channel_allocator.sv 39 | - src/serial_link_network.sv 40 | - src/serial_link_data_link.sv 41 | - src/serial_link_physical.sv 42 | 43 | # Serial Link Wrapper 44 | - src/serial_link.sv 45 | 46 | # Wrapper for Occamy 47 | - src/serial_link_occamy_wrapper.sv 48 | 49 | - target: synthesis 50 | files: 51 | - src/serial_link_synth_wrapper.sv 52 | 53 | - target: any(simulation, test) 54 | files: 55 | - test/axi_channel_compare.sv 56 | 57 | - target: test 58 | files: 59 | - test/tb_axi_serial_link.sv 60 | - test/tb_ch_calib_serial_link.sv 61 | - test/tb_stream_chopper.sv 62 | - test/tb_stream_chopper_dechopper.sv 63 | - test/tb_channel_allocator.sv 64 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## Unreleased 8 | 9 | - Added Support for Single-Data-Rate (SDR) PHY 10 | 11 | ## 1.1.2 - 2024-08-30 12 | 13 | - Removed ternary statement in `serial_link_physical` for better EDA tool compatibility. 14 | 15 | ## 1.1.1 - 2024-02-07 16 | 17 | ### Changed 18 | 19 | - The DDR output data is now muxed by a normal signal instead of a clock signal. Some tools infer a clock gate when a clock signal is used as a mux select signal. 20 | 21 | ## 1.1.0 - 2023-07-03 22 | 23 | ### Changed 24 | - Renamed clock division configuration registers to deliberately introduce breaking changes when using the old incorrect configuration registers. 25 | 26 | ### Fixed 27 | - SW Clock division configuration 28 | 29 | ## 1.0.1 - 2023-03-13 30 | 31 | ### Changed 32 | - Added `NoRegCdc` parameter to `serial_link` module to disable the CDC between the RegBus Clock and the System Clock 33 | 34 | ## 1.0.0 - 2023-01-26 35 | - Initial release 36 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Global owners 2 | * @fischeti 3 | 4 | # Channel Allocator 5 | # TODO: activate for release 6 | # src/channel_allocator @meggiman 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | SOLDERPAD HARDWARE LICENSE version 0.51 2 | 3 | This license is based closely on the Apache License Version 2.0, but is not 4 | approved or endorsed by the Apache Foundation. A copy of the non-modified 5 | Apache License 2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0. 6 | 7 | As this license is not currently OSI or FSF approved, the Licensor permits any 8 | Work licensed under this License, at the option of the Licensee, to be treated 9 | as licensed under the Apache License Version 2.0 (which is so approved). 10 | 11 | This License is licensed under the terms of this License and in particular 12 | clause 7 below (Disclaimer of Warranties) applies in relation to its use. 13 | 14 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 15 | 16 | 1. Definitions. 17 | 18 | "License" shall mean the terms and conditions for use, reproduction, and 19 | distribution as defined by Sections 1 through 9 of this document. 20 | 21 | "Licensor" shall mean the Rights owner or entity authorized by the Rights owner 22 | that is granting the License. 23 | 24 | "Legal Entity" shall mean the union of the acting entity and all other entities 25 | that control, are controlled by, or are under common control with that entity. 26 | For the purposes of this definition, "control" means (i) the power, direct or 27 | indirect, to cause the direction or management of such entity, whether by 28 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 29 | outstanding shares, or (iii) beneficial ownership of such entity. 30 | 31 | "You" (or "Your") shall mean an individual or Legal Entity exercising 32 | permissions granted by this License. 33 | 34 | "Rights" means copyright and any similar right including design right (whether 35 | registered or unregistered), semiconductor topography (mask) rights and 36 | database rights (but excluding Patents and Trademarks). 37 | 38 | "Source" form shall mean the preferred form for making modifications, including 39 | but not limited to source code, net lists, board layouts, CAD files, 40 | documentation source, and configuration files. 41 | 42 | "Object" form shall mean any form resulting from mechanical transformation or 43 | translation of a Source form, including but not limited to compiled object 44 | code, generated documentation, the instantiation of a hardware design and 45 | conversions to other media types, including intermediate forms such as 46 | bytecodes, FPGA bitstreams, artwork and semiconductor topographies (mask 47 | works). 48 | 49 | "Work" shall mean the work of authorship, whether in Source form or other 50 | Object form, made available under the License, as indicated by a Rights notice 51 | that is included in or attached to the work (an example is provided in the 52 | Appendix below). 53 | 54 | "Derivative Works" shall mean any work, whether in Source or Object form, that 55 | is based on (or derived from) the Work and for which the editorial revisions, 56 | annotations, elaborations, or other modifications represent, as a whole, an 57 | original work of authorship. For the purposes of this License, Derivative Works 58 | shall not include works that remain separable from, or merely link (or bind by 59 | name) or physically connect to or interoperate with the interfaces of, the Work 60 | and Derivative Works thereof. 61 | 62 | "Contribution" shall mean any design or work of authorship, including the 63 | original version of the Work and any modifications or additions to that Work or 64 | Derivative Works thereof, that is intentionally submitted to Licensor for 65 | inclusion in the Work by the Rights owner or by an individual or Legal Entity 66 | authorized to submit on behalf of the Rights owner. For the purposes of this 67 | definition, "submitted" means any form of electronic, verbal, or written 68 | communication sent to the Licensor or its representatives, including but not 69 | limited to communication on electronic mailing lists, source code control 70 | systems, and issue tracking systems that are managed by, or on behalf of, the 71 | Licensor for the purpose of discussing and improving the Work, but excluding 72 | communication that is conspicuously marked or otherwise designated in writing 73 | by the Rights owner as "Not a Contribution." 74 | 75 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 76 | of whom a Contribution has been received by Licensor and subsequently 77 | incorporated within the Work. 78 | 79 | 2. Grant of License. Subject to the terms and conditions of this License, each 80 | Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 81 | no-charge, royalty-free, irrevocable license under the Rights to reproduce, 82 | prepare Derivative Works of, publicly display, publicly perform, sublicense, 83 | and distribute the Work and such Derivative Works in Source or Object form and 84 | do anything in relation to the Work as if the Rights did not exist. 85 | 86 | 3. Grant of Patent License. Subject to the terms and conditions of this 87 | License, each Contributor hereby grants to You a perpetual, worldwide, 88 | non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this 89 | section) patent license to make, have made, use, offer to sell, sell, import, 90 | and otherwise transfer the Work, where such license applies only to those 91 | patent claims licensable by such Contributor that are necessarily infringed by 92 | their Contribution(s) alone or by combination of their Contribution(s) with the 93 | Work to which such Contribution(s) was submitted. If You institute patent 94 | litigation against any entity (including a cross-claim or counterclaim in a 95 | lawsuit) alleging that the Work or a Contribution incorporated within the Work 96 | constitutes direct or contributory patent infringement, then any patent 97 | licenses granted to You under this License for that Work shall terminate as of 98 | the date such litigation is filed. 99 | 100 | 4. Redistribution. You may reproduce and distribute copies of the Work or 101 | Derivative Works thereof in any medium, with or without modifications, and in 102 | Source or Object form, provided that You meet the following conditions: 103 | 104 | You must give any other recipients of the Work or Derivative Works a copy 105 | of this License; and 106 | 107 | You must cause any modified files to carry prominent notices stating that 108 | You changed the files; and 109 | 110 | You must retain, in the Source form of any Derivative Works that You 111 | distribute, all copyright, patent, trademark, and attribution notices from 112 | the Source form of the Work, excluding those notices that do not pertain to 113 | any part of the Derivative Works; and 114 | 115 | If the Work includes a "NOTICE" text file as part of its distribution, then 116 | any Derivative Works that You distribute must include a readable copy of 117 | the attribution notices contained within such NOTICE file, excluding those 118 | notices that do not pertain to any part of the Derivative Works, in at 119 | least one of the following places: within a NOTICE text file distributed as 120 | part of the Derivative Works; within the Source form or documentation, if 121 | provided along with the Derivative Works; or, within a display generated by 122 | the Derivative Works, if and wherever such third-party notices normally 123 | appear. The contents of the NOTICE file are for informational purposes only 124 | and do not modify the License. You may add Your own attribution notices 125 | within Derivative Works that You distribute, alongside or as an addendum to 126 | the NOTICE text from the Work, provided that such additional attribution 127 | notices cannot be construed as modifying the License. You may add Your own 128 | copyright statement to Your modifications and may provide additional or 129 | different license terms and conditions for use, reproduction, or 130 | distribution of Your modifications, or for any such Derivative Works as a 131 | whole, provided Your use, reproduction, and distribution of the Work 132 | otherwise complies with the conditions stated in this License. 133 | 134 | 5. Submission of Contributions. Unless You explicitly state otherwise, any 135 | Contribution intentionally submitted for inclusion in the Work by You to the 136 | Licensor shall be under the terms and conditions of this License, without any 137 | additional terms or conditions. Notwithstanding the above, nothing herein shall 138 | supersede or modify the terms of any separate license agreement you may have 139 | executed with Licensor regarding such Contributions. 140 | 141 | 6. Trademarks. This License does not grant permission to use the trade names, 142 | trademarks, service marks, or product names of the Licensor, except as required 143 | for reasonable and customary use in describing the origin of the Work and 144 | reproducing the content of the NOTICE file. 145 | 146 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in 147 | writing, Licensor provides the Work (and each Contributor provides its 148 | Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 149 | KIND, either express or implied, including, without limitation, any warranties 150 | or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any risks 153 | associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, whether in 156 | tort (including negligence), contract, or otherwise, unless required by 157 | applicable law (such as deliberate and grossly negligent acts) or agreed to in 158 | writing, shall any Contributor be liable to You for damages, including any 159 | direct, indirect, special, incidental, or consequential damages of any 160 | character arising as a result of this License or out of the use or inability to 161 | use the Work (including but not limited to damages for loss of goodwill, work 162 | stoppage, computer failure or malfunction, or any and all other commercial 163 | damages or losses), even if such Contributor has been advised of the 164 | possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or 167 | Derivative Works thereof, You may choose to offer, and charge a fee for, 168 | acceptance of support, warranty, indemnity, or other liability obligations 169 | and/or rights consistent with this License. However, in accepting such 170 | obligations, You may act only on Your own behalf and on Your sole 171 | responsibility, not on behalf of any other Contributor, and only if You agree 172 | to indemnify, defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason of your 174 | accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2022 ETH Zurich and University of Bologna. 2 | # Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | # SPDX-License-Identifier: SHL-0.51 4 | 5 | # Author: Tim Fischer 6 | 7 | BENDER ?= bender 8 | VSIM ?= vsim 9 | PYTHON ?= python3 10 | REGGEN_PATH ?= $(shell ${BENDER} path register_interface)/vendor/lowrisc_opentitan/util/regtool.py 11 | REGGEN = $(PYTHON) $(REGGEN_PATH) 12 | WORK ?= work 13 | 14 | all: compile_questa 15 | 16 | clean: clean_bender clean_questa clean_vcs 17 | 18 | run: run_questa 19 | 20 | # Ensure half-built targets are purged 21 | .DELETE_ON_ERROR: 22 | 23 | # -------------- 24 | # General 25 | # -------------- 26 | 27 | .PHONY: clean_bender 28 | 29 | Bender.lock: 30 | $(BENDER) update 31 | 32 | clean_bender: 33 | rm -rf .bender 34 | rm -rf Bender.lock 35 | 36 | 37 | # -------------- 38 | # Registers 39 | # -------------- 40 | 41 | .PHONY: update-regs 42 | 43 | update-regs: src/regs/*.hjson 44 | echo $(REGGEN) 45 | $(REGGEN) src/regs/serial_link.hjson -r -t src/regs 46 | $(REGGEN) src/regs/serial_link_single_channel.hjson -r -t src/regs 47 | 48 | # -------------- 49 | # QuestaSim 50 | # -------------- 51 | 52 | TB_DUT ?= tb_axi_serial_link 53 | 54 | BENDER_FLAGS := -t test -t simulation 55 | 56 | VLOG_FLAGS += -suppress vlog-2583 57 | VLOG_FLAGS += -suppress vlog-13314 58 | VLOG_FLAGS += -suppress vlog-13233 59 | VLOG_FLAGS += -timescale 1ns/1ps 60 | VLOG_FLAGS += -work $(WORK) 61 | 62 | VSIM_FLAGS += $(TB_DUT) 63 | VSIM_FLAGS += -work $(WORK) 64 | VSIM_FLAGS += $(RUN_ARGS) 65 | 66 | ifeq ($(GUI), true) 67 | VSIM_FLAGS += -voptargs=+acc 68 | VSIM_FLAGS += -do "log -r /*; do util/serial_link_wave.tcl; run -all" 69 | else 70 | VSIM_FLAGS += -c 71 | VSIM_FLAGS += -do "run -all; exit" 72 | endif 73 | 74 | .PHONY: compile_questa clean_questa run_questa 75 | 76 | scripts/compile_vsim.tcl: Bender.lock 77 | @mkdir -p scripts 78 | @echo 'set ROOT [file normalize [file dirname [info script]]/..]' > $@ 79 | $(BENDER) script vsim --vlog-arg="$(VLOG_FLAGS)" $(BENDER_FLAGS) | grep -v "set ROOT" >> $@ 80 | @echo >> $@ 81 | 82 | compile_questa: scripts/compile_vsim.tcl 83 | ifeq ($(SINGLE_CHANNEL),1) 84 | @sed 's/NumChannels = [0-9]*/NumChannels = 1/' src/serial_link_pkg.sv -i.prev 85 | $(VSIM) -c -work $(WORK) -do "source $<; quit" | tee $(dir $<)vsim.log 86 | @mv src/serial_link_pkg.sv.prev src/serial_link_pkg.sv 87 | else 88 | $(VSIM) -c -work $(WORK) -do "source $<; quit" | tee $(dir $<)vsim.log 89 | endif 90 | @! grep -P "Errors: [1-9]*," $(dir $<)vsim.log 91 | 92 | clean_questa: 93 | @rm -rf scripts/compile_vsim.tcl 94 | @rm -rf work* 95 | @rm -rf vsim.wlf 96 | @rm -rf transcript 97 | @rm -rf modelsim.ini 98 | @rm -rf *.vstf 99 | @rm -rf scripts/vsim.log 100 | 101 | run_questa: 102 | $(VSIM) $(VSIM_FLAGS) 103 | 104 | 105 | # -------------- 106 | # VCS 107 | # -------------- 108 | 109 | .PHONY: compile_vcs clean_vcs 110 | 111 | VLOGAN_ARGS := -assert svaext 112 | VLOGAN_ARGS += -assert disable_cover 113 | VLOGAN_ARGS += -full64 114 | VLOGAN_ARGS += -sysc=q 115 | VLOGAN_ARGS += -q 116 | VLOGAN_ARGS += -timescale=1ns/1ps 117 | 118 | VCS_ARGS := -full64 119 | VCS_ARGS += -Mlib=$(WORK) 120 | VCS_ARGS += -Mdir=$(WORK) 121 | VCS_ARGS += -debug_access+pp 122 | VCS_ARGS += -j 8 123 | VCS_ARGS += -CFLAGS "-Os" 124 | 125 | VCS_PARAMS ?= 126 | TB_DUT ?= tb_axi_serial_link 127 | 128 | VLOGAN ?= vlogan 129 | VCS ?= vcs 130 | 131 | VLOGAN_REL_PATHS ?= | grep -v "ROOT=" | sed '3 i ROOT="."' 132 | 133 | scripts/compile_vcs.sh: Bender.yml Bender.lock 134 | @mkdir -p scripts 135 | $(BENDER) script vcs -t test -t rtl -t simulation --vlog-arg "\$(VLOGAN_ARGS)" --vlogan-bin "$(VLOGAN)" $(VLOGAN_REL_PATHS) > $@ 136 | chmod +x $@ 137 | 138 | compile_vcs: scripts/compile_vcs.sh 139 | ifeq ($(SINGLE_CHANNEL),1) 140 | @sed 's/NumChannels = [0-9]*/NumChannels = 1/' src/serial_link_pkg.sv -i.prev 141 | $< > scripts/compile_vcs.log 142 | @mv src/serial_link_pkg.sv.prev src/serial_link_pkg.sv 143 | else 144 | $< > scripts/compile_vcs.log 145 | endif 146 | 147 | bin/%.vcs: scripts/compile_vcs.sh compile_vcs 148 | mkdir -p bin 149 | $(VCS) $(VCS_ARGS) $(VCS_PARAMS) $(TB_DUT) -o $@ 150 | 151 | clean_vcs: 152 | @rm -rf AN.DB 153 | @rm -f scripts/compile_vcs.sh 154 | @rm -rf bin 155 | @rm -rf work-vcs 156 | @rm -f ucli.key 157 | @rm -f vc_hdrs.h 158 | @rm -f logs/*.vcs.log 159 | @rm -f scripts/compile_vcs.log 160 | 161 | # -------------- 162 | # CI 163 | # -------------- 164 | 165 | .PHONY: bender 166 | 167 | bender: 168 | ifeq (,$(wildcard ./bender)) 169 | curl --proto '=https' --tlsv1.2 -sSf https://pulp-platform.github.io/bender/init \ 170 | | bash -s -- 0.25.3 171 | touch bender 172 | endif 173 | 174 | .PHONY: remove_bender 175 | remove_bender: 176 | rm -f bender 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Serial Link 3 | [![SHL-0.51 license](https://img.shields.io/badge/license-SHL--0.51-green)](LICENSE) 4 | 5 | The serial link is a simple all-digital Double-Data-Rate (DDR) or Single-Data-Rate (SDR) link with a source-synchronous interface. The link is scalable and can be used for high-bandwidth low latency applications like Die2Die communication as well as lower demanding tasks like binary preloading. The link has an AXI4 interface and implements Network, Data Link and Physical layer. The serial link is part of the [PULP (Parallel Ultra-Low Power) Platform](https://pulp-platform.org/) and is being used in various chip tapeouts e.g. [Snitch based Systems](https://github.com/pulp-platform/snitch) 6 | 7 | ## Architecture Overview 8 | The serial link implements the 3 lowest layers of the OSI reference model: 9 | * **Network Layer:** AXI requests and the responses are serialized and translated to an AXI-Stream interface 10 | * **Data Link Layer:** Splits the payload of the AXI stream into multiple packets which are distributed over the physical channels. A *Channel Allocator* reshuffles the packets and is able to recover defects of physical channels. It is able to apply back-pressure with a credit-based flow control mechanism. It also synchronizes the packets of multiple channels. 11 | * **Physical Layer:** Parametrizable number of channels and wires per channel. Each TX channel forwards its own source-synchronous clock which is a divided clock of the system clock. The RX channels samples the data with the received clock and has a CDC to synchronize to the local system clock. 12 | 13 | ## License 14 | The Serial Link is released under Solderpad v0.51 (SHL-0.51) see [`LICENSE`](LICENSE): 15 | 16 | ## Getting started 17 | ### Dependencies 18 | The link 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`. Currently, we do not provide any open-source simulation setup. Internally, the Serial Link was tested using QuestaSim. 19 | 20 | ### Simulation 21 | The Serial Link can be simulated in QuestaSim with the following steps: 22 | ```sh 23 | # To compile the link, run the following command: 24 | make all 25 | # Run the simulation. This will start the simulation in batch mode. 26 | make run 27 | # To open it in the GUI mode, run the following command: 28 | # This command will also add all interesting waves to the wave window. 29 | make run GUI=true 30 | ``` 31 | 32 | ## Configuration 33 | The link can be parametrized with arbitrary AXI interfaces resp. structs (`axi_req_t`, `axi_rsp_t`). Further, the number of Channels number of Lanes per Channel is configurable in `serial_link_pkg.sv`. 34 | 35 | ### Single-Channel 36 | For simple use cases with lower low bandwidth requirements (e.g. binary preloading), it is recommended to use a single-channel configuration, possibly with Single-Data-Rate. Single-channel configurations come with less overhead for channel synchronization and fault detection. 37 | 38 | ### Multi-Channel 39 | For use cases that require a higher bandwidth (e.g. Die2Die communication), a multi-channel configuration is recommended. In multi-channel configurations, each channel has its own source-synchronous forwarded clock and the channels are synchronized on the receiver side again. Further, a channel allocator handles faulty channels by redistributing the packets to functional channels. The detection of faulty channels can be done entirely in SW with a special _Raw Mode_ that decouples the link from the AXI interface and allows full controllability and observability of independent channels. 40 | 41 | ### Configuration Registers 42 | Single-channel and Multi-channels currently use different configuration register files because the multi-channel configuration requires additional registers for the channel allocator etc. The registers are generated with the [reggen](https://opentitan.org/book/util/reggen/index.html). The config files for single-channel (`serial_link_single_channel.hjson`) and multi-channel (`serial_link.hjson`) can be found in the `src/regs` folder and can be regenerated with the following command: 43 | 44 | ``` 45 | make update-regs 46 | ``` 47 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # Documentation of Serial Link 2 | This folder contains the documentation of the architecture which is split into different modules: 3 | * Network layer ([`serial_link_network`](../src/serial_link_network.sv)) 4 | * Data Link layer ([`serial_link_data_link`](../src/serial_link_data_link.sv)) 5 | * Channel Allocator ([`serial_link_channel_allocator`](../src/channel_allocator/serial_link_channel_allocator.sv)) 6 | * Physical layer ([`serial_link_physical`](../src/serial_link_physical.sv)) 7 | 8 | ## Architecture 9 | An high-level block diagram of the Serial Link design is shown below 10 | 11 | ![Block-diagram showing the design Serial Link.](fig/serial_link.svg "Block-diagram showing the design Serial Link.") 12 | 13 | ## Configuration 14 | The Serial Link has the following configuration Parameters: 15 | | Name | Type | Definition | 16 | |---------------|----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 17 | | `axi_req_t` | `struct` | arbitrary AXI request struct defined with `AXI_TYPEDEF` macro. Both Master and Slave ports need to use the same `req` struct | 18 | | `axi_rsp_t` | `struct` | arbitrary AXI response struct defined with `AXI_TYPEDEF` macro. Both Master and Slave ports need to use the same `rsp` struct | 19 | | `*_chan_t` | `struct` | AXI channel structs which matches `axi_req_t` and `axi_rsp_t`. Byproduct of `AXI_TYPEDEF` macro | 20 | | `cfg_req_t` | `struct` | `register_interface` request struct defined with `REG_BUS_TYPEDEF` macro | 21 | | `cfg_rsp_t` | `struct` | `register_interface` response struct defined with `REG_BUS_TYPEDEF` macro | 22 | | `hw2reg_t` | `struct` | struct that defines signals from the logic to the configuration registers generated by `reggen` e.g. `serial_link_reg_pkg::serial_link_hw2reg_t` | 23 | | `reg2hw_t` | `struct` | struct that defines signals from the configuration registers defined by `reggen` to the logic e.g. `serial_link_reg_pkg::serial_link_reg2hw_t` | 24 | | `NumChannels` | `int unsigned` | Number of Channels of the Serial Link. If `NumChannels > 1`, a *Channel Allocator* will be instantiated | 25 | | `NumLanes` | `int unsigned` | Number of Lanes per Channel. Higher numbers reduce the wire overhead for the clocks for the source-synchronous interface but increase the timing complexity and defect probability. Recommended number of lanes are `8` | 26 | | `NumCredits` | `int unsigned` | Number of outstanding payloads in flight that have not been processed yet by the other side. Be careful with increasing this parameter as it will increase the FIFOs inside the Serial Link. | 27 | | `MaxClkDiv` | `int unsigned` | Maximum clock division factor. Mainly used to dimension the counter for the clock division generator | 28 | | `EnDdr` | `bit` | Sets the transmission mode. By default DDR is enabled. 29 | 30 | ## Ports 31 | | Name | Type | Description | 32 | |-----------------|--------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 33 | | `clk_i` | `logic` | Always-on system clock coming from the SoC domain. Only the configuration registers are connected to this clock | 34 | | `rst_ni` | `logic` | Global asynchronous reset, active-low. Only the configuration registers are connected to this reset signal | 35 | | `clk_sl_i` | `logic` | Clock for `serial_link_network`, `serial_link_data_link`, `serial_link_channel_allocator` and `serial_link_physical`. Intended for clock gating purposes, otherwise connect to `clk_i` | 36 | | `rst_sl_ni` | `logic` | Asynchronous reset, active low. Resets `serial_link_network`, `serial_link_data_link`, `serial_link_channel_allocator` and `serial_link_physical`. Intended for SW reset purposes, otherwise connect to `rst_ni` | 37 | | `clk_reg_i` | `logic` | Clock for `cfg_req_i` and `cfg_rsp_o` | 38 | | `rst_reg_ni` | `logic` | Asynchronous active-low reset for `cfg_req_i` and `cfg_rsp_o` | 39 | | `testmode_i` | `logic` | Currently not used, tie to `'0` | 40 | | `axi_in_req_i` | `axi_req_t` | AXI4 Slave request port | 41 | | `axi_in_rsp_o` | `axi_rsp_t` | AXI4 Slave response port | 42 | | `axi_out_req_o` | `axi_req_t` | AXI4 Master request port | 43 | | `axi_out_rsp_i` | `axi_rsp_t` | AXI4 Master response port | 44 | | `cfg_req_i` | `cfg_req_t` | `register_interface` request port for configuration registers | 45 | | `cfg_rsp_o` | `cfg_rsp_t` | `register_interface` response port for configuration registers | 46 | | `ddr_rcv_clk_i` | `logic[NumChannels]` | Source-synchronous input clock to sample data. One clock per channel | 47 | | `ddr_rcv_clk_o` | `logic[NumChannels]` | Source-synchronous output clock which is forwarded together with the data. One clock per channel | 48 | | `ddr_i` | `logic[NumChannels][NumLanes]` | Double-Data-Rate (DDR) input data | 49 | | `ddr_o` | `logic[NumChannels][NumLanes]` | Double-Data-Rate (DDR) output data | 50 | | `isolated_i` | `logic[1:0]` | AXI isolation observation signal for in and output. Currently not used, tie to `2'b0`. This signal can be observed through the configuration registers, intended to observe a `axi_isolate` module | 51 | | `isolate_o` | `logic[1:0]` | AXI isolation control signal for in and output. Currently not used, can be left floating. This signal can be controlled over the configuration registers, intended to control a `axi_isolate` module | 52 | | `clk_ena_o` | `logic` | Clock enable control signal. Currently not used, can be left floating. This signal can be controlled over the configuration registers, intented to control a clock gating cell | 53 | | `reset_no` | `logic` | Asynchronous reset, active-low. SW controlable. Currently not used, can be left floating. Intended to SW reset wrapper modules like `axi_isolate` etc. | 54 | -------------------------------------------------------------------------------- /doc/channel_allocator.md: -------------------------------------------------------------------------------- 1 | ## Channel Allocator 2 | 3 | The *Channel Allocator* is a module of the Serial Link between the Data Link Layer and the Physical Layer that handles faulty channels. It is able to redistribute packets assigned to faulty channels to working channel. Further, it also takes care of the synchronization of the RX channels. 4 | 5 | ### Calibration 6 | The *Channel Allocator* needs to be configured with the active/working RX and TX channels. To this end, the Serial Link needs to be calibrated initially. The calibration is done by configuring the Serial Link in the [*Raw Mode*](https://github.com/pulp-platform/serial_link/blob/doc/doc/data_link_layer.md#raw-mode) where a mask of working resp. faulty channels is extracted and which is used to configure the Channel Allocator 7 | 8 | ### Operating modes 9 | The *Channel Allocator* can be bypassed by SW configuration e.g. this is required during the *Raw Mode*. 10 | 11 | ### RX Synchronization 12 | The Data Link layer splits up a AXI Stream payload to smaller packets that are sent over different channels. On the receiving side those packets need to be assembled again to a AXI Stream payload. Since different channels might have different delays, the packets of the channels need to be synchronized, which is done by the *Channel Allocator* -------------------------------------------------------------------------------- /doc/constraints.md: -------------------------------------------------------------------------------- 1 | ## Constraints 2 | The Serial Link can be run using either Double-Data-Rate (DDR) or Single-Data-Rate and a source-synchronous interface with generated clocks which makes the constraints quite involved. The constraints for DDR were by the *Source-Synchronous DDR IO Timing Constraints Cookbook* by David Olsen, Altera Corporation, 2010. The constraints for SDR were by *AN 433: Constraining and Analyzing Source-Synchronous Interfaces, Altera Corporation, 2016*. 3 | 4 | ### Generated and Virtual Clocks 5 | The physical layer of the serial link needs to be constrained with generated slower clocks, which is achieved internally with a clock divider. The clock division factor is denoted as `FWD_CLK_DIV`, while the resulting clock period is `T_FWD_CLK` (which is equal to `T_CLK * FWD_CLK_DIV`). 6 | 7 | The data is synchronous to a zero phase shifted clock. This clock needs to be defined as virtual since it does not exist at the receiver side. The actual clock is shifted with respect to the virtual clock by -90 or +270 degrees (resp. shifted by +90 degrees and then inverted) for DDR, and by -180 or +180 degrees (resp. inverted) for SDR. The clocks on RX side are generated the following way (depending on the DDR or SDR mode): 8 | 9 | ```sdc 10 | # DDR 11 | # Rising edge is at 270 degree, falling edge at 450 (resp. 90) degrees 12 | set ddr_edge_list [list [expr $FWD_CLK_DIV / 4 * 3] [expr $FWD_CLK_DIV / 4 * 5]] 13 | create_clock -name vir_clk_ddr_in -period $T_FWD_CLK 14 | create_clock -name clk_ddr_in -period $T_FWD_CLK -waveform $ddr_edge_list [get_ports ddr_rcv_clk_i] 15 | ``` 16 | 17 | ```sdc 18 | # SDR 19 | # Rising edge is at 180 degree, falling edge at 360 (resp. 0) degrees. 20 | set sdr_edge_list [list [expr $FWD_CLK_DIV / 2] [expr $FWD_CLK_DIV]] 21 | create_clock -name vir_clk_sdr_in -period $T_FWD_CLK 22 | create_clock -name clk_sdr_in -period $T_FWD_CLK -waveform $sdr_edge_list [get_ports ddr_rcv_clk_i] 23 | ``` 24 | 25 | The clocks on TX side are generated the following way: 26 | 27 | ```sdc 28 | # DDR 29 | # The data launching clock with 0 degree clock phase 30 | create_generated_clock -name clk_slow -source clk_i -divide_by $FWD_CLK_DIV \ 31 | [get_pins -hierarchical clk_slow_reg/Q] 32 | 33 | # this is the "forwarded clock", we are assuming it is shifted by -90 or +270 degrees (or +90 degrees and inverted) 34 | set ddr_edge_list [list [expr 1 + $FWD_CLK_DIV / 2 * 3] [expr 1 + $FWD_CLK_DIV / 2 * 5] [expr 1 + $FWD_CLK_DIV / 2 * 7]] 35 | create_generated_clock -name clk_ddr_out -source clk_i -edges $ddr_edge_list \[get_pins -hierarchical ddr_rcv_clk_o_reg/Q] 36 | ``` 37 | 38 | ```sdc 39 | # SDR 40 | # The data launching clock with 0 degree clock phase 41 | create_generated_clock -name clk_slow -source clk_i -divide_by $FWD_CLK_DIV \ 42 | [get_pins -hierarchical clk_slow_reg/Q] 43 | 44 | # this is the "forwarded clock", we are assuming it is shifted by -180 or +180 degrees (or just inverted) 45 | set sdr_edge_list [list [expr 1 + $FWD_CLK_DIV / 2 * 2] [expr 1 + $FWD_CLK_DIV / 2 * 4] [expr 1 + $FWD_CLK_DIV / 2 * 6]] 46 | create_generated_clock -name clk_ddr_out -source clk_i -edges $sdr_edge_list \[get_pins -hierarchical ddr_rcv_clk_o_reg/Q] 47 | ``` 48 | 49 | ### False paths 50 | #### DDR False paths 51 | DDR has some timing arcs that needs to be removed. See an illustration below 52 | 53 | ![False paths](fig/constraint.svg "False paths") 54 | 55 | There is no conventional setup relationship (rise to rise and fall to fall). 56 | We leave only the inter-clock launching edge to capturing edge timing arcs (rise to fall and fall to rise) 57 | 58 | ```sdc 59 | # DDR 60 | # Input 61 | set_false_path -setup -rise_from [get_clocks vir_clk_ddr_in] -rise_to [get_clocks clk_ddr_in] 62 | set_false_path -setup -fall_from [get_clocks vir_clk_ddr_in] -fall_to [get_clocks clk_ddr_in] 63 | # Output 64 | set_false_path -setup -rise_from [get_clocks clk_slow] -rise_to [get_clocks clk_ddr_out] 65 | set_false_path -setup -fall_from [get_clocks clk_slow] -fall_to [get_clocks clk_ddr_out] 66 | ``` 67 | 68 | There is no actual hold relationship from non consecutive launching to capturing edges; data change at every edge, therefore we can remove the timing arcs that do not go from the current edge to the previous one (rise to fall and fall to rise). We leave only inter-clocks hold relationship (fall to fall and rise to rise) 69 | ```sdc 70 | # DDR 71 | # Input 72 | set_false_path -hold -rise_from [get_clocks vir_clk_ddr_in] -fall_to [get_clocks clk_ddr_in] 73 | set_false_path -hold -fall_from [get_clocks vir_clk_ddr_in] -rise_to [get_clocks clk_ddr_in] 74 | # Output 75 | set_false_path -hold -rise_from [get_clocks clk_slow] -fall_to [get_clocks clk_ddr_out] 76 | set_false_path -hold -fall_from [get_clocks clk_slow] -rise_to [get_clocks clk_ddr_out] 77 | ``` 78 | 79 | There is one last false path from the system/fll clock to the forwarded clock 80 | ```sdc 81 | set_false_path -from [get_pins my_system_clock_pin] -to [get_ports my_clk_ddr_out_port] 82 | ``` 83 | #### SDR False paths 84 | 85 | In SDR, only the timing relationship between rising edges is of concern. 86 | 87 | ```sdc 88 | # SDR 89 | # Input 90 | set_false_path -setup -rise_from [get_clocks vir_clk_sdr_in] -fall_to [get_clocks clk_sdr_in] 91 | set_false_path -setup -fall_from [get_clocks vir_clk_sdr_in] -rise_to [get_clocks clk_sdr_in] 92 | set_false_path -hold -rise_from [get_clocks vir_clk_sdr_in] -fall_to [get_clocks clk_sdr_in] 93 | set_false_path -hold -fall_from [get_clocks vir_clk_sdr_in] -rise_to [get_clocks clk_sdr_in] 94 | # Output 95 | set_false_path -setup -rise_from [get_clocks clk_slow] -fall_to [get_clocks clk_sdr_out] 96 | set_false_path -setup -fall_from [get_clocks clk_slow] -rise_to [get_clocks clk_sdr_out] 97 | set_false_path -hold -rise_from [get_clocks clk_slow] -fall_to [get_clocks clk_sdr_out] 98 | set_false_path -hold -fall_from [get_clocks clk_slow] -rise_to [get_clocks clk_sdr_out] 99 | ``` 100 | The false path from the system/fll clock to the forwarded clock remains: 101 | 102 | ```sdc 103 | set_false_path -from [get_pins my_system_clock_pin] -to [get_ports my_clk_ddr_out_port] 104 | ``` 105 | 106 | ### I/O Delays 107 | The input and ouput delay is constrained in a way that it arrives in between a window around the clock edge of the virtual (0 degree) clock for the input side resp. the forwarded clock (90 degree DDR, 180 degree SDR) for the output side. 108 | 109 | ```sdc 110 | # DDR 111 | # Window has a margin on both side of 5% of a quarter of the clock period 112 | set MARGIN [expr $T_FWD_CLK / 4 * 0.05] 113 | 114 | # Input delays 115 | set_input_delay -max -clock [get_clocks vir_clk_ddr_in] [expr $MARGIN] [get_ports ddr_i] 116 | set_input_delay -add_delay -min -clock [get_clocks vir_clk_ddr_in] [expr -$MARGIN] [get_ports ddr_i] 117 | set_input_delay -add_delay -max -clock_fall -clock [get_clocks vir_clk_ddr_in] [expr $MARGIN] [get_ports ddr_i] 118 | set_input_delay -add_delay -min -clock_fall -clock [get_clocks vir_clk_ddr_in] [expr -$MARGIN] [get_ports ddr_i] 119 | 120 | # Output delays 121 | set_output_delay -max -clock [get_clocks clk_ddr_out] [expr $T_FWD_CLK / 4 + $MARGIN] -reference_pin [get_ports ddr_rcv_clk_o] [get_ports ddr_o] 122 | set_output_delay -add_delay -min -clock [get_clocks clk_ddr_out] [expr $T_FWD_CLK / 4 - $MARGIN] -reference_pin [get_ports ddr_rcv_clk_o] [get_ports ddr_o] 123 | set_output_delay -add_delay -max -clock_fall -clock [get_clocks clk_ddr_out] [expr $T_FWD_CLK / 4 + $MARGIN] -reference_pin [get_ports ddr_rcv_clk_o] [get_ports ddr_o] 124 | set_output_delay -add_delay -min -clock_fall -clock [get_clocks clk_ddr_out] [expr $T_FWD_CLK / 4 - $MARGIN] -reference_pin [get_ports ddr_rcv_clk_o] [get_ports ddr_o] 125 | ``` 126 | 127 | ```sdc 128 | # SDR 129 | # Window has a margin on both side of 5% of half of the clock period 130 | set MARGIN [expr $T_FWD_CLK / 2 * 0.05] 131 | 132 | # Input delays 133 | set_input_delay -max -clock [get_clocks vir_clk_sdr_in] [expr $MARGIN] [get_ports ddr_i] 134 | set_input_delay -add_delay -min -clock [get_clocks vir_clk_sdr_in] [expr -$MARGIN] [get_ports ddr_i] 135 | set_input_delay -add_delay -max -clock_fall -clock [get_clocks vir_clk_sdr_in] [expr $MARGIN] [get_ports ddr_i] 136 | set_input_delay -add_delay -min -clock_fall -clock [get_clocks vir_clk_sdr_in] [expr -$MARGIN] [get_ports ddr_i] 137 | 138 | # Output delays 139 | set_output_delay -max -clock [get_clocks clk_sdr_out] [expr $T_FWD_CLK / 2 + $MARGIN] -reference_pin [get_ports ddr_rcv_clk_o] [get_ports ddr_o] 140 | set_output_delay -add_delay -min -clock [get_clocks clk_sdr_out] [expr $T_FWD_CLK / 2 - $MARGIN] -reference_pin [get_ports ddr_rcv_clk_o] [get_ports ddr_o] 141 | set_output_delay -add_delay -max -clock_fall -clock [get_clocks clk_sdr_out] [expr $T_FWD_CLK / 2 + $MARGIN] -reference_pin [get_ports ddr_rcv_clk_o] [get_ports ddr_o] 142 | set_output_delay -add_delay -min -clock_fall -clock [get_clocks clk_sdr_out] [expr $T_FWD_CLK / 2 - $MARGIN] -reference_pin [get_ports ddr_rcv_clk_o] [get_ports ddr_o] 143 | ``` 144 | 145 | 146 | ### Clock Domain Crossings 147 | Every RX channel has a [`cdc_fifo_gray`](https://github.com/pulp-platform/common_cells/blob/master/src/cdc_fifo_gray.sv) module of the `common_cells` libary which needs to be constrained. Readers are refered to the documention of this module. Further, there is a [`cdc_2phase`](https://github.com/pulp-platform/common_cells/blob/master/src/cdc_2phase.sv) module to synchronize the `REG_BUS` which is connected to the configuration registers running with the system clock. 148 | -------------------------------------------------------------------------------- /doc/data_link_layer.md: -------------------------------------------------------------------------------- 1 | ## Data Link Layer 2 | 3 | The Data Link layer doesn't play a big role in the Serial Link. It mainly acts as an adapter between the Network Layer and the Channel Allocator. In the network layer the AXI Stream payload is split into multiple channel-sized packets which can then be reshuffled in the *Channel Allocator* which is a separate module. Further it also implements a Raw-Mode which is described in more detail below. 4 | 5 | ### Raw Mode 6 | To calibrate and bring-up the Serial Link in the beginning, it is advantageous to be able to control channels independently and decouple the AXI interface. This feature is implemented in the Serial Link as the *Raw Mode* and it allows to detect if some of the channels have faults. The *Raw Mode* is fully SW controllable and can be configured throught the configuration registers which are mapped in to the configuration address space of the Serial Link. The logic includes a FIFO which can be filled with a sequence of calibration patterns which can be defined at runtime. An output mask can be configured to select the channels which should send out a the sequence of calibration patterns to the other side e.g. single-channel, multi-channel or broadcasting of patterns is possible. On the receiving side, each channel stores the sequence of patterns in a FIFO where it can be subsequently read out. -------------------------------------------------------------------------------- /doc/network_layer.md: -------------------------------------------------------------------------------- 1 | ## Network layer 2 | The Network layer has a duplex (Master/Slave) AXI4 interface and translates AXI4 transactions to AXI4-Streams and vice-versa. On the input side, it implements an arbiter to select an AXI4 beat (AW, W, B, AR, R) which is then forward to the Data Link Layer. The arbitration is implemented in a way that prevents deadlock caused by the AXI4 to AXI4-Stream protocol conversion. A block diagram is shown below 3 | 4 | ![Block-diagram of the Network Layer](fig/network_layer.svg "Block-diagram of the Network Layer") 5 | 6 | ### Packeting 7 | The payload that is sent to the data link layer contains the AXI beats (AW, W, B, AR, R), a header to encode type of AXI beat and credits for the flow control. Since B responses are very small, they are packeted together with one of the other AXI channels to prevent BW degredation. 8 | 9 | ### Arbitration 10 | The arbitration of AXI requests and respones have the following priorities: 11 | 1. B responses are always granted as they can be sent along other req/rsp 12 | 2. AR/AW beats have priority, but only one AR/AW can be in flight each. Otherwise deadlocks can occur if the AR/AW beats have consumed all the credits but the transaction has not been terminated. 13 | 3. R/W beats have lowest priority 14 | 15 | Further, W requests are not granted before the corresponding AW request. Arbitration between AR/AW and R/W beats are implemented as psuedo Round-Robin to prevent starvation of one channel. 16 | 17 | ### Flow control 18 | Each time a new payload is granted, a credit counter is decremented. This allows to generate back-pressure to the AXI interface. Otherwise the FIFOs on the receiving side can potentially overflow. The credit counters are incremented once the receiving side sends back the credits together with a request/response. There is a mechanism to force the network layer to send out packets with an empty payload but with credits. This prevents deadlock situations where the communication is very onsided e.g. long write bursts. Technically, flow-control would be part of the Data Link layer. However, the implementation is simpler in the `serial_link_network` module since there are simple handshaking interfaces. -------------------------------------------------------------------------------- /doc/physical_layer.md: -------------------------------------------------------------------------------- 1 | ## Physical Layer 2 | 3 | The Physical Layer of the Serial Link has multiple channels where each channel has a configurable number of lanes and its own source-synchronous clock. The physical protocol is essentially based on oversampling which is achieved by sending the data with a generated divided clock. The data is sent with either Double-Data-Rate (DDR) or Single-Data-Rate (SDR). 4 | 5 | ### Protocol 6 | The physical protocol of the Serial Link is shown below 7 | 8 | ![Physical Protocol of the Serial Link](fig/phy-protocol.svg "Physical Protocol of the Serial Link") 9 | 10 | The data is sent synchronous to a generated divided clock, which is generated by a simple and SW configurable clock divider. Further, a clock is forwarded together with the data to sample the data on the receiving side. This forwarded source-synchronous clock typically has a phase shift of 90 or 180 degrees, for DDR or SDR, respectively, such that sampling occurs when the eye opening is at a maximum. The exact phase shift is configurable i.e. the negative and positive edge of the forwarded clock can be configured independently by SW. 11 | 12 | ### TX Channel 13 | The implementation of a TX channel is pretty simple. It contains a clock a configurable clock divider for generating the divided data and the forwarded clock. In case of DDR the TX channel just multiplexes the output based on the data clock. To signal when actual data is being sent the TX channel clock-gates the forwarded clock i.e. in idle operating mode the forwarded clock is tied to `'1`. 14 | 15 | ### RX Channel 16 | On the RX side a CDC FIFO takes care of synchronizing the data to the system clock. DDR is handled by sampling the first part of data on negedge-triggered FF and sampling the second part directly with the CDC Fifo which is positive-edge triggered, while for SDR sampling happens directly in the positive-edge triggered CDC Fifo. -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | hjson 2 | mistletoe 3 | mako 4 | pyyaml 5 | tabulate -------------------------------------------------------------------------------- /src/axis/src/axis_intf.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2022 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Author: Noah Huetter 6 | 7 | /// An AXI4 stream interface. 8 | interface AXIS_BUS #( 9 | parameter int unsigned AXIS_DATA_WIDTH = 0, 10 | parameter int unsigned AXIS_ID_WIDTH = 0, 11 | parameter int unsigned AXIS_DEST_WIDTH = 0, 12 | parameter int unsigned AXIS_USER_WIDTH = 0, 13 | parameter int unsigned AXIS_READY_WIDTH = 0, 14 | parameter int unsigned AXIS_LAST_WIDTH = 0 15 | ); 16 | localparam int unsigned AXIS_STRB_WIDTH = AXIS_DATA_WIDTH / 8; 17 | localparam int unsigned AXIS_KEEP_WIDTH = AXIS_DATA_WIDTH / 8; 18 | 19 | typedef logic [AXIS_DATA_WIDTH-1:0] tdata_t; 20 | typedef logic [AXIS_STRB_WIDTH-1:0] tstrb_t; 21 | typedef logic [AXIS_KEEP_WIDTH-1:0] tkeep_t; 22 | typedef logic [AXIS_ID_WIDTH-1:0] tid_t; 23 | typedef logic [AXIS_DEST_WIDTH-1:0] tdest_t; 24 | typedef logic [AXIS_USER_WIDTH-1:0] tuser_t; 25 | typedef logic [AXIS_READY_WIDTH-1:0] tready_t; 26 | typedef logic [AXIS_LAST_WIDTH-1:0] tlast_t; 27 | 28 | // Signal list 29 | logic tvalid; 30 | tready_t tready; 31 | tdata_t tdata; 32 | tstrb_t tstrb; 33 | tkeep_t tkeep; 34 | tlast_t tlast; 35 | tid_t tid; 36 | tdest_t tdest; 37 | tuser_t tuser; 38 | 39 | // Module ports 40 | modport Master(output tvalid, tdata, tstrb, tkeep, tlast, tid, tdest, tuser, input tready); 41 | modport Slave(input tvalid, tdata, tstrb, tkeep, tlast, tid, tdest, tuser, output tready); 42 | 43 | endinterface 44 | 45 | /// A clocked AXI4 stream interface for use in design verification. 46 | interface AXIS_BUS_DV #( 47 | parameter int unsigned AXIS_DATA_WIDTH = 0, 48 | parameter int unsigned AXIS_ID_WIDTH = 0, 49 | parameter int unsigned AXIS_DEST_WIDTH = 0, 50 | parameter int unsigned AXIS_USER_WIDTH = 0, 51 | parameter int unsigned AXIS_READY_WIDTH = 0, 52 | parameter int unsigned AXIS_LAST_WIDTH = 0 53 | ) ( 54 | input logic clk_i 55 | ); 56 | localparam int unsigned AXIS_STRB_WIDTH = AXIS_DATA_WIDTH / 8; 57 | localparam int unsigned AXIS_KEEP_WIDTH = AXIS_DATA_WIDTH / 8; 58 | 59 | typedef logic [AXIS_DATA_WIDTH-1:0] tdata_t; 60 | typedef logic [AXIS_STRB_WIDTH-1:0] tstrb_t; 61 | typedef logic [AXIS_KEEP_WIDTH-1:0] tkeep_t; 62 | typedef logic [AXIS_ID_WIDTH-1:0] tid_t; 63 | typedef logic [AXIS_DEST_WIDTH-1:0] tdest_t; 64 | typedef logic [AXIS_USER_WIDTH-1:0] tuser_t; 65 | typedef logic [AXIS_READY_WIDTH-1:0] tready_t; 66 | typedef logic [AXIS_LAST_WIDTH-1:0] tlast_t; 67 | 68 | // Signal list 69 | logic tvalid; 70 | tready_t tready; 71 | tdata_t tdata; 72 | tstrb_t tstrb; 73 | tkeep_t tkeep; 74 | tlast_t tlast; 75 | tid_t tid; 76 | tdest_t tdest; 77 | tuser_t tuser; 78 | 79 | // Module ports 80 | modport Master(output tvalid, tdata, tstrb, tkeep, tlast, tid, tdest, tuser, input tready); 81 | modport Slave(input tvalid, tdata, tstrb, tkeep, tlast, tid, tdest, tuser, output tready); 82 | modport Monitor(input tvalid, tready, tdata, tstrb, tkeep, tlast, tid, tdest, tuser); 83 | 84 | // pragma translate_off 85 | `ifndef VERILATOR 86 | // Single-Channel Assertions: Signals including valid must not change between valid and handshake. 87 | assert property (@(posedge clk_i) (tvalid && !tready |=> $stable(tdata))); 88 | assert property (@(posedge clk_i) (tvalid && !tready |=> $stable(tstrb))); 89 | assert property (@(posedge clk_i) (tvalid && !tready |=> $stable(tkeep))); 90 | assert property (@(posedge clk_i) (tvalid && !tready |=> $stable(tlast))); 91 | assert property (@(posedge clk_i) (tvalid && !tready |=> $stable(tid))); 92 | assert property (@(posedge clk_i) (tvalid && !tready |=> $stable(tdest))); 93 | assert property (@(posedge clk_i) (tvalid && !tready |=> $stable(tuser))); 94 | assert property (@(posedge clk_i) (tvalid && !tready |=> tvalid)); 95 | `endif 96 | // pragma translate_on 97 | 98 | endinterface 99 | -------------------------------------------------------------------------------- /src/axis/src/axis_test.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2022 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Author: Noah Huetter 6 | 7 | /// A set of testbench utilities for AXI Stream interfaces. 8 | package axis_test; 9 | 10 | // import axis_pkg::*; 11 | 12 | /// A driver for AXI4-Stream interface. 13 | class axis_driver #( 14 | parameter int unsigned DW = 0, 15 | parameter int unsigned IW = 0, 16 | parameter int unsigned DESTW = 0, 17 | parameter int unsigned UW = 0, 18 | parameter int unsigned RW = 0, 19 | parameter int unsigned LW = 0, 20 | parameter time TA = 0ns, // stimuli application time 21 | parameter time TT = 0ns // stimuli test time 22 | ); 23 | virtual AXIS_BUS_DV #( 24 | .AXIS_DATA_WIDTH(DW), 25 | .AXIS_ID_WIDTH(IW), 26 | .AXIS_DEST_WIDTH(DESTW), 27 | .AXIS_USER_WIDTH(UW), 28 | .AXIS_READY_WIDTH(RW), 29 | .AXIS_LAST_WIDTH(LW) 30 | ) axis; 31 | 32 | function new(virtual AXIS_BUS_DV #( 33 | .AXIS_DATA_WIDTH(DW), 34 | .AXIS_ID_WIDTH(IW), 35 | .AXIS_DEST_WIDTH(DESTW), 36 | .AXIS_USER_WIDTH(UW), 37 | .AXIS_READY_WIDTH(RW), 38 | .AXIS_LAST_WIDTH(LW) 39 | ) axis); 40 | this.axis = axis; 41 | endfunction 42 | 43 | function void reset_master(); 44 | axis.tvalid <= '0; 45 | axis.tdata <= '0; 46 | axis.tstrb <= '0; 47 | axis.tkeep <= '0; 48 | axis.tlast <= '0; 49 | axis.tid <= '0; 50 | axis.tdest <= '0; 51 | axis.tuser <= '0; 52 | endfunction 53 | 54 | function void reset_slave(); 55 | axis.tready <= '0; 56 | endfunction 57 | 58 | task cycle_start; 59 | #TT; 60 | endtask 61 | 62 | task cycle_end; 63 | @(posedge axis.clk_i); 64 | endtask 65 | 66 | /// Issue a beat 67 | task send(input logic [DW-1:0] data, input logic [LW-1:0] last); 68 | axis.tdata <= #TA data; 69 | axis.tstrb <= '0; 70 | axis.tkeep <= '0; 71 | axis.tlast <= #TA last; 72 | axis.tid <= '0; 73 | axis.tdest <= '0; 74 | axis.tuser <= '0; 75 | axis.tvalid <= #TA 1; 76 | cycle_start(); 77 | while (axis.tready != 1) begin 78 | cycle_end(); 79 | cycle_start(); 80 | end 81 | cycle_end(); 82 | axis.tdata <= #TA '0; 83 | axis.tlast <= #TA '0; 84 | axis.tvalid <= #TA 0; 85 | endtask 86 | 87 | /// Wait for a beat 88 | task recv(output [DW-1:0] data, output logic [LW-1:0] last); 89 | axis.tready <= #TA 1; 90 | cycle_start(); 91 | while (axis.tvalid != 1) begin 92 | cycle_end(); 93 | cycle_start(); 94 | end 95 | data = axis.tdata; 96 | last = axis.tlast; 97 | cycle_end(); 98 | axis.tready <= #TA 0; 99 | endtask 100 | 101 | endclass 102 | 103 | /// The data transferred on a beat 104 | class axis_beat #( 105 | parameter DW = 32, 106 | parameter IW = 0, 107 | parameter DESTW = 0, 108 | parameter UW = 0, 109 | parameter RW = 0, 110 | parameter LW = 1 111 | ); 112 | logic [DW-1:0] tdata = '0; 113 | logic [DW/8-1:0] tstrb = '0; 114 | logic [DW/8-1:0] tkeep = '0; 115 | logic [LW-1:0] tlast = '0; 116 | logic [IW-1:0] tid = '0; 117 | logic [DESTW-1:0] tdest = '0; 118 | logic [UW-1:0] tuser = '0; 119 | endclass 120 | 121 | 122 | class axis_rand_slave #( 123 | // AXI interface parameters 124 | parameter int unsigned DW = 0, 125 | parameter int unsigned IW = 0, 126 | parameter int unsigned DESTW = 0, 127 | parameter int unsigned UW = 0, 128 | parameter int unsigned RW = 0, 129 | parameter int unsigned LW = 0, 130 | // Stimuli application and test time 131 | parameter time TA = 2ns, 132 | parameter time TT = 8ns, 133 | // Upper and lower bounds on wait cycles 134 | parameter int MIN_WAIT_CYCLES = 0, 135 | parameter int MAX_WAIT_CYCLES = 100 136 | ); 137 | typedef axis_test::axis_driver#( 138 | .DW(DW), 139 | .IW(IW), 140 | .DESTW(DESTW), 141 | .UW(UW), 142 | .RW(RW), 143 | .LW(LW) 144 | ) axis_driver_t; 145 | 146 | typedef logic [DW-1:0] data_t; 147 | typedef logic [LW-1:0] last_t; 148 | typedef logic [DW/8-1:0] strb_t; 149 | 150 | string name; 151 | axis_driver_t drv; 152 | data_t recv_queue[$]; 153 | 154 | function new( 155 | virtual AXIS_BUS_DV #( 156 | .AXIS_DATA_WIDTH(DW), 157 | .AXIS_ID_WIDTH(IW), 158 | .AXIS_DEST_WIDTH(DESTW), 159 | .AXIS_USER_WIDTH(UW), 160 | .AXIS_READY_WIDTH(RW), 161 | .AXIS_LAST_WIDTH(LW) 162 | ) axis, 163 | input string name); 164 | this.drv = new(axis); 165 | this.name = name; 166 | assert (DW != 0) 167 | else $fatal(1, "Data width must be non-zero!"); 168 | endfunction 169 | 170 | function void reset(); 171 | this.drv.reset_slave(); 172 | endfunction 173 | 174 | task automatic rand_wait(input int unsigned min, max); 175 | int unsigned rand_success, cycles; 176 | rand_success = std::randomize( 177 | cycles 178 | ) with { 179 | cycles >= min; 180 | cycles <= max; 181 | }; 182 | assert (rand_success) 183 | else $error("Failed to randomize wait cycles!"); 184 | repeat (cycles) @(posedge this.drv.axis.clk_i); 185 | endtask 186 | 187 | task automatic recv(); 188 | forever begin 189 | automatic data_t data; 190 | automatic last_t last; 191 | rand_wait(MIN_WAIT_CYCLES, MAX_WAIT_CYCLES); 192 | this.drv.recv(data, last); 193 | $display("%0t %s> Recv AR with DATA: %h LAST: %b", $time(), this.name, data, last); 194 | this.recv_queue.push_back(data); 195 | end 196 | endtask : recv 197 | 198 | task automatic run(); 199 | fork 200 | recv(); 201 | join 202 | endtask 203 | endclass 204 | 205 | 206 | endpackage 207 | -------------------------------------------------------------------------------- /src/channel_allocator/channel_despread_sfr.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2022 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Author: Manuel Eggimann 6 | 7 | 8 | /// This module receives input words of which K out of N subelements are valid. 9 | /// The despread shift register compacts the input word such that the first K out 10 | /// of N subelements in the output word are valid (shifts all subwords to the 11 | /// right until there are no more holes of invalid data within the valid data 12 | /// section). 13 | module channel_despread_sfr #( 14 | parameter type element_t = logic[15:0], 15 | parameter int unsigned Width = -1, 16 | localparam int Log2Width = $clog2(Width) 17 | )( 18 | input logic clk_i, 19 | input logic rst_ni, 20 | input logic clear_i, 21 | input logic [Width-1:0] valid_i, 22 | output logic ready_o, 23 | input element_t [Width-1:0] data_i, 24 | output logic [Width-1:0] valid_o, 25 | input logic ready_i, 26 | output element_t [Width-1:0] data_o 27 | ); 28 | 29 | typedef enum logic[1:0] {IDLE, DESPREADING, WAIT_READY} state_e; 30 | 31 | state_e state_d, state_q; 32 | 33 | element_t [Width-1:0] out_buffer_d, out_buffer_q; 34 | logic [Width-1:0] element_valid_buffer_d, element_valid_buffer_q; 35 | logic s_load_en; 36 | logic s_shift_en; 37 | 38 | logic [Width-1:0] s_shift_mask; 39 | // This register stores the target valid bit pattern, i.e. a bitmask where the 40 | // first k out Width bits are asserted with k = popcount(valid_i during last 41 | // handshake). 42 | 43 | logic [Log2Width:0] s_valid_pocount; 44 | logic [Width-1:0] despreaded_mask_d, despreaded_mask_q; 45 | 46 | // logic for despread mask 47 | always_comb begin 48 | s_valid_pocount = '0; 49 | despreaded_mask_d = despreaded_mask_q; 50 | foreach(valid_i[i]) begin 51 | s_valid_pocount += valid_i[i]; 52 | end 53 | if (s_load_en) begin 54 | // This generates the required bit pattern (popcount(valid_i) LSBs set) 55 | despreaded_mask_d = (1< 6 | 7 | /// The stream chopper module chops a stream of elements into a new 8 | /// stream with at-runtime configurable < Width. The output stream will 9 | /// only have the first elements valid. This module is required as a 10 | /// building block for the channel allocator ip within the Serial Link. This 11 | /// first implementation uses simple but area hungry barrel shifter to perform 12 | /// the chopping in at most 2 cycles. The module has an auto-flush feature: If 13 | /// enabled, the stream_chopper will wait at most cfg_auto_flush_count clock 14 | /// cycles before starting a flush operation with a partial (less than 15 | /// cfg_chopsize_i) valid elements. This feature prefents lookup if there are 16 | /// only sporadic transmissions in the input stream where we could end up waiting 17 | /// forever for input stream to send another word to fill the next output word 18 | /// (i.e. *%cfg_chopsize_i != 0). 19 | module stream_chopper #( 20 | parameter type element_t = logic[15:0], 21 | parameter int signed Width = -1, 22 | parameter int unsigned FlushCounterWidth = 6, 23 | localparam int Log2Width = $clog2(Width) 24 | )( 25 | input logic clk_i, 26 | input logic rst_ni, 27 | input logic clear_i, 28 | input logic bypass_en_i, 29 | input logic flush_i, 30 | input logic cfg_auto_flush_en_i, 31 | input logic [FlushCounterWidth-1:0] cfg_auto_flush_count_i, 32 | input logic [Log2Width-1:0] cfg_chopsize_i, 33 | input element_t[Width-1:0] data_i, 34 | input logic valid_i, 35 | output logic ready_o, 36 | output element_t[Width-1:0] data_o, 37 | output logic [Width-1:0] valid_o, 38 | input logic ready_i 39 | ); 40 | 41 | logic [Log2Width-1:0] out_buffer_fill_count_d, out_buffer_fill_count_q; 42 | logic [Log2Width-1:0] input_consumed_count_d, input_consumed_count_q; 43 | element_t [Width-1:0] s_shifted_input; 44 | 45 | logic [Width-1:0] s_out_buffer_en; 46 | logic s_out_buffer_clear; 47 | element_t [Width-1:0] out_buffer_d, out_buffer_q; 48 | logic s_fetch_next_word; 49 | 50 | logic [Width-1:0] output_valid_d, output_valid_q; 51 | logic [Log2Width-1:0] s_input_consumed_increase; 52 | logic signed [Log2Width:0] s_shift_amount; 53 | 54 | logic [FlushCounterWidth-1:0] auto_flush_counter_d, auto_flush_counter_q; 55 | 56 | logic s_stall; 57 | 58 | assign s_shift_amount = out_buffer_fill_count_q - input_consumed_count_q; 59 | 60 | assign s_out_buffer_clear = output_valid_q; 61 | 62 | 63 | // Input Barrel Shifter 64 | always_comb begin 65 | s_shifted_input = data_i; 66 | foreach(data_i[i]) begin 67 | if (i+s_shift_amount >= Width) begin 68 | s_shifted_input[i+s_shift_amount-Width] = data_i[i]; 69 | end else if (i+s_shift_amount < 0) begin 70 | s_shifted_input[i+s_shift_amount+Width] = data_i[i]; 71 | end else begin 72 | s_shifted_input[i+s_shift_amount] = data_i[i]; 73 | end 74 | end 75 | end 76 | 77 | // Output Register Logic 78 | always_comb begin 79 | out_buffer_d = out_buffer_q; 80 | if (clear_i) begin 81 | out_buffer_d = '0; 82 | end else begin 83 | for (int i = 0; i < Width; i++) begin 84 | if (s_out_buffer_en[i]) begin 85 | out_buffer_d[i] = s_shifted_input[i]; 86 | end else if (s_out_buffer_clear) begin 87 | out_buffer_d[i] = '0; 88 | end 89 | end 90 | end 91 | end 92 | 93 | always_comb begin 94 | s_out_buffer_en = '0; 95 | out_buffer_fill_count_d = out_buffer_fill_count_q; 96 | input_consumed_count_d = input_consumed_count_q; 97 | s_input_consumed_increase = '0; 98 | s_fetch_next_word = 1'b0; 99 | output_valid_d = '0; 100 | auto_flush_counter_d = '0; 101 | 102 | if (clear_i) begin 103 | out_buffer_fill_count_d = '0; 104 | input_consumed_count_d = '0; 105 | output_valid_d = '0; 106 | auto_flush_counter_d = '0; 107 | end else begin 108 | if (!bypass_en_i) begin 109 | // First check if the input is valid 110 | if (valid_i) begin 111 | // Now we consume min(cfg_chopsize_i - 112 | // out_buffer_fill_count_q, Width - input_consumed_count_q) from the input. I.e. we 113 | // consume either as many elements as we need to assemble the next 114 | // output word or as many as are left in the current input word and 115 | // store it in the output buffer. The barrel shifter rotates the input 116 | // to the right location. We just need to assert the right enable 117 | // signals of the output buffer. 118 | s_input_consumed_increase = 119 | ((cfg_chopsize_i-out_buffer_fill_count_q) < (Width-input_consumed_count_q))? 120 | cfg_chopsize_i-out_buffer_fill_count_q : (Width-input_consumed_count_q); 121 | for (int i = 0; i < Width; i++) begin 122 | if (i >= out_buffer_fill_count_q && 123 | i < out_buffer_fill_count_q+s_input_consumed_increase) begin 124 | s_out_buffer_en[i] = 1'b1; 125 | end 126 | end 127 | // Also we have to update the counters: 128 | 129 | // If the out_buffer fill counter overflows, we have a complete word to 130 | // send downstream. 131 | if (out_buffer_fill_count_q + s_input_consumed_increase == cfg_chopsize_i) begin 132 | out_buffer_fill_count_d = '0; 133 | for (int i = 0; i < Width; i++) begin 134 | if (i < cfg_chopsize_i) begin 135 | output_valid_d[i] = 1'b1; 136 | end 137 | end 138 | end else begin 139 | out_buffer_fill_count_d = out_buffer_fill_count_q + s_input_consumed_increase; 140 | end 141 | 142 | // If the input was fully consumed, fetch the next input word 143 | if (input_consumed_count_q + s_input_consumed_increase == Width) begin 144 | input_consumed_count_d = '0; 145 | s_fetch_next_word = 1'b1; 146 | end else begin 147 | input_consumed_count_d = input_consumed_count_q + s_input_consumed_increase; 148 | end 149 | 150 | // check if the auto-flush feature is enabled and we have a partially 151 | // filled output buffer 152 | end else if(out_buffer_fill_count_q > 0 && (cfg_auto_flush_en_i | flush_i )) begin 153 | // If it is, check if the counter is expired 154 | if (flush_i | auto_flush_counter_q == cfg_auto_flush_count_i) begin 155 | // Flush the module by sending a partial output word and resetting the counters 156 | auto_flush_counter_d = '0; 157 | for (int i = 0; i < Width; i++) begin 158 | if (i < out_buffer_fill_count_q) begin 159 | output_valid_d[i] = 1'b1; 160 | end 161 | end 162 | input_consumed_count_d = '0; 163 | out_buffer_fill_count_d = '0; 164 | end else begin 165 | // If it is not, just count upwards 166 | auto_flush_counter_d = auto_flush_counter_q+1; 167 | end 168 | end 169 | end 170 | end 171 | end 172 | 173 | // If we try to send an output word downstream while downstream is not ready, 174 | // stall the whole circuit 175 | 176 | assign s_stall = output_valid_q & !ready_i; 177 | 178 | always_ff @(posedge clk_i, negedge rst_ni) begin 179 | if (!rst_ni) begin 180 | out_buffer_q <= '0; 181 | input_consumed_count_q <= '0; 182 | out_buffer_fill_count_q <= '0; 183 | output_valid_q <= '0; 184 | auto_flush_counter_q <= '0; 185 | end else begin 186 | if (!s_stall) begin 187 | out_buffer_q <= out_buffer_d; 188 | input_consumed_count_q <= input_consumed_count_d; 189 | out_buffer_fill_count_q <= out_buffer_fill_count_d; 190 | output_valid_q <= output_valid_d; 191 | auto_flush_counter_q <= auto_flush_counter_d; 192 | end 193 | end 194 | end 195 | 196 | //-------------------- Output Assignments -------------------- 197 | // always_comb begin 198 | // if (bypass_en_i) begin 199 | // data_o = data_i; 200 | // end else begin 201 | // // Mirror the output 202 | // for (int i = 0; i < Width; i++) begin 203 | // data_o[i] = out_buffer_q[Width-i-1]; 204 | // end 205 | // end 206 | // end 207 | 208 | assign data_o = bypass_en_i? data_i : out_buffer_q; 209 | always_comb begin 210 | if (bypass_en_i) begin 211 | if (valid_i) begin 212 | valid_o = '1; 213 | end else begin 214 | valid_o = '0; 215 | end 216 | end else begin 217 | valid_o = output_valid_q; 218 | end 219 | end 220 | assign ready_o = bypass_en_i? ready_i: s_fetch_next_word && !s_stall; 221 | 222 | endmodule 223 | -------------------------------------------------------------------------------- /src/channel_allocator/stream_dechopper.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2022 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Author: Manuel Eggimann 6 | 7 | `ifndef SERIAL_LINK_MIN_EXPR 8 | `define SERIAL_LINK_MIN_EXPR(a,b) (((a)<(b))?(a):(b)) 9 | `endif 10 | 11 | /// This module receives a stream of width elements where the first 12 | /// (starting from LSB) k elements, with 0 <= k < Width are valid and 13 | /// reassemebles them into a full width output stream. This module is the counter 14 | /// part of the stream_chopper module. 15 | module stream_dechopper #( 16 | parameter type element_t = logic [15:0], 17 | parameter int signed Width = -1, 18 | parameter int unsigned FlushCounterWidth = 6, 19 | localparam int unsigned Log2Width = $clog2(Width) 20 | )( 21 | input logic clk_i, 22 | input logic rst_ni, 23 | input logic clear_i, 24 | input logic bypass_en_i, 25 | input logic [Width-1:0] valid_i, 26 | output logic ready_o, 27 | input element_t [Width-1:0] data_i, 28 | output logic valid_o, 29 | input logic ready_i, 30 | output element_t [Width-1:0] data_o 31 | ); 32 | 33 | logic [Log2Width-1:0] s_chopsize; 34 | 35 | logic [Log2Width-1:0] out_buffer_fill_count_d, out_buffer_fill_count_q; 36 | logic [Log2Width-1:0] input_consumed_count_d, input_consumed_count_q; 37 | element_t [Width-1:0] s_shifted_input; 38 | 39 | logic [Width-1:0] s_out_buffer_en; 40 | logic s_out_buffer_clear; 41 | element_t [Width-1:0] out_buffer_d, out_buffer_q; 42 | logic s_fetch_next_word; 43 | 44 | logic output_valid_d, output_valid_q; 45 | logic [Log2Width-1:0] s_input_consumed_increase; 46 | logic signed [Log2Width:0] s_shift_amount; 47 | 48 | logic s_stall; 49 | 50 | logic s_bypass_en; 51 | logic s_all_valid; 52 | 53 | assign s_shift_amount = out_buffer_fill_count_q - input_consumed_count_q; 54 | 55 | assign s_out_buffer_clear = output_valid_q; 56 | 57 | // Count trailing 1s 58 | lzc #( 59 | .WIDTH(Width), 60 | .MODE(0) 61 | ) i_lzc( 62 | .in_i(~valid_i), 63 | .cnt_o(s_chopsize), 64 | .empty_o(s_all_valid) 65 | ); 66 | 67 | // If the lzc counter recognizes that all elements are valid, automaticaly 68 | // assert the bypass signals since no dechoping is necessary. 69 | assign s_bypass_en = s_all_valid || bypass_en_i; 70 | 71 | // Input Barrel Shifter 72 | always_comb begin 73 | s_shifted_input = data_i; 74 | for (int signed i = 0; i < Width; i++) begin 75 | if (i+s_shift_amount >= Width) begin 76 | s_shifted_input[i+s_shift_amount-Width] = data_i[i]; 77 | end else if (i+s_shift_amount < 0) begin 78 | s_shifted_input[i+s_shift_amount+Width] = data_i[i]; 79 | end else begin 80 | s_shifted_input[i+s_shift_amount] = data_i[i]; 81 | end 82 | end 83 | end 84 | 85 | // Output Register Logic 86 | always_comb begin 87 | out_buffer_d = out_buffer_q; 88 | if (clear_i) begin 89 | out_buffer_d = '0; 90 | end else begin 91 | for (int i = 0; i < Width; i++) begin 92 | if (s_out_buffer_en[i]) begin 93 | out_buffer_d[i] = s_shifted_input[i]; 94 | end else if (s_out_buffer_clear) begin 95 | out_buffer_d[i] = '0; 96 | end 97 | end 98 | end 99 | end 100 | 101 | always_comb begin 102 | s_out_buffer_en = '0; 103 | out_buffer_fill_count_d = out_buffer_fill_count_q; 104 | input_consumed_count_d = input_consumed_count_q; 105 | s_input_consumed_increase = '0; 106 | s_fetch_next_word = 1'b0; 107 | output_valid_d = 1'b0; 108 | 109 | if (clear_i) begin 110 | out_buffer_fill_count_d = '0; 111 | input_consumed_count_d = '0; 112 | output_valid_d = '0; 113 | end else begin 114 | if (!s_bypass_en) begin 115 | // First check if the input is valid 116 | if (valid_i) begin 117 | // Now we consume min(Width - 118 | // out_buffer_fill_count_q, s_chopsize - input_consumed_count_q) from the input. I.e. we 119 | // consume either as many elements as we need to assemble the next 120 | // output word or as many as are left in the current input word and 121 | // store it in the output buffer. The barrel shifter rotates the input 122 | // to the right location. We just need to assert the right enable 123 | // signals of the output buffer. 124 | s_input_consumed_increase = `SERIAL_LINK_MIN_EXPR(Width-out_buffer_fill_count_q, 125 | s_chopsize-input_consumed_count_q); 126 | for (int i = 0; i < Width; i++) begin 127 | if (i >= out_buffer_fill_count_q 128 | && i < out_buffer_fill_count_q+s_input_consumed_increase) begin 129 | s_out_buffer_en[i] = 1'b1; 130 | end 131 | end 132 | // Also we have to update the counters: 133 | 134 | // If the out_buffer fill counter overflows, we have a complete word to 135 | // send downstream. 136 | if (out_buffer_fill_count_q + s_input_consumed_increase == Width) begin 137 | out_buffer_fill_count_d = '0; 138 | output_valid_d = 1'b1; 139 | end else begin 140 | out_buffer_fill_count_d = out_buffer_fill_count_q + s_input_consumed_increase; 141 | end 142 | 143 | // If the input was fully consumed, fetch the next input word 144 | if (input_consumed_count_q + s_input_consumed_increase == s_chopsize) begin 145 | input_consumed_count_d = '0; 146 | s_fetch_next_word = 1'b1; 147 | end else begin 148 | input_consumed_count_d = input_consumed_count_q + s_input_consumed_increase; 149 | end 150 | end 151 | end 152 | end 153 | end 154 | 155 | // If we try to send an output word downstream while downstream is not ready, 156 | // stall the whole circuit 157 | 158 | assign s_stall = output_valid_q & !ready_i; 159 | 160 | always_ff @(posedge clk_i, negedge rst_ni) begin 161 | if (!rst_ni) begin 162 | out_buffer_q <= '0; 163 | input_consumed_count_q <= '0; 164 | out_buffer_fill_count_q <= '0; 165 | output_valid_q <= 1'b0; 166 | end else begin 167 | if (!s_stall) begin 168 | out_buffer_q <= out_buffer_d; 169 | input_consumed_count_q <= input_consumed_count_d; 170 | out_buffer_fill_count_q <= out_buffer_fill_count_d; 171 | output_valid_q <= output_valid_d; 172 | end 173 | end 174 | end 175 | 176 | assign data_o = s_bypass_en? data_i : out_buffer_q; 177 | always_comb begin 178 | if (s_bypass_en) begin 179 | valid_o = valid_i; 180 | end else begin 181 | for (int i = 0; i < Width; i++) begin 182 | valid_o = output_valid_q; 183 | end 184 | end 185 | end 186 | assign ready_o = s_bypass_en? ready_i: s_fetch_next_word && !s_stall; 187 | 188 | endmodule 189 | -------------------------------------------------------------------------------- /src/channel_allocator/stream_dechopper.sv.bak: -------------------------------------------------------------------------------- 1 | // Copyright 2022 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Author: Manuel Eggimann 6 | 7 | `ifndef SERIAL_LINK_MIN_EXPR 8 | `define SERIAL_LINK_MIN_EXPR(a,b) (((a)<(b))?(a):(b)) 9 | `endif 10 | 11 | /// This module receives a stream of width elements where the first 12 | /// (starting from LSB) k elements, with 0 <= k < Width are valid and 13 | /// reassemebles them into a full width output stream. This module is the counter 14 | /// part of the stream_chopper module. 15 | module stream_dechopper #( 16 | parameter type element_t = logic [15:0], 17 | parameter int unsigned Width = -1, 18 | parameter int unsigned FlushCounterWidth = 6, 19 | localparam Log2Width = $clog2(Width) 20 | )( 21 | input logic clk_i, 22 | input logic rst_ni, 23 | input logic bypass_en_i, 24 | input logic [Width-1:0] valid_i, 25 | output logic ready_o, 26 | input element_t [Width-1:0] data_i, 27 | output logic valid_o, 28 | input logic ready_i, 29 | output element_t [Width-1:0] data_o 30 | ); 31 | 32 | logic [Log2Width-1:0] s_chopsize; 33 | 34 | logic [Log2Width-1:0] out_buffer_fill_count_d, out_buffer_fill_count_q; 35 | logic [Log2Width-1:0] input_consumed_count_d, input_consumed_count_q; 36 | element_t [Width-1:0] s_shifted_input; 37 | 38 | logic [Width-1:0] s_out_buffer_en; 39 | logic s_out_buffer_clear; 40 | element_t [Width-1:0] out_buffer_d, out_buffer_q; 41 | logic s_fetch_next_word; 42 | 43 | logic output_valid_d, output_valid_q; 44 | logic [Log2Width-1:0] s_input_consumed_increase; 45 | logic signed [Log2Width-1:0] s_shift_amount; 46 | 47 | logic s_stall; 48 | 49 | assign s_shift_amount = out_buffer_fill_count_q - input_consumed_count_q; 50 | 51 | assign s_out_buffer_clear = output_valid_q; 52 | 53 | 54 | // Count trailing 1s 55 | lzc #( 56 | .WIDTH(Width), 57 | .MODE(0) 58 | ) i_lzc( 59 | .in_i(~valid_i), 60 | .cnt_o(s_chopsize), 61 | .empty_o() 62 | ); 63 | 64 | // Input Barrel Shifter 65 | always_comb begin 66 | s_shifted_input = data_i; 67 | for (int signed i = 0; i < Width; i++) begin 68 | if (i+s_shift_amount >= Width) begin 69 | s_shifted_input[i+s_shift_amount-Width] = data_i[i]; 70 | end else if (i+s_shift_amount < 0) begin 71 | s_shifted_input[i+s_shift_amount+Width] = data_i[i]; 72 | end else begin 73 | s_shifted_input[i+s_shift_amount] = data_i[i]; 74 | end 75 | end 76 | end 77 | 78 | // Output Register Logic 79 | always_comb begin 80 | out_buffer_d = out_buffer_q; 81 | for (int i = 0; i < Width; i++) begin 82 | if (s_out_buffer_en[i]) begin 83 | out_buffer_d[i] = s_shifted_input[i]; 84 | end else if (s_out_buffer_clear) begin 85 | out_buffer_d[i] = '0; 86 | end 87 | end 88 | end 89 | 90 | always_comb begin 91 | s_out_buffer_en = '0; 92 | out_buffer_fill_count_d = out_buffer_fill_count_q; 93 | input_consumed_count_d = input_consumed_count_q; 94 | s_input_consumed_increase = '0; 95 | s_fetch_next_word = 1'b0; 96 | output_valid_d = 1'b0; 97 | 98 | if (!bypass_en_i) begin 99 | // First check if the input is valid 100 | if (valid_i) begin 101 | // Now we consume min(Width - 102 | // out_buffer_fill_count_q, s_chopsize - input_consumed_count_q) from the input. I.e. we 103 | // consume either as many elements as we need to assemble the next 104 | // output word or as many as are left in the current input word and 105 | // store it in the output buffer. The barrel shifter rotates the input 106 | // to the right location. We just need to assert the right enable 107 | // signals of the output buffer. 108 | s_input_consumed_increase = `SERIAL_LINK_MIN_EXPR(Width-out_buffer_fill_count_q, s_chopsize-input_consumed_count_q); 109 | for (int i = 0; i < Width; i++) begin 110 | if (i >= out_buffer_fill_count_q && i < out_buffer_fill_count_q+s_input_consumed_increase) begin 111 | s_out_buffer_en[i] = 1'b1; 112 | end 113 | end 114 | // Also we have to update the counters: 115 | 116 | // If the out_buffer fill counter overflows, we have a complete word to 117 | // send downstream. 118 | if (out_buffer_fill_count_q + s_input_consumed_increase == Width) begin 119 | out_buffer_fill_count_d = '0; 120 | output_valid_d = 1'b1; 121 | end else begin 122 | out_buffer_fill_count_d = out_buffer_fill_count_q + s_input_consumed_increase; 123 | end 124 | 125 | // If the input was fully consumed, fetch the next input word 126 | if (input_consumed_count_q + s_input_consumed_increase == s_chopsize) begin 127 | input_consumed_count_d = '0; 128 | s_fetch_next_word = 1'b1; 129 | end else begin 130 | input_consumed_count_d = input_consumed_count_q + s_input_consumed_increase; 131 | end 132 | end 133 | end 134 | end 135 | 136 | // If we try to send an output word downstream while downstream is not ready, 137 | // stall the whole circuit 138 | 139 | assign s_stall = output_valid_q & !ready_i; 140 | 141 | always_ff @(posedge clk_i, negedge rst_ni) begin 142 | if (!rst_ni) begin 143 | out_buffer_q <= '0; 144 | input_consumed_count_q <= '0; 145 | out_buffer_fill_count_q <= '0; 146 | output_valid_q <= 1'b0; 147 | end else begin 148 | if (!s_stall) begin 149 | out_buffer_q <= out_buffer_d; 150 | input_consumed_count_q <= input_consumed_count_d; 151 | out_buffer_fill_count_q <= out_buffer_fill_count_d; 152 | output_valid_q <= output_valid_d; 153 | end 154 | end 155 | end 156 | 157 | assign data_o = bypass_en_i? data_i : out_buffer_q; 158 | always_comb begin 159 | if (bypass_en_i) begin 160 | valid_o = valid_i; 161 | end else begin 162 | for (int i = 0; i < Width; i++) begin 163 | valid_o = output_valid_q; 164 | end 165 | end 166 | end 167 | assign ready_o = bypass_en_i? ready_i: s_fetch_next_word && !s_stall; 168 | 169 | endmodule 170 | -------------------------------------------------------------------------------- /src/regs/serial_link_single_channel.hjson: -------------------------------------------------------------------------------- 1 | // Copyright 2022 ETH Zurich and University of Bologna. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Author: Tim Fischer 6 | 7 | { 8 | name: "serial_link_single_channel", 9 | clock_primary: "clk_i" 10 | reset_primary: "rst_ni" 11 | bus_interfaces: [ 12 | { protocol: "reg_iface", direction: "device"} 13 | ] 14 | regwidth: "32", 15 | param_list: [ 16 | { name: "NumChannels", 17 | desc: "Number of channels", 18 | type: "int", 19 | default: "1", 20 | local: "true" 21 | }, 22 | { name: "Log2NumChannels", 23 | desc: "Number of channels", 24 | type: "int", 25 | default: "1", 26 | local: "true" 27 | }, 28 | { name: "NumBits", 29 | desc: "Number of bits transfered in one clock cycle (2 * NumLanes)", 30 | type: "int", 31 | default: "16", //16 for DDR, 8 for SDR 32 | local: "true" 33 | }, 34 | { name: "Log2MaxClkDiv", 35 | desc: "Number of bits for clock divider counter", 36 | type: "int", 37 | default: "10", 38 | local: "true" 39 | }, 40 | { name: "Log2RawModeTXFifoDepth", 41 | desc: "The depth of the TX FIFO for raw mode operation." 42 | type: "int", 43 | default: "3", 44 | local: "true" 45 | } 46 | ], 47 | 48 | registers: [ 49 | { 50 | name: "CTRL", 51 | desc: "Global clock, isolation and reset control configuration" 52 | swaccess: "rw", 53 | hwaccess: "hro", 54 | // Clock disabled (i.e. gated) by default 55 | fields: [ 56 | { 57 | bits: "0", 58 | name: "clk_ena", 59 | desc: "Clock gate enable for network, link, physical layer. (active-high)", 60 | resval: 0, 61 | }, 62 | { 63 | bits: "1", 64 | name: "reset_n", 65 | resval: 1, 66 | // *Not* held in reset (i.e. signal high) by default. 67 | // Since clock is gated on reset, inner serial link state should *not* change until ungate 68 | desc: "SW controlled synchronous reset. (active-low)" 69 | }, 70 | // All channels isolated by default 71 | { 72 | bits: "8", 73 | name: "axi_in_isolate", 74 | resval: 1, 75 | desc: "Isolate AXI slave in port. (active-high)" 76 | }, 77 | { 78 | bits: "9", 79 | name: "axi_out_isolate", 80 | resval: 1, 81 | desc: "Isolate AXI master out port. (active-high)" 82 | } 83 | ] 84 | }, 85 | { 86 | name: "ISOLATED", 87 | desc: "Isolation status of AXI ports", 88 | swaccess: "ro", 89 | hwaccess: "hwo", 90 | hwqe: "true", 91 | hwext: "true", 92 | // All channels isolated by default 93 | fields: [ 94 | {bits: "0:0", name: "axi_in", resval: 1, desc: "slave in isolation status"}, 95 | {bits: "1:1", name: "axi_out", resval: 1, desc: "master out isolation status"}, 96 | ] 97 | }, 98 | { multireg: 99 | { 100 | name: "TX_PHY_CLK_DIV", 101 | desc: "Holds clock divider factor for forwarded clock of the TX Phys", 102 | count: "NumChannels", 103 | cname: "TX_PHY_CLK_DIV", 104 | swaccess: "rw", 105 | hwaccess: "hro", 106 | compact: false, 107 | fields: [ 108 | { bits: "Log2MaxClkDiv:0", 109 | desc: "Clock division factor of TX clock", 110 | name: "clk_divs", 111 | resval: 8 112 | } 113 | ] 114 | } 115 | }, 116 | { multireg: 117 | { 118 | name: "TX_PHY_CLK_START", 119 | desc: "Controls duty cycle and phase of rising edge in TX Phys", 120 | count: "NumChannels", 121 | cname: "TX_PHY_CLK_START", 122 | compact: false, 123 | swaccess: "rw", 124 | hwaccess: "hro", 125 | fields: [ 126 | { bits: "Log2MaxClkDiv:0", 127 | name: "clk_shift_start", 128 | desc: "Positive Edge of divided, shifted clock", 129 | resval: 2 130 | } 131 | ] 132 | } 133 | }, 134 | { multireg: 135 | { 136 | name: "TX_PHY_CLK_END", 137 | desc: "Controls duty cycle and phase of falling edge in TX Phys", 138 | count: "NumChannels", 139 | cname: "TX_PHY_CLK_END", 140 | compact: false, 141 | swaccess: "rw", 142 | hwaccess: "hro", 143 | fields: [ 144 | { bits: "Log2MaxClkDiv:0", 145 | name: "clk_shift_end", 146 | desc: "Negative Edge of divided, shifted clock", 147 | resval: 6 148 | } 149 | ] 150 | } 151 | }, 152 | { 153 | name: "RAW_MODE_EN", 154 | desc: "Enables Raw mode", 155 | swaccess: "wo", 156 | hwaccess: "hro", 157 | fields: [ 158 | {bits: "0", resval: 0} 159 | ] 160 | }, 161 | { 162 | name: "RAW_MODE_IN_CH_SEL", 163 | desc: "Receive channel select in RAW mode", 164 | swaccess: "wo", 165 | hwaccess: "hro", 166 | fields: [ 167 | { bits: "Log2NumChannels-1:0", 168 | resval: 0 169 | } 170 | ] 171 | }, 172 | { 173 | name: "RAW_MODE_IN_DATA_VALID", 174 | desc: "Mask for valid data in RX FIFOs during RAW mode.", 175 | swaccess: "ro", 176 | hwaccess: "hwo", 177 | hwext: "true", 178 | fields: [ 179 | { bits: "NumChannels-1:0", 180 | } 181 | ] 182 | }, 183 | { 184 | name: "RAW_MODE_IN_DATA", 185 | desc: "Data received by the selected channel in RAW mode", 186 | swaccess: "ro", 187 | hwaccess: "hrw", 188 | hwext: "true", 189 | hwre: "true", 190 | fields: [ 191 | { bits: "NumBits-1:0", 192 | } 193 | ] 194 | }, 195 | { 196 | name: "RAW_MODE_OUT_CH_MASK", 197 | desc: "Selects channels to send out data in RAW mode, '1 corresponds to broadcasting", 198 | swaccess: "wo", 199 | hwaccess: "hro", 200 | fields: [ 201 | { bits: "NumChannels-1:0", 202 | resval: 0 203 | } 204 | ] 205 | }, 206 | { 207 | name: "RAW_MODE_OUT_DATA_FIFO", 208 | desc: "Data that will be pushed to the RAW mode output FIFO", 209 | swaccess: "wo", 210 | hwaccess: "hro", 211 | hwqe: "true", 212 | fields: [ 213 | { bits: "NumBits-1:0", 214 | resval: 0 215 | } 216 | ] 217 | }, 218 | { 219 | name: "RAW_MODE_OUT_DATA_FIFO_CTRL", 220 | desc: "Status and control register for the RAW mode data out FIFO", 221 | swaccess: "rw", 222 | hwaccess: "hrw", 223 | hwext: "true", 224 | hwqe: "true", 225 | fields: [ 226 | { 227 | bits: "0", 228 | name: "clear", 229 | swaccess: "wo", 230 | hwaccess: "hro", 231 | desc: "Clears the raw mode TX FIFO.", 232 | }, 233 | { 234 | bits: "8+Log2RawModeTXFifoDepth-1:8", 235 | name: "fill_state", 236 | swaccess: "ro", 237 | hwaccess: "hwo", 238 | desc: "The number of elements currently stored in the RAW mode TX FIFO that are ready to be sent.", 239 | resval: 0 240 | }, 241 | { 242 | bits: "31", 243 | name: "is_full", 244 | swaccess: "ro", 245 | hwaccess: "hwo", 246 | desc: "If '1' the FIFO is full and does not accept any more items. Any additional write to the data fill register will be ignored until there is sufficient space again.", 247 | resval: 0 248 | } 249 | ] 250 | }, 251 | { 252 | name: "RAW_MODE_OUT_EN", 253 | desc: "Enable transmission of data currently hold in the output FIFO", 254 | swaccess: "rw", 255 | hwaccess: "hro", 256 | fields: [ 257 | { bits: "0", 258 | resval: 0 259 | } 260 | ] 261 | }, 262 | { 263 | name: "FLOW_CONTROL_FIFO_CLEAR", 264 | desc: "Clears the flow control Fifo", 265 | swaccess: "wo", 266 | hwaccess: "hro", 267 | hwext: "true", 268 | hwqe: "true", 269 | fields: [ 270 | { bits: "0", 271 | resval: 0 272 | } 273 | ] 274 | }, 275 | ] 276 | } 277 | -------------------------------------------------------------------------------- /src/regs/serial_link_single_channel_reg_pkg.sv: -------------------------------------------------------------------------------- 1 | // Copyright lowRISC contributors. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Register Package auto-generated by `reggen` containing data structure 6 | 7 | package serial_link_single_channel_reg_pkg; 8 | 9 | // Param list 10 | parameter int NumChannels = 1; 11 | parameter int Log2NumChannels = 1; 12 | parameter int NumBits = 16; 13 | parameter int Log2MaxClkDiv = 10; 14 | parameter int Log2RawModeTXFifoDepth = 3; 15 | 16 | // Address widths within the block 17 | parameter int BlockAw = 6; 18 | 19 | //////////////////////////// 20 | // Typedefs for registers // 21 | //////////////////////////// 22 | 23 | typedef struct packed { 24 | struct packed { 25 | logic q; 26 | } clk_ena; 27 | struct packed { 28 | logic q; 29 | } reset_n; 30 | struct packed { 31 | logic q; 32 | } axi_in_isolate; 33 | struct packed { 34 | logic q; 35 | } axi_out_isolate; 36 | } serial_link_single_channel_reg2hw_ctrl_reg_t; 37 | 38 | typedef struct packed { 39 | logic [10:0] q; 40 | } serial_link_single_channel_reg2hw_tx_phy_clk_div_mreg_t; 41 | 42 | typedef struct packed { 43 | logic [10:0] q; 44 | } serial_link_single_channel_reg2hw_tx_phy_clk_start_mreg_t; 45 | 46 | typedef struct packed { 47 | logic [10:0] q; 48 | } serial_link_single_channel_reg2hw_tx_phy_clk_end_mreg_t; 49 | 50 | typedef struct packed { 51 | logic q; 52 | } serial_link_single_channel_reg2hw_raw_mode_en_reg_t; 53 | 54 | typedef struct packed { 55 | logic q; 56 | } serial_link_single_channel_reg2hw_raw_mode_in_ch_sel_reg_t; 57 | 58 | typedef struct packed { 59 | logic [15:0] q; 60 | logic re; 61 | } serial_link_single_channel_reg2hw_raw_mode_in_data_reg_t; 62 | 63 | typedef struct packed { 64 | logic q; 65 | } serial_link_single_channel_reg2hw_raw_mode_out_ch_mask_reg_t; 66 | 67 | typedef struct packed { 68 | logic [15:0] q; 69 | logic qe; 70 | } serial_link_single_channel_reg2hw_raw_mode_out_data_fifo_reg_t; 71 | 72 | typedef struct packed { 73 | struct packed { 74 | logic q; 75 | logic qe; 76 | } clear; 77 | } serial_link_single_channel_reg2hw_raw_mode_out_data_fifo_ctrl_reg_t; 78 | 79 | typedef struct packed { 80 | logic q; 81 | } serial_link_single_channel_reg2hw_raw_mode_out_en_reg_t; 82 | 83 | typedef struct packed { 84 | logic q; 85 | logic qe; 86 | } serial_link_single_channel_reg2hw_flow_control_fifo_clear_reg_t; 87 | 88 | typedef struct packed { 89 | struct packed { 90 | logic d; 91 | } axi_in; 92 | struct packed { 93 | logic d; 94 | } axi_out; 95 | } serial_link_single_channel_hw2reg_isolated_reg_t; 96 | 97 | typedef struct packed { 98 | logic d; 99 | } serial_link_single_channel_hw2reg_raw_mode_in_data_valid_reg_t; 100 | 101 | typedef struct packed { 102 | logic [15:0] d; 103 | } serial_link_single_channel_hw2reg_raw_mode_in_data_reg_t; 104 | 105 | typedef struct packed { 106 | struct packed { 107 | logic [2:0] d; 108 | } fill_state; 109 | struct packed { 110 | logic d; 111 | } is_full; 112 | } serial_link_single_channel_hw2reg_raw_mode_out_data_fifo_ctrl_reg_t; 113 | 114 | // Register -> HW type 115 | typedef struct packed { 116 | serial_link_single_channel_reg2hw_ctrl_reg_t ctrl; // [78:75] 117 | serial_link_single_channel_reg2hw_tx_phy_clk_div_mreg_t [0:0] tx_phy_clk_div; // [74:64] 118 | serial_link_single_channel_reg2hw_tx_phy_clk_start_mreg_t [0:0] tx_phy_clk_start; // [63:53] 119 | serial_link_single_channel_reg2hw_tx_phy_clk_end_mreg_t [0:0] tx_phy_clk_end; // [52:42] 120 | serial_link_single_channel_reg2hw_raw_mode_en_reg_t raw_mode_en; // [41:41] 121 | serial_link_single_channel_reg2hw_raw_mode_in_ch_sel_reg_t raw_mode_in_ch_sel; // [40:40] 122 | serial_link_single_channel_reg2hw_raw_mode_in_data_reg_t raw_mode_in_data; // [39:23] 123 | serial_link_single_channel_reg2hw_raw_mode_out_ch_mask_reg_t raw_mode_out_ch_mask; // [22:22] 124 | serial_link_single_channel_reg2hw_raw_mode_out_data_fifo_reg_t raw_mode_out_data_fifo; // [21:5] 125 | serial_link_single_channel_reg2hw_raw_mode_out_data_fifo_ctrl_reg_t raw_mode_out_data_fifo_ctrl; // [4:3] 126 | serial_link_single_channel_reg2hw_raw_mode_out_en_reg_t raw_mode_out_en; // [2:2] 127 | serial_link_single_channel_reg2hw_flow_control_fifo_clear_reg_t flow_control_fifo_clear; // [1:0] 128 | } serial_link_single_channel_reg2hw_t; 129 | 130 | // HW -> register type 131 | typedef struct packed { 132 | serial_link_single_channel_hw2reg_isolated_reg_t isolated; // [22:21] 133 | serial_link_single_channel_hw2reg_raw_mode_in_data_valid_reg_t raw_mode_in_data_valid; // [20:20] 134 | serial_link_single_channel_hw2reg_raw_mode_in_data_reg_t raw_mode_in_data; // [19:4] 135 | serial_link_single_channel_hw2reg_raw_mode_out_data_fifo_ctrl_reg_t raw_mode_out_data_fifo_ctrl; // [3:0] 136 | } serial_link_single_channel_hw2reg_t; 137 | 138 | // Register offsets 139 | parameter logic [BlockAw-1:0] SERIAL_LINK_SINGLE_CHANNEL_CTRL_OFFSET = 6'h 0; 140 | parameter logic [BlockAw-1:0] SERIAL_LINK_SINGLE_CHANNEL_ISOLATED_OFFSET = 6'h 4; 141 | parameter logic [BlockAw-1:0] SERIAL_LINK_SINGLE_CHANNEL_TX_PHY_CLK_DIV_OFFSET = 6'h 8; 142 | parameter logic [BlockAw-1:0] SERIAL_LINK_SINGLE_CHANNEL_TX_PHY_CLK_START_OFFSET = 6'h c; 143 | parameter logic [BlockAw-1:0] SERIAL_LINK_SINGLE_CHANNEL_TX_PHY_CLK_END_OFFSET = 6'h 10; 144 | parameter logic [BlockAw-1:0] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_EN_OFFSET = 6'h 14; 145 | parameter logic [BlockAw-1:0] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_IN_CH_SEL_OFFSET = 6'h 18; 146 | parameter logic [BlockAw-1:0] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_IN_DATA_VALID_OFFSET = 6'h 1c; 147 | parameter logic [BlockAw-1:0] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_IN_DATA_OFFSET = 6'h 20; 148 | parameter logic [BlockAw-1:0] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_OUT_CH_MASK_OFFSET = 6'h 24; 149 | parameter logic [BlockAw-1:0] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_OUT_DATA_FIFO_OFFSET = 6'h 28; 150 | parameter logic [BlockAw-1:0] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_OUT_DATA_FIFO_CTRL_OFFSET = 6'h 2c; 151 | parameter logic [BlockAw-1:0] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_OUT_EN_OFFSET = 6'h 30; 152 | parameter logic [BlockAw-1:0] SERIAL_LINK_SINGLE_CHANNEL_FLOW_CONTROL_FIFO_CLEAR_OFFSET = 6'h 34; 153 | 154 | // Reset values for hwext registers and their fields 155 | parameter logic [1:0] SERIAL_LINK_SINGLE_CHANNEL_ISOLATED_RESVAL = 2'h 3; 156 | parameter logic [0:0] SERIAL_LINK_SINGLE_CHANNEL_ISOLATED_AXI_IN_RESVAL = 1'h 1; 157 | parameter logic [0:0] SERIAL_LINK_SINGLE_CHANNEL_ISOLATED_AXI_OUT_RESVAL = 1'h 1; 158 | parameter logic [0:0] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_IN_DATA_VALID_RESVAL = 1'h 0; 159 | parameter logic [15:0] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_IN_DATA_RESVAL = 16'h 0; 160 | parameter logic [31:0] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_OUT_DATA_FIFO_CTRL_RESVAL = 32'h 0; 161 | parameter logic [2:0] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_OUT_DATA_FIFO_CTRL_FILL_STATE_RESVAL = 3'h 0; 162 | parameter logic [0:0] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_OUT_DATA_FIFO_CTRL_IS_FULL_RESVAL = 1'h 0; 163 | parameter logic [0:0] SERIAL_LINK_SINGLE_CHANNEL_FLOW_CONTROL_FIFO_CLEAR_RESVAL = 1'h 0; 164 | parameter logic [0:0] SERIAL_LINK_SINGLE_CHANNEL_FLOW_CONTROL_FIFO_CLEAR_FLOW_CONTROL_FIFO_CLEAR_RESVAL = 1'h 0; 165 | 166 | // Register index 167 | typedef enum int { 168 | SERIAL_LINK_SINGLE_CHANNEL_CTRL, 169 | SERIAL_LINK_SINGLE_CHANNEL_ISOLATED, 170 | SERIAL_LINK_SINGLE_CHANNEL_TX_PHY_CLK_DIV, 171 | SERIAL_LINK_SINGLE_CHANNEL_TX_PHY_CLK_START, 172 | SERIAL_LINK_SINGLE_CHANNEL_TX_PHY_CLK_END, 173 | SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_EN, 174 | SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_IN_CH_SEL, 175 | SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_IN_DATA_VALID, 176 | SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_IN_DATA, 177 | SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_OUT_CH_MASK, 178 | SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_OUT_DATA_FIFO, 179 | SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_OUT_DATA_FIFO_CTRL, 180 | SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_OUT_EN, 181 | SERIAL_LINK_SINGLE_CHANNEL_FLOW_CONTROL_FIFO_CLEAR 182 | } serial_link_single_channel_id_e; 183 | 184 | // Register width information to check illegal writes 185 | parameter logic [3:0] SERIAL_LINK_SINGLE_CHANNEL_PERMIT [14] = '{ 186 | 4'b 0011, // index[ 0] SERIAL_LINK_SINGLE_CHANNEL_CTRL 187 | 4'b 0001, // index[ 1] SERIAL_LINK_SINGLE_CHANNEL_ISOLATED 188 | 4'b 0011, // index[ 2] SERIAL_LINK_SINGLE_CHANNEL_TX_PHY_CLK_DIV 189 | 4'b 0011, // index[ 3] SERIAL_LINK_SINGLE_CHANNEL_TX_PHY_CLK_START 190 | 4'b 0011, // index[ 4] SERIAL_LINK_SINGLE_CHANNEL_TX_PHY_CLK_END 191 | 4'b 0001, // index[ 5] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_EN 192 | 4'b 0001, // index[ 6] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_IN_CH_SEL 193 | 4'b 0001, // index[ 7] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_IN_DATA_VALID 194 | 4'b 0011, // index[ 8] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_IN_DATA 195 | 4'b 0001, // index[ 9] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_OUT_CH_MASK 196 | 4'b 0011, // index[10] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_OUT_DATA_FIFO 197 | 4'b 1111, // index[11] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_OUT_DATA_FIFO_CTRL 198 | 4'b 0001, // index[12] SERIAL_LINK_SINGLE_CHANNEL_RAW_MODE_OUT_EN 199 | 4'b 0001 // index[13] SERIAL_LINK_SINGLE_CHANNEL_FLOW_CONTROL_FIFO_CLEAR 200 | }; 201 | 202 | endpackage 203 | 204 | -------------------------------------------------------------------------------- /src/regs/serial_link_single_channel_sdr.hjson: -------------------------------------------------------------------------------- 1 | // Copyright 2022 ETH Zurich and University of Bologna. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Author: Tim Fischer 6 | 7 | { 8 | name: "serial_link_single_channel", 9 | clock_primary: "clk_i" 10 | reset_primary: "rst_ni" 11 | bus_interfaces: [ 12 | { protocol: "reg_iface", direction: "device"} 13 | ] 14 | regwidth: "32", 15 | param_list: [ 16 | { name: "NumChannels", 17 | desc: "Number of channels", 18 | type: "int", 19 | default: "1", 20 | local: "true" 21 | }, 22 | { name: "Log2NumChannels", 23 | desc: "Number of channels", 24 | type: "int", 25 | default: "1", 26 | local: "true" 27 | }, 28 | { name: "NumBits", 29 | desc: "Number of bits transfered in one clock cycle (2 * NumLanes)", 30 | type: "int", 31 | default: "8", //16 for DDR, 8 for SDR 32 | local: "true" 33 | }, 34 | { name: "Log2MaxClkDiv", 35 | desc: "Number of bits for clock divider counter", 36 | type: "int", 37 | default: "10", 38 | local: "true" 39 | }, 40 | { name: "Log2RawModeTXFifoDepth", 41 | desc: "The depth of the TX FIFO for raw mode operation." 42 | type: "int", 43 | default: "3", 44 | local: "true" 45 | } 46 | ], 47 | 48 | registers: [ 49 | { 50 | name: "CTRL", 51 | desc: "Global clock, isolation and reset control configuration" 52 | swaccess: "rw", 53 | hwaccess: "hro", 54 | // Clock disabled (i.e. gated) by default 55 | fields: [ 56 | { 57 | bits: "0", 58 | name: "clk_ena", 59 | desc: "Clock gate enable for network, link, physical layer. (active-high)", 60 | resval: 0, 61 | }, 62 | { 63 | bits: "1", 64 | name: "reset_n", 65 | resval: 1, 66 | // *Not* held in reset (i.e. signal high) by default. 67 | // Since clock is gated on reset, inner serial link state should *not* change until ungate 68 | desc: "SW controlled synchronous reset. (active-low)" 69 | }, 70 | // All channels isolated by default 71 | { 72 | bits: "8", 73 | name: "axi_in_isolate", 74 | resval: 1, 75 | desc: "Isolate AXI slave in port. (active-high)" 76 | }, 77 | { 78 | bits: "9", 79 | name: "axi_out_isolate", 80 | resval: 1, 81 | desc: "Isolate AXI master out port. (active-high)" 82 | } 83 | ] 84 | }, 85 | { 86 | name: "ISOLATED", 87 | desc: "Isolation status of AXI ports", 88 | swaccess: "ro", 89 | hwaccess: "hwo", 90 | hwqe: "true", 91 | hwext: "true", 92 | // All channels isolated by default 93 | fields: [ 94 | {bits: "0:0", name: "axi_in", resval: 1, desc: "slave in isolation status"}, 95 | {bits: "1:1", name: "axi_out", resval: 1, desc: "master out isolation status"}, 96 | ] 97 | }, 98 | { multireg: 99 | { 100 | name: "TX_PHY_CLK_DIV", 101 | desc: "Holds clock divider factor for forwarded clock of the TX Phys", 102 | count: "NumChannels", 103 | cname: "TX_PHY_CLK_DIV", 104 | swaccess: "rw", 105 | hwaccess: "hro", 106 | compact: false, 107 | fields: [ 108 | { bits: "Log2MaxClkDiv:0", 109 | desc: "Clock division factor of TX clock", 110 | name: "clk_divs", 111 | resval: 8 112 | } 113 | ] 114 | } 115 | }, 116 | { multireg: 117 | { 118 | name: "TX_PHY_CLK_START", 119 | desc: "Controls duty cycle and phase of rising edge in TX Phys", 120 | count: "NumChannels", 121 | cname: "TX_PHY_CLK_START", 122 | compact: false, 123 | swaccess: "rw", 124 | hwaccess: "hro", 125 | fields: [ 126 | { bits: "Log2MaxClkDiv:0", 127 | name: "clk_shift_start", 128 | desc: "Positive Edge of divided, shifted clock", 129 | resval: 0 130 | } 131 | ] 132 | } 133 | }, 134 | { multireg: 135 | { 136 | name: "TX_PHY_CLK_END", 137 | desc: "Controls duty cycle and phase of falling edge in TX Phys", 138 | count: "NumChannels", 139 | cname: "TX_PHY_CLK_END", 140 | compact: false, 141 | swaccess: "rw", 142 | hwaccess: "hro", 143 | fields: [ 144 | { bits: "Log2MaxClkDiv:0", 145 | name: "clk_shift_end", 146 | desc: "Negative Edge of divided, shifted clock", 147 | resval: 4 148 | } 149 | ] 150 | } 151 | }, 152 | { 153 | name: "RAW_MODE_EN", 154 | desc: "Enables Raw mode", 155 | swaccess: "wo", 156 | hwaccess: "hro", 157 | fields: [ 158 | {bits: "0", resval: 0} 159 | ] 160 | }, 161 | { 162 | name: "RAW_MODE_IN_CH_SEL", 163 | desc: "Receive channel select in RAW mode", 164 | swaccess: "wo", 165 | hwaccess: "hro", 166 | fields: [ 167 | { bits: "Log2NumChannels-1:0", 168 | resval: 0 169 | } 170 | ] 171 | }, 172 | { 173 | name: "RAW_MODE_IN_DATA_VALID", 174 | desc: "Mask for valid data in RX FIFOs during RAW mode.", 175 | swaccess: "ro", 176 | hwaccess: "hwo", 177 | hwext: "true", 178 | fields: [ 179 | { bits: "NumChannels-1:0", 180 | } 181 | ] 182 | }, 183 | { 184 | name: "RAW_MODE_IN_DATA", 185 | desc: "Data received by the selected channel in RAW mode", 186 | swaccess: "ro", 187 | hwaccess: "hrw", 188 | hwext: "true", 189 | hwre: "true", 190 | fields: [ 191 | { bits: "NumBits-1:0", 192 | } 193 | ] 194 | }, 195 | { 196 | name: "RAW_MODE_OUT_CH_MASK", 197 | desc: "Selects channels to send out data in RAW mode, '1 corresponds to broadcasting", 198 | swaccess: "wo", 199 | hwaccess: "hro", 200 | fields: [ 201 | { bits: "NumChannels-1:0", 202 | resval: 0 203 | } 204 | ] 205 | }, 206 | { 207 | name: "RAW_MODE_OUT_DATA_FIFO", 208 | desc: "Data that will be pushed to the RAW mode output FIFO", 209 | swaccess: "wo", 210 | hwaccess: "hro", 211 | hwqe: "true", 212 | fields: [ 213 | { bits: "NumBits-1:0", 214 | resval: 0 215 | } 216 | ] 217 | }, 218 | { 219 | name: "RAW_MODE_OUT_DATA_FIFO_CTRL", 220 | desc: "Status and control register for the RAW mode data out FIFO", 221 | swaccess: "rw", 222 | hwaccess: "hrw", 223 | hwext: "true", 224 | hwqe: "true", 225 | fields: [ 226 | { 227 | bits: "0", 228 | name: "clear", 229 | swaccess: "wo", 230 | hwaccess: "hro", 231 | desc: "Clears the raw mode TX FIFO.", 232 | }, 233 | { 234 | bits: "8+Log2RawModeTXFifoDepth-1:8", 235 | name: "fill_state", 236 | swaccess: "ro", 237 | hwaccess: "hwo", 238 | desc: "The number of elements currently stored in the RAW mode TX FIFO that are ready to be sent.", 239 | resval: 0 240 | }, 241 | { 242 | bits: "31", 243 | name: "is_full", 244 | swaccess: "ro", 245 | hwaccess: "hwo", 246 | desc: "If '1' the FIFO is full and does not accept any more items. Any additional write to the data fill register will be ignored until there is sufficient space again.", 247 | resval: 0 248 | } 249 | ] 250 | }, 251 | { 252 | name: "RAW_MODE_OUT_EN", 253 | desc: "Enable transmission of data currently hold in the output FIFO", 254 | swaccess: "rw", 255 | hwaccess: "hro", 256 | fields: [ 257 | { bits: "0", 258 | resval: 0 259 | } 260 | ] 261 | }, 262 | { 263 | name: "FLOW_CONTROL_FIFO_CLEAR", 264 | desc: "Clears the flow control Fifo", 265 | swaccess: "wo", 266 | hwaccess: "hro", 267 | hwext: "true", 268 | hwqe: "true", 269 | fields: [ 270 | { bits: "0", 271 | resval: 0 272 | } 273 | ] 274 | }, 275 | ] 276 | } 277 | -------------------------------------------------------------------------------- /src/serial_link_data_link.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2022 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Author: Tim Fischer 6 | 7 | `include "common_cells/registers.svh" 8 | `include "common_cells/assertions.svh" 9 | 10 | // Implements the Data Link layer of the Serial Link 11 | // Handles the RAW mode 12 | module serial_link_data_link #( 13 | parameter type axis_req_t = logic, 14 | parameter type axis_rsp_t = logic, 15 | parameter type phy_data_t = logic, 16 | parameter int NumChannels = 1, 17 | parameter int NumLanes = 8, 18 | parameter int RecvFifoDepth = -1, 19 | parameter int RawModeFifoDepth = 8, 20 | parameter int PayloadSplits = -1, 21 | parameter bit EnDdr = 1'b1, 22 | localparam int Log2NumChannels = (NumChannels > 1)? $clog2(NumChannels) : 1, 23 | localparam int unsigned Log2RawModeFifoDepth = $clog2(RawModeFifoDepth) 24 | ) ( 25 | input logic clk_i, 26 | input logic rst_ni, 27 | // AXI Stream interface signals 28 | input axis_req_t axis_in_req_i, 29 | output axis_rsp_t axis_in_rsp_o, 30 | output axis_req_t axis_out_req_o, 31 | input axis_rsp_t axis_out_rsp_i, 32 | // Phy Channel interface signals 33 | output phy_data_t [NumChannels-1:0] data_out_o, 34 | output logic [NumChannels-1:0] data_out_valid_o, 35 | input logic data_out_ready_i, 36 | input phy_data_t [NumChannels-1:0] data_in_i, 37 | input logic [NumChannels-1:0] data_in_valid_i, 38 | output logic [NumChannels-1:0] data_in_ready_o, 39 | // Debug/Calibration signals 40 | input logic cfg_flow_control_fifo_clear_i, 41 | input logic cfg_raw_mode_en_i, 42 | input logic [Log2NumChannels-1:0] cfg_raw_mode_in_ch_sel_i, 43 | output phy_data_t cfg_raw_mode_in_data_o, 44 | output logic [NumChannels-1:0] cfg_raw_mode_in_data_valid_o, 45 | input logic cfg_raw_mode_in_data_ready_i, 46 | input logic [NumChannels-1:0] cfg_raw_mode_out_ch_mask_i, 47 | input phy_data_t cfg_raw_mode_out_data_i, 48 | input logic cfg_raw_mode_out_data_valid_i, 49 | input logic cfg_raw_mode_out_en_i, 50 | input logic cfg_raw_mode_out_data_fifo_clear_i, 51 | output logic [Log2RawModeFifoDepth-1:0] cfg_raw_mode_out_data_fifo_fill_state_o, 52 | output logic cfg_raw_mode_out_data_fifo_is_full_o 53 | ); 54 | 55 | import serial_link_pkg::link_state_e; 56 | import serial_link_pkg::LinkSendIdle; 57 | import serial_link_pkg::LinkSendBusy; 58 | 59 | 60 | logic [PayloadSplits-1:0] recv_reg_in_valid, recv_reg_in_ready; 61 | logic [PayloadSplits-1:0] recv_reg_out_valid, recv_reg_out_ready; 62 | phy_data_t [PayloadSplits-1:0][NumChannels-1:0] recv_reg_data; 63 | logic [$clog2(PayloadSplits)-1:0] recv_reg_index_q, recv_reg_index_d; 64 | 65 | link_state_e link_state_q, link_state_d; 66 | logic [$clog2(PayloadSplits*NumChannels*NumLanes*(1+EnDdr)):0] link_out_index_q, link_out_index_d; 67 | 68 | logic raw_mode_fifo_full, raw_mode_fifo_empty; 69 | logic raw_mode_fifo_push, raw_mode_fifo_pop; 70 | phy_data_t raw_mode_fifo_data_in, raw_mode_fifo_data_out; 71 | 72 | 73 | ///////////////// 74 | // DATA IN // 75 | ///////////////// 76 | 77 | //Datatype for the stream fifo and register 78 | typedef phy_data_t [NumChannels-1:0] phy_data_chan_t; 79 | phy_data_chan_t flow_control_fifo_data_out; 80 | logic flow_control_fifo_valid_out, flow_control_fifo_ready_out; 81 | logic flow_control_fifo_valid_in, flow_control_fifo_ready_in; 82 | 83 | stream_fifo #( 84 | .T(phy_data_chan_t), 85 | .DEPTH (RecvFifoDepth) 86 | ) i_flow_control_fifo ( 87 | .clk_i ( clk_i ), 88 | .rst_ni ( rst_ni ), 89 | .flush_i ( cfg_flow_control_fifo_clear_i ), 90 | .testmode_i ( 1'b0 ), 91 | .usage_o ( ), 92 | .data_i ( data_in_i ), 93 | .valid_i ( flow_control_fifo_valid_in ), 94 | .ready_o ( flow_control_fifo_ready_in ), 95 | .data_o ( flow_control_fifo_data_out ), 96 | .valid_o ( flow_control_fifo_valid_out ), 97 | .ready_i ( flow_control_fifo_ready_out ) 98 | ); 99 | 100 | for (genvar i = 0; i < PayloadSplits; i++) begin : gen_recv_reg 101 | stream_register #( 102 | .T (phy_data_chan_t) 103 | ) i_recv_reg ( 104 | .clk_i ( clk_i ), 105 | .rst_ni ( rst_ni ), 106 | .clr_i ( 1'b0 ), 107 | .testmode_i ( 1'b0 ), 108 | .valid_i ( recv_reg_in_valid[i] ), 109 | .ready_o ( recv_reg_in_ready[i] ), 110 | .data_i ( flow_control_fifo_data_out ), 111 | .valid_o ( recv_reg_out_valid[i] ), 112 | .ready_i ( recv_reg_out_ready[i] ), 113 | .data_o ( recv_reg_data[i] ) 114 | ); 115 | end 116 | 117 | 118 | always_comb begin 119 | recv_reg_in_valid = '0; 120 | data_in_ready_o = '0; 121 | recv_reg_index_d = recv_reg_index_q; 122 | axis_out_req_o.tvalid = 1'b0; 123 | axis_out_req_o.t.data = recv_reg_data; 124 | recv_reg_out_ready = '0; 125 | cfg_raw_mode_in_data_o = '0; 126 | cfg_raw_mode_in_data_valid_o = '0; 127 | flow_control_fifo_valid_in = 1'b0; 128 | flow_control_fifo_ready_out = 1'b0; 129 | 130 | if (cfg_raw_mode_en_i) begin 131 | // Raw mode 132 | cfg_raw_mode_in_data_valid_o = data_in_valid_i; 133 | // Ready is asserted if there is a read access 134 | if (cfg_raw_mode_in_data_ready_i) begin 135 | // Select channel to read from and wait for valid data 136 | if (data_in_valid_i[cfg_raw_mode_in_ch_sel_i]) begin 137 | // Pop item from CDC RX FIFO 138 | data_in_ready_o[cfg_raw_mode_in_ch_sel_i] = 1'b1; 139 | // respond with data from selected channel 140 | cfg_raw_mode_in_data_o = data_in_i[cfg_raw_mode_in_ch_sel_i]; 141 | end else begin 142 | // TODO: send out Error response 143 | end 144 | end 145 | end else begin 146 | // Normal operating mode 147 | // If all inputs of each channel have valid data, push it to fifo 148 | flow_control_fifo_valid_in = &data_in_valid_i; 149 | data_in_ready_o = {NumChannels{flow_control_fifo_valid_in & flow_control_fifo_ready_in}}; 150 | // Pop from Fifo and assemble in register 151 | if (flow_control_fifo_valid_out & recv_reg_in_ready[recv_reg_index_q]) begin 152 | recv_reg_in_valid[recv_reg_index_q] = 1'b1; 153 | flow_control_fifo_ready_out = 1'b1; 154 | // Increment recv reg counter 155 | recv_reg_index_d = (recv_reg_index_q == PayloadSplits - 1)? 0 : recv_reg_index_q + 1; 156 | end 157 | 158 | // Once all Recv Stream Registers are filled -> generate AXI stream request 159 | axis_out_req_o.tvalid = &recv_reg_out_valid; 160 | recv_reg_out_ready = {PayloadSplits{axis_out_rsp_i.tready}}; 161 | end 162 | end 163 | 164 | `FF(recv_reg_index_q, recv_reg_index_d, '0) 165 | 166 | ////////////////// 167 | // DATA OUT // 168 | ////////////////// 169 | 170 | always_comb begin 171 | axis_in_rsp_o.tready = 1'b0; 172 | data_out_o = '0; 173 | data_out_valid_o = '0; 174 | link_out_index_d = link_out_index_q; 175 | link_state_d = link_state_q; 176 | raw_mode_fifo_pop = 1'b0; 177 | 178 | if (cfg_raw_mode_en_i) begin 179 | // Raw mode 180 | if (cfg_raw_mode_out_en_i & ~raw_mode_fifo_empty) begin 181 | data_out_valid_o = cfg_raw_mode_out_ch_mask_i; 182 | data_out_o = {{NumChannels}{raw_mode_fifo_data_out}}; 183 | if (data_out_ready_i) begin 184 | raw_mode_fifo_pop = 1'b1; 185 | end 186 | end 187 | end else begin 188 | // Normal operating mode 189 | unique case (link_state_q) 190 | LinkSendIdle: begin 191 | if (axis_in_req_i.tvalid) begin 192 | link_out_index_d = NumChannels * NumLanes * (1 + EnDdr); 193 | data_out_valid_o = '1; 194 | data_out_o = axis_in_req_i.t.data; 195 | if (data_out_ready_i) begin 196 | link_state_d = LinkSendBusy; 197 | if (link_out_index_d >= $bits(axis_in_req_i.t.data)) begin 198 | link_state_d = LinkSendIdle; 199 | axis_in_rsp_o.tready = 1'b1; 200 | end 201 | end 202 | end 203 | end 204 | 205 | LinkSendBusy: begin 206 | data_out_valid_o = '1; 207 | data_out_o = axis_in_req_i.t.data >> link_out_index_q; 208 | if (data_out_ready_i) begin 209 | link_out_index_d = link_out_index_q + NumChannels * NumLanes * (1 + EnDdr); 210 | if (link_out_index_d >= $bits(axis_in_req_i.t.data)) begin 211 | link_state_d = LinkSendIdle; 212 | axis_in_rsp_o.tready = 1'b1; 213 | end 214 | end 215 | end 216 | default:; 217 | endcase 218 | end 219 | end 220 | 221 | fifo_v3 #( 222 | .dtype ( phy_data_t ), 223 | .DEPTH ( RawModeFifoDepth ) 224 | ) i_raw_mode_fifo ( 225 | .clk_i ( clk_i ), 226 | .rst_ni ( rst_ni ), 227 | .flush_i ( cfg_raw_mode_out_data_fifo_clear_i ), 228 | .testmode_i ( 1'b0 ), 229 | .full_o ( raw_mode_fifo_full ), 230 | .empty_o ( raw_mode_fifo_empty ), 231 | .usage_o ( cfg_raw_mode_out_data_fifo_fill_state_o ), 232 | .data_i ( raw_mode_fifo_data_in ), 233 | .push_i ( raw_mode_fifo_push ), 234 | .data_o ( raw_mode_fifo_data_out ), 235 | .pop_i ( raw_mode_fifo_pop ) 236 | ); 237 | 238 | assign cfg_raw_mode_out_data_fifo_is_full_o = raw_mode_fifo_full; 239 | assign raw_mode_fifo_push = cfg_raw_mode_out_data_valid_i & ~raw_mode_fifo_full; 240 | assign raw_mode_fifo_data_in = cfg_raw_mode_out_data_i; 241 | 242 | `FF(link_out_index_q, link_out_index_d, '0) 243 | `FF(link_state_q, link_state_d, LinkSendIdle) 244 | 245 | endmodule 246 | -------------------------------------------------------------------------------- /src/serial_link_occamy_wrapper.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2022 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Author: Tim Fischer 6 | 7 | /// A wrapper around the Serial Link intended for integration into Occamy 8 | /// The wrapper additionally includes AXI isolation, reset controller & clock gating 9 | module serial_link_occamy_wrapper #( 10 | parameter type axi_req_t = logic, 11 | parameter type axi_rsp_t = logic, 12 | parameter type aw_chan_t = logic, 13 | parameter type ar_chan_t = logic, 14 | parameter type r_chan_t = logic, 15 | parameter type w_chan_t = logic, 16 | parameter type b_chan_t = logic, 17 | parameter type cfg_req_t = logic, 18 | parameter type cfg_rsp_t = logic, 19 | parameter int NumChannels = 1, 20 | parameter int NumLanes = 4, 21 | parameter int MaxClkDiv = 32, 22 | parameter bit EnDdr = 1'b1 23 | ) ( 24 | input logic clk_i, 25 | input logic rst_ni, 26 | input logic clk_reg_i, 27 | input logic rst_reg_ni, 28 | input logic testmode_i, 29 | input axi_req_t axi_in_req_i, 30 | output axi_rsp_t axi_in_rsp_o, 31 | output axi_req_t axi_out_req_o, 32 | input axi_rsp_t axi_out_rsp_i, 33 | input cfg_req_t cfg_req_i, 34 | output cfg_rsp_t cfg_rsp_o, 35 | input logic [NumChannels-1:0] ddr_rcv_clk_i, 36 | output logic [NumChannels-1:0] ddr_rcv_clk_o, 37 | input logic [NumChannels-1:0][NumLanes-1:0] ddr_i, 38 | output logic [NumChannels-1:0][NumLanes-1:0] ddr_o 39 | ); 40 | 41 | logic clk_serial_link; 42 | logic rst_serial_link_n; 43 | 44 | logic clk_ena; 45 | logic reset_n; 46 | 47 | axi_req_t axi_in_req, axi_out_req; 48 | axi_rsp_t axi_in_rsp, axi_out_rsp; 49 | 50 | // Quadrant clock gate controlled by register 51 | tc_clk_gating i_tc_clk_gating ( 52 | .clk_i, 53 | .en_i (clk_ena), 54 | .test_en_i (testmode_i), 55 | .clk_o (clk_serial_link) 56 | ); 57 | 58 | // Reset directly from register (i.e. (de)assertion inherently synchronized) 59 | // Multiplex with glitchless multiplexor, top reset for testing purposes 60 | tc_clk_mux2 i_tc_reset_mux ( 61 | .clk0_i (reset_n), 62 | .clk1_i (rst_ni), 63 | .clk_sel_i (testmode_i), 64 | .clk_o (rst_serial_link_n) 65 | ); 66 | 67 | logic [1:0] isolated, isolate; 68 | 69 | axi_isolate #( 70 | .TerminateTransaction(0), 71 | .AtopSupport(1), 72 | .AxiIdWidth($bits(axi_in_req_i.aw.id)), 73 | .AxiAddrWidth($bits(axi_in_req_i.aw.addr)), 74 | .AxiDataWidth($bits(axi_in_req_i.w.data)), 75 | .AxiUserWidth($bits(axi_in_req_i.aw.user)), 76 | .axi_req_t ( axi_req_t ), 77 | .axi_resp_t ( axi_rsp_t ) 78 | 79 | ) i_serial_link_in_isolate ( 80 | .clk_i ( clk_i ), 81 | .rst_ni ( rst_ni ), 82 | .slv_req_i ( axi_in_req_i ), 83 | .slv_resp_o ( axi_in_rsp_o ), 84 | .mst_req_o ( axi_in_req ), 85 | .mst_resp_i ( axi_in_rsp ), 86 | .isolate_i ( isolate[0] ), 87 | .isolated_o ( isolated[0] ) 88 | ); 89 | 90 | axi_isolate #( 91 | .TerminateTransaction(0), 92 | .AtopSupport(1), 93 | .AxiIdWidth($bits(axi_in_req_i.aw.id)), 94 | .AxiAddrWidth($bits(axi_in_req_i.aw.addr)), 95 | .AxiDataWidth($bits(axi_in_req_i.w.data)), 96 | .AxiUserWidth($bits(axi_in_req_i.aw.user)), 97 | .axi_req_t ( axi_req_t ), 98 | .axi_resp_t ( axi_rsp_t ) 99 | 100 | ) i_serial_link_out_isolate ( 101 | .clk_i ( clk_i ), 102 | .rst_ni ( rst_ni ), 103 | .slv_req_i ( axi_out_req ), 104 | .slv_resp_o ( axi_out_rsp ), 105 | .mst_req_o ( axi_out_req_o ), 106 | .mst_resp_i ( axi_out_rsp_i ), 107 | .isolate_i ( isolate[1] ), 108 | .isolated_o ( isolated[1] ) 109 | ); 110 | 111 | if (NumChannels > 1) begin : gen_multi_channel_serial_link 112 | serial_link #( 113 | .axi_req_t ( axi_req_t ), 114 | .axi_rsp_t ( axi_rsp_t ), 115 | .aw_chan_t ( aw_chan_t ), 116 | .w_chan_t ( w_chan_t ), 117 | .b_chan_t ( b_chan_t ), 118 | .ar_chan_t ( ar_chan_t ), 119 | .r_chan_t ( r_chan_t ), 120 | .cfg_req_t ( cfg_req_t ), 121 | .cfg_rsp_t ( cfg_rsp_t ), 122 | .hw2reg_t ( serial_link_reg_pkg::serial_link_hw2reg_t ), 123 | .reg2hw_t ( serial_link_reg_pkg::serial_link_reg2hw_t ), 124 | .NumChannels ( NumChannels ), 125 | .NumLanes ( NumLanes ), 126 | .MaxClkDiv ( MaxClkDiv ), 127 | .EnDdr ( EnDdr ) 128 | ) i_serial_link ( 129 | .clk_i ( clk_i ), 130 | .rst_ni ( rst_ni ), 131 | .clk_sl_i ( clk_serial_link ), 132 | .rst_sl_ni ( rst_serial_link_n ), 133 | .clk_reg_i ( clk_reg_i ), 134 | .rst_reg_ni ( rst_reg_ni ), 135 | .testmode_i ( 1'b0 ), 136 | .axi_in_req_i ( axi_in_req ), 137 | .axi_in_rsp_o ( axi_in_rsp ), 138 | .axi_out_req_o ( axi_out_req ), 139 | .axi_out_rsp_i ( axi_out_rsp ), 140 | .cfg_req_i ( cfg_req_i ), 141 | .cfg_rsp_o ( cfg_rsp_o ), 142 | .ddr_rcv_clk_i ( ddr_rcv_clk_i ), 143 | .ddr_rcv_clk_o ( ddr_rcv_clk_o ), 144 | .ddr_i ( ddr_i ), 145 | .ddr_o ( ddr_o ), 146 | .isolated_i ( isolated ), 147 | .isolate_o ( isolate ), 148 | .clk_ena_o ( clk_ena ), 149 | .reset_no ( reset_n ) 150 | ); 151 | end else begin : gen_single_channel_serial_link 152 | serial_link #( 153 | .axi_req_t ( axi_req_t ), 154 | .axi_rsp_t ( axi_rsp_t ), 155 | .aw_chan_t ( aw_chan_t ), 156 | .w_chan_t ( w_chan_t ), 157 | .b_chan_t ( b_chan_t ), 158 | .ar_chan_t ( ar_chan_t ), 159 | .r_chan_t ( r_chan_t ), 160 | .cfg_req_t ( cfg_req_t ), 161 | .cfg_rsp_t ( cfg_rsp_t ), 162 | .hw2reg_t ( serial_link_single_channel_reg_pkg::serial_link_single_channel_hw2reg_t ), 163 | .reg2hw_t ( serial_link_single_channel_reg_pkg::serial_link_single_channel_reg2hw_t ), 164 | .NumChannels ( NumChannels ), 165 | .NumLanes ( NumLanes ), 166 | .MaxClkDiv ( MaxClkDiv ), 167 | .EnDdr ( EnDdr ) 168 | ) i_serial_link ( 169 | .clk_i ( clk_i ), 170 | .rst_ni ( rst_ni ), 171 | .clk_sl_i ( clk_serial_link ), 172 | .rst_sl_ni ( rst_serial_link_n ), 173 | .clk_reg_i ( clk_reg_i ), 174 | .rst_reg_ni ( rst_reg_ni ), 175 | .testmode_i ( 1'b0 ), 176 | .axi_in_req_i ( axi_in_req ), 177 | .axi_in_rsp_o ( axi_in_rsp ), 178 | .axi_out_req_o ( axi_out_req ), 179 | .axi_out_rsp_i ( axi_out_rsp ), 180 | .cfg_req_i ( cfg_req_i ), 181 | .cfg_rsp_o ( cfg_rsp_o ), 182 | .ddr_rcv_clk_i ( ddr_rcv_clk_i ), 183 | .ddr_rcv_clk_o ( ddr_rcv_clk_o ), 184 | .ddr_i ( ddr_i ), 185 | .ddr_o ( ddr_o ), 186 | .isolated_i ( isolated ), 187 | .isolate_o ( isolate ), 188 | .clk_ena_o ( clk_ena ), 189 | .reset_no ( reset_n ) 190 | ); 191 | end 192 | 193 | endmodule 194 | -------------------------------------------------------------------------------- /src/serial_link_physical.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2022 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Author: Tim Fischer 6 | // Contributor: Chandra de Viragh 7 | 8 | 9 | `include "common_cells/registers.svh" 10 | `include "common_cells/assertions.svh" 11 | 12 | // Implements a single TX channel which forwards the source-synchronous clock 13 | module serial_link_physical_tx #( 14 | parameter int NumLanes = 8, 15 | parameter int MaxClkDiv = 32, 16 | parameter bit EnDdr = 1'b1, 17 | parameter type phy_data_t = logic, 18 | parameter type clk_div_t = logic [$clog2(MaxClkDiv):0] 19 | ) ( 20 | input logic clk_i, 21 | input logic rst_ni, 22 | input clk_div_t clk_div_i, 23 | input clk_div_t clk_shift_start_i, 24 | input clk_div_t clk_shift_end_i, 25 | input phy_data_t data_out_i, 26 | input logic data_out_valid_i, 27 | output logic data_out_ready_o, 28 | output logic ddr_rcv_clk_o, 29 | output logic [NumLanes-1:0] ddr_o 30 | ); 31 | phy_data_t data_out_q; 32 | 33 | clk_div_t clk_cnt_q, clk_cnt_d; 34 | logic clk_enable; 35 | logic clk_toggle, clk_slow_toggle; 36 | logic clk_slow; 37 | logic ddr_sel; 38 | 39 | // Valid is always set, but 40 | // src_clk is clock gated 41 | assign data_out_ready_o = data_out_valid_i & (clk_cnt_q == clk_div_i - 1); 42 | 43 | /////////////////////////////////////// 44 | // CLOCK DIVIDER + PHASE SHIFTER // 45 | /////////////////////////////////////// 46 | 47 | always_comb begin 48 | 49 | clk_cnt_d = 0; 50 | 51 | if (data_out_valid_i) begin 52 | clk_cnt_d = (clk_cnt_q == clk_div_i - 1)? '0 : clk_cnt_q + 1; 53 | end 54 | 55 | clk_enable = data_out_valid_i; 56 | clk_toggle = (clk_cnt_q == clk_shift_start_i) | (clk_cnt_q == clk_shift_end_i); 57 | clk_slow_toggle = (clk_cnt_q == 0) | (clk_cnt_q == clk_div_i/2); 58 | end 59 | 60 | `FF(clk_cnt_q, clk_cnt_d, '0) 61 | 62 | // The ddr_rcv_clk_o T-Flip-Flop intentionally uses blocking assignments! If we were to use 63 | // non-blocking assignment like we normally do for flip-flops, we would create 64 | // a race condition when sampling data from the fast clock domain into 65 | // flip-flops clocked by ddr_rcv_clk_o. To avoid this, we use blocking assignments 66 | // which is the reccomended method acording to: 67 | // S. Sutherland and D. Mills, 68 | // Verilog and System Verilog gotchas: 101 common coding errors and how to 69 | // avoid them. New York: Springer, 2007. page 64. 70 | 71 | always_ff @(posedge clk_i, negedge rst_ni) begin 72 | if (~rst_ni) begin 73 | ddr_rcv_clk_o = 1'b1; 74 | clk_slow <= 1'b0; 75 | ddr_sel <= 1'b0; 76 | end else begin 77 | if (clk_enable) begin 78 | if (clk_toggle) begin 79 | ddr_rcv_clk_o = !ddr_rcv_clk_o; 80 | end 81 | if (clk_slow_toggle) begin 82 | clk_slow <= !clk_slow; 83 | ddr_sel <= !ddr_sel; 84 | end 85 | end else begin 86 | ddr_rcv_clk_o = 1'b1; 87 | clk_slow <= 1'b0; 88 | ddr_sel <= 1'b0; 89 | end 90 | end 91 | end 92 | 93 | ///////////////// 94 | // DDR OUT // 95 | ///////////////// 96 | `FF(data_out_q, data_out_i, '0, clk_slow, rst_ni) 97 | 98 | if (EnDdr) begin : gen_ddr_mode 99 | assign ddr_o = (ddr_sel)? data_out_q[NumLanes-1:0] : data_out_q[NumLanes*2-1:NumLanes]; 100 | end else begin : gen_sdr_mode 101 | assign ddr_o = data_out_q; 102 | end 103 | 104 | endmodule 105 | 106 | // Impelements a single RX channel which samples the data with the received clock 107 | // Synchronizes the data with the System clock with a CDC 108 | module serial_link_physical_rx #( 109 | parameter int NumLanes = 8, 110 | parameter int FifoDepth = 8, 111 | parameter int CdcSyncStages = 2, 112 | parameter bit EnDdr = 1'b1, 113 | parameter type phy_data_t = logic 114 | ) ( 115 | input logic clk_i, 116 | input logic rst_ni, 117 | input logic ddr_rcv_clk_i, 118 | output phy_data_t data_in_o, 119 | output logic data_in_valid_o, 120 | input logic data_in_ready_i, 121 | input logic [NumLanes-1:0] ddr_i 122 | ); 123 | phy_data_t data_in; 124 | logic [NumLanes-1:0] ddr_q; 125 | 126 | /////////////////////////////// 127 | // CLOCK DOMAIN CROSSING // 128 | /////////////////////////////// 129 | 130 | cdc_fifo_gray #( 131 | .T ( phy_data_t ), 132 | .LOG_DEPTH ( $clog2(FifoDepth) + CdcSyncStages ), 133 | .SYNC_STAGES ( CdcSyncStages ) 134 | ) i_cdc_in ( 135 | .src_clk_i ( ddr_rcv_clk_i ), 136 | .src_rst_ni ( rst_ni ), 137 | .src_data_i ( data_in ), 138 | .src_valid_i ( 1'b1 ), 139 | .src_ready_o ( ), 140 | 141 | .dst_clk_i ( clk_i ), 142 | .dst_rst_ni ( rst_ni ), 143 | .dst_data_o ( data_in_o ), 144 | .dst_valid_o ( data_in_valid_o ), 145 | .dst_ready_i ( data_in_ready_i ) 146 | ); 147 | 148 | // TODO: Fix assertion during reset 149 | // `ASSERT(CdcRxFifoFull, !(i_cdc_in.src_valid_i & ~i_cdc_in.src_ready_o), ddr_rcv_clk_i, !rst_ni) 150 | 151 | //////////////// 152 | // DDR IN // 153 | //////////////// 154 | if (EnDdr) begin : gen_ddr_mode 155 | always_ff @(negedge ddr_rcv_clk_i, negedge rst_ni) begin 156 | if (!rst_ni) begin 157 | ddr_q <= 0; 158 | end else begin 159 | ddr_q <= ddr_i; 160 | end 161 | end 162 | assign data_in = {ddr_i, ddr_q}; 163 | end else begin : gen_sdr_mode 164 | assign data_in = ddr_i; 165 | end 166 | endmodule 167 | 168 | // Implements the Physical Layer of the Serial Link 169 | // The number of Channels and Lanes per Channel is parametrizable 170 | module serial_link_physical #( 171 | // Number of Wires in one channel 172 | parameter int NumLanes = 8, 173 | // Fifo Depth of CDC, dependent on 174 | // Num Credit for Flow control 175 | parameter int FifoDepth = 8, 176 | // Maximum factor of ClkDiv 177 | parameter int MaxClkDiv = 32, 178 | // Enable DDR mode 179 | parameter bit EnDdr = 1'b1, 180 | // Data input type of the PHY 181 | parameter type phy_data_t = logic, 182 | // Clock division type 183 | parameter type clk_div_t = logic [$clog2(MaxClkDiv):0] 184 | ) ( 185 | input logic clk_i, 186 | input logic rst_ni, 187 | input clk_div_t clk_div_i, 188 | input clk_div_t clk_shift_start_i, 189 | input clk_div_t clk_shift_end_i, 190 | input logic ddr_rcv_clk_i, 191 | output logic ddr_rcv_clk_o, 192 | input phy_data_t data_out_i, 193 | input logic data_out_valid_i, 194 | output logic data_out_ready_o, 195 | output phy_data_t data_in_o, 196 | output logic data_in_valid_o, 197 | input logic data_in_ready_i, 198 | input logic [NumLanes-1:0] ddr_i, 199 | output logic [NumLanes-1:0] ddr_o 200 | ); 201 | 202 | //////////////// 203 | // PHY TX // 204 | //////////////// 205 | serial_link_physical_tx #( 206 | .NumLanes ( NumLanes ), 207 | .EnDdr ( EnDdr ), 208 | .phy_data_t ( phy_data_t ), 209 | .clk_div_t ( clk_div_t ) 210 | ) i_serial_link_physical_tx ( 211 | .rst_ni, 212 | .clk_i, 213 | .clk_div_i, 214 | .clk_shift_start_i, 215 | .clk_shift_end_i, 216 | .data_out_i, 217 | .data_out_valid_i, 218 | .data_out_ready_o, 219 | .ddr_rcv_clk_o, 220 | .ddr_o 221 | ); 222 | 223 | //////////////// 224 | // PHY RX // 225 | //////////////// 226 | serial_link_physical_rx #( 227 | .NumLanes ( NumLanes ), 228 | .FifoDepth ( FifoDepth ), 229 | .EnDdr ( EnDdr ), 230 | .phy_data_t ( phy_data_t ) 231 | ) i_serial_link_physical_rx ( 232 | .clk_i, 233 | .rst_ni, 234 | .ddr_rcv_clk_i, 235 | .data_in_o, 236 | .data_in_valid_o, 237 | .data_in_ready_i, 238 | .ddr_i 239 | ); 240 | 241 | 242 | endmodule 243 | -------------------------------------------------------------------------------- /src/serial_link_pkg.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2022 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Author: Tim Fischer 6 | 7 | /// A simple serial link package 8 | package serial_link_pkg; 9 | 10 | // Physical Layer parameters 11 | // Also modify in serial_link.hjson! 12 | localparam int NumChannels = 38; 13 | localparam int NumLanes = 8; 14 | 15 | // Number of outstanding transactions for flow control 16 | // FIFO depths depend on it -> expensive to increase! 17 | localparam int NumCredits = 8; 18 | 19 | // Number of words in the pattern sequence that can be sent out at once 20 | // Must be smaller than RecvFifoDepth 21 | localparam int RawModeFifoDepth = 8; 22 | 23 | // Maximum Clock division 24 | localparam int MaxClkDiv = 1024; 25 | 26 | // Also modify the "NumBit", "TX_PHY_CLK_START" and "TX_PHY_CLK_END parameter 27 | //accordingly in serial_link.hjson or serial_link_single_channel.hjson, respectively 28 | localparam bit EnDdr = 1'b1; //by default 1, set to 0 for SDR 29 | 30 | typedef logic [NumLanes*(1+EnDdr)-1:0] phy_data_t; 31 | typedef logic [NumLanes-1:0] phy_ddr_data_t; 32 | 33 | typedef enum logic [1:0] {LinkSendIdle, LinkSendBusy} link_state_e; 34 | 35 | // TODO(zarubaf,fschuiki) ICEBOX: Fix 36 | // B beats are generated on the write side without the knowledge of the 37 | // receiver. Hence we also have no means to see whether writes actually 38 | // succeeded. 39 | typedef enum logic [3:0] { 40 | TagIdle = 0, 41 | TagAW = 1, 42 | TagW = 2, 43 | TagAR = 3, 44 | TagR = 4 45 | } tag_e; 46 | 47 | typedef logic [$clog2(NumCredits):0] credit_t; 48 | 49 | // PHY FSM 50 | typedef enum logic [2:0] { 51 | PHYIdle = 0, 52 | PHYBusy = 1, 53 | PHYTrain = 2, 54 | PHYTrainWaitPeer = 3, 55 | PHYTrainWaitIdle = 4 56 | } phy_state_e; 57 | 58 | function automatic int find_max_channel(input int channel[5]); 59 | int max_value = 0; 60 | for (int i = 0; i < 5; i++) begin 61 | if (max_value < channel[i]) max_value = channel[i]; 62 | end 63 | return max_value; 64 | endfunction 65 | 66 | endpackage : serial_link_pkg 67 | -------------------------------------------------------------------------------- /src/serial_link_synth_wrapper.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2022 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Authors: 6 | // - Tim Fischer 7 | // - Roman Marquart 8 | 9 | `include "axi/assign.svh" 10 | `include "axi/typedef.svh" 11 | `include "register_interface/assign.svh" 12 | `include "register_interface/typedef.svh" 13 | 14 | module serial_link_synth_wrapper #( 15 | parameter type axi_req_t = logic, 16 | parameter type axi_rsp_t = logic, 17 | parameter type aw_chan_t = logic, 18 | parameter type ar_chan_t = logic, 19 | parameter type r_chan_t = logic, 20 | parameter type w_chan_t = logic, 21 | parameter type b_chan_t = logic, 22 | parameter type cfg_req_t = logic, 23 | parameter type cfg_rsp_t = logic, 24 | parameter int NumChannels = 1, 25 | parameter int NumLanes = 8, 26 | parameter int MaxClkDiv = 1024 27 | ) ( 28 | input logic clk_i, 29 | input logic rst_ni, 30 | input logic clk_reg_i, 31 | input logic rst_reg_ni, 32 | input logic testmode_i, 33 | input axi_req_t axi_in_req_i, 34 | output axi_rsp_t axi_in_rsp_o, 35 | output axi_req_t axi_out_req_o, 36 | input axi_rsp_t axi_out_rsp_i, 37 | input cfg_req_t cfg_req_i, 38 | output cfg_rsp_t cfg_rsp_o, 39 | input logic [NumChannels-1:0] ddr_rcv_clk_i, 40 | output logic [NumChannels-1:0] ddr_rcv_clk_o, 41 | input logic [NumChannels-1:0][NumLanes-1:0] ddr_i, 42 | output logic [NumChannels-1:0][NumLanes-1:0] ddr_o 43 | ); 44 | 45 | if (NumChannels > 1) begin : gen_multi_channel_serial_link 46 | serial_link #( 47 | .axi_req_t ( axi_req_t ), 48 | .axi_rsp_t ( axi_rsp_t ), 49 | .aw_chan_t ( aw_chan_t ), 50 | .w_chan_t ( w_chan_t ), 51 | .b_chan_t ( b_chan_t ), 52 | .ar_chan_t ( ar_chan_t ), 53 | .r_chan_t ( r_chan_t ), 54 | .cfg_req_t ( cfg_req_t ), 55 | .cfg_rsp_t ( cfg_rsp_t ), 56 | .hw2reg_t ( serial_link_reg_pkg::serial_link_hw2reg_t ), 57 | .reg2hw_t ( serial_link_reg_pkg::serial_link_reg2hw_t ), 58 | .NumChannels ( NumChannels ), 59 | .NumLanes ( NumLanes ), 60 | .MaxClkDiv ( MaxClkDiv ) 61 | ) i_serial_link ( 62 | .clk_i ( clk_i ), 63 | .rst_ni ( rst_ni ), 64 | .clk_sl_i ( clk_i ), 65 | .rst_sl_ni ( rst_ni ), 66 | .clk_reg_i ( clk_reg_i ), 67 | .rst_reg_ni ( rst_reg_ni ), 68 | .testmode_i ( testmode_i ), 69 | .axi_in_req_i ( axi_in_req_i ), 70 | .axi_in_rsp_o ( axi_in_rsp_o ), 71 | .axi_out_req_o ( axi_out_req_o ), 72 | .axi_out_rsp_i ( axi_out_rsp_i ), 73 | .cfg_req_i ( cfg_req_i ), 74 | .cfg_rsp_o ( cfg_rsp_o ), 75 | .ddr_rcv_clk_i ( ddr_rcv_clk_i ), 76 | .ddr_rcv_clk_o ( ddr_rcv_clk_o ), 77 | .ddr_i ( ddr_i ), 78 | .ddr_o ( ddr_o ), 79 | .isolated_i ( '0 ), 80 | .isolate_o ( ), 81 | .clk_ena_o ( ), 82 | .reset_no ( ) 83 | ); 84 | end else begin : gen_single_channel_serial_link 85 | serial_link #( 86 | .axi_req_t ( axi_req_t ), 87 | .axi_rsp_t ( axi_rsp_t ), 88 | .aw_chan_t ( aw_chan_t ), 89 | .w_chan_t ( w_chan_t ), 90 | .b_chan_t ( b_chan_t ), 91 | .ar_chan_t ( ar_chan_t ), 92 | .r_chan_t ( r_chan_t ), 93 | .cfg_req_t ( cfg_req_t ), 94 | .cfg_rsp_t ( cfg_rsp_t ), 95 | .hw2reg_t ( serial_link_single_channel_reg_pkg::serial_link_single_channel_hw2reg_t ), 96 | .reg2hw_t ( serial_link_single_channel_reg_pkg::serial_link_single_channel_reg2hw_t ), 97 | .NumChannels ( NumChannels ), 98 | .NumLanes ( NumLanes ), 99 | .MaxClkDiv ( MaxClkDiv ) 100 | ) i_serial_link ( 101 | .clk_i ( clk_i ), 102 | .rst_ni ( rst_ni ), 103 | .clk_sl_i ( clk_i ), 104 | .rst_sl_ni ( rst_ni ), 105 | .clk_reg_i ( clk_reg_i ), 106 | .rst_reg_ni ( rst_reg_ni ), 107 | .testmode_i ( testmode_i ), 108 | .axi_in_req_i ( axi_in_req_i ), 109 | .axi_in_rsp_o ( axi_in_rsp_o ), 110 | .axi_out_req_o ( axi_out_req_o ), 111 | .axi_out_rsp_i ( axi_out_rsp_i ), 112 | .cfg_req_i ( cfg_req_i ), 113 | .cfg_rsp_o ( cfg_rsp_o ), 114 | .ddr_rcv_clk_i ( ddr_rcv_clk_i ), 115 | .ddr_rcv_clk_o ( ddr_rcv_clk_o ), 116 | .ddr_i ( ddr_i ), 117 | .ddr_o ( ddr_o ), 118 | .isolated_i ( '0 ), 119 | .isolate_o ( ), 120 | .clk_ena_o ( ), 121 | .reset_no ( ) 122 | ); 123 | end 124 | 125 | endmodule 126 | 127 | 128 | module serial_link_synth_wrapper_intf #( 129 | //AXI parameters 130 | parameter int unsigned AxiAddrWidth = 32, 131 | parameter int unsigned AxiDataWidth = 64, 132 | parameter int unsigned AxiIdWidth = 8, 133 | parameter int unsigned AxiUserWidth = 8, 134 | // Regbus parameters 135 | parameter int unsigned RegAddrWidth = 32, 136 | parameter int unsigned RegDataWidth = 32, 137 | // Serial link configuration 138 | parameter int unsigned NumChannels = 1, 139 | parameter int unsigned NumLanes = 8, 140 | parameter int unsigned MaxClkDiv = 1024 141 | ) ( 142 | input logic clk_i, 143 | input logic rst_ni, 144 | input logic clk_reg_i, 145 | input logic rst_reg_ni, 146 | input logic testmode_i, 147 | // axi in 148 | AXI_BUS.Slave axi_slv_in, 149 | // axi out 150 | AXI_BUS.Master axi_mst_out, 151 | // cfg reg 152 | REG_BUS.in cfg_reg, 153 | // link 154 | output logic [NumChannels*NumLanes-1:0] ddr_o, 155 | input logic [NumChannels*NumLanes-1:0] ddr_i, 156 | output logic [NumChannels-1:0] ddr_rcv_clk_o, 157 | input logic [NumChannels-1:0] ddr_rcv_clk_i 158 | ); 159 | 160 | localparam int unsigned AxiStrbWidth = AxiDataWidth / 8; 161 | localparam int unsigned RegStrbWidth = RegDataWidth / 8; 162 | 163 | typedef logic [AxiAddrWidth-1:0] axi_addr_t; 164 | typedef logic [AxiIdWidth-1:0] axi_id_t; 165 | typedef logic [AxiDataWidth-1:0] axi_data_t; 166 | typedef logic [AxiStrbWidth-1:0] axi_strb_t; 167 | typedef logic [AxiUserWidth-1:0] axi_user_t; 168 | 169 | `AXI_TYPEDEF_ALL(axi, axi_addr_t, axi_id_t, axi_data_t, axi_strb_t, axi_user_t) 170 | axi_req_t axi_in_req_i, axi_out_req_o; 171 | axi_resp_t axi_in_rsp_o, axi_out_rsp_i; 172 | 173 | `AXI_ASSIGN_TO_REQ(axi_in_req_i, axi_slv_in) 174 | `AXI_ASSIGN_FROM_RESP(axi_slv_in, axi_in_rsp_o) 175 | 176 | `AXI_ASSIGN_FROM_REQ(axi_mst_out, axi_out_req_o) 177 | `AXI_ASSIGN_TO_RESP(axi_out_rsp_i, axi_mst_out) 178 | 179 | typedef logic [RegAddrWidth-1:0] cfg_addr_t; 180 | typedef logic [RegDataWidth-1:0] cfg_data_t; 181 | typedef logic [RegStrbWidth-1:0] cfg_strb_t; 182 | 183 | // Regbus Types 184 | `REG_BUS_TYPEDEF_ALL(cfg, cfg_addr_t, cfg_data_t, cfg_strb_t) 185 | cfg_req_t cfg_req_i; 186 | cfg_rsp_t cfg_rsp_o; 187 | 188 | `REG_BUS_ASSIGN_TO_REQ(cfg_req_i, cfg_reg) 189 | `REG_BUS_ASSIGN_FROM_RSP(cfg_reg, cfg_rsp_o) 190 | 191 | serial_link_synth_wrapper #( 192 | .axi_req_t(axi_req_t), 193 | .axi_rsp_t(axi_resp_t), 194 | .aw_chan_t(axi_aw_chan_t), 195 | .ar_chan_t(axi_ar_chan_t), 196 | .r_chan_t (axi_r_chan_t), 197 | .w_chan_t (axi_w_chan_t), 198 | .b_chan_t (axi_b_chan_t), 199 | .cfg_req_t(cfg_req_t), 200 | .cfg_rsp_t(cfg_rsp_t), 201 | .NumChannels(NumChannels), 202 | .NumLanes (NumLanes), 203 | .MaxClkDiv (MaxClkDiv) 204 | ) i_serial_link_synth_wrapper ( 205 | .clk_i(clk_i), 206 | .rst_ni(rst_ni), 207 | .clk_reg_i(clk_reg_i), 208 | .rst_reg_ni(rst_reg_ni), 209 | .testmode_i(testmode_i), 210 | .axi_in_req_i(axi_in_req_i), 211 | .axi_in_rsp_o(axi_in_rsp_o), 212 | .axi_out_req_o(axi_out_req_o), 213 | .axi_out_rsp_i(axi_out_rsp_i), 214 | .cfg_req_i(cfg_req_i), 215 | .cfg_rsp_o(cfg_rsp_o), 216 | .ddr_o(ddr_o), 217 | .ddr_i(ddr_i), 218 | .ddr_rcv_clk_o(ddr_rcv_clk_o), 219 | .ddr_rcv_clk_i(ddr_rcv_clk_i) 220 | ); 221 | 222 | endmodule 223 | -------------------------------------------------------------------------------- /test/axi_channel_compare.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2022 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Authors: 6 | // - Thomas Benz 7 | // - Paul Scheffler 8 | // - Tim Fischer 9 | 10 | module axi_channel_compare #( 11 | parameter type aw_chan_t = logic, 12 | parameter type w_chan_t = logic, 13 | parameter type b_chan_t = logic, 14 | parameter type ar_chan_t = logic, 15 | parameter type r_chan_t = logic, 16 | parameter type req_t = logic, 17 | parameter type resp_t = logic 18 | )( 19 | input logic clk_a_i, 20 | input logic clk_b_i, 21 | input req_t axi_a_req, 22 | input resp_t axi_a_res, 23 | input req_t axi_b_req, 24 | input resp_t axi_b_res 25 | ); 26 | 27 | function automatic void print_aw ( 28 | input aw_chan_t aw_expected, 29 | input aw_chan_t aw_received 30 | ); 31 | // verilog_lint: waive-start line-length 32 | $display("AW | expected | received "); 33 | $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); 34 | $display("id: | %64d | %64d", aw_expected.id, aw_received.id); 35 | $display("addr: | %64x | %64x", aw_expected.addr, aw_received.addr); 36 | $display("len: | %64d | %64d", aw_expected.len, aw_received.len); 37 | $display("size: | %64d | %64d", aw_expected.size, aw_received.size); 38 | $display("burst: | %64d | %64d", aw_expected.burst, aw_received.burst); 39 | $display("lock: | %64d | %64d", aw_expected.lock, aw_received.lock); 40 | $display("cache: | %64d | %64d", aw_expected.cache, aw_received.cache); 41 | $display("prot: | %64d | %64d", aw_expected.prot, aw_received.prot); 42 | $display("qos: | %64d | %64d", aw_expected.qos, aw_received.qos); 43 | $display("region: | %64d | %64d", aw_expected.region, aw_received.region); 44 | $display("user: | %64d | %64d", aw_expected.user, aw_received.user); 45 | $display("atop: | %64d | %64d", aw_expected.atop, aw_received.atop); 46 | $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); 47 | // verilog_lint: waive-stop line-length 48 | endfunction 49 | 50 | function automatic void print_ar ( 51 | input ar_chan_t ar_expected, 52 | input ar_chan_t ar_received 53 | ); 54 | // verilog_lint: waive-start line-length 55 | $display("AR | expected | received "); 56 | $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); 57 | $display("id: | %64d | %64d", ar_expected.id, ar_received.id); 58 | $display("addr: | %64x | %64x", ar_expected.addr, ar_received.addr); 59 | $display("len: | %64d | %64d", ar_expected.len, ar_received.len); 60 | $display("size: | %64d | %64d", ar_expected.size, ar_received.size); 61 | $display("burst: | %64d | %64d", ar_expected.burst, ar_received.burst); 62 | $display("lock: | %64d | %64d", ar_expected.lock, ar_received.lock); 63 | $display("cache: | %64d | %64d", ar_expected.cache, ar_received.cache); 64 | $display("prot: | %64d | %64d", ar_expected.prot, ar_received.prot); 65 | $display("qos: | %64d | %64d", ar_expected.qos, ar_received.qos); 66 | $display("region: | %64d | %64d", ar_expected.region, ar_received.region); 67 | $display("user: | %64d | %64d", ar_expected.user, ar_received.user); 68 | $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); 69 | // verilog_lint: waive-stop line-length 70 | endfunction 71 | 72 | function automatic void print_w ( 73 | input w_chan_t w_expected, 74 | input w_chan_t w_received 75 | ); 76 | // verilog_lint: waive-start line-length 77 | $display("W | expected | received "); 78 | $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); 79 | $display("data: | %64x | %64x", w_expected.data, w_received.data); 80 | $display("strb: | %64d | %64d", w_expected.strb, w_received.strb); 81 | $display("last: | %64d | %64d", w_expected.last, w_received.last); 82 | $display("user: | %64d | %64d", w_expected.user, w_received.user); 83 | $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); 84 | // verilog_lint: waive-stop line-length 85 | endfunction 86 | 87 | function automatic void print_b ( 88 | input b_chan_t b_expected, 89 | input b_chan_t b_received 90 | ); 91 | // verilog_lint: waive-start line-length 92 | $display("B | expected | received "); 93 | $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); 94 | $display("id: | %64d | %64d", b_expected.id, b_received.id); 95 | $display("resp: | %64d | %64d", b_expected.resp, b_received.resp); 96 | $display("user: | %64d | %64d", b_expected.user, b_received.user); 97 | $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); 98 | // verilog_lint: waive-stop line-length 99 | endfunction 100 | 101 | function automatic void print_r ( 102 | input r_chan_t r_expected, 103 | input r_chan_t r_received 104 | ); 105 | // verilog_lint: waive-start line-length 106 | $display("R | expected | received "); 107 | $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); 108 | $display("id: | %64d | %64d", r_expected.id, r_received.id); 109 | $display("data: | %64x | %64x", r_expected.data, r_received.data); 110 | $display("resp: | %64d | %64d", r_expected.resp, r_received.resp); 111 | $display("last: | %64d | %64d", r_expected.last, r_received.last); 112 | $display("user: | %64d | %64d", r_expected.user, r_received.user); 113 | $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); 114 | // verilog_lint: waive-stop line-length 115 | endfunction 116 | 117 | // queues 118 | aw_chan_t aw_queue [$]; 119 | w_chan_t w_queue [$]; 120 | b_chan_t b_queue [$]; 121 | ar_chan_t ar_queue [$]; 122 | r_chan_t r_queue [$]; 123 | 124 | // requests generated at axi A: enqueue elements 125 | always_ff @(posedge clk_a_i) begin : proc_enqueue_a 126 | // aw 127 | if (axi_a_req.aw_valid & axi_a_res.aw_ready) 128 | aw_queue.push_back(axi_a_req.aw); 129 | // w 130 | if (axi_a_req.w_valid & axi_a_res.w_ready) 131 | w_queue.push_back(axi_a_req.w); 132 | // ar 133 | if (axi_a_req.ar_valid & axi_a_res.ar_ready) 134 | ar_queue.push_back(axi_a_req.ar); 135 | end 136 | 137 | // responses generated at axi B: enqueue elements 138 | always_ff @(posedge clk_b_i) begin : proc_enqueue_b 139 | // b 140 | if (axi_b_res.b_valid & axi_b_req.b_ready) 141 | b_queue.push_back(axi_b_res.b); 142 | // r 143 | if (axi_b_res.r_valid & axi_b_req.r_ready) 144 | r_queue.push_back(axi_b_res.r); 145 | end 146 | 147 | // requests arriving at axi B from A: dequeue elements and check 148 | always_ff @(posedge clk_b_i) begin : proc_dequeue_and_check_b 149 | // aw 150 | if (axi_b_req.aw_valid & axi_b_res.aw_ready) begin 151 | automatic aw_chan_t aw; 152 | if (aw_queue.size() == 0) $error("AW queue is empty!"); 153 | aw = aw_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking 154 | if (axi_b_req.aw !== aw) begin 155 | $error("AW mismatch!"); 156 | print_aw(aw, axi_b_req.aw); 157 | end 158 | end 159 | // w 160 | if (axi_b_req.w_valid & axi_b_res.w_ready) begin 161 | automatic w_chan_t w; 162 | if (w_queue.size() == 0) $error("W queue is empty!"); 163 | w = w_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking 164 | if (axi_b_req.w !== w) begin 165 | $error("W mismatch!"); 166 | print_w(w, axi_b_req.w); 167 | end 168 | end 169 | // ar 170 | if (axi_b_req.ar_valid & axi_b_res.ar_ready) begin 171 | automatic ar_chan_t ar; 172 | if (ar_queue.size() == 0) $error("AR queue is empty!"); 173 | ar = ar_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking 174 | if (axi_b_req.ar !== ar) begin 175 | $error("AR mismatch!"); 176 | print_ar(ar, axi_b_req.ar); 177 | end 178 | end 179 | end 180 | 181 | // responses arriving at axi A from B: dequeue elements and check 182 | always_ff @(posedge clk_a_i) begin : proc_dequeue_and_check_a 183 | // b 184 | if (axi_a_res.b_valid & axi_a_req.b_ready) begin 185 | automatic b_chan_t b; 186 | if (b_queue.size() == 0) $error("B queue is empty!"); 187 | b = b_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking 188 | if (axi_a_res.b !== b) begin 189 | $error("B mismatch!"); 190 | print_b(b, axi_a_res.b); 191 | end 192 | end 193 | // r 194 | if (axi_a_res.r_valid & axi_a_req.r_ready) begin 195 | automatic r_chan_t r; 196 | if (r_queue.size() == 0) $error("R queue is empty!"); 197 | r = r_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking 198 | if (axi_a_res.r !== r) begin 199 | $error("R mismatch!"); 200 | print_r(r, axi_a_res.r); 201 | end 202 | end 203 | end 204 | 205 | endmodule : axi_channel_compare 206 | -------------------------------------------------------------------------------- /test/tb_stream_chopper.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2022 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Author: Manuel Eggimann 6 | module tb_stream_chopper; 7 | 8 | localparam time Tck = 200ns; 9 | localparam int unsigned ChannelCount = 32; 10 | localparam int unsigned Log2ChannelCount = $clog2(ChannelCount); 11 | localparam int unsigned RstClkCycles = 1; 12 | localparam int unsigned MaxWaitCycles = 0; 13 | localparam int unsigned TestLength = 10; 14 | localparam int unsigned NumWords = 1000; 15 | localparam int unsigned AutoFlushWaitCount = 16; 16 | 17 | typedef logic [15:0] element_t; 18 | typedef element_t[ChannelCount-1:0] payload_t; 19 | 20 | typedef stream_test::stream_driver #( 21 | .payload_t (payload_t), 22 | .TA (Tck*0.2), 23 | .TT (Tck*0.8) 24 | ) stream_driver_t; 25 | 26 | logic clk, rstn; 27 | logic sim_done = 0; 28 | 29 | logic [Log2ChannelCount-1:0] cfg_chopsize; 30 | logic cfg_auto_flush_en; 31 | logic [5:0] cfg_auto_flush_count; 32 | logic flush; 33 | logic bypass_en; 34 | logic [ChannelCount-1:0] valid_out; 35 | 36 | STREAM_DV #(.payload_t(payload_t)) master_bus(clk); 37 | STREAM_DV #(.payload_t(payload_t)) slave_bus(clk); 38 | 39 | stream_driver_t master_driver = new(master_bus); 40 | stream_driver_t slave_driver = new(slave_bus); 41 | 42 | element_t send_queue[$]; 43 | 44 | int sent_count = 0; 45 | int rcv_count = 0; 46 | 47 | initial begin : proc_source 48 | automatic payload_t word; 49 | automatic int wait_cycl; 50 | @(posedge rstn); 51 | master_driver.reset_in(); 52 | 53 | // Enable auto-flush feature with 16 wait cycles 54 | cfg_auto_flush_en = 1'b1; 55 | cfg_auto_flush_count = 16; 56 | flush = 1'b0; 57 | 58 | // Test bypassing 59 | $info("Testing bypass mode..."); 60 | bypass_en = 1'b1; 61 | cfg_chopsize = ChannelCount; 62 | for (int i = 0; i < NumWords; i++) begin 63 | assert(std::randomize(word)) else 64 | $error("Randomization failed"); 65 | master_driver.send(word); 66 | // Push the word element by element into the queue 67 | for (int i = 0; i < ChannelCount; i++) begin 68 | send_queue.push_back(word[i]); 69 | sent_count++; 70 | end 71 | // Randomly delay sending of next item 72 | wait_cycl = $urandom_range(0, MaxWaitCycles); 73 | repeat(wait_cycl) @(posedge clk); 74 | end 75 | // Wait a couple of cycles for the final elements to sink 76 | repeat(MaxWaitCycles+AutoFlushWaitCount+3) @(posedge clk); 77 | // Verify we received every elementp 78 | assert(rcv_count == sent_count) else 79 | $error("Did not receive the correct number of elements. Expected %0d but was %0d.", 80 | sent_count,rcv_count); 81 | rcv_count = 0; 82 | sent_count = 0; 83 | $info("Bypass test finished"); 84 | 85 | // Test with different output chop sizes 86 | bypass_en = 1'b0; 87 | for (int j = 0; j < TestLength; j++) begin 88 | cfg_chopsize = $urandom_range(1, ChannelCount-1); 89 | $info("Testing with chopsize %0d...", cfg_chopsize); 90 | for (int i = 0; i < NumWords; i++) begin 91 | assert(std::randomize(word)) else 92 | $error("Randomization failed"); 93 | // Push the word element by element into the queue 94 | for (int i = 0; i < ChannelCount; i++) begin 95 | send_queue.push_back(word[i]); 96 | sent_count++; 97 | end 98 | // Apply word to chopper 99 | master_driver.send(word); 100 | // Randomly delay sending of next item 101 | wait_cycl = $urandom_range(0, MaxWaitCycles); 102 | repeat(wait_cycl) @(posedge clk); 103 | end 104 | // Wait a couple of cycles for the final elements to sink 105 | repeat(MaxWaitCycles+AutoFlushWaitCount+3) @(posedge clk); 106 | // Verify we received every element 107 | assert(rcv_count == sent_count) else 108 | $error("Did not receive the correct number of elements. Expected %0d but was %0d.", 109 | sent_count,rcv_count); 110 | rcv_count = 0; 111 | sent_count = 0; 112 | $info("Test done"); 113 | end 114 | sim_done = 1'b1; 115 | end 116 | 117 | initial begin : proc_sink 118 | automatic int unsigned wait_cycl; 119 | automatic payload_t rcv_word; 120 | automatic element_t expected_element; 121 | @(posedge rstn); 122 | slave_driver.reset_out(); 123 | 124 | forever begin 125 | wait_cycl = $urandom_range(0, MaxWaitCycles); 126 | repeat(wait_cycl) @(posedge clk); 127 | slave_driver.recv(rcv_word); 128 | for (int i = 0; i < ChannelCount; i++) begin 129 | if (bypass_en | i < cfg_chopsize) begin 130 | if (send_queue.size() > 0) begin 131 | expected_element = send_queue.pop_front(); 132 | assert(expected_element == rcv_word[i]) else 133 | $error("Received %0h but expected %0h.", rcv_word[i], expected_element); 134 | assert(valid_out[i] == 1'b1) else 135 | $error("The valid signal correpsonding to element idx %0d was not asserted.",i); 136 | rcv_count++; 137 | end else begin 138 | assert(valid_out[i] == 1'b0) else 139 | $error("Valid signal corresponding to invalid element idx %0d", i, 140 | "of flushed (partial) output word should not be assertd"); 141 | end 142 | end else begin 143 | assert(valid_out[i] == 1'b0) else 144 | $error("The valid signal correpsonding to element idx %0d should not be asserted.",i); 145 | end 146 | 147 | end 148 | end 149 | end 150 | 151 | 152 | initial begin : proc_stop_sim 153 | @(posedge rstn); 154 | wait (&sim_done); 155 | repeat (20) @(posedge clk); 156 | $display("Sim done."); 157 | $stop(); 158 | end 159 | 160 | 161 | 162 | 163 | // system clock and reset 164 | clk_rst_gen #( 165 | .ClkPeriod ( Tck ), 166 | .RstClkCycles ( RstClkCycles ) 167 | ) i_clk_rst_gen_reg ( 168 | .clk_o ( clk ), 169 | .rst_no ( rstn ) 170 | ); 171 | 172 | stream_chopper #( 173 | .element_t ( element_t ), 174 | .Width ( ChannelCount ) 175 | ) i_dut ( 176 | .clk_i ( clk ), 177 | .rst_ni ( rstn ), 178 | .bypass_en_i ( bypass_en ), 179 | .flush_i ( flush ), 180 | .cfg_auto_flush_en_i ( cfg_auto_flush_en ), 181 | .cfg_auto_flush_count_i ( cfg_auto_flush_count ), 182 | .cfg_chopsize_i ( cfg_chopsize ), 183 | .data_i ( master_bus.data ), 184 | .valid_i ( master_bus.valid ), 185 | .ready_o ( master_bus.ready ), 186 | .data_o ( slave_bus.data ), 187 | .valid_o ( valid_out ), 188 | .ready_i ( slave_bus.ready ) 189 | ); 190 | 191 | assign slave_bus.valid = |valid_out; 192 | 193 | endmodule 194 | -------------------------------------------------------------------------------- /test/tb_stream_chopper_dechopper.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2022 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Author: Manuel Eggimann 6 | module tb_stream_chopper_dechopper; 7 | 8 | localparam time Tck = 200ns; 9 | localparam int unsigned ChannelCount = 32; 10 | localparam int unsigned Log2ChannelCount = $clog2(ChannelCount); 11 | localparam int unsigned RstClkCycles = 1; 12 | localparam int unsigned MaxWaitCycles = 0; 13 | localparam int unsigned TestLength = 100; 14 | localparam int unsigned NumWords = 1000; 15 | localparam int unsigned AutoFlushWaitCount = 16; 16 | 17 | typedef logic [15:0] element_t; 18 | typedef element_t[ChannelCount-1:0] payload_t; 19 | 20 | typedef stream_test::stream_driver #( 21 | .payload_t (payload_t), 22 | .TA (Tck*0.2), 23 | .TT (Tck*0.8) 24 | ) stream_driver_t; 25 | 26 | logic clk, rstn; 27 | logic sim_done = 0; 28 | 29 | logic [Log2ChannelCount-1:0] cfg_chopsize; 30 | logic cfg_auto_flush_en; 31 | logic [5:0] cfg_auto_flush_count; 32 | logic flush; 33 | logic bypass_en; 34 | logic [ChannelCount-1:0] chopper2dechoper_valid; 35 | 36 | STREAM_DV #(.payload_t(payload_t)) source_bus(clk); 37 | STREAM_DV #(.payload_t(payload_t)) chopper2dechopper_bus(clk); 38 | STREAM_DV #(.payload_t(payload_t)) sink_bus(clk); 39 | 40 | stream_driver_t source_driver = new(source_bus); 41 | stream_driver_t sink_driver = new(sink_bus); 42 | 43 | payload_t send_queue[$]; 44 | 45 | int sent_count = 0; 46 | int rcv_count = 0; 47 | 48 | initial begin : proc_source 49 | automatic payload_t word; 50 | automatic int wait_cycl; 51 | @(posedge rstn); 52 | source_driver.reset_in(); 53 | 54 | // Enable auto-flush feature with 16 wait cycles 55 | cfg_auto_flush_en = 1'b1; 56 | cfg_auto_flush_count = 16; 57 | flush = 1'b0; 58 | 59 | // Test bypassing 60 | $info("Testing bypass mode..."); 61 | bypass_en = 1'b1; 62 | cfg_chopsize = ChannelCount; 63 | for (int i = 0; i < NumWords; i++) begin 64 | assert(std::randomize(word)) else 65 | $error("Randomization failed"); 66 | // Push the word element by element into the queue 67 | send_queue.push_back(word); 68 | sent_count++; 69 | source_driver.send(word); 70 | // Randomly delay sending of next item 71 | wait_cycl = $urandom_range(0, MaxWaitCycles); 72 | repeat(wait_cycl) @(posedge clk); 73 | end 74 | // Wait a couple of cycles for the final elements to sink 75 | repeat(MaxWaitCycles+AutoFlushWaitCount+3) @(posedge clk); 76 | // Verify we received every elementp 77 | assert(rcv_count == sent_count) else 78 | $error("Did not receive the correct number of elements. Expected %0d but was %0d.", 79 | sent_count,rcv_count); 80 | rcv_count = 0; 81 | sent_count = 0; 82 | $info("Bypass test finished"); 83 | 84 | // Test with different output chop sizes 85 | bypass_en = 1'b0; 86 | for (int j = 0; j < TestLength; j++) begin 87 | cfg_chopsize = $urandom_range(1, ChannelCount-1); 88 | $info("Testing with chopsize %0d...", cfg_chopsize); 89 | for (int i = 0; i < NumWords; i++) begin 90 | assert(std::randomize(word)) else 91 | $error("Randomization failed"); 92 | // Push the word element by element into the queue 93 | send_queue.push_back(word); 94 | sent_count++; 95 | // Apply word to chopper 96 | source_driver.send(word); 97 | // Randomly delay sending of next item 98 | wait_cycl = $urandom_range(0, MaxWaitCycles); 99 | repeat(wait_cycl) @(posedge clk); 100 | end 101 | // Wait a couple of cycles for the final elements to sink 102 | repeat(MaxWaitCycles+AutoFlushWaitCount+4) @(posedge clk); 103 | // Verify we received every element 104 | assert(rcv_count == sent_count) else 105 | $error("Did not receive the correct number of elements. Expected %0d but was %0d.", 106 | sent_count,rcv_count); 107 | rcv_count = 0; 108 | sent_count = 0; 109 | $info("Test done"); 110 | end 111 | 112 | sim_done = 1'b1; 113 | end 114 | 115 | initial begin : proc_sink 116 | automatic int unsigned wait_cycl; 117 | automatic payload_t rcv_word; 118 | automatic payload_t expected_word; 119 | @(posedge rstn 120 | ); 121 | sink_driver.reset_out(); 122 | 123 | forever begin 124 | wait_cycl = $urandom_range(0, MaxWaitCycles); 125 | repeat(wait_cycl) @(posedge clk); 126 | sink_driver.recv(rcv_word); 127 | expected_word = send_queue.pop_front(); 128 | assert(expected_word == rcv_word) else 129 | $error("Received %0h but expected %0h.", rcv_word, expected_word); 130 | rcv_count++; 131 | end 132 | end 133 | 134 | 135 | initial begin : proc_stop_sim 136 | @(posedge rstn); 137 | wait (&sim_done); 138 | repeat (20) @(posedge clk); 139 | $display("Sim done."); 140 | $stop(); 141 | end 142 | 143 | 144 | 145 | 146 | // system clock and reset 147 | clk_rst_gen #( 148 | .ClkPeriod ( Tck ), 149 | .RstClkCycles ( RstClkCycles ) 150 | ) i_clk_rst_gen_reg ( 151 | .clk_o ( clk ), 152 | .rst_no ( rstn ) 153 | ); 154 | 155 | stream_chopper #( 156 | .element_t ( element_t ), 157 | .Width ( ChannelCount ) 158 | ) i_chopper ( 159 | .clk_i ( clk ), 160 | .rst_ni ( rstn ), 161 | .bypass_en_i ( bypass_en ), 162 | .flush_i ( flush ), 163 | .cfg_auto_flush_en_i ( cfg_auto_flush_en ), 164 | .cfg_auto_flush_count_i ( cfg_auto_flush_count ), 165 | .cfg_chopsize_i ( cfg_chopsize ), 166 | .data_i ( source_bus.data ), 167 | .valid_i ( source_bus.valid ), 168 | .ready_o ( source_bus.ready ), 169 | .data_o ( chopper2dechopper_bus.data ), 170 | .valid_o ( chopper2dechoper_valid ), 171 | .ready_i ( chopper2dechopper_bus.ready ) 172 | ); 173 | assign chopper2dechopper_bus.valid = |chopper2dechoper_valid; 174 | 175 | stream_dechopper #( 176 | .element_t ( element_t ), 177 | .Width ( ChannelCount ) 178 | ) i_dechopper( 179 | .clk_i ( clk ), 180 | .rst_ni ( rstn ), 181 | .bypass_en_i ( bypass_en ), 182 | .valid_i ( chopper2dechoper_valid ), 183 | .ready_o ( chopper2dechopper_bus.ready ), 184 | .data_i ( chopper2dechopper_bus.data ), 185 | .valid_o ( sink_bus.valid ), 186 | .ready_i ( sink_bus.ready ), 187 | .data_o ( sink_bus.data ) 188 | ); 189 | 190 | 191 | endmodule 192 | -------------------------------------------------------------------------------- /util/check-license: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2020 ETH Zurich and University of Bologna. 3 | # Solderpad Hardware License, Version 0.51, see LICENSE for details. 4 | # SPDX-License-Identifier: SHL-0.51 5 | 6 | set -e 7 | ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 8 | 9 | echo $ROOT 10 | $ROOT/util/lowrisc_misc-linters/licence-checker/licence-checker.py --config $ROOT/util/licence-checker.hjson -------------------------------------------------------------------------------- /util/licence-checker.hjson: -------------------------------------------------------------------------------- 1 | // Copyright lowRISC contributors. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | { 5 | // We cover ETH Zurich and lowRISC licenses and Apache 2.0 (mostly for SW) 6 | // and Solderpad for the hardware. 7 | licence: 8 | ''' 9 | Copyright (\d{4}(-\d{4})?\s)?(ETH Zurich and University of Bologna|lowRISC contributors). 10 | (Solderpad Hardware License, Version 0.51|Licensed under the Apache License, Version 2.0), see LICENSE for details. 11 | SPDX-License-Identifier: (SHL-0.51|Apache-2.0) 12 | ''', 13 | match_regex: 'true', 14 | exclude_paths: [ 15 | // Exclude anything in vendored directories 16 | 'util/lowrisc_misc-linters/*', 17 | // Generated by reggen 18 | 'src/regs/**/*.sv' 19 | // Wave files 20 | util/serial_link_wave.do 21 | ], 22 | } -------------------------------------------------------------------------------- /util/lowrisc_misc-linters/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing code to the misc-linters repository 2 | 3 | ## Contributor License Agreement 4 | 5 | Contributions to misc-linters must be accompanied by sign-off text that indicates 6 | acceptance of the Contributor License Agreement (see [CLA](CLA) for full 7 | text), which is closely derived from the Apache Individual Contributor License 8 | Agreement. The sign-off text must be included once per commit, in the commit 9 | message. The sign-off can be automatically inserted using a command such as 10 | `git commit -s`, which will generate the text in the form: 11 | `Signed-off-by: Random J Developer ` 12 | 13 | By adding this sign-off, you are certifying: 14 | 15 | _By signing-off on this submission, I agree to be bound by the terms of the 16 | Contributor License Agreement located at the root of the project repository, 17 | and I agree that this submission constitutes a "Contribution" under that 18 | Agreement._ 19 | 20 | Please note that this project and any contributions to it are public and that 21 | a record of all contributions (including any personal information submitted 22 | with it, including a sign-off) is maintained indefinitely and may be 23 | redistributed consistent with this project or the open source license(s) 24 | involved. 25 | 26 | ## Quick guidelines 27 | 28 | * Keep a clean commit history. This means no merge commits, and no long series 29 | of "fixup" patches (rebase or squash as appropriate). Structure work as a 30 | series of logically ordered, atomic patches. `git rebase -i` is your friend. 31 | * Changes should be made via pull request, with review. Do not commit until 32 | you've had an explicit "looks good to me". We don't yet have, but plan to 33 | create a policy describing code owners and the like. In the meantime use your 34 | best judgement. If you're submitting a change against something that was 90% 35 | authored by a single person, you'll want to get their ACK before committing. 36 | * When changes are restricted to a specific area, you are recommended to add a 37 | tag to the beginning of the first line of the commit message in square 38 | brackets. e.g. "[UART] Fix bug #157". 39 | * Code review is not design review and doesn't remove the need for discussing 40 | implementation options. If you would like to make a large-scale change or 41 | discuss multiple implementation options, discuss on the mailing list. 42 | * Create pull requests from a fork rather than making new branches in 43 | `github.com/lowrisc/misc-linters`. 44 | * Do not force push. 45 | * Do not attempt to commit code with a non-Apache license without discussing 46 | first. 47 | * If a relevant bug or tracking issue exists, reference it in the pull request 48 | and commits. 49 | -------------------------------------------------------------------------------- /util/lowrisc_misc-linters/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /util/lowrisc_misc-linters/README.md: -------------------------------------------------------------------------------- 1 | # lowRISC's Miscellaneous Linters 2 | 3 | ## About the project 4 | 5 | These are linters and checkers, usually for source code, which we have written 6 | for various lowRISC projects. 7 | 8 | They include 9 | * `licence-checker/licence-checker.py` which will ensure source code in your 10 | repository contains correctly formatted licence headers. This is designed to 11 | be reused: it is configured with `licence-checker.hjson`. 12 | 13 | ## How to contribute 14 | 15 | Have a look at [CONTRIBUTING](./CONTRIBUTING.md) for guidelines on how to 16 | contribute code to this repository. 17 | 18 | ## Licensing 19 | 20 | Unless otherwise noted, everything in this repository is covered by the Apache 21 | License, Version 2.0 (see [LICENSE](./LICENSE) for full text). 22 | -------------------------------------------------------------------------------- /util/lowrisc_misc-linters/licence-checker/README.md: -------------------------------------------------------------------------------- 1 | # Licence Checker 2 | 3 | This script can check most text file formats that we have checked our 4 | repositories, though it has some limitations. It ensures the entire licence 5 | appears on consecutive lines in the first comment in the file, and those lines 6 | contain nothing else. 7 | 8 | The primary limitation of the checker is that each file suffix can only match 9 | one comment style, which is used for checking for the licence header. In text 10 | formats which accept multiple comment styles, there is now a canonical one that 11 | the licence must use. Where available, the licence should use a line comment 12 | style. 13 | 14 | The other limitation is for files where the canonical style is block comments, 15 | like `/* */`, each line must be wrapped in the comment prefix and suffix, rather 16 | than the whole licence header being wrapped in a single comment prefix and 17 | suffix. This is an artefact of how the checker searches for the licence. 18 | 19 | The checker is configured using a hjson file, which contains the exact licence 20 | header, and a list of file patterns to exclude from checking the licence for, 21 | which is used to exclude vendored and other externally sourced files. 22 | Additionally, the licence checker can be configured to use regex matching using 23 | the `match_regex` key set to "true". 24 | 25 | # Configuration Example 26 | 27 | ``` 28 | { 29 | // Licence to check against. 30 | licence: 31 | ''' 32 | Copyright lowRISC contributors. 33 | Licensed under the Apache License, Version 2.0, see LICENSE for details. 34 | SPDX-License-Identifier: Apache-2.0 35 | ''', 36 | // Optionally match the licence using regex (can be left off). 37 | // The default is not to use regex. 38 | match_regex: 'false', 39 | // Don't consider those paths and files when checking 40 | // for the licence (can contain wildcards such as `*`). 41 | exclude_paths: [ 42 | '.style.yapf', 43 | ], 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /util/lowrisc_misc-linters/requirements.txt: -------------------------------------------------------------------------------- 1 | # Copyright lowRISC contributors. 2 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | hjson 6 | tabulate 7 | yapf 8 | 9 | -------------------------------------------------------------------------------- /util/serial_link_wave.tcl: -------------------------------------------------------------------------------- 1 | # Copyright 2023 ETH Zurich and University of Bologna. 2 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | onerror {resume} 6 | quietly WaveActivateNextPane {} 0 7 | 8 | set tb_name tb_axi_serial_link 9 | 10 | for {set i 1} {$i < 3} {incr i} { 11 | set group_name "DDR $i" 12 | set num_channels [expr {[llength [find instances -bydu serial_link_physical -recursive]] / 2}] 13 | if {$num_channels == 1} { 14 | set gen_block_name gen_single_channel_serial_link 15 | } else { 16 | set gen_block_name gen_multi_channel_serial_link 17 | } 18 | 19 | add wave -noupdate -expand -group $group_name -ports /$tb_name/i_serial_link_$i/$gen_block_name/i_serial_link/* 20 | 21 | add wave -noupdate -group $group_name -group {NETWORK} /$tb_name/i_serial_link_$i/$gen_block_name/i_serial_link/i_serial_link_network/* 22 | 23 | add wave -noupdate -group $group_name -group {LINK} /$tb_name/i_serial_link_$i/$gen_block_name/i_serial_link/i_serial_link_data_link/* 24 | 25 | if {$num_channels > 1} { 26 | add wave -noupdate -group $group_name -group {CHANNEL_ALLOCATOR} -ports /$tb_name/i_serial_link_$i/$gen_block_name/i_serial_link/gen_channel_alloc/i_channel_allocator/* 27 | } 28 | 29 | for {set c 0} {$c < $num_channels} {incr c} { 30 | add wave -noupdate -group $group_name -group PHY -group TX -group CH$c /$tb_name/i_serial_link_$i/$gen_block_name/i_serial_link/gen_phy_channels[$c]/i_serial_link_physical/i_serial_link_physical_tx/* 31 | add wave -noupdate -group $group_name -group PHY -group RX -group CH$c /$tb_name/i_serial_link_$i/$gen_block_name/i_serial_link/gen_phy_channels[$c]/i_serial_link_physical/i_serial_link_physical_rx/* 32 | } 33 | 34 | add wave -noupdate -group $group_name -group {CONFIG} /$tb_name/i_serial_link_$i/$gen_block_name/i_serial_link/reg2hw 35 | add wave -noupdate -group $group_name -group {CONFIG} /$tb_name/i_serial_link_$i/$gen_block_name/i_serial_link/hw2reg 36 | add wave -noupdate -group $group_name -group {CONFIG} /$tb_name/i_serial_link_$i/$gen_block_name/i_serial_link/cfg_req 37 | add wave -noupdate -group $group_name -group {CONFIG} /$tb_name/i_serial_link_$i/$gen_block_name/i_serial_link/cfg_rsp 38 | } 39 | 40 | TreeUpdate [SetDefaultTree] 41 | quietly wave cursor active 1 42 | configure wave -namecolwidth 220 43 | configure wave -valuecolwidth 110 44 | configure wave -justifyvalue left 45 | configure wave -signalnamewidth 1 46 | configure wave -snapdistance 10 47 | configure wave -datasetprefix 0 48 | configure wave -rowmargin 4 49 | configure wave -childrowmargin 2 50 | configure wave -gridoffset 0 51 | configure wave -gridperiod 1 52 | configure wave -griddelta 40 53 | configure wave -timeline 0 54 | configure wave -timelineunits ns 55 | update 56 | -------------------------------------------------------------------------------- /util/verible.waiver: -------------------------------------------------------------------------------- 1 | # Copyright 2022 ETH Zurich and University of Bologna. 2 | # Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | # SPDX-License-Identifier: SHL-0.51 4 | 5 | # This rule does not apply to flip-flops that drive other FF's clock pin. There non-blocking assingments can cause race conditions! 6 | waive --rule=always-ff-non-blocking --regex="ddr_rcv_clk_o = " --location="src/serial_link_physical.sv" 7 | # The AXIS package is waived to stay compatible with other AXIS IPs 8 | waive --rule=parameter-name-style --location="src/axis/*" 9 | waive --rule=interface-name-style --location="src/axis/*" 10 | waive --rule=explicit-parameter-storage-type --location="src/axis/*" 11 | waive --rule=explicit-function-task-parameter-type --location="src/axis/*" 12 | # Auto-generated configuration registers are waived 13 | waive --rule=typedef-structs-unions --location="src/regs/*" 14 | waive --rule=line-length --location="src/regs/*" 15 | --------------------------------------------------------------------------------