Vivado: An ISE guy’s exploration notes

This post was written by eli on May 4, 2014
Posted Under: FPGA,Vivado

Just a few things I wrote down for myself, as I got acquainted with Vivado 2014.1 (having worked with ISE until now).

Kicking off in Linux

$ cd trashcan-directory
$ . /path/to/Vivado_2014.1/Vivado/2014.1/
$ vivado &

or running Vivado with a certain Tcl script

$ vivado -mode batch -source /path/to/project.tcl

or implementing a project without GUI:

$ vivado -mode tcl

and then at Vivado’s Tcl prompt:

open_project /path/to/example.xpr
launch_runs -jobs 8 impl_1 -to_step write_bitstream
wait_on_run impl_1

Of course, this can be the content of a file, in which case the -batch is used to run it (the “exit” at the end is then redundant).

In batch mode, Tcl code is printed out as it’s executed. The -notrace flag on vivado’s command line shuts this off. This flag also applies to the “source” command inside a Tcl script.


  • Vivado is very sensitive to which directory it’s run from. Not just journal files, but also Tcl script executions. New projects are created in that directory by default.
  • One can’t just move a Vivado project. Many file dependencies are internally stated as absolute paths (IP in particular)
  • “reset_project” in Vivado’s Tcl console is probably the parallel to “Cleanup Project files”. Not really, but close.
  • The instantiation template of a Block Design (e.g. “system”) is given as e.g. hdl/system_wrapper.v in the block design’s dedicated directory.
  • Make sure that the flow navigator is at the left. Or restore it with “View > Show Flow Navigator”. Otherwise it gets confusing.
  • Vivado has no problem importing NGC files (synthesized netlists from ISE) and EDIFs. As a matter of fact, NGC files are translated into EDIFs by Vivado automatically as the Vivado project is implemented.
  • DCP = Design checkpoint. It’s a file blob resulting from the partial implementation of a sub-block, typically an IP. So it’s a bit like an NGC or EDIF, but not as easy to understand.
  • For the optimistic: To import some settings from an XPS .xml file: Right-click the Zynq block, pick Customize block, and pick Import XPS Settings (at the top), and pick .xml file. Use the file in system/data, because the exported file is empty(!). Only some attributes are imported. This doesn’t work very well.
  • To import an IP from another path (packed as IP), go to IP Catalog, right-click > IP Settings… > Add Repository
  • In an IP, all information is stored in component.xml. Interfaces, ports, file names. Everything. More about it in this post.
  • But the file to be included in a project for defining the IP is the XCI file. The component.xml file is generated as Vivado “unpacks” the project.
  • Vivado’s IP core definitions are at /path/to/Vivado_2014.1/Vivado/2014.1/data/ip/xilinx/
  • When packaging an existing core, use “package your project” and not “Create a new AXI4 peripheral”, even if a new AXI peripheral is created. The latter creates an empty core.
  • Definitions of bus interfaces (“conduits”) can be found in /path/to/Vivado_2014.1/Vivado/2014.1/data/ip/interfaces/. According to this thread and this one, there is probably no sane way to make custom interfaces.
  • Unlike ISE, don’t include a black box HDL file in the sources when an IP is used. Just add the IP’s XCI file. There should be no system.v, no fifo32x512.v etc.
  • In XCI/XML files, $PPRDIR can be used to represent the project’s root directory (where the .xpr is), possibly in conjunction with ../../-kind of paths for directories above it. This is true for paths given without quotes as well (e.g. USER_REPO_PATHS in XCI files).
  • In a block design involving a Zynq processor, protocol converters are found in the .bd file as “axi_protocol_converter” and with names going e.g. “system_auto_pc_19″. In general, “pc” and “auto_pc” are related to protocol conversion.
  • Unlike ISE, there is no such thing as “unrelated clocks” in Vivado. If two clocks are from origins with unknown relations, they are considered “Timed” and “unsafe” in the Clock Interaction report. The relevant paths will be calculated, and are likely to fail timing. To handle this, either make a false path between each pair of such clocks, or use set_clock_groups to tell set up groups of clock that are mutually unrelated.
  • There was a problem with connecting KC705 to Vivado 2015.2 over JTAG. Xilinx’ JTAG cable worked fine, and KCU105 too. Comparing with my laptop computer, which worked fine with the same Vivado version, it turned out that when things work properly, /dev/ttyUSB0 appears briefly and is soon removed, and only /dev/ttyUSB1 is left when the KC705 card is connected. On the computer it didn’t work on, both /dev/ttyUSB0 and /dev/ttyUSB1 were left. The dmesg log showed that the proper sequence is that the first USB interface is “disconnected” almost immediately after connection. This sounds very much like a Cypress EasyUSB re-enumeration that didn’t complete properly on the faulty computer. My hunch is that’s it’s a firmware loading issue, but I didn’t follow this up. On both computers the permissions were the same (rw for root user and group, the latter was dialout). It’s not the issue.

