Hi all!
I am trying to implement the macros htons and htonl used for changing the endianness of 2 and 4 bytes words.
Those should be as fast as possible, so I wanted to implement them in ASM, since there exists the instructions for ARM which already do that: REV and REV16.
My question is: how can I implement this in a .c file (actually in a .h, since the functions will be defined as static inline)?
I.e: in my .h file:
//Declare the function static inlined, with code in ASM and register calling convention.
static inline __asm declspec(register_abi) uint32_t htonl(uint32_t x)
{
rev r0, r0
}
All my attempts lead to syntax errors or linkage errors.
I am using CodeWarrior 10.6, and Kinetis K60 as processor
Any help is appreciated.
Best regards,
Ruben
已解决! 转到解答。
Hi Santiago!
Thank you for reply. I was looking for that syntax and behaviour.
I have investigated a bit more about the function __asm, and I have found that it accepts input and output parameters. The problem is that its syntax is quite strange. Since I have not found any documentation nor samples, I will show here, for reference, my experience (I don't know how accurate can it be, since it has been just try and compile....):
__asm("ASM INSTRUCCIONS"
: [asm_var_name1] "mode" (c_var_name1)
: [asm_var_name2] "mode" (c_var_name2)
...
);
Variables in ASM are declared using "%[var_name]".
Mode can be "r" for register passing, "m" for direct memory access. Output must have mode with "=".
Output ASM variable MUST be the first.
So, my final implementation of the functions are:
static __attribute__((always_inline, optimize(3))) inline uint16_t Reverse16(uint16_t u16Source)
{
__asm("REV16 %[output], %[input]"
: [output] "=r" (u16Source) : [input] "r" (u16Source)
);
return u16Source;
}
static __attribute__((always_inline, optimize(3))) inline uint32_t Reverse32(uint32_t u32Source)
{
__asm("REV %[output], %[input]"
: [output] "=r" (u32Source) : [input] "r" (u32Source)
);
return u32Source;
}
This implementation generates just the REV and REV16 instructions, without the need to store / load the variables from memory, neither to know in which register are stored the variables, allowing to inline and optimize the functions.
Best regards,
Ruben
Hi Matthew!
Thank you for your answer.
Sure, I can use those functions ( I actually didn't know they exist). But when I use them I am getting an error:
"undefined reference to '__builtin_bswap16' "
The other one (the 32 bit version) works fine (I don't actually tried it, it just compiles fine).
The 16 bit version could be implemented by shifting a couple of bits, but I don't want to do that, since the processor already has one instruction to do that...
Best regards,
Rubén
Hi
In GCC only __builtin_bswap32(); and __builtin_bswap64(); is present (as per my knowledge) and that work fine for uint32_t. I was looking something similar for signed nos and float numbers. Did you come across something like this for float?
Kind Regards
Amit Kumar
The function__builtin_bswap32() just swaps bytes, it does not care what the bytes represent. Passing the argument and returning the result as a uint32_t is just a convention. You can use the function to swap the bytes of any 32-bit type. The usual way to do this is to use a union to allow you to deal with a collection of bytes in various different ways.
typedef union helper_u {
uint32_t i;
float f;
} helper_t;
For example, if you have 32 bits x that represented a float on a foreign system, and you need to swap the bytes around to get the proper representation of a float y on this system:
helper_t h;
h.i = __builtin_bswap32(x);
y = h.f;
Hi Ruben,
There is a way to include assembler instructions in C code using CodeWarrior. All you need to do is use the asm instruction with the following syntax.
asm(“ASSEMBLER_INSTRUCTION PARAMETER1, PARAMETERn”);
The PARAMETERs will depend on the instruction to call. For example, the NOP instruction does not need any additional parameters so it will be called in the following way:
asm(“NOP”);
Hope it helps
Saludos
Santiago Lopez
Hi Santiago!
Thank you for your answer, but I still have got problems:
"asm" is only recognized if GNU extensions are enabled (I spent quite a time to find out). The one which works without extensions was "__asm". I really don't care to use extensions or not, but without knowing this, it can be quite disapointing (always the same error: symbol not found "asm"...).
Anyway, with extensions enabled, I am facing the problem that I don't know in which register my variables are:
static inline uint16 htons(uint16 x)
{
asm("rev r0, r0"); //<-- How can I know that "x" is actually in r0?
return x; // <-- The same here, how can I know that r0 is actually in "x"?
}
So, my next question is: how can I load from a C variable to an actual register? Is because of this that I introduced in my previous post the register calling convention ("declspec(register_abi)"). So, how can I force this?
Thank you in advance,
Best regards,
Ruben
Hi Ruben,
In CodeWarrior, when you call a function that includes parameters, these are stored in the first CPU registers in calling order. For example, if you call the following function:
TheFunction (Parameter1, Parameter2);
Those parameters are stored in the following way:
R0 = Parameter 1
R1 = Parameter 2
Now, the load (LDR) and store (STR) instructions of ARM require an address in order to load/store a value into a memory location. Knowing this, you can implement a function that sends the data to reverse in a first parameter and the memory address of the variable where it will be stored in a second parameter. Then execute the respective ASM instructions to reverse and store the data.
The following code implements both, REV and REV16 instructions using __asm(). You will notice that we are taking the data to reverse from R0 since the first parameter is the data. Then we are storing the information on the address pointed by R1 (second parameter). In both cases, we are using 0x11223344 so you can see how byte reversing (REV) and half-word reversing (REV16) works in ARM. give it a try and let us know if it worked.
Saludos
Santiago Lopez
void inline Reverse8 (uint32_t u32Source, uint32_t* pu32Destination);
void inline Reverse16 (uint32_t u32Source, uint32_t* pu32Destination);
int main(void)
{
uint32_t A,B;
uint32_t C,D;
A = 0x11223344;
B = 0x00000000;
Reverse8(A, &B);
C = 0x11223344;
D = 0x00000000;
Reverse16(C, &D);
__asm("BKPT");
return 0;
}
void inline Reverse16 (uint32_t u32Source, uint32_t* pu32Destination)
{
__asm("REV16 R3, R0"); //Reverse Half-Word order
__asm("STR R3, [R1]"); //Store value on destination address
}
void inline Reverse8(uint32_t u32Source, uint32_t* pu32Destination)
{
__asm("REV R3, R0"); //Reverse Byte order
__asm("STR R3, [R1]"); //Store value on destination address
}
Hi Santiago Lopez,
In your reply,
'TheFunction (Parameter1, Parameter2);
Those parameters are stored in the following way:
R0 = Parameter 1
R1 = Parameter 2',
why the parameter1 is passed to R0,and the parameter2 is passed to R1?Can you give me some recommended Docs or links about this topic?
Regards,
Frank
Hi Santiago!
Thank you for reply. I was looking for that syntax and behaviour.
I have investigated a bit more about the function __asm, and I have found that it accepts input and output parameters. The problem is that its syntax is quite strange. Since I have not found any documentation nor samples, I will show here, for reference, my experience (I don't know how accurate can it be, since it has been just try and compile....):
__asm("ASM INSTRUCCIONS"
: [asm_var_name1] "mode" (c_var_name1)
: [asm_var_name2] "mode" (c_var_name2)
...
);
Variables in ASM are declared using "%[var_name]".
Mode can be "r" for register passing, "m" for direct memory access. Output must have mode with "=".
Output ASM variable MUST be the first.
So, my final implementation of the functions are:
static __attribute__((always_inline, optimize(3))) inline uint16_t Reverse16(uint16_t u16Source)
{
__asm("REV16 %[output], %[input]"
: [output] "=r" (u16Source) : [input] "r" (u16Source)
);
return u16Source;
}
static __attribute__((always_inline, optimize(3))) inline uint32_t Reverse32(uint32_t u32Source)
{
__asm("REV %[output], %[input]"
: [output] "=r" (u32Source) : [input] "r" (u32Source)
);
return u32Source;
}
This implementation generates just the REV and REV16 instructions, without the need to store / load the variables from memory, neither to know in which register are stored the variables, allowing to inline and optimize the functions.
Best regards,
Ruben