cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Highlighted
Visitor
Visitor
516 Views
Registered: ‎07-11-2018

Making sense of the VHDL structure in Vivado

Jump to solution

Hi all! Ok so I have been developing micro controller solutions for quite some time and decided that I would like to know more about FPGA's so I got an Arty A7 board and started doing some work. Now most of the learning curve was steep but manageable on my own and internet but recently I have came to a wall and just can't figure out what am I doing wrong for something I thought should be obvious and a bit more straightforward. I know how much documentation there is and there is probably something about this but I just can't find it after a lot of time spent searching.

Now, the problem I have is I can't understand the structure of the project (and the terminology) when I want to have different subdivisions of the project in different files. So the divisions are completely different parts of the logic that don't interact with each other so I thought it was natural that such sections can be in different folders and just somehow referenced to the compiler/Vivado, just like you would write some code in C and then in the header just reference to the file with #include xxfile.c command. 

What I have here is the test code that does work but it is all crammed in the same top level file but I wanted it to be split in two files. First one would be the processes with PWM on the top and the other file would be the buttons and LED's. But every time the second file would be "Unreferenced" and the compiler would just compile the first one and ignore the second file no matter what I do. 
Can anyone show me an example of how is it done or point me to a valuable tutorial or whatever? I am sure there is a nice way of doing this, I just can't figure it out..

Here is the code:

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

entity main_entity is Port
(PWM_OUT1 : out STD_LOGIC;
PWM_OUT2 : out STD_LOGIC;
system_clock : in STD_LOGIC;
sw1 : in STD_LOGIC;
sw2 : in STD_LOGIC;
sw3 : in STD_LOGIC;
sw4 : in STD_LOGIC;
btn1 : in STD_LOGIC;
btn2 : in STD_LOGIC;
btn3 : in STD_LOGIC;
btn4 : in STD_LOGIC;
led1 : out STD_LOGIC;
led2 : out STD_LOGIC;
led3 : out STD_LOGIC;
led4 : out STD_LOGIC;
rled1 : out STD_LOGIC;
gled1 : out STD_LOGIC;
bled1 : out STD_LOGIC;
rled2 : out STD_LOGIC;
gled2 : out STD_LOGIC;
bled2 : out STD_LOGIC;
rled3 : out STD_LOGIC;
gled3 : out STD_LOGIC;
bled3 : out STD_LOGIC;
rled4 : out STD_LOGIC;
gled4 : out STD_LOGIC;
bled4 : out STD_LOGIC);
end main_entity;

architecture PWM_CLOCKING of main_entity is

signal counter1 : integer;
signal counter2 : integer range 0 to 20000;
signal counter3 : integer;
signal counter4 : integer range 0 to 20000;
signal pwm_value1 : integer;
signal pwm_value2 : integer;
signal clock1 : STD_LOGIC;
signal clock2 : STD_LOGIC;

component clk_wiz_0
port
(-- Clock in ports
-- Clock out ports
clk_out1 : out std_logic;
clk_out2 : out std_logic;
clk_in1 : in std_logic
);
end component;

begin

custom_clocking_solution : clk_wiz_0
port map (
-- Clock out ports
clk_out1 => clock1,
clk_out2 => clock2,
-- Clock in ports
clk_in1 => system_clock
);


process (clock1)
begin
if rising_edge(clock1) then
counter1 <= counter1 + 1;
if counter1 < pwm_value1 then
PWM_OUT1 <= '1';
else
PWM_OUT1 <= '0';
end if;
if counter1 = 256 then
counter1 <= 0;
counter2 <= counter2 + 1;
if counter2 = 5000 then
pwm_value1 <= pwm_value1 + 1;
if pwm_value1 = 256 then
pwm_value1 <= 0;
end if;
counter2 <= 0;
end if;
end if;
end if;
end process;

process (clock2)
begin
if rising_edge(clock2) then
counter3 <= counter3 + 1;
if counter3 < pwm_value2 then
PWM_OUT2 <= '1';
else
PWM_OUT2 <= '0';
end if;
if counter3 = 256 then
counter3 <= 0;
counter4 <= counter4 + 1;
if counter4 = 5000 then
pwm_value2 <= pwm_value2 + 1;
if pwm_value2 = 256 then
pwm_value2 <= 0;
end if;
counter4 <= 0;
end if;
end if;
end if;
end process;

led1 <= btn1;
led2 <= btn2;
led3 <= btn3;
led4 <= btn4;

bled1 <= sw1;
bled2 <= sw1;
bled3 <= sw1;
bled4 <= sw1 and sw4;

