Section 4.6. Understanding Processes


4.6. Understanding Processes

Processes are the fundamental units of computation in an Impulse C application. Once they have been created, assigned, and started, the processes in an application execute as independently synchronized units of code on the target platform.

Programming with Impulse C processes is conceptually similar to programming with threads. As with thread programming, each Impulse C process has its own control flow, is independently synchronized, and has access to its own local memory resources (which will vary depending on the target platform). For this reason it is relatively easy to convert applications written in threaded C (for example, using the Posix thread library) to Impulse C. There are some key differences, however:

  • In thread programming, it is assumed that globals and heap memory are shared among threads. In Impulse C, heap memory may be explicitly shared, but global variables are not supported in general.

  • In thread programming, the threads are assumed to execute on the same processor. In Impulse C, the assumption is that each process is assigned to an independently synchronized processor or block of logic.

  • The Impulse C programming model is specifically designed to support mixed hardware and software targets, with process communication and synchronization occurring primarily in hardware buffers (FIFOs). In thread programming, threads often communicate implicitly using shared data structures and semaphores.

  • The Impulse C programming model assumes that processes are defined and created at the time the application is initialized and loaded, rather than dynamically created, invoked, and torn down as in a threaded application.

The Impulse C desktop simulation library is based on a threading model, so global variables and heap memory are shared. Also, during simulation all Impulse C processes are executed on one processing element (your desktop computer), although this may not be the case on the target platform. It is your responsibility as the programmer to avoid using global variables and heap memory allocated outside of the Impulse C library.

Note

In desktop simulation, when all processes are translated to software running on a single processor, the scheduling of instructions being executed within each process is predictable, but the scheduling of instructions across processes depends on the threading model used in the host compiler and/or the host operating system. This can result in behavioral differences between software and hardware implementations of an application. In particular, your application must not assume that one process will start and execute before another process starts and executes. If such process synchronization is required, you should use signals, which are described later in this chapter, or rethink your application's use of streams.


What is meant by "timed" and "untimed" C?

When applied to C-to-hardware programming methodologies, the terms "timed" and "untimed" refer to a requirement (or lack thereof) for the application programmer to understand and specify where the clock boundaries are within a given sequence of C statements. If the programmer must explicitly define these these boundaries, or if by definition each C statement or source file line represents a distinct clock cycle, the programming model is that of timed C. In such an environment, the programmer is given a great deal of control over how an application is mapped across clock boundaries, but this level of control also means significantly more work for the programmer, who must manually insert information into the design to indicate how otherwise-sequential statements are to be made parallel. This work is not trivial and can result in C code representing hardware that is not functionally equivalent to the original description as rendered in untimed C.

Impulse C represents an untimed method of expressing applications. In this programming model, statements within a process are optimized with the goal of minimizing the total number of instruction stages. Only more-general controls (in the form of compiler pragmas and external tool options) are provided for influencing the optimizer and meeting size/speed requirements. Impulse C provides no way to relate a specific C-language statement to a specific clock event, or to introduce any other such low-level hardware concept (such as a clock enable), except when such concepts have a direct and defined relationship to standard C coding styles.

The advantage of an untimed approach is that it more closely emulates a software programming experience. Applications can be expressed at a higher level and can then (perhaps at a much later time) be optimized through the addition of more hardware-centric compiler hints and C-level optimizations. The disadvantage, of course, is that a hardware-savvy programmer may not have the ability to precisely control the generation of hardware. As a result, he or she may be required to drop into lower-level HDL programming for some parts of the application if such a level of control is required.


Creating Processes

Processes are created and named in the configuration function of your application. In the following example, the my_app_configuration function declares three processes (procHost1, procPe1, and procPe2) and associates these processes with three corresponding process run functions named Host1, Pe1, and Pe2:

 #define BUFSIZE 4 void my_app_configuration() {   co_process procHost1, procPe1, procPe2;   co_stream s1, s2;   s1 = co_stream_create("s1", INT_TYPE(16), BUFSIZE);   s2 = co_stream_create("s2", INT_TYPE(16), BUFSIZE);   procHost1 = co_process_create("Host1", (co_function)Host1, 1, s1);   procPe1 = co_process_create("Pe1", (co_function)Pe1, 2, s1, s2);   procPe2 = co_process_create("Pe2", (co_function)Pe2, 1, s2); } 

The co_process_create function is used to define both hardware and software processes. Unless otherwise assigned, all processes created using the co_create_process function are assumed to be software processes. The function accepts three or more arguments. The first argument must be a pointer to a character string (NULL terminated) containing a process name. This name does not have to match the variable name used within the process itself; it is only used as a label when monitoring the process externallyfor example, using the application monitor.

The second argument to co_process_create is a function pointer of type co_function. This function pointer identifies the specific run function that is to be associated with (or instantiated from) the call to co_process_create.

The third argument to co_process_create indicates the number of ports (inputs and outputs) that are defined by the process. This number must match the number of actual port arguments that follow and must also match the parameters of the specified run function. For example, if the number of ports is two, there must be two ports declared as arguments four and five.

Tip

Specifying the wrong number or wrong order of ports in the co_process_create function is a common mistake and can be difficult to debug. This is particularly true when connecting streams, which may appear to compile properly, yet exhibit incorrect behavior during simulation. Check these connections carefully.


Port arguments specified for an Impulse C run process may be one of five distinct types:

  • co_stream A streaming poin t-to-point interface on which data is transferred via a FIFO buffer interface.

  • co_signal A buffered point- to- point interface on which messages may be posted by a sending process and waited for by a receiving process.

  • co_memory A shared memory interface supporting block reads and writes. Memory characteristics are specific to the target platform.

  • co_register A low-level, unbuffered hardware interface.

  • co_parameter A compile-time parameter.

Variables of these types must be declared in the configuration function and (in the case of co_stream, co_signal, co_register, and co_memory) the appropriate co_xxxxx_create function must be used to create an instance of the desired communication channel.

When your application creates a process using the co_process_create function, the process is configured to run, and it begins execution, when the co_execute function is called at the start of the program's execution. This is a fundamental difference between thread programming and programming in Impulse C: in Impulse C, the entire application and all its parallel processing components are set up in advance to minimize runtime overhead.



    Practical FPGA Programming in C
    Practical FPGA Programming in C
    ISBN: 0131543180
    EAN: 2147483647
    Year: 2005
    Pages: 208

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net