Вы находитесь на странице: 1из 204

Basic Embedded

System Design
Tutorial
using MICROBLAZE and ZYNQ-7000 AP SOC embedded
processors to design two frequencies PWM modulator
system

January 17, 2017


Contents

1 INTRODUCTION 1
1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Purpose of this tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.3 Objectives of this tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.4 One possible solution for the modulator design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.4.1 Block Diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.4.2 Design Steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.5 Embedded Design Process Flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

2 CREATING THE HARDWARE PLATFORM 13


2.1 Create a New Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.2 Vivado Integrated Design Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.3 Create MicroBlaze-based hardware platform . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.4 Create ARM-based hardware platform . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
2.5 Create a socius board based hardware platform . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

3 CREATING THE SOFTWARE PLATFORM USING SDK 83


3.1 Board Support Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
3.2 Creating an application project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
3.3 Creating a C/C++ source files for MicroBlaze-based processor system . . . . . . . . . . . . . . . . . . . 90
3.4 Creating a C/C++ source files for ARM-based processor system . . . . . . . . . . . . . . . . . . . . . . . 99
3.5 Creating a C/C++ source files for a socius board based hardware platform . . . . . . . . . . . . . . . . . 105
3.6 Viewing and configuring Linker Script file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
3.7 Building application and generating ELF file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
3.8 Running Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
3.8.1 Downloading MicroBlaze-based bitstream file . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
3.8.2 Downloading ARM-based bitstream file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
3.9 Application Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
3.9.1 Debug Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
3.9.2 Debug Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
3.9.3 Debug Perspective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126

4 DEBUGGING SYSTEM USING VIVADO LOGIC ANALYZER 135


4.1 Illustration of using Vivado Logic Analyzer tool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
CONTENTS

4.2 Illustration of using Vivado Logic Analyzer together with SDK Debugger . . . . . . . . . . . . . . . . . . . 149

5 USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN 157


5.1 Using custom IP in MicroBlaze-based processor system . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
5.2 Using custom IP in ARM-based processor system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
5.3 Developing a device driver for the custom IP core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
5.3.1 Device driver for PWM Modulator IP core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
5.3.2 Creating Xilinx driver file and folder structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180

6 CONCLUSION 187

7 EXERCISES 191

Index 192

ii
List of Figures

1.1 Example of the PWM signal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2


1.2 Sine wave with 256 samples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Structure of microprocessor-based embedded system that will be used in tutorial . . . . . . . . . . . . . . 4
1.4 MicroBlaze core block diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5 Zynq-7000 AP SoC block diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.6 UART block diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.7 AXI Timer core block diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.8 AXI INTC core block diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.9 AXI GPIO block diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.10 Design steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.11 Typical embedded design process flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2.1 The Vivado Getting Started page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13


2.2 Create a New Vivado Project dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.3 Project Name dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.4 Project Type dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.5 Default Part dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.6 New Project Summary dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.7 Vivado IDE Viewing Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.8 Vivado IDE Default Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.9 Project Summary View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.10 Create Block Design option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.11 Create Block Design dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.12 Vivado IDE with a blank design canvas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.13 Add IP option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.14 Add IP link . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.15 Add IP button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.16 MicroBlaze core in the IP Catalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.17 Automatically instantiated MicroBlaze core in the IP Integrator design canvas . . . . . . . . . . . . . . . . 23
2.18 IP Integrator design canvas with instantiated IPs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.19 Run Block Automation dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.20 A basic system design created by the Block Automation feature . . . . . . . . . . . . . . . . . . . . . . . 25
2.21 Run Connection Automation dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
LIST OF FIGURES

2.22 Re-customize IP - AXI GPIO (2.0) dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

2.23 Re-customize IP - Clocking Wizard (5.1) dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

2.24 Run Connection Automation dialog box for axi_gpio_0 -> GPIO interface . . . . . . . . . . . . . . . . . . 29

2.25 Re-customize IP - Constant (1.1) dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

2.26 Re-customize IP - Processor System Reset (5.0) dialog box . . . . . . . . . . . . . . . . . . . . . . . . . 31

2.27 Final Block Diagram of our design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

2.28 Validate Design option from the main menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

2.29 Validate Design button from the main toolbar menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

2.30 Validate Design dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

2.31 Project Settings dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

2.32 Create HDL Wrapper option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

2.33 Create HDL Wrapper dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

2.34 Sources window with generated HDL wrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

2.35 modulator_mb_wrapper.vhd source file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

2.36 Block diagram of the modulator_wrapper.vhd file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

2.37 Add Sources command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

2.38 Add Sources dialog box - add or create design sources option . . . . . . . . . . . . . . . . . . . . . . . . 36

2.39 Add or Create Design Sources dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

2.40 Add Source Files dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

2.41 Add or Create Design Sources dialog box - with added files . . . . . . . . . . . . . . . . . . . . . . . . . 38

2.42 Sources view with new wrapper file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

2.43 IO Planning layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

2.44 I/O Planning View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

2.45 I/O Ports tab with assigned pin locations and I/O standards . . . . . . . . . . . . . . . . . . . . . . . . . 40

2.46 Properties window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

2.47 Created modulator_wrapper constraints file in the Sources window . . . . . . . . . . . . . . . . . . . . . 41

2.48 modulator_wrapper.xdc file with physical constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

2.49 Timing Constraints option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

2.50 Timing Constraints window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

2.51 Create Clock dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

2.52 Specify Clock Source Objects dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

2.53 Create Clock dialog box after specifying the period for the clk_p . . . . . . . . . . . . . . . . . . . . . . . 46

2.54 Timing Constraints window with the create_clock constraint . . . . . . . . . . . . . . . . . . . . . . . . . 47

2.55 modulator_wrapper.xdc file with physical and timing constraints . . . . . . . . . . . . . . . . . . . . . . . 48

2.56 Run Synthesis, Run Implementation and Generate Bitstream commands from the Vivado Flow Navigator . 49

2.57 Create Block Design dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

2.58 Zynq7 Processor core in the IP Catalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

2.59 Automatically instantiated Zynq7 Processor core in the IP Integrator design canvas . . . . . . . . . . . . . 50

2.60 IP Integrator design canvas with all three instantiated IPs . . . . . . . . . . . . . . . . . . . . . . . . . . 51

2.61 Zynq-7 Run Block Automation dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

2.62 Zynq-7000 AP SoC Processing System after Running Block Automation . . . . . . . . . . . . . . . . . . 52

iv
LIST OF FIGURES

2.63 Re-customize IP - AXI GPIO (2.0) dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

2.64 Zynq-7000 AP SoC Processing System after Running Connection Automation . . . . . . . . . . . . . . . 54

2.65 Re-customize IP - ZYNQ7 Processing System (5.5) dialog box . . . . . . . . . . . . . . . . . . . . . . . 55

2.66 Re-customize IP - Constant (1.1) dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

2.67 Re-Customize IP - Processor System Reset (5.0) dialog box . . . . . . . . . . . . . . . . . . . . . . . . . 57

2.68 Final Block Diagram of our design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

2.69 Sources window with generated HDL wrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

2.70 I/O Planning layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

2.71 I/O Planning view . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

2.72 Run Implementation and Generate Bitstream commands from the Vivado Flow Navigator . . . . . . . . . 59

2.73 Project Name dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

2.74 Default Part dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

2.75 Vivado IDE Viewing Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

2.76 Sources view with added new files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

2.77 Sources view with added constraints file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

2.78 Tcl Console window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

2.79 Block diagram of Zynq PS configured to run on socius board . . . . . . . . . . . . . . . . . . . . . . . . 82

3.1 SDK application development flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

3.2 Export Hardware dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

3.3 Launch SDK dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

3.4 SDK main window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

3.5 Board Support Package option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

3.6 New Board Support Package Project dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

3.7 Board Support Package Settings dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

3.8 Application Project dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88

3.9 Templates dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

3.10 SDK main window after C project creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90

3.11 New Source File dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

3.12 Adding math library to Libraries list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

3.13 Added math library to Libraries list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

3.14 Linking and Locating process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

3.15 Linker and Locator Flows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

3.16 Generate linker script dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

3.17 SDK Software Flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

3.18 Console window with code size information for the modulator_no_intc build configuration for MicroBlaze-
based system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

3.19 Console window with code size information for the modulator_no_intc build configuration for ARM-based
system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

3.20 Run Workflow diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

3.21 Program FPGA option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

3.22 Program FPGA dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

v
LIST OF FIGURES

3.23 Program FPGA flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118


3.24 Program FPGA diloag box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
3.25 SDK Terminal window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
3.26 Connect to serial port dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
3.27 Terminal notification about successful connection to serial port . . . . . . . . . . . . . . . . . . . . . . . 120
3.28 Terminal window with messages sent by software application . . . . . . . . . . . . . . . . . . . . . . . . 120
3.29 Terminal window with messages sent by software application after changing the switch position on the
development board . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
3.30 GDB overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
3.31 Debug Workflow diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
3.32 Debug Configurations option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
3.33 Creating a new configuration of the selected type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
3.34 Automatically generated “modulator_no_intc Debug by the SDK” configuration . . . . . . . . . . . . . . . 124
3.35 Application tab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
3.36 Open Perspective button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
3.37 Open Perspective dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
3.38 SDK Debug Perspective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
3.39 Result of the execution of the Step Into command on the XGpio_Initialize function . . . . . . . . . . . . . 127
3.40 Result of Step Return command execution within XGpio_Initialize function . . . . . . . . . . . . . . . . . 128
3.41 Result of the execution of the Step Over command on the XGpio_SetDataDirection function . . . . . . . . 128
3.42 Add Breakpoint option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
3.43 Properties for C/C++ Line Breakpoint dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
3.44 Breakpoint added . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
3.45 Breakpoint reached . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
3.46 Starting address of the sine_ampl array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
3.47 Monitor Memory dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
3.48 Content of the sine_ampl array in Memory window before array initialization . . . . . . . . . . . . . . . . . 131
3.49 Change of i variable value indication in the Variables tab . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
3.50 Indication of the change of the sine_ampl value in Memory window . . . . . . . . . . . . . . . . . . . . . 132
3.51 Properties for C/C++ Line Breakpoint dialog box - condition breakpoint setup i==5 . . . . . . . . . . . . . 132
3.52 Conditional breakpoint added . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
3.53 Conditional breakpoint reached, i==5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133

4.1 Finial Block Diagram of our design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136


4.2 Mark Debug option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
4.3 Set Up Debug button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
4.4 Tools - Set Up Debug option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
4.5 Set Up Debug dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
4.6 Nets to Debug dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
4.7 Find Nets dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
4.8 Remove Nets option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
4.9 ILA Core Options dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

vi
LIST OF FIGURES

4.10 Set Up Debug Summary dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140


4.11 Netlist window with generated ILA core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
4.12 Run Implementation and Generate Bitstream options . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
4.13 Program FPGA dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
4.14 Open Hardware Manager command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
4.15 Open Hardware Target dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
4.16 Hardware Server Settings dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
4.17 Select Hardware Target dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
4.18 Open Hardware Target Summary dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
4.19 Hardware view after opening a connection to the hardware target . . . . . . . . . . . . . . . . . . . . . . 145
4.20 ILA Dashboard window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
4.21 Create new trigger state machine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
4.22 Trigger FSM code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
4.23 Insert Probe window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
4.24 Trigger FSM State Diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
4.25 Captured waveform after trigger condition is reached . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
4.26 Breakpoint tab - Remove option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
4.27 Added Breakpoint at line 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
4.28 Properties for C/C++ Line Breakpoint dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
4.29 Debug Probes option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
4.30 Add Probes to Basic Trigger Setup option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
4.31 Added probes to the ILA Basic Trigger Setup window . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
4.32 Value dialog box for the microblaze_0_dlmb_1_ABUS[0:31] ILA debug probe . . . . . . . . . . . . . . . . 152
4.33 Value dialog box for the microblaze_0_dlmb_1 WRITESTROBE ILA debug probe . . . . . . . . . . . . . . 152
4.34 ILA Properties window after arming trigger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
4.35 Add Probes to Waveform option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
4.36 Waveform window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
4.37 Memory tab with data value on the memory address 0xA690 . . . . . . . . . . . . . . . . . . . . . . . . 155

5.1 Structure of microprocessor-based embedded system, using a custom IP to generate pwm signal . . . . . 157
5.2 Repository Manager window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
5.3 Add Repository dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
5.4 Repository Manager with selected ip_repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
5.5 IP Catalog with added modulator_axi_ip_v1.0 IP core . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
5.6 IP Integrator design canvas with instantiated IPs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
5.7 Re-customize IP - modulator_axi_ip_v1.0 (1.0) dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . 161
5.8 A basic MicroBlaze-based system design created by the Block Automation feature . . . . . . . . . . . . . 162
5.9 Re-customize IP - AXI GPIO (2.0) dialog box - Board tab . . . . . . . . . . . . . . . . . . . . . . . . . . 163
5.10 Re-customize IP - AXI GPIO (2.0) dialog box - IP Configuration tab . . . . . . . . . . . . . . . . . . . . . 164
5.11 Re-customize IP - Clocking Wizard (5.1) dialog box - Clocking Options tab . . . . . . . . . . . . . . . . . 165
5.12 Re-customize IP - Clocking Wizard (5.1) dialog box - Output Clocks tab . . . . . . . . . . . . . . . . . . . 166
5.13 A basic MicroBlaze-based system design after running Connection Automation feature . . . . . . . . . . . 166

vii
LIST OF FIGURES

5.14 Re-customize IP - Constant (1.1) dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167


5.15 Re-Customize IP - Processor System Reset (5.0) dialog box . . . . . . . . . . . . . . . . . . . . . . . . . 167
5.16 Final Block Diagram of the modulator_axi_mb block design . . . . . . . . . . . . . . . . . . . . . . . . . 168
5.17 Re-customize IP - Processor System Design (5.0) dialog box - values of the External and Auxillary reset
after design validation process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
5.18 Sources window with created modulator_axi_mb_wrapper.xdc constraints file . . . . . . . . . . . . . . . . 170
5.19 IP Integrator design canvas with instantiated IPs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
5.20 Zynq-7000 AP SoC Processing System after Running Block Automation . . . . . . . . . . . . . . . . . . 173
5.21 Zynq-7000 AP SoC Processing System after Running Connection Automation . . . . . . . . . . . . . . . 174
5.22 Final Block Diagram of the modulator_axi_arm block design . . . . . . . . . . . . . . . . . . . . . . . . . 174
5.23 Required folder structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
5.24 Preferences dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
5.25 Browse For Folder dialog box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
5.26 Board Support Package Settings dialog box with selected modulator_axi_ip custom driver . . . . . . . . . 184

viii
List of Tables
Chapter 1

INTRODUCTION

1.1 Motivation

Basic Embedded System Design Tutorial is a document made for beginners who are entering the embedded system design
using FPGAs. This tutorial explains, step by step, the procedure of designing a simple digital system using C language and
Xilinx Vivado Design Suite.

1.2 Purpose of this tutorial

This tutorial is made to introduce you how to create and test an project and run it on your development board.
After completing this tutorial, you will be able to:

• Launch and navigate the Vivado Integrated Design Environment (IDE)

• Create a MicroBlaze and Zynq-7000 AP SoC processor system project using Vivado IP Integrator tool

• Synthesize and implement the design in the Vivado IDE

• Export a hardware description XML file for later software development

• Create and debug your software application using SDK tool

• Generate the hardware implementation bitstream file and download it to the target Xilinx development board

• Debug a design in hardware using Vivado Logic Analyzer

The following project is designed for:

• Designing Surface: VIVADO 2016.4

• Programming Language: C

• Device: Xilinx ZedBoard Zynq Evaluation and Development Platform

1.3 Objectives of this tutorial

In this tutorial a PWM signal modulated using the sine wave with two different frequencies (1 Hz and 3.5 Hz) will be
created. Frequency that will be chosen depends on the position of the two-state on-board switch (sw0).
PWM Signal
Pulse-width modulation (PWM) uses a rectangular pulse wave whose pulse width is modulated by some other signal (in
our case we will use a sine wave) resulting in the variation of the average value of the waveform. Typically, PWM signals
INTRODUCTION

are used to either convey information over a communications channel or control the amount of power sent to a load. To
learn more about PWM signals, please visit http://en.wikipedia.org/wiki/Pulse-width_modulation.
Illustration 1.1. illustrates the principle of pulse-width modulation. In this picture an arbitrary signal is used to modulate the
PWM signal, but in our case sine wave signal will be used.

Figure 1.1: Example of the PWM signal

1.4 One possible solution for the modulator design

Considering that we are working with digital systems and signals, our task will be to generate an digital representation of
an analog (sine) signal with two frequencies: 1 Hz and 3.5 Hz.
Illustration 1.2 is showing the sine wave that will be used to modulate the PWM signal.

Figure 1.2: Sine wave with 256 samples

One period of the sine wave is represented with 256 (2∧ 8) samples, where each sample can take one of 4096 (2∧ 12)
possible values. Since the sine wave is a periodic signal, we only need to store samples of one period of the signal.
Note : Pay attention that all of sine signals with the same amplitude, regardless their frequency, look the same during the
one period of a signal. The only thing that is different between these sine signals is duration of a signal period. This means
that the sample rate of these signals is different.
Considering that the whole system will be clocked with the 100 MHz system clock, to get 1 Hz and 3.5 Hz frequencies
(which is much smaller than 100 MHz) we should divide system clock frequency with integer value N.

2
1.4 One possible solution for the modulator design

In the Tables 1.1 and 1.2 are shown parameters that are necessary for generating sine signals with 1 Hz and 3.5 Hz
frequencies.
Table 1.1: Sine signal with the frequency of 1 Hz
Division Factor Steps Calculation Explanation
T=1 s T=1/1 Hz=1 s T is the period of the signal
f1=256 Hz f1=256∗1 Hz=256 Hz (or read in f1 is the frequency of reading whole
time: 1 s/256) period (T) with 256 samples
N1=390625 N1=100 MHz/256 Hz=390625 N1 is the number which divides
frequency of the input clock signal
(100 MHz) to the required frequency
for the digital sine module
N2=95 N2=390625/4096=95.3674 N2 is the number which divides
frequency of the input clock signal
(100 MHz) to the required frequency
for the PWM’s FSM module
N1=389120 N1=95∗4096=389120 This is new calculation, because N1
must be divisible with 4096
Table 1.2: Sine signal with the frequency of 3.5 Hz
Division Factor Steps Calculation Explanation
T=0.286 s T=1/3.5 Hz=0.286 s T is the period of the signal
f2=896 Hz f2=256∗3.5 Hz=896 Hz (or read in f2 is the frequency of reading whole
time: 0.286 s/256) period (T)
N1=111607.1429 N1=100 MHz/896 N1 is the number which divides
Hz=111607.1428571 frequency of the input clock signal
(100 MHz) to the required frequency
for the digital sine module
N2=27 N2=111607.1428571/4096=27.2478 N2 is the number which divides
frequency of the input clock signal
(100 MHz) to the required frequency
for the PWM’s FSM module
N1=110592 N1=27∗4096=110592 This is new calculation, because N1
must be divisible with 4096
Now, it is obvious that the sine wave can be generated by reading sample values of one period, that are stored in one
table, with appropriate speed. In our case the values will be generated using the sine function from the C numerics library
(math.h) and will be stored in an array.

1.4.1 Block Diagram

Block diagram on the Illustration 1.3 shows the structure of one possible system that can be used to generate required
PWM signals.

3
INTRODUCTION

Figure 1.3: Structure of microprocessor-based embedded system that will be used in tutorial

The embedded system is composed of:

• MicroBlaze or Zynq-7000 AP SoC Processor core

• AXI Timer core

• AXI Interrupt Controller core

• AXI GPIO core to drive the LED and to read the status of the SWITCH

Let us briefly explain each part of this system:


MicroBlaze Processor - is a 32-bit soft processor, which is included in the Vivado 2016.4 release. The MicroBlaze is
highly configurable soft core processor, allowing you to select a specific set of features required by your design.
The fixed feature set of the processor includes:

• Thirty-two 32-bit general purpose registers

• 32-bit instruction word with three operands and two addressing modes

• 32-bit address bus

• Single issue pipeline

In addition to these fixed features, the MicroBlaze processor is parametrized to allow selective enabling of additional
functionality:

• Hardware multiplier and divider

• Hardware debug logic

• Floating Point Unit (FPU)

• Memory Management Unit (MMU)

• LMB data and instruction side interface

• AXI4 data (M_AXI_DP) and instruction (M_AXI_IP) side interface

• AXI4 protocol for D-Cache (M_AXI_DC) and I-Cache (M_AXI_IC)

4
1.4 One possible solution for the modulator design

• AXI4 protocol for stream access

• etc...

Figure 1.4: MicroBlaze core block diagram

The MicroBlaze core is organized as a Harvard architecture with separate bus interface units for data and instruction
accesses. The following two memory interfaces are supported: Local Memory Bus (LMB) , and the AMBA AXI4 interface
(AXI4) and the AMBA AXI4 interface (AXI4) and ACE interface (ACE). The LMB provides single-cycle access to on-chip
dual-port block RAM. The AXI4 interfaces provide a connection to both on-chip and off-chip peripherals and memory. The
ACE interfaces provide cache coherent connections to memory. MicroBlaze also supports up to 16 AXI4-Stream interface
ports, each with one master and one slave interface.
MicroBlaze can be configured with the following bus interfaces:

• The AMBA AXI4 Interface for peripheral interfaces, and the AMBA AXI4 or AXI Coherency Extension (ACE) Interface
for cache interfaces

• LMB provides simple synchronous protocol for efficient block RAM transfers

• AXI4-Stream provides a fast non-arbitrated streaming communication mechanism

• Debug interface for use with the Microprocessor Debug Module (MDM) core

• Trace interface for performance analysis

Note: If you want to read and learn more about the MicroBlaze Processor core, please refer to "MicroBlaze Processor
Reference Guide (v2016.4)".
Zynq-7000 AP Soc Processor - The Zynq-7000 family is based on the Xilinx All Programmable SoC (AP SoC) architecture.
The Zynq-7000 AP SoC is composed of two major functional blocks: Processing System (PS) and Programmable
Logic (PL) , see Illustration 1.5. The hart of the Processing System block is dual- core ARM Cortex-A9 MPCore CPU .
Beside ARM processor, PS also includes Application Processor Unit (APU), Memory Interface, I/O Peripherals (IOP) and
Interconnect.

5
INTRODUCTION

Figure 1.5: Zynq-7000 AP SoC block diagram

Processing System (PS) :


Application Processor Unit (APU) - provides an extensive offering of high-performance features and standards-compliant
capabilities. APU contains:
Dual ARM Cortex-A9 MPCore CPUs with ARM v7:

• Run time options allows single processor, asymmetrical (AMP) or symmetrical multiprocessing (SMP) configurations

• ARM version 7 ISA: standard ARM instruction set and Thumb-2, Jazelle RCT and Jazelle DBX Java acceleration

• NEON 128 SIMD coprocessor and VFPv3 per MPCore

• 32 KB instruction and data L1 caches with parity per MPCore

• 512 KB of shareable L2 cache with parity

• Private timers and watchdog timers

Memory Controller - the memory interfaces includes multiple memory technologies:

• DDR Controller

• DDR Controller Core and Transaction Scheduler

• Quad-SPI Controller

• Static Memory Controller (SMC)

I/O Peripherals - the I/O Peripherals (IOP) are a collection of industry-standard interfaces for external data communication:

6
1.4 One possible solution for the modulator design

• GPIO

• Gigabit Ethernet Controllers (two)

• USB Controller: Each as Host, Device or OTG (two)

• SD/SDIO Controllers (two)

• SPI Controllers (two): Master or Slave

• CAN Controllers (two)

• UART Controllers (two)

• I2C Controllers (two)

• PS MIO I/Os

UART Controller - is a full-duplex asynchronous receiver and transmitter that supports a wide range of programmable
baud rates and I/O signal formats. The controller can accommodate automatic parity generation and multi-master detection
mode.
The UART operations are controlled by the configuration and mode registers. The state of the FIFOs, modem signals and
other controller functions are read using status, interrupt status and modem status registers.
The controller is structured with separate Rx and Tx data paths. Each path includes a 64-byte FIFO. The controller serial-
izes and de-serializes data in the Tx and Rx FIFOs and includes a mode switch to support various loopback configurations
for the RxD and TxD signals. Software reads and writes data bytes using Rx and Tx data port registers.
Each UART controller (UART 0 and UART 1) has the following features:

• Programmable baud rate generator

• 64-byte receive and transmit FIFOs

• Programmable protocol

• Parity, framing and overrun error detection

• Line-break generation

• Interrupts generation

• RxD and TxD modes: Normal/echo and diagnostic loopbacks using the mode switch

• Loop UART 0 and UART 1 option

• Modem control signals

The block diagram of the UART module is shown on the Illustration 1.6.

Figure 1.6: UART block diagram

7
INTRODUCTION

Note: The UART Controller will be used in the Sub-chapter 3.4 Creating a C/C++ source files for ARM-based processor
system to transmit debug and system status information during application execution to the attached PC.
If you want to read and learn more about UART Controller, please refer to Chapter 19 “UART Controller” in the “Zynq-7000
All Programmable SoC – Technical Reference Manual”.
Programmable Logic (PL) :
The PL provides a rich architecture of user-configurable capabilities:

• Configurable Logic Blocks (CLB)

• 36 Kb Block RAM

• Digital Signal Processing - DSP48E1 Slice

• Clock Management

• Configurable I/Os

• Low-Power Gigabit Transceivers

• Analog to Digital Converter (XADC)

• Integrated Interface Blocks for PCI Express designs

Note: If you want to read and learn more about the Zynq-7000 AP Soc processor core, please refer to "Zynq-7000 All
Programmable SoC - Technical Reference Manual".
LogiCORE IP AXI Timer/Counter - The LogiCORE IP AXI Timer/Counter is a 32/64-bit timer module that interfaces to the
AXI4-Lite interface. The AXI Timer is organized as two identical timer modules. Each timer module has an associated load
register that is used to hold either the initial value for the counter for event generation or a capture value, depending on the
mode of the timer.
The AXI Timer includes the following features:

• AXI interface based on the AXI4-Lite specification

• Two programmable interval timers with interrupt, event generation, and event capture capabilities

• Configurable counter width

• One Pulse Width Modulation (PWM) output

• Cascaded operation of timers in generate and capture modes

• Freeze input for halting counters during software debug

Figure 1.7: AXI Timer core block diagram

8
1.4 One possible solution for the modulator design

Note: If you want to read and learn more about the AXI Timer/Counter core, please refer to "LogiCORE IP AXI Timer v2.0
Product Guide".
LogiCORE IP AXI Interrupt Controller (INTC) - The LogiCORE IP AXI Interrupt Controller (INTC) core receives multiple
interrupt inputs from peripheral devices and merges them to a single interrupt output to the system processor. The registers
used for storing interrupt vector addresses, checking, enabling and acknowledging interrupts are accessed through the A-
XI4-Lite interface.
The AXI Interrupt Controller includes the following features:

• Register access through the AXI4-Lite interface

• Fast Interrupt mode

• Supports up to 32 interrupts. Cascadable to provide additional interrupt inputs

• Single interrupt output

• Priority between interrupt requests is determined by vector position. The least significant bit (LSB, in the case bit 0)
has the highest priority

• Interrupt Enable Register for selectively enabling individual interrupt inputs

• Master Enable Register for enabling interrupts request output

• Each input is configurable for edge or level sensitivity. Edge sensitivity can be configured for rising or falling. Level
sensitivity can be active-high or active-low

• Output interrupt request pin is configurable for edge or level generation. Edge generation is configurable for rising or
falling and level generation is configurable for active-high or active-low

• Configurable Software Interrupt capability

• Support for nested interrupts

Figure 1.8: AXI INTC core block diagram

The LogiCORE IP INTC core concentrates multiple interrupt inputs from peripheral devices to a single interrupt output to
the system processor. The registers used for checking, enabling, and acknowledging interrupts are accessed through the
AXI4-Lite interface.

9
INTRODUCTION

Note : If you want to read and learn more about the AXI Interrupt Controller core, please refer to "LogiCORE IP AXI
Interrupt Controller (INTC) v4.1 Product Guide".
LogiCORE IP AXI General Purpose Input/Output (GPIO) - The LogiCORE IP AXI General Purpose Input/Output (GPIO)
core provides a general purpose input/output interface to the AXI interface. This 32-bit soft IP core is designed to interface
with the AXI4-Lite interface.
The AXI GPIO includes the following features:

• Supports the AXI4-Lite interface specification

• Supports configurable single or dual GPIO channel(s)

• Supports configurable channel width for GPIO pins from 1 to 32 bits

• Supports dynamic programming of each GPIO bit as input or output

• Supports individual configuration of each channel

• Supports independent reset values for each bit of all registers

• Supports optional interrupt request generation

The AXI GPIO design provides a general purpose input/output interface to an AXI4-Lite interface. The AXI GPIO can be
configured as either a single or a dual-channel device. The width of each channel is independently configurable.
The ports are configured dynamically for input or output by enabling or disabling the 3-state buffer. The channels can be
configured to generate an interrupt when a transition on any of their inputs occurs.

Figure 1.9: AXI GPIO block diagram

Note : If you want to read and learn more about the AXI GPIO core, please refer to "LogiCORE IP AXI GPIO v2.0 Product
Guide".

1.4.2 Design Steps

This tutorial will be realized step by step with the idea to explain the whole procedure of designing an digital system, using
Vivado tool.

• First, we will create a MicroBlaze or Zynq-7000 AP SoC processor system project ("modulator") using Vivado IP
Integrator tool. The block diagram of this system is shown on the Illustration 1.3. Here we will configure the selected
microprocessor and peripherals, and specify the interconnections between these components.

10
1.5 Embedded Design Process Flow

• After we create modulator project using Vivado IP Integrator tool, we will perform synthesis, implementation and
bitstream file generation.

• Then, we will export our hardware platform description to the Software Development Kit (SDK). The exported file has
all the necessary information that SDK requires for software development and debug work on the hardware platform
that we designed.

• In the SDK, we will create and debug the software application for this project. There will be two different software
applications, one without and one with the interrupt controller. Source codes for these two applications will be stored
in modulator_no_intc.c and modulator_intc.c source file respectively.

• Now, the design is ready to be implemented. The last step will be to initialize the bitstream with the appropriate ELF
file and download it to the target Xilinx development board.

Figure 1.10: Design steps

1.5 Embedded Design Process Flow

Vivado Design Suite is designed to help us in all phases of the embedded design process. On the Illustration 1.11 is shown
the Vivado architecture structure of how the tools operate together to create an embedded system.

11
INTRODUCTION

Figure 1.11: Typical embedded design process flow

12
Chapter 2

CREATING THE HARDWARE PLATFORM

In the previous chapter, we have defined the structure of the microprocessor based system that will be used as a part of the
solution of PWM signal generation. In this chapter, we will explain how to generate this system using Vivado IP Integrator
tool. While entire designs can be created using the IP Integrator, the typical design will consist of HDL, IP and IP integrator
block designs.

2.1 Create a New Project

The first step in creating a new design will be to create a new project. We will crate a new project using the Vivado IDE
New Project wizard. The New Project wizard will create an XPR project file for us. It will be place where Vivado IDE will
organize our design files and save the design status whenever the processes are run.
To create a new project, follow these steps:
Step 1. Launch the Vivado software:
Select Start -> All Programs -> Xilinx Design Tools -> Vivado 2016.4 -> Vivado 2016.4 and the Vivado Getting
Started page will appear, see Illustration 2.1
As you can see from the illustration below, the Getting Started page contains a lot of usable links (shortcuts) like Cre-
ate New Project, Open an existing Project, Open Example Project, Open Hardware Manager, Documentation and
Tutorial and so on, see Illustration 2.1
Step 2. On the Getting Started page, choose Create New Project option

Figure 2.1: The Vivado Getting Started page


CREATING THE HARDWARE PLATFORM

Step 3. In the Create a New Vivado Project dialog box, click Next and the wizard will guide you through the process of a
new project creation, see Illustration 2.2

Figure 2.2: Create a New Vivado Project dialog box

Step 4. In the Project Name dialog box specify the name and the location of the new project:

• In the Project name field type modulator as the name of the project

• In the Project location field specify the location where project data will be stored

• Leave Create project subdirectory option enabled, see Illustration 2.3

Figure 2.3: Project Name dialog box

Step 5. Click Next


Step 6. In the Project Type dialog box specify the type of project you want to create. In our case we will choose RTL
Project option. Select Do not specify sources at this time also, see Illustration 2.4

14
2.1 Create a New Project

Figure 2.4: Project Type dialog box

• RTL Project - The RTL Project environment enables you to add RTL source files and constraints, configure IP with
the Vivado IP catalog, create IP subsystems with the Vivado IP integrator, synthesize and implement the design, and
perform design planning and analysis.

• Post-synthesis Project - This type of project enables you to import third-party netlists, implement the design, and
perform design planning and analysis.

• I/O Planning Project - With this type of project you can create an empty project for use with early I/O planning
and device exploration prior to having RTL sources.∗Imported Project∗ - This type of project enables you to import
existing project sources from the ISE Design Suite, Xilinx Synthesis Technology (XST), or Synopsys Synplify.

• Example Project - This type of project enables you to target the example Zynq-7000 or MicroBlaze embedded
designs to the available Xilinx evaluation boards.

Step 7. Click Next


Step 8. In the Default Part dialog box choose a default Xilinx part or board for your project. Select Boards to choose the
default board for the project and a list of evaluation boards will be displayed, see Illustration 2.5

15
CREATING THE HARDWARE PLATFORM

Figure 2.5: Default Part dialog box

Step 9. Select ZedBoard Zynq Evaluation and Development Kit as it is shown on the illustration above
Step 10. Click Next
Step 11. In the New Project Summary dialog box click Finish if you are satisfied with the summary of your project. If you
are not satisfied, you can go back as much as necessary to correct all the questionable issues, see Illustration 2.6

Figure 2.6: New Project Summary dialog box

After we finished with the new project creation, in a few seconds Vivado IDE Viewing Environment will appear, see
Illustration 2.7.
When Vivado creates new project, it also creates a directory with the name and at the location that we specified in the
GUI (see Illustration 2.2). That means that the all project data will be stored in the project_name (modulator) directory
containing the following:

• project_name.xpr file - object that is selected to open a project (Vivado IDE project file)

16
2.2 Vivado Integrated Design Environment

• project_name.runs directory - contains all run data

• project_name.srcs directory - contains all imported local HDL source files, netlists, and XDC files

• project_name.data directory - stores floorplan and netlist data

• project_name.sim directory - contains all simulation data

Figure 2.7: Vivado IDE Viewing Environment

2.2 Vivado Integrated Design Environment

The Vivado IDE can be used for a variety of purposes at various stages in the design flow and is very helpful at detecting
design problems early in the design flow.
The Vivado IDE allows different file types to be added as design sources, including Verilog, VHDL, EDIF, NGC format cores,
SDC, XDC, and TCL constraints files, and simulation test benches. These files can be stored in variety of ways using the
tabs at the bottom of the Sources window: Hierarchy , Library or Compile Order , see Illustration 2.8.
By default, after launching, the Vivado IDE opens the Default Layout. Each docked window in the Vivado IDE is called a
view, so you can find Sources View, Properties View, Project Summary View ans so on, see Illustration 2.8.

17
CREATING THE HARDWARE PLATFORM

Figure 2.8: Vivado IDE Default Layout

Flow Navigator
The vertical toolbar present on the left side of the Vivado IDE is the Flow Navigator . The Flow Navigator provides control
over the major design process tasks, such as project configuration, synthesis, implementation and bitstream creation.
Sources View
The Sources view displays the list of source files that has been added in the project.

• The Design Sources folder helps you keep track of VHDL and Verilog design source files and libraries.

• The Constraints folder helps you keep track of the constraints files.

• The Simulation Sources folder helps keep track of VHDL and Verilog simulation sources source files and libraries.

Notice that the design hierarchy is displayed as default.

• In the Libraries tab, sources are grouped by file type, while the Compile Order tab shows the file order used for
synthesis.

Project Summary View


The Project Summary view provides a brief overview of the status of different processes executed in the Vivado IDE, see
Illustration 2.9.

18
2.3 Create MicroBlaze-based hardware platform

Figure 2.9: Project Summary View

The Project Settings panel displays the project name, product family, project location, project part, and top module name.
Clicking a link in this panel you will open the Project Settings dialog box.

• The Messages panel summarizes the number of errors and warnings encountered during the design process.

• The Synthesis panel summarizes the state of synthesis in the active run. The synthesis panel also shows the target
part and the strategy applied in the run.

• The Implementation panel summarizes the state of implementation in the active run. The Implementation panel
also shows the target part and the strategy applied in the run.

Tcl Console
Below the Project Summary view, see Illustration 2.8, is the Tcl Console which echoes the Tcl commands as operations
are performed. It also provides a means to control the design flow using Tcl commands.

2.3 Create MicroBlaze-based hardware platform

To accelerate the creation of highly integrated and complex designs, Vivado Design Suite is delivered with IP Integrator
(IPI) which provides a new graphical and Tcl-based IP- and system-centric design development flow.
Rapid development of smarter systems requires levels of automation that go beyond RTL-level design. The Vivado IP
Integrator accelerates IP- and system-centric design implementation by providing the following:

• Seamless inclusion of IPI sub-systems into the overall design

• Rapid capture and packing of IPI designs for reuse

• Tcl scripting and graphical design

• Rapid simulation and cross-probing between multiple design views

• Support for processor or processor-less designs

19
CREATING THE HARDWARE PLATFORM

• Integration of algorithmic and RTL-level IP

• Combination of DSP, video, analog, embedded, connectivity and logic

• Matches typical designer flows

• Easy to reuse complex sub-systems

• DRCs on complex interface level connections during design assembly

• Recognition and correction of common design errors

• Automatic IP parameter propagation to interconnected IP

• System-level optimizations

The Xilinx Vivado Design Suite IP Integrator feature lets you create complex system designs by instantiating and intercon-
necting IP cores from the Vivado IP Catalog onto a design canvas.
You can create designs interactively through the IP Integrator design canvas GUI, or using a Tcl programming interface.
You will typically construct design at the AXI interface level for greater productivity, but you may also manipulate designs at
the port level for more precise design control.
In this tutorial you will instantiate a few IPs in the IP Integrator tool and then stitch them together to create an IP based
system design. While working on this tutorial, you will be introduced to the IP Integrator GUI, run design rule checks
(DRC) on your design, and then integrate the design in a top-level design in the Vivado Design Suite. Finally, you will run
synthesis and implementation process, generate bitstream file and run your design on the ZedBoard Zynq evaluation and
development board.
The following steps describe how to use the IP Integrator within your project:
Step 1. In the Flow Navigator , under the IP Integrator, select Create Block Design command, see Illustration 2.10

Figure 2.10: Create Block Design option

Step 2. In the Create Block Design dialog box, specify a name for your IP subsystem design in the Design name field
(in our case it will be modulator_mb), leave Directory field set to the default value of Local to Project and click OK , see
Illustration 2.11

Figure 2.11: Create Block Design dialog box

The Vivado IDE will display a blank design canvas. You can quickly create complex subsystem by integrating IP cores in it,
see Illustration 2.12

20
2.3 Create MicroBlaze-based hardware platform

Figure 2.12: Vivado IDE with a blank design canvas

Step 3. The modulator_mb design is empty. To get started, add IPs from the IP Catalog. You can do that on three ways:

• In the design canvas, right-click and choose Add IP... option, see Illustration 2.13, or

Figure 2.13: Add IP option

• Use the Add IP link in the IP Integrator canvas, see Illustration 2.14, or

Figure 2.14: Add IP link

21
CREATING THE HARDWARE PLATFORM

• Click on the Add IP button in the IP Integrator sidebar menu, see Illustration 2.15

Figure 2.15: Add IP button

Step 4. In the IP Catalog , search for the MicroBlaze core, see Illustration 2.16

Figure 2.16: MicroBlaze core in the IP Catalog

Step 5. When you find it, press enter on the keyboard or simply double- click on the MicroBlaze (microblaze_0) core in
the IP Catalog and the selected core will be automatically instantiated into the IP Integrator design canvas, see Illustration
2.17

22
2.3 Create MicroBlaze-based hardware platform

Figure 2.17: Automatically instantiated MicroBlaze core in the IP Integrator design canvas

Step 6. Right-click in the IP integrator canvas and select the Add IP... option to add the rest of the necessary IPs:

• AXI Timer (axi_timer_0)

• AXI GPIO (axi_gpio_0) to drive the LED and to read the status of the SWITCH

Note : We will not add AXI Interrupt Controller (axi_intc_0) core at this stage of the design. It will be added later as a
basic MicroBlaze feature.
Step 7. At this point, the IP Integrator canvas should look like as it is shown on the Illustration 2.18

Figure 2.18: IP Integrator design canvas with instantiated IPs

Step 8. In the IP Integrator window, click the Run Block Automation link, see Illustration 2.18
As you can see from the Illustration 2.18, beside the Run Block Automation link is one more link, Run Connection
Automation . Both of the links are IP Integrator features that assist you in putting together a basic microprocessor system,
making internal connections between different blocks and making connections to external interfaces.

23
CREATING THE HARDWARE PLATFORM

The Block Automation feature is provided when a microprocessor (such as MicroBlaze processor) is instantiated in the IP
Integrator block design. When you click on the Run Block Automation link, you will get assistance with putting together a
simple MicroBlaze system.
Step 9. In the Run Block Automation dialog box

• change the Local Memory size from 8 KB to 128 KB

• enable Interrupt Controller

• leave all other parameters unchanged

Figure 2.19: Run Block Automation dialog box

As you can see Run Block Automation dialog box allows you to provide input about basic features that the microprocessor
system needs, among other things, there is Interrupt Controller core. That was the reason why we didn’t add it earlier in
the design, in step 6.
Step 10. When you finished with the Run Block Automation settings, click OK
Ones you specify the necessary options, the Block Automation feature automatically creates a basic system design, see
Illustration 2.20

24
2.3 Create MicroBlaze-based hardware platform

Figure 2.20: A basic system design created by the Block Automation feature

In our case, a basic MicroBlaze system consists of:

• mdm_1 block - MicroBlaze Debug Module (MDM)

• microblaze_0_xlconcat block - Concat

• clk_wiz_1 block - Clocking Wizard

• rst_clk_wiz_1_100M block – Processor System Reset

• microblaze_0_local_memory block – a hierarchical block that has the Local Memory Bus, the Local Memory Bus
Controller and the Block Memory Generator

• microblalze_0_axi_intc block – AXI Interrupt Controller

• microblaze_0_axi_periph block – AXI Interconnect

Since the design is not connected to any external I/O at this point, IP Integrator tool provides the Connection Automation
feature. With this feature you will get assistance in hooking interfaces and/or ports to external I/O ports.
Step 11. In the IP Integrator window, click the Run Connection Automation link and the list of the ports/interfaces that
can use the Connection Automation feature will show up, see Illustration 2.21

25
CREATING THE HARDWARE PLATFORM

Figure 2.21: Run Connection Automation dialog box

As you can see from the illustration above, the design knows the FPGA pins that are connected or used on the target
boards. Based on that information, the IP Integrator tool with connection automation feature can assist you in connection
the ports in the design to external ports. IP Integrator tool then creates the appropriate physical constraints and other IO
constraints required for the IO port in question.
Step 12. Before we use Connection Automation feature, we will re-customize AXI GPIO (axi_gpio_0) and Clocking
Wizard (clk_wiz_1) block.
Step 13. Double-click on the AXI GPIO (axi_gpio_0 ) block and in the Board tab make the following changes:

• associate GPIO IP Interface with leds 8bits board interface, and

• associate GPIO2 IP Interface with sws 8bits board interface

• Click OK

This is important, because each AXI GPIO IP has two registers. In our example, one will be used for LEDs and the second
one will be used for SWITCHes.

26
2.3 Create MicroBlaze-based hardware platform

Figure 2.22: Re-customize IP - AXI GPIO (2.0) dialog box

Step 14. Double-click on the Clocking Wizard (clk_wiz_1) block and in the Clocking Options tab change the Source of
the Primary Input Clock to be Single ended clock capable pin instead of Differential clock capable pin and click OK,
see Illustration 2.23

27
CREATING THE HARDWARE PLATFORM

Figure 2.23: Re-customize IP - Clocking Wizard (5.1) dialog box

Step 15. Click the Run Connection Automation link and in the Run Connection Automation dialog box select the All
Automation (0 out of 7 selected) check box.
As you select each interface for which connection automation is to be run, the description and options available for that
interface appear in the right pane.
Step 16. Select the axi_gpio_0 -> GPIO interface and you will see that the GPIO interface of the AXI GPIO block is
automatically connected to the LEDs on the board, see Illustration 2.24

28
2.3 Create MicroBlaze-based hardware platform

Figure 2.24: Run Connection Automation dialog box for axi_gpio_0 -> GPIO interface

Step 17. Select the next axi_gpio_0 -> GPIO2 interface and check is the sws_8bits option selected as Board Part
Interface
Step 18. Select the next axi_gpio_0 -> S_AXI interface and leave selected Auto option for Clock Connection (for
unconnected clks)
This dialog box informs you that the slave AXI port of the GPIO can be connected to the MicroBlaze master. If there are
multiple masters in the design, then you will have a choice to select between different masters.
If you click OK in the Run Connection Automation dialog box, the connections will be made and highlighted in the IP
Integrator design canvas.
Step 19. For axi_timer_0 -> S_AXI interface, leave selected Auto option for Clock Connection (for unconnected clks)
Step 20. For clk_wiz_1 -> clk_in1 port, leave selected sys_clock option selected as Board Part Interface
Step 21. For clk_wiz_1 -> reset port, leave selected ACTIVE_HIGH option as Selected Reset Polarity
Step 22. For rst_clk_wiz_1_100M -> ext_reset_in port, leave selected ACTIVE_LOW option as Selected Reset Polarity
Step 23. Click OK
Now, when we have connected all the ports/interfaces offered by the Connection Automation wizard, we will manually
connect the rest of the ports/interfaces
Step 24. First, remove Concat (microblaze_0_xlconcat) block from the IP Integrator design canvas, because in our
design there is only one interrupt source (axi_timer_0)
To remove block from the IP Integrator design canvas, it is enough to select desired block and click Delete button on your
keyboard
Step 25. Connect the interrupt port of the axi_timer_0 block to the intr[0:0] port of the microblaze_0_axi_intc block
Place the cursor on top of the desired pin and you can notice that the cursor changes into a pencil indicating that a
connection can be made from that pin. Clicking the left mouse button a connection starts. Click and drag the cursor from
one pin to another. You must press and hold down the left mouse button while dragging the connection from one pin to
another. As you drag the connection wire, a green checkmark appears on the port indicating that a valid connection can be
made between these points. The Vivado IP Integrator highlights all possible connections points in the subsystem design
as you interactively wire the pins and ports. Release the left mouse button and Vivado IP integrator makes connection
between desired ports. Repeat this procedure until all the pins become associated.
Step 26. Right-click in the IP integrator canvas and select the Add IP... option to add the last IP block necessary for our
design. Search for the Constant (xlconstant_0 ) core and add it into our design.

29
CREATING THE HARDWARE PLATFORM

Step 27. Double-click on the Constant block and set the constant value, Const Val, to be 0 and click OK , see Illustration
2.25.
This step is important because we have to connect the unconnected input ports of the AXI Timer (axi_timer_0) block
(capturetrig0 , capturetrig1 and freeze) to GND, because we won’t need them in our design. Using the Constant block is
one possible way how to solve this problem.

Figure 2.25: Re-customize IP - Constant (1.1) dialog box

Step 28. Connect the capturetrig0, capturetrig1 and freeze ports of the AXI Timer (axi_timer_0) block to the dout[0:0]
port of the new Constant (xlconstnt_0) block
Step 29. Also connect the aux_reset_in port of the Processor System Reset (rst_clk_wiz_1_100M) to the dout[0:0]
port of the Constant (xlconstnt_0) block
Step 30. Leave generateout0, generateout1 nd pwm output ports of the AXI Timer (axi_timer_0) block unconnected
Step 31. The last step in our block design creation will be to re- customize Processor System Reset block. Double-click
on the Processor System Reset (rst_clk_wiz_1_100M) block.
Step 32. In the Processor System Reset (5.0) dialog box, change the Auxillary Reset option from Auto to Manual and
click OK, see Illustration 2.26

30
2.3 Create MicroBlaze-based hardware platform

Figure 2.26: Re-customize IP - Processor System Reset (5.0) dialog box

When we have connected all the necessary ports/interfaces and after we have customized all the necessary blocks in our
design, see Illustration 2.27, it is time to validate it.
Before validation, click on the Regenerate Layout button on the sidebar menu to re-arrange IP blocks on the design
canvas, to get a better view of the block design. You can re-arrange a completed diagram or a diagram in progress.
The optimized layout of the design should look similar to layout that is shown on the Illustration 2.27.

Figure 2.27: Final Block Diagram of our design

Step 33. From the sidebar menu of the design canvas, run the IP subsystem design rule checks by clicking the Validate
Design button
Alternatively, you can do the same by selecting Tools -> Validate Design from the main menu, see Illustration 2.28, or

31
CREATING THE HARDWARE PLATFORM

Figure 2.28: Validate Design option from the main menu

by clicking the design canvas and selecting Validate Design button from the main toolbar menu, see Illustration 2.29

Figure 2.29: Validate Design button from the main toolbar menu

Step 34. ln the Validate Design dialog box, click OK , see Illustration 2.30

Figure 2.30: Validate Design dialog box

Step 35. At this point, you should save the IP integrator design. Use the File -> Save Block Design command from the
main menu to save the design.
Step 36. In the Vivado Flow Navigator, under the Project Manager, click on the Project Settings command and in the
Project Settings dialog box check is the Target language set to VHDL. If it is not, please change it to be VHDL, see
Illustration 2.31.

32
2.3 Create MicroBlaze-based hardware platform

Figure 2.31: Project Settings dialog box

Step 37. In the Sources window, select modulator_mb, right-click on it and choose Create HDL Wrapper... option, see
Illustration 2.32

Figure 2.32: Create HDL Wrapper option

Step 38. In the Create HDL Wrapper dialog box, select Let Vivado manage wrapper and auto-update option and click
OK, see Illustration 2.33

Figure 2.33: Create HDL Wrapper dialog box

33
CREATING THE HARDWARE PLATFORM

Step 39. After the HDL wrapper is generated, you should see it in the Sources window, see Illustration 2.34

Figure 2.34: Sources window with generated HDL wrapper

Step 40. Double-click on the modulator_mb_wrapper - STRUCTURE (modulator_mb_wrapper.vhd) to see how Vivado
IDE automatically generated .vhd file based on our IP Integrator block design (shown on the Illustration 2.27), see Illustration
2.35

Figure 2.35: modulator_mb_wrapper.vhd source file

Step 41. Using automatically generated modulator_mb_wrapper.vhd file as a reference, we will create additional
modulator_wrapper.vhd file to illustrate how a microprocessor-based module can be used within a larger design

34
2.3 Create MicroBlaze-based hardware platform

Step 42. Create modulator_wrapper.vhd file based on the block diagram shown on the Illustration 2.36

Figure 2.36: Block diagram of the modulator_wrapper.vhd file

modulator_wrapper.vhd file :

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

library unisim;
use unisim.vcomponents.all;

use work.modulator_pkg.all;

entity modulator_wrapper is
generic(
-- Parameter that specifies major characteristics of the board that will be used
-- to implement the modulator design
-- Possible choices: """lx9""", """zedboard""", """ml605""", """kc705""", """microzed""", ""socius"
""
-- Adjust the modulator_pkg.vhd file to add more
board_name_g : string := """zedboard"""
);

port (
clk_p : in std_logic; -- differential input clock signal
clk_n : in std_logic; -- differential input clock signal
sw0 : in std_logic; -- signal made for selecting frequency
pwm_out: out std_logic -- pulse width modulated signal
);
end;

architecture structure of modulator_wrapper is

signal clk_in_s : std_logic;

begin
-- if module is top, it has to generate the differential clock buffer in case
-- of a differential clock, otherwise it will get a single ended clock signal
-- from the higher hierarchy

clk_buf : if (get_board_info_f(board_name_g).has_diff_clk = yes) generate


IBUFGDS_inst : IBUFGDS
generic map(
IBUF_LOW_PWR => TRUE,
-- low power (TRUE) vs. performance (FALSE) setting for referenced I/O standards
IOSTANDARD => "DEFAULT"
)

port map (
O => clk_in_s, -- clock buffer output
I => clk_p, -- diff_p clock buffer input
IB => clk_n -- diff_n clock buffer input
);
end generate clk_buf;

no_clk_buf : if (get_board_info_f(board_name_g).has_diff_clk = no) generate


IBUFG_inst : IBUFG
generic map (
IBUF_LOW_PWR => TRUE,
-- Low power (TRUE) vs. performance (FALSE) setting for referenced I/O standards
IOSTANDARD => "DEFAULT")

port map (
O => clk_in_s, -- Clock buffer output
I => clk_p -- Clock buffer input (connect directly to top-level port)
);
end generate no_clk_buf;

mb_wrapper : entity work.modulator_mb_wrapper(structure) -- modulator_mb_wrapper instance


port map(

35
CREATING THE HARDWARE PLATFORM

sys_clock => clk_in_s,


reset_rtl => ’0’, -- external reset logic level = 1
reset_rtl_0 => ’1’, -- external reset logic level = 0
sws_8bits_tri_i (7 downto 1) => x"00",
sws_8bits_tri_i (0) => sw0,
leds_8bits_tri_o (7 downto 1) => open,
leds_8bits_tri_o (0) => pwm_out
);
end;

Step 43. When we finished with the modulator_wrapper.vhd file creation, in the Vivado Flow Navigator, click Add Sources
command, see Illustration 2.37

Figure 2.37: Add Sources command

Step 44. In the Add Sources dialog box, select Add or create design sources option to add the design source files into
the project and click Next, see Illustration 2.38

Figure 2.38: Add Sources dialog box - add or create design sources option

Step 45. In the Add or Create Design Sources dialog box, click the + icon and select Add Files... option to include the
existing source files into the project, see Illustration 2.39

36
2.3 Create MicroBlaze-based hardware platform

Figure 2.39: Add or Create Design Sources dialog box

Step 46. In the Add Source Files dialog box, browse to the project working directory and select the modulator_wrapper.-
vhd and modulator_pkg.vhd source files, see Illustration 2.40

Figure 2.40: Add Source Files dialog box

Step 47. Click OK and the modulator_wrapper.vhd and modulator_pkg.vhd source files should appear in the Add or
Create Design Sources dialog box, as it is shown on the Illustration 2.41

37
CREATING THE HARDWARE PLATFORM

Figure 2.41: Add or Create Design Sources dialog box - with added files

Step 48. Click Finish and your source files should appear under the Design Sources in the Sources view in the Project
Manager window, see Illustration 2.42

Figure 2.42: Sources view with new wrapper file

Note: You can see that Vivado IDE automatically recognized new wrapper file (modulator_wrapper.vhd) as a top module
file.
Step 49. In the Vivado Flow Navigator, click Run Synthesis command (Synthesis option) and wait for task to be
completed
Note : You can monitor the Synthesis progress in the bar in the upper-right corner of the Vivado IDE.
Step 50. After the synthesis is completed, the Synthesis Completed dialog box will appear. Select Open Synthesized
Design and click OK
Step 51. The last step in our hardware design will be to crate and add modulator_mb.xdc constraints file. To create a
XDC file using the Vivado IDE GUI, do the following:
Step 52. Change the layout from the Default Layout to I/O Planning view, in the layout pull-down menu in the main
toolbar, to identify pins that don’t have an assigned location, see Illustration 2.43

38
2.3 Create MicroBlaze-based hardware platform

Figure 2.43: IO Planning layout

This will change the layout from the Default view to the I/O Planning view, see Illustration 2.44
The main window of the I/O Planning view displays the package view of the ZedBoard Zynq device. Below the Package
view, two additional tabs are populated. One tab displays the list of I/O ports of the design and the second tab displays the
list of package pins on the device package.

Figure 2.44: I/O Planning View

Step 53. In the I/O Ports tab, click Expand All option, or just expand All ports , which shows all I/O Ports of your design
Grey icons indicate unplaced ports, while yellow icons indicate placed ports. After we assign a pin location to each of the
I/O ports they will be colored yellow, as can be seen on the Illustration 2.45.

39
CREATING THE HARDWARE PLATFORM

Figure 2.45: I/O Ports tab with assigned pin locations and I/O standards

On the Illustration 2.45 you can see that Vivado IDE automatically assigned pin locations and I/O standards according
to the selected development board. If you are satisfied with the automatically generated placement constraints, please
proceed with the defining timing constrains (step 64). If you would like to change automatically assigned pin locations or
I/O standards, please follow the next steps.
Step 54. To connect your logical with your physical ports, select one scalar port (for example sw0) and find in the Zed-
Board (Zynq Evaluation and Development) Hardware User’s Guide to which pin location you would like to connect your sw0
port. In our design we should connect sw0 port with one of the SWITCHes that are physically present on the ZedBoard
evaluation board. If you open ZedBoard user guide, "User I/O" section, you can find that the FPGA pin location of the SW0
switch is F22 and that the I/O standard that must be used is LVCMOS25.
LVCMOS25 is a low voltage CMOS I/O standard using 2.5V power supply voltage. For more information about this I/O
standard, please refer to the "JEDEC Standard JESD8-5A.01, 2.5 V +/- 0.2 V (Normal Range) and 1.8 V - 2.7 V (Wide
Range) Power Supply Voltage and Interface Standard for Nonterminated Digital Integrated Circuits " standard.
Step 55. In the I/O Ports tab, click on the sw0’s Site column and choose F22 as a pin location to connect the sw0 port
Step 56. Click on the sw0’s I/O Std column and change the I/O standard to be LVCMOS25
Step 57. Leave all the other sw0’s options unchanged, because they are default values
Note: After assigning pin location and I/O standard for sw0 port, we can notice that Properties window popped up. This
is the another way to change port properties, see Illustration 2.46. If you want to apply some changes that you made, just
click the Automatically update button.

Figure 2.46: Properties window

Step 58. Repeat these configuration steps for the remaining ports using the pin locations and necessary I/O standards
information shown below:

• pwm_out - pin location: T22 , I/O standard: LVCMOS33

• clk_p - pin location: Y9 , I/O standard: LVCMOS33

Note: All this information has been extracted from the ZedBoard (Zynq Evaluation and Development) Hardware User’s
Guide.

40
2.3 Create MicroBlaze-based hardware platform

LVCMOS33 is a low voltage CMOS I/O standard using 3.3V power supply voltage. For more information about this I/-
O standard, please refer to the "JEDEC Standard JESD8C.01, Interface Standard for Nominal 3 V/3.3 V Supply Digital
Integrated Circuits " standard.
Step 59. When you are finished with the placement constraints, click File -> Save Constraints As...
Step 60. In the Save Constraints dialog box, type the name of the constraints file in the File name field and click OK. In
our case, the name will be modulator_wrapper.
Step 61. In the Save Constraints As dialog box, type the name of the constraint set in the New Constraints set name
field. In our case, the name will be modulator_mb.
Step 62. Click OK and your modulator_mb constraint set with modulator_wrapper.xdc file should appear in the Sources
window under the Constraints , see Illustration 2.47

Figure 2.47: Created modulator_wrapper constraints file in the Sources window

Step 63. If you want to see what the tool has created for us, double-click on the modulator_wrapper.xdc file to open it,
see Illustration 2.48

Figure 2.48: modulator_wrapper.xdc file with physical constraints

In the modulator_wrapper.xdc constraints file you can see assigned pin locations and I/O standards for each logical port
of our design. For each logical port two constraints are necessary:

• First constraint connects selected logical port (by using get_ports Tcl command) with specified pin location (by setting
the PACKAGE_PIN property, using set_property Tcl command).

41
CREATING THE HARDWARE PLATFORM

• Second constraint sets the I/O standard that should be used for selected logical port by setting the IOSTANDARD
property, using set_property Tcl command.

Prior to implementation process, there are physical and timing constraints that need to be defined. In the previous steps
we have defined physical constraints. Now, it’s time to define timing constraints also.
To define timing constraints, follow the next steps:
Step 64. Select Window -> Timing Constraints option to open the Timing Constraints window, see Illustration 2.49

Figure 2.49: Timing Constraints option

The another way to open the Timing Constraints window is to click Edit Timing Constraints command under the Syn-
thesis in the Flow Navigator.
The Timing Constraints window will appear in the main window of the Vivado IDE, see Illustration 2.50.

42
2.3 Create MicroBlaze-based hardware platform

Figure 2.50: Timing Constraints window

Now we will define the primary clock constraint by creating a clock object with a specified period. The modulator design
has a 100 MHz clock supplied through differential clock input ports on the FPGA. You will first define the primary clock
object for the design and then define a PERIOD constraint for the clock object.
Step 65. In the Timing Constraints window, double-click on the Create Clock (3) option under the Clocks (7) section to
create a clock constraint
Step 66. In the Create Clock dialog box, enter clock_name (clk_p) in the Clock name field, see Illustration 2.51

43
CREATING THE HARDWARE PLATFORM

Figure 2.51: Create Clock dialog box

Step 67. Click the icon next to the Source objects field and Specify Clock Source Objects dialog box will appear, see
Illustration 2.52
Note: This step is important to associate the clock input port to the clock definition.
Step 68. In the Specify Clock Source Objects dialog box (see Illustration 2.52), do the following:

• Ensure that I/O Ports is selected from the Find names of type drop-down list

• Enter clk in the empty search field

• Click Find

• In the Find results: 2 section, select clk_p

• Click the -> icon to select clk_p

• Click Set

44
2.3 Create MicroBlaze-based hardware platform

Figure 2.52: Specify Clock Source Objects dialog box

Step 69. In the Create Clock dialog box, specify the period by setting the period property of the clock. In this step, you will
describe the period property and review the waveform details of the clock objects, see Illustration 2.53:

• Enter 10 ns in the Period field in the Waveform section, because 10 ns is the period of the 100 MHz input clock
signal

• Ensure that the Rise at and Fall at fields are set to 0 and 2.5 respectively, which means that the duty cycle of the
input clock signal will be 50%

• Click OK to create the clock constraint

45
CREATING THE HARDWARE PLATFORM

Figure 2.53: Create Clock dialog box after specifying the period for the clk_p

The Timing Constraints window now displays the timing constraint applied to the design, see Illustration 2.54

46
2.3 Create MicroBlaze-based hardware platform

Figure 2.54: Timing Constraints window with the create_clock constraint

Notice that the create_clock XDC command for the created clock is also displayed in the All Constraints view of the Timing
Constraints window.
All the timing constraints that have been run are applied to the design that is loaded in the memory. The applied constraints
can be saved by writing them to the XDC file. All the timing constraints applied to the design are available in the All
Constraints view of the Timing Constraints window, see Illustration 2.54.
Step 70. To save your timing constraints to the modulator_wrapper.xdc constraints file, select File -> Save Constraints
Note: If you didn’t change automatically generated placement constraints and you directly proceed with timing constraints
generation, you have to repeat 59, 60, 61 and 62 steps from this chapter to create modulator_wrapper.xdc constraints
file.
If you want to verify that the timing constraints have been applied to the modulator_wrapper.xdc file, double-click on the
modulator_wrapper.xdc file and you should see that your timing constraints were saved to the XDC file, see Illustrations
2.55.
Note: If you didn’t change automatically generated placement constraints and you directly proceed with timing constraints
generation, in the modulator_wrapper.xdc constraints file you will find only generated timing constraints.

47
CREATING THE HARDWARE PLATFORM

Figure 2.55: modulator_wrapper.xdc file with physical and timing constraints

In the modulator_wrapper.xdc file you will see four blocks of commands, see Illustration 2.55. First three blocks (first six
lines) are the Physical Constraints and the last line is the Timing Constraint.
Important: The another way to create your modulator_wrapper.xdc constraints file is using Vivado text editor:

1. In the main Vivado IDE menu, click File -> New File... option to open Vivado text editor

2. In the New File dialog box, type the name of your constraints file (constraints_file_name.xdc) in the File name field
and choose to save it into your working directory

3. When you click Save, Vivado IDE will automatically open empty constraints file in Vivado text editor

4. Write the constraints in the text file

5. When you finish with constraints creation, click File -> Save File option from the main Vivado IDE menu, or just click
Ctrl + S to save it

6. In the Vivado Flow Navigator, click the Add Sources command

7. In the Add Sources dialog box select Add or create constraints option to add the constraints file to the project and
click Next

8. In the Add or Create Constraints dialog box, click + icon and select Add Files... option

9. In the Add Constraint Files dialog box, browse to the project working directory and select the modulator_wrapper
.xdc constraints file

10. Click OK and the modulator_wrapper.xdc constraints file should appear in the Add or Create Constraints dialog
box

11. Click Finish and your constraints file should appear under the Constraints in the Sources view

To prepare our design to work in the FPGA device, we must convert it to a bitstream file. This process is composed of
two important steps. One is to generate a netlist file that will represent your hardware platform and the second one is
to generate a bitstream file that will represent the hardware and software platforms together. This bitstream file will be
downloaded to the FPGA device.
There are two possibilities for netlist and bitstream file generation. One is to generate these files after a hardware platform
is specified and the second one is to generate them after a software application development is completed.
If you would like to generate netlist and bitstream file after hardware platform specification:
Step 71. In the Vivado Flow Navigator , click Run Synthesis command, see Illustration 2.56, and wait for task to be
completed

48
2.4 Create ARM-based hardware platform

Step 72. When the synthesis process is completed, click Run Implementation command, see Illustration 2.56, and wait
for task to be completed
Step 73. At the end, when the implementation process is completed, click Generate Bitstream command, see Illustration
2.56After this step, bitstream file will be generated.

Figure 2.56: Run Synthesis, Run Implementation and Generate Bitstream commands from the Vivado Flow Navigator

If you would you like to generate netlist and bitstream files after a software application development is completed, please
follow the next chapter where will be explained in detail the necessary steps.

2.4 Create ARM-based hardware platform

As we already said at the beginning of this tutorial, we will create two hardware platforms, one MicroBlaze processor-based
and the other, ARM processor-based hardware platform. Both of the platforms are based on the same to show how one
problem can be solved using different microprocessors.
This sub-chapter will show how to build Zynq-7000 All Programmable (AP) SoC processor modulator design using Vivado
IDE.
In this sub-chapter, like in the previous, you will instantiate a few IPs in the IP Integrator tool and then stitch them together to
create an IP based system design. At the end, you will run synthesis and implementation process and generate bitstream
file.
The following steps describe how to create ARM-based hardware platform:
Step 1. Create a new project (modulator_arm) using the Vivado IDE wizard. To create a new project, please repeat steps
- from sub-chapter 2.1 "Create a New Project"
Step 2. In the Flow Navigator, under the IP Integrator , select Create Block Design command
Step 3. In the Create Block Design dialog box, specify a name for your IP subsystem design in the Design name field
(in our case it will be modulator_arm), leave Directory field set to the default value of Local to Project and click OK, see
Illustration 2.57

49
CREATING THE HARDWARE PLATFORM

Figure 2.57: Create Block Design dialog box

Step 4. In the empty design canvas, right-click and choose Add IP... option
Step 5. In the IP Catalog, type zynq to find the ZYNQ7 Processing System core, see Illustration 2.58

Figure 2.58: Zynq7 Processor core in the IP Catalog

Step 6. When you find it, select it and press enter on the keyboard or simply double-click on the ZYNQ7 Processing
System core in the IP Catalog and the selected core will be automatically instantiated into the IP Integrator design canvas,
see Illustration 2.59

Figure 2.59: Automatically instantiated Zynq7 Processor core in the IP Integrator design canvas

Step 7. Right-click in the IP integrator canvas and select the Add IP... option to add the rest of the necessary IPs:

50
2.4 Create ARM-based hardware platform

• AXI Timer (axi_timer_0)

• AXI GPIO (axi_gpio_0)

Note: We will not add AXI Interrupt Controller (axi_intc_0) core at this stage of the design. It will be added later as a
Zynq7 Processor System core feature.
Step 8. At this point, the IP Integrator canvas should look like as it is shown on the Illustration 2.60

Figure 2.60: IP Integrator design canvas with all three instantiated IPs

Step 9. In the IP Integrator window, click the Run Block Automation link
Step 10. The Run Block Automation dialog box opens, stating that the FIXED_IO, Trigger and DDR interfaces will be
created for the Zynq-7000 AP SoC core, see Illustration 2.61.
Also, Apply Board Preset check box should be checked, because the selected target board is ZC702. Make sure that
both Cross Trigger In and Cross Trigger Out options are disabled.
Step 11. Click OK

51
CREATING THE HARDWARE PLATFORM

Figure 2.61: Zynq-7 Run Block Automation dialog box

After running block automation on the Zynq-7000 AP SoC processor, the IP integrator diagram should look as follows:

Figure 2.62: Zynq-7000 AP SoC Processing System after Running Block Automation

Step 12. Before we use Connection Automation feature, we will re-customize AXI GPIO (axi_gpio_0) block.
Step 13. Double-click on the AXI GPIO (axi_gpio_0) block in the Board tab make the following changes:

52
2.4 Create ARM-based hardware platform

• associate GPIO IP Interface with leds 8bits board interface, and

• associate GPIO2 IP Interface with sws 8bits board interface

• click OK

This is important, because each AXI GPIO IP has two registers. In our example, one will be used for LEDs and the second
one will be used for SWITCHes.

Figure 2.63: Re-customize IP - AXI GPIO (2.0) dialog box

Step 14. Click the Run Connection Automation link and in the Run Connection Automation dialog box select the All
Automation (0 out of 4 selected) check box.
As you select each interface for which connection automation is to be run, the description and options available for that
interface appear in the right pane.
Step 15. Select the axi_gpio_0 -> GPIO interface and you will see that the GPIO interface of the AXI GPIO block is
automatically connected to the LEDs on the board
Step 16. Select the next axi_gpio_0 -> GPIO2 interface and check is the sws_8bits option selected as Board Part
Interface
Step 17. Select the next axi_gpio_0 -> S_AXI interface and leave selected Auto option for Clock Connection (for
unconnected clks)
Step 18. For axi_timer_0 -> S_AXI interface, leave selected Auto option for Clock Connection (for unconnected clks)
Step 19. When you click OK in the Run Connection Automation dialog box, the connections will be made and highlighted
in the IP Integrator design canvas, see Illustration 2.64

53
CREATING THE HARDWARE PLATFORM

Figure 2.64: Zynq-7000 AP SoC Processing System after Running Connection Automation

Now, when we have connected all ports/interfaces offered by the Connection Automation wizard, we will manually connect
the rest of the ports/interfaces
Step 20. Double-click on the ZYNQ7 Processing System (processing_systam7_0) block to re-customize it
Step 21. In the ZYNQ7 Processing System (5.5) dialog box, select Interrupts in the Page Navigator window. In the
Interrupts window:

• enable and expand Fabric Interrupts

• under the Fabric Interrupts, expand PL-PS Interrupt Ports

• under the PL-PS Interrupt Ports, enable IRQ_F2P[15:0] option and click OK

54
2.4 Create ARM-based hardware platform

Figure 2.65: Re-customize IP - ZYNQ7 Processing System (5.5) dialog box

Step 22. Connect the interrupt port of the AXI Timer (axi_timer_0) block to the new IRQ_F2P[0:0] port of the ZYNQ7
Processing System (processing_systam7_0) block
Step 23. Right-click in the IP integrator canvas and select the Add IP... option to add the last IP block necessary for our
design. Search for the Constant (xlconstant_0) core and add it into our design.
Step 24. Double-click on the Constant block and set the constant value, Const Val, to be 0 and click OK, see Illustration
2.66
This step is important because we have to connect the unconnected input ports of the AXI Timer (axi_timer_0) block
(capturetrig0, capturetrig1 and freeze) to GND, because we won’t need them in our design. Using the Constant block is
one possible way how to solve this problem.

55
CREATING THE HARDWARE PLATFORM

Figure 2.66: Re-customize IP - Constant (1.1) dialog box

Step 25. Connect the capturetrig0, capturetrig1 and freeze ports of the AXI Timer (axi_timer_0) block to the dout[0:0]
port of the new Constant (xlconstnt_0) block
Step 26. Also connect the aux_reset_in port of the Processor System Reset (rst_ps7_0_100M) to the dout[0:0] port of
the Constant (xlconstnt_0) block
Step 27. Leave generateout0, generateout1 and pwm0 output ports of the AXI Timer (axi_timer_0) block unconnected
Step 28. The last step in our block design creation will be to re-customize Processor System Reset block. Double-click
on the Processor System Reset (rst_ps7_0_100M) block.
Step 29. In the Processor System Reset (5.0) dialog box, change the Auxillary Reset option from Auto to Manual and
click OK, see Illustration 2.67

56
2.4 Create ARM-based hardware platform

Figure 2.67: Re-Customize IP - Processor System Reset (5.0) dialog box

When we have connected all the necessary ports/interfaces and after we have customized all the necessary blocks in our
design, see Illustration 2.68, it is time to validate it.
Before validation, click on the Regenerate Layout button on the sidebar menu to re-arrange IP blocks on the design
canvas, to get a better view of the block design. You can re-arrange a completed diagram or a diagram in progress.
The optimized layout of the design should look similar to layout that is shown on the Illustration 2.68.

Figure 2.68: Final Block Diagram of our design

Step 30. At this point, you should save the IP integrator design. Use the File -> Save Block Design command from the

57
CREATING THE HARDWARE PLATFORM

main menu to save the design


Step 31. Validate you design by clicking the Validate Design button
Step 32. If your design is successfully validated, create HDL wrapper. In the Sources window, select modulator_arm,
right- click on it and choose Create HDL Wrapper... option
Step 33. In the Create HDL Wrapper dialog box, select Let Vivado manage wrapper and auto-update option and click
OK
Step 34. After the HDL wrapper is generated, you should see it in the Sources window, see Illustration 2.69

Figure 2.69: Sources window with generated HDL wrapper

Step 35. In the Vivado Flow Navigator , click Run Synthesis command (Synthesis option) and wait for task to be
completed
Step 36. After the synthesis is completed, the Synthesis Completed dialog box will appear. Select Open Synthesized
Design and click OK
Step 37. The last step in our hardware design will be to crate and add constraints file
Step 38. Change the layout from the Default Layout to I/O Planning view, in the layout pull-down menu in the main
toolbar, see Illustration 2.70

Figure 2.70: I/O Planning layout

This will change the layout from the Default view to the I/O Planning view, see Illustration 2.71
The main window of the I/O Planning view displays the package view of the ZedBoard Zynq device. Below the Package
view, two additional tabs are populated. One tab displays the list of I/O ports of the design and the second tab displays the
list of package pins on the device package.
By expanding any of the ports of our design in the I/O Ports tab, you can see that for each of them Vivado IDE automatically

58
2.4 Create ARM-based hardware platform

assigned appropriate pin location based on the selected development board. This means that tool has automatically
created a XDC file for us.

Figure 2.71: I/O Planning view

To prepare our design to work in the FPGA device, we must first convert it to a bitstream file.
Step 39. In the Vivado Flow Navigator , click Run Implementation command, see Illustration 2.72, and wait for task to
be completed
Step 40. At the end, when the implementation process is completed, click Generate Bitstream command, see Illustration
2.72
After this step, bitstream file will be generated.

Figure 2.72: Run Implementation and Generate Bitstream commands from the Vivado Flow Navigator

59
CREATING THE HARDWARE PLATFORM

2.5 Create a socius board based hardware platform

A socius development platform is a small, portable electronic device that can easily be powered from a USB port, USB
charger, Power Over Ethernet or a battery pack. You can easily develop software and/or digital hardware for it, because
it uses an FPGA with an embedded processors. For many applications, 90% of the overall design is fixed and only 10%
makes the difference. Socius delivers already a well designed board and should help you to focus on the specifics of
your project and can be easily extended to meet your needs. The main system with many interfaces and Linux is already
preconfigured and ready for use.
Since existing LEDs and switches on the socius board are connected to the PS part of the Zynq FPGA a new hardware
design, slightly different from the design presented in section 2.3 has to be developed. In this new design we will not use
PL part of the Zynq FPGA to implement timer and GPIO modules, because we would not be able to connect them to the
socius board LEDs and switches. Instead, we must use timer and GPIO modules from the PS part of the Zynq FPGA. More
specifically, we will use one Triple Timer Counter (TTC) module, TTC0, that is present in the PS part of the Zynq FPGA
and four General Purpose IO ports from the GPIO module that is also present in the PS part of the Zynq FPGA.
The following steps describe how to create ARM-based hardware platform for socius development board:
Step 1. Launch the Vivado software:
Select Start -> All Programs -> Xilinx Design Tools -> Vivado 2016.4 -> Vivado 2016.4 and the Vivado Getting
Started page will appear
Step 2. On the Getting Started page, choose Create New Project option
Step 3. In the Create a New Vivado Project dialog box, click Next and the wizard will guide you through the process of a
new project creation
Step 4. In the Project Name dialog box specify the name and the location of the new project:

• In the Project name field type modulator_socius_arm as the name of the project

• In the Project location field specify the location where project data will be stored

• Leave Create project subdirectory option enabled, see Illustration 2.73

Figure 2.73: Project Name dialog box

Step 5. Click Next


Step 6. In the Project Type dialog box specify the type of project you want to create. In our case we will choose RTL
Project option. Select Do not specify sources at this time also and click Next.
Step 7. In the Default Part dialog box select Parts option and set the following parameters as it is shown on the Illustration
2.74

60
2.5 Create a socius board based hardware platform

Figure 2.74: Default Part dialog box

Step 8. Click Next


Step 9. In the New Project Summary dialog box click Finish if you are satisfied with the summary of your project. If you
are not satisfied, you can go back as much as necessary to correct all the questionable issues.
After we finished with the new project creation, in a few seconds Vivado IDE Viewing Environment will appear, see
Illustration 2.75.

Figure 2.75: Vivado IDE Viewing Environment

Step 10. Create modulator_socius_arm_rtl.vhd and socius_components_package.vhd files using Vivado test editor

61
CREATING THE HARDWARE PLATFORM

and save them in the working directory. modulator_socius_arm_rtl.vhd file will hold top level module of our design
in which Zynq PS component configured for socius board will be instantiated. socius_components_package.vhd will
contain socius PS module component declaration.
modulator_socius_arm_rtl.vhd file:

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

library unisim;
use unisim.vcomponents.all;

library work;
use work.socius_components_package.all;

entity modulator_socius_arm is
port(
--! expansion top slot
pl_io_t_io_p_io : inout std_logic_vector (18 downto 0);
pl_io_t_io_n_io : inout std_logic_vector (18 downto 0);
--! expansion main slot
pl_io_m_io_p_io : inout std_logic_vector (18 downto 0);
pl_io_m_io_n_io : inout std_logic_vector (18 downto 0);
--! expansion bottom slot
pl_io_b_io_p_io : inout std_logic_vector (18 downto 0);
pl_io_b_io_n_io : inout std_logic_vector (18 downto 0);
-- ps io
ps_ddr3_addr : inout std_logic_vector(14 downto 0);
ps_ddr3_ba : inout std_logic_vector(2 downto 0);
ps_ddr3_cas_n : inout std_logic;
ps_ddr3_ck_n : inout std_logic;
ps_ddr3_ck_p : inout std_logic;
ps_ddr3_cke : inout std_logic;
ps_ddr3_cs_n : inout std_logic;
ps_ddr3_dm : inout std_logic_vector( 3 downto 0);
ps_ddr3_dq : inout std_logic_vector(31 downto 0);
ps_ddr3_dqs_n : inout std_logic_vector( 3 downto 0);
ps_ddr3_dqs_p : inout std_logic_vector( 3 downto 0);
ps_ddr3_odt : inout std_logic;
ps_ddr3_ras_n : inout std_logic;
ps_ddr3_reset_n : inout std_logic;
ps_ddr3_we_n : inout std_logic;
ps_ddr_vrn : inout std_logic;
ps_ddr_vrp : inout std_logic;
ps_clk_i : inout std_logic;
ps_por_n_i : inout std_logic;
ps_srst_n_i : inout std_logic;
ps_phy_mdc_io : inout std_logic;
ps_phy_mdio_io : inout std_logic;
ps_phy_rx_clk_io : inout std_logic;
ps_phy_rx_ctrl_io : inout std_logic;
ps_phy_rxd_io : inout std_logic_vector(3 downto 0);
ps_phy_tx_clk_io : inout std_logic;
ps_phy_tx_ctrl_io : inout std_logic;
ps_phy_txd_io : inout std_logic_vector(3 downto 0);
ps_i2c_scl_io : inout std_logic;
ps_i2c_sda_io : inout std_logic;
ps_led_error_n_io : inout std_logic;
ps_led_front_n_io : inout std_logic_vector(1 downto 0);
ps_led_sdcard_n_io : inout std_logic;
ps_sw0_a_io : inout std_logic;
ps_sw0_b_io : inout std_logic;
ps_sw1_a_io : inout std_logic;
ps_sw1_b_io : inout std_logic;
ps_sw2_a_io : inout std_logic;
ps_sw2_b_io : inout std_logic;
ps_sw3_a_io : inout std_logic;
ps_sw3_b_io : inout std_logic;
ps_uart_rx_io : inout std_logic;
ps_uart_tx_io : inout std_logic;
ps_qspi_cs_n_io : inout std_logic;
ps_qspi_data_io : inout std_logic_vector(3 downto 0);
ps_qspi_clk_io : inout std_logic;
ps_sdio_clk_io : inout std_logic;
ps_sdio_cmd_io : inout std_logic;
ps_sdio_data_io : inout std_logic_vector(3 downto 0);
ps_usb_clk_io : inout std_logic;
ps_usb_data_io : inout std_logic_vector(7 downto 0);
ps_usb_dir_io : inout std_logic;
ps_usb_nxt_io : inout std_logic;
ps_usb_stp_io : inout std_logic
-- ce_o : out std_logic;
-- pl_clk0_o : out std_logic
);
end entity;

62
2.5 Create a socius board based hardware platform

architecture structural of modulator_socius_arm is

-- Between architecture and begin is declaration area for types, signals and constants
-- Everything declared here will be visible in the whole architecture

--bram register interface soc


signal pl_bram_soc_addr_s : std_logic_vector (15 downto 0);
signal pl_bram_soc_din_s : std_logic_vector (31 downto 0);
signal pl_bram_soc_dout_s : std_logic_vector (31 downto 0);
signal pl_bram_soc_en_s : std_logic;
signal pl_bram_soc_rst_s : std_logic;
signal pl_bram_soc_we_s : std_logic_vector ( 3 downto 0);
--bram register interface mid
signal pl_bram_mid_addr_s : std_logic_vector (15 downto 0);
signal pl_bram_mid_din_s : std_logic_vector (31 downto 0);
signal pl_bram_mid_dout_s : std_logic_vector (31 downto 0);
signal pl_bram_mid_en_s : std_logic;
signal pl_bram_mid_rst_s : std_logic;
signal pl_bram_mid_we_s : std_logic_vector ( 3 downto 0);
--bram register interface top
signal pl_bram_top_addr_s : std_logic_vector (15 downto 0);
signal pl_bram_top_din_s : std_logic_vector (31 downto 0);
signal pl_bram_top_dout_s : std_logic_vector (31 downto 0);
signal pl_bram_top_en_s : std_logic;
signal pl_bram_top_rst_s : std_logic;
signal pl_bram_top_we_s : std_logic_vector ( 3 downto 0);
--bram register interface bot
signal pl_bram_bot_addr_s : std_logic_vector (15 downto 0);
signal pl_bram_bot_din_s : std_logic_vector (31 downto 0);
signal pl_bram_bot_dout_s : std_logic_vector (31 downto 0);
signal pl_bram_bot_en_s : std_logic;
signal pl_bram_bot_rst_s : std_logic;
signal pl_bram_bot_we_s : std_logic_vector ( 3 downto 0);

-- declaration for fixed signal PL to PS


signal pl_clk0_s : std_logic;
signal pl_clk1_s : std_logic;
signal pl_clk2_s : std_logic;
signal pl_clk3_s : std_logic;
signal pl_reset_n_s : std_logic;

-- ps signals
signal ps_mio_s : std_logic_vector(53 downto 0);

--uart, i2c, spi signals


signal uart_rxd_s : std_logic;
signal uart_txd_s : std_logic;
signal spi_io0_i_s : std_logic;
signal spi_io0_o_s : std_logic;
signal spi_io0_t_s : std_logic;
signal spi_io1_i_s : std_logic;
signal spi_io1_o_s : std_logic;
signal spi_io1_t_s : std_logic;
signal spi_sck_i_s : std_logic;
signal spi_sck_o_s : std_logic;
signal spi_sck_t_s : std_logic;
signal spi_ss1_o_s : std_logic;
signal spi_ss2_o_s : std_logic;
signal spi_ss_i_s : std_logic;
signal spi_ss_o_s : std_logic;
signal spi_ss_t_s : std_logic;
signal iic_scl_i_s : std_logic;
signal iic_scl_o_s : std_logic;
signal iic_scl_t_s : std_logic;
signal iic_sda_i_s : std_logic;
signal iic_sda_o_s : std_logic;
signal iic_sda_t_s : std_logic;

--interrupt signals to ps
signal pl_int_soc_s : std_logic;
signal pl_int_top_s : std_logic;
signal pl_int_mid_s : std_logic;
signal pl_int_bot_s : std_logic;

begin

-- instance of processor system PS

socius_xz_io_ps_bd_i: component socius_xz_io_ps_bd


port map (
ddr3_addr => ps_ddr3_addr,
ddr3_ba => ps_ddr3_ba,
ddr3_cas_n => ps_ddr3_cas_n,
ddr3_ck_n => ps_ddr3_ck_n,
ddr3_ck_p => ps_ddr3_ck_p,
ddr3_cke => ps_ddr3_cke,
ddr3_cs_n => ps_ddr3_cs_n,
ddr3_dm => ps_ddr3_dm,
ddr3_dq => ps_ddr3_dq,
ddr3_dqs_n => ps_ddr3_dqs_n,
ddr3_dqs_p => ps_ddr3_dqs_p,

63
CREATING THE HARDWARE PLATFORM

ddr3_odt => ps_ddr3_odt,


ddr3_ras_n => ps_ddr3_ras_n,
ddr3_reset_n => ps_ddr3_reset_n,
ddr3_we_n => ps_ddr3_we_n,
fixed_io_ddr_vrn => ps_ddr_vrn,
fixed_io_ddr_vrp => ps_ddr_vrp,
fixed_io_mio => ps_mio_s,
fixed_io_ps_clk => ps_clk_i,
fixed_io_ps_porb => ps_por_n_i,
fixed_io_ps_srstb => ps_srst_n_i,
pl_uart_1_rxd => uart_rxd_s,
pl_uart_1_txd => uart_txd_s,
pl_spi_0_io0_i => spi_io0_i_s,
pl_spi_0_io0_o => spi_io0_o_s,
pl_spi_0_io0_t => spi_io0_t_s,
pl_spi_0_io1_i => spi_io1_i_s,
pl_spi_0_io1_o => spi_io1_o_s,
pl_spi_0_io1_t => spi_io1_t_s,
pl_spi_0_sck_i => spi_sck_i_s,
pl_spi_0_sck_o => spi_sck_o_s,
pl_spi_0_sck_t => spi_sck_t_s,
pl_spi_0_ss1_o => spi_ss1_o_s,
pl_spi_0_ss2_o => spi_ss2_o_s,
pl_spi_0_ss_i => spi_ss_i_s,
pl_spi_0_ss_o => spi_ss_o_s,
pl_spi_0_ss_t => spi_ss_t_s,
pl_iic_1_scl_i => iic_scl_i_s,
pl_iic_1_scl_o => iic_scl_o_s,
pl_iic_1_scl_t => iic_scl_t_s,
pl_iic_1_sda_i => iic_sda_i_s,
pl_iic_1_sda_o => iic_sda_o_s,
pl_iic_1_sda_t => iic_sda_t_s,
sdio_0_cdn => ’1’, -- pl_sd_cd_n_i,
usbind_0_port_indctl => open,
usbind_0_vbus_pwrfault => ’1’, -- pl_usb_fault_n_i,
usbind_0_vbus_pwrselect => open,
pl_bram_bot_addr => pl_bram_bot_addr_s,
pl_bram_bot_clk => open,
pl_bram_bot_din => pl_bram_bot_din_s,
pl_bram_bot_dout => pl_bram_bot_dout_s,
pl_bram_bot_en => pl_bram_bot_en_s,
pl_bram_bot_rst => pl_bram_bot_rst_s,
pl_bram_bot_we => pl_bram_bot_we_s,
pl_bram_mid_addr => pl_bram_mid_addr_s,
pl_bram_mid_clk => open,
pl_bram_mid_din => pl_bram_mid_din_s,
pl_bram_mid_dout => pl_bram_mid_dout_s,
pl_bram_mid_en => pl_bram_mid_en_s,
pl_bram_mid_rst => pl_bram_mid_rst_s,
pl_bram_mid_we => pl_bram_mid_we_s,
pl_bram_soc_addr => pl_bram_soc_addr_s,
pl_bram_soc_clk => open,
pl_bram_soc_din => pl_bram_soc_din_s,
pl_bram_soc_dout => pl_bram_soc_dout_s,
pl_bram_soc_en => pl_bram_soc_en_s,
pl_bram_soc_rst => pl_bram_soc_rst_s,
pl_bram_soc_we => pl_bram_soc_we_s,
pl_bram_top_addr => pl_bram_top_addr_s,
pl_bram_top_clk => open,
pl_bram_top_din => pl_bram_top_din_s,
pl_bram_top_dout => pl_bram_top_dout_s,
pl_bram_top_en => pl_bram_top_en_s,
pl_bram_top_rst => pl_bram_top_rst_s,
pl_bram_top_we => pl_bram_top_we_s,
pl_clk0 => pl_clk0_s,
pl_clk1 => pl_clk1_s,
pl_clk2 => pl_clk2_s,
pl_clk3 => pl_clk3_s,
pl_reset_n => pl_reset_n_s,
pl_int_soc(0) => pl_int_soc_s,
pl_int_top(0) => pl_int_top_s,
pl_int_mid(0) => pl_int_mid_s,
pl_int_bot(0) => pl_int_bot_s
);

-- assignment of MIO to board names

ps_mio_s (53) <= ps_phy_mdio_io;


ps_mio_s (52) <= ps_phy_mdc_io;
ps_mio_s (51) <= ps_uart_tx_io;
ps_mio_s (50) <= ps_uart_rx_io;
ps_mio_s (49) <= ps_led_error_n_io;
ps_mio_s (48 downto 47) <= ps_led_front_n_io(1 downto 0);
ps_mio_s (46) <= ps_led_sdcard_n_io;
ps_mio_s (45 downto 42) <= ps_sdio_data_io;
ps_mio_s (41) <= ps_sdio_cmd_io;
ps_mio_s (40) <= ps_sdio_clk_io;
ps_mio_s (39) <= ps_usb_data_io(7);
ps_mio_s (38) <= ps_usb_data_io(6);
ps_mio_s (37) <= ps_usb_data_io(5);
ps_mio_s (36) <= ps_usb_clk_io;

64
2.5 Create a socius board based hardware platform

ps_mio_s (35) <= ps_usb_data_io(3);


ps_mio_s (34) <= ps_usb_data_io(2);
ps_mio_s (33) <= ps_usb_data_io(1);
ps_mio_s (32) <= ps_usb_data_io(0);
ps_mio_s (31) <= ps_usb_nxt_io;
ps_mio_s (30) <= ps_usb_stp_io;
ps_mio_s (29) <= ps_usb_dir_io;
ps_mio_s (28) <= ps_usb_data_io(4);
ps_mio_s (27) <= ps_phy_rx_ctrl_io;
ps_mio_s (26 downto 23) <= ps_phy_rxd_io;
ps_mio_s (22) <= ps_phy_rx_clk_io;
ps_mio_s (21) <= ps_phy_tx_ctrl_io;
ps_mio_s (20 downto 17) <= ps_phy_txd_io;
ps_mio_s (16) <= ps_phy_tx_clk_io;
ps_mio_s (15) <= ps_i2c_sda_io;
ps_mio_s (14) <= ps_i2c_scl_io;
ps_mio_s (13) <= ps_sw3_b_io;
ps_mio_s (12) <= ps_sw3_a_io;
ps_mio_s (11) <= ps_sw2_b_io;
ps_mio_s (10) <= ps_sw2_a_io;
ps_mio_s (9) <= ps_sw1_b_io;
ps_mio_s (8) <= ps_sw1_a_io;
ps_mio_s (7) <= ps_sw0_b_io;
ps_mio_s (6) <= ps_qspi_clk_io;
ps_mio_s (5 downto 2) <= ps_qspi_data_io;
ps_mio_s (1) <= ps_qspi_cs_n_io;
ps_mio_s (0) <= ps_sw0_a_io;

end architecture;

socius_components_package.vhd file:

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

package socius_components_package is

component socius_xz_io_ps_bd is
port(
ddr3_addr : inout std_logic_vector ( 14 downto 0 );
ddr3_ba : inout std_logic_vector ( 2 downto 0 );
ddr3_cas_n : inout std_logic;
ddr3_ck_n : inout std_logic;
ddr3_ck_p : inout std_logic;
ddr3_cke : inout std_logic;
ddr3_cs_n : inout std_logic;
ddr3_dm : inout std_logic_vector ( 3 downto 0 );
ddr3_dq : inout std_logic_vector ( 31 downto 0 );
ddr3_dqs_n : inout std_logic_vector ( 3 downto 0 );
ddr3_dqs_p : inout std_logic_vector ( 3 downto 0 );
ddr3_odt : inout std_logic;
ddr3_ras_n : inout std_logic;
ddr3_reset_n : inout std_logic;
ddr3_we_n : inout std_logic;
fixed_io_ddr_vrn : inout std_logic;
fixed_io_ddr_vrp : inout std_logic;
fixed_io_mio : inout std_logic_vector ( 53 downto 0 );
fixed_io_ps_clk : inout std_logic;
fixed_io_ps_porb : inout std_logic;
fixed_io_ps_srstb : inout std_logic;
pl_uart_1_rxd : in std_logic;
pl_uart_1_txd : out std_logic;
pl_spi_0_io0_i : in std_logic;
pl_spi_0_io0_o : out std_logic;
pl_spi_0_io0_t : out std_logic;
pl_spi_0_io1_i : in std_logic;
pl_spi_0_io1_o : out std_logic;
pl_spi_0_io1_t : out std_logic;
pl_spi_0_sck_i : in std_logic;
pl_spi_0_sck_o : out std_logic;
pl_spi_0_sck_t : out std_logic;
pl_spi_0_ss1_o : out std_logic;
pl_spi_0_ss2_o : out std_logic;
pl_spi_0_ss_i : in std_logic;
pl_spi_0_ss_o : out std_logic;
pl_spi_0_ss_t : out std_logic;
pl_iic_1_scl_i : in std_logic;
pl_iic_1_scl_o : out std_logic;
pl_iic_1_scl_t : out std_logic;
pl_iic_1_sda_i : in std_logic;
pl_iic_1_sda_o : out std_logic;
pl_iic_1_sda_t : out std_logic;
sdio_0_cdn : in std_logic;
usbind_0_port_indctl : out std_logic_vector ( 1 downto 0 );
usbind_0_vbus_pwrfault : in std_logic;
usbind_0_vbus_pwrselect : out std_logic;
pl_bram_bot_addr : out std_logic_vector ( 15 downto 0 );

65
CREATING THE HARDWARE PLATFORM

pl_bram_bot_clk : out std_logic;


pl_bram_bot_din : out std_logic_vector ( 31 downto 0 );
pl_bram_bot_dout : in std_logic_vector ( 31 downto 0 );
pl_bram_bot_en : out std_logic;
pl_bram_bot_rst : out std_logic;
pl_bram_bot_we : out std_logic_vector ( 3 downto 0 );
pl_bram_mid_addr : out std_logic_vector ( 15 downto 0 );
pl_bram_mid_clk : out std_logic;
pl_bram_mid_din : out std_logic_vector ( 31 downto 0 );
pl_bram_mid_dout : in std_logic_vector ( 31 downto 0 );
pl_bram_mid_en : out std_logic;
pl_bram_mid_rst : out std_logic;
pl_bram_mid_we : out std_logic_vector ( 3 downto 0 );
pl_bram_soc_addr : out std_logic_vector ( 15 downto 0 );
pl_bram_soc_clk : out std_logic;
pl_bram_soc_din : out std_logic_vector ( 31 downto 0 );
pl_bram_soc_dout : in std_logic_vector ( 31 downto 0 );
pl_bram_soc_en : out std_logic;
pl_bram_soc_rst : out std_logic;
pl_bram_soc_we : out std_logic_vector ( 3 downto 0 );
pl_bram_top_addr : out std_logic_vector ( 15 downto 0 );
pl_bram_top_clk : out std_logic;
pl_bram_top_din : out std_logic_vector ( 31 downto 0 );
pl_bram_top_dout : in std_logic_vector ( 31 downto 0 );
pl_bram_top_en : out std_logic;
pl_bram_top_rst : out std_logic;
pl_bram_top_we : out std_logic_vector ( 3 downto 0 );
pl_clk0 : out std_logic;
pl_clk1 : out std_logic;
pl_clk2 : out std_logic;
pl_clk3 : out std_logic;
pl_reset_n : out std_logic;
pl_int_bot : in std_logic_vector ( 0 to 0 );
pl_int_mid : in std_logic_vector ( 0 to 0 );
pl_int_soc : in std_logic_vector ( 0 to 0 );
pl_int_top : in std_logic_vector ( 0 to 0 )
);
end component;

end package;

Step 10. When we finished with the modulator_socius_arm_rtl.vhd and socius_components_package.vhd files cre-
ation, in the Vivado Flow Navigator, click Add Sources command
Step 11. In the Add Sources dialog box, select Add or create design sources option to add the design source files into
the project and click Next
Step 12. In the Add or Create Design Sources dialog box, click the + icon and select Add Files... option to include the
existing source files into the project
Step 13. In the Add Source Files dialog box, browse to the project working directory and select the modulator_socius_-
arm_rtl.vhd and socius_components_package.vhd source files
Step 14. Click OK and the modulator_socius_arm_rtl.vhd and socius_components_package.vhd source files should
appear in the Add or Create Design Sources dialog box
Step 15. Click Finish and your source files should appear under the Design Sources in the Sources view in the Project
Manager window, see Illustration 2.76

Figure 2.76: Sources view with added new files

Step 16. Now is the time to create constraints file for the socius board, modulator_socius.xdc. Open Vivado text editor,
copy your constraints code in it or write directly in it and save the constraints file in your working directory. The complete
modulator_socius.xdc source file tou can find in the text below.

66
2.5 Create a socius board based hardware platform

modulator_socius.xdc constraints file:

# set properties for bitstream genration


set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
#set_property BITSTREAM.GENERAL.XADCENHANCEDLINEARITY ON [current_design]
#set_property BITSTREAM.GENERAL.XADCPOWERDOWN ENABLE [current_design]

# set configuration bank voltages


set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]

# set condition for power analyzer


set_operating_conditions -ambient_temp 50
set_operating_conditions -board small
set_operating_conditions -airflow 250
set_operating_conditions -heatsink low
set_operating_conditions -board_layers 12to15

# unrelate clock domains in PL for clocks genrated in PS f


#set_false_path -from [get_clocks clk_fpga_1] -to [get_clocks clk_fpga_0]
#set_false_path -from [get_clocks clk_fpga_0] -to [get_clocks clk_fpga_1]
#set_clock_groups -asynchronous -group clk_fpga_0 -group clk_fpga_1

# only for power designs


#set_property C_CLK_INPUT_FREQ_HZ 300000000 [get_debug_cores dbg_hub]
#set_property C_ENABLE_CLK_DIVIDER false [get_debug_cores dbg_hub]
#set_property C_USER_SCAN_CHAIN 1 [get_debug_cores dbg_hub]
#connect_debug_port dbg_hub/clk [get_nets pl_clk3]

# Push flip flops to IOBs


#set_property IOB true [get_cells -hier *io_i_s_reg*]
#set_property IOB true [get_cells -hier *io_o_reg*]
#set_property IOB true [get_cells -hier *io_t_reg*]

# PL pins with fixed functionality for xz1 and xz2

set_property PACKAGE_PIN M14 [get_ports pl_b35_m14_io]


set_property IOSTANDARD LVCMOS33 [get_ports pl_b35_m14_io]
set_output_delay -clock [get_clocks clk_fpga_0] -max 1.000 [get_ports pl_b35_m14_io]
set_output_delay -clock [get_clocks clk_fpga_0] -min 0.500 [get_ports pl_b35_m14_io]

set_property PACKAGE_PIN M15 [get_ports pl_b35_m15_io]


set_property IOSTANDARD LVCMOS33 [get_ports pl_b35_m15_io]
set_output_delay -clock [get_clocks clk_fpga_0] -max 1.000 [get_ports pl_b35_m15_io]
set_output_delay -clock [get_clocks clk_fpga_0] -min 0.500 [get_ports pl_b35_m15_io]

set_property PACKAGE_PIN T19 [get_ports pl_hsw_good_i]


set_property IOSTANDARD LVCMOS33 [get_ports pl_hsw_good_i]
set_input_delay -clock [get_clocks clk_fpga_0] -max 5.000 [get_ports pl_hsw_good_i]
set_input_delay -clock [get_clocks clk_fpga_0] -min 4.500 [get_ports pl_hsw_good_i]

set_property PACKAGE_PIN V13 [get_ports pl_phy_reset_n_o]


set_property IOSTANDARD LVCMOS33 [get_ports pl_phy_reset_n_o]

set_property PACKAGE_PIN T15 [get_ports pl_sd_cd_n_i]


set_property IOSTANDARD LVCMOS33 [get_ports pl_sd_cd_n_i]

set_property PACKAGE_PIN J15 [get_ports pl_pwm_fan_o]


set_property IOSTANDARD LVCMOS33 [get_ports pl_pwm_fan_o]

set_property PACKAGE_PIN R19 [get_ports pl_pwr_en_i]


set_property IOSTANDARD LVCMOS33 [get_ports pl_pwr_en_i]
set_input_delay -clock [get_clocks clk_fpga_0] -max 5.000 [get_ports pl_pwr_en_i]
set_input_delay -clock [get_clocks clk_fpga_0] -min 4.500 [get_ports pl_pwr_en_i]

set_property PACKAGE_PIN G14 [get_ports pl_rtc_out_i]


set_property IOSTANDARD LVCMOS33 [get_ports pl_rtc_out_i]
set_input_delay -clock [get_clocks clk_fpga_0] -max 5.000 [get_ports pl_rtc_out_i]
set_input_delay -clock [get_clocks clk_fpga_0] -min 4.500 [get_ports pl_rtc_out_i]

set_property PACKAGE_PIN U13 [get_ports pl_usb_reset_n_o]


set_property IOSTANDARD LVCMOS33 [get_ports pl_usb_reset_n_o]
set_output_delay -clock [get_clocks clk_fpga_0] -min 1.000 [get_ports pl_usb_reset_n_o]
set_output_delay -clock [get_clocks clk_fpga_0] -max 0.500 [get_ports pl_usb_reset_n_o]

set_property PACKAGE_PIN T14 [get_ports pl_usb_fault_n_i]


set_property IOSTANDARD LVCMOS33 [get_ports pl_usb_fault_n_i]
set_input_delay -clock [get_clocks clk_fpga_0] -max 5.000 [get_ports pl_usb_fault_n_i]
set_input_delay -clock [get_clocks clk_fpga_0] -min 4.500 [get_ports pl_usb_fault_n_i]

#set_property PACKAGE_PIN M14 [get_ports pl_b35_m14_io]


#set_property IOSTANDARD LVCMOS33 [get_ports pl_b35_m14_io]
#set_output_delay -clock [get_clocks clk_fpga_0] -max 1.000 [get_ports pl_b35_m14_io]
#set_output_delay -clock [get_clocks clk_fpga_0] -min 0.500 [get_ports pl_b35_m14_io]

#set_property PACKAGE_PIN M15 [get_ports pl_b35_m15_io]


#set_property IOSTANDARD LVCMOS33 [get_ports pl_b35_m15_io]
#set_output_delay -clock [get_clocks clk_fpga_0] -max 1.000 [get_ports pl_b35_m15_io]
#set_output_delay -clock [get_clocks clk_fpga_0] -min 0.500 [get_ports pl_b35_m15_io]

67
CREATING THE HARDWARE PLATFORM

#set_property PACKAGE_PIN T19 [get_ports pl_hsw_good_i]


#set_property IOSTANDARD LVCMOS33 [get_ports pl_hsw_good_i]
#set_input_delay -clock [get_clocks clk_fpga_0] -max 5.000 [get_ports pl_hsw_good_i]
#set_input_delay -clock [get_clocks clk_fpga_0] -min 4.500 [get_ports pl_hsw_good_i]

#set_property PACKAGE_PIN V13 [get_ports pl_phy_reset_n_o]


#set_property IOSTANDARD LVCMOS33 [get_ports pl_phy_reset_n_o]
#set_output_delay -clock [get_clocks clk_fpga_1] -max 1.000 [get_ports pl_phy_reset_n_o]
#set_output_delay -clock [get_clocks clk_fpga_1] -min 0.500 [get_ports pl_phy_reset_n_o]

#set_property PACKAGE_PIN T15 [get_ports pl_sd_cd_n_i]


#set_property IOSTANDARD LVCMOS33 [get_ports pl_sd_cd_n_i]

#set_property PACKAGE_PIN J15 [get_ports pl_pwm_fan_o]


#set_property IOSTANDARD LVCMOS33 [get_ports pl_pwm_fan_o]
#set_output_delay -clock [get_clocks clk_fpga_1] -max 1.000 [get_ports pl_pwm_fan_o]
#set_output_delay -clock [get_clocks clk_fpga_1] -min 0.500 [get_ports pl_pwm_fan_o]

#set_property PACKAGE_PIN R19 [get_ports pl_pwr_en_i]


#set_property IOSTANDARD LVCMOS33 [get_ports pl_pwr_en_i]
#set_input_delay -clock [get_clocks clk_fpga_0] -max 5.000 [get_ports pl_pwr_en_i]
#set_input_delay -clock [get_clocks clk_fpga_0] -min 4.500 [get_ports pl_pwr_en_i]

#set_property PACKAGE_PIN G14 [get_ports pl_rtc_out_i]


#set_property IOSTANDARD LVCMOS33 [get_ports pl_rtc_out_i]
#set_input_delay -clock [get_clocks clk_fpga_0] -max 5.000 [get_ports pl_rtc_out_i]
#set_input_delay -clock [get_clocks clk_fpga_0] -min 4.500 [get_ports pl_rtc_out_i]

#set_property PACKAGE_PIN U13 [get_ports pl_usb_reset_n_o]


#set_property IOSTANDARD LVCMOS33 [get_ports pl_usb_reset_n_o]
#set_output_delay -clock [get_clocks clk_fpga_0] -min 1.000 [get_ports pl_usb_reset_n_o]
#set_output_delay -clock [get_clocks clk_fpga_0] -max 0.500 [get_ports pl_usb_reset_n_o]

#set_property PACKAGE_PIN T14 [get_ports pl_usb_fault_n_i]


#set_property IOSTANDARD LVCMOS33 [get_ports pl_usb_fault_n_i]
#set_input_delay -clock [get_clocks clk_fpga_0] -max 5.000 [get_ports pl_usb_fault_n_i]
#set_input_delay -clock [get_clocks clk_fpga_0] -min 4.500 [get_ports pl_usb_fault_n_i]

Step 17. In the Vivado Flow Navigator, click the Add Sources command
Step 18. In the Add Sources dialog box select Add or create constraints option to add the modulator_socius.xdc
constraints file into our project and click Next
Step 19. In the Add or Create Constraints dialog box, click + icon and select Add Files... option
Step 20. In the Add Constraint Files dialog box, browse to the project working directory and select the modulator_-
socius.xdc constraints file
Step 21. Click OK and the modulator_socius.xdc constraints file should appear in the Add or Create Constraints dialog
box
Step 22. Click Finish and your constraints file should appear under the Constraints in the Sources view, see Illustration
2.77

Figure 2.77: Sources view with added constraints file

Finally, we must configure the Zynq PS part to work on socius development board. This includes a number of configuration
steps, one of them being the proper configuration of the PS GPIO module to connect to the LEDs and switches that are
present on the socius board. Also, we must enable the Triple Timer Counter 0 (TTC0) module within Zynq PS, that will
be used in the modulator design. All these PS configuration steps can be done using the Vivado GUI, by creating a block
design. However, since this task includes a lot of manual settings of the Zynq PS, a better approach would be to do this
manual configuration only once and then to create a Tcl script file that can be used in all future configurations of the Zynq
PS part. The Tcl script that should be used to correctly configure Zynq PS to work on socius board is shown below.
socius_xz_io_ps_bd.tcl file:

68
2.5 Create a socius board based hardware platform

################################################################
# This is a generated script based on design: socius_xz_io_ps_bd
#
# Though there are limitations about the generated script,
# the main purpose of this utility is to make learning
# IP Integrator Tcl commands easier.
################################################################

################################################################
# Check if script is running in correct Vivado version.
################################################################
set scripts_vivado_version 2016.4
set current_vivado_version [version -short]

if { [string first $scripts_vivado_version $current_vivado_version] == -1 } {


puts ""
puts "ERROR: This script was generated using Vivado <$scripts_vivado_version> and is being run in
<$current_vivado_version> of Vivado. Please run the script in Vivado <$scripts_vivado_version> then open the
design in Vivado <$current_vivado_version>. Upgrade the design by running \"Tools => Report => Report IP
Status...\", then run write_bd_tcl to create an updated script."

return 1
}

################################################################
# START
################################################################

# To test this script, run the following commands from Vivado Tcl console:
# source socius_xz_io_ps_bd_script.tcl

# If you do not already have a project created,


# you can create a project using the following command:
# create_project project_1 myproj -part xc7z020clg400-1

# CHECKING IF PROJECT EXISTS


if { [get_projects -quiet] eq "" } {
puts "ERROR: Please open or create a project!"
return 1
}

# CHANGE DESIGN NAME HERE


set design_name socius_xz_io_ps_bd

# If you do not already have an existing IP Integrator design open,


# you can create a design using the following command:
# create_bd_design $design_name

# Creating design if needed


set errMsg ""
set nRet 0

set cur_design [current_bd_design -quiet]


set list_cells [get_bd_cells -quiet]

if { ${design_name} eq "" } {
# USE CASES:
# 1) Design_name not set

set errMsg "ERROR: Please set the variable <design_name> to a non-empty value."
set nRet 1

} elseif { ${cur_design} ne "" && ${list_cells} eq "" } {


# USE CASES:
# 2): Current design opened AND is empty AND names same.
# 3): Current design opened AND is empty AND names diff; design_name NOT in project.
# 4): Current design opened AND is empty AND names diff; design_name exists in project.

if { $cur_design ne $design_name } {
puts "INFO: Changing value of <design_name> from <$design_name> to <$cur_design> since current design
is empty."
set design_name [get_property NAME $cur_design]
}
puts "INFO: Constructing design in IPI design <$cur_design>..."

} elseif { ${cur_design} ne "" && $list_cells ne "" && $cur_design eq $design_name } {


# USE CASES:
# 5) Current design opened AND has components AND same names.

set errMsg "ERROR: Design <$design_name> already exists in your project, please set the variable
<design_name> to another value."
set nRet 1
} elseif { [get_files -quiet ${design_name}.bd] ne "" } {
# USE CASES:
# 6) Current opened design, has components, but diff names, design_name exists in project.
# 7) No opened design, design_name exists in project.

set errMsg "ERROR: Design <$design_name> already exists in your project, please set the variable
<design_name> to another value."

69
CREATING THE HARDWARE PLATFORM

set nRet 2

} else {
# USE CASES:
# 8) No opened design, design_name not in project.
# 9) Current opened design, has components, but diff names, design_name not in project.

puts "INFO: Currently there is no design <$design_name> in project, so creating one..."

create_bd_design $design_name

puts "INFO: Making design <$design_name> as current_bd_design."


current_bd_design $design_name

puts "INFO: Currently the variable <design_name> is equal to \"$design_name\"."

if { $nRet != 0 } {
puts $errMsg
return $nRet
}

##################################################################
# DESIGN PROCs
##################################################################

# Hierarchical cell: socius_xz_bram_switch_bd


proc create_hier_cell_socius_xz_bram_switch_bd { parentCell nameHier } {

if { $parentCell eq "" || $nameHier eq "" } {


puts "ERROR: create_hier_cell_socius_xz_bram_switch_bd() - Empty argument(s)!"
return
}

# Get object for parentCell


set parentObj [get_bd_cells $parentCell]
if { $parentObj == "" } {
puts "ERROR: Unable to find parent cell <$parentCell>!"
return
}

# Make sure parentObj is hier blk


set parentType [get_property TYPE $parentObj]
if { $parentType ne "hier" } {
puts "ERROR: Parent <$parentObj> has TYPE = <$parentType>. Expected to be <hier>."
return
}

# Save current instance; Restore later


set oldCurInst [current_bd_instance .]

# Set parent object as current


current_bd_instance $parentObj

# Create cell and set as current instance


set hier_obj [create_bd_cell -type hier $nameHier]
current_bd_instance $hier_obj

# Create interface pins


create_bd_intf_pin -mode Slave -vlnv xilinx.com:interface:aximm_rtl:1.0 S00_AXI
create_bd_intf_pin -mode Master -vlnv xilinx.com:interface:bram_rtl:1.0 bram_bot
create_bd_intf_pin -mode Master -vlnv xilinx.com:interface:bram_rtl:1.0 bram_mid
create_bd_intf_pin -mode Master -vlnv xilinx.com:interface:bram_rtl:1.0 bram_soc
create_bd_intf_pin -mode Master -vlnv xilinx.com:interface:bram_rtl:1.0 bram_top

# Create pins
create_bd_pin -dir I -type rst M01_ARESETN
create_bd_pin -dir I -type clk S00_ACLK

# Create instance: axi3_axi4_converter, and set properties


set axi3_axi4_converter [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_protocol_converter:2.1
axi3_axi4_converter ]
set_property -dict [ list \
CONFIG.TRANSLATION_MODE {0} \
] $axi3_axi4_converter

# Create instance: axi3_axi4_converter1, and set properties


set axi3_axi4_converter1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_protocol_converter:2.1
axi3_axi4_converter1 ]
set_property -dict [ list \
CONFIG.TRANSLATION_MODE {0} \
] $axi3_axi4_converter1

# Create instance: axi3_axi4_converter2, and set properties


set axi3_axi4_converter2 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_protocol_converter:2.1
axi3_axi4_converter2 ]
set_property -dict [ list \
CONFIG.TRANSLATION_MODE {0} \
] $axi3_axi4_converter2

70
2.5 Create a socius board based hardware platform

# Create instance: axi3_axi4_converter3, and set properties


set axi3_axi4_converter3 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_protocol_converter:2.1
axi3_axi4_converter3 ]
set_property -dict [ list \
CONFIG.TRANSLATION_MODE {0} \
] $axi3_axi4_converter3

# Create instance: socius_xz_bram_if_pl_bot, and set properties


set socius_xz_bram_if_pl_bot [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_bram_ctrl:4.0
socius_xz_bram_if_pl_bot ]
set_property -dict [ list \
CONFIG.PROTOCOL {AXI4LITE} \
CONFIG.SINGLE_PORT_BRAM {1} \
] $socius_xz_bram_if_pl_bot

# Create instance: socius_xz_bram_if_pl_mid, and set properties


set socius_xz_bram_if_pl_mid [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_bram_ctrl:4.0
socius_xz_bram_if_pl_mid ]
set_property -dict [ list \
CONFIG.PROTOCOL {AXI4LITE} \
CONFIG.SINGLE_PORT_BRAM {1} \
] $socius_xz_bram_if_pl_mid

# Create instance: socius_xz_bram_if_pl_soc, and set properties


set socius_xz_bram_if_pl_soc [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_bram_ctrl:4.0
socius_xz_bram_if_pl_soc ]
set_property -dict [ list \
CONFIG.PROTOCOL {AXI4LITE} \
CONFIG.SINGLE_PORT_BRAM {1} \
] $socius_xz_bram_if_pl_soc

# Create instance: socius_xz_bram_if_pl_top, and set properties


set socius_xz_bram_if_pl_top [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_bram_ctrl:4.0
socius_xz_bram_if_pl_top ]
set_property -dict [ list \
CONFIG.PROTOCOL {AXI4LITE} \
CONFIG.SINGLE_PORT_BRAM {1} \
] $socius_xz_bram_if_pl_top

# Create instance: socius_xz_bram_interconnect, and set properties


set socius_xz_bram_interconnect [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect:2.1
socius_xz_bram_interconnect ]
set_property -dict [ list \
CONFIG.NUM_MI {4} \
] $socius_xz_bram_interconnect

# Create instance: xlconstant_1, and set properties


set xlconstant_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 xlconstant_1 ]

# Create instance: xlconstant_2, and set properties


set xlconstant_2 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 xlconstant_2 ]

# Create instance: xlconstant_3, and set properties


set xlconstant_3 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 xlconstant_3 ]

# Create instance: xlconstant_4, and set properties


set xlconstant_4 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 xlconstant_4 ]

# Create interface connections


connect_bd_intf_net -intf_net S00_AXI_1 [get_bd_intf_pins S00_AXI] [get_bd_intf_pins
socius_xz_bram_interconnect/S00_AXI]
connect_bd_intf_net -intf_net axi3_axi4_converter_M_AXI [get_bd_intf_pins axi3_axi4_converter/M_AXI] [
get_bd_intf_pins socius_xz_bram_if_pl_soc/S_AXI]
connect_bd_intf_net -intf_net axi3_axi4_converter_M_AXI1 [get_bd_intf_pins axi3_axi4_converter1/M_AXI] [
get_bd_intf_pins socius_xz_bram_if_pl_mid/S_AXI]
connect_bd_intf_net -intf_net axi3_axi4_converter_M_AXI2 [get_bd_intf_pins axi3_axi4_converter2/M_AXI] [
get_bd_intf_pins socius_xz_bram_if_pl_top/S_AXI]
connect_bd_intf_net -intf_net axi3_axi4_converter_M_AXI3 [get_bd_intf_pins axi3_axi4_converter3/M_AXI] [
get_bd_intf_pins socius_xz_bram_if_pl_bot/S_AXI]
connect_bd_intf_net -intf_net axi_interconnect_0_M00_AXI [get_bd_intf_pins axi3_axi4_converter/S_AXI] [
get_bd_intf_pins socius_xz_bram_interconnect/M00_AXI]
connect_bd_intf_net -intf_net axi_interconnect_0_M01_AXI [get_bd_intf_pins axi3_axi4_converter1/S_AXI] [
get_bd_intf_pins socius_xz_bram_interconnect/M01_AXI]
connect_bd_intf_net -intf_net axi_interconnect_0_M02_AXI [get_bd_intf_pins axi3_axi4_converter2/S_AXI] [
get_bd_intf_pins socius_xz_bram_interconnect/M02_AXI]
connect_bd_intf_net -intf_net axi_interconnect_0_M03_AXI [get_bd_intf_pins axi3_axi4_converter3/S_AXI] [
get_bd_intf_pins socius_xz_bram_interconnect/M03_AXI]
connect_bd_intf_net -intf_net socius_xz_bram_if_pl_BRAM_PORTA [get_bd_intf_pins bram_soc] [
get_bd_intf_pins socius_xz_bram_if_pl_soc/BRAM_PORTA]
connect_bd_intf_net -intf_net socius_xz_bram_if_pl_BRAM_PORTA1 [get_bd_intf_pins bram_mid] [
get_bd_intf_pins socius_xz_bram_if_pl_mid/BRAM_PORTA]
connect_bd_intf_net -intf_net socius_xz_bram_if_pl_BRAM_PORTA2 [get_bd_intf_pins bram_top] [
get_bd_intf_pins socius_xz_bram_if_pl_top/BRAM_PORTA]
connect_bd_intf_net -intf_net socius_xz_bram_if_pl_BRAM_PORTA3 [get_bd_intf_pins bram_bot] [
get_bd_intf_pins socius_xz_bram_if_pl_bot/BRAM_PORTA]

# Create port connections


connect_bd_net -net ACLK_1 [get_bd_pins S00_ACLK] [get_bd_pins axi3_axi4_converter/aclk] [get_bd_pins
axi3_axi4_converter1/aclk] [get_bd_pins axi3_axi4_converter2/aclk] [get_bd_pins axi3_axi4_converter3/aclk] [
get_bd_pins socius_xz_bram_if_pl_bot/s_axi_aclk] [get_bd_pins socius_xz_bram_if_pl_mid/s_axi_aclk] [get_bd_pins

71
CREATING THE HARDWARE PLATFORM

socius_xz_bram_if_pl_soc/s_axi_aclk] [get_bd_pins socius_xz_bram_if_pl_top/s_axi_aclk] [get_bd_pins


socius_xz_bram_interconnect/ACLK] [get_bd_pins socius_xz_bram_interconnect/M00_ACLK] [get_bd_pins
socius_xz_bram_interconnect/M01_ACLK] [get_bd_pins socius_xz_bram_interconnect/M02_ACLK] [get_bd_pins
socius_xz_bram_interconnect/M03_ACLK] [get_bd_pins socius_xz_bram_interconnect/S00_ACLK]
connect_bd_net -net ARESETN_1 [get_bd_pins M01_ARESETN] [get_bd_pins socius_xz_bram_interconnect/ARESETN]
[get_bd_pins socius_xz_bram_interconnect/M00_ARESETN] [get_bd_pins socius_xz_bram_interconnect/M01_ARESETN]
[get_bd_pins socius_xz_bram_interconnect/M02_ARESETN] [get_bd_pins socius_xz_bram_interconnect/M03_ARESETN]
[get_bd_pins socius_xz_bram_interconnect/S00_ARESETN]
connect_bd_net -net xlconstant_0_dout [get_bd_pins axi3_axi4_converter/aresetn] [get_bd_pins
socius_xz_bram_if_pl_soc/s_axi_aresetn] [get_bd_pins xlconstant_1/dout]
connect_bd_net -net xlconstant_0_dout1 [get_bd_pins axi3_axi4_converter1/aresetn] [get_bd_pins
socius_xz_bram_if_pl_mid/s_axi_aresetn] [get_bd_pins xlconstant_2/dout]
connect_bd_net -net xlconstant_0_dout2 [get_bd_pins axi3_axi4_converter2/aresetn] [get_bd_pins
socius_xz_bram_if_pl_top/s_axi_aresetn] [get_bd_pins xlconstant_3/dout]
connect_bd_net -net xlconstant_0_dout3 [get_bd_pins axi3_axi4_converter3/aresetn] [get_bd_pins
socius_xz_bram_if_pl_bot/s_axi_aresetn] [get_bd_pins xlconstant_4/dout]

# Restore current instance


current_bd_instance $oldCurInst
}

# Hierarchical cell: socius_xz_io_ps


proc create_hier_cell_socius_xz_io_ps { parentCell nameHier } {

if { $parentCell eq "" || $nameHier eq "" } {


puts "ERROR: create_hier_cell_socius_xz_io_ps() - Empty argument(s)!"
return
}

# Get object for parentCell


set parentObj [get_bd_cells $parentCell]
if { $parentObj == "" } {
puts "ERROR: Unable to find parent cell <$parentCell>!"
return
}

# Make sure parentObj is hier blk


set parentType [get_property TYPE $parentObj]
if { $parentType ne "hier" } {
puts "ERROR: Parent <$parentObj> has TYPE = <$parentType>. Expected to be <hier>."
return
}

# Save current instance; Restore later


set oldCurInst [current_bd_instance .]

# Set parent object as current


current_bd_instance $parentObj

# Create cell and set as current instance


set hier_obj [create_bd_cell -type hier $nameHier]
current_bd_instance $hier_obj

# Create interface pins


create_bd_intf_pin -mode Master -vlnv xilinx.com:interface:ddrx_rtl:1.0 ddr
create_bd_intf_pin -mode Master -vlnv xilinx.com:display_processing_system7:fixedio_rtl:1.0 fixed_io
create_bd_intf_pin -mode Master -vlnv xilinx.com:interface:iic_rtl:1.0 iic_1
create_bd_intf_pin -mode Master -vlnv xilinx.com:interface:bram_rtl:1.0 pl_bram_bot
create_bd_intf_pin -mode Master -vlnv xilinx.com:interface:bram_rtl:1.0 pl_bram_mid
create_bd_intf_pin -mode Master -vlnv xilinx.com:interface:bram_rtl:1.0 pl_bram_soc
create_bd_intf_pin -mode Master -vlnv xilinx.com:interface:bram_rtl:1.0 pl_bram_top
create_bd_intf_pin -mode Master -vlnv xilinx.com:interface:sdio_rtl:1.0 sdio_0
create_bd_intf_pin -mode Master -vlnv xilinx.com:interface:spi_rtl:1.0 spi_0
create_bd_intf_pin -mode Master -vlnv xilinx.com:interface:uart_rtl:1.0 uart_1
create_bd_intf_pin -mode Master -vlnv xilinx.com:display_processing_system7:usbctrl_rtl:1.0 usbind_0

# Create pins
create_bd_pin -dir O -type clk fclk_clk0
create_bd_pin -dir O -type clk fclk_clk1
create_bd_pin -dir O -type clk fclk_clk2
create_bd_pin -dir O -type clk fclk_clk3
create_bd_pin -dir O -type rst fclk_reset0_n
create_bd_pin -dir I -from 0 -to 0 pl_int_bot
create_bd_pin -dir I -from 0 -to 0 pl_int_mid
create_bd_pin -dir I -from 0 -to 0 pl_int_soc
create_bd_pin -dir I -from 0 -to 0 pl_int_top

# Create instance: socius_xz_bram_switch_bd


create_hier_cell_socius_xz_bram_switch_bd $hier_obj socius_xz_bram_switch_bd

# Create instance: socius_xz_io_ps_bd, and set properties


set socius_xz_io_ps_bd [ create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7:5.5
socius_xz_io_ps_bd ]
set_property -dict [ list \
CONFIG.PCW_ACT_APU_PERIPHERAL_FREQMHZ {666.666687} \
CONFIG.PCW_ACT_CAN0_PERIPHERAL_FREQMHZ {23.8095} \
CONFIG.PCW_ACT_CAN1_PERIPHERAL_FREQMHZ {23.8095} \
CONFIG.PCW_ACT_CAN_PERIPHERAL_FREQMHZ {10.000000} \
CONFIG.PCW_ACT_DCI_PERIPHERAL_FREQMHZ {10.158731} \
CONFIG.PCW_ACT_ENET0_PERIPHERAL_FREQMHZ {125.000000} \
CONFIG.PCW_ACT_ENET1_PERIPHERAL_FREQMHZ {10.000000} \

72
2.5 Create a socius board based hardware platform

CONFIG.PCW_ACT_FPGA0_PERIPHERAL_FREQMHZ {50.000000} \
CONFIG.PCW_ACT_FPGA1_PERIPHERAL_FREQMHZ {100.000000} \
CONFIG.PCW_ACT_FPGA2_PERIPHERAL_FREQMHZ {125.000000} \
CONFIG.PCW_ACT_FPGA3_PERIPHERAL_FREQMHZ {200.000000} \
CONFIG.PCW_ACT_I2C_PERIPHERAL_FREQMHZ {50} \
CONFIG.PCW_ACT_PCAP_PERIPHERAL_FREQMHZ {200.000000} \
CONFIG.PCW_ACT_QSPI_PERIPHERAL_FREQMHZ {200.000000} \
CONFIG.PCW_ACT_SDIO_PERIPHERAL_FREQMHZ {100.000000} \
CONFIG.PCW_ACT_SMC_PERIPHERAL_FREQMHZ {10.000000} \
CONFIG.PCW_ACT_SPI_PERIPHERAL_FREQMHZ {166.666672} \
CONFIG.PCW_ACT_TPIU_PERIPHERAL_FREQMHZ {200.000000} \
CONFIG.PCW_ACT_TTC0_CLK0_PERIPHERAL_FREQMHZ {111.111115} \
CONFIG.PCW_ACT_TTC0_CLK1_PERIPHERAL_FREQMHZ {111.111115} \
CONFIG.PCW_ACT_TTC0_CLK2_PERIPHERAL_FREQMHZ {111.111115} \
CONFIG.PCW_ACT_TTC1_CLK0_PERIPHERAL_FREQMHZ {111.111115} \
CONFIG.PCW_ACT_TTC1_CLK1_PERIPHERAL_FREQMHZ {111.111115} \
CONFIG.PCW_ACT_TTC1_CLK2_PERIPHERAL_FREQMHZ {111.111115} \
CONFIG.PCW_ACT_TTC_PERIPHERAL_FREQMHZ {50} \
CONFIG.PCW_ACT_UART_PERIPHERAL_FREQMHZ {100.000000} \
CONFIG.PCW_ACT_USB0_PERIPHERAL_FREQMHZ {60} \
CONFIG.PCW_ACT_USB1_PERIPHERAL_FREQMHZ {60} \
CONFIG.PCW_ACT_WDT_PERIPHERAL_FREQMHZ {111.111115} \
CONFIG.PCW_APU_CLK_RATIO_ENABLE {6:2:1} \
CONFIG.PCW_APU_PERIPHERAL_FREQMHZ {666.666666} \
CONFIG.PCW_CAN0_PERIPHERAL_CLKSRC {External} \
CONFIG.PCW_CAN0_PERIPHERAL_ENABLE {0} \
CONFIG.PCW_CAN1_PERIPHERAL_CLKSRC {External} \
CONFIG.PCW_CAN1_PERIPHERAL_ENABLE {0} \
CONFIG.PCW_CAN_PERIPHERAL_CLKSRC {IO PLL} \
CONFIG.PCW_CAN_PERIPHERAL_VALID {0} \
CONFIG.PCW_CLK0_FREQ {50000000} \
CONFIG.PCW_CLK1_FREQ {100000000} \
CONFIG.PCW_CLK2_FREQ {125000000} \
CONFIG.PCW_CLK3_FREQ {200000000} \
CONFIG.PCW_CPU_CPU_6X4X_MAX_RANGE {667} \
CONFIG.PCW_CPU_PERIPHERAL_CLKSRC {ARM PLL} \
CONFIG.PCW_CRYSTAL_PERIPHERAL_FREQMHZ {33.333333} \
CONFIG.PCW_DCI_PERIPHERAL_CLKSRC {DDR PLL} \
CONFIG.PCW_DCI_PERIPHERAL_FREQMHZ {10.159} \
CONFIG.PCW_DDR_PERIPHERAL_CLKSRC {DDR PLL} \
CONFIG.PCW_DDR_RAM_BASEADDR {0x00100000} \
CONFIG.PCW_DDR_RAM_HIGHADDR {0x3FFFFFFF} \
CONFIG.PCW_DM_WIDTH {4} \
CONFIG.PCW_DQS_WIDTH {4} \
CONFIG.PCW_DQ_WIDTH {32} \
CONFIG.PCW_ENET0_BASEADDR {0xE000B000} \
CONFIG.PCW_ENET0_ENET0_IO {MIO 16 .. 27} \
CONFIG.PCW_ENET0_GRP_MDIO_ENABLE {1} \
CONFIG.PCW_ENET0_GRP_MDIO_IO {MIO 52 .. 53} \
CONFIG.PCW_ENET0_HIGHADDR {0xE000BFFF} \
CONFIG.PCW_ENET0_PERIPHERAL_CLKSRC {IO PLL} \
CONFIG.PCW_ENET0_PERIPHERAL_ENABLE {1} \
CONFIG.PCW_ENET0_PERIPHERAL_FREQMHZ {1000 Mbps} \
CONFIG.PCW_ENET1_PERIPHERAL_CLKSRC {IO PLL} \
CONFIG.PCW_ENET1_PERIPHERAL_ENABLE {0} \
CONFIG.PCW_ENET_RESET_ENABLE {0} \
CONFIG.PCW_ENET_RESET_POLARITY {Active Low} \
CONFIG.PCW_EN_4K_TIMER {0} \
CONFIG.PCW_EN_CAN0 {0} \
CONFIG.PCW_EN_CAN1 {0} \
CONFIG.PCW_EN_CLK0_PORT {1} \
CONFIG.PCW_EN_CLK1_PORT {1} \
CONFIG.PCW_EN_CLK2_PORT {1} \
CONFIG.PCW_EN_CLK3_PORT {1} \
CONFIG.PCW_EN_CLKTRIG0_PORT {0} \
CONFIG.PCW_EN_CLKTRIG1_PORT {0} \
CONFIG.PCW_EN_CLKTRIG2_PORT {0} \
CONFIG.PCW_EN_CLKTRIG3_PORT {0} \
CONFIG.PCW_EN_DDR {1} \
CONFIG.PCW_EN_EMIO_CAN0 {0} \
CONFIG.PCW_EN_EMIO_CAN1 {0} \
CONFIG.PCW_EN_EMIO_CD_SDIO0 {1} \
CONFIG.PCW_EN_EMIO_CD_SDIO1 {0} \
CONFIG.PCW_EN_EMIO_ENET0 {0} \
CONFIG.PCW_EN_EMIO_ENET1 {0} \
CONFIG.PCW_EN_EMIO_GPIO {0} \
CONFIG.PCW_EN_EMIO_I2C0 {0} \
CONFIG.PCW_EN_EMIO_I2C1 {1} \
CONFIG.PCW_EN_EMIO_MODEM_UART0 {0} \
CONFIG.PCW_EN_EMIO_MODEM_UART1 {0} \
CONFIG.PCW_EN_EMIO_PJTAG {0} \
CONFIG.PCW_EN_EMIO_SDIO0 {0} \
CONFIG.PCW_EN_EMIO_SDIO1 {0} \
CONFIG.PCW_EN_EMIO_SPI0 {1} \
CONFIG.PCW_EN_EMIO_SPI1 {0} \
CONFIG.PCW_EN_EMIO_SRAM_INT {0} \
CONFIG.PCW_EN_EMIO_TRACE {0} \
CONFIG.PCW_EN_EMIO_TTC0 {0} \
CONFIG.PCW_EN_EMIO_TTC1 {0} \
CONFIG.PCW_EN_EMIO_UART0 {0} \
CONFIG.PCW_EN_EMIO_UART1 {1} \

73
CREATING THE HARDWARE PLATFORM

CONFIG.PCW_EN_EMIO_WDT {1} \
CONFIG.PCW_EN_EMIO_WP_SDIO0 {0} \
CONFIG.PCW_EN_EMIO_WP_SDIO1 {0} \
CONFIG.PCW_EN_ENET0 {1} \
CONFIG.PCW_EN_ENET1 {0} \
CONFIG.PCW_EN_GPIO {1} \
CONFIG.PCW_EN_I2C0 {1} \
CONFIG.PCW_EN_I2C1 {1} \
CONFIG.PCW_EN_MODEM_UART0 {0} \
CONFIG.PCW_EN_MODEM_UART1 {0} \
CONFIG.PCW_EN_PJTAG {0} \
CONFIG.PCW_EN_QSPI {1} \
CONFIG.PCW_EN_RST0_PORT {1} \
CONFIG.PCW_EN_RST1_PORT {0} \
CONFIG.PCW_EN_RST2_PORT {0} \
CONFIG.PCW_EN_RST3_PORT {0} \
CONFIG.PCW_EN_SDIO0 {1} \
CONFIG.PCW_EN_SDIO1 {0} \
CONFIG.PCW_EN_SMC {0} \
CONFIG.PCW_EN_SPI0 {1} \
CONFIG.PCW_EN_SPI1 {0} \
CONFIG.PCW_EN_TRACE {0} \
CONFIG.PCW_EN_TTC0 {0} \
CONFIG.PCW_EN_TTC1 {0} \
CONFIG.PCW_EN_UART0 {1} \
CONFIG.PCW_EN_UART1 {1} \
CONFIG.PCW_EN_USB0 {1} \
CONFIG.PCW_EN_USB1 {0} \
CONFIG.PCW_EN_WDT {1} \
CONFIG.PCW_FCLK0_PERIPHERAL_CLKSRC {IO PLL} \
CONFIG.PCW_FCLK1_PERIPHERAL_CLKSRC {IO PLL} \
CONFIG.PCW_FCLK2_PERIPHERAL_CLKSRC {IO PLL} \
CONFIG.PCW_FCLK3_PERIPHERAL_CLKSRC {IO PLL} \
CONFIG.PCW_FCLK_CLK0_BUF {true} \
CONFIG.PCW_FPGA0_PERIPHERAL_FREQMHZ {50} \
CONFIG.PCW_FPGA1_PERIPHERAL_FREQMHZ {100} \
CONFIG.PCW_FPGA2_PERIPHERAL_FREQMHZ {125} \
CONFIG.PCW_FPGA3_PERIPHERAL_FREQMHZ {200} \
CONFIG.PCW_FPGA_FCLK0_ENABLE {1} \
CONFIG.PCW_GPIO_BASEADDR {0xE000A000} \
CONFIG.PCW_GPIO_EMIO_GPIO_ENABLE {0} \
CONFIG.PCW_GPIO_HIGHADDR {0xE000AFFF} \
CONFIG.PCW_GPIO_MIO_GPIO_ENABLE {1} \
CONFIG.PCW_GPIO_MIO_GPIO_IO {MIO} \
CONFIG.PCW_GPIO_PERIPHERAL_ENABLE {0} \
CONFIG.PCW_I2C0_BASEADDR {0xE0004000} \
CONFIG.PCW_I2C0_GRP_INT_ENABLE {0} \
CONFIG.PCW_I2C0_HIGHADDR {0xE0004FFF} \
CONFIG.PCW_I2C0_I2C0_IO {MIO 14 .. 15} \
CONFIG.PCW_I2C0_PERIPHERAL_ENABLE {1} \
CONFIG.PCW_I2C1_PERIPHERAL_ENABLE {1} \
CONFIG.PCW_I2C_PERIPHERAL_FREQMHZ {111.111115} \
CONFIG.PCW_I2C_RESET_ENABLE {0} \
CONFIG.PCW_I2C_RESET_POLARITY {Active Low} \
CONFIG.PCW_IMPORT_BOARD_PRESET {None} \
CONFIG.PCW_INCLUDE_ACP_TRANS_CHECK {0} \
CONFIG.PCW_IRQ_F2P_INTR {1} \
CONFIG.PCW_MIO_0_IOTYPE {LVCMOS 3.3V} \
CONFIG.PCW_MIO_0_PULLUP {enabled} \
CONFIG.PCW_MIO_0_SLEW {slow} \
CONFIG.PCW_MIO_10_IOTYPE {LVCMOS 3.3V} \
CONFIG.PCW_MIO_10_PULLUP {enabled} \
CONFIG.PCW_MIO_10_SLEW {slow} \
CONFIG.PCW_MIO_11_IOTYPE {LVCMOS 3.3V} \
CONFIG.PCW_MIO_11_PULLUP {enabled} \
CONFIG.PCW_MIO_11_SLEW {slow} \
CONFIG.PCW_MIO_12_IOTYPE {LVCMOS 3.3V} \
CONFIG.PCW_MIO_12_PULLUP {enabled} \
CONFIG.PCW_MIO_12_SLEW {slow} \
CONFIG.PCW_MIO_13_IOTYPE {LVCMOS 3.3V} \
CONFIG.PCW_MIO_13_PULLUP {enabled} \
CONFIG.PCW_MIO_13_SLEW {slow} \
CONFIG.PCW_MIO_14_IOTYPE {LVCMOS 3.3V} \
CONFIG.PCW_MIO_14_PULLUP {enabled} \
CONFIG.PCW_MIO_14_SLEW {slow} \
CONFIG.PCW_MIO_15_IOTYPE {LVCMOS 3.3V} \
CONFIG.PCW_MIO_15_PULLUP {enabled} \
CONFIG.PCW_MIO_15_SLEW {slow} \
CONFIG.PCW_MIO_16_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_16_PULLUP {enabled} \
CONFIG.PCW_MIO_16_SLEW {slow} \
CONFIG.PCW_MIO_17_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_17_PULLUP {enabled} \
CONFIG.PCW_MIO_17_SLEW {slow} \
CONFIG.PCW_MIO_18_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_18_PULLUP {enabled} \
CONFIG.PCW_MIO_18_SLEW {slow} \
CONFIG.PCW_MIO_19_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_19_PULLUP {enabled} \
CONFIG.PCW_MIO_19_SLEW {slow} \
CONFIG.PCW_MIO_1_IOTYPE {LVCMOS 3.3V} \

74
2.5 Create a socius board based hardware platform

CONFIG.PCW_MIO_1_PULLUP {enabled} \
CONFIG.PCW_MIO_1_SLEW {slow} \
CONFIG.PCW_MIO_20_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_20_PULLUP {enabled} \
CONFIG.PCW_MIO_20_SLEW {slow} \
CONFIG.PCW_MIO_21_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_21_PULLUP {enabled} \
CONFIG.PCW_MIO_21_SLEW {slow} \
CONFIG.PCW_MIO_22_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_22_PULLUP {enabled} \
CONFIG.PCW_MIO_22_SLEW {slow} \
CONFIG.PCW_MIO_23_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_23_PULLUP {enabled} \
CONFIG.PCW_MIO_23_SLEW {slow} \
CONFIG.PCW_MIO_24_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_24_PULLUP {enabled} \
CONFIG.PCW_MIO_24_SLEW {slow} \
CONFIG.PCW_MIO_25_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_25_PULLUP {enabled} \
CONFIG.PCW_MIO_25_SLEW {slow} \
CONFIG.PCW_MIO_26_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_26_PULLUP {enabled} \
CONFIG.PCW_MIO_26_SLEW {slow} \
CONFIG.PCW_MIO_27_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_27_PULLUP {enabled} \
CONFIG.PCW_MIO_27_SLEW {slow} \
CONFIG.PCW_MIO_28_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_28_PULLUP {enabled} \
CONFIG.PCW_MIO_28_SLEW {slow} \
CONFIG.PCW_MIO_29_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_29_PULLUP {enabled} \
CONFIG.PCW_MIO_29_SLEW {slow} \
CONFIG.PCW_MIO_2_IOTYPE {LVCMOS 3.3V} \
CONFIG.PCW_MIO_2_SLEW {slow} \
CONFIG.PCW_MIO_30_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_30_PULLUP {enabled} \
CONFIG.PCW_MIO_30_SLEW {slow} \
CONFIG.PCW_MIO_31_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_31_PULLUP {enabled} \
CONFIG.PCW_MIO_31_SLEW {slow} \
CONFIG.PCW_MIO_32_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_32_PULLUP {enabled} \
CONFIG.PCW_MIO_32_SLEW {slow} \
CONFIG.PCW_MIO_33_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_33_PULLUP {enabled} \
CONFIG.PCW_MIO_33_SLEW {slow} \
CONFIG.PCW_MIO_34_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_34_PULLUP {enabled} \
CONFIG.PCW_MIO_34_SLEW {slow} \
CONFIG.PCW_MIO_35_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_35_PULLUP {enabled} \
CONFIG.PCW_MIO_35_SLEW {slow} \
CONFIG.PCW_MIO_36_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_36_PULLUP {enabled} \
CONFIG.PCW_MIO_36_SLEW {slow} \
CONFIG.PCW_MIO_37_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_37_PULLUP {enabled} \
CONFIG.PCW_MIO_37_SLEW {slow} \
CONFIG.PCW_MIO_38_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_38_PULLUP {enabled} \
CONFIG.PCW_MIO_38_SLEW {slow} \
CONFIG.PCW_MIO_39_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_39_PULLUP {enabled} \
CONFIG.PCW_MIO_39_SLEW {slow} \
CONFIG.PCW_MIO_3_IOTYPE {LVCMOS 3.3V} \
CONFIG.PCW_MIO_3_SLEW {slow} \
CONFIG.PCW_MIO_40_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_40_PULLUP {enabled} \
CONFIG.PCW_MIO_40_SLEW {slow} \
CONFIG.PCW_MIO_41_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_41_PULLUP {enabled} \
CONFIG.PCW_MIO_41_SLEW {slow} \
CONFIG.PCW_MIO_42_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_42_PULLUP {enabled} \
CONFIG.PCW_MIO_42_SLEW {slow} \
CONFIG.PCW_MIO_43_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_43_PULLUP {enabled} \
CONFIG.PCW_MIO_43_SLEW {slow} \
CONFIG.PCW_MIO_44_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_44_PULLUP {enabled} \
CONFIG.PCW_MIO_44_SLEW {slow} \
CONFIG.PCW_MIO_45_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_45_PULLUP {enabled} \
CONFIG.PCW_MIO_45_SLEW {slow} \
CONFIG.PCW_MIO_46_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_46_PULLUP {enabled} \
CONFIG.PCW_MIO_46_SLEW {slow} \
CONFIG.PCW_MIO_47_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_47_PULLUP {enabled} \
CONFIG.PCW_MIO_47_SLEW {slow} \
CONFIG.PCW_MIO_48_IOTYPE {LVCMOS 1.8V} \

75
CREATING THE HARDWARE PLATFORM

CONFIG.PCW_MIO_48_PULLUP {enabled} \
CONFIG.PCW_MIO_48_SLEW {slow} \
CONFIG.PCW_MIO_49_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_49_PULLUP {enabled} \
CONFIG.PCW_MIO_49_SLEW {slow} \
CONFIG.PCW_MIO_4_IOTYPE {LVCMOS 3.3V} \
CONFIG.PCW_MIO_4_SLEW {slow} \
CONFIG.PCW_MIO_50_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_50_PULLUP {enabled} \
CONFIG.PCW_MIO_50_SLEW {slow} \
CONFIG.PCW_MIO_51_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_51_PULLUP {enabled} \
CONFIG.PCW_MIO_51_SLEW {slow} \
CONFIG.PCW_MIO_52_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_52_PULLUP {enabled} \
CONFIG.PCW_MIO_52_SLEW {slow} \
CONFIG.PCW_MIO_53_IOTYPE {LVCMOS 1.8V} \
CONFIG.PCW_MIO_53_PULLUP {enabled} \
CONFIG.PCW_MIO_53_SLEW {slow} \
CONFIG.PCW_MIO_5_IOTYPE {LVCMOS 3.3V} \
CONFIG.PCW_MIO_5_SLEW {slow} \
CONFIG.PCW_MIO_6_IOTYPE {LVCMOS 3.3V} \
CONFIG.PCW_MIO_6_SLEW {slow} \
CONFIG.PCW_MIO_7_IOTYPE {LVCMOS 3.3V} \
CONFIG.PCW_MIO_7_SLEW {slow} \
CONFIG.PCW_MIO_8_IOTYPE {LVCMOS 3.3V} \
CONFIG.PCW_MIO_8_SLEW {slow} \
CONFIG.PCW_MIO_9_IOTYPE {LVCMOS 3.3V} \
CONFIG.PCW_MIO_9_PULLUP {enabled} \
CONFIG.PCW_MIO_9_SLEW {slow} \
CONFIG.PCW_MIO_PRIMITIVE {54} \
CONFIG.PCW_MIO_TREE_PERIPHERALS {GPIO#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI
Flash#Quad SPI Flash#GPIO#GPIO#GPIO#GPIO#GPIO#GPIO#GPIO#I2C 0#I2C 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet
0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB
0#USB 0#SD 0#SD 0#SD 0#SD 0#SD 0#SD 0#GPIO#GPIO#GPIO#GPIO#UART 0#UART 0#Enet 0#Enet 0} \
CONFIG.PCW_MIO_TREE_SIGNALS {gpio[0]#qspi0_ss_b#qspi0_io[0]#qspi0_io[1]#qspi0_io[2]#qspi0_io[3]#qspi0_sclk#
gpio[7]#gpio[8]#gpio[9]#gpio[10]#gpio[11]#gpio[12]#gpio[13]#scl#sda#tx_clk#txd[0]#txd[1]#txd[2]#txd[3]#
tx_ctl#rx_clk#rxd[0]#rxd[1]#rxd[2]#rxd[3]#rx_ctl#data[4]#dir#stp#nxt#data[0]#data[1]#data[2]#data[3]#clk#data[5]#
data[6]#data[7]#clk#cmd#data[0]#data[1]#data[2]#data[3]#gpio[46]#gpio[47]#gpio[48]#gpio[49]#rx#tx#mdc#mdio}
\
CONFIG.PCW_NAND_CYCLES_T_AR {1} \
CONFIG.PCW_NAND_CYCLES_T_CLR {1} \
CONFIG.PCW_NAND_CYCLES_T_RC {2} \
CONFIG.PCW_NAND_CYCLES_T_REA {1} \
CONFIG.PCW_NAND_CYCLES_T_RR {1} \
CONFIG.PCW_NAND_CYCLES_T_WC {2} \
CONFIG.PCW_NAND_CYCLES_T_WP {1} \
CONFIG.PCW_NOR_CS0_T_CEOE {1} \
CONFIG.PCW_NOR_CS0_T_PC {1} \
CONFIG.PCW_NOR_CS0_T_RC {2} \
CONFIG.PCW_NOR_CS0_T_TR {1} \
CONFIG.PCW_NOR_CS0_T_WC {2} \
CONFIG.PCW_NOR_CS0_T_WP {1} \
CONFIG.PCW_NOR_CS0_WE_TIME {0} \
CONFIG.PCW_NOR_CS1_T_CEOE {1} \
CONFIG.PCW_NOR_CS1_T_PC {1} \
CONFIG.PCW_NOR_CS1_T_RC {2} \
CONFIG.PCW_NOR_CS1_T_TR {1} \
CONFIG.PCW_NOR_CS1_T_WC {2} \
CONFIG.PCW_NOR_CS1_T_WP {1} \
CONFIG.PCW_NOR_CS1_WE_TIME {0} \
CONFIG.PCW_NOR_SRAM_CS0_T_CEOE {1} \
CONFIG.PCW_NOR_SRAM_CS0_T_PC {1} \
CONFIG.PCW_NOR_SRAM_CS0_T_RC {2} \
CONFIG.PCW_NOR_SRAM_CS0_T_TR {1} \
CONFIG.PCW_NOR_SRAM_CS0_T_WC {2} \
CONFIG.PCW_NOR_SRAM_CS0_T_WP {1} \
CONFIG.PCW_NOR_SRAM_CS0_WE_TIME {0} \
CONFIG.PCW_NOR_SRAM_CS1_T_CEOE {1} \
CONFIG.PCW_NOR_SRAM_CS1_T_PC {1} \
CONFIG.PCW_NOR_SRAM_CS1_T_RC {2} \
CONFIG.PCW_NOR_SRAM_CS1_T_TR {1} \
CONFIG.PCW_NOR_SRAM_CS1_T_WC {2} \
CONFIG.PCW_NOR_SRAM_CS1_T_WP {1} \
CONFIG.PCW_NOR_SRAM_CS1_WE_TIME {0} \
CONFIG.PCW_OVERRIDE_BASIC_CLOCK {0} \
CONFIG.PCW_PACKAGE_DDR_BOARD_DELAY0 {0.089} \
CONFIG.PCW_PACKAGE_DDR_BOARD_DELAY1 {0.075} \
CONFIG.PCW_PACKAGE_DDR_BOARD_DELAY2 {0.085} \
CONFIG.PCW_PACKAGE_DDR_BOARD_DELAY3 {0.092} \
CONFIG.PCW_PACKAGE_DDR_DQS_TO_CLK_DELAY_0 {-0.025} \
CONFIG.PCW_PACKAGE_DDR_DQS_TO_CLK_DELAY_1 {0.014} \
CONFIG.PCW_PACKAGE_DDR_DQS_TO_CLK_DELAY_2 {-0.009} \
CONFIG.PCW_PACKAGE_DDR_DQS_TO_CLK_DELAY_3 {-0.033} \
CONFIG.PCW_PACKAGE_NAME {clg400} \
CONFIG.PCW_PCAP_PERIPHERAL_CLKSRC {IO PLL} \
CONFIG.PCW_PCAP_PERIPHERAL_FREQMHZ {200} \
CONFIG.PCW_PERIPHERAL_BOARD_PRESET {None} \
CONFIG.PCW_PJTAG_PERIPHERAL_ENABLE {0} \
CONFIG.PCW_PRESET_BANK0_VOLTAGE {LVCMOS 3.3V} \
CONFIG.PCW_PRESET_BANK1_VOLTAGE {LVCMOS 1.8V} \

76
2.5 Create a socius board based hardware platform

CONFIG.PCW_PS7_SI_REV {PRODUCTION} \
CONFIG.PCW_QSPI_GRP_FBCLK_ENABLE {0} \
CONFIG.PCW_QSPI_GRP_IO1_ENABLE {0} \
CONFIG.PCW_QSPI_GRP_SINGLE_SS_ENABLE {1} \
CONFIG.PCW_QSPI_GRP_SINGLE_SS_IO {MIO 1 .. 6} \
CONFIG.PCW_QSPI_GRP_SS1_ENABLE {0} \
CONFIG.PCW_QSPI_INTERNAL_HIGHADDRESS {0xFCFFFFFF} \
CONFIG.PCW_QSPI_PERIPHERAL_CLKSRC {IO PLL} \
CONFIG.PCW_QSPI_PERIPHERAL_ENABLE {1} \
CONFIG.PCW_QSPI_PERIPHERAL_FREQMHZ {200} \
CONFIG.PCW_QSPI_QSPI_IO {MIO 1 .. 6} \
CONFIG.PCW_SD0_GRP_CD_ENABLE {1} \
CONFIG.PCW_SD0_GRP_CD_IO {EMIO} \
CONFIG.PCW_SD0_GRP_POW_ENABLE {0} \
CONFIG.PCW_SD0_GRP_WP_ENABLE {0} \
CONFIG.PCW_SD0_PERIPHERAL_ENABLE {1} \
CONFIG.PCW_SD0_SD0_IO {MIO 40 .. 45} \
CONFIG.PCW_SD1_PERIPHERAL_ENABLE {0} \
CONFIG.PCW_SDIO0_BASEADDR {0xE0100000} \
CONFIG.PCW_SDIO0_HIGHADDR {0xE0100FFF} \
CONFIG.PCW_SDIO_PERIPHERAL_CLKSRC {IO PLL} \
CONFIG.PCW_SDIO_PERIPHERAL_FREQMHZ {100} \
CONFIG.PCW_SDIO_PERIPHERAL_VALID {1} \
CONFIG.PCW_SMC_CYCLE_T0 {NA} \
CONFIG.PCW_SMC_CYCLE_T1 {NA} \
CONFIG.PCW_SMC_CYCLE_T2 {NA} \
CONFIG.PCW_SMC_CYCLE_T3 {NA} \
CONFIG.PCW_SMC_CYCLE_T4 {NA} \
CONFIG.PCW_SMC_CYCLE_T5 {NA} \
CONFIG.PCW_SMC_CYCLE_T6 {NA} \
CONFIG.PCW_SMC_PERIPHERAL_CLKSRC {IO PLL} \
CONFIG.PCW_SMC_PERIPHERAL_VALID {0} \
CONFIG.PCW_SPI0_PERIPHERAL_ENABLE {1} \
CONFIG.PCW_SPI1_PERIPHERAL_ENABLE {0} \
CONFIG.PCW_SPI_PERIPHERAL_CLKSRC {IO PLL} \
CONFIG.PCW_SPI_PERIPHERAL_VALID {1} \
CONFIG.PCW_TPIU_PERIPHERAL_CLKSRC {External} \
CONFIG.PCW_TRACE_INTERNAL_WIDTH {2} \
CONFIG.PCW_TRACE_PERIPHERAL_ENABLE {0} \
CONFIG.PCW_TTC0_CLK0_PERIPHERAL_CLKSRC {CPU_1X} \
CONFIG.PCW_TTC0_CLK0_PERIPHERAL_DIVISOR0 {1} \
CONFIG.PCW_TTC0_CLK1_PERIPHERAL_CLKSRC {CPU_1X} \
CONFIG.PCW_TTC0_CLK1_PERIPHERAL_DIVISOR0 {1} \
CONFIG.PCW_TTC0_CLK2_PERIPHERAL_CLKSRC {CPU_1X} \
CONFIG.PCW_TTC0_CLK2_PERIPHERAL_DIVISOR0 {1} \
CONFIG.PCW_TTC0_PERIPHERAL_ENABLE {1} \
CONFIG.PCW_TTC1_CLK0_PERIPHERAL_CLKSRC {CPU_1X} \
CONFIG.PCW_TTC1_CLK0_PERIPHERAL_DIVISOR0 {1} \
CONFIG.PCW_TTC1_CLK1_PERIPHERAL_CLKSRC {CPU_1X} \
CONFIG.PCW_TTC1_CLK1_PERIPHERAL_DIVISOR0 {1} \
CONFIG.PCW_TTC1_CLK2_PERIPHERAL_CLKSRC {CPU_1X} \
CONFIG.PCW_TTC1_CLK2_PERIPHERAL_DIVISOR0 {1} \
CONFIG.PCW_TTC1_PERIPHERAL_ENABLE {0} \
CONFIG.PCW_UART0_BASEADDR {0xE0000000} \
CONFIG.PCW_UART0_BAUD_RATE {115200} \
CONFIG.PCW_UART0_GRP_FULL_ENABLE {0} \
CONFIG.PCW_UART0_HIGHADDR {0xE0000FFF} \
CONFIG.PCW_UART0_PERIPHERAL_ENABLE {1} \
CONFIG.PCW_UART0_UART0_IO {MIO 50 .. 51} \
CONFIG.PCW_UART1_PERIPHERAL_ENABLE {1} \
CONFIG.PCW_UART1_UART1_IO {EMIO} \
CONFIG.PCW_UART_PERIPHERAL_CLKSRC {IO PLL} \
CONFIG.PCW_UART_PERIPHERAL_FREQMHZ {100} \
CONFIG.PCW_UIPARAM_ACT_DDR_FREQ_MHZ {533.333374} \
CONFIG.PCW_UIPARAM_DDR_ADV_ENABLE {0} \
CONFIG.PCW_UIPARAM_DDR_AL {0} \
CONFIG.PCW_UIPARAM_DDR_BL {8} \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY0 {0.0} \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY1 {0.0} \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY2 {0.0} \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY3 {0.0} \
CONFIG.PCW_UIPARAM_DDR_BUS_WIDTH {32 Bit} \
CONFIG.PCW_UIPARAM_DDR_CLOCK_0_LENGTH_MM {0} \
CONFIG.PCW_UIPARAM_DDR_CLOCK_0_PACKAGE_LENGTH {80.4535} \
CONFIG.PCW_UIPARAM_DDR_CLOCK_0_PROPOGATION_DELAY {160} \
CONFIG.PCW_UIPARAM_DDR_CLOCK_1_LENGTH_MM {0} \
CONFIG.PCW_UIPARAM_DDR_CLOCK_1_PACKAGE_LENGTH {80.4535} \
CONFIG.PCW_UIPARAM_DDR_CLOCK_1_PROPOGATION_DELAY {160} \
CONFIG.PCW_UIPARAM_DDR_CLOCK_2_LENGTH_MM {0} \
CONFIG.PCW_UIPARAM_DDR_CLOCK_2_PACKAGE_LENGTH {80.4535} \
CONFIG.PCW_UIPARAM_DDR_CLOCK_2_PROPOGATION_DELAY {160} \
CONFIG.PCW_UIPARAM_DDR_CLOCK_3_LENGTH_MM {0} \
CONFIG.PCW_UIPARAM_DDR_CLOCK_3_PACKAGE_LENGTH {80.4535} \
CONFIG.PCW_UIPARAM_DDR_CLOCK_3_PROPOGATION_DELAY {160} \
CONFIG.PCW_UIPARAM_DDR_CLOCK_STOP_EN {0} \
CONFIG.PCW_UIPARAM_DDR_DQS_0_LENGTH_MM {0} \
CONFIG.PCW_UIPARAM_DDR_DQS_0_PACKAGE_LENGTH {105.056} \
CONFIG.PCW_UIPARAM_DDR_DQS_0_PROPOGATION_DELAY {160} \
CONFIG.PCW_UIPARAM_DDR_DQS_1_LENGTH_MM {0} \
CONFIG.PCW_UIPARAM_DDR_DQS_1_PACKAGE_LENGTH {66.904} \
CONFIG.PCW_UIPARAM_DDR_DQS_1_PROPOGATION_DELAY {160} \

77
CREATING THE HARDWARE PLATFORM

CONFIG.PCW_UIPARAM_DDR_DQS_2_LENGTH_MM {0} \
CONFIG.PCW_UIPARAM_DDR_DQS_2_PACKAGE_LENGTH {89.1715} \
CONFIG.PCW_UIPARAM_DDR_DQS_2_PROPOGATION_DELAY {160} \
CONFIG.PCW_UIPARAM_DDR_DQS_3_LENGTH_MM {0} \
CONFIG.PCW_UIPARAM_DDR_DQS_3_PACKAGE_LENGTH {113.63} \
CONFIG.PCW_UIPARAM_DDR_DQS_3_PROPOGATION_DELAY {160} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_0 {0.0} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_1 {0.0} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_2 {0.0} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_3 {0.0} \
CONFIG.PCW_UIPARAM_DDR_DQ_0_LENGTH_MM {0} \
CONFIG.PCW_UIPARAM_DDR_DQ_0_PACKAGE_LENGTH {98.503} \
CONFIG.PCW_UIPARAM_DDR_DQ_0_PROPOGATION_DELAY {160} \
CONFIG.PCW_UIPARAM_DDR_DQ_1_LENGTH_MM {0} \
CONFIG.PCW_UIPARAM_DDR_DQ_1_PACKAGE_LENGTH {68.5855} \
CONFIG.PCW_UIPARAM_DDR_DQ_1_PROPOGATION_DELAY {160} \
CONFIG.PCW_UIPARAM_DDR_DQ_2_LENGTH_MM {0} \
CONFIG.PCW_UIPARAM_DDR_DQ_2_PACKAGE_LENGTH {90.295} \
CONFIG.PCW_UIPARAM_DDR_DQ_2_PROPOGATION_DELAY {160} \
CONFIG.PCW_UIPARAM_DDR_DQ_3_LENGTH_MM {0} \
CONFIG.PCW_UIPARAM_DDR_DQ_3_PACKAGE_LENGTH {103.977} \
CONFIG.PCW_UIPARAM_DDR_DQ_3_PROPOGATION_DELAY {160} \
CONFIG.PCW_UIPARAM_DDR_ENABLE {1} \
CONFIG.PCW_UIPARAM_DDR_FREQ_MHZ {533.333333} \
CONFIG.PCW_UIPARAM_DDR_HIGH_TEMP {Normal (0-85)} \
CONFIG.PCW_UIPARAM_DDR_MEMORY_TYPE {DDR 3} \
CONFIG.PCW_UIPARAM_DDR_PARTNO {MT41K256M16 RE-125} \
CONFIG.PCW_UIPARAM_DDR_TRAIN_DATA_EYE {0} \
CONFIG.PCW_UIPARAM_DDR_TRAIN_READ_GATE {0} \
CONFIG.PCW_UIPARAM_DDR_TRAIN_WRITE_LEVEL {0} \
CONFIG.PCW_UIPARAM_DDR_USE_INTERNAL_VREF {0} \
CONFIG.PCW_UIPARAM_GENERATE_SUMMARY {NA} \
CONFIG.PCW_USB0_BASEADDR {0xE0102000} \
CONFIG.PCW_USB0_HIGHADDR {0xE0102fff} \
CONFIG.PCW_USB0_PERIPHERAL_ENABLE {1} \
CONFIG.PCW_USB0_USB0_IO {MIO 28 .. 39} \
CONFIG.PCW_USB1_PERIPHERAL_ENABLE {0} \
CONFIG.PCW_USB_RESET_ENABLE {0} \
CONFIG.PCW_USB_RESET_POLARITY {Active Low} \
CONFIG.PCW_USE_AXI_FABRIC_IDLE {0} \
CONFIG.PCW_USE_CORESIGHT {0} \
CONFIG.PCW_USE_CROSS_TRIGGER {0} \
CONFIG.PCW_USE_CR_FABRIC {1} \
CONFIG.PCW_USE_DDR_BYPASS {0} \
CONFIG.PCW_USE_DEBUG {0} \
CONFIG.PCW_USE_DMA0 {0} \
CONFIG.PCW_USE_DMA1 {0} \
CONFIG.PCW_USE_DMA2 {0} \
CONFIG.PCW_USE_DMA3 {0} \
CONFIG.PCW_USE_EXPANDED_IOP {0} \
CONFIG.PCW_USE_FABRIC_INTERRUPT {1} \
CONFIG.PCW_USE_HIGH_OCM {0} \
CONFIG.PCW_USE_M_AXI_GP0 {1} \
CONFIG.PCW_USE_M_AXI_GP1 {0} \
CONFIG.PCW_USE_PROC_EVENT_BUS {0} \
CONFIG.PCW_USE_PS_SLCR_REGISTERS {0} \
CONFIG.PCW_USE_S_AXI_ACP {0} \
CONFIG.PCW_USE_S_AXI_GP0 {0} \
CONFIG.PCW_USE_S_AXI_GP1 {0} \
CONFIG.PCW_USE_S_AXI_HP0 {1} \
CONFIG.PCW_USE_S_AXI_HP1 {1} \
CONFIG.PCW_USE_S_AXI_HP2 {1} \
CONFIG.PCW_USE_S_AXI_HP3 {1} \
CONFIG.PCW_USE_TRACE {0} \
CONFIG.PCW_VALUE_SILVERSION {3} \
CONFIG.PCW_WDT_PERIPHERAL_CLKSRC {CPU_1X} \
CONFIG.PCW_WDT_PERIPHERAL_DIVISOR0 {1} \
CONFIG.PCW_WDT_PERIPHERAL_ENABLE {1} \
] $socius_xz_io_ps_bd

# Create instance: xlconcat_0, and set properties


set xlconcat_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconcat:2.1 xlconcat_0 ]
set_property -dict [ list \
CONFIG.IN0_WIDTH {1} \
CONFIG.IN1_WIDTH {1} \
CONFIG.IN2_WIDTH {1} \
CONFIG.IN3_WIDTH {1} \
CONFIG.NUM_PORTS {4} \
] $xlconcat_0

# Create interface connections


connect_bd_intf_net -intf_net Conn1 [get_bd_intf_pins spi_0] [get_bd_intf_pins socius_xz_io_ps_bd/SPI_0]
connect_bd_intf_net -intf_net Conn2 [get_bd_intf_pins iic_1] [get_bd_intf_pins socius_xz_io_ps_bd/IIC_1]
connect_bd_intf_net -intf_net Conn3 [get_bd_intf_pins uart_1] [get_bd_intf_pins socius_xz_io_ps_bd/UART_1
]
connect_bd_intf_net -intf_net Conn12 [get_bd_intf_pins pl_bram_bot] [get_bd_intf_pins
socius_xz_bram_switch_bd/bram_bot]
connect_bd_intf_net -intf_net Conn13 [get_bd_intf_pins pl_bram_mid] [get_bd_intf_pins
socius_xz_bram_switch_bd/bram_mid]
connect_bd_intf_net -intf_net Conn14 [get_bd_intf_pins pl_bram_soc] [get_bd_intf_pins
socius_xz_bram_switch_bd/bram_soc]

78
2.5 Create a socius board based hardware platform

connect_bd_intf_net -intf_net Conn15 [get_bd_intf_pins pl_bram_top] [get_bd_intf_pins


socius_xz_bram_switch_bd/bram_top]
connect_bd_intf_net -intf_net processing_system7_0_DDR [get_bd_intf_pins ddr] [get_bd_intf_pins
socius_xz_io_ps_bd/DDR]
connect_bd_intf_net -intf_net processing_system7_0_FIXED_IO [get_bd_intf_pins fixed_io] [get_bd_intf_pins
socius_xz_io_ps_bd/FIXED_IO]
connect_bd_intf_net -intf_net processing_system7_0_SDIO_0 [get_bd_intf_pins sdio_0] [get_bd_intf_pins
socius_xz_io_ps_bd/SDIO_0]
connect_bd_intf_net -intf_net processing_system7_0_USBIND_0 [get_bd_intf_pins usbind_0] [get_bd_intf_pins
socius_xz_io_ps_bd/USBIND_0]
connect_bd_intf_net -intf_net socius_xz_io_ps_bd_M_AXI_GP0 [get_bd_intf_pins socius_xz_bram_switch_bd/
S00_AXI] [get_bd_intf_pins socius_xz_io_ps_bd/M_AXI_GP0]

# Create port connections


connect_bd_net -net pl_int_bot_1 [get_bd_pins pl_int_bot] [get_bd_pins xlconcat_0/In3]
connect_bd_net -net pl_int_mid_1 [get_bd_pins pl_int_mid] [get_bd_pins xlconcat_0/In1]
connect_bd_net -net pl_int_soc_1 [get_bd_pins pl_int_soc] [get_bd_pins xlconcat_0/In0]
connect_bd_net -net pl_int_top_1 [get_bd_pins pl_int_top] [get_bd_pins xlconcat_0/In2]
connect_bd_net -net socius_xz_io_ps_bd_FCLK_CLK0 [get_bd_pins fclk_clk0] [get_bd_pins socius_xz_io_ps_bd/
FCLK_CLK0]
connect_bd_net -net socius_xz_io_ps_bd_FCLK_CLK1 [get_bd_pins fclk_clk1] [get_bd_pins
socius_xz_bram_switch_bd/S00_ACLK] [get_bd_pins socius_xz_io_ps_bd/FCLK_CLK1] [get_bd_pins socius_xz_io_ps_bd/M_AXI_GP0_ACL
get_bd_pins socius_xz_io_ps_bd/S_AXI_HP0_ACLK] [get_bd_pins socius_xz_io_ps_bd/S_AXI_HP1_ACLK] [get_bd_pins
socius_xz_io_ps_bd/S_AXI_HP2_ACLK] [get_bd_pins socius_xz_io_ps_bd/S_AXI_HP3_ACLK]
connect_bd_net -net socius_xz_io_ps_bd_FCLK_CLK2 [get_bd_pins fclk_clk2] [get_bd_pins socius_xz_io_ps_bd/
FCLK_CLK2]
connect_bd_net -net socius_xz_io_ps_bd_FCLK_CLK3 [get_bd_pins fclk_clk3] [get_bd_pins socius_xz_io_ps_bd/
FCLK_CLK3]
connect_bd_net -net socius_xz_io_ps_bd_FCLK_RESET0_N [get_bd_pins fclk_reset0_n] [get_bd_pins
socius_xz_bram_switch_bd/M01_ARESETN] [get_bd_pins socius_xz_io_ps_bd/FCLK_RESET0_N]
connect_bd_net -net xlconcat_0_dout [get_bd_pins socius_xz_io_ps_bd/IRQ_F2P] [get_bd_pins xlconcat_0/dout
]

# Perform GUI Layout


regenerate_bd_layout -hierarchy [get_bd_cells /socius_xz_io_ps] -layout_string {
guistr: "# # String gsaved with Nlview 6.5.5 2015-06-26 bk=1.3371 VDI=38 GEI=35 GUI=JA:1.8
# -string -flagsOSRD
preplace port pl_bram_mid -pg 1 -y 320 -defaultsOSRD
preplace port fixed_io -pg 1 -y 70 -defaultsOSRD
preplace port fclk_reset0_n -pg 1 -y 410 -defaultsOSRD
preplace port pl_bram_bot -pg 1 -y 300 -defaultsOSRD
preplace port uart_1 -pg 1 -y 90 -defaultsOSRD
preplace port iic_1 -pg 1 -y 110 -defaultsOSRD
preplace port spi_0 -pg 1 -y 130 -defaultsOSRD
preplace port fclk_clk0 -pg 1 -y 190 -defaultsOSRD
preplace port sdio_0 -pg 1 -y 150 -defaultsOSRD
preplace port ddr -pg 1 -y 50 -defaultsOSRD
preplace port fclk_clk1 -pg 1 -y 210 -defaultsOSRD
preplace port fclk_clk2 -pg 1 -y 230 -defaultsOSRD
preplace port fclk_clk3 -pg 1 -y 250 -defaultsOSRD
preplace port usbind_0 -pg 1 -y 170 -defaultsOSRD
preplace port pl_bram_top -pg 1 -y 360 -defaultsOSRD
preplace port pl_bram_soc -pg 1 -y 340 -defaultsOSRD
preplace portBus pl_int_bot -pg 1 -y 330 -defaultsOSRD
preplace portBus pl_int_mid -pg 1 -y 290 -defaultsOSRD
preplace portBus pl_int_top -pg 1 -y 310 -defaultsOSRD
preplace portBus pl_int_soc -pg 1 -y 270 -defaultsOSRD
preplace inst xlconcat_0 -pg 1 -lvl 1 -y 300 -defaultsOSRD
preplace inst socius_xz_io_ps_bd -pg 1 -lvl 2 -y 170 -defaultsOSRD
preplace inst socius_xz_bram_switch_bd -pg 1 -lvl 3 -y 330 -defaultsOSRD
preplace netloc Conn1 1 2 2 NJ 120 NJ
preplace netloc processing_system7_0_DDR 1 2 2 NJ 40 NJ
preplace netloc Conn2 1 2 2 NJ 100 NJ
preplace netloc pl_int_top_1 1 0 1 N
preplace netloc Conn3 1 2 2 NJ 90 NJ
preplace netloc pl_int_bot_1 1 0 1 N
preplace netloc socius_xz_io_ps_bd_M_AXI_GP0 1 2 1 630
preplace netloc pl_int_mid_1 1 0 1 N
preplace netloc socius_xz_io_ps_bd_FCLK_CLK0 1 2 2 NJ 190 NJ
preplace netloc socius_xz_io_ps_bd_FCLK_CLK1 1 1 3 200 350 650 210 NJ
preplace netloc processing_system7_0_USBIND_0 1 2 2 NJ 160 NJ
preplace netloc xlconcat_0_dout 1 1 1 N
preplace netloc socius_xz_io_ps_bd_FCLK_CLK2 1 2 2 NJ 220 NJ
preplace netloc pl_int_soc_1 1 0 1 N
preplace netloc processing_system7_0_FIXED_IO 1 2 2 NJ 60 NJ
preplace netloc socius_xz_io_ps_bd_FCLK_CLK3 1 2 2 NJ 240 NJ
preplace netloc Conn12 1 3 1 NJ
preplace netloc Conn13 1 3 1 NJ
preplace netloc Conn14 1 3 1 NJ
preplace netloc processing_system7_0_SDIO_0 1 2 2 NJ 140 NJ
preplace netloc Conn15 1 3 1 NJ
preplace netloc socius_xz_io_ps_bd_FCLK_RESET0_N 1 2 2 620 410 NJ
levelinfo -pg 1 0 110 410 790 930 -top 0 -bot 430
",
}

# Restore current instance


current_bd_instance $oldCurInst
}

79
CREATING THE HARDWARE PLATFORM

# Procedure to create entire design; Provide argument to make


# procedure reusable. If parentCell is "", will use root.
proc create_root_design { parentCell } {

if { $parentCell eq "" } {
set parentCell [get_bd_cells /]
}

# Get object for parentCell


set parentObj [get_bd_cells $parentCell]
if { $parentObj == "" } {
puts "ERROR: Unable to find parent cell <$parentCell>!"
return
}

# Make sure parentObj is hier blk


set parentType [get_property TYPE $parentObj]
if { $parentType ne "hier" } {
puts "ERROR: Parent <$parentObj> has TYPE = <$parentType>. Expected to be <hier>."
return
}

# Save current instance; Restore later


set oldCurInst [current_bd_instance .]

# Set parent object as current


current_bd_instance $parentObj

# Create interface ports


set ddr3 [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:ddrx_rtl:1.0 ddr3 ]
set fixed_io [ create_bd_intf_port -mode Master -vlnv xilinx.com:display_processing_system7:fixedio_rtl:1
.0 fixed_io ]
set pl_bram_bot [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:bram_rtl:1.0 pl_bram_bot ]
set_property -dict [ list \
CONFIG.MASTER_TYPE {BRAM_CTRL} \
] $pl_bram_bot
set pl_bram_mid [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:bram_rtl:1.0 pl_bram_mid ]
set_property -dict [ list \
CONFIG.MASTER_TYPE {BRAM_CTRL} \
] $pl_bram_mid
set pl_bram_soc [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:bram_rtl:1.0 pl_bram_soc ]
set_property -dict [ list \
CONFIG.MASTER_TYPE {BRAM_CTRL} \
] $pl_bram_soc
set pl_bram_top [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:bram_rtl:1.0 pl_bram_top ]
set_property -dict [ list \
CONFIG.MASTER_TYPE {BRAM_CTRL} \
] $pl_bram_top
set pl_iic_1 [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:iic_rtl:1.0 pl_iic_1 ]
set pl_spi_0 [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:spi_rtl:1.0 pl_spi_0 ]
set pl_uart_1 [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:uart_rtl:1.0 pl_uart_1 ]
set sdio_0 [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:sdio_rtl:1.0 sdio_0 ]
set usbind_0 [ create_bd_intf_port -mode Master -vlnv xilinx.com:display_processing_system7:usbctrl_rtl:1
.0 usbind_0 ]

# Create ports
set pl_clk0 [ create_bd_port -dir O -type clk pl_clk0 ]
set pl_clk1 [ create_bd_port -dir O -type clk pl_clk1 ]
set pl_clk2 [ create_bd_port -dir O -type clk pl_clk2 ]
set pl_clk3 [ create_bd_port -dir O -type clk pl_clk3 ]
set pl_int_bot [ create_bd_port -dir I -from 0 -to 0 pl_int_bot ]
set pl_int_mid [ create_bd_port -dir I -from 0 -to 0 pl_int_mid ]
set pl_int_soc [ create_bd_port -dir I -from 0 -to 0 pl_int_soc ]
set pl_int_top [ create_bd_port -dir I -from 0 -to 0 pl_int_top ]
set pl_reset_n [ create_bd_port -dir O -type rst pl_reset_n ]

# Create instance: socius_xz_io_ps


create_hier_cell_socius_xz_io_ps [current_bd_instance .] socius_xz_io_ps

# Create interface connections


connect_bd_intf_net -intf_net processing_system7_0_DDR [get_bd_intf_ports ddr3] [get_bd_intf_pins
socius_xz_io_ps/ddr]
connect_bd_intf_net -intf_net processing_system7_0_FIXED_IO [get_bd_intf_ports fixed_io] [
get_bd_intf_pins socius_xz_io_ps/fixed_io]
connect_bd_intf_net -intf_net processing_system7_0_SDIO_0 [get_bd_intf_ports sdio_0] [get_bd_intf_pins
socius_xz_io_ps/sdio_0]
connect_bd_intf_net -intf_net processing_system7_0_USBIND_0 [get_bd_intf_ports usbind_0] [
get_bd_intf_pins socius_xz_io_ps/usbind_0]
connect_bd_intf_net -intf_net socius_xz_io_ps_IIC_1 [get_bd_intf_ports pl_iic_1] [get_bd_intf_pins
socius_xz_io_ps/iic_1]
connect_bd_intf_net -intf_net socius_xz_io_ps_SPI_0 [get_bd_intf_ports pl_spi_0] [get_bd_intf_pins
socius_xz_io_ps/spi_0]
connect_bd_intf_net -intf_net socius_xz_io_ps_UART_1 [get_bd_intf_ports pl_uart_1] [get_bd_intf_pins
socius_xz_io_ps/uart_1]
connect_bd_intf_net -intf_net socius_xz_io_ps_bram_bot [get_bd_intf_ports pl_bram_bot] [get_bd_intf_pins
socius_xz_io_ps/pl_bram_bot]
connect_bd_intf_net -intf_net socius_xz_io_ps_bram_mid [get_bd_intf_ports pl_bram_mid] [get_bd_intf_pins
socius_xz_io_ps/pl_bram_mid]
connect_bd_intf_net -intf_net socius_xz_io_ps_bram_soc [get_bd_intf_ports pl_bram_soc] [get_bd_intf_pins
socius_xz_io_ps/pl_bram_soc]

80
2.5 Create a socius board based hardware platform

connect_bd_intf_net -intf_net socius_xz_io_ps_bram_top [get_bd_intf_ports pl_bram_top] [get_bd_intf_pins


socius_xz_io_ps/pl_bram_top]

# Create port connections


connect_bd_net -net In0_1 [get_bd_ports pl_int_mid] [get_bd_pins socius_xz_io_ps/pl_int_mid]
connect_bd_net -net In1_2 [get_bd_ports pl_int_top] [get_bd_pins socius_xz_io_ps/pl_int_top]
connect_bd_net -net In2_2 [get_bd_ports pl_int_bot] [get_bd_pins socius_xz_io_ps/pl_int_bot]
connect_bd_net -net In3_1 [get_bd_ports pl_int_soc] [get_bd_pins socius_xz_io_ps/pl_int_soc]
connect_bd_net -net socius_xz_io_ps_bd_FCLK_CLK0 [get_bd_ports pl_clk0] [get_bd_pins socius_xz_io_ps/
fclk_clk0]
connect_bd_net -net socius_xz_io_ps_bd_FCLK_CLK1 [get_bd_ports pl_clk1] [get_bd_pins socius_xz_io_ps/
fclk_clk1]
connect_bd_net -net socius_xz_io_ps_bd_FCLK_CLK2 [get_bd_ports pl_clk2] [get_bd_pins socius_xz_io_ps/
fclk_clk2]
connect_bd_net -net socius_xz_io_ps_bd_FCLK_CLK3 [get_bd_ports pl_clk3] [get_bd_pins socius_xz_io_ps/
fclk_clk3]
connect_bd_net -net socius_xz_io_ps_bd_FCLK_RESET0_N [get_bd_ports pl_reset_n] [get_bd_pins
socius_xz_io_ps/fclk_reset0_n]

# Create address segments


create_bd_addr_seg -range 0x10000 -offset 0x40030000 [get_bd_addr_spaces socius_xz_io_ps/
socius_xz_io_ps_bd/Data] [get_bd_addr_segs socius_xz_io_ps/socius_xz_bram_switch_bd/socius_xz_bram_if_pl_bot/S_AXI/Mem0]
SEG_socius_xz_bram_if_pl_bot_Mem0
create_bd_addr_seg -range 0x10000 -offset 0x40010000 [get_bd_addr_spaces socius_xz_io_ps/
socius_xz_io_ps_bd/Data] [get_bd_addr_segs socius_xz_io_ps/socius_xz_bram_switch_bd/socius_xz_bram_if_pl_mid/S_AXI/Mem0]
SEG_socius_xz_bram_if_pl_mid_Mem0
create_bd_addr_seg -range 0x10000 -offset 0x40000000 [get_bd_addr_spaces socius_xz_io_ps/
socius_xz_io_ps_bd/Data] [get_bd_addr_segs socius_xz_io_ps/socius_xz_bram_switch_bd/socius_xz_bram_if_pl_soc/S_AXI/Mem0]
SEG_socius_xz_bram_if_pl_soc_Mem0
create_bd_addr_seg -range 0x10000 -offset 0x40020000 [get_bd_addr_spaces socius_xz_io_ps/
socius_xz_io_ps_bd/Data] [get_bd_addr_segs socius_xz_io_ps/socius_xz_bram_switch_bd/socius_xz_bram_if_pl_top/S_AXI/Mem0]
SEG_socius_xz_bram_if_pl_top_Mem0

# Perform GUI Layout


regenerate_bd_layout -layout_string {
guistr: "# # String gsaved with Nlview 6.5.5 2015-06-26 bk=1.3371 VDI=38 GEI=35 GUI=JA:1.8
# -string -flagsOSRD
preplace port pl_spi_0 -pg 1 -y 130 -defaultsOSRD
preplace port pl_bram_mid -pg 1 -y 210 -defaultsOSRD
preplace port fixed_io -pg 1 -y 70 -defaultsOSRD
preplace port pl_uart_1 -pg 1 -y 150 -defaultsOSRD
preplace port pl_iic_1 -pg 1 -y 90 -defaultsOSRD
preplace port pl_bram_bot -pg 1 -y 190 -defaultsOSRD
preplace port pl_clk0 -pg 1 -y 270 -defaultsOSRD
preplace port pl_clk1 -pg 1 -y 290 -defaultsOSRD
preplace port sdio_0 -pg 1 -y 110 -defaultsOSRD
preplace port pl_reset_n -pg 1 -y 350 -defaultsOSRD
preplace port pl_clk2 -pg 1 -y 310 -defaultsOSRD
preplace port pl_clk3 -pg 1 -y 330 -defaultsOSRD
preplace port usbind_0 -pg 1 -y 170 -defaultsOSRD
preplace port pl_bram_top -pg 1 -y 250 -defaultsOSRD
preplace port pl_bram_soc -pg 1 -y 230 -defaultsOSRD
preplace port ddr3 -pg 1 -y 50 -defaultsOSRD
preplace portBus pl_int_bot -pg 1 -y 170 -defaultsOSRD
preplace portBus pl_int_mid -pg 1 -y 190 -defaultsOSRD
preplace portBus pl_int_top -pg 1 -y 230 -defaultsOSRD
preplace portBus pl_int_soc -pg 1 -y 210 -defaultsOSRD
preplace inst socius_xz_io_ps -pg 1 -lvl 1 -y 200 -defaultsOSRD
preplace netloc processing_system7_0_DDR 1 1 1 NJ
preplace netloc In1_2 1 0 1 NJ
preplace netloc socius_xz_io_ps_bram_top 1 1 1 NJ
preplace netloc In2_2 1 0 1 NJ
preplace netloc socius_xz_io_ps_SPI_0 1 1 1 NJ
preplace netloc socius_xz_io_ps_bram_bot 1 1 1 NJ
preplace netloc socius_xz_io_ps_bram_soc 1 1 1 NJ
preplace netloc socius_xz_io_ps_IIC_1 1 1 1 NJ
preplace netloc socius_xz_io_ps_bd_FCLK_CLK0 1 1 1 NJ
preplace netloc In0_1 1 0 1 NJ
preplace netloc socius_xz_io_ps_bd_FCLK_CLK1 1 1 1 NJ
preplace netloc processing_system7_0_USBIND_0 1 1 1 NJ
preplace netloc socius_xz_io_ps_bd_FCLK_CLK2 1 1 1 NJ
preplace netloc In3_1 1 0 1 NJ
preplace netloc processing_system7_0_FIXED_IO 1 1 1 NJ
preplace netloc socius_xz_io_ps_bd_FCLK_CLK3 1 1 1 NJ
preplace netloc socius_xz_io_ps_bram_mid 1 1 1 NJ
preplace netloc socius_xz_io_ps_UART_1 1 1 1 NJ
preplace netloc processing_system7_0_SDIO_0 1 1 1 NJ
preplace netloc socius_xz_io_ps_bd_FCLK_RESET0_N 1 1 1 NJ
levelinfo -pg 1 -30 150 370 -top 0 -bot 400
",
}

# Restore current instance


current_bd_instance $oldCurInst

save_bd_design
}
# End of create_root_design()

##################################################################

81
CREATING THE HARDWARE PLATFORM

# MAIN FLOW
##################################################################

create_root_design ""

Step 23. Next step is to execute the presented Tcl file in the Vivado IDE. Go to the Tcl console window and type the
following and press enter:

source <path>/socius_xz_io_ps_bd.tcl

Where <path> stands for the full path to the folder where the socius_xz_io_ps_bd.tcl Tcl file is stored.

Figure 2.78: Tcl Console window

After Vivado has finished with the Tcl script execution, a created block diagram containing Zynq PS will be visible in the
Vivado IDE, as shown on the Illustration 2.79.

Figure 2.79: Block diagram of Zynq PS configured to run on socius board

To prepare our design to work in the FPGA device, we must convert it to a bitstream file.
Step 24. In the Vivado Flow Navigator, click Run Synthesis command and wait for task to be completed
Step 25. When the synthesis process is completed, click Run Implementation command and wait for task to be completed
Step 26. At the end, when the implementation process is completed, click Generate Bitstream command. After this step,
bitstream file will be generated.

82
Chapter 3

CREATING THE SOFTWARE PLATFORM USING SDK

In the previous chapter we have designed the hardware component of our embedded system. To complete the design
process we must now create a software component for our embedded system. This application specific software will be
executed on the MicroBlaze or ARM processor that is a part of our hardware platform.
When using Xilinx development tools, software design process is done using the Software Development Kit (SDK) tool.
Our software application will be developed for the hardware platform built in IP Integrator tool.
Illustration 3.1 shows, in detail, SDK application development flow.

Figure 3.1: SDK application development flow

The first step in software creation is to export the hardware design into the SDK. The hardware design will be exported to
an XML files that will be used by the SDK to create a software application for our hardware.
To create a software platform for your embedded design, use the following steps:
CREATING THE SOFTWARE PLATFORM USING SDK

Step 1. Select File -> Export -> Export Hardware... option from the main Vivado IDE menu
Step 2. In the Export Hardware dialog box, make sure that Include bitstream check box is checked, and Export to field
is set to Local to Project, see Illustration 3.2 and click OK

Figure 3.2: Export Hardware dialog box

Step 3. To launch SDK after the hardware platform has been exported, select File -> Launch SDK from the main Vivado
IDE menu
Step 4. In the Launch SDK dialog box, make sure that both Exported location and Workspace are set to Local to
Project, see Illustration 3.3 and click OK
SDK will be launched in a separate window, see Illustration 3.4

Figure 3.3: Launch SDK dialog box

84
3.1 Board Support Package

Figure 3.4: SDK main window

As you can see from the illustration above SDK has automatically created a new hardware platform specification,
modulator_wrapper_hw_platform_0 with the system.hdf file inside. The system.hdf file is the hardware platform speci-
fication file which contains information about the target device, address map for the selected processor, about the IP blocks
present in the design along with the links to the datasheets of all system peripherals.

• The Source tab shows the content of the system.hdf file

• The Overview tab shows most of the information necessary for writing software

3.1 Board Support Package

Each software project must have a corresponding Board Support Package (BSP) .
A Board Support Package is a collection of libraries and drivers that form the lowest level of your software application stack.
Before you can create and use software applications in SDK, you must create a board support package. You can create
more then one application to run on the BSP.
You can use SDK to create BSP for three different run-time environments:

• Standalone - A simple, low-level software layer. It provides access to basic processor feature such as cache,
interrupts and exceptions as well as the basic features of a hosted environment, such as standard input and output,
profiling, abort and exit.

• FreeRTOS - A popular real-time operating system kernel for embedded devices, that has been ported to 35 mi-
crocontrollers, including ARM and Xilinx MicroBlaze. Xilinx offers two versions FreeRTOS: freertos821_xilinx and
freertos832_xilinx.

• Xilkernel - A simple and lightweight kernel that provides POSIX style services such as scheduling, threads, synchro-
nization, message passing, and timers. The kernel requires a programmable timer that is either built-in or attached
to the processor as a peripheral.

85
CREATING THE SOFTWARE PLATFORM USING SDK

In SDK we can have a multiple Board Support Packages. For example, one for a design that runs on the standalone
environment, and one that uses Xilkernel.
Generating BSP is very easy. The program that is launched is called LibGen (Library Generator). It reads the system.mss
file for directions on which drivers and libraries to include. The MSS file is built when a software platform or BSP is created,
and contains a software instance for every processor and peripheral IP.
The purpose of running LibGen is to compile the BSP ones. Ones compiled by LibGen, the BSP resides as an object ready
to be linked to the compiled software application.
To create a Board Support Package do the following:
Step 1. Select File -> New -> Board Support Package option, see Illustration 3.5

Figure 3.5: Board Support Package option

Step 2. In the New Board Support Package Project dialog box leave all default options as they are set and click Finish,
see Illustration 3.6

Figure 3.6: New Board Support Package Project dialog box

Step 3. In the Board Support Package Settings dialog box leave all default options as they are set in all four tabs
(Overview, standalone, drivers and microblaze_0) and click OK, see Illustration 3.7

86
3.2 Creating an application project

Figure 3.7: Board Support Package Settings dialog box

3.2 Creating an application project

Now is the moment when you will create an application project. The actual application will be written in C/C++ programming
language. The SDK will create and maintain make file for you.
To create an application project, do the following:
Step 1. Select File -> New -> Application Project and the Application Project dialog box will appear, see Illustration
3.8

87
CREATING THE SOFTWARE PLATFORM USING SDK

Figure 3.8: Application Project dialog box

Step 2. In the Project name field, type a name of the new project. In our case it will be modulator_no_intc, see Illustration
3.8
Note: In our design we will create two application projects. One will be without interrupt controller (modulator_no_intc),
and the second one will be with interrupt controller (modulator_intc).
Step 3. Select the location for the project. If you want to leave default location as it is displayed in the Location field, leave
the Use default location check box selected. Otherwise, type or browse a directory location of your new project.
Step 4. For the OS Platform, leave standalone platform selected
Step 5. For the Target Hardware, leave modulator_wrapper_hw_platform_0 selected as the Hardware Platform and
microblaze_0 as the Processor
Step 6. For the Target Software, choose C as the Language and choose Use existing standalone_bsp_0 as the Board
Support Package
Step 7. Click Next
Step 8. In the Templates dialog box, choose one of the available templates to generate a fully-functioning application
project, see Illustration 3.10. SDK provides a useful sample applications, which are listed in the Available Templates box.
Beside the Available Template box you can find a description box which gives a brief description of the selected sample
application. To create a blank C project, select Empty Application template, see Illustration 3.9

88
3.2 Creating an application project

Figure 3.9: Templates dialog box

Step 8. Click Finish to create our modulator_no_intc application project


After we have created Board Support Package project (standalone_bsp_0) and C Project (modulator_no_intc), both
of them should appear in the SDK Project Explorer window, see Illustration 3.10

89
CREATING THE SOFTWARE PLATFORM USING SDK

Figure 3.10: SDK main window after C project creation

Step 10. Repeat the same procedure to create the other application project, named modulator_intc
After this step you should have both application projects in the SDK Project Explorer window.

3.3 Creating a C/C++ source files for MicroBlaze-based processor system

Now it’s time to start writing the software for this project. With the Vivado tool we have advantage to develop software
independently from the hardware, using SDK tool.
As we already said in the previous sub-chapter, we will develop two application projects. One will be without interrupt
controller (modulator_no_intc), and the second one will be with interrupt controller (modulator_intc). The idea was to
illustrate how the same problem can be solved in a number of different ways.
Modulator design without interrupt controller:
To create source files necessary for modulator_no_intc application project, please do the following:
Step 1. Expand modulator_no_intc application project in Project Explorer and src folder should appear. In the src folder
you should find your source code after creation.
Step 2. Right-click on the src folder and select New -> Source File option
Step 3. In the New Source File dialog box

• check Source folder field,

• enter Source file name (in our case it will be modulator_no_intc_mb.c),

• and use "Default C source template" as Template option, as it is shown on the Illustration 3.11

90
3.3 Creating a C/C++ source files for MicroBlaze-based processor system

Figure 3.11: New Source File dialog box

Step 4. Click Finish and your modulator_no_intc_mb.c source file should appear in the src folder, as we said
Step 5. Double-click on the modulator_no_intc_mb.c source file in the Project Explorer and it will be immediately opened
Step 6. Copy your source code in it, or write directly in it
Step 7. When you finished with all modifications, click Save and SDK will automatically build your application
Step 8. Repeat the same procedure to also create modulator.h and init_sin.c source files
Note: The complete source files for modulator_no_intc_mb.c, modulator.h and init_sin.c you can find in the text below.
modulator_no_intc_mb.c:

#include "xparameters.h"
#include "xgpio.h"
#include "xstatus.h"
#include "xtmrctr.h"
#include "modulator.h"

int main(void)
{
/******************** Variable Definitions ********************/

XGpio GpioLeds; // XGPIO instance that will be used to work with LED
XGpio GpioSwitches; // XGPIO instance that will be used to work with SWITCH
XTmrCtr TimerCounter; // TIMER instance

int count_depth = 0; // counter for sine samples


int sw0; // switch used for selecting frequency
int scaling_factor; // will be used to represent the SCALING_FACTOR_0 or SCALING_FACTOR_1 value

unsigned int end_time; // will be used to represent the END_TIME_0 or END_TIME_1 value
unsigned int current_time; // represents the current timer value
unsigned int threshold; // will be used to represent the current value of the sine signal

// sine amplitude values that will be used to generate the PWM signal
static unsigned int sine_ampl[COUNT_DEPTH_END];

/*********************** Initialization ***********************/

// LEDs initialization
XGpio_Initialize(&GpioLeds, XPAR_AXI_GPIO_0_DEVICE_ID);
XGpio_SetDataDirection(&GpioLeds, LED_CHANNEL, 0);

// SWITCHes initialization
XGpio_Initialize(&GpioSwitches, XPAR_AXI_GPIO_0_DEVICE_ID);
XGpio_SetDataDirection(&GpioSwitches, SWITCH_CHANNEL, 0xff);

// TIMER initialization
XTmrCtr_Initialize(&TimerCounter, XPAR_AXI_TIMER_0_DEVICE_ID);
XTmrCtr_SetOptions(&TimerCounter, AXI_TIMER_0, 0x0);

// sine_ampl array initialization


init_sin_f (sine_ampl);

/************************* Main Loop *************************/

91
CREATING THE SOFTWARE PLATFORM USING SDK

while(1)
{

// start timer
XTmrCtr_Start(&TimerCounter, AXI_TIMER_0);

// read the switch position


sw0 = XGpio_DiscreteRead (&GpioSwitches, SWITCH_CHANNEL);

// check the switch position


if ((sw0 & SWITCH_POS) == 0) // masking (we want to check the status of SW0 only)
{
end_time = END_TIME_0;
scaling_factor = SCALING_FACTOR_0;
}
else
{
end_time = END_TIME_1;
scaling_factor = SCALING_FACTOR_1;
}

// turn on the LED


XGpio_DiscreteWrite(&GpioLeds, LED_CHANNEL, LED_POS);

do // pause
{
current_time = XTmrCtr_GetValue(&TimerCounter, AXI_TIMER_0);
}
while (current_time<threshold);

//turn off the LED


XGpio_DiscreteWrite(&GpioLeds, LED_CHANNEL, 0);

do //pause
{
current_time = XTmrCtr_GetValue(&TimerCounter, AXI_TIMER_0);
}
while (current_time<end_time);

// reset timer
XTmrCtr_Reset(&TimerCounter, AXI_TIMER_0);

count_depth ++;
if (count_depth == COUNT_DEPTH_END)
count_depth = 0;

threshold = scaling_factor * sine_ampl[count_depth];


// we must multiply current amplitude value of the sine signal with the scaling_factor
// (389120/4096=95) to "stretch" the range from (0 - 2^width(=4096)) to (0 - 389120)
}

return 0;
}

modulator.h:

#ifndef MODULATOR_H_
#define MODULATOR_H_

#include "math.h"

/********************** Constant Definitions ***********************/

#define LED_CHANNEL 1 // GPIO channel (1 or 2) to operate on


#define SWITCH_CHANNEL 2 // GPIO channel (1 or 2) to operate on
#define AXI_TIMER_0 0 // timer counter of the device to operate on

#define SYS_CLK_MHZ 100 // 100 MHz system clock


#define CLOCK_RATE 1000000 * SYS_CLK_MHZ // system clock value, expressed in Hz
// CLOCK_RATE = 100000000 Hz (= 100 MHz)

#define F_LOW 1.0 // F_LOW = 1 Hz


#define F_HIGH 3.5 // F_HIGH = 3.5 Hz

#define DEPTH 8 // the number of samples in one period of the signal (2^8=256)
#define WIDTH 12 // the number of bits used to represent amplitude value (2^12=4096)

#define C1 (1 << DEPTH) // 2^DEPTH


#define C2 (1 << (WIDTH-1)) // 2^(WIDTH-1)
#define C3 (1 << WIDTH) // 2^WIDTH

// variable that will be used for calculating, DIV_FACTOR_FREQLOW and DIV_FACTOR_FREQHIGH


// C = SYS_CLK_MHZ / (2^DEPTH * 2^WIDTH) = 95.3674
#define C roundf ((float)CLOCK_RATE / (float)(C1 * C3))

// input clock division factor, when sw0 = 0 (F_LOW = 1 Hz)


// DIV_FACTOR_FREQLOW = (C / F_LOW) * 2^WIDTH = 389120
#define DIV_FACTOR_FREQLOW (roundf((float)C / (float)F_LOW)) * C3;

92
3.3 Creating a C/C++ source files for MicroBlaze-based processor system

// input clock division factor, when sw0 = 1 (F_HIGH = 3.5 Hz)


// DIV_FACTOR_FREQHIGH = (C / F_HIGH) * 2^WIDTH = 110592
#define DIV_FACTOR_FREQHIGH (roundf((float)C / (float)F_HIGH)) * C3;

// END_TIME_0 is timer threshold value, when sw0 = 0 (F_LOW = 1 Hz)


#define END_TIME_0 DIV_FACTOR_FREQLOW
// END_TIME_1 is timer threshold value, when sw0 = 1 (F_HIGH = 3.5 Hz)
#define END_TIME_1 DIV_FACTOR_FREQHIGH

# define SCALING_FACTOR_0 roundf ((float)C / (float)F_LOW) // SCALING_FACTOR_0 = C / F_LOW (=95)


# define SCALING_FACTOR_1 roundf ((float)C / (float)F_HIGH) // SCALING_FACTOR_1 = C / F_HIGH (=27)

#define SWITCH_POS 0x01 // mask to select switch position


#define LED_POS 0x01 // mask to select led position

#define COUNT_DEPTH_END 1 << DEPTH // final threshold value for the depth counter (2^8=256)

void init_sin_f (unsigned int *sine_ampl);

#endif /* MODULATOR_H_ */

init_sin.c:

#include "modulator.h"

void init_sin_f (unsigned int *sine_ampl)


{
int i;
float pi = 4.0*atan(1.0); // pi=3.14...

for (i=0; i<256; i++)


//sine_ampl[i] = sin(2*pi*i/pow(2,depth)) * (pow(2,width-1)-1) + pow(2.0,width-1)-1;
sine_ampl[i] = (sin(2*pi*i/C1) * (C2-1) + C2 - 1); // [sin(2*pi*i/N)*(2^(width-1)-1)] +
[2^(width-1)-1], N = 2^depth
}

As you can see from the example above, we have used a lot of different functions.

• For LED and SWITCH initialization, we used

– XGpio_Initialize (XGpio ∗InstancePtr, u16 DeviceId) and


– XGpio_SetDataDirection (XGpio InstancePtr, unsigned Channel, u32 DirectionMask) functions

• For TIMER initialization, we used

– XTmrCtr_Initialize (XTmrCtr ∗InstancePtr, u16 DeviceId)


– XTmrCtr_SetOptions (XTmrCtr ∗InstancePrt, u8 TmrCtrNumber, u32 Options) functions

• The rest of the functions that we have used for LEDs, SWITCHes and TIMER are:

– XGpio_DiscreteRead (XGpio ∗InstancePtr, unsigned Channel)


– XGpio_DiscreteWrite (XGpio ∗InstancePtr, unsigned Channel, u32 Mask)
– XTmrCtr_Start (XTmrCtr ∗InstancePtr, u8 TmrCtrNumber)
– XTmrCtr_GetValue (XTmrCtr ∗InstancePtr, u8 TmrCtrNumber)
– XTmrCtr_Reset (XTmrCtr ∗InstancePtr, u8 TmrCtrNumber)

All of these functions and it’s definitions and explanations, you can find in the Xilinx directory:
Xilinx \ SDK \ 2015.4 \ data \ embeddedsw \ XilinxProcessorIPLib \ drivers \ gpio_v... (or tmrctr_v... or some other
peripheral) \ doc \ html \ api \ xgpio_8c.html (or xtmrctr_8c.html).
There, you can find a plenty of different functions that you can use in your software design. In this tutorial we have represent
just those functions that we have used in our software design. Here are some of them:

• XGpio_Initialize (XGpio ∗InstancePtr, u16 DeviceId)

– Initialize the XGpio instance provided by the caller based on the given DeviceID.
Parameters:
InstancePtr - is a pointer to an XGpio instance. The memory the pointer references must be pre-allocated by
the caller. Further calls to manipulate the instance/driver through the XGpio API must be made with this pointer.
DeviceId - is the unique id of the device by the XGpio instance. Passing in a device id associates the generic
XGpio instance to a specific device, as chosen by the caller or application developer.

93
CREATING THE SOFTWARE PLATFORM USING SDK

Note: The exact DeviceId information you can find in the xparameters.h file. As you can see from the code above, we
already include that file. The xparameters.h file contains base addresses of peripherals and defines parameters used to
access peripherals in drivers and user programs. This file is automatically generated by Libgen (Library Generation) tool.
xparameters.h file, you can find in your SDK project directory:
... \ modulator \ modulator.sdk \ standalon e_bsp_0 \ microblaze_0 \ include \ xparameters.h

• XGpio_SetDataDirection (XGpio ∗InstancePtr, unsigned Channel, u32 DirectionMask)

– Set the input/output direction of all discrete signals for the specified GPIO channel.
Parameters:
InstancePtr - is a pointer to an XGpio instance to be worked on.
Channel - contains the channel of the GPIO (1 or 2) to operate on.
DirectionMask - is a bitmask specifying which discretes are input and which are output. Bits set to 0 are output
and bits set to 1 are input.
Note: The exact DeviceId information you can find in the xparameters.h file.

• XGpio_DiscreteRead (XGpio ∗InstancePtr, unsigned Channel)

– Read state of discretes for the specified GPIO channel.


Parameters:
InstancePtr - is a pointer to an XGpio instance to be worked on.
Channel - contains the channel of the GPIO (1 or 2) to operate on.

• XGpio_DiscreteWrite (XGpio ∗InstancePtr, unsigned Channel, u32 Mask)

– Write to discretes register for the specified GPIO channel.


Parameters :
InstancePtr - is a pointer to an XGpio instance to be worked on.
Channel - contains the channel of the GPIO (1 or 2) to operate on.
Mask - is the value to be written to the discretes register.

• XtmrCtr_Initialize (XtmrCtr ∗InstancePtr, u16 DeviceId)

– Initializes a specific timer/counter instance/driver. Initialize fields of the XTmrCtr structure, then reset the timer/-
counter
Parameters: InstancePtr - is a pointer to the XTmrCtr instance.
DeviceId - is the unique id of the device controlled by this XTmrCtr component. Passing in a device id associates
the generic XTmrCtr component to a specific device, as chosen by the caller or application developer.
Note: The exact DeviceId information you can find in the xparameters.h file.

• XtmrCtr_SetOptions (XTmrCtr ∗InstancePrt, u8 TmrCtrNumber, u32 Options)

– Enables the specified options for the specified timer counter. This function sets the options without regard to
the current options of the driver. To prevent a loss of the current options, the user should call XTmrCtr_Get-
Options() prior to this function and modify the retrieved options to pass into this function to prevent loss of the
current options.
Parameters:
InstancePtr - is a pointer to the XTmrCtr instance.
TmrCtrNumber - is the timer counter of the device to operate on. Each device may contain multiple timer
counters. The timer number is a zero based number with a range of 0 - (XTC_DEVICE_TIMER_COUNT - 1).
Options - contains the desired options to be set or cleared. Setting the option to ’1’ enables the option, clearing
the to ’0’ disables the option. The options are bit masks such that multiple options may be set or cleared. The
options are described in xtmrctr.h file.

• XTmrCtr_Start (XTmrCtr ∗InstancePtr, u8 TmrCtrNumber)

– Starts the specified timer counter of the device such that it starts running. The timer counter is reset before it
is started and the reset value is loaded into the timer counter.

94
3.3 Creating a C/C++ source files for MicroBlaze-based processor system

– If interrupt mode is specified in the options, it is necessary for the caller to connect the interrupt handler of
the timer/counter to the interrupt source, typically an interrupt controller, and enable the interrupt within the
interrupt controller.
Parameters:
InstancePtr - is a pointer to the XTmrCtr instance.
TmrCtrNumber - is the timer counter of the device to operate on. Each device may contain multiple timer
counters. The timer number is a zero based number with a range of 0 - (XTC_DEVICE_TIMER_COUNT - 1).

• XTmrCtr_GetValue (XTmrCtr ∗InstancePtr, u8 TmrCtrNumber)

– Get the current value of the specified timer counter. The timer counter may be either incrementing or decre-
menting based upon the current mode of operation.
Parameters:
InstancePtr - is a pointer to the XTmrCtr instance.
TmrCtrNumber - is the timer counter of the device to operate on. Each device may contain multiple timer
counters. The timer number is a zero based number with a range of 0 - (XTC_DEVICE_TIMER_COUNT - 1).

• XTmrCtr_Reset (XTmrCtr ∗InstancePtr, u8 TmrCtrNumber)

– Resets the specified timer counter of the device. A reset causes the timer counter to set it’s value to the reset
value.
Parameters:
InstancePtr - is a pointer to the XTmrCtr instance.
TmrCtrNumber - is the timer counter of the device to operate on. Each device may contain multiple timer
counters. The timer number is a zero based number with a range of 0 - (XTC_DEVICE_TIMER_COUNT - 1).

Modulator design with interrupt controller :


To create source files necessary for our modulator_intc application project, please do the following:
Step 1. Expand modulator_intc application project in Project Explorer and src folder should appear. In the src folder
you should find your source code after creation.
Step 2. Repeat the steps 2 - 8 from the Modulator design without interrupt controller.
Note: In step 3 use the modulator_intc_mb.c as the file name for the C source code file
modulator.h - this file is identical with the modulator.h file used in the design without interrupt controller
init_sin.c - this file is identical with the init_sin.c file used in the design without interrupt controller
modulator_intc_mb.c:

#include "xparameters.h"
#include "xgpio.h"
#include "xstatus.h"
#include "xtmrctr.h"
#include "modulator.h"

#include "xintc.h"
#include "mb_interface.h"

int interrupt_occurred; // variable which will signal when that interrupt has occurred

// interrupt handler for the timer


void Timer_InterruptHandler(void *CallBackRef, unsigned char TmrCtrNumber)
{
interrupt_occurred = 1;
}

int main(void)
{

/*********************** Variable Definitions ********************/

XGpio GpioLeds; // XGPIO instance that will be used to work with LED
XGpio GpioSwitches; // XGPIO instance that will be used to work with SWITCH
XTmrCtr TimerCounter; // TIMER instance
XIntc InterruptController; // INTERRUPT CONTROLLER instance

int count_depth = 0; // counter for sine samples


int sw0; // switch used for selecting frequency
int scaling_factor; // will be used to represent the SCALING_FACTOR_0 or SCALING_FACTOR_1 value

unsigned int end_time; // will be used to represent the END_TIME_0 or END_TIME_1 value

95
CREATING THE SOFTWARE PLATFORM USING SDK

unsigned int threshold; // will be used to represent the current value of the sine signal

// sine amplitude values that will be used to generate the PWM signal
unsigned int sine_ampl[COUNT_DEPTH_END];

int led_state; // current state of the LED

int reset_value_0; // is the value for timer to now from which value will start counting downwards
// reset_value_0 = end_time - threshold
int reset_value_1; // is the value for timer to now from which value will start counting downwards
// reset_value_1 = threshold

/*********************** Initialization **************************/

// LEDs initialization
XGpio_Initialize(&GpioLeds, XPAR_AXI_GPIO_0_DEVICE_ID);
XGpio_SetDataDirection(&GpioLeds, LED_CHANNEL, 0);

// SWITCHes initialization
XGpio_Initialize(&GpioSwitches, XPAR_AXI_GPIO_0_DEVICE_ID);
XGpio_SetDataDirection(&GpioSwitches, SWITCH_CHANNEL, 0xff);

// INTERRUPT CONTROLLER initialization


XIntc_Initialize (&InterruptController, XPAR_MICROBLAZE_0_AXI_INTC_DEVICE_ID);
XIntc_Connect (&InterruptController, XPAR_MICROBLAZE_0_AXI_INTC_DEVICE_ID,
(XInterruptHandler)XTmrCtr_InterruptHandler, (void *)(&TimerCounter));
XIntc_Start (&InterruptController, XIN_REAL_MODE);
XIntc_Enable (&InterruptController, XPAR_MICROBLAZE_0_AXI_INTC_DEVICE_ID);
microblaze_enable_interrupts();

// TIMER initialization
XTmrCtr_Initialize(&TimerCounter, XPAR_AXI_TIMER_0_DEVICE_ID);
XTmrCtr_SetHandler(&TimerCounter, Timer_InterruptHandler, &TimerCounter);
XTmrCtr_SetOptions(&TimerCounter, AXI_TIMER_0, XTC_INT_MODE_OPTION | XTC_DOWN_COUNT_OPTION);

// sine_ampl array initialization


init_sin_f (sine_ampl);

// TIMER configuration
// read the switch position
sw0 = XGpio_DiscreteRead (&GpioSwitches, SWITCH_CHANNEL);

// check the switch position


if ((sw0 & SWITCH_POS) == 0) // masking (we want to check the status of SW0 only)
{
end_time = END_TIME_0;
scaling_factor = SCALING_FACTOR_0;
}
else
{
end_time = END_TIME_1;
scaling_factor = SCALING_FACTOR_1;
}
threshold = scaling_factor * sine_ampl[count_depth];
// threshold = current amplitude value of the sine signal
// we must multiply current amplitude value of the sine signal with the scaling_factor
// (389120/4096=95) to "stretch" the range from (0 - 2^width(=4096)) to (0 - 389120)

reset_value_0 = end_time - threshold;


reset_value_1 = threshold;

// set timer
XTmrCtr_SetResetValue(&TimerCounter, AXI_TIMER_0, reset_value_1);

// turn on the LED


XGpio_DiscreteWrite(&GpioLeds, LED_CHANNEL, LED_POS);

// start timer
XTmrCtr_Start(&TimerCounter, AXI_TIMER_0);

interrupt_occurred = 0; // interrupt_occurred initialization


led_state = 1; // set the initial state of the LED

/************************* Main Loop *****************************/

while(1)
{
// read the switch position
sw0 = XGpio_DiscreteRead (&GpioSwitches, SWITCH_CHANNEL);

// check the switch position


if ((sw0 & SWITCH_POS) == 0) // masking (we want to check the status of SW0 only)
{
end_time = END_TIME_0;
scaling_factor = SCALING_FACTOR_0;
}
else
{
end_time = END_TIME_1;
scaling_factor = SCALING_FACTOR_1;

96
3.3 Creating a C/C++ source files for MicroBlaze-based processor system

if (interrupt_occurred == 1)
{
interrupt_occurred = 0;

// stop timer
XTmrCtr_Stop (&TimerCounter, AXI_TIMER_0);

if (led_state == 1)
{
// write the starting counter value, reset_value_0 = end_time - threshold
XTmrCtr_SetResetValue(&TimerCounter, AXI_TIMER_0, reset_value_0);

// turn off the LED


XGpio_DiscreteWrite(&GpioLeds, LED_CHANNEL, 0);

// start timer
XTmrCtr_Start(&TimerCounter, AXI_TIMER_0);

led_state = 0;

count_depth ++;
if (count_depth == COUNT_DEPTH_END)
count_depth = 0;
threshold = scaling_factor * sine_ampl[count_depth];

reset_value_0 = end_time - threshold;


reset_value_1 = threshold;
}
else
{
// write the starting counter value, reset_value_1 = threshold
XTmrCtr_SetResetValue(&TimerCounter, AXI_TIMER_0, reset_value_1);

// turn on the LED


XGpio_DiscreteWrite(&GpioLeds, LED_CHANNEL, LED_POS);

// start timer
XTmrCtr_Start(&TimerCounter, AXI_TIMER_0);

led_state = 1;
}
}
}

return 0;
}

In the modulator_intc.c source code with the interrupt controller, we have used almost the same functions as in the
modulator_no_intc.c source code without using an interrupt controller. Here are the functions that we have used in our
design. Some of them are explained below the modulator_no_intc.c source file.

• For LED and SWITCH initialization, we used

– XGpio_Initialize (XGpio ∗InstancePtr, u16 DeviceId) and


– XGpio_SetDataDirection (XGpio InstancePtr, unsigned Channel, u32 DirectionMask) functions

• For TIMER initialization, we used

– XTmrCtr_Initialize (XTmrCtr ∗InstancePtr, u16 DeviceId)


– XTmrCtr_SetOptions (XTmrCtr ∗InstancePrt, u8 TmrCtrNumber, u32 Options) functions
– XTmrCtr_SetHandler (XTmrCtr ∗InstancePtr, XTmrCtr Handler FuncPtr, void ∗CallBackRef)

• For INTERRUPT CONTROLLER initialization, we used

– XIntc_Initialize (XIntc ∗InstancePtr, u16 DeviceId)


– XIntc_Connect (XIntc ∗InstancePtr, u8 Id, XinterruptHandler Handler, void ∗Call BackRef)
– XIntc_Start (XIntc ∗InstancePtr, u8 Mode)
– XIntc_Enable (XIntc ∗InstancePtr, u8 Id)

• The rest of the functions that we have used for LEDs, SWITCHes, INTERRUPT CONTROLLER and TIMER are:

– XGpio_DiscreteRead (XGpio ∗InstancePtr, unsigned Channel)


– XGpio_DiscreteWrite (XGpio ∗InstancePtr, unsigned Channel, u32 Mask)
– XTmrCtr_Start (XTmrCtr ∗InstancePtr, u8 TmrCtrNumber)

97
CREATING THE SOFTWARE PLATFORM USING SDK

– XTmrCtr_Stop (XTmrCtr ∗InstancePtr, u8 TmrCtrNumber)


– XTmrCtr_SetResetValue (XTmrCtr ∗InstancePtr, u8 TmrCtrNumber, u32 ResetValue)

Here is the explanation some of the functions that are used in modulator_intc.c source code with the interrupt controller,
that are not mentioned before in this tutorial:

• XTmrCtr_SetHandler (XTmrCtr ∗InstancePtr, XTmrCtr Handler FuncPtr, void ∗CallBackRef)

– Sets the timer callback function, which the driver calls when the specified timer times out.
Parameters:
InstancePtr - is a pointer to the XTmrCtr instance.
FunctionPtr - is the pointer to the callback function.
CallBackRef - is the upper layer callback reference passed back when the callback function is invoked.

• XIntc_Initialize (XIntc ∗InstancePtr, u16 DeviceId)

– Initialize a specific interrupt controller instance/driver. The initialization entails:

* Initialize fields of the XIntc structure


* Initial vector table with stub function calls
* All interrupt sources are disabled
* Interrupt output is disabled
Parameters:
InstancePtr - is a pointer to the XIntc instance to be worked on.
DeviceId - is the unique id of the device controlled by this XIntc instance. Passing in a device id associates the
generic XIntc instance to a specific device, as chosen by the caller or application developer.
Note: The exact DeviceId information you can find in the xparameters.h file.

• XIntc_Connect (XIntc ∗InstancePtr, u8 Id, XinterruptHandler Handler, void ∗CallBackRef)

– Makes the connection between the Id of the interrupt source and the associated handler that is to run when the
interrupt is recognized. The argument provided in this call as the Callbackref is used as the argument for the
handler when it is called.
Parameters:
InstancePtr - is a pointer to the XIntc instance to be worked on.
Id - contains the ID of the interrupt source and should be in the range of 0 to XPAR_INTC_MAX_NUM_INTR-
_INPUTS - 1 with 0 being the highest priority interrupt.
Handler - to the handler for that interrupt.
CallBackRef - is the callback reference, usually the instance pointer of the connecting driver.
Note: The exact Id information you can find in the xparameters.h file.

• XIntc_Start (XIntc ∗InstancePtr, u8 Mode)

– Starts the interrupt controller by enabling the output from the controller to the processor. Interrupts may be
generated by the interrupt controller after this function is called.
– It is necessary for the caller to connect the interrupt handler of this component to the proper interrupt source.
Parameters:
InstancePtr - is a pointer to the XIntc instance to be worked on.
Mode - determines if software is allowed to simulate interrupts or real interrupts are allowed to occur. Note that
these modes are mutually exclusive. The interrupt controller hardware resets in a mode that allows software to
simulate interrupts until this mode is exited. It cannot be reentered once it has been exited.

• XIntc_Enable (XIntc ∗InstancePtr, u8 Id)

– Enables the interrupt source provided as the argument Id. Any pending interrupt condition for the specified Id
will occur after this function is called.
Parameters:
InstancePtr - is a pointer to the XIntc instance to be worked on.
Id - contains the ID of the interrupt source and should be in the range of 0 to XPAR_INTC_MAX_NUM_INTR-
_INPUTS - 1 with 0 being the highest priority interrupt.
Note : The exact Id information you can find in the xparameters.h file.

98
3.4 Creating a C/C++ source files for ARM-based processor system

• XTmrCtr_Stop (XTmrCtr ∗InstancePtr, u8 TmrCtrNumber)

– Stops the timer counter by disabling it.


– It is the callers’ responsibility to disconnect the interrupt handler of the timer_counter from the interrupt source,
typically an interrupt controller, and disable the interrupt within the interrupt controller.
Parameters:
InstancePtr - is a pointer to the XTmrCtr instance.
TmrCtrNumber - is the timer counter of the device to operate on. Each device may contain multiple timer
counters. The timer number is a zero based number with a range of 0 - (XTC_DEVICE_TIMER_COUNT - 1).

• XTmrCtr_SetResetValue (XTmrCtr ∗InstancePtr, u8 TmrCtrNumber, u32 ResetValue)

– Set the reset value for the specified timer counter. This is the value that is loaded into the timer counter when
it is reset. This value is also loaded when the timer counter is started.
Parameters:
InstancePtr - is a pointer to the XTmrCtr instance.
TmrCtrNumber - is the timer counter of the device to operate on. Each device may contain multiple timer
counters. The timer number is a zero based number with a range of 0 - (XTC_DEVICE_TIMER_COUNT - 1).
ResetValue - contains the value to be used to reset the timer counter.

3.4 Creating a C/C++ source files for ARM-based processor system

To create source files necessary for our ARM-based processor system, please repeat the steps from the previous sub-
chapter 3.3 Creating a C/C++ source files for MicroBlaze-based processor system.
Like for the MicroBlaze-based processor system we will create two application projects, one without using interrupt con-
troller (modulator_no_intc) and the second one with using interrupt controller (modulator_intc). To create modulator_-
no_intc and modulator_intc application projects, repeat steps from the sub-chapter 3.2 "Creating an application project".
The only difference will be that in the ARM-based processor system we will use UART Controller , that is integral part of
the Zynq7 processing system, to transmit debug and system status information during application execution to the attached
PC.
Modulator design without interrupt controller:
Source files for the first application project (modulator_no_intc) will be mainly the same like for the MicroBlaze-based
processor system. modulator.h and init_sin.c will be the same, and you can find them in the previous sub-chapter 3.3
Creating a C/C++ source files for MicroBlaze-based processor system.
modulator_no_intc_arm.c file will be slightly different from the software in MicroBlaze-based processor system. The only
difference between them is in additional lines of C code that are added in the ARM case, which are used to transmit system
status and debug information to the PC using UART. This is achieved using xil_printf function. The new modulator_no_-
intc_arm.c source file, created for ARM-based processor system is written in the text below:
modulator_no_intc_arm.c:

#include "xparameters.h
#include "xgpio.h"
#include "xstatus.h"
#include "xtmrctr.h"
#include "modulator.h"
#include "xil_printf.h"

int main(void)
{
/*********************** Variable Definitions ********************/

XGpio GpioLeds; // XGPIO instance that will be used to work with LED
XGpio GpioSwitches; // XGPIO instance that will be used to work with SWITCH
XTmrCtr TimerCounter; // TIMER instance

int count_depth = 0; // counter for sine samples


int sw0, prev_sw0; // switch used for selecting frequency
int scaling_factor; // will be used to represent the SCALING_FACTOR_0 or SCALING_FACTOR_1 value

unsigned int end_time; // will be used to represent the END_TIME_0 or END_TIME_1 value
unsigned int current_time; // represents the current timer value
unsigned int threshold; // will be used to represent the current value of the sine signal

// sine amplitude values that will be used to generate the PWM signal

99
CREATING THE SOFTWARE PLATFORM USING SDK

static unsigned int sine_ampl[COUNT_DEPTH_END];

/*********************** Initialization **************************/

xil_printf("Initializing peripherals!rn");

xil_printf("Initializing LEDs!rn");
// LEDs initialization
XGpio_Initialize(&GpioLeds, XPAR_AXI_GPIO_0_DEVICE_ID);
XGpio_SetDataDirection(&GpioLeds, LED_CHANNEL, 0);

xil_printf("Initializing SWITCHes!rn");
// SWITCHes initialization
XGpio_Initialize(&GpioSwitches, XPAR_AXI_GPIO_0_DEVICE_ID);
XGpio_SetDataDirection(&GpioSwitches, SWITCH_CHANNEL, 0xff);

xil_printf("Initializing TIMER!rn");
// TIMER initialization
XTmrCtr_Initialize(&TimerCounter, XPAR_AXI_TIMER_0_DEVICE_ID);
XTmrCtr_SetOptions(&TimerCounter, AXI_TIMER_0, 0x0);

xil_printf("Initializing sine_ampl array!rn");


// sine_ampl array initialization
init_sin_f (sine_ampl);

/************************* Main Loop *****************************/

xil_printf("Entering main loop.rn");


while(1)
{
// start timer
XTmrCtr_Start(&TimerCounter, AXI_TIMER_0);

// read the switch position


sw0 = XGpio_DiscreteRead (&GpioSwitches, SWITCH_CHANNEL);

// check the switch position


if ((sw0 & SWITCH_POS) == 0) // masking (we want to check the status of SW0 only)
{
end_time = END_TIME_0;
scaling_factor = SCALING_FACTOR_0;
}
else
{
end_time = END_TIME_1;
scaling_factor = SCALING_FACTOR_1;
}

// send current system status information to the terminal using UART


if (sw0 != prev_sw0)
{
if ((sw0 & SWITCH_POS) == 0)
xil_printf("User selected PWM signal generation with 1 Hz frequency.rn");
else
xil_printf("User selected PWM signal generation with 3.5 Hz frequency.rn");
}
prev_sw0 = sw0;

// turn on the LED


XGpio_DiscreteWrite(&GpioLeds, LED_CHANNEL, LED_POS);

do // pause
{
current_time = XTmrCtr_GetValue(&TimerCounter, AXI_TIMER_0);
}
while (current_time<threshold);

//turn off the LED


XGpio_DiscreteWrite(&GpioLeds, LED_CHANNEL, 0);

do //pause
{
current_time = XTmrCtr_GetValue(&TimerCounter, AXI_TIMER_0);
}
while(current_time<end_time);

// reset timer
XTmrCtr_Reset(&TimerCounter, AXI_TIMER_0);

count_depth ++;
if(count_depth == COUNT_DEPTH_END)
count_depth = 0;

threshold = scaling_factor * sine_ampl[count_depth];


// we must multiply current amplitude value of the sine signal with the scaling_factor
// (389120/4096=95) to "stretch" the range from (0 - 2^width(=4096)) to (0 - 389120)
}

return 0;
}

100
3.4 Creating a C/C++ source files for ARM-based processor system

Please follow the procedure (steps 1-8) from 3.3 Creating a C/C++ source files for MicroBlaze-based processor sys-
tem, to add the necessary source files to this application project. In case of ARM- based design following additional steps
must be performed:
Step 9. Select modulator_no_intc application project, right- click on it and choose C/C++ Build Settings option
Step 10. In the Properties for modulator_no_intc dialog box choose C/C++ Build -> Settings option
Step 11. In the Settings dialog box, select Tool Settings tab and under the ARM v7 gcc linker select Libraries option
Step 12. In the Libraries (-l) window click on the Add... icon, see Illustration 3.13
Step 13. In the Enter Value dialog box, type m and click OK to add math library in the Libraries list, see Illustration 3.12

Figure 3.12: Adding math library to Libraries list

Figure 3.13: Added math library to Libraries list

101
CREATING THE SOFTWARE PLATFORM USING SDK

Step 14. In the Properties for modulator_no_intc diloag box, click OK


Modulator design with interrupt controller :
Like in the previous application project, modulator.h and init_sin.c source files for the second application project
(modulator_intc) are the same comparing with the MicroBlaze-baseed processor system. Only modulator_intc_arm.c
source file is different:
modulator_intc_arm.c:

#include "xparameters.h"
#include "xgpio.h"
#include "xstatus.h"
#include "xtmrctr.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "modulator.h"
#include "xil_printf.h"

// global variables necessary for the Global Interrupt Controller initialization


XScuGic INTCInst
XScuGic_Config *IntcConfig;

static int interrupt_occurred; // variable which will signal when that interrupt has occurred

// interrupt handler for the timer


void Timer_InterruptHandler(void *CallBackRef, u8 TmrCtrNumber)
{
interrupt_occurred = 1;

// stop timer
XTmrCtr_Stop (CallBackRef, TmrCtrNumber);
}

int IntcInitFunction(u16 DeviceId, XTmrCtr *TmrInstancePtr)


{
int status;

// Interrupt controller initialization


IntcConfig = XScuGic_LookupConfig(DeviceId);
status = XScuGic_CfgInitialize(&INTCInst, IntcConfig, IntcConfig->CpuBaseAddress);
if (status != XST_SUCCESS) return XST_FAILURE;

// Connect the interrupt controller interrupt handler to the hardware interrupt


// handling logic in the ARM
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
&INTCInst);

// Enable interrupt controller


Xil_ExceptionEnable();

// Connect timer interrupt to device driver handler


status = XScuGic_Connect(&INTCInst,
XPAR_FABRIC_AXI_TIMER_0_INTERRUPT_INTR,
(Xil_ExceptionHandler)XTmrCtr_InterruptHandler,
(void *)TmrInstancePtr);
if (status != XST_SUCCESS) return XST_FAILURE;

// Enable timer interrupt in the interrupt controller


XScuGic_Enable(&INTCInst, XPAR_FABRIC_AXI_TIMER_0_INTERRUPT_INTR);

return XST_SUCCESS;
}

int main(void)
{
/*********************** Variable Definitions ********************/

XGpio GpioLeds; // XGPIO instance that will be used to work with LED
XGpio GpioSwitches; // XGPIO instance that will be used to work with SWITCH
XTmrCtr TimerCounter; // TIMER instance

int count_depth = 0; // counter for sine samples


int sw0, prev_sw0; // switch used for selecting frequency
int scaling_factor; // will be used to represent the SCALING_FACTOR_0 or SCALING_FACTOR_1 value

unsigned int end_time; // will be used to represent the END_TIME_0 or END_TIME_1 value
unsigned int threshold; // will be used to represent the current value of the sine signal

// sine amplitude values that will be used to generate the PWM signal
unsigned int sine_ampl[COUNT_DEPTH_END];

int led_state; // current state of the LED


int reset_value_0; // is the value for timer to now from which value will start counting downwards
// reset_value_0 = end_time - threshold
int reset_value_1; // is the value for timer to now from which value will start counting downwards
// reset_value_1 = threshold

102
3.4 Creating a C/C++ source files for ARM-based processor system

int status;

/*********************** Initialization **************************/


xil_printf("Initializing peripherals!\r\n");

xil_printf("Initializing LEDs!\r\n");
// LEDs initialization
XGpio_Initialize(&GpioLeds, XPAR_AXI_GPIO_0_DEVICE_ID);
XGpio_SetDataDirection(&GpioLeds, LED_CHANNEL, 0);

xil_printf("Initializing SWITCHes!\r\n");
// SWITCHes initialization
XGpio_Initialize(&GpioSwitches, XPAR_AXI_GPIO_0_DEVICE_ID);
XGpio_SetDataDirection(&GpioSwitches, SWITCH_CHANNEL, 0xff);

xil_printf("Initializing TIMER!\r\n");
// TIMER initialization
XTmrCtr_Initialize(&TimerCounter, XPAR_AXI_TIMER_0_DEVICE_ID);
XTmrCtr_SetHandler(&TimerCounter, Timer_InterruptHandler, &TimerCounter);
XTmrCtr_SetOptions(&TimerCounter, AXI_TIMER_0, XTC_INT_MODE_OPTION | XTC_DOWN_COUNT_OPTION);

xil_printf("Initializing INTERRUPT CONTROLLER!\r\n");


// INTERRUPT CONTROLLER initialization
status = IntcInitFunction(XPAR_PS7_SCUGIC_0_DEVICE_ID, &TimerCounter);
if (status != XST_SUCCESS) return XST_FAILURE;

xil_printf("Initializing sine_ampl array!\r\n");


// sine_ampl array initialization
init_sin_f (sine_ampl);

// TIMER configuration
// read the switch position
sw0 = XGpio_DiscreteRead (&GpioSwitches, SWITCH_CHANNEL);

// check the switch position


if ((sw0 & SWITCH_POS) == 0) // masking (we want to check the status of SW0 only)
{
end_time = END_TIME_0;
scaling_factor = SCALING_FACTOR_0;
}
else
{
end_time = END_TIME_1;
scaling_factor = SCALING_FACTOR_1;
}
threshold = scaling_factor * sine_ampl[count_depth]; // threshold = current amplitude value of the sine
signal
// we must multiply current amplitude value of the sine signal with the scaling_factor
// (389120/4096=95) to "stretch" the range from (0 - 2^width(=4096)) to (0 - 389120)

reset_value_0 = end_time - threshold;


reset_value_1 = threshold;

// set timer
XTmrCtr_SetResetValue(&TimerCounter, AXI_TIMER_0, reset_value_1);

// turn on the LED


XGpio_DiscreteWrite(&GpioLeds, LED_CHANNEL, LED_POS);

// start timer
XTmrCtr_Start(&TimerCounter, AXI_TIMER_0);

interrupt_occurred = 0; // interrupt_occurred initialization


led_state = 1; // set the initial state of the LED

/************************* Main Loop *****************************/

xil_printf("Entering main loop.\r\n");


while(1)
{
// read the switch position
sw0 = XGpio_DiscreteRead (&GpioSwitches, SWITCH_CHANNEL);

// check the switch position


if ((sw0 & SWITCH_POS) == 0) // masking (we want to check the status of SW0 only)
{
end_time = END_TIME_0;
scaling_factor = SCALING_FACTOR_0;
}
else
{
end_time = END_TIME_1;
scaling_factor = SCALING_FACTOR_1;
}

// send current system status information to the terminal using UART


if (sw0 != prev_sw0)
{
if ((sw0 & SWITCH_POS) == 0)
xil_printf("User selected PWM signal generation with 1 Hz frequency.\r\n");
else

103
CREATING THE SOFTWARE PLATFORM USING SDK

xil_printf("User selected PWM signal generation with 3.5 Hz frequency.\r\n");


}
prev_sw0 = sw0;

if (interrupt_occurred == 1)
{
interrupt_occurred = 0;

if (led_state == 1)
{
// write the starting counter value, reset_value_0 = end_time - threshold
XTmrCtr_SetResetValue(&TimerCounter, AXI_TIMER_0, reset_value_0);

// turn off the LED


XGpio_DiscreteWrite(&GpioLeds, LED_CHANNEL, 0);

// start timer
XTmrCtr_Start(&TimerCounter, AXI_TIMER_0);

led_state = 0;

count_depth ++;
if (count_depth == COUNT_DEPTH_END)
count_depth = 0;
threshold = scaling_factor * sine_ampl[count_depth];

reset_value_0 = end_time - threshold;


reset_value_1 = threshold;
}
else
{
// write the starting counter value, reset_value_1 = threshold
XTmrCtr_SetResetValue(&TimerCounter, AXI_TIMER_0, reset_value_1);

// turn on the LED


XGpio_DiscreteWrite(&GpioLeds, LED_CHANNEL, LED_POS);

// start timer
XTmrCtr_Start(&TimerCounter, AXI_TIMER_0);

led_state = 1;
}
}

return 0;

If you compare the source code above (modulator_intc_arm.c) for the ARM-based processor system with the modulator-
_intc_mb.c source code for the MicroBlaze-based processor system, you can conclude that additional code has been
included before the main. The function IntcInitFunction (u16 DeviceId, XtmrCtr ∗TmrInstancePtr) is necessary and contains
additional code to:

• initialize interrupt controller

• connect the interrupt controller interrupt handler to the hardware interrupt handling logic in the ARM processor

• enable the interrupt controller

• connect a timer device driver handler that will be called when an interrupt for the timer occurs. This device driver
handler performs the specific interrupt processing for the device.

• Enable timer interrupt in the interrupt controller

As you can see from the code above, in the IntcInitFunction (u16 DeviceId, XtmrCtr ∗TmrInstancePtr) definition we have
used some new functions:

• XScuGic_LookupConfig (u32 Int_Id)

– Retrieves a pointer to the configuration table for a device driver.

Parameters:
Int_Id - contains the ID of the interrupt source and should be in the range of 0 to XSCUGIC_MAX_NUM_INTR_INP-
UTS - 1

• XScuGic_CfgInitialize (XScuGic ∗InstancePtr, XScuGic_Config ∗ConfigPtr, u32 EffectiveAddr)

104
3.5 Creating a C/C++ source files for a socius board based hardware platform

– CfgInitialize a specific interrupt controller instance/driver. The initialization entails:


* Initialize fields of the XScuGic structure
* Initial vector table with stub function calls
* All interrupt sources are disabledParameters:
InstancePtr - is a pointer to the XScuGic instance.
ConfigPtr - is a pointer to a config table for the particular device this driver is associated with.
EffectiveAddr - is the device base address in the virtual memory address space. The caller is responsible for keeping
the address mapping from EffectiveAddr to the device phisical base address unchanged once this function is invoked.
Unexpected errors may occur if the address mapping changes after this function is called. If address translation is
not used, use Config->BaseAddress for this parameters, passing the physical address instead..
Returns: XST_SUCCESS if initialization was successful

• XScuGic_Connect (XScuGic ∗InstancePtr, u32 Int_Id, Xil_InterruptHandler Handler, void ∗CallBackRef)

– Makes the connection between the Int_Id of the interrupt source and the associated handler that is to run when
the interrupt is recognized. The argument provided in this call as the Callbackref is used as the argument for
the handler when it is called.

Parameters:
InstancePtr - is a pointer to the XScuGic instance.
Int_Id - contains the ID of the interrupt source and should be in the range of 0 to XSCUGIC_MAX_NUM_INTR_INP-
UTS - 1.
Handler - to the handler for that interrupt.
CallBackRef - is the callback reference, usually the instance pointer of the connecting driver.
Returns: XST_SUCCESS if initialization was successful

• XScuGic_Enable (XScuGic ∗InstancePtr, u32 Int_Id)

– Enables the interrupt source provided as the argument Int_Id. Any pending interrupt condition for the specified
Int_Id will occur after this function is called.

Parameters:
InstancePtr - is a pointer to the XScuGic instance.
Int_Id – contains the ID of the interrupt source and should be in the range of 0 to XSCUGIC_MAX_NUM_INTR_IN-
PUTS - 1.

3.5 Creating a C/C++ source files for a socius board based hardware platform

To create source files necessary for our embedded system that will be running on the socius board, we must now create a
software component. This application specific software will be executed on the ARM processor that is already part of our
hardware platform.
As we already explained, the first step in software creation is to export the hardware design into the SDK tool. To export
your hardware platform into the SDK tool and to launch SDK, please repeat steps 1 - 4 from the chapter 3 "Creating the
Software Platform using SDK".
Next, a Board Support Package should be created. To create BSP, please repeat steps from the sub-chapter 3.1 "Board
Support Chapter".
After that you should create an application project. In our design we will create two application projects. One will be without
interrupt controller (modulator_socius_no_intc), and the second one will be with interrupt controller (modulator_socius-
_intc). To create modulator_socius_no_intc and modulator_socius_intc application projects, repeat steps from the
sub-chapter 3.2 "Creating an application project".
Now is the time to write the software for this project.
Modulator design without interrupt controller:
Step 1. In the modulator_socius_no_intc application project, create modulator_socius_no_intc.c, modulator.h and
init_sin.c source files on the same way as it is explained in the sub-chapter 3.3 "Creating a C/C++ source files for Micro-
Blaze-based processor system".

105
CREATING THE SOFTWARE PLATFORM USING SDK

The complete modulator.h and init_sin.c source files you can also find in the sub-chapter 3.3 "Creating a C/C++ source
files for MicroBlaze-based processor system".
The complete modulator_socius_no_intc.c source files you can find in the text below.
modulator_socius_no_intc.c:

#include "xparameters.h"
#include "xgpiops.h"
#include "xstatus.h"
#include "xttcps.h"
#include "modulator.h"
#include "xil_printf.h"

#define TTC_DEVICE_ID XPAR_XTTCPS_0_DEVICE_ID

// Definitions of actual pin locations for LED and push-button on the socius board
#define ps_sw2_a 10
#define ps_sw2_b 11
#define ps_sw3_a 12

// New type definition that will hold all relevant configuration parameters for the TTC module
typedef struct {
u32 OutputHz; /* Output frequency */
u16 Interval; /* Interval value */
u8 Prescaler; /* Prescaler value */
u16 Options; /* Option settings */
} TmrCntrSetup;

// Instance of a TmrCntrSetup type, holding TTC0 configuration parameters that will be used in modulator
example
static TmrCntrSetup SettingsTable[1] = {
{10, 65000, 2, 0}, /* Ticker timer counter initial setup, only output freq */
};

int main(void)
{
/*********************** Variable Definitions ********************/
XGpioPs GpioLeds; // XGPIO instance that will be used to work with LED
XGpioPs_Config *GPIOConfigPtr;

XTtcPs_Config *Config; // Pointer to a XTtcPs_Config type


XTtcPs TimerInst; // TIMER instance
XTtcPs *Timer; // Pointer to a XTtcPs type
TmrCntrSetup *TimerSetup; // Pointer to a TmrCntrSetup type

int count_depth = 0; // Counter for sine samples


int sw0, prev_sw0; // Switch used for selecting frequency
int scaling_factor; // Will be used to represent the SCALING_FACTOR_0 or SCALING_FACTOR_1 value

unsigned int end_time; // Will be used to represent the END_TIME_0 or END_TIME_1 value
unsigned int threshold; // Will be used to represent the current value of the sine signal
u16 current_time; // Represents the current timer value

// Sine amplitude values that will be used to generate the PWM signal
static unsigned int sine_ampl[COUNT_DEPTH_END];

/*********************** Initialization **************************/

xil_printf("Initializing peripherals!\r\n");

xil_printf("Initializing LEDs!\r\n");
// LEDs initialization
GPIOConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
XGpioPs_CfgInitialize(&GpioLeds, GPIOConfigPtr, GPIOConfigPtr ->BaseAddr);
// On the socius board we must properly control both ends of a LED, hence we must use two GPIO ports
XGpioPs_SetDirectionPin(&GpioLeds, ps_sw2_a, 1);
XGpioPs_SetOutputEnablePin(&GpioLeds, ps_sw2_a, 1);
XGpioPs_SetDirectionPin(&GpioLeds, ps_sw2_b, 1);
XGpioPs_SetOutputEnablePin(&GpioLeds, ps_sw2_b, 1);
// Set the value of one end of LED to always be equal to zero, by changing the other end we will turn
it on and off
XGpioPs_WritePin(&GpioLeds, ps_sw2_b, 0x0);

xil_printf("Initializing SWITCHes!\r\n");
// SWITCHes initialization
// Set the direction of the GPIO port connected to the push button to INPUT
XGpioPs_SetDirectionPin(&GpioLeds, ps_sw3_a, 0);

xil_printf("Initializing TIMER!\r\n");
// TIMER initialization
TimerSetup = &SettingsTable[TTC_DEVICE_ID];

// Read the current configuration of the timer module


Config = XTtcPs_LookupConfig(TTC_DEVICE_ID);

// Store the current configuration of the timer in a TimerInst object, using a pointer to access it
Timer = &TimerInst;

106
3.5 Creating a C/C++ source files for a socius board based hardware platform

Timer->Config = *Config;

// Stop the timer


XTtcPs_Stop(Timer);

// Initialize the timer with the required configuration parameters


XTtcPs_CfgInitialize(Timer, Config, Config->BaseAddress);
TimerSetup->Options |= (XTTCPS_OPTION_INTERVAL_MODE |
XTTCPS_OPTION_WAVE_DISABLE);
XTtcPs_SetOptions(Timer, TimerSetup->Options);
XTtcPs_SetInterval(Timer, TimerSetup->Interval);
XTtcPs_SetPrescaler(Timer, TimerSetup->Prescaler);

xil_printf("Initializing sine_ampl array!\r\n");


// sine_ampl array initialization
init_sin_f (sine_ampl);

threshold = (SCALING_FACTOR_0/8) * sine_ampl[count_depth];

/************************* Main Loop *****************************/


xil_printf("Entering main loop.\r\n");
while(1)
{
// Start the timer
XTtcPs_Start(Timer);

// Read the switch position


sw0 = XGpioPs_ReadPin (&GpioLeds, ps_sw3_a);

// Check the switch position


if ((sw0 & SWITCH_POS) == 0) // Masking (we want to check the status of SW0 only)
{
end_time = END_TIME_0;
// We must further divide the end_time with the factor of 8, because we use pre-scaler set to
value 8
end_time = end_time/8;
// We must further divide the scaling_factor with the factor of 8, because we use pre-scaler
set to value 8
scaling_factor = SCALING_FACTOR_0/8;
}
else
{
end_time = END_TIME_1;
end_time = end_time/8;
scaling_factor = SCALING_FACTOR_1/8;
}

// Send current system status information to the terminal using UART


if (sw0 != prev_sw0)
{
if ((sw0 & SWITCH_POS) == 0)
xil_printf("User selected PWM signal generation with 1 Hz frequency.\r\n");
else
xil_printf("User selected PWM signal generation with 3.5 Hz frequency.\r\n");
}
prev_sw0 = sw0;

// Turn on the LED


XGpioPs_WritePin(&GpioLeds, ps_sw2_a, 0x1);

do // Pause
{
current_time = XTtcPs_GetCounterValue(Timer);
}
while(current_time<threshold);

// Turn off the LED


XGpioPs_WritePin(&GpioLeds, ps_sw2_a, 0x0);

do // Pause
{
current_time = XTtcPs_GetCounterValue(Timer);
}
while(current_time<end_time);

// Reset the timer


XTtcPs_ResetCounterValue(Timer);

count_depth ++;
if (count_depth == COUNT_DEPTH_END)
count_depth = 0;

threshold = scaling_factor * sine_ampl[count_depth];


// We must multiply current amplitude value of the sine signal with the scaling_factor
// (48640/4096=11) to "stretch" the range from (0 - 2^width(=4096)) to (0 - 48640)
}

return 0;
}

107
CREATING THE SOFTWARE PLATFORM USING SDK

Modulator design with interrupt controller:


Step 2. In the modulator_socius_intc application project, create modulator_socius_intc.c, modulator.h and init_sin.c
source files on the same way as it is explained in the sub-chapter 3.3 "Creating a C/C++ source files for MicroBlaze-based
processor system".
The complete modulator.h and init_sin.c source files you can also find in the sub-chapter 3.3 "Creating a C/C++ source
files for MicroBlaze-based processor system".
The complete modulator_socius_intc.c source files you can find in the text below.
modulator_socius_intc.c:

#include "xparameters.h"
#include "xparameters_ps.h"
#include "xgpiops.h"
#include "xstatus.h"
#include "xttcps.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "modulator.h"
#include "xil_printf.h"

#define TTC_DEVICE_ID XPAR_XTTCPS_0_DEVICE_ID


#define TTC_INTR_ID XPAR_XTTCPS_0_INTR

// Definitions of actual pin locations for LED and push-button on the socius board
#define ps_sw2_a 10
#define ps_sw2_b 11
#define ps_sw3_a 12

// New type definition that will hold all relevant configuration parameters for the TTC module
typedef struct {
u32 OutputHz; /* Output frequency */
u16 Interval; /* Interval value */
u8 Prescaler; /* Prescaler value */
u16 Options; /* Option settings */
} TmrCntrSetup;

// Instance of a TmrCntrSetup type, holding TTC0 configuration parameters that will be used in modulator
example
static TmrCntrSetup SettingsTable[1] = {
{10, 65000, 2, 0}, /* Ticker timer counter initial setup, only output freq */
};

// global variables necessary for the Global Interrupt Controller initialization


XScuGic INTCInst;
XScuGic_Config *IntcConfig;

static int interrupt_occurred = 0; // variable which will signal when that interrupt has occurred

// Interrupt handler for the timer


void Timer_InterruptHandler(void *CallBackRef)
{
u32 StatusEvent;

interrupt_occurred = 1;

// stop timer
XTtcPs_Stop((XTtcPs *)CallBackRef);
StatusEvent = XTtcPs_GetInterruptStatus((XTtcPs *)CallBackRef);
XTtcPs_ClearInterruptStatus((XTtcPs *)CallBackRef, StatusEvent);
}

// Interrupt system initialization function


int IntcInitFunction(u16 DeviceId, XTtcPs *TtcPsInt)
{
int status;

// Interrupt controller initialization


IntcConfig = XScuGic_LookupConfig(DeviceId);
status = XScuGic_CfgInitialize(&INTCInst, IntcConfig, IntcConfig->CpuBaseAddress);
if(status != XST_SUCCESS) return XST_FAILURE;

// Connect the interrupt controller interrupt handler to the hardware interrupt


// handling logic in the ARM
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
&INTCInst);

// Enable interrupt controller


Xil_ExceptionEnable();

// Connect timer interrupt to device driver handler


status = XScuGic_Connect(&INTCInst,
TTC_INTR_ID,
(Xil_ExceptionHandler)Timer_InterruptHandler,
(void *)TtcPsInt);

108
3.5 Creating a C/C++ source files for a socius board based hardware platform

if(status != XST_SUCCESS) return XST_FAILURE;

// Enable timer interrupt in the interrupt controller


XScuGic_Enable(&INTCInst, TTC_INTR_ID);

// Enable timer interrupt generation in the timer module


XTtcPs_EnableInterrupts(TtcPsInt, XTTCPS_IXR_ALL_MASK);

return XST_SUCCESS;
}

int main(void)
{
/*********************** Variable Definitions ********************/
XGpioPs GpioLeds; // XGPIO instance that will be used to work with LED
XGpioPs_Config *GPIOConfigPtr;

XTtcPs_Config *Config; // Pointer to a XTtcPs_Config type


XTtcPs TimerInst; // TIMER instance
XTtcPs *Timer; // Pointer to a XTtcPs type
TmrCntrSetup *TimerSetup; // Pointer to a TmrCntrSetup type

int count_depth = 0; // Counter for sine samples


int sw0, prev_sw0; // Switch used for selecting frequency
int scaling_factor; // Will be used to represent the SCALING_FACTOR_0 or SCALING_FACTOR_1 value

unsigned int end_time; // Will be used to represent the END_TIME_0 or END_TIME_1 value
unsigned int threshold; // Will be used to represent the current value of the sine signal

unsigned int sine_ampl[COUNT_DEPTH_END]; // Sine amplitude values that will be used to generate the PWM
signal

int led_state; // Current state of the LED


int reset_value_0; // Is the value for timer to now from which value will start counting
downwards
// reset_value_0 = end_time - threshold
int reset_value_1; // Is the value for timer to now from which value will start counting
downwards
// reset_value_1 = threshold

int status; // Interrupt initialization function return status variable

/*********************** Initialization **************************/


xil_printf("Initializing peripherals!\r\n");

xil_printf("Initializing LEDs!\r\n");
// LEDs initialization
GPIOConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
XGpioPs_CfgInitialize(&GpioLeds, GPIOConfigPtr, GPIOConfigPtr ->BaseAddr);
// On the socius board we must properly control both ends of a LED, hence we must use two GPIO ports
XGpioPs_SetDirectionPin(&GpioLeds, ps_sw2_a, 1);
XGpioPs_SetOutputEnablePin(&GpioLeds, ps_sw2_a, 1);
XGpioPs_SetDirectionPin(&GpioLeds, ps_sw2_b, 1);
XGpioPs_SetOutputEnablePin(&GpioLeds, ps_sw2_b, 1);
// Set the value of one end of LED to always be equal to zero, by changing the other end we will turn
it on and off
XGpioPs_WritePin(&GpioLeds, ps_sw2_b, 0x0);

xil_printf("Initializing SWITCHes!\r\n");
// SWITCHes initialization
// Set the direction of the GPIO port connected to the push button to INPUT
XGpioPs_SetDirectionPin(&GpioLeds, ps_sw3_a, 0);

xil_printf("Initializing TIMER!\r\n");
// TIMER initialization
TimerSetup = &SettingsTable[TTC_DEVICE_ID];

// Read the current configuration of the timer module


Config = XTtcPs_LookupConfig(TTC_DEVICE_ID);

// Store the current configuration of the timer in a TimerInst object, using a pointer to access it
Timer = &TimerInst;
Timer->Config = *Config;

// Stop the timer


XTtcPs_Stop(Timer);

// Initialize the timer with the required configuration parameters


XTtcPs_CfgInitialize(Timer, Config, Config->BaseAddress);
TimerSetup->Options |= (XTTCPS_OPTION_INTERVAL_MODE |
XTTCPS_OPTION_DECREMENT |
XTTCPS_OPTION_WAVE_DISABLE);
XTtcPs_SetOptions(Timer, TimerSetup->Options);
XTtcPs_SetPrescaler(Timer, TimerSetup->Prescaler);

xil_printf("Initializing INTERRUPT CONTROLLER!\r\n");


// INTERRUPT CONTROLLER initialization
status = IntcInitFunction(XPAR_PS7_SCUGIC_0_DEVICE_ID, Timer);
if(status != XST_SUCCESS) return XST_FAILURE;

109
CREATING THE SOFTWARE PLATFORM USING SDK

xil_printf("Initializing sine_ampl array!\r\n");


// sine_ampl array initialization
init_sin_f (sine_ampl);

// Read the switch position


sw0 = XGpioPs_ReadPin (&GpioLeds, ps_sw3_a);

// Check the switch position


if ((sw0 & SWITCH_POS) == 0) // Masking (we want to check the status of SW0 only)
{
end_time = END_TIME_0;
// We must further divide the end_time with the factor of 8, because we use pre-scaler set to value
8
end_time = end_time/8;
// We must further divide the scaling_factor with the factor of 8, because we use pre-scaler set to
value 8
scaling_factor = SCALING_FACTOR_0/8;
}
else
{
end_time = END_TIME_1;
end_time = end_time/8;
scaling_factor = SCALING_FACTOR_1/8;
}
threshold = scaling_factor * sine_ampl[count_depth]; // threshold = current amplitude value of the
sine signal
// We must multiply current amplitude value of the sine signal with the scaling_factor
// (48640/4096=11) to "stretch" the range from (0 - 2^width(=4096)) to (0 - 48640)

reset_value_0 = end_time - threshold;


reset_value_1 = threshold;

// Set the timer countdown interval


XTtcPs_SetInterval(Timer, reset_value_1);

// Turn on the LED


XGpioPs_WritePin(&GpioLeds, ps_sw2_a, 0x1);

// Start the timer


XTtcPs_Start(Timer);

interrupt_occurred = 0; // Interrupt_occurred initialization


led_state = 1; // Set the initial state of the LED

/************************* Main Loop *****************************/


xil_printf("Entering main loop.\r\n");
while(1)
{
// Read the switch position
sw0 = XGpioPs_ReadPin (&GpioLeds, ps_sw3_a);

// Check the switch position


if ((sw0 & SWITCH_POS) == 0) // Masking (we want to check the status of SW0 only)
{
end_time = END_TIME_0;
// We must further divide the end_time with the factor of 8, because we use pre-scaler set to
value 8
end_time = end_time/8;
// We must further divide the scaling_factor with the factor of 8, because we use pre-scaler
set to value 8
scaling_factor = SCALING_FACTOR_0/8;
}
else
{
end_time = END_TIME_1;
end_time = end_time/8;
scaling_factor = SCALING_FACTOR_1/8;
}

// Send the current system status information to the terminal using UART
if (sw0 != prev_sw0)
{
if ((sw0 & SWITCH_POS) == 0)
xil_printf("User selected PWM signal generation with 1 Hz frequency.\r\n");
else
xil_printf("User selected PWM signal generation with 3.5 Hz frequency.\r\n");
}
prev_sw0 = sw0;

if (interrupt_occurred == 1)
{
interrupt_occurred = 0;

if (led_state == 1)
{
// Write the starting counter value, reset_value_0 = end_time - threshold
XTtcPs_SetInterval(Timer, reset_value_0);

// Turn off the LED

110
3.6 Viewing and configuring Linker Script file

XGpioPs_WritePin(&GpioLeds, ps_sw2_a, 0x0);

// Start the timer


XTtcPs_Start(Timer);

led_state = 0;

count_depth ++;
if (count_depth == COUNT_DEPTH_END)
count_depth = 0;
threshold = scaling_factor * sine_ampl[count_depth];

reset_value_0 = end_time - threshold;


reset_value_1 = threshold;

}
else
{
// Write the starting counter value, reset_value_1 = threshold
XTtcPs_SetInterval(Timer, reset_value_1);

// Turn on the LED


XGpioPs_WritePin(&GpioLeds, ps_sw2_a, 0x1);

// Start the timer


XTtcPs_Start(Timer);

led_state = 1;
}
}
}

return 0;
}

Step 3. Final step requires adding a math library to the ARM v7 gcc linker library settings. This needs to be done for
both modulator_socius_no_intc and modulator_socius_intc application projects. To include math library, please repeat
steps 9 - 14 from sub-chapter 3.4 "Creating a C/C++ source files for ARM-based processor system".

3.6 Viewing and configuring Linker Script file

A linker is a program that takes one or more object files (.o) generated by a compiler and combines them into a single
executable (.elf) file, see Illustration 3.14.

Figure 3.14: Linking and Locating process

Linker program combines all files from the application project into the executable .elf file. This process is controlled by the
Linker Script file.
Elf file is organized by logical section from each object file. Each section is located in a physical memory space as defined
by the linker script. Relocatable symbols are resolved to their physical addresses. Other symbols, such as those for
debugging are also added to the .elf file.

111
CREATING THE SOFTWARE PLATFORM USING SDK

Figure 3.15: Linker and Locator Flows

When the linker executes, it first combines all of the object sections. Then it resolves addresses and writes LDL files, see
Illustration 3.15.
Linker Script controls the linking process. It maps the code and data to a specified memory space, sets the entry point to
the executable, reserve space for the heap and stack, define the layout and start address of each section. Linker script
is required if the design contains a discontinuous memory space. It has it’s own language and can be difficult to write.
Because of this, Xilinx provides a Linker Script Generator.
Linker Script will be automatically generated when you create an Xilinx C Project within Xilinx SDK tool. In our case it will
be in the moment when we have created modulator_no_intc C project.
If you want to view or make some modifications to existing linker script file, please do the following:
Step 1. In the SDK Project Explorer tab, right-click on the modulator_no_intc project, select Generate Linker Script
and the Linker Scrip dialog box will appear, see Illustration 3.16

Figure 3.16: Generate linker script dialog box

Step 2. If you want to modify the default settings for the Linker Script file, make the required modifications, click Generate
button and the new linker script file will be created

112
3.7 Building application and generating ELF file

3.7 Building application and generating ELF file

In a microprocessor-based design such as a MicroBlaze design, an ELF file generated in the SDK or in some other software
development tool, can be imported and associated with a block design in the Vivado IDE. A bistream file can be generated
that includes the ELF content from the Vivado IDE and run on target hardware.
To build an executable file for this application, SDK performs the following actions, see Illustration 3.17.

Figure 3.17: SDK Software Flow

• First, SDK builds the Board Support Package (BSP) using LibGen tool. In our case it is called software platform.

• Then, SDK compiles the application software using platform- specific gcc/g++ compiler.

• At the end, the object files from the application and the BSP are linked together to form the final executable file (.elf
file). This step is performed by a linker which takes as input a set of object files and a linker script that specifies
where object files should be placed in memory.

SDK builds BSP ones, after it’s creation. For every source file modification, SDK will automatically generate a new .elf file,
compiling all source files that are out of date and linking them with the BSP.
The following sections provide an overview of concepts involved in building applications:
Makefiles
Compilation of source files into object files is controlled using Makefiles. With SDK, there are two possible options for
Makefiles:

1. Managed Make : For Managed Make projects, SDK automatically creates Makefiles. Makefiles created by SDK
typically compile the sources into object files, and finally link the different object files into an executable. In most
cases, managed make simply eliminates the job of writing Makefiles. This is the suggested option.

2. Standard Make : If you want ultimate control over the compilation process, use standard make projects. In this case,
you must manually write a Makefile with steps to compile and link an application. Using the standard Make flow
hides a number of dependencies from SDK, so you must follow manual steps for other tasks such as debugging or
running the application from within SDK. Therefore, the Standard Make flow is not recommended for general use.

113
CREATING THE SOFTWARE PLATFORM USING SDK

Build Configurations
Software developers typically build different versions of executables, with different settings used to build those executables.
For example, an application that is built for debugging uses a certain set of options (such as compiler flags and macro
definitions), while the same application is built with a different set of options for eventual release to customers. SDK makes
it easier to maintain these different profiles using the concept of build configurations.
Each build configuration could customize:

• Compiler Settings: Debug and Optimization levels

• Macros passed for compilation

• Linker Settings

When the program finishes building selected configuration, build report will be visible in the SDK’s Console window, includ-
ing generated code size information. For example, in case of building modulator_no_intc configuration for MicroBlaze and
ARM microprocessors, code size reports are shown on the Illustrations 3.18 and 3.19.

Figure 3.18: Console window with code size information for the modulator_no_intc build configuration for MicroBlaze-based
system

Figure 3.19: Console window with code size information for the modulator_no_intc build configuration for ARM-based
system

As a part of building process, information on the size of your application will normally be displayed at the end of the build log
of the Console view, as it is shown on the Illustrations 3.18 and 3.19. Here is the explanation for each column separately:

• text - shows the size of the code and read-only (constant) data in your application (in decimal)

• data - shows the size of the initialised data in your application (in decimal). Data counted in the "data" section is not
constant, so it will end up in RAM memory. But, because this data is initialised and the initial value is constant, it
will be stored in the FLASH memory. The linker allocates the space for initial data values in FLASH which are then
copied to RAM in the startup code.

• bss - shows the size of uninitialized data in your application (in decimal). bss counts for the uninitialized data in the
RAM which will be initialized to zero value in the startup code.

114
3.8 Running Application

• dec - total size, "text" + "data" + "bss" (in decimal)

• hex - hexadecimal equivalent of "dec"

Typically,

• the FLASH consumption of your application will be "text" + "data"

• the RAM consumption of your application will be "data"+ "bss"

Remember that the RAM consumption provided by this is only that of global data. It will not include any memory consumed
by application stack and heap when application is actually executing.
By comparing the sizes of the generated executable files for MicroBlaze and ARM processors, shown on the illustrations
3.18 ans 3.19, we can see that they differ. This is typical if we compile the same source code targeting different processors.
The reason for this is that different tool-chains (compilers, linkers, assemblers) are used with different processors. Further-
more, different processors have different instruction sets and micro-architectures. All this inevitably leads to generation
of different sizes of executable files when targeting different processors, even if completely identical source code is used.
Please notice that in our case source codes for the MicroBlaze and ARM targets are actually not completely identical,
since in the ARM case additional xil_printf function calls have been used. This also contributed to the increased size of
the generated executable file for the ARM-based system. Because of all this when planing to migrate an existing software
application to a new processor, careful considerations need to be made regarding the expected executable file size, which
can either increase or decrease compared to existing solution. The same holds for the execution time of the software
application when targeting different processor platforms.
Additional aspect that can also influence the size and speed of the generated software platform is the choice of the pro-
gramming language used to specify the software part of an embedded system. Currently C language dominates embedded
software development, but C++ language is increasingly starting to be used also. The reason for this is that the C++ is
one of the dominant programming languages used in desktop applications, servers and networks to which an embedded
system will typically interface. Furthermore, there are many additional pros for using C++ language in embedded system
development: better support for multicore programming, use of object oriented programming style, function overloaded,
use of templates, etc. However, when compiled, C++ programs tend to be bigger and slower then equivalent C programs.
In the past this was a major concern, but nowadays, with the availability of matured C++ compilers targeting embedded
systems and with careful usage of advanced C++ language features this no longer needs to be or is the case. Having
said this, this doesn’t imply that C language will stop being one of the major programming languages used for embedded
software development in the foreseeable future, especially in the resource- and time-critical embedded applications.

3.8 Running Application

You can run your software application on your hardware platform using SDK tool. The program will run to termination. You
can also stop the program at any time of execution. The run workflow is described in the following diagram, see Illustration
3.20.

115
CREATING THE SOFTWARE PLATFORM USING SDK

Figure 3.20: Run Workflow diagram

The workflow is made up of the following components:

• Executable ELF File : To debug your application, you must use a compiled Executable and Linkable Format (ELF)
file.

• Run Configuration : To launch the run session, you must create a run configuration in SDK. This configuration
captures options required to start a run session, including the executable name, processor target to run and other
information.

• JTAG Settings : In most cases, SDK can automatically detect the JTAG settings and doesn’t require special settings.

• Run Console : This XMD console view enables you to stop the program execution or terminate the run session.

You can repeat the cycle of modifying the code, building the executable, and running the program in SDK. The program
can be run on all supported debug targets.
Before you can run your application, you must generate netlist and bitstream file (if you didn’t generate them after the
hardware platform is specified) and download the FPGA’s bitstream file to the board.
To generate netlist and bitstream file, go back to the Vivado IDE main window and follow the same steps as it is explained
in the Chapter 2.3 Create MicroBlazebased hardware platform, steps 70, 71 and 72.

3.8.1 Downloading MicroBlaze-based bitstream file

To download your MicroBlaze-based bitstream file to the target board, do the following:
Step 1. Select Xilinx Tools -> Program FPGA from the SDK main window

116
3.8 Running Application

Figure 3.21: Program FPGA option

Step 2. In the Program FPGA dialog box:


For Hardware Configuration:

• set Hardware Platform to be modulator_wrapper_hw_platform_0

• set Connection to be Local

For Software Configuration:

• choose in the EFF File to Initialize in Block RAM drop-down many modulator_no_intc.elf file

Figure 3.22: Program FPGA dialog box

Step 3. Click Program and your updated bitstream file with the .elf file will be downloaded into the target FPGA device,
see Illustration 3.23

117
CREATING THE SOFTWARE PLATFORM USING SDK

Figure 3.23: Program FPGA flow

A Block RAM Memory Map (BMM) file is a text file that has syntactic descriptions of how individual block RAMs constitute a
contiguous logical data space. When updating the FPGA bitstream with memory initialization data (typically the executable
program), the Data2MEM utility uses the BMM file to direct the translation of data into the proper initialization form. Although
the BMM file is a text file direct editing is not recommended. This file is generated by the PlatGen (Platform Generator) tool
and updated with physical location information by the BitGen (Bitstream Generator) tool.

3.8.2 Downloading ARM-based bitstream file

To download your ARM-based bitstream file to the target board, do the following. Connect the additional USB cable that
will be used to provide UART interface, that will be used during system debug, and do the following steps:
Step 1. Select Xilinx Tools -> Program FPGA from the SDK main window
Step 2. The Program FPGA diloag box will appear. The bitstream field should already be populated with the correct
bitstream file, see Illustration 3.24. Click Program
Ones the device has successfully been programmed, the DONE LED on the ZedBoard will turn blue.

118
3.8 Running Application

Figure 3.24: Program FPGA diloag box

Step 3. At the bottom of the SDK, open SDK Terminal tab and click the green "+" button to connect with the serial port,
see Illustration 3.25

Figure 3.25: SDK Terminal window

Step 4. In the Connect to serial port dialog box, in the Port field choose COM3 serial port to connect with and leave all
other parameters unchanged, see Illustration 3.26. Click OK

119
CREATING THE SOFTWARE PLATFORM USING SDK

Figure 3.26: Connect to serial port dialog box

After connecting the terminal with the serial port, in the SDK Terminal window you should see notification about success-
fully connection, see Illustration 3.27

Figure 3.27: Terminal notification about successful connection to serial port

Step 5. When the ZynqPL is successfully configured with the bitstream file, we can now launch our software application
on the Zynq PS:
In the Project Expolorer select your application project, right-click on it and select Run As -> Launch on Hardware
(System Debugger) option
Step 6. Open the SDK Terminal window and you should see all the messages sent by software application, see Illustration
3.28. As you can see, before changing switch position, one PWM signal generation frequency is selected.

Figure 3.28: Terminal window with messages sent by software application

120
3.9 Application Debugging

Step 7. Change the switch position on the development board and the terminal will detect that the second PWM signal
generation frequency is selected, as we predicted in the software application, see Illustration 3.29

Figure 3.29: Terminal window with messages sent by software application after changing the switch position on the devel-
opment board

3.9 Application Debugging

Debugging is an integral part of embedded systems development. The debugging process implies testing, stabilizing,
localizing and correcting errors. There are two methods of debugging:

• Hardware debugging via logic probe, logic analyzer, on- circuit emulator, or background debugger

• Software debugging via a debugging instrument

SDK supports software debugging through:

• GDB tools
GDB (GNU Debugger) is a powerful, flexible tool that provides a unified graphical interface for debugging and ver-
ifying MicroBlaze processor systems during various development phases. With GDB debugger you can debug
programs written in C and C++.

• Xilinx Microprocessor Debugger (XMD)

– Runs all the hardware debugging tools and communicates with the hardware
– Shell for hardware communication
– Tool command language (Tcl) syntax and command interpreter

• GNU tools

– Communicate with the hardware through XMD

The actual debugger is XMD. GDB is the user interface, or GUI, that talks to XMD through a TCP/IP port via Tcl commands,
see Illustration 3.30.

Figure 3.30: GDB overview

121
CREATING THE SOFTWARE PLATFORM USING SDK

The main purpose of XMD is to attach to the debug hardware interface of the embedded processor, the MicroBlaze Debug
Module (MDM). This is done via an internal JTAG chain facilitated by the BSCAN component on the FPGA. The MDM also
offers a JTAG uart feature that will show up as a AXI bus uart peripheral for the MicroBlaze. XMD provides many services,
including download cable connection and control. One of the main functions of XMD is the debug engine. This engine
provides the command interface to the processor debug hardware via a Tcl script and/or simple command line interface.
You could directly debug a program from the XMD command line console, but this would be a painful process. The GDB
debugger provides an easy-to-use graphical interface that interfaces Tcl with XMD.

3.9.1 Debug Overview

With the SDK debugger, you can see what is happening to a program while it executes. You can set breakpoints or watch-
points to stop the processor, step through program execution, view the program variables and stack, and view the contents
of the memory in the system. The SDK debugger uses the GNU Debugger (GDB) with Xilinx Microprocessor Debug-
ger (XMD) as the underlying debug engine. It translates each user interface action into a sequence of GDB commands
and processes the output from GDB to display the current state of the program being debugged. It communicates to the
processor on the hardware and Instruction Set Simulator (ISS) target using XMD.

Figure 3.31: Debug Workflow diagram

The workflow is made up of the following components:

• Executable ELF File : To debug your application, you must use an Executable and Linkable Format (ELF) file
compiled for debugging. The debug ELF file contains additional debug information for the debugger to make direct
associations between the source code and the binaries generated from that original source.

• Debug Configuration : In order to launch the debug session, you must create a debug configuration in SDK. This
configuration captures options required to start a debug session, including the executable name, processor target to
debug, and other information.

• JTAG Settings : When debugging the program on a hardware target, SDK uses XMD for communication to the
processor using a JTAG interface on the board. The JTAG settings for the debug session can be specified in the
JTAG Settings dialog box. In most cases, the debugger can automatically detect the JTAG settings and do not need
to provide special settings.

• SDK Debug Perspective : Using the Debug perspective, you can manage the debugging or running of a program
in the Workbench. You can control the execution of your program by setting breakpoints, suspending launched
programs, stepping through your code, and examining the contents of variables.

You can repeat the cycle of modifying the code, building the executable, and debugging the program in the SDK.

122
3.9 Application Debugging

Note: If you edit the source code after compiling, the line numbering will be out of step because the debug information is
tied directly to the source. Similarly, debugging optimized binaries can also cause unexpected jumps in the execution trace.
Hardware debug target
SDK supports debugging of a program on processor running on a FPGA. All processor architectures are supported. SDK
communicates to the processor on the FPGA over the JTAG interface using the Xilinx JTAG cable. Before you debug the
processor on the FPGA, you should configure the FPGA with the appropriate system bitstream.
The debug logic for each processor enables program debugging by controlling the processor execution. The debug logic
on hard ARM processor cores is built in and always available for debugging. However, the debug logic on soft MicroBlaze
processor cores is configurable and can be enabled or disabled by the hardware designer when building the embedded
hardware.
Enabling the debug logic on MicroBlaze processors provides advanced debugging capabilities such as hardware break-
points, read/write memory watchpoints, safe-mode debugging, and more visibility into MicroBlaze processors. This is the
recommended method of debugging MicroBlaze software.
If the debug logic is disabled on the hardware, you can debug programs using XMDStub (a ROM monitor). XMDStub is a
small debug stub that runs on MicroBlaze processors and can perform basic debug operations such as reading and writing
memory and register values and controlling the program execution. It should be initialized to the processor local memory
at the reset location, so when the processor resets, the XMDStub is run and ready for debugging. It communicates to XMD
over a Universal Asynchronous Receiver-Transmitter (UART), which could be JTAG-based or RS232-based. This method
is not supported in SDK and you should use the XMD command-line tool for debugging.

3.9.2 Debug Configuration

To debug, run, and profile an application, you must create a configuration that captures the settings for executing the
application. The configurations for debugging, running, and profiling an application are similar.
To setup a debug configuration, do the following:
Step 1. In the SDK main window, select modulator_no_intc project and select Run -> Debug Configurations... option,
see Illustration 3.32

Figure 3.32: Debug Configurations option

The another way to open Debug Configurations dialog box is to select modulator_no_intc project in the Project Explorer
window, right-click on it and choose Debug As -> Debug Configurations... option
Step 2. In the Debug Configurations dialog box, right-click on the Xilinx C/C++ application (System Debugger) and
choose New option, or click on the New launch configuration button on the upper left corner of the window to create a
configuration of the selected type, see Illustration 3.33

123
CREATING THE SOFTWARE PLATFORM USING SDK

Figure 3.33: Creating a new configuration of the selected type

Step 3. You can see that SDK automatically create new System Debugger using Debug_modulator_no_intc.elf on
Local Debug configuration for us, see Illustration 3.34

Figure 3.34: Automatically generated “modulator_no_intc Debug by the SDK” configuration

124
3.9 Application Debugging

As you can see, you can provide an unique name for your configuration and select the application executable to use for
execution. Select the appropriate executable for debug configurations, see Illustration 3.35. You only need to create the
configuration once for the first execution of the application. For subsequent execution of the application, you can select the
configuration from the Debug drop-down list in the toolbar.

Figure 3.35: Application tab

Step 4. Click Debug


The ELF file will be downloaded to the FPGA into the bootloop placeholder space in the betstream.
Step 5. If the Confirm Perspective Switch dialog box appears, click Yes to switch to the Debug perspective.
You can also switch to this perspective by clicking on the Open Perspective button on the top right bar, see Illustration
3.36

Figure 3.36: Open Perspective button

When the Open Pespective dialog box appears, choose Debug option and click OK, see Illusteation 3.37.

125
CREATING THE SOFTWARE PLATFORM USING SDK

Figure 3.37: Open Perspective dialog box

3.9.3 Debug Perspective

The Debug perspective lets you manage the debugging or running of a program in the Workbench. You can control
the execution of your program by setting breakpoints, suspending launched programs, stepping through your code, and
examining the content of variables.
The Debug perspective displays the following information:

• Each program running on the processor (represented as a node in the tree)

• The stack frame for the suspended program that you are debugging

The Debug perspective also drives the C/C++ Editor. As you step through your program, the C/C++ Editor highlights the
location of the execution pointer, see Illustration 3.38.
Step 1. The Debug perspective will open, showing the modulator_no_intc_mb.c source file in the source view, various
variables defined in the file in the Variables view, Outline view showing the objects which are in the current scope, thread
created and the program suspended in the Debug view. Note that the program operation is suspended at the first executable
statement (at the main() entry point), see Illustration 3.38.

126
3.9 Application Debugging

Figure 3.38: SDK Debug Perspective

In the process of debugging the most important task is adding breakpoints to halt program execution at the user specified
points. Once program execution is suspended, user can use Step Into, Step Over, Step Return and Resume commands
to control program execution from the encountered breakpoint. These commands, together with variables and memory
views, enable user to have total control and overview of the program execution process during debugging.
Following steps will illustrate how these commands can be used in debug process.
Step 2. First we will illustrate the usage of Step Into, Step Over and Step Return commands. As already noted, after
debugging process is started, program operation will suspend at the start of the main() function. We can use Step Into
button to enter into the Xilinx provided function for the GPIO initialization, XGpio_Initialize, in order to overview the function
execution in more details. After you press the Step Into button, debugger will reach the XGpio_Initialize function call within
the main() function. After pressing the Step Into button once more, debugger will automatically jump to the first executable
statement of the XGpio_Initialize function as shown on the Illustration 3.39.

Figure 3.39: Result of the execution of the Step Into command on the XGpio_Initialize function

127
CREATING THE SOFTWARE PLATFORM USING SDK

Step 3. While the debugger is working within a function call, you can use Step Return button to execute all remaining
statements within a function in order to quickly return to the point where a function has been called. In our case if we press
Step Return button once, debugger will execute all remaining statements within the XGpio_Initialize function and return
to the main() function, because XGpio_Initialize function has been called from the main() function, and suspend program
execution at the next executable statement, which in our case is yet another function call, this time to the XGpio_SetData-
Direction function, as shown on the Illustration 3.40.

Figure 3.40: Result of Step Return command execution within XGpio_Initialize function

Step 4. Although Step Into command can be very useful in the process of program debugging, quite often we are not
interested into details of every function execution. If we would like to skip over known working functions, because we have
debugged them previously, we can use Step Over button that will execute the complete function in one step, treating C
function calls as a single C statement. Please notice that our program that we are currently debugging has suspended
execution at the XGpio_SetDataDirection function call as shown on the previous illustration. If you are not interested in
the details of the execution of this function, you can execute it at once by clicking on the Step Over button. Debugger will
now execute all the statements within the XGpio_SetDataDirection function and only then suspend the program execution
once more, after reaching the first executable statement located after the XGpio_SetDataDirection function, in our case
this would be another XGpio_Initialize function call, as shown on the Illustration 3.41.

Figure 3.41: Result of the execution of the Step Over command on the XGpio_SetDataDirection function

Breakpoints
Next we will illustrate how to use breakpoints to suspend program execution at the user-selected line of program code.
A breakpoint suspends the execution of a program at the location where the breakpoint is set. By default, SDK sets
breakpoints at main( ) and exit( ) functions. When you start a debug session, the processor stops at the start of the main(
) function of the program. There are two types of breakpoints used by the debugger:

• Software Breakpoint - To set a software breakpoint, the debugger modifies the program instruction at the breakpoint
address. The debugger does not require any hardware resources for setting a software breakpoint, so you can
essentially set any number of software breakpoints in your debug session. The debugger requires access to read
and write to the breakpoint address location. This is the default breakpoint used by the debugger.

• Hardware Breakpoint - To set a hardware breakpoint, the debugger does not require modification of the program
instruction at the breakpoint address. Each processor provides a limited set of hardware breakpoints. In the case
of MicroBlaze processors, this is configurable and set by the hardware developer and should be used wisely. You
should use hardware breakpoints when the debugger cannot read or write to the program memory, such as when
using Flash memory.

128
3.9 Application Debugging

Step 5. We will place the first breakpoint within init_sin_f function, at the line 36, where for loop is located. init_sin_f
function is defined in the init_sin.c source file. First we must select init_sin.c source file by clicking on the init_sin.c tab.
Next, point the mouse to the line 36 in the init_sin.c source file and right-click on the blue stripe located on the left border
of the Sources window. A drop-down menu will appear from which Add Breakpoint... option should be selected, see
Illustration 3.42.

Figure 3.42: Add Breakpoint option

Step 6. When you select Add Breakpoint... option a Properties for C/C++ Line Breakpoint dialog box will appear
allowing you to specify the properties of the new breakpoint as shown on the Illustration 3.43. Since we want to add a
simple breakpoint at this moment, we don’t have to change anything, so simply click OK.

Figure 3.43: Properties for C/C++ Line Breakpoint dialog box

Step 7. After you have added a new breakpoint, its location in the program code will be made visible by the blue circle
marker located on the blue stripe just left of the program code line for which the breakpoint was specified, see Illustration
3.44.

129
CREATING THE SOFTWARE PLATFORM USING SDK

Figure 3.44: Breakpoint added

Step 8. Please remember that our program execution is currently suspended at line 62 in the modulator_no_intc_mb.c
source file and that there are several executable statements located between this line and line 70 where call to the init-
_sin_f function is located, which contains the breakpoint. These statements need to be executed before reaching the
breakpoint. These executable statements can be executed by pressing the Step Over button appropriate number of times
until we reach line 70. However, this would be a very inefficient way of program debugging. Instead we can use Resume
button to quickly execute all executable statements between our current position and the breakpoint position.
Step 9. After we have pressed Resume button, debugger will execute all necessary statements until it reaches a breakpoint
set at line 36 within the init_sin.c source file and then suspend program execution, as show on the Illustration 3.45.

Figure 3.45: Breakpoint reached

Step 10. Next we will illustrate how the Memory tab can be used to monitor the content of the array variables. Please
notice that program execution is suspended at line 36. We will use the Monitor tab to overlook this initialization process.
First thing that must done is to determine the base address at which the sine_ampl array is stored in the memory. To do
so, look in the Variables tab for variable with sine_ampl name. Inspect the content of the Value field located in the same
row. This is the starting address of the sine_ampl array.

Figure 3.46: Starting address of the sine_ampl array

130
3.9 Application Debugging

Step 11. To open Memory tab, select Window -> Show View -> Memory option from the main menu
Step 12. In the Memory tab, click on the Add Memory Monitor button. A new dialog box will appear where we should
specify the address or expression to monitor. In our case we will specify the starting address of the sine_ampl array,
0xa690 , see Illustration 3.47. After you do so, click OK .

Figure 3.47: Monitor Memory dialog box

Step 12. In the Memory tab, please notice that a new Memory monitor has been added, monitoring the memory content
starting from the address 0xa690 as shown on the Illustration 3.48. Currently the content of all memory locations staring
from the address 0xa690 is 0x00000000. This is fine, since we still have not initialized the sine_ampl array.

Figure 3.48: Content of the sine_ampl array in Memory window before array initialization

Step 14. Let us initialize the first member of the sine_ampl array, with index value 0. Please press Step Over button once.
After the first step over command, debugger will execute the for statement. Since this is the first time this statement is
executed it will set the value of the iterator variable i to 0, as specified by the for statement. This change of the variable i
value is also indicated in the Variables tab, where line holding the variable i is coloured yellow and holds the new value for
the variable i, see Illustration 3.49.

Figure 3.49: Change of i variable value indication in the Variables tab

Step 15. Press Step Over button twice more. This time debugger will execute the line of code that initialises sine_ampl
array member with index value i=0. After debugger finishes executing this line of code it will suspend program execution and

131
CREATING THE SOFTWARE PLATFORM USING SDK

update the Memory tab as shown on the Illustration 3.50. If you inspect the value stored at the memory location 0xa690,
you can see that it has changed from 0x00000000 to 0x000007FF which is the correct initial value for the sine_ampl[0]
array member.

Figure 3.50: Indication of the change of the sine_ampl value in Memory window

Step 16. Finally, we will create a conditional breakpoint in order to stop the sine_ampl initialization process after a specified
number of array element have been initialized. Right-click on the blue stripe just left of the line 38 and select Add Break-
point... option once more. Properties for C/C++ Line Breakpoint dialog box will appear as before. Since now we would
like to place a conditional breakpoint we must specify breakpoint condition using the Condition field. In this example we
would like to break a program execution when loop iterator i reaches the value 5. This would mean that debugger should
stop sine_ampl array initialization process after sine_ampl members 0-4 have been initialized. To specify this condition
type i==5 in the Condition field as show on the Illustration 3.51 press OK button to complete the conditional breakpoint
setup.

Figure 3.51: Properties for C/C++ Line Breakpoint dialog box - condition breakpoint setup i==5

Step 17. You can verify that a new conditional breakpoint has been placed at the line 38 which is designated by the blue
circle located on the blue stripe just left to the line 38. Since this breakpoint is conditional breakpoint, next to the blue circle
a question marker is also visible as shown on the Illustration 3.52.

132
3.9 Application Debugging

Figure 3.52: Conditional breakpoint added

Step 18. Remove the existing breakpoint at line 36 by right-clicking on the blue stripe just left of the line 36 and selecting
Toggle Breakpoint option
Step 19. Press Resume button to continue program execution. Debugger will continue initialising sine_ampl array until
it reaches the condition specified in the conditional breakpoint located at line 38. This will happen when loop iterator
i reaches the value of 5. After this condition is met, debugger will suspend program execution and display the current
content of sine_ampl array in the memory tab as show on the Illustration 3.53. Please notice that sine_ampl members
with index values 0-4 have already been initialised to appropriate values, because memory locations with addresses from
0xA690 to 0xA6A0 have values that are different from 0. Since sine_ampl array is an array of unsigned integers, each
array member occupies one double word in the memory. This means that sine_ampl members with index values 0-4 should
occupied memory block starting from 0xa690 to 0xa6a0, which is exactly the memory block that has values different from
0 as show in the Memory tab.

Figure 3.53: Conditional breakpoint reached, i==5

133
CREATING THE SOFTWARE PLATFORM USING SDK

134
Chapter 4

DEBUGGING SYSTEM USING VIVADO LOGIC


ANALYZER

Vivado Logic Analyzer is an integrated logic analyzer in the Vivado Design Suite. In this chapter you will learn how to debug
your MicroBlaze-based system using the Vivado logic analyzer and you will take advantage of it’s functions to debug and
discover some potential root causes of your design.
In-system debugging allows you to debug your design in real-time on your target hardware. IP Integrator provides ways to
instrument your design for debugging, which will be explained in this chapter. There are two flows (methods) supported in
the Vivado Debug Probing:
1. HDL Instantiation Debug Probing Flow - This flow involves the manual customizations, instantiation, and connection
of various debug core components directly in the HDL design source. Debug cores that are supported in this flow, in the
Vivado tool, are:

• Integrated Logic Analyzer (ILA) core v6.2

• Virtual Input/Output (VIO) core v3.0

• Integrated Bit Error Ratio Tester (IBERT) core v3.0

• JTAG to AXI Master core v1.1

2. Using the Netlist Insertion Debug Probing Flow - Insertion of debug cores in the Vivado tool is presented in a layered
approach to address different needs of the diverse group of Vivado users:

• The highest level is a simple wizard that creates and configures Integrated Logic Analyzer (ILA) cores aurtomatically
based on the selected set of nets to debug

• The next level is the main Debug window allowing control over individual debug cores, ports and their properties

• The lowest level is the set of Tcl debug commands that you can enter manually or replay as a script What is also
important to note is that you can use combination of the modes to insert and customize debug cores

Choosing the flow depends on your preferences and types of nets/signals that you are interested in debugging. In this
tutorial we will explain the Netlist Insertion Debug Probing Flow on the Modulator IP integrated design.
In this flow you will mark nets that you are interested in analyzing in the block design. Marking nets for debug in the block
design offers more control in terms of identifying debug signals during coding and enabling/disabling debugging later in the
flow.
To start debugging process using the Netlist Insertion Flow in IP Integrator tool, please do the following:
Step 1. In the Vivado IDE Flow Navigator, click on the Open Block Design command under the IP Integrator to open our
modulator_mb block design, see Illustration 4.1
DEBUGGING SYSTEM USING VIVADO LOGIC ANALYZER

Figure 4.1: Finial Block Diagram of our design

Step 2. The next step will be to mark nets for debug


Nets can be marked for debug in the block design by highlighting them, right-clicking on them and selecting Debug option,
see Illustration 4.2.

Figure 4.2: Mark Debug option

Step 3. Mark the following nets for debug:

• in the microblaze_0_local_memory IP - DLMB interface

• in the axi_gpio_0 IP - S-AXI interface and GPIO bus

The nets that have been marked for debug will show a small bug icon placed on top of the net in the block design. Likewise,
a bug icon can be seen placed on the nets to be debugged in the Design Hierarchy window as well.
Step 4. Save block design

136
Step 5. The next step is to synthesize the design by clicking on the Run Synthesis command from the Flow Navigator,
under the Synthesis drop-down list
Step 6. In the Synthesis Completed dialog box, select Open Synthesized Design option and click OK
Step 7. The Schematic and the Debug window opens
Step 8. In the Debug window, click on the Set Up Debug icon, see Illustration 4.3, to launch Set Up Debug wizard to
guide you through the process of automatically creating debug cores and assigning debug nets to the inputs of the cores

Figure 4.3: Set Up Debug button

The another way to launch this wizard is to select Tools -> Set up Debug... option from the Vivado IDE main menu, see
Illustration 4.4

Figure 4.4: Tools - Set Up Debug option

Step 9. In the Set Up Debug dialog box, click Next

137
DEBUGGING SYSTEM USING VIVADO LOGIC ANALYZER

Figure 4.5: Set Up Debug dialog box

Step 10. In the Nets to Debug dialog box you will find nets that you have marked for debugging, see Illustration 4.6

Figure 4.6: Nets to Debug dialog box

In the Nets to Debug dialog box, you have also an opportunity to add more nets or remove existing nets from the table.
Click Find Nets to Add... button to open Find Nets dialog box, see Illustration 4.7

138
Figure 4.7: Find Nets dialog box

Step 11. If you are satisfied with the debug net selection, click OK
Step 12. If you want to remove some net from the Nets to Debug list, just select the desired net, right-click on it and
choose Remove Nets option, see Illustration 4.8

Figure 4.8: Remove Nets option

Step 13. Remove all the nets with undefined clock domain. In our case it will be six nets.
Note that all nets belong to the same clock domain, because signals captured by the same ILA core must have the same
clock domain.
Step 14. Ones you are satisfied with the debug net selection, click Next
Step 15. In the ILA Core Options dialog box:

• change the Sample of data depth value to 8192

• leave Input pipe stages to be 0

139
DEBUGGING SYSTEM USING VIVADO LOGIC ANALYZER

• enable Capture control and Advanced trigger options in the Trigger and Storage Settings, see Illustration 4.9

• click Next

Figure 4.9: ILA Core Options dialog box

Important: The Set up Debug wizard inserts one ILA core per clock domain!
The nets that were selected for debug are assigned automatically to the probe ports of the inserted ILA v6.2 core. The last
wizard screen shows the core creation summary displaying the number of clocks found and ILA cores to be created and/or
removed, see Illustration 4.10
Step 16. If you are satisfied with the results, click Finish in the Set Up Debug Summary dialog box to insert and connect
the ILA 6.2 core in your synthesized design netlist, see Illustration 4.10

Figure 4.10: Set Up Debug Summary dialog box

Step 17. The debug nets are now assigned to the ILA v6.2 debug core, what you can see in the Debug window

140
The generated ILA core you can also find in the Netlist window, see Illustration 4.11

Figure 4.11: Netlist window with generated ILA core

Now when debug nets are assigned to the ILA debug core, we must re- implement our design and re-generate new
bitstream file.
Step 18. In the Vivado Flow Navigator, click Run Implementation command, see Illustration 4.12, and wait for task to be
completed
Step 19. When the implementation process is completed, click Generate Bitstream command, see Illustration 4.12, and
wait for task to be completed
After this step, bitstream file will be generated.

Figure 4.12: Run Implementation and Generate Bitstream options

Step 20. Select File -> Export -> Export Hardware option from the main Vivado IDE menu
Step 21. In the Export Hardware dialog box, make sure that Include bitstream check box is checked, and Export to field
is set to Local to Project and click OK
Step 22. Select File -> Launch SDK from the main Vivado IDE menu to launch SDK after the hardware platform has been
exported
Step 23. In the Launch SDK dialog box, make sure that both Exported location and Workspace set to Local to Project
and click OK
Step 24. To download your new bitstream file to the target board, select Xilinx Tools -> Program FPGA from the SDK
main window
Step 25. In the Program FPGA dialog box:
For Hardware Configuration:

• set Hardware Platform to be modulator_wrapper_hw_platform_0

• set Connection to be Local

For Software Configuration:

141
DEBUGGING SYSTEM USING VIVADO LOGIC ANALYZER

• choose in the EFF File to Initialize in Block RAM drop-down many new modulator_no_intc.elf file, see Illustration
4.13

Figure 4.13: Program FPGA dialog box

Step 26. Click Program and your updated bitstream file with the .elf file will be downloaded into the target FPGA device

4.1 Illustration of using Vivado Logic Analyzer tool

Ones you have the debug cores in your design, you can use the logic analyzer to debug the design in hardware.
To access the Vivado logic analyzer feature:
Step 1. In the Vivado Flow Navigato, click the Open Hardware Manager -> Open Target -> Open New Target..
command in the Program and Debug section to program your FPGA device, see Illustration 4.14

Figure 4.14: Open Hardware Manager command

Step 2. In the Open Hardware Target dialog box, click Next

142
4.1 Illustration of using Vivado Logic Analyzer tool

Figure 4.15: Open Hardware Target dialog box

Step 3. In the Hardware Server Settings dialog box, specify or select a local or remote server, depending on what
machine your hardware target is connected to. Leave the default Local server and click Next, see Illustration 4.16
Local server: Use this setting if your hardware target is connected to the same machine on which you are running the
Vivado IDE. The Vivado software automatically starts the Vivado hardware server (hw_server) application on the local
machine.
Remote server: Use this setting if your hardware target is connected to a different machine on which you are running the
Vivado IDE. Specify the host name or IP address of the remote machine and the port number for the hardware server
(hw_server) application that is running on that machine.

Figure 4.16: Hardware Server Settings dialog box

143
DEBUGGING SYSTEM USING VIVADO LOGIC ANALYZER

Step 4. In the Select Hardware Target dialog box, select the appropriate hardware target from the list of targets that are
managed by the hardware server. Note that when you select a target, you will see the various hardware devices that are
available on the hardware target, see Illustration 4.17

Figure 4.17: Select Hardware Target dialog box

Note: If one or more of the devices is unknown to Vivado tool, you can provide the instruction register (IR) length directly
in the Hardware Devices table of the Open Hardware Target wizard
Step 5. Click Next
Step 6. In the Open Hardware Target Summary dialog box, click Finish to connect to the hardware described in the
summary window, see Illustration 4.18

Figure 4.18: Open Hardware Target Summary dialog box

144
4.1 Illustration of using Vivado Logic Analyzer tool

Ones you finish opening a connection to a hardware target, the Hardware window is populated with the hardware server,
hardware target, and various hardware devices for the open target, see Illustration 4.19

Figure 4.19: Hardware view after opening a connection to the hardware target

Step 7. The next step in design debugging process is to set up the ILA core. When debug cores are detected upon refresh-
ing a hardware device, the default dashboard for each debug core is automatically opened. The default ILA Dashboard
can be seen on the Illustration 4.20.

Figure 4.20: ILA Dashboard window

Every default dashboard contains windows relevant to the debug core the dashboard is created for. The default dashboard
created for the ILA debug core contains five windows, as can be seen on the previous illustration:

• Settings window

• Status window

• Trigger Setup window

• Capture Setup window

• Waveform window

Step 8. First we will use the ILA core to capture the AXI data write transaction to the axi_gpio_0 component. GPIO port
of the axi_gpio_0 component is connected to the LEDs, present on the development board, so to turn on and off the LED

145
DEBUGGING SYSTEM USING VIVADO LOGIC ANALYZER

we must write the appropriate values into the register that is part of the axi_gpio_0 component. This is done using the
AXI interface that connects the axi_gpio_0 component to the MicroBlaze processor. In order to capture the AXI data write
transaction to the axi_gpio_0 component, ILA core must be setup in the following way. We will setup the ILA core to trigger
when a LED is turned on. This means that the value 0x80 will be written into the axi_gpio_0 component. To capture this
condition, we must use advanced trigger mode of ILA core. In the ILA Settings window, select ADVANCED_ONLY option
from the Trigger mode drop-down list, like it is shown on the Illustration 4.21.
Step 9. ILA core will now use advanced triggering mode allowing us to specify a sequence of events that must occur in
order to trigger the ILA core. To specify trigger sequence we must create a new trigger state machine by selecting Create
new trigger state machine option in the Trigger Setup window, like it is show on the Illustration 4.24. At this time we can
also set Trigger position in window value to 4000 , which is also show on the Illustration 4.21.

Figure 4.21: Create new trigger state machine

Step 10. Ones you click on the Create new trigger state machine option a window will appear, asking you to specify the
new trigger state machine file name and its location. Name the new trigger state machine file as Trigger_FSM1 and save
it.
Step 11. After you have created Trigger_FSM1 state machine file its current content will be shown in the Trigger Setup
window. We must modify this content, as shown on the Illustration 4.22.

146
4.1 Illustration of using Vivado Logic Analyzer tool

Figure 4.22: Trigger FSM code

You can start adding probes to the Trigger Setup window by clicking the green + button and selecting probes from the
Insert Probes window, see Illustration 4.23

Figure 4.23: Insert Probe window

Our trigger state machine consists from two states, my_state0 and my_state1, as shown on the Illustration 4.24. Initially,
trigger state machine is in the my_state0 state, waiting until axi_gpio_0_GPIO_TRI_O probe has the value of 0x00. When
this condition is met, trigger state machine transitions to my_state1 state. While in this state, trigger state machine waits

147
DEBUGGING SYSTEM USING VIVADO LOGIC ANALYZER

until axi_gpio_0_GPIO_TRI_O probe has the value of 0x01. When this condition is met, trigger state machine will trigger
the ILA core and sample and store the ILA probe signals.

Figure 4.24: Trigger FSM State Diagram

Step 12. Having defined trigger sequence, we can arm the ILA core by clicking on the Run Trigger button.
Step 13. Once the ILA core captured data has been uploaded to the Vivado IDE, it is displayed in the Waveform window.
Use Zoom Fit and then Zoom In button to zoom-in into the area around the trigger marker, shown as the vertical red line,
in order to see waveforms of different signals and buses that are part of the AXI interface, as shown on the Illustration 4.25.

Figure 4.25: Captured waveform after trigger condition is reached

From the Illustration 4.25 we can see that the ILA has triggered exactly on the transition that we have specified in our trigger
state machine code. Trigger marker is positioned exactly on the point where axi_gpio_0_GPIO_TRI_O probe transitions
from value 0x00 to value 0x01. ILA has stored 4000 samples before this condition and additional 4192 samples after
this condition in its buffer, exactly as was specified. Furthermore, from the captured waveform we can also see the AXI
interface data write sequence. Value of 0x00000001 is placed on the microblaze_0_axi_periph_M02_AXI_WDATA write
data bus while the microblaze_0_axi_periph_M02_AXI_AWADDR write address bus has the value of 0x000. This is the
address of the Channel1 AXI GPIO Data Register from the axi_gpio_0 component that is connected to the LEDs on the
development boards, please see Table 2-4, on page 9, in the “PG144 LogiCORE AXI GPIO v2.0 Product Guide”.
Data is actually written into this register when microblaze_0_axi_periph_M02_AXI_WSTRB bus is set to value of 0xf and
microblaze_0_axi_periph_M02_AXI_WREADY signal goes high.

148
4.2 Illustration of using Vivado Logic Analyzer together with SDK Debugger

4.2 Illustration of using Vivado Logic Analyzer together with SDK Debugger

In the previous sub-chapter we have shown in one example how you can use Vivado Logic Analyzer to debug MicroBlaze-
based system. Now we will show in one another example how you can use Vivado Logic Analyzer together with SDK
debugger tool to debug your design.
Step 1. Our first step will be to remove all the Breakpoints added in our design in the previous sub-chapters. To remove
breakpoints from our design, in the SDK open Breakpoints tab, select breakpoints that you want to remove, right-click on
them and select Remove option, see Illustration 4.26.

Figure 4.26: Breakpoint tab - Remove option

Step 2. When we removed all unnecessary breakpoints from our design, we will now add new breakpoint, at line 38, see
Illustration 4.27. This breakpoint will stop program execution at the moment of sine_ampl array initialization. We can use the
Vivado Logic Analyzer to overview this initialization process directly in hardware, by monitoring the DLMB memory interface
that connects the MicroBlaze processor with the on-chip memory sub-system where sine_ampl array will be stored.

Figure 4.27: Added Breakpoint at line 10

Step 3. In the Properties for C/C++ Line Breakpoint dialog box, leave all parameters unchanged and click OK, see
Illustration 4.28

149
DEBUGGING SYSTEM USING VIVADO LOGIC ANALYZER

Figure 4.28: Properties for C/C++ Line Breakpoint dialog box

Step 4. In the SDK, click Resume button in order to execute program until a breakpoint at line 38 is reached
Step 5. Turn back to the Vivado IDE, to the Hardware Manager and in the ILA Settings window set ILA Trigger mode to
BASIC_ONLY, see Illustration 4.31
Step 6. The next step will be to decide what ILA debug probes are required to participate in the trigger condition. Open
Debug Probes window by clicking Window -> Debug Probes option from the main Vivado IDE menu (see Illustration
4.29) to see all the probes corresponding to the ILA core (see Illustration 4.30)

Figure 4.29: Debug Probes option

Step 7. Go to the Debug Probes window, select the desired ILA debug probes (microblaze_0_dlmb_1_ABUS[0:31] and
microblaze_0_dlmb_1_WRITESTROBE), right-click on them and select Add Probes to Basic Trigger Setup option, see
Illustration 4.30

150
4.2 Illustration of using Vivado Logic Analyzer together with SDK Debugger

Figure 4.30: Add Probes to Basic Trigger Setup option

The another way to add debug probes to the Basic Trigger Setup window is to drag and drop the probes from the Debug
Probes window to the Basic Trigger Setup window.
Important: Only probes that are in the Basic Trigger Setup window participate in the trigger condition. Any probes that
are not in the window are set to "don’t care" values and are not used as part of the trigger condition.
Note: If you want to remove probes from the Basic Trigger Setup window, select the probe, right-click on it and choose
Remove option.

Figure 4.31: Added probes to the ILA Basic Trigger Setup window

Step 8. Now, when the ILA debug probes microblaze_0_dlmb_1_ABUS[0:31] and microblaze_0_dlmb_1_WRITEST-

151
DEBUGGING SYSTEM USING VIVADO LOGIC ANALYZER

ROBE are in the Basic Trigger Setup window, see Illustration 4.31, we can create trigger condition and debug probe
compare values.
Step 9. In the Trigger Setup window, leave default Operator and Radix cell values for the microblaze_0_dlmb_1_ABU-
S[0:31] ILA debug probe and select Value cell to open the Value dialog box, see Illustration 4.32

Figure 4.32: Value dialog box for the microblaze_0_dlmb_1_ABUS[0:31] ILA debug probe

Step 10. In the Value dialog box, set the Value to be 0000_A690 as it is shown on the Illustration 4.32, because this
is the address of the first element in the sine_ampl array. Please refer to step 10 in the Sub-chapter 3.9 "Application
Debugging" for the explanation how to find out the starting address of the sine_ampl array. Click OK.
Step 11. Repeat the same procedure for microblaze_0_dlmb_1_WRITESTROBE probe. Leave default Operator and
Radix cell values and set the Value cell to be 1 (logical one), see Illustration 4.33. This probe is needed in order to trigger
ILA core in the moment when data is being written into the memory location with address 0xA690.

Figure 4.33: Value dialog box for the microblaze_0_dlmb_1 WRITESTROBE ILA debug probe

152
4.2 Illustration of using Vivado Logic Analyzer together with SDK Debugger

Step 12. After we set all the ILA core parameters, we can arm the ILA core. Click the Run Trigger button
Step 13. After we arm the ILA core, in the ILA Properties window you can see the ILA Trigger Capture Status, see
Illustration 4.34. You can see that the ILA Core status is "Waiting for Trigger", because the triggering condition hasn’t
been met yet. Capture status shows that 4000 samples have already been stored in the ILA core buffer and the remaining
4192 samples will be stored when the trigger condition is met. This is because we have specified Trigger position in
window to be 4000 samples.

Figure 4.34: ILA Properties window after arming trigger

Step 14. Go back to the SDK and click Step Over command to execute the line 38
Step 15. Ones the ILA core captured data has been uploaded to the Vivado IDE, it is displayed in the Waveform window.
Open the waveform window and remove all the signals that are present.
Step 16. Go to the Debug Probes window and add all the probes connected to the DLMB interface (there should be 8
probes). Select all the probes connected to the DLMB interface, right-click on them and select Add Probes to Waveform
option, see Illustration 4.35.

153
DEBUGGING SYSTEM USING VIVADO LOGIC ANALYZER

Figure 4.35: Add Probes to Waveform option

Step 17. In the Waveform window you should see all the probes connected to the DLMB interface, see Illustration 4.36.
Press Zoom In button several times to zoom-in into the area around the trigger marker, shown as the vertical red line, in
order to see waveforms of different signals and buses that are part of the DLMB interface.

Figure 4.36: Waveform window

From the Illustration 4.36 we can see that in the moment when microblaze_0_dlmb_1_WRITESTROBE probe change its
value from 0 to 1, data 000007ff (microblaze_0_dlmb_1_WRITEDBUS[0:31]) is being written into the memory location
address 0000a690 (microblaze_0_dlmb_1_ABUS[0:31]).
microblaze_0_dlmb_1_READY probe also changed its value from 0 to 1 which signalize that memory has finished one
write transaction and it is ready for the next transaction.
If you want to check is correct data value (000007ff) written into the memory location address 0xA690, open the SDK tool
and in the Memory tab you will find the correct data value, see Illustration 4.37.

154
4.2 Illustration of using Vivado Logic Analyzer together with SDK Debugger

Figure 4.37: Memory tab with data value on the memory address 0xA690

Step 18. To see the next data value that will be written in the next element of sine_ampl array, at memory location address
0xA694, change the Value cell for the microblaze_0_dlmb_1_ABUS[0:31] ILA debug probe to value 0xA694, arm the ILA
core again and in the SDK tool press the Step Over command twice and check is the data value from the Memory tab
equal to the data value shown in the Waveform window.

155
DEBUGGING SYSTEM USING VIVADO LOGIC ANALYZER

156
Chapter 5

USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

In this chapter you will learn how to integrate a custom IP within the MicroBlaze or ARM-based embedded system using
Xilinx Vivado and SDK tools. First we will show how to create a hardware platform that includes a custom IP, in our case it
will be PWM Modulator IP core (modulator_axi_ip_v1.0) with AXI-Lite interface. Next, we will show how to develop a basic
driver for PWM Modulator IP core, how to integrate in the Xilinx SDK tool chain, and how to develop an application that will
use this driver to communicate with PWM Modulator IP core.
PWM Modulator IP core (modulator_axi_ip_v1.0) was developed and packaged in the sub-chapter 12.4 of the Vivado "-
Basic FPGA Tutorial". Users not familiar with the details of PWM Modulator IP core and the process of packaging it to the
Vivado compliant IP core, please refer to the sub-chapter 12.4 for more details.
Block diagram of the hardware platform that we will create is shown on the Illustration 5.1.

Figure 5.1: Structure of microprocessor-based embedded system, using a custom IP to generate pwm signal

Comparing Illustration 5.1 with Illustration 1.3 it can be seen that the structure of new hardware platform is simpler than
that of Illustration 1.3. In new hardware platform only two IP cores are needed: standard Xilinx one-channel GPIO IP core
and an instance of PWM Modulator (modulator_axi_ip_v1.0) custom IP core. Since all the functionality needed to generate
USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

pwm signal is packaged inside PWM Modulator custom IP core, there is no need for timer and interrupt controller IP cores
that are present on Illustration 1.3.
PWM Modulator (modulator_axi_ip_v1.0) custom IP core is designed to be fully self-contained pwm generator module. It
can generate pwm output signal, modulated by the sine signal, with two different, user-defined frequencies. It uses an
AXI-Lite interface to connect to the AXI4 internal system bus of any AXI enabled microprocessor. Operation of the PWM
Modulator custom IP core is controlled through the set of three internal 32-bit configuration registers, which are accessed
through the AXI-Lite interface:

• the first register (sw0 REGISTER in the block diagram) is used to select the frequency of generated pwm signal.
PWM Modulator custom IP can generate output pwm signal with two different frequencies, whose values can be
specified by the user. Setting the LSB bit inside the sw0 register to 0 or 1, user can select which one of these
frequencies will be used when generating pwm signal

• the second register (div_factor_freqhigh REGISTER in the block diagram) is used to specify the appropriate fre-
quency division factor (div_factor_freqhigh) that will generate the high frequency pwm signal

• the third register (div_factor_freqlow REGISTER in the block diagram) is used to specify the appropriate frequency
division factor (div_factor_freqlow) that will generate the low frequency pwm signal

Table 5.1 shows the internal 32-bit configuration registers address map.
Table 5.1: Internal Registers Address Map of the Modulator IP Core
Internal Register Name S_AXI_AWADDR Value
"sw0" register "0000" (0)
"div_factor_freqhigh" register "0100" (4)
"div_factor_freqlow" register "1000" (8)
This information is important for the software developer in order to correctly access, configure and control PWM Modulator
IP core.
Next, we will show how to generate two hardware platforms, one based on MicroBlaze processor and the other based on
ARM processor, using Vivado IP integrator tool.

5.1 Using custom IP in MicroBlaze-based processor system

In order to create a MicroBlaze-based embedded system that uses PWM Modulator custom IP core, following steps need
to be performed.
Step 1. Create a new modulator_axi_mb project using Vivado IDE wizard. To create a new project, please repeat steps
1-11 from sub-chapter 2.1 Create a New Project.
Step 2. In the Vivado Flow Navigator, under the Project Manager, click on the Project Settings command and in the
Project Settings General window check is the Target language set to VHDL. If it is not, please change it to be VHDL and
click OK.
When we have created a new project, we have to add packaged IP to the IP Catalog. The following steps will show you
how to add packaged IP to the IP Catalog:
Step 3. Create a new folder, ip_repository, in the same directory where modulator_axi_mb project is created. This new
folder will be place where we will copy the packaged modulator_axi_ip IP core.
Step 4. Copy the packaged modulator_axi_ip IP core to the ip_repository folder and extract it inside the same folder
Step 5. Then, in the Flow Navigator, under the Project Manager, click on the Project Settings command and choose IP
from the left pane
Step 6. In the IP window, select Repository Manager tab, see Illustration 5.2
Repository Manager lets you add or remove user repositories and establish precedence between repositories.

158
5.1 Using custom IP in MicroBlaze-based processor system

Figure 5.2: Repository Manager window

Step 7. In the Repository Manager window, click + icon to add the desired repository, see Illustration 5.2
Step 8. In the IP Repositories window, choose ip_repository folder with packaged modulator_axi_ip IP core and click
Select
Step 9. In the Add Repository dialog box, click OK to add the selected repository (ip_repository with 1 IP) to the project,
see Illustration 5.3

Figure 5.3: Add Repository dialog box

Step 10. In the Repository Manager window, when ip_repository is added to the IP Repositories section, click OK, see
Illustration 5.4

159
USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

Figure 5.4: Repository Manager with selected ip_repository

Step 11. In the Flow Navigator, under the Project Manager, click IP Catalog command to verify the presence of the
previously added IP in the IP Catalog. In the Search field type the name of your IP core (modulator_axi_ip) and you
should find it under AXI Peripheral IPs, see Illustration 5.5.

Figure 5.5: IP Catalog with added modulator_axi_ip_v1.0 IP core

Now, when we have all the necessary IPs for our design, we will create block design for our project.
Step 12. Create a new modulator_axi_mb block design and add the following IPs:

• MicroBlaze (microblaze_0)

• modulator_axi_ip_v1.0 (modulator_axi_ip_0)

• AXI GPIO (axi_gpio_0)

160
5.1 Using custom IP in MicroBlaze-based processor system

How to create a new block design and how to instantiate IPs into your design, please refer to sub-chapter 2.3 Create
MicroBlaze-based hardware platform, steps 1-7.
At this point, the IP Integrator canvas should look like as it is shown on the Illustration 5.6.

Figure 5.6: IP Integrator design canvas with instantiated IPs

You can double-click on the modulator_axi_ip_v1.0 (modulator_axi_ip_0) IP core to see its re-customization dialog box.
As you can see from the Illustration 5.7, in the modulator_axi_ip_v1.0 (1.0) re-customization dialog box, you can set the
number of samples in one period of the modulating signal (Depth G) and the number of bits used to represent amplitude
values of the modulating signal (Width G). In our case we don’t have to change settings parameters, because they are set
properly, we can just click OK to proceed.

Figure 5.7: Re-customize IP - modulator_axi_ip_v1.0 (1.0) dialog box

Step 13. Run Block Automation command from the IP Integrator window and in the Run Block Automation dialog box,
change only the Local Memory size from 8 KB to 128 KB and click OK

161
USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

If you are not sure how to run block automation process and how to set block automation parameters, please repeat steps
7-10 from sub-chapter 2.3 Create MicroBlaze-based hardware platform.
Ones you specify the necessary options, the Block Automation feature automatically creates a basic system design, as it
is presented on the Illustration 5.8.

Figure 5.8: A basic MicroBlaze-based system design created by the Block Automation feature

Step 14. Before we use Run Connection Automation feature, we will re-customize AXI GPIO (axi_gpio_0) and Clocking
Wizard (clk_wiz_1) blocks
Step 15. Double-click on the AXI GPIO (axi_gpio_0) block and make the following changes:
In the Board tab:

• associate GPIO IP Interface with sws 8bits Board Interface

• leave GPIO2 IP Interface to be associated with Custom Board Interface

162
5.1 Using custom IP in MicroBlaze-based processor system

Figure 5.9: Re-customize IP - AXI GPIO (2.0) dialog box - Board tab

In the IP Configuration tab:

• disable Enable Dual Channel option, because in this example we will need only one AXI GPIO channel, for switches

163
USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

Figure 5.10: Re-customize IP - AXI GPIO (2.0) dialog box - IP Configuration tab

Step 16. When you finish with the AXI GPIO IP re-customization, click OK
Step 17. Double-click on the Clocking Wizard (clk_wiz_1) block and make the following changes:
In the Clocking Options tab:

• change the Source of the Primary Input Clock to be Single ended clock capable pin instead of Differential clock
capable pin, see Illustration 5.11

164
5.1 Using custom IP in MicroBlaze-based processor system

Figure 5.11: Re-customize IP - Clocking Wizard (5.1) dialog box - Clocking Options tab

In the Output Clocks tab:

• disable reset port, see Illustration 5.12

• and click OK

165
USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

Figure 5.12: Re-customize IP - Clocking Wizard (5.1) dialog box - Output Clocks tab

Step 18. Click the Run Connection Automation link and in the Run Connection Automation dialog box select the All
Automation (0 out of 5 selected) check box
Step 19. When you click OK in the Run Connection Automation dialog box, the connection will be made and highlighted
in the IP Integrator design canvas, see Illustration 5.13

Figure 5.13: A basic MicroBlaze-based system design after running Connection Automation feature

166
5.1 Using custom IP in MicroBlaze-based processor system

Step 20. Right-click in the IP design canvas to add the Constant (xlconstant_0) core into our design
Step 21. Double-click on the Constant block and set the constant value, Const Val, to be 0, and click OK , see Illustration
5.14

Figure 5.14: Re-customize IP - Constant (1.1) dialog box

Step 22. In the block design canvas connect the aux_reset_in port of the Processor System Reset (rst_clk_wiz_1_100-
M) to the dout[0:0] port of the Constant (xlconst_0) block, because by default Extenal Reset and Auxillary Reset of the
Processor System Reset block are set to 1, see Illustration 5.15

Figure 5.15: Re-Customize IP - Processor System Reset (5.0) dialog box

167
USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

Step 23. Delete reset_rtl port and connect ext_reset_in port of the Processor System Reset to the dout[0:0] port of the
Constant block, following the same concept from the previous step
Step 24. The last step in modulator_axi_mb design creation will be to create addition output port for pwm_out signal.
Select pwm_out signal, right-click on it and choose Create Port... option. The tool will automatically assign pwm_out port
name, so in the Create Port dialog box just click OK .
Step 25. Before validation, click Regenerate Layout button. The optimized layout of the design should look similar to layout
that is shown on the Illustration 5.16.

Figure 5.16: Final Block Diagram of the modulator_axi_mb block design

Step 26. Save the modulator_axi_mb block design


Step 27. Validate your design by clicking the Validate Design button
Note: After design validation process, open Processor System Reset block to check did External Reset and Auxillary
Reset values still have default values, as it is presented in the step 22. This is important step because Vivado IDE
sometimes, after design validation process, automatically change the value of these two resets, see Illustration 5.17. If
the tool automatically changed the value of the External and Auxillary reset, please double-click on the Constant block
to re-customize Const Val value from 0 to 1 and click OK. This step is necessary step, because otherwise MicroBlaze
processor will be all the time in reset.

168
5.1 Using custom IP in MicroBlaze-based processor system

Figure 5.17: Re-customize IP - Processor System Design (5.0) dialog box - values of the External and Auxillary reset after
design validation process

Step 28. After this correction, save modulator_axi_mb block design and validate design ones more
Step 29. If your design is successfully validated, create HDL wrapper. In the Sources window, select modualtor_axi_mb
block design, right-click on it and choose Create HDL Wrapper... option
Step 30. In the Vivado Flow Navigator, click Run Synthesis command and wait for task to be completed
Step 31. In the Synthesis Completed dialog box, select Open Synthesized Design and click OK
Step 32. The last step in our hardware design creation is to create and add constraints in the design
Step 33. Change the layout from Default to I/O Planning view, in the layout pull-down menu in the main toolbar
Step 34. By expanding the ports of our design in the I/O Ports tab, you can see that Vivado IDE automatically assigned
appropriate pin location for each of them, except for pwm_out port. This is because we added this port afterwords and in
this case the tool didn’t know what pin location to assign to pwm_out port.
In the I/O Ports tab, select pwm_out port and connect it to the T22 pin location and choose LVCMOS33 as I/O standard
Step 35. When you are finished, click File -> Save Constraints As...
Step 36. In the Save Constraints dialog box, type the name of the constraints file in the File name field and click OK. In
our case, the name can be modulator_axi_mb_wrapper.xdc.
Step 37. In the Save Constraints As dialog box, type the name of the constraint set in the New Constraints set name
field. In our case, the name can be modulator_axi.
Step 38. Click OK and your modulator_rtl constraint set with modulator_axi_mb_wrapper.xdc file should appear in the
Sources window under the Constraints, see Illustration 5.18.

169
USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

Figure 5.18: Sources window with created modulator_axi_mb_wrapper.xdc constraints file

Step 39. Now when we have all the necessary constraints in our design, turn back to the block design and start synthesis
process ones more
Step 40. When the synthesis process is completed, click Run Implementation command and wait for task to be completed
Step 41. At the end, when the implementation process is completed, click Generate Bitstream command to create
bitstream file
To complete the design, we must now create a software component for our embedded system. This application specific
software will be executed on the MicroBlaze processor that is already part of our hardware platform.
Step 1. As we already explained, the first step in software creation is to export the hardware design into the SDK tool. To
export your hardware platform into the SDK tool and to launch SDK, please repeat steps 1 - 4 from the chapter 3
Step 2. Create Board Support Package. To create BSP, please repeat steps from the sub-chapter 3.1
Step 3. To create modulator_axi application project, repeat steps from the sub-chapter 3.2
Now is the time to write the software for this project.
Step 4. In the modulator_axi application project, create modulator_axi.c and modulator_axi.h source files on the same
way as it is explained in the sub-chapter 3.3
The complete modulator_axi.c and modulator_axi.h source files you can find in the text below.
modulator_axi.c :

#include "xparameters.h"
#include "xgpio.h"
#include "xstatus.h"
#include "modulator_axi.h"

int main(void )

/*********************** Variable Definitions ***********************/

XGpio GpioSwitches; // XGPIO instance that will be used to work with SWITCHEs

int *modulator_axi4_lite_interface; // pointer to modulator_axi_ip memory-mapped internal registers

int sw0 = 0; // switch used for selecting frequency


int current_sw0 = 0; // current value of the sw0 bit in the modulator_axi_ip IP core

/*********************** Initialization ***********************/

// SWITCHes initialization
XGpio_Initialize(&GpioSwitches, XPAR_AXI_GPIO_0_DEVICE_ID);
XGpio_SetDataDirection(&GpioSwitches, SWITCH_CHANNEL, 0xff);

// modulator_axi_ip IP core initialization


modulator_axi4_lite_interface = SW0_REGISTER_ADDRESS;
modulator_axi4_lite_interface = sw0;

modulator_axi4_lite_interface = DIV_FACTOR_FREQHIGH_REGISTER_ADDRESS;
modulator_axi4_lite_interface = DIV_FACTOR_FREQHIGH;

modulator_axi4_lite_interface = DIV_FACTOR_FREQLOW_REGISTER_ADDRESS;
modulator_axi4_lite_interface = DIV_FACTOR_FREQLOW;

/*********************** Main Loop ***********************/

while (1)

170
5.2 Using custom IP in ARM-based processor system

{
// read the switch position
sw0 = XGpio_DiscreteRead (&GpioSwitches, SWITCH_CHANNEL);
sw0 = sw0 & SWITCH_POS; // masking (we want to check the status of SW0 only)

if (sw0 != current_sw0)
{
current_sw0 = sw0;

modulator_axi4_lite_interface = SW0_REGISTER_ADDRESS;
modulator_axi4_lite_interface = sw0;
}
}

return 0;
}

modulator_axi.h:

#ifndef MODULATOR_H_
#define MODULATOR_H_

#include "math.h"
#include "xparameters.h"

/*********************** Constant Definitions ***********************/

#define SWITCH_CHANNEL 1 // GPIO channel (1 or 2) to operate on

// address map for modulator_axi_ip internal registers


// address of sw0 register
#define SW0_REGISTER_ADDRESS XPAR_MODULATOR_AXI_IP_0_S00_AXI_BASEADDR
// address of div_factor_freqhigh register
#define DIV_FACTOR_FREQHIGH_REGISTER_ADDRESS XPAR_MODULATOR_AXI_IP_0_S00_AXI_BASEADDR + 4
// address of div_factor_freqlow register
#define DIV_FACTOR_FREQLOW_REGISTER_ADDRESS XPAR_MODULATOR_AXI_IP_0_S00_AXI_BASEADDR + 8

#define SYS_CLK_MHZ 100 // 100 MHz system clock


#define CLOCK_RATE 1000000 * SYS_CLK_MHZ // system clock value, expressed in Hz
// CLOCK_RATE = 100000000 Hz (= 100 MHz)

#define F_LOW 1.0 // F_LOW = 1 Hz


#define F_HIGH 3.5 // F_HIGH = 3.5 Hz

#define DEPTH 8 // the number of samples in one period of the signal (2^8=256)
#define WIDTH 12 // the number of bits used to represent amplitude value (2^12=4096)

#define C1 (1 << DEPTH) // 2^DEPTH


#define C2 (1 << (WIDTH-1)) // 2^(WIDTH-1)
#define C3 (1 << WIDTH) // 2^WIDTH

// variable that will be used for calculating, div_factor_freqlow and div_factor_freqhigh


// C = SYS_CLK_MHZ / (2^DEPTH * 2^WIDTH) = 95.3674
#define C roundf ((float)CLOCK_RATE / (float)(C1 * C3))

// input clock division factor, when sw0 = 0 (F_LOW = 1 Hz)


// DIV_FACTOR_FREQLOW = (C / F_LOW) * 2^WIDTH = 389120
#define DIV_FACTOR_FREQLOW (roundf((float)C / (float)F_LOW)) * C3;

// input clock division factor, when sw0 = 1 (F_HIGH = 3.5 Hz)


// DIV_FACTOR_FREQHIGH = (C / F_HIGH) * 2^WIDTH = 110592
#define DIV_FACTOR_FREQHIGH (roundf((float)C / (float)F_HIGH)) * C3;

#define SWITCH_POS 0x01 // mask to select switch position

#endif /* MODULATOR_H_ */

Step 5. To download your new MicroBlaze-based bitstream file to the target board, please repeat steps from the sub-
chapter 3.7.1 Downloading MicroBlazebased bitstream file

5.2 Using custom IP in ARM-based processor system

In order to create a ARM-based embedded system that uses PWM Modulator custom IP core, following steps need to be
performed.
Step 1. Create new modulator_axi_arm project Vivado IDE wizard
Step 2. In the Project Settings General window, change the Target language to be VHDL
Step 3. In the Project Settings IP window, add the repository with packaged modulator_axi_ip_v1.0 IP core into our
project

171
USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

Step 4. Create new modulator_axi_arm block design


Step 5. In the modulator_axi_arm design canvas add the following IPs to our design:

• ZYNQ7 Processing System (processing_system7_0)

• modulator_axi_ip_v1.0 (modulator_axi_ip_0)

• AXI GPIO (axi_gpio_0)

The IP Integrator canvas should look like as it is shown on the Illustration 5.19.

Figure 5.19: IP Integrator design canvas with instantiated IPs

Step 6. Run Block Automation command from the IP Integrator window and in the Run Block Automation dialog box
just click OK
After running block automation on the Zynq-7000 AP SoC processor, the IP integrator diagram should look as follows:

172
5.2 Using custom IP in ARM-based processor system

Figure 5.20: Zynq-7000 AP SoC Processing System after Running Block Automation

Step 7. Before we use Run Connection Automation feature, we will re- customize AXI GPIO (axi_gpio_0)
Step 8. Double-click on the AXI GPIO (axi_gpio_0) block and make the following changes:
In the Board tab:

• associate GPIO IP Interface with sws 8bits Board Interface

• leave GPIO2 IP Interface to be associated with Custom Board Interface, see Illustration 5.9

In the IP Configuration tab:

• disable Enable Dual Channel option, because in this example we will need only one AXI GPIO channel, for switches,
see Illustration 5.10

Step 9. When you finish with the AXI GPIO IP re-customization, click OK
Step 10. Click the Run Connection Automation link and in the Run Connection Automation dialog box select the All
Automation (out of 3 selected) check box and click OK
After running connection automation process, your IP integrator design canvas should look like as it is shown on the
Illustration 5.21

173
USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

Figure 5.21: Zynq-7000 AP SoC Processing System after Running Connection Automation

Step 11. The last step in modulator_axi_arm design creation will be to create addition output port for pwm_out signal.
Select pwm_out signal, right-click on it and choose Create Port... option. The tool will automatically assign pwm_out port
name, so in the Create Port dialog box click OK .
When we have connected all the necessary ports/interfaces and after we have customized all the necessary blocks in our
design, it is time to validate it.
Before validation, click Regenerate Layout button. The optimized layout of the design should look similar to layout that is
shown on the Illustration 5.22

Figure 5.22: Final Block Diagram of the modulator_axi_arm block design

Step 12. Save the modulator_axi_arm block design


Step 13. Validate design

174
5.3 Developing a device driver for the custom IP core

Step 14. If your design is successfully validated, create HDL wrapper for the modulator_axi_arm block design
Step 15. Run Synthesis process
Step 16. Open Synthesized Design
Step 17. Create constraints file on the same way as it is done in the MicroBlaze-based system design, steps 33, 34 from
the previous sub-chapter
Step 18. When you are finished, save constraints file as modulator_axi_arm_wrapper.xdc and the constraints set as
modulator_axi
Step 19. Now when we have all the necessary constraints in our design, turn back to the block design and start synthesis
process ones more
Step 20. When the synthesis process is completed, click Run Implementation command and wait for task to be completed
Step 21. At the end, when the implementation process is completed, click Generate Bitstream command to create
bitstream file
To complete the design, we must now create a software component for our embedded system. This application specific
software will be executed on the ARM processor that is already part of our hardware platform.
Step 1. Export hardware platform to SDK and launch SDK tool
Step 2. Create Board Support Package
Step 3. Create modulator_axi application project
Step 4. In the modulator_axi application project, create modulator_axi.c and modulator_axi.h source files on the same
way as it is explained in the previous sub-chapter
Step 5. To download your new ARM-based bitstream file to the target board, please repeat steps from the sub-chapter
3.7.2 Downloading ARM-based bitstream file

5.3 Developing a device driver for the custom IP core

In all previous software applications communication with the modulator_axi_ip IP core was done by directly accessing its
internal registers. This requires intimate knowledge of modulator_axi_ip IP core’s internal organization, as well as exact
address values for every internal register. This approach significantly complicates software application development by a
typical software developer, who is not aware and even doesn’t want to be aware of all these details about the hardware
device he is using. A better approach includes an additional software layer that provides a software interface to the
hardware device, enabling user application to access hardware functions without needing to know precise details of the
hardware being used. This additional software layer is known as a device driver .
A device driver typically communicates with the hardware device through the system bus or some other communication
subsystem to which the hardware device is connected. When a user application program invokes a routine in the driver, the
driver issues low level commands to the hardware device. Once the hardware device sends data back to the device driver,
the driver may invoke routines in the original calling program. Device drivers are hardware dependent and operating system
specific. Device drivers simplify programming by acting as translator between a hardware device and the applications or
operating system that use it. Programmers can write the higher level application code independently of what ever specific
hardware the end-user is using.
In this sub-chapter it will be shown how to develop a device driver for the modulator_axi_ip IP core and how to integrate it
and use it within the Xilinx SDK tool.

5.3.1 Device driver for PWM Modulator IP core

The great way to start writing your own driver is to use the supplied Xilinx IO driver functions. The xil_io driver provides
some useful IO functions which can read and write data values to registers in the hardware IP core, and these are perfect
when the user wishes to design a driver for custom IP. xil_io is easy to use and can be included in any project using a
simple #include statement in the C code. The most commonly used functions calls are Xil_Out32() and Xil_In32(), but
similar functions exist for 16 bit and 8 bit data transfers.
Writing your drivers with a hierarchical structure is considered to be good design practice, and will save you a lot of time
when you need to debug the design. Once you have verified that you have some measurable results by writing to one

175
USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

register, you can then move on to testing some of the others. It is advisable to create a function that can be re-used in your
code, because this will form the basis of your driver hierarchy.
For example, following two functions can be used to write and read a value from the selected internal register of the IP core.
modulator_axi_ip_Set_Register function writes a user specified value (supplied through the value input argument) to the
selected custom IP internal register (selected by specifying base address value, via baseaddr input argument and offset
value, via offset input argument) using Xil_Out32 function. Similarly, modulator_axi_ip_Get_Register function reads the
current value from the selected custom IP internal register and returns it to the caller, using Xil_In32 function.

void modulator_axi_ip_Set_Register (int baseaddr, int offset, int value)


{
Xil_Out32 (baseaddr + offset, value);
}

int modulator_axi_ip_Get_Register (int baseaddr, int offset)


{
int temp = 0;
temp = Xil_In32 (baseaddr + offset);
return (temp);
}

In these functions, we have implemented the use of offsets from the base address, rather then using hard-coded addresses
each time. Another good coding practice would be to use #define statements to specify names for commonly used hex
values, enabling the user to quickly identify which register is being addressed without having to constantly cross-reference
everything to the address map of the IP.
Presented two functions enable us to communicate with the custom IP core’s internal registers to exercise the various
features of the custom IP core. In the case of the modulator_axi_ip IP core, following additional driver functions can be
defined:

• modulator_axi_ip_Set_PWM_Frequency function - used to set the desired frequency of the PWM signal that will
be generated

• modulator_axi_ip_Get_PWM_Frequency function - used to read the current frequency settings used in the PWM
signal generation

• modulator_axi_ip_Select_PWM_Frequency function - used to select one of two possible frequencies for the PWM
signal generation

• modulator_axi_ip_Get_Selected_PWM_Frequency function - used to read the current selection of the PWM signal
frequency

Source code of the modulator_axi_ip_Set_PWM_Frequency driver function is shown below:

void modulator_axi_ip_Set_PWM_Frequency (Modulator_AXI *InstancePtr, int freq_sel, float freq_val)


{
int c1 = (1 << InstancePtr->Depth); // 2^DEPTH
int c3 = (1 << InstancePtr->Width); // 2^WIDTH

// C = SYS_CLK_MHZ / (2^DEPTH * 2^WIDTH) = 95.3674


float c = (int)((float)InstancePtr->ClockRate / (float)(c1 * c3) + 0.5);

float div_factor = (int)((c / freq_val) + 0.5) * (float)c3;

modulator_axi_ip_Set_Register(InstancePtr->BaseAddress, 4+4*freq_sel, (int) div_factor);

When called, this function sets the selected PWM frequency value to the user specified value in the selected instance of the
modulator_axi_ip IP core. Instance of the modulator_axi_ip IP core for which one of the PWM frequency values should be
changes is specified through the Modulator_AXI ∗InstancePtr input argument. Which one of the two PWM frequency values
should be changed is specified through the int freq_sel input argument. Finally, new PWM frequency value that should be
used is specified through float freq_val input argument. Function calculates the necessary value of the clock division factor
(variable div_factor) based on the desired PWM frequency value and specified characteristics of the selected instance
of the modulator_axi_ip IP core (InstancePtr->Depth , InstancePtr->Width and InstancePtr->ClockRate). Calculated
division factor is then written to the appropriate internal register of the selected instance of the modulator_axi_ip IP core,
using modulator_axi_ip_Set_Register function.
Source code of the modulator_axi_ip_Get_PWM_Frequency driver function is shown below:

176
5.3 Developing a device driver for the custom IP core

float modulator_axi_ip_Get_PWM_Frequency (Modulator_AXI * InstancePtr, int freq_sel)


{
int c1 = (1 << InstancePtr->Depth); // 2^DEPTH
int c3 = (1 << InstancePtr->Width); // 2^WIDTH

// C = SYS_CLK_MHZ / (2^DEPTH * 2^WIDTH) = 95.3674


float c = (int)((float)InstancePtr->ClockRate / (float)(c1 * c3) + 0.5);

float div_factor = (float) modulator_axi_ip_Get_Register(InstancePtr->BaseAddress, 4+4*freq_sel);

float temp = (c / div_factor) * (float) c3;

return temp;
}

When called, this function returns the current value of selected PWM frequency from the selected instance of the modulator-
_axi_ip IP core. Instance of the modulator_axi_ip IP core from which one of the PWM frequency values will be read is
specified through the Modulator_AXI ∗InstancePtr input argument. Which one of the two PWM frequency values should
be read is specified through the int freq_sel input argument. Function first reads the current value of the clock division
factor from the selected instance of the modulator_axi_ip IP core, using modulator_axi_ip_Get_Register function. This
clock division factor value is then converted to frequency value and returned to the calling function.
Source code of the modulator_axi_ip_Select_PWM_Frequency driver function is shown below:

void modulator_axi_ip_Select_PWM_Frequency (Modulator_AXI * InstancePtr, int freq_sel)


{
modulator_axi_ip_Set_Register(InstancePtr->BaseAddress, 0, freq_sel);
}

When called, this function sets the value of the PWM frequency selector in the selected instance of the modulator_axi_ip IP
core to the user specified value. Instance of the modulator_axi_ip IP core for which the PWM frequency selector value will
be set is specified through the Modulator_AXI InstancePtr input argument. Which one of the two PWM frequency values
should be used is specified through the int freq_sel input argument. Function simply writes the freq_sel input argument’s
value to the appropriate internal register of the selected instance of the modulator_axi_ip IP core by calling modulator_axi-
_ip_Set_Register function.
Source code of the modulator_axi_ip_Get_Selected_PWM_Frequency driver function is shown below:

int modulator_axi_ip_Get_Selected_PWM_Frequency (Modulator_AXI * InstancePtr)


{
return modulator_axi_ip_Get_Register(InstancePtr->BaseAddress, 0);
}

When called, this function simply returns the current PWM frequency selector value from the selected instance of the
modulator_axi_ip IP core. Instance of the modulator_axi_ip IP core from which one of the PWM frequency values will be
read is specified through the Modulator_AXI InstancePtr input argument. Current PWM frequency selector value is read
from the selected instance of the modulator_axi_ip IP core, using modulator_axi_ip_Get_Register function.
To facilitate the possibility of having more then one instance of the modulator_axi_ip IP core in the system, driver functions
presented above were written using a parametrized approach, via Modulator_AXI ∗InstancePtr input argument. By modi-
fying this input argument value, calling function can communicate with different instances of the modulator_axi_ip IP core
that may be present in the system. However, this approach requires that the user initialize every instance of the modulator-
_axi_ip IP core that is present in the system, and keep track of this via a separate Modulator_AXI object. For this purpose
following driver function can be used.

int modulator_axi_ip_Initialize(Modulator_AXI *InstancePtr, u16 DeviceId)


{
modulator_axi_ip_Config *ConfigPtr = NULL;
int Index;

// Assert arguments
Xil_AssertNonvoid(InstancePtr != NULL);

/* Lookup configuration data in the device configuration table.


* Use this configuration info down below when initializing this
* driver. */
for (Index = 0; Index < XPAR_MODULATOR_AXI_IP_NUM_INSTANCES; Index++)
{
if (modulator_axi_ip_ConfigTable[Index].DeviceId == DeviceId)
{
ConfigPtr = &modulator_axi_ip_ConfigTable[Index];
break;
}
}

177
USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

if (ConfigPtr == (modulator_axi_ip_Config *) NULL)


{
InstancePtr->IsReady = 0;
return (XST_DEVICE_NOT_FOUND);
}

// Initialize the driver


InstancePtr->BaseAddress = ConfigPtr->BaseAddress;
InstancePtr->Width = ConfigPtr->Width;
InstancePtr->Depth = ConfigPtr->Depth;

// Indicate the instance is now ready to use, initialized without error


InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
return (XST_SUCCESS);
}

modulator_axi_ip_Initialize function initializes the selected Modulator_AXI object (specified by the Modulator_AX-
I InstancePtr input argument) with the configuration information related to the modulator_axi_ip IP core whose device
ID value is specified via the u16 DeviceId input argument. First, the function searches for the configuration information
of the selected modulator_axi_ip IP core by searching through a modulator_axi_ip_ConfigTable array. modulator_axi_ip-
_ConfigTable array holds the configuration information of every modulator_axi_ip IP core instance that is present in the
system. Please notice that this configuration information array is created automatically by the Xilinx SDK tool, during the
building of the Board Support Package, using the instructions in the specific user-defined TCL file, that will be discussed
later in this chapter. If a device ID match is found, configuration of the selected modulator_axi_ip IP core is copied to
the ConfigPtr object. Using this configuration information, supplied Modulator_AXI is initialized and returned to the calling
function, along with the status information about the completed initialization process.
Finally, to complete our device driver for the modulator_axi_ip IP core, one more driver function is needed, modulator_axi-
_ip_Set_Input_Clock_Frequency. This driver function is called for each instance of the modulator_axi_ip IP core during
the initialization process, to correctly set the system clock frequency value that is used in every instance of the modulator-
_axi_ip IP core. This information is needed to correctly calculate clock frequency division factors in the modulator_axi_-
ip_Set_PWM_Frequency and modulator_axi_ip_Get_PWM_Frequency driver functions.

void modulator_axi_ip_Set_Input_Clock_Frequency (Modulator_AXI *InstancePtr, int clock_rate)


{
InstancePtr->ClockRate = clock_rate;
}

When called, modulator_axi_ip_Set_Input_Clock_Frequency driver function simply updates the ClockRate field of the spec-
ified Modulator_AXI object with the user-specified value (specified via the int clock_rate input argument).
The driver functions shown above should illustrate that drivers can quickly be built to exercise the various features of the
custom IP. It should also be very obvious that we are starting to generate significant amounts of code, and we already have
many functions in our source file which are beginning to be untidy. To continue the development of the driver functions,
we can now move to code into a separate modulator_axi_ip.c source file, and the custom prototypes into a modulator-
_axi_ip.h header file. To maintain visibility of the functions across the different files, be sure to add the line #include
"modulator_axi_ip.h" at the top of the main application source file, and also to the modulator_axi_ip.c source file. With this
file structure in place, it is possible to quickly and easily continue the development of the custom IP drivers, while keeping
the source code in the main test application tidy and manageable. For very complex driver functions, additional source files
can be added to the file set to facilitate hierarchy in the source code.
Below, the complete device driver source code for modulator_axi_ip IP core, organized into two separate files, modulator-
_axi_ip.c and modulator_axi_ip.h, is presented.
modulator_axi_ip.c:

#include "xil_io.h"
#include "xstatus.h"
#include "xparameters.h"
#include "modulator_axi_ip.h"

extern modulator_axi_ip_Config modulator_axi_ip_ConfigTable[];

/**************************************************************************/
int modulator_axi_ip_Initialize(Modulator_AXI *InstancePtr, u16 DeviceId)
{
modulator_axi_ip_Config *ConfigPtr = NULL;
int Index;

// Assert arguments
Xil_AssertNonvoid(InstancePtr != **NULL** );

/* Lookup configuration data in the device configuration table.

178
5.3 Developing a device driver for the custom IP core

* Use this configuration info down below when initializing this


* driver. */
for (Index = 0; Index < XPAR_MODULATOR_AXI_IP_NUM_INSTANCES; Index++)
{
if (modulator_axi_ip_ConfigTable[Index].DeviceId == DeviceId)
{
ConfigPtr = &modulator_axi_ip_ConfigTable[Index];
break;
}
}

if (ConfigPtr == (modulator_axi_ip_Config *) NULL)


{
InstancePtr->IsReady = 0;
return (XST_DEVICE_NOT_FOUND);
}

// Initialize the driver


InstancePtr->BaseAddress = ConfigPtr->BaseAddress;
InstancePtr->Width = ConfigPtr->Width;
InstancePtr->Depth = ConfigPtr->Depth;

// Indicate the instance is now ready to use, initialized without error


InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
return (XST_SUCCESS);
}

void modulator_axi_ip_Set_Input_Clock_Frequency (Modulator_AXI *InstancePtr, int clock_rate)


{
InstancePtr->ClockRate = clock_rate;
}

void modulator_axi_ip_Set_Register(int baseaddr, int offset, int value)


{
Xil_Out32(baseaddr + offset, value);
}

int modulator_axi_ip_Get_Register(int baseaddr, int offset)


{
int temp = 0;
temp = Xil_In32(baseaddr + offset);
return (temp);
}

void modulator_axi_ip_Set_PWM_Frequency (Modulator_AXI * InstancePtr, int freq_sel, float freq_val)


{
int c1 = (1 << InstancePtr->Depth); // 2^DEPTH
int c3 = (1 << InstancePtr->Width); // 2^WIDTH

// C = SYS_CLK_MHZ / (2^DEPTH * 2^WIDTH) = 95.3674


float c = (int)((float)InstancePtr->ClockRate / (float)(c1 * c3) + 0.5);

float div_factor = (int)((c / freq_val) + 0.5) * (float)c3;

modulator_axi_ip_Set_Register(InstancePtr->BaseAddress, 4+4*freq_sel, (int) div_factor);


}

float modulator_axi_ip_Get_PWM_Frequency (Modulator_AXI * InstancePtr, int freq_sel)


{
int c1 = (1 << InstancePtr->Depth); // 2^DEPTH
int c3 = (1 << InstancePtr->Width); // 2^WIDTH

// C = SYS_CLK_MHZ / (2^DEPTH * 2^WIDTH) = 95.3674


float c = (int)((float)InstancePtr->ClockRate / (float)(c1 * c3) + 0.5);

float div_factor = (float) modulator_axi_ip_Get_Register(InstancePtr->BaseAddress, 4+4*freq_sel);

float temp = (c / div_factor) * (float) c3;

return temp;
}

void modulator_axi_ip_Select_PWM_Frequency (Modulator_AXI * InstancePtr, int freq_sel)


{
modulator_axi_ip_Set_Register(InstancePtr->BaseAddress, 0, freq_sel);
}

int modulator_axi_ip_Get_Selected_PWM_Frequency (Modulator_AXI * InstancePtr)


{
return modulator_axi_ip_Get_Register(InstancePtr->BaseAddress, 0);
}

modulator_axi_ip.h :

#ifndef MODULATOR_AXI_IP_H /* prevent circular inclusions */


#define MODULATOR_AXI_IP_H /* by using protection macros */

#include "xil_types.h"

179
USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

/*********************** Type Definitions ***********************/

typedef struct {
u16 DeviceId; /* Unique ID of device */
u32 BaseAddress; /* Device base address */
u32 Width; /* Number of bits used to represent every sample of the sine function */
u32 Depth; /* Number of samples of the sine function = 2**Depth */
} modulator_axi_ip_Config;

typedef struct {
u32 BaseAddress; /* Device base address */
u32 IsReady; /* Device is initialized and ready */
u32 ClockRate; /* Input clock frequency, specified in Hz */
u32 Width; /* Number of bits used to represent every sample of the sine function */
u32 Depth; /* Number of samples of the sine function = 2**Depth */
} Modulator_AXI;

int modulator_axi_ip_Initialize(Modulator_AXI *InstancePtr, u16 DeviceId);


void modulator_axi_ip_Set_Input_Clock_Frequency (Modulator_AXI *InstancePtr, int clock_rate);
void modulator_axi_ip_Set_Register(int baseaddr, int offset, int value);
int modulator_axi_ip_Get_Register(int baseaddr, int offset);
void modulator_axi_ip_Set_PWM_Frequency (Modulator_AXI * InstancePtr, nt freq_sel, float freq_val);
float modulator_axi_ip_Get_PWM_Frequency (Modulator_AXI * InstancePtr, int freq_sel);
void modulator_axi_ip_Select_PWM_Frequency (Modulator_AXI * InstancePtr, int freq_sel);
int modulator_axi_ip_Get_Selected_PWM_Frequency (Modulator_AXI * InstancePtr);

#endif /* end of protection macro */

5.3.2 Creating Xilinx driver file and folder structure

When the driver source files have been written, the next step is to put the driver files into a directory tree structure that the
Xilinx SDK will understand. This is a very important step because the Xilinx tools will expect to find files and folders with
reserved names. An example of the required folder structure is show on the Illustration 5.23.

Figure 5.23: Required folder structure

Step 1. Create the folder structure in the same way as it is shown on the Illustration 5.22, wherever you like on the disk
The top level folder can be called anything that the user chooses, and be placed anywhere that they choose, but must
contain a sub-folder called "drivers". Below that, there may be one or more folders which represent each driver for the
custom IP. We have just one driver in our example which is called "modulator_axi_v1_00_a". The suffix "v1_00_a" is
important as it denotes the revision of the driver. The accepted notation is a major/minor numeric revision in decimal digits
(1.00 in this example) followed by a sub-version denoted by a single alpha character (in this case "a"). The accepted use of
these version numbers is that major and minor changes to the functionality of the driver should be represented numerically,
and bug fixes / performance improvements that do not affect the intended functionality should be represented by an alpha
character change.
Under the structure described above, three further folders are expected:

• The "src" folder contains the .c and .h source files for the driver, in addition to a Makefile which can be written to
describe the build process required to compile the sources in the correct order / dependency.

• The "example" folder contains examples of software application code, showing how your driver must be used in a
final application.

• The "data" folder contains following control files which are specific to the operation of the Xilinx SDK tools, and
which detail how various device driver source files should be used:

180
5.3 Developing a device driver for the custom IP core

– the MDD file - Microprocessor Driver Definition file


– the TCL file - used by the Xilinx BSP creation tools to automatically generate some parameters which can be
used later by the software developer

Step 2. Save the source files, modulator_axi_ip.c and modulator_axi_ip.h, created in the previous sub-chapter in the
"src" folder, because the "src" folder should contain the .c and .h source files for the driver
The MDD file
The first control file in the "data" folder is the Microprocessor Driver Definition (MDD) file. The file name is required to have
a suffix of "_v2_1_0.mdd", and in the case of our example has the full name of "modulator_axi_v2_1_0.mdd". The suffix
relates to the version of the syntax that is used within the MDD file, in this case v2.10. The content of the MDD file is
relatively simple, and for most MDD files the text is identical with the exception of one or two lines. In our case, the content
of the "modulator_axi_v2_1_0.mdd" MDD file is the following:
modulator_axi_v2_1_0.mdd:

OPTION psf_version = 2.1;

BEGIN DRIVER modulator_axi

OPTION supported_peripherals = (modulator_axi_ip);


OPTION driver_state = ACTIVE;
OPTION copyfiles = all;
OPTION VERSION = 1.0;
OPTION NAME = modulator_axi;

END DRIVER

The "OPTION supported_peripherals" line should be updated to list the peripherals that are served by the custom driver.
In the example shown here, there is just one peripheral which will be supported by the driver, but multiple peripherals can
be listed, separated by a space. The names of supported peripherals must match the names of the IPs that have been
created using the IP Packager. In our case driver supports only one IP, modulator_axi_ip .
The "BEGIN driver" line should be edited to create a name for the driver that is being developed. There are no specific
rules for the naming convention of this parameter, but it is wise to create a name that will be clearly identifiable to the end
user. In our case, a name for the driver will be modulator_axi. Identical name should be used in the "OPTION NAME" line
of the MDD file.
The "OPTION driver_state" line allows the user to maintain a recognised lifespan of their custom driver, and list the driver
as either ACTIVE, DEPRECATED, or OBSOLETE as and when the developer choose to supersede or retire it from the
service. The effect of changing this option away from the "active" state, generates either warnings or errors in the driver
compile process when it is invoked by the end user. Driver that we are developing is in the active state, so this option is set
to ACTIVE.
The "OPTION copyfiles" line tells the Xilinx SDK which source files in the custom driver’s "src" directory should be copied
when the SDK generates the BSP. In most cases this will be left set to "all".
The "VERSION" line allows the user to specify a version number for their driver. This should match the directory name
used previously.
Step 3. Create modulator_axi_v2_1_0.mdd control file, following instructions from the text above, and save it into the
"data" folder
The TCL file
The second control file in the "data" folder is the TCL file, which is used by the Xilinx BSP creation tools to automatically
generate some parameters which can be used later by the software developers. The content of the TCL file is shown in the
text below:
modulator_axi_v2_1_0.tcl:

proc generate {drv_handle} {

::his::utils::define_include_file $drv_handle "xparameters.h" "modulator_axi_ip" "NUM_INSTANCES" "DEVICE_ID


" "C_S00_AXI_BASEADDR" "C_S00_AXI_HIGHADDR" "width_g" "depth_g" "FCLK_CLK0"

::hsi::utils::define_config_file $drv_handle "modulator_axi_driver_g.c" "modulator_axi_ip" "DEVICE_ID" "


C_S00_AXI_BASEADDR" "width_g" "depth_g"

::hsi::utils::define_canonical_xpars $drv_handle "xparameters.h" "modulator_axi_ip" "NUM_INSTANCES" "


DEVICE_ID" "C_S00_AXI_BASEADDR" "C_S00_AXI_HIGHADDR" "width_g" "depth_g"

181
USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

Step 4. Create modulator_axi_v2_1_0.tcl control file, following instructions from the text above, and save it into the "data"
folder
The TCL file shown above, that is part of a device driver we are developing, is actually only 4 lines in length, including the
final curly bracket on its own line, but lines 2, 3 & 4 are extremely long and therefore difficult to re-produce clearly in this
document.
The lines 2 & 4 should be edited by the developer (beginning with "define_include_file" and "define_canonical_xpars"), to
list a number of parameters that will be generated automatically by the BSP generation tools before being placed into a file
called "xparameters.h" within the automatically generated board support package.
The editable section of these lines begins with the parameter after "xparameters.h". In this example, the first two ed-
itable parameters are "modulator_axi_ip" and "NUM_INSTANCES". These two parameters are used to create a #define
statement in the "xparameters.h" file called "XPAR_MODULATOR_AXI_IP_NUM_INSTANCES" and assign a numerical
value to it. The Xilinx BSP generation tools have the ability to automatically count the number of instances of each type
of peripheral that are added into the user’s embedded processor design. This information can be of great use when the
same driver is used to control multiple instances of the same peripheral, and provides the ability for a loop to be created
in software that will automatically update when the number of instances of a given peripheral is increased and decreased
in the design. In our example there is only one instance of the modulator_axi_ip peripheral, so we need not worry about
such a feature, but the line "#define XPAR_MODULATOR_AXI_IP_NUM_INSTANCES 1" will be automatically added to
the "xparameters.h" file when the BSP is generated.
The list of parameters in the TCL file on the rest of lines 2 and 4 represent additional #define statements that will be
generated in the "xparameters.h" file. Each of the #define statements will have a prefix related to the instance name of the
IP, and a suffix copied from the text string in quotes on lines 2 and 4 of the TCL file. Each of the text strings matches the
name of a Generic in the top level VHDL entity for the custom IP, and the value of each #define is populated using either
the Generic’s default value in the VHDL code, or the value that the user has set in the Vivado block diagram tool to override
that default. In our case, seven #define statements will be created in the "xparameters.h" file during BSP generation, and
they will be populated according to the user’s chosen settings and the number of instances of the custom IP that were
added to the user’s design. In our case, a section of "xparameters.h" file related to the modulator_axi_ip peripheral, that
will be automatically generated, is shown below:

/* Definitions for driver MODULATOR_AXI */


#define XPAR_MODULATOR_AXI_IP_NUM_INSTANCES 1

/* Definitions for peripheral MODULATOR_AXI_IP_0 */


#define XPAR_MODULATOR_AXI_IP_0_DEVICE_ID 0
#define XPAR_MODULATOR_AXI_IP_0_S00_AXI_BASEADDR 0x43C00000
#define XPAR_MODULATOR_AXI_IP_0_S00_AXI_HIGHADDR 0x43C0FFFF
#define XPAR_MODULATOR_AXI_IP_0_WIDTH_G 12
#define XPAR_MODULATOR_AXI_IP_0_DEPTH_G 8
#define XPAR_MODULATOR_AXI_IP_0_FCLK_CLK0 0

Tcl command in line 3 is used to automatically generate modulator_axi_ip configuration table array object, that was dis-
cussed in the modulator_axi_ip_Initialize function. This time, the editable section of this line begins with the parameter
after $drv_handle. First parameter specifies the filename of the C source code file ("modulator_axi_driver_g.c", in our case)
that will be automatically generated by the Xilinx SDK tool during the BSP generation process. This C file will hold a defi-
nition of the configuration table related to the IP, whose name is specified by the second parameter ("modulator_axi_ip").
Remaining parameters in the line define the fields that each configuration table array member should contain (in our case
each member should consist from four fields, "DEVICE_ID" "C_S00_AXI_BASEADDR" "width_g" "depth_g"). As before,
each of the text strings must match the name of a Generic in the top level VHDL entity for the custom IP, and the values of
these fields will be populated automatically using either the Generic’s default value in the VHDL code, or the value that the
user has set in the Vivado block diagram tool to override that default.
The ability to generate a software BSP which will automatically update itself based upon changes made to the hardware
design is an incredibly powerful feature, and can save the software engineering team a lot of manual development effort
and time. The end user’s software application can then make use of these parameters, allowing the hardware and software
teams to work completely independently, yet vastly reduce the possibility for software errors and bugs to occur due to any
changes made in the hardware design that were not manually communicated to the software engineering team.
Configuring the Xilinx SDK
With the driver control files written and placed in the correct folder structure, the final step is to configure the Xilinx SDK
tools so that they have visibility of the new driver in the list of available driver repositories.
Step 5. In the SDK, open the Preferences dialog by choosing Window -> Preferences from the menu bar
Step 6. In the Preferences dialog box, select the Xilinx SDK -> Repositories pane, as shown on the Illustration 5.24

182
5.3 Developing a device driver for the custom IP core

Figure 5.24: Preferences dialog box

Step 7. In the Preferences dialog box, click the New... button next to the Local Repositories list to add a new repository
Step 8. In the Browse For Folder dialog box, point to the folder that you created for your custom drivers and click OK, see
Illustration 5.25

Figure 5.25: Browse For Folder dialog box

The folder you choose here should be the level of the file system above the “drivers” folder. In our case it is “pwm_-
modulator_driver” folder, see Illustration 5.23
Step 9. Click the Rescan Repositories button, followed by OK button
This setting will provide visibility of your custom driver to the SDK tool, and will enable your driver to be selected in the BSP

183
USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

settings. There is an addition list shown on this screen called Global Repositories, see Illustration 5.23. This performs
an identical function to that of adding the drivers to the "Local Repositories" list, with the exception that the drivers will be
visible to any SDK workspace that is created or opened in the future. This is a powerful feature if your goal is to create a
single repository of custom drivers for multiple custom IPs, and still have them routinely available and visible to all of your
SDK workspaces.
Selecting a Custom Driver in the BSP
The task of creating and configuring your custom IP and custom driver is now complete, and the Vivado and SDK tools will
now have visibility of them. The final stage is to select your custom driver and allocate it to be automatically compiled as
part of the BSP for your user application.
Step 10. Right-click on the Board Support Package in the Project Explorer that you created just before (standalone_bsp_0),
and choose the Board Support Package Settings menu item from the bottom of the list
Step 11. In the Board Support Package Settings dialog box, choose the drivers pane from the choices shown on the
left of the window
Step 12. In the Drivers configuration table, identify the instance of your custom IP (modulator_axi). It will now be possible
to select your custom driver from the drop down menu in the Driver column of the table, see Illustration 5.26.

Figure 5.26: Board Support Package Settings dialog box with selected modulator_axi_ip custom driver

Note: If you had created multiple folders representing different versions of the same driver, you will also be able to choose

184
5.3 Developing a device driver for the custom IP core

the version of the driver in the Driver Version column.


Step 13. Click OK and the BSP will automatically be re-generated
If you now examine the "include" and "libsrc" folders in the standalone_bsp_0 BSP’s source tree, you will find that your
header file (modulator_axi_ip.h) and C source file (modulator_axi_ip.c) have been automatically copied and compiled into
the BSP. Furthermore, one additional C source file is automatically generated, modulator_axi_driver_g.c. Remember
that this is the source file we have specified in the modulator_axi_v2_1_0.tcl TCL file. This source file contains the
configuration table array object, holding all relevant configuration information for every modulator_axi_ip peripheral that
is present in the embedded system. In our case, we have only one instance of the modulator_axi_ip peripheral, so the
generated configuration table array object has only one entry, as can be seen by inspecting the generated modulator_axi-
_driver_g.c source file. There is no longer any requirement for custom driver functions to be added to the list of sources for
each application, because they are now included in the BSP alongside the drivers provided by Xilinx for supplied IP.
Creating new application project that will use developed device driver
The last step in our design process will be to create a new application project that will use developed modulator_axi device
driver. One possible solution is shown below:
modulator_axi_using_driver.c :

#include "xparameters.h"
#include "xgpio.h"
#include "xstatus.h"
#include "modulator_axi_using_driver.h"
#include "modulator_axi_ip.h"

int main(void)
{
/*********************** Variable Definitions ***********************/

XGpio GpioSwitches; // XGPIO instance that will be used to work with SWITCHEs
Modulator_AXI pwm_modulator; // Modulator AXI instance

int sw0 = 0; // switch used for selecting frequency


int current_sw0 = 0; // current value of the sw0 bit in the modulator_axi_ip IP core
float temp;

/************************* Initialization **************************/

// SWITCHes initialization
XGpio_Initialize(&GpioSwitches, XPAR_AXI_GPIO_0_DEVICE_ID);
XGpio_SetDataDirection(&GpioSwitches, SWITCH_CHANNEL, 0xff);

// modulator_axi_ip IP core initialization


modulator_axi_ip_Initialize(&pwm_modulator, XPAR_MODULATOR_AXI_IP_0_DEVICE_ID);

// Set the input clock frequency for the Modulator_AXI instance to appropriate value
modulator_axi_ip_Set_Input_Clock_Frequency (&pwm_modulator, CLOCK_RATE);

// Set the values for two possible frequencies for the PWM signal
modulator_axi_ip_Set_PWM_Frequency (&pwm_modulator, 0, F_HIGH);
temp = modulator_axi_ip_Get_PWM_Frequency (&pwm_modulator, 0);

modulator_axi_ip_Set_PWM_Frequency (&pwm_modulator, 1, F_LOW);


temp = modulator_axi_ip_Get_PWM_Frequency (&pwm_modulator, 1);

// Select the initial frequency that will be used to generate PWM signal
modulator_axi_ip_Select_PWM_Frequency (&pwm_modulator, sw0);
current_sw0 = modulator_axi_ip_Get_Selected_PWM_Frequency (&pwm_modulator);

/************************* Main Loop **************************/

while(1)
{
// read the switch position
sw0 = XGpio_DiscreteRead (&GpioSwitches, SWITCH_CHANNEL);
sw0 = sw0 & SWITCH_POS; // masking (we want to check the status of SW0 only)

if (sw0 != current_sw0)
{
// Write the new switch position to the pwm_modulator core
modulator_axi_ip_Select_PWM_Frequency (&pwm_modulator, sw0);
current_sw0 = modulator_axi_ip_Get_Selected_PWM_Frequency (&pwm_modulator);
}
}
return 0;
}

modulator_axi_using_driver.h:

#ifndef MODULATOR_H_

185
USING CUSTOM IPS IN EMBEDDED SYSTEM DESIGN

#define MODULATOR_H_

#include "math.h"
#include "xparameters.h"

/*********************** Constant Definitions ***********************/

#define SWITCH_CHANNEL 1 // GPIO channel (1 or 2) to operate on

#define SYS_CLK_MHZ 90 // 90 MHz system clock


#define CLOCK_RATE 1000000 * SYS_CLK_MHZ // system clock value, expressed in Hz
// CLOCK_RATE = 100000000 Hz (= 100 MHz)

#define F_LOW 1.0 // F_LOW = 1 Hz


#define F_HIGH 3.5 // F_HIGH = 3.5 Hz

#define SWITCH_POS 0x01 // mask to select switch position

#endif /* MODULATOR_H_ */

Note: For example, these two files can be stored in the "examples" folder from the device driver folder structure.
Step 14. In the SDK, select File -> New -> Application Project option from the main SDK menu
Step 13. In the Application Project dialog box

• type a name of the new application project in the Project name field. In our case the name will be modulator_axi_-
using_driver

• in the Target Software section use Use existing standalone_bsp_0 Board Support Package instead of creating new

• leave all other parameters unchanged

• click Next and choose Empty Application as a default template

• click Finish and the new modulator_axi_using_driver application project should appear in the Project Explorer
window

Step 16. Expand modulator_axi_using_driver application project in the Project Explorer window and in the src folder
add previously created modulator_axi_using_driver.c and modulator_axi_using_driver.h files
Step 17. Run your application on the Zynq development board, as explained in the Sub-chapter 3.7 Running Applica-
tion.

186
Chapter 6

CONCLUSION

In this tutorial a range of different implementations of the PWM Modulator system has been presented:

1. A software solution that uses an external timer, PWM_SW_no_intc

2. A software solution that uses an external timer and interrupt controller, PWM_SW_intc

3. A hardware/software co-design solution, that uses a custom PWM Modulator IP core, PWM_HW/SW

Furthermore, in the "Basic FPGA Tutorial" a pure hardware solution for the PWM Modulator system has also been pre-
sented, PWM_HW .
All these solutions represent valid and functionally correct implementations of the PWM Modulator system, but they differ
in a way how this functionality is actually implemented and therefore have different performance results and resource
requirements.
The performance of the PWM Modulator system can be represented by three attributes:

1. Maximum frequency of generated PWM signal

2. Maximum accuracy of duty cycle value of generated PWM signal

3. Minimum jitter value of generated PWM signal

Maximum frequency of generated PWM signal is probably the most important performance attribute of any PWM Modulator
system. Typical PWM signal frequency values range from 0.05 Hz, in case of an electric stove, 120 Hz, in case of a lamp
dimmer, from few kHz to tens of kHz for a motor drive, and well into the tens or hundreds of kHz in audio amplifiers and
computer power supplies.
Four proposed PWM Modulator systems have different maximum PWM frequency values.
PWM_HW and PWM_HW/SW solutions can generate PWM signals with the highest frequency values, because the PWM
signal generation is done completely in hardware. In the proposed hardware solution, maximum PWM frequency is limited
by two factors:

• maximum operating frequency of the Frequency Trigger module (see Chapter 2 "Frequency Trigger" from the "Basic
FPGA Tutorial"), Fmax, and

• the number of bits used to represent amplitude value of the PWM modulating signal ("Width G" field from the
modulator_axi_ip customization window, shown on the Illustration 5.6, or width field in the design_setting_g generic
from the modulator top-level entity, see Chapter 8 "Modulator" from the "Basic FPGA Tutorial"), width.

The maximum PWM frequency that can be generated by the PWM_HW and PWM_HW/SW solutions can be calculated by
the following formula:

Fmax
PW MFmax =
2width
CONCLUSION

For example, maximum PWM signal frequency that can be generated with the configuration of the PWM Modulator IP core
that was used in this tutorial is equal to:

100MHz
PW MFmax = = 24414Hz
212

This value is sufficient for the majority of PWM applications, described earlier.
Estimating the maximum PWM signal frequencies for the software solutions (PWM_SW_no_intc and PWM_SW_intc ) is
not so straightforward. In these implementations maximum PWM frequency is also constrained by the amount of time
required to complete one iteration of the while loop located in the main procedures from the modulator_no_intc_mb.-
c, modulator_intc_mb.c, modulator_no_intc_axi.c and modulator_intc_axi.c source codes. The estimations of exact
maximum PWM signal frequency values would require a measurement of this while loop time. Furthermore, this time would
also depend on the type of the processor that executes the PWM Modulator software and the C/C++ compiler settings that
were used to build the executable version.
Next, we will comment on the pros and cons of each of the proposed implementation of the PWM Modulator system.
PWM_SW_no_intc implementation
Pros:

• Easiest implementation with the shortest design time

• Easy to modify the used PWM generator algorithm, for instance easy to change the waveform of the modulating
signal, all we need to do is to recompile the modified PWM Modulator source code

Cons:

• Maximum frequency of generated PWM signal is the lowest from all proposed implementations, because the duration
of one iteration of the while loop from the modulator_no_intc_mb.c and modulator_no_intc_axi.c source codes
is the longest. This duration becomes even longer if the processor needs to carry out some additional tasks other
then PWM generation, because all these tasks would have to be included in this while loop.

• Jitter of generated PWM signal is significant, and potentially can be very high, but it can be relatively accurately
estimated (since we know the amount and ordering of additional commands that are located in the while loop) and
compensated.

PWM_SW_intc implementation
Pros:

• More difficult to implement than the PWM_SW_no_intc implementation, because it requires the configuration of the
interrupt controller and writing the interrupt routine

• In case a processor has to perform some other actions apart from the PWM signal generation, this is the only
acceptable solution (from "pure" software solutions)

• Easy to modify the used PWM generator algorithm, for instance easy to change the waveform of the modulating
signal, all we need to do is to recompile the modified PWM Modulator source code

Cons:

• Maximum frequency of generated PWM signal should be a little bit higher then the maximum PWM signal frequency
generated by the PWM_SW_no_intc implementation, because the duration of one iteration of the while loop in this
case is shorter

• Jitter of generated PWM signal is significant, and potentially can be very high, but this time it can not be predicted in
advance, because we can not estimate the actual point in time when an PWM timer interrupt will be generated. This
is even more unpredictable if there are other interrupt sources with equal or higher interrupt priority present in the
system.

PWM_HW/SW implementation
Pros:

188
• Offers the generation of PWM signal with the highest possible frequency, identical to the PWM_HW implementation,
because in this implementation, as well as PWM_HW implementation, PWM signal generation is done completely in
hardware

• Jitter of generated PWM signal is minimal

• Great flexibility (for example, easy adjusting of frequencies of generated PWM signals, easy integration into more
complex systems like multiple PWM generators, etc.), especially when compared with the PWM_HW implementation

Cons:

• If PWM Modulator IP core is not readily available, then the development time is significant, however if this IP is
available then the development time is comparable with the "pure" software solutions

PWM_HW implementation
Pros:

• Offers the generation of PWM signal with the highest possible frequency, identical to the PWM_HW implementation,
because in this implementation, as well as PWM_HW implementation, PWM signal generation is done completely in
hardware

• Jitter of generated PWM signal is minimal

• Minimum amount of required hardware resources, minimum power consumption

Cons:

• Limited flexibility, because every modification of the design requires a modification of hardware, leading to the neces-
sity to reimplement the complete system (redo the hardware synthesis, place and route the design), which in large
systems can take several hours

• Long development time, because we need to design and verify a complete hardware system, which is much more
time consuming that software design

189
CONCLUSION

190
Chapter 7

EXERCISES

This section holds a set of exercises that can be used to verify the knowledge acquired by reading this tutorial.
Exercise 1
Modify the init_sin.c source code in order to be able to generate PWM signal modulated by the following signals:

1. Triangle wave signal

2. Sawtooth wave signal

Exercise 2
Make the necessary modifications to the MicroBlaze-based embedded system and modulator_no_intc.c application to
enable the user to specify which type of modulating signal (sine, triangle or sawtooth) should be used to generate PWM
signal on-the-fly, during the operation of the system.
Exercise 3
Using the MicroBlaze-based embedded system and modulator_no_intc.c application as a starting point, make the neces-
sary modifications to both the hardware and software parts of the MicroBlaze-based embedded system in order to be able
to generate two completely independent PWM generators.
Exercise 4
Follow the instructions from exercise 3, but this time using the ARM- based embedded system.
Exercise 5
Using the MicroBlaze-based embedded system and modulator_intc.c application as a starting point, make the necessary
modifications to both the hardware and software parts of the MicroBlaze-based embedded system in order to be able to
generate two completely independent PWM generators.
Exercise 6
Follow the instructions from exercise 5, but this time using the ARM- based embedded system.
Exercise 7
Using the MicroBlaze-based embedded system based on the PWM modulator custom IP core and modulator_axi.c applica-
tion as a starting point, make the necessary modifications to both the hardware and software parts of the MicroBlaze-based
embedded system in order to be able to generate two completely independent PWM generators.
Exercise 8
Follow the instructions from exercise 7, but this time using the ARM- based embedded system.
Exercise 9
Using the MicroBlaze-based embedded system based on the PWM modulator custom IP core, modulator_axi driver and
modulator_axi_using_driver.c application as a starting point, make the necessary modifications to both the hardware and
software parts of the MicroBlaze-based embedded system in order to be able to generate two completely independent
PWM generators.
Exercise 8
EXERCISES

Follow the instructions from exercise 9, but this time using the ARM- based embedded system.

192

Вам также может понравиться