Menu Close

UART 接收器设计(RXD)–判多

上节内容讲解了过采样原理以及利用过采样进行数据中心位置的采样,从而消除采样时由于采样点相位不确定带来的亚稳态、抗干扰性能差,数据误判等因素。本节内容介绍另外一种常用的方法,即将过采样的每个值进行统计,观察在一个数据位的采样中0与1的比例,从而判定该位是0还是1。例如在位的采样中,如果是8倍过采样,则每个数据位总共采样8次,理论上讲如果该数据位为0,则8次采样的值都应该为0。如果该位为1,采样的8次结果都应该为1,但由于频率偏移,采样相位偏移,噪声等各种因素的影响,最终会出现非全1和非全0的情况。因此需要利用判决。下面就起始位的判多采样为例进行介绍。为了方便说明问题,如图1,2展示了奇数次过采样(9次)对数据位(bit)的采样过程。实际采样中一般采用偶数次过采样(如4次或8次等),这与2进制的进位特性有关。

图1

图2

1.判多的方式依然需要边沿检测配合

边沿检测可以正确定位数据的位置。如图1,由于0采样点为高电平,1采样点为低电平,可以知道在此处有下降沿变化。由此推断采样点1,2…8应该分布在整个数据为上。图2可以看出,如果不考虑边沿检测,从采样点1到8可能随机分布在不同的接收位上,极端的情况如图2,一半采样在IDLE位(高电平),一半在起始位,在这种情况下,判多方法也一样无法正确将接收的位提取出来。如图2,从第一个0采样点到到采样点8,统计为1的个数为5,0的个数为4,判定为1,此时没用捕获到起始位。如果从第2个0采样点开始,如果起始位后面的数据为1,9次采样后的结果也是1多0少,因此将会丢失起始位的判定,从而无法成功捕获整个数据帧。因此需要首先检测边沿,确定位起始后在进行判多处理。

除了边沿检测,是否还有其他的方法可以成功捕获起始位呢?可以使用滑动串口的方法,即在IDLE状态通过移位的方式动态统计8采用中1的个数,判定起始位。

 

2. 判决方法

如图1,一旦边沿确定后,利用采样点1…8的值就可以进行判决,判决方法如下:采样点1…8中1的个数大于等于5,判定该数据位为1,否则为0。

3. 其它接收位的判定

起始位正确定位后,紧随起始位的其它位(数据位、校验位、停止位等)不用判断边沿,直接从下一个的0采样点开始统计每个位的1的个数即可。

例1,利用判多设计RX的参数化接收程序,要求使用8次过采样。

