From 18c6f9d68108af19a4cd5041534d90852e9eea60 Mon Sep 17 00:00:00 2001 From: Shareef Jalloq Date: Fri, 12 Apr 2024 21:45:38 +0100 Subject: [PATCH] feat[vivado]: fix for #426 This commit updates tools/vivado.py to allow the user to skip the link_design step normally used after reading in a toplevel netlist. The use case here is when an EDIF netlist, or any other netlist, is read in as a submodule and is not the toplevel. In this case the link_design step will fail as Vivado is expecting a netlist with the toplevel name. In order to bypass the link_design step, use the no_link_design tag when specifying the netlist in your Core file: filesets: netlist: files: - cute_a7_core/cute_a7_core.edf: { file_type: edif, tags: [no_link_design] } --- edalize/tools/vivado.py | 5 +- tests/test_vivado.py | 48 ++++++++ tests/test_vivado/edif_netlist/Makefile | 30 +++++ .../edif_netlist/test_vivado_0.tcl | 19 ++++ .../edif_netlist/test_vivado_0_pgm.tcl | 105 ++++++++++++++++++ .../edif_netlist/test_vivado_0_run.tcl | 33 ++++++ .../edif_netlist/test_vivado_0_synth.tcl | 9 ++ tests/test_vivado/edif_netlist/vivado.cmd | 2 + .../edif_netlist_no_link_design/Makefile | 30 +++++ .../test_vivado_0.tcl | 18 +++ .../test_vivado_0_pgm.tcl | 105 ++++++++++++++++++ .../test_vivado_0_run.tcl | 33 ++++++ .../test_vivado_0_synth.tcl | 9 ++ .../edif_netlist_no_link_design/vivado.cmd | 2 + 14 files changed, 447 insertions(+), 1 deletion(-) create mode 100644 tests/test_vivado/edif_netlist/Makefile create mode 100644 tests/test_vivado/edif_netlist/test_vivado_0.tcl create mode 100644 tests/test_vivado/edif_netlist/test_vivado_0_pgm.tcl create mode 100644 tests/test_vivado/edif_netlist/test_vivado_0_run.tcl create mode 100644 tests/test_vivado/edif_netlist/test_vivado_0_synth.tcl create mode 100644 tests/test_vivado/edif_netlist/vivado.cmd create mode 100644 tests/test_vivado/edif_netlist_no_link_design/Makefile create mode 100644 tests/test_vivado/edif_netlist_no_link_design/test_vivado_0.tcl create mode 100644 tests/test_vivado/edif_netlist_no_link_design/test_vivado_0_pgm.tcl create mode 100644 tests/test_vivado/edif_netlist_no_link_design/test_vivado_0_run.tcl create mode 100644 tests/test_vivado/edif_netlist_no_link_design/test_vivado_0_synth.tcl create mode 100644 tests/test_vivado/edif_netlist_no_link_design/vivado.cmd diff --git a/edalize/tools/vivado.py b/edalize/tools/vivado.py index 6736b1e5d..3c904cf60 100644 --- a/edalize/tools/vivado.py +++ b/edalize/tools/vivado.py @@ -105,6 +105,7 @@ def setup(self, edam): has_xci = False unused_files = [] bd_files = [] + netlist_flow = False dep_files = [] for f in self.files: @@ -119,6 +120,8 @@ def setup(self, edam): elif file_type == "edif": cmd = "read_edif" edif_files.append(f["name"]) + if not "no_link_design" in f.get("tags", []): + netlist_flow = True elif file_type.startswith("vhdlSource"): cmd = "read_vhdl" if file_type == "vhdlSource-2008": @@ -176,7 +179,7 @@ def setup(self, edam): "vlogparam": self.vlogparam, "vlogdefine": self.vlogdefine, "generic": self.generic, - "netlist_flow": bool(edif_files), + "netlist_flow": netlist_flow, "has_vhdl2008": has_vhdl2008, "has_xci": has_xci, "bd_files": bd_files, diff --git a/tests/test_vivado.py b/tests/test_vivado.py index f576d0cce..56d85d448 100644 --- a/tests/test_vivado.py +++ b/tests/test_vivado.py @@ -98,3 +98,51 @@ def test_vivado_board_file(make_edalize_test): tf.backend.build() tf.compare_files(["vivado.cmd"]) + + +def test_vivado_edif_netlist(make_edalize_test): + tf = make_edalize_test( + "vivado", + ref_dir="edif_netlist", + files=[{"name": "netlist.edif", "file_type": "edif"}], + param_types=["generic", "vlogdefine", "vlogparam"], + tool_options={"part": "xc7a35tcsg324-1"}, + ) + tf.backend.configure() + tf.compare_files( + [ + "Makefile", + tf.test_name + ".tcl", + tf.test_name + "_synth.tcl", + tf.test_name + "_run.tcl", + tf.test_name + "_pgm.tcl", + ] + ) + + tf.backend.build() + tf.compare_files(["vivado.cmd"]) + + +def test_vivado_edif_netlist_no_link_design(make_edalize_test): + tf = make_edalize_test( + "vivado", + ref_dir="edif_netlist_no_link_design", + files=[ + {"name": "netlist.edif", "file_type": "edif", "tags": ["no_link_design"]} + ], + param_types=["generic", "vlogdefine", "vlogparam"], + tool_options={"part": "xc7a35tcsg324-1"}, + ) + tf.backend.configure() + tf.compare_files( + [ + "Makefile", + tf.test_name + ".tcl", + tf.test_name + "_synth.tcl", + tf.test_name + "_run.tcl", + tf.test_name + "_pgm.tcl", + ] + ) + + tf.backend.build() + tf.compare_files(["vivado.cmd"]) diff --git a/tests/test_vivado/edif_netlist/Makefile b/tests/test_vivado/edif_netlist/Makefile new file mode 100644 index 000000000..d3085f502 --- /dev/null +++ b/tests/test_vivado/edif_netlist/Makefile @@ -0,0 +1,30 @@ +#Auto generated by Edalize + +all: post_build + +pre_build: + +test_vivado_0.xpr: test_vivado_0.tcl netlist.edif netlist.edif | pre_build + $(EDALIZE_LAUNCHER) vivado -notrace -mode batch -source test_vivado_0.tcl + +test_vivado_0.v test_vivado_0.edn: test_vivado_0_synth.tcl test_vivado_0_netlist.tcl | test_vivado_0.xpr + $(EDALIZE_LAUNCHER) vivado -notrace -mode batch -source test_vivado_0_synth.tcl test_vivado_0_netlist.tcl test_vivado_0.xpr + +synth: test_vivado_0.v test_vivado_0.edn + +test_vivado_0.bit: test_vivado_0_synth.tcl test_vivado_0_run.tcl | test_vivado_0.xpr + $(EDALIZE_LAUNCHER) vivado -notrace -mode batch -source test_vivado_0_synth.tcl test_vivado_0_run.tcl test_vivado_0.xpr + +build-gui: test_vivado_0.xpr + $(EDALIZE_LAUNCHER) vivado test_vivado_0.xpr + +pgm: test_vivado_0_pgm.tcl test_vivado_0.bit + $(EDALIZE_LAUNCHER) vivado -quiet -nolog -notrace -mode batch -source test_vivado_0_pgm.tcl -tclargs xc7a35tcsg324-1 test_vivado_0.bit + +post_build: test_vivado_0.bit + +pre_run: + +run: pre_run + +post_run: run diff --git a/tests/test_vivado/edif_netlist/test_vivado_0.tcl b/tests/test_vivado/edif_netlist/test_vivado_0.tcl new file mode 100644 index 000000000..7ca306333 --- /dev/null +++ b/tests/test_vivado/edif_netlist/test_vivado_0.tcl @@ -0,0 +1,19 @@ +# Auto-generated project tcl file + + +create_project test_vivado_0 -force + +set_property part xc7a35tcsg324-1 [current_project] + + +set_property generic {vlogparam_bool=1 vlogparam_int=42 vlogparam_str=hello } [get_filesets sources_1] +set_property generic {generic_bool=true generic_int=42 generic_str=hello } [get_filesets sources_1] +set_property verilog_define {vlogdefine_bool=1 vlogdefine_int=42 vlogdefine_str=hello } [get_filesets sources_1] +read_edif {netlist.edif} + +set_property include_dirs [list .] [get_filesets sources_1] +set_property top top_module [current_fileset] +set_property source_mgmt_mode None [current_project] + + +link_design -top test_vivado_0 -part xc7a35tcsg324-1 \ No newline at end of file diff --git a/tests/test_vivado/edif_netlist/test_vivado_0_pgm.tcl b/tests/test_vivado/edif_netlist/test_vivado_0_pgm.tcl new file mode 100644 index 000000000..1cdbd7696 --- /dev/null +++ b/tests/test_vivado/edif_netlist/test_vivado_0_pgm.tcl @@ -0,0 +1,105 @@ +# Auto-generated program tcl file + +set part [lindex $argv 0] +set bitstream [lindex $argv 1] + + +if {[info exists env(HW_TARGET)]} { + set explicit_hw_target $env(HW_TARGET) +} else { + set explicit_hw_target "" +} +if {[info exists env(JTAG_FREQ)]} { + set jtag_freq $env(JTAG_FREQ) +} else { + set jtag_freq "" +} + +puts "FuseSoC Xilinx FPGA Programming Tool" +puts "====================================" +puts "" +puts "INFO: Programming part $part with bitstream $bitstream" +if { $explicit_hw_target != "" } { + puts "INFO: Programming target $explicit_hw_target" +} + +# Connect to Xilinx Hardware Server +if { [ catch { open_hw_manager } ] } { open_hw } +connect_hw_server + +if { $explicit_hw_target == "" } { + set hw_targets [get_hw_targets] +} else { + set hw_targets [get_hw_targets $explicit_hw_target] +} + +if { [llength $hw_targets] == 0 } { + if { $explicit_hw_target == "" } { + puts "ERROR: Failed to find any targets" + } else { + puts "ERROR: Failed to find target: $target" + } +} + +# Find the first target and device that contains a FPGA $part. +set hw_device_found 0 +foreach hw_target $hw_targets { + puts "INFO: Trying to use hardware target $hw_target" + current_hw_target $hw_target + + # Open hardware target + # The Vivado hardware server isn't always able to reliably open a target. + # Try three times before giving up. + set hw_target_opened 0 + for {set open_hw_target_try 1} {$open_hw_target_try <= 3} {incr open_hw_target_try} { + if {[catch {open_hw_target} res_open_hw_target] == 0} { + set hw_target_opened 1 + break + } + } + if { $hw_target_opened == 0 } { + puts "WARNING: Unable to open hardware target $hw_target after " \ + "$open_hw_target_try tries. Skipping." + continue + } + puts "INFO: Opened hardware target $hw_target on try $open_hw_target_try." + + # Iterate through all devices and find one which contains $part + foreach { hw_device } [get_hw_devices] { + if { [string first [get_property PART $hw_device] $part] == 0 } { + puts "INFO: Found $part as part of $hw_device." + current_hw_device $hw_device + set hw_device_found 1 + break + } + } + + if { $hw_device_found == 1 } { + break + } else { + # Close currently tried device, and try with next one. + puts "INFO: Part not found as part of $hw_target. Trying next device." + close_hw_target + } +} +if { $hw_device_found == 0 } { + puts "ERROR: None of the hardware targets included a $part FPGA part. \ + Check cables and ensure that jumpers are correct for JTAG programming." + exit 1 +} +puts "INFO: Programming bitstream to device $hw_device on target $hw_target." + +# Do the programming +current_hw_device $hw_device +set_property PROGRAM.FILE $bitstream [current_hw_device] +if {$jtag_freq != ""} { + set_property PARAM.FREQUENCY $jtag_freq [get_hw_targets $hw_target] +} +program_hw_devices [current_hw_device] + +# Disconnect from Xilinx Hardware Server +close_hw_target +disconnect_hw_server + +puts "" +puts "INFO: SUCCESS! FPGA $part successfully programmed with bitstream $bitstream." diff --git a/tests/test_vivado/edif_netlist/test_vivado_0_run.tcl b/tests/test_vivado/edif_netlist/test_vivado_0_run.tcl new file mode 100644 index 000000000..a1d38e707 --- /dev/null +++ b/tests/test_vivado/edif_netlist/test_vivado_0_run.tcl @@ -0,0 +1,33 @@ +# Create a bin file which can be used to program the flash on the FPGA +set_property STEPS.WRITE_BITSTREAM.ARGS.BIN_FILE true [get_runs impl_1] + +# Vivado will raise an error if impl_1 is launched when it is already done. So +# check the progress first and only launch if its not complete. +if { [get_property PROGRESS [get_runs impl_1]] != "100%"} { + # Vivado only outputs to stdout for jobs that are explicitly waited on with + # 'wait_on_run'. So launch and wait on synth then launch and wait on impl to + # get logging to stdout from both. + + launch_runs impl_1 -to_step write_bitstream + wait_on_run impl_1 + puts "Bitstream generation completed" +} else { + puts "Bitstream generation already complete" +} + +if { [get_property PROGRESS [get_runs impl_1]] != "100%"} { + puts "ERROR: Implementation and bitstream generation step failed." + exit 1 +} + +# By default, Vivado writes the bitstream to a file named after the toplevel and +# put into the *.runs/impl_1 folder. +# fusesoc/edalize historically used a bitstream name based on the project name, +# and puts it into the top-level project workroot. +# To keep backwards-compat, copy the Vivado default bitstream file to the +# traditional edalize location. +# The Vivado default name is beneficial when using the GUI, as it is set as +# default bitstream in the "Program Device" dialog; non-standard names need to +# be selected from a file picker first. +set vivadoDefaultBitstreamFile [ get_property DIRECTORY [current_run] ]/[ get_property top [current_fileset] ].bit +file copy -force $vivadoDefaultBitstreamFile [pwd]/[current_project].bit diff --git a/tests/test_vivado/edif_netlist/test_vivado_0_synth.tcl b/tests/test_vivado/edif_netlist/test_vivado_0_synth.tcl new file mode 100644 index 000000000..3f417ddc0 --- /dev/null +++ b/tests/test_vivado/edif_netlist/test_vivado_0_synth.tcl @@ -0,0 +1,9 @@ +set outdated [get_property NEEDS_REFRESH [get_runs synth_1]] +set progress [get_property PROGRESS [get_runs synth_1]] + +if {$outdated || $progress != "100%"} { + reset_runs synth_1 + launch_runs synth_1 + wait_on_run synth_1 +} +#exit [regexp -nocase -- {synth_design (error|failed)} [get_property STATUS [get_runs synth_1]] match] diff --git a/tests/test_vivado/edif_netlist/vivado.cmd b/tests/test_vivado/edif_netlist/vivado.cmd new file mode 100644 index 000000000..3a0fd5470 --- /dev/null +++ b/tests/test_vivado/edif_netlist/vivado.cmd @@ -0,0 +1,2 @@ +-notrace -mode batch -source test_vivado_0.tcl +-notrace -mode batch -source test_vivado_0_synth.tcl test_vivado_0_run.tcl test_vivado_0.xpr diff --git a/tests/test_vivado/edif_netlist_no_link_design/Makefile b/tests/test_vivado/edif_netlist_no_link_design/Makefile new file mode 100644 index 000000000..d3085f502 --- /dev/null +++ b/tests/test_vivado/edif_netlist_no_link_design/Makefile @@ -0,0 +1,30 @@ +#Auto generated by Edalize + +all: post_build + +pre_build: + +test_vivado_0.xpr: test_vivado_0.tcl netlist.edif netlist.edif | pre_build + $(EDALIZE_LAUNCHER) vivado -notrace -mode batch -source test_vivado_0.tcl + +test_vivado_0.v test_vivado_0.edn: test_vivado_0_synth.tcl test_vivado_0_netlist.tcl | test_vivado_0.xpr + $(EDALIZE_LAUNCHER) vivado -notrace -mode batch -source test_vivado_0_synth.tcl test_vivado_0_netlist.tcl test_vivado_0.xpr + +synth: test_vivado_0.v test_vivado_0.edn + +test_vivado_0.bit: test_vivado_0_synth.tcl test_vivado_0_run.tcl | test_vivado_0.xpr + $(EDALIZE_LAUNCHER) vivado -notrace -mode batch -source test_vivado_0_synth.tcl test_vivado_0_run.tcl test_vivado_0.xpr + +build-gui: test_vivado_0.xpr + $(EDALIZE_LAUNCHER) vivado test_vivado_0.xpr + +pgm: test_vivado_0_pgm.tcl test_vivado_0.bit + $(EDALIZE_LAUNCHER) vivado -quiet -nolog -notrace -mode batch -source test_vivado_0_pgm.tcl -tclargs xc7a35tcsg324-1 test_vivado_0.bit + +post_build: test_vivado_0.bit + +pre_run: + +run: pre_run + +post_run: run diff --git a/tests/test_vivado/edif_netlist_no_link_design/test_vivado_0.tcl b/tests/test_vivado/edif_netlist_no_link_design/test_vivado_0.tcl new file mode 100644 index 000000000..5e06cac73 --- /dev/null +++ b/tests/test_vivado/edif_netlist_no_link_design/test_vivado_0.tcl @@ -0,0 +1,18 @@ +# Auto-generated project tcl file + + +create_project test_vivado_0 -force + +set_property part xc7a35tcsg324-1 [current_project] + + +set_property generic {vlogparam_bool=1 vlogparam_int=42 vlogparam_str=hello } [get_filesets sources_1] +set_property generic {generic_bool=true generic_int=42 generic_str=hello } [get_filesets sources_1] +set_property verilog_define {vlogdefine_bool=1 vlogdefine_int=42 vlogdefine_str=hello } [get_filesets sources_1] +read_edif {netlist.edif} + +set_property include_dirs [list .] [get_filesets sources_1] +set_property top top_module [current_fileset] +set_property source_mgmt_mode None [current_project] + + diff --git a/tests/test_vivado/edif_netlist_no_link_design/test_vivado_0_pgm.tcl b/tests/test_vivado/edif_netlist_no_link_design/test_vivado_0_pgm.tcl new file mode 100644 index 000000000..1cdbd7696 --- /dev/null +++ b/tests/test_vivado/edif_netlist_no_link_design/test_vivado_0_pgm.tcl @@ -0,0 +1,105 @@ +# Auto-generated program tcl file + +set part [lindex $argv 0] +set bitstream [lindex $argv 1] + + +if {[info exists env(HW_TARGET)]} { + set explicit_hw_target $env(HW_TARGET) +} else { + set explicit_hw_target "" +} +if {[info exists env(JTAG_FREQ)]} { + set jtag_freq $env(JTAG_FREQ) +} else { + set jtag_freq "" +} + +puts "FuseSoC Xilinx FPGA Programming Tool" +puts "====================================" +puts "" +puts "INFO: Programming part $part with bitstream $bitstream" +if { $explicit_hw_target != "" } { + puts "INFO: Programming target $explicit_hw_target" +} + +# Connect to Xilinx Hardware Server +if { [ catch { open_hw_manager } ] } { open_hw } +connect_hw_server + +if { $explicit_hw_target == "" } { + set hw_targets [get_hw_targets] +} else { + set hw_targets [get_hw_targets $explicit_hw_target] +} + +if { [llength $hw_targets] == 0 } { + if { $explicit_hw_target == "" } { + puts "ERROR: Failed to find any targets" + } else { + puts "ERROR: Failed to find target: $target" + } +} + +# Find the first target and device that contains a FPGA $part. +set hw_device_found 0 +foreach hw_target $hw_targets { + puts "INFO: Trying to use hardware target $hw_target" + current_hw_target $hw_target + + # Open hardware target + # The Vivado hardware server isn't always able to reliably open a target. + # Try three times before giving up. + set hw_target_opened 0 + for {set open_hw_target_try 1} {$open_hw_target_try <= 3} {incr open_hw_target_try} { + if {[catch {open_hw_target} res_open_hw_target] == 0} { + set hw_target_opened 1 + break + } + } + if { $hw_target_opened == 0 } { + puts "WARNING: Unable to open hardware target $hw_target after " \ + "$open_hw_target_try tries. Skipping." + continue + } + puts "INFO: Opened hardware target $hw_target on try $open_hw_target_try." + + # Iterate through all devices and find one which contains $part + foreach { hw_device } [get_hw_devices] { + if { [string first [get_property PART $hw_device] $part] == 0 } { + puts "INFO: Found $part as part of $hw_device." + current_hw_device $hw_device + set hw_device_found 1 + break + } + } + + if { $hw_device_found == 1 } { + break + } else { + # Close currently tried device, and try with next one. + puts "INFO: Part not found as part of $hw_target. Trying next device." + close_hw_target + } +} +if { $hw_device_found == 0 } { + puts "ERROR: None of the hardware targets included a $part FPGA part. \ + Check cables and ensure that jumpers are correct for JTAG programming." + exit 1 +} +puts "INFO: Programming bitstream to device $hw_device on target $hw_target." + +# Do the programming +current_hw_device $hw_device +set_property PROGRAM.FILE $bitstream [current_hw_device] +if {$jtag_freq != ""} { + set_property PARAM.FREQUENCY $jtag_freq [get_hw_targets $hw_target] +} +program_hw_devices [current_hw_device] + +# Disconnect from Xilinx Hardware Server +close_hw_target +disconnect_hw_server + +puts "" +puts "INFO: SUCCESS! FPGA $part successfully programmed with bitstream $bitstream." diff --git a/tests/test_vivado/edif_netlist_no_link_design/test_vivado_0_run.tcl b/tests/test_vivado/edif_netlist_no_link_design/test_vivado_0_run.tcl new file mode 100644 index 000000000..a1d38e707 --- /dev/null +++ b/tests/test_vivado/edif_netlist_no_link_design/test_vivado_0_run.tcl @@ -0,0 +1,33 @@ +# Create a bin file which can be used to program the flash on the FPGA +set_property STEPS.WRITE_BITSTREAM.ARGS.BIN_FILE true [get_runs impl_1] + +# Vivado will raise an error if impl_1 is launched when it is already done. So +# check the progress first and only launch if its not complete. +if { [get_property PROGRESS [get_runs impl_1]] != "100%"} { + # Vivado only outputs to stdout for jobs that are explicitly waited on with + # 'wait_on_run'. So launch and wait on synth then launch and wait on impl to + # get logging to stdout from both. + + launch_runs impl_1 -to_step write_bitstream + wait_on_run impl_1 + puts "Bitstream generation completed" +} else { + puts "Bitstream generation already complete" +} + +if { [get_property PROGRESS [get_runs impl_1]] != "100%"} { + puts "ERROR: Implementation and bitstream generation step failed." + exit 1 +} + +# By default, Vivado writes the bitstream to a file named after the toplevel and +# put into the *.runs/impl_1 folder. +# fusesoc/edalize historically used a bitstream name based on the project name, +# and puts it into the top-level project workroot. +# To keep backwards-compat, copy the Vivado default bitstream file to the +# traditional edalize location. +# The Vivado default name is beneficial when using the GUI, as it is set as +# default bitstream in the "Program Device" dialog; non-standard names need to +# be selected from a file picker first. +set vivadoDefaultBitstreamFile [ get_property DIRECTORY [current_run] ]/[ get_property top [current_fileset] ].bit +file copy -force $vivadoDefaultBitstreamFile [pwd]/[current_project].bit diff --git a/tests/test_vivado/edif_netlist_no_link_design/test_vivado_0_synth.tcl b/tests/test_vivado/edif_netlist_no_link_design/test_vivado_0_synth.tcl new file mode 100644 index 000000000..3f417ddc0 --- /dev/null +++ b/tests/test_vivado/edif_netlist_no_link_design/test_vivado_0_synth.tcl @@ -0,0 +1,9 @@ +set outdated [get_property NEEDS_REFRESH [get_runs synth_1]] +set progress [get_property PROGRESS [get_runs synth_1]] + +if {$outdated || $progress != "100%"} { + reset_runs synth_1 + launch_runs synth_1 + wait_on_run synth_1 +} +#exit [regexp -nocase -- {synth_design (error|failed)} [get_property STATUS [get_runs synth_1]] match] diff --git a/tests/test_vivado/edif_netlist_no_link_design/vivado.cmd b/tests/test_vivado/edif_netlist_no_link_design/vivado.cmd new file mode 100644 index 000000000..3a0fd5470 --- /dev/null +++ b/tests/test_vivado/edif_netlist_no_link_design/vivado.cmd @@ -0,0 +1,2 @@ +-notrace -mode batch -source test_vivado_0.tcl +-notrace -mode batch -source test_vivado_0_synth.tcl test_vivado_0_run.tcl test_vivado_0.xpr