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.
This commit is contained in:
Paul Mathieu 2021-07-25 23:50:53 -07:00
parent 1d69c453a8
commit 112b593660

View File

@ -39,20 +39,23 @@ end uart;
architecture Behavioral of uart is architecture Behavioral of uart is
constant BAUD: positive := baudrate; constant BAUD: positive := baudrate;
constant SYSFREQ: natural := 100_000_000; 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); type Fifo is array(integer range <>) of std_logic_vector(7 downto 0);
---- all dffs below ---- all dffs below
signal rxfifo : Fifo(0 to 3); signal rxfifo : Fifo(0 to 7);
signal txfifo : Fifo(3 downto 0); signal txfifo : Fifo(3 downto 0);
signal rxcnt : unsigned(2 downto 0); signal rxcnt : unsigned(3 downto 0);
signal txcnt : unsigned(2 downto 0); signal txcnt : unsigned(3 downto 0);
signal sysclk : unsigned(9 downto 0); -- up to 868 counts signal rxclk : unsigned(15 downto 0); -- possibly down to 1525 baud
signal uarten : std_logic; -- 1 when state machines can do stuff 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 RxState_t is (IDLE, SHIFT_IN);
type TxState_t is (IDLE, SHIFT_OUT); type TxState_t is (IDLE, SHIFT_OUT);
@ -68,10 +71,14 @@ architecture Behavioral of uart is
signal txshiftcnt : unsigned(3 downto 0); signal txshiftcnt : unsigned(3 downto 0);
signal rxshiftcnt : 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 begin
-- rx process -- rx process
-- drives rxstate, rxpushed, rxshift, rxshiftcnt, rxfifo -- drives rxstate, rxpushed, rxshift, rxshiftcnt, rxfifo, clockoffset
process(clk, rst) process(clk, rst)
begin begin
if rst = '1' then if rst = '1' then
@ -79,17 +86,35 @@ begin
rxpushed <= '0'; rxpushed <= '0';
rxshift <= x"00"; rxshift <= x"00";
rxshiftcnt <= "0000"; 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"; rxfifo(i) <= x"00";
end loop; end loop;
elsif rising_edge(clk) then elsif rising_edge(clk) then
if uarten = '1' then if rxen = '1' and unsigned(rxsamplecount) < 2 then
rxpushed <= '0'; 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 case rxstate is
when IDLE => when IDLE =>
if rx_pin = '0' then -- start bit!! (hopefully) if rxpinprev(1) = '0' then -- start bit!! (hopefully)
rxshift <= x"00"; rxshift <= x"00";
rxshiftcnt <= "0000"; rxshiftcnt <= "0000";
rxstate <= SHIFT_IN; rxstate <= SHIFT_IN;
@ -97,8 +122,8 @@ begin
when SHIFT_IN => when SHIFT_IN =>
if rxshiftcnt = 8 then if rxshiftcnt = 8 then
-- by now we should be seeing the stop bit, check -- by now we should be seeing the stop bit, check
if rx_pin = '1' then -- all good, push away if rxpinprev(1) = '1' then -- all good, push away
for i in 2 downto 0 loop -- right to left for i in rxfifo'high - 1 downto rxfifo'low loop -- right to left
rxfifo(i + 1) <= rxfifo(i); rxfifo(i + 1) <= rxfifo(i);
end loop; end loop;
rxfifo(0) <= rxshift; rxfifo(0) <= rxshift;
@ -106,7 +131,7 @@ begin
end if; end if;
rxstate <= IDLE; -- either way, we're done rxstate <= IDLE; -- either way, we're done
else else
rxshift <= rx_pin & rxshift(7 downto 1); rxshift <= rxpinprev(1) & rxshift(7 downto 1);
rxshiftcnt <= rxshiftcnt + 1; rxshiftcnt <= rxshiftcnt + 1;
end if; end if;
end case; end case;
@ -125,7 +150,7 @@ begin
txshiftcnt <= "0000"; txshiftcnt <= "0000";
tx_pin <= '1'; tx_pin <= '1';
elsif rising_edge(clk) then elsif rising_edge(clk) then
if uarten = '1' then if txen = '1' then
txpopped <= '0'; txpopped <= '0';
case txstate is case txstate is
@ -153,37 +178,53 @@ begin
end if; end if;
end process; end process;
process(clk, rst) -- drives sysclk, uarten process(clk, rst) -- drives rxclk, rxen
begin begin
if rst = '1' then if rst = '1' then
sysclk <= to_unsigned(0, 10); rxclk <= (others => '0');
uarten <= '0'; rxen <= '0';
elsif rising_edge(clk) then elsif rising_edge(clk) then
if sysclk = to_unsigned(CLKCNT - 1, 10) then if unsigned(rxclk + clockoffset) = RXCLKCNT - 1 then
sysclk <= to_unsigned(0, 10); rxclk <= 0 - clockoffset;
uarten <= '1'; rxen <= '1';
else else
sysclk <= sysclk + 1; rxclk <= rxclk + 1;
uarten <= '0'; 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 if; end if;
end process; end process;
process(clk, rst) -- drives dout, rxcnt, txcnt, txfifo process(clk, rst) -- drives dout, rxcnt, txcnt, txfifo
variable txn: unsigned(2 downto 0); variable txn: unsigned(3 downto 0);
variable rxn: unsigned(2 downto 0); variable rxn: unsigned(3 downto 0);
variable txpopdone : std_logic := '0'; -- latch variable txpopdone : std_logic := '0'; -- latch
variable rxpushdone : std_logic := '0'; -- latch variable rxpushdone : std_logic := '0'; -- latch
begin begin
if rst = '1' then if rst = '1' then
for i in 0 to 3 loop for i in txfifo'low to txfifo'high loop
txfifo(i) <= x"00"; txfifo(i) <= x"00";
end loop; end loop;
rxcnt <= "000"; rxcnt <= (others => '0');
txcnt <= "000"; txcnt <= (others => '0');
txpopdone := '0'; txpopdone := '0';
rxpushdone := '0'; rxpushdone := '0';
@ -197,7 +238,7 @@ begin
if txpopped = '1' then if txpopped = '1' then
if txpopdone = '0' then if txpopdone = '0' then
-- shift our fifo to the right -- 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); txfifo(i) <= txfifo(i + 1);
end loop; end loop;
txpopdone := '1'; txpopdone := '1';
@ -226,7 +267,7 @@ begin
case addr is case addr is
when x"0000" => when x"0000" =>
if we = '1' then 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); txfifo(to_integer(txn)) <= din(7 downto 0);
txcnt <= txn + 1; txcnt <= txn + 1;
end if; end if;