[Icc-430] memcpy() from 16-bit hardware registers

Kris Heidenstrom kris at abbey.co.nz
Thu Feb 8 15:41:28 PST 2007


Richard wrote:

> Kris, you must have the old DNS entry.
> Weird since the website changeover was
> almost a month ago.
> Somewhere your system is still caching it.
>
> Try using the new IP directly: http://198.207.204.28
> and http://198.207.204.28/pub/iccv7430_demo.exe

Thanks, that works.

I've been having an issue with memcpy() and I noticed
that you've improved it slightly with this release, but the
change doesn't solve my problem so I'm posting my
comments here for feedback.

I will first describe the problem, then describe my fix
with code which Imagecraft is welcome to use if you
think it's worthwhile.

The icc430 v7.05 memcpy() always copies one byte at
a time. This isn't very efficient but it simplifies the code
in _memcpy since it needn't worry about alignment issues
(the same loop will copy all four combinations of source
and destination areas being even-aligned or odd-aligned).

Apart from the efficiency, the only problem with this
method occurs when you're trying to memcpy() from
16-bit hardware registers. This is an unusual thing to do,
which is probably why no one has mentioned it before.
If you access a 16-bit hardware register using two 8-bit
accesses, the high-order byte doesn't read correctly (it
always read as 0xFF for me). This is fair enough since
TI's docs state that 16-bit hardware registers must be
read and written using 16-bit-wide instructions, and
normally would be, unless you're using memcpy() to
read them.

In our applications we have generalised diagnostic
support that can read "memory" at any address
range in the device. The data is memcpy()ed into
a RAM buffer then sent upstream over a serial or
SPI link. We may want to read hardware registers
in this way, but using byte-at-a-time memcpy()
doesn't work correctly.

Byte-at-a-time memcpy() presumably wouldn't
work for _writing_ to the hardware registers either,
so if code uses memcpy() to copy an array of words
to registers for bulk initialisation, it would probably
write the wrong values to the registers too.

For our application I have written a memcpy()
equivalent function, aligned_memcpy(), which
uses 16-bit-wide reads at even source addresses.
It is always faster than icc430's memcpy() (except
perhaps if the count is very small) and is fastest
copying even-to-even. If all parameters (source,
dest and nbytes) are even, it uses only 16-bit
reads and writes, so it will also correctly initialise
an array of 16-bit hardware registers from an
array of words. Like normal memcpy() it doesn't
handle overlapping memory areas. Here is the
pseudo-code:

void *memcpy(void *dest, const void *source, unsigned int nbytes) {
 word val16;
 word initial_dest = dest;
 if (nbytes) {
  if ((word)source & 1) { /* If source is odd */
   *(byte *)dest++ = *(byte *)source++;
   --nbytes;
   }
/* Now source must be even */
  if ((word)dest & 1) { /* If dest is odd */
   while (nbytes >= 2) {
    val16 = *(word *)source++;
    *(byte *)dest++ = val16 & 0xFF;
    *(byte *)dest++ = val16 >> 8;
    nbytes -= 2;
    }
   }
  else {     /* If dest is even */
   while (nbytes >= 2) {
    *(word *)dest++ = *(word *)source++;
    nbytes -= 2;
    }
   }
/* Copy the final byte if present */
  if (nbytes)
   *(byte *)dest = *(byte *)source;
  }
 return initial_dest;
 }/*memcpy*/

I haven't compiled the above code; probably some
of the typecasting is not allowed. The intention is
just to show what the code is supposed to do.

Here is the assembly language version. This is written for
ICC430 version 7.05 which passes parameters in R14,
R15 and R12. The parameter passing convention is
different for earlier versions of ICC.

_aligned_memcpy:
;
; void *aligned_memcpy(void *dest, const void *source, unsigned int 
nbytes);
;
; dest   --> R14
; source --> R15
; nbytes --> R12
; R4~11 are not modified
; R13 is modified
;
    push r14    ; Keep dest to restore before return
    tst  r12    ; Any nbytes?
    jeq  amc_done  ; If not
    bit  #1,r15   ; Is source even or odd?
    jeq  amc_src_even ; If even, skip this special case
    mov.b @r15+,0(r14) ; Copy one byte and bump source pointer
    inc  r14    ; Bump dest pointer
    dec  r12    ; Decrement byte count
amc_src_even: bit  #1,r14   ; Source is even now. Is dest is even or 
odd?
    jeq  amc_e2e_test ; If dest is also even
    jmp  amc_e2o_test ; Go to middle of loop
amc_e2o_loop: mov.w @r15+,r13  ; Get 16-bit value from source
    mov.b r13,0(r14)  ; Copy lobyte
    inc  r14    ; Bump destination pointer by one
    swpb r13    ; Get hibyte to lobyte in R13
    mov.b r13,0(r14)  ; Copy hibyte
    inc  r14    ; Bump destination pointer by one again
amc_e2o_test: decd r12    ; Double-decrement nbytes
    jc  amc_e2o_loop ; Loop if more (carry = ~borrow)
    jmp  amc_last_byte ; Handle the final byte if present
amc_e2e_loop: mov.w @r15+,0(r14) ; Copy the word directly and bump 
source pointer
    incd r14    ; Bump dest pointer
amc_e2e_test: decd r12    ; Double-decrement nbytes
    jc  amc_e2e_loop ; Loop if more (carry = ~borrow)
amc_last_byte: incd r12    ; Double-increment nbytes to get actual 
count of bytes remaining to be copied
    jeq  amc_done  ; If zero, there's no final byte
    mov.b @r15,0(r14)  ; Copy the final byte
amc_done:  pop  r14    ; Reinstate original dest parameter as the 
return value
    ret

This code is 43 words long, compared with 11 words
for the simple memcpy(), so might not be appropriate
to use as a standard memcpy().

Kris
--
Kris Heidenstrom         Embedded systems designer / programmer
kris at abbey.co.nz         Abbey Systems Ltd - Telemetry Specialists
Wellington NEW ZEALAND   Voice +64-4-385-6611  Fax +64-4-385-6848



More information about the Icc-430 mailing list