`timescale 1ns / 1ps
module rx_more_one
#(
   parameter PARITY = "ODD",    //"ODD"--odd parity, "EVEN"--even parity, others--no parity
   parameter STOP_BIT = 1
)
(
   input sys_clk,
   input rst,
   
   input rx,
   
   input bd8_rate,

   output reg [7:0] rx_data = 0,
   output reg rx_rdy = 0
);


localparam [ 3: 0 ]
           IDLE0     = 0,
           IDLE1     = 1,
           START     = 2,
           FIRST_BIT = 3,
           SEC_BIT   = 4,
           THIRD_BIT = 5,
           FOUTH_BIT = 6,
           FIF_BIT   = 7,
           SIX_BIT   = 8,
           SEVN_BIT  = 9,
           EIGTH_BIT = 10,
           PAR_BIT   = 11,
           STOP1_BIT = 12,
           STOP2_BIT = 13;

localparam EXTRA_COUNT = (PARITY == "ODD"||PARITY == "EVEN") ? 8 : 0;
localparam EXTRA_STOP_COUNT = (STOP_BIT == 2) ? 8 : 0;


reg [ 1: 0 ] rx_r;
reg rx_nedge; //
reg [ 7: 0 ] rx_data_tmp;
reg rx_rdy_tmp;
reg [ 7: 0 ] sample_bit;

wire [ 3: 0 ] count_one;
wire rx_bit;



reg [ 3: 0 ] sample_st_cs, sample_st_ns;
reg [ 6: 0 ] sample_count_cs, sample_count_ns;
reg [ 6: 0 ] sample_count_cs_r;


assign count_one = sample_bit[ 0 ] + sample_bit[ 1 ] + sample_bit[ 2 ] + sample_bit[ 3 ] + sample_bit[ 4 ] + sample_bit[ 5 ] + sample_bit[ 6 ] + sample_bit[ 7 ];

assign rx_bit = ( count_one > 4 ) ? 1'b1 : 1'b0;

always@( posedge sys_clk or posedge rst )
if ( rst )
begin
    rx_r <= 0;
    rx_nedge <= 0;
end
else
begin
    if ( bd8_rate )
    begin
        rx_r <= { rx_r[ 0 ], rx };         // rx shift left
        rx_nedge <= (rx_r == 2'b10) ? 1'b1 : 1'b0;  // capture the falling edge
    end
end


always@( posedge sys_clk or posedge rst )
if ( rst )
begin
    sample_st_cs <= IDLE0;
end
else
begin
    sample_st_cs <= sample_st_ns;
end


always@( posedge sys_clk or posedge rst )
if ( rst )
begin
    sample_count_cs <= 0;
end
else
begin
    sample_count_cs <= sample_count_ns;
end


always@( * )
begin
    sample_count_ns = sample_count_cs;
    sample_st_ns = sample_st_cs;

    if ( bd8_rate )
    begin
        case ( sample_st_cs )
        IDLE0:
        begin     //idle state, waiting for ta least 10bits 1
            if ( sample_count_cs == (80 + EXTRA_COUNT + EXTRA_STOP_COUNT ) )
                sample_st_ns = IDLE1;
            else if ( rx_r[ 1 ] )
                sample_count_ns = sample_count_cs + 1;
            else
                sample_count_ns = 0;
        end
        IDLE1:
        begin //waiting for start_bit's edge
            sample_count_ns = 0;
            if ( rx_nedge )
                sample_st_ns = START;
        end
        START:
        begin
            sample_count_ns = sample_count_cs + 1;
            if ( sample_count_cs == 5 )
            begin
                sample_count_ns = 0;
                if ( rx_bit ) // sample times bigger than 4
                    sample_st_ns = IDLE1; // detect start flag
                else
                    sample_st_ns = FIRST_BIT;
            end
        end
        FIRST_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = SEC_BIT;
            end
        end
        SEC_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = THIRD_BIT;
            end
        end
        THIRD_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = FOUTH_BIT;
            end
        end
        FOUTH_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = FIF_BIT ;
            end
        end
        FIF_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = SIX_BIT;
            end
        end
        SIX_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = SEVN_BIT;
            end
        end
        SEVN_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = EIGTH_BIT;
            end
        end
        EIGTH_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;

                if ( ( PARITY == "ODD" ) || ( PARITY == "EVEN" ) )
                    sample_st_ns = PAR_BIT;
                else
                    sample_st_ns = STOP1_BIT;
            end
        end
        PAR_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = STOP1_BIT;
            end
        end
        STOP1_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 6 )
            begin
                sample_count_ns = 0;

                if ( STOP_BIT == 2 )
                    sample_st_ns = STOP2_BIT;
                else
                    sample_st_ns = IDLE1;
            end
        end
        STOP2_BIT:
        begin
            sample_count_ns = sample_count_cs + 1;

            if ( sample_count_cs == 7 )
            begin
                sample_count_ns = 0;
                sample_st_ns = IDLE1;
            end
        end
        endcase
    end
end


always@( posedge sys_clk or posedge rst )
if ( rst )
begin
    sample_bit <= 0;
    rx_rdy_tmp <= 1'b0;
    rx_data_tmp <= 0;
end
else
begin
    rx_rdy_tmp <= 1'b0;

    if ( sample_st_cs == IDLE1 )
        sample_bit <= rx_r[ 1 ];

    case ( sample_st_cs )
    START, FIRST_BIT, SEC_BIT, THIRD_BIT, FOUTH_BIT, FIF_BIT, SIX_BIT, SEVN_BIT, EIGTH_BIT, PAR_BIT, STOP1_BIT, STOP2_BIT :
    begin
        if ( bd8_rate )
            sample_bit <= { sample_bit[ 6: 0 ], rx_r[ 1 ] };
    end
    endcase

    sample_count_cs_r <= sample_count_cs;

    if (( sample_count_cs_r == 7 ) & bd8_rate )
    begin //delay one clock
        case ( sample_st_cs )
        FIRST_BIT:  rx_data_tmp[ 0 ] <= rx_bit;
        SEC_BIT:    rx_data_tmp[ 1 ] <= rx_bit;
        THIRD_BIT:  rx_data_tmp[ 2 ] <= rx_bit;
        FOUTH_BIT:  rx_data_tmp[ 3 ] <= rx_bit;
        FIF_BIT:    rx_data_tmp[ 4 ] <= rx_bit;
        SIX_BIT:    rx_data_tmp[ 5 ] <= rx_bit;
        SEVN_BIT:   rx_data_tmp[ 6 ] <= rx_bit;
        EIGTH_BIT:
        begin
            rx_data_tmp[ 7 ] <= rx_bit;
            rx_rdy_tmp <= 1'b1;
        end
        default: ;
        endcase
    end
end


always@( posedge sys_clk )
begin
    if ( rx_rdy_tmp )
    begin
        rx_data <= rx_data_tmp;
        rx_rdy <= 1'b1;
    end
    else rx_rdy <= 1'b0;
end

endmodule

 

仿真程序:仿真程序继续使用上节内容提供的(中心对齐)testbench,

`timescale 1 ns / 1 ps
 