rled1 <= sw2;
rled2 <= sw2;
rled3 <= sw2;
rled4 <= sw2 and sw4;

gled1 <= sw3;
gled2 <= sw3;
gled3 <= sw3;
gled4 <= sw3 and sw4;

end PWM_CLOCKING;

Thank you all in advance!

0 Kudos
1 Solution

Accepted Solutions
Highlighted
343 Views
Registered: ‎01-22-2015

@marijan 

Learning FPGAs and VHDL by yourself is a very steep hill to climb.  I don’t recommend doing it.  However, it is the way I learned and therefore I can remember and feel your pain. 

If you are not going to take classes (which is highly recommended) then you must find a good book.  I'm sorry that my previous reading suggestions did not help.  However, I have one more reading suggestion.  Please go to my 02-15-2018 post in the following thread and download the attachment called FPGAs_VHDL_First_Steps_v2p1.pdf.   It is a free book that I think you will find helpful.

https://forums.xilinx.com/t5/General-Technical-Discussion/Recommended-books-for-FPGAs/m-p/831155

 

Meanwhile, here is some VHDL to get you going.

  1. Here is a simple VHDL component. In Vivado, you can create a new source file called TWO_BIT.vhd, copy and paste the VHDL below into the blank source file, and save the source file.
    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL; 
    
    entity TWO_BIT is
      port( 
            CLK     : in std_logic;           
            IN1     : in std_logic;  	
            IN2     : in std_logic;  	
            OUT1    : out std_logic;            	
    );
    end TWO_BIT;
    
    architecture BEHAVIOR of TWO_BIT is
        signal bOUT : std_logic;        	       	
    begin
        OUT1 <= bOUT;
        
        PRC1: process(CLK)      	
            begin    
                if rising_edge(CLK) then
                    bOUT <= IN1 or IN2
    end if; end process PRC1; end BEHAVIOR;
  2. Here is another simple VHDL component, which will be our top-level component. In Vivado, you can create a new source file called TOP.vhd, copy and paste the VHDL below into the blank source file, and save the source file.  Note how TWO_BIT.vhd is made available to TOP.vhd by “declaring” TWO_BIT.vhd.  Note how TWO_BIT.vhd is used by TOP.vhd by “instantiating” TWO_BIT.vhd. 
    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL; 
    
    entity TOP is
      port( 
            CLK_IN     : in std_logic;           
            INP1       : in std_logic;  	
            INP2       : in std_logic;  	
            LED_CTRL   : out std_logic;            	
    );
    end TOP;
    
    architecture BEHAVIOR of TOP is
        
        --Declaration for component, TWO_BIT
        component TWO_BIT is	    
            port(  
            CLK     : in std_logic;           
            IN1     : in std_logic;  	
            IN2     : in std_logic;  	
            OUT1    : out std_logic;                        );	 
        end component;
    	       	
    begin
    
        --Instantiation for component, TWO_BIT
        tbt: TWO_BIT 
    	   port map(
    	        CLK     => CLK_IN,
                    IN1     => INP1,
                    IN2     => INP2,
                    OUT1    => LED_CTRL
    	           );	           
    end BEHAVIOR;

     

Good luck,
Mark

View solution in original post

5 Replies
Highlighted
488 Views
Registered: ‎01-22-2015

@marijan 

Welcome!

In VHDL, we make separate components and store the VHDL for each component in a separate Vivado source file.  See UG888 about using Vivado to make and store separate source files.

https://www.xilinx.com/support/documentation/sw_manuals/xilinx2019_2/ug888-vivado-design-flows-overview-tutorial.pdf#page29

 

Your design currently has only one VHDL component and it is called the top level component because its ports connect directly to pins of the FPGA.  Other VHDL components that you create will have a format similar to your current top level component, but ports of these other components cannot connect directly to pins of the FPGA.  Instead, ports of these other components will connect to signals in the top level component.

You can use a VHDL component inside another by declaring and instantiating a VHDL component inside another.  See your VHDL book for details of doing this.  If you don’t have a VHDL book then the following book is good and free.

http://freerangefactory.org/pdf/df344hdh4h8kjfh3500ft2/free_range_vhdl.pdf

Mark

0 Kudos
Highlighted
Visitor
Visitor
359 Views
Registered: ‎07-11-2018

Hi, thank you for the quick response. 
Unfortunately I don't see any useful information in either links you have sent me and I spent last couple of days going trough them. UG888 mentions Port Map and Component 0 times and only once Module. I had a look trough it but too many times have I read in depth those documents just to realise there is nothing useful in them (incredibly frustrating). The book kind of just mentions those things but I think it does not solve my problem at all or I am incredibly stupid. 

Can someone please post a really simple example which includes 3 different files where one is the top level file and 2 are some functions/modules/components that reference to the top one?

I can't believe I am the only one asking for this. I have spent months searching the net for an example or an explanation just to realise the closest to a concise explanation/example is the clocking solution in my own code that I have generated using a tutorial from ''some guy'', not Xilinx or any of the "official" resources which quite frankly are next to useless in my opinion. But because the clocking IP is quite complicated, it is still not 100% clear what is reference to what.

I beg for the simplest example.
Thank you!

0 Kudos
Highlighted
344 Views
Registered: ‎01-22-2015

@marijan 

Learning FPGAs and VHDL by yourself is a very steep hill to climb.  I don’t recommend doing it.  However, it is the way I learned and therefore I can remember and feel your pain. 

If you are not going to take classes (which is highly recommended) then you must find a good book.  I'm sorry that my previous reading suggestions did not help.  However, I have one more reading suggestion.  Please go to my 02-15-2018 post in the following thread and download the attachment called FPGAs_VHDL_First_Steps_v2p1.pdf.   It is a free book that I think you will find helpful.

https://forums.xilinx.com/t5/General-Technical-Discussion/Recommended-books-for-FPGAs/m-p/831155

 

Meanwhile, here is some VHDL to get you going.

  1. Here is a simple VHDL component. In Vivado, you can create a new source file called TWO_BIT.vhd, copy and paste the VHDL below into the blank source file, and save the source file.
    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL; 
    
    entity TWO_BIT is
      port( 
            CLK     : in std_logic;           
            IN1     : in std_logic;  	
            IN2     : in std_logic;  	
            OUT1    : out std_logic;            	
    );
    end TWO_BIT;
    
    architecture BEHAVIOR of TWO_BIT is
        signal bOUT : std_logic;        	       	
    begin
        OUT1 <= bOUT;
        
        PRC1: process(CLK)      	
            begin    
                if rising_edge(CLK) then
                    bOUT <= IN1 or IN2
    end if; end process PRC1; end BEHAVIOR;
  2. Here is another simple VHDL component, which will be our top-level component. In Vivado, you can create a new source file called TOP.vhd, copy and paste the VHDL below into the blank source file, and save the source file.  Note how TWO_BIT.vhd is made available to TOP.vhd by “declaring” TWO_BIT.vhd.  Note how TWO_BIT.vhd is used by TOP.vhd by “instantiating” TWO_BIT.vhd. 
    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL; 
    
    entity TOP is
      port( 
            CLK_IN     : in std_logic;           
            INP1       : in std_logic;  	
            INP2       : in std_logic;  	
            LED_CTRL   : out std_logic;            	
    );
    end TOP;
    
    architecture BEHAVIOR of TOP is
        
        --Declaration for component, TWO_BIT
        component TWO_BIT is	    
            port(  
            CLK     : in std_logic;           
            IN1     : in std_logic;  	
            IN2     : in std_logic;  	
            OUT1    : out std_logic;                        );	 
        end component;
    	       	
    begin
    
        --Instantiation for component, TWO_BIT
        tbt: TWO_BIT 
    	   port map(
    	        CLK     => CLK_IN,
                    IN1     => INP1,
                    IN2     => INP2,
                    OUT1    => LED_CTRL
    	           );	           
    end BEHAVIOR;

     

Good luck,
Mark

View solution in original post

Highlighted
Visitor
Visitor
329 Views
Registered: ‎07-11-2018

Hi Mark and thank you very much.

This did helped me to crack the situation.

For anyone asking the same question I will include my code in a way I have thought of doing it. Please advise me if this is not the best approach but the code does compile and work and in the Implementation schematic looks exactly as I wanted it to look.

So this is the top level (main) code:

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

--Define the main ports (pins). Only the ports here, in the top level document, can be connected to the actual pins of the device.
--Don't forget to define the constraints.xdc file in relation to this.
entity main_entity is Port
        (PWM_OUT1 : out STD_LOGIC;
        PWM_OUT2 : out STD_LOGIC;
        system_clock : in STD_LOGIC;
        sw1 : in STD_LOGIC;
        sw2 : in STD_LOGIC;
        sw3 : in STD_LOGIC;
        sw4 : in STD_LOGIC;
        btn1 : in STD_LOGIC;
        btn2 : in STD_LOGIC;
        btn3 : in STD_LOGIC;
        btn4 : in STD_LOGIC;
        led1 : out STD_LOGIC;
        led2 : out STD_LOGIC;
        led3 : out STD_LOGIC;
        led4 : out STD_LOGIC;
        rled1 : out STD_LOGIC;
        gled1 : out STD_LOGIC;
        bled1 : out STD_LOGIC;
        rled2 : out STD_LOGIC;
        gled2 : out STD_LOGIC;
        bled2 : out STD_LOGIC;
        rled3 : out STD_LOGIC;
        gled3 : out STD_LOGIC;
        bled3 : out STD_LOGIC;
        rled4 : out STD_LOGIC;
        gled4 : out STD_LOGIC;
        bled4 : out STD_LOGIC); 
