diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..0323ec1 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,106 @@ +# This is a basic workflow to help you get started with Actions + +name: SPI FPGA CI + +# Controls when the action will run. +on: + # Triggers the workflow on push or pull request events but only for the master branch + push: + branches: [ master, dev ] + pull_request: + branches: [ master, dev ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + spi_master_sim: + name: SPI Master Simulation + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Setup GHDL + # You may pin to the exact commit or the version. + # uses: ghdl/setup-ghdl-ci@233774d8c0c5021af4c3686ea405013cb1494fd1 + uses: ghdl/setup-ghdl-ci@nightly + with: # Select GHDL backend (mcode, llvm or gcc) + backend: llvm # optional, default is mcode + + - name: GHDL version check + run: ghdl --version + + - name: Run Test 1 (CLK_FREQ=50e6, SPI_FREQ=1e6, WORD_SIZE=8) + run: | + cd ./sim/ + sh ./spi_master_tb_ghdl_setup.sh + ghdl -r SPI_MASTER_TB -gCLK_FREQ=50e6 -gSPI_FREQ=1e6 -gWORD_SIZE=8 -gTRANS_COUNT=2e4 + + - name: Run Test 2 (CLK_FREQ=12e6, SPI_FREQ=2e6, WORD_SIZE=8) + run: | + cd ./sim/ + sh ./spi_master_tb_ghdl_setup.sh + ghdl -r SPI_MASTER_TB -gCLK_FREQ=12e6 -gSPI_FREQ=2e6 -gWORD_SIZE=8 -gTRANS_COUNT=2e4 + + - name: Run Test 3 (CLK_FREQ=100e6, SPI_FREQ=5e6, WORD_SIZE=16) + run: | + cd ./sim/ + sh ./spi_master_tb_ghdl_setup.sh + ghdl -r SPI_MASTER_TB -gCLK_FREQ=100e6 -gSPI_FREQ=5e6 -gWORD_SIZE=16 -gTRANS_COUNT=2e4 + + - name: Run Test 4 (CLK_FREQ=25e6, SPI_FREQ=3e6, WORD_SIZE=16) + run: | + cd ./sim/ + sh ./spi_master_tb_ghdl_setup.sh + ghdl -r SPI_MASTER_TB -gCLK_FREQ=25e6 -gSPI_FREQ=3e6 -gWORD_SIZE=16 -gTRANS_COUNT=2e4 + + spi_slave_sim: + name: SPI Slave Simulation + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Setup GHDL + # You may pin to the exact commit or the version. + # uses: ghdl/setup-ghdl-ci@233774d8c0c5021af4c3686ea405013cb1494fd1 + uses: ghdl/setup-ghdl-ci@nightly + with: # Select GHDL backend (mcode, llvm or gcc) + backend: llvm # optional, default is mcode + + - name: GHDL version check + run: ghdl --version + + - name: Run Test 1 (CLK_FREQ=50e6, SPI_FREQ=1e6, WORD_SIZE=8) + run: | + cd ./sim/ + sh ./spi_slave_tb_ghdl_setup.sh + ghdl -r SPI_SLAVE_TB -gCLK_FREQ=50e6 -gSPI_FREQ=1e6 -gWORD_SIZE=8 -gTRANS_COUNT=2e4 + + - name: Run Test 2 (CLK_FREQ=12e6, SPI_FREQ=2e6, WORD_SIZE=8) + run: | + cd ./sim/ + sh ./spi_slave_tb_ghdl_setup.sh + ghdl -r SPI_SLAVE_TB -gCLK_FREQ=12e6 -gSPI_FREQ=2e6 -gWORD_SIZE=8 -gTRANS_COUNT=2e4 + + - name: Run Test 3 (CLK_FREQ=100e6, SPI_FREQ=5e6, WORD_SIZE=16) + run: | + cd ./sim/ + sh ./spi_slave_tb_ghdl_setup.sh + ghdl -r SPI_SLAVE_TB -gCLK_FREQ=100e6 -gSPI_FREQ=5e6 -gWORD_SIZE=16 -gTRANS_COUNT=2e4 + + - name: Run Test 4 (CLK_FREQ=25e6, SPI_FREQ=3e6, WORD_SIZE=16) + run: | + cd ./sim/ + sh ./spi_slave_tb_ghdl_setup.sh + ghdl -r SPI_SLAVE_TB -gCLK_FREQ=25e6 -gSPI_FREQ=3e6 -gWORD_SIZE=16 -gTRANS_COUNT=2e4 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c4dbede --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog of SPI master and SPI slave for FPGA + +**Version 1.1 - released on 24 April 2021** +- Changed license to The MIT License. +- Added better simulations and enabled GitHub CI. +- Added Spirit Level example design for CYC1000 board. +- Added sync FFs to SPI slave for elimination metastability. +- Added WORD_SIZE generic. +- Many minor changes, fixes and optimizations. + +**Version 1.0 - released on 28 September 2017** +- First non-beta release. +- Added new version of master module with many optimizations. +- Added DIN_LAST input to master module, for CS_N signal control. +- Added simulation tcl script for ModelSim. +- Updated simulation testbench. +- Updated example design. +- Optimized and cleaned slave module. diff --git a/LICENSE b/LICENSE index 65c5ca8..f420ae1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,165 +1,21 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. +The MIT License (MIT) + +Copyright (c) 2015-2021 Jakub Cabal + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index d7b65fe..ead0630 100644 --- a/README.md +++ b/README.md @@ -1,80 +1,143 @@ # SPI MASTER AND SLAVE FOR FPGA -The SPI master and SPI slave are simple controllers for communication between FPGA and various peripherals via the SPI interface. The SPI master and SPI slave have been implemented using VHDL 93 and are applicable to any FPGA. +The [SPI master](#spi-master) and [SPI slave](#spi-slave) are simple controllers for communication between FPGA and various peripherals via the SPI interface. The SPI master and SPI slave have been implemented using VHDL 93 and are applicable to any FPGA. **The SPI master and SPI slave controllers support only SPI mode 0 (CPOL=0, CPHA=0)!** -The SPI master and SPI slave controllers were simulated and tested in hardware. If you have a question or you have a tip for improvement, send me an e-mail or create a issue. +The SPI master and SPI slave controllers were simulated and tested in hardware. I use the GHDL tool for CI: automated VHDL simulations in the GitHub Actions environment ([setup-ghdl-ci](https://github.com/ghdl/setup-ghdl-ci)). If you have a question or an improvement tip, send me an e-mail or create an issue. -## Table of resource usage summary: +## SPI master -CONTROLLER | LE (LUT) | FF | BRAM | Fmax -:---:|:---:|:---:|:---:|:---: -SPI MASTER | 34 | 23 | 0 | 327.3 MHz -SPI SLAVE | 24 | 15 | 0 | 318.0 MHz +### Generics: -*Synthesis have been performed using Quartus Prime 17 Lite Edition for FPGA Altera Cyclone IV with these settings: CLK_FREQ = 50 MHz, SCLK_FREQ = 2 MHz, SLAVE_COUNT = 1.* +```vhdl +CLK_FREQ : natural := 50e6; -- set system clock frequency in Hz +SCLK_FREQ : natural := 5e6; -- set SPI clock frequency in Hz (condition: SCLK_FREQ <= CLK_FREQ/10) +WORD_SIZE : natural := 8; -- size of transfer word in bits, must be power of two +SLAVE_COUNT : natural := 1 -- count of SPI slaves +``` -## The SPI loopback example design: +### Ports: -The SPI loopback example design is for testing data transfer between SPI master and SPI slave over external wires. +```vhdl +CLK : in std_logic; -- system clock +RST : in std_logic; -- high active synchronous reset +-- SPI MASTER INTERFACE +SCLK : out std_logic; -- SPI clock +CS_N : out std_logic_vector(SLAVE_COUNT-1 downto 0); -- SPI chip select, active in low +MOSI : out std_logic; -- SPI serial data from master to slave +MISO : in std_logic; -- SPI serial data from slave to master +-- INPUT USER INTERFACE +DIN : in std_logic_vector(WORD_SIZE-1 downto 0); -- data for transmission to SPI slave +DIN_ADDR : in std_logic_vector(natural(ceil(log2(real(SLAVE_COUNT))))-1 downto 0); -- SPI slave address +DIN_LAST : in std_logic; -- when DIN_LAST = 1, last data word, after transmit will be asserted CS_N +DIN_VLD : in std_logic; -- when DIN_VLD = 1, data for transmission are valid +DIN_RDY : out std_logic; -- when DIN_RDY = 1, SPI master is ready to accept valid data for transmission +-- OUTPUT USER INTERFACE +DOUT : out std_logic_vector(WORD_SIZE-1 downto 0); -- received data from SPI slave +DOUT_VLD : out std_logic -- when DOUT_VLD = 1, received data are valid +``` -Please read [README file of SPI loopback example design](example/README.md). +### Resource usage: + +LE | FF | M9K | Fmax +---|----|-----|----------- +34 | 23 | 0 | 330.1 MHz + +*Implementation was performed using Quartus Prime Lite Edition 20.1.0 for Intel Cyclone 10 FPGA (10CL025YU256C8G) with default generics.* + +### Simulation: + +A simulation is prepared in the [```sim/```](sim/) folder. You can use the prepared TCL script to run simulation in ModelSim. +``` +vsim -do spi_master_tb_msim_run.tcl +``` + +Or it is possible to run the simulation using the [GHDL tool](https://github.com/ghdl/ghdl). Linux users can use the prepared bash script to run the simulation in GHDL: +``` +./spi_master_tb_ghdl_run.sh +``` + +## SPI slave + +### Generics: + +```vhdl +WORD_SIZE : natural := 8; -- size of transfer word in bits, must be power of two +``` + +### Ports: + +```vhdl +CLK : in std_logic; -- system clock +RST : in std_logic; -- high active synchronous reset +-- SPI SLAVE INTERFACE +SCLK : in std_logic; -- SPI clock +CS_N : in std_logic; -- SPI chip select, active in low +MOSI : in std_logic; -- SPI serial data from master to slave +MISO : out std_logic; -- SPI serial data from slave to master +-- USER INTERFACE +DIN : in std_logic_vector(WORD_SIZE-1 downto 0); -- data for transmission to SPI master +DIN_VLD : in std_logic; -- when DIN_VLD = 1, data for transmission are valid +DIN_RDY : out std_logic; -- when DIN_RDY = 1, SPI slave is ready to accept valid data for transmission +DOUT : out std_logic_vector(WORD_SIZE-1 downto 0); -- received data from SPI master +DOUT_VLD : out std_logic -- when DOUT_VLD = 1, received data are valid +``` + +### Resource usage: + +LE | FF | M9K | Fmax +---|----|-----|----------- +29 | 21 | 0 | 324.5 MHz + +*Implementation was performed using Quartus Prime Lite Edition 20.1.0 for Intel Cyclone 10 FPGA (10CL025YU256C8G) with default generics.* + +### Simulation: + +A simulation is prepared in the [```sim/```](sim/) folder. You can use the prepared TCL script to run simulation in ModelSim. +``` +vsim -do spi_slave_tb_msim_run.tcl +``` + +Or it is possible to run the simulation using the [GHDL tool](https://github.com/ghdl/ghdl). Linux users can use the prepared bash script to run the simulation in GHDL: +``` +./spi_slave_tb_ghdl_run.sh +``` + +## Examples: + +### Spirit Level: + +The [Spirit Level example design](examples/spirit_level) shows one possible use of the SPI Master controller. The example design is prepared for [FPGA board CYC1000](https://shop.trenz-electronic.de/en/TEI0003-02-CYC1000-with-Cyclone-10-FPGA-8-MByte-SDRAM) with Intel Cyclone 10 FPGA (10CL025YU256C8G) and [digital accelerometer (LIS3DH)](https://www.st.com/resource/en/datasheet/lis3dh.pdf). Here you can find [the documentation of the CYC1000 board](https://www.trenz-electronic.de/fileadmin/docs/Trenz_Electronic/Modules_and_Module_Carriers/2.5x6.15/TEI0003/REV02/Documents/CYC1000%20User%20Guide.pdf). In this design, the SPI Master controller is used to configure and read data from the accelerometer. The LEDs on the board show the values from the accelerometer in the form of a spirit level. You can watch the Spirit Level example [video on YouTube](https://youtu.be/EI1BEAkZu5Q). + +[![Spirit Level example video](docs/spirit_level_example.gif)](https://youtu.be/EI1BEAkZu5Q) + +### SPI loopback: + +The [SPI loopback example design](examples/loopback) allows testing transfers between SPI master and SPI slave over external wires. The example design is prepared for FPGA board [EP4CE6 Starter Board](http://www.ebay.com/itm/111975895262) with Altera FPGA Cyclone IV (EP4CE6E22C8), few buttons and a seven-segment display (four digit). You can watch the SPI loopback example [video on YouTube](https://youtu.be/-TbtB6Sm2Xk). [![Video of SPI loopback example design](https://img.youtube.com/vi/-TbtB6Sm2Xk/0.jpg)](https://youtu.be/-TbtB6Sm2Xk) +Display description (from right on board in video): + +``` +Digit0 = value on SPI slave input +Digit1 = value on SPI slave output +Digit2 = value on SPI master input +Digit3 = value on SPI master output +``` + +Buttons description (from right on board in video): + +``` +BTN_ACTION (in mode0) = setup value on SPI slave input +BTN_ACTION (in mode1) = write (set valid) of SPI slave input value +BTN_ACTION (in mode2) = setup value on SPI master input +BTN_ACTION (in mode3) = write (set valid) of SPI slave input value and start transfer between SPI master and SPI slave +BTN_MODE = switch between modes (mode0 = light decimal point on digit0,...) +BTN_RESET = reset FPGA design +``` + ## License: -The SPI master and SPI slave controllers are available under the GNU LESSER GENERAL PUBLIC LICENSE Version 3. - -Please read [LICENSE file](LICENSE). - -# SPI master - -## Table of generics: - -Generic name | Type | Default value | Generic description ----|:---:|:---:|:--- -CLK_FREQ | natural | 50e6 | System clock frequency in Hz. -SCLK_FREQ | natural | 2e6 | Set SPI clock frequency in Hz (condition: SCLK_FREQ <= CLK_FREQ/10). -SLAVE_COUNT | natural | 1 | Count of SPI slave controllers. - -## Table of inputs and outputs ports: - -Port name | IN/OUT | Width [b]| Port description ----|:---:|:---:|--- -CLK | IN | 1 | System clock. -RST | IN | 1 | High active synchronous reset. ---- | --- | --- | --- -SCLK | OUT | 1 | SPI clock. -CS_N | OUT | SLAVE_COUNT | SPI chip select, active in low. -MOSI | OUT | 1 | SPI serial data from master to slave. -MISO | IN | 1 | SPI serial data from slave to master. ---- | --- | --- | --- -ADDR | IN | log2(SLAVE_COUNT) | SPI slave address. -DIN | IN | 8 | Input data for SPI slave. -DIN_LAST | IN | 1 | When DIN_LAST = 1, after transmit these input data is asserted CS_N. -DIN_VLD | IN | 1 | When DIN_VLD = 1, input data are valid. -READY | OUT | 1 | When READY = 1, valid input data are accept. -DOUT | OUT | 8 | Output data from SPI slave. -DOUT_VLD | OUT | 1 | When DOUT_VLD = 1, output data are valid. - -# SPI slave - -## Table of inputs and outputs ports: - -Port name | IN/OUT | Width [b]| Port description ----|:---:|:---:|--- -CLK | IN | 1 | System clock. -RST | IN | 1 | High active synchronous reset. ---- | --- | --- | --- -SCLK | IN | 1 | SPI clock. -CS_N | IN | 1 | SPI chip select active in low. -MOSI | IN | 1 | SPI serial data from master to slave. -MISO | OUT | 1 | SPI serial data from slave to master. ---- | --- | --- | --- -DIN | IN | 8 | Input data for SPI master. -DIN_VLD | IN | 1 | When DIN_VLD = 1, input data are valid. -READY | OUT | 1 | When READY = 1, valid input data are accept. -DOUT | OUT | 8 | Output data from SPI master. -DOUT_VLD | OUT | 1 | When DOUT_VLD = 1, output data are valid. +This whole repository (include SPI master and SPI slave controllers) is available under the MIT license. Please read [LICENSE file](LICENSE). diff --git a/docs/spirit_level_example.gif b/docs/spirit_level_example.gif new file mode 100644 index 0000000..10a199f Binary files /dev/null and b/docs/spirit_level_example.gif differ diff --git a/example/README.md b/example/README.md deleted file mode 100644 index 3d83530..0000000 --- a/example/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# SPI LOOPBACK EXAMPLE - -The SPI loopback example design is for testing data transfer between SPI master and SPI slave over external wires. -I use it on my FPGA board ([EP4CE6 Starter Board](http://www.ebay.com/itm/111975895262) with Altera FPGA Cyclone IV EP4CE6E22C8 for $45) with few buttons and a seven-segment display (four digit). - -There is video of the SPI loopback example design: https://youtu.be/-TbtB6Sm2Xk - -[![Video of SPI loopback example design](https://img.youtube.com/vi/-TbtB6Sm2Xk/0.jpg)](https://youtu.be/-TbtB6Sm2Xk) - -## Control of SPI loopback example design: - -**Display description (from right on board in video):** - -* Digit0 = value on SPI slave input -* Digit1 = value on SPI slave output -* Digit2 = value on SPI master input -* Digit3 = value on SPI master output - -**Buttons description (from right on board in video):** - -* BTN_ACTION (in mode0) = setup value on SPI slave input -* BTN_ACTION (in mode1) = write (set valid) of SPI slave input value -* BTN_ACTION (in mode2) = setup value on SPI master input -* BTN_ACTION (in mode3) = write (set valid) of SPI slave input value and start transfer between SPI master and SPI slave -* BTN_MODE = switch between modes (mode0 = light decimal point on digit0,...) -* BTN_RESET = reset FPGA design diff --git a/example/comp/btn_debounce.vhd b/example/comp/btn_debounce.vhd deleted file mode 100644 index 01f878c..0000000 --- a/example/comp/btn_debounce.vhd +++ /dev/null @@ -1,82 +0,0 @@ --------------------------------------------------------------------------------- --- PROJECT: FPGA MISC --------------------------------------------------------------------------------- --- NAME: BTN_DEBOUNCE --- AUTHORS: Jakub Cabal --- LICENSE: The MIT License --- WEBSITE: https://github.com/jakubcabal/fpga-misc --------------------------------------------------------------------------------- - -library IEEE; -use IEEE.STD_LOGIC_1164.ALL; -use IEEE.NUMERIC_STD.ALL; - -entity BTN_DEBOUNCE is - Port ( - CLK : in std_logic; -- system clock - CLK_EN_1K : in std_logic; -- clock enable 1 KHz - ASYNC_RST : in std_logic; -- asynchrounous reset - BTN_RAW : in std_logic; -- button raw signal - BTN_DEB : out std_logic; -- button debounce signal - BTN_DEB_EN : out std_logic -- button debounce rising edge enable - ); -end BTN_DEBOUNCE; - -architecture RTL of BTN_DEBOUNCE is - - signal btn_raw_shreg : std_logic_vector(3 downto 0); - signal btn_deb_comb : std_logic; - signal btn_deb_reg : std_logic; - signal btn_deb_en_reg : std_logic; - -begin - - -- ------------------------------------------------------------------------- - -- SHIFT REGISTER OF BUTTON RAW SIGNAL - -- ------------------------------------------------------------------------- - - btn_shreg_p : process (CLK, ASYNC_RST) - begin - if (ASYNC_RST = '1') then - btn_raw_shreg <= (others => '0'); - elsif (rising_edge(CLK)) then - if (CLK_EN_1K = '1') then - btn_raw_shreg <= btn_raw_shreg(2 downto 0) & BTN_RAW; - end if; - end if; - end process; - - -- ------------------------------------------------------------------------- - -- DEBOUNCE REGISTER OF BUTTON RAW SIGNAL - -- ------------------------------------------------------------------------- - - btn_deb_comb <= btn_raw_shreg(0) and btn_raw_shreg(1) and - btn_raw_shreg(2) and btn_raw_shreg(3); - - btn_deb_reg_p : process (CLK, ASYNC_RST) - begin - if (ASYNC_RST = '1') then - btn_deb_reg <= '0'; - elsif (rising_edge(CLK)) then - btn_deb_reg <= btn_deb_comb; - end if; - end process; - - BTN_DEB <= btn_deb_reg; - - -- ------------------------------------------------------------------------- - -- RISING EDGE DETECTOR OF BUTTON DEBOUNCE SIGNAL - -- ------------------------------------------------------------------------- - - sseg_an_cnt_p : process (CLK, ASYNC_RST) - begin - if (ASYNC_RST = '1') then - btn_deb_en_reg <= '0'; - elsif (rising_edge(CLK)) then - btn_deb_en_reg <= btn_deb_comb and not btn_deb_reg; - end if; - end process; - - BTN_DEB_EN <= btn_deb_en_reg; - -end RTL; diff --git a/example/spi_loopback_assignments.qsf b/example/spi_loopback_assignments.qsf deleted file mode 100644 index 94617b7..0000000 --- a/example/spi_loopback_assignments.qsf +++ /dev/null @@ -1,64 +0,0 @@ -#------------------------------------------------------------------------------- -# PROJECT: SPI MASTER AND SLAVE FOR FPGA -#------------------------------------------------------------------------------- -# NAME: CONSTRAINTS FOR EP4CE6 STARTER BOARD -# AUTHORS: Jakub Cabal -# LICENSE: LGPL-3.0, please read LICENSE file -# WEBSITE: https://github.com/jakubcabal/spi-fpga -#------------------------------------------------------------------------------- -# COPYRIGHT NOTICE: -#------------------------------------------------------------------------------- -# SPI MASTER AND SLAVE FOR FPGA -# Copyright (C) 2016 Jakub Cabal -# -# This source file is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This source file is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . -#------------------------------------------------------------------------------- - -# GLOBAL CONSTRAINTS FOR EP4CE6 STARTER BOARD - -set_global_assignment -name FAMILY "Cyclone IV" -set_global_assignment -name DEVICE EP4CE6E22C8 -set_global_assignment -name TOP_LEVEL_ENTITY SPI_LOOPBACK - -# FPGA PINS ASSIGNMENT - -set_location_assignment PIN_91 -to CLK -set_location_assignment PIN_25 -to BTN_RST - -set_location_assignment PIN_85 -to M_SCLK -set_location_assignment PIN_86 -to M_CS_N -set_location_assignment PIN_87 -to M_MOSI -set_location_assignment PIN_99 -to M_MISO - -set_location_assignment PIN_111 -to S_SCLK -set_location_assignment PIN_110 -to S_CS_N -set_location_assignment PIN_98 -to S_MOSI -set_location_assignment PIN_100 -to S_MISO - -set_location_assignment PIN_24 -to BTN_MENU_MODE -set_location_assignment PIN_23 -to BTN_MENU_ACTION - -set_location_assignment PIN_7 -to SSEG[0] -set_location_assignment PIN_3 -to SSEG[1] -set_location_assignment PIN_2 -to SSEG[2] -set_location_assignment PIN_1 -to SSEG[3] -set_location_assignment PIN_144 -to SSEG[4] -set_location_assignment PIN_143 -to SSEG[5] -set_location_assignment PIN_142 -to SSEG[6] -set_location_assignment PIN_141 -to SSEG[7] - -set_location_assignment PIN_137 -to SSEG_AN[0] -set_location_assignment PIN_138 -to SSEG_AN[1] -set_location_assignment PIN_10 -to SSEG_AN[2] -set_location_assignment PIN_11 -to SSEG_AN[3] diff --git a/example/spi_loopback_constraints.sdc b/example/spi_loopback_constraints.sdc deleted file mode 100644 index 0681a7a..0000000 --- a/example/spi_loopback_constraints.sdc +++ /dev/null @@ -1,28 +0,0 @@ -#------------------------------------------------------------------------------- -# PROJECT: SPI MASTER AND SLAVE FOR FPGA -#------------------------------------------------------------------------------- -# NAME: TIMING CONSTRAINTS -# AUTHORS: Jakub Cabal -# LICENSE: LGPL-3.0, please read LICENSE file -# WEBSITE: https://github.com/jakubcabal/spi-fpga -#------------------------------------------------------------------------------- -# COPYRIGHT NOTICE: -#------------------------------------------------------------------------------- -# SPI MASTER AND SLAVE FOR FPGA -# Copyright (C) 2016 Jakub Cabal -# -# This source file is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This source file is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . -#------------------------------------------------------------------------------- - -create_clock -name CLK50 -period 20.000 [get_ports {CLK}] diff --git a/examples/common/btn_debounce.vhd b/examples/common/btn_debounce.vhd new file mode 100644 index 0000000..eb97d5e --- /dev/null +++ b/examples/common/btn_debounce.vhd @@ -0,0 +1,118 @@ +-------------------------------------------------------------------------------- +-- PROJECT: SPI MASTER AND SLAVE FOR FPGA +-------------------------------------------------------------------------------- +-- AUTHORS: Jakub Cabal +-- LICENSE: The MIT License, please read LICENSE file +-- WEBSITE: https://github.com/jakubcabal/spi-fpga +-------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity BTN_DEBOUNCE is + Generic ( + CNT_WIDTH : natural := 2 -- width of debounce counter + ); + Port ( + CLK : in std_logic; -- system clock + ASYNC_RST : in std_logic; -- asynchrounous reset + SAMPLE_EN : in std_logic; -- sample clock enable + BTN_RAW : in std_logic; -- button raw signal + BTN_DEB : out std_logic; -- button debounce signal + BTN_DEB_RE : out std_logic -- rising edge of debounced signal + ); +end entity; + +architecture RTL of BTN_DEBOUNCE is + + signal btn_raw_sync_reg1 : std_logic; + signal btn_raw_sync_reg2 : std_logic; + signal btn_raw_sample_reg : std_logic; + signal btn_raw_diff : std_logic; + signal deb_cnt : unsigned(CNT_WIDTH-1 downto 0); + signal deb_cnt_max : std_logic; + signal btn_deb_reg : std_logic; + signal btn_deb_re_reg : std_logic; + +begin + + -- ------------------------------------------------------------------------- + -- BUTTON RAW SIGNAL SYNCHRONIZATION REGISTERS + -- ------------------------------------------------------------------------- + + btn_raw_sync_reg_p : process (CLK) + begin + if (rising_edge(CLK)) then + btn_raw_sync_reg1 <= BTN_RAW; + btn_raw_sync_reg2 <= btn_raw_sync_reg1; + end if; + end process; + + -- ------------------------------------------------------------------------- + -- BUTTON RAW SIGNAL SAMPLE REGISTERS + -- ------------------------------------------------------------------------- + + btn_raw_sample_reg_p : process (CLK) + begin + if (rising_edge(CLK)) then + if (SAMPLE_EN = '1') then + btn_raw_sample_reg <= btn_raw_sync_reg2; + end if; + end if; + end process; + + btn_raw_diff <= btn_raw_sample_reg xor btn_raw_sync_reg2; + + -- ------------------------------------------------------------------------- + -- DEBOUNCE COUNTER + -- ------------------------------------------------------------------------- + + deb_cnt_p : process (CLK, ASYNC_RST) + begin + if (ASYNC_RST = '1') then + deb_cnt <= (others => '0'); + elsif (rising_edge(CLK)) then + if (SAMPLE_EN = '1') then + if (btn_raw_diff = '1') then + deb_cnt <= (others => '0'); + else + deb_cnt <= deb_cnt + 1; + end if; + end if; + end if; + end process; + + deb_cnt_max <= '1' when (deb_cnt = (2**CNT_WIDTH)-1) else '0'; + + -- ------------------------------------------------------------------------- + -- BUTTON DEBOUNCE SIGNAL REGISTER + -- ------------------------------------------------------------------------- + + btn_deb_reg_p : process (CLK) + begin + if (rising_edge(CLK)) then + if (deb_cnt_max = '1') then + btn_deb_reg <= btn_raw_sample_reg; + end if; + end if; + end process; + + BTN_DEB <= btn_deb_reg; + + -- ------------------------------------------------------------------------- + -- RISING EDGE DETECTOR OF BUTTON DEBOUNCE SIGNAL + -- ------------------------------------------------------------------------- + + btn_deb_re_reg_p : process (CLK, ASYNC_RST) + begin + if (ASYNC_RST = '1') then + btn_deb_re_reg <= '0'; + elsif (rising_edge(CLK)) then + btn_deb_re_reg <= deb_cnt_max and btn_raw_sample_reg and not btn_deb_reg; + end if; + end process; + + BTN_DEB_RE <= btn_deb_re_reg; + +end architecture; diff --git a/example/comp/clk_en_gen.vhd b/examples/common/clk_en_gen.vhd similarity index 93% rename from example/comp/clk_en_gen.vhd rename to examples/common/clk_en_gen.vhd index 7e0a18c..8715641 100644 --- a/example/comp/clk_en_gen.vhd +++ b/examples/common/clk_en_gen.vhd @@ -1,10 +1,9 @@ -------------------------------------------------------------------------------- --- PROJECT: FPGA MISC +-- PROJECT: SPI MASTER AND SLAVE FOR FPGA -------------------------------------------------------------------------------- --- NAME: CLK_EN_GEN -- AUTHORS: Jakub Cabal --- LICENSE: The MIT License --- WEBSITE: https://github.com/jakubcabal/fpga-misc +-- LICENSE: The MIT License, please read LICENSE file +-- WEBSITE: https://github.com/jakubcabal/spi-fpga -------------------------------------------------------------------------------- library IEEE; diff --git a/examples/common/rst_sync.vhd b/examples/common/rst_sync.vhd new file mode 100644 index 0000000..99bffc3 --- /dev/null +++ b/examples/common/rst_sync.vhd @@ -0,0 +1,50 @@ +-------------------------------------------------------------------------------- +-- PROJECT: SPI MASTER AND SLAVE FOR FPGA +-------------------------------------------------------------------------------- +-- AUTHORS: Jakub Cabal +-- LICENSE: The MIT License, please read LICENSE file +-- WEBSITE: https://github.com/jakubcabal/spi-fpga +-------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity RST_SYNC is + Port ( + CLK : in std_logic; + ASYNC_RST : in std_logic; + SYNCED_RST : out std_logic + ); +end entity; + +architecture RTL of RST_SYNC is + + attribute ALTERA_ATTRIBUTE : string; + attribute PRESERVE : boolean; + + signal meta_reg : std_logic; + signal reset_reg : std_logic; + + attribute ALTERA_ATTRIBUTE of RTL : architecture is "-name SDC_STATEMENT ""set_false_path -to [get_registers {*RST_SYNC:*|meta_reg}] """; + attribute ALTERA_ATTRIBUTE of meta_reg : signal is "-name SYNCHRONIZER_IDENTIFICATION ""FORCED IF ASYNCHRONOUS"""; + attribute ALTERA_ATTRIBUTE of reset_reg : signal is "-name SYNCHRONIZER_IDENTIFICATION ""FORCED IF ASYNCHRONOUS"""; + attribute PRESERVE of meta_reg : signal is TRUE; + attribute PRESERVE of reset_reg : signal is TRUE; + +begin + + process (CLK, ASYNC_RST) + begin + if (ASYNC_RST = '1') then + meta_reg <= '1'; + reset_reg <= '1'; + elsif (rising_edge(CLK)) then + meta_reg <= '0'; + reset_reg <= meta_reg; + end if; + end process; + + SYNCED_RST <= reset_reg; + +end architecture; diff --git a/example/comp/sseg_driver.vhd b/examples/common/sseg_driver.vhd similarity index 97% rename from example/comp/sseg_driver.vhd rename to examples/common/sseg_driver.vhd index bc162a8..9c57e80 100644 --- a/example/comp/sseg_driver.vhd +++ b/examples/common/sseg_driver.vhd @@ -1,10 +1,9 @@ -------------------------------------------------------------------------------- --- PROJECT: FPGA MISC +-- PROJECT: SPI MASTER AND SLAVE FOR FPGA -------------------------------------------------------------------------------- --- NAME: SSEG_DRIVER -- AUTHORS: Jakub Cabal --- LICENSE: The MIT License --- WEBSITE: https://github.com/jakubcabal/fpga-misc +-- LICENSE: The MIT License, please read LICENSE file +-- WEBSITE: https://github.com/jakubcabal/spi-fpga -------------------------------------------------------------------------------- library IEEE; diff --git a/examples/loopback/quartus/spi_loopback_ep4ce6sb.qpf b/examples/loopback/quartus/spi_loopback_ep4ce6sb.qpf new file mode 100644 index 0000000..d35c510 --- /dev/null +++ b/examples/loopback/quartus/spi_loopback_ep4ce6sb.qpf @@ -0,0 +1,9 @@ +#------------------------------------------------------------------------------- +# PROJECT: SPI MASTER AND SLAVE FOR FPGA +#------------------------------------------------------------------------------- +# AUTHORS: Jakub Cabal +# LICENSE: The MIT License, please read LICENSE file +# WEBSITE: https://github.com/jakubcabal/spi-fpga +#------------------------------------------------------------------------------- + +PROJECT_REVISION = "spi_loopback_ep4ce6sb" diff --git a/examples/loopback/quartus/spi_loopback_ep4ce6sb.qsf b/examples/loopback/quartus/spi_loopback_ep4ce6sb.qsf new file mode 100644 index 0000000..9e97a3a --- /dev/null +++ b/examples/loopback/quartus/spi_loopback_ep4ce6sb.qsf @@ -0,0 +1,54 @@ +#------------------------------------------------------------------------------- +# PROJECT: SPI MASTER AND SLAVE FOR FPGA +#------------------------------------------------------------------------------- +# AUTHORS: Jakub Cabal +# LICENSE: The MIT License, please read LICENSE file +# WEBSITE: https://github.com/jakubcabal/spi-fpga +#------------------------------------------------------------------------------- + +# GLOBAL CONSTRAINTS FOR EP4CE6 STARTER BOARD + +set_global_assignment -name FAMILY "Cyclone IV E" +set_global_assignment -name DEVICE EP4CE6E22C8 +set_global_assignment -name TOP_LEVEL_ENTITY SPI_LOOPBACK + +# PROJECT VHDL FILES +set_global_assignment -name VHDL_FILE ../../../rtl/spi_slave.vhd +set_global_assignment -name VHDL_FILE ../../../rtl/spi_master.vhd +set_global_assignment -name VHDL_FILE ../../common/rst_sync.vhd +set_global_assignment -name VHDL_FILE ../../common/clk_en_gen.vhd +set_global_assignment -name VHDL_FILE ../../common/sseg_driver.vhd +set_global_assignment -name VHDL_FILE ../../common/btn_debounce.vhd +set_global_assignment -name VHDL_FILE ../spi_loopback.vhd + +# FPGA PINS ASSIGNMENT + +set_location_assignment PIN_91 -to CLK +set_location_assignment PIN_25 -to BTN_RST + +set_location_assignment PIN_85 -to M_SCLK +set_location_assignment PIN_86 -to M_CS_N +set_location_assignment PIN_87 -to M_MOSI +set_location_assignment PIN_99 -to M_MISO + +set_location_assignment PIN_111 -to S_SCLK +set_location_assignment PIN_110 -to S_CS_N +set_location_assignment PIN_98 -to S_MOSI +set_location_assignment PIN_100 -to S_MISO + +set_location_assignment PIN_24 -to BTN_MENU_MODE +set_location_assignment PIN_23 -to BTN_MENU_ACTION + +set_location_assignment PIN_7 -to SSEG[0] +set_location_assignment PIN_3 -to SSEG[1] +set_location_assignment PIN_2 -to SSEG[2] +set_location_assignment PIN_1 -to SSEG[3] +set_location_assignment PIN_144 -to SSEG[4] +set_location_assignment PIN_143 -to SSEG[5] +set_location_assignment PIN_142 -to SSEG[6] +set_location_assignment PIN_141 -to SSEG[7] + +set_location_assignment PIN_137 -to SSEG_AN[0] +set_location_assignment PIN_138 -to SSEG_AN[1] +set_location_assignment PIN_10 -to SSEG_AN[2] +set_location_assignment PIN_11 -to SSEG_AN[3] diff --git a/examples/loopback/quartus/spi_loopback_ep4ce6sb.sdc b/examples/loopback/quartus/spi_loopback_ep4ce6sb.sdc new file mode 100644 index 0000000..c9fe2a3 --- /dev/null +++ b/examples/loopback/quartus/spi_loopback_ep4ce6sb.sdc @@ -0,0 +1,9 @@ +#------------------------------------------------------------------------------- +# PROJECT: SPI MASTER AND SLAVE FOR FPGA +#------------------------------------------------------------------------------- +# AUTHORS: Jakub Cabal +# LICENSE: The MIT License, please read LICENSE file +# WEBSITE: https://github.com/jakubcabal/spi-fpga +#------------------------------------------------------------------------------- + +create_clock -name CLK50 -period 20.000 [get_ports {CLK}] diff --git a/example/spi_loopback.vhd b/examples/loopback/spi_loopback.vhd similarity index 73% rename from example/spi_loopback.vhd rename to examples/loopback/spi_loopback.vhd index 5de65c2..af14df6 100644 --- a/example/spi_loopback.vhd +++ b/examples/loopback/spi_loopback.vhd @@ -1,29 +1,10 @@ -------------------------------------------------------------------------------- -- PROJECT: SPI MASTER AND SLAVE FOR FPGA -------------------------------------------------------------------------------- --- NAME: SPI_LOOPBACK -- AUTHORS: Jakub Cabal --- LICENSE: LGPL-3.0, please read LICENSE file +-- LICENSE: The MIT License, please read LICENSE file -- WEBSITE: https://github.com/jakubcabal/spi-fpga -------------------------------------------------------------------------------- --- COPYRIGHT NOTICE: --------------------------------------------------------------------------------- --- SPI MASTER AND SLAVE FOR FPGA --- Copyright (C) 2016 Jakub Cabal --- --- This source file is free software: you can redistribute it and/or modify --- it under the terms of the GNU Lesser General Public License as published by --- the Free Software Foundation, either version 3 of the License, or --- (at your option) any later version. --- --- This source file is distributed in the hope that it will be useful, --- but WITHOUT ANY WARRANTY; without even the implied warranty of --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --- GNU Lesser General Public License for more details. --- --- You should have received a copy of the GNU Lesser General Public License --- along with this program. If not, see . --------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; @@ -49,10 +30,11 @@ entity SPI_LOOPBACK is SSEG : out std_logic_vector(7 downto 0); SSEG_AN : out std_logic_vector(3 downto 0) ); -end SPI_LOOPBACK; +end entity; architecture RTL of SPI_LOOPBACK is + signal reset : std_logic; signal clk_en_1k : std_logic; signal menu_mode_en : std_logic; @@ -60,8 +42,8 @@ architecture RTL of SPI_LOOPBACK is signal menu_cnt : unsigned(1 downto 0); signal menu_mode_led : std_logic_vector(3 downto 0); - signal m_cnt : unsigned(7 downto 0); - signal s_cnt : unsigned(7 downto 0); + signal m_cnt : unsigned(3 downto 0); + signal s_cnt : unsigned(3 downto 0); signal m_cnt_en : std_logic; signal s_cnt_en : std_logic; @@ -78,6 +60,17 @@ architecture RTL of SPI_LOOPBACK is begin + -- ------------------------------------------------------------------------- + -- RESET SYNCHRONIZER + -- ------------------------------------------------------------------------- + + reset_sync_i : entity work.RST_SYNC + port map ( + CLK => CLK, + ASYNC_RST => BTN_RST, + SYNCED_RST => reset + ); + -- ------------------------------------------------------------------------- -- CLOCK ENABLE GENERATOR -- ------------------------------------------------------------------------- @@ -88,7 +81,7 @@ begin ) port map ( CLK => CLK, - ASYNC_RST => BTN_RST, + ASYNC_RST => reset, CLK_EN_1K => clk_en_1k ); @@ -99,33 +92,33 @@ begin btn1_debounce_i : entity work.BTN_DEBOUNCE port map ( CLK => CLK, - CLK_EN_1K => clk_en_1k, - ASYNC_RST => BTN_RST, + ASYNC_RST => reset, + SAMPLE_EN => clk_en_1k, BTN_RAW => BTN_MENU_MODE, BTN_DEB => open, - BTN_DEB_EN => menu_mode_en + BTN_DEB_RE => menu_mode_en ); btn2_debounce_i : entity work.BTN_DEBOUNCE port map ( CLK => CLK, - CLK_EN_1K => clk_en_1k, - ASYNC_RST => BTN_RST, + ASYNC_RST => reset, + SAMPLE_EN => clk_en_1k, BTN_RAW => BTN_MENU_ACTION, BTN_DEB => open, - BTN_DEB_EN => menu_action_en + BTN_DEB_RE => menu_action_en ); -- ------------------------------------------------------------------------- -- MENU MODE COUNTER -- ------------------------------------------------------------------------- - menu_cnt_p : process (CLK) + menu_cnt_p : process (CLK, reset) begin - if (rising_edge(CLK)) then - if (BTN_RST = '1') then - menu_cnt <= (others => '0'); - elsif (menu_mode_en = '1') then + if (reset = '1') then + menu_cnt <= (others => '0'); + elsif (rising_edge(CLK)) then + if (menu_mode_en = '1') then if (menu_cnt = "11") then menu_cnt <= (others => '0'); else @@ -175,13 +168,13 @@ begin -- MASTER COUNTER -- ------------------------------------------------------------------------- - m_cnt_p : process (CLK) + m_cnt_p : process (CLK, reset) begin - if (rising_edge(CLK)) then - if (BTN_RST = '1') then - m_cnt <= (others => '0'); - elsif (m_cnt_en = '1') then - if (m_cnt = "00001111") then + if (reset = '1') then + m_cnt <= (others => '0'); + elsif (rising_edge(CLK)) then + if (m_cnt_en = '1') then + if (m_cnt = "1111") then m_cnt <= (others => '0'); else m_cnt <= m_cnt + 1; @@ -190,19 +183,19 @@ begin end if; end process; - m_din <= std_logic_vector(m_cnt); + m_din <= std_logic_vector(m_cnt) & std_logic_vector(m_cnt); -- ------------------------------------------------------------------------- -- SLAVE COUNTER -- ------------------------------------------------------------------------- - s_cnt_p : process (CLK) + s_cnt_p : process (CLK, reset) begin - if (rising_edge(CLK)) then - if (BTN_RST = '1') then - s_cnt <= (others => '0'); - elsif (s_cnt_en = '1') then - if (s_cnt = "00001111") then + if (reset = '1') then + s_cnt <= (others => '0'); + elsif (rising_edge(CLK)) then + if (s_cnt_en = '1') then + if (s_cnt = "1111") then s_cnt <= (others => '0'); else s_cnt <= s_cnt + 1; @@ -211,7 +204,7 @@ begin end if; end process; - s_din <= std_logic_vector(s_cnt); + s_din <= std_logic_vector(s_cnt) & std_logic_vector(s_cnt); -- ------------------------------------------------------------------------- -- SPI MASTER AND SLAVE @@ -220,33 +213,33 @@ begin master_i : entity work.SPI_MASTER generic map( CLK_FREQ => 50e6, - SCLK_FREQ => 2e6, + SCLK_FREQ => 1e6, SLAVE_COUNT => 1 ) port map ( CLK => CLK, - RST => BTN_RST, + RST => reset, -- SPI MASTER INTERFACE SCLK => M_SCLK, CS_N(0) => M_CS_N, MOSI => M_MOSI, MISO => M_MISO, -- USER INTERFACE - ADDR => (others => '0'), + DIN_ADDR => (others => '0'), DIN => m_din, DIN_LAST => '1', DIN_VLD => m_din_vld, - READY => open, + DIN_RDY => open, DOUT => m_dout, DOUT_VLD => m_dout_vld ); - m_dout_reg_p : process (CLK) + m_dout_reg_p : process (CLK, reset) begin - if (rising_edge(CLK)) then - if (BTN_RST = '1') then - m_dout_reg <= (others => '0'); - elsif (m_dout_vld = '1') then + if (reset = '1') then + m_dout_reg <= (others => '0'); + elsif (rising_edge(CLK)) then + if (m_dout_vld = '1') then m_dout_reg <= m_dout(3 downto 0); end if; end if; @@ -255,7 +248,7 @@ begin slave_i : entity work.SPI_SLAVE port map ( CLK => CLK, - RST => BTN_RST, + RST => reset, -- SPI MASTER INTERFACE SCLK => S_SCLK, CS_N => S_CS_N, @@ -264,17 +257,17 @@ begin -- USER INTERFACE DIN => s_din, DIN_VLD => s_din_vld, - READY => open, + DIN_RDY => open, DOUT => s_dout, DOUT_VLD => s_dout_vld ); - s_dout_reg_p : process (CLK) + s_dout_reg_p : process (CLK, reset) begin - if (rising_edge(CLK)) then - if (BTN_RST = '1') then - s_dout_reg <= (others => '0'); - elsif (s_dout_vld = '1') then + if (reset = '1') then + s_dout_reg <= (others => '0'); + elsif (rising_edge(CLK)) then + if (s_dout_vld = '1') then s_dout_reg <= s_dout(3 downto 0); end if; end if; @@ -288,7 +281,7 @@ begin port map ( CLK => CLK, CLK_EN_1K => clk_en_1k, - ASYNC_RST => BTN_RST, + ASYNC_RST => reset, DATA0 => s_din(3 downto 0), DATA1 => s_dout_reg, DATA2 => m_din(3 downto 0), @@ -298,4 +291,4 @@ begin SSEG_AN => SSEG_AN ); -end RTL; +end architecture; diff --git a/examples/spirit_level/quartus/spirit_level_cyc1000.qpf b/examples/spirit_level/quartus/spirit_level_cyc1000.qpf new file mode 100644 index 0000000..a24927b --- /dev/null +++ b/examples/spirit_level/quartus/spirit_level_cyc1000.qpf @@ -0,0 +1,9 @@ +#------------------------------------------------------------------------------- +# PROJECT: SIMPLE UART FOR FPGA +#------------------------------------------------------------------------------- +# AUTHORS: Jakub Cabal +# LICENSE: The MIT License, please read LICENSE file +# WEBSITE: https://github.com/jakubcabal/uart-for-fpga +#------------------------------------------------------------------------------- + +PROJECT_REVISION = "UART_LOOPBACK_CYC1000" diff --git a/examples/spirit_level/quartus/spirit_level_cyc1000.qsf b/examples/spirit_level/quartus/spirit_level_cyc1000.qsf new file mode 100644 index 0000000..72e8965 --- /dev/null +++ b/examples/spirit_level/quartus/spirit_level_cyc1000.qsf @@ -0,0 +1,38 @@ +#------------------------------------------------------------------------------- +# PROJECT: SPI MASTER AND SLAVE FOR FPGA +#------------------------------------------------------------------------------- +# AUTHORS: Jakub Cabal +# LICENSE: The MIT License, please read LICENSE file +# WEBSITE: https://github.com/jakubcabal/spi-fpga +#------------------------------------------------------------------------------- + +# QUARTUS SETTINGS FILE FOR CYC1000 BOARD +set_global_assignment -name FAMILY "Cyclone 10 LP" +set_global_assignment -name DEVICE 10CL025YU256C8G +set_global_assignment -name TOP_LEVEL_ENTITY SPIRIT_LEVEL_CYC1000 + +# PROJECT VHDL FILES +set_global_assignment -name VHDL_FILE ../../../rtl/spi_master.vhd +set_global_assignment -name VHDL_FILE ../../common/rst_sync.vhd +set_global_assignment -name VHDL_FILE ../spirit_level_cyc1000.vhd + +# TIMING CONSTRAINTS +set_global_assignment -name SDC_FILE ./spirit_level_cyc1000.sdc + +# FPGA PINS ASSIGNMENT +set_location_assignment PIN_M2 -to CLK_12M +set_location_assignment PIN_N6 -to RST_BTN_N + +set_location_assignment PIN_F3 -to SCLK +set_location_assignment PIN_D1 -to CS_N +set_location_assignment PIN_G2 -to MOSI +set_location_assignment PIN_G1 -to MISO + +set_location_assignment PIN_M6 -to USER_LEDS[0] +set_location_assignment PIN_T4 -to USER_LEDS[1] +set_location_assignment PIN_T3 -to USER_LEDS[2] +set_location_assignment PIN_R3 -to USER_LEDS[3] +set_location_assignment PIN_T2 -to USER_LEDS[4] +set_location_assignment PIN_R4 -to USER_LEDS[5] +set_location_assignment PIN_N5 -to USER_LEDS[6] +set_location_assignment PIN_N3 -to USER_LEDS[7] diff --git a/examples/spirit_level/quartus/spirit_level_cyc1000.sdc b/examples/spirit_level/quartus/spirit_level_cyc1000.sdc new file mode 100644 index 0000000..20f9f8f --- /dev/null +++ b/examples/spirit_level/quartus/spirit_level_cyc1000.sdc @@ -0,0 +1,9 @@ +#------------------------------------------------------------------------------- +# PROJECT: SPI MASTER AND SLAVE FOR FPGA +#------------------------------------------------------------------------------- +# AUTHORS: Jakub Cabal +# LICENSE: The MIT License, please read LICENSE file +# WEBSITE: https://github.com/jakubcabal/spi-fpga +#------------------------------------------------------------------------------- + +create_clock -name CLK12M -period 12MHz [get_ports {CLK_12M}] diff --git a/examples/spirit_level/spirit_level_cyc1000.vhd b/examples/spirit_level/spirit_level_cyc1000.vhd new file mode 100644 index 0000000..17c1453 --- /dev/null +++ b/examples/spirit_level/spirit_level_cyc1000.vhd @@ -0,0 +1,196 @@ +-------------------------------------------------------------------------------- +-- PROJECT: SPI MASTER AND SLAVE FOR FPGA +-------------------------------------------------------------------------------- +-- AUTHORS: Jakub Cabal +-- LICENSE: The MIT License, please read LICENSE file +-- WEBSITE: https://github.com/jakubcabal/spi-fpga +-------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity SPIRIT_LEVEL_CYC1000 is + Port ( + CLK_12M : in std_logic; -- system clock 12 MHz + RST_BTN_N : in std_logic; -- low active reset button + -- SPI MASTER INTERFACE TO LIS3DH SENSOR + SCLK : out std_logic; + CS_N : out std_logic; + MOSI : out std_logic; + MISO : in std_logic; + -- USER LEDS + USER_LEDS : out std_logic_vector(8-1 downto 0) + ); +end entity; + +architecture RTL of SPIRIT_LEVEL_CYC1000 is + + signal rst_btn : std_logic; + signal reset : std_logic; + + signal spi_din : std_logic_vector(8-1 downto 0); + signal spi_din_last : std_logic; + signal spi_din_vld : std_logic; + signal spi_din_rdy : std_logic; + signal spi_dout : std_logic_vector(8-1 downto 0); + signal spi_dout_vld : std_logic; + + type state is (cfg1_addr, cfg1_wr, out_addr, out_rd, out_do); + signal fsm_pstate : state; + signal fsm_nstate : state; + + signal sensor_wr : std_logic; + signal sensor_data : std_logic_vector(8-1 downto 0); + signal sensor_sigd : signed(8-1 downto 0); + +begin + + rst_btn <= not RST_BTN_N; + + rst_sync_i : entity work.RST_SYNC + port map ( + CLK => CLK_12M, + ASYNC_RST => rst_btn, + SYNCED_RST => reset + ); + + spi_master_i : entity work.SPI_MASTER + generic map( + CLK_FREQ => 12e6, + SCLK_FREQ => 1e6, + SLAVE_COUNT => 1 + ) + port map ( + CLK => CLK_12M, + RST => reset, + -- SPI MASTER INTERFACE + SCLK => SCLK, + CS_N(0) => CS_N, + MOSI => MOSI, + MISO => MISO, + -- USER INTERFACE + DIN_ADDR => (others => '0'), + DIN => spi_din, + DIN_LAST => spi_din_last, + DIN_VLD => spi_din_vld, + DIN_RDY => spi_din_rdy, + DOUT => spi_dout, + DOUT_VLD => spi_dout_vld + ); + + -- ------------------------------------------------------------------------- + -- FSM + -- ------------------------------------------------------------------------- + + process (CLK_12M) + begin + if (rising_edge(CLK_12M)) then + if (reset = '1') then + fsm_pstate <= cfg1_addr; + else + fsm_pstate <= fsm_nstate; + end if; + end if; + end process; + + process (fsm_pstate, spi_din_rdy, spi_dout_vld) + begin + fsm_nstate <= fsm_pstate; + spi_din <= (others => '0'); + spi_din_last <= '0'; + spi_din_vld <= '0'; + sensor_wr <= '0'; + + case fsm_pstate is + when cfg1_addr => + spi_din <= "00100000"; + spi_din_vld <= '1'; + if (spi_din_rdy = '1') then + fsm_nstate <= cfg1_wr; + end if; + + when cfg1_wr => + spi_din <= X"37"; + spi_din_vld <= '1'; + spi_din_last <= '1'; + if (spi_din_rdy = '1') then + fsm_nstate <= out_addr; + end if; + + when out_addr => + spi_din <= "10101001"; + spi_din_vld <= '1'; + if (spi_din_rdy = '1') then + fsm_nstate <= out_rd; + end if; + + when out_rd => + spi_din_vld <= '1'; + spi_din_last <= '1'; + if (spi_din_rdy = '1') then + fsm_nstate <= out_do; + end if; + + when out_do => + if (spi_dout_vld = '1') then + sensor_wr <= '1'; + fsm_nstate <= out_addr; + end if; + end case; + end process; + + -- ------------------------------------------------------------------------- + -- SENSOR DATA REGISTER + -- ------------------------------------------------------------------------- + + process (CLK_12M) + begin + if (rising_edge(CLK_12M)) then + if (sensor_wr = '1') then + sensor_data <= spi_dout; + end if; + end if; + end process; + + -- ------------------------------------------------------------------------- + -- USER LEDS LOGIC + -- ------------------------------------------------------------------------- + + sensor_sigd <= signed(sensor_data); + + process (CLK_12M) + begin + if (rising_edge(CLK_12M)) then + USER_LEDS <= "00000000"; + if (sensor_sigd <= -16) then + USER_LEDS <= "10000000"; + end if; + if (sensor_sigd > -16) and (sensor_sigd <= -12) then + USER_LEDS <= "01000000"; + end if; + if (sensor_sigd > -12) and (sensor_sigd <= -8) then + USER_LEDS <= "00100000"; + end if; + if (sensor_sigd > -8) and (sensor_sigd <= -4) then + USER_LEDS <= "00010000"; + end if; + if (sensor_sigd > -4) and (sensor_sigd < 4) then + USER_LEDS <= "00011000"; + end if; + if (sensor_sigd >= 4) and (sensor_sigd < 8) then + USER_LEDS <= "00001000"; + end if; + if (sensor_sigd >= 8) and (sensor_sigd < 12) then + USER_LEDS <= "00000100"; + end if; + if (sensor_sigd >= 12) and (sensor_sigd < 16) then + USER_LEDS <= "00000010"; + end if; + if (sensor_sigd >= 16) then + USER_LEDS <= "00000001"; + end if; + end if; + end process; + +end architecture; diff --git a/rtl/spi_master.vhd b/rtl/spi_master.vhd index 5daa569..10641ae 100644 --- a/rtl/spi_master.vhd +++ b/rtl/spi_master.vhd @@ -1,29 +1,10 @@ -------------------------------------------------------------------------------- -- PROJECT: SPI MASTER AND SLAVE FOR FPGA -------------------------------------------------------------------------------- --- NAME: SPI_MASTER -- AUTHORS: Jakub Cabal --- LICENSE: LGPL-3.0, please read LICENSE file +-- LICENSE: The MIT License, please read LICENSE file -- WEBSITE: https://github.com/jakubcabal/spi-fpga -------------------------------------------------------------------------------- --- COPYRIGHT NOTICE: --------------------------------------------------------------------------------- --- SPI MASTER AND SLAVE FOR FPGA --- Copyright (C) 2016 Jakub Cabal --- --- This source file is free software: you can redistribute it and/or modify --- it under the terms of the GNU Lesser General Public License as published by --- the Free Software Foundation, either version 3 of the License, or --- (at your option) any later version. --- --- This source file is distributed in the hope that it will be useful, --- but WITHOUT ANY WARRANTY; without even the implied warranty of --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --- GNU Lesser General Public License for more details. --- --- You should have received a copy of the GNU Lesser General Public License --- along with this program. If not, see . --------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; @@ -36,6 +17,7 @@ entity SPI_MASTER is Generic ( CLK_FREQ : natural := 50e6; -- set system clock frequency in Hz SCLK_FREQ : natural := 5e6; -- set SPI clock frequency in Hz (condition: SCLK_FREQ <= CLK_FREQ/10) + WORD_SIZE : natural := 8; -- size of transfer word in bits, must be power of two SLAVE_COUNT : natural := 1 -- count of SPI slaves ); Port ( @@ -47,63 +29,62 @@ entity SPI_MASTER is MOSI : out std_logic; -- SPI serial data from master to slave MISO : in std_logic; -- SPI serial data from slave to master -- INPUT USER INTERFACE - ADDR : in std_logic_vector(integer(ceil(log2(real(SLAVE_COUNT))))-1 downto 0); -- SPI slave address - DIN : in std_logic_vector(7 downto 0); -- input data for SPI slave - DIN_LAST : in std_logic; -- when DIN_LAST = 1, after transmit these input data is asserted CS_N - DIN_VLD : in std_logic; -- when DIN_VLD = 1, input data are valid - READY : out std_logic; -- when READY = 1, valid input data are accept + DIN : in std_logic_vector(WORD_SIZE-1 downto 0); -- data for transmission to SPI slave + DIN_ADDR : in std_logic_vector(natural(ceil(log2(real(SLAVE_COUNT))))-1 downto 0); -- SPI slave address + DIN_LAST : in std_logic; -- when DIN_LAST = 1, last data word, after transmit will be asserted CS_N + DIN_VLD : in std_logic; -- when DIN_VLD = 1, data for transmission are valid + DIN_RDY : out std_logic; -- when DIN_RDY = 1, SPI master is ready to accept valid data for transmission -- OUTPUT USER INTERFACE - DOUT : out std_logic_vector(7 downto 0); -- output data from SPI slave - DOUT_VLD : out std_logic -- when DOUT_VLD = 1, output data are valid + DOUT : out std_logic_vector(WORD_SIZE-1 downto 0); -- received data from SPI slave + DOUT_VLD : out std_logic -- when DOUT_VLD = 1, received data are valid ); -end SPI_MASTER; +end entity; architecture RTL of SPI_MASTER is - constant DIVIDER_VALUE : integer := (CLK_FREQ/SCLK_FREQ)/2; - constant WIDTH_CLK_CNT : integer := integer(ceil(log2(real(DIVIDER_VALUE)))); - constant WIDTH_ADDR : integer := integer(ceil(log2(real(SLAVE_COUNT)))); - - signal addr_reg : std_logic_vector(WIDTH_ADDR-1 downto 0); - signal sys_clk_cnt : unsigned(WIDTH_CLK_CNT-1 downto 0); - signal sys_clk_cnt_max : std_logic; - signal sys_clk_cnt_rst : std_logic; - signal spi_clk : std_logic; - signal spi_clk_en : std_logic; - signal din_last_reg_n : std_logic; - signal first_edge_en : std_logic; - signal second_edge_en : std_logic; - signal chip_select_n : std_logic; - signal load_data : std_logic; - signal miso_reg : std_logic; - signal shreg : std_logic_vector(7 downto 0); - signal bit_cnt : unsigned(2 downto 0); - signal bit_cnt_max : std_logic; - signal bit_cnt_rst : std_logic; - signal rx_data_vld : std_logic; - signal master_ready : std_logic; - - type state is (idle, first_edge, second_edge, transmit_end, transmit_gap); - signal present_state, next_state : state; + constant DIVIDER_VALUE : natural := (CLK_FREQ/SCLK_FREQ)/2; + constant WIDTH_CLK_CNT : natural := natural(ceil(log2(real(DIVIDER_VALUE)))); + constant WIDTH_ADDR : natural := natural(ceil(log2(real(SLAVE_COUNT)))); + constant BIT_CNT_WIDTH : natural := natural(ceil(log2(real(WORD_SIZE)))); + + type state_t is (idle, first_edge, second_edge, transmit_end, transmit_gap); + + signal addr_reg : unsigned(WIDTH_ADDR-1 downto 0); + signal sys_clk_cnt : unsigned(WIDTH_CLK_CNT-1 downto 0); + signal sys_clk_cnt_max : std_logic; + signal spi_clk : std_logic; + signal spi_clk_rst : std_logic; + signal din_last_reg_n : std_logic; + signal first_edge_en : std_logic; + signal second_edge_en : std_logic; + signal chip_select_n : std_logic; + signal load_data : std_logic; + signal miso_reg : std_logic; + signal shreg : std_logic_vector(WORD_SIZE-1 downto 0); + signal bit_cnt : unsigned(BIT_CNT_WIDTH-1 downto 0); + signal bit_cnt_max : std_logic; + signal rx_data_vld : std_logic; + signal master_ready : std_logic; + signal present_state : state_t; + signal next_state : state_t; begin ASSERT (DIVIDER_VALUE >= 5) REPORT "condition: SCLK_FREQ <= CLK_FREQ/10" SEVERITY ERROR; load_data <= master_ready and DIN_VLD; - READY <= master_ready; + DIN_RDY <= master_ready; -- ------------------------------------------------------------------------- -- SYSTEM CLOCK COUNTER -- ------------------------------------------------------------------------- sys_clk_cnt_max <= '1' when (to_integer(sys_clk_cnt) = DIVIDER_VALUE-1) else '0'; - sys_clk_cnt_rst <= RST or sys_clk_cnt_max; sys_clk_cnt_reg_p : process (CLK) begin if (rising_edge(CLK)) then - if (sys_clk_cnt_rst = '1') then + if (RST = '1' or sys_clk_cnt_max = '1') then sys_clk_cnt <= (others => '0'); else sys_clk_cnt <= sys_clk_cnt + 1; @@ -118,7 +99,7 @@ begin spi_clk_gen_p : process (CLK) begin if (rising_edge(CLK)) then - if (RST = '1' or spi_clk_en = '0') then + if (RST = '1' or spi_clk_rst = '1') then spi_clk <= '0'; elsif (sys_clk_cnt_max = '1') then spi_clk <= not spi_clk; @@ -132,13 +113,12 @@ begin -- BIT COUNTER -- ------------------------------------------------------------------------- - bit_cnt_max <= '1' when (bit_cnt = "111") else '0'; - bit_cnt_rst <= RST or not spi_clk_en; + bit_cnt_max <= '1' when (bit_cnt = WORD_SIZE-1) else '0'; bit_cnt_p : process (CLK) begin if (rising_edge(CLK)) then - if (bit_cnt_rst = '1') then + if (RST = '1' or spi_clk_rst = '1') then bit_cnt <= (others => '0'); elsif (second_edge_en = '1') then bit_cnt <= bit_cnt + 1; @@ -156,20 +136,26 @@ begin if (RST = '1') then addr_reg <= (others => '0'); elsif (load_data = '1') then - addr_reg <= ADDR; + addr_reg <= unsigned(DIN_ADDR); end if; end if; end process; - cs_n_g : for i in 0 to SLAVE_COUNT-1 generate - cs_n_p : process (addr_reg, chip_select_n) - begin - if (to_integer(unsigned(addr_reg)) = i) then - CS_N(i) <= chip_select_n; - else - CS_N(i) <= '1'; - end if; - end process; + one_slave_g: if (SLAVE_COUNT = 1) generate + CS_N(0) <= chip_select_n; + end generate; + + more_slaves_g: if (SLAVE_COUNT > 1) generate + cs_n_g : for i in 0 to SLAVE_COUNT-1 generate + cs_n_p : process (addr_reg, chip_select_n) + begin + if (addr_reg = i) then + CS_N(i) <= chip_select_n; + else + CS_N(i) <= '1'; + end if; + end process; + end generate; end generate; -- ------------------------------------------------------------------------- @@ -210,13 +196,13 @@ begin if (load_data = '1') then shreg <= DIN; elsif (second_edge_en = '1') then - shreg <= shreg(6 downto 0) & miso_reg; + shreg <= shreg(WORD_SIZE-2 downto 0) & miso_reg; end if; end if; end process; DOUT <= shreg; - MOSI <= shreg(7); + MOSI <= shreg(WORD_SIZE-1); -- ------------------------------------------------------------------------- -- DATA OUT VALID RESISTER @@ -305,7 +291,7 @@ begin when idle => master_ready <= '1'; chip_select_n <= not din_last_reg_n; - spi_clk_en <= '0'; + spi_clk_rst <= '1'; first_edge_en <= '0'; second_edge_en <= '0'; rx_data_vld <= '0'; @@ -313,7 +299,7 @@ begin when first_edge => master_ready <= '0'; chip_select_n <= '0'; - spi_clk_en <= '1'; + spi_clk_rst <= '0'; first_edge_en <= sys_clk_cnt_max; second_edge_en <= '0'; rx_data_vld <= '0'; @@ -321,7 +307,7 @@ begin when second_edge => master_ready <= '0'; chip_select_n <= '0'; - spi_clk_en <= '1'; + spi_clk_rst <= '0'; first_edge_en <= '0'; second_edge_en <= sys_clk_cnt_max; rx_data_vld <= '0'; @@ -329,7 +315,7 @@ begin when transmit_end => master_ready <= '0'; chip_select_n <= '0'; - spi_clk_en <= '0'; + spi_clk_rst <= '1'; first_edge_en <= '0'; second_edge_en <= '0'; rx_data_vld <= sys_clk_cnt_max; @@ -337,19 +323,19 @@ begin when transmit_gap => master_ready <= '0'; chip_select_n <= not din_last_reg_n; - spi_clk_en <= '0'; + spi_clk_rst <= '1'; first_edge_en <= '0'; second_edge_en <= '0'; rx_data_vld <= '0'; when others => master_ready <= '0'; - chip_select_n <= '1'; - spi_clk_en <= '0'; + chip_select_n <= not din_last_reg_n; + spi_clk_rst <= '1'; first_edge_en <= '0'; second_edge_en <= '0'; rx_data_vld <= '0'; end case; end process; -end RTL; +end architecture; diff --git a/rtl/spi_slave.vhd b/rtl/spi_slave.vhd index 8b0f64a..c010e64 100644 --- a/rtl/spi_slave.vhd +++ b/rtl/spi_slave.vhd @@ -1,37 +1,22 @@ -------------------------------------------------------------------------------- -- PROJECT: SPI MASTER AND SLAVE FOR FPGA -------------------------------------------------------------------------------- --- NAME: SPI_SLAVE -- AUTHORS: Jakub Cabal --- LICENSE: LGPL-3.0, please read LICENSE file +-- LICENSE: The MIT License, please read LICENSE file -- WEBSITE: https://github.com/jakubcabal/spi-fpga -------------------------------------------------------------------------------- --- COPYRIGHT NOTICE: --------------------------------------------------------------------------------- --- SPI MASTER AND SLAVE FOR FPGA --- Copyright (C) 2016 Jakub Cabal --- --- This source file is free software: you can redistribute it and/or modify --- it under the terms of the GNU Lesser General Public License as published by --- the Free Software Foundation, either version 3 of the License, or --- (at your option) any later version. --- --- This source file is distributed in the hope that it will be useful, --- but WITHOUT ANY WARRANTY; without even the implied warranty of --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --- GNU Lesser General Public License for more details. --- --- You should have received a copy of the GNU Lesser General Public License --- along with this program. If not, see . --------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; +use IEEE.MATH_REAL.ALL; -- THE SPI SLAVE MODULE SUPPORT ONLY SPI MODE 0 (CPOL=0, CPHA=0)!!! entity SPI_SLAVE is + Generic ( + WORD_SIZE : natural := 8 -- size of transfer word in bits, must be power of two + ); Port ( CLK : in std_logic; -- system clock RST : in std_logic; -- high active synchronous reset @@ -41,30 +26,55 @@ entity SPI_SLAVE is MOSI : in std_logic; -- SPI serial data from master to slave MISO : out std_logic; -- SPI serial data from slave to master -- USER INTERFACE - DIN : in std_logic_vector(7 downto 0); -- input data for SPI master - DIN_VLD : in std_logic; -- when DIN_VLD = 1, input data are valid - READY : out std_logic; -- when READY = 1, valid input data are accept - DOUT : out std_logic_vector(7 downto 0); -- output data from SPI master - DOUT_VLD : out std_logic -- when DOUT_VLD = 1, output data are valid + DIN : in std_logic_vector(WORD_SIZE-1 downto 0); -- data for transmission to SPI master + DIN_VLD : in std_logic; -- when DIN_VLD = 1, data for transmission are valid + DIN_RDY : out std_logic; -- when DIN_RDY = 1, SPI slave is ready to accept valid data for transmission + DOUT : out std_logic_vector(WORD_SIZE-1 downto 0); -- received data from SPI master + DOUT_VLD : out std_logic -- when DOUT_VLD = 1, received data are valid ); -end SPI_SLAVE; +end entity; architecture RTL of SPI_SLAVE is + constant BIT_CNT_WIDTH : natural := natural(ceil(log2(real(WORD_SIZE)))); + + signal sclk_meta : std_logic; + signal cs_n_meta : std_logic; + signal mosi_meta : std_logic; + signal sclk_reg : std_logic; + signal cs_n_reg : std_logic; + signal mosi_reg : std_logic; signal spi_clk_reg : std_logic; signal spi_clk_redge_en : std_logic; signal spi_clk_fedge_en : std_logic; - signal bit_cnt : unsigned(2 downto 0); + signal bit_cnt : unsigned(BIT_CNT_WIDTH-1 downto 0); signal bit_cnt_max : std_logic; signal last_bit_en : std_logic; signal load_data_en : std_logic; - signal data_shreg : std_logic_vector(7 downto 0); + signal data_shreg : std_logic_vector(WORD_SIZE-1 downto 0); signal slave_ready : std_logic; signal shreg_busy : std_logic; signal rx_data_vld : std_logic; begin + -- ------------------------------------------------------------------------- + -- INPUT SYNCHRONIZATION REGISTERS + -- ------------------------------------------------------------------------- + + -- Synchronization registers to eliminate possible metastability. + sync_ffs_p : process (CLK) + begin + if (rising_edge(CLK)) then + sclk_meta <= SCLK; + cs_n_meta <= CS_N; + mosi_meta <= MOSI; + sclk_reg <= sclk_meta; + cs_n_reg <= cs_n_meta; + mosi_reg <= mosi_meta; + end if; + end process; + -- ------------------------------------------------------------------------- -- SPI CLOCK REGISTER -- ------------------------------------------------------------------------- @@ -76,7 +86,7 @@ begin if (RST = '1') then spi_clk_reg <= '0'; else - spi_clk_reg <= SCLK; + spi_clk_reg <= sclk_reg; end if; end if; end process; @@ -85,23 +95,23 @@ begin -- SPI CLOCK EDGES FLAGS -- ------------------------------------------------------------------------- - -- Falling edge is detect when SCLK=0 and spi_clk_reg=1. - spi_clk_fedge_en <= not SCLK and spi_clk_reg; - -- Rising edge is detect when SCLK=1 and spi_clk_reg=0. - spi_clk_redge_en <= SCLK and not spi_clk_reg; + -- Falling edge is detect when sclk_reg=0 and spi_clk_reg=1. + spi_clk_fedge_en <= not sclk_reg and spi_clk_reg; + -- Rising edge is detect when sclk_reg=1 and spi_clk_reg=0. + spi_clk_redge_en <= sclk_reg and not spi_clk_reg; -- ------------------------------------------------------------------------- -- RECEIVED BITS COUNTER -- ------------------------------------------------------------------------- -- The counter counts received bits from the master. Counter is enabled when - -- falling edge of SPI clock is detected and not asserted CS_N. + -- falling edge of SPI clock is detected and not asserted cs_n_reg. bit_cnt_p : process (CLK) begin if (rising_edge(CLK)) then if (RST = '1') then bit_cnt <= (others => '0'); - elsif (spi_clk_fedge_en = '1' and CS_N = '0') then + elsif (spi_clk_fedge_en = '1' and cs_n_reg = '0') then if (bit_cnt_max = '1') then bit_cnt <= (others => '0'); else @@ -112,7 +122,7 @@ begin end process; -- The flag of maximal value of the bit counter. - bit_cnt_max <= '1' when (bit_cnt = "111") else '0'; + bit_cnt_max <= '1' when (bit_cnt = WORD_SIZE-1) else '0'; -- ------------------------------------------------------------------------- -- LAST BIT FLAG REGISTER @@ -150,7 +160,7 @@ begin if (RST = '1') then shreg_busy <= '0'; else - if (DIN_VLD = '1' and (CS_N = '1' or rx_data_vld = '1')) then + if (DIN_VLD = '1' and (cs_n_reg = '1' or rx_data_vld = '1')) then shreg_busy <= '1'; elsif (rx_data_vld = '1') then shreg_busy <= '0'; @@ -161,9 +171,9 @@ begin end if; end process; - -- The SPI slave is ready for accept new input data when CS_N is assert and + -- The SPI slave is ready for accept new input data when cs_n_reg is assert and -- shift register not busy or when received data are valid. - slave_ready <= (CS_N and not shreg_busy) or rx_data_vld; + slave_ready <= (cs_n_reg and not shreg_busy) or rx_data_vld; -- The new input data is loaded into the shift register when the SPI slave -- is ready and input data are valid. @@ -180,8 +190,8 @@ begin if (rising_edge(CLK)) then if (load_data_en = '1') then data_shreg <= DIN; - elsif (spi_clk_redge_en = '1' and CS_N = '0') then - data_shreg <= data_shreg(6 downto 0) & MOSI; + elsif (spi_clk_redge_en = '1' and cs_n_reg = '0') then + data_shreg <= data_shreg(WORD_SIZE-2 downto 0) & mosi_reg; end if; end if; end process; @@ -191,14 +201,14 @@ begin -- ------------------------------------------------------------------------- -- The output MISO register ensures that the bits are transmit to the master - -- when is not assert CS_N and falling edge of SPI clock is detected. + -- when is not assert cs_n_reg and falling edge of SPI clock is detected. miso_p : process (CLK) begin if (rising_edge(CLK)) then if (load_data_en = '1') then - MISO <= DIN(7); - elsif (spi_clk_fedge_en = '1' and CS_N = '0') then - MISO <= data_shreg(7); + MISO <= DIN(WORD_SIZE-1); + elsif (spi_clk_fedge_en = '1' and cs_n_reg = '0') then + MISO <= data_shreg(WORD_SIZE-1); end if; end if; end process; @@ -207,8 +217,8 @@ begin -- ASSIGNING OUTPUT SIGNALS -- ------------------------------------------------------------------------- - READY <= slave_ready; + DIN_RDY <= slave_ready; DOUT <= data_shreg; DOUT_VLD <= rx_data_vld; -end RTL; +end architecture; diff --git a/sim/simulation.tcl b/sim/simulation.tcl deleted file mode 100644 index 5abf98e..0000000 --- a/sim/simulation.tcl +++ /dev/null @@ -1,39 +0,0 @@ -#------------------------------------------------------------------------------- -# PROJECT: SPI MASTER AND SLAVE FOR FPGA -#------------------------------------------------------------------------------- -# NAME: SIMULATION TCL SCRIPT FOR MODELSIM -# AUTHORS: Jakub Cabal -# LICENSE: LGPL-3.0, please read LICENSE file -# WEBSITE: https://github.com/jakubcabal/spi-fpga -#------------------------------------------------------------------------------- -# COPYRIGHT NOTICE: -#------------------------------------------------------------------------------- -# SPI MASTER AND SLAVE FOR FPGA -# Copyright (C) 2017 Jakub Cabal -# -# This source file is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This source file is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . -#------------------------------------------------------------------------------- - -# Compile VHDL files -vlib work -vcom ../rtl/spi_master.vhd -vcom ../rtl/spi_slave.vhd -vcom ./spi_tb.vhd -# Load testbench -vsim work.spi_tb -# Setup and start simulation -add wave * -add wave sim:/spi_tb/master_i/* -add wave sim:/spi_tb/slave_i/* -run 6 us \ No newline at end of file diff --git a/sim/spi_master_tb.vhd b/sim/spi_master_tb.vhd new file mode 100644 index 0000000..8cb76b5 --- /dev/null +++ b/sim/spi_master_tb.vhd @@ -0,0 +1,218 @@ +-------------------------------------------------------------------------------- +-- PROJECT: SPI MASTER AND SLAVE FOR FPGA +-------------------------------------------------------------------------------- +-- AUTHORS: Jakub Cabal +-- LICENSE: The MIT License, please read LICENSE file +-- WEBSITE: https://github.com/jakubcabal/spi-fpga +-------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; +use IEEE.MATH_REAL.ALL; + +entity SPI_MASTER_TB is + Generic ( + CLK_FREQ : natural := 50e6; -- system clock frequency in Hz + SPI_FREQ : natural := 1e6; -- spi clock frequency in Hz + WORD_SIZE : natural := 8; -- size of transfer word in bits, must be power of two + TRANS_COUNT : natural := 1e4 -- number of test transaction + ); +end entity; + +architecture SIM of SPI_MASTER_TB is + + constant CLK_PERIOD : time := 1 ns * integer(real(1e9)/real(CLK_FREQ)); + constant SPI_PERIOD : time := 1 ns * integer(real(1e9)/real(SPI_FREQ)); + constant RX_OFFSET : natural := 42; + constant TX_OFFSET : natural := 11; + + signal CLK : std_logic; + signal RST : std_logic; + + signal sclk : std_logic; + signal cs_n : std_logic; + signal mosi : std_logic; + signal miso : std_logic; + + signal udi : std_logic_vector(WORD_SIZE-1 downto 0); + signal udi_vld : std_logic; + signal udi_rdy : std_logic; + signal udo : std_logic_vector(WORD_SIZE-1 downto 0); + signal udo_exp : std_logic_vector(WORD_SIZE-1 downto 0); + signal udo_vld : std_logic; + + signal spi_sdi : std_logic_vector(WORD_SIZE-1 downto 0); + signal spi_sdo : std_logic_vector(WORD_SIZE-1 downto 0); + signal spi_sdo_exp : std_logic_vector(WORD_SIZE-1 downto 0); + + signal spi_model_done : std_logic := '0'; + signal udi_done : std_logic := '0'; + signal udo_done : std_logic := '0'; + signal sim_done : std_logic := '0'; + signal rand_int : integer := 0; + signal count_rx : integer; + signal count_tx : integer; + + procedure SPI_SLAVE ( + signal SSM_SDI : in std_logic_vector(WORD_SIZE-1 downto 0); + signal SSM_SDO : out std_logic_vector(WORD_SIZE-1 downto 0); + signal SSM_SCLK : in std_logic; + signal SSM_CS_N : in std_logic; + signal SSM_MOSI : in std_logic; + signal SSM_MISO : out std_logic + ) is + begin + wait until SSM_CS_N = '0'; + for i in 0 to (WORD_SIZE-1) loop + SSM_MISO <= SSM_SDI(WORD_SIZE-1-i); + wait until rising_edge(SSM_SCLK); + SSM_SDO(WORD_SIZE-1-i) <= SSM_MOSI; + wait until falling_edge(SSM_SCLK); + end loop; + end procedure; + +begin + + rand_int_p : process + variable seed1, seed2: positive; + variable rand : real; + begin + uniform(seed1, seed2, rand); + rand_int <= integer(rand*real(20)); + --report "Random number X: " & integer'image(rand_int); + wait for CLK_PERIOD; + if (sim_done = '1') then + wait; + end if; + end process; + + dut : entity work.SPI_MASTER + generic map ( + CLK_FREQ => CLK_FREQ, + SCLK_FREQ => SPI_FREQ, + WORD_SIZE => WORD_SIZE + ) + port map ( + CLK => CLK, + RST => RST, + -- SPI SLAVE INTERFACE + SCLK => sclk, + CS_N(0) => cs_n, + MOSI => mosi, + MISO => miso, + -- USER INTERFACE + DIN => udi, + DIN_ADDR => (others => '0'), + DIN_LAST => '1', + DIN_VLD => udi_vld, + DIN_RDY => udi_rdy, + DOUT => udo, + DOUT_VLD => udo_vld + ); + + clk_gen_p : process + begin + CLK <= '0'; + wait for CLK_PERIOD/2; + CLK <= '1'; + wait for CLK_PERIOD/2; + if (sim_done = '1') then + wait; + end if; + end process; + + rst_gen_p : process + begin + report "======== SIMULATION START! ========"; + report "Total transactions for master to slave direction: " & integer'image(TRANS_COUNT); + report "Total transactions for slave to master direction: " & integer'image(TRANS_COUNT); + RST <= '1'; + wait for CLK_PERIOD*3; + RST <= '0'; + wait; + end process; + + -- ------------------------------------------------------------------------- + -- DUT TEST + -- ------------------------------------------------------------------------- + + spi_slave_model_p : process + begin + count_tx <= 1; + wait until RST = '0'; + for i in 0 to TRANS_COUNT-1 loop + spi_sdi <= std_logic_vector(to_unsigned(((i+RX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); + spi_sdo_exp <= std_logic_vector(to_unsigned(((i+TX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); + SPI_SLAVE(spi_sdi, spi_sdo, sclk, cs_n, mosi, miso); + if (spi_sdo = spi_sdo_exp) then + if ((count_tx mod (TRANS_COUNT/10)) = 0) then + report "Transactions received from master: " & integer'image(count_tx); + end if; + else + report "======== UNEXPECTED TRANSACTION ON MOSI SIGNAL (master to slave)! ========" severity failure; + end if; + count_tx <= count_tx + 1; + end loop; + spi_model_done <= '1'; + wait; + end process; + + spi_master_udi_p : process + begin + wait until RST = '0'; + wait until rising_edge(CLK); + wait for CLK_PERIOD/2; + for i in 0 to TRANS_COUNT-1 loop + udi <= std_logic_vector(to_unsigned(((i+TX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); + udi_vld <= '1'; + if (udi_rdy = '0') then + wait until udi_rdy = '1'; + wait for CLK_PERIOD/2; + end if; + wait for CLK_PERIOD; + udi_vld <= '0'; + wait for rand_int*CLK_PERIOD; + end loop; + udi_done <= '1'; + wait; + end process; + + spi_master_udo_p : process + begin + count_rx <= 1; + for i in 0 to TRANS_COUNT-1 loop + udo_exp <= std_logic_vector(to_unsigned(((i+RX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); + wait until udo_vld = '1'; + if (udo = udo_exp) then + if ((count_rx mod (TRANS_COUNT/10)) = 0) then + report "Transactions received from slave: " & integer'image(count_rx); + end if; + else + report "======== UNEXPECTED TRANSACTION ON DOUT SIGNAL (slave to master)! ========" severity failure; + end if; + count_rx <= count_rx + 1; + wait for CLK_PERIOD; + end loop; + udo_done <= '1'; + wait; + end process; + + -- ------------------------------------------------------------------------- + -- TEST DONE CHECK + -- ------------------------------------------------------------------------- + + test_done_p : process + variable v_test_done : std_logic; + begin + v_test_done := spi_model_done and udi_done and udo_done; + if (v_test_done = '1') then + wait for 100*CLK_PERIOD; + sim_done <= '1'; + report "======== SIMULATION SUCCESSFULLY COMPLETED! ========"; + wait; + end if; + wait for CLK_PERIOD; + end process; + +end architecture; diff --git a/sim/spi_master_tb_ghdl_run.sh b/sim/spi_master_tb_ghdl_run.sh new file mode 100644 index 0000000..a25ae5a --- /dev/null +++ b/sim/spi_master_tb_ghdl_run.sh @@ -0,0 +1,14 @@ +#!/bin/bash +#------------------------------------------------------------------------------- +# PROJECT: SPI MASTER AND SLAVE FOR FPGA +#------------------------------------------------------------------------------- +# AUTHORS: Jakub Cabal +# LICENSE: The MIT License, please read LICENSE file +# WEBSITE: https://github.com/jakubcabal/spi-fpga +#------------------------------------------------------------------------------- + +# Setup simulation +sh ./spi_master_tb_ghdl_setup.sh + +# Run the simulation +ghdl -r SPI_MASTER_TB diff --git a/sim/spi_master_tb_ghdl_setup.sh b/sim/spi_master_tb_ghdl_setup.sh new file mode 100644 index 0000000..2e4cd6d --- /dev/null +++ b/sim/spi_master_tb_ghdl_setup.sh @@ -0,0 +1,19 @@ +#!/bin/bash +#------------------------------------------------------------------------------- +# PROJECT: SPI MASTER AND SLAVE FOR FPGA +#------------------------------------------------------------------------------- +# AUTHORS: Jakub Cabal +# LICENSE: The MIT License, please read LICENSE file +# WEBSITE: https://github.com/jakubcabal/spi-fpga +#------------------------------------------------------------------------------- + +# Analyse sources +ghdl -a ../rtl/spi_master.vhd +ghdl -a ./spi_master_tb.vhd + +# Elaborate the top-level +ghdl -e SPI_MASTER_TB + +# Run the simulation +# The following command is allocated to a separate script due to CI purposes. +#ghdl -r SPI_MASTER_TB diff --git a/sim/spi_master_tb_msim_run.tcl b/sim/spi_master_tb_msim_run.tcl new file mode 100644 index 0000000..5c6e3be --- /dev/null +++ b/sim/spi_master_tb_msim_run.tcl @@ -0,0 +1,22 @@ +#------------------------------------------------------------------------------- +# PROJECT: SPI MASTER AND SLAVE FOR FPGA +#------------------------------------------------------------------------------- +# AUTHORS: Jakub Cabal +# LICENSE: The MIT License, please read LICENSE file +# WEBSITE: https://github.com/jakubcabal/spi-fpga +#------------------------------------------------------------------------------- + +# Create work library +vlib work + +# Compile VHDL files +vcom -93 ../rtl/spi_master.vhd +vcom -93 ./spi_master_tb.vhd + +# Load testbench +vsim work.spi_master_tb + +# Setup and start simulation +add wave sim:/spi_master_tb/* +add wave sim:/spi_master_tb/dut/* +run -All diff --git a/sim/spi_slave_tb.vhd b/sim/spi_slave_tb.vhd new file mode 100644 index 0000000..6c9a98c --- /dev/null +++ b/sim/spi_slave_tb.vhd @@ -0,0 +1,225 @@ +-------------------------------------------------------------------------------- +-- PROJECT: SPI MASTER AND SLAVE FOR FPGA +-------------------------------------------------------------------------------- +-- AUTHORS: Jakub Cabal +-- LICENSE: The MIT License, please read LICENSE file +-- WEBSITE: https://github.com/jakubcabal/spi-fpga +-------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; +use IEEE.MATH_REAL.ALL; + +entity SPI_SLAVE_TB is + Generic ( + CLK_FREQ : natural := 50e6; -- system clock frequency in Hz + SPI_FREQ : natural := 1e6; -- spi clock frequency in Hz + WORD_SIZE : natural := 8; -- size of transfer word in bits, must be power of two + TRANS_COUNT : natural := 1e4 -- number of test transaction + ); +end entity; + +architecture SIM of SPI_SLAVE_TB is + + constant CLK_PERIOD : time := 1 ns * integer(real(1e9)/real(CLK_FREQ)); + constant SPI_PERIOD : time := 1 ns * integer(real(1e9)/real(SPI_FREQ)); + constant RX_OFFSET : natural := 42; + constant TX_OFFSET : natural := 11; + + signal CLK : std_logic; + signal RST : std_logic; + + signal sclk : std_logic := '0'; + signal cs_n : std_logic := '1'; + signal mosi : std_logic; + signal miso : std_logic; + + signal udi : std_logic_vector(WORD_SIZE-1 downto 0); + signal udi_vld : std_logic; + signal udi_rdy : std_logic; + signal udo : std_logic_vector(WORD_SIZE-1 downto 0); + signal udo_exp : std_logic_vector(WORD_SIZE-1 downto 0); + signal udo_vld : std_logic; + + signal spi_mdi : std_logic_vector(WORD_SIZE-1 downto 0); + signal spi_mdo : std_logic_vector(WORD_SIZE-1 downto 0); + signal spi_mdo_exp : std_logic_vector(WORD_SIZE-1 downto 0); + + signal spi_model_done : std_logic := '0'; + signal udi_done : std_logic := '0'; + signal udo_done : std_logic := '0'; + signal sim_done : std_logic := '0'; + signal rand_int : integer := 0; + signal count_rx : integer; + signal count_tx : integer; + + procedure SPI_MASTER ( + constant SPI_PER : time; + signal SMM_MDI : in std_logic_vector(WORD_SIZE-1 downto 0); + signal SMM_MDO : out std_logic_vector(WORD_SIZE-1 downto 0); + signal SMM_SCLK : out std_logic; + signal SMM_CS_N : out std_logic; + signal SMM_MOSI : out std_logic; + signal SMM_MISO : in std_logic + ) is + begin + SMM_CS_N <= '0'; + for i in 0 to (WORD_SIZE-1) loop + SMM_SCLK <= '0'; + SMM_MOSI <= SMM_MDI(WORD_SIZE-1-i); + wait for SPI_PER/2; + SMM_SCLK <= '1'; + SMM_MDO(WORD_SIZE-1-i) <= SMM_MISO; + wait for SPI_PER/2; + end loop; + SMM_SCLK <= '0'; + wait for SPI_PER/2; + SMM_CS_N <= '1'; + end procedure; + +begin + + rand_int_p : process + variable seed1, seed2: positive; + variable rand : real; + begin + uniform(seed1, seed2, rand); + rand_int <= integer(rand*real(20)); + --report "Random number X: " & integer'image(rand_int); + wait for CLK_PERIOD; + if (sim_done = '1') then + wait; + end if; + end process; + + dut : entity work.SPI_SLAVE + generic map ( + WORD_SIZE => WORD_SIZE + ) + port map ( + CLK => CLK, + RST => RST, + -- SPI MASTER INTERFACE + SCLK => sclk, + CS_N => cs_n, + MOSI => mosi, + MISO => miso, + -- USER INTERFACE + DIN => udi, + DIN_VLD => udi_vld, + DIN_RDY => udi_rdy, + DOUT => udo, + DOUT_VLD => udo_vld + ); + + clk_gen_p : process + begin + CLK <= '0'; + wait for CLK_PERIOD/2; + CLK <= '1'; + wait for CLK_PERIOD/2; + if (sim_done = '1') then + wait; + end if; + end process; + + rst_gen_p : process + begin + report "======== SIMULATION START! ========"; + report "Total transactions for master to slave direction: " & integer'image(TRANS_COUNT); + report "Total transactions for slave to master direction: " & integer'image(TRANS_COUNT); + RST <= '1'; + wait for CLK_PERIOD*3; + RST <= '0'; + wait; + end process; + + -- ------------------------------------------------------------------------- + -- DUT TEST + -- ------------------------------------------------------------------------- + + spi_master_model_p : process + begin + count_tx <= 1; + cs_n <= '1'; + sclk <= '0'; + wait until RST = '0'; + wait for 33 ns; + for i in 0 to TRANS_COUNT-1 loop + spi_mdi <= std_logic_vector(to_unsigned(((i+RX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); + spi_mdo_exp <= std_logic_vector(to_unsigned(((i+TX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); + wait for SPI_PERIOD/2; -- minimum idle time between transactions + SPI_MASTER(SPI_PERIOD, spi_mdi, spi_mdo, sclk, cs_n, mosi, miso); + if (spi_mdo = spi_mdo_exp) then + if ((count_tx mod (TRANS_COUNT/10)) = 0) then + report "Transactions received from slave: " & integer'image(count_tx); + end if; + else + report "======== UNEXPECTED TRANSACTION ON MISO SIGNAL (slave to master)! ========" severity failure; + end if; + count_tx <= count_tx + 1; + wait for (rand_int/2) * SPI_PERIOD; + end loop; + spi_model_done <= '1'; + wait; + end process; + + spi_slave_udi_p : process + begin + wait until RST = '0'; + wait until rising_edge(CLK); + wait for CLK_PERIOD/2; + for i in 0 to TRANS_COUNT-1 loop + udi <= std_logic_vector(to_unsigned(((i+TX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); + udi_vld <= '1'; + if (udi_rdy = '0') then + wait until udi_rdy = '1'; + wait for CLK_PERIOD/2; + end if; + wait for CLK_PERIOD; + udi_vld <= '0'; + --wait for rand_int*CLK_PERIOD; + end loop; + udi_done <= '1'; + wait; + end process; + + spi_slave_udo_p : process + begin + count_rx <= 1; + for i in 0 to TRANS_COUNT-1 loop + udo_exp <= std_logic_vector(to_unsigned(((i+RX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); + wait until udo_vld = '1'; + if (udo = udo_exp) then + if ((count_rx mod (TRANS_COUNT/10)) = 0) then + report "Transactions received from master: " & integer'image(count_rx); + end if; + else + report "======== UNEXPECTED TRANSACTION ON DOUT SIGNAL (master to slave)! ========" severity failure; + end if; + count_rx <= count_rx + 1; + wait for CLK_PERIOD; + end loop; + udo_done <= '1'; + wait; + end process; + + -- ------------------------------------------------------------------------- + -- TEST DONE CHECK + -- ------------------------------------------------------------------------- + + test_done_p : process + variable v_test_done : std_logic; + begin + v_test_done := spi_model_done and udi_done and udo_done; + if (v_test_done = '1') then + wait for 100*CLK_PERIOD; + sim_done <= '1'; + report "======== SIMULATION SUCCESSFULLY COMPLETED! ========"; + wait; + end if; + wait for CLK_PERIOD; + end process; + +end architecture; diff --git a/sim/spi_slave_tb_ghdl_run.sh b/sim/spi_slave_tb_ghdl_run.sh new file mode 100644 index 0000000..679ed57 --- /dev/null +++ b/sim/spi_slave_tb_ghdl_run.sh @@ -0,0 +1,14 @@ +#!/bin/bash +#------------------------------------------------------------------------------- +# PROJECT: SPI MASTER AND SLAVE FOR FPGA +#------------------------------------------------------------------------------- +# AUTHORS: Jakub Cabal +# LICENSE: The MIT License, please read LICENSE file +# WEBSITE: https://github.com/jakubcabal/spi-fpga +#------------------------------------------------------------------------------- + +# Setup simulation +sh ./spi_slave_tb_ghdl_setup.sh + +# Run the simulation +ghdl -r SPI_SLAVE_TB diff --git a/sim/spi_slave_tb_ghdl_setup.sh b/sim/spi_slave_tb_ghdl_setup.sh new file mode 100644 index 0000000..d2ba989 --- /dev/null +++ b/sim/spi_slave_tb_ghdl_setup.sh @@ -0,0 +1,19 @@ +#!/bin/bash +#------------------------------------------------------------------------------- +# PROJECT: SPI MASTER AND SLAVE FOR FPGA +#------------------------------------------------------------------------------- +# AUTHORS: Jakub Cabal +# LICENSE: The MIT License, please read LICENSE file +# WEBSITE: https://github.com/jakubcabal/spi-fpga +#------------------------------------------------------------------------------- + +# Analyse sources +ghdl -a ../rtl/spi_slave.vhd +ghdl -a ./spi_slave_tb.vhd + +# Elaborate the top-level +ghdl -e SPI_SLAVE_TB + +# Run the simulation +# The following command is allocated to a separate script due to CI purposes. +#ghdl -r SPI_SLAVE_TB diff --git a/sim/spi_slave_tb_msim_run.tcl b/sim/spi_slave_tb_msim_run.tcl new file mode 100644 index 0000000..32a6692 --- /dev/null +++ b/sim/spi_slave_tb_msim_run.tcl @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------------- +# PROJECT: SPI MASTER AND SLAVE FOR FPGA +#------------------------------------------------------------------------------- +# AUTHORS: Jakub Cabal +# LICENSE: The MIT License, please read LICENSE file +# WEBSITE: https://github.com/jakubcabal/spi-fpga +#------------------------------------------------------------------------------- + +# Create work library +vlib work + +# Compile VHDL files +vcom -93 ../rtl/spi_slave.vhd +vcom -93 ./spi_slave_tb.vhd + +# Load testbench +vsim work.spi_slave_tb + +# Setup and start simulation +add wave sim:/spi_slave_tb/dut/* +run -All diff --git a/sim/spi_tb.vhd b/sim/spi_tb.vhd deleted file mode 100644 index a640307..0000000 --- a/sim/spi_tb.vhd +++ /dev/null @@ -1,187 +0,0 @@ --------------------------------------------------------------------------------- --- PROJECT: SPI MASTER AND SLAVE FOR FPGA --------------------------------------------------------------------------------- --- NAME: SPI_TB --- AUTHORS: Jakub Cabal --- LICENSE: LGPL-3.0, please read LICENSE file --- WEBSITE: https://github.com/jakubcabal/spi-fpga --------------------------------------------------------------------------------- --- COPYRIGHT NOTICE: --------------------------------------------------------------------------------- --- SPI MASTER AND SLAVE FOR FPGA --- Copyright (C) 2016 Jakub Cabal --- --- This source file is free software: you can redistribute it and/or modify --- it under the terms of the GNU Lesser General Public License as published by --- the Free Software Foundation, either version 3 of the License, or --- (at your option) any later version. --- --- This source file is distributed in the hope that it will be useful, --- but WITHOUT ANY WARRANTY; without even the implied warranty of --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --- GNU Lesser General Public License for more details. --- --- You should have received a copy of the GNU Lesser General Public License --- along with this program. If not, see . --------------------------------------------------------------------------------- - -library IEEE; -use IEEE.STD_LOGIC_1164.ALL; -use IEEE.NUMERIC_STD.ALL; -use IEEE.MATH_REAL.ALL; - -entity SPI_TB is -end SPI_TB; - -architecture SIM of SPI_TB is - - constant CLK_PERIOD : time := 20 ns; - constant SLAVE_COUNT : integer := 4; - - signal CLK : std_logic := '0'; - signal RST : std_logic := '1'; - - signal sclk : std_logic; - signal cs_n : std_logic_vector(SLAVE_COUNT-1 downto 0); - signal miso : std_logic; - signal mosi : std_logic; - - signal m_addr : std_logic_vector(integer(ceil(log2(real(SLAVE_COUNT))))-1 downto 0); - signal m_din : std_logic_vector(7 downto 0); - signal m_din_last : std_logic; - signal m_din_vld : std_logic; - signal m_ready : std_logic; - signal m_dout : std_logic_vector(7 downto 0); - signal m_dout_vld : std_logic; - - signal s_din : std_logic_vector(7 downto 0); - signal s_din_vld : std_logic; - signal s_ready : std_logic; - signal s_dout : std_logic_vector(7 downto 0); - signal s_dout_vld : std_logic; - -begin - - master_i : entity work.SPI_MASTER - generic map( - CLK_FREQ => 50e6, - SCLK_FREQ => 5e6, - SLAVE_COUNT => SLAVE_COUNT - ) - port map ( - CLK => CLK, - RST => RST, - -- SPI MASTER INTERFACE - SCLK => sclk, - CS_N => cs_n, - MOSI => mosi, - MISO => miso, - -- USER INTERFACE - ADDR => m_addr, - DIN => m_din, - DIN_LAST => m_din_last, - DIN_VLD => m_din_vld, - READY => m_ready, - DOUT => m_dout, - DOUT_VLD => m_dout_vld - ); - - slave_i : entity work.SPI_SLAVE - port map ( - CLK => CLK, - RST => RST, - -- SPI MASTER INTERFACE - SCLK => sclk, - CS_N => cs_n(0), - MOSI => mosi, - MISO => miso, - -- USER INTERFACE - DIN => s_din, - DIN_VLD => s_din_vld, - READY => s_ready, - DOUT => s_dout, - DOUT_VLD => s_dout_vld - ); - - clk_process : process - begin - CLK <= '1'; - wait for CLK_PERIOD/2; - CLK <= '0'; - wait for CLK_PERIOD/2; - end process; - - rst_process : process - begin - RST <= '1'; - wait for 40 ns; - RST <= '0'; - wait; - end process; - - master_test_process : process - begin - m_addr <= (others => '0'); - m_din <= (others => 'Z'); - m_din_vld <= '0'; - m_din_last <= '0'; - - wait until RST = '0'; - wait for 30 ns; - wait until rising_edge(CLK); - - m_din <= X"12"; - m_din_vld <= '1'; - m_din_last <= '0'; - wait until m_ready = '0'; - - m_din <= (others => '0'); - m_din_vld <= '0'; - - wait until m_ready = '1'; - wait for 190 ns; - wait until rising_edge(CLK); - - m_din <= X"F4"; - m_din_vld <= '1'; - m_din_last <= '1'; - wait until m_ready = '0'; - - m_din <= X"47"; - m_din_vld <= '1'; - m_din_last <= '1'; - wait until m_ready = '0'; - - m_din <= (others => '0'); - m_din_vld <= '0'; - - wait; - end process; - - slave_test_process : process - begin - s_din <= (others => 'Z'); - s_din_vld <= '0'; - - wait until RST = '0'; - wait until rising_edge(CLK); - - s_din <= X"A1"; - s_din_vld <= '1'; - wait until m_ready = '0'; - - s_din <= X"B2"; - s_din_vld <= '1'; - wait until m_ready = '0'; - - s_din <= X"E8"; - s_din_vld <= '1'; - wait until m_ready = '0'; - - s_din <= (others => '0'); - s_din_vld <= '0'; - - wait; - end process; - -end SIM;