您的位置:首页 > 其它

Dual-Port Block RAM with Two Write Ports and Byte-wide Write Enable in Read-First Mode

2012-05-10 13:58 726 查看
Initializing Block RAM from external intel hex file

1 --
-- Dual-Port Block RAM with Two Write Ports and
-- Byte-wide Write Enable in Read-First Mode
--
-- Initializing Block RAM from external intel hex file
--
-- http://www.keil.com/support/docs/1584/ --------------------------------------------------------------------
-- http://en.wikipedia.org/wiki/Intel_HEX --------------------------------------------------------------------
--
--  The format is a text file, with each line containing hexadecimal values
--  encoding a sequence of data and their starting offset or absolute address.
--
--  There are three types of Intel HEX:
--  8-bit, 16-bit, and 32-bit. They are distinguished by their byte order.
--
--  Each line of Intel HEX file consists of six parts:
--
--  Start code, one character, an ASCII colon ':'.
--
--  Byte count, two hex digits, a number of bytes (hex digit pairs) in the data field.
--    16 (0x10) or 32 (0x20) bytes of data are the usual compromise values
--    between line length and address overhead.
--
--  Address, four hex digits, a 16-bit address of the beginning of the memory position for the data.
--    Limited to 64 kilobytes, the limit is worked around by specifying higher bits via additional record types.
--    This address is big endian.
--
--  Record type, two hex digits, 00 to 05, defining the type of the data field.
--
--  Data, a sequence of n bytes of the data themselves, represented by 2n hex digits.
--
--  Checksum, two hex digits - the least significant byte of the two's complement
--    of the sum of the values of all fields except fields 1 and 6
--    (Start code ":" byte and two hex digits of the Checksum).
--    It is calculated by adding together the hex-encoded bytes (hex digit pairs),
--    then leaving only the least significant byte of the result,
--    and making a 2's complement (either by subtracting the byte from 0x100,
--    or inverting it by XOR-ing with 0xFF and adding 0x01).
--    If you are not working with 8-bit variables,
--    you must suppress the overflow by AND-ing the result with 0xFF.
--    The overflow may occur since both 0x100-0 and (0x00 XOR 0xFF)+1 equal 0x100.
--    If the checksum is correctly calculated, adding all the bytes
--    (the Byte count, both bytes in Address, the Record type, each Data byte and the Checksum)
--    together will always result in a value wherein the least significant byte is zero (0x00).
--
--    For example, on :0300300002337A1E
--    03 + 00 + 30 + 00 + 02 + 33 + 7A = E2, 2's complement is 1E
--
--  There are six record types:
--    00, data record, contains data and 16-bit address. The format described above.
--
--    01, End Of File record.
--      Must occur exactly once per file in the last line of the file.
--      The byte count is 00 and the data field is empty.
--      Usually the address field is also 0000, in which case the complete line is ':00000001FF'.
--      Originally the End Of File record could contain a start address for the program being loaded,
--      e.g. :00AB2F0125 would cause a jump to address AB2F.
--      This was convenient when programs were loaded from punched paper tape.
--
--    02, Extended Segment Address Record, segment-base address (two hex digit pairs in big endian order).
--      Used when 16 bits are not enough, identical to 80x86 real mode addressing.
--      The address specified by the data field of the most recent 02 record is
--      multiplied by 16 (shifted 4 bits left) and added to the subsequent 00 record addresses.
--      This allows addressing of up to a megabyte of address space.
--      The address field of this record has to be 0000, the byte count is 02 (the segment is 16-bit).
--      The least significant hex digit of the segment address is always 0.
--
--    03, Start Segment Address Record. For 80x86 processors, it specifies the initial content of the CS:IP registers.
--      The address field is 0000, the byte count is 04,
--      the first two bytes are the CS value, the latter two are the IP value.
--
--    04, Extended Linear Address Record, allowing for fully 32 bit addressing (up to 4GiB).
--      The address field is 0000, the byte count is 02.
--      The two data bytes (two hex digit pairs in big endian order) represent
--      the upper 16 bits of the 32 bit address for all subsequent 00 type records
--      until the next 04 type record comes.
--      If there is not a 04 type record, the upper 16 bits default to 0000.
--      To get the absolute address for subsequent 00 type records,
--      the address specified by the data field of the most recent 04 record is added to the 00 record addresses.
--
--    05, Start Linear Address Record. The address field is 0000, the byte count is 04.
--      The 4 data bytes represent the 32-bit value loaded into the EIP register of the 80386 and higher CPU.

