OpenCores
URL https://opencores.org/ocsvn/bitserial/bitserial/trunk

Subversion Repositories bitserial

[/] [bitserial/] [trunk/] [uart.vhd] - Rev 2

Compare with Previous | Blame | View Log

-- FILE:      uart.vhd
-- BRIEF:     UART TX/RX module
-- LICENSE:   MIT
-- COPYRIGHT: Richard James Howe (2019, 2020)
--
-- The UART (Universal Asynchronous Receiver/Transmitter) is one of the simplest
-- serial communication methods available. It is often used for debugging, for
-- issuing commands to embedded devices and sometimes even for uploading firmware
-- to devices. The data format and speed are configurable but there is no method
-- for automatically configuring a UART, both sides must have agreed on the
-- settings before hand. Configurable options include; baud, use of an
-- even of odd parity bit, number of data bits and number of stop bits.
--
-- The clock is not transmitted as part of the signal (which is why baud
-- must be agreed upon before hand), a single packet starts with a 'start bit',
-- where the line goes low. The receiver must synchronize to this start bit (it
-- resets the clock that generates pulse at the sample rate and baud when it
-- encounters a start bit).
--
-- A transmission with 8 data bits, 1 parity bit and 1 stop bit looks like
-- this:
-- ____   ______________________________   ________________
--     \_/|0|1|2|3|4|5|6|7|P|S|         \_/
--     Start   Data     Parity Stop    |--- More data --->
--
-- Start bits are always low, stop bits high. The most common format is
-- 8 data bits, no parity bit, and 1 stop bit at either 9600 or 115200 baud.
--
-- For the receiver a clock that is a multiple of the baud is used so the
-- bits can be sampled with a higher frequency than the bit rate.
--
-- Some notes:
-- * We can transmit from an 8-bit UART to a less than 8-bit UART fine, so
-- long as parity is not used, as
-- * Certain UARTs have the ability to transmit a BREAK signal by holding
-- the line low for a period greater than the packet length. We can transmit
-- that by lowering the baud rate and transmitting all zeroes. Receiving a
-- break (correctly) would require changing the receiver.
--
--
-- See:
-- * <https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter>
--
-- NOTE: We could replace this entire package with an entirely software driven
-- solution. The only hardware we would need two timers driven at the sample
-- rate (one for RX, one for TX) and a deglitched RX signal. An interrupt
-- would be generated on the timers expiry.
--
library ieee, work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
package uart_pkg is
	constant uart_8N1: std_ulogic_vector(7 downto 0) := "10000100";
 
	type uart_generics is record
		clock_frequency:    positive; -- clock frequency of module clock
		delay:              time;     -- gate delay for simulation purposes
		asynchronous_reset: boolean;  -- use asynchronous reset if true
	end record;
 
	component uart_tx is
		generic (
			g:       uart_generics;
			N:       positive;
			format:  std_ulogic_vector(7 downto 0) := uart_8N1;
			use_cfg: boolean);
		port (
			clk:    in std_ulogic;
			rst:    in std_ulogic;
			cr:    out std_ulogic;
			baud:   in std_ulogic; -- Pulse at baud
			tx:    out std_ulogic;
			ok:    out std_ulogic;
			ctr:    in std_ulogic_vector(format'range);
			ctr_we: in std_ulogic;
			we:     in std_ulogic; -- di write enable
			di:     in std_ulogic_vector(N - 1 downto 0));
	end component;
 
	component uart_rx is
		generic (
			g:       uart_generics;
			N:       positive;
			D:       positive;
			format:  std_ulogic_vector(7 downto 0) := uart_8N1;
			use_cfg: boolean);
		port (
			clk:     in std_ulogic;
			rst:     in std_ulogic;
			cr:     out std_ulogic;
			baud:    in std_ulogic;
			sample:  in std_ulogic;
			failed: out std_ulogic_vector(1 downto 0);
			ctr:     in std_ulogic_vector(7 downto 0);
			ctr_we:  in std_ulogic;
			rx:      in std_ulogic;
			we:     out std_ulogic;
			do:     out std_ulogic_vector(N - 1 downto 0));
	end component;
 
	component uart_fifo is
		generic (
			g:           uart_generics;
			data_width:  positive;
			fifo_depth:  positive;
			read_first:  boolean := true);
		port (
			clk:   in  std_ulogic;
			rst:   in  std_ulogic;
			di:    in  std_ulogic_vector(data_width - 1 downto 0);
			we:    in  std_ulogic;
			re:    in  std_ulogic;
			do:    out std_ulogic_vector(data_width - 1 downto 0);
 
			-- optional
			full:  out std_ulogic := '0';
			empty: out std_ulogic := '1');
	end component;
 
	component uart_baud is -- Generates a pulse at the sample rate and baud
		generic (
			g:    uart_generics;
			init: integer;
			N:    positive := 16;
			D:    positive := 3);
		port (
			clk:      in std_ulogic;
			rst:      in std_ulogic;
			we:       in std_ulogic;
			cnt:      in std_ulogic_vector(N - 1 downto 0);
			cr:       in std_ulogic := '0';
			sample:  out std_ulogic;
			baud:    out std_ulogic);
	end component;
 
	component uart_top is
		generic (
			clock_frequency:    positive; -- clock frequency of module clock
			delay:              time;     -- gate delay for simulation purposes
			asynchronous_reset: boolean;  -- use asynchronous reset if true
			baud:               positive := 115200;
			format:             std_ulogic_vector(7 downto 0) := uart_8N1;
			fifo_depth:         natural := 0;
			use_cfg:            boolean := false;
			use_tx:             boolean := true;
			use_rx:             boolean := true
			); -- Use FIFO to buffer results?
		port (
			clk:    in std_ulogic;
			rst:    in std_ulogic;
 
			tx:            out std_ulogic;
			tx_fifo_full:  out std_ulogic;
			tx_fifo_empty: out std_ulogic;
			tx_fifo_we:     in std_ulogic;
			tx_fifo_data:   in std_ulogic_vector(7 downto 0);
 
			rx:             in std_ulogic;
			rx_fifo_full:  out std_ulogic;
			rx_fifo_empty: out std_ulogic;
			rx_fifo_re:     in std_ulogic;
			rx_fifo_data:  out std_ulogic_vector(7 downto 0);
 
			reg:             in std_ulogic_vector(15 downto 0);
			clock_reg_tx_we: in std_ulogic;
			clock_reg_rx_we: in std_ulogic;
			control_reg_we:  in std_ulogic);
	end component;
 
	component uart_tb is
		generic(g: uart_generics);
	end component;
 
	function parity(slv:std_ulogic_vector; even: boolean) return std_ulogic;
	function parity(slv:std_ulogic_vector; even: std_ulogic) return std_ulogic;
	constant ctr_use_parity:  integer := 0;
	constant ctr_even_parity: integer := 1;
	function ctr_stop_bits(ctr: std_ulogic_vector(7 downto 0)) return integer;
	function ctr_data_bits(ctr: std_ulogic_vector(7 downto 0)) return integer;
end package;
 
package body uart_pkg is
	function parity(slv: std_ulogic_vector; even: boolean) return std_ulogic is
		variable z: std_ulogic := '0';
	begin
		if not even then
			z := '1';
		end if;
		for i in slv'range loop
			z := z xor slv(i);
		end loop;
		return z;
	end;
 
	function parity(slv:std_ulogic_vector; even: std_ulogic) return std_ulogic is
		variable z: boolean := false;
	begin
		if even = '1' then
			z := true;
		end if;
		return parity(slv, z);
	end;
 
	function ctr_stop_bits(ctr: std_ulogic_vector(7 downto 0)) return integer is
		variable ii: std_ulogic_vector(1 downto 0);
	begin
		ii := ctr(3 downto 2);
		return to_integer(unsigned(ii));
	end function;
 
	function ctr_data_bits(ctr: std_ulogic_vector(7 downto 0)) return integer is
		variable ii: std_ulogic_vector(3 downto 0);
	begin
		ii := ctr(7 downto 4);
		return to_integer(unsigned(ii));
	end function;
end;
 
library ieee, work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.uart_pkg.all;
 
entity uart_top is
	generic (
		clock_frequency:    positive; -- clock frequency of module clock
		delay:              time;     -- gate delay for simulation purposes
		asynchronous_reset: boolean;  -- use asynchronous reset if true
		baud:               positive := 115200;
		format:             std_ulogic_vector(7 downto 0) := uart_8N1;
		fifo_depth:         natural := 0; -- FIFO depth, 0,1 = no FIFO
		use_cfg:            boolean := false; -- allow run time configuration of baud rate and format?
		use_tx:             boolean := true;  -- instantiate TX functionality?
		use_rx:             boolean := true   -- instantiate RX functionality?
	);
	port (
		clk:    in std_ulogic;
		rst:    in std_ulogic;
 
		tx:            out std_ulogic;
		tx_fifo_full:  out std_ulogic;
		tx_fifo_empty: out std_ulogic;
		tx_fifo_we:     in std_ulogic;
		tx_fifo_data:   in std_ulogic_vector(7 downto 0);
 
		rx:             in std_ulogic;
		rx_fifo_full:  out std_ulogic;
		rx_fifo_empty: out std_ulogic;
		rx_fifo_re:     in std_ulogic;
		rx_fifo_data:  out std_ulogic_vector(7 downto 0);
 
		reg:             in std_ulogic_vector(15 downto 0);
		clock_reg_tx_we: in std_ulogic;
		clock_reg_rx_we: in std_ulogic;
		control_reg_we:  in std_ulogic);
end entity;
 
architecture structural of uart_top is
	constant g: uart_generics := (
		clock_frequency => clock_frequency,
		delay => delay,
		asynchronous_reset => asynchronous_reset
	);
	constant tx_init: integer  := g.clock_frequency / (baud * 16); -- 54 = 115200 @ 100 MHz
	constant rx_init: positive := tx_init - 1; -- 50 = 115200 @ 100 MHz + Fudge Factor
	constant N: positive := 8;
 
	signal rx_ok, rx_nd, rx_push, rx_re: std_ulogic := '0';
	signal rx_pushed: std_ulogic_vector(rx_fifo_data'range) := (others => '0');
	signal tx_pop, tx_ok: std_ulogic := '0';
	signal tx_popped: std_ulogic_vector(tx_fifo_data'range) := (others => '0');
	signal tx_fifo_empty_b, rx_fifo_full_b: std_ulogic := '0';
 
	signal tx_sample, tx_baud, tx_cr: std_ulogic := '0';
	signal rx_sample, rx_baud, rx_cr, rx_we: std_ulogic := '0';
	signal rx_fail: std_ulogic_vector(1 downto 0) := (others => '0');
	signal rx_ok_buf: std_ulogic := '0';
	signal do, do_c, do_n: std_ulogic_vector(rx_fifo_data'range) := (others => '0');
	signal fail_c, fail_n: std_ulogic_vector(1 downto 0) := (others => '0');
	signal nd_c, nd_n: std_ulogic := '0'; -- new data
 
begin
	rx_ok_buf <= not (fail_c(0) or fail_c(1)) after g.delay;
	rx_ok <= rx_ok_buf;
	rx_pushed <= do after g.delay;
	rx_nd <= nd_c and rx_ok_buf after g.delay; -- no new data if there are errors
	rx_re <= rx_push;
 
	process (clk, rst)
	begin
		if use_rx then
			if rst = '1' and g.asynchronous_reset then
				do_c   <= (others => '0') after g.delay;
				fail_c <= (others => '0') after g.delay;
				nd_c   <= '0' after g.delay;
			elsif rising_edge(clk) then
				if rst = '1' and not g.asynchronous_reset then
					do_c   <= (others => '0') after g.delay;
					fail_c <= (others => '0') after g.delay;
					nd_c   <= '0' after g.delay;
				else
					do_c <= do_n     after g.delay;
					nd_c <= nd_n     after g.delay;
					fail_c <= fail_n after g.delay;
				end if;
			end if;
		end if;
	end process;
 
	process (do_c, do, nd_c, rx_we, rx_re, fail_c, rx_fail)
	begin
		if use_tx then
			do_n   <= do_c   after g.delay;
			nd_n   <= nd_c   after g.delay;
			fail_n <= fail_c after g.delay;
			if rx_we = '1' then
				nd_n   <= '1'     after g.delay;
				do_n   <= do      after g.delay;
				fail_n <= rx_fail after g.delay;
			elsif rx_re = '1' then
				nd_n   <= '0' after g.delay;
			end if;
		end if;
	end process;
 
	ugen0: if fifo_depth <= 1 generate
			tx_pop        <= tx_fifo_we;
			tx_popped     <= tx_fifo_data;
			tx_fifo_full  <= not tx_ok;
			tx_fifo_empty <= tx_ok;
 
			rx_push       <= rx_fifo_re;
			rx_fifo_data  <= rx_pushed;
			rx_fifo_full  <= rx_nd;
			rx_fifo_empty <= not rx_nd;
	end generate;
 
	ugen1: if fifo_depth > 1 generate
		tx_fifo_empty <=               tx_fifo_empty_b;
		tx_pop        <= tx_ok and not tx_fifo_empty_b;
 
		ugen1_fifo_tx: if use_tx generate
			uart_fifo_tx_0: work.uart_pkg.uart_fifo
				generic map(g => g,
					   data_width => tx_fifo_data'length,
					   fifo_depth => fifo_depth)
				port map (clk  => clk, rst => rst,
					  di   => tx_fifo_data,
					  we   => tx_fifo_we,
					  re   => tx_pop,
					  do   => tx_popped,
					  full => tx_fifo_full,
					  empty => tx_fifo_empty_b);
		end generate;
 
		rx_fifo_full <=                         rx_fifo_full_b;
		rx_push      <= rx_nd and rx_ok and not rx_fifo_full_b;
		ugen1_fifo_rx: if use_rx generate
			uart_fifo_rx_0: work.uart_pkg.uart_fifo
				generic map(g => g,
					   data_width => rx_fifo_data'length,
					   fifo_depth => fifo_depth,
					   read_first => false)
				port map (clk  => clk, rst => rst,
					  di   => rx_pushed,
					  we   => rx_push,
					  re   => rx_fifo_re,
					  do   => rx_fifo_data,
					  full => rx_fifo_full_b, empty => rx_fifo_empty);
		end generate;
	end generate;
 
	uart_tx_gen: if use_tx generate
		baud_tx: work.uart_pkg.uart_baud
			generic map(g => g, init => tx_init, N => reg'length, D => 3)
			port map(
				clk    => clk,
				rst    => rst,
				we     => clock_reg_tx_we,
				cnt    => reg,  -- 0x32/50 is 1152000 @ 100MHz clk
				cr     => tx_cr,
				sample => tx_sample,
				baud   => tx_baud);
 
		tx_0: work.uart_pkg.uart_tx
			generic map(g => g, N => N, format => format, use_cfg => use_cfg)
			port map(
				clk     => clk,
				rst     => rst,
 
				cr      => tx_cr,
				baud    => tx_baud,
				ok      => tx_ok,
				we      => tx_pop,
				ctr     => reg(reg'high downto reg'high - 7),
				ctr_we  => control_reg_we,
				di      => tx_popped,
				tx      => tx);
	end generate;
 
	uart_rx_gen: if use_rx generate
		baud_rx: work.uart_pkg.uart_baud
			generic map(g => g, init => rx_init, N => reg'length, D => 3)
			port map(
				clk    => clk,
				rst    => rst,
				we     => clock_reg_rx_we,
				cnt    => reg,
				cr     => rx_cr,
				sample => rx_sample,
				baud   => rx_baud);
 
		rx_0: work.uart_pkg.uart_rx
			generic map(g => g, N => N, D => 3, format => format, use_cfg => use_cfg)
			port map(
				clk     => clk,
				rst     => rst,
 
				baud    => rx_baud,
				sample  => rx_sample,
				cr      => rx_cr,
				failed  => rx_fail,
				ctr     => reg(reg'low + 7 downto reg'low),
				ctr_we  => control_reg_we,
				we      => rx_we,
				do      => do,
				rx      => rx);
	end generate;
end architecture;
 
library ieee, work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.uart_pkg.all;
 
entity uart_fifo is
	generic (g: uart_generics;
		data_width: positive;
		fifo_depth: positive;
		read_first: boolean := true);
	port (
		clk:   in  std_ulogic;
		rst:   in  std_ulogic;
		di:    in  std_ulogic_vector(data_width - 1 downto 0);
		we:    in  std_ulogic;
		re:    in  std_ulogic;
		do:    out std_ulogic_vector(data_width - 1 downto 0);
 
		-- optional
		full:  out std_ulogic := '0';
		empty: out std_ulogic := '1');
end uart_fifo;
 
architecture behavior of uart_fifo is
	type fifo_data_t is array (0 to fifo_depth - 1) of std_ulogic_vector(di'range);
	signal data: fifo_data_t := (others => (others => '0'));
	function rindex_init return integer is
	begin
		if read_first then
			return 0;
		end if;
		return fifo_depth - 1;
	end function;
 
	signal count:  integer range 0 to fifo_depth := 0;
	signal windex: integer range 0 to fifo_depth - 1 := 0;
	signal rindex: integer range 0 to fifo_depth - 1 := rindex_init;
 
	signal is_full:  std_ulogic := '0';
	signal is_empty: std_ulogic := '1';
begin
	do       <= data(rindex) after g.delay;
	full     <= is_full after g.delay;  -- buffer these bad boys
	empty    <= is_empty after g.delay;
	is_full  <= '1' when count = fifo_depth else '0' after g.delay;
	is_empty <= '1' when count = 0          else '0' after g.delay;
 
	process (rst, clk) is
	begin
		if rst = '1' and g.asynchronous_reset then
			windex <= 0 after g.delay;
			count  <= 0 after g.delay;
			rindex <= rindex_init after g.delay;
		elsif rising_edge(clk) then
			if rst = '1' and not g.asynchronous_reset then
				windex <= 0 after g.delay;
				count  <= 0 after g.delay;
				rindex <= rindex_init after g.delay;
			else
				if we = '1' and re = '0' then
					if is_full = '0' then
						count <= count + 1 after g.delay;
					end if;
				elsif we = '0' and re = '1' then
					if is_empty = '0' then
						count <= count - 1 after g.delay;
					end if;
				end if;
 
				if re = '1' and is_empty = '0' then
					if rindex = (fifo_depth - 1) then
						rindex <= 0 after g.delay;
					else
						rindex <= rindex + 1 after g.delay;
					end if;
				end if;
 
				if we = '1' and is_full = '0' then
					if windex = (fifo_depth - 1) then
						windex <= 0 after g.delay;
					else
						windex <= windex + 1 after g.delay;
					end if;
					data(windex) <= di after g.delay;
				end if;
			end if;
		end if;
	end process;
end behavior;
 
-- This module generates a sample pulse and a baud pulse. The sample rate
-- can be controlled by setting 'cnt'. This sample rate is then divided by
-- 2^(D+1) to get the baud.
library ieee, work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.uart_pkg.all;
 
entity uart_baud is
	generic (g: uart_generics; init: integer; N: positive := 16; D: positive := 3);
	port (
		clk:     in std_ulogic;
		rst:     in std_ulogic;
 
		we:      in std_ulogic;
		cnt:     in std_ulogic_vector(N - 1 downto 0);
		cr:      in std_ulogic := '0';
 
		sample: out std_ulogic;  -- sample pulse
		baud:   out std_ulogic); -- baud (sample rate / 2^(D+1))
end entity;
 
architecture behaviour of uart_baud is
	constant cmp_init: std_ulogic_vector := std_ulogic_vector(to_unsigned(init, cnt'length));
	signal cmp_c, cmp_n: std_ulogic_vector(cnt'range)  :=  cmp_init;
	signal cnt_c, cnt_n: std_ulogic_vector(cnt'range)  := (others => '0');
	signal div_c, div_n: std_ulogic_vector(D downto 0) := (others => '0');
	signal pul_c, pul_n: std_ulogic := '0';
	signal pulse: std_ulogic := '0';
begin
	pulse  <= (not cr) and pul_n and (pul_c xor pul_n) after g.delay; -- rising edge detector
	baud   <= (not cr) and div_n(div_n'high) and (div_c(div_c'high) xor div_n(div_n'high)) after g.delay;
	sample <= pulse;
 
	process (clk, rst)
	begin
		if rst = '1' and g.asynchronous_reset then
			cmp_c <= cmp_init after g.delay;
			cnt_c <= (others => '0') after g.delay;
			div_c <= (others => '0') after g.delay;
			pul_c <= '0' after g.delay;
		elsif rising_edge(clk) then
			if rst = '1' and not g.asynchronous_reset then
				cmp_c <= cmp_init after g.delay;
				cnt_c <= (others => '0') after g.delay;
				div_c <= (others => '0') after g.delay;
				pul_c <= '0' after g.delay;
			else
				cmp_c <= cmp_n after g.delay;
				cnt_c <= cnt_n after g.delay;
				div_c <= div_n after g.delay;
				pul_c <= pul_n after g.delay;
			end if;
		end if;
	end process;
 
	process (pulse, div_c, cr, we)
	begin
		div_n <= div_c after g.delay;
		if cr = '1' or we = '1' then
			div_n <= (others => '0') after g.delay;
			div_n(div_n'high) <= '1' after g.delay;
		elsif pulse = '1' then
			div_n <= std_ulogic_vector(unsigned(div_c) + 1) after g.delay;
		end if;
	end process;
 
	process (cmp_c, cnt_c, we, cnt, cr)
	begin
		cmp_n <= cmp_c after g.delay;
		cnt_n <= cnt_c after g.delay;
 
		if we = '1' then
			cmp_n <= cnt after g.delay;
			cnt_n <= (others => '0') after g.delay;
			pul_n <= '0' after g.delay;
		elsif cr = '1' then
			cnt_n <= (others => '0') after g.delay;
			pul_n <= '0' after g.delay;
		elsif cnt_c = cmp_c then
			cnt_n <= (others => '0') after g.delay;
			pul_n <= '1' after g.delay;
		else
			cnt_n <= std_ulogic_vector(unsigned(cnt_c) + 1) after g.delay;
			pul_n <= '0' after g.delay;
		end if;
	end process;
end architecture;
 
library ieee, work;
use ieee.std_logic_1164.all;
use work.uart_pkg.all;
 
entity uart_rx is
	generic (
		g:       uart_generics;
		N:       positive;
		D:       positive;
		format:  std_ulogic_vector(7 downto 0) := uart_8N1;
		use_cfg: boolean);
	port (
		clk:     in std_ulogic;
		rst:     in std_ulogic;
		cr:     out std_ulogic; -- reset sample/baud clock when start bit detected
		baud:    in std_ulogic;
		sample:  in std_ulogic; -- pulses at baud and sample rate
		failed: out std_ulogic_vector(1 downto 0);
		ctr:     in std_ulogic_vector(7 downto 0);
		ctr_we:  in std_ulogic;
		rx:      in std_ulogic; -- physical RX signal
		we:     out std_ulogic; -- write 'do' to output register
		do:     out std_ulogic_vector(N - 1 downto 0));
end entity;
 
architecture behaviour of uart_rx is
	type state is (reset, idle, start, data, parity, stop, done);
	signal state_c, state_n: state;
	signal ctr_c, ctr_n: std_ulogic_vector(ctr'range);
	signal rx_c, rx_n: std_ulogic_vector(do'range);
	signal sr_c, sr_n: std_ulogic_vector(D - 1 downto 0);
	signal rx_sync: std_ulogic;
	signal count_c, count_n: integer range 0 to N - 1;
	signal majority: std_ulogic;
	signal fail_c, fail_n: std_ulogic_vector(1 downto 0);
begin
	do     <= rx_c after g.delay;
	failed <= fail_c after g.delay;
 
	assert D < 4 severity failure;
 
	majority_1: if D = 1 generate
		majority <= sr_c(0) after g.delay;
	end generate;
 
	majority_2: if D = 2 generate
		majority <= sr_c(0) and sr_c(1) after g.delay; -- even wins is 'sr_c(0) or sr_c(1)'
	end generate;
 
	majority_3: if D = 3 generate
		majority <= (sr_c(0) and sr_c(1)) or (sr_c(1) and sr_c(2)) or (sr_c(0) and sr_c(2)) after g.delay;
	end generate;
 
	process (clk, rst)
		procedure reset is
		begin
			rx_c     <= (others => '0') after g.delay;
			sr_c     <= (others => '0') after g.delay;
			fail_c   <= (others => '0') after g.delay;
			state_c  <= reset after g.delay;
			count_c  <= 0 after g.delay;
			rx_sync  <= '0' after g.delay;
			if use_cfg then ctr_c  <= (others => '0') after g.delay; end if;
		end procedure;
	begin
		if rst = '1' and g.asynchronous_reset then
			reset;
		elsif rising_edge(clk) then
			if rst = '1' and not g.asynchronous_reset then
				reset;
			else
				rx_c     <= rx_n after g.delay;
				sr_c     <= sr_n after g.delay;
				state_c  <= state_n after g.delay;
				count_c  <= count_n after g.delay;
				fail_c   <= fail_n after g.delay;
				rx_sync  <= rx after g.delay;
				if use_cfg then ctr_c <= ctr_n after g.delay; end if;
			end if;
		end if;
		if not use_cfg then ctr_c <= format after g.delay; end if;
	end process;
 
	process (rx_c, sr_c, ctr_c, ctr_we, ctr, state_c, rx_sync, baud, sample, count_c, fail_c, majority)
	begin
		fail_n  <= fail_c  after g.delay;
		rx_n    <= rx_c    after g.delay;
		sr_n    <= sr_c    after g.delay;
		state_n <= state_c after g.delay;
		we      <= '0'     after g.delay;
		cr      <= '0'     after g.delay;
		count_n <= count_c after g.delay;
		if use_cfg then ctr_n <= ctr_c   after g.delay; end if;
 
		if sample = '1' then
			sr_n <= sr_c(sr_c'high - 1 downto sr_c'low) & rx_sync after g.delay;
		end if;
 
		case state_c is
		when reset =>
			rx_n     <= (others => '0') after g.delay;
			sr_n     <= (others => '0') after g.delay;
			fail_n   <= (others => '0') after g.delay;
			if use_cfg then
				ctr_n    <= format after g.delay; -- 8 bits, 1 stop bit, parity off
			end if;
			state_n  <= idle after g.delay;
		when idle =>
			count_n <= 0 after g.delay;
			if rx_sync = '0' then -- and majority = '1' then
				state_n <= start after g.delay;
				cr      <= '1' after g.delay;
				sr_n    <= (others => '0') after g.delay;
				fail_n  <= (others => '0') after g.delay;
			end if;
		when start =>
			if baud = '1' then
				if majority /= '0' then
					state_n   <= done after g.delay; -- frame error
					fail_n(0) <= '1' after g.delay;
				else
					state_n <= data after g.delay;
				end if;
				sr_n <= (others => '0') after g.delay;
			end if;
		when data =>
			rx_n(count_c) <= majority after g.delay;
			if baud = '1' then
				if count_c = (ctr_data_bits(ctr_c) - 1) then
					count_n <= 0 after g.delay;
					if ctr_c(ctr_use_parity) = '1' then
						state_n <= parity after g.delay;
					else
						state_n <= stop after g.delay;
					end if;
				else
					count_n <= count_c + 1 after g.delay;
				end if;
				sr_n <= (others => '0') after g.delay;
			end if;
		when parity =>
			if baud = '1' then
				if (ctr_c(ctr_use_parity) = '1') and (majority /= parity(rx_c, ctr_c(ctr_even_parity))) then
					fail_n(1) <= '1' after g.delay; -- parity error, still process stop bits
				end if;
				state_n <= stop after g.delay;
				sr_n <= (others => '0') after g.delay;
			end if;
		when stop =>
			if baud = '1' then
				if majority /= '1' then
					state_n   <= done after g.delay; -- frame error
					fail_n(0) <= '1' after g.delay;
				elsif count_c = ctr_stop_bits(ctr_c) then
					state_n <= done after g.delay;
				else
					count_n <= count_c + 1 after g.delay;
				end if;
			end if;
		when done => -- The consuming module needs to store rx_c/fail_c immediately
			we      <= '1' after g.delay;
			state_n <= idle after g.delay;
		end case;
 
		if ctr_we = '1' then
			if not (state_c = idle or state_c = done or state_c = reset) then
				rx_n      <= (others => '0') after g.delay;
				state_n   <= idle after g.delay;
				we        <= '1' after g.delay;
				fail_n(0) <= '1' after g.delay; -- frame error!
			end if;
			ctr_n <= ctr after g.delay;
		end if;
	end process;
end architecture;
 
library ieee, work;
use ieee.std_logic_1164.all;
use work.uart_pkg.all;
 
entity uart_tx is
	generic (
		g:       uart_generics;
		N:       positive;
		format:  std_ulogic_vector(7 downto 0) := uart_8N1;
		use_cfg: boolean);
	port (
		clk:    in std_ulogic;
		rst:    in std_ulogic;
		cr:    out std_ulogic;
		baud:   in std_ulogic; -- Pulse at baud
		tx:    out std_ulogic;
		ok:    out std_ulogic;
		ctr:    in std_ulogic_vector(format'range);
		ctr_we: in std_ulogic;
		we:     in std_ulogic; -- di write enable
		di:     in std_ulogic_vector(N - 1 downto 0));
end entity;
 
architecture behaviour of uart_tx is
	type state is (reset, idle, start, data, parity, stop);
	signal state_c, state_n: state;
	signal di_c, di_n: std_ulogic_vector(di'range);
	signal ctr_c, ctr_n: std_ulogic_vector(ctr'range);
	signal busy: std_ulogic;
	signal parity_c, parity_n: std_ulogic;
	signal count_c, count_n: integer range 0 to 15;
begin
	busy <= '0' when state_c = idle else '1' after g.delay;
	ok <= not busy after g.delay;
 
	process (clk, rst)
		procedure reset is
		begin
			di_c     <= (others => '0') after g.delay;
			state_c  <= reset after g.delay;
			parity_c <= '0' after g.delay;
			count_c  <= 0 after g.delay;
			if use_cfg then ctr_c <= (others => '0') after g.delay; end if;
		end procedure;
	begin
		if rst = '1' and g.asynchronous_reset then
			reset;
		elsif rising_edge(clk) then
			if rst = '1' and not g.asynchronous_reset then
				reset;
			else
				di_c     <= di_n     after g.delay;
				state_c  <= state_n  after g.delay;
				parity_c <= parity_n after g.delay;
				count_c  <= count_n  after g.delay;
				if use_cfg then ctr_c <= ctr_n after g.delay; end if;
			end if;
		end if;
		if not use_cfg then ctr_c <= format after g.delay; end if;
	end process;
 
	process (di_c, di, ctr_c, ctr_we, ctr, state_c, we, baud, parity_c, count_c)
	begin
		count_n <= count_c after g.delay;
		state_n <= state_c after g.delay;
		di_n    <= di_c after g.delay;
		if use_cfg then ctr_n <= ctr_c after g.delay; end if;
		tx <= '1' after g.delay;
		cr <= '0';
 
		if ctr_c(ctr_use_parity) = '1' then
			parity_n <= parity(di_c, ctr_c(ctr_even_parity)) after g.delay;
		else
			parity_n <= '0' after g.delay;
		end if;
 
		case state_c is
		when reset  =>
			state_n  <= idle after g.delay;
			ctr_n    <= format after g.delay; -- 8 bits, 1 stop bit, parity off
			count_n  <= 0 after g.delay;
			parity_n <= '0' after g.delay;
			di_n     <= (others => '0') after g.delay;
		when idle   =>
			count_n  <= 0 after g.delay;
			if use_cfg and ctr_we = '1' then -- NB. We can either lose data, or control writes
				ctr_n <= ctr after g.delay;
			elsif we = '1' then
				di_n    <= di after g.delay;
				cr      <= '1';
				state_n <= start after g.delay;
			end if;
		when start  =>
			tx <= '0' after g.delay;
			if baud = '1' then
				state_n <= data after g.delay;
			end if;
		when data   =>
			tx <= di_c(count_c) after g.delay;
			if baud = '1' then
				if count_c = (ctr_data_bits(ctr_c) - 1) then
					count_n <= 0 after g.delay;
					if ctr_c(ctr_use_parity) = '1' then
						state_n <= parity after g.delay;
					else
						state_n <= stop after g.delay;
					end if;
				else
					count_n <= count_c + 1 after g.delay;
				end if;
			end if;
		when parity =>
			tx <= parity_c after g.delay;
			if baud = '1' then
				state_n <= stop after g.delay;
			end if;
		when stop   =>
			tx <= '1' after g.delay;
			if baud = '1' then
				if count_c = ctr_stop_bits(ctr_c) then
					count_n <= 0 after g.delay;
					state_n <= idle after g.delay;
				else
					count_n <= count_c + 1 after g.delay;
				end if;
			end if;
		end case;
	end process;
end architecture;
 
library ieee, work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.uart_pkg.all;
 
entity uart_tb is
	generic(g: uart_generics);
end entity;
 
architecture testing of uart_tb is
	constant clock_period: time     := 1000 ms / g.clock_frequency;
	constant fifo_depth: natural    := 8;
	signal rst, clk:     std_ulogic := '1';
	signal stop:         boolean    := false;
	signal loopback:     boolean    := true;
	signal tx, rx:       std_ulogic;
	signal di, do:       std_ulogic_vector(7 downto 0);
	signal reg:          std_ulogic_vector(15 downto 0);
	signal clock_reg_tx_we: std_ulogic;
	signal clock_reg_rx_we: std_ulogic;
	signal control_reg_we:  std_ulogic;
 
	signal tx_fifo_full:  std_ulogic;
	signal tx_fifo_empty: std_ulogic;
	signal tx_fifo_we:    std_ulogic := '0';
	signal rx_fifo_full:  std_ulogic;
	signal rx_fifo_empty: std_ulogic;
	signal rx_fifo_re:    std_ulogic := '0';
begin
	-- duration: process begin wait for 20000 us; stop <= true; wait; end process;
	clk_process: process
	begin
		rst <= '1';
		wait for clock_period * 5;
		rst <= '0';
		while not stop loop
			clk <= '1';
			wait for clock_period / 2;
			clk <= '0';
			wait for clock_period / 2;
		end loop;
		wait;
	end process;
 
	stimulus: process
		procedure write(data: std_ulogic_vector(di'range)) is
		begin
			wait for clock_period * 1;
			while tx_fifo_full = '1' loop
				wait for clock_period;
			end loop;
			di <= data;
			tx_fifo_we <= '1';
			wait for clock_period;
			tx_fifo_we <= '0';
		end procedure;
	begin
		di <= x"00";
		wait until rst = '0';
		wait for clock_period;
		reg             <=  x"8080";
		control_reg_we  <=  '1';
		wait for clock_period;
		control_reg_we  <=  '0';
		reg             <=  x"0036";
		clock_reg_tx_we <=  '1';
		wait for clock_period;
		clock_reg_tx_we <=  '0';
		clock_reg_rx_we <=  '1';
		reg             <=  x"0035";
		wait for clock_period;
		clock_reg_rx_we <=  '0';
		wait for clock_period;
 
		write(x"AA");
		write(x"BB");
		write(x"CC");
		write(x"DD");
		write(x"EE");
		write(x"FF");
		wait for clock_period;
		while tx_fifo_empty = '0' loop
			wait for clock_period;
		end loop;
		loopback <= false;
		wait for clock_period * 50000;
		stop <= true;
		wait;
	end process;
 
	ack: process
	begin
		while not stop loop
			if rx_fifo_empty = '0' then
				rx_fifo_re <= '1';
			else
				rx_fifo_re <= '0';
			end if;
			wait for clock_period;
		end loop;
		wait;
	end process;
	rx <= tx when loopback else '0'; -- loop back test
	uut: work.uart_pkg.uart_top
		generic map (
		clock_frequency    => g.clock_frequency,
		delay              => g.delay,
		asynchronous_reset => g.asynchronous_reset,
		baud               => 115200,
		format             => uart_8N1,
		fifo_depth         => fifo_depth)
		port map (
			clk => clk,
			rst => rst,
 
			tx             =>  tx,
			tx_fifo_full   =>  tx_fifo_full,
			tx_fifo_empty  =>  tx_fifo_empty,
			tx_fifo_we     =>  tx_fifo_we,
			tx_fifo_data   =>  di,
 
			rx             =>  rx,
			rx_fifo_full   =>  rx_fifo_full,
			rx_fifo_empty  =>  rx_fifo_empty,
			rx_fifo_re     =>  rx_fifo_re,
			rx_fifo_data   =>  do,
 
			reg             => reg,
			clock_reg_tx_we => clock_reg_tx_we,
			clock_reg_rx_we => clock_reg_rx_we,
			control_reg_we  => control_reg_we);
 
end architecture;
 
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.