SystemC開(kāi)發(fā)學(xué)習(xí) ~ (1)使用template實(shí)現(xiàn)module配置參數(shù)化

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文件里。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容