咱们好,这几天在各个论坛上,常常就有人在向我咨询根据FPGA的串口通讯代码,大部分都是在网上下载一个现成的代码,但是在运用中就遇到了各种问题,于是就发到了论坛上来求助。在阅读了他们的代码之后,我发现简直出自同一个版别(现在确定为特权同学的根据EPM240入门试验的代码)。他们在调试这个代码的时分,常常存在这样几个问题:1、部分人对该串口通讯模块彻底不了解,对每句话,乃至每个模块的功用都不了解;2、部分人选用最原始的画波形的方法来对该模块进行仿真,成果无法得到仿真成果;3、部分人不会运用modelsim对该规划进行仿真;4、绝大部分人不会编写testbench;5、下板测验无法进行正确的字符串收发。在公司内部,我将这种现象和几位教师沟通之后,夏宇闻教师主张我专门针对该代码写一个由原理到代码,由仿真到板级的调试笔记。争取用最浅显,也是最笨的方法,手把手的教会咱们来调试这个代码。
本调试笔记首要由五个部分组成:原始代码剖析;原始代码验证;对原始代码进行修正;对修正后的代码进行验证;对修正后的规划进行板级验证。每个部分,小梅哥都会用图文结合的方法,教咱们一步一步的来进行。
原始代码剖析
该代码来自小梅哥最崇拜的大神,特权同学。其时小梅哥也是看着特权同学的书和视频教程一步一步走过来的。特权同学的代码完结了单字节的收发测验,没有对接连字节的收发进行测验。特权同学其时也说过,这个仅仅一个简略的试验,离实践工业使用还有必定的间隔。考虑到论坛上许多小伙伴都期望能够完结接连字节的收发功用,因而小梅哥就在特权同学的代码进步行了修正。修正后的代码,输入时钟能够在必定范围内挑选恣意频率,现在现已支撑5种波特率挑选(9600、19200、38400、57600、115200),实践小梅哥还做过更高波特率的测验,现在实测在115200波特率的速率下能够完结超越9999999次接连无间断的收发。这儿,小梅哥首要将特权同学规划架构在这儿列出来,以给读者一个直观的形象。
由上图可知,特权同学的UART串口规划首要包含了四个模块:串口发送模块(my_uart_tx)、串口接纳模块(my_uart_rx)、串口接纳波特率发生器(speed_rx)和串口发送波特率发生器(speed_tx),其间,串口发送波特率发生器首要用来发生串口发送模块发送数据时所需的波特率时钟,串口接纳波特率发生器首要用来发生串口接纳模块接纳数据时的波特率时钟,串口发送模块首要担任在指定波特率的速率下将待发送字节发送出去,串口接纳模块则首要担任接纳来自其他设备发送过来的串口数据。my_uart_top模块即串口收发顶层模块完结了各个模块间的信号衔接功用,经过该顶层模块的衔接,完结了将串口接纳到的数据再发送出去的功用,即咱们测验串口通讯最常用的一种方法——回环测验。特权同学该试验的各个端口和内部信号的含义如表1所示:
该试验的内容为,串口接纳模块在检测到发送设备发送过来的数据开端位时,接纳中止信号置1,该信号则作为发动串口接纳波特率发送器的操控信号,然后在每个波特率时钟上升沿到来时读取串口接纳端口(rs232_rx)上的数据。一帧(字节)数据接纳完结后,接纳中止信号拉低,中止波特率发生器作业,接纳完结,体系进入等候状况,等候下一次的数据到来。
一起,接纳中止信号的下降沿也作为串口发送模块的发送使能信号,由于一旦接纳中止信号的下降沿呈现,就标明接纳模块完结了一次数据的接纳,此刻,就开端使能串口发送波特率发生器,串口发送模块则在波特率时钟的上升沿到来时顺次将接纳模块接纳到的数据的每一位(发送模块主动增加开端位和中止位)顺次发送出去,当数据发送完结之后,中止串口发送波特率发生器的使能,模块进入等候状况,等候下一次接纳中止信号下降沿的到来。
这儿,咱们首要对该规划的波特率发生器模块进行剖析。该模块相对简略,代码如下所示:
以下是代码片段:
1 module speed_select (
2 clk, rst_n ,
3 bps_start , clk_bps
4 );
5
6 input clk; // 50MHz
7 input rst_n ; //
8 input bps_start ; //
9 output clk_bps ; // clk_bps
10
11 /*
12 parameter bps9600 = 5207, // 9600bps
13 bps19200 = 2603, // 19200bps
14 bps38400 = 1301, // 38400bps
15 bps57600 = 867, // 57600bps
16 bps115200 = 433; // 115200bps
17
18 parameter bps9600_2 = 2603,
19 bps19200_2 = 1301,
20 bps38400_2 = 650,
21 bps57600_2 = 433,
22 bps115200_2 = 216;
23 */
24
25 //
26 `define BPS_PARA 5207 // 9600
27 `define BPS_PARA_2 2603 // 9600
28
29 reg[ 12 : 0] cnt ; //
30 reg clk_bps_r ; //
31
32 //———————————————————
33 reg[ 2 : 0] uart_ctrl ; // uart
34 //———————————————————
35
36 always @ ( posedge clk or negedge rst_n )
37 if(! rst_n ) cnt <= 13'd0 ;
38 else if(( cnt == `BPS_PARA ) || ! bps_start ) cnt <= 13'd0 ;
39 else cnt <= cnt+1'b1 ; //
40
41 always @ ( posedge clk or negedge rst_n )
42 if(! rst_n ) clk_bps_r <= 1'b0 ;
43 else if( cnt == `BPS_PARA_2 ) clk_bps_r <= 1'b1 ;
//clk_bps_r ,
44 else clk_bps_r <= 1'b0 ;
45
46 assign clk_bps = clk_bps_r ;
47
48 endmodule
该代码的12-22行用注释的方法告知了咱们,取得不同波特率时波特率分频计数器的值应该为多少,以及波特率计数器计数到一半时的值为多少(该值作为对信号的采样点,由于一般情况下,一位数据,在该位数据坚持时间的中心时间是最安稳的)。26行和27行界说的两个参数BPS_PARA和BPS_PARA_2别离便是波特率分频计数器的最大值和中心值。实践运用时,只需求根据你所需求的波特率,更改这两个参数的值即可 。例如,挑选波特率为9600bps时,设定BPS_PARA=5207,BPS_PARA_2=2603。关于这个值的核算,这儿暂时不提,后文会有告知。
36行至39行为波特率分频计数器的计数进程,即波特率发生模块没有被使能(! bps_start)或许计数器计数到BPS_PARA后则将计数器清零,其它情况下则每来一个时钟上升沿计数器自加1。经过此进程,则可得到相对精准的波特率守时。
41行至44行为数据采样时间的生成,上面提到过“一般情况下,一位数据,在该位数据坚持时间的中心时间是最安稳的”,因而这儿咱们在计数器计数到一半时,发生一个时钟周期的高脉冲,此脉冲作为采样数据的使能信号。