Build System
This build system has been developed for easier implementation and simulation of the large projects and individual components as well. The main idea is based on the uniform definition of components hierarchy, which is used for sythesis and simulation purposes. Translation system is mainly implemented using Makefile and Tcl scripts. The Tcl language is independent of target operation system and it is supported in the most of tools dedicated for hardware developement.
Hierarchy description in Modules.tcl
The objective of hierarchy description is to define a structure of complex projects. Generally, a project is composed of several components and a component is recursively composed of several subcomponents and modules. Further, each subcomponent can occure in several instances and each subcomponent instance can use a different architecture. The Modules.tcl file describes all direct necessary dependencies (e.g. instantiated subcomponents or packages) for the component and the source file of the component itself. Sometimes it may also be appropriate to include more independent components into one Modules.tcl file as one bundle. In such cases, it is recomended to include only source files from the same directory as the Modules.tcl file, exceptionally from direct subdirectories. file.
For the definition of a component structure two variables are reserved - MOD
and
COMPONENTS
. The MOD
variable specifies the list of modules (typically
VHDL files in current directory) while the COMPONENTS
variable specifies
the subcomponent list of the component.
Variables in Modules.tcl obtained by the build system
PACKAGES defines the list of VHDL files, which serve as packages. These packages are usually used at the begining of VHDL files using command “use library.package_name.function”. Via this build system, packages are translated sooner than other VHDL modules and components. During translation, the order of files defined in variable is preserved. Common packages used by multiple sources are usually included in Modules.tcl for top-level, not in component’s Modules.tcl.
MOD variable defines the list of modules (VHDL or Verilog source files), which specify the component structure. It’s important to preserve the order of modules declared in this list. Module used by another module in current Modules.tcl scope has to be defined sooner in the list.
COMPONENTS defines the list of subcomponents, needed for the component. Each item of the list is also the list with following parameters:
[list ENTITY ENTITY_BASE ARCHGRP]
ENTITY specifies the entity name. It’s used by the Translation System to distinguish between several entities in the same directory, although it is not commonly used.
ENTITY_BASE defines the path to the subcomponent. It’s strongly recommended to specify this path relatively to another directory (mostly to the root folder of currently used git repository), the current
ENTITY_BASE
should be used for this purpose. Target path must contain the Modules.tcl file, which will be parsed.ARCHGRP specifies the architecture (or more architectures) of a subcomponent. For example, architecture can be an empty implementation (string
EMPTY
) or a full implementation (stringFULL
), but the value can also be a list of specific configurations to its subcomponents.
SV_LIB List of dynamic libraries used for SystemVerilog DPI verification. If the dynamic library file doesn’t exist, the build system requires existing Makefile in the same directory and executes make.
Note
Do not include prefix (lib) nor suffix (.dll or .so) in the filename.
Variables ENTITY
, ENTITY_BASE
and ARCHGRP
are predefined (provided) by the build system to be used in every Modules.tcl file. Their values are obtained from the respective COMPONENT
list item of the ancestor’s Modules.tcl.
Note
Prefer to use lappend MOD "myfile.vhd"
instead of set MOD "$MOD myfile.vhd"
,
because the lappend
better express the operation and is faster.
List of properties used in MOD variables
For translation, it is often required to specify more of the details about items (files)
present in MOD
and PACKAGES
variables. In this case the translation
system can get a list with property name and value pairs instead of a single item, e.g.:
lappend MOD [list $ENTITY_BASE/myfile.vhd LIBRARY another_lib SIM_MODULE glbl]
LIBRARY specifies another library name than default work into which the module will be compiled.
TYPE overrides automatically selected file type which is otherwise based on the file extension. Currently supported types are:
CONSTR_QUARTUS
CONSTR_VIVADO
VIVADO_IP_XACT - automatically used for xci files
SCOPED_TO_REF - only for the CONSTR_VIVADO type, calls
set_property SCOPED_TO_REF
for the filePROCESSING_ORDER - only for the CONSTR_VIVADO type, calls
set_property PROCESSING_ORDER
for the fileUSED_IN - only for the CONSTR_VIVADO type, calls
set_property USED_IN
for the fileVIVADO_SET_PROPERTY calls
set_property {*}$value
for the fileSIM_MODULE - the file uses another module for simulation, which must be simulated together like this:
vsim extra_module testbench
SIM_LIB - the file uses a simulation library which must be loaded like this:
vsim -L extra_library testbench
Example of using properties
lappend MOD [list $ENTITY_BASE/dp_bmem_behav.vhd VIVADO_SET_PROPERTY [list -quiet FILE_TYPE {VHDL}]] ;# set the VHDL98 standard for this file
lappend MOD [list "$ENTITY_BASE/bus_handshake.xdc" TYPE "CONSTR_VIVADO" SCOPED_TO_REF "ASYNC_BUS_HANDSHAKE" PROCESSING_ORDER "LATE"]
List of properties used in SV_LIBS
MAKE_PARAMS - value will be passed to
make
command as the parameters
Example of using Modules.tcl variables
# HFE top level entity
if {$ENTITY == "HFE_TOP"} {
if {$ARCHGRP == "FULL"} {
# This architecture relies on HFE component, which is located
# in the same directory as current entity and shares this Modules.tcl file.
lappend COMPONENTS [list HFE $ENTITY_BASE FULL]
# This file will be compiled to library work
lappend MOD $ENTITY_BASE/file_to_work.vhd
# This file will be compiled to library anotherlib
lappend MOD [list $ENTITY_BASE/file_to_anotherlib.vhd LIBRARY anotherlib]
}
if {$ARCHGRP == "EMPTY"} {
lappend MOD $ENTITY_BASE/hfe_empty.vhd
}
}
# HFE core entity
if {$ENTITY == "HFE"} {
if {$ARCHGRP == "FULL"} {
lappend MOD "$ENTITY_BASE/hfe_pipe.vhd"
lappend MOD "$ENTITY_BASE/hfe_parser.vhd"
lappend MOD "$ENTITY_BASE/hfe_full.vhd"
} elseif {$ARCHGRP == "EMPTY"} {
lappend MOD "$ENTITY_BASE/hfe_empty.vhd"
}
}
Component synthesis
Synthesis of the component is typically handled by a simple user-created Makefile
.
It can be located anywhere, but the recommendation is to use the synth
subdirectory of the synthesized component.
The Makefile
sets the TOP_LEVEL_ENT
variable and calls the comp
target
from global Makefile
located in $OFM_PATH/build/Makefile
, which must be included.
After calling make
the synthesis will be performed.
Advanced synthesis configuration
User can specify those variables in Makefile
:
TOP_LEVEL_ENT - required. Name of the synthesized entity.
TOP_LEVEL_PATH - optional, default value is “..”. Path to the Modules.tcl with the synthesized entity.
TOP_LEVEL_ARCHGRP - optional, default value is “FULL”.
CLK_PORTS - optional, default value is CLK. Name or list of space-separated names of component ports which serves as clock input.
CLK_PERIOD - optional, default value is 5.0. One or more space-separated integer or float values. Clock constraints will be generated with this value in ns. If there are more CLK ports than period values, unspecified periods will be calculated with a simply formula (add 1.0 for each next clock).
SYNTH - optional, default value is “vivado”. Synthesis tool can be {vivado, quartus}. For lazy users, there is a
vivado
/quartus
target in globalMakefile
, which sets this variable and callsmake
recursively with the default target.
Example of Makefile for component synthesis
TOP_LEVEL_ENT=RX_MAC_LITE
TOP_LEVEL_PATH=../../mac/rx
SYNTH=quartus
CLK_PORTS=RX_CLK TX_CLK MI_CLK
CLK_PERIOD=3.500 2.500 5.000
.PHONY: all
all: comp
include ../../../../../build/Makefile
The comp
target in Makefile
The make comp
runs the comp_$(SYNTH).tcl
script located in $OFM_PATH/build/targets/
with the synthesis tool.
Script sets some default values for mandatory variables and fetches environment variables listed above.
The script also tries to source Vivado.inc.tcl
/ Quartust.inc.tcl
file (if it exists) in a current directory.
This can be useful for overriding some variables, e.g. SYNTH_FLAGS
or CONSTR_TEXT
.
User should override the CONSTR_TEXT
variable in this file for example when the TOP_LEVEL_ENT
has very specific clock/constraints requirements.
The constraint file for current synthesis tool is generated from the CONSTR_TEXT
variable at the end of the preparation.
The file is overwritten only when needs to be updated, otherwise is leaved untouched, which is useful for typical make
run: If all sources are unchanged from the last build,
the targed file (synthesised project) is up-to-date and doesn’t need to rebuild.
Finally, the script calls default Tcl target (proc target_default
) which then passes to SynthesizeProject
procedure documented below.
Chip design synthesis and implementation
It is a good practice to split common functionality from application specific functionality:
top-level entity of card together with main constraints and build scripts,
application entity for end user with minimum build scripts.
In this scheme, the process basically starts at the user Vivado/Quartus.tcl file (the default value of SYNTHFILES
variable in Makefile)
where the user includes a common build script from a top-level entity.
This fills the HIERARCHY
array with varables COMPONENTS
and MOD
and sets up other neccessary values in SYNTH_FLAGS
array.
After Tcl interpreter goes back from common build script, the user tcl should add architecture (implementation) of application entity into the appropriate variables of HIERARCHY
array, either MOD
or COMPONENTS
.
User tcl can tune some values of SYNTH_FLAGS as well.
Final step in user tcl file is to call the nb_main
procedure, which passes to SynthesizeProject procedure within target_default
similarly as in the comp target.
SynthesizeProject
1. Init phase (SetupDesign)
This creates a project within synthesis tool, sets the FPGA device type and does the necessary project setup before adding any source files.
2. File add phase (AddInputFiles)
In this stage, files and components are processed from HIERARCHY array and passed to procedure EvalFile. EvalFile is called for each entry in PACKAGES/MOD variables and should instruct the synthesis tool to compile source file including fine-tunnig of additional properties based on extra file properties
3. Synthesis and Implemenation (SynthetizeDesign, ImplementDesign)
Procedures configure rest of parameters of the project and run the main process: the synthesis and the implementation.
4. Final phase (SaveDesign)
In this step, the binary programming file is generated.
Other features of the build system
EvalFile
EvalFile procedure is specific for each synthesis tool and is being used as callback when the common code goes through hierarchy of modules. Procedure usually adds source files into the project and sets additional properties based on extra file properties.
Batch feature in EvalFile
Although the EvalFile procedure receives one file for processing in each call, it can use the lazy evaluation mechanism, which processes a batch of source files in one command run. This mechanism is enabled in the simulation environment (Modelsim.inc.fdo file), where it has significantly positive impact on compilation time.
Source files which have the same compile flags (e.g. same library name or -vhdl2008 parameter) are stored into the special variable together with the flags instead of being processed (compiled) immediately. When EvalFile gets a file with a different set of flags, the files stored inside the batch variable must be compiled immediately, the variable is then emptied and the newly evaluated file is inserted into it. At the end of the AddInputFiles phase, the last batch must be compiled explicitely.
Makefile
There are few mechanisms in the global Makefile which deserve an explanation.
Some targets in the Makefile are aware of unchanged files. If none of the source files
for such target has been modified and the target already exists, it will not be remade.
This is handled by the make
itself, but the build system must supply a list of source files.
The list is generated by executing Tcl target called ‘makefile’, which goes through entire hierarchy of Modules.tcl,
gathers filenames and writes the list in form of target: prerequisites
into $PROJECT.$SYNTH.mk
file,
which is simply included in the main Makefile. This approach is not so simple and hides some caveats.
Makefile doesn’t propagate target specific variables
to global scope and it is unreliable to get prerequisites for generated Makefile.
Hence the generated Makefile is created (by shadowed target with same name) in the first make run
(only for concerned targets)
and the real main target is launched in a recursive run of make
.
Environment variables available in make
run aren’t exported to subprocess, except variables which are set using the export
keyword.
If the user needs to pass an environment variable into tclsh or synthesis tool, it’s better he uses the USER_ENV
variable.
It is a necessity to export user defined variable for targets which needs a generated makefile mentioned above.
There are also targets, which can trigger an user defined procedure in Tcl: ttarget_%
and starget_%
.
The user defines a Tcl procedure named for example target_myproc
.
Executing make ttarget_myproc
will trigger the stem target:
either bare tclsh (ttarget) or synthesis tool (starget) is started, the $SYNTHFILES
script
is sourced and if the script includes common build/[Vivado|Quartus|...].inc.tcl
script
and runs nb_main (which is recommended for best integration with build system),
the user defined procedure will be run.
This is also used for generating a source files inside the Tcl from make target. Common used files are DevTree.dts/dtb/vhd and user_const.vhd.
The (incomplete) list of SYNTH_FLAGS array items
PROJ_ONLY {false, true}: Only the project file will be created. Neither synthesis nor implementation will be run.
SYNTH_ONLY {false, true}: Only the synthesis will be run, the implementation will be skipped.
PHASE_SAVE {true, false}: Do not generate programming files and archives after implementation.
DEVICE {ULTRASCALE, VIRTEX7, STRATIX10, AGILEX}: Sets the FPGA family. In the comp target is mapped to specific FPGA.
FPGA {xcvu7p-flvb2104-2-i, 1SD280PT2F55E1VG, …}: Sets the FPGA part directly.
- SETUP_FLAGS: List of specific flags for entire project:
USE_XPM_LIBRARIES: includes XPM_CDC XPM_MEMORY XPM_FIFO in Vivado projects
For other values and their purpose see the Vivado.inc.tcl or Quartus.inc.tcl file in the build directory.