--
--  :10 0100 00 214601360121470136007EFE09D21901 40
--  :10 0110 00 2146017EB7C20001FF5F160021480119 88
--  :10 0120 00 194E79234623965778239EDA3F01B2CA A7
--  :10 0130 00 3F0156702B5E712B722B732146013421 C7
--  :00 0000 01                                  FF
--------------------------------------------------------------------

library ieee;
use ieee.numeric_std.all;
use ieee.std_logic_1164.all;
use ieee.std_logic_textio.hread;
use std.textio.all;

entity dpram is

generic (
HEX_FILE_NAME : string := "dpram.hex";
ADDR_WIDTH    : natural := 13; -- 32K BYTE = 8K DWORD
BYTE_WIDTH    : natural := 8;  -- always 8
BYTES         : natural := 4); -- 1, 2, 4

port (
clk1      : in  std_logic;
en1       : in  std_logic;
we1       : in  std_logic_vector (BYTES - 1 downto 0);
addr1     : in  std_logic_vector(ADDR_WIDTH - 1 downto 0);
data_in1  : in  std_logic_vector(BYTES*BYTE_WIDTH - 1 downto 0);
data_out1 : out std_logic_vector(BYTES*BYTE_WIDTH-1 downto 0);
clk2      : in  std_logic;
en2       : in  std_logic;
we2       : in  std_logic_vector (BYTES - 1 downto 0);
addr2     : in  std_logic_vector(ADDR_WIDTH - 1 downto 0);
data_in2  : in  std_logic_vector(BYTES*BYTE_WIDTH - 1 downto 0);
data_out2 : out std_logic_vector(BYTES*BYTE_WIDTH-1 downto 0));

end dpram;

architecture rtl of dpram is
type ram_array_type is array (2 ** ADDR_WIDTH-1 downto 0) of std_logic_vector (BYTES*BYTE_WIDTH-1 downto 0);
type ram_byte_array_type is array (BYTES*(2 ** ADDR_WIDTH )-1 downto 0) of std_logic_vector (BYTE_WIDTH-1 downto 0);

impure function ram_init_from_hex_file (constant hex_file_name : in string) return ram_array_type is
file hex_file           : text is in hex_file_name;
variable ram_array      : ram_array_type;
variable ram_byte_array : ram_byte_array_type;
variable line_buf       : line;
variable char           : CHARACTER;
variable rec_type       : STD_LOGIC_VECTOR( 7 downto 0);
variable byte           : STD_LOGIC_VECTOR( 7 downto 0);
variable count          : STD_LOGIC_VECTOR( 7 downto 0);
variable check          : STD_LOGIC_VECTOR( 7 downto 0);
variable addr           : unsigned(15 downto 0);
begin
-- clear all bytes
for i in 0 to BYTES*(2 ** ADDR_WIDTH )-1 loop
ram_byte_array( i ) := ( others => '0' );
end loop;

-- load from hex file
while not endfile( hex_file ) loop
-- read line
readline(hex_file, line_buf);

-- read ':' from line
read(line_buf, char);
if char /= ':' then  next;  end if; -- go to next loop
-- read count from line
hread(line_buf, count);
-- read address from line
hread(line_buf, byte); addr(15 downto 8)  := unsigned( byte );
hread(line_buf, byte); addr( 7 downto  0) := unsigned( byte );

-- check recored type
hread(line_buf, rec_type);
if rec_type = "00000001" then exit; end if; -- it is an end of file record

if rec_type = "00000000" then
-- read bytes from line
for i in 1 to to_integer( unsigned( count ) )  loop
hread(line_buf, byte);
ram_byte_array( to_integer( addr ) ) := byte;

addr := addr + 1;
end loop;
end if;

-- read checksum from line
hread(line_buf, check);
end loop;

-- convert ram_byte_array to ram_array
if BYTES = 4 then
for i in 0 to 2 ** ADDR_WIDTH-1 loop
ram_array( i ) := ram_byte_array( i * 4 + 3 )
& ram_byte_array( i * 4 + 2 )
& ram_byte_array( i * 4 + 1 )
& ram_byte_array( i * 4 + 0 );
end loop;

elsif BYTES = 2 then
for i in 0 to 2 ** ADDR_WIDTH-1 loop
ram_array( i ) := ram_byte_array( i * 2 + 1 )
& ram_byte_array( i * 2  ) ;
end loop;

