OpenEdv-开源电子网

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

[XILINX] UART章节中说的亚稳态是什么意思?

[复制链接]

5

主题

17

帖子

0

精华

新手上路

积分
48
金钱
48
注册时间
2023-12-1
在线时间
4 小时
发表于 2023-12-21 23:43:46 | 显示全部楼层 |阅读模式

学到这里,没搞懂为什么要打拍,什么是亚稳态,这和上升时间、下降时间有什么区别?这又不是按键,不存在抖动呀。
我理解直接做个边沿触发,然后输入一个115200的抽样时钟就可以了。是不是不能使用边沿触发?我后续分享出来。

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

3

主题

2013

帖子

0

精华

资深版主

Rank: 8Rank: 8

积分
5617
金钱
5617
注册时间
2018-10-21
在线时间
1591 小时
发表于 2023-12-22 14:08:22 | 显示全部楼层
比如你用一个时钟来采集一个在变化的异步信号,将采集到的值保存到reg0,这个时候采集到的值是不确定的,reg0有可能是0也有可能是1;这个不确定性会继续往后级传递,也就是reg0的值也是不确定的。将reg0赋值给Data0和Data1,假设Data0=0,Data1=1,那还搞个锤子,数据不就全乱套了吗。所以对异步信号打两拍,就是为了确保得到的这个reg值是一个确定的值。
回复 支持 反对

使用道具 举报

5

主题

17

帖子

0

精华

新手上路

积分
48
金钱
48
注册时间
2023-12-1
在线时间
4 小时
 楼主| 发表于 2023-12-23 17:24:02 | 显示全部楼层
QinQZ 发表于 2023-12-22 14:08
比如你用一个时钟来采集一个在变化的异步信号,将采集到的值保存到reg0,这个时候采集到的值是不确定的,re ...

是不是电平处于无法判决的中间态?但是这个值已经被判决为0或者1了,那打2拍这个值不还是不确定的吗?
回复 支持 反对

使用道具 举报

5

主题

17

帖子

0

精华

新手上路

积分
48
金钱
48
注册时间
2023-12-1
在线时间
4 小时
 楼主| 发表于 2023-12-23 17:41:00 | 显示全部楼层
看了源码找到了大概原因:
1、教材中的源码使用的是对比2个reg判断是否为下降沿,而不是真正的下降沿触发。
2、RXD的输入电压可能是缓变的,系统时钟为采样时钟,采样时钟的周期可能远短于信号的上升时间或下降时间,导致电平判决时刻处于CMOS的中间电平。
3、CMOS中间电平判决时,判决结果既可能是1也可能是0。导致逻辑电平反复在0、1翻转。

加上看不懂课程,我重写了代码。使用真正的下降沿触发(节约n个d触发器)
思路是:
1、准备好采样时钟,计数器。
2、在检测到rxd的下降沿后,启动采样时钟(对齐判决时钟相位)
3、每个采样时钟上升沿进行抽样判决,采样计数器累加。
4、最后1个bit判决完成后,在最后一个采样周期结束时,拉起done信号。
我另外画了电路图,个人觉得做这个设计需要先画数电的电路图,不然看课程的波形一头雾水。
完整源码如下:
module uart_rx(
    input               clk         ,  //系统时钟
    input               rst_n       ,  //系统复位,低有效

    input               uart_rxd    ,  //UART接收端口
    output  reg         uart_rx_done,  //UART接收完成信号
    output    [7:0]  uart_rx_data   //UART接收到的数据
    );

parameter CLK_FREQ = 50000000;               //系统时钟频率
parameter UART_BPS = 115200  ;               //串口波特率
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次

//reg define
reg  [3:0 ]  rx_cnt     ;  //接收数据计数器
reg  [15:0]  baud_cnt   ;  //波特率计数器
//reg  [9:0 ]  rx_data_t  ;  //接收数据寄存器

reg sample_clk;
reg rx_flag;


//起始位下降沿触发
initial rx_flag <= 1'b0;
always @(negedge uart_rxd or posedge uart_rx_done)begin
        if(!uart_rxd)
                rx_flag <= 1'b1;
        else if(uart_rx_done)
                rx_flag <= 1'b0;
end


//波特率的计数器
initial sample_clk = 0;
always @(posedge clk or negedge rst_n,rx_flag ) begin
    if(!rst_n || !rx_flag)
        baud_cnt <= 16'd0;
    else if(rx_flag)    //处于接收过程时,波特率计数器(baud_cnt)进行循环计数
        if(baud_cnt < BAUD_CNT_MAX/2 - 1'b1)
            baud_cnt <= baud_cnt + 16'b1;
        else begin
            baud_cnt <= 16'd0; //计数达到一个波特率周期后清零
                        sample_clk <= ~sample_clk;
                end
    else
        baud_cnt <= 16'd0;     //接收过程结束时计数器清零
end

//复位条件:1、起始位为1。2、停止位为0.
//uart_rx_done,rx_cnt == 9

//采样计数器
always @(posedge sample_clk or negedge rst_n,rx_flag)begin
        if(!rst_n || !rx_flag)
                rx_cnt <= 4'd0;
        else if(sample_clk && rx_cnt < 4'd10)
                rx_cnt <= rx_cnt + 1;
        else
                rx_cnt <= 4'd0;
end


//相位对齐,采样
reg                [9:0]        rx_temp;                //数据缓存,等同于原子哥代码中的rx_data_t
initial                        rx_temp <= 10'd0;
always @(posedge sample_clk,rx_flag or negedge rst_n)begin
        if(!rst_n)
                rx_temp <= 10'd0;
        else if(sample_clk)begin
                rx_temp = rx_temp << 1;
                rx_temp[0] = uart_rxd;
        end
        else if(rx_flag)                        //新的数据到达
                rx_temp <= 10'd0;                       
        else
                rx_temp <= rx_temp;
end

//结束标记,采样时钟下降沿确认
wire check;
initial uart_rx_done <= 1'b0;
assign check = rx_temp[9] & !rx_temp[0];        //协议检查,确认起始位和停止位
always @(posedge rx_flag or negedge rst_n,sample_clk)begin
        if(!rst_n)
                uart_rx_done <= 1'b0;
        else if(rx_cnt == 4'ha && sample_clk == 1'b0)
                uart_rx_done <= 1'b1;
        else if(rx_flag)
                uart_rx_done <= 1'b0;
end

assign uart_rx_data[7:0] = rx_temp[8:1];        //直连

endmodule
回复 支持 反对

使用道具 举报

5

主题

17

帖子

0

精华

新手上路

积分
48
金钱
48
注册时间
2023-12-1
在线时间
4 小时
 楼主| 发表于 2023-12-24 16:06:45 | 显示全部楼层
大无语,功能仿真pass了,综合出来的电路不是正常的电路,好多电路被删除了。。。
回复 支持 反对

使用道具 举报

5

主题

17

帖子

0

精华

新手上路

积分
48
金钱
48
注册时间
2023-12-1
在线时间
4 小时
 楼主| 发表于 2023-12-24 22:03:44 | 显示全部楼层
luzhenyue 发表于 2023-12-24 16:06
大无语,功能仿真pass了,综合出来的电路不是正常的电路,好多电路被删除了。。。

我知道了!我知道了!
如果使用下降沿触发,会出现如下告警
Mix of synchronous and asynchronous control for register uart_rx_done_reg in module uart_rx.

这种告警看起来无关痛痒,仿真也能pass,但是在synthesis的时候会出现综合出来的电路有问题(RTL电路没问题),但是有的时候也能被synthesis出来正确的电路。
目前暂时理解为:vivado的synthesis无法实现同步、异步混合赋值。
理论上应该可以用逻辑单元构造出来,有时间我试试。
回复 支持 反对

使用道具 举报

5

主题

17

帖子

0

精华

新手上路

积分
48
金钱
48
注册时间
2023-12-1
在线时间
4 小时
 楼主| 发表于 2023-12-26 23:36:47 | 显示全部楼层
luzhenyue 发表于 2023-12-24 22:03
我知道了!我知道了!
如果使用下降沿触发,会出现如下告警
Mix of synchronous and asynchronous cont ...

搞定了!无打拍的uart_rx模块,触发时刻相比打拍在概率提前10ns。
总结:
vivado无法synthesis多信号边沿触发复位的代码(电路不正确),需要使用组合逻辑电路+REG(D触发器)的形式帮助vivado实现电路(schematic),目前只要schematic对了,implementation暂时没遇到问题。


源码如下:

module uart_rx(
    input               clk         ,  //系统时钟
    input               rst_n       ,  //系统复位,低有效

    input               uart_rxd    ,  //UART接收端口
    output  reg         uart_rx_done,  //UART接收完成信号
    output   [7:0]        uart_rx_data   //UART接收到的数据
    );

parameter CLK_FREQ = 50000000;               //系统时钟频率
parameter UART_BPS = 115200  ;               //串口波特率
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次

//reg define
reg  [3:0 ]  rx_cnt     ;  //接收数据计数器
reg  [15:0]  baud_cnt   ;  //波特率计数器


reg sample_clk;
reg rx_flag;


//起始位下降沿触发
initial rx_flag = 1'b0;                //初始化
wire rx_flag_clk;                        //多信号边沿触发
assign rx_flag_clk = (!uart_rxd && !rx_flag) || (uart_rx_done && rx_flag);        //REG输入值预备
always @(posedge rx_flag_clk or negedge rst_n)begin
    if(!rst_n)                        //异步复位
                rx_flag <= 1'b0;
        else
                rx_flag <= ~rx_flag;        //rx_flag_clk的组合逻辑已经确定了值,在电路上仅做翻转。
end

//波特率的计数器
initial sample_clk = 0;                        //采样时钟
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        baud_cnt <= 16'd0;
        sample_clk <= 1'b0;
    end
    else if(rx_flag)    //处于接收过程时,波特率计数器(baud_cnt)进行循环计数
        if(baud_cnt < BAUD_CNT_MAX/2 - 1'b1)
            baud_cnt <= baud_cnt + 16'b1;
        else begin
            baud_cnt <= 16'd0; //计数达到一个波特率周期后清零
                        sample_clk <= ~sample_clk;
                end
    else begin
        baud_cnt <= 16'd0;
        sample_clk <= 1'b0;
        end
end


//数据计数器,用于记录采样到的bit
wire rx_cnt_clk;        //多信号边沿触发
assign rx_cnt_clk = sample_clk || (!uart_rxd && !rx_flag);
always @(posedge rx_cnt_clk or negedge rst_n)begin
        if(!rst_n)
                rx_cnt <= 4'd0;
        else
                if(rx_flag)                                        //在接收状态时才进行累加
                        rx_cnt <= rx_cnt + 1;
                else
                        rx_cnt <= 4'd0;
end


//相位对齐,采样
wire        [8:0]        rx_temp_next_state;
reg                [8:0]        rx_temp;                //数据缓存,等同于原子哥代码中的rx_data_t
assign rx_temp_next_state = {uart_rxd,rx_temp[8:1]};
initial rx_temp = 9'h0;
always @(posedge sample_clk)begin
    if(sample_clk && rx_cnt < 4'd9)                //这里注意是在d9就停止采样了(省略停止位)
                rx_temp[8:0] <= rx_temp_next_state;
        else
                rx_temp[8:0] <= rx_temp[8:0];
end


wire check;
//assign check = rx_temp[9] & !rx_temp[0];        //协议检查,确认起始位和停止位。没有FEC没啥意义。

wire done_clk;        //多信号边沿触发
wire done_sig;        //结束标记
assign done_sig = (rx_cnt == 4'ha);        //结束信号
assign done_clk = (!uart_rxd &&uart_rx_done) || (!uart_rx_done && done_sig);
always @(posedge done_clk or negedge rst_n)begin
        if(!rst_n)//复位
                uart_rx_done <= 1'b0;
        else
                uart_rx_done <= ~uart_rx_done;                //同上,化简之后是电平翻转
end

assign uart_rx_data[7:0] = rx_temp[8:1];        //直连

endmodule
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2024-11-22 16:56

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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