├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs ├── uart2wb.svg ├── uart_loopback.svg ├── user_din.svg └── user_dout.svg ├── examples ├── common │ └── rst_sync.vhd ├── loopback │ ├── quartus │ │ ├── uart_loopback_cyc1000.qpf │ │ ├── uart_loopback_cyc1000.qsf │ │ └── uart_loopback_cyc1000.sdc │ └── uart_loopback_cyc1000.vhd └── uart2wb │ ├── quartus │ ├── uart2wb_cyc1000.qpf │ ├── uart2wb_cyc1000.qsf │ └── uart2wb_cyc1000.sdc │ ├── sw │ └── wishbone.py │ ├── uart2wb_fpga_cyc1000.vhd │ └── uart2wbm.vhd ├── rtl ├── comp │ ├── uart_clk_div.vhd │ ├── uart_debouncer.vhd │ ├── uart_parity.vhd │ ├── uart_rx.vhd │ └── uart_tx.vhd └── uart.vhd └── sim ├── sim.tcl └── uart_tb.vhd /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog of Simple UART for FPGA 2 | 3 | **Version 1.3 - released on 10 April 2021** 4 | - Added better simulation with automatic checking of transactions. 5 | - Little code cleaning and code optimization. 6 | - Added UART2WB bridge example (access to WB registers via UART). 7 | - Added Parity Error output. 8 | 9 | **Version 1.2 - released on 23 December 2019** 10 | - Added double FF for safe CDC. 11 | - Fixed fake received transaction after FPGA boot without reset. 12 | - Added more precisely clock dividers, dividing with rounding. 13 | - UART loopback example is for CYC1000 board now. 14 | 15 | **Version 1.1 - released on 20 December 2018** 16 | - Added better debouncer. 17 | - Added simulation script and Quartus project file. 18 | - Removed unnecessary resets. 19 | - Signal BUSY replaced by DIN_RDY. 20 | - Many other optimizations and changes. 21 | 22 | **Version 1.0 - released on 27 May 2016** 23 | - Initial release. 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple UART for FPGA 2 | 3 | Simple UART for FPGA is UART (Universal Asynchronous Receiver & Transmitter) controller for serial communication with an FPGA. The UART controller was implemented using VHDL 93 and is applicable to any FPGA. 4 | 5 | **Simple UART for FPGA requires: 1 start bit, 8 data bits, 1 stop bit!** 6 | 7 | The UART controller was simulated and tested in hardware. 8 | 9 | ## UART controller: 10 | 11 | ### Generics: 12 | 13 | ``` 14 | CLK_FREQ : integer := 50e6; -- system clock frequency in Hz 15 | BAUD_RATE : integer := 115200; -- baud rate value 16 | PARITY_BIT : string := "none"; -- type of parity: "none", "even", "odd", "mark", "space" 17 | USE_DEBOUNCER : boolean := True -- enable/disable debouncer 18 | ``` 19 | 20 | ### Inputs and outputs ports: 21 | 22 | ``` 23 | -- CLOCK AND RESET 24 | CLK : in std_logic; -- system clock 25 | RST : in std_logic; -- high active synchronous reset 26 | -- UART INTERFACE 27 | UART_TXD : out std_logic; -- serial transmit data 28 | UART_RXD : in std_logic; -- serial receive data 29 | -- USER DATA INPUT INTERFACE 30 | DIN : in std_logic_vector(7 downto 0); -- input data to be transmitted over UART 31 | DIN_VLD : in std_logic; -- when DIN_VLD = 1, input data (DIN) are valid 32 | DIN_RDY : out std_logic -- when DIN_RDY = 1, transmitter is ready and valid input data will be accepted for transmiting 33 | -- USER DATA OUTPUT INTERFACE 34 | DOUT : out std_logic_vector(7 downto 0); -- output data received via UART 35 | DOUT_VLD : out std_logic; -- when DOUT_VLD = 1, output data (DOUT) are valid (is assert only for one clock cycle) 36 | FRAME_ERROR : out std_logic; -- when FRAME_ERROR = 1, stop bit was invalid (is assert only for one clock cycle) 37 | PARITY_ERROR : out std_logic -- when PARITY_ERROR = 1, parity bit was invalid (is assert only for one clock cycle) 38 | ``` 39 | 40 | ### User interface examples: 41 | 42 | Example of sending data on the user interface of the UART controller. 43 | 44 |  45 | 46 | Example of receiving data on the user interface of the UART controller. The last transaction in the example was corrupted during transmission, it contains a bad parity and stop bit. 47 | 48 |  49 | 50 | ### Table of resource usage summary: 51 | 52 | Parity type | LE | FF | M9k | Fmax 53 | :---:|:---:|:---:|:---:|:---: 54 | none | 77 | 56 | 0 | 305.5 MHz 55 | even/odd | 84 | 60 | 0 | 289.4 MHz 56 | mark/space | 82 | 60 | 0 | 290.7 MHz 57 | 58 | *Implementation was performed using Quartus Prime Lite Edition 20.1.0 for Intel Cyclone 10 FPGA (10CL025YU256C8G). Setting of some generics: USE_DEBOUNCER = True, BAUD_RATE = 115200, CLK_FREQ = 50e6.* 59 | 60 | ## Simulation: 61 | 62 | A simulation is prepared in the repository. You can use the prepared TCL script to run simulation in ModelSim. 63 | ``` 64 | vsim -do sim/sim.tcl 65 | ``` 66 | 67 | ## Examples: 68 | 69 | The repository also includes several UART example designs. I use it on my [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 FTDI USB to UART Bridge. 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). 70 | 71 | ### UART loopback: 72 | 73 | The UART loopback example design is for testing data transfer between FPGA and PC. Data that you send from the PC to the FPGA via UART will be automatically sent back to the PC. 74 | 75 |  76 | 77 | ### UART2WB bridge: 78 | 79 | The UART2WB bridge example design is for testing access to Wishbone registers via the UART bridge. The example uses a simple script written in Python that allows you to read or write to 32-bit user registers connected to the [Wishbone bus](http://cdn.opencores.org/downloads/wbspec_b4.pdf). 80 | 81 |  82 | 83 | After connecting the CYC1000 board to the PC, upload an example design to the FPGA and run the script ([Python 3](https://www.python.org) and [PySerial](https://pyserial.readthedocs.io/en/latest/shortintro.html) is required): 84 | ``` 85 | python examples/uart2wb/sw/wishbone.py 86 | ``` 87 | 88 | The expected output is: 89 | ``` 90 | Test of access to CSR (control status registers) via UART2WBM module... 91 | ======================================================================= 92 | The UART on COM1 is open. 93 | The wishbone bus is ready. 94 | 95 | READ from 0x0: 96 | 0x20210406 97 | 98 | READ from 0x4: 99 | 0xABCDEF12 100 | 101 | WRITE 0x12345678 to 0x4. 102 | 103 | READ from 0x4: 104 | 0x12345678 105 | 106 | WRITE 0xABCDEF12 to 0x4. 107 | 108 | READ from 0x4: 109 | 0xABCDEF12 110 | 111 | READ from 0x8844: 112 | 0xDEADCAFE 113 | 114 | The UART is closed. 115 | ``` 116 | 117 | ## License: 118 | 119 | This UART controller is available under the MIT license. Please read [LICENSE file](LICENSE). 120 | -------------------------------------------------------------------------------- /docs/uart2wb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | CYC1000 boardCYC1000 boardUSB2UARTUSB2UARTFPGAFPGAUARTUARTPC(Wishbone registers are accessible using wishbone.py script)PC...UART2WBMUART2WBMWishbone user registersWishbone user regi...Viewer does not support full SVG 1.1 -------------------------------------------------------------------------------- /docs/uart_loopback.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | CYC1000 boardCYC1000 boardUSB2UARTUSB2UARTFPGAFPGAUARTUARTUART_RXUART_RXUART_TXUART_TXPCPCViewer does not support full SVG 1.1 -------------------------------------------------------------------------------- /docs/user_din.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CLKDIND0D1D2DIN_VLDDIN_RDY -------------------------------------------------------------------------------- /docs/user_dout.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CLKDOUTD0D1D2DOUT_VLDFRAME_ERRORPARITY_ERROR -------------------------------------------------------------------------------- /examples/common/rst_sync.vhd: -------------------------------------------------------------------------------- 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 | 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/loopback/quartus/uart_loopback_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/loopback/quartus/uart_loopback_cyc1000.qsf: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # PROJECT: SIMPLE UART FOR FPGA 3 | #------------------------------------------------------------------------------- 4 | # AUTHORS: Jakub Cabal 5 | # LICENSE: The MIT License (MIT), please read LICENSE file 6 | # WEBSITE: https://github.com/jakubcabal/uart-for-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 UART_LOOPBACK_CYC1000 13 | 14 | # PROJECT VHDL FILES 15 | set_global_assignment -name VHDL_FILE ../../../rtl/comp/uart_clk_div.vhd 16 | set_global_assignment -name VHDL_FILE ../../../rtl/comp/uart_parity.vhd 17 | set_global_assignment -name VHDL_FILE ../../../rtl/comp/uart_debouncer.vhd 18 | set_global_assignment -name VHDL_FILE ../../../rtl/comp/uart_tx.vhd 19 | set_global_assignment -name VHDL_FILE ../../../rtl/comp/uart_rx.vhd 20 | set_global_assignment -name VHDL_FILE ../../../rtl/uart.vhd 21 | set_global_assignment -name VHDL_FILE ../../common/rst_sync.vhd 22 | set_global_assignment -name VHDL_FILE ../uart_loopback_cyc1000.vhd 23 | 24 | # TIMING CONSTRAINTS 25 | set_global_assignment -name SDC_FILE ./uart_loopback_cyc1000.sdc 26 | 27 | # FPGA PINS ASSIGNMENT 28 | set_location_assignment PIN_M2 -to CLK_12M 29 | set_location_assignment PIN_N6 -to RST_BTN_N 30 | set_location_assignment PIN_T7 -to UART_TXD 31 | set_location_assignment PIN_R7 -to UART_RXD 32 | -------------------------------------------------------------------------------- /examples/loopback/quartus/uart_loopback_cyc1000.sdc: -------------------------------------------------------------------------------- 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 | create_clock -name CLK12M -period 12MHz [get_ports {CLK_12M}] 10 | -------------------------------------------------------------------------------- /examples/loopback/uart_loopback_cyc1000.vhd: -------------------------------------------------------------------------------- 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 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | 13 | -- UART LOOPBACK EXAMPLE TOP MODULE FOR CYC1000 BOARD 14 | -- ================================================== 15 | -- UART FOR FPGA REQUIRES: 1 START BIT, 8 DATA BITS, 1 STOP BIT!!! 16 | -- OTHER PARAMETERS CAN BE SET USING GENERICS. 17 | 18 | entity UART_LOOPBACK_CYC1000 is 19 | Generic ( 20 | CLK_FREQ : integer := 12e6; -- set system clock frequency in Hz 21 | BAUD_RATE : integer := 115200; -- baud rate value 22 | PARITY_BIT : string := "none"; -- legal values: "none", "even", "odd", "mark", "space" 23 | USE_DEBOUNCER : boolean := True -- enable/disable debouncer 24 | ); 25 | Port ( 26 | CLK_12M : in std_logic; -- system clock 12 MHz 27 | RST_BTN_N : in std_logic; -- low active reset button 28 | -- UART INTERFACE 29 | UART_TXD : out std_logic; 30 | UART_RXD : in std_logic 31 | ); 32 | end entity; 33 | 34 | architecture RTL of UART_LOOPBACK_CYC1000 is 35 | 36 | signal rst_btn : std_logic; 37 | signal reset : std_logic; 38 | signal data : std_logic_vector(7 downto 0); 39 | signal valid : std_logic; 40 | 41 | begin 42 | 43 | rst_btn <= not RST_BTN_N; 44 | 45 | rst_sync_i : entity work.RST_SYNC 46 | port map ( 47 | CLK => CLK_12M, 48 | ASYNC_RST => rst_btn, 49 | SYNCED_RST => reset 50 | ); 51 | 52 | uart_i: entity work.UART 53 | generic map ( 54 | CLK_FREQ => CLK_FREQ, 55 | BAUD_RATE => BAUD_RATE, 56 | PARITY_BIT => PARITY_BIT, 57 | USE_DEBOUNCER => USE_DEBOUNCER 58 | ) 59 | port map ( 60 | CLK => CLK_12M, 61 | RST => reset, 62 | -- UART INTERFACE 63 | UART_TXD => UART_TXD, 64 | UART_RXD => UART_RXD, 65 | -- USER DATA INPUT INTERFACE 66 | DIN => data, 67 | DIN_VLD => valid, 68 | DIN_RDY => open, 69 | -- USER DATA OUTPUT INTERFACE 70 | DOUT => data, 71 | DOUT_VLD => valid, 72 | FRAME_ERROR => open, 73 | PARITY_ERROR => open 74 | ); 75 | 76 | end architecture; 77 | -------------------------------------------------------------------------------- /examples/uart2wb/quartus/uart2wb_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 = "UART2WB_CYC1000" 10 | -------------------------------------------------------------------------------- /examples/uart2wb/quartus/uart2wb_cyc1000.qsf: -------------------------------------------------------------------------------- 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 | # 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 UART2WB_FPGA_CYC1000 13 | 14 | # PROJECT VHDL FILES 15 | set_global_assignment -name VHDL_FILE ../../../rtl/comp/uart_clk_div.vhd 16 | set_global_assignment -name VHDL_FILE ../../../rtl/comp/uart_parity.vhd 17 | set_global_assignment -name VHDL_FILE ../../../rtl/comp/uart_debouncer.vhd 18 | set_global_assignment -name VHDL_FILE ../../../rtl/comp/uart_tx.vhd 19 | set_global_assignment -name VHDL_FILE ../../../rtl/comp/uart_rx.vhd 20 | set_global_assignment -name VHDL_FILE ../../../rtl/uart.vhd 21 | set_global_assignment -name VHDL_FILE ../../common/rst_sync.vhd 22 | set_global_assignment -name VHDL_FILE ../uart2wbm.vhd 23 | set_global_assignment -name VHDL_FILE ../uart2wb_fpga_cyc1000.vhd 24 | 25 | # TIMING CONSTRAINTS 26 | set_global_assignment -name SDC_FILE ./uart2wb_cyc1000.sdc 27 | 28 | # FPGA PINS ASSIGNMENT 29 | set_location_assignment PIN_M2 -to CLK_12M 30 | set_location_assignment PIN_N6 -to RST_BTN_N 31 | set_location_assignment PIN_T7 -to UART_TXD 32 | set_location_assignment PIN_R7 -to UART_RXD 33 | -------------------------------------------------------------------------------- /examples/uart2wb/quartus/uart2wb_cyc1000.sdc: -------------------------------------------------------------------------------- 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 | create_clock -name CLK12M -period 12MHz [get_ports {CLK_12M}] 10 | -------------------------------------------------------------------------------- /examples/uart2wb/sw/wishbone.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #------------------------------------------------------------------------------- 3 | # PROJECT: SIMPLE UART FOR FPGA 4 | #------------------------------------------------------------------------------- 5 | # AUTHORS: Jakub Cabal 6 | # LICENSE: The MIT License, please read LICENSE file 7 | # WEBSITE: https://github.com/jakubcabal/uart-for-fpga 8 | #------------------------------------------------------------------------------- 9 | 10 | import serial 11 | 12 | byteorder="little" 13 | 14 | class wishbone: 15 | def __init__(self, port="COM1", baudrate=9600): 16 | self.uart = serial.Serial(port, baudrate, timeout=2) 17 | print("The UART on " + self.uart.name + " is open.") 18 | print("The wishbone bus is ready.\n") 19 | 20 | def read(self,addr): 21 | cmd = 0x0 22 | cmd = cmd.to_bytes(1,byteorder) 23 | self.uart.write(cmd) 24 | addr = addr.to_bytes(2,byteorder) 25 | self.uart.write(addr) 26 | rbytes=self.uart.read(1) 27 | rbytes=self.uart.read(4) 28 | drd=int.from_bytes(rbytes,byteorder) 29 | return drd 30 | 31 | def write(self, addr, data): 32 | cmd = 0x1 33 | cmd = cmd.to_bytes(1,byteorder) 34 | self.uart.write(cmd) 35 | addr = addr.to_bytes(2,byteorder) 36 | self.uart.write(addr) 37 | data = data.to_bytes(4,byteorder) 38 | self.uart.write(data) 39 | rbytes=self.uart.read(1) 40 | 41 | def close(self): 42 | self.uart.close() 43 | 44 | if __name__ == '__main__': 45 | print("Test of access to CSR (control status registers) via UART2WBM module...") 46 | print("=======================================================================") 47 | wb = wishbone("COM1") 48 | 49 | print("\nREAD from 0x0:") 50 | rd = wb.read(0x0) 51 | print("0x%02X" % rd) 52 | 53 | print("\nREAD from 0x4:") 54 | rd = wb.read(0x4) 55 | print("0x%02X" % rd) 56 | 57 | print("\nWRITE 0x12345678 to 0x4.") 58 | wb.write(0x4,0x12345678) 59 | 60 | print("\nREAD from 0x4:") 61 | rd = wb.read(0x4) 62 | print("0x%02X" % rd) 63 | 64 | print("\nWRITE 0xABCDEF12 to 0x4.") 65 | wb.write(0x4,0xABCDEF12) 66 | 67 | print("\nREAD from 0x4:") 68 | rd = wb.read(0x4) 69 | print("0x%02X" % rd) 70 | 71 | print("\nREAD from 0x8844:") 72 | rd = wb.read(0x8844) 73 | print("0x%02X" % rd) 74 | 75 | wb.close() 76 | print("\nThe UART is closed.") 77 | -------------------------------------------------------------------------------- /examples/uart2wb/uart2wb_fpga_cyc1000.vhd: -------------------------------------------------------------------------------- 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 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | 13 | -- TOP MODULE OF UART 2 WISHBONE EXAMPLE FOR CYC1000 BOARD 14 | -- ======================================================= 15 | 16 | entity UART2WB_FPGA_CYC1000 is 17 | Port ( 18 | CLK_12M : in std_logic; -- system clock 12 MHz 19 | RST_BTN_N : in std_logic; -- low active reset button 20 | -- UART INTERFACE 21 | UART_RXD : in std_logic; 22 | UART_TXD : out std_logic 23 | ); 24 | end entity; 25 | 26 | architecture RTL of UART2WB_FPGA_CYC1000 is 27 | 28 | signal rst_btn : std_logic; 29 | signal reset : std_logic; 30 | 31 | signal wb_cyc : std_logic; 32 | signal wb_stb : std_logic; 33 | signal wb_we : std_logic; 34 | signal wb_addr : std_logic_vector(15 downto 0); 35 | signal wb_dout : std_logic_vector(31 downto 0); 36 | signal wb_stall : std_logic; 37 | signal wb_ack : std_logic; 38 | signal wb_din : std_logic_vector(31 downto 0); 39 | 40 | signal debug_reg_sel : std_logic; 41 | signal debug_reg_we : std_logic; 42 | signal debug_reg : std_logic_vector(31 downto 0); 43 | 44 | begin 45 | 46 | rst_btn <= not RST_BTN_N; 47 | 48 | rst_sync_i : entity work.RST_SYNC 49 | port map ( 50 | CLK => CLK_12M, 51 | ASYNC_RST => rst_btn, 52 | SYNCED_RST => reset 53 | ); 54 | 55 | uart2wbm_i : entity work.UART2WBM 56 | generic map ( 57 | CLK_FREQ => 12e6, 58 | BAUD_RATE => 9600 59 | ) 60 | port map ( 61 | CLK => CLK_12M, 62 | RST => reset, 63 | -- UART INTERFACE 64 | UART_TXD => UART_TXD, 65 | UART_RXD => UART_RXD, 66 | -- WISHBONE MASTER INTERFACE 67 | WB_CYC => wb_cyc, 68 | WB_STB => wb_stb, 69 | WB_WE => wb_we, 70 | WB_ADDR => wb_addr, 71 | WB_DOUT => wb_din, 72 | WB_STALL => wb_stall, 73 | WB_ACK => wb_ack, 74 | WB_DIN => wb_dout 75 | ); 76 | 77 | debug_reg_sel <= '1' when (wb_addr = X"0004") else '0'; 78 | debug_reg_we <= wb_stb and wb_we and debug_reg_sel; 79 | 80 | debug_reg_p : process (CLK_12M) 81 | begin 82 | if (rising_edge(CLK_12M)) then 83 | if (debug_reg_we = '1') then 84 | debug_reg <= wb_din; 85 | end if; 86 | end if; 87 | end process; 88 | 89 | wb_stall <= '0'; 90 | 91 | wb_ack_reg_p : process (CLK_12M) 92 | begin 93 | if (rising_edge(CLK_12M)) then 94 | wb_ack <= wb_cyc and wb_stb; 95 | end if; 96 | end process; 97 | 98 | wb_dout_reg_p : process (CLK_12M) 99 | begin 100 | if (rising_edge(CLK_12M)) then 101 | case wb_addr is 102 | when X"0000" => 103 | wb_dout <= X"20210406"; 104 | when X"0004" => 105 | wb_dout <= debug_reg; 106 | when others => 107 | wb_dout <= X"DEADCAFE"; 108 | end case; 109 | end if; 110 | end process; 111 | 112 | end architecture; 113 | -------------------------------------------------------------------------------- /examples/uart2wb/uart2wbm.vhd: -------------------------------------------------------------------------------- 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 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | 13 | entity UART2WBM is 14 | Generic ( 15 | CLK_FREQ : integer := 50e6; -- system clock frequency in Hz 16 | BAUD_RATE : integer := 115200 -- baud rate value 17 | ); 18 | Port ( 19 | -- CLOCK AND RESET 20 | CLK : in std_logic; 21 | RST : in std_logic; 22 | -- UART INTERFACE 23 | UART_TXD : out std_logic; 24 | UART_RXD : in std_logic; 25 | -- WISHBONE MASTER INTERFACE 26 | WB_CYC : out std_logic; 27 | WB_STB : out std_logic; 28 | WB_WE : out std_logic; 29 | WB_ADDR : out std_logic_vector(15 downto 0); 30 | WB_DOUT : out std_logic_vector(31 downto 0); 31 | WB_STALL : in std_logic; 32 | WB_ACK : in std_logic; 33 | WB_DIN : in std_logic_vector(31 downto 0) 34 | ); 35 | end entity; 36 | 37 | architecture RTL of UART2WBM is 38 | 39 | type state is (cmd, addr_low, addr_high, dout0, dout1, dout2, dout3, 40 | request, wait4ack, response, din0, din1, din2, din3); 41 | signal fsm_pstate : state; 42 | signal fsm_nstate : state; 43 | 44 | signal cmd_reg : std_logic_vector(7 downto 0); 45 | signal cmd_next : std_logic_vector(7 downto 0); 46 | signal addr_reg : std_logic_vector(15 downto 0); 47 | signal addr_next : std_logic_vector(15 downto 0); 48 | signal dout_reg : std_logic_vector(31 downto 0); 49 | signal dout_next : std_logic_vector(31 downto 0); 50 | signal din_reg : std_logic_vector(31 downto 0); 51 | 52 | signal uart_dout : std_logic_vector(7 downto 0); 53 | signal uart_dout_vld : std_logic; 54 | signal uart_din : std_logic_vector(7 downto 0); 55 | signal uart_din_vld : std_logic; 56 | signal uart_din_rdy : std_logic; 57 | 58 | begin 59 | 60 | process (CLK) 61 | begin 62 | if (rising_edge(CLK)) then 63 | cmd_reg <= cmd_next; 64 | addr_reg <= addr_next; 65 | dout_reg <= dout_next; 66 | end if; 67 | end process; 68 | 69 | WB_WE <= cmd_reg(0); 70 | WB_ADDR <= addr_reg; 71 | WB_DOUT <= dout_reg; 72 | 73 | process (CLK) 74 | begin 75 | if (rising_edge(CLK)) then 76 | if (WB_ACK = '1') then 77 | din_reg <= WB_DIN; 78 | end if; 79 | end if; 80 | end process; 81 | 82 | -- ------------------------------------------------------------------------- 83 | -- FSM 84 | -- ------------------------------------------------------------------------- 85 | 86 | process (CLK) 87 | begin 88 | if (rising_edge(CLK)) then 89 | if (RST = '1') then 90 | fsm_pstate <= cmd; 91 | else 92 | fsm_pstate <= fsm_nstate; 93 | end if; 94 | end if; 95 | end process; 96 | 97 | process (fsm_pstate, uart_dout, uart_dout_vld, cmd_reg, addr_reg, dout_reg, 98 | WB_STALL, WB_ACK, uart_din_rdy, din_reg) 99 | begin 100 | fsm_nstate <= cmd; 101 | cmd_next <= cmd_reg; 102 | addr_next <= addr_reg; 103 | dout_next <= dout_reg; 104 | WB_STB <= '0'; 105 | WB_CYC <= '0'; 106 | uart_din <= cmd_reg; 107 | uart_din_vld <= '0'; 108 | 109 | case fsm_pstate is 110 | when cmd => -- idle and read request cmd from UART 111 | cmd_next <= uart_dout; 112 | 113 | if (uart_dout_vld = '1') then 114 | fsm_nstate <= addr_low; 115 | else 116 | fsm_nstate <= cmd; 117 | end if; 118 | 119 | when addr_low => -- read low bits of address from UART 120 | addr_next(7 downto 0) <= uart_dout; 121 | 122 | if (uart_dout_vld = '1') then 123 | fsm_nstate <= addr_high; 124 | else 125 | fsm_nstate <= addr_low; 126 | end if; 127 | 128 | when addr_high => -- read high bits of address from UART 129 | addr_next(15 downto 8) <= uart_dout; 130 | 131 | if (uart_dout_vld = '1') then 132 | if (cmd_reg(0) = '1') then 133 | fsm_nstate <= dout0; -- write cmd 134 | else 135 | fsm_nstate <= request; -- read cmd 136 | end if; 137 | else 138 | fsm_nstate <= addr_high; 139 | end if; 140 | 141 | when dout0 => -- read data byte 0 from UART (write cmd only) 142 | dout_next(7 downto 0) <= uart_dout; 143 | 144 | if (uart_dout_vld = '1') then 145 | fsm_nstate <= dout1; 146 | else 147 | fsm_nstate <= dout0; 148 | end if; 149 | 150 | when dout1 => -- read data byte 1 from UART (write cmd only) 151 | dout_next(15 downto 8) <= uart_dout; 152 | 153 | if (uart_dout_vld = '1') then 154 | fsm_nstate <= dout2; 155 | else 156 | fsm_nstate <= dout1; 157 | end if; 158 | 159 | when dout2 => -- read data byte 2 from UART (write cmd only) 160 | dout_next(23 downto 16) <= uart_dout; 161 | 162 | if (uart_dout_vld = '1') then 163 | fsm_nstate <= dout3; 164 | else 165 | fsm_nstate <= dout2; 166 | end if; 167 | 168 | when dout3 => -- read data byte 3 from UART (write cmd only) 169 | dout_next(31 downto 24) <= uart_dout; 170 | 171 | if (uart_dout_vld = '1') then 172 | fsm_nstate <= request; -- write request 173 | else 174 | fsm_nstate <= dout3; 175 | end if; 176 | 177 | when request => -- send WR or RD request to Wishbone bus 178 | WB_STB <= '1'; -- request is valid 179 | WB_CYC <= '1'; 180 | 181 | if (WB_STALL = '0') then 182 | fsm_nstate <= wait4ack; 183 | else 184 | fsm_nstate <= request; 185 | end if; 186 | 187 | when wait4ack => -- wait for ACK on Wishbone bus 188 | WB_CYC <= '1'; 189 | 190 | if (WB_ACK = '1') then 191 | fsm_nstate <= response; 192 | else 193 | fsm_nstate <= wait4ack; 194 | end if; 195 | 196 | when response => -- send response cmd to UART 197 | uart_din <= cmd_reg; 198 | uart_din_vld <= '1'; 199 | 200 | if (uart_din_rdy = '1') then 201 | if (cmd_reg(0) = '1') then 202 | fsm_nstate <= cmd; -- idle or new read request cmd (write cmd only) 203 | else 204 | fsm_nstate <= din0; -- send read data to UART (read cmd only) 205 | end if; 206 | else 207 | fsm_nstate <= response; 208 | end if; 209 | 210 | when din0 => -- send read data byte 0 to UART (read cmd only) 211 | uart_din <= din_reg(7 downto 0); 212 | uart_din_vld <= '1'; 213 | 214 | if (uart_din_rdy = '1') then 215 | fsm_nstate <= din1; 216 | else 217 | fsm_nstate <= din0; 218 | end if; 219 | 220 | when din1 => -- send read data byte 1 to UART (read cmd only) 221 | uart_din <= din_reg(15 downto 8); 222 | uart_din_vld <= '1'; 223 | 224 | if (uart_din_rdy = '1') then 225 | fsm_nstate <= din2; 226 | else 227 | fsm_nstate <= din1; 228 | end if; 229 | 230 | when din2 => -- send read data byte 2 to UART (read cmd only) 231 | uart_din <= din_reg(23 downto 16); 232 | uart_din_vld <= '1'; 233 | 234 | if (uart_din_rdy = '1') then 235 | fsm_nstate <= din3; 236 | else 237 | fsm_nstate <= din2; 238 | end if; 239 | 240 | when din3 => -- send read data byte 3 to UART (read cmd only) 241 | uart_din <= din_reg(31 downto 24); 242 | uart_din_vld <= '1'; 243 | 244 | if (uart_din_rdy = '1') then 245 | fsm_nstate <= cmd; 246 | else 247 | fsm_nstate <= din3; 248 | end if; 249 | 250 | end case; 251 | end process; 252 | 253 | -- ------------------------------------------------------------------------- 254 | -- UART module 255 | -- ------------------------------------------------------------------------- 256 | 257 | uart_i : entity work.UART 258 | generic map ( 259 | CLK_FREQ => CLK_FREQ, 260 | BAUD_RATE => BAUD_RATE, 261 | PARITY_BIT => "none", 262 | USE_DEBOUNCER => True 263 | ) 264 | port map ( 265 | CLK => CLK, 266 | RST => RST, 267 | -- UART INTERFACE 268 | UART_TXD => UART_TXD, 269 | UART_RXD => UART_RXD, 270 | -- USER DATA INPUT INTERFACE 271 | DIN => uart_din, 272 | DIN_VLD => uart_din_vld, 273 | DIN_RDY => uart_din_rdy, 274 | -- USER DATA OUTPUT INTERFACE 275 | DOUT => uart_dout, 276 | DOUT_VLD => uart_dout_vld, 277 | FRAME_ERROR => open, 278 | PARITY_ERROR => open 279 | ); 280 | 281 | end architecture; 282 | -------------------------------------------------------------------------------- /rtl/comp/uart_clk_div.vhd: -------------------------------------------------------------------------------- 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 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | use IEEE.MATH_REAL.ALL; 13 | 14 | entity UART_CLK_DIV is 15 | Generic ( 16 | DIV_MAX_VAL : integer := 16; 17 | DIV_MARK_POS : integer := 1 18 | ); 19 | Port ( 20 | CLK : in std_logic; -- system clock 21 | RST : in std_logic; -- high active synchronous reset 22 | -- USER INTERFACE 23 | CLEAR : in std_logic; -- clock divider counter clear 24 | ENABLE : in std_logic; -- clock divider counter enable 25 | DIV_MARK : out std_logic -- output divider mark (divided clock enable) 26 | ); 27 | end entity; 28 | 29 | architecture RTL of UART_CLK_DIV is 30 | 31 | constant CLK_DIV_WIDTH : integer := integer(ceil(log2(real(DIV_MAX_VAL)))); 32 | 33 | signal clk_div_cnt : unsigned(CLK_DIV_WIDTH-1 downto 0); 34 | signal clk_div_cnt_mark : std_logic; 35 | 36 | begin 37 | 38 | clk_div_cnt_p : process (CLK) 39 | begin 40 | if (rising_edge(CLK)) then 41 | if (CLEAR = '1') then 42 | clk_div_cnt <= (others => '0'); 43 | elsif (ENABLE = '1') then 44 | if (clk_div_cnt = DIV_MAX_VAL-1) then 45 | clk_div_cnt <= (others => '0'); 46 | else 47 | clk_div_cnt <= clk_div_cnt + 1; 48 | end if; 49 | end if; 50 | end if; 51 | end process; 52 | 53 | clk_div_cnt_mark <= '1' when (clk_div_cnt = DIV_MARK_POS) else '0'; 54 | 55 | div_mark_p : process (CLK) 56 | begin 57 | if (rising_edge(CLK)) then 58 | DIV_MARK <= ENABLE and clk_div_cnt_mark; 59 | end if; 60 | end process; 61 | 62 | end architecture; 63 | -------------------------------------------------------------------------------- /rtl/comp/uart_debouncer.vhd: -------------------------------------------------------------------------------- 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 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | 13 | entity UART_DEBOUNCER is 14 | Generic ( 15 | -- latency of debouncer in clock cycles, minimum value is 2, 16 | -- value also corresponds to the number of bits compared 17 | LATENCY : natural := 4 18 | ); 19 | Port ( 20 | CLK : in std_logic; -- system clock 21 | DEB_IN : in std_logic; -- input of signal from outside FPGA 22 | DEB_OUT : out std_logic -- output of debounced (filtered) signal 23 | ); 24 | end entity; 25 | 26 | architecture RTL of UART_DEBOUNCER is 27 | 28 | constant SHREG_DEPTH : natural := LATENCY-1; 29 | 30 | signal input_shreg : std_logic_vector(SHREG_DEPTH-1 downto 0); 31 | signal output_reg_rst : std_logic; 32 | signal output_reg_set : std_logic; 33 | 34 | begin 35 | 36 | -- parameterized input shift register 37 | input_shreg_p : process (CLK) 38 | begin 39 | if (rising_edge(CLK)) then 40 | input_shreg <= input_shreg(SHREG_DEPTH-2 downto 0) & DEB_IN; 41 | end if; 42 | end process; 43 | 44 | -- output register will be reset when all compared bits are low 45 | output_reg_rst_p : process (DEB_IN, input_shreg) 46 | variable or_var : std_logic; 47 | begin 48 | or_var := DEB_IN; 49 | all_bits_or_l : for i in 0 to SHREG_DEPTH-1 loop 50 | or_var := or_var or input_shreg(i); 51 | end loop; 52 | output_reg_rst <= not or_var; 53 | end process; 54 | 55 | -- output register will be set when all compared bits are high 56 | output_reg_set_p : process (DEB_IN, input_shreg) 57 | variable and_var : std_logic; 58 | begin 59 | and_var := DEB_IN; 60 | all_bits_and_l : for i in 0 to SHREG_DEPTH-1 loop 61 | and_var := and_var and input_shreg(i); 62 | end loop; 63 | output_reg_set <= and_var; 64 | end process; 65 | 66 | -- output register 67 | output_reg_p : process (CLK) 68 | begin 69 | if (rising_edge(CLK)) then 70 | if (output_reg_rst = '1') then 71 | DEB_OUT <= '0'; 72 | elsif (output_reg_set = '1') then 73 | DEB_OUT <= '1'; 74 | end if; 75 | end if; 76 | end process; 77 | 78 | end architecture; 79 | -------------------------------------------------------------------------------- /rtl/comp/uart_parity.vhd: -------------------------------------------------------------------------------- 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 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | 13 | entity UART_PARITY is 14 | Generic ( 15 | DATA_WIDTH : integer := 8; 16 | PARITY_TYPE : string := "none" -- legal values: "none", "even", "odd", "mark", "space" 17 | ); 18 | Port ( 19 | DATA_IN : in std_logic_vector(DATA_WIDTH-1 downto 0); 20 | PARITY_OUT : out std_logic 21 | ); 22 | end entity; 23 | 24 | architecture RTL of UART_PARITY is 25 | 26 | begin 27 | 28 | -- ------------------------------------------------------------------------- 29 | -- PARITY BIT GENERATOR 30 | -- ------------------------------------------------------------------------- 31 | 32 | even_parity_g : if (PARITY_TYPE = "even") generate 33 | process (DATA_IN) 34 | variable parity_temp : std_logic; 35 | begin 36 | parity_temp := '0'; 37 | for i in DATA_IN'range loop 38 | parity_temp := parity_temp XOR DATA_IN(i); 39 | end loop; 40 | PARITY_OUT <= parity_temp; 41 | end process; 42 | end generate; 43 | 44 | odd_parity_g : if (PARITY_TYPE = "odd") generate 45 | process (DATA_IN) 46 | variable parity_temp : std_logic; 47 | begin 48 | parity_temp := '1'; 49 | for i in DATA_IN'range loop 50 | parity_temp := parity_temp XOR DATA_IN(i); 51 | end loop; 52 | PARITY_OUT <= parity_temp; 53 | end process; 54 | end generate; 55 | 56 | mark_parity_g : if (PARITY_TYPE = "mark") generate 57 | PARITY_OUT <= '1'; 58 | end generate; 59 | 60 | space_parity_g : if (PARITY_TYPE = "space") generate 61 | PARITY_OUT <= '0'; 62 | end generate; 63 | 64 | end architecture; 65 | -------------------------------------------------------------------------------- /rtl/comp/uart_rx.vhd: -------------------------------------------------------------------------------- 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 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | 13 | entity UART_RX is 14 | Generic ( 15 | CLK_DIV_VAL : integer := 16; 16 | PARITY_BIT : string := "none" -- type of parity: "none", "even", "odd", "mark", "space" 17 | ); 18 | Port ( 19 | CLK : in std_logic; -- system clock 20 | RST : in std_logic; -- high active synchronous reset 21 | -- UART INTERFACE 22 | UART_CLK_EN : in std_logic; -- oversampling (16x) UART clock enable 23 | UART_RXD : in std_logic; -- serial receive data 24 | -- USER DATA OUTPUT INTERFACE 25 | DOUT : out std_logic_vector(7 downto 0); -- output data received via UART 26 | DOUT_VLD : out std_logic; -- when DOUT_VLD = 1, output data (DOUT) are valid without errors (is assert only for one clock cycle) 27 | FRAME_ERROR : out std_logic; -- when FRAME_ERROR = 1, stop bit was invalid (is assert only for one clock cycle) 28 | PARITY_ERROR : out std_logic -- when PARITY_ERROR = 1, parity bit was invalid (is assert only for one clock cycle) 29 | ); 30 | end entity; 31 | 32 | architecture RTL of UART_RX is 33 | 34 | signal rx_clk_en : std_logic; 35 | signal rx_data : std_logic_vector(7 downto 0); 36 | signal rx_bit_count : unsigned(2 downto 0); 37 | signal rx_parity_bit : std_logic; 38 | signal rx_parity_error : std_logic; 39 | signal rx_parity_check_en : std_logic; 40 | signal rx_done : std_logic; 41 | signal fsm_idle : std_logic; 42 | signal fsm_databits : std_logic; 43 | signal fsm_stopbit : std_logic; 44 | 45 | type state is (idle, startbit, databits, paritybit, stopbit); 46 | signal fsm_pstate : state; 47 | signal fsm_nstate : state; 48 | 49 | begin 50 | 51 | -- ------------------------------------------------------------------------- 52 | -- UART RECEIVER CLOCK DIVIDER AND CLOCK ENABLE FLAG 53 | -- ------------------------------------------------------------------------- 54 | 55 | rx_clk_divider_i : entity work.UART_CLK_DIV 56 | generic map( 57 | DIV_MAX_VAL => CLK_DIV_VAL, 58 | DIV_MARK_POS => 3 59 | ) 60 | port map ( 61 | CLK => CLK, 62 | RST => RST, 63 | CLEAR => fsm_idle, 64 | ENABLE => UART_CLK_EN, 65 | DIV_MARK => rx_clk_en 66 | ); 67 | 68 | -- ------------------------------------------------------------------------- 69 | -- UART RECEIVER BIT COUNTER 70 | -- ------------------------------------------------------------------------- 71 | 72 | uart_rx_bit_counter_p : process (CLK) 73 | begin 74 | if (rising_edge(CLK)) then 75 | if (RST = '1') then 76 | rx_bit_count <= (others => '0'); 77 | elsif (rx_clk_en = '1' AND fsm_databits = '1') then 78 | if (rx_bit_count = "111") then 79 | rx_bit_count <= (others => '0'); 80 | else 81 | rx_bit_count <= rx_bit_count + 1; 82 | end if; 83 | end if; 84 | end if; 85 | end process; 86 | 87 | -- ------------------------------------------------------------------------- 88 | -- UART RECEIVER DATA SHIFT REGISTER 89 | -- ------------------------------------------------------------------------- 90 | 91 | uart_rx_data_shift_reg_p : process (CLK) 92 | begin 93 | if (rising_edge(CLK)) then 94 | if (rx_clk_en = '1' AND fsm_databits = '1') then 95 | rx_data <= UART_RXD & rx_data(7 downto 1); 96 | end if; 97 | end if; 98 | end process; 99 | 100 | DOUT <= rx_data; 101 | 102 | -- ------------------------------------------------------------------------- 103 | -- UART RECEIVER PARITY GENERATOR AND CHECK 104 | -- ------------------------------------------------------------------------- 105 | 106 | uart_rx_parity_g : if (PARITY_BIT /= "none") generate 107 | uart_rx_parity_gen_i: entity work.UART_PARITY 108 | generic map ( 109 | DATA_WIDTH => 8, 110 | PARITY_TYPE => PARITY_BIT 111 | ) 112 | port map ( 113 | DATA_IN => rx_data, 114 | PARITY_OUT => rx_parity_bit 115 | ); 116 | 117 | uart_rx_parity_check_reg_p : process (CLK) 118 | begin 119 | if (rising_edge(CLK)) then 120 | if (rx_clk_en = '1') then 121 | rx_parity_error <= rx_parity_bit XOR UART_RXD; 122 | end if; 123 | end if; 124 | end process; 125 | end generate; 126 | 127 | uart_rx_noparity_g : if (PARITY_BIT = "none") generate 128 | rx_parity_error <= '0'; 129 | end generate; 130 | 131 | -- ------------------------------------------------------------------------- 132 | -- UART RECEIVER OUTPUT REGISTER 133 | -- ------------------------------------------------------------------------- 134 | 135 | rx_done <= rx_clk_en and fsm_stopbit; 136 | 137 | uart_rx_output_reg_p : process (CLK) 138 | begin 139 | if (rising_edge(CLK)) then 140 | if (RST = '1') then 141 | DOUT_VLD <= '0'; 142 | FRAME_ERROR <= '0'; 143 | PARITY_ERROR <= '0'; 144 | else 145 | DOUT_VLD <= rx_done and not rx_parity_error and UART_RXD; 146 | FRAME_ERROR <= rx_done and not UART_RXD; 147 | PARITY_ERROR <= rx_done and rx_parity_error; 148 | end if; 149 | end if; 150 | end process; 151 | 152 | -- ------------------------------------------------------------------------- 153 | -- UART RECEIVER FSM 154 | -- ------------------------------------------------------------------------- 155 | 156 | -- PRESENT STATE REGISTER 157 | process (CLK) 158 | begin 159 | if (rising_edge(CLK)) then 160 | if (RST = '1') then 161 | fsm_pstate <= idle; 162 | else 163 | fsm_pstate <= fsm_nstate; 164 | end if; 165 | end if; 166 | end process; 167 | 168 | -- NEXT STATE AND OUTPUTS LOGIC 169 | process (fsm_pstate, UART_RXD, rx_clk_en, rx_bit_count) 170 | begin 171 | case fsm_pstate is 172 | 173 | when idle => 174 | fsm_stopbit <= '0'; 175 | fsm_databits <= '0'; 176 | fsm_idle <= '1'; 177 | 178 | if (UART_RXD = '0') then 179 | fsm_nstate <= startbit; 180 | else 181 | fsm_nstate <= idle; 182 | end if; 183 | 184 | when startbit => 185 | fsm_stopbit <= '0'; 186 | fsm_databits <= '0'; 187 | fsm_idle <= '0'; 188 | 189 | if (rx_clk_en = '1') then 190 | fsm_nstate <= databits; 191 | else 192 | fsm_nstate <= startbit; 193 | end if; 194 | 195 | when databits => 196 | fsm_stopbit <= '0'; 197 | fsm_databits <= '1'; 198 | fsm_idle <= '0'; 199 | 200 | if ((rx_clk_en = '1') AND (rx_bit_count = "111")) then 201 | if (PARITY_BIT = "none") then 202 | fsm_nstate <= stopbit; 203 | else 204 | fsm_nstate <= paritybit; 205 | end if ; 206 | else 207 | fsm_nstate <= databits; 208 | end if; 209 | 210 | when paritybit => 211 | fsm_stopbit <= '0'; 212 | fsm_databits <= '0'; 213 | fsm_idle <= '0'; 214 | 215 | if (rx_clk_en = '1') then 216 | fsm_nstate <= stopbit; 217 | else 218 | fsm_nstate <= paritybit; 219 | end if; 220 | 221 | when stopbit => 222 | fsm_stopbit <= '1'; 223 | fsm_databits <= '0'; 224 | fsm_idle <= '0'; 225 | 226 | if (rx_clk_en = '1') then 227 | fsm_nstate <= idle; 228 | else 229 | fsm_nstate <= stopbit; 230 | end if; 231 | 232 | when others => 233 | fsm_stopbit <= '0'; 234 | fsm_databits <= '0'; 235 | fsm_idle <= '0'; 236 | fsm_nstate <= idle; 237 | 238 | end case; 239 | end process; 240 | 241 | end architecture; 242 | -------------------------------------------------------------------------------- /rtl/comp/uart_tx.vhd: -------------------------------------------------------------------------------- 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 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | 13 | entity UART_TX is 14 | Generic ( 15 | CLK_DIV_VAL : integer := 16; 16 | PARITY_BIT : string := "none" -- type of parity: "none", "even", "odd", "mark", "space" 17 | ); 18 | Port ( 19 | CLK : in std_logic; -- system clock 20 | RST : in std_logic; -- high active synchronous reset 21 | -- UART INTERFACE 22 | UART_CLK_EN : in std_logic; -- oversampling (16x) UART clock enable 23 | UART_TXD : out std_logic; -- serial transmit data 24 | -- USER DATA INPUT INTERFACE 25 | DIN : in std_logic_vector(7 downto 0); -- input data to be transmitted over UART 26 | DIN_VLD : in std_logic; -- when DIN_VLD = 1, input data (DIN) are valid 27 | DIN_RDY : out std_logic -- when DIN_RDY = 1, transmitter is ready and valid input data will be accepted for transmiting 28 | ); 29 | end entity; 30 | 31 | architecture RTL of UART_TX is 32 | 33 | signal tx_clk_en : std_logic; 34 | signal tx_clk_div_clr : std_logic; 35 | signal tx_data : std_logic_vector(7 downto 0); 36 | signal tx_bit_count : unsigned(2 downto 0); 37 | signal tx_bit_count_en : std_logic; 38 | signal tx_ready : std_logic; 39 | signal tx_parity_bit : std_logic; 40 | signal tx_data_out_sel : std_logic_vector(1 downto 0); 41 | 42 | type state is (idle, txsync, startbit, databits, paritybit, stopbit); 43 | signal tx_pstate : state; 44 | signal tx_nstate : state; 45 | 46 | begin 47 | 48 | DIN_RDY <= tx_ready; 49 | 50 | -- ------------------------------------------------------------------------- 51 | -- UART TRANSMITTER CLOCK DIVIDER AND CLOCK ENABLE FLAG 52 | -- ------------------------------------------------------------------------- 53 | 54 | tx_clk_divider_i : entity work.UART_CLK_DIV 55 | generic map( 56 | DIV_MAX_VAL => CLK_DIV_VAL, 57 | DIV_MARK_POS => 1 58 | ) 59 | port map ( 60 | CLK => CLK, 61 | RST => RST, 62 | CLEAR => tx_clk_div_clr, 63 | ENABLE => UART_CLK_EN, 64 | DIV_MARK => tx_clk_en 65 | ); 66 | 67 | -- ------------------------------------------------------------------------- 68 | -- UART TRANSMITTER INPUT DATA REGISTER 69 | -- ------------------------------------------------------------------------- 70 | 71 | uart_tx_input_data_reg_p : process (CLK) 72 | begin 73 | if (rising_edge(CLK)) then 74 | if (DIN_VLD = '1' AND tx_ready = '1') then 75 | tx_data <= DIN; 76 | end if; 77 | end if; 78 | end process; 79 | 80 | -- ------------------------------------------------------------------------- 81 | -- UART TRANSMITTER BIT COUNTER 82 | -- ------------------------------------------------------------------------- 83 | 84 | uart_tx_bit_counter_p : process (CLK) 85 | begin 86 | if (rising_edge(CLK)) then 87 | if (RST = '1') then 88 | tx_bit_count <= (others => '0'); 89 | elsif (tx_bit_count_en = '1' AND tx_clk_en = '1') then 90 | if (tx_bit_count = "111") then 91 | tx_bit_count <= (others => '0'); 92 | else 93 | tx_bit_count <= tx_bit_count + 1; 94 | end if; 95 | end if; 96 | end if; 97 | end process; 98 | 99 | -- ------------------------------------------------------------------------- 100 | -- UART TRANSMITTER PARITY GENERATOR 101 | -- ------------------------------------------------------------------------- 102 | 103 | uart_tx_parity_g : if (PARITY_BIT /= "none") generate 104 | uart_tx_parity_gen_i: entity work.UART_PARITY 105 | generic map ( 106 | DATA_WIDTH => 8, 107 | PARITY_TYPE => PARITY_BIT 108 | ) 109 | port map ( 110 | DATA_IN => tx_data, 111 | PARITY_OUT => tx_parity_bit 112 | ); 113 | end generate; 114 | 115 | uart_tx_noparity_g : if (PARITY_BIT = "none") generate 116 | tx_parity_bit <= '0'; 117 | end generate; 118 | 119 | -- ------------------------------------------------------------------------- 120 | -- UART TRANSMITTER OUTPUT DATA REGISTER 121 | -- ------------------------------------------------------------------------- 122 | 123 | uart_tx_output_data_reg_p : process (CLK) 124 | begin 125 | if (rising_edge(CLK)) then 126 | if (RST = '1') then 127 | UART_TXD <= '1'; 128 | else 129 | case tx_data_out_sel is 130 | when "01" => -- START BIT 131 | UART_TXD <= '0'; 132 | when "10" => -- DATA BITS 133 | UART_TXD <= tx_data(to_integer(tx_bit_count)); 134 | when "11" => -- PARITY BIT 135 | UART_TXD <= tx_parity_bit; 136 | when others => -- STOP BIT OR IDLE 137 | UART_TXD <= '1'; 138 | end case; 139 | end if; 140 | end if; 141 | end process; 142 | 143 | -- ------------------------------------------------------------------------- 144 | -- UART TRANSMITTER FSM 145 | -- ------------------------------------------------------------------------- 146 | 147 | -- PRESENT STATE REGISTER 148 | process (CLK) 149 | begin 150 | if (rising_edge(CLK)) then 151 | if (RST = '1') then 152 | tx_pstate <= idle; 153 | else 154 | tx_pstate <= tx_nstate; 155 | end if; 156 | end if; 157 | end process; 158 | 159 | -- NEXT STATE AND OUTPUTS LOGIC 160 | process (tx_pstate, DIN_VLD, tx_clk_en, tx_bit_count) 161 | begin 162 | 163 | case tx_pstate is 164 | 165 | when idle => 166 | tx_ready <= '1'; 167 | tx_data_out_sel <= "00"; 168 | tx_bit_count_en <= '0'; 169 | tx_clk_div_clr <= '1'; 170 | 171 | if (DIN_VLD = '1') then 172 | tx_nstate <= txsync; 173 | else 174 | tx_nstate <= idle; 175 | end if; 176 | 177 | when txsync => 178 | tx_ready <= '0'; 179 | tx_data_out_sel <= "00"; 180 | tx_bit_count_en <= '0'; 181 | tx_clk_div_clr <= '0'; 182 | 183 | if (tx_clk_en = '1') then 184 | tx_nstate <= startbit; 185 | else 186 | tx_nstate <= txsync; 187 | end if; 188 | 189 | when startbit => 190 | tx_ready <= '0'; 191 | tx_data_out_sel <= "01"; 192 | tx_bit_count_en <= '0'; 193 | tx_clk_div_clr <= '0'; 194 | 195 | if (tx_clk_en = '1') then 196 | tx_nstate <= databits; 197 | else 198 | tx_nstate <= startbit; 199 | end if; 200 | 201 | when databits => 202 | tx_ready <= '0'; 203 | tx_data_out_sel <= "10"; 204 | tx_bit_count_en <= '1'; 205 | tx_clk_div_clr <= '0'; 206 | 207 | if ((tx_clk_en = '1') AND (tx_bit_count = "111")) then 208 | if (PARITY_BIT = "none") then 209 | tx_nstate <= stopbit; 210 | else 211 | tx_nstate <= paritybit; 212 | end if ; 213 | else 214 | tx_nstate <= databits; 215 | end if; 216 | 217 | when paritybit => 218 | tx_ready <= '0'; 219 | tx_data_out_sel <= "11"; 220 | tx_bit_count_en <= '0'; 221 | tx_clk_div_clr <= '0'; 222 | 223 | if (tx_clk_en = '1') then 224 | tx_nstate <= stopbit; 225 | else 226 | tx_nstate <= paritybit; 227 | end if; 228 | 229 | when stopbit => 230 | tx_ready <= '1'; 231 | tx_data_out_sel <= "00"; 232 | tx_bit_count_en <= '0'; 233 | tx_clk_div_clr <= '0'; 234 | 235 | if (DIN_VLD = '1') then 236 | tx_nstate <= txsync; 237 | elsif (tx_clk_en = '1') then 238 | tx_nstate <= idle; 239 | else 240 | tx_nstate <= stopbit; 241 | end if; 242 | 243 | when others => 244 | tx_ready <= '0'; 245 | tx_data_out_sel <= "00"; 246 | tx_bit_count_en <= '0'; 247 | tx_clk_div_clr <= '0'; 248 | tx_nstate <= idle; 249 | 250 | end case; 251 | end process; 252 | 253 | end architecture; 254 | -------------------------------------------------------------------------------- /rtl/uart.vhd: -------------------------------------------------------------------------------- 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 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | use IEEE.MATH_REAL.ALL; 13 | 14 | -- SIMPLE UART FOR FPGA 15 | -- ==================== 16 | -- UART FOR FPGA REQUIRES: 1 START BIT, 8 DATA BITS, 1 STOP BIT!!! 17 | -- OTHER PARAMETERS CAN BE SET USING GENERICS. 18 | 19 | entity UART is 20 | Generic ( 21 | CLK_FREQ : integer := 50e6; -- system clock frequency in Hz 22 | BAUD_RATE : integer := 115200; -- baud rate value 23 | PARITY_BIT : string := "none"; -- type of parity: "none", "even", "odd", "mark", "space" 24 | USE_DEBOUNCER : boolean := True -- enable/disable debouncer 25 | ); 26 | Port ( 27 | -- CLOCK AND RESET 28 | CLK : in std_logic; -- system clock 29 | RST : in std_logic; -- high active synchronous reset 30 | -- UART INTERFACE 31 | UART_TXD : out std_logic; -- serial transmit data 32 | UART_RXD : in std_logic; -- serial receive data 33 | -- USER DATA INPUT INTERFACE 34 | DIN : in std_logic_vector(7 downto 0); -- input data to be transmitted over UART 35 | DIN_VLD : in std_logic; -- when DIN_VLD = 1, input data (DIN) are valid 36 | DIN_RDY : out std_logic; -- when DIN_RDY = 1, transmitter is ready and valid input data will be accepted for transmiting 37 | -- USER DATA OUTPUT INTERFACE 38 | DOUT : out std_logic_vector(7 downto 0); -- output data received via UART 39 | DOUT_VLD : out std_logic; -- when DOUT_VLD = 1, output data (DOUT) are valid (is assert only for one clock cycle) 40 | FRAME_ERROR : out std_logic; -- when FRAME_ERROR = 1, stop bit was invalid (is assert only for one clock cycle) 41 | PARITY_ERROR : out std_logic -- when PARITY_ERROR = 1, parity bit was invalid (is assert only for one clock cycle) 42 | ); 43 | end entity; 44 | 45 | architecture RTL of UART is 46 | 47 | constant OS_CLK_DIV_VAL : integer := integer(real(CLK_FREQ)/real(16*BAUD_RATE)); 48 | constant UART_CLK_DIV_VAL : integer := integer(real(CLK_FREQ)/real(OS_CLK_DIV_VAL*BAUD_RATE)); 49 | 50 | signal os_clk_en : std_logic; 51 | signal uart_rxd_meta_n : std_logic; 52 | signal uart_rxd_synced_n : std_logic; 53 | signal uart_rxd_debounced_n : std_logic; 54 | signal uart_rxd_debounced : std_logic; 55 | 56 | begin 57 | 58 | -- ------------------------------------------------------------------------- 59 | -- UART OVERSAMPLING (~16X) CLOCK DIVIDER AND CLOCK ENABLE FLAG 60 | -- ------------------------------------------------------------------------- 61 | 62 | os_clk_divider_i : entity work.UART_CLK_DIV 63 | generic map( 64 | DIV_MAX_VAL => OS_CLK_DIV_VAL, 65 | DIV_MARK_POS => OS_CLK_DIV_VAL-1 66 | ) 67 | port map ( 68 | CLK => CLK, 69 | RST => RST, 70 | CLEAR => RST, 71 | ENABLE => '1', 72 | DIV_MARK => os_clk_en 73 | ); 74 | 75 | -- ------------------------------------------------------------------------- 76 | -- UART RXD CROSS DOMAIN CROSSING 77 | -- ------------------------------------------------------------------------- 78 | 79 | uart_rxd_cdc_reg_p : process (CLK) 80 | begin 81 | if (rising_edge(CLK)) then 82 | uart_rxd_meta_n <= not UART_RXD; 83 | uart_rxd_synced_n <= uart_rxd_meta_n; 84 | end if; 85 | end process; 86 | 87 | -- ------------------------------------------------------------------------- 88 | -- UART RXD DEBAUNCER 89 | -- ------------------------------------------------------------------------- 90 | 91 | use_debouncer_g : if (USE_DEBOUNCER = True) generate 92 | debouncer_i : entity work.UART_DEBOUNCER 93 | generic map( 94 | LATENCY => 4 95 | ) 96 | port map ( 97 | CLK => CLK, 98 | DEB_IN => uart_rxd_synced_n, 99 | DEB_OUT => uart_rxd_debounced_n 100 | ); 101 | end generate; 102 | 103 | not_use_debouncer_g : if (USE_DEBOUNCER = False) generate 104 | uart_rxd_debounced_n <= uart_rxd_synced_n; 105 | end generate; 106 | 107 | uart_rxd_debounced <= not uart_rxd_debounced_n; 108 | 109 | -- ------------------------------------------------------------------------- 110 | -- UART RECEIVER 111 | -- ------------------------------------------------------------------------- 112 | 113 | uart_rx_i: entity work.UART_RX 114 | generic map ( 115 | CLK_DIV_VAL => UART_CLK_DIV_VAL, 116 | PARITY_BIT => PARITY_BIT 117 | ) 118 | port map ( 119 | CLK => CLK, 120 | RST => RST, 121 | -- UART INTERFACE 122 | UART_CLK_EN => os_clk_en, 123 | UART_RXD => uart_rxd_debounced, 124 | -- USER DATA OUTPUT INTERFACE 125 | DOUT => DOUT, 126 | DOUT_VLD => DOUT_VLD, 127 | FRAME_ERROR => FRAME_ERROR, 128 | PARITY_ERROR => PARITY_ERROR 129 | ); 130 | 131 | -- ------------------------------------------------------------------------- 132 | -- UART TRANSMITTER 133 | -- ------------------------------------------------------------------------- 134 | 135 | uart_tx_i: entity work.UART_TX 136 | generic map ( 137 | CLK_DIV_VAL => UART_CLK_DIV_VAL, 138 | PARITY_BIT => PARITY_BIT 139 | ) 140 | port map ( 141 | CLK => CLK, 142 | RST => RST, 143 | -- UART INTERFACE 144 | UART_CLK_EN => os_clk_en, 145 | UART_TXD => UART_TXD, 146 | -- USER DATA INPUT INTERFACE 147 | DIN => DIN, 148 | DIN_VLD => DIN_VLD, 149 | DIN_RDY => DIN_RDY 150 | ); 151 | 152 | end architecture; 153 | -------------------------------------------------------------------------------- /sim/sim.tcl: -------------------------------------------------------------------------------- 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 | # Create work library 10 | vlib work 11 | 12 | # Compile VHDL files 13 | vcom -93 ../rtl/comp/uart_clk_div.vhd 14 | vcom -93 ../rtl/comp/uart_debouncer.vhd 15 | vcom -93 ../rtl/comp/uart_parity.vhd 16 | vcom -93 ../rtl/comp/uart_tx.vhd 17 | vcom -93 ../rtl/comp/uart_rx.vhd 18 | vcom -93 ../rtl/uart.vhd 19 | vcom -93 ./uart_tb.vhd 20 | 21 | # Load testbench 22 | vsim work.uart_tb 23 | 24 | # Setup and start simulation 25 | add wave sim:/uart_tb/utt/* 26 | #add wave sim:/uart_tb/utt/uart_rx_i/* 27 | #add wave sim:/uart_tb/utt/uart_tx_i/* 28 | #add wave sim:/uart_tb/* 29 | run -All -------------------------------------------------------------------------------- /sim/uart_tb.vhd: -------------------------------------------------------------------------------- 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 | library IEEE; 10 | use IEEE.STD_LOGIC_1164.ALL; 11 | use IEEE.NUMERIC_STD.ALL; 12 | use IEEE.MATH_REAL.ALL; 13 | 14 | entity UART_TB is 15 | end entity; 16 | 17 | architecture SIM of UART_TB is 18 | 19 | signal CLK : std_logic; 20 | signal RST : std_logic; 21 | 22 | signal driver_rxd_din : std_logic_vector(7 downto 0); 23 | signal driver_rxd : std_logic := '1'; 24 | signal driver_rxd_done : std_logic := '0'; 25 | 26 | signal monitor_dout_expected : std_logic_vector(7 downto 0); 27 | signal monitor_dout : std_logic_vector(7 downto 0); 28 | signal monitor_dout_vld : std_logic; 29 | signal monitor_dout_done : std_logic := '0'; 30 | 31 | signal driver_din : std_logic_vector(7 downto 0); 32 | signal driver_din_vld : std_logic := '0'; 33 | signal driver_din_rdy : std_logic; 34 | signal driver_din_done : std_logic := '0'; 35 | 36 | signal monitor_txd_dout_expected : std_logic_vector(7 downto 0); 37 | signal monitor_txd_dout : std_logic_vector(7 downto 0); 38 | signal monitor_txd : std_logic := '1'; 39 | signal monitor_txd_done : std_logic := '0'; 40 | signal monitor_txd_start_bit : std_logic := '0'; 41 | signal monitor_txd_stop_bit : std_logic := '0'; 42 | 43 | signal frame_error : std_logic; 44 | signal parity_error : std_logic; 45 | 46 | signal rand_int : integer := 0; 47 | 48 | constant CLK_FREQ : natural := 50e6; 49 | constant BAUD_RATE : natural := 115200; 50 | constant TRANS_COUNT : natural := 2**8; 51 | constant CLK_PERIOD : time := 1 ns * integer(real(1e9)/real(CLK_FREQ)); 52 | constant UART_PERIOD_I : natural := integer(real(1e9)/real(BAUD_RATE)); 53 | constant UART_PERIOD : time := 1 ns * UART_PERIOD_I; 54 | 55 | procedure UART_DRIVER ( 56 | constant UART_PER : time; 57 | signal UART_DIN : in std_logic_vector(7 downto 0); 58 | signal UART_TXD : out std_logic 59 | ) is 60 | variable rnd_delay : natural; 61 | begin 62 | -- start bit 63 | UART_TXD <= '0'; 64 | wait for UART_PER; 65 | -- data bits 66 | for i in 0 to (UART_DIN'LENGTH-1) loop 67 | UART_TXD <= UART_DIN(i); 68 | wait for UART_PER; 69 | end loop; 70 | -- stop bit 71 | UART_TXD <= '1'; 72 | wait for UART_PER; 73 | end procedure; 74 | 75 | procedure UART_MONITOR ( 76 | constant UART_PER : time; 77 | signal UART_RXD : in std_logic; 78 | signal UART_DOUT : out std_logic_vector(7 downto 0); 79 | signal UART_START_BIT : out std_logic; 80 | signal UART_STOP_BIT : out std_logic 81 | ) is begin 82 | if (UART_RXD = '1') then 83 | wait until UART_RXD = '0'; 84 | end if; 85 | UART_START_BIT <= '1'; 86 | -- start bit 87 | wait for UART_PER; 88 | UART_START_BIT <= '0'; 89 | -- data bits 90 | wait for UART_PER/2; -- move to middle data bit 91 | for i in 0 to (UART_DOUT'LENGTH-2) loop 92 | UART_DOUT(i) <= UART_RXD; 93 | wait for UART_PER; 94 | end loop; 95 | -- last data bit 96 | UART_DOUT(UART_DOUT'LENGTH-1) <= UART_RXD; 97 | wait for UART_PER/2; 98 | -- stop bit 99 | UART_STOP_BIT <= '1'; 100 | -- move to middle of stop bit 101 | wait for UART_PER/2; 102 | if (UART_RXD = '0') then 103 | report "======== INVALID STOP BIT IN UART_MONITOR! ========" severity failure; 104 | end if; 105 | UART_STOP_BIT <= '0'; 106 | -- in middle of stop bit move to resync (wait for start bit) 107 | end procedure; 108 | 109 | begin 110 | 111 | rand_int_p : process 112 | variable seed1, seed2: positive; 113 | variable rand : real; 114 | begin 115 | uniform(seed1, seed2, rand); 116 | rand_int <= integer(rand*real(20)); 117 | --report "Random number X: " & integer'image(rand_int); 118 | wait for CLK_PERIOD; 119 | end process; 120 | 121 | utt : entity work.UART 122 | generic map ( 123 | CLK_FREQ => CLK_FREQ, 124 | BAUD_RATE => BAUD_RATE, 125 | PARITY_BIT => "none" -- parity bit is not supported in this simulation 126 | ) 127 | port map ( 128 | CLK => CLK, 129 | RST => RST, 130 | -- UART INTERFACE 131 | UART_TXD => monitor_txd, 132 | UART_RXD => driver_rxd, 133 | -- USER DATA INPUT INTERFACE 134 | DIN => driver_din, 135 | DIN_VLD => driver_din_vld, 136 | DIN_RDY => driver_din_rdy, 137 | -- USER DATA OUTPUT INTERFACE 138 | DOUT => monitor_dout, 139 | DOUT_VLD => monitor_dout_vld, 140 | FRAME_ERROR => frame_error, 141 | PARITY_ERROR => parity_error 142 | ); 143 | 144 | clk_gen_p : process 145 | begin 146 | CLK <= '0'; 147 | wait for CLK_PERIOD/2; 148 | CLK <= '1'; 149 | wait for CLK_PERIOD/2; 150 | end process; 151 | 152 | rst_gen_p : process 153 | begin 154 | RST <= '1'; 155 | wait for CLK_PERIOD*3; 156 | RST <= '0'; 157 | wait; 158 | end process; 159 | 160 | -- ------------------------------------------------------------------------- 161 | -- UART MODULE RECEIVING TEST 162 | -- ------------------------------------------------------------------------- 163 | 164 | driver_rxd_p : process 165 | begin 166 | driver_rxd <= '1'; 167 | wait until RST = '0'; 168 | wait for 33 ns; 169 | for i in 0 to TRANS_COUNT-1 loop 170 | driver_rxd_din <= std_logic_vector(to_unsigned(i,driver_rxd_din'LENGTH)); 171 | UART_DRIVER(UART_PERIOD, driver_rxd_din, driver_rxd); 172 | wait for (rand_int/2) * UART_PERIOD; 173 | end loop; 174 | driver_rxd_done <= '1'; 175 | wait; 176 | end process; 177 | 178 | monitor_dout_p : process 179 | begin 180 | for i in 0 to TRANS_COUNT-1 loop 181 | monitor_dout_expected <= std_logic_vector(to_unsigned(i,monitor_dout_expected'LENGTH)); 182 | wait until monitor_dout_vld = '1'; 183 | if (monitor_dout = monitor_dout_expected) then 184 | --report "Transaction on DOUT port is OK." severity note; 185 | else 186 | report "======== UNEXPECTED TRANSACTION ON DOUT PORT! ========" severity failure; 187 | end if; 188 | wait for CLK_PERIOD; 189 | end loop; 190 | monitor_dout_done <= '1'; 191 | wait; 192 | end process; 193 | 194 | -- ------------------------------------------------------------------------- 195 | -- UART MODULE TRANSMISSION TEST 196 | -- ------------------------------------------------------------------------- 197 | 198 | driver_din_p : process 199 | begin 200 | wait until RST = '0'; 201 | wait until rising_edge(CLK); 202 | wait for CLK_PERIOD/2; 203 | for i in 0 to TRANS_COUNT-1 loop 204 | driver_din <= std_logic_vector(to_unsigned(i,driver_din'LENGTH)); 205 | driver_din_vld <= '1'; 206 | if (driver_din_rdy = '0') then 207 | wait until driver_din_rdy = '1'; 208 | wait for CLK_PERIOD/2; 209 | end if; 210 | wait for CLK_PERIOD; 211 | driver_din_vld <= '0'; 212 | wait for rand_int*(UART_PERIOD_I/16)*CLK_PERIOD; 213 | end loop; 214 | driver_din_done <= '1'; 215 | wait; 216 | end process; 217 | 218 | monitor_txd_p : process 219 | begin 220 | for i in 0 to TRANS_COUNT-1 loop 221 | monitor_txd_dout_expected <= std_logic_vector(to_unsigned(i,monitor_txd_dout_expected'LENGTH)); 222 | UART_MONITOR(UART_PERIOD, monitor_txd, monitor_txd_dout, monitor_txd_start_bit, monitor_txd_stop_bit); 223 | if (monitor_txd_dout = monitor_txd_dout_expected) then 224 | --report "Transaction on UART_TXD port is OK." severity note; 225 | else 226 | report "======== UNEXPECTED TRANSACTION ON UART_TXD PORT! ========" severity failure; 227 | end if; 228 | end loop; 229 | monitor_txd_done <= '1'; 230 | wait; 231 | end process; 232 | 233 | -- ------------------------------------------------------------------------- 234 | -- TEST DONE CHECK 235 | -- ------------------------------------------------------------------------- 236 | 237 | test_done_p : process 238 | variable v_test_done : std_logic; 239 | begin 240 | v_test_done := driver_rxd_done and monitor_dout_done and driver_din_done and monitor_txd_done; 241 | if (v_test_done = '1') then 242 | wait for 100*CLK_PERIOD; 243 | report "======== SIMULATION SUCCESSFULLY COMPLETED! ========" severity failure; 244 | end if; 245 | wait for CLK_PERIOD; 246 | end process; 247 | 248 | end architecture; 249 | --------------------------------------------------------------------------------