diff options
author | Clifford Wolf <clifford@clifford.at> | 2013-01-05 11:13:26 +0100 |
---|---|---|
committer | Clifford Wolf <clifford@clifford.at> | 2013-01-05 11:13:26 +0100 |
commit | 7764d0ba1dcf064ae487ee985c43083a0909e7f4 (patch) | |
tree | 18c05b8729df381af71b707748ce1d605e0df764 /tests/tools | |
download | yosys-7764d0ba1dcf064ae487ee985c43083a0909e7f4.tar.gz yosys-7764d0ba1dcf064ae487ee985c43083a0909e7f4.tar.bz2 yosys-7764d0ba1dcf064ae487ee985c43083a0909e7f4.zip |
initial import
Diffstat (limited to 'tests/tools')
-rwxr-xr-x | tests/tools/autotest.sh | 164 | ||||
-rw-r--r-- | tests/tools/cmp_tbdata.c | 67 | ||||
-rwxr-xr-x | tests/tools/profiler.pl | 55 | ||||
-rwxr-xr-x | tests/tools/rtlview.sh | 63 | ||||
-rwxr-xr-x | tests/tools/vcdcd.pl | 201 |
5 files changed, 550 insertions, 0 deletions
diff --git a/tests/tools/autotest.sh b/tests/tools/autotest.sh new file mode 100755 index 000000000..6b22f9023 --- /dev/null +++ b/tests/tools/autotest.sh @@ -0,0 +1,164 @@ +#!/bin/bash + +libs="" +genvcd=false +use_isim=false +verbose=false +keeprunning=false +backend_opts="-noattr -noexpr" +kompare_xst=false +scriptfiles="" +toolsdir="$(cd $(dirname $0); pwd)" + +if [ ! -f $toolsdir/cmp_tbdata -o $toolsdir/cmp_tbdata.c -nt $toolsdir/cmp_tbdata ]; then + ( set -ex; gcc -Wall -o $toolsdir/cmp_tbdata $toolsdir/cmp_tbdata.c; ) || exit 1 +fi + +while getopts il:wkvrxs: opt; do + case "$opt" in + i) + use_isim=true ;; + l) + libs="$libs $(cd $(dirname $OPTARG); pwd)/$(basename $OPTARG)";; + w) + genvcd=true ;; + k) + keeprunning=true ;; + v) + verbose=true ;; + r) + backend_opts="$backend_opts norename" ;; + x) + kompare_xst=true ;; + s) + [[ "$OPTARG" == /* ]] || OPTARG="$PWD/$OPTARG" + scriptfiles="$scriptfiles $OPTARG" ;; + *) + echo "Usage: $0 [-i] [-w] [-k] [-v] [-r] [-x] [-l libs] [-s script] verilog-files\n" >&2 + exit 1 + esac +done + +create_ref() { + if $kompare_xst; then + echo "verilog work $1" > $2.prj + cat <<- EOT > $2.xst + run + -ifn $2.prj -ifmt mixed -ofn $2 -ofmt NGC -p xc6slx4-3-tqg144 + -top $( grep ^module $1 | sed -r 's,[^0-9A-Za-z_]+, ,g' | awk '{ print $2; exit; }'; ) + -opt_mode Speed -opt_level 1 -iobuf NO + EOT + ( + set +x + prefix="$2" + xilver=$( ls -v /opt/Xilinx/ | tail -n1; ) + case "$( uname -m )" in + x86_64) + set --; . /opt/Xilinx/$xilver/ISE_DS/settings64.sh ;; + *) + set --; . /opt/Xilinx/$xilver/ISE_DS/settings32.sh ;; + esac + set -x + xst -ifn $prefix.xst + netgen -w -ofmt verilog $prefix.ngc $prefix + ) + else + cp "$1" "$2.v" + fi +} + +compile_and_run() { + exe="$1"; output="$2"; shift 2 + if $use_isim; then + ( + set +x + files=( "$@" ) + xilver=$( ls -v /opt/Xilinx/ | tail -n1; ) + case "$( uname -m )" in + x86_64) + set --; . /opt/Xilinx/$xilver/ISE_DS/settings64.sh ;; + *) + set --; . /opt/Xilinx/$xilver/ISE_DS/settings32.sh ;; + esac + set -x + vlogcomp "${files[@]}" + if $kompare_xst; then + fuse -o "$exe" -lib unisims_ver -top testbench -top glbl + else + fuse -o "$exe" -top testbench + fi + { echo "run all"; echo "exit"; } > run-all.tcl + PATH="$PATH:" "$exe" -tclbatch run-all.tcl > "$output" + ) + else + iverilog -s testbench -o "$exe" "$@" + vvp -n "$exe" > "$output" + fi +} + +shift $((OPTIND - 1)) + +for fn +do + bn=${fn%.v} + if [ "$bn" == "$fn" ]; then + echo "Invalid argument: $fn" >&2 + exit 1 + fi + [[ "$bn" == *_tb ]] && continue + echo -n "Test: $bn " + + rm -f ${bn}.{err,log} + mkdir -p ${bn}.out + rm -rf ${bn}.out/* + + body() { + cd ${bn}.out + cp ../$fn $fn + if [ ! -f ../${bn}_tb.v ]; then + "$toolsdir"/../../yosys -b autotest -o ${bn}_tb.v $fn + else + cp ../${bn}_tb.v ${bn}_tb.v + fi + if $genvcd; then sed -i 's,// \$dump,$dump,g' ${bn}_tb.v; fi + create_ref $fn ${bn}_ref + compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.v $libs + if $genvcd; then mv testbench.vcd ${bn}_ref.vcd; fi + + test_count=0 + test_passes() { + "$toolsdir"/../../yosys -b "verilog $backend_opts" "$@" -o ${bn}_syn${test_count}.v $fn $scriptfiles + compile_and_run ${bn}_tb_syn${test_count} ${bn}_out_syn${test_count} \ + ${bn}_tb.v ${bn}_syn${test_count}.v $libs \ + "$toolsdir"/../../techlibs/simlib.v \ + "$toolsdir"/../../techlibs/stdcells_sim.v + if $genvcd; then mv testbench.vcd ${bn}_syn${test_count}.vcd; fi + $toolsdir/cmp_tbdata ${bn}_out_ref ${bn}_out_syn${test_count} + test_count=$(( test_count + 1 )) + } + + if [ -n "$scriptfiles" ]; then + test_passes + else + test_passes -p hierarchy -p proc -p memory -p opt -p fsm -p opt + test_passes -p hierarchy -p proc -p memory -p opt -p fsm -p opt -p techmap -p opt + # test_passes -p hierarchy -p proc -p memory -p opt -p techmap -p opt -p abc -p opt + fi + touch ../${bn}.log + } + + if $verbose; then + echo ".." + echo "Output written to console." > ${bn}.err + ( set -ex; body; ) + else + ( set -ex; body; ) > ${bn}.err 2>&1 + fi + + if [ -f ${bn}.log ]; then + mv ${bn}.err ${bn}.log + echo "-> ok" + else echo "-> ERROR!"; $keeprunning || exit 1; fi +done + +exit 0 diff --git a/tests/tools/cmp_tbdata.c b/tests/tools/cmp_tbdata.c new file mode 100644 index 000000000..86485efd0 --- /dev/null +++ b/tests/tools/cmp_tbdata.c @@ -0,0 +1,67 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> + +int line = 0; +char buffer1[1024]; +char buffer2[1024]; + +void check(bool ok) +{ + if (ok) + return; + fprintf(stderr, "Error in testbench output compare (line=%d):\n-%s\n+%s\n", line, buffer1, buffer2); + exit(1); +} + +int main(int argc, char **argv) +{ + FILE *f1, *f2; + bool eof1, eof2; + int i; + + check(argc == 3); + + f1 = fopen(argv[1], "r"); + f2 = fopen(argv[2], "r"); + + check(f1 && f2); + + while (!feof(f1) && !feof(f2)) + { + line++; + buffer1[0] = 0; + buffer2[0] = 0; + + eof1 = fgets(buffer1, 1024, f1) == NULL; + eof2 = fgets(buffer2, 1024, f2) == NULL; + + if (*buffer1 && buffer1[strlen(buffer1)-1] == '\n') + buffer1[strlen(buffer1)-1] = 0; + + if (*buffer2 && buffer2[strlen(buffer2)-1] == '\n') + buffer2[strlen(buffer2)-1] = 0; + + check(eof1 == eof2); + + for (i = 0; buffer1[i] || buffer2[i]; i++) + { + check(buffer1[i] != 0 && buffer2[i] != 0); + + // first argument is the reference. An 'z' or 'x' + // here means we don't care about the result. + if (buffer1[i] == 'z' || buffer1[i] == 'x') + continue; + + check(buffer1[i] == buffer2[i]); + } + } + + check(feof(f1) && feof(f2)); + + fclose(f1); + fclose(f2); + return 0; +} + diff --git a/tests/tools/profiler.pl b/tests/tools/profiler.pl new file mode 100755 index 000000000..456f634bc --- /dev/null +++ b/tests/tools/profiler.pl @@ -0,0 +1,55 @@ +#!/usr/bin/perl +# parse 'yosys -t' logfile and find slow passes + +my $max_depth = 0; +my %last_line_by_depth; +my %last_time_by_depth; + +my @lines_text; +my @lines_depth; +my @lines_time; + +while (<>) +{ + chomp; + next unless /^\[([0-9.]+)\] (([0-9]+\.)+)/; + my ($this_time, $this_id, $this_header) = ($1, $2, $4); + + push @lines_text, $_; + push @lines_depth, 0; + push @lines_time, 0; + + my $depth = $this_id; + $depth =~ s/[^.]//g; + $depth = length $depth; + $max_depth = $depth if $depth > $max_depth; + + for (my $i = $depth; $i <= $max_depth; $i++) { + next unless exists $last_time_by_depth{$i}; + $lines_time[$last_line_by_depth{$i}] = $this_time-$last_time_by_depth{$i}; + delete $last_time_by_depth{$i}; + delete $last_header_by_depth{$i}; + } + + $last_time_by_depth{$depth} = $this_time; + $last_line_by_depth{$depth} = $#lines_text; + $lines_depth[$#lines_text] = $depth; +} + +for (my $depth = 1; $depth <= $max_depth; $depth++) { + printf "\nSlow passes on recursion depth %d:\n", $depth; + my @lines; + for (my $i = 0; $i <= $#lines_text; $i++) { + next if $lines_depth[$i] != $depth or $lines_time[$i] < 1.0; + push @lines, sprintf("%3d %08.2f %s\n", $lines_depth[$i], $lines_time[$i], $lines_text[$i]); + } + for my $line (sort {$b cmp $a} @lines) { + print $line; + } +} + +printf "\nFull journal of headers:\n"; +for (my $i = 0; $i <= $#lines_text; $i++) { + printf "%3d %08.2f %s\n", $lines_depth[$i], $lines_time[$i], $lines_text[$i]; +} + diff --git a/tests/tools/rtlview.sh b/tests/tools/rtlview.sh new file mode 100755 index 000000000..6a4adcae1 --- /dev/null +++ b/tests/tools/rtlview.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# using Xilinx ISE to display RTL schematics + +if [ ! -f "$1" ]; then + echo "Usage: $0 <verilog-file>" >&2 + exit 1 +fi + +prjdir="$(dirname $0)/rtlview.tmp" +mkdir -p "$prjdir" + +cp "$1" "$prjdir"/schematic.v +cp "$(dirname $0)"/../../techlibs/blackbox.v "$prjdir"/blackbox.v +cd "$prjdir" + +if fuser -s ise.out; then + echo "ISE already running. Re-create RTL schematic from GUI." + exit 1 +fi + +xilver=$( ls -v /opt/Xilinx/ | grep '^[0-9]' | tail -n1; ) + +cat > rtlview.xise << EOT +<?xml version="1.0" encoding="UTF-8" standalone="no" ?> +<project xmlns="http://www.xilinx.com/XMLSchema" xmlns:xil_pn="http://www.xilinx.com/XMLSchema"> + <header/> + <version xil_pn:ise_version="$xilver" xil_pn:schema_version="2"/> + + <files> + <file xil_pn:name="schematic.v" xil_pn:type="FILE_VERILOG"> + <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="1"/> + <association xil_pn:name="Implementation" xil_pn:seqID="2"/> + </file> + <file xil_pn:name="blackbox.v" xil_pn:type="FILE_VERILOG"> + <association xil_pn:name="BehavioralSimulation" xil_pn:seqID="1"/> + <association xil_pn:name="Implementation" xil_pn:seqID="2"/> + </file> + </files> + + <properties> + <property xil_pn:name="Device" xil_pn:value="xc6slx4" xil_pn:valueState="default"/> + <property xil_pn:name="Device Family" xil_pn:value="Spartan6" xil_pn:valueState="non-default"/> + <property xil_pn:name="Device Speed Grade/Select ABS Minimum" xil_pn:value="-3" xil_pn:valueState="default"/> + </properties> + + <bindings/> + <libraries/> + <autoManagedFiles/> +</project> +EOT + +set -- +case "$( uname -m )" in +x86_64) + . /opt/Xilinx/$xilver/ISE_DS/settings64.sh ;; +*) + . /opt/Xilinx/$xilver/ISE_DS/settings32.sh ;; +esac + +ise rtlview.xise > ise.out 2>&1 & +echo "ISE is now starting up. Create RTL schematic from GUI." + diff --git a/tests/tools/vcdcd.pl b/tests/tools/vcdcd.pl new file mode 100755 index 000000000..4875eeeb0 --- /dev/null +++ b/tests/tools/vcdcd.pl @@ -0,0 +1,201 @@ +#!/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; + +if ($#ARGV != 1) { + print STDERR "\n"; + print STDERR "VCDCD - Value Change Dump Change Dumper\n"; + print STDERR "\n"; + print STDERR "Usage: $0 gold.vcd gate.vcd\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 with 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 $_ (sort keys %orig_net_names) { + push @signals, $_; + 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; + } + } +} + +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; + + unshift @a, "-" while $#a < $#b; + unshift @b, "-" while $#b < $#a; + + for (my $i = 0; $i <= $#a; $i++) { + return 0 if $a[$i] ne "x" && $a[$i] ne $b[$i]; + } + + return 1; +} + +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}; + printf "%-10s %-20d %-*s %-*s %s\n", "<sync>", $time, $signal_maxlen, $stgo, $signal_maxlen, $stga, $net; + $signal_sync{$net} = 1; + } else { + printf "\n%-10s %-20s %-*s %-*s %s\n", "count", "time", $signal_maxlen, "gold", $signal_maxlen, "gate", "net" if $diffcount++ == 0; + printf "%-10d %-20d %-*s %-*s %s\n", $diffcount, $time, $signal_maxlen, $stgo, $signal_maxlen, $stga, $net; + $signal_sync{$net} = 0; + } + } +} + +print "Found $diffcount differences.\n"; +exit ($diffcount > 0 ? 1 : 0); |