使用软件及版本
- 脚本部分
- Python3
- openpyxl
- VCS版本2018的ralgen
- EXCEL
一,使用EXCEL做好寄存器描述表格
简单的示例如下
二,编写脚本生成.ralf文件(以Python为例)
(1)首先通过openpyxl读取EXCEL表格
workbook = openpyxl.load_workbook('reg.xlsx') # 返回一个workbook数据类型的值
(2)获得有效表格的整体大小即最大行和列
sheet = workbook['Sheet1'] # 获取某一个表
print(sheet.dimensions) # 获取表格的尺寸大小
row_num = sheet.max_row # 行
line_num = sheet.max_column # 列
(3)写出寄存器的各部分所对应的列,字母是用于后方访问单元格
reg_column = 'A' # 各部分的所在列
address_column = 'B'
reg_access_column = 'C'
field_column = 'D'
field_access_column = 'E'
reset_value_column = 'F'
bit_end_column = 'G'
bit_start_column = 'H'
function_column = 'I'
(3)读取到寄存器名(regname)不为空(None)时先处理寄存器的部分再处理寄存器的域,因为写入.ralf文件的顺序就是先写reg再是field。
(4)按照寄存器名(regname)判断每一个寄存器有几个域(field)。通过regname之间空了多少个来判断。中间穿插了写.ralf的部分
for i in range(2,row_num+1): # i < row_num 所以要加一 第一行是描述性的
j = i
cell_data = sheet[reg_column + str(i)]
if(cell_data.value!=None): # 处理寄存器
start_row = cell_data.row
# 寄存器的相关属性
reg_name = cell_data.value
reg_addr = sheet[address_column + str(i)].value
reg_access = sheet[reg_access_column + str(i)].value
file_obj.write(" register " + reg_name+ " @" +reg_addr+" {\n")
# file_obj.write("bytes " + str(1)) # byte 没有则自动计算
print("reg name is",reg_name,"reg addr is ",reg_addr,"reg access is ",reg_access)
while(1):
j += 1
if(j < row_num):
if(sheet[reg_column + str(j)].value!=None):
end_row = j
reg_filed = end_row - start_row
print(cell_data.value,"reg filed is" ,reg_filed )
break
else:
end_row = j
reg_filed = end_row - start_row
print(cell_data.value,"reg filed is" ,reg_filed )
break
判断逻辑:
当知道regname不为空(None)时,记录当前行数,开始寻找下一个不为空的名字。注意None的第一个字符是大写
在寻找的过程中要保证不能超出最大范围
找到下一个regname不为空的行数,并与之前记录的作差,并记录下field的个数
(5)根据field的个数依次向下x行,读取field的相关信息
for field_num in range(0,reg_filed) :
fiedl_name = sheet[field_column+str(i+field_num)].value # 域的名字
print("fiedl_name is ",fiedl_name)
field_access = sheet[field_access_column+str(i+field_num)].value # 域的access
print("field access is ",field_access)
field_reset_num = sheet[reset_value_column + str(i+field_num)].value # 复位值
print("field_reset_nume ",field_reset_num)
field_bit_start = sheet[bit_start_column+str(i+field_num)].value # 起始位数
field_bit_end = sheet[bit_end_column+str(i+field_num)].value # 最终位数
field_bits = field_bit_end - field_bit_start + 1 # 共计多少bit
print("bit start " , field_bit_start , "end " ,field_bit_end ,"bits " ,field_bits)
file_obj.write(" field " + str(fiedl_name)+" {\n")
file_obj.write(" bits " + str(field_bits)+";\n")
file_obj.write(" reset " + str(field_reset_num)+";\n")
file_obj.write(" access " + str(field_access).lower()+";\n") # ralgen 区分大小写
file_obj.write(" }\n")
file_obj.write(" }\n")
print("out for ",i)
中间穿插了大量的写文件操作,并且为了.ralf文件内的风格添加了大量空格
最后一行的out for是为了调试时查看是否跳出一次for循环
可以打印以下内容 方便调试。
完整代码如下:
import os
import openpyxl
dst_filename = 'mgc_uart_reg.ralf'
workbook = openpyxl.load_workbook('reg.xlsx') # 返回一个workbook数据类型的值
file_obj = open(dst_filename,'w') # 打开目标文件
print(workbook.sheetnames) # 打印Excel表中的所有表
sheet = workbook['Sheet1'] # 获取某一个表
print(sheet.dimensions) # 获取表格的尺寸大小
row_num = sheet.max_row # 行
line_num = sheet.max_column # 列
reg_column = 'A' # 各部分的所在列
address_column = 'B'
reg_access_column = 'C'
field_column = 'D'
field_access_column = 'E'
reset_value_column = 'F'
bit_end_column = 'G'
bit_start_column = 'H'
function_column = 'I'
# 这里只有一个block所在在循环外面写
file_obj.write("block uart_reg_block { \n")
file_obj.write(" bytes " + str(11)+';\n') # byte
for i in range(2,row_num+1): # i < row_num 所以要加一 第一行是描述性的
j = i
cell_data = sheet[reg_column + str(i)]
if(cell_data.value!=None): # 处理寄存器域
start_row = cell_data.row
# 寄存器的相关属性
reg_name = cell_data.value
reg_addr = sheet[address_column + str(i)].value
reg_access = sheet[reg_access_column + str(i)].value
file_obj.write(" register " + reg_name+ " @" +reg_addr+" {\n")
# file_obj.write("bytes " + str(1)) # byte 没有则自动计算
print("reg name is",reg_name,"reg addr is ",reg_addr,"reg access is ",reg_access)
while(1):
j += 1
if(j < row_num):
if(sheet[reg_column + str(j)].value!=None):
end_row = j
reg_filed = end_row - start_row
print(cell_data.value,"reg filed is" ,reg_filed )
break
else:
end_row = j
reg_filed = end_row - start_row
print(cell_data.value,"reg filed is" ,reg_filed )
break
for field_num in range(0,reg_filed) :
fiedl_name = sheet[field_column+str(i+field_num)].value # 域的名字
print("fiedl_name is ",fiedl_name)
field_access = sheet[field_access_column+str(i+field_num)].value # 域的access
print("field access is ",field_access)
field_reset_num = sheet[reset_value_column + str(i+field_num)].value # 复位值
print("field_reset_nume ",field_reset_num)
field_bit_start = sheet[bit_start_column+str(i+field_num)].value # 起始位数
field_bit_end = sheet[bit_end_column+str(i+field_num)].value # 最终位数
field_bits = field_bit_end - field_bit_start + 1 # 共计多少bit
print("bit start " , field_bit_start , "end " ,field_bit_end ,"bits " ,field_bits)
file_obj.write(" field " + str(fiedl_name)+" {\n")
file_obj.write(" bits " + str(field_bits)+";\n")
file_obj.write(" reset " + str(field_reset_num)+";\n")
file_obj.write(" access " + str(field_access).lower()+";\n") # ralgen 区分大小写
file_obj.write(" }\n")
file_obj.write(" }\n")
print("out for ",i)
file_obj.write("}")
file_obj.close()
print("clear")
SystemExit
最终生成的.ralf文件效果:
三, 通过ralgen的命令生成寄存器模型
文件格式随手一搜就有,注意access后的操作类型如 rw 不能大写,所以在Python中用lower()函数将字符串全部变为小写
以下是本次实验用到的
(1)需要使用-l 指明语言
-l sv
(2)指明最顶层的block
-t uart_reg_block
(3)使用64位操作系统
-full64
(4)uvm使用
-uvm
(5)生成对应的html
-gen_html
(6)需要的ralf文件
mgc_uart_reg.ralf
(7)输出文件名
-o mgc_rm
(8)整个命令
ralgen -l sv -t uart_reg_block -full64 -uvm -gen_html mgc_uart_reg.ralf -o mgc_rm
四,生成的文件
(1)HTML文件
(2)寄存器模型的sv文件
有几个地方我们需要注意一下:
- 寄存器类名都为ral__( 所属block名 )_寄存器名
- 操作类型RO变成大写了
- 会在block里将所有field也表示出来
- UVM_REG_ADDR_WIDTH需要自己写
- 上面ralf文件中寄存器地址是@+0x?? 但生成的文件中已经变成’h??了。证明ralgen可以识别0x?? 并转换成需要的格式。并不用像大多数博客写的@'h??
2023.7.16 更
以上脚本仍然有些小问题:(1)无法应对有一个寄存器中有多个reserve域(2)configure函数中的0X??会报错
解决方法:
(1): 开辟一个数组,存放该寄存器内已知的域,在写之前读出已知域,如有重名加后缀 ( _1 _2)
(2): 通过字符截位,将读出的复位值去掉0x前缀
field_reset_num_str = sheet[reset_value_column + str(i+field_num)].value# 复位值
field_reset_num = str(field_reset_num_str)[2:]