From 69c4bc53884661771542d4f9dcfcd3c0777e0dad Mon Sep 17 00:00:00 2001 From: tmeissner Date: Sun, 19 Jul 2020 23:50:24 +0200 Subject: [PATCH] Add UART receive component & UART testbench * New UART receive component with features similar to UART tx * New UART simple testbench for the UART components * TODO: checking of parity error * Functional coverage * Formal verification --- syn/UartRx.vhd | 150 ++++++++++++++++++++++++++++++++++++++++++++++ test/Makefile | 16 ++++- test/UartT.vhd | 158 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 321 insertions(+), 3 deletions(-) create mode 100644 syn/UartRx.vhd create mode 100644 test/UartT.vhd diff --git a/syn/UartRx.vhd b/syn/UartRx.vhd new file mode 100644 index 0000000..4091b36 --- /dev/null +++ b/syn/UartRx.vhd @@ -0,0 +1,150 @@ +-- ====================================================================== +-- UART Receiver +-- Copyright (C) 2020 Torsten Meissner +------------------------------------------------------------------------- +-- This program is free software; you can redistribute it and/or +-- modify it under the terms of the GNU Lesser General Public +-- License as published by the Free Software Foundation; either +-- version 3 of the License, or (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +-- Lesser General Public License for more details. +-- +-- You should have received a copy of the GNU Lesser General Public License +-- along with this program; if not, write to the Free Software Foundation, +-- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +-- ====================================================================== + + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + + +entity UartRx is + generic ( + DATA_LENGTH : positive range 5 to 9 := 8; + PARITY : boolean := true; + CLK_DIV : natural := 10 + ); + port ( + reset_n_i : in std_logic; -- async reset + clk_i : in std_logic; -- clock + data_o : out std_logic_vector(DATA_LENGTH-1 downto 0); -- data output + error_o : out std_logic; -- rx error + valid_o : out std_logic; -- output data valid + accept_i : in std_logic; -- output data accepted + rx_i : in std_logic -- uart rx input + ); +end entity UartRx; + + + +architecture rtl of UartRx is + + + function odd_parity (data : in std_logic_vector(DATA_LENGTH-1 downto 0)) return std_logic is + variable v_data : std_logic := '0'; + begin + for i in data'range loop + v_data := v_data xor data(i); + end loop; + return not v_data; + end function odd_parity; + + function to_integer (data : in boolean) return integer is + begin + if data then + return 1; + else + return 0; + end if; + end function to_integer; + + + type t_uart_state is (IDLE, RECEIVE, VALID); + signal s_uart_state : t_uart_state; + + signal s_data : std_logic_vector(DATA_LENGTH+1+to_integer(PARITY) downto 0); + signal s_clk_en : boolean; + + +begin + + + ClkDivP : process (clk_i, reset_n_i) is + variable v_clk_cnt : natural range 0 to CLK_DIV-1; + begin + if (reset_n_i = '0') then + s_clk_en <= false; + v_clk_cnt := CLK_DIV-1; + elsif (rising_edge(clk_i)) then + s_clk_en <= false; + if (s_uart_state = IDLE) then + v_clk_cnt := CLK_DIV-2; + elsif (s_uart_state = RECEIVE) then + if (v_clk_cnt = 0) then + v_clk_cnt := CLK_DIV-1; + else + v_clk_cnt := v_clk_cnt - 1; + end if; + if (v_clk_cnt = CLK_DIV/2-1) then + s_clk_en <= true; + end if; + end if; + end if; + end process ClkDivP; + + + RxP : process (clk_i, reset_n_i) is + variable v_bit_cnt : natural range 0 to s_data'length-1; + begin + if (reset_n_i = '0') then + s_uart_state <= IDLE; + s_data <= (others => '0'); + valid_o <= '0'; + v_bit_cnt := 0; + elsif (rising_edge(clk_i)) then + FsmL : case s_uart_state is + when IDLE => + valid_o <= '0'; + v_bit_cnt := s_data'length-1; + if (rx_i = '0') then + s_uart_state <= RECEIVE; + end if; + when RECEIVE => + if (s_clk_en) then + s_data <= rx_i & s_data(s_data'length-1 downto 1); + if (v_bit_cnt = 0) then + valid_o <= '1'; + s_uart_state <= VALID; + else + v_bit_cnt := v_bit_cnt - 1; + end if; + end if; + when VALID => + valid_o <= '1'; + if (valid_o = '1' and accept_i = '1') then + valid_o <= '0'; + s_uart_state <= IDLE; + end if; + end case; + end if; + end process RxP; + + + ParityG : if PARITY generate + data_o <= s_data(s_data'length-3 downto 1); + error_o <= '1' when odd_parity(s_data(s_data'length-3 downto 1)) /= s_data(s_data'length-2) or + s_data(s_data'length-1) = '0' else + '0'; + else generate + data_o <= s_data(s_data'length-2 downto 1); + error_o <= '1' when s_data(s_data'length-1) = '0' else '0'; + end generate ParityG; + + +end architecture rtl; diff --git a/test/Makefile b/test/Makefile index 65701ce..d19c0e1 100644 --- a/test/Makefile +++ b/test/Makefile @@ -7,7 +7,7 @@ VHD_STD := 08 .PHONY: all -all: queue dict stack sim wishbone +all: queue dict stack sim wishbone uart OsvvmContext.o: $(OSVVM_SRC)/*.vhd @@ -28,7 +28,7 @@ OsvvmContext.o: $(OSVVM_SRC)/*.vhd ghdl -a --std=$(VHD_STD) --work=osvvm $(OSVVM_SRC)/ScoreboardPkg_int.vhd ghdl -a --std=$(VHD_STD) --work=osvvm $(OSVVM_SRC)/ResolutionPkg.vhd ghdl -a --std=$(VHD_STD) --work=osvvm $(OSVVM_SRC)/TbUtilPkg.vhd - ghdl -a --std=$(VHD_STD) --work=osvvm $(OSVVM_SRC)/OsvvmContext.vhd + ghdl -a --std=$(VHD_STD) --work=osvvm $(OSVVM_SRC)/OsvvmContext.vhd UtilsP.o: $(CMN_SRC)/UtilsP.vhd @@ -57,16 +57,25 @@ simt: OsvvmContext.o UtilsP.o AssertP.o QueueP.o SimP.o SimT.vhd ghdl -a --std=$(VHD_STD) SimT.vhd ghdl -e --std=$(VHD_STD) $@ - spit: OsvvmContext.o UtilsP.o $(SYN_SRC)/SpiSlaveE.vhd $(SYN_SRC)/SpiMasterE.vhd SpiT.vhd ghdl -a --std=$(VHD_STD) -fpsl $(SYN_SRC)/SpiSlaveE.vhd $(SYN_SRC)/SpiMasterE.vhd ghdl -a --std=$(VHD_STD) -fpsl SpiT.vhd ghdl -e --std=$(VHD_STD) $@ +uartt: OsvvmContext.o UtilsP.o $(SYN_SRC)/UartTx.vhd $(SYN_SRC)/UartRx.vhd UartT.vhd + ghdl -a --std=$(VHD_STD) -fpsl $(SYN_SRC)/UartTx.vhd $(SYN_SRC)/UartRx.vhd + ghdl -a --std=$(VHD_STD) -fpsl UartT.vhd + ghdl -e --std=$(VHD_STD) $@ + + .PHONY: spi spi: spit ghdl -r --std=$(VHD_STD) $@t --wave=$@t.ghw +.PHONY: uart +uart: uartt + ghdl -r --std=$(VHD_STD) $@t --wave=$@t.ghw + wishbonet: OsvvmContext.o AssertP.o SimP.o QueueP.o DictP.o UtilsP.o $(SYN_SRC)/WishBoneCheckerE.vhd \ $(SYN_SRC)/WishBoneP.vhd $(SYN_SRC)/WishBoneMasterE.vhd $(SYN_SRC)/WishBoneSlaveE.vhd WishBoneT.vhd @@ -96,6 +105,7 @@ clean: rm -f stringt rm -f simt rm -f spit + rm -f uartt rm -f wishbonet rm -f *.json diff --git a/test/UartT.vhd b/test/UartT.vhd new file mode 100644 index 0000000..e497be4 --- /dev/null +++ b/test/UartT.vhd @@ -0,0 +1,158 @@ +-- ====================================================================== +-- UART testbench +-- Copyright (C) 2020 Torsten Meissner +------------------------------------------------------------------------- +-- This program is free software; you can redistribute it and/or +-- modify it under the terms of the GNU Lesser General Public +-- License as published by the Free Software Foundation; either +-- version 3 of the License, or (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +-- Lesser General Public License for more details. +-- +-- You should have received a copy of the GNU Lesser General Public License +-- along with this program; if not, write to the Free Software Foundation, +-- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +-- ====================================================================== + + +library ieee; + use ieee.std_logic_1164.all; + use ieee.numeric_std.all; + +library osvvm; + use osvvm.RandomPkg.all; + +use std.env.all; + + +entity UartT is +end entity UartT; + + + +architecture sim of UartT is + + + component UartTx is + generic ( + DATA_LENGTH : positive range 5 to 9 := 8; + PARITY : boolean := false; + CLK_DIV : natural := 10 + ); + port ( + reset_n_i : in std_logic; -- async reset + clk_i : in std_logic; -- clock + data_i : in std_logic_vector(DATA_LENGTH-1 downto 0); -- data input + valid_i : in std_logic; -- input data valid + accept_o : out std_logic; -- inpit data accepted + tx_o : out std_logic -- uart tx data output + ); + end component UartTx; + + component UartRx is + generic ( + DATA_LENGTH : positive range 5 to 9 := 8; + PARITY : boolean := true; + CLK_DIV : natural := 10 + ); + port ( + reset_n_i : in std_logic; -- async reset + clk_i : in std_logic; -- clock + data_o : out std_logic_vector(DATA_LENGTH-1 downto 0); -- data output + error_o : out std_logic; -- rx error + valid_o : out std_logic; -- output data valid + accept_i : in std_logic; -- output data accepted + rx_i : in std_logic -- uart rx input + ); + end component UartRx; + + constant c_data_length : positive range 5 to 8 := 8; + + signal s_reset_n : std_logic := '0'; + signal s_clk : std_logic := '1'; + signal s_tx_data : std_logic_vector(c_data_length-1 downto 0); + signal s_tx_valid : std_logic; + signal s_tx_accept : std_logic; + + signal s_rx_data : std_logic_vector(c_data_length-1 downto 0); + signal s_rx_error : std_logic; + signal s_rx_valid : std_logic; + signal s_rx_accept : std_logic; + + signal s_uart : std_logic; + + +begin + + + Dut_UartTx : UartTx + generic map ( + DATA_LENGTH => c_data_length, + PARITY => true, + CLK_DIV => 10 + ) + port map ( + reset_n_i => s_reset_n, + clk_i => s_clk, + data_i => s_tx_data, + valid_i => s_tx_valid, + accept_o => s_tx_accept, + tx_o => s_uart + ); + + + Dut_UartRx : UartRx + generic map ( + DATA_LENGTH => c_data_length, + PARITY => true, + CLK_DIV => 10 + ) + port map ( + reset_n_i => s_reset_n, + clk_i => s_clk, + data_o => s_rx_data, + error_o => s_rx_error, + valid_o => s_rx_valid, + accept_i => s_rx_accept, + rx_i => s_uart + ); + + + s_clk <= not s_clk after 5 ns; + s_reset_n <= '1' after 20 ns; + + + TestP : process is + variable v_data : std_logic_vector(c_data_length-1 downto 0); + variable v_random : RandomPType; + begin + v_random.InitSeed(v_random'instance_name); + s_tx_valid <= '0'; + s_rx_accept <= '0'; + s_tx_data <= (others => '0'); + wait until s_reset_n = '1'; + for i in 0 to 2**c_data_length-1 loop + wait until rising_edge(s_clk); + s_tx_valid <= '1'; + s_rx_accept <= '1'; + v_data := v_random.RandSlv(8); + s_tx_data <= v_data; + wait until rising_edge(s_clk) and s_tx_accept = '1'; + s_tx_valid <= '0'; + wait until rising_edge(s_clk) and s_rx_valid = '1'; + assert s_rx_data = v_data + report "Received data 0x" & to_hstring(s_rx_data) & ", expected 0x" & to_hstring(v_data) + severity failure; + assert s_rx_error = '0' + report "Received error 0b" & to_string(s_rx_error) & ", expected 0b0" + severity failure; + end loop; + wait for 10 us; + stop(0); + end process TestP; + + +end architecture sim;