Some Tcl insights

There’s a post about setting up a Vivado project from scratch using Tcl. And another post on XDC constraints.

  • File > Write Project tcl… is very useful for getting the project in a nutshell. There’s also File > Export… > Export Block Design, which creates a Tcl file that sets up the block design (command write_bd_tcl).
  • The real Tcl documentation is with “help” commands at Tcl prompt, e.g. “help startgroup”. Or UG835.
  • Run a Tcl script:
    source {/path/to/script.tcl}
  • To test “get_ports” and such, open “Elaborated Design” (on the Flow navigator) or use “open_run synth_1 -name netlist_1″. get_ports and get_pins etc. work on that set. Or all properties with
    set x [get_port "DDR_WEB"]; foreach p [list_property $x] { puts "$p = [ get_property $p $x ]" }
  • The Tcl script for a certain run is given as e.g. project.runs/impl_1/ptoject.tcl. There’s also a file in the same directory, which can run standalone for that run.
  • Using launch_runs in batch mode will cause the shell prompt to be returned before the implementation is done. Use wait_on_run to block until the run is done.
  • Obtain a timing report on paths (50 worst) going through a certain set of nets:
    report_timing -name my_timing_report -nworst 50 -through [get_nets -hier -filter {name=~*/wr_client_0_ins/*}]

    or from a set of cells:

    report_timing -name my_timing_report -nworst 50 -from [get_cells -hier -filter {name=~*/wr_client_0_ins/*}]

    Note that the -name parameter causes the output to go to a timing tab in the GUI. Without it, a textual report goes to the console. Or if -file is specified, to a file.
    And report all I/O ports’ timing:

    report_timing -delay_type min_max -max_paths 100 -name timing_1 -to [get_ports -filter {direction==out}]
    report_timing -delay_type min_max -max_paths 100 -name timing_2 -from [get_ports -filter {direction==in}]
  • Search for nets matching a name patters (where “*” may substitute slashes):
    join [ get_nets -match_style ucf */sys_clk* ] "\n"
  • Get the cells touching a net (including the hierarchy) or the BELs:
    join [get_cells -of_objects [ get_nets -match_style ucf */pcie_ref_clk* ] ] "\n"
    join [get_bels -of_objects [ get_nets -match_style ucf */pcie_ref_clk* ] ] "\n"

The PS pins bug

The ARM processor in a Zynq architecture (“PS”) has many pins that are connected directly to the processor’s hard IP core, without any relation to the FPGA (“PL”) logic fabric. The obvious evidence to this fact is that the processor is able to boot before the PL part is configured. The attributes of these pins are controlled completely by the ARM processor’s registers, and regardless of the PL configuration, if any.

And still, Xilinx has chosen to include these pins in the processor’s model in the logic design. In sample HDL designs, these pins are wired from the processor’s instance to the toplevel ports, as if it mattered. This wiring is completely meaningless to the bitstream that is compiled from the HDL. In fact, it’s probably meaningless in any respect.

As for Vivado, it seems like four signals, PS_CLK, PS_PORB, PS_SRSTB, and DDR_WEB (plus sometimes DDR_CLK) are treated, at some stages, as general I/Os and not direct connections to the PS7. This is evident in the toplevel schematic view of the project, where IBUFs an OBUFs are added to the PS_ ports, and DDR_CLK’s port is left floating. This causes Vivado to allocate “real” PL I/O pins to these signals, and then complain about them being unplacable and/or unroutable. The simple solution to this is to disconnect these signals from the toplevel port, possibly by turning them to wires, rather than ports, on the toplevel module. Vivado responds with treating these as useless signals, and therefore doesn’t bother itself any more with them. Except for complaining that the placement constraints in the processor-dedicated XDC constraint file can’t be applied.

In fact, there is no reason to connect any of these PS signals to real ports in Vivado. Having said that, ISE related tools may complain if these signals aren’t connected to ports, in particular when the toplevel module is in VHDL.

So the name of the game is to connect those PS signals to the toplevel port, or disconnect them, whatever makes the tools happy. It doesn’t make any real difference anyhow.

Getting the frequency of a clock

I wanted to be sure that the DDR memory controller’s user interface frequency was what I thought it was. That turned out no to be so simple. I implemented the example design, and tried to find what stands behind c0_clk or aclk or c0_ui_clk or any of the names it has. But in Vivado, nets tend to have many names (“aliases”), depending on their names in different modules. Someone must have thought it’s a great idea.

True, it’s possible to get a list of clocks in the timing report, or use the Tcl command


but there was no sign of anything resembling the known names mentioned above. Now, there must be a way better than what I did, but this is what I can suggest: Pick a register that is clocked by the desired clock. For example, in mig_7series_v2_0_tg.v, it says

  always @(posedge clk) begin
    if (tg_state[TG_IDLE] | tg_state[TG_UPDT_CNTR])
      curr_rd_ptr <= 1'b0;
    else if (cmd_rd_en)
      curr_rd_ptr <= ~curr_rd_ptr;

and clk is known to be the AXI clock.

Now look it up. Method #1 is to find the clocks attached to a cell: In the Tcl console

get_clocks -of_objects [get_cells -hierarchical -filter {name=~*cmd_rd_en*}]

Assuming that only one clock is given, look it up in the list of clocks of the Timing Summary Report.

To be absolutely sure, let’s go to method #2: Produce a mini timing report for the cell. Again, in the Tcl console (the get_cell query is equivalent, given here for variety):

join [ get_cells -match_style ucf *cmd_rd_en* ] "/n"

OK nice, there’s one single register with matching that name. It’s a bit hard to be sure we’re exactly on the spot, but that is soon resolved.

To get the clock information, get specific timing info for that specific cell:

report_timing -from [ get_cells -match_style ucf *cmd_rd_en* ]

[ ... ]

Timing Report

Slack (MET) :             5.327ns  (required time - arrival time)
  Source:                 c1_u_axi4_tg_inst/traffic_gen_inst/cmd_rd_en_reg/C
                            (rising edge-triggered cell FDRE clocked by clk_pll_i_1  {rise@0.000ns fall@3.000ns period=6.000ns})
  Destination:            c1_u_axi4_tg_inst/traffic_gen_inst/wr_proc_reg/D
                            (rising edge-triggered cell FDRE clocked by clk_pll_i_1  {rise@0.000ns fall@3.000ns period=6.000ns})
  Path Group:             clk_pll_i_1
  Path Type:              Setup (Max at Slow Process Corner)
  Requirement:            6.000ns  (clk_pll_i_1 rise@6.000ns - clk_pll_i_1 rise@0.000ns)
  Data Path Delay:        0.633ns  (logic 0.266ns (42.005%)  route 0.367ns (57.995%))
  Logic Levels:           1  (LUT4=1)

[ ... ]

The full output also showed the worst timing path, but that’s not the point.

What we have here is the source and destination points in human-readable format, so there’s no confusion about which register we’re talking about. And there’s the definition of the clock, and its period, 6 ns (scroll to the far right). So there we have it. The AXI clock, in this case, runs at 166.66 MHz.

Getting a list of paths between clocks

Knowing which clock is which, this information can be used to list paths that cross clock domains. This is useful to verify that no mixed-clock path has sneaked in inadvertently. For example,

join [get_timing_paths -filter {name=~*/rd_client_7_ins/*} -max_paths 100000 -from [get_clocks clk_pll_i] -to [get_clocks clkout2]] "\n"
WARNING: [Timing 38-164] This design has multiple clocks. Inter clock paths are considered valid unless explicitly excluded by timing constraints such as set_clock_groups or set_false_path.
{mem_top_ins/rd_client_7_ins/localreset_reg/C --> mem_top_ins/rd_client_7_ins/localreset_sync_reg/D}
{mem_top_ins/rd_client_7_ins/localreset_reg/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/rstblk/ngwrdrst.grst.g7serrst.rd_rst_asreg_reg/PRE}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[2]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[2]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[4]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[4]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[6]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[6]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[7]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[7]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[1]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[1]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[0]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[0]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[3]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[3]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[5]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[5]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/wr_pntr_gc_reg[8]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].rd_stg_inst/Q_reg_reg[8]/D}
join [get_timing_paths -filter {name=~*/rd_client_7_ins/*} -max_paths 100000 -to [get_clocks clk_pll_i] -from [get_clocks clkout2]] "\n"
WARNING: [Timing 38-164] This design has multiple clocks. Inter clock paths are considered valid unless explicitly excluded by timing constraints such as set_clock_groups or set_false_path.
{mem_top_ins/rd_client_7_ins/consumed_toggle_reg/C --> mem_top_ins/rd_client_7_ins/consumed_toggle_sync_reg[0]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[8]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[8]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[5]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[5]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[6]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[6]/D}
{mem_top_ins/reposition_7_ins/allow_new_frame_reg/C --> mem_top_ins/rd_client_7_ins/allow_new_frame_d_reg/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[4]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[4]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[1]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[1]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[3]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[3]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[7]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[7]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[0]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[0]/D}
{mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/rd_pntr_gc_reg[2]/C --> mem_top_ins/rd_client_7_ins/fifo_wide_rd/U0/inst_fifo_gen/gconvfifo.rf/grf.rf/gntv_or_sync_fifo.gcx.clkx/gsync_stage[1].wr_stg_inst/Q_reg_reg[2]/D}

Important: It seems like -max_paths counts paths prior to filtering (on Vivado 2014.1), which is why several paths shown above didn’t appear when it was set to 20. So to be sure to cover all paths, eventually give a very large number, as shown above.

Note that there’s an implicit -hier flag enabled, so * matches across hierarchies. As seen in the example above, it’s enough that one of the endpoints match the string. The paths shown in the example above are ignored for timing, so this proves that all paths should appear, even those not timed.

Programming the FPGA on Linux

The interface in Vivado with hardware cables is through two servers: vcse_serv which listens to TCP port 60001 and hw_server listening to TCP port 3121. These are usually started automatically when Vivado is launched, but they can be kicked off manually with the hw_server command, in particular when the server needs to be accessed from a remote computer.

A common problem is that the server runs with the privileges of the user who started Vivado, but accessing the device files that are related to the configuration cable requires root access. Vivado will say that “There is no valid target connected to the server”. In other words, no card was detected — because the server wasn’t granted access.

Recent development boards arrive with a USB plug for direct connection to the PC, so a PC cable isn’t required. For the KC705 board, the USB support origins from Digilent.

There is an explanation for how to install the drivers on this page. It tells me to change directory to {some path}/cable_drivers/lin64/digilent/ and run ./ as root. I don’t run stranger’s scripts as root. But in this case it isn’t too bad. It just says

cp -f 52-xilinx-digilent-usb.rules /etc/udev/rules.d/52-xilinx-digilent-usb.rules
chmod 644 /etc/udev/rules.d/52-xilinx-digilent-usb.rules

Looking at the udev file in question, it merely allows read-write access to everyone (mode 0666) for device files generated by devices with vendor ID 0x1443 or vendor ID 0x0403 and the Manufacturer string set to “Digilent”. Seems harmless enough to me.

After installing the udev rule, disconnect and reconnect the cable, and kill the two server processes. After this, Vivado detects the card OK. In fact, going

$ killall hw_server

seems to be necessary every now and then, when the card has gone off and on again, and isn’t detected.

Or maybe… I should have run the all-driver installation script to begin with? First fxload and libusb (is it necessary?)

# apt-get install fxload libusb-dev

Under Vivado/2014.1/data/xicom/cable_drivers/lin64/install_script/install_drivers/, as root:

# ./install_drivers
--Install log = /.xinstall/install.log
--Installing cable drivers.
--Script name = ./install_drivers
--HostName = .....
--Current working dir = ...
--Kernel version = 3.13.0-35-generic.
--Arch = x86_64.
--Installer version = 1101
--Unsetting ARCH environment variable.
--User has root permission.
--Installing USB drivers------------------------------------------
--File /etc/hotplug/usb/xusbdfwu.fw/xusbdfwu.hex does not exist.
--File version of /etc/hotplug/usb/xusbdfwu.fw/xusbdfwu.hex = 0000.
--Updating xusbdfwu.hex file.
--File /etc/hotplug/usb/xusbdfwu.fw/xusb_xlp.hex does not exist.
--File version of /etc/hotplug/usb/xusbdfwu.fw/xusb_xlp.hex = 0000.
--Updating xusb_xlp.hex file.
--File /etc/hotplug/usb/xusbdfwu.fw/xusb_emb.hex does not exist.
--File version of /etc/hotplug/usb/xusbdfwu.fw/xusb_emb.hex = 0000.
--Updating xusb_emb.hex file.
--File /etc/hotplug/usb/xusbdfwu.fw/xusb_xpr.hex does not exist.
--File version of /etc/hotplug/usb/xusbdfwu.fw/xusb_xpr.hex = 0000.
--Updating xusb_xpr.hex file.
--File /etc/hotplug/usb/xusbdfwu.fw/xusb_xup.hex does not exist.
--File version of /etc/hotplug/usb/xusbdfwu.fw/xusb_xup.hex = 0000.
--Updating xusb_xup.hex file.
--File /etc/hotplug/usb/xusbdfwu.fw/xusb_xp2.hex does not exist.
--File version of /etc/hotplug/usb/xusbdfwu.fw/xusb_xp2.hex = 0000.
--Updating xusb_xp2.hex file.
--File /etc/hotplug/usb/xusbdfwu.fw/xusb_xse.hex does not exist.
--File version of /etc/hotplug/usb/xusbdfwu.fw/xusb_xse.hex = 0000.
--Updating xusb_xse.hex file.
cat: /etc/hotplug/usb.usermap: No such file or directory
--Adding Product ID 0007 to the usermap.
--Adding Product ID 0009 to the usermap.
--Adding Product ID 000d to the usermap.
--Adding Product ID 000f to the usermap.
--Adding Product ID 0013 to the usermap.
--Adding Product ID 0015 to the usermap.
--Adding Product ID 0008 to the usermap.

--Digilent Return code = 0
--Xilinx Return code = 0
--Return code = 0
--Driver installation successful.

But that’s based upon /etc/hotplug, which Ubuntu doesn’t seem to care much about. But reading through /etc/hotplug/usb/xusbdfwu it’s quite evident how to match between the firmware files and the device IDs, leading to the conclusion that my red JTAG platform cable should be programmed with the firmware with

# fxload -v -t fx2 -D /dev/bus/usb/002/010 -I /etc/hotplug/usb/xusbdfwu.fw/xusb_xp2.hex

The bus path 002/010 was derived from its appearance in lsusb:

Bus 002 Device 010: ID 03fd:0013 Xilinx, Inc.

The interesting thing is that the device’s LED goes on only following the load of the firmware.

So let’s make a udev file of this, based upon this post: Copy-paste this into 52-xilinx-jtag-platform.rules

# udev rules for programming Xilinx' JTAG platform cables
ATTRS{idVendor}=="03fd", ATTRS{idProduct}=="0008", MODE="666"
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="03fd", ATTRS{idProduct}=="0007", RUN+="/sbin/fxload -t fx2 -I /etc/hotplug/usb/xusbdfwu.fw/xusbdfwu.hex -D $tempnode"
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="03fd", ATTRS{idProduct}=="0009", RUN+="/sbin/fxload -t fx2 -I /etc/hotplug/usb/xusbdfwu.fw/xusb_xup.hex -D $tempnode"
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="03fd", ATTRS{idProduct}=="000d", RUN+="/sbin/fxload -t fx2 -I /etc/hotplug/usb/xusbdfwu.fw/xusb_emb.hex -D $tempnode"
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="03fd", ATTRS{idProduct}=="000f", RUN+="/sbin/fxload -t fx2 -I /etc/hotplug/usb/xusbdfwu.fw/xusb_xlp.hex -D $tempnode"
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="03fd", ATTRS{idProduct}=="0013", RUN+="/sbin/fxload -t fx2 -I /etc/hotplug/usb/xusbdfwu.fw/xusb_xp2.hex -D $tempnode"
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="03fd", ATTRS{idProduct}=="0015", RUN+="/sbin/fxload -t fx2 -I /etc/hotplug/usb/xusbdfwu.fw/xusb_xse.hex -D $tempnode"

The platform cable’s LED should go on soon after plugging it into the computer. This should work on any platform cable.

If this doesn’t work, make sure manually that the programming with fxload works.

Programming the QSPI flash

Starting with Vivado 2014.1, the mcs file can be created with Vivado. Before this version, ISE’s promgen had to be used. Let’s assume a QPSI flash of 128M (e.g. N25Q128) connected to a Virtex device.

The command for creating an MCS file is

write_cfgmem -format mcs -interface SPIx4 -size 128 -loadbit {up 0x0 /path/to/virtex.bit} starter-virtex.mcs
Creating config memory files...
Creating bitstream load up from address 0x00000000
Loading bitfile /path/to/virtex.bit
Writing file /path/to/starter-virtex.mcs

As mentioned in the second post of this thread, the following lines should be added to a constraint file (.xdc) before implementing the design:

set_property BITSTREAM.Config.SPI_BUSWIDTH 4 [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]

The second line sets the configuration rate to 50 MHz, which is probably desirable for a quick load. The first line is required for the MCS file creating. Failing to have it will lead to the following error when attempting to build the mcs file:

write_cfgmem -format mcs -interface SPIx4 -size 128 -loadbit {up 0x0 /path/to/virtex.bit} starter-virtex.mcs
Creating config memory files...
Creating bitstream load up from address 0x00000000
Loading bitfile /path/to/virtex.bit
ERROR: [Vivado 12-3619] Cannot create SPIX4 PROM for bitfile /path/to/virtex.bit with SPI_buswidth setting of "None".
ERROR: [Common 17-39] 'write_cfgmem' failed due to earlier errors.

For those who forgot to do this, and don’t feel like re-implementing the entire design, open the implemented design, and create the bitfile with the property set.

set_property BITSTREAM.Config.SPI_BUSWIDTH 4 [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
write_bitstream /path/to/virtex.bit

Note that using the GUI button for this purpose will not work.

It’s also possible to set the SPI_BUSWIDTH and CONFIGRATE properties by opening Tools > Edit Device Properties… while having an implemented design opened (otherwise this menu items doesn’t appear).

Open the hardware manager, and open the target, just like for programming a bitfile. Then pick “Add Configuration Memory Device” at the bottom of the Flow Navigator to the left, and choose the FPGA for which the flash should be programmed. The next popup window suggests to program the device immediately. Go for it. In the following window pick the MCS file generated above, and pick the “Erase”, “Write” and “Verify” options (“Blank Check” can be left unselected).

The programming runs in four phases:

  • Programming the FPGA with a bitfile that turns the FPGA into a programmer for its flash
  • “Step 1″: Erasing the FPGA
  • “Step 2″: Writing to the FPGA
  • “Step 3″: Verifying

All in all, 15 minutes for a 7V330 device isn’t unusual.


Reader Comments


I am using Vivado 2014.1 WebPack on a custom ZYNQ board. I have run into a strange issue, I run a Helloworld, see the output. Then I power-cycle the board. I can not get to see the Hello World again. So the project I created seems to be one shot. Have you faced something like this before?

Best Regards,

Written By Berk on June 6th, 2014 @ 17:15


If you’re referring to a situation where the board sometimes boots up when you power it on, and sometimes it doesn’t, it’s indeed not a pleasant situation.

It can be basically everything, but I would start with taking an oscilloscope and verify that supply voltages, clocks and resets are waking up in the right timing sequence.

Written By eli on June 6th, 2014 @ 18:35

Thank you for your answer. Actually in ISE 14.7, everything works fine. I can see the output. It just happens in Vivado. However another mysterious thing is, after ISE works and if I dont powercycle the board and download the VIVADO bitstream, it works as well. No idea of whats going on..

Written By Anonymous on June 10th, 2014 @ 09:25

Iam using Vivado 2014.2 and using virtex 7, Iam trying to flush a design through pnr implementation and wnated to know if it could be done without any constraints, just the netlist and go ahead with implementation. Is it possible at all in Vivado, as Iam trying to identify specific design parameters by doing this exercise and do not have a lot time to make a xdc for the design given the fact that the design is pretty complex and huge…Could you help with this regards.

Thanks & Regards

Written By xhay on October 12th, 2014 @ 19:53

Regarding the PS pins bug, it seems that the Vivado processing_system7 IP makes these 4 pins inout, rather than the in (for PS_*) and out (DDR_WEB) pins that XPS creates. I’ve found that simply changing them to inout and connecting them as top level ports results in no errors or warnings. This does however mean that any HDL needs to be modified depending on whether the design is in ISE or Vivado.

I did also have a similar issue with DDR_Clk in a Xillybus design, but it turned out to be a typo in the system.v file: DDR_ck_p was mapped to processing_system7_0_DDR_Clk_p when processing_system7_0_DDR_Clk was declared as a port.

Written By Jack Lovell on December 18th, 2014 @ 14:55

Thanks for this post.

The old ISE would win not any interface awards from me, but the Vivado GUI is like they read multiple top 20 lists of “Common GUI Errors & User Hostility” and then implemented every thing on them.

At the end of the work day I’d rather they spent the dev dollars on the back end, but, my gods, hire *one* GUI consultant at least. Faster synthesis stymied by slower interfacing isn’t an overall gain.

Written By PaulB on March 18th, 2016 @ 17:57

Another useful thing is to compress bitstream property.

set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]

By default the bitstream is not compressed. For small designs and large chips the gain is 50-60MByte. That translates into much faster configuration.

Written By Evgeni Stavinov on February 2nd, 2017 @ 21:51

Add a Comment

required, use real name
required, will not be published
optional, your blog address