#!/usr/bin/perl # # Generate I/O-device boilerplate for firmware and Verilog # use integer; use strict; use File::Spec; # Variables from configuration file our %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);