Commit c0614a39 authored by Grzegorz Daniluk's avatar Grzegorz Daniluk

Merge branch 'greg_ram_init' into proposed_master

parents b833b5d8 0973db51
......@@ -45,6 +45,7 @@ package genram_pkg is
function f_log2_size (A : natural) return natural;
function f_gen_dummy_vec (val : std_logic; size : natural) return std_logic_vector;
function f_zeros (size : integer) return std_logic_vector;
function f_check_bounds(x : integer; minx : integer; maxx : integer) return integer;
type t_generic_ram_init is array (integer range <>, integer range <>) of std_logic;
......@@ -244,5 +245,15 @@ package body genram_pkg is
return std_logic_vector(to_unsigned(0, size));
end f_zeros;
function f_check_bounds(x : integer; minx : integer; maxx : integer) return integer is
begin
if(x < minx) then
return minx;
elsif(x > maxx) then
return maxx;
else
return x;
end if;
end f_check_bounds;
end genram_pkg;
......@@ -12,10 +12,6 @@ package memory_loader_pkg is
subtype t_meminit_array is t_generic_ram_init;
function f_hexchar_to_slv (c : character) return std_logic_vector;
function f_hexstring_to_slv (s : string; n_digits : integer) return std_logic_vector;
function f_get_token(s : string; n : integer) return string;
function f_load_mem_from_file
(file_name : string;
mem_size : integer;
......@@ -27,128 +23,22 @@ end memory_loader_pkg;
package body memory_loader_pkg is
function f_hexchar_to_slv (c : character) return std_logic_vector is
variable t : std_logic_vector(3 downto 0);
begin
case c is
when '0' => t := x"0";
when '1' => t := x"1";
when '2' => t := x"2";
when '3' => t := x"3";
when '4' => t := x"4";
when '5' => t := x"5";
when '6' => t := x"6";
when '7' => t := x"7";
when '8' => t := x"8";
when '9' => t := x"9";
when 'a' => t := x"a";
when 'A' => t := x"a";
when 'b' => t := x"b";
when 'B' => t := x"b";
when 'c' => t := x"c";
when 'C' => t := x"c";
when 'd' => t := x"d";
when 'D' => t := x"d";
when 'e' => t := x"e";
when 'E' => t := x"e";
when 'f' => t := x"f";
when 'F' => t := x"f";
when others =>
report "f_hexchar_to_slv(): unrecognized character '" &c&" in hex text string" severity failure;
end case;
return t;
end f_hexchar_to_slv;
function f_hexstring_to_slv (s : string; n_digits : integer) return std_logic_vector is
variable tmp : std_logic_vector(255 downto 0) := (others => '0');
begin
if s'length > tmp'length then
report "f_hexstring_to_slv(): string length exceeds the limit" severity failure;
end if;
for i in 0 to s'length-1 loop
tmp(4 * (s'length - i) - 1 downto 4 * (s'length - 1 - i)) := f_hexchar_to_slv(s(i+1));
end loop; -- i
return tmp(n_digits * 4 - 1 downto 0);
end f_hexstring_to_slv;
function f_get_token(s : string; n : integer) return string is
variable cur_pos : integer;
variable tmp : string (1 to 128);
variable cur_token : integer;
variable tmp_pos : integer;
begin
cur_pos := 1;
cur_token := 1;
tmp_pos := 1;
loop
if(cur_pos >= s'length) then
return "";
end if;
while cur_pos <= s'length and (s(cur_pos) = ' ' or s(cur_pos) = character'val(9) or s(cur_pos) = character'val(0)) loop
cur_pos := cur_pos + 1;
end loop;
if(cur_pos >= s'length) then
return "";
end if;
while(cur_pos <= s'length and s(cur_pos) /= ' ' and s(cur_pos) /= character'val(9) and s(cur_pos) /= character'val(0)) loop
if(cur_token = n) then
tmp(tmp_pos) := s(cur_pos);
tmp_pos := tmp_pos + 1;
end if;
cur_pos := cur_pos + 1;
end loop;
if(cur_token = n) then
return tmp(1 to tmp_pos-1);
end if;
cur_token := cur_token + 1;
if(cur_pos >= s'length) then
return "";
end if;
end loop;
return "";
end f_get_token;
function f_load_mem_from_file
(file_name : string;
mem_size : integer;
mem_width : integer;
(file_name : in string;
mem_size : in integer;
mem_width : in integer;
fail_if_notfound : boolean)
return t_meminit_array is
file f_in : text;
variable l : line;
variable ls : string(1 to 128);
variable cmd : string(1 to 128);
variable line_len : integer;
FILE f_in : text;
variable l : line;
variable tmp_bv : bit_vector(mem_width-1 downto 0);
variable tmp_sv : std_logic_vector(mem_width-1 downto 0);
variable mem: t_meminit_array(0 to mem_size-1, mem_width-1 downto 0);
variable status : file_open_status;
variable mem : t_meminit_array(0 to mem_size-1, mem_width-1 downto 0);
variable i : integer;
variable c : character;
variable good : boolean;
variable addr : integer;
variable data_tmp : unsigned(mem_width-1 downto 0);
variable data_int : integer;
begin
if(file_name = "" or file_name = "none") then
mem := (others => (others => '0'));
mem:= (others => (others => '0'));
return mem;
end if;
......@@ -162,44 +52,18 @@ package body memory_loader_pkg is
end if;
end if;
while true loop
i := 0;
while (i < 4096) loop
-- stupid ISE restricts the loop length
readline(f_in, l);
line_len := 0;
loop
read(l, ls(line_len+1), good);
exit when good = false;
line_len := line_len + 1;
end loop;
if(line_len /= 0 and f_get_token(ls, 1) = "write") then
addr := to_integer(unsigned(f_hexstring_to_slv(f_get_token(ls, 2), 8)));
data_tmp := resize(unsigned(f_hexstring_to_slv(f_get_token(ls, 3), 8)), mem_width);
data_int := to_integer(data_tmp);
-- report "addr: " & integer'image(addr) & " data: " & integer'image(data_int);
for i in 0 to mem_width-1 loop
mem(addr, i) := std_logic(data_tmp(i));
end loop; -- i in 0 to mem_width-1
-- report "addr: " & integer'image(addr) & " data: " & integer'image(data_int);
end if;
if endfile(f_in) then
file_close(f_in);
return mem;
end if;
i := i+1;
for I in 0 to mem_size-1 loop
readline (f_in, l);
-- read function gives us bit_vector
read (l, tmp_bv);
tmp_sv := to_stdlogicvector(tmp_bv);
for J in 0 to mem_width-1 loop
mem(i, j) := tmp_sv(j);
end loop;
end loop;
file_close(f_in);
return mem;
end f_load_mem_from_file;
end memory_loader_pkg;
......@@ -3,6 +3,7 @@ files = [
"generic_dpram_sameclock.vhd",
"generic_dpram_dualclock.vhd",
"generic_simple_dpram.vhd",
"generic_dpram_split.vhd",
"generic_spram.vhd",
"gc_shiftreg.vhd"]
......@@ -76,6 +76,33 @@ end generic_dpram;
architecture syn of generic_dpram is
constant c_gen_split :boolean := (g_dual_clock = false and g_data_width=32 and
g_with_byte_enable=true and (g_addr_conflict_resolution="dont_care" or
g_addr_conflict_resolution="read_first"));
constant c_gen_sc :boolean := (not c_gen_split) and (not g_dual_clock);
constant c_gen_dc :boolean := g_dual_clock;
component generic_dpram_split
generic (
g_size : natural;
g_addr_conflict_resolution : string := "dont_care";
g_init_file : string := "none";
g_fail_if_file_not_found : boolean := true);
port (
rst_n_i : in std_logic := '1';
clk_i : in std_logic;
bwea_i : in std_logic_vector(3 downto 0);
wea_i : in std_logic;
aa_i : in std_logic_vector(f_log2_size(g_size)-1 downto 0);
da_i : in std_logic_vector(31 downto 0);
qa_o : out std_logic_vector(31 downto 0);
bweb_i : in std_logic_vector(3 downto 0);
web_i : in std_logic;
ab_i : in std_logic_vector(f_log2_size(g_size)-1 downto 0);
db_i : in std_logic_vector(31 downto 0);
qb_o : out std_logic_vector(31 downto 0));
end component;
component generic_dpram_sameclock
generic (
g_data_width : natural;
......@@ -125,7 +152,36 @@ architecture syn of generic_dpram is
begin
gen_single_clk : if(g_dual_clock = false) generate
-- generic_dpram_split is like generic_dpram_sameclock, but hardcoded to
-- 32-bit data width and split into 4 BRAMs, each of them 8-bit wide. It's
-- better for Xilinx ISE, because it's unable to infer DPRAM with byte-write
-- enables without using huge number of LUTs.
-- Since it's hardcoded to 32-bits data width, we need to keep
-- generic_dpram_sameclock as well. For reasons why generic_dpram_split is
-- hardcoded to 32-bits please check the Note in generic_dpram_split.vhd.
gen_splitram: if c_gen_split generate
U_RAM_SPLIT: generic_dpram_split
generic map(
g_size => g_size,
g_addr_conflict_resolution => g_addr_conflict_resolution,
g_init_file => g_init_file,
g_fail_if_file_not_found => g_fail_if_file_not_found)
port map(
rst_n_i => rst_n_i,
clk_i => clka_i,
bwea_i => bwea_i,
wea_i => wea_i,
aa_i => aa_i,
da_i => da_i,
qa_o => qa_o,
bweb_i => bweb_i,
web_i => web_i,
ab_i => ab_i,
db_i => db_i,
qb_o => qb_o);
end generate gen_splitram;
gen_single_clk : if c_gen_sc generate
U_RAM_SC: generic_dpram_sameclock
generic map (
g_data_width => g_data_width,
......@@ -151,7 +207,7 @@ begin
end generate gen_single_clk;
gen_dual_clk : if(g_dual_clock = true) generate
gen_dual_clk : if c_gen_dc generate
U_RAM_DC: generic_dpram_dualclock
generic map (
g_data_width => g_data_width,
......
......@@ -117,20 +117,8 @@ architecture syn of generic_dpram_sameclock is
signal s_we_b : std_logic_vector(c_num_bytes-1 downto 0);
signal s_ram_in_b : std_logic_vector(g_data_width-1 downto 0);
signal wea_rep, web_rep : std_logic_vector(c_num_bytes-1 downto 0);
function f_check_bounds(x : integer; minx : integer; maxx : integer) return integer is
begin
if(x < minx) then
return minx;
elsif(x > maxx) then
return maxx;
else
return x;
end if;
end f_check_bounds;
begin
wea_rep <= (others => wea_i);
......
-------------------------------------------------------------------------------
-- Title : Dual-port synchronous RAM with byte-write for Xilinx
-------------------------------------------------------------------------------
-- File : generic_dpram_split.vhd
-- Author : Grzegorz Daniluk
-- Company : CERN BE-CO-HT
-- Created : 2017-02-13
-- Last update: 2017-02-13
-- Platform :
-- Standard : VHDL'93
-------------------------------------------------------------------------------
-- Description:
-- This module is 32-bit RAM with byte-write enables. It was created for Xilinx
-- FPGAs, since Xilinx ISE is unable to infer dual-port block-RAM with
-- byte-writes (e.g. based on generic_dpram_sameclock.vhd module). When
-- synthesizing generic_dpram_sameclock with g_with_byte_enable, ISE uses a lot
-- of LUTs to get the byte-write behavior (instead of using the features of BRAM
-- blocks).
--
-- Note:
-- This module is hardcoded to 32-bits and 4 ram modules (ram0-3). It would be
-- much cleaner to have a generic code and using ram(0 to g_data_width/8-1).
-- However, it looks that ISE is not able to initialize 3-d array that would be
-- needed in this case.
-- I.e. this works:
-- shared variable ram0 : t_split_ram := f_memarray_to_ramtype(f_file_contents, 0);
-- shared variable ram1 : t_split_ram := f_memarray_to_ramtype(f_file_contents, 1);
-- shared variable ram2 : t_split_ram := f_memarray_to_ramtype(f_file_contents, 2);
-- shared variable ram3 : t_split_ram := f_memarray_to_ramtype(f_file_contents, 3);
--
-- but this doesn't:
-- type t_split_ram_array is array(0 to 3) of t_split_ram;
-- shared variable ram : t_split_ram_array := (f_memarray_to_ramtype(f_file_contents, 0),
-- f_memarray_to_ramtype(f_file_contents, 1),f_memarray_to_ramtype(f_file_contents, 2),
-- f_memarray_to_ramtype(f_file_contents, 3));
--
-- By "doesn't work" I mean that ISE does not fail during the synthesis, but RAM
-- does not get initialized.
-------------------------------------------------------------------------------
-- Copyright (c) 2017 CERN
--
-- This source file is free software; you can redistribute it
-- and/or modify it under the terms of the GNU Lesser General
-- Public License as published by the Free Software Foundation;
-- either version 2.1 of the License, or (at your option) any
-- later version.
--
-- This source is distributed in the hope that it will be
-- useful, but WITHOUT ANY WARRANTY; without even the implied
-- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-- PURPOSE. See the GNU Lesser General Public License for more
-- details.
--
-- You should have received a copy of the GNU Lesser General
-- Public License along with this source; if not, download it
-- from http://www.gnu.org/licenses/lgpl-2.1.html
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.genram_pkg.all;
use work.memory_loader_pkg.all;
entity generic_dpram_split is
generic (
g_size : natural := 16384;
g_addr_conflict_resolution : string := "read_first";
g_init_file : string := "";
g_fail_if_file_not_found : boolean := true);
port (
rst_n_i : in std_logic := '1';
clk_i : in std_logic;
-- Port A
bwea_i : in std_logic_vector(3 downto 0);
wea_i : in std_logic;
aa_i : in std_logic_vector(f_log2_size(g_size)-1 downto 0);
da_i : in std_logic_vector(31 downto 0);
qa_o : out std_logic_vector(31 downto 0);
-- Port B
bweb_i : in std_logic_vector(3 downto 0);
web_i : in std_logic;
ab_i : in std_logic_vector(f_log2_size(g_size)-1 downto 0);
db_i : in std_logic_vector(31 downto 0);
qb_o : out std_logic_vector(31 downto 0));
end generic_dpram_split;
architecture syn of generic_dpram_split is
constant c_data_width : integer := 32;
constant c_num_bytes : integer := (c_data_width+7)/8;
type t_split_ram is array(0 to g_size-1) of std_logic_vector(7 downto 0);
function f_memarray_to_ramtype(arr : t_meminit_array; idx : integer) return t_split_ram is
variable tmp : t_split_ram;
variable n, pos : integer;
begin
pos := 0;
while(pos < g_size)loop
n := 0;
-- avoid ISE loop iteration limit
while (pos < g_size and n<4096) loop
for i in 0 to 7 loop
tmp(pos)(i) := arr(pos, i+idx*8);
end loop;
n := n+1;
pos := pos + 1;
end loop;
end loop;
return tmp;
end f_memarray_to_ramtype;
function f_file_contents return t_meminit_array is
begin
return f_load_mem_from_file(g_init_file, g_size, c_data_width, g_fail_if_file_not_found);
end f_file_contents;
shared variable ram0 : t_split_ram := f_memarray_to_ramtype(f_file_contents, 0);
shared variable ram1 : t_split_ram := f_memarray_to_ramtype(f_file_contents, 1);
shared variable ram2 : t_split_ram := f_memarray_to_ramtype(f_file_contents, 2);
shared variable ram3 : t_split_ram := f_memarray_to_ramtype(f_file_contents, 3);
signal s_we_a : std_logic_vector(c_num_bytes-1 downto 0);
signal s_we_b : std_logic_vector(c_num_bytes-1 downto 0);
signal wea_rep : std_logic_vector(c_num_bytes-1 downto 0);
signal web_rep : std_logic_vector(c_num_bytes-1 downto 0);
begin
assert (g_addr_conflict_resolution = "read_first" or g_addr_conflict_resolution = "dont_care")
report "generic_dpram_split: only read_first and dont_care supported for now" severity failure;
-- combine byte-write enable with write signals
wea_rep <= (others => wea_i);
web_rep <= (others => web_i);
s_we_a <= bwea_i and wea_rep;
s_we_b <= bweb_i and web_rep;
--------------------------------------------------
-- yes, I know this is 4 times exactly the same code for ram0,1,2,3,
-- but ISE fails to initialize BRAM when ram is an array (check Note
-- in the header of this file).
GEN_RAM0: process (clk_i)
begin
if rising_edge(clk_i) then
qa_o(7 downto 0) <= ram0(f_check_bounds(to_integer(unsigned(aa_i)), 0, g_size-1));
qb_o(7 downto 0) <= ram0(f_check_bounds(to_integer(unsigned(ab_i)), 0, g_size-1));
if s_we_a(0) = '1' then
ram0(f_check_bounds(to_integer(unsigned(aa_i)), 0, g_size-1)) := da_i(7 downto 0);
end if;
if s_we_b(0) = '1' then
ram0(f_check_bounds(to_integer(unsigned(ab_i)), 0, g_size-1)) := db_i(7 downto 0);
end if;
end if;
end process;
GEN_RAM1: process (clk_i)
begin
if rising_edge(clk_i) then
qa_o(15 downto 8) <= ram1(f_check_bounds(to_integer(unsigned(aa_i)), 0, g_size-1));
qb_o(15 downto 8) <= ram1(f_check_bounds(to_integer(unsigned(ab_i)), 0, g_size-1));
if s_we_a(1) = '1' then
ram1(f_check_bounds(to_integer(unsigned(aa_i)), 0, g_size-1)) := da_i(15 downto 8);
end if;
if s_we_b(1) = '1' then
ram1(f_check_bounds(to_integer(unsigned(ab_i)), 0, g_size-1)) := db_i(15 downto 8);
end if;
end if;
end process;
GEN_RAM2: process (clk_i)
begin
if rising_edge(clk_i) then
qa_o(23 downto 16) <= ram2(f_check_bounds(to_integer(unsigned(aa_i)), 0, g_size-1));
qb_o(23 downto 16) <= ram2(f_check_bounds(to_integer(unsigned(ab_i)), 0, g_size-1));
if s_we_a(2) = '1' then
ram2(f_check_bounds(to_integer(unsigned(aa_i)), 0, g_size-1)) := da_i(23 downto 16);
end if;
if s_we_b(2) = '1' then
ram2(f_check_bounds(to_integer(unsigned(ab_i)), 0, g_size-1)) := db_i(23 downto 16);
end if;
end if;
end process;
GEN_RAM3: process (clk_i)
begin
if rising_edge(clk_i) then
qa_o(31 downto 24) <= ram3(f_check_bounds(to_integer(unsigned(aa_i)), 0, g_size-1));
qb_o(31 downto 24) <= ram3(f_check_bounds(to_integer(unsigned(ab_i)), 0, g_size-1));
if s_we_a(3) = '1' then
ram3(f_check_bounds(to_integer(unsigned(aa_i)), 0, g_size-1)) := da_i(31 downto 24);
end if;
if s_we_b(3) = '1' then
ram3(f_check_bounds(to_integer(unsigned(ab_i)), 0, g_size-1)) := db_i(31 downto 24);
end if;
end if;
end process;
end syn;
......@@ -55,13 +55,6 @@ end xwb_dpram;
architecture struct of xwb_dpram is
function f_zeros(size : integer)
return std_logic_vector is
begin
return std_logic_vector(to_unsigned(0, size));
end f_zeros;
signal s_wea : std_logic;
signal s_web : std_logic;
signal s_bwea : std_logic_vector(3 downto 0);
......@@ -72,9 +65,6 @@ architecture struct of xwb_dpram is
signal slave2_in : t_wishbone_slave_in;
signal slave2_out : t_wishbone_slave_out;
begin
U_Adapter1 : wb_slave_adapter
generic map (
......@@ -108,11 +98,6 @@ begin
master_i => slave2_out,
master_o => slave2_in);
GEN_INITF: if g_init_file /= "" and g_init_file /= "none" generate
-- Unfortunately stupid ISE has problem with understanding bytesel
-- description in generic_dpram so it instantiates this using numerous LUTs
-- for connecting BRAMs and supporting bytesel. When initialization with
-- file is not needed it's better to use GEN_NO_INITF.
U_DPRAM : generic_dpram
generic map(
-- standard parameters
......@@ -140,38 +125,6 @@ begin
db_i => slave2_in.dat,
qb_o => slave2_out.dat
);
end generate;
GEN_NO_INITF: if g_init_file = "" or g_init_file = "none" generate
-- This trick splits ram into four 8-bit blocks of RAM. Now the problem ISE
-- has with understanding correctly bytesel is bypassed and the
-- implementation takes almost none LUTs, just BRAMs.
GEN_BYTESEL: for i in 0 to 3 generate
U_DPRAM: generic_dpram
generic map(
g_data_width => 8,
g_size => g_size,
g_with_byte_enable => false,
g_addr_conflict_resolution => "dont_care",
g_init_file => "",
g_dual_clock => false)
port map(
rst_n_i => rst_n_i,
-- Port A
clka_i => clk_sys_i,
wea_i => s_bwea(i),
aa_i => slave1_in.adr(f_log2_size(g_size)-1 downto 0),
da_i => slave1_in.dat((i+1)*8-1 downto i*8),
qa_o => slave1_out.dat((i+1)*8-1 downto i*8),
-- Port B
clkb_i => clk_sys_i,
web_i => s_bweb(i),
ab_i => slave2_in.adr(f_log2_size(g_size)-1 downto 0),
db_i => slave2_in.dat((i+1)*8-1 downto i*8),
qb_o => slave2_out.dat((i+1)*8-1 downto i*8)
);
end generate;
end generate;
-- I know this looks weird, but otherwise ISE generates distributed RAM instead of block
-- RAM
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment