2021-01-28

FPGA tutorial Step 3: Convert module to block design

When creating a vhdl-file and compiling it as with the previous post's example, the result consists of a single stand-alone vhdl file. To split logic into multiple files for "separation of concern" and reusability, things can be split up into modules. And Vivado provides a graphical interface for connecting these to each other. This post will show how to create a block design to achieve this.

When compiling and deploying any vivado project, one module is marked as the top level module. This is the module which is mapped to the pins specified in the constraints-file. When using a single vhdl-file, this is the top level module and the in and out parameters in the module must match its names (e.g. clk, sw and led) with the same names in the constrain-file.

When creating a block design, a world of possibilities will appear to you.

Create a new demo project

In the following project, I have created a new project and added two vhdl-files, named ArrayShifter.vhdl and ArrayReverser.vhdl:image-20210208215839632

Note that ArrayReverser is shown in bold because it has been chosen as the top-level module. This module cannot be deployed as its input and output parameter names do not match the ones in my constraint file.

ArrayReverser.vhdl:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity ArrayReverser is
    Port ( in_array : in STD_LOGIC_VECTOR (15 downto 0);
           out_array : out STD_LOGIC_VECTOR (0 to 15));
end ArrayReverser;

architecture Behavioral of ArrayReverser is

begin

    out_array <= in_array;

end Behavioral;

ArrayShifter.vhdl:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity ArrayShifter is
    Port ( in_array : in STD_LOGIC_VECTOR (15 downto 0);
           out_array : out STD_LOGIC_VECTOR (15 downto 0));
end ArrayShifter;

architecture Behavioral of ArrayShifter is

begin

    out_array(15) <=  in_array(14);
    out_array(14) <=  in_array(13);
    out_array(13) <=  in_array(12);
    out_array(12) <=  in_array(11);
    out_array(11) <=  in_array(10);
    out_array(10) <=  in_array(9);
    out_array(9) <=  in_array(8);
    out_array(8) <=  in_array(7);
    out_array(7) <=  in_array(6);
    out_array(6) <=  in_array(5);
    out_array(5) <=  in_array(4);
    out_array(4) <=  in_array(3);
    out_array(3) <=  in_array(2);
    out_array(2) <=  in_array(1);
    out_array(1) <=  in_array(0);
    out_array(0) <= '0';

end Behavioral;

Basys-3-master.xdc:

## This file is a general .xdc for the Basys3 rev B board
## To use it in a project:
## - uncomment the lines corresponding to used pins
## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project

## Clock signal
set_property PACKAGE_PIN W5 [get_ports clk]
	set_property IOSTANDARD LVCMOS33 [get_ports clk]
	create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports clk]

## Switches
set_property PACKAGE_PIN V17 [get_ports {sw[0]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[0]}]
set_property PACKAGE_PIN V16 [get_ports {sw[1]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[1]}]
set_property PACKAGE_PIN W16 [get_ports {sw[2]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[2]}]
set_property PACKAGE_PIN W17 [get_ports {sw[3]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[3]}]
set_property PACKAGE_PIN W15 [get_ports {sw[4]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[4]}]
set_property PACKAGE_PIN V15 [get_ports {sw[5]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[5]}]
set_property PACKAGE_PIN W14 [get_ports {sw[6]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[6]}]
set_property PACKAGE_PIN W13 [get_ports {sw[7]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[7]}]
set_property PACKAGE_PIN V2 [get_ports {sw[8]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[8]}]
set_property PACKAGE_PIN T3 [get_ports {sw[9]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[9]}]
set_property PACKAGE_PIN T2 [get_ports {sw[10]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[10]}]
set_property PACKAGE_PIN R3 [get_ports {sw[11]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[11]}]
set_property PACKAGE_PIN W2 [get_ports {sw[12]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[12]}]
set_property PACKAGE_PIN U1 [get_ports {sw[13]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[13]}]
set_property PACKAGE_PIN T1 [get_ports {sw[14]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[14]}]
set_property PACKAGE_PIN R2 [get_ports {sw[15]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[15]}]

## LEDs
set_property PACKAGE_PIN U16 [get_ports {led[0]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
set_property PACKAGE_PIN E19 [get_ports {led[1]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
set_property PACKAGE_PIN U19 [get_ports {led[2]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
set_property PACKAGE_PIN V19 [get_ports {led[3]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}]
set_property PACKAGE_PIN W18 [get_ports {led[4]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[4]}]
set_property PACKAGE_PIN U15 [get_ports {led[5]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[5]}]
set_property PACKAGE_PIN U14 [get_ports {led[6]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[6]}]
set_property PACKAGE_PIN V14 [get_ports {led[7]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[7]}]
set_property PACKAGE_PIN V13 [get_ports {led[8]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[8]}]
set_property PACKAGE_PIN V3 [get_ports {led[9]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[9]}]
set_property PACKAGE_PIN W3 [get_ports {led[10]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[10]}]
set_property PACKAGE_PIN U3 [get_ports {led[11]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[11]}]
set_property PACKAGE_PIN P3 [get_ports {led[12]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[12]}]
set_property PACKAGE_PIN N3 [get_ports {led[13]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[13]}]
set_property PACKAGE_PIN P1 [get_ports {led[14]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[14]}]
set_property PACKAGE_PIN L1 [get_ports {led[15]}]
	set_property IOSTANDARD LVCMOS33 [get_ports {led[15]}]

## Configuration options, can be used for all designs
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property CFGBVS VCCO [current_design]

Create a block design

And now to the interesting part; creating a block diagram. Start clicking "Create Block Design" and give it a name:

image-20210208220841497

image-20210208220916049

You will then see an empty design view like this:

image-20210208221034279

Start by switching back to the Sources-view:

image-20210208221105279

Now, we will create an auto-maintained wrapper module around the block design which will be kept updated by Vivado whenever we make changes to the soon-to-be-demonstrated block design UI.

This is done by right clicking the orange module and select "Create HDL Wrapper":

image-20210208221302741

and let Vivado keep it updated:

image-20210208221321080

A new module has now been created and notice that this is now marked as bold:

image-20210208221417132

If it is not marked as bold in your case, right-click it and select "Set as top". Now expand the module and double-clock the orange line:

image-20210208221538748

Build a module by using your own parts

Now, you right click the white area and choose "Create port...":

image-20210208221646449

This is done 3 times and the following settings are created:

image-20210209222636031

image-20210208221753019

image-20210208221815422

The names "clk", "led" and "sw" must match the names in the constraint-file. The result will be this:

image-20210208221859295

Now, drag the ArrayShiter onto the white area twice and the ArrayReverser onto the area once. Give vivado time between each adding to update the block design. The design now looks something like this:

image-20210208222051831

Now, drag the modules to make them have the following order and click and drag the mouse from the small black pins to connect them:

image-20210208222224494

(We will not use the clk-input in this demo).

After saving the project and letting Vivado update the sources-view, you will see the two instances of ArrayShifter module and the ArrayReverser modules:

image-20210208222546194

Now, go run synthesize, run implementation, generate bitstream and deploy the result to your basys3.