IC 設計工具篇 -- 暫存器模組生成指令碼設計過程(verilog 版)
簡介
能讓電腦完成的就不要手寫了,自動生成暫存器模組verilog程式碼的指令碼設計過程
不知道大家是否向我一樣,需要快速的實現模組。
我大部分寫的都是演算法模組,而且演算法每次修改都特別大,除了控制部分可以複用以外其他都需要重新設計。
但是有些模組還是有寫規律可循的,比如暫存器部分。
不管演算法如何改變暫存器的配置方式可以說都是差不多的,那就可以讓電腦代替我來完成這部分工作了。
指令碼功能
- 直接生成暫存器模組的verilog檔案
- 配置介面採用apb介面,32bit資料和16bit地址
- 支援 RW、RO 型別的暫存器
- 支援地址自動分配
- 支援地址和bit位粘連檢查
基礎知識
- python
- verilog
python現在主要有兩個大版本2.x 和 3.x ,linux 現在大部分預設還是python2,所以下面所有的都以python2為基礎
python 2.x 和 3.x 有些許不同,請大家注意
建議使用2.7和3.4及其以上版本
程式碼格式
先定義下最後生成的程式碼風格(我就以我平時喜歡的風格來設計了)
程式碼中有下面兩類暫存器
1.可讀可寫的暫存器:base,test
2.只讀暫存器:dbg
module Mbase_reg_cfg ( // clk / rst_n inputi_clk ,inputi_rst_n // apb port ,inputi_psel ,input[15:0]i_paddr ,inputi_penable ,inputi_pwrite ,input[31:0]i_pwdata ,output [31:0]o_prdata // output port ,outputo_sw_base_0 ,output [3:0]o_sw_base_1 ,output [3:0]o_sw_tt_dbg_1 ,outputo_sw_tt_dbg_0 // input port ,input[3:0]i_ro_dbg_1 ,input[6:0]i_ro_dbg_0 ); //========================================================== // apb bus ctrlstart/*{{{*/ //========================================================== wirewr; wirerd; 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 ctrlend/*}}}*/ //========================================================== //========================================================== // localparamstart/*{{{*/ //========================================================== localparam BASE_ADDR= 16'h0000 ; localparam DBG_ADDR= 16'h0004 ; localparam TEST_ADDR= 16'h0008 ; //========================================================== // localparamend/*}}}*/ //========================================================== //========================================================== // reg and wirestart/*{{{*/ //========================================================== // write signal wirewr_base_w; wirewr_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 wireend/*}}}*/ //========================================================== //========================================================== // write signal genstart/*{{{*/ //========================================================== assign wr_base_w= wr & (addr == BASE_ADDR); assign wr_test_w= wr & (addr == TEST_ADDR); //========================================================== // write signal genend/*}}}*/ //========================================================== //========================================================== // basestart/*{{{*/ //========================================================== 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 }; //========================================================== // baseend/*}}}*/ //========================================================== //========================================================== // dbgstart/*{{{*/ //========================================================== assign rdata_dbg_w = { 14'd0 ,i_ro_dbg_0 ,3'd0 ,i_ro_dbg_1 ,4'd0 }; //========================================================== // dbgend/*}}}*/ //========================================================== //========================================================== // teststart/*{{{*/ //========================================================== 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 }; //========================================================== // testend/*}}}*/ //========================================================== //========================================================== // rdatastart/*{{{*/ //========================================================== 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 //========================================================== // rdataend/*}}}*/ //========================================================== endmodule
生成用列表檔案內容
產生暫存器檔案需要知道有那些暫存器和如何拼接
下面為產生上面暫存器檔案所使用的列表檔案格式
生成模組名M<name>_reg_cfg
列表檔名<name>_reg_list
列表列表名<name>_reg_list
- 如果列表檔案和列表名不一致或是不匹配會出現問題
base_reg_list = [ { "name" : "base", "addr" : 0x0000, "type" : "RW",# "RW" or "RO" "bit": [ ["base_0", [0], 0x1],# [暫存器名稱, 使用bit位, 預設值] ["base_1", [7,4], 0xf],# [暫存器名稱, 使用bit位, 預設值] ] }, { "name" : "dbg", # "addr" : 0x0004,# 地址可以不顯示指定,預設自增 "type" : "RO", "bit": [ ["dbg_1", [7,4]], ["dbg_0", [17,11], 0x0],# 只讀暫存器預設值不起作用 ] }, { "name" : "test", "addr" : 0x0008, # "type" : "RW",# 型別可以不顯示指定,預設 "RW" "bit": [ ["tt_dbg_1", [7,4]],# 預設值可以不顯示指定,預設 "0" ["tt_dbg_0", [31], 0x0], ] }, ]
指令碼處理步驟
1.讀取暫存器列表
2.對沒有制定預設值的地方進行新增,沒有地址的使用前面進行累加
3.檢查地址、bit位有無粘連
4.產生輸入輸出埠
5.產生localparam 地址內容
6.產生後續會使用到的訊號定義
7.產生暫存器寫使能邏輯
8.產生每個暫存器的寫過程和輸出訊號拆分和讀取訊號合併內容
9.產生暫存器讀邏輯
10.合併上述內容進行輸出
指令碼設計過程問題與關鍵點
1.讀取暫存器列表
主要讀取配置檔案並生成指令碼識別格式
為了省事,就不對暫存器列表進行語法解析,而是使用python的exec函式
exec : 執行字串中的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
上面主要步驟
- 開啟檔案並讀取全部內容,將檔案列表拼接為一個大的字串
- 執行字串中內容,將字串中的暫存器列表返回
2.對沒有制定預設值的地方進行新增,沒有地址的使用前面進行累加
為了後面程式碼不會每次還判讀時候有指定,沒有指定用預設值。
所以在給到後面處理時將沒有顯示指定的地方新增上預設值,簡化後面程式碼處理
前面定義了地址預設自增,型別預設只讀,預設值為0.
下面程式碼有相應的操作
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]]
因為在暫存器只有一個bit的情況下,我們列表中只有一個值
為了將多bit和單bit統一,所以將單bit轉為和多bit相同的方式
3.檢查地址、bit位有無粘連
在設計過程中我一直有一個觀點:人是最不靠譜的
在上面我們手動需要寫的就是list檔案
因為是手寫,沒有辦法保證100%正確,所以需要新增適當的檢查機制,確保正確性
常見的問題有幾點:地址重疊、bit重疊、bit超出範圍
下面程式碼就是針對這幾點進行的檢查
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.產生輸入輸出埠
接下來就該進入正式的verilog程式碼部分了
首先我們先確定需要產生的部分(下面內容為上面程式碼格式部分)
// output port ,outputo_sw_base_0 ,output [3:0]o_sw_base_1 ,output [3:0]o_sw_tt_dbg_1 ,outputo_sw_tt_dbg_0 // input port ,input[3:0]i_ro_dbg_1 ,input[6:0]i_ro_dbg_0
這部分是我們使用過程中需要的輸入輸出埠
所有的模組我們都需要配置相應的暫存器來確定工作模式或是開關
上面輸出口就是通過匯流排配置後的值
輸入一般為執行後的輸出,或是debug用的
下面為詳細的產生程式碼
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.產生localparam 地址內容
接下來就改產生地址資訊了,這裡使用lcalparam定義地址,然後使用
所以這裡就只產生定義,具體使用在後面的寫邏輯生成
下面為需要產生的部分(下面內容為上面程式碼格式部分)
localparam BASE_ADDR= 16'h0000 ; localparam DBG_ADDR= 16'h0004 ; localparam TEST_ADDR= 16'h0008 ;
下面為產生程式碼。
這裡沒有什麼難點
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.產生後續會使用到的訊號定義
下面就是訊號的定義部分
這裡主要定義了:寫使能訊號、暫存器訊號、讀資料訊號
// write signal wirewr_base_w; wirewr_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;
寫使能和暫存器訊號都是"RW"的才有,而讀資料訊號是所有型別的都有
主要是因為"RO"的不需要寫,資料直接通過輸入進來,只需要匯流排能讀取即可
下面為相應產生部分
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("// 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("// 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("// 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.產生暫存器寫使能邏輯
接下來就是產生寫使能邏輯
assign wr_base_w= wr & (addr == BASE_ADDR); assign wr_test_w= wr & (addr == TEST_ADDR);
這裡只需要產生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.產生每個暫存器的寫過程和輸出訊號拆分和讀取訊號合併內容
接下來就是最主要的部分了
暫存器的讀寫、輸出連線、讀拼接都是在這裡做的
下面為兩類暫存器生成的最終生成內容(生成的註釋中有vim識別的摺疊,這是個人習慣)
//========================================================== // basestart/*{{{*/ //========================================================== 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 }; //========================================================== // baseend/*}}}*/ //========================================================== //========================================================== // dbgstart/*{{{*/ //========================================================== assign rdata_dbg_w = { 14'd0 ,i_ro_dbg_0 ,3'd0 ,i_ro_dbg_1 ,4'd0 }; //========================================================== // dbgend/*}}}*/ //==========================================================
下面程式碼內容較多,對照上面生成內容看會更好
def gen_reg(data): reg_str = [] for reg in data: reg_str.append("//==========================================================\n") reg_str.append("// %-44s start/*{{{*/\n" % reg['name']) reg_str.append("//==========================================================\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("//==========================================================\n") reg_str.append("// %-44send/*}}}*/\n" % reg['name']) reg_str.append("//==========================================================\n") reg_str.append("\n") return reg_str
首先產生需要一些名稱
產生預設值、可寫bit 這兩個localparam
產生暫存器寫的always
產生輸出訊號的連線
產生輸出訊號的拼接,這裡RW拼接的是o_sw_* 而RO拼接的是i_ro_*
9.產生暫存器讀邏輯
這可以說已經是最後了
前面大部分邏輯都生成了,就差能讓匯流排讀取到的邏輯了
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
這裡恆簡單,因為前面已經將訊號拼接好了,這裡直接使用即可
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.合併上述內容進行輸出
上面產生完所有邏輯內容,這裡就只需要將所有內容拼接起來
下面程式碼雖然長,但其實沒有什麼內容。
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("inputi_clk\n") f.write(",inputi_rst_n\n") f.write("\n") f.write("// apb port\n") f.write(",inputi_psel\n") f.write(",input[15:0]i_paddr\n") f.write(",inputi_penable\n") f.write(",inputi_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("//==========================================================\n") f.write("// apb bus ctrlstart/*{{{*/\n") f.write("//==========================================================\n") f.write("wirewr;\n") f.write("wirerd;\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("//==========================================================\n") f.write("// apb bus ctrlend/*}}}*/\n") f.write("//==========================================================\n") f.write("\n") f.write("//==========================================================\n") f.write("// localparamstart/*{{{*/\n") f.write("//==========================================================\n") f.write("".join(localparam_str)) f.write("//==========================================================\n") f.write("// localparamend/*}}}*/\n") f.write("//==========================================================\n") f.write("\n") f.write("//==========================================================\n") f.write("// reg and wirestart/*{{{*/\n") f.write("//==========================================================\n") f.write("".join(reg_wire_str)) f.write("//==========================================================\n") f.write("// reg and wireend/*}}}*/\n") f.write("//==========================================================\n") f.write("\n") f.write("//==========================================================\n") f.write("// write signal genstart/*{{{*/\n") f.write("//==========================================================\n") f.write("".join(write_signal_str)) f.write("//==========================================================\n") f.write("// write signal genend/*}}}*/\n") f.write("//==========================================================\n") f.write("\n") f.write("".join(reg_str)) f.write("//==========================================================\n") f.write("// rdatastart/*{{{*/\n") f.write("//==========================================================\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("//==========================================================\n") f.write("// rdataend/*}}}*/\n") f.write("//==========================================================\n") f.write("\n") f.write("endmodule\n") f.close()
結尾
在工作過程中有許多的事,而且IC這一行容錯率非常低,稍微一個bug就可以是流片失敗(FPGA的除外)
而我一直奉行<人是最不靠譜的>觀點。而工作後會發現有很多的工具就是專門為了幫助人們找錯誤的,模擬就是其一。
然而工具使用的在熟練,也是需要花費不少時間的。
所以提高效率和準確性就是流程化,並且自動化
下面為完整的指令碼
#!/bin/env python import os import sys # ========================================================== #get reg liststart#{{{ # ========================================================== 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 listend#}}} # ========================================================== # ========================================================== #add defaultstart#{{{ # ========================================================== 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 defaultend#}}} # ========================================================== # ========================================================== #check overlapstart#{{{ # ========================================================== 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 overlapend#}}} # ========================================================== # ========================================================== #gen io portstart#{{{ # ========================================================== 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 portend#}}} # ========================================================== # ========================================================== #gen localparamstart#{{{ # ========================================================== 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 localparamend#}}} # ========================================================== # ========================================================== #gen reg wirestart#{{{ # ========================================================== 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("// 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("// 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("// 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 wireend#}}} # ========================================================== # ========================================================== #gen write signalstart#{{{ # ========================================================== 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 signalend#}}} # ========================================================== # ========================================================== #gen regstart#{{{ # ========================================================== def gen_reg(data): reg_str = [] for reg in data: reg_str.append("//==========================================================\n") reg_str.append("// %-44s start/*{{{*/\n" % reg['name']) reg_str.append("//==========================================================\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("//==========================================================\n") reg_str.append("// %-44send/*}}}*/\n" % reg['name']) reg_str.append("//==========================================================\n") reg_str.append("\n") return reg_str # ========================================================== #gen regend#}}} # ========================================================== # ========================================================== #gen rdatastart#{{{ # ========================================================== 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 rdataend#}}} # ========================================================== # ========================================================== #gen reg filestart#{{{ # ========================================================== 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("inputi_clk\n") f.write(",inputi_rst_n\n") f.write("\n") f.write("// apb port\n") f.write(",inputi_psel\n") f.write(",input[15:0]i_paddr\n") f.write(",inputi_penable\n") f.write(",inputi_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("//==========================================================\n") f.write("// apb bus ctrlstart/*{{{*/\n") f.write("//==========================================================\n") f.write("wirewr;\n") f.write("wirerd;\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("//==========================================================\n") f.write("// apb bus ctrlend/*}}}*/\n") f.write("//==========================================================\n") f.write("\n") f.write("//==========================================================\n") f.write("// localparamstart/*{{{*/\n") f.write("//==========================================================\n") f.write("".join(localparam_str)) f.write("//==========================================================\n") f.write("// localparamend/*}}}*/\n") f.write("//==========================================================\n") f.write("\n") f.write("//==========================================================\n") f.write("// reg and wirestart/*{{{*/\n") f.write("//==========================================================\n") f.write("".join(reg_wire_str)) f.write("//==========================================================\n") f.write("// reg and wireend/*}}}*/\n") f.write("//==========================================================\n") f.write("\n") f.write("//==========================================================\n") f.write("// write signal genstart/*{{{*/\n") f.write("//==========================================================\n") f.write("".join(write_signal_str)) f.write("//==========================================================\n") f.write("// write signal genend/*}}}*/\n") f.write("//==========================================================\n") f.write("\n") f.write("".join(reg_str)) f.write("//==========================================================\n") f.write("// rdatastart/*{{{*/\n") f.write("//==========================================================\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("//==========================================================\n") f.write("// rdataend/*}}}*/\n") f.write("//==========================================================\n") f.write("\n") f.write("endmodule\n") f.close() # ========================================================== #gen reg fileend#}}} # ========================================================== 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, )
如果生成不了或是錯誤可能是下面幾點
1.列表檔名不是<name>_reg_list
2.列表檔案中列表名和檔案不一致
3.程式碼中生成目錄為 "../reg_file/<name>_reg_cfg.v",需要有相應的目錄才能生成
4.是不是python版本問題
如果有問題或是有想探討的可以加我微信:anyuexiu