Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where does the Xilinx TCL shell emit the results?

I'm trying to develop a Python based wrapper around the Xilinx ISE TCL shell xtclsh.exe. If it works, I'll add support for other shells like PlanAhead or Vivado ...

So what's the big picture? I have a list of VHDL source files, which form an IP core. I would like to open an existing ISE project, search for missing VHDL files and add them if necessary. Because IP cores have overlapping file dependencies, it's possible that a project already contains some files, so I'm only looking for missing files.

The example user Python 3.x and subprocess with pipes. The xtclsh.exe is launched and commands are send line by line to the shell. The output is monitored for results. To ease the example, I redirected STDERR to STDOUT. A dummy output POC_BOUNDARY is inserted into the command stream, to indicate completed commands.

The attached example code can be tested by setting up an example ISE project, which has some VHDL source files.

My problem is that INFO, WARNING and ERROR messages are displayed, but the results from the TCL commands can not be read by the script.

Manually executing search *.vhdl -type file in 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

Executing 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 to?
  • How can I read the results from TCL commands?

Btw: The prompt sign % is also not visible to my script.


Python code to reproduce the behavior:

import subprocess

class XilinxTCLShellProcess(object):
  # executable = "sortnet_BitonicSort_tb.exe"
  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)  # 0.1 seconds

    key = ord(getch())
    if key == 27:    # ESC
      print("aborting")
      print("sending 'exit'")
      xtcl.sendLine("exit")
      break
    elif key == 13: # ENTER
      if (i == 1):
        #print("sending 'project new test.xise'")
        #xtcl.sendLine("project new test.xise")
        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!")

# entry point
if __name__ == "__main__":
  main()
like image 255
Paebbels Avatar asked Dec 16 '15 22:12

Paebbels


1 Answers

I have tried several approaches on Linux, but it seemes that xtclsh detects whether standard input is connected to a pipe or a (pseudo) terminal. If it is connected to a pipe, xtclsh suppresses any output which would be normally written to standard output (prompt output, command results). I think, the same applies to Windows.

Messages (whether informative, warning or error) which are printed on standard error still go there even if the input is connected to a pipe.

To get the messages printed on standard output you can use the puts tcl command which always prints on standard output. That is, puts [command] takes the standard output of command and prints it always to standard output.

Example: Let's assume we have a test.xise project with two files: the top-level entity in test.vhd and the testbench in test_tb.vhd. And, we want to list all files in the project using this tcl script (commands.tcl):

puts [project open test]
puts "-----------------------------------------------------------------------"
puts [search *.vhd]
exit

Then the call xtclsh < commands.tcl 2> error.log prints this on standard output:

test
-----------------------------------------------------------------------
/home/zabel/tmp/test/test.vhd
/home/zabel/tmp/test/test_tb.vhd

And this is printed on standard error (into file error.log):

INFO:HDLCompiler:1061 - Parsing VHDL file "/home/zabel/tmp/test/test.vhd" into
   library work
INFO:ProjectMgmt - Parsing design hierarchy completed successfully.
like image 197
Martin Zabel Avatar answered Oct 26 '22 04:10

Martin Zabel