123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832 |
- #!/usr/bin/perl
- #
- # Simple tool for generating a USB desciptor table or ROM/RAM
- #
- use integer;
- use strict;
- use File::Spec;
- use Encode;
- require 'langid.ph';
- # Some forward declarations
- my($mode, $infile, $outfile) = @ARGV;
- my $module_name;
- my $err = 0;
- # Descriptor types. This follows the document in which they were specified;
- # some USB documents specify in hex and others in decimal...
- our %DT = (
- 'device' => 1,
- 'configuration' => 2, 'config' => 2, 'conf' => 2,
- 'string' => 3, 'str' => '3',
- 'interface' => 4, 'if' => '4',
- 'endpoint' => 5, 'ep' => 5,
- 'device_qualifier' => 6, 'dq' => 6,
- 'other_speed_configuration' => 7, 'otherspeed' => 7, 'osc' => 7,
- 'interface_power' => 8, 'ifpwr' => 8,
- 'otg' => 9,
- 'debug' => 10,
- 'interface_association' => 11, 'iad' => 11,
- 'bos' => 15, 'binary_object_storage' => 15,
- 'device_capability' => 16, 'devcap' => 16,
- 'cs_interface' => 0x24, 'cs_if' => 0x24,
- 'cs_endpoint' => 0x25, 'cs_ep' => 0x25,
- 'superspeed_usb_endpoint_companion' => 48, 'usb_ep_companion' => 48,
- 'superspeedplus_isochronous_endpoint_companion' => 49, 'iso_ep_compation' => 49,
- );
- # Descriptor subtypes, where applicable
- our %DS = (
- # Under CS_INTERFACE
- 0x24 => {
- 'header' => 0x00,
- 'call' => 0x01, 'call_management' => 0x01,
- 'abstract_control' => 0x02, 'acm' => 0x02,
- 'direct_line' => 0x03,
- 'ringer' => 0x04,
- 'line_state' => 0x05,
- 'union' => 0x06,
- 'country' => 0x07,
- 'op_mode' => 0x08,
- 'terminal' => 0x09,
- 'net_channel' => 0x0a,
- 'protocol_unit' => 0x0b,
- 'extension_unit' => 0x0c,
- 'multi_channel' => 0x0d,
- 'capi' => 0x0e,
- 'ethernet' => 0x0f,
- 'atm' => 0x10,
- 'whcm' => 0x11, 'wireless_handset' => 0x11,
- 'mobile_line' => 0x12, 'mobile_direct_line' => 0x12,
- 'mdlm_detail' => 0x13, 'mdlm' => 0x13,
- 'device_management' => 0x14, 'device' => 0x14, 'mgmt' => 0x14,
- 'command_set' => 0x16,
- 'command_set_detail' => 0x17,
- 'telephone_control' => 0x18, 'tcm' => 0x18, 'phone' => 0x18,
- 'obex_service_identifier' => 0x19, 'obex' => 0x19
- }
- );
- #
- # Class, subclass, and protocol codes. Map a string to a number, then
- # use that number as an index to descend, if that entry exists. 0 is
- # the default; for some classes the subclass code is unused and so is
- # set to 0; no string to look up.
- #
- # Numbers below the class level are massively incomplete, feel free to add.
- #
- # Applies to all levels
- my %class_all_levels = (
- undef => 0x00, 'undef' => 0x00, '-' => 0x00,
- 'none' => 0x00, 'default' => 0x00,
- 'vendor_specific' => 0xff, 'vendor' => 0xff,
- );
- my $cdc_pstn_protocols = {
- 'v250' => 0x01, 'at' => 0x01, 'v25ter' => 0x01,
- 'pcca101' => 0x02,
- 'pcca101o' => 0x03, 'pcca' => 0x03,
- 'gsm707' => 0x04, 'gsm' => 0x04,
- '3gpp2707' => 0x05, '3gpp' => 0x05, '3g' => 0x05,
- 'ca00170' => 0x06, 'tia_cdma' => 0x06, 'cdma' => 0x06
- };
- my %class_codes = (
- 'multi' => 0x00, # Real class code in interface descriptors
- 'audio' => 0x01,
- 'cdc' => 0x02, 'communications' => 0x02,
- 0x02 => {
- 'dlcm' => 0x01, 'direct_line_control' => 0x01,
- 'acm' => 0x02, 'abstract_control' => 0x02,
- 0x02 => $cdc_pstn_protocols,
- 'tcm' => 0x03, 'telephone_control' => 0x03,
- 0x03 => $cdc_pstn_protocols,
- 'mccm' => 0x04, 'multi_channel_control' => 0x04,
- 0x04 => $cdc_pstn_protocols,
- 'ccm' => 0x05, 'capi' => 0x05, 'capi_control' => 0x05,
- 0x05 => $cdc_pstn_protocols,
- 'ecm' => 0x06, 'ethernet_control' => 0x06,
- 0x06 => $cdc_pstn_protocols,
- 'atm' => 0x07, 'ancm' => 0x07, 'atm_networking_control' => 0x07,
- 'whcm' => 0x08, 'wireless_handset_control' => 0x08,
- 0x08 => $cdc_pstn_protocols,
- 'device_management' => 0x09, 'mgmt' => 0x09, 'device' => 0x09,
- 'mdlm' => 0x0a, 'mobile_direct_line' => 0x0a,
- 'obex' => 0x0b,
- 'eem' => 0x0c, 'ethernet' => 0x0c, 'ethernet_emulation' => 0x0c,
- 0x0c => {
- 'ethernet' => 0x07, 'eem' => 0x07
- },
- 'ncm' => 0x0d, 'net' => 0x0d, 'network_control' => 0x0d,
- },
- 'hid' => 0x03,
- 'physical' => 0x05,
- 'imaging' => 0x06, 'photo' => 0x06, 'idc' => 0x06,
- 'printer' => 0x07,
- 'mass_storage' => 0x08, 'storage' => 0x08, 'disk' => 0x08,
- 'hub' => 0x09,
- 'cdc_data' => 0x0a, 'data' => 0x0a,
- 0x0a => {
- 0 => {
- 'ntb' => 0x01, 'network_transfer_block' => 0x01,
- 'isdn_bri' => 0x30, 'isdn' => 0x30,
- 'hdlc' => 0x31,
- 'transparent' => 0x32,
- 'q921_management' => 0x50, 'q921m' => 0x50,
- 'q921_datalink' => 0x51, 'q921' => 0x51,
- 'q921_tei_mux' => 0x52, 'q921tm' => 0x52,
- 'v42bis' => 0x90,
- 'euro_isdn' => 0x91, 'q931' => 0x91,
- 'v120' => 0x92, 'isdn_v24' => 0x92,
- 'capi' => 0x93,
- 'host' => 0xfd,
- 'external' => 0xfe,
- 'vendor' => 0xff
- },
- },
- 'smart_card' => 0x0b, 'smartcard' => 0x0b, 'scdc' => 0x0b,
- 'content_security' => 0x0d, 'drm' => 0x0d, 'csdc' => 0x0d,
- 'video' => 0x0e, 'vdc' => 0x0e,
- 'personal_healthcare' => 0x0f, 'healthcare' => 0x0f, 'health' => 0x0f, 'phdc' => 0x0f,
- 'audio_video' => 0x10, 'av' => 0x10, 'avdc' => 0x10,
- 'billboard' => 0x11, 'bdc' => 0x11,
- 'usb_c_bridge' => 0x12, 'usbc' => 0x12, 'usbcbdc' => 0x12,
- 'diagnostic' => 0xdc,
- 'wireless_controller' => 0xe0, 'wireless' => 0xe0,
- 'miscellaneous' => 0xef, 'misc' => 0xef,
- 'application_specific' => 0xfe, 'app_specific' => 0xfe, 'app' => 0xfe,
- );
- my %packfmt = ( 1 => 'C', 2 => 'v', 4 => 'V', 8 => 'Q<' );
- my $utf16le = find_encoding('utf16le');
- sub atom($@) {
- my($bytes,$b,$adj) = @_;
- my @o = ();
- $adj = toint($adj);
-
- my $t;
- while (($t = ref $b) eq 'REF') {
- $b = $$b;
- }
- if ($t eq 'SCALAR') {
- # To be resolved later
- push(@o, {'bytes' => $bytes, 'num' => $b, 'adj' => $adj });
- } elsif ($t eq '') {
- push(@o, pack($packfmt{$bytes}, $b+$adj));
- } else {
- push(@o, {'bytes' => $bytes, 'data' => $b});
- }
- return [@o];
- }
- sub byte(@) {
- return atom(1,@_);
- }
- sub word(@) {
- return atom(2,@_);
- }
- sub dword(@) {
- return atom(4,@_);
- }
- sub qword(@) {
- return atom(8,@_);
- }
- # Generate endpoint identifiers
- sub ep_i($) {
- my($n) = @_;
- return byte($n,0x80);
- }
- sub ep_o($) {
- my($n) = @_;
- return byte($n,0x00);
- }
- #
- # General handy stuff...
- #
- # Decode an integer in C notation
- sub toint($) {
- my($i) = @_;
- return ($i =~ /^0/) ? oct $i : ($i =~ /^[1-9]/) ? $i+0 : undef;
- }
- # For sorting
- sub numeric {
- return $a <=> $b;
- }
- # Generate class code triplets
- sub usb_class($;$$) {
- my @cl = @_;
- my $lvl = \%class_codes;
- my $cd = '';
- for (my $i = 0; $i < 3; $i++) {
- my $cs = shift(@cl);
- my $cc = defined($cs) ? toint($cs) : 0;
- if (!defined($cc)) {
- $cs = lc($cs);
- $cs =~ s/\P{Alnum}+/_/g;
- $cc = $lvl->{$cs} if (defined($lvl));
- $cc = $class_all_levels{$cs} unless (defined($cc));
- if (!defined($cc)) {
- print STDERR "$0: unknown class code ", join('.', @_), "\n";
- $err = 1;
- $cc = 0;
- }
- }
- $cd .= pack('C', $cc);
- $lvl = $lvl->{$cc};
- }
- return $cd;
- }
- sub datalen(@) {
- my $l = 0;
- foreach my $e (@_) {
- my $b = $e;
- my $t;
- while (($t = ref $b) eq 'REF') {
- $b = $$b;
- }
- if ($t eq 'HASH') {
- $l += $b->{'bytes'};
- } elsif ($t eq 'ARRAY') {
- $l += datalen(@$b);
- } elsif ($t eq 'SCALAR') {
- $l += length($$b);
- } elsif ($t eq '') {
- $l += length($b);
- } else {
- die;
- }
- }
- return $l;
- }
- sub makedata(@) {
- my $o = '';
- foreach my $e (@_) {
- my $b = $e;
- my $t;
- while (($t = ref $b) eq 'REF') {
- $b = $$b;
- }
- if ($t eq 'HASH') {
- unless (defined($b->{'raw'})) {
- if (defined(my $n = $b->{'num'})) {
- my $tt;
- my $adj = $b->{'adj'};
- while (($tt = ref $n) eq 'REF') {
- $n = $$n;
- }
- if ($tt eq 'SCALAR') {
- $b->{'raw'} = pack($packfmt{$b->{'bytes'}}, $$n+$adj);
- } else {
- $b->{'raw'} = makedata($n);
- }
- } elsif (defined($b->{'data'})) {
- $b->{'raw'} = makedata($b->{'data'});
- } else {
- die;
- }
- }
- $b->{'offs'} = length($o);
- $b->{'bytes'} = length($b->{'raw'});
- $o .= $b->{'raw'};
- } elsif ($t eq 'ARRAY') {
- $o .= makedata(@$b);
- } elsif ($t eq 'SCALAR') {
- $o .= makedata($$b);
- } elsif ($t eq '') {
- $o .= $b;
- } else {
- die;
- }
- }
- return $o;
- }
- # USB descriptor set
- my $u_self = { 'children' => \(my $children = 0), clist => [] };
- sub usb_dset(&) {
- my($contents) = @_;
- my $parent = $u_self;
- my $children = 0;
- my $index = ${$u_self->{'children'}}++;
- my $ds = { 'type' => 'dset',
- 'parent' => $parent,
- 'data' => undef,
- 'bytes' => undef,
- 'raw' => undef,
- 'children' => \$children,
- 'index' => \$index,
- 'offs' => undef,
- 'clist' => [] };
- $u_self = $ds;
- push(@{$parent->{'clist'}}, $ds);
- my @data = $contents->($ds, $parent);
- $ds->{'data'} = \@data;
- $ds->{'bytes'} = datalen(@data);
- $u_self = $parent;
- return $ds;
- }
- sub usb_totallen(;$) {
- my($r) = @_;
- $r = $u_self unless(defined($r));
- return \$r->{'bytes'};
- }
- sub usb_index(;$) {
- my($r) = @_;
- $r = $u_self unless(defined($r));
- return $r->{'index'};
- }
- sub usb_peers(;$) {
- my($r) = @_;
- $r = $u_self unless(defined($r));
- return $r->{'parent'}{'children'};
- }
- sub usb_children(;$) {
- my($r) = @_;
- $r = $u_self unless(defined($r));
- return $r->{'children'};
- }
- # USB descriptor
- sub usb_desc($@) {
- my($typestr, @data) = @_;
- my($type,$subtype) = split(/\./, $typestr, 2);
- my $tn;
- my $sn;
- my $hdr;
- my $dlen = datalen(@data);
- $tn = toint($type);
- $tn = $DT{lc($type)} unless (defined($tn));
- die "$0: unknown descriptor type: $typestr\n" unless (defined($tn));
- if (defined($subtype)) {
- $sn = toint($subtype);
- $sn = $DS{$tn}->{$subtype} unless (defined($sn));
- die "$0: unknown descriptor type: $typestr\n" unless (defined($sn));
- $dlen += 3;
- $hdr = pack('CCC', $dlen, $tn, $sn);
- } else {
- $dlen += 2;
- $hdr = pack('CC', $dlen, $tn);
- }
- return { 'type' => 'descriptor',
- 'data' => [$hdr, @data],
- 'bytes' => $dlen };
- }
- # Device top level
- my $device_dset;
- sub usb_device(&) {
- my($contents) = @_;
- $device_dset = usb_dset(\&$contents);
- }
- # Additional USB data
- my $additional_dset;
- sub usb_additional_data(&) {
- my($contents) = @_;
- $additional_dset = usb_dset(\&$contents);
- }
- my @langlist;
- my %lang;
- my @lang_mask = (0xffff, 0x03ff, 0); # Masks for language codes
- my $stringdata;
- my %stringoffs; # Pointer into stringdata
- # Reserved string descriptor numbers
- my $strdesc_lang = 0; # 0 = reserved for language descriptors
- my $strdesc_serial = 1; # 1 = reserved for serial number (see below)
- my $strdesc_msft = 0xee;
- my %special_strings = ($strdesc_lang => undef,
- $strdesc_serial => undef,
- $strdesc_msft => undef);
- # The index of string descriptors are sets of descriptors which
- # match for ALL languages so they can be given the same index.
- my %strdesci = ();
- my @strdescs = ();
- # Register a string into the string table and return a descriptor index byte.
- # Input should be a hash.
- sub usb_string(%) {
- my(%strh) = @_;
- my $descval = '';
- my %txts;
- my $found = 0;
- if (!%strh) {
- %strh = ( '' => '' ); # Null string descriptor
- }
-
- foreach my $l (keys(%strh)) {
- my $str = $strh{$l};
- my $co = langid($l);
- next unless (defined($co));
- foreach my $m (@lang_mask) {
- my $coi = $co & $m;
- next unless (defined($lang{$coi}));
- next if (defined($txts{$coi}));
- $txts{$coi} = $str;
- $found++;
- }
- }
- return pack('C', 0) unless ($found);
- foreach my $co (@langlist) {
- my $txt;
- foreach my $m (@lang_mask) {
- last if (defined($txt = $txts{$co & $m}));
- }
- my $utf16str = $utf16le->encode($txt, Encode::FB_WARN);
- unless (defined($stringoffs{$utf16str})) {
- $stringoffs{$utf16str} = length($stringdata);
- $stringdata .= pack('CC', length($utf16str)+2, $DT{string});
- $stringdata .= $utf16str;
- }
- $descval .= pack('v', $stringoffs{$utf16str});
- }
- my $descindex = $strdesci{$descval};
- unless (defined($descindex)) {
- $descindex = scalar(@strdescs) - 1;
- while (exists($special_strings{++$descindex})) {
- push(@strdescs, undef); # Skip reserved index
- }
- $strdesci{$descval} = $descindex;
- push(@strdescs, $descval);
- }
- return pack('C', $descindex);
- }
- #
- # Special string descriptors. Currently supports the serial number,
- # but could also be used for the M$ special 0xEE string descriptor
- # with a bit more infrastructure. Noteworthy is that the serial number
- # descriptor is quite likely to need to be patched in the field or
- # at runtime. Thus, these special string descriptors come at the
- # very beginning of the data, in index order.
- #
- # Language codes are not supported for special strings.
- #
- # Usage: usb_special_string(index,'default',length)
- #
- sub usb_special_string($@) {
- my($ix,$dft,$len) = @_;
- if (defined($dft) || $len || !defined($special_strings{$ix})) {
- $dft = $utf16le->encode($dft, Encode::FB_WARN);
- my $dchar = length($dft) >> 1;
- if (!$len) {
- $len = $dchar;
- } elsif ($len < $dchar) {
- $dft = substr($dft, 0, $len << 1);
- } elsif ($len > $dchar) {
- # Pad with spaces
- $dft .= $utf16le->encode(' ' x ($len - $dchar));
- }
- $special_strings{$ix} = pack('CC', ($len << 1)+2, $DT{string}).$dft;
- }
- return pack('C', $ix);
- }
- sub usb_serial($;$) {
- return usb_special_string($strdesc_serial, @_);
- }
- #
- # Register the possible languages
- #
- sub usb_languages(@) {
- my @langs = @_;
- # There are two differences between keys(%lang) and @langlist: the
- # former lists all possible language codes for which a lookup is
- # possible in no specific order, whereas @langlist is exactly the
- # items wanted in order in string descriptor zero.
- #
- # In other words, %lang is a reverse mapping of langlist, *plus*
- # permissible aliases.
- %lang = ();
- @langlist = ();
- foreach my $l (@langs) {
- my $co = langid($l);
- if (defined($co) && !defined($lang{$co})) {
- my $nlang = scalar(@langlist);
- push(@langlist, $co);
- # Defaults for partial languages?
- foreach my $m (@lang_mask) {
- if (!defined($lang{$co & $m})) {
- $lang{$co & $m} = $nlang;
- }
- }
- }
- }
- if (!scalar(@langlist)) {
- $stringdata = '';
- } else {
- $stringdata = pack("CCv*", scalar(@langlist)*2 + 2,
- $DT{string}, @langlist);
- }
- }
- my $descriptor_data;
- my @descriptor_ptrs;
- my $additional_offs;
- my $additional_len;
- sub generate_data()
- {
- my $data = '';
- my @ptrs = ();
- # Special string descriptors first
- foreach my $ssn (sort numeric keys(%special_strings)) {
- my $ss = $special_strings{$ssn};
- next unless defined($ss);
- push(@ptrs, {'type' => $DT{string}, 'dindex' => $ssn,
- 'offs' => length($data),
- 'len' => length($ss)});
- $data .= $ss;
- }
- # Device and configuration descriptors
- my $dev_offs = length($data);
- $data .= makedata($device_dset);
- foreach my $dc (@{$device_dset->{data}}) {
- my $offs = $dev_offs + $dc->{offs};
- # Length and type of the *first* descriptor in a set
- my($flen,$ftype) = unpack('CC', substr($data, $offs, 2));
- my $dindex = $dc->{index};
- $dindex = $$dindex while (ref $dindex eq 'SCALAR');
- push(@ptrs, {'type' => $ftype, 'dindex' => $dindex,
- 'offs' => $offs, 'len' => $dc->{bytes}});
- }
- my $string_offs = length($data);
- # 'Regular' string descriptors
- push(@ptrs, {'type' => $DT{string}, 'dindex' => 0,
- 'offs' => $string_offs,
- 'len' => unpack('C', substr($stringdata, 0, 1))});
- for (my $i = 1; $i < scalar(@strdescs); $i++) {
- my @sofs = unpack('v*', $strdescs[$i]);
- for (my $j = 0; $j < scalar(@sofs); $j++) {
- my $co = $langlist[$j];
- my $of = $sofs[$j];
- my $m = $lang_mask[0];
- # Widen the mask as much as possible
- foreach my $mx (@lang_mask) {
- last unless ($lang{$co & $mx} == $lang{$co & $m});
- $m = $mx;
- }
- push(@ptrs, {'type' => $DT{string}, 'dindex' => $i,
- 'windex' => $co & $m,
- 'wimask' => $m,
- 'offs' => $string_offs + $of,
- 'len' => unpack('C', substr($stringdata,$of,1))});
- }
- }
- $data .= $stringdata;
- $additional_offs = length($data);
- if (defined($additional_dset)) {
- $data .= makedata($additional_dset);
- }
- $additional_len = length($data) - $additional_offs;
-
- $descriptor_data = $data;
- @descriptor_ptrs = @ptrs;
- return $descriptor_data;
- }
- # Output a number and a mask as a Verilog case with ? as needed
- sub v_case($$$) {
- my($b,$v,$m) = @_;
- my $o = sprintf("%d'b", $b);
- for (my $i = $b-1; $i >= 0; $i--) {
- my $ix = 1 << $i;
- $o .= ($m & $ix) ? (($v & $ix) ? '1' : '0') : '?';
- $o .= '_' if ($i > 0 && ($i & 7) == 0);
- }
- return $o;
- }
- # Output a Verilog module
- sub output_verilog($) {
- my($out) = @_;
- my $bytes = length($descriptor_data);
- my $abits = 1;
- while ((1 << $abits) < $bytes) {
- $abits++;
- }
- my $amax = $abits-1;
- my $bmax = (1 << $abits)-1;
- my $afmt = sprintf("%d'h%%0%dx", $abits, ($abits+3) >> 2);
- my $abad = sprintf("%d'h", $abits) . ('x' x (($abits+3) >> 2));
- my $bfmt = "8'h%02x";
- my $bbad = "8'hxx";
- print $out <<"EOF";
- /*
- * Call it a ROM even through it can be optionally written to.
- * Trust the tools to figure out if we don't need part of the whole thing.
- */
- module ${module_name}_rom (
- input clk,
- input [$amax:0] usb_addr,
- output [7:0] usb_rdata,
- input cpu_clk,
- input [$amax:0] cpu_addr,
- output [7:0] cpu_rdata,
- input [7:0] cpu_wdata,
- input cpu_wren
- );
- reg [7:0] rom [0:$bmax];
- initial begin
- EOF
- my $addr = 0;
- foreach my $b (unpack("C*", $descriptor_data)) {
- printf $out "\t\trom[$afmt] = $bfmt;\n", $addr++, $b;
- }
- while ($addr < $bytes) {
- printf $out "\t\trom[$afmt] = $bbad;\n", $addr++;
- }
- print $out <<"EOF";
- end
- always \@(posedge clk) begin
- usb_rdata <= rom[usb_addr];
- end
- always \@(posedge cpu_clk) begin
- cpu_rdata <= rom[cpu_addr];
- if (cpu_wren)
- rom[cpu_addr] <= cpu_wdata;
- end
- endmodule
- module ${module_name}_index (
- input [7:0] dtype,
- input [7:0] dindex,
- input [15:0] windex,
- input additional,
- output reg [$amax:0] addr,
- output reg [$amax:0] len
- );
- always \@(\*)
- if (additional)
- EOF
- printf $out "\t\t{addr,len} = {$afmt,$afmt};\n", $additional_offs, $additional_len;
- print $out <<"EOF";
- else priority casez ({windex,dindex,dtype})
- EOF
- my @cases;
- foreach my $d ({ 'len' => 0 }, @descriptor_ptrs) {
- my $id = 0;
- my $mask = 0;
- if (defined($d->{type})) {
- $id |= $d->{type};
- $mask |= 0xff;
- }
- if (defined($d->{dindex})) {
- $id |= $d->{dindex} << 8;
- $mask |= 0xff << 8;
- }
- if (defined($d->{windex})) {
- $id |= $d->{windex} << 16;
- $mask |= $d->{wimask} << 16;
- }
- my $cs = "\t\t".v_case(32,$id,$mask).': {addr,len} = ';
- if ($d->{len}) {
- $cs .= sprintf("{$afmt,$afmt};\n", $d->{offs},$d->{len});
- } else {
- $cs .= sprintf("{$abad,$afmt};\n", $d->{len});
- }
- push(@cases, $cs);
- }
- # This relies on ? sorting after digits
- print $out sort @cases;
- print $out <<'EOF';
- endcase
- endmodule
- EOF
- }
- if (scalar(@ARGV) < 3) {
- die "Usage: $0 {bin|v} input output\n";
- }
- my @ofs = File::Spec->splitpath($outfile);
- $module_name = $ofs[2];
- $module_name =~ s/\..*$//;
- $module_name =~ s/\P{Alnum}+/_/g;
- unless (defined(do File::Spec->rel2abs($infile))) {
- die "$0: $infile: ".(($@ ne '') ? $@ : $!)."\n";
- }
- generate_data();
- exit ($err) if ($err);
- open(my $out, '>', $outfile)
- or die "$0: $outfile: $!\n";
- if ($mode eq 'bin') {
- print $out $descriptor_data;
- } elsif ($mode eq 'v') {
- output_verilog($out);
- } else {
- die "$0: $mode: unknown operations mode\n";
- }
- close($out);
- if ($err) {
- remove($outfile);
- }
- exit($err);
|