久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 6379|回復: 0
打印 上一主題 下一主題
收起左側

FPGA學習-例解阻塞賦值與非阻塞賦值

[復制鏈接]
跳轉到指定樓層
樓主
ID:108531 發表于 2016-3-12 22:34 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
據說阻塞賦值和非阻塞賦值是Verilog 語言中最難理解的知識點之一,我也覺得,從網上翻閱了資料,也看過一些視頻。

以下從兩個典型的例子以及多個角度去分析得到與驗證阻塞賦值與非阻塞賦值的區別,以及各自的特點。
 
 

非阻塞賦值與阻塞賦值特性

1、非阻塞賦值的特性:

賦值語句的流程:

a)計算右邊的表達式得到結果

b)將結果賦值到左邊的變量

這個過程中允許來自任何其他Verilog語句的干擾,也就是說其他Verilog語句都可以在這個時候執行。

2、阻塞賦值特性:

賦值語句的流程:

c)計算右邊的表達式得到結果

d)將結果賦值到左邊的變量

這個過程中不允許有來自任何其他Verilog語句的干擾,這就導致其他的Verilog語句會等待(阻塞)該賦值語句執行完畢后才能被執行,這就是阻塞的含義。

 

根據小梅哥視頻《07_例解阻塞賦值與非阻塞賦值》去分析

一、非阻塞賦值分析

1、非阻塞賦值源程序

/* 實驗名稱:非阻塞賦值 */

module mytest(clk, rst_n, a, b, c, out);

 

    input clk, rst_n, a, b, c;

    output reg [1:0]    out;   

    reg[1:0] d;

   

    always@(posedge clk or negedge rst_n)

        if(!rst_n)

            out <= 2'b0;

        else begin

            d <= a + b;

            out <= d + c;

        end

 

endmodule

 

從源碼中分析,根據非阻塞語句的特性, 假設 a = 0,b = 1, c = 0;

當rst_n為高時,由于d<=a+b、out<=d+c; 是同時執行,所以:

1、右邊表達式的結果:a + b = 0 + 1 = 1、d + c = 0 + 0 = 0

2、再將結果賦值給左邊: d = a + 1就等于 1,out = d + c 就等于 0

 

2、RTL視圖

 

    其實RTL視圖中也能看出來,假設a = 0,b = 1, c = 0; d寄存器為0,out寄存器為0 ;

那么當一個時鐘周期到來:(以下兩步會同時執行,即d與out寄存器會同時從加法器中取值)

1、Add0寄存器會將a(0) + b(1) = 1的值給d寄存器,d寄存器由0變為1

2、Add1寄存器會將d(0) + c(0) = 0的值給out寄存器,out寄存器仍是0

 

3、仿真測試源程序

`timescale 1ns/1ns

`define clock_period 20

 

module mytest_tb;

 

    reg clk, rst_n, a, b, c;

    wire [1:0] out;

   

    mytest u1(clk, rst_n, a, b, c, out);

   

    initial clk = 1;

    always#(`clock_period / 2) clk = ~clk;

   

    initial begin

        rst_n = 1'b0;

        a = 0;

        b = 0;

        c = 0;

        #(`clock_period * 200 + 1);

        rst_n = 1'b1;

        #(`clock_period * 20);

       

        a = 0; b = 0; c = 0;

        #(`clock_period * 200);

        a = 0; b = 0; c = 1;

        #(`clock_period * 200);

        a = 0; b = 1; c = 0;

        #(`clock_period * 200);

        a = 0; b = 1; c = 1;

        #(`clock_period * 200);

        a = 1; b = 0; c = 0;

        #(`clock_period * 200);

        a = 1; b = 0; c = 1;

        #(`clock_period * 200);

        a = 1; b = 1; c = 0;

        #(`clock_period * 200);

        a = 1; b = 1; c = 1;

        #(`clock_period * 200);

        #(`clock_period * 200);

        $stop;

    end

 

endmodule

 

4、波形圖

 

5、波形分析
 

這是 out 從 0 變成 1 前一個時鐘周期的情況,由上圖可以看到,當時鐘上升沿,采樣得到的 a、b、d、c都是等于0,而c是在時鐘上升沿之后才被拉高,所以要在下一個時鐘周期的上升沿才能采樣到c的狀態,

如下圖:

然后我們再往后看看。

 

首先我們看到 01 與 01 直接夾著 0,那么01對應的應該是如下幾行的測試代碼,從代碼可以看到不應該有00的存在。

a = 0; b = 0; c = 1;

#(`clock_period * 200);

a = 0; b = 1; c = 0;

#(`clock_period * 200);

