olofk / fusesoc

Package manager and build abstraction tool for FPGA/ASIC development
BSD 2-Clause "Simplified" License
1.16k stars 241 forks source link

Question: How scripts work in fusesoc? #203

Closed ThomasHornschuh closed 6 years ago

ThomasHornschuh commented 6 years ago

Hi, I tried to add tcl a tcl script to a core to be build with Vivado. Specifically I have a build script for a Vivado IP integrator block design (created with the write_bd macro) which I want to add to my build.

The idea behind is, that the toplevel of my design is build with IP integrator (basically this is the safest and easiest way to combine custom RTL with Xilinx IP like DDR3 RAM interface).

So my core file looks like this:

CAPI=1
[main]
name = ::bonfire-arty:0
depend = ::bonfire-axi:0 ::bonfire-axi4l2wb:0 ::zpuino-uart:0
backend = vivado

[fileset rtl]
files = bonfire_axi_sysio.vhd wb_io.vhd
    ../lib/bonfire-soc/spi/wb_spi_interface.vhd
    ../lib/bonfire-soc/spi/spimaster.vhd
    BootMem.vhd
    wishbone_subsystem.vhd
file_type=vhdlSource

[fileset xdc]
files = Arty.xdc
file_type=xdc
scope=synth

[fileset scripts]
file_type=tclSource
files = ../scripts/fusesoc_build_1.tcl  ../scripts/bd_bonfire_arty_1704.tcl  ../scripts/fusesoc_build_2.tcl

[simulator]
toplevel = bonfire_axi_top

[ghdl]
analyze_options = --ieee=synopsys
run_options = --ieee=synopsys

[vivado]
part=xc7a35ticsg324-1L

When running a fusesoc build the Vivado tcl will source the scripts before adding any files from fileset rtl. But I need it the other way around, I want my scripts be added between project setup and implementation run.

I tried to add a [script] section, but I doen't change anything. But this section is also not really documented what it exactly means. Currently I can work around, with running build with the --setup option, move the scripts to the right place in the build tcl and then run it.

