Tracing tools monitor what is going in a program’s execution by logging low-level and frequent events. Thus tracing can detect and help debug performance issues in embedded system applications.
In Circuit Cellar’s April issue, Thiadmer Riemersma describes his DIY tracing setup for small embedded systems. His system comprises three parts: a set of macros to include in the source files of a device under test (DUT), a PC workstation viewer that displays retrieved trace data, and a USB dongle that interfaces the DUT with the workstation.
Small embedded devices typically have limited-performance microcontrollers and scarce interfaces, so Riemersma’s tracing system uses only a single I/O pin on the microcontroller.
Designing a serial protocol that keeps data compact is also important. Riemersma, who develops embedded software for the products of his Netherlands-based company, CompuPhase, explains why:
Compactness of the information transferred from the embedded system to the workstation [which decodes and formats the trace information] is important because the I/O interface that is used for the transfer will probably be the bottleneck. Assuming you are transmitting trace messages bit by bit over a single pin using standard wire and 5- or 3.3-V logic levels, the transfer rate may be limited to roughly 100 Kbps.
My proposed trace protocol achieves compactness by sending numbers in binary, rather than as human-readable text. Common phrases can be sent as numeric message IDs. The trace viewer (or trace ‘listener’) can translate these message IDs back to the human-readable strings.
One important part of the system is the hardware interface—the trace dongle. Since many microcontrollers are designed with only those interfaces used for specific application needs, Riemersma says, typically the first step is finding a spare I/0 pin that can be used to implement the trace protocol.
In the following article excerpt, Riemersma describes his trace dongle and implementation requiring a single I/O pin on the microcontroller:
Photo 1 shows the trace dongle. To transmit serial data over a single pin, you need to use an asynchronous protocol. Instead of using a kind of (bit-banged) RS-232, I chose biphase encoding. Biphase encoding has the advantage of being a self-clocking and self-synchronizing protocol. This means that biphase encoding is simple to implement because timing is not critical. The RS-232 protocol requires timing to stay within a 3% error margin; however, biphase encoding is tolerant to timing errors of up to 20% per bit. And, since the protocol resynchronizes on each bit, error accumulation is not an issue.
Figure 1 shows the transitions to transmit an arbitrary binary value in biphase encoding—to be more specific, this variant is biphase mark coding. In the biphase encoding scheme, there is a transition at the start of each bit.
For a 1 bit there is also a transition halfway through the clock period. With a 0 bit, there is no extra transition. The absolute levels in biphase encoding are irrelevant, only the changes in the output line are important. In the previous example, the transmission starts with the idle state at a high logic level but ends in an idle state at a low logic level.
Listing 1 shows an example implementation to transmit a byte in biphase encoding over a single I/O pin. The listing refers to the trace_delay() and toggle_pin() functions (or macros). These system-dependent functions must be implemented on the DUT. The trace_delay() function should create a short delay, but not shorter than 5 µs (and not longer than 50 ms). The toggle_pin() function must toggle the output pin from low to high or from high to low.
For each bit, the function in Listing 1 inverts the pin and invokes trace_delay() twice. However, if the bit is a 1, it inverts the pin again between the two delay periods. Therefore, a bit’s clock cycle takes two basic “delay periods.”
The biphase encoding signal goes from the DUT to a trace dongle. The dongle decodes the signal and forwards it as serial data from a virtual RS-232 port to the workstation (see Photo 2 and the circuit in Figure 2).
The buffer is there to protect the microcontroller’s input pin from spikes and to translate the DUT’s logic levels to 5-V TTL levels. I wanted the trace dongle to work whether the DUT used 3-, 3.3-, or 5-V logic. I used a buffer with a Schmitt trigger to avoid the “output high” level of the DUT at 3-V logic, plus noise picked up by the test cable would fall in the undefined area of 5-V TTL input.
Regarding the inductor, the USB interface provides 5 V and the electronics run at 5 V. There isn’t room for a voltage regulator. Since the USB power comes from a PC, I assumed it might not be a “clean” voltage. I used the LC filter to reduce noise on the power line.
The trace dongle uses a Future Technology Devices International (FTDI) FT232RL USB-to-RS-232 converter and a Microchip Technology PIC16F1824 microcontroller. The main reason I chose the FT232RL converter is FTDI’s excellent drivers for multiple OSes. True, your favorite OS already comes with a driver for virtual serial ports, but it is adequate at best. The FTDI drivers offer lower latency and a flexible API. With these drivers, the timestamps displayed in the trace viewers are as accurate as what can be achieved with the USB protocol, typically within 2 ms.
I chose the PIC microcontroller for its low cost and low pin count. I selected the PIC16F1824 because I had used it in an earlier project and I had several on hand. The microcontroller runs on a 12-MHz clock that is provided by the FTDI chip.
The pins to connect to the DUT are a ground and a data line. The data line is terminated at 120 Ω to match the impedance of the wire between the dongle and the DUT.
The cable between the DUT and the trace dongle may be fairly long; therefore signal reflections in the cable should be considered even for relatively low transmission speeds of roughly 250 kHz. That cable is typically just loose wire. The impedance of loose wire varies, but 120 Ω is a good approximate value.
The data line can handle 3-to-5-V logic voltages. Voltages up to 9 V do not harm the dongle because it is protected with a Zener diode (the 9-V limit is due to the selected Zener diode’s maximum power dissipation). The data line has a 10-kΩ to 5-V pull-up, so you can use it on an open-collector output.
The last item of interest in the circuit is a bicolor LED that is simply an indicator for the trace dongle’s status and activity. The LED illuminates red when the dongle is “idle” (i.e., it has been enumerated by the OS). It illuminates green when biphase encoded data is being received.
After the dongle is built, it must be programmed. First, the FT232RL must be programmed (with FTDI’s “FT Prog” program) to provide a 12-MHz clock on Pin C0. The “Product Description” in the USB string descriptors should be set to “tracedongle” so the trace viewers can find the dongle among other FTDI devices (if any). To avoid the dongle being listed as a serial port, I also set the option to only load the FTDI D2XX drivers.
To upload the firmware in the PIC microcontroller, you need a programmer (e.g., Microchip Technology’s PICkit) and a Tag-Connect cable, which eliminates the need for a six-pin header on the PCB, so it saves board space and cost.
The rest of the article provides details of how to create the dongle firmware, how to add trace statements to the DUT software being monitored, and how to use the GUI version of the trace viewer.
The tracing system is complete, but it can be enhanced, Riemersma says. “Future improvements to the tracing system would include the ability to draw graphs (e.g., for task switches or queue capacity) or a way to get higher precision timestamps of received trace packets,” he says.