1. Introduction to the hardware and development environments

Post date: Sep 23, 2013 3:47:54 AM

This post corresponds to part one of the "Bare Metal: Interacting with ZC702 Evaluation Kit's Pins" subproject, wherein I will describe the steps I took to initially configure, build a hardware description for, write software for, and finally program the ZC702 Evaluation Board. As the name of the subproject's title suggests, my goal was to simply program the ZC702 such that I could interact with the ZC702's push buttons and cause the ZC702's LEDs to turn either on or off. The goal also includes learning everything I need to know about the hardware, i.e. the ZC702 and its ZynqTM EPP, and the development environments, i.e. Vivado© Design Suite and the Software Development Kit, in order to development a runnable project from scratch. Please note most if not all the content presented in this post can be found in Xilinx's many tutorial videos, user manuals, and even through the development environments themselves. This post and subsequent posts similar to this tie together information important to what I am trying to achieve and also point out many of the issues I managed to avoid. I will, though, try my best to include links to sources where I got my information. This post is organized into three sections, the first of which deals with the ZC702 (i.e. the hardware), and the last two sections deals with the Vivado Design Suite and SDK (i.e. the development environments).

The ZC702 Evaluation Board. The ZC702 is an evaluation board that acts as a platform for developing hardware descriptions and software for the Zynq EPP, specifically the Z-7020. The ZC702 is comprised of many components and peripheral interfaces common in many embedded applications, such as DDR3 SDRAM for main memory, general purpose input/outputs (GPIO), and universal asynchronous receiver and transmitters (UART), LEDs, and pushbuttons. The ZC702's features can even be extended by peripheral module (PMOD) connectors. Please note most of the information regarding the ZC702 itself I took from the UG850 User Guide.

Figure 1. The figure above displays the ZC702 and highlights the components pertinent to this post.

The ZC702's components pertinent to this post are described as follows. Please note each description's number corresponds to figure 1.

    1. Zynq EPP (i.e. Z-7020). The Z-7020 is the System-on-Chip device that combines the processing capabilities of a Dual-core ARM Cortex-A9 processor with programmable logic of an Artix-7 Field Programmable Gate Array (FPGA). The Zynq EPP is what executes the program being described in this post.

    2. Slide Switches. The two sets of slide switches highlighted in figure 1 are set in order to enable JTAG mode and utilize the JTAG Module, which is necessary for programming the ZC702. If the ZC702 has never been used, the slide switches should already be in their default positions. However, if the steps relevant to programming the ZC702 fail, please refer to figure 2 for pictures of the slide switches in positions necessary for programming with the JTAG Module.

    3. Digilent's JTAG Configuration Bridge Module w/USB. The JTAG Module is one of the available options for downloading programs, probing, and debugging certain hardware on the ZC702, including the Zynq EPP. There are other options available, but the JTAG approach was the most convenient for me since it only required a "Standard-A to Micro-B USB" cable to connect from the JTAG Module to the computer.

    4. 12V 5A power Input 2x6 Connector and Power Switch. Obviously necessary to power ZC702.

    5. Silicon Labs' USB-to-UART Bridge Device. The USB-to-UART Bridge allows a host computer to connect to the ZC702's UART with a "Standard-A to Mini-B USB" cable. If the Silicon Labs Virtual COM Port (VCP) drivers are also installed on the host computer, the USB-to-UART Bridge will appears as a COM port and thereby enable asynchronous serial communication with the ZC702. A user can then communicate with the ZC702, using software such as HyperTerm or PuTTy.

    6. LEDs and pushbutton. The project explained in this post will only utilize the LEDs and a single pushbutton. Specifically, the LEDs labeled DS15, DS16, DS17, and DS18 directly on the ZC702 (i.e. the first four LEDs, starting from the left) and the push button labeled SW5 (i.e. the left push button) are the user interfaces utilized in this post's project.

    7. PMOD Connector. The project will also incorporate the PMOD Module seen in figure 3.

Figure 2. These slide switches must be set to the positions shown in this figure in order to program the ZC702 with the JTAG Module

Figure 3. PMOD Module with 4 LEDs

The Vivado© Design Suite. Vivado is essentially a comprehensive set of tools for configuring Xilinx's 7 series -- which includes the Zynq EPP -- and other SoC devices. The set of tools includes behavioral synthesis tools for converting hardware descriptions or register-transfer level (RTL) designs down to bitstreams and high-level synthesis (HLS) tools for converting source code written in C, C++, or SystemC down to bitstreams, as well. Bitstreams are the binaries files that can be executed on FPGAs. Vivado is built to incorporate a design model that enables an embedded designer to modify his/her design within a series a stages before the final bitstream is generated. These stages include synthesis, implementation, power analysis, and the generation of the bitstream. Vivado also consists of tools for the management and development of Intellectual Property (IP) blocks, simulation tools, and much more that I am still in the process of learning. Designs are developed in one of two modes supported by Vivado, that is, non-project and project modes. Non-project mode runs primarily within a command line interpreter (CLI) that executes either Tools Command Language (Tcl -- pronounced "tickle") commands or batch files containing Tcl commands. Within non-project mode, the entire process of development, synthesis, implementation, and configuration of hardware is primarily done in main memory and not from a project saved on disk. As a result, synthesis and similar tools are supposed to execute relatively faster compared to executing in project mode. Project mode, however, is the more conventional approach and relies heavily on a graphical user interface (GUI) known as the Vivado Integrated Development Environment (IDE). In the context of this post, the tools of importance are the steps from Vivado's design model, IP Integrator, documentation/tutorials, and the function that exports the block design and bitstream to the SDK. Please note some of the information on Vivado presented in this post I gained from simply using Vivado, but the majority of the information I am paraphrasing from sources provided by Xilinx. These sources include the manuals UG910, UG904, UG908; and videos "Vivado Design Methodology", "Vivado Design Flows Overview", "Design with Vivado IP Integrator", "Targeting Zynq Using Vivado IP Integrator", and probably many more videos found on Xilinx's Vivado Video Tutorials.

Figure 4. Opening screen to Vivado's Integrated Development Environment (IDE), i.e. the GUI portion of Vivado

After installation, the Vivado IDE, can be opened either by its icon or by executing the Tcl command "start_gui" in the "Vivado 2013.2 Tcl Shell" CLI. As shown in figure 4, the IDE's starting screen provides the option of creating either a new project or opening a saved project. The project described in this post is largely based on the "Zynq System" project found under "Open Example Project". Select "Create New Project" to create a new project and open up the "New Project" window in which the new project's name, location on disk, and type are configured. Vivado offers 4 available project types, but the one corresponding to this post is the "RTL Project". I chose the option to not specify any sources since I plan on creating all relevant sources within the IDE. The selection of the evaluation board is done in the "New Project" window's next page. Finally, a "New Project Summary" is presented and the project is created.

Figure 5. Project type and board selection

Once the project's environment opens up, the configuration of the Zynq EPP is begun by selecting the "Create Block Design" option under the "IP Integrator" in the "Flow Navigator" tab (see figure 6.1). Once the block design has been named, the "Block Design" and Diagram windows should appear. The "ZYNQ7 Processing System" IP block is added by selecting the "Add IP" (see figure 6.2) and by either typing in or selecting the name of the IP block (see figure 6.4). During certain instances, towards the top of the Diagram window appears the "Design Assistance" where helpful hints to the design are provided (see figure 6.3). The configuration of the Zynq EPP's Processing System (PS) -- i.e. the Dual-core ARM Cortex A9 processor -- is done through the "ZYNQ7 Processing System" IP block. (From this point forward I will use "ZNYQ7 Processing System" IP block and PS, interchangeably.)

Figure 6. In order to configure the PS, first create a block design (1.) and either select the "Add IP" button (2.) or the "Add IP" hyperlink from the "Design Assistance" (3.) to an add an IP block, and then select the "ZYNQ7 Processing System" block (4.).

Once the "ZYNQ7 Processing System" is created, the "Design Assistance" provides the option to "Run Block Automation", which automatically connects the "ZYNQ7 Processing System" IP block's DDR and FIXED_IO interfaces to their respective port interfaces. This is not of importance to this post, but double-clicking on the "ZYNQ7 Processing System" IP block opens up the "Re-customize IP" window for the IP block. There, design options from configuring the PS's peripheral interfaces to configuring how the PS interacts with the surrounding programmable logic (PL) are done.

In this post, I show how 4 LEDs, PMOD2 Connector , and a pushbutton can be accessed from the Zynq EPP, using the "AXI Interconnect" IP block. The IP block, as with all IP blocks, is added by the same approach as the "ZYNQ7 Processing System" IP block. Please view figure 7 to see how both IP blocks are connected. The "AXI Interconnect" block serves as a memory-mapped interface between the PS and other AXI-peripherals connected to the "AXI Interconnect". The "AXI Interconnect" is treated as a slave to the PS, whereas other blocks connected to the "AXI Interconnect" are treated as slaves to the IP block. Opening the "AXI Interconnect" IP block's "Re-customize IP" window provides options to add more slave and master interfaces to the IP block, though none of these options are changed within this post.

Figure 7. Connecting the PS and the "AXI Interconnect"

The next step involves adding the "AXI GPIO" IP blocks that connect to the port interfaces of the 4 LEDs and a pushbutton. Once the first "AXI GPIO" IP block is added to the diagram, the option to take advantage of the "Run Connection Automation" becomes available from the "Designer Assistance". The first option of "Run Connection Automation" causes the "AXI GPIO" IP block's s_axi interface to connect to the M00_AXI interface of the "AXI Interconnect" and then creates a whole other IP block called "Proc Sys Reset". The "peripheral_aresetn" interface of the "Proc Sys Reset" is automatically connected to other interfaces, as well. At this time, connect the "ext_reset_in" input net of the "Proc Sys Reset" to the PS's "FCLK_RESET0_N" output net. Running the "/axi_gpio_1/gpio" from the "Run Connection Automation" opens up a window with the option to select a pre-constructed board interface and automatically connect it to the "AXI_GPIO" IP block. One of the two options -- which should be "gpio_sw" and "leds_4bits" -- should be selected and another "AXI_GPIO" IP block should be added to the diagram for the other option. Make sure to run the two options from the "Run Connection Automation" for the last "AXI GPIO" IP block. Finally, the "interconnect_aresetn" and "peripheral_aresetn" interfaces of the "Proc Sys Reset" IP block are connected to the "ARESETN" and "S00_ARESETN" interfaces of the "AXI Interconnect", respectively. Figure 8 shows how the completed block design should appear.

Figure 8. The highlighted area labeled 1. is the "Validate Design" button.

Please note the the "AXI Interconnect" can be configured to include more slaves and more "AXI GPIO" IP blocks with up to 32-bits can be added to connect to other modules within the Zynq EPP's PL or external pins on the ZC702. The "AXI GPIO" IP blocks can, of course, be configured for custom port interfaces, but more information on how will be included in the next post. For now, the predefined port interfaces are selected and thus, invisible to the user, the IDE handles configuring the appropriate constraints for the rest of the design.

The "Validate Design" button should be selected to ensure the block diagram is built, properly. Chances are the addresses associated with the "AXI GPIO" IP blocks have not been assigned and will cause validation to fail and raise an alarm to the un-mapped AXI interfaces. In the "Address Editor" window, which appear next to the tab of the "Diagram" window in the IDE, the addresses can be auto-assigned by the "Design Assistance".

Figure 9. The unassigned addresses can be auto-assigned in the "Address Editor" (1.). The highlighted menu appears by right-clicking in the "Address Editor" (2.).

The final step before synthesis is to create a "HDL Wrapper" to encapsulate the block design and act as the project's Top Module. To create the "HDL Wrapper", make sure the "Sources" window is open and right-click the block diagram and select the "Create HDL Wrapper" option. If the block design was properly developed, a HDL module should appear, written in the user's target hardware description language (HDL) and encapsulating the block design.

Figure 10. Clicking on the "Project Manager" button (1.) opens a window that has the tabs "Sources" and "Templates". The "Sources" tab (2.) should be selected. Here, the project's source files, constraints (XDC), and simulation sources are found. Right-clicking the newly created design block (3.) opens up a menu from where the "Create HDL Wrapper" (4.) is available. Finally, the "Generate Bitstream" button is selected to initiate synthesis, implementation, and bitstream generation in a single stroke.