From the three scripts in the example above the first one should be run before adding rtl (it sets things like the board type and custom IP repo directories, the second and third should run after rtl (the last script creates the toplevel hd wrapper for the block design and set it as top module.

I'm not sure if my ideas are in scope of FuseSoC, so consider this issue really as question.

olofk commented 6 years ago

Hi Thomas,

I see the problem, and I have been wondering if this would become an issue at some point, no one has complained until now :)

I see two possible ways to get around this

  1. Offer more control over the main Vivado TCL script that FuseSoC generates. This is something that I have also been considering for ModelSim. The idea is roughly that FuseSoC would use its current behaviour by default, but create a new TCL file with just the commands for adding source files and source this from the main script. For cases like you describe, there could be a option to use a user-supplied main TCL file instead of the one FuseSoC generates. In that file you are free to choose in which order you want to add files and run your own commands.

  2. The other option would be to use Vivado hooks. It has been a while since I used this myself, so I'm not sure how and if this will help in your case. The idea is that your tcl files will contain functions and register these functions as hooks with the Vivado TCL API. That will make Vivado run your functions at predetermined stages during the build instead of just running them directly at project creation. I have used that to run certain things after synthesis but before P&R, but it was some years ago, so I don't remember exactly which stages are available or even how to do it

I think 2 would be the cleanest option, but if that doesn't work I need to look at implementing 1. If I find the time I will dig up some info on how the hooks work in Vivado

olofk commented 6 years ago

ok, I have made some progress here. Please see if this (somewhat hacky) solution works for you

  1. For the script you want to be run after the files are added, use file type user and set the copyto attribute. e.g. scripts/fusesoc_build_2.tcl[file_type=user,copyto=fusesoc_build_2.tcl] By setting file_type to user, Vivado will not source it directly, and by using the copyto attribute, the file will end up in the work root. This is important for the next stage.
  2. Create a new file with file_type=tclSource and add the following to this file set_property STEPS.SYNTH_DESIGN.TCL.PRE {../../fusesoc_build_2.tcl} [get_runs synth_1]. This will tell Vivado to execute the file added in stage 1 just before synthesis. The reason for the ../../ is that synthesis will execute from a newly created subdirectory two levels down from the work root. This is also the reason why we used copyto in the first step, to make it easier to find the file from Vivado later on

Please give it a try and see if that works

ThomasHornschuh commented 6 years ago

Sounds great. I will give it a try in the next days, I‘m a bit busy with my main job currently

ThomasHornschuh commented 6 years ago

Hi, today I tried your idea. Unfortunately it is not working yet. The Script is executed on start of synthesis but the BD creation is not finding the RTL modules (see the line with ERROR BD_TCL_115). I don't understand the mechanics of Vivado well enough, but I think when starting the synthesis run the complete hierarchy from toplevel down must already be created. As you see in the output Vivado assumes bonfire_axi_top to be the upmost toplevel, which is not the case. The outer level is the HDL wrapper around the Block Design.

When I execute the fusesoc_build_2.tcl script manually in the TCL window (outside the context of the synth run everything works fine. So I'm afraid that there must be a way to control the order of scripts in fusesoc independently of the Vivado mechanisms.

*** Running vivado
    with args -log bonfire_axi_top.vds -m64 -product Vivado -mode batch -messageDb vivado.pb -notrace -source bonfire_axi_top.tcl

****** Vivado v2018.1 (64-bit)
  **** SW Build 2188600 on Wed Apr  4 18:39:19 MDT 2018
  **** IP Build 2185939 on Wed Apr  4 20:55:05 MDT 2018
    ** Copyright 1986-2018 Xilinx, Inc. All Rights Reserved.

source bonfire_axi_top.tcl -notrace
source /home/thomas/development/bonfire/bonfire_arty_a7_full/scripts/fusesoc_build_2.tcl
CRITICAL WARNING: [filemgmt 20-730] Could not find a top module in the fileset sources_1.
Resolution: With the gui up, review the source files in the Sources window. Use Add Sources to add any needed sources. If the files are disabled, enable them. You can also select the file and choose Set Used In from the pop-up menu. Review if they are being used at the proper points of the flow.
INFO: [BD_TCL-3] Currently there is no design <design_1> in project, so creating one...
Wrote  : </home/thomas/development/Vivado/build1.9/2018.1/bonfire-arty-a7_0/bld-vivado/bonfire-arty-a7_0.runs/synth_1/.srcs/sources_1/bd/design_1/design_1.bd> 
INFO: [BD_TCL-4] Making design <design_1> as current_bd_design.
INFO: [BD_TCL-5] Currently the variable <design_name> is equal to "design_1".
INFO: [BD_TCL-6] Checking if the following IPs exist in the project's IP catalog:  
xilinx.com:ip:axi_ethernetlite:3.0 xilinx.com:ip:axi_gpio:2.0 xilinx.com:ip:axi_quad_spi:3.2 xilinx.com:ip:clk_wiz:6.0 xilinx.com:ip:mig_7series:4.1 xilinx.com:ip:proc_sys_reset:5.0  .
INFO: [BD_TCL-6] Checking if the following modules exist in the project's sources:  
BootMem bonfire_axi_top bonfire_axi4l2wb bonfire_axi4l2wb wishbone_subsystem zpuino_uart zpuino_uart  .
ERROR: [BD_TCL-115] The following module(s) are not found in the project: BootMem bonfire_axi_top bonfire_axi4l2wb bonfire_axi4l2wb wishbone_subsystem zpuino_uart zpuino_uart
INFO: [BD_TCL-8] Please add source files for the missing module(s) above.
WARNING: [BD_TCL-1003] Will not continue with creation of design due to the error(s) above.
ThomasHornschuh commented 6 years ago

Maybe it is possible to create a more modular approach for the EDA tool script. The value of FuseSoc for me is to collect all the RTL and other files needed for a project and add it to a EDA tool like Vivado. So all the "read_vhdl" and "read_xdc" lines are the interesting ones. The Workflow steps "around" like Project creation, synthesis innvocation are the ones which I need to adapt often.

A better approach would be to define a user defined "toplevel script" which is executed on e.g. the build command, and leave it to the responsibility of this script to invoke the generated the file setup script

E.g. a Viado section could look like

[vivado]
project_script=myscript.tcl
addfiles_script_name=addfiles

myscript.tcl would then setup the project would source the auto generated addfiles.tcl When project_script is not specified fusesoc will automatically create a generic one, which is similar to todays script

Maybe the idea on this form is also "hacky" and very much focused to my individual requirements. But maybe you get the concept. Another option would be some "templating mechanism" to provide user templates for the script. I think the "hard coded" project scripts as current will always suffer from being very static and will lead to a lot of hard to maintain feature requests over time. Remember our discussion about ISE bmm files. And there are lot of other cases. For example in my tcl script I added

set_property board_part digilentinc.com:arty:part0:1.1 [current_project]
set_property target_language VHDL `[current_project]``
set_property default_lib work [current_project]
set_property  ip_repo_paths  $script_folder/../ip_repo [current_project]

which is not possbile to setup in the vivado section currently. But others may require completly different settings to get their projects running

olofk commented 6 years ago

I think that's the way to go. I have already made some of the other backends more modular and will look at doing something similar for Vivado. Unfortunately I'm quite busy right now so I can't make any promises when I will be done with the work

olofk commented 6 years ago

I have started some work on this, but would need your input as I'm trying to keep changes to a minimum. One of your original problems was that the TCL files were sourced before the RTL files were added. This is now fixed, so that the files (including sourcing TCL files) come in the order they appear in the .core file. By specifying your own TCL files as the last fileset in your core file, they should now run after all RTL files are added. With this change, it's still a bit tricky to run TCL files before the RTL files, but do you really need that? If so, a workaround would be to add a new core and place it as the first dependency and put the TCL files there to make them appear before the other files.

Another change is that the creation and running of the project has been split up into two stages. A makefile target will first create the xpr file. A second makefile target will open the project and run the implementation. So if you run fusesoc build --setup and then run make build-gui from the work root to create the project and open in GUI mode.

A third change is that reading files is split out into a separate TCL file which is sourced by a main TCL script. While I haven't added a way to supply your own main TCL file, at least you can use that file and source from your own script manually.

So, I'm planning to implement this the coming days, but I'm not sure if that helps with all your issues

ThomasHornschuh commented 6 years ago

I think the approach is ok and will work. When you take the order in the core file into account, I‘m wondering why you see a problem in executing tcl files before adding rtl. On the other side I currently have no strict requirement in running command before rtl. Most of my additions can also be executed after rtl adding, maybe with the exeception of setting the default_lib property. But I learned in the meantime that this is not really needed.

As you can see in my example code, I only set properties on [current_project]. Maybe it is possible to add a more generic property setup mechanism to the vivado section. They follow all the same schema and a lot of things in Vivado are set up with properties.

Some additional thoughts about your ideas: Wrapping the tcl scripts with a makefile has its pros and cons. I think you use the approach already with the simulations. The advantage is of course the ability to specify different targets. The disadvantage is that it is not possible to run the makefile out of Vivado tcl console. Currently I start Vivado without opening a project, cd to the build dir, and source the script. When something is wrong, I close the project, rerun fusesoc (which as a side effect cleans the build dir) and then source the script again. This is quite fast because I save the time to load Vivado into memory.

On the other side, when using makefiles, it may be an idea to go further with this concept and for example offer more targets:

So when reasoning about the potentials the makefile approach will pay out eventually.

olofk commented 6 years ago

I implemented almost what I wrote before, but kept the file reading in the main TCL file. After having thought a bit about it, I think that would work just as well.

The number of properties set by FuseSoC is reduced to a minimum. FuseSoC will now

  1. create the project
  2. Set part if that is defined in the Vivado options
  3. Enable vhdl2008 parsing if any VHDL-2008 files are detected (this option is always enabled from Vivado 2016.1 so it will have no effect for modern Vivado versions)
  4. Set generics, parameters and defines
  5. Read all source (and TCL files)
  6. Set include directories
  7. Set toplevel is that is defined in the core file
  8. Upgrade all IPs

So, in theory, everything that is set before 5 can be overridden by a custom TCL file, so the only thing that would be out of your control are step 6 and 8. Let me know if there are any valid reasons to not always do them.

Running is deferred to another TCL file. More on that later.

I think the approach is ok and will work. When you take the order in the core file into account, I‘m wondering why you see a problem in executing tcl files before adding rtl. On the other side I currently have no strict requirement in running command before rtl. Most of my additions can also be executed after rtl adding, maybe with the exeception of setting the default_lib property. But I learned in the meantime that this is not really needed.

Yeah. The default lib thing was the first potential problem I could see yesterday. I tried it though, and it works fine to set it after files are read.

As you can see in my example code, I only set properties on [current_project]. Maybe it is possible to add a more generic property setup mechanism to the vivado section. They follow all the same schema and a lot of things in Vivado are set up with properties.

My philosophy here is that I want to avoid adding code for things that can be done in a more generic way. This keeps the FuseSoC code base small and easier to manage. This is a good example as you can just as well write a TCL file, even if it's slighly more code.

Wrapping the tcl scripts with a makefile has its pros and cons. I think you use the approach already with the simulations. The advantage is of course the ability to specify different targets. The disadvantage is that it is not possible to run the makefile out of Vivado tcl console. Currently I start Vivado without opening a project, cd to the build dir, and source the script. When something is wrong, I close the project, rerun fusesoc (which as a side effect cleans the build dir) and then source the script again. This is quite fast because I save the time to load Vivado into memory.

All the logic is still kept in the TCL files. There are three targets in the makefile, of which two are used by FuseSoC and the third is currently intended to be run manually by someone who stops after the --setup stage.

First target creates the xpr file by sourcing the TCL script mentioned in the beginning. Second target will open this project and run the implementation flow including writing the bitstream

Third target, called build-gui, will launch Vivado in GUI mode and create the xpr file first if needed. To get the same behaviour as previously, just source your_core.tcl and then your_core_run.tcl

On the other side, when using makefiles, it may be an idea to go further with this concept and for example offer more targets:

Build in batch

Done

Create project interactively without running implementation

Done

Create project for interactive debug/simulation (in fact this would be more comfortable than having separate build and simulation workflows as today)

I have considered this too, but as Vivado really is the only tool (plus perhaps ISE) that offers a combined implementation/simulation shell, it will get quite tricky to get that right.

Program bitfile

Ah, right. The programming TCL file only gets written during the programming stage. We should just write it out in the configure phase instead. That way you can just source it from Vivado

... all sorts of other ideas

So when reasoning about the potentials the makefile approach will pay out eventually.

Please try the latest patches. Interested to hear if that solves your case. Thanks for the feedback and discussion. Other people's use cases are very important to move FuseSoC forward

lvoudour commented 6 years ago

So, in theory, everything that is set before 5 can be overridden by a custom TCL file, so the only thing that would be out of your control are step 6 and 8. Let me know if there are any valid reasons to not always do them.

Hi, I didn't want to open a new issue because this discussion seems relevant to my problem:

When I try to build with Vivado I get the following error:

WARNING: [Coretcl 2-176] No IPs found                     
# upgrade_ip [get_ips]       
ERROR: [Common 17-69] Command failed: No IP specified.  Please specify IP with 'objects'

I don't use any IP cores so it makes sense for the upgrade_ip command to return an error (I can manually build if I just generate the output files and then comment out the ip commands). Is there some way to generate the tcl script without the ip specific options unless the core explicitly specifies ip cores?

olofk commented 6 years ago

Oh, didn't think of that. I just pushed 20e2175 to (hopefully) fix that. Please let me know if that helps

lvoudour commented 6 years ago

Yes that did the trick, cheers!

ThomasHornschuh commented 6 years ago

Hi Olof, sorry for my late replay, I was quite busy with a lot of activites including preparing a release of my project. Today I was finally able to pull the latest fusesoc and test your changes.

In short: It works great, I'm able to build my Design completely with fusesoc, without hand-editing the generated tcl files. The new Makefile approach is also very nice (BTW: Is there any option to just run the make targets from fusesoc? This would e.g. save the requirement to cd to the build directory).

Nevertheless, in a design with dependencies it is not possible to run a tcl script before all source file additions: If a tcl script section is added as first section in a core file, the script will be included after the dependency sources and before the local sources. I'm not sure if this is an issue at all, all things I do in tcl (e.g. setting various properties) can really be done after the sources are added. So for me it was not a problem.

For automatic synthesis it would be very helpful to have the possibility to add the -jobs parameter to the run tcl, e.g.: launch_runs impl_1 -jobs 6 When using block designs this greatly speeds up build time, because synthesis the IP cores in the block design can run in parallel. On a computer with a fast SSD the speedup is nearly the number of jobs !!

olofk commented 6 years ago

Is there any option to just run the make targets from fusesoc? This would e.g. save the requirement to cd to the build directory

Not really. I want to add a --gui option eventually that would run run-gui or build-gui, but nothing more than that.

Nevertheless, in a design with dependencies it is not possible to run a tcl script before all source file additions: If a tcl script section is added as first section in a core file, the script will be included after the dependency sources and before the local sources. I'm not sure if this is an issue at all, all things I do in tcl (e.g. setting various properties) can really be done after the sources are added. So for me it was not a problem.

There's a (very hacky) way to do it. If you create a new .core file with just the TCL file you want to run and add it as the first dependency in your top-level core, it should execute first. But I thought a bit about that too, and I can't think of any cases where you need to run a TCL file before adding files, so hopefuly it's a non-issue

For automatic synthesis it would be very helpful to have the possibility to add the -jobs parameter to the run tcl, e.g.: launch_runs impl_1 -jobs 6 When using block designs this greatly speeds up build time, because synthesis the IP cores in the block design can run in parallel. On a computer with a fast SSD the speedup is nearly the number of jobs !!

This is good information. The verilator backend is currently the only backend to set a jobs option depending on the number of cores available. This does seem to be a good thing to set by default in the vivado backend too. I'll look into it.

Thanks for the testing. Are we ready to close the issue now? :)

ThomasHornschuh commented 6 years ago

Yes, we can close it. Should I create a new one for the jobs parameter?

olofk commented 6 years ago

Opening a new issue for the jobs parameter is a good idea so that we don't forget about it