Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Correctly use QProcess to read stdout and write stdin



  • Hello,

    I am trying to automate programming of an FPGA in a Qt application. I need to use Xilinx Vivado to control a Xilinx programming pod to program the FPGA.

    There aren't any API plugins they offer that I know of, but the Vivado tool has a command line option. I am trying to start the vivado_lab.exe with 3 arguments and a specific environment needed to run, as a QProcess. I have setup the environment with QProcessEnvironment object, but I get no output when running the code. I know the tool starts, as I can find its PID from windows when I run my code. But when I try to read the stdout of the process I see no output. I am expecting the command line prompt "vivado_lab%".

    The code I am referring to is below, and runs inside of a thread.

                QProcess vivadoConn;
                vivadoConn.setProcessChannelMode(QProcess::MergedChannels);
    
                // Command to start vivado in tcl mode
                //QString vivadoCmd = l_toolDirs.xilinxToolDir.toUtf8() + " -nojournal -nolog -mode tcl";
    
                // Start the Vivado command line
                if(l_testInProgress)
                {
                    emit updateTestProgressText("Starting Vivado, Please wait...\n");
    
                    QStringList args;
                    args.append("-mode tcl");
                    args.append("-nojournal");
                    args.append("-nolog");
    
                    QProcessEnvironment vivadoEnv;
                    vivadoEnv.insert("HDI_APPROOT","C:/Xilinx/Vivado_Lab/2018.2");
                    vivadoEnv.insert("RDI_APPROOT","C:/Xilinx/Vivado_Lab/2018.2");
                    vivadoEnv.insert("RT_LIBPATH","C:/Xilinx/Vivado_Lab/2018.2/scripts/rt/data");
                    vivadoEnv.insert("RT_TCL_PATH","C:/Xilinx/Vivado_Lab/2018.2/scripts/rt/base_tcl/tcl");
                    vivadoEnv.insert("TCL_LIBRARY","C:/Xilinx/Vivado_Lab/2018.2/tps/tcl/tcl8.5");
                    vivadoEnv.insert("XILINX_PLANAHEAD","C:/Xilinx/Vivado_Lab/2018.2");
                    vivadoEnv.insert("XILINX_VIVADO","C:/Xilinx/Vivado_Lab/2018.2");
                    vivadoEnv.insert("Path","C:/Xilinx/Vivado_Lab/2018.2/bin;C:/Xilinx/Vivado_Lab/2018.2/lib/win64.o;C:/Xilinx/Vivado_Lab/2018.2/tps/win64/jre/bin/server;C:/Xilinx/Vivado_Lab/2018.2/tps/win64/jre/bin;");
    
                    QString xilinxvivadoResp, test1;
                    //vivadoConn.setProgram(l_toolDirs.xilinxToolDir.toUtf8());
                    vivadoConn.setProcessEnvironment(vivadoEnv);
                    //vivadoConn.setArguments(args);
                    vivadoConn.start(l_toolDirs.xilinxToolDir.toUtf8() + " -nojournal -nolog -mode tcl");
    
                    vivadoConn.waitForStarted();
    
                    qint64 vivado_pid = 0;
                    vivado_pid = FindProcessId(L"vivado_lab.exe");
    
                    xilinxvivadoResp = vivadoConn.readAllStandardOutput();
                    test1 = vivadoConn.readAllStandardError();
                    while(vivadoConn.waitForReadyRead(1000)) xilinxvivadoResp += vivadoConn.readAllStandardOutput();
    
                    emit updateTestProgressText("Output String: " + xilinxvivadoResp + "\n");
                    emit updateTestProgressText("Error String: " + test1 + "\n");
    
                    emit updateTestProgressText("Process ID for Vivado is: " + QString::number(vivado_pid) + "\n");
    
                    if((!xilinxvivadoResp.contains("vivado_lab%")) || (vivadoConn.state() == QProcess::ProcessState::NotRunning))
                    {
                        emit updateTestProgressText("Error connecting to Xilinx Vivado!\n\nExiting!\n");
                        vivadoConn.write("exit");
                        vivadoConn.kill();
                        threadStopping();
                        return;
                    }
    
                    // Increment progress
                    for(auto i = 10; i <= 20; i++)
                    {
                        emit updateTestProgress(i);
                    }
                }
    
                // Open a connection to the Xilinx pod hardware from vivado
                if(l_testInProgress)
                {
                    vivadoConn.write("open_hw");
                    this->sleep(3); // Wait for app response
    
                    QString xilinxvivadoResp = vivadoConn.readAll();
    
                    if((!xilinxvivadoResp.contains("vivado_lab%")) || (vivadoConn.state() == QProcess::ProcessState::NotRunning))
                    {
                        emit updateTestProgressText("Error opening Xilinx hardware!\n\nExiting!\n");
                        vivadoConn.write("exit");
                        vivadoConn.kill();
                        threadStopping();
                        return;
                    }
    
                    // Increment progress
                    for(auto i = 20; i <= 30; i++)
                    {
                        emit updateTestProgress(i);
                    }
                }
    
                // Connect to the new Xilinx hardware connection
                if(l_testInProgress)
                {
                    vivadoConn.write("connect_hw_server");
                    QThread::sleep(10); // Wait for app response
    
                    QString xilinxvivadoResp = vivadoConn.readAll();
    
                    if((!xilinxvivadoResp.contains("connect_hw_server: Time")) || (vivadoConn.state() == QProcess::ProcessState::NotRunning))
                    {
                        emit updateTestProgressText("Error connecting to Xilinx hardware!\n\nExiting!\n");
                        vivadoConn.write("exit");
                        vivadoConn.kill();
                        threadStopping();
                        return;
                    }
    
                    // Increment progress
                    for(auto i = 30; i <= 40; i++)
                    {
                        emit updateTestProgress(i);
                    }
                }
    
                // Check for Xilinx devices
                if(l_testInProgress)
                {
                    vivadoConn.write("get_hw_devices");
                    QThread::sleep(5); // Wait for app response
    
                    QString xilinxvivadoResp = vivadoConn.readAll();
    
                    if((!xilinxvivadoResp.contains("No matching hw_devices were found")) || (vivadoConn.state() == QProcess::ProcessState::NotRunning))
                    {
                        emit updateTestProgressText("Error: Could not find Xilinx FPGA!\n\nExiting!\n");
                        vivadoConn.write("exit");
                        vivadoConn.kill();
                        threadStopping();
                        return;
                    }
    
                    // Increment progress
                    for(auto i = 40; i <= 50; i++)
                    {
                        emit updateTestProgress(i);
                    }
                }
                threadStopping();
    

  • Lifetime Qt Champion

    Hi and welcome to devnet,

    Did you try using the asynchronous API ?
    What about passing the parameters as a QStringList as is recommended ? Does it yield the same result ?

    There's no error checks done, you should add them.



  • Hi @SGaist, thanks for the reply!

    I can't use the asynchronous as I cannot proceed to other commands and hardware controls until I can verify these commands have taken successfully. I had also tried passing the QStringList as the second argument into start() and also tried adding QIODevice::ReadWrite as the third arg as well.

    What Error checks are you referring to? Error checks in the QProcess object?

    I did try "emit updateTestProgressText("QProcess Error: " + vivadoConn.errorString());" and got "QProcess Error: Process operation timed out"

    I should add that I can run the same program with the same arguments and environment in a batch script in windows, and get the tool command line to show.

    Thank you for your time!


  • Lifetime Qt Champion

    @MNorvalls said in Correctly use QProcess to read stdout and write stdin:

    and get the tool command line to show.

    What do you mean by that ?



  • Running this executable from the command prompt has the tool execute a CLI based TCL command line to interface with their Xilinx tools. You can tell the tool command line is active because instead of the cmd prompt "C:/>", the tool prompt becomes "vivado_lab%", that is what I mean by getting the tool command to show.

    Does that make sense? I have also included a screenshot below :)

    7e0e8c17-f019-48ed-a566-09419d9a5cee-image.png


  • Lifetime Qt Champion

    One thing I just saw, split the "-mode" and "tcl" in the argument list. Each element that appears on the command line is a separate element of the list.



  • So, I was not using the argument list in that snippet of code but had tried it before and it didn't work. With your suggestion, the arguments now work (process does not crash immediately). I was tagging them onto the command string because I could get the process to run before in that case. I assume passing the arguments in a QStringList is the proper way to use QProcess::start() so I will use that moving forward.

    I have tried both of these argument methods and they both run because if I change the mode to gui, I get the Xilinx GUI window after a short amount of time.

    Unfortunately, passing arguments the way you recommended still yields no output when reading.


  • Lifetime Qt Champion

    Does it open it in a new command line window or in the same one that you call the application in ?



  • @MNorvalls said in Correctly use QProcess to read stdout and write stdin:

    I am expecting the command line prompt "vivado_lab%".

    It may be that the shell will not send an output prompt if it detects that standard input is not a terminal.

    Outside of Qt, you should be able to test redirecting stdin or stdout from/to a pipe (<, >, |) to see what can actually be captured.

    And you should be capturing stderr as well as stin/out, if you are not doing so already.


  • Lifetime Qt Champion

    @MNorvalls said in Correctly use QProcess to read stdout and write stdin:

    args.append("-mode tcl");

    This is wrong, they're two arguments.



  • @Christian-Ehrlicher We've moved on from that now, Christian :)



  • Thanks guys, yes it seems to be the executable that does not want to output its stdout in the tcl mode. I can see output if I call it in GUI mode (but obviously the GUI starts up instead). I am just going to have it write output to a file unfortunately.

    @SGaist The tcl command line opens in the same command window. When calling the GUI mode, some output shows in the command window, and then the GUI appears in its own window.

    @JonB I will give the command line piping a try as well for curiosity's sake, thank you.

    Thanks for the help!


Log in to reply