elsif BYTES = 1 then
for i in 0 to 2 ** ADDR_WIDTH-1 loop
ram_array( i ) := ram_byte_array( i );
end loop;

end if;

return ram_array;
end function;

shared variable ram : ram_array_type := ram_init_from_hex_file(HEX_FILE_NAME);
attribute ram_style        : string;
attribute ram_style of ram : variable is "block";

type data_byte_array_type is array  (BYTES-1 downto 0) of std_logic_vector (BYTE_WIDTH-1 downto 0);
signal data_in1_byte_array  : data_byte_array_type;
signal data_in2_byte_array  : data_byte_array_type;
signal data_out1_byte_array : data_byte_array_type;
signal data_out2_byte_array : data_byte_array_type;

signal data_in1_pack  : std_logic_vector (BYTES*BYTE_WIDTH-1 downto 0);
signal data_in2_pack  : std_logic_vector (BYTES*BYTE_WIDTH-1 downto 0);
signal data_out1_pack : std_logic_vector (BYTES*BYTE_WIDTH-1 downto 0);
signal data_out2_pack : std_logic_vector (BYTES*BYTE_WIDTH-1 downto 0);

begin
BYTES_1_generate : if BYTES = 1 generate
data_in1_pack  <= data_in1_byte_array(0);
data_in2_pack  <= data_in2_byte_array(0);
data_out1_pack <= data_out1_byte_array(0);
data_out2_pack <= data_out2_byte_array(0);
end generate BYTES_1_generate;

BYTES_2_generate : if BYTES = 2 generate
data_in1_pack  <= data_in1_byte_array(1) & data_in1_byte_array(0);
data_in2_pack  <= data_in2_byte_array(1) & data_in2_byte_array(0);
data_out1_pack <= data_out1_byte_array(1) & data_out1_byte_array(0);
data_out2_pack <= data_out2_byte_array(1) & data_out2_byte_array(0);
end generate BYTES_2_generate;

BYTES_4_generate : if BYTES = 4 generate
data_in1_pack  <= data_in1_byte_array(3) & data_in1_byte_array(2) & data_in1_byte_array(1) & data_in1_byte_array(0);
data_in2_pack  <= data_in2_byte_array(3) & data_in2_byte_array(2) & data_in2_byte_array(1) & data_in2_byte_array(0);
data_out1_pack <= data_out1_byte_array(3) & data_out1_byte_array(2) & data_out1_byte_array(1) & data_out1_byte_array(0);
data_out2_pack <= data_out2_byte_array(3) & data_out2_byte_array(2) & data_out2_byte_array(1) & data_out2_byte_array(0);
end generate BYTES_4_generate;

unpack : for i in 0 to BYTES - 1 generate
data_out1_byte_array(i) <= ram( to_integer( unsigned( addr1 ) ) ) ( (i+1)*BYTE_WIDTH-1 downto i*BYTE_WIDTH ) ;
data_out2_byte_array(i) <= ram( to_integer( unsigned( addr2 ) ) ) ( (i+1)*BYTE_WIDTH-1 downto i*BYTE_WIDTH ) ;

process( we1(i), data_in1, addr1 )
begin
if we1(i) = '1' then
data_in1_byte_array(i) <= data_in1((i+1)*BYTE_WIDTH-1 downto i*BYTE_WIDTH );
else
data_in1_byte_array(i) <= ram(  to_integer( unsigned( addr1 ) ) ) ( (i+1)*BYTE_WIDTH-1 downto i*BYTE_WIDTH ) ;
end if;
end process;

process( we2(i), data_in2, addr2 )
begin
if we2(i) = '1' then
data_in2_byte_array(i) <= data_in2((i+1)*BYTE_WIDTH-1 downto i*BYTE_WIDTH );
else
data_in2_byte_array(i) <= ram(  to_integer( unsigned( addr2 ) ) ) ( (i+1)*BYTE_WIDTH-1 downto i*BYTE_WIDTH ) ;
end if;
end process;

end generate unpack;

process(clk1)
begin
if(rising_edge(clk1)) then
if(en1 = '1') then
ram(  to_integer( unsigned( addr1 ) ) ) := data_in1_pack;
data_out1 <= data_out1_pack;
end if;
end if;
end process;

process(clk2)
begin
if(rising_edge(clk2)) then
if(en2 = '1') then
ram(  to_integer( unsigned( addr2 ) ) ) := data_in2_pack;
data_out2 <= data_out2_pack;
end if;
end if;
end process;

end rtl;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: