├── .editorconfig ├── .gitignore ├── Bender.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── include └── apb │ ├── assign.svh │ └── typedef.svh ├── scripts ├── compile_vsim.sh ├── run_vsim.sh └── synth.sh ├── src ├── apb_cdc.sv ├── apb_demux.sv ├── apb_err_slv.sv ├── apb_intf.sv ├── apb_pkg.sv ├── apb_regs.sv └── apb_test.sv └── test ├── synth_bench.sv ├── tb_apb_cdc.sv ├── tb_apb_demux.sv ├── tb_apb_regs.sv └── tb_apb_regs.wave.do /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig (http://editorconfig.org/) 2 | root = true 3 | 4 | # Default Settings 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 4 9 | indent_style = space 10 | insert_final_newline = true 11 | tab_width = 4 12 | trim_trailing_whitespace = true 13 | max_line_length = 100 14 | 15 | [Makefile] 16 | indent_style = tab 17 | 18 | [{*.sv,*.svh}] 19 | indent_size = 2 20 | 21 | [*.yml] 22 | indent_size = 2 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | !.git* 3 | !/.editorconfig 4 | /build 5 | /Bender.lock 6 | /Bender.local 7 | -------------------------------------------------------------------------------- /Bender.yml: -------------------------------------------------------------------------------- 1 | package: 2 | name: apb 3 | authors: 4 | - "Andreas Kurth " # current maintainer 5 | - "Fabian Schuiki " 6 | - "Wolfgang Roenninger " 7 | 8 | dependencies: 9 | common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.16.2 } 10 | 11 | export_include_dirs: 12 | - include 13 | 14 | sources: 15 | # Source files grouped in levels. Files in level 0 have no dependencies on files in this 16 | # package. Files in level 1 only depend on files in level 0, files in level 2 on files in 17 | # levels 1 and 0, etc. Files within a level are ordered alphabetically. 18 | # Level 0 19 | - src/apb_pkg.sv 20 | # Level 1 21 | - src/apb_intf.sv 22 | # Level 2 23 | - src/apb_err_slv.sv 24 | - src/apb_regs.sv 25 | - src/apb_cdc.sv 26 | - src/apb_demux.sv 27 | 28 | - target: simulation 29 | files: 30 | - src/apb_test.sv 31 | 32 | - target: test 33 | files: 34 | - test/tb_apb_regs.sv 35 | - test/tb_apb_cdc.sv 36 | - test/tb_apb_demux.sv 37 | 38 | - target: synth_test 39 | files: 40 | # Level 0 41 | - test/synth_bench.sv 42 | -------------------------------------------------------------------------------- /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 | 8 | ## Unreleased 9 | 10 | ### Added 11 | 12 | ### Changed 13 | 14 | ### Fixed 15 | 16 | ## 0.2.4 - 2023-05-17 17 | 18 | ### Added 19 | - Add `APB_TYPEDEF_ALL` macro. 20 | 21 | ## 0.2.3 - 2022-12-20 22 | 23 | ### Added 24 | - Add APB error slave (`apb_err_slv`). 25 | - Add APB demux (`apb_demux`) and testbench. 26 | 27 | 28 | ## 0.2.2 - 2022-12-02 29 | 30 | ### Added 31 | - Add APB clock domain crossing (`apb_cdc`). 32 | - Add additional VIP classes `apb_rand_slave` and `apb_rand_master`. 33 | 34 | ### Fixed 35 | - Improve tool compatibility by explicitly annotating the type of a `struct` assignment. 36 | - Set the initial value of the registers as soon as the reset is released, rather than as reset 37 | value, because driving the reset value from an input signal is potentially unsafe. 38 | 39 | 40 | ## 0.2.1 - 2021-06-02 41 | 42 | ### Fixed 43 | - Fix bug in `APB_TO_RESP` macro. 44 | 45 | 46 | ## 0.2.0 - 2020-03-13 47 | 48 | ### Added 49 | - Add clocked `APB_DV` interface for design verification. 50 | - Define macros for APB typedefs. 51 | - Define macros for assigning APB interfaces. 52 | - Add `apb_regs` read-write registers with APB interface with optional read only mapping. 53 | - Add basic test infrastructure for APB modules. 54 | - Add contribution guidelines. 55 | - Add RTL testbenches for modules. 56 | - Add synthesis and simulation scripts. 57 | - `synth_bench`: add synthesis bench. 58 | 59 | ### Changed 60 | - Rename `APB_BUS` interface to `APB`, change its parameters to constants, and remove `in` and `out` modports. 61 | 62 | 63 | ## 0.1.0 - 2018-09-12 64 | ### Changed 65 | - Open source release. 66 | 67 | ### Added 68 | - Initial commit. 69 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | ## Coding Style 4 | 5 | All SystemVerilog code in this repository _must_ adhere to the [SystemVerilog Coding Style Guide by 6 | lowRISC](https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md) and the 7 | following rules: 8 | 9 | - All module names _must_ start with `apb_`. 10 | 11 | - User-facing modules _must_ have SystemVerilog `struct`s as APB ports. The concrete `struct` type 12 | _must_ be defined as `parameter` to the module. The fields of the `struct` _must_ correspond to 13 | those defined by our [`typedef` 14 | macros](https://github.com/pulp-platform/apb/blob/master/include/apb/typedef.svh). 15 | 16 | - User-facing modules _may_ come with a variant that has SystemVerilog interfaces as APB ports. 17 | - Such an interface variant module _must not_ implement any functionality except wiring its 18 | interfaces to the `struct` ports of the original module. 19 | - The name of an interface variant _must_ be the name of the original module suffixed by `_intf`. 20 | - The parameters of an interface variant must be formatted `ALL_CAPS`. 21 | 22 | 23 | ## Collaboration Guidelines 24 | 25 | We follow [`pulp-platform`'s Collaboration 26 | Guidelines](https://github.com/pulp-platform/style-guidelines/blob/master/CONTRIBUTING.md). 27 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # APB 2 | 3 | This is the implementation of the AMBA APB4 protocol, version 2.0, developed as part of the PULP 4 | platform at ETH Zurich. 5 | 6 | Maintainer: Andreas Kurth 7 | 8 | ## Overview 9 | 10 | ### Package / Macros 11 | 12 | | Name | Description | 13 | |------------------------------------------|-------------------------------------------------------------------| 14 | | [`apb_pkg`](src/apb_pkg.sv) | Package with APB4 constants and type definitions | 15 | | [`apb/typedef`](include/apb/typedef.svh) | Macros which define the APB4 request/response structs | 16 | | [`apb/assign`](include/apb/typedef.svh) | Macros which assign/set/translates APB4 interfaces and structs | 17 | 18 | ### Interfaces 19 | 20 | | Name | Description | 21 | |------------------------------------------|-------------------------------------------------------------------| 22 | | [`APB`](src/apb_intf.sv) | APB4 interface with configurable address, data and sel widths | 23 | | [`APB_DV`](src/apb_intf.sv) | Clocked variant of `APB` for design verification | 24 | 25 | ### Leaf Modules 26 | 27 | | Name | Description | 28 | |----------------------------------+-----------------------------------------------------------| 29 | | [`apb_regs`](src/apb_regs.sv) | Read and write registers, with optional read only mapping | 30 | | [`apb_demux`](src/apb_demux.sv) | APB4 demultiplexer with using select signal | 31 | 32 | ### Verification and Simulation 33 | 34 | | Name | Description | 35 | |------------------------------------------|-------------------------------------------------------------------| 36 | | [`apb_driver`](src/apb_test.sv) | APB driver (can act as either slave or master) | 37 | -------------------------------------------------------------------------------- /include/apb/assign.svh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 ETH Zurich, University of Bologna 2 | // 3 | // Copyright and related rights are licensed under the Solderpad Hardware 4 | // License, Version 0.51 (the "License"); you may not use this file except in 5 | // compliance with the License. You may obtain a copy of the License at 6 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 7 | // or agreed to in writing, software, hardware and materials distributed under 8 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | // specific language governing permissions and limitations under the License. 11 | 12 | `ifndef APB_ASSIGN_SVH_ 13 | `define APB_ASSIGN_SVH_ 14 | 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// 16 | // Assign an APB4 interface to another, as if you would do in `assign slv = mst;`. 17 | // 18 | // Usage example: 19 | // `APB_ASSIGN(slv, mst) 20 | `define APB_ASSIGN(dst, src) \ 21 | assign dst.paddr = src.paddr; \ 22 | assign dst.pprot = src.pprot; \ 23 | assign dst.psel = src.psel; \ 24 | assign dst.penable = src.penable; \ 25 | assign dst.pwrite = src.pwrite; \ 26 | assign dst.pwdata = src.pwdata; \ 27 | assign dst.pstrb = src.pstrb; \ 28 | assign src.pready = dst.pready; \ 29 | assign src.prdata = dst.prdata; \ 30 | assign src.pslverr = dst.pslverr; 31 | //////////////////////////////////////////////////////////////////////////////////////////////////// 32 | 33 | //////////////////////////////////////////////////////////////////////////////////////////////////// 34 | // Internal implementation for assigning interfaces from structs, allows for standalone assignments 35 | // (with `opt_as = assign`) and assignments inside process (with `opt_as` void) with the same code. 36 | `define APB_FROM_REQ(opt_as, apb_if, req_struct) \ 37 | opt_as apb_if.paddr = req_struct.paddr; \ 38 | opt_as apb_if.pprot = req_struct.pprot; \ 39 | opt_as apb_if.psel = req_struct.psel; \ 40 | opt_as apb_if.penable = req_struct.penable; \ 41 | opt_as apb_if.pwrite = req_struct.pwrite; \ 42 | opt_as apb_if.pwdata = req_struct.pwdata; \ 43 | opt_as apb_if.pstrb = req_struct.pstrb; 44 | `define APB_FROM_RESP(opt_as, apb_if, resp_struct) \ 45 | opt_as apb_if.pready = resp_struct.pready; \ 46 | opt_as apb_if.prdata = resp_struct.prdata; \ 47 | opt_as apb_if.pslverr = resp_struct.pslverr; 48 | //////////////////////////////////////////////////////////////////////////////////////////////////// 49 | 50 | //////////////////////////////////////////////////////////////////////////////////////////////////// 51 | // Setting an interface from request/response structs inside a process. 52 | // 53 | // Usage Example: 54 | // always_comb begin 55 | // `APB_SET_FROM_REQ(my_if, my_req_struct) 56 | // `APB_SET_FROM_RESP(my_if, my_resp_struct) 57 | // end 58 | `define APB_SET_FROM_REQ(apb_if, req_struct) `APB_FROM_REQ(, apb_if, req_struct) 59 | `define APB_SET_FROM_RESP(apb_if, resp_struct) `APB_FROM_RESP(, apb_if, resp_struct) 60 | //////////////////////////////////////////////////////////////////////////////////////////////////// 61 | 62 | //////////////////////////////////////////////////////////////////////////////////////////////////// 63 | // Assigning an interface from request/response structs outside a process. 64 | // 65 | // Usage Example: 66 | // `APB_ASSIGN_FROM_REQ(my_if, my_req_struct) 67 | // `APB_ASSIGN_FROM_RESP(my_if, my_resp_struct) 68 | `define APB_ASSIGN_FROM_REQ(apb_if, req_struct) `APB_FROM_REQ(assign, apb_if, req_struct) 69 | `define APB_ASSIGN_FROM_RESP(apb_if, resp_struct) `APB_FROM_RESP(assign, apb_if, resp_struct) 70 | //////////////////////////////////////////////////////////////////////////////////////////////////// 71 | 72 | //////////////////////////////////////////////////////////////////////////////////////////////////// 73 | // Internal implementation for assigning to structs from interfaces, allows for standalone 74 | // assignments (with `opt_as = assign`) and assignments inside processes (with `opt_as` void) with 75 | // the same code. 76 | `define APB_TO_REQ(opt_as, req_struct, apb_if) \ 77 | opt_as req_struct = '{ \ 78 | paddr: apb_if.paddr, \ 79 | pprot: apb_if.pprot, \ 80 | psel: apb_if.psel, \ 81 | penable: apb_if.penable, \ 82 | pwrite: apb_if.pwrite, \ 83 | pwdata: apb_if.pwdata, \ 84 | pstrb: apb_if.pstrb \ 85 | }; 86 | `define APB_TO_RESP(opt_as, resp_struct, apb_if) \ 87 | opt_as resp_struct = '{ \ 88 | pready: apb_if.pready, \ 89 | prdata: apb_if.prdata, \ 90 | pslverr: apb_if.pslverr \ 91 | }; 92 | //////////////////////////////////////////////////////////////////////////////////////////////////// 93 | 94 | //////////////////////////////////////////////////////////////////////////////////////////////////// 95 | // Setting to an interface request/response structs inside a process. 96 | // 97 | // Usage Example: 98 | // always_comb begin 99 | // `APB_SET_TO_REQ(my_req_struct, my_if); 100 | // `APB_SET_TO_RESP(my_resp_struct, my_if); 101 | // end 102 | `define APB_SET_TO_REQ(req_struct, apb_if) `APB_TO_REQ(, req_struct, apb_if) 103 | `define APB_SET_TO_RESP(resp_struct, apb_if) `APB_TO_RESP(, resp_struct, apb_if) 104 | //////////////////////////////////////////////////////////////////////////////////////////////////// 105 | 106 | //////////////////////////////////////////////////////////////////////////////////////////////////// 107 | // Assigning to an interface request/response structs outside a process. 108 | // 109 | // Usage Example: 110 | // `APB_ASSIGN_TO_REQ(my_req_struct, my_if); 111 | `define APB_ASSIGN_TO_REQ(req_struct, apb_if) `APB_TO_REQ(assign, req_struct, apb_if) 112 | `define APB_ASSIGN_TO_RESP(resp_struct, apb_if) `APB_TO_RESP(assign, resp_struct, apb_if) 113 | //////////////////////////////////////////////////////////////////////////////////////////////////// 114 | 115 | `endif 116 | -------------------------------------------------------------------------------- /include/apb/typedef.svh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 ETH Zurich, University of Bologna 2 | // 3 | // Copyright and related rights are licensed under the Solderpad Hardware 4 | // License, Version 0.51 (the "License"); you may not use this file except in 5 | // compliance with the License. You may obtain a copy of the License at 6 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 7 | // or agreed to in writing, software, hardware and materials distributed under 8 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | // specific language governing permissions and limitations under the License. 11 | 12 | // Author: 13 | // Wolfgang Roenninger 14 | 15 | // Macros to define APB4 Request/Response Structs 16 | 17 | `ifndef APB_TYPEDEF_SVH_ 18 | `define APB_TYPEDEF_SVH_ 19 | 20 | //////////////////////////////////////////////////////////////////////////////////////////////////// 21 | // APB4 (v2.0) Request/Response Structs 22 | // 23 | // Usage Example: 24 | // `APB_TYPEDEF_REQ_T ( apb_req_t, addr_t, data_t, strb_t ) 25 | // `APB_TYPEDEF_RESP_T ( apb_resp_t, data_t ) 26 | `define APB_TYPEDEF_REQ_T(apb_req_t, addr_t, data_t, strb_t) \ 27 | typedef struct packed { \ 28 | addr_t paddr; \ 29 | apb_pkg::prot_t pprot; \ 30 | logic psel; \ 31 | logic penable; \ 32 | logic pwrite; \ 33 | data_t pwdata; \ 34 | strb_t pstrb; \ 35 | } apb_req_t; 36 | `define APB_TYPEDEF_RESP_T(apb_resp_t, data_t) \ 37 | typedef struct packed { \ 38 | logic pready; \ 39 | data_t prdata; \ 40 | logic pslverr; \ 41 | } apb_resp_t; 42 | //////////////////////////////////////////////////////////////////////////////////////////////////// 43 | 44 | 45 | //////////////////////////////////////////////////////////////////////////////////////////////////// 46 | // All APB request/response structs in one macro. 47 | // 48 | // Usage Example: 49 | // `APB_TYPEDEF_ALL(my_apb, addr_t, data_t, strb_t) 50 | // 51 | // This defines the `my_apb_req_t` and `my_apb_resp_t` request/response structs. 52 | `define APB_TYPEDEF_ALL(__name, __addr_t, __data_t, __strb_t) \ 53 | `APB_TYPEDEF_REQ_T(__name``_req_t, __addr_t, __data_t, __strb_t) \ 54 | `APB_TYPEDEF_RESP_T(__name``_resp_t, __data_t) 55 | //////////////////////////////////////////////////////////////////////////////////////////////////// 56 | 57 | 58 | `endif 59 | -------------------------------------------------------------------------------- /scripts/compile_vsim.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) 2014-2020 ETH Zurich, University of Bologna 3 | # 4 | # Copyright and related rights are licensed under the Solderpad Hardware 5 | # License, Version 0.51 (the "License"); you may not use this file except in 6 | # compliance with the License. You may obtain a copy of the License at 7 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 8 | # or agreed to in writing, software, hardware and materials distributed under 9 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 10 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | # specific language governing permissions and limitations under the License. 12 | # 13 | # Fabian Schuiki 14 | # Andreas Kurth 15 | # Wolfgang Roenninger 16 | 17 | set -e 18 | 19 | bender script vsim -t test --vlog-arg="-svinputport=compat" --vlog-arg="-override_timescale 1ns/1ps" > compile.tcl 20 | echo 'return 0' >> compile.tcl 21 | vsim -c -do 'exit -code [source compile.tcl]' 22 | -------------------------------------------------------------------------------- /scripts/run_vsim.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) 2014-2020 ETH Zurich, University of Bologna 3 | # 4 | # Copyright and related rights are licensed under the Solderpad Hardware 5 | # License, Version 0.51 (the "License"); you may not use this file except in 6 | # compliance with the License. You may obtain a copy of the License at 7 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 8 | # or agreed to in writing, software, hardware and materials distributed under 9 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 10 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | # specific language governing permissions and limitations under the License. 12 | # 13 | # Fabian Schuiki 14 | # Andreas Kurth 15 | # Wolfgang Roenninger 16 | 17 | set -e 18 | ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 19 | 20 | [ ! -z "$VSIM" ] || VSIM=vsim 21 | 22 | call_vsim() { 23 | echo "run -all" | $VSIM "$@" | tee vsim.log 2>&1 24 | grep "Errors: 0," vsim.log 25 | } 26 | 27 | call_vsim tb_apb_regs -64 -t 1ns -coverage -lib work -voptargs="+acc +cover=bcesfx" 28 | -------------------------------------------------------------------------------- /scripts/synth.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) 2014-2018 ETH Zurich, University of Bologna 3 | # 4 | # Copyright and related rights are licensed under the Solderpad Hardware 5 | # License, Version 0.51 (the "License"); you may not use this file except in 6 | # compliance with the License. You may obtain a copy of the License at 7 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 8 | # or agreed to in writing, software, hardware and materials distributed under 9 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 10 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | # specific language governing permissions and limitations under the License. 12 | # 13 | # Fabian Schuiki 14 | # Andreas Kurth 15 | # Wolfgang Roenninger 16 | 17 | set -e 18 | ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 19 | 20 | [ ! -z "$SYNOPSYS_DC" ] || SYNOPSYS_DC="synopsys dc_shell -64" 21 | 22 | echo 'remove_design -all' > ./synth.tcl 23 | bender script synopsys -t synth_test >> ./synth.tcl 24 | echo 'elaborate synth_bench' >> ./synth.tcl 25 | 26 | cat ./synth.tcl | $SYNOPSYS_DC | tee synth.log 2>&1 27 | grep -i "warning:" synth.log || true 28 | ! grep -i "error:" synth.log 29 | -------------------------------------------------------------------------------- /src/apb_cdc.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | // APB Clock Domain Crossing 12 | // Author: Manuel Eggimann 13 | // Description: A clock domain crossing module on an APB interface. The module uses gray-counting 14 | // CDC FIFOS in both directions to synchronize source side with destination side. 15 | // Parameters: 16 | // - `LogDepth`: The FIFO crossing the clock domain has `2^LogDepth` entries 17 | // - `req_t`: APB4 request struct. See macro definition in `include/typedef.svh` 18 | // - `resp_t`: APB4 response struct. See macro definition in `include/typedef.svh` 19 | // 20 | // Ports: 21 | // 22 | // - `src_pclk_i: Source side clock input signal (1-bit). 23 | // - `src_preset_ni: Source side asynchronous active low reset signal (1-bit). 24 | // - `src_req_i: Source side APB4 request struct, bundles all APB4 signals from the master (req_t). 25 | // - `src_resp_o: Source side APB4 response struct, bundles all APB4 signals to the master (resp_t). 26 | // - `dst_pclk_i: Destination side clock input signal (1-bit). 27 | // - `dst_preset_ni: Destination side asynchronous active low reset signal (1-bit). 28 | // - `dst_req_o: Destination side APB4 request struct, bundles all APB4 signals to the slave (req_t). 29 | // - `dst_resp_i: Destination side APB4 response struct, bundles all APB4 signals from the slave (resp_t). 30 | // 31 | // This file also features the module `apb_cdc_intf`. The difference is that instead of the 32 | // request and response structs it uses a `APB` interfaces. The parameters have the same 33 | // function, however are defined in `ALL_CAPS`. 34 | 35 | (* no_ungroup *) 36 | (* no_boundary_optimization *) 37 | module apb_cdc #( 38 | parameter LogDepth = 1, 39 | parameter type req_t = logic, 40 | parameter type resp_t = logic, 41 | parameter type addr_t = logic, 42 | parameter type data_t = logic, 43 | parameter type strb_t = logic 44 | ) ( 45 | // synchronous slave port - clocked by `src_pclk_i` 46 | input logic src_pclk_i, 47 | input logic src_preset_ni, 48 | input req_t src_req_i, 49 | output resp_t src_resp_o, 50 | // synchronous master port - clocked by `dst_pclk_i` 51 | input logic dst_pclk_i, 52 | input logic dst_preset_ni, 53 | output req_t dst_req_o, 54 | input resp_t dst_resp_i 55 | ); 56 | 57 | typedef struct packed { 58 | addr_t paddr; 59 | apb_pkg::prot_t pprot; 60 | logic pwrite; 61 | data_t pwdata; 62 | strb_t pstrb; 63 | } apb_async_req_data_t; 64 | 65 | typedef struct packed { 66 | data_t prdata; 67 | logic pslverr; 68 | } apb_async_resp_data_t; 69 | 70 | typedef enum logic {Src_Idle, Src_Busy} src_fsm_state_e; 71 | typedef enum logic[1:0] {Dst_Idle, Dst_Access, Dst_Busy} dst_fsm_state_e; 72 | src_fsm_state_e src_state_d, src_state_q; 73 | dst_fsm_state_e dst_state_d, dst_state_q; 74 | logic src_req_valid, src_req_ready, src_resp_valid, src_resp_ready; 75 | logic dst_req_valid, dst_req_ready, dst_resp_valid, dst_resp_ready; 76 | 77 | apb_async_req_data_t src_req_data, dst_req_data; 78 | 79 | apb_async_resp_data_t dst_resp_data_d, dst_resp_data_q, src_resp_data; 80 | 81 | assign src_req_data.paddr = src_req_i.paddr; 82 | assign src_req_data.pprot = src_req_i.pprot; 83 | assign src_req_data.pwrite = src_req_i.pwrite; 84 | assign src_req_data.pwdata = src_req_i.pwdata; 85 | assign src_req_data.pstrb = src_req_i.pstrb; 86 | 87 | assign dst_req_o.paddr = dst_req_data.paddr; 88 | assign dst_req_o.pprot = dst_req_data.pprot; 89 | assign dst_req_o.pwrite = dst_req_data.pwrite; 90 | assign dst_req_o.pwdata = dst_req_data.pwdata; 91 | assign dst_req_o.pstrb = dst_req_data.pstrb; 92 | 93 | assign src_resp_o.prdata = src_resp_data.prdata; 94 | assign src_resp_o.pslverr = src_resp_data.pslverr; 95 | 96 | ////////////////////////// 97 | // SRC DOMAIN HANDSHAKE // 98 | ////////////////////////// 99 | 100 | // In the source domain we translate simultaneous assertion of psel and penable into a transaction 101 | // on the CDC. The FSM then transitions into a busy state where it waits for a response comming 102 | // back on the other CDC FIFO. Once this response appears the pready signal is asserted to finish 103 | // the APB transaction. 104 | 105 | always_comb begin 106 | src_state_d = src_state_q; 107 | src_req_valid = 1'b0; 108 | src_resp_ready = 1'b0; 109 | src_resp_o.pready = 1'b0; 110 | case (src_state_q) 111 | Src_Idle: begin 112 | if (src_req_i.psel & src_req_i.penable) begin 113 | src_req_valid = 1'b1; 114 | if (src_req_ready) src_state_d = Src_Busy; 115 | end 116 | end 117 | Src_Busy: begin 118 | src_resp_ready = 1'b1; 119 | if (src_resp_valid) begin 120 | src_resp_o.pready = 1'b1; 121 | src_state_d = Src_Idle; 122 | end 123 | end 124 | default:; 125 | endcase 126 | end 127 | 128 | always_ff @(posedge src_pclk_i, negedge src_preset_ni) begin 129 | if (!src_preset_ni) 130 | src_state_q <= Src_Idle; 131 | else 132 | src_state_q <= src_state_d; 133 | end 134 | 135 | 136 | ////////////////////////// 137 | // DST DOMAIN HANDSHAKE // 138 | ////////////////////////// 139 | 140 | // In the destination domain we need to perform a proper APB handshake with setup and access 141 | // phase. Once the destination slave asserts the pready signal we store the response data and 142 | // transition into a busy state. In the busy state we send the response data back to the src 143 | // domain and wait for the transaction to complete. 144 | 145 | always_comb begin 146 | dst_state_d = dst_state_q; 147 | dst_req_ready = 1'b0; 148 | dst_resp_valid = 1'b0; 149 | dst_req_o.psel = 1'b0; 150 | dst_req_o.penable = 1'b0; 151 | dst_resp_data_d = dst_resp_data_q; 152 | case (dst_state_q) 153 | Dst_Idle: begin 154 | if (dst_req_valid) begin 155 | dst_req_o.psel = 1'b1; 156 | dst_state_d = Dst_Access; 157 | end 158 | end 159 | 160 | Dst_Access: begin 161 | dst_req_o.psel = 1'b1; 162 | dst_req_o.penable = 1'b1; 163 | if (dst_resp_i.pready) begin 164 | dst_req_ready = 1'b1; 165 | dst_resp_data_d.prdata = dst_resp_i.prdata; 166 | dst_resp_data_d.pslverr = dst_resp_i.pslverr; 167 | dst_state_d = Dst_Busy; 168 | end 169 | end 170 | 171 | Dst_Busy: begin 172 | dst_resp_valid = 1'b1; 173 | if (dst_resp_ready) begin 174 | dst_state_d = Dst_Idle; 175 | end 176 | end 177 | 178 | default: begin 179 | dst_state_d = Dst_Idle; 180 | end 181 | endcase 182 | end 183 | 184 | always_ff @(posedge dst_pclk_i, negedge dst_preset_ni) begin 185 | if (!dst_preset_ni) begin 186 | dst_state_q <= Dst_Idle; 187 | dst_resp_data_q <= '0; 188 | end else begin 189 | dst_state_q <= dst_state_d; 190 | dst_resp_data_q <= dst_resp_data_d; 191 | end 192 | end 193 | 194 | 195 | /////////////// 196 | // CDC FIFOS // 197 | /////////////// 198 | 199 | cdc_fifo_gray #( 200 | .T ( apb_async_req_data_t ), 201 | .LOG_DEPTH ( LogDepth ) 202 | ) i_cdc_fifo_gray_req ( 203 | .src_clk_i ( src_pclk_i ), 204 | .src_rst_ni ( src_preset_ni ), 205 | .src_data_i ( src_req_data ), 206 | .src_valid_i ( src_req_valid ), 207 | .src_ready_o ( src_req_ready ), 208 | 209 | .dst_clk_i ( dst_pclk_i ), 210 | .dst_rst_ni ( dst_preset_ni ), 211 | .dst_data_o ( dst_req_data ), 212 | .dst_valid_o ( dst_req_valid ), 213 | .dst_ready_i ( dst_req_ready ) 214 | ); 215 | 216 | cdc_fifo_gray #( 217 | .T ( apb_async_resp_data_t ), 218 | .LOG_DEPTH ( LogDepth ) 219 | ) i_cdc_fifo_gray_resp ( 220 | .src_clk_i ( dst_pclk_i ), 221 | .src_rst_ni ( dst_preset_ni ), 222 | .src_data_i ( dst_resp_data_q ), 223 | .src_valid_i ( dst_resp_valid ), 224 | .src_ready_o ( dst_resp_ready ), 225 | 226 | .dst_clk_i ( src_pclk_i ), 227 | .dst_rst_ni ( src_preset_ni ), 228 | .dst_data_o ( src_resp_data ), 229 | .dst_valid_o ( src_resp_valid ), 230 | .dst_ready_i ( src_resp_ready ) 231 | ); 232 | 233 | endmodule // apb_cdc 234 | 235 | 236 | `include "apb/typedef.svh" 237 | `include "apb/assign.svh" 238 | 239 | module apb_cdc_intf #( 240 | parameter int unsigned APB_ADDR_WIDTH = 0, 241 | parameter int unsigned APB_DATA_WIDTH = 0, 242 | /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. 243 | parameter int unsigned LOG_DEPTH = 1 244 | )( 245 | input logic src_pclk_i, 246 | input logic src_preset_ni, 247 | APB.Slave src, 248 | input logic dst_pclk_i, 249 | input logic dst_preset_ni, 250 | APB.Master dst 251 | ); 252 | 253 | typedef logic [APB_ADDR_WIDTH-1:0] addr_t; 254 | typedef logic [APB_DATA_WIDTH-1:0] data_t; 255 | typedef logic [APB_DATA_WIDTH/8-1:0] strb_t; 256 | 257 | `APB_TYPEDEF_REQ_T(apb_req_t, addr_t, data_t, strb_t) 258 | `APB_TYPEDEF_RESP_T(apb_resp_t, data_t) 259 | 260 | apb_req_t src_req, dst_req; 261 | apb_resp_t dst_resp, src_resp; 262 | 263 | `APB_ASSIGN_TO_REQ(src_req, src) 264 | `APB_ASSIGN_FROM_REQ(dst, dst_req) 265 | `APB_ASSIGN_FROM_RESP(src, src_resp) 266 | `APB_ASSIGN_TO_RESP(dst_resp, dst) 267 | 268 | apb_cdc #( 269 | .LogDepth ( LOG_DEPTH ), 270 | .req_t ( apb_req_t ), 271 | .resp_t ( apb_resp_t ), 272 | .addr_t ( addr_t ), 273 | .data_t ( data_t ), 274 | .strb_t ( strb_t ) 275 | ) i_apb_cdc ( 276 | .src_pclk_i, 277 | .src_preset_ni, 278 | .src_req_i ( src_req ), 279 | .src_resp_o ( src_resp ), 280 | .dst_pclk_i, 281 | .dst_preset_ni, 282 | .dst_req_o ( dst_req ), 283 | .dst_resp_i ( dst_resp ) 284 | ); 285 | endmodule 286 | -------------------------------------------------------------------------------- /src/apb_demux.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2018 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | // APB Demultiplexer Description: 12 | // This module demultiplexes one APB slave portto several master ports using 13 | // the select_i signal. 14 | 15 | module apb_demux #( 16 | parameter int unsigned NoMstPorts = 32'd2, // The number of demux ports. If 17 | // 1, degenerates gracefully to a 18 | // feedthrough. 19 | parameter type req_t = logic, // APB request strcut 20 | parameter type resp_t = logic, // APB response struct 21 | // DEPENDENT PARAMETERS DO NOT OVERWRITE! 22 | parameter int unsigned SelectWidth = (NoMstPorts > 32'd1)? $clog2(NoMstPorts) : 32'd1, 23 | parameter type select_t = logic [SelectWidth-1:0] 24 | )( 25 | input req_t slv_req_i, 26 | output resp_t slv_resp_o, 27 | output req_t [NoMstPorts-1:0] mst_req_o, 28 | input resp_t [NoMstPorts-1:0] mst_resp_i, 29 | input select_t select_i 30 | ); 31 | 32 | if (NoMstPorts == 32'd1) begin 33 | assign mst_req_o[0] = slv_req_i; 34 | assign slv_resp_o = mst_resp_i[0]; 35 | end else begin 36 | for (genvar idx = 0; idx < NoMstPorts; idx++) begin 37 | assign mst_req_o[idx].paddr = slv_req_i.paddr; 38 | assign mst_req_o[idx].pprot = slv_req_i.pprot; 39 | assign mst_req_o[idx].psel = (select_i == idx)? slv_req_i.psel: 1'b0; 40 | assign mst_req_o[idx].penable = (select_i == idx)? slv_req_i.penable: 1'b0; 41 | assign mst_req_o[idx].pwrite = slv_req_i.pwrite; 42 | assign mst_req_o[idx].pwdata = slv_req_i.pwdata; 43 | assign mst_req_o[idx].pstrb = slv_req_i.pstrb; 44 | end 45 | 46 | always_comb begin 47 | if (select_i < NoMstPorts) begin 48 | slv_resp_o.pready = mst_resp_i[select_i].pready; 49 | slv_resp_o.prdata = mst_resp_i[select_i].prdata; 50 | slv_resp_o.pslverr = mst_resp_i[select_i].pslverr; 51 | end else begin 52 | slv_resp_o.pready = 1'b1; 53 | slv_resp_o.prdata = '0; 54 | slv_resp_o.pslverr = 1'b1; 55 | end 56 | end 57 | end 58 | 59 | endmodule 60 | 61 | `include "apb/typedef.svh" 62 | `include "apb/assign.svh" 63 | 64 | module apb_demux_intf #( 65 | parameter int unsigned APB_ADDR_WIDTH = 0, 66 | parameter int unsigned APB_DATA_WIDTH = 0, 67 | parameter int unsigned NoMstPorts = 32'd2, 68 | // DEPENDENT PARAMETERS DO NOT OVERWRITE! 69 | parameter int unsigned SelectWidth = (NoMstPorts > 32'd1)? $clog2(NoMstPorts) : 32'd1, 70 | parameter type select_t = logic [SelectWidth-1:0] 71 | )( 72 | APB.Slave slv, 73 | APB.Master mst [NoMstPorts-1:0], 74 | input select_t select_i 75 | ); 76 | 77 | typedef logic [APB_ADDR_WIDTH-1:0] addr_t; 78 | typedef logic [APB_DATA_WIDTH-1:0] data_t; 79 | typedef logic [APB_DATA_WIDTH/8-1:0] strb_t; 80 | 81 | `APB_TYPEDEF_REQ_T(apb_req_t, addr_t, data_t, strb_t) 82 | `APB_TYPEDEF_RESP_T(apb_resp_t, data_t) 83 | 84 | apb_req_t slv_req; 85 | apb_resp_t slv_resp; 86 | 87 | apb_req_t [NoMstPorts-1:0] mst_req; 88 | apb_resp_t [NoMstPorts-1:0] mst_resp; 89 | 90 | for (genvar idx = 0; idx < NoMstPorts; idx++) begin 91 | `APB_ASSIGN_FROM_REQ(mst[idx], mst_req[idx]) 92 | `APB_ASSIGN_TO_RESP(mst_resp[idx], mst[idx]) 93 | end 94 | `APB_ASSIGN_TO_REQ(slv_req, slv) 95 | `APB_ASSIGN_FROM_RESP(slv, slv_resp) 96 | 97 | apb_demux #( 98 | .NoMstPorts ( NoMstPorts ), 99 | .req_t ( apb_req_t ), 100 | .resp_t ( apb_resp_t ) 101 | ) i_apb_cdc ( 102 | .slv_req_i ( slv_req ), 103 | .slv_resp_o ( slv_resp ), 104 | .mst_req_o ( mst_req ), 105 | .mst_resp_i ( mst_resp ), 106 | .select_i 107 | ); 108 | endmodule 109 | -------------------------------------------------------------------------------- /src/apb_err_slv.sv: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Title : APB Error Slave 3 | //----------------------------------------------------------------------------- 4 | // File : apb_err_slv.sv 5 | // Author : Manuel Eggimann 6 | // Created : 05.12.2022 7 | //----------------------------------------------------------------------------- 8 | // Description : 9 | // This module always responds with an error response on any request of its APB 10 | // slave port. Use thismodule e.g. as the default port in a APB demultiplexer 11 | // to handle illegal access. Following the reccomendation of the APB 2.0 12 | // specification, the error response will only be asserted on a valid 13 | // transaction. 14 | //----------------------------------------------------------------------------- 15 | // Copyright (C) 2013-2022 ETH Zurich, University of Bologna 16 | // Copyright and related rights are licensed under the Solderpad Hardware 17 | // License, Version 0.51 (the "License"); you may not use this file except in 18 | // compliance with the License. You may obtain a copy of the License at 19 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 20 | // or agreed to in writing, software, hardware and materials distributed under 21 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 22 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 23 | // specific language governing permissions and limitations under the License. 24 | //----------------------------------------------------------------------------- 25 | module apb_err_slv #( 26 | parameter type req_t = logic, // APB request struct 27 | parameter type resp_t = logic, // APB response struct 28 | parameter int unsigned RespWidth = 32'd32, // Data width of the response. Gets zero extended or truncated to r.data. 29 | parameter logic [RespWidth-1:0] RespData = 32'hBADCAB1E // Hexvalue for the data to return on error. 30 | ) ( 31 | input req_t slv_req_i, 32 | output resp_t slv_resp_o 33 | ); 34 | 35 | assign slv_resp_o.prdata = RespData; 36 | assign slv_resp_o.pready = 1'b1; 37 | // Following the APB recommendations, we only assert the error signal if there is actually a 38 | // request. 39 | assign slv_resp_o.pslverr = (slv_req_i.psel & slv_req_i.penable)? 40 | apb_pkg::RESP_SLVERR : apb_pkg::RESP_OKAY; 41 | 42 | endmodule 43 | 44 | 45 | `include "apb/typedef.svh" 46 | `include "apb/assign.svh" 47 | 48 | module apb_err_slv_intf #( 49 | parameter int unsigned APB_ADDR_WIDTH = 0, 50 | parameter int unsigned APB_DATA_WIDTH = 0, 51 | parameter logic [APB_DATA_WIDTH-1:0] RespData = 32'hBADCAB1E 52 | ) ( 53 | APB.Slave slv 54 | ); 55 | 56 | typedef logic [APB_ADDR_WIDTH-1:0] addr_t; 57 | typedef logic [APB_DATA_WIDTH-1:0] data_t; 58 | typedef logic [APB_DATA_WIDTH/8-1:0] strb_t; 59 | 60 | `APB_TYPEDEF_REQ_T(apb_req_t, addr_t, data_t, strb_t) 61 | `APB_TYPEDEF_RESP_T(apb_resp_t, data_t) 62 | 63 | apb_req_t slv_req; 64 | apb_resp_t slv_resp; 65 | 66 | `APB_ASSIGN_TO_REQ(slv_req, slv) 67 | `APB_ASSIGN_FROM_RESP(slv, slv_resp) 68 | 69 | apb_err_slv #( 70 | .req_t ( apb_req_t ), 71 | .resp_t ( apb_resp_t ), 72 | .RespWidth ( APB_DATA_WIDTH ), 73 | .RespData ( RespData ) 74 | ) i_apb_err_slv ( 75 | .slv_req_i ( slv_req ), 76 | .slv_resp_o ( slv_resp ) 77 | ); 78 | 79 | endmodule 80 | -------------------------------------------------------------------------------- /src/apb_intf.sv: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 ETH Zurich, University of Bologna 2 | // 3 | // Copyright and related rights are licensed under the Solderpad Hardware 4 | // License, Version 0.51 (the "License"); you may not use this file except in 5 | // compliance with the License. You may obtain a copy of the License at 6 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 7 | // or agreed to in writing, software, hardware and materials distributed under 8 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | // specific language governing permissions and limitations under the License. 11 | 12 | // An APB4 (v2.0) interface 13 | interface APB #( 14 | parameter int unsigned ADDR_WIDTH = 32'd32, 15 | parameter int unsigned DATA_WIDTH = 32'd32 16 | ); 17 | localparam int unsigned STRB_WIDTH = cf_math_pkg::ceil_div(DATA_WIDTH, 8); 18 | typedef logic [ADDR_WIDTH-1:0] addr_t; 19 | typedef logic [DATA_WIDTH-1:0] data_t; 20 | typedef logic [STRB_WIDTH-1:0] strb_t; 21 | 22 | addr_t paddr; 23 | apb_pkg::prot_t pprot; 24 | logic psel; 25 | logic penable; 26 | logic pwrite; 27 | data_t pwdata; 28 | strb_t pstrb; 29 | logic pready; 30 | data_t prdata; 31 | logic pslverr; 32 | 33 | modport Master ( 34 | output paddr, pprot, psel, penable, pwrite, pwdata, pstrb, 35 | input pready, prdata, pslverr 36 | ); 37 | 38 | modport Slave ( 39 | input paddr, pprot, psel, penable, pwrite, pwdata, pstrb, 40 | output pready, prdata, pslverr 41 | ); 42 | 43 | endinterface 44 | 45 | // A clocked APB4 (v2.0) interface for use in design verification 46 | interface APB_DV #( 47 | parameter int unsigned ADDR_WIDTH = 32'd32, 48 | parameter int unsigned DATA_WIDTH = 32'd32 49 | ) ( 50 | input logic clk_i 51 | ); 52 | localparam int unsigned STRB_WIDTH = cf_math_pkg::ceil_div(DATA_WIDTH, 8); 53 | typedef logic [ADDR_WIDTH-1:0] addr_t; 54 | typedef logic [DATA_WIDTH-1:0] data_t; 55 | typedef logic [STRB_WIDTH-1:0] strb_t; 56 | 57 | addr_t paddr; 58 | apb_pkg::prot_t pprot; 59 | logic psel; 60 | logic penable; 61 | logic pwrite; 62 | data_t pwdata; 63 | strb_t pstrb; 64 | logic pready; 65 | data_t prdata; 66 | logic pslverr; 67 | 68 | modport Master ( 69 | output paddr, pprot, psel, penable, pwrite, pwdata, pstrb, 70 | input pready, prdata, pslverr 71 | ); 72 | 73 | modport Slave ( 74 | input paddr, pprot, psel, penable, pwrite, pwdata, pstrb, 75 | output pready, prdata, pslverr 76 | ); 77 | 78 | endinterface 79 | -------------------------------------------------------------------------------- /src/apb_pkg.sv: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | // Author: Wolfgang Roenninger 12 | 13 | // Description: Package with constant APB v2.0 constants 14 | 15 | package apb_pkg; 16 | 17 | typedef logic [2:0] prot_t; 18 | 19 | localparam RESP_OKAY = 1'b0; 20 | localparam RESP_SLVERR = 1'b1; 21 | 22 | endpackage 23 | -------------------------------------------------------------------------------- /src/apb_regs.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2018 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | // APB Read-Write Registers 12 | // Description: This module exposes a number of registers on an APB interface. 13 | // It responds to not mapped accesses with a slave error. 14 | // Some of the registers can be configured to be read only. 15 | // Parameters: 16 | // - `NoApbRegs`: Number of registers. 17 | // - `ApbAddrWidth`: Address width of `req_i.paddr`, is used to generate internal address map. 18 | // - `AddrOffset`: The address offset in bytes between the registers. Is asserted to be a least 19 | // `32'd4`, the reason is to prevent overlap in the address map. 20 | // Each register is mapped to a 4 byte wide address range. When this parameter 21 | // is bigger than `32'd4` there will be holes in the address map and the module 22 | // answers with `apb_pkg::RESP_SLVERR`. It is recommended that this value is a 23 | // power of 2, to prevent data alignment issues on upstream buses. 24 | // - `ApbDataWidth`: The data width of the APB4 bus, this value can be up to `32'd32` (bit). 25 | // - `RegDataWidth`: The data width of the registers, this value has to be less or equal to 26 | // `ApbDataWidth`. If it is less the register gets zero extended for reads and 27 | // higher bits on writes get ignored. 28 | // - `ReadOnly`: This flag can specify a read only register at the given register index of the 29 | // array. When in the array the corresponding bit is set, the `reg_init_i` signal 30 | // at given index can be read out. Writes are ignored if the flag is set. 31 | // - `req_t`: APB4 request struct. See macro definition in `include/typedef.svh` 32 | // - `resp_t`: APB4 response struct. See macro definition in `include/typedef.svh` 33 | // 34 | // Ports: 35 | // 36 | // - `pclk_i: Clock input signal (1-bit). 37 | // - `preset_ni: Asynchronous active low reset signal (1-bit). 38 | // - `req_i: APB4 request struct, bundles all APB4 signals from the master (req_t). 39 | // - `resp_o: APB4 response struct, bundles all APB4 signals to the master (resp_t). 40 | // - `base_addr_i: Base address of this module, from here the registers `0` are mapped, starting 41 | // with index `0`. All subsequent register with higher indices have their bases 42 | // mapped with reg_index * `AddrOffset` from this value (ApbAddrWidth-bit). 43 | // - `reg_init_i: Initialization value for each register, when the register index is configured 44 | // as `ReadOnly[reg_index] == 1'b1` this value is passed through directly to 45 | // the APB4 bus (Array size `NoApbRegs` * RegDataWidth-bit). 46 | // - `reg_q_o: The current value of the register. If the register at the array index is 47 | // read only, the `reg_init_i` value is passed through to the respective index 48 | // (Array size `NoApbRegs` * RegDataWidth-bit). 49 | // 50 | // This file also features the module `apb_regs_intf`. The difference is that instead of the 51 | // request and response structs it uses an `APB.Salve` interface. The parameters have the same 52 | // Function, however are defined in `ALL_CAPS`. 53 | 54 | 55 | `include "common_cells/registers.svh" 56 | 57 | module apb_regs #( 58 | parameter int unsigned NoApbRegs = 32'd0, 59 | parameter int unsigned ApbAddrWidth = 32'd0, 60 | parameter int unsigned AddrOffset = 32'd4, 61 | parameter int unsigned ApbDataWidth = 32'd0, 62 | parameter int unsigned RegDataWidth = 32'd0, 63 | parameter bit [NoApbRegs-1:0] ReadOnly = 32'h0, 64 | parameter type req_t = logic, 65 | parameter type resp_t = logic, 66 | // DEPENDENT PARAMETERS DO NOT OVERWRITE! 67 | parameter type apb_addr_t = logic[ApbAddrWidth-1:0], 68 | parameter type reg_data_t = logic[RegDataWidth-1:0] 69 | ) ( 70 | // APB Interface 71 | input logic pclk_i, 72 | input logic preset_ni, 73 | input req_t req_i, 74 | output resp_t resp_o, 75 | // Register Interface 76 | input apb_addr_t base_addr_i, // base address of the read/write registers 77 | input reg_data_t [NoApbRegs-1:0] reg_init_i, 78 | output reg_data_t [NoApbRegs-1:0] reg_q_o 79 | ); 80 | localparam int unsigned IdxWidth = (NoApbRegs > 32'd1) ? $clog2(NoApbRegs) : 32'd1; 81 | typedef logic [IdxWidth-1:0] idx_t; 82 | typedef logic [ApbAddrWidth-1:0] apb_data_t; 83 | typedef struct packed { 84 | int unsigned idx; 85 | apb_addr_t start_addr; 86 | apb_addr_t end_addr; 87 | } rule_t; 88 | 89 | logic has_reset_d, has_reset_q; 90 | `FFARN(has_reset_q, has_reset_q, 1'b0, pclk_i, preset_ni) 91 | assign has_reset_d = 1'b1; 92 | 93 | // signal declarations 94 | rule_t [NoApbRegs-1:0] addr_map; 95 | idx_t reg_idx; 96 | logic decode_valid; 97 | // register signals 98 | reg_data_t [NoApbRegs-1:0] reg_d, reg_q; 99 | logic [NoApbRegs-1:0] reg_update; 100 | 101 | // generate address map for the registers 102 | for (genvar i = 0; i < NoApbRegs; i++) begin: gen_reg_addr_map 103 | assign addr_map[i] = rule_t'{ 104 | idx: unsigned'(i), 105 | start_addr: base_addr_i + apb_addr_t'( i * AddrOffset), 106 | end_addr: base_addr_i + apb_addr_t'((i+32'd1) * AddrOffset) 107 | }; 108 | end 109 | 110 | always_comb begin 111 | // default assignments 112 | reg_d = has_reset_q ? reg_q : reg_init_i; 113 | reg_update = '0; 114 | resp_o = '{ 115 | pready: req_i.psel & req_i.penable, 116 | prdata: apb_data_t'(32'h0BAD_B10C), 117 | pslverr: apb_pkg::RESP_OKAY 118 | }; 119 | if (req_i.psel) begin 120 | if (!decode_valid) begin 121 | // Error response on decode errors 122 | resp_o.pslverr = apb_pkg::RESP_SLVERR; 123 | end else begin 124 | if (req_i.pwrite) begin 125 | if (!ReadOnly[reg_idx]) begin 126 | for (int unsigned i = 0; i < RegDataWidth; i++) begin 127 | if (req_i.pstrb[i/8]) begin 128 | reg_d[reg_idx][i] = req_i.pwdata[i]; 129 | end 130 | end 131 | reg_update[reg_idx] = |req_i.pstrb; 132 | end else begin 133 | // this register is read only 134 | resp_o.pslverr = apb_pkg::RESP_SLVERR; 135 | end 136 | end else begin 137 | if (!ReadOnly[reg_idx]) begin 138 | resp_o.prdata = apb_data_t'(reg_q[reg_idx]); 139 | end else begin 140 | // for read only register the directly connect the init signal 141 | resp_o.prdata = apb_data_t'(reg_init_i[reg_idx]); 142 | end 143 | end 144 | end 145 | end 146 | end 147 | 148 | // output assignment and registers 149 | for (genvar i = 0; i < NoApbRegs; i++) begin : gen_rw_regs 150 | assign reg_q_o[i] = ReadOnly[i] ? reg_init_i[i] : reg_q[i]; 151 | `FFLARN(reg_q[i], reg_d[i], reg_update[i], '0, pclk_i, preset_ni) 152 | end 153 | 154 | addr_decode #( 155 | .NoIndices ( NoApbRegs ), 156 | .NoRules ( NoApbRegs ), 157 | .addr_t ( apb_addr_t ), 158 | .rule_t ( rule_t ) 159 | ) i_addr_decode ( 160 | .addr_i ( req_i.paddr ), 161 | .addr_map_i ( addr_map ), 162 | .idx_o ( reg_idx ), 163 | .dec_valid_o ( decode_valid ), 164 | .dec_error_o ( /*not used*/ ), 165 | .en_default_idx_i ( '0 ), 166 | .default_idx_i ( '0 ) 167 | ); 168 | 169 | // Validate parameters. 170 | // pragma translate_off 171 | `ifndef VERILATOR 172 | initial begin: p_assertions 173 | assert (NoApbRegs > 32'd0) 174 | else $fatal(1, "The number of registers must be at least 1!"); 175 | assert (ApbAddrWidth > 32'd2) 176 | else $fatal(1, "ApbAddrWidth is not wide enough, has to be at least 3 bit wide!"); 177 | assert (AddrOffset > 32'd3) 178 | else $fatal(1, "AddrOffset has to be at least 4 and is recommended to be a power of 2!"); 179 | assert ($bits(req_i.paddr) == ApbAddrWidth) 180 | else $fatal(1, "AddrWidth does not match req_i.paddr!"); 181 | assert (ApbDataWidth == $bits(resp_o.prdata)) 182 | else $fatal(1, "ApbDataWidth has to be: ApbDataWidth == $bits(req_i.prdata)!"); 183 | assert (ApbDataWidth > 32'd0 && ApbDataWidth <= 32'd32) 184 | else $fatal(1, "ApbDataWidth has to be: 32'd32 >= RegDataWidth > 0!"); 185 | assert ($bits(resp_o.prdata) == $bits(req_i.pwdata)) 186 | else $fatal(1, "req_i.pwdata has to match resp_o.prdata in width!"); 187 | assert (RegDataWidth > 32'd0 && RegDataWidth <= 32'd32) 188 | else $fatal(1, "RegDataWidth has to be: 32'd32 >= RegDataWidth > 0!"); 189 | assert (RegDataWidth <= $bits(resp_o.prdata)) 190 | else $fatal(1, "RegDataWidth has to be: RegDataWidth <= $bits(req_i.prdata)!"); 191 | assert (NoApbRegs == $bits(ReadOnly)) 192 | else $fatal(1, "Each register need a `ReadOnly` flag!"); 193 | end 194 | `endif 195 | // pragma translate_on 196 | endmodule 197 | 198 | `include "apb/assign.svh" 199 | `include "apb/typedef.svh" 200 | 201 | module apb_regs_intf #( 202 | parameter int unsigned NO_APB_REGS = 32'd0, // number of read only registers 203 | parameter int unsigned APB_ADDR_WIDTH = 32'd0, // address width of `paddr` 204 | parameter int unsigned ADDR_OFFSET = 32'd4, // address offset in bytes 205 | parameter int unsigned APB_DATA_WIDTH = 32'd0, // data width of the registers 206 | parameter int unsigned REG_DATA_WIDTH = 32'd0, 207 | parameter bit [NO_APB_REGS-1:0] READ_ONLY = 32'h0, 208 | // DEPENDENT PARAMETERS DO NOT OVERWRITE! 209 | parameter type apb_addr_t = logic[APB_ADDR_WIDTH-1:0], 210 | parameter type reg_data_t = logic[REG_DATA_WIDTH-1:0] 211 | ) ( 212 | // APB Interface 213 | input logic pclk_i, 214 | input logic preset_ni, 215 | APB.Slave slv, 216 | // Register Interface 217 | input apb_addr_t base_addr_i, // base address of the read only registers 218 | input reg_data_t [NO_APB_REGS-1:0] reg_init_i, // initalisation value for the registers 219 | output reg_data_t [NO_APB_REGS-1:0] reg_q_o 220 | ); 221 | localparam int unsigned APB_STRB_WIDTH = cf_math_pkg::ceil_div(APB_DATA_WIDTH, 8); 222 | typedef logic [APB_DATA_WIDTH-1:0] apb_data_t; 223 | typedef logic [APB_STRB_WIDTH-1:0] apb_strb_t; 224 | 225 | `APB_TYPEDEF_REQ_T(apb_req_t, apb_addr_t, apb_data_t, apb_strb_t) 226 | `APB_TYPEDEF_RESP_T(apb_resp_t, apb_data_t) 227 | 228 | apb_req_t apb_req; 229 | apb_resp_t apb_resp; 230 | 231 | `APB_ASSIGN_TO_REQ(apb_req, slv) 232 | `APB_ASSIGN_FROM_RESP(slv, apb_resp ) 233 | 234 | apb_regs #( 235 | .NoApbRegs ( NO_APB_REGS ), 236 | .ApbAddrWidth ( APB_ADDR_WIDTH ), 237 | .AddrOffset ( ADDR_OFFSET ), 238 | .ApbDataWidth ( APB_DATA_WIDTH ), 239 | .RegDataWidth ( REG_DATA_WIDTH ), 240 | .ReadOnly ( READ_ONLY ), 241 | .req_t ( apb_req_t ), 242 | .resp_t ( apb_resp_t ) 243 | ) i_apb_regs ( 244 | .pclk_i, 245 | .preset_ni, 246 | .req_i ( apb_req ), 247 | .resp_o ( apb_resp ), 248 | .base_addr_i, 249 | .reg_init_i, 250 | .reg_q_o 251 | ); 252 | 253 | // Validate parameters. 254 | // pragma translate_off 255 | `ifndef VERILATOR 256 | initial begin: p_assertions 257 | assert (APB_ADDR_WIDTH == $bits(slv.paddr)) 258 | else $fatal(1, "APB_ADDR_WIDTH does not match slv interface!"); 259 | assert (APB_DATA_WIDTH == $bits(slv.pwdata)) 260 | else $fatal(1, "APB_DATA_WIDTH does not match slv interface!"); 261 | end 262 | `endif 263 | // pragma translate_on 264 | endmodule 265 | -------------------------------------------------------------------------------- /src/apb_test.sv: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 ETH Zurich, University of Bologna 2 | // 3 | // Copyright and related rights are licensed under the Solderpad Hardware 4 | // License, Version 0.51 (the "License"); you may not use this file except in 5 | // compliance with the License. You may obtain a copy of the License at 6 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 7 | // or agreed to in writing, software, hardware and materials distributed under 8 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | // specific language governing permissions and limitations under the License. 11 | 12 | // Test infrastructure for APB interfaces 13 | package apb_test; 14 | 15 | class apb_request #( 16 | parameter ADDR_WIDTH = 32'd32, 17 | parameter DATA_WIDTH = 32'd32 18 | ); 19 | localparam STRB_WIDTH = cf_math_pkg::ceil_div(DATA_WIDTH, 8); 20 | 21 | rand logic [ADDR_WIDTH-1:0] paddr = '0; 22 | rand logic [DATA_WIDTH-1:0] pwdata = '0; 23 | rand logic [STRB_WIDTH-1:0] pstrb = '0; 24 | rand logic pwrite = 1'b1; 25 | endclass; 26 | 27 | class apb_response #( 28 | parameter DATA_WIDTH = 32'd32 29 | ); 30 | rand logic [DATA_WIDTH-1:0] prdata = '0; 31 | rand logic pslverr = 1'b0; 32 | endclass; 33 | 34 | // Simple APB driver with thread-safe read and write functions 35 | class apb_driver #( 36 | parameter int unsigned ADDR_WIDTH = 32'd32, // APB4 address width 37 | parameter int unsigned DATA_WIDTH = 32'd32, // APB4 data width 38 | parameter time TA = 0ns, // application time 39 | parameter time TT = 0ns // test time 40 | ); 41 | localparam int unsigned STRB_WIDTH = cf_math_pkg::ceil_div(DATA_WIDTH, 8); 42 | typedef logic [ADDR_WIDTH-1:0] addr_t; 43 | typedef logic [DATA_WIDTH-1:0] data_t; 44 | typedef logic [STRB_WIDTH-1:0] strb_t; 45 | typedef apb_request #(.ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(DATA_WIDTH)) apb_request_t; 46 | typedef apb_response #(.DATA_WIDTH(DATA_WIDTH)) apb_response_t; 47 | virtual APB_DV #( 48 | .ADDR_WIDTH(ADDR_WIDTH), 49 | .DATA_WIDTH(DATA_WIDTH) 50 | ) apb; 51 | std::semaphore lock; 52 | 53 | function new(virtual APB_DV #(.ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(DATA_WIDTH)) apb); 54 | this.apb = apb; 55 | this.lock = new(1); 56 | endfunction 57 | 58 | function void reset_master(); 59 | apb.paddr <= '0; 60 | apb.pprot <= '0; 61 | apb.psel <= 1'b0; 62 | apb.penable <= 1'b0; 63 | apb.pwrite <= 1'b0; 64 | apb.pwdata <= '0; 65 | apb.pstrb <= '0; 66 | endfunction 67 | 68 | function void reset_slave(); 69 | apb.pready <= 1'b0; 70 | apb.prdata <= '0; 71 | apb.pslverr <= 1'b0; 72 | endfunction 73 | 74 | task cycle_start; 75 | #TT; 76 | endtask 77 | 78 | task cycle_end; 79 | @(posedge apb.clk_i); 80 | endtask 81 | 82 | // this task reads from an APB4 slave, acts as master 83 | task read( 84 | input addr_t addr, 85 | output data_t data, 86 | output logic err 87 | ); 88 | while (!lock.try_get()) begin 89 | cycle_end(); 90 | end 91 | apb.paddr <= #TA addr; 92 | apb.pwrite <= #TA 1'b0; 93 | apb.psel <= #TA 1'b1; 94 | cycle_end(); 95 | apb.penable <= #TA 1'b1; 96 | cycle_start(); 97 | while (!apb.pready) begin 98 | cycle_end(); 99 | cycle_start(); 100 | end 101 | data = apb.prdata; 102 | err = apb.pslverr; 103 | cycle_end(); 104 | apb.paddr <= #TA '0; 105 | apb.psel <= #TA 1'b0; 106 | apb.penable <= #TA 1'b0; 107 | lock.put(); 108 | endtask 109 | 110 | // this task writes to an APB4 slave, acts as master 111 | task write( 112 | input addr_t addr, 113 | input data_t data, 114 | input strb_t strb, 115 | output logic err 116 | ); 117 | while (!lock.try_get()) begin 118 | cycle_end(); 119 | end 120 | apb.paddr <= #TA addr; 121 | apb.pwdata <= #TA data; 122 | apb.pstrb <= #TA strb; 123 | apb.pwrite <= #TA 1'b1; 124 | apb.psel <= #TA 1'b1; 125 | cycle_end(); 126 | apb.penable <= #TA 1'b1; 127 | cycle_start(); 128 | while (!apb.pready) begin 129 | cycle_end(); 130 | cycle_start(); 131 | end 132 | err = apb.pslverr; 133 | cycle_end(); 134 | apb.paddr <= #TA '0; 135 | apb.pwdata <= #TA '0; 136 | apb.pstrb <= #TA '0; 137 | apb.pwrite <= #TA 1'b0; 138 | apb.psel <= #TA 1'b0; 139 | apb.penable <= #TA 1'b0; 140 | lock.put(); 141 | endtask 142 | 143 | // Wait for incoming apb request 144 | task recv_request ( 145 | output apb_request_t request 146 | ); 147 | cycle_start(); 148 | while (!apb.psel) begin cycle_end(); cycle_start(); end 149 | while (!apb.penable) begin cycle_end(); cycle_start(); end 150 | request = new; 151 | request.paddr = apb.paddr; 152 | request.pwdata = apb.pwdata; 153 | request.pwrite = apb.pwrite; 154 | request.pstrb = apb.pstrb; 155 | cycle_end(); 156 | endtask 157 | 158 | // Acknowledge incoming APB write request (assert pready). 159 | task send_write_ack(); 160 | apb.pready <= #TA 1; 161 | cycle_end(); 162 | apb.pready <= #TA 0; 163 | endtask 164 | 165 | // Send response for incoming APB read request acknowledging the request (assert pready). 166 | task send_read_response ( 167 | input apb_response_t response 168 | ); 169 | apb.pready <= #TA 1; 170 | apb.prdata <= #TA response.prdata; 171 | apb.pslverr <= #TA response.pslverr; 172 | cycle_end(); 173 | apb.pready <= #TA 0; 174 | apb.prdata <= #TA '0; 175 | apb.pslverr <= #TA 0; 176 | endtask 177 | 178 | endclass 179 | 180 | class apb_rand_slave #( 181 | // APB interface parmeters 182 | parameter int unsigned ADDR_WIDTH = 32'd32, 183 | parameter int unsigned DATA_WIDTH = 32'd32, 184 | // Stimuli application and test time 185 | parameter time TA = 0ps, 186 | parameter time TT = 0ps, 187 | // Upper and lower bounds on wait states on Response (pready) 188 | parameter int RESP_MIN_WAIT_CYCLES = 0, 189 | parameter int RESP_MAX_WAIT_CYCLES = 20 190 | ); 191 | 192 | typedef apb_test::apb_driver #( 193 | .ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(DATA_WIDTH), .TA(TA), .TT(TT) 194 | ) apb_driver_t; 195 | 196 | typedef apb_driver_t::addr_t addr_t; 197 | typedef apb_driver_t::data_t data_t; 198 | typedef apb_driver_t::strb_t strb_t; 199 | 200 | typedef apb_driver_t::apb_request_t apb_request_t; 201 | typedef apb_driver_t::apb_response_t apb_response_t; 202 | 203 | apb_driver_t drv; 204 | std::mailbox #(apb_request_t) request_queue; 205 | std::mailbox #(apb_response_t) response_queue; 206 | 207 | function new( 208 | virtual APB_DV #( 209 | .ADDR_WIDTH(ADDR_WIDTH), 210 | .DATA_WIDTH(DATA_WIDTH) 211 | ) apb 212 | ); 213 | this.drv = new(apb); 214 | this.request_queue = new; 215 | this.response_queue = new; 216 | this.reset(); 217 | endfunction 218 | 219 | function reset(); 220 | drv.reset_slave(); 221 | endfunction 222 | 223 | // TODO: The `rand_wait` task exists in `rand_verif_pkg`, but that task cannot be called with 224 | // `this.drv.apb.clk_i` as `clk` argument. What is the syntax getting an assignable reference? 225 | task automatic rand_wait(input int unsigned min, max); 226 | int unsigned rand_success, cycles; 227 | rand_success = std::randomize(cycles) with { 228 | cycles >= min; 229 | cycles <= max; 230 | }; 231 | assert (rand_success) else $error("Failed to randomize wait cycles!"); 232 | repeat (cycles) @(posedge this.drv.apb.clk_i); 233 | endtask : rand_wait 234 | 235 | task handle_requests(); 236 | forever begin 237 | automatic apb_request_t request; 238 | automatic apb_response_t read_response = new; 239 | automatic logic rand_success; 240 | drv.recv_request(request); 241 | request_queue.put(request); 242 | if (request.pwrite) begin 243 | rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); 244 | drv.send_write_ack(); 245 | end else begin 246 | rand_success = read_response.randomize(); assert(rand_success); 247 | response_queue.put(read_response); 248 | rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); 249 | drv.send_read_response(read_response); 250 | end 251 | end 252 | endtask : handle_requests // recv_requests 253 | 254 | task run(); 255 | handle_requests(); 256 | endtask 257 | 258 | endclass 259 | 260 | class apb_rand_master #( 261 | // APB interface parmeters 262 | parameter int unsigned ADDR_WIDTH = 32'd32, 263 | parameter int unsigned DATA_WIDTH = 32'd32, 264 | // Stimuli application and test time 265 | parameter time TA = 0ps, 266 | parameter time TT = 0ps, 267 | // Upper and lower bounds on wait cycles on request channel 268 | parameter int REQ_MIN_WAIT_CYCLES = 0, 269 | parameter int REQ_MAX_WAIT_CYCLES = 20 270 | ); 271 | 272 | typedef apb_test::apb_driver #( 273 | .ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(DATA_WIDTH), .TA(TA), .TT(TT) 274 | ) apb_driver_t; 275 | 276 | typedef apb_driver_t::addr_t addr_t; 277 | typedef apb_driver_t::data_t data_t; 278 | typedef apb_driver_t::strb_t strb_t; 279 | 280 | typedef apb_driver_t::apb_request_t apb_request_t; 281 | typedef apb_driver_t::apb_response_t apb_response_t; 282 | 283 | apb_driver_t drv; 284 | std::mailbox #(apb_request_t) request_queue; 285 | std::mailbox #(apb_response_t) response_queue; 286 | 287 | 288 | function new( 289 | virtual APB_DV #( 290 | .ADDR_WIDTH(ADDR_WIDTH), 291 | .DATA_WIDTH(DATA_WIDTH) 292 | ) apb 293 | ); 294 | this.drv = new(apb); 295 | this.request_queue = new; 296 | this.response_queue = new; 297 | this.reset(); 298 | endfunction 299 | 300 | function reset(); 301 | drv.reset_master(); 302 | endfunction 303 | 304 | // TODO: The `rand_wait` task exists in `rand_verif_pkg`, but that task cannot be called with 305 | // `this.drv.apb.clk_i` as `clk` argument. What is the syntax getting an assignable reference? 306 | task automatic rand_wait(input int unsigned min, max); 307 | int unsigned rand_success, cycles; 308 | rand_success = std::randomize(cycles) with { 309 | cycles >= min; 310 | cycles <= max; 311 | }; 312 | assert (rand_success) else $error("Failed to randomize wait cycles!"); 313 | repeat (cycles) @(posedge this.drv.apb.clk_i); 314 | endtask 315 | 316 | task automatic send_requests(input int unsigned n_requests); 317 | automatic apb_request_t request = new; 318 | automatic apb_response_t response = new; 319 | automatic logic write_err; 320 | automatic logic rand_success; 321 | repeat (n_requests) begin 322 | rand_wait(REQ_MIN_WAIT_CYCLES, REQ_MAX_WAIT_CYCLES); 323 | rand_success = request.randomize(); assert(rand_success); 324 | request_queue.put(request); 325 | if (request.pwrite) begin 326 | $info("Sending write request with addr: %h, pstrb: %b and pwdata: %h", request.paddr, request.pstrb, request.pwdata); 327 | drv.write(request.paddr, request.pwdata, request.pstrb, write_err); 328 | end else begin 329 | $info("Sending read request with addr: %h...", request.paddr); 330 | drv.read(request.paddr, response.prdata, response.pslverr); 331 | response_queue.put(response); 332 | end 333 | end 334 | endtask : send_requests 335 | 336 | task automatic run(input int unsigned n_request); 337 | $info("Run apb_rand_master for %0d transactions", n_request); 338 | send_requests(n_request); 339 | endtask 340 | 341 | endclass 342 | endpackage 343 | -------------------------------------------------------------------------------- /test/synth_bench.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2020 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | // 11 | // Wolfgang Roenninger 12 | 13 | /// A synthesis test bench which instantiates various APB modules. 14 | module synth_bench ( 15 | input logic clk_i, 16 | input logic rst_ni 17 | ); 18 | 19 | localparam int unsigned APB_ADDR_WIDTH[5] = {3, 7, 16, 27, 32}; 20 | localparam int unsigned APB_DATA_WIDTH[5] = {1, 2, 16, 27, 32}; 21 | localparam int unsigned REG_DATA_WIDTH[5] = {1, 4, 13, 27, 32}; 22 | 23 | // APB REGS 24 | for (genvar i = 0; i < 5; i++) begin : gen_ro_addr 25 | for (genvar j = 0; j < 5; j++) begin : gen_ro_data 26 | for (genvar k = 0; k < 5; k++) begin : gen_ro_width 27 | for (genvar l = 0; l < 4; l++) begin : gen_no_ro_regs 28 | localparam int unsigned NoApbRegs = 2**l; 29 | localparam int unsigned RegDataWidth = (APB_DATA_WIDTH[j] >= REG_DATA_WIDTH[k]) ? 30 | REG_DATA_WIDTH[k] : APB_DATA_WIDTH[j]; 31 | synth_apb_regs #( 32 | .NoApbRegs ( NoApbRegs ), 33 | .ApbAddrWidth( APB_ADDR_WIDTH[i] ), 34 | .ApbDataWidth( APB_DATA_WIDTH[j] ), 35 | .RegDataWidth( RegDataWidth ) 36 | ) i_synth_apb_regs (.*); 37 | end 38 | end 39 | end 40 | end 41 | 42 | endmodule 43 | 44 | 45 | module synth_apb_regs #( 46 | parameter int unsigned NoApbRegs = 32'd0, 47 | parameter int unsigned ApbAddrWidth = 32'd0, 48 | parameter int unsigned ApbDataWidth = 32'd0, 49 | parameter int unsigned RegDataWidth = 32'd0 50 | ) ( 51 | input logic clk_i, 52 | input logic rst_ni 53 | ); 54 | localparam logic [NoApbRegs-1:0] ReadOnly = {NoApbRegs{1'b0}}; 55 | typedef logic [ApbAddrWidth-1:0] apb_addr_t; 56 | typedef logic [RegDataWidth-1:0] reg_data_t; 57 | 58 | APB #( 59 | .ADDR_WIDTH ( ApbAddrWidth ), 60 | .DATA_WIDTH ( ApbDataWidth ) 61 | ) apb_slave(); 62 | 63 | apb_addr_t base_addr; 64 | reg_data_t [NoApbRegs-1:0] register, reg_q; 65 | 66 | apb_regs_intf #( 67 | .NO_APB_REGS ( NoApbRegs ), 68 | .APB_ADDR_WIDTH ( ApbAddrWidth ), 69 | .ADDR_OFFSET ( 32'd4 ), 70 | .APB_DATA_WIDTH ( ApbDataWidth ), 71 | .REG_DATA_WIDTH ( RegDataWidth ), 72 | .READ_ONLY ( ReadOnly ) 73 | ) i_apb_reg_intf ( 74 | .pclk_i ( clk_i ), 75 | .preset_ni ( rst_ni ), 76 | .slv ( apb_slave ), 77 | .base_addr_i ( base_addr ), 78 | .reg_init_i ( register ), 79 | .reg_q_o ( reg_q ) 80 | ); 81 | endmodule 82 | -------------------------------------------------------------------------------- /test/tb_apb_cdc.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2018 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | // Author: Manuel Eggimann 12 | 13 | // Description: Testbench for `apb_cdc`. 14 | // Step 1: Read over a range of addresses, where the registers are inbetween. 15 | // Step 2: Random reads and writes to the different registers. 16 | // Assertions provide checking for correct functionality. 17 | 18 | `include "apb/assign.svh" 19 | 20 | module tb_apb_cdc #( 21 | parameter int unsigned ApbAddrWidth = 32'd32, 22 | parameter int unsigned ApbDataWidth = 32'd27, 23 | localparam int unsigned ApbStrbWidth = cf_math_pkg::ceil_div(ApbDataWidth, 8), 24 | parameter int unsigned CDCLogDepth = 3, 25 | 26 | // TB Parameters 27 | parameter time TCLK_UPSTREAM = 10ns, 28 | parameter time TA_UPSTREAM = TCLK_UPSTREAM * 1/4, 29 | parameter time TT_UPSTREAM = TCLK_UPSTREAM * 3/4, 30 | parameter time TCLK_DOWNSTREAM = 3ns, 31 | parameter time TA_DOWNSTREAM = TCLK_DOWNSTREAM * 1/4, 32 | parameter time TT_DOWNSTREAM = TCLK_DOWNSTREAM * 3/4, 33 | parameter int unsigned REQ_MIN_WAIT_CYCLES = 0, 34 | parameter int unsigned REQ_MAX_WAIT_CYCLES = 10, 35 | parameter int unsigned RESP_MIN_WAIT_CYCLES = 0, 36 | parameter int unsigned RESP_MAX_WAIT_CYCLES = REQ_MAX_WAIT_CYCLES/2, 37 | parameter int unsigned N_TXNS = 1000 38 | ); 39 | 40 | logic clk; 41 | logic rst_n; 42 | logic done; 43 | 44 | 45 | // Clocks and Resets 46 | 47 | logic upstream_clk, 48 | downstream_clk, 49 | upstream_rst_n, 50 | downstream_rst_n; 51 | 52 | clk_rst_gen #( 53 | .ClkPeriod (TCLK_UPSTREAM), 54 | .RstClkCycles (5) 55 | ) i_clk_rst_gen_upstream ( 56 | .clk_o (upstream_clk), 57 | .rst_no (upstream_rst_n) 58 | ); 59 | 60 | clk_rst_gen #( 61 | .ClkPeriod (TCLK_DOWNSTREAM), 62 | .RstClkCycles (5) 63 | ) i_clk_rst_gen_downstream ( 64 | .clk_o (downstream_clk), 65 | .rst_no (downstream_rst_n) 66 | ); 67 | 68 | 69 | APB_DV #( 70 | .ADDR_WIDTH ( ApbAddrWidth ), 71 | .DATA_WIDTH ( ApbDataWidth ) 72 | ) upstream_dv(upstream_clk); 73 | APB #( 74 | .ADDR_WIDTH ( ApbAddrWidth ), 75 | .DATA_WIDTH ( ApbDataWidth ) 76 | ) upstream(); 77 | `APB_ASSIGN(upstream, upstream_dv) 78 | 79 | APB_DV #( 80 | .ADDR_WIDTH ( ApbAddrWidth ), 81 | .DATA_WIDTH ( ApbDataWidth ) 82 | ) downstream_dv(downstream_clk); 83 | APB #( 84 | .ADDR_WIDTH ( ApbAddrWidth ), 85 | .DATA_WIDTH ( ApbDataWidth ) 86 | ) downstream(); 87 | `APB_ASSIGN(downstream_dv, downstream) 88 | 89 | 90 | typedef logic [ApbAddrWidth-1:0] apb_addr_t; 91 | typedef logic [ApbDataWidth-1:0] apb_data_t; 92 | typedef logic [ApbStrbWidth-1:0] apb_strb_t; 93 | typedef apb_test::apb_request #(.ADDR_WIDTH(ApbAddrWidth), .DATA_WIDTH(ApbDataWidth)) apb_request_t; 94 | typedef apb_test::apb_response #(.DATA_WIDTH(ApbDataWidth)) apb_response_t; 95 | 96 | apb_cdc_intf #( 97 | .APB_ADDR_WIDTH ( ApbAddrWidth ), 98 | .APB_DATA_WIDTH ( ApbDataWidth ), 99 | .LOG_DEPTH ( CDCLogDepth ) 100 | ) i_dut ( 101 | .src_pclk_i ( upstream_clk ), 102 | .src_preset_ni ( upstream_rst_n ), 103 | .src ( upstream ), 104 | .dst_pclk_i ( downstream_clk ), 105 | .dst_preset_ni ( downstream_rst_n ), 106 | .dst ( downstream ) 107 | ); 108 | 109 | apb_test::apb_rand_master #( 110 | .ADDR_WIDTH ( ApbAddrWidth ), 111 | .DATA_WIDTH ( ApbDataWidth ), 112 | .TA ( TA_UPSTREAM ), 113 | .TT ( TT_UPSTREAM ) 114 | ) apb_master = new(upstream_dv); 115 | 116 | 117 | logic mst_done = 1'b0; 118 | initial begin 119 | wait(upstream_rst_n); 120 | apb_master.run(N_TXNS); 121 | mst_done = 1'b1; 122 | end 123 | 124 | apb_test::apb_rand_slave #( 125 | .ADDR_WIDTH ( ApbAddrWidth ), 126 | .DATA_WIDTH ( ApbDataWidth ), 127 | .TA ( TA_DOWNSTREAM ), 128 | .TT ( TT_DOWNSTREAM ) 129 | ) apb_slave = new(downstream_dv); 130 | 131 | initial begin 132 | wait (downstream_rst_n); 133 | apb_slave.run(); 134 | end 135 | 136 | int unsigned upstream_errors = 0; 137 | 138 | // Monitor Upstream Respones 139 | initial begin 140 | automatic apb_response_t received_response = new; 141 | automatic apb_response_t expected_response = new; 142 | automatic bit pop_success; 143 | forever begin 144 | apb_master.response_queue.get(received_response); 145 | $info("Received new response"); 146 | pop_success = apb_slave.response_queue.try_get(expected_response); 147 | assert(pop_success) else begin 148 | $error("Upstream: Received spurious read response. prdata: %h, pslverr: %b", received_response.prdata, received_response.pslverr); 149 | upstream_errors++; 150 | continue; 151 | end 152 | //Compare upstream received response with downstream sent response 153 | assert(received_response.prdata == expected_response.prdata) else begin 154 | $error("Upstream: Received response's prdata: %h does not match the expected prdata: %h", received_response.prdata, expected_response.prdata); 155 | upstream_errors++; 156 | end 157 | assert(received_response.pslverr == expected_response.pslverr) else begin 158 | $error("Upstream: Received response's pslverr: %b does not match the expected pslverr: %b", received_response.pslverr, expected_response.pslverr); 159 | upstream_errors++; 160 | end 161 | end 162 | end // initial begin 163 | 164 | int unsigned downstream_errors = 0; 165 | 166 | // Monitor Downstream Requests 167 | initial begin 168 | automatic apb_request_t received_request = new; 169 | automatic apb_request_t expected_request = new; 170 | automatic bit pop_success; 171 | forever begin 172 | apb_slave.request_queue.get(received_request); 173 | $info("Received new request"); 174 | pop_success = apb_master.request_queue.try_get(expected_request); // Since the requests are recorded in 175 | // the master driver strictly before they arrive in the slave, an 176 | // empy request_queue in the master means, that we received an 177 | // spurious request. 178 | assert(pop_success) else begin 179 | $error("Downstream: Received spurious request. Addr: %h, Data: %h, write_en: %b", received_request.paddr, received_request.pdata, received_request.pwrite); 180 | downstream_errors++; 181 | continue; 182 | end 183 | //Compare downstream received request with upstream sent request 184 | assert(received_request.paddr == expected_request.paddr) else begin 185 | $error("Downstream: Received request's paddr: %h does not match the paddr: %h", received_request.paddr, expected_request.paddr); 186 | downstream_errors++; 187 | end 188 | assert(received_request.pdata == expected_request.pdata) else begin 189 | $error("Downstream: Received request's pdata: %h does not match the expected pdata: %h", received_request.pdata, expected_request.pdata); 190 | downstream_errors++; 191 | end 192 | assert(received_request.pwrite == expected_request.pwrite) else begin 193 | $error("Downstream: Received request's pwrite: %b does not match the expected pwrite: %b", received_request.pwrite, expected_request.pwrite); 194 | downstream_errors++; 195 | end 196 | end 197 | end // initial begin 198 | 199 | // Terminate simulation after all transactions have left master and the last response should have 200 | // arrived back 201 | 202 | initial begin 203 | wait(mst_done); 204 | // 10 Additional cycles for the request/response to pass through the 205 | // CDC FIFO should be enough. 206 | #((REQ_MAX_WAIT_CYCLES+10)*TCLK_UPSTREAM+(RESP_MAX_WAIT_CYCLES+10)*TCLK_DOWNSTREAM); 207 | 208 | // Now check if all queues are empty. If all requests/responses passed through the CDC the 209 | // master and slave queue should all be empty now. 210 | assert(apb_master.request_queue.num() == 0) else begin 211 | $error("Lost %0d requests.", apb_master.request_queue.num()); 212 | downstream_errors += apb_master.request_queue.num(); 213 | end 214 | assert(apb_slave.response_queue.num() == 0) else begin 215 | $error("Lost %0d responses.", apb_slave.response_queue.num()); 216 | upstream_errors += apb_slave.response_queue.num(); 217 | end 218 | $info("TB CDC finished after simulation of %0d random transactions.", N_TXNS); 219 | $info("Got %0d upstream errors and %0d downstream errors.", upstream_errors, downstream_errors); 220 | $finish(); 221 | end 222 | 223 | endmodule 224 | -------------------------------------------------------------------------------- /test/tb_apb_demux.sv: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Copyright (C) 2022 ETH Zurich, University of Bologna 3 | // Copyright and related rights are licensed under the Solderpad Hardware 4 | // License, Version 0.51 (the "License"); you may not use this file except in 5 | // compliance with the License. You may obtain a copy of the License at 6 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 7 | // or agreed to in writing, software, hardware and materials distributed under 8 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | // specific language governing permissions and limitations under the License. 11 | // SPDX-License-Identifier: SHL-0.51 12 | //----------------------------------------------------------------------------- 13 | 14 | // Author: Manuel Eggimann 15 | 16 | `include "apb/assign.svh" 17 | 18 | module tb_apb_demux #( 19 | parameter int unsigned ApbAddrWidth = 32'd15, 20 | parameter int unsigned ApbDataWidth = 32'd32, 21 | parameter int unsigned NoMstPorts = 5, 22 | localparam int unsigned ApbStrbWidth = cf_math_pkg::ceil_div(ApbDataWidth, 8), 23 | 24 | // TB Parameters 25 | parameter time TCLK = 10ns, 26 | parameter time TA = TCLK * 1/4, 27 | parameter time TT = TCLK * 3/4, 28 | parameter int unsigned REQ_MIN_WAIT_CYCLES = 0, 29 | parameter int unsigned REQ_MAX_WAIT_CYCLES = 10, 30 | parameter int unsigned RESP_MIN_WAIT_CYCLES = 0, 31 | parameter int unsigned RESP_MAX_WAIT_CYCLES = REQ_MAX_WAIT_CYCLES/2, 32 | parameter int unsigned N_TXNS = 1000 33 | ); 34 | logic clk; 35 | logic rst_n; 36 | logic done; 37 | 38 | logic [$clog2(NoMstPorts)-1:0] select; 39 | 40 | clk_rst_gen #( 41 | .ClkPeriod (TCLK), 42 | .RstClkCycles (5) 43 | ) i_clk_rst_gen ( 44 | .clk_o (clk), 45 | .rst_no (rst_n) 46 | ); 47 | 48 | APB_DV #( 49 | .ADDR_WIDTH ( ApbAddrWidth ), 50 | .DATA_WIDTH ( ApbDataWidth ) 51 | ) source_bus_dv(clk); 52 | 53 | APB #( 54 | .ADDR_WIDTH ( ApbAddrWidth ), 55 | .DATA_WIDTH ( ApbDataWidth ) 56 | ) source_bus(); 57 | `APB_ASSIGN(source_bus, source_bus_dv) 58 | 59 | APB_DV #( 60 | .ADDR_WIDTH ( ApbAddrWidth ), 61 | .DATA_WIDTH ( ApbDataWidth ) 62 | ) sink_bus_dv[NoMstPorts-1:0](clk); 63 | APB #( 64 | .ADDR_WIDTH ( ApbAddrWidth ), 65 | .DATA_WIDTH ( ApbDataWidth ) 66 | ) sink_bus[NoMstPorts-1:0](); 67 | 68 | typedef logic [ApbAddrWidth-1:0] apb_addr_t; 69 | typedef logic [ApbDataWidth-1:0] apb_data_t; 70 | typedef logic [ApbStrbWidth-1:0] apb_strb_t; 71 | typedef apb_test::apb_request #(.ADDR_WIDTH(ApbAddrWidth), .DATA_WIDTH(ApbDataWidth)) apb_request_t; 72 | typedef apb_test::apb_response #(.DATA_WIDTH(ApbDataWidth)) apb_response_t; 73 | 74 | typedef struct packed { 75 | int unsigned idx; 76 | apb_addr_t start_addr; 77 | apb_addr_t end_addr; 78 | } rule_t; 79 | 80 | localparam int unsigned ADDR_REGION_SIZE = (2**ApbAddrWidth)/NoMstPorts; 81 | rule_t[NoMstPorts-1:0] addr_map; 82 | 83 | addr_decode #( 84 | .NoIndices ( NoMstPorts ), 85 | .NoRules ( NoMstPorts ), 86 | .addr_t ( apb_addr_t ), 87 | .rule_t ( rule_t ) 88 | ) i_addr_decode( 89 | .addr_i ( source_bus_dv.paddr ), 90 | .addr_map_i ( addr_map ), 91 | .idx_o ( select ), 92 | .dec_valid_o ( ), 93 | .dec_error_o ( ), 94 | .en_default_idx_i ( 1'b1 ), 95 | .default_idx_i ( '0 ) 96 | ); 97 | 98 | apb_test::apb_rand_master #( 99 | .ADDR_WIDTH ( ApbAddrWidth ), 100 | .DATA_WIDTH ( ApbDataWidth ), 101 | .TA ( TA ), 102 | .TT ( TT ) 103 | ) apb_master = new(source_bus_dv); 104 | 105 | logic mst_done = 1'b0; 106 | initial begin 107 | wait(rst_n); 108 | apb_master.run(N_TXNS); 109 | mst_done = 1'b1; 110 | end 111 | 112 | int unsigned slv_errors[NoMstPorts-1:0] = '{default: 0}; 113 | int unsigned errors = 0; 114 | 115 | apb_test::apb_rand_slave #( 116 | .ADDR_WIDTH ( ApbAddrWidth ), 117 | .DATA_WIDTH ( ApbDataWidth ), 118 | .TA ( TA ), 119 | .TT ( TT ) 120 | ) apb_slave[NoMstPorts-1:0]; 121 | 122 | for (genvar idx = 0; idx < NoMstPorts; idx++) begin :gen_slaves 123 | `APB_ASSIGN(sink_bus_dv[idx], sink_bus[idx]) 124 | 125 | assign addr_map[idx] = '{idx: idx, start_addr: idx*ADDR_REGION_SIZE, end_addr: (idx+1)*ADDR_REGION_SIZE}; 126 | 127 | initial begin 128 | apb_slave[idx] = new(sink_bus_dv[idx]); 129 | wait (rst_n); 130 | apb_slave[idx].run(); 131 | end 132 | end 133 | 134 | // Monitor master 135 | initial begin 136 | automatic apb_request_t expected_request = new; 137 | automatic apb_request_t received_request = new; 138 | automatic apb_response_t received_response = new; 139 | automatic apb_response_t expected_response = new; 140 | automatic int unsigned expected_select; 141 | automatic bit pop_success; 142 | forever begin 143 | apb_master.request_queue.get(expected_request); 144 | @(posedge source_bus_dv.penable); 145 | $info("Checking new transaction..."); 146 | expected_select = expected_request.paddr 12 | 13 | // Description: Testbench for `apb_rw_regs`. 14 | // Step 1: Read over a range of addresses, where the registers are inbetween. 15 | // Step 2: Random reads and writes to the different registers. 16 | // Assertions provide checking for correct functionality. 17 | 18 | `include "apb/assign.svh" 19 | 20 | module tb_apb_regs; 21 | 22 | localparam int unsigned ApbAddrWidth = 32'd32; 23 | localparam int unsigned ApbDataWidth = 32'd27; 24 | localparam int unsigned ApbStrbWidth = cf_math_pkg::ceil_div(ApbDataWidth, 8); 25 | localparam int unsigned RegDataWidth = 32'd16; 26 | 27 | localparam int unsigned NoApbRegs = 32'd342; 28 | localparam logic [NoApbRegs-1:0] ReadOnly = 342'hFFF0; 29 | 30 | localparam time CyclTime = 10ns; 31 | localparam time ApplTime = 2ns; 32 | localparam time TestTime = 8ns; 33 | localparam int unsigned NoRandAccesses = 32'd50000; 34 | 35 | typedef logic [ApbAddrWidth-1:0] apb_addr_t; 36 | typedef logic [ApbDataWidth-1:0] apb_data_t; 37 | typedef logic [ApbStrbWidth-1:0] apb_strb_t; 38 | typedef logic [RegDataWidth-1:0] reg_data_t; 39 | 40 | localparam apb_addr_t BaseAddr = 32'h0003_0000; 41 | localparam apb_addr_t TestStartAddr = 32'h0002_FF00; 42 | localparam apb_addr_t TestEndAddr = 32'h0003_0F00; 43 | 44 | 45 | logic clk; 46 | logic rst_n; 47 | logic done; 48 | apb_addr_t last_addr; 49 | reg_data_t [NoApbRegs-1:0] reg_data, reg_init, reg_compare; 50 | 51 | 52 | APB_DV #( 53 | .ADDR_WIDTH ( ApbAddrWidth ), 54 | .DATA_WIDTH ( ApbDataWidth ) 55 | ) apb_slave_dv(clk); 56 | APB #( 57 | .ADDR_WIDTH ( ApbAddrWidth ), 58 | .DATA_WIDTH ( ApbDataWidth ) 59 | ) apb_slave(); 60 | `APB_ASSIGN ( apb_slave, apb_slave_dv ) 61 | 62 | //----------------------------------- 63 | // Clock generator 64 | //----------------------------------- 65 | clk_rst_gen #( 66 | .CLK_PERIOD ( CyclTime ), 67 | .RST_CLK_CYCLES( 5 ) 68 | ) i_clk_gen ( 69 | .clk_o (clk), 70 | .rst_no(rst_n) 71 | ); 72 | 73 | apb_test::apb_driver #( 74 | .ADDR_WIDTH ( ApbAddrWidth ), 75 | .DATA_WIDTH ( ApbDataWidth ), 76 | .TA ( ApplTime ), 77 | .TT ( TestTime ) 78 | ) apb_master = new(apb_slave_dv); 79 | 80 | initial begin : proc_apb_master 81 | automatic apb_addr_t addr; 82 | automatic apb_data_t data; 83 | automatic apb_strb_t strb; 84 | automatic logic resp; 85 | automatic bit w; 86 | automatic reg_data_t init_val; 87 | // initialize reset values and golden model 88 | for (int unsigned i = 0; i < NoApbRegs; i++) begin 89 | init_val = reg_data_t'($urandom()); 90 | reg_init[i] = init_val; 91 | reg_compare[i] = init_val; 92 | end 93 | done <= 1'b0; 94 | 95 | // reset dut 96 | @(posedge rst_n); 97 | apb_master.reset_master(); 98 | repeat (10) @(posedge clk); 99 | 100 | // First test 101 | addr = apb_addr_t'(BaseAddr); 102 | data = apb_data_t'(32'd0000_0000); 103 | strb = apb_strb_t'(4'hF); 104 | 105 | apb_master.write( addr, data, strb, resp); 106 | $display("Write addr: %0h", addr); 107 | $display("Write data: %0h strb: %0h", data, strb); 108 | $display("Write resp: %0h", resp); 109 | assert(resp == apb_pkg::RESP_OKAY); 110 | if (resp == apb_pkg::RESP_OKAY) begin 111 | // update golden model 112 | $display("Update golden model"); 113 | for (int unsigned j = 0; j < RegDataWidth; j++) begin 114 | if (strb[j/8]) begin 115 | $info("Write bit @%0h, bit: %0h", addr>>2, j); 116 | reg_compare[(addr-BaseAddr)>>2][j] = data[j]; 117 | end 118 | end 119 | end 120 | 121 | // Step 1 122 | for (int unsigned i = TestStartAddr; i < TestEndAddr; i++) begin 123 | addr = apb_addr_t'(i); 124 | apb_master.read(addr, data, resp); 125 | $display("Read from addr: %0h", addr); 126 | $display("Read data: %0h", data); 127 | $display("Read resp: %0h", resp); 128 | repeat ($urandom_range(0,5)) @(posedge clk); 129 | end 130 | 131 | // Step 2 132 | for (int unsigned i = 0; i < NoRandAccesses; i++) begin 133 | w = bit'($urandom()); 134 | addr = apb_addr_t'($urandom_range(TestStartAddr, TestEndAddr)); 135 | data = apb_data_t'($urandom()); 136 | strb = apb_strb_t'($urandom()); 137 | if (w) begin 138 | apb_master.write( addr, data, strb, resp); 139 | $display("Write addr: %0h", addr); 140 | $display("Write data: %0h strb: %0h", data, strb); 141 | $display("Write resp: %0h", resp); 142 | if (resp == apb_pkg::RESP_OKAY) begin 143 | // update golden model 144 | for (int unsigned j = 0; j < RegDataWidth; j++) begin 145 | if (strb[j/8]) begin 146 | reg_compare[(addr-BaseAddr)>>2][j] = data[j]; 147 | end 148 | end 149 | end 150 | end else begin 151 | apb_master.read(addr, data, resp); 152 | $display("Read from addr: %0h", addr); 153 | $display("Read data: %0h", data); 154 | $display("Read resp: %0h", resp); 155 | end 156 | $display("Last Addr: %0h", last_addr); 157 | repeat ($urandom_range(0,5)) @(posedge clk); 158 | end 159 | 160 | done <= 1'b1; 161 | end 162 | 163 | initial begin : proc_end_sim 164 | @(posedge done); 165 | repeat(10) @(posedge clk); 166 | $stop(); 167 | end 168 | 169 | // one cycle delayed addr 170 | always_ff @(posedge clk or negedge rst_n) begin : proc_last_addr_reg 171 | if(~rst_n) begin 172 | last_addr <= '0; 173 | end else begin 174 | last_addr <= apb_slave.paddr; 175 | end 176 | end 177 | 178 | 179 | // pragma translate_off 180 | `ifndef VERILATOR 181 | // Assertions to determine correct APB protocol sequencing 182 | default disable iff (!rst_n); 183 | // when psel is not asserted, the bus is in the idle state 184 | sequence APB_IDLE; 185 | !apb_slave.psel; 186 | endsequence 187 | 188 | // when psel is set and penable is not, it is the setup state 189 | sequence APB_SETUP; 190 | apb_slave.psel && !apb_slave.penable; 191 | endsequence 192 | 193 | // when psel and penable are set it is the access state 194 | sequence APB_ACCESS; 195 | apb_slave.psel && apb_slave.penable; 196 | endsequence 197 | 198 | sequence APB_RESP_OKAY; 199 | apb_slave.pready && (apb_slave.pslverr == apb_pkg::RESP_OKAY); 200 | endsequence 201 | 202 | sequence APB_RESP_SLVERR; 203 | apb_slave.pready && (apb_slave.pslverr == apb_pkg::RESP_SLVERR); 204 | endsequence 205 | 206 | // APB Transfer is APB state going from setup to access 207 | sequence APB_TRANSFER; 208 | APB_SETUP ##1 APB_ACCESS; 209 | endsequence 210 | 211 | apb_complete: assert property ( @(posedge clk) 212 | (APB_SETUP |-> APB_TRANSFER)); 213 | 214 | apb_penable: assert property ( @(posedge clk) 215 | (apb_slave.penable && apb_slave.psel && apb_slave.pready |=> (!apb_slave.penable))); 216 | 217 | control_stable: assert property ( @(posedge clk) 218 | (APB_TRANSFER |-> $stable({apb_slave.pwrite, apb_slave.paddr}))); 219 | 220 | apb_valid: assert property ( @(posedge clk) 221 | (APB_TRANSFER |-> ((!{apb_slave.pwrite, apb_slave.pstrb, apb_slave.paddr}) !== 1'bx))); 222 | 223 | write_stable: assert property ( @(posedge clk) 224 | ((apb_slave.penable && apb_slave.pwrite) |-> $stable(apb_slave.pwdata))); 225 | 226 | strb_stable: assert property ( @(posedge clk) 227 | ((apb_slave.penable && apb_slave.pwrite) |-> $stable(apb_slave.pstrb))); 228 | 229 | correct_rdata: assert property ( @(posedge clk) 230 | (apb_slave.penable && apb_slave.pready && 231 | (apb_slave.pslverr == apb_pkg::RESP_OKAY) && !apb_slave.pwrite) 232 | |-> (apb_slave.prdata == apb_data_t'(reg_compare[(apb_slave.paddr-BaseAddr)>>2]))) else 233 | $fatal(1, "Unexpected read response!"); 234 | correct_wdata: assert property ( @(posedge clk) 235 | ((apb_slave.penable && apb_slave.pready && 236 | (apb_slave.pslverr == apb_pkg::RESP_OKAY) && apb_slave.pwrite) 237 | |=> (reg_data[(last_addr-BaseAddr)>>2] == reg_compare[(last_addr-BaseAddr)>>2]))) else 238 | $fatal(1, "Unexpected write output!: addr: %0h, output: %0h, expected: %0h", 239 | (last_addr-BaseAddr)>>2, reg_data[(last_addr-BaseAddr)>>2], 240 | reg_compare[(last_addr-BaseAddr)>>2] ); 241 | `endif 242 | // pragma translate_on 243 | 244 | // Dut 245 | apb_regs_intf #( 246 | .NO_APB_REGS ( NoApbRegs ), 247 | .ADDR_OFFSET ( 32'd4 ), 248 | .APB_ADDR_WIDTH ( ApbAddrWidth ), 249 | .APB_DATA_WIDTH ( ApbDataWidth ), 250 | .REG_DATA_WIDTH ( RegDataWidth ), 251 | .READ_ONLY ( ReadOnly ) 252 | ) i_apb_regs_dut ( 253 | .pclk_i ( clk ), 254 | .preset_ni ( rst_n ), 255 | .slv ( apb_slave ), 256 | .base_addr_i ( BaseAddr ), 257 | .reg_init_i ( reg_init ), 258 | .reg_q_o ( reg_data ) 259 | ); 260 | endmodule 261 | -------------------------------------------------------------------------------- /test/tb_apb_regs.wave.do: -------------------------------------------------------------------------------- 1 | log -r * 2 | onerror {resume} 3 | quietly WaveActivateNextPane {} 0 4 | add wave -noupdate /tb_apb_regs/i_apb_regs_dut/pclk_i 5 | add wave -noupdate /tb_apb_regs/i_apb_regs_dut/preset_ni 6 | add wave -noupdate /tb_apb_regs/i_apb_regs_dut/base_addr_i 7 | add wave -noupdate -expand /tb_apb_regs/i_apb_regs_dut/apb_req 8 | add wave -noupdate -expand /tb_apb_regs/i_apb_regs_dut/apb_resp 9 | add wave -noupdate /tb_apb_regs/i_apb_regs_dut/base_addr_i 10 | add wave -noupdate /tb_apb_regs/i_apb_regs_dut/reg_init_i 11 | add wave -noupdate /tb_apb_regs/i_apb_regs_dut/reg_q_o 12 | TreeUpdate [SetDefaultTree] 13 | WaveRestoreCursors {{Cursor 1} {0 ns} 0} 14 | quietly wave cursor active 0 15 | configure wave -namecolwidth 310 16 | configure wave -valuecolwidth 100 17 | configure wave -justifyvalue left 18 | configure wave -signalnamewidth 0 19 | configure wave -snapdistance 10 20 | configure wave -datasetprefix 0 21 | configure wave -rowmargin 4 22 | configure wave -childrowmargin 2 23 | configure wave -gridoffset 0 24 | configure wave -gridperiod 1 25 | configure wave -griddelta 40 26 | configure wave -timeline 0 27 | configure wave -timelineunits ns 28 | update 29 | WaveRestoreZoom {0 ns} {20648 ns} 30 | --------------------------------------------------------------------------------