<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>my tech blog &#187; Tcl</title>
	<atom:link href="http://billauer.se/blog/category/tcl/feed/" rel="self" type="application/rss+xml" />
	<link>https://billauer.se/blog</link>
	<description>Anything I found worthy to write down.</description>
	<lastBuildDate>Thu, 12 Mar 2026 11:36:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.2</generator>
		<item>
		<title>Vivado: Failed to install all user apps</title>
		<link>https://billauer.se/blog/2022/02/vivado-failed-to-install-user-apps/</link>
		<comments>https://billauer.se/blog/2022/02/vivado-failed-to-install-user-apps/#comments</comments>
		<pubDate>Tue, 22 Feb 2022 12:53:04 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[FPGA]]></category>
		<category><![CDATA[Tcl]]></category>
		<category><![CDATA[Vivado]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=6589</guid>
		<description><![CDATA[Every now and then Vivado whines with [Common 17-356] Failed to install all user apps. And every time I&#8217;m looking up how to to solve this, and then I find that the command that fixes this is tclapp::reset_tclstore in the Tcl command window. And then quit Vivado, and start it again. Why? I&#8217;ll never know. [...]]]></description>
			<content:encoded><![CDATA[<p>Every now and then Vivado whines with</p>
<pre>[Common 17-356] Failed to install all user apps.
</pre>
<p>And every time I&#8217;m looking up how to to solve this, and then I find that the command that fixes this is</p>
<pre>tclapp::reset_tclstore</pre>
<p>in the Tcl command window. And then quit Vivado, and start it again. Why? I&#8217;ll never know.</p>
<p>Just wanted it written down where I&#8217;ll be looking for it.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2022/02/vivado-failed-to-install-user-apps/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Critical Warnings after upgrading a PCIe block for Ultrascale+ on Vivado 2020.1</title>
		<link>https://billauer.se/blog/2021/06/pci-express-ultrascale-plus-vivado-upgrade/</link>
		<comments>https://billauer.se/blog/2021/06/pci-express-ultrascale-plus-vivado-upgrade/#comments</comments>
		<pubDate>Tue, 01 Jun 2021 11:06:00 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[FPGA]]></category>
		<category><![CDATA[PCI express]]></category>
		<category><![CDATA[Tcl]]></category>
		<category><![CDATA[Vivado]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=6355</guid>
		<description><![CDATA[Introduction Checking Xillybus&#8217; bundle for Kintex Ultrascale+ on Vivado 2020.1, I got several critical warnings related to the PCIe block. As the bundle is intended to show how Xillybus&#8217; IP core is used for simplifying communication with the host, these warnings aren&#8217;t directly related, and yet they&#8217;re unacceptable. This bundle is designed to work with [...]]]></description>
			<content:encoded><![CDATA[<h3>Introduction</h3>
<p>Checking <a href="http://xillybus.com/pcie-download" target="_blank">Xillybus&#8217; bundle for Kintex Ultrascale+</a> on Vivado 2020.1, I got several critical warnings related to the PCIe block. As the bundle is intended to show how Xillybus&#8217; IP core is used for simplifying communication with the host, these warnings aren&#8217;t directly related, and yet they&#8217;re unacceptable.</p>
<p>This bundle is designed to work with Vivado 2017.3 and later: It sets up the project by virtue of a Tcl script, which among others calls the upgrade_ip function for updating all IPs. Unfortunately, a bug in Vivado 2020.1 (and possibly other versions) causes the upgraded PCIe block to end up misconfigured.</p>
<p>This bug applies to Zynq Ultrascale+ as well, but curiously enough not with Virtex Ultrascale+. At least with my setting there was no problem.</p>
<h3>The problem</h3>
<p>Having upgraded an UltraScale+ Integrated Block (PCIE4) for PCI Express IP block from Vivado 2017.3 (or 2018.3) to Vivado 2020.1, I got several Critical Warnings. Three during synthesis:</p>
<pre>[Vivado 12-4739] create_clock:No valid object(s) found for '-objects [get_pins -filter REF_PIN_NAME=~TXOUTCLK -of_objects [get_cells -hierarchical -filter {NAME =~ *gen_channel_container[1200].*gen_gtye4_channel_inst[3].GT*E4_CHANNEL_PRIM_INST}]]'. ["project/pcie_ip_block/source/ip_pcie4_uscale_plus_x0y0.xdc":127]
[Vivado 12-4739] get_clocks:No valid object(s) found for '--of_objects [get_pins -hierarchical -filter {NAME =~ *gen_channel_container[1200].*gen_gtye4_channel_inst[3].GTYE4_CHANNEL_PRIM_INST/TXOUTCLK}]'. ["project/pcie_ip_block/synth/pcie_ip_block_late.xdc":63]
[Vivado 12-4739] get_clocks:No valid object(s) found for '--of_objects [get_pins -hierarchical -filter {NAME =~ *gen_channel_container[1200].*gen_gtye4_channel_inst[3].GTYE4_CHANNEL_PRIM_INST/TXOUTCLK}]'. ["project/pcie_ip_block/synth/pcie_ip_block_late.xdc":64]</pre>
<p>and another seven during implementation:</p>
<pre>[Vivado 12-4739] create_clock:No valid object(s) found for '-objects [get_pins -filter REF_PIN_NAME=~TXOUTCLK -of_objects [get_cells -hierarchical -filter {NAME =~ *gen_channel_container[1200].*gen_gtye4_channel_inst[3].GT*E4_CHANNEL_PRIM_INST}]]'. ["project/pcie_ip_block/source/ip_pcie4_uscale_plus_x0y0.xdc":127]
[Vivado 12-4739] set_clock_groups:No valid object(s) found for '-group [get_clocks -of_objects [get_pins -hierarchical -filter {NAME =~ *gen_channel_container[1200].*gen_gtye4_channel_inst[3].GTYE4_CHANNEL_PRIM_INST/TXOUTCLK}]]'. ["project/pcie_ip_block/synth/pcie_ip_block_late.xdc":63]
[Vivado 12-4739] set_clock_groups:No valid object(s) found for '-group '. ["project/pcie_ip_block/synth/pcie_ip_block_late.xdc":63]
[Vivado 12-4739] set_clock_groups:No valid object(s) found for '-group [get_clocks -of_objects [get_pins -hierarchical -filter {NAME =~ *gen_channel_container[1200].*gen_gtye4_channel_inst[3].GTYE4_CHANNEL_PRIM_INST/TXOUTCLK}]]'. ["project/pcie_ip_block/synth/pcie_ip_block_late.xdc":64]
[Vivado 12-4739] set_clock_groups:No valid object(s) found for '-group '. ["project/pcie_ip_block/synth/pcie_ip_block_late.xdc":64]
[Vivado 12-5201] set_clock_groups: cannot set the clock group when only one non-empty group remains. ["project/pcie_ip_block/synth/pcie_ip_block_late.xdc":63]
[Vivado 12-5201] set_clock_groups: cannot set the clock group when only one non-empty group remains. ["project/pcie_ip_block/synth/pcie_ip_block_late.xdc":64]</pre>
<p>The first warning in each group points at this line in ip_pcie4_uscale_plus_x0y0.xdc, which was automatically generated by the tools:</p>
<pre>create_clock -period 4.0 [get_pins -filter {REF_PIN_NAME=~TXOUTCLK} -of_objects [get_cells -hierarchical -filter {NAME =~ *<span style="color: #ff0000;"><strong>gen_channel_container[1200]</strong></span>.*gen_gtye4_channel_inst[3].GT*E4_CHANNEL_PRIM_INST}]]</pre>
<p>And the other at these two lines in pcie_ip_block_late.xdc, also generated by the tools:</p>
<pre>set_clock_groups -asynchronous -group [get_clocks -of_objects [get_ports sys_clk]] -group [get_clocks -of_objects [get_pins -hierarchical -filter {NAME =~ *<strong><span style="color: #ff0000;">gen_channel_container[1200]</span></strong>.*gen_gtye4_channel_inst[3].GTYE4_CHANNEL_PRIM_INST/TXOUTCLK}]]
set_clock_groups -asynchronous -group [get_clocks -of_objects [get_pins -hierarchical -filter {NAME =~ *<span style="color: #ff0000;"><strong>gen_channel_container[1200]</strong></span>.*gen_gtye4_channel_inst[3].GTYE4_CHANNEL_PRIM_INST/TXOUTCLK}]] -group [get_clocks -of_objects [get_ports sys_clk]]</pre>
<p>So this is clearly about a reference to a non-existent logic cell supposedly named gen_channel_container[1200], and in particular that index, 1200, looks suspicious.</p>
<p>I would have been <em>relatively</em> fine with ignoring these warnings had it been just the set_clock_groups that failed, as these create false paths. If the design implements properly without these, it&#8217;s fine. But failing a create_clock command is serious, as this can leave paths unconstrained. I&#8217;m not sure if this is indeed the case, and it doesn&#8217;t matter all that much. One shouldn&#8217;t get used to ignoring critical warnings.</p>
<p>Looking at the .xci file for this PCIe block, it&#8217;s apparent that several changes were made to it while upgrading to 2020.1. Among those changes, these three lines were added:</p>
<pre>&lt;spirit:configurableElementValue spirit:referenceId="MODELPARAM_VALUE.MASTER_GT"&gt;GTHE4_CHANNEL_<span style="color: #ff0000;"><strong>X49Y99</strong></span>&lt;/spirit:configurableElementValue&gt;
&lt;spirit:configurableElementValue spirit:referenceId="MODELPARAM_VALUE.MASTER_GT_CONTAINER"&gt;<span style="color: #ff0000;"><strong>1200</strong></span>&lt;/spirit:configurableElementValue&gt;
&lt;spirit:configurableElementValue spirit:referenceId="MODELPARAM_VALUE.MASTER_GT_QUAD_INX"&gt;3&lt;/spirit:configurableElementValue&gt;</pre>
<p>Also, somewhere else in the XCI file, this line was added:</p>
<pre>&lt;spirit:configurableElementValue spirit:referenceId="PARAM_VALUE.MASTER_GT"&gt;GTHE4_CHANNEL_<span style="color: #ff0000;"><strong>X49Y99</strong></span>&lt;/spirit:configurableElementValue&gt;</pre>
<p>So there&#8217;s a bug in the upgrading mechanism, which sets some internal parameter to select the a nonexistent GT site.</p>
<h3>The manual fix (GUI)</h3>
<p>To rectify the wrong settings manually, enter the settings of the PCIe block, and click the checkbox for &#8220;Enable GT Quad Selection&#8221; twice: Once for unchecking, and once for checking it. Make sure that the selected GT hasn&#8217;t changed.</p>
<p>Then it might be required to return some unrelated settings to their desired values. In particular, the PCI Device ID and similar attributes change to Xilinx&#8217; default as a result of this. It&#8217;s therefore recommended to make a copy of the XCI file before making this change, and then use a diff tool to compare the before and after files, looking for irrelevant changes. Given that this revert to default has been going on for so many years, it seems like Xilinx considers this a feature.</p>
<p>But this didn&#8217;t solve my problem, as the bundle needs to set itself correctly out of the box.</p>
<h3>Modifying the XCI file? (Not)</h3>
<p>The immediate thing to check was whether this problem applies to PCIe  blocks that are created in Vivado 2020.1 from scratch inside a project which is set to target KCU116 (which is what the said Xillybus bundle targets). As expected, it  doesn&#8217;t &#8212; this occurs just on upgraded IP blocks: With the project that was set up from scratch, the related lines in the XCI file read:</p>
<pre>&lt;spirit:configurableElementValue spirit:referenceId="MODELPARAM_VALUE.MASTER_GT"&gt;<strong>GTYE4_CHANNEL_X0Y7</strong>&lt;/spirit:configurableElementValue&gt;
&lt;spirit:configurableElementValue spirit:referenceId="MODELPARAM_VALUE.MASTER_GT_CONTAINER"&gt;<strong>1</strong>&lt;/spirit:configurableElementValue&gt;
&lt;spirit:configurableElementValue spirit:referenceId="MODELPARAM_VALUE.MASTER_GT_QUAD_INX"&gt;3&lt;/spirit:configurableElementValue&gt;</pre>
<p>and</p>
<pre>&lt;spirit:configurableElementValue spirit:referenceId="PARAM_VALUE.MASTER_GT"&gt;<strong>GTYE4_CHANNEL_X0Y7</strong>&lt;/spirit:configurableElementValue&gt;</pre>
<p>respectively. These are values that make sense.</p>
<p>With this information at hand, my first  attempt to solve this was to add the four new lines to  the old XCI file. This allowed using the XCI file with Vivado 2020.1 properly,  however synthesizing the PCIe  block on older Vivado versions failed: As  it turns out, all MODELPARAM_VALUE attributes become instantiation    parameters for pcie_uplus_pcie4_uscale_core_top inside the PCIe block.   However looking at the source file (on 2020.1), these parameters are  indeed defined  (only in those generated in 2020.1), and yet they are  unused, like many  other instantiation parameters in this module. So  apparently, Vivado&#8217;s  machinery generates an instantiation parameter for  each of these, even  if they&#8217;re not used. Those unused parameters are  most likely intended  for scripting.</p>
<p>So this trick made Vivado instantiate the  pcie_uplus_pcie4_uscale_core_top with instantiation parameters that it  doesn&#8217;t have, and hence its synthesis failed. Dead end.</p>
<p>I didn&#8217;t examine the  possibility to deselect &#8220;Enable GT Quad Selection&#8221; in the original  block, because Vivado 2017.3 chooses the wrong GT for the board without  this option.</p>
<h3>Workaround with Tcl</h3>
<p>Eventually, I solved the problem by adding a few lines to the Tcl script.</p>
<p>Assuming that $ip_name has been set to the name of the PCIe block IP, this Tcl snippet rectifies the bug:</p>
<pre>if {![string equal "" [get_property <strong>-quiet</strong> CONFIG.MASTER_GT [get_ips $ip_name]]]} {
  set_property -dict [list CONFIG.en_gt_selection {true} CONFIG.MASTER_GT {<span style="color: #ff0000;"><strong>GTYE4_CHANNEL_X0Y7</strong></span>}] [get_ips $ip_name]
}</pre>
<p>This snippet should of course be inserted <strong>after</strong> updating the IP core (with e.g. upgrade_ip [get_ips]). The code first checks if the MASTER_GT is defined, and only if so, it sets it to the desired value. This ensures that nothing happens with the older Vivado versions. Note the &#8220;quiet&#8221; flag of get_properly, which prevents it from generating an error if the property isn&#8217;t defined. Rather, it returns an empty string if that&#8217;s the case, which is what the result is compared against.</p>
<p>Setting MASTER_GT this way also rectifies GT_CONTAINER correctly, and surprisingly enough, this <strong>doesn&#8217;t</strong> change anything it shouldn&#8217;t, and in particular, the Device IDs remain intact.</p>
<p>However the disadvantage with this solution is that the GT to select is hardcoded in the Tcl code. But that&#8217;s fine in my case, for which a specific board (KCU116) is targeted by the bundle.</p>
<p>Another way to go, which is less recommended, is to emulate the check and uncheck of &#8220;Enable GT Quad Selection&#8221;:</p>
<pre>if {![string equal "" [get_property -quiet CONFIG.MASTER_GT [get_ips $ip_name]]]} {
  set_property CONFIG.en_gt_selection {false} [get_ips $ip_name]
  set_property CONFIG.en_gt_selection {true} [get_ips $ip_name]
}</pre>
<p>However turning the en_gt_selection flag off and on again also resets the Device ID to default as with manual toggling of the checkbox. And even though it sets the MASTER_GT correctly in my specific case, I&#8217;m not sure whether this can be relied upon.</p>
<h3>Vivado 2025.2 update</h3>
<p>Fast forward to late 2025, it turns out that Vivado 2025.2 suddenly reacts to the workaround by selecting the GTY quad to GTY_Quad_227, and resetting the Device ID to its default value. This doesn&#8217;t happen with Vivado 2025.1 and back.</p>
<p>Removing the workaround code still resulted in MASTER_GT being set to GTHE4_CHANNEL_X49Y99. However, as of 2025.1, this makes no difference anymore (actually, also as soon as 2023.1). This property doesn&#8217;t influence anything of the PCIe block IP&#8217;s files, except for the XCI and XML files, where this property has a different value. As it has no influence on the source files used for implementation, the workaround can be removed safely for all versions starting with 2025.1 (and probably earlier too).</p>
<p>So the update workaround looks like this:</p>
<pre>if {<span class="punch">[version -short] &lt; 2025.1 &amp;&amp;</span> \
    ![string equal "" [get_property -quiet CONFIG.MASTER_GT [get_ips $ip_name]]]} {
  set_property -dict [list CONFIG.en_gt_selection {true} CONFIG.MASTER_GT {GTYE4_CHANNEL_X0Y7}] [get_ips $ip_name]
}</pre>
<p>The obvious change is that the workaround isn&#8217;t applied from Vivado 2025.1 and later.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2021/06/pci-express-ultrascale-plus-vivado-upgrade/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Making any IP in the IP Catalog availabe in QSys</title>
		<link>https://billauer.se/blog/2018/04/quartus-qsys-ip-catalog/</link>
		<comments>https://billauer.se/blog/2018/04/quartus-qsys-ip-catalog/#comments</comments>
		<pubDate>Mon, 30 Apr 2018 15:20:10 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[FPGA]]></category>
		<category><![CDATA[Intel FPGA (Altera)]]></category>
		<category><![CDATA[Tcl]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=5416</guid>
		<description><![CDATA[Introduction I needed the Cyclone V Transceiver Native PHY IP Core inside QSys. Why? Actually, part of a failed attempt to find solve a compilation error. The IP is available in Quartus 15.1&#8242;s IP Catalog, but inside the same toolkit&#8217;s QSys it doesn&#8217;t appear in the list of IPs. As discussed in this forum thread, [...]]]></description>
			<content:encoded><![CDATA[<h3>Introduction</h3>
<p>I needed the Cyclone V Transceiver Native PHY IP Core inside QSys. Why? Actually, part of a <a title="Combining PCIe and Gigabit Transceiver on Cyclone V" href="https://billauer.se/blog/2018/04/pcie-mgtxcvr-qsys/" target="_blank">failed attempt</a> to find solve a compilation error.</p>
<p>The IP is available in Quartus 15.1&#8242;s IP Catalog, but inside the same toolkit&#8217;s QSys it doesn&#8217;t appear in the list of IPs. As discussed in <a href="https://alteraforum.com/forum/showthread.php?t=50303" target="_blank">this forum thread</a>, this is intentional: Altera doesn&#8217;t support having it inside QSys, seemingly because it&#8217;s not &#8220;fully verified&#8221;. OK, so I&#8217;ll take the risk. How do I make QSys list this IP, so it can be included?</p>
<h3>The fix</h3>
<p>As mentioned in <a href="https://www.ovro.caltech.edu/~dwh/correlator/pdf/altera_ttk_examples.pdf" target="_blank">this guide</a>, the thing is that IPs which are hidden from QSys have the INTERNAL property set to &#8220;true&#8221;. All that is left is hence to edit the relevant Tcl file, and update the IP database.</p>
<p>Mission number one is to find the correct Tcl file. The hints on the file&#8217;s name are:</p>
<ul>
<li>It&#8217;s probably related to the IP&#8217;s name and functionality</li>
<li>It ends with *_hw.tcl</li>
<li>The FPGA family is denoted by &#8220;av&#8221;, &#8220;cv&#8221; &#8220;sv&#8221; etc</li>
</ul>
<p>Eventually the file I was looking for was at /path/to/quartus/ip/altera/alt_xcvr/altera_xcvr_native_phy/cv/tcl/altera_xcvr_native_cv_hw.tcl. Unlike many other HW Tcl files, it doesn&#8217;t just assign parameters directly (in which case it&#8217;s easy to spot the assignment to INTERNAL), but it merely consists of adding a couple of directories to some search path, and then it goes:</p>
<pre>::altera_xcvr_native_cv::module::declare_module</pre>
<p>which refers to module.tcl, which has the following code snippet:</p>
<pre>  namespace export \
    declare_module

  # Internal variables
  variable module {\
    {NAME                   VERSION                 INTERNAL  ANALYZE_HDL EDITABLE  ELABORATION_CALLBACK                        PARAMETER_UPGRADE_CALLBACK                    DISPLAY_NAME                        GROUP                                 AUTHOR                DESCRIPTION DATASHEET_URL                                           DESCRIPTION  }\
    {altera_xcvr_native_cv  15.1  <span style="color: #ff0000;"><strong>true</strong></span>      false       false     ::altera_xcvr_native_cv::module::elaborate  ::altera_xcvr_native_cv::parameters::upgrade  "Cyclone V Transceiver Native PHY"  "Interface Protocols/Transceiver PHY" "Altera Corporation"  NOVAL       "http://www.altera.com/literature/ug/xcvr_user_guide.pdf" "Cyclone V Transceiver Native PHY."}\
  }
}</pre>
<p>This is an assignment of multiple variables: The names of the variables are listed on the first curly brackets, and the values in the second. As the third variable is INTERNAL, that&#8217;s the one to fix. So the actual edit consist of changing the &#8220;true&#8221; marked in red above to &#8220;false&#8221;.</p>
<h3>Updating the IP catalog</h3>
<p>Only making the change above isn&#8217;t enough. The IP Catalog cache must be updated as well.</p>
<p>Change directory to something like /path/to/quartus/ip/altera/ and set up the environment variables:</p>
<pre>$ ../../nios2eds/nios2_command_shell.sh</pre>
<p>and then create an IP Catalog cache:</p>
<pre>$ ip-make-ipx</pre>
<p>Once done, overwrite the previous file (you may want to make a copy of it first):</p>
<pre>$ mv components.ipx altera_components.ipx</pre>
<p>And now restart Quartus. The said IP now appears in QSys&#8217; IP Catalog.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2018/04/quartus-qsys-ip-catalog/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Under the hood of Vivado runs: Some scripting essentials</title>
		<link>https://billauer.se/blog/2016/02/vivado-runs-tcl-bash/</link>
		<comments>https://billauer.se/blog/2016/02/vivado-runs-tcl-bash/#comments</comments>
		<pubDate>Wed, 10 Feb 2016 16:07:52 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[FPGA]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Tcl]]></category>
		<category><![CDATA[Vivado]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=4931</guid>
		<description><![CDATA[Introduction My motivation for looking inside Vivado runs was that I wanted to implement a Vivado project from within XEmacs, using the Compile button, and all that within a rather tangled Makefile-based build system. But I also wanted to leave the possibility to open the project using Vivado&#8217;s GUI, if something went wrong or needed [...]]]></description>
			<content:encoded><![CDATA[<h3>Introduction</h3>
<p>My motivation for looking inside Vivado runs was that I wanted to implement a Vivado project from within XEmacs, using the Compile button, and all that within a rather tangled Makefile-based build system. But I also wanted to leave the possibility to open the project using Vivado&#8217;s GUI, if something went wrong or needed inspection. So working in non-project mode was out of the question.</p>
<p>On the face of it, the solution was simple: Just execute the runme.sh scripts in the run directories. Or use launch_runs in a Tcl script. Well, that sounds simple, but there is no output to console during these runs. In particular, the implementation is completely silent. I opted out the fun of staring on the cursor for an hour or so, having no idea what&#8217;s going on during the implementation. Leaving me no option but to get my hands a bit dirty.</p>
<p>This was written in February 2016 and relates to Vivado 2015.2. Feel free to update stuff in the comments below.</p>
<p>It&#8217;s recommended to first take a look on <a href="http://xillybus.com/tutorials/vivado-version-control-packaging" target="_blank">this page</a>, which discusses other aspects of scripting.</p>
<h3>Preparing the runs &amp; OOCs</h3>
<p>Vivado runs are just an execution of a Tcl script in one of the *.runs directories. This holds true for all runs, both Out-Of-Context runs (OOCs, e.g. IP cores) as well as synthesis and implementation runs.</p>
<p>Say that the project&#8217;s name is myproj, and the top-level module&#8217;s name is top.v (or top.vhd, if you insist). As the project is generated, Vivado creates a directory named myproj.run, which contains a set of subdirectories, for example fifo_32x512_synth_1/, fifo_8x2048_synth_1/, synth_1/ and impl_1/. In this example, the first two directories belong to two FIFO IPs, and the other two are implementation related.</p>
<p>synth_1 and impl_1 are most likely generated when the project is created in Vivado&#8217;s GUI, or with create_run Tcl calls if the project is generated with a setup scripts (again, take a look on <a href="http://xillybus.com/tutorials/vivado-version-control-packaging" target="_blank">this page</a>). This is kinda out of scope here. The thing is to create and invoke the runs for the IPs (that is, the Out-Of-Context parts, OOCs).</p>
<p>In my personal preference, these OOCs are added to the project with the following Tcl snippet:</p>
<pre>foreach i $oocs {
    if [file exists "$essentials_dir/$i/$i.dcp"] {
	read_checkpoint "$essentials_dir/$i/$i.dcp"
    } else {
	add_files -norecurse -fileset $obj "$essentials_dir/$i/$i.xci"
    }
}</pre>
<p>To make a long story short, the idea is to include the DCP file rather than the XCI if possible, so the IP isn&#8217;t re-generated if it has already been so. Which means that the DCP file has to be deleted if the IP core&#8217;s attributes have been changed, or the changes won&#8217;t take any effect.</p>
<p>We&#8217;ll assume that the IPs were included as XCIs, because including DCPs requires no runs.</p>
<p>The next step is to create the scripts for all runs with the following Tcl command:</p>
<pre>launch_runs -scripts_only impl_1 -to_step write_bitstream</pre>
<p>Note that thanks to the -scripts_only flag, no run executes here, but just the run directories and their respective scripts. In particular, the IPs are elaborated, or generated,  at this point. But not synthesized.</p>
<h3>Building the OOCs</h3>
<p>It&#8217;s a waste of time to run the IPs&#8217; synthesis one after the other, as each synthesis doesn&#8217;t depend on the other. So a parallel launch can be done as follows:</p>
<p>First, obtain a list of runs to be run, and reset them:</p>
<pre>set ooc_runs [get_runs -filter {IS_SYNTHESIS &amp;&amp; name != "synth_1"} ]

foreach run $ooc_runs { reset_run $run }</pre>
<p>The filter grabs the synthesis target of the IPs&#8217; runs, and skips synth_1. Resetting is done, or Vivado complains it should.</p>
<p>Next, launch these specific runs in parallel:</p>
<pre>if { [ llength $ooc_runs ] } {
  launch_runs -jobs 8 $ooc_runs
}</pre>
<p>Note that ooc_runs may be an empty list, in particular if all IPs were loaded as DCPs before. If launch_runs is called with no runs, it fails with an error. To prevent this, $ooc_runs is checked first.</p>
<p>And then finally, wait for all runs to finish. wait_on_run can only wait on one specific run, but it&#8217;s fine looping on all launched runs. The loop will finish after the last run has finished:</p>
<pre>foreach run $ooc_runs { wait_on_run $run }</pre>
<h3>Finally: Implementing the project</h3>
<p>As mentioned above, launching a run actually consists of executing runme.sh (or runme.bat on Windows, never tried it). The runme.sh shell script sets the PATH with the current Vivado executable, and then invokes the following command with ISEWrap.sh as a wrapper:</p>
<pre>vivado -log <span style="color: #888888;">top</span>.vds -m64 -mode batch -messageDb vivado.pb -notrace -source <span style="color: #888888;">top</span>.tcl</pre>
<p>(Recall that &#8220;top&#8221; is the name of the toplevel module)</p>
<p><strong>Spoiler:</strong> Just invoking the command above will execute the run with all log output going to console, but Vivado&#8217;s GUI will not reflect that the execution took place properly. More on that below.</p>
<p>It&#8217;s important to note that the &#8220;vivado&#8221; executable is invoked. This is in fact the way it&#8217;s done even when launched from within the GUI or with a launch_runs Tcl command. If the -jobs parameter is given to launch_runs, it will invoke the &#8220;vivado&#8221; executable several times in parallel. If you want to convince yourself that this indeed happens, note that you get something like this in the console inside Vivado&#8217;s GUI, which is exactly what Vivado prints out when invoked from the command line:</p>
<pre>****** Vivado v2015.2 (64-bit)
  **** SW Build 1266856 on Fri Jun 26 16:35:25 MDT 2015
  **** IP Build 1264090 on Wed Jun 24 14:22:01 MDT 2015
    ** Copyright 1986-2015 Xilinx, Inc. All Rights Reserved.</pre>
<p>Vivado&#8217;s invocation involves three flags that are undocumented:</p>
<ul>
<li>The -notrace flag simply means that Vivado doesn&#8217;t print out the Tcl commands it executes, which it would otherwise do by default. I drop this flag in my own scripts: With all the mumbo-jumbo that is emitted anyhow, the Tcl commands are relatively informative.</li>
<li>The -m64 probably means &#8220;run in 64 bit mode&#8221;, but I have no idea.</li>
<li>The -messageDb seems to set the default message *.pb output, which is probably some kind of database from which the GUI takes its data to present in the Message tab. Note that the main Tcl script for impl_1 (e.g. top.tcl) involves several calls to create_msg_db followed by close_msg_db, which is probably how the implementation run has messages divided into subcategories. Just my guesses, since nothing of this is documented (not even these Tcl commands).</li>
</ul>
<p>The ISEWrap.sh wrapper is crucially important if you want to be able to open the GUI after the implementation and work as if it was done in the GUI: It makes it possible for the GUI to tell which run has started, completed or failed. Namely, it creates two files, one when the run starts, and one when it ends.</p>
<p>For example, during the invocation of a run, .vivado.begin.rst is created (note the &#8220;hidden file name&#8221; starting with a dot), and contains something like this:</p>
<pre>&lt;?xml version="1.0"?&gt;
&lt;ProcessHandle Version="1" Minor="0"&gt;
    &lt;Process Command="vivado" Owner="eli" Host="myhost.localdomain" Pid="1003"&gt;
    &lt;/Process&gt;
&lt;/ProcessHandle&gt;</pre>
<p>And if the process terminates successfully, another empty file is created, .vivado.end.rst. If it failed, the empty file .vivado.error.rst is created instead. The synth_1 run creates only these two, but as for impl_1, individual files are generated for each step in the implementation Tcl script by virtue of file-related Tcl commands, e.g. .init_design.begin.rst, .place_design.begin.rst etc (and also end scripts). And yes, the run system is somewhat messy in that these files are created in several different ways.</p>
<p>If these files aren&#8217;t generated, the Vivado GUI will get confused on whether the runs have taken place or not. In particular, the synth_1 run will stand at &#8220;Scripts Generated&#8221; even after a full implementation.</p>
<h3>Bottom line</h3>
<p>Recall that the reason for all this diving into the Vivado runs mechanism, was to perform these runs with log output on the console.</p>
<p>The  ISEWrap.sh wrapper (actually, the way it&#8217;s used) is the reason why there is no output to console during the run&#8217;s execution. The end of runme.sh goes:</p>
<pre>ISEStep="./ISEWrap.sh"
EAStep()
{
     $ISEStep $HD_LOG "$@" <strong><span style="color: #ff0000;">&gt;&gt; $HD_LOG 2&gt;&amp;1</span></strong>
     if [ $? -ne 0 ]
     then
         exit
     fi
}

# pre-commands:
/bin/touch .init_design.begin.rst
EAStep vivado -log top.vdi -applog -m64 -messageDb vivado.pb -mode batch -source top.tcl -notrace</pre>
<p>The invocation of vivado is done by calling EAStep() with the desired command line as arguments. This is passed on by EAStep() to the wrapper as arguments, which in turn executes vivado as required, along with the creating of the begin-end files. But note the redirection (marked in red) to the log file. It goes there, but not to console.</p>
<p>So one possibility is to rewrite runme.sh slightly, and modify EAStep() so it uses the &#8220;tee&#8221; UNIX utility or doesn&#8217;t redirect at all into a log file. Or modify the wrapper for your own needs. I went for option B (there were plenty of scripts anyhow in my build system).</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2016/02/vivado-runs-tcl-bash/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Tcl scripting: Which version of Quartus am I running?</title>
		<link>https://billauer.se/blog/2013/12/quartus-tcl-revision-dump-array/</link>
		<comments>https://billauer.se/blog/2013/12/quartus-tcl-revision-dump-array/#comments</comments>
		<pubDate>Mon, 23 Dec 2013 14:28:08 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[Intel FPGA (Altera)]]></category>
		<category><![CDATA[Tcl]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=4031</guid>
		<description><![CDATA[The short answer is $quartus(version). Those familiar with Tcl immediately tell that there&#8217;s a named array (hash), $quartus, containing a key &#8220;version&#8221; which returns the full revision name. So, entering an interactive session, $ quartus_sh -s Info: ******************************************************************* Info: Running Quartus II 32-bit Shell Info: Version 13.0.1 Build 232 06/12/2013 Service Pack 1 SJ Web [...]]]></description>
			<content:encoded><![CDATA[<p>The short answer is $quartus(version). Those familiar with Tcl immediately tell that there&#8217;s a named array (hash), $quartus, containing a key &#8220;version&#8221; which returns the full revision name.</p>
<p>So, entering an interactive session,</p>
<pre>$ <strong>quartus_sh -s</strong>
<span style="color: #888888;">Info: *******************************************************************
Info: Running Quartus II 32-bit Shell
 Info: Version 13.0.1 Build 232 06/12/2013 Service Pack 1 SJ Web Edition
 Info: Copyright (C) 1991-2013 Altera Corporation. All rights reserved.
 Info: Your use of Altera Corporation's design tools, logic functions
 Info: and other software and tools, and its AMPP partner logic
 Info: functions, and any output files from any of the foregoing
 Info: (including device programming or simulation files), and any
 Info: associated documentation or information are expressly subject
 Info: to the terms and conditions of the Altera Program License
 Info: Subscription Agreement, Altera MegaCore Function License
 Info: Agreement, or other applicable license agreement, including,
 Info: without limitation, that your use is for the sole purpose of
 Info: programming logic devices manufactured by Altera and sold by
 Info: Altera or its authorized distributors.  Please refer to the
 Info: applicable agreement for further details.
 Info: Processing started: Mon Dec 23 16:08:47 2013
Info: *******************************************************************
Info: The Quartus II Shell supports all TCL commands in addition
Info: to Quartus II Tcl commands. All unrecognized commands are
Info: assumed to be external and are run using Tcl's "exec"
Info: command.
Info: - Type "exit" to exit.
Info: - Type "help" to view a list of Quartus II Tcl packages.
Info: - Type "help &lt;package name&gt;" to view a list of Tcl commands
Info:   available for the specified Quartus II Tcl package.
Info: - Type "help -tcl" to get an overview on Quartus II Tcl usages.
Info: *******************************************************************</span></pre>
<p>one can get both the Quartus revision and the Tcl version:</p>
<pre>tcl&gt; <strong>puts $quartus(version)</strong>
Version 13.0.1 Build 232 06/12/2013 Service Pack 1 SJ Web Edition
tcl&gt; <strong>info tclversion</strong>
8.5</pre>
<p>A simple regular expression can be used to fetch a clean Quartus version number:</p>
<pre>tcl&gt; <strong>regexp {[\.0-9]+} $quartus(version) clean_number</strong>
1
tcl&gt; <strong>puts $clean_number</strong>
13.0.1</pre>
<p>The first command runs the regular expression on the full version string, and finds the first sequence consisting of digits and dots. The return value is &#8220;1&#8243; because such a sequence was found. The third argument to regexp makes the interpreter put the matched string into the $clean_number variable, which is printed in the second command.</p>
<p>To list all elements in the $quartus array,</p>
<pre>tcl&gt; <strong>foreach key [array names quartus] { puts "${key}=$quartus($key)" }</strong>
version_base=13.0
ip_rootpath=/path/to/13.0sp1/ip/
copyright=Copyright (C) 1991-2013 Altera Corporation
load_report_is_needed=0
advanced_use=0
nativelink_tclpath=/path/to/13.0sp1/quartus/common/tcl/internal/nativelink/
quartus_rootpath=/path/to/13.0sp1/quartus/
processing=0
tclpath=/path/to/13.0sp1/quartus/common/tcl/
ipc_mode=0
nameofexecutable=quartus_sh
tcl_console_mode=2
natural_bus_naming=1
eda_tclpath=/path/to/13.0sp1/quartus/common/tcl/internal/eda_utils/
settings=
internal_use=0
regtest_mode=0
package_table={ddr_timing_model quartus_sta hidden} {rpwq qacv hidden} <span style="color: #888888;">[...]</span>
eda_libpath=/path/to/13.0sp1/quartus/eda/
args=
ipc_sh=0
version=Version 13.0.1 Build 232 06/12/2013 Service Pack 1 SJ Web Edition
binpath=/path/to/13.0sp1/quartus/linux/
project=
is_report_loaded=0
available_packages=::quartus::external_memif_toolkit ::quartus::iptclgen ::quartus::project ::quartus::device ::quartus::partial_reconfiguration ::quartus::report ::quartus::misc ::quartus::rapid_recompile ::quartus::incremental_compilation ::quartus::flow ::quartus::systemconsol</pre>
<p>package_table was snipped, as it was very long. I&#8217;ve also mangled the path to Quartus&#8217; files into /path/to, also in order to keep it short.</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2013/12/quartus-tcl-revision-dump-array/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Experimenting with SDC/Tcl wildcards: Quartus TimingQuest Timing Analyzer</title>
		<link>https://billauer.se/blog/2013/11/sdc-tcl-quartus-wildcards/</link>
		<comments>https://billauer.se/blog/2013/11/sdc-tcl-quartus-wildcards/#comments</comments>
		<pubDate>Sat, 09 Nov 2013 08:48:38 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[FPGA]]></category>
		<category><![CDATA[Intel FPGA (Altera)]]></category>
		<category><![CDATA[Tcl]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=3903</guid>
		<description><![CDATA[Wildcards There is a certain confusion regarding how wildcards are matched in the SDC file (in fact, by the Tcl commands), which is why full paths are often used. This causes overloaded SDC files that don&#8217;t survive changes in the hierarchy. For example, regarding get_pins, the SDC and TimeQuest API Reference Manual page 2-15 states [...]]]></description>
			<content:encoded><![CDATA[<h3>Wildcards</h3>
<p>There is a certain confusion regarding how wildcards are matched in the SDC file (in fact, by the Tcl commands), which is why full paths are  often used. This causes overloaded SDC files that don&#8217;t survive changes  in the hierarchy.</p>
<p>For example, regarding get_pins, the <a href="http://www.altera.com/literature/manual/mnl_sdctmq.pdf" target="_blank">SDC and TimeQuest API Reference Manual</a> page 2-15 states that pipe characters (&#8220;|&#8221;) are treated as  special characters, and are therefore not matched against the &#8220;*&#8221;  wildcard in the default mode. So by default, the full path has to be  given, except for specific strings within hierarchies.</p>
<p>The -hierarchical flag somewhat helps by allowing relative names (skip the beginning of the path).</p>
<p>For a classic Tcl match, where &#8216;*&#8217; can match a pipe character, use -compatibility_mode.</p>
<p>But what about get_clocks?</p>
<h3>Experimenting</h3>
<p>One significant advantage of Tcl scripting over Xilinx&#8217; UCF is that one can try out the expressions in a Tcl shell. Some basics can be found in page 3-17 of the Quartus II Handbook, vol.1, <a href="http://www.altera.com/literature/hb/qts/qts_qii52003.pdf" target="_blank">chapter 3</a>.</p>
<p>More inspiration can be taken from the examples in the <a href="http://www.altera.com/literature/manual/mnl_sdctmq.pdf" target="_blank">SDC and TimeQuest API Reference Manual.</a> It may also be helpful to look at the <a href="http://www.altera.com/literature/manual/TclScriptRefMnl.pdf" target="_blank">Quartus II Scripting Reference Manual</a>.</p>
<p>From the command line, using the &#8220;-s&#8221; flag:</p>
<pre>$ quartus_sta -s</pre>
<p>After the welcome note, open the project (after fitting) and create a timing netlist:</p>
<pre>tcl&gt; project_open <span style="color: #808080;"><em>myproject</em></span>
tcl&gt; create_timing_netlist
tcl&gt; read_sdc
tcl&gt; update_timing_netlist</pre>
<p>for effectively opening myproject.qsf. The two latter are required for get_clocks to work. One can go e.g.</p>
<pre>tcl&gt; get_clocks -long_help</pre>
<p>to get some help (same text as in the manuals).</p>
<p>It&#8217;s possible to test what matches which command. For example, to list the PLL-derived clocks (based upon the signal&#8217;s name):</p>
<pre>tcl&gt; set mypins [ get_pins -compatibility_mode *|divclk ]
tcl&gt; foreach_in_collection pin $mypins { puts [get_pin_info -name $pin] }</pre>
<p>The Tcl shell will print something like &#8220;_col0&#8243; in the middle to indicate that a collection has been set up. This collection is accessed through $mypins. The second command prints the matched pins to the console.</p>
<p>Or for those who prefer one-liners (all pins on top-level):</p>
<pre>tcl&gt; query_collection -all [ get_pins * ]</pre>
<p>The &#8220;-all&#8221; flag overrides the default limit of 20 elements. To have each printed on a separate line,</p>
<pre>tcl&gt; foreach i [ query_collection -all [ get_pins <strong><span style="color: #ff0000;">-hierarchical</span></strong> * ] ] { puts "Pin: $i" }</pre>
<p>The -hierarchical flag is important. Without it, only the toplevel pins are given (even for just [ get_pins ]). Counterintuitive, but nevertheless true. The -compatibility_mode flag is also fine (used a lot in this post) but is Quartus specific.</p>
<p>The counterpart for &#8220;-all&#8221; is &#8220;-limit 1&#8243;, which fetches only the first element.</p>
<h3>So what about get_clocks?</h3>
<p>Listing all clocks in the design on separate lines:</p>
<pre>tcl&gt; foreach i [ query_collection -all [ get_clocks ] ] { puts "$i" }</pre>
<p>Or use foreach_in_collection:</p>
<pre>tcl &gt; foreach_in_collection i [ get_clocks ] { puts [ get_clock_info $i -name ] }</pre>
<p>Note that get_clock_info can obtain information other than just the name.</p>
<p>Alternatively, use &#8220;join&#8221; instead of &#8220;foreach&#8221;:</p>
<pre>tcl&gt; join [ query_collection -all [ get_clocks ] ] "\n"</pre>
<p>If nothing is printed, and instead it says</p>
<pre>Warning (332173): Ignored filter: * could not be matched with a clock</pre>
<p>it&#8217;s most likely because read_sdc and update_timing_netlist haven&#8217;t been issued, as mentioned above.</p>
<pre>tcl&gt; foreach i [ query_collection -all [ get_clocks *|vga_pll|*|divclk] ] { puts $i }</pre>
<p>which, surprisingly enough worked in the convenient way: The wildcards matched pipe characters, so one can, in fact, use this simple format in SDC files, e.g.</p>
<pre>set_false_path -from [get_clocks *|vga_pll|*|divclk] -to [get_clocks *|bus_pll|*|divclk]
set_false_path -from [get_clocks *|bus_pll|*|divclk] -to [get_clocks *|vga_pll|*|divclk]</pre>
<p>for setting up false paths between two clocks that are derived from a common reference with PLLs, <strong>the wrong way</strong>.</p>
<p>The correct way is with <strong>set_clock_groups</strong>, but that&#8217;s not what this post is about&#8230; And by the way, Quartus doesn&#8217;t have a shortcut to include derived clocks in set_clock_groups, like in Vivado. So the PLLs&#8217; output clocks must be named explicitly (but wildcards help).</p>
<h3>Getting all kind of info</h3>
<p>If we&#8217;re at it, all kind of info can be obtained on cells in the design. For example, the location of certain instances in the design (pins etc. can also be obtained with different parameters to get_cell_info):</p>
<pre>foreach_in_collection cell [ get_cells -compatibility_mode *rx_pma.rx_cdr] { puts "[get_cell_info $cell -location ]: [get_cell_info $cell -name]" }</pre>
<h3>QSF scripting</h3>
<p>Pointing at specific cells by virtue of expressions works within a script, but not in the QSF file. For example, this works as a script</p>
<pre>set_instance_assignment -name CDR_BANDWIDTH_PRESET High -to [ get_cells -compatibility_mode *|xcvr_inst|*rx_pma.rx_cdr]</pre>
<p>but fails in a QSF file.</p>
<p>It&#8217;s possible to retrieve the already existing assignments. Try</p>
<pre>tcl&gt; get_instance_assignment -help
tcl&gt; get_all_assignments -long_help</pre>
<p>The latter gives some interesting examples on scanning existing assignments. In particular, turning one of the examples into a (very long) one-liner, once can go</p>
<pre>tcl&gt; foreach_in_collection asgn_id [get_all_assignments -type instance -name *] { set from [get_assignment_info $asgn_id -from] ; set to [get_assignment_info $asgn_id -to] ; set name   [get_assignment_info $asgn_id -name] ; set value  [get_assignment_info $asgn_id -value] ; puts "$name ($from -&gt; $to) = $value" }</pre>
<p>in order to list all instance assignments (i.e. echo the QSF&#8217;s assignments).</p>
<p>Same for all global assignments (and there are many):</p>
<pre>foreach_in_collection asgn_id [get_all_assignments -type global -name *] { set entity [get_assignment_info $asgn_id -entity] ; set name [get_assignment_info $asgn_id -name] ; set value  [get_assignment_info $asgn_id -value] ; puts "$entity: $name = $value" }</pre>
<p>Not clear what it&#8217;s useful for, but anyhow</p>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2013/11/sdc-tcl-quartus-wildcards/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
