仿真中的阻塞赋值与非阻塞赋值

仿真中的阻塞赋值与非阻塞赋值

问题背景

今天在写uart串口的代码,仿真的时候遇到了一个问题。在uart模块中的两个信号send_en和tx_state。本来期望的是send_en出现一个时钟高电平,tx_state拉高代表了串口正在传送数据。代码如下:

//tx_state信号
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            tx_state <= 1'b0;
        else if(send_en)
            tx_state <= 1'b1;
        else if(baud_clk_cnt == 4'd11)
            tx_state <= 1'b0;
        else 
            tx_state <= tx_state;
    end

但是仿真的时候出现了一个问题:在send_en信号出现一个时钟的高电平之前,tx_state信号就已经被拉高了。
仿真如图:
在这里插入图片描述
从图中可以看出send_en信号还没有拉高,但是tx_state信号已经发生了变化。

原因

这是由于在仿真代码没有特别区分阻塞赋值和非阻塞赋值所引起了。
仿真代码如下:

initial clk = 1;
    always #(`clock_period/2) clk = ~clk;
 
    initial begin
        rst_n = 0;
        send_en = 0;
        tx_data = 8'h55;
        baud_set = 3'b001;
        #(`clock_period*50);
        rst_n = 1;
        #(`clock_period*500);
        send_en = 1;
        #(`clock_period);
        send_en = 0;
        @(posedge tx_done);
        #(`clock_period);
        send_en = 1;
        tx_data = 8'haa;
        #`clock_period;
        send_en = 0;
        #(`clock_period*500);
        @(posedge tx_done);
        #(`clock_period*50000);
        $stop;
    end

从上面的代码可以看出所有的仿真语句用的都是非阻塞赋值,那所有的信号都是由非阻塞赋值产生的,此时clk采样如果遇到了信号的上升沿就会提取上升沿之后的信号值。而之前产生的问题就是在图中可以看clk信号在上升沿提取的信号是send_en信号上升沿之后的值也就是高电平,而此时就可以驱动tx_state信号变为高电平,这样就出现了send_en的变化没有引起tx_state信号变化的问题了。

解决方法

这里只需要将send_en信号的赋值由非阻塞赋值转为阻塞赋值就可以了。同时我们也将除了时钟信号的其他信号也转为阻塞赋值。

initial clk = 1;
    always #(`clock_period/2) clk = ~clk;
 
    initial begin
        rst_n <= 0;
        send_en <= 0;
        tx_data <= 8'h55;
        baud_set <= 3'b001;
        #(`clock_period*50);
        rst_n <= 1;
        #(`clock_period*500);
        send_en <= 1;
        #(`clock_period);
        send_en <= 0;
        @(posedge tx_done);
        #(`clock_period);
        send_en <= 1;
        tx_data <= 8'haa;
        #`clock_period;
        send_en <= 0;
        #(`clock_period*500);
        @(posedge tx_done);
        #(`clock_period*50000);
        $stop;
    end

仿真结果如下图
在这里插入图片描述

思考

这个问题让我们注意到在仿真的时候也需要考虑到阻塞赋值和非阻塞赋值的问题,不然很可能和造成代码明明是正确的却出现和仿真不一致的问题。
这里我还特意写一个计数器来确定rst_n信号在仿真中使用阻塞赋值和非阻塞赋值的区别。
cnt_test.v


module cnt_test(
    clk,
    rst_n,
    cnt
);
    input clk;
    input rst_n;
    output reg [7:0]cnt;

    always@(posedge clk or negedge rst_n)begin
        if(~rst_n)
            cnt <= 8'd0;
        else if(cnt == 8'd99)
                cnt <= 8'd0;
        else
            cnt <= cnt + 1'b1;
    end
endmodule

仿真代码
cnt_test_tb.v

`timescale 1ns/1ns
`define clock_period 20
module test_tb;

    reg clk;
    reg rst_n;
    wire [7:0]cnt;

    test u_test(
        .clk    ( clk    ),
        .rst_n  ( rst_n  ),
        .cnt    ( cnt    )
    );

    initial begin
        clk = 1'b1;
        forever begin
            #10 clk = ~clk;
        end
    end

    initial begin
        rst_n <= 1'b0;
        #(`clock_period*10);
        rst_n <= 1'b1;
        #(`clock_period*500);
        $stop;
    end

endmodule 

有兴趣的朋友可以看看将仿真代码里的阻塞赋值改为非阻塞赋值,其中rst_n对计数器cnt的影响。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值