Line 1... |
Line 1... |
--------------------------------------------------------------------------------
|
-- FILE: uart.vhd
|
--| @file uart.vhd
|
-- BRIEF: UART TX/RX module
|
--| @brief implements a universal asynchronous receiver transmitter with
|
-- LICENSE: MIT
|
--| parameterisable baud rate. tested on a spartan 6 lx9 connected to a
|
-- COPYRIGHT: Richard James Howe (2019)
|
--| silicon labs cp210 usb-uart bridge.
|
--
|
--|
|
-- The UART (Universal Asynchronous Receiver/Transmitter) is one of the simplest
|
--| @author peter a bennett
|
-- serial communication methods available. It is often used for debugging, for
|
--| @copyright (c) 2012 peter a bennett
|
-- issuing commands to embedded devices and sometimes even for uploading firmware
|
--| @license Apache 2.0
|
-- to devices. The data format and speed are configurable but there is no method
|
--| @email pab850@googlemail.com
|
-- for automatically configuring a UART, both sides must have agreed on the
|
--| @contact www.bytebash.com
|
-- settings before hand. Configurable options include; baud, use of an
|
--|
|
-- even of odd parity bit, number of data bits and number of stop bits.
|
--| See https://github.com/pabennett/uart
|
--
|
--|
|
-- The clock is not transmitted as part of the signal (which is why baud
|
--| There have been many changes to the original code, consult the git logs
|
-- must be agreed upon before hand), a single packet starts with a 'start bit',
|
--| for a full list of changes.
|
-- 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
|
--| @note Changes made to range to stop Xilinx warnings and with formatting,
|
-- encounters a start bit).
|
--| the UART has also been wrapped up in a package and top level component
|
--
|
--| (called "uart_top") to make the interface easier to use and less confusing.
|
-- A transmission with 8 data bits, 1 parity bit and 1 stop bit looks like
|
--| This has not be tested yet.
|
-- this:
|
--|
|
-- ____ ______________________________ ________________
|
--| @note Somewhere along the chain from the computer, to the Nexys3 board,
|
-- \_/|0|1|2|3|4|5|6|7|P|S| \_/
|
--| to the UART module, and finally to the H2 core, bytes are being lost in
|
-- Start Data Parity Stop |--- More data --->
|
--| transmission from the computer. This UART really should be buffered
|
--
|
--| as well.
|
-- 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.
|
--| START 0 1 2 3 4 5 6 7 STOP
|
--
|
--| ----\_/-|-|-|-|-|-|-|-|-|-------
|
-- 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.
|
--------------------------------------------------------------------------------
|
--
|
library ieee;
|
-- 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.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
use work.util.common_generics;
|
|
|
package uart_pkg is
|
package uart_pkg is
|
|
constant uart_8N1: std_ulogic_vector(7 downto 0) := "10000100";
|
|
|
component uart_top is
|
component uart_tx is
|
generic (baud_rate: positive; clock_frequency: positive; fifo_depth: positive := 8);
|
generic (g: common_generics; N: positive; format: std_ulogic_vector(7 downto 0) := uart_8N1);
|
port (
|
port (
|
clk: in std_ulogic;
|
clk: in std_ulogic;
|
rst: 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;
|
|
|
rx_data: out std_ulogic_vector(7 downto 0);
|
component uart_rx is
|
rx_fifo_empty: out std_ulogic;
|
generic (g: common_generics; N: positive; D: positive; format: std_ulogic_vector(7 downto 0) := uart_8N1);
|
rx_fifo_full: out std_ulogic;
|
port (
|
rx_data_re: in std_ulogic;
|
clk: in std_ulogic;
|
|
rst: in std_ulogic;
|
|
cr: out std_ulogic;
|
|
baud, 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;
|
|
|
tx_data: in std_ulogic_vector(7 downto 0);
|
component uart_baud is -- Generates a pulse at the sample rate and baud
|
tx_fifo_full: out std_ulogic;
|
generic (g: common_generics; init: integer; N: positive := 16; D: positive := 3);
|
tx_fifo_empty: out std_ulogic;
|
port (
|
tx_data_we: in std_ulogic;
|
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;
|
|
|
tx: out std_ulogic;
|
component uart_core is
|
rx: in std_ulogic);
|
generic (g: common_generics; baud: positive := 115200; format: std_ulogic_vector(7 downto 0) := uart_8N1);
|
|
port (
|
|
clk: in std_ulogic;
|
|
rst: in std_ulogic;
|
|
|
|
tx: out std_ulogic;
|
|
tx_ok: out std_ulogic;
|
|
tx_we: in std_ulogic;
|
|
tx_di: in std_ulogic_vector(7 downto 0);
|
|
|
|
rx: in std_ulogic;
|
|
rx_ok: out std_ulogic;
|
|
rx_nd: out std_ulogic;
|
|
rx_re: in std_ulogic;
|
|
rx_do: 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;
|
end component;
|
|
|
component uart_core is
|
component uart_top is
|
generic (baud_rate: positive; clock_frequency: positive);
|
generic (
|
|
g: common_generics;
|
|
baud: positive := 115200;
|
|
format: std_ulogic_vector(7 downto 0) := uart_8N1;
|
|
use_fifo: boolean := false);
|
port (
|
port (
|
clk: in std_ulogic;
|
clk: in std_ulogic;
|
rst: in std_ulogic;
|
rst: in std_ulogic;
|
din: in std_ulogic_vector(7 downto 0);
|
|
din_stb: in std_ulogic;
|
|
din_ack: out std_ulogic := '0';
|
|
din_busy: out std_ulogic;
|
|
|
|
dout: out std_ulogic_vector(7 downto 0);
|
|
dout_stb: out std_ulogic;
|
|
dout_ack: in std_ulogic;
|
|
dout_busy: out std_ulogic;
|
|
|
|
tx: out std_ulogic;
|
tx: out std_ulogic;
|
rx: in 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;
|
end component;
|
|
|
end package;
|
component uart_tb is
|
|
generic(g: common_generics);
|
|
end component;
|
|
|
---- UART Package --------------------------------------------------------------
|
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;
|
|
|
---- UART Top ------------------------------------------------------------------
|
package body uart_pkg is
|
|
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;
|
library ieee, work;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_1164.all;
|
use ieee.numeric_std.all;
|
use ieee.numeric_std.all;
|
use work.util.fifo;
|
use work.uart_pkg.all;
|
use work.uart_pkg.uart_core;
|
use work.util.common_generics;
|
|
|
entity uart_top is
|
entity uart_top is
|
generic (baud_rate: positive; clock_frequency: positive; fifo_depth: positive := 8);
|
generic (
|
|
g: common_generics;
|
|
baud: positive := 115200;
|
|
format: std_ulogic_vector(7 downto 0) := uart_8N1;
|
|
use_fifo: boolean := false);
|
port (
|
port (
|
clk: in std_ulogic;
|
clk: in std_ulogic;
|
rst: in std_ulogic;
|
rst: in std_ulogic;
|
|
|
rx_data: out std_ulogic_vector(7 downto 0);
|
tx: out std_ulogic;
|
rx_fifo_empty: out std_ulogic;
|
|
rx_fifo_full: out std_ulogic;
|
|
rx_data_re: in std_ulogic;
|
|
|
|
tx_data: in std_ulogic_vector(7 downto 0);
|
|
tx_fifo_full: out std_ulogic;
|
tx_fifo_full: out std_ulogic;
|
tx_fifo_empty: out std_ulogic;
|
tx_fifo_empty: out std_ulogic;
|
tx_data_we: in std_ulogic;
|
tx_fifo_we: in std_ulogic;
|
|
tx_fifo_data: in std_ulogic_vector(7 downto 0);
|
|
|
tx: out std_ulogic;
|
rx: in std_ulogic;
|
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;
|
end entity;
|
|
|
architecture behav of uart_top is
|
architecture structural of uart_top is
|
signal rx_sync, rx_uart, tx_uart: std_ulogic := '0';
|
constant fifo_depth: positive := 8;
|
|
signal rx_ok, rx_nd, rx_push, rx_re: std_ulogic;
|
|
signal rx_pushed: std_ulogic_vector(rx_fifo_data'range);
|
|
signal tx_pop, tx_ok: std_ulogic;
|
|
signal tx_popped: std_ulogic_vector(tx_fifo_data'range);
|
|
signal tx_fifo_empty_b, rx_fifo_full_b: std_ulogic;
|
|
begin
|
|
uart_core_0: work.uart_pkg.uart_core
|
|
generic map(g => g, baud => baud, format => format)
|
|
port map(
|
|
clk => clk,
|
|
rst => rst,
|
|
|
signal din: std_ulogic_vector(7 downto 0) := (others => '0');
|
tx => tx,
|
signal din_stb: std_ulogic := '0';
|
tx_we => tx_pop,
|
signal din_ack: std_ulogic := '0';
|
tx_di => tx_popped,
|
signal din_busy: std_ulogic := '0';
|
tx_ok => tx_ok,
|
|
|
|
rx => rx,
|
|
rx_nd => rx_nd,
|
|
rx_ok => rx_ok,
|
|
rx_do => rx_pushed,
|
|
rx_re => rx_push,
|
|
|
|
reg => reg,
|
|
clock_reg_rx_we => clock_reg_rx_we,
|
|
clock_reg_tx_we => clock_reg_tx_we,
|
|
control_reg_we => control_reg_we);
|
|
|
|
ugen0: if not use_fifo 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 use_fifo generate
|
|
tx_fifo_empty <= tx_fifo_empty_b;
|
|
tx_pop <= tx_ok and not tx_fifo_empty_b;
|
|
uart_fifo_tx_0: work.util.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);
|
|
|
|
rx_fifo_full <= rx_fifo_full_b;
|
|
rx_push <= rx_nd and rx_ok and not rx_fifo_full_b;
|
|
uart_fifo_rx_0: work.util.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 architecture;
|
|
|
signal dout: std_ulogic_vector(7 downto 0) := (others => '0');
|
library ieee, work;
|
signal dout_stb: std_ulogic := '0';
|
use ieee.std_logic_1164.all;
|
signal dout_ack: std_ulogic := '0';
|
use ieee.numeric_std.all;
|
signal dout_busy: std_ulogic := '0';
|
use work.uart_pkg.all;
|
|
use work.util.common_generics;
|
|
|
signal tx_fifo_re: std_ulogic := '0';
|
entity uart_core is
|
signal tx_fifo_empty_internal: std_ulogic := '1';
|
generic (
|
signal tx_fifo_full_internal: std_ulogic := '0';
|
g: common_generics;
|
|
baud: positive := 115200;
|
|
format: std_ulogic_vector(7 downto 0) := uart_8N1);
|
|
port (
|
|
clk: in std_ulogic;
|
|
rst: in std_ulogic;
|
|
|
signal wrote_c, wrote_n: std_ulogic := '0';
|
tx: out std_ulogic; -- physical UART TX signal
|
|
tx_ok: out std_ulogic; -- not busy
|
|
tx_we: in std_ulogic; -- write data
|
|
tx_di: in std_ulogic_vector(7 downto 0);
|
|
|
|
rx: in std_ulogic; -- physical UART RX signal
|
|
rx_ok: out std_ulogic; -- data has no errors (parity or frame)
|
|
rx_re: in std_ulogic; -- read data
|
|
rx_nd: out std_ulogic; -- new data available
|
|
rx_do: 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;
|
|
|
begin
|
architecture structural of uart_core is
|
uart_deglitch: process (clk, rst)
|
constant tx_init: integer := g.clock_frequency / (baud * 16); -- 54 = 115200 @ 100 MHz
|
begin
|
constant rx_init: positive := tx_init - 1; -- 50 = 115200 @ 100 MHz + Fudge Factor
|
if rst = '1' then
|
constant N: positive := 8;
|
wrote_c <= '0';
|
signal tx_sample, tx_baud, tx_cr: std_ulogic;
|
|
signal rx_sample, rx_baud, rx_cr, rx_we: std_ulogic;
|
|
signal rx_fail: std_ulogic_vector(1 downto 0);
|
|
signal rx_ok_buf: std_ulogic;
|
|
signal do, do_c, do_n: std_ulogic_vector(rx_do'range);
|
|
signal fail_c, fail_n: std_ulogic_vector(1 downto 0);
|
|
signal nd_c, nd_n: std_ulogic; -- new data
|
|
begin
|
|
rx_ok_buf <= not (fail_c(0) or fail_c(1)) after g.delay;
|
|
rx_ok <= rx_ok_buf;
|
|
rx_do <= do after g.delay;
|
|
rx_nd <= nd_c and rx_ok_buf after g.delay; -- no new data if there are errors
|
|
|
|
process (clk, rst)
|
|
begin
|
|
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
|
elsif rising_edge(clk) then
|
rx_sync <= rx;
|
if rst = '1' and not g.asynchronous_reset then
|
rx_uart <= rx_sync;
|
do_c <= (others => '0') after g.delay;
|
tx <= tx_uart;
|
fail_c <= (others => '0') after g.delay;
|
wrote_c <= wrote_n;
|
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;
|
end process;
|
|
|
process(dout_stb, tx_fifo_empty_internal, tx_fifo_full_internal, din_ack, wrote_c, din_busy)
|
process (do_c, do, nd_c, rx_we, rx_re, fail_c, rx_fail)
|
begin
|
begin
|
dout_ack <= '0';
|
do_n <= do_c after g.delay;
|
din_stb <= '0';
|
nd_n <= nd_c after g.delay;
|
tx_fifo_re <= '0';
|
fail_n <= fail_c after g.delay;
|
wrote_n <= wrote_c;
|
if rx_we = '1' then
|
|
nd_n <= '1' after g.delay;
|
if dout_stb = '1' then
|
do_n <= do after g.delay;
|
dout_ack <= '1';
|
fail_n <= rx_fail after g.delay;
|
end if;
|
elsif rx_re = '1' then
|
|
nd_n <= '0' after g.delay;
|
if tx_fifo_empty_internal = '0' and tx_fifo_full_internal = '0' and din_busy = '0' then
|
|
tx_fifo_re <= '1';
|
|
wrote_n <= '1';
|
|
elsif din_ack = '0' and wrote_c = '1' then
|
|
--elsif wrote_c = '1' then
|
|
-- assert din_ack = '1' on the next cycle?
|
|
din_stb <= '1';
|
|
wrote_n <= '0';
|
|
end if;
|
end if;
|
end process;
|
end process;
|
|
|
rx_fifo: work.util.fifo
|
baud_tx: work.uart_pkg.uart_baud
|
generic map (
|
generic map(g => g, init => tx_init, N => reg'length, D => 3)
|
data_width => 8,
|
|
fifo_depth => fifo_depth)
|
|
port map(
|
port map(
|
clk => clk,
|
clk => clk,
|
rst => rst,
|
rst => rst,
|
di => dout,
|
we => clock_reg_tx_we,
|
we => dout_stb,
|
cnt => reg, -- 0x32/50 is 152000 @ 100MHz clk
|
re => rx_data_re,
|
cr => tx_cr,
|
do => rx_data,
|
sample => tx_sample,
|
full => rx_fifo_full,
|
baud => tx_baud);
|
empty => rx_fifo_empty);
|
|
|
baud_rx: work.uart_pkg.uart_baud
|
tx_fifo: work.util.fifo
|
generic map(g => g, init => rx_init, N => reg'length, D => 3)
|
generic map (
|
|
data_width => 8,
|
|
fifo_depth => fifo_depth)
|
|
port map(
|
port map(
|
clk => clk,
|
clk => clk,
|
rst => rst,
|
rst => rst,
|
di => tx_data,
|
we => clock_reg_rx_we,
|
we => tx_data_we,
|
cnt => reg,
|
re => tx_fifo_re,
|
cr => rx_cr,
|
do => din,
|
sample => rx_sample,
|
full => tx_fifo_full_internal,
|
baud => rx_baud);
|
empty => tx_fifo_empty_internal);
|
|
|
tx_0: work.uart_pkg.uart_tx
|
tx_fifo_empty <= tx_fifo_empty_internal;
|
generic map(g => g, N => N, format => format)
|
-- @bug This is a hack, it should be just 'tx_fifo_full_internal', but
|
|
-- it does not work correctly, so as a temporary hack the busy signal
|
|
-- is or'd in so the data source can block until the FIFO is 'not full'
|
|
-- and not lose any data thinking it has been transmitted.
|
|
tx_fifo_full <= '1' when tx_fifo_full_internal = '1' or din_busy = '1' else '0';
|
|
|
|
uart: work.uart_pkg.uart_core
|
|
generic map(
|
|
baud_rate => baud_rate,
|
|
clock_frequency => clock_frequency)
|
|
port map(
|
port map(
|
clk => clk,
|
clk => clk,
|
rst => rst,
|
rst => rst,
|
din => din,
|
|
din_stb => din_stb,
|
|
din_ack => din_ack,
|
|
din_busy => din_busy,
|
|
dout => dout,
|
|
dout_stb => dout_stb,
|
|
dout_ack => dout_ack,
|
|
dout_busy=> dout_busy,
|
|
rx => rx_uart,
|
|
tx => tx_uart);
|
|
|
|
end;
|
cr => tx_cr,
|
|
baud => tx_baud,
|
|
ok => tx_ok,
|
|
we => tx_we,
|
|
ctr => reg(reg'high downto reg'high - 7),
|
|
ctr_we => control_reg_we,
|
|
di => tx_di,
|
|
tx => tx);
|
|
|
---- UART Top ------------------------------------------------------------------
|
rx_0: work.uart_pkg.uart_rx
|
|
generic map(g => g, N => N, D => 3, format => format)
|
---- UART Core -----------------------------------------------------------------
|
port map(
|
|
clk => clk,
|
|
rst => rst,
|
|
|
library ieee;
|
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 architecture;
|
|
|
|
-- 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.std_logic_1164.all;
|
use ieee.numeric_std.all;
|
use ieee.numeric_std.all;
|
|
use work.uart_pkg.all;
|
|
use work.util.common_generics;
|
|
|
entity uart_core is
|
entity uart_baud is
|
generic(baud_rate: positive; clock_frequency: positive);
|
generic (g: common_generics; init: integer; N: positive := 16; D: positive := 3);
|
port(
|
port(
|
clk: in std_ulogic;
|
clk: in std_ulogic;
|
rst: in std_ulogic;
|
rst: in std_ulogic;
|
din: in std_ulogic_vector(7 downto 0);
|
|
din_stb: in std_ulogic;
|
|
din_ack: out std_ulogic := '0';
|
|
din_busy: out std_ulogic;
|
|
|
|
dout: out std_ulogic_vector(7 downto 0);
|
|
dout_stb: out std_ulogic;
|
|
dout_ack: in std_ulogic;
|
|
dout_busy: out std_ulogic;
|
|
|
|
tx: out std_ulogic;
|
we: in std_ulogic;
|
rx: in std_ulogic);
|
cnt: in std_ulogic_vector(N - 1 downto 0);
|
end entity;
|
cr: in std_ulogic := '0';
|
|
|
architecture behav of uart_core is
|
sample: out std_ulogic; -- sample pulse
|
|
baud: out std_ulogic); -- baud (sample rate / 2^(D+1))
|
|
end entity;
|
|
|
constant uart_tx_count_max: positive := 7;
|
architecture behaviour of uart_baud is
|
constant uart_rx_count_max: positive := 7;
|
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;
|
-- baud generation
|
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');
|
constant c_tx_divider_val: integer := clock_frequency / baud_rate;
|
signal pul_c, pul_n: std_ulogic := '0';
|
constant c_rx_divider_val: integer := clock_frequency / (baud_rate * 16);
|
signal pulse: std_ulogic := '0';
|
|
begin
|
signal baud_counter: integer range 0 to c_tx_divider_val;
|
pulse <= (not cr) and pul_n and (pul_c xor pul_n) after g.delay; -- rising edge detector
|
signal baud_tick: std_ulogic := '0';
|
baud <= (not cr) and div_n(div_n'high) and (div_c(div_c'high) xor div_n(div_n'high)) after g.delay;
|
signal oversample_baud_counter: integer range 0 to c_rx_divider_val := 0;
|
sample <= pulse;
|
signal oversample_baud_tick: std_ulogic := '0';
|
|
|
process (clk, rst)
|
----------------------------------------------------------------------------
|
begin
|
-- transmitter signals
|
if rst = '1' and g.asynchronous_reset then
|
----------------------------------------------------------------------------
|
cmp_c <= cmp_init after g.delay;
|
type uart_tx_states is (idle,
|
cnt_c <= (others => '0') after g.delay;
|
wait_for_tick,
|
div_c <= (others => '0') after g.delay;
|
send_start_bit,
|
pul_c <= '0' after g.delay;
|
transmit_data,
|
|
send_stop_bit);
|
|
|
|
signal uart_tx_state: uart_tx_states := idle;
|
|
|
|
signal uart_tx_data_block: std_ulogic_vector(7 downto 0) := (others => '0');
|
|
signal uart_tx_data: std_ulogic := '1';
|
|
signal uart_tx_count: integer range 0 to uart_tx_count_max := 0;
|
|
signal uart_rx_data_in_ack: std_ulogic := '0';
|
|
----------------------------------------------------------------------------
|
|
-- receiver signals
|
|
----------------------------------------------------------------------------
|
|
type uart_rx_states is (rx_wait_start_synchronise,
|
|
rx_get_start_bit,
|
|
rx_get_data,
|
|
rx_get_stop_bit,
|
|
rx_send_block);
|
|
|
|
signal uart_rx_state: uart_rx_states := rx_get_start_bit;
|
|
signal uart_rx_bit: std_ulogic := '1'; -- @note should the be 0 or 1?
|
|
signal uart_rx_data_block: std_ulogic_vector(7 downto 0) := (others => '0');
|
|
signal uart_rx_data_vec: std_ulogic_vector(1 downto 0) := (others => '0');
|
|
signal uart_rx_filter: unsigned(1 downto 0) := (others => '1');
|
|
signal uart_rx_count: integer range 0 to uart_rx_count_max := 0;
|
|
signal uart_rx_data_out_stb: std_ulogic := '0';
|
|
signal uart_rx_bit_spacing: unsigned (3 downto 0) := (others => '0');
|
|
signal uart_rx_bit_tick: std_ulogic := '0';
|
|
begin
|
|
|
|
din_ack <= uart_rx_data_in_ack;
|
|
dout <= uart_rx_data_block;
|
|
dout_stb <= uart_rx_data_out_stb;
|
|
tx <= uart_tx_data;
|
|
|
|
din_busy <= '0' when uart_tx_state = idle else '1';
|
|
dout_busy <= '0' when uart_rx_state = rx_get_start_bit or uart_rx_state = rx_send_block else '1';
|
|
|
|
-- the input clk is 100MHz, this needs to be divided down to the
|
|
-- rate dictated by the baud_rate. for example, if 115200 baud is selected
|
|
-- (115200 baud = 115200 bps - 115.2kbps) a tick must be generated once
|
|
-- every 1/115200
|
|
tx_clk_divider: process (clk, rst)
|
|
begin
|
|
if rst = '1' then
|
|
baud_counter <= 0;
|
|
baud_tick <= '0';
|
|
elsif rising_edge (clk) then
|
elsif rising_edge (clk) then
|
if baud_counter = c_tx_divider_val then
|
if rst = '1' and not g.asynchronous_reset then
|
baud_counter <= 0;
|
cmp_c <= cmp_init after g.delay;
|
baud_tick <= '1';
|
cnt_c <= (others => '0') after g.delay;
|
|
div_c <= (others => '0') after g.delay;
|
|
pul_c <= '0' after g.delay;
|
else
|
else
|
baud_counter <= baud_counter + 1;
|
cmp_c <= cmp_n after g.delay;
|
baud_tick <= '0';
|
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 if;
|
end if;
|
end process;
|
end process;
|
|
|
-- get data from din and send it one bit at a time
|
process (pulse, div_c, cr, we)
|
-- upon each baud tick. lsb first.
|
|
-- wait 1 tick, send start bit (0), send data 0-7, send stop bit (1)
|
|
uart_send_data: process(clk, rst)
|
|
begin
|
begin
|
if rst = '1' then
|
div_n <= div_c after g.delay;
|
uart_tx_data <= '1';
|
if cr = '1' or we = '1' then
|
uart_tx_data_block <= (others => '0');
|
div_n <= (others => '0') after g.delay;
|
uart_tx_count <= 0;
|
div_n(div_n'high) <= '1' after g.delay;
|
uart_tx_state <= idle;
|
elsif pulse = '1' then
|
uart_rx_data_in_ack <= '0';
|
div_n <= std_ulogic_vector(unsigned(div_c) + 1) after g.delay;
|
elsif rising_edge(clk) then
|
|
uart_rx_data_in_ack <= '0';
|
|
case uart_tx_state is
|
|
when idle =>
|
|
if din_stb = '1' then
|
|
uart_tx_data_block <= din;
|
|
uart_rx_data_in_ack <= '1';
|
|
uart_tx_state <= wait_for_tick;
|
|
end if;
|
|
when wait_for_tick =>
|
|
if baud_tick = '1' then
|
|
uart_tx_state <= send_start_bit;
|
|
end if;
|
|
when send_start_bit =>
|
|
if baud_tick = '1' then
|
|
uart_tx_data <= '0';
|
|
uart_tx_state <= transmit_data;
|
|
uart_tx_count <= 0;
|
|
end if;
|
|
when transmit_data =>
|
|
if baud_tick = '1' then
|
|
if uart_tx_count < uart_tx_count_max then
|
|
uart_tx_data <= uart_tx_data_block(uart_tx_count);
|
|
uart_tx_count <= uart_tx_count + 1;
|
|
else
|
|
uart_tx_data <= uart_tx_data_block(7);
|
|
uart_tx_count <= 0;
|
|
uart_tx_state <= send_stop_bit;
|
|
end if;
|
|
end if;
|
|
when send_stop_bit =>
|
|
if baud_tick = '1' then
|
|
uart_tx_data <= '1';
|
|
uart_tx_state <= idle;
|
|
end if;
|
|
when others =>
|
|
uart_tx_data <= '1';
|
|
uart_tx_state <= idle;
|
|
end case;
|
|
end if;
|
end if;
|
end process;
|
end process;
|
|
|
-- generate an oversampled tick (baud * 16)
|
process (cmp_c, cnt_c, we, cnt, cr)
|
oversample_clk_divider: process (clk, rst)
|
|
begin
|
begin
|
if rst = '1' then
|
cmp_n <= cmp_c after g.delay;
|
oversample_baud_counter <= 0;
|
cnt_n <= cnt_c after g.delay;
|
oversample_baud_tick <= '0';
|
|
elsif rising_edge (clk) then
|
if we = '1' then
|
if oversample_baud_counter = c_rx_divider_val then
|
cmp_n <= cnt after g.delay;
|
oversample_baud_counter <= 0;
|
cnt_n <= (others => '0') after g.delay;
|
oversample_baud_tick <= '1';
|
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
|
else
|
oversample_baud_counter <= oversample_baud_counter + 1;
|
cnt_n <= std_ulogic_vector(unsigned(cnt_c) + 1) after g.delay;
|
oversample_baud_tick <= '0';
|
pul_n <= '0' after g.delay;
|
end if;
|
|
end if;
|
end if;
|
end process;
|
end process;
|
|
end architecture;
|
|
|
|
library ieee, work;
|
|
use ieee.std_logic_1164.all;
|
|
use work.uart_pkg.all;
|
|
use work.util.common_generics;
|
|
use work.util.parity;
|
|
|
|
entity uart_rx is
|
|
generic (
|
|
g: common_generics;
|
|
N: positive;
|
|
D: positive;
|
|
format: std_ulogic_vector(7 downto 0) := uart_8N1);
|
|
port (
|
|
clk: in std_ulogic;
|
|
rst: in std_ulogic;
|
|
cr: out std_ulogic; -- reset sample/baud clock when start bit detected
|
|
baud, 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;
|
|
|
-- synchronise rxd to the oversampled baud
|
architecture behaviour of uart_rx is
|
rxd_synchronise: process(clk, rst)
|
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;
|
|
ctr_c <= (others => '0') after g.delay;
|
|
state_c <= reset after g.delay;
|
|
count_c <= 0 after g.delay;
|
|
rx_sync <= '0' after g.delay;
|
|
end procedure;
|
begin
|
begin
|
if rst = '1' then
|
if rst = '1' and g.asynchronous_reset then
|
uart_rx_data_vec <= (others => '0');
|
reset;
|
elsif rising_edge(clk) then
|
elsif rising_edge(clk) then
|
if oversample_baud_tick = '1' then
|
if rst = '1' and not g.asynchronous_reset then
|
uart_rx_data_vec(0) <= rx;
|
reset;
|
uart_rx_data_vec(1) <= uart_rx_data_vec(0);
|
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;
|
|
ctr_c <= ctr_n after g.delay;
|
|
rx_sync <= rx after g.delay;
|
end if;
|
end if;
|
end if;
|
end if;
|
end process;
|
end process;
|
|
|
-- filter rxd with a 2 bit counter.
|
process (rx_c, sr_c, ctr_c, ctr_we, ctr, state_c, rx_sync, baud, sample, count_c, fail_c, majority)
|
rxd_filter: process(clk, rst)
|
|
begin
|
begin
|
if rst = '1' then
|
fail_n <= fail_c after g.delay;
|
uart_rx_filter <= (others => '1');
|
rx_n <= rx_c after g.delay;
|
uart_rx_bit <= '1';
|
sr_n <= sr_c after g.delay;
|
elsif rising_edge(clk) then
|
ctr_n <= ctr_c after g.delay;
|
if oversample_baud_tick = '1' then
|
state_n <= state_c after g.delay;
|
-- filter rxd.
|
we <= '0' after g.delay;
|
if uart_rx_data_vec(1) = '1' and uart_rx_filter < 3 then
|
cr <= '0' after g.delay;
|
uart_rx_filter <= uart_rx_filter + 1;
|
count_n <= count_c after g.delay;
|
elsif uart_rx_data_vec(1) = '0' and uart_rx_filter > 0 then
|
|
uart_rx_filter <= uart_rx_filter - 1;
|
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;
|
|
ctr_n <= format after g.delay; -- 8 bits, 1 stop bit, parity off
|
|
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;
|
end if;
|
-- set the rx bit.
|
sr_n <= (others => '0') after g.delay;
|
if uart_rx_filter = 3 then
|
|
uart_rx_bit <= '1';
|
|
elsif uart_rx_filter = 0 then
|
|
uart_rx_bit <= '0';
|
|
end if;
|
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;
|
end if;
|
|
else
|
|
count_n <= count_c + 1 after g.delay;
|
end if;
|
end if;
|
end process;
|
sr_n <= (others => '0') after g.delay;
|
|
end if;
|
rx_bit_spacing: process (clk, rst)
|
when parity =>
|
begin
|
if baud = '1' then
|
if rising_edge(clk) then
|
if (ctr_c(ctr_use_parity) = '1') and (majority /= parity(rx_c, ctr_c(ctr_even_parity))) then
|
uart_rx_bit_tick <= '0';
|
fail_n(1) <= '1' after g.delay; -- parity error, still process stop bits
|
if oversample_baud_tick = '1' then
|
end if;
|
if uart_rx_bit_spacing = 15 then
|
state_n <= stop after g.delay;
|
uart_rx_bit_tick <= '1';
|
sr_n <= (others => '0') after g.delay;
|
uart_rx_bit_spacing <= (others => '0');
|
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
|
else
|
uart_rx_bit_spacing <= uart_rx_bit_spacing + 1;
|
count_n <= count_c + 1 after g.delay;
|
end if;
|
end if;
|
if uart_rx_state = rx_get_start_bit then
|
|
uart_rx_bit_spacing <= (others => '0');
|
|
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;
|
|
--rx_n <= (others => '0') after g.delay;
|
|
--sr_n <= (others => '0') after g.delay;
|
|
--fail_n <= (others => '0') 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;
|
end if;
|
|
ctr_n <= ctr after g.delay;
|
end if;
|
end if;
|
end process;
|
end process;
|
|
end architecture;
|
|
|
|
library ieee, work;
|
|
use ieee.std_logic_1164.all;
|
|
use work.uart_pkg.all;
|
|
use work.util.common_generics;
|
|
use work.util.parity;
|
|
|
uart_receive_data: process(clk, rst)
|
entity uart_tx is
|
|
generic (g: common_generics; N: positive; format: std_ulogic_vector(7 downto 0) := uart_8N1);
|
|
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;
|
|
ctr_c <= (others => '0') after g.delay;
|
|
state_c <= reset after g.delay;
|
|
parity_c <= '0' after g.delay;
|
|
count_c <= 0 after g.delay;
|
|
end procedure;
|
begin
|
begin
|
if rst = '1' then
|
if rst = '1' and g.asynchronous_reset then
|
uart_rx_state <= rx_get_start_bit;
|
reset;
|
uart_rx_data_block <= (others => '0');
|
|
uart_rx_count <= 0;
|
|
uart_rx_data_out_stb <= '0';
|
|
elsif rising_edge(clk) then
|
elsif rising_edge(clk) then
|
case uart_rx_state is
|
if rst = '1' and not g.asynchronous_reset then
|
when rx_get_start_bit =>
|
reset;
|
if oversample_baud_tick = '1' and uart_rx_bit = '0' then
|
|
uart_rx_state <= rx_get_data;
|
|
end if;
|
|
when rx_get_data =>
|
|
if uart_rx_bit_tick = '1' then
|
|
if uart_rx_count < uart_rx_count_max then
|
|
uart_rx_data_block(uart_rx_count) <= uart_rx_bit;
|
|
uart_rx_count <= uart_rx_count + 1;
|
|
else
|
|
uart_rx_data_block(7) <= uart_rx_bit;
|
|
uart_rx_count <= 0;
|
|
uart_rx_state <= rx_get_stop_bit;
|
|
end if;
|
|
end if;
|
|
when rx_get_stop_bit =>
|
|
if uart_rx_bit_tick = '1' then
|
|
if uart_rx_bit = '1' then
|
|
uart_rx_state <= rx_send_block;
|
|
uart_rx_data_out_stb <= '1';
|
|
end if;
|
|
end if;
|
|
when rx_send_block =>
|
|
if dout_ack = '1' then
|
|
uart_rx_data_out_stb <= '0';
|
|
uart_rx_data_block <= (others => '0');
|
|
uart_rx_state <= rx_get_start_bit;
|
|
else
|
else
|
uart_rx_data_out_stb <= '1';
|
di_c <= di_n after g.delay;
|
|
ctr_c <= ctr_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;
|
|
end if;
|
|
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;
|
|
ctr_n <= ctr_c after g.delay;
|
|
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 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 if;
|
when others =>
|
|
uart_rx_state <= rx_get_start_bit;
|
|
end case;
|
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;
|
|
use work.util.common_generics;
|
|
|
|
entity uart_tb is
|
|
generic(g: common_generics);
|
|
end entity;
|
|
|
|
architecture testing of uart_tb is
|
|
constant clock_period: time := 1000 ms / g.clock_frequency;
|
|
constant use_fifo: boolean := true;
|
|
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;
|
end if;
|
|
wait for clock_period;
|
|
end loop;
|
|
wait;
|
end process;
|
end process;
|
end;
|
rx <= tx when loopback else '0'; -- loop back test
|
|
uut: work.uart_pkg.uart_top
|
|
generic map (g => g, baud => 115200, format => uart_8N1, use_fifo => use_fifo)
|
|
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;
|
|
|
---- UART Core -----------------------------------------------------------------
|
|
|
|
No newline at end of file
|
No newline at end of file
|