#!/usr/bin/perl
#
# xdltap - Route nets from inside the FPGA to I/O pads
#
# Written 2011 by Werner Almesberger
# Copyright 2011 by Werner Almesberger
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#


sub change
{
	local ($s, $v, $from, $to) = @_;

	if (defined $from) {
		die "${v}::$from not found in\n$s"
		    unless $s =~ /\b${v}::$from/;
		return "$`${v}::$to$'";
	} else {
		die "${v}::... not found in\n$s"
		    unless $s =~ /\b${v}::\S+/;
		return "$`${v}::$to$'";
	}
}


sub change_var
{
	local ($s, $from, $to) = @_;

	die "${from}::... not found in\n$s"
	    unless $s =~ /\b${from}::/;
	return "$`${to}::$'";
}


sub reassign
{
	local ($s, $from, $to) = @_;

	$s =~ s/\bINBUF:${to}_IBUF:/OUTBUF:${to}_OBUF:/ ||
	    die "INBUF:${to}_IBUF: not found in\n$s";
	$s =~ s/\bPULL:${to}_PULLDOWN:// ||
	    die "PULL:${to}_PULLDOWN: not found in\n$s";
	$s =~ s/\bIMUX:[^:]+:I/IMUX::#OFF/ ||
	    die "IMUX:...:I not found in\n$s";
	$s = &change($s, "BYPASS_MUX", "I", "#OFF");
	$s = &change($s, "DRIVEATTRBOX", "#OFF", "12");
	$s = &change($s, "OUSED", "#OFF", "0");
	$s = &change($s, "PULLTYPE", "PULLDOWN", "#OFF");
	$s = &change($s, "SLEW", "#OFF", "SLOW");
	$s = &change($s, "SUSPEND", "#OFF", "3STATE");
	$s = &change_var($s, "ISTANDARD", "OSTANDARD");
	return $s;
}


sub usage
{
	print STDERR <<"END"
usage: $0 [net=output ...] [file]

  net     fully qualified path to the signal in question, e.g.,
          usb/sie/rx_pending
  output  name of the (input) connected to the output pin, e.g., exp<8>

For better shell compatibility, { and } can be used instead of < and >
END
	    ;
	exit(1);
}


&usage if $ARGV[0] =~ /^-/;

while ($ARGV[0] =~ /=/) {
	($from, $to) = ($`, $');
	$to =~ tr/{}/<>/;
	$from{$from} = $to;
	$to{$to} = $from;
	$need{$from} = $need{$to} = 1;
	shift @ARGV;
}

while (<>) {
	if ($_ =~ /^inst "([^"]*)"/ && defined $to{$1}) {
		die "" unless $need{$1};
		delete $need{$1};
		$s = $_;
		while (!/;\s*$/) {
			$_ = <>;
			$s .= $_;
		}
		print &reassign($s, $to{$1}, $1) || die "write: $!";
		next;
	}
	if (/^net "([^"]*)_IBUF"/ && defined $to{$1}) {
		next if /;\s*$/;
		while (<>) {
			last if /;\s*$/;
		}
		next;
	}
	print || die "write: $!";
	if (/^net "([^"]*)"/) {
		next unless defined $from{$1};
		die unless $need{$1};
		delete $need{$1};
		print "  inpin \"$from{$1}\" O,\n" || die "write: $!";
	}
}

die "not found: ".join(", ", sort keys %need) if %need;
