Signal timing is a huge component in FPGA design, and while many tutorials demonstrate how to handle timing within the programmable logic I thought it would be helpful to show how to measure external circuitry timing when it's being driven by an FPGA's I/O and has a direct impact on the timing of the logic in RTL.
Recently I posted a project about driving a 5-digit 7-segment display with an FPGA where I had to calculate the proper refresh rate of how long a character needed to be held on each display to appear stable to the human eye. Each common cathode of the 5 7-segment displays where connected to the collector of an NPN 2N3904 BJT that were connected to ground by an I/O pin from the FPGA being set high. Thus, past of this timing of the overall refresh rate included how long it would take for the collector to be connected to the emitter (and ultimately ground) after the FPGA I/O signal was asserted.
Using the oscilloscope function of my Analog Discovery Pro ADP3450 with its control interface software WaveForms, I set up a test circuit with a single 2N3904 and LED on its collector to measure the amount of delay between the FPGA I/O pin being asserted on the base of the BJT to when the LED is illuminated. Which effectively is measuring the slew of the BJT to add as an offset value to the refresh rate in the RTL logic driving the scrolling text across the 5-digit 7-segment display.
Overall, each character on needs to be held on the 7-segment display for 1 millisecond plus the slew of the 2N3904 BJT.ADP3450 Calibration
While the ADP3450 is shipped fully calibrated, my inner test engineer always like to rerun calibration any time the instrument is moved a distance farther than a scoot across the bench and/or anytime I get a new set of cables/probes. Since both of these things were true for me when I went to use my ADP3450 in this instance (I threw mine in my suitcase when I went home for the holidays, and I also ordered a new set of test probes for the oscilloscope channels), I decided to run a calibration.
Open Waveforms, and navigate to the Device Manager under the Settings tab:
Click Calibrate to get the calibration menu to come up:
You'll see a list of calibration actions. Unless you have a specific reason otherwise, I recommend always running them all in the order they are listed from top to bottom. Thus, we'll start with the waveform generator channels, then the oscilloscope channels, followed by the digital power supply, and finally the frequency compensation of the oscilloscope channels.
Step 1 - Calibrate Waveform Generator Channels
The two waveform generator channels are the first thing to be calibrated since they are then used to calibrate the oscilloscope channels.
There is a linearity calibration done that is 100% internal so nothing needs to be connected yet. Click on Waveform Generator Linearity in the list and click Next in the window that pops up to execute the function.
When completed, the pop up window will tell you to click Finish which will return you to the list of calibration functions.
For the next measurement, Waveform Generator 1 Low Range, channel one of the waveform generator will output low voltages such as 1.00V, which needs to be measured using a known-good DMM. I used BNC cables to mini hook test leads for this, connecting the black test lead to the negative probe of my DMM and the red test lead to the positive probe of my test lead.
After getting the DMM hooked up to channel 1 of the waveform generator, go ahead and click on Waveform Generator 1 Low Range in the function list. As you are prompted, enter the voltage value read by the DMM in the box in the lower right-hand cornet of the window and click Next. The button text will change from Next to Finish after all measurements have been completed. Clicking Finish will return you to the calibration functions list.
These steps will be exactly the same for Waveform Generator 1 High Range, you'll just see higher voltage values (5.00V max) being output to the DMM.
Move the BNC cable to channel 2 of the waveform generator and repeat for Waveform Generator 2 Low Range and Waveform Generator 2 High Range.
Step 2 - Calibrate Oscilloscope
To calibrate the oscilloscope, all of its inputs first need to be connected to ground. While I could use the BNC cables to mini hook test leads to make this hookup easier, BNC test probes are what I'll actually be using to take my measurements, so I want those cables to be a part of the calibrated system. This made my breadboard setup a bit messier, but not terrible. The probes all need to be set to 1X gain, including all of the negative end alligator clips. I used the ground signal from the digital head to feed the negative rail of my breadboard:
Once everything is connected to ground for the oscilloscope channels, select Oscilloscope from the calibrations function menu. Click Next in the pop-up window to perform the function.
When prompted, connect all of the probes to the positive output of channel one of the waveform generator (leaving the negative end alligator clips connected to ground) and click next to continue the oscilloscope calibration.
Once completed, click Finish.
Step 3 - Calibrate Digital Power Supply
The digital header of the ADP3450 has two pins labeled VIO which are sourced by a digital power supply that can output either 1.20V or 3.20V with a current limit of 300mA. Both pins (there is one VIO pin on the top row of the header and another on the bottom row) on the digital header are connected to the same supply so you only need to calibrate one of them.
I used some jumper wires to connect to the female header and then wrapped the other end of the jumper wires around the probes of my DMM.
Select Digital Supply in the list of the calibration menu, the VIO will first output 1.2V and you need to input what your DMM measures then click Next. VIO will then output 3.2V. Click Finish to return to the calibration menu.
Step 4 - Frequency Compensation
The final calibration function to execute is the frequency compensation for the oscilloscope channels. All of the oscilloscope channels either need to be connected to ground the same way as in the previous calibration step 2 or everything needs to be disconnected. I chose to disconnect the BNC cables altogether as it was the easier option.
Select Frequency Compensation from the list of the calibration menu and click Next in the pop-up window.
A progress bar will appear at the top of the page, as well as you'll see the measurements in the bottom window. Once completed, click Finish.
Step 5 - Apply Offsets from Calibration
At this point, all of the calibration measurements have been taken and those offsets just need to be applied in order to take the most accurate real measurements.
At the bottom of the Device Calibration window, click Apply and then Yes in the confirmation pop-up window.
Once the calibration offset values have been applied, close the Device Calibration window.ADP3450 Measurement Setup
To set up the physical test circuit, I used one 2N3904 with a 100Ω resistor and LED on the collector, then a 10kΩ in series on the base with the FPGA I/O pin:
There were three signals to make in this overall measurement: the signal on the collector, the signal on the base directly, and the signal right at the I/O pin of the FPGA to verify the delay across the solder less breadboard was not influential. So I used 3 of the 4 oscilloscope channels on the ADP3450:
- Oscilloscope Channel 1: Collector of 2N3904
- Oscilloscope Channel 2: Base of 2N3904
- Oscilloscope Channel 3: FPGA I/O pin
I also used one of the FPGA I/O pins that my 7-segment display driver used to drive a cathode to drive the one BJT of my test circuit so I could simply use the 7-segment display driver bitstream instead of writing custom logic for this test setup.
To program the FPGA with the bitstream to drive the I/O pin, I used Vitis and launched a debug run so I could have control over when the driver logic was activated in the RTL.
Returning to WaveForms, I opened a new workspace and selected Scope from the Welcome tab.
By default, all four channels are enabled, but I disabled channel 4 since nothing is connected to it. To disable the channel, I simply unchecked the box for Channel 4 in the panel on the right side of the Scope window.
Since all of the signals being measured are around the 3.3V mark, either a 500mV or 1V per division value is good for the range setting of each channel, and all channels should have an offset of 0V so when can view them superimposed on each other.
I set up a trigger on channel 1 (Source: Channel 1) for a rising edge value (Condition: Rising, Type: Edge) that exceeds 1V (Level: 1V). This means when the collector sees a signal that exceeds a value of 1V, a measurement will be take by the oscilloscope.
After setting up the trigger, you can click Single or Run. Single will record a single trigger even and stop the measurement, while Run will start measuring after a trigger event occurs then continue measuring until manually stopped.
You may have noticed that I did not specify a base value for the time per division for the x-axis. This is because after the measurement is taken and stopped, you can zoom in on the x-axis using the scroll wheel of a mouse or pinch to zoom of a trackpad to see the desired section of the signal. This will automatically set time per division of the x-axis as that is how the zoom function is being implemented.
After setting up the three scope channels and the trigger, I clicked Run then returned to Vitis to program the bitstream onto the FPGA to start driving the I/O pin connected to the 2N3904.
After successfully seeing the pulses being measured I clicked Stop and zoomed in on one. To measure the time delay between the signal going high on the base (Channel 1 - yellow) and the signal going low on the collector since it's being connected to ground (Channel 2 - blue) I used the cursor function.
Click X Cursors from the menu along the top of the scope window, then select + Normal from the window that pops up along the bottom of the scope window. Drag and drop the cursor on the point where the rising signal starts on Channel 1. Then select Delta from the cursor window from the bottom of the scope window then drag and drop the second cursor that appears to the point of the signal on Channel 2 where it hits ground potential (near 0V). You'll notice that the cursor window along bottom of the scope window will automatically calculate the time delta between the two cursors.
In this case the total time delay between the signal going high on the base (Channel 1 - yellow) and the signal going low on the collector since it's being connected to ground (Channel 2 - blue) is 1.352 microseconds (us).
I chose to save the workspace in case I wanted to use it in the future, by selecting Workspace > Save As.Results
In this particular case, the slew across the BJT turned out to be non-consequential to the 1 millisecond refresh rate of the 7-segment display as it was within the tolerance of what appears stable to the human eye. I validated this by testing a bitstream with the refresh rate set to exactly 1ms and 1.1352ms (1ms + 1.352ms) and saw no visible difference.
However, while it wasn't impactful this time, this measurement was easy enough to setup that it's worth double-checking because if the refresh rate had been closer to 1us or faster, the BJT slew absolutely would need to be taken into account.