Aerospace



Discussion Forum

Home

Company Information

Information Request

Linux How-to Guides

ADSP 21xx
Digital Signal Processing
Tutorials

SW Utilities

On-line Order Form

Aerospace Projects

Commercial Projects

Circuit Boards

Server Support


bonk

Have you found this site useful? Did we save you time? Did we cure your head-ache? Is your hair growing back now?

Please make a donation to help with maintenance.


Objective Real-Time Software on the ADSP21XX

HDLC Protocol Framing

General

HDLC framing is used in many applications, PPP links, Ethernet, X25, modems etc. In short, HDLC is used wherever a bit stream is used to transmit packetized data.

In this chapter, we present an HDLC frame receiver. Creating a frame transmitter is always easier than the receiver, since the transmitter is in control of the situation, so I'm not going to bother with that one, maybe I'll add it later. The difficult part is the receiver, since it has to synchronize with the received data bit stream and extract 8 bit bytes from it.

The complete HDLC protocol is a complicated beast, allowing for multiple messages to be outstanding, with a multiple message ack/nak system. It is consequently a quite efficient protocol, but many applications do not use the complete protocol, only the framing and once you have the framing right and can extract HDLC encapsulated messages, the rest is a lot easier.

This example was extracted from a real application and cleaned up to make it presentable. It is a multi channel beast, which in this case handles 4 channels of HDLC data. You can change it to handle any number of channels, by changing a single literal HD_CHAN_MAX. Effectively, this chapter provides all you need to create a multi channel HDLC protocol analyzer.

The HDLC Frame

The HDLC Frame consists of a data packet, surrounded by flag sequences. The flag sequence is unique and is binary 01111110 or hexadecimal 7EH. This is a pattern consisting of a leading 0, followed by 6 1s and then another 0. Hey! That can occur in a real life data pattern, so it's not unique! Hmm, well, yes and no. In practice, the data is twiddled just a wee bit, to prevent the data from ever resembling a flag sequence, so don't worry about it now.

To receive a packet of data, one has to scan for a start flag, then capture the message until one detects an end flag. The framer then sends the message up in the protocol stack and go look for the next message.

In practice, there are a few problems to bear in mind here. Firstly, there are no start/end flags, a flag, is a flag, is a flag. Furthermore, there may be only one or more flags between packets, and there may be abort sequences when the system is idle and doesn't have anything useful to transmit. (Some systems transmit 7EH flags when idle, others send FFH). Also, since the data is a bit stream, you can be sure that the data will never start/stop on byte boundaries, so complex bit shifting is required in the receiver to extract a byte stream from the raw data.

Abort Sequence

An abort sequence is any sequence with more than 6 consecutive 1s. Hey! Surely that can happen with real data? Yes it can, but the data is twiddled just a wee bit, to prevent the data from ever resembling an abort sequence, so don't worry about it now either. An abort should cause whatever the receiver is doing to terminate and go back to looking for a start flag. I have seen several HDLC systems where the designers did not allow for the abort sequence, causing weird results once in a while.

Cyclic Redundancy Check

HDLC encapsulated messages are usually also protected with a cyclic redundancy check. This is usually either the ITU-T CRC16 or CRC32. For messages with lengths up to 1 kilobyte, CRC16 is usually employed. There is a chapter on CRCs elsewhere in this book, so I'm not going to elaborate here, but I did add look-up table driven assembler routines for the CRC16 to it, so do go have a look.

Bit Stuffing

So, how do we handle those Flag and Abort turkeys? Easy, we stuff the turkey so it won't have any data resembling either a flag or an abort sequence.

Whenever the packet has 5 consecutive 1s, we stuff in a 0.

This stuffing has to be done in the transmitter after the message was protected with a CRC. Therefore, we have to remove the stuffing before we can recalculate the CRC.

Framer Code

The full source of the framer is on the CD-ROM in directory work\hdlc.

Initialization

Initialization of the framer is performed by procedure hd_init(), which will initialize the whole machine for all channels and should be called at startup. During operation, procedure hd_init_chan() is used to initialize a specific channel when an abort sequence is received. Procedure hd_reset_msg_ptr() is used to clear a channel message buffer.

HDLC Receiver

The HDLC framer in procedure hd_frame() has to keep track of a lot of detail. It grabs one byte of data at a time from the bit stream and saves it in a variable called hd_data_byte. It then runs in a loop over all 8 bits of this byte and keeps track of the number of consecutive 1s in the variable hd_one_count. If the current bit is a zero, it resets the 1 counter.

The framer assumes that the data is already in the variable hd_rx_data when this procedure is called. You have to supply the I/O handler which will probably be some interrupt driven thing and which may use a SPORT, PLD or USART, then call hd_frame for every byte of data received.

The data bits are packed into an output variable hd_rx_byte and it keeps track of the number of bits in this byte using variable hd_bit_count. As soon as 8 bits are reached, the rx byte is full, is appended to the receive message and the receive message length in hd_byte_count is incremented.

If the current bit is 0, it starts a sequence of tests:

  • If hd_one_count is 5, then this zero is stuffing and is discarded
  • If hd_one_count is 6, then this is a flag sequence and if hd_byte_count is zero, then this is a start flag, else it is an end flag and we have to test the crc
  • If hd_one_count is > 6, then this is an abort sequence and we have to reset all variables and start looking for a flag again.

Finally, if the CRC test passes with a result equal to F0B8H, then we have a message that can be sent up into the protocol stack. Obviously, if we haven't received a full set of bytes, then the CRC test is not going to pass and the message will be discarded.

Message Handler

The message handler procedure hd_analyze_msg() performs a rudimentary check on the message length before bothering to calculate the checksum. This can save a lot of processing time when things are going wrong. You have to insert your message handling code at the marker "YOUR CODE GOES HERE" in this procedure.

Hardware Solutions

One also get complete hardware solutions for HDLC framing. The framer chips will handle HDLC transmit/receive and CRC-16 generation/test and can be handy in some applications, but a software solution is always more fun.

Code Listing

Table 1 contains the HDLC Framer code listing.


 
 
Table 1. HDLC Framer Code Listing
/*********************************************************************
*
* Name:        HDLC.dsp
*
* Description: HDLC Framing Handler
*
* Copyright (c) 1996 Aerospace Software Ltd
*
* Revisions:
* -------------------------------------------------------------------
* 1.0       May 96      Herman Oosthuysen    
*
*
*********************************************************************/

#define HD
#include "macros.h"
#include "crc16.h"
#include "hdlc.h"
#undef HD

/********************************************************************
* Name:        hd_init
* Description: initialize the complete HDLC protocol machine
* Constraints: none
* Tested in simulator
********************************************************************/
hd_init: 
   MAC_ENTER
   
   /*
    * Zap all protocol machine variables
    */
   ay1 = ^hd_msg_buf; 
   l3 = 0; 
   m3 = HD_MSG_SIZE; 
   ax0 = 0; 
   ar = 0; 
   
   cntr = HD_CHAN_MAX; 
   do hd_init_loop until ce; 
   MAC_WR_DM ( ax0, ^hd_rx_byte, ar )
   MAC_WR_DM ( ax0, ^hd_rx_data, ar )
   MAC_WR_DM ( ax0, ^hd_bit_count, ar )
   MAC_WR_DM ( ax0, ^hd_byte_count, ar )
   MAC_WR_DM ( ax0, ^hd_one_count, ar )
   
   MAC_WR_DM ( ay1, ^hd_msg_ptr, ar )
   i3 = ay1; 
   modify ( i3, m3 ); 
   ay1 = i3; 
   ar = ar + 1; 
hd_init_loop: nop; 

   /* ----INITIALIZE YOUR VARIABLES HERE---- */

   MAC_EXIT
   rts; 
   
/********************************************************************
* Name:        hd_reset_msg_ptr
* Description: initialize msg pointer to start of msg buffer
* Constraints: ar = channel
* Tested in simulator and on ICE
********************************************************************/
hd_reset_msg_ptr: 
   MAC_ENTER
   
   ay0 = ar; 
   mr0 = ^hd_msg_buf; 
   mr1 = 0; 
   my0 = ay0; 
   mx0 = HD_MSG_SIZE; 
   mr = mr + mx0 * my0 ( UU ); 
   MAC_WR_DM ( mr0, ^hd_msg_ptr, ay0 )
   
   MAC_EXIT
   rts; 
   
/********************************************************************
* Name:        hd_init_chan
* Description: initialize the HDLC protocol machine for one channel
* Constraints: ar = channel
* Tested in simulator and on ICE
********************************************************************/
hd_init_chan: 
   MAC_QUICK_ENTER

   /*
    * reset all bit counters
    */ 
   ay0 = ar; 
   ar = 0; 
   MAC_WR_DM ( ar, ^hd_rx_byte, ay0 )
   MAC_WR_DM ( ar, ^hd_rx_data, ay0 )
   MAC_WR_DM ( ar, ^hd_bit_count, ay0 )
   MAC_WR_DM ( ar, ^hd_byte_count, ay0 )
   MAC_WR_DM ( ar, ^hd_one_count, ay0 )

   /*
    * reset the msg pointer to the start of the msg buffer
    */
   ar = ay0; 
   call hd_reset_msg_ptr; 

   /*
    * zap the msg buffer
    * (easier debugging if one can see the new msg uniquely)
    */
   ax1 = 0; 
   MAC_RD_DM ( ax0, ^hd_msg_ptr, ay0 )
   cntr = HD_MSG_SIZE; 
   do hd_init_chan_loop until ce; 
   MAC_WR_DM ( ax1, ax0, ay0 )
   ar = ax0 + 1; 
   ax0 = ar; 
hd_init_chan_loop: nop; 

   MAC_QUICK_EXIT
   rts; 
   
/********************************************************************
* Name:        hd_frame
* Description: Look for a HDLC flag sequence, collect data bits etc
* Constraints: ar = data, ay0 = channel
* Tested in simulator and on ICE
********************************************************************/
hd_frame: 
   MAC_QUICK_ENTER
   
   /*
    * Get the data and HDLC state
    * Assumes raw data is already in variable hd_rx_data
    */
   ax0 = ar; 
#if HD_DEBUG_RX
   call ut_hex_out2; 
   ar = ASC_COLON; 
   call ut_char_out; 
#endif 

   MAC_WR_DM ( ax0, ^hd_rx_data, ay0 )      /* ax0 = rx data */

   MAC_RD_DM ( ax1, ^hd_rx_byte, ay0 )      /* ax1 = rx byte */
   MAC_RD_DM ( mr0, ^hd_bit_count, ay0 )    /* mr0 = bit counter */
   MAC_RD_DM ( mr1, ^hd_one_count, ay0 )    /* mr1 = one_counter */
   
   /*
    * Process an 8 bit data byte
    * of PPP compatible HDLC data
    */
   cntr = HD_BYTE_SIZE; 
   do hd_frame_loop until ce; 
   ar = tstbit 0 of ax0;                    /* test LSB of received data */
   sr0 = ax0; 
   sr = LSHIFT sr0 by - 1 ( lo );           /* shift data and save it in ax0 */
   ax0 = sr0; 
   
   if eq jump hd_frame_flag0;               /* bit test - one or zero? */

   /*
    * received a 1 bit 
    */
hd_frame1: 
   sr0 = 0x0080;                            /* shift a 1 into the rx byte */
   sr1 = 0; 
   si = ax1; 
   sr = sr or LSHIFT si by - 1 ( lo ); 
   ax1 = sr0; 
   
   ar = mr0 + 1;                            /* increment bit counter */
   mr0 = ar; 

   ar = mr1 + 1;                            /* increment one counter */
   mr1 = ar; 

   ay1 = 7;                                 /* reset test (7 1s) */
   ar = ar - ay1; 
   if lt jump hd_frame_test_byte1; 
   ar = ay0; 
   call hd_init_chan;                       /* reset the channel */
   ax0 = 0; 
   ax1 = 0; 
   mr0 = 0; 
   mr1 = 0; 
   jump hd_frame_loop; 
   
   /* 
    * received a 0 bit 
    */
hd_frame0: 
   ay1 = 5;                                 /* 0 stuffing test (5 1s) */
   ar = mr1 - ay1; 
   if eq jump hd_wait_frame_test_byte0;     /* discard stuffing bit */
   
hd_frame_frame: 
   sr0 = ax1;                               /* shift a 0 into the rx byte */
   sr1 = 0; 
   sr = LSHIFT sr0 by - 1 ( lo ); 
   ax1 = sr0; 
   
   ar = mr0 + 1;                            /* increment bit counter */
   mr0 = ar; 

   ay1 = 6;                                 /* flag = 7E test (6 1s) */
   ar = mr1 - ay1; 
   if ne jump hd_frame_test_byte0; 
   MAC_RD_DM ( ar, ^hd_byte_count, ay0 )
   ar = pass ar; 
   ar = ay0; 
   if ne call hd_analyze_msg;               /* end flag - process message */
   
   /* Byte grabber is now synchronized! */
   mr0 = 0;                                 /* zero bit counter after a flag */
   ax1 = 0;                                 /* zero the rx byte */
#if HD_DEBUG_FLAG
   ar = ASC_f; 
   call ut_char_out; 
#endif 

   /* 
    * see whether 8 bits received 
    */ 
hd_frame_test_byte0: 
   mr1 = 0;                                 /* zero 1 counter */
   
hd_frame_test_byte1: 
   ay1 = 8;                                 /* 8 bit byte test */
   ar = mr0 - ay1; 
   if ne jump hd_frame_loop; 
   MAC_RD_DM ( ar, ^hd_byte_count, ay0 )
   ay1 = HD_MSG_SIZE;                       /* stop when msg buffer is full */
   af = ar - ay1; 
   if ge jump hd_frame_test_full; 
   ar = ar + 1;                             /* increment the byte counter */
   MAC_WR_DM ( ar, ^hd_byte_count, ay0 )

   m3 = 1; 
   l3 = HD_MSG_SIZE; 
   MAC_RD_DM ( ar, ^hd_msg_ptr, ay0 )
   i3 = ar; 
   dm ( i3, m3 ) = ax1;                     /* save the received byte */
   ar = i3;                                 /* in the msg buffer */
   MAC_WR_DM ( ar, ^hd_msg_ptr, ay0 )
   
#if HD_DEBUG_DATA
   ar = ax1; 
   call ut_hex_out2; 
   ar = ASC_SP; 
   call ut_char_out; 
#endif
hd_frame_test_full: 
   ax1 = 0;                                 /* zero rx byte */
   mr0 = 0;                                 /* zero bit counter */

   /* WEND */
hd_frame_loop: nop;                         /* loop back on rx nibble */

   /*
    * Save the HDLC protocol state
    */
hd_frame_done: 
   MAC_WR_DM ( ax1, ^hd_rx_byte, ay0 )      /* ax1 = rx byte */
   MAC_WR_DM ( mr0, ^hd_bit_count, ay0 )    /* mr0 = bit counter */
   MAC_WR_DM ( mr1, ^hd_one_count, ay0 )    /* mr1 = one_counter */

hd_frame_exit: 
   MAC_QUICK_EXIT
   rts; 
   
   
/********************************************************************
* Name:        hd_analyze_msg
* Description: Analyze the received message
* Constraints: ar = channel
********************************************************************/
hd_analyze_msg: 
   MAC_QUICK_ENTER

   /* 
    * Reset the port status timer whenever a msg is received
    * See whether the message is long enough, 
    * but not too long
    */
   ay0 = ar;                                /* Save port number in ay0 */

   MAC_RD_DM ( ax0, ^hd_byte_count, ay0 )   /* message too short? */
   ay1 = HD_MSG_MIN_SIZE; 
   ar = ax0 - ay1; 
   if le jump hd_analyze_msg_done; 
   
   ay1 = HD_MSG_SIZE;                       /* message too long? */
   ar = ax0 - ay1; 
   if ge jump hd_analyze_msg_done; 

   MAC_RD_DM ( ax0, ^hd_byte_count, ay0 )   /* decr length */
   ay1 = HD_HEADER_MSG_SIZE;                /* disregard header and crc */
   ar = ax0 - ay1; 
   MAC_WR_DM ( ar, ^hd_byte_count, ay0 )
   if le jump hd_analyze_msg_done;          /* quit if empty message */
   
   /* 
    * verify the CRC-16 
    * the CRC is inverted and byte swapped
    */
hd_analyze_msg_verify: 
#if HD_DEBUG_MSG
   ar = ASC_m; 
   call ut_char_out; 
