#========================================================================= # Makefile #========================================================================= # This generated Makefile chains together the asic flow steps for this # design according to the design's configuration file. For each asic flow # step, a make target is generated that contains the commands specified in # the step's configuration file. # # Extra commonly useful targets are also provided: # # - all : Execute all steps # - clean : Clean up junk # - debug-* : Bring up debug view for many targets # - graph : Draws an ASCII dependency graph of steps # - list : List all configured steps + short names + other targets # - print.* : Print any Makefile variable # - print : Print contents of all variables in "print_list" # - run-* : Run command with Makefile variables available # - runtimes : Prints runtimes for each executed step # - seed : Creates all build directories and seeds the flow steps # # Author : Christopher Torng # Date : March 26, 2018 #------------------------------------------------------------------------- # Front matter #------------------------------------------------------------------------- .PHONY: default default: all # Disable built-in rules .SUFFIXES: #------------------------------------------------------------------------- # Generated variables from configuration #------------------------------------------------------------------------- design = @design@ flow_path = @flow_path@ include setup-adk.mk include setup-design.mk include setup-flow.mk #------------------------------------------------------------------------- # Directories #------------------------------------------------------------------------- # Base directory relative to the build directory. The base directory has # the top-level steps directory, the design-flow directory, etc. export relative_base_dir := .. # Absolute base directory, which is marked as the base directory because # it has the base directory marker file in it. This small shell script # searches up the directory hierarchy until it finds the marker file. Then # it labels that directory as the absolute base directory. export base_dir_marker := .TOP export base_dir := $(shell \ x=$${PWD}; \ while [ "$$x" != "/" ]; do \ x=$$(dirname "$$x"); \ find "$$x" -maxdepth 1 -name $(base_dir_marker) | xargs -I {} dirname {}; \ done;) # Master steps directory in the base directory with all possible steps export master_steps_dir = steps # Master directory in the base directory with all assembled flows export master_flows_dir = assembled-flows # Build directories export flow_dir = flow export plugins_dir = plugins export logs_dir = logs export reports_dir = reports export results_dir = results export collect_dir = collect export handoff_dir = handoff export timestamps_dir = timestamps export githashes_dir = githashes #------------------------------------------------------------------------- # Step management #------------------------------------------------------------------------- # Set up VPATH # # The VPATH variable is a list of directories to be searched for missing # prerequisites. # # VPATH can be cleverly used to abstract the make tool's dependency # tracking into "steps" that are easier for the user to understand and # manipulate than the actual detailed Makefile dependencies. The interface # is a list of flow steps (i.e., PHONY target files) in $(VPATH). export VPATH = vpath junk += $(VPATH) # list # # Useful target that lists all configured steps and their short names. The # abbreviated versions can be defined in each step's configuration file # and should be defined as a variable called "abbr.name_of_the_step". If a # variable for "descriptions.name_of_the_step" is provided, the # description will also be listed. # # The output will look something like this: # # (Long Name) (Short Name) (Description) # ---------------------------------------------------------------- # - info Prints useful design information # - dc-synthesis synth Synthesize RTL into gates # - ... # list: @echo "--------------------------------------------------------------------------" @echo "Available steps targets" @echo "--------------------------------------------------------------------------" @echo @printf "%s %-21s %-15s %s\n" ' ' "(Long Name)" "(Short Name)" "(Description)" @echo " ------------------------------------------------------------------------" @$(foreach step, $(steps),printf "%s %-21s %-15s %s\n" '-' "$(step)" "$(abbr.$(step))" $(descriptions.$(step)) ;) @echo @echo "Other useful targets" @echo @echo "- all : Execute all steps" @echo "- clean : Clean up junk" @echo "- debug-* : Bring up debug view for many targets" @echo "- graph : Draws an ASCII dependency graph of steps" @echo "- list : List all configured steps + short names + other targets" @echo "- print.* : Print any Makefile variable" @echo "- print : Print contents of all variables in "print_list"" @echo "- run-* : Run command with Makefile variables available" @echo "- runtimes : Prints runtimes for each executed step" @echo "- seed : Creates all build directories and seeds the flow steps" @echo # runtimes # # Useful target that prints the runtimes for all steps given the # timestamps for each step (e.g., innovus-place.start, innovus-place.end). # # Notes: # # - Using "date -f" reads and prints the date in the given file # - Using "date +%s" formats into seconds since 1970-01-01 00:00:00 UTC # - The "... 2>/dev/null || echo 0" returns "0" if the file does not exist # - All bash variables need to be double escaped with $$myvariable # - This sed command can be added to blank out zero-value printouts # sed "s/ 0 hr/ /; s/ 0 min/ /" # - The total runtime is aggregated across all steps to compute the total. # This number might be different compared to subtracting the first/last # timestamps because the user might have paused between steps. # Improvements from Khalid # # * An flag is printed next to runtimes that are still executing # (i.e, believed to be still running) # * Runtimes for steps that are still executing will be running runtime # * If the end time is older than the start, we assume start is invalid # * if start time do not exist while end does, we assume step never started runtimes: @echo "--------------------------------------------------------------------------" @echo "Runtimes" @echo "--------------------------------------------------------------------------" @(tot_secs=0; \ $(foreach step, $(steps), \ start_e=$(shell [[ -e $(timestamps_dir)/$(step).start ]] && echo "y"); \ end_e=$(shell [[ -e $(timestamps_dir)/$(step).end ]] && echo "y"); \ end_1=$(shell date -f $(timestamps_dir)/$(step).end +%s 2>/dev/null || echo 0); \ start_1=$(shell date -f $(timestamps_dir)/$(step).start +%s 2>/dev/null || echo 0); \ end_2=$$(if [[ -n "$$start_e" ]]; then echo $$end_1; else echo 0; fi); \ start_2=$$start_1; \ flag=; \ flag=$$([[ "$$end_2" -lt "$$start_2" ]] && echo " <-- in progress"); \ end=$$(if [[ -n "$$flag" ]]; then date +%s; else echo $$end_2; fi); \ start=$$start_2; \ step_secs=$$(( $$end - $$start )); \ tot_secs=$$(($$tot_secs + $$step_secs)); \ h=$$(( $$step_secs / 3600 )); \ m=$$(( $$step_secs % 3600 / 60 )); \ s=$$(( $$step_secs % 60 )); \ printf '%-21s -- %2d hr %2d min %2d sec%s\n' $(step) $$h $$m $$s "$$flag"| \ sed "s/ 0 hr/ /; s/ 0 min/ /";) \ echo "--------------------------------------------------------------------------"; \ h=$$(( $$tot_secs / 3600 )); \ m=$$(( $$tot_secs % 3600 / 60 )); \ s=$$(( $$tot_secs % 60 )); \ printf '%-21s -- %2d hr %2d min %2d sec\n' "Total" $$h $$m $$s; ) # graph # # Useful target that draws an ASCII dependency graph of all steps graph: @python $(base_dir)/utils/graph.py -f setup-flow.mk junk += .graph # hidden dir used by utils/graph.py # git hashes # # Each step that runs will mark the $(githashes_dir) with the commit hash # at the time of running it. This can be useful to see which commit a # build was on or which branch was being tracked over time. # # The following command generates a short hash and the dash-separated # first line of the commit message for marking the step. githash_cmd = \ $(shell git log --oneline -1 | \ tr ' !@\#$$%^&*()[]{}/:;"' '-------------------' | \ sed "s|'|-|g" | \ cut -c 1-80) #------------------------------------------------------------------------- # Seeding the build directory #------------------------------------------------------------------------- # The "seed" target prepares the directory structure and assembles the # "steps" directory with all of the configured asic flow steps for this # design according to the configuration file. It also pulls all of the # design-specific plugins into the "plugins" directory. seed: # Generate all asic flow directories @mkdir -p $(VPATH) $(flow_dir) $(logs_dir) @mkdir -p $(reports_dir) $(results_dir) @mkdir -p $(collect_dir) $(handoff_dir) $(timestamps_dir) @mkdir -p $(githashes_dir) # Seed links to the master steps into the flow directory @$(foreach x, $(steps), \ (cd $(flow_dir) && \ cp -rf ../$(relative_base_dir)/$(master_steps_dir)/$x .);) # ln -sf ../$(relative_base_dir)/$(master_steps_dir)/$x);) # Link in the setup make fragments for reference # @ln -sf @flow_path@/setup-adk.mk # @ln -sf @flow_path@/setup-design.mk # @ln -sf @flow_path@/setup-flow.mk # Link in the plugins # @ln -sf @flow_path@/plugins @rm -rf ./$(plugins_dir) @cp -rf @flow_path@/plugins ./$(plugins_dir) # Mark this step done @touch $(VPATH)/$@ junk += $(flow_dir) $(logs_dir) junk += $(reports_dir) $(results_dir) junk += $(collect_dir) $(handoff_dir) $(timestamps_dir) junk += $(githashes_dir) #junk += setup-adk.mk setup-design.mk setup-flow.mk junk += $(plugins_dir) #------------------------------------------------------------------------- # Rules template for each step #------------------------------------------------------------------------- # This section defines the rules template used to generate the target for # each asic flow step and "chain together" dependent steps. # # The minimum requirement to implement an asic flow step is to create a # directory called (name of the step) in the master steps directory which # contains the file "configure.mk", and this file must specify the # variable "commands.(name of the step)" specifying how to run the step. # # For example, if the step name is "myflowstep", then the configure.mk can # specify: # # commands.myflowstep = echo "Running myflowstep" # # The build system uses the following template to include the # "configure.mk" for the step and then essentially just tells the step to # run itself by calling the specified command. The template also collects # handoffs from each of the dependency steps, collects timestamps for # pretty printing later, and handles misc support functions. # step_template # # - $(1): Name of the step (e.g., "dc-synthesis") # # Additional features # # - ascii.$(1) # - (optional) # - This hook can be defined in the step configure.mk to print a # pretty banner before executing the step, which is visible as text # flies by. # # - abbr.$(1) # - (optional) # - This hook can be used to create a short alias target for the step # # - extra_dependencies.$(1) # - (optional) # - The step configure can define this variable to draw any additional # custom dependencies for this step. # # - skipvpath.$(1) # - (optional) # - If defined, the build system will not use VPATH for this step define step_template # Generate variables for this step flow_dir.$(1) = $(flow_dir)/$(1) plugins_dir.$(1) = $(plugins_dir)/$(1) logs_dir.$(1) = $(logs_dir)/$(1) reports_dir.$(1) = $(reports_dir)/$(1) results_dir.$(1) = $(results_dir)/$(1) collect_dir.$(1) = $(collect_dir)/$(1) handoff_dir.$(1) = $(handoff_dir)/$(1) # Include the step makefile fragment include $$(relative_base_dir)/$$(master_steps_dir)/$(1)/configure.mk # Primary command target for this step $(1): $$(dependencies.$(1)) $$(extra_dependencies.$(1)) # Mark the git commit hash used for this step @rm -f ./$$(githashes_dir)/[$(1)]-* @touch $$(githashes_dir)/[$(1)]-$$(githash_cmd) # Collect files from handoffs of dependencies @rm -rf ./$$(collect_dir.$(1)) @mkdir -p $$(collect_dir.$(1)) @$$(foreach dep, $$(dependencies.$(1)), \ if test x$$(handoff_dir.$$(dep)) != x; then true; \ $$(foreach file, $$(wildcard $$(handoff_dir.$$(dep))/*), \ (cd $$(collect_dir.$(1)) && ln -sf ../../$$(file));) \ fi;) # Execute the step @date > $$(timestamps_dir)/$(1).start ifdef ascii.$(1) $$(ascii.$(1)) endif $$(commands.$(1)) @date > $$(timestamps_dir)/$(1).end ifndef skipvpath.$(1) @touch $(VPATH)/$(1) endif # Alias short name target for this step ifdef abbr.$(1) $$(abbr.$(1)): $(1) endif endef #------------------------------------------------------------------------- # Generate targets for all steps #------------------------------------------------------------------------- $(foreach step, $(steps), \ $(eval $(call step_template,$(step)))) .PHONY: all all: $(dependencies.all) #------------------------------------------------------------------------- # Regenerate configure information #------------------------------------------------------------------------- configure_prereq = \ $(base_dir)/configure.ac \ $(base_dir)/ctx.m4 $(base_dir)/configure : $(configure_prereq) cd $(base_dir) && autoconf config_status_prereq = \ $(base_dir)/configure config.status : $(config_status_prereq) ./config.status --recheck makefile_prereq = \ config.status \ $(base_dir)/Makefile.in Makefile : $(makefile_prereq) ./config.status #------------------------------------------------------------------------- # Misc #------------------------------------------------------------------------- # clean # # Try to prefix with "./" to avoid calling "rm -rf /" due to unresolved # variables. Also remove directories starting with "s.", which is a # namespace for useful shortcut directories that the user wants quick # access to. .PHONY: clean clean: rm -rf $(foreach x, $(junk),./$x) rm -rf ./s.* rm -rf ./summary-synthesis.txt rm -rf ./summary-place-route.txt rm -rf ./summary-power.txt rm -rf ./activity-scaled.saif ./activity.saif rm -rf ./clk-def.tcl ./parasitics_command.log ./pt_shell_command.log rm -rf ./power.rpt # print.% # # This target makes it easier to debug Makefile rules by allowing you to # print any variable. For example, "make print.junk" prints the value of # the junk variable. print.% : @echo $* = $($*) # print # # Print the values of any variables in the print_list print: @echo "--------------------------------------------------------------------------" @echo "Variables in print_list" @echo "--------------------------------------------------------------------------" @$(foreach var, $(print_list), \ printf "%30.30s -- %38.38s .\n" "$(var)" "$($(var))";) @echo # run-% # # Generic target to execute commands with environment variables set from # the Makefile. # # Note that one particularly useful target is "make run-bash", which gives # you a new shell with all of the exported environment variables from the # Makefile available to use. run-%: $* # Colors for printouts echo_green = '\033[92m' echo_nocolor = '\033[0m'