As stated last in figure 10's description, the "Generate Bitstream" button finally begins the compilation of the design. While compilation is completing, take the time to explore the IDE, read the documentation, and/or watch tutorial videos. If there are any errors revealed during compilation, review the "Zynq System" example found under the Vivado's example folder. Recall, the examples folder can be found on the IDE's welcome screen (see figure 4). Once the "Bitstream Generation Completed" window opens, the option is given to "Open Hardware Session". Once the "Hardware Session" is opened, click on the "Open a new hardware target" in the "Design Assistance" toolbar to open up the "Open New Hardware Target" window. In the window's second page, a field is shown where the "name" of the device to which the ZC702, or other Vivado-compatible devices, is connected. "Name" can either be an IP (i.e. Internet Protocol, in this context) Address for remote connection or "localhost" for the computer on which Vivado's IDE is currently running.

Figure 11. Steps to open up the "Open New Hardware Target" window

If the ZC702 slide switches have been successfully configured, its JTAG Configuration Bridge Module is connected to the host machine with the correct USB cable, and the appropriate device drivers are installed on the host machine, then clicking next should open up the "Select Hardware Target" page where the ZC702 is listed as the "Hardware Target" "xilinx_tcf". The "Hardware Devices" should include XC7Z020, which is the part number of the Z-7020 Zynq EPP.

Figure 12. The "Select Hardware Target" page

Nothing should be changed in the next few pages of the "Open New Hardware Target" window. After the finish button is selected, the connected devices should appear in the hardware window. Right-clicking the XCZ020 opens a menu where the option to select the bitstream (i.e. binary file ending in ".bit") and program the Z-7020 is available. The program is only downloaded to the Z-7020's main memory, which is volatile. As such, the Z-7020 will need reprogramming if turned off.

Figure 13. The "XC7Z020_1" device (1.) is right-clicked and the "Program Device" option (2.) is selected in order to program the Zynq EPP's hardware with the design discussed so far within this post. In preparation for exporting the block design and the bitstream to the SDK to develop software for the Zynq EPP's PS, the implemented design (3.) and then the block design (4.) must be opened.

The Software Development Kit. The SDK is another IDE developed by Xilinx and whose purpose is for developing, debugging, and loading embedded programs onto Xilinx's softcore and hardcore processors, including the two cores of Zynq EPP's ARM processing system. The SDK was developed based on the popularly used Eclipse IDE and thus application programs can also be developed from the SDK. In addition to its compiler, editor, tools for memory management, the SDK also contains tools for loading either a Linux or another kernel onto the Zynq EPP's PS, debugging tools for multiprocessing applications, and libraries/device drivers facilitating software development. Loading kernels and debugging will be explored in later posts. This post's discussion on the SDK introduces the libraries specific to embedded programs for the Zyne EPP's PS.

Figure 13's description explains the steps required before the block design and bitstream can be exported to the SDK. Once the block design is open, the SDK is opened through the Vivado IDE's File menu, Export, and then the "Export Hardware for SDK" option.

Figure 14. Where the "Export Hardware for SDK" button is found

In the "Export Hardware for SDK" window, make sure "Export Hardware", "Include bitstream", and "Launch SDK" are selected and then select "OK" to open the SDK. In the future, the SDK can be opened without exporting the block design (i.e. hardware) and the bitstream by only having the "Launch SDK" check box selected. However, if any changes are made to the design, the block design and bitstream will have to be exported to the SDK, again. As an incredibly useful feature, the SDK automatically modifies its auto-generated libraries and drivers so that the software remains compatible with the hardware.

Figure 15. "Export Hardware for SDK" window

Once SDK opens and finishes its initializing procedures, the "hw_platform_0" (default name) hardware platform should be an open folder located within the "Project Explorer". The "Project Explorer" window should be located along the left-hand side of the screen. To create a project for an embedded program, select the File menu, New, and then "Application Project" as shown in figure 16.

Figure 16. How to create a new embedded program

The "New Project" window that opens provides the options to select the project's hardware platform, one of the two processor cores to program, target operating system, language, and either create or load a "Board Support Package". For now, just follow the options I select in figure 17.

Figure 17. The embedded program explained in this post will function just as well as a C++ program; however, the language is selected as C to show the available templates not shown if C++ is selected.

