In TCL FPGA Wizards Trust
TCL, Tool Command Language, is THE swiss army knife of choice for FPGA wizards. Learn it or be left behind, but what exactly is it and how does one go about mastering it?
As is the case for all great names, the answer is within. In this case TCL is a tool to take command of tools with a language. It is a programmatic way to interact with a tool, as opposed to the point and click adventures that all too often end in frustration expressed through cursing.
But why should we go back to the 70's? Wasn't the command line all the rage in the 70's? Because every tool, of which TCL is also one, has a place and in the context of FPGA's it solves a lot of pain points, particularly when it comes to building and testing projects in a reproducible way that is also orders of magnitude faster than a frustrating fury of highly error prone mouse clicks.
Let's then take a dive head first with a simple TCL script to create a project for a Xilinx xc7a35tcpg236-1 FPGA with a setlist of source and constraint files followed by synthesis and implementation steps:
We can do this piecewise, beginning with the project name and directory structure:
# Set the reference directory for relative paths set origin_dir "." set project_name "example_project" # Set the directory path for the new project set proj_dir [file join $origin_dir $project_name] # Create project and set the part number create_project $project_name $proj_dir -part xc7a35tcpg236-1 # Set the directory path for the new project set proj_dir [get_property directory [current_project]]
Not too bad so far, right? Just a bit of syntactical sugar in the form of directives more or less.
From here we can define the design and simulation languages (VHDL/Verilog in this case):
set obj [current_project] set_property -name "default_lib" -value "xil_defaultlib" -objects $obj set_property -name "simulator_language" -value "VHDL" -objects $obj set_property -name "target_language" -value "VHDL" -objects $obj
We have chosen the default library and VHDL for the simulator and design languages. We can then bake in our design and constraint files:
# Create 'sources_1' fileset (if not found) if {[string equal [get_filesets -quiet sources_1] ""]} { create_fileset -srcset sources_1 } # Add HDL source files add_files -norecurse -fileset [get_filesets sources_1] [list \ "[file normalize "$origin_dir/src/top.vhd"]"\ "[file normalize "$origin_dir/src/ip_core.vhd"]"\ ] # Set 'sources_1' fileset properties set obj [get_filesets sources_1] # Assign the "top level" file to be top.vhd set_property -name "top" -value "top" -objects $obj # Create 'constrs_1' fileset if {[string equal [get_filesets -quiet constrs_1] ""]} { create_fileset -constrset constrs_1 } # Add constraints files add_files -fileset constrs_1 [list \ "[file normalize "$origin_dir/constrs/constraints.xdc"]"\ ] # Create 'sim_1' fileset if {[string equal [get_filesets -quiet sim_1] ""]} { create_fileset -simset sim_1 } # Add simulation files add_files -fileset sim_1 -norecurse [list \ "[file normalize "$origin_dir/sim/top_tb.vhd"]"\ "[file normalize "$origin_dir/sim/ip_core_tb.vhd"]"\ ] # Set 'sim_1' fileset properties set obj [get_filesets sim_1] set_property -name "top" -value "testbench" -objects $ob
Finally we can save our project (it would be a shame to lose all that hard work) followed by the synthesis and implementation and bitstream generation steps as well as opening our implemented design:
# Save project save_project_as $project_name $proj_dir -force puts "Project created: $project_name" # Run Synthesis puts "Running synthesis..." launch_runs synth_1 -jobs 8 wait_on_run synth_1 # Run Implementation puts "Running implementation..." launch_runs impl_1 -jobs 8 wait_on_run impl_1 # Generate Bitstream puts "Generating bitstream..." launch_runs impl_1 -to_step write_bitstream -jobs 8 wait_on_run impl_1 puts "Synthesis, implementation, and bitstream generation completed." # Open implemented design open_run impl_1
Note the -jobs in the above, this denotes the number of cores used during the step. Feel free to change this as is appropriate for your machine.
The above is a very simple example, in a similar vein we can also cook up some testbench goodness:
# Run Simulation puts "Running simulation..." launch_simulation run all # Open Wave Window puts "Opening waveform viewer..." open_wave_config # Add signals to the wave window add_wave {{/testbench/dut}} # Add more signals as needed, for example: # add_wave {{/testbench/dut/sub_module_inst}} # Rerun simulation to populate waveform run all
One can imagine the above applied to automated pipelines running on platforms such as Jenkins which are triggered by commits to a branch.
- Comments
- Write a Comment Select to add a comment
To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.
Please login (on the right) if you already have an account on this platform.
Otherwise, please use this form to register (free) an join one of the largest online community for Electrical/Embedded/DSP/FPGA/ML engineers: