Issue
This is a code I used to write some data in memory for debugging (until printf is available in u-boot program). Variable myptr
is located in .__mydebug
section and it is incremented by 8 after every 8-byte write and I want to write any value I'm interested in in the form of {debug_tag, debug_value}
pair. Here debug_tag
is some value to show the debug data sequence, and the debug_value
is the value I want to check(or see) during the debug. This is arm64 assembly.
.global myptr
ldr x28, =myptr /* load the address of myptr */
add x28, x28, #8 /* set write pointer to the next address after the myptr variable */
mov x27, #0x33 /* first debug write starts with tag value 0x33 */
str x27, [x28], #8 /* write the tag value, increment the pointer */
mov x27, some_value /* some_value : the value I want to see with tag value 0x33 */
str x27, [x28], #8 /* write the debug value, increment the pointer */ ldr x26, =myptr /* load pointer address */
/* next debug write, in the same assembly code, x28 hasn't changed, so use as is */
mov x27, #0x34 /* new debug tag */
str x27, [x28], #8 /* write new tag, increment pointer */
mov x27, some_another_value /* another data I want to check */
str x27, [x28], #8 /* write the data, increment pointer */
ldr x26, =myptr /* load the address of myptr to x26 */
str x28, [x26] /* save the updated pointer in myptr, just in case x28 is modified and \
the pointer should be used later in assembly or C code .. */
.... (skip) ....
.section .__mydebug
myptr: .double 0x0
data_start: .double 0x0
So this is sequentially writing debug info in a memory section which works.
I can continue this debug write later in a .c program as below and it also works.
// debug print
int xx=sizeof(struct global_data);
*((uint64_t *)myptr) = 0x101; myptr+=8; /* debug tag start with 0x101 here */
*((uint64_t *)myptr) = xx; myptr+=8; /* write some data I want to check.. */
*((uint64_t *)myptr) = 0x102; myptr+=8; /* another debug tag */
*((uint64_t *)myptr) = base; myptr+=8; /* another value I want to check */
Ok, I can live with that. But this doesn't look nice and inconvenient.
So I'm curious how I can do the above in C program using a function with inline assembly. I want to pass to the function the tag value and the debug value(64-bit) as arguments. The function should retrieve the myptr
value to write the tag and data and should update the myptr
value each time. I tried writing a function below.
void dbg_print(unsigned int tag, uint64_t data)
{
uint64_t ptr_addr1;
__asm (
"ldr %[ptr_addr], =myptr" \
"ldr %[ptr_val], [%[ptr_addr]]" \
"str %[tag_val], [%[ptr_val]], #8" \
"str %[data_val], [%[ptr_val]], #8"
: /* no output */ \
: [tag_val] "r" (tag), [data_val] "r" (data) /* input list */ \
: "memory" /* no specific clobbered register, but memory modified */
);
}
When I compile it, I get this compile error.
common/init/board_init.c: In function 'dbg_print':
common/init/board_init.c:144:1: error: undefined named operand 'ptr_addr'
144 | );
| ^
common/init/board_init.c:144:1: error: undefined named operand 'ptr_val'
common/init/board_init.c:144:1: error: undefined named operand 'ptr_addr'
common/init/board_init.c:144:1: error: undefined named operand 'ptr_val'
common/init/board_init.c:144:1: error: undefined named operand 'ptr_val'
make[2]: *** [scripts/Makefile.build:254: spl/common/init/board_init.o] Error 1
make[1]: *** [scripts/Makefile.spl:515: spl/common/init] Error 2
I can't understand undefined named operand
error. Do I need to define the operand in the template somewhere? In the example in https://www.keil.com/support/man/docs/armclang_ref/armclang_ref_qbn1517569205870.htm, the operands in the template are just used without defining. The variables in C is declared anyway but aren't the operands in the assembly template substituted by the compiler anyway?
Thank you for reading and I would be grateful if someone could clarify this thing to me.
ADD :
Having read Nate Eldredge and Peter Cordes's comments, I realized 'defining the operand' means to connect the value into the C world using the operand specifiers (those fields after : and connected with :). So I tried changing the code to this and I'll see if this works.
void dbg_print(unsigned int tag, uint64_t data)
{
uint64_t ptra, ptrv;
__asm (
"ldr %[ptr_addr], =myptr \n" /* get pointer address */
"ldr %[ptr_val], [%[ptr_addr]] \n" /* get pointer value */
"str %[tag_val], [%[ptr_val]], #8 \n" /* write to pointed addr */
"str %[data_val], [%[ptr_val]], #8 \n" /* write to pointed addr */
: [ptr_addr] "=r" (ptra), [ptr_val] "=r" (ptrv)
: [tag_val] "r" (tag), [data_val] "r" (data) /* input list */
: "memory" /* no specific clobbered register, but memory modified */
);
}
Solution
I can't understand undefined named operand error. Do I need to define the operand in the template somewhere? In the example in https://www.keil.com/support/man/docs/armclang_ref/armclang_ref_qbn1517569205870.htm, the operands in the template are just used without defining.
Yes, you need to define them either in the inputs or the outputs of the asm statement. The examples you link all define all the names they use. Your code just defines tag_val
and data_val
there, so ptr_val
and ptr_addr
are undefined.
Answered By - Chris Dodd