I’m learning VHDL, and what better way to learn than to have a project in it. So, the part of my project now is creating a little memory component.
Here is my full code:
entity memory is
port(
Address: in std_logic_vector(15 downto 0); --memory address
Write: in std_logic; --write or read
UseTopBits: in std_logic; --if 1, top 8 bits of data is ignored and not written to memory
Clock: in std_logic;
DataIn: in std_logic_vector(15 downto 0);
DataOut: out std_logic_vector(15 downto 0);
Reset: in std_logic
);
end memory;
architecture Behavioral of memory is
constant SIZE : integer := 4096;
type memorytype is array(0 to (size-1)) of std_logic_vector(7 downto 0);
signal mem: memorytype;
begin
resetmem: process(Clock, Reset) --this process is the troublemaker
begin
if(Reset ='1' and rising_edge(Clock)) then
mem <= (others => "00000000");
end if;
end process;
writemem: process(Reset,Write, Address, UseTopBits, Clock)
variable addr: integer;
begin
addr := conv_integer(Address);
if(addr>size-1) then
addr:=0;
end if;
if(Write='1' and Reset='0') then
if(rising_edge(clock)) then
mem(conv_integer(addr)) <= DataIn(7 downto 0);
if(UseTopBits='1') then
mem(conv_integer(addr)+1) <= DataIn(15 downto 8);
end if;
end if;
end if;
end process;
readmem: process(Reset,Address,Write,Clock)
variable addr: integer;
begin
addr := conv_integer(Address);
if(addr>size-1) then
addr:=0;
end if;
if(Reset='1') then
DataOut <= (others => '0');
elsif(Write='0') then
DataOut <= mem(conv_integer(addr)+1) & mem(conv_integer(addr));
else
DataOut <= (others => '0');
end if;
end process;
end Behavioral;
Before adding the Reset process, my code was working fine according to my test benches. Now after adding it I get all sorts of weirdness on DataOut. Seemingly random bits will have the logic state of X instead of a 0 or 1 and this causes all of my testbenches to fail (as it should)
I fixed it by putting the code into the writemem process as so:
begin
--where resetmem process was
writemem: process(Reset,Write, Address, UseTopBits, Clock)
variable addr: integer;
begin
addr := conv_integer(Address);
if(addr>size-1) then
addr:=0;
end if;
if(Reset ='1' and rising_edge(Clock)) then --code is now here
mem <= (others => "00000000");
elsif(Write='1' and Reset='0') then
if(rising_edge(clock)) then
mem(conv_integer(addr)) <= DataIn(7 downto 0);
if(UseTopBits='1') then
mem(conv_integer(addr)+1) <= DataIn(15 downto 8);
end if;
end if;
end if;
end process;
So the issue is fixed now, but I don’t understand why having resetmem as a separate process caused all of these odd problems. Can anyone shed some light as to how this happened?
VHDL has this concept of “drivers” on signals.
Each process that writes to the signal (or part of it for a bus) creates a driver. When multiple drivers are connected to a signal a “resolution function” is called which decides what the resultant value should be. It happens that (for a first approximation) the resolution function for
std_logic(and hence for astd_logic_vectors bits) produces Xs when drivers with opposing values (1and0) are driven onto a signal. There is also a value calledZwhich is treated as a non-driving value which can be overridden by other values. (and there areW,LandH, but we’ll leave those aside for now, andUfor uninitialised)This process:
Says (paraphrased)
It doesn’t say anything about what to do if the condition is false, and therefore it continues to drive zeros (for the rest of time). When your other process drives values, they clash and produce
XsFor simulation purposes, you could do
which would make it “drive” a high-impedance which the other process could override.
However I think what you are trying to do is initialise the RAM to all zeros. FPGA RAMs cannot be reset with a signal to a particular value, but they can be loaded with values at configuration time. The simulator knows nothing about the configuration process, you simulations are deemed to be happening after configuration has finished.
Therefore, one way to simulate this behaviour is to initialise the signal when you declare it: