Example Code

Multi-Channel HDLC with Manchester Encoding on FlexRIO Reference Example

Code and Documents

Attachment

Overview


This is an example implementation of the HDLC serial protocol with Manchester Encoding using the NI 6584 RS-485/RS-422 Adapter Module for NI FlexRIO that provides 16 Half or Full duplex RS422/485 channels.

Overview

The High-Level Data Link Control (HDLC) protocol is a bit-oriented synchronous data link layer protocol developed by the International Organization for Standardization (ISO).

The general structure of the HDLC protocol is:

Flag Address Control Information FCS Flag
8 bits 8 or more bits 8 or 16 bits Variable length, 0 or more bits 16 or 32 bits 8 bits

This example implements the HDLC protocol with a single (1) byte address, a single (1) byte control, a variable length information packet, and an FPGA based Frame Check Sequence (FCS) using a 16-bit CRC-CCITT.

Implementation

The implementation uses DMA to transfer data from the Host LabVIEW VI application to the LabVIEW VI running on the FPGA.  One DMA channel is used to transfer data to be transmitted and a second DMA channel is used to transfer data that has been received.

Host LabVIEW VI

The Host LabVIEW VI (HDLC Host.vi) handles sending data to the FPGA VI via DMA, receiving data from the FPGA via DMA, and configuring and controlling the FPGA VI.

The Host VI starts by downloading and configuring the FPGA VI.  This includes setting the Transmit and Receive Clock rates (in number of FPGA 40MHz ticks) as well as the device address.  The Transmit and Receive Clocks are the Manchester transmission rates, not the data rate.  This means that in order to transmit data at 2.5MHz, we must have a Manchester Clock rate of 5.0MHz which correlates to a cycle length of 200ns or 8 40MHz ticks.  The code directly calculates the number of ticks necessary to meet the desired data rate by dividing the data Clock Rate by 20,000,000 (1/2 of the 40MHz clock).

The DMA process for data to be transmitted includes formatting the data into a DMA "packet" that contains not only the data to be sent but additional meta-data such as channel number to transmit and if a the byte is a flag or not.  Each byte is transferred with an additional 8 bits of meta-data appended it.  The highest 4 bits represent if a byte is a flag (1010) or not (1001).  The next 4 bits below the flag meta-data represent the channel number (0000 to 1111 for channels 0 to 15).  Finally, the lowest 8 bits are the actual data byte to be transmitted in the case of non-flags.  The lowest 8 bits on flags are ignored.

When receiving DMA data from the FPGA, we must take into account that data can come in from multiple channels at a time, interleaved with other channels.  The data is sent from the FPGA as a U16 with the high 8 bits being meta-data and the low 8 bits being the data transmitted.  In order to properly re-construct the received data, a state machine is utilized to watch for the start of a DMA transmission for a specific channel.  Once a channel has been started, the data is stored for that specific channel by the state machine until a stop flag is received.  The stop flag contains additional meta-data indicating if the CRC was validated on the FPGA after reception.  After a full frame of data has been received for a channel, the frame is displayed on the Host LabVIEW VI's Front Panel.  The exact structure of the DMA messages that are transmitted from the FPGA to the Host via DMA are documented in the FPGA LabVIEW VI section.

FPGA LabVIEW VI

The FPGA LabVIEW VI is broken into 5 distinct sections of functionality.  These include Frontend Initialization, Host to FPGA DMA, Transmit I/O, Receive I/O, and FPGA to Host DMA.  The FPGA code is written using modular re-entrant SubVIs in such a way to allow easy extension of the code to handle additional channels.

FPGA LabVIEW VI - Frontend Initialization

The NI 6584 FlexRIO Frontend has two I/O nodes associated with the transmission lines for each of the 16 channels on the module.  These are TX_n and TX_Enable_n where n is the channel in question.  In order to utilize any of the TX_n channels for output, you must first set TX_Enable_n to true.  This is done in the FPGA VI in the first frame of the flat sequence structure.

FPGA LabVIEW VI - Host to FPGA DMA

As packets are received via DMA, this SubVI (Host DMA to FPGA and put in TX FIFOs.vi) will read the data stored in the DMA Packet and if valid, send it to the Transmit DMA State Machine. This will handle the reception order from the Host and will send the data to the correct Target Scoped FIFO so that the data is output by the correct Transmit I/O SubVI.

The Transmit DMA State Machine also calculates the CRC as data is received and sends the computed CRC value at the end of the received Frame. Once the CRC has been transmitted, the DMA State Machine checks for the end of the Frame before reading more data from the Host to FPGA DMA so that the final flag is sent to the Transmit I/O SubVI. This requires one additional cycle of the Single Cycle Timed Loop.