因為c變為0,b變為1是在時鐘周期的上升沿之后才變化,這里暫時不考慮是在上升沿或下降沿時變化

而out和d的狀態因為過程中需要采樣和賦值操作,會有一個邏輯延遲的現象,也就是說也是在時鐘上升沿之后才會變化。以下為后仿真的波形圖,根據接近實際狀況。

 

即圖中A上升沿時:(以下兩步的采樣是在同一個時鐘上升沿同時進行)

1、d的狀態:舊狀態是0,經過以下操作新的狀態是0

同時采樣a的狀態得到是0,b的狀態得到的是0。所以d的狀態變為0

2、out的狀態:舊狀態是1,經過以下操作新的狀態變為1

同時到d是0,c的狀態得到的是1,所以導致out變為1。

    3、在時鐘為穩定電平期間c變為0,b變為1,即在上升沿之后。

 

那么到了B上升沿時(以下兩步的采樣是在同一個時鐘上升沿同時進行)

1、d的狀態:舊狀態是0,經過以下操作新的狀態是1

同時采樣a的狀態得到是0, b的狀態得到的是1。所以d的狀態變為1

2、out的狀態:舊狀態是1,經過以下操作新的狀態變為0

同時采樣到d是0,c的狀態得到的是0,所以導致out變為0。

 

當到C上升沿的時:(以下兩步的采樣是在同一個時鐘上升沿同時進行)

1、d的狀態:舊狀態是1,經過以下操作新的狀態是1

同時采樣a的狀態得到是0b的狀態得到的是1。所以d的狀態變為1

2、out的狀態:舊狀態是0,經過以下操作新的狀態變為1

同時采樣到d是1,c的狀態得到的是0,所以導致out變為0。

 

6、解決出現0的情況。

    分析原因:

從RTL視圖中可以看到是因為多了幾個d寄存器導致out寄存器慢了一個時鐘。

    從波形視圖中可以看到也是因為多了d,導致out寄存器出現了0狀態。

    結論:

將d去掉即可,以后寫程序過程中盡量不要采用中間變量,避免出現多余的寄存器出現,導致不同步。

 

    源程序如下:

    `timescale 1ns/1ns

/* 實驗名稱:非阻塞賦值 */

module mytest(clk, rst_n, a, b, c, out);

 

    input clk, rst_n, a, b, c;

    output reg [1:0]    out;

   

    //reg[1:0] d;

   

    always@(posedge clk or negedge rst_n)

        if(!rst_n)

            out <= 2'b0;

        else begin

            //d <= a + b;

            //out <= d + c;

            out <= a + b + c;

        end

 

endmodule

 

RTL視圖:(從下圖可以看到,來一個時鐘周期,那么out直接就取Add0和Add1的值)

 

    波形圖:(下圖比較小,可以對比上面的波形圖)

 

二、阻塞賦值分析

1、源程序(為了更加清晰的理解,我修改了視頻作者的源碼)

`timescale 1ns/1ns

/* 實驗名稱:非阻塞賦值 */

module mytest(clk, rst_n, a, b, c, out);

 

    input clk, rst_n, a, b, c;

    output reg [1:0]    out;

 

    reg[1:0] d;

    reg run;        // 這個是用來指示當前執行的位置

   

    always@(posedge clk or negedge rst_n)

        if(!rst_n)

            out = 2'b0;

        else begin

            d = a + b;

            run =#1 1;    // 準備執行out賦值時

            out = d + c;

            run =#1 0;    // 執行out賦值后

        end

 

endmodule

 

從源碼中分析,根據阻塞語句的特性, 假設 a = 0,b = 1, c = 0;

當rst_n為高時,由于d=a+b、out=d+c; 是順序執行,所以:

1、先執行d = a + b = 0 + 1 = 1

2、在執行:out = d + c = 1 + 0 = 1;

 

2、RTL視圖(沒看錯,就和去掉了d寄存器的非阻塞賦值代碼生成的電路一樣)

    這里就能看出與非阻塞賦值的不同之處,此處不再解釋。

 

3、仿真測試程序   

與非阻塞仿真測試程序一樣,略。

 

4、波形圖

 

5、波形圖分析

 

根據代碼我們知道run信號是執行完d = a + b 之后為高電平,執行完 out = d + c 為低電平。

通過run信號知道,每次都是在out變化之后才出現低電平。因為如果是同時執行的話,速度非常快,由于run原先就是低電平,變為高電平和變為低電平都是延時1ns,也就是說同時執行1ns之后run依然會是低電平,如下圖,同樣的延時,改為非阻塞的方式,out一直為低。

 

