ACCESSING DUAL-PORTED MEMORY MAPPED CARDS


Dual-ported memory can be read and written by more than one processor and is an excellent way to asynchronously pass data between them. Typically some locations are used for passing commands or flags, to co-ordinate the exchange
of data.

Since the operating system virtualizes access to main memory, it is necessary to specially define access to physical memory. These techniques also apply to things like video memory, which is accessed at a specific location.

Since QNX runs in protected mode it is necessary to create a selector which points to the physical memory and defines its characteristics. The function qnx_segment_overlay() or qnx_segment_overlay_flags() is used to create the selector. The documentation implies that shm_open() and mmap() should be used since the qnx_* functions are intended only for 16-bit executables, which is untrue.

The macro MK_FP() is used to obtain a pointer to the memory region from the selector. This is considered a "far" reference, even in 32 bit memory models, since this selector is different than the data selector. Be watchful for functions such as memcpy(), which use near pointers, defaulting to the data selector. Use functions such as _fmemcpy() instead.

Frequently it is most convenient to declare a structure which identifies the definition of each byte in the dual ported ram. When defining the structure, ensure the complier is not padding the structure for efficient 32 bit alignment.

Also watch for lengths of integers. Make sure the compiler is using variables which match the size of the variables used in the API of the dual-ported card. For 32 bit QNX compiles with Watcom 10.6:
char is 8 bits
short and unsigned short are 16 bits
int, unsigned, long and unsigned long are 32 bits.
Use typedefs, so that you can easily change the code to match the variables of
a new version of the compiler.

Use volatile in declarations of variables for the dual-ported memory to prevent problems with the optimizer maintaining values in registers, while the other processor is changing data in the dual ported memory.

You may also need to change CMOS settings to make sure that the dual-ported memory region is not cached or shadowed.

The cc option -T1 must be specified for linking to allow access to the physical memory. The program must be run by root. If a regular user must run it, the permissions of the executable must be set so that it runs with root permissions. You can use group privileges to restrict which users can run the executable.

chgrp <group><program>
chown root <program>
chmod 4750 <program>


The following is a sample of code as illustration:

#include <stdio.h>
#include <i86.h>
#include <sys/seginfo.h>

typedef unsigned char BYTE;
typedef unsigned short int WORD;

/* Definition Of I/O Card Data */
#pragma pack (1);  /* make sure compiler doesn't double word align members */
struct card_data {
    BYTE status1;
    WORD inhead1;
    WORD intail1;
    BYTE inbuf1[128];
    WORD outhead1;
    WORD outtail1;
    BYTE outbuf1[128];
    BYTE status2;
    WORD inhead2;
    WORD intail2;
    BYTE inbuf2[128];
    WORD outhead2;
    WORD outtail2;
    BYTE outbuf2[128];
    };
typedef struct card_data CARD_DATA;

long card_address;                  /* Physical address of card */
                                    /* Must be on a 4096 byte page boundary */
long card_size;                     /* Length of dual port memory */
                                    /* Must be a multiple of 4096 bytes */
                                    /* Except below 1M point */
unsigned selector;                  /* Selector to dual port memory */
volatile CARD_DATA __far *card_mem; /* Pointer to base of dual port memory */
                                    /* volatile tells the opimizer that this */
                                    /* data may be changed by external events */
int
main ()
{
    card_address = 0x1800000L;          /* At 24M point */
    card_address = 0x00D0000L;          /* Between Video Ram and System BIOS */
    card_size = 4096;                   /* 4K bytes */
    if (sizeof (WORD) != 2){
        fprintf (stderr, "WORD is not 16 bits !\n");
        exit (1);
    }
    if ((selector = qnx_segment_overlay (card_address, card_size)) == -1){
        fprintf (stderr, "Error - Unable to allocate a selector to dual-ported memory\n");
        exit (1);
    }
    
    /* Convert The Selector To A Far Pointer */
    card_mem = MK_FP (selector, 0);
    /* Read Status Of Channel 2 */
    printf ("%02X\n", card_mem->status2);
    
    return (0);
}



Note: These instructions are for typical situations. Individual configuration may differ. If you have any questions, please contact us for assistance.

Copyright © 1998 Qenesis Inc. All rights reserved.