VHDL实现USART
本文主要介绍USART的VHDL实现。
1 串口时序
串口时序比较简单,如图所示。本文将串口状态分为四种:NONE、START、SHIFT和STOP状态。
2 分频器
分频模块比较简单,这里不过多介绍。简单说一下本文的设计思路,本文是产生16倍波特率的时钟,然后在每个码元周期内的第八个时钟的上升沿采集数据。分频系数计算公式如下:
ceof=时钟频率波特率⋅16ceof=\frac{ \text{时钟频率}}{{波特率·16}}ceof=波特率⋅16时钟频率
分频模块如下:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;entity frq_division is
generic(coef_div : integer:=651); --50M/4800/16=651
port( clk_50M : in std_logic;clk_usart : out std_logic);
end entity frq_division;architecture behavior of frq_division is
beginprocess(clk_50M)variable cnt : integer range 0 to coef_div;beginif clk_50M='1' and clk_50M'event thenif cnt=coef_div thencnt:=0;clk_usart<='1';elsecnt:=cnt+1;clk_usart<='0'; end if; end if;end process;
end behavior;
3 串口发送函数
这里,我是用状态机实现发送。我将发送过程分为四个子过程,分别是TX_NONE、TX_START、TX_SHIFT、TX_STOP。TX_NONE表示空闲状态,TX_START表示处于起始位状态,TX_SHIFT表述处于发送数据状态,TX_STOP表示处于停止位状态。
3.1 发送函数
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;entity tx is --奇校验
generic(data_bit : integer:=8; --8位数据位stop_bit : integer:=2); --2位停止位port( clk_usart : in std_logic; --串口时钟,是波特率的16倍tx_cmd : in std_logic; --发送控制位,持续一个码元周期,高有效tx_datain : in std_logic_vector(0 to data_bit-1); --传输数据tx_dataout : out std_logic; --串口发送tx_flag : out std_logic); --发送时为高,空闲为低end entity tx; architecture beheavior of tx is
type states is(TX_NONE,TX_START,TX_SHIFT,TX_STOP); --状态机状态
signal tx_odd_check : std_logic;
signal tx_data_reg : std_logic_vector(0 to data_bit); --数据位加校验位
beginprocess(clk_usart)variable cnt_clk : integer range 0 to 15:=0; --时钟计数variable cnt_bit : integer range 0 to 8:=0; --数据位计数variable state : states :=TX_NONE; --状态beginif clk_usart='1' and clk_usart'event thencase state is--空闲状态,等待发送when TX_NONE=> if tx_cmd='1' then state:=TX_START;tx_flag<='1'; --发送状态tx_data_reg<=tx_datain & (not(tx_datain(0) xor tx_datain(1) xor tx_datain(2) xor tx_datain(3) xor tx_datain(4) xor tx_datain(5) xor tx_datain(6) xor tx_datain(7)));--校验码计算elsestate:=TX_NONE;cnt_clk:=0;cnt_bit:=0;tx_dataout<='1'; --空闲为高tx_flag<='0'; --空闲状态end if;--起始状态,准备发送数据when TX_START=> if cnt_clk=15 thencnt_clk:=0;state:=TX_SHIFT;elsecnt_clk:=cnt_clk+1;tx_dataout<='0'; --起始位发送end if;--发送数据状态when TX_SHIFT=>if cnt_clk=15 thencnt_clk:=0;if cnt_bit="1000" thenstate:=TX_STOP;cnt_bit:=0;elsecnt_bit:=cnt_bit+1;end if;tx_data_reg<=tx_data_reg(1 to 8)&'0';elsecnt_clk:=cnt_clk+1;tx_dataout<=tx_data_reg(0);end if;--停止位状态,2位停止位when TX_STOP=>if cnt_clk=15 thencnt_clk:=0;if cnt_bit=1 thenstate:=TX_NONE;cnt_bit:=0;tx_flag<='0';elsecnt_bit:=cnt_bit+1;end if;elsecnt_clk:=cnt_clk+1;tx_dataout<='1';end if;end case;end if;end process;
end beheavior;
3.2 测试函数
library ieee;
use ieee.STD_LOGIC_UNSIGNED.all;
use ieee.std_logic_1164.all;-- Add your library and packages declaration here ...entity tx_tb is-- Generic declarations of the tested unitgeneric(data_bit : INTEGER := 8;stop_bit : INTEGER := 2 );
end tx_tb;architecture TB_ARCHITECTURE of tx_tb is-- Component declaration of the tested unitcomponent txgeneric(data_bit : INTEGER := 8;stop_bit : INTEGER := 2 );port(clk_usart : in STD_LOGIC;tx_cmd : in STD_LOGIC;tx_datain : in STD_LOGIC_VECTOR(0 to 7);tx_dataout : out STD_LOGIC;tx_flag : out STD_LOGIC );end component;-- Stimulus signals - signals mapped to the input and inout ports of tested entitysignal clk_usart : STD_LOGIC;signal tx_cmd : STD_LOGIC;signal tx_datain : STD_LOGIC_VECTOR(0 to 7);-- Observed signals - signals mapped to the output ports of tested entitysignal tx_dataout : STD_LOGIC;signal tx_flag : STD_LOGIC;-- Add your code here ...begin-- Unit Under Test port mapUUT : txgeneric map (data_bit => data_bit,stop_bit => stop_bit)port map (clk_usart => clk_usart,tx_cmd => tx_cmd,tx_datain => tx_datain,tx_dataout => tx_dataout,tx_flag => tx_flag);process --时钟beginwait for 10us;clk_usart<='1';wait for 10us;clk_usart<='0'; end process;process --数据beginwait for 200us;tx_datain<="11110000";wait;end process;process --数据beginwait for 210us;tx_cmd<='1';wait for 20us;tx_cmd<='0';wait;end process;end TB_ARCHITECTURE;configuration TESTBENCH_FOR_tx of tx_tb isfor TB_ARCHITECTUREfor UUT : txuse entity work.tx(beheavior);end for;end for;
end TESTBENCH_FOR_tx;
3.3 测试结果
4 串口接收函数
接收函数与发送函数类似,这里不一一赘述。
4.1 接收函数
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all;entity rx is
generic(data_bit : integer:=8; --8位数据位stop_bit : integer:=2); --2位停止位
port( clk_usart : in std_logic; --串口时钟rx_datain : in std_logic; --串口接受数据rx_flag : out std_logic; --接收完成标志位,高有效rx_dataout : out std_logic_vector(0 to data_bit-1)); --接收数据
end entity rx;architecture beheavior of rx is
type states is (RX_NONE,RX_START,RX_SHIFT,RX_STOP); --状态机状态
signal rx_data_reg : std_logic_vector(0 to data_bit);
beginprocess(clk_usart)variable cnt_clk : integer range 0 to 15:=0;variable cnt_bit : integer range 0 to data_bit:=0; variable state : states :=RX_NONE;beginif clk_usart='1' and clk_usart'event thencase state is --等待状态,等待接收when RX_NONE=>--if rx_datain='0' and rx_datain'event thenif rx_datain='0' thenstate:=RX_START;elsestate:=RX_NONE;rx_flag<='0';rx_dataout<="00000000";cnt_clk:=0;cnt_bit:=0;end if;--准备接收状态,准备开始接收数据when RX_START=>if cnt_clk=15 thencnt_clk:=0;state:=RX_SHIFT;elsif cnt_clk=7 thencnt_clk:=cnt_clk+1;if rx_datain/='0' thenstate:=RX_NONE;end if;else cnt_clk:=cnt_clk+1;end if;--数据接收中when RX_SHIFT=>if cnt_clk=15 then cnt_clk:=0;if cnt_bit=8 thencnt_bit:=0;if (not(rx_data_reg(0) xor rx_data_reg(1) xor rx_data_reg(2) xor rx_data_reg(3) xor rx_data_reg(4) xor rx_data_reg(5) xor rx_data_reg(6) xor rx_data_reg(7)))=rx_data_reg(8) thenstate:=RX_STOP;elsestate:=RX_NONE;end if;elsecnt_bit:=cnt_bit+1;end if;elsif cnt_clk=7 thencnt_clk:=cnt_clk+1;rx_data_reg<=rx_data_reg(1 to 8)&rx_datain;elsecnt_clk:=cnt_clk+1; end if;--接收停止位when RX_STOP=>if cnt_clk=15 thencnt_clk:=0;if cnt_bit=1 thencnt_bit:=0;state:=RX_NONE;rx_flag<='1';end if;cnt_bit:=cnt_bit+1;elsif cnt_clk=7 thencnt_clk:=cnt_clk+1;if rx_datain='1' thenif cnt_bit=1 thenrx_dataout<=rx_data_reg(0 to data_bit-1);end if;else state:=RX_NONE;end if;elsecnt_clk:=cnt_clk+1;end if;end case;end if;end process;
end beheavior;
4.2 测试程序
library ieee;
use ieee.STD_LOGIC_UNSIGNED.all;
use ieee.std_logic_1164.all;-- Add your library and packages declaration here ...entity rx_tb is-- Generic declarations of the tested unitgeneric(data_bit : INTEGER := 8;stop_bit : INTEGER := 2 );
end rx_tb;architecture TB_ARCHITECTURE of rx_tb is-- Component declaration of the tested unitcomponent rxgeneric(data_bit : INTEGER := 8;stop_bit : INTEGER := 2 );port(clk_usart : in STD_LOGIC;rx_datain : in STD_LOGIC;rx_flag : out STD_LOGIC;rx_dataout : out STD_LOGIC_VECTOR(0 to data_bit-1) );end component;-- Stimulus signals - signals mapped to the input and inout ports of tested entitysignal clk_usart : STD_LOGIC;signal rx_datain : STD_LOGIC;-- Observed signals - signals mapped to the output ports of tested entitysignal rx_flag : STD_LOGIC;signal rx_dataout : STD_LOGIC_VECTOR(0 to data_bit-1);-- Add your code here ...begin-- Unit Under Test port mapUUT : rxgeneric map (data_bit => data_bit,stop_bit => stop_bit)port map (clk_usart => clk_usart,rx_datain => rx_datain,rx_flag => rx_flag,rx_dataout => rx_dataout);process --时钟beginclk_usart<='1';wait for 500ns;clk_usart<='0';wait for 500ns; end process;process --时钟begin--第一个数据测试rx_datain<='1';wait for 50us;--起始位rx_datain<='0';wait for 16us;--数据位rx_datain<='1';wait for 4*16us;rx_datain<='0';wait for 4*16us;rx_datain<='1';wait for 1*16us;--停止位rx_datain<='1';wait for 2*16us;--第二个数据测试rx_datain<='1';wait for 48us;--起始位rx_datain<='0';wait for 16us;--数据位rx_datain<='0';wait for 16us;rx_datain<='1';wait for 16us;rx_datain<='0';wait for 16us;rx_datain<='1';wait for 16us;rx_datain<='0';wait for 16us;rx_datain<='1';wait for 16us;rx_datain<='0';wait for 16us;rx_datain<='1';wait for 16us;rx_datain<='1';wait for 16us;--停止位rx_datain<='1';wait for 2*16us;wait;--结束end process;end TB_ARCHITECTURE;configuration TESTBENCH_FOR_rx of rx_tb isfor TB_ARCHITECTUREfor UUT : rxuse entity work.rx(beheavior);end for;end for;
end TESTBENCH_FOR_rx;
4.3 测试结果
5 顶层函数
5.1 顶层函数
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;entity top is
--类属参数
generic(data_bit : integer:=8; --数据位8bitcoef_div : integer:=651; --分频系数:4800baud--651,9600stop_bit : integer:=2); --停止位2bit
--接口定义
port( clk_50M : in std_logic; --时钟rx_bit : in std_logic; --接收信号tx_cmd : in std_logic; --发送控制位,高电平有效tx_data : in std_logic_vector(0 to 7); --需要发送的数据rx_flag : out std_logic; --接收标志位,表示接收完成,高定平有效rx_data : out std_logic_vector(0 to 7); --接收到的数据 tx_flag : out std_logic; --发送完成标志位,高定平有效 tx_bit : out std_logic); --发送信号
end top;
architecture beheavior of top is
signal clk_usart : std_logic;
--分频器元件例化
component frq_divisiongeneric(coef_div : integer:=651);port (clk_50M : in std_logic;clk_usart : out std_logic);
end component;
--发送模块元件例化
component TXgeneric(data_bit : integer:=8; --8位数据位stop_bit : integer:=2); --2位停止位port( clk_usart : in std_logic; --串口时钟,是波特率的16倍tx_cmd : in std_logic; --发送控制位,持续一个码元周期tx_datain : in std_logic_vector(0 to data_bit-1); --传输数据tx_dataout : out std_logic; --串口发送tx_flag : out std_logic); --发送时为高,空闲为低
end component;
--接收模块元件例化
component RXgeneric(data_bit : integer:=8; --8位数据位stop_bit : integer:=2); --2位停止位port( clk_usart : in std_logic; --串口时钟rx_datain : in std_logic; --串口接受数据rx_flag : out std_logic; --接收完成标志位,高有效rx_dataout : out std_logic_vector(0 to data_bit-1)); --接收数据
end component;
begin--分频器调用U1 : frq_divisiongeneric map(coef_div => coef_div)port map(clk_50M => clk_50M,clk_usart => clk_usart);--发送模块调用U2 : TXgeneric map(data_bit => data_bit,stop_bit => stop_bit) port map(clk_usart => clk_usart,tx_cmd => tx_cmd,tx_datain => tx_data,tx_dataout => tx_bit,tx_flag => tx_flag);--接收模块调用U3 : RXgeneric map(data_bit => data_bit,stop_bit => stop_bit)port map(clk_usart => clk_usart,rx_datain => rx_bit,rx_flag => rx_flag,rx_dataout => rx_data);
end beheavior;
5.2 测试程序
library ieee;
use ieee.STD_LOGIC_UNSIGNED.all;
use ieee.std_logic_1164.all;-- Add your library and packages declaration here ...entity rx_tb is-- Generic declarations of the tested unitgeneric(data_bit : INTEGER := 8;stop_bit : INTEGER := 2 );
end rx_tb;architecture TB_ARCHITECTURE of rx_tb is-- Component declaration of the tested unitcomponent rxgeneric(data_bit : INTEGER := 8;stop_bit : INTEGER := 2 );port(clk_usart : in STD_LOGIC;rx_datain : in STD_LOGIC;rx_flag : out STD_LOGIC;rx_dataout : out STD_LOGIC_VECTOR(0 to data_bit-1) );end component;-- Stimulus signals - signals mapped to the input and inout ports of tested entitysignal clk_usart : STD_LOGIC;signal rx_datain : STD_LOGIC;-- Observed signals - signals mapped to the output ports of tested entitysignal rx_flag : STD_LOGIC;signal rx_dataout : STD_LOGIC_VECTOR(0 to data_bit-1);-- Add your code here ...begin-- Unit Under Test port mapUUT : rxgeneric map (data_bit => data_bit,stop_bit => stop_bit)port map (clk_usart => clk_usart,rx_datain => rx_datain,rx_flag => rx_flag,rx_dataout => rx_dataout);process --时钟beginclk_usart<='1';wait for 500ns;clk_usart<='0';wait for 500ns; end process;process --时钟begin--第一个数据测试rx_datain<='1';wait for 50us;--起始位rx_datain<='0';wait for 16us;--数据位rx_datain<='1';wait for 4*16us;rx_datain<='0';wait for 4*16us;rx_datain<='1';wait for 1*16us;--停止位rx_datain<='1';wait for 2*16us;--第二个数据测试rx_datain<='1';wait for 48us;--起始位rx_datain<='0';wait for 16us;--数据位rx_datain<='0';wait for 16us;rx_datain<='1';wait for 16us;rx_datain<='0';wait for 16us;rx_datain<='1';wait for 16us;rx_datain<='0';wait for 16us;rx_datain<='1';wait for 16us;rx_datain<='0';wait for 16us;rx_datain<='1';wait for 16us;rx_datain<='1';wait for 16us;--停止位rx_datain<='1';wait for 2*16us;wait;--结束end process;end TB_ARCHITECTURE;configuration TESTBENCH_FOR_rx of rx_tb isfor TB_ARCHITECTUREfor UUT : rxuse entity work.rx(beheavior);end for;end for;
end TESTBENCH_FOR_rx;
5.3 测试结果
6 上板验证
上板验证时,是将接收到的数据再次转发。所以,修改了顶层模块:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;entity USART is
--类属参数
generic(data_bit : integer:=8; --数据位8bit--计算式:分频系数=50M/波特率/16,这里串口时钟用的波特率的16倍,每个码元的中间取值coef_div : integer:=27; --分频系数:4800baud--651,9600baud--326,38400baud--81,115200baud--27stop_bit : integer:=2); --停止位2bit
--接口定义
port( clk_50M : in std_logic; --时钟rx_bit : in std_logic; --接收信号 tx_bit : out std_logic); --发送信号
end USART;
architecture beheavior of USART is
signal clk_usart : std_logic;
signal tx_cmd : std_logic; --发送控制位,高电平有效
signal tx_data : std_logic_vector(0 to 7); --需要发送的数据
signal rx_flag : std_logic; --接收标志位,表示接收完成,高定平有效
signal rx_data : std_logic_vector(0 to 7); --接收到的数据
signal tx_flag : std_logic; --发送完成标志位,高定平有效
--分频器元件例化
component frq_divisiongeneric(coef_div : integer:=651);port (clk_50M : in std_logic;clk_usart : out std_logic);
end component;
--发送模块元件例化
component TXgeneric(data_bit : integer:=8; --8位数据位stop_bit : integer:=2); --2位停止位port( clk_usart : in std_logic; --串口时钟,是波特率的16倍tx_cmd : in std_logic; --发送控制位,持续一个码元周期tx_datain : in std_logic_vector(0 to data_bit-1); --传输数据tx_dataout : out std_logic; --串口发送tx_flag : out std_logic); --发送时为高,空闲为低
end component;
--接收模块元件例化
component RXgeneric(data_bit : integer:=8; --8位数据位stop_bit : integer:=2); --2位停止位port( clk_usart : in std_logic; --串口时钟rx_datain : in std_logic; --串口接受数据rx_flag : out std_logic; --接收完成标志位,高有效rx_dataout : out std_logic_vector(0 to data_bit-1)); --接收数据
end component;
beginprocess(clk_usart)beginif tx_flag='0' and rx_flag='1' thentx_cmd<='1';tx_data<=rx_data;elsetx_cmd<='0';end if;end process;--分频器调用U1 : frq_divisiongeneric map(coef_div => coef_div)port map(clk_50M => clk_50M,clk_usart => clk_usart);--发送模块调用U2 : TXgeneric map(data_bit => data_bit,stop_bit => stop_bit) port map(clk_usart => clk_usart,tx_cmd => tx_cmd,tx_datain => tx_data,tx_dataout => tx_bit,tx_flag => tx_flag);--接收模块调用U3 : RXgeneric map(data_bit => data_bit,stop_bit => stop_bit)port map(clk_usart => clk_usart,rx_datain => rx_bit,rx_flag => rx_flag,rx_dataout => rx_data);
end beheavior;
硬件结果如下:
但是在测试时,出现一点问题:在长时间转发数据之后,会接收不到数据。这一点暂时还没有解决。
VHDL实现USART相关推荐
- GPIO,I2C,SPI,UART,USART,USB的区别
1.简单区别: 1) GPIO(General Purpose Input Output )为通用输入/输出,通用端口,总线扩展器, 利用工业标准I2C.SMBus™或SPI™接口简化了I/O口的扩展 ...
- 通讯接口:I2C和USART,SPI,CAN,USB2.0
文章目录 一.通讯接口介绍 二.I2C总线 三.通用同步/异步收发器USART 四.串行外设接口SPI 五.控制器区域网络CAN 六.串行通用总线USB 杨桃32学习笔记,本文图片文字皆为转述 一.通 ...
- Verilog以及VHDL所倡导的的代码准则
文章目录 写在前面 正文 前缀 关于大写的说明 关于初始化信号的注意事项 Xilinx related HDL coding guidelines Altera's Recommended HDL C ...
- FPGA/ASIC初学者应该学习Verilog还是VHDL?
博文目录 写在前面 正文 常识讨论 数据分析 写在最后 参考资料 交个朋友 写在前面 个人微信公众号: FPGA LAB 个人博客首页 正文 对于FPGA或者ASIC的初学者来说,选择哪种语言貌似应该 ...
- FPGA之道(38)VHDL与Verilog的比较
文章目录 前言 VHDL与Verilog的比较 语法比较 基本程序框架比较 端口定义比较 范围表示方法比较 元件调用与实例化比较 Process与always比较 标准逻辑类型比较 逻辑常量赋值比较 ...
- FPGA之道(31)VHDL编写注意事项
文章目录 前言 VHDL编写注意事项 大小写不敏感 VHDL中的关键字 多余的符号 纠结的downto 与to 数组范围混用 逻辑向量范围混用 范围中的变量 仿真雷区 进程敏感量表缺失 进程间语句顺序 ...
- FPGA之道(30)编写自己的vhdl库文件
文章目录 前言 编写自己的vhdl库文件 Work库 记录数据类型 子程序介绍 函数 过程 子程序使用总结 程序包 自定义程序包范例 前言 本文节选自<FPGA之道>来一起学习下高阶Ver ...
- FPGA之道(29)VHDL的串行语句
文章目录 前言 VHDL的串行语句 VHDL直接信号赋值语句 VHDL变量赋值语句 VHDL条件语句 优先级条件语句 无优先级条件语句 优先级条件语句与无优先级条件语句的对比 case-when的一些 ...
- FPGA之道(28)VHDL的并行语句
文章目录 前言 VHDL的并行语句 VHDL直接信号赋值语句 VHDL条件式信号设置语句 VHDL选择式信号设置语句 VHDL进程语句 时钟事件表示方法 纯组合process 纯时序process 具 ...
最新文章
- 图形驱动程序和显卡驱动什么区别_我们常说的计算机驱动程序到底是什么,深入解读驱动程序本质...
- 小米故事:凭什么把MIUI用户做到1亿 | PMcaff-干货
- 一步步编写操作系统 26 打开A20地址线
- Codeforces Round #263 (Div. 2) D. Appleman and Tree 树形dp
- mysql galera status_MySQL galera cluster集群的监控
- 使用sharepoint自带的文本编辑器2
- ctr z撤回反向_Ctrl+Z 的反快捷键是什么
- 在其他的电脑上配置绿色Jre+tomcat运行环境
- python自动化输入文本_快速掌握Python Selenium Web自动化:)四、使用Selenium在网页上进行操作...
- comsol光学仿真02
- java opencv 之车辆识别
- 2021年10月国产数据库大事记-墨天轮
- php中怎么合并单元格,phpword合并单元格
- 大陆车牌识别算法的背景与技术
- Java千百问_01基本概念(015)_阻塞、非阻塞有什么区别
- 2022年操作系统行业研究报告
- 遇到程序员不修改bug时怎么办?我教你
- kubectl常用命令大全详解
- 利用matlab实现光波(电磁波)的仿真
- 群晖NAS简介(转)