| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 | #!/usr/bin/perl## Generate I/O-device boilerplate for firmware and Verilog#use integer;use strict;use File::Spec;# Variables from configuration fileour %consts;our $iodev_addr_bits;our $iodev_addr_shift;our $xdev_addr_bits;our $xdev_addr_shift;our @sysirqs;our @iodevs;sub base($$$) {    my($num,$bits,$shift) = @_;    my $v = ($num | (~0 << $bits)) << $shift;    return $v & 0xffffffff;}sub generate_h($) {    my($out) = @_;    print $out "#ifndef IODEVS_H\n";    print $out "#define IODEVS_H\n\n";    foreach my $c (sort(keys(%consts))) {	printf $out "#define %-23s 0x%08x /* %d */\n",	    $c, $consts{$c}, $consts{$c};    }    print $out "\n";    my $ndev  = 0;    my $nxdev = 0;    my $nirq  = 0;    foreach my $sysirq (@sysirqs) {	printf $out "#define %-23s %d\n", "\U$sysirq\E_IRQ", $nirq++;    }    foreach my $dev (@iodevs) {	my $dcount = $dev->{-count};	my $icount = length($dev->{-irq});	$dcount = 1 unless (defined($dcount));	next unless ($dcount);	my $name = uc($dev->{-name});	my $xdev = $dev->{-xdev};	if ($xdev) {	    printf $out "\n#define %-23s %d\n", $name.'_XDEV', $nxdev;	    printf $out "#define %-23s 0x%08x\n", $name.'_BASE',		base($nxdev, $xdev_addr_bits, $xdev_addr_shift);	} else {	    printf $out "\n#define %-23s %d\n", $name.'_DEV', $ndev;	    printf $out "#define %-23s 0x%08x\n", $name.'_BASE',		base($ndev, $iodev_addr_bits, $iodev_addr_shift);	}	printf $out "#define %-23s %d\n", $name.'_DEV_COUNT', $dcount;	if ($icount) {	    printf $out "#define %-23s %d\n", $name.'_IRQ', $nirq;	}	$ndev  += $xdev ? 0 : $dcount;	$nxdev += $xdev ? $dcount : 0;	$nirq  += $dcount * $icount;    }    printf $out "\n#define %-23s %d\n", 'IRQ_VECTORS', $nirq;    print $out "\n#endif /* IODEVS_H */\n";}sub generate_irqtbl($){    my($out) = @_;    my $nirq = 0;    my @irqtbl = ();    print $out "/* This is a generated file */\n\n";    print $out "#ifndef IRQENTRIES\n";    print $out "#define IRQENTRIES(name,irqbase,irqcount)\n";    print $out "#endif\n\n";    print $out "#ifndef IRQENTRY\n";    print $out "#define IRQENTRY(name,irqbase,irqn,irqcount)\n";    print $out "#endif\n\n";    foreach my $sysirq (@sysirqs) {	push(@irqtbl, [$sysirq, 1]);	$nirq++;    }    foreach my $dev (@iodevs) {	my $dcount = $dev->{-count};	my $icount = length($dev->{-irq});	$dcount = 1 unless (defined($dcount));	next unless ($dcount && $icount);	my $name = $dev->{-name};	push(@irqtbl, [$name, $dcount*$icount]);	$nirq += $dcount*$icount;    }    my $dirq = 0;    foreach my $irq (@irqtbl) {	printf $out "IRQENTRIES(%s,%d,%d)\n",	    $irq->[0], $dirq, $irq->[1];	for (my $i = 0; $i < $irq->[1]; $i++) {	    printf $out "IRQENTRY(%s,%d,%d,%d)\n",		$irq->[0], $dirq, $i, $irq->[1];	}	$dirq += $irq->[1];    }}sub generate_verilog($){    my($out) = @_;    foreach my $c (sort(keys(%consts))) {	printf $out "\tlocalparam %-23s = \'h%08x; // %d\n",	    $c, $consts{$c}, $consts{$c};    }    print $out "\n";    my $ndev = 0;    my $nxdev = 0;    my $nirq = scalar(@sysirqs);    my $irq_edge = 0;    my @imux = ();    my @xmux = ();    my @wait = ();    my @valid = ();    my @irqs = ();    print $out "\treg  [31:0] nxdev_rdata;\n";    print $out "\treg  [31:0] iodev_rdata;\n";    printf $out "\twire [%2d:0] xdev_valid  = iodev_mem_valid << cpu_mem_addr[%d:%d];\n",	(1 << $xdev_addr_bits)-1,	$xdev_addr_shift+$xdev_addr_bits-1, $xdev_addr_shift;    printf $out "\twire [%2d:0] iodev_valid = xdev_valid[%d] << cpu_mem_addr[%d:%d];\n",	(1 << $iodev_addr_bits)-1, (1 << $xdev_addr_bits)-1,	$iodev_addr_shift+$iodev_addr_bits-1, $iodev_addr_shift;    print $out "\n";    foreach my $dev (@iodevs) {	my $dcount = $dev->{-count};	my $irq    = $dev->{-irq};	my $xdev   = $dev->{-xdev};	$dcount = 1 unless (defined($dcount));	next unless ($dcount);	my $name = $dev->{-name};	my $didx = ($dcount > 1) ? sprintf('[0:%d]', $dcount-1) : '';	printf $out "\twire [31:0] iodev_rdata_%s%s;\n", $name, $didx;	if ($irq ne '') {	    printf $out "\twire [%2d:0] iodev_irq_%s%s;\n",		length($irq)-1, $name, $didx;	}	if ($xdev) {	    printf $out "\twire [%2d:0] iodev_valid_%s = xdev_valid[%d:%d];\n",		$dcount-1, $name, $nxdev+$dcount-1, $nxdev;	} else {	    printf $out "\twire [%2d:0] iodev_valid_%s = iodev_valid[%d:%d];\n",		$dcount-1, $name, $ndev+$dcount-1, $ndev;	}	printf $out "\ttri1 [%2d:0] iodev_wait_n_%s;\n", $dcount-1, $name;	push(@wait, "(&iodev_wait_n_$name)");	print $out "\n";	for (my $d = 0; $d < $dcount; $d++) {	    my $dsuf = ($dcount > 1) ? "[$d]" : '';	    if ($xdev) {		push(@xmux, "iodev_rdata_$name$dsuf");	    } else {		push(@imux, "iodev_rdata_$name$dsuf");	    }	    for (my $i = 0; $i < length($irq); $i++) {		my $isuf = "[$i]";		my $type = substr($irq,$i,1);		push(@irqs, "iodev_irq_$name$dsuf$isuf");		if ($type eq 'e') {		    $irq_edge |= 1 << $nirq;		}		$nirq++;	    }	    if ($xdev) {		$nxdev++;	    } else {		$ndev++;	    }	}    }    print  $out "\t// I/O input MUX\n";    print  $out "\talways_comb\n";    printf $out "\t\tcase (cpu_mem_addr[%d:%d])\n",	$xdev_addr_shift+$xdev_addr_bits-1, $xdev_addr_shift;    my $nxdev = 0;    foreach my $dev (@xmux) {	printf $out "\t\t\t%d'd%d:\t iodev_rdata = %s;\n",	    $xdev_addr_bits, $nxdev++, $dev;    }    printf $out "\t\t\t%d'd%d:\n",	    $xdev_addr_bits, (1 << $xdev_addr_bits)-1;    printf $out "\t\t\tcase (cpu_mem_addr[%d:%d])\n",	$iodev_addr_shift+$iodev_addr_bits-1, $iodev_addr_shift;    my $ndev = 0;    foreach my $dev (@imux) {	printf $out "\t\t\t\t%d'd%d:\t iodev_rdata = %s;\n",	    $iodev_addr_bits, $ndev++, $dev;    }    print $out "\t\t\t\tdefault: iodev_rdata = 32'hxxxxxxxx;\n";    print $out "\t\t\tendcase\n";    print $out "\t\t\tdefault: iodev_rdata = 32'hxxxxxxxx;\n";    print $out "\t\tendcase\n";    print $out "\n";    print $out "\ttri0 [31:0] sys_irq;\n";    my $nirq = scalar(@sysirqs);    foreach my $irq (@irqs) {	printf $out "\tassign sys_irq[%2d] = %s;\n", $nirq++, $irq;    }    print $out "\n";    printf $out "\tlocalparam [31:0] irq_edge_mask =  32'h%08x;\n", $irq_edge;    printf $out "\tlocalparam [31:0] irq_masked    = ~32'h%08x;\n\n",	((1 << $nirq)-1);    printf $out "\twire iodev_wait_n = ";    if (scalar(@wait)) {	print $out join(" & \n\t\t", @wait);    } else {	print $out "1'b1";    }    print $out ";\n";}my($mode, $infile, $outfile) = @ARGV;unless (defined(do File::Spec->rel2abs($infile))) {    die "$0: $infile: $@\n"}# Export these as constants, too$consts{'IODEV_ADDR_BITS'}  = $iodev_addr_bits;$consts{'IODEV_ADDR_SHIFT'} = $iodev_addr_shift;$consts{'XDEV_ADDR_BITS'}   = $xdev_addr_bits;$consts{'XDEV_ADDR_SHIFT'}  = $xdev_addr_shift;open(my $out, '>', $outfile) or die;if ($mode eq 'h') {    generate_h($out);} elsif ($mode eq 'irqh') {    generate_irqtbl($out);} elsif ($mode eq 'v') {    generate_verilog($out);}close($out);
 |