VHDL 硬體描述語言 數位電路設計實務 第四章 VHDL 的語言結構
4-1 VHDL 語言的結構圖
每個 entity 或多或少都會用到一個 package,例如:在 library ieee 內的 package、自已寫的 package … 等等。 引用這個 entity 一定要給輸出入 port 的部份,generic 敘述是用來產生易讀、易於維護、可以配置(configuration)、易於調整的設計方式 (Scalable Design) 程式碼寫法,讓您設計/引用這個 entity 更有彈性 (細節請看 generic 敘述) 。 每個 entity 一定會有一個 architecture 裡面寫著 concurrent 敘述以及 process 敘述,在 process 敘述頭則是可以寫的 Sequential 敘述。
4-1.1 package 的構成 library ieee; use ieee.std_logic_1164.all; library library名稱; use work.package名稱.all; package package名稱is constant 常數宣告 attribute 屬性宣告 type 型態宣告 sub-type 子型態宣告 component 元件宣告 function 函數宣告 -- 只有 function 函數的輸出入定義沒有內容 procedure 程序宣告 -- 只有 procedure 程序的輸出入定義沒有內容 end package名稱;
package body package名稱is constant 常數宣告 attribute 屬性宣告 type 型態宣告 sub-type 子型態宣告 component 元件宣告 function 函數內容 -- 有 function 函數的輸出入定義和內容 procedure 程序內容 -- 有 procedure 程序的輸出入定義和內容 end package名稱;
4-1.2 *.vhd 程式檔案內的構成 library ieee; use ieee.std_logic_1164.all; use work.package名稱.all; entity 單體名稱 is generic 宣告; port ( 埠列與埠的宣告 (input, output, inout, buffer等) ); end單體名稱;
architecture arc of architecture名稱 is “architecture 宣告部份”(architecture declarative part) begin --“敘述”(statement) 部份 u 同時性的訊號指定 (concurrent signal assignment) signal-name <= expression; u 有條件的訊號指定 (conditional signal assignment) signal-name <= expression-1 when boolean-expression-1 else expression-2 when boolean-expression-2 else ... expression-M when boolean-expression-M else expression-N;
u 選擇性的訊號指定 (selected signal assignment) with expression select signal-name <= signal-value-1 when choices-1, signal-value-2 when choices-2, ... signal-value-M when choices-M, signal-value-N when others; u 或者是 引用已經設計好的元件 / 單體 (entity)
-- process 是可有可無的,也可以有很多個 process process ( sensitivity-lisit ) “process 宣告部份”(process declarative part) begin 訊號指定敘述 ( <= ) 或者是 變數指定敘述 ( := ) 或者是 判斷敘述 (if、then、else、elsif、end if … 等等) 或者是 條件選擇敘述 (case、when … 等等) 或者是 迴圈敘述 (for、while … 等等) 或者是 引用已經設計好的函數 (function) / 程序 (procedure) end process;
u 同時性的訊號指定 (concurrent signal assignment) u 有條件的訊號指定 (conditional signal assignment) u 選擇性的訊號指定 (selected signal assignment) u 或者是 引用已經設計好的元件 / 單體 (entity) end arc;
4-2結構化描述 (structural description) 與 行為化描述 (behavioral description) 資料流 (Data Flow) 模式 它是用一種 “同時性”的訊號指定敘述(concurrent signal assignment statement) 來描述元件 (component) 的行為。 行為 (Behavioral) 模式 使用類似高階語言的寫法,像是:變數 (variable)、迴圈 (loop) 以及 if … then … else、when、case … 等等來描述複雜電元件動作行為。 行為式 (Behavioral) 的寫法可以將將演算法給實踐成電路,不過合成出來的電路的面積可能會較大,比較合用來作為電路模擬的一種寫法。
4-3 VHDL 的語法 (syntax) 4-3.1 entity v.s. architecture entity 只是用來定義電路的『輸出』和『輸入』訊號,architecture 則是詳細地描述了電路的功能。 一個 VHDL 檔案中只能有一個 entity 以及一個 architecture。 例如:
4-3.2 symbol v.s entity 傳統的設計方式是電路設計人員在 CAD工作站以基本的電路元件來組織並構成一個完整的邏輯功能,這些最基本的電路元件像是:not 邏輯閘、and 邏輯閘、or 邏輯閘或者是暫存器 (register),在 CAD工作站的螢幕中是以線條和圖案,每個元件清楚地定義了『輸出』和『輸入』訊號,這種電路元件的表示方式稱之為『Symbol』。 在VHDL的語言中,沒有所謂的『Symbol』、取而代之的是 Entity (單體)。不過 VHDL 的 Entity 等於傳統的設計方式的 Symbol。
entity (單體) 是組成一個電路的最小單位,它描述了該:電路的名稱、所需的輸出入埠的名稱、輸出入埠的個數、輸出入埠的大小 … 等等。
4-3.3 entity 的語法 entity entity-name is generic ( generic 宣告 ); -- 可以不寫 port ( signal-name-1 : mode signal-type; signal-name-2 : mode signal-type; ... signal-name-N : mode signal-type ); ”宣告部份“ -- 可以不寫 end entity-name;
其中: u generic 宣告 generic ( generic 宣告 ); 這一部份是可以不寫的。 細節請參考“4-3.4 entity 宣告加上 generic 子句”。 u entity-name 用來描述這個 entity 的名稱。entity 的名稱上下要一樣,例如: entity ECC_Check is end ECC_Check; u signal-names-1 …signal-names-N 用來描述『輸出』和『輸入』訊號的名稱,二個訊號之間要用分號 (;) 區隔開來。
u mode 訊號的模式,可以是下列四種之一: l in – 『輸入』訊號 表示該接腳要從外界接收訊號。 l out – 『輸出』訊號 表示該接腳要將處理完後的訊號值送到外界去。
l inout – 可以當作『輸入』訊號以及『輸出』訊號 表示該接腳既可以從外界接收訊號,也可以將處理完後的訊號值送到外界去。 l buffer – 『輸出』訊號 表示該接腳除了要將處理完後的訊號值送到外界去, 更可以讓在 architecture 區塊內的訊號 或者是 process 區塊內的變數可以去取用它。
u signal-type 訊號的型態,用來描述訊號的型態。 例如:std_logic、std_logic_vector( 3 downto 0)、bit_vector( 3 downto 0) … 等等。 請參考“4-3.3 entity 宣告加上generic子句”。
l subprogram declaration, 程序宣告 l subprogram body, 程序內容 ”宣告部份“ 是可以不寫的,但內容可以包括: l subprogram declaration, 程序宣告 l subprogram body, 程序內容 l type declaration, 型態宣告 l subtype declaration, 子型態宣告 l constant declaration, 常數宣告 l signal declaration, 訊號宣告 l variable declaration, 變數宣告 l file declaration, 檔案宣告 l alias declaration, 別名宣告 l attribute declaration, 屬性宣告 l attribute specification, 屬性規格 l disconnection specification, 中止/不使用規格 l use clause, use 子句 l group template declaration, 群組樣版宣告 l group declaration, 群組宣告
一個位元全加器 (Full_Adder) 電路的方塊圖
4-3.4 entity 宣告加上generic子句 architecture 敘述用來定義:元件的“功能”(function of component) ,它包含“宣告”與“敘述”二大部份: generic 子句提供 VHDL 語言中可以訂定元件參數模型的能力,更是用於產生易讀、易於維護、可以配置(configuration)、易於調整的設計方式 (Scalable Design) 程式碼寫法的利器。 generic 語法,如下: generic (generic_name : type [:= default_value]); 其中: u generic_name 是 generic 的名稱。 u type 是 generic 的型態。 例如:std_logic、std_logic_vector( 3 downto 0)、bit_vector( 3 downto 0) … 等等。請參考“VHDL 內定的資料型態”。 default_value 是“預設值”,也就是當引用這個 entity 卻沒有給 generic_name 之後的值,那麼就會用“預設值”來用。
generic 子句的例子 entity Generic_Test is generic ( Adder_Bits : integer := 10; -- 預設:整數 File_Name : string := string'("Simulation.out"); -- 預設:字串 Data_Signed : std_logic := '0' -- 預設:標輯邏輯值 ) port( A : in std_logic_vector( Adder_Bits - 1 downto 0); B : in std_logic_vector( Adder_Bits - 1 downto 0); Sum : out std_logic_vector( Adder_Bits downto 0) ); end Generic_Test;
當在引用該 entity 時可以 在 generic map的地方把“預設值”給取代 u 把三個“預設值”都取代 Generic_Test_Inst: Generic_Test generic map ( Adder_Bits => 12 File_Name => string'("NewSim.out"), Data_Signed => '1' ) port map ( A => A, B => B, Sum => Sum );
u 只取代其中一個“預設值” Generic_Test_Inst: Generic_Test generic map ( Adder_Bits => 11 ) port map ( A => A, B => B, Sum => Sum );
u 完全使用“預設值” Generic_Test_Inst: Generic_Test port map ( A => A, B => B, Sum => Sum );
使用generic設計『易於調整Adder位元數』的VHDL 程式碼 注意在 Generic_Adder.vhd 檔案中,entity 的名稱一定要和 architecture 的名稱相同。 entity Generic_Adder is … end Generic_Adder; architecture arc of Generic_Adder is begin end arc;
Generic_Adder.vhd library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; use ieee.std_logic_unsigned.all; use work.pkg_Generic_Adder.all; entity Generic_Adder is generic( Adder_Bits : integer := 10 -- 預設為10個Bits的Adder ); port( A : in std_logic_vector( Adder_Bits - 1 downto 0); B : in std_logic_vector( Adder_Bits - 1 downto 0); Sum : out std_logic_vector( Adder_Bits downto 0) end Generic_Adder;
architecture arc of Generic_Adder is begin Do_Generic_Adder: process ( A, B ) Sum <= signed(A(Adder_Bits - 1)&A) + signed(B); end process Do_Generic_Adder; end arc;
tb_Generic_Adder.vhd (測試碼, testbench) library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; use ieee.std_logic_unsigned.all; use work.pkg_Generic_Adder.all; entity tb_Generic_Adder is end; architecture arc of tb_Generic_Adder is constant Max_Adder_Bits : integer := 8; constant Max_int : integer := 2 ** (Max_Adder_Bits - 1) - 1; constant Min_int : integer := -1 * 2 ** (Max_Adder_Bits - 1); signal A : std_logic_vector( Max_Adder_Bits - 1 downto 0); signal B : std_logic_vector( Max_Adder_Bits - 1 downto 0); signal Sum : std_logic_vector( Max_Adder_Bits downto 0);
constant Max_Number : std_logic_vector( Max_Adder_Bits - 1 downto 0) := conv_std_logic_vector( Max_int, Max_Adder_Bits + 1) ( Max_Adder_Bits - 1 downto 0); constant Min_Number : std_logic_vector( Max_Adder_Bits - 1 downto 0) := conv_std_logic_vector( Min_int, Max_Adder_Bits + 1) begin process A <= Max_Number; B <= Max_Number; wait for 20 ns; A <= Max_Number; B <= Min_Number; A <= Min_Number; B <= Max_Number;
A <= Min_Number; B <= Min_Number; wait for 20 ns; assert false report " End of Simulation" severity failure; end process; Generic_Adder_Inst: Generic_Adder generic map ( Adder_Bits => Max_Adder_Bits ) port map ( A => A, B => B, Sum => Sum ); end arc;
Generic_Adder 的模擬結果
4-3.5 architecture 宣告 architecture 敘述用來定義:元件的“功能”(function of component) ,它包含“宣告”與“敘述”二大部份: & “宣告”部份 (declarative part) u 包括:component 宣告、constant宣告、function宣告、procedure宣告、type 宣告、signal 宣告 … 等等。 & “敘述”(statement) 部份 u 引用已經設計好的元件 / 單體 (entity)。 u 引用已經設計好的函數 (function) / 程序 (procedure)。 u 電路所需功能的訊號指定敘述、變數指定敘述。 “宣告”部份要寫先再寫“敘述”的部份。 “宣告”部份並沒有一定的撰寫順序,“敘述”的部份也沒有一定的撰寫順序。
4-3.6 architecture 的語法 architecture architecture-name of entity-name is “宣告部份“ begin concurrent-statement; ... end architecture-name; 其中: u architecture-name 用來描述這個 architecture 的名稱。 architecture 的名稱上下要一樣,例如: architecture arch of ECC_Check is end arch;
4-3.6 architecture 的語法 u 最後是 begin … end 區塊 用來描述電路的功能。在這個區塊內可以有:訊號的指定動作、使用己經設計好的元件、process 區塊 … 等等。例如: begin data_out <= data_in; -- 訊號的指定動作 Traffic_Inst: Traffic port map -- 使用己經設計好的元件 ( Clock => Clock, Reset => Reset, RED => RED, GREEN => GREEN, YELLOW => YELLOW );
process ( x, y, enable ) -- process 區塊 begin if ( enable = '1' ) then process ( x, y, enable ) -- process 區塊 begin if ( enable = '1' ) then result <= x xor y; else result <= '0'; end if; end process; end architecture-name;
u ”宣告部份“ ”宣告部份“ 是可以不寫的,但內容可以包括: l subprogram declaration, 程序宣告 l subprogram body, 程序內容 l type declaration, 型態宣告 l subtype declaration, 子型態宣告 l constant declaration, 常數宣告 l signal declaration, 訊號宣告
用來描述 architecture 區塊內的的訊號。例如: signal State : Traffic_State; l variable declaration, 變數宣告 l file declaration, 檔案宣告 l alias declaration, 別名宣告 l component declaration, 元件宣告 l attribute declaration, 屬性宣告 l attribute specification, 屬性規格 l disconnection specification, 中止/不使用規格 l use clause, use 子句 l group template declaration, 群組樣版宣告 l group declaration, 群組宣告
4-3.7 symbol v.s architecture 傳統的設計方式中:任一階層的『Symbol』,電路的“功能”是以深藏在它下面的實際線絡 (Schematic) 來表示,而在 VHDL 中擔任這項工作的即是所謂的Architecture 描述。 NAND3 的『Schematic』實際上是由 3個 pmos 與 3個 nmos 組成的 cmos 電路。 在 architecture 裏頭有各式各樣的 VHDL 敘述 (statement),這些敘述包括:最低層次的『邏輯閘』層次 (Gate Level)、或者稱之為『結構化』層次 (Structural Level)、次低層次的『暫存器轉移』層次 (RTL Level, Register Transfer Level),以及最高層層次的『行為』 (Behavior Level) 描述 / 『資料流』層次 (Data Flow Level),三種方式都可以很完整地表現一個設計的所有功能,只是寫法不大相容、容易程度也大異其趣。
一個 entity 外部所表現出來的功能不受 architecture 內部描述所使用層次的影響,也就是說對於一整個電路而言每個 entity 內部的詳細構造是隱藏無法得知的。 IC/ASIC設計人員可以依據不同的需要而使用不同的層次來描述模組的功能或內部電路。在實際的電路設計中,我們可以三種不同的層次混合使用。
4-4 component 元件宣告 所有的 entity 都必需將該 entity 的名稱以及訊號的定義改用 component component-name 寫在某個 package 裡面,好讓上層的 architecture 裡面可以去引用已經設計好的 entity。即使是只有一個 entity 也要寫寫在某個 package 裡面,因為作電路模擬時、testbench 一定要引用這個 component 的。 component 元件宣告,用來定義 component 的輸出入界面訊號。 通常寫在 architecture宣告或者是 package 宣告裡面。 語法:component component_name is generic ( generic_變數宣告 ) ; -- 可以不寫 port ( 輸出入埠宣告 ) ; end component component_name; 其中,generic_變數宣告的語法:variable_name : variable_type := value; 其中,輸出入埠宣告的語法:variable_name : port_mode variable_type; port_mode 可以是 in、out、inout、buffer 或者是linkage。
component 宣告寫在 architecture 的宣告區中,例如: architecture structural of adderN is component adder port (a : in std_logic; b : in std_logic; cin : in std_logic; sum : out std_logic; cout : out std_logic); end component; begin … end structural; 底下的這個例子中:寫在 pkg_BCD_Adder.vhd 中的是 component BCD_Adder …end component; 而在 BCD_Adder.vhd中的是 entity BCD_Adder is …end BCD_Adder; 不過有用到 use work.pkg_BCD_Adder.all; 敘述去引用 pkg_BCD_Adder 這個 package。在 tb_BCD_Adder.vhd中也是用到 use work.pkg_BCD_Adder.all; 敘述去引用 pkg_BCD_Adder 這個 package,最後引用 BCD_Adder_Inst: BCD_Adder port map。
程式碼 pkg_BCD_Adder.vhd library ieee; use ieee.std_logic_1164.all; package pkg_BCD_Adder is component BCD_Adder port( X : in std_logic_vector( 3 downto 0); Y : in std_logic_vector( 3 downto 0); Cin : in std_logic; BCD : out std_logic_vector( 3 downto 0); Cout : out std_logic ); end component; end pkg_BCD_Adder;
程式碼 BCD_Adder.vhd library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; use ieee.std_logic_unsigned.all; use work.pkg_BCD_Adder.all; entity BCD_Adder is port( X : in std_logic_vector( 3 downto 0); Y : in std_logic_vector( 3 downto 0); Cin : in std_logic; BCD : out std_logic_vector( 3 downto 0); Cout : out std_logic ); end BCD_Adder;
architecture arc of BCD_Adder is begin Do_BCD: process ( X, Y, Cin ) variable S : std_logic_vector( 4 downto 0); variable Cb : std_logic; variable vBCD : std_logic_vector( 4 downto 0); S := '0'&X + Y + Cin; Cb := S(4) or (S(3) and S(2)) or (S(3) and S(1)); vBCD := ('0' & Cb & Cb & '0') + S; Cout <= vBCD(4); BCD <= vBCD( 3 downto 0); end process Do_BCD; end arc;
測試碼 tb_BCD_Adder.vhd (部份而已) library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; use ieee.std_logic_unsigned.all; use work.pkg_BCD_Adder.all; entity tb_BCD_Adder is end;
architecture arc of tb_BCD_Adder is … begin BCD_Adder_Inst: BCD_Adder port map ( X => X, Y => Y, Cin => Cin, BCD => BCD, Cout => Cout ); end arc;
4-5 VHDL 的三種設計層次 三種不同的設計層次: u 邏輯閘層次模型 (Structural Level / Gate Level Model) u 暫存器轉移層次 (RTL Level, Register Transfer Level) u 行為式層次 (Behavioral Level / Data Flow Level)
4-5.1 邏輯閘層次模型 (Structural Level / Gate Level Model) 在這個層次中,模組是由最基本的邏輯閘連接而形成的。在電路面積要求的情況下,可以先將所需的電路化簡、再使用現有的邏輯閘元件湊出 (組合出) 所需要的電路。 例如:not、and、or、nand、nor、xor … 等邏輯閘元件。 使用事先描述好的元件 (components),像是:“合成軟體”(或者是“晶圓廠”) 所提供的 Cell-Library 中的邏輯閘來描述電路。 entity Half_Adder is port( x : in std_logic; y : in std_logic; enable : in std_logic; carry : out std_logic result : out std_logic ); end Half_Adder;
architecture gate_level of Half_Adder is component and2 port ( in0, in1 : in bit; out0 : out bit ); end component; component and3 port ( in0, in1, in2 : in bit; out0 : out bit ); component xor2
signal xor_res : bit; -- 內部的 signal begin a0 : and2 port map (enable, xor_res, result); a1 : and3 port map (x, y, enable, carry); x0 : xor2 port map (x, y, xor_res); end Half_Adder;
2對4解碼器 (Decoder_2to4) inv.vhd (部份) architecture arc of inv is begin Y <= not I; end arc; and3.vhd (部份) architecture arc of and3 is signal and_i0_i1 : std_logic; and_i0_i1 <= I0 and I1; Y <= I2 and and_i0_i1;
Decoder_2to4.vhd (部份) architecture arc of Decoder_2to4 is signal NOTI0, NOTI1 : std_logic; begin U1 : inv port map ( I0, NOTI0 ); U2 : inv port map ( I1, NOTI1 ); U3 : and3 port map ( NOTI0, NOTI1, EN, Y0 ); U4 : and3 port map ( I0, NOTI1, EN, Y1 ); U5 : and3 port map ( NOTI0, I1, EN, Y2 ); U6 : and3 port map ( I0, I1, EN, Y3 ); end arc;
4-5.2 暫存器轉移層次 (RTL Level, Register Transfer Level) 使用“邏輯函數”(logic equation) 作為“資料流”層次的電路描述。 在這個層次中,設計電路的重點在於說明資料如何在暫存器中儲存與傳送。 例如:使用 <= 敘述產生的指定動作。 architecture RTL of Half_Adder is begin carry <= enable and (x and y); result <= enable and (x xor y); end RTL;
4-5.3 行為式層次 (Behavioral Level / Data Flow Level) 用類似“高階語言”的語法來描述電路的功能 (function)。 這個層次是VHDL 中 最高階的層次,在這個層次中我們只需要考慮模組的功能,並不須要考慮元件的物理特性、連接線路的特性 … 等等關於硬體方面詳細的部份。相較於『邏輯閘』層次 (Gate Level),在這個層次電路設計的工作就好像是在寫C語言一樣、是一種比較高階的設計方式。 例如:使用 process、for、while、case … 等等之類的敘述所產生的電路。 architecture Behavioral of Half_Adder is begin process ( x, y, enable ) if ( enable = '1' ) then carry <= x and y; result <= x xor y; else carry <= '0'; result <= '0'; end if; end process; end Behavioral;
4-6 configuration 敘述 configuration 敘述通常會在 test bench 測試程式中看得到,它主要的功能用於選擇已經事先設計好的 component,而這些功能相同的 component 通常會有一種以上的設計方式,為了驗證不同的設計方式電路模擬的結果是否相同就會用到 configuration 敘述。 比方說某個您可能先用 Behavioral Level 的方式來設計一個 design,也可以 RTL Level 的方式來設計,也可能用 Structural Level 的方式去設計;不同的設計方式電路模擬的結果也要相同才能保證您用三種不同 Level 設計出來的 design 是相同功能的,還有另外一種可能是您用不能合成的 VHDL 語法寫出一個功能模擬 (functional simulation) 的寫法、只是為了驗證您的電路動作情形是否相同;所以會用到 configuration 敘述選擇一個設計中的不同 architecture / architecture 也可能會用到不同的 component 來作電路的模擬,兩兩之間電路模擬的結果就可以拿來作比較與驗證了。
大多數的 VHDL 編譯器以及模擬軟體支援電路的最上層可以使用 configuration 敘述。 configuration 敘述確實是比較複雜一點,您可以先不用看這個部份;以後再回頭過來看也是可以的;因為寫 VHDL 程式完全不想用 configuration 敘述也是都可以的;而本書中也只有這一個章節有用到 configuration 敘述而已,其他地方完全沒有出現過。 configuration 語法: configuration identifier of entity_name is -- use 敘述 for architecture_name { block_configuration } end for; end configuration;
block_configuration ::= for block_specification -- use 敘述 { configuration_item } end for; configuration_item ::= block_configuration | component_configuration component_configuration ::= for component_specification [ binding_indication ; ] [ block_configuration ] end for ;
例如:設計一個 N Bits 的 Adder,同時採用:Structural Level、RTL Level 以及 Behavioral Level 三種方式去設計;test bench 測試程式中則是使用 configuration 敘述去引用三種不同設計層次的 architecture。
VHDL 程式碼 (參考資料來源 Modelsim 5.6b 中的 example,有作些修改) pkg_Adder.vhd library ieee; use ieee.std_logic_1164.all; package pkg_Adder is component xor_gate generic ( gate_delay_H2L : time := 1 ns; gate_delay_L2H : time := 1 ns); port ( in1, in2 : in std_logic; out1 : out std_logic); end component;
component and_gate generic ( gate_delay_H2L : time := 1 ns; gate_delay_L2H : time := 1 ns); port ( in1, in2 : in std_logic; out1 : out std_logic); end component; component or_gate
component adder_1bit port ( a : in std_logic; b : in std_logic; cin : in std_logic; sum : out std_logic; cout : out std_logic); end component; component adder_Nbits generic( n : integer := 8); port ( a : in std_logic_vector( n downto 1); b : in std_logic_vector( n downto 1); sum : out std_logic_vector( n downto 1); end pkg_Adder;
xor_gate.vhd entity xor_gate is generic ( gate_delay_H2L : time := 1 ns; gate_delay_L2H : time := 1 ns); port ( in1, in2 : in std_logic; out1 : out std_logic); end xor_gate; architecture behav of xor_gate is begin process( in1, in2 ) variable val : std_logic;
val := in1 xor in2; case val is when '0' => out1 <= '0' after gate_delay_H2L; when '1' => out1 <= '1' after gate_delay_L2H; when others => out1 <= val; end case; end process; end behav;
and_gate.vhd entity and_gate is generic ( gate_delay_H2L : time := 1 ns; gate_delay_L2H : time := 1 ns); port ( in1, in2 : in std_logic; out1 : out std_logic); end and_gate; architecture behav of and_gate is begin process( in1, in2 ) variable val : std_logic;
val := in1 and in2; case val is when '0' => out1 <= '0' after gate_delay_H2L; when '1' => out1 <= '1' after gate_delay_L2H; when others => out1 <= val; end case; end process; end behav;
or_gate.vhd entity or_gate is generic ( gate_delay_H2L : time := 1 ns; gate_delay_L2H : time := 1 ns); port ( in1, in2 : in std_logic; out1 : out std_logic); end or_gate; architecture behav of or_gate is begin process( in1, in2 ) variable val : std_logic;
val := in1 or in2; case val is when '0' => out1 <= '0' after gate_delay_H2L; when '1' => out1 <= '1' after gate_delay_L2H; when others => out1 <= val; end case; end process; end behav;
adder_1bit.vhd entity adder_1bit is port ( a : in std_logic; b : in std_logic; cin : in std_logic; sum : out std_logic; cout : out std_logic); end adder_1bit; architecture structural of adder_1bit is signal a_xor_b, a_and_b, cin_and_a_xor_b : std_logic; begin -- sum <= (a xor b) xor cin; xor1 : xor_gate port map( in1 => a, in2 => b, out1 => a_xor_b );
xor2 : xor_gate port map( in1 => a_xor_b, in2 => cin, out1 => sum ); -- cout <= (a and b) or (cin and (a xor b)); and1 : and_gate port map( in1 => a, in2 => b, out1 => a_and_b
and2 : and_gate port map( in1 => cin, in2 => a_xor_b, out1 => cin_and_a_xor_b ); org1 : or_gate port map( in1 => a_and_b, in2 => cin_and_a_xor_b, out1 => cout end structural;
adder_Nbits.vhd library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; use ieee.std_logic_unsigned.all; use work.pkg_Adder.all; entity adder_Nbits is generic( n : integer := 8); port ( a : in std_logic_vector( n downto 1); b : in std_logic_vector( n downto 1); cin : in std_logic; sum : out std_logic_vector( n downto 1); cout : out std_logic); end adder_Nbits;
Structural -- Structural adder_Nbits architecture structural of adder_Nbits is signal carry : std_logic_vector( 0 to n); begin carry(0) <= cin; struc_adder_Nbits: for i in 1 to n generate add1 : adder_1bit port map ( a => a(i), b => b(i), cin => carry(i - 1), sum => sum(i), cout => carry(i) ); end generate; cout <= carry(n); end structural;
RTL -- RTL adder_Nbits architecture rtl of adder_Nbits is begin process( a, b, cin ) variable vsum : std_logic_vector( n downto 1); variable carry : std_logic; carry := cin; for i in 1 to n loop vsum(i) := (a(i) xor b(i)) xor carry; carry := (a(i) and b(i)) or (carry and (a(i) xor b(i))); end loop; sum <= vsum; cout <= carry; end process rtl_adder; end rtl;
Behavioral -- Behavioral adder_Nbits architecture behavioral of adder_Nbits is begin behav_adder: process( a, b, cin ) variable vsum : std_logic_vector( n + 1 downto 1); variable vsum_c : std_logic_vector( n + 1 downto 1); vsum := '0'&unsigned(a) + unsigned(b); vsum_c := unsigned(vsum) + cin; sum <= vsum_c( n downto 1); cout <= vsum_c( n + 1); end process behav_adder; end behavioral;
tb_Nbits_adder.vhd configuration test_adder_structural of tb_Nbits_adder is for adder_8bits for all : adder_Nbits use entity work.adder_Nbits(structural); for structural for struc_adder_Nbits for all : adder_1bit use entity work.adder_1bit(structural); end for; end test_adder_structural;
configuration test_adder_rtl of tb_Nbits_adder is for adder_8bits for all : adder_Nbits use entity work.adder_Nbits(rtl); end for; end test_adder_rtl; configuration test_adder_behavioral of tb_Nbits_adder is use entity work.adder_Nbits(behavioral); end test_adder_behavioral;
4-7 process 敘述 每個 entity 一定會有一個 architecture 裡面寫著 concurrent 敘述以及 process 敘述,在 process 敘述頭則是可以寫的 Sequential 敘述。 process 是寫在 architecture 區塊裡面,在 architecture 區塊裡面可以完全沒有 process 宣告、也可以有一個以上的 process 宣告。 process 的語法結構,如下: [ label: ] process ( sensitivity-lisit ) ”宣告部份“ -- 可以不寫 begin 訊號指定敘述 ( <= ) 或者是 變數指定敘述 ( := ) 或者是 判斷敘述 (if、then、else、elsif、end if … 等等) 或者是 條件選擇敘述 (case、when … 等等) 或者是 迴圈敘述 (for、while … 等等) 或者是 引用已經設計好的函數 (function) / 程序 (procedure) 或者是 wait 敘述 (但不能用於電路的合成) end process [ label ] ;
”宣告部份“ 是可以不寫的,但內容可以包括: u label 是可以不寫的。 u ”宣告部份“ ”宣告部份“ 是可以不寫的,但內容可以包括: l subprogram declaration, 程序宣告 l subprogram body, 程序內容 l type declaration, 型態宣告 l subtype declaration, 子型態宣告 l constant declaration, 常數宣告 l variable declaration, 變數宣告 l file declaration, 檔案宣告 l alias declaration, 別名宣告 l attribute declaration, 屬性宣告 l attribute specification, 屬性規格 l disconnection specification, 中止/不使用規格 l use clause, use 子句 l group template declaration, 群組樣版宣告 l group declaration, 群組宣告 特別要注意的是:在 process 區塊內是不能寫“訊號宣告”(signal declaration) 的,所有的“訊號宣告”只能寫在 architecture … begin 區裡。
process 敘述的觀念有點像是一名警衛一樣、隨時在監督外界 (輸出入埠) 訊號的情況,當外界的訊號一有變化隨即告知模組內部要配合作相對應的處理。 只要在 sensitivity-lisit“感測列”中的訊號有變化,那麼就會進到該 process 區塊中去處理 process 內的動作。 用 process 敘述來描述組合邏輯電路 (combinatorial) 如果在『感測列』少寫了 process 區塊內的訊號來源的話,那麼就很可能發生 VHDL 電路在『合成之前』的模擬結果與『合成之後』的模擬結果是不相同的。 請參考:4-7.4『準位觸發』(Level-Trigger) 以及 4-7.5『邊緣觸發』(Edge-Trigger) 這二個章節。
4-7.1 VHDL的時序模式 (timing model) VHDL 使用底下的訊號模擬週期作為數位電路模擬的流程,免不了的電路模擬需要給定輸入訊號值 (stimulus) 每一個模擬過程都會有模擬結果 (response)。
4-7.2『事件』(Event) VHDL 和 Verilog 一樣都是:事件基礎時間控制 (Event-based Timing Control)。 什麼是「事件」? 當由外界將資料送到 entity 的輸入埠的值時候都,會發生一個事件。 某個訊號 (signal) 的值被改變的時候,也會發生一個事件。 事件可以用來觸發一道敘述或者是一個內含多個敘述的 process 區塊。
正規事件控制 (Regular Event Control) 當訊產生正緣觸發 (Rising_Edge)、負緣 (Falling_Edge)、轉換 (transition) 或者值被改變時,敘述才會被執行。 例如: process ( Clock ) -- 每當 Clock 的訊號有變化時 begin q1 <= d1; end process;
process ( Clock ) -- 每當 Clock 的訊號有變化時 begin -- 當 Clock 的訊號由 0 變為 1 時 if Rising_Edge( Clk ) then -- 「正緣」觸發 -- if ( Clk'event and Clk = '1' ) then – 也可以這樣 q1 <= d1; end if; end process; begin -- 當 Clock 的訊號由 1 變為 0 時 if Falling_Edge( Clk ) then -- 「負緣」觸發 -- if ( Clk'event and Clk = '0' ) then – 也可以這樣
事件「或」控制 (Event OR Control) 當有多個訊號或事件觸發一個敘述或一個內含多個敘述的區塊,這種寫法如同將這些訊號作「或」的運算。 例如: Result <= data1 & data2 & data3; 只要 data1, data2或者是data3其中一個訊號發生變化就產生一個事件。 -- 非同步具有預設以及重設控制的暫存器 process ( Preset, Reset, Clk ) -- 只要其中一個訊號發生變化 begin -- 就會觸發這個 process if ( Preset = '1' ) then Q <= '1'; elsif ( Reset = '1' ) then Q <= '0'; elsif Rising_Edge( Clk ) then Q <= D; end if; end process Do_D_FlipFlop_ASR;
4-7.3 Combinatorial process 與 Clocked process 與 clock 不相關的 combinational process 只要在訊號a 或者是訊號b有變化,那麼就會進到該 process 區塊中去處理 process 內的動作。 所謂某個訊號有變化是指該訊號由0 Z、01、1Z、10 … 等等只要前後訊號的值不同即是訊號有變化。 這樣的作法使得 process 內的動作並不與 Clock 時脈訊號同步處理。 例如: process ( a, b ) begin -- 只要訊號 a 或者是 b 有變化 f <= a and b; -- 則更新 f 的值 end process;
與 clock 相關的 clocked process
Clk 訊號由 0 轉變為 1 (0 → 1)、 「正緣」觸發
Clk 訊號由 1 轉變為 0 ( 1 → 0)、「負緣」觸發
只要在訊號 Clk 有變化,那麼就會進到該 process 區塊中去處理 process 內的動作。 這樣的作法使得 process 內的動作與 Clock 時脈訊號同步處理。 例如: process ( Clk ) begin -- 只要訊號 Clk 有變化 (由 0→1 或 1→0) if Rising_Edge( Clk ) then -- 「正緣」觸發 (由 0→1) f <= a and b; -- 則更新 f 的值 end if; end process;
值得注意的是: if Rising_Edge( Clk ) then -- 「正緣」觸發 並不等於 if not Falling_Edge( Clk ) then if Falling_Edge( Clk ) then -- 「負緣」觸發 也不等於 if not Rising_Edge( Clk ) then if (Clk’event and Clk = ‘1’) then -- 「正緣」觸發 並不等於 if not (Clk’event and Clk = ‘0’) then if (Clk’event and Clk = ‘0’) then -- 「負緣」觸發 並不等於 if not (Clk’event and Clk = ‘1’) then
4-7.4『準位觸發』(Level-Trigger) 在sensitivy-list (感測列) 中的所有變數必須在 process 的 begin … end process; 這個 block 中出現,否則合成出來的 Gate Level 電路將會有閂鎖 (Latches)。 u 準位觸發 (Level Trigger) 的例子 process ( a, b, c ) begin -- 只要訊號 a, b 或者是 c 有變化 f <= a and b or c; -- 則更新 f 的值 end process;
l 在「感測列」(Sensitivity list) 中,少了 c 這個訊號,模擬軟體或者是 HDL 在編譯 (Compile) 時會有“Incomplete sensitivity list - assuming completeness”及“Referenced variable c is not in sensitivity list”的警告 (Warning) 訊息。 例如: process ( a, b ) -- c 不在「感測列」中 begin -- 只要訊號 a 或者是 b 有變化 f <= a and b or c; -- 則更新 f 的值 end process; 用 process 敘述來描述組合邏輯電路 (combinatorial) 如果在『感測列』少寫了 process 區塊內的訊號來源的話,那麼就很可能發生 VHDL 電路在『合成之前』的模擬結果與『合成之後』的模擬結果是不相同的。
l 在「感測列」(Sensitivity list) 中,多了 d 這個訊號,則合成出來的電路也沒有任何的影響。 例如: 雖然合成出來的電路也沒有任何的影響,但是 .vhd 程式的可讀性變差了因為可能會讓人誤認為 d 也訊號來源之一。電路在模擬時,只要 d 有發生變化那麼也會觸發該 process 產生動作,結果雖然沒有影響到任何一個敘述,但因此讓模擬的速度慢了一些。 電路“合成軟體“會出現“Warning: Ignored unnecessary INPUT pin ‘d’”的警告 (Warning) 訊息。 process ( a, b, c, d ) -- process 區塊裡不需要用到 d begin -- 只要訊號 a、b或者是 c 有變化 (由 0→1 或 1→0) f <= a and b or c; -- 則更新 f 的值 end process;
4-7.5『邊緣觸發』(Edge-Trigger) 在 Sensitivity list 中訊號名稱的前面如果加上 Rising_Edge (正緣觸發) 或者是 Falling_Edge (負緣觸發) 這二個識別字 (Keyword) 的其中一種,則合成軟體將會使用 flip-flop 來合成出 Gate Level 電路。 u 正緣觸發 (Rising_Edge) 的例子 process ( Clk ) -- 只要訊號 Clock 為正緣觸發 begin if Rising_Edge ( Clk ) then -- if ( Clk'event and Clk = '1' ) then – 也可以這樣 f <= a and b or c; -- 則更新 f 的值 end if; end process; 訊號 Clock 為 posedge edge trigger (正緣觸發)
負緣觸發 (Falling_Edge) 的例子 process ( Clk ) -- 只要訊號 Clock 為負緣觸發 begin if Falling_Edge ( Clk ) then -- if ( Clk'event and Clk = '0' ) then – 也可以這樣 f <= a and b or c; -- 則更新 f 的值 end if; end process; 訊號 Clock 為 negedge edge trigger (負緣觸發)
4-7.6『準位觸發』與『邊緣觸發』 時序圖說明例子
4-8 block 敘述 block 敘述主要是將同一個電路中、某一連串的 concurrent statement 結合起來形成一個獨立的電路一個群組,許多個獨立的電路群組結合起來形成我們設計的電路。 模組化的設計方式,可以讓將來對於電路的維護與除錯較為清楚。 各個群組可以一一為之命名,如語法中的 label 部份。 語法: label : block [ ( guard expression ) ] [ is ] generic ( generic_變數宣告 ) ; -- 可以不寫 port ( 輸出入埠宣告 ) ; block 內的訊號宣告 begin <concurrent statement> end block [ label ] ;
l 第一個 label 一定要有,最後那個 label 是可以不寫的。 例如: signal x : std_logic := '1'; signal y : std_logic := '0'; signal z : std_logic := '1'; signal MM, NN : std_logic; x <= not x after 20 ns; y <= not y after 40 ns; z <= not z after 40 ns; block3 : -- 沒有 guard block begin MM <= x or y; NN <= x and not y; end block block3;
block4: -- x'stable(2 ns) 時 guard = true, 否則 guard = false block ( x'stable(2 ns) ) constant delay_time : time := 3 ns; signal tmp, Result : std_logic; begin tmp <= x xor y after delay_time; Result <= z nor tmp; end block;
4-9三種用於『資料流』(data flow) 的敘述 大部份的情況會需要描述資料的運作以及運算完的結果會存放那個訊號裡頭的觀念就稱作『資料流』,很像用“高階語言”的語法來描述電路的功能 (function) 、是一種比較高階的設計方式。 它們必需寫在 architecture 敘述之內,process 敘述的外面。 VHDL 提供三種用於資料流的敘述: 同時性的訊號指定 (concurrent signal assignment) 有條件的訊號指定 (conditional signal assignment) 選擇性的訊號指定 (selected signal assignment)
4-9.1 同時性的訊號指定 (Concurrent signal assignment) 同時性的訊號指定 (concurrent signal assignment) 必需寫在 architecture 敘述之內,process 敘述的外面。 語法:signal-name <= expression; 就是把值或者是運算式 (expression) 指定給某個訊號 (signal-name)。要注意 signal-name 和 expression 的型態以及資料的長度都要一致。 在標準的 C 或 Pascal 語言裏頭,程式的執行是按照程式碼編寫的先後順序一個接一個有既定順序的 (Sequential) 地被執行。 基本上,在同一時間中只有一個程式碼被執行。但在真實的硬體世界裏,許多彼此不相關連的訊號變化或運算可以平行進行 (Concurrent),為了能充份表現硬體線路的所有特性,VHDL允許 Sequential 和 Concurrent 兩種不同的描述存在。 在 architecture 的主區塊中只能接受 concurrent 的敘述,但是在 process的區塊裏頭,所有的敘述 (statement) 則是循序地被執行,是所謂的 sequential statement。 在 architecture 的區塊中是由一連串的 concurrent statement 所組成的,也包括 process 敘述;每個 concurrent statement 會同時地執行。
在底下的這個例子中,Data1_or_2_or_3 先是等於 Data1、再來 Data1_or_2_or_3 等於 Data2、後來 Data1_or_Data2 等於 Data3,那麼電路模擬的結果 Data1_or_2_or_3 等於那個值?電路模擬的結果 Data1_or_2_or_3 等於 Data3。結論是以最後一次更新的動作為結果,這個結果表示著最後電路的穩定狀態。 process ( Data1, Data2, Data3 ) begin Data1_or_2_or_3 <= Data1; -- … … -- … … 許多其他的敘述或動作 Data1_or_2_or_3 <= Data2; Data1_or_2_or_3 <= Data3; end process;
4-9.2 最基本的 Concurrent statement 最基本的 Concurrent statement 就是一個在 architecture 中的元件 (component)。將 architecture 引用元件的語法,如下: label : component-name port map ( signal_1, signal_2, …, signal_n ); 其中: l label 直接翻譯叫作“標記”,實際上是用來區分不同的元件代號,因為在同一個 architecture 中可能會引用二個或以上且是相同功能的元件,區分的方法就是用“標記”。要在模擬結果的波型畫面 (waveform) 看訊號也是用“標記”來區分不同名稱但相同元件的波型。例如: l component-name component-name 就是要被引用事先定義好或者是寫好的 entity“單體”的名稱。 l port map port map 是關鍵字,一定要有。 l ( signal1, signal2, …, signaln ); 引用元件要傳遞的輸出入訊號參數,可以用“埠的名稱對映” (port mapping) 的方式、也可以用“埠的位置對映” (position mapping) 的方式來叫用。請看底下的範例。
例如:同樣都是用 Segment_7 這個元件,第一個例子是用“埠的名稱對映” (port mapping) 的方式、而第二個是用“埠的位置對映” (position mapping) 的方式來叫用。 Segment_7_Sec10: Segment_7 port map ( Data => To_7Seg_Sec10, a => Sec10_a, b => Sec10_b, c => Sec10_c, d => Sec10_d, e => Sec10_e, f => Sec10_f, g => Sec10_g ); Segment_7_Sec1: Segment_7 port map ( Data, a, b, c, d, e, f, g )
4-9.2 有條件的訊號指定 (Conditional signal assignment) 有條件的訊號指定 (conditional signal assignment) 必需寫在 architecture 敘述之內,process 敘述的外面。 語法: signal-name <= expression-1 when boolean-expression-1 else expression-2 when boolean-expression-2 else ... expression-M when boolean-expression-M else expression-N; l 如果 boolean-expression-1 條件成立則signal-name = expression-1 否則如果 boolean-expression-2 條件成立則signal-name = expression-2 … 否則如果 boolean-expression-M 條件成立則signal-name = expression-M 否則 signal-name = expression-N
l boolean-expression-1 條件具有最高優先權 (Priority),boolean-expression-2 條件具有第2高優先權 … boolean-expression-M 條件具有倒數第二高優先權,最低的優先權則是 signal-name = expression-N。 l 也就是說“合成軟體”將會使用“優先權編碼”電路的方式來合成出 Gate Level 電路。 l 布林運算式 (boolean-expression) 可以只是一個邏輯值的判斷、使用 VHDL 內建的組合邏輯運算元 (and、or、not … ) 或者是VHDL 內建的關係運算運算元 (=、/=、>、>=、<、<=)。 l 如果沒有將“所有可能的”分支判別條件都指定,那麼“合成軟體”將會使用 Latch (閂鎖器) 來合成出 Gate Level 電路,解決的方法就是最後一個 else 無論如何也要考量進來。 例如:target <= source when (OK = ‘1’); target <= source1 when (OK = ‘1’) else source2;
4對1的多工器 (Multiplexier) 因為 Select 是 VHDL 的保留字,所以才會用 Sel。 如果 Sel = 00、則 Output = a,否則如果 Sel = 01、則 Output = b,否則如果 Sel = 10,則 Output = c,否則如果 Sel = 11、則 Output = d,否則Output = ‘0’。 Output <= a when ( Sel = “00” ) else b when ( Sel = “01” ) else c when ( Sel = “10” ) else d when ( Sel = “11” ) else '0';
4-9.3 選擇性的訊號指定 (Selected signal assignment) 選擇性的訊號指定 (selected signal assignment) 必需寫在 architecture 敘述之內,process 敘述的外面。 語法: with expression select signal-name <= signal-value-1 when choice-1, signal-value-2 when choice-2, -- 可以不寫 .. … -- 可以不寫 signal-value-M when choice-M, -- 可以不寫 signal-value-N when others; -- 可以不寫
l 這個敘述會將 expression 同時和所有的 choice- 作比較,當 expression的值符合 choices-1 則 signal-name 等於 signal-value-1,當 expression的值符合 choices-2 如果符合則 signal-name 等於 signal-value-2, … ,如果 expression 的值都不符合的話則 signal-name 會等於 signal-value-N。 l choices-1、choices-2、 … 、choices-M 以及 others 的優先權都是相同的。 l 也就是說“合成軟體”使用平行運作處理的方式來合成出 Gate Level 電路。 l choices-1 到 choices-M 可以只是一個邏輯值或者是另一個運算式。 l choices-1 到 choices-M 必需要有互斥 (mutually exclusive) 的特性,才能讓每一個條件都有機會去選擇到。 l when others 代表都不符合 choices-1 到 choices-M 的情形,則會用 signal-value-N 給 signal-name。 l 如果沒有將“所有可能的”分支判別條件都指定,那麼“合成軟體”將會使用 Latch (閂鎖器) 來合成出 Gate Level 電路,解決的方法就是考量 when others; 時訊號該給什麼值。
其他例子: signal choice_slv : std_logic_vector( 1 downto 0); signal slv : std_logic := ‘1’; signal result_slv : std_logic_vector( 2 downto 0); with choice_slv select result_slv <= ( '0', '0', '0' ) when "00", ( '0', '0', slv ) when "01", ( '0', slv, '0' ) when "10", ( slv, '0', '0' ) when "11", ( '1', '1', '1' ) when others; -- 用 std_logic_vector 時 when others 這一行一定要寫,要不然編譯器會出現錯誤訊息:Case statement covers only 4 out of 81 cases.
signal choice_bv : bit_vector( 1 downto 0); signal bv : bit; signal result_bv : bit_vector( 2 downto 0); bv <= '1'; with choice_bv select result_bv <= ( '0', '0', '0' ) when "00", ( '0', '0', bv ) when "01", ( '0', bv, '0' ) when "10", ( bv, '0', '0' ) when "11", ( '1', '1', '1' ) when others; -- 相同的處理,用 bit_vector 時 when others 這一行可以不寫,編譯器並不會出現錯誤訊息:Case statement covers only 4 out of 81 cases.