【FPGA篇章七】FPGA系統任務:詳述常用的一些系統函數以及使用方法


歡迎大家關注我的微信公眾賬號,支持程序媛寫出更多優秀的文章

 

 

系統任務和系統函數是Verilog標准的一部分,都以字符"$"為開頭。系統任務可划分為六類,下面分別給出一些常用任務的用法。

 

1 顯示任務

  1.1 display和write任務

  向終端或文件寫入值時,系統會自動決定表達式參數值的位置大小。

  比如一個16bit大小的數,用十六進制需要4個字符寬度(最大FFFF),用十進制需要5個字符寬度(最大65535)。

  如果在%和radix間加一個0,可以取消這種自動決定的機制,看下面的示例:

1 reg [15:0] data = 200;
2 initial $display("data: %d.", data);    // data:   200.  數據占5個字符
3 initial $display("data: %0d.", data);   // data: 200.   數據占3個字符

  1.2 strobe監控

  $strobe使用的參數(包括所有轉義字符、格式控制)和$display完全相同。  

  這組系統任務$strobe、$strobeb、$strobeo、$strobeh可以在選擇的時間點處顯示仿真數據。

  當前仿真時刻的其它所有語句、事件執行完后,$strobe系統任務才會執行,以確保顯示的是正確的數據。示例如下:

1 reg [15:0] cnt = 0;
2 always @ (posedge clk) cnt = $random;
3 
4 initial begin
5     forever @(posedge clk) 
6         $strobe("At time %d, data is %d", $time, cnt);
7 end

  終端打印的信息如下:

1 At time                   10, data is 13604
2 At time                   30, data is 24193
3 At time                   50, data is 54793
4 At time                   70, data is 22115
5 ......

  1.3 連續監控

  $monitor使用的參數(包括所有轉義字符、格式控制)和$display完全相同。

  當$monitor任務的參數列表中有一個或多個參數的值發生變化時(不包括time、time、time、stime和$realtime),打印信息。這個連續地監控參數值的特性,稱作連續監控。

  $monitoron 和 $monitoroff用於啟用或禁用監控過程,相當於打開或關閉監控標志。打開監控標志時,無論參數值是否發生變化,會立即打印一次信息關閉監控標志后,監控任務處於待機狀態,不會執行。默認情況下, 仿真開始的時候會自動打開監控標志。

2 文件I/O任務和函數

  Verilog文件操作涉及到的函數也放在這里。

  2.1 打開文件和關閉文件

  打開文件任務 $fopen 和關閉文件任務 $fclose 的使用方法如下:

1     mcd = $fopen("file_name");
2     fd = $fopen("file_name", type);
3     fclose(mcd);
4     fclose(fd);

  $fopen的輸入參數都是字符串。如果沒有指定type參數,返回值稱作多通道描述符(MCD);如果設置了type參數,設定打開文件的方式,返回值稱作文件描述符(FD)

  MCD和FD都是32bit的數,二者的區別大致如下:

  MCD:32bit,最高位保留不用,剩下的每bit代表一個打開的文件(置1),因此同時最多只能打開31個文件。LSB被“標准輸出”文件占用。

    這種方法的優勢是可以將多個MCD用按位或的方式組合在一起,從而同時向多個文件寫入內容。這種打開方式相當於是FD的"w"方式,不支持讀取文件內容。

  FD:32bit,最高位保留不用,恆定為1。三個默認打開的文件會占用三個文件描述符:STDIN、STDOUT、STDERR。

    這種方法的優勢是可以指定打開文件的方式,不過不支持用按位或的方式組合FD,因此不能同時寫入多個文件。

  

  $fopen支持的文件打開方式如下表所示,支持的方式與在C語言種的含義相

