This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

efficient memcpy of far registers

Dear colleagues,

we are using the TINI DS80C400 platform and the KEIL PK51 development tools.
We would like to perform a fast memcpy of some physical CAN registers located in far memory to a buffer also located in far memory.

We use the following code lines:

#pragma moddp2
#include "absacc.h"

unsigned char far *SrcPtr;
unsigned char far *DestPtr;

SrcPtr = &FVAR(unsigned char, 0xFFDBF7);
/* physical register COM15D0 */
/* or alternatively: SrcPtr = (void far*)0xFFDBF7; */
DestPtr = ...;

memcpy(DestPtr, SrcPtr, 8);
/* should copy 8 bytes */

But it does not work!
What's wrong here?

Thanks for any hint.

Raoul

  • I think you have a problem with your chosen memory map.

    From L51_BANK.A51:

    ; Each function gets as a parameter the memory address with 3 BYTE POINTER    *
    ; representation in the CPU registers R1/R2/R3.  The register R3 holds the    *
    ; memory type.  The C51 compiler uses the following memory types:             *
    ;                                                                             *
    ; R3 Value | Memory Type | Memory Class | Address Range                       *
    ; -----------------------+--------------+--------------------------           *
    ;    00    | data/idata  | DATA/IDATA   | I:0x00     .. I:0xFF                *
    ;    01    | xdata       | XDATA        | X:0x0000   .. X:0xFFFF              *
    ;  02..7F  | far         | HDATA        | X:0x010000 .. X:0x7E0000            *
    ;  80..FD  | far const   | HCONST       | C:0x800000 .. C:0xFD0000 (see note) *
    ;    FE    | pdata       | XDATA        | one 256-byte page in XDATA memory   *
    ;    FF    | code        | CODE         | C:0x0000   .. C:0xFFFF              *
    ;                                                                               *
    

    From absacc.h:
    #define FVAR(object, addr)    (*((object volatile far *) ((addr)+0x10000L)))
    

    Incidentally, you could use FARRAY() to get a pointer rather than &FAR().

    0xFFDBF7 + 0x10000 is going to roll over to 0x00DBF7. A tag byte of 0 indicates data memory, not xdata memory. A tag byte of 0xFF indicates code memory, not far xdata memory. The xdata address space can only go up to 0x7EFFFF (which maps to a tag byte of 0x7F).

    Can you remap your CAN registers to a lower address?


    (I suspect Keil suffers from a backward compatibility problem here. It would have been much cleaner if three-byte xdata addresses started with a tag of 0x00.... for xdata bank 0. That means the data tag would have to move up, say 0x7d. But that would mean changing all the libraries and breaking anyone's assembler code that processes C pointers. So we're probably stuck with this particular inefficiency and wart.)

    While I'm on the subject of far memory:

    There's FCARRAY, which returns a (object const *). FARRAY returns (object volatile *). This causes lint errors ("increase in pointer capability") for assigments such as

    object* o = FARRAY (object, addr);

    because o loses the volatile qualifier. This statement should be pretty innocuous, though. If you want to insert a volatile qualifier, there should be an FVARRAY to parallel FCARRAY. (There should also be FCVARRAY for completeness, if we're going to keep up this naming convention.)

    FARRAY should just return a bare pointer without any qualifers.

  • You'll have to be more specific than just stating "it does not work" before anyone can give you meaningful help. How exactly does it fail to work?

  • We first tested memcpy from a source to a destination buffer in the far memory space. This works, even across a 64kb boundary.

    When we set the source start address to 0xFFDBF7 which should be the physical location of a CAN peripheral the copy action is not performed at runtime.

    Copying however works when instead of using pointers we use individual _at_ variables.
    This is however not an efficient solution.

    I suspect that 0xFFDBF7 is not a valid far address, and it seems to me that remapping of the corresponding registers is the only solution. Does this make sense?

    best regards,

    Raoul

  • A good, solid look at the "Pointer Conversions" and "Storage Formats" sections of the C51 manual confirms your suspicions quite nicely. In a nutshell, a generic pointer with the most significant byte equal to 0xff is interpreteted as a pointer to CODE memory, not XDATA.

    Other than writing yourself a function fmemcpy() that takes arguments of type void far *, there's not much that could be done about this, either: it's built into the very concept of generic pointer support, as designed by Keil.

    You have still two other options:

    1) direct moves through far pointers

    2) re-configuration of the CAN part to move it to a different address than the problematic 0xff.....

  • The real problem can be boiled down to this.

    A generic pointer (3-bytes) can address only 16 megabytes of stuff. Using the Keil tools, you can only address 8 megabytes of CPDE and 8 megabytes of XDATA.

    The Dallas 390 and compatible parts have 16 megabytes of CODE and 16 megabytes of XDATA address space.

    To access the CAN register at FF????h you just have to re-map this address range to something in the Keil XDATA address space (02????h-7F????h). These modifications are made in the XBANKING file.

    To map the CAN register to the address range 55????h, all you need to do is modify the far variable access routines in XBANKING to look for 55 in the HI address byte. If found, change it to FF and do the MOVX instruction as normal.

    Jon

  • But would XBANKING.C even be involved at all, for a Dallas 390? In contiguous mode?