新闻中心

EEPW首页 > 手机与无线通信 > 牛人业话 > 小梅哥和你一起深入学习FPGA之串口调试(一)(上)

小梅哥和你一起深入学习FPGA之串口调试(一)(上)

作者:时间:2015-11-19来源:网络收藏

  大家好,这几天在各个论坛上,经常就有人在向我咨询基于的串口通信代码,大部分都是在网上下载一个现成的代码,但是在使用中就遇到了各种问题,于是就发到了论坛上来求助。在阅读了他们的代码之后,我发现几乎出自同一个版本(目前确定为特权同学的基于EPM240入门实验的代码)。他们在调试这个代码的时候,经常存在这样几个问题:1、部分人对该串口通讯模块完全不理解,对每句话,甚至每个模块的功能都不理解;2、部分人采用最原始的画波形的方式来对该模块进行仿真,结果无法得到仿真结果;3、部分人不会使用modelsim对该设计进行仿真;4、绝大部分人不会编写testbench;5、下板测试无法进行正确的字符串收发。在公司内部,我将这种现象和几位老师交流之后,夏宇闻老师建议我专门针对该代码写一个由原理到代码,由仿真到板级的调试笔记。争取用最通俗,也是最笨的办法,手把手的教会大家来调试这个代码。

本文引用地址:http://www.eepw.com.cn/article/283059.htm

  本调试笔记主要由五个部分组成:原始代码分析;原始代码验证;对原始代码进行修改;对修改后的代码进行验证;对修改后的设计进行板级验证。每个部分,小梅哥都会用图文结合的方式,教大家一步一步的来进行。

  原始代码分析

  该代码来自小梅哥最崇拜的大神,特权同学。当时小梅哥也是看着特权同学的书和视频教程一步一步走过来的。特权同学的代码实现了单字节的收发测试,没有对连续字节的收发进行测试。特权同学当时也说过,这个只是一个简单的实验,离实际工业应用还有一定的距离。考虑到论坛上很多小伙伴都希望能够实现连续字节的收发功能,因此小梅哥就在特权同学的代码上进行了修改。修改后的代码,输入时钟可以在一定范围内选择任意频率,目前已经支持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行为数据采样时刻的生成,上面提到过“一般情况下,一位数据,在该位数据保持时间的中间时刻是最稳定的”,因此这里我们在计数器计数到一半时,产生一个时钟周期的高脉冲,此脉冲作为采样数据的使能信号。

 


上一页 1 2 3 下一页

关键词: FPGA 串口调试

评论


相关推荐

技术专区

关闭