In order to test the RTM3004, it was important to have something that generated signals that could be used to challenge the unit. I view testing the unit to be an important part of properly reviewing a product and using the inbuilt demo features or generators is often not sufficient to uncover deficiencies. As a result, I pulled together some of the scraps I had at home and all of the skills I had to generate some signals.

 

This part details some of the special-purpose programs and designs used, although some other tests were performed based on testing existing projects which are not detailed in this appendix.

 

Arduino Mega 1280 16-bit Linear Feedback Shift Register (LFSR)

To test a 16-bit logic bus with signals that are not trivial can be surprisingly difficult. While a binary counter is a “classic” test, it’s a bit boring, simple and predictable. As a result, I decided to leverage linear feedback shift registers (LFSR) as a way of generating a pseudorandom signal stream that had known properties that are useful for testing.

 

In short, an LFSR can be made of a chain of D-flip-flops where the input is connected to a set of XOR gates fed from the output of the last in the chain and several intermediary outputs. Maximal LFSR “polynomials” generate all binary states except for all zeros and each state merely consists of all bits shifted across by one with one new bit being generated by the XOR function.

 

To do this, my first thought was to use my old Arduino Mega 1280, a lowly 16Mhz 8-bit AVR microcontroller. To optimise performance, interrupts were disabled (otherwise a periodic “hiccup” was encountered) and direct port I/O is used to drive the outputs in the most efficient way I could think of. This results in each 8-bit port being written in sequence before another port is “toggled” to produce a trigger clock signal.

The code is as follows:

// Arduino Mega1280 16-bit LFSR by Gough Lui

// Port D,F,K DIO

// A0-A15 = LFSR Outputs

// D21 = Toggles on Update

 

unsigned int lfsrState=0x0000FFFF;

unsigned int newBit;

 

void setup() {

  noInterrupts();

  DDRD = 0x01;

  DDRF = 0xFF;

  DDRK = 0xFF;

  PORTD = 0x00;

  PORTF = 0x00;

  PORTK = 0x00;

}

 

void loop() {

  newBit = ((lfsrState >> 0) ^ (lfsrState >> 1) ^ (lfsrState >> 3) ^ (lfsrState >> 12) ) & 1;

  lfsrState = (lfsrState >> 1) | (newBit << 15);

  PORTF = lfsrState>>8;

  PORTK = (lfsrState)&(0x000000FF);

  PORTD = ~PORTD;

}

 

While the code does perform the LFSR function, due to the speed of the board, the performance was only a low ~250khz. This wasn’t fast enough to challenge the oscilloscope but it was enough to generate some inputs to look at.

 

Digilent Uno32 16-bit Linear Feedback Shift Register (LFSR)

As a result, I thought I’d take out a PIC32 based prototype board that I purchased a while back but never got around to using – the Digilent Uno32 which has a much more impressive clock rate. Utilising the same idea of using direct port I/O, I ported the above code over, however as there was no “nice” 16-bit or even two 8-bit ports that mapped to contiguous pin headers, I had to do quite a bit of bit-arithmetic which resulted in the speed being somewhat degraded.

 

The code was as follows:

// Digilent Uno32 16-bit LFSR by Gough Lui

// Port B,D,E,F DIO

// D0-7 & D26-33 = LFSR Outputs

// A0 = Toggles on Update

 

unsigned int lfsrState=0x0000FFFF;

unsigned int newBit;

 

void setup() {

  noInterrupts();

  TRISB = TRISB & 0xFFFB;

  TRISD = TRISD & 0xFCF8;

  TRISE = TRISE & 0xFF00;

  TRISF = TRISF & 0xFFF1;

  LATB = 0x0;

}

 

void loop() {

  newBit = ((lfsrState >> 0) ^ (lfsrState >> 1) ^ (lfsrState >> 3) ^ (lfsrState >> 12) ) & 1;

  lfsrState = (lfsrState >> 1) | (newBit << 15);

  LATE = lfsrState>>8;

  LATF = ((lfsrState&0x03)<<2)|((lfsrState&0x010)>>3);

  LATD = ((lfsrState&0x04)<<6)|((lfsrState&0x08)>>3)|((lfsrState&0x020)>>4)|((lfsrState&0x040)>>4)|((lfsrState&0x080)<<2);

  LATB = ~LATB;

}

 

The performance increased to about 1Mhz or about four times faster than the one above. This was still a far cry from the 400Mhz capability of the RTM3004, but at least I managed to give the Uno32 something to do after all these years.

 

Digilent Nexys3 (Xilinx Spartan 6) FPGA-based 16-bit Counter with Switchable Clock & Linear Feedback Shift Register (LFSR)

Enter the heavy duty solution, namely an (expensive) FPGA-based development board. This Nexys 3 is something I’ve also had for a while but never really got around to using properly. To my dismay, Xilinx has decided that the Spartan 6 won’t be supported by Vivado, relegating it to the ISE suite (which I’m semi-familiar with) which doesn’t work properly under Windows 10. As a result, before I could get anything to work, I had to install Windows 7 in a Virtual Machine, install and license ISE Webpack on that and then create my design.

 

I decided I would design with simplicity in mind. Not being a highly skilled FPGA designer, I opted to draw it in Schematic Capture, not paying too much attention as to signal routing and stuffing whatever was necessary in the .ucf file to ignore my clock-routing transgressions in my desperation to use all four DCMs available to me to generate 400, 333, 300 and 266Mhz clocks from the onboard 100Mhz reference. This was switched through a 4:1 mux to drive two cascaded 8-bit counters (JA/JB ports) and a 16-bit LFSR (JC/JD ports) at that clock rate.

Digilent Nexys3 LFSR and Counter Schematic Design

While I could use just one DCM and dynamically reconfigure it, I didn’t have the time to design my own schematic block with the SPI logic to load the M and D values at runtime. Maybe that’s a project for another time.

 

Unfortunately, as it turns out, I was being too optimistic (and I knew it). Trying to get such high clock rate signals out of ports/pins which are not impedance matched or otherwise designed for it, along with spaghetti wiring and poor grounding practices made the signal either evaporate or look like mush. The orange is the 400Mhz clock (roughly) and the yellow is the LFSR output (which is practically no longer digital as far as I’m concerned).

Digilent Nexys3 LFSR Output at 400Mhz

As a result, it wasn’t possible to push it to 400Mhz easily, so I relented and changed the design so the LFSR would be clocked direct out of the 100Mhz reference clock instead. That way, it had a stable clock reference that wasn’t too fast and produced a decent signal for test purposes. Unfortunately, I paid no attention to the switching characteristics of the FPGA, so there is some skew on the outputs due to propagation delays and occasional glitches due to violation of the maximum number of simultaneously switching outputs. It’s still good enough, as long as you’re aware of the limitations.

 

The design is uploaded as a .zip file attached to the post. If you have a Nexys 3, you can probably just load the .bit file using Digilent Adept.

 

Arbitrary Waveform Generator Graphics

You might have wondered how I developed the arbitrary waveform generator values needed to draw graphics on the screen. Well, in fact, it’s quite simple but takes a little bit of thinking and planning.

 

To do it, you need to realise that the horizontal scanning is controlled by the oscilloscope timebase, so basically the image is being drawn from left to right continually. This is mainly because we only have one channel of generation to play with – if you had two, then you could have X-Y mode and basically turn an oscilloscope into a vector display.

 

To begin, you need to decide on the rough aspect ratio of the image (approximately 16:9) and the number of data points your AWG has. For every dot drawn in each column, we have to “wait” in that location (a voltage level) – a dwell time of at least two samples is needed so that the oscilloscope can “distinguish” a “blank” area where the sample is being changed from a drawn dot. In this case, for 32,000 points, something like 160x90 seemed sensible as it provided 14,400 pixels and if each was black, then you would need 28,800 data points. In the case a column actually had no lit pixels, the time still needed to be passed, so I selected the top voltage value as the “dummy” fill.

 

I decided to divide the 32,000 data points evenly between all 160 columns for 200 samples per column. The program generates the voltage values for each pixel in each column cyclically with the allotted dwell time, so some dots towards the bottom may be “redrawn” more than the dots at the top. This is no big issue, however.

 

In order to do everything, I decided to sketch the image up in Photoshop and rotate it for ease of data processing. This produces a 90x160 TIFF file in 8-bit greyscale, but containing only black and white values (for convenience of data processing). The TIFF headers are manually stripped out to create just the raw pixel data (8-bits-per-pixel) which is fed via STDIN into the program. The program’s STDOUT is piped into a file which contains voltage values of 0.05v per pixel (almost any sensible value can be used).

 

The voltage values need to have a monotonically increasing time value in the first column, so I imported the data into Excel and generated a series of dummy time values. I always left the last column empty, so I can replace it with a dummy value of -1V so as to provide a trigger point. The format of the data needs to be changed to scientific before saving as a CSV file. If you plot it in Excel, you can see what the traces would look like in line mode.

AWG Graphics Excel Simulation Line

But if you plot only the “dots”, as you would by changing the oscilloscope to dot mode and turning on persistence, then you can see the image.

AWG Graphics Excel Simulation Dot

If you import the file into the oscilloscope as a reference waveform and then select that for generation from the AWG and loop the output back into an input channel, you can plot it in real life, as I did in Chapter 7: Arbritrary Waveform, Function and Pattern Generator.

 

I later made an optimisation based on actually measuring the screen aspect and assuming that each drawn column can only have 50% of dots active at any time (or data will be missing at the top). This resulted in an image resolution size of 238 x 134 which can be achieved just by changing a few #defines.

 

The C-code (written as quickly as possible) is as follows:

// Hacky Program to Create AWG Data from a B/W Image

// by Gough Lui - July 2018

// ------------------------------------------------------------------------

// Input is STDIO, one byte per pixel where 0xFF is White and 0x00 is Black

// Image Orientation is Portrait (90 x 160px) - Rotate Image First!

// Best to save in TIFF and remove headers!

// Each row is converted to 200 samples, cycling between the voltage values

// with a two sample dwell time at each black pixel.

// For rows with no black pixel, last value is set.

// Output is 32000 Values (one per line) with 50mV/px scaling

// Combine in Excel with a dummy time-series and save as .csv for import

// as reference waveform in RTM3004.

// Suggest you leave the last row blank and manually place a dummy value to

// create an obvious trigger voltage to synchronize the horizontal sweep.

// ------------------------------------------------------------------------

// Program performs no error checks! No warranties implied!

// Change #defines at your own risk!

// ------------------------------------------------------------------------

 

#include <stdio.h>

 

#define WIDTH 90

#define HEIGHT 160

#define SAMPLES_PER_ROW 200

#define DWELL 2

#define PX_AMPLITUDE 0.05

#define WHITE 0xFF

#define BLACK 0x00

 

int main (void) {

  int linenr;

  int samplenr;

  int rowdata[WIDTH];

  int counter;

  int counter2;

 

  for(linenr=0;linenr<HEIGHT;linenr++) {

    for(counter=0;counter<WIDTH;counter++) {

      rowdata[counter]=getchar();

    }

    for(counter=0;(rowdata[counter]==WHITE)&&(counter<WIDTH);counter++) {

    }

    if(counter==WIDTH) {

      rowdata[WIDTH-1]=0;

    }

    counter=0;

for(samplenr=0;samplenr<SAMPLES_PER_ROW;samplenr+=DWELL) {

      while(rowdata[counter]==WHITE) {

        counter=(counter+1)%WIDTH;

      }

for(counter2=0;counter2<DWELL;counter2++) {

printf("%f\r\n",counter*PX_AMPLITUDE);

      }

      counter=(counter+1)%WIDTH;

    }

  }

}

 

Arduino 2Mbit/s Serial-Blaster Pattern Generator

This is just a simple program that generates a stream of pair of bytes, A to Z in the first byte and 0x00 through to 0xFF in the second byte which is helpful to test for high speed serial decoding at a custom rate and dead-time between oscilloscope captures.

// Serial Pattern Generator with Arduino Uno

// by Gough Lui

// A-Z for first byte

// 0x00 to 0xFF for second byte

// UART at 2Mbit/s, Interrupts Off

 

byte charone;

byte chartwo;

 

void setup() {

  noInterrupts();

  charone='A';

  chartwo=0;

  Serial.begin(2000000);

}

 

void loop() {

  Serial.write(charone);

  Serial.write(chartwo);

  if(chartwo==255) {

    if(charone=='Z') {

      charone='A';

    } else {

      charone++;

    }

  }

  chartwo++;

}

 

Conclusion

Sometimes having to do a proper review actually gives you the inspiration to push yourself and an excuse to play with development boards and design things just for fun. It was definitely an enjoyable additional benefit, some of the ideas coming from strange meditative moments in the shower. I hope it inspires you to go and tinker with something ... anything!