From 112b593660e200cd54fc4357173feef3929b3ede Mon Sep 17 00:00:00 2001 From: Paul Mathieu Date: Sun, 25 Jul 2021 23:50:53 -0700 Subject: [PATCH] uart: RX adaptive clock and over sampling Now sampling 3 times per bit and shifting the clock accordingly. Had to split the rx and tx clocks. --- uart/uart.vhdl | 101 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 71 insertions(+), 30 deletions(-) diff --git a/uart/uart.vhdl b/uart/uart.vhdl index 0daa97e..662a58b 100644 --- a/uart/uart.vhdl +++ b/uart/uart.vhdl @@ -39,20 +39,23 @@ end uart; architecture Behavioral of uart is constant BAUD: positive := baudrate; constant SYSFREQ: natural := 100_000_000; - constant CLKCNT: natural := SYSFREQ / BAUD; + constant RXCLKCNT: natural := SYSFREQ / BAUD / 3; + constant TXCLKCNT: natural := SYSFREQ / BAUD; type Fifo is array(integer range <>) of std_logic_vector(7 downto 0); ---- all dffs below - signal rxfifo : Fifo(0 to 3); + signal rxfifo : Fifo(0 to 7); signal txfifo : Fifo(3 downto 0); - signal rxcnt : unsigned(2 downto 0); - signal txcnt : unsigned(2 downto 0); + signal rxcnt : unsigned(3 downto 0); + signal txcnt : unsigned(3 downto 0); - signal sysclk : unsigned(9 downto 0); -- up to 868 counts - signal uarten : std_logic; -- 1 when state machines can do stuff + signal rxclk : unsigned(15 downto 0); -- possibly down to 1525 baud + signal txclk : unsigned(15 downto 0); -- possibly down to 1525 baud + signal rxen : std_logic; -- 1 when rx state machine can do stuff + signal txen : std_logic; -- 1 when tx state machine can do stuff type RxState_t is (IDLE, SHIFT_IN); type TxState_t is (IDLE, SHIFT_OUT); @@ -68,10 +71,14 @@ architecture Behavioral of uart is signal txshiftcnt : unsigned(3 downto 0); signal rxshiftcnt : unsigned(3 downto 0); + + signal clockoffset : unsigned(15 downto 0); + signal rxpinprev: std_logic_vector(1 downto 0); + signal rxsamplecount: unsigned(2 downto 0); begin -- rx process - -- drives rxstate, rxpushed, rxshift, rxshiftcnt, rxfifo + -- drives rxstate, rxpushed, rxshift, rxshiftcnt, rxfifo, clockoffset process(clk, rst) begin if rst = '1' then @@ -79,17 +86,35 @@ begin rxpushed <= '0'; rxshift <= x"00"; rxshiftcnt <= "0000"; + clockoffset <= (others => '0'); + rxpinprev <= (others => '0'); + rxsamplecount <= (others => '0'); - for i in 0 to 3 loop + for i in rxfifo'low to rxfifo'high loop rxfifo(i) <= x"00"; end loop; elsif rising_edge(clk) then - if uarten = '1' then - rxpushed <= '0'; + if rxen = '1' and unsigned(rxsamplecount) < 2 then + rxpinprev(to_integer(unsigned(rxsamplecount))) <= rx_pin; + rxsamplecount <= rxsamplecount + 1; + end if; + if rxen = '1' and unsigned(rxsamplecount) = 2 then + rxpushed <= '0'; + rxsamplecount <= (others => '0'); + + if rxpinprev(0) /= rxpinprev(1) then + -- we are too fast, slow down + clockoffset <= clockoffset - RXCLKCNT / 2; + elsif rx_pin /= rxpinprev(1) then + -- we are too slow, speed up + clockoffset <= clockoffset + RXCLKCNT / 2; + end if; + + -- use rxpinprev(1) as the value case rxstate is when IDLE => - if rx_pin = '0' then -- start bit!! (hopefully) + if rxpinprev(1) = '0' then -- start bit!! (hopefully) rxshift <= x"00"; rxshiftcnt <= "0000"; rxstate <= SHIFT_IN; @@ -97,8 +122,8 @@ begin when SHIFT_IN => if rxshiftcnt = 8 then -- by now we should be seeing the stop bit, check - if rx_pin = '1' then -- all good, push away - for i in 2 downto 0 loop -- right to left + if rxpinprev(1) = '1' then -- all good, push away + for i in rxfifo'high - 1 downto rxfifo'low loop -- right to left rxfifo(i + 1) <= rxfifo(i); end loop; rxfifo(0) <= rxshift; @@ -106,7 +131,7 @@ begin end if; rxstate <= IDLE; -- either way, we're done else - rxshift <= rx_pin & rxshift(7 downto 1); + rxshift <= rxpinprev(1) & rxshift(7 downto 1); rxshiftcnt <= rxshiftcnt + 1; end if; end case; @@ -125,7 +150,7 @@ begin txshiftcnt <= "0000"; tx_pin <= '1'; elsif rising_edge(clk) then - if uarten = '1' then + if txen = '1' then txpopped <= '0'; case txstate is @@ -153,37 +178,53 @@ begin end if; end process; - process(clk, rst) -- drives sysclk, uarten + process(clk, rst) -- drives rxclk, rxen begin if rst = '1' then - sysclk <= to_unsigned(0, 10); - uarten <= '0'; + rxclk <= (others => '0'); + rxen <= '0'; elsif rising_edge(clk) then - if sysclk = to_unsigned(CLKCNT - 1, 10) then - sysclk <= to_unsigned(0, 10); - uarten <= '1'; + if unsigned(rxclk + clockoffset) = RXCLKCNT - 1 then + rxclk <= 0 - clockoffset; + rxen <= '1'; else - sysclk <= sysclk + 1; - uarten <= '0'; + rxclk <= rxclk + 1; + rxen <= '0'; + end if; + end if; + end process; + + process(clk, rst) -- drives txclk, rxen + begin + if rst = '1' then + txclk <= (others => '0'); + txen <= '0'; + elsif rising_edge(clk) then + if unsigned(txclk) = TXCLKCNT - 1 then + txclk <= (others => '0'); + txen <= '1'; + else + txclk <= txclk + 1; + txen <= '0'; end if; end if; end process; process(clk, rst) -- drives dout, rxcnt, txcnt, txfifo - variable txn: unsigned(2 downto 0); - variable rxn: unsigned(2 downto 0); + variable txn: unsigned(3 downto 0); + variable rxn: unsigned(3 downto 0); variable txpopdone : std_logic := '0'; -- latch variable rxpushdone : std_logic := '0'; -- latch begin if rst = '1' then - for i in 0 to 3 loop + for i in txfifo'low to txfifo'high loop txfifo(i) <= x"00"; end loop; - rxcnt <= "000"; - txcnt <= "000"; + rxcnt <= (others => '0'); + txcnt <= (others => '0'); txpopdone := '0'; rxpushdone := '0'; @@ -197,7 +238,7 @@ begin if txpopped = '1' then if txpopdone = '0' then -- shift our fifo to the right - for i in 0 to 2 loop -- 0 to 2 is right to left + for i in txfifo'high to txfifo'low - 1 loop -- 0 to 2 is right to left txfifo(i) <= txfifo(i + 1); end loop; txpopdone := '1'; @@ -226,7 +267,7 @@ begin case addr is when x"0000" => if we = '1' then - if to_integer(txn) < 4 then + if to_integer(txn) <= txfifo'low then txfifo(to_integer(txn)) <= din(7 downto 0); txcnt <= txn + 1; end if;