mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 11:42:30 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			287 lines
		
	
	
	
		
			7.3 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			287 lines
		
	
	
	
		
			7.3 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable file
		
	
	
	
	
| #!/usr/bin/perl -w
 | |
| #
 | |
| # Note: You might need to install the Verilog::VCD package using CPAN..
 | |
| 
 | |
| use strict;
 | |
| use Data::Dumper;
 | |
| use Verilog::VCD qw(parse_vcd list_sigs);
 | |
| 
 | |
| $| = 1;
 | |
| 
 | |
| my $opt_width = 0;
 | |
| my $opt_delay = 0;
 | |
| 
 | |
| while ($#ARGV >= 0)
 | |
| {
 | |
| 	if ($ARGV[0] eq '-w') {
 | |
| 		$opt_width = +$ARGV[1];
 | |
| 		shift @ARGV;
 | |
| 		shift @ARGV;
 | |
| 		next;
 | |
| 	}
 | |
| 	if ($ARGV[0] eq '-d') {
 | |
| 		$opt_delay = +$ARGV[1];
 | |
| 		shift @ARGV;
 | |
| 		shift @ARGV;
 | |
| 		next;
 | |
| 	}
 | |
| 	last;
 | |
| }
 | |
| 
 | |
| if ($#ARGV != 1) {
 | |
| 	print STDERR "\n";
 | |
| 	print STDERR "VCDCD - Value Change Dump Change Dumper\n";
 | |
| 	print STDERR "\n";
 | |
| 	print STDERR "Usage: $0 [-w N] [-d N] gold.vcd gate.vcd\n";
 | |
| 	print STDERR "\n";
 | |
| 	print STDERR "  -w N\n";
 | |
| 	print STDERR "    reserve N characters for bitmap in text output (default: auto)\n";
 | |
| 	print STDERR "\n";
 | |
| 	print STDERR "  -d N\n";
 | |
| 	print STDERR "    allow for N timesteps delay between gate and gold (default: 0)\n";
 | |
| 	print STDERR "\n";
 | |
| 	print STDERR "Compare a known-good (gold) vcd file with a second (gate) vcd file.\n";
 | |
| 	print STDERR "This is not very efficient -- so use with care on large vcd files.\n";
 | |
| 	print STDERR "\n";
 | |
| 	exit 1;
 | |
| }
 | |
| 
 | |
| my $fn_gold = $ARGV[0];
 | |
| my $fn_gate = $ARGV[1];
 | |
| 
 | |
| print "Finding common signals..\n";
 | |
| my @gold_signals = list_sigs($fn_gold);
 | |
| my @gate_signals = list_sigs($fn_gate);
 | |
| 
 | |
| my %gold_signals_hash;
 | |
| my %gate_signals_hash;
 | |
| 
 | |
| for (@gold_signals) {
 | |
| 	my $fullname = $_;
 | |
| 	s/(\[([0-9]+|[0-9]+:[0-9]+)\])$//;
 | |
| 	$gold_signals_hash{$_}->{$fullname} = 1 unless /(^|\.)_[0-9]+_/;
 | |
| }
 | |
| 
 | |
| for (@gate_signals) {
 | |
| 	my $fullname = $_;
 | |
| 	s/(\[([0-9]+|[0-9]+:[0-9]+)\])$//;
 | |
| 	$gate_signals_hash{$_}->{$fullname} = 1 unless /(^|\.)_[0-9]+_/;
 | |
| }
 | |
| 
 | |
| my @signals;
 | |
| for my $net (sort keys %gold_signals_hash) {
 | |
| 	next unless exists $gate_signals_hash{$net};
 | |
| 	# next unless $net eq "tst_bench_top.i2c_top.byte_controller.bit_controller.cnt";
 | |
| 	my %orig_net_names;
 | |
| 	print "common signal: $net";
 | |
| 	for my $fullname (keys %{$gold_signals_hash{$net}}) {
 | |
| 		$orig_net_names{$fullname} = 1;
 | |
| 	}
 | |
| 	for my $fullname (keys %{$gate_signals_hash{$net}}) {
 | |
| 		$orig_net_names{$fullname} = 1;
 | |
| 	}
 | |
| 	for my $net (sort keys %orig_net_names) {
 | |
| 		push @signals, $net;
 | |
| 		print " $1" if /(\[([0-9]+|[0-9]+:[0-9]+)\])$/;
 | |
| 	}
 | |
| 	print "\n";
 | |
| }
 | |
| 
 | |
| print "Loading gold vcd data..\n";
 | |
| my $vcd_gold = parse_vcd($fn_gold, {siglist => \@signals});
 | |
| 
 | |
| print "Loading gate vcd data..\n";
 | |
| my $vcd_gate = parse_vcd($fn_gate, {siglist => \@signals});
 | |
| 
 | |
| # print Dumper($vcd_gold);
 | |
| # print Dumper($vcd_gate);
 | |
| 
 | |
| my %times;
 | |
| my $signal_maxlen = 8;
 | |
| my $data_gold = { };
 | |
| my $data_gate = { };
 | |
| 
 | |
| sub checklen($$)
 | |
| {
 | |
| 	my ($net, $val) = @_;
 | |
| 	my $thislen = length $val;
 | |
| 	$thislen += $1 if $net =~ /\[([0-9]+)\]$/;
 | |
| 	$thislen += $1 if $net =~ /\[([0-9]+):[0-9]+\]$/;
 | |
| 	$signal_maxlen = $thislen if $signal_maxlen < $thislen;
 | |
| }
 | |
| 
 | |
| print "Processing gold vcd data..\n";
 | |
| for my $key (keys %$vcd_gold) {
 | |
| 	for my $net (@{$vcd_gold->{$key}->{'nets'}}) {
 | |
| 		my $netname = $net->{'hier'} . "." . $net->{'name'};
 | |
| 		for my $tv (@{$vcd_gold->{$key}->{'tv'}}) {
 | |
| 			my $time = int($tv->[0]);
 | |
| 			my $value = $tv->[1];
 | |
| 			checklen($netname, $value);
 | |
| 			$data_gold->{$time}->{$netname} = $value;
 | |
| 			$times{$time} = 1;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| print "Processing gate vcd data..\n";
 | |
| for my $key (keys %$vcd_gate) {
 | |
| 	for my $net (@{$vcd_gate->{$key}->{'nets'}}) {
 | |
| 		my $netname = $net->{'hier'} . "." . $net->{'name'};
 | |
| 		for my $tv (@{$vcd_gate->{$key}->{'tv'}}) {
 | |
| 			my $time = int($tv->[0]);
 | |
| 			my $value = $tv->[1];
 | |
| 			checklen($netname, $value);
 | |
| 			$data_gate->{$time}->{$netname} = $value;
 | |
| 			$times{$time} = 1;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| $signal_maxlen = $opt_width if $opt_width > 0;
 | |
| 
 | |
| my $diffcount = 0;
 | |
| my %state_gold;
 | |
| my %state_gate;
 | |
| my %signal_sync;
 | |
| my %touched_nets;
 | |
| 
 | |
| sub set_state_bit($$$$)
 | |
| {
 | |
| 	my ($state, $net, $bit, $value) = @_;
 | |
| 	my @data;
 | |
| 	@data = split //, $state->{$net} if exists $state->{$net};
 | |
| 	unshift @data, "-" while $#data < $bit;
 | |
| 	$data[$#data - $bit] = $value;
 | |
| 	$state->{$net} = join "", @data;
 | |
| 	$signal_sync{$net} = 1 unless exists $signal_sync{$net};
 | |
| 	$touched_nets{$net} = 1;
 | |
| }
 | |
| 
 | |
| sub set_state($$$)
 | |
| {
 | |
| 	my ($state, $net, $value) = @_;
 | |
| 
 | |
| 	if ($net =~ /(.*)\[([0-9]+)\]$/) {
 | |
| 		set_state_bit($state, $1, $2, $value);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if ($net =~ /(.*)\[([0-9]+):([0-9]+)\]$/) {
 | |
| 		my ($n, $u, $d) = ($1, $2, $3);
 | |
| 		my @bits = split //, $value;
 | |
| 		my $extbit = $bits[0] eq "1" ? "0" : $bits[0];
 | |
| 		unshift @bits, $extbit while $#bits < $u - $d;
 | |
| 		set_state_bit($state, $n, $u--, shift @bits) while $u >= $d;
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	$state->{$net} = $value;
 | |
| 	$signal_sync{$net} = 1 unless exists $signal_sync{$net};
 | |
| 	$touched_nets{$net} = 1;
 | |
| }
 | |
| 
 | |
| sub cmp_signal($$)
 | |
| {
 | |
| 	my ($a, $b) = @_;
 | |
| 	return 1 if $a eq $b;
 | |
| 
 | |
| 	my @a = split //, $a;
 | |
| 	my @b = split //, $b;
 | |
| 
 | |
| 	my $trail_a = $#a < 0 ? '-' : $a[0] eq '1' ? '0' : $a[0];
 | |
| 	my $trail_b = $#b < 0 ? '-' : $b[0] eq '1' ? '0' : $b[0];
 | |
| 
 | |
| 	unshift @a, $trail_a while $#a < $#b;
 | |
| 	unshift @b, $trail_b while $#b < $#a;
 | |
| 
 | |
| 	for (my $i = 0; $i <= $#a; $i++) {
 | |
| 		next if $a[$i] eq "-" || $b[$i] eq "-";
 | |
| 		return 0 if $a[$i] ne "x" && $a[$i] ne $b[$i];
 | |
| 	}
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| # Message objects: .text, .time, .signal, .sync
 | |
| my @messages;
 | |
| 
 | |
| print "Comparing vcd data..\n";
 | |
| for my $time (sort { $a <=> $b } keys %times)
 | |
| {
 | |
| 	%touched_nets = ();
 | |
| 	for my $net (keys %{$data_gold->{$time}}) {
 | |
| 		set_state(\%state_gold, $net, $data_gold->{$time}->{$net});
 | |
| 	}
 | |
| 	for my $net (keys %{$data_gate->{$time}}) {
 | |
| 		set_state(\%state_gate, $net, $data_gate->{$time}->{$net});
 | |
| 	}
 | |
| 	for my $net (sort keys %touched_nets) {
 | |
| 		my ($stgo, $stga) = ('-', '-');
 | |
| 		$stgo = $state_gold{$net} if exists $state_gold{$net};
 | |
| 		$stga = $state_gate{$net} if exists $state_gate{$net};
 | |
| 		if (cmp_signal($stgo, $stga)) {
 | |
| 			next if $signal_sync{$net};
 | |
| 			my $message = { };
 | |
| 			$message->{text} = sprintf "%-10s %-20d %-*s %-*s %s\n", "<sync>", $time, $signal_maxlen, $stgo, $signal_maxlen, $stga, $net;
 | |
| 			$message->{time} = $time;
 | |
| 			$message->{signal} = $net;
 | |
| 			$message->{sync} = 1;
 | |
| 			push @messages, $message;
 | |
| 			$signal_sync{$net} = 1;
 | |
| 		} else {
 | |
| 			my $message = { };
 | |
| 			$message->{text} = sprintf "%-10d %-20d %-*s %-*s %s\n", $diffcount, $time, $signal_maxlen, $stgo, $signal_maxlen, $stga, $net;
 | |
| 			$message->{time} = $time;
 | |
| 			$message->{signal} = $net;
 | |
| 			$message->{sync} = 0;
 | |
| 			push @messages, $message;
 | |
| 			$signal_sync{$net} = 0;
 | |
| 			$diffcount++;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| print "Found $diffcount differences.\n";
 | |
| 
 | |
| if ($opt_delay > 0) {
 | |
| 	my %per_net_history;
 | |
| 	my $removed_diff_count = 0;
 | |
| 	for (my $i = 0; $i <= $#messages; $i++) {
 | |
| 		my $message = $messages[$i];
 | |
| 		$message->{deleted} = 0;
 | |
| 		$per_net_history{$message->{signal}} = [ ] unless exists $per_net_history{$message->{signal}};
 | |
| 		if ($message->{sync}) {
 | |
| 			my $deleted_all = 1;
 | |
| 			for my $j (@{$per_net_history{$message->{signal}}}) {
 | |
| 				my $m = $messages[$j];
 | |
| 				if ($m->{time} + $opt_delay >= $message->{time}) {
 | |
| 					$m->{deleted} = 1;
 | |
| 					$removed_diff_count++;
 | |
| 				} else {
 | |
| 					$deleted_all = 0;
 | |
| 				}
 | |
| 			}
 | |
| 			$message->{deleted} = 1 if $deleted_all;
 | |
| 			$per_net_history{$message->{signal}} = [ ];
 | |
| 		} else {
 | |
| 			push @{$per_net_history{$message->{signal}}}, $i;
 | |
| 		}
 | |
| 	}
 | |
| 	my @new_messages;
 | |
| 	for my $message (@messages) {
 | |
| 		push @new_messages, $message unless $message->{deleted};
 | |
| 	}
 | |
| 	@messages = @new_messages;
 | |
| 	printf "Removed %d differences using delay filtering.\n", $removed_diff_count;
 | |
| 	$diffcount = $diffcount - $removed_diff_count;
 | |
| }
 | |
| 
 | |
| if ($#messages >= 0) {
 | |
| 	printf "\n%-10s %-20s %-*s %-*s %s\n", "count", "time", $signal_maxlen, "gold", $signal_maxlen, "gate", "net" if $diffcount++ == 0;
 | |
| 	for (my $i = 0; $i <= $#messages; $i++) {
 | |
| 		printf "%s", $messages[$i]->{text};
 | |
| 	}
 | |
| }
 | |
| 
 | |
| exit ($diffcount > 0 ? 1 : 0);
 |