在VScode中自动生成Verilog仿真文件(Python)-FPGA常见问题社区-FPGA CPLD-ChipDebug

在VScode中自动生成Verilog仿真文件(Python)

一、实现功能

1、可以自动创建文件夹

2、根据Verilog文件自动生成测试文件模板(TB文件名字是)

3、自动打开生成的文件

4、自动调取modelsim仿真(后续添加)

 

、原创声明

代码分为两部份:一部份是从GITHUB上截取的代码。

另一部分是对打印的数据进行提取和加工,生产文件夹,生成文件,打开文件

等后续处理的功能,这部分是作者的原创代码。

出于尊重原创角度,现贴出第一部分代码出处的链接,请读者自行查看:

https://github.com/truecrab/VSCode_Extension_Verilog

VHDL的TB生成代码全部由作者原创,将在下期公布相关代码。

三、基本介绍

使用语言:python

环境:win10/python3.7(需要chardet,代码中有注释)

运行软件:Vscode/IDLE (Python 3.7 64-bit)(目前这个脚本还没有封装成插件)

调试最方便的就是在VScode里面安装python的插件

四、python代码

4.1 提取Verilog文件关键字的代码

##------------------------------------------------------------------------------------------------
##--
##-- Coding: UTF-8
##-- @File    :   vTbgenerator.py
##-- @Version :   1.0
##-- @Author  :   AriesOpenFPGA
##-- @Email   :   2577802337@qq.com
##-- @License :   (C)Copyright 2019-2021,AriesOpenFPGA
##-- @Time    :   2021/01/25 21:17:18
##-- Description:
##--
##------------------------------------------------------------------------------------------------

import re
import sys
import chardet #请大家尤其注意此处,调用这个是需要下载另外一个文件的
               #如果找不到,可以找群主要(Python Universal Character Encoding Detector)
import os
import time

def delComment( Text ):
    """ removed comment """
    single_line_comment = re.compile(r"//(.*)$", re.MULTILINE)# (.*) 代表匹配除换行符之外的所有字符re.MULTILINE变成每行开始和结束
    multi_line_comment  = re.compile(r"/\*(.*?)\*/",re.DOTALL)# (.*?)后面多个问号,代表非贪婪模式,也就是说只匹配符合条件的最少字符
    Text = multi_line_comment.sub('\n',Text)          #  re.DOTALL表示多行匹配
    Text = single_line_comment.sub('\n',Text)
    return Text

def delBlock( Text ) :
    """ removed task and function block """
    Text = re.sub(r'\Wtask\W[\W\w]*?\Wendtask\W','\n',Text)     #re.sub表示替换'\s+'表示空格
    Text = re.sub(r'\Wfunction\W[\W\w]*?\Wendfunction\W','\n',Text) #pattern = re.compile(r'([a-z]+) ([a-z]+)', re.I) re.I表示忽略大小写
    return Text  #\w匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'
  
        #\W匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。

def findName(inText):
    """ find module name and port list"""
    p = re.search(r'([a-zA-Z_][a-zA-Z_0-9]*)\s*',inText)
    mo_Name = p.group(0).strip()#去除group(0)的首尾空格
    return mo_Name

def paraDeclare(inText ,portArr) :
    """ find parameter declare """
    pat = r'\s'+ portArr + r'\s[\w\W]*?[;,)]'# \s匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
    ParaList = re.findall(pat ,inText)

    return ParaList

def portDeclare(inText ,portArr) :
    """find port declare, Syntax:
       input [ net_type ] [ signed ] [ range ] list_of_port_identifiers

       return list as : (port, [range])
    """
    port_definition = re.compile(
        r'\b' + portArr +
        r''' (\s+(wire|reg)\s+)* (\s*signed\s+)*  (\s*\[.*?:.*?\]\s*)*
        (?P<port_list>.*?)
        (?= \binput\b | \boutput\b | \binout\b | ; | \) )
        ''',
        re.VERBOSE|re.MULTILINE|re.DOTALL
    )

    pList = port_definition.findall(inText)

    t = []
    for ls in pList:
        if len(ls) >=2  :
            t = t+ portDic(ls[-2:])
    return t

def portDic(port) :
    """delet as : input a =c &d;
        return list as : (port, [range])
    """
    pRe = re.compile(r'(.*?)\s*=.*', re.DOTALL)

    pRange = port[0]
    pList  = port[1].split(',')#split以,为分隔符
    pList  = [ i.strip() for i in pList if i.strip() !='' ]
    pList  = [(pRe.sub(r'\1', p), pRange.strip() ) for p in pList ]

    return pList

def formatPort(AllPortList,isPortRange =1) :
    PortList = AllPortList[0] + AllPortList[1] + AllPortList[2]

    str =''
    if PortList !=[] :
        l1 = max([len(i[0]) for i in PortList])+2
        l2 = max([len(i[1]) for i in PortList])
        l3 = max(24, l1)

        strList = []
        for pl in AllPortList :
            if pl  != [] :
                str = ',\n'.join( [' '*4+'.'+ i[0].ljust(l3)
                                  + '( '+ (i[0].ljust(l1 )+i[1].ljust(l2))
                                  + ' )' for i in pl ] )
                strList = strList + [ str ]

        str = ',\n\n'.join(strList)

    return str

def formatDeclare(PortList,portArr, initial = "" ):
    str =''
    if initial !="" :
        initial = " = " + initial

    if PortList!=[] :
        str = '\n'.join( [ portArr.ljust(4) +'  '+(i[1]+min(len(i[1]),1)*'  '
                           +i[0]).ljust(36)+ initial + ' ;' for i in PortList])
    return str

def formatPara(ParaList) :
    paraDec = ''
    paraDef = ''
    if ParaList !=[]:
        s = '\n'.join( ParaList)
        pat = r'([a-zA-Z_][a-zA-Z_0-9]*)\s*=\s*([\w\W]*?)\s*[;,)]'
        p = re.findall(pat,s)

        l1 = max([len(i[0] ) for i in p])
        l2 = max([len(i[1] ) for i in p])
        paraDec = '\n'.join( ['parameter %s = %s;'
                             %(i[0].ljust(l1 +1),i[1].ljust(l2 ))
                             for i in p])
        paraDef =  '#(\n' +',\n'.join( ['    .'+ i[0].ljust(l1 +1)
                    + '( '+ i[0].ljust(l1 )+' )' for i in p])+ ')\n'
    else:
        l1 = 6
        l2 = 2
    preDec = '\n'.join( ['parameter %s = %s;\n'
                             %('PERIOD'.ljust(l1 +1), '10'.ljust(l2 ))])
    paraDec = preDec + paraDec
    return paraDec,paraDef

 

4.2 创建文件夹和在文件中写入数据

""" 打开目标文件,这里的文件需要大家自己手动给出路径,当封装为插件后则
是可以自动获取文件路径,这里仅做测试用"""

target_dir_v = "C:\\Users\\25778\\Desktop\\VScode\\test\\"

"""这里的i2c_dri.v是需要生成TB的原文件名字,需要给出名字,封装后也是自动获取"""
with open(target_dir_v + "i2c_dri.v", 'rb') as f:
    f_info =  chardet.detect(f.read())
    f_encoding = f_info['encoding']
with open(target_dir_v + "i2c_dri.v", encoding=f_encoding) as inFile:
    inText  = inFile.read()

# removed comment,task,function
inText = delComment(inText)
inText = delBlock  (inText)

# moduel ... endmodule  #
moPos_begin = re.search(r'(\b|^)module\b', inText ).end()
moPos_end   = re.search(r'\bendmodule\b', inText ).start()
inText = inText[moPos_begin:moPos_end]

name  = findName(inText)
paraList = paraDeclare(inText,'parameter')
paraDec , paraDef = formatPara(paraList)

ioPadAttr = [ 'input','output','inout']
input  =  portDeclare(inText,ioPadAttr[0])
output =  portDeclare(inText,ioPadAttr[1])
inout  =  portDeclare(inText,ioPadAttr[2])

portList = formatPort( [input , output , inout] )
input  = formatDeclare(input ,'reg', '0' )
output = formatDeclare(output ,'wire')
inout  = formatDeclare(inout ,'wire')

timescale = '`timescale  1ns / 1ps\n'
clk = '''
initial
begin
    forever #(PERIOD/2)  clk=~clk;
end'''
rst = '''
initial
begin
    #(PERIOD*2) rst_n  =  1;
end
'''

