本文为明德扬原创及录用文章,转载请注明出处!
案例编号:000900000024
1.1 总体设计
1.1.1 概述
串行接口简称串口,也称串行通讯接口或者串行通信接口,是采用串行通信方式的扩展接口。串行接口是指数据一位一位地顺序传送 ,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适合用于远距离通信,但传送速度较慢。一条信息的各位数据被逐位按顺序传送的通信方式称作串行通信。串行通信的特点是:数据位的传送,按位顺序进行,最少只需要一根传输线即可完成;成本低,但传送速度慢。串行通信的距离可以从几米到几千米;根据信息的传送方向,串行通信可以进一步分为单工、半双工和全双工三种。
串口的出现是在 1980 年前后,数据传输率是 115kbps~230kbps。串口出现的初期是为了实现连接计算机外设的目的,初期串口一般用来连接鼠标和外置 Modem 以及老式摄像头和写字板等设备。
串口也可以应用于两台计算机(或设备)之间的互联及数据传输。由于串口不支持热插拔及传输速率较低,部分新主板和大部分便携电脑已开始取消该接口。串口多用于工业控制和测量设备以及部分通信设备中。
1.1.2 设计目标
本练习要求实现串口回环功能,具体功能要求如下:
1、上位机于 FPGA 之间通过串口进行通信,规定波特率为 9600,数据位为 8bit,无奇偶校
验位,停止位为 1。
2、 FPGA 内部有一个可保存 128 字节的 FIFO。
3、 FPGA 从上位机接收到数据后,将数据保存到 FIFO 中。
4、当 FIFO 保存的数据超过 60 个数据时,启动发送数据操作:读取 FIFO 的数据,将数据返
回给上位机。
5、 在启动发送数据操作过程中,如果 FIFO 变空,结束发送操作,等待下一次的启动。
注意:上位机接收到的数据与发送的数据相同,不能多也不能少。
1.1.3 系统结构框图
系统结构框图如下所示:
1.1.4 模块功能
➢ 串口接收模块实现功能
1、 将输入数据进行同步化处理。
2、 解析串口时序,将有效数据进行串并转换。
➢ 数据处理模块实现功能
1、 包含一个 FIFO,用来存储接收到的数据。
2、 满足发送条件后,读出 FIFO 的数据送给下游模块。
➢ 串口发送模块实现功能
1、 将接收到的数据进行并串转换,发送给上位机。
1.1.5 顶层信号
1.1.6 参考代码
下面是本工程的顶层代码:
1. module com_prj(
2. rst_n ,
3. clk ,
4. rx_uart,
5. tx_uart
6. );
7. parameter BPS = 5208;
8.
9. input rst_n ;
10. input clk ;
11. input rx_uart ;
12. output tx_uart ;
13.
14. wire [7:0] uart_in ;
15. wire uart_in_vld ;
16. wire [7:0] uart_out ;
17. wire uart_out_vld;
18. wire rdy ;
19.
20. uart_rx#(.BPS(BPS)) uart_rx(
21. .clk (clk ),
22. .rst_n (rst_n ),
23. .din (rx_uart ),
24. .dout (uart_in ),
25. .dout_vld(uart_in_vld)
26. );
27. uart_tx#(.BPS(BPS)) uart_tx(
28. .clk (clk ),
29. .rst_n (rst_n ),
30. .din (uart_out ),
31. .din_vld (uart_out_vld),
32. .rdy (rdy ),
33. .dout (tx_uart )
34. );
35. data_handle u_data_handle(
36. .clk (clk ),
37. .rst_n (rst_n ),
38. .din (uart_in ),
39. .din_vld (uart_in_vld ),
40. .dout (uart_out ),
41. .dout_vld(uart_out_vld),
42. .rdy (rdy )
43. );
44.
45. endmodule
1.2 串口接收模块设计
1.2.1 接口信号
1.2.2 设计思路
➢ UART 异步串行口简介
数据通信的基本方式可分为并行通信和串行通信两种:
并行通信:
是指利用多条数据线将一个资料的各位同时传送。特点是传输速度快,适合用于短距
离通信,但要求通信速率较高的应用场合。
串行通信:
是指利用一条传输线将资料一位位的顺序传送。特点是通信线路简单,利用简单的线
缆就可以实现通信,减低成本,适用于远距离通信,但传输速度慢的应用场合。
在 FPGA 看来,串口只有两根线,一根线用于接收,一根线用于发送。
➢ UART 异步串行口的传输格式
异步通信以一个字符为传输单位,通信中两个字符间的时间间隔是不固定的,然而在同一个字符
中的两个相邻位代码间的时间间隔是固定的。
通信协议(通信规程):是指通信双方约定的一些规则。在使用异步串行口传送一个字符的信息
时,对资料格式有如下规定:规定有空闲位、起始位、数据位、奇偶校验位、停止位。通讯时序图如
下:
每一个数据位的宽度等于传送波特率的倒数。微机异步串行通信中,常用的波特率为 110,150,
300,600,1200,2400,4800,9600 ,19200,38400,115200 等。代表每个码元传输的速
率。在二进制数据传输中,波特率和比特率相同都是为每个比特数据传输的速率,其倒数为 1bit
数据的宽度,也就是 1bit 数据持续的时间。确定这一时间,就可用 FPGA 构造计数器实现比特
周期的延时,从而实现特定波特率的数据传输。
➢ 开始前,线路处于空闲状态,送出连续"1"。传送开始时首先发一个"0"作为起始位,
然后出现在通信线上的是字符的二进制编码数据。
➢ 每个字符的数据位长可以约定为 5 位、6 位、7 位或 8 位。
➢ 后面是奇偶校验位,顾名思义,检验位适用于数据校验。分为奇校验和偶校验。奇校验需
要保证传输数据总共有奇数个逻辑高电平,偶校验则需要保证传输数据有偶数个逻辑高电
平。即"奇偶"指的是数据中(包括该校验位)1 的个数。例如:传输的数据是 01000011,
如果校验方式是奇校验,则校验位为 0 ,若是偶校验则校验位是 1.传输中校验位不是必须
项,双方可以约定不要校验位,或者使用奇/偶校验方式。
➢ 最后是表示停止位的"1"信号,这个停止位可以约定持续 1 位、1.5 位或 2 位的时间宽
度。由于每台设备有其自己的时钟,很可能再通信中两台设备间出现小小的不同步。因此
停止位不仅仅是表示传输的结束,并且提供一个校正时钟同步的机会,让从机可以正确的
识别下一轮数据的起始位。停止位的位数越多,不同时钟同步的容忍程度就越大,但是数
据传输速率也越慢。
➢ 至此一个字符传送完毕,线路又进入空闲,持续为"1"。经过一段随机的时间后,下一个
字符开始传送才又发出起始位。
➢ 架构设计
上位机发送的数据会按照上图所示串口的时序图的顺序过来,因此我们需要按照其对应的格式进
行接收。发送 8 位数据 data 前,串口接收数据线会先变 0 并持续一段时间(起始位),然后发送 da
ta[0]、data[1],以此类推直至发送完 data[7],发送每位数据时都会持续一段时间,发送完毕后数据
线会变为 1 并持续一段时间(结束位)。至此,完成数据的发送。可以看出每段有效信号的开始前和
结束后,都会有特殊信号:有效数据开始前会有一段变 0 的信号,用以告知 FPGA 开始传送数据;
结束后会有一段变 1 的信号,告知 FPGA 此数据传送结束。
由上面时序分析可知,当我们检测到数据线从高电平(空闲位)变为低电平(起始位)就表示开
始数据的传输了,因此需要进行下降沿的检测,检测方法如下:
该检测方法主要利用 D 触发器打拍来实现。
din:输入串口数据。
din_ff0:输入串口数据经过一级缓存之后的信号,目的是为了将异步信号同步化。
din_ff1:输入串口数据经过二级缓存之后的信号,目的是为了减少亚稳态造成的影响。
din_ff2:输入串口数据经过三级缓存之后的信号,目的是为了检测信号的下降沿。
flag_add:接收状态指示信号,当在时钟上升沿检测到 din_ff1 等于 0 并且 din_ff2 等于 1 的时候
表示检测到了下降沿,此时将 flag_add 信号拉高,表示进入到接收状态。
根据串口时序,可以提出两个计数器的架构,如下图所示:
该架构由两个计数器组成:时钟计数器 cnt 和数据计数器 data_num。
时钟计数器 cnt:用于计数发送 1bit 数据所需要的时间,加一条件为 flag_add,表示进入接收状
态时就开始计数;结束条件为数 5208 个,开发板晶振时钟是 50M,对应周期为 20ns,每位数据的
持续时间为 104166ns/20ns=5208.3 个时钟周期,近似为 5208 个时钟周期。
数据计数器 data_num:用于对接收的每一比特数据进行计数,加一条件为 end_cnt,表示接收
到 1bit 的数据就加一;结束条件为数 9 个,一个起始位加上八个数据位,共 9 位,数完就清零。
➢ 注意事项
1、 串口接收模块中的数据计数器一定不要把停止位也数上去,否则在接受的数据的时
候会出错。感兴趣的同学可以使用 signaltap 抓取信号进行分析(仿真没有用)。
2、 由于工程中串口的每 1bit 数据传输所需要的时间是近似值,也就是存在误差,因此
串口接收在采集数据的时候,需要在数据的中间时刻进行采样,这样才能保证数据
的正确性。
1.2.3 参考代码
下面是使用明德扬的计数器模板等写出来的本模块代码。
1. always @ (posedge clk or negedge rst_n) begin
2. if(!rst_n) begin
3. din_ff0 <= 1'b1;
4. din_ff1 <= 1'b1;
5. din_ff2 <= 1'b1;
6. end
7. else begin
8. din_ff0 <= din;
9. din_ff1 <= din_ff0;
10. din_ff2 <= din_ff1;
11. end
12. end
13.
14. always @ (posedge clk or negedge rst_n)begin
15. if(!rst_n) begin
16. flag_add <= 1'b0;
17. end
18. else if(din_ff2 & ~din_ff1) begin
19. flag_add <= 1'b1;
20. end
21. else if(data_num==4'd8&&end_cnt) begin
22. flag_add <= 1'b0;
23. end
24. end
25.
26. always @ (posedge clk or negedge rst_n)begin
27. if(!rst_n)begin
28. cnt <= 0;
29. end
30. else if(add_cnt)begin
31. if(end_cnt)begin
32. cnt <= 0;
33. end
34. else begin
35. cnt <= cnt+1'b1;
36. end
37. end
38. else begin
39. cnt <= 0;
40. end
41. end
42. assign add_cnt = flag_add;
43. assign end_cnt = add_cnt && cnt == BPS-1;
44.
45.
46.
47. always @(posedge clk or negedge rst_n) begin
48. if (rst_n==0) begin
49. data_num <= 0;
50. end
51. else if(add_data_num) begin
52. if(end_data_num)
53. data_num <= 0;
54. else
55. data_num <= data_num+1 ;
56. end
57. end
58. assign add_data_num = end_cnt;
59. assign end_data_num = add_data_num && data_num == 9-1 ;
60.
61. always @ (posedge clk or negedge rst_n)begin
62. if(!rst_n) begin
63. dout <= 8'd0;
64. end
65. else if(add_cnt && cnt==BPS_P-1 && data_num!=0) begin
66. dout<={din,{dout[7:1]}};
67. end
68. else begin
69. dout<=dout;
70. end
71. end
72.
73. always @ (posedge clk or negedge rst_n)begin
74. if(!rst_n) begin
75. dout_vld <= 1'b0;
76. end
77. else if(add_data_num && data_num == 4'd8) begin
78. dout_vld <= 1'b1;
79. end
80. else begin
81. dout_vld <= 1'b0;
82. end
83. end
1.3 数据处理模块设计
1.3.1 接口信号
1.3.2 设计思路
➢ FIFO 原理简介
FIFO(first input first output),即先进先出的数据缓存器,本质上还是 RAM,与普通存储器的区别:没有外部读写地址线,这样使用起来非常简单。特点就是只能顺序写入数据,顺序读出数据,其数据地址由内部读写指针自动加一完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。
FIFO 根据读写时钟的区别,分为同步 FIFO 和异步 FIFO。同步 FIFO 读写共用一个相同的时钟;
异步 FIFO 读写可以使用不同的时钟。由于异步 FIFO 内部存在同步化电路,因此资源占用要比同步FIFO 大。
再 FPGA 中,FIFO 的使用主要有两种场合,应用于缓存和跨时钟域处理。FIFO 本身就是存储器,自然可以用作数据的缓存,只不过在选择上需要跟普通的 RAM 区分开。又由于异步 FIFO 的存
在,可以使得其写侧和读侧时钟不同,因此又可用作跨时钟域处理。
更多关于 FIFO 的内容,可以关注明德扬 FIFO 专题课,或者是明德扬免费的 FIFO 课程
➢ 架构设计
按照功能要求,本模块需要对接收的数据进行存储,当存够 60 个的时候开始发送,下面是本模
块的架构图。
本模块大致分为三部分:FIFO 的写控制电路、FIFO、FIFO 的读控制电路。
写控制电路:只要输入数据有效,就将数据写进 FIFO,主要信号为写数据 data 和写使能 wrreq。
FIFO ip 核:存储数据。
读控制电路:当 FIFO 内部有 60 个数据、FIFO 非空并且下游模块准备好接收数据的时候,就开
始读。主要信号为输出数据 q、FIFO 有效数据量指示信号 usedw、空指示信号 empty、读使能 rdreq。
1.3.3 参考代码
84. assign data = din ;
85. assign wrreq = din_vld ;
86.
87.
88. my_fifo u_my_fifo (
89. .clock(clk ),
90. .data (data ),
91. .rdreq(rdreq),
92. .wrreq(wrreq),
93. .empty(empty),
94. .q (q ),
95. .usedw(usedw)
96. );
97.
98.
99. always@(*)begin
100. if(rd_flag && empty==1'b0 && rdy)
101. rdreq = 1'b1;
102. else
103. rdreq = 1'b0;
104. end
105.
106. always@(posedge clk or negedge rst_n)begin
107. if(rst_n==1'b0)begin
108. rd_flag <= 1'b0;
109. end
110. else if(rd_flag==1'b0 && usedw>=60) begin
111. rd_flag <= 1'b1;
112. end
113. else if(rd_flag==1'b1 && empty)begin
114. rd_flag <= 1'b0;
115. end
116. end
117.
118. always @(posedge clk or negedge rst_n)begin
119. if(rst_n==1'b0)begin
120. dout <= 0;
121. end
122. else begin
123. dout <= q;
124. end
125. end
126.
127. always @(posedge clk or negedge rst_n)begin
128. if(rst_n==1'b0)begin
129. dout_vld <= 1'b0;
130. end
131. else begin
132. dout_vld <= rdreq;
133. End
134. end
1.4 串口发送模块设计
1.4.1 接口信号
1.4.2 设计思路
串口发送就是要按照串口的时序,对数据进行并串转换,在介绍架构之前,先来描述一下本模块
一些重要的信号的含义:
工作状态指示信号 tx_flag:初始状态为 0,表示处于空闲状态,当检测到输入数据有效的时候,
该信号变为 1,表示处于工作状态,当数据发送完之后,重新拉低,进入空闲状态,等待下一个数据的输入。
数据锁存信号 tx_data_tmp:位宽为 8bit,初始状态为 0,当模块处于空闲状态,并且输入数据
有效的时候,就接收输入的数据进行锁存。由于输入数据在串口发送的时间内都需要用到,因此为了防止数据发生变化,导致串口发送出现问题,所以引入此信号进行数据的锁存。
准备好接收指示信号 rdy:当接收到输入数据,或者模块处于发送状态的时候,此信号为 1,表
示不能接收数据,其他情况为 1,表示准备好接收数据了。由于上游模块数据输出速率要比串口发送模块发送的速度快得多,所以需要此信号来控制上游模块的输出,当串口发送模块收到有效数据的时候,需要立刻把此信号拉高,所以需要用组合逻辑产生。
我们可以得到两个计数器组成的计数器架构,如下图所示:
该架构由两个计数器组成:时钟计数器 cnt 和数据计数器 data_num。
时钟计数器 cnt:用于计数发送 1bit 数据所需要的时间,加一条件为 tx_flag,表示进入工作状态
时就开始计数;结束条件为数 5208 个,开发板晶振时钟是 50M,对应周期为 20ns,每位数据的持
续时间为 104166ns/20ns=5208.3 个时钟周期,近似为 5208 个时
温馨提示:明德扬2023推出了全新课程——逻辑设计基本功修炼课,降低学习FPGA门槛的同时,增加了学习的趣味性,并组织了考试赢积分活动
http://www.mdy-edu.com/ffkc/415.html
(点击→了解课程详情☝)感兴趣请联系易老师:13112063618(微信同步)