Introduction: G-ICE. Simulation in Hardware

It is a tool based on a ZYBO board which allows testing and debugging of microprocessors designs. Testing is carried on hardware, not software. Therefore, results are much more reliable. It fills the hole in the hardware design flow between the software simulation of the design and the physical implementation. The designer will be able to implement and test her design on the PL part of the board, while also choose the code that will be executed and set breakpoints on it via the application running in the ARM. After that she will run the code and look on a screen the different registers of interest (watches), previously set in the application, and how they evolve. That is possible thanks to a well prepared infrastructure which allows everyone to implement their very own design in a fast and comfortable way, focusing on the operation of the device and debugging capabilities.

How do you use it?

G-ICE interfaces with an app running on the ARM processor. Using this app, it is possible to debug and handle the state of the processor under test.

G-ICE is implemented on a ZYBO board, which allows the design phase to be divided in two major parts:

The first one is the software running on the ARM processor. Its function is to let the user select a microcontroller design she wants to test and the code it will run, in addition to setting break points, watches to registers or running modes (step by step or normal mode, in which the code will be executed at a fixed clock rate). During execution, values of all registers will be available to the user.

The second one is the programmable logic, and in particular the JTAG interface implemented on it. This interface will allow the U.U.T. (in this case, the microcontroller under test) to communicate with the software running on the ARM processor via JTAG. This guarantees a total code reusability, since G-ICE will be compatible with every design which implements JTAG.

Main advantages

Reliability:

The design doesn't need to be tested on software. This way, by taking the ASIC design and porting it to G-ICE, the user will be able to get a realistic emulation and output values.

Speed:

G-ICE greatly reduces the testing time of a device due to the testing infrastructure it provides. The designer only has to include all requested registers to the boundary scan chain, and provide a description of the JTAG chain to the JTAG software interface. G-ICE provides a fully working debugging environment without effort.

Comfortable and portable:

G-ICE is a standalone tool needing only a PC screen and a power supply to work. This makes unnecessary additional equipment, usually located in a laboratory or a special sector of the company or university, so it is possible for the designer to work almost anywhere.

Source files:

Step 1: Step 1: JTAG Peripheral

The first objective is to develop a JTAG peripheral that will allow us to interface the microcontroller under test with the ARM and therefore with the debugging application.

JTAG is specially a great choice for this design, since it is already a well established standard that is extensively used to debug microcontrollers from almost all vendors. On the other hand, for the developer, having a JTAG interface will be extremely useful when trying to visualize low-level signals coming from the microcontroller.

The peripheral will include the following registers:

  1. Register 0: Data In
  2. Register 1: Control
  3. Register 2: Chain Length
  4. Register 3: Offset
  5. Register 4: Status (Read Only)
  6. Register 5: Data Out (Read Only)

Chain Length and Offset should always be written before writing to Control, since a write to Control will start the operation. The Control register includes the following fields:

  • Instruction [31:30]
  • Shifth Length [29:25]
  • WriteToChain [24]
  • ReadFromChain[23]
  • Reserved bits [22:0]

The available instructions are:

  1. Reset -> Code 0x00
  2. DR Scan -> Code 0x01
  3. IR Scan -> Code 0x02

The status register will indicate whether there is data to read from Data Out and if the Ctrl fifo is full.

  • Bit 0: Ctrl FIFO Full
  • Bit 1: Data to read available.

Step 2: Step 2: Linux Implementation

We provide a Linux-based solution, so the first thing to do is to make the ARM work with it. For that we used the ramdisk image provided by Xilinx in their wiki:

http://www.wiki.xilinx.com/Build+and+Modify+a+Rootfs

In order for it to work it is necessary to wrap the image with the U-Boot header.

Although with it we were able to develop all our system, our aim is to develop a Linux system from scratch in order to have a fully optimized system rather a generic one.

The boot process is as follows:

  1. First stage bootloader (FSBL) configures the HP and loads the bitstream on the FPGA and loads the second stage bootloader.
  2. Second stage bootloader loads U-Boot.
  3. U-Boot loads the Linux Kernel image, the device tree and the ramdisk image into memory.
  4. Linux Kernel execution starts.

Step 3: Step 3: Driver Development

In order to be able to use the JTAG peripheral developed previously from Linux, we need a driver which creates an entry in /proc (specifically /proc/jtag). We will use that entry as a normal file in order to write and read the peripheral registers. The driver code is based on the one showed in the "Hands on tutorial", but since our driver have various registers we need a way to select the register are we going to work with. Due to this the driver has a variable which holds the number of the selected register, and when it detects a write event on /proc/jtag it checks the data length, if the length is 1 then it changes the selected register, else it writes that data to the selected register. For example if we were about to write the value "70" on the third register and after that read the second register we could do:

  1. echo 3 > /proc/jtag
  2. echo 70 > /proc/jtag
  3. echo 2 > /proc/jtag
  4. cat /proc/jtag

Once the user has a device which she wants to test, creates a file that defines the registers and their position in the JTAG chain. We are developing a high-level API that allows the user to use that file to write and read registers of the device to be tested, and based on it we will develop the proper debugging application.