簡(jiǎn)介
能讓電腦完成的就不要手寫了低剔,自動(dòng)生成寄存器模塊verilog代碼的腳本設(shè)計(jì)過程
不知道大家是否向我一樣账蓉,需要快速的實(shí)現(xiàn)模塊袱蚓。
我大部分寫的都是算法模塊钞啸,而且算法每次修改都特別大,除了控制部分可以復(fù)用以外其他都需要重新設(shè)計(jì)喇潘。
但是有些模塊還是有寫規(guī)律可循的体斩,比如寄存器部分。
不管算法如何改變寄存器的配置方式可以說都是差不多的颖低,那就可以讓電腦代替我來完成這部分工作了絮吵。
腳本功能
- 直接生成寄存器模塊的verilog文件
- 配置接口采用apb接口,32bit數(shù)據(jù)和16bit地址
- 支持 RW忱屑、RO 類型的寄存器
- 支持地址自動(dòng)分配
- 支持地址和bit位粘連檢查
基礎(chǔ)知識(shí)
- python
- verilog
python現(xiàn)在主要有兩個(gè)大版本2.x 和 3.x 蹬敲,linux 現(xiàn)在大部分默認(rèn)還是python2暇昂,所以下面所有的都以python2為基礎(chǔ)
python 2.x 和 3.x 有些許不同,請(qǐng)大家注意
建議使用2.7和3.4及其以上版本
代碼格式
先定義下最后生成的代碼風(fēng)格(我就以我平時(shí)喜歡的風(fēng)格來設(shè)計(jì)了)
代碼中有下面兩類寄存器
1.可讀可寫的寄存器:base伴嗡,test
2.只讀寄存器:dbg
module Mbase_reg_cfg
(
// clk / rst_n
input i_clk
,input i_rst_n
// apb port
,input i_psel
,input [15:0] i_paddr
,input i_penable
,input i_pwrite
,input [31:0] i_pwdata
,output [31:0] o_prdata
// output port
,output o_sw_base_0
,output [3:0] o_sw_base_1
,output [3:0] o_sw_tt_dbg_1
,output o_sw_tt_dbg_0
// input port
,input [3:0] i_ro_dbg_1
,input [6:0] i_ro_dbg_0
);
//==========================================================
// apb bus ctrl start/*{{{*/
//==========================================================
wire wr ;
wire rd ;
wire [15:0] addr ;
wire [31:0] wdata ;
reg [31:0] rdata ;
assign wr = i_psel & i_penable & i_pwrite ;
assign rd = i_psel & ~i_penable & ~i_pwrite ;
assign addr = i_paddr ;
assign wdata = i_pwdata ;
assign o_prdata = rdata ;
//==========================================================
// apb bus ctrl end/*}}}*/
//==========================================================
//==========================================================
// localparam start/*{{{*/
//==========================================================
localparam BASE_ADDR = 16'h0000 ;
localparam DBG_ADDR = 16'h0004 ;
localparam TEST_ADDR = 16'h0008 ;
//==========================================================
// localparam end/*}}}*/
//==========================================================
//==========================================================
// reg and wire start/*{{{*/
//==========================================================
// write signal
wire wr_base_w ;
wire wr_test_w ;
// reg
reg [31:0] reg_base_r ;
reg [31:0] reg_test_r ;
// read data
wire [31:0] rdata_base_w ;
wire [31:0] rdata_dbg_w ;
wire [31:0] rdata_test_w ;
//==========================================================
// reg and wire end/*}}}*/
//==========================================================
//==========================================================
// write signal gen start/*{{{*/
//==========================================================
assign wr_base_w = wr & (addr == BASE_ADDR );
assign wr_test_w = wr & (addr == TEST_ADDR );
//==========================================================
// write signal gen end/*}}}*/
//==========================================================
//==========================================================
// base start/*{{{*/
//==========================================================
localparam BASE_VALID_BIT = 32'h00_00_00_f1;
localparam BASE_DEFAULT = 32'h00_00_00_f1;
always@(posedge i_clk) begin
if(i_rst_n == 1'd0) begin
reg_base_r <= BASE_DEFAULT;
end
else if(wr_base_w) begin
reg_base_r <= wdata & BASE_VALID_BIT;
end
end
assign o_sw_base_0 = reg_base_r[0];
assign o_sw_base_1 = reg_base_r[7:4];
assign rdata_base_w = {
24'd0
,o_sw_base_1
,3'd0
,o_sw_base_0
};
//==========================================================
// base end/*}}}*/
//==========================================================
//==========================================================
// dbg start/*{{{*/
//==========================================================
assign rdata_dbg_w = {
14'd0
,i_ro_dbg_0
,3'd0
,i_ro_dbg_1
,4'd0
};
//==========================================================
// dbg end/*}}}*/
//==========================================================
//==========================================================
// test start/*{{{*/
//==========================================================
localparam TEST_VALID_BIT = 32'h80_00_00_f0;
localparam TEST_DEFAULT = 32'h00_00_00_00;
always@(posedge i_clk) begin
if(i_rst_n == 1'd0) begin
reg_test_r <= TEST_DEFAULT;
end
else if(wr_test_w) begin
reg_test_r <= wdata & TEST_VALID_BIT;
end
end
assign o_sw_tt_dbg_1 = reg_test_r[7:4];
assign o_sw_tt_dbg_0 = reg_test_r[31];
assign rdata_test_w = {
o_sw_tt_dbg_0
,23'd0
,o_sw_tt_dbg_1
,4'd0
};
//==========================================================
// test end/*}}}*/
//==========================================================
//==========================================================
// rdata start/*{{{*/
//==========================================================
always@(posedge i_clk) begin
if(i_rst_n == 1'd0) begin
rdata <= 32'd0;
end
else if(rd) begin
case(addr)
BASE_ADDR : rdata <= rdata_base_w ;
DBG_ADDR : rdata <= rdata_dbg_w ;
TEST_ADDR : rdata <= rdata_test_w ;
default : rdata <= 32'd0 ;
endcase
end
else begin
rdata <= rdata;
end
end
//==========================================================
// rdata end/*}}}*/
//==========================================================
endmodule
生成用列表文件內(nèi)容
產(chǎn)生寄存器文件需要知道有那些寄存器和如何拼接
下面為產(chǎn)生上面寄存器文件所使用的列表文件格式
生成模塊名M<name>_reg_cfg
列表文件名<name>_reg_list
列表列表名<name>_reg_list
- 如果列表文件和列表名不一致或是不匹配會(huì)出現(xiàn)問題
base_reg_list = [
{
"name" : "base",
"addr" : 0x0000,
"type" : "RW", # "RW" or "RO"
"bit" : [
["base_0", [0], 0x1], # [寄存器名稱急波, 使用bit位, 默認(rèn)值]
["base_1", [7,4], 0xf], # [寄存器名稱瘪校, 使用bit位澄暮, 默認(rèn)值]
]
},
{
"name" : "dbg",
# "addr" : 0x0004, # 地址可以不顯示指定,默認(rèn)自增
"type" : "RO",
"bit" : [
["dbg_1", [7,4]],
["dbg_0", [17,11], 0x0], # 只讀寄存器默認(rèn)值不起作用
]
},
{
"name" : "test",
"addr" : 0x0008,
# "type" : "RW", # 類型可以不顯示指定阱扬,默認(rèn) "RW"
"bit" : [
["tt_dbg_1", [7,4]], # 默認(rèn)值可以不顯示指定泣懊,默認(rèn) "0"
["tt_dbg_0", [31], 0x0],
]
},
]
腳本處理步驟
1.讀取寄存器列表
2.對(duì)沒有制定默認(rèn)值的地方進(jìn)行添加,沒有地址的使用前面進(jìn)行累加
3.檢查地址麻惶、bit位有無粘連
4.產(chǎn)生輸入輸出端口
5.產(chǎn)生localparam 地址內(nèi)容
6.產(chǎn)生后續(xù)會(huì)使用到的信號(hào)定義
7.產(chǎn)生寄存器寫使能邏輯
8.產(chǎn)生每個(gè)寄存器的寫過程和輸出信號(hào)拆分和讀取信號(hào)合并內(nèi)容
9.產(chǎn)生寄存器讀邏輯
10.合并上述內(nèi)容進(jìn)行輸出
腳本設(shè)計(jì)過程問題與關(guān)鍵點(diǎn)
1.讀取寄存器列表
主要讀取配置文件并生成腳本識(shí)別格式
為了省事馍刮,就不對(duì)寄存器列表進(jìn)行語法解析,而是使用python的exec函數(shù)
exec : 執(zhí)行字符串中的python語句
def get_reg_list(file_name):
with open(file_name, "r") as f:
data_str = f.readlines()
data_str = "".join(data_str)
_locals = locals()
exec(data_str, globals(), _locals)
data = _locals[file_name]
return data
上面主要步驟
- 打開文件并讀取全部?jī)?nèi)容用踩,將文件列表拼接為一個(gè)大的字符串
- 執(zhí)行字符串中內(nèi)容渠退,將字符串中的寄存器列表返回
2.對(duì)沒有制定默認(rèn)值的地方進(jìn)行添加,沒有地址的使用前面進(jìn)行累加
為了后面代碼不會(huì)每次還判讀時(shí)候有指定脐彩,沒有指定用默認(rèn)值碎乃。
所以在給到后面處理時(shí)將沒有顯示指定的地方添加上默認(rèn)值,簡(jiǎn)化后面代碼處理
前面定義了地址默認(rèn)自增惠奸,類型默認(rèn)只讀梅誓,默認(rèn)值為0.
下面代碼有相應(yīng)的操作
def add_default(data):
def_addr = 0x0000
def_type = "RW"
def_value = 0x0
for reg in data:
# add default addr
try:
def_addr = reg["addr"] + 4
except:
reg["addr"] = def_addr
def_addr = reg["addr"] + 4
# add default type
try:
reg["type"]
except:
reg["type"] = def_type
for bit in reg["bit"]:
# add default range
if len(bit[1]) == 1:
bit[1] = [bit[1][0],bit[1][0]]
# add default value
try:
bit[2]
except:
bit.append(def_value)
上面代碼中這兩行不知道你有沒有想到為什么這么做。
if len(bit[1]) == 1:
bit[1] = [bit[1][0],bit[1][0]]
因?yàn)樵诩拇嫫髦挥幸粋€(gè)bit的情況下佛南,我們列表中只有一個(gè)值
為了將多bit和單bit統(tǒng)一梗掰,所以將單bit轉(zhuǎn)為和多bit相同的方式
3.檢查地址、bit位有無粘連
在設(shè)計(jì)過程中我一直有一個(gè)觀點(diǎn):人是最不靠譜的
在上面我們手動(dòng)需要寫的就是list文件
因?yàn)槭鞘謱懶峄兀瑳]有辦法保證100%正確及穗,所以需要添加適當(dāng)?shù)臋z查機(jī)制,確保正確性
常見的問題有幾點(diǎn):地址重疊绵载、bit重疊埂陆、bit超出范圍
下面代碼就是針對(duì)這幾點(diǎn)進(jìn)行的檢查
def check_overlap(data):
err = 0
# addr overlap check
addr_list = []
for reg in data:
addr_list.append(reg["addr"])
set_addr_list = list(set(addr_list))
if len(addr_list) != len(set_addr_list):
overlap_addr = [ addr for addr in set_addr_list if addr_list.count(addr) > 1]
for addr in overlap_addr:
print("Err : 0x%08x addr overlap !!!" % addr)
err = 1
# bit overlap check
for reg in data:
bit_list = []
for bit in reg["bit"]:
bit_start = bit[1][1]
bit_end = bit[1][0]
bit_list += range(bit_start,bit_end+1)
set_bit_list = list(set(bit_list))
if len([bit for bit in set_bit_list if bit >= 32]) > 0:
print("Err : reg %s addr 0x%08x bit out range !!!" % (reg['name'], reg['addr']))
err = 1
if len(bit_list) != len(set_bit_list):
overlap_bit = [ bit for bit in set_bit_list if bit_list.count(bit) > 1]
print("Err : 0x%08x bit %s overlap !!!" % (reg['addr'], str(overlap_bit)))
err = 1
if err:
sys.exit(1)
4.產(chǎn)生輸入輸出端口
接下來就該進(jìn)入正式的verilog代碼部分了
首先我們先確定需要產(chǎn)生的部分(下面內(nèi)容為上面代碼格式部分)
// output port
,output o_sw_base_0
,output [3:0] o_sw_base_1
,output [3:0] o_sw_tt_dbg_1
,output o_sw_tt_dbg_0
// input port
,input [3:0] i_ro_dbg_1
,input [6:0] i_ro_dbg_0
這部分是我們使用過程中需要的輸入輸出端口
所有的模塊我們都需要配置相應(yīng)的寄存器來確定工作模式或是開關(guān)
上面輸出口就是通過總線配置后的值
輸入一般為運(yùn)行后的輸出,或是debug用的
下面為詳細(xì)的產(chǎn)生代碼
def gen_io_port(data):
io_str = []
rw_list = [ reg for reg in data if reg['type'] == 'RW']
ro_list = [ reg for reg in data if reg['type'] == 'RO']
# output port
io_str.append(" // output port\n")
for reg in rw_list:
for bit in reg['bit']:
bit_range = bit[1]
bit_len = bit_range[0] - bit_range[1]
if bit_len != 0:
range_str = "[%d:0]" % bit_len
else:
range_str = ""
bit_name_str = "o_sw_%s" % bit[0]
io_str.append(" ,output %-17s %s\n" % (range_str, bit_name_str,))
io_str.append("\n");
# input port
io_str.append(" // input port\n")
for reg in ro_list:
for bit in reg['bit']:
bit_range = bit[1]
bit_len = bit_range[0] - bit_range[1]
if bit_len != 0:
range_str = "[%d:0]" % bit_len
else:
range_str = ""
bit_name_str = "i_ro_%s" % bit[0]
io_str.append(" ,input %-17s %s\n" % (range_str, bit_name_str,))
io_str.append("\n");
return io_str
5.產(chǎn)生localparam 地址內(nèi)容
接下來就改產(chǎn)生地址信息了娃豹,這里使用lcalparam定義地址焚虱,然后使用
所以這里就只產(chǎn)生定義,具體使用在后面的寫邏輯生成
下面為需要產(chǎn)生的部分(下面內(nèi)容為上面代碼格式部分)
localparam BASE_ADDR = 16'h0000 ;
localparam DBG_ADDR = 16'h0004 ;
localparam TEST_ADDR = 16'h0008 ;
下面為產(chǎn)生代碼懂版。
這里沒有什么難點(diǎn)
def gen_localparam(data):
localparam_str = []
for reg in data:
addr_name = "%s_ADDR" % reg['name'].upper()
addr_str = "16'h%04x" % reg['addr']
localparam_str.append("localparam %-36s = %s ;\n" % (addr_name, addr_str,))
return localparam_str
6.產(chǎn)生后續(xù)會(huì)使用到的信號(hào)定義
下面就是信號(hào)的定義部分
這里主要定義了:寫使能信號(hào)鹃栽、寄存器信號(hào)、讀數(shù)據(jù)信號(hào)
// write signal
wire wr_base_w ;
wire wr_test_w ;
// reg
reg [31:0] reg_base_r ;
reg [31:0] reg_test_r ;
// read data
wire [31:0] rdata_base_w ;
wire [31:0] rdata_dbg_w ;
wire [31:0] rdata_test_w ;
寫使能和寄存器信號(hào)都是"RW"的才有躯畴,而讀數(shù)據(jù)信號(hào)是所有類型的都有
主要是因?yàn)?RO"的不需要寫民鼓,數(shù)據(jù)直接通過輸入進(jìn)來薇芝,只需要總線能讀取即可
下面為相應(yīng)產(chǎn)生部分
def gen_reg_wire(data):
reg_wire_str = []
rw_list = [ reg for reg in data if reg['type'] == 'RW']
reg_wire_str.append("\n")
# write signal
reg_wire_str.append("http:// write signal\n")
for reg in rw_list:
wire_name = "wr_%s_w" % reg['name']
reg_wire_str.append("wire %-47s;\n" % wire_name)
reg_wire_str.append("\n")
# reg
reg_wire_str.append("http:// reg\n")
for reg in rw_list:
reg_name = "reg_%s_r" % reg['name']
reg_wire_str.append("reg [31:0] %-47s;\n" % reg_name)
reg_wire_str.append("\n")
# read data
reg_wire_str.append("http:// read data\n")
for reg in data:
wire_name = "rdata_%s_w" % reg['name']
reg_wire_str.append("wire [31:0] %-47s;\n" % wire_name)
reg_wire_str.append("\n")
return reg_wire_str
7.產(chǎn)生寄存器寫使能邏輯
接下來就是產(chǎn)生寫使能邏輯
assign wr_base_w = wr & (addr == BASE_ADDR );
assign wr_test_w = wr & (addr == TEST_ADDR );
這里只需要產(chǎn)生RW類型的寫使能即可,RO不需要
def gen_write_signal(data):
write_signal_str = []
rw_list = [ reg for reg in data if reg['type'] == 'RW']
for reg in rw_list:
wire_name = "wr_%s_w" % reg['name']
addr_name = "%s_ADDR" % reg['name'].upper()
write_signal_str.append("assign %-20s = wr & (addr == %-14s);\n" % (wire_name, addr_name,))
return write_signal_str
8.產(chǎn)生每個(gè)寄存器的寫過程和輸出信號(hào)拆分和讀取信號(hào)合并內(nèi)容
接下來就是最主要的部分了
寄存器的讀寫丰嘉、輸出連接恩掷、讀拼接都是在這里做的
下面為兩類寄存器生成的最終生成內(nèi)容(生成的注釋中有vim識(shí)別的折疊,這是個(gè)人習(xí)慣)
//==========================================================
// base start/*{{{*/
//==========================================================
localparam BASE_VALID_BIT = 32'h00_00_00_f1;
localparam BASE_DEFAULT = 32'h00_00_00_f1;
always@(posedge i_clk) begin
if(i_rst_n == 1'd0) begin
reg_base_r <= BASE_DEFAULT;
end
else if(wr_base_w) begin
reg_base_r <= wdata & BASE_VALID_BIT;
end
end
assign o_sw_base_0 = reg_base_r[0];
assign o_sw_base_1 = reg_base_r[7:4];
assign rdata_base_w = {
24'd0
,o_sw_base_1
,3'd0
,o_sw_base_0
};
//==========================================================
// base end/*}}}*/
//==========================================================
//==========================================================
// dbg start/*{{{*/
//==========================================================
assign rdata_dbg_w = {
14'd0
,i_ro_dbg_0
,3'd0
,i_ro_dbg_1
,4'd0
};
//==========================================================
// dbg end/*}}}*/
//==========================================================
下面代碼內(nèi)容較多供嚎,對(duì)照上面生成內(nèi)容看會(huì)更好
def gen_reg(data):
reg_str = []
for reg in data:
reg_str.append("http://==========================================================\n")
reg_str.append("http:// %-44s start/*{{{*/\n" % reg['name'])
reg_str.append("http://==========================================================\n")
reg_name = "reg_%s_r" % reg['name']
value_bit_name = "%s_VALID_BIT" % reg['name'].upper()
default_name = "%s_DEFAULT" % reg['name'].upper()
write_name = "wr_%s_w" % reg['name']
rdata_name = "rdata_%s_w" % reg['name']
if reg['type'] == 'RW':
# localparam
value_bit = 0
default = 0
for bit in reg['bit']:
bit_range = bit[1]
bit_def = bit[2]
bit_start = bit_range[1]
bit_end = bit_range[0]
bit_len = bit_end - bit_start + 1
value_bit |= (((2**bit_len) - 1) << bit_start)
default |= bit_def << bit_start
value_bit_str = "%08x" % value_bit
default_str = "%08x" % default
value_bit_str = "_".join([ value_bit_str[i*2:i*2+2] for i in range(4)])
default_str = "_".join([ default_str[i*2:i*2+2] for i in range(4)])
reg_str.append("localparam %-30s = 32'h%s;\n" % (value_bit_name, value_bit_str))
reg_str.append("localparam %-30s = 32'h%s;\n" % (default_name, default_str))
reg_str.append("\n")
# reg function
reg_str.append("always@(posedge i_clk) begin\n" )
reg_str.append(" if(i_rst_n == 1'd0) begin\n" )
reg_str.append(" %s <= %s;\n" % (reg_name, default_name,))
reg_str.append(" end\n" )
reg_str.append(" else if(%s) begin\n" % write_name)
reg_str.append(" %s <= wdata & %s;\n" % (reg_name, value_bit_name,))
reg_str.append(" end\n" )
reg_str.append("end\n" )
reg_str.append("\n" )
# output
for bit in reg['bit']:
output_name = "o_sw_%s" % bit[0]
bit_range = bit[1]
bit_start = bit_range[1]
bit_end = bit_range[0]
bit_len = bit_end - bit_start + 1
if bit_len > 1:
bit_range_str = "[%d:%d]" % (bit_end, bit_start)
else:
bit_range_str = "[%d]" % bit_start
reg_str.append("assign %-16s = %s%s;\n" % (output_name, reg_name, bit_range_str,))
reg_str.append("\n")
# rdata
bit_index = 0
rdata_list = []
for bit in reg['bit']:
bit_range = bit[1]
bit_start = bit_range[1]
bit_end = bit_range[0]
if bit_index != bit_start:
rdata_list.append("%d'd0" % (bit_start - bit_index))
if reg['type'] == "RW":
rdata_list.append("o_sw_%s" % bit[0])
else:
rdata_list.append("i_ro_%s" % bit[0])
bit_index = bit_end + 1
if bit_index != 32:
rdata_list.append("%d'd0" % (32 - bit_index))
reg_str.append("assign %s = {\n" % rdata_name)
reg_str.append(" " + "\n ,".join(rdata_list[::-1]) + "\n")
reg_str.append("};\n")
reg_str.append("http://==========================================================\n")
reg_str.append("http:// %-44s end/*}}}*/\n" % reg['name'])
reg_str.append("http://==========================================================\n")
reg_str.append("\n")
return reg_str
首先產(chǎn)生需要一些名稱
產(chǎn)生默認(rèn)值、可寫bit 這兩個(gè)localparam
產(chǎn)生寄存器寫的always
產(chǎn)生輸出信號(hào)的連接
產(chǎn)生輸出信號(hào)的拼接峭状,這里RW拼接的是o_sw_* 而RO拼接的是i_ro_*
9.產(chǎn)生寄存器讀邏輯
這可以說已經(jīng)是最后了
前面大部分邏輯都生成了克滴,就差能讓總線讀取到的邏輯了
always@(posedge i_clk) begin
if(i_rst_n == 1'd0) begin
rdata <= 32'd0;
end
else if(rd) begin
case(addr)
BASE_ADDR : rdata <= rdata_base_w ;
DBG_ADDR : rdata <= rdata_dbg_w ;
TEST_ADDR : rdata <= rdata_test_w ;
default : rdata <= 32'd0 ;
endcase
end
else begin
rdata <= rdata;
end
end
這里恒簡(jiǎn)單,因?yàn)榍懊嬉呀?jīng)將信號(hào)拼接好了优床,這里直接使用即可
def gen_rdata(data):
rdata_str = []
for reg in data:
addr_name = "%s_ADDR" % reg['name'].upper()
rdata_name = "rdata_%s_w" % reg['name']
rdata_str.append(" %-22s : rdata <= %-21s;\n" % (addr_name, rdata_name,))
return rdata_str
10.合并上述內(nèi)容進(jìn)行輸出
上面產(chǎn)生完所有邏輯內(nèi)容劝赔,這里就只需要將所有內(nèi)容拼接起來
下面代碼雖然長(zhǎng),但其實(shí)沒有什么內(nèi)容胆敞。
def gen_reg_file(
mode_name,
io_str,
localparam_str,
reg_wire_str,
write_signal_str,
reg_str,
rdata_str,
):
with open("../reg_file/%s_reg_cfg.v" % mode_name, "w") as f:
f.write("module M%s_reg_cfg\n" % mode_name )
f.write("(\n" )
f.write(" // clk / rst_n\n" )
f.write(" input i_clk\n" )
f.write(" ,input i_rst_n\n" )
f.write("\n" )
f.write(" // apb port\n" )
f.write(" ,input i_psel\n" )
f.write(" ,input [15:0] i_paddr\n" )
f.write(" ,input i_penable\n" )
f.write(" ,input i_pwrite\n" )
f.write(" ,input [31:0] i_pwdata\n" )
f.write(" ,output [31:0] o_prdata\n" )
f.write("\n" )
f.write("".join(io_str))
f.write(");\n" )
f.write("http://==========================================================\n")
f.write("http:// apb bus ctrl start/*{{{*/\n")
f.write("http://==========================================================\n")
f.write("wire wr ;\n")
f.write("wire rd ;\n")
f.write("wire [15:0] addr ;\n")
f.write("wire [31:0] wdata ;\n")
f.write("reg [31:0] rdata ;\n")
f.write("\n" )
f.write("assign wr = i_psel & i_penable & i_pwrite ;\n")
f.write("assign rd = i_psel & ~i_penable & ~i_pwrite ;\n")
f.write("assign addr = i_paddr ;\n")
f.write("assign wdata = i_pwdata ;\n")
f.write("assign o_prdata = rdata ;\n")
f.write("http://==========================================================\n")
f.write("http:// apb bus ctrl end/*}}}*/\n")
f.write("http://==========================================================\n")
f.write("\n" )
f.write("http://==========================================================\n")
f.write("http:// localparam start/*{{{*/\n")
f.write("http://==========================================================\n")
f.write("".join(localparam_str))
f.write("http://==========================================================\n")
f.write("http:// localparam end/*}}}*/\n")
f.write("http://==========================================================\n")
f.write("\n" )
f.write("http://==========================================================\n")
f.write("http:// reg and wire start/*{{{*/\n")
f.write("http://==========================================================\n")
f.write("".join(reg_wire_str))
f.write("http://==========================================================\n")
f.write("http:// reg and wire end/*}}}*/\n")
f.write("http://==========================================================\n")
f.write("\n" )
f.write("http://==========================================================\n")
f.write("http:// write signal gen start/*{{{*/\n")
f.write("http://==========================================================\n")
f.write("".join(write_signal_str))
f.write("http://==========================================================\n")
f.write("http:// write signal gen end/*}}}*/\n")
f.write("http://==========================================================\n")
f.write("\n" )
f.write("".join(reg_str))
f.write("http://==========================================================\n")
f.write("http:// rdata start/*{{{*/\n")
f.write("http://==========================================================\n")
f.write("always@(posedge i_clk) begin\n" )
f.write(" if(i_rst_n == 1'd0) begin\n" )
f.write(" rdata <= 32'd0;\n" )
f.write(" end\n" )
f.write(" else if(rd) begin\n" )
f.write(" case(addr)\n" )
f.write("".join(rdata_str))
f.write(" default : rdata <= 32'd0 ;\n")
f.write(" endcase\n" )
f.write(" end\n" )
f.write(" else begin\n" )
f.write(" rdata <= rdata;\n" )
f.write(" end\n" )
f.write("end\n" )
f.write("http://==========================================================\n")
f.write("http:// rdata end/*}}}*/\n")
f.write("http://==========================================================\n")
f.write("\n" )
f.write("endmodule\n" )
f.close()
結(jié)尾
在工作過程中有許多的事着帽,而且IC這一行容錯(cuò)率非常低,稍微一個(gè)bug就可以是流片失斠撇恪(FPGA的除外)
而我一直奉行<人是最不靠譜的>觀點(diǎn)仍翰。而工作后會(huì)發(fā)現(xiàn)有很多的工具就是專門為了幫助人們找錯(cuò)誤的,仿真就是其一观话。
然而工具使用的在熟練予借,也是需要花費(fèi)不少時(shí)間的。
所以提高效率和準(zhǔn)確性就是流程化频蛔,并且自動(dòng)化
下面為完整的腳本
#!/bin/env python
import os
import sys
# ==========================================================
# get reg list start#{{{
# ==========================================================
def get_reg_list(file_name):
try:
with open(file_name, "r") as f:
data_str = f.readlines()
data_str = "".join(data_str)
_locals = locals()
exec(data_str, globals(), _locals)
data = _locals[file_name]
return data
except:
print("open file %s err" % file_name)
sys.exit(1)
# ==========================================================
# get reg list end#}}}
# ==========================================================
# ==========================================================
# add default start#{{{
# ==========================================================
def add_default(data):
def_addr = 0x0000
def_type = "RW"
def_value = 0x0
for reg in data:
# add default addr
try:
def_addr = reg["addr"] + 4
except:
reg["addr"] = def_addr
def_addr = reg["addr"] + 4
# add default type
try:
reg["type"]
except:
reg["type"] = def_type
for bit in reg["bit"]:
# add default range
if len(bit[1]) == 1:
bit[1] = [bit[1][0],bit[1][0]]
# add default value
try:
bit[2]
except:
bit.append(def_value)
# ==========================================================
# add default end#}}}
# ==========================================================
# ==========================================================
# check overlap start#{{{
# ==========================================================
def check_overlap(data):
err = 0
# addr overlap check
addr_list = []
for reg in data:
addr_list.append(reg["addr"])
set_addr_list = list(set(addr_list))
if len(addr_list) != len(set_addr_list):
overlap_addr = [ addr for addr in set_addr_list if addr_list.count(addr) > 1]
for addr in overlap_addr:
print("Err : 0x%08x addr overlap !!!" % addr)
err = 1
# bit overlap check
for reg in data:
bit_list = []
for bit in reg["bit"]:
bit_start = bit[1][1]
bit_end = bit[1][0]
bit_list += range(bit_start,bit_end+1)
set_bit_list = list(set(bit_list))
if len([bit for bit in set_bit_list if bit >= 32]) > 0:
print("Err : reg %s addr 0x%08x bit out range !!!" % (reg['name'], reg['addr']))
err = 1
if len(bit_list) != len(set_bit_list):
overlap_bit = [ bit for bit in set_bit_list if bit_list.count(bit) > 1]
print("Err : 0x%08x bit %s overlap !!!" % (reg['addr'], str(overlap_bit)))
err = 1
if err:
sys.exit(1)
# ==========================================================
# check overlap end#}}}
# ==========================================================
# ==========================================================
# gen io port start#{{{
# ==========================================================
def gen_io_port(data):
io_str = []
rw_list = [ reg for reg in data if reg['type'] == 'RW']
ro_list = [ reg for reg in data if reg['type'] == 'RO']
# output port
io_str.append(" // output port\n")
for reg in rw_list:
for bit in reg['bit']:
bit_range = bit[1]
bit_len = bit_range[0] - bit_range[1]
if bit_len != 0:
range_str = "[%d:0]" % bit_len
else:
range_str = ""
bit_name_str = "o_sw_%s" % bit[0]
io_str.append(" ,output %-17s %s\n" % (range_str, bit_name_str,))
io_str.append("\n");
# input port
io_str.append(" // input port\n")
for reg in ro_list:
for bit in reg['bit']:
bit_range = bit[1]
bit_len = bit_range[0] - bit_range[1]
if bit_len != 0:
range_str = "[%d:0]" % bit_len
else:
range_str = ""
bit_name_str = "i_ro_%s" % bit[0]
io_str.append(" ,input %-17s %s\n" % (range_str, bit_name_str,))
io_str.append("\n");
return io_str
# ==========================================================
# gen io port end#}}}
# ==========================================================
# ==========================================================
# gen localparam start#{{{
# ==========================================================
def gen_localparam(data):
localparam_str = []
for reg in data:
addr_name = "%s_ADDR" % reg['name'].upper()
addr_str = "16'h%04x" % reg['addr']
localparam_str.append("localparam %-36s = %s ;\n" % (addr_name, addr_str,))
return localparam_str
# ==========================================================
# gen localparam end#}}}
# ==========================================================
# ==========================================================
# gen reg wire start#{{{
# ==========================================================
def gen_reg_wire(data):
reg_wire_str = []
rw_list = [ reg for reg in data if reg['type'] == 'RW']
reg_wire_str.append("\n")
# write signal
reg_wire_str.append("http:// write signal\n")
for reg in rw_list:
wire_name = "wr_%s_w" % reg['name']
reg_wire_str.append("wire %-47s;\n" % wire_name)
reg_wire_str.append("\n")
# reg
reg_wire_str.append("http:// reg\n")
for reg in rw_list:
reg_name = "reg_%s_r" % reg['name']
reg_wire_str.append("reg [31:0] %-47s;\n" % reg_name)
reg_wire_str.append("\n")
# read data
reg_wire_str.append("http:// read data\n")
for reg in data:
wire_name = "rdata_%s_w" % reg['name']
reg_wire_str.append("wire [31:0] %-47s;\n" % wire_name)
reg_wire_str.append("\n")
return reg_wire_str
# ==========================================================
# gen reg wire end#}}}
# ==========================================================
# ==========================================================
# gen write signal start#{{{
# ==========================================================
def gen_write_signal(data):
write_signal_str = []
rw_list = [ reg for reg in data if reg['type'] == 'RW']
for reg in rw_list:
wire_name = "wr_%s_w" % reg['name']
addr_name = "%s_ADDR" % reg['name'].upper()
write_signal_str.append("assign %-20s = wr & (addr == %-14s);\n" % (wire_name, addr_name,))
return write_signal_str
# ==========================================================
# gen write signal end#}}}
# ==========================================================
# ==========================================================
# gen reg start#{{{
# ==========================================================
def gen_reg(data):
reg_str = []
for reg in data:
reg_str.append("http://==========================================================\n")
reg_str.append("http:// %-44s start/*{{{*/\n" % reg['name'])
reg_str.append("http://==========================================================\n")
reg_name = "reg_%s_r" % reg['name']
value_bit_name = "%s_VALID_BIT" % reg['name'].upper()
default_name = "%s_DEFAULT" % reg['name'].upper()
write_name = "wr_%s_w" % reg['name']
rdata_name = "rdata_%s_w" % reg['name']
if reg['type'] == 'RW':
# localparam
value_bit = 0
default = 0
for bit in reg['bit']:
bit_range = bit[1]
bit_def = bit[2]
bit_start = bit_range[1]
bit_end = bit_range[0]
bit_len = bit_end - bit_start + 1
value_bit |= (((2**bit_len) - 1) << bit_start)
default |= bit_def << bit_start
value_bit_str = "%08x" % value_bit
default_str = "%08x" % default
value_bit_str = "_".join([ value_bit_str[i*2:i*2+2] for i in range(4)])
default_str = "_".join([ default_str[i*2:i*2+2] for i in range(4)])
reg_str.append("localparam %-30s = 32'h%s;\n" % (value_bit_name, value_bit_str))
reg_str.append("localparam %-30s = 32'h%s;\n" % (default_name, default_str))
reg_str.append("\n")
# reg function
reg_str.append("always@(posedge i_clk) begin\n" )
reg_str.append(" if(i_rst_n == 1'd0) begin\n" )
reg_str.append(" %s <= %s;\n" % (reg_name, default_name,))
reg_str.append(" end\n" )
reg_str.append(" else if(%s) begin\n" % write_name)
reg_str.append(" %s <= wdata & %s;\n" % (reg_name, value_bit_name,))
reg_str.append(" end\n" )
reg_str.append("end\n" )
reg_str.append("\n" )
# output
for bit in reg['bit']:
output_name = "o_sw_%s" % bit[0]
bit_range = bit[1]
bit_start = bit_range[1]
bit_end = bit_range[0]
bit_len = bit_end - bit_start + 1
if bit_len > 1:
bit_range_str = "[%d:%d]" % (bit_end, bit_start)
else:
bit_range_str = "[%d]" % bit_start
reg_str.append("assign %-16s = %s%s;\n" % (output_name, reg_name, bit_range_str,))
reg_str.append("\n")
# rdata
bit_index = 0
rdata_list = []
for bit in reg['bit']:
bit_range = bit[1]
bit_start = bit_range[1]
bit_end = bit_range[0]
if bit_index != bit_start:
rdata_list.append("%d'd0" % (bit_start - bit_index))
if reg['type'] == "RW":
rdata_list.append("o_sw_%s" % bit[0])
else:
rdata_list.append("i_ro_%s" % bit[0])
bit_index = bit_end + 1
if bit_index != 32:
rdata_list.append("%d'd0" % (32 - bit_index))
reg_str.append("assign %s = {\n" % rdata_name)
reg_str.append(" " + "\n ,".join(rdata_list[::-1]) + "\n")
reg_str.append("};\n")
reg_str.append("http://==========================================================\n")
reg_str.append("http:// %-44s end/*}}}*/\n" % reg['name'])
reg_str.append("http://==========================================================\n")
reg_str.append("\n")
return reg_str
# ==========================================================
# gen reg end#}}}
# ==========================================================
# ==========================================================
# gen rdata start#{{{
# ==========================================================
def gen_rdata(data):
rdata_str = []
for reg in data:
addr_name = "%s_ADDR" % reg['name'].upper()
rdata_name = "rdata_%s_w" % reg['name']
rdata_str.append(" %-22s : rdata <= %-21s;\n" % (addr_name, rdata_name,))
return rdata_str
# ==========================================================
# gen rdata end#}}}
# ==========================================================
# ==========================================================
# gen reg file start#{{{
# ==========================================================
def gen_reg_file(
mode_name,
io_str,
localparam_str,
reg_wire_str,
write_signal_str,
reg_str,
rdata_str,
):
with open("../reg_file/%s_reg_cfg.v" % mode_name, "w") as f:
f.write("module M%s_reg_cfg\n" % mode_name )
f.write("(\n" )
f.write(" // clk / rst_n\n" )
f.write(" input i_clk\n" )
f.write(" ,input i_rst_n\n" )
f.write("\n" )
f.write(" // apb port\n" )
f.write(" ,input i_psel\n" )
f.write(" ,input [15:0] i_paddr\n" )
f.write(" ,input i_penable\n" )
f.write(" ,input i_pwrite\n" )
f.write(" ,input [31:0] i_pwdata\n" )
f.write(" ,output [31:0] o_prdata\n" )
f.write("\n" )
f.write("".join(io_str))
f.write(");\n" )
f.write("http://==========================================================\n")
f.write("http:// apb bus ctrl start/*{{{*/\n")
f.write("http://==========================================================\n")
f.write("wire wr ;\n")
f.write("wire rd ;\n")
f.write("wire [15:0] addr ;\n")
f.write("wire [31:0] wdata ;\n")
f.write("reg [31:0] rdata ;\n")
f.write("\n" )
f.write("assign wr = i_psel & i_penable & i_pwrite ;\n")
f.write("assign rd = i_psel & ~i_penable & ~i_pwrite ;\n")
f.write("assign addr = i_paddr ;\n")
f.write("assign wdata = i_pwdata ;\n")
f.write("assign o_prdata = rdata ;\n")
f.write("http://==========================================================\n")
f.write("http:// apb bus ctrl end/*}}}*/\n")
f.write("http://==========================================================\n")
f.write("\n" )
f.write("http://==========================================================\n")
f.write("http:// localparam start/*{{{*/\n")
f.write("http://==========================================================\n")
f.write("".join(localparam_str))
f.write("http://==========================================================\n")
f.write("http:// localparam end/*}}}*/\n")
f.write("http://==========================================================\n")
f.write("\n" )
f.write("http://==========================================================\n")
f.write("http:// reg and wire start/*{{{*/\n")
f.write("http://==========================================================\n")
f.write("".join(reg_wire_str))
f.write("http://==========================================================\n")
f.write("http:// reg and wire end/*}}}*/\n")
f.write("http://==========================================================\n")
f.write("\n" )
f.write("http://==========================================================\n")
f.write("http:// write signal gen start/*{{{*/\n")
f.write("http://==========================================================\n")
f.write("".join(write_signal_str))
f.write("http://==========================================================\n")
f.write("http:// write signal gen end/*}}}*/\n")
f.write("http://==========================================================\n")
f.write("\n" )
f.write("".join(reg_str))
f.write("http://==========================================================\n")
f.write("http:// rdata start/*{{{*/\n")
f.write("http://==========================================================\n")
f.write("always@(posedge i_clk) begin\n" )
f.write(" if(i_rst_n == 1'd0) begin\n" )
f.write(" rdata <= 32'd0;\n" )
f.write(" end\n" )
f.write(" else if(rd) begin\n" )
f.write(" case(addr)\n" )
f.write("".join(rdata_str))
f.write(" default : rdata <= 32'd0 ;\n")
f.write(" endcase\n" )
f.write(" end\n" )
f.write(" else begin\n" )
f.write(" rdata <= rdata;\n" )
f.write(" end\n" )
f.write("end\n" )
f.write("http://==========================================================\n")
f.write("http:// rdata end/*}}}*/\n")
f.write("http://==========================================================\n")
f.write("\n" )
f.write("endmodule\n" )
f.close()
# ==========================================================
# gen reg file end#}}}
# ==========================================================
if __name__ == '__main__':
if(len(sys.argv) < 2):
print("not have input file")
print(" %s reg_list" % sys.argv[0])
sys.exit(0)
file_name = sys.argv[1]
mode_name = file_name.split("_")[0]
data = get_reg_list(file_name)
add_default(data)
check_overlap(data)
io_str = gen_io_port(data)
localparam_str = gen_localparam(data)
reg_wire_str = gen_reg_wire(data)
write_signal_str = gen_write_signal(data)
reg_str = gen_reg(data)
rdata_str = gen_rdata(data)
gen_reg_file(
mode_name,
io_str,
localparam_str,
reg_wire_str,
write_signal_str,
reg_str,
rdata_str,
)
如果生成不了或是錯(cuò)誤可能是下面幾點(diǎn)
1.列表文件名不是<name>_reg_list
2.列表文件中列表名和文件不一致
3.代碼中生成目錄為 "../reg_file/<name>_reg_cfg.v"灵迫,需要有相應(yīng)的目錄才能生成
4.是不是python版本問題
如果有問題或是有想探討的可以加我微信:anyuexiu