OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 19474|回复: 33

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

[复制链接]

40

主题

179

帖子

0

精华

高级会员

Rank: 4

积分
921
金钱
921
注册时间
2013-10-23
在线时间
94 小时
发表于 2014-12-26 21:53:10 | 显示全部楼层 |阅读模式

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

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

一、      原始代码分析

该代码来自小梅哥最崇拜的大神,特权同学。当时小梅哥也是看着特权同学的书和视频教程一步一步走过来的。特权同学的代码实现了单字节的收发测试,没有对连续字节的收发进行测试。特权同学当时也说过,这个只是一个简单的实验,离实际工业应用还有一定的距离。考虑到论坛上很多小伙伴都希望能够实现连续字节的收发功能,因此小梅哥就在特权同学的代码上进行了修改。修改后的代码,输入时钟可以在一定范围内选择任意频率,目前已经支持5种波特率选择(9600192003840057600115200),实际小梅哥还做过更高波特率的测试,目前实测在115200波特率的速率下可以实现超过9999999次连续无间断的收发。这里,小梅哥首先将特权同学设计架构在这里列出来,以给读者一个直观的印象。


由上图可知,特权同学的UART串口设计主要包含了四个模块:串口发送模块(my_uart_tx)、串口接收模块(my_uart_rx)、串口接收波特率发生器(speed_rx)和串口发送波特率发生器(speed_tx),其中,串口发送波特率发生器主要用来产生串口发送模块发送数据时所需的波特率时钟,串口接收波特率发生器主要用来产生串口接收模块接收数据时的波特率时钟,串口发送模块主要负责在指定波特率的速率下将待发送字节发送出去,串口接收模块则主要负责接收来自其他设备发送过来的串口数据。my_uart_top模块即串口收发顶层模块实现了各个模块间的信号连接功能,通过该顶层模块的连接,实现了将串口接收到的数据再发送出去的功能,即我们测试串口通信最常用的一种方式——回环测试。特权同学该实验的各个端口和内部信号的意义如表1所示:

 

所属模块

端口/信号名

端口/信号意义

顶层模块

my_uart_top

clk

系统时钟,本设计中,固定为50M频率的时钟

rst_n

系统复位,低电平复位

rs232_rx

串口接收端口

rs232_tx

串口发送端口

接收模块

my_uart_rx

clk

系统时钟,本设计中,固定为50M频率的时钟

rst_n

系统复位,低电平复位

rs232_rx

串口接收端口

rx_data

串口接收模块接收到的字节数据

rx_int

串口接收成功中断请求信号,为一表示正在接收数据

clk_bps

波特率时钟

bps_start

波特率发生模块使能信号

发送模块

my_uart_tx

clk

系统时钟,本设计中,固定为50M频率的时钟

rst_n

系统复位,低电平复位

rx_data

待发送的字节数据,来自串口接收模块

rx_int

串口接收成功中断请求信号

rs232_tx

串口发送端口

clk_bps

波特率时钟

bps_start

波特率发生模块使能信号

收发波特率发生模块

speed_tx/speed_rx

clk

系统时钟,本设计中,固定为50M频率的时钟

rst_n

系统复位,低电平复位

clk_bps

波特率时钟

bps_start

波特率发生模块使能信号

 

该实验的内容为,串口接收模块在检测到发送设备发送过来的数据起始位时,接收中断信号置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 rangered;font-family:"Courier New";">5207 // 9600

27 `define BPS_PARA_2 rangered;font-family:"Courier New";">2603 // 9600

28

29 reg[ rangered;font-family:"Courier New";">12 : rangered;font-family:"Courier New";">0] cnt ; //

30 reg clk_bps_r ; //

31

32 //---------------------------------------------------------

33 reg[ rangered;font-family:"Courier New";">2 : rangered;font-family:"Courier New";">0] uart_ctrl ; // uart

34 //---------------------------------------------------------

35

36 always @ ( posedge clk or negedge rst_n )

37 if(! rst_n ) cnt <= rangered;font-family:"Courier New";">13'd0 ;

38 else if(( cnt == `BPS_PARA ) || ! bps_start ) cnt <= rangered;font-family:"Courier New";">13'd0 ;

39 else cnt <= cnt+rangered;font-family:"Courier New";">1'b1 ; //

40

41 always @ ( posedge clk or negedge rst_n )

42 if(! rst_n ) clk_bps_r <= rangered;font-family:"Courier New";">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_PARABPS_PARA_2分别就是波特率分频计数器的最大值和中间值。实际使用时,只需要根据你所需要的波特率,更改这两个参数的值即可 。例如,选择波特率为9600bps时,设定BPS_PARA=5207BPS_PARA_2=2603。关于这个值的计算,这里暂时不提,后文会有交代。

36行至39行为波特率分频计数器的计数进程,即波特率发生模块没有被使能(! bps_start)或者计数器计数到BPS_PARA后则将计数器清零,其它情况下则每来一个时钟上升沿计数器自加1。通过此进程,则可得到相对精准的波特率定时。

41行至44行为数据采样时刻的生成,上面提到过“一般情况下,一位数据,在该位数据保持时间的中间时刻是最稳定的”,因此这里我们在计数器计数到一半时,产生一个时钟周期的高脉冲,此脉冲作为采样数据的使能信号。

以上为波特率发生器的代码及分析,波特率发生模块在例化时被分别例化为串口发送波特率发生器和串口接收波特率发生器。接下来我们再来分析串口接收模块的代码。

1 module my_uart_rx (
2 clk, rst_n ,
3 rs232_rx , rx_data , rx_int ,
4 clk_bps , bps_start
5 );
6
7
input clk; // 50MHz
8 input rst_n ; //
9 input rs232_rx ; // RS232
10 input clk_bps ; // clk_bps
11 output bps_start ; //
12 output [ 7: 0] rx_data ; //
13 output rx_int ; // ,
14
15
//---------------------------------------------------------
16 reg rs232_rx0 , rs232_rx1 , rs232_rx2 , rs232_rx3 ; //
17 wire neg_rs232_rx ; //
18
19
always @ ( posedge clk or negedge rst_n ) begin
20 if(! rst_n ) begin
21    rs232_rx0 <= 1'b0 ;
22    rs232_rx1 <=
1'b0 ;
23    rs232_rx2 <=
1'b0 ;
24    rs232_rx3 <=
1'b0 ;
25
end
26 else begin
27    rs232_rx0 <= rs232_rx ;
28    rs232_rx1 <= rs232_rx0 ;
29    rs232_rx2 <= rs232_rx1 ;
30    rs232_rx3 <= rs232_rx2 ;
31
end
32 end
33 // <20ns-40ns ( )
34 //
35 // 40ns
36 assign neg_rs232_rx = rs232_rx3 & rs232_rx2 & ~rs232_rx1 & ~rs232_rx0; // neg_rs232_rx
37
38
//---------------------------------------------------------


39 reg bps_start_r ;
40
reg[ 3: 0] num; //
41 reg rx_int ; // ,
42
43
always @ ( posedge clk or negedge rst_n )
44
if(! rst_n ) begin
45    bps_start_r <= 1'bz ;
46    rx_int <=
1'b0 ;
47
end
48 else if( neg_rs232_rx ) begin
// rs232_rx
49    bps_start_r <= 1'b1 ; //
50    rx_int <= 1'b1 ; //
51 end
52 else if( num==4'd12 ) begin //
53    bps_start_r <= 1'b0 ; //
54    rx_int <= 1'b0 ; //
55 end
56
57
assign bps_start = bps_start_r ;
58
59
//---------------------------------------------------------
60 reg[ 7 : 0] rx_data_r ; //
61 //---------------------------------------------------------
62
63
reg[ 7 : 0] rx_temp_data ; //
64
65
always @ ( posedge clk or negedge rst_n )
66
if(! rst_n ) begin
67    rx_temp_data <= 8'd0 ;
68    num <=
4'd0 ;
69    rx_data_r <=
8'd0 ;
70
end
71 else if( rx_int ) begin //
72    if( clk_bps ) begin
// , 8bit 1 2
73        num <= num+1'b1 ;
74       
case ( num)
75          
4'd1:rx_temp_data[0] <= rs232_rx; // 0bit
76           4'd2:rx_temp_data [1] <= rs232_rx; // 1bit
77           4'd3:rx_temp_data [2] <= rs232_rx; // 2bit
78           4'd4:rx_temp_data [3] <= rs232_rx; // 3bit
79           4'd5:rx_temp_data [4] <= rs232_rx; // 4bit
80           4'd6:rx_temp_data [5] <= rs232_rx; // 5bit
81           4'd7:rx_temp_data [6] <= rs232_rx; // 6bit
82           4'd8:rx_temp_data [7] <= rs232_rx; // 7bit
83           default : ;
84       
endcase
85    end
86    else if( num == 4'd12 ) begin
// 1+8+1(2)=11bit
87        num <= 4'd0 ; // STOP ,num
88        rx_data_r <= rx_temp_data ; // rx_data
89    end
90 end
91
92
assign rx_data = rx_data_r ;
93
94
endmodule


19行到第36行为起始位检测部分,1932行,实现了对rs232_rx端口上电平的连续四个时钟周期的寄存,第36行则对这连续4个时钟上升沿时的rs232_rx端口电平进行逻辑操作,得出rs232_rx端口信号下降沿的到来。neg_rs232_rx = rs232_rx3 & rs232_rx2 & ~rs232_rx1 & ~rs232_rx0,即后两次寄存的状态为低电平而前两次寄存的装填为高电平,则表明该端口上的信号发生了1->0的跳变,即有下降沿出现。neg_rs232_rx信号会产生一个周期的高脉冲。


43行至第55行则根据neg_rs232_rxnum计数值来控制串口接收波特率发生器的工作和接收中断信号。第65行至第90行则采用线性序列机的设计方式,进行一个字节的数据的接收。

以上为对串口接收模块的一个简单分析,接下来,再进行串口发送模块的分析。
1 module my_uart_tx (
2 clk, rst_n ,
3 rx_data , rx_int , rs232_tx ,
4 clk_bps , bps_start
5 );
6
7
input clk; // 50MHz
8 input rst_n ; //
9 input clk_bps ; // clk_bps_r ,
10 input [ 7 : 0] rx_data ; //
11 input rx_int ;

12 output rs232_tx ; // RS232
13 output bps_start ; //
14
15
//---------------------------------------------------------
16 reg rx_int0 , rx_int1 , rx_int2 ; //rx_int
17 wire neg_rx_int ; // rx_int
18
19
always @ ( posedge clk or negedge rst_n ) begin
20 if(! rst_n ) begin
21    rx_int0 <= 1'b0 ;
22    rx_int1 <=
1'b0 ;
23    rx_int2 <=
1'b0 ;
24
end
25 else begin
26    rx_int0 <= rx_int ;
27    rx_int1 <= rx_int0 ;
28    rx_int2 <= rx_int1 ;
29
end
30 end
31
32
assign neg_rx_int = ~rx_int1 & rx_int2 ; // neg_rx_int
33
34
//---------------------------------------------------------
35 reg[ 7 : 0] tx_data ; //
36 //---------------------------------------------------------
37 reg bps_start_r ;
38
reg tx_en ; //
39 reg[ 3: 0] num;
40
41
always @ ( posedge clk or negedge rst_n ) begin
42 if(! rst_n ) begin
43    bps_start_r <= 1'bz ;
44    tx_en <=
1'b0 ;
45    tx_data <=
8'd0 ;
46
end
47 else if( neg_rx_int ) begin //
48    bps_start_r <= 1'b1 ;
49    tx_data <= rx_data ;
//
50    tx_en <= 1'b1 ; //
51 end
52 else if( num==4'd11 ) begin //
53    bps_start_r <= 1'b0 ;
54    tx_en <=
1'b0 ;
55
end
56 end
57
58
assign bps_start = bps_start_r ;
59
60
//---------------------------------------------------------
61 reg rs232_tx_r ;
62
63
always @ ( posedge clk or negedge rst_n ) begin
64 if(! rst_n ) begin
65    num <= 4'd0 ;
66    rs232_tx_r <=
1'b1 ;
67
end
68 else if( tx_en ) begin
69    if( clk_bps ) begin
70        num <= num+1'b1 ;
71       
case ( num)
72          
4'd0 : rs232_tx_r <= 1'b0 ; //
73           4'd1 : rs232_tx_r <= tx_data [0]; // bit0
74           4'd2 : rs232_tx_r <= tx_data [1]; // bit1
75           4'd3 : rs232_tx_r <= tx_data [2];// bit2
76           4'd4 : rs232_tx_r <= tx_data [3]; // bit3
77           4'd5 : rs232_tx_r <= tx_data [4];// bit4
78           4'd6 : rs232_tx_r <= tx_data [5]; // bit5
79           4'd7 : rs232_tx_r <= tx_data [6]; // bit6
80           4'd8 : rs232_tx_r <= tx_data [7]; // bit7
81           4'd9 : rs232_tx_r <= 1'b1 ; //
82           default : rs232_tx_r <= 1'b1 ;
83       
endcase
84    end
85    else if( num==4'd11 ) num <= 4'd0 ; //
86    end
87 end
88
89
assign rs232_tx = rs232_tx_r ;
90
91
endmodule

代码19行到30行对串口接收模块的接收中断信号进行了3次寄存,第32行则通过对连续两次寄存结果的判断,来检测接收中断信号rx_int的下降沿。如果有下降沿到来,neg_rx_int信号则会产生一个时钟周期的高脉冲信号,第47行则通过对该信号的状态判断,来确定是否启动发送波特率发生器模块。如果检测到了该高脉冲,则使能串口发送(tx_en <= 1'b1),同时将待发送的字节数据(rx_data)寄存到发送寄存器(tx_data)中。第52行,即计数到11后,表明一个字节的数据发送完成,此时停止发送波特率发生器的工作,同时禁止发送模块的工作(tx_en <= 1'b0),即完成一个字节的数据的发送。

63行至第87行采用线性序列机设计方式,实现一个字节(自动添加起始位和停止位)的发送操作。

以上就是对特权同学的串口代码的一个简单分析,顶层模块就只进行了一个简单的例化和连线作用,因此小梅哥就不做分析了。小梅哥个人能力有限,可能存在理解不到位或者表述不专业的地方,不妥之处还望各位多多指出,以促进共同学习。可以说,相对于淘宝网上漫天飞舞的开发板和五花八门的串口例程,特权同学的设计代码相当规范,架构合理,思路清楚,确实是值得我们每一个FPGA爱好者学习。

 

一、      原始代码验证

前面,通过对设计代码的一个简单分析,弄清楚了特权同学设计代码的基本架构和思路。那么看过特权同学教学视频的都知道,该代码能够实现一个字节的数据收发测试。那么这里,小梅哥就先对该设计进行一个仿真,通过仿真来分析此设计的性能。

仿真的思路很简单,就是通过模拟串口发送过程,给该设计模块发送数据,由前面分析可知,该设计模块接收到数据后,会立即将数据发送出去,因此我们还需要对串口发送出来的数据进行分析,这里,熟悉Uart协议的,我们可以直接观察发送波形。当然,为了更加直观,我们也可以设计一个模拟串口接收数据的仿真模型,通过该模块来读取串口发送出来的数据。考虑到看这篇文章的大多是初学者,为了让大家能够更好的查看我们的仿真结果,同时教大家进行仿真模型的设计,小梅哥还是自己编写了一个虚拟的串口仿真模型。验证时,只需要将该仿真模型挂接到串口模块上,则该模型便能够自动的给串口模块发送数据,同时接收串口发送过来的数据。并会实时的将发送的数据和接收的数据打印出来,实际在观察仿真结果时,我们便只需要观看打印的结果就可以了。该串口仿真模型的代码如下所示:

1 `timescale 1ns/1ps
2
3
module Uart_module ( uart_rx , uart_tx , send_state );
4
5
input uart_rx ;
6
output reg uart_tx ;
7
output reg send_state ;
8
9
reg Clk;
10
reg Rst_n ;
11
12
wire Mid_Flag_send ;
13
wire Mid_Flag_Receive ;
14
15
reg Receive_Baud_Start ;
16
reg [ 7 : 0] rx_data ;
17
18
initial Clk = 1;
19
always #10 Clk = ~Clk;
20
21 speed_select speed_select_Send (
22    . clk( Clk),
23    . rst_n ( Rst_n ),
24    . bps_start (
1'b1 ),
25    . clk_bps ( Mid_Flag_send )
26 );
27
28 speed_select speed_select_receive (
29    . clk( Clk),
30    . rst_n ( Rst_n ),
31    . bps_start ( Receive_Baud_Start ),
32    . clk_bps ( Mid_Flag_Receive )
33 );
34
35
initial begin
36    Rst_n = 0;
37    uart_tx =
1;
38    send_state =
0;
39    #
300 Rst_n = 1;
40
41    $display (
"Set Baud As 9600bps" );
42    #
200 ; Uart_Send ( 8'hb6 );
43    #
20 ; Uart_Send ( 8'he7 );
44    #
80 ; Uart_Send ( 8'hf0 );
45    #
500 ; Uart_Send ( 8'h02 );
46    #
300 ; Uart_Send ( 8'hb4 );
47    #
40 ; Uart_Send ( 8'he5 );
48    #
90 ; Uart_Send ( 8'hb0 );
49    #
1000 ; Uart_Send ( 8'h32 );
50    #
2000000 ;
51    $stop ;
52
end
53
54
task Uart_Send ;
55   
input [ 7: 0] Data ;
56   
begin
57    send_state = 1;
58    @(
posedge Mid_Flag_send) #0.1 uart_tx = 0;
59    @(
posedge Mid_Flag_send) #0.1 uart_tx = Data [0];
60    @( posedge Mid_Flag_send) #0.1 uart_tx = Data [1];
61    @(
posedge Mid_Flag_send) #0.1 uart_tx = Data [2];
62    @(
posedge Mid_Flag_send) #0.1 uart_tx = Data [3];
63    @(
posedge Mid_Flag_send) #0.1 uart_tx = Data [4];
64    @(
posedge Mid_Flag_send) #0.1 uart_tx = Data [5];
65    @(
posedge Mid_Flag_send) #0.1 uart_tx = Data [6];
66    @(
posedge Mid_Flag_send) #0.1 uart_tx = Data [7];
67    @(
posedge Mid_Flag_send) #0.1 uart_tx = 1;
68    $display (
"Uart_Send Data = %0h" , Data );
69    send_state =
0;
70   
end
71 endtask
72
73
initial begin
74 forever begin
75 @( negedge uart_rx )
76
begin
77    Receive_Baud_Start = 1;
78    @(
posedge Mid_Flag_Receive);
79    @(
posedge Mid_Flag_Receive) rx_data [0] = uart_rx ;
80    @(
posedge Mid_Flag_Receive) rx_data [1] = uart_rx ;
81    @(
posedge Mid_Flag_Receive) rx_data [2] = uart_rx ;
82    @(
posedge Mid_Flag_Receive) rx_data [3] = uart_rx ;
83    @(
posedge Mid_Flag_Receive) rx_data [4] = uart_rx ;
84    @(
posedge Mid_Flag_Receive) rx_data [5] = uart_rx ;
85    @(
posedge Mid_Flag_Receive) rx_data [6] = uart_rx ;
86    @(
posedge Mid_Flag_Receive) rx_data [7] = uart_rx ;
87    @(
posedge Mid_Flag_Receive) Receive_Baud_Start = 0;
88    $display (
"Uart_receive Data = %0h" , rx_data );
89
end
90 end
91 end
92
93
endmodule
94

因为在将代码复制到word的过程中,会有一定的格式兼容问题,所以文中部分格式不是太规范,望各位理解,另外,完整的代码,小梅哥也以pdf的形式提供了,感兴趣的朋友可以下载学习。

本仿真模型的第一句话“`timescale 1ns/1ps”为仿真精度及时间的说明,定义时间精度为1ps,时间单位为1ns,那么我们在代码编写的过程中,如果写成“#200”则表示延时200ns,因为时间精度为1ps,因此我们还可以进一步提高延时精度,如“#200.1”表示延时200.1ns。一般的测试文件(testbench)中,这句话作为第一句话,必写,当然,时间精度和单位我们可以根据自己的需求更改,如写成“`timescale 1us/1ns”或者“`timescale 1ns/1ns”等都是可以的。

该模块作为一个仿真模型,就是虚拟了一个串口收发仪器,既然是一个串口收发仪器,则必然有串口发送端口和串口接收端口,因此在模块名后面定义了三个端口。这与一般的testbench不同,一般的testbench作为仿真时的顶层,不需要端口,因此模块名后就直接以“;”结束。该模块的三个端口“uart_rx , uart_tx , send_state”分别为串口接收端口、串口发送端口、串口发送状态信号。串口收发端口不用说,大家也已经知道了,串口发送状态信号主要作为指示信号,指示当前仿真模型正在进行数据的发送过程。

9行至第16行为测试文件中信号的定义,以前我们总是理解说这些信号就是待测试模块的端口,需要在测试文件中定义。那么这里小梅哥更喜欢换一种方式来理解:我们自己的设计,本设计中即特权的串口模块,是一个功能未知的黑盒子,这个黑盒子有一些信号线引出,有的信号线是作为输入的,即需要外部输入一定的信号作为激励,而有的信号线是作为输出的,能够输出一些数据,当然还有一些信号线是既能够作为输入,又能够作为输出的,即三态。我们要想知道这个黑盒子的功能,就需要给这个黑盒子的输入信号线接上信号源,通过给这些输入信号线一定的激励,观察其输出端口上的响应,从而获知该黑盒子的功能。那么在这里,对于待测试模块的输入端口,我们就接上信号发生器,对于输出端口,我们就接上示波器或者逻辑分析仪,这样,我们就能够通过信号发生器给输入端口产生一定的激励,然后通过示波器观察输出端口的输出了。即如下图所示:


那么,我们的testbench主要实现信号发生器的功能,既然是信号发生器那么就一定有数据信号输出,这个数据信号输出就可以连接到我们的待测模块上。待测试模块的输出端口,连接到我们的示波器或者逻辑分析仪的探头上,这样就实现了一个完整的测试系统,那么我们信号发生器的信号源,可能命名叫做,ab,c,d,e……. 而我们示波器的探头则命名为探头1,探头2……接下来就好理解了,在testbench,我们将信号发生器的输出信号定义为reg型,而示波器的探头定义为wire型。我们信号发生器的输出信号线和示波器的探头线都可以任意命名,实际使用时一一对应连接到待测试模块的端口上,也可以就直接与待测模块的各个端口名保持一致。本设计中,小梅哥让testbench中的信号与待测模块的端口保持一致。

18行和19行为产生50MHz时钟的语句。

因为本仿真模型实质上就是一个串口收发模块,因此也需要有收发波特率发生器,这里小梅哥为了省事,直接调用了特权同学的波特率发生器模块,来作为我仿真模型的波特率发生器。因为该波特率发生器本身也属于待测试部分,小梅哥之所以敢放心的调用,是因为事先我已经通过仿真,确定了该波特率发生器功能的正确性。第21行至33行为分别例化得到发送波特率发生器和接收波特率发生器的代码。

54行至71行为发送一个完整字节的数据(自动添加起始位和停止位)的代码,该部分写成任务的形式,方便调用。当我们需要发送一个字节的数据时,例如,发送8'hb6只需要写“Uart_Send ( 8'hb6 )”即可,该任务便将自动执行,将数据发送出去。在一个字节的数据发送完成后,同时使用系统任务$display来打印当前发送的数据是多少,以方便我们直观的观察仿真运行过程。至于$display这个系统任务中各个部分的含义,请读者自行阅读verilog的语法书。代码的4249行便是调用此任务进行了多次数据的发送。

73行至91行为模拟串口接收部分,通过对串口模块发送出来的数据进行接收,并将接收到的数据用$display函数打印出来。我们只需要阅读发送数据和接收数据后打印出来的信息,即可判断通信是否成功,待测模块功能是否正常。

这里需要注意的是,打印出来的接收数据和发送数据是针对仿真模型来说的,send data是仿真模型发送出去的数据,对应待测模块应该接收到的数据。receive data则是仿真模型接收到的数据,对应待测模块发送的数据。

我们所编写的测试文件,一定要是可控的,即在所有事务完成后,将仿真停下来,否则,仿真会一直进行下去,导致出现大量冗余波形,影响我们对仿真结果的分析。因此在第51行,当所有测试已经完成后,使用系统任务$stop将仿真停下来。

以上对小梅哥写的串口仿真模型进行了介绍,在实际使用中,只需要将该模型与待测模块按照如下图所示的方式连接起来即可。


 

这里,小梅哥使用一个testbench文件作为顶层,将这两个部分连接起来,同时产生my_uart_top工作所需的时钟和复位信号。该文件详细代码如下:

 
1 `timescale 1ns /1ns
2

3
module Uart_tb ;
4
5
reg
Clk;
6
reg
Rst_n ;
7
8
wire
uart_rx ;
9
wire
uart_tx ;
10
wire
send_state ;
11
12 my_uart_top u1 (
13    . clk( Clk),
14    . rst_n ( Rst_n ),
15    . rs232_rx ( uart_tx ),
16    . rs232_tx ( uart_rx )
17 );
18
19 Uart_module u2 (
20    . uart_rx ( uart_rx ),
21    . uart_tx ( uart_tx ),
22    . send_state ( send_state )
23 );
24
25
initial begin
26    Clk = 1
;
27    Rst_n =
0
;
28    #
200
;
29    Rst_n =
1
;
30
end
31

32
always #10 Clk = ~Clk;
33
34
endmodule
35

该代码实在简单,只是实现了一个启动时的初始化和50MHz时钟的产生,因此小梅哥就不做任何分析了。

 

http://xiaomeige.taobao.com。做最用心的FPGA学习板和教程资料
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

40

主题

179

帖子

0

精华

高级会员

Rank: 4

积分
921
金钱
921
注册时间
2013-10-23
在线时间
94 小时
 楼主| 发表于 2014-12-26 21:53:30 | 显示全部楼层
fficeffice" />

以上为小梅哥为了对特权同学的串口收发模块进行测试所展开的部分工作,到这里,仿真测试所需要的准备工作我们就做好了,接下来将实际进行仿真,通过仿真来分析该模块的性能。

这里极力推荐大家使用modelsim进行仿真,因为quartusII自带的仿真工具灵活性和功能都赶modelsim相差甚远。Modelsim作为一款强大的仿真软件,在业界被广泛使用。同时,modelsim针对不同的EDA厂家,也推出了OEM版本,modelsim-altera就是为Altera公司开发的OEM版本,此版本针对Altera公司的器件预先做了许多的工作,使我们使用的时候能够更加的快捷方便,这里,小梅哥就使用modelsim-Altera版本来仿真这个设计。

我们可以使用modelsim-Altera,通过完全手动化的方式来建立仿真工程,添加仿真库、编译文件,添加波形,运行仿真,当然也可以使用Nativelink的方式,通过quartus II软件实现一键调用,实现自动化的仿真过程。这里,对于初学者,小梅哥还是推荐采用这种自动化的方式,因为简单,不需要过多的手动操作,且不容易出错。这里,小梅哥也使用这种方式来进行仿真。

要使用这种方式仿真,我们需要在QuartusII软件中进行EDA Tools的设置。在quartus II软件中,依次点击“tools – options ”,在打开的选项卡中选择“EDA tool options”,在modelsim-altera一栏中输入你的modelsim-Altera的安装路径(部分在安装的时候就已经自动设置好了,就不用去管),如下图所示:


这里设置完成后,点击OK即可。

当确认这一步没有问题后,我们就可以开始进行Nativelink的设置了。Nativelive就是一个链接的过程,通过设置Nativelink,让Quartus II软件能够自动的调用modelsim-altera软件,并建立仿真工程,添加仿真库、编译文件,添加波形,运行仿真。接下来我们就来进行Nativelink的设置

第一步:依次点击“assignments– settings”,


在弹出的选项卡中,选中“EDA Tool Settings”(红色标号1处),在子选项中选择“Simulation”(红色标号2处),注意红色标号5处和6处应分别选择为modelsim-alteraVerilog HDL,如果不是,请通过下拉列表选择为上述选项。点击“Compile test bench (红色标号3处),然后点击最右侧的“Test Benchs (红色标号4处),就会弹出如下所示的界面:


点击“New (红色标号1处),接着会弹出“New Test Bench Settings”界面,如下图所示:


首先我们点击红色标号1处的三个小点,在弹出的文件选择界面中,选中我们需要加入的testbench文件,如下所示:


这里我们选择“Uart_tb.v”和“Uart_module.v”,点击“Open”打开。这里,“Uart_tb.v”为仿真顶层文件,“Uart_module.v”为串口仿真模型。

文件选择好之后,回到New Test Bench Settings界面,如下图所示,


在顶端“test bench name”(红色标号2)处输入我们的仿真顶层文件名,即Uart_tb,注意,不要加“.v”后缀。于是,”Top level module in test bench”会自动与“Test bench name”保持一致。

至此,我们的Nativelink设置就完成了,一路选择OK下去,直到回到Quartus II软件的主界面。此时,我们已经完成了仿真需要的所有设置,我们直接点击Quartus II软件上的RTL Simulation图标


即可启动仿真,也可以通过“Tools– Run Simulation Tool – RTL Simulation”启动仿真。

接下来,我们需要做的就是大约20秒左右的等待,这个过程中,Quartus II会自动启动modelsim-altera软件,建立仿真工程,添加仿真库、编译文件,添加波形,运行仿真。最后停留在如下所示的状态:


这里,右侧深色窗口为波形窗口,下方为副本界面,打印了软件运行过程中的信息,包括添加文件、编译文件、添加波形、运行等,同时还打印了testbench中需要打印输出的信息。具体的打印信息如下:


由此打印信息可知,串口仿真模型总共进行了8次数据发送,却只收到了6次数据,因此有两次发送给串口模块的数据丢失或者串口模块发送的数据不正确。同时,第一次发送出去的数据接收回来还是正确的,但是第二次发送的数据再接收回来,就错误了。这两次数据发送之间的时间间隔为40ns。考虑可能是发送间隔太短导致,但是当第4次发送的数据(s4)被正确接收后(r3),紧接着第5次发送的数据在发回的时候,再次出错,而s4s5之间间隔了300ns,因此可知,该串口收发模块在连续两次发送间隔很短的情况下,很容易出错。那么,怎么样才能保证连续两次发送之间,即使很短的间隔,也不出错误呢?小梅哥通过对仿真波形的分析发现:一次接收数据,总共有12个波特率脉冲,如下图所示:


这里小梅哥就有点疑惑了,我们一般的应用中,串口一帧的数据为十位,包含1位起始位、8位数据位、一位停止位,一般不含校验位。因此,这里明显多了两个波特率周期,那么,问题很有可能就出在这里。回到这部分的代码:


43 always @ ( posedge clk or negedge rst_n )
44
if(! rst_n ) begin
45 bps_start_r <= rangered;font-family:"Courier New";">1'bz ;
46 rx_int <=
rangered;font-family:"Courier New";">1'b0 ;
47
end
48 else if( neg_rs232_rx ) begin
// rs232_rx
49 bps_start_r <= rangered;font-family:"Courier New";">1'b1 ; //
50 rx_int <= rangered;font-family:"Courier New";">1'b1 ; //
51 end
52 else if( num==rangered;font-family:"Courier New";">4'd12 ) begin //
53 bps_start_r <= rangered;font-family:"Courier New";">1'b0 ; //
54 rx_int <= rangered;font-family:"Courier New";">1'b0 ; //
55 end
56
57
assign bps_start = bps_start_r ;
58
59
//---------------------------------------------------------
60 reg[ rangered;font-family:"Courier New";">7 : 0] rx_data_r ; //
61 //---------------------------------------------------------
62
63
reg[ 7 : 0] rx_temp_data ; //
64
65
always @ ( posedge clk or negedge rst_n )
66
if(! rst_n ) begin
67 rx_temp_data <= 8'd0 ;
68 num <=
4'd0 ;
69 rx_data_r <=
8'd0 ;
70
end
71 else if( rx_int ) begin //
72 if( clk_bps ) begin
// , 8bit 1 2
73 num <= num+1'b1 ;
74
case ( num)
75
4'd1:rx_temp_data[0] <= rs232_rx; // 0bit
76 4'd2:rx_temp_data [1] <= rs232_rx; // 1bit
77 4'd3:rx_temp_data [2] <= rs232_rx; // 2bit
78 4'd4:rx_temp_data [3] <= rs232_rx; // 3bit
79 4'd5:rx_temp_data [4] <= rs232_rx; // 4bit
80 4'd6:rx_temp_data [5] <= rs232_rx; // 5bit
81 4'd7:rx_temp_data [6] <= rs232_rx; // 6bit
82 4'd8:rx_temp_data [7] <= rs232_rx; // 7bit
83 default : ;
84
endcase
85 end
86 else if( num == 4'd12 ) begin//我们的标准接收模式下只有1+8+1(2)=11bit的有效数据
87 num <= 4'd0 ; // STOP ,num
88 rx_data_r <= rx_temp_data ; // rx_data
89 end
90 end


那么,特权同学在这里有通过注释解释为什么是12个波特率周期:“我们的标准接收模式下只有1+8+1(2)=11bit的有效数据”。即使是11位,小梅哥还是无法理解为什么需要12个波特率时钟。我们在实际使用的时候,一般不去考虑校验位的问题,因此总共只有10位,也就是说,我写的10位数据位的仿真模型和特权的11位方式不兼容。考虑到大多数的应用过程中是10位,因此小梅哥在特权大神的代码上稍作修改,以适应10位模式。这里,将第52行代码“else if( num==4'd12 ) begin ”改为“else if( num==4'd10 ) begin ”,将第86行代码“else if( num == 4'd12 ) begin”改为“else if( num == 4'd10 ) begin”。通过这样更改,就能够适应1bit的数据接收。同时,小梅哥在实际调试中发现,第45行的“bps_start_r <= 1'bz ;”代码也存在问题,在复位时,给了bps_start_r一个高阻态,这样在实际仿真时,会导致错误,因此小梅哥在这里,将这一行代码进行了修改,复位时将bps_start_r置为0,即修改为bps_start_r <= 1'b0 ;”

以上为接收部分的修改。发送部分的波特率波形如下所示:


同样有11个波特率时钟,因此,小梅哥将发送部分的代码稍作修改,以适应10bit的数据发送。特权同学原始代码如下:

41 always @ ( posedge clk or negedge rst_n ) begin
42 if(! rst_n ) begin
43 bps_start_r <= 1'bz ;
44 tx_en <=
1'b0 ;
45 tx_data <=
8'd0 ;
46
end
47 else if( neg_rx_int ) begin //
48 bps_start_r <= 1'b1 ;
49 tx_data <= rx_data ;
//
50 tx_en <= 1'b1 ; //
51 end
52 else if( num==4'd11 ) begin //
53 bps_start_r <= 1'b0 ;
54 tx_en <=
1'b0 ;
55
end
56 end
57
58
assign bps_start = bps_start_r ;
59
60
//---------------------------------------------------------
61 reg rs232_tx_r ;
62
63
always @ ( posedge clk or negedge rst_n ) begin
64 if(! rst_n ) begin
65 num <= 4'd0 ;
66 rs232_tx_r <=
1'b1 ;
67
end
68 else if( tx_en ) begin
69 if( clk_bps ) begin
70 num <= num+1'b1 ;
71
case ( num)
72
4'd0 : rs232_tx_r <= 1'b0 ; //
73 4'd1 : rs232_tx_r <= tx_data [0]; // bit0
74 4'd2 : rs232_tx_r <= tx_data [1]; // bit1
75 4'd3 : rs232_tx_r <= tx_data [2];// bit2
76 4'd4 : rs232_tx_r <= tx_data [3]; // bit3
77 4'd5 : rs232_tx_r <= tx_data [4];// bit4
78 4'd6 : rs232_tx_r <= tx_data [5]; // bit5
79 4'd7 : rs232_tx_r <= tx_data [6]; // bit6
80 4'd8 : rs232_tx_r <= tx_data [7]; // bit7
81 4'd9 : rs232_tx_r <= 1'b1 ; //
82 default : rs232_tx_r <= 1'b1 ;
83
endcase
84 end
85 else if( num==4'd11 ) num <= 4'd0 ; //
86 end
87 end
88
这里,我们将第52行代码“else if( num==4'd11 ) begin”修改为“else if( num==4'd10 ) begin”,将第85行代码“else if( num==4'd11 ) num <= 4'd0 ;”修改为“else if( num==4'd10 ) num <= 4'd0 ;”同样,第43行,针对“bps_start_r <= 1'bz也进行了与接收部分相同的修改,即修改为bps_start_r <= 1'b0 ;”其他部分不变,然后再进行仿真编译测试。仿真结果如下:


由以上仿真结果可知,通过修改,该串口模块已经能够实现正确的收发了。接下来,小梅哥将通过板级验证来对该模块进行测试。

这里,小梅哥使用至芯科技提供的一块入门级FPGA开发板来完成该模块的板级验证。以下为该开发板的照片:



该开发板上集成了一枚PL2303USB转串口芯片,因此,我们不再需要笨拙的9针串口线,只需要一根USB线将开发板与电脑的USB口相连,再安装PL2303的驱动,即可在PC端模拟出一个串口端口,该串口端口的使用和传统串口端口没有任何差别。具体的串口驱动安装过程如下图所示:






通过以上步骤,我们就实现了PL2303USB转串口芯片驱动的安装。接下来,我们需要根据各自使用的开发板的电路分配引脚,这里小梅哥先用图文的形式介绍一下针对ZX2开发板的引脚分配及最终将配置文件下载到开发板的流程:












通过以上步骤,电路就已经成功的配置到了我们的开发板上并运行起来了,这个时候,我们就需要通过调试软件来进行调试,以验证我们修改后的UART收发模块能否稳定运行。要调试我们的设计,我们还需要一个串口调试工具,通过串口调试工具,对我们编写的串口模块持续发送数据,然后接收串口模块发送回来的数据,通过对比发送与接收的数据,如果相同,则表明我们的模块是能够稳定工作的,如果出现丢码或者乱码,表明收发过程中出现了错误,则表明我们设计的模块存在工作不稳定的情况,需要进一步修改优化。

这里,我们使用程序匠人前辈编写的强大串口调试软件——串口猎人。该软件的下载和安装小梅哥就不多说了,当我们启动串口猎人软件后,会显示以下界面,接下来小梅哥就直接以图片配文字的形式来一步一步演示该软件的使用:




通过以上测试,我们知道了,经过小梅哥的修改,该串口模块目前能够以连续无间隔的发码速度进行收发,而且不存在任何误码和丢码。因此,可以说,小梅哥的修改是成功的。


这一篇,主要带领大家一起进行了串口代码的分析、仿真模型的设计(testbench)、Quartus II软件下使用Nativelink调用Modelsim-altera软件进行仿真、仿真结果简单分析、串口代码的修改、串口模块的板级验证。相信通过此文,初学者能够学到部分知识。

这是该系列文档的第一篇,主要对特权大神的UART代码进行了简单的修改,以适应小梅哥自己的用途。下一篇,小梅哥将详细介绍基于此模块的扩展设计,其实扩展也没多少内容,主要就是加上可变波特率功能,顺便介绍一下代码中一些增加设计灵活性的技巧。第三篇,则是小梅哥开始手把手教大家从原理入手,设计我们自己的UART模块,设计中会加入收发FIFO,并使用标准的Avalon ST接口协议,以形成IP核的形式。


http://xiaomeige.taobao.com。做最用心的FPGA学习板和教程资料
回复 支持 反对

使用道具 举报

557

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165080
金钱
165080
注册时间
2010-12-1
在线时间
2103 小时
发表于 2014-12-26 22:48:21 | 显示全部楼层
不错,顶!!!!
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

13

主题

45

帖子

0

精华

初级会员

Rank: 2

积分
166
金钱
166
注册时间
2013-8-27
在线时间
12 小时
发表于 2014-12-26 22:50:04 | 显示全部楼层
囫囵吞枣的先看一遍解馋。。然后该慢慢研究啦,教程详细,不能更赞
回复 支持 反对

使用道具 举报

40

主题

179

帖子

0

精华

高级会员

Rank: 4

积分
921
金钱
921
注册时间
2013-10-23
在线时间
94 小时
 楼主| 发表于 2014-12-26 23:42:27 | 显示全部楼层
今天网络不行,明天把原版工程和修改后的工程都传上来
http://xiaomeige.taobao.com。做最用心的FPGA学习板和教程资料
回复 支持 反对

使用道具 举报

9

主题

52

帖子

0

精华

初级会员

Rank: 2

积分
164
金钱
164
注册时间
2013-2-5
在线时间
18 小时
发表于 2014-12-27 12:56:41 | 显示全部楼层
帮顶,我就喜欢这样的帖子
话说我有严重的拖延症,怎样才能不做事不拖拖拉拉的呢...
回复 支持 反对

使用道具 举报

16

主题

189

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1296
金钱
1296
注册时间
2013-8-27
在线时间
503 小时
发表于 2014-12-27 15:05:28 | 显示全部楼层
厉害,顶起!
回复 支持 反对

使用道具 举报

30

主题

705

帖子

1

精华

新手上路

积分
21
金钱
21
注册时间
2013-3-14
在线时间
52 小时
发表于 2014-12-27 15:27:05 | 显示全部楼层
不错,顶,mark。不知道楼主搞过xilinx的芯片没。
原子哥是不是又要出黑科技了呀。^_^...
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
21
金钱
21
注册时间
2014-12-28
在线时间
0 小时
发表于 2014-12-28 15:10:24 | 显示全部楼层
楼主很用心啊,今后多交流哦!
回复 支持 反对

使用道具 举报

40

主题

179

帖子

0

精华

高级会员

Rank: 4

积分
921
金钱
921
注册时间
2013-10-23
在线时间
94 小时
 楼主| 发表于 2014-12-28 20:44:17 | 显示全部楼层
回复【8楼】fanghuiopenedv:
---------------------------------
穷啊,买不起赛灵思的板子,除非用IP核,单纯用verilog自己写逻辑的话,差别不大。
http://xiaomeige.taobao.com。做最用心的FPGA学习板和教程资料
回复 支持 反对

使用道具 举报

40

主题

179

帖子

0

精华

高级会员

Rank: 4

积分
921
金钱
921
注册时间
2013-10-23
在线时间
94 小时
 楼主| 发表于 2014-12-28 20:45:46 | 显示全部楼层
这里把原版工程和小梅哥修改后的工程都传上来,供需要的同学研究

原版和修改后工程.rar

3.63 MB, 下载次数: 2210

http://xiaomeige.taobao.com。做最用心的FPGA学习板和教程资料
回复 支持 反对

使用道具 举报

40

主题

179

帖子

0

精华

高级会员

Rank: 4

积分
921
金钱
921
注册时间
2013-10-23
在线时间
94 小时
 楼主| 发表于 2014-12-28 20:46:28 | 显示全部楼层
回复【9楼】约瑟叔叔:
---------------------------------
好的,欢迎交流
http://xiaomeige.taobao.com。做最用心的FPGA学习板和教程资料
回复 支持 反对

使用道具 举报

4

主题

28

帖子

0

精华

初级会员

Rank: 2

积分
148
金钱
148
注册时间
2013-1-11
在线时间
12 小时
发表于 2014-12-29 16:03:56 | 显示全部楼层
赞啊  感谢楼主啊
回复 支持 反对

使用道具 举报

0

主题

23

帖子

0

精华

初级会员

Rank: 2

积分
120
金钱
120
注册时间
2013-6-11
在线时间
16 小时
发表于 2014-12-31 00:29:12 | 显示全部楼层
……楼主好人……
回复 支持 反对

使用道具 举报

40

主题

179

帖子

0

精华

高级会员

Rank: 4

积分
921
金钱
921
注册时间
2013-10-23
在线时间
94 小时
 楼主| 发表于 2014-12-31 01:12:56 | 显示全部楼层
回复【14楼】Dream_V:
---------------------------------
谢谢。现在回复你只为了证明我比你晚睡。快快抱你家小狐狸睡觉觉吧
http://xiaomeige.taobao.com。做最用心的FPGA学习板和教程资料
回复 支持 反对

使用道具 举报

0

主题

23

帖子

0

精华

初级会员

Rank: 2

积分
120
金钱
120
注册时间
2013-6-11
在线时间
16 小时
发表于 2015-1-2 08:35:44 | 显示全部楼层
回复【15楼】小梅哥:
---------------------------------
起的有我早吗?你懂的
回复 支持 反对

使用道具 举报

13

主题

45

帖子

0

精华

初级会员

Rank: 2

积分
166
金钱
166
注册时间
2013-8-27
在线时间
12 小时
发表于 2015-1-4 21:06:13 | 显示全部楼层
在等待后续的教程中......
回复 支持 反对

使用道具 举报

13

主题

45

帖子

0

精华

初级会员

Rank: 2

积分
166
金钱
166
注册时间
2013-8-27
在线时间
12 小时
发表于 2015-1-20 14:18:19 | 显示全部楼层
2呢,2呢?
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
21
金钱
21
注册时间
2015-2-1
在线时间
0 小时
发表于 2015-2-1 11:29:41 | 显示全部楼层
在这个代码的基础上已经加上了,收发的奇偶校验功能,实际测试没有错误,需要的可以交流
回复 支持 反对

使用道具 举报

40

主题

179

帖子

0

精华

高级会员

Rank: 4

积分
921
金钱
921
注册时间
2013-10-23
在线时间
94 小时
 楼主| 发表于 2015-2-1 21:24:04 | 显示全部楼层
回复【19楼】blackroot:
---------------------------------
如果奇偶校验发现传输中有误,怎么发起重传呢?
http://xiaomeige.taobao.com。做最用心的FPGA学习板和教程资料
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
21
金钱
21
注册时间
2015-3-5
在线时间
0 小时
发表于 2015-3-5 15:13:02 | 显示全部楼层
回复【11楼】小梅哥:
---------------------------------
我下载了打不开,用的是quartus 11.0的,求指导!
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
21
金钱
21
注册时间
2015-3-10
在线时间
0 小时
发表于 2015-3-10 18:29:59 | 显示全部楼层
请问版主,发送模块的72行,会不会使起始位只保持一半周期呢?因为num开始时0,clk_bps,9600的一半,会不会导致起始位只保持一半周期呢?我是初学者,你能仿真看看是什么样子吗?谢谢!!!
 72           4'd0: rs232_tx_r <= 1'b0 ; //
回复 支持 反对

使用道具 举报

40

主题

179

帖子

0

精华

高级会员

Rank: 4

积分
921
金钱
921
注册时间
2013-10-23
在线时间
94 小时
 楼主| 发表于 2015-3-19 23:51:32 | 显示全部楼层
回复【22楼】daydream1:
---------------------------------
恩,这个问题我后面也发现了,并在我自己的代码中特别留意了这个问题。这两天抽个时间把文档写一下了发布吧
http://xiaomeige.taobao.com。做最用心的FPGA学习板和教程资料
回复 支持 反对

使用道具 举报

557

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165080
金钱
165080
注册时间
2010-12-1
在线时间
2103 小时
发表于 2015-3-20 00:04:57 | 显示全部楼层
回复【23楼】小梅哥:
---------------------------------
好久不见小梅哥发帖了,最近忙啥呢?
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
21
金钱
21
注册时间
2015-5-7
在线时间
0 小时
发表于 2015-5-7 11:02:53 | 显示全部楼层
例如,选择波特率为9600bps时,设定BPS_PARA=5207BPS_PARA_2=2603。关于这个值的计算,这里暂时不提,后文会有交代。

小梅哥,下文中没有解释啊。
求解释!!!!
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手上路

积分
27
金钱
27
注册时间
2015-5-25
在线时间
1 小时
发表于 2015-5-25 13:53:10 | 显示全部楼层
这帖子真棒,讲得真好!正在用Xilinx的vc707做东西,算法需要串口先把数据传输到板子上,FPGA的测试比GPU麻烦就在于实际测试起来太繁琐,找了一些帖子,这个是最严谨的了,谢谢分享和辛勤工作!
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手上路

积分
27
金钱
27
注册时间
2015-5-25
在线时间
1 小时
发表于 2015-5-25 17:07:02 | 显示全部楼层
回复【2楼】小梅哥:
---------------------------------
请问一下,串口每次收发8bit,而如果我需要每次接收18bit的话,只能在收发FIFO上面做文章了吗?
回复 支持 反对

使用道具 举报

13

主题

145

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
469
金钱
469
注册时间
2015-5-14
在线时间
65 小时
发表于 2015-7-2 22:15:48 | 显示全部楼层
谢谢小梅哥。。。。。。。。。。。。。。。。。。。。。。。
回复 支持 反对

使用道具 举报

0

主题

7

帖子

0

精华

初级会员

Rank: 2

积分
137
金钱
137
注册时间
2013-4-30
在线时间
32 小时
发表于 2015-8-25 15:16:38 | 显示全部楼层
谢谢,非常好的学习资料。
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
21
金钱
21
注册时间
2015-8-3
在线时间
0 小时
发表于 2015-9-11 12:21:33 | 显示全部楼层
回复【2楼】小梅哥:
---------------------------------

不得不回复一个赞。。


不过想问一个问题,本文的接收模块就是检测接收引脚上的一个下降沿,但是如果这个下降沿是数据端引起的,该如何处理(这种情况有可能出现,在FPGA开始工作之前串口已经有数据了,或者在串口传输的过程中给FPGA一个复位),这时候按照这样的接收方案,应该会有一定的问题。
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
21
金钱
21
注册时间
2015-9-29
在线时间
0 小时
发表于 2015-9-29 10:50:28 | 显示全部楼层
回复【2楼】小梅哥:
---------------------------------
相当好,非常详细!
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

新手入门

积分
23
金钱
23
注册时间
2014-8-18
在线时间
0 小时
发表于 2015-12-1 08:48:53 | 显示全部楼层
顶起小梅哥,虽然实验已经实现,依然奇怪,串口发送,为什么会有起始位,串口发送的起始检测,不是由assign neg_rs232_rx = rs232_rx3 & rs232_rx2 & ~rs232_rx1 & ~rs232_rx0; // neg_rs232_rx这个语句实现的吗?
为寂寞的生活,找点生趣!
回复 支持 反对

使用道具 举报

6

主题

21

帖子

0

精华

初级会员

Rank: 2

积分
71
金钱
71
注册时间
2015-3-19
在线时间
0 小时
发表于 2016-3-15 22:07:56 | 显示全部楼层
222222呢
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
15
金钱
15
注册时间
2018-5-21
在线时间
1 小时
发表于 2018-5-22 10:07:49 | 显示全部楼层
blackroot 发表于 2015-2-1 11:29
**** 作者被禁止或删除 内容自动屏蔽 ****

工程还在么,想做个参考
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-7-5 06:14

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表