寫在前面
學過一門或多門軟件語言的數字設計初學者經常會犯一些錯誤 ,例如硬件語言的併發性,可綜合以及不可綜合語句區分,循環語句的使用等等。本文的建議將帶你區別並掃除這些易錯點,助你成爲一名優秀的硬件設計師。
正文
可綜合以及不可綜合的代碼
瞭解如何編寫可在FPGA或ASIC上運行的代碼?
當您編寫Verilog或VHDL代碼時,您正在編寫將被轉換爲門,寄存器,RAM等的代碼。執行此任務的程序稱爲綜合工具。綜合工具的工作是將您的Verilog或VHDL代碼轉換爲FPGA可以理解的代碼。但是,Verilog和VHDL的某些部分FPGA根本無法實現。當您這樣編寫代碼時,它稱爲不可綜合的代碼。
那麼,爲什麼您要使用一種語言,該語言包含無法綜合的代碼?原因是它使您的測試平臺功能更強大。當您編寫用於仿真的測試平臺時,通常使用不可合成的代碼結構會使您的測試平臺更好,並使您更輕鬆地完成工作。
延遲聲明
最基本的不可合成代碼是延遲語句。 FPGA沒有時間概念,因此不可能告訴FPGA等待10納秒。 相反,您需要使用時鐘和觸發器來實現您的目標。 下面是一個不可合成代碼的示例,該代碼已被轉換爲可以由工具合成的代碼。
-- Non-Synthesizable Delay Statement:
r_Enable <= '0';
wait for 100 ns;
r_Enable <= '1';
-- Converted to Synthesizable code (assuming 100 MHz clock):
always@(posedge clk) begin
if( index < 10 ) begin
index <= index + 1;
r_Enable <= 0;
end
else begin
r_Enable <= 1;
index <= 0;
end
end
循環語句
數字設計初學者經常濫用的另一段代碼是循環語句,例如while,for,repeat等。可綜合代碼中的循環實際上無法像在C等軟件語言中那樣使用。 硬件開發初學者面臨的巨大問題是, 他們已經在C語言中看到了數百次循環,因此他們認爲在Verilog和VHDL中它們是相同的。 在這裏讓我清楚:循環在硬件中的行爲與在軟件中的行爲不同。 在您瞭解循環語句如何工作之前,您不應該使用它們。
知道綜合和不可綜合代碼之間的區別對於成爲一名優秀的數字設計師非常重要。 僅在編寫將在FPGA上運行的代碼時使用可綜合的構造!
每個軟件程序員需要了解的有關硬件設計的內容
對於數字設計新手而言最重要的部分
嘗試使用VHDL或Verilog進行編程的每個瞭解C或Java語言的軟件開發人員都會遇到相同的問題。他們對代碼的工作原理進行了假設。這些假設通常適用於所有軟件語言。不幸的是,這些假設不適用於硬件描述語言。如果您不熟悉硬件開發,但懂一種或兩種軟件語言,請先閱讀本文提供了代碼示例,並解釋了代碼在軟件世界和硬件世界中如何工作,以向您展示它們之間的區別。
假設1:串行與並行邏輯
這可能是硬件和軟件編程語言之間最根本的區別。軟件設計師僅見過串行代碼,但他們可能沒有意識到這一事實。串行代碼的意思是代碼行一次執行一行。例如,第2行只能在第1行完成後才能執行。VHDL和Verilog不會這樣!它們被稱爲並行邏輯語言,所有代碼行都可以並且將同時執行。這稱爲併發。這是演示串行和並行邏輯之間區別的示例。假設一位設計師希望每十個時鐘點亮一次LED。
示例軟件代碼:
while (1)
{
if (count == 9)
{
LED_on = 1;
count = 0;
}
else
{
LED_on = 0;
count = count + 1;
}
}
與其等價的Verilog代碼爲:
always@(posedge clk ) begin
if(count < 9) begin
count <= count + 1;
end
else begin
count <= 0;
end
end
assign LED_on = (count == 9) ? 1 : 0;
VHDL代碼爲:
P_INCREMENT : process (clock)
begin
if rising_edge(clock) then
if (count < 9) then
count <= count + 1;
else
count <= 0;
end if;
end if;
end process P_INCREMENT;
LED_on <= ‘1’ when count = 9 else ‘0’;
這裏要意識到的重要一點是,在軟件代碼中,每行都將執行,然後允許下一行執行。在VHDL和Verilog中並非如此,這在分配LED_on信號的最後一行中得到了證明。該行與VHDL進程同時運行。它始終爲LED_on分配“ 1”或“ 0”。如果這是軟件,則只有在執行了前面的代碼行後才能到達此行。優秀的數字設計師需要始終記住VHDL和Verilog是並行語言。
假設2:循環
這是新硬件開發人員面臨的一個巨大問題。他們已經在C語言中看到了數百次循環,因此他們認爲在Verilog和VHDL中它們是相同的。在這裏讓我清楚:for循環在硬件和軟件中的行爲不同。在您瞭解for循環如何工作之前,您不應該使用它們。
示例軟件代碼:
for (int i=0; i<10; i++)
data[i] = data[i] + 1;
此代碼將獲取數組“ data”中的每個值並將其遞增1。
等效爲Verilog代碼爲:
//reg [3 : 0] index = 0;
always@(posedge clk) begin
if(index < 10) begin
data[index] <= data[index] + 1;
index <= index + 1;
end
end
這是VHDL中的等效代碼:
P_INCREMENT : process (clock)
begin
if rising_edge(clock) then
if (index < 10) then
data[index] <= data[index] + 1;
index <= index + 1;
end if;
end if;
end process P_INCREMENT;
開始在這裏看到一個模式?用C編寫的代碼幾乎可以減少與VHDL或Verilog中的代碼類似的功能。我要大膽地說一下:如果您至少沒有做過3種FPGA設計,則永遠不要使用for循環。因此,請考慮如何重寫軟件中編寫的代碼,以使其永遠不使用for循環。通常,您所需要做的就是添加一個計數器信號(例如上例中的index),以完成與for循環相同的操作。
當然,如果你不是一個數字設計新手,你可以使用可綜合的for循環:
always@(posedge clk) begin : for_
for(integer index = 0; index < 10; index = index + 1) begin
data[index] <= data[index] + 1;
end
end
假設3:立即執行代碼
這與上面的串行與並行邏輯討論有關,但這是軟件開發人員經常犯的一個常見錯誤。軟件開發人員需要記住,不會立即執行一行代碼,並且會更新信號值以供使用。下面的示例詳細說明了狀態機的這一假設。下面的示例顯示了某些外圍組件(例如模數轉換器)的初始化例程。FPGA需要向ADC寫入數據:數據線上的5、6、1、0。軟件中描述的狀態機將允許數據值隨狀態變化而變化。不幸的是,在下面的硬件代碼示例中,情況並非如此:
示例軟件代碼:
state = INITIALIZE;
data = 5;
state = LOAD_1;
data = 6;
state = LOAD_2;
data = 1;
state = DONE;
data = 0;
新手容易等價爲(錯的):
P_STATE_MACHINE : process (clock)
begin
if rising_edge(clock) then
state <= INITIALIZE;
data <= 5;
state <= LOAD_1;
data <= 6;
state <= LOAD_2;
data <= 1;
state <= DONE;
data <= 0;
end if;
end process P_STATE_MACHINE;
或(錯的):
always@(posedge clk) begin
state <= INITIALIZE;
data <= 5;
state <= LOAD_1;
data <= 6;
state <= LOAD_2;
data <= 1;
state <= DONE;
data <= 0;
end
此代碼的硬件表示形式非常糟糕!(多驅動)由於進程中的每一行同時執行,因此數據將始終停留在0且狀態將始終停留在DONE(仿真)。此代碼永遠不會像在軟件代碼中那樣執行狀態機。下面的代碼是用VHDL編寫此狀態機的正確方法:
等效的硬件代碼應爲:
P_STATE_MACHINE : process (clock)
begin
if rising_edge(clock) then
if (state == INITIALIZE) then
data <= 5;
state <= LOAD_1;
elsif (state == LOAD_1) then
data <= 6;
state <= LOAD_2;
elsif (state == LOAD_2) then
data <= 1;
state <= DONE;
elsif (state == DONE) then
data = 0;
else
state <= INITIALIZE;
end if;
end if;
end process P_STATE_MACHINE;
或:
always@(posedge clk) begin
case(state)
INITIALIZE: begin
state <= LOAD_1;
data <= 5;
end
LOAD_1: begin
state <= LOAD_2;
data <= 6;
end
LOAD_2: begin
state <= DONE;
data <= 1;
end
DONE: begin
data <= 0;
end
defeault: begin
state <= INITIALIZE;
end
endcase
end
換成if else結構也行,對功能不影響。
上面的三個例子是在VHDL或Verilog中開始新設計時,新軟件開發人員經常會遇到困難的三個地方。上面的三個假設應該始終是新的數字設計師想到的。應該始終考慮有關併發性,for循環和代碼執行的問題。一旦完全理解了上面的示例,你將很快成爲成功的數字設計師!
參考資料
交個朋友
-
個人微信公衆號:FPGA LAB,左下角二維碼;
-
知乎:李銳博恩,右下角二維碼。