$unit编译单元声明
$unit編譯單元聲明
SystemVerilog含有編譯單元。
相比Verilog,SystemVerilog增加了編譯單元的概念。編譯單元是同時(shí)編譯的所有源文件。編譯單元為軟件工具提供了一種對(duì)整個(gè)設(shè)計(jì)子塊單獨(dú)編譯的方法。一個(gè)子塊可能包含第一個(gè)或多個(gè)模塊(module)。這下模塊可能包含在一個(gè)或多個(gè)文件中。設(shè)計(jì)的子塊可能還包含接口塊和測(cè)試程序塊。
編譯單元域包含外部聲明。
SystemVerilog允許在包、模塊、接口和程序塊的外部進(jìn)行聲明擴(kuò)展Verilog的聲明域。這些外部聲明在“編譯單元域”中,并且對(duì)所有同時(shí)編譯的模塊都是可見的。
編譯單元域可以包含:
(1)時(shí)間單元和精度聲明
(2)變量聲明
(3)net聲明
(4)常量聲明
(5)用戶定義數(shù)據(jù)類型,使用typedef、enum、或class
(6)任務(wù)和函數(shù)定義
編譯單元域中的外部聲明(不可綜合)
///外部聲明/ parameter VERSION = "1.2a"; //外部常量 reg resetN = 1; //外部變量(低有效) typedef struct packes{ //外部用戶定義類型reg [31:0] address;reg [31:0] data;reg [31:0] opcode; }instruction_word_t;function automatic int log2 (input int n);//外部函數(shù)if (n <= 1) return (1);log2 = 0;while( n > 1)beginn = n/2;log2++;endreturn(log2); endfunction/模塊定義 //用外部聲明定義端口類型 module register(output instruction_word_t q,input instruction_word_t d,input wire clock );always @(posedge clock ,negedge resetN)if(!resetN) q <= 0;//使用外部復(fù)位else q <= d; endmodule 注意:外部編譯單元域聲明不是全局的編譯單元域中的聲明與全局聲明不一樣。真正的全局聲明,如全局變量或函數(shù),不管源文件是單獨(dú)還是同時(shí)編譯,都會(huì)被設(shè)計(jì)的所有模塊共享。
SystemVerilog的編譯單元域只作用于同時(shí)編譯的源文件。每次編譯源文件,就創(chuàng)建一個(gè)唯一僅針對(duì)此次編譯的編譯單元域。例如,如果模塊CPU和模塊controller都引用外部聲明的變量regset,那么就可能存才兩種假設(shè):
(1)如果兩個(gè)模塊同時(shí)編譯,將只要一個(gè)編譯單元域。外部聲明的reset變量將被這兩個(gè)模塊共用。
(2)如果兩個(gè)模塊分別編譯,將會(huì)有兩個(gè)編譯單元域,可能有兩個(gè)不同的reset變量。
在后一種假設(shè)下,包含外部reset聲明的編譯看上去沒有問(wèn)題。另一個(gè)文件在單獨(dú)編譯時(shí)會(huì)有自己唯一的$unit編譯域,不會(huì)看到前一個(gè)編譯中的reset聲明。取決于reset使用的環(huán)境,第二個(gè)編譯可能會(huì)由于未聲明變量而失敗,也可能使reset成為隱式net而編譯成功。這種可能性是很危險(xiǎn)的。如果第二個(gè)編譯通過(guò)使reset成為隱式net而成功,那么現(xiàn)在就會(huì)有兩個(gè)叫做reset的信號(hào),每個(gè)編譯中一個(gè)。這兩個(gè)不同的reset信號(hào)用什么方式都不能連在一起.
$unit只能用于導(dǎo)入包
(1)不要在 $unit空間進(jìn)行任何聲明!所有的聲明都要在命名包內(nèi)進(jìn)行。
(2)必要時(shí)可以將包導(dǎo)入到 $unit中。這在模塊或接口的多個(gè)端口使用用戶自定義類型,而這個(gè)類型定義又在包中時(shí)非常有用。
在 $unit編譯單元域中直接聲明對(duì)象會(huì)在文件單獨(dú)編譯時(shí)導(dǎo)致設(shè)計(jì)出錯(cuò)。如果聲明分散在多個(gè)文件中,還會(huì)產(chǎn)生spaghetti代碼,難以維護(hù)、重用或調(diào)試聲明錯(cuò)位。(spaghetti代碼指結(jié)構(gòu)混亂、邏輯性差、難于調(diào)試的代碼)
SystemVerilog標(biāo)識(shí)符搜索規(guī)則
編譯單元域中的聲明可以在組成編譯單元的模塊的任何層次引用。
SystemVerilog定義了簡(jiǎn)單的搜索規(guī)則來(lái)引用標(biāo)識(shí)符:
(1)搜索那些按IEEE 1364Verilog標(biāo)準(zhǔn)定義的局部聲明
(2)搜索統(tǒng)配符導(dǎo)入到當(dāng)前作用域的包中的聲明
(3)搜索編譯單元域中是聲明
(4)搜索設(shè)計(jì)層次中的聲明,遵循IEEE1364Verilog搜索規(guī)則
編譯單元域中的變量和net
當(dāng)使用外部聲明時(shí)有一點(diǎn)需要著重考慮。Verilog支持隱式聲明,即在特定環(huán)境下,為聲明的標(biāo)識(shí)符假定為net類型(通常為wire類型)。當(dāng)從上下文不能推斷出隱式類型或者希望使用默認(rèn)net類型以外的類型時(shí),Verilog要求在標(biāo)識(shí)符引用之前要顯式聲明標(biāo)識(shí)符類型。
隱式類型聲明規(guī)則影響編譯單元域中的變量和net聲明。軟件工具必須在標(biāo)識(shí)符引用之前找到外部聲明。否則,這個(gè)名稱將被看做未聲明的標(biāo)識(shí)符并遵循Verilog隱式類型規(guī)則。
將包導(dǎo)入$unit的編碼原則
SystemVerilog允許將模塊端口聲明為用戶定義類型。
當(dāng)許多模塊端口都是用戶自定義類型時(shí),像上面那樣顯式地引用包中就會(huì)顯得繁瑣了。一種可選擇的風(fēng)格是在模塊聲明之前將包導(dǎo)入到$unit編譯單元域中。
//將包中特定子項(xiàng)導(dǎo)入到$unit中 import definitions::instruction_t;module ALU ( input definitions::instruction_t IW,input logic clock,output logic[31:0] result );包還可以通過(guò)通配符導(dǎo)入到$unit域中。注意通配符導(dǎo)入實(shí)際上不能導(dǎo)入包中所有子項(xiàng)。它只是將包加到SystemVerilog源路徑中。
//將包中特定子項(xiàng)導(dǎo)入到$unit中 import definitions::*;module ALU ( input definitions::instruction_t IW,input logic clock,output logic[31:0] result );使用單獨(dú)編譯將包導(dǎo)入到$unit中
文件順序編譯依賴性
當(dāng)子項(xiàng)從包中導(dǎo)入(包中特定子項(xiàng)導(dǎo)入或者是通配符導(dǎo)入)時(shí),import語(yǔ)句必須出現(xiàn)在包中子項(xiàng)被引用之前。如果包導(dǎo)入語(yǔ)句與模塊或接口不在同一文件中,而模塊和接口文件又要引用包中子項(xiàng),那么包含import文件必須在文件比編譯順序中首先列出來(lái)。如果文件順序不正確,模塊或接口的編譯要么失敗,要么因看不到包中子項(xiàng)而錯(cuò)誤地指定為隱式net。
多文件編譯與單文件編譯
可以讀入Verilog和SystemVerilog源代碼的綜合編譯器、代碼檢測(cè)器、一些仿真器和其他可能的工具通常可以一次編譯一個(gè)或多個(gè)文件。當(dāng)多個(gè)文件同時(shí)進(jìn)行編譯時(shí),只要一個(gè)$unit域。包導(dǎo)入(不管是包中特定子項(xiàng)導(dǎo)入或者是通配符導(dǎo)入)到 $unit域中使包中子項(xiàng)對(duì)導(dǎo)入語(yǔ)之后讀入的所有模塊和接口都可見。但是,如果這些文件單獨(dú)編譯,將會(huì)產(chǎn)生多個(gè)不同的 $unit編譯單元。一個(gè) $unit中導(dǎo)入的包對(duì)另一個(gè) $unit是不可見的。
在每個(gè)文件中使用導(dǎo)入語(yǔ)句
對(duì)于將包中子項(xiàng)導(dǎo)入到 $unit編譯單元域中出現(xiàn)的這兩個(gè)問(wèn)題,解決辦法就是將import語(yǔ)句放到每個(gè)文件模塊或接口定義之前。這個(gè)解決方法在每個(gè)文件單獨(dú)編譯時(shí)很奏效。但是,當(dāng)多個(gè)文件同時(shí)進(jìn)行編譯時(shí),還要當(dāng)心。將同樣的包中子項(xiàng)多次導(dǎo)入到同一 $unit域中是非法的。(這與在同一名稱空間中兩次聲明同一變量一樣是非法的)
用 $unit包導(dǎo)入的條件編譯
可以使用C語(yǔ)言常用的編程技巧,使我們?cè)趩蝹€(gè)文件編譯和多個(gè)文件編譯時(shí)都可以將包中子項(xiàng)導(dǎo)入到 $unit域中。這個(gè)技巧是利用條件編譯,使第一次遇到導(dǎo)入語(yǔ)句時(shí)將其編譯到 $unit中,而再次出現(xiàn)則不編譯這些語(yǔ)句。為了說(shuō)明導(dǎo)入語(yǔ)句在當(dāng)前 $unit域中是否已經(jīng)編譯完,在導(dǎo)入語(yǔ)句第一次編譯時(shí)設(shè)置`define標(biāo)志。
帶條件編譯的包
`ifndef DFFS_DONE //如果已編譯標(biāo)志沒設(shè)置`define DFFS_DONE //設(shè)置該標(biāo)志package definitions;paramter VERSION = "1.1";typedef enum{ADD,SUB,MUL} opcodes_t;typedef struct{logic [31:0] a,b;opcodes_t opcode;}instruction_t;function automatic [31:0] multiplier(input [31:0] a,b);//用戶定義的32位乘法return a*b; //抽象乘法(無(wú)錯(cuò)誤檢測(cè))endfunction endpackage import definitions::*; //將包導(dǎo)入到 $unit中`endif每個(gè)需要包中定義的設(shè)計(jì)或測(cè)試平臺(tái)都有應(yīng)該將
`include "definitions.pkg"放在文件的開始。當(dāng)設(shè)計(jì)或測(cè)試平臺(tái)文件被編譯時(shí),都會(huì)將包和導(dǎo)入語(yǔ)包含進(jìn)去。definitions.pkg文件中的條件編譯將保證如果包還沒被編譯和導(dǎo)入,就去完成這兩個(gè)任務(wù)。如果包已經(jīng)編譯并導(dǎo)入到當(dāng)前 $unit域中,該文件的編譯將被忽略。
注意:對(duì)于這種編碼風(fēng)格,包文件應(yīng)該用`include編譯命令間接傳遞給軟件工具編譯器。這種條件編譯風(fēng)格使用Verilog的`include命令把definitions.pkg文件當(dāng)作其他文件的一部分進(jìn)行編譯。這樣做時(shí)為了保證definitions.pkg文件末尾的導(dǎo)入語(yǔ)將包導(dǎo)入到設(shè)計(jì)或測(cè)試文件,編譯正在使用的同一 $unit域中。如果definitions.pkg文件直接出現(xiàn)在軟件工具編譯器的命令行中,包和導(dǎo)入語(yǔ)句就會(huì)被編譯到另一個(gè) $unit空間,而不是設(shè)計(jì)或測(cè)試平臺(tái)塊正在使用 $unit域中。
//包含條件編譯包文件的設(shè)計(jì)文件 `include "definitions.pkg" //編譯包文件module ALU ( input definitions::instruction_t IW,input logic clock,output logic[31:0] result ); always_ff @(posedge clock) begincase(IW.opcode)definitions::ADD : result = IW.a + IW.b;definitions::SUB : result = IW.a - IW.b;definitions::MUL : result = definitions::multiplier(IW.a,IW.b);endcase end endmodule //包含條件編譯包文件的測(cè)試平臺(tái)文件 `include "definitions.pkg" //編譯包文件module test;instruction_t test_word;logic [31:0] alu_out;logic clock = 0;ALU dut (.IW(test_word),.clock(clock ),. result(alu_out));always #10 clock = ~clock;initial begin@(posedge clock)test_word.a = 5;test_word.b =7;test_word.opcode = ADD;@(negedge clock)$display ("alu_out = %d (expected 12)", alu_out);$finish; end endmodule`include對(duì)單文件和多文件編譯
進(jìn)行單文件編譯時(shí),包將被編譯并導(dǎo)入到每個(gè) $unit編譯單元中。這就保證了每個(gè) $unit都能看到相同的包中子項(xiàng)。由于每個(gè) $unit都是唯一的,不會(huì)在多次編譯包的過(guò)程中出現(xiàn)名稱沖突。
進(jìn)行多文件編譯時(shí),條件保證包只進(jìn)行一次編譯并導(dǎo)入到所有模塊的公共 $unit編譯域中。不管設(shè)計(jì)或測(cè)試平臺(tái)文件那個(gè)先編譯,都會(huì)導(dǎo)入包,保證包中子項(xiàng)對(duì)所有文件都是可見的
包中變量是共享變量(不可綜合)
包中可以包含變量聲明。包中變量為所有導(dǎo)入變量的設(shè)計(jì)塊(和測(cè)試塊)共享。包中變量的行為在單文件編譯和多文件編譯中截然不同。在多文件編譯中,包被導(dǎo)入到同一 $unit編譯域中。每個(gè)設(shè)計(jì)塊或測(cè)試塊將看到相同的包中變量。一個(gè)塊寫到包中變量的值將對(duì)其他所有塊可見。在單文件編譯中,每個(gè) $unit域都有一個(gè)唯一的變量,它恰好與另一個(gè)不同的 $unit域中的變量同名。一個(gè)設(shè)計(jì)塊或測(cè)試塊對(duì)包中變量寫入的值對(duì)其他設(shè)計(jì)或測(cè)試塊不可見。
包中的靜態(tài)任務(wù)和函數(shù)是不可綜合
靜態(tài)任務(wù)和函數(shù)或者含靜態(tài)儲(chǔ)存的自動(dòng)任務(wù)和函數(shù),也具有同樣的潛在問(wèn)題。在多文件編譯中,只有一個(gè) $unit域,它將導(dǎo)入任務(wù)和函數(shù)的一個(gè)實(shí)例。任務(wù)或函數(shù)中的靜態(tài)儲(chǔ)存對(duì)所有設(shè)計(jì)和驗(yàn)證塊都是可見的。在單文件編譯中,每一個(gè)獨(dú)立的 $unit導(dǎo)入的任務(wù)或函數(shù)實(shí)例不同。任務(wù)或函數(shù)的靜態(tài)儲(chǔ)存不會(huì)在設(shè)計(jì)和測(cè)試塊中共享。
這個(gè)關(guān)于條件編譯導(dǎo)入語(yǔ)句到 $unit中的限制在可綜合的模型中沒有問(wèn)題,因?yàn)榫C合不支持包中的變量聲明、靜態(tài)任務(wù)和函數(shù)。
使用包而不用 $unit是更好的編碼風(fēng)格
可在編譯單元域(所有模塊和接口定義的外部)中聲明的可綜合結(jié)構(gòu)有
(1)typedef用戶定義類型定義
(2)自動(dòng)函數(shù)
(3)自動(dòng)任務(wù)
(4)parameter和localparam常量
(5)包導(dǎo)入
雖然在編譯單元域定義用戶定義類型不是一種好的風(fēng)格,但它可綜合的。更好的風(fēng)格是吧用戶定義類型的定義放在有名稱的包中,這可以降低出現(xiàn)spaghetti代碼和文件順序依懶性的風(fēng)險(xiǎn)。
外部任務(wù)和函數(shù)必須是自動(dòng)的
在 $unit編譯單元域中聲明任務(wù)和函數(shù)也不是一種好的編碼風(fēng)格。但是,在 $unit中定義的任務(wù)和函數(shù)是可綜合的。當(dāng)模塊引用編譯單元域中定義的任務(wù)和函數(shù)時(shí),綜合將復(fù)制該任務(wù)或函數(shù)代碼并把它看成在模塊中定義的一樣。為了綜合,編譯單元域中定義的任務(wù)和函數(shù)必須聲明為automatic,并且不能包含靜態(tài)變量。這還是因?yàn)樽詣?dòng)任何和函數(shù)的儲(chǔ)存區(qū)在每次調(diào)用時(shí)才會(huì)實(shí)際分配。因此,每個(gè)模塊引用的自動(dòng)任務(wù)或函數(shù)的引用的綜合前仿真行為與綜合后行為相同(綜合后任務(wù)或函數(shù)的功能已經(jīng)在模塊內(nèi)實(shí)現(xiàn)了)。
編譯單元域中定義的parameter常量不能重新定義,因?yàn)樗皇悄K實(shí)例的一部分。綜合會(huì)將編譯單元域中聲明的常量看作文本值。在 $unit域中聲明參數(shù)不是一種好的建模風(fēng)格,因?yàn)槌A柯暶魑募c模板文件分開編譯時(shí),這些常量對(duì)模塊是不可見的。
總結(jié)
以上是生活随笔為你收集整理的$unit编译单元声明的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: SystemVerilog声明的位置
- 下一篇: 2022 年 Q4 最缺工的 100 个