123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- #!/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 "#include \"picorv32.h\"\n";
- print $out "#include \"irq.h\"\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;
- }
- print $out "static IRQHANDLER(spurious)\n";
- print $out "{\n";
- print $out "\tmask_irq(vector);\n";
- print $out "}\n\n";
- foreach my $irq (@irqtbl) {
- printf $out "IRQHANDLER(%s) __attribute__((weak,alias(\"irqhandler_spurious\")));\n", $irq->[0];
- }
- print $out "\nirqhandler_t __attribute__((section(\".sdata\")))\n";
- printf $out "__irq_handler_table[%d] = {\n", $nirq;
- foreach my $irq (@irqtbl) {
- for (my $i = 0; $i < $irq->[1]; $i++) {
- printf $out "\tirqhandler_%s,\n", $irq->[0];
- }
- }
- print $out "};\n\n";
- }
- 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 'c') {
- generate_irqtbl($out);
- } elsif ($mode eq 'v') {
- generate_verilog($out);
- }
- close($out);
|