File Coverage

blib/lib/Verilog/Readmem.pm
Criterion Covered Total %
statement 75 76 98.6
branch 47 52 90.3
condition n/a
subroutine 8 8 100.0
pod 0 5 0.0
total 130 141 92.2


line stmt bran cond sub pod time code
1             package Verilog::Readmem;
2              
3 1     1   21000 use warnings;
  1         3  
  1         33  
4 1     1   6 use strict;
  1         2  
  1         35  
5 1     1   6 use Carp;
  1         2  
  1         1390  
6              
7             require Exporter;
8             our @ISA = qw(Exporter);
9             our @EXPORT_OK = qw(parse_readmem);
10             our @EXPORT;
11              
12             our $VERSION = '0.04';
13              
14              
15             sub bin2dec {
16 6     6 0 26 return unpack("N", pack("B32", substr("0" x 32 . shift, -32)));
17             }
18              
19             sub parse_readmem {
20 25     25 0 441224 my ($arg_ref) = @_;
21 25         37 my $file;
22             my $hex_mode;
23 0         0 my $numeric;
24              
25             # Check inputs.
26 25 100       65 if (exists $arg_ref->{filename}) {
27 24         42 $file = $arg_ref->{filename};
28             }
29             else {
30 1         231 croak "Error: filename is required.\n";
31             }
32              
33 24 100       50 if (exists $arg_ref->{binary}) {
34 6 100       21 $hex_mode = ($arg_ref->{binary} eq 1) ? 0 : 1;
35             }
36             else {
37 18         21 $hex_mode = 1;
38             }
39              
40 24 100       51 if (exists $arg_ref->{string}) {
41 6 50       18 $numeric = ($arg_ref->{string} eq 1) ? 0 : 1;
42             }
43             else {
44 18         22 $numeric = 1;
45             }
46              
47             # Remove comments from input file.
48 24         46 my $lines = remove_comments($file);
49 24         7877 $lines =~ s/^\s+//m; # Remove any leading whitespace prior to split
50 24         27206 my @tokens = split /\s+/, $lines;
51              
52             # Create array-of-arrays corresponding to all address blocks.
53 24         2195 my @all_blocks;
54             my @block;
55 24         34 push @block, '0';
56 24         51 for (@tokens) {
57 65691         81986 $_ = lc;
58 65691 100       92057 if (/^@/) {
59 25         40 my $addr = check_addr($_, $numeric);
60 20 100       42 if (@block > 1) {push @all_blocks, [@block]}
  12         29  
61 20         32 @block = ();
62 20         34 push @block, $addr;
63             }
64             else {
65 65666         107975 push @block, check_data($_, $hex_mode, $numeric);
66             }
67             }
68 15 100       39 if (@block > 1) {push @all_blocks, [@block]}
  11         8584  
69              
70 15         5680 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 65666     65666 0 73620 my ($dat, $hex_mode, $numeric) = @_;
77 65666 100       117967 if ($dat =~ /^_/) {
78 1         127 croak "Error: illegal leading underscore for data '$dat'.\n";
79             }
80 65665 100       80956 if ($numeric) { # Convert to numeric
81 65630         67338 $dat =~ s/_//g;
82 65630 100       84480 if ($hex_mode) {
83 65623 100       117239 croak "Error: unsupported characters in 2-state readmemh input '$dat'.\n" if ($dat =~ /[^\da-f]/);
84 65622 100       103171 croak "Error: Hex value exceeds 32-bits '$dat'.\n" if (length($dat) > 8);
85 65621         75505 $dat = hex $dat;
86             }
87             else {
88 7 50       18 croak "Error: unsupported characters in 2-state readmemb input '$dat'.\n" if ($dat =~ /[^01]/);
89 7 100       115 croak "Error: Binary value exceeds 32-bits '$dat'.\n" if (length($dat) > 32);
90 6         10 $dat = bin2dec($dat);
91             }
92             }
93             else { # String mode
94 35 100       52 if ($hex_mode) {
95 23 50       58 croak "Error: unsupported characters in 4-state readmemh input '$dat'.\n" if ($dat =~ /[^\da-fxz_]/);
96             }
97             else {
98 12 50       22 croak "Error: unsupported characters in 4-state readmemb input '$dat'.\n" if ($dat =~ /[^01xz_]/);
99             }
100             }
101 65662         119218 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 37 my ($addr, $numeric) = @_;
108 25         62 $addr =~ s/^@//;
109 25 100       59 return 0 unless length $addr;
110 23 100       48 if ($numeric) { # Convert to numeric
111 20         28 $addr =~ s/_//g;
112 20 100       39 return 0 unless length $addr;
113 19 100       478 croak "Error: unsupported characters in 2-state address '$addr'.\n" if ($addr =~ /[^\da-f]/);
114 16 100       127 croak "Error: Hex address exceeds 32-bits '$addr'.\n" if (length($addr) > 8);
115 15         20 $addr = hex $addr;
116             }
117             else { # String mode
118 3 100       109 croak "Error: unsupported characters in 2-state string address '$addr'.\n" if ($addr =~ /[^\da-f_]/);
119             }
120 17         29 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 24     24 0 28 my $file = shift;
130 24         74 local $/ = undef;
131 24 50       1023 open my $IN_FH, '<', $file or croak "$!";
132 24         2451 my $lines = <$IN_FH>;
133 24         262 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 24         504 $lines =~ s{//\*}{// \*}g;
145 24         513 $lines =~ s{/\*}{ /\*}g;
146              
147             # Use regex from perlfaq6 (C++ comments).
148 24 100       3987 $lines =~ s#/\*[^*]*\*+([^/*][^*]*\*+)*/|//[^\n]*|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^/"'\\]*)#defined $2 ? $2 : ""#gse;
  138         1537  
149              
150             # Returns all lines as a string
151 24         145 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.04.
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             parse_readmem
227              
228             Read in a Verilog $readmem format text file and return the addresses
229             and data as a reference to an array of arrays. All comments are
230             stripped out. All options to the C function must
231             be passed as a single B.
232              
233             =head2 OPTIONS
234              
235             =over 4
236              
237             =item filename
238              
239             A filename must be provided.
240              
241             my $mem_ref = parse_readmem({filename => 'memory.hex'});
242              
243             =item binary
244              
245             By default, the input file format is hexadecimal, consistent with
246             the Verilog C<$readmemh()> system task. To read in a binary format,
247             consistent with the Verilog C<$readmemb()> system task,
248             use C<< binary=>1 >>.
249              
250             my $mem_ref = parse_readmem({filename=>$file, binary=>1});
251              
252             =item string
253              
254             By default, all addresses and data values will be converted
255             to numeric (decimal) values. If numeric conversion is not
256             desired, use C<< string=>1 >>.
257              
258             my $mem_ref = parse_readmem({filename=>$file, string=>1});
259              
260             In numeric conversion mode, data must represent 2-state
261             logic (0 and 1). If an application requires 4-state logic (0, 1, x, z),
262             numeric conversion must be disabled using C<< string=>1 >>.
263              
264             To parse a binary format file using string mode:
265              
266             my $mem_ref = parse_readmem(
267             {
268             string => 1,
269             binary => 1,
270             filename => '/path/to/file.bin'
271             }
272             );
273              
274             =back
275              
276             =head2 EXAMPLE
277              
278             The returned array-of-arrays has the following structure:
279              
280             [a0, d01, d02, d03],
281             [a1, d11, d12, d13, d14, d15],
282             [a2, d21, d22]
283              
284             Each array corresponds to a block of memory. The first item in
285             each array is the start address of the block. All subsequent
286             items are data values. In the example above, there are 3 memory
287             blocks. The 1st block starts at address a0 and has 3 data values.
288             The 2nd block starts at address a1 and has 5 data values.
289             The 3rd block starts at address a2 and has 2 data values.
290              
291             =head2 EXPORT
292              
293             None by default.
294              
295             =head1 DIAGNOSTICS
296              
297             Error conditions cause the program to die using C from the
298             standard C module.
299              
300             =head1 LIMITATIONS
301              
302             In the default numeric conversion mode, address and data values
303             may not be larger than 32-bit. If an application requires larger values,
304             numeric conversion must be disabled using C<< string=>1 >>. This allows
305             for post-processing of strings in either hexadecimal or binary format.
306              
307             =head1 SEE ALSO
308              
309             Refer to the following Verilog documentation:
310              
311             IEEE Standard Verilog (c) Hardware Description Language
312             IEEE Std 1364-2001
313             Version C
314             Section 17.2.8, "Loading memory data from a file"
315              
316             =head1 AUTHOR
317              
318             Gene Sullivan (gsullivan@cpan.org)
319              
320             =head1 COPYRIGHT AND LICENSE
321              
322             Copyright (c) 2008 Gene Sullivan. All rights reserved.
323              
324             This module is free software; you can redistribute it and/or modify
325             it under the same terms as Perl itself. See L.
326              
327             =cut
328              
329             1;
330              
331             __END__