From a0265ba8a42730de751d27e6737894b91c86a69b Mon Sep 17 00:00:00 2001 From: Erik Kooistra <kooistra@astron.nl> Date: Thu, 22 May 2014 10:03:40 +0000 Subject: [PATCH] Moved or copied files to RadioHDL. --- tools/modelsim/commands.do | 562 +++++++++++++++++++++++++++++++++++++ 1 file changed, 562 insertions(+) create mode 100644 tools/modelsim/commands.do diff --git a/tools/modelsim/commands.do b/tools/modelsim/commands.do new file mode 100644 index 0000000000..0664b008bd --- /dev/null +++ b/tools/modelsim/commands.do @@ -0,0 +1,562 @@ +############################################################################### +# +# Copyright (C) 2009 +# ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################### + +# Purpose: Provide useful commands for simulating Modelsim projects +# Desription: +# +# * The user commands are typically used at the Modelsim prompt are: +# +# . lp <name> : load UniBoard <name>.mpf project +# . mk <name> : make one or range of UniBoard mpf projects +# . as # : add signals for # levels of hierarchy to the wave window +# . ds : delete all signals from the wave window +# +# * The other procedures in this commands.do are internal command procedures +# that are typically not used at the Modelsim prompt. However they can be +# used to create user commands for other projects (i.e. another arg_env then +# UNB with other arg_dir for the libraries). +# +# * The general lp and mk commands assume that the Modelsim project file is +# located at: +# +# "$env($arg_env)/Firmware/$sdir/$arg_lib/build/sim/modelsim" +# +# or for designs with an SOPC system at: +# +# "$env($arg_env)/Firmware/$sdir/$arg_lib/build/synth/quartus/*_sim/" +# +# * The recommended project directory structure is: +# +# $arg_lib/build/sim/modelsim : Modelsim project file +# /synth/quartus : Quartus project file +# /src/vhdl : VHDL source code that gets synthesized +# /tb/vhdl : VHDL source code for test bench +# + + +#------------------------------------------------------------------------------- +# UniBoard settings +#------------------------------------------------------------------------------- + +puts "Loading general UniBoard commands..." + +# UniBoard environment variable +proc unb_env {} { + return "UNB" +} + +# UniBoard project directories +proc unb_dir {} { + return {"designs" "modules" "modules/Altera" "modules/MegaWizard" "modules/Lofar" "dsp" "systems"} +} + + +#------------------------------------------------------------------------------- +# LP = Load project +#------------------------------------------------------------------------------- + +# Default load UniBoard project +proc lp {{arg_lib ""}} { + lpu $arg_lib +} + +# UniBoard load project +proc lpu {{arg_lib ""}} { + set arg_env [unb_env] + set arg_dir [unb_dir] + lp_gen $arg_env $arg_dir $arg_lib +} + +# General load project +proc lp_gen {arg_env arg_dir arg_lib} { + set mpf [eval project env] + set cur_lib [string range $mpf [expr [string last / $mpf]+1] [expr [string last . $mpf]-1]] + + if {[string equal $arg_lib ""] || [string equal $arg_lib $cur_lib]} { + # find out current module name + return $cur_lib + } elseif [file exists $arg_lib] { + set mpf $arg_lib + } else { + set sim [simdir $arg_env $arg_dir $arg_lib] + set mpf $sim/$arg_lib.mpf + } + if [file exists $mpf] then { +# if {[this_os]=="Windows"} { +# if [file attributes $mpf -readonly] then { +# file attributes $mpf -readonly 0 +# } +# } else { +# set mpf_readonly [file attributes $mpf -permissions] ;# 5 char string: 0,0,u(rwx),g(rwx),a(rwx) +# set mpf_readonly [expr !([string index $mpf_readonly 2] & 0x2)] ;# filter out user write status +# if {$mpf_readonly==1} then { +# file attributes $mpf -permissions u+w +# } +# } + if {! [string equal $cur_lib ""]} then { + project close + } + project open $mpf + } else { + error "Project file $arg_lib not found" + } + return $arg_lib +} + + +#------------------------------------------------------------------------------- +# MK = Make project +#------------------------------------------------------------------------------- + +# Default make UniBoard project +# . The args is a special TCL argument because it allows more than one formal. +# However when args is subsequently passed on to proc mku, then all arguments +# in args will be treated as a signal argument in mku. Therefore duplicate +# the content of proc mku in proc mk, because simply calling mku $args in mk +# does not work. +# . However using an alias also works and is a nicer solution: +# One can also define a command as an alias to a sequence of one or more +# words, which will be substituted for it before execution. (The funny {} +# arguments are names of the source and target interpreter, which typically +# is the current one, named by the empty string {} or ""). +interp alias {} mk {} mku + + +# UniBoard make project +proc mku args { + set arg_env [unb_env] + set arg_dir [unb_dir] + set arg_cmd [parse_for_cmds $args] + set arg_lib [parse_for_libs $args] + set arg_lib [extract_unb_libs $arg_lib] + + mk_gen $arg_env $arg_dir $arg_cmd $arg_lib +} + + +# Extract groups of UniBoard libs from arg_lib +proc extract_unb_libs arg_lib { + # Check arg_lib for make groups of UniBoard modules and designs + # Remarks: + # . order of groups is important + # . for the designs that have an SOPC system that needs to be generated first with SOPC Builder (to avoid pop up windows if these files are missing) + # . e.g. group of designs for which the node component is reused in other designs + # . e.g. group of reference designs that are still maintained + set m_unb_common {fmf easics tst common mm dp uth} + set m_unb_lofar {async_logic diag util i2c rcuh sens mdio eth ado pfs pft2 ss st} + set m_unb_dsp {bf rTwoSDF fft filter wpfb} + set m_unb_modules {diagnostics ppsh tse aduh tr_nonbonded ddr3 udp_packetizer remu epcs unb_common} + set m_unb_designs {unb_tr_nonbonded unb_ddr3 unb_tr_xaui bn_terminal_bg fn_terminal_db} + set m_old_designs {unb_sens unb_tse unb_heater fn_mdio unb_base bn_base fn_base} + set m_apertif_designs {bn_capture fn_bf bn_filterbank fn_beamformer} + + if [ string equal $arg_lib "all_mod" ] { set arg_lib "$m_unb_common $m_unb_lofar $m_unb_modules $m_unb_dsp" + } elseif [ string equal $arg_lib "unb_designs" ] { set arg_lib "$m_unb_designs" + } elseif [ string equal $arg_lib "all_unb" ] { set arg_lib "$m_unb_common $m_unb_lofar $m_unb_modules $m_unb_dsp $m_unb_designs" + } elseif [ string equal $arg_lib "all_apertif" ] { set arg_lib "$m_unb_common $m_unb_lofar $m_unb_modules $m_unb_dsp $m_unb_designs $m_apertif_designs" + } elseif [ string equal $arg_lib "old_designs" ] { set arg_lib "$m_old_designs" + } elseif [ string equal $arg_lib "all" ] { set arg_lib "$m_unb_common $m_unb_lofar $m_unb_modules $m_unb_dsp $m_unb_designs $m_apertif_designs $m_old_designs" + } + return $arg_lib +} + +# Get commands from the mk args +proc parse_for_cmds arg_list { + set cmds {} + if [ string equal $arg_list "help" ] then { + puts "mk \[commands\] \[projects\]" + puts " possible commands are:" + puts " check: check for absolute paths in project sources" + puts " clean: removes the library files" + puts " compile: runs project compileall" + puts " delete: delete Modelsim project file" + puts " files: list files in compile order" + puts " help: displays this help" + puts " make: runs makefile" + puts " test: runs test cases" + puts " vmake: creates makefile" + puts "" + puts "commands are executed for the projects indicated" + puts "- when no command is specified, 'make' is used as default" + puts "- when no projects are specified, the current project is used" + puts "- the keyword 'all_mod' is expanded to a subset of all uniboard reuseable modules" + puts "- the keyword 'all_unb' is expanded to a subset of all uniboard reuseable designs and reference designs" + puts "" + return + } else { + # search for commands in arg_list + foreach cmd {check clean compile delete files make test vmake} { + if {[lsearch $arg_list $cmd] >= 0} then { + lappend cmds $cmd + } + } + if {[llength $cmds] == 0} then { + # no commands found, use default commands + set cmds {make} + } + } + return $cmds +} + + +# Get libraries (modules, designs) from the mk args +proc parse_for_libs arg_list { + # strip the commands from arg_list to keep only the modules + set libs $arg_list + foreach cmd {check clean compile delete files make test vmake} { + set i [lsearch $libs $cmd] + if {$i >= 0} then { + set libs [lreplace $libs $i $i] + } + } + return $libs +} + + +# General make project +proc mk_gen {arg_env arg_dir arg_cmd arg_lib} { + if {[llength $arg_cmd] > 0} then { + set cur_lib [lp_gen $arg_env $arg_dir ""] + + # without arguments mk current lib + if { [llength $arg_lib] == 0 } { + set arg_lib $cur_lib + } + # perform the commands on the specified libs + foreach cmd $arg_cmd { + foreach lib $arg_lib { + if { [ catch { eval ["mk_$cmd" $arg_env $arg_dir $lib] } msg ] } { + puts stderr $msg + } + } + } + # back to original lib + lp_gen $arg_env $arg_dir $cur_lib + } +} + + +proc mk_check {arg_env arg_dir arg_lib} { + lp_gen $arg_env $arg_dir $arg_lib + puts "\[mk check $arg_lib\]" + foreach file [project compileorder] { + if {[string first "../" $file] != 0 && + [string first "\$" $file] != 0} { + puts stderr "Warning: $file is an absolute path" + } + } +} + +# Issue mk delete all to delete all Modelsim project files to ensure that SVN +# update refreshes them all from the repository. This is necessary because +# Modelsim edits the mpf files even when they have no significant modification +# and then the edited mpf files will not get SVN updated. +proc mk_delete {arg_env arg_dir arg_lib} { + puts "\[mk delete $arg_lib\]" + set sim [simdir $arg_env $arg_dir $arg_lib] + if {[file exists "$sim/$arg_lib.mpf"]} then { + file delete $sim/$arg_lib.mpf + } +} + +proc mk_clean {arg_env arg_dir arg_lib} { + puts "\[mk clean $arg_lib\]" + set sim [simdir $arg_env $arg_dir $arg_lib] + if {[file exists "$sim/work"]} then { + vdel -lib $sim/work -all + } + if {[file exists "$sim/makefile"]} then { + file delete $sim/makefile + } + if {[file exists "$sim/vsim.wlf"]} then { + file delete $sim/vsim.wlf + } + if {[file exists "$sim/$arg_lib.cr.mti"]} then { + file delete $sim/$arg_lib.cr.mti + } +} + +proc mk_compile {arg_env arg_dir arg_lib} { + if {[string compare [env] "<No Context>"] != 0} { + puts "A project cannot be closed while a simulation is in progress.\nUse the \"quit -sim\" command to unload the design first." + return + } + puts "\[mk compile $arg_lib\]" + lp_gen $arg_env $arg_dir $arg_lib + if {![file exists work"]} then { + vlib work; + } + project compileall +} + +proc mk_files {arg_env arg_dir arg_lib} { + lp_gen $arg_env $arg_dir $arg_lib + foreach file [project compileorder] { + puts $file + } +} + +proc mk_vmake {arg_env arg_dir arg_lib} { + set sim [simdir $arg_env $arg_dir $arg_lib] + if {![file exists "$sim/work/_info"]} then { + mk_compile $arg_env $arg_dir $arg_lib + } + puts "\[mk vmake $arg_lib\]" + if {![file exists "$sim/makefile"] || + ([file mtime "$sim/makefile"] < [file mtime "$sim/work/_info"]) } then { + # Both the specific library name $(arg_lib)_lib and the work library map to the same local work library, + # so to be compatible for both names always use work to generate the makefile + puts [exec vmake -fullsrcpath work > $sim/makefile] + adapt_makefile "$sim/makefile" + } + if {[file exists "$sim/work"]} then { + vdel -lib $sim/work -all + vlib work + } +} + +proc mk_make {arg_env arg_dir arg_lib} { + global env + set sim [simdir $arg_env $arg_dir $arg_lib] + if {! [file exists "$sim/makefile"] } then { + mk_vmake $arg_env $arg_dir $arg_lib + } + puts "\[mk make $arg_lib\]" + if {[this_os]=="Windows"} { + puts [exec $env(UNB)/Firmware/sim/bin/make.exe -C $sim -s -k -f makefile] + } else { + puts [exec /usr/bin/make -C $sim -s -k -f makefile] + } +} + +proc mk_test {arg_env arg_dir arg_lib} { + # only for directory /modules, so arg_dir is not used but kept to match the other proc mk_* + puts "\[mk test $arg_lib\]" + radix -decimal + vsim -quiet tst_lib.tb_$arg_lib + set tb [tbdir $arg_env $arg_lib] + + foreach tc [glob -directory $tb/data -type d -nocomplain tc*] { + puts "testcase $tc" + foreach fileName [glob -directory $tc -type f -nocomplain *.in *.out *.ref] { + file copy -force $fileName . + } + restart -force + run 1 ms + foreach fileName [glob -dir . -type f -nocomplain *.in *.out *.ref] { + file delete -force $fileName + } + } + quit -sim +} + + +#------------------------------------------------------------------------------- +# Auxiliary procedures +#------------------------------------------------------------------------------- + +# compute simulation directory +proc simdir {arg_env arg_dir arg_lib {return_code -1}} { + global env + # The order of arg_dir is important when modules with the same name exist, + # the first one where the mpf is found will be used + foreach sdir $arg_dir { + # First look in the project default Modelsim project directory + if {[file exists "$env($arg_env)/Firmware/$sdir/$arg_lib/build/sim/modelsim"]} { + return $env($arg_env)/Firmware/$sdir/$arg_lib/build/sim/modelsim + } else { + # then also support the <sopc design name>_sim directory generated by SOPC Builder + # note that for this path to be found the modelsim/ directory in sim/ must be deleted + if {[catch {glob -directory $env($arg_env)/Firmware/$sdir/$arg_lib/build/synth/quartus *_sim/}] == 0} { + # If there exists more then one dir ending at '_sim' then return only the first + return [lindex [glob -directory $env($arg_env)/Firmware/$sdir/$arg_lib/build/synth/quartus *_sim/] 0] + } + } + } + if {$return_code==-1} { + # Default raise error to abort script + error "Project directory $arg_lib not found" + } else { + # Optionally return with return_code to continue script + return $return_code + } +} + +# compute tb directory +proc tbdir {arg_env arg_lib} { + global env + return $arg_env/Firmware/modules/$arg_lib/tb +} + +# find out which environment operating system we are on +proc this_os {} { + if {$::tcl_platform(platform)=="windows"} { + return "Windows" + } else { + return "Not Windows" ;# Linux, Unix, ... + } +} + +# adapt makefile to allow (arch_name) in filenames by changing them into \\(arch_name\\) +proc adapt_makefile arg { + if {[this_os]=="Windows"} { + # Nothing to do, works OK + } else { + set arch_names {"pkg" "rtl" "str" "wrap" "recursive" "beh" "empty" "stratix4"} + set fh [open $arg r] + set txt [read $fh] + close $fh + foreach an $arch_names { + set ai [string first "($an)" $txt] + while {$ai != -1} { + set txt [string replace $txt $ai [expr $ai + [string length $an] + 1] "\\($an\\)"] + incr ai 2 + set ai [string first "($an)" $txt $ai] + } + } + set fh [open $arg w] + puts $fh $txt + close $fh + } +} + + + +#------------------------------------------------------------------------------- +# DS = Delete Signals : deletes all signals in the waveform window. +#------------------------------------------------------------------------------- +proc ds {} { + delete wave * +} + +#------------------------------------------------------------------------------- +# AS = Add signals : adds all signals up to hierarchy depth to the wave window +#------------------------------------------------------------------------------- +proc as {depth {inst ""}} { + #asf $depth + asg $depth $inst +} + +#------------------------------------------------------------------------------- +# ASF = add signals flat : flat adds all signals up to hierarchy depth to the wave window +# It will automatically add dividers between the blocks, and it will discard all +# nxt_ and i_ signals. Altera alt_ blocks will also be ignored. +#------------------------------------------------------------------------------- +proc asf depth { + global env + # Start with all signals in the model. + add wave -noupdate -divider {as} + add wave -noupdate -depth $depth -r "/*" + # Allow users to set environment variable if they don't want the signals to be deleted + if { ![info exists ::env(MODELSIM_WAVE_NO_DEL) ] } { + delete wave */nxt_* + delete wave */i_* + } + #delete wave */alt* + configure wave -signalnamewidth 0 + echo "Done." +} + +#------------------------------------------------------------------------------- +# ASG = add signals in groups : recursively scans the hierarchy and adds signals +# groupwise to the wave window. +# Normal use: +# . asg [depth] +# => Adds all signals down to a depth of [depth]. +# Advanced/debugging use: +# . asg [depth] [instance_name] +# => Adds all signals in [instance_name] down to to a level of [depth] +# NOTE: instance_name = NOT the entity name! +#------------------------------------------------------------------------------- +proc asg {depth {inst ""}} { + add_wave_grouped_recursive "" "" $depth $inst 0 + wave refresh + # The grouping already displays the hierarchy, so use short signal names. + config wave -signalnamewidth 1 + # With our short signal names, the name column can be narrower than default. + config wave -namecolwidth 300 +} + +# called by ASG: +proc add_wave_grouped_recursive {current_level prev_group_option depth target_inst target_inst_det} { + # Find all instances (=next hierarchy levels) in the ecurrent hierarchy level + set found_instances [find instances "$current_level/*"] + + # Find all blocks (=GENERATE statement labels that are also hierarchy levels to be explored) + set found_blocks [find blocks "$current_level/*"] + + # Concatenate the instance list with the block list, sort them alphabetically + set objects [lsort -dictionary [concat $found_instances $found_blocks]] + + foreach object $objects { + # Separate "/object_path" from "(entity_name)" + set object_path [lindex [split $object " "] 0] + # Get the word after last "/" + set gname [lrange [split $object_path "/"] end end] + + if {[path_depth $object_path]<$depth} { + if { $gname == $target_inst || $target_inst_det==1} { + # Found an instance that matches user input - or we're already inside that instance. + add_wave_grouped_recursive "$object_path" "$prev_group_option -group $gname" $depth $target_inst 1 + } else { + add_wave_grouped_recursive "$object_path" "$prev_group_option -group $gname" $depth $target_inst 0 + } + } + } + + if { $current_level != "" } { + # First check if what we're about to add is an instance, not merely a GENERATE level + if {[context isInst $current_level]==1} { + set CMD "add wave -noupdate -radix unsigned $prev_group_option $current_level/*" + + if {$target_inst!=""} { + # User passed a target inst. Check if we inside of it. + if {$target_inst_det==0} { + # We're not in in instance. Only add a group and move on. + set CMD "add wave -noupdate -radix unsigned $prev_group_option" + } + } + # Use catch so e.g. empty entities don't cause script to fail + catch {eval $CMD} + } + return + } +} + +# Count the number of occurences in a string: +proc scount {subs string} { + regsub -all $subs $string $subs string +} + +# Return the depth of a given path; e.g. /some/path/to/some/thing = 5. +proc path_depth path { + scount "/" $path +} + + +#------------------------------------------------------------------------------- +# NOWARN default disables the library warnings for subsequent simulation runs. +# Use argument 0 to enable the warnings again. +#------------------------------------------------------------------------------- +proc nowarn {{off 1}} { + set ::StdArithNoWarnings $off + set ::NumericStdNoWarnings $off +} -- GitLab