#endif
   ar = ay0; 
   call hd_reset_msg_ptr; 
   
   MAC_RD_DM ( ax1, ^hd_msg_ptr, ay0 )      /* get start of msg buf */

   l3 = 0; 
   m3 = 1; 
   i3 = ax1;                                /* get start of msg buf */

   ay1 = ay0;                               /* Save port number in ay1 */
   ay0 = HD_CRC16_INIT;                     /* ay0 is the shiftreg = CRC */

hd_analyze_msg_crc: 
   cntr = ax0;                              /* length */
   do hd_analyze_msg_loop until ce;         /* calc CRC with 4808 poly */
   ar = dm ( i3, m3 ); 
   call cc_calc_crc;                        /* lookup table based CRC-16 */
   ay0 = ar; 
hd_analyze_msg_loop: nop; 
   ay0 = ay1;                               /* restore port number to ay0 */
   
   ay1 = HD_CRC16_CONST;                    /* compare with F0B8 */
   af = ar - ay1; 
   if ne jump hd_analyze_msg_crc_fail; 

   
   /*
    * CRC-16 is good:
    */
hd_analyze_msg_crc_pass: 
#if HD_DEBUG_MSG
   ar = ASC_g; 
   call ut_char_out; 
#endif
   /* 
    * switch on the msg opcode 
    * Packet Format:
    * Flag DD DD... CC Flag
    */
   MAC_RD_DM ( ar, ^hd_msg_ptr, ay0 )
   
   /* -------YOUR CODE GOES HERE-------- */




   /*
    * CRC-16 is BAD:
    */
hd_analyze_msg_crc_fail: 
#if HD_DEBUG_MSG
   ar = ASC_b; 
   call ut_char_out; 
#endif

   /* ----YOU MAY WANT TO ADD SOMETHING HERE---- */
   

   /*
    * Prepare for the next message packet
    */
hd_analyze_msg_done: 
   ar = ay0; 
   call hd_init_chan; 
   
hd_analyze_msg_exit: 
   MAC_QUICK_EXIT
   rts; 

   
   
.ENDMOD; 
/**************************** EOF - hdlc.DSP ***********************/

 
 

Header File

Table 2 contains the HDLC Framer header file.


 
 
Table 2. HDLC Framer Header File.
/*********************************************************************
*
* Name:        HDLC.h
*
* Description: HDLC Frame Handler
*
* Copyright (c) 1996 Aerospace Software Ltd.
*
* Revisions:
* -------------------------------------------------------------------
* 1.0       May 96      Herman Oosthuysen    
*
*
*********************************************************************/

/* Resolve public definitions */
#undef   PUBLIC
#undef   PROTOTYPE
#ifdef   HD
#define  PUBLIC      GLOBAL
#define  PROTOTYPE   ENTRY
#else
#define  PUBLIC      EXTERNAL
#define  PROTOTYPE   EXTERNAL
#endif

/***************************** Literals *****************************/
#define  HD_CHAN_MAX             0x0004
#define  HD_MSG_SIZE             0x0020   
#define  HD_BUF_SIZE             (HD_MSG_SIZE * HD_CHAN_MAX)

#define  HD_BYTE_SIZE            8
#define  HD_MSG_MIN_SIZE         0x0006


/***************************** Variables ****************************/
#ifdef HD
.VAR/DM/RAM/circ/SEG=INT_DM
   hd_msg_buf[HD_BUF_SIZE];
   
.VAR/DM/RAM/SEG=INT_DM
   hd_msg_ptr[HD_CHAN_MAX],
   hd_rx_byte[HD_CHAN_MAX],
   hd_rx_data[HD_CHAN_MAX],
   hd_bit_count[HD_CHAN_MAX],
   hd_byte_count[HD_CHAN_MAX],
   hd_one_count[HD_CHAN_MAX];


#endif

/****************************** Publics *****************************/
.PUBLIC
   hd_rx_byte,
   hd_rx_data,
   hd_bit_count,
   hd_byte_count,
   hd_one_count;

/***************************** Prototypes ***************************/
.PROTOTYPE
   hd_init,
   hd_init_chan,
   hd_frame,
   hd_analyze_msg,
   hd_reset_msg_ptr;
   
/**************************** EOF - HDLC.H **************************/


Copyright © 1996-2008, Aerospace Software Ltd., GPL.