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:
parent
1d69c453a8
commit
112b593660
101
uart/uart.vhdl
101
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;
|
||||
|
Loading…
Reference in New Issue
Block a user