├── .github └── workflows │ └── Test.yml ├── .gitignore ├── .gitmodules ├── LICENSE.md ├── README.md ├── common └── UtilsP.vhd ├── formal ├── Makefile ├── WishBoneMasterE.sby └── WishBoneSlaveE.sby ├── sim ├── AssertP.vhd ├── DictP.vhd ├── QueueP.vhd ├── SimP.vhd └── StackP.vhd ├── syn ├── SpiMasterE.vhd ├── SpiSlaveE.vhd ├── UartRx.vhd ├── UartTx.vhd ├── WishBoneCheckerE.vhd ├── WishBoneMasterE.vhd ├── WishBoneP.vhd └── WishBoneSlaveE.vhd └── test ├── DictT.vhd ├── Makefile ├── QueueT.vhd ├── SimT.vhd ├── SpiT.vhd ├── StackT.vhd ├── UartT.vhd ├── WishBoneT.tcl └── WishBoneT.vhd /.github/workflows/Test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | 9 | Test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | with: 14 | submodules: recursive 15 | - run: docker run --rm -tv $(pwd):/src -w /src/test -e BUILD_NAME=ACCEPTANCE ghdl/vunit:llvm make all 16 | 17 | Formal: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v4 21 | with: 22 | submodules: recursive 23 | - run: docker run --rm -tv $(pwd):/src -w /src/formal -e BUILD_NAME=ACCEPTANCE ghdl/synth:formal make all 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | test/vhdl_2008/*.vhdl 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/OSVVM"] 2 | path = lib/OSVVM 3 | url = https://github.com/OSVVM/OSVVM.git 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | ## TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | ### 1. Definitions. 8 | 9 | *"License"* shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 10 | 11 | *"Licensor"* shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 12 | 13 | *"Legal Entity"* shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 14 | 15 | *"You"* (or *"Your"*) shall mean an individual or Legal Entity exercising permissions granted by this License. 16 | 17 | *"Source"* form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 18 | 19 | *"Object"* form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 20 | 21 | *"Work"* shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 22 | 23 | *"Derivative Works"* shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 24 | 25 | *"Contribution"* shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, *"submitted"* means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as *"Not a Contribution."* 26 | 27 | *"Contributor"* shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 28 | 29 | ### 2. Grant of Copyright License. 30 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 31 | 32 | ### 3. Grant of Patent License. 33 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 34 | 35 | ### 4. Redistribution. 36 | You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 37 | 38 | - You must give any other recipients of the Work or Derivative Works a copy of this License; and 39 | - You must cause any modified files to carry prominent notices stating that You changed the files; and 40 | - You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 41 | - If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. 42 | 43 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 44 | 45 | ### 5. Submission of Contributions. 46 | Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 47 | 48 | ### 6. Trademarks. 49 | This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 50 | 51 | ### 7. Disclaimer of Warranty. 52 | Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 53 | 54 | ### 8. Limitation of Liability. 55 | In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 56 | 57 | ### 9. Accepting Warranty or Additional Liability. 58 | While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 59 | 60 | 61 | ## Appendix: How to apply the Apache License to your work 62 | 63 | To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. 64 | 65 | Copyright [yyyy] [name of copyright owner] 66 | 67 | Licensed under the Apache License, Version 2.0 (the "License"); 68 | you may not use this file except in compliance with the License. 69 | You may obtain a copy of the License at 70 | 71 | http://www.apache.org/licenses/LICENSE-2.0 72 | 73 | Unless required by applicable law or agreed to in writing, software 74 | distributed under the License is distributed on an "AS IS" BASIS, 75 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 76 | See the License for the specific language governing permissions and 77 | limitations under the License. 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://img.shields.io/github/actions/workflow/status/tmeissner/libvhdl/Test.yml?style=flat-square&logo=Github%20Actions&logoColor=fff&label=Test)](https://github.com/tmeissner/libvhdl/actions/workflows/Test.yml) 2 | 3 | The original repository is now located on my own git-server at [https://git.goodcleanfun.de/tmeissner/libvhdl](https://git.goodcleanfun.de/tmeissner/libvhdl) 4 | It is mirrored to github with every push, so both should be in sync. 5 | 6 | # libvhdl 7 | A permissive licensed library of reusable components for VHDL designs and testbenches. 8 | 9 | The intention of this library is not to realize the most optimized and highest performing code. 10 | Instead it serves more as an example how to implement various things in VHDL and test them efficiently. 11 | 12 | ## sim 13 | (Non-)synthesizable components for testbenches 14 | 15 | ##### AssertP (Deprecated, better use Alerts from OSVVM instead) 16 | Package with various assertion procedures. 17 | 18 | * `assert_true(x[, str, level])` checks if boolean x = false 19 | * `assert_false(x[, str, level])` checks if boolean x = false 20 | * `assert_equal(x, y[, str, level])` checks if x = y 21 | * `assert_unequal(x, y[, str, level])` checks if x /= y 22 | 23 | All of the assert_* procedures have following optional parameters: 24 | 25 | * `str` print string str to console instead implemented one 26 | * `level` severity level (note, warning, error, failure) 27 | 28 | ##### SimP 29 | Package with various components general useful for simulation 30 | 31 | * `wait_cycles(x, n)` waits for n rising edges on std_logic signal x 32 | * `spi_master()` configurable master for SPI protocol, supports all cpol/cpha modes 33 | * `spi_slave()` configurable slave for SPI protocol, supports all cpol/cpha modes 34 | 35 | ##### QueueP 36 | Generic package with various implementations of queue types: 37 | 38 | * `t_simple_queue` simple array based FIFO queue 39 | * `t_list_queue` linked list FIFO queue using access types 40 | 41 | ##### DictP 42 | Generic package with implementation of dictionary (aka associative array) type: 43 | 44 | * `t_dict` linked list dictionary using access types 45 | 46 | ## syn 47 | Synthesizable components for implementing in FPGA 48 | 49 | ##### SpiMasterE 50 | Configurable SPI master with support modes 0-3 and simple VAI local backend. 51 | 52 | ##### SpiSlaveE 53 | Configurable SPI slave with support modes 0-3 and simple VAI local backend. 54 | 55 | ##### UartTx 56 | Configurable UART transmitter 57 | 58 | ##### UartRx 59 | Configurable UART receiver 60 | 61 | ##### WishBoneMasterE 62 | Simple WishBone bus master with support of classic single write & read 63 | 64 | ##### WishBoneSlaveE 65 | Simple WishBone bus slave with support of classic single write & read and register backend 66 | 67 | 68 | ## test 69 | Unit tests for each component 70 | 71 | ##### QueueT 72 | Unit tests for components of QueueP package 73 | 74 | ##### SimT 75 | Unit tests for components of SimP package 76 | 77 | ##### SpiT 78 | Unit tests for SpiMasterE and SpiSlaveE components 79 | 80 | ##### UartT 81 | Unit test for UartTx and UartRx components 82 | 83 | ##### WishBoneT 84 | Unit tests for WishBoneMasterE and WishBoneSlaveE components 85 | 86 | 87 | ## formal 88 | Formal verification for selected components 89 | 90 | 91 | ## common 92 | Common utilities 93 | 94 | ##### UtilsP 95 | Common functions useful for simulation/synthesis 96 | 97 | * `and_reduce(x)` returns and of all items in x, collapsed to one std_logic/boolean 98 | * `or_reduce(x)` returns or of all items in x, collapsed to one std_logic/boolean 99 | * `xor_reduce(x)` returns xor of items in x, collapsed to one std_logic 100 | * `even_parity(x)` returns even parity of x 101 | * `odd_parity(x)` returns odd parity of x 102 | * `count_ones(x)` returns number of '1' in x 103 | * `one_hot(x)` returns true if x is one-hot coded, false otherwise 104 | * `is_unknown(x)` returns true if x contains 'U' bit, false otherwise 105 | * `uint_to_slv(x, l)` returns std_logic_vector (unsigned) with length l converted from x (natural) 106 | * `slv_to_uint(x)` returns natural converted from x (std_logic_vector) (unsigned) 107 | * `uint_bitsize(x)` returns number of bits needed for given x (natural) 108 | 109 | 110 | ## Dependencies 111 | To run the tests, you have to install GHDL. You can get it from 112 | [https://github.com/tgingold/ghdl/](https://github.com/tgingold/ghdl/). Your GHDL version should not be too old, because libvhdl needs VHDL-2008 support. So, it's best to get the latest stable release or build from latest sources. 113 | 114 | libvhdl uses the OSVVM library to generate random data for the unit tests. It is shipped with libvhdl as git submodule. You have to use the `--recursive` option when clone 115 | the libvhdl Repository to get it: `git clone --recursive https://git.goodcleanfun.de/tmeissner/libvhdl` 116 | 117 | Another useful tool is GTKWave, install it if you want to use the waveform files generated by some of the tests. 118 | 119 | 120 | ## Building 121 | Type `make` to do all tests. You should see the successfully running tests like this: 122 | 123 | ``` 124 | $ make 125 | ghdl -a --std=02 ../sim/QueueP.vhd QueueT.vhd 126 | ghdl -e --std=02 QueueT 127 | ghdl -r --std=02 QueueT 128 | QueueT.vhd:52:5:@0ms:(report note): INFO: t_simple_queue test finished successfully 129 | QueueT.vhd:87:5:@0ms:(report note): INFO: t_list_queue test finished successfully 130 | ``` 131 | -------------------------------------------------------------------------------- /common/UtilsP.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | 22 | 23 | package UtilsP is 24 | 25 | 26 | function and_reduce (data : in std_logic_vector) return std_logic; 27 | function and_reduce (data : in boolean_vector) return boolean; 28 | 29 | function or_reduce (data : in std_logic_vector) return std_logic; 30 | function or_reduce (data : in boolean_vector) return boolean; 31 | 32 | function xor_reduce (data : in std_logic_vector) return std_logic; 33 | 34 | function even_parity (data : in std_logic_vector) return std_logic; 35 | function odd_parity (data : in std_logic_vector) return std_logic; 36 | 37 | function count_ones (data : in std_logic_vector) return natural; 38 | 39 | function one_hot (data : in std_logic_vector) return boolean; 40 | 41 | function is_unknown (data : in std_logic_vector) return boolean; 42 | 43 | function uint_to_slv (data: in natural; len : in positive) return std_logic_vector; 44 | function slv_to_uint (data: in std_logic_vector) return natural; 45 | 46 | function uint_bitsize(data : in natural) return natural; 47 | 48 | 49 | end package UtilsP; 50 | 51 | 52 | 53 | package body UtilsP is 54 | 55 | 56 | function and_reduce (data : in std_logic_vector) return std_logic is 57 | variable v_return : std_logic := '1'; 58 | begin 59 | for i in data'range loop 60 | v_return := v_return and data(i); 61 | end loop; 62 | return v_return; 63 | end function and_reduce; 64 | 65 | function and_reduce (data : in boolean_vector) return boolean is 66 | begin 67 | for i in data'range loop 68 | if (not(data(i))) then 69 | return false; 70 | end if; 71 | end loop; 72 | return true; 73 | end function and_reduce; 74 | 75 | 76 | function or_reduce (data : in std_logic_vector) return std_logic is 77 | variable v_return : std_logic := '0'; 78 | begin 79 | for i in data'range loop 80 | v_return := v_return or data(i); 81 | end loop; 82 | return v_return; 83 | end function or_reduce; 84 | 85 | function or_reduce (data : in boolean_vector) return boolean is 86 | begin 87 | for i in data'range loop 88 | if data(i) then 89 | return true; 90 | end if; 91 | end loop; 92 | return false; 93 | end function or_reduce; 94 | 95 | 96 | function xor_reduce (data : in std_logic_vector) return std_logic is 97 | variable v_return : std_logic := '0'; 98 | begin 99 | for i in data'range loop 100 | v_return := v_return xor data(i); 101 | end loop; 102 | return v_return; 103 | end function xor_reduce; 104 | 105 | 106 | function even_parity (data : in std_logic_vector) return std_logic is 107 | begin 108 | return xor_reduce(data); 109 | end function even_parity; 110 | 111 | function odd_parity (data : in std_logic_vector) return std_logic is 112 | begin 113 | return not(xor_reduce(data)); 114 | end function odd_parity; 115 | 116 | 117 | function count_ones (data : in std_logic_vector) return natural is 118 | variable v_return : natural := 0; 119 | begin 120 | for i in data'range loop 121 | if (to_ux01(data(i)) = '1') then 122 | v_return := v_return + 1; 123 | end if; 124 | end loop; 125 | return v_return; 126 | end function count_ones; 127 | 128 | 129 | function one_hot (data : in std_logic_vector) return boolean is 130 | begin 131 | return count_ones(data) = 1; 132 | end function one_hot; 133 | 134 | 135 | function is_unknown (data : in std_logic_vector) return boolean is 136 | begin 137 | for i in data'range loop 138 | if (to_ux01(data(i)) = 'U') then 139 | return true; 140 | end if; 141 | end loop; 142 | end function is_unknown; 143 | 144 | 145 | function uint_to_slv (data: in natural; len : in positive) return std_logic_vector is 146 | begin 147 | assert len >= uint_bitsize(data) 148 | report "Warning: std_logic_vector result truncated" 149 | severity warning; 150 | return std_logic_vector(to_unsigned(data, len)); 151 | end function uint_to_slv; 152 | 153 | function slv_to_uint (data: in std_logic_vector) return natural is 154 | begin 155 | if data'ascending then 156 | assert data'length <= 31 or or_reduce(data(data'left to data'right-31)) = '0' 157 | report "WARNING: integer result overflow" 158 | severity warning; 159 | else 160 | assert data'length <= 31 or or_reduce(data(data'left downto data'right+31)) = '0' 161 | report "WARNING: integer result overflow" 162 | severity warning; 163 | end if; 164 | return to_integer(unsigned(data)); 165 | end function slv_to_uint; 166 | 167 | 168 | function uint_bitsize(data : in natural) return natural is 169 | variable v_nlz : natural := 0; 170 | variable v_data : unsigned(30 downto 0) := to_unsigned(data, 31); 171 | begin 172 | if (data = 0) then 173 | return 1; 174 | end if; 175 | for i in 30 downto 0 loop 176 | if(v_data(i) /= '0') then 177 | exit; 178 | else 179 | v_nlz := v_nlz + 1; 180 | end if; 181 | end loop; 182 | return 31 - v_nlz; 183 | end function uint_bitsize; 184 | 185 | 186 | end package body UtilsP; 187 | -------------------------------------------------------------------------------- /formal/Makefile: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2014 - 2022 by Torsten Meissner 2 | ## 3 | ## Licensed under the Apache License, Version 2.0 (the "License"); 4 | ## you may not use this file except in compliance with the License. 5 | ## You may obtain a copy of the License at 6 | ## 7 | ## https://www.apache.org/licenses/LICENSE-2.0 8 | ## 9 | ## Unless required by applicable law or agreed to in writing, software 10 | ## distributed under the License is distributed on an "AS IS" BASIS, 11 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ## See the License for the specific language governing permissions and 13 | ## limitations under the License. 14 | 15 | 16 | 17 | .PHONY: all-cover all-bmc all-prove all 18 | all: all-cover all-bmc all-prove 19 | all-cover: WishBoneMasterE-cover WishBoneSlaveE-cover 20 | all-bmc: WishBoneMasterE-bmc WishBoneSlaveE-bmc 21 | all-prove: WishBoneMasterE-prove WishBoneSlaveE-prove 22 | 23 | 24 | %-cover: ../syn/%.vhd %.sby 25 | mkdir -p work 26 | sby --yosys "yosys -m ghdl" -f -d work/$@ $(subst -cover,,$@).sby cover 27 | 28 | %-bmc: ../syn/%.vhd %.sby 29 | mkdir -p work 30 | sby --yosys "yosys -m ghdl" -f -d work/$@ $(subst -bmc,,$@).sby bmc 31 | 32 | %-prove: ../syn/%.vhd %.sby 33 | mkdir -p work 34 | sby --yosys "yosys -m ghdl" -f -d work/$@ $(subst -prove,,$@).sby prove 35 | 36 | 37 | clean: 38 | rm -rf work 39 | -------------------------------------------------------------------------------- /formal/WishBoneMasterE.sby: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2014 - 2022 by Torsten Meissner 2 | ## 3 | ## Licensed under the Apache License, Version 2.0 (the "License"); 4 | ## you may not use this file except in compliance with the License. 5 | ## You may obtain a copy of the License at 6 | ## 7 | ## https://www.apache.org/licenses/LICENSE-2.0 8 | ## 9 | ## Unless required by applicable law or agreed to in writing, software 10 | ## distributed under the License is distributed on an "AS IS" BASIS, 11 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ## See the License for the specific language governing permissions and 13 | ## limitations under the License. 14 | 15 | 16 | 17 | [tasks] 18 | bmc 19 | prove 20 | cover 21 | 22 | [options] 23 | depth 25 24 | bmc: mode bmc 25 | prove: mode prove 26 | cover: mode cover 27 | 28 | [engines] 29 | bmc: smtbmc z3 30 | prove: abc pdr 31 | cover: smtbmc z3 32 | 33 | [script] 34 | bmc: ghdl --std=08 -gCoverage=false -gFormal=true -gSimulation=false -gAddressWidth=32 -gDataWidth=32 WishBoneMasterE.vhd -e wishbonemastere 35 | prove: ghdl --std=08 -gCoverage=false -gFormal=true -gSimulation=false -gAddressWidth=32 -gDataWidth=32 WishBoneMasterE.vhd -e wishbonemastere 36 | cover: ghdl --std=08 -gCoverage=true -gFormal=true -gSimulation=false -gAddressWidth=32 -gDataWidth=32 WishBoneMasterE.vhd -e wishbonemastere 37 | prep -auto-top 38 | 39 | [files] 40 | ../syn/WishBoneMasterE.vhd 41 | -------------------------------------------------------------------------------- /formal/WishBoneSlaveE.sby: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2014 - 2022 by Torsten Meissner 2 | ## 3 | ## Licensed under the Apache License, Version 2.0 (the "License"); 4 | ## you may not use this file except in compliance with the License. 5 | ## You may obtain a copy of the License at 6 | ## 7 | ## https://www.apache.org/licenses/LICENSE-2.0 8 | ## 9 | ## Unless required by applicable law or agreed to in writing, software 10 | ## distributed under the License is distributed on an "AS IS" BASIS, 11 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ## See the License for the specific language governing permissions and 13 | ## limitations under the License. 14 | 15 | 16 | 17 | [tasks] 18 | bmc 19 | prove 20 | cover 21 | 22 | [options] 23 | depth 25 24 | bmc: mode bmc 25 | prove: mode prove 26 | cover: mode cover 27 | 28 | [engines] 29 | bmc: smtbmc z3 30 | prove: abc pdr 31 | cover: smtbmc z3 32 | 33 | [script] 34 | bmc: ghdl --std=08 -gFormal=true -gSimulation=false -gAddressWidth=32 -gDataWidth=32 WishBoneSlaveE.vhd -e wishboneslavee 35 | prove: ghdl --std=08 -gFormal=true -gSimulation=false -gAddressWidth=32 -gDataWidth=32 WishBoneSlaveE.vhd -e wishboneslavee 36 | cover: ghdl --std=08 -gFormal=true -gSimulation=false -gAddressWidth=32 -gDataWidth=32 WishBoneSlaveE.vhd -e wishboneslavee 37 | prep -auto-top 38 | 39 | [files] 40 | ../syn/WishBoneSlaveE.vhd 41 | -------------------------------------------------------------------------------- /sim/AssertP.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | 20 | --+ including vhdl 2008 libraries 21 | --+ These lines can be commented out when using 22 | --+ a simulator with built-in VHDL 2008 support 23 | --library ieee_proposed; 24 | -- use ieee_proposed.standard_additions.all; 25 | -- use ieee_proposed.std_logic_1164_additions.all; 26 | 27 | 28 | 29 | package AssertP is 30 | 31 | 32 | procedure assert_true ( a : in boolean; 33 | str : in string := "a should be evaluate to true"; 34 | level : in severity_level := failure); 35 | 36 | procedure assert_false ( a : in boolean; 37 | str : in string := "a should be evaluate to false"; 38 | level : in severity_level := failure); 39 | 40 | procedure assert_equal (a, b : in integer; 41 | str : in string := ""; 42 | level : in severity_level := failure); 43 | 44 | procedure assert_equal ( a, b : in std_logic_vector; 45 | str : in string := ""; 46 | level : in severity_level := failure); 47 | 48 | 49 | procedure assert_equal ( a, b : in string; 50 | str : in string := ""; 51 | level : in severity_level := failure); 52 | 53 | procedure assert_unequal (a, b : in integer; 54 | str : in string := ""; 55 | level : in severity_level := failure); 56 | 57 | procedure assert_unequal ( a, b : in std_logic_vector; 58 | str : in string := ""; 59 | level : in severity_level := failure); 60 | 61 | 62 | procedure assert_unequal ( a, b : in string; 63 | str : in string := ""; 64 | level : in severity_level := failure); 65 | 66 | 67 | end package AssertP; 68 | 69 | 70 | 71 | package body AssertP is 72 | 73 | 74 | procedure assert_true ( a : in boolean; 75 | str : in string := "a should be evaluate to true"; 76 | level : in severity_level := failure) is 77 | begin 78 | assert a 79 | report str 80 | severity level; 81 | end procedure assert_true; 82 | 83 | 84 | procedure assert_false ( a : in boolean; 85 | str : in string := "a should be evaluate to false"; 86 | level : in severity_level := failure) is 87 | begin 88 | assert not(a) 89 | report str 90 | severity level; 91 | end procedure assert_false; 92 | 93 | 94 | procedure assert_equal ( a, b : in integer; 95 | str : in string := ""; 96 | level : in severity_level := failure) is 97 | begin 98 | if (str'length = 0) then 99 | assert a = b 100 | report to_string(a) & " should be equal to " & integer'image(b) 101 | severity level; 102 | else 103 | assert a = b 104 | report str 105 | severity level; 106 | end if; 107 | end procedure assert_equal; 108 | 109 | 110 | procedure assert_equal ( a, b : in std_logic_vector; 111 | str : in string := ""; 112 | level : in severity_level := failure) is 113 | begin 114 | if (str'length = 0) then 115 | assert a = b 116 | report "FAILURE: 0x" & to_hstring(a) & " should be equal to 0x" & to_hstring(b) 117 | severity level; 118 | else 119 | assert a = b 120 | report str 121 | severity level; 122 | end if; 123 | end procedure assert_equal; 124 | 125 | 126 | procedure assert_equal ( a,b : in string; 127 | str : in string := ""; 128 | level : in severity_level := failure) is 129 | begin 130 | if (str'length = 0) then 131 | assert a = b 132 | report "FAILURE: " & a & " should be equal to " & b 133 | severity level; 134 | else 135 | assert a = b 136 | report str 137 | severity level; 138 | end if; 139 | end procedure assert_equal; 140 | 141 | 142 | procedure assert_unequal ( a, b : in integer; 143 | str : in string := ""; 144 | level : in severity_level := failure) is 145 | begin 146 | if (str'length = 0) then 147 | assert a /= b 148 | report to_string(a) & " should not be equal to " & integer'image(b) 149 | severity level; 150 | else 151 | assert a /= b 152 | report str 153 | severity level; 154 | end if; 155 | end procedure assert_unequal; 156 | 157 | 158 | procedure assert_unequal ( a, b : in std_logic_vector; 159 | str : in string := ""; 160 | level : in severity_level := failure) is 161 | begin 162 | if (str'length = 0) then 163 | assert a /= b 164 | report "FAILURE: " & to_hstring(a) & " should not be equal to " & to_hstring(b) 165 | severity level; 166 | else 167 | assert a /= b 168 | report str 169 | severity level; 170 | end if; 171 | end procedure assert_unequal; 172 | 173 | 174 | procedure assert_unequal ( a,b : in string; 175 | str : in string := ""; 176 | level : in severity_level := failure) is 177 | begin 178 | if (str'length = 0) then 179 | assert a /= b 180 | report "FAILURE: " & a & " should not be equal to " & b 181 | severity level; 182 | else 183 | assert a /= b 184 | report str 185 | severity level; 186 | end if; 187 | end procedure assert_unequal; 188 | 189 | 190 | end package body AssertP; 191 | -------------------------------------------------------------------------------- /sim/DictP.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | 20 | 21 | 22 | package DictP is 23 | 24 | 25 | generic ( 26 | type KEY_TYPE; 27 | type VALUE_TYPE; 28 | function key_to_string(d : in KEY_TYPE) return string; 29 | function value_to_string(d : in VALUE_TYPE) return string 30 | ); 31 | 32 | 33 | 34 | type t_dict_key_ptr is access KEY_TYPE; 35 | type t_dict_data_ptr is access VALUE_TYPE; 36 | 37 | type t_dict_dir is (UP, DOWN); 38 | type t_dict_iter is (TAIL, HEAD); 39 | 40 | type t_dict is protected 41 | 42 | procedure set (constant key : in KEY_TYPE; constant data : in VALUE_TYPE); 43 | procedure get (constant key : in KEY_TYPE; data : out VALUE_TYPE); 44 | procedure del (constant key : in KEY_TYPE); 45 | procedure init (constant logging : in boolean := false); 46 | procedure clear; 47 | impure function hasKey (constant key : KEY_TYPE) return boolean; 48 | impure function size return natural; 49 | procedure setIter(constant start : in t_dict_iter := TAIL); 50 | impure function iter (constant dir : t_dict_dir := UP) return KEY_TYPE; 51 | impure function get (constant key : KEY_TYPE) return VALUE_TYPE; 52 | 53 | end protected t_dict; 54 | 55 | procedure merge(d0 : inout t_dict; d1 : inout t_dict; d : inout t_dict); 56 | 57 | 58 | end package DictP; 59 | 60 | 61 | 62 | package body DictP is 63 | 64 | 65 | type t_dict is protected body 66 | 67 | 68 | type t_entry; 69 | type t_entry_ptr is access t_entry; 70 | 71 | type t_entry is record 72 | key : t_dict_key_ptr; 73 | data : t_dict_data_ptr; 74 | last_entry : t_entry_ptr; 75 | next_entry : t_entry_ptr; 76 | end record t_entry; 77 | 78 | variable v_tail : t_entry_ptr := null; 79 | variable v_head : t_entry_ptr := null; 80 | variable v_iterator : t_entry_ptr := null; 81 | variable v_size : natural := 0; 82 | variable v_logging : boolean := false; 83 | 84 | impure function find (constant key : KEY_TYPE) return t_entry_ptr; 85 | 86 | procedure set (constant key : in KEY_TYPE; constant data : in VALUE_TYPE) is 87 | variable v_entry : t_entry_ptr := find(key); 88 | begin 89 | if (v_entry = null) then 90 | if (v_head /= null) then 91 | v_entry := new t_entry; 92 | v_entry.key := new KEY_TYPE'(key); 93 | v_entry.data := new VALUE_TYPE'(data); 94 | v_entry.last_entry := v_head; 95 | v_entry.next_entry := null; 96 | v_head := v_entry; 97 | v_head.last_entry.next_entry := v_head; 98 | else 99 | v_head := new t_entry; 100 | v_head.key := new KEY_TYPE'(key); 101 | v_head.data := new VALUE_TYPE'(data); 102 | v_head.last_entry := null; 103 | v_head.next_entry := null; 104 | v_tail := v_head; 105 | end if; 106 | if (v_logging) then 107 | report t_dict'instance_name & ": Add key " & key_to_string(key) & " with value " & value_to_string(data) & " to dictionary"; 108 | end if; 109 | v_size := v_size + 1; 110 | else 111 | v_entry.data.all := data; 112 | if (v_logging) then 113 | report t_dict'instance_name & ": Set value of key " & key_to_string(key) & " to 0x" & value_to_string(data); 114 | end if; 115 | end if; 116 | end procedure set; 117 | 118 | procedure get (constant key : in KEY_TYPE; data : out VALUE_TYPE) is 119 | variable v_entry : t_entry_ptr := find(key); 120 | begin 121 | assert v_entry /= null 122 | report t_dict'instance_name & ": key " & key_to_string(key) & " not found" 123 | severity failure; 124 | if(v_entry /= null) then 125 | data := v_entry.data.all; 126 | if v_logging then 127 | report t_dict'instance_name & ": Got key " & key_to_string(key) & " with value " & value_to_string(v_entry.data.all); 128 | end if; 129 | end if; 130 | end procedure get; 131 | 132 | procedure del (constant key : in KEY_TYPE) is 133 | variable v_entry : t_entry_ptr := find(key); 134 | begin 135 | assert v_entry /= null 136 | report t_dict'instance_name & ": key " & key_to_string(key) & " not found" 137 | severity failure; 138 | if (v_entry /= null) then 139 | -- remove head entry 140 | if(v_entry.next_entry = null and v_entry.last_entry /= null) then 141 | v_entry.last_entry.next_entry := null; 142 | v_head := v_entry.last_entry; 143 | -- remove start entry 144 | elsif(v_entry.next_entry /= null and v_entry.last_entry = null) then 145 | v_entry.next_entry.last_entry := null; 146 | --v_entry.next_entry.last_entry := v_entry.last_entry; 147 | v_tail := v_entry.next_entry; 148 | -- remove from between 149 | elsif(v_entry.next_entry /= null and v_entry.last_entry /= null) then 150 | v_entry.last_entry.next_entry := v_entry.next_entry; 151 | v_entry.next_entry.last_entry := v_entry.last_entry; 152 | end if; 153 | deallocate(v_entry.key); 154 | deallocate(v_entry.data); 155 | deallocate(v_entry); 156 | v_size := v_size - 1; 157 | end if; 158 | end procedure del; 159 | 160 | impure function find (constant key : KEY_TYPE) return t_entry_ptr is 161 | variable v_entry : t_entry_ptr := v_head; 162 | begin 163 | while (v_entry /= null) loop 164 | if(v_entry.key.all = key) then 165 | return v_entry; 166 | end if; 167 | v_entry := v_entry.last_entry; 168 | end loop; 169 | return null; 170 | end function find; 171 | 172 | procedure clear is 173 | variable v_entry : t_entry_ptr := v_head; 174 | variable v_entry_d : t_entry_ptr; 175 | begin 176 | while (v_entry /= null) loop 177 | v_entry_d := v_entry; 178 | del(v_entry_d.key.all); 179 | v_entry := v_entry.last_entry; 180 | end loop; 181 | end procedure clear; 182 | 183 | impure function hasKey (constant key : KEY_TYPE) return boolean is 184 | begin 185 | return find(key) /= null; 186 | end function hasKey; 187 | 188 | impure function size return natural is 189 | begin 190 | return v_size; 191 | end function size; 192 | 193 | procedure init (constant logging : in boolean := false) is 194 | begin 195 | v_logging := logging; 196 | end procedure init; 197 | 198 | procedure setIter (constant start : in t_dict_iter := TAIL) is 199 | begin 200 | if (start = TAIL) then 201 | v_iterator := v_tail; 202 | else 203 | v_iterator := v_head; 204 | end if; 205 | end procedure setIter; 206 | 207 | impure function iter (constant dir : t_dict_dir := UP) return KEY_TYPE is 208 | variable v_key : t_dict_key_ptr := null; 209 | begin 210 | v_key := new KEY_TYPE'(v_iterator.key.all); 211 | if (dir = UP) then 212 | if (v_iterator.next_entry /= null) then 213 | v_iterator := v_iterator.next_entry; 214 | end if; 215 | else 216 | if (v_iterator.last_entry /= null) then 217 | v_iterator := v_iterator.last_entry; 218 | end if; 219 | end if; 220 | return v_key.all; 221 | end function iter; 222 | 223 | impure function get(constant key : KEY_TYPE) return VALUE_TYPE is 224 | variable v_entry : t_entry_ptr := find(key); 225 | begin 226 | assert v_entry /= null 227 | report t_dict'instance_name & ": key " & key_to_string(key) & " not found" 228 | severity failure; 229 | return v_entry.data.all; 230 | end function get; 231 | 232 | 233 | end protected body t_dict; 234 | 235 | 236 | procedure merge(d0 : inout t_dict; d1 : inout t_dict; d : inout t_dict) is 237 | variable v_key : t_dict_key_ptr; 238 | variable v_data : t_dict_data_ptr; 239 | begin 240 | if (d0.size > 0) then 241 | d0.setIter(TAIL); 242 | for i in 0 to d0.size-1 loop 243 | v_key := new KEY_TYPE'(d0.iter(UP)); 244 | v_data := new VALUE_TYPE'(d0.get(v_key.all)); 245 | d.set(v_key.all, v_data.all); 246 | end loop; 247 | end if; 248 | if (d1.size > 0) then 249 | d1.setIter(TAIL); 250 | for i in 0 to d1.size-1 loop 251 | v_key := new KEY_TYPE'(d1.iter(UP)); 252 | v_data := new VALUE_TYPE'(d1.get(v_key.all)); 253 | d.set(v_key.all, v_data.all); 254 | end loop; 255 | end if; 256 | end procedure merge; 257 | 258 | 259 | end package body DictP; 260 | -------------------------------------------------------------------------------- /sim/QueueP.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | 20 | 21 | 22 | package QueueP is 23 | 24 | 25 | generic ( 26 | type QUEUE_TYPE; 27 | MAX_LEN : natural := 64; 28 | function to_string(d : in QUEUE_TYPE) return string 29 | ); 30 | 31 | 32 | -- simple queue interface 33 | type t_simple_queue is protected 34 | 35 | procedure push (data : in QUEUE_TYPE); 36 | procedure pop (data : out QUEUE_TYPE); 37 | procedure init (logging : in boolean := false); 38 | impure function is_empty return boolean; 39 | impure function is_full return boolean; 40 | impure function fillstate return natural; 41 | 42 | end protected t_simple_queue; 43 | 44 | 45 | -- linked list queue interface 46 | type t_list_queue is protected 47 | 48 | procedure push (data : in QUEUE_TYPE); 49 | procedure pop (data : out QUEUE_TYPE); 50 | procedure init (logging : in boolean := false); 51 | impure function is_empty return boolean; 52 | impure function is_full return boolean; 53 | impure function fillstate return natural; 54 | 55 | end protected t_list_queue; 56 | 57 | 58 | end package QueueP; 59 | 60 | 61 | 62 | package body QueueP is 63 | 64 | 65 | -- simple queue implementation 66 | -- inspired by noasic article http://noasic.com/blog/a-simple-fifo-using-vhdl-protected-types/ 67 | type t_simple_queue is protected body 68 | 69 | type t_queue_array is array (0 to MAX_LEN-1) of QUEUE_TYPE; 70 | 71 | variable v_queue : t_queue_array; 72 | variable v_count : natural range 0 to t_queue_array'length := 0; 73 | variable v_head : natural range 0 to t_queue_array'high := 0; 74 | variable v_tail : natural range 0 to t_queue_array'high := 0; 75 | variable v_logging : boolean := false; 76 | 77 | -- write one entry into queue 78 | procedure push (data : in QUEUE_TYPE) is 79 | begin 80 | assert not(is_full) 81 | report "push into full queue -> discarded" 82 | severity failure; 83 | v_queue(v_head) := data; 84 | v_head := (v_head + 1) mod t_queue_array'length; 85 | v_count := v_count + 1; 86 | if v_logging then 87 | report t_simple_queue'instance_name & " pushed 0x" & to_string(data) & " into queue"; 88 | end if; 89 | end procedure push; 90 | 91 | -- read one entry from queue 92 | procedure pop (data : out QUEUE_TYPE) is 93 | variable v_data : QUEUE_TYPE; 94 | begin 95 | assert not(is_empty) 96 | report "pop from empty queue -> discarded" 97 | severity failure; 98 | v_data := v_queue(v_tail); 99 | v_tail := (v_tail + 1) mod t_queue_array'length; 100 | v_count := v_count - 1; 101 | if v_logging then 102 | report t_simple_queue'instance_name & " popped 0x" & to_string(v_data) & " from queue"; 103 | end if; 104 | data := v_data; 105 | end procedure pop; 106 | 107 | -- returns true if queue is empty, false otherwise 108 | impure function is_empty return boolean is 109 | begin 110 | return v_count = 0; 111 | end function is_empty; 112 | 113 | -- returns true if queue is full, false otherwise 114 | impure function is_full return boolean is 115 | begin 116 | return v_count = t_queue_array'length; 117 | end function is_full; 118 | 119 | -- returns number of filled slots in queue 120 | impure function fillstate return natural is 121 | begin 122 | return v_count; 123 | end function fillstate; 124 | 125 | -- returns number of free slots in queue 126 | impure function freeslots return natural is 127 | begin 128 | return t_queue_array'length - v_count; 129 | end function freeslots; 130 | 131 | procedure init (logging : in boolean := false) is 132 | begin 133 | v_logging := logging; 134 | end procedure init; 135 | 136 | 137 | end protected body t_simple_queue; 138 | 139 | 140 | -- linked liste queue implementation 141 | type t_list_queue is protected body 142 | 143 | 144 | type t_entry; 145 | type t_entry_ptr is access t_entry; 146 | 147 | type t_data_ptr is access QUEUE_TYPE; 148 | 149 | type t_entry is record 150 | data : t_data_ptr; 151 | last_entry : t_entry_ptr; 152 | next_entry : t_entry_ptr; 153 | end record t_entry; 154 | 155 | variable v_head : t_entry_ptr; 156 | variable v_tail : t_entry_ptr; 157 | variable v_count : natural := 0; 158 | variable v_logging : boolean := false; 159 | 160 | -- write one entry into queue by 161 | -- creating new entry at head of list 162 | procedure push (data : in QUEUE_TYPE) is 163 | variable v_entry : t_entry_ptr; 164 | begin 165 | if (not(is_full)) then 166 | if (v_count /= 0) then 167 | v_entry := new t_entry; 168 | v_entry.data := new QUEUE_TYPE'(data); 169 | v_entry.last_entry := v_head; 170 | v_entry.next_entry := null; 171 | v_head := v_entry; 172 | v_head.last_entry.next_entry := v_head; 173 | else 174 | v_head := new t_entry; 175 | v_head.data := new QUEUE_TYPE'(data); 176 | v_head.last_entry := null; 177 | v_head.next_entry := null; 178 | v_tail := v_head; 179 | end if; 180 | v_count := v_count + 1; 181 | if v_logging then 182 | report t_list_queue'instance_name & " pushed 0x" & to_string(data) & " into queue"; 183 | end if; 184 | else 185 | assert false 186 | report "Push to full queue -> discared" 187 | severity warning; 188 | end if; 189 | end procedure push; 190 | 191 | -- read one entry from queue at tail of list and 192 | -- delete that entry from list after read 193 | procedure pop (data : out QUEUE_TYPE) is 194 | variable v_entry : t_entry_ptr := v_tail; 195 | variable v_data : QUEUE_TYPE; 196 | begin 197 | assert not(is_empty) 198 | report "pop from empty queue -> discarded" 199 | severity failure; 200 | v_data := v_tail.data.all; 201 | v_tail := v_tail.next_entry; 202 | deallocate(v_entry.data); 203 | deallocate(v_entry); 204 | v_count := v_count - 1; 205 | if v_logging then 206 | report t_list_queue'instance_name & " popped 0x" & to_string(v_data) & " from queue"; 207 | end if; 208 | data := v_data; 209 | end procedure pop; 210 | 211 | procedure init (logging : in boolean := false) is 212 | begin 213 | v_logging := logging; 214 | end procedure init; 215 | 216 | -- returns true if queue is full, false otherwise 217 | impure function is_full return boolean is 218 | begin 219 | return v_count = MAX_LEN; 220 | end function is_full; 221 | 222 | -- returns true if queue is empty, false otherwise 223 | impure function is_empty return boolean is 224 | begin 225 | return v_tail = null; 226 | end function is_empty; 227 | 228 | -- returns number of filled slots in queue 229 | impure function fillstate return natural is 230 | begin 231 | return v_count; 232 | end function fillstate; 233 | 234 | end protected body t_list_queue; 235 | 236 | 237 | end package body QueueP; 238 | -------------------------------------------------------------------------------- /sim/SimP.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | 20 | --+ including vhdl 2008 libraries 21 | --+ These lines can be commented out when using 22 | --+ a simulator with built-in VHDL 2008 support 23 | --library ieee_proposed; 24 | -- use ieee_proposed.standard_additions.all; 25 | -- use ieee_proposed.std_logic_1164_additions.all; 26 | 27 | library libvhdl; 28 | use libvhdl.AssertP.all; 29 | 30 | 31 | 32 | 33 | package SimP is 34 | 35 | 36 | procedure wait_cycles (signal clk : in std_logic; n : in natural); 37 | 38 | procedure spi_master ( data_in : in std_logic_vector; data_out : out std_logic_vector; 39 | signal sclk : inout std_logic; signal ste : out std_logic; 40 | signal mosi : out std_logic; signal miso : in std_logic; 41 | dir : in natural range 0 to 1; cpol : in natural range 0 to 1; 42 | cpha : in natural range 0 to 1; period : in time); 43 | 44 | procedure spi_slave ( data_in : in std_logic_vector; data_out : out std_logic_vector; 45 | signal sclk : in std_logic; signal ste : in std_logic; 46 | signal mosi : in std_logic; signal miso : out std_logic; 47 | dir : in natural range 0 to 1; cpol : in natural range 0 to 1; 48 | cpha : in natural range 0 to 1); 49 | 50 | 51 | end package SimP; 52 | 53 | 54 | 55 | package body SimP is 56 | 57 | 58 | -- wait for n rising edges on clk 59 | procedure wait_cycles (signal clk : in std_logic; n : in natural) is 60 | begin 61 | for i in 1 to n loop 62 | wait until rising_edge(clk); 63 | end loop; 64 | end procedure wait_cycles; 65 | 66 | 67 | -- configurable spi master which supports all combinations of cpol & cpha 68 | procedure spi_master ( data_in : in std_logic_vector; data_out : out std_logic_vector; 69 | signal sclk : inout std_logic; signal ste : out std_logic; 70 | signal mosi : out std_logic; signal miso : in std_logic; 71 | dir : in natural range 0 to 1; cpol : in natural range 0 to 1; 72 | cpha : in natural range 0 to 1; period : in time) is 73 | begin 74 | assert_equal(data_in'length, data_out'length, spi_master'simple_name & ": data_in & data_out must have same length!"); 75 | sclk <= std_logic'val(cpol+2); 76 | ste <= '0'; 77 | if (cpha = 0) then 78 | for i in data_in'range loop 79 | if (dir = 0) then 80 | mosi <= data_in(data_in'high - i); 81 | else 82 | mosi <= data_in(i); 83 | end if; 84 | wait for period/2; 85 | sclk <= not(sclk); 86 | if (dir = 0) then 87 | data_out(data_out'high - i) := miso; 88 | else 89 | data_out(i) := miso; 90 | end if; 91 | wait for period/2; 92 | sclk <= not(sclk); 93 | end loop; 94 | wait for period/2; 95 | else 96 | mosi <= '1'; 97 | wait for period/2; 98 | for i in data_in'range loop 99 | sclk <= not(sclk); 100 | if (dir = 0) then 101 | mosi <= data_in(data_in'high - i); 102 | else 103 | mosi <= data_in(i); 104 | end if; 105 | wait for period/2; 106 | sclk <= not(sclk); 107 | if (dir = 0) then 108 | data_out(data_out'high - i) := miso; 109 | else 110 | data_out(i) := miso; 111 | end if; 112 | wait for period/2; 113 | end loop; 114 | end if; 115 | ste <= '1'; 116 | mosi <= '1'; 117 | wait for period/2; 118 | end procedure spi_master; 119 | 120 | 121 | -- configurable spi slave which supports all combinations of cpol & cpha 122 | procedure spi_slave ( data_in : in std_logic_vector; data_out : out std_logic_vector; 123 | signal sclk : in std_logic; signal ste : in std_logic; 124 | signal mosi : in std_logic; signal miso : out std_logic; 125 | dir : in natural range 0 to 1; cpol : in natural range 0 to 1; 126 | cpha : in natural range 0 to 1) is 127 | variable v_cpol : std_logic := std_logic'val(cpol+2); 128 | begin 129 | assert_equal(data_in'length, data_out'length, spi_slave'simple_name & ": data_in & data_out must have same length!"); 130 | miso <= 'Z'; 131 | wait until ste = '0'; 132 | if (cpha = 0) then 133 | for i in data_in'range loop 134 | if (dir = 0) then 135 | miso <= data_in(data_in'high - i); 136 | else 137 | miso <= data_in(i); 138 | end if; 139 | wait until sclk'event and sclk = not(v_cpol); 140 | if (dir = 0) then 141 | data_out(data_out'high - i) := mosi; 142 | else 143 | data_out(i) := mosi; 144 | end if; 145 | wait until sclk'event and sclk = v_cpol; 146 | end loop; 147 | else 148 | for i in data_in'range loop 149 | wait until sclk'event and sclk = not(v_cpol); 150 | if (dir = 0) then 151 | miso <= data_in(data_in'high - i); 152 | else 153 | miso <= data_in(i); 154 | end if; 155 | wait until sclk'event and sclk = v_cpol; 156 | if (dir = 0) then 157 | data_out(data_out'high - i) := mosi; 158 | else 159 | data_out(i) := mosi; 160 | end if; 161 | end loop; 162 | end if; 163 | wait until ste = '1'; 164 | miso <= 'Z'; 165 | end procedure spi_slave; 166 | 167 | 168 | end package body SimP; 169 | -------------------------------------------------------------------------------- /sim/StackP.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | 20 | 21 | 22 | package StackP is 23 | 24 | 25 | generic ( 26 | type STACK_TYPE; 27 | MAX_LEN : natural := 64; 28 | function to_string(d : in STACK_TYPE) return string 29 | ); 30 | 31 | -- linked list stack interface 32 | type t_stack is protected 33 | 34 | procedure push (data : in STACK_TYPE); 35 | procedure pop (data : inout STACK_TYPE); 36 | procedure init (logging : in boolean := false); 37 | impure function is_empty return boolean; 38 | impure function is_full return boolean; 39 | impure function fillstate return natural; 40 | 41 | end protected t_stack; 42 | 43 | 44 | end package StackP; 45 | 46 | 47 | 48 | package body StackP is 49 | 50 | 51 | -- linked list stack implementation 52 | type t_stack is protected body 53 | 54 | type t_entry; 55 | type t_entry_ptr is access t_entry; 56 | 57 | type t_data_ptr is access STACK_TYPE; 58 | 59 | type t_entry is record 60 | data : t_data_ptr; 61 | last_entry : t_entry_ptr; 62 | next_entry : t_entry_ptr; 63 | end record t_entry; 64 | 65 | variable v_head : t_entry_ptr := null; 66 | variable v_tail : t_entry_ptr := null; 67 | variable v_count : natural := 0; 68 | variable v_logging : boolean := false; 69 | 70 | -- write one entry into queue by 71 | -- creating new entry at head of list 72 | procedure push (data : in STACK_TYPE) is 73 | variable v_entry : t_entry_ptr; 74 | begin 75 | if (not(is_full)) then 76 | if (v_count /= 0) then 77 | v_entry := new t_entry; 78 | v_entry.data := new STACK_TYPE'(data); 79 | v_entry.last_entry := v_head; 80 | v_entry.next_entry := null; 81 | v_head := v_entry; 82 | v_head.last_entry.next_entry := v_head; 83 | else 84 | v_head := new t_entry; 85 | v_head.data := new STACK_TYPE'(data); 86 | v_head.last_entry := null; 87 | v_head.next_entry := null; 88 | v_tail := v_head; 89 | end if; 90 | v_count := v_count + 1; 91 | if v_logging then 92 | report t_stack'instance_name & " pushed 0x" & to_string(data) & " on stack"; 93 | end if; 94 | else 95 | assert false 96 | report t_stack'instance_name & " push to full stack -> discared" 97 | severity warning; 98 | end if; 99 | end procedure push; 100 | 101 | -- read one entry from queue at tail of list and 102 | -- delete that entry from list after read 103 | procedure pop (data : inout STACK_TYPE) is 104 | variable v_entry : t_entry_ptr := v_head; 105 | begin 106 | assert not(is_empty) 107 | report "pop from empty queue -> discarded" 108 | severity failure; 109 | data := v_head.data.all; 110 | v_head := v_head.last_entry; 111 | deallocate(v_entry.data); 112 | deallocate(v_entry); 113 | v_count := v_count - 1; 114 | if v_logging then 115 | report t_stack'instance_name & " popped 0x" & to_string(data) & " from stack"; 116 | end if; 117 | end procedure pop; 118 | 119 | procedure init (logging : in boolean := false) is 120 | begin 121 | v_logging := logging; 122 | end procedure init; 123 | 124 | -- returns true if queue is empty, false otherwise 125 | impure function is_empty return boolean is 126 | begin 127 | return v_head = null; 128 | end function is_empty; 129 | 130 | -- returns true if queue is full, false otherwise 131 | impure function is_full return boolean is 132 | begin 133 | return v_count = MAX_LEN; 134 | end function is_full; 135 | 136 | -- returns number of filled slots in queue 137 | impure function fillstate return natural is 138 | begin 139 | return v_count; 140 | end function fillstate; 141 | 142 | end protected body t_stack; 143 | 144 | 145 | end package body StackP; 146 | -------------------------------------------------------------------------------- /syn/SpiMasterE.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | 22 | 23 | entity SpiMasterE is 24 | generic ( 25 | G_DATA_WIDTH : positive := 8; --* data bus width 26 | G_DATA_DIR : natural range 0 to 1 := 0; --* start from lsb/msb 0/1 27 | G_SPI_CPOL : natural range 0 to 1 := 0; --* SPI clock polarity 28 | G_SPI_CPHA : natural range 0 to 1 := 0; --* SPI clock phase 29 | G_SCLK_DIVIDER : positive range 6 to positive'high := 10 --* SCLK divider related to system clock 30 | ); 31 | port ( 32 | --+ system if 33 | Reset_n_i : in std_logic; 34 | Clk_i : in std_logic; 35 | --+ SPI slave if 36 | SpiSclk_o : out std_logic; 37 | SpiSte_o : out std_logic; 38 | SpiMosi_o : out std_logic; 39 | SpiMiso_i : in std_logic; 40 | --+ local VAI if 41 | Data_i : in std_logic_vector(G_DATA_WIDTH-1 downto 0); 42 | DataValid_i : in std_logic; 43 | DataAccept_o : out std_logic; 44 | Data_o : out std_logic_vector(G_DATA_WIDTH-1 downto 0); 45 | DataValid_o : out std_logic; 46 | DataAccept_i : in std_logic 47 | ); 48 | end entity SpiMasterE; 49 | 50 | 51 | 52 | architecture rtl of SpiMasterE is 53 | 54 | 55 | type t_spi_state is (IDLE, WRITE, READ, CYCLE, STORE, SET_STE); 56 | signal s_spi_state : t_spi_state; 57 | 58 | signal s_send_register : std_logic_vector(G_DATA_WIDTH-1 downto 0); 59 | signal s_recv_register : std_logic_vector(G_DATA_WIDTH-1 downto 0); 60 | 61 | signal s_miso_d : std_logic_vector(1 downto 0); 62 | 63 | signal s_mosi : std_logic; 64 | signal s_sclk : std_logic; 65 | signal s_ste : std_logic; 66 | 67 | signal s_data_valid : std_logic; 68 | signal s_data_accept : std_logic; 69 | signal s_transfer_valid : boolean; 70 | 71 | signal s_sclk_rising : boolean; 72 | signal s_sclk_falling : boolean; 73 | signal s_read_edge : boolean; 74 | signal s_write_edge : boolean; 75 | 76 | alias a_miso : std_logic is s_miso_d(s_miso_d'left); 77 | 78 | constant C_BIT_COUNTER_START : natural := (G_DATA_WIDTH-1) * G_DATA_DIR; 79 | constant C_BIT_COUNTER_END : natural := (G_DATA_WIDTH-1) * to_integer(not(to_unsigned(G_DATA_DIR, 1))); 80 | 81 | 82 | begin 83 | 84 | 85 | --* Sync asynchronous SPI inputs with 2 stage FF line 86 | SpiSyncP : process (Reset_n_i, Clk_i) is 87 | begin 88 | if (Reset_n_i = '0') then 89 | s_miso_d <= (others => '0'); 90 | elsif rising_edge(Clk_i) then 91 | s_miso_d <= s_miso_d(0) & SpiMiso_i; 92 | end if; 93 | end process SpiSyncP; 94 | 95 | 96 | --* Save local data input when new data is provided and 97 | --* we're not inside a running SPI transmission 98 | SendRegisterP : process (Reset_n_i, Clk_i) is 99 | begin 100 | if (Reset_n_i = '0') then 101 | s_send_register <= (others => '0'); 102 | s_data_accept <= '0'; 103 | elsif rising_edge(Clk_i) then 104 | s_data_accept <= '0'; 105 | if (DataValid_i = '1' and s_spi_state = IDLE) then 106 | s_send_register <= Data_i; 107 | s_data_accept <= '1'; 108 | end if; 109 | end if; 110 | end process SendRegisterP; 111 | 112 | 113 | --* Spi master control FSM 114 | SpiControlP : process (Reset_n_i, Clk_i) is 115 | variable v_bit_counter : natural range 0 to G_DATA_WIDTH-1; 116 | variable v_sclk_counter : natural range 0 to G_SCLK_DIVIDER-1; 117 | begin 118 | if (Reset_n_i = '0') then 119 | s_recv_register <= (others => '0'); 120 | v_bit_counter := C_BIT_COUNTER_START; 121 | v_sclk_counter := G_SCLK_DIVIDER-1; 122 | s_transfer_valid <= false; 123 | s_sclk <= std_logic'val(G_SPI_CPOL+2); 124 | s_mosi <= '1'; 125 | s_spi_state <= IDLE; 126 | elsif rising_edge(Clk_i) then 127 | case s_spi_state is 128 | 129 | when IDLE => 130 | s_sclk <= std_logic'val(G_SPI_CPOL+2); 131 | s_mosi <= '1'; 132 | s_recv_register <= (others => '0'); 133 | v_bit_counter := C_BIT_COUNTER_START; 134 | v_sclk_counter := G_SCLK_DIVIDER/2-1; 135 | s_transfer_valid <= false; 136 | if(DataValid_i = '1' and s_data_accept = '1') then 137 | s_spi_state <= WRITE; 138 | end if; 139 | 140 | when WRITE => 141 | if (G_SPI_CPHA = 0 and v_bit_counter = C_BIT_COUNTER_START) then 142 | s_mosi <= s_send_register(v_bit_counter); 143 | s_spi_state <= READ; 144 | else 145 | if (v_sclk_counter = 0) then 146 | v_sclk_counter := G_SCLK_DIVIDER/2-1; 147 | s_sclk <= not(s_sclk); 148 | s_mosi <= s_send_register(v_bit_counter); 149 | s_spi_state <= READ; 150 | else 151 | v_sclk_counter := v_sclk_counter - 1; 152 | end if; 153 | end if; 154 | 155 | when READ => 156 | if (v_sclk_counter = 0) then 157 | s_sclk <= not(s_sclk); 158 | s_recv_register(v_bit_counter) <= a_miso; 159 | v_sclk_counter := G_SCLK_DIVIDER/2-1; 160 | if (v_bit_counter = C_BIT_COUNTER_END) then 161 | if (G_SPI_CPHA = 0) then 162 | s_spi_state <= CYCLE; 163 | else 164 | s_spi_state <= STORE; 165 | end if; 166 | else 167 | if (G_DATA_DIR = 0) then 168 | v_bit_counter := v_bit_counter + 1; 169 | else 170 | v_bit_counter := v_bit_counter - 1; 171 | end if; 172 | s_spi_state <= WRITE; 173 | end if; 174 | else 175 | v_sclk_counter := v_sclk_counter - 1; 176 | end if; 177 | 178 | when CYCLE => 179 | if (v_sclk_counter = 0) then 180 | s_sclk <= not(s_sclk); 181 | v_sclk_counter := G_SCLK_DIVIDER/2-1; 182 | s_spi_state <= STORE; 183 | else 184 | v_sclk_counter := v_sclk_counter - 1; 185 | end if; 186 | 187 | when STORE => 188 | if (v_sclk_counter = 0) then 189 | s_transfer_valid <= true; 190 | v_sclk_counter := G_SCLK_DIVIDER/2-1; 191 | s_spi_state <= SET_STE; 192 | else 193 | v_sclk_counter := v_sclk_counter - 1; 194 | end if; 195 | 196 | when SET_STE => 197 | s_transfer_valid <= false; 198 | if (v_sclk_counter = 0) then 199 | s_spi_state <= IDLE; 200 | else 201 | v_sclk_counter := v_sclk_counter - 1; 202 | end if; 203 | 204 | when others => 205 | s_spi_state <= IDLE; 206 | 207 | end case; 208 | end if; 209 | end process SpiControlP; 210 | 211 | 212 | 213 | --* Provide received SPI data to local interface 214 | --* Output data is overwritten if it isn't fetched 215 | --* until next finished SPI transmission 216 | RecvRegisterP : process (Reset_n_i, Clk_i) is 217 | begin 218 | if (Reset_n_i = '0') then 219 | Data_o <= (others => '0'); 220 | s_data_valid <= '0'; 221 | elsif rising_edge(Clk_i) then 222 | if (s_transfer_valid) then 223 | Data_o <= s_recv_register; 224 | s_data_valid <= '1'; 225 | end if; 226 | if (DataAccept_i = '1' and s_data_valid = '1') then 227 | s_data_valid <= '0'; 228 | end if; 229 | end if; 230 | end process RecvRegisterP; 231 | 232 | 233 | --+ internal signals 234 | s_ste <= '1' when s_spi_state = IDLE or s_spi_state = SET_STE else '0'; 235 | 236 | --+ Output port connections 237 | DataValid_o <= s_data_valid; 238 | DataAccept_o <= s_data_accept; 239 | SpiSte_o <= s_ste; 240 | SpiSclk_o <= s_sclk; 241 | SpiMosi_o <= s_mosi when s_ste = '0' else '1'; 242 | 243 | 244 | assert G_SCLK_DIVIDER rem 2 = 0 245 | report "WARNING: " & SpiMasterE'instance_name & LF & "G_SCLK_DIVIDER " & integer'image(G_SCLK_DIVIDER) & 246 | " rounded down to next even value " & integer'image(G_SCLK_DIVIDER-1) 247 | severity warning; 248 | 249 | 250 | -- psl default clock is rising_edge(Clk_i); 251 | -- 252 | -- psl assert always (s_spi_state = IDLE or s_spi_state = WRITE or s_spi_state = READ or 253 | -- s_spi_state = CYCLE or s_spi_state = SET_STE or s_spi_state = STORE); 254 | -- psl assert always (s_data_valid and DataAccept_i) -> next not(s_data_valid); 255 | 256 | 257 | end architecture rtl; -------------------------------------------------------------------------------- /syn/SpiSlaveE.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | 22 | 23 | entity SpiSlaveE is 24 | generic ( 25 | G_DATA_WIDTH : positive := 8; --* data bus width 26 | G_DATA_DIR : natural range 0 to 1 := 0; --* start from lsb/msb 0/1 27 | G_SPI_CPOL : natural range 0 to 1 := 0; --* SPI clock polarity 28 | G_SPI_CPHA : natural range 0 to 1 := 0 --* SPI clock phase 29 | ); 30 | port ( 31 | --+ system if 32 | Reset_n_i : in std_logic; 33 | Clk_i : in std_logic; 34 | --+ SPI slave if 35 | SpiSclk_i : in std_logic; 36 | SpiSte_i : in std_logic; 37 | SpiMosi_i : in std_logic; 38 | SpiMiso_o : out std_logic; 39 | --+ local VAI if 40 | Data_i : in std_logic_vector(G_DATA_WIDTH-1 downto 0); 41 | DataValid_i : in std_logic; 42 | DataAccept_o : out std_logic; 43 | Data_o : out std_logic_vector(G_DATA_WIDTH-1 downto 0); 44 | DataValid_o : out std_logic; 45 | DataAccept_i : in std_logic 46 | ); 47 | end entity SpiSlaveE; 48 | 49 | 50 | 51 | architecture rtl of SpiSlaveE is 52 | 53 | 54 | type t_spi_state is (IDLE, TRANSFER, STORE); 55 | signal s_spi_state : t_spi_state; 56 | 57 | signal s_send_register : std_logic_vector(G_DATA_WIDTH-1 downto 0); 58 | signal s_recv_register : std_logic_vector(G_DATA_WIDTH-1 downto 0); 59 | 60 | signal s_sclk_d : std_logic_vector(2 downto 0); 61 | signal s_ste_d : std_logic_vector(2 downto 0); 62 | signal s_mosi_d : std_logic_vector(2 downto 0); 63 | 64 | signal s_miso : std_logic; 65 | 66 | signal s_data_valid : std_logic; 67 | signal s_transfer_valid : boolean; 68 | 69 | signal s_sclk_rising : boolean; 70 | signal s_sclk_falling : boolean; 71 | signal s_read_edge : boolean; 72 | signal s_write_edge : boolean; 73 | 74 | alias a_ste : std_logic is s_ste_d(s_ste_d'left); 75 | alias a_mosi : std_logic is s_mosi_d(s_mosi_d'left); 76 | 77 | constant C_BIT_COUNTER_START : natural := (G_DATA_WIDTH-1) * G_DATA_DIR; 78 | constant C_BIT_COUNTER_END : natural := (G_DATA_WIDTH-1) * to_integer(not(to_unsigned(G_DATA_DIR, 1))); 79 | 80 | 81 | begin 82 | 83 | 84 | --* help signals for edge detection on sclk 85 | s_sclk_rising <= true when s_sclk_d(2 downto 1) = "01" else false; 86 | s_sclk_falling <= true when s_sclk_d(2 downto 1) = "10" else false; 87 | 88 | s_read_edge <= s_sclk_rising when G_SPI_CPOL = G_SPI_CPHA else s_sclk_falling; 89 | s_write_edge <= s_sclk_falling when G_SPI_CPOL = G_SPI_CPHA else s_sclk_rising; 90 | 91 | 92 | --* Sync asynchronous SPI inputs with 3 stage FF line 93 | --* We use 3 FF because of edge detection on sclk line 94 | --* Mosi & ste are also registered with 3 FF to stay in 95 | --* sync with registered sclk 96 | SpiSyncP : process (Reset_n_i, Clk_i) is 97 | begin 98 | if (Reset_n_i = '0') then 99 | if (G_SPI_CPOL = 0) then 100 | s_sclk_d <= (others => '0'); 101 | else 102 | s_sclk_d <= (others => '1'); 103 | end if; 104 | s_ste_d <= (others => '1'); 105 | s_mosi_d <= (others => '0'); 106 | elsif rising_edge(Clk_i) then 107 | s_sclk_d <= s_sclk_d(1 downto 0) & SpiSclk_i; 108 | s_ste_d <= s_ste_d(1 downto 0) & SpiSte_i; 109 | s_mosi_d <= s_mosi_d(1 downto 0) & SpiMosi_i; 110 | end if; 111 | end process SpiSyncP; 112 | 113 | 114 | --* Save local data input when new data is provided and 115 | --* we're not inside a running SPI transmission 116 | --* Beware: Last saved data before a started SPI transmission "wins" 117 | SendRegisterP : process (Reset_n_i, Clk_i) is 118 | begin 119 | if (Reset_n_i = '0') then 120 | s_send_register <= (others => '0'); 121 | DataAccept_o <= '0'; 122 | elsif rising_edge(Clk_i) then 123 | DataAccept_o <= '0'; 124 | if (DataValid_i = '1' and s_spi_state = IDLE) then 125 | s_send_register <= Data_i; 126 | DataAccept_o <= '1'; 127 | end if; 128 | end if; 129 | end process SendRegisterP; 130 | 131 | 132 | --* Spi slave control FSM 133 | SpiControlP : process (Reset_n_i, Clk_i) is 134 | variable v_bit_counter : natural range 0 to G_DATA_WIDTH-1; 135 | begin 136 | if (Reset_n_i = '0') then 137 | s_miso <= '0'; 138 | s_recv_register <= (others => '0'); 139 | v_bit_counter := C_BIT_COUNTER_START; 140 | s_transfer_valid <= false; 141 | s_spi_state <= IDLE; 142 | elsif rising_edge(Clk_i) then 143 | case s_spi_state is 144 | 145 | when IDLE => 146 | s_miso <= '0'; 147 | s_recv_register <= (others => '0'); 148 | v_bit_counter := C_BIT_COUNTER_START; 149 | s_transfer_valid <= false; 150 | if (a_ste = '0') then 151 | if (G_SPI_CPHA = 0) then 152 | s_miso <= s_send_register(v_bit_counter); 153 | end if; 154 | s_spi_state <= TRANSFER; 155 | end if; 156 | 157 | when TRANSFER => 158 | if s_read_edge then 159 | s_recv_register(v_bit_counter) <= a_mosi; 160 | if (v_bit_counter = C_BIT_COUNTER_END) then 161 | s_spi_state <= STORE; 162 | else 163 | if (G_DATA_DIR = 0) then 164 | v_bit_counter := v_bit_counter + 1; 165 | else 166 | v_bit_counter := v_bit_counter - 1; 167 | end if; 168 | end if; 169 | elsif s_write_edge then 170 | s_miso <= s_send_register(v_bit_counter); 171 | else 172 | if (a_ste = '1') then 173 | s_spi_state <= IDLE; 174 | end if; 175 | end if; 176 | 177 | when STORE => 178 | if (a_ste = '1') then 179 | s_transfer_valid <= true; 180 | s_spi_state <= IDLE; 181 | end if; 182 | 183 | when others => 184 | s_spi_state <= IDLE; 185 | 186 | end case; 187 | end if; 188 | end process SpiControlP; 189 | 190 | 191 | --* Provide received SPI data to local interface 192 | --* Output data is overwritten if it isn't fetched 193 | --* until next finished SPI transmission 194 | RecvRegisterP : process (Reset_n_i, Clk_i) is 195 | begin 196 | if (Reset_n_i = '0') then 197 | Data_o <= (others => '0'); 198 | s_data_valid <= '0'; 199 | elsif rising_edge(Clk_i) then 200 | if (s_transfer_valid) then 201 | Data_o <= s_recv_register; 202 | s_data_valid <= '1'; 203 | end if; 204 | if (DataAccept_i = '1' and s_data_valid = '1') then 205 | s_data_valid <= '0'; 206 | end if; 207 | end if; 208 | end process RecvRegisterP; 209 | 210 | 211 | --+ Output port connections 212 | DataValid_o <= s_data_valid; 213 | SpiMiso_o <= 'Z' when SpiSte_i = '1' else s_miso; 214 | 215 | 216 | -- psl default clock is rising_edge(Clk_i); 217 | -- 218 | -- psl assert always (s_spi_state = IDLE or s_spi_state = TRANSFER or s_spi_state = STORE); 219 | -- psl assert always (s_data_valid and DataAccept_i) -> next not(s_data_valid); 220 | 221 | 222 | end architecture rtl; 223 | -------------------------------------------------------------------------------- /syn/UartRx.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | library libvhdl; 22 | use libvhdl.UtilsP.all; 23 | 24 | 25 | 26 | entity UartRx is 27 | generic ( 28 | DATA_LENGTH : positive range 5 to 9 := 8; 29 | PARITY : boolean := true; 30 | CLK_DIV : natural := 10 31 | ); 32 | port ( 33 | reset_n_i : in std_logic; -- async reset 34 | clk_i : in std_logic; -- clock 35 | data_o : out std_logic_vector(DATA_LENGTH-1 downto 0); -- data output 36 | error_o : out std_logic; -- rx error 37 | valid_o : out std_logic; -- output data valid 38 | accept_i : in std_logic; -- output data accepted 39 | rx_i : in std_logic -- uart rx input 40 | ); 41 | end entity UartRx; 42 | 43 | 44 | 45 | architecture rtl of UartRx is 46 | 47 | 48 | function to_integer (data : in boolean) return integer is 49 | begin 50 | if data then 51 | return 1; 52 | else 53 | return 0; 54 | end if; 55 | end function to_integer; 56 | 57 | 58 | type t_uart_state is (IDLE, RECEIVE, VALID); 59 | signal s_uart_state : t_uart_state; 60 | 61 | signal s_data : std_logic_vector(DATA_LENGTH+1+to_integer(PARITY) downto 0); 62 | signal s_clk_en : boolean; 63 | 64 | 65 | begin 66 | 67 | 68 | ClkDivP : process (clk_i, reset_n_i) is 69 | variable v_clk_cnt : natural range 0 to CLK_DIV-1; 70 | begin 71 | if (reset_n_i = '0') then 72 | s_clk_en <= false; 73 | v_clk_cnt := CLK_DIV-1; 74 | elsif (rising_edge(clk_i)) then 75 | s_clk_en <= false; 76 | if (s_uart_state = IDLE) then 77 | v_clk_cnt := CLK_DIV-2; 78 | elsif (s_uart_state = RECEIVE) then 79 | if (v_clk_cnt = 0) then 80 | v_clk_cnt := CLK_DIV-1; 81 | else 82 | v_clk_cnt := v_clk_cnt - 1; 83 | end if; 84 | if (v_clk_cnt = CLK_DIV/2-1) then 85 | s_clk_en <= true; 86 | end if; 87 | end if; 88 | end if; 89 | end process ClkDivP; 90 | 91 | 92 | RxP : process (clk_i, reset_n_i) is 93 | variable v_bit_cnt : natural range 0 to s_data'length-1; 94 | begin 95 | if (reset_n_i = '0') then 96 | s_uart_state <= IDLE; 97 | s_data <= (others => '0'); 98 | valid_o <= '0'; 99 | v_bit_cnt := 0; 100 | elsif (rising_edge(clk_i)) then 101 | FsmL : case s_uart_state is 102 | when IDLE => 103 | valid_o <= '0'; 104 | v_bit_cnt := s_data'length-1; 105 | if (rx_i = '0') then 106 | s_uart_state <= RECEIVE; 107 | end if; 108 | when RECEIVE => 109 | if (s_clk_en) then 110 | s_data <= rx_i & s_data(s_data'length-1 downto 1); 111 | if (v_bit_cnt = 0) then 112 | valid_o <= '1'; 113 | s_uart_state <= VALID; 114 | else 115 | v_bit_cnt := v_bit_cnt - 1; 116 | end if; 117 | end if; 118 | when VALID => 119 | valid_o <= '1'; 120 | if (valid_o = '1' and accept_i = '1') then 121 | valid_o <= '0'; 122 | s_uart_state <= IDLE; 123 | end if; 124 | end case; 125 | end if; 126 | end process RxP; 127 | 128 | 129 | ParityG : if PARITY generate 130 | data_o <= s_data(s_data'length-3 downto 1); 131 | error_o <= '1' when odd_parity(s_data(s_data'length-3 downto 1)) /= s_data(s_data'length-2) or 132 | s_data(s_data'length-1) = '0' else 133 | '0'; 134 | else generate 135 | data_o <= s_data(s_data'length-2 downto 1); 136 | error_o <= '1' when s_data(s_data'length-1) = '0' else '0'; 137 | end generate ParityG; 138 | 139 | 140 | end architecture rtl; 141 | -------------------------------------------------------------------------------- /syn/UartTx.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | library libvhdl; 22 | use libvhdl.UtilsP.all; 23 | 24 | 25 | 26 | entity UartTx is 27 | generic ( 28 | DATA_LENGTH : positive range 5 to 9 := 8; 29 | PARITY : boolean := true; 30 | CLK_DIV : natural := 10 31 | ); 32 | port ( 33 | reset_n_i : in std_logic; -- async reset 34 | clk_i : in std_logic; -- clock 35 | data_i : in std_logic_vector(DATA_LENGTH-1 downto 0); -- data input 36 | valid_i : in std_logic; -- input data valid 37 | accept_o : out std_logic; -- inpit data accepted 38 | tx_o : out std_logic -- uart tx data output 39 | ); 40 | end entity UartTx; 41 | 42 | 43 | 44 | architecture rtl of UartTx is 45 | 46 | 47 | function to_integer (data : in boolean) return integer is 48 | begin 49 | if data then 50 | return 1; 51 | else 52 | return 0; 53 | end if; 54 | end function to_integer; 55 | 56 | 57 | type t_uart_state is (IDLE, SEND); 58 | signal s_uart_state : t_uart_state; 59 | 60 | signal s_data : std_logic_vector(DATA_LENGTH+1+to_integer(PARITY) downto 0); 61 | signal s_clk_en : boolean; 62 | 63 | 64 | begin 65 | 66 | 67 | ClkDivP : process (clk_i, reset_n_i) is 68 | variable v_clk_cnt : natural range 0 to CLK_DIV-1; 69 | begin 70 | if (reset_n_i = '0') then 71 | s_clk_en <= false; 72 | v_clk_cnt := CLK_DIV-1; 73 | elsif (rising_edge(clk_i)) then 74 | if (s_uart_state = IDLE) then 75 | v_clk_cnt := CLK_DIV-2; 76 | s_clk_en <= false; 77 | elsif (s_uart_state = SEND) then 78 | if (v_clk_cnt = 0) then 79 | v_clk_cnt := CLK_DIV-1; 80 | s_clk_en <= true; 81 | else 82 | v_clk_cnt := v_clk_cnt - 1; 83 | s_clk_en <= false; 84 | end if; 85 | end if; 86 | end if; 87 | end process ClkDivP; 88 | 89 | 90 | TxP : process (clk_i, reset_n_i) is 91 | variable v_bit_cnt : natural range 0 to s_data'length-1; 92 | begin 93 | if (reset_n_i = '0') then 94 | s_uart_state <= IDLE; 95 | s_data <= (0 => '1', others => '0'); 96 | accept_o <= '0'; 97 | v_bit_cnt := 0; 98 | elsif (rising_edge(clk_i)) then 99 | FsmL : case s_uart_state is 100 | when IDLE => 101 | accept_o <= '1'; 102 | v_bit_cnt := s_data'length-1; 103 | if (valid_i = '1' and accept_o = '1') then 104 | accept_o <= '0'; 105 | if (PARITY) then 106 | s_data <= '1' & odd_parity(data_i) & data_i & '0'; 107 | else 108 | s_data <= '1' & data_i & '0'; 109 | end if; 110 | s_uart_state <= SEND; 111 | end if; 112 | when SEND => 113 | if (s_clk_en) then 114 | s_data <= '1' & s_data(s_data'length-1 downto 1); 115 | if (v_bit_cnt = 0) then 116 | accept_o <= '1'; 117 | s_uart_state <= IDLE; 118 | else 119 | v_bit_cnt := v_bit_cnt - 1; 120 | end if; 121 | end if; 122 | end case; 123 | end if; 124 | end process TxP; 125 | 126 | 127 | tx_o <= s_data(0); 128 | 129 | 130 | end architecture rtl; 131 | -------------------------------------------------------------------------------- /syn/WishBoneCheckerE.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | 22 | 23 | entity WishBoneCheckerE is 24 | port ( 25 | --+ wishbone system if 26 | WbRst_i : in std_logic; 27 | WbClk_i : in std_logic; 28 | --+ wishbone outputs 29 | WbMCyc_i : in std_logic; 30 | WbMStb_i : in std_logic; 31 | WbMWe_i : in std_logic; 32 | WbMAdr_i : in std_logic_vector; 33 | WbMDat_i : in std_logic_vector; 34 | --+ wishbone inputs 35 | WbSDat_i : in std_logic_vector; 36 | WbSAck_i : in std_logic; 37 | WbSErr_i : in std_logic; 38 | WbRty_i : in std_logic 39 | ); 40 | end entity WishBoneCheckerE; 41 | 42 | 43 | 44 | architecture check of WishBoneCheckerE is 45 | 46 | begin 47 | 48 | 49 | -- psl default clock is rising_edge(WbClk_i); 50 | -- 51 | -- Wishbone protocol checks 52 | -- 53 | -- psl property initialize_interface (boolean init_state) is 54 | -- always ({WbRst_i} |=> {init_state[+] && {WbRst_i[*]; not(WbRst_i)}}); 55 | -- 56 | -- psl RULE_3_00 : assert initialize_interface (not(WbMCyc_i) and not(WbMStb_i) and not(WbMWe_i)) 57 | -- report "Wishbone rule 3.00 violated"; 58 | -- 59 | -- psl property reset_signal is 60 | -- always {not(WbRst_i); WbRst_i} |=> {(WbRst_i and not(WbClk_i))[*]; WbRst_i and WbClk_i}; 61 | -- 62 | -- psl RULE_3_05 : assert reset_signal 63 | -- report "Wishbone rule 3.05 violated"; 64 | -- 65 | -- psl property CYC_O_signal is 66 | -- always {not(WbMStb_i); WbMStb_i} |-> {(WbMCyc_i and WbMStb_i)[+]; not(WbMStb_i)}; 67 | -- 68 | -- psl RULE_3_25 : assert CYC_O_signal 69 | -- report "Wishbone rule 3.25 violated"; 70 | -- 71 | -- psl property slave_no_response is 72 | -- always not(WbMCyc_i) -> not(WbSAck_i) and not(WbSErr_i); 73 | -- 74 | -- psl property slave_response_to_master is 75 | -- always {not(WbMStb_i); WbMStb_i} |-> 76 | -- {{(WbMStb_i and not(WbSAck_i))[*]; 77 | -- WbMStb_i and WbSAck_i; 78 | -- not(WbMStb_i)} | 79 | -- {(WbMStb_i and not(WbSErr_i))[*]; 80 | -- WbMStb_i and WbSErr_i; 81 | -- not(WbMStb_i)} | 82 | -- {(WbMStb_i and not(WbRty_i))[*]; 83 | -- WbMStb_i and WbRty_i; 84 | -- not(WbMStb_i)} 85 | -- }; 86 | -- 87 | -- psl RULE_3_30_0 : assert slave_no_response 88 | -- report "Wishbone rule 3.30_0 violated"; 89 | -- 90 | -- psl RULE_3_30_1 : assert slave_response_to_master 91 | -- report "Wishbone rule 3.30_0 violated"; 92 | -- 93 | -- psl property slave_response is 94 | -- always {not(WbMStb_i); WbMCyc_i and WbMStb_i} |-> 95 | -- {not(WbSAck_i or WbSErr_i or WbRty_i)[*]; WbSAck_i or WbSErr_i or WbRty_i}; 96 | -- 97 | -- psl RULE_3_35 : assert slave_response 98 | -- report "Wishbone rule 3.35 violated"; 99 | -- 100 | -- psl property response_signals is 101 | -- never ((WbSErr_i and WbRty_i) or (WbSErr_i and WbSAck_i) or (WbSAck_i and WbRty_i)); 102 | -- 103 | -- psl RULE_3_45 : assert response_signals 104 | -- report "Wishbone rule 3.45 violated"; 105 | -- 106 | -- -- psl property slave_negated_response is 107 | 108 | 109 | end architecture check; -------------------------------------------------------------------------------- /syn/WishBoneMasterE.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | 22 | 23 | entity WishBoneMasterE is 24 | generic ( 25 | Coverage : boolean := false; 26 | Formal : boolean := false; 27 | Simulation : boolean := false; 28 | AddressWidth : natural := 8; 29 | DataWidth : natural := 8 30 | ); 31 | port ( 32 | --+ wishbone system if 33 | WbRst_i : in std_logic; 34 | WbClk_i : in std_logic; 35 | --+ wishbone outputs 36 | WbCyc_o : out std_logic; 37 | WbStb_o : out std_logic; 38 | WbWe_o : out std_logic; 39 | WbAdr_o : out std_logic_vector(AddressWidth-1 downto 0); 40 | WbDat_o : out std_logic_vector(DataWidth-1 downto 0); 41 | --+ wishbone inputs 42 | WbDat_i : in std_logic_vector(DataWidth-1 downto 0); 43 | WbAck_i : in std_logic; 44 | WbErr_i : in std_logic; 45 | --+ local register if 46 | LocalWen_i : in std_logic; 47 | LocalRen_i : in std_logic; 48 | LocalAdress_i : in std_logic_vector(AddressWidth-1 downto 0); 49 | LocalData_i : in std_logic_vector(DataWidth-1 downto 0); 50 | LocalData_o : out std_logic_vector(DataWidth-1 downto 0); 51 | LocalAck_o : out std_logic; 52 | LocalError_o : out std_logic 53 | ); 54 | end entity WishBoneMasterE; 55 | 56 | 57 | 58 | architecture rtl of WishBoneMasterE is 59 | 60 | 61 | type t_wb_master_fsm is (IDLE, ADDRESS, DATA); 62 | signal s_wb_master_fsm : t_wb_master_fsm; 63 | 64 | signal s_wb_wen : std_logic; 65 | 66 | 67 | begin 68 | 69 | 70 | --+ Wishbone master control state machine 71 | WbMasterStatesP : process (WbClk_i) is 72 | begin 73 | if (rising_edge(WbClk_i)) then 74 | if (WbRst_i = '1') then 75 | s_wb_master_fsm <= IDLE; 76 | else 77 | WbReadC : case s_wb_master_fsm is 78 | 79 | when IDLE => 80 | if ((LocalWen_i xor LocalRen_i) = '1') then 81 | s_wb_master_fsm <= ADDRESS; 82 | end if; 83 | 84 | when ADDRESS => 85 | if (WbAck_i = '1' or WbErr_i = '1') then 86 | s_wb_master_fsm <= IDLE; 87 | else 88 | s_wb_master_fsm <= DATA; 89 | end if; 90 | 91 | when DATA => 92 | if (WbErr_i = '1' or WbAck_i = '1') then 93 | s_wb_master_fsm <= IDLE; 94 | end if; 95 | 96 | when others => 97 | s_wb_master_fsm <= IDLE; 98 | 99 | end case; 100 | end if; 101 | end if; 102 | end process WbMasterStatesP; 103 | 104 | 105 | --+ combinatoral local register if outputs 106 | LocalData_o <= WbDat_i when s_wb_master_fsm = DATA else (others => '0'); 107 | LocalError_o <= WbErr_i when s_wb_master_fsm /= IDLE else '0'; 108 | LocalAck_o <= WbAck_i when (s_wb_master_fsm = ADDRESS or s_wb_master_fsm = DATA) and WbErr_i = '0' else '0'; 109 | 110 | --+ combinatoral wishbone if outputs 111 | WbStb_o <= '1' when s_wb_master_fsm /= IDLE else '0'; 112 | WbCyc_o <= '1' when s_wb_master_fsm /= IDLE else '0'; 113 | WbWe_o <= s_wb_wen when s_wb_master_fsm /= IDLE else '0'; 114 | 115 | 116 | --+ registered wishbone if outputs 117 | OutRegsP : process (WbClk_i) is 118 | begin 119 | if (rising_edge(WbClk_i)) then 120 | if (WbRst_i = '1') then 121 | WbAdr_o <= (others => '0'); 122 | WbDat_o <= (others => '0'); 123 | s_wb_wen <= '0'; 124 | else 125 | if (s_wb_master_fsm = IDLE) then 126 | if ((LocalWen_i xor LocalRen_i) = '1') then 127 | WbAdr_o <= LocalAdress_i; 128 | s_wb_wen <= LocalWen_i; 129 | end if; 130 | if (LocalWen_i = '1') then 131 | WbDat_o <= LocalData_i; 132 | end if; 133 | end if; 134 | end if; 135 | end if; 136 | end process OutRegsP; 137 | 138 | 139 | default clock is rising_edge(WbClk_i); 140 | 141 | FormalG : if Formal generate 142 | 143 | -- Glue logic 144 | signal s_local_data : std_logic_vector(DataWidth-1 downto 0); 145 | signal s_local_address : std_logic_vector(AddressWidth-1 downto 0); 146 | 147 | begin 148 | 149 | process is 150 | begin 151 | wait until rising_edge(WbClk_i); 152 | if (s_wb_master_fsm = IDLE) then 153 | if (LocalWen_i = '1') then 154 | s_local_data <= LocalData_i; 155 | s_local_address <= LocalAdress_i; 156 | end if; 157 | if (LocalRen_i = '1') then 158 | s_local_address <= LocalAdress_i; 159 | end if; 160 | end if; 161 | end process; 162 | 163 | restrict {WbRst_i = '1'; WbRst_i = '0'[+]}[*1]; 164 | 165 | RESET : assert always 166 | WbRst_i -> next 167 | WbCyc_o = '0' and WbStb_o = '0' and WbWe_o = '0' and 168 | to_integer(unsigned(WbAdr_o)) = 0 and to_integer(unsigned(WbDat_o)) = 0 and 169 | LocalAck_o = '0' and LocalError_o = '0' and to_integer(unsigned(LocalData_o)) = 0 170 | report "WB master: Reset error"; 171 | 172 | WB_WRITE : assert always 173 | ((not WbCyc_o and not WbStb_o and LocalWen_i and not LocalRen_i) -> 174 | next (WbCyc_o and WbStb_o and WbWe_o)) abort WbRst_i 175 | report "WB master: Write error"; 176 | 177 | WB_READ : assert always 178 | ((not WbCyc_o and not WbStb_o and LocalRen_i and not LocalWen_i) -> 179 | next (WbCyc_o and WbStb_o and not WbWe_o)) abort WbRst_i 180 | report "WB master: Read error"; 181 | 182 | assert never LocalError_o and LocalAck_o; 183 | 184 | assert always WbStb_o = WbCyc_o; 185 | 186 | assert always 187 | not WbRst_i and WbCyc_o and not WbAck_i and not WbErr_i -> 188 | next (WbCyc_o until (WbAck_i or WbErr_i)) abort WbRst_i; 189 | 190 | assert always WbCyc_o and WbAck_i -> next not WbCyc_o; 191 | assert always WbWe_o and WbAck_i -> next not WbWe_o; 192 | assert always WbWe_o -> WbCyc_o; 193 | 194 | assert always WbWe_o -> WbDat_o = s_local_data abort WbRst_i; 195 | assert always WbWe_o -> WbAdr_o = s_local_address abort WbRst_i; 196 | 197 | assert always WbCyc_o and not WbWe_o -> WbAdr_o = s_local_address abort WbRst_i; 198 | 199 | end generate FormalG; 200 | 201 | 202 | CoverageG : if Coverage generate 203 | 204 | restrict {WbRst_i = '1'; WbRst_i = '0'[+]}[*1]; 205 | 206 | COVER_LOCAL_WRITE : cover {s_wb_master_fsm = IDLE and LocalWen_i = '1' and 207 | LocalRen_i = '0' and WbRst_i = '0'} 208 | report "WB master: Local write"; 209 | 210 | COVER_LOCAL_READ : cover {s_wb_master_fsm = IDLE and LocalRen_i = '1' and 211 | LocalWen_i = '0' and WbRst_i = '0'} 212 | report "WB master: Local read"; 213 | 214 | COVER_LOCAL_WRITE_READ : cover {s_wb_master_fsm = IDLE and LocalWen_i = '1' and 215 | LocalRen_i = '1' and WbRst_i = '0'} 216 | report "WB master: Local write & read"; 217 | 218 | end generate CoverageG; 219 | 220 | 221 | SimulationG : if Simulation generate 222 | 223 | -- assert directives 224 | RESET : assert always 225 | WbRst_i -> 226 | WbCyc_o = '0' and WbStb_o = '0' and WbWe_o = '0' and 227 | to_integer(unsigned(WbAdr_o)) = 0 and to_integer(unsigned(WbDat_o)) = 0 and 228 | LocalAck_o = '0' and LocalError_o = '0' and to_integer(unsigned(LocalData_o)) = 0 229 | report "WB master: Reset error"; 230 | 231 | WB_WRITE : assert always 232 | ((not(WbCyc_o) and not(WbStb_o) and LocalWen_i and not (LocalRen_i)) -> 233 | next (WbCyc_o = '1' and WbStb_o = '1' and WbWe_o = '1')) abort WbRst_i 234 | report "WB master: Write error"; 235 | 236 | WB_READ : assert always 237 | ((not(WbCyc_o) and not(WbStb_o) and LocalRen_i and not(LocalWen_i)) -> 238 | next (WbCyc_o = '1' and WbStb_o = '1' and WbWe_o = '0')) abort WbRst_i 239 | report "WB master: Read error"; 240 | 241 | 242 | -- cover directives 243 | COVER_LOCAL_WRITE : cover {s_wb_master_fsm = IDLE and LocalWen_i = '1' and 244 | LocalRen_i = '0' and WbRst_i = '0'} 245 | report "WB master: Local write"; 246 | 247 | COVER_LOCAL_READ : cover {s_wb_master_fsm = IDLE and LocalRen_i = '1' and 248 | LocalWen_i = '0' and WbRst_i = '0'} 249 | report "WB master: Local read"; 250 | 251 | COVER_LOCAL_WRITE_READ : cover {s_wb_master_fsm = IDLE and LocalWen_i = '1' and 252 | LocalRen_i = '1' and WbRst_i = '0'} 253 | report "WB master: Local write & read"; 254 | 255 | end generate SimulationG; 256 | 257 | 258 | end architecture rtl; 259 | -------------------------------------------------------------------------------- /syn/WishBoneP.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | 20 | 21 | 22 | package WishBoneP is 23 | 24 | 25 | 26 | component WishBoneMasterE is 27 | generic ( 28 | Coverage : boolean := false; 29 | Formal : boolean := false; 30 | Simulation : boolean := false; 31 | AddressWidth : natural := 8; 32 | DataWidth : natural := 8 33 | ); 34 | port ( 35 | --+ wishbone system if 36 | WbRst_i : in std_logic; 37 | WbClk_i : in std_logic; 38 | --+ wishbone outputs 39 | WbCyc_o : out std_logic; 40 | WbStb_o : out std_logic; 41 | WbWe_o : out std_logic; 42 | WbAdr_o : out std_logic_vector; 43 | WbDat_o : out std_logic_vector; 44 | --+ wishbone inputs 45 | WbDat_i : in std_logic_vector; 46 | WbAck_i : in std_logic; 47 | WbErr_i : in std_logic; 48 | --+ local register if 49 | LocalWen_i : in std_logic; 50 | LocalRen_i : in std_logic; 51 | LocalAdress_i : in std_logic_vector; 52 | LocalData_i : in std_logic_vector; 53 | LocalData_o : out std_logic_vector; 54 | LocalAck_o : out std_logic; 55 | LocalError_o : out std_logic 56 | ); 57 | end component WishBoneMasterE; 58 | 59 | 60 | component WishBoneSlaveE is 61 | generic ( 62 | Formal : boolean := false; 63 | Simulation : boolean := false; 64 | AddressWidth : natural := 32; 65 | DataWidth : natural := 32 66 | ); 67 | port ( 68 | --+ wishbone system if 69 | WbRst_i : in std_logic; 70 | WbClk_i : in std_logic; 71 | --+ wishbone inputs 72 | WbCyc_i : in std_logic; 73 | WbStb_i : in std_logic; 74 | WbWe_i : in std_logic; 75 | WbAdr_i : in std_logic_vector; 76 | WbDat_i : in std_logic_vector; 77 | --* wishbone outputs 78 | WbDat_o : out std_logic_vector; 79 | WbAck_o : out std_logic; 80 | WbErr_o : out std_logic; 81 | --+ local register if 82 | LocalWen_o : out std_logic; 83 | LocalRen_o : out std_logic; 84 | LocalAdress_o : out std_logic_vector; 85 | LocalData_o : out std_logic_vector; 86 | LocalData_i : in std_logic_vector 87 | ); 88 | end component WishBoneSlaveE; 89 | 90 | 91 | component WishBoneCheckerE is 92 | port ( 93 | --+ wishbone system if 94 | WbRst_i : in std_logic; 95 | WbClk_i : in std_logic; 96 | --+ wishbone outputs 97 | WbMCyc_i : in std_logic; 98 | WbMStb_i : in std_logic; 99 | WbMWe_i : in std_logic; 100 | WbMAdr_i : in std_logic_vector; 101 | WbMDat_i : in std_logic_vector; 102 | --+ wishbone inputs 103 | WbSDat_i : in std_logic_vector; 104 | WbSAck_i : in std_logic; 105 | WbSErr_i : in std_logic; 106 | WbRty_i : in std_logic 107 | ); 108 | end component WishBoneCheckerE; 109 | 110 | 111 | type t_wishbone_if is record 112 | --+ wishbone outputs 113 | Cyc : std_logic; 114 | Stb : std_logic; 115 | We : std_logic; 116 | Adr : std_logic_vector; 117 | WDat : std_logic_vector; 118 | --+ wishbone inputs 119 | RDat : std_logic_vector; 120 | Ack : std_logic; 121 | Err : std_logic; 122 | end record t_wishbone_if; 123 | 124 | 125 | 126 | end package WishBoneP; -------------------------------------------------------------------------------- /syn/WishBoneSlaveE.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | 22 | 23 | entity WishBoneSlaveE is 24 | generic ( 25 | Formal : boolean := false; 26 | Simulation : boolean := false; 27 | AddressWidth : natural := 32; 28 | DataWidth : natural := 32 29 | ); 30 | port ( 31 | --+ wishbone system if 32 | WbRst_i : in std_logic; 33 | WbClk_i : in std_logic; 34 | --+ wishbone inputs 35 | WbCyc_i : in std_logic; 36 | WbStb_i : in std_logic; 37 | WbWe_i : in std_logic; 38 | WbAdr_i : in std_logic_vector(AddressWidth-1 downto 0); 39 | WbDat_i : in std_logic_vector(DataWidth-1 downto 0); 40 | --+ wishbone outputs 41 | WbDat_o : out std_logic_vector(DataWidth-1 downto 0); 42 | WbAck_o : out std_logic; 43 | WbErr_o : out std_logic; 44 | --+ local register if 45 | LocalWen_o : out std_logic; 46 | LocalRen_o : out std_logic; 47 | LocalAdress_o : out std_logic_vector(AddressWidth-1 downto 0); 48 | LocalData_o : out std_logic_vector(DataWidth-1 downto 0); 49 | LocalData_i : in std_logic_vector(DataWidth-1 downto 0) 50 | ); 51 | end entity WishBoneSlaveE; 52 | 53 | 54 | 55 | architecture rtl of WishBoneSlaveE is 56 | 57 | 58 | type t_wb_slave_fsm is (IDLE, ADDRESS, DATA); 59 | signal s_wb_slave_fsm : t_wb_slave_fsm; 60 | 61 | signal s_wb_active : boolean; 62 | 63 | 64 | begin 65 | 66 | 67 | WbSlaveControlP : process (WbClk_i) is 68 | begin 69 | if (rising_edge(WbClk_i)) then 70 | if (WbRst_i = '1') then 71 | s_wb_slave_fsm <= IDLE; 72 | else 73 | WbReadC : case s_wb_slave_fsm is 74 | 75 | when IDLE => 76 | s_wb_slave_fsm <= ADDRESS; 77 | 78 | when ADDRESS => 79 | if (s_wb_active and WbWe_i = '0') then 80 | s_wb_slave_fsm <= DATA; 81 | end if; 82 | 83 | when DATA => 84 | s_wb_slave_fsm <= ADDRESS; 85 | 86 | when others => 87 | s_wb_slave_fsm <= IDLE; 88 | 89 | end case; 90 | end if; 91 | end if; 92 | end process WbSlaveControlP; 93 | 94 | 95 | s_wb_active <= true when s_wb_slave_fsm /= IDLE and WbCyc_i = '1' and WbStb_i = '1' else false; 96 | 97 | --+ local register if outputs 98 | LocalWen_o <= WbWe_i when s_wb_slave_fsm = ADDRESS and s_wb_active else '0'; 99 | LocalRen_o <= not(WbWe_i) when s_wb_slave_fsm = ADDRESS and s_wb_active else '0'; 100 | LocalAdress_o <= WbAdr_i when s_wb_slave_fsm /= IDLE and s_wb_active else (others => '0'); 101 | LocalData_o <= WbDat_i when s_wb_slave_fsm = ADDRESS and s_wb_active and WbWe_i = '1' else (others => '0'); 102 | 103 | --+ wishbone if outputs 104 | WbDat_o <= LocalData_i when s_wb_slave_fsm = DATA and WbWe_i = '0' else (others => '0'); 105 | WbAck_o <= '1' when (s_wb_slave_fsm = DATA and WbWe_i = '0') or (s_wb_slave_fsm = ADDRESS and s_wb_active and WbWe_i = '1') else '0'; 106 | WbErr_o <= '1' when s_wb_slave_fsm = DATA and WbWe_i = '1' else '0'; 107 | 108 | 109 | default clock is rising_edge(WbClk_i); 110 | 111 | FormalG : if Formal generate 112 | 113 | -- Glue logic 114 | signal s_wb_data : std_logic_vector(DataWidth-1 downto 0); 115 | signal s_wb_address : std_logic_vector(AddressWidth-1 downto 0); 116 | 117 | begin 118 | 119 | SyncWbSignals : process is 120 | begin 121 | wait until rising_edge(WbClk_i); 122 | if (s_wb_slave_fsm = ADDRESS and WbCyc_i = '1' and WbStb_i = '1') then 123 | if (WbWe_i = '1') then 124 | s_wb_data <= WbDat_i; 125 | end if; 126 | s_wb_address <= WbAdr_i; 127 | end if; 128 | end process SyncWbSignals; 129 | 130 | restrict {WbRst_i = '1'; WbRst_i = '0'[+]}[*1]; 131 | 132 | assume always WbCyc_i = WbStb_i; 133 | assume always WbWe_i -> WbStb_i; 134 | assume always WbWe_i and WbAck_o -> next not WbWe_i; 135 | 136 | -- FSM state checks 137 | FSM_IDLE_TO_ADDRESS : assert always 138 | not WbRst_i and s_wb_slave_fsm = IDLE -> 139 | next s_wb_slave_fsm = ADDRESS abort WbRst_i; 140 | 141 | FSM_ADDRESS_TO_DATA : assert always 142 | not WbRst_i and s_wb_slave_fsm = ADDRESS and WbStb_i and WbCyc_i and not WbWe_i -> 143 | next s_wb_slave_fsm = DATA abort WbRst_i; 144 | 145 | FSM_ADDRESS_TO_ADDRESS : assert always 146 | not WbRst_i and s_wb_slave_fsm = ADDRESS and not (WbStb_i and WbCyc_i and not WbWe_i) -> 147 | next s_wb_slave_fsm = ADDRESS abort WbRst_i; 148 | 149 | FSM_DATA_TO_ADDRESS : assert always 150 | not WbRst_i and s_wb_slave_fsm = DATA -> 151 | next s_wb_slave_fsm = ADDRESS abort WbRst_i; 152 | 153 | -- Wishbone write cycle checks 154 | WB_WRITE_CYCLE_0 : assert always 155 | s_wb_slave_fsm = ADDRESS and WbStb_i and WbCyc_i and WbWe_i -> 156 | LocalWen_o and WbAck_o; 157 | 158 | WB_WRITE_CYCLE_1 : assert always 159 | LocalWen_o -> LocalAdress_o = WbAdr_i; 160 | 161 | WB_WRITE_CYCLE_2 : assert always 162 | LocalWen_o -> LocalData_o = WbDat_i; 163 | 164 | -- Wishbone read cycle checks 165 | WB_READ_CYCLE_0 : assert always 166 | s_wb_slave_fsm = ADDRESS and WbStb_i and WbCyc_i and not WbWe_i -> 167 | LocalRen_o and not WbAck_o; 168 | 169 | WB_READ_CYCLE_1 : assert always 170 | LocalRen_o -> LocalAdress_o = WbAdr_i; 171 | 172 | WB_READ_CYCLE_2 : assert always 173 | s_wb_slave_fsm = DATA and not WbWe_i -> 174 | WbAck_o and WbDat_o = LocalData_i; 175 | 176 | WB_READ_ERROR : assert always 177 | s_wb_slave_fsm = DATA and WbWe_i -> 178 | WbErr_o; 179 | 180 | WB_NEVER_ACK_AND_ERR : assert never 181 | WbAck_o and WbErr_o; 182 | 183 | WB_ERR : assert always 184 | WbErr_o -> 185 | (WbCyc_i and WbStb_i) 186 | report "PSL ERROR: WbErr invalid"; 187 | 188 | LOCAL_WE : assert always 189 | LocalWen_o -> 190 | (WbCyc_i and WbStb_i and WbWe_i and not LocalRen_o) and 191 | (next not LocalWen_o) 192 | report "PSL ERROR: LocalWen invalid"; 193 | 194 | LOCAL_RE : assert always 195 | LocalRen_o -> 196 | (WbCyc_i and WbStb_i and not WbWe_i and not LocalWen_o) and 197 | (next not LocalRen_o) 198 | report "PSL ERROR: LocalRen invalid"; 199 | 200 | RESET : assert always 201 | WbRst_i -> next 202 | (to_integer(unsigned(WbDat_o)) = 0 and WbAck_o = '0' and WbErr_o = '0' and 203 | LocalWen_o = '0' and LocalRen_o = '0' and to_integer(unsigned(LocalAdress_o)) = 0 and to_integer(unsigned(LocalData_o)) = 0) 204 | report "PSL ERROR: Reset error"; 205 | 206 | end generate FormalG; 207 | 208 | 209 | SimulationG : if Simulation generate 210 | 211 | LOCAL_WRITE : assert always 212 | ((WbCyc_i and WbStb_i and WbWe_i) -> 213 | (LocalWen_o = '1' and WbAck_o = '1' and LocalAdress_o = WbAdr_i and LocalData_o = WbDat_i)) abort WbRst_i 214 | report "PSL ERROR: Local write error"; 215 | 216 | LOCAL_READ : assert always 217 | ({not(WbCyc_i) and not(WbStb_i); WbCyc_i and WbStb_i and not(WbWe_i)} |-> 218 | {LocalRen_o = '1' and LocalAdress_o = WbAdr_i and WbAck_o = '0'; LocalRen_o = '0' and WbDat_o = LocalData_i and WbAck_o = '1'}) abort WbRst_i 219 | report "PSL ERROR: Local read error"; 220 | 221 | WB_ACK : assert always 222 | WbAck_o -> 223 | (WbCyc_i and WbStb_i) 224 | report "PSL ERROR: WbAck invalid"; 225 | 226 | WB_ERR : assert always 227 | WbErr_o -> 228 | (WbCyc_i and WbStb_i) 229 | report "PSL ERROR: WbErr invalid"; 230 | 231 | LOCAL_WE : assert always 232 | LocalWen_o -> 233 | (WbCyc_i and WbStb_i and WbWe_i and not(LocalRen_o)) and 234 | (next not(LocalWen_o)) 235 | report "PSL ERROR: LocalWen invalid"; 236 | 237 | LOCAL_RE : assert always 238 | LocalRen_o -> 239 | (WbCyc_i and WbStb_i and not(WbWe_i) and not(LocalWen_o)) and 240 | (next not(LocalRen_o)) 241 | report "PSL ERROR: LocalRen invalid"; 242 | 243 | RESET : assert always 244 | WbRst_i -> 245 | (to_integer(unsigned(WbDat_o)) = 0 and WbAck_o = '0' and WbErr_o = '0' and 246 | LocalWen_o = '0' and LocalRen_o = '0' and to_integer(unsigned(LocalAdress_o)) = 0 and to_integer(unsigned(LocalData_o)) = 0) 247 | report "PSL ERROR: Reset error"; 248 | 249 | end generate SimulationG; 250 | 251 | 252 | end architecture rtl; 253 | -------------------------------------------------------------------------------- /test/DictT.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | 22 | 23 | library osvvm; 24 | use osvvm.RandomPkg.all; 25 | 26 | library libvhdl; 27 | 28 | 29 | 30 | entity DictT is 31 | end entity DictT; 32 | 33 | 34 | 35 | architecture sim of DictT is 36 | 37 | 38 | type t_scoreboard is array (natural range <>) of std_logic_vector(7 downto 0); 39 | 40 | function to_string(d : string) return string is 41 | begin 42 | return d; 43 | end function to_string; 44 | 45 | 46 | package StringSlvDict is new libvhdl.DictP 47 | generic map (KEY_TYPE => string, 48 | VALUE_TYPE => std_logic_vector, 49 | key_to_string => to_string, 50 | value_to_string => to_hstring); 51 | 52 | use StringSlvDict.all; 53 | 54 | shared variable sv_dict : t_dict; 55 | shared variable sv_dact : t_dict; 56 | shared variable sv_duct : t_dict; 57 | 58 | 59 | begin 60 | 61 | 62 | 63 | DictInitP : process is 64 | begin 65 | sv_dict.init(false); 66 | sv_dact.init(false); 67 | sv_duct.init(false); 68 | wait; 69 | end process DictInitP; 70 | 71 | 72 | DictTestP : process is 73 | variable v_key : t_dict_key_ptr; 74 | variable v_last_key : t_dict_key_ptr; 75 | variable v_random : RandomPType; 76 | variable v_input : std_logic_vector(7 downto 0); 77 | variable v_output : std_logic_vector(7 downto 0); 78 | variable v_scoreboard : t_scoreboard(0 to 511); 79 | begin 80 | v_random.InitSeed(v_random'instance_name); 81 | 82 | -- check initial emptiness 83 | assert sv_dict.size = 0 84 | report "ERROR: Dict should be empty" 85 | severity failure; 86 | 87 | -- fill dictionary and check count 88 | report "INFO: Test 1: Fill dictionary"; 89 | for i in 0 to 255 loop 90 | v_input := v_random.RandSlv(8); 91 | sv_dict.set(integer'image(i), v_input); 92 | v_scoreboard(i) := v_input; 93 | assert sv_dict.size = i+1 94 | report "ERROR: Dict should have " & to_string(i+1) & " entries" 95 | severity failure; 96 | end loop; 97 | report "INFO: Test successful"; 98 | 99 | -- read all entries and check for correct data 100 | report "INFO: Test 2: Read dictionary"; 101 | for i in 0 to 255 loop 102 | sv_dict.get(integer'image(i), v_output); 103 | assert v_output = v_scoreboard(i) 104 | report "ERROR: Got 0x" & to_hstring(v_output) & ", expected 0x" & to_hstring(v_scoreboard(i)) 105 | severity failure; 106 | end loop; 107 | report "INFO: Test successful"; 108 | 109 | -- overwrite a key/value pair 110 | report "INFO: Test 3: Overwrite a entry"; 111 | v_input := v_random.RandSlv(8); 112 | sv_dict.set("128", v_input); 113 | v_scoreboard(128) := v_input; 114 | sv_dict.get("128", v_output); 115 | assert v_output = v_scoreboard(128) 116 | report "ERROR: Got 0x" & to_hstring(v_output) & ", expected 0x" & to_hstring(v_scoreboard(128)) 117 | severity failure; 118 | report "INFO: Test successful"; 119 | 120 | -- check for existing keys 121 | report "INFO: Test 4: Check hasKey() method"; 122 | for i in 0 to 255 loop 123 | assert sv_dict.hasKey(integer'image(i)) 124 | report "ERROR: Key" & integer'image(i) & " should exist in dictionary" 125 | severity failure; 126 | end loop; 127 | assert not(sv_dict.hasKey("AFFE")) 128 | report "ERROR: Key AFFE shouldn't exist in dictionary" 129 | severity failure; 130 | report "INFO: Test successful"; 131 | 132 | -- iterate up over all entries 133 | report "INFO: Test 5: Iterate up over all entries"; 134 | sv_dict.setIter; 135 | for i in 0 to 255 loop 136 | v_key := new string'(sv_dict.iter(UP)); 137 | assert v_key.all = integer'image(i) 138 | report "ERROR: Got key " & v_key.all & ", expected " & integer'image(i) 139 | severity failure; 140 | sv_dict.get(v_key.all, v_output); 141 | assert v_key.all = integer'image(i) and v_output = v_scoreboard(i) 142 | report "ERROR: Got 0x" & to_hstring(v_output) & ", expected 0x" & to_hstring(v_scoreboard(i)) 143 | severity failure; 144 | end loop; 145 | v_last_key := v_key; 146 | v_key := new string'(sv_dict.iter(UP)); 147 | assert v_key.all = v_last_key.all 148 | report "ERROR: Got key " & v_key.all & ", expected key" & v_last_key.all 149 | severity failure; 150 | report "INFO: Test successful"; 151 | 152 | -- iterate down over all entries 153 | report "INFO: Test 6: Iterate down over all entries"; 154 | sv_dict.setIter(HEAD); 155 | for i in 255 downto 0 loop 156 | v_key := new string'(sv_dict.iter(DOWN)); 157 | assert v_key.all = integer'image(i) 158 | report "ERROR: Got key " & v_key.all & ", expected " & integer'image(i) 159 | severity failure; 160 | sv_dict.get(v_key.all, v_output); 161 | assert v_key.all = integer'image(i) and v_output = v_scoreboard(i) 162 | report "ERROR: Got 0x" & to_hstring(v_output) & ", expected 0x" & to_hstring(v_scoreboard(i)) 163 | severity failure; 164 | end loop; 165 | v_last_key := v_key; 166 | v_key := new string'(sv_dict.iter(DOWN)); 167 | assert v_key.all = v_last_key.all 168 | report "ERROR: Got key " & v_key.all & ", expected key" & v_last_key.all 169 | severity failure; 170 | deallocate(v_key); 171 | report "INFO: Test successful"; 172 | 173 | -- merge 2 dictionaries 174 | -- fill dictionary and check count 175 | report "INFO: Test 7: Merge dictionaries"; 176 | for i in 256 to 511 loop 177 | v_input := v_random.RandSlv(8); 178 | sv_dact.set(integer'image(i), v_input); 179 | v_scoreboard(i) := v_input; 180 | assert sv_dact.size = i-255 181 | report "ERROR: Dict should have " & to_string(i-255) & " entries" 182 | severity failure; 183 | end loop; 184 | -- merge dictionaries 185 | merge(sv_dict, sv_dact, sv_duct); 186 | -- read all entries and check for correct data 187 | for i in 0 to 511 loop 188 | sv_duct.get(integer'image(i), v_output); 189 | assert v_output = v_scoreboard(i) 190 | report "ERROR: Got 0x" & to_hstring(v_output) & ", expected 0x" & to_hstring(v_scoreboard(i)) 191 | severity failure; 192 | end loop; 193 | report "INFO: Test successful"; 194 | 195 | -- Remove key/value pair from head of dictionary 196 | report "INFO: Test 8: Removing entry from head of dictionary"; 197 | sv_dict.del("255"); 198 | assert not(sv_dict.hasKey("255")) 199 | report "ERROR: Key 255 shouldn't exist in dictionary" 200 | severity failure; 201 | report "INFO: Test successful"; 202 | 203 | -- Remove key/value pair from head of dictionary 204 | report "INFO: Test 9: Removing entry from middle of dictionary"; 205 | sv_dict.del("127"); 206 | assert not(sv_dict.hasKey("127")) 207 | report "ERROR: Key 127 shouldn't exist in dictionary" 208 | severity failure; 209 | report "INFO: Test successful"; 210 | 211 | -- Remove key/value pair from head of dictionary 212 | report "INFO: Test 10: Removing entry from beginning of dictionary"; 213 | sv_dict.del("0"); 214 | assert not(sv_dict.hasKey("0")) 215 | report "ERROR: Key 0 shouldn't exist in dictionary" 216 | severity failure; 217 | report "INFO: Test successful"; 218 | 219 | -- Remove key/value pair from head of dictionary 220 | report "INFO: Test 11: Clear all entries from dictionary"; 221 | sv_dict.clear; 222 | assert sv_dict.size = 0 223 | report "ERROR: Dict should be empty" 224 | severity failure; 225 | report "INFO: Test successful"; 226 | 227 | report "INFO: t_dict test finished successfully"; 228 | wait; 229 | end process DictTestP; 230 | 231 | 232 | end architecture sim; 233 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2014 - 2022 by Torsten Meissner 2 | ## 3 | ## Licensed under the Apache License, Version 2.0 (the "License"); 4 | ## you may not use this file except in compliance with the License. 5 | ## You may obtain a copy of the License at 6 | ## 7 | ## https://www.apache.org/licenses/LICENSE-2.0 8 | ## 9 | ## Unless required by applicable law or agreed to in writing, software 10 | ## distributed under the License is distributed on an "AS IS" BASIS, 11 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ## See the License for the specific language governing permissions and 13 | ## limitations under the License. 14 | 15 | 16 | 17 | SIM_SRC := ../sim 18 | SYN_SRC := ../syn 19 | CMN_SRC := ../common 20 | OSVVM_DIR := ../lib/OSVVM 21 | OSVVM_SRC := \ 22 | $(OSVVM_DIR)/TextUtilPkg.vhd \ 23 | $(OSVVM_DIR)/ResolutionPkg.vhd \ 24 | $(OSVVM_DIR)/NamePkg.vhd \ 25 | $(OSVVM_DIR)/OsvvmGlobalPkg.vhd \ 26 | $(OSVVM_DIR)/VendorCovApiPkg.vhd \ 27 | $(OSVVM_DIR)/TranscriptPkg.vhd \ 28 | $(OSVVM_DIR)/AlertLogPkg.vhd \ 29 | $(OSVVM_DIR)/NameStorePkg.vhd \ 30 | $(OSVVM_DIR)/MessageListPkg.vhd \ 31 | $(OSVVM_DIR)/SortListPkg_int.vhd \ 32 | $(OSVVM_DIR)/RandomBasePkg.vhd \ 33 | $(OSVVM_DIR)/RandomPkg.vhd \ 34 | $(OSVVM_DIR)/RandomProcedurePkg.vhd \ 35 | $(OSVVM_DIR)/CoveragePkg.vhd \ 36 | $(OSVVM_DIR)/ScoreboardGenericPkg.vhd \ 37 | $(OSVVM_DIR)/ScoreboardPkg_slv.vhd \ 38 | $(OSVVM_DIR)/ScoreboardPkg_int.vhd \ 39 | $(OSVVM_DIR)/ResizePkg.vhd \ 40 | $(OSVVM_DIR)/MemoryPkg.vhd \ 41 | $(OSVVM_DIR)/TbUtilPkg.vhd \ 42 | $(OSVVM_DIR)/ReportPkg.vhd \ 43 | $(OSVVM_DIR)/OsvvmTypesPkg.vhd \ 44 | $(OSVVM_DIR)/OsvvmContext.vhd 45 | VHD08_SRC := vhdl_2008 46 | VHD_STD := 08 47 | 48 | 49 | .PHONY: all 50 | all: queue dict stack sim wishbone uart spi 51 | 52 | 53 | OsvvmContext.o: $(OSVVM_SRC) 54 | ghdl -a --std=$(VHD_STD) --work=osvvm -Wno-hide $(OSVVM_SRC) 55 | 56 | 57 | UtilsP.o: $(CMN_SRC)/UtilsP.vhd 58 | ghdl -a --std=$(VHD_STD) --work=libvhdl $< 59 | 60 | 61 | # Default rule for compiling packages 62 | %P.o: $(SIM_SRC)/%P.vhd 63 | ghdl -a --std=$(VHD_STD) --work=libvhdl $< 64 | 65 | 66 | queuet: OsvvmContext.o AssertP.o QueueP.o QueueT.vhd 67 | ghdl -a --std=$(VHD_STD) QueueT.vhd 68 | ghdl -e --std=$(VHD_STD) $@ 69 | 70 | 71 | dictt: OsvvmContext.o DictP.o DictT.vhd 72 | ghdl -a --std=$(VHD_STD) DictT.vhd 73 | ghdl -e --std=$(VHD_STD) $@ 74 | 75 | stackt: OsvvmContext.o AssertP.o StackP.o StackT.vhd 76 | ghdl -a --std=$(VHD_STD) StackT.vhd 77 | ghdl -e --std=$(VHD_STD) $@ 78 | 79 | simt: OsvvmContext.o UtilsP.o AssertP.o QueueP.o SimP.o SimT.vhd 80 | ghdl -a --std=$(VHD_STD) SimT.vhd 81 | ghdl -e --std=$(VHD_STD) $@ 82 | 83 | spit: OsvvmContext.o UtilsP.o QueueP.o AssertP.o SimP.o $(SYN_SRC)/SpiSlaveE.vhd $(SYN_SRC)/SpiMasterE.vhd SpiT.vhd 84 | ghdl -a --std=$(VHD_STD) -fpsl $(SYN_SRC)/SpiSlaveE.vhd $(SYN_SRC)/SpiMasterE.vhd 85 | ghdl -a --std=$(VHD_STD) -fpsl SpiT.vhd 86 | ghdl -e --std=$(VHD_STD) $@ 87 | 88 | uartt: OsvvmContext.o UtilsP.o $(SYN_SRC)/UartTx.vhd $(SYN_SRC)/UartRx.vhd UartT.vhd 89 | ghdl -a --std=$(VHD_STD) -fpsl $(SYN_SRC)/UartTx.vhd $(SYN_SRC)/UartRx.vhd 90 | ghdl -a --std=$(VHD_STD) -fpsl UartT.vhd 91 | ghdl -e --std=$(VHD_STD) $@ 92 | 93 | 94 | .PHONY: spi 95 | spi: spit 96 | ghdl -r --std=$(VHD_STD) $@t 97 | # --wave=$@t.ghw 98 | 99 | .PHONY: uart 100 | uart: uartt 101 | ghdl -r --std=$(VHD_STD) $@t --wave=$@t.ghw 102 | 103 | 104 | wishbonet: OsvvmContext.o AssertP.o SimP.o QueueP.o DictP.o UtilsP.o $(SYN_SRC)/WishBoneCheckerE.vhd \ 105 | $(SYN_SRC)/WishBoneP.vhd $(SYN_SRC)/WishBoneMasterE.vhd $(SYN_SRC)/WishBoneSlaveE.vhd WishBoneT.vhd 106 | ghdl -a --std=$(VHD_STD) -fpsl $(SYN_SRC)/WishBoneP.vhd 107 | ghdl -a --std=$(VHD_STD) -fpsl $(SYN_SRC)/WishBoneCheckerE.vhd $(SYN_SRC)/WishBoneMasterE.vhd $(SYN_SRC)/WishBoneSlaveE.vhd 108 | ghdl -a --std=$(VHD_STD) -fpsl WishBoneT.vhd 109 | ghdl -e --std=$(VHD_STD) $@ 110 | 111 | .PHONY: wishbone 112 | wishbone: wishbonet 113 | ghdl -r --std=$(VHD_STD) $@t --wave=$@t.ghw --psl-report=$@_psl_coverage.json 114 | 115 | 116 | # Default rule for running simulation 117 | %: %t 118 | ghdl -r --std=$(VHD_STD) $@t 119 | 120 | 121 | .PHONY: clean 122 | clean: 123 | rm -f *.o 124 | rm -f *.cf 125 | rm -f *.ghw 126 | rm -f queuet 127 | rm -f dictt 128 | rm -f stackt 129 | rm -f stringt 130 | rm -f simt 131 | rm -f spit 132 | rm -f uartt 133 | rm -f wishbonet 134 | rm -f *.json 135 | 136 | .PHONY: distclean 137 | distclean: clean 138 | -------------------------------------------------------------------------------- /test/QueueT.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | library libvhdl; 22 | use libvhdl.AssertP.all; 23 | 24 | library osvvm; 25 | use osvvm.RandomPkg.all; 26 | 27 | 28 | 29 | entity QueueT is 30 | end entity QueueT; 31 | 32 | 33 | 34 | architecture sim of QueueT is 35 | 36 | 37 | constant C_QUEUE_DEPTH : natural := 64; 38 | 39 | package SlvQueue is new libvhdl.QueueP 40 | generic map ( 41 | QUEUE_TYPE => std_logic_vector(63 downto 0), 42 | MAX_LEN => C_QUEUE_DEPTH, 43 | to_string => to_hstring 44 | ); 45 | 46 | shared variable sv_simple_queue : SlvQueue.t_simple_queue; 47 | shared variable sv_list_queue : SlvQueue.t_list_queue; 48 | 49 | 50 | begin 51 | 52 | 53 | QueueInitP : process is 54 | begin 55 | sv_simple_queue.init(false); 56 | sv_list_queue.init(false); 57 | wait; 58 | end process QueueInitP; 59 | 60 | 61 | 62 | SimpleQueueTestP : process is 63 | variable v_data : std_logic_vector(63 downto 0); 64 | variable v_random : RandomPType; 65 | begin 66 | -- check initial emptiness 67 | assert_true(sv_simple_queue.is_empty, "Queue should be empty!"); 68 | -- Fill queue 69 | v_random.InitSeed(v_random'instance_name); 70 | for i in 0 to C_QUEUE_DEPTH-1 loop 71 | v_data := v_random.RandSlv(64); 72 | sv_simple_queue.push(v_data); 73 | end loop; 74 | -- check that it's full 75 | assert_true(sv_simple_queue.is_full, "Queue should be full!"); 76 | -- Check number of entries 77 | assert_equal(sv_simple_queue.fillstate, C_QUEUE_DEPTH, "Queue should have" & integer'image(C_QUEUE_DEPTH) & "entries"); 78 | -- empty the queue 79 | v_random.InitSeed(v_random'instance_name); 80 | for i in 0 to C_QUEUE_DEPTH-1 loop 81 | sv_simple_queue.pop(v_data); 82 | assert_equal(v_data, v_random.RandSlv(64)); 83 | end loop; 84 | -- check emptiness 85 | assert_true(sv_simple_queue.is_empty, "Queue should be empty!"); 86 | report "INFO: t_simple_queue test finished successfully"; 87 | wait; 88 | end process SimpleQueueTestP; 89 | 90 | 91 | ListQueueTestP : process is 92 | variable v_data : std_logic_vector(63 downto 0); 93 | variable v_random : RandomPType; 94 | begin 95 | -- check initial emptiness 96 | assert_true(sv_list_queue.is_empty, "Queue should be empty!"); 97 | -- Fill queue 98 | v_random.InitSeed(v_random'instance_name); 99 | for i in 0 to C_QUEUE_DEPTH-1 loop 100 | v_data := v_random.RandSlv(64); 101 | sv_list_queue.push(v_data); 102 | end loop; 103 | -- check that it's full 104 | assert_true(sv_list_queue.is_full, "Queue should be full!"); 105 | -- Check number of entries 106 | assert_equal(sv_list_queue.fillstate, C_QUEUE_DEPTH, "Queue should have" & integer'image(C_QUEUE_DEPTH) & "entries"); 107 | -- empty the queue 108 | v_random.InitSeed(v_random'instance_name); 109 | for i in 0 to C_QUEUE_DEPTH-1 loop 110 | sv_list_queue.pop(v_data); 111 | assert_equal(v_data, v_random.RandSlv(64)); 112 | end loop; 113 | -- check emptiness 114 | assert_true(sv_list_queue.is_empty, "Queue should be empty!"); 115 | report "INFO: t_list_queue test finished successfully"; 116 | wait; 117 | end process ListQueueTestP; 118 | 119 | 120 | end architecture sim; 121 | -------------------------------------------------------------------------------- /test/SimT.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | 22 | library osvvm; 23 | use osvvm.RandomPkg.all; 24 | 25 | library libvhdl; 26 | use libvhdl.AssertP.all; 27 | use libvhdl.SimP.all; 28 | use libvhdl.UtilsP.all; 29 | 30 | 31 | 32 | entity SimT is 33 | end entity SimT; 34 | 35 | 36 | 37 | architecture sim of SimT is 38 | 39 | 40 | --* testbench global clock period 41 | constant C_PERIOD : time := 5 ns; 42 | --* SPI data transfer data width 43 | constant C_DATA_WIDTH : natural := 8; 44 | 45 | signal s_tests_done : boolean_vector(0 to 1) := (others => false); 46 | 47 | signal s_clk : std_logic := '0'; 48 | 49 | signal s_sclk : std_logic; 50 | signal s_ste : std_logic; 51 | signal s_mosi : std_logic; 52 | signal s_miso : std_logic; 53 | 54 | package SlvQueue is new libvhdl.QueueP 55 | generic map ( 56 | QUEUE_TYPE => std_logic_vector(C_DATA_WIDTH-1 downto 0), 57 | MAX_LEN => 32, 58 | to_string => to_hstring 59 | ); 60 | 61 | shared variable sv_mosi_queue : SlvQueue.t_list_queue; 62 | shared variable sv_miso_queue : SlvQueue.t_list_queue; 63 | 64 | 65 | begin 66 | 67 | 68 | s_clk <= not(s_clk) after C_PERIOD when not(and_reduce(s_tests_done)) else '0'; 69 | 70 | 71 | QueueInitP : process is 72 | begin 73 | sv_mosi_queue.init(false); 74 | sv_miso_queue.init(false); 75 | wait; 76 | end process QueueInitP; 77 | 78 | 79 | SimTestP : process is 80 | variable v_time : time; 81 | begin 82 | wait until s_clk = '1'; 83 | v_time := now; 84 | wait_cycles(s_clk, 10); 85 | assert (now - v_time) = C_PERIOD * 20 86 | severity failure; 87 | s_tests_done(0) <= true; 88 | report "INFO: wait_cycles() procedure tests finished successfully"; 89 | wait; 90 | end process SimTestP; 91 | 92 | 93 | -- Unit test of spi master procedure, checks all combinations 94 | -- of cpol & cpha against spi slave procedure 95 | SpiMasterP : process is 96 | variable v_send_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0'); 97 | variable v_receive_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0'); 98 | variable v_queue_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0'); 99 | variable v_random : RandomPType; 100 | begin 101 | v_random.InitSeed(v_random'instance_name); 102 | for direction in 0 to 1 loop 103 | for mode in 0 to 3 loop 104 | for i in 0 to 255 loop 105 | v_send_data := v_random.RandSlv(C_DATA_WIDTH); 106 | sv_mosi_queue.push(v_send_data); 107 | spi_master (data_in => v_send_data, 108 | data_out => v_receive_data, 109 | sclk => s_sclk, 110 | ste => s_ste, 111 | mosi => s_mosi, 112 | miso => s_miso, 113 | dir => direction, 114 | cpol => mode / 2, 115 | cpha => mode mod 2, 116 | period => 1 us 117 | ); 118 | sv_miso_queue.pop(v_queue_data); 119 | assert_equal(v_receive_data, v_queue_data); 120 | end loop; 121 | end loop; 122 | end loop; 123 | wait; 124 | end process SpiMasterP; 125 | 126 | 127 | -- Unit test of spi slave procedure, checks all combinations 128 | -- of cpol & cpha against spi master procedure 129 | SpiSlaveP : process is 130 | variable v_send_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0'); 131 | variable v_receive_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0'); 132 | variable v_queue_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0'); 133 | variable v_random : RandomPType; 134 | begin 135 | v_random.InitSeed(v_random'instance_name); 136 | for direction in 0 to 1 loop 137 | for mode in 0 to 3 loop 138 | for i in 0 to 255 loop 139 | v_send_data := v_random.RandSlv(C_DATA_WIDTH); 140 | sv_miso_queue.push(v_send_data); 141 | spi_slave (data_in => v_send_data, 142 | data_out => v_receive_data, 143 | sclk => s_sclk, 144 | ste => s_ste, 145 | mosi => s_mosi, 146 | miso => s_miso, 147 | dir => direction, 148 | cpol => mode / 2, 149 | cpha => mode mod 2 150 | ); 151 | sv_mosi_queue.pop(v_queue_data); 152 | assert_equal(v_receive_data, v_queue_data); 153 | end loop; 154 | end loop; 155 | end loop; 156 | report "INFO: All tests of valid spi_master() & spi_slave() combinations finished successfully"; 157 | s_tests_done(1) <= true; 158 | wait; 159 | end process SpiSlaveP; 160 | 161 | 162 | end architecture sim; 163 | -------------------------------------------------------------------------------- /test/SpiT.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | library osvvm; 22 | use osvvm.RandomPkg.all; 23 | 24 | library libvhdl; 25 | use libvhdl.SimP.all; 26 | use libvhdl.UtilsP.all; 27 | 28 | use std.env.all; 29 | 30 | 31 | entity SpiT is 32 | end entity SpiT; 33 | 34 | 35 | 36 | architecture sim of SpiT is 37 | 38 | 39 | component SpiMasterE is 40 | generic ( 41 | G_DATA_WIDTH : positive := 8; 42 | G_DATA_DIR : natural range 0 to 1 := 0; 43 | G_SPI_CPOL : natural range 0 to 1 := 0; 44 | G_SPI_CPHA : natural range 0 to 1 := 0; 45 | G_SCLK_DIVIDER : positive range 6 to positive'high := 10 46 | ); 47 | port ( 48 | --+ system if 49 | Reset_n_i : in std_logic; 50 | Clk_i : in std_logic; 51 | --+ SPI slave if 52 | SpiSclk_o : out std_logic; 53 | SpiSte_o : out std_logic; 54 | SpiMosi_o : out std_logic; 55 | SpiMiso_i : in std_logic; 56 | --+ local VAI if 57 | Data_i : in std_logic_vector(G_DATA_WIDTH-1 downto 0); 58 | DataValid_i : in std_logic; 59 | DataAccept_o : out std_logic; 60 | Data_o : out std_logic_vector(G_DATA_WIDTH-1 downto 0); 61 | DataValid_o : out std_logic; 62 | DataAccept_i : in std_logic 63 | ); 64 | end component SpiMasterE; 65 | 66 | 67 | component SpiSlaveE is 68 | generic ( 69 | G_DATA_WIDTH : positive := 8; 70 | G_DATA_DIR : natural range 0 to 1 := 0; 71 | G_SPI_CPOL : natural range 0 to 1 := 0; 72 | G_SPI_CPHA : natural range 0 to 1 := 0 73 | ); 74 | port ( 75 | --+ system if 76 | Reset_n_i : in std_logic; 77 | Clk_i : in std_logic; 78 | --+ SPI slave if 79 | SpiSclk_i : in std_logic; 80 | SpiSte_i : in std_logic; 81 | SpiMosi_i : in std_logic; 82 | SpiMiso_o : out std_logic; 83 | --+ local VAI if 84 | Data_i : in std_logic_vector(G_DATA_WIDTH-1 downto 0); 85 | DataValid_i : in std_logic; 86 | DataAccept_o : out std_logic; 87 | Data_o : out std_logic_vector(G_DATA_WIDTH-1 downto 0); 88 | DataValid_o : out std_logic; 89 | DataAccept_i : in std_logic 90 | ); 91 | end component SpiSlaveE; 92 | 93 | 94 | --* testbench global clock period 95 | constant C_PERIOD : time := 5 ns; 96 | --* SPI data transfer data width 97 | constant C_DATA_WIDTH : natural := 8; 98 | 99 | --* testbench global clock 100 | signal s_clk : std_logic := '0'; 101 | --* testbench global reset 102 | signal s_reset_n : std_logic := '0'; 103 | 104 | --* SPI mode range subtype 105 | subtype t_spi_mode is natural range 0 to 3; 106 | 107 | --+ test done array with entry for each test 108 | signal s_test_done : boolean_vector(t_spi_mode'low to 4*t_spi_mode'high+3) := (others => false); 109 | 110 | package SlvQueue is new libvhdl.QueueP 111 | generic map ( 112 | QUEUE_TYPE => std_logic_vector(C_DATA_WIDTH-1 downto 0), 113 | MAX_LEN => 32, 114 | to_string => to_hstring 115 | ); 116 | 117 | 118 | begin 119 | 120 | 121 | --* testbench global clock 122 | s_clk <= not(s_clk) after C_PERIOD/2; 123 | --* testbench global reset 124 | s_reset_n <= '1' after 100 ns; 125 | 126 | 127 | ControlP : process is 128 | begin 129 | wait until and s_test_done; 130 | finish(0); 131 | end process ControlP; 132 | 133 | 134 | --* Generate tests for both direction 135 | DataDirectionG : for direction in 0 to 1 generate 136 | 137 | 138 | --* Generate SpiMasterE tests for all 4 modes 139 | SpiMastersG : for mode in t_spi_mode'low to t_spi_mode'high generate 140 | 141 | 142 | signal s_sclk : std_logic; 143 | signal s_ste : std_logic; 144 | signal s_mosi : std_logic; 145 | signal s_miso : std_logic; 146 | 147 | signal s_din : std_logic_vector(C_DATA_WIDTH-1 downto 0); 148 | signal s_din_valid : std_logic; 149 | signal s_din_accept : std_logic; 150 | signal s_dout : std_logic_vector(C_DATA_WIDTH-1 downto 0); 151 | signal s_dout_valid : std_logic; 152 | signal s_dout_accept : std_logic; 153 | 154 | shared variable sv_mosi_queue : SlvQueue.t_list_queue; 155 | shared variable sv_miso_queue : SlvQueue.t_list_queue; 156 | 157 | 158 | begin 159 | 160 | 161 | QueueInitP : process is 162 | begin 163 | sv_mosi_queue.init(false); 164 | sv_miso_queue.init(false); 165 | wait; 166 | end process QueueInitP; 167 | 168 | 169 | --* Stimuli generator and BFM for the valid-accept interface 170 | --* on the local data input of the DUT 171 | --* 172 | --* Generates random stimuli and serves it to the 173 | --* valid-accept interface at the input of the DUT 174 | --* 175 | --* The stimuli data is also pushed into the mosi queue 176 | --* which serves as simple abstract reference model 177 | --* of the SPI transmit (master -> slave) channel 178 | SpiMasterStimP : process is 179 | variable v_random : RandomPType; 180 | begin 181 | v_random.InitSeed(v_random'instance_name); 182 | s_din_valid <= '0'; 183 | s_din <= (others => '0'); 184 | wait until s_reset_n = '1'; 185 | for i in 0 to integer'(2**C_DATA_WIDTH-1) loop 186 | s_din <= v_random.RandSlv(C_DATA_WIDTH); 187 | s_din_valid <= '1'; 188 | wait until rising_edge(s_clk) and s_din_accept = '1'; 189 | s_din_valid <= '0'; 190 | sv_mosi_queue.push(s_din); 191 | wait until rising_edge(s_clk); 192 | end loop; 193 | wait; 194 | end process SpiMasterStimP; 195 | 196 | 197 | --* DUT: SpiMasterE component 198 | i_SpiMasterE : SpiMasterE 199 | generic map ( 200 | G_DATA_WIDTH => C_DATA_WIDTH, 201 | G_DATA_DIR => direction, 202 | G_SPI_CPOL => mode / 2, 203 | G_SPI_CPHA => mode mod 2, 204 | G_SCLK_DIVIDER => 10 205 | ) 206 | port map ( 207 | --+ system if 208 | Reset_n_i => s_reset_n, 209 | Clk_i => s_clk, 210 | --+ SPI slave if 211 | SpiSclk_o => s_sclk, 212 | SpiSte_o => s_ste, 213 | SpiMosi_o => s_mosi, 214 | SpiMiso_i => s_miso, 215 | --+ local VAI if 216 | Data_i => s_din, 217 | DataValid_i => s_din_valid, 218 | DataAccept_o => s_din_accept, 219 | Data_o => s_dout, 220 | DataValid_o => s_dout_valid, 221 | DataAccept_i => s_dout_accept 222 | ); 223 | 224 | 225 | --* Checker and BFM for the valid-accept interface 226 | --* on the local data output of the DUT 227 | --* 228 | --* Reads the output of the DUT and compares it to 229 | --* data popped from the miso queue which serves as 230 | --* simple abstract reference model of the SPI receive 231 | --* (slave -> master) channel 232 | SpiMasterCheckP : process is 233 | variable v_queue_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0'); 234 | begin 235 | s_dout_accept <= '0'; 236 | wait until s_reset_n = '1'; 237 | for i in 0 to integer'(2**C_DATA_WIDTH-1) loop 238 | wait until rising_edge(s_clk) and s_dout_valid = '1'; 239 | s_dout_accept <= '1'; 240 | sv_miso_queue.pop(v_queue_data); 241 | assert s_dout = v_queue_data 242 | report "SPI master MISO error: Received 0x" & to_hstring(s_dout) & ", expected 0x" & to_hstring(v_queue_data) 243 | severity failure; 244 | wait until rising_edge(s_clk); 245 | s_dout_accept <= '0'; 246 | end loop; 247 | report "INFO: SpiMaster (direction=" & to_string(direction) & ", mode=" & to_string(mode) & ") test successfully"; 248 | s_test_done(mode+direction*4) <= true; 249 | wait; 250 | end process SpiMasterCheckP; 251 | 252 | 253 | --* Stimuli generator and BFM for the SPI slave 254 | --* interface on the SPI miso input of the DUT 255 | --* 256 | --* Generates random stimuli and serves it to the 257 | --* SPI interface at the input of the DUT 258 | --* 259 | --* The stimuli data is also pushed into the miso queue 260 | --* which serves as simple abstract reference model 261 | --* of the SPI receive (slave -> master) channel 262 | --* 263 | --* Furthermore the data received by the SPI slave BFM 264 | --* is checked against data popped from the mosi queue 265 | --* which serves as simple abstract reference model of 266 | --* the SPI receive (master -> slave) channel 267 | SpiSlaveP : process is 268 | variable v_send_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0'); 269 | variable v_receive_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0'); 270 | variable v_queue_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0'); 271 | variable v_random : RandomPType; 272 | begin 273 | v_random.InitSeed(v_random'instance_name); 274 | s_miso <= 'Z'; 275 | wait until s_reset_n = '1'; 276 | for i in 0 to integer'(2**C_DATA_WIDTH-1) loop 277 | v_send_data := v_random.RandSlv(C_DATA_WIDTH); 278 | sv_miso_queue.push(v_send_data); 279 | spi_slave (data_in => v_send_data, 280 | data_out => v_receive_data, 281 | sclk => s_sclk, 282 | ste => s_ste, 283 | mosi => s_mosi, 284 | miso => s_miso, 285 | dir => direction, 286 | cpol => mode / 2, 287 | cpha => mode mod 2 288 | ); 289 | sv_mosi_queue.pop(v_queue_data); 290 | assert v_receive_data = v_queue_data 291 | report "SPI master MOSI error: Received 0x" & to_hstring(v_receive_data) & ", expected 0x" & to_hstring(v_queue_data) 292 | severity failure; 293 | end loop; 294 | wait; 295 | end process SpiSlaveP; 296 | 297 | 298 | end generate SpiMastersG; 299 | 300 | 301 | --* Generate SpiMasterE tests for all 4 modes 302 | SpiSlavesG : for mode in t_spi_mode'low to t_spi_mode'high generate 303 | 304 | 305 | signal s_sclk : std_logic; 306 | signal s_ste : std_logic; 307 | signal s_mosi : std_logic; 308 | signal s_miso : std_logic; 309 | 310 | signal s_din : std_logic_vector(C_DATA_WIDTH-1 downto 0); 311 | signal s_din_valid : std_logic; 312 | signal s_din_accept : std_logic; 313 | signal s_dout : std_logic_vector(C_DATA_WIDTH-1 downto 0); 314 | signal s_dout_valid : std_logic; 315 | signal s_dout_accept : std_logic; 316 | 317 | shared variable sv_mosi_queue : SlvQueue.t_list_queue; 318 | shared variable sv_miso_queue : SlvQueue.t_list_queue; 319 | 320 | 321 | begin 322 | 323 | 324 | QueueInitP : process is 325 | begin 326 | sv_mosi_queue.init(false); 327 | sv_miso_queue.init(false); 328 | wait; 329 | end process QueueInitP; 330 | 331 | 332 | --* Unit test of spi master procedure, checks all combinations 333 | --* of cpol & cpha against spi slave procedure 334 | SpiMasterP : process is 335 | variable v_send_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0'); 336 | variable v_receive_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0'); 337 | variable v_queue_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0'); 338 | variable v_random : RandomPType; 339 | begin 340 | v_random.InitSeed(v_random'instance_name); 341 | s_sclk <= '1'; 342 | s_ste <= '1'; 343 | s_mosi <= '1'; 344 | wait until s_reset_n = '1'; 345 | for i in 0 to integer'(2**C_DATA_WIDTH-1) loop 346 | v_send_data := v_random.RandSlv(C_DATA_WIDTH); 347 | sv_mosi_queue.push(v_send_data); 348 | spi_master (data_in => v_send_data, 349 | data_out => v_receive_data, 350 | sclk => s_sclk, 351 | ste => s_ste, 352 | mosi => s_mosi, 353 | miso => s_miso, 354 | dir => direction, 355 | cpol => mode / 2, 356 | cpha => mode mod 2, 357 | period => C_PERIOD * 10 358 | ); 359 | sv_miso_queue.pop(v_queue_data); 360 | assert v_receive_data = v_queue_data 361 | report "SPI slave MISO error: Received 0x" & to_hstring(v_receive_data) & ", expected 0x" & to_hstring(v_queue_data) 362 | severity failure; 363 | end loop; 364 | report "INFO: SpiSlave (direction=" & to_string(direction) & ", mode=" & to_string(mode) & ") test successfully"; 365 | s_test_done(mode+8+direction*4) <= true; 366 | wait; 367 | end process SpiMasterP; 368 | 369 | 370 | SpiSlaveStimP : process is 371 | variable v_random : RandomPType; 372 | begin 373 | v_random.InitSeed(v_random'instance_name); 374 | s_din_valid <= '0'; 375 | s_din <= (others => '0'); 376 | wait until s_reset_n = '1'; 377 | for i in 0 to integer'(2**C_DATA_WIDTH-1) loop 378 | s_din <= v_random.RandSlv(C_DATA_WIDTH); 379 | s_din_valid <= '1'; 380 | wait until rising_edge(s_clk) and s_din_accept = '1'; 381 | s_din_valid <= '0'; 382 | sv_miso_queue.push(s_din); 383 | wait until rising_edge(s_clk) and s_dout_valid = '1'; 384 | end loop; 385 | wait; 386 | end process SpiSlaveStimP; 387 | 388 | 389 | i_SpiSlaveE : entity work.SpiSlaveE 390 | generic map ( 391 | G_DATA_WIDTH => C_DATA_WIDTH, 392 | G_DATA_DIR => direction, 393 | G_SPI_CPOL => mode / 2, 394 | G_SPI_CPHA => mode mod 2 395 | ) 396 | port map ( 397 | --+ system if 398 | Reset_n_i => s_reset_n, 399 | Clk_i => s_clk, 400 | --+ SPI slave if 401 | SpiSclk_i => s_sclk, 402 | SpiSte_i => s_ste, 403 | SpiMosi_i => s_mosi, 404 | SpiMiso_o => s_miso, 405 | --+ local VAI if 406 | Data_i => s_din, 407 | DataValid_i => s_din_valid, 408 | DataAccept_o => s_din_accept, 409 | Data_o => s_dout, 410 | DataValid_o => s_dout_valid, 411 | DataAccept_i => s_dout_accept 412 | ); 413 | 414 | 415 | SpiSlaveCheckP : process is 416 | variable v_queue_data : std_logic_vector(C_DATA_WIDTH-1 downto 0) := (others => '0'); 417 | begin 418 | s_dout_accept <= '0'; 419 | wait until s_reset_n = '1'; 420 | for i in 0 to integer'(2**C_DATA_WIDTH-1) loop 421 | wait until rising_edge(s_clk) and s_dout_valid = '1'; 422 | s_dout_accept <= '1'; 423 | sv_mosi_queue.pop(v_queue_data); 424 | assert s_dout = v_queue_data 425 | report "SPI slave MOSI error: Received 0x" & to_hstring(s_dout) & ", expected 0x" & to_hstring(v_queue_data) 426 | severity failure; 427 | wait until rising_edge(s_clk); 428 | s_dout_accept <= '0'; 429 | end loop; 430 | wait; 431 | end process SpiSlaveCheckP; 432 | 433 | 434 | end generate SpiSlavesG; 435 | 436 | 437 | end generate DataDirectionG; 438 | 439 | 440 | end architecture sim; 441 | -------------------------------------------------------------------------------- /test/StackT.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | library osvvm; 22 | use osvvm.RandomPkg.all; 23 | 24 | library libvhdl; 25 | use libvhdl.AssertP.all; 26 | 27 | 28 | 29 | entity StackT is 30 | end entity StackT; 31 | 32 | 33 | 34 | architecture sim of StackT is 35 | 36 | 37 | constant C_STACK_DEPTH : natural := 64; 38 | 39 | package SlvStack is new libvhdl.StackP 40 | generic map ( 41 | STACK_TYPE => std_logic_vector(63 downto 0), 42 | MAX_LEN => C_STACK_DEPTH, 43 | to_string => to_hstring 44 | ); 45 | 46 | shared variable sv_stack : SlvStack.t_stack; 47 | 48 | 49 | begin 50 | 51 | 52 | StackInitP : process is 53 | begin 54 | sv_stack.init(false); 55 | wait; 56 | end process StackInitP; 57 | 58 | 59 | StackTestP : process is 60 | variable v_data : std_logic_vector(63 downto 0); 61 | variable v_random : RandomPType; 62 | type t_scoreboard is array (natural range <>) of std_logic_vector(63 downto 0); 63 | variable v_scoreboard : t_scoreboard(0 to C_STACK_DEPTH-1); 64 | begin 65 | -- Check initial emptiness 66 | assert_true(sv_stack.is_empty, "Stack should be empty!"); 67 | -- Fill stack 68 | v_random.InitSeed(v_random'instance_name); 69 | for i in 0 to C_STACK_DEPTH-1 loop 70 | v_data := v_random.RandSlv(64); 71 | v_scoreboard(i) := v_data; 72 | sv_stack.push(v_data); 73 | end loop; 74 | -- Check that it's full 75 | assert_true(sv_stack.is_full, "Stack should be full!"); 76 | -- Check number of entries 77 | assert_equal(sv_stack.fillstate, C_STACK_DEPTH, "Stack should have" & integer'image(C_STACK_DEPTH) & "entries"); 78 | -- Empty the stack 79 | for i in C_STACK_DEPTH-1 downto 0 loop 80 | sv_stack.pop(v_data); 81 | assert_equal(v_data, v_scoreboard(i)); 82 | end loop; 83 | -- Check emptiness 84 | assert_true(sv_stack.is_empty, "Stack should be empty!"); 85 | report "INFO: t_stack test finished successfully"; 86 | wait; 87 | end process StackTestP; 88 | 89 | 90 | end architecture sim; -------------------------------------------------------------------------------- /test/UartT.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | library osvvm; 22 | use osvvm.RandomPkg.all; 23 | use osvvm.CoveragePkg.all; 24 | 25 | use std.env.all; 26 | 27 | 28 | entity UartT is 29 | end entity UartT; 30 | 31 | 32 | 33 | architecture sim of UartT is 34 | 35 | 36 | constant c_data_length : positive range 5 to 9 := 8; 37 | constant c_parity : boolean := true; 38 | constant c_clk_div : natural := 10; 39 | 40 | signal s_reset_n : std_logic := '0'; 41 | signal s_clk : std_logic := '1'; 42 | signal s_tx_data : std_logic_vector(c_data_length-1 downto 0); 43 | signal s_tx_valid : std_logic; 44 | signal s_tx_accept : std_logic; 45 | 46 | signal s_rx_data : std_logic_vector(c_data_length-1 downto 0); 47 | signal s_rx_error : std_logic; 48 | signal s_rx_valid : std_logic; 49 | signal s_rx_accept : std_logic; 50 | 51 | signal s_tx_uart : std_logic := '1'; 52 | signal s_rx_uart : std_logic := '1'; 53 | 54 | type t_error is (NONE, DATA, STOP); 55 | signal s_error_inject : t_error := NONE; 56 | signal s_error_injected : t_error := NONE; 57 | 58 | shared variable sv_uart_err_coverage : CovPType; 59 | 60 | procedure injectError (signal inject : out t_error) is 61 | variable v_injected : boolean; 62 | variable v_random : RandomPType; 63 | begin 64 | v_random.InitSeed(v_random'instance_name & to_string(now)); 65 | loop 66 | -- Wait for new UART transmission 67 | v_injected := false; 68 | wait until s_tx_valid = '1' and s_tx_accept = '1'; 69 | wait until falling_edge(s_tx_uart); 70 | -- Skip start bit 71 | for i in 0 to c_clk_div-1 loop 72 | wait until rising_edge(s_clk); 73 | end loop; 74 | -- Possibly distort one of the data bits 75 | -- and update coverage object 76 | for i in 0 to c_data_length loop 77 | if (not v_injected and v_random.DistValInt(((0, 9), (1, 1))) = 1) then 78 | v_injected := true; 79 | sv_uart_err_coverage.ICover(i); 80 | if (i = c_data_length) then 81 | inject <= STOP; 82 | report "Injected transmit error on stop bit"; 83 | else 84 | inject <= DATA; 85 | report "Injected transmit error on data bit #" & to_string(i); 86 | end if; 87 | end if; 88 | for y in 0 to c_clk_div-1 loop 89 | wait until rising_edge(s_clk); 90 | end loop; 91 | inject <= NONE; 92 | end loop; 93 | end loop; 94 | wait; 95 | end procedure injectError; 96 | 97 | 98 | begin 99 | 100 | 101 | Dut_UartTx : entity work.UartTx 102 | generic map ( 103 | DATA_LENGTH => c_data_length, 104 | PARITY => c_parity, 105 | CLK_DIV => c_clk_div 106 | ) 107 | port map ( 108 | reset_n_i => s_reset_n, 109 | clk_i => s_clk, 110 | data_i => s_tx_data, 111 | valid_i => s_tx_valid, 112 | accept_o => s_tx_accept, 113 | tx_o => s_tx_uart 114 | ); 115 | 116 | 117 | -- Error injection based on random 118 | sv_uart_err_coverage.AddBins("DATA_ERROR", GenBin(0, c_data_length-1)); 119 | sv_uart_err_coverage.AddBins("STOP_ERROR", GenBin(c_data_length)); 120 | injectError(s_error_inject); 121 | s_rx_uart <= s_tx_uart when s_error_inject = NONE else not(s_tx_uart); 122 | 123 | 124 | Dut_UartRx : entity work.UartRx 125 | generic map ( 126 | DATA_LENGTH => c_data_length, 127 | PARITY => c_parity, 128 | CLK_DIV => c_clk_div 129 | ) 130 | port map ( 131 | reset_n_i => s_reset_n, 132 | clk_i => s_clk, 133 | data_o => s_rx_data, 134 | error_o => s_rx_error, 135 | valid_o => s_rx_valid, 136 | accept_i => s_rx_accept, 137 | rx_i => s_rx_uart 138 | ); 139 | 140 | 141 | s_clk <= not s_clk after 5 ns; 142 | s_reset_n <= '1' after 20 ns; 143 | 144 | 145 | -- Store if an error was injected in the current frame 146 | s_error_injected <= s_error_inject when rising_edge(s_clk) and s_error_inject /= NONE else 147 | NONE when s_tx_valid = '1'; 148 | 149 | 150 | TestP : process is 151 | variable v_data : std_logic_vector(c_data_length-1 downto 0); 152 | variable v_random : RandomPType; 153 | begin 154 | v_random.InitSeed(v_random'instance_name); 155 | s_tx_valid <= '0'; 156 | s_rx_accept <= '0'; 157 | s_tx_data <= (others => '0'); 158 | wait until s_reset_n = '1'; 159 | for i in 0 to 2**c_data_length-1 loop 160 | wait until rising_edge(s_clk); 161 | s_tx_valid <= '1'; 162 | s_rx_accept <= '1'; 163 | v_data := v_random.RandSlv(8); 164 | s_tx_data <= v_data; 165 | report "Testcase #" & to_string(i) & ": Transmit 0x" & to_hstring(v_data); 166 | wait until rising_edge(s_clk) and s_tx_accept = '1'; 167 | s_tx_valid <= '0'; 168 | wait until rising_edge(s_clk) and s_rx_valid = '1'; 169 | if s_error_injected /= NONE then 170 | if s_error_injected = DATA then 171 | assert s_rx_data /= v_data 172 | report "Received data 0x" & to_hstring(s_rx_data) & ", expected 0x" & to_hstring(v_data) 173 | severity failure; 174 | end if; 175 | assert s_rx_error = '1' 176 | report "Received error 0b" & to_string(s_rx_error) & ", expected 0b1" 177 | severity failure; 178 | else 179 | assert s_rx_data = v_data 180 | report "Received data 0x" & to_hstring(s_rx_data) & ", expected 0x" & to_hstring(v_data) 181 | severity failure; 182 | assert s_rx_error = '0' 183 | report "Received error 0b" & to_string(s_rx_error) & ", expected 0b0" 184 | severity failure; 185 | end if; 186 | end loop; 187 | wait for 10 us; 188 | sv_uart_err_coverage.SetMessage("UART bit error coverage"); 189 | sv_uart_err_coverage.WriteBin; 190 | finish(0); 191 | end process TestP; 192 | 193 | 194 | end architecture sim; 195 | -------------------------------------------------------------------------------- /test/WishBoneT.tcl: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2014 - 2022 by Torsten Meissner 2 | ## 3 | ## Licensed under the Apache License, Version 2.0 (the "License"); 4 | ## you may not use this file except in compliance with the License. 5 | ## You may obtain a copy of the License at 6 | ## 7 | ## https://www.apache.org/licenses/LICENSE-2.0 8 | ## 9 | ## Unless required by applicable law or agreed to in writing, software 10 | ## distributed under the License is distributed on an "AS IS" BASIS, 11 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ## See the License for the specific language governing permissions and 13 | ## limitations under the License. 14 | 15 | 16 | 17 | set signals [list] 18 | lappend signals "top.WishBoneT.s_wb_reset" 19 | lappend signals "top.WishBoneT.s_wb_clk" 20 | lappend signals "top.WishBoneT.s_wishbone.cyc" 21 | lappend signals "top.WishBoneT.s_wishbone.stb" 22 | lappend signals "top.WishBoneT.s_wishbone.we" 23 | lappend signals "top.WishBoneT.s_wishbone.ack" 24 | lappend signals "top.WishBoneT.s_wishbone.adr" 25 | lappend signals "top.WishBoneT.s_wishbone.wdat" 26 | lappend signals "top.WishBoneT.s_wishbone.rdat" 27 | set num_added [ gtkwave::addSignalsFromList $signals ] 28 | -------------------------------------------------------------------------------- /test/WishBoneT.vhd: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 - 2022 by Torsten Meissner 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | library osvvm; 22 | use osvvm.RandomPkg.all; 23 | use osvvm.CoveragePkg.all; 24 | 25 | library libvhdl; 26 | use libvhdl.AssertP.all; 27 | use libvhdl.SimP.all; 28 | use libvhdl.UtilsP.all; 29 | 30 | library work; 31 | use work.WishBoneP.all; 32 | 33 | library std; 34 | use std.env.all; 35 | 36 | 37 | 38 | entity WishBoneT is 39 | end entity WishBoneT; 40 | 41 | 42 | 43 | architecture sim of WishBoneT is 44 | 45 | 46 | --* testbench global clock period 47 | constant C_PERIOD : time := 5 ns; 48 | --* Wishbone data width 49 | constant C_DATA_WIDTH : natural := 8; 50 | --* Wishbone address width 51 | constant C_ADDRESS_WIDTH : natural := 8; 52 | 53 | signal s_wishbone : t_wishbone_if( 54 | Adr(C_ADDRESS_WIDTH-1 downto 0), 55 | WDat(C_DATA_WIDTH-1 downto 0), 56 | RDat(C_DATA_WIDTH-1 downto 0) 57 | ); 58 | 59 | --* testbench global clock 60 | signal s_wb_clk : std_logic := '1'; 61 | --* testbench global reset 62 | signal s_wb_reset : std_logic := '1'; 63 | 64 | signal s_master_local_wen : std_logic; 65 | signal s_master_local_ren : std_logic; 66 | signal s_master_local_adress : std_logic_vector(C_ADDRESS_WIDTH-1 downto 0); 67 | signal s_master_local_din : std_logic_vector(C_DATA_WIDTH-1 downto 0); 68 | signal s_master_local_dout : std_logic_vector(C_DATA_WIDTH-1 downto 0); 69 | signal s_master_local_ack : std_logic; 70 | signal s_master_local_error : std_logic; 71 | signal s_slave_local_wen : std_logic; 72 | signal s_slave_local_ren : std_logic; 73 | signal s_slave_local_adress : std_logic_vector(C_ADDRESS_WIDTH-1 downto 0); 74 | signal s_slave_local_dout : std_logic_vector(C_DATA_WIDTH-1 downto 0); 75 | signal s_slave_local_din : std_logic_vector(C_DATA_WIDTH-1 downto 0); 76 | 77 | 78 | package SlvQueue is new libvhdl.QueueP 79 | generic map ( 80 | QUEUE_TYPE => std_logic_vector(C_ADDRESS_WIDTH-1 downto 0), 81 | MAX_LEN => 2**C_ADDRESS_WIDTH, 82 | to_string => to_hstring 83 | ); 84 | 85 | shared variable sv_wishbone_queue : SlvQueue.t_list_queue; 86 | 87 | package IntSlvDict is new libvhdl.DictP 88 | generic map (KEY_TYPE => natural, 89 | VALUE_TYPE => std_logic_vector, 90 | key_to_string => to_string, 91 | value_to_string => to_hstring); 92 | 93 | shared variable sv_wb_master_dict : IntSlvDict.t_dict; 94 | shared variable sv_wb_slave_dict : IntSlvDict.t_dict; 95 | 96 | shared variable sv_coverage : CovPType; 97 | 98 | 99 | begin 100 | 101 | 102 | --* testbench global clock 103 | s_wb_clk <= not(s_wb_clk) after C_PERIOD/2; 104 | --* testbench global reset 105 | s_wb_reset <= '0' after C_PERIOD * 5; 106 | 107 | 108 | QueueInitP : process is 109 | begin 110 | sv_wishbone_queue.init(false); 111 | sv_wb_master_dict.init(false); 112 | sv_wb_slave_dict.init(false); 113 | wait; 114 | end process QueueInitP; 115 | 116 | 117 | WbMasterLocalP : process is 118 | variable v_random : RandomPType; 119 | variable v_wbmaster_address : integer; 120 | variable v_master_local_adress : std_logic_vector(C_ADDRESS_WIDTH-1 downto 0); 121 | variable v_wbmaster_data : std_logic_vector(C_DATA_WIDTH-1 downto 0); 122 | begin 123 | v_random.InitSeed(v_random'instance_name); 124 | v_wbmaster_data := (others => '0'); 125 | s_master_local_din <= (others => '0'); 126 | s_master_local_adress <= (others => '0'); 127 | s_master_local_wen <= '0'; 128 | s_master_local_ren <= '0'; 129 | wait until s_wb_reset = '0'; 130 | -- write the wishbone slave registers 131 | sv_coverage.AddBins(GenBin(0)); 132 | sv_coverage.AddBins(GenBin(integer'(2**C_ADDRESS_WIDTH-1))); 133 | sv_coverage.AddBins(GenBin(1, integer'(2**C_ADDRESS_WIDTH-2), 64)); 134 | while not sv_coverage.IsCovered loop 135 | v_wbmaster_address := sv_coverage.RandCovPoint; 136 | v_wbmaster_data := v_random.RandSlv(C_DATA_WIDTH); 137 | s_master_local_din <= v_wbmaster_data; 138 | s_master_local_adress <= uint_to_slv(v_wbmaster_address, C_ADDRESS_WIDTH); 139 | s_master_local_wen <= '1'; 140 | wait until rising_edge(s_wb_clk); 141 | s_master_local_din <= (others => '0'); 142 | s_master_local_adress <= (others => '0'); 143 | s_master_local_wen <= '0'; 144 | wait until rising_edge(s_wb_clk) and s_master_local_ack = '1'; 145 | sv_wishbone_queue.push(uint_to_slv(v_wbmaster_address, C_ADDRESS_WIDTH)); 146 | sv_wb_master_dict.set(v_wbmaster_address, v_wbmaster_data); 147 | sv_coverage.ICover(v_wbmaster_address); 148 | end loop; 149 | -- read back and check the wishbone slave registers 150 | while not(sv_wishbone_queue.is_empty) loop 151 | sv_wishbone_queue.pop(v_master_local_adress); 152 | s_master_local_adress <= v_master_local_adress; 153 | s_master_local_ren <= '1'; 154 | wait until rising_edge(s_wb_clk); 155 | s_master_local_adress <= (others => '0'); 156 | s_master_local_ren <= '0'; 157 | wait until rising_edge(s_wb_clk) and s_master_local_ack = '1'; 158 | sv_wb_master_dict.get(slv_to_uint(v_master_local_adress), v_wbmaster_data); 159 | assert_equal(s_master_local_dout, v_wbmaster_data); 160 | end loop; 161 | -- test local write & read at the same time 162 | wait until rising_edge(s_wb_clk); 163 | s_master_local_wen <= '1'; 164 | s_master_local_ren <= '1'; 165 | wait until rising_edge(s_wb_clk); 166 | s_master_local_wen <= '0'; 167 | s_master_local_ren <= '0'; 168 | wait until rising_edge(s_wb_clk); 169 | -- Test finished 170 | report "INFO: Test successfully finished!"; 171 | sv_coverage.SetMessage("WishboneT coverage results"); 172 | sv_coverage.WriteBin; 173 | finish; 174 | wait; 175 | end process WbMasterLocalP; 176 | 177 | 178 | i_WishBoneMasterE : WishBoneMasterE 179 | generic map ( 180 | Coverage => false, 181 | Formal => false, 182 | Simulation => true, 183 | AddressWidth => C_ADDRESS_WIDTH, 184 | DataWidth => C_DATA_WIDTH 185 | ) 186 | port map ( 187 | --+ wishbone system if 188 | WbRst_i => s_wb_reset, 189 | WbClk_i => s_wb_clk, 190 | --+ wishbone outputs 191 | WbCyc_o => s_wishbone.Cyc, 192 | WbStb_o => s_wishbone.Stb, 193 | WbWe_o => s_wishbone.We, 194 | WbAdr_o => s_wishbone.Adr, 195 | WbDat_o => s_wishbone.WDat, 196 | --+ wishbone inputs 197 | WbDat_i => s_wishbone.RDat, 198 | WbAck_i => s_wishbone.Ack, 199 | WbErr_i => s_wishbone.Err, 200 | --+ local register if 201 | LocalWen_i => s_master_local_wen, 202 | LocalRen_i => s_master_local_ren, 203 | LocalAdress_i => s_master_local_adress, 204 | LocalData_i => s_master_local_din, 205 | LocalData_o => s_master_local_dout, 206 | LocalAck_o => s_master_local_ack, 207 | LocalError_o => s_master_local_error 208 | ); 209 | 210 | 211 | WishBoneBusMonitorP : process is 212 | variable v_master_local_adress : std_logic_vector(C_ADDRESS_WIDTH-1 downto 0); 213 | variable v_master_local_data : std_logic_vector(C_DATA_WIDTH-1 downto 0); 214 | variable v_valid_access : std_logic; 215 | begin 216 | wait until (s_master_local_wen = '1' or s_master_local_ren = '1') and rising_edge(s_wb_clk); 217 | v_master_local_adress := s_master_local_adress; 218 | v_master_local_data := s_master_local_din; 219 | v_valid_access := s_master_local_wen xor s_master_local_ren; 220 | wait until rising_edge(s_wb_clk); 221 | WB_CYC : assert v_valid_access = s_wishbone.Cyc 222 | report "ERROR: Wishbone cycle should be 0b" & to_string(v_valid_access) & " instead of 0b" & to_string(s_wishbone.Cyc) 223 | severity failure; 224 | if (v_valid_access = '1') then 225 | WB_ADDR : assert s_wishbone.Adr = v_master_local_adress 226 | report "ERROR: Wishbone address 0x" & to_hstring(s_wishbone.Adr) & " differ from local address 0x" & to_hstring(v_master_local_adress) 227 | severity failure; 228 | if (s_wishbone.We = '1') then 229 | WB_DATA : assert s_wishbone.WDat = v_master_local_data 230 | report "ERROR: Wishbone data 0x" & to_hstring(s_wishbone.WDat) & " differ from local data 0x" & to_hstring(v_master_local_data) 231 | severity failure; 232 | end if; 233 | end if; 234 | end process WishBoneBusMonitorP; 235 | 236 | 237 | i_WishBoneSlaveE : WishBoneSlaveE 238 | generic map ( 239 | Formal => false, 240 | Simulation => true, 241 | AddressWidth => C_ADDRESS_WIDTH, 242 | DataWidth => C_DATA_WIDTH 243 | ) 244 | port map ( 245 | --+ wishbone system if 246 | WbRst_i => s_wb_reset, 247 | WbClk_i => s_wb_clk, 248 | --+ wishbone inputs 249 | WbCyc_i => s_wishbone.Cyc, 250 | WbStb_i => s_wishbone.Stb, 251 | WbWe_i => s_wishbone.We, 252 | WbAdr_i => s_wishbone.Adr, 253 | WbDat_i => s_wishbone.WDat, 254 | --* wishbone outputs 255 | WbDat_o => s_wishbone.RDat, 256 | WbAck_o => s_wishbone.Ack, 257 | WbErr_o => s_wishbone.Err, 258 | --+ local register if 259 | LocalWen_o => s_slave_local_wen, 260 | LocalRen_o => s_slave_local_ren, 261 | LocalAdress_o => s_slave_local_adress, 262 | LocalData_o => s_slave_local_dout, 263 | LocalData_i => s_slave_local_din 264 | ); 265 | 266 | 267 | WbSlaveLocalP : process is 268 | begin 269 | wait until rising_edge(s_wb_clk); 270 | if (s_wb_reset = '1') then 271 | s_slave_local_din <= (others => '0'); 272 | else 273 | if (s_slave_local_wen = '1') then 274 | sv_wb_slave_dict.set(slv_to_uint(s_slave_local_adress), s_slave_local_dout); 275 | elsif (s_slave_local_ren = '1') then 276 | WB_SLAVE_REG : assert sv_wb_slave_dict.hasKey(slv_to_uint(s_slave_local_adress)) 277 | report "ERROR: Requested register at addr 0x" & to_hstring(s_slave_local_adress) & " not written before" 278 | severity failure; 279 | s_slave_local_din <= sv_wb_slave_dict.get(slv_to_uint(s_slave_local_adress)); 280 | end if; 281 | end if; 282 | end process WbSlaveLocalP; 283 | 284 | 285 | i_WishBoneChecker : WishBoneCheckerE 286 | port map ( 287 | --+ wishbone system if 288 | WbRst_i => s_wb_reset, 289 | WbClk_i => s_wb_clk, 290 | --+ wishbone outputs 291 | WbMCyc_i => s_wishbone.Cyc, 292 | WbMStb_i => s_wishbone.Stb, 293 | WbMWe_i => s_wishbone.We, 294 | WbMAdr_i => s_wishbone.Adr, 295 | WbMDat_i => s_wishbone.WDat, 296 | --+ wishbone inputs 297 | WbSDat_i => s_wishbone.RDat, 298 | WbSAck_i => s_wishbone.Ack, 299 | WbSErr_i => s_wishbone.Err, 300 | WbRty_i => '0' 301 | ); 302 | 303 | 304 | end architecture sim; 305 | --------------------------------------------------------------------------------