After the work space is finished building, two new folders appear in the "Project Explorer", the application project and board support package. All three folders are necessary to create a complete embedded program for the PS. More information on the SDK's concepts is found by selecting the Help menu and then "Help Contents". For help information pertinent to the embedded software development, pay attention to the file system.mss in the board support package folder.

Figure 18. The "system.mss" file

The SDK includes links to the documentation relevant to the hardware platform and board support package in the system.mss file. If the hardware platform changes -- that is to say, the block design exported from the Zynq IDE undergoes a change -- the documentation may reflect those changes.

Time to finally create the source file that contains the main function's definition.

Figure 19. Right-click the application project (1.), select new (2.), and then finally select File to create a file (3.). Make sure to end the source file''s name with .c to ensure the file becomes a C source file. The outline tab (4.) I found to be an extremely useful tool because it provides convenient links to header files, and the header files contained detail descriptions on how certain are called.

Below is the source code.

/* the source code below was written by Andrew Powell on 9/27/2013 */ #include "xparameters.h" /* contains the auto-generated constants */ #include "xgpio.h" /* prototypes and structure definitions for AXI GPIO interaction */ /* set up boolean */ typedef unsigned char bool; #define true 1 #define false 0 /* where the embedded program enters */ int main() { /* these data structures hold the necessary information for interacting with AXI */ XGpio pushbutton, leds; /* prevents continuously pressing the pushbutton from messing up program */ bool lock = false; /* for counting the number of times the pushbutton was pressed */ const int counterMax = 5; int counter = 0; /* attempt to initialize the AXI GPIOs */ if ((XGpio_Initialize(&pushbutton, XPAR_AXI_GPIO_1_DEVICE_ID) != XST_SUCCESS) || (XGpio_Initialize(&leds, XPAR_AXI_GPIO_2_DEVICE_ID) != XST_SUCCESS)) { return XST_FAILURE; } /* set the directions of the GPIOs */ XGpio_SetDataDirection(&pushbutton, 1, 0x1); XGpio_SetDataDirection(&leds, 1, 0x0); /* quit program once the user pressed the pushbutton to the amount equaled to counterMax*/ while (counter <= counterMax) { /* check if pushbutton is pressed */ /* if the pushbutton is pressed, turn on all 4 leds */ /* if the pushbutton is not pressed, turn off all 4 leds and increment the counter */ if (XGpio_DiscreteRead(&pushbutton, 1)) { XGpio_DiscreteWrite(&leds, 1, 0xF); if (!lock) { lock = true; } } else if (!XGpio_DiscreteRead(&pushbutton, 1)) { XGpio_DiscreteWrite(&leds, 1, 0x0); if (lock) { counter++; lock = false; } } } return XST_SUCCESS; }

The "xparameters.h" header file is extremely important because constants, such as "XPAR_AXI_GPIO_1_DEVICE_ID" and "XPAR_AXI_GPIO_2_DEVICE_ID", refer to specifics regarding the hardware platform. I found the "xparameters.h" header file goes through the most changes if the hardware platform's "ZYNQ7 Processing System" IP block changed, significantly. "xgpio.h" simply contains the structures and prototypes necessary to service the "AXI GPIO" IP blocks.

Figure 20. Note that the source file is compiled down to an elf file (i.e. the binary that is downloaded onto the PS) whenever the source file saved; in other words, prior to programming the PS, make sure the source file is saved without compilation error.

Performing the steps in figure 21's description, enter the application project's "Run Configurations". Select "Xilinx C/C++ application (GDB)" and then select the "New" button to create the configuration. If all steps up to this point were done correctly, the name of the C/C++ Aplication should be an elf file. The Run button towards the button of the window should also be available. If the bitstream was downloaded onto the Z-7020 with the Vivado IDE, then go ahead select Run to finally build the entire embedded program and execute the program on the Zynq EPP's PS.

Figure 21. To enter the "Run Configuration" window, first right-click the application project (1.), scroll down to "Run As" (2.), and then select "Run Configurations" (3.).

Figure 22. The embedded program is finally built and executed by first selecting the "Xilinx C/C++ application (GDB)" option (1.), creating a new configuration (2.), and then selecting "Run" (3.).