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