對一些數(shù)據(jù)處理的模塊進行調(diào)試仿真,模塊需要特定的數(shù)據(jù)輸入,比如單一頻率的正弦波;為了解決這個問題,我們可以用matlab,python等工具生成文本數(shù)據(jù),然后使用Verilog將數(shù)據(jù)讀取進來;Testbench可以使用2種方法進行文本數(shù)據(jù)操作
readmemb, readmemh, writememb, writememh操作
從字面意思理解,readmem是讀取數(shù)據(jù)到memory,后綴的b, h代表了數(shù)據(jù)的進制;同理,writemem是將memory的數(shù)據(jù)寫入到文件中;
所以,在使用這一類系統(tǒng)自帶函數(shù)時,首先要有一個memory類型的變量。定義方法如下:
reg [M-1:0] mem [N:1];
mem”變量“(應該叫寄存器組)有N個”一維“變量,每個”一維“變量的bit寬度為M;你可以將mem理解為C語言中的二維數(shù)組,里面包含了N個一維數(shù)組,每個一維數(shù)組有M個元素,元素為bit。
事實上,我們稱M為mem的數(shù)據(jù)寬度,N為mem的數(shù)據(jù)深度。
以readmemb為例,進行數(shù)據(jù)讀取操作
initial
begin
$readmemb("data.txt", mem);
end
readmemb的第一個參數(shù)為文件名,第二個參數(shù)為memory變量名;至此,data.txt內(nèi)部的N行數(shù)據(jù)存入了mem里。readmemh操作類似,不同的是data.txt的數(shù)據(jù)要求為16進制。
那么可能有人會有疑問了,假如有以下問題,mem存入的數(shù)據(jù)會是啥樣:
- data.txt每行的數(shù)據(jù)位寬小于M或者大于M
圖1 數(shù)據(jù)位寬小于M波形
圖2 數(shù)據(jù)位寬小于M存儲
圖3 數(shù)據(jù)位寬大于M
圖4 數(shù)據(jù)位寬大于M,VCS警告
經(jīng)過試驗,M大于數(shù)據(jù)位寬,數(shù)據(jù)可以正常讀取,高位補0;小于數(shù)據(jù)位寬,數(shù)據(jù)無法正常讀取。
- data.txt的數(shù)據(jù)不是二進制,或者不是純數(shù)字
與M小于數(shù)據(jù)位寬的情況一致,無法正常讀取數(shù)據(jù)。
- 如果data.txt的行數(shù)小于N或大于N
圖5 行數(shù)小于N
圖6 行數(shù)大于N
經(jīng)過試驗,行數(shù)大于N,仿真器會出警告,但數(shù)據(jù)可以正常讀取。小于N時,多余的部分memory的值為不定狀態(tài)。
數(shù)據(jù)存入mem但還沒有進入到模塊的輸入,接下來的操作可以參考下列代碼:
reg [M-1:0] data_in;
integer index = 1;
initial
begin
forever
begin
@(posegde clk);
data_in = mem[index];
index = (index >= N) ? 1 : index + 1;
end
end
代碼里面,等待clk的上升沿,然后將mem的index元素賦值給data_in,然后index完成加1操作;整個過程不斷循環(huán);這里設置了index計數(shù)到N返回1的計數(shù)保護,防止出現(xiàn)無效數(shù)據(jù)。
再將data_in與被測模塊的數(shù)據(jù)輸入端口相連,數(shù)據(jù)就送入進去了。
圖7 送入的數(shù)據(jù)波形
writememb的操作與readmemb反過來,將mem的數(shù)據(jù)存儲為文本操作如下:
initial
begin
$writememb("new_data_b.txt", mem);
$writememh("new_data_h.txt", mem);
end
存儲之后的
圖8 writememh
圖9 writememb
fscanf, fwrite等操作
Verilog本身的語法與C類似,其自身也有文本操作的函數(shù),也與C類似。使用Verilog對文本操作,首先需要進行如下操作:
integer fid;
initial
begin
fid = $fopen("data.txt", "r");
//fid = $fopen("data.txt", "w"); //write
if (!fid)
$display("file open error");
end
如同C語言中的fopen一樣,第一個參數(shù)為文件名,第二個參數(shù)為操作模式,包括讀(r, rb),寫(w, wb)等操作;根據(jù)返回值判斷文件操作是否有錯誤。
然后,根據(jù)文本文件的數(shù)據(jù)格式,進行數(shù)據(jù)讀取操作。
reg [M-1:0] data_in;
always @ (posedge clk)
$fscanf(fid, "%d %d %d", data_in, mem[0], mem[1]);
fscanf用法與C語言類似,文件句柄為第一個參數(shù),第二個參數(shù)為格式參數(shù),第三個為數(shù)據(jù)保存變量,但不需要加&了。讀取文件的時候第二個參數(shù)與第三個參數(shù)需要對應,否則數(shù)據(jù)讀取可能會出錯。(親身經(jīng)歷)
數(shù)據(jù)存儲操作如下,在前面fopen使用w模式下:
always @ (posegde clk)
$fwrite(fid, "%d, %d, %d\\n", $signed(data_in), $signed(data_in)+1, $signed(data_in)+2);
數(shù)據(jù)可以按照第二個參數(shù)的格式存儲進文本文件。還有一系列如fdisplay, 相對于fwrite, 它的文件寫入數(shù)據(jù)之后會自動到下一行,所以第二個參數(shù)不需要加入“\\n”;ftell等函數(shù)。
注意,想要存儲十進制的負數(shù),除了第二個參數(shù)用%d,第三個參數(shù)的寄存器變量還要使用$signed轉換為有符號數(shù)形式
圖10 正常情況文本操作讀取后存儲的數(shù)據(jù)
圖11 寄存器位寬小于數(shù)據(jù)位寬時,文本操作讀取后存儲的數(shù)據(jù)
注意,當存儲的寄存器位寬小于數(shù)據(jù)位寬時,數(shù)據(jù)會被自動截去高位保留低位。
之前使用文件操作存儲被測模塊的輸出時,每次文件的數(shù)據(jù)量(行數(shù))都與理想中的數(shù)目對不上,找各種原因,最后才發(fā)現(xiàn)自己犯了一個低級錯誤,沒有使用fclose關閉文件句柄。
initial
begin
#1000;
$fclose(fid);
$finish;
end
停止仿真前,一定要用fclose關閉文件句柄,否則數(shù)據(jù)存取會出現(xiàn)不可預知的問題。
歡迎使用本文使用的Testbench做實驗,注意,在windows下使用modelsim做實驗時,文件名必須是絕對路徑;tb中,使用"READMEM_ON"宏定義決定運行readmem或文件操作,可以嘗試修改宏定義的值改變文件操作的函數(shù)類型;本文使用資源在公眾號回復116獲取;
兩種方法差異對比
- readmem,writemem方法只能存取二進制或十六進制數(shù)據(jù),數(shù)據(jù)格式固定,對多維其他格式數(shù)據(jù)讀取不支持,沒有文件操作靈活;文本操作方便其他的工具,如matlab,python處理數(shù)據(jù)
- readmem是可綜合語句,所以可以用于對模塊內(nèi)的memory變量進行賦值,但其他語句是不可綜合語句,只能用于仿真測試中
- 文件操作雖然支持各種格式的文本存取,但是操作上沒有readmem, writemem簡單;假如數(shù)據(jù)需要循環(huán)使用,readmem讀取進memory之后,通過復位index就可以循環(huán)使用數(shù)據(jù),而文本操作就麻煩一些。
-
matlab
+關注
關注
185文章
2979瀏覽量
230723 -
寄存器
+關注
關注
31文章
5359瀏覽量
120795 -
正弦波
+關注
關注
11文章
647瀏覽量
55514 -
仿真器
+關注
關注
14文章
1019瀏覽量
83843 -
C語言
+關注
關注
180文章
7613瀏覽量
137247
發(fā)布評論請先 登錄
相關推薦
評論