├── .gitignore ├── .gitlab-ci.yml ├── Bender.lock ├── Bender.yml ├── CHANGELOG.md ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── VERSION ├── docs ├── axi_hyper.svg ├── wavedrom_input.png └── wavedrom_input.txt ├── models ├── README.md ├── configurable_delay.behav.sv └── configurable_delay.fpga.sv ├── scripts └── start.tcl ├── src ├── hyperbus.sv ├── hyperbus_axi.sv ├── hyperbus_cfg_regs.sv ├── hyperbus_clk_gen.sv ├── hyperbus_clock_diff_out.sv ├── hyperbus_ddr_out.sv ├── hyperbus_delay.sv ├── hyperbus_phy.sv ├── hyperbus_phy2r.sv ├── hyperbus_phy_if.sv ├── hyperbus_pkg.sv ├── hyperbus_stub.sv ├── hyperbus_synth_wrap.sv ├── hyperbus_trx.sv └── hyperbus_w2phy.sv └── test ├── axi_hyper_tb.sv ├── dut_if.sv ├── fixture_hyperbus.sv └── hyperbus_tb.sv /.gitignore: -------------------------------------------------------------------------------- 1 | .bender 2 | scripts/compile.tcl 3 | models/s27ks0641 -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 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 | # Luca Valente 6 | 7 | before_script: 8 | - export PATH=/home/ci-pulp/:$PATH 9 | - cp -r /home/ci-pulp/s27ks0641/ ./models 10 | 11 | stages: 12 | - compile 13 | - simulate-simple 14 | - simulate-random 15 | 16 | vsim-compile: 17 | stage: compile 18 | script: 19 | - make clean 20 | - bender update 21 | - make scripts/compile.tcl 22 | - make build 23 | - (! grep -n "Error:" transcript) 24 | - (! grep -n "Fatal:" transcript) 25 | artifacts: 26 | expire_in: 1 week 27 | paths: 28 | - work 29 | 30 | vsim-simple: 31 | stage: simulate-simple 32 | needs: 33 | - vsim-compile 34 | script: 35 | - vsim -c hyperbus_tb -t 1ps -voptargs=+acc -classdebug -logfile hyperbus.vsim.log -do "run -all" 36 | - (! grep -n "Error:" hyperbus.vsim.log) 37 | - (! grep -n "Fatal:" hyperbus.vsim.log) 38 | - (! grep -n "Unexpected" hyperbus.vsim.log) 39 | artifacts: 40 | paths: 41 | - hyperbus.vsim.log 42 | 43 | vsim-random: 44 | stage: simulate-random 45 | needs: 46 | - vsim-compile 47 | script: 48 | - vsim -c axi_hyper_tb -t 1ps -voptargs=+acc -classdebug -logfile hyperbus-rnd.vsim.log -do "run -all" 49 | - (! grep -n "Error:" hyperbus-rnd.vsim.log) 50 | - (! grep -n "Fatal:" hyperbus-rnd.vsim.log) 51 | - (! grep -n "Unexpected" hyperbus-rnd.vsim.log) 52 | artifacts: 53 | paths: 54 | - hyperbus-rnd.vsim.log 55 | 56 | -------------------------------------------------------------------------------- /Bender.lock: -------------------------------------------------------------------------------- 1 | packages: 2 | apb: 3 | revision: 77ddf073f194d44b9119949d2421be59789e69ae 4 | version: 0.2.4 5 | source: 6 | Git: https://github.com/pulp-platform/apb.git 7 | dependencies: 8 | - common_cells 9 | axi: 10 | revision: fccffb5953ec8564218ba05e20adbedec845e014 11 | version: 0.39.1 12 | source: 13 | Git: https://github.com/pulp-platform/axi.git 14 | dependencies: 15 | - common_cells 16 | - common_verification 17 | - tech_cells_generic 18 | common_cells: 19 | revision: 53b0b58af2db5bd3c850a7038fae170ed78326bb 20 | version: 1.31.1 21 | source: 22 | Git: https://github.com/pulp-platform/common_cells.git 23 | dependencies: 24 | - common_verification 25 | - tech_cells_generic 26 | common_verification: 27 | revision: 9c07fa860593b2caabd9b5681740c25fac04b878 28 | version: 0.2.3 29 | source: 30 | Git: https://github.com/pulp-platform/common_verification.git 31 | dependencies: [] 32 | register_interface: 33 | revision: 146501d80052b61475cdc333d3aab4cd769fd5dc 34 | version: 0.3.9 35 | source: 36 | Git: https://github.com/pulp-platform/register_interface.git 37 | dependencies: 38 | - apb 39 | - axi 40 | - common_cells 41 | tech_cells_generic: 42 | revision: 298b7297d220ba2601d0f24f684f97ff32f61123 43 | version: 0.2.12 44 | source: 45 | Git: https://github.com/pulp-platform/tech_cells_generic.git 46 | dependencies: 47 | - common_verification 48 | -------------------------------------------------------------------------------- /Bender.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 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: hyperbus 7 | authors: 8 | - "Armin Berger " 9 | - "Stephan Keck " 10 | - "Thomas Benz " 11 | - "Paul Scheffler " 12 | - "Luca Valente " # current maintainer 13 | 14 | dependencies: 15 | common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.21.0 } 16 | axi: { git: "https://github.com/pulp-platform/axi.git", version: 0.39.1 } 17 | tech_cells_generic: { git: "https://github.com/pulp-platform/tech_cells_generic.git", version: 0.2.4 } 18 | register_interface: { git: "https://github.com/pulp-platform/register_interface.git", version: 0.3.2 } 19 | 20 | sources: 21 | files: 22 | - target: all(fpga,xilinx) 23 | defines: 24 | FPGA_EMUL: ~ 25 | files: 26 | - models/configurable_delay.fpga.sv 27 | - target: test 28 | files: 29 | - models/configurable_delay.behav.sv 30 | - src/hyperbus_pkg.sv 31 | - src/hyperbus_clk_gen.sv 32 | - src/hyperbus_clock_diff_out.sv 33 | - src/hyperbus_w2phy.sv 34 | - src/hyperbus_phy2r.sv 35 | - src/hyperbus_ddr_out.sv 36 | - src/hyperbus_delay.sv 37 | - src/hyperbus_trx.sv 38 | - src/hyperbus_cfg_regs.sv 39 | - src/hyperbus_phy.sv 40 | - src/hyperbus_phy_if.sv 41 | - src/hyperbus_axi.sv 42 | - target: hyper_test 43 | files: 44 | # Device models. TODO: extend 45 | - models/s27ks0641/s27ks0641.v 46 | # Testbench 47 | - test/fixture_hyperbus.sv 48 | - test/hyperbus_tb.sv 49 | - test/dut_if.sv 50 | - test/axi_hyper_tb.sv 51 | - src/hyperbus.sv 52 | -------------------------------------------------------------------------------- /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 | ## 0.0.1 - 2023-02-23 8 | 9 | Initial release of HyperBus v2. 10 | 11 | ### IP's history 12 | 13 | - *February to June 2018*: Development of HyperBus v1 for [Scarabaeus](http://asic.ethz.ch/2018/Scarabaeus.html) as part of the *Kerbin* project - [IP home](https://iis-git.ee.ethz.ch/bergerar/hyperbus). 14 | - *February to June 2021*: Cleanup and redesign to HyperBus v2 - [IP home](https://iis-git.ee.ethz.ch/bslk/hyperbus). 15 | - *2021/2022*: Various improvements and additions 16 | - *February 2023*: Release 17 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Global owners 2 | * @paulsc96 @thommythomaso @luca-valente 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guide 2 | 3 | We are happy to accept pull requests and issues from any contributors. Please 4 | note that we try to maintain a consistent quality standard. For a quick overview 5 | please find some of the most important points below. 6 | 7 | ## Quick Overview 8 | 9 | * Keep a clean commit history. This means no merge commits, and no long series 10 | of "fixup" patches (rebase or squash as appropriate). Structure work as a 11 | series of logically ordered, atomic patches. `git rebase -i` is your friend. 12 | * Changes should only be made via pull request, with review. A pull request will 13 | be committed by a "committer" (an account listed in `CODEOWNERS`) once it has 14 | had an explicit positive review. 15 | * When changes are restricted to a specific area, you are recommended to add a 16 | tag to the beginning of the first line of the commit message in square 17 | brackets e.g., "uart: Fix bug #157". 18 | * Areas which are not clear by their name alone (e.g., "vendor") should be 19 | separated by a slash. For example "hw/vendor: Add patch directory". 20 | * Create pull requests from a fork rather than making new branches in 21 | `github.com/pulp_platform/hyperbus`. 22 | * Do not force push. 23 | * Do not attempt to commit code with a non-Solderpad license without discussing 24 | first. 25 | * If a relevant bug or tracking issue exists, reference it in the pull request 26 | and commits. 27 | 28 | ## Git Considerations 29 | 30 | * Separate subject from body with a blank line 31 | * Limit the subject line to 100 characters 32 | * Capitalize the subject line 33 | * Do not end the subject line with a period 34 | * Use the imperative mood in the subject line 35 | * Use the body to explain what and why vs. how 36 | 37 | For further information please see the excellent 38 | [guide](https://chris.beams.io/posts/git-commit/) by Chris Beams. 39 | 40 | ## Code Style 41 | 42 | Consistent code style is important. We try to follow existing style conventions 43 | as much as possible: 44 | 45 | * For RTL we use [lowRISC's SystemVerilog style 46 | guidelines](https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md). 47 | * For C/C++ we follow [Google's style 48 | guide](https://google.github.io/styleguide/cppguide.html), pleas note the 49 | `clang-format` in the projects root. 50 | * For Python code we follow the official [`pep8` style 51 | guide](https://www.python.org/dev/peps/pep-0008/). 52 | * For `yaml` files (such as the `Bender.yml`) we format using `ymlfmt`. 53 | Unfortunately, we do not know whether an official style guide exists. 54 | 55 | Please make sure that any code you submit is adhering to the correct standard. 56 | -------------------------------------------------------------------------------- /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 2023 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 | # Paul Scheffler 6 | 7 | GIT ?= git 8 | BENDER ?= bender 9 | VSIM ?= vsim 10 | 11 | all: build run 12 | 13 | clean: sim_clean 14 | 15 | # Ensure half-built targets are purged 16 | .DELETE_ON_ERROR: 17 | 18 | ifdef gui 19 | VSIM_ARGS := -do 20 | else 21 | VSIM_ARGS := -c -do 22 | endif 23 | 24 | # -------------- 25 | # RTL SIMULATION 26 | # -------------- 27 | 28 | VLOG_ARGS += -suppress vlog-2583 -suppress vlog-13314 -suppress vlog-13233 -timescale \"1 ns / 1 ps\" 29 | XVLOG_ARGS += -64bit -compile -vtimescale 1ns/1ns -quiet 30 | 31 | define generate_vsim 32 | echo 'set ROOT [file normalize [file dirname [info script]]/$3]' > $1 33 | bender script $(VSIM) --vlog-arg="$(VLOG_ARGS)" $2 | grep -v "set ROOT" >> $1 34 | echo >> $1 35 | endef 36 | 37 | sim_all: scripts/compile.tcl 38 | 39 | sim_clean: 40 | rm -rf scripts/compile.tcl 41 | rm -rf work 42 | 43 | # Download (partially non-free) simulation models from publically available sources; 44 | # by running these targets or targets depending on them, you accept this (see README.md). 45 | models/s27ks0641: 46 | mkdir -p $@ 47 | rm -rf model_tmp && mkdir model_tmp 48 | cd model_tmp; wget --no-check-certificate --content-disposition "https://www.infineon.com/dgdl/Infineon-S27KL0641_S27KS0641_VERILOG-SimulationModels-v05_00-EN.zip?fileId=8ac78c8c7d0d8da4017d0f6349a14f68&da=t" 49 | mv model_tmp/*.zip model_tmp/model.zip 50 | cd model_tmp; unzip model.zip 51 | cd model_tmp; mv 'S27KL0641 S27KS0641' exe_folder 52 | cd model_tmp/exe_folder; unzip S27ks0641.exe 53 | cp model_tmp/exe_folder/S27ks0641/model/s27ks0641.v $@ 54 | cp model_tmp/exe_folder/S27ks0641/model/s27ks0641_verilog.sdf models/s27ks0641/s27ks0641.sdf 55 | rm -rf model_tmp 56 | 57 | scripts/compile.tcl: Bender.yml models/s27ks0641 58 | $(call generate_vsim, $@, -t rtl -t test -t hyper_test,..) 59 | 60 | build: scripts/compile.tcl 61 | $(VSIM) -c -do "source scripts/compile.tcl; exit" 62 | 63 | run: clean build 64 | $(VSIM) $(VSIM_ARGS) "source scripts/start.tcl" 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HyperBus v2 2 | 3 | This peripheral implements an AXI4-compliant interface for the HyperBus protocol, described [in its specification](https://www.cypress.com/file/213356/download). HyperBus v2 is part of the [PULP (Parallel Ultra-Low-Power) platform](https://pulp-platform.org/). 4 | 5 | Hyperbus is mainly used for off-chip memories (HyperFlash and HyperRAM), but also supported by some generic peripherals. 6 | 7 | ## Features 8 | 9 | The AXI data widths are parameterizable from 16 to 1024, with transfers of all sizes possible. It is also possible to decide how many memories to connect on the same HyperBUS to set the overall available memory. 10 | 11 | Multiple memories on the same bus are placed contiguously in the address memory map and selected through their dedicated CS. At runtime, one can communicate to the controller the size 12 | of the HyperRAMs, and the controller will demultiplex the transactions accordingly. Also, one can choose how many HyperBUS interfaces (1 or 2) to expose. Both buses will have the same number of CSs. 13 | When exposing 2 HyperBUSes, the pair of memories on the same chip select will be mapped as interleaved: each memory will be seen as a memory block of 16-bit width. Doing so will double the maximum achievable bandwidth, up to 6.4 Gbps, 14 | doubling the pin count. 15 | 16 | The main restrictions are as follows: 17 | 18 | - Atomics are not supported. 19 | - Only linear bursts are supported. 20 | - All accesses except byte-size accesses must be aligned to 16-bit boundaries. 21 | - Only communication with HyperRAMs has been tested. Support for flash is WIP. 22 | 23 | The address width is also fully parameterizable. We support bursts of any size permitted by AXI and stalling through PHY-level clock stopping and protocol-level burst splitting. We do _not_ buffer bursts to support devices without the clock stop feature; please ensure your device supports clock stop or ensure sufficient buffering upstream. 24 | 25 | The configuration register interface uses the minimal Regbus protocol. Data and address widths are parameterizable, but register sizes must be a power of two larger than 16 bits. 26 | 27 | ## Architecture 28 | 29 | The block diagram below outlines the approximate architecture. Note that there are *two* internally provided clock domains,`clk_sys` and `clk_phy`, as well as an incoming clock `clk_rwds_in` which is then internally delayed. 30 | The `clk_phy` and `clk_phy_90` have a 90 degree difference in phase. To obtain the two clocks one can either shift the input `clk_phy_i` with the clock delayer or to use the `ddr_clk` module that halves the frequency and generated 4 90-degree shifted clocks. 31 | 32 | ![HyperBus v2 block diagram](./docs/axi_hyper.svg) 33 | 34 | ## Simulation 35 | 36 | † * `models/s27ks0641/s27ks0641.v` will download externally provided peripheral simulation models, some proprietary and with non-free license terms, from their publically accessible sources; see `Makefile` for details. By running `models/s27ks0641/s27ks0641.v` or the default target `run`, you accept this.* 37 | 38 | To run a simulation you need [Bender](https://github.com/pulp-platform/bender) and Questasim. Export your path to include your bender binary, and then: 39 | 40 | ```bash 41 | bender update 42 | make run #(will download proprietary models from Infineon !!) 43 | ``` 44 | 45 | ## ToDos 46 | 47 | - [ ] Support byte-aligned accesses for non-byte-size transfer 48 | - [ ] Test HyperFlash 49 | - [ ] PSRAM support through additional CA decoder 50 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.0.1 2 | -------------------------------------------------------------------------------- /docs/wavedrom_input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulp-platform/hyperbus/2a14bd8f9a985b488ee23d240764f52f129f7729/docs/wavedrom_input.png -------------------------------------------------------------------------------- /docs/wavedrom_input.txt: -------------------------------------------------------------------------------- 1 | {signal: [ 2 | {name: 'rwds_i', wave: '0p..' , period:4 , node:'....'}, 3 | {name: 'rwds_delayed', wave: '0....1.0.1.0.1.0', node:'.....B...'}, 4 | {name: 'reg_d=dq_i', wave: 'z...3.4.5.6.x...', data: ['A0', 'A1', 'B0', 'B1'], node:'....A.H..' }, 5 | {name: 'fifo_in[15:8]=reg_q', wave: 'z.....3...5...x..', data: ['A0', 'B0'] , phase: 0.5, node:'......C......'}, 6 | {name: 'rwds_delayed_n', wave: '1....0.1.0.1.0.1', node:'.......s..'}, 7 | {name: 'fifo_in[7:0]=dq_i', wave: 'z.3456x.', period:2, data: ['A0', 'A1', 'B0', 'B1'], node:'...EO' } , 8 | 9 | ], 10 | edge: [ 11 | 'A|->B ts', 'b|g','B~C ', 'C|->s ts', 'E|->s ts', 'B-|H th', 's-|O th' 12 | ] 13 | 14 | } 15 | -------------------------------------------------------------------------------- /models/README.md: -------------------------------------------------------------------------------- 1 | # Simulation Models 2 | 3 | This folder is just a placeholder. The verilog model of the Spansion s27ks0641 ram is proprietary code of CypressSemiconductor Corporation, which can currently be downloaded from [here](https://www.infineon.com/dgdl/Infineon-S27KL0641_S27KS0641_VERILOG-SimulationModels-v05_00-EN.zip?fileId=8ac78c8c7d0d8da4017d0f6349a14f68) 4 | 5 | Once the package has been downloaded, the following steps are necessary to integrate the model in the platform: 6 | 7 | 1. unzip it (it might be necessary to rename it 260016.zip first) 8 | 2. execute s27ks0641.exe 9 | 3. move the newly created `s27ks0641.v` file in `models/s27ks0641/s27ks0641.v`, according to the `Bender.yml` 10 | 11 | ## Vendor Specific Peripherals 12 | 13 | - `s27ks0641`: Cypress, HyperBus pseudo SRAM model/self-refresh DRAM (8/16 MByte) 14 | 15 | ## Generic Delay 16 | 17 | - `generic_delay_D4_O1_3P750_CG0.behav.sv`: Delay macro with 4-bit delay control, 1 output, 3.75 ns total delay, no clock gate 18 | -------------------------------------------------------------------------------- /models/configurable_delay.behav.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Thomas Benz 6 | // Paul Scheffler 7 | // 8 | // Based on work of: 9 | // Fabian Schuiki 10 | // Florian Zaruba 11 | 12 | 13 | // Automatically generated by the Generic Delay generator. 14 | `timescale 1ps/1ps 15 | 16 | (* no_ungroup *) 17 | (* no_boundary_optimization *) 18 | module configurable_delay #( 19 | parameter int unsigned NUM_STEPS, // The desired number of delay taps. Must be 20 | // a power of 2. Don't use very large values 21 | // here, otherwise strategy to just let STA 22 | // (with the right SDC) do the job for us 23 | // will not work. 24 | localparam DELAY_SEL_WIDTH = $clog2(NUM_STEPS) 25 | ) ( 26 | input logic clk_i, 27 | input logic enable_i, 28 | input logic [DELAY_SEL_WIDTH-1:0] delay_i, 29 | output logic clk_o 30 | ); 31 | 32 | logic enable_latched; 33 | logic clk; 34 | 35 | assign clk = clk_i; 36 | 37 | always @(clk) clk_o <= #(real'(delay_i)*3.750ns/15) clk; 38 | 39 | endmodule 40 | -------------------------------------------------------------------------------- /models/configurable_delay.fpga.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Thomas Benz 6 | // Paul Scheffler 7 | // 8 | // Based on work of: 9 | // Fabian Schuiki 10 | // Florian Zaruba 11 | 12 | `timescale 1ps/1ps 13 | 14 | (* no_ungroup *) 15 | (* no_boundary_optimization *) 16 | module configurable_delay #( 17 | parameter int unsigned NUM_STEPS, // The desired number of delay taps. Must be 18 | // a power of 2. Don't use very large values 19 | // here, otherwise strategy to just let STA 20 | // (with the right SDC) do the job for us 21 | // will not work. 22 | localparam DELAY_SEL_WIDTH = $clog2(NUM_STEPS) 23 | ) ( 24 | input logic clk_i, 25 | input logic enable_i, 26 | input logic [DELAY_SEL_WIDTH-1:0] delay_i, 27 | output logic clk_o 28 | ); 29 | 30 | IBUF # 31 | ( 32 | .IBUF_LOW_PWR ("FALSE") 33 | ) u_ibufg_sys_clk_o 34 | ( 35 | .I (clk_i), 36 | .O (clk_o) 37 | ); 38 | 39 | endmodule 40 | -------------------------------------------------------------------------------- /scripts/start.tcl: -------------------------------------------------------------------------------- 1 | # Copyright 2023 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 | vsim axi_hyper_tb -t 1ps -voptargs=+acc -classdebug 6 | 7 | set StdArithNoWarnings 1 8 | set NumericStdNoWarnings 1 9 | log -r /* 10 | 11 | delete wave * 12 | 13 | run -all 14 | -------------------------------------------------------------------------------- /src/hyperbus.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Thomas Benz 6 | // Paul Scheffler 7 | // Luca Valente 8 | 9 | module hyperbus #( 10 | parameter int unsigned NumChips = -1, 11 | parameter int unsigned NumPhys = 2, 12 | parameter int unsigned IsClockODelayed = 0, 13 | parameter int unsigned AxiAddrWidth = -1, 14 | parameter int unsigned AxiDataWidth = -1, 15 | parameter int unsigned AxiIdWidth = -1, 16 | parameter int unsigned AxiUserWidth = -1, 17 | parameter type axi_req_t = logic, 18 | parameter type axi_rsp_t = logic, 19 | parameter type axi_w_chan_t = logic, 20 | parameter type axi_b_chan_t = logic, 21 | parameter type axi_ar_chan_t = logic, 22 | parameter type axi_r_chan_t = logic, 23 | parameter type axi_aw_chan_t = logic, 24 | parameter int unsigned RegAddrWidth = -1, 25 | parameter int unsigned RegDataWidth = -1, 26 | parameter int unsigned MinFreqMHz = 100, 27 | parameter type reg_req_t = logic, 28 | parameter type reg_rsp_t = logic, 29 | parameter type axi_rule_t = logic, 30 | // The below have sensible defaults, but should be set on integration! 31 | parameter int unsigned RxFifoLogDepth = 2, 32 | parameter int unsigned TxFifoLogDepth = 2, 33 | parameter logic [RegDataWidth-1:0] RstChipBase = 'h0, // Base address for all chips 34 | parameter logic [RegDataWidth-1:0] RstChipSpace = 'h1_0000, // 64 KiB: Current maximum HyperBus device size 35 | parameter hyperbus_pkg::hyper_cfg_t RstCfg = hyperbus_pkg::gen_RstCfg(NumPhys,MinFreqMHz), 36 | parameter int unsigned PhyStartupCycles = 300 * 200, /* us*MHz */ // Conservative maximum frequency estimate 37 | parameter int unsigned AxiLogDepth = 3, 38 | parameter int unsigned SyncStages = 2 39 | ) ( 40 | input logic clk_phy_i, 41 | input logic rst_phy_ni, 42 | input logic clk_sys_i, 43 | input logic rst_sys_ni, 44 | input logic test_mode_i, 45 | // AXI bus 46 | input axi_req_t axi_req_i, 47 | output axi_rsp_t axi_rsp_o, 48 | // Reg bus 49 | input reg_req_t reg_req_i, 50 | output reg_rsp_t reg_rsp_o, 51 | // Physical interace: facing HyperBus PADs 52 | output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, 53 | output logic [NumPhys-1:0] hyper_ck_o, 54 | output logic [NumPhys-1:0] hyper_ck_no, 55 | output logic [NumPhys-1:0] hyper_rwds_o, 56 | input logic [NumPhys-1:0] hyper_rwds_i, 57 | output logic [NumPhys-1:0] hyper_rwds_oe_o, 58 | input logic [NumPhys-1:0][7:0] hyper_dq_i, 59 | output logic [NumPhys-1:0][7:0] hyper_dq_o, 60 | output logic [NumPhys-1:0] hyper_dq_oe_o, 61 | output logic [NumPhys-1:0] hyper_reset_no 62 | ); 63 | 64 | typedef struct packed { 65 | logic [(16*NumPhys)-1:0] data; 66 | logic last; 67 | logic [(2*NumPhys)-1:0] strb; // mask data 68 | } hyper_tx_t; 69 | 70 | typedef struct packed { 71 | logic [(16*NumPhys)-1:0] data; 72 | logic last; 73 | logic error; 74 | } hyper_rx_t; 75 | 76 | // Combined transfer type for CDC 77 | typedef struct packed { 78 | hyperbus_pkg::hyper_tf_t trans; 79 | logic [NumChips-1:0] cs; 80 | } tf_cdc_t; 81 | 82 | 83 | logic clk_phy_i_0, clk_phy_i_90, rst_phy; 84 | 85 | // Register file 86 | hyperbus_pkg::hyper_cfg_t cfg; 87 | axi_rule_t [NumChips-1:0] chip_rules; 88 | logic trans_active; 89 | 90 | // AXI slave 91 | hyper_rx_t axi_rx; 92 | logic axi_rx_valid; 93 | logic axi_rx_ready; 94 | hyper_tx_t axi_tx; 95 | logic axi_tx_valid; 96 | logic axi_tx_ready; 97 | logic axi_b_error; 98 | logic axi_b_valid; 99 | logic axi_b_ready; 100 | tf_cdc_t axi_tf_cdc; 101 | logic axi_trans_valid; 102 | logic axi_trans_ready; 103 | 104 | // PHY 105 | hyper_rx_t phy_rx; 106 | logic phy_rx_valid; 107 | logic phy_rx_ready; 108 | hyper_tx_t phy_tx; 109 | logic phy_tx_valid; 110 | logic phy_tx_ready; 111 | logic phy_b_error; 112 | logic phy_b_valid; 113 | logic phy_b_ready; 114 | tf_cdc_t phy_tf_cdc; 115 | logic phy_trans_valid; 116 | logic phy_trans_ready; 117 | 118 | // Config register File 119 | hyperbus_cfg_regs #( 120 | .NumChips ( NumChips ), 121 | .NumPhys ( NumPhys ), 122 | .RegAddrWidth ( RegAddrWidth ), 123 | .RegDataWidth ( RegDataWidth ), 124 | .reg_req_t ( reg_req_t ), 125 | .reg_rsp_t ( reg_rsp_t ), 126 | .rule_t ( axi_rule_t ), 127 | .RstChipBase ( RstChipBase ), 128 | .RstChipSpace ( RstChipSpace ), 129 | .RstCfg ( RstCfg ) 130 | ) i_cfg_regs ( 131 | .clk_i ( clk_sys_i ), 132 | .rst_ni ( rst_sys_ni ), 133 | .reg_req_i ( reg_req_i ), 134 | .reg_rsp_o ( reg_rsp_o ), 135 | .cfg_o ( cfg ), 136 | .chip_rules_o ( chip_rules ), 137 | .trans_active_i ( trans_active ) 138 | ); 139 | 140 | // AXI slave interfacing PHY 141 | hyperbus_axi #( 142 | .AxiDataWidth ( AxiDataWidth ), 143 | .AxiAddrWidth ( AxiAddrWidth ), 144 | .AxiIdWidth ( AxiIdWidth ), 145 | .AxiUserWidth ( AxiUserWidth ), 146 | .axi_req_t ( axi_req_t ), 147 | .axi_rsp_t ( axi_rsp_t ), 148 | .NumChips ( NumChips ), 149 | .NumPhys ( NumPhys ), 150 | .hyper_rx_t ( hyper_rx_t ), 151 | .hyper_tx_t ( hyper_tx_t ), 152 | .rule_t ( axi_rule_t ) 153 | ) i_axi_slave ( 154 | .clk_i ( clk_sys_i ), 155 | .rst_ni ( rst_sys_ni ), 156 | 157 | .axi_req_i ( axi_req_i ), 158 | .axi_rsp_o ( axi_rsp_o ), 159 | 160 | .rx_i ( axi_rx ), 161 | .rx_valid_i ( axi_rx_valid ), 162 | .rx_ready_o ( axi_rx_ready ), 163 | .tx_o ( axi_tx ), 164 | .tx_valid_o ( axi_tx_valid ), 165 | .tx_ready_i ( axi_tx_ready ), 166 | .b_error_i ( axi_b_error ), 167 | .b_valid_i ( axi_b_valid ), 168 | .b_ready_o ( axi_b_ready ), 169 | .trans_o ( axi_tf_cdc.trans ), 170 | .trans_cs_o ( axi_tf_cdc.cs ), 171 | .trans_valid_o ( axi_trans_valid ), 172 | .trans_ready_i ( axi_trans_ready ), 173 | 174 | .chip_rules_i ( chip_rules ), 175 | .which_phy_i ( cfg.which_phy ), 176 | .phys_in_use_i ( cfg.phys_in_use ), 177 | .addr_mask_msb_i ( cfg.address_mask_msb ), 178 | .addr_space_i ( cfg.address_space ), 179 | .trans_active_o ( trans_active ) 180 | ); 181 | 182 | hyperbus_phy_if #( 183 | .IsClockODelayed( IsClockODelayed ), 184 | .NumChips ( NumChips ), 185 | .StartupCycles ( PhyStartupCycles ), 186 | .NumPhys ( NumPhys ), 187 | .hyper_rx_t ( hyper_rx_t ), 188 | .hyper_tx_t ( hyper_tx_t ), 189 | .SyncStages ( SyncStages ) 190 | ) i_phy ( 191 | .clk_i ( clk_phy_i_0 ), 192 | .clk_i_90 ( clk_phy_i_90 ), 193 | .rst_ni ( rst_phy ), 194 | .test_mode_i ( test_mode_i ), 195 | 196 | .cfg_i ( cfg ), 197 | 198 | .rx_o ( phy_rx ), 199 | .rx_valid_o ( phy_rx_valid ), 200 | .rx_ready_i ( phy_rx_ready ), 201 | .tx_i ( phy_tx ), 202 | .tx_valid_i ( phy_tx_valid ), 203 | .tx_ready_o ( phy_tx_ready ), 204 | .b_error_o ( phy_b_error ), 205 | .b_valid_o ( phy_b_valid ), 206 | .b_ready_i ( phy_b_ready ), 207 | .trans_i ( phy_tf_cdc.trans ), 208 | .trans_cs_i ( phy_tf_cdc.cs ), 209 | .trans_valid_i ( phy_trans_valid ), 210 | .trans_ready_o ( phy_trans_ready ), 211 | 212 | .hyper_cs_no ( hyper_cs_no ), 213 | .hyper_ck_o ( hyper_ck_o ), 214 | .hyper_ck_no ( hyper_ck_no ), 215 | .hyper_rwds_o ( hyper_rwds_o ), 216 | .hyper_rwds_i ( hyper_rwds_i ), 217 | .hyper_rwds_oe_o( hyper_rwds_oe_o ), 218 | .hyper_dq_i ( hyper_dq_i ), 219 | .hyper_dq_o ( hyper_dq_o ), 220 | .hyper_dq_oe_o ( hyper_dq_oe_o ), 221 | .hyper_reset_no ( hyper_reset_no ) 222 | ); 223 | 224 | cdc_2phase #( 225 | .T ( tf_cdc_t ) 226 | ) i_cdc_2phase_trans ( 227 | .src_rst_ni ( rst_sys_ni ), 228 | .src_clk_i ( clk_sys_i ), 229 | .src_data_i ( axi_tf_cdc ), 230 | .src_valid_i ( axi_trans_valid ), 231 | .src_ready_o ( axi_trans_ready ), 232 | 233 | .dst_rst_ni ( rst_phy ), 234 | .dst_clk_i ( clk_phy_i_0 ), 235 | .dst_data_o ( phy_tf_cdc ), 236 | .dst_valid_o ( phy_trans_valid ), 237 | .dst_ready_i ( phy_trans_ready ) 238 | ); 239 | 240 | cdc_2phase #( 241 | .T ( logic ) 242 | ) i_cdc_2phase_b ( 243 | .src_rst_ni ( rst_phy ), 244 | .src_clk_i ( clk_phy_i_0 ), 245 | .src_data_i ( phy_b_error ), 246 | .src_valid_i ( phy_b_valid ), 247 | .src_ready_o ( phy_b_ready ), 248 | 249 | .dst_rst_ni ( rst_sys_ni ), 250 | .dst_clk_i ( clk_sys_i ), 251 | .dst_data_o ( axi_b_error ), 252 | .dst_valid_o ( axi_b_valid ), 253 | .dst_ready_i ( axi_b_ready ) 254 | ); 255 | 256 | // Write data, TX CDC FIFO 257 | cdc_fifo_gray #( 258 | .T ( hyper_tx_t ), 259 | .LOG_DEPTH ( TxFifoLogDepth ) 260 | ) i_cdc_fifo_tx ( 261 | .src_rst_ni ( rst_sys_ni ), 262 | .src_clk_i ( clk_sys_i ), 263 | .src_data_i ( axi_tx ), 264 | .src_valid_i ( axi_tx_valid ), 265 | .src_ready_o ( axi_tx_ready ), 266 | 267 | .dst_rst_ni ( rst_phy ), 268 | .dst_clk_i ( clk_phy_i_0 ), 269 | .dst_data_o ( phy_tx ), 270 | .dst_valid_o ( phy_tx_valid ), 271 | .dst_ready_i ( phy_tx_ready ) 272 | ); 273 | 274 | // Read data, RX CDC FIFO 275 | cdc_fifo_gray #( 276 | .T ( hyper_rx_t ), 277 | .LOG_DEPTH ( RxFifoLogDepth ) 278 | ) i_cdc_fifo_rx ( 279 | .src_rst_ni ( rst_phy ), 280 | .src_clk_i ( clk_phy_i_0 ), 281 | .src_data_i ( phy_rx ), 282 | .src_valid_i ( phy_rx_valid ), 283 | .src_ready_o ( phy_rx_ready ), 284 | 285 | .dst_rst_ni ( rst_sys_ni ), 286 | .dst_clk_i ( clk_sys_i ), 287 | .dst_data_o ( axi_rx ), 288 | .dst_valid_o ( axi_rx_valid ), 289 | .dst_ready_i ( axi_rx_ready ) 290 | ); 291 | 292 | // Shift clock by 90 degrees 293 | generate 294 | if(IsClockODelayed==0) begin : clock_generator 295 | hyperbus_clk_gen ddr_clk ( 296 | .clk_i ( clk_phy_i ), 297 | .rst_ni ( rst_phy_ni ), 298 | .clk0_o ( clk_phy_i_0 ), 299 | .clk90_o ( clk_phy_i_90 ), 300 | .clk180_o ( ), 301 | .clk270_o ( ), 302 | .rst_no ( rst_phy ) 303 | ); 304 | end else if (IsClockODelayed==1) begin 305 | assign clk_phy_i_0 = clk_phy_i; 306 | assign rst_phy = rst_phy_ni; 307 | hyperbus_delay i_delay_tx_clk_90 ( 308 | .in_i ( clk_phy_i_0 ), 309 | .delay_i ( cfg.t_tx_clk_delay ), 310 | .out_o ( clk_phy_i_90 ) 311 | ); 312 | end 313 | endgenerate 314 | 315 | endmodule : hyperbus 316 | -------------------------------------------------------------------------------- /src/hyperbus_axi.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Thomas Benz 6 | // Paul Scheffler 7 | 8 | `include "axi/typedef.svh" 9 | `include "common_cells/registers.svh" 10 | 11 | module hyperbus_axi #( 12 | parameter int unsigned AxiDataWidth = -1, 13 | parameter int unsigned AxiAddrWidth = -1, 14 | parameter int unsigned AxiIdWidth = -1, 15 | parameter int unsigned AxiUserWidth = -1, 16 | parameter type axi_req_t = logic, 17 | parameter type axi_rsp_t = logic, 18 | parameter int unsigned NumChips = -1, 19 | parameter int unsigned NumPhys = -1, 20 | parameter type hyper_tx_t = logic, 21 | parameter type hyper_rx_t = logic, 22 | parameter type rule_t = logic 23 | ) ( 24 | input logic clk_i, 25 | input logic rst_ni, 26 | // AXI port 27 | input axi_req_t axi_req_i, 28 | output axi_rsp_t axi_rsp_o, 29 | // PHI port 30 | input hyper_rx_t rx_i, 31 | input logic rx_valid_i, 32 | output logic rx_ready_o, 33 | 34 | output hyper_tx_t tx_o, 35 | output logic tx_valid_o, 36 | input logic tx_ready_i, 37 | 38 | input logic b_error_i, 39 | input logic b_valid_i, 40 | output logic b_ready_o, 41 | 42 | output hyperbus_pkg::hyper_tf_t trans_o, 43 | output logic [NumChips-1:0] trans_cs_o, 44 | output logic trans_valid_o, 45 | input logic trans_ready_i, 46 | 47 | input rule_t [NumChips-1:0] chip_rules_i, 48 | input logic phys_in_use_i, 49 | input logic which_phy_i, 50 | input logic [4:0] addr_mask_msb_i, 51 | input logic addr_space_i, 52 | output logic trans_active_o 53 | ); 54 | 55 | localparam AxiDataBytes = AxiDataWidth/8; 56 | localparam AxiBusAddrWidth = $clog2(AxiDataBytes); 57 | localparam PhyDataWidth = NumPhys*16; 58 | localparam PhyDataBytes = PhyDataWidth/8; 59 | localparam ChipSelWidth = cf_math_pkg::idx_width(NumChips); 60 | localparam ByteCntWidth = cf_math_pkg::idx_width(AxiDataBytes); 61 | 62 | typedef logic [AxiAddrWidth-1:0] axi_addr_t; 63 | typedef logic [ByteCntWidth-1:0] byte_cnt_t; 64 | typedef logic [ByteCntWidth-3:0] word_cnt_t; 65 | typedef logic [AxiDataWidth-1:0] axi_data_t; 66 | typedef logic [ChipSelWidth-1:0] chip_sel_idx_t; 67 | 68 | `AXI_TYPEDEF_ALL_CT(axi_fifo,axi_fifo_req,axi_fifo_rsp,axi_addr_t,logic[AxiIdWidth-1:0],axi_data_t,logic[AxiDataBytes-1:0],logic[AxiUserWidth-1:0]) 69 | 70 | // No need to track ID: serializer buffers it for us 71 | typedef struct packed { 72 | axi_addr_t addr; 73 | axi_pkg::len_t len; 74 | axi_pkg::burst_t burst; 75 | axi_pkg::size_t size; 76 | } axi_ax_t; 77 | 78 | typedef struct packed { 79 | axi_ax_t ax_data; 80 | logic write; 81 | } ax_channel_spill_t; 82 | 83 | typedef struct packed { 84 | logic valid; 85 | axi_data_t data; 86 | logic error; 87 | logic last; 88 | } axi_r_t; 89 | 90 | typedef struct packed { 91 | logic [AxiDataWidth/8-1:0] strb; 92 | axi_data_t data; 93 | logic [AxiUserWidth-1:0] user; 94 | logic last; 95 | } axi_w_chan_t; 96 | 97 | typedef struct packed { 98 | logic [7:0] data; 99 | logic strb; 100 | } axi_wbyte_t; 101 | 102 | // AXI FIFO downstream 103 | axi_req_t fifo_out_req; 104 | axi_rsp_t fifo_out_rsp; 105 | 106 | // Atomics Filter downstream 107 | axi_req_t atop_out_req; 108 | axi_rsp_t atop_out_rsp; 109 | 110 | // ID serializer downstream 111 | axi_req_t ser_out_req; 112 | axi_rsp_t ser_out_rsp; 113 | axi_ax_t ser_out_req_aw; 114 | axi_ax_t ser_out_req_ar; 115 | 116 | // AX arbiter downstream 117 | axi_ax_t rr_out_req_ax; 118 | logic rr_out_req_write; 119 | logic spill_ax_valid, spill_ax_ready; 120 | axi_ax_t spill_rr_out_req_ax; 121 | logic spill_rr_out_req_write; 122 | ax_channel_spill_t spill_ax_channel_in, spill_ax_channel_out; 123 | 124 | // AX handling 125 | logic trans_handshake; 126 | logic ax_valid, ax_ready; 127 | axi_pkg::size_t ax_size_d, ax_size_q; 128 | chip_sel_idx_t ax_chip_sel_idx; 129 | hyperbus_pkg::hyper_blen_t ax_blen_postinc; 130 | logic ax_blen_inc; 131 | 132 | // R channel 133 | axi_r_t s_r_split; 134 | 135 | // R channel merge when phys_in_use != NumPhys 136 | logic [PhyDataWidth-1:0] s_rx_data; 137 | logic [PhyDataWidth/2-1:0] s_rx_data_lower_d, s_rx_data_lower_q; 138 | logic s_rx_error; 139 | logic s_rx_last; 140 | logic s_rx_valid; 141 | logic s_rx_ready; 142 | logic merge_r_d, merge_r_q; 143 | 144 | // W channel 145 | logic w_data_valid; 146 | logic w_data_ready; 147 | axi_w_chan_t w_data_fifo; 148 | axi_w_chan_t w_data_fifo_in; 149 | 150 | // W channel split when phys_in_use != NumPhys 151 | logic [PhyDataWidth-1:0] s_tx_data; 152 | logic [PhyDataBytes-1:0] s_tx_strb; 153 | logic s_tx_last; 154 | logic s_tx_valid; 155 | logic s_tx_ready; 156 | logic split_w_d, split_w_q; 157 | 158 | // Whether a transfer is currently active 159 | logic trans_active_d, trans_active_q; 160 | logic trans_active_set, trans_active_reset; 161 | logic trans_wready_d, trans_wready_q; 162 | logic trans_wready_set, trans_wready_reset; 163 | 164 | logic [1:0] phys_in_use; 165 | 166 | assign phys_in_use = (NumPhys==2) ? (phys_in_use_i + 1) : 1; 167 | 168 | // ============================ 169 | // Serialize requests 170 | // ============================ 171 | 172 | axi_fifo #( 173 | .Depth ( 4 ), 174 | .FallThrough ( 1'b0 ), 175 | .aw_chan_t ( axi_fifo_aw_chan_t ), 176 | .w_chan_t ( axi_fifo_w_chan_t ), 177 | .b_chan_t ( axi_fifo_b_chan_t ), 178 | .ar_chan_t ( axi_fifo_ar_chan_t ), 179 | .r_chan_t ( axi_fifo_r_chan_t ), 180 | .axi_req_t ( axi_req_t ), 181 | .axi_resp_t ( axi_rsp_t ) 182 | ) i_axi_fifo ( 183 | .clk_i, 184 | .rst_ni, 185 | .test_i ( 1'b0 ), 186 | .slv_req_i ( axi_req_i ), 187 | .slv_resp_o ( axi_rsp_o ), 188 | .mst_req_o ( fifo_out_req ), 189 | .mst_resp_i ( fifo_out_rsp ) 190 | ); 191 | 192 | 193 | // Block unsupported atomics 194 | axi_atop_filter #( 195 | .AxiIdWidth ( AxiIdWidth ), 196 | .AxiMaxWriteTxns ( 1 ), 197 | .axi_req_t ( axi_req_t ), 198 | .axi_resp_t ( axi_rsp_t ) 199 | ) i_axi_atop_filter ( 200 | .clk_i, 201 | .rst_ni, 202 | .slv_req_i ( fifo_out_req ), 203 | .slv_resp_o ( fifo_out_rsp ), 204 | .mst_req_o ( atop_out_req ), 205 | .mst_resp_i ( atop_out_rsp ) 206 | ); 207 | 208 | // Ensure we only handle one ID (master) at a time 209 | axi_serializer #( 210 | .MaxReadTxns ( 1 ), 211 | .MaxWriteTxns ( 1 ), 212 | .AxiIdWidth ( AxiIdWidth ), 213 | .axi_req_t ( axi_req_t ), 214 | .axi_resp_t ( axi_rsp_t ) 215 | ) i_axi_serializer ( 216 | .clk_i, 217 | .rst_ni, 218 | .slv_req_i ( atop_out_req ), 219 | .slv_resp_o ( atop_out_rsp ), 220 | .mst_req_o ( ser_out_req ), 221 | .mst_resp_i ( ser_out_rsp ) 222 | ); 223 | 224 | // Round-robin-arbitrate between AR and AW channels (HyperBus is simplex) 225 | assign ser_out_req_ar.addr = ser_out_req.ar.addr; 226 | assign ser_out_req_ar.len = ser_out_req.ar.len; 227 | assign ser_out_req_ar.burst = ser_out_req.ar.burst; 228 | assign ser_out_req_ar.size = ser_out_req.ar.size; 229 | 230 | assign ser_out_req_aw.addr = ser_out_req.aw.addr; 231 | assign ser_out_req_aw.len = ser_out_req.aw.len; 232 | assign ser_out_req_aw.burst = ser_out_req.aw.burst; 233 | assign ser_out_req_aw.size = ser_out_req.aw.size; 234 | 235 | rr_arb_tree #( 236 | .NumIn ( 2 ), 237 | .DataType ( axi_ax_t ), 238 | .AxiVldRdy ( 1 ), 239 | .ExtPrio ( 1'b1 ) 240 | ) i_rr_arb_tree_ax ( 241 | .clk_i, 242 | .rst_ni, 243 | .flush_i ( 1'b0 ), 244 | .rr_i ( '0 ), 245 | .req_i ( { ser_out_req.aw_valid, ser_out_req.ar_valid } ), 246 | .gnt_o ( { ser_out_rsp.aw_ready, ser_out_rsp.ar_ready } ), 247 | .data_i ( { ser_out_req_aw, ser_out_req_ar } ), 248 | .req_o ( spill_ax_valid ), 249 | .gnt_i ( spill_ax_ready ), 250 | .data_o ( spill_rr_out_req_ax ), 251 | .idx_o ( spill_rr_out_req_write ) 252 | ); 253 | 254 | // Cut paths between serializer and rr arb tree 255 | assign spill_ax_channel_in.ax_data = spill_rr_out_req_ax; 256 | assign spill_ax_channel_in.write = spill_rr_out_req_write; 257 | 258 | spill_register #( 259 | .T ( ax_channel_spill_t ) 260 | ) ax_spill_register ( 261 | .clk_i, 262 | .rst_ni, 263 | .valid_i (spill_ax_valid), 264 | .ready_o (spill_ax_ready), 265 | .data_i (spill_ax_channel_in), 266 | .valid_o (ax_valid), 267 | .ready_i (ax_ready), 268 | .data_o (spill_ax_channel_out) 269 | ); 270 | 271 | assign rr_out_req_ax = spill_ax_channel_out.ax_data; 272 | assign rr_out_req_write = spill_ax_channel_out.write; 273 | 274 | assign trans_valid_o = ax_valid & ~trans_active_q; 275 | assign ax_ready = trans_ready_i & ~trans_active_q; 276 | 277 | assign trans_handshake = trans_valid_o & trans_ready_i; 278 | 279 | // ============================ 280 | // AX channel: handle 281 | // ============================ 282 | 283 | // Handle address mapping to chip select 284 | addr_decode #( 285 | .NoIndices ( NumChips ), 286 | .NoRules ( NumChips ), 287 | .addr_t ( axi_addr_t ), 288 | .rule_t ( rule_t ) 289 | ) i_addr_decode_chip_sel ( 290 | .addr_i ( rr_out_req_ax.addr ), 291 | .addr_map_i ( chip_rules_i ), 292 | .idx_o ( ax_chip_sel_idx ), 293 | .dec_valid_o ( ), 294 | .dec_error_o ( ), 295 | .en_default_idx_i ( 1'b1 ), 296 | .default_idx_i ( '0 ) 297 | ); 298 | 299 | // Chip select binary to one hot decoding 300 | always_comb begin : proc_comb_trans_cs 301 | trans_cs_o = '0; 302 | trans_cs_o[ax_chip_sel_idx] = 1'b1; 303 | end 304 | 305 | // AX channel: forward, converting unmasked byte to masked word addresses 306 | assign trans_o.write = rr_out_req_write; 307 | assign trans_o.burst_type = 1'b1; // Wrapping bursts not (yet) supported 308 | assign trans_o.address_space = addr_space_i; 309 | assign trans_o.address = ( (rr_out_req_ax.addr & ~32'(32'hFFFF_FFFF << addr_mask_msb_i)) >> ( NumPhys ) ) << ( (NumPhys==2) & ~phys_in_use_i ); 310 | 311 | // Convert burst length from decremented, unaligned beats to non-decremented, aligned 16-bit words 312 | always_comb begin 313 | trans_o.burst= NumPhys; 314 | ax_blen_inc = 1'b1; 315 | if (rr_out_req_ax.size > NumPhys) begin 316 | ax_blen_inc = 1'b1; 317 | if( ((rr_out_req_ax.addr>>rr_out_req_ax.size)<>rr_out_req_ax.size)<> NumPhys ) + 1 ) << (NumPhys-1); 337 | end else begin 338 | trans_o.burst = ( ( ( rr_out_req_ax.addr[NumPhys-1:0] + (ax_blen_postinc<> NumPhys ) + 1 ) << (NumPhys-1); 339 | end 340 | end 341 | end 342 | end 343 | 344 | assign ax_blen_postinc = rr_out_req_ax.len + hyperbus_pkg::hyper_blen_t'(ax_blen_inc) ; 345 | 346 | // ============================ 347 | // R channel 348 | // ============================ 349 | 350 | assign ser_out_rsp.r.data = s_r_split.data; 351 | assign ser_out_rsp.r.last = s_r_split.last; 352 | assign ser_out_rsp.r.resp = s_r_split.error ? axi_pkg::RESP_SLVERR : axi_pkg::RESP_OKAY; 353 | assign ser_out_rsp.r.id = '0; 354 | assign ser_out_rsp.r.user = '0; 355 | 356 | always_comb begin 357 | s_rx_valid = rx_valid_i; 358 | rx_ready_o = s_rx_ready; 359 | s_rx_data = rx_i.data; 360 | s_rx_last = rx_i.last; 361 | s_rx_error = rx_i.error; 362 | s_rx_data_lower_d = s_rx_data_lower_q; 363 | merge_r_d = merge_r_q; 364 | if( (NumPhys==2) & (~phys_in_use_i) ) begin 365 | if(rx_valid_i & s_rx_ready) begin 366 | merge_r_d = merge_r_q + 1; 367 | end 368 | if(~which_phy_i) 369 | s_rx_data = { rx_i.data[PhyDataWidth/2-1:0] , s_rx_data_lower_q }; 370 | else 371 | s_rx_data = { rx_i.data[PhyDataWidth-1:PhyDataWidth/2] , s_rx_data_lower_q }; 372 | s_rx_valid = rx_valid_i & merge_r_q; 373 | rx_ready_o = s_rx_ready; 374 | s_rx_last = rx_i.last & merge_r_q; 375 | s_rx_error = rx_i.error; 376 | if(~merge_r_q) begin 377 | if(~which_phy_i) 378 | s_rx_data_lower_d = rx_i.data[PhyDataWidth/2-1:0]; 379 | else 380 | s_rx_data_lower_d = rx_i.data[PhyDataWidth-1:PhyDataWidth/2]; 381 | end 382 | end 383 | end 384 | 385 | hyperbus_phy2r #( 386 | .AxiDataWidth ( AxiDataWidth ), 387 | .BurstLength ( hyperbus_pkg::HyperBurstWidth ), 388 | .T ( axi_r_t ), 389 | .NumPhys ( NumPhys ) 390 | ) i_hyperbus_phy2r ( 391 | .clk_i, 392 | .rst_ni, 393 | .size ( rr_out_req_ax.size ), 394 | .trans_handshake ( trans_handshake ), 395 | .start_addr ( rr_out_req_ax.addr[AxiBusAddrWidth-1:0] ), 396 | .burst_len ( ax_blen_postinc ), 397 | .is_a_read ( !rr_out_req_write ), 398 | .phy_valid_i ( s_rx_valid ), 399 | .phy_ready_o ( s_rx_ready ), 400 | .data_i ( s_rx_data ), 401 | .last_i ( s_rx_last ), 402 | .error_i ( s_rx_error ), 403 | .axi_valid_o ( ser_out_rsp.r_valid ), 404 | .axi_ready_i ( ser_out_req.r_ready ), 405 | .data_o ( s_r_split ) 406 | ); 407 | 408 | `FFARN(merge_r_q,merge_r_d,1'b0,clk_i,rst_ni) 409 | `FFARN(s_rx_data_lower_q,s_rx_data_lower_d,'0,clk_i,rst_ni) 410 | 411 | // ========================================================= 412 | // W channel: Buffer. Cuts path and upsamples when needed 413 | // ========================================================= 414 | 415 | assign w_data_fifo_in.data = ser_out_req.w.data; 416 | assign w_data_fifo_in.strb = ser_out_req.w.strb; 417 | assign w_data_fifo_in.last = ser_out_req.w.last; 418 | assign w_data_fifo_in.user = ser_out_req.w.user; 419 | 420 | stream_fifo #( 421 | .FALL_THROUGH ( 1'b0 ), 422 | .T ( axi_w_chan_t ), 423 | .DEPTH ( 8 ) 424 | ) wchan_stream_fifo ( 425 | .clk_i, 426 | .rst_ni, 427 | .flush_i ( 1'b0 ), 428 | .testmode_i ( 1'b0 ), 429 | .usage_o ( ), 430 | .data_i ( w_data_fifo_in ), 431 | .valid_i ( ser_out_req.w_valid ), 432 | .ready_o ( ser_out_rsp.w_ready ), 433 | .data_o ( w_data_fifo ), 434 | .valid_o ( w_data_valid ), 435 | .ready_i ( w_data_ready ) 436 | ); 437 | 438 | hyperbus_w2phy #( 439 | .AxiDataWidth ( AxiDataWidth ), 440 | .BurstLength ( hyperbus_pkg::HyperBurstWidth ), 441 | .T ( axi_w_chan_t ), 442 | .NumPhys ( NumPhys ) 443 | ) i_hyperbus_w2phy ( 444 | .clk_i, 445 | .rst_ni, 446 | .size ( rr_out_req_ax.size ), 447 | .len ( ax_blen_postinc ), 448 | .is_a_write ( rr_out_req_write ), 449 | .trans_handshake ( trans_handshake ), 450 | .start_addr ( rr_out_req_ax.addr[AxiBusAddrWidth-1:0] ), 451 | .data_i ( w_data_fifo ), 452 | .axi_valid_i ( w_data_valid ), 453 | .axi_ready_o ( w_data_ready ), 454 | .data_o ( s_tx_data ), 455 | .last_o ( s_tx_last ), 456 | .strb_o ( s_tx_strb ), 457 | .phy_valid_o ( s_tx_valid ), 458 | .phy_ready_i ( s_tx_ready ) 459 | ); 460 | 461 | always_comb begin 462 | tx_o.data = s_tx_data; 463 | tx_o.strb = s_tx_strb; 464 | tx_o.last = s_tx_last; 465 | tx_valid_o = s_tx_valid; 466 | s_tx_ready = tx_ready_i; 467 | split_w_d = split_w_q; 468 | if( (NumPhys==2) & (~phys_in_use_i) ) begin 469 | if(s_tx_valid & tx_ready_i) begin 470 | split_w_d = split_w_q+1; 471 | end 472 | tx_o.last = s_tx_last & split_w_q; 473 | tx_valid_o = s_tx_valid; 474 | s_tx_ready = tx_ready_i & split_w_q; 475 | tx_o.data = { s_tx_data[PhyDataWidth/2-1:0] , s_tx_data[PhyDataWidth/2-1:0] }; 476 | tx_o.strb = { s_tx_strb[NumPhys-1:0] , s_tx_strb[NumPhys-1:0] }; 477 | if(split_w_q) begin 478 | tx_o.data = { s_tx_data[PhyDataWidth-1:PhyDataWidth/2] , s_tx_data[PhyDataWidth-1:PhyDataWidth/2] }; 479 | tx_o.strb = { s_tx_strb[NumPhys*2-1:NumPhys] , s_tx_strb[NumPhys*2-1:NumPhys] }; 480 | end 481 | end 482 | end 483 | 484 | `FFARN(split_w_q,split_w_d,1'b0,clk_i,rst_ni) 485 | 486 | // ============================ 487 | // B channel: passthrough 488 | // ============================ 489 | 490 | assign ser_out_rsp.b.resp = b_error_i ? axi_pkg::RESP_SLVERR : axi_pkg::RESP_OKAY; 491 | assign ser_out_rsp.b.user = '0; 492 | assign ser_out_rsp.b.id = '0; 493 | assign ser_out_rsp.b_valid = b_valid_i; 494 | assign b_ready_o = ser_out_req.b_ready; 495 | 496 | // ============================ 497 | // Transfer status 498 | // ============================ 499 | 500 | assign trans_active_o = trans_active_q; 501 | 502 | assign trans_active_set = trans_handshake; 503 | assign trans_active_reset = (rx_valid_i & rx_ready_o & rx_i.last) | (b_valid_i & b_ready_o); 504 | 505 | // Allow W transfers iff currently engaged in write (AW already received) 506 | assign trans_wready_set = trans_active_set & rr_out_req_write; 507 | assign trans_wready_reset = (tx_valid_o & tx_ready_i & tx_o.last); 508 | 509 | // Set overrules reset as a transfer must start before it finishes 510 | always_comb begin : proc_comb_trans_active 511 | trans_active_d = trans_active_q; 512 | trans_wready_d = trans_wready_q; 513 | if (trans_active_reset) trans_active_d = 1'b0; 514 | if (trans_active_set) trans_active_d = 1'b1; 515 | if (trans_wready_reset) trans_wready_d = 1'b0; 516 | if (trans_wready_set) trans_wready_d = 1'b1; 517 | end 518 | 519 | // ========================= 520 | // Registers 521 | // ========================= 522 | 523 | always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff 524 | if(~rst_ni) begin 525 | trans_active_q <= '0; 526 | trans_wready_q <= '0; 527 | end else begin 528 | trans_active_q <= trans_active_d; 529 | trans_wready_q <= trans_wready_d; 530 | end 531 | end 532 | 533 | // ========================= 534 | // Assertions 535 | // ========================= 536 | 537 | // pragma translate_off 538 | `ifndef VERILATOR 539 | initial assert (AxiDataWidth >= 16 && AxiDataWidth <= 1024) 540 | else $error("AxiDatawidth must be a power of two within [16, 1024]."); 541 | 542 | // access_16b_align : assert property( 543 | // @(posedge clk_i) trans_handshake & (rr_out_req_ax.size != '0) |-> (rr_out_req_ax.addr[0] == 1'b0)) 544 | // else $fatal (1, "The address of a non-byte-size access must be 2-byte aligned."); 545 | 546 | burst_type : assert property( 547 | @(posedge clk_i) trans_handshake |-> ( (rr_out_req_ax.burst == axi_pkg::BURST_INCR) || ((rr_out_req_ax.burst == axi_pkg::BURST_FIXED) && (rr_out_req_ax.len == '0)) ) ) 548 | else $fatal (1, "Non-incremental burst passed; this is currently not supported."); 549 | `endif 550 | // pragma translate_on 551 | 552 | endmodule 553 | -------------------------------------------------------------------------------- /src/hyperbus_cfg_regs.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Paul Scheffler 6 | 7 | module hyperbus_cfg_regs #( 8 | parameter int unsigned NumChips = -1, 9 | parameter int unsigned NumPhys = -1, 10 | parameter int unsigned RegAddrWidth = -1, 11 | parameter int unsigned RegDataWidth = -1, 12 | parameter type reg_req_t = logic, 13 | parameter type reg_rsp_t = logic, 14 | parameter type rule_t = logic, 15 | parameter logic [RegDataWidth-1:0] RstChipBase = -1, // Base address for all chips 16 | parameter logic [RegDataWidth-1:0] RstChipSpace = -1, // 64 KiB: Current maximum HyperBus device size 17 | parameter int unsigned MinFreqMHz = 100, 18 | parameter hyperbus_pkg::hyper_cfg_t RstCfg = hyperbus_pkg::gen_RstCfg(NumPhys,MinFreqMHz) 19 | ) ( 20 | input logic clk_i, 21 | input logic rst_ni, 22 | 23 | input reg_req_t reg_req_i, 24 | output reg_rsp_t reg_rsp_o, 25 | 26 | output hyperbus_pkg::hyper_cfg_t cfg_o, 27 | output rule_t [NumChips-1:0] chip_rules_o, 28 | input trans_active_i 29 | ); 30 | `include "common_cells/registers.svh" 31 | 32 | // Internal Parameters 33 | localparam int unsigned NumBaseRegs = 11; 34 | localparam int unsigned NumRegs = 2*NumChips + NumBaseRegs; 35 | localparam int unsigned RegsBits = cf_math_pkg::idx_width(NumRegs); 36 | localparam int unsigned RegStrbWidth = RegDataWidth/8; // TODO ASSERT: Must be power of two >= 16!! 37 | 38 | // Data and index types 39 | typedef logic [RegsBits-1:0] reg_idx_t; 40 | typedef logic [RegDataWidth-1:0] reg_data_t; 41 | 42 | // Local signals 43 | hyperbus_pkg::hyper_cfg_t cfg_d, cfg_q, cfg_rstval; 44 | reg_data_t [NumChips-1:0][1:0] crange_d, crange_q, crange_rstval; 45 | reg_idx_t sel_reg; 46 | logic sel_reg_mapped; 47 | reg_data_t wmask; 48 | 49 | assign sel_reg = reg_req_i.addr[$clog2(RegStrbWidth) +: RegsBits]; 50 | assign sel_reg_mapped = (sel_reg < NumRegs); 51 | 52 | assign reg_rsp_o.ready = ~trans_active_i; // Config writeable unless currently in transfer 53 | assign reg_rsp_o.error = ~sel_reg_mapped; 54 | 55 | // Read from register 56 | always_comb begin : proc_comb_read 57 | reg_data_t [NumRegs-1:0] rfield; 58 | reg_rsp_o.rdata = '0; 59 | if (sel_reg_mapped) begin 60 | rfield = { 61 | crange_q, 62 | reg_data_t'(cfg_q.t_csh_cycles), 63 | reg_data_t'(cfg_q.which_phy), 64 | reg_data_t'(cfg_q.phys_in_use), 65 | reg_data_t'(cfg_q.address_space), 66 | reg_data_t'(cfg_q.address_mask_msb), 67 | reg_data_t'(cfg_q.t_tx_clk_delay), 68 | reg_data_t'(cfg_q.t_rx_clk_delay), 69 | reg_data_t'(cfg_q.t_read_write_recovery), 70 | reg_data_t'(cfg_q.t_burst_max), 71 | reg_data_t'(cfg_q.en_latency_additional), 72 | reg_data_t'(cfg_q.t_latency_access) 73 | }; 74 | reg_rsp_o.rdata = rfield[sel_reg]; 75 | end 76 | end 77 | 78 | // Generate write mask 79 | for (genvar i = 0; unsigned'(i) < RegStrbWidth; ++i ) begin : gen_wmask 80 | assign wmask[8*i +: 8] = {8{reg_req_i.wstrb[i]}}; 81 | end 82 | 83 | // Write to register 84 | always_comb begin : proc_comb_write 85 | logic chip_reg; 86 | logic [$clog2(NumChips)-1:0] sel_chip; 87 | cfg_d = cfg_q; 88 | crange_d = crange_q; 89 | if (reg_req_i.valid & reg_req_i.write & sel_reg_mapped) begin 90 | case (sel_reg) 91 | 'h0: cfg_d.t_latency_access = (~wmask & cfg_q.t_latency_access ) | (wmask & reg_req_i.wdata); 92 | 'h1: cfg_d.en_latency_additional = (~wmask & cfg_q.en_latency_additional ) | (wmask & reg_req_i.wdata); 93 | 'h2: cfg_d.t_burst_max = (~wmask & cfg_q.t_burst_max ) | (wmask & reg_req_i.wdata); 94 | 'h3: cfg_d.t_read_write_recovery = (~wmask & cfg_q.t_read_write_recovery ) | (wmask & reg_req_i.wdata); 95 | 'h4: cfg_d.t_rx_clk_delay = (~wmask & cfg_q.t_rx_clk_delay ) | (wmask & reg_req_i.wdata); 96 | 'h5: cfg_d.t_tx_clk_delay = (~wmask & cfg_q.t_tx_clk_delay ) | (wmask & reg_req_i.wdata); 97 | 'h6: cfg_d.address_mask_msb = (~wmask & cfg_q.address_mask_msb ) | (wmask & reg_req_i.wdata); 98 | 'h7: cfg_d.address_space = (~wmask & cfg_q.address_space ) | (wmask & reg_req_i.wdata); 99 | 'h8: cfg_d.phys_in_use = (NumPhys==1) ? 0 : ( (~wmask & cfg_q.phys_in_use ) | (wmask & reg_req_i.wdata) ); 100 | 'h9: cfg_d.which_phy = (NumPhys==1) ? 0 : ( (~wmask & cfg_q.which_phy ) | (wmask & reg_req_i.wdata) ); 101 | 'ha: cfg_d.t_csh_cycles = (~wmask & cfg_q.t_csh_cycles ) | (wmask & reg_req_i.wdata); 102 | default: begin 103 | {sel_chip, chip_reg} = sel_reg - NumBaseRegs; 104 | crange_d[sel_chip][chip_reg] = (~wmask & crange_q[sel_chip][chip_reg]) | (wmask & reg_req_i.wdata); 105 | end 106 | endcase // sel_reg 107 | end 108 | end 109 | 110 | for (genvar i = 0; unsigned'(i) < NumChips; i++) begin : gen_crange_rstval 111 | assign crange_rstval[i][0] = RstChipBase + (RstChipSpace * i); 112 | assign crange_rstval[i][1] = RstChipBase + (RstChipSpace * (i+1)); // Address decoder: end noninclusive 113 | end 114 | 115 | // Registers 116 | `FFARN(cfg_q, cfg_d, RstCfg, clk_i, rst_ni); 117 | `FFARN(crange_q, crange_d, crange_rstval, clk_i, rst_ni); 118 | 119 | // Outputs 120 | assign cfg_o = cfg_q; 121 | for (genvar i = 0; unsigned'(i) < NumChips; ++i) begin : gen_crange_out 122 | assign chip_rules_o[i].idx = unsigned'(i); // No overlap: keep indices sequential 123 | assign chip_rules_o[i].start_addr = crange_q[i][0]; 124 | assign chip_rules_o[i].end_addr = crange_q[i][1]; 125 | end 126 | 127 | // pragma translate_off 128 | `ifndef VERILATOR 129 | initial assert (RegDataWidth >= 16 && $countones(RegDataWidth) == 1) 130 | else $error("RegDataWidth must be a power of two bigger than 16."); 131 | `endif 132 | // pragma translate_on 133 | 134 | endmodule : hyperbus_cfg_regs 135 | -------------------------------------------------------------------------------- /src/hyperbus_clk_gen.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Hayate Okuhara 6 | 7 | /// Generates 4 phase shifted clocks out of one faster clock 8 | module hyperbus_clk_gen ( 9 | input logic clk_i, // input clock 10 | input logic rst_ni, 11 | output logic clk0_o, // have the input clock - 0deg phase shift 12 | output logic clk90_o, // have the input clock - 90deg phase shift 13 | output logic clk180_o, // have the input clock - 180deg phase shift 14 | output logic clk270_o, // have the input clock - 270deg phase shift 15 | output logic rst_no 16 | ); 17 | 18 | 19 | logic r_clk0_o; 20 | logic r_clk90_o; 21 | logic r_clk180_o; 22 | logic r_clk270_o; 23 | 24 | logic s_clk0_n; 25 | 26 | assign clk0_o = r_clk0_o; 27 | assign clk90_o = r_clk90_o; 28 | assign clk180_o = r_clk180_o; 29 | assign clk270_o = r_clk270_o; 30 | assign rst_no = rst_ni; 31 | 32 | tc_clk_inverter i_clk0_inverter ( 33 | .clk_i (r_clk0_o), 34 | .clk_o (s_clk0_n) 35 | ); 36 | 37 | 38 | always_ff @(posedge clk_i or negedge rst_ni) begin 39 | if(~rst_ni) begin 40 | r_clk0_o <= 0; 41 | r_clk180_o <= 1; 42 | end else begin 43 | r_clk0_o <= s_clk0_n; 44 | r_clk180_o <= r_clk90_o; 45 | end 46 | end 47 | 48 | always_ff @(negedge clk_i or negedge rst_ni) begin 49 | if (~rst_ni) begin 50 | r_clk90_o <= 0; 51 | r_clk270_o <= 1; 52 | end else begin 53 | r_clk90_o <= r_clk0_o; 54 | r_clk270_o <= r_clk180_o; 55 | end 56 | end 57 | 58 | endmodule 59 | -------------------------------------------------------------------------------- /src/hyperbus_clock_diff_out.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Armin Berger 6 | // Stephan Keck 7 | 8 | /// A Hyperbus differential clock output generator. 9 | module hyperbus_clock_diff_out 10 | ( 11 | input logic in_i, 12 | input logic en_i, //high enable 13 | output logic out_o, 14 | output logic out_no 15 | ); 16 | 17 | `ifdef FPGA_EMUL 18 | 19 | logic en_sync; 20 | 21 | always_latch 22 | begin 23 | if (in_i == 1'b0) 24 | en_sync <= en_i; 25 | end 26 | 27 | assign out_o = in_i & en_sync; 28 | assign out_no = ~out_o; 29 | 30 | `else 31 | 32 | tc_clk_gating i_hyper_ck_gating ( 33 | .clk_i ( in_i ), 34 | .en_i ( en_i ), 35 | .test_en_i ( 1'b0 ), 36 | .clk_o ( out_o ) 37 | ); 38 | 39 | tc_clk_inverter i_hyper_ck_no_inv ( 40 | .clk_i ( out_o ), 41 | .clk_o ( out_no ) 42 | ); 43 | 44 | `endif // !`ifdef FPGA_EMUL 45 | 46 | endmodule 47 | -------------------------------------------------------------------------------- /src/hyperbus_ddr_out.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Armin Berger 6 | // Stephan Keck 7 | 8 | module hyperbus_ddr_out #( 9 | parameter logic Init = 1'b0 10 | )( 11 | input logic clk_i, 12 | input logic rst_ni, 13 | input logic d0_i, 14 | input logic d1_i, 15 | output logic q_o 16 | ); 17 | logic q0; 18 | logic q1; 19 | 20 | `ifdef FPGA_EMUL 21 | always_comb 22 | begin 23 | if(clk_i == 1'b0) 24 | q_o = q1; 25 | else 26 | q_o = q0; 27 | end 28 | `else 29 | tc_clk_mux2 i_ddrmux ( 30 | .clk_o ( q_o ), 31 | .clk0_i ( q1 ), 32 | .clk1_i ( q0 ), 33 | .clk_sel_i ( clk_i ) 34 | ); 35 | `endif // !`ifdef FPGA_EMUL 36 | 37 | always_ff @(posedge clk_i or negedge rst_ni) begin 38 | if (~rst_ni) begin 39 | q0 <= Init; 40 | q1 <= Init; 41 | end else begin 42 | q0 <= d0_i; 43 | q1 <= d1_i; 44 | end 45 | end 46 | 47 | endmodule 48 | -------------------------------------------------------------------------------- /src/hyperbus_delay.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Thomas Benz 6 | // Paul Scheffler 7 | 8 | module hyperbus_delay ( 9 | input logic in_i, 10 | input logic [3:0] delay_i, 11 | output logic out_o 12 | ); 13 | 14 | configurable_delay #( 15 | .NUM_STEPS(16) 16 | ) i_delay ( 17 | .clk_i ( in_i ), 18 | `ifndef TARGET_ASIC 19 | .enable_i ( 1'b1 ), 20 | `endif 21 | .delay_i ( delay_i ), 22 | .clk_o ( out_o ) 23 | ); 24 | 25 | endmodule : hyperbus_delay 26 | -------------------------------------------------------------------------------- /src/hyperbus_phy.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Armin Berger 6 | // Stephan Keck 7 | // Thomas Benz 8 | // Paul Scheffler 9 | 10 | module hyperbus_phy import hyperbus_pkg::*; #( 11 | parameter int unsigned IsClockODelayed = -1, 12 | parameter int unsigned NumChips = 2, 13 | parameter int unsigned NumPhys = -1, 14 | parameter int unsigned TimerWidth = 16, 15 | parameter int unsigned RxFifoLogDepth = 3, 16 | parameter int unsigned SyncStages = 2, 17 | parameter int unsigned StartupCycles = 300 /*us*/ * 200 /*MHz*/ // Conservative maximum frequency estimate 18 | )( 19 | input logic clk_i, 20 | input logic clk_i_90, 21 | input logic rst_ni, 22 | input logic test_mode_i, 23 | // Config registers 24 | input hyper_cfg_t cfg_i, 25 | // PHY control status 26 | output logic busy_o, 27 | // Transactions 28 | input logic trans_valid_i, 29 | output logic trans_ready_o, 30 | input hyper_tf_t trans_i, // TODO: increase burst width! 31 | input logic [NumChips-1:0] trans_cs_i, 32 | // Transmitting channel 33 | input logic tx_valid_i, 34 | output logic tx_ready_o, 35 | input logic [15:0] tx_data_i, 36 | input logic [1:0] tx_strb_i, 37 | input logic tx_last_i, 38 | // Receiving channel 39 | output logic rx_valid_o, 40 | input logic rx_ready_i, 41 | output logic [15:0] rx_data_o, 42 | output logic rx_error_o, 43 | output logic rx_last_o, 44 | // B response 45 | output logic b_valid_o, 46 | input logic b_ready_i, 47 | output logic b_error_o, 48 | // Physical interface 49 | output logic [NumChips-1:0] hyper_cs_no, 50 | output logic hyper_ck_o, 51 | output logic hyper_ck_no, 52 | output logic hyper_rwds_o, 53 | input logic hyper_rwds_i, 54 | output logic hyper_rwds_oe_o, 55 | input logic [7:0] hyper_dq_i, 56 | output logic [7:0] hyper_dq_o, 57 | output logic hyper_dq_oe_o, 58 | output logic hyper_reset_no 59 | ); 60 | 61 | logic [1:0] phys_in_use; 62 | 63 | assign phys_in_use = (NumPhys==2) ? (cfg_i.phys_in_use + 1) : 1; 64 | 65 | // PHY state 66 | hyper_phy_state_t state_d, state_q; 67 | logic [TimerWidth-1:0] timer_d, timer_q; 68 | hyper_tf_t tf_d, tf_q; 69 | logic [NumChips-1:0] cs_d, cs_q; 70 | 71 | // Whether B response is pending 72 | logic b_pending_q; 73 | logic b_pending_set; 74 | logic b_pending_clear; 75 | 76 | // How many R response words are outstanding if any 77 | logic [RxFifoLogDepth:0] r_outstand_q; 78 | logic r_outstand_inc; 79 | logic r_outstand_dec; 80 | 81 | // Auxiliar control signals 82 | logic ctl_write_zero_lat; 83 | logic ctl_add_latency; 84 | logic ctl_tf_burst_last; 85 | logic ctl_tf_burst_done; 86 | logic ctl_timer_two; 87 | logic ctl_timer_one; 88 | logic ctl_timer_zero; 89 | logic ctl_timer_rwr_done; 90 | logic ctl_rclk_ena; 91 | logic ctl_rcnt_ena; 92 | logic ctl_wclk_ena; 93 | 94 | // Command-address 95 | hyper_phy_ca_t ca; 96 | 97 | // Transciever IO 98 | logic trx_clk_ena; 99 | logic trx_cs_ena; 100 | logic trx_rwds_sample; 101 | logic trx_rwds_sample_ena; 102 | logic [15:0] trx_tx_data; 103 | logic trx_tx_data_oe; 104 | logic [1:0] trx_tx_rwds; 105 | logic trx_tx_rwds_oe; 106 | logic trx_rx_clk_set; 107 | logic trx_rx_clk_reset; 108 | logic [15:0] trx_rx_data; 109 | logic trx_rx_valid; 110 | logic trx_rx_ready; 111 | 112 | // ================= 113 | // Transciever 114 | // ================= 115 | 116 | hyperbus_trx #( 117 | .IsClockODelayed( IsClockODelayed ), 118 | .NumChips ( NumChips ), 119 | .RxFifoLogDepth ( RxFifoLogDepth ), 120 | .SyncStages ( SyncStages ) 121 | ) i_trx ( 122 | .clk_i, 123 | .clk_i_90, 124 | .rst_ni, 125 | .test_mode_i, 126 | .cs_i ( cs_q ), 127 | .cs_ena_i ( trx_cs_ena ), 128 | .rwds_sample_o ( trx_rwds_sample ), 129 | .rwds_sample_ena_i ( trx_rwds_sample_ena ), 130 | .tx_clk_delay_i ( cfg_i.t_tx_clk_delay ), 131 | .tx_clk_ena_i ( trx_clk_ena ), 132 | .tx_data_i ( trx_tx_data ), 133 | .tx_data_oe_i ( trx_tx_data_oe ), 134 | .tx_rwds_i ( trx_tx_rwds ), 135 | .tx_rwds_oe_i ( trx_tx_rwds_oe ), 136 | .rx_clk_delay_i ( cfg_i.t_rx_clk_delay ), 137 | .rx_clk_set_i ( trx_rx_clk_set ), 138 | .rx_clk_reset_i ( trx_rx_clk_reset ), 139 | .rx_data_o ( trx_rx_data ), 140 | .rx_valid_o ( trx_rx_valid ), 141 | .rx_ready_i ( trx_rx_ready ), 142 | .hyper_cs_no, 143 | .hyper_ck_o, 144 | .hyper_ck_no, 145 | .hyper_rwds_o, 146 | .hyper_rwds_i, 147 | .hyper_rwds_oe_o, 148 | .hyper_dq_i, 149 | .hyper_dq_o, 150 | .hyper_dq_oe_o, 151 | .hyper_reset_no 152 | ); 153 | 154 | // ============== 155 | // Dataflow 156 | // ============== 157 | 158 | // Command-address 159 | assign ca = hyper_phy_ca_t '{ 160 | write: ~tf_q.write, 161 | addr_space: tf_q.address_space, 162 | burst_type: tf_q.burst_type, 163 | addr_upper: tf_q.address[31:3], 164 | reserved: '0, 165 | addr_lower: tf_q.address[2:0] 166 | }; 167 | 168 | // Write dataflow 169 | always_comb begin : proc_comb_tx 170 | trx_tx_data = '0; 171 | trx_tx_rwds = '0; 172 | tx_ready_o = 1'b0; 173 | ctl_wclk_ena = 1'b0; 174 | if (state_q == SendCA) begin 175 | // In CA phase: use timer to select word 176 | trx_tx_data = ca[(8'(timer_q) << 4) +: 16]; 177 | end else if (state_q == Write) begin 178 | trx_tx_data = tx_data_i; 179 | trx_tx_rwds = ~tx_strb_i; 180 | tx_ready_o = 1'b1; // Memory always ready within HyperBus burst 181 | ctl_wclk_ena = tx_valid_i; 182 | end 183 | end 184 | 185 | // Write response dataflow 186 | assign b_valid_o = b_pending_q; 187 | assign b_error_o = 1'b0; // TODO 188 | assign b_pending_clear = b_valid_o & b_ready_i; 189 | 190 | // FF indicating whether B response pending 191 | always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_b_pending 192 | if (~rst_ni) b_pending_q <= 1'b0; 193 | else if (b_pending_set) b_pending_q <= 1'b1; 194 | else if (b_pending_clear) b_pending_q <= 1'b0; 195 | end 196 | 197 | // Read response dataflow 198 | assign rx_data_o = trx_rx_data; 199 | assign rx_error_o = 1'b0; 200 | assign rx_last_o = (state_q != Read) & ctl_tf_burst_done & (r_outstand_q == 1); 201 | 202 | assign trx_rx_ready = rx_ready_i; 203 | assign rx_valid_o = trx_rx_valid & (r_outstand_q != '0); 204 | // Suspend clock one cycle for every stall caused by upstream. 205 | // This ensures that a sufficiently large RX FIFO will not overflow. 206 | assign ctl_rclk_ena = ~(rx_valid_o & ~rx_ready_i); 207 | // Disable incoming RWDS clock enable once all words received 208 | assign trx_rx_clk_reset = b_pending_clear; 209 | 210 | // Counter for outstanding R responses 211 | assign r_outstand_dec = rx_valid_o & rx_ready_i; 212 | always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_r_outstand 213 | if (~rst_ni) r_outstand_q <= '0; 214 | else if (r_outstand_inc & ~r_outstand_dec) r_outstand_q <= r_outstand_q + 1; 215 | else if (r_outstand_dec & ~r_outstand_inc) r_outstand_q <= r_outstand_q - 1; 216 | end 217 | 218 | // ============= 219 | // Control 220 | // ============= 221 | 222 | // Auxiliary control signals 223 | assign ctl_write_zero_lat = tf_q.address_space & tf_q.write; 224 | // cfg_i.latency_addentional overwrites the trx_rwds_sample. Be careful. 225 | assign ctl_add_latency = trx_rwds_sample | cfg_i.en_latency_additional; 226 | 227 | assign ctl_tf_burst_last = (tf_q.burst == 1) || (tf_q.burst == phys_in_use); 228 | assign ctl_tf_burst_done = (tf_q.burst == 0); 229 | 230 | assign ctl_timer_rwr_done = (timer_q <= 3); 231 | assign ctl_timer_two = (timer_q == 2); 232 | assign ctl_timer_one = (timer_q == 1); 233 | assign ctl_timer_zero = (timer_q == 0); 234 | 235 | assign busy_o = (state_q != Idle); 236 | 237 | // FSM logic 238 | always_comb begin : proc_comb_phy_fsm 239 | // Default outputs 240 | trans_ready_o = 1'b0; 241 | r_outstand_inc = 1'b0; 242 | b_pending_set = 1'b0; 243 | trx_cs_ena = 1'b1; 244 | trx_clk_ena = 1'b0; 245 | trx_rx_clk_set = 1'b0; 246 | trx_rwds_sample_ena = 1'b0; 247 | // Default next state 248 | state_d = state_q; 249 | timer_d = timer_q - 1; 250 | tf_d = tf_q; 251 | cs_d = cs_q; 252 | // Tri-state control of dq and rwds 253 | trx_tx_rwds_oe = 1'b0; 254 | trx_tx_data_oe = 1'b0; 255 | // State-dependent logic 256 | case (state_q) 257 | Startup: begin 258 | trx_cs_ena = 1'b0; 259 | // Timer resets to parameterized startup delay 260 | if (ctl_timer_one) begin 261 | state_d = Idle; 262 | end 263 | end 264 | Idle: begin 265 | trx_cs_ena = 1'b0; 266 | timer_d = timer_q; 267 | // Signal ready for, pop next transfer if Write response sent 268 | trans_ready_o = 1'b1; 269 | if (trans_valid_i & ~b_pending_q & r_outstand_q == '0) begin 270 | tf_d = trans_i; 271 | cs_d = trans_cs_i; 272 | // Send 3 CA words (t_CSS respected through clock delay) 273 | timer_d = 2; 274 | state_d = SendCA; 275 | // Enable output driver (needs to be enabled one cycle 276 | // earlier since tri-state enables of IO pads are quite 277 | // slow compared to the data pins) 278 | trx_tx_data_oe = 1'b1; 279 | end 280 | end 281 | SendCA: begin 282 | // Dataflow handled outside FSM 283 | trx_clk_ena = 1'b1; 284 | trx_tx_data_oe = 1'b1; 285 | trx_rwds_sample_ena = ~ctl_write_zero_lat; 286 | if (ctl_timer_zero) begin 287 | if (ctl_write_zero_lat) begin 288 | timer_d = cfg_i.t_burst_max; 289 | state_d = Write; 290 | end else begin 291 | timer_d = TimerWidth'(cfg_i.t_latency_access) << ctl_add_latency; 292 | state_d = WaitLatAccess; 293 | end 294 | end 295 | end 296 | WaitLatAccess: begin 297 | trx_clk_ena = 1'b1; 298 | trx_tx_data_oe = 1'b1; 299 | // Substract cycle for last CA and another for state delay 300 | if (ctl_timer_two) begin 301 | timer_d = cfg_i.t_burst_max; 302 | // Switch to write or read phase and already start 303 | // turnaround of tri-state driver (depending on latency 304 | // config and if read or write transaction). 305 | if (tf_q.write) begin 306 | state_d = Write; 307 | trx_tx_data_oe = 1'b1; 308 | // For zero latency writes, we must not drive the RWDS 309 | // signal (see specs page 9). Depending on the latency 310 | // mode we thus drive only the DQ signals or DQ + RWDS. 311 | trx_tx_rwds_oe = ~ctl_write_zero_lat; 312 | end else begin 313 | state_d = Read; 314 | trx_tx_data_oe = 1'b0; 315 | trx_tx_rwds_oe = 1'b0; 316 | end 317 | end 318 | end 319 | Read: begin 320 | // Dataflow handled outside FSM 321 | trx_rx_clk_set = 1'b1; 322 | if (ctl_rclk_ena) begin 323 | trx_clk_ena = 1'b1; 324 | r_outstand_inc = 1'b1; 325 | tf_d.burst = tf_q.burst - phys_in_use; 326 | tf_d.address = tf_q.address + 1; 327 | if (ctl_tf_burst_last) begin 328 | timer_d = cfg_i.t_csh_cycles; 329 | state_d = WaitXfer; 330 | end 331 | end 332 | // Force-terminate access on burst time limit 333 | if (ctl_timer_one) begin 334 | timer_d = cfg_i.t_csh_cycles; 335 | state_d = WaitXfer; 336 | end 337 | end 338 | Write: begin 339 | // Drive DQ lines in write mode 340 | trx_tx_data_oe = 1'b1; 341 | // For zero-latency writes we must not use RWDS as mask signal 342 | trx_tx_rwds_oe = ~ctl_write_zero_lat; 343 | // Dataflow handled outside FSM 344 | if (ctl_wclk_ena) begin 345 | trx_clk_ena = 1'b1; 346 | tf_d.burst = tf_q.burst - phys_in_use; 347 | tf_d.address = tf_q.address + 1; 348 | if (ctl_tf_burst_last) begin 349 | b_pending_set = 1'b1; 350 | timer_d = cfg_i.t_csh_cycles; 351 | state_d = WaitXfer; 352 | end 353 | end 354 | // Force-terminate access on burst time limit 355 | if (ctl_timer_one) begin 356 | timer_d = cfg_i.t_csh_cycles; 357 | state_d = WaitXfer; 358 | end 359 | end 360 | WaitXfer: begin 361 | // Wait for FFed Clock and output to stop 362 | // May have to be prolonged for potential future devices with t_CSH > 0 363 | if (ctl_timer_zero) begin 364 | timer_d = cfg_i.t_read_write_recovery; 365 | state_d = WaitRWR; 366 | end 367 | end 368 | WaitRWR: begin 369 | trx_cs_ena = 1'b0; 370 | if (ctl_timer_rwr_done) begin 371 | if (ctl_tf_burst_done) begin 372 | state_d = Idle; 373 | end else begin 374 | state_d = SendCA; 375 | // Re-enable the io driver if we immediately start the 376 | // next transaction. 377 | trx_tx_data_oe = 1'b1; 378 | end 379 | end 380 | end 381 | endcase 382 | end 383 | 384 | // PHY state registers, including timer and transfer 385 | always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_phy 386 | if (~rst_ni) begin 387 | state_q <= Startup; 388 | timer_q <= StartupCycles; 389 | tf_q <= hyper_tf_t'{burst_type: 1'b1, default:'0}; 390 | cs_q <= '0; 391 | end else begin 392 | state_q <= state_d; 393 | timer_q <= timer_d; 394 | tf_q <= tf_d; 395 | cs_q <= cs_d; 396 | end 397 | end 398 | 399 | endmodule 400 | -------------------------------------------------------------------------------- /src/hyperbus_phy2r.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Luca Valente 6 | 7 | module hyperbus_phy2r #( 8 | parameter int unsigned AxiDataWidth = -1, 9 | parameter int unsigned NumPhys = -1, 10 | parameter type T = logic, 11 | parameter int unsigned BurstLength = -1, 12 | parameter int unsigned AddrWidth = $clog2(AxiDataWidth/8) 13 | ) ( 14 | input logic clk_i, 15 | input logic rst_ni, 16 | input logic [2:0] size, 17 | input logic is_a_read, 18 | input logic trans_handshake, 19 | input logic [AddrWidth-1:0] start_addr, 20 | input logic [BurstLength-1:0] burst_len, 21 | output logic axi_valid_o, 22 | input logic axi_ready_i, 23 | output T data_o, 24 | input logic phy_valid_i, 25 | output logic phy_ready_o, 26 | input logic [16*NumPhys-1:0] data_i, 27 | input logic last_i, 28 | input logic error_i 29 | ); 30 | 31 | // Cutting the combinatorial path between cdc fifo and AXI Master 32 | typedef enum logic [2:0] { 33 | Idle, 34 | WaitData, 35 | Sample, 36 | CntReady 37 | } hyper_splitter_state_t; 38 | 39 | hyper_splitter_state_t state_d, state_q; 40 | 41 | localparam int unsigned NumAxiBytes = AxiDataWidth/8; 42 | localparam int unsigned NumPhyBytes = NumPhys*2; 43 | localparam int unsigned AxiBytesInPhyBeat = NumAxiBytes/NumPhyBytes; 44 | localparam int unsigned WordCntWidth = (AxiBytesInPhyBeat==1) ? 1 : $clog2(AxiBytesInPhyBeat); 45 | 46 | logic [BurstLength-1:0] byte_axi_addr_d, byte_axi_addr_q; 47 | logic [BurstLength-1:0] byte_phy_cnt_d, byte_phy_cnt_q; 48 | logic [BurstLength-1:0] last_addr_d, last_addr_q; 49 | 50 | logic [3:0] size_d, size_q; 51 | T data_buffer_d, data_buffer_q; 52 | 53 | logic is_16_bw, is_8_bw; 54 | logic [WordCntWidth-1:0] word_cnt; 55 | logic enough_data; 56 | logic sent_available_data; 57 | logic [BurstLength-1:0] next_axi_addr; 58 | 59 | assign word_cnt = (AxiBytesInPhyBeat==1) ? '0 : byte_phy_cnt_q[($clog2(NumPhys)+1) +:WordCntWidth]; 60 | assign next_axi_addr = ((byte_axi_addr_q>>size_d)<< size_d) + (1<= next_axi_addr; 62 | assign sent_available_data = byte_axi_addr_d >= byte_phy_cnt_q; 63 | assign data_o.data = data_buffer_q.data; 64 | assign data_o.error = data_buffer_q.error; 65 | assign data_o.valid = '0; 66 | assign data_o.last = data_buffer_q.last && (last_addr_q==byte_axi_addr_d); 67 | 68 | always_comb begin : counter 69 | byte_axi_addr_d = byte_axi_addr_q; 70 | size_d = size_q; 71 | byte_phy_cnt_d = byte_phy_cnt_q; 72 | last_addr_d = last_addr_q; 73 | if (trans_handshake & is_a_read) begin 74 | byte_axi_addr_d[BurstLength-1:AddrWidth] = '0; 75 | byte_axi_addr_d[AddrWidth-1:0] = start_addr; 76 | size_d = size; 77 | byte_phy_cnt_d[BurstLength-1:AddrWidth] = '0; 78 | byte_phy_cnt_d[AddrWidth-1:0] = (start_addr>>NumPhys)<>size)<>size_q)<< size_q) + (1<200 is outside the spec and is unlikely to work with any HyperBus devices 66 | automatic hyper_cfg_t cfg = hyper_cfg_t'{ 67 | t_latency_access: 'h6, 68 | en_latency_additional: 'b0, 69 | t_burst_max: ((MinFreqMhz*35)/10), // t_{csm}: At lowest legal clock (100 MHz) 3.5us (0.5us safety margin) 70 | t_read_write_recovery: 'h6, 71 | t_rx_clk_delay: 'h8, 72 | t_tx_clk_delay: 'h8, 73 | address_mask_msb: 'd25, // 26 bit addresses = 2^6*2^20B == 64 MB per chip (biggest availale as of now) 74 | address_space: 'b0, 75 | phys_in_use: NumPhys-1, 76 | which_phy: NumPhys-1, 77 | t_csh_cycles: 'h1 78 | }; 79 | 80 | return cfg; 81 | endfunction 82 | 83 | endpackage 84 | -------------------------------------------------------------------------------- /src/hyperbus_stub.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Thomas Benz 6 | // Paul Scheffler 7 | 8 | module hyperbus #( 9 | parameter int unsigned NumChips = -1, 10 | parameter int unsigned AxiDataWidth = -1, 11 | parameter int unsigned AxiIdWidth = -1, 12 | parameter type axi_req_t = logic, 13 | parameter type axi_rsp_t = logic, 14 | parameter type axi_rule_t = logic 15 | ) ( 16 | input logic clk_phy_i, 17 | input logic clk_sys_i, 18 | input logic rst_ni, 19 | input logic test_mode_i, 20 | // AXI bus 21 | input axi_req_t axi_req_i, 22 | output axi_rsp_t axi_rsp_o, 23 | // Reg bus 24 | input reg_intf_pkg::req_a32_d32 reg_req_i, 25 | output reg_intf_pkg::rsp_d32 reg_rsp_o, 26 | // PHY interface 27 | output logic [NumChips-1:0] hyper_cs_no, 28 | output logic hyper_ck_o, 29 | output logic hyper_ck_no, 30 | output logic hyper_rwds_o, 31 | input logic hyper_rwds_i, 32 | output logic hyper_rwds_oe_o, 33 | input logic [7:0] hyper_dq_i, 34 | output logic [7:0] hyper_dq_o, 35 | output logic hyper_dq_oe_o, 36 | output logic hyper_reset_no, 37 | // Debug interface 38 | output logic debug_hyper_rwds_oe_o, 39 | output logic debug_hyper_dq_oe_o, 40 | output logic [3:0] debug_hyper_phy_state_o 41 | ); 42 | 43 | assign axi_rsp_o = '0; 44 | assign reg_rsp_o = '0; 45 | assign hyper_cs_no = '0; 46 | assign hyper_ck_o = '0; 47 | assign hyper_ck_no = '0; 48 | assign hyper_rwds_o = '0; 49 | assign hyper_rwds_oe_o = '0; 50 | assign hyper_dq_o = '0; 51 | assign hyper_dq_oe_o = '0; 52 | assign hyper_reset_no = '0; 53 | assign debug_hyper_rwds_oe_o = '0; 54 | assign debug_hyper_dq_oe_o = '0; 55 | assign debug_hyper_phy_state_o = '0; 56 | 57 | endmodule : hyperbus 58 | -------------------------------------------------------------------------------- /src/hyperbus_synth_wrap.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Thomas Benz 6 | // Paul Scheffler 7 | 8 | `include "axi/assign.svh" 9 | `include "axi/typedef.svh" 10 | `include "register_interface/typedef.svh" 11 | 12 | module hyperbus_lint_wrap #( 13 | // HyperBus parameters 14 | parameter int unsigned NumChips = 2, 15 | parameter int unsigned NumPhys = 2, 16 | parameter int unsigned IsClockODelayed = 0, 17 | // AXI parameters 18 | parameter int unsigned AxiIdWidth = 6, 19 | parameter int unsigned AxiAddrWidth = 48, 20 | parameter int unsigned AxiDataWidth = 128, 21 | parameter int unsigned AxiUserWidth = 1, 22 | // Regbus parameters 23 | parameter int unsigned RegAddrWidth = 32, 24 | parameter int unsigned RegDataWidth = 32, 25 | // Dependent parameters; do not override! 26 | parameter type axi_addr_t = logic [AxiAddrWidth-1:0], 27 | parameter type axi_data_t = logic [AxiDataWidth-1:0], 28 | parameter type axi_strb_t = logic [AxiDataWidth/8-1:0], 29 | parameter type axi_id_t = logic [AxiIdWidth-1:0], 30 | parameter type axi_user_t = logic [AxiUserWidth-1:0], 31 | parameter type reg_addr_t = logic [RegAddrWidth-1:0], 32 | parameter type reg_data_t = logic [RegDataWidth-1:0], 33 | parameter type reg_strb_t = logic [RegDataWidth/8-1:0] 34 | ) ( 35 | // SoC 36 | input logic clk_phy_i, 37 | input logic rst_phy_ni, 38 | input logic clk_sys_i, 39 | input logic rst_sys_ni, 40 | input logic test_mode_i, 41 | 42 | // AXI bus 43 | input axi_id_t axi_aw_id_i, 44 | input axi_addr_t axi_aw_addr_i, 45 | input axi_pkg::len_t axi_aw_len_i, 46 | input axi_pkg::size_t axi_aw_size_i, 47 | input axi_pkg::burst_t axi_aw_burst_i, 48 | input logic axi_aw_lock_i, 49 | input axi_pkg::cache_t axi_aw_cache_i, 50 | input axi_pkg::prot_t axi_aw_prot_i, 51 | input axi_pkg::qos_t axi_aw_qos_i, 52 | input axi_pkg::region_t axi_aw_region_i, 53 | input axi_pkg::atop_t axi_aw_atop_i, 54 | input axi_user_t axi_aw_user_i, 55 | input logic axi_aw_valid_i, 56 | output logic axi_aw_ready_o, 57 | input axi_data_t axi_w_data_i, 58 | input axi_strb_t axi_w_strb_i, 59 | input logic axi_w_last_i, 60 | input axi_user_t axi_w_user_i, 61 | input logic axi_w_valid_i, 62 | output logic axi_w_ready_o, 63 | output axi_id_t axi_b_id_o, 64 | output axi_pkg::resp_t axi_b_resp_o, 65 | output axi_user_t axi_b_user_o, 66 | output logic axi_b_valid_o, 67 | input logic axi_b_ready_i, 68 | input axi_id_t axi_ar_id_i, 69 | input axi_addr_t axi_ar_addr_i, 70 | input axi_pkg::len_t axi_ar_len_i, 71 | input axi_pkg::size_t axi_ar_size_i, 72 | input axi_pkg::burst_t axi_ar_burst_i, 73 | input logic axi_ar_lock_i, 74 | input axi_pkg::cache_t axi_ar_cache_i, 75 | input axi_pkg::prot_t axi_ar_prot_i, 76 | input axi_pkg::qos_t axi_ar_qos_i, 77 | input axi_pkg::region_t axi_ar_region_i, 78 | input axi_user_t axi_ar_user_i, 79 | input logic axi_ar_valid_i, 80 | output logic axi_ar_ready_o, 81 | output axi_id_t axi_r_id_o, 82 | output axi_data_t axi_r_data_o, 83 | output axi_pkg::resp_t axi_r_resp_o, 84 | output logic axi_r_last_o, 85 | output axi_user_t axi_r_user_o, 86 | output logic axi_r_valid_o, 87 | input logic axi_r_ready_i, 88 | 89 | // Reg bus 90 | input reg_addr_t rbus_req_addr_i, 91 | input logic rbus_req_write_i, 92 | input reg_data_t rbus_req_wdata_i, 93 | input reg_strb_t rbus_req_wstrb_i, 94 | input logic rbus_req_valid_i, 95 | output reg_data_t rbus_rsp_rdata_o, 96 | output logic rbus_rsp_ready_o, 97 | output logic rbus_rsp_error_o, 98 | 99 | // PHY interface 100 | output logic [NumPhys-1:0][NumChips-1:0] hyper_cs_no, 101 | output logic [NumPhys-1:0] hyper_ck_o, 102 | output logic [NumPhys-1:0] hyper_ck_no, 103 | output logic [NumPhys-1:0] hyper_rwds_o, 104 | input logic [NumPhys-1:0] hyper_rwds_i, 105 | output logic [NumPhys-1:0] hyper_rwds_oe_o, 106 | input logic [NumPhys-1:0][7:0] hyper_dq_i, 107 | output logic [NumPhys-1:0][7:0] hyper_dq_o, 108 | output logic [NumPhys-1:0] hyper_dq_oe_o, 109 | output logic [NumPhys-1:0] hyper_reset_no 110 | ); 111 | 112 | // Types 113 | `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, axi_addr_t, axi_id_t, axi_user_t) 114 | `AXI_TYPEDEF_W_CHAN_T(w_chan_t, axi_data_t, axi_strb_t, axi_user_t) 115 | `AXI_TYPEDEF_B_CHAN_T(b_chan_t, axi_id_t, axi_user_t) 116 | `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, axi_addr_t, axi_id_t, axi_user_t) 117 | `AXI_TYPEDEF_R_CHAN_T(r_chan_t, axi_data_t, axi_id_t, axi_user_t) 118 | `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) 119 | `AXI_TYPEDEF_RESP_T(axi_rsp_t, b_chan_t, r_chan_t) 120 | 121 | axi_req_t axi_req; 122 | axi_rsp_t axi_rsp; 123 | 124 | `REG_BUS_TYPEDEF_REQ(reg_req_t, reg_addr_t, reg_data_t, reg_strb_t) 125 | `REG_BUS_TYPEDEF_RSP(reg_rsp_t, reg_data_t) 126 | 127 | reg_req_t reg_req; 128 | reg_rsp_t reg_rsp; 129 | 130 | typedef axi_pkg::xbar_rule_32_t axi_rule_t; 131 | 132 | // Wrapped instance 133 | hyperbus #( 134 | .NumChips ( NumChips ), 135 | .NumPhys ( NumPhys ), 136 | .IsClockODelayed ( IsClockODelayed ), 137 | .AxiAddrWidth ( AxiAddrWidth ), 138 | .AxiDataWidth ( AxiDataWidth ), 139 | .AxiIdWidth ( AxiIdWidth ), 140 | .AxiUserWidth ( AxiUserWidth ), 141 | .axi_req_t ( axi_req_t ), 142 | .axi_rsp_t ( axi_rsp_t ), 143 | .axi_w_chan_t ( w_chan_t ), 144 | .axi_b_chan_t ( b_chan_t ), 145 | .axi_ar_chan_t ( ar_chan_t ), 146 | .axi_r_chan_t ( r_chan_t ), 147 | .axi_aw_chan_t ( aw_chan_t ), 148 | .RegAddrWidth ( RegAddrWidth ), 149 | .RegDataWidth ( RegDataWidth ), 150 | .reg_req_t ( reg_req_t ), 151 | .reg_rsp_t ( reg_rsp_t ), 152 | .axi_rule_t ( axi_rule_t ) 153 | ) i_hyperbus ( 154 | .clk_phy_i ( clk_phy_i ), 155 | .rst_phy_ni ( rst_phy_ni ), 156 | .clk_sys_i ( clk_sys_i ), 157 | .rst_sys_ni ( rst_sys_ni ), 158 | .test_mode_i ( test_mode_i ), 159 | .axi_req_i ( axi_req ), 160 | .axi_rsp_o ( axi_rsp ), 161 | .reg_req_i ( reg_req ), 162 | .reg_rsp_o ( reg_rsp ), 163 | .hyper_cs_no ( hyper_cs_no ), 164 | .hyper_ck_o ( hyper_ck_o ), 165 | .hyper_ck_no ( hyper_ck_no ), 166 | .hyper_rwds_o ( hyper_rwds_o ), 167 | .hyper_rwds_i ( hyper_rwds_i ), 168 | .hyper_rwds_oe_o ( hyper_rwds_oe_o ), 169 | .hyper_dq_i ( hyper_dq_i ), 170 | .hyper_dq_o ( hyper_dq_o ), 171 | .hyper_dq_oe_o ( hyper_dq_oe_o ), 172 | .hyper_reset_no ( hyper_reset_no ) 173 | ); 174 | 175 | // AXI Slave 176 | assign axi_req.aw.id = axi_aw_id_i; 177 | assign axi_req.aw.addr = axi_aw_addr_i; 178 | assign axi_req.aw.len = axi_aw_len_i; 179 | assign axi_req.aw.size = axi_aw_size_i; 180 | assign axi_req.aw.burst = axi_aw_burst_i; 181 | assign axi_req.aw.lock = axi_aw_lock_i; 182 | assign axi_req.aw.cache = axi_aw_cache_i; 183 | assign axi_req.aw.prot = axi_aw_prot_i; 184 | assign axi_req.aw.qos = axi_aw_qos_i; 185 | assign axi_req.aw.region = axi_aw_region_i; 186 | assign axi_req.aw.atop = axi_aw_atop_i; 187 | assign axi_req.aw.user = axi_aw_user_i; 188 | assign axi_req.aw_valid = axi_aw_valid_i; 189 | assign axi_aw_ready_o = axi_rsp.aw_ready; 190 | assign axi_req.w.data = axi_w_data_i; 191 | assign axi_req.w.strb = axi_w_strb_i; 192 | assign axi_req.w.last = axi_w_last_i; 193 | assign axi_req.w.user = axi_w_user_i; 194 | assign axi_req.w_valid = axi_w_valid_i; 195 | assign axi_w_ready_o = axi_rsp.w_ready; 196 | assign axi_b_id_o = axi_rsp.b.id; 197 | assign axi_b_resp_o = axi_rsp.b.resp; 198 | assign axi_b_user_o = axi_rsp.b.user; 199 | assign axi_b_valid_o = axi_rsp.b_valid; 200 | assign axi_req.b_ready = axi_b_ready_i; 201 | assign axi_req.ar.id = axi_ar_id_i; 202 | assign axi_req.ar.addr = axi_ar_addr_i; 203 | assign axi_req.ar.len = axi_ar_len_i; 204 | assign axi_req.ar.size = axi_ar_size_i; 205 | assign axi_req.ar.burst = axi_ar_burst_i; 206 | assign axi_req.ar.lock = axi_ar_lock_i; 207 | assign axi_req.ar.cache = axi_ar_cache_i; 208 | assign axi_req.ar.prot = axi_ar_prot_i; 209 | assign axi_req.ar.qos = axi_ar_qos_i; 210 | assign axi_req.ar.region = axi_ar_region_i; 211 | assign axi_req.ar.user = axi_ar_user_i; 212 | assign axi_req.ar_valid = axi_ar_valid_i; 213 | assign axi_ar_ready_o = axi_rsp.ar_ready; 214 | assign axi_r_id_o = axi_rsp.r.id; 215 | assign axi_r_data_o = axi_rsp.r.data; 216 | assign axi_r_resp_o = axi_rsp.r.resp; 217 | assign axi_r_last_o = axi_rsp.r.last; 218 | assign axi_r_user_o = axi_rsp.r.user; 219 | assign axi_r_valid_o = axi_rsp.r_valid; 220 | assign axi_req.r_ready = axi_r_ready_i; 221 | 222 | // Regbus slave 223 | assign reg_req.addr = rbus_req_addr_i; 224 | assign reg_req.write = rbus_req_write_i; 225 | assign reg_req.wdata = rbus_req_wdata_i; 226 | assign reg_req.wstrb = rbus_req_wstrb_i; 227 | assign reg_req.valid = rbus_req_valid_i; 228 | assign rbus_rsp_rdata_o = reg_rsp.rdata; 229 | assign rbus_rsp_ready_o = reg_rsp.ready; 230 | assign rbus_rsp_error_o = reg_rsp.error; 231 | 232 | endmodule : hyperbus_lint_wrap 233 | -------------------------------------------------------------------------------- /src/hyperbus_trx.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Paul Scheffler 6 | // Armin Berger 7 | // Stephan Keck 8 | 9 | module hyperbus_trx #( 10 | parameter int unsigned IsClockODelayed = -1, 11 | parameter int unsigned NumChips = 2, 12 | parameter int unsigned RxFifoLogDepth = 3, 13 | parameter int unsigned SyncStages = 2 14 | )( 15 | // Global signals 16 | input logic clk_i, 17 | input logic clk_i_90, 18 | input logic rst_ni, 19 | input logic test_mode_i, 20 | // Transciever control: facing controller 21 | input logic [NumChips-1:0] cs_i, 22 | input logic cs_ena_i, 23 | output logic rwds_sample_o, 24 | input logic rwds_sample_ena_i, 25 | 26 | input logic [3:0] tx_clk_delay_i, 27 | input logic tx_clk_ena_i, 28 | input logic [15:0] tx_data_i, 29 | input logic tx_data_oe_i, 30 | input logic [1:0] tx_rwds_i, 31 | input logic tx_rwds_oe_i, 32 | 33 | input logic [3:0] rx_clk_delay_i, 34 | input logic rx_clk_set_i, 35 | input logic rx_clk_reset_i, 36 | output logic [15:0] rx_data_o, 37 | output logic rx_valid_o, 38 | input logic rx_ready_i, 39 | // Physical interace: facing HyperBus 40 | output logic [NumChips-1:0] hyper_cs_no, 41 | output logic hyper_ck_o, 42 | output logic hyper_ck_no, 43 | output logic hyper_rwds_o, 44 | input logic hyper_rwds_i, 45 | output logic hyper_rwds_oe_o, 46 | input logic [7:0] hyper_dq_i, 47 | output logic [7:0] hyper_dq_o, 48 | output logic hyper_dq_oe_o, 49 | output logic hyper_reset_no 50 | ); 51 | 52 | // 90-degree-shifted clocks generated with delay line 53 | logic tx_clk_ena_q; 54 | logic tx_clk_90; 55 | logic rx_rwds_90; 56 | 57 | // Delayed clock enable synchronous with data 58 | 59 | // Intermediate RX signals for RWDS domain 60 | logic rx_rwds_clk_ena; 61 | logic rx_rwds_clk_orig; 62 | logic rx_rwds_clk; 63 | logic rx_rwds_soft_rst; 64 | logic [15:0] rx_rwds_fifo_in; 65 | logic rx_rwds_fifo_valid; 66 | logic rx_rwds_fifo_ready; 67 | 68 | // Feed through async reset 69 | assign hyper_reset_no = rst_ni; 70 | 71 | // ================= 72 | // TX + control 73 | // ================= 74 | 75 | // Shift clock by 90 degrees 76 | assign tx_clk_90 = clk_i_90; 77 | 78 | // 90deg-shifted differential output clock, sampling output bytes centrally 79 | hyperbus_clock_diff_out i_clock_diff_out ( 80 | .in_i ( tx_clk_90 ), 81 | .en_i ( tx_clk_ena_q ), 82 | .out_o ( hyper_ck_o ), 83 | .out_no ( hyper_ck_no ) 84 | ); 85 | 86 | // Synchronize output chip select to shifted differential output clock 87 | always_ff @(posedge tx_clk_90 or negedge rst_ni) begin : proc_ff_tx_shift90 88 | if (~rst_ni) hyper_cs_no <= '1; 89 | else hyper_cs_no <= cs_ena_i ? ~cs_i : '1; 90 | end 91 | 92 | // Data output DDR converters 93 | for (genvar i = 0; i <= 7; i++) begin: gen_ddr_tx_data 94 | hyperbus_ddr_out #( 95 | .Init ( 1'b0 ) 96 | ) i_ddr_tx_data ( 97 | .clk_i ( clk_i ), 98 | .rst_ni ( rst_ni ), 99 | .d0_i ( tx_data_i [i+8] ), 100 | .d1_i ( tx_data_i [i] ), 101 | .q_o ( hyper_dq_o [i] ) 102 | ); 103 | end 104 | 105 | // RWDS output DDR converter 106 | hyperbus_ddr_out #( 107 | .Init ( 1'b0 ) 108 | ) i_ddr_tx_rwds ( 109 | .clk_i ( clk_i ), 110 | .rst_ni ( rst_ni ), 111 | .d0_i ( tx_rwds_i [1] ), 112 | .d1_i ( tx_rwds_i [0] ), 113 | .q_o ( hyper_rwds_o ) 114 | ); 115 | 116 | // Delay output, clock enables to be synchronous with DDR-converted data 117 | // The delayed clock also ensures t_CSS is respected at the start, end of CS 118 | always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_tx_delay 119 | if(~rst_ni) begin 120 | hyper_rwds_oe_o <= 1'b0; 121 | hyper_dq_oe_o <= 1'b0; 122 | tx_clk_ena_q <= 1'b0; 123 | end else begin 124 | hyper_rwds_oe_o <= tx_rwds_oe_i; 125 | hyper_dq_oe_o <= tx_data_oe_i; 126 | tx_clk_ena_q <= tx_clk_ena_i; 127 | end 128 | end 129 | 130 | // Sample RWDS on demand for extra latency determination 131 | always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_rwds_sample 132 | if (~rst_ni) rwds_sample_o <= '0; 133 | else if (rwds_sample_ena_i) rwds_sample_o <= hyper_rwds_i; 134 | end 135 | 136 | // ======== 137 | // RX 138 | // ======== 139 | 140 | // Set and Reset RX clock enable 141 | always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ff_rx_delay 142 | if (~rst_ni) rx_rwds_clk_ena <= 1'b0; 143 | else if (rx_clk_set_i) rx_rwds_clk_ena <= 1'b1; 144 | else if (rx_clk_reset_i) rx_rwds_clk_ena <= 1'b0; 145 | end 146 | 147 | // Shift RWDS clock by 90 degrees 148 | hyperbus_delay i_delay_rx_rwds_90 ( 149 | .in_i ( hyper_rwds_i ), 150 | .delay_i ( rx_clk_delay_i ), 151 | .out_o ( rx_rwds_90 ) 152 | ); 153 | 154 | // Gate delayed RWDS clock with RX clock enable 155 | tc_clk_gating i_rwds_in_clk_gate ( 156 | .clk_i ( rx_rwds_90 ), 157 | .en_i ( rx_rwds_clk_ena ), 158 | .test_en_i ( test_mode_i ), 159 | .clk_o ( rx_rwds_clk_orig ) 160 | ); 161 | 162 | // Reset RX state on async reset or on gated clock (whenever inactive) 163 | // TODO: is this safe? Replace with tech cells? 164 | assign rx_rwds_soft_rst = ~rst_ni | (~rx_rwds_clk_ena & ~test_mode_i); 165 | 166 | // RX data is valid one cycle after each RX soft reset 167 | always_ff @(posedge rx_rwds_clk or posedge rx_rwds_soft_rst) begin : proc_read_in_valid 168 | if (rx_rwds_soft_rst) rx_rwds_fifo_valid <= 1'b0; 169 | else rx_rwds_fifo_valid <= 1'b1; 170 | end 171 | 172 | // If testing, replace gated RWDS clock with primary (PHY) clock; 173 | // PHY clock itself may be flattened with system clock _outside_ hyperbus! 174 | `ifdef TARGET_FPGA 175 | assign rx_rwds_clk = rx_rwds_clk_orig; 176 | `else 177 | tc_clk_mux2 i_rx_rwds_clk_mux ( 178 | .clk0_i ( rx_rwds_clk_orig ), 179 | .clk1_i ( clk_i ), 180 | .clk_sel_i ( test_mode_i ), 181 | .clk_o ( rx_rwds_clk ) 182 | ); 183 | `endif 184 | 185 | // Data input DDR conversion 186 | assign rx_rwds_fifo_in[7:0] = hyper_dq_i; 187 | always @(posedge rx_rwds_clk or posedge rx_rwds_soft_rst) begin : proc_ff_ddr_in 188 | if(rx_rwds_soft_rst) rx_rwds_fifo_in[15:8] <= '0; 189 | else rx_rwds_fifo_in[15:8] <= hyper_dq_i; 190 | end 191 | 192 | tc_clk_inverter i_rwds_clk_inverter ( 193 | .clk_i ( rx_rwds_clk ), 194 | .clk_o ( rx_rwds_clk_n ) 195 | ); 196 | 197 | // Cross input data from RWDS domain into system domain 198 | cdc_fifo_gray #( 199 | .T ( logic [15:0] ), 200 | .LOG_DEPTH ( RxFifoLogDepth ), 201 | .SYNC_STAGES( SyncStages ) 202 | ) i_rx_rwds_cdc_fifo ( 203 | // RWDS domain 204 | .src_clk_i ( rx_rwds_clk_n ), 205 | .src_rst_ni ( rst_ni ), 206 | .src_data_i ( rx_rwds_fifo_in ), 207 | .src_valid_i ( rx_rwds_fifo_valid ), 208 | .src_ready_o ( rx_rwds_fifo_ready ), 209 | // System domain 210 | .dst_clk_i ( clk_i ), 211 | .dst_rst_ni ( rst_ni ), 212 | .dst_data_o ( rx_data_o ), 213 | .dst_valid_o ( rx_valid_o ), 214 | .dst_ready_i ( rx_ready_i ) 215 | ); 216 | 217 | // assert that the FIFO does not drop data in simulation 218 | `ifndef SYNTHESIS 219 | always @(negedge rx_rwds_fifo_ready) assert(rx_rwds_fifo_ready) 220 | else $error("%m: HyperBus RX FIFO must always be ready to receive data"); 221 | `endif 222 | 223 | endmodule 224 | -------------------------------------------------------------------------------- /src/hyperbus_w2phy.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Thomas Benz 6 | // Paul Scheffler 7 | // Luca Valente 8 | 9 | module hyperbus_w2phy #( 10 | parameter int unsigned AxiDataWidth = -1, 11 | parameter int unsigned NumPhys = -1, 12 | parameter int unsigned BurstLength = -1, 13 | parameter type T = logic, 14 | parameter int unsigned AddrWidth = $clog2(AxiDataWidth/8) 15 | ) ( 16 | input logic clk_i, 17 | input logic rst_ni, 18 | input logic [2:0] size, 19 | input logic [AddrWidth-1:0] start_addr, 20 | input logic [BurstLength-1:0] len, 21 | input logic is_a_write, 22 | input logic trans_handshake, 23 | input logic axi_valid_i, 24 | output logic axi_ready_o, 25 | input T data_i, 26 | output logic phy_valid_o, 27 | input logic phy_ready_i, 28 | output logic [16*NumPhys-1:0] data_o, 29 | output logic last_o, 30 | output logic [2*NumPhys-1:0] strb_o 31 | ); 32 | 33 | localparam int unsigned NumAxiBytes = AxiDataWidth/8; 34 | localparam int unsigned NumPhyBytes = NumPhys*2; 35 | localparam int unsigned AxiBytesInPhyBeat = NumAxiBytes/NumPhyBytes; 36 | localparam int unsigned WordCntWidth = (AxiBytesInPhyBeat==1) ? 1 : $clog2(AxiBytesInPhyBeat); 37 | // Cutting the combinatorial path between AXI master and cdc fifo 38 | typedef enum logic [2:0] { 39 | Idle, 40 | Sample, 41 | CntReady 42 | } hyper_upsizer_state_t; 43 | 44 | hyper_upsizer_state_t state_d, state_q; 45 | 46 | typedef struct packed { 47 | logic [AxiDataWidth/8-1:0] strb; 48 | logic [AxiDataWidth-1:0] data; 49 | logic last; 50 | } w2phy_chan_t; 51 | 52 | w2phy_chan_t data_buffer_d, data_buffer_q; 53 | 54 | logic is_16_bw, is_8_bw; 55 | logic upsize; 56 | logic enough_data; 57 | logic first_tx_d, first_tx_q; 58 | 59 | 60 | logic [NumPhys*2-1:0] mask_strobe_d, mask_strobe_q; 61 | logic [WordCntWidth-1:0] word_cnt; 62 | logic [AddrWidth-1:0] byte_idx_d, byte_idx_q; 63 | logic [3:0] size_d, size_q; 64 | logic [AddrWidth-1:0] cnt_data_phy_d, cnt_data_phy_q; 65 | logic keep_sampling, keep_sending; 66 | logic upsize_q; 67 | 68 | assign is_8_bw = (size_d == 0); 69 | assign is_16_bw = (size_d == 1) ; 70 | assign upsize = (is_16_bw && (NumPhys==2)) | is_8_bw ; 71 | assign upsize_q = ( (size_q==1) && (NumPhys==2)) | (size_q==0) ; 72 | assign enough_data = !upsize; 73 | assign keep_sampling = (size_d<($clog2(NumPhys)+1)) && (byte_idx_d[NumPhys-1:0]!='0); 74 | assign keep_sending = (size_d>($clog2(NumPhys)+1)) && (cnt_data_phy_d != byte_idx_q); 75 | assign word_cnt = cnt_data_phy_q>>($clog2(NumPhys)+1); 76 | 77 | 78 | assign data_o = data_buffer_q.data[(16*NumPhys)*word_cnt +:(16*NumPhys)]; 79 | assign strb_o = data_buffer_q.strb[ (2*NumPhys)*word_cnt +: (2*NumPhys)] & mask_strobe_q; 80 | assign last_o = data_buffer_q.last && (!keep_sending || upsize_q); 81 | 82 | always_comb begin : counter 83 | byte_idx_d = byte_idx_q; 84 | size_d = size_q; 85 | cnt_data_phy_d = cnt_data_phy_q; 86 | first_tx_d = first_tx_q; 87 | if (trans_handshake & is_a_write) begin 88 | byte_idx_d = start_addr; 89 | size_d = size; 90 | cnt_data_phy_d = (start_addr>>NumPhys)<>size_d)<< size_d) + (1<=NumPhys) begin 173 | if (cnt_data_phy_d != byte_idx_q) begin 174 | state_d = CntReady; 175 | end else if (axi_valid_i) begin 176 | axi_ready_o = 1'b1; 177 | state_d = enough_data ? CntReady : Sample; 178 | end else begin 179 | state_d = Sample; 180 | end 181 | end else if (size_d 6 | 7 | module axi_hyper_tb 8 | import axi_pkg::*; 9 | #( 10 | parameter int unsigned NumChips = 2, 11 | parameter int unsigned NumPhys = 2, 12 | parameter int unsigned IsClockODelayed = 0, 13 | parameter int unsigned NB_CH = 1, 14 | /// ID width of the Full AXI slave port, master port has ID `AxiIdWidthFull + 32'd1` 15 | parameter int unsigned TbAxiIdWidthFull = 32'd6, 16 | /// Address width of the full AXI bus 17 | parameter int unsigned TbAxiAddrWidthFull = 32'd32, 18 | /// Data width of the full AXI bus 19 | parameter int unsigned TbAxiDataWidthFull = 32'd64, 20 | /// Number of random write transactions in a testblock. 21 | parameter int unsigned TbNumWrites = 32'd1000, 22 | /// Number of random read transactions in a testblock. 23 | parameter int unsigned TbNumReads = 32'd1000, 24 | /// Cycle time for the TB clock generator 25 | parameter time TbCyclTime = 5ns, 26 | /// Application time to the DUT 27 | parameter time TbApplTime = 1ns, 28 | /// Test time of the DUT 29 | parameter time TbTestTime = 4ns 30 | ); 31 | ///////////////////////////// 32 | // Axi channel definitions // 33 | ///////////////////////////// 34 | `include "axi/typedef.svh" 35 | `include "axi/assign.svh" 36 | 37 | ///////////////////////// 38 | // Clock and Reset gen // 39 | ///////////////////////// 40 | logic clk, rst_n; 41 | clk_rst_gen #( 42 | .ClkPeriod ( TbCyclTime ), 43 | .RstClkCycles ( 32'd5 ) 44 | ) i_clk_rst_gen ( 45 | .clk_o ( clk ), 46 | .rst_no ( rst_n ) 47 | ); 48 | 49 | localparam int WaitOneRefCycleBeforeAXI = 1; 50 | localparam int unsigned TbAxiUserWidthFull = 32'd1; 51 | typedef logic [TbAxiAddrWidthFull-1:0] axi_addr_t; 52 | typedef axi_pkg::xbar_rule_32_t rule_t; 53 | 54 | localparam int unsigned RegBusDW = 32; 55 | localparam int unsigned RegBusAW = 8; 56 | 57 | localparam int unsigned TbDramDataWidth = 8; 58 | localparam int unsigned TbDramLenWidth = 32'h80000; 59 | 60 | logic end_of_sim; 61 | 62 | //////////////////////////////// 63 | // Stimuli generator typedefs // 64 | //////////////////////////////// 65 | typedef axi_test::axi_rand_master #( 66 | .AW ( TbAxiAddrWidthFull ), 67 | .DW ( TbAxiDataWidthFull ), 68 | .IW ( TbAxiIdWidthFull ), 69 | .UW ( TbAxiUserWidthFull ), 70 | .TA ( TbApplTime ), 71 | .TT ( TbTestTime ), 72 | .TRAFFIC_SHAPING ( 0 ), 73 | .SIZE_ALIGN ( 1 ), 74 | .MAX_READ_TXNS ( 8 ), 75 | .MAX_WRITE_TXNS ( 8 ), 76 | .AX_MIN_WAIT_CYCLES ( 0 ), 77 | .AX_MAX_WAIT_CYCLES ( 0 ), 78 | .W_MIN_WAIT_CYCLES ( 0 ), 79 | .W_MAX_WAIT_CYCLES ( 0 ), 80 | .RESP_MIN_WAIT_CYCLES ( 0 ), 81 | .RESP_MAX_WAIT_CYCLES ( 0 ), 82 | .AXI_BURST_FIXED ( 1'b0 ), 83 | .AXI_BURST_INCR ( 1'b1 ), 84 | .AXI_BURST_WRAP ( 1'b0 ) 85 | ) axi_rand_master_t; 86 | 87 | typedef axi_test::axi_scoreboard #( 88 | .IW( TbAxiIdWidthFull ), 89 | .AW( TbAxiAddrWidthFull ), 90 | .DW( TbAxiDataWidthFull ), 91 | .UW( TbAxiUserWidthFull ), 92 | .TT( TbTestTime ) 93 | ) axi_scoreboard_mst_t; 94 | 95 | typedef reg_test::reg_driver #( 96 | .AW ( RegBusAW ), 97 | .DW ( RegBusDW ), 98 | .TT ( TbTestTime ) 99 | ) reg_bus_master_t; 100 | 101 | logic s_reg_error; 102 | 103 | AXI_BUS_DV #( 104 | .AXI_ADDR_WIDTH ( TbAxiAddrWidthFull ), 105 | .AXI_DATA_WIDTH ( TbAxiDataWidthFull ), 106 | .AXI_ID_WIDTH ( TbAxiIdWidthFull ), 107 | .AXI_USER_WIDTH ( TbAxiUserWidthFull ) 108 | ) axi_mst_intf_dv ( 109 | .clk_i ( clk ) 110 | ); 111 | 112 | AXI_BUS_DV #( 113 | .AXI_ADDR_WIDTH ( TbAxiAddrWidthFull ), 114 | .AXI_DATA_WIDTH ( TbAxiDataWidthFull ), 115 | .AXI_ID_WIDTH ( TbAxiIdWidthFull ), 116 | .AXI_USER_WIDTH ( TbAxiUserWidthFull ) 117 | ) score_mst_intf_dv ( 118 | .clk_i ( clk ) 119 | ); 120 | 121 | `AXI_ASSIGN_MONITOR(score_mst_intf_dv, axi_mst_intf_dv) 122 | 123 | REG_BUS #( 124 | .ADDR_WIDTH(RegBusAW), 125 | .DATA_WIDTH(RegBusDW) 126 | ) reg_bus_mst (.clk_i (clk)); 127 | 128 | //////////////////// 129 | // Address Ranges // 130 | //////////////////// 131 | localparam axi_addr_t MemRegionStart = axi_addr_t'(32'h8000_0000); 132 | localparam axi_addr_t MemRegionLength = axi_addr_t'(TbDramDataWidth * TbDramLenWidth); 133 | 134 | logic s_error; 135 | logic [31:0] reg_read; 136 | 137 | initial begin : proc_sim_crtl 138 | 139 | automatic axi_scoreboard_mst_t mst_scoreboard = new( score_mst_intf_dv ); 140 | automatic axi_rand_master_t axi_master = new( axi_mst_intf_dv ); 141 | automatic reg_bus_master_t reg_master = new( reg_bus_mst ); 142 | 143 | // Reset the AXI drivers and scoreboards 144 | end_of_sim = 1'b0; 145 | mst_scoreboard.reset(); 146 | axi_master.reset(); 147 | reg_master.reset_master(); 148 | 149 | // Set some mem regions for rand axi master 150 | axi_master.add_memory_region(32'h8000_0000, 32'h8000_0000 + ( TbDramDataWidth * TbDramLenWidth ), axi_pkg::NORMAL_NONCACHEABLE_BUFFERABLE); 151 | 152 | mst_scoreboard.enable_all_checks(); 153 | 154 | @(posedge rst_n); 155 | mst_scoreboard.monitor(); 156 | 157 | #600350ns; 158 | 159 | $display("==========================="); 160 | $display("= Random AXI transactions ="); 161 | $display("==========================="); 162 | 163 | axi_master.run(TbNumReads, TbNumWrites); 164 | 165 | $display("==========================="); 166 | $display("= Test finished ="); 167 | $display("==========================="); 168 | 169 | #50ns; 170 | 171 | if(NumPhys==2) begin 172 | 173 | mst_scoreboard.clear_range(32'h8000_0000, 32'h8000_0000 + ( TbDramDataWidth * TbDramLenWidth )); 174 | 175 | $display("==========================="); 176 | $display("= Use only phy 0 ="); 177 | $display("==========================="); 178 | 179 | reg_master.send_write(32'h20,1'b0,'1,s_reg_error); 180 | reg_master.send_write(32'h24,1'b0,'1,s_reg_error); 181 | if (s_reg_error != 1'b0) $error("unexpected error"); 182 | 183 | axi_master.reset(); 184 | 185 | $display("==========================="); 186 | $display("= Random AXI transactions ="); 187 | $display("==========================="); 188 | 189 | axi_master.run(TbNumReads, TbNumWrites); 190 | 191 | $display("==========================="); 192 | $display("= Test finished ="); 193 | $display("==========================="); 194 | 195 | mst_scoreboard.clear_range(32'h8000_0000, 32'h8000_0000 + ( TbDramDataWidth * TbDramLenWidth )); 196 | 197 | $display("==========================="); 198 | $display("= Use only phy 1 ="); 199 | $display("==========================="); 200 | 201 | reg_master.send_write(32'h24,1'b1,'1,s_reg_error); 202 | if (s_reg_error != 1'b0) $error("unexpected error"); 203 | 204 | axi_master.reset(); 205 | 206 | $display("==========================="); 207 | $display("= Random AXI transactions ="); 208 | $display("==========================="); 209 | 210 | axi_master.run(TbNumReads, TbNumWrites); 211 | 212 | $display("==========================="); 213 | $display("= Test finished ="); 214 | $display("==========================="); 215 | 216 | end // if (NumPhys==2) 217 | 218 | end_of_sim = 1'b1; 219 | $finish(); 220 | end 221 | 222 | /////////////////////// 223 | // Design under test // 224 | /////////////////////// 225 | dut_if #( 226 | .TbTestTime ( TbTestTime ), 227 | .AxiDataWidth ( TbAxiDataWidthFull ), 228 | .AxiAddrWidth ( TbAxiAddrWidthFull ), 229 | .AxiIdWidth ( TbAxiIdWidthFull ), 230 | .AxiUserWidth ( TbAxiUserWidthFull ), 231 | 232 | .RegAw ( RegBusAW ), 233 | .RegDw ( RegBusDW ), 234 | 235 | .NumChips ( NumChips ), 236 | .NumPhys ( NumPhys ), 237 | .IsClockODelayed ( IsClockODelayed ), 238 | .axi_rule_t ( rule_t ) 239 | ) i_dut_if ( 240 | // clk and rst signal 241 | .clk_i ( clk ), 242 | .rst_ni ( rst_n ), 243 | .end_sim_i ( end_of_sim ), 244 | .axi_slv_if ( axi_mst_intf_dv ), 245 | .reg_slv_if ( reg_bus_mst ) 246 | ); 247 | 248 | endmodule 249 | -------------------------------------------------------------------------------- /test/dut_if.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2023 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 | // Luca Valente 6 | 7 | `timescale 1 ns/1 ps 8 | 9 | `include "axi/typedef.svh" 10 | `include "axi/assign.svh" 11 | `include "register_interface/typedef.svh" 12 | `include "register_interface/assign.svh" 13 | 14 | module dut_if 15 | #( 16 | parameter time TbTestTime = 4ns, 17 | parameter int AxiDataWidth = -1, 18 | parameter int AxiAddrWidth = -1, 19 | parameter int AxiIdWidth = -1, 20 | parameter int AxiUserWidth = -1, 21 | 22 | parameter int RegAw = -1, 23 | parameter int RegDw = -1, 24 | 25 | parameter int NumChips = -1, 26 | parameter int NumPhys = -1, 27 | parameter int IsClockODelayed = -1, 28 | parameter type axi_rule_t = logic 29 | )( 30 | input logic clk_i, 31 | input logic rst_ni, 32 | input logic end_sim_i, 33 | 34 | AXI_BUS_DV.Slave axi_slv_if, 35 | REG_BUS.in reg_slv_if 36 | ); 37 | localparam int unsigned DRAM_DB_WIDTH = 16; 38 | 39 | typedef logic [AxiIdWidth-1:0] axi_id_t; 40 | typedef logic [AxiAddrWidth-1:0] axi_addr_t; 41 | typedef logic [AxiDataWidth-1:0] axi_data_t; 42 | typedef logic [AxiDataWidth/8-1:0] axi_strb_t; 43 | typedef logic [AxiUserWidth-1:0] axi_user_t; 44 | 45 | `AXI_TYPEDEF_AW_CHAN_T(axi_aw_chan_t, axi_addr_t, axi_id_t, axi_user_t) 46 | `AXI_TYPEDEF_W_CHAN_T(axi_w_chan_t, axi_data_t, axi_strb_t, axi_user_t) 47 | `AXI_TYPEDEF_B_CHAN_T(axi_b_chan_t, axi_id_t, axi_user_t) 48 | `AXI_TYPEDEF_AR_CHAN_T(axi_ar_chan_t, axi_addr_t, axi_id_t, axi_user_t) 49 | `AXI_TYPEDEF_R_CHAN_T(axi_r_chan_t, axi_data_t, axi_id_t, axi_user_t) 50 | `AXI_TYPEDEF_REQ_T(axi_req_t, axi_aw_chan_t, axi_w_chan_t, axi_ar_chan_t) 51 | `AXI_TYPEDEF_RESP_T(axi_resp_t, axi_b_chan_t, axi_r_chan_t) 52 | 53 | axi_req_t axi_req; 54 | axi_resp_t axi_resp; 55 | 56 | `AXI_ASSIGN_TO_REQ(axi_req, axi_slv_if) 57 | `AXI_ASSIGN_FROM_RESP(axi_slv_if, axi_resp) 58 | 59 | typedef logic [RegAw-1:0] reg_addr_t; 60 | typedef logic [RegDw-1:0] reg_data_t; 61 | typedef logic [RegDw/8-1:0] reg_strb_t; 62 | 63 | `REG_BUS_TYPEDEF_REQ(reg_req_t, reg_addr_t, reg_data_t, reg_strb_t) 64 | `REG_BUS_TYPEDEF_RSP(reg_rsp_t, reg_data_t) 65 | 66 | reg_req_t reg_req; 67 | reg_rsp_t reg_resp; 68 | 69 | `REG_BUS_ASSIGN_TO_REQ(reg_req,reg_slv_if) 70 | `REG_BUS_ASSIGN_FROM_RSP(reg_slv_if,reg_resp) 71 | 72 | 73 | logic [NumPhys-1:0][NumChips-1:0] hyper_cs_n_wire; 74 | logic [NumPhys-1:0] hyper_ck_wire; 75 | logic [NumPhys-1:0] hyper_ck_n_wire; 76 | logic [NumPhys-1:0] hyper_rwds_o; 77 | logic [NumPhys-1:0] hyper_rwds_i; 78 | logic [NumPhys-1:0] hyper_rwds_oe; 79 | logic [NumPhys-1:0][7:0] hyper_dq_i; 80 | logic [NumPhys-1:0][7:0] hyper_dq_o; 81 | logic [NumPhys-1:0] hyper_dq_oe; 82 | logic [NumPhys-1:0] hyper_reset_n_wire; 83 | 84 | wire [NumPhys-1:0][NumChips-1:0] pad_hyper_csn; 85 | wire [NumPhys-1:0] pad_hyper_ck; 86 | wire [NumPhys-1:0] pad_hyper_ckn; 87 | wire [NumPhys-1:0] pad_hyper_rwds; 88 | wire [NumPhys-1:0] pad_hyper_reset; 89 | wire [NumPhys-1:0][7:0] pad_hyper_dq; 90 | 91 | axi_chan_logger #( 92 | .aw_chan_t ( axi_aw_chan_t ), 93 | .w_chan_t ( axi_w_chan_t ), 94 | .b_chan_t ( axi_b_chan_t ), 95 | .ar_chan_t ( axi_ar_chan_t ), 96 | .r_chan_t ( axi_r_chan_t ), 97 | .TestTime ( TbTestTime ) 98 | ) i_chan_logger 99 | ( 100 | .clk_i ( clk_i ), 101 | .rst_ni ( rst_ni ), 102 | .end_sim_i ( end_sim_i ), 103 | 104 | .aw_chan_i ( axi_req.aw ), 105 | .aw_valid_i ( axi_req.aw_valid ), 106 | .aw_ready_i ( axi_resp.aw_ready ), 107 | 108 | .w_chan_i ( axi_req.w ), 109 | .w_valid_i ( axi_req.w_valid ), 110 | .w_ready_i ( axi_resp.w_ready ), 111 | 112 | .b_chan_i ( axi_resp.b ), 113 | .b_valid_i ( axi_resp.b_valid ), 114 | .b_ready_i ( axi_req.b_ready ), 115 | 116 | .ar_chan_i ( axi_req.ar ), 117 | .ar_valid_i ( axi_req.ar_valid ), 118 | .ar_ready_i ( axi_resp.ar_ready ), 119 | 120 | .r_chan_i ( axi_resp.r ), 121 | .r_valid_i ( axi_resp.r_valid ), 122 | .r_ready_i ( axi_req.r_ready ) 123 | ); 124 | 125 | // DUT 126 | hyperbus #( 127 | .NumChips ( NumChips ), 128 | .NumPhys ( NumPhys ), 129 | .AxiAddrWidth ( AxiAddrWidth ), 130 | .AxiDataWidth ( AxiDataWidth ), 131 | .AxiIdWidth ( AxiIdWidth ), 132 | .AxiUserWidth ( AxiUserWidth ), 133 | .axi_req_t ( axi_req_t ), 134 | .axi_rsp_t ( axi_resp_t ), 135 | .axi_aw_chan_t ( axi_aw_chan_t ), 136 | .axi_w_chan_t ( axi_w_chan_t ), 137 | .axi_b_chan_t ( axi_b_chan_t ), 138 | .axi_ar_chan_t ( axi_ar_chan_t ), 139 | .axi_r_chan_t ( axi_r_chan_t ), 140 | .RegAddrWidth ( RegAw ), 141 | .RegDataWidth ( RegDw ), 142 | .reg_req_t ( reg_req_t ), 143 | .reg_rsp_t ( reg_rsp_t ), 144 | .IsClockODelayed( 0 ), 145 | .axi_rule_t ( axi_rule_t ) 146 | ) i_dut ( 147 | .clk_phy_i ( clk_i ), 148 | .rst_phy_ni ( rst_ni ), 149 | .clk_sys_i ( clk_i ), 150 | .rst_sys_ni ( rst_ni ), 151 | .test_mode_i ( 1'b0 ), 152 | .axi_req_i ( axi_req ), 153 | .axi_rsp_o ( axi_resp ), 154 | .reg_req_i ( reg_req ), 155 | .reg_rsp_o ( reg_resp ), 156 | 157 | .hyper_cs_no ( hyper_cs_n_wire ), 158 | .hyper_ck_o ( hyper_ck_wire ), 159 | .hyper_ck_no ( hyper_ck_n_wire ), 160 | .hyper_rwds_o ( hyper_rwds_o ), 161 | .hyper_rwds_i ( hyper_rwds_i ), 162 | .hyper_rwds_oe_o ( hyper_rwds_oe ), 163 | .hyper_dq_i ( hyper_dq_i ), 164 | .hyper_dq_o ( hyper_dq_o ), 165 | .hyper_dq_oe_o ( hyper_dq_oe ), 166 | .hyper_reset_no ( hyper_reset_n_wire ) 167 | 168 | ); 169 | 170 | 171 | generate 172 | for (genvar i=0; i 6 | 7 | `timescale 1 ns/1 ps 8 | 9 | `include "axi/assign.svh" 10 | `include "axi/typedef.svh" 11 | `include "register_interface/typedef.svh" 12 | 13 | module fixture_hyperbus #( 14 | parameter int unsigned NumChips = 2, 15 | parameter int unsigned NumPhys = 2 16 | ); 17 | 18 | 19 | int unsigned k, j; 20 | 21 | localparam time SYS_TCK = 4ns; 22 | localparam time SYS_TA = 2ns; 23 | localparam time SYS_TT = SYS_TCK - 1ns; 24 | 25 | localparam time PHY_TCK = 6ns; 26 | 27 | logic sys_clk = 0; 28 | logic phy_clk = 0; 29 | logic test_mode = 0; 30 | logic rst_n = 1; 31 | logic eos = 0; // end of sim 32 | 33 | // -------------------- AXI drivers -------------------- 34 | 35 | localparam AxiAw = 32; 36 | localparam AxiDw = 64; 37 | localparam AxiMaxSize = $clog2(AxiDw/8); 38 | localparam AxiIw = 6; 39 | localparam RegAw = 32; 40 | localparam RegDw = 32; 41 | 42 | typedef axi_pkg::xbar_rule_32_t rule_t; 43 | 44 | typedef logic [AxiAw-1:0] axi_addr_t; 45 | typedef logic [AxiDw-1:0] axi_data_t; 46 | typedef logic [AxiDw/8-1:0] axi_strb_t; 47 | typedef logic [AxiIw-1:0] axi_id_t; 48 | 49 | `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, axi_addr_t, axi_id_t, logic [0:0]) 50 | `AXI_TYPEDEF_W_CHAN_T(w_chan_t, axi_data_t, axi_strb_t, logic [0:0]) 51 | `AXI_TYPEDEF_B_CHAN_T(b_chan_t, axi_id_t, logic [0:0]) 52 | `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, axi_addr_t, axi_id_t, logic [0:0]) 53 | `AXI_TYPEDEF_R_CHAN_T(r_chan_t, axi_data_t, axi_id_t, logic [0:0]) 54 | `AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) 55 | `AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) 56 | 57 | req_t axi_master_req; 58 | resp_t axi_master_rsp; 59 | 60 | AXI_BUS_DV #( 61 | .AXI_ADDR_WIDTH(AxiAw ), 62 | .AXI_DATA_WIDTH(AxiDw ), 63 | .AXI_ID_WIDTH (AxiIw ), 64 | .AXI_USER_WIDTH(1 ) 65 | ) axi_dv(sys_clk); 66 | 67 | AXI_BUS #( 68 | .AXI_ADDR_WIDTH(AxiAw ), 69 | .AXI_DATA_WIDTH(AxiDw ), 70 | .AXI_ID_WIDTH (AxiIw ), 71 | .AXI_USER_WIDTH(1 ) 72 | ) axi_master(); 73 | 74 | `AXI_ASSIGN(axi_master, axi_dv) 75 | 76 | `AXI_ASSIGN_TO_REQ(axi_master_req, axi_master) 77 | `AXI_ASSIGN_FROM_RESP(axi_master, axi_master_rsp) 78 | 79 | typedef axi_test::axi_driver #(.AW(AxiAw ), .DW(AxiDw ), .IW(AxiIw ), .UW(1), .TA(SYS_TA), .TT(SYS_TT)) axi_drv_t; 80 | axi_drv_t axi_master_drv = new(axi_dv); 81 | 82 | axi_test::axi_ax_beat #(.AW(AxiAw ), .IW(AxiIw ), .UW(1)) ar_beat = new(); 83 | axi_test::axi_r_beat #(.DW(AxiDw ), .IW(AxiIw ), .UW(1)) r_beat = new(); 84 | axi_test::axi_ax_beat #(.AW(AxiAw ), .IW(AxiIw ), .UW(1)) aw_beat = new(); 85 | axi_test::axi_w_beat #(.DW(AxiDw ), .UW(1)) w_beat = new(); 86 | axi_test::axi_b_beat #(.IW(AxiIw ), .UW(1)) b_beat = new(); 87 | 88 | // -------------------------- Regbus driver -------------------------- 89 | 90 | typedef logic [RegAw-1:0] reg_addr_t; 91 | typedef logic [RegDw-1:0] reg_data_t; 92 | typedef logic [RegDw/8-1:0] reg_strb_t; 93 | 94 | `REG_BUS_TYPEDEF_REQ(reg_req_t, reg_addr_t, reg_data_t, reg_strb_t) 95 | `REG_BUS_TYPEDEF_RSP(reg_rsp_t, reg_data_t) 96 | 97 | logic [AxiDw-1:0] trans_wdata; 98 | logic [AxiDw-1:0] trans_rdata; 99 | axi_addr_t temp_waddr; 100 | axi_addr_t temp_raddr; 101 | logic [4:0] last_waddr; 102 | logic [4:0] last_raddr; 103 | typedef logic [AxiDw-1:0] data_t; 104 | data_t memory[bit [31:0]]; 105 | int read_index = 0; 106 | int write_index = 0; 107 | 108 | 109 | reg_req_t reg_req; 110 | reg_rsp_t reg_rsp; 111 | 112 | REG_BUS #( 113 | .ADDR_WIDTH( RegAw ), 114 | .DATA_WIDTH( RegDw ) 115 | ) i_rbus ( 116 | .clk_i (sys_clk) 117 | ); 118 | integer fr, fw; 119 | 120 | reg_test::reg_driver #( 121 | .AW ( RegAw ), 122 | .DW ( RegDw ), 123 | .TA ( SYS_TA ), 124 | .TT ( SYS_TT ) 125 | ) i_rmaster = new( i_rbus ); 126 | 127 | `ifdef TARGET_POST_SYNTH_SIM 128 | assign reg_req = reg_req_t'{ 129 | addr: $isunknown(i_rbus.addr ) ? 1'b0 : i_rbus.addr , 130 | write: $isunknown(i_rbus.write) ? 1'b0 : i_rbus.write, 131 | wdata: $isunknown(i_rbus.wdata) ? 1'b0 : i_rbus.wdata, 132 | wstrb: $isunknown(i_rbus.wstrb) ? 1'b0 : i_rbus.wstrb, 133 | valid: $isunknown(i_rbus.valid) ? 1'b0 : i_rbus.valid 134 | }; 135 | `else 136 | assign reg_req = reg_req_t'{ 137 | addr: i_rbus.addr, 138 | write: i_rbus.write, 139 | wdata: i_rbus.wdata, 140 | wstrb: i_rbus.wstrb, 141 | valid: i_rbus.valid 142 | }; 143 | `endif 144 | 145 | assign i_rbus.rdata = reg_rsp.rdata; 146 | assign i_rbus.ready = reg_rsp.ready; 147 | assign i_rbus.error = reg_rsp.error; 148 | 149 | // -------------------------- DUT -------------------------- 150 | logic [NumPhys-1:0][NumChips-1:0] hyper_cs_n_wire; 151 | logic [NumPhys-1:0] hyper_ck_wire; 152 | logic [NumPhys-1:0] hyper_ck_n_wire; 153 | logic [NumPhys-1:0] hyper_rwds_o; 154 | logic [NumPhys-1:0] hyper_rwds_i; 155 | logic [NumPhys-1:0] hyper_rwds_oe; 156 | logic [NumPhys-1:0][7:0] hyper_dq_i; 157 | logic [NumPhys-1:0][7:0] hyper_dq_o; 158 | logic [NumPhys-1:0] hyper_dq_oe; 159 | logic [NumPhys-1:0] hyper_reset_n_wire; 160 | 161 | wire [NumPhys-1:0][NumChips-1:0] pad_hyper_csn; 162 | wire [NumPhys-1:0] pad_hyper_ck; 163 | wire [NumPhys-1:0] pad_hyper_ckn; 164 | wire [NumPhys-1:0] pad_hyper_rwds; 165 | wire [NumPhys-1:0] pad_hyper_reset; 166 | wire [NumPhys-1:0][7:0] pad_hyper_dq; 167 | 168 | // DUT 169 | hyperbus #( 170 | .NumChips ( NumChips ), 171 | .NumPhys ( NumPhys ), 172 | .AxiAddrWidth ( AxiAw ), 173 | .AxiDataWidth ( AxiDw ), 174 | .AxiIdWidth ( AxiIw ), 175 | .AxiUserWidth ( 1 ), 176 | .axi_req_t ( req_t ), 177 | .axi_rsp_t ( resp_t ), 178 | .axi_aw_chan_t ( aw_chan_t ), 179 | .axi_w_chan_t ( w_chan_t ), 180 | .axi_b_chan_t ( b_chan_t ), 181 | .axi_ar_chan_t ( ar_chan_t ), 182 | .axi_r_chan_t ( r_chan_t ), 183 | .RegAddrWidth ( RegAw ), 184 | .RegDataWidth ( RegDw ), 185 | .reg_req_t ( reg_req_t ), 186 | .reg_rsp_t ( reg_rsp_t ), 187 | .IsClockODelayed( 0 ), 188 | .axi_rule_t ( rule_t ) 189 | ) i_dut ( 190 | .clk_phy_i ( phy_clk ), 191 | .rst_phy_ni ( rst_n ), 192 | .clk_sys_i ( sys_clk ), 193 | .rst_sys_ni ( rst_n ), 194 | .test_mode_i ( test_mode ), 195 | .axi_req_i ( axi_master_req ), 196 | .axi_rsp_o ( axi_master_rsp ), 197 | .reg_req_i ( reg_req ), 198 | .reg_rsp_o ( reg_rsp ), 199 | .hyper_cs_no ( hyper_cs_n_wire ), 200 | .hyper_ck_o ( hyper_ck_wire ), 201 | .hyper_ck_no ( hyper_ck_n_wire ), 202 | .hyper_rwds_o ( hyper_rwds_o ), 203 | .hyper_rwds_i ( hyper_rwds_i ), 204 | .hyper_rwds_oe_o ( hyper_rwds_oe ), 205 | .hyper_dq_i ( hyper_dq_i ), 206 | .hyper_dq_o ( hyper_dq_o ), 207 | .hyper_dq_oe_o ( hyper_dq_oe ), 208 | .hyper_reset_no ( hyper_reset_n_wire ) 209 | 210 | ); 211 | 212 | 213 | generate 214 | for (genvar i=0; iAxiMaxSize) begin 356 | $display("Not supported"); 357 | end else begin 358 | 359 | axi_master_drv.send_ar(ar_beat); 360 | $display("%p", ar_beat); 361 | 362 | temp_raddr = raddr; 363 | last_raddr = '0; 364 | 365 | for(int unsigned i = 0; i < burst_len + 1; i++) begin 366 | axi_master_drv.recv_r(r_beat); 367 | `ifdef AXI_VERBOSE 368 | $display("%p", r_beat); 369 | $display("%x", r_beat.r_data); 370 | `endif 371 | trans_rdata = '1; 372 | if (i==0) begin 373 | for(k =temp_raddr[AxiMaxSize-1:0]; k<((temp_raddr[AxiMaxSize-1:0]>>size)<>size)<>size)<>size)<>size)<>size)< 6 | // Luca Valente 7 | 8 | `timescale 1 ns/1 ps 9 | 10 | module hyperbus_tb; 11 | 12 | localparam NumPhys=2; 13 | 14 | fixture_hyperbus #(.NumChips(2), .NumPhys(NumPhys) ) fix (); 15 | 16 | logic error; 17 | 18 | initial begin 19 | fix.reset_end(); 20 | #500us; 21 | // Set initial latency to additional latency 22 | fix.i_rmaster.send_write('h4, 'h1, '1, error); 23 | // Reduce max burst length from 350 to 250 to avoid CS to be 0 more than 4us 24 | fix.i_rmaster.send_write('h8, 'd250, '1, error); 25 | 26 | #200ns; 27 | 28 | $display("================="); 29 | $display("128 BIT MEGABURST"); 30 | $display("================="); 31 | 32 | // 128 bit access (burst, extrawide --> will be split) 33 | fix.write_axi('ha00, 4090, 4, 0, 'hffff); 34 | fix.read_axi('ha00, 4090, 4); 35 | 36 | $display("================="); 37 | $display("128 BIT BURSTS"); 38 | $display("================="); 39 | 40 | // 128 bit access (burst) 41 | fix.write_axi('h110, 3, 4, 0, 'hffff); 42 | fix.read_axi('h110, 3, 4); 43 | 44 | $display("================="); 45 | $display("64 BIT MEGABURST "); 46 | $display("================="); 47 | 48 | // 128 bit access (burst, extrawide --> will be split) 49 | fix.write_axi('ha00, 4090, 3, 0, 'hffff); 50 | fix.read_axi('ha00, 4090, 3); 51 | 52 | $display("================="); 53 | $display("64 BIT BURSTS"); 54 | $display("================="); 55 | 56 | // TODO (unaligned xfers not yet supported): narrow 64 bit burst 57 | fix.write_axi('h210, 0, 3, 0, 'hffff); 58 | fix.read_axi('h210, 0, 3); 59 | 60 | // wide 64 bit burst 61 | fix.write_axi('h228, 3, 3, 0, 'hffff); 62 | fix.read_axi('h228, 3, 3); 63 | 64 | #1471ns; 65 | 66 | $display("================="); 67 | $display("32 BIT MEGABURST "); 68 | $display("================="); 69 | 70 | // 128 bit access (burst, extrawide --> will be split) 71 | fix.write_axi('ha00, 4090, 2, 0, 'hffff); 72 | fix.read_axi('ha00, 4090, 2); 73 | 74 | $display("================="); 75 | $display("32 BIT BURSTS"); 76 | $display("================="); 77 | 78 | // narrow 32 bit burst 79 | fix.write_axi('h304, 1, 2, 0, 'hffff); 80 | fix.read_axi('h304, 1, 2); 81 | 82 | // wide 32 bit burst 83 | fix.write_axi('h314, 5, 2, 0, 'hffff); 84 | fix.read_axi('h314, 5, 2); 85 | 86 | $display("================="); 87 | $display("16 BIT BURSTS"); 88 | $display("================="); 89 | 90 | // wide 16 bit burst 91 | fix.write_axi('h410, 18, 1, 0, 'hffff); 92 | fix.read_axi('h410, 18, 1); 93 | 94 | // narrow 16 bit burst 95 | fix.write_axi('h402, 5, 1, 0, 'hffff); 96 | fix.read_axi('h402, 5, 1); 97 | 98 | // wide 16 bit burst 99 | fix.write_axi('h470, 5, 1, 0, 'hffff); 100 | fix.read_axi('h470, 5, 1); 101 | 102 | // narrow 16 bit burst 103 | fix.write_axi('h452, 6, 1, 0, 'hffff); 104 | fix.read_axi('h452, 6, 1); 105 | 106 | $display("================="); 107 | $display("8 BIT BURSTS"); 108 | $display("================="); 109 | 110 | // narrow 8 bit burst 111 | fix.write_axi('h500, 5, 0, 0, 'hffff); 112 | fix.read_axi('h500, 5, 0); 113 | 114 | // wide 8 bit burst 115 | fix.write_axi('h513, 25, 0, 0, 'hffff); 116 | fix.read_axi('h513, 25, 0); 117 | 118 | $display("================="); 119 | $display("128 BIT ALIGNED ACCESSES"); 120 | $display("================="); 121 | 122 | // 128 bit access 123 | fix.write_axi('h100, 0, 4, 'hbad0_beef_cafe_dead_b00b_8888_7777_aa55, 'hffff); 124 | fix.read_axi('h100, 0, 4); 125 | 126 | $display("================="); 127 | $display("64 BIT ALIGNED ACCESSES"); 128 | $display("================="); 129 | 130 | fix.write_axi('h00, 0, 3, 0, 'h00ff); 131 | fix.write_axi('h08, 0, 3, 0, 'hff00); 132 | 133 | fix.read_axi('h00, 0, 3); 134 | fix.read_axi('h08, 0, 3); 135 | 136 | #2557ns; 137 | 138 | for(int p; p<100; p++) begin 139 | fix.write_axi(p*8, 0, 3, 0, 'hffff); 140 | fix.read_axi(p*8, 0, 3); 141 | end 142 | 143 | // clean up 144 | fix.write_axi('h00, 100, 3, 1, 'hffff); 145 | fix.read_axi('h0, 100, 3); 146 | 147 | 148 | $display("================="); 149 | $display("32 BIT ALIGNED ACCESSES"); 150 | $display("================="); 151 | 152 | fix.write_axi('h10, 0, 2, 0, 'h000f); 153 | fix.write_axi('h14, 0, 2, 0, 'h00f0); 154 | fix.write_axi('h18, 0, 2, 0, 'h0f00); 155 | fix.write_axi('h1c, 0, 2, 0, 'hf000); 156 | 157 | fix.read_axi('h10, 0, 2); 158 | fix.read_axi('h14, 0, 2); 159 | fix.read_axi('h18, 0, 2); 160 | fix.read_axi('h1c, 0, 2); 161 | 162 | $display("================="); 163 | $display("16 BIT ALIGNED ACCESSES"); 164 | $display("================="); 165 | 166 | fix.write_axi('h20, 0, 1, 0, 'h0003); 167 | fix.write_axi('h22, 0, 1, 0, 'h000c); 168 | fix.write_axi('h24, 0, 1, 0, 'h0030); 169 | fix.write_axi('h26, 0, 1, 0, 'h00c0); 170 | fix.write_axi('h28, 0, 1, 0, 'h0300); 171 | fix.write_axi('h2a, 0, 1, 0, 'h0c00); 172 | fix.write_axi('h2c, 0, 1, 0, 'h3000); 173 | fix.write_axi('h2e, 0, 1, 0, 'hc000); 174 | 175 | fix.read_axi('h20, 0, 1); 176 | fix.read_axi('h22, 0, 1); 177 | fix.read_axi('h24, 0, 1); 178 | fix.read_axi('h26, 0, 1); 179 | fix.read_axi('h28, 0, 1); 180 | fix.read_axi('h2a, 0, 1); 181 | fix.read_axi('h2c, 0, 1); 182 | fix.read_axi('h2e, 0, 1); 183 | 184 | $display("================="); 185 | $display("8 BIT ALIGNED ACCESSES"); 186 | $display("================="); 187 | 188 | fix.write_axi('h30, 0, 0, 0, 'h0001); 189 | fix.write_axi('h31, 0, 0, 0, 'h0002); 190 | fix.write_axi('h32, 0, 0, 0, 'h0004); 191 | fix.write_axi('h33, 0, 0, 0, 'h0008); 192 | fix.write_axi('h34, 0, 0, 0, 'h0010); 193 | fix.write_axi('h35, 0, 0, 0, 'h0020); 194 | fix.write_axi('h36, 0, 0, 0, 'h0040); 195 | fix.write_axi('h37, 0, 0, 0, 'h0080); 196 | fix.write_axi('h38, 0, 0, 0, 'h0100); 197 | fix.write_axi('h39, 0, 0, 0, 'h0200); 198 | fix.write_axi('h3a, 0, 0, 0, 'h0400); 199 | fix.write_axi('h3b, 0, 0, 0, 'h0800); 200 | fix.write_axi('h3c, 0, 0, 0, 'h1000); 201 | fix.write_axi('h3d, 0, 0, 0, 'h2000); 202 | fix.write_axi('h3e, 0, 0, 0, 'h4000); 203 | fix.write_axi('h3f, 0, 0, 0, 'h8000); 204 | 205 | fix.read_axi('h30, 0, 0); 206 | fix.read_axi('h31, 0, 0); 207 | fix.read_axi('h32, 0, 0); 208 | fix.read_axi('h33, 0, 0); 209 | fix.read_axi('h34, 0, 0); 210 | fix.read_axi('h35, 0, 0); 211 | fix.read_axi('h36, 0, 0); 212 | fix.read_axi('h37, 0, 0); 213 | fix.read_axi('h38, 0, 0); 214 | fix.read_axi('h39, 0, 0); 215 | fix.read_axi('h3a, 0, 0); 216 | fix.read_axi('h3b, 0, 0); 217 | fix.read_axi('h3c, 0, 0); 218 | fix.read_axi('h3d, 0, 0); 219 | fix.read_axi('h3e, 0, 0); 220 | fix.read_axi('h3f, 0, 0); 221 | 222 | $display("================="); 223 | $display("COMBINED"); 224 | $display("================="); 225 | 226 | fix.write_axi('h800, 0, 4, 1, 'hffff); 227 | fix.read_axi('h800, 0, 4); 228 | fix.write_axi('h800, 0, 4, 0, 'hc03f); 229 | fix.read_axi('h800, 0, 4); 230 | fix.write_axi('h806, 0, 1, 0, 'h00c0); 231 | fix.write_axi('h80a, 0, 1, 0, 'h0c00); 232 | fix.write_axi('h80e, 0, 1, 0, 'hc000); 233 | fix.read_axi('h806, 0, 1); 234 | fix.read_axi('h80a, 0, 1); 235 | fix.read_axi('h80e, 0, 1); 236 | 237 | $display("================="); 238 | $display("UNALIGNED"); 239 | $display("================="); 240 | 241 | fix.write_axi('h900, 10, 4, 1, 'hffff); 242 | fix.read_axi('h900, 10, 4); 243 | 244 | // 32b inner 3-burst on 16b boundary 245 | fix.write_axi('h902, 2, 2, 0, 'hF0FF); 246 | fix.read_axi('h902, 2, 2); 247 | 248 | // 32b outer 10-burst on 16b boundary 249 | fix.write_axi('h90a, 9, 2, 0, 'hF0FF); 250 | fix.read_axi('h90a, 9, 2); 251 | 252 | // 64b inner single on 16b boundary 253 | fix.write_axi('h910, 10, 3, 1, 'hffff); 254 | fix.read_axi('h910, 10, 3); 255 | fix.write_axi('h91C, 0, 3, 0, 'hFF0F); 256 | fix.read_axi('h91C, 0, 3); 257 | 258 | // 64b inner 5-burst on 16b boundary 259 | fix.write_axi('h990, 5, 3, 1, 'hFFFF); 260 | fix.read_axi('h990, 5, 3); 261 | fix.write_axi('h992, 4, 3, 0, 'hFF0F); 262 | fix.read_axi('h992, 4, 3); 263 | 264 | // 64b inner single on 32b boundary 265 | fix.write_axi('h924, 0, 3, 0, 'hF0FF); 266 | fix.read_axi('h924, 0, 3); 267 | 268 | // 128 outer single on 16b boundary (read back in aligned fasion) 269 | fix.write_axi('h930, 0, 4, 0, 'hFFFF); 270 | fix.read_axi('h930, 0, 4); 271 | 272 | fix.write_axi('h954, 0, 4, 1, 'hFFFF); 273 | fix.read_axi('h954, 0, 4); 274 | 275 | // 128 outer single on 32b boundary (read back in aligned fasion) 276 | fix.write_axi('h954, 0, 4, 0, 'hFFFF); 277 | fix.read_axi('h954, 0, 4); 278 | 279 | 280 | // 128 outer single on 64b boundary (read back in aligned fasion) 281 | fix.write_axi('h978, 0, 4, 1, 'hFFFF); 282 | fix.read_axi('h978, 0, 4); //siamo qua 283 | // 128 outer single on 64b boundary (read back in aligned fasion) 284 | fix.write_axi('h978, 0, 4, 0, 'hFFFF); 285 | fix.read_axi('h978, 0, 4); 286 | 287 | // 128 5-burst single on 16b boundary (read back in aligned fasion) //here 288 | fix.write_axi('h1c02, 4, 4, 0, 'hFFFF); 289 | fix.read_axi('h1c02, 4, 4); 290 | 291 | // 128 bit access (burst, extrawide --> will be split) 292 | fix.write_axi('ha00, 4090, 4, 0, 'hffff); 293 | fix.read_axi('ha00, 4090, 4); 294 | 295 | // 64 bit access (burst, extrawide --> will be split) 296 | fix.write_axi('ha00, 4090, 3, 0, 'hffff); 297 | fix.read_axi('ha00, 4090, 3); 298 | 299 | // 32 bit access (burst, extrawide --> will be split) 300 | fix.write_axi('ha00, 4090, 2, 0, 'hffff); 301 | fix.read_axi('ha00, 4090, 2); 302 | 303 | $display("======================"); 304 | $display("AXI DONE WITH SUCCESS!"); 305 | $display("======================"); 306 | 307 | #5us; 308 | $stop(); 309 | end 310 | 311 | endmodule : hyperbus_tb 312 | --------------------------------------------------------------------------------