I need to get some data from a module I was given, but I don't know if it is even possible or how to approach the problem.
Is it possible to get information from another module if that module doesn't have an entity map? It only has a generic with TIME statements.
Is it at all possible to get anything out of this module?
It writes to a memory, could I pull things out of that?
This is the file I have.
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all;
use IEEE.STD_LOGIC_ARITH.all;
use STD.TEXTIO.all;
use IEEE.STD_LOGIC_TEXTIO.all;
entity MIPS is
generic (
MEM_DLY : TIME := 0.5 ns;
CYC_TIME: TIME := 2 ns
);
end entity MIPS;
architecture MIPS of MIPS is
signal PC : STD_LOGIC_VECTOR ( 31 downto 0 ) := X"0000_0010";
signal READ_DATA2 : STD_LOGIC_VECTOR ( 31 downto 0 ) := ( others => '0');
signal HUH : BIT_VECTOR ( 31 downto 0 );
signal HUHINS : STRING ( 1 to 25 );
signal INSTRUC : STD_LOGIC_VECTOR ( 31 downto 0 );
signal M_DATA_IN : STD_LOGIC_VECTOR ( 31 downto 0 ) := ( others => 'Z');
signal M_DATA_OUT : STD_LOGIC_VECTOR ( 31 downto 0 ):= ( others => 'Z');
signal M_ADDR : STD_LOGIC_VECTOR ( 11 downto 0 ) := ( others => '0');
signal CLK : STD_LOGIC := '0';
signal MEMREAD : STD_LOGIC := '0';
signal M_DATA_WHEN : STD_LOGIC := '0';
signal MEMWRITE : STD_LOGIC := '0';
signal CYCLE : INTEGER := 1;
begin
CLOCK_PROC:
process
begin
CLK <= '1';
wait for CYC_TIME/2;
CLK <= '0';
wait for CYC_TIME/2;
CYCLE <= CYCLE + 1;
end process;
TEST_PC_PROC:
process ( CLK ) is
begin
if RISING_EDGE ( CLK ) then
PC <= PC + 4;
end if;
end process;
INSTR_MEM_PROC:
process ( PC ) is -- make subject only to address
type INSTR_STR_ARY is array ( 0 to 1023 ) of STRING ( 1 to 25 );
variable MEMSTRR : INSTR_STR_ARY:=(others => " ");
type MEMORY is array ( 0 to 1023 ) of BIT_VECTOR ( 31 downto 0 );
variable MEM : MEMORY := ( others => X"0000_0000");
variable IADDR : INTEGER; -- integer for address
variable DTEMP : BIT_VECTOR ( 31 downto 0 );
variable INIT : INTEGER := 0; -- when to initialize...
file IN_FILE : TEXT open READ_MODE is "instr_mem.txt";
variable BUF : LINE;
variable ADR_STR : STD_LOGIC_VECTOR ( 31 downto 0 );
variable TADR : INTEGER;
variable TDATA : STD_LOGIC_VECTOR ( 31 downto 0 );
variable BDATA : BIT_VECTOR ( 31 downto 0 );
variable STR_ING : STRING ( 1 to 25 );
begin
if INIT = 0 then
while not (ENDFILE ( IN_FILE )) loop
READLINE ( IN_FILE, BUF );
HREAD ( BUF, ADR_STR ); -- get the address on the line
TADR := CONV_INTEGER ( ADR_STR (14 downto 2));
HREAD ( BUF, TDATA ); -- get the data on the line
BDATA := To_bitvector (TDATA);
MEM ( TADR ) := BDATA; -- put into memory
for J in 1 to 25 loop
STR_ING(J) := ' ';
end loop;
READ ( BUF, STR_ING ); -- get instruction string
MEMSTRR ( TADR ) := STR_ING;
report "iteration of loop";
end loop;
INIT := 1; -- when all data in, set INIT to 1;
end if; -- end of INIT check
IADDR := CONV_INTEGER ( PC ( 14 downto 2 ));
HUH <= MEM ( IADDR );
INSTRUC <= To_StdLogicVector ( MEM ( IADDR )) after MEM_DLY;
HUHINS <= MEMSTRR ( IADDR );
report "should hit INSTRUC";
end process;
M_DATA_IN_STMT:
M_DATA_IN <= READ_DATA2 ;
-- The following is the magic process
-- User must supply:
-- M_ADDR - memory address (data memory) as a 12 bit STD_LOGIC_VECTOR
-- Remember the M_ADDR is a WORD address
-- M_DATA_IN - value going to memory from hardware (data path)
-- Remember that this is 32 bit STD_LOGIC_VECTOR, user supplied
-- READ_DATA2 - this is to be replaced by user's sourceof info for memory
DATA_MEMORY_PROCESS: -- name of process ...
process ( M_ADDR, CLK, MEMREAD ) is -- Sens: M_ADDR, CLK, MEMREAD
file IN_FILE: TEXT open READ_MODE is "data_mem_init.txt"; -- initial data
file OUT_FILE: TEXT open WRITE_MODE is "mem_trans.txt"; -- results
variable BUF : LINE; -- declare BUF as LINE
variable TVAL : STD_LOGIC_VECTOR ( 31 downto 0 ); -- var for temp value
variable TADRHEX : STD_LOGIC_VECTOR ( 31 downto 0 ); -- var for address
variable TADR : INTEGER; -- address as integer
type MEM_TYPE is array ( 0 to 1023 ) of STD_LOGIC_VECTOR ( 31 downto 0 );
variable THE_MEMORY : MEM_TYPE := ( others => X"00000000" ); -- the memory
variable FIRST : BOOLEAN := TRUE; -- flag for first time thru
constant STR : STRING ( 1 to 3 ) := " "; -- 3 spaces - for printing
constant WR_STR : STRING ( 1 to 3 ) := "W "; -- for write
constant RD_STR : STRING ( 1 to 3 ) := "R "; -- for read
variable TSTR2 : STRING ( 1 to 29 ); -- to create a string
type MEMSTR_TYPE is array ( 0 to 1023 ) of STRING ( 1 to 29 ); --
variable INSTRS : MEMSTR_TYPE;
begin -- start here
if FIRST then -- first time thru,
while FIRST loop -- loop on data available - until
if not ( ENDFILE ( IN_FILE )) then -- end of file shows up
READLINE(IN_FILE, BUF); -- read a line from file,
HREAD(BUF, TADRHEX); -- get address from BUF
TADR := CONV_INTEGER ( TADRHEX ); -- turn it into integer
HREAD(BUF, TVAL); -- next, get value from BUF
THE_MEMORY(TADR/4) := TVAL; -- put TVAL into the memory
else -- the 'else' is for end of file
FIRST := FALSE; -- EOF shows up - set FIRST false
end if;
end loop; -- where loop ends...
end if; -- where if FIRST ends ...
if MEMREAD = '1' then -- now, memory function 'read'
M_DATA_OUT <= THE_MEMORY ( CONV_INTEGER ( M_ADDR ) / 4 ); -- get val from
M_DATA_WHEN <= not M_DATA_WHEN; -- and invert M_DATA_WHEN
else -- if not MEMREAD,
M_DATA_OUT <= ( others => 'Z' ); -- set memory out to 'Z's
end if;
if RISING_EDGE ( CLK ) then -- on clock edge...
if MEMREAD = '1' then -- if MEMREAD asserted,
TADR := CONV_INTEGER ( M_ADDR ) / 4; -- set TADR to address as int
TVAL := THE_MEMORY ( TADR ); -- and get contents to TVAL
WRITE (BUF, RD_STR); -- then build BUF; put read indi
HWRITE (BUF, M_ADDR); -- and the address
WRITE (BUF, STR); -- some spaces
HWRITE (BUF, TVAL); -- and the value
WRITE (BUF, STR); -- more spaces
WRITE (BUF, NOW); -- current simulation time
WRITELINE (OUT_FILE, BUF); -- and send line to file.
elsif MEMWRITE = '1' then -- if not read, but it is write
TADR := CONV_INTEGER ( M_ADDR ) / 4; -- set TADR to address as int
TVAL := M_DATA_IN; -- set TVAL as data in value
WRITE (BUF, WR_STR); -- start buffer with write indi
HWRITE (BUF, M_ADDR); -- then the address
WRITE (BUF, STR); -- then some spaces
HWRITE (BUF, TVAL); -- and the value written
WRITE (BUF, STR); -- still more spaces
WRITE (BUF, NOW); -- simulation time
WRITELINE (OUT_FILE, BUF); -- and send line to file
THE_MEMORY ( CONV_INTEGER ( M_ADDR ) / 4) := M_DATA_IN;
-- and finally, value to the mem
end if;
end if;
end process;
end architecture MIPS;
The code you presented simulates the memories that your MIPS processor will interact with - a program memory and a data memory.
Your MIPS will interact with the program memory by providing a value for PC; the corresponding instruction will be handed to your CPU on signal INSTRUCT. You'll probably delete the lines corresponding to the TEST_PC_PROC process, since the actual PC value will come from the MIPS. The program to be run by the CPU is given in file data_mem_init.txt. This program memory is asynchronous.
Your MIPS will interact with the data memory through signals M_ADDR, M_DATA_OUT, M_DATA_IN, and MEMREAD. To read data, your CPU will set M_ADDR and MEMREAD=1, and provide the address in M_ADDR. The given code will set M_DATA_OUT with the requested data. To write data, you will set M_DATA_IN or READ_DATA2 (or replace READ_DATA2 with a signal of your choice). The data will be written on the rising edge of CLK.
Don't be distracted by the WRITE/HWRITE calls, they just keep a log on file mem_trans.txt.
IMO, this interface is much more complicated than it needed to be. You're probably better or if you keep your MIPS implementation in totally separate files, and just add the signals needed to interact with this model to its ports list.
It's not entirely clear from your question what you are hoping to achieve with this mystery module that you have... but here's some ideas which might trigger something:
If you have a component for the module in question, then you can instantiate it within your design and then manipulate its inputs to make its outputs do what they should. Maybe it has some documentation to give you some clues!
If it writes to memory and you have a multi port memory controller within your system connected to the same memory, you could build something which will read data from the memory after your mystery module has written to it.
Or finally, if this is an FPGA, you can embed a logic analyser into the FPGA bitstream to observe the signals going to and from the secret module.
Related
I have a question related to the implementation of a clocked SRAM memory which is is supposed to store data written by user and then display the memory content. In addition, I created a module named display which eases the reading process so that there is no need to provide the memory address from which data should be extracted, by incrementing a register after each reading. Nevertheless, when I simulate the circuit, I cannot see the correct output; thus, when I read the memory, I notice some undefined output sequences. I posted the corresponding code below.
//stores output data
module sram_1port_data(
input clk,//clocked memory
input wr_en,//when high, data is written, otherwise is read
input [15:0] address_in,//suppose timer cannout count more than 13ms
input [3:0] wr_data,//memory does not sotre values greater than 13(ms)
output reg [3:0] rd_data
);
reg [3:0] memory [2 ** 15 - 1 : 0];
always #(posedge clk) begin
if(wr_en) memory[address_in] <= wr_data;
else rd_data <= memory[address_in];
end
endmodule
//display interfacedesigned for the second memory
module display(
input clk,
input wr_en,
input [15:0] address_in,
input [3:0] wr_data,
output [3:0] rd_data
);
reg [15:0] pointer,address;
initial pointer = 16'd0;
sram_1port_data i0(.clk(clk),.wr_en(wr_en),.address_in(address),.wr_data(wr_data),.rd_data(rd_data));
always #(posedge clk) begin
if(!wr_en) begin
address <= pointer;
pointer <= pointer + 1;
end
else address <= address_in;
end
endmodule
//tb for display
module display_tb(
output reg clk,
output reg wr_en,
output reg [15:0] address_in,
output reg [3:0] wr_data,
output [3:0] rd_data
);
display i0(.clk(clk),.wr_en(wr_en),.address_in(address_in),.wr_data(wr_data),.rd_data(rd_data));
initial $dumpvars(0,display_tb);
initial begin
clk = 1'd1;
repeat (2000)
#100 clk = ~clk;
end
initial begin
wr_en = 1'd1;
#100000 wr_en = 1'd0;
end
integer i;
initial begin
wr_data = 3'd0;
for(i = 1;i < 500;i = i + 1) begin
#200 wr_data = i;
end
end
initial begin
address_in = 16'd0;
for(i = 1;i < 500;i = i + 1) begin
#200 address_in = i;
end
end
endmodule
When I look at waveforms, I see that rd_data is unknown (X) on every other read. Looking at an internal address signal, I see that the unknowns are only for even addresses. Early in the simulation, you are only writing to even addresses.
In your testbench, you are using the same variable (i) in 2 different for loops. In your 2nd for loop, use a different variable name (j, for example):
integer j;
initial begin
address_in = 16'd0;
for(j = 1;j < 500;j = j + 1) begin
#200 address_in = j;
end
end
Now, all reads have known values because it writes to all addresses (even and odd).
You could also set the address inside the same for loop as the write data. This is likely a better approach in this case.
initial begin
wr_data = 3'd0;
address_in = 16'd0;
for(i = 1;i < 500;i = i + 1) begin
#200;
wr_data = i;
address_in = i;
end
end
I encountered an issue trying to design a SRAM memory. To be more specific, the memory is clocked, has a write enable - when high, one could write data and when low, one could read data - , an address input, which specifies the memory address to/from which data is written/read. Then, I created a module named user, which facilitates the write operation; thus, there is no need to provide a memory address when writing data.
My problem occurs when I try to simulate the circuit, because there cannot be seen anything when accessing the content of the memory. Within the test bench, I specified some values to be stored in the memory, then, I extracted data, but no success.
I attached the code here.
//stores instructions
module sram_1port_instructions(
input clk,//clocked memory
input wr_en,//when high, data is writeen, otherwise is read
input [15:0] address_in,//suppose timer cannot count more than 13ms
input [2:0] wr_data,//3 bit instructions
output reg [2:0] rd_data
);
reg [2:0] memory [2 ** 15 - 1 : 0];
always #(posedge clk) begin
if(wr_en) memory[address_in] <= wr_data;
else rd_data <= memory[address_in];
end
endmodule
//user interface designed for the first memory
module user(
input clk,
input wr_en,
input [15:0] address_in,
input [2:0] wr_data,
output [2:0] rd_data
);
reg [15:0] pointer,address;
initial pointer = 16'd0;
sram_1port_instructions i0(.clk(clk),.wr_en(wr_en),.address_in(address_in),.wr_data(wr_data),.rd_data(rd_data));
always #(posedge clk) begin
if(wr_en) begin
address <= pointer;
pointer <= pointer + 1;
end
else address <= address_in;
end
endmodule
//user tb
module user_tb(
output reg clk, wr_en,
output reg [15:0] address_in,
output reg [2:0] wr_data,
output [2:0] rd_data
);
user cut(.clk(clk),.wr_en(wr_en),.address_in(address_in),.wr_data(wr_data),.rd_data(rd_data));
initial $dumpvars(0,user_tb);
initial begin
clk = 1'd1;
repeat (2000)
#100 clk = ~clk;
end
initial begin
wr_en = 1'd1;
#100000 wr_en = 1'd0;
end
integer i;
initial begin
wr_data = 3'd0;
for(i = 1;i < 500;i = i + 1) begin
#200 wr_data = i;
end
end
initial begin
address_in = 16'd0;
#100000 address_in = 16'd0;
for(i = 1;i < 500;i = i + 1) begin
#200 address_in = i;
end
end
endmodule
When I run your simulation and look at waveforms, I see rd_data=3 at time 100000. At that time, you read address 0, and that is the last value you wrote to address 0. Otherwise, all your reads of other addresses return X (unknown). When you do all your writes, you only write to address 0. From time=0 to time=100000, wr_en=1, and address_in=0 (inside your sram_1port_instructions module). You can see this when you look at the waveforms in your VCD file.
wr_data changes every clock cycle, but you write to the same address every cycle.
But, in user, if you connect the address signal to the to address_in port of the sram_1port_instructions module, you will write to different addresses.
Change:
sram_1port_instructions i0(.clk(clk),.wr_en(wr_en),.address_in(address_in),.wr_data(wr_data),.rd_data(rd_data));
to:
sram_1port_instructions i0(.clk(clk),.wr_en(wr_en),.address_in(address),.wr_data(wr_data),.rd_data(rd_data));
Your code has the constant address_in signal connected to the address_in port.
When I make that change, I see all different values being read from the RAM.
I have a problem with translating VHDL to Verilog.
It's part of my source code on VHDL.
With I/O I somehow understood, but have some problems to translate this string
ib1 <= std_logic_vector(to_unsigned(i,ib1'length));
to verilog?
COMPONENT GenerateModel
PORT(
ib1 : IN std_logic_vector(3 downto 0);
);
END COMPONENT;
--Inputs
signal ib1 : std_logic_vector(3 downto 0) := (others => '0');
BEGIN
uut: GenerateModel PORT MAP (
ib1 => ib1,
);
process
begin
for i in 0 to 15 loop
ib1 <= std_logic_vector(to_unsigned(i,ib1'length));
wait for 10 ns;
end loop;
end process;
end;
To extend into Verilog from Paebbels' comment, the line you are looking at does an explicit conversion from the type of the loop variable i to the port variable ib1. In Verilog, that explicit conversion is not needed, you can just assign the port variable directly. So, for example (in Verilog IEEE 1364-1995 compatible):
integer i;
...
for (i = 0; i < 16; i = i + 1) begin
ib1 = i; // <-- The line
#10; // -- Assume 1 step is 1 ns, can specific timescale if needed
end
If you want, you can even loop through the variable directly if its of type reg (ie, not a net):
for (ib1 = 0; ib1 < 15; ib1 = ib1 + 1) begin
#10;
end
#10;
[Note that as Greg mentioned, you need to be sure you dont create an infinite loop as if ib1 is 4-bits wide, it will always be less than 16, thus I fixed the example above to loop until ib1 is 15 (4'b1111)]
I am trying to split a string using an integer array as mask.
The task is simple but I am not accustomed to ADA (which is a constraint).
Here is my code. It works exept that I have an one character offset when testing against a file. Can someone help me remove this offset. it is drinving me nuts.
generic_functions.adb :
package body Generic_Functions is
-- | Sums up the elements of an array of Integers
function Sum_Arr_Int(Arr_To_Sum: Int_Array) return Integer is
Sum: Integer;
begin
Sum := 0;
for I in Arr_To_Sum'Range loop
Sum := Sum + Arr_To_Sum(I);
end loop;
return Sum;
end Sum_Arr_Int;
-- | Split up a String into a array of Unbounded_String following pattern from an Int_Array
function Take_Enregistrements(Decoup_Tab: Int_Array; Str_To_Read: String) return Str_Array is
Previous, Next : Integer;
Arr_To_Return : Str_Array(Decoup_Tab'Range);
begin
if Sum_Arr_Int(Decoup_Tab) > Str_To_Read'Length then
raise Constraint_Error;
else
Previous := Decoup_Tab'First;
Next := Decoup_Tab(Decoup_Tab'First);
for I in Decoup_Tab'Range loop
if I /= Decoup_Tab'First then
Previous := Next + 1;
Next := (Previous - 1) + Decoup_Tab(I);
end if;
Arr_To_Return(I) := To_Unbounded_String(Str_To_Read(Previous..Next));
end loop;
return Arr_To_Return;
end if;
end Take_Enregistrements;
end Generic_Functions;
generic_functions.ads :
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
package Generic_Functions is
-- | Types
type Int_Array is array(Positive range <>) of Integer;
type Str_Array is array(Positive range <>) of Unbounded_String;
-- | end of Types
-- | Functions
function Sum_Arr_Int(Arr_To_Sum: Int_Array) return Integer;
function Take_Enregistrements(Decoup_Tab: Int_Array; Str_To_Read: String) return Str_Array;
-- | end of Functions
end Generic_Functions;
generic_functions_tests.adb :
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Generic_Functions; use Generic_Functions;
procedure Generic_Functions_Tests is
-- | Variables
Decoup_Test : constant Int_Array(1..8) := (11, 19, 60, 24, 255, 10, 50, 255);
Test_Str_Arr : Str_Array(Decoup_Test'Range);
Test_Str_Arr2 : Str_Array(Decoup_Test'Range);
Test_Str_Arr3 : Str_Array(Decoup_Test'Range);
--Test_Int : Integer;
Test_Handle : File_Type;
-- | end of Variables
begin
Open(Test_Handle, In_File, "EXPORTFINAL.DAT");
Test_Str_Arr := Take_Enregistrements(Decoup_Test, Get_Line(Test_Handle));
Test_Str_Arr2 := Take_Enregistrements(Decoup_Test, Get_Line(Test_Handle));
Test_Str_Arr3 := Take_Enregistrements(Decoup_Test, Get_Line(Test_Handle));
for I in Test_Str_Arr'Range loop
Put_Line(To_String(Test_Str_Arr(I)));
end loop;
for I in Test_Str_Arr2'Range loop
Put_Line(To_String(Test_Str_Arr2(I)));
end loop;
for I in Test_Str_Arr3'Range loop
Put_Line(To_String(Test_Str_Arr3(I)));
end loop;
-- for I in Test_Str_Arr'Range loop
-- Test_Int := To_String(Test_Str_Arr(I))'Length;
-- Put_Line(Integer'Image(Test_Int));
-- end loop;
-- for I in Test_Str_Arr2'Range loop
-- Test_Int := To_String(Test_Str_Arr2(I))'Length;
-- Put_Line(Integer'Image(Test_Int));
-- end loop;
-- for I in Test_Str_Arr3'Range loop
-- Test_Int := To_String(Test_Str_Arr3(I))'Length;
-- Put_Line(Integer'Image(Test_Int));
-- end loop;
Close(Test_Handle);
end Generic_Functions_Tests;
and finaly the file:
000000000012012-01-01 10:00:00 IBM IBM COMPAGNIE IBM FRANCE 17 AVENUE DE l'EUROPE 92275 BOIS-COLOMBES CEDEX CONFIGURATION COMPLETE SERVEUR000000000000000000000019 .6000000000001000000000000000000001000.00000000000000000000000000000196.00000000000000000000000000001196.00000000
000000000022012-01-01 11:00:00 MICROSOFT MSC 39 QUAI DU PRESIDENT ROOSEVELT 92130 ISSY-LES-MOULINEAUX AMENAGEMENT SALLE INFORMATIQUE000000000000000000000019.6000000000001000000000000000000001000.00000000000000000000000000000196.00000000000000000000000000001196.00000000
000000000032012-01-01 12:00:00 MICROSOFT MSC 39 QUAI DU PRESIDENT ROOSEVELT 92130 ISSY-LES-MOULINEAUX TESTS SUR SITE000000000000000000000019.6000000000001000000000000000000003226.52000000000000000000000000000632.39792000000000000000000000003858.91792000 DELEGATION TECHNICIEN HARD000000000000000000000019.60000000000000000000000000000001.00000000000000000000000000001000.00000000000000000000000000000196.00000000000000000000000000001196.00000000
These lines:
if I = Decoup_Tab'Last then
Arr_To_Return(I) := To_Unbounded_String(Str_To_Read(Previous..Next));
end if;
will overwrite the last element in your array.
Also, are you sure that the line number (00000000001, 00000000002, etc) is one of the strings you want to split based on the integer mask? As your code is right now, you use '11' twice, once for the line number and once for the date-field. If you skip the line number, the other numbers seem to make more sense.
I have this piece of IP that is supposed to be a 32 bits byte addressable memory. But I can't make it infer block rams, it is inferring a huge amount of flip flops...
It is supposed to fit on a Spartan3e (xc3s1200e-4fg320) which has only dual port block rams, indeed the memory is split into two arrays in an even-odd arrange...
Here is the code, I hope that might help understand what am I doing wrong?
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
package mem_types is
type memory_t is array (natural range <>) of std_logic_vector(7 downto 0);
end mem_types;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use work.mem_types.all;
---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity ram is
generic (
INIT : memory_t(0 to 4095) := (others => (others => '0'))
);
port ( clk, rst : in std_logic;
addr : in std_logic_vector(11 downto 0);
din : in std_logic_vector(31 downto 0);
dout : out std_logic_vector(31 downto 0);
we : std_logic_vector(3 downto 0)
);
end ram;
architecture Behavioral of ram is
type ramport_t is record
addr : std_logic_vector(10 downto 0);
dout : std_logic_vector(7 downto 0);
din : std_logic_vector(7 downto 0);
wea : std_logic;
end record;
signal port0a, port0b, port1a, port1b : ramport_t;
signal addr_a, addr_b, addr_c, addr_d : std_logic_vector(11 downto 0);
signal memory0, memory1 : memory_t(0 to 2047);
begin
addr_a <= addr;
addr_b <= addr+1;
addr_c <= addr+2;
addr_d <= addr+3;
port0a.addr <= addr_a(11 downto 1) when addr_a(0) = '0' else addr_b(11 downto 1);
port1a.addr <= addr_b(11 downto 1) when addr_b(0) = '1' else addr_a(11 downto 1);
port0b.addr <= addr_c(11 downto 1) when addr_c(0) = '0' else addr_d(11 downto 1);
port1b.addr <= addr_d(11 downto 1) when addr_d(0) = '1' else addr_c(11 downto 1);
dout(07 downto 00) <= port0a.dout when addr_a(0) = '0' else port1a.dout;
dout(15 downto 08) <= port1a.dout when addr_b(0) = '1' else port0a.dout;
dout(23 downto 16) <= port0b.dout when addr_c(0) = '0' else port1b.dout;
dout(31 downto 24) <= port1b.dout when addr_d(0) = '1' else port0b.dout;
port0a.din <= din(07 downto 00) when addr_a(0) = '0' else din(15 downto 08);
port1a.din <= din(15 downto 08) when addr_b(0) = '1' else din(07 downto 00);
port0b.din <= din(23 downto 16) when addr_c(0) = '0' else din(31 downto 24);
port1b.din <= din(31 downto 24) when addr_d(0) = '1' else din(23 downto 16);
port0a.wea <= we(0) when addr_a(0) = '0' else we(1);
port1a.wea <= we(1) when addr_b(0) = '1' else we(0);
port0b.wea <= we(2) when addr_c(0) = '0' else we(3);
port1b.wea <= we(3) when addr_d(0) = '1' else we(2);
port0a.dout <= memory0(conv_integer(port0a.addr));
port0b.dout <= memory0(conv_integer(port0b.addr));
port1a.dout <= memory1(conv_integer(port1a.addr));
port1b.dout <= memory1(conv_integer(port1b.addr));
process (clk, rst)
begin
if rst = '1' then
for a in 0 to 2047 loop
memory0(a) <= INIT(a*2);
end loop;
elsif falling_edge(clk) then
if (port0a.wea = '1') then
memory0(conv_integer(port0a.addr)) <= port0a.din;
end if;
if (port0b.wea = '1') then
memory0(conv_integer(port0b.addr)) <= port0b.din;
end if;
end if;
end process;
process (clk, rst)
begin
if rst = '1' then
for a in 0 to 2047 loop
memory1(a) <= INIT((a*2)+1);
end loop;
elsif falling_edge(clk) then
if (port1a.wea = '1') then
memory1(conv_integer(port1a.addr)) <= port1a.din;
end if;
if (port1b.wea = '1') then
memory1(conv_integer(port1b.addr)) <= port1b.din;
end if;
end if;
end process;
end Behavioral;
This is described in the Xilinx Synthesis Guide under Coding for FPGA Flow. I'm almost certain that reset loop is causing flops to be inferred. That code requires accessing all elements of the memory simultaneously, which is not possible with Block RAM.
You can't do this:
process (clk, rst)
begin
if rst = '1' then
for a in 0 to 2047 loop
memory0(a) <= INIT(a*2);
end loop;
...as that is asking for a resettable memory, rather than an initialised one.
To initialise, you need to change your signal declaration to be of the form
signal memory0 : memory_t(0 to 2047) := ( some list of integers or something that returns an array of integers);
The way you are currently doing it (with your init's interleaved) means you'll have to use a function:
function init_mem(init_values: memory_t) returns memory_t is
variable retval : memory_t(init_values'high/2)+1 downto 0);
begin
for i in retval'range loop
retval(i) := init_values(2*i);
end for;
end function;
(note that that was typed off the top of my head and I've not even tried to compile it, so apologies for any typos and syntax errors... but I hope you get the idea :)
Then you can use this to init the signal:
signal memory0 : memory_t(0 to 2047) := init_mem(INIT);
This will all work for simulation. You may or may not have success with the XST synthesiser inferring the INIT values - I haven't tried. Check the synthesis logfile to see what it reports - and please do report back to us whether it works and on what version of XST you tried it.