#!/usr/bin/env perl

#use warnings 'all';
#use strict;
#use FindBin;
#use Math::Round;

use verilog::gen;

my $scriptInfo = 
    "/*------------------------------------------------------------------------------\n" .
    " * This code was generated by Spiral FIR Filter Generator, www.spiral.net\n" .
    " * Copyright (c) 2006, Carnegie Mellon University\n" .
    " * All rights reserved.\n" .
    " * The code is distributed under a BSD style license\n" .
    " * (see http://www.opensource.org/licenses/bsd-license.php)\n" .
    " *------------------------------------------------------------------------------" .
    " */\n";
#    "/*------------------------------------------------------------------------------\n" .
#    " * Verilog generated by $FindBin::RealScript:\n" .
#    " *      generates FIR filter verilog for constant multiplier chains\n" .
#    " * author: jtrapass (Joseph Paul Trapasso)\n" .
#    " * based on the mcm tool by Yevgen Voronenko\n" .
#    " * under the guidance of James Hoe and Markus Pueschel\n" .
#    " *------------------------------------------------------------------------------" .
#    " */\n";

#-----------------------------------------------------------------------
# @brief Calls multBlockGen.pl on the commandline and generates the
#   multiply block
# @param fh file handle to print to, pass with \*FH
# @param option "vanilla" - base or "addChain" - optimized
# @param moduleName multiply block module name
# @param ports hash of port names: \{i_data => "in", o_data => "out"}
# @param bitwidth input bitwidth to multiplier block
# @param fixedPoint how many bits of data are below the decimal point,
# @param constants array of constants
# @return undef
#-----------------------------------------------------------------------
sub genMultiply {
    my ($fh, $option, $moduleName, $bitwidth, $fixedPoint, $constants, $debug) = @_;
    
    my $mult_cmd = "./multBlockGen.pl";
    
    my $constantCount = scalar(@$constants);

    for(my $i = 0; $i < $constantCount; $i++){
	$mult_cmd .= " " . $constants->[$i];
    }
    
    if($option eq "addChain"){
	$mult_cmd .= " -addChain";
    }
    elsif($option eq "base"){
	$mult_cmd .= " -base";
    }
    else{
	print STDERR "./firGen.pl: genMultiply doesn't know how to handle option: $option\n";
	exit(-2);
    }
    
    $mult_cmd .= " -suppressInfo" .
	" -moduleName $moduleName" .
	" -bitWidth $bitwidth" .
	" -fractionalBits $fixedPoint" .
	" -inData X " .
	" -outData Y " .
	" -debug ";
    
    if(!open(MULT_OUT, "$mult_cmd |")){
	print STDERR "Error executing multBlockGen.pl\n";
	exit(-2);
    }
    
    my $multBlockSize;

    my %outputBitwidths = ();
    
    if($option eq "addChain"){
	while(my $line = <MULT_OUT>){
	    chomp $line;

	    if($constantCount > 1){
		#assign Y[0] = w109[30:15];    //BitwidthUsed (0, 7)
		if($line =~ /assign \w+\[(\d+)\].+\/\/BitwidthUsed\((.*)\)/){
		    my $index = $1;
		    my $values = $2;
		    if($values eq "none"){
			my @info = (0,0,0);
			$outputBitwidths{$index} = \@info;
		    }else{
			if($values=~ /^(\d+), (\d+)$/){
			    my @info = (1,$1,$2);
			    $outputBitwidths{$index} = \@info;
			}else{
			    print $fh "doesn't look right: $line\n";
			    exit(-2);
			}
			
		    }
		}
		elsif($line =~ /MultiplyBlock\s+area estimate\s*=\s*([\d\.]+);/){
		    $multBlockSize = getnum($1);
		}
	    }else{
		#assign Y[0] = w109[30:15];    //BitwidthUsed (0, 7)
		if($line =~ /assign .+\/\/BitwidthUsed\(([\d, ]+)\)/){
		    my $index = 0;
		    my $values = $1;
		    if($values eq "none"){
			my @info = (0,0,0);
			$outputBitwidths{$index} = \@info;
		    }else{
			if($values=~ /^(\d+), (\d+)$/){
			    my @info = (1,$1,$2);
			    $outputBitwidths{$index} = \@info;
			}else{
			    print $fh "doesn't look right: $line\n";
			    exit(-2);
			}
			
		    }
		}
		elsif($line =~ /MultiplyBlock\s+area estimate\s*=\s*([\d\.]+);/){
		    $multBlockSize = getnum($1);
		}
	    }
	    
	    if(!$debug){
		$line =~ s/;.*\/\/.*$/;/;
	    }
	    print $fh $line . "\n";

	}
    }else{
	while(my $line = <MULT_OUT>){
	    print $fh $line;
	}
    }
    
    
    if(!close(MULT_OUT)){ #check exit code
	print STDERR "Error executing multBlockGen.pl: nonzero exit code\n";
	exit(-2);
    }
    
    print $fh "\n\n";

    return ($multBlockSize, \%outputBitwidths);
}

#-----------------------------------------------------------------------
# @brief Generates code for FIR filter
# @param fh file handle to print to, pass with \*FH
# @param ports hash of port names: \{i_data => "in", o_data => "out", clk => "clk"}
# @param bitWidth input bitwidth to multiplier block
# @param multiplyName multiply block module name
# @param fixedPoint how many bits of data are below the decimal point
# @param constantCount array of constants
# @return undef
#-----------------------------------------------------------------------
sub genFir{
    my ($fh, $ports, $bitwidth, $multiplyName, $outputBitwidths, $constantCount, $reset_edge, $debug) = @_;
    
    my $multOut = "multProducts";

    #multOut wires
    print $fh "  wire [" . ($bitwidth - 1) . ":0] $multOut";
    print $fh " [0:" . ($constantCount - 1) . "]" if($constantCount > 1);
    print $fh ";\n\n";
    
    print $fh "  $multiplyName my_$multiplyName(\n"
	. "    .X(" . $ports->{i_data} . ")";
    
    if($constantCount > 1){
	for(my $i = 0; $i < $constantCount; $i++){
	    print $fh ",\n    .Y" . ($i + 1) .
		"($multOut\[$i\])";
	}
    }
    else{
	print $fh ",\n    .Y" . 
	    "($multOut)"; 
    }
    
    print $fh "\n  );\n\n";
    
    #always block
    my $arrayName = "firStep";
    if($constantCount > 1){
	print $fh "  reg \[" . ($bitwidth-1) . ":0\] $arrayName";
	if ($constantCount > 2){
	    print $fh "[0:" . ($constantCount-2) . "]";
	}
    }
    
    print $fh ";\n\n";
    
    my $resetSense = "";
    my $addedPrefix = "";
    if(defined($reset_edge)){
	$resetSense = " or $reset_edge " . $ports->{"reset"};
	$addedPrefix = "  ";
    }

    my $areaSum = 0;
    my $prevAddVal = 0;
    my $prevAddLow = 0;
    my $prevAddHigh = 0;
    
    if($constantCount > 1){
	print $fh "  always@(posedge ". $ports->{"clk"} . $resetSense . ") begin\n";;
	if(defined($reset_edge)){
	    print $fh "    if(~" . $ports->{"reset"} . ") begin\n";
	    print $fh "      ${arrayName}";
	    print $fh "[0]" if($constantCount > 2);
	    print $fh " <= " . toHex(0, $bitwidth) . ";\n";
	    
	    for(my $i = 1; $i < $constantCount - 1; $i++){
		my $index = $i - 1;
		print $fh "      ${arrayName}\[$i\] <= ". toHex(0, $bitwidth) .";\n";
	    }
	    print $fh "    end\n";
	    print $fh "    else begin\n";
	}
    
	print $fh $addedPrefix . "    ${arrayName}";
	print $fh "[0]" if($constantCount > 2);
	print $fh " <=  $multOut";
	print $fh "[0]" if($constantCount > 1);
	print $fh ";";
	
	{
	    my $info = $outputBitwidths->{0};
	    if(defined($info)){
		my ($val, $low, $high) = @$info;
		{#if($val){
		    
		    #HACK
		    $low = 0;
		    $high = ($bitwidth - 1);
		    
		    $prevAddVal = 1;
		    $prevAddLow = $low;
		    $prevAddHigh = $high;
		    my $flopSize = flopArea($high-$low + 1, defined($reset_edge));
		    $areaSum += $flopSize;
		    print $fh " // $flopSize = flop($low, $high)" if($debug);
		}
		#else{
		#	print $fh "// 0 = flop(none)";
		#    }
	    }
	}
	print $fh "\n";
	
	for(my $i = 1; $i < $constantCount - 1; $i++){
	    my $index = $i - 1;
	    my $index2 = $i;
	    print $fh $addedPrefix . "    ${arrayName}\[$index2\] <=  ${arrayName}\[$index\] + ${multOut}\[$index2\];";
	    
	    my $info = $outputBitwidths->{$i};
	    if(defined($info)){
		my ($val, $low, $high) = @$info;
		
		#print $fh "// found: [$i] = ($val, $low, $high) ";
		
		if($val){
		    my $addLow = 0;
		    my $addHigh = 0;
		    if($prevAddVal){
			$addLow = max($low, $prevAddLow);
			$addHigh= max($high, $prevAddHigh);
			$addHigh = min($addHigh, ($bitwidth - 1));
			
			$high = min($addHigh + 1, ($bitwidth - 1));
			
			$low = min($low, $prevAddLow);
		    }
		    my $flopSize = flopArea($high-$low + 1, defined($reset_edge));
		    my $adderSize = adderArea($addHigh-$addLow + 1);
		    my $totalSize = $flopSize + $adderSize;
		    $areaSum += $totalSize;
		    
		    if($debug){
			print $fh " // $totalSize = flop($low, $high) + adder(";
			if($prevAddVal){
			    print $fh "$addLow, $addHigh)";
			}else{
			    print $fh "none)";
			}
		    }
		    
		    $prevAddVal = 1;
		    $prevAddLow = $low;
		    $prevAddHigh = $high;
		    
		}elsif($prevAddVal){
		    my $flopSize = flopArea($prevAddHigh-$prevAddLow +1, defined($reset_edge));
		    print $fh " // $flopSize = flop($prevAddLow, $prevAddHigh) + adder(none)" if($debug);
		    $areaSum += $flopSize;
		}else{
		    print $fh "// 0 = flop(none) + adder(none)" if($debug);
		}
	    }
	    print $fh "\n";
	}
	
	print $fh "    end\n" if(defined($reset_edge));
	print $fh "  end\n\n";
	
    }
    
    if($constantCount > 1){
	print $fh "  assign " . $ports->{o_data} . " = ${arrayName}";
	print $fh "[". ($constantCount - 2) . "\]" if ($constantCount > 2);
	print $fh "+ ${multOut}\[". ($constantCount - 1) ."\];";
    
	my $info = $outputBitwidths->{($constantCount - 1)};
	if(defined($info)){
	    my ($val, $low, $high) = @$info;
	    
	    #print $fh "// found: [".($constantCount - 1) ."] = ($val, $low, $high) ";
	    
	    if($val){
		my $addLow = 0;
		my $addHigh = 0;
		if($prevAddVal){
		    $addLow = max($low, $prevAddLow);
		    $addHigh= max($high, $prevAddHigh);
		    $addHigh = min($addHigh, ($bitwidth - 1));
		    
		    $high = min($addHigh + 1, ($bitwidth - 1));

		    $low = min($low, $prevAddLow);
		}
		my $adderSize = adderArea($addHigh-$addLow + 1);
		my $totalSize = $adderSize;
		$areaSum += $totalSize;
		
		if($debug){
		    print $fh " // $totalSize = adder(";
		    if($prevAddVal){
			print $fh "$addLow, $addHigh)";
		    }else{
			print $fh "none)";
		    }
		}
		
		$prevAddVal = 1;
		$prevAddLow = $low;
		$prevAddHigh = $high;
		
	    }else{
		print $fh "// 0 = adder(none)" if($debug);
	    }
	}
    }else{ #$constantCount == 1
	print $fh "  assign " . $ports->{o_data} . " = ${multOut};\n";
    }
    print $fh "\n";
    
    return $areaSum;
}

#-----------------------------------------------------------------------
# @brief generates a testBench, which provides the filter with an impulse
#  and, if all goes correct, should return a time reversed reading of the
#  filter
#-----------------------------------------------------------------------
sub genTestBench{
    my ($fh, $ports, $moduleList, $bitwidth, $filterSteps, $registerDelays, $fixedPoint, $reset_edge, $constants) = @_;
    
    print $fh "  integer testEndedGracefully;\n\n";

    print $fh "  reg [" . ($bitwidth - 1) . ":0] inData;\n".
	"  reg clk;\n";
    
    my $verify;
    
    if(!scalar(@$moduleList)){
	print STDERR "Error: Cannot write a testBench for no modules.\n";
	exit(-2);
    }
    
    foreach my $module(@$moduleList){
	print $fh
	    "  wire  [" . ($bitwidth - 1) . ":0] ${module}_out;\n" .
	    "  integer ${module}_val;\n";
    }
    
    print $fh "\n";
    
    print $fh
	"  initial begin\n";
    
    print $fh "    \$display(\"                   Time, ". join("_val, ", @$moduleList) . "_val\");\n";
    
    print $fh
	"    clk = 1;\n".
	"    forever \#1 clk =~clk;\n".
	"  end\n\n";
    
    foreach my $module(@$moduleList){
	print  $fh
	    "  ${module} my_${module}(\n".
	    "    ." . $ports->{"clk"} ."(clk),\n".
	    "    ." . $ports->{"i_data"} ."(inData),\n".
	    "    ." . $ports->{"o_data"} ."(${module}_out)\n".
	    "  );\n\n";
    }
    
    

    my $pr_str = "\$display(\"Output: \%d";
    foreach my $module(@$moduleList){
	$pr_str .= ", %d";
    }
    $pr_str .= "\", \$time";
    foreach my $module(@$moduleList){
	$pr_str .= ", ${module}_val";
    }
    $pr_str .= ");\n";
    
    print $fh "\n  //Convert unsigned values to signed integers for comparison\n";
    foreach my $module(@$moduleList){
	print  $fh
	    "  always@(${module}_out) begin\n" .
	    "    if(${module}_out >= ".(1 << ($bitwidth - 1)).")\n" .
	    "      ${module}_val = ${module}_out - ".(1 << $bitwidth).";\n" .
	    "    else\n" .
	    "      ${module}_val = ${module}_out;\n" .
	    "  end\n\n";
    }
    
    print $fh
	"  initial begin\n" . 
        "    //ignore initial x's and initial\n" .
        "    //delays through registers\n" .
	"    #". (2*($filterSteps + 3*$registerDelays)) .";\n";
    
    for(my $i = scalar(@$constants) - 1; $i >= 0; $i--){
	my $const = $constants->[$i];
	print $fh
	    "\n    @(posedge clk);\n" .
	    "      #0;\n" .
	    "      $pr_str";
	
	foreach my $module(@$moduleList){
	    print $fh
		"      if(($const+1 <= ${module}_val) && (${module}_val <= $const+1))\n" .
		"        \$display(\"ERROR: ${module}_val should be $const\");\n";
	}
    }

    print $fh
	"\n    //All outputs have been seen\n" .
	"    testEndedGracefully <= 1;\n\n";
    
    print $fh
	"\n    forever @(posedge clk) begin\n" .
	"      #0;\n";
    foreach my $module(@$moduleList){
	print $fh
	    "      if(${module}_val !== 0 )\n" .
	    "        \$display(\"ERROR: ${module}_val should be 0\");\n";
    }

    print $fh
	"    end //always@(posedge clk)\n\n".
	"  end //initial begin\n\n";

    print  $fh
	"  initial begin\n".
	"    testEndedGracefully <= 0;\n" .
	"    inData <= 0;\n".
        "    #". (2*($filterSteps + $registerDelays)) .";\n".
	"    //feed an impulse\n" .
	"    inData <=  1 << $fixedPoint;\n".
	"    #2;\n".
	"    inData <= 0;\n".
	"    #". (2*$filterSteps + 10) .";\n".
        "    if(testEndedGracefully !== 1)\n" .
        "      \$display(\"ERROR: Test ended too soon.  Not all outputs seen.\");\n" .
        "    \$stop;\n".
        "    \$finish;\n".
        "  end\n\n";
}

#-----------------------------------------------------------------------
# @brief puts it all together, reading input, etc
#-----------------------------------------------------------------------
sub main {

    #cmdline args = defaults:
    my @constants = ();
    my $bitWidth = 32;
    my $fixedPoint = 0;
    my $moduleName = "firFilter";
    my $inReg = 0;
    my $outReg = 0;
    my $nonOptimal = 0;
    my $optimal = 0;
    my $testBench = 0;
    my $testBenchName = "top";
    my $outFileName = undef;
    my $reset_edge = undef;
    my $fh;
    my $suppressInfo;
    my $filterForm = 1;
    my $debug = 0;
    my %ports = 
	("i_data" => "i_data",
	 "o_data" => "o_data",
	 "clk" => "clk");

    my $cmdLine = "./firGen.pl " . join(" ", @ARGV);
    
    #my args
	my $parseConstants = 1;

    #parse args
    for(my $i = 0; $i < scalar(@ARGV); $i++){

	if($ARGV[$i] eq ""){
	    next;
	}
	elsif($ARGV[$i] eq "-debug"){
	    $debug = 1;
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-filterForm"){
	    $i++;
	    $filterForm = $ARGV[$i];
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-moduleName"){
	    $i++;
	    $moduleName = $ARGV[$i];
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-bitWidth"){
	    $i++;
	    $bitWidth = $ARGV[$i];
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-fractionalBits"){
	    $i++;
	    $fixedPoint = $ARGV[$i];
	    $parseConstants = 0;
	    if($fixedPoint < 0){
		print STDERR "Fixed point argument must be non-negative: $ARGV[$i]\n";
		printUsage();
	    }
	}
	elsif($ARGV[$i] eq "-reset_edge"){
	    $i++;
	    $reset_edge = $ARGV[$i];
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-reset"){
	    $i++;
	    $ports{"reset"} = $ARGV[$i];
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-inData"){
	    $i++;
	    $ports{"i_data"} = $ARGV[$i];
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-inReg"){
	    $inReg = 1;
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-outData"){
	    $i++;
	    $ports{"o_data"} = $ARGV[$i];
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-outReg"){
	    $outReg = 1;
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-clk"){
	    $i++;
	    $ports{"clk"} = $ARGV[$i];
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-h" || $ARGV[$i] eq "--help"){
	    printUsage();
	}
	elsif($ARGV[$i] eq "-base"){
	    $nonOptimal = 1;
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-addChain"){
	    $optimal = 1;
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-testBench"){
	    $testBench = 1;
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-testBenchName"){
	    $i++;
	    $testBench = 1;
	    $testBenchName = $ARGV[$i];
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-outFile"){
	    $i++;
	    $outFileName = $ARGV[$i];
	    $parseConstants = 0;
	}
	elsif($ARGV[$i] eq "-suppressInfo"){
	    $suppressInfo = 1;
	    $parseConstants = 0;
	}
	elsif($parseConstants){
	    #verify we were given a number:
	    my $temp = getnum($ARGV[$i]);
	    if(defined(scalar($temp))){
		push(@constants, fix_zero($temp));
	    }
	    else{
		print STDERR "UNDEFINED ARG: " . $ARGV[$i] . "\n";
		printUsage();
	    }
	}
	else{
	    print STDERR "UNDEFINED ARG: " . $ARGV[$i] . "\n";
	    printUsage();
	}
    }

    my $optWarn = 0;

    @constants = reverse @constants;
    
    while(scalar(@constants) && ($constants[0] == 0)){
	shift(@constants);
	$optWarn = 1;
    }

    if(!scalar(@constants)){
	print STDERR "At least 1 non-zero-valued constant is required.  Please check the inputs and try again.\n";
	printUsage();
    }
    
    if(defined($ports{"reset"}) && !defined($reset_edge)){
	$reset_edge = "negedge";
    }
    elsif(defined($reset_edge)){
	if($reset_edge eq "negedge"){
	    if(!defined($ports{"reset"})){
		$ports{"reset"} = "rst_n";
	    }
	}
	elsif($reset_edge eq "posedge"){
	    if(!defined($ports{"reset"})){
		$ports{"reset"} = "rst";
	    }
	}
	else{
	    print STDERR "Invalid reset_edge: $reset_edge\n";
	    printUsage();
	}
    }

    if(defined($outFileName)){
	if(!open(OUTFILE, "> $outFileName")){
	    print STDERR "Unable to open outfile for writing: $outFileName\n";
	    exit(-2);
	}
	$fh = \*OUTFILE;
    }
    else{
	$fh = \*STDOUT;
    }


    #ports
    my @portList = (["input  ", $ports{"i_data"}, $bitWidth],
		    ["input  ", $ports{"clk"}],
		    ["output ", $ports{"o_data"}, $bitWidth]);

    if(defined($ports{"reset"})){
	push(@portList, ["input  ", $ports{"reset"}]);
    }
    
    my $optSuffix = "";
    my $vanillaSuffix = "";
    if($optimal && $nonOptimal){
	$optSuffix = "_addChain";
	$vanillaSuffix = "_base";
    }
    elsif(!($optimal || $nonOptimal)){
	$optimal = 1; #default
    }

    unless($suppressInfo){
	print $fh $scriptInfo;
	print $fh "/* $cmdLine */\n";
    }

    if($optWarn){
	print $fh "/* Warning: zero-valued filter taps have been optimized away. */\n";
    }

    #registered ports:
    my %regPorts = %ports;

    $regPorts{"i_data"} = $ports{"i_data"} . "_in" if($inReg);
    $regPorts{"o_data"} = $ports{"o_data"} . "_in" if($outReg);
	
    my $prefix = "w";

    #inRegList:($inWire, $inReg, $bitWidth)
    my @inRegList = ([$ports{"i_data"}, $regPorts{"i_data"}, $bitWidth]);

    #outRegList:($outReg, $outWire, $bitWidth)
    my @outRegList = ([$ports{"o_data"}, $regPorts{"o_data"}, $bitWidth]);
    
    if($optimal){
	my $multiplyName = $moduleName . $optSuffix . "_MultiplyBlock";

	#multiply block is separate module
	# outputHash->{0} = (1,low,high) or (0,0,0) if no bits were used
	my ($multSize, $outputHash) = genMultiply($fh, "addChain", $multiplyName, $bitWidth, $fixedPoint, \@constants, $debug);

	genHeader($fh, $moduleName . $optSuffix, \@portList);
	my $regSize = 0;
	
	$regSize += registerIn($fh, \@inRegList, $ports{"clk"}, $ports{"reset"}, $reset_edge) if($inReg);
	$regSize += registerOut($fh, \@outRegList, $ports{"clk"}, $ports{"reset"}, $reset_edge) if($outReg);
 
	my $firSize = genFir($fh, \%regPorts, $bitWidth, $multiplyName, $outputHash, scalar(@constants), $reset_edge, $debug);
	genTail($fh, $moduleName . $optSuffix, $multSize + $regSize + $firSize);
    }
    if($nonOptimal){
	my $multiplyName = $moduleName . $vanillaSuffix . "_MultiplyBlock";
	
	#multiply block is separate module
	genMultiply($fh, "base", $multiplyName, $bitWidth, $fixedPoint, \@constants);
	    
	my %outputHash = ();

	genHeader($fh, $moduleName . $vanillaSuffix, \@portList);
	registerIn($fh, \@inRegList, $ports{"clk"}, $ports{"reset"}, $reset_edge) if($inReg);
	registerOut($fh, \@outRegList, $ports{"clk"}, $ports{"reset"}, $reset_edge) if($outReg);
	genFir($fh, \%regPorts, $bitWidth, $multiplyName, \%outputHash, scalar(@constants), $reset_edge);
	genTail($fh, $moduleName . $vanillaSuffix);
    }
    if($testBench){
	my @dummy = ();
	my @moduleList =();
	push(@moduleList, $moduleName . $optSuffix) if($optimal);
	push(@moduleList, $moduleName . $vanillaSuffix) if($nonOptimal);
	
	genHeader($fh, $testBenchName, \@dummy);
        genTestBench($fh, \%ports, \@moduleList, $bitWidth, scalar(@constants), (($inReg?1:0) + ($outReg?1:0)),  $fixedPoint, $reset_edge, \@constants);
	genTail($fh, $testBenchName);
    }
    close($fh) if(defined($outFileName));

    return 1;
}

#-----------------------------------------------------------------------
# @brief prints the usage and exits -1
#-----------------------------------------------------------------------
sub printUsage(){
    
    print STDERR <<EOF;

$scriptInfo

  ./firGen.pl 10 20 30 40 30 20 10 [-moduleName firFilter] [-bitWidth 32]
         [-fractionalBits value] [-inData i_data] [-inReg] [-outData o_data] [-outReg] 
         [-clk clk] [-base] [-addChain] [-testBench | -testBenchName top] [-outFile fileName]
         [-suppressInfo] [-reset wireName] [-reset_edge negedge|posedge]

  currently prints to stdout, will soon have -outFile working.

  constants:the constants to multiply by.
    must occur before any options

  -moduleName: verilog module name default to firFilter
  -bitWidth: How many bits of data the multiplier multiplies by
  -fractionalBits: how many bits of data are below the decimal point, default: 0
     when this is used, the constants still need to be whole numbers, merely 
     2^x is now construed to be 1, where x is the fractionalBits value
  -inData: verilog input port name
  -inReg: cleanly register input
  -outData: verilog output port name
  -outReg: cleanly register output
  -clk: verilog clk name
  -reset: wireName for reset wire
  -reset_edge: use asynchronous reset at negedge or posedge
     if neither reset or reset_edge is provided on the commandline, filter generated
       defaults to no reset line.  
     If -reset is defined but not -reset_edge, edge defaults to negedge.
     If -reset_edge is defined, but not -reset, reset wire defaults to rst_n for negedge
       reset and rst for posedge reset
  -base: generate the vanilla, non-optimized version, for comparison
     may be used in conjunction with -addChain, will generate 2 modules 
  -addChain: generate the optimized version, default
     may be used in conjunction with -base, will generate 2 modules
  -testBench: generate testBench
     if used alone or in conjunction with one of the above two options, 
     will generate a test bench to print the inputs and outputs
     based on a pseudo random input stream
 
     if used in conjunction with both -base and -addChain, will
     will generate code to compare the output of the two when run
  -testBenchName: generate testBench with the given name

  -outFile print output to said fileName, defaults to stdout
  -suppressInfo don't tag script info at the beginning of the verilog file

EOF

  exit(-1);
}

if(!main()){
    print STDERR "Script Failed.\n";
    exit(-1);
}
else{
    exit(0);
}

1;