基於Active-HDL的HDL設計錄入與模擬
作者:毛蘢瑋 / Saint
掘金:https://juejin.im/user/5aa1f89b6fb9a028bb18966a
微博:https://weibo.com/5458277467/profile?topnav=1&wvr=6&is_all=1
GitHub:github.com/saint-000
CSDN: https://me.csdn.net/qq_40531974
基於Active-HDL的HDL設計錄入與模擬
一、前言:
Active-HDL是圍繞共同核心的HDL模擬器所構建的FPGA開發環境。支援基於文字和圖形設計輸入和除錯工具,允許混合語言模擬(VHDL/ Verilog/ EDIF/ SystemC/ SystemVerilog),並提供統一的介面以及各種合成和實施工具。我們通過對選擇器,加法器,三八譯碼器,優先編碼器,計數器,分頻器上述六種設計錄入和除錯得到輸出波形圖。
二、目的:
通過在Active-HDL上編寫程式碼,熟悉Active-HDL的程式碼錄入方式,溫習並掌握VHDL源程式的語法結構,在通過對不同功能模組的設計過程中,瞭解每種設計它所需要的輸入激勵,輸入波形需要加的恰當適宜,這樣可以方便我們在示波器上觀察輸出波形是否正確。
三、內容:
選擇器,加法器,三八譯碼器,優先編碼器,計數器,分頻器的VHDL程式碼錄入及波形模擬。
四、平臺(裝置、元器件):
①軟體Active-HDL9.2 ②Windows作業系統
五、(1)選擇器
步驟:
1.開啟Active-HDL9.2,頁面會跳出提示框,可以選擇之前建立好的檔案開啟,也可以建立新的設計檔案,這裡我們對選擇器進行建立新的設計,故點選Create new workspace,然後設定Workspace name為 mux4。
2. 建立一個空的設計,新增一些基本資訊,輸入設計檔名,此處輸入study3。
3.在軟體中點選File,選擇New然後點選建立VHDL Source,對設計模型的實體,構造體進行命名,命名為 mux4,此處四選一資料選擇器的構造體填RTL,我們採用暫存器傳輸級描述。
4.在描述埠的時候輸入輸出應用不同的埠方向,比如四選一資料選擇器中,6個埠是輸入,1個埠是輸出,同時我們此處的埠也d0-3可用陣列表示:d[3:0]。
5.完成了埠設定軟體會自動生成好基本的程式碼框架,我們在此基礎上進行程式的編寫。
library IEEE; use IEEE.STD_LOGIC_1164.all; entity mux4 is port( d0 : in STD_LOGIC; d1 : in STD_LOGIC; d2 : in STD_LOGIC; d3 : in STD_LOGIC; s0 : in STD_LOGIC; s1 : in STD_LOGIC; y : out STD_LOGIC ); end mux4; --}} End of automatically maintained section architecture rtl of mux4 is begin process(d0,d1,d2,d3,s0,s1) begin if s1='0' and s0='0'THEN y<=d0; elsif s1='0' and s0='1'THEN y<=d1; elsif s1='1' and s0='0'THEN y<=d2; elsif s1='1' and s0='1'THEN y<=d3; else y<='Z'; end if; end process; end rtl;
6.把完成的程式碼進行編譯,編譯好的檔案出現子項,其對應於檔案中的實體和構造體,我們將出現的子項設為頂層,其目的是優先進行模擬,有時候多專案在同一個工作環境中,我們需選定優先模擬物件,然後進行模擬。
7.將待測訊號載入到波形視窗,然後對各項輸入訊號設定波形訊號,加模擬激勵的方式只要有三種:圖形化介面手動加激勵;編寫巨集檔案加激勵;編寫測平臺加激勵。在本次實驗中主要是用圖形化介面手動加激勵的方式,在設定波形視窗發現,一共有7種圖形化加激勵方式,常用的是時鐘型別,公式型別,數值型別。
8.對於四選一資料選擇器,我們將d0的激勵設恆為1,將d1的激勵設恆為0,d2和d3的波形任意給出,設定s0和s1的波形使形成的波形可以實現00-11,具體波形及結果如下:
9.在狀態列點選模擬按鍵對待測訊號進行模擬測試。
六、(1)選擇器:四選一資料選擇器
資料及結果分析:
對於四選一資料選擇器而言,我們的目標在於改變s0,s1的值可以有四種不同的變化即對應四路資料,我們在波形圖上可以發現s1,s0為00,01,10,11分別輸出的是不同的波形,我們將每一段輸出y的值與d0-3相對比可驗證實現了四路數選功能。
六、(2)加法器
實驗步驟:
1.開啟Active-HDL9.2,頁面會跳出提示框,可以選擇之前建立好的檔案開啟,也可以建立新的設計檔案,這裡我們對選擇器進行建立新的設計,故點選Create new workspace,然後設定Workspace name為half_adder。(加法器我們這裡以半加器為例)
2.建立一個空的設計,新增一些基本資訊,輸入設計檔名。
3.在描述埠處設定a,b為輸入,co,s為輸出。
4.在軟體中點選File,選擇New然後點選建立VHDL Source,對設計模型的實體,構造體進行命名,命名為half_adder,此處半加器的構造體填RTL,我們採用暫存器傳輸級描述。
5.完成了埠設定軟體會自動生成好基本的程式碼框架,我們在此基礎上進行程式的編寫。
程式碼如下:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity half_adder is
port(
a : in STD_LOGIC;
b : in STD_LOGIC;
s : out STD_LOGIC;
co : out STD_LOGIC
);
end half_adder;
architecture rtl of half_adder is
SIGNAL c,d:STD_LOGIC;
begin
c<=a or b;
d<=a nand b;
co<=not d;
s<=c and d;
end rtl;
6.把完成的程式碼進行編譯,然後進行模擬,模擬前加入如下激勵方便觀察波形。
七、(2)加法器:半加器
資料及結果分析:
對於半加器而言,我們的目標在於將ab相加,用s來表示兩數之和,用co來表示二進位制的加法是否進位,若進位則co為1,無進位co為0;我們將每一段輸出的s,co的值與a+b相對比可驗證實現了半加器加法功能。
六、(3)三八譯碼器
步驟:
1.前期的建立新的工作區不在重述,我們設定Workspace name為decoder。(三八譯碼器器為例)
2.建立一個空的設計,新增一些基本資訊,輸入設計檔名,在描述埠處設定a,b,c,g1,g2a,g2b為輸入,y0-y7為輸出。(可以設定陣列形式表示輸出)
3.在軟體中點選File,選擇New然後點選建立VHDL Source,對設計模型的實體,構造體進行命名,命名為decoder_3_to_8,此處三八譯碼器的構造體填RTL,我們採用暫存器傳輸級描述。
4.完成了埠設定軟體會自動生成好基本的程式碼框架,我們在此基礎上進行程式的編寫。
程式碼如下:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity decoder_3_to_8 is
port(
a : in STD_LOGIC;
b : in STD_LOGIC;
c : in STD_LOGIC;
g1 : in STD_LOGIC;
g2a : in STD_LOGIC;
g2b : in STD_LOGIC;
Y : out STD_LOGIC_VECTOR(7 downto 0)
);
end decoder_3_to_8;
architecture rtl of decoder_3_to_8 is
signal indata: STD_LOGIC_VECTOR(2 downto 0);
begin
indata<=c&b&a;
process(indata,g1,g2a,g2b)
begin
IF(g1='1'AND g2a='0' AND g2b='0') THEN
case indata is
when "000"=>y<="11111110" ;
when "001"=>y<="11111101" ;
when "010"=>y<="11111011" ;
when "011"=>y<="11110111" ;
when "100"=>y<="11101111" ;
when "101"=>y<="11011111" ;
when "110"=>y<="10111111" ;
when "111"=>y<="01111111" ;
when others=>y<="11111111" ;
end case ;
else
y<="11111111";
end if;
end process;
end rtl;
5.把完成的程式碼進行編譯,然後進行模擬。
七、(3)三八譯碼器
資料及結果分析:
對於三八譯碼器而言,我們要實現將3位2進位制數通過電路轉換成八路不同狀態的輸出,我們在波形圖上可以將數值與下表資料相對比可驗證實現了三八譯碼器譯碼輸出功能。
此處僅列出前幾個輸入對應的輸出波形,其餘的情況我們可以根據規律同理得出。
六、(4)優先編碼器
步驟:
1.建立新的工作區,我們設定Workspace name為 encoder_priority。(優先編碼器為例)
2.建立一個空的設計,新增一些基本資訊,輸入設計檔名,在描述埠處設定input0-7為輸入,y0-y2為輸出。(可以設定陣列形式表示輸入和輸出)
3.在軟體中點選File,選擇New然後點選建立VHDL Source,對設計模型的實體,構造體進行命名,命名為 encoder_priority,此處優先編碼器的構造體填RTL,我們採用暫存器傳輸級描述。
4.完成了埠設定軟體會自動生成好基本的程式碼框架,我們在此基礎上進行程式的編寫。
程式碼如下:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity encoder_priority is
port(
input : in STD_LOGIC_VECTOR(7 downto 0);
y : out STD_LOGIC_VECTOR(2 downto 0)
);
end encoder_priority;
architecture rtl of encoder_priority is
begin
process(input)
begin
if(input(0)='0')THEN
y<="111";
ELSIF(input(1)='0')THEN
y<="110";
ELSIF(input(2)='0')THEN
y<="101";
ELSIF(input(3)='0')THEN
y<="100";
ELSIF(input(4)='0')THEN
y<="011";
ELSIF(input(5)='0')THEN
y<="010";
ELSIF(input(6)='0')THEN
y<="001";
else
y<="000";
end if;
END PROCESS;
end rtl;
5.把完成的程式碼進行編譯,然後進行模擬。
七、(4)優先編碼器
資料及結果分析:
對於優先編碼器而言,我們要實現同時在幾個輸入端有輸入訊號,編碼器按輸入訊號排定的優先順序,只對同時輸入的幾個訊號中優先權最高的一個進行編碼,我們在波形圖上可以將數值與下表資料相對比可驗證實現了三八譯碼器譯碼輸出功能。
此處僅列出前幾個輸入對應的輸出波形,其餘的情況我們可以根據規律同理得出。我們容易發現input(7)為任意波形對輸出結果不影響。
六、(5)計數器
步驟:
1.建立新的工作區,我們設定Workspace name為count10en。(四位二進位制加‘1’同步計數器為例)
2.建立一個空的設計,新增一些基本資訊,輸入設計檔名,在描述埠處設定clr,clk,en為輸入,q3-q1,full為輸出。(可以設定陣列形式表示輸出)
3.在軟體中點選File,選擇New然後點選建立VHDL Source,對設計模型的實體,構造體進行命名,命名為 count10en,此處優先編碼器的構造體填RTL,我們採用暫存器傳輸級描述。
4.完成了埠設定軟體會自動生成好基本的程式碼框架,我們在此基礎上進行程式的編寫。
程式碼如下:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY count10en IS
PORT(
clk : IN STD_LOGIC;
clr : IN STD_LOGIC;
en : IN STD_LOGIC;
full : OUT STD_LOGIC;
q : OUT STD_LOGIC_VECTOR(3 downto 0)
);
END count10en ;
ARCHITECTURE rtl OF count10en IS
SIGNAL count_4: STD_LOGIC_VECTOR(3 downto 0) ;
BEGIN
PROCESS(clk,clr)
BEGIN
IF(clr='1')THEN
count_4<="0000";
full<='0';
ELSIF(clk'EVENT AND clk='1')THEN
IF(en='1')THEN
IF(count_4="1001")THEN
count_4<="0000";
full<='1';
ELSE
count_4<=count_4+'1';
full<='0';
END IF;
END IF;
END IF;
END PROCESS;
q<=count_4 ;
END rtl ;
5.把完成的程式碼進行編譯,然後進行模擬。
七、(5)計數器
資料及結果分析:
對於計數器而言,我們要實現在指定範圍內等間隔計數,full是進位我們在波形圖上可以直觀的驗證計數器從0計到8.
注意調節clk激勵訊號和總截止時間來更好的驗證輸出波形。
六、(6)分頻器
步驟:
1.建立新的工作區,我們設定Workspace name為clk_div10 。(十分頻器為例)
2.建立一個空的設計,新增一些基本資訊,輸入設計檔名,在描述埠處設定clk為輸入,clk_div10為輸出。
3.在軟體中點選File,選擇New然後點選建立VHDL Source,對設計模型的實體,構造體進行命名,命名為clk_div10,此處優先編碼器的構造體填RTL,我們採用暫存器傳輸級描述。
4.完成了埠設定軟體會自動生成好基本的程式碼框架,我們在此基礎上進行程式的編寫。
程式碼如下:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_LOGIC_UNSIGNED.all;
entity clk_div10 is
port(
clk : in STD_LOGIC;
clk_div10 : out STD_LOGIC
);
end clk_div10;
--}} End of automatically maintained section
architecture rtl of clk_div10 is
SIGNAL counter:STD_LOGIC_VECTOR(2 DOWNTO 0);
SIGNAL clk_temp:STD_LOGIC;
begin
PROCESS(clk)
begin
if(clk'EVENT AND clk='1')THEN
IF(counter="100")THEN
counter<="000";
clk_temp<=NOT clk_temp;
ELSE
counter<=counter+1;
END IF;
END IF;
END PROCESS;
clk_div10<=clk_temp;
end architecture rtl;
5.把完成的程式碼進行編譯,然後進行模擬。
七、(6)分頻器
資料及結果分析:
對於分頻器而言,我們驗證分頻後訊號輸出一個週期時鐘經歷10個週期即達到十分頻器的功能。
八、結論:
通過VHDL來描述硬體功能,我們需要在特定的框架下程式設計,對於不同功能的硬體,我們編寫的方式也會有不同,編譯成功後用示波器並加入激勵訊號進行軟體虛擬波形模擬來驗證模組的功能。
九、總結及心得體會:
(1)我們可以通過對一個模型的功能列出真值表,知道所需要的埠,主要是給出輸出和輸入,然後編寫實體和結構體程式,在軟體中編寫程式相比其他程式設計環境更為人性化的是active具有程式語言助手功能,可以根據使用者的自定義生成實體和構造體框架,大大減少人工錄入程式碼的工作量。
(2)實驗中我們主要定義的埠只是in和out,但事實上VHDL共定義了5種類型的埠,分別是In, Out,Inout, Buffer;
與Out埠比,Buffer埠具有回讀功能,也即內部反饋,但在設計時最好不要使用buffer,因為buffer型別的埠不能連線到其他型別的埠上,無法把包含該型別埠的設計作為子模組元件例化,不利於大型設計和程式的可讀性。若設計時需要實現某個輸出的回讀功能,可以通過增加中間訊號作為緩衝,由該訊號完成回讀功能。
雙向埠Inout比較特殊,即一個埠可以做輸出,也可以做輸入,在某些程式中同一個埠在不同的情況下作用會有不同,此時就會用到這種特定的埠。
(3)編寫程式碼時,我們主要注意的是構造體核心部分程式碼,VHDL的編碼支援大小寫字母,然後佈局軟體有個縮排佈局按鍵可以將程式碼以縮排式對齊,在佈局的同時,對使用者來說也有檢查程式碼的好處,另外就是PROCESS,我們在編寫程式碼時常用到的就是程序,我們知道程序內部是順序執行的,程序之間是並行執行的;
VHDL中的所有並行語句都可以理解為特殊的程序,只是不以Process結構出現,其輸入訊號和判斷訊號就是隱含的敏感表。
(4)在模擬時,我們要注意加入激勵的方式,加入合適的激勵訊號可以使我們更好的驗證輸出波形,如果是自定義波形的話,公式方式新增激勵會方便一點,但是要注意的是,這隻種方法加入的波形只能限定在週期內的兩個變化值的輸入,所以有一點侷限,不過好在我們本次做的實驗需要的激勵都可以通過時鐘方式,公式方式加激勵模擬。
同時我們注意除錯波形時的波形重新整理和顯示,我們更新波形時常用的兩個鍵如下所示:
十二、改進建議:
(1)這裡採用的主要的設計錄入方式是HDL編輯器錄入方式,我們還可以用框圖錄入方式,狀態圖錄入方式。如果需要程式設計的硬體內部比較複雜的話,用圖形輸入方式,需要比較繁瑣的轉化,轉化成基本原件的連線很耗時,若用文字輸入的話,思路比較清晰,分析起來很簡單。一般功能描述用文字輸入法,邏輯描述用圖形檔案比較好,所以我覺得可以將本次實驗中的分頻器,優先編碼器用框圖錄入方式去實現。
(2)分頻器是十分頻,我們的設計只能是針對十分頻,這樣就非常侷限,因此我們可以編寫一個數控N分頻器,實際應用中更加的方便。
程式碼如下:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_LOGIC_UNSIGNED.all;
entity ndiv is
port(
clk : in STD_LOGIC;
clr : in STD_LOGIC;
clk_div : out STD_LOGIC
);
end ndiv;
architecture rtl of ndiv is
SIGNAL counter:STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL clk_temp:STD_LOGIC;
begin
process(clk,clr)
begin
if(clr='1')then
counter<="00000001";
clk_temp<='1';
elsif(clk'EVENT AND clk='1')THEN
IF(counter="1010")THEN
counter<="00000001";
clk_temp<='1';
ELSE
counter<=counter+1;
clk_temp<='0';
END IF;
END IF;
END PROCESS;
clk_div<=clk_temp;
end rtl;
我們可以在程式碼:IF(counter="1010")THEN
中修改counter的數值,實現不同頻次的分頻器,例如:IF(counter="100")THEN
中實現四分頻器功能。
特別要注意的是:在實驗中錯誤理解分頻器的功能,一開始我以為分頻器實現時鐘訊號每翻轉十次,分頻電路翻轉一次,但實際上這種說法是錯誤的,對應的情況是特殊的輸出波形,下圖這種情況便屬於這種情況,但實際上我們一眼可以看出來這個模擬是有問題的,所以針對分頻器的功能,我們的正確理解是:N分頻就是通過有分頻作用的電路結構,在時鐘每觸發N個週期時,電路輸出1個週期訊號。