Verilog可以使用parameter定義常量,它允許在模塊中定義可重用的參數(shù)。這些參數(shù)可以在模塊實(shí)例化時(shí)被覆蓋,從而使得模塊更加靈活和可配置。parameter通常用于定義諸如數(shù)據(jù)寬度、數(shù)組大小等需要在多個(gè)地方使用的值,以下為一個(gè)例子:
//可配置寬度的加法器模塊
module?adder #(parameter?WIDTH =?8) (
? ? input?wire?[WIDTH-1:0] a,
? ? input?wire?[WIDTH-1:0] b,
? ? output?reg?[WIDTH-1:0] sum);
always?@(*)
begin
? ? sum = a + b;end
endmodule
//測(cè)試模塊,實(shí)例化兩個(gè)不同寬度的加法器
module?test();
//實(shí)例化一個(gè)8位加法器(使用默認(rèn)參數(shù))
adder adder_8bit (
? ? .a(8'd5),
? ? .b(8'd10),
? ? .sum() //輸出連接到sum_8
? ? );
//實(shí)例化一個(gè)16位加法器,通過(guò)參數(shù)傳遞寬度
adder #(16) adder_16bit (
? ? .a(16'd100),
? ? .b(16'd200),
? ? .sum() //輸出連接到sum_16
? ? );
//通過(guò)名稱指定參數(shù),實(shí)例化一個(gè)12位加法器
adder #(.WIDTH(12)) adder_12bit (
? ? .a(12'd50),
? ? .b(12'd100),
? ? .sum() //輸出連接到sum_12
? ? );
endmodule
這里的加法器就帶一個(gè)可配置的參數(shù)WIDTH,就可以支持例化時(shí)候,通過(guò)傳參實(shí)現(xiàn)不同位寬的加法器。
我們以前學(xué)習(xí)基礎(chǔ)的C/C++,支持函數(shù)傳參、指針傳參,要么傳值、要么傳地址(指針),對(duì)于位寬這種結(jié)構(gòu)性的參數(shù)很難簡(jiǎn)單傳遞。比如我們常見(jiàn)的可變深度的數(shù)組,一般都不能使用通常的定義,而是定義一個(gè)指針p,通過(guò)執(zhí)行時(shí)動(dòng)態(tài)分配內(nèi)存的方式實(shí)現(xiàn),就像下面一樣:
int?myArray[100] = {0}; //定義一個(gè)100個(gè)元素的數(shù)組,這里面的100不能使用變量
//動(dòng)態(tài)分配內(nèi)存方法,定義一個(gè)深度可參數(shù)化的數(shù)組
int* p;
int num = 100;
p = new int[num]
...
delete[] p;
如果要讓systemc支持類似verilog這樣的位寬可參數(shù)化,就要用到相對(duì)高級(jí)一些的寫法,即template。template其實(shí)在1992年就開(kāi)始有編譯器部分支持(比如Borland C++3.1),直到1998年才作為正式標(biāo)準(zhǔn)(C++98)發(fā)布,2000年左右各大編譯器都基本完全支持。
現(xiàn)在使用template最好包含<type_traits>頭,下面是一個(gè)例子,用systemc描述可配置位寬的SRAM module:
#include?<systemc.h>
#include?<iostream>
#include?<iomanip>
#include?<algorithm>
#include?<cstdint>
#include?<sstream>
#include?<type_traits>
// SRAM模塊 - 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的內(nèi)存子系統(tǒng)
//采用template實(shí)現(xiàn)參數(shù)化內(nèi)存子系統(tǒng)
template<uint32_t?ADDR_WIDTH?=?10,uint32_t?DATA_WIDTH?=?32>
class?SRAM?: public?sc_module{
//參數(shù)有效性檢查
? ? static_assert(ADDR_WIDTH >?0?&&?ADDR_WIDTH <=?32, "ADDR_WIDTH must be between 1 and 32 bits");
? ? static_assert(DATA_WIDTH >?0?&&?DATA_WIDTH <=?64, "DATA_WIDTH must be between 1 and 64 bits");
? ? static_assert(DATA_WIDTH %?8?==?0, "DATA_WIDTH must be multiple of 8");
public:
? ? // ==========類型定義 ==========
? ? using?AddrType?=?sc_uint<ADDR_WIDTH>;
? ? using?DataType?=?sc_uint<DATA_WIDTH>;
? ? using?StorageType?=?typename?std::conditional<(DATA_WIDTH?<=?32), uint32_t, uint64_t>::type;
? ? // ==========容量計(jì)算 ==========
? ? static?constexpr?uint32_t?MEM_SIZE =?1UL?<<?ADDR_WIDTH;
? ? static?constexpr?uint32_t?BYTES_PER_WORD =?DATA_WIDTH /?8;
? ? // ==========輸入端口 ==========
? ? sc_in<bool>?? ? ?iClk;//時(shí)鐘輸入
? ? sc_in<bool>?? ? ?iRst_n;//復(fù)位輸入(低有效)
? ? sc_in<bool>?? ? ?iStb;//選通信號(hào)輸入
? ? sc_in<bool>?? ? ?iWe;//寫使能輸入
? ? sc_in<AddrType>??iAddr;//地址輸入
? ? sc_in<DataType>??iData;//數(shù)據(jù)輸入(寫入數(shù)據(jù))
? ? // ==========輸出端口 ==========
? ? sc_out<DataType>?oData;//數(shù)據(jù)輸出(讀取數(shù)據(jù))
? ? sc_out<bool>?? ? oAck;//應(yīng)答信號(hào)輸出
private:??
//實(shí)體單元,棧上構(gòu)建對(duì)象,減少內(nèi)存泄漏風(fēng)險(xiǎn)
? ? StorageType m_memory_cells[MEM_SIZE];
? ? // ========模擬硬件行為進(jìn)程 =========
//內(nèi)存訪問(wèn)處理進(jìn)程
? ? void?MemFunction();
public:??
? ? // ==========構(gòu)造函數(shù) ==========
? ? SRAM(sc_module_name?name);//,
? ? // ==========調(diào)試信息與功能 ==========
// SRAM容量,單位字
? ? const?uint32_t?mem_size_info =?MEM_SIZE;
//(調(diào)試接口)加載數(shù)據(jù)到內(nèi)存
? ? bool?LoadData(uint32_t?loadAddr, const?StorageType*?DataBuf,uint32_t?wordcount);
? ? //(調(diào)試接口)直接讀取內(nèi)存1個(gè)字
? ? StorageType?DirectRead(uint32_t?addr) const;
//(調(diào)試接口)直接寫入內(nèi)存1個(gè)字
? ? ?void?DirectWrite(uint32_t?addr, StorageType?data);
? ? //(調(diào)試接口)打印內(nèi)存內(nèi)容(用于調(diào)試)
? ? void?DumpMemory(uint32_t?startAddr, uint32_t?wordCount) const; ? ?
};
這里使用了module(類)定義與實(shí)現(xiàn)分離的形式,上面只列出了聲明部分。其中template<uint32_t?ADDR_WIDTH?=?10,uint32_t?DATA_WIDTH?=?32>聲明了ADDR_WIDTH、和DATA_WIDTH兩個(gè)參數(shù)是可變的,并且給出了兩者的默認(rèn)值,當(dāng)例化時(shí),將會(huì)傳遞參數(shù):
SRAM<12,32> SRAM_TEST;??????????//ADDR_WIDTH = 10,uint32_t DATA_WIDTH = 32
SRAM<> SRAM_TEST; ?????????????//使用默認(rèn)值,ADDR_WIDTH = 10,DATA_WIDTH = 32?
通過(guò)下面的代碼,定義了AddrType和DataType兩種類型,他們與可變參數(shù)ADDR_WIDTH和DATA_WIDTH關(guān)聯(lián),這樣就實(shí)現(xiàn)了地址和數(shù)據(jù)位寬參數(shù)化。
using?AddrType?=?sc_uint<ADDR_WIDTH>;
using?DataType?=?sc_uint<DATA_WIDTH>;
...
sc_in<AddrType>??iAddr;//數(shù)據(jù)輸入(寫入數(shù)據(jù))
sc_out<DataType>?oData;//數(shù)據(jù)輸出(讀取數(shù)據(jù))
但是需要注意,template定義的類型實(shí)現(xiàn)在module類之內(nèi),在外部使用需要注意其作用域,否則編譯會(huì)出錯(cuò)。
如module中的DirectRead()方法,其聲明在module內(nèi)部,使用了StorageType,看上去與一般的聲明沒(méi)什么區(qū)別:
//(調(diào)試接口)直接讀取內(nèi)存1個(gè)字
? ? StorageType?DirectRead(uint32_t?addr) const;
然而在同一個(gè).hpp文件內(nèi),實(shí)現(xiàn)代碼放在了類聲明外部,就得重復(fù)一遍template聲明,看上去復(fù)雜了許多:
// ==========調(diào)試接口:讀取內(nèi)存 ==========
template<uint32_t?ADDR_WIDTH, uint32_t?DATA_WIDTH>
typename?SRAM<ADDR_WIDTH,DATA_WIDTH>::StorageType
SRAM<ADDR_WIDTH,DATA_WIDTH>::DirectRead(uint32_t?addr) const?{
//檢查地址是否在有效范圍內(nèi)
? ? if?(addr>=?mem_size_info) {
? ? ? ?std::stringstream ss;
? ? ? ?ss <<?"Direct Read address "?<<?std::hex <<?addr <<?" is out of range!";
? ? ? ?SC_REPORT_ERROR(this->name(), ss.str().c_str());
? ? ? ? return?0;
? ? }
? ? return?m_memory_cells[addr];
}
有一個(gè)做法是在聲明時(shí)就完成實(shí)現(xiàn),這樣避免后面重復(fù)寫template那一套,但我覺(jué)得這會(huì)讓類聲明變得很冗長(zhǎng),個(gè)人還是習(xí)慣于分開(kāi),不過(guò)目前雖然分開(kāi)了,但是仍舊是放在同一個(gè)hpp文件里。