1. 前言
本文記錄CPU驗(yàn)證環(huán)境中一些基本但重要的機(jī)制,包括驗(yàn)證環(huán)境中的編譯、CPU啟動(dòng)配置、Tube機(jī)制和Testcase結(jié)束機(jī)制。
2. 編譯
CPU驗(yàn)證環(huán)境編譯分為軟件編譯和硬件編譯。因?yàn)镃PU的Core會(huì)去memory中取指令去執(zhí)行,而這些指令需要我們提前生成好,并后門放在memory中取,這些指令的集合通常使用hex格式的文件存儲(chǔ),軟件編譯就是將C語(yǔ)言或匯編語(yǔ)言轉(zhuǎn)換為二進(jìn)制指令碼,并存儲(chǔ)在hex格式文件中。硬件編譯是將RTL和UVM驗(yàn)證環(huán)境一同編譯起來,形成一個(gè)驗(yàn)證環(huán)境的骨架,軟件編譯的hex文件依托于硬件編譯產(chǎn)生的平臺(tái)。編譯環(huán)境如圖1所示。
圖1 編譯環(huán)境
2.1 軟件編譯
軟件編譯通常使用makefile來管理的。makefile調(diào)用各CPU的子makefile去生成各個(gè)CPU的hex文件。步驟如圖2所示。
圖2 軟件編譯流程
確定軟件編譯環(huán)境(編譯器、編譯選項(xiàng)、編譯文件和目錄)
將文件編譯成.o格式的文件
確定link方式(link器、link選項(xiàng)、規(guī)則、文件)
將.o文件link成elf文件
將elf文件轉(zhuǎn)成hex文件
2.2 硬件編譯
硬件編譯就是使用Xcelium/Questasim/VCS對(duì)SystemVerilog代碼進(jìn)行編譯、細(xì)分和開啟仿真的過程。在如今普遍使用UVM的環(huán)境中,會(huì)形成一個(gè)UVM樹,使用uvm_test來啟動(dòng)仿真。
在仿真開始時(shí),驗(yàn)證環(huán)境會(huì)使用后門加載軟件編譯的hex文件到對(duì)應(yīng)的DDR/SRAM/ROM里。根據(jù)加載到memory類型的不同,通常還有不同的方式:
如果memory是真實(shí)RTL,可以使用SystemVerilog語(yǔ)法的$readmemh將hex數(shù)據(jù)從后門填充到真實(shí)DDR/RAM/ROM里。
如果memory是虛擬RAM,通常用虛擬RAM提供的函數(shù)將hex數(shù)據(jù)從后門填充進(jìn)去。
3. CPU啟動(dòng)配置
3.1 確定PC初始值
CPU啟動(dòng)最關(guān)鍵的要確定PC值,PC值決定了CPU從哪里開始取值并執(zhí)行。
對(duì)于ARM Cortex-A來說,初始PC是放在RVBAR(Reset Vector Base Address Register
)寄存器中,RVBAR寄存器是RO類型的,也就是實(shí)現(xiàn)就定義好的了。在IP驗(yàn)證中,通常force這個(gè)寄存器的值來控制CPU啟動(dòng)PC值。在SoC驗(yàn)證中,如果RVBAR為0,那么會(huì)提前在0地址處放置跳轉(zhuǎn)指令(BL),將Cortex-A CPU的PC跳轉(zhuǎn)到真正的啟動(dòng)地址去執(zhí)行。
對(duì)于ARM Cortex-M來說,沒有RVBAR寄存器,在復(fù)位后會(huì)查詢中斷向量表并進(jìn)入reset中斷函數(shù)執(zhí)行,因此在reset中斷函數(shù)里可以控制PC跳轉(zhuǎn)到真正的啟動(dòng)地址。中斷向量表一般存放在ROM中,Cortex-M CPU在boot的過程中可以讀取它的內(nèi)容。
3.2 Cortex-A啟動(dòng)流程
Cortex-A啟動(dòng)流程通常按以下步驟進(jìn)行:
把通用寄存器值、堆棧指針、link指針和狀態(tài)寄存器全部初始化為0;
關(guān)掉Trap功能;
設(shè)置VBAR(Vector Base Address Register),使它指向中斷向量表;
初始化堆棧指針,內(nèi)容來自scatter file指定的;
使能NEON和初始化相關(guān)寄存器為0;(包括使能其它想要打開的特性)
創(chuàng)建頁(yè)表,配置TTBR/TCR/MAIR等;
使能cache和MMU;
使能中斷;
進(jìn)入main主程序,完成主要操作;
進(jìn)入WFI;
通常步驟1~8以及步驟10在CPU驗(yàn)證中是固定的流程,不需要反復(fù)寫,大部分驗(yàn)證人員只需要在步驟9的main()函數(shù)中完成想要驗(yàn)證的功能。
3.3 Cortex-M啟動(dòng)流程
Cortex-M啟動(dòng)流程通常按以下步驟進(jìn)行:
復(fù)位后,進(jìn)入reset_handler,初始化通用寄存器為0;
調(diào)用系統(tǒng)初始化函數(shù)去配置UART、配置MPU等等;
進(jìn)入C庫(kù)的啟動(dòng)函數(shù)__main,完成一些初始化工作,像設(shè)置棧指針、初始化全局變量等,之后調(diào)用用戶編寫的main()函數(shù);
進(jìn)入main主程序,完成主要操作;
進(jìn)入WFI;
類似Cortex-A,步驟1~4以及步驟5在CPU驗(yàn)證中是固定的流程,不需要反復(fù)寫,大部分驗(yàn)證人員只需要在步驟4的main()函數(shù)中完成想要驗(yàn)證的功能。
4. Tube機(jī)制
SystemVerilog代碼可以使用$display()等內(nèi)置的系統(tǒng)函數(shù)很方便的將信息打印到log上,但是C語(yǔ)言就不好弄。
常用的方法是Tube機(jī)制,把將要打印的字符串寫到UART口上,UART上對(duì)接的組件會(huì)一直監(jiān)控出現(xiàn)在UART口上的二進(jìn)制數(shù)據(jù),并把它們轉(zhuǎn)成ASIIC碼,然后調(diào)用SystemVerilog的$display()函數(shù)打印到log上。
總得來說,還是借用了SystemVerilog的系統(tǒng)函數(shù)來打印的。
5. Testcase結(jié)束機(jī)制
對(duì)于只使用SystemVerilog和UVM環(huán)境的驗(yàn)證同學(xué)可能有疑問,Testcase結(jié)束不就很簡(jiǎn)單嗎,直接調(diào)用$stop或drop_objection就ok了。但對(duì)于包含CPU運(yùn)行的環(huán)境來說就不一樣了,CPU執(zhí)行的是C語(yǔ)言,從Memory中取值執(zhí)行,驗(yàn)證環(huán)境不好監(jiān)控它是否執(zhí)行完畢,這就需要CPU發(fā)出一些可以被驗(yàn)證環(huán)境捕捉到的信號(hào),用于表示CPU執(zhí)行的狀態(tài)。
一種常用的方法是CPU在執(zhí)行完之后,往UART口寫某些特定字符。UART口連接的監(jiān)控組件如果解析到這些字符,將字符含義傳達(dá)給驗(yàn)證環(huán)境。
同理,如果有多個(gè)CPU,那么需要UART監(jiān)控多個(gè)CPU都往UART寫完某些字符后,再使用event或全局變量等觸發(fā)一個(gè)事件,UVM等到這個(gè)事件,再執(zhí)行相應(yīng)動(dòng)作。