Showing results for 
Show  only  | Search instead for 
Did you mean: 
Registered: ‎05-30-2017

My Greatest Xilinx Hack


What I'd like to share today is perhaps my greatest Xilinx hack ever. Its the journey to create an architecture-independent IP core that automatically time-stamps each build, and makes that time stamp available in firmware.

Along the way, I had to learn the answers to many other questions, like: "How do I create a user-IP with parameter propagation enabled?", "How do I get the time value automatically at build time?", "How do I have Vivado change parameters of IP at validation time?", etc. I will share my journey here in the hopes that it will help other people facing similar brick walls.

A little back story...

All of my builds needs timestamps, which will serve also to uniquely identify builds. The Zynq Processor needs to be able to read this value from firmware to know which firmware load it is dealing with. After searching the forums for help and finding some fun discussions , I decided to have a go at it myself. 3 days later I crawled out of my office with a solution.

The solution

So here's what it takes. 

  1. Create a new project in Vivado. The top level should be a source file with 3 generics/parameters: "BUILD_TIME" (32-bit unsigned int), "BUILD_TIME_STR" (string), BUILD_DATE_STR" (string), and an AXI lite slave interface that can give access to one single 32-bit register value. BUILD_TIME should be initialized to 0.port_list_stamp.png
  2. Code the single 32-bit register value to be initialized with "BUILD_TIME". This will be later populated with the 32-bit number of seconds since the 1970 epoch. If you don't already have AXI register interface code readily available, you can get started by going to Vivado -> Tools -> Create and Package New IP -> Create AXI4 Peripheral.peripheral.png
  3. Optional: Write an initial block that will check to see if BUILD_TIME is non-zero, else fatal. This will stop synthesis if something went wrong and the BUILD_TIME was not overridden. Note that verilog does not (to my knowledge) support $fatal, so this must be done in a submodule that is systemverilog. The top level must be VHDL or Verilog to be a valid top module for the IP packager.initial_check.png
  4. Package the project as IP (Vivado -> Tools -> Create and Package New IP -> Package your current project) , taking care to associate your AXI clock to your AXI lite bus in the ports and interfaces tab. This association will happen automatically if interfaces were specified in RTL (see attributes in image in step 1. You can find more attributes like this in Vivado -> Language Templates -> Verilog/VHDL -> IP Integrator HDL.
  5. In the Customization GUI tab of the IP packager, double click BUILD_TIME and specify as invisible, but editable, with a default value of 0.build_time.png
  6. In the same tab, specify BUILD_TIME_STR as visible, non-editable, with dependency set to the following expression: expr 1?{[clock format $BUILD_TIME -format {%T %Z} ]}:"". This dependency expression is Xilinx's way of letting you set the value of a parameter to be based off of another parameter's value. Its read by Vivado as a TCL command. However, Vivado limits you to only be allowed to do very specific expression-type commands. Well, we don't want that, we want to convert $BUILD_TIME here to a human-readable format. If we try to do so directly with: clock format $BUILD_TIME -format {%T %Z}, Vivado will bark at us with an error because its not an expression. To get around this, I found that if we start any command here with the TCL keyword "expr", then Vivado will give us a free pass for anything that follows. Lucky for us, there is one single operator that "expr" evaluates which allows us to pass through whatever we want, and that is the inline if-then-else operator (see here for details)! Yay! So we just do this now: expr 1?{anything}:"". Brick wall averted.build_time_str.png
  7. Repeat step 6 with BUILD_DATE_STR, with the following expression: expr 1?{[clock format $BUILD_TIME -format %D ]}:""
  8. Package the IP. Note we're not done yet.
  9. At this point, Vivado will have dumped you a nice little .zip file with you IP core data. At this point, the IP core will do everything we want it to do, except that it won't populate the BUILD_TIME parameter automatically at build time. To do this, start by unzipping your .zip file (note that in Linux, simply hitting "extract here" won't unzip sub directories. You'll need to do the command line: unzip -d <directory to unzip to> <zip file>
  10. Create another directory called bd and paste into this directory the bd.tcl file that I attached. This deserves some explanation. Apparently, when you instantiate an IP core into Vivado's IP integrator, Vivado will look to see if that IP core has a bd directory with bd.tcl in it. If so, it will run functions in that file that has certain names at certain times. Look at the file to follow along. You'll see the init function. This function is run when the IP initializes (ie its added to the BD, or the project opens). There's a function Vivado will run at the beginning of validation called pre_propagate, and a function that it runs at the end of validation called propagate.
  11. Modify the init function so the pins match your IP's pin names. You'll notice that while we're here, I took the opportunity to change the clock parameter value sources to be "PROPAGATED". The IP packager by default will set this to CONSTANT, which means that you have to manually override clock values in your BD to match your IP in order to pass validation. This modification eliminates this pesky issue. There's a forum post on this here .
  12. Note the pre_propagate function. You don't need to modify this, but it is important to understand that this function is run at the beginning of validation, and will grab the current time in seconds with the TCL [time seconds] command, and update the hidden (but editable) BUILD_TIME parameter. The Vivado hooks are already in place by the IP packager to detect when BUILD_TIME changes, and to run the dependency expressions we wrote in step 6 and 7.
  13. Copy your newly modified IP directory to wherever you want. To use it, simply set it as a project repository, as described in UG896 , section "Managing IP Repositories".

You may be wondering why I chose to split up BUILD_TIME_STR and BUILD_DATE_STR. The reason is because these are really present just to give user feedback. With these, you can open up the IP core and see what the last build stamp was, in human readable format. However, the amount of space that Vivado will give you for a parameter string in the IP GUI is limited. I had to split it up to be able to view it all.

The other failed attempts

Before reaching this solution, I tried several other things:

  1. Using the USR_ACCESS primitive, as outlined here . Using this primitive, you can set an XDC constraint to load the build timestamp into this primitive's register, which readable in fabric. I found this to be a fairly reasonable approach. There's some talk about it here . However, this primitive is no longer available in versal. That's a no-go for future-proofing for me. This is suppose to be an IP core I can use years into the future.
  2. Using synthesis tcl.pre. There are several problems with this. The first one is that we need to be able to mark the IP core as out-of-date some time before the build start. As noted in the solution here , tcl.pre does not have access to the project settings. I tried to get around this by running and changing directories in the tcl script and reading the .vds file to try to see what the parameter was bound to, and touching source files to mark the design as out-of-date. This got real messy real fast, and ultimately stopped working as soon as I imported the IP into another project, because the relative path to the parent project became unknown.
  3. Using RTL. I actually was able to come up with something in RTL (that drops directly into IP integrator) that almost worked. However, this required me to do way to much hacking like in option 2 which made me decide against it. It would be too much to set up in other projects (or expect others to know how to years into the future).
2 Replies
Registered: ‎07-09-2009

Well done you

 it shows the ingenuity of humans to over come,

Its just a pity that AMD / Xilinx after all these decades of us asking has not, unlike other companies, added the ability to easily pass in a time / date / version number, 


   before some one says , but you can,

   yes the Xilinx answer is to use a fully TCL driven batch design build !!

As some one once said, to paraphrase, given enough random TCL you can do anything !!!!!



<== If this was helpful, please feel free to give Kudos, and close if it answers your question ==>
Registered: ‎05-30-2017

Thank you! Also, here's a screenshot of the final solution. The time / date updates every time you validate the design.