Features .............................................................................................................................................. 1–2
Design Abstraction and the Rise of C for FPGAs ........................................................................ 1–3
What to Expect From the C2H Compiler ...................................................................................... 1–5
C2H Support in Nios II Tool Flows ............................................................................................... 1–6
Understanding Code to Find Opportunities for Acceleration ................................................. 1–15
Next Steps ............................................................................................................................................. 1–16
Set up the Hardware for the Project .............................................................................................. 2–5
Create the Software Project ............................................................................................................. 2–6
Run the Project as Software Only .................................................................................................. 2–7
Create and Configure a Hardware Accelerator ........................................................................... 2–8
Rebuild the Project ......................................................................................................................... 2–10
Observe Results in the Report File ............................................................................................... 2–11
Observe the Accelerator in SOPC Builder .................................................................................. 2–14
Run the Project with the Accelerator ........................................................................................... 2–14
Remove the Accelerator ................................................................................................................ 2–15
Altera Corporation 9.1iii
Contents
Next Steps ............................................................................................................................................. 2–16
Cycles Per Loop Iteration (CPLI) ................................................................................................. 4–11
Scheduling Information ................................................................................................................. 4–14
Further Reading ................................................................................................................................... 4–19
Chapter 5. Accelerating Code Using the Nios II Software Build Tools Command Line
Creating an Accelerator from the Command Line ........................................................................... 5–1
Language ................................................................................................................................................. 7–1
Miscellaneous Unsupported Features ........................................................................................... 7–8
Other Restrictions .................................................................................................................................. 7–9
Revision History ........................................................................................................................................ 2
How to Contact Altera .............................................................................................................................. 3
vi 9.1Altera Corporation
Nios II C2H Compiler User Guide
1. Introduction to the
C2H Compiler
The Nios® II C-to-Hardware Acceleration (C2H) Compiler is a tool that
allows you to create custom hardware accelerators directly from ANSI C
source code. A hardware accelerator is a block of logic that implements a
C function in hardware, which often improves the execution performance
by an order of magnitude. Using the C2H Compiler, you can develop and
debug an algorithm in C targeting an Altera
quickly convert the C code to a hardware accelerator implemented in a
field programmable gate array (FPGA).
The C2H Compiler improves the performance of Nios II programs by
implementing specific C functions as hardware accelerators. The
C2H Compiler is not designed to create arbitrary hardware systems from
C code. Rather, the C2H Compiler is a tool for generating a hardware
accelerator module, functionally identical to the original C function, that
offloads and enhances the performance of the Nios II processor.
®
Nios II processor, and then
User Guide
Overview
Altera Corporation 9.11–1
November 2009
This user guide comprises the following chapters:
■Chapter 1, Introduction to the C2H Compiler provides a detailed
background on the C2H Compiler and the concepts required to use
it.
■Chapter 2, Getting Started Tutorial provides hands-on instructions
that teach you the first steps to begin using the C2H Compiler.
■Chapter 3, C-to-Hardware Mapping Reference provides reference on
how the C2H Compiler translates C constructs to hardware
structures.
■Chapter 4, Understanding the C2H View helps you use the C2H
view to get performance information and to control the compilation
of accelerators.
■Chapter 5, Accelerating Code Using the Nios II Software Build Tools
Command Line explains how to use the C2H Compiler with the
Nios® II software build tools.
■Chapter 6, Pragma Reference summarizes usage of all C2H #pragma
directives.
■Chapter 7, ANSI C Compliance and Restrictions documents all
sections of the ANSI C specification that the C2H Compiler does not
support.
Target Audience
Target Audience
Introduction
This user guide assumes you have at least a basic understanding of
hardware design for field programmable gate arrays (FPGAs). It also
assumes you are fluent in the C language and you have experience with
software design in C for microprocessors.
The C2H Compiler operates in conjunction with the following Altera
tools:
■Quartus II software for creating FPGA designs
■SOPC Builder system integration tool for creating Nios II processor
hardware systems
■C programming environments for the Nios II processor:
●Nios II integrated development environment (IDE)
●Nios II software build tools
To benefit from this user guide, you do not need to be an expert in these
tools, and you do not need an understanding of any particular Altera
FPGA family. However, at least a basic understanding of each tool is
required to use the C2H Compiler practically.
This chapter introduces the Nios II C2H Compiler. The sections in this
chapter discuss the features, background, and principles of the
C2H Compiler, and describe the most appropriate types of C code for
acceleration. After reading this chapter, you will understand all the
concepts necessary to begin using the C2H Compiler.
Features
The C2H Compiler is founded on the following premises:
■ANSI C syntax is sufficient to describe computationally intensive or
memory access-intensive tasks.
■A C-to-hardware tool must not disrupt existing software and
hardware development flows.
Based on these premises, the C2H Compiler's design methodology
provides the following features:
■ANSI C compliance – The C2H Compiler operates on plain ANSI C
code, and supports most C constructs, including pointers, arrays,
structures, global and local variables, loops, and subfunction calls.
The C2H Compiler does not require special syntax or library
functions to specify the structure of the hardware. Unsupported
ANSI C constructs are documented.
1–2 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Introduction to the C2H Compiler
■Straightforward C-to-hardware mapping – The C2H Compiler maps
each element of C syntax to a defined hardware structure, giving you
control over the structure of your hardware accelerator.
■Integration with C language development environments for the
Nios II processor, including the Nios II integrated development
environment (IDE), and the Nios II software build tools. You control
the C2H Compiler with the Nios II C development tools. You do not
need to learn a new environment to use the C2H Compiler.
■Based on SOPC Builder and Avalon system interconnect fabric – The
C2H Compiler uses SOPC Builder as the infrastructure to connect
hardware accelerators into Nios II systems. A C2H accelerator
becomes a component within an existing Nios II system. SOPC
Builder automatically generates system interconnect fabric to
connect the accelerator to the system, saving you the time of
manually integrating the hardware accelerator.
■Reporting of generated results – The C2H Compiler produces a
detailed report of hardware structure, resource usage, and
throughput.
Hardware accelerators generated by the C2H Compiler have the
following characteristics:
■Parallel scheduling – The C2H Compiler recognizes events that can
occur in parallel. Independent statements are performed
simultaneously in hardware.
■Direct memory access – Accelerators access the same memories that
the Nios II processor does during execution.
■Loop pipelining – The C2H Compiler pipelines the logic
implemented for loops, based on memory access latency and the
amount of code that operates in parallel.
■Memory access pipelining – The C2H Compiler pipelines memory
accesses to reduce the effects of memory latency.
Design Abstraction and the Rise of C for FPGAs
There is much interest in “C-to-gates” tools that promise a practical
method to create hardware logic directly from C code. However, early
attempts have had limited success gaining acceptance in the design
community. This section discusses the historical background of the
C2H Compiler, and looks at the questions “why is this methodology a
good idea?” and “why now?”
C compilers and FPGA design tools have evolved along separate paths,
but both are founded on the same premise: Higher levels of design
abstraction enable engineers to create designs of greater size and
complexity. Simultaneous with this evolution, Moore's law has delivered
chips of increasing density and complexity, such as FPGAs capable of
Altera Corporation 9.11–3
November 2009Nios II C2H Compiler User Guide
Introduction
implementing entire systems on a chip. As a result, the tools available to
FPGA and software designers have undergone continual transformation
of design-entry methods and behind-the-scenes optimization techniques.
This transformation has enabled designers to create ever-bigger designs
to fill ever-growing chip capacity.
Recent years have seen the broad acceptance of FPGA-based
microprocessor cores, such as the Nios II processor, and system
integration tools, such as SOPC Builder. These tools made it possible, for
the first time, to implement C code easily in an FPGA-based system.
Optimizing and evolving these tools is a natural next step for C-based
design on FPGAs. This background sets the stage for practical advances
in C-to-hardware technologies based on an established design
methodology.
FPGA-based processors and system integration tools offer new ways to
improve the performance of embedded systems. Traditional methods to
increase performance of processor systems include:
■Increasing clock speed
■Upgrading to a processor with higher Dhrystone MIPS-per-
megahertz performance
■Coding critical sections of software in assembly language
FPGA-based processor systems enable additional optimization
techniques capable of achieving much higher performance gains. These
techniques include:
■The ability to rapidly alter the FPGA design, allowing you to
prototype a variety of architectures
■The ability to divide and conquer processing tasks by instantiating
multiple processor cores
■The ability to augment a processor with custom hardware that off-
loads processor-intensive operations into the FPGA fabric
■The ability to adjust memory architecture for memory-intensive
operations, such as using high-speed, point-to-point connections to
fast memory buffers
The application of these techniques relies on real-world tools to
implement them. Consequently, the acceptance of these techniques has
grown as system integration tools, such as Altera's SOPC Builder, have
matured and gained acceptance. It is a fortunate coincidence that these
techniques also directly benefit C-to-gates methodologies. Flexibility of
hardware architecture and ease of implementation are at the heart of the
appeal of C-to-gates tools.
1–4 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Introduction to the C2H Compiler
The Nios II C-to-Hardware Acceleration (C2H) Compiler represents
Altera's next step in the evolution of embedded systems design. The
C2H Compiler uses the infrastructure provided by SOPC Builder and the
Nios II processor, and adds a higher level of abstraction: converting C
functions directly to hardware.
What to Expect From the C2H Compiler
The C2H Compiler is not designed to build all types of FPGA systems. It
is designed specifically to augment the performance of programs that run
on the Nios II processor; it does not replace the processor. Two notable
implications are:
■The C2H Compiler assumes that your C code runs successfully on a
Nios II processor system.
■The result of using the C2H Compiler is a program that runs on a
Nios II processor system.
The C2H Compiler works best on C code that adheres to certain
structural rules. It works well for many types of programs, but not all.
Through education and habit, programmers structure C programs with
an existing compiler in mind. Experienced designers learn the particular
structures that produce optimal compiled results. The C2H Compiler is
also a C compiler. It takes ANSI C programs that execute normally on a
processor. However, the program structure for producing optimal
hardware results with the C2H Compiler often differs from code
structured for execution on a processor. You achieve the best results if you
have a reasonable understanding of how the C2H Compiler translates C
structures to hardware. Refer to chapter Chapter 3, C-to-Hardware
Mapping Reference for details.
The C2H Compiler is not a replacement for traditional HDL-based
hardware design. Tasks such as connecting modules together and
interfacing to bus protocols are not easily inferred from ANSI C code. In
the hands of an experienced user, the C2H Compiler allows considerable
control over circuit latency and parallelism. However, it does not provide
the ability to define user logic with complex timing requirements. For
example, the C2H Compiler does not allow you to create an arbitrary
state machine that guarantees a particular operation on a specific clock
cycle.
1The Nios II processor is little-endian. For Nios II compatibility,
C2H accelerators expect to exchange little-endian data with the
processor. If your accelerator must handle big-endian data, you
can swap the byte order in the accelerated C code. Ensure that
the data is in little-endian form when your accelerated function
transfers it to any unaccelerated function.
Altera Corporation 9.11–5
November 2009Nios II C2H Compiler User Guide
C2H Compiler Concepts
fFor information about using the Nios II IDE, refer to the Using the Nios II
fFor information about using C2H on the command line, refer to
C2H Support in Nios II Tool Flows
The Nios II IDE is the preferred tool flow for developing Nios II C2H
programs. The Nios II IDE allows you to carry out the following
important tasks:
■Debug your function prior to accelerating
■Generate the accelerator and incorporate it into your hardware
■Test and profile your software and hardware with the C2H
accelerator
Altera recommends creating new C2H systems with the Nios II IDE.
Integrated Development Environment appendix to the Nios II Software
Developer's Handbook, or to the Nios II IDE help system. For information
about Nios II tool flows, refer to “Development Flows for Creating Nios
II Programs” in the Overview chapter of the Nios II Software Developer's
Handbook.
The Nios II Software Build Tools also provide command-line support for
pre-existing command-line C2H projects.
Chapter 5, Accelerating Code Using the Nios II Software Build Tools
Command Line.
1The Nios II Software Build Tools for Eclipse do not support the
C2H Compiler.
C2H Compiler
Concepts
This section describes fundamental concepts underpinning the
C2H Compiler. These concepts help you better understand how the
C2H Compiler works and how you can produce optimal results.
Simplicity and Ease of Use
The C2H Compiler minimizes interruptions to existing design flows. The
flow to generate a hardware accelerator and link software for it uses the
familiar Nios II and SOPC Builder design tools. When you create a Nios II
software project, you specify which C function (or functions) compiles as
a hardware accelerator rather than instructions on a processor . The
1–6 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Introduction to the C2H Compiler
C2H Compiler calls other tools in the background to handle the hardware
and software integration tasks. Specifically, the C2H Compiler
automatically performs the following tasks in the background:
1.Calls SOPC Builder to specify how the accelerator connects to the
system, and then generates the system hardware.
®
2.Calls the Quartus
and generate an SRAM object file (.sof).
II software to recompile the hardware design
Rapid Design Iteration to Find Optimal Partitioning of
Hardware and Software
The C2H Compiler allows you to move the dividing line between
hardware and software easily in C code, without significant additional
design effort. As a result, you have the freedom to design iteratively, and
explore multiple architectures. By contrast, writing a hardware
accelerator by hand in a hardware description language (HDL) would
require a significant amount of time to create the logic design and
integrate it into the system. Changing the functional or performance
requirements of hand-written HDL blocks can significantly impact
design time.
With the C2H Compiler, you can accelerate as many functions as
necessary to achieve the desired performance. You can balance the tradeoff between performance and resource utilization with simple edits to the
C source.
With these tools available to you, the process of achieving desired system
performance undergoes a profound change: The balance of design time
shifts away from creating, interfacing, and debugging hardware in favor
of perfecting the algorithm implementation and finding the optimal
system architecture.
Accelerate Performance-Critical Sections of Code
The C2H Compiler converts only sections of code that you specify. A
typical program contains a mix of performance-critical code and other
code. Performance-critical sections are often iterative and simple, but
consume the majority of a program's execution time on a processor. They
might occupy the processor by either computing a value, moving data, or
both. The best use of hardware resources is to accelerate only the
performance-critical functions of a program, rather than converting an
entire program to hardware.
Altera Corporation 9.11–7
November 2009Nios II C2H Compiler User Guide
C2H Compiler Concepts
The C2H Compiler Operates at the Function Level
Code you want to accelerate must be expressed as an individual C
function. The C2H Compiler converts all code within and below the
chosen function to a hardware accelerator block. If the function you are
accelerating calls a subfunction, the C2H Compiler also converts the
subfunction to a hardware accelerator. Therefore, you must be careful that
subfunctions are also good candidates for C2H acceleration.
If the code you want to accelerate is not isolated in a separate function, a
good practice is to partition the function to separate the critical section
into its own function. The resulting hardware accelerator then replaces
only processor-intensive tasks, rather than setup or control tasks which
the processor can implement efficiently.
1–8 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
System Architecture
Nios II
Processor
M
Hardware
Accelerator
Data
Memory
S
Arbitrator
Peripherals
S
S
Instruction
M
Data
MM
Control
Arbitrator
S
Instruction
Memory
Avalon
Switch
Fabric
Write Data & Control Path
Read Data
M
S
Avalon Master Port
Avalon Slave Port
MUX
Data
Memory
S
Figure 1–1 shows the architecture of a simple Nios II processor system
that includes one hardware accelerator.
Figure 1–1. Example System Topology with Single Hardware Accelerator
Introduction to the C2H Compiler
SOPC Builder automatically integrates the accelerator logic into the
system as an SOPC Builder component. If there is more than one
accelerator in the system, multiple accelerators appear in SOPC Builder.
Altera Corporation 9.11–9
November 2009Nios II C2H Compiler User Guide
Accelerators are separate from the Nios II processor but can access the
same memory devices that the Nios II processor can.
C2H Compiler Concepts
The accelerator's connections are managed by the C2H Compiler. You can
manually customize the connections using pragma directives in the
accelerated C code. Chapter 6, Pragma Reference, describes
C2H Compiler pragma usage. You cannot edit the accelerator's
connections in the SOPC Builder GUI.
Generation of a Hardware Accelerator
The C2H compilation flow shares commonalities with a conventional C
compiler, but the scheduling of statements, optimization, and object
generation is different. When generating a hardware accelerator, the
C2H Compiler does the following:
1.Runs the GNU GCC preprocessor to evaluate macros, includes, and
other preprocessing directives.
2.Parses code.
3.Creates a graph of data dependencies.
4.Performs some optimizations.
5.Determines the best sequence in which to perform each operation.
6.Generates an object file for the hardware accelerator. This object file
is a synthesizable HDL file.
7.Generates a C wrapper function that isolates and hides the details of
how the Nios II processor interacts with the hardware accelerator.
The wrapper function is a C file that replaces the original C function
at software link time.
The generated accelerator logic includes the following:
■One or more state machines that manage the sequence of operations
defined by the C function. On any clock cycle, an arbitrary number
of computations and memory accesses can happen simultaneously,
orchestrated by the state machines.
■One or more Avalon Memory-Mapped (Avalon-MM) master ports,
which fetch and store data as required by the state machines.
■An Avalon-MM slave port and a set of memory-mapped registers
that allow the processor to set up, start, and stop the accelerator.
1–10 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Introduction to the C2H Compiler
The software wrapper, executing on the Nios II processor, controls the
accelerator by reading and writing the register interface. From the
perspective of the calling function, the result of calling the software
wrapper is functionally the same as calling the original C function. The
basic operation of the software wrapper is as follows:
1.Sets up parameters for the accelerator, similar to passing variables to
the original, unaccelerated function.
2.Optionally flushes the processor's data cache to avoid cache
coherency problems. Flushing the data cache might be necessary if
the accelerator accesses the same memory that the processor does.
3.Starts the accelerator. Once an accelerator is running, it can return a
value, terminate, or run continuously, depending on the design of
the C source code.
4.Polls registers in the accelerator hardware to determine when the
task completes.
5.If the function returns a result, reads the result value, and returns it
to the calling function.
One-to-One Mapping From C Syntax to Hardware Structure
The C2H Compiler maps each element of C syntax to an equivalent
hardware structure using straightforward translation rules that directly
instantiate hardware resources based on the input C code. Once familiar
with the C2H Compiler mappings, you can control the generated
hardware structure with simple changes to your C source.
The following are examples of how the C2H Compiler translates C to
hardware:
■Mathematical operators (such as +, -, *, >>) become direct hardware
equivalent circuits (such as add, subtract, multiply and shift circuits).
These circuits might be shared between operations, depending on
the degree of parallelism inherent in the C code.
■Loops (such as for, while, do-while) become state machines that
iterate over the operations inside the loop, until the loop condition is
exhausted.
■Pointer dereferences and array accesses (such as *p, array[i][j])
become Avalon-MM master ports that access the same memory that
the processor does.
■Statements not dependent on the result of a previous operation are
scheduled as early as possible, allowing parallel execution to the
extent possible.
Altera Corporation 9.11–11
November 2009Nios II C2H Compiler User Guide
C2H Compiler Concepts
■Subfunctions called within an accelerated function are also
converted to hardware using the same C-to-hardware mapping
rules. The C2H Compiler creates only one hardware instance of the
subfunction, regardless of how many times the subfunction is called
within the top-level function. Isolating accelerated C code into a
subfunction provides a method of creating a shared hardware
resource within an accelerator.
The C2H Compiler performs certain optimizations when it can reduce
logic utilization based on resource sharing.
Refer to Chapter 3, C-to-Hardware Mapping Reference for complete
details of the C2H Compiler mappings.
Performance Depends on Memory Access Time
Applications that run on a processor are typically compute-bound, which
means the performance bottleneck depends on the rate the processor
executes instructions. Memory access time affects the execution time, but
instruction and data caches minimize the time the processor waits for
memory accesses.
With C2H hardware accelerators, the performance bottleneck undergoes
a profound change: Applications typically become memory bound,
which means the performance bottleneck depends on the memory
latency and bandwidth. When multiple operations do not have data
dependencies that require them to execute sequentially, the
C2H Compiler schedules them in parallel. The resulting accelerator logic
often must access memory to feed data to each parallel operation. If the
hardware does not have fast access to memory, the hardware stalls
waiting for data, reducing the performance and efficiency.
Achieving maximum performance from a hardware accelerator often
involves examining your system's memory topology and data flow, and
making modifications to reduce or eliminate memory bottlenecks. For
example, if your C code randomly accesses a large buffer of data stored in
slow SDRAM, performance suffers due to constant bank switching in
SDRAM. You can alleviate this bottleneck by first copying blocks of data
to an on-chip RAM, and allowing the accelerator to access this fast, lowlatency RAM. Note that you can also accelerate the copy operation, which
creates a direct memory access (DMA) hardware accelerator.
1–12 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Introduction to the C2H Compiler
C Code
Appropriate for
Hardware
Acceleration
This section describes guidelines for identifying code that is appropriate
for the C2H Compiler.
Ideal Acceleration Candidates
Sections of C code that consume the most CPU time with the least amount
of code are excellent candidates for acceleration. These tend to have the
following characteristics:
■They contain a relatively small and simple loop or set of nested
loops.
■They iterate over a set of data, performing one or more operations on
the data per iteration, and then store the result.
Examples of such iterative tasks include memory copy-and-modify tasks,
checksum calculations, data encryption, decryption, and filtering
operations. In each of these cases, the C code iterates over a set of data
many times, with either one or more memory reads or writes performed
during each iteration.
Example 1–1 demonstrates a routine that performs a checksum
calculation. This code excerpt is from a TCP/IP stack, and it calculates the
checksum over ranges of data in a network protocol stack. Checksum
calculations are typically a time-consuming pa rt of an IP sta ck , b ecause all
data transmitted and received must be validated, which requires the
processor to loop through all bytes.
Altera Corporation 9.11–13
November 2009Nios II C2H Compiler User Guide
C Code Appropriate for Hardware Acceleration
Example 1–1. Checksum Calculation
u16_t standard_chksum(void *dataptr, int len)
{
u32_t acc;
/* Checksum loop: iterate over all data in buffer */
for(acc = 0; len > 1; len -= 2)
{
acc += *(u16_t *)dataptr;
dataptr = (void *)((u16_t *)dataptr + 1);
}
/* Handle odd buffer lengths */
if (len == 1)
{
acc += htons((u16_t)((*(u8_t *)dataptr)&0xff)<< 8);
}
/* Modify result for IP stack needs */
acc = (acc >> 16) + (acc & 0xffffUL);
if ((acc & 0xffff0000) != 0)
{
acc = (acc >> 16) + (acc & 0xffffUL);
}
return (u16_t)acc;
}
Accelerating this function could have a significant impact on execution
time, especially the amount of time spent in the for loop. The remaining
code executes once per call to format the result and check boundary cases.
Accelerating the code outside the loop has little benefit, unless the entire
standard_chksum() function is a called from another function that is
also a good acceleration candidate. The most efficient hardware
accelerator for this code would replace only the for loop. To accelerate
the for loop only, you need to refactor the code to isolate the loop in a
separate function.
Poor Acceleration Candidates
Accelerating some code can have negative performance impacts, or can
unacceptably increase resource utilization, or both. Use the following
guidelines to identify functions not to accelerate:
■Code that contains many data or control dependencies must perform
many sequential operations, and is a poor candidate for acceleration.
A large number of dependencies makes it difficult for the
C2H Compiler to fully optimize loops. Processors are designed to
perform such operations efficiently.
1–14 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Introduction to the C2H Compiler
■If the code contains C syntax not supported by the C2H Compiler, it
cannot be accelerated. Examples are floating point operations and
recursive functions. Refer to Chapter 7, ANSI C Compliance and
Restrictions.
■Code that calls system and runtime library functions is a poor
candidate for acceleration. For example, there is little point in
accelerating printf() or malloc(). The underlying code contains
a complex set of sequential operations and does not contain
performance-critical loops.
■Code that makes extensive use of global or external variables is a
poor candidate for acceleration. Each time the C2H accelerator uses
a global or external variable, it must access the Nios II processor’s
data memory, which is likely to cause a bottleneck.
There are exceptions to these guidelines. For example:
■Experienced C coders often "unroll" iterative algorithms,
representing them as a sequential set of operations to work better
with an optimizing C compiler. If you can refactor the code and "roll
up" the loop, you might be able to create an efficient hardware
accelerator.
■A critical inner loop might have a complex set of sequential
operations which, if accelerated in hardware, consumes a lot of logic
resources. This presents a trade-off: If the processor spends an
unacceptable amount of time in this loop, it might be worth the
hardware cost to accelerate the whole loop.
■Some runtime library functions are iterative in nature. Examples
include common data movement functions and buffer set functions,
such as memcpy() or memset(). If your code calls one of these
functions, you might consider writing a simple, custom
implementation of the function, which you can then accelerate.
■If your cod e uses g lobal or exter na l va riabl es, it mi ght be ea sy for y ou
to refactor it to be suitable for acceleration. Refactor your code to
copy the global or external variables to local storage, perform the
calculation with the local variables, and then copy results back to
global or external storage. The C2H Compiler implements local
variables as fast, pipelined registers inside of the accelerator.
Understanding Code to Find Opportunities for Acceleration
The best way to obtain optimal results with the C2H Compiler is to
understand your code, and know where the critical loops are. If you
wrote the program from scratch, you probably understand where the
critical sections of code are. If you are starting with an existing code base
that you want to accelerate, the C2H Compiler can benefit you to the
Altera Corporation 9.11–15
November 2009Nios II C2H Compiler User Guide
Next Steps
extent that you analyze the code and understand it. In either case, the
Nios II IDE profiling features can help you determine where the
processor spends most of its time.
Examine the structure of the code for processor-specific or compilerspecific optimizations written into the structure of the code. These
sections of code might result in poor performance with the
C2H Compiler, and could benefit from refactoring for the C2H Compiler.
It can be difficult to identify the critical loop just by inspecting code,
because programs often spend the majority of time iterating on just a few
lines of code. The only way to know exactly where the processor spends
the most time is to profile the application, and inspect the bottleneck
functions.
fRefer to AN 391: Profiling Nios II Systems for further information.
Next Steps
Now that you understand the underlying concepts of the Nios II
C-to-Hardware Acceleration Compiler, you are ready for hands-on
experience accelerating designs. Chapter 2, Getting Started
Tu to ri a ldescribes the C2H Compiler design flow, and gives step-by-step
instructions to accelerate your first design. Altera also provides tutorials
and application notes to deepen your understanding of the
C2H Compiler.
fRefer to the Nios II literature page for further C2H Compiler
1–16 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
2. Getting Started Tutorial
Introduction
fFor introductory information on designing with the Nios II processor,
C2H Compiler
Design Flow
This chapter describes the design flow for the Nios® II C-to-Hardware
Acceleration (C2H) Compiler. This chapter provides a design example
and gives you a step-by-step tutorial to guide you through the process of
creating your first hardware accelerator.
The example software design performs multiple iterations of a data-copy
function. By accelerating the data-copy function, you achieve more than
a 10-fold improvement in the execution performance. The resulting
hardware accelerator resembles a hardware block with direct memory
access (DMA) to copy data without processor intervention.
This tutorial assumes that you are familiar with the Nios II processor and
the Nios II design flow.
refer to the Nios II Hardware Development Tutorial available on the Altera
Nios II literature page at http://www.altera.com/literature/lit-nio2.jsp,
and to the Nios II Software Development Tutorial available in the Nios II
integrated development environment (IDE) help system.
This section discusses the design flow to create a hardware accelerator
with the C2H Compiler.
Starting Point for the C2H Compiler Design Flow
The design flow for the C2H Compiler starts with one or more C files that
compile successfully targeting the Nios II processor. Before you accelerate
a function with the C2H Compiler, you must:
■Identify the functions that require acceleration.
■Debug the functions first targeting the Nios II processor. After
accelerating a function, you can no longer debug individual C
statements within the function.
You might have existing C code that you need to accelerate to improve
performance. Alternatively, you might develop and debug a function in
C with the explicit purpose of converting it to hardware. In either case,
you achieve the best results if the C code is structured for the
C2H Compiler. To start with, you can accelerate your code as-is, and
determine if the results meet the design requirements.
Altera Corporation 9.12–1
November 2009
Typical Design Flow
Typical Design
Flow
A typical design flow using the C2H Compiler to accelerate a function
involves the following steps:
1.Develop and debug your application or algorithm in C targeting a
Nios II processor system.
2.Profile the code to identify the areas that would benefit from
hardware acceleration.
3.Isolate the code you want to accelerate into an individual C
function.
4.Specify the function you want to accelerate in the Nios II IDE.
5.Rebuild the project in the Nios II IDE.
6.Profile the results in hardware, or observe estimates from the C2H
report in the Nios II IDE.
7.If the results do not meet the design requirements, modify the C
source code and system architecture (for example, the memory
topology).
8.Return to Step 5, and iterate.
The typical C2H Compiler design flow is an iterative process of
accelerating a function, comparing the performance to design
requirements, and modifying C code to improve results. If you start with
C code that is not optimized for the C2H Compiler, the first iteration of
acceleration might not dramatically improve performance. Further
iterations, modifying the C code for optimal hardware structure, often
improve the final results significantly over the first pass results.
fThis tutorial does not describe techniques for optimizing hardware
accelerator performance. For further information on optimizing
C2H Compiler results, refer to the Accelerating Nios II Systems with the
C2H Compiler Tutorial.
Software Requirements
The C2H Compiler in evaluation mode is installed as part of the Altera®
Quartus® II Complete Design Suite. You can download the Quartus II
Complete Design Suite free from the Altera website. Visit
www.altera.com and click Download.
2–2 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Getting Started Tutorial
During the design process with the C2H Compiler, you use the following
tools:
■Nios II Integrated Development Environment (IDE) – You control
acceleration options for individual functions in the Nios II IDE. The
results of accelerating functions are reported in the Nios II IDE. The
output is an executable linking file (.elf) targeting a Nios II CPU. The
C2H Compiler also invokes SOPC Builder and optionally the
Quartus II software in the background to regenerate the Nios II
system and update the SRAM object file (.sof).
■SOPC Builder – SOPC Builder manages the generation of C2H logic
and Avalon-MM system interconnect fabric to connect hardware
accelerators to the processor. During the software build process, the
Nios II IDE can invoke SOPC Builder in the background to update
the hardware accelerators when necessary and integrate them into
the Nios II hardware design. The output is a set of hardware
description language (HDL) files (.v or .vhd) and an SOPC Builder
system file (.sopcinfo) defining your system: Nios II processor cores,
peripherals, accelerators, on-chip memory, and interfaces to off-chip
memory.
■Quartus II software – The Quartus II software compiles and
synthesizes HDL produced by the C2H Compiler and SOPC Builder
tools, along with any other custom logic in your Quartus II project.
During the software build process, the Nios II IDE can invoke the
Quartus II software in the background to recompile the Quartus II
project. The output is a .sof file that includes the updated Nios II
system with accelerators.
OpenCore Plus Evaluation
Hardware accelerator blocks generated by the C2H Compiler support
OpenCore®Plus evaluation. OpenCore Plus evaluation allows you to use
the C2H Compiler and evaluate the performance of hardware
accelerators in real systems before purchasing a license for the tool. With
Altera's free OpenCore Plus evaluation feature, you can:
■Verify the functionality of your design, as well as evaluate its size
and speed easily
■Generate time-limited device programming files for designs that
include megafunctions
■Program an FPGA and verify your design in hardware
■Simulate the behavior of an accelerator in your system
Altera Corporation 9.12–3
November 2009Nios II C2H Compiler User Guide
Tutorial
OpenCore Plus hardware evaluation supports the tethered mode of
operation for C2H. In tethered mode the accelerator runs indefinitely, as
long as the target board remains connected to the host computer by an
Altera download cable
You need to purchase a license for the Nios II C-to-Hardware
Acceleration Compiler only when you are completely satisfied with the
functionality and performance of your accelerated Nios II system, and
want to take your design to production.
fFor more information on OpenCore Plus hardware evaluation, see
AN 320: OpenCore Plus Evaluation of Megafunctions.
Tutorial
This section guides you through the steps to accelerate a function using
the C2H Compiler. You create a new software project in the Nios II IDE
using the provided example design files, accelerate a function, and
observe the performance improvement.
This tutorial guides you through the steps to implement the example
design. These steps start with a C source file and end with a running
application that includes an accelerated function. The steps you perform
are described in the following sections:
1.“Set up the Hardware for the Project” on page 2–5
2.“Create the Software Project” on page 2–6
3.“Run the Project as Software Only” on page 2–7
4.“Create and Configure a Hardware Accelerator” on page 2–8
5.“Rebuild the Project” on page 2–10
6.“Observe Results in the Report File” on page 2–11
7.“Observe the Accelerator in SOPC Builder” on page 2–14
Tutorial Design
The hardware design for this tutorial is based on the standard hardware
example design provided with the Nios II EDS. The software design is a
C file named dma_c2h_tutorial.c, which is available for download from
the Altera website. You can run the tutorial design on any Nios
development board available from Altera.
2–4 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Getting Started Tutorial
fYou can download dma_c2h_tutorial.c from the Nios II literature page.
The file is located next to this document (Nios II C2H Compiler User
Guide) on the Altera Nios II literature page at http://www.altera.com/
literature/lit-nio2.jsp.
The file dma_c2h_tutorial.c includes two functions:
■do_dma() – This is the function you accelerate. It performs a block
memory copy. do_dma() takes a source address pointer, a
destination address pointer, and an integer number of bytes to copy.
When implemented in hardware, do_dma() resembles DMA copy
logic. The prototype for do_dma() is as follows:
int do_dma( int * __restrict__ dest_ptr,
int * __restrict__ source_ptr, int length )
The __restrict__ qualifier informs the compiler that the
pointers dest_ptr and source_ptr point to mutually exclusive
address ranges. For further information about the __restrict__
qualifier, see “Pointer Aliasing” on page 3–32 of Chapter 3, C-to-
Hardware Mapping Reference.
■main() – main() calls do_dma() and measures the amount of time
taken, so that you can compare the software implementation with the
hardware accelerator.
main() performs the following actions:
1.Allocates two 1 MB buffers in main memory
2.Fills the source buffer with incrementing values
3.Fills destination buffer with all 0x0.
4.Calls the do_dma() function 100 times
5.Checks the copied data to ensure there were no errors
6.Frees the two allocated buffers
To measure the time it takes for the copy operations to complete, there are
timer routines around the loop that calls the do_dma() function. After
the application runs, the number of milliseconds that were spent
performing the copy operations is displayed to the Console view in the
Nios II IDE.
Set up the Hardware for the Project
To set up the hardware for the tutorial, perform the following steps:
1.Connect your Nios development board to power, and connect the
board to your host computer with an Altera download cable.
Altera Corporation 9.12–5
November 2009Nios II C2H Compiler User Guide
Tutorial
2.Set up the hardware project directory
a.Using a file management tool on the host computer, locate the
standard hardware example design for your Nios development
board. For example, on a Windows PC, use Windows Explorer
to find the Verilog HDL design files for the Nios development
®
board, Cyclone
II Edition at <Nios II EDS install path>/
examples/verilog/ niosII_cycloneII_2c35/standard.
b.Copy the standard directory and name the copied directory
c2h_tutorial_hw. This new directory serves as the hardware
design for the tutorial.
3.Start the Quartus II software.
4.Open the Quartus II project standard.qpf located in the
c2h_tutorial_hw directory.
The Quartus II software might give a warning "Do you want to
overwrite the database ... created by Quartus II Version <version>...
The database format is compatible..." if the project was created with
an earlier version of the software. If so, click Yes to update the
database.
5.Configure the FPGA on the Nios development board.
a.On the Tools menu click Programmer. The Programmer
appears, with the SRAM object file standard.sof automatically
ready to download to the FPGA.
b.Turn on the Program/Configure check box for standard.sof.
c.Click Start. The programmer downloads the configuration data
to the FPGA.
1If Start is not enabled, click Hardware Setup to configure
your JTAG download cable.
Create the Software Project
To set up the software project for the tutorial, perform the following steps.
1.Start the Nios II IDE.
2.If the Workspace Launcher dialog box appears, click OK to accept
the default workspace.
2–6 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Getting Started Tutorial
3.If the Welcome to the Altera Nios II IDE page displays, close it to
view the workbench.
4.Create a new C/C++ Application project.
a.On the File menu, point to New and click C/C++ Application.
The New Project wizard appears.
b.In the Name box, type c2h_tutorial_sw.
c.In the Select Project Template list, select Blank Project.
d.Use the Select Target Hardware settings to browse to and select
the SOPC Builder system (.ptf) file in your c2h_tutorial_hw
directory. After you specify the SOPC Builder system, the IDE
automatically sets the CPU setting to cpu, which is the name of
the only Nios II processor core available in this SOPC Builder
system.
e.Click Finish. The IDE generates a new project c2h_tutorial_sw
and a new system library project c2h_tutorial_sw_syslib.
5.Download the software file dma_c2h_tutorial.c from the Nios II
literature page and save it to a known location on your host
computer. The file is located next to this document (Nios II C2H Compiler User Guide) on the Altera Nios II literature page at http://
www.altera.com/literature/lit-nio2.jsp.
6.Import the C file dma_c2h_tutorial.c into the c2h_tutorial_sw
project. The easiest way to do this is to use an external file
management tool, such as Windows Explorer, and drag the file onto
the c2h_tutorial_sw project folder in the C/C++ Projects view of the
Nios II IDE.
Run the Project as Software Only
In this section, you build and run the project as a software-only
implementation, and observe the time required to run the program. To
run the program, perform the following steps:
1.In the C/C++ Projects view, right-click the c2h_tutorial_sw project,
point to Run As and click Nios II Hardware. The Nios II IDE takes a
few minutes to build and run the program.
Altera Corporation 9.12–7
November 2009Nios II C2H Compiler User Guide
Tutorial
2.Observe the execution time in the Console view. Example 2–1 shows
results of approximately 86000 milliseconds. The results you see
might be different, depending on the memory characteristics of the
target board and the clock speed of the example design.
Example 2–1. Execution Results as Software-Only Implementation
This simple program copies 1048576 bytes of data from a source buffer to a
destination buffer.
The program performs 100 iterations of the copy operation, and calculates
the time spent.
Copy beginning
SUCCESS: Source and destination data match. Copy verified.
Total time: 86520 ms
Create and Configure a Hardware Accelerator
In this section, you create an accelerator for the do_dma() function. To
create the hardware accelerator, perform the following steps:
1.Open the dma_c2h_tutorial.c source file in the Nios II IDE editor, if
it is not already open.
2.In the source file, double-click the name of the do_dma() function
to select it.
3.Right-click do_dma and click Accelerate with the Nios II C2H Compiler. The C2H view appears in the bottom pane of Nios II
IDE.
1In this example, for simplicity, the do_dma() function exists in
the same file as the rest of the application code. However, a good
practice is to isolate functions for acceleration into a separate C
file. The project makefile cannot determine specifically what
part of a file has changed. As a result, if an accelerated function
coexists in the same file with other unaccelerated code, the
C2H Compiler is forced to rebuild the accelerator, even if you
edit unrelated code.
4.Set the build options for the new accelerator, as shown in Figure 2–1.
a.Click the + icon to expand c2h_tutorial_sw in the C2H view.
2–8 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Getting Started Tutorial
b.Turn on Build software, generate SOPC Builder system, and
run Quartus II compilation. When you build the project in the
Nios II IDE, this option causes the C2H Compiler to invoke
SOPC Builder and the Quartus II software in the background to
generate a new .sof file.
1Quartus II compilations can take a long time. You only need
to turn on this option when you want to update the .sof file.
You must regenerate the .sof file after you make changes
that affect one or more hardware accelerators, and you want
to run a program on the hardware system.
c.Expand do_dma() in the C2H view.
d.Under do_dma(), select Use hardware accelerator in place of
software implementation. Flush data cache before each call.
At run time, this option causes the program to activate the
accelerator hardware for do_dma(). With this option, the C2H
wrapper function flushes the processor data cache before
activating the accelerator.
1The wrapper function needs to flush the data cache before
activating the hardware accelerator if the processor has a
data cache and if the processor writes to the same memory
that the accelerated function operates on. Failing to flush
the cache might result in cache coherency problems.
Figure 2–1. Setting the Build Options for the Accelerator
Altera Corporation 9.12–9
November 2009Nios II C2H Compiler User Guide
Tutorial
Rebuild the Project
To rebuild the project, perform the following step:
v In the C/C++ Projects view, right-click c2h_tutorial_sw and click
Build Project.
1The rebuild process can take over 20 minutes, depending on
your computer's performance and the target FPGA.
In the background, the Nios II IDE performs the following tasks:
1.Launches the C2H Compiler to analyze the do_dma() function,
generates the hardware accelerator, and generates the C wrapper
function.
2.Invokes SOPC Builder to connect the accelerator into the SOPC
Builder system. The build process modifies the SOPC Builder
system file (.ptf) in the Quartus II project directory to include the
new accelerator as a component in the system.
3.Invokes the Quartus II software to compile the hardware project and
regenerate the .sof file.
4.Rebuilds the C/C++ application project and links the accelerator
wrapper function into the application.
Progress messages display in the Console view. The build process creates
the following files:
■accelerator_c2h_tutorial_sw_do_dma.v (or .vhd) – This file is the
HDL code for the accelerated function. It is stored in the Quartus II
project directory, and the name follows the format accelerator_<IDE project name>_<function name>. This file is not visible in the Nios II
IDE.
■alt_c2h_do_dma.c – This file is the C2H accelerator driver file,
containing the wrapper function for the accelerator. It is located in
software project's Debug or Release directory, and the name follows
the convention alt_c2h_<function name>.c. (If you use the Nios II
software build tools, these files are located in your software
application directory.)
■c2h_accelerator_base_addresses.h — This is the C2H accelerator
base addresses header file. It is located in the same directory as
alt_c2h_<function name>.c.
2–10 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Getting Started Tutorial
1If you copy or move a C2H project to a different directory, you
must make sure you have the generated C source files and C2H
makefile fragments in the new location. If you regenerate your
accelerator in the new location, the C2H Compiler recreates
these files for you. This is the simplest way, although not the
fastest, to ensure that you have these files.
If you want to avoid regenerating, simply copy or move the two
files when you copy or move your original C source files. Copy
or move the files, alt_c2h_<function_name>.c and c2h_accelerator_base_addresses.h, to the subdirectory in the
new project location corresponding to their original location.
Observe Results in the Report File
The C2H Compiler produces a detailed build report in the C2H view. The
build report contains information about accelerator performance and
resource utilization, which you can use to optimize your C code for the
C2H Compiler. This section introduces the main features of the report file.
Inspect the report by performing the following steps:
1.Click the C2H view in the Nios II IDE. You can double click the C2H
tab to view the report in full-screen mode.
2.In the C2H view, expand c2h_tutorial_sw, do_dma(), Build report.
1For designs with multiple accelerators, a build report appears
under each function listed in the C2H view.
3.Expand the Glossary section. This section defines the terminology
used in the rest of the report.
Altera Corporation 9.12–11
November 2009Nios II C2H Compiler User Guide
Tutorial
4.Expand the Resources section and all subsections, as shown in
Figure 2–2.
Figure 2–2. Resource Section of the C2H Build Report
The Resources section lists all the master ports on the hardware
accelerator. Each master port corresponds to a pointer dereference in the
source code. In this example, there are two master ports: one for
dereferencing the read pointer, *source_ptr, and one for dereferencing
the write pointer, *dest_ptr.
2–12 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
5.Expand the Performance section and all subsections, as shown in
Figure 2–3.
Figure 2–3. Performance Section of the C2H Build Report
Getting Started Tutorial
The Performance section shows the performance characteristics of each
loop in the accelerated function. There are two metrics that determine a
loop's performance: loop latency and cycles per loop-iteration (CPLI).
Loop latency is the number of cycles required to fill the pipeline. CPLI is
the number of cycles required to complete one iteration of the loop,
assuming the pipeline is filled and no stalls occur. For example, consider
the case of an accelerated function with one loop with loop latency of 13
and CPLI value of 1. (These values can differ, depending on the memory
latency on your target board.) These numbers indicate that the pipeline
takes 13 cycles to fill; once the pipeline is filled, the pipeline generates a
new result every cycle.
1In general, the goal of optimizing an application for better
accelerator performance is to reduce loop latency and CPLI.
Altera Corporation 9.12–13
November 2009Nios II C2H Compiler User Guide
Tutorial
fFor further information on optimizing C2H Compiler results, refer to the
Accelerating Nios II Systems with the C2H Compiler Tutorial.
Observe the Accelerator in SOPC Builder
After the C2H Compiler adds the hardware accelerator to your SOPC
Builder system, the accelerator appears in SOPC Builder.
To look at the newly-added accelerator in your SOPC Builder system,
perform the following steps:
1.Return to the Quartus II window.
2.On the Tools menu click SOPC Builder... to open SOPC Builder.
3.On the System Contents tab in SOPC Builder, notice the new
component accelerator_c2h_tutorial_sw_do_dma, located at the
bottom of the table of active components.
4.Close SOPC Builder.
1You cannot modify the accelerator from within SOPC Builder.
You must use the Nios II IDE interface to remove or alter it.
cClose SOPC Builder while building projects in the Nios II IDE
with the C2H Compiler. The C2H Compiler modifies the SOPC
Builder system in the background. If SOPC Builder is open
while you build a Nios II IDE project with C2H accelerators, the
system displayed in the SOPC Builder window can become outof-date.
If you inadvertently leave SOPC Builder open while building an
accelerator with the C2H Compiler, be sure to close the SOPC
Builder system file without saving it. If you save the out-of-date
file, you overwrite your accelerator-enhanced system file.
Run the Project with the Accelerator
You are now ready to run the accelerated project. Perform the following
steps:
1.Return to the Quartus II window, if it is not already open.
2.Configure the FPGA with the new .sof file that contains the
accelerator.
2–14 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Getting Started Tutorial
a.If the Programmer window is not still open, on the Tools menu
click Programmer. The Programmer window lists the file
standard.sof.
b.Turn on the Program/Configure box for standard.sof.
1If you don't have a license for the C2H Compiler, the Quartus II
software generates a time-limited .sof file with a different name.
In this case, select standard.sof and click Delete, then click Add
and open the time-limited .sof file.
c.Click Start. The programmer downloads the new configuration
data to the FPGA.
3.Return to the Nios II IDE window.
4.In the C/C++ Projects view, right-click the c2h_tutorial_sw project,
point to Run As and click Nios II Hardware. The Nios II IDE
downloads the accelerated program to the board and runs it.
5.Observe the execution time in the Console view. Example 2–2 shows
timing results of approximately 8470 milliseconds. The results you
see might be different, depending on your target board.
Example 2–2. Execution Results with Hardware Acceleration
This simple program copies 1048576 bytes of data from a source buffer to a
destination buffer.
The program performs 100 iterations of the copy operation, and calculates
the time spent.
Copy beginning
SUCCESS: Source and destination data match. Copy verified.
Total time: 8470 ms
Remove the Accelerator
You can remove an accelerator from a design by performing the following
steps in the Nios II IDE.
1.Right-click the function name in the C2H view and click Remove C2H Accelerator, as shown in Figure 2–4.
Altera Corporation 9.12–15
November 2009Nios II C2H Compiler User Guide
Next Steps
2.Rebuild the project in the Nios II IDE.
1You must rebuild the project to remove the hardware
accelerator from the SOPC Builder system hardware.
Figure 2–4. Removing a C2H Accelerator
Removing the accelerator removes the hardware accelerator component
from the SOPC Builder project, and replaces the C2H software wrapper
with the original, unaccelerated function. The next time you build the
project in the Nios II IDE, the C2H Compiler regenerates the SOPC
Builder system and recompiles the Quartus II project to generate a .sof
file without the accelerator hardware.
1To remove an accelerator from the hardware system, you must
use the Remove C2H Accelerator command in the Nios II IDE.
Do not use SOPC Builder to manually delete the component
from the system. If you delete the component from the SOPC
Builder system using the SOPC Builder GUI, the C2H Compiler
produces undefined results the next time you build the software
project.
Next Steps
2–16 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Congratulations! You have successfully converted an ANSI C function to
a hardware accelerator using the C2H Compiler and observed a
significant performance increase.
After accelerating a function and running it for the first time, your next
steps vary depending on your system requirements. If your starting goal
is to off-load a routine from the processor to reduce CPU load, you might
find that no additional action is required. If the hardware accelerator does
not meet performance or resource requirements, you can perform one or
Getting Started Tutorial
more iterations of optimization to produce better results. In either case,
you can continue developing your system software and hardware, and
the accelerator remains in place.
1It is common to be able to improve first-pass performance
results significantly by optimizing the C code and system
architecture.
If you modify the accelerated C code, the Nios II IDE automatically
regenerates the accelerator hardware with the C2H Compiler the next
time you build the C/C++ application project. Alternatively, you can
disable an accelerator after it is built and relink the original software
implementation, while leaving the hardware accelerator inactive in the
hardware.
fTo get a better understanding of how the C2H Compiler translates C to
hardware, read Chapter 3, C-to-Hardware Mapping Reference. After
that, for further information on optimizing C2H Compiler results, refer
to the Accelerating Nios II Systems with the C2H Compiler Tutorial.
Altera Corporation 9.12–17
November 2009Nios II C2H Compiler User Guide
Next Steps
2–18 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
3. C-to-Hardware Mapping
Reference
This chapter describes how the Nios® II C-to-Hardware Acceleration
(C2H) Compiler translates ANSI C constructs into functional blocks in a
hardware accelerator. Understanding the C-to-hardware mappings
enables you to write C functions optimized for the C2H Compiler to
achieve higher performance and lower resource utilization.
One-to-One
C-to-Hardware
Mapping
The C2H Compiler translates each element of C syntax to an equivalent
hardware structure using straightforward mapping rules. The mapping
rules provide a one-to-one association between elements of C syntax and
their equivalent hardware structures. By learning the C-to-hardware
mappings, you can control the hardware structure of an accelerator, based
on the structure of the C code.
The C2H Compiler can perform resource-sharing optimizations which
reduce the resource utilization for an accelerator. In these cases, the result
is a better than one-to-one mapping.
Arithmetic and Logical Operators
Every arithmetic and logical operator in the C code translates to a
corresponding hardware block in the accelerator. Consider the function
MAC() shown in Example 3–1.
Example 3–1. Function with Arithmetic and Logical Operators
long long MAC (int* a, int* b, int len)
{
long long result = 0;
while (len > 0)
{
result += *a++ * *b++;
len--;
}
return result;
}
Altera Corporation 9.13–1
November 2009
One-to-One C-to-Hardware Mapping
Table 3–1 lists the equivalent hardware structures resulting from this
function.
Table 3–1. Hardware Structure for Arithmetic and Logical Operators
while (len > 0) {while
result += *a++ * *b++;+=
len--;--
LineC ElementHardware Structure
Finite state machine with
nominal control logic. Refer to
section “Iteration Statements” on
page 3–5.
>
* (pointer)
++
* (multiply)
32-bit comparator
64-bit adder
Avalon-MM master port to read
data (two total for
*b++). Refer to section
“Indirection Operator (Pointer
Dereference)” on page 3–16.
32-bit up-counter (two total for
*a++ and *b++)
32x32=64-bit multiplier
32-bit down-counter
*a++ and
Assignments
A C assignment operator stores the value of an expression to a variable.
As a general rule, every assignment operator in the C code, such as =,
translates to a registered signal in hardware. The value of an assignment's
expression is calculated in one clock cycle. Figure 3–1 shows the
hardware that results from the following statement:
int sum = x + y;
Figure 3–1. Hardware Resulting from Assignment
3–2 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
C-to-Hardware Mapping Reference
There are two types of exceptions to this rule:
■Assignments that require zero logic elements in hardware
■Assignments that use multiple registers to pipeline complex
arithmetic operations
The following sections discuss these exceptions.
Unregistered Operations and Assignments
Certain logical and bit-wise operations involving constants are trivial and
require no logic. In hardware, they are performed simply by
manipulating wires. Table 3–2 lists the applicable operators and
conditions. If an assignment consists solely of such operations, then its
result is not registered.
Table 3–2. Operators That Can Result in Unregistered Assignments
OperatorDescriptionRequired Condition
>>
<<
&
|
^
~
)
Right bit-wise shiftRight-hand side is constant
Left bit-wise shiftRight-hand side is constant
bit-wise ANDEither operand is constant
bit-wise inclusive OREither operand is constant
bit-wise exclusive OREither operand is constant
bit-wise inversionRight-hand side is unregistered
Type castRight-hand side is unregistered
The following assignment is an example of a zero logic-element
operation.
int masked_data = data_in & 0x000fffff;
The C2H Compiler generates no register for the variable masked_data,
because its value is represented simply by concatenating 12 bits of zeroes
with the lower 20 bits of data_in.
Additional examples of unregistered assignments:
shift_by_constant = data_in << 3;
or_with_constant = data_in | 0xf0f0f0f0;
invert_shift_and_consts = (~data_in & 0xff) << 8;
Altera Corporation 9.13–3
November 2009Nios II C2H Compiler User Guide
One-to-One C-to-Hardware Mapping
Pipelined Operations and Assignments
The C2H Compiler always registers the results of the operators listed in
Table 3–3. Some arithmetic operations, such as multiplication, use a large
amount of logic, which creates a significant propagation delay through
the circuit. Calculating these operations in series with other operations in
a single clock cycle would incur unacceptable propagation delays and
significantly reduce the maximum achievable clock frequency (f
the system. The C2H Compiler pipelines these operations by giving each
its own registered assignment. There are exceptions for cases in which an
operation reduces to trivial logic, as listed in Table 3–3
Table 3–3. Complex Arithmetic Operations Pipelined by the C2H Compiler
OperatorDescriptionExceptions
>>
<<
) for
MAX
*
/
%
MultiplicationEither operand is a constant power of
2, which reduces to left-shift operation
DivisionRight-hand operand is a constant
power of 2, which reduces to a rightshift operation
ModulusRight-hand operand is a constant
power of 2, which reduces to a
masking operation
Right bit-wise shiftRight-hand side is constant
Left bit-wise shiftRight-hand side is constant
The general rule "one registered assignment for every = operator" can be
amended to read, "one registered assignment for every = operator or
complex arithmetic operator".
3–4 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Figure 3–1 shows the hardware that results from the following statement:
int foo = a * b + x;
Figure 3–2. Pipelined Multiplication Operator
Figure 3–2 shows the hardware that results from the following statement:
int bar = a * b * c + x;
Figure 3–3. Two Stages of Pipelined Multiplication Operators
C-to-Hardware Mapping Reference
Iteration Statements
An iteration statement (do, for, or while), also known as a loop
statement, translates to a finite state machine in hardware. The state
machine controls execution of all the statements inside the loop block. A
loop inhibits (stalls) its parent state machine. In other words, if a loop
exists within an outer loop, the state machine for the outer loop stalls each
iteration and waits for the inner loop state machine to complete.
Altera Corporation 9.13–5
November 2009Nios II C2H Compiler User Guide
One-to-One C-to-Hardware Mapping
The fundamental iteration statement for the C2H Compiler is the do loop,
which evaluates its condition at the end of each iteration. See section
“Loop Pipelining” on page 3–42 for information about loop state
machines and scheduling.
Selection Statements
A selection statement (if-else, case, switch, and ?:) translates to a
multiplexer in hardware. The structure of the hardware depends on the
type of statement, as described in the following sections.
if Statement
An if-else statement translates to three elements in hardware:
■Logic to perform all operations in the then block
■Logic to perform all operations in the else block
■Selection logic that determines which result to use
The results of each element are registered, and the registered signals feed
a multiplexer.
If the if statement has both a then and an else block, the operations for
both blocks execute in parallel. When all operations have completed, the
multiplexer selects which value propagates to subsequent statements,
based on the value of the control expression.
Figure 3–4 shows the circuit that results from the code in Example 3–2.
Example 3–2. If-else Logic
if (foo > bar)
foo += bar;
else
foo *= bar;
3–6 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Figure 3–4. if-else Logic
C-to-Hardware Mapping Reference
If the if statement has only a then or only an else block, the resulting
logic is a simplification of the if-else case. The multiplexer selects
whether to propagate the result of the if block or the initial values from
before the if statement.
Figure 3–5 shows the circuit that results from the code in Example 3–3.
Example 3–3. if Logic Without else
if (foo > bar)
foo += bar;
Altera Corporation 9.13–7
November 2009Nios II C2H Compiler User Guide
One-to-One C-to-Hardware Mapping
Figure 3–5. if Logic Without else
Conditional Operator ?:
The ?: (conditional) operator is functionally equivalent to the if-else
statement, but the placement of registers is different. The condition logic
and selection logic compute in the same clock cycle, and the result is
registered.
3–8 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Figure 3–6. if-else Logic
C-to-Hardware Mapping Reference
Figure 3–6 shows the circuit that results from the following code:
foo = (foo > bar) ? (foo + bar) : (foo * bar);
switch and case Statements
The C2H Compiler converts switch statements to functionallyequivalent nested if-else statements, and then translates the if-else
statements to hardware, as described in section “if Statement” on
page 3–6.
Altera Corporation 9.13–9
November 2009Nios II C2H Compiler User Guide
One-to-One C-to-Hardware Mapping
Table 3–4 shows an example of a switch statement converted to
equivalent if-else statements.
Table 3–4. switch Statement Converted to if-else Statements
switch (byte_select)
{
case 1:
out = in & 0x0000ff00;
break;
case 2:
out = in & 0x00ff0000;
break;
case 3:
out = in & 0xff000000;
break;
default:
out = in & 0x000000ff;
}
Figure 3–7 shows the logic that results of translating the if-else code
from Table 3–4.
switch Implementationif-else Implementation
if (byte_select == 1)
out = in & 0x0000ff00;
else
if (byte_select == 2)
out = in & 0x00ff0000;
else
if (byte_select == 3)
out = in & 0xff000000;
else
out = in & 0x000000ff;
3–10 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Figure 3–7. switch Logic
C-to-Hardware Mapping Reference
Subfunction Calls
A subfunction is a C function called from within an accelerated function.
The C2H Compiler translates subfunctions to hardware using the same
mapping rules as for the top-level function. The resulting HDL module
for the accelerated subfunction becomes a submodule of the top-level
function, as illustrated in Figure 3–8 on page 3–12.
The C2H Compiler translates the top-level function and all subfunctions
to a single hardware accelerator. The C2H Compiler creates only one
instance of the subfunction hardware logic, regardless of how many times
the subfunction is called within the top-level function. If the calling
function calls the subfunction multiple times, the subfunction logic
becomes a shared resource. However, the subfunction is a private
resource exclusive to the calling function. In other words, if multiple
separate, accelerated functions call a common subfunction, the
C2H Compiler creates separate instances of the subfunction logic.
Altera Corporation 9.13–11
November 2009Nios II C2H Compiler User Guide
One-to-One C-to-Hardware Mapping
Table 3–5 shows an example of a subfunction called by two different
functions. In this case, functions foo() and bar() both call a
subfunction foobar_sub().
The C2H Compiler does not support external subfunctions. You must
locate the subfunction in the same source file as the accelerated function.
This is because, unlike the #include construct, a C external function
reference requires the presence of a linker. The C2H Compiler has no
linker.
3–12 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
C-to-Hardware Mapping Reference
The Nios II C2H Compiler does not perform any type of inline
substitution. It ignores the inline function specifier. You can achieve the
effect of an inline function through the use of preprocessing macros. If
you wish to call a function for which accelerated hardware is replicated
for each call, then you must define a macro containing the logic for this
function. Before parsing the accelerated code, the C2H Compiler calls the
GNU GCC preprocessor, which evaluates the macro and replaces each
macro call with your macro definition.
Macros and Preprocessing Directives
Any preprocessing directives in your code are processed before
translation to hardware. In order to ensure identical interpretation
between software and hardware, the C2H Compiler operates on the
output of the nios2-elf-gcc preprocessor.
Variable
This section describes how the C2H Compiler translates variable
declarations and other C elements that define data storage.
Declarations
Local vs. Non-Local Variables
The C2H Compiler treats variables differently, depending on the scope of
the variable. In general, local variables translate to memory elements
inside the accelerator hardware; non-local variables translate to AvalonMM master ports capable of accessing memory outside of the accelerator.
For the purposes of this document, any variable declared within the scope
of an accelerated function is considered to be local. Example 3–4
illustrates locality of variables to a function mac(). In this example, only
int my_global is not local, and all other variables are local.
Example 3–4. Local vs. Non-Local Variables
int my_global; // my_global is not local to mac().
int mac(int *src, int *dst, int len)
// src, dst, and len are local to mac().
{
int res = 0; // res is local to mac().
while (len--)
{
int product = (*src++) * (*dst++); // product is local to mac().
res += product;
}
return res;
}
Altera Corporation 9.13–13
November 2009Nios II C2H Compiler User Guide
Variable Declarations
Scalar Variables
For local scalar variables, the C2H Compiler creates hardware registers
inside the accelerator. For example, declaring a char creates an 8-bit
register; declaring a short int creates a 16-bit register, and so forth.
Declaring a pointer allocates only storage for the pointer itself. For
example, declaring a char* creates a 32-bit register to store the address
of a char. If you use a scalar variable within a pipelined loop, then its
register is replicated for pipelining as needed.
The C2H Compiler considers a variable to be scalar if it is not an array,
structure, or union. Example 3–5 demonstrates some examples of scalar
variables.
Example 3–5. Scalar Variables
int i;
int *p;
char **c;
struct struct_type *pointer_to_struct;
int (*pointer_to_array)[8];
Example 3–6 demonstrates some examples of nonscalar variables.
Example 3–6. Nonscalar Variables
int data[1024];
struct struct_type tx_rec;
int *array_of_pointers[8];
Arrays, Structures, and Unions
For local arrays, structures, and unions, the C2H Compiler creates
memory elements inside the accelerator. Depending on the size of the
memory required and the target device family, the C2H Compiler can
implement these memory elements either as logic elements or embedded
memory blocks.
The memory elements are single-ported. As a result, a given array, union,
or structure has a maximum bandwidth of one transaction per cycle, even
if the C code could be structured to allow parallel scheduling of accesses.
1The following constructs are declared in the driver file (and
therefore in the processor's data memory), and passed into the
accelerator by reference:
3–14 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
C-to-Hardware Mapping Reference
•Arrays that are initialized
•Global variables
•Static variables
Avalon-MM master ports are generated even when local arrays
(such as the ones discussed here) are referenced. These master
ports only connect to internal slave ports inside the accelerator.
However, the master ports do appear in the C2H Compiler build
report.
Example 3–7 demonstrates the creation of a local 4 kbyte memory inside
the accelerator to store array data[1024]. Because this memory buffer
is large, it translates to an embedded memory block.
Example 3–7. Local Array That Uses 4 KBytes of On-Chip Memory Inside Accelerator
int my_func(int a_parameter)
{
int data[1024]; // 1K 4-byte ints
... // Body of the function
return data[0];
}
Global and Static Variables
Global and static variables must persist outside the scope of the
accelerated function, and they have real addresses accessible by the
processor. For this reason, global and static variables are stored in
memory that the processor can access, outside of the accelerator
hardware. In other words, the C2H Compiler does not affect the location
of these variables; it creates logic in the accelerator capable of accessing
the memory where the variables reside.
References to global and static variables translate to master ports in
hardware, which enable access to a given variable's specific memory
location. For further details, refer to section “Memory Accesses” on
page 3–15.
Memory
Accesses
Altera Corporation 9.13–15
November 2009Nios II C2H Compiler User Guide
Hardware accelerators generated by the C2H Compiler use Avalon-MM
master ports to access memory, similar to the Nios II processor and other
SOPC Builder components. The Altera® SOPC Builder system integration
tool handles the task of physically connecting both accelerators and
processors to memory, and creating arbitration logic. As a result, the
Memory Accesses
behavior of a C function accessing memory is the same, regardless of
whether the function is implemented as hardware logic or software
instructions.
fFor more information on SOPC Builder, Avalon interfaces, and how
SOPC Builder generates system interconnect fabric, refer to the
Quartus II Handbook, volume 4: SOPC Builder and the Avalon MemoryMapped Interface Specification.
In order to maximize bandwidth, the C2H Compiler creates a master port
on the accelerator for every C operator that accesses external memory.
Multiple master ports allow the accelerator to read and write data to an
unlimited number of locations simultaneously, thereby reducing the
bandwidth limitations inherent in a CPU with a single data master port.
In some cases, the C2H Compiler can determine that master ports can be
shared between several external memory operations without sacrificing
performance. However, as a general rule, an Avalon-MM master port is
created for each of the following:
■Pointer dereference (* operator)
■Index into an array ([ operator)
■Index into a struct or union (. or -> operator)
■Usage of a global or static variable
Example 3–8 demonstrates various lines of code that generate a master
port in hardware.
Example 3–8. C Statements that Generate Avalon-MM master ports
The following sections describe each case in detail.
Indirection Operator (Pointer Dereference)
The indirection operator (*) is the fundamental expression of
dereferencing and indirection. This section describes how the
C2H Compiler handles pointer dereferencing.
3–16 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
C-to-Hardware Mapping Reference
Because the array subscript operation and the member operation for
structures and unions can be expressed in terms of an address
computation and a pointer dereference, this section is fundamental to
understanding how arrays, structures, and unions translate to hardware
as well.
Creation of Avalon-MM master ports
In general, the C2H Compiler creates a master port on the accelerator for
every instance of the indirection operator. The following are exceptions to
this rule:
■The C2H Compiler identifies certain opportunities for optimization.
In some cases it can collapse multiple master ports to a single master
port without affecting performance, which reduces resource
utilization.
■If the C2H Compiler determines that two pointers are exactly
equivalent, it consolidates them to a single master port.
■There are considerations for multidimensional arrays. Refer to
section “Array Subscript Operator” on page 3–26.
Consolidation of Equivalent Pointers
The C2H Compiler consolidates pointer dereferences when it determines
that the address expressions are always equal and the referenced data
cannot change between dereferences. In this case, the pointer
dereferences share an Avalon-MM master port.
Example 3–9 shows two dereferences that are identical. The
C2H Compiler consolidates them into a single master port.
Altera Corporation 9.13–17
November 2009Nios II C2H Compiler User Guide
Memory Accesses
Example 3–10 shows two dereferences that are identical inside of a loop.
The C2H Compiler consolidates them into a single master port.
Example 3–10. Equivalent Pointers in a Loop
void equivalent_pointers(char *packed_data, int len)
{
int i = 0;
while (i < len)
{
char ms_nibble = *(packed_data) >> 4;
char ls_nibble = *(packed_data++) & 0x0f;
...
i++;
}
}
Example 3–11 demonstrates a case of non equivalent pointers.
Example 3–11 is similar to Example 3–10, but packed_data increments
between the two pointer dereferences. In this case the address
expressions have different values, which translate to two separate master
ports.
Example 3–11. Nonequivalent Pointers
void nonequivalent_pointers(char *packed_data, int len)
{
int i = 0;
while (i < len)
{
char ms_nibble = *(packed_data++) >> 4;
char ls_nibble = *(packed_data) & 0x0f;
...
i++;
}
}
3–18 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
C-to-Hardware Mapping Reference
Example 3–12 demonstrates another case of non equivalent pointers.
Example 3–12 is similar to Example 3–10, but a value is written to address
some_other_pointer between the reads from address
(packed_data + i).
Example 3–12. Nonequivalent Pointers Due to Potential Aliasing
void nonequivalent_pointers(char *packed_data,
int *some_other_pointer,
int len)
{
int i = 0;
while (i < len)
{
char ms_nibble = *(packed_data + i) >> 4;
char ls_nibble;
*some_other_pointer = i;
ls_nibble = *(packed_data + i) & 0x0f;
...
}
}
In this code, the C2H Compiler cannot determine if
some_other_pointer and packed_data overlap addresses (known
as aliasing), which would affect the result of the second evaluation of
*(packed_data + i). Therefore, the C2H Compiler creates a separate
master port for each dereference, creating a total of three master ports. For
details on how to inform the C2H Compiler that two pointers do not alias,
see section “Pointer Aliasing” on page 3–32.
Volatile Type Qualifier
The C2H Compiler does not consolidate or optimize pointers for
dereferenced types that use the volatile type qualifier. The volatile
type qualifier forces the variable to be evaluated strictly according to the
rules of the language. volatile is normally used to access non-memory
peripherals, such as timers and communication devices.
1The volatile type qualifier overrides the __restrict__
pointer qualifier. For further information, see “Pointer Aliasing”
on page 3–32.
Altera Corporation 9.13–19
November 2009Nios II C2H Compiler User Guide
Memory Accesses
Example 3–13 demonstrates the use of volatile to guarantee multiple,
By comparison, Example 3–14 demonstrates two sections of code that are
equivalent, due to the consolidation of equivalent pointers. In this case,
the type of *DataFIFO is not declared volatile.
// The code above is equivalent to the following:
char *DataFIFO = FIFO_BASE;
char dereferenced_DataFIFO = *DataFIFO;
char Byte0 = dereferenced_DataFIFO;
char Byte1 = dereferenced_DataFIFO;
char Byte2 = dereferenced_DataFIFO;
char Byte3 = dereferenced_DataFIFO;
Avalon-MM Master Port Signal Generation
A dereference operation, such as *(ptr_to_int + i), translates to an
Avalon-MM master port on the accelerator. This section describes how
hardware accelerator logic generates the signals that drive the master
port.
Avalon-MM master ports created by the C2H Compiler comprise the
following fundamental elements:
■Logic to compute the address signal
■For write transfers only, logic to compute the write-data signal
■Logic to control the read-enable or write-enable signal
3–20 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Each of these signals is registered at the master port interface of the
hardware accelerator. Logic within the accelerator synchronizes these
signals to produce coherent Avalon-MM master transfers at the master
port.
Address Computation
Consider the pointer dereference in the following code which performs a
read operation:
int j = *(ptr_to_int + i);
The C2H Compiler generates logic of the following form to compute the
address signal:
ptr_to_int_i_addr = ptr_to_int + i * sizeof(int);
Figure 3–9 shows an example of the logic created for this pointer
dereference for a read operation.
Figure 3–9. Address Generation for a Read Operation
C-to-Hardware Mapping Reference
In Figure 3–9, first, the address expression is evaluated. Assuming
sizeof(int) is 4, i must be multiplied by four, which is equivalent to
left-shifting by 2 bits. Bitwise shift operators require no logic elements to
compute, and the result is not registered. (See section “Unregistered
Operations and Assignments” on page 3–3.) The signal
ptr_to_int_i_address feeds registers that drive the address signals
on the Avalon-MM master port. As soon as the address signal
ptr_to_int_i_address is valid, read-enable control logic asserts the
signal ptr_to_int_i_read, which initiates a transfer on the master
Altera Corporation 9.13–21
November 2009Nios II C2H Compiler User Guide
Memory Accesses
port. After some number of clock cycles determined by the slave memory
latency and arbitration delay, valid readdata returns to the master port.
(See section “Read Operations with Latency” on page 3–37.)
Data Computation
For write operations to dereferenced pointers, data-computation logic in
the accelerator computes the value of the expression to write to memory.
This value is the write-data for an Avalon-MM master transfer to
memory. Data-computation logic operates in parallel with the addresscomputation logic.
Consider the pointer dereference in the following code which performs a
write operation:
*(ptr_to_int + i) = x + y;
Figure 3–10 shows an example of the logic created for this pointer
dereference for a write operation.
Figure 3–10. Data Generation for a Write Operation
3–22 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
The write-data signal for the Avalon-MM master port is computed and
registered in parallel with the address assignment. As soon as
ptr_to_int_i_addr and ptr_to_int_i_writedata are valid,
write-enable control logic asserts the signal ptr_to_int_i_write,
which initiates a transfer on the master port.
Figure 3–11 shows the logic created for the following write operation to a
dereferenced pointer. Translation of the data-computation logic follows
the rules described in section “Assignments” on page 3–2.
*(ptr_to_int + i) = a*x + y;
Figure 3–11. Complex Write Operation
C-to-Hardware Mapping Reference
Master-Slave Connections
The C2H Compiler uses pragmas that allow user control of master-slave
connections and arbitration shares. This section describes the pragmas to
control master-slave connections.
The C language specification dictates that when a compiler
implementation encounters a pragma directive it does not recognize, the
compiler ignores the pragma. By using pragmas, you can write directives
to optimize the C2H Compiler results, without making the C code
incompatible with other compilers.
Altera Corporation 9.13–23
November 2009Nios II C2H Compiler User Guide
Memory Accesses
Specifying Master-Slave Connections
By default, the C2H Compiler connects all
master ports of an accelerator
to all the memory slave ports that the CPU data master port connects to.
These default connections guarantee that the accelerator can access any
memory addresses that the processor can access. However, master-slave
connections have an associated hardware resource cost. The extra
multiplexing and arbitration logic associated with a master-slave
connection often reduces the maximum achievable frequency (f
MAX
) of
the system. If an accelerated function, by design, does not ever access
certain memories, you can eliminate the connection to the slave memory
to save resources and improve f
MAX
.
The C2H Compiler provides a connection pragma that associates a
pointer variable with an Avalon-MM slave port, which is typically a
memory. A pointer variable can translate to one or more master ports,
depending on how many times it is dereferenced in the C code. The
connection pragma directs the C2H Compiler to connect all master ports
generated for a particular variable to a specific slave port in the SOPC
Builder system.
The connection pragma is defined as follows:
#pragma altera_accelerate connect_variable \
<function name>/<variable name> to <module>[/<slave name>]
The connection pragma must be placed outside the function to accelerate
in the same file. <function name> and <variable name> are the exact names
of the accelerated function and the pointer variable. <module> is the exact
name of the component instance, as specified in SOPC Builder.
<slave name> is optional. If <slave name> is provided, it is the exact name
of a specific slave port on <module>. If the module only co nt ains one sla ve ,
you can omit <slave name>. However, if you omit <slave name> when the
module contains multiple slaves, the compiler issues an error.
To connect a variable's master ports to multiple slave ports, you can use
multiple pragmas. If you use the connection pragma for a specific
variable, the C2H Compiler connects only the slave ports specified in
pragma statements.
In addition to reducing arbitration logic, the connection pragma helps the
C2H Compiler determine if two pointers overlap. If the memory
connections for two separate variables are mutually exclusive, the
compiler concludes that the pointers are never dependent on each other.
For more information, refer to section “Pointer Aliasing” on page 3–32.
3–24 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
C-to-Hardware Mapping Reference
Example 3–15 illustrates usage of the connection pragma to connect two
master ports for the variable my_ptr to the memory module named
onchip_buffer.
Example 3–15. Pragma Connecting a Master Port to a Slave Port
#pragma altera_accelerate connect_variable foo/my_ptr to onchip_buffer
int foo(int *my_ptr)
{
int x = *my_ptr;
my_ptr[8] = 23;
}
Example 3–16 illustrates using multiple pragmas to connect a pointer
variable's master ports to multiple slave ports.
Example 3–16. Pragma Connecting a Master Port to Multiple Slave Ports
#pragma altera_accelerate connect_variable foo/my_ptr to onchip_buffer_0
#pragma altera_accelerate connect_variable foo/my_ptr to ext_ram_bridge
#pragma altera_accelerate connect_variable foo/my_ptr to sdram
#pragma altera_accelerate connect_variable foo/my_ptr to \
onchip_buffer_1/s2
int foo(int *my_ptr)
{
int x = *my_ptr;
my_ptr[8] = 23;
}
Specifying Arbitration Shares
Arbitration shares benefit memories that have higher efficiency when
accessed sequentially, such as SDRAM. You can use arbitration shares to
reduce interruptions to sequences of transfers with a specific slave. For
example, if a master-slave connection has an arbitration share value of
ten, then the arbitrator grants at least ten consecutive transfers to the
master port when it begins a sequence of transfer requests.
The connection pragma with additional terms for arbitration share is
defined as follows, where <shares> is a positive integer from 1 to 100:
Altera Corporation 9.13–25
November 2009Nios II C2H Compiler User Guide
Memory Accesses
Example 3–17 connects the variable x in function myfunc to the memory
module named sdram with an arbitration share of 16.
Example 3–17. Pragma Specifying Arbitration Share
#pragma altera_accelerate connect_variable myfunc/x to sdram \
arbitration_share 16
Specifying Flow Control
Avalon-MM transfers with flow control force a master port to obey flow
control signals controlled by a slave port. For example, a slave FIFO might
assert flow control signals to prevent write transfers when the FIFO
memory is full. The C2H Compiler provides a flow control pragma which
enables flow control for all master ports related to a specific pointer
variable.
The flow control pragma must be placed in the same file as the function
to be accelerated, but outside the function. <function name> and
<variable name> are the exact names of the accelerated function and the
pointer variable.
fFor details about Avalon-MM flow control, refer to the Avalon Memory-
Mapped Interface Specification.
Array Subscript Operator
The C2H Compiler converts array subscript operations to equivalent
pointer dereferences, and then translates the pointer dereferences to
hardware. An array is a pointer with abstractions of length and
dimension. The value of an array type is defined to be a pointer to its first
element. The C language specification defines the array subscript
operator as follows:
The definition of the subscript operator [] is that E1[E2] is identical to
(*((E1)+(E2))).
By this definition, any array subscripting (indexing) operation can be
expressed in terms of an indirection (pointer dereference) operation.
3–26 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
C-to-Hardware Mapping Reference
Although an array is considered to be a pointer type, dereferencing an
array variable does not always mean the same thing as dereferencing a
pointer. For example, dereferencing or indexing once into a
multidimensional array returns a pointer to the first element in another
array. For an N-dimensional array, a dereference or index into any of the
first (N-1) dimensions does not read a value from the array memory; it
computes an offset from the array's base address to determine the address
of a subsection of the array.
Example 3–18 demonstrates that indexing to the first level of a two-
dimensional array does not result in a memory access.
Example 3–18. Indexing a Multidimensional Array without Causing a Memory Access
char a[LENGTH][WIDTH]; // Here's a two-dimensional array
// The following assignments are all equivalent.
char *subscripting = a[3];
char *dereferencing = *(a + 3);
char *offset = (char *) (a + 3);
char *ptr_arithmetic = (char *) ((void *)a + 3*WIDTH);
Indexing into any of the first (N-1) dimensions of an N-dimensional array
requires a multiplication operation, as demonstrated by the evaluation of
ptr_arithmetic in Example 3–18. If the size of the resultant array is an
integer power of two, then the multiplication operation is reduced to a
constant-shift operation, which does not require a hardware multiplier.
(Refer to section “Unregistered Operations and Assignments” on
page 3–3.)
A series of subscript operations that index into all N dimensions of an N-
dimensional array is equivalent to an indirection operation, which creates
an Avalon-MM master port. Example 3–19 illustrates several cases that
generate an Avalon-MM master port to dereference an array variable.
Example 3–19. Indexing an Array and Causing a Memory Access
char a[LENGTH][WIDTH]; /* a is a two-dimensional array */
// The following assignments are equivalent.
char *subscripting_a = a[3][2];
char *dereferencing_a = *(*(a + 3) + 2);
char b[LENGTH]; /* b is a one-dimensional array */
// The following assignments are equivalent.
char *subscripting_b = b[1];
char *dereferencing_b = *(b + 1);
Altera Corporation 9.13–27
November 2009Nios II C2H Compiler User Guide
Memory Accesses
Structure and Union Operators
The structure and union constructs in the C language provide an
abstraction for creating objects that consist of multiple types. The .
(member) and -> (structure pointer) operators abstract accesses to
structures and unions. These operators can be converted to an equivalent
address expression and indirection operation.
The C2H Compiler keeps storage for global structure and union variables
in the Nios II processor’s data memory. For global structures and unions,
the C2H Compiler converts structure and union operations to equivalent
pointer dereferences, and then translates the pointer dereference to
hardware. The pointer dereference creates an Avalon-MM master port on
the hardware accelerator, as described in section “Indirection Operator
(Pointer Dereference)” on page 3–16.
For storage of local structure and union variables, the C2H Compiler uses
on-chip memory resources inside the accelerator. For these local
variables, the C2H Compiler generates master ports internal to the
accelerator, which connect only to the internal memory. Refer to section
“Arrays, Structures, and Unions” on page 3–14.
Member Operator .
The member operator (.) accesses a single member of a structure or
union.
The struct member operation (mystruct.a) is equivalent to
Similarly, the union member operation (myunion.a) is equivalent to
Example 3–21.
Example 3–21. Converted union Member Operation
*((pointer_to_type_of_a)(&myunion))
3–28 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Consider the simple struct declaration shown in Example 3–22.
Example 3–22. Structure Declaration
struct s
{
int element_a;
int element_b;
int element_c;
} my_struct;
In this example, the expression (my_struct.c) translates to the
following:
*((int *)((void *)&my_struct + 2*sizeof(int)))
Structure Pointer Operator ->
The structure pointer operator is used to access a single member of a
structure or union via a pointer to the structure or union. The structure
pointer operator behaves similarly to the member operator, except that
the left-hand side of the operator must be a pointer.
C-to-Hardware Mapping Reference
The structure pointer operation on a struct(e.g. mystruct->a) is
equivalent to Example 3–23.
The structure pointer operation on a union (e.g. myunion->a) is
equivalent to Example 3–24.
Example 3–24. Converted union Pointer Operation
*((pointer_to_type_of_a)myunion)
Altera Corporation 9.13–29
November 2009Nios II C2H Compiler User Guide
Scheduling
Consider the simple struct declaration shown in Example 3–25.
Example 3–25. Structure Pointer Declaration
struct s_ptr
{
int element_a;
int element_b;
int element_c;
} * my_struct;
In this example, the expression (my_struct->element_c) translates
to the following:
*((int *)((void *)mystruct + 2*sizeof(int)))
Scheduling
This section describes how the C2H Compiler schedules operations. The
C2H Compiler is similar to a traditional C compiler in many respects: It
parses code, creates a graph of the dependencies, performs some
optimizations, schedules the sequence to execute each operation, and
outputs an object file in the form of a hardware accelerator. However,
fundamental differences exist between scheduling for a microprocessor
and scheduling for a hardware accelerator.
Scheduling Concepts for Hardware Accelerators
A microprocessor has limited computational resources, defined by its
arithmetic logic unit (ALU), and limited I/O resources, defined by its
data bus architecture. In contrast, a hardware accelerator can have
arbitrary computational and I/O resources, limited only by the practical
bounds of resource utilization and the ability to achieve frequency
performance. These resources can operate in parallel, stalling only to wait
for data dependencies to resolve.
The C2H Compiler uses the following fundamental rule for scheduling:
Perform computation operations and I/O operations as soon as data
dependencies are resolved.
State Machines
Sections “One-to-One C-to-Hardware Mapping” on page 3–1, “Variable
Declarations” on page 3–13, and “Memory Accesses” on page 3–15
described how the C2H Compiler translates individual operations,
assignments, and memory accesses to atomic functional units in
3–30 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
C-to-Hardware Mapping Reference
hardware. After the C2H Compiler creates the functional units, it
generates a hierarchy of state machines to control the operation and
interaction of these units.
The C2H Compiler generates a distinct state machine for each of the
following:
■Accelerated function (top level)
■Loop
■Subfunction
The states comprise a sequence of stages that compute the results of the C
function. The C2H Compiler assigns each operation to a state of the state
machine. An arbitrary number of operations can execute during one state,
allowing multiple operations to execute in parallel. Generally, the time for
one state to execute equates to one clock cycle, although certain
conditions cause stalls in the state machine's progression through states.
Data Dependencies
Scheduling of assignments within an accelerator is based on the data
dependencies between the assignments. If assignment B depends on a
value calculated in assignment A, then B cannot execute until A has
completed. If two or more assignments are not dependent on each other,
they can be scheduled in parallel.
One way to illustrate data dependencies is through a dependency graph.
For each expression, arrows in a dependency graph represent where the
inputs come from, and where the output is used. These arrows illustrate
the flow of data through this function.
Figure 3–12 shows the dependency graph for Example 3–26.
Example 3–26. Data Dependency
int foo(int a, int b, int c)
{
int x = a * b;
int y = b * c;
int z = x + y;
return z;
}
Altera Corporation 9.13–31
November 2009Nios II C2H Compiler User Guide
Scheduling
Figure 3–12. Dependency Graph
The C2H Compiler uses the dependency graph to assign each assignment
to a state in the state machine. Figure 3–13 shows how the C2H Compiler
assigns each assignment to a state.
Figure 3–13. Scheduling Assignments in a Dependency Graph
Pointer Aliasing
Aliasing is a situation where it is possible for a change to one variable or
reference to affect another. In the C language, aliasing can occur due to the
indirection introduced by pointers. If the address ranges referenced by
two pointers overlap, the pointers alias. Pointer aliasing is another form
of data dependency that the C2H Compiler must consider. Any read or
write operation with a pointer is dependent on all pointer write-
3–32 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
operations that come before it. Because arrays and structures are
equivalent to pointer operations, the same considerations apply when
indexing into an array or structures.
This section describes the implications of aliasing on the C2H Compiler
and outlines methods to prevent unnecessary dependencies.
Figure 3–14 shows the dependency graph for Example 3–27.
Example 3–27. Pointer Aliasing
void foo(int *ptr_a, int *ptr_b)
{
int a, b;
a = *ptr_a;
*ptr_a = a + 7;
b = *ptr_b;
*ptr_b = b + 8;
}
Figure 3–14. Pointer-Related Data Dependency
C-to-Hardware Mapping Reference
In this example, the C2H Compiler cannot determine whether or not
ptr_a and ptr_b ever point to the same address. Therefore, it schedules
conservatively, under the assumption that they do. The dependency
graph shows that the read operation from ptr_b depends on the write
operation to ptr_a. This is not a dependency on the variable ptr_a, but
rather a dependency on a location in memory that is unknown at
Altera Corporation 9.13–33
November 2009Nios II C2H Compiler User Guide
Scheduling
compile-time due to the possibility of aliasing. This dependency causes
the read operation from ptr_b to be scheduled at State 2, rather than at
State 0.
__restrict__ Pointer Type Qualifier to Break Dependencies
If you know that a pointer never overlaps with another, you can inform
the compiler by declaring the pointer to be a restricted pointer. The
restrict type qualifier is introduced in the ISO C 99 specification. The
compiler ignores any or all aliasing implications of a pointer qualified by
__restrict__. The C99 specification states that if a pointer “p” is
declared with restrict, and another pointer “q” accesses any location also
accessed by “p,” the behavior is undefined. In other words, a restricted
pointer promises to never alias another pointer.
Example 3–28 demonstrates several pointers declared using the
__restrict__ type qualifier.
1The qualifier comes after the *; __restrict__ qualifies the
pointer type, not the type that the pointer points to.
Example 3–28. Pointer Declarations with __restrict__
int * __restrict__ my_restricted_pointer_to_integer;
const int * __restrict__ my_restricted_pointer_to_constant_integer;
int * const __restrict__ my_constant_restricted_pointer_to_integer;
Figure 3–15 shows the dependency graph for Example 3–29, which uses
the __restrict__ type qualifier to inform the C2H Compiler that
ptr_a and ptr_b do not alias:
Example 3–29. Using __restrict__
void foo(int * __restrict__ ptr_a,
int * __restrict__ ptr_b)
{
int a, b;
a = *ptr_a;
*ptr_a = a + 7;
b = *ptr_b;
*ptr_b = b + 8;
}
3–34 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Figure 3–15. __restrict__ Pointer Type Breaks Dependencies
Although a pointer qualified with __restrict__ creates no
dependencies with other pointers, it can create dependencies with itself.
Figure 3–16 shows the dependency graph for Example 3–30.
Example 3–30. Pointers Always Depend on Themselves
void foo(int * __restrict__ my_ptr,
int offset_a,
int offset_b)
{
int a, b;
a = my_ptr[offset_a];
my_ptr[offset_a] = a + 7;
b = my_ptr[offset_b];
my_ptrb[offset_b] = b + 8;
}
C-to-Hardware Mapping Reference
Altera Corporation 9.13–35
November 2009Nios II C2H Compiler User Guide
Scheduling
Figure 3–16. Pointers Always Depend on Themselves
In this example, the C2H Compiler cannot schedule the two read
operations in parallel, because it assumes that the two address
expressions of my_ptr could overlap. Assuming that offset_a never
equals offset_b, to make these operations execute in parallel, you need
to declare another restricted pointer.
Figure 3–17 shows the dependency graph for Example 3–31, which
introduces a new restricted pointer, my_ptr_b, to prevent the data
dependency present in Figure 3–16.
Example 3–31. Using Another Pointer to Avoid Self-Dependence
void foo(int * __restrict__ my_ptr,
int offset_a,
int offset_b)
{
int a, b;
int * __restrict__ my_ptr_b = my_ptr;
a = my_ptr[offset_a];
my_ptr[offset_a] = a + 7;
b = my_ptr_b[offset_b];
my_ptr_b[offset_b] = b + 8;
}
3–36 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Figure 3–17. Using Another Pointer to Avoid Self-Dependence
1If a data structure is referenced by two pointers and one or more
of them is restrict-qualified, the ISO C 99 standard specifies that
the behavior is undefined. Therefore, make sure that you fully
understand the range of values that a pointer can take on during
the execution of your application before applying the
__restrict__ qualifier. Improper application can result in
undesirable functional changes to the code than cannot be
debugged in software, due to the limitations of restrict-based
optimizations in conventional compilers.
C-to-Hardware Mapping Reference
1The ISO C 99 standard specifies that the volatile type
qualifier overrides the __restrict__ pointer type. This
means that __restrict__ has no effect on volatile
pointers. To break pointer dependencies between volatile
pointers, use separate interrupt-enabled accelerators instead of
multiple loops in the same accelerator. For details about
interrupt-enabled accelerators, see “Interrupt Pragma” on
page 6–4.
Using Physically Separate Slave Ports to Break Dependencies
If the master ports associated with two pointers do not access any shared
slave ports in the SOPC Builder system, then the C2H Compiler assumes
that the pointers do not alias. Section “Master-Slave Connections” on
page 3–23 describes how to limit the master-slave connections, which can
be an effective method to prevent aliasing.
Read Operations with Latency
Memory latency and other access delays affect how the C2H Compiler
schedules operations. Inherently, an operation cannot proceed until the
data for the operation arrives, which depends on memory latency. The
C2H Compiler generates logic within hardware accelerators to manage
Altera Corporation 9.13–37
November 2009Nios II C2H Compiler User Guide
Scheduling
and mitigate the effects of memory latency. Through close integration
with SOPC Builder, the C2H Compiler can determine the latency
characteristics of the slave ports connected to the accelerator. The
C2H Compiler generates logic to maximize bandwidth for the specific
memories in the system.
Avalon-MM pipelined read transfers increase the bandwidth for
synchronous slave ports that require several cycles of latency to return
data for the first access, but can return data every cycle thereafter. Using
pipelined read transfers, a slave port can begin a new transfer before data
from the previous transfer returns. There are only pipelined read
transfers; Avalon-MM write transfers do not benefit from pipelined
functionality.
The C2H Compiler takes memory latency into account when scheduling
operations, allowing an accelerator to perform nondependent operations
while waiting for data to return from a memory with latency. The master
ports associated with a pointer might connect to multiple slave ports with
different latency properties. In this case, the C2H Compiler uses the
maximum latency of all slave ports.
Figure 3–18 shows the dependency graph for function foo(), shown in
Example 3–32. This example uses the connection pragma to exclusively
connect a pointer named ptr_in to a memory with two cycles of read
latency. (Refer to section “Master-Slave Connections” on page 3–23.)
Example 3–32. Early Scheduling of Read Operation with Latency
#pragma altera_accelerate connect_variable \
foo/ptr_in to \
my_memory_with_two_cycles_read_latency
int foo(int *ptr_in, int x, int y, int z)
{
int xy = x * y;
int xy_plus_z = xy + z;
int ptr_data = *ptr_in;
int prod = ptr_data * xy_plus_z;
return prod;
}
3–38 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Figure 3–18. Early Scheduling of Read Operation with Latency
The C2H Compiler optimizes the dependency graph for this function by
moving the read operation for ptr_in up to state 0. This optimization
allows the calculation of xy and xy_plus_z to occur during the two
cycles of latency required to fetch data for ptr_in.
Stalling
C-to-Hardware Mapping Reference
A state machine stalls when data needed for an operation is not available.
A state machine might stall while waiting for one or more of the following
actions to complete:
■Inner loop
■Subfunction call
■Memory transfer
The state machine does not proceed until all reasons for stalling are
resolved.
Inner Loops
Each loop is implemented as a state machine, and an inner loop translates
to a particular state within the state machine for its containing function or
outer loop. In other words, an inner loop translates to a state machine
within a state machine. As the state machine for an inner loop executes,
the outer state machine stalls until the inner loop has completed.
Altera Corporation 9.13–39
November 2009Nios II C2H Compiler User Guide
Scheduling
For the purposes of scheduling, the C2H Compiler treats a loop and its
dependencies as a unit. No lines of code past the loop block execute until
the whole loop completes. Figure 3–19 shows the dependency graph for
the function transform_and_hash_matrix(), shown in
Example 3–33.
Example 3–33. Dependency Graph for a Function Containing a Loop
int transform_and_hash_matrix(int *matrix,
int length, int width)
{
int n_words = length * width;
int hash = 1;
int i;
for (i=0; i<n_words; i++)
{
...perform some transform...
hash = ...some hash calculation...
}
return hash;
}
Figure 3–19. Dependency Graph for a Function Containing a Loop
As shown in Figure 3–19, some part of the for loop depends on
n_words, and so the C2H Compiler does not schedule the loop until after
the assignment to n_words completes. The return statement outside
the loop depends on hash, which is assigned inside the loop. As a result,
the C2H Compiler does not schedule the return statement until the loop
completes.
In this case, the state machine for transform_and_hash_matrix()
has three states. However, the state machine does not complete in three
clock cycles, because State 1 consists of a sub-state-machine, which
requires multiple clock cycles to complete.
3–40 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
C-to-Hardware Mapping Reference
If multiple loops have no interdependencies, the C2H Compiler
schedules the loops on the same state, allowing the loops to execute in
parallel. The code in Example 3–34 has two while loops with no
dependencies on each other. The C2H Compiler schedules these loops on
the same state.
Example 3–34. Loops Without Interdependencies Scheduled in Parallel
void double_mac (int* __restrict__ a, int* __restrict__ b,
int* __restrict__ c, int* __restrict__ d,
long long* __restrict__ res_ab,
long long* __restrict__ res_cd,
int len)
{
int len_cd = len; // duplicate the length index
// Compute the MAC for a & b
long long mac_ab = 0;
while (len_ab > 0)
{
mac_ab += *a++ * *b++;
len_ab--;
}
// Compute the MAC for c & d
long long mac_cd = 0;
while (len_cd > 0)
{
mac_cd += *c++ * *d++;
len_cd--;
}
*res_ab = mac_ab;
*res_cd = mac_cd;
return;
}
Subfunction Calls
A subfunction call can stall the state machine in the same way that an
inner loop does. When a subfunction contains a looping structure or
shares a data dependency with its caller, the subfunction is not pipelined.
If this is the case, when the outer state machine reaches its state for the
subfunction, the outer state machine stalls until the subfunction has
completed.
If the subfunction does not contain loops or shared data dependencies the
C2H Compiler can pipeline the subfunction. For details about pipelined
subfunctions, see “Subfunction Pipelining” on page 3–49.
Altera Corporation 9.13–41
November 2009Nios II C2H Compiler User Guide
Scheduling
Memory Transfers
Avalon-MM system interconnect fabric manages arbitration between
multiple Avalon-MM master ports that access a single slave port. A
master port might have to wait several clock cycles before beginning a
transfer due to arbitration. If a master port on an accelerator is being
forced to wait, the state machine for the accelerator stalls until the transfer
can proceed.
Loop Pipelining
The C2H Compiler structures the state machine for a loop so that
iterations of the loop are pipelined. In other words, consecutive iterations
of the loop can begin before prior iterations have completed.
Pipelining Loop Iterations
Figure 3–20 shows the dependency graph for the loop block in
Example 3–35.
Example 3–35. Loop Block
int mac(int *data_array, int *coef_array, int len)
{
int sum = 0;
do
{
int x = *data_array++;
int c = *coef_array++;
int prod = c * x;
sum += prod;
} while (i++ < len);
return sum;
}
3–42 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Figure 3–20. Dependency Graph for a Loop Block
C-to-Hardware Mapping Reference
Altera Corporation 9.13–43
November 2009Nios II C2H Compiler User Guide
Scheduling
Figure 3–21 illustrates how the C2H Compiler schedules successive
iterations of the loop shown in Figure 3–20. The C2H Compiler is able to
start a new iteration of the loop immediately after the prior iteration
completes State 0.
This is an example of an ideally-pipelined loop. Although the
C2H Compiler can pipeline many loops ideally, it is sometimes not
possible due to the lack of inherent parallelism in the code, as shown in
“Loop-Carried Dependencies”.
Figure 3–21. Pipelined Loop Iterations
Loop-Carried Dependencies
Loop-carried dependencies are data dependencies that manifest when
pipelining successive iterations of a loop. If the result of one calculation
in an iteration of the loop is used in a later iteration of the loop, then a
loop-carried dependency exists between the two operations.
3–44 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Figure 3–22 shows the dependency graph for the do loop in
Example 3–36, which has loop-carried dependencies.
Example 3–36. Loop-Carried Dependency
int simple_hash(int *data, int len)
{
int hash = 0;
do
{
int data_word = *data++;
hash = hash + data_word;
hash = hash ^ data_word;
} while (len--);
return hash;
}
Figure 3–22. Loop-Carried Dependency
C-to-Hardware Mapping Reference
Va ri ab l es data and hash have loop-carried dependencies, illustrated by
the cyclic arrows in Figure 3–22. The arrow for hash indicates that the
calculation on State 1 in iteration N is dependent on the result of the
calculation from State 2 in iteration (N-1). The arrow for data in State 0
illustrates the ideal case in which a state depends only on its own output.
The ideal case does not restrict the scheduling of successive iterations.
Altera Corporation 9.13–45
November 2009Nios II C2H Compiler User Guide
Scheduling
Figure 3–23 illustrates how the C2H Compiler schedules successive
iterations of the loop shown in Figure 3–22, based on the restrictions
imposed by hash. State 1 cannot execute until the previous iteration has
completed State 2. The C2H Compiler schedules the states as shown in
Figure 3–23 to satisfy the loop-carried dependency.
Figure 3–23. Pipelined Loop Iterations with a Loop-Carried Dependency
In Figure 3–23, the cyclic arrow for hash in Figure 3–22 translates to
straight arrows between iterations.
Pipelining Avalon-MM Read Transfers from Multiple Iterations
As discussed in section “Read Operations with Latency” on page 3–37,
the C2H Compiler is aware of read latency in slave memories. Master
ports on C2H accelerators can use Avalon-MM pipelined read transfers,
which allow multiple read transfers to be pending at a given time. As a
result, for a loop that reads from memory with latency, the next iteration
of the loop can begin fetching data before data from the previous iteration
has returned. For such a loop, the C2H Compiler creates a master port
3–46 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
that performs Avalon-MM pipelined read transfers. Inside the
accelerator, the master port connects to a FIFO, which guarantees the
accelerator can receive data for all pending read transfers, regardless of
whether the state machine stalls.
Figure 3–24 shows the dependency graph for Example 3–37, which
demonstrates a loop that pipelines memory accesses with latency. This
example uses the connection pragma to connect the master port for
variable list to a slave memory named
my_mem_with_two_cycles_read_latency, as described in section
“Master-Slave Connections” on page 3–23.
Example 3–37. Accessing Memory with Latency
#pragma altera_accelerate connect_variable \
sum_elements/list to \
my_mem_with_two_cycles_read_latency
int sum_elements (int *list, int len)
{
int i;
int sum = 0;
for (i=0; i<len; i++)
sum += *list++;
}
C-to-Hardware Mapping Reference
Figure 3–24. Accessing Memory with Latency
Altera Corporation 9.13–47
November 2009Nios II C2H Compiler User Guide
Scheduling
State 1 in Figure 3–24 remains empty because list has two cycles of read
latency. The loop-carried dependencies on variables sum and list are
ideal cases, which do not impose restrictions on the pipeline scheduling.
Figure 3–25 illustrates how the C2H Compiler schedules successive
iterations of the loop.
Figure 3–25. Pipelined Loop Iterations Reading Memory with Latency
As shown in Figure 3–25, the C2H Compiler is able to start a new iteration
of the loop immediately after the prior iteration completes State 0. At
Time 1, Iteration 1 starts a new read access from list, even though data
from list hasn't returned for Iteration 0. Due to the two cycles of read
latency, at any given time, there can be a maximum of two pending read
operations.
Over successive iterations of a loop, the C2H Compiler hides the memory
latency by pipelining the read transfers. Although multiple cycles of
latency are required to fill the pipeline, successive iterations can complete
at a rate of one per clock cycle, assuming no stalling occurs (see section
“Stalling” on page 3–39).
3–48 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
C-to-Hardware Mapping Reference
Loop Latency and Cycles per Loop Iteration (CPLI)
There are two metrics that determine the efficiency of an accelerated loop:
loop latency and cycles per loop iteration (CPLI).
Loop latency is the amount of time required for the first iteration of a loop
to complete, assuming one clock cycle per state of the loop's state
machine. An iteration completes when it passes from the first state
through the last state, and so the loop latency is equal to the number of
states in the loop's state machine.
CPLI is the minimum possible period for issuing successive loop
iterations. A new iteration of the loop state machine is issued every CPLI
clock cycles, assuming one clock per state. After initial loop latency is
overcome, a loop's state machine produces results for each successive
iteration every CPLI clock cycles. CPLI is determined by the loop-carried
dependencies present in a loop.
In general, the goal of optimizing C code for the C2H Compiler is to
reduce both loop latency and CPLI to as close to 1 as possible for all loops
in the accelerator. Loop latency and CPLI values assume no stalling,
however, which is not always realistic (see section “Stalling” on
page 3–39). Inner loops, subfunction calls, and slow memory accesses all
cause stalling at runtime, which cannot be reflected in CPLI and loop
latency. Conceptually, C2H optimization techniques are similar to
traditional C compilers: Focus effort on minimizing loop latency and
CPLI for critical inner loops first; reduce execution time of subfunction
calls; and use fast memory for performance-critical sections of code.
Subfunction Pipelining
Each subfunction is implemented as a state machine, and a subfunction
call translates to a particular state within the containing function or loop.
In other words, a subfunction translates to a state machine within a state
machine. The fact that it is a distinct state machine allows it to be a shared
resource within the containing function.
If the subfunction does not contain loops or shared data dependencies,
the C2H Compiler can pipeline the subfunction. The subfunction has its
own state machine, but the datapath is pipelined as if it were the body of
a loop. When the outer state machine reaches the state to call the
subfunction, it can continue to execute other operations in parallel with
the inner state machine. Data sets from multiple subfunction calls are
pipelined in the subfunction’s state machine.The code in Example 3–38
contains a subfunction which is pipelined by the C2H Compiler.
Altera Corporation 9.13–49
November 2009Nios II C2H Compiler User Guide
Scheduling
Example 3–38. Pipelined Subfunction
int MAX(int a, int b)
{
return ((a > b)? a : b);
}
#pragma altera_accelerate connect_variable MAX_loop/a to sdram
#pragma altera_accelerate connect_variable MAX_loop/b to
onchip_ram_64_kbytes
int MAX_loop(int * __restrict__ a, int * __restrict__ b)
{
int i, c = 0;
for (i = 0; i < 1024; i++)
{
c += MAX(a[i], b[i]);
}
return c;
}
If the subfunction performs a memory access that stalls, then the outer
state machine also stalls.
Pipelined subfunctions provide a useful option for controlling shared
resources. For further information, see “Resource Sharing”.
3–50 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
C-to-Hardware Mapping Reference
Resource
Sharing
The C2H Compiler is capable of sharing resources which consume
significant amounts of logic. A resource only becomes shared if it is
under-utilized. In other words, the C2H Compiler only shares a resource
if the performance of accelerator is not affected. Table 3–6 lists all of the
resources that can be shared automatically by the C2H Compiler.
Table 3–6. Sharable Resource
Dereference or
Operator
*var
var[]
*Multiply- Promoted data width (32 or 64 bit) must be the same for all
/Divide- Promoted data width (32 or 64 bit) must be the same for all divisions.
%Modulo- Promoted data width (32 or 64 bit) must be the same for all modulo
<<Left Shift- Promoted data width (32 or 64 bit) must be the same for all left shift
>>Right Shift- Promoted data width (32 or 64 bit) must be the same for all right shift
DescriptionRequired Conditions
Memory Access- Multiple dereferences to access data from the same Avalon-MM
memory port
- Multiple dereferences occurring within the same level in the
algorithm (function or loop)
- Loop CPLI must not increase
multiplications. The promoted data width is shown in the C2H report.
- Multiplications occurring within the same level in the algorithm
(function or loop)
- Both operands must be either signed or unsigned.
- Loop CPLI must not increase
The promoted data width is shown in the C2H report.
- Divisions occurring within the same level in the algorithm (function
or loop)
- Both operands must be either signed or unsigned.
- Loop CPLI must not increase
operations. The promoted data width is shown in the C2H report.
- Modulo operations occurring within the same level in the algorithm
(function or loop)
- Both operands must be either signed or unsigned.
- Loop CPLI must not increase
operations. The promoted data width is shown in the C2H report.
- Left shift operations occurring within the same level in the algorithm
(function or loop)
- Both operands must be either signed or unsigned.
- Loop CPLI must not increase
operations. The promoted data width is shown in the C2H report.
- Right shift operations occurring within the same level in the
algorithm (function or loop)
- Both operands must be either signed or unsigned.
- Loop CPLI must not increase
Altera Corporation 9.13–51
November 2009Nios II C2H Compiler User Guide
Resource Sharing
The resource sharing technique used for memory accesses differs slightly
from all other sharable resources. Memory accesses which share the same
Avalon-MM master port use byte enables to control the width of the
access. The master port width is equal to the widest data type being
accessed. The other sharable resources do not use byte enables, so
operator inputs and result must be of equal width for the resource to be
shared. 8-bit and 16-bit operators are automatically promoted to be 32 bits
wide, so as long as both operands are either signed or unsigned, the
operator can be shared.
As previously mentioned, resources are not shared if the performance of
the hardware accelerator degrades. For example, if the algorithm is
capable of performing two multiplications in parallel, the C2H Compiler
does not share a multiplier resource. The C2H Compiler optimizes for
performance by default and enables sharing only when appropriate.
The exception to this rule is when two pointer dereferences occur that
access data from the same Avalon-MM memory port. The C2H Compiler
knows that memory arbitration already limits the performance of the
accelerator and so the resources are shared. This exception is bound to
Avalon-MM ports since multi-port components can be accessed
concurrently. If you use the appropriate connection pragma statements
and __restrict__ qualifier, you can ensure that memory accesses
occur concurrently instead of becoming a shared Avalon-MM master
port.
The C2H Compiler only shares resources if they reside at the same level
within a loop or function. Figure 3–26 shows an algorithm which contains
shared and independent resources.
3–52 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
C-to-Hardware Mapping Reference
Figure 3–26. Shared And Independent Resources
Another way of managing shared resources is to place the code that uses
the resource in a subfunction. For example, to ensure that a mathintensive function uses no more than three multipliers, you could place
the multiply operation in three subfunctions mul1(), mul2() and
mul3(). With pipelined subfunctions, the latency overhead of this
approach is not excessive. For further details, see “Subfunction Calls” on
page 3–41.
Altera Corporation 9.13–53
November 2009Nios II C2H Compiler User Guide
Resource Sharing
3–54 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
4. Understanding the C2H
View
Introduction
Overview
This chapter discusses the Altera Nios II C-to-Hardware Acceleration
(C2H) Compiler view available in the Nios II IDE. Understanding the
C2H view allows you to estimate the resource usage and the performance
of the accelerator. You can use this information to perform optimizations
to reduce the logic resource size or increase the performance of the
accelerator.
You can use the C2H view to do the following:
■Add or update hardware accelerators
■Control how the C2H Compiler compiles your C code to hardware
■Display information about hardware accelerators
The C2H view displays performance information about your accelerated
functions. This information makes it easy to select the best configuration
for the next compile.
The C2H view contains two sections:
■Generation/Compilation Configurations – the C2H view allows
you to specify various generation and compilation configurations for
your accelerated functions. The configurations allow you to control
the build flow of the entire software project and the individual
accelerated functions.
■Build Report – The C2H Compiler creates the build report during a
software compilation when it analyzes functions or generates
accelerators. Using the build report you can view the resource usage
and scheduling information for each accelerated function.
Generation/Compilation Configurations
The following sections discuss the two levels of configurations that you
can use to control the build flow.
Project Build Configurations
The project build configurations allow you to control linker settings for a
project with multiple accelerators. The project build configurations also
control the integration of the accelerators with SOPC Builder and the
Quartus II software.
Altera Corporation 9.14–1
November 2009
Overview
Table 4–1 *summarizes the project build configurations.
Table 4–1. Project Build Configurations (Part 1 of 2)
ConfigurationMeaning
Use software
implementation for
all accelerators
Use the existing
accelerators
Analyze all
accelerators
You can use this global override configuration to force the linker to use the software
implementation of all the existing accelerators in the system.
You can use this configuration to perform functional changes to your algorithm without
having to regenerate your system or compile the hardware design. This is a global
configuration, and affects all accelerated functions.
If you only wish to revert to the software implementation of an individual accelerated
function, use the function build configurations section.
When you use the software implementation, the hardware accelerator remains in the
system, but is not used.
This global override configuration allows you to use accelerators that already exist in a
system. If you previously s witched to Use software implementation for all accelerators,
this configuration lets you revert back to the accelerator hardware.
In this configuration, the C2H Compiler does not regenerate the accelerator even if you
make changes to the accelerator source. You can use this configuration near the end of
the product development cycle to help prevent accidental hardware regeneration.
If you select Analyze all accelerators, the C2H Compiler analyzes the functions you ha ve
marked for acceleration, and produces a report of expected accelerator performance. It
does not compile the accelerators. If you have existing accelerators (from a previous
compile), they are untouched. In other words, Analyze all accelerators is the same as
Use the existing accelerators, except that it also produces a report.
Analyze all accelerators lets you quickly display build information without regenerating
the SOPC Builder system.
This configuration does not overwrite the existing accelerator logic, it simply analyz es the
source code.
4–2 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Table 4–1. Project Build Configurations (Part 2 of 2)
ConfigurationMeaning
Build software and
generate SOPC
Builder system
Build software,
generate SOPC
Builder system, and
run Quartus II
compilation
This configuration allows you to update the function accelerators in your system by forcing
SOPC Builder to regenerate the logic. SOPC Builder runs in the background and you can
view the process of the logic regeneration from the Nios II IDE console view.
Do not open SOPC Builder until logic generation is complete. The Nios II IDE coordinates
the software and hardware builds.
Once the SOPC Builder regeneration is complete, the Nios II IDE displays the updated
build information in the C2H view.
The logic regeneration only occurs if the accelerator did not previously exist or the
accelerated function source code changes. In order for these changes to take effect you
must compile the hardware design using the Quartus II software.
This configuration is a superset of Build software and generate SOPC Builder system.
Once SOPC Builder generates the system, Quartus II runs in the background and
compiles the hardware design. While the Quartus II software is running in the background
you can view the progress from the Nios II IDE console view.
Function Build Configurations
Understanding the C2H View
The function build configurations let you control the linking of individual
accelerated functions of a software project and the cache coherency
settings. This control is important for systems with multiple accelerators
per software project since cache coherency might not be an issue for all
the accelerated functions. While you work on a single accelerator, you can
compile the other accelerated functions in software-only mode, speeding
up C2H compilation.
Table 4–2 describes the available function build configurations.
Resources
The resources section of the build report shows information about the
resource usage of the accelerated function. The following are the
resources that can appear in this section of the build report:
■Avalon-MM master ports
■Multipliers
■Dividers
■Barrel shifters (variable shift)
Each of these resources has information about how they are configured in
the hardware and the line of source code that mapped to them. In the
following discussion of these resources, refer to Example 4–1.
Altera Corporation 9.14–3
November 2009Nios II C2H Compiler User Guide
Resources
Table 4–2. Function Build Configurations
ConfigurationMeaning
Use hardware
accelerator in place
of software
implementation.
Flush data cache
before each call
Use hardware
accelerator in place
of software
implementation
Use software
implementation
Use this configuration if you are not certain whether cache coherency is an issue in your
system.
In this configuration, every time the software calls the accelerated function, the wrapper
function flushes the entire Nios II data cache, to prevent cache coherency issues. The
C2H Compiler inserts flush code into the wrapper function so no source code modification
is necessary. Since flushing the cache is a fixed overhead, if you have strict processing
time requirements you need to study the system architecture and determine if this
operation is necessary.
This configuration uses the hardware accelerator without flushing the data cache before
each invocation. Use this configuration with algorithms that do not require Nios II data
cache flushing.
Do not use this configuration if you have not studied your algorithm to determine if it could
have cache coherency problems. The accelerated function might create cache coherency
problems in certain corner cases. To pre vent cache coherency problems, use one or more
of the following techniques in your code:
● Allocate all shared data in uncached Nios II memory space. Refer to “Bit-31 Cache
Bypass” in the Cache and Tightly-Coupled Memory chapter of the Nios II Software
Developer's Handbook. (1)
● Flush all shared data before calling the accelerated function.
● Place all shared data in a tightly coupled memory.
● Manage cache coherency in a multiprocessor system by establishing a cache
coherency protocol between the processor controlling the accelerated function and all
other processors.
● Use cache bypass macros to access all shared data.
● Do not share memory between the hardware accelerator and the Nios II processor
● Refer to the Cache and Tightly-Coupled Memory chapter of the Nios II Software
Developer's Handbook to learn more about cache coherency.
Much like the project wide configuration, this causes the C2H Compiler to link the
software implementation of the accelerated function. Unlike the project wide
configuration, this only affects a single accelerated function and not the entire software
project. You can use this configuration to prototype changes to your algorithm without
having to regenerate or recompile the hardware. This project configuration does not
remove the existing accelerator from the system.
Note to Ta b l e 4 –2 :
(1) Use caution passing uncached pointers to a C2H accelerator. There are two unusual situations that require special
consideration: (a) If the accelerator performs direct arithmetic or comparisons on the address value of a pointer, it
must account for the possibility that bit 31 is set. (b) If the accelerated function masters a component whose true
address is >= 0x80000000, and uses the same Avalon-MM master port to connect to the memory that it shares
with the Nios II processor, uncached pointers to the shared memory might result in spurious Avalon-MM transfers
to addresses >= 0x80000000.
4–4 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Understanding the C2H View
Example 4–1. Vector Power Calculation
#pragma altera_accelerate connect_variable power_calculation/voltage to onchipRAM1
#pragma altera_accelerate connect_variable power_calculation/current to onchipRAM1
#pragma altera_accelerate connect_variable power_calculation/power to onchipRAM2
void power_calculation ( short * __ restrict_ _ voltage,
short * __restrict__ current,
short * __restrict__ power,
short downscale,
int length)
Example 4–1 requires Avalon-MM read and write master ports to
perform memory accesses. It also requires a multiplier and a barrel shifter
to perform the right shift operation. The pragma statements inform the
C2H Compiler that the input data is stored in a memory called
onchipRAM1 and the output data is to be stored in onchipRAM2. When
the C2H Compiler compiles this function, the Nios II IDE generates a
build report as shown in Figure 4–1.
Altera Corporation 9.14–5
November 2009Nios II C2H Compiler User Guide
Resources
Figure 4–1. Build Report - Resources
The resources section contains a subsection for each type of resource. The
report shows Avalon-MM master port resources in a different layout from
other operator resources due to the differences between the functionality.
Avalon-MM Master Port Resources
The C2H Compiler uses Avalon-MM master ports to implement
dereference operations (memory accesses). The resources section of the
C2H build report shows each Avalon-MM master port created by the C2H
Compiler. The C2H Compiler creates the optimal number of master ports
for the memory accesses it finds in the code. The C2H Compiler conserves
Avalon-MM resources by creating a single Avalon-MM master port to
access memory for multiple dereference operations if there is no
performance disadvantage. In Example 4–1, the data pointed to by
voltage and current reside in the same single-ported physical
4–6 9.1Altera Corporation
Nios II C2H Compiler User GuideNovember 2009
Loading...
+ hidden pages
You need points to download manuals.
1 point = 1 manual.
You can buy points or you can get point for every manual you upload.