end main_entity;

--Declare the start of the architecture.. This seems to be necessary
architecture main_architecture of main_entity is

--Declare the components defined in other files. This is like referencing. It is a copy/paste of the entity declaration from the other file.
--First component 
component PWM is Port
        (PWM_OUT1 : out STD_LOGIC;
        PWM_OUT2 : out STD_LOGIC;
        system_clock : in STD_LOGIC);
end component;

--Second component
component LED_and_button_handler is
Port ( sw1 : in STD_LOGIC;
           sw2 : in STD_LOGIC;
           sw3 : in STD_LOGIC;
           sw4 : in STD_LOGIC;
           btn1 : in STD_LOGIC;
           btn2 : in STD_LOGIC;
           btn3 : in STD_LOGIC;
           btn4 : in STD_LOGIC;
           led1 : out STD_LOGIC;
           led2 : out STD_LOGIC;
           led3 : out STD_LOGIC;
           led4 : out STD_LOGIC;
           rled1 : out STD_LOGIC;
           gled1 : out STD_LOGIC;
           bled1 : out STD_LOGIC;
           rled2 : out STD_LOGIC;
           gled2 : out STD_LOGIC;
           bled2 : out STD_LOGIC;
           rled3 : out STD_LOGIC;
           gled3 : out STD_LOGIC;
           bled3 : out STD_LOGIC;
           rled4 : out STD_LOGIC;
           gled4 : out STD_LOGIC;
           bled4 : out STD_LOGIC);
end component;

--Start the high level (main) bit of the software. Seems necessary
begin

--Declare the port maps  (links the component ports to main code or "main_entity" entity ports).. I think
--I am using the same names because the ports are named the same in the main file as in the referenced files
--Component 1 port map
custom_PWM : PWM
   port map ( 
   PWM_OUT1 => PWM_OUT1,
   PWM_OUT2 => PWM_OUT2,
   system_clock => system_clock
 );
 --Component 2 port map
 custom_LED_and_button_handler : LED_and_button_handler
    port map ( 
    sw1 => sw1,
    sw2 => sw2,
    sw3 => sw3,
    sw4 => sw4,
    btn1 => btn1,
    btn2 => btn2,
    btn3 => btn3,
    btn4 => btn4,
    led1 => led1,
    led2 => led2,
    led3 => led3,
    led4 => led4,
    rled1 => rled1,
    bled1 => bled1,
    gled1 => gled1,
    rled2 => rled2,
    bled2 => bled2,
    gled2 => gled2,
    rled3 => rled3,
    bled3 => bled3,
    gled3 => gled3,
    rled4 => rled4,
    bled4 => bled4,
    gled4 => gled4
 );

--If there is any code to be "executed" as part of the "main_architecture", it should be here

--Nothing in this example so end the "main_architecture"
end main_architecture;

Next bit is the first separate file:

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

--Name the ports used in the code below. Names are applicable only in this file. 
--To use them outside the file you need to declare the component (this is a component as a file) and define the Port map function.
--Port map links the names given here with the names of the ports in the main file.
entity LED_and_button_handler is
    Port ( sw1 : in STD_LOGIC;
           sw2 : in STD_LOGIC;
           sw3 : in STD_LOGIC;
           sw4 : in STD_LOGIC;
           btn1 : in STD_LOGIC;
           btn2 : in STD_LOGIC;
           btn3 : in STD_LOGIC;
           btn4 : in STD_LOGIC;
           led1 : out STD_LOGIC;
           led2 : out STD_LOGIC;
           led3 : out STD_LOGIC;
           led4 : out STD_LOGIC;
           rled1 : out STD_LOGIC;
           gled1 : out STD_LOGIC;
           bled1 : out STD_LOGIC;
           rled2 : out STD_LOGIC;
           gled2 : out STD_LOGIC;
           bled2 : out STD_LOGIC;
           rled3 : out STD_LOGIC;
           gled3 : out STD_LOGIC;
           bled3 : out STD_LOGIC;
           rled4 : out STD_LOGIC;
           gled4 : out STD_LOGIC;
           bled4 : out STD_LOGIC);
end LED_and_button_handler;

--Start the code definition - architecture.
architecture LED_and_button_architecture of LED_and_button_handler is

begin
    
    led1 <= btn1;
    led2 <= btn2;
    led3 <= btn3;
    led4 <= btn4;
    
    bled1 <= sw1;
    bled2 <= sw1;
    bled3 <= sw1;
    bled4 <= sw1 and sw4;
    
    rled1 <= sw2;
    rled2 <= sw2;
    rled3 <= sw2;
    rled4 <= sw2 and sw4;
    
    gled1 <= sw3;
    gled2 <= sw3;
    gled3 <= sw3;
    gled4 <= sw3 and sw4;
    
end LED_and_button_architecture;

And last is the third file that contains configurable PWM code:

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

--Name the ports used in the code below. Names are applicable only in this file. 
--To use them outside the file you need to declare the component (this is a component as a file) and define the Port map function.
--Port map links the names given here with the names of the ports in the main file.
entity PWM is Port
        (PWM_OUT1 : out STD_LOGIC;
        PWM_OUT2 : out STD_LOGIC;
        system_clock : in STD_LOGIC);

end PWM;


architecture PWM_CLOCKING of PWM is

signal counter1 : integer;
signal counter2 : integer range 0 to 20000;
signal counter3 : integer;
signal counter4 : integer range 0 to 20000;
signal pwm_value1 : integer;
signal pwm_value2 : integer;
signal clock1 : STD_LOGIC;
signal clock2 : STD_LOGIC;

--This is an autogenerated clocking IP made using a clocking wizard.
--It follows the same method used in the main file to refernece this file as the clocking ports are referenced here from the autogenerated clocking file.
--Declare the component and later on declare the Port map so that the names are refferenced correctly.
component clk_wiz_0
port
 (-- Clock in ports
  -- Clock out ports
  clk_out1          : out    std_logic;
  clk_out2          : out    std_logic;
  clk_in1           : in     std_logic
 );
end component;

begin

custom_clocking_solution : clk_wiz_0
   port map ( 
  -- Clock out ports  
   clk_out1 => clock1,
   clk_out2 => clock2,
   -- Clock in ports
   clk_in1 => system_clock
 );
 
   -- pwm_value1 <= 100;
    
process (clock1)
    begin 
        if rising_edge(clock1) then
            counter1 <= counter1 + 1;
            if counter1 < pwm_value1 then
                PWM_OUT1 <= '1';
                    else 
                PWM_OUT1 <= '0';
            end if;
            if counter1 = 256 then
                counter1 <= 0;
                counter2 <= counter2 + 1;
                if counter2 = 5000 then
                    pwm_value1 <= pwm_value1 + 1;
                    if pwm_value1 = 256 then
                        pwm_value1 <= 0;
                    end if;
                    counter2 <= 0;
                end if;
            end if;
        end if;
    end process;
    
process (clock2)
    begin 
        if rising_edge(clock2) then
            counter3 <= counter3 + 1;
            if counter3 < pwm_value2 then
                PWM_OUT2 <= '1';
                    else 
                PWM_OUT2 <= '0';
            end if;
            if counter3 = 256 then
                counter3 <= 0;
                counter4 <= counter4 + 1;
                if counter4 = 5000 then
                 pwm_value2 <= pwm_value2 + 1;
                    if pwm_value2 = 256 then
                        pwm_value2 <= 0;
                    end if;
                    counter4 <= 0;
                end if;
            end if;
        end if;
    end process;

end PWM_CLOCKING;

Thanks Mark for your help and I hope this code helps someone else also.

Kind regards

Highlighted
319 Views
Registered: ‎01-22-2015

@marijan 

Good job! -and thanks for showing your code to help others.

-a minor comment: 
We usually declare and instantiate the Clocking Wizard component in the top-level VHDL module.  Then, we pass the clock output of the Clocking Wizard component into all the other components in the design.  For now, stick with using a single clock input to the FPGA and a single clock output of the Clocking Wizard.  Multi-clock designs is advanced stuff.

Soon, you will start saying "Why isn't my VHDL working?".  Often this question can be answered using Vivado simulation.  So, next up, I suggest you learn about simulation.  The official Xilinx document is UG900 (sorry, yet another UG).  The book I referenced has examples of doing simulation with VHDL.  Basically, you write another VHDL component called a "test bench", in which you declare/instantiate the VHDL component that you want to test.  Test bench VHDL components are written differently than VHDL components that you want to synthesis - but you'll get the hang of it.

I look forward to seeing more questions from you on the Forum.

Mark

 

0 Kudos