YosysHQ / yosys

Yosys Open SYnthesis Suite
https://yosyshq.net/yosys/
ISC License
3.46k stars 886 forks source link

Yosys tcl command substitution does not return the results of the command #2980

Open macd opened 3 years ago

macd commented 3 years ago

The result of a tcl command such as set xxx [cmd] should set the variable xxx to the result of command cmd. This works for normal tcl commands but not for Yosys commands. Here is a simple example. Say we have test.tcl with the following:

yosys -import
set xxx [expr 1 + 2]
puts "variable xxx: $xxx"

set yyy [help zinit]
puts "variable yyy: $yyy"

This results in

yosys> tcl test.tcl
[TCL: yosys -import] Command name collision: found pre-existing command `cd' -> skip.
[TCL: yosys -import] Command name collision: found pre-existing command `eval' -> skip.
[TCL: yosys -import] Command name collision: found pre-existing command `exec' -> skip.
[TCL: yosys -import] Command name collision: found pre-existing command `read' -> skip.
[TCL: yosys -import] Command name collision: found pre-existing command `trace' -> skip.
variable xxx: 3

    zinit [options] [selection]

Add inverters as needed to make all FFs zero-initialized.

    -all
        also add zero initialization to uninitialized FFs

variable yyy: 

yosys> 
rockybulwinkle commented 2 years ago

Did you find how to capture the output of yosys commands? I am struggling with this same issue.

rockybulwinkle commented 2 years ago

Well after much trial and tribulation, I made an incredibly ugly hack to get this functionality.

Basically, It redirects stdout to a temporary file, reads that file, and returns the value. It also restores stdout before returning.

I'm at a loss why I had to make this hack, it seems like basic functionality that should be builtin to yosys. What's the point of TCL scripting if the script can't react to what yosys returns?

Anway, here's the ugly code. I do depend on tclx for access to the "dup" command (for stdout preservation). There is a way to do it without dup (read here: https://stackoverflow.com/a/8532484/600853), but ugly as it is, if we don't preserve stdout, it will mess up logging.

#for some reason yosys doesn't initialize basic functionality like the ability to load packages. Do it manually here.
set tcl_library "/usr/share/tcl8.6"
source /usr/share/tcl8.6/init.tcl

#require tclx for dup command
package require Tclx

#import the yosys commands
yosys -import

proc capture_stdout {args} {
    #open a tmpfile.
    set temp [exec mktemp]
    set newstdout [open $temp w]

    #Save the old stdout to restore later
    set old_stdout [dup stdout]
    #close old stdout
    close stdout

    #make the new file act as stdout
    dup $newstdout stdout

    #Run the yosys command
    {*}$args

    #close the tempfile (acting as stdout)
    close stdout

    #restore stdout fd
    dup $old_stdout stdout

    #read the temp file
    set response [open $temp r]
    set retval [read $response]
    close $response

    #Cleanup
    file delete $temp

    #return what we read
    return $retval
}

#it works with any command. For example:
puts "before"
set capture [capture_stdout "puts" "hello world"]
puts "after"
puts $capture

I am emphatically not a TCL expert, there may be some glaring issues in this code. But it works well enough for me...

nakengelhardt commented 2 years ago

It's not implemented because the TCL interface is a straight import of the Yosys CLI commands, and the Yosys CLI doesn't have conditionals. We usually make a ScriptPass if we need to decide something on contents of the design.

BTW, could you avoid the need for the package if you use the yosys tee command to write the output to the temp file instead?

rockybulwinkle commented 2 years ago

That's a good idea, using the tee command. Here's a revised version of my function. It is so much simpler:

#import the yosys commands
yosys -import

proc capture_stdout {args} {    
    #open a tmpfile.
    set temp [exec mktemp]

    #Run the yosys command
    tee -o $temp {*}$args

    #read the temp file
    set response [open $temp r]
    set retval [read $response]
    close $response

    #Cleanup
    file delete $temp

    #return what we read
    return $retval
}

Thanks for bringing tee to my attention!

rockybulwinkle commented 2 years ago

Side note, the documentation does not make it clear that yosys does not return values to the TCL prompt. I think it should be updated to make this clearer and the solution we came to here (tee) be mentioned.

I've made a pull request to this effect in yosys-web.

nakengelhardt commented 2 years ago

The documentation is actually autogenerated from the help messages included in the source code, so the right place to edit this would be here: https://github.com/YosysHQ/yosys/blob/197c9e04e8778f99f82b1b6bddc9eba3fbf85104/kernel/yosys.cc#L759-L777

nakengelhardt commented 1 year ago

With #3572 you can now skip the file on disk and return the output via the scratchpad (still using tee, in the future we plan to make commands return values directly where it makes sense).