liampcas
liampcas

Reputation: 9

Updating signals sequentially in VHDL

I am a current student looking for some knowledge on VHDL. I understand that VHDL is a concurrent language. I've learned that you are able to update signals sequentially using a process statement. I am currently working on a project. I have this Digilent 16-button keypad that I've connected to my DE10-Lite FPGA. I've created a decoder that can determine which button is being pressed on the keypad (0-F) and output the corresponding four-bit value (ex. 4 = "0100"). I also have a button-press flag which is active high when a button is pressed. This four-bit value which represents my number that I am pressing is then driven to another component where I am trying to sequentially update different four-bit signals. Basically, when a button is pressed (BP_in) I want the value from the decoder (Dec_in) to represent Num0. If a button is pressed again, I want the new value from the decoder to be the new value of Num0 and the old value of Num0 to be shifted to another signal Num1. If a button is pressed again, I want Num0 <= Dec_in, Num1 <= Num0, Num2 <= Num1. I want this cycle to continue thereafter so that each time a new input from the keypad is pressed, the numbers update sequentially. Unfortunately, when I've attempted to do this I continue to run into the same problem where the values are updated all at once causing Num0, Num1, and Num2 to all equal whatever the dec_in input is.

I've tried creating a finite state machine which only updates individual signals in each state but the same problem occurs. This is the simple code that I want to work:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity NumShift is
    port(  Dec_in  : in std_logic_vector(3 downto 0);
           BP_in   : in std_logic;
           Num0    : buffer std_logic_vector(3 downto 0);
           Num1    : buffer std_logic_vector(3 downto 0);
           Num2    : buffer std_logic_vector(3 downto 0));
end entity NumShift;

architecture Behavioral of NumShift is
    begin
    process (BP_in)
    begin
        if (BP_in = '1') then
            Num2 <= Num1;
            Num1 <= Num0;
            Num0 <= dec_in;
        end if;
    end process;
end architecture Behavioral;

Upvotes: 0

Views: 243

Answers (1)

lahsma
lahsma

Reputation: 26

Let's start with the most obvious issue in your code : You expect this to work like a memory, but this is not a memory. It is a series of MUXes that propagate the values. See Snapshot below Snapshot 1

Those Muxes will behave like Latches (in fact, I would go as far as calling them latches myself). The reason you get this behavior, is because you did not assign all the possible branches of the if statement inside your process. When you are using a conditional inside a VHDL process, you must assign a value for all the possible conditions for that conditional statement, and any value that you use inside the process as an input should be added to the sensitivity list. Otherwise, you will get a latch or unwanted behavior. To fix things, let's take a look at your process:

process (BP_in)
    begin
        if (BP_in = '1') then
            Num2 <= Num1;
            Num1 <= Num0;
            Num0 <= dec_in;
        end if;
    end process;

Let's start with the most obvious issue. Your sensitivity list is missing a lot of signals: Here, you only trigger the process when BP_in changes. Any transition of BP_in triggers the if conditional. This means that any changes to any other input signal inside your process does not trigger it, and no changes to the outputs. As I understand, you want the value of dec_in to be stored when it changes as well. Your process does not exhibit that behaviour. Therefore, you will have to add dec_in to the sensitivity list.

The second issue we need to address is the if conditional: You only check one possible branch of the conditional, which is when BP_in = '1', but what happens when BP_in = '0'? I assume you don't want the values to be changed. Therefore, you will need to assign values for the condition when BP_in = '0'. There are two ways for this to be done:

First is by adding an else statement to your if conditional (in my opinion, this should always be done in case there are no nested if statements):

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity NumShift is
...
end entity NumShift;

architecture Behavioral of NumShift is
    begin
    process (BP_in, Dec_in)
    begin
        if (BP_in = '1') then
            Num2 <= Num1;
            Num1 <= Num0;
            Num0 <= dec_in;
        else
            Num2 <= Num2;
            Num1 <= Num1;
            Num0 <= Num0;
      end if;
    end process;
end architecture Behavioral;

Second is by assigning "default" values before the if conditional :

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity NumShift is
...
end entity NumShift;

architecture Behavioral of NumShift is
    begin
    process (BP_in, Dec_in)
    begin
        Num2 <= Num2;
        Num1 <= Num1;
        Num0 <= Num0;
        if (BP_in = '1') then
            Num2 <= Num1;
            Num1 <= Num0;
            Num0 <= dec_in;
        end if;
   end process;
end architecture Behavioral;

Furthermore, even with those crucial changes, you MUXes are still behaving like latches. This should fix your issue of values not being updated correctly. This will still generate the snapshot above.

But, we still need to address a much larger issue at hand: This circuit is entirely combinational. You say you want the values to be updated sequentially. This means that you need memory in your design. To achieve that, you will have to use registers to store values. This will include adding a clock port to your design to enable clock edge triggered sequential logic. Even with the fixes above, I am not sure if your design is robust and you might see some issues moving forward. I suggest you look into using a shift register or three different registers connected in series to achieve your desired goal.

In addition, there are a few different things I would do to make this code more robust. I added my edits and comments below

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity NumShift is
  port( 
    Dec_in : in std_logic_vector(3 downto 0);
    BP_in  : in     std_logic;
    Num0   : buffer std_logic_vector(3 downto 0);
    Num1   : buffer std_logic_vector(3 downto 0);
    Num2   : buffer std_logic_vector(3 downto 0)
);
end entity NumShift;

architecture Behavioral of NumShift is
  -- use signals to carry the values from the process to output ports.
  signal Num0_v : std_logic_vector(3 downto 0);
  signal Num1_v : std_logic_vector(3 downto 0);
  signal Num2_v : std_logic_vector(3 downto 0);

begin
  process (BP_in, Dec_in)
  begin
    
    if (BP_in = '1') then
      Num2_v <= Num1_v;
      Num1_v <= Num0_v;
      Num0_v <= dec_in;
    else
      -- This is a simple if statement so an else statement is sufficient.
      Num2_v <= Num2_v;
      Num1_v <= Num1_v;
      Num0_v <= Num0_v;
    end if;
  end process;

  -- always assign output ports concurrently
  Num2 <= Num2_v;
  Num1 <= Num1_v;
  Num0 <= Num0_v;
end architecture Behavioral;

Upvotes: 0

Related Questions