iodevs.pl 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. #!/usr/bin/perl
  2. #
  3. # Generate I/O-device boilerplate for firmware and Verilog
  4. #
  5. use integer;
  6. use strict;
  7. use File::Spec;
  8. # Variables from configuration file
  9. our %consts;
  10. our $iodev_addr_bits;
  11. our $iodev_addr_shift;
  12. our $xdev_addr_bits;
  13. our $xdev_addr_shift;
  14. our @sysirqs;
  15. our @iodevs;
  16. sub base($$$) {
  17. my($num,$bits,$shift) = @_;
  18. my $v = ($num | (~0 << $bits)) << $shift;
  19. return $v & 0xffffffff;
  20. }
  21. sub generate_h($) {
  22. my($out) = @_;
  23. print $out "#ifndef IODEVS_H\n";
  24. print $out "#define IODEVS_H\n\n";
  25. foreach my $c (sort(keys(%consts))) {
  26. printf $out "#define %-23s 0x%08x /* %d */\n",
  27. $c, $consts{$c}, $consts{$c};
  28. }
  29. print $out "\n";
  30. my $ndev = 0;
  31. my $nxdev = 0;
  32. my $nirq = 0;
  33. foreach my $sysirq (@sysirqs) {
  34. printf $out "#define %-23s %d\n", "\U$sysirq\E_IRQ", $nirq++;
  35. }
  36. foreach my $dev (@iodevs) {
  37. my $dcount = $dev->{-count};
  38. my $icount = length($dev->{-irq});
  39. $dcount = 1 unless (defined($dcount));
  40. next unless ($dcount);
  41. my $name = uc($dev->{-name});
  42. my $xdev = $dev->{-xdev};
  43. if ($xdev) {
  44. printf $out "\n#define %-23s %d\n", $name.'_XDEV', $nxdev;
  45. printf $out "#define %-23s 0x%08x\n", $name.'_BASE',
  46. base($nxdev, $xdev_addr_bits, $xdev_addr_shift);
  47. } else {
  48. printf $out "\n#define %-23s %d\n", $name.'_DEV', $ndev;
  49. printf $out "#define %-23s 0x%08x\n", $name.'_BASE',
  50. base($ndev, $iodev_addr_bits, $iodev_addr_shift);
  51. }
  52. printf $out "#define %-23s %d\n", $name.'_DEV_COUNT', $dcount;
  53. if ($icount) {
  54. printf $out "#define %-23s %d\n", $name.'_IRQ', $nirq;
  55. }
  56. $ndev += $xdev ? 0 : $dcount;
  57. $nxdev += $xdev ? $dcount : 0;
  58. $nirq += $dcount * $icount;
  59. }
  60. printf $out "\n#define %-23s %d\n", 'IRQ_VECTORS', $nirq;
  61. print $out "\n#endif /* IODEVS_H */\n";
  62. }
  63. sub generate_irqtbl($)
  64. {
  65. my($out) = @_;
  66. my $nirq = 0;
  67. my @irqtbl = ();
  68. print $out "/* This is a generated file */\n\n";
  69. foreach my $sysirq (@sysirqs) {
  70. push(@irqtbl, [$sysirq, 1]);
  71. $nirq++;
  72. }
  73. foreach my $dev (@iodevs) {
  74. my $dcount = $dev->{-count};
  75. my $icount = length($dev->{-irq});
  76. $dcount = 1 unless (defined($dcount));
  77. next unless ($dcount && $icount);
  78. my $name = $dev->{-name};
  79. push(@irqtbl, [$name, $dcount*$icount]);
  80. $nirq += $dcount*$icount;
  81. }
  82. my $dirq = 0;
  83. foreach my $irq (@irqtbl) {
  84. for (my $i = 0; $i < $irq->[1]; $i++) {
  85. printf $out "IRQENTRY(%s,%d,%d,%d)\n",
  86. $irq->[0], $dirq++, $i, $irq->[1];
  87. }
  88. }
  89. }
  90. sub generate_verilog($)
  91. {
  92. my($out) = @_;
  93. foreach my $c (sort(keys(%consts))) {
  94. printf $out "\tlocalparam %-23s = \'h%08x; // %d\n",
  95. $c, $consts{$c}, $consts{$c};
  96. }
  97. print $out "\n";
  98. my $ndev = 0;
  99. my $nxdev = 0;
  100. my $nirq = scalar(@sysirqs);
  101. my $irq_edge = 0;
  102. my @imux = ();
  103. my @xmux = ();
  104. my @wait = ();
  105. my @valid = ();
  106. my @irqs = ();
  107. print $out "\treg [31:0] nxdev_rdata;\n";
  108. print $out "\treg [31:0] iodev_rdata;\n";
  109. printf $out "\twire [%2d:0] xdev_valid = iodev_mem_valid << cpu_mem_addr[%d:%d];\n",
  110. (1 << $xdev_addr_bits)-1,
  111. $xdev_addr_shift+$xdev_addr_bits-1, $xdev_addr_shift;
  112. printf $out "\twire [%2d:0] iodev_valid = xdev_valid[%d] << cpu_mem_addr[%d:%d];\n",
  113. (1 << $iodev_addr_bits)-1, (1 << $xdev_addr_bits)-1,
  114. $iodev_addr_shift+$iodev_addr_bits-1, $iodev_addr_shift;
  115. print $out "\n";
  116. foreach my $dev (@iodevs) {
  117. my $dcount = $dev->{-count};
  118. my $irq = $dev->{-irq};
  119. my $xdev = $dev->{-xdev};
  120. $dcount = 1 unless (defined($dcount));
  121. next unless ($dcount);
  122. my $name = $dev->{-name};
  123. my $didx = ($dcount > 1) ? sprintf('[0:%d]', $dcount-1) : '';
  124. printf $out "\twire [31:0] iodev_rdata_%s%s;\n", $name, $didx;
  125. if ($irq ne '') {
  126. printf $out "\twire [%2d:0] iodev_irq_%s%s;\n",
  127. length($irq)-1, $name, $didx;
  128. }
  129. if ($xdev) {
  130. printf $out "\twire [%2d:0] iodev_valid_%s = xdev_valid[%d:%d];\n",
  131. $dcount-1, $name, $nxdev+$dcount-1, $nxdev;
  132. } else {
  133. printf $out "\twire [%2d:0] iodev_valid_%s = iodev_valid[%d:%d];\n",
  134. $dcount-1, $name, $ndev+$dcount-1, $ndev;
  135. }
  136. printf $out "\ttri1 [%2d:0] iodev_wait_n_%s;\n", $dcount-1, $name;
  137. push(@wait, "(&iodev_wait_n_$name)");
  138. print $out "\n";
  139. for (my $d = 0; $d < $dcount; $d++) {
  140. my $dsuf = ($dcount > 1) ? "[$d]" : '';
  141. if ($xdev) {
  142. push(@xmux, "iodev_rdata_$name$dsuf");
  143. } else {
  144. push(@imux, "iodev_rdata_$name$dsuf");
  145. }
  146. for (my $i = 0; $i < length($irq); $i++) {
  147. my $isuf = "[$i]";
  148. my $type = substr($irq,$i,1);
  149. push(@irqs, "iodev_irq_$name$dsuf$isuf");
  150. if ($type eq 'e') {
  151. $irq_edge |= 1 << $nirq;
  152. }
  153. $nirq++;
  154. }
  155. if ($xdev) {
  156. $nxdev++;
  157. } else {
  158. $ndev++;
  159. }
  160. }
  161. }
  162. print $out "\t// I/O input MUX\n";
  163. print $out "\talways_comb\n";
  164. printf $out "\t\tcase (cpu_mem_addr[%d:%d])\n",
  165. $xdev_addr_shift+$xdev_addr_bits-1, $xdev_addr_shift;
  166. my $nxdev = 0;
  167. foreach my $dev (@xmux) {
  168. printf $out "\t\t\t%d'd%d:\t iodev_rdata = %s;\n",
  169. $xdev_addr_bits, $nxdev++, $dev;
  170. }
  171. printf $out "\t\t\t%d'd%d:\n",
  172. $xdev_addr_bits, (1 << $xdev_addr_bits)-1;
  173. printf $out "\t\t\tcase (cpu_mem_addr[%d:%d])\n",
  174. $iodev_addr_shift+$iodev_addr_bits-1, $iodev_addr_shift;
  175. my $ndev = 0;
  176. foreach my $dev (@imux) {
  177. printf $out "\t\t\t\t%d'd%d:\t iodev_rdata = %s;\n",
  178. $iodev_addr_bits, $ndev++, $dev;
  179. }
  180. print $out "\t\t\t\tdefault: iodev_rdata = 32'hxxxxxxxx;\n";
  181. print $out "\t\t\tendcase\n";
  182. print $out "\t\t\tdefault: iodev_rdata = 32'hxxxxxxxx;\n";
  183. print $out "\t\tendcase\n";
  184. print $out "\n";
  185. print $out "\ttri0 [31:0] sys_irq;\n";
  186. my $nirq = scalar(@sysirqs);
  187. foreach my $irq (@irqs) {
  188. printf $out "\tassign sys_irq[%2d] = %s;\n", $nirq++, $irq;
  189. }
  190. print $out "\n";
  191. printf $out "\tlocalparam [31:0] irq_edge_mask = 32'h%08x;\n", $irq_edge;
  192. printf $out "\tlocalparam [31:0] irq_masked = ~32'h%08x;\n\n",
  193. ((1 << $nirq)-1);
  194. printf $out "\twire iodev_wait_n = ";
  195. if (scalar(@wait)) {
  196. print $out join(" & \n\t\t", @wait);
  197. } else {
  198. print $out "1'b1";
  199. }
  200. print $out ";\n";
  201. }
  202. my($mode, $infile, $outfile) = @ARGV;
  203. unless (defined(do File::Spec->rel2abs($infile))) {
  204. die "$0: $infile: $@\n"
  205. }
  206. # Export these as constants, too
  207. $consts{'IODEV_ADDR_BITS'} = $iodev_addr_bits;
  208. $consts{'IODEV_ADDR_SHIFT'} = $iodev_addr_shift;
  209. $consts{'XDEV_ADDR_BITS'} = $xdev_addr_bits;
  210. $consts{'XDEV_ADDR_SHIFT'} = $xdev_addr_shift;
  211. open(my $out, '>', $outfile) or die;
  212. if ($mode eq 'h') {
  213. generate_h($out);
  214. } elsif ($mode eq 'irqh') {
  215. generate_irqtbl($out);
  216. } elsif ($mode eq 'v') {
  217. generate_verilog($out);
  218. }
  219. close($out);