Overview
This tutorial shows you how to use the System Console debugging tool to program a compiled FPGA design into an FPGA device, then access the hardware modules (i.e. peripherals) that are instantiated in that FPGA design. This System Console tutorial is based on the FPGA design created in the "Build a Custom Hardware System" tutorial. In that tutorial, you create this Qsys system:
Figure 1: Qsys System Block Diagram
In this tutorial, you will interact with the Qsys system through a JTAG cable connected to the FPGA, sending read and write transactions through the JTAG master component to interact with the slave peripherals connected to it.
Materials
Hardware
- Windows* or Linux* development host PC
- Intel® Cyclone® 10 LP FPGA Evaluation Kit
- USB Type A to Mini-B cable
Software
-
Installed Intel® Quartus® Prime Software Suite. Either the Lite or Standard Edition, but not the Pro Edition. This was accomplished in the "How to Program Your First FPGA" tutorial.
-
A completed Quartus project from "Build a Custom Hardware System" tutorial. If you have not completed that tutorial you should go back and complete it now.
Step 1: Preparing the Board
This section describes how to prepare the Intel® Cyclone® 10 LP FPGA board for use in this tutorial.
-
Plug the mini USB cable into the USB connector of the board. Then plug the other end of the USB cable to your host PC.
-
Make sure DIP switch S1-4 (BYPASS) is in the default "OFF" position as shown in figure 2. This enables a JTAG connection to the board via USB. The other 3 switches are general user inputs and can be in any state.
Figure 2: USB Connector
Step 2: Getting Started with System Console
This section describes how to use System Console to download the FPGA design into the FPGA device, and then interact with the JTAG master IP core inside the Qsys system to read from, and write to the attached slave peripherals. It also demonstrates JTAG link debug and Qsys system health observability provided by System Console.
- Launch the Intel® Quartus® software and open the blink project you created in the "Build a Custom Hardware System" tutorial by selecting File > Open Project, navigating to your project folder, and selecting the
blink.qpf
file. - Launch System Console from the Tools > System Debugging Tools > System Console menu.
Figure 3: System Console Menu
- The System Console tool looks for devices connected to the JTAG cable. To list the available devices, locate the TCL Console pane at the bottom right corner of the System Console window, and type the following command, then press ENTER:
get_service_paths device
Figure 4: System Console’s TCL Console
- You can use tab completion to finish commands as you type them, and even show you what possible commands can complete from the prefix you type. For instance, type get on the command line and then press TAB, to see a pop-up list of all commands that begin with get. You can use the up and down arrows on the keyboard to scroll up and down the list to select one of the commands. Press Enter to select a command from the pop-up menu.
Figure 5: Command Line Before Pressing Tab
Figure 6: Command Line After Pressing Tab
The TCL console history buffer can also be scrolled backward and forward using the up and down arrows on the keyboard, letting you easily recall previously executed command lines.
- All commands provide help if you pass them into the help command. At the tcl prompt type:
help get_service_paths
Figure 7: Getting Help for Commands
Type help help
to see a list of commands.
Step 3: Program the FPGA
-
To program the FPGA device, we first need to get a service path to the device. We will assign that path to a variable called d_path
by typing the following command:
set d_path [lindex [get_service_paths device] 0 ]
-
Download the SOF file to the device by typing the following command:
device_download_sof $d_path output_files/blink.sof
The amber D5 CONFIG LED should be illuminated once the commands above complete
Step 4: Using the JTAG Debug Service
Exercise the JTAG DEBUG service path provided by the JTAG master component.
-
Locate the JTAG DEBUG service path of the JTAG master and store it in a new variable called jd_path
by typing the following command:
set jd_path [lindex [get_service_paths jtag_debug] 0]
-
Exercise the JTAG interface by sending a list of values through a loopback at tdi and tdo of a system-level debug node (SLD). This command checks the physical interface of the board and JTAG TAP controller pins of the FPGA device. Type the following command and observe the values returned:
jtag_debug_loop $jd_path [list 1 2 4 8 15 16]
-
Check the clock connected to the JTAG master component in the Qsys system. This command senses whether the clock is active, and returns a 1 if it is, and a 0 if it is not. Type the following command:
jtag_debug_sense_clock $jd_path
-
Sample the "state" of the clock connected to the JTAG master component. Repeatedly sample the clock until you catch it in both the high (1) and low (0) state. Type the following command and press ENTER to observe the clock state. Repeat the command by pressing the up arrow followed by ENTER:
jtag_debug_sample_clock $jd_path
-
Sample the reset signal connected to the JTAG master component inside the Qsys system. The S3 push button on the board is connected to the Qsys system reset input (active low). To sample the reset signal type the following command:
jtag_debug_sample_reset $jd_path
-
To see the reset signal change, press and hold the S3 push button and resample the reset, then release the S3 push button and resample the reset.
Figure 8: Location of Push Button S3
Step 5: Using the JTAG Master Service
Next, we will exercise the master service path provided by the JTAG master. This will require us to recall the address map produced in the previous "Build a Custom Hardware System" tutorial. Remember that we used the sopc-create- header-files in that tutorial to create the master_0.h header file, and then dumped the base address macros from that file. To refresh your memory, here is what those addresses are:
# define OCRAM_64K_BASE 0x0
# define LED PIO BASE 0x10000
# define BUTTON PIO BASE 0x10010
# define SWITCH_PIO_BASE 0x10020
# define SYSTEM_ID_BASE 0x10030
-
First, set several TCL variables to allow us to more easily recall these base addresses in the rest of this tutorial. Execute each of the following commands to setup these variables:
set ocram 0x0
set led 0x10000
set button 0x10010
set switch 0x10020
set sysid 0x10030
-
To communicate with the peripherals in the device, we are going to use the "master" service. Locate the service path to the JTAG Master component and save it to the tcl variable m_path by typing the following command:
set m_path [lindex [get_service_paths master] 0 ]
Claim the master service path so that we can interact with the JTAG Master component, and save it to the tcl variable c_path by typing the following command:
set c_path [claim_service master $m_path ""]
-
Now that we can send commands to the JTAG Master, let’s interact with the on-chip RAM component by reading a single word from its base address. Type the following command and press ENTER:
master_read_32 $c_path $ocram 1
Now write a word to the base address by typing the following command and pressing ENTER:
master_write_32 $c_path $ocram 0x1234de10
Finally, let’s verify the word was written by reading from the on-chip RAM once more:
master_read_32 $c_path $ocram 1
-
Next we'll use the JTAG Master to interact with the LED PIO component. First, turn on every other LED by typing the following command and pressing ENTER:
master_write_32 $c_path $led 0x55
Now turn those LEDs off and turn the other ones on by typing the following command:
master_write_32 $c_path $led 0xaa
New can also create a loop to toggle LED0 and LED1 every half second for 10 times by typing the following commands (or cutting and pasting into the Tcl Console):
set COUNT 0
while {$COUNT < 10} {
master_write_32 $c_path $led 0x01
after 500
master_write_32 $c_path $led 0x02
after 500
set COUNT [expr $COUNT + 1]
}
Finally, let’s turn off all the user LEDs with the following command:
master_write_32 $c_path $led 0xff
-
Exercise the JTAG DEBUG service reset functionality. Now that we have some LEDs illuminated, let’s look at one more JTAG DEBUG service provided by the JTAG master component. We can assert the reset signal connected to the Qsys system by the JTAG master component. To do that we use the following command:
jtag_debug_reset_system $jd_path
After executing that command, the LEDs should all turn ON, returned to their reset state. You can trigger the same reset effect by pressing the S3 push button like we did before. Turn OFF some LEDs again and try pressing S3 to prove that as well.
-
Interact with the button PIO component by reading the status of the S4 Push Button.
First, read it before pressing the button with the following command:
master_read_32 $c_path $button 1
Next, press and hold S4 and repeat the command.
-
Interact with the switch PIO component. There are 4 slide switches in DIP switch SW1, but only 3 of them are for GPIO use. We will use SW1-1 through SW1-3; avoid changing SW1-4 which causes the device JTAG link to be bypassed.
First, set SW1-1 through SW1-3 to the ON position and read the switches with the following command:
master_read_32 $c_path $switch 1
Now, one by one, set SW1-3 through SW1-1 to the OFF position and read the switches again.
-
Interact with the system ID component. This component contains two 32-bit words. The first word is the ID value that we set to 0xde10de10 in the previous hardware portion of this tutorial, and the second word that represents the Unix second time value when the Qsys system was generated.
master_read_32 $c_path $sysid 2
-
Exercise the default slave peripheral. Here we will demonstrate what happens when we read and write to unmapped addresses.
Our peripherals are mapped into addresses 0x0000_0000 thru 0x0001_0038. By reading an address outside that range, we will read instead from the default slave peripheral (which was initialized at device configuration to 0x0000_0000). Read from an unmapped address with the following command:
master_read_32 $c_path 0x01000000 4
The read operation should return the following data:
0x00000000 0x00000000 0x00000000 0x00000000
Let’s write to that same address 4 words with a specific incrementing pattern.
master_write_32 $c_path 0x01000000 [list 0x0badf00d 0x1badf00d 0x2badf00d 0x3badf00d]
Now verify that pattern repeats every 4 words
master_read_32 $c_path 0x01000000 8
The read operation should return the data shown below:
0x0badf00d 0x1badf00d 0x2badf00d 0x3badf00d 0x0badf00d 0x1badf00d 0x2badf00d 0x3badf00d
Let’s write to a slave that has no write interface, like the system ID peripheral. That write will not be decoded by any slave in the system and will be directed into the default slave.
master_write_32 $c_path $sysid 0xfacecafe
Now verify that this write operation was sent to the default slave.
master_read_32 $c_path 0x01000000 4
The read operation should return the data shown below. Since only a single word was written (0xfacecafe), only the first word of the default slave was changed.
0xfacecafe 0x1badf00d 0x2badf00d 0x3badf00d
-
Close the master service by typing the following command:
close_service master $c_path
That’s it, you’ve used System Console to debug and test the FPGA hardware system.
As you have seen, System Console is useful for low-level, scripted or interactive testing and debugging of IP cores. In this exercise, we communicated with the device over a JTAG link, but you can also use other communication channels, such as TCP/IP making the tool useful for remote debug as well. For more details on using System Console check out the dedicated web page here.