├── .github └── workflows │ └── main.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs └── spirit_level_example.gif ├── examples ├── common │ ├── btn_debounce.vhd │ ├── clk_en_gen.vhd │ ├── rst_sync.vhd │ └── sseg_driver.vhd ├── loopback │ ├── quartus │ │ ├── spi_loopback_ep4ce6sb.qpf │ │ ├── spi_loopback_ep4ce6sb.qsf │ │ └── spi_loopback_ep4ce6sb.sdc │ └── spi_loopback.vhd └── spirit_level │ ├── quartus │ ├── spirit_level_cyc1000.qpf │ ├── spirit_level_cyc1000.qsf │ └── spirit_level_cyc1000.sdc │ └── spirit_level_cyc1000.vhd ├── rtl ├── spi_master.vhd └── spi_slave.vhd └── sim ├── spi_master_tb.vhd ├── spi_master_tb_ghdl_run.sh ├── spi_master_tb_ghdl_setup.sh ├── spi_master_tb_msim_run.tcl ├── spi_slave_tb.vhd ├── spi_slave_tb_ghdl_run.sh ├── spi_slave_tb_ghdl_setup.sh └── spi_slave_tb_msim_run.tcl /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: SPI FPGA CI 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the master branch 8 | push: 9 | branches: [ master, dev ] 10 | pull_request: 11 | branches: [ master, dev ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | spi_master_sim: 19 | name: SPI Master Simulation 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - name: Checkout repository 27 | uses: actions/checkout@v2 28 | 29 | - name: Setup GHDL 30 | # You may pin to the exact commit or the version. 31 | # uses: ghdl/setup-ghdl-ci@233774d8c0c5021af4c3686ea405013cb1494fd1 32 | uses: ghdl/setup-ghdl-ci@nightly 33 | with: # Select GHDL backend (mcode, llvm or gcc) 34 | backend: llvm # optional, default is mcode 35 | 36 | - name: GHDL version check 37 | run: ghdl --version 38 | 39 | - name: Run Test 1 (CLK_FREQ=50e6, SPI_FREQ=1e6, WORD_SIZE=8) 40 | run: | 41 | cd ./sim/ 42 | sh ./spi_master_tb_ghdl_setup.sh 43 | ghdl -r SPI_MASTER_TB -gCLK_FREQ=50e6 -gSPI_FREQ=1e6 -gWORD_SIZE=8 -gTRANS_COUNT=2e4 44 | 45 | - name: Run Test 2 (CLK_FREQ=12e6, SPI_FREQ=2e6, WORD_SIZE=8) 46 | run: | 47 | cd ./sim/ 48 | sh ./spi_master_tb_ghdl_setup.sh 49 | ghdl -r SPI_MASTER_TB -gCLK_FREQ=12e6 -gSPI_FREQ=2e6 -gWORD_SIZE=8 -gTRANS_COUNT=2e4 50 | 51 | - name: Run Test 3 (CLK_FREQ=100e6, SPI_FREQ=5e6, WORD_SIZE=16) 52 | run: | 53 | cd ./sim/ 54 | sh ./spi_master_tb_ghdl_setup.sh 55 | ghdl -r SPI_MASTER_TB -gCLK_FREQ=100e6 -gSPI_FREQ=5e6 -gWORD_SIZE=16 -gTRANS_COUNT=2e4 56 | 57 | - name: Run Test 4 (CLK_FREQ=25e6, SPI_FREQ=3e6, WORD_SIZE=16) 58 | run: | 59 | cd ./sim/ 60 | sh ./spi_master_tb_ghdl_setup.sh 61 | ghdl -r SPI_MASTER_TB -gCLK_FREQ=25e6 -gSPI_FREQ=3e6 -gWORD_SIZE=16 -gTRANS_COUNT=2e4 62 | 63 | spi_slave_sim: 64 | name: SPI Slave Simulation 65 | # The type of runner that the job will run on 66 | runs-on: ubuntu-latest 67 | 68 | # Steps represent a sequence of tasks that will be executed as part of the job 69 | steps: 70 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 71 | - name: Checkout repository 72 | uses: actions/checkout@v2 73 | 74 | - name: Setup GHDL 75 | # You may pin to the exact commit or the version. 76 | # uses: ghdl/setup-ghdl-ci@233774d8c0c5021af4c3686ea405013cb1494fd1 77 | uses: ghdl/setup-ghdl-ci@nightly 78 | with: # Select GHDL backend (mcode, llvm or gcc) 79 | backend: llvm # optional, default is mcode 80 | 81 | - name: GHDL version check 82 | run: ghdl --version 83 | 84 | - name: Run Test 1 (CLK_FREQ=50e6, SPI_FREQ=1e6, WORD_SIZE=8) 85 | run: | 86 | cd ./sim/ 87 | sh ./spi_slave_tb_ghdl_setup.sh 88 | ghdl -r SPI_SLAVE_TB -gCLK_FREQ=50e6 -gSPI_FREQ=1e6 -gWORD_SIZE=8 -gTRANS_COUNT=2e4 89 | 90 | - name: Run Test 2 (CLK_FREQ=12e6, SPI_FREQ=2e6, WORD_SIZE=8) 91 | run: | 92 | cd ./sim/ 93 | sh ./spi_slave_tb_ghdl_setup.sh 94 | ghdl -r SPI_SLAVE_TB -gCLK_FREQ=12e6 -gSPI_FREQ=2e6 -gWORD_SIZE=8 -gTRANS_COUNT=2e4 95 | 96 | - name: Run Test 3 (CLK_FREQ=100e6, SPI_FREQ=5e6, WORD_SIZE=16) 97 | run: | 98 | cd ./sim/ 99 | sh ./spi_slave_tb_ghdl_setup.sh 100 | ghdl -r SPI_SLAVE_TB -gCLK_FREQ=100e6 -gSPI_FREQ=5e6 -gWORD_SIZE=16 -gTRANS_COUNT=2e4 101 | 102 | - name: Run Test 4 (CLK_FREQ=25e6, SPI_FREQ=3e6, WORD_SIZE=16) 103 | run: | 104 | cd ./sim/ 105 | sh ./spi_slave_tb_ghdl_setup.sh 106 | ghdl -r SPI_SLAVE_TB -gCLK_FREQ=25e6 -gSPI_FREQ=3e6 -gWORD_SIZE=16 -gTRANS_COUNT=2e4 107 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog of SPI master and SPI slave for FPGA 2 | 3 | **Version 1.1 - released on 24 April 2021** 4 | - Changed license to The MIT License. 5 | - Added better simulations and enabled GitHub CI. 6 | - Added Spirit Level example design for CYC1000 board. 7 | - Added sync FFs to SPI slave for elimination metastability. 8 | - Added WORD_SIZE generic. 9 | - Many minor changes, fixes and optimizations. 10 | 11 | **Version 1.0 - released on 28 September 2017** 12 | - First non-beta release. 13 | - Added new version of master module with many optimizations. 14 | - Added DIN_LAST input to master module, for CS_N signal control. 15 | - Added simulation tcl script for ModelSim. 16 | - Updated simulation testbench. 17 | - Updated example design. 18 | - Optimized and cleaned slave module. 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2021 Jakub Cabal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SPI MASTER AND SLAVE FOR FPGA 2 | 3 | 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. 4 | 5 | **The SPI master and SPI slave controllers support only SPI mode 0 (CPOL=0, CPHA=0)!** 6 | 7 | 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. 8 | 9 | ## SPI master 10 | 11 | ### Generics: 12 | 13 | ```vhdl 14 | CLK_FREQ : natural := 50e6; -- set system clock frequency in Hz 15 | SCLK_FREQ : natural := 5e6; -- set SPI clock frequency in Hz (condition: SCLK_FREQ <= CLK_FREQ/10) 16 | WORD_SIZE : natural := 8; -- size of transfer word in bits, must be power of two 17 | SLAVE_COUNT : natural := 1 -- count of SPI slaves 18 | ``` 19 | 20 | ### Ports: 21 | 22 | ```vhdl 23 | CLK : in std_logic; -- system clock 24 | RST : in std_logic; -- high active synchronous reset 25 | -- SPI MASTER INTERFACE 26 | SCLK : out std_logic; -- SPI clock 27 | CS_N : out std_logic_vector(SLAVE_COUNT-1 downto 0); -- SPI chip select, active in low 28 | MOSI : out std_logic; -- SPI serial data from master to slave 29 | MISO : in std_logic; -- SPI serial data from slave to master 30 | -- INPUT USER INTERFACE 31 | DIN : in std_logic_vector(WORD_SIZE-1 downto 0); -- data for transmission to SPI slave 32 | DIN_ADDR : in std_logic_vector(natural(ceil(log2(real(SLAVE_COUNT))))-1 downto 0); -- SPI slave address 33 | DIN_LAST : in std_logic; -- when DIN_LAST = 1, last data word, after transmit will be asserted CS_N 34 | DIN_VLD : in std_logic; -- when DIN_VLD = 1, data for transmission are valid 35 | DIN_RDY : out std_logic; -- when DIN_RDY = 1, SPI master is ready to accept valid data for transmission 36 | -- OUTPUT USER INTERFACE 37 | DOUT : out std_logic_vector(WORD_SIZE-1 downto 0); -- received data from SPI slave 38 | DOUT_VLD : out std_logic -- when DOUT_VLD = 1, received data are valid 39 | ``` 40 | 41 | ### Resource usage: 42 | 43 | LE | FF | M9K | Fmax 44 | ---|----|-----|----------- 45 | 34 | 23 | 0 | 330.1 MHz 46 | 47 | *Implementation was performed using Quartus Prime Lite Edition 20.1.0 for Intel Cyclone 10 FPGA (10CL025YU256C8G) with default generics.* 48 | 49 | ### Simulation: 50 | 51 | A simulation is prepared in the [```sim/```](sim/) folder. You can use the prepared TCL script to run simulation in ModelSim. 52 | ``` 53 | vsim -do spi_master_tb_msim_run.tcl 54 | ``` 55 | 56 | 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: 57 | ``` 58 | ./spi_master_tb_ghdl_run.sh 59 | ``` 60 | 61 | ## SPI slave 62 | 63 | ### Generics: 64 | 65 | ```vhdl 66 | WORD_SIZE : natural := 8; -- size of transfer word in bits, must be power of two 67 | ``` 68 | 69 | ### Ports: 70 | 71 | ```vhdl 72 | CLK : in std_logic; -- system clock 73 | RST : in std_logic; -- high active synchronous reset 74 | -- SPI SLAVE INTERFACE 75 | SCLK : in std_logic; -- SPI clock 76 | CS_N : in std_logic; -- SPI chip select, active in low 77 | MOSI : in std_logic; -- SPI serial data from master to slave 78 | MISO : out std_logic; -- SPI serial data from slave to master 79 | -- USER INTERFACE 80 | DIN : in std_logic_vector(WORD_SIZE-1 downto 0); -- data for transmission to SPI master 81 | DIN_VLD : in std_logic; -- when DIN_VLD = 1, data for transmission are valid 82 | DIN_RDY : out std_logic; -- when DIN_RDY = 1, SPI slave is ready to accept valid data for transmission 83 | DOUT : out std_logic_vector(WORD_SIZE-1 downto 0); -- received data from SPI master 84 | DOUT_VLD : out std_logic -- when DOUT_VLD = 1, received data are valid 85 | ``` 86 | 87 | ### Resource usage: 88 | 89 | LE | FF | M9K | Fmax 90 | ---|----|-----|----------- 91 | 29 | 21 | 0 | 324.5 MHz 92 | 93 | *Implementation was performed using Quartus Prime Lite Edition 20.1.0 for Intel Cyclone 10 FPGA (10CL025YU256C8G) with default generics.* 94 | 95 | ### Simulation: 96 | 97 | A simulation is prepared in the [```sim/```](sim/) folder. You can use the prepared TCL script to run simulation in ModelSim. 98 | ``` 99 | vsim -do spi_slave_tb_msim_run.tcl 100 | ``` 101 | 102 | 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: 103 | ``` 104 | ./spi_slave_tb_ghdl_run.sh 105 | ``` 106 | 107 | ## Examples: 108 | 109 | ### Spirit Level: 110 | 111 | 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). 112 | 113 | [![Spirit Level example video](docs/spirit_level_example.gif)](https://youtu.be/EI1BEAkZu5Q) 114 | 115 | ### SPI loopback: 116 | 117 | 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). 118 | 119 | [![Video of SPI loopback example design](https://img.youtube.com/vi/-TbtB6Sm2Xk/0.jpg)](https://youtu.be/-TbtB6Sm2Xk) 120 | 121 | Display description (from right on board in video): 122 | 123 | ``` 124 | Digit0 = value on SPI slave input 125 | Digit1 = value on SPI slave output 126 | Digit2 = value on SPI master input 127 | Digit3 = value on SPI master output 128 | ``` 129 | 130 | Buttons description (from right on board in video): 131 | 132 | ``` 133 | BTN_ACTION (in mode0) = setup value on SPI slave input 134 | BTN_ACTION (in mode1) = write (set valid) of SPI slave input value 135 | BTN_ACTION (in mode2) = setup value on SPI master input 136 | BTN_ACTION (in mode3) = write (set valid) of SPI slave input value and start transfer between SPI master and SPI slave 137 | BTN_MODE = switch between modes (mode0 = light decimal point on digit0,...) 138 | BTN_RESET = reset FPGA design 139 | ``` 140 | 141 | ## License: 142 | 143 | This whole repository (include SPI master and SPI slave controllers) is available under the MIT license. Please read [LICENSE file](LICENSE). 144 | -------------------------------------------------------------------------------- /docs/spirit_level_example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakubcabal/spi-fpga/d8240ff3f59fdeeadd87692333aeafb69b0b88a1/docs/spirit_level_example.gif -------------------------------------------------------------------------------- /examples/common/btn_debounce.vhd: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | -------------------------------------------------------------------------------- 4 | -- AUTHORS: Jakub Cabal 5 | -- LICENSE: The MIT License, please read LICENSE file 6 | -- WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | -------------------------------------------------------------------------------- 8 | 9 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | 13 | entity BTN_DEBOUNCE is 14 | Generic ( 15 | CNT_WIDTH : natural := 2 -- width of debounce counter 16 | ); 17 | Port ( 18 | CLK : in std_logic; -- system clock 19 | ASYNC_RST : in std_logic; -- asynchrounous reset 20 | SAMPLE_EN : in std_logic; -- sample clock enable 21 | BTN_RAW : in std_logic; -- button raw signal 22 | BTN_DEB : out std_logic; -- button debounce signal 23 | BTN_DEB_RE : out std_logic -- rising edge of debounced signal 24 | ); 25 | end entity; 26 | 27 | architecture RTL of BTN_DEBOUNCE is 28 | 29 | signal btn_raw_sync_reg1 : std_logic; 30 | signal btn_raw_sync_reg2 : std_logic; 31 | signal btn_raw_sample_reg : std_logic; 32 | signal btn_raw_diff : std_logic; 33 | signal deb_cnt : unsigned(CNT_WIDTH-1 downto 0); 34 | signal deb_cnt_max : std_logic; 35 | signal btn_deb_reg : std_logic; 36 | signal btn_deb_re_reg : std_logic; 37 | 38 | begin 39 | 40 | -- ------------------------------------------------------------------------- 41 | -- BUTTON RAW SIGNAL SYNCHRONIZATION REGISTERS 42 | -- ------------------------------------------------------------------------- 43 | 44 | btn_raw_sync_reg_p : process (CLK) 45 | begin 46 | if (rising_edge(CLK)) then 47 | btn_raw_sync_reg1 <= BTN_RAW; 48 | btn_raw_sync_reg2 <= btn_raw_sync_reg1; 49 | end if; 50 | end process; 51 | 52 | -- ------------------------------------------------------------------------- 53 | -- BUTTON RAW SIGNAL SAMPLE REGISTERS 54 | -- ------------------------------------------------------------------------- 55 | 56 | btn_raw_sample_reg_p : process (CLK) 57 | begin 58 | if (rising_edge(CLK)) then 59 | if (SAMPLE_EN = '1') then 60 | btn_raw_sample_reg <= btn_raw_sync_reg2; 61 | end if; 62 | end if; 63 | end process; 64 | 65 | btn_raw_diff <= btn_raw_sample_reg xor btn_raw_sync_reg2; 66 | 67 | -- ------------------------------------------------------------------------- 68 | -- DEBOUNCE COUNTER 69 | -- ------------------------------------------------------------------------- 70 | 71 | deb_cnt_p : process (CLK, ASYNC_RST) 72 | begin 73 | if (ASYNC_RST = '1') then 74 | deb_cnt <= (others => '0'); 75 | elsif (rising_edge(CLK)) then 76 | if (SAMPLE_EN = '1') then 77 | if (btn_raw_diff = '1') then 78 | deb_cnt <= (others => '0'); 79 | else 80 | deb_cnt <= deb_cnt + 1; 81 | end if; 82 | end if; 83 | end if; 84 | end process; 85 | 86 | deb_cnt_max <= '1' when (deb_cnt = (2**CNT_WIDTH)-1) else '0'; 87 | 88 | -- ------------------------------------------------------------------------- 89 | -- BUTTON DEBOUNCE SIGNAL REGISTER 90 | -- ------------------------------------------------------------------------- 91 | 92 | btn_deb_reg_p : process (CLK) 93 | begin 94 | if (rising_edge(CLK)) then 95 | if (deb_cnt_max = '1') then 96 | btn_deb_reg <= btn_raw_sample_reg; 97 | end if; 98 | end if; 99 | end process; 100 | 101 | BTN_DEB <= btn_deb_reg; 102 | 103 | -- ------------------------------------------------------------------------- 104 | -- RISING EDGE DETECTOR OF BUTTON DEBOUNCE SIGNAL 105 | -- ------------------------------------------------------------------------- 106 | 107 | btn_deb_re_reg_p : process (CLK, ASYNC_RST) 108 | begin 109 | if (ASYNC_RST = '1') then 110 | btn_deb_re_reg <= '0'; 111 | elsif (rising_edge(CLK)) then 112 | btn_deb_re_reg <= deb_cnt_max and btn_raw_sample_reg and not btn_deb_reg; 113 | end if; 114 | end process; 115 | 116 | BTN_DEB_RE <= btn_deb_re_reg; 117 | 118 | end architecture; 119 | -------------------------------------------------------------------------------- /examples/common/clk_en_gen.vhd: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | -------------------------------------------------------------------------------- 4 | -- AUTHORS: Jakub Cabal 5 | -- LICENSE: The MIT License, please read LICENSE file 6 | -- WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | -------------------------------------------------------------------------------- 8 | 9 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | use IEEE.MATH_REAL.ALL; 13 | 14 | entity CLK_EN_GEN is 15 | Generic ( 16 | CLK_FREQ : natural := 50e6 -- set system clock frequency in Hz 17 | ); 18 | Port ( 19 | CLK : in std_logic; -- system clock 20 | ASYNC_RST : in std_logic; -- asynchrounous reset 21 | CLK_EN_1K : out std_logic -- clock enable 1 KHz output 22 | ); 23 | end CLK_EN_GEN; 24 | 25 | architecture RTL of CLK_EN_GEN is 26 | 27 | constant CLK_EN_1K_DIV : integer := CLK_FREQ/1e3; 28 | 29 | signal clk_en_1k_cnt : integer range 0 to CLK_EN_1K_DIV-1; 30 | signal clk_en_1k_comb : std_logic; 31 | signal clk_en_1k_reg : std_logic; 32 | 33 | begin 34 | 35 | -- ------------------------------------------------------------------------- 36 | -- COUNTER OF CLOCK ENABLE (~1KHz) 37 | -- ------------------------------------------------------------------------- 38 | 39 | clk_en_1k_cnt_p : process (CLK, ASYNC_RST) 40 | begin 41 | if (ASYNC_RST = '1') then 42 | clk_en_1k_cnt <= 0; 43 | elsif (rising_edge(CLK)) then 44 | if (clk_en_1k_cnt = CLK_EN_1K_DIV-1) then 45 | clk_en_1k_cnt <= 0; 46 | else 47 | clk_en_1k_cnt <= clk_en_1k_cnt + 1; 48 | end if; 49 | end if; 50 | end process; 51 | 52 | -- ------------------------------------------------------------------------- 53 | -- GENERATOR OF CLOCK ENABLE (~1KHz) 54 | -- ------------------------------------------------------------------------- 55 | 56 | clk_en_1k_comb <= '1' when (clk_en_1k_cnt = CLK_EN_1K_DIV-1) else '0'; 57 | 58 | clk_en_1k_reg_p : process (CLK, ASYNC_RST) 59 | begin 60 | if (ASYNC_RST = '1') then 61 | clk_en_1k_reg <= '0'; 62 | elsif (rising_edge(CLK)) then 63 | clk_en_1k_reg <= clk_en_1k_comb; 64 | end if; 65 | end process; 66 | 67 | CLK_EN_1K <= clk_en_1k_reg; 68 | 69 | end RTL; 70 | -------------------------------------------------------------------------------- /examples/common/rst_sync.vhd: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | -------------------------------------------------------------------------------- 4 | -- AUTHORS: Jakub Cabal 5 | -- LICENSE: The MIT License, please read LICENSE file 6 | -- WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | -------------------------------------------------------------------------------- 8 | 9 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | 13 | entity RST_SYNC is 14 | Port ( 15 | CLK : in std_logic; 16 | ASYNC_RST : in std_logic; 17 | SYNCED_RST : out std_logic 18 | ); 19 | end entity; 20 | 21 | architecture RTL of RST_SYNC is 22 | 23 | attribute ALTERA_ATTRIBUTE : string; 24 | attribute PRESERVE : boolean; 25 | 26 | signal meta_reg : std_logic; 27 | signal reset_reg : std_logic; 28 | 29 | attribute ALTERA_ATTRIBUTE of RTL : architecture is "-name SDC_STATEMENT ""set_false_path -to [get_registers {*RST_SYNC:*|meta_reg}] """; 30 | attribute ALTERA_ATTRIBUTE of meta_reg : signal is "-name SYNCHRONIZER_IDENTIFICATION ""FORCED IF ASYNCHRONOUS"""; 31 | attribute ALTERA_ATTRIBUTE of reset_reg : signal is "-name SYNCHRONIZER_IDENTIFICATION ""FORCED IF ASYNCHRONOUS"""; 32 | attribute PRESERVE of meta_reg : signal is TRUE; 33 | attribute PRESERVE of reset_reg : signal is TRUE; 34 | 35 | begin 36 | 37 | process (CLK, ASYNC_RST) 38 | begin 39 | if (ASYNC_RST = '1') then 40 | meta_reg <= '1'; 41 | reset_reg <= '1'; 42 | elsif (rising_edge(CLK)) then 43 | meta_reg <= '0'; 44 | reset_reg <= meta_reg; 45 | end if; 46 | end process; 47 | 48 | SYNCED_RST <= reset_reg; 49 | 50 | end architecture; 51 | -------------------------------------------------------------------------------- /examples/common/sseg_driver.vhd: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | -------------------------------------------------------------------------------- 4 | -- AUTHORS: Jakub Cabal 5 | -- LICENSE: The MIT License, please read LICENSE file 6 | -- WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | -------------------------------------------------------------------------------- 8 | 9 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | 13 | entity SSEG_DRIVER is 14 | Port ( 15 | CLK : in std_logic; -- system clock 16 | CLK_EN_1K : in std_logic; -- clock enable 1 KHz 17 | ASYNC_RST : in std_logic; -- asynchrounous reset 18 | -- USER INTERFACE 19 | DATA0 : in std_logic_vector(3 downto 0); -- BCD or HEX(4b) 20 | DATA1 : in std_logic_vector(3 downto 0); -- BCD or HEX(4b) 21 | DATA2 : in std_logic_vector(3 downto 0); -- BCD or HEX(4b) 22 | DATA3 : in std_logic_vector(3 downto 0); -- BCD or HEX(4b) 23 | DOTS : in std_logic_vector(3 downto 0); -- DP on/off 24 | -- SSEG INTERFACE 25 | SSEG : out std_logic_vector(7 downto 0); -- "dot" & "gfedcba" 26 | SSEG_AN : out std_logic_vector(3 downto 0) -- sseg anodes 27 | ); 28 | end SSEG_DRIVER; 29 | 30 | architecture RTL of SSEG_DRIVER is 31 | 32 | signal sseg_anode_cnt : unsigned(1 downto 0); 33 | signal sseg_anode : std_logic_vector(3 downto 0); 34 | signal sseg_anode_reg : std_logic_vector(3 downto 0); 35 | signal sseg_bin : std_logic_vector(3 downto 0); 36 | signal sseg_code : std_logic_vector(6 downto 0); 37 | signal sseg_dp : std_logic; 38 | signal sseg_REG : std_logic_vector(7 downto 0); 39 | 40 | begin 41 | 42 | -- ------------------------------------------------------------------------- 43 | -- SSEG ANODE MUXING 44 | -- ------------------------------------------------------------------------- 45 | 46 | sseg_an_cnt_p : process (CLK, ASYNC_RST) 47 | begin 48 | if (ASYNC_RST = '1') then 49 | sseg_anode_cnt <= (others => '0'); 50 | elsif (rising_edge(CLK)) then 51 | if (CLK_EN_1K = '1') then 52 | sseg_anode_cnt <= sseg_anode_cnt + 1; 53 | end if; 54 | end if; 55 | end process; 56 | 57 | process(sseg_anode_cnt, DATA0, DATA1, DATA2, DATA3, DOTS) 58 | begin 59 | case sseg_anode_cnt is 60 | when "00" => 61 | sseg_bin <= DATA0; 62 | sseg_dp <= not DOTS(0); 63 | sseg_anode <= "1110"; 64 | when "01" => 65 | sseg_bin <= DATA1; 66 | sseg_dp <= not DOTS(1); 67 | sseg_anode <= "1101"; 68 | when "10" => 69 | sseg_bin <= DATA2; 70 | sseg_dp <= not DOTS(2); 71 | sseg_anode <= "1011"; 72 | when "11" => 73 | sseg_bin <= DATA3; 74 | sseg_dp <= not DOTS(3); 75 | sseg_anode <= "0111"; 76 | when others => 77 | sseg_bin <= DATA0; 78 | sseg_dp <= not DOTS(0); 79 | sseg_anode <= "1110"; 80 | end case; 81 | end process; 82 | 83 | -- ------------------------------------------------------------------------- 84 | -- SSEG ANODE REGISTER 85 | -- ------------------------------------------------------------------------- 86 | 87 | sseg_anode_reg_p : process (CLK, ASYNC_RST) 88 | begin 89 | if (ASYNC_RST = '1') then 90 | sseg_anode_reg <= (others => '1'); 91 | elsif (rising_edge(CLK)) then 92 | sseg_anode_reg <= sseg_anode; 93 | end if; 94 | end process; 95 | 96 | SSEG_AN <= sseg_anode_reg; 97 | 98 | -- ------------------------------------------------------------------------- 99 | -- SSEG DECODER 100 | -- ------------------------------------------------------------------------- 101 | 102 | with sseg_bin select 103 | sseg_code <= "1000000" when "0000", -- 0 104 | "1111001" when "0001", -- 1 105 | "0100100" when "0010", -- 2 106 | "0110000" when "0011", -- 3 107 | "0011001" when "0100", -- 4 108 | "0010010" when "0101", -- 5 109 | "0000010" when "0110", -- 6 110 | "1111000" when "0111", -- 7 111 | "0000000" when "1000", -- 8 112 | "0010000" when "1001", -- 9 113 | "0001000" when "1010", -- A 114 | "0000011" when "1011", -- B 115 | "1000110" when "1100", -- C 116 | "0100001" when "1101", -- D 117 | "0000110" when "1110", -- E 118 | "0001110" when "1111", -- F 119 | "1111111" when others; -- nic 120 | -- "gfedcba" 121 | 122 | -- ------------------------------------------------------------------------- 123 | -- SSEG REGISTER 124 | -- ------------------------------------------------------------------------- 125 | 126 | sseg_reg_p : process (CLK, ASYNC_RST) 127 | begin 128 | if (ASYNC_RST = '1') then 129 | sseg_reg <= (others => '1'); 130 | elsif (rising_edge(CLK)) then 131 | sseg_reg <= sseg_dp & sseg_code; 132 | end if; 133 | end process; 134 | 135 | SSEG <= sseg_reg; 136 | 137 | end RTL; 138 | -------------------------------------------------------------------------------- /examples/loopback/quartus/spi_loopback_ep4ce6sb.qpf: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | #------------------------------------------------------------------------------- 4 | # AUTHORS: Jakub Cabal 5 | # LICENSE: The MIT License, please read LICENSE file 6 | # WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | #------------------------------------------------------------------------------- 8 | 9 | PROJECT_REVISION = "spi_loopback_ep4ce6sb" 10 | -------------------------------------------------------------------------------- /examples/loopback/quartus/spi_loopback_ep4ce6sb.qsf: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | #------------------------------------------------------------------------------- 4 | # AUTHORS: Jakub Cabal 5 | # LICENSE: The MIT License, please read LICENSE file 6 | # WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | #------------------------------------------------------------------------------- 8 | 9 | # GLOBAL CONSTRAINTS FOR EP4CE6 STARTER BOARD 10 | 11 | set_global_assignment -name FAMILY "Cyclone IV E" 12 | set_global_assignment -name DEVICE EP4CE6E22C8 13 | set_global_assignment -name TOP_LEVEL_ENTITY SPI_LOOPBACK 14 | 15 | # PROJECT VHDL FILES 16 | set_global_assignment -name VHDL_FILE ../../../rtl/spi_slave.vhd 17 | set_global_assignment -name VHDL_FILE ../../../rtl/spi_master.vhd 18 | set_global_assignment -name VHDL_FILE ../../common/rst_sync.vhd 19 | set_global_assignment -name VHDL_FILE ../../common/clk_en_gen.vhd 20 | set_global_assignment -name VHDL_FILE ../../common/sseg_driver.vhd 21 | set_global_assignment -name VHDL_FILE ../../common/btn_debounce.vhd 22 | set_global_assignment -name VHDL_FILE ../spi_loopback.vhd 23 | 24 | # FPGA PINS ASSIGNMENT 25 | 26 | set_location_assignment PIN_91 -to CLK 27 | set_location_assignment PIN_25 -to BTN_RST 28 | 29 | set_location_assignment PIN_85 -to M_SCLK 30 | set_location_assignment PIN_86 -to M_CS_N 31 | set_location_assignment PIN_87 -to M_MOSI 32 | set_location_assignment PIN_99 -to M_MISO 33 | 34 | set_location_assignment PIN_111 -to S_SCLK 35 | set_location_assignment PIN_110 -to S_CS_N 36 | set_location_assignment PIN_98 -to S_MOSI 37 | set_location_assignment PIN_100 -to S_MISO 38 | 39 | set_location_assignment PIN_24 -to BTN_MENU_MODE 40 | set_location_assignment PIN_23 -to BTN_MENU_ACTION 41 | 42 | set_location_assignment PIN_7 -to SSEG[0] 43 | set_location_assignment PIN_3 -to SSEG[1] 44 | set_location_assignment PIN_2 -to SSEG[2] 45 | set_location_assignment PIN_1 -to SSEG[3] 46 | set_location_assignment PIN_144 -to SSEG[4] 47 | set_location_assignment PIN_143 -to SSEG[5] 48 | set_location_assignment PIN_142 -to SSEG[6] 49 | set_location_assignment PIN_141 -to SSEG[7] 50 | 51 | set_location_assignment PIN_137 -to SSEG_AN[0] 52 | set_location_assignment PIN_138 -to SSEG_AN[1] 53 | set_location_assignment PIN_10 -to SSEG_AN[2] 54 | set_location_assignment PIN_11 -to SSEG_AN[3] 55 | -------------------------------------------------------------------------------- /examples/loopback/quartus/spi_loopback_ep4ce6sb.sdc: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | #------------------------------------------------------------------------------- 4 | # AUTHORS: Jakub Cabal 5 | # LICENSE: The MIT License, please read LICENSE file 6 | # WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | #------------------------------------------------------------------------------- 8 | 9 | create_clock -name CLK50 -period 20.000 [get_ports {CLK}] 10 | -------------------------------------------------------------------------------- /examples/loopback/spi_loopback.vhd: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | -------------------------------------------------------------------------------- 4 | -- AUTHORS: Jakub Cabal 5 | -- LICENSE: The MIT License, please read LICENSE file 6 | -- WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | -------------------------------------------------------------------------------- 8 | 9 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | 13 | entity SPI_LOOPBACK is 14 | Port ( 15 | CLK : in std_logic; 16 | BTN_RST : in std_logic; 17 | -- SPI MASTER INTERFACE 18 | M_SCLK : out std_logic; 19 | M_CS_N : out std_logic; 20 | M_MOSI : out std_logic; 21 | M_MISO : in std_logic; 22 | -- SPI SLAVE INTERFACE 23 | S_SCLK : in std_logic; 24 | S_CS_N : in std_logic; 25 | S_MOSI : in std_logic; 26 | S_MISO : out std_logic; 27 | -- USER INTERFACE 28 | BTN_MENU_MODE : in std_logic; 29 | BTN_MENU_ACTION : in std_logic; 30 | SSEG : out std_logic_vector(7 downto 0); 31 | SSEG_AN : out std_logic_vector(3 downto 0) 32 | ); 33 | end entity; 34 | 35 | architecture RTL of SPI_LOOPBACK is 36 | 37 | signal reset : std_logic; 38 | signal clk_en_1k : std_logic; 39 | 40 | signal menu_mode_en : std_logic; 41 | signal menu_action_en : std_logic; 42 | 43 | signal menu_cnt : unsigned(1 downto 0); 44 | signal menu_mode_led : std_logic_vector(3 downto 0); 45 | signal m_cnt : unsigned(3 downto 0); 46 | signal s_cnt : unsigned(3 downto 0); 47 | signal m_cnt_en : std_logic; 48 | signal s_cnt_en : std_logic; 49 | 50 | signal m_din : std_logic_vector(7 downto 0); 51 | signal s_din : std_logic_vector(7 downto 0); 52 | signal m_din_vld : std_logic; 53 | signal s_din_vld : std_logic; 54 | signal m_dout : std_logic_vector(7 downto 0); 55 | signal s_dout : std_logic_vector(7 downto 0); 56 | signal m_dout_vld : std_logic; 57 | signal s_dout_vld : std_logic; 58 | signal m_dout_reg : std_logic_vector(3 downto 0); 59 | signal s_dout_reg : std_logic_vector(3 downto 0); 60 | 61 | begin 62 | 63 | -- ------------------------------------------------------------------------- 64 | -- RESET SYNCHRONIZER 65 | -- ------------------------------------------------------------------------- 66 | 67 | reset_sync_i : entity work.RST_SYNC 68 | port map ( 69 | CLK => CLK, 70 | ASYNC_RST => BTN_RST, 71 | SYNCED_RST => reset 72 | ); 73 | 74 | -- ------------------------------------------------------------------------- 75 | -- CLOCK ENABLE GENERATOR 76 | -- ------------------------------------------------------------------------- 77 | 78 | clk_en_gen_i : entity work.CLK_EN_GEN 79 | generic map ( 80 | CLK_FREQ => 50e6 81 | ) 82 | port map ( 83 | CLK => CLK, 84 | ASYNC_RST => reset, 85 | CLK_EN_1K => clk_en_1k 86 | ); 87 | 88 | -- ------------------------------------------------------------------------- 89 | -- BUTTONS DEBOUNCE 90 | -- ------------------------------------------------------------------------- 91 | 92 | btn1_debounce_i : entity work.BTN_DEBOUNCE 93 | port map ( 94 | CLK => CLK, 95 | ASYNC_RST => reset, 96 | SAMPLE_EN => clk_en_1k, 97 | BTN_RAW => BTN_MENU_MODE, 98 | BTN_DEB => open, 99 | BTN_DEB_RE => menu_mode_en 100 | ); 101 | 102 | btn2_debounce_i : entity work.BTN_DEBOUNCE 103 | port map ( 104 | CLK => CLK, 105 | ASYNC_RST => reset, 106 | SAMPLE_EN => clk_en_1k, 107 | BTN_RAW => BTN_MENU_ACTION, 108 | BTN_DEB => open, 109 | BTN_DEB_RE => menu_action_en 110 | ); 111 | 112 | -- ------------------------------------------------------------------------- 113 | -- MENU MODE COUNTER 114 | -- ------------------------------------------------------------------------- 115 | 116 | menu_cnt_p : process (CLK, reset) 117 | begin 118 | if (reset = '1') then 119 | menu_cnt <= (others => '0'); 120 | elsif (rising_edge(CLK)) then 121 | if (menu_mode_en = '1') then 122 | if (menu_cnt = "11") then 123 | menu_cnt <= (others => '0'); 124 | else 125 | menu_cnt <= menu_cnt + 1; 126 | end if; 127 | end if; 128 | end if; 129 | end process; 130 | 131 | process(menu_cnt,menu_action_en) 132 | begin 133 | case menu_cnt is 134 | when "00" => 135 | m_cnt_en <= '0'; 136 | s_cnt_en <= menu_action_en; 137 | m_din_vld <= '0'; 138 | s_din_vld <= '0'; 139 | menu_mode_led <= "0001"; 140 | when "01" => 141 | m_cnt_en <= '0'; 142 | s_cnt_en <= '0'; 143 | m_din_vld <= '0'; 144 | s_din_vld <= menu_action_en; 145 | menu_mode_led <= "0010"; 146 | when "10" => 147 | m_cnt_en <= menu_action_en; 148 | s_cnt_en <= '0'; 149 | m_din_vld <= '0'; 150 | s_din_vld <= '0'; 151 | menu_mode_led <= "0100"; 152 | when "11" => 153 | m_cnt_en <= '0'; 154 | s_cnt_en <= '0'; 155 | m_din_vld <= menu_action_en; 156 | s_din_vld <= '0'; 157 | menu_mode_led <= "1000"; 158 | when others => 159 | m_cnt_en <= '0'; 160 | s_cnt_en <= '0'; 161 | m_din_vld <= '0'; 162 | s_din_vld <= '0'; 163 | menu_mode_led <= "0000"; 164 | end case; 165 | end process; 166 | 167 | -- ------------------------------------------------------------------------- 168 | -- MASTER COUNTER 169 | -- ------------------------------------------------------------------------- 170 | 171 | m_cnt_p : process (CLK, reset) 172 | begin 173 | if (reset = '1') then 174 | m_cnt <= (others => '0'); 175 | elsif (rising_edge(CLK)) then 176 | if (m_cnt_en = '1') then 177 | if (m_cnt = "1111") then 178 | m_cnt <= (others => '0'); 179 | else 180 | m_cnt <= m_cnt + 1; 181 | end if; 182 | end if; 183 | end if; 184 | end process; 185 | 186 | m_din <= std_logic_vector(m_cnt) & std_logic_vector(m_cnt); 187 | 188 | -- ------------------------------------------------------------------------- 189 | -- SLAVE COUNTER 190 | -- ------------------------------------------------------------------------- 191 | 192 | s_cnt_p : process (CLK, reset) 193 | begin 194 | if (reset = '1') then 195 | s_cnt <= (others => '0'); 196 | elsif (rising_edge(CLK)) then 197 | if (s_cnt_en = '1') then 198 | if (s_cnt = "1111") then 199 | s_cnt <= (others => '0'); 200 | else 201 | s_cnt <= s_cnt + 1; 202 | end if; 203 | end if; 204 | end if; 205 | end process; 206 | 207 | s_din <= std_logic_vector(s_cnt) & std_logic_vector(s_cnt); 208 | 209 | -- ------------------------------------------------------------------------- 210 | -- SPI MASTER AND SLAVE 211 | -- ------------------------------------------------------------------------- 212 | 213 | master_i : entity work.SPI_MASTER 214 | generic map( 215 | CLK_FREQ => 50e6, 216 | SCLK_FREQ => 1e6, 217 | SLAVE_COUNT => 1 218 | ) 219 | port map ( 220 | CLK => CLK, 221 | RST => reset, 222 | -- SPI MASTER INTERFACE 223 | SCLK => M_SCLK, 224 | CS_N(0) => M_CS_N, 225 | MOSI => M_MOSI, 226 | MISO => M_MISO, 227 | -- USER INTERFACE 228 | DIN_ADDR => (others => '0'), 229 | DIN => m_din, 230 | DIN_LAST => '1', 231 | DIN_VLD => m_din_vld, 232 | DIN_RDY => open, 233 | DOUT => m_dout, 234 | DOUT_VLD => m_dout_vld 235 | ); 236 | 237 | m_dout_reg_p : process (CLK, reset) 238 | begin 239 | if (reset = '1') then 240 | m_dout_reg <= (others => '0'); 241 | elsif (rising_edge(CLK)) then 242 | if (m_dout_vld = '1') then 243 | m_dout_reg <= m_dout(3 downto 0); 244 | end if; 245 | end if; 246 | end process; 247 | 248 | slave_i : entity work.SPI_SLAVE 249 | port map ( 250 | CLK => CLK, 251 | RST => reset, 252 | -- SPI MASTER INTERFACE 253 | SCLK => S_SCLK, 254 | CS_N => S_CS_N, 255 | MOSI => S_MOSI, 256 | MISO => S_MISO, 257 | -- USER INTERFACE 258 | DIN => s_din, 259 | DIN_VLD => s_din_vld, 260 | DIN_RDY => open, 261 | DOUT => s_dout, 262 | DOUT_VLD => s_dout_vld 263 | ); 264 | 265 | s_dout_reg_p : process (CLK, reset) 266 | begin 267 | if (reset = '1') then 268 | s_dout_reg <= (others => '0'); 269 | elsif (rising_edge(CLK)) then 270 | if (s_dout_vld = '1') then 271 | s_dout_reg <= s_dout(3 downto 0); 272 | end if; 273 | end if; 274 | end process; 275 | 276 | -- ------------------------------------------------------------------------- 277 | -- SSEG DRIVER 278 | -- ------------------------------------------------------------------------- 279 | 280 | sseg_driver_i : entity work.SSEG_DRIVER 281 | port map ( 282 | CLK => CLK, 283 | CLK_EN_1K => clk_en_1k, 284 | ASYNC_RST => reset, 285 | DATA0 => s_din(3 downto 0), 286 | DATA1 => s_dout_reg, 287 | DATA2 => m_din(3 downto 0), 288 | DATA3 => m_dout_reg, 289 | DOTS => menu_mode_led, 290 | SSEG => SSEG, 291 | SSEG_AN => SSEG_AN 292 | ); 293 | 294 | end architecture; 295 | -------------------------------------------------------------------------------- /examples/spirit_level/quartus/spirit_level_cyc1000.qpf: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # PROJECT: SIMPLE UART FOR FPGA 3 | #------------------------------------------------------------------------------- 4 | # AUTHORS: Jakub Cabal 5 | # LICENSE: The MIT License, please read LICENSE file 6 | # WEBSITE: https://github.com/jakubcabal/uart-for-fpga 7 | #------------------------------------------------------------------------------- 8 | 9 | PROJECT_REVISION = "UART_LOOPBACK_CYC1000" 10 | -------------------------------------------------------------------------------- /examples/spirit_level/quartus/spirit_level_cyc1000.qsf: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | #------------------------------------------------------------------------------- 4 | # AUTHORS: Jakub Cabal 5 | # LICENSE: The MIT License, please read LICENSE file 6 | # WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | #------------------------------------------------------------------------------- 8 | 9 | # QUARTUS SETTINGS FILE FOR CYC1000 BOARD 10 | set_global_assignment -name FAMILY "Cyclone 10 LP" 11 | set_global_assignment -name DEVICE 10CL025YU256C8G 12 | set_global_assignment -name TOP_LEVEL_ENTITY SPIRIT_LEVEL_CYC1000 13 | 14 | # PROJECT VHDL FILES 15 | set_global_assignment -name VHDL_FILE ../../../rtl/spi_master.vhd 16 | set_global_assignment -name VHDL_FILE ../../common/rst_sync.vhd 17 | set_global_assignment -name VHDL_FILE ../spirit_level_cyc1000.vhd 18 | 19 | # TIMING CONSTRAINTS 20 | set_global_assignment -name SDC_FILE ./spirit_level_cyc1000.sdc 21 | 22 | # FPGA PINS ASSIGNMENT 23 | set_location_assignment PIN_M2 -to CLK_12M 24 | set_location_assignment PIN_N6 -to RST_BTN_N 25 | 26 | set_location_assignment PIN_F3 -to SCLK 27 | set_location_assignment PIN_D1 -to CS_N 28 | set_location_assignment PIN_G2 -to MOSI 29 | set_location_assignment PIN_G1 -to MISO 30 | 31 | set_location_assignment PIN_M6 -to USER_LEDS[0] 32 | set_location_assignment PIN_T4 -to USER_LEDS[1] 33 | set_location_assignment PIN_T3 -to USER_LEDS[2] 34 | set_location_assignment PIN_R3 -to USER_LEDS[3] 35 | set_location_assignment PIN_T2 -to USER_LEDS[4] 36 | set_location_assignment PIN_R4 -to USER_LEDS[5] 37 | set_location_assignment PIN_N5 -to USER_LEDS[6] 38 | set_location_assignment PIN_N3 -to USER_LEDS[7] 39 | -------------------------------------------------------------------------------- /examples/spirit_level/quartus/spirit_level_cyc1000.sdc: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | #------------------------------------------------------------------------------- 4 | # AUTHORS: Jakub Cabal 5 | # LICENSE: The MIT License, please read LICENSE file 6 | # WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | #------------------------------------------------------------------------------- 8 | 9 | create_clock -name CLK12M -period 12MHz [get_ports {CLK_12M}] 10 | -------------------------------------------------------------------------------- /examples/spirit_level/spirit_level_cyc1000.vhd: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | -------------------------------------------------------------------------------- 4 | -- AUTHORS: Jakub Cabal 5 | -- LICENSE: The MIT License, please read LICENSE file 6 | -- WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | -------------------------------------------------------------------------------- 8 | 9 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | 13 | entity SPIRIT_LEVEL_CYC1000 is 14 | Port ( 15 | CLK_12M : in std_logic; -- system clock 12 MHz 16 | RST_BTN_N : in std_logic; -- low active reset button 17 | -- SPI MASTER INTERFACE TO LIS3DH SENSOR 18 | SCLK : out std_logic; 19 | CS_N : out std_logic; 20 | MOSI : out std_logic; 21 | MISO : in std_logic; 22 | -- USER LEDS 23 | USER_LEDS : out std_logic_vector(8-1 downto 0) 24 | ); 25 | end entity; 26 | 27 | architecture RTL of SPIRIT_LEVEL_CYC1000 is 28 | 29 | signal rst_btn : std_logic; 30 | signal reset : std_logic; 31 | 32 | signal spi_din : std_logic_vector(8-1 downto 0); 33 | signal spi_din_last : std_logic; 34 | signal spi_din_vld : std_logic; 35 | signal spi_din_rdy : std_logic; 36 | signal spi_dout : std_logic_vector(8-1 downto 0); 37 | signal spi_dout_vld : std_logic; 38 | 39 | type state is (cfg1_addr, cfg1_wr, out_addr, out_rd, out_do); 40 | signal fsm_pstate : state; 41 | signal fsm_nstate : state; 42 | 43 | signal sensor_wr : std_logic; 44 | signal sensor_data : std_logic_vector(8-1 downto 0); 45 | signal sensor_sigd : signed(8-1 downto 0); 46 | 47 | begin 48 | 49 | rst_btn <= not RST_BTN_N; 50 | 51 | rst_sync_i : entity work.RST_SYNC 52 | port map ( 53 | CLK => CLK_12M, 54 | ASYNC_RST => rst_btn, 55 | SYNCED_RST => reset 56 | ); 57 | 58 | spi_master_i : entity work.SPI_MASTER 59 | generic map( 60 | CLK_FREQ => 12e6, 61 | SCLK_FREQ => 1e6, 62 | SLAVE_COUNT => 1 63 | ) 64 | port map ( 65 | CLK => CLK_12M, 66 | RST => reset, 67 | -- SPI MASTER INTERFACE 68 | SCLK => SCLK, 69 | CS_N(0) => CS_N, 70 | MOSI => MOSI, 71 | MISO => MISO, 72 | -- USER INTERFACE 73 | DIN_ADDR => (others => '0'), 74 | DIN => spi_din, 75 | DIN_LAST => spi_din_last, 76 | DIN_VLD => spi_din_vld, 77 | DIN_RDY => spi_din_rdy, 78 | DOUT => spi_dout, 79 | DOUT_VLD => spi_dout_vld 80 | ); 81 | 82 | -- ------------------------------------------------------------------------- 83 | -- FSM 84 | -- ------------------------------------------------------------------------- 85 | 86 | process (CLK_12M) 87 | begin 88 | if (rising_edge(CLK_12M)) then 89 | if (reset = '1') then 90 | fsm_pstate <= cfg1_addr; 91 | else 92 | fsm_pstate <= fsm_nstate; 93 | end if; 94 | end if; 95 | end process; 96 | 97 | process (fsm_pstate, spi_din_rdy, spi_dout_vld) 98 | begin 99 | fsm_nstate <= fsm_pstate; 100 | spi_din <= (others => '0'); 101 | spi_din_last <= '0'; 102 | spi_din_vld <= '0'; 103 | sensor_wr <= '0'; 104 | 105 | case fsm_pstate is 106 | when cfg1_addr => 107 | spi_din <= "00100000"; 108 | spi_din_vld <= '1'; 109 | if (spi_din_rdy = '1') then 110 | fsm_nstate <= cfg1_wr; 111 | end if; 112 | 113 | when cfg1_wr => 114 | spi_din <= X"37"; 115 | spi_din_vld <= '1'; 116 | spi_din_last <= '1'; 117 | if (spi_din_rdy = '1') then 118 | fsm_nstate <= out_addr; 119 | end if; 120 | 121 | when out_addr => 122 | spi_din <= "10101001"; 123 | spi_din_vld <= '1'; 124 | if (spi_din_rdy = '1') then 125 | fsm_nstate <= out_rd; 126 | end if; 127 | 128 | when out_rd => 129 | spi_din_vld <= '1'; 130 | spi_din_last <= '1'; 131 | if (spi_din_rdy = '1') then 132 | fsm_nstate <= out_do; 133 | end if; 134 | 135 | when out_do => 136 | if (spi_dout_vld = '1') then 137 | sensor_wr <= '1'; 138 | fsm_nstate <= out_addr; 139 | end if; 140 | end case; 141 | end process; 142 | 143 | -- ------------------------------------------------------------------------- 144 | -- SENSOR DATA REGISTER 145 | -- ------------------------------------------------------------------------- 146 | 147 | process (CLK_12M) 148 | begin 149 | if (rising_edge(CLK_12M)) then 150 | if (sensor_wr = '1') then 151 | sensor_data <= spi_dout; 152 | end if; 153 | end if; 154 | end process; 155 | 156 | -- ------------------------------------------------------------------------- 157 | -- USER LEDS LOGIC 158 | -- ------------------------------------------------------------------------- 159 | 160 | sensor_sigd <= signed(sensor_data); 161 | 162 | process (CLK_12M) 163 | begin 164 | if (rising_edge(CLK_12M)) then 165 | USER_LEDS <= "00000000"; 166 | if (sensor_sigd <= -16) then 167 | USER_LEDS <= "10000000"; 168 | end if; 169 | if (sensor_sigd > -16) and (sensor_sigd <= -12) then 170 | USER_LEDS <= "01000000"; 171 | end if; 172 | if (sensor_sigd > -12) and (sensor_sigd <= -8) then 173 | USER_LEDS <= "00100000"; 174 | end if; 175 | if (sensor_sigd > -8) and (sensor_sigd <= -4) then 176 | USER_LEDS <= "00010000"; 177 | end if; 178 | if (sensor_sigd > -4) and (sensor_sigd < 4) then 179 | USER_LEDS <= "00011000"; 180 | end if; 181 | if (sensor_sigd >= 4) and (sensor_sigd < 8) then 182 | USER_LEDS <= "00001000"; 183 | end if; 184 | if (sensor_sigd >= 8) and (sensor_sigd < 12) then 185 | USER_LEDS <= "00000100"; 186 | end if; 187 | if (sensor_sigd >= 12) and (sensor_sigd < 16) then 188 | USER_LEDS <= "00000010"; 189 | end if; 190 | if (sensor_sigd >= 16) then 191 | USER_LEDS <= "00000001"; 192 | end if; 193 | end if; 194 | end process; 195 | 196 | end architecture; 197 | -------------------------------------------------------------------------------- /rtl/spi_master.vhd: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | -------------------------------------------------------------------------------- 4 | -- AUTHORS: Jakub Cabal 5 | -- LICENSE: The MIT License, please read LICENSE file 6 | -- WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | -------------------------------------------------------------------------------- 8 | 9 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | use IEEE.MATH_REAL.ALL; 13 | 14 | -- THE SPI MASTER MODULE SUPPORT ONLY SPI MODE 0 (CPOL=0, CPHA=0)!!! 15 | 16 | entity SPI_MASTER is 17 | Generic ( 18 | CLK_FREQ : natural := 50e6; -- set system clock frequency in Hz 19 | SCLK_FREQ : natural := 5e6; -- set SPI clock frequency in Hz (condition: SCLK_FREQ <= CLK_FREQ/10) 20 | WORD_SIZE : natural := 8; -- size of transfer word in bits, must be power of two 21 | SLAVE_COUNT : natural := 1 -- count of SPI slaves 22 | ); 23 | Port ( 24 | CLK : in std_logic; -- system clock 25 | RST : in std_logic; -- high active synchronous reset 26 | -- SPI MASTER INTERFACE 27 | SCLK : out std_logic; -- SPI clock 28 | CS_N : out std_logic_vector(SLAVE_COUNT-1 downto 0); -- SPI chip select, active in low 29 | MOSI : out std_logic; -- SPI serial data from master to slave 30 | MISO : in std_logic; -- SPI serial data from slave to master 31 | -- INPUT USER INTERFACE 32 | DIN : in std_logic_vector(WORD_SIZE-1 downto 0); -- data for transmission to SPI slave 33 | DIN_ADDR : in std_logic_vector(natural(ceil(log2(real(SLAVE_COUNT))))-1 downto 0); -- SPI slave address 34 | DIN_LAST : in std_logic; -- when DIN_LAST = 1, last data word, after transmit will be asserted CS_N 35 | DIN_VLD : in std_logic; -- when DIN_VLD = 1, data for transmission are valid 36 | DIN_RDY : out std_logic; -- when DIN_RDY = 1, SPI master is ready to accept valid data for transmission 37 | -- OUTPUT USER INTERFACE 38 | DOUT : out std_logic_vector(WORD_SIZE-1 downto 0); -- received data from SPI slave 39 | DOUT_VLD : out std_logic -- when DOUT_VLD = 1, received data are valid 40 | ); 41 | end entity; 42 | 43 | architecture RTL of SPI_MASTER is 44 | 45 | constant DIVIDER_VALUE : natural := (CLK_FREQ/SCLK_FREQ)/2; 46 | constant WIDTH_CLK_CNT : natural := natural(ceil(log2(real(DIVIDER_VALUE)))); 47 | constant WIDTH_ADDR : natural := natural(ceil(log2(real(SLAVE_COUNT)))); 48 | constant BIT_CNT_WIDTH : natural := natural(ceil(log2(real(WORD_SIZE)))); 49 | 50 | type state_t is (idle, first_edge, second_edge, transmit_end, transmit_gap); 51 | 52 | signal addr_reg : unsigned(WIDTH_ADDR-1 downto 0); 53 | signal sys_clk_cnt : unsigned(WIDTH_CLK_CNT-1 downto 0); 54 | signal sys_clk_cnt_max : std_logic; 55 | signal spi_clk : std_logic; 56 | signal spi_clk_rst : std_logic; 57 | signal din_last_reg_n : std_logic; 58 | signal first_edge_en : std_logic; 59 | signal second_edge_en : std_logic; 60 | signal chip_select_n : std_logic; 61 | signal load_data : std_logic; 62 | signal miso_reg : std_logic; 63 | signal shreg : std_logic_vector(WORD_SIZE-1 downto 0); 64 | signal bit_cnt : unsigned(BIT_CNT_WIDTH-1 downto 0); 65 | signal bit_cnt_max : std_logic; 66 | signal rx_data_vld : std_logic; 67 | signal master_ready : std_logic; 68 | signal present_state : state_t; 69 | signal next_state : state_t; 70 | 71 | begin 72 | 73 | ASSERT (DIVIDER_VALUE >= 5) REPORT "condition: SCLK_FREQ <= CLK_FREQ/10" SEVERITY ERROR; 74 | 75 | load_data <= master_ready and DIN_VLD; 76 | DIN_RDY <= master_ready; 77 | 78 | -- ------------------------------------------------------------------------- 79 | -- SYSTEM CLOCK COUNTER 80 | -- ------------------------------------------------------------------------- 81 | 82 | sys_clk_cnt_max <= '1' when (to_integer(sys_clk_cnt) = DIVIDER_VALUE-1) else '0'; 83 | 84 | sys_clk_cnt_reg_p : process (CLK) 85 | begin 86 | if (rising_edge(CLK)) then 87 | if (RST = '1' or sys_clk_cnt_max = '1') then 88 | sys_clk_cnt <= (others => '0'); 89 | else 90 | sys_clk_cnt <= sys_clk_cnt + 1; 91 | end if; 92 | end if; 93 | end process; 94 | 95 | -- ------------------------------------------------------------------------- 96 | -- SPI CLOCK GENERATOR AND REGISTER 97 | -- ------------------------------------------------------------------------- 98 | 99 | spi_clk_gen_p : process (CLK) 100 | begin 101 | if (rising_edge(CLK)) then 102 | if (RST = '1' or spi_clk_rst = '1') then 103 | spi_clk <= '0'; 104 | elsif (sys_clk_cnt_max = '1') then 105 | spi_clk <= not spi_clk; 106 | end if; 107 | end if; 108 | end process; 109 | 110 | SCLK <= spi_clk; 111 | 112 | -- ------------------------------------------------------------------------- 113 | -- BIT COUNTER 114 | -- ------------------------------------------------------------------------- 115 | 116 | bit_cnt_max <= '1' when (bit_cnt = WORD_SIZE-1) else '0'; 117 | 118 | bit_cnt_p : process (CLK) 119 | begin 120 | if (rising_edge(CLK)) then 121 | if (RST = '1' or spi_clk_rst = '1') then 122 | bit_cnt <= (others => '0'); 123 | elsif (second_edge_en = '1') then 124 | bit_cnt <= bit_cnt + 1; 125 | end if; 126 | end if; 127 | end process; 128 | 129 | -- ------------------------------------------------------------------------- 130 | -- SPI MASTER ADDRESSING 131 | -- ------------------------------------------------------------------------- 132 | 133 | addr_reg_p : process (CLK) 134 | begin 135 | if (rising_edge(CLK)) then 136 | if (RST = '1') then 137 | addr_reg <= (others => '0'); 138 | elsif (load_data = '1') then 139 | addr_reg <= unsigned(DIN_ADDR); 140 | end if; 141 | end if; 142 | end process; 143 | 144 | one_slave_g: if (SLAVE_COUNT = 1) generate 145 | CS_N(0) <= chip_select_n; 146 | end generate; 147 | 148 | more_slaves_g: if (SLAVE_COUNT > 1) generate 149 | cs_n_g : for i in 0 to SLAVE_COUNT-1 generate 150 | cs_n_p : process (addr_reg, chip_select_n) 151 | begin 152 | if (addr_reg = i) then 153 | CS_N(i) <= chip_select_n; 154 | else 155 | CS_N(i) <= '1'; 156 | end if; 157 | end process; 158 | end generate; 159 | end generate; 160 | 161 | -- ------------------------------------------------------------------------- 162 | -- DIN LAST RESISTER 163 | -- ------------------------------------------------------------------------- 164 | 165 | din_last_reg_n_p : process (CLK) 166 | begin 167 | if (rising_edge(CLK)) then 168 | if (RST = '1') then 169 | din_last_reg_n <= '0'; 170 | elsif (load_data = '1') then 171 | din_last_reg_n <= not DIN_LAST; 172 | end if; 173 | end if; 174 | end process; 175 | 176 | -- ------------------------------------------------------------------------- 177 | -- MISO SAMPLE REGISTER 178 | -- ------------------------------------------------------------------------- 179 | 180 | miso_reg_p : process (CLK) 181 | begin 182 | if (rising_edge(CLK)) then 183 | if (first_edge_en = '1') then 184 | miso_reg <= MISO; 185 | end if; 186 | end if; 187 | end process; 188 | 189 | -- ------------------------------------------------------------------------- 190 | -- DATA SHIFT REGISTER 191 | -- ------------------------------------------------------------------------- 192 | 193 | shreg_p : process (CLK) 194 | begin 195 | if (rising_edge(CLK)) then 196 | if (load_data = '1') then 197 | shreg <= DIN; 198 | elsif (second_edge_en = '1') then 199 | shreg <= shreg(WORD_SIZE-2 downto 0) & miso_reg; 200 | end if; 201 | end if; 202 | end process; 203 | 204 | DOUT <= shreg; 205 | MOSI <= shreg(WORD_SIZE-1); 206 | 207 | -- ------------------------------------------------------------------------- 208 | -- DATA OUT VALID RESISTER 209 | -- ------------------------------------------------------------------------- 210 | 211 | dout_vld_reg_p : process (CLK) 212 | begin 213 | if (rising_edge(CLK)) then 214 | if (RST = '1') then 215 | DOUT_VLD <= '0'; 216 | else 217 | DOUT_VLD <= rx_data_vld; 218 | end if; 219 | end if; 220 | end process; 221 | 222 | -- ------------------------------------------------------------------------- 223 | -- SPI MASTER FSM 224 | -- ------------------------------------------------------------------------- 225 | 226 | -- PRESENT STATE REGISTER 227 | fsm_present_state_p : process (CLK) 228 | begin 229 | if (rising_edge(CLK)) then 230 | if (RST = '1') then 231 | present_state <= idle; 232 | else 233 | present_state <= next_state; 234 | end if; 235 | end if; 236 | end process; 237 | 238 | -- NEXT STATE LOGIC 239 | fsm_next_state_p : process (present_state, DIN_VLD, sys_clk_cnt_max, 240 | bit_cnt_max) 241 | begin 242 | case present_state is 243 | when idle => 244 | if (DIN_VLD = '1') then 245 | next_state <= first_edge; 246 | else 247 | next_state <= idle; 248 | end if; 249 | 250 | when first_edge => 251 | if (sys_clk_cnt_max = '1') then 252 | next_state <= second_edge; 253 | else 254 | next_state <= first_edge; 255 | end if; 256 | 257 | when second_edge => 258 | if (sys_clk_cnt_max = '1') then 259 | if (bit_cnt_max = '1') then 260 | next_state <= transmit_end; 261 | else 262 | next_state <= first_edge; 263 | end if; 264 | else 265 | next_state <= second_edge; 266 | end if; 267 | 268 | when transmit_end => 269 | if (sys_clk_cnt_max = '1') then 270 | next_state <= transmit_gap; 271 | else 272 | next_state <= transmit_end; 273 | end if; 274 | 275 | when transmit_gap => 276 | if (sys_clk_cnt_max = '1') then 277 | next_state <= idle; 278 | else 279 | next_state <= transmit_gap; 280 | end if; 281 | 282 | when others => 283 | next_state <= idle; 284 | end case; 285 | end process; 286 | 287 | -- OUTPUTS LOGIC 288 | fsm_outputs_p : process (present_state, din_last_reg_n, sys_clk_cnt_max) 289 | begin 290 | case present_state is 291 | when idle => 292 | master_ready <= '1'; 293 | chip_select_n <= not din_last_reg_n; 294 | spi_clk_rst <= '1'; 295 | first_edge_en <= '0'; 296 | second_edge_en <= '0'; 297 | rx_data_vld <= '0'; 298 | 299 | when first_edge => 300 | master_ready <= '0'; 301 | chip_select_n <= '0'; 302 | spi_clk_rst <= '0'; 303 | first_edge_en <= sys_clk_cnt_max; 304 | second_edge_en <= '0'; 305 | rx_data_vld <= '0'; 306 | 307 | when second_edge => 308 | master_ready <= '0'; 309 | chip_select_n <= '0'; 310 | spi_clk_rst <= '0'; 311 | first_edge_en <= '0'; 312 | second_edge_en <= sys_clk_cnt_max; 313 | rx_data_vld <= '0'; 314 | 315 | when transmit_end => 316 | master_ready <= '0'; 317 | chip_select_n <= '0'; 318 | spi_clk_rst <= '1'; 319 | first_edge_en <= '0'; 320 | second_edge_en <= '0'; 321 | rx_data_vld <= sys_clk_cnt_max; 322 | 323 | when transmit_gap => 324 | master_ready <= '0'; 325 | chip_select_n <= not din_last_reg_n; 326 | spi_clk_rst <= '1'; 327 | first_edge_en <= '0'; 328 | second_edge_en <= '0'; 329 | rx_data_vld <= '0'; 330 | 331 | when others => 332 | master_ready <= '0'; 333 | chip_select_n <= not din_last_reg_n; 334 | spi_clk_rst <= '1'; 335 | first_edge_en <= '0'; 336 | second_edge_en <= '0'; 337 | rx_data_vld <= '0'; 338 | end case; 339 | end process; 340 | 341 | end architecture; 342 | -------------------------------------------------------------------------------- /rtl/spi_slave.vhd: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | -------------------------------------------------------------------------------- 4 | -- AUTHORS: Jakub Cabal 5 | -- LICENSE: The MIT License, please read LICENSE file 6 | -- WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | -------------------------------------------------------------------------------- 8 | 9 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | use IEEE.MATH_REAL.ALL; 13 | 14 | -- THE SPI SLAVE MODULE SUPPORT ONLY SPI MODE 0 (CPOL=0, CPHA=0)!!! 15 | 16 | entity SPI_SLAVE is 17 | Generic ( 18 | WORD_SIZE : natural := 8 -- size of transfer word in bits, must be power of two 19 | ); 20 | Port ( 21 | CLK : in std_logic; -- system clock 22 | RST : in std_logic; -- high active synchronous reset 23 | -- SPI SLAVE INTERFACE 24 | SCLK : in std_logic; -- SPI clock 25 | CS_N : in std_logic; -- SPI chip select, active in low 26 | MOSI : in std_logic; -- SPI serial data from master to slave 27 | MISO : out std_logic; -- SPI serial data from slave to master 28 | -- USER INTERFACE 29 | DIN : in std_logic_vector(WORD_SIZE-1 downto 0); -- data for transmission to SPI master 30 | DIN_VLD : in std_logic; -- when DIN_VLD = 1, data for transmission are valid 31 | DIN_RDY : out std_logic; -- when DIN_RDY = 1, SPI slave is ready to accept valid data for transmission 32 | DOUT : out std_logic_vector(WORD_SIZE-1 downto 0); -- received data from SPI master 33 | DOUT_VLD : out std_logic -- when DOUT_VLD = 1, received data are valid 34 | ); 35 | end entity; 36 | 37 | architecture RTL of SPI_SLAVE is 38 | 39 | constant BIT_CNT_WIDTH : natural := natural(ceil(log2(real(WORD_SIZE)))); 40 | 41 | signal sclk_meta : std_logic; 42 | signal cs_n_meta : std_logic; 43 | signal mosi_meta : std_logic; 44 | signal sclk_reg : std_logic; 45 | signal cs_n_reg : std_logic; 46 | signal mosi_reg : std_logic; 47 | signal spi_clk_reg : std_logic; 48 | signal spi_clk_redge_en : std_logic; 49 | signal spi_clk_fedge_en : std_logic; 50 | signal bit_cnt : unsigned(BIT_CNT_WIDTH-1 downto 0); 51 | signal bit_cnt_max : std_logic; 52 | signal last_bit_en : std_logic; 53 | signal load_data_en : std_logic; 54 | signal data_shreg : std_logic_vector(WORD_SIZE-1 downto 0); 55 | signal slave_ready : std_logic; 56 | signal shreg_busy : std_logic; 57 | signal rx_data_vld : std_logic; 58 | 59 | begin 60 | 61 | -- ------------------------------------------------------------------------- 62 | -- INPUT SYNCHRONIZATION REGISTERS 63 | -- ------------------------------------------------------------------------- 64 | 65 | -- Synchronization registers to eliminate possible metastability. 66 | sync_ffs_p : process (CLK) 67 | begin 68 | if (rising_edge(CLK)) then 69 | sclk_meta <= SCLK; 70 | cs_n_meta <= CS_N; 71 | mosi_meta <= MOSI; 72 | sclk_reg <= sclk_meta; 73 | cs_n_reg <= cs_n_meta; 74 | mosi_reg <= mosi_meta; 75 | end if; 76 | end process; 77 | 78 | -- ------------------------------------------------------------------------- 79 | -- SPI CLOCK REGISTER 80 | -- ------------------------------------------------------------------------- 81 | 82 | -- The SPI clock register is necessary for clock edge detection. 83 | spi_clk_reg_p : process (CLK) 84 | begin 85 | if (rising_edge(CLK)) then 86 | if (RST = '1') then 87 | spi_clk_reg <= '0'; 88 | else 89 | spi_clk_reg <= sclk_reg; 90 | end if; 91 | end if; 92 | end process; 93 | 94 | -- ------------------------------------------------------------------------- 95 | -- SPI CLOCK EDGES FLAGS 96 | -- ------------------------------------------------------------------------- 97 | 98 | -- Falling edge is detect when sclk_reg=0 and spi_clk_reg=1. 99 | spi_clk_fedge_en <= not sclk_reg and spi_clk_reg; 100 | -- Rising edge is detect when sclk_reg=1 and spi_clk_reg=0. 101 | spi_clk_redge_en <= sclk_reg and not spi_clk_reg; 102 | 103 | -- ------------------------------------------------------------------------- 104 | -- RECEIVED BITS COUNTER 105 | -- ------------------------------------------------------------------------- 106 | 107 | -- The counter counts received bits from the master. Counter is enabled when 108 | -- falling edge of SPI clock is detected and not asserted cs_n_reg. 109 | bit_cnt_p : process (CLK) 110 | begin 111 | if (rising_edge(CLK)) then 112 | if (RST = '1') then 113 | bit_cnt <= (others => '0'); 114 | elsif (spi_clk_fedge_en = '1' and cs_n_reg = '0') then 115 | if (bit_cnt_max = '1') then 116 | bit_cnt <= (others => '0'); 117 | else 118 | bit_cnt <= bit_cnt + 1; 119 | end if; 120 | end if; 121 | end if; 122 | end process; 123 | 124 | -- The flag of maximal value of the bit counter. 125 | bit_cnt_max <= '1' when (bit_cnt = WORD_SIZE-1) else '0'; 126 | 127 | -- ------------------------------------------------------------------------- 128 | -- LAST BIT FLAG REGISTER 129 | -- ------------------------------------------------------------------------- 130 | 131 | -- The flag of last bit of received byte is only registered the flag of 132 | -- maximal value of the bit counter. 133 | last_bit_en_p : process (CLK) 134 | begin 135 | if (rising_edge(CLK)) then 136 | if (RST = '1') then 137 | last_bit_en <= '0'; 138 | else 139 | last_bit_en <= bit_cnt_max; 140 | end if; 141 | end if; 142 | end process; 143 | 144 | -- ------------------------------------------------------------------------- 145 | -- RECEIVED DATA VALID FLAG 146 | -- ------------------------------------------------------------------------- 147 | 148 | -- Received data from master are valid when falling edge of SPI clock is 149 | -- detected and the last bit of received byte is detected. 150 | rx_data_vld <= spi_clk_fedge_en and last_bit_en; 151 | 152 | -- ------------------------------------------------------------------------- 153 | -- SHIFT REGISTER BUSY FLAG REGISTER 154 | -- ------------------------------------------------------------------------- 155 | 156 | -- Data shift register is busy until it sends all input data to SPI master. 157 | shreg_busy_p : process (CLK) 158 | begin 159 | if (rising_edge(CLK)) then 160 | if (RST = '1') then 161 | shreg_busy <= '0'; 162 | else 163 | if (DIN_VLD = '1' and (cs_n_reg = '1' or rx_data_vld = '1')) then 164 | shreg_busy <= '1'; 165 | elsif (rx_data_vld = '1') then 166 | shreg_busy <= '0'; 167 | else 168 | shreg_busy <= shreg_busy; 169 | end if; 170 | end if; 171 | end if; 172 | end process; 173 | 174 | -- The SPI slave is ready for accept new input data when cs_n_reg is assert and 175 | -- shift register not busy or when received data are valid. 176 | slave_ready <= (cs_n_reg and not shreg_busy) or rx_data_vld; 177 | 178 | -- The new input data is loaded into the shift register when the SPI slave 179 | -- is ready and input data are valid. 180 | load_data_en <= slave_ready and DIN_VLD; 181 | 182 | -- ------------------------------------------------------------------------- 183 | -- DATA SHIFT REGISTER 184 | -- ------------------------------------------------------------------------- 185 | 186 | -- The shift register holds data for sending to master, capture and store 187 | -- incoming data from master. 188 | data_shreg_p : process (CLK) 189 | begin 190 | if (rising_edge(CLK)) then 191 | if (load_data_en = '1') then 192 | data_shreg <= DIN; 193 | elsif (spi_clk_redge_en = '1' and cs_n_reg = '0') then 194 | data_shreg <= data_shreg(WORD_SIZE-2 downto 0) & mosi_reg; 195 | end if; 196 | end if; 197 | end process; 198 | 199 | -- ------------------------------------------------------------------------- 200 | -- MISO REGISTER 201 | -- ------------------------------------------------------------------------- 202 | 203 | -- The output MISO register ensures that the bits are transmit to the master 204 | -- when is not assert cs_n_reg and falling edge of SPI clock is detected. 205 | miso_p : process (CLK) 206 | begin 207 | if (rising_edge(CLK)) then 208 | if (load_data_en = '1') then 209 | MISO <= DIN(WORD_SIZE-1); 210 | elsif (spi_clk_fedge_en = '1' and cs_n_reg = '0') then 211 | MISO <= data_shreg(WORD_SIZE-1); 212 | end if; 213 | end if; 214 | end process; 215 | 216 | -- ------------------------------------------------------------------------- 217 | -- ASSIGNING OUTPUT SIGNALS 218 | -- ------------------------------------------------------------------------- 219 | 220 | DIN_RDY <= slave_ready; 221 | DOUT <= data_shreg; 222 | DOUT_VLD <= rx_data_vld; 223 | 224 | end architecture; 225 | -------------------------------------------------------------------------------- /sim/spi_master_tb.vhd: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | -------------------------------------------------------------------------------- 4 | -- AUTHORS: Jakub Cabal 5 | -- LICENSE: The MIT License, please read LICENSE file 6 | -- WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | -------------------------------------------------------------------------------- 8 | 9 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | use IEEE.MATH_REAL.ALL; 13 | 14 | entity SPI_MASTER_TB is 15 | Generic ( 16 | CLK_FREQ : natural := 50e6; -- system clock frequency in Hz 17 | SPI_FREQ : natural := 1e6; -- spi clock frequency in Hz 18 | WORD_SIZE : natural := 8; -- size of transfer word in bits, must be power of two 19 | TRANS_COUNT : natural := 1e4 -- number of test transaction 20 | ); 21 | end entity; 22 | 23 | architecture SIM of SPI_MASTER_TB is 24 | 25 | constant CLK_PERIOD : time := 1 ns * integer(real(1e9)/real(CLK_FREQ)); 26 | constant SPI_PERIOD : time := 1 ns * integer(real(1e9)/real(SPI_FREQ)); 27 | constant RX_OFFSET : natural := 42; 28 | constant TX_OFFSET : natural := 11; 29 | 30 | signal CLK : std_logic; 31 | signal RST : std_logic; 32 | 33 | signal sclk : std_logic; 34 | signal cs_n : std_logic; 35 | signal mosi : std_logic; 36 | signal miso : std_logic; 37 | 38 | signal udi : std_logic_vector(WORD_SIZE-1 downto 0); 39 | signal udi_vld : std_logic; 40 | signal udi_rdy : std_logic; 41 | signal udo : std_logic_vector(WORD_SIZE-1 downto 0); 42 | signal udo_exp : std_logic_vector(WORD_SIZE-1 downto 0); 43 | signal udo_vld : std_logic; 44 | 45 | signal spi_sdi : std_logic_vector(WORD_SIZE-1 downto 0); 46 | signal spi_sdo : std_logic_vector(WORD_SIZE-1 downto 0); 47 | signal spi_sdo_exp : std_logic_vector(WORD_SIZE-1 downto 0); 48 | 49 | signal spi_model_done : std_logic := '0'; 50 | signal udi_done : std_logic := '0'; 51 | signal udo_done : std_logic := '0'; 52 | signal sim_done : std_logic := '0'; 53 | signal rand_int : integer := 0; 54 | signal count_rx : integer; 55 | signal count_tx : integer; 56 | 57 | procedure SPI_SLAVE ( 58 | signal SSM_SDI : in std_logic_vector(WORD_SIZE-1 downto 0); 59 | signal SSM_SDO : out std_logic_vector(WORD_SIZE-1 downto 0); 60 | signal SSM_SCLK : in std_logic; 61 | signal SSM_CS_N : in std_logic; 62 | signal SSM_MOSI : in std_logic; 63 | signal SSM_MISO : out std_logic 64 | ) is 65 | begin 66 | wait until SSM_CS_N = '0'; 67 | for i in 0 to (WORD_SIZE-1) loop 68 | SSM_MISO <= SSM_SDI(WORD_SIZE-1-i); 69 | wait until rising_edge(SSM_SCLK); 70 | SSM_SDO(WORD_SIZE-1-i) <= SSM_MOSI; 71 | wait until falling_edge(SSM_SCLK); 72 | end loop; 73 | end procedure; 74 | 75 | begin 76 | 77 | rand_int_p : process 78 | variable seed1, seed2: positive; 79 | variable rand : real; 80 | begin 81 | uniform(seed1, seed2, rand); 82 | rand_int <= integer(rand*real(20)); 83 | --report "Random number X: " & integer'image(rand_int); 84 | wait for CLK_PERIOD; 85 | if (sim_done = '1') then 86 | wait; 87 | end if; 88 | end process; 89 | 90 | dut : entity work.SPI_MASTER 91 | generic map ( 92 | CLK_FREQ => CLK_FREQ, 93 | SCLK_FREQ => SPI_FREQ, 94 | WORD_SIZE => WORD_SIZE 95 | ) 96 | port map ( 97 | CLK => CLK, 98 | RST => RST, 99 | -- SPI SLAVE INTERFACE 100 | SCLK => sclk, 101 | CS_N(0) => cs_n, 102 | MOSI => mosi, 103 | MISO => miso, 104 | -- USER INTERFACE 105 | DIN => udi, 106 | DIN_ADDR => (others => '0'), 107 | DIN_LAST => '1', 108 | DIN_VLD => udi_vld, 109 | DIN_RDY => udi_rdy, 110 | DOUT => udo, 111 | DOUT_VLD => udo_vld 112 | ); 113 | 114 | clk_gen_p : process 115 | begin 116 | CLK <= '0'; 117 | wait for CLK_PERIOD/2; 118 | CLK <= '1'; 119 | wait for CLK_PERIOD/2; 120 | if (sim_done = '1') then 121 | wait; 122 | end if; 123 | end process; 124 | 125 | rst_gen_p : process 126 | begin 127 | report "======== SIMULATION START! ========"; 128 | report "Total transactions for master to slave direction: " & integer'image(TRANS_COUNT); 129 | report "Total transactions for slave to master direction: " & integer'image(TRANS_COUNT); 130 | RST <= '1'; 131 | wait for CLK_PERIOD*3; 132 | RST <= '0'; 133 | wait; 134 | end process; 135 | 136 | -- ------------------------------------------------------------------------- 137 | -- DUT TEST 138 | -- ------------------------------------------------------------------------- 139 | 140 | spi_slave_model_p : process 141 | begin 142 | count_tx <= 1; 143 | wait until RST = '0'; 144 | for i in 0 to TRANS_COUNT-1 loop 145 | spi_sdi <= std_logic_vector(to_unsigned(((i+RX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); 146 | spi_sdo_exp <= std_logic_vector(to_unsigned(((i+TX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); 147 | SPI_SLAVE(spi_sdi, spi_sdo, sclk, cs_n, mosi, miso); 148 | if (spi_sdo = spi_sdo_exp) then 149 | if ((count_tx mod (TRANS_COUNT/10)) = 0) then 150 | report "Transactions received from master: " & integer'image(count_tx); 151 | end if; 152 | else 153 | report "======== UNEXPECTED TRANSACTION ON MOSI SIGNAL (master to slave)! ========" severity failure; 154 | end if; 155 | count_tx <= count_tx + 1; 156 | end loop; 157 | spi_model_done <= '1'; 158 | wait; 159 | end process; 160 | 161 | spi_master_udi_p : process 162 | begin 163 | wait until RST = '0'; 164 | wait until rising_edge(CLK); 165 | wait for CLK_PERIOD/2; 166 | for i in 0 to TRANS_COUNT-1 loop 167 | udi <= std_logic_vector(to_unsigned(((i+TX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); 168 | udi_vld <= '1'; 169 | if (udi_rdy = '0') then 170 | wait until udi_rdy = '1'; 171 | wait for CLK_PERIOD/2; 172 | end if; 173 | wait for CLK_PERIOD; 174 | udi_vld <= '0'; 175 | wait for rand_int*CLK_PERIOD; 176 | end loop; 177 | udi_done <= '1'; 178 | wait; 179 | end process; 180 | 181 | spi_master_udo_p : process 182 | begin 183 | count_rx <= 1; 184 | for i in 0 to TRANS_COUNT-1 loop 185 | udo_exp <= std_logic_vector(to_unsigned(((i+RX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); 186 | wait until udo_vld = '1'; 187 | if (udo = udo_exp) then 188 | if ((count_rx mod (TRANS_COUNT/10)) = 0) then 189 | report "Transactions received from slave: " & integer'image(count_rx); 190 | end if; 191 | else 192 | report "======== UNEXPECTED TRANSACTION ON DOUT SIGNAL (slave to master)! ========" severity failure; 193 | end if; 194 | count_rx <= count_rx + 1; 195 | wait for CLK_PERIOD; 196 | end loop; 197 | udo_done <= '1'; 198 | wait; 199 | end process; 200 | 201 | -- ------------------------------------------------------------------------- 202 | -- TEST DONE CHECK 203 | -- ------------------------------------------------------------------------- 204 | 205 | test_done_p : process 206 | variable v_test_done : std_logic; 207 | begin 208 | v_test_done := spi_model_done and udi_done and udo_done; 209 | if (v_test_done = '1') then 210 | wait for 100*CLK_PERIOD; 211 | sim_done <= '1'; 212 | report "======== SIMULATION SUCCESSFULLY COMPLETED! ========"; 213 | wait; 214 | end if; 215 | wait for CLK_PERIOD; 216 | end process; 217 | 218 | end architecture; 219 | -------------------------------------------------------------------------------- /sim/spi_master_tb_ghdl_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #------------------------------------------------------------------------------- 3 | # PROJECT: SPI MASTER AND SLAVE FOR FPGA 4 | #------------------------------------------------------------------------------- 5 | # AUTHORS: Jakub Cabal 6 | # LICENSE: The MIT License, please read LICENSE file 7 | # WEBSITE: https://github.com/jakubcabal/spi-fpga 8 | #------------------------------------------------------------------------------- 9 | 10 | # Setup simulation 11 | sh ./spi_master_tb_ghdl_setup.sh 12 | 13 | # Run the simulation 14 | ghdl -r SPI_MASTER_TB 15 | -------------------------------------------------------------------------------- /sim/spi_master_tb_ghdl_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #------------------------------------------------------------------------------- 3 | # PROJECT: SPI MASTER AND SLAVE FOR FPGA 4 | #------------------------------------------------------------------------------- 5 | # AUTHORS: Jakub Cabal 6 | # LICENSE: The MIT License, please read LICENSE file 7 | # WEBSITE: https://github.com/jakubcabal/spi-fpga 8 | #------------------------------------------------------------------------------- 9 | 10 | # Analyse sources 11 | ghdl -a ../rtl/spi_master.vhd 12 | ghdl -a ./spi_master_tb.vhd 13 | 14 | # Elaborate the top-level 15 | ghdl -e SPI_MASTER_TB 16 | 17 | # Run the simulation 18 | # The following command is allocated to a separate script due to CI purposes. 19 | #ghdl -r SPI_MASTER_TB 20 | -------------------------------------------------------------------------------- /sim/spi_master_tb_msim_run.tcl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | #------------------------------------------------------------------------------- 4 | # AUTHORS: Jakub Cabal 5 | # LICENSE: The MIT License, please read LICENSE file 6 | # WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | #------------------------------------------------------------------------------- 8 | 9 | # Create work library 10 | vlib work 11 | 12 | # Compile VHDL files 13 | vcom -93 ../rtl/spi_master.vhd 14 | vcom -93 ./spi_master_tb.vhd 15 | 16 | # Load testbench 17 | vsim work.spi_master_tb 18 | 19 | # Setup and start simulation 20 | add wave sim:/spi_master_tb/* 21 | add wave sim:/spi_master_tb/dut/* 22 | run -All 23 | -------------------------------------------------------------------------------- /sim/spi_slave_tb.vhd: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | -------------------------------------------------------------------------------- 4 | -- AUTHORS: Jakub Cabal 5 | -- LICENSE: The MIT License, please read LICENSE file 6 | -- WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | -------------------------------------------------------------------------------- 8 | 9 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | use IEEE.MATH_REAL.ALL; 13 | 14 | entity SPI_SLAVE_TB is 15 | Generic ( 16 | CLK_FREQ : natural := 50e6; -- system clock frequency in Hz 17 | SPI_FREQ : natural := 1e6; -- spi clock frequency in Hz 18 | WORD_SIZE : natural := 8; -- size of transfer word in bits, must be power of two 19 | TRANS_COUNT : natural := 1e4 -- number of test transaction 20 | ); 21 | end entity; 22 | 23 | architecture SIM of SPI_SLAVE_TB is 24 | 25 | constant CLK_PERIOD : time := 1 ns * integer(real(1e9)/real(CLK_FREQ)); 26 | constant SPI_PERIOD : time := 1 ns * integer(real(1e9)/real(SPI_FREQ)); 27 | constant RX_OFFSET : natural := 42; 28 | constant TX_OFFSET : natural := 11; 29 | 30 | signal CLK : std_logic; 31 | signal RST : std_logic; 32 | 33 | signal sclk : std_logic := '0'; 34 | signal cs_n : std_logic := '1'; 35 | signal mosi : std_logic; 36 | signal miso : std_logic; 37 | 38 | signal udi : std_logic_vector(WORD_SIZE-1 downto 0); 39 | signal udi_vld : std_logic; 40 | signal udi_rdy : std_logic; 41 | signal udo : std_logic_vector(WORD_SIZE-1 downto 0); 42 | signal udo_exp : std_logic_vector(WORD_SIZE-1 downto 0); 43 | signal udo_vld : std_logic; 44 | 45 | signal spi_mdi : std_logic_vector(WORD_SIZE-1 downto 0); 46 | signal spi_mdo : std_logic_vector(WORD_SIZE-1 downto 0); 47 | signal spi_mdo_exp : std_logic_vector(WORD_SIZE-1 downto 0); 48 | 49 | signal spi_model_done : std_logic := '0'; 50 | signal udi_done : std_logic := '0'; 51 | signal udo_done : std_logic := '0'; 52 | signal sim_done : std_logic := '0'; 53 | signal rand_int : integer := 0; 54 | signal count_rx : integer; 55 | signal count_tx : integer; 56 | 57 | procedure SPI_MASTER ( 58 | constant SPI_PER : time; 59 | signal SMM_MDI : in std_logic_vector(WORD_SIZE-1 downto 0); 60 | signal SMM_MDO : out std_logic_vector(WORD_SIZE-1 downto 0); 61 | signal SMM_SCLK : out std_logic; 62 | signal SMM_CS_N : out std_logic; 63 | signal SMM_MOSI : out std_logic; 64 | signal SMM_MISO : in std_logic 65 | ) is 66 | begin 67 | SMM_CS_N <= '0'; 68 | for i in 0 to (WORD_SIZE-1) loop 69 | SMM_SCLK <= '0'; 70 | SMM_MOSI <= SMM_MDI(WORD_SIZE-1-i); 71 | wait for SPI_PER/2; 72 | SMM_SCLK <= '1'; 73 | SMM_MDO(WORD_SIZE-1-i) <= SMM_MISO; 74 | wait for SPI_PER/2; 75 | end loop; 76 | SMM_SCLK <= '0'; 77 | wait for SPI_PER/2; 78 | SMM_CS_N <= '1'; 79 | end procedure; 80 | 81 | begin 82 | 83 | rand_int_p : process 84 | variable seed1, seed2: positive; 85 | variable rand : real; 86 | begin 87 | uniform(seed1, seed2, rand); 88 | rand_int <= integer(rand*real(20)); 89 | --report "Random number X: " & integer'image(rand_int); 90 | wait for CLK_PERIOD; 91 | if (sim_done = '1') then 92 | wait; 93 | end if; 94 | end process; 95 | 96 | dut : entity work.SPI_SLAVE 97 | generic map ( 98 | WORD_SIZE => WORD_SIZE 99 | ) 100 | port map ( 101 | CLK => CLK, 102 | RST => RST, 103 | -- SPI MASTER INTERFACE 104 | SCLK => sclk, 105 | CS_N => cs_n, 106 | MOSI => mosi, 107 | MISO => miso, 108 | -- USER INTERFACE 109 | DIN => udi, 110 | DIN_VLD => udi_vld, 111 | DIN_RDY => udi_rdy, 112 | DOUT => udo, 113 | DOUT_VLD => udo_vld 114 | ); 115 | 116 | clk_gen_p : process 117 | begin 118 | CLK <= '0'; 119 | wait for CLK_PERIOD/2; 120 | CLK <= '1'; 121 | wait for CLK_PERIOD/2; 122 | if (sim_done = '1') then 123 | wait; 124 | end if; 125 | end process; 126 | 127 | rst_gen_p : process 128 | begin 129 | report "======== SIMULATION START! ========"; 130 | report "Total transactions for master to slave direction: " & integer'image(TRANS_COUNT); 131 | report "Total transactions for slave to master direction: " & integer'image(TRANS_COUNT); 132 | RST <= '1'; 133 | wait for CLK_PERIOD*3; 134 | RST <= '0'; 135 | wait; 136 | end process; 137 | 138 | -- ------------------------------------------------------------------------- 139 | -- DUT TEST 140 | -- ------------------------------------------------------------------------- 141 | 142 | spi_master_model_p : process 143 | begin 144 | count_tx <= 1; 145 | cs_n <= '1'; 146 | sclk <= '0'; 147 | wait until RST = '0'; 148 | wait for 33 ns; 149 | for i in 0 to TRANS_COUNT-1 loop 150 | spi_mdi <= std_logic_vector(to_unsigned(((i+RX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); 151 | spi_mdo_exp <= std_logic_vector(to_unsigned(((i+TX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); 152 | wait for SPI_PERIOD/2; -- minimum idle time between transactions 153 | SPI_MASTER(SPI_PERIOD, spi_mdi, spi_mdo, sclk, cs_n, mosi, miso); 154 | if (spi_mdo = spi_mdo_exp) then 155 | if ((count_tx mod (TRANS_COUNT/10)) = 0) then 156 | report "Transactions received from slave: " & integer'image(count_tx); 157 | end if; 158 | else 159 | report "======== UNEXPECTED TRANSACTION ON MISO SIGNAL (slave to master)! ========" severity failure; 160 | end if; 161 | count_tx <= count_tx + 1; 162 | wait for (rand_int/2) * SPI_PERIOD; 163 | end loop; 164 | spi_model_done <= '1'; 165 | wait; 166 | end process; 167 | 168 | spi_slave_udi_p : process 169 | begin 170 | wait until RST = '0'; 171 | wait until rising_edge(CLK); 172 | wait for CLK_PERIOD/2; 173 | for i in 0 to TRANS_COUNT-1 loop 174 | udi <= std_logic_vector(to_unsigned(((i+TX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); 175 | udi_vld <= '1'; 176 | if (udi_rdy = '0') then 177 | wait until udi_rdy = '1'; 178 | wait for CLK_PERIOD/2; 179 | end if; 180 | wait for CLK_PERIOD; 181 | udi_vld <= '0'; 182 | --wait for rand_int*CLK_PERIOD; 183 | end loop; 184 | udi_done <= '1'; 185 | wait; 186 | end process; 187 | 188 | spi_slave_udo_p : process 189 | begin 190 | count_rx <= 1; 191 | for i in 0 to TRANS_COUNT-1 loop 192 | udo_exp <= std_logic_vector(to_unsigned(((i+RX_OFFSET) mod 2**WORD_SIZE),WORD_SIZE)); 193 | wait until udo_vld = '1'; 194 | if (udo = udo_exp) then 195 | if ((count_rx mod (TRANS_COUNT/10)) = 0) then 196 | report "Transactions received from master: " & integer'image(count_rx); 197 | end if; 198 | else 199 | report "======== UNEXPECTED TRANSACTION ON DOUT SIGNAL (master to slave)! ========" severity failure; 200 | end if; 201 | count_rx <= count_rx + 1; 202 | wait for CLK_PERIOD; 203 | end loop; 204 | udo_done <= '1'; 205 | wait; 206 | end process; 207 | 208 | -- ------------------------------------------------------------------------- 209 | -- TEST DONE CHECK 210 | -- ------------------------------------------------------------------------- 211 | 212 | test_done_p : process 213 | variable v_test_done : std_logic; 214 | begin 215 | v_test_done := spi_model_done and udi_done and udo_done; 216 | if (v_test_done = '1') then 217 | wait for 100*CLK_PERIOD; 218 | sim_done <= '1'; 219 | report "======== SIMULATION SUCCESSFULLY COMPLETED! ========"; 220 | wait; 221 | end if; 222 | wait for CLK_PERIOD; 223 | end process; 224 | 225 | end architecture; 226 | -------------------------------------------------------------------------------- /sim/spi_slave_tb_ghdl_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #------------------------------------------------------------------------------- 3 | # PROJECT: SPI MASTER AND SLAVE FOR FPGA 4 | #------------------------------------------------------------------------------- 5 | # AUTHORS: Jakub Cabal 6 | # LICENSE: The MIT License, please read LICENSE file 7 | # WEBSITE: https://github.com/jakubcabal/spi-fpga 8 | #------------------------------------------------------------------------------- 9 | 10 | # Setup simulation 11 | sh ./spi_slave_tb_ghdl_setup.sh 12 | 13 | # Run the simulation 14 | ghdl -r SPI_SLAVE_TB 15 | -------------------------------------------------------------------------------- /sim/spi_slave_tb_ghdl_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #------------------------------------------------------------------------------- 3 | # PROJECT: SPI MASTER AND SLAVE FOR FPGA 4 | #------------------------------------------------------------------------------- 5 | # AUTHORS: Jakub Cabal 6 | # LICENSE: The MIT License, please read LICENSE file 7 | # WEBSITE: https://github.com/jakubcabal/spi-fpga 8 | #------------------------------------------------------------------------------- 9 | 10 | # Analyse sources 11 | ghdl -a ../rtl/spi_slave.vhd 12 | ghdl -a ./spi_slave_tb.vhd 13 | 14 | # Elaborate the top-level 15 | ghdl -e SPI_SLAVE_TB 16 | 17 | # Run the simulation 18 | # The following command is allocated to a separate script due to CI purposes. 19 | #ghdl -r SPI_SLAVE_TB 20 | -------------------------------------------------------------------------------- /sim/spi_slave_tb_msim_run.tcl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # PROJECT: SPI MASTER AND SLAVE FOR FPGA 3 | #------------------------------------------------------------------------------- 4 | # AUTHORS: Jakub Cabal 5 | # LICENSE: The MIT License, please read LICENSE file 6 | # WEBSITE: https://github.com/jakubcabal/spi-fpga 7 | #------------------------------------------------------------------------------- 8 | 9 | # Create work library 10 | vlib work 11 | 12 | # Compile VHDL files 13 | vcom -93 ../rtl/spi_slave.vhd 14 | vcom -93 ./spi_slave_tb.vhd 15 | 16 | # Load testbench 17 | vsim work.spi_slave_tb 18 | 19 | # Setup and start simulation 20 | add wave sim:/spi_slave_tb/dut/* 21 | run -All 22 | --------------------------------------------------------------------------------