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