I am trying to create a Python based shell around the Xilinx ISE TCL shell xtclsh.exe. If this works, I will add support for other shells like PlanAhead or Vivado ...
So what is the picture? I have a list of VHDL source files that make up the IP core. I would like to open an existing ISE project, look for missing VHDL files and add them if necessary. Since IP cores have overlapping file dependencies, it is possible that the project already contains some files, so I am only looking for missing files.
Python 3.x user example and subprocessusing pipes. It starts xtclsh.exeand commands are sent in turn to the shell. The result is monitored to obtain results. To facilitate this example, I redirected STDERR to STDOUT. The dummy output POC_BOUNDARY is inserted into the command stream to indicate completed commands.
You can test the attached sample code by setting up an example ISE project that has some VHDL source files.
My problem is that INFO, WARNING, and ERROR messages are displayed, but the results of TCL commands cannot be read using a script.
Manually executing search *.vhdl -type filein xtclsh.exe results in:
% search *.vhdl -type file
D:/git/PoC/src/common/config.vhdl
D:/git/PoC/src/common/utils.vhdl
D:/git/PoC/src/common/vectors.vhdl
Running the script results in:
....
press ENTER for the next step
sending 'search *.vhdl -type file'
stdoutLine='POC_BOUNDARY
'
output consumed until boundary string
....
Questions:
- Where does xtclsh write?
- How can I read the results of TCL commands?
Btw: % script.
Python :
import subprocess
class XilinxTCLShellProcess(object):
executable = r"C:\Xilinx\14.7\ISE_DS\ISE\bin\nt64\xtclsh.exe"
boundarString = "POC_BOUNDARY"
boundarCommand = bytearray("puts {0}\n".format(boundarString), "ascii")
def create(self, arguments):
sysargs = []
sysargs.append(self.executable)
self.proc = subprocess.Popen(sysargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
self.sendBoundardCommand()
while(True):
stdoutLine = self.proc.stdout.readline().decode()
if (self.boundarString in stdoutLine):
break
print("found boundary string")
def terminate(self):
self.proc.terminate()
def sendBoundardCommand(self):
self.proc.stdin.write(self.boundarCommand)
self.proc.stdin.flush()
def sendCommand(self, line):
command = bytearray("{0}\n".format(line), "ascii")
self.proc.stdin.write(command)
self.sendBoundardCommand()
def sendLine(self, line):
self.sendCommand(line)
while(True):
stdoutLine = self.proc.stdout.readline().decode()
print("stdoutLine='{0}'".format(stdoutLine))
if (stdoutLine == ""):
print("reached EOF in stdout")
break
elif ("vhdl" in stdoutLine):
print("found a file name")
elif (self.boundarString in stdoutLine):
print("output consumed until boundary string")
break
def main():
print("creating 'XilinxTCLShellProcess' instance")
xtcl = XilinxTCLShellProcess()
print("launching process")
arguments = []
xtcl.create(arguments)
i = 1
while True:
print("press ENTER for the next step")
from msvcrt import getch
from time import sleep
sleep(0.1)
key = ord(getch())
if key == 27:
print("aborting")
print("sending 'exit'")
xtcl.sendLine("exit")
break
elif key == 13:
if (i == 1):
print("sending 'project open PoCTest.xise'")
xtcl.sendLine("project open PoCTest.xise")
i += 1
elif (i == 2):
print("sending 'lib_vhdl get PoC files'")
xtcl.sendLine("lib_vhdl get PoC files")
i += 1
elif (i == 3):
print("sending 'search *.vhdl -type file'")
xtcl.sendLine("search *.vhdl -type file")
i += 1
elif (i == 4):
print("sending 'xfile add ../../src/common/strings.vhdl -lib_vhdl PoC -view ALL'")
xtcl.sendLine("xfile add ../../src/common/strings.vhdl -lib_vhdl PoC -view ALL")
i += 16
elif (i == 20):
print("sending 'project close'")
xtcl.sendLine("project close")
i += 1
elif (i == 21):
print("sending 'exit'")
xtcl.sendCommand("exit")
break
print("exit main()")
xtcl.terminate()
print("the end!")
if __name__ == "__main__":
main()