VHDL signal assignment delay and simulation confusion - vhdl

New to VHDL and trying to implement a small cache.
Part of my cache.vhd
entity cache is
Port ( clock : in STD_LOGIC;
rw_sel : in STD_LOGIC; --'1' to read from cache ,
--'0' to write to cache
ad_sel: in STD_LOGIC; --'0' to send address, '1' to send data
data_in :in STD_LOGIC_VECTOR (7 downto 0);
--send data or address controled by ad_sel
...
end cache;
architecture Behavioral of cache is
....
signal block_addr_n : integer range 0 to 15;
-- cache block address
signal block_cell_addr_n: integer range 0 to 1;
-- byte-in-cache address
begin
process(clock, init)
begin
case init is
when '0' =>
for i in 0 to 15 loop
cache_memory(i)<="1111111111111111";
tag_memory(i)<="11";
end loop;
when others =>null;
end case;
case ad_sel is
when '0' =>
address<=data_in(6 downto 0);
when '1' => data_cpu_wr<=data_in;
when others =>null;
end case;
block_addr_n <= conv_integer(unsigned(address(4 downto 1)));
block_cell_addr_n<=conv_integer(address(0));
case rw_sel is
....
part of my testbench file :
....
--Inputs
signal clock : std_logic := '0';
signal rw_sel : std_logic := '1';
signal ad_sel : std_logic := '0';
signal init: std_logic:='1';
signal data_in : std_logic_vector(7 downto 0) := (others => '0');
....
stim_proc: process
begin
-- initialize
init<='0';
wait for 100 ns;
-- read address "0101111"(MSB cut), expecting miss
cache_mem_data<="1100110011001100";
ad_sel<='0';
rw_sel<='1';
data_in<="00101111";
clock <= not clock;
wait for 100 ns;
-- write to address "0101111" but written in "0000000" which is default
data_in<="10101010";
ad_sel<='1';
rw_sel<='0';
clock<=not clock;
wait for 100 ns;
data_in<="00101111";
ad_sel<='0';
rw_sel<='1';
clock<=not clock;
wait;
end process;
END;
And what I got in the ISim window is
Why doesn't the block_addr_n and block_cell_addr_n be assigned in the second 100 ns since the signal address be assigned at that time directly? I think this is the reason that causes my program into unexpected results, since the cache block 0 is written while I just set address 0 as default but pass in the address "0101111" after the first 100 ns.
Any ideas?

The sensitivity list does not contain all the signals that are used in the process, since the address is a signal (assigned in the same process, but that does not matter) but the signal is not in the sensitivity list of the process. So the process is not reexecuted by the simulator when the value of address changes. Process parts:
process(clock, init)
begin
...
address<=data_in(6 downto 0);
...
block_addr_n <= conv_integer(unsigned(address(4 downto 1)));
block_cell_addr_n<=conv_integer(address(0));
If you are using VHDL-2008, then you can make the sensitivity list automatically with process (all), but I don't think Xilinx has implemented VHDL-2008 yet, so you must probably make the sensitivity list yourself by manually including all used signals in the sensitivity list.
Btw, consider making the processes either purely combinatorial (gates), or clocked (flip-flops or RAMs), so that combinatorial process should not have for example clock in the sensitivity list.

I think that your actual problem might be a misunderstanding about how to write a clocked process.
The way your process is written now it will generate purely combinational logic and latches.
What you want here is registers, RAM and combinational logic.
A clocked process would be written like this:
process(clock, init) --A clocked process shall have only the clock and reset signal in the sensitivity list. This is correct.
begin
if init = '0' then --"init" is used as a reset signal
for i in 0 to 15 loop
--This will reset the cache memory. It works, but
--doing it this way prevents the synthesis tool from infering a RAM block.
--To allow RAM inference you can write only one memory location per clock cycle.
cache_memory(i)<="1111111111111111";
tag_memory(i)<="11";
end loop;
elsif rising_edge( clock ) then
case ad_sel is
when '0' =>
address<=data_in(6 downto 0);
when '1' => data_cpu_wr<=data_in;
when others =>null;
end case;
block_addr_n <= conv_integer(unsigned(address(4 downto 1)));
block_cell_addr_n<=conv_integer(address(0));
case rw_sel is
...
end if;
Note that this will not work straight away. There are issues in your testbench as well. For example, your init signal is low indefinitely.
You may also want to use "if" statements rather than "case" for simple conditions. Example:
if ad_sel='0' then
address<=data_in(6 downto 0);
else
data_cpu_wr<=data_in;
end if;

Related

Read, then write RAM VHDL

in VHDL all the code lines are executed in a parallel way, since its a machine.
i want to create this RAM that reads a certain register from a ram block to the output and only 'afterwards' writes to the same register the input. my code goes like this:
architecture Behavioral of RAM is
type ram_t is array (0 to numOfRegs-1) of std_logic_vector (rLength-1 downto 0);
signal ram_s: ram_t;
signal loc : integer;
begin
process(clk)
begin
if(rising_edge(clk)) then
if(we='1') then
dataout <= ram_s(loc); -- reads the 'old' data to the output
ram_s(loc) <= datain; -- writes the 'new' data to the RAM
loc <= conv_integer(addr);
end if;
end if;
end process;
end Behavioral;
there is a similar case presented
here.
so I'd like to ask, is my code works fine or is there need for tweaking like putting a delay of half clock cycle, and if so, how to implement it.
I'm very new to VHDL thanks for your patience and help.
ive add a testbench simulation below . as can be seen the dataout isnt working at all.
Your question doesn't present a Minimal, Verifiable and Complete example, lacking the ability to replicate your results.
One of the consequences of this is that answers can be ambiguous should there be one or more causes of the problem in portions of your code not shown.
Brian's comment that you aren't reading data when we is invalid is poignant and would be responsible for 'U's in the clock cycle left of your yellow marker in your waveform.
There's also the issue with loc being a signal. Signals are scheduled for update, and no update occurs while any process that is scheduled to resume in the current simulation cycle has not been resumed and suspended.
This means the integer version of your address is delayed and won't be seen in the process until the next rising edge.
Fixing loc by making it a variable as an alternative to pipelining datain and moving the dataout assignment are accomplished in the following changes to your RAM process:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all; -- standard package
entity ram is
generic (
ADDRLENGTH: natural := 8;
RLENGTH: natural := 16;
NUMOFREGS: natural := 256
);
port (
clk: in std_logic;
we: in std_logic;
addr: in std_logic_vector (ADDRLENGTH - 1 downto 0);
datain: in std_logic_vector (RLENGTH - 1 downto 0);
dataout: out std_logic_vector (RLENGTH - 1 downto 0)
);
end entity;
architecture behavioral of ram is
type ram_t is array (0 to NUMOFREGS - 1) of
std_logic_vector (RLENGTH - 1 downto 0);
signal ram_s: ram_t;
-- signal loc: integer; -- USE VARIABLE in process instead
begin
process(clk)
variable loc: integer; -- MAKE loc variable so it's immediately available
begin
if rising_edge(clk) then
loc := to_integer(unsigned(addr)); -- MOVED so READ works
if we = '1' then
-- dataout <= ram_s(loc); -- reads the 'old' data to the output
ram_s(loc) <= datain; -- writes the 'new' data to the ram
-- loc <= conv_integer(addr);
end if;
dataout <= ram_s(loc); -- MOVED reads the 'old' data to the output
end if;
end process;
end architecture behavioral;
There's also the liberty of filling in the entity declaration and converting from conv_integer using Synopsys's package std_logic_arith to to_integer in the IEEE's numeric_std package. With a -2008 compliant tool chain you could instead use IEEE's package numeric_std_unsigned and do away with the type conversion to unsigned.
Because the ram_test testbench was also not supplied a testbench was written to replicate your waveform display image:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity ram_tb is
end entity;
architecture foo of ram_tb is
constant ADDRLENGTH: natural := 8;
constant RLENGTH: natural := 16;
constant NUMOFREGS: natural := 256;
signal clk: std_logic := '0';
signal we: std_logic := '1';
signal addr: std_logic_vector (ADDRLENGTH - 1 downto 0);
signal datain: std_logic_vector (RLENGTH - 1 downto 0);
signal dataout: std_logic_vector (RLENGTH - 1 downto 0);
begin
DUT:
entity work.ram
generic map (
ADDRLENGTH => ADDRLENGTH,
RLENGTH => RLENGTH,
NUMOFREGS => NUMOFREGS
)
port map (
clk => clk,
we => we,
addr => addr,
datain => datain,
dataout => dataout
);
CLOCK:
process
begin
if now = 500 ps then
wait for 200 ps;
else
wait for 100 ps;
end if;
clk <= not clk;
if now >= 1100 ps then
wait;
end if;
end process;
STIMULI:
process
begin
for i in 0 to 2 loop
addr <= std_logic_vector(to_unsigned (i, ADDRLENGTH));
case i is
when 0 =>
datain <= x"00FF";
when 1 =>
datain <= x"FF00";
when 2 =>
datain <= x"FFFF";
end case;
wait until falling_edge(clk);
if i = 1 then
we <= '0';
end if;
end loop;
for i in 1 to 2 loop
addr <= std_logic_vector(to_unsigned (i, ADDRLENGTH));
case i is
when 1 =>
datain <= x"FF00";
when 2 =>
datain <= x"FFFF";
end case;
wait until falling_edge(clk);
end loop;
wait;
end process;
end architecture;
And this produced:
Where the one written address that is subsequently read shows the correct data.
The simulator used does not present non-signals in a waveform dump (bounds in declarations are required to be static) and rst is not found in the portion of your design specification provided.
As noted previously there is no guarantee there isn't another issue with portions of your design specification or testbench not provided in your question.
The testbench shown is by no means comprehensive.

VHDL code behaves abnormally after synthesis (I2C)

First of all, because of company disclosure agreement I am not allowed to post my code thus I will describe the symptoms of the behavior and hopefully it's enough.
I am using the IP for I2C master provided by Scott Larson on eewiki and I use it the same way in the example provided on the page. The difference is that I have several procedures as described in the example
WHEN get_data => --state for conducting this transaction
busy_prev <= i2c_busy; --capture the value of the previous i2c busy signal
IF(busy_prev = '0' AND i2c_busy = '1') THEN --i2c busy just went high
busy_cnt := busy_cnt + 1; --counts the times busy has gone from low to high during transaction
END IF;
CASE busy_cnt IS --busy_cnt keeps track of which command we are on
WHEN 0 => --no command latched in yet
i2c_ena <= '1'; --initiate the transaction
i2c_addr <= slave_addr; --set the address of the slave
i2c_rw <= '0'; --command 1 is a write
i2c_data_wr <= data_to_write; --data to be written
WHEN 1 => --1st busy high: command 1 latched, okay to issue command 2
i2c_rw <= '1'; --command 2 is a read (addr stays the same)
WHEN 2 => --2nd busy high: command 2 latched, okay to issue command 3
i2c_rw <= '0'; --command 3 is a write
i2c_data_wr <= new_data_to_write; --data to be written
IF(i2c_busy = '0') THEN --indicates data read in command 2 is ready
data(15 DOWNTO 8) <= i2c_data_rd; --retrieve data from command 2
END IF;
WHEN 3 => --3rd busy high: command 3 latched, okay to issue command 4
i2c_rw <= '1'; --command 4 is read (addr stays the same)
WHEN 4 => --4th busy high: command 4 latched, ready to stop
i2c_ena <= '0'; --deassert enable to stop transaction after command 4
IF(i2c_busy = '0') THEN --indicates data read in command 4 is ready
data(7 DOWNTO 0) <= i2c_data_rd; --retrieve data from command 4
busy_cnt := 0; --reset busy_cnt for next transaction
state <= home; --transaction complete, go to next state in design
END IF;
WHEN OTHERS => NULL;
END CASE;
For example at the end if the get_data instead of going to home I go to an initializing case to reset busy_cnt and i2c_ena and other signals then to another case for writing similar to get_data.
Current Situation
The code executes well in simulation, I switch the i2c_busy on and off and the behavior is as expected.
There are no warnings regarding the sensitivity list or any latches. I have a warning that 5 clock signals were routed using generic routing resource and might suffer from excessive delay and/or skew
The Problem
As I mentioned before the code is divided into several cases similar to get_data in the example above and when executing it is stuck in an infinite loop in one of them. And when I change something in the case that is stuck another case before it misbehaves and also stuck in an infinite loop. Even changing a simple signal that has nothing to do with the algorithm (LED output for debugging) in one case might cause a case before it to misbehave.
The infinite loop behavior is also strange and shows state is not set at the end of the case.
Note that I am not using the same way in the example to update state rather I use a separate Process for the state update (curr_state <= next_state;) and the next_state is the one updated.
My Speculation
I assumed that the problem could be because I have to set every output in every case. But even after setting all I could the behavior was similar.
For the sake of completion: The development environment is Lattice Diamond and the FPGA is MachXO2.
The posted code snippet has severe problems, if it's not part of a clocked process anymore according to the comments:
Me: Is the posted code still part of a clocked process?
mmahdich: #Martin In my code it's not, the clocked process only updates the curr_state <= next_state
If I embed the code from the question into the following test architecture, then the synthesis compiler XST (from ISE 14.7) reports warnings about latches for the signals busy_cnt, busy_prev and data. The reason that the OP observes no latch warnings may result from further optimizations or interference from the undisclosed code parts. (I have no Lattice Diamond at hand.)
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_test is
port (
clk : in std_logic;
i2c_busy : in std_logic;
slave_addr : in std_logic;
data_to_write : in std_logic_vector(7 downto 0);
new_data_to_write : in std_logic_vector(7 downto 0);
i2c_data_rd : in std_logic_vector(7 downto 0);
i2c_ena : out std_logic;
i2c_addr : out std_logic;
i2c_rw : out std_logic;
i2c_data_wr : out std_logic_vector(7 downto 0);
data : out std_logic_vector(15 downto 0));
end i2c_test;
architecture rtl of i2c_test is
type state_t is (get_data, home);
signal curr_state : state_t := home;
signal state : state_t; -- next state is named "state" in OP code
signal busy_prev : std_logic;
begin -- rtl
process (clk)
begin -- process
if rising_edge(clk) then
curr_state <= state;
end if;
end process;
process(curr_state, busy_prev, i2c_busy, slave_addr, data_to_write, new_data_to_write, i2c_data_rd)
variable busy_cnt : integer range 0 to 4 := 0;
begin
state <= curr_state; -- next state is named "state" in OP code
i2c_ena <= '0';
i2c_addr <= '-';
i2c_rw <= '-';
i2c_data_wr <= (others => '-');
-- no default assignments for busy_prev and data here, because the usage
-- below indicates that a register was intended
case curr_state is
when home => state <= get_data;
---------------------------------------------------------------
-- Add code from question here
---------------------------------------------------------------
end case;
end process;
end rtl;
First of all, the signal for the next state seems to be named state.
Then, XST founds latches for the signals busy_prev and data. I have not added default assignments for these signals in the combinational process because the following assignments in the OP's code indicate that a register was intended:
busy_prev <= i2c_busy; --capture the value of the previous i2c busy signal
data(15 DOWNTO 8) <= i2c_data_rd; --retrieve data from command 2
data(7 DOWNTO 0) <= i2c_data_rd; --retrieve data from command 4
Finally, one cannot (easily) implement this wait counter without a clocked process:
WHEN get_data => --state for conducting this transaction
IF(busy_prev = '0' AND i2c_busy = '1') THEN --i2c busy just went high
busy_cnt := busy_cnt + 1; --counts the times busy has gone from low to high during transaction
END IF;
EDIT
Synthesizing the above would require flip-flops for busy_cnt which is triggered by all signals listed in the process sensitivity list. Of course, a new state will be loaded into the flip-flops only if the condition (curr_state = get_data and busy_prev = '0' and i2c_busy = '1') is true. For example, XST synthesizes a latch for this, which is enabled when the condition is true. But then, busy_cnt forms a combinational loop during an enabled latch. This synthesized behaviour does not match the VHDL description.
The solution would be to implement the registers busy_prev, data and busy_cnt in the clocked process.

Why does incrementing a std_logic_vector give unknown value?

I'm attempting to write a I2C bus master in VHDL - and thoroughly test it to ensure it works etc. In doing so, I've written the module and a subsequent test-bench to test it under different stimuli - namely changing addresses and data input after each sending cycle (which is when the busy goes low).
To do this, I first attempted to increment the value in the data bus and decrement the values in the address bus, using the techniques described in this question here. However, when I did this, rather than the address bus taking the new value, it takes an uninitialised value and the assignment statement doesnt seem to execute.
I then attempted to use an intermediate integer signal, however this produced the same results, however this means that the address & data buses take the correct value for the first cycle - but then don't increment and instead take a unknown state!
Ultimately my question is why do these errors occur - and how do I fix/avoid them?
My code for the I2C master module can be found here and the code for the I2CBus (testbench) can be found here. Any other information I can provide to help please just say!
Thanks very much,
David
-----Code using intermediate signals------
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
ENTITY I2CBus IS
END I2CBus;
ARCHITECTURE behavior OF I2CBus IS
COMPONENT IIC_Master
PORT(
CLOCK : IN std_logic;
RESET_N : IN std_logic;
ENA : IN std_logic;
ADR : IN std_logic_vector(6 downto 0);
RW : IN std_logic;
DAT_WR : IN std_logic_vector(7 downto 0);
DAT_RD : OUT std_logic_vector(7 downto 0);
BUSY : OUT std_logic;
SCL : INOUT std_logic;
SDA : INOUT std_logic;
ACK_ERR : BUFFER std_logic
);
END COMPONENT;
--Inputs
signal CLOCK : std_logic := '0';
signal RESET_N : std_logic; --active high
signal ENA : std_logic; --active high
signal ADR : std_logic_vector(6 downto 0);
signal RW : std_logic; --read high write low
signal DAT_WR : std_logic_vector(7 downto 0);
--BiDirs
signal SCL : std_logic;
signal SDA : std_logic;
--Outputs
signal DAT_RD : std_logic_vector(7 downto 0);
signal BUSY : std_logic;
signal ACK_ERR : std_logic;
-- Clock period definitions
constant CLOCK_period : time := 5 ns;
--Signals to vary
signal address : integer := 127;
signal input_data : integer := 0;
BEGIN
-- Instantiate the Unit Under Test (UUT)
uut : IIC_Master PORT MAP(
CLOCK => CLOCK,
RESET_N => RESET_N,
ENA => ENA,
ADR => ADR,
RW => RW,
DAT_WR => DAT_WR,
DAT_RD => DAT_RD,
BUSY => BUSY,
SCL => SCL,
SDA => SDA,
ACK_ERR => ACK_ERR
);
-- Clock process definitions
CLOCK_process : process
begin
CLOCK <= '0';
wait for CLOCK_period / 2;
CLOCK <= '1';
wait for CLOCK_period / 2;
end process;
-- Reset process
reset : process
begin
reset_n <= '0';
ADR <= "1111111"; --This doesn't seem to happen the first time?
DAT_WR <= "00000000"; --Nor does this?
RW <= '0';
wait for 50 ns;
reset_n <= '1';
ENA <= '1';
wait;
end process;
stim_proc : process
begin
DAT_WR <= std_logic_vector(to_unsigned(input_data, 8));
ADR <= std_logic_vector(to_unsigned(address, 7));
if input_data < 127 then
address <= address - 1;
input_data <= input_data + 1;
wait until BUSY = '0' and RESET_N = '1';
elsif unsigned(DAT_WR) > 126 then
wait;
end if;
end process;
end behavior;
------1st Stimulus Process w/out intermediate signals
stim_proc : process
begin
if input_data < 127 then
wait until BUSY = '0' and RESET_N = '1';
ADR <= std_logic_vector(unsigned(ADR) + 1);
DAT_WR <= std_logic_vector(unsigned(DAT_WR) + 1);
elsif unsigned(DAT_WR) > 126 then
wait;
end if;
end process;
Results from second simulation using intermediate signals
The usual cause of this is the resolution of multiple assignments to signals like ADR in several different processes. In hardware terms. this is equivalent to short circuiting together the output of several different ICs. (It doesn't work without special care, and can destroy your ICs).
So check that you are assigning ADR and similar signals in only one process, and if necessary, rewrite to combine those processes into one process.
If you need to connect several outputs together, there are 2 ways that work successfully, though I don't think it's the right approach here.
Wired-OR outputs (or Wired-AND). In Wired-OR scheme the signal is permanently pulled weakly low by an assignment like ADR <= (others -> 'L'); and this can be overridden by each output driving either 'Z' for low or '1' for high. This works because multiple outputs can safely drive the signal at once.
Tri-state outputs, with arbitration to make sure only one process drives the output at any time. The others all drive 'Z' or (others => 'Z') onto the same signal, to signify they are inactive.
The answer from Brian Drummond is correct. I just wand to explain in at your example. At time 0, both the reset as well as the stim_proc process assign the same value:
reset : process
begin
...
ADR <= "1111111";
...
end process;
and
stim_proc : process
begin
ADR <= std_logic_vector(to_unsigned(address, 7)); -- with address = 127
...
end process;
So, the result for ADR is "1111111". But after decrementing address (in original code with intermediate version), the stim_proc process assigns a different value (than the reset process) after it starts over. You see this in the waveform. When address gets 126, which is "1111110", only the lowest bit of ADR gets X because only this bit differs from "1111111" assigned in the reset process.
Solution 1
If you just want to initialize a signal, assign the initialization value at the signal declaration. (I think, this is what you want according to your VHDL comment.) That is:
signal ADR : std_logic_vector(6 downto 0) := (others => '1');
Solution 2
If you (really) want to assign "1111111" only for the first 50 ns seconds from the reset process, then you have to assign (others => 'Z') (tri-state), (others => 'Z') (weak pull-down) or (others => 'H') (weak pull-up) afterwards in this process to allow an "overriding" by the stim_proc process, e.g.:
reset : process
begin
ADR <= "1111111";
wait for 50 ns;
ADR <= (others => 'Z');
wait;
end process;

VHDL - Comparing present and past inputs

I have a system that has a 3 input D_in which is read at every positive clk edge.
If say I want to see if the current input, D_in is greater then the previous D_in by at least 2, then a count will increment. How do I write this in VHDL?
if clk'event and clk = '1' then --read at positive edge
if D_in > (D_in + 010) then <---I am sure this is wrong. How to write the proper code?
Entity ABC is
Port(D_in: in std_logic_vector(2 downto 0);
Count: out std_logic_vector(2 downto 0));
Architecture ABC_1 of ABC is
signal D_last: std_logic_vector(2 downto 0);
Begin
Process(D_in)
D_last <= D_in;
if clk'event and clk = '1' then
if D_last > (D_in + 2) then
count <= count + 1;
end if;
end process;
end ABC_1;
The "good" way to write this process is as follow :
process (clk)
begin
if (rising_edge(clk)) then
-- store the value for the next time the process will be activated
-- Note that D_last value will be changed after the whole process is completed
D_last <= D_in;
-- compare the actual D_in value with the previous one stored in D_last.
-- D_last value is its value at the very beginning of the process activation
if (D_in > D_last + 2) then
-- increment the counter
count <= count + 1;
end if;
end if;
end process;
Note that D_in, D_last and count has to be declared as unsigned and not as std_logic_vector.
I suggest you to read this post which explains how a process actually works : when are signals updated and which signal value is used into the process.
Cheers
[edit] This answer should be fine for your question. But the code you show has other errors :
The signal clk has to be an input for your entity.
The signal count can't be read in your architecture because it's defined as output in the entity. Then the line "count <= count + 1" can't be resolved. You have to use an internal signal and then assign its value to "count" outside of a process :
count <= count_in;
There are several other errors in your design specification as well. This answer attempts to answer all concerns in one place.
VHDL is simulated by executing processes in simulation cycles. Every
concurrent statement can be expresses as either an equivalent process
statement or combination of process statements and block statements.
Signal assignment is to a projected output waveform queue for a specified
time. When no time is specified it's the current time, and the value will be updated
prior to executing processes in the next simulation cycle, a delta cycle, simulation
time is advanced when there are no remaining events scheduled for the
current simulation time.
To avoid confusion over when signal assignments occur, view them as
separate processes (whether you express them that way or not).
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity abc is
port (
clk: in std_logic; -- Note 1
d_in: in std_logic_vector(2 downto 0);
count: out std_logic_vector(2 downto 0)
);
end entity; -- Note 2
architecture foo of abc is
signal d_last: std_logic_vector(2 downto 0);
begin
DLAST: -- Note 3
process (clk)
begin
if rising_edge(clk) then -- Note 4
d_last <= d_in;
end if;
end process;
INC_COUNT:
process (clk)
variable cnt: unsigned(2 downto 0) := "000"; -- Note 5
begin
if rising_edge(clk) and
unsigned(d_last) > unsigned(d_in) + 2 then -- Mote 6,7
cnt := cnt + 1;
end if;
count <= std_logic_vector(cnt);
end process;
end architecture;
Notes
Missing clk from port interface
Missing end statement for entity ABC.
Conceptually view D_last
register separately from Count counter sensitive to clk. (Can be
merged as one process)
rising_edge function expresses clk'event and clk = '1' ('event
and "=" are both functions)
The counter must represent a binary value for "+" to produce a
binary result
"+" is higher priority than ">", which is higher priority than "and"
(you don't need parentheses)
Package numeric_std provide relational and adding operators for
type sign and type unsigned, requiring type conversion for D_last
and D_in.
Alternatively use Synopsys package std_logic_unsigned which
depends on Synopsys package std_logic_arith and treats
std_logic_vector as unsigned. This avoids type conversion, and
allows array types to be declared as type std_logic_vector.
The variable cnt can be done away with if port count were to be declared mode buffer and provided a default value:
count: buffer std_logic_vector(2 downto 0) :="000" -- Note 5
and
INC_COUNT:
process (clk)
begin
if rising_edge(clk) and
unsigned(d_last) > unsigned(d_in) + 2 then -- Note 6,7
count <= std_logic_vector(unsigned(count) + 1);
end if;
end process;
You can't use Count as mode out to algorithmically modify it's own value. The ability to access the value of a mode out port is intended for verification and is a IEEE Std 1076-2008 feature.
And about now you can see the value of Synopsys's std_logic_unsigned package, at least as far avoiding type conversions.
Also, i got another question. If d_in is 0 for 3 consecutive clk cycles, i want to reset count to 0. How do i write the code to represent for 3 clk cycles?
Add another pipeline signal for D_in:
signal d_last: std_logic_vector(2 downto 0) := "000";
signal d_last1: std_logic_vector(2 downto 0) := "000";
Note these also have default values, which FPGA synthesis will generally honor, it's represented by the state of the flip flop in the bistream image used for programming the FPGA.
And modify how the counter is operated:
INC_COUNT:
process (clk)
begin
if rising_edge(clk) then
if d_in = "000" and d_last = "000" and d_last1 = "000" then
count <= "000";
elsif unsigned(d_last) > unsigned(d_in) + 2 then -- Note 6,7
count <= std_logic_vector(unsigned(count) + 1);
end if;
end if;
end process;
The three incarnations of the example all analyze, they haven't been simulation and should be synthesis eligible.

Unexpected delays with register VHDL

Found a strange occurance with this register I coded. I'm very new to VHDL, but I was taught when writing a value to output ports like data_out you should always use a "middleman" signal to transfer your value. Here I tried to use the signal "data" to transfer the signal, but this implementation resulted in a delay before data_out changes (when ld goes high). Taking out data completely and coding how I would in a C program removes this delay and the register works perfectly. Any idea on why this is and why I shouldn't do this?
Broken code:
entity register is
generic (
DATA_WIDTH : natural := 12);
port (
data_in : in std_logic_vector(DATA_WIDTH-1 downto 0);
ld : in std_logic;
clk : in std_logic;
rst_L : in std_logic;
data_out : out std_logic_vector(DATA_WIDTH-1 downto 0));
end entity register;
architecture bhv of register is
signal data : std_logic_vector(DATA_WIDTH-1 downto 0);
begin -- bhv
REG : process (clk, rst_L, ld, data_in)
begin -- process REG
if rst_L = '0' then
data <= (others => '0');
else
if clk'event and clk = '1' then
if ld = '1' then
data <= data_in;
end if;
end if;
end if;
data_out <= data;
end process REG;
end architecture bhv;
Changes for process that made it work:
REG : process (clk, rst_L, ld, data_in)
begin -- process REG
if rst_L = '0' then
data <= (others => '0');
else
if clk'event and clk = '1' then
if ld = '1' then
data_out <= data_in;
end if;
end if;
end if;
end process REG;
Just wanted to know what I did wrong and why I even have to use a signal to transfer the value if the code works fine. Thanks!
The problem in the broken process code is that data signal is not updated for
read until after a delta delay, thus the data_out update by
data_out <= data assign is postponed until next execution of the process code, thereby giving a delay in simulation.
Note that the ld and data_in in the sensitivity list of the initial process are not required, since use of these are guarded by rising clk.
Update of the code can be:
reg : process (clk, rst_L)
begin -- process REG
if rst_L = '0' then
data <= (others => '0');
else
if clk'event and clk = '1' then -- May be written as rising_edge(clk) instead
if ld = '1' then
data <= data_in;
end if;
end if;
end if;
end process REG;
data_out <= data;
It may be useful to take a look at
VHDL's crown jewel for some information about processes and delta cycles in VHDL.
Note that register is a VHDL reserved word, so it can't be used as identifier
for the entity.
Unexpected delays with register VHDL
The problem with the original process is that data is not in the sensitivity list, delaying the assignment to data_out until the next time the process executes instead of the next simulation data cycle. The process will execute on any transaction of a signal in the sensitivity list - originally as you indicated ld.
Adding data to the sensitivity list of your original process:
REG : process (clk, rst_L, data)
begin -- process REG
if rst_L = '0' then
data <= (others => '0');
else
if clk'event and clk = '1' then
if ld = '1' then
data <= data_in;
end if;
end if;
end if;
data_out <= data;
end process REG;
Will allow the assignment to data_out to occur in a delta simulation cycle instead of waiting until a transition on a signal currently in the sensitivity list.
I removed the extraneous signals from the sensitivity list for the same reason Morten did in his answer. These were causing execution of the process at other times, but not when a transaction occurred on data.
And yes, as long as you don't have the data_out signal in an expression (e.g. on the right hand side of an assignment statement) in your architecture you don't need the intermediary variable data and your simulation will go a bit faster.
Your changed process is the most efficient implementation once the sensitivity list is pared down.
Simply adding data to the sensitivity list would have caused the original process to simulate correctly, feel free to try it.

Resources