Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi all,

I am seeing a peculiar behavior.

I have some code that parses a data file containing some signals. This code is in a file called min.pl and it looks like below:

1 #!/usr/bin/perl 2 use strict; 3 use warnings; 4 use diagnostics; 5 use diagnostics -verbose; 6 7 my $vcdFile = $ARGV[0]; 8 9 my $datObj = Datafile->new(); 10 11 my ($inTimeRangeSignalsHRef, $sigsNotInTimeRangeHRef, $timeRngRef, + $hierScopeRef) = $datObj->collectSignals($vcdFile); 12 13 package Datafile; 14 sub new { 15 my $class = shift; 16 my $self = {}; 17 bless($self, $class); 18 return $self; 19 } 20 21 sub collectSignals { 22 my $self = shift; 23 my $vcdFileOrig = shift; 24 my %vcdSigValsScopeH = (); 25 my %inTimeRangeSignalsH = (); 26 my %sigsNotInTimeRangeH = (); 27 my $timeRng = ""; 28 my $hierScope = ""; 29 my $vcdObj = Datafile->new(); 30 my ($lastKnownSigValHRef, $scopesHierHRef, $scopesHRef, $timeRang +eHRef, $timeIntervalBasedActivityHRef, $vcdFile, $timeUnits) = $vcdOb +j->parseData($vcdFileOrig); 31 if(exists $timeRangeHRef->{$vcdFile}) { 32 $timeRng = $timeRangeHRef->{$vcdFile}->{'TIMERANGES'}->[-1]; 33 if(exists $timeIntervalBasedActivityHRef->{$vcdFile}) { 34 if(exists $timeIntervalBasedActivityHRef->{$vcdFile}->{$timeRng +}) { 35 foreach my $sigId (keys %{$timeIntervalBasedActivityHRef->{$vc +dFile}->{$timeRng}}) { 36 foreach my $sigName (keys %{$timeIntervalBasedActivityHRef->{ +$vcdFile}->{$timeRng}->{$sigId}}) { 37 foreach my $scopeNm (keys %{$timeIntervalBasedActivityHRef-> +{$vcdFile}->{$timeRng}->{$sigId}->{$sigName}}) { 38 if ( ($scopeNm =~ /_TB/) && (exists $timeIntervalBasedActiv +ityHRef->{$vcdFile}->{$timeRng}->{$sigId}->{$sigName}->{$scopeNm}->{' +EXPANDEDVAL'}) ) { 39 $hierScope = $scopeNm; 40 my $sigValue = $timeIntervalBasedActivityHRef->{$vcdFile} +->{$timeRng}->{$sigId}->{$sigName}->{$scopeNm}->{'EXPANDEDVAL'}->[-1] +; 41 $inTimeRangeSignalsH{$vcdFile}{$sigName}{$sigId}{$timeRng} +{$scopeNm} = $sigValue; 42 } 43 } 44 } 45 } 46 } 47 foreach my $signal (keys %{$inTimeRangeSignalsH{$vcdFile}}) { 48 print "Line 48 ::Dbg:: $signal $vcdFile\n"; 49 } 50 } 51 } 52 if(exists $lastKnownSigValHRef->{$vcdFile}) { 53 foreach my $signalId (keys %{$lastKnownSigValHRef->{$vcdFile}}) +{ 54 foreach my $signalNm (keys %{$lastKnownSigValHRef->{$vcdFile}-> +{$signalId}}) { 55 if(!exists $inTimeRangeSignalsH{$vcdFile}{$signalNm}{$signalId +}) { 56 $sigsNotInTimeRangeH{$vcdFile}{$signalNm}{$signalId} = $lastK +nownSigValHRef->{$vcdFile}->{$signalId}->{$signalNm}->{$hierScope}->[ +0]; 57 } 58 } 59 } 60 } 61 foreach my $vcdFile (keys %inTimeRangeSignalsH) { 62 foreach my $sig (keys %{$inTimeRangeSignalsH{$vcdFile}}) { 63 print "Line 63 ::Dbg:: $sig $vcdFile\n"; 64 } 65 } 66 return(\%inTimeRangeSignalsH, \%sigsNotInTimeRangeH, \$timeRng, \ +$hierScope); 67 } 68 69 sub parseData { 70 my $self = shift; 71 my $vcdFile = shift; 72 my @scopesA = (); 73 my @timeValsA = (); 74 my %scopesHierH = (); 75 my %timedSigChangesH = (); 76 my %totalSigActivityH = (); 77 my %signalNotFoundInAnyScope = (); 78 my %timeRangeH = (); 79 my %timeValsH = (); 80 my %dumpVarsH = (); 81 my %scopesH = (); 82 my %lastKnownSigValH = (); 83 my $timescaleFlg = 0; 84 my $scopeFlg = 0; 85 my $scopeCnt = 0; 86 my $upscopeCnt = 0; 87 my $dumpvarsFlg = 0; 88 my $inTimeFlg = 0; 89 my $timeCnt = 0; 90 my $timeScale = ""; 91 my $timeUnits = ""; 92 my $timeMultiplier = ""; 93 my $scopeName = ""; 94 my $timeValue = ""; 95 my $foundSignalInSomeScope = 0; 96 97 open(VCD,"<", $vcdFile) or die "Cannot open $vcdFile:$!"; 98 while(<VCD>) { 99 chomp; 100 if(/^\s*\$timescale\s*$/) { 101 $timescaleFlg = 1; 102 } 103 if($timescaleFlg == 1) { 104 if(/^\s*\$end\s*$/) { 105 $timescaleFlg = 0; 106 }else{ 107 if(/^\s*([0-9]+)\s+(\S+)\s*$/) { 108 $timeMultiplier = $1; 109 $timeUnits = $2; 110 } 111 } 112 } 113 if(/^\s*\$scope\s+module\s+(\S+)\s+\$end/) { 114 $scopeFlg = 1; 115 $scopeName = $1; 116 $scopeCnt++; 117 push(@{$scopesHierH{$vcdFile}{'SCOPES'}}, $scopeName); 118 } 119 if($scopeFlg == 1) { 120 if(/^\s*\$upscope\s+\$end/) { 121 $upscopeCnt++; 122 if($upscopeCnt == $scopeCnt) { 123 $scopeFlg = 0; 124 } 125 }else{ 126 my $varLine = ""; 127 if(/^\s*\$var\s+.*\$end\s*$/) { 128 $varLine = $_; 129 $varLine =~ s/\s+\[(.*)\]\s+\$end/\[$1\] \$end/g; 130 } 131 if($varLine =~ /^\s*\$var\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+\$ +end\s*$/) { 132 my $sigName = $4; 133 my $sigId = $3; 134 my $sigSize = $2; 135 my $sigType = $1; 136 if($sigName =~ /\[\S+\]/) { 137 } 138 if( (defined $scopeName) && ($scopeName ne "")) { 139 if((defined $sigId) && ($sigId ne "")) { 140 if((defined $sigName) && ($sigName ne "")) { 141 if((defined $sigSize) && ($sigSize ne "")) { 142 if((defined $sigType) && ($sigType ne "")) { 143 $scopesH{$vcdFile}{$sigId}{$sigName}{$scopeName} = [$sig +Type, $sigSize]; 144 } 145 } 146 } 147 } 148 } 149 } 150 } 151 } 152 if( (/^\s*\$dumpvars\s*$/) && ($inTimeFlg == 0) ) { 153 $dumpvarsFlg = 1; 154 } 155 if( ($dumpvarsFlg == 1) && ($inTimeFlg == 0) ) { 156 if(/^\s*\$end\s*$/) { 157 $dumpvarsFlg = 0; 158 }else{ 159 if(/^\s*(b.*)\s+(\S+)\s*$/) { 160 my $sigId = $2; 161 my $sigVal = $1; 162 if(exists $scopesH{$vcdFile}{$sigId}) { 163 foreach my $sigNm (sort keys %{$scopesH{$vcdFile}{$sigId}}) +{ 164 foreach my $scopeNm (sort keys %{$scopesH{$vcdFile}{$sigId +}{$sigNm}}) { 165 $lastKnownSigValH{$vcdFile}{$sigId}{$sigNm}{$scopeNm} = [$ +sigVal, 'dumpvars']; 166 } 167 } 168 } 169 }elsif( (/^\s*(\S)(\S+)\s*$/) && !(/^\s*(b.*)\s+(\S+)\s*$/) ) +{ 170 my $sigId = $2; 171 my $sigVal = $1; 172 if(exists $scopesH{$vcdFile}{$sigId}) { 173 foreach my $sigNm (sort keys %{$scopesH{$vcdFile}{$sigId}}) +{ 174 foreach my $scopeNm (sort keys %{$scopesH{$vcdFile}{$sigId +}{$sigNm}}) { 175 $lastKnownSigValH{$vcdFile}{$sigId}{$sigNm}{$scopeNm} = [$ +sigVal, 'dumpvars']; 176 } 177 } 178 } 179 } 180 } 181 } 182 if(/^\s*(#\S+)\s*$/) { 183 $inTimeFlg = 1; 184 my $timePoint = $1; 185 } 186 if($inTimeFlg == 1) { 187 if(/^\s*#(\S+)\s*$/) { 188 my $time = $1; 189 $time *= $timeMultiplier; 190 $timeValue = '#'.$time; 191 push(@timeValsA, $timeValue); 192 }elsif( (/^\s*(\S)(\S+)\s*$/) || (/^\s*(b.*)\s+(\S+)\s*$/) ) { 193 my $signalVal = $1; 194 my $signalId = $2; 195 if(!exists $totalSigActivityH{$vcdFile}{$signalId}) { 196 $totalSigActivityH{$vcdFile}{$signalId}{'CNT'} = 0; 197 }else{ 198 $totalSigActivityH{$vcdFile}{$signalId}{'CNT'}++; 199 } 200 if(exists $scopesH{$vcdFile}{$signalId}) { 201 foreach my $sigNm (sort keys %{$scopesH{$vcdFile}{$signalId}} +) { 202 foreach my $scopeNm (sort keys %{$scopesH{$vcdFile}{$signal +Id}{$sigNm}}) { 203 if($scopeNm =~ /_TB/) { 204 $lastKnownSigValH{$vcdFile}{$signalId}{$sigNm}{$scopeNm} = + [$signalVal, $timeValue]; 205 if(exists $timedSigChangesH{$vcdFile}{$timeValue}{$signalI +d}{$sigNm}{$scopeNm}) { 206 if( (exists $timedSigChangesH{$vcdFile}{$timeValue}{$sign +alId}{$sigNm}{$scopeNm}{'VAL'}) && ($timedSigChangesH{$vcdFile}{$time +Value}{$signalId}{$sigNm}{$scopeNm}{'VAL'} ne "") ) { 207 if($timedSigChangesH{$vcdFile}{$timeValue}{$signalId}{$s +igNm}{$scopeNm}{'VAL'} ne $signalVal) { 208 if(exists $timedSigChangesH{$vcdFile}{$timeValue}{$sign +alId}{$sigNm}{$scopeNm}{'CNT'}) { 209 $timedSigChangesH{$vcdFile}{$timeValue}{$signalId}{$si +gNm}{$scopeNm}{'CNT'}++; 210 }else{ 211 $timedSigChangesH{$vcdFile}{$timeValue}{$signalId}{$si +gNm}{$scopeNm}{'CNT'} = 0; 212 } 213 $timedSigChangesH{$vcdFile}{$timeValue}{$signalId}{$sig +Nm}{$scopeNm}{'VAL'} = $signalVal; 214 my $expandedVal = $signalVal; 215 push(@{$timedSigChangesH{$vcdFile}{$timeValue}{$signalI +d}{$sigNm}{$scopeNm}{'EXPANDEDVAL'}}, $expandedVal); 216 }elsif($timedSigChangesH{$vcdFile}{$timeValue}{$signalId +}{$sigNm}{$scopeNm}{'VAL'} eq $signalVal) { 217 my $expandedVal = $signalVal; 218 push(@{$timedSigChangesH{$vcdFile}{$timeValue}{$signalI +d}{$sigNm}{$scopeNm}{'EXPANDEDVAL'}}, $expandedVal); 219 if (!exists $timedSigChangesH{$vcdFile}{$timeValue}{$si +gnalId}{$sigNm}{$scopeNm}{'CNT'}) { 220 $timedSigChangesH{$vcdFile}{$timeValue}{$signalId}{$si +gNm}{$scopeNm}{'CNT'} = 0; 221 } 222 } 223 }elsif(!exists $timedSigChangesH{$vcdFile}{$timeValue}{$s +ignalId}{$sigNm}{$scopeNm}{'VAL'}) { 224 $timedSigChangesH{$vcdFile}{$timeValue}{$signalId}{$sigN +m}{$scopeNm}{'VAL'} = $signalVal; 225 $timedSigChangesH{$vcdFile}{$timeValue}{$signalId}{$sigN +m}{$scopeNm}{'CNT'} = 0; 226 my $expandedVal = $signalVal; 227 push(@{$timedSigChangesH{$vcdFile}{$timeValue}{$signalId +}{$sigNm}{$scopeNm}{'EXPANDEDVAL'}}, $expandedVal); 228 $signalNotFoundInAnyScope{$vcdFile}{$signalId}++;; 229 } 230 }elsif(!exists $timedSigChangesH{$vcdFile}{$timeValue}{$si +gnalId}{$sigNm}{$scopeNm}) { 231 $timedSigChangesH{$vcdFile}{$timeValue}{$signalId}{$sigNm +}{$scopeNm}{'VAL'} = $signalVal; 232 $timedSigChangesH{$vcdFile}{$timeValue}{$signalId}{$sigNm +}{$scopeNm}{'CNT'} = 0; 233 my $expandedVal = $signalVal; 234 push(@{$timedSigChangesH{$vcdFile}{$timeValue}{$signalId} +{$sigNm}{$scopeNm}{'EXPANDEDVAL'}}, $expandedVal); 235 } 236 } 237 } 238 } 239 } 240 } 241 } 242 } 243 close VCD; 244 my %timeIntervalBasedActivityH = (); 245 my @timeRangeA = (); 246 push(@timeValsA, 'END'); 247 my $limit = scalar (@timeValsA) - 1; 248 for(my $i = 0; $i <= $limit; $i++) { 249 if(exists $timedSigChangesH{$vcdFile}{$timeValsA[$i]}) { 250 my $lBound = $timeValsA[$i]; 251 my $hBound = $timeValsA[$i+1]; 252 my $timeRange = $lBound." ".$hBound; 253 push(@{$timeRangeH{$vcdFile}{'TIMERANGES'}}, $timeRange); 254 foreach my $sigId (keys %{$timedSigChangesH{$vcdFile}{$lBound}} +) { 255 foreach my $sigName (keys %{$timedSigChangesH{$vcdFile}{$lBoun +d}{$sigId}}) { 256 foreach my $scopeNm (keys %{$timedSigChangesH{$vcdFile}{$lBou +nd}{$sigId}{$sigName}}) { 257 if(exists $timedSigChangesH{$vcdFile}{$lBound}{$sigId}{$sigN +ame}{$scopeNm}{'CNT'}) { 258 $timeIntervalBasedActivityH{$vcdFile}{$timeRange}{$sigId}{$ +sigName}{$scopeNm}{'CNT'} = $timedSigChangesH{$vcdFile}{$lBound}{$sig +Id}{$sigName}{$scopeNm}{'CNT'}; 259 } 260 if(exists $timedSigChangesH{$vcdFile}{$lBound}{$sigId}{$sigN +ame}{$scopeNm}{'EXPANDEDVAL'}) { 261 foreach my $expandedVal (@{$timedSigChangesH{$vcdFile}{$lBo +und}{$sigId}{$sigName}{$scopeNm}{'EXPANDEDVAL'}}) { 262 push(@{$timeIntervalBasedActivityH{$vcdFile}{$timeRange}{$ +sigId}{$sigName}{$scopeNm}{'EXPANDEDVAL'}}, $expandedVal); 263 } 264 } 265 } 266 } 267 } 268 } 269 } 270 return(\%lastKnownSigValH, \%scopesHierH, \%scopesH, \%timeRangeH +, \%timeIntervalBasedActivityH, $vcdFile, $timeUnits); 271 }

This code is used to process the below data file. The contents of the data file (named as junkSignals.dat), looks like below:

### Datafile ### $date Dec 02, 2012 04:51:31 $end $version TOOL: customOwned(64) 10.20-s027 $end $timescale 1 ps $end $scope module JUNKSIGNALS_TB $end $var wire 1 ! I0 $end $var wire 1 " I1 $end $var wire 1 # I2 $end $var wire 1 $ RIGHT $end $var wire 1 % SERIAL $end $var wire 1 & ZEN $end $scope module DUTJNKCELL $end $var wire 1 ! I0 $end $var wire 1 " I1 $end $var wire 1 # I2 $end $var wire 1 & ZEN $end $var wire 1 $ RIGHT $end $var wire 1 % SERIAL $end $var wire 1 ' lnx0_out $end $var wire 1 ( lnx1_out $end $var wire 1 ) lnx2_out $end $var wire 1 * lnx3_out $end $var wire 1 + lnx4_out $end $var wire 1 , lnx5_out $end $upscope $end $upscope $end $enddefinitions $end $dumpvars 0! 0" 0# 1$ 1% 1& 0' 0( 1) 0* 1+ 0, $end #10 1! 1' 1( 0) 0$ 0& #20
When I execute the code in min.pl using the above datafile saved in junkSignals.dat, my keys for the same hash %inTimeRangeSignalsH are different as seen in all the lines beginning  Line 48 versus lines beginning Line 63.
### Output ### Line 48 ::Dbg:: I0 junkSignals.dat Line 48 ::Dbg:: ZEN junkSignals.dat Line 48 ::Dbg:: RIGHT junkSignals.dat Line 63 ::Dbg:: lnx4_out junkSignals.dat Line 63 ::Dbg:: I0 junkSignals.dat Line 63 ::Dbg:: lnx1_out junkSignals.dat Line 63 ::Dbg:: ZEN junkSignals.dat Line 63 ::Dbg:: lnx3_out junkSignals.dat Line 63 ::Dbg:: RIGHT junkSignals.dat Line 63 ::Dbg:: lnx2_out junkSignals.dat Line 63 ::Dbg:: lnx5_out junkSignals.dat Line 63 ::Dbg:: lnx0_out junkSignals.dat Line 63 ::Dbg:: I2 junkSignals.dat Line 63 ::Dbg:: SERIAL junkSignals.dat Line 63 ::Dbg:: I1 junkSignals.dat

Coming from Python background, I am absolutely not sure what is causing this problem!

What am I doing wrong here?

Thankful for any help/insights

-jeff

Replies are listed 'Best First'.
Re: Why are Hash keys different for the same hash? Confusing. (VCD)
by toolic (Bishop) on Feb 27, 2016 at 21:11 UTC
    tl;dr, but... for parsing a VCD file, don't reinvent this wheel if you don't have to: Verilog::VCD. For example, the following code parses your VCD file and shows all signal names, times and values:
    use warnings FATAL => 'all'; use strict; use Verilog::VCD qw(parse_vcd); my $file = shift; my $vcd = parse_vcd($file); for my $code (keys %{ $vcd }) { my $name = "$vcd->{$code}{nets}[0]{hier}.$vcd->{$code}{nets}[0]{n +ame}"; my $times = @{ $vcd->{$code}{tv} }; for my $aref (@{ $vcd->{$code}{tv} }) { print "$name @{ $aref }\n"; } } __END__ JUNKSIGNALS_TB.RIGHT 0 1 JUNKSIGNALS_TB.RIGHT 10 0 JUNKSIGNALS_TB.SERIAL 0 1 JUNKSIGNALS_TB.I2 0 0 JUNKSIGNALS_TB.DUTJNKCELL.lnx5_out 0 0 JUNKSIGNALS_TB.DUTJNKCELL.lnx3_out 0 0 JUNKSIGNALS_TB.DUTJNKCELL.lnx4_out 0 1 JUNKSIGNALS_TB.I1 0 0 JUNKSIGNALS_TB.DUTJNKCELL.lnx2_out 0 1 JUNKSIGNALS_TB.DUTJNKCELL.lnx2_out 10 0 JUNKSIGNALS_TB.ZEN 0 1 JUNKSIGNALS_TB.ZEN 10 0 JUNKSIGNALS_TB.DUTJNKCELL.lnx0_out 0 0 JUNKSIGNALS_TB.DUTJNKCELL.lnx0_out 10 1 JUNKSIGNALS_TB.DUTJNKCELL.lnx1_out 0 0 JUNKSIGNALS_TB.DUTJNKCELL.lnx1_out 10 1 JUNKSIGNALS_TB.I0 0 0 JUNKSIGNALS_TB.I0 10 1

    If you can explain in words what you are trying to accomplish, I can try to help (I speak Verilog). Also keep in mind that hashes are un-ordered in Perl, and generally, you want to sort the keys.

    UPDATE: A few years ago, some translated this Perl code to Python: https://pypi.python.org/pypi/Verilog_VCD

      Hi toolic, that is great you speak Verilog. May be, I can seek some help with my hash described below from the original code :)

      Here is a very basic question I have. Perhaps, I did not ask the core of my question earlier, correctly and got lost in the details:

      In lines 47 through 49, I have the following code, where I am printing keys:

      47 foreach my $signal (keys %{$inTimeRangeSignalsH{$vcdFile}}) { 48 print "Line 48 ::Dbg:: $signal $vcdFile\n"; 49 }

      Further down in my code in lines 61 through 65, I am again printing the same keys and expecting that I get the same keys are the result (order of results is not important).

      But unfortunately, that expectation does not seem to be true.

      Why are the keys from the same hash different, when printed using lines 48 through 49 and lines 61 through 65?

      61 foreach my $vcdFile (keys %inTimeRangeSignalsH) { 62 foreach my $sig (keys %{$inTimeRangeSignalsH{$vcdFile}}) { 63 print "Line 63 ::Dbg:: $sig $vcdFile\n"; 64 } 65 }

      Any insights/corrections will be a great help.

        Your code is very difficult to read. Using standard four spaces for indentation would help.

        Each of those debug loops happens within a different conditional clause, so (without deciphering the function of your program) it would not be surprising if the data within the hash were different in each case.

        As someone else mentioned, you can use sort to print out your hash in the same order each time; makes it easier to see what's going on.

        You can also use Data::Dumper to save yourself a lot of boilerplate debugging code.

        (Data::Dumper is part of core Perl, so your overlords should be okay with it. On the other hand, I would seriously reconsider my participation if my boss told me to do a project in Perl without CPAN -- that's just silly.)

        The way forward always starts with a minimal test.
      Unfortunately, we cannot use outside modules as we have to write everything from scratch (customer gets what he wants:) )
        Sure you can. It is pure Perl code. Just copy and paste it from the CPAN website, just as you would copy and paste whatever code someone posts here.
Re: Why are Hash keys different for the same hash? Confusing.
by poj (Abbot) on Feb 27, 2016 at 21:57 UTC

    See my reply here Re^2: Hashes not working as expected

    Try this demo

    #!perl use Data::Dumper; my %hash = ( a=>{} ); if ( !exists $hash{'a'}{'b'}{'c'} ){}; print Dumper \%hash;

    The problem is with exists creating intermediate levels

     foreach my $signalNm (keys %{$lastKnownSigValHRef->{$vcdFile}->{$signalId}}) {
        ### here
        if(!exists $inTimeRangeSignalsH{$vcdFile}{$signalNm}{$signalId}) {
         $sigsNotInTimeRangeH{$vcdFile}{$signalNm}{$signalId} = $lastKnownSigValHRef->{$vcdFile}->{$signalId}->{$signalNm}->{$hierScope}->[0];
        }
      }
    

    I think you can fix it like this

    foreach my $signalNm (keys %{$lastKnownSigValHRef->{$vcdFile}->{$signalId}}) {
    
      if (exists $inTimeRangeSignalsH{$vcdFile}{$signalNm}) {
        if (exists $inTimeRangeSignalsH{$vcdFile}{$signalNm}{$signalId}) {
          next;
        }
      }
      $sigsNotInTimeRangeH{$vcdFile}{$signalNm}{$signalId} 
      = $lastKnownSigValHRef->{$vcdFile}{$signalId}{$signalNm}{$hierScope}[0];
    
    }
    

    Update : Rather than returning variables from your methods you can encapsulate them into the object. For example, replacing the return statement from parseData with

    #return(\%lastKnownSigValH, \%scopesHierH, \%scopesH, \%timeRangeH, # \%timeIntervalBasedActivityH, $vcdFile, $timeUnits); $self->{lastKnownSigValH} = \%lastKnownSigValH; $self->{scopesHierH} = \%scopesHierH; $self->{scopesH} = \%scopesH; $self->{timeRangeH} = \%timeRangeH; $self->{timeIntervalBasedActivityH} = \%timeIntervalBasedActivityH; $self->{vcdFile} = $vcdFile; $self->{timeUnits} = $timeUnits;

    would allow you to write more readable code like this

    #!/usr/bin/perl use strict; use warnings; use diagnostics -verbose; my $vcdFile = $ARGV[0]; my $datObj = Datafile->new(); $datObj->collectSignals($vcdFile); package Datafile; sub new { my $class = shift; my $self = { inTimeRangeSignalsH => {}, sigsNotInTimeRangeH => {}, timeRng => {}, hierScope => {}, }; return bless $self, $class; } sub collectSignals { my $self = shift; my $vcdFileOrig = shift; $self->parseData($vcdFileOrig); if (exists $self->{timeRangeH}{$vcdFile}) { my $timeRng = $self->{timeRangeH}{$vcdFile}{'TIMERANGES'}[-1]; $self->{timeRng} = $timeRng; if (exists $self->{timeIntervalBasedActivityH}{$vcdFile}) { my $hr1 = $self->{timeIntervalBasedActivityH}{$vcdFile}; if (exists $hr1->{$timeRng}) { my $hr2 = $hr1->{$timeRng}; for my $sigId (keys %$hr2) { for my $sigName (keys %{$hr2->{$sigId}}) { for my $scopeNm (keys %{$hr2->{$sigId}{$sigName}}) { if ( ($scopeNm =~ /_TB/) && (exists $hr2->{$sigId}{$sigName}{$scopeNm}{'EXPANDE +DVAL'}) ) { $self->{hierScope} = $scopeNm; my $sigValue = $hr2->{$sigId}{$sigName}{$scopeNm}{'EX +PANDEDVAL'}[-1]; $self->{inTimeRangeSignalsH}{$vcdFile}{$sigName}{$sigI +d}{$timeRng}{$scopeNm} = $sigValue; } } } } } foreach my $signal (keys %{$self->{inTimeRangeSignalsH}{$vcdFile +}}) { print "Line 51 ::Dbg:: $signal $vcdFile\n"; } } } if (exists $self->{lastKnownSigValH}{$vcdFile}) { my $hr1 = $self->{lastKnownSigValH}{$vcdFile}; foreach my $signalId (keys %$hr1) { foreach my $signalNm (keys %{$hr1->{$signalId}}) { if (exists $self->{inTimeRangeSignalsH}{$vcdFile}{$signalNm}){ if (exists $self->{inTimeRangeSignalsH}{$vcdFile}{$signalNm} +{$signalId}){ next; } } #if(!exists $self->{inTimeRangeSignalsH}{$vcdFile}{$signalNm}{ +$signalId}) { $self->{sigsNotInTimeRangeH}{$vcdFile}{$signalNm}{$signalId} = $hr1->{$signalId}{$signalNm}{$self->{hierScope}}[0]; #} } } } my $hr = $self->{inTimeRangeSignalsH}; for my $vcdFile (keys %$hr) { for my $sig (keys %{$hr->{$vcdFile}}) { print "Line 75 ::Dbg:: $sig $vcdFile\n"; } } }

    For debugging, it also allows you to dump all the data easily with

    use Data::Dump 'pp';
    pp $datObj;
    
    poj
      The problem is with exists creating intermediate levels
      Note that, if you consider this to be a problem you can install the autovivification module, then specify no autovivification to prevent it:
      #!/usr/bin/env perl use strict; use warnings; use 5.010; no autovivification; use Data::Dumper; my %hash = ( a=>{} ); if ( !exists $hash{'a'}{'b'}{'c'} ){}; print Dumper \%hash;
      Output:
      $VAR1 = { 'a' => {} };
      This is, however, apparently not an option for the OP, due to short-sighted customer requirements and autovivification is an XS module, so using its code independently of the original module would be a bit more involved than just copy/pasting the source.