---------------------------------------------------------------- -- Chapter 17 Case Study: A Package for Memories ---------------------------------------------------------------- ---------------------------------------------------------------- -- 17.1 The Memories Package ---------------------------------------------------------------- library ieee; use ieee.std_logic_1164.std_ulogic_vector; package memories is generic ( width : positive; depth : positive; type control_type; type address_type; type data_type; pure function "??" (c : control_type) return boolean is <>; function rising_edge(signal c : control_type) return boolean is <>; pure function to_integer (a : address_type) return natural is <>; pure function to_address_type (a : natural) return address_type is <>; pure function to_std_ulogic_vector (d : data_type) return std_ulogic_vector is <>; pure function to_data_type (d : std_ulogic_vector) return data_type is <> ); type RAM_type is array (0 to 2**depth - 1) of data_type; procedure read_RAM (signal RAM : in RAM_type; constant address : in address_type; signal data : out data_type); procedure write_RAM (signal RAM : out RAM_type; constant address : in address_type; constant data : in data_type); procedure asynch_SRAM (signal RAM : in RAM_type; signal wr : in control_type; signal address : in address_type; signal data_in : out data_type; signal data_out : out data_type); procedure flow_through_SSRAM (signal RAM : in RAM_type; signal clk, en, wr : in control_type; signal address : in address_type; signal data_in : out data_type; signal data_out : out data_type); procedure pipelined_SSRAM (signal RAM : in RAM_type; signal clk, en, wr : in control_type; signal address : in address_type; signal data_in : out data_type; signal data_out : out data_type); procedure dump_RAM (signal RAM : in RAM_type; constant file_name : in string; constant start_address : in address_type := to_address_type(0); constant finish_address : in address_type := to_address_type(2**depth - 1)); impure function load_RAM (constant file_name : in string; constant start_address : in address_type := to_address_type(0); constant finish_address : in address_type := to_address_type(2**depth - 1)) return RAM_type; end package memories; package body memories is procedure read_RAM (signal RAM : in RAM_type; constant address : in address_type; signal data : out data_type) is begin assert to_integer(address) <= 2**depth - 1; data <= RAM(to_integer(address)); end procedure read_RAM; procedure write_RAM (signal RAM : out RAM_type; constant address : in address_type; constant data : in data_type) is begin assert to_integer(address) <= 2**depth - 1; RAM(to_integer(address)) <= data; end procedure write_RAM; procedure asynch_SRAM (signal RAM : in RAM_type; signal wr : in control_type; signal address : in address_type; signal data_in : out data_type; signal data_out : out data_type) is begin loop if wr then write_RAM(RAM, address, data_in); data_out <= data_in; else read_RAM(RAM, address, data_out); end if; wait on wr, address, data_in; end loop; end procedure asynch_SRAM; procedure flow_through_SSRAM (signal RAM : in RAM_type; signal clk, en, wr : in control_type; signal address : in address_type; signal data_in : out data_type; signal data_out : out data_type) is begin loop if rising_edge(clk) then if en then if wr then write_RAM(RAM, address, data_in); data_out <= data_in; else read_RAM(RAM, address, d_out); end if; end if; end if; wait on clk; end loop; end procedure flow_through_SSRAM; procedure pipelined_SSRAM (signal RAM : in RAM_type; signal clk, en, wr : in control_type; signal address : in address_type; signal data_in : out data_type; signal data_out : out data_type) is variable pipelined_en : control_type; variable pipelined_data_out : data_type; begin loop if rising_edge(clk) then if pipelined_en then data_out <= pipelined_data_out; end if; pipelined_en := en; if en then if wr then write_RAM(RAM, address, data_in); pipelined_data_out := data_in; else assert to_integer(address) <= 2**depth - 1; pipelined_data_out := RAM(to_integer(address)); end if; end if; end if; wait on clk; end loop; end procedure pipelined_SSRAM; use std.textio.all; use ieee.numeric_std.all; procedure dump_RAM (signal RAM : in RAM_type; constant file_name : in string; constant start_address : in address_type := to_address_type(0); constant finish_address : in address_type := to_address_type(2**depth - 1)) is file dump_file : text; variable status : file_open_status; variable L : line; constant start_address_int : natural := to_integer(start_address); constant finish_address_int : natural := to_integer(finish_address); variable address : natural; begin if start_address_int >= 2**depth - 1 then report "dump_RAM: start address " & to_hstring(start_address_int) & " out of range" severity error; return; end if; assert finish_address_int <= 2**depth - 1 then report "dump_RAM: finish address " & to_hstring(finish_address_int) & " out of range" severity error; return; end if; file_open(f => dump_file, external_name => file_name, open_kind => write_mode, status => status); if status /= open_ok then report "dump_RAM: " & to_string(status) " opening file " & file_name severity error; return; end if; -- Write the start address write(L, '@'); hwrite(L, to_unsigned(to_integer(start_address), depth)); writeline(dump_file, L); -- Write the data for address in to_integer(start_address) to to_integer(finish_address) loop hwrite(L, to_std_ulogic_vector(RAM(address))); writeline(dump_file, L); end loop; file_close(f => dump_file); end procedure dump_RAM; impure function load_RAM (constant file_name : in string; constant start_address : in address_type := to_address_type(0); constant finish_address : in address_type := to_address_type(2**depth - 1)) return RAM_type is file load_file : text; variable status : file_open_status; variable L : line := null; variable ok : boolean; constant start_address_int : natural := to_integer(start_address); constant finish_address_int : natural := to_integer(finish_address); variable next_address : natural := to_integer(start_address); variable next_address_unsigned : unsigned; variable data_sulv : std_ulogic_vector(0 to width); variable RAM : RAM_type; procedure skip_whitespace_and_comments is variable skipping_block_comment : boolean := false; variable whitespace_char, comment_char : character; begin loop if L = null or L'length = 0 then if endfile(load_file) then if skipping_block_comment then report "load_RAM: unterminated block comment in file" & file_name; ok := false; else ok := true; end if; return; else readline(load_file, L); end if; elsif L(L'left) = ' ' or L(L'left) = ' ' or L(L'left) = HT or L(L'left) = FF then read(L, whitespace_char); elsif skipping_block_comment then read(L, comment_char); if comment_char = '*' and L /= null and L'length /= 0 and L(L'left) = '/' then read(L, comment_char); skipping_block_comment := false; end if; elsif L(L'left) = '/' then read(L, comment_char); read(L, comment_char, ok); if not ok then report "load_RAM: error reading comment in file " & file_name; return; -- ok is false end if; if comment_char = '/' then -- single-line comment deallocate(L); -- skip rest of comment line elsif comment_char = '*' then -- block comment skipping_block_comment := true; else report "load_RAM: malformed comment in file " & file_name; ok := false; return; end if; else -- not whitespace or comment ok := true; return; end if; end loop; end procedure skip_whitespace_and_comments; begin if start_address_int > 2**depth - 1 then report "dump_RAM: start address " & to_hstring(start_address_int) & " out of range"; return RAM; end if; if finish_address_int > 2**depth - 1 then report "dump_RAM: finish address " & to_hstring(finish_address_int) & " out of range"; return RAM; end if; file_open(f => load_file, external_name => file_name, open_kind => read_mode, status => status); if status /= open_ok then report "load_RAM: " & to_string(status) " opening file " & file_name; return RAM; end if; -- code to read and parse memory file contents loop skip_whitespace_and_comments; exit when not ok or endfile(load_file); if L(L'left) = '@' then -- address follows read(L, at_char); hread(L, next_address_unsigned, ok); if not ok then report "load_RAM: error reading address in file " & file_name; exit; end if; next_address := to_integer(next_address_unsigned); else -- data number -- read the number hread(L, data_sulv, ok); if not ok then report "load_RAM: error reading data at address " & to_hstring(next_address) & " in file " & file_name; exit; end if; if next_address > finish_address_int or next_address < start_address_int then report "load_RAM: address " & to_hstring(next_address) & " out of range " & to_hstring(start_address_int) & " to " & to_hstring(finish_address_int) & LF & " in file " & file_name; ok := false; exit; RAM(next_address) := to_data_type(data_sulv); next_address := next_address + 1; end if; end loop; file_close(f => load_file); return RAM; end procedure load_RAM; end package body memories; ---------------------------------------------------------------- -- 17.2 Using the Memories Package ---------------------------------------------------------------- library ieee; use ieee.std_logic_1164.all, ieee.numeric_bit.all; entity instruction_ROM is generic ( file_name : string ); port ( address : in unsigned(16 downto 0); d_out : out std_ulogic_vector(15 downto 0) ); end entity instruction_ROM; library ieee; use ieee.std_logic_1164.all, ieee.numeric_bit.all; entity data_RAM is port ( clk, en, wr : in std_ulogic; address : in unsigned(14 downto 0); d_in : in std_ulogic_vector(31 downto 0); d_out : out std_ulogic_vector(31 downto 0) ); end entity data_RAM; library ieee; use ieee.std_logic_1164.all, ieee.numeric_bit.all; package embedded_memories is constant instruction_width : positive := 16; constant instruction_depth : positive := 17; constant data_width : positive := 32; constant data_depth : positive := 15; function to_instruction_address_type ( n : natural ) return unsigned; function to_data_address_type ( n : natural ) return unsigned; function to_std_ulogic_vector ( d : std_ulogic_vector ) return std_ulogic_vector; package instruction_memories is new work.memories generic map ( width => instruction_width, depth => instruction_depth, control_type => std_ulogic, address_type => unsigned(19 downto 0), data_type => std_ulogic_vector(15 downto 0), "??" => ieee.std_logic_1164."??", rising_edge => ieee.std_logic_1164.rising_edge, to_integer => ieee.numeric_std.to_integer, to_address_type => to_instruction_address_type, to_std_ulogic_vector => to_std_ulogic_vector, to_data_type => to_std_ulogic_vector ); package data_memories is new work.memories generic map ( width => data_width, depth => data_depth, control_type => std_ulogic, address_type => unsigned(17 downto 0), data_type => std_ulogic_vector(31 downto 0), "??" => ieee.std_logic_1164."??", rising_edge => ieee.std_logic_1164.rising_edge, to_integer => ieee.numeric_std.to_integer, to_address_type => to_data_address_type, to_std_ulogic_vector => to_std_ulogic_vector, to_data_type => to_std_ulogic_vector ); end package embedded_memories; package body embedded_memories is function to_instruction_address_type ( n : natural ) return unsigned is begin return ieee.numeric_std_to_unsigned(n, instruction_depth); end function to_instruction_address_type; function to_data_address_type ( n : natural ) return unsigned is begin return ieee.numeric_std_to_unsigned(n, data_depth); end function to_data_address_type; function to_std_ulogic_vector ( d : std_ulogic_vector ) return std_ulogic_vector is begin return d; end function to_std_ulogic_vector; end package body embedded_memories; architecture file_loaded of instruction_ROM is use work.embedded_memories.instruction_memories.all; signal ROM : RAM_type := load_RAM(file_name); begin read_RAM(ROM, address, d_out); end architecture file_loaded; architecture rtl of data_RAM is use work.embedded_memories.data_memories.all; signal RAM : RAM_type; begin flow_through_SSRAM(RAM, clk, en, wr, address, d_in, d_out); end architecture rtl; entity embedded_system is generic ( code_file_name : string ); port ( ... ); end entity embedded_system; ---------------------------------------------------------------- architecture struct of embedded_system is ... begin instr : entity work.instruction_ROM(file_loaded) generic map ( file_name => code_file_name ) port map ( ... ); data : entity work.data_RAM(rtl) port map ( ... ); ... end architecture struct; entity testbench is end entity testbench; library ieee; use ieee.std_logic_1164.all, ieee.numeric_bit.all; architecture dump_data of testbench is ... begin dut : entity work.embedded_system(struct) generic map ( code_file_name => "testbench.vmem" ) port map ( ... ); dumper : process is use work.embedded_memories.data_memories.all; alias RAM is <>; begin wait until dump_triggered; dump_RAM(RAM, "testbench-dump.vmem"); wait; end process dumper; end architecture dump_data; ---------------------------------------------------------------- -- 17.2.1 Common Address and Data Conversions ---------------------------------------------------------------- library ieee; use ieee.all; use std_logic_1164.std_ulogic_vector; package memories_support is generic ( width : positive; depth : positive; package fixed_pkg is new fixed_generic_pkg generic map ( <> ); package float_pkg is new float_generic_pkg generic map ( <> ) ); -- Conversions for actual types for address_type: -- natural, std_ulogic_vector, unsigned pure function to_integer (a: natural) return natural; alias to_integer is ieee.numeric_std_unsigned.to_integer; alias to_integer is ieee.numeric_std.to_integer [ieee.numeric_std.unsigned return natural]; pure function to_address_type (a : natural) return natural; pure function to_address_type (a : natural) return std_ulogic_vector; pure function to_address_type (a : natural) return numeric_std.unsigned; -- Conversions for actual types for data_type: -- natural, std_ulogic_vector, unsigned, signed, -- ufixed, sfixed, float pure function to_std_ulogic_vector (d : natural) return std_ulogic_vector; pure function to_std_ulogic_vector (d : std_ulogic_vector) return std_ulogic_vector; pure function to_std_ulogic_vector (d : numeric_std.unsigned) return std_ulogic_vector; pure function to_std_ulogic_vector (d : numeric_std.signed) return std_ulogic_vector; alias to_std_ulogic_vector is fixed_pkg.to_std_ulogic_vector [fixed_pkg.ufixed return std_ulogic_vector]; alias to_std_ulogic_vector is fixed_pkg.to_std_ulogic_vector [fixed_pkg.sfixed return std_ulogic_vector]; alias to_std_ulogic_vector is float_pkg.to_std_ulogic_vector [float_pkg.float return std_ulogic_vector]; pure function to_data_type (d : std_ulogic_vector) return natural; pure function to_data_type (d : std_ulogic_vector) return std_ulogic_vector; pure function to_data_type (d : std_ulogic_vector) return numeric_std.unsigned; pure function to_data_type (d : std_ulogic_vector) return numeric_std.signed; pure function to_data_type_generic_ufixed generic ( left_index, right_index : integer ) parameter (d : std_ulogic_vector) return fixed_pkg.ufixed; pure function to_data_type_generic_sfixed generic ( left_index, right_index : integer ) parameter (d : std_ulogic_vector) return fixed_pkg.sfixed; pure function to_data_type_generic_float generic ( exponent_width : natural := float_pkg.float_exponent_width; fraction_width : natural := float_pkg.float_fraction_width) parameter (d : std_ulogic_vector) return float_pkg.float; end package memories_support; package body memories_support is pure function to_integer (a: natural) return natural is begin return a; end function to_integer; pure function to_address_type (a : natural) return natural is begin return a; end function to_address_type; pure function to_address_type (a : natural) return std_ulogic_vector is begin return numeric_std_unsigned.to_std_ulogic_vector(a, depth); end function to_address_type; pure function to_address_type (a : natural) return numeric_std.unsigned is begin return numeric_std.to_unsigned(a, depth); end function to_address_type; pure function to_std_ulogic_vector (d : natural) return std_ulogic_vector is begin return numeric_std_unsigned.to_std_ulogic_vector(d, width); end function to_std_ulogic_vector; pure function to_std_ulogic_vector (d : std_ulogic_vector) return std_ulogic_vector is begin return d; end function to_std_ulogic_vector; pure function to_std_ulogic_vector (d : numeric_std.unsigned) return std_ulogic_vector is begin return std_ulogic_vector(d); end function to_std_ulogic_vector; pure function to_std_ulogic_vector (d : numeric_std.signed) return std_ulogic_vector is begin return std_ulogic_vector(d); end function to_std_ulogic_vector; pure function to_data_type (d : std_ulogic_vector) return natural is begin return numeric_std_unsigned.to_integer(d); end function to_data_type; pure function to_data_type (d : std_ulogic_vector) return std_ulogic_vector is begin return d; end function to_data_type; pure function to_data_type (d : std_ulogic_vector) return numeric_std.unsigned is begin return numeric_std.unsigned(d); end function to_data_type; pure function to_data_type (d : std_ulogic_vector) return numeric_std.signed is begin return numeric_std.signed(d); end function to_data_type; pure function to_data_type_generic_ufixed generic ( left_index, right_index : integer ) parameter (d : std_ulogic_vector) return fixed_pkg.sfixed is begin return fixed_pkg.to_ufixed(d, left_index, right_index); end function to_data_type; pure function to_data_type_generic_sfixed generic ( left_index, right_index : integer ) parameter (d : std_ulogic_vector) return fixed_pkg.sfixed is begin return fixed_pkg.to_sfixed(d, left_index, right_index); end function to_data_type; pure function to_data_type_generic_float generic ( exponent_width : natural := float_pkg.float_exponent_width; fraction_width : natural := float_pkg.float_fraction_width) parameter (d : std_ulogic_vector) return float_pkg.float is begin return float_pkg.to_float(d, exponent_width, fraction_width); end function to_data_type; end package body memories_support; library ieee; use ieee.std_logic_1164.all, ieee.numeric_bit.all; package embedded_memories is constant instruction_width : positive := 16; constant instruction_depth : positive := 17; constant data_width : positive := 32; constant data_depth : positive := 15; package instruction_support is new work.memories_support generic map ( width => instruction_width, depth => instruction_depth, fixed_pkg => ieee.fixed_pkg, float_pkg => ieee.float_pkg ); package instruction_memories is new work.memories generic map ( width => instruction_width, depth => instruction_depth, control_type => std_ulogic, address_type => unsigned(19 downto 0), data_type => std_ulogic_vector(15 downto 0), to_integer => instruction_support.to_integer, to_address_type => instruction_support.to_address_type, to_std_ulogic_vector => instruction_support.to_std_ulogic_vector, to_data_type => instruction_support.to_data_type ); package data_support is new work.memories_support generic map ( width => data_width, depth => data_depth, fixed_pkg => ieee.fixed_pkg, float_pkg => ieee.float_pkg ); package data_memories is new work.memories generic map ( width => data_width, depth => data_depth, control_type => std_ulogic, address_type => unsigned(17 downto 0), data_type => std_ulogic_vector(31 downto 0), to_integer => data_support.to_integer, to_address_type => data_support.to_address_type, to_std_ulogic_vector => data_support.to_std_ulogic_vector, to_data_type => data_support.to_data_type ); end package embedded_memories; library ieee; use ieee.fixed_float_types.all; package dsp_fixed_pkg is new ieee.fixed_generic_pkg generic map ( fixed_round_style => fixed_truncate, fixed_overflow_style => fixed_saturate, fixed_guard_bits => 2, no_warning ); library ieee; use ieee.std_logic_1164.all, ieee.numeric_bit.all; use work.dsp_fixed_pkg.all; package dsp_data_pkg is package dsp_data_support is new work.memories_support generic map ( width => 16, depth => 12, fixed_pkg => work.dsp_fixed_pkg, float_pkg => ieee.float_pkg ); use dsp_data_support.all; function to_data_type is new to_data_type_generic_sfixed generic map ( left_index => 4, right_index => -11 ); package dsp_data_memories is new work.memories generic map ( width => 16, depth => 12, control_type => std_ulogic, address_type => unsigned(11 downto 0), data_type => sfixed(4 downto -11) ); end package dsp_data_pkg; library ieee; use ieee.std_logic_1164.all, ieee.numeric_bit.all; use work.dsp_fixed_pkg.all; entity dsp_data_RAM is port ( clk, en, wr : in std_ulogic; address : in unsigned(11 downto 0); d_in : in sfixed(4 downto -11); d_out : out sfixed(4 downto -11) ); end entity dsp_data_RAM; ---------------------------------------------------------------- architecture rtl of dsp_data_RAM is use work.dsp_data_pkg.dsp_data_memories.all; signal RAM : RAM_type; begin pipelined_SSRAM(RAM, clk, en, wr, address, d_in, d_out); end architecture rtl; ---------------------------------------------------------------- -- Exercises ----------------------------------------------------------------