0
\$\begingroup\$

I have created a 4 bit counter with the following inputs and outputs

clockN: active low clock

clearN: active low clear

cP: When high, the counter counts. When low, the counter stays the same.

eP: Active high. This activates the tri-state buffer at the outputs

When I simulate this, and assert the clearN (N meaning active low) at t=0, it does not actually clear until the next positive clock edge. I do not understand why. Also, each time qOut counts up by one, it does it on the positive clock edge even though my code is telling it to do it on the negative clock edge. My code, testbench, and modelsim output are shown below.

Here is the counter code

    --Program Counter for SAP-1

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;    --This is required when doing additions to STD_LOGIC_VECTORs

ENTITY PROG_COUNT_SAP_1 IS 

    GENERIC(size: INTEGER:= 3); --This is the size of the register

    PORT(
        clockN, clearN, cP, eP: IN STD_LOGIC;
        qOut: OUT STD_LOGIC_VECTOR(size DOWNTO 0));

END PROG_COUNT_SAP_1;

ARCHITECTURE Behavioral OF PROG_COUNT_SAP_1 IS
    SIGNAL valueBeforeTriBuffer: STD_LOGIC_VECTOR(size DOWNTO 0);

BEGIN

    PROCESS(clockN, clearN, cP, eP)
    BEGIN
        IF(clearN = '0') THEN
            valueBeforeTriBuffer<=(OTHERS=>'0');

        ELSIF (falling_edge(clockN)) THEN

            IF (cP = '1') THEN
                valueBeforeTriBuffer<= valueBeforeTriBuffer + 1;
            END IF;

        END IF;

        IF (eP = '1') THEN
            qOut<= valueBeforeTriBuffer;
        ELSE
            qOut<=(OTHERS=>'Z');
        END IF;

    END PROCESS;

END Behavioral;

Here is the testbench

    library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity TB_PROG_COUNT_SAP_1 is
end TB_PROG_COUNT_SAP_1;

architecture test of TB_PROG_COUNT_SAP_1 is

--create time constant
constant CLOCK_PERIOD: time:=2 us;

--create constants for all generics.
constant size: INTEGER:=3;

--create signals for every port
signal clockN: std_logic;
signal clearN: std_logic;
signal cP: std_logic;
signal eP: std_logic;
signal qOut: std_logic_vector(size DOWNTO 0);

begin

dut: entity work.PROG_COUNT_SAP_1
generic map(size=>size)
port map(clockN=>clockN,
    clearN=>clearN,
    cP=>cP,
    eP=>eP,
    qOut=>qOut);

--simulate the clock
clockNSimulation: process
BEGIN

    FOR count IN 1 TO 16 LOOP
        clockN<= '0';
        wait for CLOCK_PERIOD/2;
        clockN<='1';
        wait for CLOCK_PERIOD/2;
    END LOOP;

END PROCESS clockNSimulation;



--simulate the clearN
clearNSimulation: process
BEGIN

    clearN<='0';
    wait for  3 us;
    clearN<='1';
    wait for 7 us;
    clearN<='0';
    wait for 2 us;
    clearN<='1';
    wait;

END PROCESS clearNSimulation;


--simulate the cP
cPSimulation: process
BEGIN
    cP<='1';
    wait;

END PROCESS cPSimulation;   

ePSimulation: process
BEGIN
    eP<='1';
    wait;

END PROCESS ePSimulation;   


end architecture test;

Here is the output waveform from modelsim: enter image description here

\$\endgroup\$
  • \$\begingroup\$ In PROG_COUNT_SAP_1 your if statement at the bottom of the unlabelled process is purely combinatorial and one input (valueBeforeTriBuffer) is not in the sensitivity list so qOut get's updated when the next event occurs on a signal that is in the sensitivity list. That happens to be the rising_edge of clockN. \$\endgroup\$ – user8352 Aug 28 '18 at 5:11
  • \$\begingroup\$ Why doesn't the bottom IF statement run on the same falling edge event? I get that it wouldn't if it was part of the upper IF statement. \$\endgroup\$ – BPoy Aug 28 '18 at 5:22
  • \$\begingroup\$ Oh wait...let me guess... because it is happening in parallel, valueBeforeTriBuffer hasn't been updated yet. So the bottom if statement is actually running at the falling edge, but with the old value? Sound right? \$\endgroup\$ – BPoy Aug 28 '18 at 5:30
  • 1
    \$\begingroup\$ Signals are scheduled for updates. Updates occur earlier in the simulation cycle that processes being resumed ans suspended again. An assignment to the same simulation time (no after time_unit) will cause a delta cycle. In that next simulation cycle signals will update, then processes will resumed then suspend again. You can't see a signal update in the same cycle the assignment occurs in. \$\endgroup\$ – user8352 Aug 28 '18 at 5:44
  • \$\begingroup\$ You are a vhdl NINJA! Are you going to post an answer or do you want me to do it? \$\endgroup\$ – BPoy Aug 28 '18 at 6:02
0
\$\begingroup\$

As mentioned by user8352 in the comments: your problem is that you need to induce an extra delta cycle when you have updated valuebeforetribuffer (pro-tip: VHDL is case-insensitive). The quick and dirty solution is to add valuebeforetribuffer to your sensitivity list. At that point you're gravitating towards a bigger and bigger mess of a code, though.

Cleaner is:

p_main: process(clk, clear_n, enable_out)
  variable r_value_before_tri_buffer : unsigned(size-1 downto 0);
begin

  if clear_n = '0' then
    r_value_before_tri_buffer := (others=>'0');
  elsif falling_edge(clk) then
    if c_p = '1' then
      r_value_before_tri_buffer := r_value_before_tri_buffer +1;
    end if;
  end if;

  if enable_out then
    q_out <= std_logic_vector(r_value_before_tri_buffer);
  else
    q_out <= (others=>'Z');
  end if;

end process;

Other remarks:

  • I use numeric_std instead of the mess that is std_logic_unsigned
  • size downto 0 declares a vector of size+1
  • no need to have c_p in your sensitivity list if you only use it at clock flanks, also cuts down on your simulation time
\$\endgroup\$
  • \$\begingroup\$ Won't I have to convert r_value_before_tri_buffer to a std_logic_vector to make it compatible with q_out? \$\endgroup\$ – BPoy Aug 29 '18 at 1:43
  • \$\begingroup\$ @BPoy You're right, adjusted \$\endgroup\$ – DonFusili Aug 29 '18 at 7:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.