1 |
73 |
ja_rd |
################################################################################
|
2 |
|
|
# hexconv.pl -- inserts object code in HEX format into an VHDL template.
|
3 |
|
|
#
|
4 |
|
|
# This program reads an Intel HEX file with 8-bit object code and inserts it
|
5 |
|
|
# into a VHDL template, in the form of a VHDL std_logic_vector array
|
6 |
|
|
# initializer. This is meant to initialize FPGA ROM/RAM blocks with object code.
|
7 |
|
|
# When the program finds a template line which begins with "--@rom_data", it
|
8 |
|
|
# replaces the whole line with the VHDL table.
|
9 |
|
|
# When it finds the text @PROGNAME@ in a line, it replaces that text with the
|
10 |
|
|
# file name (without path or extension) of the hex file.
|
11 |
|
|
# Otherwise, it just copies the template to stdout verbatim.
|
12 |
|
|
#
|
13 |
|
|
# See usage details below, and examples in the BAT file in the asm directory.
|
14 |
|
|
################################################################################
|
15 |
|
|
|
16 |
|
|
$usage = "Use: hexconv.pl <hexfile> <template file> <start addr> <table size>";
|
17 |
|
|
|
18 |
|
|
# read command line arguments; HEX file name...
|
19 |
|
|
$file = shift(@ARGV);
|
20 |
|
|
if($file eq ''){die $usage};
|
21 |
|
|
# ...VHDL template file name...
|
22 |
|
|
$template = shift(@ARGV);
|
23 |
|
|
if($template eq ''){die $usage};
|
24 |
|
|
# ...object code start address...
|
25 |
|
|
$start_addr = shift(@ARGV);
|
26 |
|
|
if($start_addr eq ''){die $usage};
|
27 |
|
|
$start_addr = hex $start_addr;
|
28 |
|
|
# ...and VHDL table size
|
29 |
|
|
$table_size = shift(@ARGV);
|
30 |
|
|
if($table_size eq ''){die $usage};
|
31 |
|
|
$table_size = hex $table_size;
|
32 |
|
|
|
33 |
|
|
# read HEX file...
|
34 |
|
|
open(INFO, $file) or die "file $file not found";
|
35 |
|
|
@lines = <INFO>;
|
36 |
|
|
close(INFO);
|
37 |
|
|
|
38 |
|
|
# ...and VHDL template
|
39 |
|
|
open(INFO, $template) or die "file $template not found";
|
40 |
|
|
@vhdl_lines = <INFO>;
|
41 |
|
|
close(INFO);
|
42 |
|
|
|
43 |
|
|
$min_address = 65536;
|
44 |
|
|
$max_address = 0;
|
45 |
|
|
$bytes_read = 0;
|
46 |
|
|
|
47 |
|
|
# make up a 'ram image' table of 64K bytes where the object code will be put.
|
48 |
|
|
@data_array = ();
|
49 |
|
|
for($i=0;$i<65536;$i++){ $data_array[$i] = 0; };
|
50 |
|
|
|
51 |
|
|
# read input HEX file into ram image table
|
52 |
|
|
$line_no = 0;
|
53 |
|
|
foreach $line (@lines){
|
54 |
|
|
|
55 |
|
|
chomp($line);
|
56 |
|
|
$line_no++;
|
57 |
|
|
|
58 |
|
|
if(length($line)>=11 and substr($line, 0, 1) eq ':'){
|
59 |
|
|
$total_length = length($line);
|
60 |
|
|
$len = substr($line, 1,2);
|
61 |
|
|
$addr = substr($line, 3,4);
|
62 |
|
|
$type = substr($line, 7,2);
|
63 |
|
|
$csum = substr($line, $total_length-3,2);
|
64 |
|
|
$data = substr($line, 9,$total_length-11);
|
65 |
|
|
|
66 |
|
|
# Process data records and utterly ignore all others.
|
67 |
|
|
# Note that the checksum field is ignored too; we rely on the correctness
|
68 |
|
|
# of the hex file.
|
69 |
|
|
if($type eq '00'){
|
70 |
|
|
$len = hex $len;
|
71 |
|
|
$first_addr = hex $addr;
|
72 |
|
|
$last_addr = $first_addr + $len - 1;
|
73 |
|
|
|
74 |
|
|
if($first_addr < $min_address){
|
75 |
|
|
$min_address = $first_addr;
|
76 |
|
|
};
|
77 |
|
|
if($last_addr > $max_address){
|
78 |
|
|
$max_address = $last_addr;
|
79 |
|
|
};
|
80 |
|
|
|
81 |
|
|
$chksum = 0;
|
82 |
|
|
for($i=0;$i<$len;$i++){
|
83 |
|
|
$data_byte = substr($line, 9+$i*2, 2);
|
84 |
|
|
$data_byte = hex $data_byte;
|
85 |
|
|
$chksum += $data_byte;
|
86 |
|
|
$data_array[$first_addr+$i] = $data_byte;
|
87 |
|
|
$bytes_read++;
|
88 |
|
|
}
|
89 |
|
|
}
|
90 |
|
|
}
|
91 |
|
|
else{
|
92 |
|
|
die "Wrong format in line $line_no\n";
|
93 |
|
|
}
|
94 |
|
|
}
|
95 |
|
|
|
96 |
|
|
# Make sure all the object code we read from the hex file will fit in the VHDL
|
97 |
|
|
# memory; this is a typo-catcher.
|
98 |
|
|
|
99 |
|
|
if($min_address < $start_addr or $max_address < $start_addr){
|
100 |
|
|
die "Hex data out of bounds";
|
101 |
|
|
}
|
102 |
|
|
|
103 |
|
|
$upper_bound = $start_addr + $table_size;
|
104 |
|
|
|
105 |
|
|
if($min_address > $upper_bound or
|
106 |
|
|
$max_address > $upper_bound){
|
107 |
|
|
die "Hex data out of bounds: ".$upper_bound;
|
108 |
|
|
}
|
109 |
|
|
|
110 |
|
|
# debug output
|
111 |
|
|
#printf "Data address span [%04x : %04x]\n", $min_address, $max_address;
|
112 |
|
|
#$bytes_defaulted = ($max_address-$min_address+1)-$bytes_read;
|
113 |
|
|
#if($bytes_defaulted > 0){
|
114 |
|
|
# printf "(%d bytes defaulted to 0)\n", $bytes_defaulted;
|
115 |
|
|
#}
|
116 |
|
|
|
117 |
|
|
#### Now process the template inserting the ROM bytes where necessary
|
118 |
|
|
|
119 |
|
|
# display only the template filename, cut away any path that may be present
|
120 |
|
|
if($template =~ /^.*[\\\/](.*\..*)/){ $template = $1; }
|
121 |
|
|
# put a reminder in the 1st lines of the VHDL output
|
122 |
|
|
$comm = "--------------------";
|
123 |
|
|
print $comm.$comm.$comm.$comm."\n";
|
124 |
|
|
print "-- Generated from template $template by hexconv.pl\n";
|
125 |
|
|
|
126 |
|
|
# Extract program name from the hex file name, stripping path and extension
|
127 |
|
|
if($file =~ /^.*[\\\/](.*)\..*/){
|
128 |
|
|
$file = $1;
|
129 |
|
|
}
|
130 |
|
|
elsif($file =~ /^(.*)\..*/){
|
131 |
|
|
$file = $1;
|
132 |
|
|
}
|
133 |
|
|
|
134 |
|
|
# Output template file contents to stdout, line by line, inserting the object
|
135 |
|
|
# code when we find the 'data tag' "@rom_data".
|
136 |
|
|
foreach $vhdl (@vhdl_lines){
|
137 |
|
|
if($vhdl =~ /^\s*--\@rom_data/){
|
138 |
|
|
# if we find the ROM data tag in a comment line, replace line
|
139 |
|
|
# with VHDL table.
|
140 |
|
|
print_rom_code($start_addr, $table_size, @data_array);
|
141 |
|
|
}
|
142 |
|
|
else{
|
143 |
|
|
# otherwise, output template line
|
144 |
|
|
$vhdl =~ s/\@PROGNAME\@/$file/;
|
145 |
|
|
printf $vhdl;
|
146 |
|
|
};
|
147 |
|
|
}
|
148 |
|
|
|
149 |
|
|
# Prints a chunk of bytes as a VHDL table of std_logic_vectors, formatted as
|
150 |
|
|
# 8 bytes per column.
|
151 |
|
|
#
|
152 |
|
|
# print_rom_code ($obj_code_table, $obj_code_start, $obj_code_size)
|
153 |
|
|
# $obj_code_start : address of the 1st byte that we want to put in the VHDL RAM.
|
154 |
|
|
# $obj_code_size : number of bytes to put in the VHDL memory.
|
155 |
|
|
# @obj_code_table : image of the CPU 64K memory map with the object code in it.
|
156 |
|
|
sub print_rom_code {
|
157 |
|
|
my($obj_code_start, $obj_code_size, @obj_code_table) = @_;
|
158 |
|
|
$col = 0;
|
159 |
|
|
for($i=0;$i<$obj_code_size;$i++){
|
160 |
|
|
$q = $obj_code_table[$obj_code_start+$i];
|
161 |
|
|
print $q
|
162 |
|
|
printf "X\"%02x\"", $q;
|
163 |
|
|
if($i<$obj_code_size-1){
|
164 |
|
|
printf ",";
|
165 |
|
|
}
|
166 |
|
|
$col++;
|
167 |
|
|
if($col eq 8){
|
168 |
|
|
print "\n";
|
169 |
|
|
$col = 0;
|
170 |
|
|
}
|
171 |
|
|
}
|
172 |
|
|
}
|