This website uses cookies. By using this site, you consent to the use of cookies. For more information, please take a look at our Privacy Policy.
Home > FPGA Technical Tutorials > The Zynq Book > Spotlight on High-Level Synthesis > Introducing Vivado HLS

TABLE OF CONTENTS

Xilinx FPGA FPGA Forum

Introducing Vivado HLS

FONT SIZE : AAA

Introducing Vivado HLS

In this section, we will start by defining what Vivado HLS does and the steps involved,  before considering its role in the design flow for Zynq. Later, Chapter 15 will cover use of  the tool on a practical level, along with further discussions of algorithm and interface  synthesis, and the processes involved in creating solutions and evaluating them.

What Does Vivado HLS Do?

In short, Vivado HLS transforms a C, C++ or SystemC design into an RTL implementation, which can then be synthesised and implemented onto the programmable logic of a  Xilinx FPGA or Zynq device [33]. This represents the high-level synthesis step depicted in  Figure 14.2 on page 258.

It is important to reiterate that all C-based designs in the context of HLS are for imple mentation in programmable logic; i.e. as distinct from software code intended to run on a  processor (whether Zynq’s ARM processor, or a soft processor such as MicroBlaze).  

In performing HLS, the two primary aspects of the design are analysed:

• The interface of the design, i.e. its top-level connections, and 

• The functionality of the design, i.e. the algorithm(s) that it implements.

In Vivado HLS design, the functionality is synthesised from the input code via the  process of Algorithm Synthesis. The interface is created using one of two alternatives: it can  either be (i) manually specified, or (ii) inferred from the code (Interface Synthesis). A  simple conceptual diagram is provided in Figure 14.4 (note that this depicts only a subset of  interface types). 

In the specific case of the SystemC input language, interfaces must be manually  specified, with two exceptions which will be mentioned later [33]

Clarification of the algorithm and interface, and showing a subset of interface types.png

Algorithm Synthesis

Algorithm Synthesis is concerned with the functionality of the design. The desired  behaviour is interpreted from the C, C++, or SystemC code that forms the input to the  process (the input to the HLS process is a function written in the chosen programming  language). Operations are inferred, and these are translated into a set of RTL statements,  which are usually executed over several clock cycles.  

As will be discussed later, the designer can exert control over the algorithm synthesis  process via Vivado HLS directives, resulting in variations on the RTL output from HLS  being produced. For example, Solution 1 might require 500 slices to implement and 20  clock cycles to execute, whereas Solution 2 might require 1200 slices and 10 clock cycles.  The designer is able to experiment and thus achieve a favourable result in terms of their  priority implementation metric(s).

Interfaces I: Interface Synthesis

As its name suggests, Interface Synthesis refers to the interface of the HLS design, and  this includes both the ports and the protocols used. The details of all ports (in terms of their  types, dimensions, and directions) are inferred from the top-level function arguments and  return values of the source C/C++ file; protocols are inferred from the behaviour of the  ports. For example, the simplest interface would be a simple 1-bit wire, while for more  complex interfaces, a bus or RAM interface may be used. Naturally the synthesised  interface facilitates communicate with other modules in the system.  

Interfaces that can be inferred from interface synthesis include: wires, registers, one-way  and two-way handshakes, FIFOs, memories, and buses [33]. Further options are available  relating to interface synthesis, specifically to permit ports to be inferred from global  variables, and to include a global clock enable. Interface synthesis will be discussed in detail  in Chapter 15.

Interfaces II: Manual Specification

Although C and C++ designs are fully supported for interface synthesis, SystemC  designs are not, and as such, the interfaces of SystemC designs must be manually specified  and their behaviour fully described. This corresponds with the hardware-specific features  of the SystemC language, and an example will be provided in Chapter 15 which illustrates  the similarity between SystemC-based interface specification, and the equivalent in VHDL. 

 Notably there are two exceptions to the general rule stated above: memory and bus  interface types can both be inferred by interface synthesis of SystemC code.

Manual interface specification is also supported for C and C++ designs and may be used  if desired; this means that there is the option to explicitly define interfaces, rather than have  the interface synthesis process infer them.

Vivado HLS Design Flow

In the previous section, our discussion was limited to the primary processes and outputs,  i.e. the execution of HLS algorithms and the production of RTL code. However, the full  design flow for HLS comprises further stages, including elements of verification in  particular. The full design flow is shown in Figure 14.5, and then summarised.

An overview of the Vivado HLS design flow.png


Inputs to the HLS Process

The primary input to the HLS process is a C/C++/SystemC function, along with a Cbased testbench which has been developed to exercise the function and verify correct  operation. This will involve a ‘golden reference’ against which to test the outputs produced  by the function intended for synthesis; the golden reference may take the form of a  prepared set of output values, or it may form part of the testbench itself.

Functional Verification

Firstly, it is necessary to verify the functional integrity of the C/C++/SystemC code that  forms the input to HLS, before beginning the process of synthesising it into RTL code. This  can be achieved by writing a testbench in the same high-level language, and checking the  results produced against some form of ‘golden reference’; for instance, this might be a  prepared set of output test vectors which are known to be correct.

High-Level Synthesis

The next step is to undertake the HLS process itself, which involves analysis and  processing of the C-based code, together with user-supplied directives and constraints, to  create an RTL description of the circuit. Once the HLS process is complete, a set of output  files is produced, including design files in the desired RTL language. Various other log and  report files, testbenches, scripts, etc. are also created.

C/RTL Cosimulation

Once HLS has been performed and the equivalent RTL model produced, it can be  checked against the original C/C++/SystemC code via the process of C/RTL cosimulation in  Vivado HLS. This process re-uses the original, C-based testbench to supply inputs to the  RTL version generated by HLS, and check the outputs it produces against expected values.  Importantly, this saves the effort of generating a new RTL testbench. The SystemC output is  particularly useful here, as it provides a mechanism for verifying the design in environments where an HDL simulator is not available. A good example covering both C  functional verification and C/RTL cosimulation is available in [32].

Evaluation of Implementation

Along with verifying the integrity of the design, it is also necessary to evaluate the RTL  output in terms of its implementation and performance. For example, the numbers of  resources it requires in the PL, the latency of the design, maximum supported clock  frequency, and so on. We will outline these metrics in Section 14.4.4, and discuss them in

further detail in Chapter 15. For now, it is sufficient to note that, as the designer, we can  influence the implementation via the constraints and directives applied to the HLS process.

Design Iterations

Noting the above, as part of the design flow, the implementation of the RTL is evaluated  and, as necessary, the constraints and directives are refined; each revision corresponds to a  new ‘solution’ in Vivado HLS terminology (more to follow on solutions in Section 14.4.6). It  is also possible that evaluation of the design will prompt more fundamental review and  refinement of the original algorithm, as designed in C code and input to the HLS process.  Figure 14.5 shows the steps undertaken from C design, to the creation of outputs for  RTL synthesis. Note that multiple HLS iterations may be undertaken using modified directives and constraints, in order to find a ‘best’ solution; this corresponds to the feedback  path shown on the right hand side of the diagram.  Should the designer be prompted to change the input C code, a more significant step  backwards in the design process is involved, and this is indicated by the arrow at the left  hand side of the diagram. Any changes to the C code require functional re-verification,  before the subsequent HLS, C/RTL verification, and implementation evaluation processes  are again performed and iterated as needed.

RTL Export

Once the design has been validated, and the implementation iterated to the point of  achieving the intended design goals, it will be intended for integration into a larger system.  This can be achieved directly using the RTL files automatically created by the HLS process  (i.e. VHDL or Verilog code), however it may be more convenient to use the facilities of  Vivado HLS for packaging IP. Packaging the outputs produced by Vivado HLS means that  HLS designs can be easily introduced into other Xilinx tools, namely IP Integrator within  Vivado IDE, XPS (for the ISE design flow), and System Generator.

SystemC outputs from the HLS process are produced to enable verification of the  hardware produced, but are not themselves synthesisable.

C Functional Verification and C/RTL Cosimulation

Given the importance of verification, it is useful to further detail the C functional verification and C/RTL cosimulation processes. These are described graphically in Figure 14.6.

Depicted on the left hand side, a C-based testbench has been designed to create and  supply input test vectors to the functional C module. The same test vectors are passed

through a ‘known good’ golden reference design, or alternatively read from a prepared file,  to give golden reference output test vectors. These are compared with the outputs from the  C module, and the testbench reports a pass if the two set of results match, or failure  otherwise. The testbench may also be designed to report the total number of errors, or to  provide other automated feedback on the results.

As part of the Vivado HLS C/RTL cosimulation process, an equivalent testbench configuration is automatically created by Vivado HLS (shown on the right of Figure 14.6). The  testbench verifies the RTL version of the original C module, i.e. the primary output of HLS,  against the golden reference, and reports success or failure as before.

C functional verification and C RTL cosimulation in Vivado HLS.png

All of the files required for C/RTL cosimulation are created automatically by Vivado  HLS, which removes the need for manual RTL testbench creation. The generated testbench  includes the necessary translations of data passing between the C-based testbench and the  RTL module being tested.  

Vivado HLS is also capable of creating a bit-true, cycle accurate System-C model of the  generated hardware, and this can be co-simulated in circumstances where an RTL  simulation is not available.

Verification is clearly an important aspect of the design process, and the availability of  this tool support for RTL-level testing enables increased productivity. In particular, the  designer does not need to spend time creating an equivalent testbench for RTL simulation, and the potential for introducing errors by doing so are eliminated. However, it is  important to note that RTL simulation is a functional simulation, which does not model  realistic timing or bus protocol behaviours. Therefore, it cannot completely verify the  correct operation of the module in non-ideal conditions.

Implementation Metrics and Considerations

At this stage, it is useful to define the implementation metrics and related considerations  which form part of the design process. We do so here in a simple and informal style; these  issues will be returned to and expanded upon later, in Chapter 15.


Resources / area — How many resources are needed to implement my design, and how does this compare to the amount available on my target FPGA / Zynq device? 

Throughput — At what rate can I pass data through the design? Does this meet the 

needs of my application?

Clock frequency — What is the maximum clock frequency that I can run my design at? Is this compatible with the rest of my system?

Latency — How many clock cycles does it take for my design to produce an output? Is this delay acceptable in the context of the system in general?

Power consumption — How much power does my design consume when it is operating? Is this part of a system sensitive to power consumption?

I/O requirements — How complex are the interfaces of my design? Are they compatible with other components of the system?

Any or all of the above factors may be constrained in some way, often with certain  factors being prioritised over others. This normally depends on the requirements of the  application. For example, a system targeted at a low cost application might prioritise  resource minimisation, in order to utilise a smaller device; whereas, on the other hand, a  system requiring to adapt quickly to changing inputs may seek to minimise latency and  maximise throughput, at the expense of greater resource utilisation.

Any or all of the above factors may be constrained in some way, often with certain  factors being prioritised over others. This normally depends on the requirements of the  application. For example, a system targeted at a low cost application might prioritise  resource minimisation, in order to utilise a smaller device; whereas, on the other hand, a  system requiring to adapt quickly to changing inputs may seek to minimise latency and  maximise throughput, at the expense of greater resource utilisation.

Overview of the High-Level Synthesis Process

Although we have discussed the design flow in general terms, it is useful to consider the  process of HLS is more detail. This section will therefore summarise the steps involved in  the HLS of C/C++/SystemC design files to achieve an RTL equivalent. For the purpose of  providing practical examples, C will be used as the design language.

Recall from Section 14.4.1 that the HLS process performs (i) algorithm synthesis and (ii)  interface synthesis (if the interface is not explicitly specified). Here we focus on the former.

Aside from the design files themselves, other inputs to this process are the specification  of a particular target device, and the directives and constraints supplied by the designer. As  will be discussed in Section 14.4.6, these directly influence the implementation produced  by HLS. For the purposes of our current review, we will consider that the target device is  fixed, and the constraints and directives applied are constant.

Algorithm synthesis comprises three primary stages, which occur in the following order:

1. Extraction of data path and control; 

2. Scheduling and binding; and 

3. Optimisations

Each of these will now be briefly explained in turn.

Extraction of Datapath and Control

The first stage of HLS is to analyse the C/C++/SystemC code and interpret the required  functionality. This may, for instance, include: logical and arithmetic operations; conditional statements and branching; array operations; and loops.

The implementation will have a datapath component, and normally there will also be a  control component. For clarification, here ‘datapath’ processing refers to operations  performed on the data samples, whereas ‘control’ is the circuitry required to co-ordinate  dataflow processing. The nature of the algorithm fundamentally defines both the datapath  and control components but, as we will see in Chapter 15, the designer can take steps  during HLS to minimise the complexity of the control component in particular.

Scheduling and Binding

HLS is comprised of two main processes: scheduling and binding. These are undertaken  on an iterative basis, as shown in Figure 14.7, as one affects the other. The operations  performed in the two processes are summarised below.

Scheduling is the translation of the RTL statements interpreted from the C code into a set of operations, each with an associated duration in terms of clock cycles. The decisions made at this stage are affected by the clock frequency and uncertainty, the target device technology, and any directives applied by the user.

Binding is the process of associating the scheduled operations with the physical resources of the target device. The functional and timing characteristics of these resources may affect scheduling, and therefore binding information is fed back into the scheduling process. For instance, the use of DSP48x resources implies a shorter critical path than an equivalent operator built from logic slices.For example, if the synthesised algorithm requires that a set of arithmetic operations are  performed, the HLS process must decide how to schedule the operations (how many clock 

Vivado HLS scheduling and binding processe.png

cycles to allocate to their completion), and how to bind the operations (i.e. how to map  them to the computational resources on the PL), bearing in mind the target clock  frequency and uncertainty. Recall that the hardware architecture is not conveyed or  specified by the C source code, but rather different architectural variations can be  generated from the source code by applying directives.

The resulting implementation has a set of characteristics, principally in terms of (i)  latency, (ii) throughput, and (iii) the resources used.  

To illustrate, let us assume that our C algorithm involves calculating the average of an  array input, consisting of ten numbers. The implied operations are: 

• 9 addition operations to find the total; followed by 

• A multiplication by 0.1 to calculate the average.

There are a few different options in terms of scheduling and binding these operations.  One possibility is to operate serially over several clock cycles, using a single adder and a  single multiplier. Alternatively, the critical path may permit multiple operations to take  place within one clock cycle at the targeted frequency, resulting in an implementation with  lower latency and higher throughput.

Three variations are depicted in Figure 14.8. Consider in particular the differences in  implementation characteristics between them.  

1. The first implementation uses the fewest resources (1 adder and 1 multiplier, both constructed from logic fabric), and has a latency of 11 clock cycles. This design has 

a low throughput — one 11th of the clock rate — because a new operation cannot 

start until the last one has finished (we assume that pipelining is not used here — to 

be explained later in Chapter 15). 

2. Based on the target technology, the HLS process determines that 3 addition operations can be scheduled per clock cycle,while meeting the timing constraints. This results in an implementation using more resources on the device, but with a shorter atency and higher throughput. 

3. Finally, it is determined that, if DSP48x slices are used in place of fabric resources, all operations can take place within one clock cycle. This corresponds to the most costly implementation, requiring 9 DSP48x slices in total (one each for the first 8 additions, with the last addition and multiplication combined into a single DSP48x slice), but it has a much reduced latency (1 clock cycle), and a throughput equal to the clock rate. This style of implementation would of course only be applicable if 


Comparison of three possible outcomes from HLS for an example function.png


timing constraints were met — otherwise Vivado HLS may insert pipelining registers to meet timing

By default, the HLS process will optimise area, i.e. it will adopt the first strategy outlined  above, which consumes the fewest resources. The disadvantages of this implementation are  its long latency and low throughput, which may not meet the requirements of the application. However, the designer can exert influence by constraining and directing the HLS  processes of scheduling and binding, and thus optimising in a different way.

Optimisations

As mentioned above, the designer has mechanisms available to drive the high-level  synthesis process towards his or her implementation goals. There are two methods which  can be used to dictate the behaviour of the HLS process, and hence influence the results:

Constraints — The designer places a limit on some aspect of the design. For  instance, the minimum clock period may be specified. This makes it easy to ensure that the implementation meets the requirements of the system into which it will be  integrated. Similarly, the designer may choose to constrain resource utilisation or  other criteria, with the aim of optimising the design for the application in hand.

Directives — The designer can exert more specific influence over aspects of the RTL  implementation via directives. There are various types of directive available which  map to certain features of the code, enabling the designer to dictate, for example,  how the HLS engine treats loops or arrays identified in the C code, or the latency of  particular operations. This can yield significant changes to the RTL output.  Therefore, with knowledge of the available directives, the designer can optimise  according to application requirements.

A Note on the RTL Output

The user has the option to specify the RTL language for the generated output files, and  can choose from VHDL, Verilog, and SystemC. It is, of course, notable that SystemC is also  one of the available input languages for HLS, and therefore it might initially seem curious  that it is also listed as an output type. However, the SystemC file produced as an output of  HLS is an RTL description of the hardware, i.e. a lower-level representation than the input  to HLS. In the same way as for the other HLS output languages (VHDL and Verilog), the  implementation is based on the chosen target device. This output can be particularly useful  in verifying the design where an RTL simulator is not available.

Solutions: Exploring the Design Space

A Vivado HLS project is comprised of a set of design files, a set of testbench files, and  project settings, The project can have multiple solutions — this is an important term  because it relates to the theme of “exploring the design space”, i.e. generating a selection of  possible implementations which can be compared, and the most suitable version chosen.

It is important to recognise that each solution is a different implementation of the same C/C++/SystemC source code. The differences in the synthesised RTL solution are determined by four factors:

• The target technology and part (e.g. Virtex-7, Kintex-7, Zynq-7000.)

• The target clock frequency

• The implementation constraints applied

• User defined synthesis directives

For a given application, the first two of these (target technology and part) are likely to  remain constant, and the designer exerts influence over the generated solutions by varying  the implementation constraints and synthesis directives, as discussed in the previous  section. These will be defined in more detail as they relate to interface and algorithm  synthesis in Chapter 15. Once created, each solution contains information about the target,  the applied directives and constraints, and the results obtained.

As will be discussed in detail in the next chapter, the results obtained for a set of  solutions can differ according to resource cost, throughput, and latency. These can be  explored, and further refined, according to the requirements of the task.

Vivado HLS Library Support

It is worth noting that Vivado HLS includes support for arithmetic and mathematical  functions, as well as linear algebra, video processing, DSP and others. Full details of library  support is available in [33].



  • XC3S500E-4CPG132I

    Manufacturer:Xilinx

  • FPGA Spartan-3E Family 500K Gates 10476 Cells 572MHz 90nm Technology 1.2V 132-Pin CSBGA
  • Product Categories: FPGAs (Field Programmable Gate Array)

    Lifecycle:Active Active

    RoHS: -

  • XC5210-4TQ144C

    Manufacturer:Xilinx

  • FPGA XC5200 Family 16K Gates 1296 Cells 83MHz 0.5um Technology 5V 144-Pin TQFP
  • Product Categories:

    Lifecycle:Obsolete -

    RoHS: No RoHS

  • XC2S50E-7FT256C

    Manufacturer:Xilinx

  • FPGA Spartan-IIE Family 50K Gates 1728 Cells 400MHz 0.15um Technology 1.8V 256-Pin FTBGA
  • Product Categories: FPGAS

    Lifecycle:Obsolete -

    RoHS: No RoHS

  • XC3S500E-4FT256C

    Manufacturer:Xilinx

  • FPGA Spartan-3E Family 500K Gates 10476 Cells 572MHz 90nm Technology 1.2V 256-Pin FTBGA Tray
  • Product Categories: FPGAs

    Lifecycle:Active Active

    RoHS: No RoHS

  • XC2C256-VQG100CMS

    Manufacturer:Xilinx

  • Xilinx TQFP-100
  • Product Categories:

    Lifecycle:Any -

    RoHS: -

Need Help?

Support

If you have any questions about the product and related issues, Please contact us.