如何使用Python实现FPGA编程“自动化”
之前读到过一个公众号文章,写了关于《使用Python实现Vivado和Modelsim仿真的自动化》,连接https://mp.weixin.qq.com/s/2YR_LjpQNtJr9beqnCz7CA。根据该文章,基于自己的编程习惯和工作需要,我做了一些修改和便于自己编程的一些python代码,这里和大家介绍一下。文章来源:https://www.toymoban.com/news/detail-412960.html
一、自动生成tb文件
1、对于被生成的模块的端口格式,必须按照以下格式,才能自动生成。
## entity top is
## Port (
## clk_in : in std_logic;
## rst : in std_logic;
## data_in : in std_logic;
## data_out: out std_logic_vector(7 downto 0);
## data_v : out std_logic_vector(7 downto 0);
## );
## end top;
2、预定义字符串列表,用于生成tb文件的基本框架,如下:
str1 = ['----------------------------------------------------------------------------------'
'-- Company: \n',
'-- Engineer: \n',
'-- \n',
'-- Create Date: \n',
'-- Design Name: \n',
'-- Module Name: - Behavioral\n',
'-- Project Name: \n',
'-- Target Devices: \n',
'-- Tool Versions: \n',
'-- Description: \n',
'-- \n',
'-- Dependencies: \n',
'-- \n',
'-- Revision:\n',
'-- Revision 0.01 - File Created\n',
'-- Additional Comments:\n',
'-- \n',
'----------------------------------------------------------------------------------\n',
'\n',
'\n',
'library IEEE;\n',
'use IEEE.STD_LOGIC_1164.ALL;\n',
'\n',
'-- Uncomment the following library declaration if using\n',
'-- arithmetic functions with Signed or Unsigned values\n',
'--use IEEE.NUMERIC_STD.ALL;\n',
'\n',
'-- Uncomment the following library declaration if instantiating\n',
'-- any Xilinx leaf cells in this code.\n',
'--library UNISIM;\n',
'--use UNISIM.VComponents.all;\n',
'\n',
'entity top is\n',
'-- port ();\n',
'end top;\n',
'\n',
'architecture Behavioral of top is\n',
'\n',
' constant period : TIME := 20 NS; --时钟周期设置\n',
' constant CNT_MAX : INTEGER := 100 ; --cnt计数器最大值\n',
' signal cnt : INTEGER RANGE 0 TO CNT_MAX := 0 ; --复位设置\n',
'begin\n',
'-----------------------复位和时钟生成----------------------------------------\n',
' clk_gen1 : PROCESS \n',
' BEGIN\n',
' clk_in <= \'1\';\n',
' WAIT FOR period/2;\n',
' clk_in <= \'0\';\n',
' WAIT FOR period/2;\n',
' END PROCESS;\n',
' \n',
' cnt : process(clk)\n',
' begin\n',
' if ( clk_in\'event and clk_in = \'1\') then\n',
' if (cnt = CNT_MAX) then\n',
' cnt <= 0;\n',
' else\n',
' cnt <= cnt + 1;\n',
' end if;\n',
' end if;\n',
' end process; \n',
'\n',
' reset : process(clk)\n',
' begin\n',
' if ( clk_in\'event and clk_in = \'1\') then\n',
' if cnt > CNT_MAX then\n',
' reset <= \'0\'; \n',
' else\n',
' reset <= \'1\'; --复位\n',
' end if;\n',
' end if;\n',
' end process;\n',
'\n',
'----------------------------UUT instantiate--------------------------\n',
' Port map(\n',
' );\n',
'\n',
'end Behavioral;']
3、读取需要仿真的模块顶层文件,读取到环境列表中
vhdfile = open('\xxx.vhd','r') #读取文件
vhdfilelines = vhdfile.readlines() #将文件列表写入vhdfilelines中
vhdfile.close()
4、从vhdfilelines中循环查找端口、模块名称
entitystr = 'entity' #实体-识别字符串
endstr = 'end ' #实体结束部分-识别字符串
numend = 0 #端口开始的行号
numstart = 0 #端口结束的行号
for listline in vhdfilelines: #循环读取文件列表
if entitystr in listline:
strline = listline
numstart = vhdfilelines.index(strline) #根据‘实体-识别字符串’查找对应的行号
strline = strline.split(' ') #按照空格来分割
entityname = strline[1] #模块名称赋值
endstr = endstr + entityname #重新定义:实体结束部分-识别字符串
continue #跳出本次循环
if endstr in listline: #查找结束位置行号
strline = listline
numend = vhdfilelines.index(strline)
break #结束循环
5、提取要仿真的模块端口信息
portlist = vhdfilelines[numstart+2:numend-1] #提取模块实体部分
row_n = len(portlist) #确定长度
column_n = 2 #列表的列数
portmap = [['' for columns in range(2)] for rows in range(row_n)] #定义一个n行2列的空列表
for i in range(0,row_n): #for循环查找对应信息
strline = portlist[i].split(':')
strline1 = strline[0].split(' ')
for j in range(0,len(strline1)):
if strline1[j] != '':
portmap[i][0] = strline1[j] #端口名称赋值
break
portmap[i][1] = strline[1]
6、修改实体、结构体部分的模块名称,如下:
ModuleName = "tb_" + entityname
str1[5] = "-- Module Name: " + ModuleName + " - Behavioral\n"
str1[32] = "entity " + ModuleName + " is\n"
str1[34] = "end " + ModuleName + ";\n"
str1[36] = "architecture Behavioral of " + ModuleName + " is\n"
7、生成tb文件中调试模块的信号定义
for i in range(row_n-1,-1,-1): #逆序循环插入
strline = portmap[i][0] #信号名称
strline = " "*tabnum + "signal " + strline #生成:" signal clk_in"
if portnamenum > len(strline): #判断是否超出最大数
kongnum = portnamenum - len(strline)
else:
kongnum = 1
strline = strline + " "*kongnum + ":" #补入空格,对其到":"
if "in " in portmap[i][1]: #去掉第二段字符串的in、out方向关键字
strline1 = portmap[i][1].replace("in ","")
elif "out" in portmap[i][1]:
strline1 = portmap[i][1].replace("out","")
strline = strline + strline1 #拼接
str1.insert(38,strline) #插入
8、生成测试实体说明部分
str1.insert(38," "*tabnum + "end component;\n")
str1.insert(38," );\n")
for i in range(row_n-1,-1,-1): #倒序插入
strline = portmap[i][0] #
strline = " "*tabnum*2 + strline #
if portnamenum > len(strline): #判断是否超出最大数
kongnum = portnamenum - len(strline)
else:
kongnum = 1
strline = strline + " "*kongnum + ":" #
strline = strline + portmap[i][1] #
str1.insert(38,strline) #
str1.insert(38," port(\n") #
str1.insert(38," "*tabnum + "component "+ entityname +"\n") #
9、生成测试实体的例化部分信号连接
numinst = str1.index(' Port map(\n') #由于上述插入多行,这里需要重新定为行号
str1.insert(numinst," UUT : "+ entityname +"\n") #
for i in range(row_n-1,-1,-1): #倒序插入
strline = portmap[i][0] #
strline = " "*tabnum*2 + strline #
if portnamenum > len(strline): #判断是否超出最大数
kongnum = portnamenum - len(strline)
else:
kongnum = 1
strline = strline + " "*kongnum + "=> " #
strline = strline + portmap[i][0] + " "*(20-len(portmap[i][0]))#
if i != row_n-1:
strline = strline + ',\n'
else:
strline = strline + '\n'
str1.insert(numinst+2,strline) #
10、将修改好的激励文件写入到对应的tb文件中
fileName= source_path + '\\' + ModuleName + '.vhd'
with open(fileName,'w',encoding='utf-8')as file:
file.writelines(str1)
二、对于vivado使用modelsim仿真时修改代码不退出重新加载的方法
1、修改修改xxxxxxxx_Compile.do脚本,删除quit -force,防止重新启动仿真时关闭软件
CompileDoFile = open('.\xxx_Compile.do', 'r')
CompileDoFileAllLines = CompileDoFile.readlines()
CompileDoFile.close()
CompileDoFile = open('.\xxx_Compile.do', 'w')
for EachLine in CompileDoFileAllLines:
if EachLine.find('quit -force') == -1:
CompileDoFile.writelines(EachLine)
CompileDoFile.close()
2、修改xxxxxxxx_simulate.do脚本,删除run 1000ns和quit -force,添加log -r ./*,并且添加do {xxxx_compile.do}
SimulateDoFile = open('.\xxx_ simulate.do', 'r')
SimulateDoFileAllLines = SimulateDoFile.readlines()
SimulateDoFile.close()
dowrite = SimulateDoFileAllLines[8]
if "vsim" in dowrite:
SimulateDoFileAllLines.insert(8,"do {xxxx_compile.do}\n")
SimulateDoFile = open('.\xxx_ simulate.do', 'w')
for EachLine in SimulateDoFileAllLines:
#EachLine.find('run 1000ns')表示列表的某一元素中存在该find字符串,如果不存在,则返回-1
if EachLine.find('run 1000ns') == -1 and EachLine.find('quit -force') == -1:
SimulateDoFile.writelines(EachLine)
if ('log -r ./*\n' not in SimulateDoFileAllLines) == True:
SimulateDoFile.writelines('\nlog -r ./*\n')
SimulateDoFile.close()
总结
对于生成的tb文件使用了基本的python语言,对文件读写操作的语法,我以前也没有怎么学过Python,但是经过2天的学习,就掌握了基本语法,可以看懂程序。并进行编写上述代码。第一个程序解决了我们每次编写仿真激励时的麻烦,在自动生成的tb文件中,会自动生成时钟和复位的代码,也会将测试的模块例化到tb文件中,开发人员只需完成模块的其它输入信号的仿真编写。在自动重启时加载修改后的代码,并不会关闭modelsim界面,这样也节省了我们开发人员的仿真时间。可以更加高效的办公。文章来源地址https://www.toymoban.com/news/detail-412960.html
到了这里,关于如何使用Python实现FPGA编程“自动化”的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!