element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
FPGA
  • Technologies
  • More
FPGA
Blog VHDL: Convert a Fixed Module into a Generic Module for Reuse
  • Blog
  • Forum
  • Documents
  • Quiz
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
FPGA requires membership for participation - click to join
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 1 Dec 2021 3:35 PM Date Created
  • Views 23558 views
  • Likes 6 likes
  • Comments 2 comments
  • tutorial
  • zynq
  • xilinx
  • fpga
  • vivado
  • vhdl
  • summer_of_fpga
  • pynq
Related
Recommended

VHDL: Convert a Fixed Module into a Generic Module for Reuse

Jan Cumps
Jan Cumps
1 Dec 2021

The goal of this post is turn a fixed VHDL design into a configurable one. Change a fixed RAM IP into RAM that can be sized, without changing source or logic.

I use michaelkellett's VHDL RAM Memory design before: use RAM design for Altera Cyclone on Vivado and PYNQ.
In that design, the size of the memory , both data size and number of elements, was fixed. 64 elements. 12 bits per element.
I'm going to reuse Michael's design a few times in the future, with different memory sizes.
That's a good opportunity to generalise the VHDL. This post describes what I've done.

The outcome is a RAM IP that can be used identical to the original one. With differently size.
I made the default size identical to what Michael designed. 64 positions of 12 bit data.
In my design, the actual size is 128 positions of 16 bit data.

That's not randomly chosen: in the future this block will hold a sample block from the Zynq XADC.
But first I want to test.

Generalising the VHDL RAM design

Check the code in the original block for the fixed design, where size of data and number of elements are fixed.
I'm generalising 3 of the attributes used in that design:

  • the data width: how many bits in each memory element
  • the data length: how many elements can the RAM store
  • the address length: how many bits needed in the address register to address all elements (e.g: 6 can address 64 distinct elements, 7 for 128, 8 for 256, ...).

The Generic code

You can define defaults in the generic code, and I use the values of Michael's original RAM design, 64 elements of 12 bit data, address needs to be 6 bits.

-- from Micheal Kellett: https://www.element14.com/community/groups/fpga-group/blog/2021/07/31/cheap-cyclone-10

library IEEE;
use IEEE.std_logic_1164.all;   
use IEEE.NUMERIC_STD.all;

package mem_inf is
component mem_inf_ram is
     generic (
       data_width : integer := 12; -- default 12 bits per element
       data_length: integer := 64; -- default 64 addressable elements 
       address_width : integer := 6 -- default range fits in 6 bits
     );
     port(
         we : in STD_LOGIC;
         en : in STD_LOGIC;
         clk : in STD_LOGIC;
         address : in STD_LOGIC_VECTOR(address_width - 1 downto 0);
         data : in STD_LOGIC_VECTOR(data_width - 1  downto 0);
         q : out STD_LOGIC_VECTOR(data_width - 1 downto 0)
         );
end component;
end package;

library IEEE;
use IEEE.std_logic_1164.all;   
use IEEE.NUMERIC_STD.all;

entity mem_inf_ram is
     generic (
       data_width : integer;
       data_length: integer; 
       address_width : integer
     );
     port(
         we : in STD_LOGIC;
         en : in STD_LOGIC;
         clk : in STD_LOGIC;
         address : in STD_LOGIC_VECTOR(address_width - 1 downto 0);
         data : in STD_LOGIC_VECTOR(data_width - 1  downto 0);
         q : out STD_LOGIC_VECTOR(data_width - 1 downto 0)
         );
end mem_inf_ram;

architecture mem_inf_ram of mem_inf_ram is   

    type ram_type is array (0 to data_length - 1) of std_logic_vector(data_width - 1 downto 0);   -- array must be 0 - n, if declared the other way wit downto to match Xilinx example then Quartus fitter fails
    signal ram : ram_type;

begin

    process(clk)
    begin
        if rising_edge(clk) then
            if en = '1' then
                if we = '1' then
                    ram(to_integer(unsigned((address)))) <= data;
                end if;
                q <= ram(to_integer(unsigned((address))));
            end if;
        end if;
    end process;

end mem_inf_ram;

The Specific implementation code

The block of memory that will be used in the design needs to be 128 elements, 16 bit data, and the address needs to be 7 bits to address all elements.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

use work.mem_inf.all;

entity mem_ram_128_16 is
  port (
         we : in STD_LOGIC;
         en : in STD_LOGIC;
         clk : in STD_LOGIC;
         address : in STD_LOGIC_VECTOR(7 - 1 downto 0);
         data : in STD_LOGIC_VECTOR(16 - 1 downto 0);
         q : out STD_LOGIC_VECTOR(16 - 1 downto 0)
  );
end mem_ram_128_16;

architecture Behavioral of mem_ram_128_16 is

begin

  mem : mem_inf_ram
    generic map (
       data_width => data'length,
       data_length => 128, 
       address_width => address'length
    )
    port map (
      we => we,
      en   => en,
      clk => clk,
      address => address,
      data => data,
      q => q
      );
      
end Behavioral;

The image below show how this block implements a usable RAM IP, and overrides the defaults:

image

All of this is vendor independent. The generic design is portable.

I'll use it on a specific design though: a Zynq FPGA. The RAM IP block can be dragged on a Vivado Block Design like any other IP.

Try it on Zynq / PYNQ

Block Design

The final 128x16 RAM IP can be used just like in the original post. You'll find the exact same elements back.
Because the address and data widths are different, you'll see that there are different bus widths used in this design: 7 + 16 = 23.

image

See the original post for the settings. Just adapt the bus, address and data sizes, in all blocks downstream the AXI Interconnect.

Test on the Zynq

I'm going to use a similar testbed as in the original post. The goal is only to see if the generic design works.
As you can see on the images, the testbed is called memory_ram_dma.ypinb. This is an indication of what my future plans are.

Setup:

image

Testbed:

image

Results:

image

Success. The generic object is ready for use in designs.
This is one of the advantages: the reusable part - that contains the actual logic - is pre-tested.

Vivado project and jupyter notebook attached.

 

