UART в VHDL и Verilog для FPGA

  1. Реализация VHDL:
  2. Реализация Verilog:

Вы знаете, как работает UART? Если нет, сначала освежите основы UART прежде чем продолжить. Рассматривали ли вы, как вы можете сэмплировать данные с помощью ПЛИС? Подумайте о данных, поступающих в вашу FPGA. Данные могут поступать самостоятельно или с часами. Когда это прибывает с часами, это - синхронный вызов. Когда он приходит без часов, он называется асинхронным. UART - это асинхронный интерфейс.

В любом асинхронном интерфейсе первое, что вам нужно знать, - это когда вы вовремя должны производить выборку (просмотр) данных. Если вы не сделаете выборку данных в нужное время, вы можете увидеть неправильные данные. Для правильного получения ваших данных передатчик и приемник должны согласовать скорость передачи данных. Скорость передачи данных - это скорость, с которой данные передаются. Например, 9600 бод означает 9600 бит в секунду. Приведенный ниже код использует универсальный в VHDL или параметр в Verilog, чтобы определить, сколько тактовых циклов есть в каждом бите. Так определяется скорость передачи.

ПЛИС постоянно выбирает линию. Как только он видит переход линии от высокого к низкому, он знает, что приходит слово данных UART. Этот первый переход указывает стартовый бит. Как только начало начального бита найдено, FPGA ожидает половину периода бита. Это обеспечивает выборку середины бита данных. С этого момента FPGA просто нужно подождать один битовый период (как определено скоростью передачи в бодах) и сэмплировать остальные данные. На рисунке ниже показано, как работает приемник UART внутри ПЛИС. Сначала в линии последовательных данных обнаруживается падающий фронт. Это представляет стартовый бит. Затем FPGA ожидает до середины первого бита данных и производит выборку данных. Это делается для всех восьми бит данных.

UART Последовательный поток данных

Приведенный выше поток данных показывает, как структурирован код ниже. Приведенный ниже код использует один стартовый бит, один стоповый бит, восемь битов данных и без проверки четности. Обратите внимание, что оба модуля передатчика имеют сигнал o_tx_active. Это используется для вывода буфер с тремя состояниями для полудуплексной связи , Это соответствует вашим конкретным требованиям проекта, если вы хотите создать полудуплексный UART или полнодуплексный UART. Код ниже будет работать для обоих!

Если вы хотите смоделировать свой код (и вам следует), вам нужно использовать испытательный стенд , К счастью, для вас уже создан тестовый стенд! В этом тестовом стенде ниже используется код передатчика и приемника. Он запрограммирован на работу в 115200 бод. Обратите внимание, что этот тестовый стенд предназначен только для моделирования и не может быть синтезирован в функциональный код ПЛИС.

Реализация VHDL:


Приемник VHDL (UART_RX.vhd):

-------------------------------------------------- -------------------- - Файл, скачанный с http://www.nandland.com ---------------- -------------------------------------------------- ---- - Этот файл содержит приемник UART. Этот приемник может - принимать 8 бит последовательных данных, один стартовый бит, один стоповый бит - и без бита четности. Когда прием завершен, o_rx_dv будет установлен на высокий такт за один такт. - - Установите общий g_CLKS_PER_BIT следующим образом: - g_CLKS_PER_BIT = (частота i_Clk) / (частота UART) - Пример: тактовая частота 10 МГц, UART 115200 бод - (10000000) / (115200) = 87 - библиотека IEEE; использовать ieee.std_logic_1164.ALL; использовать ieee.numeric_std.all; объект UART_RX является общим (g_CLKS_PER_BIT: integer: = 115 - должен быть установлен правильно); порт (i_Clk: in std_logic; i_RX_Serial: in std_logic; o_RX_DV: out std_logic; o_RX_Byte: out std_logic_vector (от 7 до 0)); конец UART_RX; архитектура rtl для UART_RX имеет тип t_SM_Main (s_Idle, s_RX_Start_Bit, s_RX_Data_Bits, s_RX_Stop_Bit, s_Cleanup); сигнал r_SM_Main: t_SM_Main: = s_Idle; сигнал r_RX_Data_R: std_logic: = '0'; сигнал r_RX_Data: std_logic: = '0'; сигнал r_Clk_Count: целочисленный диапазон от 0 до g_CLKS_PER_BIT-1: = 0; сигнал r_Bit_Index: целочисленный диапазон от 0 до 7: = 0; - Всего 8 битов сигнала r_RX_Byte: std_logic_vector (от 7 до 0): = (другие => '0'); сигнал r_RX_DV: std_logic: = '0'; begin - Цель: дважды зарегистрировать поступающие данные. - Это позволяет использовать его в часовом домене UART RX. - (Устраняет проблемы, вызванные метастабилией) p_SAMPLE: процесс (i_Clk) начинается, если восходящий_круг (i_Clk), тогда r_RX_Data_R r_RX_DV, если r_Clk_Count = (g_CLKS_PER_BIT-1) / 2, тогда, если r_RX_Data = '0___B_RIT_C_RIT_C_C_RC_C_C_R__C_C_R__C_RC_C -1 тактовый цикл для завершения Стоп-бита, если r_Clk_Count r_SM_Main r_SM_Main

VHDL-передатчик (UART_TX.vhd): ------------------------------------------ ---------------------------- - Файл, скачанный с http://www.nandland.com -------- -------------------------------------------------- ------------ - Этот файл содержит передатчик UART. Этот передатчик способен - передавать 8 бит последовательных данных, один стартовый бит, один стоповый бит, и без бита четности. Когда передача завершена, o_TX_Done будет установлен на высокий такт в течение одного тактового цикла. - - Установите общий g_CLKS_PER_BIT следующим образом: - g_CLKS_PER_BIT = (частота i_Clk) / (частота UART) - Пример: тактовая частота 10 МГц, UART 115200 бод - (10000000) / (115200) = 87 - библиотека IEEE; использовать ieee.std_logic_1164.all; использовать ieee.numeric_std.all; объект UART_TX является общим (g_CLKS_PER_BIT: integer: = 115 - должен быть установлен правильно); порт (i_Clk: в std_logic; i_TX_DV: в std_logic; i_TX_Byte: в std_logic_vector (7 до 0); o_TX_Active: вне std_logic; o_TX_Serial: вне std_logic; o_TX_Done: out) std конец UART_TX; RTL архитектуры UART_TX имеет тип t_SM_Main (s_Idle, s_TX_Start_Bit, s_TX_Data_Bits, s_TX_Stop_Bit, s_Cleanup); сигнал r_SM_Main: t_SM_Main: = s_Idle; сигнал r_Clk_Count: целочисленный диапазон от 0 до g_CLKS_PER_BIT-1: = 0; сигнал r_Bit_Index: целочисленный диапазон от 0 до 7: = 0; - Всего 8 битов сигнала r_TX_Data: std_logic_vector (от 7 до 0): = (другие => '0'); сигнал r_TX_Done: std_logic: = '0'; begin p_UART_TX: процесс (i_Clk) начинается, если восходящий_круг (i_Clk), то случай r_SM_Main - это когда s_Idle => o_TX_Active o_TX_Active o_TX_Serial o_TX_Serial o_TX_Active r_SM_Main

VHDL Testbench (UART_TB.vhd): ------------------------------------------ ---------------------------- - Файл, скачанный с http://www.nandland.com -------- -------------------------------------------------- ------------ библиотека ieee; использовать ieee.std_logic_1164.ALL; использовать ieee.numeric_std.all; сущность uart_tb является конечной uart_tb; поведение архитектуры uart_tb является компонентом uart_tx является универсальным (g_CLKS_PER_BIT: integer: = 115 - должен быть установлен правильно); порт (i_clk: в std_logic; i_tx_dv: в std_logic; i_tx_byte: в std_logic_vector (7 до 0); o_tx_active: out std_logic; o_tx_serial: out std_logic; o_tx_done: out std_logic) конечный компонент uart_tx; Компонент uart_rx является общим (g_CLKS_PER_BIT: integer: = 115 - должен быть установлен правильно); порт (i_clk: in std_logic; i_rx_serial: in std_logic; o_rx_dv: out std_logic; o_rx_byte: out std_logic_vector (от 7 до 0)); конечный компонент uart_rx; - В испытательном стенде используется тактовая частота 10 МГц. - Хотите подключиться к UART со скоростью 115200 бод - 10000000/115200 = 87 тактов на бит. константа c_CLKS_PER_BIT: целое число: = 87; константа c_BIT_PERIOD: время: = 8680 нс; сигнал r_CLOCK: std_logic: = '0'; сигнал r_TX_DV: std_logic: = '0'; сигнал r_TX_BYTE: std_logic_vector (от 7 до 0): = (другие => '0'); сигнал w_TX_SERIAL: std_logic; сигнал w_TX_DONE: std_logic; сигнал w_RX_DV: std_logic; сигнал w_RX_BYTE: std_logic_vector (от 7 до 0); сигнал r_RX_SERIAL: std_logic: = '1'; - Низкоуровневая процедура записи байтов UART_WRITE_BYTE (i_data_in: in std_logic_vector (7 downto 0); сигнал o_serial: out std_logic) начинается - Отправить стартовый бит o_serial c_CLKS_PER_BIT) карта портов (i_clk => r_CLOCK, i_T_VD) i_tx_byte => r_TX_BYTE, o_tx_active => open, o_tx_serial => w_TX_SERIAL, o_tx_done => w_TX_DONE); - Создание получателя UART UART_RX_INST: универсальная карта uart_rx (g_CLKS_PER_BIT => c_CLKS_PER_BIT), карта портов (i_clk => r_CLOCK, i_rx_serial => r_RX_SERIAL, o_rx_dv => w_RY_RX): r_CLOCK

