寫在前面
??本章節(jié)主要參考《SOC設(shè)計(jì)方法與實(shí)現(xiàn) 第三版》第七章 。也是整個(gè)SOC架構(gòu)學(xué)習(xí)的起點(diǎn),下面我們正式開始!
??目前流行的設(shè)計(jì)架構(gòu):GALS(Global Asynchronize Local Synchronize),即全局異步局部同步,這是順應(yīng)了多核SOC設(shè)計(jì)的潮流同時(shí)也符合EDA工具對(duì)同步電路設(shè)計(jì)的廣泛支持。
回顧:同步電路
??同步電路的定義:觸發(fā)器、寄存器都由一個(gè)統(tǒng)一的時(shí)鐘控制。并且在同步電路中,為方便后端設(shè)計(jì),一般使用統(tǒng)一觸發(fā)方式(上升沿或下降沿中的一種)
??同步電路時(shí)序收斂:滿足觸發(fā)器的建立時(shí)間和保持時(shí)間。EDA的時(shí)序分析工具可以檢查同步電路的收斂問題。
??同步電路優(yōu)缺點(diǎn):
同步性減少了電路競爭冒險(xiǎn)和毛刺噪聲
時(shí)鐘偏斜(時(shí)鐘到達(dá)每個(gè)觸發(fā)器時(shí)間不一致,可以用EDA時(shí)鐘樹綜合,以最長的時(shí)鐘路徑為基準(zhǔn),短的路徑加入延時(shí)單元)和功耗問題
異步電路
??異步電路的觸發(fā)器可能在任何時(shí)間跳變。
??異步電路采用握手協(xié)議實(shí)現(xiàn)自同步。常用的握手協(xié)議分為二相位和四相位握手。
下圖的Req和Ack是簡單的握手信號(hào),即request和acknowledge,先握手的是request而后回應(yīng)的是acknowledge
??二相位握手協(xié)議在Req和Ack的任何一對(duì)跳變沿組合處均可實(shí)現(xiàn)一次數(shù)據(jù)傳輸。而四相位握手協(xié)議只能實(shí)現(xiàn)一次數(shù)據(jù)傳輸。四相位握手協(xié)議是首選(因?yàn)榉€(wěn)定性高)
??異步電路優(yōu)缺點(diǎn):
模塊化突出、對(duì)信號(hào)延遲不敏感、沒有時(shí)鐘偏斜、高速低功耗
設(shè)計(jì)復(fù)雜,缺乏EDA工具支持
亞穩(wěn)態(tài)
??亞穩(wěn)態(tài)本質(zhì)上是信號(hào)跨時(shí)鐘域傳輸時(shí),輸入的信號(hào)可能會(huì)不滿足某個(gè)觸發(fā)器的建立保持時(shí)間要求,從而鎖存進(jìn)一個(gè)不正常的電平(可能代表著NMOS和PMOS同時(shí)導(dǎo)通,并且可能傳播下去,會(huì)造成后續(xù)電路工作不正常)
??降低亞穩(wěn)態(tài)發(fā)生概率:使用二級(jí)觸發(fā)器(同步器),其本質(zhì)是因?yàn)閬喎€(wěn)態(tài)最終會(huì)穩(wěn)定在一個(gè)電平上,增加一級(jí)D觸發(fā)器可以保證等亞穩(wěn)態(tài)恢復(fù)到0或者1再采樣,這樣亞穩(wěn)態(tài)就不會(huì)再傳播下去。
一點(diǎn)思考:(如有不對(duì)請指正)
??但是這樣不能保證跨時(shí)鐘域的數(shù)據(jù)傳輸正確,只能保證亞穩(wěn)態(tài)概率降到很低。
??因?yàn)榘l(fā)生亞穩(wěn)態(tài)時(shí),第二級(jí)觸發(fā)器等待第一級(jí)觸發(fā)器亞穩(wěn)態(tài)輸出回恢復(fù)穩(wěn)定再采樣,然而恢復(fù)到0還是1這是不能確定的,因此在這種情況下二級(jí)觸發(fā)器采樣輸出的數(shù)據(jù)雖說不會(huì)有暫穩(wěn)態(tài)傳播下去,但是數(shù)據(jù)有可能是錯(cuò)誤的
??同步器的代碼如下(參考上圖)
module synchronizer(
input bclk,
input reset_b,
input adat,
output bdat
);
reg bdat1,bdat2;
always@(posedge bclk, negedge reset_b)begin
if(reset_b)
{bdat2,bdat1}<=2'b0;
else
{bdat2,bdat1}<={bdat1,adat};
end
assign bdat=bdat2;
endmodule
異步控制信號(hào)同步
??同步的定義:把異步信號(hào)采樣到目的時(shí)鐘域的動(dòng)作
??1.慢時(shí)鐘域控制信號(hào)同步到快時(shí)鐘域。
??問題描述:直接用同步器采樣的話,慢時(shí)鐘域持續(xù)1個(gè)慢時(shí)鐘周期的控制信號(hào)采樣到快時(shí)鐘域可能會(huì)持續(xù)多個(gè)快周期。在慢時(shí)鐘域中被認(rèn)為是一次控制就會(huì)在快時(shí)鐘域中被認(rèn)為多次控制操作,該問題如下圖所示。
??經(jīng)過快時(shí)鐘下的同步器采樣(兩級(jí)觸發(fā)器),得到rd_en_s2f2
控制信號(hào)(圖上標(biāo)注有誤,rd_en_s2f
應(yīng)當(dāng)為rd_en_s2f2
),該信號(hào)在快時(shí)鐘域下持續(xù)多個(gè)周期而不是單個(gè)周期,需要對(duì)其進(jìn)行處理,將其轉(zhuǎn)化為快時(shí)鐘域下單個(gè)周期的信號(hào),需要縮短控制信號(hào)。
??處理方法:采用簡單的邏輯波形變換(注意,因?yàn)?code>rd_en_s2f1可能有亞穩(wěn)態(tài)現(xiàn)象,因此只能用同步器輸出的信號(hào)進(jìn)行變換(即rd_en_s2f2
)),具體變換如下圖所示。
??代碼如下
module synchronizer(
input clk_fst,
reset_b,
rd_en,
output rd_en_s2f
);
reg rd_en_s2f1,
rd_en_s2f2,
rd_en_s2f3;
always@(posedge clk_fst or negdege reset_b)begin//多級(jí)觸發(fā)器賦值
if(~reset_b)
{rd_en_s2f3,rd_en_s2f2,ed_en_s2f1}<=3'b000;
else
{rd_en_s2f3,rd_en_s2f2,ed_en_s2f1}<={rd_en_s2f2,rd_en_s2f1,rd_en};
end
always@(rd_en_s2f3,rd_en_s2f2)
case({rd_en_s2f3,rd_en_s2f2})
2'b01://條件句
rd_en_s2f<=1'b1;
default:
rd_en_s2f<=1'b0;
endcase
endmodule
重要提示:學(xué)習(xí)多級(jí)觸發(fā)器的verilog語句,熟練使用{ }語法
小結(jié):慢時(shí)鐘域控制信號(hào)同步到快時(shí)鐘域的本質(zhì)就是一個(gè)邊緣檢測或者信號(hào)計(jì)數(shù)器。
??不管持續(xù)時(shí)間多長的信號(hào)(只要大于一個(gè)快時(shí)鐘周期),都會(huì)生成一個(gè)快時(shí)鐘域內(nèi)持續(xù)一個(gè)周期的信號(hào)。
??通過對(duì)上述代碼中的條件句進(jìn)行控制,還可以實(shí)現(xiàn)下降沿的檢測
??★★★實(shí)例練習(xí):按鍵控?zé)?/strong>
題目描述:如下圖所示,需要實(shí)現(xiàn),每次按一次按鍵,燈的亮滅翻轉(zhuǎn)一次
?????其中,按下按鍵用高電平表示。燈亮用高電平表示。
??
問題轉(zhuǎn)化:翻轉(zhuǎn)與按下的時(shí)間,即高電平的時(shí)間無關(guān),只與按下這個(gè)動(dòng)作有關(guān)系。因此,該問題可以轉(zhuǎn)換為上升沿檢測器
思考與回答:
??Q1:既然是上升沿檢測,能否用@(posedge button)
來直接檢測上升沿?即如下圖所示,將按鈕輸入電平直接接在觸發(fā)器時(shí)鐘端?
??module onoff (button,light); input button; output light; reg light; always@posdege button) begin light <= ~light; end endmodule
??A1:不可以,這樣不規(guī)范,電路中時(shí)鐘端就是接時(shí)鐘的,不能將輸入接到時(shí)鐘端。
??-------------------------------------------------------------------------------------------------------------
??Q2:button
作為外界的異步輸入,與驅(qū)動(dòng)電路的時(shí)鐘并不一致,能直接接入觸發(fā)器輸入嗎?
??A2:不能,異步信號(hào)需要先使用同步器完成時(shí)鐘域同步,來避免亞穩(wěn)態(tài)現(xiàn)象。
??
??
??-------------------------------------------------------------------------------------------------------------
??Q3:同步完之后如何實(shí)現(xiàn)上升沿檢測?
??A3:輸入是慢時(shí)鐘域信號(hào)(因?yàn)槭侨耸?,比電路時(shí)鐘變化更慢),因此就是慢時(shí)鐘域信號(hào)在快時(shí)鐘域下的上升沿檢測問題,可以使用上述波形變換的方法(比較簡單),也可以使用狀態(tài)機(jī)的方法來實(shí)現(xiàn)。
??實(shí)現(xiàn)代碼如下
??波形變換法:button_light.v
module button_light
(
input clk ,
button , //i_pulse
rst_n ,
output reg light //o_pulse
);
reg button_d1 ,
button_d2 ,
button_d3 ; //button_d3用于波形變換
wire pulse ;
/*同步器,用于同步輸入,避免亞穩(wěn)態(tài)*/
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
{button_d3,button_d2,button_d1}<=2'b00;
else
{button_d3,button_d2,button_d1}<={button_d2,button_d1,button};
end
/*波形變換法實(shí)現(xiàn)上升沿檢測*/
assign pulse=(~button_d3)&(button_d2);
//light根據(jù)pulse翻轉(zhuǎn)邏輯
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
light<=0;
else
if(pulse)
light<=~light;
end
endmodule
button_light_tb.v
`timescale 1ns/1ps
`include "button_light.v"
module button_light_tb;
reg rst_n ,
clk ,
button ;
wire light ;
//模塊例化
button_light b1(
clk ,
button ,
rst_n ,
light
);
//激勵(lì)
initial begin
$dumpfile("button_light.vcd");
$dumpvars;
clk<=0;
rst_n<=0;
button<=0;
#15 rst_n<=1;
#20 button<=1;
#160 button<=0;
#125 button<=1;
#130 button<=0;
#55 button<=1;
#60 button<=0;
#1000 $finish;
end
//時(shí)鐘生成
always #10
clk=~clk;
endmodule
??最終結(jié)果如下
??
??狀態(tài)機(jī)法
??先畫狀態(tài)圖,有點(diǎn)丑,大概理解一下(╥﹏╥),懶得重新畫了,反正也不難
button_light2.v
//按鍵控?zé)艟毩?xí),見https://blog.csdn.net/SuperiorEE/article/details/128880282
//波形變換法,使用moore機(jī)
module button_light2#(
parameter S1=3'b001, //one-hot encode state
S2=3'b010,
S3=3'b100
)
(
input clk ,
button ,
rst_n ,
output reg light
);
reg [2:0] current_state ,
next_state ;
reg button_d1 ,
button_d2 ;
/*同步器,用于同步輸入,避免亞穩(wěn)態(tài)*/
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
{button_d2,button_d1}<=2'b00;
else
{button_d2,button_d1}<={button_d1,button};
end
//第一段:現(xiàn)態(tài)次態(tài)切換
always @(posedge clk,negedge rst_n) begin
if(~rst_n)
current_state<=S1; //狀態(tài)復(fù)位
else
current_state<=next_state; //狀態(tài)轉(zhuǎn)換
end
//第二段:次態(tài)組合邏輯
always@(*)begin
case (current_state)
S1:
next_state=(button_d2==0)?S1:S2;
S2:
next_state=(button_d2==0)?S1:S3;
S3:
next_state=(button_d2==0)?S1:S3;
default:
next_state=S1;
endcase
end
//第三段:輸出電路(可以是組合或者時(shí)序,根據(jù)mealy或者moore確定,時(shí)序電路可以避免輸出隨輸入變化的毛刺,但是會(huì)比mealy慢一個(gè)時(shí)鐘)
always@(posedge clk,negedge rst_n)begin
if(~rst_n)
light<=0;
else if(current_state==S2)
light<=~light;
end
endmodule
??testbench
內(nèi)容大致不變,只需要改變include
即可
??對(duì)比發(fā)現(xiàn),兩種方法輸出大致相同,只是實(shí)現(xiàn)手段不同。
實(shí)際上波形變換法快一個(gè)周期,因?yàn)槠涿}沖輸出pause更快,前沿可以與同步器輸出對(duì)齊。而狀態(tài)機(jī)法,由于是moore機(jī),其輸出會(huì)慢一個(gè)時(shí)鐘周期。
??其核心都是慢時(shí)鐘域的控制信號(hào)同步到快時(shí)鐘域下(即電平變脈沖、上升沿檢測)。但是從代碼上來看,波形變換法更加簡潔。
本題總結(jié):核心思想就是把問題轉(zhuǎn)換為上升沿檢測電路,同時(shí)注意對(duì)異步輸入需要加入同步器。
??2.快時(shí)鐘域控制信號(hào)同步到慢時(shí)鐘域。
??問題描述:慢時(shí)鐘域同步快時(shí)鐘域下持續(xù)一個(gè)快周期的信號(hào),在慢時(shí)鐘域中會(huì)不滿一個(gè)慢時(shí)鐘周期,因此在下一個(gè)慢時(shí)鐘到來之前,信號(hào)就已經(jīng)無效。該問題如下圖所示。
??因此需要延長控制信號(hào)。
??處理方法1:加入握手機(jī)制,在快時(shí)鐘域中加入反饋,只有接收方確認(rèn)收到有效控制信號(hào)后,控制信號(hào)再失效。
??代碼如下:
/*快時(shí)鐘域控制信號(hào)同步到慢時(shí)鐘域*/
module f2s_control(
input adat,
rst,
aclk,
bclk,
output reg bdat
);
reg adat1,
bdat1, bdat2, bdat3,
abdat1, abdat2;
//實(shí)例化慢時(shí)鐘域同步器
always @(posedge bclk or negedge rst) begin
if(~rst)
{bdat2,bdat1}<=2'b00;
else
{bdat2,bdat1}<={bdat1,adat1};
end
//實(shí)例化快時(shí)鐘域同步器
always @(posedge aclk or negedge rst) begin
if(~rst)
{abdat2,abdat1}<=2'b00;
else
{abdat2,abdat1}<={abdat1,bdat2};
end
//實(shí)例化快時(shí)鐘域同步慢時(shí)鐘域方法:縮短
always @(posedge bclk or negedge rst) begin
if(~rst)
bdat3<=1'b0;
else
bdat3<=bdat2;
end
always @(bdat3,bdat2) begin
bdat<=({bdat3,bdat2}==2'b01)?1'b1:1'b0;
end
//實(shí)例化慢時(shí)鐘域同步快時(shí)鐘域方法:反饋延時(shí)
always @(posedge aclk or negedge rst) begin
if(~rst)
adat1<=1'b0;
else //adat觸發(fā),abdat2關(guān)閉
if(abdat2)
adat1<=1'b0;
else
if(adat)
adat1<=1'b1;
end
endmodule
??testbench
代碼如下:
/*快時(shí)鐘域控制信號(hào)同步到慢時(shí)鐘域*/
/*測試*/
`timescale 1ns/1ns
`include "f2s_control.v"
module f2s_control_tb;
//************<端口聲明>****************
reg adat,
rst,
aclk,
bclk;
wire bdat;
//***********<模塊例化>******************
f2s_control M1(
adat,
rst,
aclk,
bclk,
bdat
);
initial begin
$dumpfile("f2s_control.vcd");
$dumpvars;
aclk<=0;
bclk<=0;
rst<=0;
adat<=0;
#12 rst<=1;
#20 @(posedge aclk) adat<=1;
@(posedge aclk) adat<=0;
#1000 $finish;
end
always #7
aclk<=~aclk;
always #10
bclk<=~bclk;
endmodule
??測試結(jié)果如下:
??存在的局限:兩次控制信號(hào)之間的間隔不能太小,要讓相關(guān)握手信號(hào)先恢復(fù)。
相關(guān)練習(xí):??途W(wǎng)verilog VL49
??處理方法2【停時(shí)鐘法】:當(dāng)檢測到控制信號(hào)輸入時(shí),通過??鞎r(shí)鐘域的時(shí)鐘來延長快時(shí)鐘域控制信號(hào),當(dāng)收到慢時(shí)鐘域握手反饋后,再恢復(fù)時(shí)鐘。
??代碼如下:
//方法2:停時(shí)鐘法
module f2s_control(
input adat,
rst,
aclk,
bclk,
output reg bdat
);
reg adat1,
bdat1,
bdat2,
bdat3,
abdat1,
abdat2,
stall;
wire aclk_gt;
//雙向同步器
always @(posedge aclk or negedge rst) begin
if(~rst)
{abdat2,abdat1}<=2'b00;
else
{abdat2,abdat1}<={abdat1,bdat2};
end
always @(posedge bclk or negedge rst) begin
if(~rst)
{bdat2,bdat1}<=2'b00;
else
{bdat2,bdat1}<={bdat1,adat1};
end
//慢同步到快波形變化
always @(posedge bclk or negedge rst) begin
if(~rst)
bdat3<=2'b00;
else
bdat3<=bdat2;
end
always @(*) begin
bdat<=({bdat3,bdat2}==2'b01)?1:0;
end
//反饋組合邏輯
assign aclk_gt=aclk & stall;
always@(*)begin
if(abdat2==1)
stall<=1'b1;//重啟時(shí)鐘
else if(adat1==1)
stall<=1'b0;//停時(shí)鐘
else
stall<=1'b1;
end
always @(posedge aclk_gt or negedge rst) begin
if(~rst)
adat1<=1'b0;
else
adat1<=adat;
end
endmodule
??testbench與方法一沒有區(qū)別
??存在的局限:與方法一一樣,兩次控制信號(hào)之間的間隔不能太小,需要快時(shí)鐘重啟讓上一次的控制信號(hào)先恢復(fù)到無效(即低電平)。
★★★★異步數(shù)據(jù)信號(hào)同步【FIFO】
??使用先入先出隊(duì)列,即FIFO.
??它由雙端口存儲(chǔ)器和一組控制邏輯組成
??雙端口存儲(chǔ)器:一個(gè)端口用來寫存儲(chǔ)器,另一個(gè)端口用來讀存儲(chǔ)器,讀和寫可以同時(shí)進(jìn)行,讀寫時(shí)鐘可以完全不同。
??雙端口存儲(chǔ)器的Verilog代碼如下,主要注意學(xué)習(xí)寄存器數(shù)組的使用
`timescale 1ns/1ns
/***************************************RAM*****************************************/
module dual_port_RAM
#( parameter DEPTH = 16,
parameter WIDTH = 8)
(
input wclk //寫時(shí)鐘
,input wenc //寫使能
,input [$clog2(DEPTH)-1:0] waddr //深度對(duì)2取對(duì)數(shù),得到地址的位寬。
,input [WIDTH-1:0] wdata //數(shù)據(jù)寫入
,input rclk //讀時(shí)鐘
,input renc //讀使能
,input [$clog2(DEPTH)-1:0] raddr //深度對(duì)2取對(duì)數(shù),得到地址的位寬。
,output reg [WIDTH-1:0] rdata //數(shù)據(jù)輸出
);
reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1]; //寄存器數(shù)組
//讀寫分離
always @(posedge wclk) begin
if(wenc)
RAM_MEM[waddr] <= wdata;
end
always @(posedge rclk) begin
if(renc)
rdata <= RAM_MEM[raddr];
end
endmodule
??
??FIFO行為簡述與結(jié)構(gòu)推導(dǎo):
??如前所述,由于FIFO內(nèi)部是有雙端口存儲(chǔ)器,因此讀寫操作是相互獨(dú)立的)
寫操作:
待同步的數(shù)據(jù)在寫信號(hào)的控制下,只要不FULL(即寫滿),就會(huì)將數(shù)據(jù)寫入到FIFO中。
每成功寫入一個(gè)數(shù)據(jù),寫指針的地址就會(huì)+1
讀操作:
目標(biāo)時(shí)鐘域只要不EMPTY(即讀空),就執(zhí)行讀操作,讀出之前寫入的數(shù)據(jù)。
每成功讀出一個(gè)數(shù)據(jù),讀指針的地址就會(huì)+1
??其大致行為可以類比數(shù)據(jù)結(jié)構(gòu)中的循環(huán)隊(duì)列
請思考:
兩個(gè)問題:
1.控制邏輯怎么產(chǎn)生產(chǎn)生讀空和寫滿信號(hào)?誰來產(chǎn)生?交換一下會(huì)有問題嗎?
2.如何解決地址同步問題,為什么不能直接使用比較器?
??結(jié)合以上思考,控制邏輯主要是負(fù)責(zé)產(chǎn)生EMPTY(讀空) 和 FULL(寫滿) 信號(hào)。而這兩個(gè)信號(hào)主要是通過 寫指針地址wptr 和 讀指針地址rptr 進(jìn)行相互比較產(chǎn)生的。
??但是,為什么不能直接使用比較器這種組合邏輯電路來比較讀寫地址并生成讀空寫滿信號(hào)呢?
??回答:以EMPTY信號(hào)為例,由于讀指針地址(讀時(shí)鐘域)和寫指針地址(寫時(shí)鐘域)是兩個(gè)不同時(shí)鐘域的異步信號(hào)。
??如果EMPTY是由讀寫地址指針的組合邏輯產(chǎn)生的,那么EMPTY就會(huì)在讀時(shí)鐘和寫時(shí)鐘的邊沿都有可能發(fā)生變化。因此,EMPTY既是讀地址也是寫地址的異步信號(hào)。
??因此在讀時(shí)鐘域或者寫時(shí)鐘域內(nèi),由于EMPTY是異步信號(hào),就可能不滿足建立保持時(shí)間,會(huì)產(chǎn)生亞穩(wěn)態(tài)問題。
??解決策略【異步地址同步】:先將一個(gè)地址同步到另一個(gè)地址的時(shí)鐘域,在兩個(gè)地址都在同一時(shí)鐘域下時(shí),再生成EMPTY或者FULL信號(hào),這樣這些讀空寫滿信號(hào)就成了同步信號(hào)。
??仍然以EMPTY信號(hào)為例,可以將寫地址指針通過同步器(2級(jí)D觸發(fā)器打兩拍)同步到讀時(shí)鐘域,然后在讀時(shí)鐘域內(nèi)根據(jù)同步過來的寫指針地址和原來的讀指針地址生成讀空信號(hào)EMPTY。
??但是,對(duì)于EMPTY信號(hào)的生成,為什么要把寫地址同步到讀地址時(shí)鐘域,能否換成 把讀地址同步到寫地址?
?? 回答:我們從讀空信號(hào)EMPTY的本質(zhì)出發(fā),如果讀寫地址都是實(shí)時(shí)產(chǎn)生的,那么讀地址等于寫地址時(shí)(即讀地址追上了寫地址)就代表讀空了。但是現(xiàn)在有一個(gè)信號(hào)是同步過來的,同步過來的信號(hào)比原時(shí)鐘域的信號(hào)會(huì)有滯后。
??假設(shè)我們把讀地址同步到寫地址時(shí)鐘域,這說明讀地址是滯后的,因此在寫地址時(shí)鐘域下,讀寫地址相同產(chǎn)生EMPTY時(shí),真實(shí)情況是,真實(shí)讀地址超過了同步過來的滯后讀地址,這樣讀地址就有很大的風(fēng)險(xiǎn)超過寫地址,這是不允許的,相當(dāng)于讀指針溢出,會(huì)讀出錯(cuò)誤數(shù)據(jù)。
??而反過來,寫地址同步到讀時(shí)鐘域,即同步寫地址滯后于真實(shí)寫地址。因此當(dāng)讀寫地址相同產(chǎn)生EMPTY時(shí),真實(shí)情況是,真實(shí)讀地址等于滯后的寫地址,而可能落后于真實(shí)的寫地址。這種情況是允許的,因?yàn)椴]有讀空,只是會(huì)短暫停歇一會(huì)(等幾拍)。
?? 【小結(jié)】經(jīng)過上述推理,我們可以簡記為,讀空信號(hào)EMPTY在讀時(shí)鐘域中生成,而寫滿信號(hào)FULL在寫時(shí)鐘域中生成。
??這里我們還忽略了一個(gè)比較重要的問題:讀空寫滿時(shí)都是
讀指針地址 = 寫指針地址 讀指針地址=寫指針地址 讀指針地址=寫指針地址
??應(yīng)該如何區(qū)分這兩種情況呢?
??解決策略【擴(kuò)展地址最高位】:
??以8個(gè)存儲(chǔ)單元的存儲(chǔ)器為例,其物理地址為3bit,從000-111,每個(gè)地址表征一個(gè)物理意義上的存儲(chǔ)單元。
??我們現(xiàn)在擴(kuò)展其最高位地址,將其變?yōu)?bit,即0000-1111,低3位仍然表征物理意義的存儲(chǔ)單元,而擴(kuò)展出來的第四位用來區(qū)分FULL和EMPTY的情況,這一位用來區(qū)分是否差了一輪。
??首先,有一個(gè)固有約束,那就是讀地址不允許在物理意義上超過寫地址,這代表了還沒寫就讀了,這顯然是不允許的。而我們在前述的小結(jié)中所規(guī)定的地址同步方法就自動(dòng)避免了這種情況的出現(xiàn)。
?? 因此,在低3位相同的情況下,如果最高位相同,則代表讀的數(shù)據(jù)和寫的數(shù)據(jù)相同,這是EMPTY讀空的情況,如果最高位不同,則代表讀寫數(shù)據(jù)之間差了一輪,并且由于讀地址限制不能超過寫地址,因此只能是寫地址超過了讀地址一輪,這是FULL寫滿的情況。
??時(shí)刻牢記,讀指針和寫指針都是讀寫完成后再加1,因此所指的都是將要讀/寫的單元。
??【小結(jié)】:
??EMPTY是讀地址 追上 寫地址。即地址低三位相同,最高位也相同。
??FULL是寫地址 套圈追上 讀地址。即地址低三位相同,最高位不同。
??
??前述使用同步器方法來同步地址生成EMPTY和FULL信號(hào),避免亞穩(wěn)態(tài)。在此基礎(chǔ)上,使用格雷碼編碼可以進(jìn)一步減少毛刺和亞穩(wěn)態(tài)現(xiàn)象,因?yàn)楦窭状a每次加一只改變一位。我們僅需在原來地址自增二進(jìn)制計(jì)數(shù)器的基礎(chǔ)上增加一個(gè)二進(jìn)制轉(zhuǎn)格雷碼的組合邏輯電路即可。
??
??格雷碼計(jì)數(shù)器:《SOC設(shè)計(jì)方法與實(shí)現(xiàn) 第三版》136頁給出了設(shè)計(jì)代碼。但是這里與書中代碼不太相同,因?yàn)榘l(fā)現(xiàn)書中代碼有些冗余了。這里的實(shí)現(xiàn)思路是——
先用時(shí)序電路always塊寫出一個(gè)二進(jìn)制計(jì)數(shù)器,然后再用組合邏輯電路實(shí)現(xiàn)二進(jìn)制碼到格雷碼的轉(zhuǎn)換。
??Verilog代碼如下
//2023年2月20日19:14:55
//格雷碼計(jì)數(shù)器,由二進(jìn)制計(jì)時(shí)器和二進(jìn)制轉(zhuǎn)換格雷碼組成
module gray_inc
#(
parameter SIZE=4, //位寬
parameter INC=1 //增量
)
(
input wire clk,
rst_n,
output reg [SIZE-1:0] gray
);
reg [SIZE-1:0] bin;
always @(posedge clk or negedge rst_n) begin//二進(jìn)制計(jì)數(shù)器
if(~rst_n)
bin<=0;
else
bin<=bin+INC;
end
always @(*) begin
gray<=(bin>>1)^bin;
end
endmodule
??
??Testbench如下
`timescale 1ns/100ps
`include "gray_inc.v"
module gray_inc_tb;
parameter SIZE=5,
INC=1;
reg clk,
rst_n;
wire [SIZE-1:0] gray;
//*********<模塊例化>*************
gray_inc
#(
SIZE,
INC
)
g1 //注意g1的位置在參數(shù)后,在port前
(
clk,
rst_n,
gray
);
initial begin
$dumpfile("gray_inc.vcd");
$dumpvars;
rst_n<=0;
clk<=0;
#25 rst_n<=1;
#1000 $finish;
end
always #10
clk<=~clk;
endmodule
??
??波形結(jié)果部分如下圖所示
??
??
重點(diǎn)提示: 二進(jìn)制和格雷碼互轉(zhuǎn)的公式
二進(jìn)制轉(zhuǎn)格雷:
??gray = (bin >> 1) ^ bin
格雷轉(zhuǎn)二進(jìn)制(解碼):
??for(i=0;i<SIZE;i=i+1)
????bin[i]=^(gray>>i)
??【易錯(cuò)點(diǎn)】:引入格雷碼之后,需要注意格雷碼判斷相等或低位相等的準(zhǔn)則與二進(jìn)制的區(qū)別
??舉個(gè)例子:
二進(jìn)制 | 格雷碼 |
---|---|
000 | 000 |
001 | 001 |
010 | 011 |
011 | 010 |
100 | 110 |
101 | 111 |
110 | 101 |
111 | 100 |
??例如判斷寫滿時(shí),二進(jìn)制判斷方法是最高位相反,其余低位相同。而對(duì)于格雷碼不是這樣。格雷碼011和111低位相同,二進(jìn)制分別為010和101,對(duì)應(yīng)的二進(jìn)制低位不同。簡而言之就是格雷碼判斷部分位相同的規(guī)則與二進(jìn)制碼不能對(duì)應(yīng)起來。
【重要提示】格雷碼制下判斷讀空與寫滿
??讀空empty:每一位都完全相同才判斷為讀空
??寫滿full:寫時(shí)鐘域的格雷碼wgray_next 和 被同步到寫時(shí)鐘域的讀指針wr2_rp 高兩位不相同,其余各位完全相同
??
代碼表述如下:assign full = (wr_addr_gray == {~(rd_addr_gray_d2[addr_width-:2]),rd_addr_gray_d2[addr_width-2:0]}) ;//高兩位不同 assign empty = ( rd_addr_gray == wr_addr_gray_d2 );
??這里特別感謝這篇博客給我的啟發(fā)讓我避免了把格雷碼再轉(zhuǎn)化回二進(jìn)制的復(fù)雜操作。
??綜合以上部分,異步FIFO的框圖如下圖所示
??異步FIFO完整代碼如下
//??途W(wǎng)VL45 異步FIFO
//2023年2月27日15:45:43
//本題格雷碼不用打一拍,可以直接由組合邏輯生成,只是因?yàn)榕?途W(wǎng)為了過測試需要打一拍
`timescale 1ns/1ns
//!!!問題:格雷碼低位相同其二進(jìn)制碼低位不一定相同
/*
000 000
001 001
010 011
011 010
100 110
101 111
110 101
111 100
格雷碼011和111低位相同,二進(jìn)制分別為010和101,低位不同
因此需要注意empty和full的判斷條件不同
*/
/***************************************RAM*****************************************/
module dual_port_RAM
#( parameter DEPTH = 16,
parameter WIDTH = 8)
(
input wclk //寫時(shí)鐘
,input wenc //寫使能
,input [$clog2(DEPTH)-1:0] waddr //深度對(duì)2取對(duì)數(shù),得到地址的位寬。
,input [WIDTH-1:0] wdata //數(shù)據(jù)寫入
,input rclk //讀時(shí)鐘
,input renc //讀使能
,input [$clog2(DEPTH)-1:0] raddr //深度對(duì)2取對(duì)數(shù),得到地址的位寬。
,output reg [WIDTH-1:0] rdata //數(shù)據(jù)輸出
);
reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1]; //寄存器數(shù)組
//讀寫分離
always @(posedge wclk) begin
if(wenc)
RAM_MEM[waddr] <= wdata;
end
always @(posedge rclk) begin
if(renc)
rdata <= RAM_MEM[raddr];
end
endmodule
/***************************************AFIFO*****************************************/
module asyn_fifo#(
parameter WIDTH = 8,
parameter DEPTH = 16
)(
input wclk ,
input rclk ,
input wrstn ,
input rrstn ,
input winc ,//寫使能
input rinc ,//讀使能
input [WIDTH-1:0] wdata ,
output wire wfull ,
output wire rempty ,
output wire [WIDTH-1:0] rdata
);
//擴(kuò)展地址
reg [$clog2(DEPTH):0] rptr_expand ,
rptr_expand_g,
rptr_expand_g_d1,
rptr_expand_g_d2,//寫時(shí)鐘域的讀地址
wptr_expand ,
wptr_expand_g,
wptr_expand_g_d1,
wptr_expand_g_d2;//讀時(shí)鐘域的寫地址
wire wenc,
renc;
assign wenc=winc&(~wfull); //寫使能且未寫滿
assign renc=rinc&(~rempty); //讀使能且未讀空
/**************原始指針讀寫變化邏輯***********************/
always@(posedge wclk,negedge wrstn)begin
if(~wrstn)
wptr_expand<=0;
else if(wenc)
wptr_expand<=wptr_expand+1;
else
wptr_expand<=wptr_expand;
end
always@(posedge rclk,negedge rrstn)begin
if(~rrstn)
rptr_expand<=0;
else if(renc)
rptr_expand<=rptr_expand+1;
else
rptr_expand<=rptr_expand;
end
/*********指針地址轉(zhuǎn)換為格雷碼*******************/
//由于定義時(shí)格雷碼是reg類型,因此打一拍更加規(guī)范,當(dāng)然也為了過本題的原因
always@(posedge wclk,negedge wrstn)begin
if(~wrstn)
wptr_expand_g<=0;
else
wptr_expand_g<=(wptr_expand>>1)^(wptr_expand);
end
always@(posedge rclk,negedge rrstn)begin
if(~rrstn)
rptr_expand_g<=0;
else
rptr_expand_g<=(rptr_expand>>1)^(rptr_expand);
end
/******************同步器************************/
always @(posedge wclk,negedge wrstn) begin
if(~wrstn)
{rptr_expand_g_d2,rptr_expand_g_d1}<=2'b0;
else
{rptr_expand_g_d2,rptr_expand_g_d1}<={rptr_expand_g_d1,rptr_expand_g};
end
always @(posedge rclk,negedge rrstn) begin
if(~rrstn)
{wptr_expand_g_d2,wptr_expand_g_d1}<=2'b0;
else
{wptr_expand_g_d2,wptr_expand_g_d1}<={wptr_expand_g_d1,wptr_expand_g};
end
/***************EMPTY&FULL************************/
assign rempty=(rptr_expand_g==wptr_expand_g_d2)?1:0;
assign wfull=(wptr_expand_g=={~(rptr_expand_g_d2[$clog2(DEPTH)-:2]),rptr_expand_g_d2[$clog2(DEPTH)-2:0]})?1:0;
/********************雙口RAM例化****************/
dual_port_RAM
#(
DEPTH,
WIDTH
)
d1
(
wclk //
,wenc //
,wptr_expand[$clog2(DEPTH)-1:0] //
,wdata //
,rclk //
,renc //
,rptr_expand[$clog2(DEPTH)-1:0] //!!!
,rdata //
);
endmodule
??附上??途W(wǎng)通過證明!
??
??FIFO的優(yōu)勢:可以有效地解決兩個(gè)沒有任何關(guān)系的時(shí)鐘源之間數(shù)據(jù)同步的問題
相對(duì)于前述異步控制信號(hào)的同步,F(xiàn)IFO在架構(gòu)上可以實(shí)現(xiàn)一個(gè)數(shù)據(jù)存放在一個(gè)存儲(chǔ)單元,并等待讀取,從架構(gòu)上解決了異步信號(hào)不同時(shí)鐘域下的信號(hào)縮短和延長問題。
一些思考: 理論上來說,F(xiàn)IFO也可以實(shí)現(xiàn)異步控制信號(hào)的同步,只需要一個(gè)存儲(chǔ)單元即可。但是FIFO的引入會(huì)加大芯片的電路面積,所以常用于異步數(shù)據(jù)信號(hào)的同步
??★★★★;異步復(fù)位,同步釋放
??參考這篇
??因?yàn)閺?fù)位信號(hào)也需要與時(shí)鐘滿足類似建立時(shí)間和保持時(shí)間(被稱為恢復(fù)時(shí)間和去除時(shí)間)。
??由于復(fù)位信號(hào)在復(fù)位開始和復(fù)位撤銷時(shí),復(fù)位信號(hào)都會(huì)產(chǎn)生變化,都是時(shí)鐘信號(hào)的異步信號(hào),都會(huì)產(chǎn)生亞穩(wěn)態(tài)現(xiàn)象。但是,由于復(fù)位信號(hào)持續(xù)時(shí)間比較長,因此復(fù)位信號(hào)開始時(shí)如果復(fù)位失敗,可以在多個(gè)時(shí)鐘周期后可以消除。而復(fù)位消除時(shí)間需要進(jìn)行同步,避免亞穩(wěn)態(tài)。這就是異步復(fù)位同步釋放。
??框圖如下所示
??至此,跨時(shí)鐘域的信號(hào)與數(shù)據(jù)同步問題便告一段落了,本章學(xué)習(xí)落下帷幕,熟練掌握異步FIFO,并時(shí)刻在腦海中要有異步信號(hào)先同步來避免亞穩(wěn)態(tài)的思想。這是我個(gè)人的一些總結(jié)。
??有空再去??退⒁幌?strong>跨時(shí)鐘域傳輸的題目,相信會(huì)有鞏固和對(duì)異步更新的理解!文章來源:http://www.zghlxwxcb.cn/news/detail-447858.html
??END文章來源地址http://www.zghlxwxcb.cn/news/detail-447858.html
到了這里,關(guān)于【SOC架構(gòu)】(一)同步與異步信號(hào)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!