Jump to content
  • 0

Protocol Development


Middy

Question

So, I need to capture data being sent over this kind of obscure protocol. It is called XY2-100, I'll attach a document that outlines it. It is for controlling laser scan heads. Basically It is made up of four differential pairs: x position, y position, clk, and sync. It is a streaming protocol with 20 bit words. The CLK line is a 2Mhz signal, so the data transfer is 2Mbits/s. The SYNC line signals the end of each word.

I want to capture the information being sent to a scan head with a PC so that I can track where the laser is being pointed. I've spent a lot of time looking into this and I wasn't able to find any off the shelf solutions. I know that FPGAs have been used in protocol development so I figured that it would be a good solution. Unfortunately I don't know much about them. I've been reading a lot, but it is a lot of information to take in. So, I'm looking for advice.

From what I've read, I've come up with an idea of how to do this. I've seen a number of solutions to implement an IP stack on an FPGA. I'm thinking I can leverage one of these to send the information from the RAM on the FPGA board to a PC over ethernet. I've got some logic of how to interpret the signal wires to capture the bits appropriately. On each CLK signal positive edge I'll save the x and y bits locally. When the SYNC bit goes low the 20 previous bits can be moved to the IP stack to get packaged up and sent. I'm thinking UDP is way easier to implement and uses fewer resources, and with the mass of data I'll be collecting the potential error rate is acceptable.

1)Does this make sense? Did I explain what I'm doing well enough? Is this a reasonable solution?

2)I'm having trouble figuring out what kind of resources this project will require in terms of the FPGA, ie number of logic cells, memory, etc. How can I get a better idea of that? Or maybe someone has done something similar and can give me some idea of the size of a project like this.

3)What FPGA board should I buy to do this? Or what should I look for on a board? I don't really want to spend a fortune, something that stays in the hundreds of dollars would be nice. And ease of use is nice, which is why I'm looking at digilent. 

Thank you for reading!

XY2-100 Protocol.pdf

Link to comment
Share on other sites

11 answers to this question

Recommended Posts

Hi @Middy

You definitely can accomplish your goals using FPGA. It is very similar to reading data from ADC chip which is pretty straight forward. The data rate is only 100 kwords/s (20 bits) or 400 kB/s when using 32bit words. Troubles start above 15 MB/s.

From my experience I suggest Zynq 7010 board. It has very capable FPGA and ARM processor which will handle communication with PC. I've used Zybo board for very similar project and was very happy with it. The board is well supported and it has everything you need, except, perhaps, some I/O level translation hardware.

BTW, at your data rate you can try to read the pulse train using GPIO directly. Then everything can be done using ARM CPU without HDL programming.

As always success depends on experience with FPGA, Zynq architecture, Xilinx Vivado. Knowledge and skills in C and HDL programming are also required.

Good luck!

Link to comment
Share on other sites

Thanks for your response @Notarobot. I don't know as much about the Zynq line of chips. I will definitely look into that, it sounds like it might work well based on my initial reading. The ARM processor should simplify setting up the IP stack. Do you know how difficult it is to move data between the FPGA and the ARM processor? Do they share memory? Is there some built in pipeline, or is it up to the user to figure out?

I've tried to do this with gpio on other devices but it was never consistent enough, because most processors are not real time. So the collected bits wouldn't be accurate to what was on the wire. I picked up a BeagleBone black because of the real-time processor that it has on board. But interfacing that with the on board ARM was a major pain. Actually everything with that device was a pain, haha. My experience with that makes me kind of hesitant to adopt other boards with multiple discrete chips on board. Hopefully the Zynq based devices are easier to use.

I know C no problem. I am getting more comfortable with Verilog by the day as well. My biggest hurdle has been the difference in how to think about problems in HDL languages versus programming languages for processors. As well as just figuring out the elements of an FPGA development environment.

Link to comment
Share on other sites

Hi Middy,

I have done similar tasks and then I have used the cores from fpga-cores.com/. I have used it on Arty A7 board and it works very well.

