[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