File Coverage

blib/lib/Verilog/Readmem.pm
Criterion Covered Total %
statement 75 76 98.6
branch 52 52 100.0
condition n/a
subroutine 8 8 100.0
pod 1 5 20.0
total 136 141 96.4


line stmt bran cond sub pod time code
1             package Verilog::Readmem;
2              
3 1     1   15628 use warnings;
  1         2  
  1         32  
4 1     1   4 use strict;
  1         1  
  1         27  
5 1     1   4 use Carp;
  1         1  
  1         1148  
6              
7             require Exporter;
8             our @ISA = qw(Exporter);
9             our @EXPORT_OK = qw(parse_readmem);
10             our @EXPORT;
11              
12             our $VERSION = '0.05';
13              
14              
15             sub bin2dec {
16 8     8 0 39 return unpack("N", pack("B32", substr("0" x 32 . shift, -32)));
17             }
18              
19             sub parse_readmem {
20 30     30 1 811024 my ($arg_ref) = @_;
21 30         42 my $file;
22             my $hex_mode;
23 0         0 my $numeric;
24              
25             # Check inputs.
26 30 100       81 if (exists $arg_ref->{filename}) {
27 29         50 $file = $arg_ref->{filename};
28             }
29             else {
30 1         208 croak "Error: filename is required.\n";
31             }
32              
33 29 100       58 if (exists $arg_ref->{binary}) {
34 8 100       27 $hex_mode = ($arg_ref->{binary} eq 1) ? 0 : 1;
35             }
36             else {
37 21         28 $hex_mode = 1;
38             }
39              
40 29 100       51 if (exists $arg_ref->{string}) {
41 9 100       34 $numeric = ($arg_ref->{string} eq 1) ? 0 : 1;
42             }
43             else {
44 20         21 $numeric = 1;
45             }
46              
47             # Remove comments from input file.
48 29         54 my $lines = remove_comments($file);
49 28         9751 $lines =~ s/^\s+//m; # Remove any leading whitespace prior to split
50 28         26588 my @tokens = split /\s+/, $lines;
51              
52             # Create array-of-arrays corresponding to all address blocks.
53 28         3108 my @all_blocks;
54             my @block;
55 28         42 push @block, '0';
56 28         52 for (@tokens) {
57 131236         110410 $_ = lc;
58 131236 100       146017 if (/^@/) {
59 25         44 my $addr = check_addr($_, $numeric);
60 20 100       37 if (@block > 1) {push @all_blocks, [@block]}
  12         22  
61 20         24 @block = ();
62 20         26 push @block, $addr;
63             }
64             else {
65 131211         138564 push @block, check_data($_, $hex_mode, $numeric);
66             }
67             }
68 16 100       42 if (@block > 1) {push @all_blocks, [@block]}
  12         11271  
69              
70 16         9422 return \@all_blocks;
71             }
72              
73             sub check_data {
74             # Check for proper syntax of a data token.
75             # Return transformed data, if there are no errors.
76 131211     131211 0 104994 my ($dat, $hex_mode, $numeric) = @_;
77 131211 100       175410 if ($dat =~ /^_/) {
78 1         135 croak "Error: illegal leading underscore for data '$dat'.\n";
79             }
80 131210 100       124852 if ($numeric) { # Convert to numeric
81 131169         101667 $dat =~ s/_//g;
82 131169 100       123745 if ($hex_mode) {
83 131159 100       190219 croak "Error: unsupported characters in 2-state readmemh input '$dat'.\n" if ($dat =~ /[^\da-f]/);
84 131158 100       163290 croak "Error: Hex value exceeds 32-bits '$dat'.\n" if (length($dat) > 8);
85 131157         104306 $dat = hex $dat;
86             }
87             else {
88 10 100       128 croak "Error: unsupported characters in 2-state readmemb input '$dat'.\n" if ($dat =~ /[^01]/);
89 9 100       153 croak "Error: Binary value exceeds 32-bits '$dat'.\n" if (length($dat) > 32);
90 8         13 $dat = bin2dec($dat);
91             }
92             }
93             else { # String mode
94 41 100       50 if ($hex_mode) {
95 26 100       235 croak "Error: unsupported characters in 4-state readmemh input '$dat'.\n" if ($dat =~ /[^\da-fxz_]/);
96             }
97             else {
98 15 100       197 croak "Error: unsupported characters in 4-state readmemb input '$dat'.\n" if ($dat =~ /[^01xz_]/);
99             }
100             }
101 131204         161509 return $dat;
102             }
103              
104             sub check_addr {
105             # Check for proper syntax of an address token.
106             # Return transformed address, if there are no errors.
107 25     25 0 29 my ($addr, $numeric) = @_;
108 25         56 $addr =~ s/^@//;
109 25 100       55 return 0 unless length $addr;
110 23 100       50 if ($numeric) { # Convert to numeric
111 20         21 $addr =~ s/_//g;
112 20 100       39 return 0 unless length $addr;
113 19 100       352 croak "Error: unsupported characters in 2-state address '$addr'.\n" if ($addr =~ /[^\da-f]/);
114 16 100       129 croak "Error: Hex address exceeds 32-bits '$addr'.\n" if (length($addr) > 8);
115 15         18 $addr = hex $addr;
116             }
117             else { # String mode
118 3 100       172 croak "Error: unsupported characters in 2-state string address '$addr'.\n" if ($addr =~ /[^\da-f_]/);
119             }
120 17         23 return $addr;
121             }
122              
123             sub remove_comments {
124             # Remove C++ and Verilog comments from input file and return all
125             # lines as a string.
126             # Removes block comments (/**/) and single-line comments (//).
127              
128             # Slurp file into $lines variable.
129 29     29 0 35 my $file = shift;
130 29         90 local $/ = undef;
131 29 100       1282 open my $IN_FH, '<', $file or croak "Error: Can not open file $file: $!";
132 28         3310 my $lines = <$IN_FH>;
133 28         675 close $IN_FH;
134              
135             # 1st, insert space before all /*
136             # This handles corner case not handled by perlfaq6 regex below.
137             # If the input file contains "123/*456*/789", the perlfaq6 regex
138             # will remove the comments, but leave a single value: 123789.
139             # But, ncverilog and vcs will replace the comment with a space,
140             # leaving 2 values: 123 and 789. This is the desired behavior.
141             # Wait... before we do that, we have to account for the other
142             # corner case of "//*": this is really a single-line comment,
143             # not a multi-line comment.
144 28         894 $lines =~ s{//\*}{// \*}g;
145 28         533 $lines =~ s{/\*}{ /\*}g;
146              
147             # Use regex from perlfaq6 (C++ comments).
148 28 100       5700 $lines =~ s#/\*[^*]*\*+([^/*][^*]*\*+)*/|//[^\n]*|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^/"'\\]*)#defined $2 ? $2 : ""#gse;
  148         1278  
149              
150             # Returns all lines as a string
151 28         189 return $lines;
152             }
153              
154              
155             =head1 NAME
156              
157             Verilog::Readmem - Parse Verilog $readmemh or $readmemb text file
158              
159             =head1 VERSION
160              
161             This document refers to Verilog::Readmem version 0.05.
162              
163             =head1 SYNOPSIS
164              
165             use Verilog::Readmem qw(parse_readmem);
166              
167             # Read memory file into Array-Of-Arrays data structure:
168             my $mem_ref = parse_readmem({filename => 'memory.hex'});
169              
170             my $num_blocks = scalar @{$mem_ref};
171             print "num_blocks = $num_blocks\n";
172              
173             # It is typical to have only one data block.
174             # Sum up all data values.
175             if ($num_blocks == 1) {
176             my ($addr, @data) = @{ $mem_ref->[0] };
177             my $sum = 0;
178             for (@data) { $sum += $_ }
179             print "addr = $addr, data sum = $sum\n";
180             }
181              
182             =head1 DESCRIPTION
183              
184             The Verilog Hardware Description Language (HDL) provides a convenient
185             way to load a memory during logic simulation. The C<$readmemh()> and
186             C<$readmemb()> system tasks are used in the HDL source code to import
187             the contents of a text file into a memory variable.
188              
189             In addition to having the simulator software read in these memory files,
190             it is also useful to analyze the contents of the file outside
191             of the simulator. For example, it may be useful to derive some
192             simulation parameters from the memory file prior to running the
193             simulation. Data stored at different addresses may be combined
194             arithmetically to produce other meaningful values. In some cases,
195             it is simpler to perform these calculations outside of the simulator.
196              
197             C emulates the Verilog C<$readmemh()> and C<$readmemb()>
198             system tasks. The same memory file which is read in by the
199             simulator can also be read into a Perl program, potentially easing
200             the burden of having the HDL code perform numeric calculations
201             or string manipulations.
202              
203             =head2 Input File Syntax
204              
205             The syntax of the text file is described in the documentation of
206             the IEEE standard for Verilog. Briefly, the file contains two types
207             of tokens: data and optional addresses. The tokens are separated by
208             whitespace and comments. Comments may be single-line (//) or
209             multi-line (/**/), similar to C++. Addresses are specified by a leading
210             "at" character (@) and are always hexadecimal strings. Data values
211             are either hexadecimal strings (C<$readmemh>) or binary strings (C<$readmemb>).
212             Data and addresses may contain underscore (_) characters. The syntax
213             supports 4-state logic for data values (0, 1, x, z), where x represents
214             an unknown value and z represents the high impedance value.
215              
216             If no address is specified, the data is assumed to start at address 0.
217             Similarly, if data exists before the first specified address, then that data
218             is assumed to start at address 0.
219              
220             There are many corner cases which are not explicitly mentioned in the
221             Verilog document. In each instance, this module was designed to behave
222             the same as two widely-known, commercially-available simulators.
223              
224             =head1 SUBROUTINES
225              
226              
227             =over 4
228              
229             =item parse_readmem
230              
231             Read in a Verilog $readmem format text file and return the addresses
232             and data as a reference to an array of arrays. All comments are
233             stripped out. All options to the C function must
234             be passed as a single B.
235              
236             =back
237              
238             =head2 OPTIONS
239              
240             =over 4
241              
242             =item filename
243              
244             A filename must be provided.
245              
246             my $mem_ref = parse_readmem({filename => 'memory.hex'});
247              
248             =item binary
249              
250             By default, the input file format is hexadecimal, consistent with
251             the Verilog C<$readmemh()> system task. To read in a binary format,
252             consistent with the Verilog C<$readmemb()> system task,
253             use C<< binary=>1 >>.
254              
255             my $mem_ref = parse_readmem({filename=>$file, binary=>1});
256              
257             =item string
258              
259             By default, all addresses and data values will be converted
260             to numeric (decimal) values. If numeric conversion is not
261             desired, use C<< string=>1 >>.
262              
263             my $mem_ref = parse_readmem({filename=>$file, string=>1});
264              
265             In numeric conversion mode, data must represent 2-state
266             logic (0 and 1). If an application requires 4-state logic (0, 1, x, z),
267             numeric conversion must be disabled using C<< string=>1 >>.
268              
269             To parse a binary format file using string mode:
270              
271             my $mem_ref = parse_readmem(
272             {
273             string => 1,
274             binary => 1,
275             filename => '/path/to/file.bin'
276             }
277             );
278              
279             =back
280              
281             =head2 EXAMPLE
282              
283             The returned array-of-arrays has the following structure:
284              
285             [a0, d01, d02, d03],
286             [a1, d11, d12, d13, d14, d15],
287             [a2, d21, d22]
288              
289             Each array corresponds to a block of memory. The first item in
290             each array is the start address of the block. All subsequent
291             items are data values. In the example above, there are 3 memory
292             blocks. The 1st block starts at address a0 and has 3 data values.
293             The 2nd block starts at address a1 and has 5 data values.
294             The 3rd block starts at address a2 and has 2 data values.
295              
296             =head1 EXPORT
297              
298             None by default.
299              
300             =head1 DIAGNOSTICS
301              
302             Error conditions cause the program to die using C from the
303             standard C module.
304              
305             =head1 LIMITATIONS
306              
307             In the default numeric conversion mode, address and data values
308             may not be larger than 32-bit. If an application requires larger values,
309             numeric conversion must be disabled using C<< string=>1 >>. This allows
310             for post-processing of strings in either hexadecimal or binary format.
311              
312             =head1 SEE ALSO
313              
314             Refer to the following Verilog documentation:
315              
316             IEEE Standard Verilog (c) Hardware Description Language
317             IEEE Std 1364-2001
318             Version C
319             Section 17.2.8, "Loading memory data from a file"
320              
321             =head1 AUTHOR
322              
323             Gene Sullivan (gsullivan@cpan.org)
324              
325             =head1 COPYRIGHT AND LICENSE
326              
327             Copyright (c) 2008 Gene Sullivan. All rights reserved.
328              
329             This module is free software; you can redistribute it and/or modify
330             it under the same terms as Perl itself. See L.
331              
332             =cut
333              
334             1;
335              
336             __END__