Table of Contents

    1. VHDL Parameterized (Configuration on Altera/Intel Cyclone V in Quartus)

    VHDL (VHSIC Hardware Description Language) is widely used for FPGA and ASIC design due to its flexibility and capability to describe complex digital circuits. Parameterized code generation in VHDL allows for the creation of versatile and reusable components by defining parameters that can be adjusted to meet specific design requirements. This is particularly useful when working with Altera (now Intel) Cyclone V FPGAs using the Quartus development environment. This article will provide an overview of how to generate and configure parameterized VHDL code for Cyclone V FPGAs in Quartus.

    2. Parameterized Code in VHDL

    Parameterized code in VHDL is achieved using generics. Generics allow you to pass parameters to a VHDL entity, making your code more flexible and reusable. Here’s an example of a simple VHDL module with generics:

    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL;
    
    entity ParametrizedModule is
      generic (
        WIDTH : integer := 8
      );
      port (
        clk   : in  std_logic;
        reset : in  std_logic;
        data_in  : in  std_logic_vector(WIDTH-1 downto 0);
        data_out : out std_logic_vector(WIDTH-1 downto 0)
      );
    end entity ParametrizedModule;
    
    architecture Behavioral of ParametrizedModule is
    begin
      process(clk, reset)
      begin
        if reset = '1' then
          data_out <= (others => '0');
        elsif rising_edge(clk) then
          data_out <= data_in;
        end if;
      end process;
    end architecture Behavioral;
    

    In this example, the WIDTH parameter defines the width of the data_in and data_out signals. This allows the same module to be instantiated with different data widths without modifying the code.

    3. Configuring Parameterized VHDL in Quartus

    3.1 Create a New Quartus Project

    1. Open Quartus and create a new project:
    2. Go to File -> New Project Wizard.
    3. Follow the prompts to specify the project directory, name, and target device (Cyclone V).

    3.2 Write the Parameterized VHDL Code

    1. In the Quartus project directory, create a new VHDL file (e.g., ParametrizedModule.vhd).
    2. Write or paste the parameterized VHDL code into this file.
    3. Save the file.

    3.2 Add the VHDL File to the Project

    1. In Quartus, add the VHDL file to the project:
    2. Go to Project -> Add/Remove Files in Project.
    3. Add the ParametrizedModule.vhd file.

    3.3 Instantiate the Parameterized Module

    1. Create a top-level VHDL file where you will instantiate the parameterized module.
    2. Instantiate the module with specific parameter values. For example:
    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    
    entity TopLevel is
      port (
        clk   : in  std_logic;
        reset : in  std_logic;
        data_in  : in  std_logic_vector(15 downto 0);
        data_out : out std_logic_vector(15 downto 0)
      );
    end entity TopLevel;
    
    architecture Behavioral of TopLevel is
      signal data_out_internal : std_logic_vector(15 downto 0);
    begin
      U1: entity work.ParametrizedModule
        generic map (
          WIDTH => 16
        )
        port map (
          clk => clk,
          reset => reset,
          data_in => data_in,
          data_out => data_out_internal
        );
    
      data_out <= data_out_internal;
    
    end architecture Behavioral;
    

    3.4 Compile the Project

    1. Save all files.
    2. Compile the project by clicking Processing -> Start Compilation.

    3.5 Assign Pins

    1. Assign the FPGA pins to the top-level ports:
    2. Go to Assignments -> Pin Planner.
    3. Assign the appropriate FPGA pins to clk, reset, data_in, and data_out.

    3.6 Program the FPGA

    1. Connect your Cyclone V FPGA development board to your computer.
    2. Open the Programmer tool in Quartus:
    3. Go to Tools -> Programmer.
    4. Add the compiled SOF file and program the FPGA.

    3.7 Using the simulation university waveforme

    Top-level

    4. Using Block diagram / Schematic file

    Quartus also enables us to create a VHDL model using schematics. Quartus compiles it and generates the VHDL code, which is not easily accessible. However, you can find it in the synthesis folder where Quartus generates the files.

    This method is not very common or popular, but it does exist. Here is a picture of a full adder designed using the block diagram and schematic file. Creating this schematic is very easy; you just need to open a new file and choose the Block Diagram / Schematic file option.

    Schematic file

    5. Advanced VHDL Configuration in Quartus for Cyclone V

    5.1. Advanced Generics and Configurations

    Example: Parameterized FIFO

    A First-In-First-Out (FIFO) buffer is a common component that can benefit from advanced parameterization. Let's define a parameterized FIFO with generics for data width and depth.

    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL;
    
    entity ParametrizedFIFO is
      generic (
        DATA_WIDTH : integer := 8;
        DEPTH      : integer := 16
      );
      port (
        clk     : in  std_logic;
        reset   : in  std_logic;
        wr_en   : in  std_logic;
        rd_en   : in  std_logic;
        data_in : in  std_logic_vector(DATA_WIDTH-1 downto 0);
        data_out: out std_logic_vector(DATA_WIDTH-1 downto 0);
        empty   : out std_logic;
        full    : out std_logic
      );
    end entity ParametrizedFIFO;
    
    architecture Behavioral of ParametrizedFIFO is
      type memory_type is array (0 to DEPTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
      signal memory     : memory_type;
      signal wr_ptr     : integer range 0 to DEPTH-1 := 0;
      signal rd_ptr     : integer range 0 to DEPTH-1 := 0;
      signal fifo_count : integer range 0 to DEPTH := 0;
    begin
      process(clk, reset)
      begin
        if reset = '1' then
          wr_ptr <= 0;
          rd_ptr <= 0;
          fifo_count <= 0;
        elsif rising_edge(clk) then
          if wr_en = '1' and fifo_count < DEPTH then
            memory(wr_ptr) <= data_in;
            wr_ptr <= (wr_ptr + 1) mod DEPTH;
            fifo_count <= fifo_count + 1;
          end if;
          if rd_en = '1' and fifo_count > 0 then
            data_out <= memory(rd_ptr);
            rd_ptr <= (rd_ptr + 1) mod DEPTH;
            fifo_count <= fifo_count - 1;
          end if;
        end if;
      end process;
    
      empty <= '1' when fifo_count = 0 else '0';
      full  <= '1' when fifo_count = DEPTH else '0';
    end architecture Behavioral;
    

    Example: Parameterized FIFO test bench

    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL;
    
    entity tb_ParametrizedFIFO is
    end entity tb_ParametrizedFIFO;
    
    architecture Behavioral of tb_ParametrizedFIFO is
      constant DATA_WIDTH : integer := 8;
      constant DEPTH      : integer := 16;
    
      signal clk     : std_logic := '0';
      signal reset   : std_logic := '0';
      signal wr_en   : std_logic := '0';
      signal rd_en   : std_logic := '0';
      signal data_in : std_logic_vector(DATA_WIDTH-1 downto 0) := (others => '0');
      signal data_out: std_logic_vector(DATA_WIDTH-1 downto 0);
      signal empty   : std_logic;
      signal full    : std_logic;
    
      -- Clock period definition
      constant clk_period : time := 10 ns;
    
    begin
      -- Instantiate the Unit Under Test (UUT)
      uut: entity work.ParametrizedFIFO
        generic map (
          DATA_WIDTH => DATA_WIDTH,
          DEPTH      => DEPTH
        )
        port map (
          clk     => clk,
          n_reset   => reset,
          wr_en   => wr_en,
          rd_en   => rd_en,
          data_in => data_in,
          data_out=> data_out,
          empty   => empty,
          full    => full
        );
    
      -- 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
      stim_proc: process
      begin     
        -- Reset the FIFO
        reset <= '0';
        wait for clk_period*2;
        reset <= '1';
        wait for clk_period*2;
    
        -- Write to the FIFO
        for i in 0 to DEPTH-1 loop
          data_in <= std_logic_vector(to_unsigned(i, DATA_WIDTH));
          wr_en <= '1';
          wait for clk_period;
        end loop;
        wr_en <= '0';
        wait for clk_period;
    
        -- Read from the FIFO
        for i in 0 to DEPTH-1 loop
          rd_en <= '1';
          wait for clk_period;
        end loop;
        rd_en <= '0';
        wait for clk_period;
    
        -- Finish simulation
        wait;
      end process;
    
    end architecture Behavioral;
    
    

    fifo

    6. Example: Using a Configuration File in VHDL

    Let's create a VHDL model that reads configuration parameters from a text file.

    6.1 Create a Configuration File

    Create a text file named config.txt with the following content:

    16
    32
    

    These two numbers will represent configuration parameters such as data width and depth for our VHDL model.

    6.2 VHDL Model

    Create a VHDL entity that uses these configuration parameters. The example below demonstrates a simple model that reads the parameters and uses them in a process.

    6.2.1 Top-level VHDL File: ConfigurableModule.vhd

    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL;
    use IEEE.STD_LOGIC_TEXTIO.ALL;
    use STD.TEXTIO.ALL;
    
    entity ConfigurableModule is
      port (
        clk   : in  std_logic;
        reset : in  std_logic;
        data_in  : in  std_logic_vector(15 downto 0);
        data_out : out std_logic_vector(15 downto 0)
      );
    end entity ConfigurableModule;
    
    architecture Behavioral of ConfigurableModule is
      constant FILE_NAME : string := "config.txt";
      signal data_width : integer;
      signal depth : integer;
      type memory_type is array (0 to depth-1) of std_logic_vector(data_width-1 downto 0);
      signal memory : memory_type;
      signal wr_ptr : integer := 0;
    begin
    
      process(clk, reset)
        file config_file : text open read_mode is FILE_NAME;
        variable line_content : line;
        variable width, depth_var : integer;
      begin
        if reset = '1' then
          data_out <= (others => '0');
        elsif rising_edge(clk) then
          if endfile(config_file) = false then
            readline(config_file, line_content);
            read(line_content, width);
            readline(config_file, line_content);
            read(line_content, depth_var);
            data_width <= width;
            depth <= depth_var;
          end if;
          -- Example usage of data_width and depth
          if wr_ptr < depth then
            memory(wr_ptr) <= data_in;
            wr_ptr <= wr_ptr + 1;
          end if;
          data_out <= memory(wr_ptr - 1);
        end if;
      end process;
    end architecture Behavioral;
    

    6.3 Explanation

    1. Configuration File: config.txt contains two integers representing the data width and depth.
    2. Entity and Architecture: The entity ConfigurableModule has standard clk, reset, data_in, and data_out ports.
    3. Process for Reading the File:
    4. Use the textio library to read from the file.
    5. Open the file config.txt in read mode.
    6. Read each line and convert it to an integer.
    7. Assign the values to data_width and depth.
    8. Usage: The data_width and depth parameters are then used within the process to create a memory buffer and manage writing and reading operations.

    6.4 Simulation

    This model can be simulated using a VHDL simulator like ModelSim or GHDL. The simulator will read the configuration file and apply the parameters during simulation.

    6.4.1 Steps for Simulation in ModelSim

    1. Compile the VHDL File: sh vcom ConfigurableModule.vhd

    2. Simulate the Model: sh vsim work.ConfigurableModule

    3. Run the Simulation: sh run -all

    Quartus projet GitHub link

    7. Conclusion

    Using a file for VHDL configuration, leveraging the textio library for dynamic parameterization, enhances the flexibility, reusability, and adaptability of designs, particularly in simulation and testing, while advanced configuration techniques ensure efficient and scalable digital systems in FPGA development.

    📝 Article Author : SEMRADE Tarik
    🏷️ Author position : Embedded Software Engineer
    🔗 Author LinkedIn : LinkedIn profile

    Comments