Задача
Принять байт с последовательной линии и выдать его с признаком готовности.
Тип схемы: Конечный автомат приёма плюс синхронизатор входной линии.
Модуль: uart_rx. Среда: Quartus, Vivado, ISE или свободный GHDL для моделирования.
Теорию по языку смотрите в статье VHDL — язык описания аппаратуры,
по самим микросхемам — на странице ПЛИС.
Алгоритм
- Обнаружить переход линии в ноль — начало старт-бита.
- Выждать половину битового интервала и убедиться, что это не помеха.
- Далее с шагом в полный интервал считывать восемь бит данных.
- Проверить стоп-бит и выдать байт с импульсом готовности.
Код на VHDL
Код проверен компилятором GHDL: проходит анализ и элаборацию без ошибок и предупреждений. Скачать: v17.vhd
--=====================================================
-- Проект 17. Приёмник UART
-- Ловит старт-бит, выбирает каждый бит в середине
-- интервала и выдаёт готовый байт с признаком valid.
--=====================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity uart_rx is
generic (
CLK_DIV : integer := 5208 -- 50 МГц / 9600 бод
);
port (
clk : in std_logic;
rst : in std_logic;
rx : in std_logic; -- линия приёма
data : out std_logic_vector(7 downto 0);
valid : out std_logic -- байт принят
);
end entity uart_rx;
architecture rtl of uart_rx is
type state_t is (IDLE, START_BIT, DATA_BITS, STOP_BIT);
signal state : state_t := IDLE;
signal sync : std_logic_vector(1 downto 0) := "11";
signal baud : integer range 0 to CLK_DIV := 0;
signal bit_idx : integer range 0 to 7 := 0;
signal shreg : std_logic_vector(7 downto 0) := (others => '0');
begin
process (clk, rst)
begin
if rst = '1' then
state <= IDLE;
valid <= '0';
sync <= "11";
elsif rising_edge(clk) then
sync <= sync(0) & rx; -- синхронизация входа
valid <= '0'; -- признак живёт один такт
case state is
when IDLE =>
if sync(1) = '0' then -- обнаружен старт-бит
baud <= 0;
state <= START_BIT;
end if;
when START_BIT =>
if baud = CLK_DIV/2 - 1 then -- смещаемся в середину бита
if sync(1) = '0' then
baud <= 0;
bit_idx <= 0;
state <= DATA_BITS;
else
state <= IDLE; -- ложное срабатывание
end if;
else
baud <= baud + 1;
end if;
when DATA_BITS =>
if baud = CLK_DIV - 1 then
baud <= 0;
shreg(bit_idx) <= sync(1); -- выборка в середине бита
if bit_idx = 7 then
state <= STOP_BIT;
else
bit_idx <= bit_idx + 1;
end if;
else
baud <= baud + 1;
end if;
when STOP_BIT =>
if baud = CLK_DIV - 1 then
baud <= 0;
state <= IDLE;
if sync(1) = '1' then -- стоп-бит корректен
data <= shreg;
valid <= '1';
end if;
else
baud <= baud + 1;
end if;
end case;
end if;
end process;
end architecture rtl;
Пояснения
- Выборка в середине бита — ключевой приём: он даёт максимальный запас по расхождению тактовых частот передатчика и приёмника.
- Проверка уровня в середине старт-бита отсеивает короткие помехи, которые иначе запустили бы ложный приём.
- Признак готовности живёт ровно один такт — это стандартный интерфейс для передачи данных дальше по схеме.
Что дальше
Следующие проекты — в разделе проекты на VHDL. Сравнить подход с языком Verilog можно в статье Verilog, а разобраться в устройстве самих кристаллов — в статье ПЛИС: FPGA и CPLD.