module tb
(
);
 
 
parameter BAUD_RATE = 115200;
parameter FREQUENCY = 100_000_000;
 
localparam  TX_DIV_COE = FREQUENCY / (BAUD_RATE);
localparam  RX_DIV_COE = FREQUENCY / (BAUD_RATE*8);
 
reg [18:0]  tx_clk_div;
reg [18:0]  rx_clk_div;
 
parameter PERIOD = 10   ; //100Mhz
 
reg   clk;
reg   rst;
reg   rx_bd_en;
reg   tx_bd_en;
 
 
 
initial 
begin
    clk = 0;
    #(PERIOD/2);
 
    forever 
        #(PERIOD/2) clk = ~clk;
end
 
initial 
begin
    rst = 1;
    #50
 
    rst = 0;
     
     #300000;
     
//	 $stop;
end
wire   tx;
reg   [7:0] tx_count;
reg   [7:0] tx_data = 0;
reg   [9:0] tx_send=10'b11_1111_1111;

 
always@(posedge clk or posedge rst)
if(rst) 
begin
    tx_count = 0;
end
else 
begin
    if(tx_bd_en)
    begin
        if(tx_count == 50)
        begin
               tx_data <= tx_data + 1;
                tx_send<= {1'b1, tx_data, 1'b0};
            tx_count = 0;
        end
        else 
        begin
               tx_send<= {1'b1, tx_send[8:1]};
            tx_count = tx_count + 1;
        end     
    end
end

assign tx = tx_send[0]; 
 
always@(posedge clk or posedge rst)
if(rst) 
begin
    tx_clk_div <= 0;
    rx_clk_div <= 0;
    tx_bd_en <= 0;
    rx_bd_en <= 0;
end
else 
begin
    tx_bd_en <= 1'b0;
    if(tx_clk_div == TX_DIV_COE)
    begin
        tx_clk_div <= 0;
        tx_bd_en <= 1'b1;
    end
    else 
        tx_clk_div <= tx_clk_div + 1'b1;
        
    rx_bd_en <= 0;
    if(rx_clk_div == RX_DIV_COE)
    begin
        rx_clk_div <= 0;
        rx_bd_en <= 1'b1;
    end
    else 
        rx_clk_div <= rx_clk_div + 1;
end
 

wire  [7:0] rx_data;
wire  rx_rdy;

//rx #(.PARITY ("NONE")) 
rx_more_one #
(
    .PARITY ("NONE"),
    .STOP_BIT (1)
) 
rx_inst
(
    .sys_clk    (clk),
    .rst        (rst),
    .rx         (tx),
    .bd8_rate   (rx_bd_en),
    .rx_data    (rx_data),
    .rx_rdy     (rx_rdy)
);

 
 
endmodule

 

图3-1

图3-2

由于判多相对于中心对齐的采样相对复杂,因此例1对判多的描述采用了3段式状态机实现,第一段采用实现次态(sample_st_ns)到现态(sample_st_cs)的转换。第二段描述了次态逻辑,根据sample_count_cs的值实现状态跳转。第三段描述了rx的数据采样及实现。

 

Posted in FPGA, FPGA 教材教案, IP开发, Quartus II, Quartus II, Verilog, Verilog, Vivado, Vivado, 教材与教案, 文章

2 Comments

  1. 李红梅

    老师,代码里面好像写错了一个,assign count_one = sample_bit [0] +……sample_bit [6] + sample_bit [7] 文件里面 将第7位写成了第0位。

    有一个疑惑:
    代码的最后用了一个时序逻辑,将rx_data_tmp 寄存到rx_data里面,将rx_rdy_tmp寄存到rx_rdy里面 输出,而不是直接输出rx_data_tmp和rx_rdy_tmp,是为了保证输出信号的稳定么?

发表回复

相关链接