VHDL coding tips and tricks: October 2010

Sunday, October 31, 2010

Sequence detector using state machine in VHDL

   Some readers were asking for more examples related with state machine and some where asking for codes related with sequence detector.This article will be helpful for state machine designers and for people who try to implement sequence detector circuit in VHDL.
   I have created a state machine for non-overlapping detection of a pattern "1011" in a sequence of bits. The state machine diagram is given below for your reference.

The VHDL code for the same is given below. I have added comments for your easy understanding.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

--Sequence detector for detecting the sequence "1011".
--Non overlapping type.
entity seq_det is
port(   clk : in std_logic;  --clock signal
        reset : in std_logic;   --reset signal
        seq : in std_logic;    --serial bit sequence   
        det_vld : out std_logic  --A '1' indicates the pattern "1011" is detected in the sequence.
        );
end seq_det;

architecture Behavioral of seq_det is

type state_type is (A,B,C,D);  --Defines the type for states in the state machine
signal state : state_type := A;  --Declare the signal with the corresponding state type.

begin

process(clk)
begin
    if( reset = '1' ) then     --resets state and output signal when reset is asserted.
        det_vld <= '0';
        state <= A;
    elsif ( rising_edge(clk) ) then   --calculates the next state based on current state and input bit.
        case state is
            when A =>   --when the current state is A.
                det_vld <= '0';
                if ( seq = '0' ) then
                    state <= A;
                else   
                    state <= B;
                end if;
            when B =>   --when the current state is B.
                if ( seq = '0' ) then
                    state <= C;
                else   
                    state <= B;
                end if;
            when C =>   --when the current state is C.
                if ( seq = '0' ) then
                    state <= A;
                else   
                    state <= D;
                end if;
            when D =>   --when the current state is D.
                if ( seq = '0' ) then
                    state <= C;
                else   
                    state <= A;
                    det_vld <= '1';   --Output is asserted when the pattern "1011" is found in the sequence.
                end if;    
            when others =>
                NULL;
        end case;
    end if;
end process;   

end Behavioral;

If you check the code you can see that in each state we go to the next state depending on the current value of inputs.So this is a mealy type state machine.
The testbench code used for testing the design is given below.It sends a sequence of bits "1101110101" to the module. The code doesnt exploit all the possible input sequences. If you want another sequence to be checked then edit the testbench code.  If it is not working as expected, let me know.

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;

ENTITY blog_cg IS
END blog_cg;

ARCHITECTURE behavior OF blog_cg IS

   signal clk,reset,seq,det_vld : std_logic := '0';
   constant clk_period : time := 10 ns;

BEGIN

    -- Instantiate the Unit Under Test (UUT)
   uut: entity work.seq_det PORT MAP (
          clk => clk,
          reset => reset,
          seq => seq,
          det_vld => det_vld
        );

   -- Clock process definitions
   clk_process :process
   begin
        clk <= '0';
        wait for clk_period/2;
        clk <= '1';
        wait for clk_period/2;
   end process;

   -- Stimulus process : Apply the bits in the sequence one by one.
   stim_proc: process
   begin       
        seq <= '1';             --1
      wait for clk_period;
        seq <= '1';             --11
      wait for clk_period;
        seq <= '0';             --110
      wait for clk_period;
        seq <= '1';             --1101
      wait for clk_period;
        seq <= '1';             --11011
      wait for clk_period;
        seq <= '1';             --110111
      wait for clk_period;
        seq <= '0';             --1101110
      wait for clk_period;
        seq <= '1';             --11011101
      wait for clk_period;
        seq <= '0';             --110111010
      wait for clk_period;
        seq <= '1';             --1101110101
      wait for clk_period;
      wait;        
   end process;

END;

The simulated waveform is shown below:

Note:- The code was simulated using Xilinx 12.1 version. The results may vary slightly depending on your simulation tool.

How to use coe file for initializing BRAM

   I hope you have gone through my previous post about how to create a BRAM using core generator.If you are not familiar with the steps then please read it here.This post is a continuation of the above mentioned article. Normally we initialize all the memory locations in a BRAM to zero using the "Fill Remaining memory locations" option in core generator. But this article will tell you how to use coe file as an initialization vector.Follow the steps:
1)Create a BRAM of depth = 16 and read and write width = 8 bits.
2)Check the "Load Init File" under the "Memory Initialization" option. Also select the file named "bram_contents.coe".
3)Before clicking on the generate button create a new file named bram_contents.coe and paste the following contents into it:
memory_initialization_radix=10;
memory_initialization_vector=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16;
In the above text radix means the raidx of the data. I have written decimal data in the vector so my radix is "10". If you want to give the memory contents in binary form then change the coe file like this:
memory_initialization_radix=2;
memory_initialization_vector=0000,0001,0010,0011,0100,0101,0110,0111,1000,1001,1010,1011,1100,1101,1110,1111;
4)Now click on the generate button.

Now simulate the BRAM with the following testbench code given below:

library IEEE;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity testbench is
end testbench;

architecture Behavioral of testbench is

--temporary signal declarations.
signal ena : std_logic := '0';
signal wea : std_logic_VECTOR(0 downto 0):="0";
signal dina,douta : std_logic_VECTOR(7 downto 0) := (others => '0');
signal addra : std_logic_VECTOR(3 downto 0) := (others => '0');
signal clk : std_logic := '0';

begin

--Instantiating BRAM.
BRAM : entity work.BRAM_test
    port map(
    clka => clk,  --clock for writing data to RAM.
    ena => ena,   --Enable signal.
    wea => wea,   --Write enable signal for Port A.
    addra => addra, --4 bit address for the RAM.
    dina => dina,   --8 bit data input to the RAM.
    douta => douta);  --8 bit data output from the RAM.

--Simulation process.
process
begin
    wait for 1 ns;
    addra <= X"0";  --reset the address value for reading from memory location "0"
    --reading all the 16 memory locations in the BRAM.
    for i in 0 to 15 loop
        ena <= '1';  --Enable RAM always.
        wea <= "0";
        wait for 2 ns;
        addra <= addra + "1";
    end loop;
    wait;
end process;  

--Clock generation - Generates 500 MHz clock with 50% duty cycle.
process
begin
    clk <= '1';
    wait for 1 ns;  --"ON" time.
    clk <= '0';
    wait for 1 ns;  --"OFF" time.
end process;  
 
end Behavioral;

The above code is used for reading the BRAM addresses from 0 to 15 which should display the contents of coe file. Check the given waveform for verification.
Note :- This is just one use of coe file.In different IP cores we can use coe file in different ways. For example coe file can be used to give the filter coefficients for FIR IP core.For testing purpose I have used Xilinx ISE 12.1 version and BRAM version 4.1. The options in the core generator tool may vary slightly depending on the version you are using.

Thursday, October 7, 2010

Design and simulation of BRAM using Xilinx Core generator

   BRAM(Block Random access memory) is an advanced memory constructor that generates area and performance-optimized memories using embedded block RAM resources in Xilinx FPGAs.I hope you have already gone through the Core generator introductory tutorial before.If you haven't please read those articles here.

   For creating a custom BRAM, run Core Generator software:
1)Create a new project and select the required device details for which you want to generate the model.
2)Select "View by Function" category.
3)Select "Memories & Storage Elements" and then "RAMs & ROMs".
4)Double click on the "Block Memory Generator(version 4.1)" and a new window will open similar to the one shown below:

   Now we can start customizing the BRAM as per our requirements. For testing purpose I am going to generate a single port RAM with size 256*8 bit size.The settings I have used are given below:
1)Component Name:BRAM_test
2)Uncheck "Use byte write enable".
3)Select the algorithm as "Low Power".
4)Go to next page.
5)Select write width and read width as "8".
6)Set write depth as "256" which is the number of locations in RAM.
7)Operating mode is "No change".
8)Go to next page.
9)Uncheck all options except "Fill Remaining memory locations" with "0".If you want you can initialize the RAM with a coe file also. Using coe file to initialize BRAM will be explained through another article.
10)Keep other options as "default" and click on "generate" button.The software will now start generating the necessary files such as ngc,vhd,xco etc.

Now close core generator software and open Xilinx ISE.Create a new project with the same device details you have used to create the core generator project.Add BRAM_test.xco or BRAM_test.ngc to this project. Create a new VHDL file in this project directory and name it as BRAM_main.vhd. This code will act like a testbench code for testing the BRAM you have generated. In general you can see how to make it work after going through this example.

The contents of BRAM_main.vhd is displayed below:

library IEEE;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity testbench is  
end testbench;

architecture Behavioral of testbench is

--temporary signal declarations.
signal ena : std_logic := '0';
signal wea : std_logic_VECTOR(0 downto 0):="0";
signal addra,dina,douta : std_logic_VECTOR(7 downto 0) := (others => '0');
signal clk : std_logic := '0';

begin

--Instantiating BRAM.
BRAM : entity work.BRAM_test
    port map(
    clka => clk,  --clock for writing data to RAM.
    ena => ena,   --Enable signal.
    wea => wea,   --Write enable signal for Port A.
    addra => addra, --8 bit address for the RAM.
    dina => dina,   --8 bit data input to the RAM.
    douta => douta);  --8 bit data output from the RAM.

--Simulation process.
process
begin
    wait for 1 ns;
    --Writing to all the memory locations of the BRAM.Set wea "1" for this.
    for i in 0 to 255 loop
        ena <= '1';  --Enable RAM always.
        wea <= "1";
        wait for 2 ns;
        addra <= addra + "1";
        dina <= dina + "1";
    end loop;  
    addra <= X"00";  --reset the address value for reading from memory location "0"
    --reading all the 256 memory locations in the BRAM.
    for i in 0 to 255 loop
        ena <= '1';  --Enable RAM always.
        wea <= "0";
        wait for 2 ns;
        addra <= addra + "1";
    end loop;
    wait;
end process;   

--Clock generation - Generates 500 MHz clock with 50% duty cycle.
process
begin
    clk <= '1';
    wait for 1 ns;  --"ON" time.
    clk <= '0';
    wait for 1 ns;  --"OFF" time.
end process;   
   
end Behavioral;

The signals used in the port mapping can be checked by viewing the BRAM_test.vhd file generated by the core generator tool.Simply copy all those signal names for instantiating the BRAM component in your design.Also remember that you have to either add the .xco file or the .ngc file to the project, for simulation to work.

The simulated waveform is shown below:


Note :- This is the most simplest BRAM. The design will get complicated when you go from single port to dual port RAM's.But the basic idea remains the same.By reading the documentation supplied by Xilinx you can explore more settings used in the GUI tool.For testing purpose I have used Xilinx ISE 12.1 version and BRAM version 4.1. The options in the core generator tool may vary slightly depending on the version you are using.

Sunday, October 3, 2010

A 3 bit Magnitude Comparator using logic gates

   I have been getting lot of requests asking for VHDL code for digital comparators. In this post I have shared a 3 bit comparator which is designed using basic logic gates such as XNOR, OR, AND etc. The code was tested using  a testbench code which tested the design for all the 81 combinations of inputs.

See the code below:

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

entity comparator is
port( a,b : in unsigned(2 downto 0);  --3 bit numbers to be compared
        a_eq_b : out std_logic;  --a equals b
        a_le_b : out std_logic;  --a less than b
        a_gt_b : out std_logic   --a greater than b
      );    
end comparator;

architecture gate_level of comparator is

signal temp1,temp2,temp3,temp4,temp5,temp6,temp7,temp8,temp9 : std_logic := '0';

BEGIN

temp1 <= not(a(2) xor b(2));  --XNOR gate with 2 inputs.
temp2 <= not(a(1) xor b(1));  --XNOR gate with 2 inputs.
temp3 <= not(a(0) xor b(0));  --XNOR gate with 2 inputs.
temp4 <= (not a(2)) and b(2);
temp5 <= (not a(1)) and b(1);
temp6 <= (not a(0)) and b(0);
temp7 <= a(2) and (not b(2));
temp8 <= a(1) and (not b(1));
temp9 <= a(0) and (not b(0));

a_eq_b <= temp1 and temp2 and temp3;  -- for a equals b.
a_le_b <= temp4 or (temp1 and temp5) or (temp1 and temp2 and temp6); --for a less than b
a_gt_b <= temp7 or (temp1 and temp8) or (temp1 and temp2 and temp9); --for a greater than b

end gate_level;

The testbench code for testing the design is given below:

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;

ENTITY tb IS
END tb;

ARCHITECTURE behavior OF tb IS
   --Inputs
   signal a : unsigned(2 downto 0) := (others => '0');
   signal b : unsigned(2 downto 0) := (others => '0');
    --Outputs
   signal a_eq_b : std_logic;
   signal a_le_b : std_logic;
   signal a_gt_b : std_logic;

    signal i,j : integer;

BEGIN

    -- Instantiate the Unit Under Test (UUT)
   uut: entity work.comparator PORT MAP (
          a => a,
          b => b,
          a_eq_b => a_eq_b,
          a_le_b => a_le_b,
          a_gt_b => a_gt_b
        );

   -- Stimulus process
   stim_proc: process
   begin       
        for i in 0 to 8 loop
            for j in 0 to 8 loop
                a <= to_unsigned(i,3); --integer to unsigned type conversion
                b <= to_unsigned(j,3);
                wait for 10 ns;
            end loop;
        end loop;  
   end process;

END;

A part of the simulation waveform is given below:

Note:- For viewing the full results simulate the program yourself. The code was tested using Xilinx ISE 12.1 version. But it will work with almost all the compilers.The code is also synthesisable.