From adb2377ee228a6ac1f149769c024c33c1233bea9 Mon Sep 17 00:00:00 2001 From: leeyunlong <1203701249@qq.com> Date: Tue, 10 Jun 2025 11:29:27 +0800 Subject: [PATCH] add the python scripts --- Scripts/.vscode/launch.json | 19 ++ Scripts/do_apb_file.py | 154 +++++++++ Scripts/gen_apb_file.py | 658 ++++++++++++++++++++++++++++++++++++ Scripts/template.xls | Bin 0 -> 49664 bytes Scripts/test.xls | Bin 0 -> 37888 bytes 5 files changed, 831 insertions(+) create mode 100644 Scripts/.vscode/launch.json create mode 100644 Scripts/do_apb_file.py create mode 100644 Scripts/gen_apb_file.py create mode 100644 Scripts/template.xls create mode 100644 Scripts/test.xls diff --git a/Scripts/.vscode/launch.json b/Scripts/.vscode/launch.json new file mode 100644 index 0000000..c4dfd39 --- /dev/null +++ b/Scripts/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + + + { + "name": "Python Debugger: Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "args": ["test.xls"] + } + ] +} \ No newline at end of file diff --git a/Scripts/do_apb_file.py b/Scripts/do_apb_file.py new file mode 100644 index 0000000..d97463d --- /dev/null +++ b/Scripts/do_apb_file.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import sys +import xlrd +import xlwt +import math + +def check(): + if(len(sys.argv) < 2): + print("[Error]:Not have input file") + print("Usage : %s .xlsx"%(sys.argv[0])) + sys.exit(1) + + if(sys.argv[1]=='-help'): + print("Usage : %s .xlsx"%(sys.argv[0])) + sys.exit(0) + + if(os.path.exists(sys.argv[1])==False): + print(f"[Error]: 文件 {sys.argv[1]} 不存在") + print("[Error]:Not such file") + sys.exit(1) + +############################################################################## +#########Main logic +############################################################################## +def process_OffsetAddress_and_RegName_col(file_path, sheet_name): + """ + 处理OffsetAddress and 列中的"charu"内容并写回Excel(.xls) + 使用纯xlwt库实现 + """ + # 读取原始Excel文件 + rb = xlrd.open_workbook(file_path) + sheet_index = rb.sheet_names().index(sheet_name) + sheet = rb.sheet_by_index(sheet_index) + + # 获取OffsetAddress列的索引 + offset_col = None + regname_col = None + for col in range(sheet.ncols): + if sheet.cell(0, col).value == 'OffsetAddress': + offset_col = col + if sheet.cell(0, col).value == 'RegName': + regname_col = col + if offset_col is not None and regname_col is not None: + break + + pattern_row = 0 + if offset_col is None: + print("[Error]: 未找到OffsetAddress列") + return False + else: + for row in range(sheet.nrows): + if sheet.cell(row, offset_col).value == 'offset' or sheet.cell(row, regname_col).value == 'regname': + pattern_row = row + print("pattern_row is : %d"%(pattern_row)) + break + + # 创建新的Excel工作簿 + wb = xlwt.Workbook() + ws = wb.add_sheet(sheet_name) + + # The cell Not copied logic + first_pattern_row = 0 + for row in range(sheet.nrows): + for col in range(sheet.ncols): + # > OffsetAddress and RegName_col col not copied + if (row >= pattern_row and (col == offset_col or col == regname_col)) or (sheet.cell(row, col).value == 'offset' or sheet.cell(row, col).value == 'regname'): + continue + # The Bits col not copied logic + ws.write(row, col, sheet.cell(row, col).value) + + wb.save(file_path) + print("Has copied all data to new file: %s"%(file_path)) + + last_valid_value = None + first_charu_found = False + modified = False + + for row in range(1, sheet.nrows): # 跳过表头 + cell_value = sheet.cell(row, offset_col).value # offset_col is the offsetAddress column + + if row >= pattern_row and cell_value and str(cell_value).strip(): + if not first_charu_found and last_valid_value is not None: + first_charu_found = True + # 将第一个"charu"替换为上一个值加4 + new_value = int(last_valid_value, 16) + 4 + ws.write(row, offset_col, f"0x{new_value:02X}") + last_valid_value = f"0x{new_value:02X}" + modified = True + elif first_charu_found and last_valid_value is not None: + # 后续"charu"继续加4 + new_value = int(last_valid_value, 16) + 4 + ws.write(row, offset_col, f"0x{new_value:02X}") + last_valid_value = f"0x{new_value:02X}" + modified = True + elif cell_value and str(cell_value).strip(): + # 记录最后一个有效值 + last_valid_value = cell_value + + last_reg = None + + for row in range(1, sheet.nrows): + cell_value = sheet.cell(row, regname_col).value + + if row >= pattern_row and cell_value and str(cell_value).strip(): + if last_reg: + try: + num = int(last_reg.split("_")[1]) + 1 + new_reg = f"reg_{num:02d}" + ws.write(row, regname_col, new_reg) + last_reg = new_reg + modified = True + except (IndexError, ValueError): + print(f"Invalid Reg Name: {last_reg}") + elif cell_value and str(cell_value).startswith('reg_'): + last_reg = cell_value # 记录最后一个有效的reg名称 + + # Ensure whether the CurrentFile is modified + if modified: + wb.save(file_path) + print(f"The updated file has been loaded into {file_path}") + else: + print("No modifications were made to the file.") + + return modified + +################################################################################################### +## +################################################################################################### + +if __name__ == "__main__": + check() + file_path = sys.argv[1] + book = xlrd.open_workbook(file_path) + sheets_num = len(book.sheet_names()) + print("This is OK,sheets_num is : %d" % sheets_num) + + for index in range(sheets_num): + sheet = book.sheet_by_index(index) + print("Sheet Name: %s"%(sheet.name)) + print("Rows: %d, Cols: %d"%(sheet.nrows, sheet.ncols)) + #for row in range(sheet.nrows): + # for col in range(sheet.ncols): + # cell_value = sheet.cell_value(row, col) + # if isinstance(cell_value, str): + # cell_value = cell_value.strip() + # print(f"Cell[{row}, {col}]: {cell_value}") + + + # process_OffsetAddress_and_RegName_col(file_path, sheet.name) + + print("\n") \ No newline at end of file diff --git a/Scripts/gen_apb_file.py b/Scripts/gen_apb_file.py new file mode 100644 index 0000000..f590a5d --- /dev/null +++ b/Scripts/gen_apb_file.py @@ -0,0 +1,658 @@ +#!/usr/bin/env python +# coding: utf-8 + +import xlrd +import re +import os +import sys +import math + +# ========================================================== +# func process excel start#{{{ +# ========================================================== +def nullUp2Valid(p_sheet,p_row,p_col):#如果cell(row,col)为空,则用往上的格代替 + if(p_sheet.cell(p_row,p_col).ctype!=0): + return p_sheet.cell(p_row,p_col).value + else: + return nullUp2Valid(p_sheet,p_row-1,p_col) + +def getValueCol(p_sheet,p_value):#返回匹配值出现的第一个列数 + for row in range (p_sheet.nrows): + for col in range (p_sheet.ncols): + if(p_sheet.cell(row,col).value==p_value): + return col +# ========================================================== +# func process excel end#}}} +# ========================================================== + +# ========================================================== +# func process bit/bus/width start#{{{ +# ========================================================== +def bit2width(var1): #input '[8:7]' return 2;input without ':' return 1 + if(":" in var1): + var1=var1.replace('[','') + var1=var1.replace(']','') + var1=var1.split(':') + var1=int(var1[0])-int(var1[1])+1 + return var1 + else: + return 1 + +def width2bus(var1): #input 2 return '[1:0]'; input 1 return ' ' + if(var1==1): + return '' + else: + return '['+str(var1-1)+':0]' + +def bus_width(var1): + var1=bit2width(var1) + var1=width2bus(var1) + return var1 +# ========================================================== +# func process bit/bus/width end#}}} +# ========================================================== + +#def wr_block +def wr_block(p_reg,p_fld,p_rst,p_bit): + wr_str=[] + wr_str.append("always@(posedge clk or negedge rst_n) begin\n") + wr_str.append(" if(!rst_n) begin\n") + wr_str.append(" %s <= %s'%s;\n"%(p_fld,bit2width(p_bit),p_rst)) + wr_str.append(" end\n") + wr_str.append(" else if(%s_wr) begin\n"%(p_reg.lower())) + wr_str.append(" %s <= pwdata%s;\n"%(p_fld,p_bit)) + wr_str.append(" end\n") + wr_str.append("end\n") + return wr_str + +#def wrc_block +def wrc_block(p_reg,p_fld,p_rst,p_bit): + wrc_str=[] + wrc_str.append("always@(posedge clk or negedge rst_n) begin\n") + wrc_str.append(" if(!rst_n) begin\n") + wrc_str.append(" %s <= %s'%s;\n"%(p_fld,bit2width(p_bit),p_rst)) + wrc_str.append(" end\n") + wrc_str.append(" else if(%s_wr) begin\n"%(p_reg.lower())) + wrc_str.append(" %s <= pwdata%s;\n"%(p_fld,p_bit)) + wrc_str.append(" end\n") + wrc_str.append(" else if(%s_wrc_clr) begin\n"%(p_fld.lower())) + wrc_str.append(" %s <= %s_wrc_clr_val;\n"%(p_fld,p_fld)) + wrc_str.append(" end\n") + wrc_str.append("end\n") + return wrc_str + +#def wrs_block +def wrs_block(p_reg,p_fld,p_rst,p_bit): + wrs_str=[] + wrs_str.append("always@(posedge clk or negedge rst_n) begin\n") + wrs_str.append(" if(!rst_n) begin\n") + wrs_str.append(" %s <= %s'%s;\n"%(p_fld,bit2width(p_bit),p_rst)) + wrs_str.append(" end\n") + wrs_str.append(" else if(%s_wr) begin\n"%(p_reg.lower())) + wrs_str.append(" %s <= pwdata%s;\n"%(p_fld,p_bit)) + wrs_str.append(" end\n") + wrs_str.append(" else if(%s_wrs_set) begin\n"%(p_fld.lower())) + wrs_str.append(" %s <= %s_wrs_set_val;\n"%(p_fld,p_fld)) + wrs_str.append(" end\n") + wrs_str.append("end\n") + return wrs_str + +#def wo_block +def wo_block(p_reg,p_fld,p_rst,p_bit): + wo_str=[] + wo_str.append("always@(posedge clk or negedge rst_n) begin\n") + wo_str.append(" if(!rst_n) begin\n") + wo_str.append(" %s <= %s'%s;\n"%(p_fld,bit2width(p_bit),p_rst)) + wo_str.append(" end\n") + wo_str.append(" else if(%s_wr) begin\n"%(p_reg.lower())) + wo_str.append(" %s <= pwdata%s;\n"%(p_fld,p_bit)) + wo_str.append(" end\n") + wo_str.append("end\n") + return wo_str + +#def w1_block +def w1_block(p_reg,p_fld,p_rst,p_bit): + w1_str=[] + w1_str.append("always@(posedge clk or negedge rst_n) begin\n") + w1_str.append(" if(!rst_n) begin\n") + w1_str.append(" %s_w1_done <= 1'b0;\n"%(p_fld)) + w1_str.append(" end\n") + w1_str.append(" else if(%s_wr) begin\n"%(p_reg.lower())) + w1_str.append(" %s_w1_done <= 1'b1;\n"%(p_fld)) + w1_str.append(" end\n") + w1_str.append("end\n") + w1_str.append("always@(posedge clk or negedge rst_n) begin\n") + w1_str.append(" if(!rst_n) begin\n") + w1_str.append(" %s <= %s'%s;\n"%(p_fld,bit2width(p_bit),p_rst)) + w1_str.append(" end\n") + w1_str.append(" else if(%s_wr && (!%s_w1_done)) begin\n"%(p_reg.lower(),p_fld.lower())) + w1_str.append(" %s <= pwdata%s;\n"%(p_fld,p_bit)) + w1_str.append(" end\n") + w1_str.append("end\n") + return w1_str + +#def wo1_block +def wo1_block(p_reg,p_fld,p_rst,p_bit): + wo1_str=[] + wo1_str.append("always@(posedge clk or negedge rst_n) begin\n") + wo1_str.append(" if(!rst_n) begin\n") + wo1_str.append(" %s_wo1_done <= 1'b0;\n"%(p_fld)) + wo1_str.append(" end\n") + wo1_str.append(" else if(%s_wr) begin\n"%(p_reg.lower())) + wo1_str.append(" %s_wo1_done <= 1'b1;\n"%(p_fld)) + wo1_str.append(" end\n") + wo1_str.append("end\n") + wo1_str.append("always@(posedge clk or negedge rst_n) begin\n") + wo1_str.append(" if(!rst_n) begin\n") + wo1_str.append(" %s <= %s'%s;\n"%(p_fld,bit2width(p_bit),p_rst)) + wo1_str.append(" end\n") + wo1_str.append(" else if(%s_wr && (!%s_wo1_done)) begin\n"%(p_reg.lower(),p_fld.lower())) + wo1_str.append(" %s <= pwdata%s;\n"%(p_fld,p_bit)) + wo1_str.append(" end\n") + wo1_str.append("end\n") + return wo1_str + +################## +def rd_block(p_reg,p_address): + rd_str=[] + p_address = p_address.replace('0x','8\'h').replace('0X','8\'h') + rd_str.append("%8s%s : prdata = %-10s;\n"%('',p_address,p_reg.upper())) + return rd_str + + +# ========================================================== +# int_logic start#{{{ +# ========================================================== +def int_logic(p_sheet): + int_str = [] + fld_col = getValueCol(p_sheet,'FieldName') + for row in range (p_sheet.nrows)[1:]: + fld_name = p_sheet.cell(row,fld_col).value.lower() + if(fld_name.endswith('_int')): + int_str.append(" | (%s & %s_en)"%(fld_name,fld_name)) + return int_str +# ========================================================== +# int_logic end#}}} +# ========================================================== + +# ========================================================== +# gen_reg_hdl start#{{{ +# ========================================================== +def gen_reg_hdl(p_sheet,ModuleName): + base_col = getValueCol(p_sheet,'BaseAddress') + base_value = p_sheet.cell(1,base_col).value.replace('0x','32\'h').replace('0X','32\'h') + + width_col = getValueCol(p_sheet,'Width') + width_value = int(p_sheet.cell(1,width_col).value) + data_bus_width = width2bus(width_value) + + + reg_col = getValueCol(p_sheet,'RegName') + fld_col = getValueCol(p_sheet,'FieldName') + rst_col = getValueCol(p_sheet,'ResetValue') + bit_col = getValueCol(p_sheet,'Bits') + access_col = getValueCol(p_sheet,'Access') + adr_col = getValueCol(p_sheet,'OffsetAddress') + + fo=open("%s_apb_cfg.v"%(ModuleName),"w") + fo.write("module %s_apb_cfg ("%(ModuleName)) + fo.write("\n"+16*" "+" clk") + fo.write("\n"+16*" "+",rst_n") + fo.write("\n"+16*" "+",pwrite") + fo.write("\n"+16*" "+",psel") + fo.write("\n"+16*" "+",penable") + fo.write("\n"+16*" "+",paddr") + fo.write("\n"+16*" "+",pwdata") + fo.write("\n"+16*" "+",prdata") + #insert other port + as_is_list = ['RW','WRC','WRS','WO','W1','WO1'] + + w1c_list =['W1C','W1CRS'] + w0c_list =['W0C','W0CRS'] + w1s_list =['W1S','W1SRC'] + w0s_list =['W0S','W0SRC'] + w1t_list =['W1T'] + w0t_list =['W0T'] + + wc_list = ['WC','WCRS','WOC'] + ws_list = ['WS','WSRC','WOS'] + rc_list = ['RC','WRC','WSRC','W1SRC','W0SRC'] + rs_list = ['RS','WRS','WCRS','W1CRS','W0CRS'] + + + for row in range (p_sheet.nrows)[1:]: + fld_name = p_sheet.cell(row,fld_col).value.lower() + fld_type = nullUp2Valid(p_sheet,row,access_col) + if(fld_name.lower() != 'reserved'): + if(fld_type in as_is_list): + fo.write("\n"+16*" "+","+fld_name) + else: + fo.write("\n"+16*" "+","+fld_name) + + if(fld_type in w1c_list): + fo.write("\n"+16*" "+",%s_%s_clr"%(fld_name,fld_type.lower())) + fo.write("\n"+16*" "+",%s_%s_clr_val"%(fld_name,fld_type.lower())) + if(fld_type in w0c_list): + fo.write("\n"+16*" "+",%s_%s_clr"%(fld_name,fld_type.lower())) + fo.write("\n"+16*" "+",%s_%s_clr_val"%(fld_name,fld_type.lower())) + if(fld_type in w1s_list): + fo.write("\n"+16*" "+",%s_%s_set"%(fld_name,fld_type.lower())) + fo.write("\n"+16*" "+",%s_%s_set_val"%(fld_name,fld_type.lower())) + if(fld_type in w0s_list): + fo.write("\n"+16*" "+",%s_%s_set"%(fld_name,fld_type.lower())) + fo.write("\n"+16*" "+",%s_%s_set_val"%(fld_name,fld_type.lower())) + if(fld_type in w1t_list): + fo.write("\n"+16*" "+",%s_%s_tog"%(fld_name,fld_type.lower())) + fo.write("\n"+16*" "+",%s_%s_tog_val"%(fld_name,fld_type.lower())) + if(fld_type in w0t_list): + fo.write("\n"+16*" "+",%s_%s_tog"%(fld_name,fld_type.lower())) + fo.write("\n"+16*" "+",%s_%s_tog_val"%(fld_name,fld_type.lower())) + + + if(fld_type in wc_list): + fo.write("\n"+16*" "+",%s_%s_clr"%(fld_name,fld_type.lower())) + fo.write("\n"+16*" "+",%s_%s_clr_val"%(fld_name,fld_type.lower())) + if(fld_type in rc_list and fld_type != 'WRC'): + fo.write("\n"+16*" "+",%s_%s_clr"%(fld_name,fld_type.lower())) + fo.write("\n"+16*" "+",%s_%s_clr_val"%(fld_name,fld_type.lower())) + if(fld_type in ws_list): + fo.write("\n"+16*" "+",%s_%s_set"%(fld_name,fld_type.lower())) + fo.write("\n"+16*" "+",%s_%s_set_val"%(fld_name,fld_type.lower())) + if(fld_type in rs_list and fld_type != 'WRS'): + fo.write("\n"+16*" "+",%s_%s_set"%(fld_name,fld_type.lower())) + fo.write("\n"+16*" "+",%s_%s_set_val"%(fld_name,fld_type.lower())) + fo.write("\n"+16*" "+");") + fo.write("\n") + + + #signal direction declare + fo.write("input clk;\n") + fo.write("input rst_n;\n") + fo.write("input pwrite;\n") + fo.write("input psel;\n") + fo.write("input penable;\n") + fo.write("input [31:0] paddr;\n") + fo.write("input %-9s%s;\n"%(data_bus_width,'pwdata')) + fo.write("output %-9s%s;\n"%(data_bus_width,'prdata')) + for row in range (p_sheet.nrows)[1:]: + fld_name = p_sheet.cell(row,fld_col).value.lower() + bit = p_sheet.cell(row,bit_col).value + fld_type = nullUp2Valid(p_sheet,row,access_col) + if(fld_name.lower() != 'reserved'): + if(fld_type in as_is_list): + fo.write("output %-9s%s;\n"%(bus_width(bit),fld_name)) + else: + fo.write("input %-9s%s;\n"%(bus_width(bit),fld_name)) + + if(fld_type in w1c_list): + fo.write("output %-9s%s_%s_clr;\n"%('',fld_name,fld_type.lower())) + fo.write("output %-9s%s_%s_clr_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in w0c_list): + fo.write("output %-9s%s_%s_clr;\n"%('',fld_name,fld_type.lower())) + fo.write("output %-9s%s_%s_clr_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in w1s_list): + fo.write("output %-9s%s_%s_set;\n"%('',fld_name,fld_type.lower())) + fo.write("output %-9s%s_%s_set_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in w0s_list): + fo.write("output %-9s%s_%s_set;\n"%('',fld_name,fld_type.lower())) + fo.write("output %-9s%s_%s_set_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in w1t_list): + fo.write("output %-9s%s_%s_tog;\n"%('',fld_name,fld_type.lower())) + fo.write("output %-9s%s_%s_tog_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in w0t_list): + fo.write("output %-9s%s_%s_tog;\n"%('',fld_name,fld_type.lower())) + fo.write("output %-9s%s_%s_tog_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + + if(fld_type in wc_list): + fo.write("output %-9s%s_%s_clr;\n"%('',fld_name,fld_type.lower())) + fo.write("output %-9s%s_%s_clr_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in rc_list and fld_type != 'WRC'): + fo.write("output %-9s%s_%s_clr;\n"%('',fld_name,fld_type.lower())) + fo.write("output %-9s%s_%s_clr_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in ws_list): + fo.write("output %-9s%s_%s_set;\n"%('',fld_name,fld_type.lower())) + fo.write("output %-9s%s_%s_set_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in rs_list and fld_type != 'WRS'): + fo.write("output %-9s%s_%s_set;\n"%('',fld_name,fld_type.lower())) + fo.write("output %-9s%s_%s_set_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + + ####################################20200713 + #signal type declare + fo.write("wire clk;\n") + fo.write("wire rst_n;\n") + fo.write("wire pwrite;\n") + fo.write("wire psel;\n") + fo.write("wire penable;\n") + fo.write("wire [31:0] paddr;\n") + fo.write("wire %-11s%s;\n"%(data_bus_width,'pwdata')) + fo.write("reg %-11s%s;\n"%(data_bus_width,'prdata')) + for row in range (p_sheet.nrows)[1:]: + fld_name = p_sheet.cell(row,fld_col).value.lower() + bit = p_sheet.cell(row,bit_col).value + fld_type = nullUp2Valid(p_sheet,row,access_col) + if(fld_name.lower() != 'reserved'): + if(fld_type in as_is_list): + fo.write("reg %-11s%s;\n"%(bus_width(bit),fld_name)) + else: + fo.write("wire %-11s%s;\n"%(bus_width(bit),fld_name)) + + if(fld_type in w1c_list): + fo.write("wire %-11s%s_%s_clr;\n"%('',fld_name,fld_type.lower())) + fo.write("wire %-11s%s_%s_clr_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in w0c_list): + fo.write("wire %-11s%s_%s_clr;\n"%('',fld_name,fld_type.lower())) + fo.write("wire %-11s%s_%s_clr_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in w1s_list): + fo.write("wire %-11s%s_%s_set;\n"%('',fld_name,fld_type.lower())) + fo.write("wire %-11s%s_%s_set_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in w0s_list): + fo.write("wire %-11s%s_%s_set;\n"%('',fld_name,fld_type.lower())) + fo.write("wire %-11s%s_%s_set_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in w1t_list): + fo.write("wire %-11s%s_%s_tog;\n"%('',fld_name,fld_type.lower())) + fo.write("wire %-11s%s_%s_tog_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in w0t_list): + fo.write("wire %-11s%s_%s_tog;\n"%('',fld_name,fld_type.lower())) + fo.write("wire %-11s%s_%s_tog_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + + if(fld_type in wc_list): + fo.write("wire %-11s%s_%s_clr;\n"%('',fld_name,fld_type.lower())) + fo.write("wire %-11s%s_%s_clr_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in rc_list): + fo.write("wire %-11s%s_%s_clr;\n"%('',fld_name,fld_type.lower())) + fo.write("wire %-11s%s_%s_clr_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in ws_list): + fo.write("wire %-11s%s_%s_set;\n"%('',fld_name,fld_type.lower())) + fo.write("wire %-11s%s_%s_set_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + if(fld_type in rs_list): + fo.write("wire %-11s%s_%s_set;\n"%('',fld_name,fld_type.lower())) + fo.write("wire %-11s%s_%s_set_val;\n"%(bus_width(bit),fld_name,fld_type.lower())) + + + for row in range (p_sheet.nrows)[1:]: + fld_name = p_sheet.cell(row,fld_col).value.lower() + fld_type = nullUp2Valid(p_sheet,row,access_col) + if(fld_type == 'W1'): + fo.write("reg %-11s%s_w1_done;\n"%('',fld_name)) + if(fld_type == 'WO1'): + fo.write("reg %-11s%s_wo1_done;\n"%('',fld_name)) + + + + #reg_declared + for row in range (p_sheet.nrows)[1:]: + reg_name = nullUp2Valid(p_sheet,row,reg_col) + if(p_sheet.cell(row,adr_col).value!=''): + fo.write("wire %-11s%s;\n"%(data_bus_width,reg_name.upper())) + #reg_wr=paddr&wr + #reg_rd=paddr&rd + for row in range (p_sheet.nrows)[1:]: + reg_name = nullUp2Valid(p_sheet,row,reg_col) + fld_type = nullUp2Valid(p_sheet,row,access_col) + if(p_sheet.cell(row,adr_col).value!=''): + fo.write("wire %-11s%s_wr;\n"%('',reg_name.lower())) + fo.write("wire %-11s%s_rd;\n"%('',reg_name.lower())) + + #insert apb-->reg_wr,reg_rd. + fo.write("wire %-11sreg_wr;\n"%('')) + fo.write("wire %-11sreg_rd;\n"%('')) + fo.write("assign reg_wr = psel & pwrite & penable;\n") + fo.write("assign reg_rd = psel & (~pwrite) & (~penable);\n") + + for row in range (p_sheet.nrows)[1:]: + reg_name = nullUp2Valid(p_sheet,row,reg_col) + fld_type = nullUp2Valid(p_sheet,row,access_col) + address = nullUp2Valid(p_sheet,row,adr_col).replace('0x','8\'h').replace('0X','8\'h') + if(p_sheet.cell(row,adr_col).value!=''): + fo.write("assign %s_wr = (paddr == %s) & reg_wr;\n"%(reg_name.lower(),base_value+' + '+address)) + fo.write("assign %s_rd = (paddr == %s) & reg_rd;\n"%(reg_name.lower(),base_value+' + '+address)) + + #################################################20200707 sheetchange + + #assign REG[1]=fld + for row in range (p_sheet.nrows)[1:]: + reg_name = nullUp2Valid(p_sheet,row,reg_col) + fld_name = p_sheet.cell(row,fld_col).value.lower() + rst_value = p_sheet.cell(row,rst_col).value + rst_value = re.search('[bodh][a-f0-9]+$',rst_value).group() + bit = p_sheet.cell(row,bit_col).value + fld_type = nullUp2Valid(p_sheet,row,access_col) + if(fld_name.lower() == 'reserved' or fld_type == 'WO' or fld_type == 'WOC' or fld_type == 'WOS' or fld_type == 'WO1'): + fo.write("assign %s%s = %s\'%s;\n"%(reg_name.upper(),bit,bit2width(bit),rst_value)) + else: + fo.write("assign %s%s = %s;\n"%(reg_name.upper(),bit,fld_name)) + #main logic for w1c + for row in range (p_sheet.nrows)[1:]: + fld_name = p_sheet.cell(row,fld_col).value.lower() + bit = p_sheet.cell(row,bit_col).value + fld_type = nullUp2Valid(p_sheet,row,access_col) + reg_name = nullUp2Valid(p_sheet,row,reg_col) + if(fld_name.lower() != 'reserved'): + if(fld_type in w1c_list): + fo.write("assign %s_%s_clr = %s_wr;\n"%(fld_name,fld_type.lower(),reg_name.lower())) + fo.write("assign %s_%s_clr_val%s =%s%s &(~pwdata%s);\n"%(fld_name,fld_type.lower(),bus_width(bit),fld_name,bus_width(bit),bit)) + if(fld_type in w0c_list): + fo.write("assign %s_%s_clr = %s_wr;\n"%(fld_name,fld_type.lower(),reg_name.lower())) + fo.write("assign %s_%s_clr_val%s =%s%s & pwdata%s;\n"%(fld_name,fld_type.lower(),bus_width(bit),fld_name,bus_width(bit),bit)) + if(fld_type in w1s_list): + fo.write("assign %s_%s_set = %s_wr;\n"%(fld_name,fld_type.lower(),reg_name.lower())) + fo.write("assign %s_%s_set_val%s =%s%s | pwdata%s;\n"%(fld_name,fld_type.lower(),bus_width(bit),fld_name,bus_width(bit),bit)) + if(fld_type in w0s_list): + fo.write("assign %s_%s_set = %s_wr;\n"%(fld_name,fld_type.lower(),reg_name.lower())) + fo.write("assign %s_%s_set_val%s =%s%s |(~pwdata%s);\n"%(fld_name,fld_type.lower(),bus_width(bit),fld_name,bus_width(bit),bit)) + if(fld_type in w1t_list): + fo.write("assign %s_%s_tog = %s_wr;\n"%(fld_name,fld_type.lower(),reg_name.lower())) + fo.write("assign %s_%s_tog_val%s =%s%s ^ pwdata%s;\n"%(fld_name,fld_type.lower(),bus_width(bit),fld_name,bus_width(bit),bit)) + if(fld_type in w0t_list): + fo.write("assign %s_%s_tog = %s_wr;\n"%(fld_name,fld_type.lower(),reg_name.lower())) + fo.write("assign %s_%s_tog_val%s =%s%s ^(~pwdata%s);\n"%(fld_name,fld_type.lower(),bus_width(bit),fld_name,bus_width(bit),bit)) + + if(fld_type in wc_list): + fo.write("assign %s_%s_clr = %s_wr;\n"%(fld_name,fld_type.lower(),reg_name.lower())) + fo.write("assign %s_%s_clr_val%s =%s'b0;\n"%(fld_name,fld_type.lower(),bus_width(bit),bit2width(bit))) + if(fld_type in rc_list): + fo.write("assign %s_%s_clr = %s_rd;\n"%(fld_name,fld_type.lower(),reg_name.lower())) + fo.write("assign %s_%s_clr_val%s =%s'b0;\n"%(fld_name,fld_type.lower(),bus_width(bit),bit2width(bit))) + if(fld_type in ws_list): + fo.write("assign %s_%s_set = %s_wr;\n"%(fld_name,fld_type.lower(),reg_name.lower())) + fo.write("assign %s_%s_set_val%s =%s'b%s;\n"%(fld_name,fld_type.lower(),bus_width(bit),bit2width(bit),pow(2,bit2width(bit))-1)) + if(fld_type in rs_list): + fo.write("assign %s_%s_set = %s_rd;\n"%(fld_name,fld_type.lower(),reg_name.lower())) + fo.write("assign %s_%s_set_val%s =%s'b%s;\n"%(fld_name,fld_type.lower(),bus_width(bit),bit2width(bit),pow(2,bit2width(bit))-1)) + + +#######################20200722########################## + #main logic reg_int + #fo.write("assign reg_int = 1'b0%s;\n"%("".join(int_logic(p_sheet)))) + #main logic Wr + for row in range (p_sheet.nrows)[1:]: + reg_name = nullUp2Valid(p_sheet,row,reg_col) + fld_name = p_sheet.cell(row,fld_col).value.lower() + rst_value = p_sheet.cell(row,rst_col).value + rst_value = re.search('[bodh][a-f0-9]+$',rst_value).group() + bit = p_sheet.cell(row,bit_col).value + fld_type = nullUp2Valid(p_sheet,row,access_col) + if(fld_name.lower() != 'reserved'): + if(fld_type == 'RW'): + fo.write("".join(wr_block(reg_name,fld_name,rst_value,bit))) + if(fld_type == 'WRC'): + fo.write("".join(wrc_block(reg_name,fld_name,rst_value,bit))) + if(fld_type == 'WRS'): + fo.write("".join(wrs_block(reg_name,fld_name,rst_value,bit))) + if(fld_type == 'WO'): + fo.write("".join(wo_block(reg_name,fld_name,rst_value,bit))) + if(fld_type == 'W1'): + fo.write("".join(w1_block(reg_name,fld_name,rst_value,bit))) + if(fld_type == 'WO1'): + fo.write("".join(wo1_block(reg_name,fld_name,rst_value,bit))) + + #main logic Rd + fo.write("always@(*) begin\n") + fo.write(" case(paddr)\n") + #fo.write("".join(rd_logic(p_sheet))) + for row in range (p_sheet.nrows)[1:]: + reg_name = nullUp2Valid(p_sheet,row,reg_col) + fld_type = nullUp2Valid(p_sheet,row,access_col) + address = nullUp2Valid(p_sheet,row,adr_col).replace('0x','8\'h').replace('0X','8\'h') + if(p_sheet.cell(row,adr_col).value!=''): + fo.write("".join(rd_block(reg_name,base_value+' + '+address))) + fo.write(" default:prdata = %s'b0;\n"%(width_value)) + fo.write(" endcase\n") + fo.write("end\n") + fo.write("endmodule") + fo.close() + print("Successfully generated %s_apb_cfg.v"%(ModuleName)) +# ========================================================== +# gen_reg_hdl end#}}} +# ========================================================== +# ========================================================== +# gen_reg_cheader start#{{{ +# ========================================================== + +def gen_reg_cheader(p_sheet,ModuleName): + base_col = getValueCol(p_sheet,'BaseAddress') + base_value = p_sheet.cell(1,base_col).value + + reg_col = getValueCol(p_sheet,'RegName') + fld_col = getValueCol(p_sheet,'FieldName') + rst_col = getValueCol(p_sheet,'ResetValue') + bit_col = getValueCol(p_sheet,'Bits') + access_col = getValueCol(p_sheet,'Access') + adr_col = getValueCol(p_sheet,'OffsetAddress') + + fo=open("%s.h"%(ModuleName),"w") + fo.write("#ifndef __TYPE_H__\n") + fo.write("#define __TYPE_H__\n") + fo.write("\n") + fo.write("#define REG32(_register_) (*(volatile unsigned int *)(_register_))\n") + fo.write("#define REG8(_register_) (*(volatile unsigned char *)(_register_))\n") + fo.write("\n") + fo.write("#endif\n") + fo.write("\n") + + fo.write("/************************** Constant Definitions *****************************/\n") + fo.write("#ifndef __%s_H__\n"%(ModuleName.upper())) + fo.write("#define __%s_H__\n"%(ModuleName.upper())) + fo.write("\n") + fo.write("#define %-19s %s\n"%(ModuleName.upper()+'_BASEADDR',base_value)) + for row in range (p_sheet.nrows)[1:]: + reg_name = nullUp2Valid(p_sheet,row,reg_col) + fld_name = p_sheet.cell(row,fld_col).value.lower() + fld_type = nullUp2Valid(p_sheet,row,access_col) + address = nullUp2Valid(p_sheet,row,adr_col)#.replace('0x','8\'h').replace('0X','8\'h') + if(p_sheet.cell(row,adr_col).value!=''): + fo.write("#define %-19s (%s_BASEADDR + %s)\n"%(reg_name.upper()+'_ADDR',ModuleName.upper(),address)) + + fo.write("\n") + fo.write("#endif\n") + fo.close() + print("Successfully generated %s.h"%(ModuleName)) + +# ========================================================== +# gen_reg_cheader end#}}} +# ========================================================== + +def gen_reg_ralf(p_sheet,ModuleName): + base_col = getValueCol(p_sheet,'BaseAddress') + base_value = p_sheet.cell(1,base_col).value + + width_col = getValueCol(p_sheet,'Width') + width_value = int(p_sheet.cell(1,width_col).value) + + reg_col = getValueCol(p_sheet,'RegName') + fld_col = getValueCol(p_sheet,'FieldName') + rst_col = getValueCol(p_sheet,'ResetValue') + bit_col = getValueCol(p_sheet,'Bits') + access_col = getValueCol(p_sheet,'Access') + adr_col = getValueCol(p_sheet,'OffsetAddress') + + fo=open("%s.ralf"%(ModuleName),"w") + fo.write("#write below command in Makefile\n") + fo.write("#ralgen:\n") + fo.write("# ralgen -l sv -uvm -t %s_regmodel %s.ralf\n"%(ModuleName,ModuleName)) + fo.write("#use 'source %s.ralf' in top.ralf\n"%(ModuleName)) + last_reg_name = '' + i = 0 + fo.write("#") + for row in reversed(range (p_sheet.nrows)[1:]): + reg_name = nullUp2Valid(p_sheet,row,reg_col) + fld_name = p_sheet.cell(row,fld_col).value.lower() + rst_value = p_sheet.cell(row,rst_col).value + rst_value = re.search('[bodh][a-f0-9]+$',rst_value).group() + bit = p_sheet.cell(row,bit_col).value + fld_type = nullUp2Valid(p_sheet,row,access_col) + if(fld_name.lower() != 'reserved'): + if(reg_name != last_reg_name): + fo.write("}\n") + fo.write("\n") + fo.write("register %s {\n"%(reg_name.upper())) + fo.write("# ToDo\n") + last_reg_name = reg_name; + fo.write(" field %s {\n"%(fld_name)) + fo.write(" bits %s;\n"%(bit2width(bit))) + fo.write(" access %s;\n"%(fld_type.lower())) + fo.write(" reset '%s;\n"%(rst_value)) + fo.write(" }\n") + + elif(fld_name.lower() == 'reserved'): + if(reg_name != last_reg_name): + fo.write("}\n") + fo.write("\n") + fo.write("register %s {\n"%(reg_name.upper())) + fo.write("# ToDo\n") + last_reg_name = reg_name; + i=i+1; + fo.write(" field reserved%s {\n"%(i)) + fo.write(" bits %s;\n"%(bit2width(bit)))#different here + fo.write(" access %s;\n"%(fld_type.lower())) + fo.write(" reset '%s;\n"%(rst_value)) + fo.write(" }\n") + fo.write("}\n") + + fo.write("\n") + fo.write("block %s_regmodel {\n"%(ModuleName)) + fo.write(" bytes %s;\n"%(width_value/8)) + for row in reversed(range (p_sheet.nrows)[1:]): + reg_name = nullUp2Valid(p_sheet,row,reg_col) + fld_name = p_sheet.cell(row,fld_col).value.lower() + rst_value = p_sheet.cell(row,rst_col).value + rst_value = re.search('[bodh][a-f0-9]+$',rst_value).group() + bit = p_sheet.cell(row,bit_col).value + address = p_sheet.cell(row,adr_col).value.replace('0x','h').replace('0X','h') + fld_type = nullUp2Valid(p_sheet,row,access_col) + if(p_sheet.cell(row,adr_col).value!=''): + fo.write(" register %-13s %-15s @'%s;\n"%(reg_name.upper(),"("+reg_name.lower()+")",address)) + + fo.write("}\n") + fo.write("\n") + fo.close() + print("Successfully generated %s.ralf"%(ModuleName)) + +#max_rows=sheet0.nrows#行数 +#max_cols=sheet0.ncols#列数 +if(len(sys.argv) < 2): + print("[Error]:Not have input file") + print("Usage : %s .xlsx"%(sys.argv[0])) + sys.exit(1) + +if(sys.argv[1]=='-help'): + print("Usage : %s .xlsx"%(sys.argv[0])) + sys.exit(0) + +if(os.path.exists(sys.argv[1])==False): + print(f"[Error]: 文件 {sys.argv[1]} 不存在") + print("[Error]:Not such file") + sys.exit(1) + + +book = xlrd.open_workbook(sys.argv[1]) +sheets_num = len(book.sheet_names()) +for index in range (sheets_num): + sheet0 = book.sheet_by_index(index) + ModuleName = sheet0.name + #ModuleName = re.search('^[a-z]+',sys.argv[1]).group()#从开头位置开始匹配返回第一个,而findall返回一个list + gen_reg_hdl(sheet0,ModuleName) + gen_reg_cheader(sheet0,ModuleName) + gen_reg_ralf(sheet0,ModuleName) +sys.exit(0) + diff --git a/Scripts/template.xls b/Scripts/template.xls new file mode 100644 index 0000000000000000000000000000000000000000..76663dbe8e1e6d74a20f85a6fd3989fec162bc6b GIT binary patch literal 49664 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 Ue0uNS=>z{q>0g&Wjwk>B09(ah@c;k- literal 0 HcmV?d00001