歡迎您光臨本站 登入註冊首頁

怎樣寫testbench

admin @ 2014-03-26 , reply:0

概述

本文的實際編程環境:ISE6.2i.03ModelSim5.8SESynplifyPro7.6編程語言VHDL在ISE中調用ModelSim進行模擬一、基本概念和基礎知識  &nb……

本文的實際編程環境:ISE 6.2i.03
ModelSim 5.8 SE
Synplify Pro 7.6
編程語言 VHDL
在ISE 中調用ModelSim 進行模擬

一、 基本概念和基礎知識
    Testbench 不僅要產生激勵也就是輸入,還要驗證響應也就是輸出。當然也可以只產生激勵,然後通過波形窗口通過人工的方法去驗證波形,這種方法只能適用於小規模的設計。
    在ISE 環境中,當前資源操作窗顯示了資源管理窗口中選中的資源文件能進行的相關操作。在資源管理窗口選中了testbench 文件后,在當前資源操作窗顯示的ModelSim Simulator 中顯示了4 種能進行的模擬操作,分別是:Simulator Behavioral Model(功能模擬)、Simulator Post-translate VHDL Model(翻譯后模擬)、Simulator Post-Map VHDL Model(映射后模擬)、Simulator Post-Place & Route VHDL Model(布局布線后模擬)。如圖1 所示:
 
圖1

  •  Simulator Behavioral Model 也就是所說的功能模擬、行為模擬、前模擬。驗證功能是否正確,這是設計的第一步。功能模擬正確的程序不一定能被正確綜合,也就是硬體實現。有的在綜合時報錯誤,有的雖然能綜合但結果並不正確。當然,功能模擬如果都不能通過,以後的步驟也就無法進行。這是必做的模擬。
  •  Simulator Post-translate VHDL Model 也就是翻譯后模擬。對源程序進行編譯后首先排除了語法錯誤,對一些像類屬命令(Generic)、生成語句(Generate)等進行了展開。不是必做的模擬。
  •  Simulator Post-Map VHDL Model也就是映射后模擬。不同的器件內部結構也不盡相同,映射的作用就是將綜合后產生的網表文件對應到實際的器件上去。由於映射不包含布線,也就是要用什麼類型的邏輯單元雖然已經確定但要用哪個位置的還沒有確定,因此,映射后模擬不包含布線延時。不是必做的模擬。
  •  Simulator Post-Place & Route VHDL Model 也就是所說的布局布線后模擬、時序模擬、后模擬。這是最完整的模擬,既包含邏輯延時又包含布線延時。在做布局布線后模擬時要用到一個叫SDF的文件。SDF文件包含設計中每個單元(Cell)的延時和時序約束數據。通過載入這個文件就能得到完整的時序情況。它是必做的模擬。

一般必須進行功能模擬和布局布線后模擬。

常見問題
為什麼有的testbench在進行功能模擬時能正確進行,而在進行布局布線后模擬時就不能運行。
有兩點要注意的地方:
(1)、在做映射后模擬或布局布線后模擬時,都已經經過了綜合工具的綜合,源程序中的類屬命令(Generic)、生成語句(Generate)等都已經進行展開。例如,如果用Generic 定義了一個參數width,綜合工具進行綜合時已經按照一個確定的width 值進行了綜合。它生成的電路已經具有一個確定的結構,不能再隨意調整。所以在映射后模擬和布局布線后模擬的testbench中,往往不能出現Generic 語句。
(2)映射后模擬和布局布線后模擬都要用到SDF 文件,並且要將SDF文件關聯到設計中的實例。所以在映射后模擬和布局布線后模擬的testbench中,第一,要將你的設計聲明成一個元件。第二,實例化你設計的元件並且實例名要取為UUT(默認的,當然也可以改)。

關於斷言語句
在模擬中為了能得到更多信息,經常要用到斷言語句(assert)。其語法如下:
Assert<條件>
Report<消息>
Severity<出錯級別>;
出錯級別共有5 種:

  •  Note
  •  Warning
  •  Error
  •  Failure
  •  Fatal

在VHDL 模型的模擬過程中,一旦斷言語句的條件為假,則發送消息並將出錯級別發送給模擬器。通常可以設置一個中止模擬器運行的出錯級別,一般默認的中止運行的出錯級別為Failure。
我們來看一個例子:
assert false
report "********* " & IMAGE(DWIDTH) & "BIT DIVIDER SEQUENCE FINISHED
AT " & IMAGE(now) & " !" & " *********"
severity note;
斷言的條件不是一個條件表達式,而直接是false。這說明只要程序執行到這裡斷言就一定會成立,送出消息。出錯級別為note,在模擬器的輸出窗口將會顯示:
 
圖2
再看一個例子:
assert (s_cyi((DWIDTH-1)/4) = '0')
and (s_ovi = '0')
and (s_qutnt = conv_std_logic_vector(v_quot,DWIDTH))
and (s_rmndr = conv_std_logic_vector(v_remd,DWIDTH))
report "ERROR in division!"
severity failure;
斷言的條件有4 個並且是與的關係,只要其中一個條件不成立則整個表達式為假,斷言成立。如果斷言成立將輸出“ERROR in division!“這個消息。並且通知模擬器出錯級別為failure,這一般會停止模擬。這個斷言實際是在對結果進行驗證。

二、實際testbench分析
    下面將詳細分析一個實際的testbench,它是用來測試8051 的ALU單元的除法功能的。8 位的除法器,被除數和除數的組合共有256×256=65536 種。我們採用的方法是窮舉所有的輸入組合,這樣的代碼覆蓋率可以達到100%。它的驗證必須通過程序自動完成,否則通過人工方法工作量太大。
    把要測試的程序當作一個元件,例如想象成一個74 系列數字電路。Testbench 的作用是在被測試電路的輸入端加上激勵,然後比較被測試電路的輸出和計算出來的期望值是否一致。對我們這個例子來說,在要模擬的ALU 輸入端產生65536 種輸入組合,然後將ALU產生的對應輸出值和testbench 算出的期望值相比較,如果有錯誤產生則停止模擬並輸出信息。ALU 的除法單元的輸入有4 個,分別是被除數、除數、進位、溢出位;輸出也有4 個,分別是商、餘數、新的進位、新的溢出位。
1、 testbench 的輸出s_dvdnd(被除數)、s_dvsor(除數)、s_cyo(進位)、s_ovo(溢出位)連接到ALU 的輸入acc_i(被除數)、ram_data_i(除數)、cy_i(進位)、ov_i(溢出位);
2、 testbench 的輸入s_qutnt(商)、s_rmndr(餘數)、s_cyi(進位)、s_ovi(溢出位)連接到ALU的輸出result_a_o(商)、 result_b_o(餘數)、new_cy_o(進位)、new_ov_o(溢出位)。
3、 總之,testbench 驅動被測試單元,同時對被測試單元的輸出進行驗證。
4、assert (s_cyi((DWIDTH-1)/4) = '0')
and (s_ovi = '0')
and (s_qutnt = conv_std_logic_vector(v_quot,DWIDTH))
and (s_rmndr = conv_std_logic_vector(v_remd,DWIDTH))
report "ERROR in division!"
severity failure;
根據51 指令系統規定,除法運算的cy 位固定為0,如果除數為0則ov 置1,否則置0。
程序中
s_qutnt = conv_std_logic_vector(v_quot,DWIDTH)
s_rmndr = conv_std_logic_vector(v_remd,DWIDTH)
用來對運算結果進行比較。conv_std_logic_vector()是類型轉換函數。