type參數  功能
r / rb 只讀,文件指針指向文件頭(不刪除文件已有內容);文件不存在則返回0
r+ / r+b / rb+ 可讀可寫,文件指針指向文件頭(刪除文件已有內容);文件不存在則返回0
w / wb 只寫,文件指針指向文件頭(刪除文件已有內容);文件不存在則嘗試創建
w+ / w+b / wb+ 可讀可寫,文件指針指向文件頭(刪除文件已有內容);文件不存在則嘗試創建
a / ab 只寫,文件指針指向文件末尾(不刪除文件已有內容);文件不存在則嘗試創建
a+ / a+b/ ab+ 讀寫方式打開,文件指針指向文件末尾(不刪除文件已有內容);文件不存在則嘗試創建

  $fclose通過MCD或FD關閉了文件后,則不能再對此文件進行讀、寫操作,對此文件的fmonitor和fmonitor和fmonitor和fstrobe操作同時也會取消。相應的資源也會釋放出來,用於打開其它文件。

  2.2 文件輸出  

  $fdisplay、$fwrite、$fstrobe、$fmonitor

  顯示任務的信息輸出對象為“標准輸出”,在Vivado中也就是Tcl控制台;加了"f"后,文件輸出任務的信息輸出對象為“文件”

  唯一區別在於:文件輸出任務的第一個參數是要寫入文件的MCD或FD

  此外也沒有與monitoron和monitoron和monitoron和monitoroff對應的任務。

  如果要關閉fmonitor或fmonitor或fmonitor或fstrobe任務的監控,需要用$fclose關閉文件來實現

  下面給出一個示例代碼,展示了MCD和FD的區別,以及如果通過按位或操作同時執行多個MCD的文件輸出:

 1 reg [31:0] fd1, fd2, fd3;
 2 reg [31:0] mcd1, mcd2, mcd3;
 3 reg [31:0] file_output;
 4 initial begin
 5     // MCD FILE OPEN
 6     mcd1 = $fopen("test1.txt");
 7         if (mcd1 == 0) begin $display("mcd open1 failed!"); $finish; end
 8     mcd2 = $fopen("test2.txt");
 9         if (mcd2 == 0) begin $display("mcd open2 failed!"); $finish; end
10     mcd3 = $fopen("test3.txt");
11         if (mcd3 == 0) begin $display("mcd open3 failed!"); $finish; end
12     // 同時寫入3個文件    
13     file_output = mcd1 | mcd2 | mcd3 | 1;
14     $fdisplay(file_output,"mcd1: %h, mcd2: %h, mcd3: %h", mcd1, mcd2, mcd3);
15     $fclose(mcd1); $fclose(mcd2); $fclose(mcd3);
16     
17     // FD FILE OPEN
18     fd1 = $fopen("test4.txt", "r+"); 
19     fd2 = $fopen("test5.txt", "r+"); 
20     fd3 = $fopen("test6.txt", "r+"); 
21     $display("fd1: %b, fd2: %b, fd3: %b", fd1, fd2, fd3);
22     $fclose(fd1); $fclose(fd2); $fclose(fd3);
23 end

  使用按位或運算符將多個MCD合並在一起,1(即32bit中的LSB)表示標准輸出,在Vivado中是Tcl控制台。執行$fdisplay會向三個文件和Tcl控制台同時輸出信息。

  終端打印信息如下,MCD和FD的值與前面所說的它們的特點相吻合:

1 mcd1: 00000002, mcd2: 00000004, mcd3: 00000008
2 fd1: ffffb1e0, fd2: ffffb1e1, fd3: ffffb1e2

 

  2.3 數據轉換為字符串

  swrite 任務(包括swriteb、swriteo、swriteo、swriteo、swriteh)和 $sformat 任務可以將數據以格式化的形式轉換為字符串,存儲到reg型變量中。

  它們的使用方法和 $fwrite 完全一樣,只是第一個參數不是要輸出的文件FD或MCD,而是要輸出的reg型變量的名稱。

 

  這兩個任務的功能和C語言stdio.h庫中sprintf函數的功能類似。這兩個的任務的區別在於:

    $swrite:和其它任務一樣,格式控制可以放在參數列表的不同參數中
    $sformat:格式控制只能放在第二個參數中,其余參數全部被視作輸出項(和stdio庫的sprintf函數用法完全相同)。

  下面給出一個示例代碼,試圖將128和351兩個數字拼接為字符串存放在reg變量中:

 1 reg [6*8:1] str_reg1, str_reg2;
 2 reg [8:0] str1 = 128, str2 = 351;
 3 initial begin
 4     // 格式控制放在不同的參數中
 5     $swrite(str_reg1,"%0d","%0d",str1,str2);//"%0d",str2);
 6     $display("The value of str_reg1 is : %s", str_reg1); 
 7     
 8     // 格式控制只能放在第二個參數中
 9     $sformat(str_reg2,"%0d","%0d",str1,str2);//"%0d",str2);
10     $display("The value of str_reg2 is : %s", str_reg2); 
11 end

  終端打印信息如下:

1 The value of str_reg1 is : 128351
2 ERROR: No Format provided for this argument
3 ERROR: No Format provided for this argument
4 The value of str_reg2 is : 437220

  兩個任務都采用將格式控制"%d"放在兩個參數內的形式。swrite正確的得到了字符串"128351";sformat得到的結果錯誤,且提示了兩次ERROR信息。這正是因為$format只將第二個參數"%d"視作格式控制,其余三個參數視作輸出項,導致錯誤發生。

  2.4 讀取數據到內存中

  $readmemb 和 $readmemh 可以批量地把文本文件中的數據讀入到內存中,是一種快速的文件讀取方法,無需打開文件、關閉文件等操作。

  讀取的文本文件只能包含以下內容:

    空白區:空格、換行、TAB、跳頁;

    注釋:支持所有類型的注釋。使用注釋和空白區的目的是為了分割不同的數字;    

    二進制或十六進制數字:readmemb用於讀取二進制數據,readmemh用於讀取十六進制數據。

  兩個任務的使用方法如下:

  假設先定義一個有256個地址的字節存儲器 memory_name

  reg [7:0] memory_name[1:256];

1     $readmemb/h("file_name.data", memory_name);
2     $readmemb/h("file_name.data", memory_name, start_addr);
3     $readmemb/h("file_name.data", memory_name, start_addr, final_addr);

  第一條語句在仿真時刻為0時,將裝載數據到以地址是1的存儲器單元為起始存放單元的存儲器中去;

  第二條語句將裝載數據到以單元地址是 start_addr 的存儲器單元為起始存放單元的存儲器中去,一直到地址是256的單元為止;

  第三條語句將從地址是 start_addr 的單元開始裝載數據,一直到地址為 final_addr的單元。

  

  內存地址也可以在文本文件中定義,使用" @hhhh ",即@符號+十六進制形式的地址數據。這樣牽涉到的情況比較復雜,下面以示例的形式說明:

 1 // test1:正常讀取
 2 
 3 調用任務:$readmemh("../test5.txt");
 4 文本內容:A5A5 1234 8BCD 5869 2386
 5 內存內容:a5a5,1234,8bcd,5869,2386,
 6 
 7 // test2:調用任務時指定內存地址 => 按地址遞增順序存放在指定的地址范圍內
 8 
 9 調用任務:$readmemh("../test5.txt", data, 2, 4);
10 文本內容:A5A5 1234 8BCD 5869 2386
11 內存內容:xxxx,xxxx,a5a5,1234,8bcd,
12 
13 // test3:調用任務時指定內存地址,起始地址小於結束地址 => 按地址遞減順序存放
14 
15 調用任務:$readmemh("../test5.txt", data, 4, 2);
16 文本內容:A5A5 1234 8BCD 5869 2386
17 內存內容:xxxx,xxxx,8bcd,1234,a5a5,
18 
19 // test4:文件內指定內存地址+注釋 => 忽略注釋的數據
20 
21 調用任務:$readmemh("../test5.txt");
22 文本內容:@2 A5A5 @3 1234 //8BCD 5869 2386
23 內存內容:xxxx,xxxx,a5a5,1234,xxxx,
24 
25 // test5:文件內指定內存地址,地址隨機順序 => 數據分別存放到指定位置
26 
27 調用任務:$readmemh("../test5.txt");
28 文本內容:@2 A5A5 @1 1234 @4 8BCD //5869 2386
29 內存內容:xxxx,1234,a5a5,xxxx,8bcd,
30 
31 // test6:調用任務和文件內都指定了地址 => 文件內的地址必須在任務參數地址的范圍內
32 
33 調用任務:$readmemh("../test5.txt");
34 文本內容:@2 A5A5 @1 1234 @4 8BCD //5869 2386
35 內存內容:xxxx,xxxx,a5a5,xxxx,xxxx,
36 報錯:ERROR: Out of bounds address specified in datafile. Read terminated.

 

  2.5 隨機數random

  $random函數用於生成隨機數,每次調用時返回一個新的32bit隨機數(帶符號整數)。該函數有一個可選的參數(reg、integer或time類型),表示隨機種子。下面給出兩個例子:

1 // example1: 產生(-b+1)到(b-1)之間的隨機數
2 reg [23:0] rand;
3 rand = $random % b;
4 
5 // example2: 產生0到b-1之間的隨機數
6 reg [23:0] rand;
7 rand = {$random} % b;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM