4.7. Understanding StreamsStreams are unidirectional communication channels that connect multiple processes, whether hardware or software. The co_stream_create function creates a stream, defines its data width and its buffer size, and makes the stream available for use in subsequent co_process_create calls. The following is an example of a stream being created with co_stream_create: strm_image_value = co_stream_create("IMG_VAL", INT_TYPE(16),BUFSIZE); There are three arguments to the co_stream_create function. The first argument is an optional name that may be assigned to the stream for debugging, external monitoring, and post-processing purposes. This name has no semantic meaning in the application but may be useful for certain downstream synthesis or simulation tools. If application monitoring will be used (as indicated by any use of cosim_ monitoring functions, which are described in a later chapter), stream names are required, and the chosen stream names must be unique across the entire application.
The second argument specifies the type and size of the stream's data element. Macros are provided for defining specific stream types, including INT_TYPE, UINT_TYPE, and CHAR_TYPE. The third and final argument to co_stream_create is the buffer size. This buffer size directly relates to the size of the FIFO buffer that will be created between two processes that are connected with a stream. A buffer size of 1 indicates that the stream is essentially unbuffered; the receiving process will block until the sending process has completed and moved data onto the stream. In contrast, a larger buffer size will result in additional hardware resources (registers and corresponding interconnect resources) being generated, but may result in more efficient process synchronization. As an application designer, you will choose buffer sizes that best meet the requirements of your particular application. When choosing buffer sizes, keep in mind that the width and depth of a stream (as specified in the call to co_stream_create) will have a significant impact on the amount of hardware required to implement the process. You should therefore choose a stream buffer size that is as small as practical for the given process. In the following example a buffer size of four has been selected for all three streams, as indicated in the definition of BUFSIZE: #define BUFSIZE 4 void my_config_function(void *arg) { co_stream host2controller,controller2pe,pe2host; co_process host1, host2; co_process controller; co_process pe; host2cntrlr=co_stream_create("host2cntrlr", INT_TYPE(32), BUFSIZE); cntrlr2pe=co_stream_create("cntrlr2pe", INT_TYPE(32), BUFSIZE); pe2host=co_stream_create("pe2host", INT_TYPE(32),BUFSIZE); host2=co_process_create("host2", (co_function)host2_run, 1, pe2host); pe=co_process_create("pe", (co_function)pe1_proc_run, 2, cntrlr2pe, pe2host); cntrlr=co_process_create("cntrlr", (co_function) cntrlr_run, 2, host2cntrlr, cntrlr2pe); host1=co_process_create("host1",(co_function)host1_run, 2, host2cntrlr, iterations); co_process_config(cntrlr, co_loc, "PE0"); co_process_config(pe, co_loc, "PE0"); } Stream I/OReading and writing of processes is performed within the process run functions that form an application. Each process run function in a dataflow- oriented Impulse C application iteratively reads one or more input streams and performs the necessary processing when data is available. If no data is available on the stream being read, the process blocks until such time as data is made available by the upstream process. Other, non-dataflow processing models are supported, including message-based process synchronization. These alternate models are described in subsequent sections. At the start of a process run function, the co_stream_open function resets a stream's internal state, making it available for either reading or writing. The co_stream_open function must be used to open all streams (input and output) prior to their being read from or written to. An example of a stream being opened is as follows: err = co_stream_open(input_stream, O_RDONLY, INT_TYPE(32)); The co_stream_open function accepts three arguments: the stream (which has previously been declared as a process argument of type co_stream), the type of stream (which may be O_RDONLY or O_WRONLY), and the data type and size, as expressed using either INT_TYPE, UINT_TYPE, or CHAR_TYPE. Streams are point-to-point and unidirectional, so each stream should be opened by exactly two processes, one for reading and the other for writing. If a stream being opened with co_stream_open has already been opened, the co_stream_open function returns the error code co_err_already_open. When the stream is no longer needed, it may be closed using the co_stream_close function: err = co_stream_close(input_stream); The co_stream_close function writes an end-of-stream (EOS) token to the output stream, which can then be detected by the downstream process when the stream is read using co_stream_read. The co_stream_read function returns an error when the EOS token is received, indicating that the stream is closed. If a stream being closed is not open (or has already been closed), the co_stream_close function returns the error code co_err_not_open. Keep in mind that all streams must be properly closed by the reading and writing process. In particular, note that the reading process blocks when it calls co_stream_close until the EOS has been received, indicating that the upstream proess has also closed the stream. Once closed, a process can open a stream again for multiple sessions. Note Stream connections between processes must be one-to-one; broadcast patterns are not supported, and many-to-one connections are not supported. It is possible to create such data distribution patterns in an application, however, by creating intermediate stream collector and distributor processes. |