Mike said:
Another possible solution is to use perl or bash
to convert the external hex file to a deferred
constant package body something like:
I wrote one of those a few years back; I've attached the
latest version below, along with some sample files:
<test.obj> input object file
<test.vh1> generated package with constant array
<test.vh2> generated package with INITs
<ylink.pl> perl 'linker' that creates VHDL source files
The perl script reads the assembler object file and coughs up
two VHDL files, one a package with a constant array suitable
for simulation or inferred RAM, the other a package with INITs
and generics for instantiated BRAMs.
At the time I wrote this, XST didn't support indexing into a
constant array from within a generate loop; I believe the current
XST version would allow putting the INITs into an array, making
it easier to support BRAM organizations other than the currently
hardcoded 512x32 (four RAMB4_S8_S8's ).
Also, some old posts regarding how to initialize dual port variable
width BRAMs (Harvard architecture, 16 bit instruction, 32 bit data)
are at:
http://groups.yahoo.com/group/fpga-cpu/message/234
http://groups.yahoo.com/group/fpga-cpu/message/332
Other ROM generation programs:
If you download MikeJ's nifty Asteroids emulation, there's a
C++ 8 bit ROM generation utility included <romgen.cpp>.
http://home.freeuk.com/fpgaarcade/ast_main.htm
Mike Butts has a hex2init perl script (more elegant than mine) that
generates a constraints file for 16 bit memories :
http://users.easystreet.com/mbutts/xr16vx_jhdl.html
Also, I believe the newer versions of Xilinx's DATA2MEM utility
will cough up a file with INIT's for primitive simulation as well
as update an existing bitstream with new memory data.
Brian
<test.obj>
@0
opcode=0000011111110000
opcode=0000011111110000
opcode=0000010111110000
opcode=0000010111110000
opcode=0000010111110000
opcode=0000001111110000
opcode=0000001111110000
opcode=0000001111110000
@200
long=12345678
word=abcd
byte=de
byte=ad
byte=be
byte=ef
<test.vh1>
--
-- Auto-generated by ylink.pl
--
library ieee;
use ieee.std_logic_1164.all;
package mem_dat_pkg is
constant MEM_SIZE : natural := 1024;
type mem_type is array (0 to MEM_SIZE-1) of std_logic_vector (15
downto 0);
constant mem_dat : mem_type :=
(
0 => X"07F0",
1 => X"07F0",
2 => X"05F0",
3 => X"05F0",
4 => X"05F0",
5 => X"03F0",
6 => X"03F0",
7 => X"03F0",
256 => X"1234",
257 => X"5678",
258 => X"abcd",
259 => X"dead",
260 => X"beef",
others => ( others => '0')
);
end mem_dat_pkg;
<test.vh2>
--
-- Auto-generated by ylink.pl
--
library ieee;
use ieee.std_logic_1164.all;
package mem_init_pkg is
constant RAM3_INIT_00 : string :=
"0000000000000000000000000000000000000000000000000000000003050507";
constant RAM2_INIT_00 : string :=
"00000000000000000000000000000000000000000000000000000000F0F0F0F0";
constant RAM1_INIT_00 : string :=
"0000000000000000000000000000000000000000000000000000000003030507";
constant RAM0_INIT_00 : string :=
"00000000000000000000000000000000000000000000000000000000F0F0F0F0";
constant RAM3_BV_INIT_00 : bit_vector :=
X"0000000000000000000000000000000000000000000000000000000003050507";
constant RAM2_BV_INIT_00 : bit_vector :=
X"00000000000000000000000000000000000000000000000000000000F0F0F0F0";
constant RAM1_BV_INIT_00 : bit_vector :=
X"0000000000000000000000000000000000000000000000000000000003030507";
constant RAM0_BV_INIT_00 : bit_vector :=
X"00000000000000000000000000000000000000000000000000000000F0F0F0F0";
<snip>
constant RAM3_INIT_04 : string :=
"0000000000000000000000000000000000000000000000000000000000beab12";
constant RAM2_INIT_04 : string :=
"0000000000000000000000000000000000000000000000000000000000efcd34";
constant RAM1_INIT_04 : string :=
"000000000000000000000000000000000000000000000000000000000000de56";
constant RAM0_INIT_04 : string :=
"000000000000000000000000000000000000000000000000000000000000ad78";
constant RAM3_BV_INIT_04 : bit_vector :=
X"0000000000000000000000000000000000000000000000000000000000beab12";
constant RAM2_BV_INIT_04 : bit_vector :=
X"0000000000000000000000000000000000000000000000000000000000efcd34";
constant RAM1_BV_INIT_04 : bit_vector :=
X"000000000000000000000000000000000000000000000000000000000000de56";
constant RAM0_BV_INIT_04 : bit_vector :=
X"000000000000000000000000000000000000000000000000000000000000ad78";
<snip>
constant RAM3_INIT_0F : string :=
"0000000000000000000000000000000000000000000000000000000000000000";
constant RAM2_INIT_0F : string :=
"0000000000000000000000000000000000000000000000000000000000000000";
constant RAM1_INIT_0F : string :=
"0000000000000000000000000000000000000000000000000000000000000000";
constant RAM0_INIT_0F : string :=
"0000000000000000000000000000000000000000000000000000000000000000";
constant RAM3_BV_INIT_0F : bit_vector :=
X"0000000000000000000000000000000000000000000000000000000000000000";
constant RAM2_BV_INIT_0F : bit_vector :=
X"0000000000000000000000000000000000000000000000000000000000000000";
constant RAM1_BV_INIT_0F : bit_vector :=
X"0000000000000000000000000000000000000000000000000000000000000000";
constant RAM0_BV_INIT_0F : bit_vector :=
X"0000000000000000000000000000000000000000000000000000000000000000";
end mem_init_pkg;
<ylink.pl>
#! /usr/local/bin/perl5
#-------------------------------------------------------------------------------
# YARD-1 Linker v0.01
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
#
# ylink.pl v0.01 (C) COPYRIGHT 2001 B. Davis
#
# This program is free software; you may use, modify, and
# redistribute it without restriction provided that:
# 1) this header is preserved on all copies
# 2) any modified versions are clearly identified as such
#
# Being free software, this program comes with neither support
# nor warranty, including any implied warranty of merchantability
# or fitness for a particular purpose.
#
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
#
# General Notes:
#
# - reads .obj file generated by yasm.pl
# - writes VHDL file with INIT_XX constants
#
# currently, more of a memory image generator than a real linker
#
# - linker output files:
# - <file>.vh2 = VHDL file with INIT_XX constants
# - <file>.vh1 = VHDL file with constant array holding memory
data
# - <file>.cmd = YARDBUG command file to load program
#
#
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
#
# 'to do' list:
#
#
# - add command line args for:
# - memory image start/size
# - block RAM organization ( generate different INIT_XX records
as needed )
# - VHDL output file name and/or package name in VHDL file
#
# - add other output formats:
# - S-record or similar hex format
# - EPIC command script for editing block RAM
# - .coe for COREGEN created memories
#
#
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
#
# history:
#
# Nov. 2001
# - moved VHDL constant array file (.vh1) generation here from
yasm.pl
#
# Jul. 2001
# - added bit vector INIT's for simulation generics ( instead of
writing VHDL xlate function )
#
# Mar. 2001
# - changed memory image and INIT code to use byte array instead of
word array
#
# Feb. 2001
# - created
#
#
#
#-------------------------------------------------------------------------------
printf("\nYARD-1 Linker Version 0.01\n");
# should have one argument, the object file
if ($#ARGV != 0) { die ("\nError: Expecting one (and only one) object
file \n") };
$obj = $ARGV[0];
$obj =~ s/\.obj$//g;
$obj_file = $obj . '.obj';
$cmd_file = $obj . '.cmd';
$vho_file = $obj . '.vh2';
$vhc_file = $obj . '.vh1';
open (OBJ_F, "$obj_file") or die ("Can't open $obj_file: $!\n");
open (CMD_F, ">$cmd_file") or die ("Can't open $cmd_file: $!\n");
open (VHO_F, ">$vho_file") or die ("Can't open $vho_file: $!\n");
open (VHC_F, ">$vhc_file") or die ("Can't open $vhc_file: $!\n");
#
# vhdl INIT output file header/trailer
#
$vho_file_header = "--
-- Auto-generated by ylink.pl
--
library ieee;
use ieee.std_logic_1164.all;
package mem_init_pkg is
";
$vho_file_trailer = "
end mem_init_pkg;
";
#
# vhdl constant array output file header/trailer
#
$vhc_file_header = "--
-- Auto-generated by ylink.pl
--
library ieee;
use ieee.std_logic_1164.all;
package mem_dat_pkg is
constant MEM_SIZE : natural := 1024;
type mem_type is array (0 to MEM_SIZE-1) of std_logic_vector (15
downto 0);
constant mem_dat : mem_type :=
(
";
$vhc_file_trailer = "
others => ( others => '0')
);
end mem_dat_pkg;
";
# $D1 turns debug prints on/off
$D1 = 0;
#
# memory array definitions ( for INIT_XX attribute calculation )
# currently hardcoded for a 2K byte RAM starting at zero
# indexed by bytes
# values stored as two digit hex string
#
$mem_init_value = "XX";
$mem_start = 0;
$mem_size = 2048;
#
# initialize memory image array
#
$mem_data[$mem_size-1] = $mem_init_value;
for($i=0 ; $i < $mem_size; $i++ ) { $mem_data[$i] = $mem_init_value;}
# clear other flags
$error = 0;
#
# Read .obj file, build memory image
#
while ($line = <OBJ_F>)
{
chop $line;
if ($line =~ /@(.+)/)
{
$address = oct ( "0x" . $1 );
}
elsif ($line =~ /opcode=(\d{16})$/)
{
$op16 = vec(pack("B16", $1),0,16);
$dat_str = sprintf "%04X", $op16;
$mem_data[ $address - $mem_start ] =
substr($dat_str,0,2);
$mem_data[ $address - $mem_start + 1 ] =
substr($dat_str,2,2);
$address = $address + 2;
}
elsif ($line =~ /byte=(.{2})$/)
{
$mem_data[ $address - $mem_start ] = $1;
$address = $address + 1;
}
elsif($line =~ /word=(.{4})$/)
{
$mem_data[ $address - $mem_start ] = substr($1,0,2);
$mem_data[ $address - $mem_start + 1 ] = substr($1,2,2);
$address = $address + 2;
}
elsif($line =~ /long=(.{8})$/)
{
$dat_str = substr($1,0,2);
$mem_data[ $address - $mem_start ] = $dat_str;
$dat_str = substr($1,2,2);
$mem_data[ $address - $mem_start + 1 ] = $dat_str;
$dat_str = substr($1,4,2);
$mem_data[ $address - $mem_start + 2 ] = $dat_str;
$dat_str = substr($1,6,2);
$mem_data[ $address - $mem_start + 3 ] = $dat_str;
$address = $address + 4;
}
} # end while
#
# Write YARDBUG command file
#
$c_start = -1;
for($i=0 ; $i < $mem_size; $i++ )
{
if ( $c_start == -1)
{
if ( $mem_data[$i] ne "XX" )
{
$c_start = $i;
printf CMD_F ("M %08X %s ", $i,$mem_data[$i] );
}
}
else
{
if ( $mem_data[$i] ne "XX" )
{
printf CMD_F ("%s ", $mem_data[$i]);
}
if ( ( $mem_data[$i] eq "XX" ) || ( $i >= ($c_start + 15) ) )
{
$c_start = -1;
printf CMD_F ("\n" );
}
}
}
printf CMD_F ("\n" );
#
# Write VHDL constant array file
#
# create output file boilerplate
print VHC_F $vhc_file_header;
# dump memory values to VHDL word array ( dumps only word locations
being used )
for($i=0 ; $i < $mem_size; $i+=2 )
{
if ( ( $mem_data[$i] ne "XX" ) || ( $mem_data[$i+1] ne "XX" ) )
{
printf VHC_F (" %12d => X\"%s%s\", \n", $i >> 1,
$mem_data[$i], $mem_data[$i+1] );
}
}
# VHDL boilerplate to end memory array
print VHC_F $vhc_file_trailer;
#
# fill unused locations with zeroes before calculating INIT strings
#
for($i=0 ; $i < $mem_size; $i++ )
{
if ( $mem_data[$i] eq "XX" )
{
$mem_data[$i] = "00";
}
}
#
# Write VHDL blockram init file
#
# create output file boilerplate
print VHO_F $vho_file_header;
#
# spit out INIT_XX strings
# - currently hardcoded to read a 2K x 8 memory image array
# - writes INIT's for a 512 x 32 block RAM built from four
RAMB4_S8_S8's
#
$init_blks = 16;
$init_blk_size = 32;
$init_str_size = 64;
for ( $i=0 ; $i < $init_blks; $i++ ) # INIT_00 through INIT_0F
{
for ( $j=0 ; $j < $init_blk_size; $j++ ) # 32 longs (32 bit) per
INIT block
{
# two hex chars. per word for each x8 BRAM; LSB value = last
char in string
$str_index = $init_str_size - ( $j*2 ) - 1;
# byte address in memory array
$baddr = ( $i * $init_blk_size * 4 ) + ( $j * 4 );
$s3[$str_index-1] = substr($mem_data[$baddr], 0, 1);
$s3[$str_index] = substr($mem_data[$baddr], 1, 1);
$s2[$str_index-1] = substr($mem_data[$baddr+1], 0, 1);
$s2[$str_index] = substr($mem_data[$baddr+1], 1, 1);
$s1[$str_index-1] = substr($mem_data[$baddr+2], 0, 1);
$s1[$str_index] = substr($mem_data[$baddr+2], 1, 1);
$s0[$str_index-1] = substr($mem_data[$baddr+3], 0, 1);
$s0[$str_index] = substr($mem_data[$baddr+3], 1, 1);
if ($D1) { printf ("INIT: %d %d %d %d %s %s\n", $i, $j,
$str_index, $baddr, $mem_data[$baddr],$mem_data[$baddr+1] ); }
if ($D1) { printf ("INIT: %d %d %d %d %s %s\n", $i, $j,
$str_index-1, $baddr+1, $mem_data[$baddr+2], $mem_data[$baddr+3] ); }
}
printf VHO_F ("constant RAM3_INIT_%02X : string :=
\"%s\";\n", $i, join('', @s3) );
printf VHO_F ("constant RAM2_INIT_%02X : string :=
\"%s\";\n", $i, join('', @s2) );
printf VHO_F ("constant RAM1_INIT_%02X : string :=
\"%s\";\n", $i, join('', @s1) );
printf VHO_F ("constant RAM0_INIT_%02X : string :=
\"%s\";\n", $i, join('', @s0) );
printf VHO_F ("\n");
printf VHO_F ("constant RAM3_BV_INIT_%02X : bit_vector :=
X\"%s\";\n", $i, join('', @s3) );
printf VHO_F ("constant RAM2_BV_INIT_%02X : bit_vector :=
X\"%s\";\n", $i, join('', @s2) );
printf VHO_F ("constant RAM1_BV_INIT_%02X : bit_vector :=
X\"%s\";\n", $i, join('', @s1) );
printf VHO_F ("constant RAM0_BV_INIT_%02X : bit_vector :=
X\"%s\";\n", $i, join('', @s0) );
printf VHO_F ("\n");
}
# finish up output file boilerplate
print VHO_F $vho_file_trailer;
# tidy up
close OBJ_F;
close CMD_F;
close VHO_F;
close VHC_F;
printf("\nTotal Errors = %d\n", $error);