??首先是對庫的引用
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
library work;
use work.mc8051_p.all;
library STD;
use STD.textio.all;
??定義結構體,testbench程序的結構體是空的。因為testbench是用來模擬的,不存在
--對外的介面,所以entity是空的。但是必須要有,這是語法的要求。
entity TBX_mc8051_alu is
end TBX_mc8051_alu;
-------------------------------------------------------------------------------
architecture TBX_ARCH_DIV of TBX_mc8051_alu is
??定義元件,映射后模擬和布局布線后模擬要使用SDF 文件,必須指定實例名。要實例
--化元件首先必須定義元件。
component mc8051_alu
port (
new_ov_o : out STD_LOGIC; ??新的ov位,輸出
ov_i : in STD_LOGIC := 'X'; ??ov位,輸入
new_cy_o : out STD_LOGIC_VECTOR ( 1 downto 0 ); ??新的cy位,輸出
acc_i : in STD_LOGIC_VECTOR ( 7 downto 0 ); ??acc,輸入
rom_data_i : in STD_LOGIC_VECTOR ( 7 downto 0 ); ??rom_data,輸入
cmd_i : in STD_LOGIC_VECTOR ( 5 downto 0 ); ??命令,輸入
ram_data_i : in STD_LOGIC_VECTOR ( 7 downto 0 ); ??ram_data,輸入
cy_i : in STD_LOGIC_VECTOR ( 1 downto 0 ); ??cy,輸入
result_b_o : out STD_LOGIC_VECTOR ( 7 downto 0 ); ??結果b,輸出
result_a_o : out STD_LOGIC_VECTOR ( 7 downto 0 ) -?結果a,輸出
);
end component;
??定義函數
-----------------------------------------------------------------------------
--
-- IMAGE - Convert a special data type to string --
--
This function uses the STD.TEXTIO.WRITE procedure to convert different --
-- VHDL data types to a string to be able to output the information via --
-- a report statement to the simulator. --
-- (VHDL'93 provides a dedicated predefinded attribute 'IMAGE) --
-----------------------------------------------------------------------------
??定義了一個函數IMAGE,之所以有兩個定義,是因為對IMAGE進行了重載,一個是把
??time變數轉換成string,另一個是把integer變數轉換成string
function IMAGE (constant tme : time) return string is
variable v_line : line;
variable v_tme : string(1 to 20) := (others => ' ');
begin
write(v_line, tme);
v_tme(v_line.all'range) := v_line.all;
deallocate(v_line);
return v_tme;
end IMAGE;
function IMAGE (constant nmbr : integer) return string is
variable v_line : line;
variable v_nmbr : string(1 to 11) := (others => ' ');
begin
write(v_line, nmbr);
v_nmbr(v_line.all'range) := v_line.all;
deallocate(v_line);
return v_nmbr;
end IMAGE;
??定義過程,它產生所有的測試輸入數據並將產生結果和期望值進行比較,如果有誤
??產生,模擬將停止並輸出一個錯誤信息。注意到testbench要產生被測試單元的輸入
??信號,因此testbench的輸出接到被測試單元的輸入,被測試單元的輸出接到
??testbench的輸入。
procedure PROC_DIV_ACC_RAM (
constant DWIDTH : in positive;
constant PROP_DELAY : in time;
signal s_cyi : in std_logic_vector;
signal s_ovi : in std_logic;
signal s_qutnt : in std_logic_vector;
signal s_rmndr : in std_logic_vector;
signal s_cyo : out std_logic_vector;
signal s_ovo : out std_logic;
signal s_dvdnd : out std_logic_vector;
signal s_dvsor : out std_logic_vector;
signal s_dvdr_end : out boolean) is
variable v_quot : integer;
variable v_remd : integer;
variable v_flags : std_logic_vector((DWIDTH-1)/4+1 downto 0);
begin
s_dvdr_end <= false;
for j in 0 to 2**DWIDTH-1 loop
s_dvdnd <= conv_std_logic_vector(j,DWIDTH); ??產生被除數
for i in 0 to 2**DWIDTH-1 loop
s_dvsor <= conv_std_logic_vector(i,DWIDTH); ??產生除數
for f in 0 to 2**(((DWIDTH-1)/4)+2)-1 loop ??產生cy和ov
v_flags := conv_std_logic_vector(f,((DWIDTH-1)/4)+2);
s_cyo <= v_flags(((DWIDTH-1)/4) downto 0); ??s_cyo和s_ovo輸出到
s_ovo <= v_flags(v_flags'HIGH); ??mc8051_alu的cy_i和ov_i
wait for PROP_DELAY; ??等待100ns
if i /= 0 then
v_quot := j/i; ??產生期待的商
v_remd := j rem i; ??產生期待的餘數
assert (s_cyi((DWIDTH-1)/4) = '0') ??對結果進行比較
and (s_ovi = '0')
and (s_qutnt = conv_std_logic_vector(v_quot,DWIDTH))
and (s_rmndr = conv_std_logic_vector(v_remd,DWIDTH))
report "ERROR in division!"
severity failure;
else ?? 否則除數為0
assert (s_cyi((DWIDTH-1)/4) = '0')
and (s_ovi = '1')
report "ERROR in division by zero - flags not correct!"
severity failure;
end if;
end loop; -- f
end loop; -- i
end loop; -- j
assert false ?? 程序執行到這裡表示沒有問題產生
report "********* " & IMAGE(DWIDTH) & "BIT DIVIDER SEQUENCE FINISHED AT "
& IMAGE(now) & " !" & " *********"
severity note;
s_dvdr_end <= true;
wait;
end PROC_DIV_ACC_RAM;
-----------------------------------------------------------------------------
??定義常數和信號
constant PROP_DELAY : time := 100 ns;
signal rom_data_DIV_ACC_RAM : std_logic_vector(7 downto 0);
signal ram_data_DIV_ACC_RAM : std_logic_vector(7 downto 0);
signal acc_DIV_ACC_RAM : std_logic_vector(7 downto 0);
signal hlp_DIV_ACC_RAM : std_logic_vector(7 downto 0);
signal cmd_DIV_ACC_RAM : std_logic_vector(5 downto 0);
signal cy_DIV_ACC_RAM : std_logic_vector(1 downto 0);
signal ov_DIV_ACC_RAM : std_logic;
signal new_cy_DIV_ACC_RAM : std_logic_vector(1 downto 0);
signal new_ov_DIV_ACC_RAM : std_logic;
signal result_a_DIV_ACC_RAM : std_logic_vector(7 downto 0);
signal result_b_DIV_ACC_RAM : std_logic_vector(7 downto 0);
signal end_DIV_ACC_RAM : boolean;
-----------------------------------------------------------------------------
begin
-----------------------------------------------------------------------------
-- Test the DIV_ACC_RAM command --
-----------------------------------------------------------------------------
rom_data_DIV_ACC_RAM <= conv_std_logic_vector(0,8);
cmd_DIV_ACC_RAM <= conv_std_logic_vector(43,6); ??43表示除法命令
UUT : mc8051_alu ??例化元件
port map (
rom_data_i => rom_data_DIV_ACC_RAM(7 downto 0),
ram_data_i => ram_data_DIV_ACC_RAM(7 downto 0),
acc_i => acc_DIV_ACC_RAM(7 downto 0),
cmd_i => cmd_DIV_ACC_RAM,
cy_i => cy_DIV_ACC_RAM(1 downto 0),
ov_i => ov_DIV_ACC_RAM,
new_cy_o => new_cy_DIV_ACC_RAM(1 downto 0),
new_ov_o => new_ov_DIV_ACC_RAM,
result_a_o => result_a_DIV_ACC_RAM(7 downto 0),
result_b_o => result_b_DIV_ACC_RAM(7 downto 0));
??mc8051_alu的被除數、除數、cyi、ovi由PROC_DIV_ACC_RAM產生
??注意到PROC_DIV_ACC_RAM的商(s_qutnt)、餘數(s_rmndr)的Port方向是in,
??用來連接mc8051_alu的輸出result_a_DIV_ACC_RAM和result_b_DIV_ACC_RAM
PROC_DIV_ACC_RAM (DWIDTH => 8, ??調用過程
PROP_DELAY => PROP_DELAY,
s_cyi => new_cy_DIV_ACC_RAM(1 downto 0),
s_ovi => new_ov_DIV_ACC_RAM,
s_qutnt => result_a_DIV_ACC_RAM(7 downto 0), ??
s_rmndr => result_b_DIV_ACC_RAM(7 downto 0), ??餘數
s_cyo => cy_DIV_ACC_RAM(1 downto 0),
s_ovo => ov_DIV_ACC_RAM,
s_dvdnd => acc_DIV_ACC_RAM(7 downto 0), ??被除數
s_dvsor => ram_data_DIV_ACC_RAM(7 downto 0), ??除數
s_dvdr_end => end_DIV_ACC_RAM);
-----------------------------------------------------------------------------
end TBX_ARCH_DIV;
??配置,表示TBX_mc8051_alu這個entity使用TBX_ARCH_DIV這個architecture
configuration TBX_CFG_mc8051_alu_TBX_ARCH_DIV of TBX_mc8051_alu is
for TBX_ARCH_DIV
end for;
end TBX_CFG_mc8051_alu_TBX_ARCH_DIV;


[admin via 研發互助社區 ] 怎樣寫testbench已經有3677次圍觀

http://cocdig.com/docs/show-post-43047.html