operation = '''
initial
begin

    $finish;
end
'''

def openfile(file_dir):
  os.startfile(file_dir)

"""这里的地址需要放到目标文件地址的上一级,封装成插件也是自动获取的
并且创建TB文件夹,同时创建.v文件,tb文件的命名方式是目标文件的模块名字+tb"""
target_dir = target_dir_v + "\\tb\\"
cr_time = time.strftime('%Y.%m.%d.%H.%M.%S')
Tb_target_dir = target_dir + name + "_"+ cr_time +'.v'

if not os.path.exists(target_dir): 
  os.mkdir(target_dir) 
  file = open(Tb_target_dir,'w')
  file.close()
else:
  file = open(Tb_target_dir,'w')
  file.close()

if __name__ == "__main__":
  file_dir = Tb_target_dir
  openfile(file_dir) 
  print("TB file created successfully")
else:
  print("Failed to create file of TB")

"""这里是将提取出来的字符写入生成的TB文件"""

with open(Tb_target_dir,"w") as OUTFile0:
    OUTFile0.write(timescale)
    OUTFile0.write("module tb_"+name+"\n\n")
    if(paraDec!=''):
        OUTFile0.write(paraDec+"\n\n")
    OUTFile0.write(input+"\n")    
    OUTFile0.write(output+"\n")
    if(inout!=''):
        OUTFile0.write(inout+"\n")
    OUTFile0.write(clk+"\n"+rst+"\n")
    OUTFile0.write(name+" "+paraDef+" "+"u_"+name+" (\n"+portList+"\n"+");")
    OUTFile0.write(operation+"\n"+"endmodule")

 

因为贴出的代码已经添加了比较详细的注释,具体的文章内容就不再赘述代码具体功能,python属于特别好上手的语言,主要去调取一些接口来实现了想要的功能。因为代码是边学边写,花的时间很短,代码质量不高,也欢迎大家批评指正。

 

五、代码测试

进入正题:

测试前准备好前面所说的环境(其他环境没有具体测试,python版本影响应该不大)

首先准备好一个测试的文件夹,一个需要测试的Verilog原文件,这里使用一个i2c_dri.v做一个简单的测试

文件夹如图

图片[1]-在VScode中自动生成Verilog仿真文件(Python)-FPGA常见问题社区-FPGA CPLD-ChipDebug

文件的路径:C:Users25778DesktopVScodetest

大家根据代码描述格式和注释添加自己的测试路径,完成的基本功能就是创建一个TB文件夹,创建测试文件(名字是:模块名+创建测试文件的时间,如果有需要可以修改为和测试文件内模块名一样(tb_模块名)),自动用Vscode打开(前提是你电脑的.v文件默认打开方式是Vscode,如果不是那么就会使用默认的编辑器打开)

测试前的情况:

图片[2]-在VScode中自动生成Verilog仿真文件(Python)-FPGA常见问题社区-FPGA CPLD-ChipDebug

图片[3]-在VScode中自动生成Verilog仿真文件(Python)-FPGA常见问题社区-FPGA CPLD-ChipDebug

运行脚本后的情况

图片[4]-在VScode中自动生成Verilog仿真文件(Python)-FPGA常见问题社区-FPGA CPLD-ChipDebug

图片[5]-在VScode中自动生成Verilog仿真文件(Python)-FPGA常见问题社区-FPGA CPLD-ChipDebug

图片[6]-在VScode中自动生成Verilog仿真文件(Python)-FPGA常见问题社区-FPGA CPLD-ChipDebug

小结:如果觉得Tb文件名不好的,读者可以自行把文件名修改为和TB文件的模块名一致(从python代码中修改),其它功能和优化会陆续进行。

 

六、总结

使用python写这个脚本配置环境其实还是有些麻烦的,有更方便快捷的脚本语言也欢迎讨论。

写这个代码主要为了给习惯使用Vscode的读者提供一个基础的平台,可以在这个基础上实现自己定制化的脚本,后续也会封装成VScode插件,VHDL的自动生成测试文件脚本也基本测试完成,后续代码也会公布。如果大家有其他便利的脚本也欢迎和作者交流。

请登录后发表评论

    没有回复内容