UPGRADE YOUR BROWSER

We have detected your current browser version is not the latest one. Xilinx.com uses the latest web technologies to bring you the best online experience possible. Please upgrade to a Xilinx.com supported browser:Chrome, Firefox, Internet Explorer 11, Safari. Thank you!

Adam Taylor’s MicroZed Chronicles Part 91: More on High-Level Synthesis and SDSoC, Part 7

by Xilinx Employee ‎07-13-2015 11:29 AM - edited ‎02-09-2017 02:15 PM (18,592 Views)

 

By Adam Taylor

 

 

As we briefly examined last week, while HLS is very powerful we must create the correct coding structures if the code is to synthesize efficiently. At the highest level there, are a number of high level rules which we must follow:

 

 

  • The function cannot contain any system calls to the operating system
  • The function must contain the entire functionality
  • C Constructs need to be bounded and of a fixed size
  • Implementation of the constructs needs to be unambiguous

 

The first point is very self-explanatory as system calls task the operating system, which is clearly not going to be synthesized. Examples of system calls are printf(), fprintf(), time(), and sleep() etc. If we need to include system calls or no synthesizable constructs within the function for use during testing, we can use the macro __SYNTHESIS__ to exclude non-synthesizable code. Of course we need to be very careful not to use this macro to alter the behavior of the function so that it behaves differently when synthesized.

 

When writing traditional C functions, it is often common to allocate and free system memory using the malloc(), alloc(), and free() functions. This coding technique poses issues for synthesis as we need to ensure the memory requirements of the function to be synthesized are bounded. Unbounded memory cannot be synthesized using finite hardware resources.

 

In this case it is often best to use a user-defined macro—as in the code below—to ensure that the code is the same when synthesized or not:

 

 

Image1.jpg

 

 

The above code snippet demonstrates how we can avoid system calls and use constructs that are fixed and bounded, thus easing synthesis.

 

One of the most popular constructs in C is the pointer. We can use pointers within code we intend to synthesize however we do need to follow some basic rules if they are to be synthesized efficiently. The first and most important rule involves casting. If we want to be able to use HLS, then we can cast between native C types. However we cannot cast using general pointers. It is permissible to use pointers within the function parameter list, to use pointer arithmetic, and use pointers to arrays.

 

We also need to ensure that we do not use recursive functions within the function that we wish to accelerate using HLS. A recursive function is one that calls itself either a finite or infinite number of times.

 

As well as coding styles that enable synthesis, there are also some coding styles that enable better optimization. For example, a loop with variable bounds can prevent optimization because HLS cannot determine the loop latency. We can address this problem using an assert macro in the C code to provide the maximum number of loops. It is worth noting here that we cannot unroll a variable bounds loop because HLS does not know how much hardware to create. In such cases, we may wish to look into ways to rewrite the function based on a fixed number of iterations.

 

Loops provide a great area for optimization. Remember in our previous example, we worked on optimizing a loop to increase the throughput. Using nested loops, we can pipeline either the inner loop (like we did in our example), which will result in the smallest logic footprint, or we can pipeline the outer loop, which will unroll the inner loop and create dedicated hardware for each iteration. Of course, increasing performance demands more resources, which may or may not be allowable depending upon resources available.

 

To flatten loops, they need to be either perfect or semi perfect. There’s only one difference between the two. A perfect loop has defined bounds while a semi perfect loop allows the outer loop bound to be variable. However both perfect and semi perfect loops must also comply with the following constructs:

 

 

  • Only the innermost loop has contents
  • There is no logic specified between loop statements

 

Having now looked more at coding styles, I will be looking at verification and test benches in the next blog before we start looking at more SDSoC examples.

 

 

 

Comments
by Adventurer
on ‎02-09-2017 12:20 AM

Hello,

 

what do you mean by "general types"?

 

If we want to be able to use HLS, then we can cast between native C types. However we cannot cast between general types.

by Observer taylo_ap
on ‎02-09-2017 02:10 PM

Cerilet

 

Apologies, that should read 

 

If we want to be able to use HLS, then we can cast between native C types. However we cannot cast using general pointers.

 

Thanks for reading 

 

Adam 

 

 

Labels
About the Author
  • Be sure to join the Xilinx LinkedIn group to get an update for every new Xcell Daily post! ******************** Steve Leibson is the Director of Strategic Marketing and Business Planning at Xilinx. He started as a system design engineer at HP in the early days of desktop computing, then switched to EDA at Cadnetix, and subsequently became a technical editor for EDN Magazine. He's served as Editor in Chief of EDN Magazine, Embedded Developers Journal, and Microprocessor Report. He has extensive experience in computing, microprocessors, microcontrollers, embedded systems design, design IP, EDA, and programmable logic.