Pynq - Zync - Vivado series
Add Pynq-Z2 board to Vivado
Learning Xilinx Zynq: port a Spartan 6 PWM example to Pynq
Learning Xilinx Zynq: use AXI with a VHDL example in Pynq
VHDL PWM generator with dead time: the design
Learning Xilinx Zynq: use AXI and MMIO with a VHDL example in Pynq
Learning Xilinx Zynq: port Rotary Decoder from Spartan 6 to Vivado and PYNQ
Learning Xilinx Zynq: FPGA based PWM generator with scroll wheel control
Learning Xilinx Zynq: use RAM design for Altera Cyclone on Vivado and PYNQ
Learning Xilinx Zynq: a Quadrature Oscillator - 2 implementations
Learning Xilinx Zynq: a Quadrature Oscillator - variable frequency
Learning Xilinx Zynq: Hardware Accelerated Software
Automate Repeatable Steps in Vivado
Learning Xilinx Zynq: Try to make my own Accelerated OpenCV Function - 1: Vitis HLS
Learning Xilinx Zynq: Try to make my own Accelerated OpenCV Function - 2: Vivado Block Design
Learning Xilinx Zynq: Logic Gates in Vivado
Learning Xilinx Zynq: Interrupt ARM from FPGA fabric
Learning Xilinx Zynq: reuse and combine components to build a multiplexer
PYNQ version 2.7 (Austin) is released
PYNQ and Zynq: the Vitis HLS Accelerator with DMA training - Part 1: Turn C++ code into an FPGA IP
PYNQ and Zynq: the Vitis HLS Accelerator with DMA training - Part 2: Add the Accelerated IP to a Vivado design
PYNQ and Zynq: the Vitis HLS Accelerator with DMA training - Part 3: Use the Hardware Accelerated Code in Software
PYNQ and Zynq: the Vitis HLS Accelerator with DMA training - Deep Dive: the data streams between Accelerator IP and ARM processors
Use the ZYNQ XADC with DMA part 1: bare metal
Use the ZYNQ XADC with DMA part 2: get and show samples in PYNQ
VHDL: Convert a Fixed Module into a Generic Module for Reuse
memory_ram_dma_vivado_project_and_jupyter_notebook.zip
  • Sign in to reply
  • Jan Cumps
    Jan Cumps over 3 years ago in reply to Jan Cumps

    ... the if_002 report, about enclosing conditions with (), could be done automaticaly by the utility.
    The process_016 and _018, about using an unlabeled process, is something you have to do manually.

    Result:

    image

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 3 years ago

    If you, like me, don't have a particular writing style for VHDL, but you do like consistency, there's a style checker available: vhdl style guide.
    I haven't made up my mind about what's best, but someone else has. I'm going to see what the effect is.

    I'm using this file as example:

    -- from Micheal Kellett: https://www.element14.com/community/groups/fpga-group/blog/2021/07/31/cheap-cyclone-10
    
    library IEEE;
    use IEEE.std_logic_1164.all;   
    use IEEE.NUMERIC_STD.all;
    
    package mem_inf is
    component mem_inf_ram is
         generic (
           data_width : integer := 12; -- default 12 bits per element
           data_length: integer := 64; -- default 64 addressable elements 
           address_width : integer := 6 -- default range fits in 6 bits
         );
         port(
             we : in STD_LOGIC;
             en : in STD_LOGIC;
             clk : in STD_LOGIC;
             address : in STD_LOGIC_VECTOR(address_width - 1 downto 0);
             data : in STD_LOGIC_VECTOR(data_width - 1  downto 0);
             q : out STD_LOGIC_VECTOR(data_width - 1 downto 0)
             );
    end component;
    end package;
    
    library IEEE;
    use IEEE.std_logic_1164.all;   
    use IEEE.NUMERIC_STD.all;
    
    entity mem_inf_ram is
         generic (
           data_width : integer;
           data_length: integer; 
           address_width : integer
         );
         port(
             we : in STD_LOGIC;
             en : in STD_LOGIC;
             clk : in STD_LOGIC;
             address : in STD_LOGIC_VECTOR(address_width - 1 downto 0);
             data : in STD_LOGIC_VECTOR(data_width - 1  downto 0);
             q : out STD_LOGIC_VECTOR(data_width - 1 downto 0)
             );
    end mem_inf_ram;
    
    architecture mem_inf_ram of mem_inf_ram is   
    
        type ram_type is array (0 to data_length - 1) of std_logic_vector(data_width - 1 downto 0);   -- array must be 0 - n, if declared the other way wit downto to match Xilinx example then Quartus fitter fails
        signal ram : ram_type;
    
    begin
    
        process(clk)
        begin
            if rising_edge(clk) then
                if en = '1' then
                    if we = '1' then
                        ram(to_integer(unsigned((address)))) <= data;
                    end if;
                    q <= ram(to_integer(unsigned((address))));
                end if;
            end if;
        end process;
    
    end mem_inf_ram;
    
    

    It uses different indents, case, ... as my other posts. The style is even inconsistent inside the file itself.
    Let's see what the style utility does.

    I first let it critique my style:

    image

    Then I let it format my code:

    image

    Result:

    -- from Micheal Kellett: https://www.element14.com/community/groups/fpga-group/blog/2021/07/31/cheap-cyclone-10
    
    library IEEE;
      use IEEE.std_logic_1164.all;
      use IEEE.NUMERIC_STD.all;
    
    package mem_inf is
    
      component mem_inf_ram is
        generic (
          data_width    : integer := 12;
          data_length   : integer := 64;
          address_width : integer := 6
        );
        port (
          we      : in    std_logic;
          en      : in    std_logic;
          clk     : in    std_logic;
          address : in    std_logic_vector(address_width - 1 downto 0);
          data    : in    std_logic_vector(data_width - 1  downto 0);
          q       : out   std_logic_vector(data_width - 1 downto 0)
        );
      end component;
    
    end package mem_inf;
    
    library IEEE;
      use IEEE.std_logic_1164.all;
      use IEEE.NUMERIC_STD.all;
    
    entity mem_inf_ram is
      generic (
        data_width    : integer;
        data_length   : integer;
        address_width : integer
      );
      port (
        we      : in    std_logic;
        en      : in    std_logic;
        clk     : in    std_logic;
        address : in    std_logic_vector(address_width - 1 downto 0);
        data    : in    std_logic_vector(data_width - 1  downto 0);
        q       : out   std_logic_vector(data_width - 1 downto 0)
      );
    end entity mem_inf_ram;
    
    architecture mem_inf_ram of mem_inf_ram is
    
      type ram_type is array (0 to data_length - 1) of std_logic_vector(data_width - 1 downto 0); -- array must be 0 - n, if declared the other way wit downto to match Xilinx example then Quartus fitter fails
    
      signal ram : ram_type;
    
    begin
    
      process (clk) is
      begin
    
        if clk'event and clk = '1' then
          if (en = '1') then
            if (we = '1') then
              ram(to_integer(unsigned((address)))) <= data;
            end if;
            q <= ram(to_integer(unsigned((address))));
          end if;
        end if;
    
      end process;
    
    end architecture mem_inf_ram;
    

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube