2
0

iodevs.pl 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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. print $out "#ifndef IRQENTRIES\n";
  70. print $out "#define IRQENTRIES(name,irqbase,irqcount)\n";
  71. print $out "#endif\n\n";
  72. print $out "#ifndef IRQENTRY\n";
  73. print $out "#define IRQENTRY(name,irqbase,irqn,irqcount)\n";
  74. print $out "#endif\n\n";
  75. foreach my $sysirq (@sysirqs) {
  76. push(@irqtbl, [$sysirq, 1]);
  77. $nirq++;
  78. }
  79. foreach my $dev (@iodevs) {
  80. my $dcount = $dev->{-count};
  81. my $icount = length($dev->{-irq});
  82. $dcount = 1 unless (defined($dcount));
  83. next unless ($dcount && $icount);
  84. my $name = $dev->{-name};
  85. push(@irqtbl, [$name, $dcount*$icount]);
  86. $nirq += $dcount*$icount;
  87. }
  88. my $dirq = 0;
  89. foreach my $irq (@irqtbl) {
  90. printf $out "IRQENTRIES(%s,%d,%d)\n",
  91. $irq->[0], $dirq, $irq->[1];
  92. for (my $i = 0; $i < $irq->[1]; $i++) {
  93. printf $out "IRQENTRY(%s,%d,%d,%d)\n",
  94. $irq->[0], $dirq, $i, $irq->[1];
  95. }
  96. $dirq += $irq->[1];
  97. }
  98. }
  99. sub generate_verilog($)
  100. {
  101. my($out) = @_;
  102. foreach my $c (sort(keys(%consts))) {
  103. printf $out "\tlocalparam %-23s = \'h%08x; // %d\n",
  104. $c, $consts{$c}, $consts{$c};
  105. }
  106. print $out "\n";
  107. my $ndev = 0;
  108. my $nxdev = 0;
  109. my $nirq = scalar(@sysirqs);
  110. my $irq_edge = 0;
  111. my @imux = ();
  112. my @xmux = ();
  113. my @wait = ();
  114. my @valid = ();
  115. my @irqs = ();
  116. print $out "\treg [31:0] nxdev_rdata;\n";
  117. print $out "\treg [31:0] iodev_rdata;\n";
  118. printf $out "\twire [%2d:0] xdev_valid = iodev_mem_valid << cpu_mem_addr[%d:%d];\n",
  119. (1 << $xdev_addr_bits)-1,
  120. $xdev_addr_shift+$xdev_addr_bits-1, $xdev_addr_shift;
  121. printf $out "\twire [%2d:0] iodev_valid = xdev_valid[%d] << cpu_mem_addr[%d:%d];\n",
  122. (1 << $iodev_addr_bits)-1, (1 << $xdev_addr_bits)-1,
  123. $iodev_addr_shift+$iodev_addr_bits-1, $iodev_addr_shift;
  124. print $out "\n";
  125. foreach my $dev (@iodevs) {
  126. my $dcount = $dev->{-count};
  127. my $irq = $dev->{-irq};
  128. my $xdev = $dev->{-xdev};
  129. $dcount = 1 unless (defined($dcount));
  130. next unless ($dcount);
  131. my $name = $dev->{-name};
  132. my $didx = ($dcount > 1) ? sprintf('[0:%d]', $dcount-1) : '';
  133. printf $out "\twire [31:0] iodev_rdata_%s%s;\n", $name, $didx;
  134. if ($irq ne '') {
  135. printf $out "\twire [%2d:0] iodev_irq_%s%s;\n",
  136. length($irq)-1, $name, $didx;
  137. }
  138. if ($xdev) {
  139. printf $out "\twire [%2d:0] iodev_valid_%s = xdev_valid[%d:%d];\n",
  140. $dcount-1, $name, $nxdev+$dcount-1, $nxdev;
  141. printf $out "\tlocalparam [31:0] iodev_%s_base = 32'h%08x;\n",
  142. $name, base($nxdev, $xdev_addr_bits, $xdev_addr_shift);
  143. } else {
  144. printf $out "\twire [%2d:0] iodev_valid_%s = iodev_valid[%d:%d];\n",
  145. $dcount-1, $name, $ndev+$dcount-1, $ndev;
  146. printf $out "\tlocalparam [31:0] iodev_%s_base = 32'h%08x;\n",
  147. $name, base($ndev, $iodev_addr_bits, $iodev_addr_shift);
  148. }
  149. printf $out "\ttri1 [%2d:0] iodev_wait_n_%s;\n", $dcount-1, $name;
  150. push(@wait, "(&iodev_wait_n_$name)");
  151. print $out "\n";
  152. for (my $d = 0; $d < $dcount; $d++) {
  153. my $dsuf = ($dcount > 1) ? "[$d]" : '';
  154. if ($xdev) {
  155. push(@xmux, "iodev_rdata_$name$dsuf");
  156. } else {
  157. push(@imux, "iodev_rdata_$name$dsuf");
  158. }
  159. for (my $i = 0; $i < length($irq); $i++) {
  160. my $isuf = "[$i]";
  161. my $type = substr($irq,$i,1);
  162. push(@irqs, "iodev_irq_$name$dsuf$isuf");
  163. if ($type eq 'e') {
  164. $irq_edge |= 1 << $nirq;
  165. }
  166. $nirq++;
  167. }
  168. if ($xdev) {
  169. $nxdev++;
  170. } else {
  171. $ndev++;
  172. }
  173. }
  174. }
  175. print $out "\t// I/O input MUX\n";
  176. print $out "\talways_comb\n";
  177. printf $out "\t\tcase (cpu_mem_addr[%d:%d])\n",
  178. $xdev_addr_shift+$xdev_addr_bits-1, $xdev_addr_shift;
  179. my $nxdev = 0;
  180. foreach my $dev (@xmux) {
  181. printf $out "\t\t\t%d'd%d:\t iodev_rdata = %s;\n",
  182. $xdev_addr_bits, $nxdev++, $dev;
  183. }
  184. printf $out "\t\t\t%d'd%d:\n",
  185. $xdev_addr_bits, (1 << $xdev_addr_bits)-1;
  186. printf $out "\t\t\tcase (cpu_mem_addr[%d:%d])\n",
  187. $iodev_addr_shift+$iodev_addr_bits-1, $iodev_addr_shift;
  188. my $ndev = 0;
  189. foreach my $dev (@imux) {
  190. printf $out "\t\t\t\t%d'd%d:\t iodev_rdata = %s;\n",
  191. $iodev_addr_bits, $ndev++, $dev;
  192. }
  193. print $out "\t\t\t\tdefault: iodev_rdata = 32'hxxxxxxxx;\n";
  194. print $out "\t\t\tendcase\n";
  195. print $out "\t\t\tdefault: iodev_rdata = 32'hxxxxxxxx;\n";
  196. print $out "\t\tendcase\n";
  197. print $out "\n";
  198. print $out "\ttri0 [31:0] sys_irq;\n";
  199. my $nirq = scalar(@sysirqs);
  200. foreach my $irq (@irqs) {
  201. printf $out "\tassign sys_irq[%2d] = %s;\n", $nirq++, $irq;
  202. }
  203. print $out "\n";
  204. printf $out "\tlocalparam [31:0] irq_edge_mask = 32'h%08x;\n", $irq_edge;
  205. printf $out "\tlocalparam [31:0] irq_masked = ~32'h%08x;\n\n",
  206. ((1 << $nirq)-1);
  207. printf $out "\twire iodev_wait_n = ";
  208. if (scalar(@wait)) {
  209. print $out join(" & \n\t\t", @wait);
  210. } else {
  211. print $out "1'b1";
  212. }
  213. print $out ";\n";
  214. }
  215. my($mode, $infile, $outfile) = @ARGV;
  216. unless (defined(do File::Spec->rel2abs($infile))) {
  217. die "$0: $infile: $@\n"
  218. }
  219. # Export these as constants, too
  220. $consts{'IODEV_ADDR_BITS'} = $iodev_addr_bits;
  221. $consts{'IODEV_ADDR_SHIFT'} = $iodev_addr_shift;
  222. $consts{'XDEV_ADDR_BITS'} = $xdev_addr_bits;
  223. $consts{'XDEV_ADDR_SHIFT'} = $xdev_addr_shift;
  224. open(my $out, '>', $outfile) or die;
  225. if ($mode eq 'h') {
  226. generate_h($out);
  227. } elsif ($mode eq 'irqh') {
  228. generate_irqtbl($out);
  229. } elsif ($mode eq 'v') {
  230. generate_verilog($out);
  231. }
  232. close($out);