Documentation on using the MicroZed FMC Carrier Card with a MicroZed FPGA developement board.
This guide starts with an existing MicroZed Vivado project (see PetaLinux on MicroZed) and modifies it to add support for controlling the FMC Carrier Card LEDs (LED1, LED2, LED3 and LED4) using an AXI register. A simple logic high on the right pin will enable the correct LED. See section 2.3.2 of the MicroZed FMC Carrier Card Hardware Guide on how this works.
This guide is written in two parts:
The instructions should be largely independent of the version of the Xilinx tools used. For reference, version 2017.4 was used to write this tutorial.
Also it is asseumd one is familiar with creating Vivado and PetaLinux projects for the MicroZed. If not, our PetaLinux on MicroZed guide is a good starting point.
Start by creating a custom IP Core with suppport for AXI slave. You can use the same settings as for axihelloworld, but name it fmc_led_controller. Add it to the TopLevel block design and run the Run Connection Automation tool to connect everything.
The next step is to modify the VHDL code of the fmc_led_controller IP Core to assign the values of the AXI registers to outputs. Right click on the fmc_led_controller_0 block and select the Edit in IP Packager option. Confirm the default values in the subsequent dialog. This will start a new Vivado session containing a temporary project of the fmc_led_controller IP Core.
Open the fmc_led_controller_v1_0.vhd file in the Vivado (or your favourite) text editor. This is the top-level entity of the IP Core and corresponds to the block that you have added to the main firmware TopLevel block design.
Add the following lines right below the =-- Users to add ports here= to define the output ports corresponding to the LED levels.
-- Users to add ports here
led0 : out std_logic;
led1 : out std_logic;
led2 : out std_logic;
led3 : out std_logic;
-- User ports ends
The next step will be to connect the output ports to one of the AXI registers. The AXI registers are stored in the fmc_led_controller_v1_0_S00_AXI entity, which annoyingly does not break any of them out. It will have to be modified to do so later in the guide. For now, pretend that it is modified and connect the registers to the output ports added above.
First add new output ports to the fmc_led_controller_v1_0_S00_AXI components declaration that will be internally connected to the AXI registers. This is accomplished by the following lines added right after the =port(= start of the fmc_led_controller_v1_0_S00_AXI component declaration.
led0 : out std_logic;
led1 : out std_logic;
led2 : out std_logic;
led3 : out std_logic;
Then connect the fmc_led_controller_v1_0_S00_AXI output ports to the fmc_led_controller_v1_0 output ports. Find the fmc_led_controller_v1_0_S00_AXI_inst entity instance and right after =port map (= add the following lines
led0 => led0,
led1 => led1,
led2 => led2,
led3 => led3,
Next open the fmc_led_controller_v1_0_S00_AXI.vhd file. This is where we connect some of the bits of the first AXI register to the =ledX= output ports.
Start by adding the new output ports below the =-- Users to add ports here= line.
-- Users to add ports here
led0 : out std_logic;
led1 : out std_logic;
led2 : out std_logic;
led3 : out std_logic;
-- User ports ends
Scroll to the end of the file, where you will find a block for custom logic. It begins with = -- Add user logic here=. In there, map the four LSB bits of the first AXI register to the =ledX= output ports. The AXI register number =X= is represented by the =slv_regX= signal.
-- Add user logic here
led0 <= slv_reg0(0);
led1 <= slv_reg0(1);
led2 <= slv_reg0(2);
led3 <= slv_reg0(3);
-- User logic ends
This ends all of the VHDL changes that have to be made to the fmc_led_controller IP Core. You can find the modified fmc_led_controller_v1_0.vhd and fmc_led_controller_v1_0_S00_AXI.vhd attached.
Before closing the IP Core edit project, make sure to update its meta-data with the new output ports. Use the Package IP window to do so. You can find the list of defined output ports for the top-level block under the Ports and Interfaces tab. Use the Merge Changes from the Ports and Interfaces Wizard tool to automatically update the meta-data with your additions.
After it finishes, the list should look as follows.
Save and close the fmc_led_controller IP Core edit project and return to the main microzed_fmc_demo project. You can import the changes to the custom IP Core by using the Refersh IP Catalog suggestion that appears on top anytime you modify the source code of an IP Core.
The IP Status window automatically selects all IP Cores that can be upgraded (aka modifications imported). Use the Upgrade Selected button to confirm and apply the changes.
You will be greeted with a few dialogs asking to regenerate Vivado managed blocks. Confirm them.
You should see the =ledX= output ports added in the previous section in the =fmc_led_controller_0= instance in the =TopLevel= block diagram. They can be directly connected to the output pins corresponding to the LEDs. Right click on each and use the Make External option.
By default, the external output ports will be called =ledX_0=, which is a bit ugly. Click on each and use the External Port Properties box to rename them to LEDX. At the end, the =TopLevel= block diagram should look as follows. You can use the Optimize Routing button to make it look a bit nicer.
The last step is to define FPGA pins used by the external ports. This can be done by importing the Programmable Logic Master User Constraints template found on their resources page. This tutorial uses the XDC version: Z7010 or Z7020 MicroZed with MBCC-FMC-PCB-B_v2.xdc.
Import it using the Add Sources wizard found under the PROJECT MANAGER section of the left-hand side toolbar. Select Add or create constraints in the first page of the wizard and click Next to continue.
Add the Z7010 or Z7020 MicroZed with MBCC-FMC-PCB-B_v2.xdc file using the Add Files button. Also use the Create File button to make a new file called pins.xcf. We will insert our custom definitions there. Make sure that the Copy constraints files into project is selected. Click Finish to close the dialog.
The names of the LED pins in the constraints file are already called =LEDX=. If you used a different name in the block diagram design, you will have to edit the XDC file. Look for lines that look like:
# ----------------------------------------------------------------------------
# User LEDs - Bank 34
# ----------------------------------------------------------------------------
set_property PACKAGE_PIN R19 [get_ports {LED0}]; # "R19.JX1_SE_0.JX1.9.LED0"
set_property PACKAGE_PIN V13 [get_ports {LED1}]; # "V13.JX1_LVDS_2_N.JX1.19.LED1"
# ----------------------------------------------------------------------------
# User LEDs - Bank 35
# ----------------------------------------------------------------------------
set_property PACKAGE_PIN K16 [get_ports {LED2}]; # "K16.JX2_LVDS_23_P.JX2.88.LED2"
set_property PACKAGE_PIN M15 [get_ports {LED3}]; # "M15.JX2_LVDS_22_N.JX2.89.LED3"
The MicroZed XDC file only contains the pin mappings. The drive type of the pin needs to be specified by the user. Add the following lines to the pins.xdc file to set the =IOSTANDARD= to =LVCMOS18=.
set_property IOSTANDARD LVCMOS18 [get_ports {LED0}]
set_property IOSTANDARD LVCMOS18 [get_ports {LED1}]
set_property IOSTANDARD LVCMOS18 [get_ports {LED2}]
set_property IOSTANDARD LVCMOS18 [get_ports {LED3}]
This completes the firmware part of the project. A PetaLinux project (or some other way to control the AXI registers) needs to be created to actually do something. Refer to our PetaLinux on MicroZed guide on how to do this.
This section assumes that you have prepared and packaged a PetaLinux project with the =peekpoke= application and the correct Device Tree. The same settings as described in our PetaLinux on MicroZed guide can be used.
Use the =poke= commant to set register =0x43C00000= to a number corresponding to a LED pattern. For example, writing the value 5
root@microzed_fmc_demo:~# poke 0x43C00000 0x5
root@microzed_fmc_demo:~# peek 0x43C00000
0x00000005
yields the following result.