Spyglass 是一个重要的CDC检查工具,主要的功能是对SOC进行抽象,生成sgdc文件帮助进行RTL分析,CDC分析,功耗分析等。一个完整的SOC工程 可能会生成 数十个sgdc文件,每一个文件 行数达到上万行,人工分析sgdc文件过于麻烦, 需要借助于脚本辅助分析。
python版本: python 3.7
用到的工具:正则表达式
任务目标:分析所有的sdgc文件,生成同名的csv文件,csv文件中详细表述抽象端口的信息
文件的预处理
首先需要以只读模式打开Spyglass生成的sgdc文件,不可以破坏原有的sgdc文件,打开sgdc文件后有大量的约束换行情况,具体如下:
clock -name "xxx" -tag xxx -domain d7 -period 40000 -edge "0" \
"20000"
clock -name "xxx" -tag xxx \
-domain d0 -period 1666.666626 -edge "0" "833.333313"
clock -name "xxx" -tag xxx \
-domain d1 -period 1250 -edge "0" "625"
clock -name "xxx" -tag xxx -domain d8 -period 1000 -edge "0" \
"500"
为了方便处理,需要将一句约束放到同一行,也就是将 "\\n"更换为一个空格’ ',具体代码如下:
def sgdc_func(path,file_name):
file1 = open(path + '/' + file_name,'r')
file1_temp_name = file_name[:-5] + '_revised' + '.sgdc'
file1_temp = open(file1_temp_name,'w+')
for line in file1:
file1_temp.write(line.replace('\\\n',' ')) # replace \ and \n with ' '
file1_temp.seek(0,0) # relocate the file pointer to file head
file1_lines = file1_temp.readlines()
从上述代码可以看到创建了一个新文件,用w+模式打开,这个文件用于存储 去掉约束换行后的新文件。
file1_lines是一个列表,元素是sgdc文件每一行的约束,元素类型是字符串。
关键字匹配
最重要的部分就是根据 sgdc文件中的关键字来进行抽取,csv文件的表头信息如下:
file2_name = file_name[:-5] + '.csv'
file2 = open(file2_name,"w")
file2.write(','.join(("name","width/clk/reset","module/tag","clock/domain/source","direction/period/divide/value")) + '\n')
name是端口名,有端口、时钟、复位三种情况;
width/clk/reset标注端口的位宽,区别clk与generated_clk,标注reset;
module/tag标注端口所属的模块,标注时钟的tag;
clock/domain/source标注端口受到哪个时钟约束,标注时钟所属的时钟域,标注generated_clk的source;
direction/period/divide/value标注端口的方向,标注时钟的周期,标注分频的系数,标注复位的值;
abstract_port
默认一个sdgc文件中所有的端口属于同一个module;
关键字abstract_port表明这句话在约束一个端口,代码如下:
num = 0
if file1_lines: # file1_lines is a list
for item in range(len(file1_lines)):
if(re.search("abstract_port -ports",file1_lines[item])): # key word abstract_port
匹配模式是 abstract_port -ports,这是因为一些注释中也会出现abstract_port,-ports 后面跟着端口的名字;
计算位宽比较麻烦,因为 sgdc文件 的端口位宽有多种写法,如下:
abstract_port -ports "xxx" -clock "xxx" -module "xxx"
abstract_port -ports "xxx[0]" -clock "xxx" -module "xxx"
abstract_port -ports "xxxx[1:3]" -clock "xxxx" -module "xxx"
abstract_port -ports "xxx[6:4]" -clock "xxx" -module "xxx" -related_ports xxx[0] xxx[1]
总结规律: 多位宽一定会有[],但是有[]不一定是多位宽,因为-related_ports的参数 也可能包含[],不带[]一定是单位宽,带了[]也可能是单位宽;
python处理时将多位宽和单位宽分开处理,一个关键的匹配模式是:
if re.search(r"(?<=(-ports ))\s*\"?\w*\[\d*:?(\d*)?\][^ \b]",file1_lines[item]): # need to merge the port [A:B] and [A]
这个匹配模式 将related_ports中的中括号忽略了,不识别单位宽,识别了多位宽的所有形式;
将带 : 的形式与 不带 : 的形式分开处理, 带 : 的形式 分别提取出 : 左右两边的 数字, 作差以后取绝对值再加1,作为此行语句的对应位宽, 不带 : 位宽直接加1;
同时需要提取出当前行 和下一行的端口名字, 如果端口名字是一样的, 表明当前的端口描述还未结束,需要继续分析下一行,如果不一样,表明当前端口描述结束,计算出端口的位宽即可,具体代码如下:
if(re.search("abstract_port -ports",file1_lines[item])): # key word abstract_port
list1 = []
if re.search(r"(?<=(-ports ))\s*\"?\w*\[\d*:?(\d*)?\][^ \b]",file1_lines[item]): # need to merge the port [A:B] and [A]
if re.search(r"(?<=(-ports ))\s*\"?\w*\[\d*:(\d*)?\][^ \b]",file1_lines[item]): # in the form of A:B
str1 = re.search(r"(?<=(-ports ))\s*\"?\w*\[\d*:(\d*)?\][^ \b]",file1_lines[item]).group()
str1 = str1.strip().strip('\"')
str2 = re.search(r"(?<=(\[))[^:]*",str1).group()
str3 = re.search(r"(?<=(:))[^\]]*",str1).group()
num_temp = abs(int(str3) - int(str2)) + 1
num += num_temp
else:
num += 1 # in the form of [A]
str1 = re.search(r"(?<=(-ports ))\s*\"?\w*\[\d*:?(\d*)?\][^ \b]",file1_lines[item]).group() # str1 needs to be deassigned
str1 = str1.strip().strip('\"')
str1 = re.match(r"[A-Za-z0-9_]*[^\[:\]]",str1).group() # the port name match not search
if re.search(r"(?<=(-ports ))\s*\"?\w*\[\d*:?(\d*)?\][^ \b]",file1_lines[item+1]):
str2 = re.search(r"(?<=(-ports ))\s*\"?\w*\[\d*:?(\d*)?\][^ \b]",file1_lines[item+1]).group()
str2 = str2.strip().strip('\"')
str2 = re.match(r"[A-Za-z0-9_]*[^\[:\]]",str2).group()
else:
str2 = ' '
if(str1 == str2):
continue
else:
list1.append(str1)
list1.append(str(num))
num = 0
单位宽语句的处理,直接将位宽加1即可!
多余空格和引号的处理
实际分析sgdc文件时,发现 -ports等所有关键词与后面的参数之间,未必是固定一个空格,可能存在若干个不定数量的空格,且参数有可能用引号包裹,也有可能没有引号包裹,所以匹配模式的通用形式是
(?<=(-module ))\s*\"?[^ \"\b]*
-xxx表明关键字,但是 后面必须要有\s*,否则会漏匹配一些语句,且python的正则表达式不支持将 不定长的内容用 ?<= 修饰,其实一般的正则表达式解释器是支持的,\"?也很重要,因为参数可能用 " 包裹,也有可能没有引号!
匹配完成后一定要去掉多余的空格和引号,方法为:
str2 = str2.strip().strip('\"')
output与clock -name与 reset
这两个的处理方法与abstract_port接近,但是clock -name关键字会同时匹配到clock -name与generated_clock -name,所以还需要再次进行区分
多文件处理
import re
import os
from sgdc_func import sgdc_func
path = 'your_path'
path_list = os.listdir(path)
path_list_sgdc = []
for item in path_list:
if re.search(r'(.*\.sgdc)[^ \.swp]?',item):
if re.search(r'(.*\.sgdc)[^ \.swp]?',item).group() == item:
path_list_sgdc.append(item)
print("There are %d sgdc files!\n" %len(path_list_sgdc))
path_list_sgdc.sort()
首先使用os.listdir()函数获取指定路径下所有的 文件名列表,注意区别sgdc格式的文件和sgdc.swp格式的文件。