This means that the code must have at least 1 40MHz cycles between each frame that is received from the Host. Each frame will take a + c + n + 1 cycles to complete where a is the number of address bytes (1), c is the number of control bytes (1) and n is the number of data bytes sent (n must be >= 1). Because of the static size of the address and control bytes (1 byte each), we can simplify this to n + 3 cycles.

This results in a minimum DMA packet processing time of 4 cycles or 100ns. Therefore, the code can transmit frames from the Host to the FPGA at a maximum of 20MHz for all channels combined using an 80MHz SCTL.

The format of the DMA packets received from the Host is documented in the Host LabVIEW VI section above.

FPGA LabVIEW VI - Transmit I/O

The Transmit I/O SubVI (TX IO.vi) is where all of the bit transmission and Manchester Encoding is accomplished for a single channel.  An instance of this SubVI is required for every channel that will be transmitting data.  The channel specific data such as the Transmit Target Scoped FIFO and the Physical Transmit channel are Controls input from the main FPGA VI along with the Transmit Clock rate and the Flag Byte value.

The internal Manchester Clock is generated by inverting a boolean each time we execute the Transmission code. Based on this, it will either output the proper bit or it's inverse, thus generating a proper G.E. Thomas encoded Manchester signal. Once the inverse has been output, the code will read the next bit in the byte to be output and repeat the same process until complete.

When the end of the byte has been reached (all 8 bits transmitted), a new byte is read from the target scoped FIFO that is written to in the Host to FPGA DMA SubVI.

After data has been read from the FIFO, the code performs zero bit insertion if necessary and sends the bit array through a shift register. Additionally the current bit that is being output from the bit array is kept track of as well.

This SubVI is executed based on a counter that is incremented on each cycle of the 40MHz SCTL. Once the counter maximum has been reached (the Transmit Clock Rate in 40MHz Ticks set by the host), it is reset and the reception code described above is executed. This allows a maximum Manchester Receive Clock rate of 20MHz (two cycles required to generate the Manchester rising and falling edges). This means that this code can run faster than the maximum rate of the NI 6584 FlexRIO adapter module (16MHz max).

FPGA LabVIEW VI - Receive I/O

In this SubVI (RX IO.vi), the reception of data, reconstruction of bytes from bits, and clock skew correction and alignment are all handled.

On every execution iteration, the next bit is read from the Physical Receive line and it is rotated into the Manchester bit array that is stored in a shift register. For every byte, the code will receive 16 bits of Manchester encoded data.  Since the data rate can be set to various speeds, the SCTL samples the data at 40MHz and stores a bit until a change in the bit occurs.  The number of samples in a row of the same bit is also stored.  This average is then compared to subsequent number of same bits to determine if two of the same real data bits were sent in a row.

The internal Manchester Receive clock is generated by inverting a boolean on each execution. If Manchester clock misalignment is detected based on the bits received (00 or 11 are invalid Manchester pairs), the code will shift the Manchester clock by one cycle to recover. This will effectively shift the data in the bit array as well, thus recovering the data.

Once at least 16 aligned Manchester bits (8 data bits) have been receive without a Manchester clock error, the data received can be evaluated to detect an HDLC Flag. This is done by constantly rotating the received bits until they equate to an HDLC Flag byte. Once this occurs, the HDLC receive clock is now aligned with the HDLC transmit clock on the other end of the bus and valid data can be received.

Flags can then be watched for to send to the FPGA to Host DMA SubVI for processing and if necessary, transmission to the Host.  If a flag is detected, this SubVI will send it as a U16 with the high 4 bits set to 1111, the next 4 bits set to the channel number, and the final 8 bits set to the flag value.

If a non-flag has been detected and at least one flag has been received previously indicating the the HDLC and Manchester clocks are aligned, this SubVI will send the non-flag byte via another Target Scoped FIFO to the FPGA to Host DMA SubVI to be processed.

As data bytes (non-flags) are received, any zero bits that were inserted into the bit stream by the Transmission side will be removed before the data is sent to the FPGA to Host DMA SubVI.

All bytes that are sent to the FPGA to Host DMA SubVI via the Target Scoped FIFO also include 8 bits of meta-data such as the channel number and if the byte is a flag.  These 8 bits are placed in the high side of the U16 that is transmitted with the low 8 bits being the actual data that is sent.

This SubVI is executed based on a counter that is incremented on each cycle of the 40MHz SCTL. Once the counter maximum has been reached (the Receive Clock Rate in 40MHz Ticks set by the host), it is reset and the reception code described above is executed.  This allows a maximum Manchester Receive Clock rate of 20MHz (two cycles required to generate the Manchester rising and falling edges). This means that this code can run faster than the maximum rate of the NI 6584 FlexRIO adapter module (16MHz max).

