summaryrefslogtreecommitdiff
path: root/tests/tools
diff options
context:
space:
mode:
authorClifford Wolf <clifford@clifford.at>2013-01-05 11:13:26 +0100
committerClifford Wolf <clifford@clifford.at>2013-01-05 11:13:26 +0100
commit7764d0ba1dcf064ae487ee985c43083a0909e7f4 (patch)
tree18c05b8729df381af71b707748ce1d605e0df764 /tests/tools
initial import
Diffstat (limited to 'tests/tools')
-rwxr-xr-xtests/tools/autotest.sh164
-rw-r--r--tests/tools/cmp_tbdata.c67
-rwxr-xr-xtests/tools/profiler.pl55
-rwxr-xr-xtests/tools/rtlview.sh63
-rwxr-xr-xtests/tools/vcdcd.pl201
5 files changed, 550 insertions, 0 deletions
diff --git a/tests/tools/autotest.sh b/tests/tools/autotest.sh
new file mode 100755
index 00000000..6b22f902
--- /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 00000000..86485efd
--- /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 00000000..456f634b
--- /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 00000000..6a4adcae
--- /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 00000000..4875eeeb
--- /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);