There is a built in logic analyzer in the core so you can watch the internal signals and you can also stream the data to the TCP or UDP port. You will be up and running in a couple of minutes :-)

And everything is free to use...

You have to look at the AXI4 Stream protocol to interface the TCP or UDP port.

Link to comment
Share on other sites

10 hours ago, Middy said:

The CLK line is a 2Mhz signal, so the data transfer is 2Mbits/s

I may have some good news: For UART mode, ...
The maximum Baud rate achieveable with FTDI's current devices is 3M Baud.

Please don't bet the farm on it, but it's easily verified (a trivial FPGA design that loops back Tx to Rx, or use a jumper wire if you have some standalone FT2232H board at hand). Then e.g. Teraterm on the PC end.
This should work on any "modern" FPGA board with FTDI chip. It's also possible to get an external module and connect with Tx/Rx/GND jumper wires or the like.

-----------------------------------------------------

A higher-performance alternative is FTDI's MPSSE mode, which achieves bit rates up to 30 MB. The vast majority of boards is not wired correctly to support this on the 2nd channel of the FT2232H chip, so you need an external board for the USB. I recommend FTDI's own board because it has electrical protection circuitry on the USB end that is missing from cheaper ones.

-----------------------------------------------------

Actually, any FPGA board with FT2232H supports the 30 MB variant through the JTAG pins but the implementation is somewhat more complex and I can't use Xilinx debug tools and IO at the same time.

 

What board? For the above FTDI-based schemes, for example CMOD A7 because it's (I think) the cheapest.

Link to comment
Share on other sites

Hi Middy,

I'll try to answer your questions.

The ARM processor (PS) and FPGA (PL) can easily communicate via Block RAM located on FPGA. Make it dual BRAM and both can read and write independently. ARM can R/W to BRAM the same way as to RAM. I've found that it is the best way for PS-PL data transfer. You can use DMA as well but it's more involved and I tend to use cleaner solutions. In my project I generate ARM interrupt when the section of BRAM is written and then ARM reads it and does what it supposed to do. At your data rate you can probably get away just polling the BRAM flag but interrupts are not difficult to implement. I believe that you can also communicate via GPIO registers but I didn't try this option.

Please note that Zynq ARM has standalone or barebone mode which is a deterministic, no OS programming environment. Xilinx provides a lot of examples with their programming suite Vivado. If you had experience with BeagleBone you will quickly pickup Zynq ARM. All ARMs very similar. BTW, TI provides noOS environment for Sitara CPU which is used in the Beaglebone. Did you use Linux experimenting with GPIO?

Regarding to UART mode I confirm that experimentally I was able to communicate at the data rate close to 1 Mbaud using UART PS with custom RS-232 PHY. This is good but not high enough for your case.

Considering board choices the board cost is the second consideration for me. The first one is the cost of development time which depends on quality of design tools, support, etc. I also look into the future upgrades and project enhancements. Having both ARM and FPGA multiplies your capabilities making life easier. In commercial world the cost of one day labor is exceeding the cost of any Digilent boards.

For cutting learning time I recommend watching video tutorials for Vivado and Zynq design. There are online classes as well. I also recommend to look at VHDL, which has origins in ADA. It has features helping to avoid error typical for beginners.

Link to comment
Share on other sites

@Notarobot:

Please check the datasheet (3 MBaud, not 1). If your UART doesn't achieve it, I'd start looking there.

The achievable USB throughput is easily confirmed in a loopback test (code below, derived from FTDI's C# example)

I measure 1352 ms for 3 MBit, which is close enough for this simple, single-threaded implementation.

CMOD A7 loopback code:

module mytop(
    input uart_txd_in,
    output uart_rxd_out
    );
    assign uart_rxd_out = uart_txd_in;
endmodule

PC end:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

using FTD2XX_NET;

namespace LoopBack {
    class Program {
        static void Main(string[] args) {
            UInt32 ftdiDeviceCount = 0;
            FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OK;

            // Create new instance of the FTDI device class
            FTDI myFtdiDevice = new FTDI();

            // Determine the number of FTDI devices connected to the machine
            ftStatus = myFtdiDevice.GetNumberOfDevices(ref ftdiDeviceCount);
            // Check status
            if(ftStatus == FTDI.FT_STATUS.FT_OK) {
                Console.WriteLine("Number of FTDI devices: " + ftdiDeviceCount.ToString());
                Console.WriteLine("");
            } else {
                // Wait for a key press
                Console.WriteLine("Failed to get number of devices (error " + ftStatus.ToString() + ")");
                Console.ReadKey();
                return;
            }

            // If no devices available, return
            if(ftdiDeviceCount == 0) {
                // Wait for a key press
                Console.WriteLine("Failed to get number of devices (error " + ftStatus.ToString() + ")");
                Console.ReadKey();
                return;
            }

            // Allocate storage for device info list
            FTDI.FT_DEVICE_INFO_NODE[] ftdiDeviceList = new FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];

            // Populate our device list
            ftStatus = myFtdiDevice.GetDeviceList(ftdiDeviceList);

            if(ftStatus == FTDI.FT_STATUS.FT_OK) {
                for(UInt32 i = 0; i < ftdiDeviceCount; i++) {
                    Console.WriteLine("Device Index: " + i.ToString());
                    Console.WriteLine("Flags: " + String.Format("{0:x}", ftdiDeviceList[i].Flags));
                    Console.WriteLine("Type: " + ftdiDeviceList[i].Type.ToString());
                    Console.WriteLine("ID: " + String.Format("{0:x}", ftdiDeviceList[i].ID));
                    Console.WriteLine("Location ID: " + String.Format("{0:x}", ftdiDeviceList[i].LocId));
                    Console.WriteLine("Serial Number: " + ftdiDeviceList[i].SerialNumber.ToString());
                    Console.WriteLine("Description: " + ftdiDeviceList[i].Description.ToString());
                    Console.WriteLine("");
                }
            }

            // Open first device in our list by serial number
            ftStatus = myFtdiDevice.OpenBySerialNumber(ftdiDeviceList[1].SerialNumber);
            if(ftStatus != FTDI.FT_STATUS.FT_OK) {
                // Wait for a key press
                Console.WriteLine("Failed to open device (error " + ftStatus.ToString() + ")");
                Console.ReadKey();
                return;
            }

            ftStatus = myFtdiDevice.SetBaudRate(3000000);
            if(ftStatus != FTDI.FT_STATUS.FT_OK) {
                // Wait for a key press
                Console.WriteLine("Failed to set Baud rate (error " + ftStatus.ToString() + ")");
                Console.ReadKey();
                return;
            }
            ftStatus = myFtdiDevice.SetLatency(0);

            // Set data characteristics - Data bits, Stop bits, Parity
            ftStatus = myFtdiDevice.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_8, FTDI.FT_STOP_BITS.FT_STOP_BITS_1, FTDI.FT_PARITY.FT_PARITY_NONE);
            if(ftStatus != FTDI.FT_STATUS.FT_OK) throw new Exception();
            
            ftStatus = myFtdiDevice.SetTimeouts(5000, 0);
            if(ftStatus != FTDI.FT_STATUS.FT_OK) if(ftStatus != FTDI.FT_STATUS.FT_OK) throw new Exception();

            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            sb.Append("Hello World");
            sb.Clear();
            for(int ix = 0; ix < 3*125; ++ix) // 3 kBit
                sb.Append("x");
            string dataToWrite = sb.ToString();

            Console.WriteLine("Start");
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start();
            for(int u = 0; u < 1000; ++u) { // 1000 repetitions
                UInt32 numBytesWritten = 0;
                ftStatus = myFtdiDevice.Write(dataToWrite, dataToWrite.Length, ref numBytesWritten);
                if((int)numBytesWritten != dataToWrite.Length) throw new Exception();
                if(ftStatus != FTDI.FT_STATUS.FT_OK) throw new Exception();

                string readData;
                UInt32 numBytesRead = 0;
                ftStatus = myFtdiDevice.Read(out readData, (uint)dataToWrite.Length, ref numBytesRead);
                if(ftStatus != FTDI.FT_STATUS.FT_OK) throw new Exception();
                if((int)numBytesRead != dataToWrite.Length) throw new Exception();
            }

            Console.WriteLine(sw.ElapsedMilliseconds + " ms for 3 MBit");
            ftStatus = myFtdiDevice.Close();
            Console.ReadKey();
        }
    }
}
   

 

Link to comment
Share on other sites

Hi xc6lx45,
I am impressed with your results and take into future considerations this possibility.
Using RS-232 was my first thought because it's so easy but it's also too slow for given data rate. Besides, in my work I used to rely on standards so that other can communicate without custom programming and using legacy equipment. I can't rely on USB, since not all equipment has USB and drivers.
It should be mentioned that RS-232 is also unreliable at high speed and that is why control systems in modern cars use CAN.
CAN has much smaller overhead that RS-232, 422, 485 but still at 1 Mbit/s you can get only around 70 kB/s of actual data rate. Using CAN FD allows for 8 Mbit/s but for Zynq it's achievable only with commercial IP.
Regarding to the OP task I wonder what are his actual requirements. Does he need continuous acquisition or sampling of data? It seems to me that the goal is monitoring not control. For monitoring purposes it might be sufficient to acquire, let say, every 100th message. This would reduce data rate to fit into RS-232 throughput. He can also save data acquired at full rate on micro SD card and analyze it on PC later. File system allows for 4GB per file. With Xilinx driver on Zybo the writing speed is close to 11MB/s, with commercial driver - 25MB/s.

Link to comment
Share on other sites

@Notarobot

No, we're not on the same page... The OP asked for 2 MBit/s. A FTDI UART is capable of this. So what exactly is the problem.

>> It should be mentioned that RS-232 is also unreliable at high speed
This and the whole "PHY" topic is off-topic and misleading because we're talking a direct board connection between the FTDI chip and a Xilinx chip. Please stop scaremongering. Or, provide specifics if FTDI claims features that don't work (which seems unlikely)
If a 3 MHz "high speed" digital signal would be unreliable across a one-inch trace, we'd all be in deep trouble...

 

Link to comment
Share on other sites

So it seems that you all think that UART would be the way to go to send my data to a PC. I was originally planning on using UDP/IP since most boards have an ethernet port right on there. Is this right? Is that the consensus? Because the UDP/IP seems pretty easy utilizing existing cores, and the transfer rate would be no problem. But do you think UART would be easier?

@PhDev Your idea does seems pretty straightforward. Looking over the info on fpga-cores it mentions having AXI Stream interface. So are you saying I'll need to learn about that interface to send my captured data over the cores IP? ie I'll have to write my own module to capture the data from the protocol then use the AXI interface to send that over UDP using that core? So would I then also need to setup some memory block to stage bits until I have enough to send in a UDP packet? Or is that taken care of?

Link to comment
Share on other sites

@Middy

Yes you need to write a simple process that writes data to the AXI stream however you don't need any additional memory. It is in the IP.  AXI stream is a very simple protocol. Basically the transfer takes place when both Ready and Valid is '1'. I can really recommend everyone to look at it. Xilinx has a lot of components that support this protocol like FIFOs, FIR filters, clock domain borders etc.

If you use UDP the only thing you have to do is to decide a packet length since UDP is packet oriented.  When adding the last byte in the packet you set TxLast to '1'.

When you use UDP server you need to write something from the PC so the FPGA(server) knows the PCs (client) address.

In your case I think using the TCP could be easier. Then you don't need to set packets length  etc you only add data to the stream and the core do everything else. The TCP is also taking care of re-transmitting etc.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...