LabVIEW FPGA VI - FPGA to Host DMA

This SubVI (Read From RX FIFO and DMA to Host.vi) takes the received bytes from the Receive I/O SubVIs, processes them, and sends the applicable bytes to the Host.

The first step is to get the data from the Receive I/O SubVI by reading the Target Scoped FIFO that is passed into this SubVI.

The received data is then passed into the Received Data FPGA to Host DMA State Machine. In this state machine, it is detect if a byte is a flag and the channel number is retrieved, using the upper 8 bits in the U16 that was received via the Target Scoped FIFO. The lower 8 bits are also split out as they contain the data.  Additionally, the previously received byte is stored in a feedback node for potential later use.

Once at least one flag has been received, the code will start reading data bytes, the first of which is the address byte. If the address byte matches the specified FPGA Device Address (set by the Host VI), the data in the received frame is addressed to this device and it should be sent to the Host.  If it is not addressed to this device, ignore the frame entirely.

If the first data byte after the flag was received and it was addressed here, a start message is sent to the Host and the code will begin calculating the CRC of the Frame for later validation.

After the first data byte (the address) is received and processed, all subsequent data bytes will be sent to the host, calculating the CRC over the entirety of these bytes.

Once another flag has been received after a string of non-flag bytes (the data in the frame), the end of the frame has been reached. The previous byte was the CRC, so validate it against the CRC that has been computed while receiving data.  Send an end message to the Host and set a bit in this message indicating if the CRC was valid or not.

Once the final flag in a frame has been received, wait for a new frame to start. Multiple flags in a row indicate the device is in the idle or quiet state so it will not send any data to the Host while this is occurring.

DMA Messages from the FPGA to the Host will take on the following forms:

Start of Frame Addressed to This Device: 1111aaaa11111111
Data Bytes in Frame: 0000aaaaxxxxxxxx
End of Frame: 1111aaaac0000000

Where aaaa is the channel number, xxxxxxxx is the data, and c is 1 if the CRC is valid and 0 if it is invalid.

Extending the LabVIEW FPGA VI to support additional channels

To add additional channels to the FPGA VI, follow these instructions.  Once complete, you must recompile the FPGA VI.  The Host VI provided does not require modification to support additional channels.

  1. Create a new Target Scoped FIFO for TX data in the Project with the following settings:
    Name: TX_n FIFO (where n is the new channel number)
    Type: Target Scoped
    Data Type: U16
    Number of Elements: 1029
    Implementation: Block Memory
     
  2. Create a new Target Scoped FIFO for RX data in the Project with the following settings:
    Name: RX_n FIFO (where n is the new channel number)
    Type: Target Scoped
    Data Type: U16
    Number of Elements: 1029
    Implementation: Block Memory
     
  3. Modify the "DMA Data to TX SubVI Engine Case Structure" in the "Host -> FPGA DMA SubVI" according to the instructions in that VI.
  4. Duplicate the "Channel 0 TX / RX / FPGA -> Host DMA SubVI" collection of SubVIs for each new channel and wire the
    "TX Clock Rate (40MHz ticks)", "Flag Byte", "RX Clock Rate (40MHz ticks)", "Data FIFO from FPGA to Host", and "Device Address" controls and constants to each of the three SubVIs you just duplicated, using the "Channel 0 TX / RX / FPGA -> Host DMA SubVI Engines" collection of SubVIs as an example for the wiring. Modify the label to read "Channel n TX / RX / FPGA -> Host DMA SubVI Engines" where n is the new channel number.
  5. In the newly created "Channel n TX / RX / FPGA -> Host DMA SubVI Engines" collection of SubVIs, specify the correct constant values for "Target Scoped TX FIFO", "TX Channel", "Target Scoped RX FIFO", "RX Channel", and "RX Channel #".

    For example, if you just added channel 2, the values should be set to:
    Target Scoped TX FIFO: TX_2 FIFO (you should have created this in Step 1)
    TX Channel: Frontend\TX_2
    Target Scoped RX FIFO: RX_2_FIFO (you should have created this in Step 2)
    RX Channel: Frontend\RX_2
    RX Channel #: 2
  6. Extend the Frontend Initialization I/O Node, select Frontend\TX_Enable_n where n is the new channel number and wire the new input of
    I/O Node to the true constant that is wired to the other inputs. This will ensure that the newly added TX channel is enabled.

 

Example code from the Example Code Exchange in the NI Community is licensed with the MIT license.

Contributors