--
|
--
|
-- sc_uart.vhd
|
-- sc_uart.vhd
|
--
|
--
|
-- 8-N-1 serial interface
|
-- 8-N-1 serial interface
|
--
|
--
|
-- wr, rd should be one cycle long => trde, rdrf goes 0 one cycle later
|
-- wr, rd should be one cycle long => trde, rdrf goes 0 one cycle later
|
--
|
--
|
-- Author: Martin Schoeberl martin@jopdesign.com
|
-- Author: Martin Schoeberl martin@jopdesign.com
|
--
|
--
|
--
|
--
|
-- resources on ACEX1K30-3
|
-- resources on ACEX1K30-3
|
--
|
--
|
-- 100 LCs, max 90 MHz
|
-- 100 LCs, max 90 MHz
|
--
|
--
|
-- resetting rts with fifo_full-1 works with C program on pc
|
-- resetting rts with fifo_full-1 works with C program on pc
|
-- but not with javax.comm: sends some more bytes after deassert
|
-- but not with javax.comm: sends some more bytes after deassert
|
-- of rts (16 byte blocks regardless of rts).
|
-- of rts (16 byte blocks regardless of rts).
|
-- Try to stop with half full fifo.
|
-- Try to stop with half full fifo.
|
--
|
--
|
-- todo:
|
-- todo:
|
--
|
--
|
--
|
--
|
-- 2000-12-02 first working version
|
-- 2000-12-02 first working version
|
-- 2002-01-06 changed tdr and rdr to fifos.
|
-- 2002-01-06 changed tdr and rdr to fifos.
|
-- 2002-05-15 changed clkdiv calculation
|
-- 2002-05-15 changed clkdiv calculation
|
-- 2002-11-01 don't wait if read fifo is full, just drop the byte
|
-- 2002-11-01 don't wait if read fifo is full, just drop the byte
|
-- 2002-11-03 use threshold in fifo to reset rts
|
-- 2002-11-03 use threshold in fifo to reset rts
|
-- don't send if cts is '0'
|
-- don't send if cts is '0'
|
-- 2002-11-08 rx fifo to 20 characters and stop after 4
|
-- 2002-11-08 rx fifo to 20 characters and stop after 4
|
-- 2003-07-05 new IO standard, change cts/rts to neg logic
|
-- 2003-07-05 new IO standard, change cts/rts to neg logic
|
-- 2003-09-19 sync ncts in!
|
-- 2003-09-19 sync ncts in!
|
-- 2004-03-23 two stop bits
|
-- 2004-03-23 two stop bits
|
-- 2005-11-30 change interface to SimpCon
|
-- 2005-11-30 change interface to SimpCon
|
-- 2006-08-07 rxd input register with clk to avoid Quartus tsu violation
|
-- 2006-08-07 rxd input register with clk to avoid Quartus tsu violation
|
-- 2006-08-13 use 3 FFs for the rxd input at clk
|
-- 2006-08-13 use 3 FFs for the rxd input at clk
|
--
|
--
|
|
|
|
|
library ieee;
|
library ieee;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_1164.all;
|
use ieee.numeric_std.all;
|
use ieee.numeric_std.all;
|
|
|
use work.marca_pkg.all;
|
use work.marca_pkg.all;
|
use work.sc_pkg.all;
|
use work.sc_pkg.all;
|
|
|
entity sc_uart is
|
entity sc_uart is
|
|
|
generic (clock_freq : integer;
|
generic (clock_freq : integer;
|
baud_rate : integer;
|
baud_rate : integer;
|
txf_depth : integer; txf_thres : integer;
|
txf_depth : integer; txf_thres : integer;
|
rxf_depth : integer; rxf_thres : integer);
|
rxf_depth : integer; rxf_thres : integer);
|
|
|
port (clock : in std_logic;
|
port (clock : in std_logic;
|
reset : in std_logic;
|
reset : in std_logic;
|
|
|
-- SimpCon interface
|
-- SimpCon interface
|
input : in SC_IN;
|
input : in SC_IN;
|
output : out SC_OUT;
|
output : out SC_OUT;
|
|
|
intr : out std_logic;
|
intr : out std_logic;
|
|
|
txd : out std_logic;
|
txd : out std_logic;
|
rxd : in std_logic;
|
rxd : in std_logic;
|
ncts : in std_logic;
|
ncts : in std_logic;
|
nrts : out std_logic);
|
nrts : out std_logic);
|
|
|
end sc_uart;
|
end sc_uart;
|
|
|
architecture rtl of sc_uart is
|
architecture rtl of sc_uart is
|
|
|
component fifo is
|
component fifo is
|
|
|
generic (width : integer;
|
generic (width : integer;
|
depth : integer;
|
depth : integer;
|
thres : integer);
|
thres : integer);
|
|
|
port (clk : in std_logic;
|
port (clk : in std_logic;
|
reset : in std_logic;
|
reset : in std_logic;
|
|
|
din : in std_logic_vector(width-1 downto 0);
|
din : in std_logic_vector(width-1 downto 0);
|
dout : out std_logic_vector(width-1 downto 0);
|
dout : out std_logic_vector(width-1 downto 0);
|
|
|
rd : in std_logic;
|
rd : in std_logic;
|
wr : in std_logic;
|
wr : in std_logic;
|
|
|
empty : out std_logic;
|
empty : out std_logic;
|
full : out std_logic;
|
full : out std_logic;
|
half : out std_logic);
|
half : out std_logic);
|
end component;
|
end component;
|
|
|
--
|
--
|
-- signals for uart connection
|
-- signals for uart connection
|
--
|
--
|
signal ua_dout : std_logic_vector(7 downto 0);
|
signal ua_dout : std_logic_vector(7 downto 0);
|
signal ua_wr, tdre : std_logic;
|
signal ua_wr, tdre : std_logic;
|
signal ua_rd, rdrf : std_logic;
|
signal ua_rd, rdrf : std_logic;
|
|
|
type uart_tx_state_type is (s0, s1);
|
type uart_tx_state_type is (s0, s1);
|
signal uart_tx_state : uart_tx_state_type;
|
signal uart_tx_state : uart_tx_state_type;
|
|
|
signal tf_dout : std_logic_vector(7 downto 0); -- fifo out
|
signal tf_dout : std_logic_vector(7 downto 0); -- fifo out
|
signal tf_rd : std_logic;
|
signal tf_rd : std_logic;
|
signal tf_empty : std_logic;
|
signal tf_empty : std_logic;
|
signal tf_full : std_logic;
|
signal tf_full : std_logic;
|
signal tf_half : std_logic;
|
signal tf_half : std_logic;
|
|
|
signal ncts_buf : std_logic_vector(2 downto 0); -- sync in
|
signal ncts_buf : std_logic_vector(2 downto 0); -- sync in
|
signal tsr : std_logic_vector(9 downto 0); -- tx shift register
|
signal tsr : std_logic_vector(9 downto 0); -- tx shift register
|
signal tx_clk : std_logic;
|
signal tx_clk : std_logic;
|
|
|
type uart_rx_state_type is (s0, s1, s2);
|
type uart_rx_state_type is (s0, s1, s2);
|
signal uart_rx_state : uart_rx_state_type;
|
signal uart_rx_state : uart_rx_state_type;
|
|
|
signal rf_wr : std_logic;
|
signal rf_wr : std_logic;
|
signal rf_empty : std_logic;
|
signal rf_empty : std_logic;
|
signal rf_full : std_logic;
|
signal rf_full : std_logic;
|
signal rf_half : std_logic;
|
signal rf_half : std_logic;
|
|
|
signal rxd_reg : std_logic_vector(2 downto 0);
|
signal rxd_reg : std_logic_vector(2 downto 0);
|
signal rx_buf : std_logic_vector(2 downto 0); -- sync in, filter
|
signal rx_buf : std_logic_vector(2 downto 0); -- sync in, filter
|
signal rx_d : std_logic; -- rx serial data
|
signal rx_d : std_logic; -- rx serial data
|
signal rsr : std_logic_vector(9 downto 0); -- rx shift register
|
signal rsr : std_logic_vector(9 downto 0); -- rx shift register
|
signal rx_clk : std_logic;
|
signal rx_clk : std_logic;
|
signal rx_clk_ena : std_logic;
|
signal rx_clk_ena : std_logic;
|
|
|
signal rdrf_iena : std_logic;
|
signal rdrf_iena : std_logic;
|
signal tdre_iena : std_logic;
|
signal tdre_iena : std_logic;
|
|
|
constant clk16_cnt : integer := (clock_freq/baud_rate+8)/16-1;
|
constant clk16_cnt : integer := (clock_freq/baud_rate+8)/16-1;
|
|
|
begin
|
begin
|
|
|
output.rdy_cnt <= "00"; -- no wait states
|
output.rdy_cnt <= "00"; -- no wait states
|
output.rd_data(SC_REG_WIDTH-1 downto 8) <= std_logic_vector(to_unsigned(0, SC_REG_WIDTH-8));
|
output.rd_data(SC_REG_WIDTH-1 downto 8) <= std_logic_vector(to_unsigned(0, SC_REG_WIDTH-8));
|
|
|
--
|
--
|
-- The registered MUX is all we need for a SimpCon read.
|
-- The registered MUX is all we need for a SimpCon read.
|
-- The read data is stored in registered rd_data.
|
-- The read data is stored in registered rd_data.
|
--
|
--
|
process(clock, reset)
|
process(clock, reset)
|
begin
|
begin
|
|
|
if reset = RESET_ACTIVE then
|
if reset = RESET_ACTIVE then
|
output.rd_data(7 downto 0) <= (others => '0');
|
output.rd_data(7 downto 0) <= (others => '0');
|
elsif rising_edge(clock) then
|
elsif rising_edge(clock) then
|
|
|
ua_rd <= '0';
|
ua_rd <= '0';
|
if input.rd='1' then
|
if input.rd='1' then
|
-- that's our very simple address decoder
|
-- that's our very simple address decoder
|
if input.address(0)='0' then
|
if input.address(0)='0' then
|
output.rd_data(7 downto 0) <= "0000" & rdrf_iena & tdre_iena & rdrf & tdre;
|
output.rd_data(7 downto 0) <= "0000" & rdrf_iena & tdre_iena & rdrf & tdre;
|
else
|
else
|
output.rd_data(7 downto 0) <= ua_dout;
|
output.rd_data(7 downto 0) <= ua_dout;
|
ua_rd <= input.rd;
|
ua_rd <= input.rd;
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
if input.wr='1' then
|
if input.wr='1' then
|
if input.address(0)='0' then
|
if input.address(0)='0' then
|
rdrf_iena <= input.wr_data(3);
|
rdrf_iena <= input.wr_data(3);
|
tdre_iena <= input.wr_data(2);
|
tdre_iena <= input.wr_data(2);
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
end if;
|
end if;
|
|
|
end process;
|
end process;
|
|
|
ua_wr <= input.wr and input.address(0);
|
ua_wr <= input.wr and input.address(0);
|
|
|
intr <= (rdrf and rdrf_iena) or (tdre and tdre_iena);
|
intr <= (rdrf and rdrf_iena) or (tdre and tdre_iena);
|
|
|
--
|
--
|
-- serial clock
|
-- serial clock
|
--
|
--
|
process(clock, reset)
|
process(clock, reset)
|
|
|
variable clk16 : integer range 0 to clk16_cnt;
|
variable clk16 : integer range 0 to clk16_cnt;
|
variable clktx : unsigned(3 downto 0);
|
variable clktx : unsigned(3 downto 0);
|
variable clkrx : unsigned(3 downto 0);
|
variable clkrx : unsigned(3 downto 0);
|
|
|
begin
|
begin
|
if reset = RESET_ACTIVE then
|
if reset = RESET_ACTIVE then
|
clk16 := 0;
|
clk16 := 0;
|
clktx := "0000";
|
clktx := "0000";
|
clkrx := "0000";
|
clkrx := "0000";
|
tx_clk <= '0';
|
tx_clk <= '0';
|
rx_clk <= '0';
|
rx_clk <= '0';
|
rx_buf <= "111";
|
rx_buf <= "111";
|
|
|
elsif rising_edge(clock) then
|
elsif rising_edge(clock) then
|
|
|
rxd_reg(0) <= rxd; -- to avoid setup timing error in Quartus
|
rxd_reg(0) <= rxd; -- to avoid setup timing error in Quartus
|
rxd_reg(1) <= rxd_reg(0);
|
rxd_reg(1) <= rxd_reg(0);
|
rxd_reg(2) <= rxd_reg(1);
|
rxd_reg(2) <= rxd_reg(1);
|
|
|
if (clk16=clk16_cnt) then -- 16 x serial clock
|
if (clk16=clk16_cnt) then -- 16 x serial clock
|
clk16 := 0;
|
clk16 := 0;
|
--
|
--
|
-- tx clock
|
-- tx clock
|
--
|
--
|
clktx := clktx + 1;
|
clktx := clktx + 1;
|
if (clktx="0000") then
|
if (clktx="0000") then
|
tx_clk <= '1';
|
tx_clk <= '1';
|
else
|
else
|
tx_clk <= '0';
|
tx_clk <= '0';
|
end if;
|
end if;
|
--
|
--
|
-- rx clock
|
-- rx clock
|
--
|
--
|
if (rx_clk_ena='1') then
|
if (rx_clk_ena='1') then
|
clkrx := clkrx + 1;
|
clkrx := clkrx + 1;
|
if (clkrx="1000") then
|
if (clkrx="1000") then
|
rx_clk <= '1';
|
rx_clk <= '1';
|
else
|
else
|
rx_clk <= '0';
|
rx_clk <= '0';
|
end if;
|
end if;
|
else
|
else
|
clkrx := "0000";
|
clkrx := "0000";
|
end if;
|
end if;
|
--
|
--
|
-- sync in filter buffer
|
-- sync in filter buffer
|
--
|
--
|
rx_buf(0) <= rxd_reg(2);
|
rx_buf(0) <= rxd_reg(2);
|
rx_buf(2 downto 1) <= rx_buf(1 downto 0);
|
rx_buf(2 downto 1) <= rx_buf(1 downto 0);
|
else
|
else
|
clk16 := clk16 + 1;
|
clk16 := clk16 + 1;
|
tx_clk <= '0';
|
tx_clk <= '0';
|
rx_clk <= '0';
|
rx_clk <= '0';
|
end if;
|
end if;
|
|
|
end if;
|
end if;
|
|
|
end process;
|
end process;
|
|
|
--
|
--
|
-- transmit fifo
|
-- transmit fifo
|
--
|
--
|
cmp_tf: fifo generic map (8, txf_depth, txf_thres)
|
cmp_tf: fifo generic map (8, txf_depth, txf_thres)
|
port map (clock, reset, input.wr_data(7 downto 0), tf_dout, tf_rd, ua_wr, tf_empty, tf_full, tf_half);
|
port map (clock, reset, input.wr_data(7 downto 0), tf_dout, tf_rd, ua_wr, tf_empty, tf_full, tf_half);
|
|
|
txd <= tsr(0);
|
txd <= tsr(0);
|
tdre <= not tf_full;
|
tdre <= not tf_full;
|
|
|
--
|
--
|
-- state machine for actual shift out
|
-- state machine for actual shift out
|
--
|
--
|
process(clock, reset)
|
process(clock, reset)
|
|
|
variable i : integer range 0 to 11;
|
variable i : integer range 0 to 11;
|
|
|
begin
|
begin
|
|
|
if reset = RESET_ACTIVE then
|
if reset = RESET_ACTIVE then
|
uart_tx_state <= s0;
|
uart_tx_state <= s0;
|
tsr <= "1111111111";
|
tsr <= "1111111111";
|
tf_rd <= '0';
|
tf_rd <= '0';
|
ncts_buf <= "111";
|
ncts_buf <= "111";
|
|
|
elsif rising_edge(clock) then
|
elsif rising_edge(clock) then
|
|
|
ncts_buf(0) <= ncts;
|
ncts_buf(0) <= ncts;
|
ncts_buf(2 downto 1) <= ncts_buf(1 downto 0);
|
ncts_buf(2 downto 1) <= ncts_buf(1 downto 0);
|
|
|
case uart_tx_state is
|
case uart_tx_state is
|
|
|
when s0 =>
|
when s0 =>
|
i := 0;
|
i := 0;
|
if (tf_empty='0' and ncts_buf(2)='0') then
|
if (tf_empty='0' and ncts_buf(2)='0') then
|
uart_tx_state <= s1;
|
uart_tx_state <= s1;
|
tsr <= tf_dout & '0' & '1';
|
tsr <= tf_dout & '0' & '1';
|
tf_rd <= '1';
|
tf_rd <= '1';
|
end if;
|
end if;
|
|
|
when s1 =>
|
when s1 =>
|
tf_rd <= '0';
|
tf_rd <= '0';
|
if (tx_clk='1') then
|
if (tx_clk='1') then
|
tsr(9) <= '1';
|
tsr(9) <= '1';
|
tsr(8 downto 0) <= tsr(9 downto 1);
|
tsr(8 downto 0) <= tsr(9 downto 1);
|
i := i+1;
|
i := i+1;
|
if (i=11) then -- two stop bits
|
if (i=11) then -- two stop bits
|
uart_tx_state <= s0;
|
uart_tx_state <= s0;
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
end case;
|
end case;
|
end if;
|
end if;
|
|
|
end process;
|
end process;
|
|
|
--
|
--
|
-- receive fifo
|
-- receive fifo
|
--
|
--
|
cmp_rf: fifo generic map (8, rxf_depth, rxf_thres)
|
cmp_rf: fifo generic map (8, rxf_depth, rxf_thres)
|
port map (clock, reset, rsr(8 downto 1), ua_dout, ua_rd, rf_wr, rf_empty, rf_full, rf_half);
|
port map (clock, reset, rsr(8 downto 1), ua_dout, ua_rd, rf_wr, rf_empty, rf_full, rf_half);
|
|
|
rdrf <= not rf_empty;
|
rdrf <= not rf_empty;
|
nrts <= rf_half; -- glitches even on empty fifo!
|
nrts <= rf_half; -- glitches even on empty fifo!
|
|
|
--
|
--
|
-- filter rxd
|
-- filter rxd
|
--
|
--
|
with rx_buf select
|
with rx_buf select
|
rx_d <= '0' when "000",
|
rx_d <= '0' when "000",
|
'0' when "001",
|
'0' when "001",
|
'0' when "010",
|
'0' when "010",
|
'1' when "011",
|
'1' when "011",
|
'0' when "100",
|
'0' when "100",
|
'1' when "101",
|
'1' when "101",
|
'1' when "110",
|
'1' when "110",
|
'1' when "111",
|
'1' when "111",
|
'X' when others;
|
'X' when others;
|
|
|
--
|
--
|
-- state machine for actual shift in
|
-- state machine for actual shift in
|
--
|
--
|
process(clock, reset)
|
process(clock, reset)
|
|
|
variable i : integer range 0 to 10;
|
variable i : integer range 0 to 10;
|
|
|
begin
|
begin
|
|
|
if reset = RESET_ACTIVE then
|
if reset = RESET_ACTIVE then
|
uart_rx_state <= s0;
|
uart_rx_state <= s0;
|
rsr <= "0000000000";
|
rsr <= "0000000000";
|
rf_wr <= '0';
|
rf_wr <= '0';
|
rx_clk_ena <= '0';
|
rx_clk_ena <= '0';
|
|
|
elsif rising_edge(clock) then
|
elsif rising_edge(clock) then
|
|
|
case uart_rx_state is
|
case uart_rx_state is
|
|
|
when s0 =>
|
when s0 =>
|
i := 0;
|
i := 0;
|
rf_wr <= '0';
|
rf_wr <= '0';
|
if (rx_d='0') then
|
if (rx_d='0') then
|
rx_clk_ena <= '1';
|
rx_clk_ena <= '1';
|
uart_rx_state <= s1;
|
uart_rx_state <= s1;
|
else
|
else
|
rx_clk_ena <= '0';
|
rx_clk_ena <= '0';
|
end if;
|
end if;
|
|
|
when s1 =>
|
when s1 =>
|
if (rx_clk='1') then
|
if (rx_clk='1') then
|
rsr(9) <= rx_d;
|
rsr(9) <= rx_d;
|
rsr(8 downto 0) <= rsr(9 downto 1);
|
rsr(8 downto 0) <= rsr(9 downto 1);
|
i := i+1;
|
i := i+1;
|
if (i=10) then
|
if (i=10) then
|
uart_rx_state <= s2;
|
uart_rx_state <= s2;
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
when s2 =>
|
when s2 =>
|
rx_clk_ena <= '0';
|
rx_clk_ena <= '0';
|
if rsr(0)='0' and rsr(9)='1' then
|
if rsr(0)='0' and rsr(9)='1' then
|
if rf_full='0' then -- if full just drop it
|
if rf_full='0' then -- if full just drop it
|
rf_wr <= '1';
|
rf_wr <= '1';
|
end if;
|
end if;
|
end if;
|
end if;
|
uart_rx_state <= s0;
|
uart_rx_state <= s0;
|
|
|
end case;
|
end case;
|
end if;
|
end if;
|
|
|
end process;
|
end process;
|
|
|
end rtl;
|
end rtl;
|
|
|