usbdescgen.pl 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  1. #!/usr/bin/perl
  2. #
  3. # Simple tool for generating a USB desciptor table or ROM/RAM
  4. #
  5. use integer;
  6. use strict;
  7. use File::Spec;
  8. use Encode;
  9. require 'langid.ph';
  10. # Some forward declarations
  11. my($mode, $infile, $outfile) = @ARGV;
  12. my $module_name;
  13. my $err = 0;
  14. # Descriptor types. This follows the document in which they were specified;
  15. # some USB documents specify in hex and others in decimal...
  16. our %DT = (
  17. 'device' => 1,
  18. 'configuration' => 2, 'config' => 2, 'conf' => 2,
  19. 'string' => 3, 'str' => '3',
  20. 'interface' => 4, 'if' => '4',
  21. 'endpoint' => 5, 'ep' => 5,
  22. 'device_qualifier' => 6, 'dq' => 6,
  23. 'other_speed_configuration' => 7, 'otherspeed' => 7, 'osc' => 7,
  24. 'interface_power' => 8, 'ifpwr' => 8,
  25. 'otg' => 9,
  26. 'debug' => 10,
  27. 'interface_association' => 11, 'iad' => 11,
  28. 'bos' => 15, 'binary_object_storage' => 15,
  29. 'device_capability' => 16, 'devcap' => 16,
  30. 'cs_interface' => 0x24, 'cs_if' => 0x24,
  31. 'cs_endpoint' => 0x25, 'cs_ep' => 0x25,
  32. 'superspeed_usb_endpoint_companion' => 48, 'usb_ep_companion' => 48,
  33. 'superspeedplus_isochronous_endpoint_companion' => 49, 'iso_ep_compation' => 49,
  34. );
  35. # Descriptor subtypes, where applicable
  36. our %DS = (
  37. # Under CS_INTERFACE
  38. 0x24 => {
  39. 'header' => 0x00,
  40. 'call' => 0x01, 'call_management' => 0x01,
  41. 'abstract_control' => 0x02, 'acm' => 0x02,
  42. 'direct_line' => 0x03,
  43. 'ringer' => 0x04,
  44. 'line_state' => 0x05,
  45. 'union' => 0x06,
  46. 'country' => 0x07,
  47. 'op_mode' => 0x08,
  48. 'terminal' => 0x09,
  49. 'net_channel' => 0x0a,
  50. 'protocol_unit' => 0x0b,
  51. 'extension_unit' => 0x0c,
  52. 'multi_channel' => 0x0d,
  53. 'capi' => 0x0e,
  54. 'ethernet' => 0x0f,
  55. 'atm' => 0x10,
  56. 'whcm' => 0x11, 'wireless_handset' => 0x11,
  57. 'mobile_line' => 0x12, 'mobile_direct_line' => 0x12,
  58. 'mdlm_detail' => 0x13, 'mdlm' => 0x13,
  59. 'device_management' => 0x14, 'device' => 0x14, 'mgmt' => 0x14,
  60. 'command_set' => 0x16,
  61. 'command_set_detail' => 0x17,
  62. 'telephone_control' => 0x18, 'tcm' => 0x18, 'phone' => 0x18,
  63. 'obex_service_identifier' => 0x19, 'obex' => 0x19
  64. }
  65. );
  66. #
  67. # Class, subclass, and protocol codes. Map a string to a number, then
  68. # use that number as an index to descend, if that entry exists. 0 is
  69. # the default; for some classes the subclass code is unused and so is
  70. # set to 0; no string to look up.
  71. #
  72. # Numbers below the class level are massively incomplete, feel free to add.
  73. #
  74. # Applies to all levels
  75. my %class_all_levels = (
  76. undef => 0x00, 'undef' => 0x00, '-' => 0x00,
  77. 'none' => 0x00, 'default' => 0x00,
  78. 'vendor_specific' => 0xff, 'vendor' => 0xff,
  79. );
  80. my $cdc_pstn_protocols = {
  81. 'v250' => 0x01, 'at' => 0x01, 'v25ter' => 0x01,
  82. 'pcca101' => 0x02,
  83. 'pcca101o' => 0x03, 'pcca' => 0x03,
  84. 'gsm707' => 0x04, 'gsm' => 0x04,
  85. '3gpp2707' => 0x05, '3gpp' => 0x05, '3g' => 0x05,
  86. 'ca00170' => 0x06, 'tia_cdma' => 0x06, 'cdma' => 0x06
  87. };
  88. my %class_codes = (
  89. 'multi' => 0x00, # Real class code in interface descriptors
  90. 'audio' => 0x01,
  91. 'cdc' => 0x02, 'communications' => 0x02,
  92. 0x02 => {
  93. 'dlcm' => 0x01, 'direct_line_control' => 0x01,
  94. 'acm' => 0x02, 'abstract_control' => 0x02,
  95. 0x02 => $cdc_pstn_protocols,
  96. 'tcm' => 0x03, 'telephone_control' => 0x03,
  97. 0x03 => $cdc_pstn_protocols,
  98. 'mccm' => 0x04, 'multi_channel_control' => 0x04,
  99. 0x04 => $cdc_pstn_protocols,
  100. 'ccm' => 0x05, 'capi' => 0x05, 'capi_control' => 0x05,
  101. 0x05 => $cdc_pstn_protocols,
  102. 'ecm' => 0x06, 'ethernet_control' => 0x06,
  103. 0x06 => $cdc_pstn_protocols,
  104. 'atm' => 0x07, 'ancm' => 0x07, 'atm_networking_control' => 0x07,
  105. 'whcm' => 0x08, 'wireless_handset_control' => 0x08,
  106. 0x08 => $cdc_pstn_protocols,
  107. 'device_management' => 0x09, 'mgmt' => 0x09, 'device' => 0x09,
  108. 'mdlm' => 0x0a, 'mobile_direct_line' => 0x0a,
  109. 'obex' => 0x0b,
  110. 'eem' => 0x0c, 'ethernet' => 0x0c, 'ethernet_emulation' => 0x0c,
  111. 0x0c => {
  112. 'ethernet' => 0x07, 'eem' => 0x07
  113. },
  114. 'ncm' => 0x0d, 'net' => 0x0d, 'network_control' => 0x0d,
  115. },
  116. 'hid' => 0x03,
  117. 'physical' => 0x05,
  118. 'imaging' => 0x06, 'photo' => 0x06, 'idc' => 0x06,
  119. 'printer' => 0x07,
  120. 'mass_storage' => 0x08, 'storage' => 0x08, 'disk' => 0x08,
  121. 'hub' => 0x09,
  122. 'cdc_data' => 0x0a, 'data' => 0x0a,
  123. 0x0a => {
  124. 0 => {
  125. 'ntb' => 0x01, 'network_transfer_block' => 0x01,
  126. 'isdn_bri' => 0x30, 'isdn' => 0x30,
  127. 'hdlc' => 0x31,
  128. 'transparent' => 0x32,
  129. 'q921_management' => 0x50, 'q921m' => 0x50,
  130. 'q921_datalink' => 0x51, 'q921' => 0x51,
  131. 'q921_tei_mux' => 0x52, 'q921tm' => 0x52,
  132. 'v42bis' => 0x90,
  133. 'euro_isdn' => 0x91, 'q931' => 0x91,
  134. 'v120' => 0x92, 'isdn_v24' => 0x92,
  135. 'capi' => 0x93,
  136. 'host' => 0xfd,
  137. 'external' => 0xfe,
  138. 'vendor' => 0xff
  139. },
  140. },
  141. 'smart_card' => 0x0b, 'smartcard' => 0x0b, 'scdc' => 0x0b,
  142. 'content_security' => 0x0d, 'drm' => 0x0d, 'csdc' => 0x0d,
  143. 'video' => 0x0e, 'vdc' => 0x0e,
  144. 'personal_healthcare' => 0x0f, 'healthcare' => 0x0f, 'health' => 0x0f, 'phdc' => 0x0f,
  145. 'audio_video' => 0x10, 'av' => 0x10, 'avdc' => 0x10,
  146. 'billboard' => 0x11, 'bdc' => 0x11,
  147. 'usb_c_bridge' => 0x12, 'usbc' => 0x12, 'usbcbdc' => 0x12,
  148. 'diagnostic' => 0xdc,
  149. 'wireless_controller' => 0xe0, 'wireless' => 0xe0,
  150. 'miscellaneous' => 0xef, 'misc' => 0xef,
  151. 'application_specific' => 0xfe, 'app_specific' => 0xfe, 'app' => 0xfe,
  152. );
  153. my %packfmt = ( 1 => 'C', 2 => 'v', 4 => 'V', 8 => 'Q<' );
  154. my $utf16le = find_encoding('utf16le');
  155. sub atom($@) {
  156. my $bytes = shift @_;
  157. my @o = ();
  158. foreach my $b (@_) {
  159. my $t = ref $b;
  160. if ($t eq 'SCALAR') {
  161. # To be resolved later
  162. push(@o, {'bytes' => $bytes, 'num' => $b});
  163. } elsif ($t eq 'ARRAY') {
  164. push (@o, atom($bytes, @$b));
  165. } elsif ($t eq 'HASH') {
  166. push(@o, $b);
  167. } elsif ($t eq '') {
  168. push(@o, pack($packfmt{$bytes}, $b));
  169. }
  170. }
  171. return @o;
  172. }
  173. sub byte(@) {
  174. return atom(1,@_);
  175. }
  176. sub word(@) {
  177. return atom(2,@_);
  178. }
  179. sub dword(@) {
  180. return atom(4,@_);
  181. }
  182. sub qword(@) {
  183. return atom(8,@_);
  184. }
  185. # Generate endpoint identifiers
  186. sub ep_i($) {
  187. my($n) = @_;
  188. return byte($n|0x80);
  189. }
  190. sub ep_o($) {
  191. my($n) = @_;
  192. return byte($n|0x00);
  193. }
  194. #
  195. # General handy stuff...
  196. #
  197. # Decode an integer in C notation
  198. sub toint($) {
  199. my($i) = @_;
  200. return ($i =~ /^0/) ? oct $i : ($i =~ /^[1-9]/) ? $i+0 : undef;
  201. }
  202. # For sorting
  203. sub numeric {
  204. return $a <=> $b;
  205. }
  206. # Generate class code triplets
  207. sub usb_class($;$$) {
  208. my @cl = @_;
  209. my $lvl = \%class_codes;
  210. my $cd = '';
  211. while (scalar(@cl) < 3) {
  212. push(@cl, undef);
  213. }
  214. while (scalar(@cl)) {
  215. my $cs = shift(@cl);
  216. my $cc = defined($cs) ? toint($cs) : 0;
  217. if (!defined($cc)) {
  218. $cs = lc($cs);
  219. $cs =~ s/\P{Alnum}+/_/g;
  220. $cc = $lvl->{$cs} if (defined($lvl));
  221. $cc = $class_all_levels{$cs} unless (defined($cc));
  222. if (!defined($cc)) {
  223. print STDERR "$0: unknown class code ", join('.', @_), "\n";
  224. $err = 1;
  225. $cc = 0;
  226. }
  227. $cd .= pack('C', $cc);
  228. $lvl = $lvl->{$cc};
  229. }
  230. }
  231. return $cd;
  232. }
  233. sub datalen(@) {
  234. my $l = 0;
  235. foreach my $b (@_) {
  236. my $t = ref $b;
  237. if ($t eq 'HASH') {
  238. $l += $b->{'bytes'};
  239. } elsif ($t eq 'ARRAY') {
  240. $l += datalen(@$b);
  241. } elsif ($t eq 'SCALAR') {
  242. $l += length($$b);
  243. } elsif ($t eq '') {
  244. $l += length($b);
  245. } else {
  246. die;
  247. }
  248. }
  249. return $l;
  250. }
  251. sub makedata(@) {
  252. my $o = '';
  253. foreach my $b (@_) {
  254. my $t = ref $b;
  255. if ($t eq 'HASH') {
  256. unless (defined($b->{'raw'})) {
  257. if (defined($b->{'num'})) {
  258. $b->{'raw'} = pack($packfmt{$b->{'bytes'}}, ${$b->{'num'}});
  259. } elsif (defined($b->{'data'})) {
  260. $b->{'raw'} = makedata($b->{'data'});
  261. } else {
  262. die;
  263. }
  264. }
  265. $b->{'offs'} = length($o);
  266. $b->{'bytes'} = length($b->{'raw'});
  267. $o .= $b->{'raw'};
  268. } elsif ($t eq 'ARRAY') {
  269. $o .= makedata(@$b);
  270. } elsif ($t eq 'SCALAR') {
  271. $o .= makedata($$b);
  272. } elsif ($t eq '') {
  273. $o .= $b;
  274. } else {
  275. die;
  276. }
  277. }
  278. return $o;
  279. }
  280. # USB descriptor set
  281. my $u_self = { 'children' => \(my $children = 0), clist => [] };
  282. sub usb_dset(&) {
  283. my($contents) = @_;
  284. my $parent = $u_self;
  285. my $children = 0;
  286. my $index = ++${$u_self->{'children'}};
  287. my $ds = { 'type' => 'dset',
  288. 'parent' => $parent,
  289. 'data' => undef,
  290. 'bytes' => undef,
  291. 'raw' => undef,
  292. 'children' => \$children,
  293. 'index' => \$index,
  294. 'offs' => undef,
  295. 'clist' => [] };
  296. $u_self = $ds;
  297. push(@{$parent->{'clist'}}, $ds);
  298. my @data = $contents->($ds, $parent);
  299. $ds->{'data'} = \@data;
  300. $ds->{'bytes'} = datalen(@data);
  301. $u_self = $parent;
  302. return $ds;
  303. }
  304. sub usb_totallen(;$) {
  305. my($r) = @_;
  306. $r = $u_self unless(defined($r));
  307. return word(\$r->{'bytes'});
  308. }
  309. sub usb_index(;$) {
  310. my($r) = @_;
  311. $r = $u_self unless(defined($r));
  312. return byte($r->{'index'});
  313. }
  314. sub usb_peers(;$) {
  315. my($r) = @_;
  316. $r = $u_self unless(defined($r));
  317. return byte($r->{'parent'}{'children'});
  318. }
  319. sub usb_children(;$) {
  320. my($r) = @_;
  321. $r = $u_self unless(defined($r));
  322. return byte($r->{'children'});
  323. }
  324. # USB descriptor
  325. sub usb_desc($@) {
  326. my($typestr, @data) = @_;
  327. my($type,$subtype) = split(/\./, $typestr, 2);
  328. my $tn;
  329. my $sn;
  330. my $hdr;
  331. my $dlen = datalen(@data);
  332. $tn = toint($type);
  333. $tn = $DT{lc($type)} unless (defined($tn));
  334. die "$0: unknown descriptor type: $typestr\n" unless (defined($tn));
  335. if (defined($subtype)) {
  336. $sn = toint($subtype);
  337. $sn = $DS{$tn}->{$subtype} unless (defined($sn));
  338. die "$0: unknown descriptor type: $typestr\n" unless (defined($sn));
  339. $dlen += 3;
  340. $hdr = pack('CCC', $dlen, $tn, $sn);
  341. } else {
  342. $dlen += 2;
  343. $hdr = pack('CC', $dlen, $tn);
  344. }
  345. return { 'type' => 'descriptor',
  346. 'data' => [$hdr, @data],
  347. 'bytes' => $dlen };
  348. }
  349. # Device top level
  350. my $device_dset;
  351. sub usb_device(&) {
  352. my($contents) = @_;
  353. $device_dset = usb_dset(\&$contents);
  354. }
  355. my @langlist;
  356. my %lang;
  357. my @lang_mask = (0xffff, 0x03ff, 0); # Masks for language codes
  358. my $stringdata;
  359. my %stringoffs; # Pointer into stringdata
  360. # Reserved string descriptor numbers
  361. my $strdesc_empty = 0; # 0 = reserved for all null strings
  362. my $strdesc_serial = 1; # 1 = reserved for serial number (see below)
  363. my $strdesc_msft = 0xee;
  364. my %special_strings = ($strdesc_serial => undef,
  365. $strdesc_msft => undef);
  366. # The index of string descriptors are sets of descriptors which
  367. # match for ALL languages so they can be given the same index.
  368. my %strdesci = ('' => $strdesc_empty);
  369. my @strdescs = ('');
  370. # Register a string into the string table and return a descriptor index byte.
  371. # Input should be a hash.
  372. sub usb_string(%) {
  373. my(%strh) = @_;
  374. my $descval = '';
  375. my %txts;
  376. my $found = 0;
  377. foreach my $l (keys(%strh)) {
  378. my $str = $strh{$l};
  379. my $co = langid($l);
  380. next unless (defined($co));
  381. foreach my $m (@lang_mask) {
  382. my $coi = $co & $m;
  383. next unless (defined($lang{$coi}));
  384. next if (defined($txts{$coi}));
  385. $txts{$coi} = $str;
  386. $found++;
  387. }
  388. }
  389. return pack('C', 0) unless ($found);
  390. foreach my $co (@langlist) {
  391. my $txt;
  392. foreach my $m (@lang_mask) {
  393. last if (defined($txt = $txts{$co & $m}));
  394. }
  395. my $utf16str = $utf16le->encode($txt, Encode::FB_WARN);
  396. unless (defined($stringoffs{$utf16str})) {
  397. $stringoffs{$utf16str} = length($stringdata);
  398. $stringdata .= pack('CC', length($utf16str)+2, $DT{string});
  399. $stringdata .= $utf16str;
  400. }
  401. $descval .= pack('v', $stringoffs{$utf16str});
  402. }
  403. my $descindex = $strdesci{$descval};
  404. unless (defined($descindex)) {
  405. $descindex = scalar(@strdescs) - 1;
  406. while (exists($special_strings{++$descindex})) {
  407. push(@strdescs, undef); # Skip reserved index
  408. }
  409. $strdesci{$descval} = $descindex;
  410. push(@strdescs, $descval);
  411. }
  412. return pack('C', $descindex);
  413. }
  414. #
  415. # Special string descriptors. Currently supports the serial number,
  416. # but could also be used for the M$ special 0xEE string descriptor
  417. # with a bit more infrastructure. Noteworthy is that the serial number
  418. # descriptor is quite likely to need to be patched in the field or
  419. # at runtime. Thus, these special string descriptors come at the
  420. # very beginning of the data, in index order.
  421. #
  422. # Language codes are not supported for special strings.
  423. #
  424. # Usage: usb_special_string(index,'default',length)
  425. #
  426. sub usb_special_string($@) {
  427. my($ix,$dft,$len) = @_;
  428. if (defined($dft) || $len || !defined($special_strings{$ix})) {
  429. $dft = $utf16le->encode($dft, Encode::FB_WARN);
  430. my $dchar = length($dft) >> 1;
  431. if (!$len) {
  432. $len = $dchar;
  433. } elsif ($len < $dchar) {
  434. $dft = substr($dft, 0, $len << 1);
  435. } elsif ($len > $dchar) {
  436. # Pad with spaces
  437. $dft .= $utf16le->encode(' ' x ($len - $dchar));
  438. }
  439. $special_strings{$ix} = pack('CC', ($len << 1)+2, $DT{string}).$dft;
  440. }
  441. return pack('C', $ix);
  442. }
  443. sub usb_serial($;$) {
  444. return usb_special_string($strdesc_serial, @_);
  445. }
  446. #
  447. # Register the possible languages
  448. #
  449. sub usb_languages(@) {
  450. my @langs = @_;
  451. # There are two differences between keys(%lang) and @langlist: the
  452. # former lists all possible language codes for which a lookup is
  453. # possible in no specific order, whereas @langlist is exactly the
  454. # items wanted in order in string descriptor zero.
  455. #
  456. # In other words, %lang is a reverse mapping of langlist, *plus*
  457. # permissible aliases.
  458. %lang = ();
  459. @langlist = ();
  460. foreach my $l (@langs) {
  461. my $co = langid($l);
  462. if (defined($co) && !defined($lang{$co})) {
  463. my $nlang = scalar(@langlist);
  464. push(@langlist, $co);
  465. # Defaults for partial languages?
  466. foreach my $m (@lang_mask) {
  467. if (!defined($lang{$co & $m})) {
  468. $lang{$co & $m} = $nlang;
  469. }
  470. }
  471. }
  472. }
  473. if (!scalar(@langlist)) {
  474. $stringdata = '';
  475. } else {
  476. $stringdata = pack("CCv*", scalar(@langlist)*2 + 2,
  477. $DT{string}, @langlist);
  478. }
  479. }
  480. my $descriptor_data;
  481. my @descriptor_ptrs;
  482. sub generate_data()
  483. {
  484. my $data = '';
  485. my @ptrs = ();
  486. # Special string descriptors first
  487. foreach my $ssn (sort numeric keys(%special_strings)) {
  488. my $ss = $special_strings{$ssn};
  489. next unless defined($ss);
  490. push(@ptrs, {'type' => $DT{string}, 'dindex' => $ssn,
  491. 'offs' => length($data),
  492. 'len' => length($ss)});
  493. $data .= $ss;
  494. }
  495. # Device and configuration descriptors
  496. my $dev_offs = length($data);
  497. $data .= makedata($device_dset);
  498. foreach my $dc (@{$device_dset->{data}}) {
  499. my $offs = $dev_offs + $dc->{offs};
  500. # Length and type of the *first* descriptor in a set
  501. my($flen,$ftype) = unpack('CC', substr($data, $offs, 2));
  502. my $dindex = $dc->{index};
  503. $dindex = $$dindex while (ref $dindex eq 'SCALAR');
  504. push(@ptrs, {'type' => $ftype, 'dindex' => $dindex,
  505. 'offs' => $offs, 'len' => $dc->{bytes}});
  506. }
  507. my $string_offs = length($data);
  508. # 'Regular' string descriptors
  509. push(@ptrs, {'type' => $DT{string}, 'dindex' => 0,
  510. 'offs' => $string_offs,
  511. 'len' => unpack('C', substr($stringdata, 0, 1))});
  512. for (my $i = 1; $i < scalar(@strdescs); $i++) {
  513. my @sofs = unpack('v*', $strdescs[$i]);
  514. for (my $j = 0; $j < scalar(@sofs); $j++) {
  515. my $co = $langlist[$j];
  516. my $of = $sofs[$j];
  517. my $m = $lang_mask[0];
  518. # Widen the mask as much as possible
  519. foreach my $mx (@lang_mask) {
  520. last unless ($lang{$co & $mx} == $lang{$co & $m});
  521. $m = $mx;
  522. }
  523. push(@ptrs, {'type' => $DT{string}, 'dindex' => $i,
  524. 'windex' => $co & $m,
  525. 'wimask' => $m,
  526. 'offs' => $string_offs + $of,
  527. 'len' => unpack('C', substr($stringdata,$of,1))});
  528. }
  529. }
  530. $data .= $stringdata;
  531. $descriptor_data = $data;
  532. @descriptor_ptrs = @ptrs;
  533. return $descriptor_data;
  534. }
  535. # Output a number and a mask as a Verilog case with ? as needed
  536. sub v_case($$$) {
  537. my($b,$v,$m) = @_;
  538. my $o = sprintf("%d'b", $b);
  539. for (my $i = $b-1; $i >= 0; $i--) {
  540. my $ix = 1 << $i;
  541. $o .= ($m & $ix) ? (($v & $ix) ? '1' : '0') : '?';
  542. $o .= '_' if ($i > 0 && ($i & 7) == 0);
  543. }
  544. return $o;
  545. }
  546. # Output a Verilog module
  547. sub output_verilog($) {
  548. my($out) = @_;
  549. my $bytes = length($descriptor_data);
  550. my $abits = 1;
  551. while ((1 << $abits) < $bytes) {
  552. $abits++;
  553. }
  554. my $amax = $abits-1;
  555. my $bmax = (1 << $abits)-1;
  556. my $afmt = sprintf("%d'h%%0%dx", $abits, ($abits+3) >> 2);
  557. my $abad = sprintf("%d'h", $abits) . ('x' x (($abits+3) >> 2));
  558. my $bfmt = "8'h%02x";
  559. my $bbad = "8'hxx";
  560. print $out <<"EOF";
  561. /*
  562. * Call it a ROM even through it can be optionally written to.
  563. * Trust the tools to figure out if we don't need part of the whole thing.
  564. */
  565. module ${module_name}_rom (
  566. input clk,
  567. input [$amax:0] usb_addr,
  568. output [7:0] usb_rdata,
  569. input [$amax:0] cpu_addr,
  570. output [7:0] cpu_rdata,
  571. input [7:0] cpu_wdata,
  572. input cpu_wren
  573. );
  574. reg [7:0] rom [0:$bmax];
  575. initial begin
  576. EOF
  577. my $addr = 0;
  578. foreach my $b (unpack("C*", $descriptor_data)) {
  579. printf $out "\t\trom[$afmt] = $bfmt;\n", $addr++, $b;
  580. }
  581. while ($addr < $bytes) {
  582. printf $out "\t\trom[$afmt] = $bbad;\n", $addr++;
  583. }
  584. print $out <<"EOF";
  585. end
  586. always \@(posedge clk) begin
  587. usb_rdata <= rom[usb_addr];
  588. cpu_rdata <= rom[cpu_addr];
  589. if (cpu_wren)
  590. rom[cpu_addr] <= cpu_wdata;
  591. end
  592. endmodule
  593. module ${module_name}_index (
  594. input [7:0] dtype,
  595. input [7:0] dindex,
  596. input [15:0] windex,
  597. output reg [$amax:0] addr,
  598. output reg [$amax:0] len
  599. );
  600. always \@(\*) priority casez ({windex,dindex,dtype})
  601. EOF
  602. my @cases;
  603. foreach my $d ({ 'len' => 0 }, @descriptor_ptrs) {
  604. my $id = 0;
  605. my $mask = 0;
  606. if (defined($d->{type})) {
  607. $id |= $d->{type};
  608. $mask |= 0xff;
  609. }
  610. if (defined($d->{dindex})) {
  611. $id |= $d->{dindex} << 8;
  612. $mask |= 0xff << 8;
  613. }
  614. if (defined($d->{windex})) {
  615. $id |= $d->{windex} << 16;
  616. $mask |= $d->{wimask} << 16;
  617. }
  618. my $cs = "\t\t".v_case(32,$id,$mask).': {addr,len} = ';
  619. if ($d->{len}) {
  620. $cs .= sprintf("{$afmt,$afmt};\n", $d->{offs},$d->{len});
  621. } else {
  622. $cs .= sprintf("{$abad,$afmt};\n", $d->{len});
  623. }
  624. push(@cases, $cs);
  625. }
  626. # This relies on ? sorting after digits
  627. print $out sort @cases;
  628. print $out <<'EOF';
  629. endcase
  630. endmodule
  631. EOF
  632. }
  633. if (scalar(@ARGV) < 3) {
  634. die "Usage: $0 {bin|v} input output\n";
  635. }
  636. my @ofs = File::Spec->splitpath($outfile);
  637. $module_name = $ofs[2];
  638. $module_name =~ s/\..*$//;
  639. $module_name =~ s/\P{Alnum}+/_/g;
  640. unless (defined(do File::Spec->rel2abs($infile))) {
  641. die "$0: $infile: $!\n"
  642. }
  643. generate_data();
  644. exit ($err) if ($err);
  645. open(my $out, '>', $outfile)
  646. or die "$0: $outfile: $!\n";
  647. if ($mode eq 'bin') {
  648. print $out $descriptor_data;
  649. } elsif ($mode eq 'v') {
  650. output_verilog($out);
  651. } else {
  652. die "$0: $mode: unknown operations mode\n";
  653. }
  654. close($out);
  655. if ($err) {
  656. remove($outfile);
  657. }
  658. exit($err);