A時鐘上升沿到來:(以下三個步驟為順序執行)

    1、d的狀態:當前狀態0,經過采樣,新的狀態更新為0

        同時采樣a的狀態為0、 b的狀態為0,d = a + b = 0 + 0 = 0;

    2、run信號:

        被拉高

    3、out的狀態:當前狀態0,經過采樣,新的狀態更新為0

        同時采樣d的狀態0、c的狀態為0,out = d + c = 0 + 0 = 0;

4、run信號:

被拉低

 

B時鐘上升沿到來:(以下三個步驟為順序執行)

    1、d的狀態:當前狀態0,經過采樣,新的狀態更新為0

        同時采樣a的狀態為0、 b的狀態為0,d = a + b = 0 + 0 = 0;

    2、run信號:

        被拉高

    3、out的狀態:當前狀態0,經過采樣,新的狀態更新為1

        同時采樣d的狀態0、c的狀態為1,out = d + c = 0 + 1 = 1;

4、run信號:

被拉低

 

C時鐘上升沿到來:(以下三個步驟為順序執行)

    1、d的狀態:當前狀態0,經過采樣,新的狀態更新為0

        同時采樣a的狀態為0、 b的狀態為0,d = a + b = 0 + 0 = 0;

    2、run信號:

        被拉高

    3、out的狀態:當前狀態1,經過采樣,新的狀態更新為1

        同時采樣d的狀態0、c的狀態為1,out = d + c = 0 + 1 = 1;

4、run信號:

被拉低

 

通過以上步驟可以發現,執行的時間、采樣的時間不一樣,會導致不一樣的結果。

 

 

另外一個實驗:根據網上的一篇文章的例子,然后再通過看波形的方式去驗證。

http://blog.163.com/xiaoting_hu/blog/static/50464772201361162838112/

 

 

一、非阻塞賦值分析

1、非阻塞賦值例程

/* 實驗名稱:阻塞與非阻塞賦值差異實驗

 * 程序功能:阻塞賦值 - 觀看 RTL-View 以及波形 */

module mytest(o_y1, o_y2, i_clk, i_rst);

 

    output reg o_y1, o_y2;

    input wire i_clk, i_rst;

   

    //異步復位 alwaysA

    always@(posedge i_clk or negedge i_rst)

        if(!i_rst)    

o_y1 <= 0;        // 低電平復位

        else        

o_y1 <= o_y2;

   

    //異步復位 alwaysB

    always@(posedge i_clk or negedge i_rst)

        if(!i_rst)   

o_y2 <= 1;        // 低電平復位

        else       

o_y2 <= o_y1;

       

endmodule

 

2、RTL 視圖

 

3、仿真源程序

`timescale 1ns/1ns

`define clock_period 20

 

module mytest_tb;

 

    reg clk, rst;

    wire y1, y2;

 

    mytest u1(y1, y2, clk, rst);

   

    initial clk = 1'b1;

    always #(`clock_period / 2) clk = ~clk;

   

    initial begin

        rst = 1'b0;                    // 復位

        #(`clock_period * 5);

        rst = 1'b1;                    // 開始執行

        #(`clock_period * 20);

       

        rst = 1'b0;

        #(`clock_period * 5);

        rst = 1'b1;

        #(`clock_period * 20);

       

        rst = 1'b0;

        #(`clock_period * 5);

        rst = 1'b1;

        #(`clock_period * 20);

       

        $stop;

    end

 

endmodule

 

4、波形圖

 

5、波形分析

//異步復位 alwaysA

    always@(posedge i_clk or negedge i_rst)

        if(!i_rst)    

o_y1 <= 0;        // 低電平復位

        else        

o_y1 <= o_y2;

   

//異步復位 alwaysB

    always@(posedge i_clk or negedge i_rst)

        if(!i_rst)   

o_y2 <= 1;        // 低電平復位

        else       

o_y2 <= o_y1;

 

當 i_rst == 0 :o_y1 被賦值為 0, o_y2 被賦值為 1,由于非阻塞賦值特性,所以無先后順序

當 i_rst == 1 :由于兩 always 塊會被同時執行。即 fpga 會在同一個時鐘上升沿進行采樣,

第一個時鐘周期:

alwaysA 會對 o_y2 采樣得到為高電平,所以給 o_y1 賦值為高電平。

alwaysB 會對 o_y1 采樣得到為低電平,所以給 o_y2 賦值為低電平。

第二個時鐘周期:

alwaysA 會對 o_y2 采樣,因為在第一個周期中 o_y2 因 o_y1 而賦值為低電平,所以采樣得到為低電平,所以給 o_y1 賦值為低電平。

alwaysB 會對 o_y1 采樣,因為在第一個周期中 o_y1 因 o_y2 而賦值為高電平,所以采樣得到為高電平,所以給 o_y2 賦值為高電平。

之后的時鐘周期都是根據這樣的規律進行變化,所以我們看到的波形 o_y1、o_y2 是相反狀態。

 

、阻塞賦值分析

1、阻塞賦值源程序

/* 實驗名稱:阻塞與非阻塞賦值差異實驗

 * 程序功能:阻塞賦值 - 觀看 RTL-View 以及波形

 */

module mytest(o_y1, o_y2, i_clk, i_rst);

 

    output reg o_y1, o_y2;

    input wire i_clk, i_rst;

   

    //異步復位

    always@(posedge i_clk or negedge i_rst)

        if(!i_rst)    

            o_y1 = 0;        // 低電平復位

        else        

            o_y1 = o_y2;

 

    //異步復位

    always@(posedge i_clk or negedge i_rst)

        if(!i_rst)   

            o_y2 = 1;        // 低電平復位

        else       

            o_y2 = o_y1;

       

endmodule

 

2、RTL 視圖(居然和阻塞一樣的 RTL 視圖)

3、仿真源程序

和非阻塞仿真源程序一樣。略

3、波形圖

 

4、波形分析

//異步復位alwaysA

    always@(posedge i_clk or negedge i_rst)

        if(!i_rst)    

            o_y1 = 0;        // 低電平復位

        else        

            o_y1 = o_y2;

   

    //異步復位 alwaysB

    always@(posedge i_clk or negedge i_rst)

        if(!i_rst)   

            o_y2 = 1;        // 低電平復位

        else       

            o_y2 = o_y1;

 

當 i_rst == 0 :o_y1 被賦值為 0, o_y2 被賦值為 1, 由于阻塞賦值特性,所以有先后順序,但無法確定誰先誰后。

當 i_rst == 1由于阻塞賦值的特性,在執行阻塞賦值語句時其他語句均不能得到執行。由于兩 always 塊會被同時執行,而且 o_y1 和 o_y2 的取值是互相影響,所以 o_y1、o_y2 值取決于那個 always 塊的賦值語句先執行。從波形來看,明顯是 alwaysA 最新得到執行。

第一個時鐘周期:

alwaysA 會對 o_y2 采樣得到為高電平,所以給 o_y1 賦值為高電平。

alwaysB 會對 o_y1 采樣,因在 alwaysA 程序塊 o_y1 因 o_y2 而賦值為高電平,所以給 o_y2 賦值為高電平。

 

第二個時鐘周期:

alwaysA 會對 o_y2 采樣,因為在第個周期中 o_y2 因 o_y1 而賦值為電平,所以采樣得到為電平,所以給 o_y1 賦值為電平。

alwaysB 會對 o_y1 采樣,因在 alwaysA 程序塊 o_y1 因 o_y2 而賦值為高電平,所以給 o_y2 賦值為高電平。

 

也就是說 o_y1 = o_y2; o_y2 = o_y1; 是順序執行的,所以我們看到的波形 o_y1、o_y2 都是高電平狀態.


最后附上非阻塞賦值的一個很有意思的例子。

 

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏1 分享淘帖 頂 踩
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 日韩成人免费视频 | 在线看亚洲 | 欧美精品中文字幕久久二区 | 国产伦精品一区二区三区高清 | 做a视频在线观看 | 91久久精品日日躁夜夜躁国产 | 国产一区二 | 91免费观看国产 | 国产精品视频免费看 | 精品国产区 | 欧美综合久久 | 亚洲国产网站 | 成人在线视频免费观看 | 久久久免费 | 亚洲精品在线91 | 中文字幕亚洲视频 | 欧美日韩精品免费观看 | 一区二区三区在线 | 亚洲激情专区 | 国产精品欧美日韩 | 日一区二区 | 亚洲精品一区二区冲田杏梨 | 欧美成人高清视频 | 中文字幕一区在线观看视频 | 一区二区电影网 | 亚洲欧美日韩网站 | 免费h在线 | 天天操夜夜操 | 日韩成人在线网址 | 黄色网址在线免费观看 | 一级a毛片 | 视频在线观看一区二区 | av网站免费 | 国产精品久久久免费 | 好姑娘影视在线观看高清 | 欧美在线观看黄色 | 日韩精品一区二区三区免费观看 | 中文字幕一区二区三区四区五区 | 毛片区| 日韩精品视频一区二区三区 | 久久久www成人免费无遮挡大片 |