Реализация Verilog:



Приемник Verilog (uart_rx.v): //////////////////////////////////////////////////////////// //////////////////////////// // Файл загружен с http://www.nandland.com //////// ////////////////////////////////////////////////// //////////// // Этот файл содержит приемник UART. Этот приемник может // принимать 8 битов последовательных данных, один стартовый бит, один стоповый бит, // и без четности. Когда прием завершен, o_rx_dv будет // установлен на высокий уровень за один такт. // // Устанавливаем параметр CLKS_PER_BIT следующим образом: // CLKS_PER_BIT = (Частота i_Clock) / (Частота UART) // Пример: тактовая частота 10 МГц, 115200 бод UART // (10000000) / (115200) = 87 модуль uart_rx # (параметр CLKS_PER_BIT) (вход i_Clock, вход i_Rx_Serial, выход o_Rx_DV, выход [7: 0] o_Rx_Byte); параметр s_IDLE = 3'b000; параметр s_RX_START_BIT = 3'b001; параметр s_RX_DATA_BITS = 3'b010; параметр s_RX_STOP_BIT = 3'b011; параметр s_CLEANUP = 3'b100; reg r_Rx_Data_R = 1'b1; reg r_Rx_Data = 1'b1; reg [7: 0] r_Clock_Count = 0; reg [2: 0] r_Bit_Index = 0; // всего 8 битов reg [7: 0] r_Rx_Byte = 0; reg r_Rx_DV = 0; reg [2: 0] r_SM_Main = 0; // Цель: дважды зарегистрировать поступающие данные. // Это позволяет использовать его в часовом домене UART RX. // (устраняет проблемы, вызванные метастабильностью) всегда @ (posedge i_Clock) begin r_Rx_Data_R

Verilog Transmitter (uart_tx.v): //////////////////////////////////////////////////////////////// //////////////////////////// // Файл загружен с http://www.nandland.com //////// ////////////////////////////////////////////////// //////////// // Этот файл содержит передатчик UART. Этот передатчик может // передавать 8 битов последовательных данных, один стартовый бит, один стоповый бит, // и без четности. Когда передача будет завершена, o_Tx_done будет // установлен на высокий уровень за один тактовый цикл. // // Устанавливаем параметр CLKS_PER_BIT следующим образом: // CLKS_PER_BIT = (Частота i_Clock) / (Частота UART) // Пример: тактовая частота 10 МГц, 115200 бод UART // (10000000) / (115200) = 87 модуль uart_tx # (параметр CLKS_PER_BIT) (вход i_Clock, вход i_Tx_DV, вход [7: 0] i_Tx_Byte, выход o_Tx_Active, выход reg o_Tx_Serial, выход o_Tx_Done); параметр s_IDLE = 3'b000; параметр s_TX_START_BIT = 3'b001; параметр s_TX_DATA_BITS = 3'b010; параметр s_TX_STOP_BIT = 3'b011; параметр s_CLEANUP = 3'b100; reg [2: 0] r_SM_Main = 0; reg [7: 0] r_Clock_Count = 0; reg [2: 0] r_Bit_Index = 0; reg [7: 0] r_Tx_Data = 0; reg r_Tx_Done = 0; reg r_Tx_Active = 0; всегда @ (posedge i_Clock) начинать дело (r_SM_Main) s_IDLE: начинать o_Tx_Serial

Verilog Testbench (uart_tb.v): //////////////////////////////////////////////////// //////////////////////////// // Файл загружен с http://www.nandland.com //////// ////////////////////////////////////////////////// //////////// // Этот испытательный стенд будет выполнять как UART Tx, так и Rx. // Он отправляет байт 0xAB через передатчик // Затем осуществляет прием, получая байт 0x3F `timescale 1ns / 10ps` include "uart_tx.v" `include" uart_rx.v "module uart_tb (); // Testbench использует тактовую частоту 10 МГц // Хотите подключиться к 115200 бод UART // 10000000/115200 = 87 тактов на бит. параметр c_CLOCK_PERIOD_NS = 100; параметр c_CLKS_PER_BIT = 87; параметр c_BIT_PERIOD = 8600; reg r_Clock = 0; reg r_Tx_DV = 0; провод w_Tx_Done; reg [7: 0] r_Tx_Byte = 0; reg r_Rx_Serial = 1; провод [7: 0] w_Rx_Byte; // принимает входной байт и сериализует его task UART_WRITE_BYTE; вход [7: 0] i_Data; целое число ii; begin // Отправить стартовый бит r_Rx_Serial

Помоги мне сделать отличный контент! Поддержи меня на Patreon! Купить Go Board!


Рассматривали ли вы, как вы можете сэмплировать данные с помощью ПЛИС?