Section 4.13. Using Shared Memories

4.13. Using Shared Memories

The Impulse C programming model supports the use of shared memories as an alternative to streams communication. Shared memories can be useful for initializing a process with some frequently used array values (for such things as coefficients) and, as described in the previous section, may be a more efficient, higher-performance means of transferring data between hardware and software processes for some platforms. Shared memory interfaces can also be used as generic interfaces to external memory resources not on the system bus. Using memories in this way, however, may require a certain amount of hardware-design expertise to develop the necessary memory controller interfaces.

To demonstrate how shared memory interfaces are described and used, we'll take a fresh look at the HelloFPGA example presented earlier. We'll modify this example so that it makes use of a shared memory as a replacement for one of the stream interfaces, the one that carries the text data from the Producer process to the DoHello process. To start, we'll modify the configuration subroutine such that the first stream (s1) is deleted and replaced with a resource of type co_memory and a corresponding call to the co_memory_create function:

 void config_hello(void *arg) {  // Configuration function   co_memory memory;   co_stream s2;   co_process producer, consumer, hello;   memory = co_memory_create("Memory", "mem0", MAXLEN*sizeof(char));   . . . 

The co_memory_create function is similar to the co_stream_create function it replaces. The co_memory_create function allocates a specified number of bytes of memory for reading and writing and returns a handle that can be passed to any number of processes for read/write access to the memory. The actual reading and writing of the memories is accomplished using the co_memory_readblock and co_memory_writeblock functions, respectively. These functions allow a specified number of bytes of data to be read from or written to a specified offset in a previously declared memory.

Unlike co_stream_create, the co_memory_create function includes a platform-specific identifier that indicates the physical location of the memory resource. The identifier you will use in the second argument to co_memory_create depends on the platform you are targeting. In the case of the generic VHDL platform, the identifier "mem0" represents a generic dual-port synchronous RAM.

Another critical distinction between stream and memory interfaces is that stream interfaces are always synchronized by virtue of the FIFO buffers used to implement them. When passing values from one process to another on streams, the producer and consumer of data on that stream never collide, and data sent by the producer process are never in a "half baked" state from the perspective of the consumer process.

Memories, on the other hand, may require careful synchronization when used for data communication. This may mean that signals or streams (or in some cases lower-level communication lines called registers) will be required in addition to the memory interface.

In the case of HelloFPGA, if you want to replace the stream s1 with an equivalent memory interface, you need to somehow synchronize the behavior of process Producer with process DoHello such that DoHello does not attempt to read data from the shared memory until Producer has finished writing to that memory. You do this by using a signal, as follows:

 co_memory memory; co_signal ready; co_stream s2; co_process producer, consumer, hello; memory = co_memory_create("Memory", "mem0", MAXLEN*sizeof(char)); ready = co_signal_create("Ready"); ss2 = co_stream_create("Stream2", CHAR_TYPE, 2); 

In this excerpt from the configuration subroutine, a signal called ready is created and is used as a means of communicating status from one process (Producer) to the other (DoHello). Because signals can also carry integer values, we will also use this signal to pass a character count (the number of characters in the "Hello FPGA!" string) to the DoHello process.

Figures 4-8 and 4-9 show the resulting source files. In this modified version, notice how the co_signal_post function is used by the Producer process to pass a message to the DoHello process indicating that the memory is ready. The co_signal_post function is a nonblocking operation, so posting this message does not result in the Producer pausing in its operation when the signal is posted.

On the DoHello side of the interface, notice how the co_post_wait function is used as a delaying mechanism, guaranteeing that the contents of the memory (which is subsequently accessed using the co_memory_readblock function) are ready for reading. With this kind of explicit synchronization, it is possible for two processes (such as a software process and a hardware process) to make simultaneous requests to read or write a co_memory block, and the two operations may in fact overlap.

Figure 4-8. HelloFPGA_mem software test bench functions (HelloFPGA_mem_sw.c).
 // HelloWorld_sw.c: Software processes to be executed on the CPU. // // In this version the Producer passes the text via a shared // memory interface instead of on a stream. // #include "co.h" #include <stdio.h> extern co_architecture co_initialize(int param); void Producer(co_memory shared_mem, co_signal ready) {   int32 count;   static char HelloWorldString[] = "Hello FPGA!";   count = strlen(HelloWorldString);   co_memory_writeblock(shared_mem, 0, HelloWorldString, count);   co_signal_post(ready, count); } void Consumer(co_stream input_stream) {   char c;   co_stream_open(input_stream, O_RDONLY, CHAR_TYPE);   while (co_stream_read(input_stream, &c, sizeof(char)) == co_err_none ) {     printf("Consumer read %c from input stream\ n", c);   }   co_stream_close(input_stream); } int main(int argc, char *argv[]) {    int param = 0;    co_architecture my_arch;    printf("HelloFPGA starting...\ n");    my_arch = co_initialize(param);    co_execute(my_arch);    printf("HelloFPGA complete.\ n");    return(0); } 

Figure 4-9. HelloFPGA_mem hardware process and configuration subroutine (HelloFPGA_mem_hw.c).
 // HelloWorld_hw.c: Hardware processes and configuration. // #include "co.h" #define MAXLEN 128 extern void Producer(co_memory shared_mem, co_signal ready); extern void Consumer(co_stream output_stream); // Hardware process: reads from memory, writes to stream void DoHello(co_memory shared_mem, co_signal ready,              co_stream output_stream) {   int32 i, count;   char buf[MAXLEN];   char c;   co_signal_wait(ready, &count);   co_memory_readblock(shared_mem, 0, buf, count);   co_stream_open(output_stream, O_WRONLY, CHAR_TYPE);   for (i=0; i < count; i++) {        c = buf[i];        co_stream_write(output_stream,&c,sizeof(char));   }   co_stream_close(output_stream);   } void config_hello(void *arg) {    // Configuration function   co_memory memory;   co_signal ready;   co_process producer, consumer, hello;   co_stream s2;   memory = co_memory_create("Memory", "mem0", MAXLEN*sizeof(char));   ready = co_signal_create("Ready");   s2 = co_stream_create("Stream2", CHAR_TYPE, 2);   producer = co_process_create("Producer", (co_function) Producer, 2, memory, ready);   hello = co_process_create("DoHello", (co_function) DoHello, 3, memory, ready, s2);   consumer = co_process_create("Consumer", (co_function) Consumer, 1, s2);   co_process_config(hello, co_loc, "PE0");  // Assign to PE0 } 

    Practical FPGA Programming in C
    Practical FPGA Programming in C
    ISBN: 0131543180
    EAN: 2147483647
    Year: 2005
    Pages: 208 © 2008-2017.
    If you may any questions please contact us: