#!/usr/bin/env perl
######################################################################
#
# Copyright 2003-2022 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
#
######################################################################

require 5.006_001;
use warnings;
use Getopt::Long;
use FindBin qw($RealBin $RealScript);
use IO::File;
use Pod::Usage;

use strict;
use vars qw($Debug @Opt_Verilator_Sw);

#######################################################################
#######################################################################
# main

autoflush STDOUT 1;
autoflush STDERR 1;

$Debug = 0;
my $opt_gdb;
my $opt_rr;
my $opt_gdbbt;
my $opt_quiet_exit;

# No arguments can't do anything useful.  Give help
if ($#ARGV < 0) {
    pod2usage(-exitstatus => 2, -verbose => 0);
}

# Insert debugging options up front
push @ARGV, (split ' ', $ENV{VERILATOR_TEST_FLAGS} || "");

# We sneak a look at the flags so we can do some pre-environment checks
# All flags will hit verilator...
foreach my $sw (@ARGV) {
    push @Opt_Verilator_Sw, $sw;
}

Getopt::Long::config("no_auto_abbrev", "pass_through");
if (! GetOptions(
          # Major operating modes
          "help"        => \&usage,
          "debug"       => \&debug,
          # "version!"  => \&version,   # Also passthru'ed
          # Switches
          "gdb!"        => \$opt_gdb,
          "gdbbt!"      => \$opt_gdbbt,
          "quiet-exit!" => \$opt_quiet_exit,
          "rr!"         => \$opt_rr,
          # Additional parameters
          "<>"          => sub {},      # Ignored
    )) {
    pod2usage(-exitstatus => 2, -verbose => 0);
}

if ($opt_gdbbt && !gdb_works()) {
    warn "-Info: --gdbbt ignored: gdb doesn't seem to be working\n" if $Debug;
    $opt_gdbbt = 0;
}

# Determine runtime flags and run
# Opt_Verilator_Sw is what we want verilator to see on its argc/argv.
# Starting with that, escape all special chars for the shell;
# The shell will undo the escapes and the verilator binary should
# then see exactly the contents of @Opt_Verilator_Sw.
my @quoted_sw = map { sh_escape($_) } @Opt_Verilator_Sw;
if ($opt_gdb) {
    # Generic GDB interactive
    run (aslr_off()
         . ($ENV{VERILATOR_GDB} || "gdb")
         . " " . verilator_bin()
         # Note, uncomment to set breakpoints before running:
         #  ." -ex 'break main'"

         # Note, we must use double-quotes ("run <switches>")
         # and not single ('run <switches>') below. Bash swallows
         # escapes as you would expect in a double-quoted string.
         # That's not true for a single-quoted string, where \'
         # actually terminates the string -- not what we want!
         . " -ex \"run " . join(' ', @quoted_sw) . "\""
         . " -ex 'set width 0'"
         . " -ex 'bt'");
} elsif ($opt_rr) {
    # Record with rr
    run (aslr_off()
         . "rr record " . verilator_bin()
         . " " . join(' ', @quoted_sw));
} elsif ($opt_gdbbt && $Debug) {
    # Run under GDB to get gdbbt
    run (aslr_off()
         . "gdb"
         . " " . verilator_bin()
         . " --batch --quiet --return-child-result"
         . " -ex \"run " . join(' ', @quoted_sw)."\""
         . " -ex 'set width 0'"
         . " -ex 'bt' -ex 'quit'");
} elsif ($Debug) {
    # Debug
    run(aslr_off() . verilator_bin() . " " . join(' ', @quoted_sw));
} else {
    # Normal, non gdb
    run(verilator_bin() . " " . join(' ', @quoted_sw));
}

#----------------------------------------------------------------------

sub usage {
    pod2usage(-verbose => 2, -exitval => 0, -output => \*STDOUT);
}

sub debug {
    shift;
    my $level = shift;
    $Debug = $level || 3;
}

#######################################################################
#######################################################################
# Builds

sub verilator_bin {
    my $bin = "";
    # Use VERILATOR_ROOT if defined, else assume verilator_bin is in the search path
    my $basename = ($ENV{VERILATOR_BIN}
                    || ($Debug ? "verilator_bin_dbg" : "verilator_bin"));
    if (defined($ENV{VERILATOR_ROOT})) {
        my $dir = $ENV{VERILATOR_ROOT};
        if (-x "$dir/bin/$basename"
            || -x "$dir/bin/$basename.exe") {  # From a "make install" into VERILATOR_ROOT
            $bin = "$dir/bin/$basename";
        } else {
            $bin = "$dir/$basename";  # From pointing to kit directory
        }
    } else {
        if (-x "$RealBin/$basename"
            || -x "$RealBin/$basename.exe") {
            $bin = "$RealBin/$basename";  # From path/to/verilator with verilator_bin installed
        } else {
            $bin = $basename;  # Find in PATH
        }
        # Note we don't look under bin/$basename which would be right if running
        # in the kit dir. Running that would likely break, since
        # VERILATOR_ROOT wouldn't be set and Verilator won't find internal files.
    }
    return $bin;
}

#######################################################################
#######################################################################
# Utilities

sub gdb_works {
    $! = undef;  # Cleanup -x
    system("gdb /bin/echo"
           . " --batch-silent --quiet --return-child-result"
           . " -ex 'run -n'"  # `echo -n`
           . " -ex 'set width 0'"
           . " -ex 'bt'"
           . " -ex 'quit'");
    my $status = $?;
    return $status == 0;
}

sub aslr_off {
    my $ok = `setarch --addr-no-randomize echo ok 2>/dev/null` || "";
    if ($ok =~ /ok/) {
        return "setarch --addr-no-randomize ";
    } else {
        return "";
    }
}

sub run {
    # Run command, check errors
    my $command = shift;
    $! = undef;  # Cleanup -x
    print "\t$command\n" if $Debug >= 3;
    system($command);
    my $status = $?;
    if ($status) {
        if ($! =~ /no such file or directory/i) {
            warn "%Error: verilator: Misinstalled, or VERILATOR_ROOT might need to be in environment\n";
        }
        if ($Debug) {  # For easy rerunning
            warn "%Error: export VERILATOR_ROOT=" . ($ENV{VERILATOR_ROOT} || "") . "\n";
            warn "%Error: $command\n";
        }
        if ($status & 127) {
            if (($status & 127) == 4  # SIGILL
                || ($status & 127) == 8  # SIGFPA
                || ($status & 127) == 11) {  # SIGSEGV
                warn "%Error: Verilator internal fault, sorry. "
                    . "Suggest trying --debug --gdbbt\n" if !$Debug;
            } elsif (($status & 127) == 6) {  # SIGABRT
                warn "%Error: Verilator aborted. "
                    . "Suggest trying --debug --gdbbt\n" if !$Debug;
            } else {
                warn "%Error: Verilator threw signal $status. "
                    . "Suggest trying --debug --gdbbt\n" if !$Debug;
            }
        }
        if (!$opt_quiet_exit && ($status != 256 || $Debug)) {  # i.e. not normal exit(1)
            warn "%Error: Command Failed $command\n";
        }
        exit $! if $!;  # errno
        exit $? >> 8 if $? >> 8;  # child exit status
        exit 255;  # last resort
    }
}

sub sh_escape {
    my ($arg) = @_;

    # This is similar to quotemeta() but less aggressive.
    # There's no need to escape hyphens, periods, or forward slashes
    # for the shell as these have no special meaning to the shell.
    $arg =~ s/([^0-9a-zA-Z_\-\+\=\.\/])/\\$1/g;
    return $arg;
}

#######################################################################
#######################################################################
package main;
__END__

=pod

=head1 NAME

Verilator - Translate and simulate SystemVerilog code using C++/SystemC


=head1 SYNOPSIS

    verilator --help
    verilator --version
    verilator --cc [options] [source_files.v]... [opt_c_files.cpp/c/cc/a/o/so]
    verilator --sc [options] [source_files.v]... [opt_c_files.cpp/c/cc/a/o/so]
    verilator --lint-only -Wall [source_files.v]...


=head1 DESCRIPTION

The "Verilator" package converts all synthesizable, and many behavioral,
Verilog and SystemVerilog designs into a C++ or SystemC model that after
compiling can be executed.  Verilator is not a traditional simulator, but a
compiler.

For documentation see L<https://verilator.org/verilator_doc.html>.


=head1 ARGUMENT SUMMARY

This is a short summary of the arguments to the "verilator" executable.
See L<https://verilator.org/guide/latest/exe_verilator.html> for the
detailed descriptions of these arguments.

=for VL_SPHINX_EXTRACT "_build/gen/args_verilator.rst"

    <file.v>                    Verilog package, module and top module filenames
    <file.c/cc/cpp>             Optional C++ files to compile in
    <file.a/o/so>               Optional C++ files to link in

     +1364-1995ext+<ext>        Use Verilog 1995 with file extension <ext>
     +1364-2001ext+<ext>        Use Verilog 2001 with file extension <ext>
     +1364-2005ext+<ext>        Use Verilog 2005 with file extension <ext>
     +1800-2005ext+<ext>        Use SystemVerilog 2005 with file extension <ext>
     +1800-2009ext+<ext>        Use SystemVerilog 2009 with file extension <ext>
     +1800-2012ext+<ext>        Use SystemVerilog 2012 with file extension <ext>
     +1800-2017ext+<ext>        Use SystemVerilog 2017 with file extension <ext>
    --assert                    Enable all assertions
    --autoflush                 Flush streams after all $displays
    --bbox-sys                  Blackbox unknown $system calls
    --bbox-unsup                Blackbox unsupported language features
    --bin <filename>            Override Verilator binary
    --build                     Build model executable/library after Verilation
     -CFLAGS <flags>            C++ compiler arguments for makefile
    --cc                        Create C++ output
    --cdc                       Clock domain crossing analysis
    --clk <signal-name>         Mark specified signal as clock
    --make <build-tool>         Generate scripts for specified build tool
    --compiler <compiler-name>  Tune for specified C++ compiler
    --converge-limit <loops>    Tune convergence settle time
    --coverage                  Enable all coverage
    --coverage-line             Enable line coverage
    --coverage-max-width <width>   Maximum array depth for coverage
    --coverage-toggle           Enable toggle coverage
    --coverage-user             Enable SVL user coverage
    --coverage-underscore       Enable coverage of _signals
     -D<var>[=<value>]          Set preprocessor define
    --debug                     Enable debugging
    --debug-check               Enable debugging assertions
    --no-debug-leak             Disable leaking memory in --debug mode
    --debugi <level>            Enable debugging at a specified level
    --debugi-<srcfile> <level>  Enable debugging a source file at a level
    --default-language <lang>   Default language to parse
     +define+<var>=<value>      Set preprocessor define
    --dpi-hdr-only              Only produce the DPI header file
    --dump-defines              Show preprocessor defines with -E
    --dump-tree                 Enable dumping .tree files
    --dump-treei <level>        Enable dumping .tree files at a level
    --dump-treei-<srcfile> <level>  Enable dumping .tree file at a source file at a level
    --dump-tree-addrids         Use short identifiers instead of addresses
     -E                         Preprocess, but do not compile
    --error-limit <value>       Abort after this number of errors
    --exe                       Link to create executable
    --expand-limit <value>      Set expand optimization limit
     -F <file>                  Parse arguments from a file, relatively
     -f <file>                  Parse arguments from a file
     -FI <file>                 Force include of a file
    --flatten                   Force inlining of all modules, tasks and functions
     -G<name>=<value>           Overwrite top-level parameter
    --gdb                       Run Verilator under GDB interactively
    --gdbbt                     Run Verilator under GDB for backtrace
    --generate-key              Create random key for --protect-key
    --getenv <var>              Get environment variable with defaults
    --help                      Display this help
    --hierarchical              Enable hierarchical Verilation
     -I<dir>                    Directory to search for includes
     -j <jobs>                  Parallelism for --build
    --gate-stmts <value>        Tune gate optimizer depth
    --if-depth <value>          Tune IFDEPTH warning
     +incdir+<dir>              Directory to search for includes
    --inline-mult <value>       Tune module inlining
    --instr-count-dpi <value>   Assumed dynamic instruction count of DPI imports
     -LDFLAGS <flags>           Linker pre-object arguments for makefile
    --l2-name <value>           Verilog scope name of the top module
    --language <lang>           Default language standard to parse
    --lib-create <name>         Create a DPI library
     +libext+<ext>+[ext]...     Extensions for finding modules
    --lint-only                 Lint, but do not make output
     -MAKEFLAGS <flags>         Arguments to pass to make during --build
    --max-num-width <value>     Maximum number width (default: 64K)
    --MMD                       Create .d dependency files
    --MP                        Create phony dependency targets
    --Mdir <directory>          Name of output object directory
    --no-merge-const-pool       Disable merging of different types in const pool
    --mod-prefix <topname>      Name to prepend to lower classes
    --no-clk <signal-name>      Prevent marking specified signal as clock
    --no-decoration             Disable comments and symbol decorations
    --no-pins64                 Don't use vluint64_t's for 33-64 bit sigs
    --no-skip-identical         Disable skipping identical output
     +notimingchecks            Ignored
     -O0                        Disable optimizations
     -O3                        High performance optimizations
     -O<optimization-letter>    Selectable optimizations
     -o <executable>            Name of final executable
    --no-order-clock-delay      Disable ordering clock enable assignments
    --no-verilate               Skip verilation and just compile previously Verilated code.
    --output-split <statements>          Split .cpp files into pieces
    --output-split-cfuncs <statements>   Split model functions
    --output-split-ctrace <statements>   Split tracing functions
     -P                         Disable line numbers and blanks with -E
    --pins-bv <bits>            Specify types for top level ports
    --pins-sc-uint              Specify types for top level ports
    --pins-sc-biguint           Specify types for top level ports
    --pins-uint8                Specify types for top level ports
    --pipe-filter <command>     Filter all input through a script
    --pp-comments               Show preprocessor comments with -E
    --prefix <topname>          Name of top level class
    --prof-c                    Compile C++ code with profiling
    --prof-cfuncs               Name functions for profiling
    --prof-exec                 Enable generating execution profile for gantt chart
    --prof-pgo                  Enable generating profiling data for PGO
    --protect-key <key>         Key for symbol protection
    --protect-ids               Hash identifier names for obscurity
    --protect-lib <name>        Create a DPI protected library
    --private                   Debugging; see docs
    --public                    Debugging; see docs
    --public-flat-rw            Mark all variables, etc as public_flat_rw
     -pvalue+<name>=<value>     Overwrite toplevel parameter
    --quiet-exit                Don't print the command on failure
    --relative-includes         Resolve includes relative to current file
    --reloop-limit              Minimum iterations for forming loops
    --report-unoptflat          Extra diagnostics for UNOPTFLAT
    --rr                        Run Verilator and record with rr
    --savable                   Enable model save-restore
    --sc                        Create SystemC output
    --stats                     Create statistics file
    --stats-vars                Provide statistics on variables
     -sv                        Enable SystemVerilog parsing
     +systemverilogext+<ext>    Synonym for +1800-2017ext+<ext>
    --threads <threads>         Enable multithreading
    --threads-dpi <mode>        Enable multithreaded DPI
    --threads-max-mtasks <mtasks>  Tune maximum mtask partitioning
    --timescale <timescale>     Sets default timescale
    --timescale-override <timescale>  Overrides all timescales
    --top <topname>             Alias of --top-module
    --top-module <topname>      Name of top level input module
    --trace                     Enable waveform creation
    --trace-coverage            Enable tracing of coverage
    --trace-depth <levels>      Depth of tracing
    --trace-fst                 Enable FST waveform creation
    --trace-max-array <depth>   Maximum bit width for tracing
    --trace-max-width <width>   Maximum array depth for tracing
    --trace-params              Enable tracing of parameters
    --trace-structs             Enable tracing structure names
    --trace-threads <threads>   Enable waveform creation on separate threads
    --trace-underscore          Enable tracing of _signals
     -U<var>                    Undefine preprocessor define
    --unroll-count <loops>      Tune maximum loop iterations
    --unroll-stmts <stmts>      Tune maximum loop body size
    --unused-regexp <regexp>    Tune UNUSED lint signals
     -V                         Verbose version and config
     -v <filename>              Verilog library
     +verilog1995ext+<ext>      Synonym for +1364-1995ext+<ext>
     +verilog2001ext+<ext>      Synonym for +1364-2001ext+<ext>
    --version                   Displays program version and exits
    --vpi                       Enable VPI compiles
    --waiver-output <filename>  Create a waiver file based on the linter warnings
     -Wall                      Enable all style warnings
     -Werror-<message>          Convert warnings to errors
     -Wfuture-<message>         Disable unknown message warnings
     -Wno-<message>             Disable warning
     -Wno-context               Disable source context on warnings
     -Wno-fatal                 Disable fatal exit on warnings
     -Wno-lint                  Disable all lint warnings
     -Wno-style                 Disable all style warnings
     -Wpedantic                 Warn on compliance-test issues
    --x-assign <mode>           Assign non-initial Xs to this value
    --x-initial <mode>          Assign initial Xs to this value
    --x-initial-edge            Enable initial X->0 and X->1 edge triggers
    --xml-only                  Create XML parser output
    --xml-output                XML output filename
     -y <dir>                   Directory to search for modules

This is a short summary of the simulation runtime arguments, i.e. for the
final Verilated simulation runtime models.  See
L<https://verilator.org/guide/latest/exe_verilator.html> for the detailed
description of these arguments.

=for VL_SPHINX_EXTRACT "_build/gen/args_verilated.rst"

     +verilator+debug                  Enable debugging
     +verilator+debugi+<value>         Enable debugging at a level
     +verilator+error+limit+<value>    Set error limit
     +verilator+help                   Display help
     +verilator+noassert               Disable assert checking
     +verilator+prof+exec+file+<filename>  Set execution profile filename
     +verilator+prof+exec+start+<value>    Set execution profile starting point
     +verilator+prof+exec+window+<value>   Set execution profile duration
     +verilator+prof+vlt+file+<filename>   Set PGO profile filename
     +verilator+rand+reset+<value>     Set random reset technique
     +verilator+seed+<value>           Set random seed
     +verilator+V                      Verbose version and config
     +verilator+version                Show version and exit


=head1 DISTRIBUTION

The latest version is available from L<https://verilator.org>.

Copyright 2003-2022 by Wilson Snyder. This program is free software; you can
redistribute it and/or modify the Verilator internals under the terms of
either the GNU Lesser General Public License Version 3 or the Perl Artistic
License Version 2.0.

All Verilog and C++/SystemC code quoted within this documentation file are
released as Creative Commons Public Domain (CC0).  Many example files and
test files are likewise released under CC0 into effectively the Public
Domain as described in the files themselves.


=head1 SEE ALSO

L<verilator_coverage>, L<verilator_gantt>, L<verilator_profcfunc>, L<make>,

L<verilator --help> which is the source for this document,

and L<https://verilator.org/verilator_doc.html> for detailed documentation.

=cut

######################################################################
# Local Variables:
# fill-column: 75
# End:
