VHDL coding tips and tricks: state machine
Showing posts with label state machine. Show all posts
Showing posts with label state machine. Show all posts

Saturday, November 28, 2020

Synthesizable Matrix Multiplier in VHDL

    Long back I had posted a simple matrix multiplier which works well in simulation but couldn't be synthesized. But many people had requested for synthesizable version of this code. So here we go.

    The design takes two matrices of 3 by 3 and outputs a matrix of 3 by 3. Each element is stored as unsigned 8 bits. This is not a generic multiplier, but if you watch the video explaining the code, you might be able to extend it to a different sized multiplier. 



    Each matrix has 9 elements, each of which is 8 bits in size. So I am passing the matrix as a 72 bit 1-Dimensional array in the design. The following table shows how the 2-D elements are mapped into the 1-D array.

Row

Column

Bit’s Position in 1-D array

0

0

7:0

0

1

15:8

0

2

23:16

1

0

31:24

1

1

39:32

1

2

47:40

2

0

55:48

2

1

63:56

2

2

71:64


Let me share the codes now...

matrix_mult.vhd:


--3 by 3 matrix multiplier. Each element of the matrix is 8 bit wide. 
--Inputs are called A and B and output is called C. 
--Each matrix has 9 elements each of which is 8 bit wide. So the inputs is 9*8=72 bit long.
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all; 

entity matrix_mult is
    port (  Clock: in std_logic; 
            reset : in std_logic;   --active high reset
            start : in std_logic;   --A '1' starts the matrix multiplication process.
            A,B : in unsigned(71 downto 0);
            C : out unsigned(71 downto 0);
            done : out std_logic    --a '1' indicates that multiplication is done and result is availble at C.
            );
end entity;

architecture Behav of matrix_mult is

type matType is array(0 to 2,0 to 2) of unsigned(7 downto 0);
signal matA, matB, matC : matType := (others => (others => X"00"));
type state_type is (init,do_mult,apply_outputs);
signal state : state_type := init;
signal i,j,k : integer := 0;

begin 

sm : process (Clock,reset)    --process implementing the state machine for multiplying the matrices.
variable temp : unsigned(15 downto 0) := (others => '0');
begin
    if(reset = '1') then
        state <= init;
        i <= 0;
        j <= 0;
        k <= 0;
        done <= '0';
        matA <= (others => (others => X"00"));
        matB <= (others => (others => X"00"));
        matC <= (others => (others => X"00"));
    elsif rising_edge(Clock) then
        case state is
            when init =>    --the matrices which are in a 1-D array are converted to 2-D matrices first.
                if(start = '1') then
                    for i in 0 to 2 loop    --run through the rows
                        for j in 0 to 2 loop    --run through the columns
                            matA(i,j) <= A((i*3+j+1)*8-1 downto (i*3+j)*8);
                            matB(i,j) <= B((i*3+j+1)*8-1 downto (i*3+j)*8);
                        end loop;
                    end loop;
                    state <= do_mult;
                end if;
            when do_mult =>
                temp := matA(i,k)*matB(k,j);
                matC(i,j) <= matC(i,j) + temp(7 downto 0);
                if(k = 2) then
                    k <= 0;
                    if(j = 2) then
                        j <= 0;
                        if (i= 2) then
                            i <= 0;
                            state <= apply_outputs;
                        else
                            i <= i + 1;
                        end if;
                    else
                        j <= j+1;
                    end if;        
                else
                    k <= k+1;
                end if;     
            when apply_outputs =>   --convert 3 by 3 matrix into a 1-D matrix.
                for i in 0 to 2 loop    --run through the rows
                    for j in 0 to 2 loop    --run through the columnss
                        C((i*3+j+1)*8-1 downto (i*3+j)*8) <= matC(i,j);
                    end loop;
                end loop;   
                done <= '1';
                state <= init;  
        end case;
    end if;
end process;
 
end architecture;


tb_matrix_mult.vhd:


--Testbench for testing the 3 by 3 matrix multiplier.
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all; 

entity tb_matrix_mult is  --testbench entity is always empty. No input or output ports.
end entity;

architecture behav of tb_matrix_mult is

component matrix_mult is
    port (  Clock: in std_logic; 
            reset : in std_logic;   --active high reset
            start : in std_logic;   --A '1' starts the matrix multiplication process.
            A,B : in unsigned(71 downto 0);
            C : out unsigned(71 downto 0);
            done : out std_logic    --a '1' indicates that multiplication is done and result is availble at C.
            );
end component;

signal A,B,C : unsigned(71 downto 0);
signal Clock,reset, start, done : std_logic := '0';
type matType is array(0 to 2,0 to 2) of unsigned(7 downto 0);
signal matC : matType := (others => (others => X"00")); 

begin

matrix_multiplier : matrix_mult port map (Clock, reset, start, A,B, C,done);

--generate a 50Mhz clock for testing the design.
Clk_generator : process
begin
    wait for 10 ns;
    Clock <= not Clock;
end process;

apply_inputs : process
begin
    reset <= '1';
    wait for 100 ns;
    reset <= '0';
    wait for 20 ns;
    A <= X"09" & X"08" & X"07" & X"06" & X"05" & X"04" & X"03" & X"02" & X"01";
    B <= X"01" & X"09" & X"08" & X"07" & X"06" & X"05" & X"04" & X"03" & X"02";
    start <= '1';
    wait for 20 ns;
    start <= '0';
    wait until done = '1';
    --The result C should be (93,150,126,57,96,81,21,42,36)
    wait for 5 ns;
    for i in 0 to 2 loop --run through the rows
        for j in 0 to 2 loop --run through the columnss
            matC(i, j) <= C((i * 3 + j + 1) * 8 - 1 downto (i * 3 + j) * 8);
        end loop;
    end loop;
    wait;
end process;

end behav;


Simulation Results:

    The design was simulated successfully using Modelsim SE 10.4a version. Screenshots of the simulation waveform is shown below:




    Please let me know if you are unable to get the code to work or if its not synthesisable. Good luck with your projects. 

 

Tuesday, September 5, 2017

Pushbutton DeBounce circuit in VHDL

Lets start with some basic definitions,

What is Bouncing?

Bouncing is the tendency of any two metal contacts in an electronic device to generate multiple signals as the contacts close or open.

What is DeBouncing?

Debouncing is any kind of hardware device or software that ensures that only a single signal will be acted upon for a single opening or closing of a contact.

In this post, I want to explain how this is done and even have shared a code in VHDL. When you want to test your piece of code on a FPGA board, you might need to use the push-buttons for taking input from the user. 

Some boards have an integrated debouncer which takes care of the bouncing problem of pushbuttons. But if its not already there, its up to you to implement it in HDL. 

When a push-button is pressed, this is how the signal, it generates might look like,

Image result for debounce vhdl

Our aim here, is to process this signal and get a single pulse for each push button press. 

There are many debouncer codes available online. Here I have implemented, my own method, so use it after going through it carefully.

I created the FSM shown below and implemented it using VHDL.

What I am trying to do here is, every time I detect an activity on the button, I start a counter. When the counter reaches the maximum count, I check again, if the button is still active. If yes, I take it as a valid button input, otherwise I ignore it and wait for another activity on the button.

VHDL code for DeBouncer circuit:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity DeBounce is
    port(   Clock : in std_logic;
                Reset : in std_logic;
            button_in : in std_logic;
            pulse_out : out std_logic
        );
end DeBounce;

architecture behav of DeBounce is

--the below constants decide the working parameters.
--the higher this is, the more longer time the user has to press the button.
constant COUNT_MAX : integer := 20; 
--set it '1' if the button creates a high pulse when its pressed, otherwise '0'.
constant BTN_ACTIVE : std_logic := '1';

signal count : integer := 0;
type state_type is (idle,wait_time); --state machine
signal state : state_type := idle;

begin
  
process(Reset,Clock)
begin
    if(Reset = '1') then
        state <= idle;
        pulse_out <= '0';
   elsif(rising_edge(Clock)) then
        case (state) is
            when idle =>
                if(button_in = BTN_ACTIVE) then  
                    state <= wait_time;
                else
                    state <= idle; --wait until button is pressed.
                end if;
                pulse_out <= '0';
            when wait_time =>
                if(count = COUNT_MAX) then
                    count <= 0;
                    if(button_in = BTN_ACTIVE) then
                        pulse_out <= '1';
                    end if;
                    state <= idle;  
                else
                    count <= count + 1;
                end if; 
        end case;       
    end if;        
end process;                  
                                                                                
end architecture behav;

Testbench code for DeBouncer circuit:

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
 
ENTITY tb_alu IS
END tb_alu;
 
ARCHITECTURE behavior OF tb_alu IS 
 
    -- Component Declaration for the Unit Under Test (UUT)
 
    COMPONENT DeBounce
    PORT(
         Clock : IN  std_logic;
         Reset : IN  std_logic;
         button_in : IN  std_logic;
         pulse_out : OUT  std_logic
        );
    END COMPONENT;
    

   --Inputs
   signal Clock : std_logic := '0';
   signal Reset : std_logic := '0';
   signal button_in : std_logic := '0';

    --Outputs
   signal pulse_out : std_logic;

   -- Clock period definitions
   constant Clock_period : time := 10 ns;
 
BEGIN
 
    -- Instantiate the Unit Under Test (UUT)
   uut: DeBounce PORT MAP (
          Clock => Clock,
          Reset => Reset,
          button_in => button_in,
          pulse_out => pulse_out
        );

   -- Clock process definitions
   Clock_process :process
   begin
        Clock <= '0';
        wait for Clock_period/2;
        Clock <= '1';
        wait for Clock_period/2;
   end process;
 

   -- Stimulus process
   stim_proc: process
   begin        
        button_in <= '0';
        reset <= '1';
      -- hold reset state for 100 ns.
      wait for 100 ns;
        reset <= '0';
      wait for Clock_period*10;
        --first activity
        button_in <= '1';   wait for Clock_period*2;
        button_in <= '0';   wait for Clock_period*1;
        button_in <= '1';   wait for Clock_period*1;
        button_in <= '0';   wait for Clock_period*20;
        --second activity
        button_in <= '1';   wait for Clock_period*1;
        button_in <= '0';   wait for Clock_period*1;
        button_in <= '1';   wait for Clock_period*1;
        button_in <= '0';   wait for Clock_period*2;
        button_in <= '1';   wait for Clock_period*20;
        button_in <= '0';   
      wait;
   end process;

END;

Simulation waveform from Xilinx ISE:



The design was synthesised successfully using Xilinx ISE 14.6.  Before you use the code in your design, make sure you choose the correct values for COUNT_MAX and  BTN_ACTIVE.