In a header I was looking at, I saw this...
... is that valid? I don't recall seeing a macro like that before.Code:#define MMM_X(x)_Z(z) (0x0C + (((x)-1)*3 + ((z)-1))*4)
Printable View
In a header I was looking at, I saw this...
... is that valid? I don't recall seeing a macro like that before.Code:#define MMM_X(x)_Z(z) (0x0C + (((x)-1)*3 + ((z)-1))*4)
Looks strange, I've never seen anything like that, actualy... Does it compile?
--
Mats
Why not use 12 instead of 0x0C? I've never understood why people use hex when decimal is simpler. Yeah, that does look weird.
What on earth is that supposed to do?
That ain't right whatever it is. Even if _Z(x) was another macro that did something, it looks malformed. And if it does do something it needs to be rewritten free of stupid so we know what the hell is going on.
> Why not use 12 instead of 0x0C? I've never understood why people use hex when decimal is simpler.
Some people are nerds. There are uses for hex values though (it can be convenient if you're talking about bits or colors), but don't mix radices like that.
I may be a nerd, but to me, there's really no difference between hex or decimal, but of course, it's not particularly easy to translate 0x1267 to 4711 decimal to me either. But common numbers such as 0x0C or 0x100, 0x200, 0xFFFF, I don't see a problem with.
There are different reasons to use one or another. In this case, perhaps the 0x0C is a register/offset/other-constant defined as 0x0C by some documentation?
--
Mats
The kernel build completes without error.Quote:
Does it compile?
As I said in the original post, it is part of a kernel header, a large one, in which dozens of register values are defined, bit masks and other offsets. All of these are in hex. If I convert this line to decimal, I will be adding a source of error, since it will be assumed to be 0x12 to a non-careful reader.Quote:
Why not use 12 instead of 0x0C?
What "the hell" is going on, is at a base address, begins a list of registers, (in an ARM9 processor), dedicated to a particular function. At offset 0x0C, a repeating structure begins whereby there are 5 sets of 3 registers dedicated to subchannels in an on chip communications controller, or at least, that is what the macro is trying to do.Quote:
we know what the hell is going on.
Thus, assuming a 0x00 start address and port 1 register 1...
MMM_X(1)_Z(1);Code:#define MMM_X(x)_Z(z) (0x0C + (((x)-1)*3 + ((z)-1))*4)
(X-1)*3 = 0
(Z-1)*4 = 0
... therefore the address of port 1 register 1 is 0x00 + 0x0C + 0 + 0 i.e. 0x0C.
That is what I believe the designer of the header wishes to acheive, reading the datasheet of the chip, it certainly makes sense.
Regardless of the proclivity for embedded designers to work in hex, as we do, and the failure of GCC to complain, the question really remains the same. Is it legal?
I suspect not, but I tend to avoid the pre-processor and have done for years. I don't know for sure.
How about using "gcc -E" to output the preprocessed stuff and see what actually comes out?
--
Mats
It would be "clearer" if the missing space had been retained:
It's a macro of a single argument, x. _Z is presumably some other macro which reduces to a function name or function pointer.Code:#define MMM_X(x) _Z(z) (0x0C + (((x)-1)*3 + ((z)-1))*4)
Not particularly mysterious.
Apart from name changes to "protect the innocent" that is as it appears in the header, there is NO space in the macro definition.Quote:
It would be "clearer" if the missing space had been retained:
Your explanation does, in no way, reflect the actual requirement. If what you are suggesting is correct, then the author is way off. Clearly, 2 arguments are necessary to return the correct register offset.
I didn't say there was.
Someone's confused, but it's not me:Quote:
Your explanation does, in no way, reflect the actual requirement. If what you are suggesting is correct, then the author is way off. Clearly, 2 arguments are necessary to return the correct register offset.
Which is nonsense. Unless of course there is a macro called _Z.Code:File mac1.c:
#define MMM_X(x)_Z(z) (0x0C + (((x)-1)*3 + ((z)-1))*4)
int main()
{
int x, z;
MMM_X(1)_Z(2);
}
$ gcc -E mac1.c
int main()
{
int x, z;
_Z(z) (0x0C + (((1)-1)*3 + ((z)-1))*4)_Z(2);
}
...Quote:
I didn't say there was.
Your implication was that a space had been omitted - that is not the case.Quote:
It would be "clearer" if the missing space had been retained:
Consider, also, the actual requirement, which I have detailed.
I have already stated that I believe the construct to be dubious, however, it does not alter the fact that it is in a kernel header applicable to my current project, indeed, I now need to modify these registers in that switch, hence my interest.
Ok, so given what brewbuck points out (which is that the macro does work correctly).
Put a simpler way, this is LEGAL, even if it's not pretty, and will expand as follows
becomesCode:#define a(x)y(x)
int main() {
a(7);
....
So, yes, it's legal and works correctly - I'm pretty sure other compilers will also work OK with this construction, not just GCC.Code:int main() {
y(7);
....
However, I would insert a space there if I was modifying the file anyways.
--
Mats
so would it be right to conclude from this then that a closing bracket in a macro name delimits the end of that macro's name (and the start of its definition)? Interesting... can anyone confirm if this complies with the ISO99 definition of a C macro?
I have no idea about what the standard says, but yes, it makes some sense that it is that way - spaces are really only used to delimit things when there is NOTHING else to go by - you can certainly use other constructs without spaces when there is a different delimiter...
I don't think there's any way to "use" the fact that there's no space between the macro name and it's definition...
--
Mats
It is the case. A space SHOULD have been there for clarity. It's not. Thus, everybody wonders what this "weird new syntax is."
The requirement doesn't change the fact that there is NO SUCH MACRO SYNTAX as A(x)B(y). Running the macro through the preprocessor proves it definitively.Quote:
Consider, also, the actual requirement, which I have detailed.
The programmer was simply trying to be one of the "Three C's": Cute, Clever, or Confusing.
The only use of this type of macro
Which i can see is that; you call this macro with two name such as MMM_X and Z. What do you all tthink. Is there any other use of using that?Code:#define MMM_X(x)_Z(z) (0x0C + (((x)-1)*3 + ((z)-1))*4)
ssharish2005
There are no other macros defined in the header. All the other defines are either constant base addresses, register offsets or bit masks.
I really don't get what your question is anymore. You asked if the macro is valid. You also say it compiles. This pretty much proves the macro is "valid" in that sense. But this doesn't mean there is some kind of macro syntax A(x)B(y) which takes two arguments x and y. It would be written AB(x, y) instead. The preprocessor proves THIS.
So what we have here is some bizarre code with unclear motivations. In other words, bad code.
Subject to a few conditions, this is valid. Although it would be more clearly expressed with an additional space before the _Z, that space is not mandatory.
As to what it does: that depends on what _Z actually is. In practice, it is probably another macro and z is probably a variable that is accessible at the point where the macro MMM_X is used (eg a global).
That wasn't an additional question, simply a statement of fact.Quote:
I really don't get what your question is anymore.
That line is the only line in the header which contains macro definition, all other lines are constant addresses, register offsets or bitmasks. There are no global variables.Quote:
As to what it does: that depends on what _Z actually is. In practice, it is probably another macro and z is probably a variable that is accessible at the point where the macro MMM_X is used (eg a global).
Okay, I am in the office now and have had the chance to play with it a little.
This program, with the odd looking macro construction does not compile. I get the following errors from gcc...Code:#include <stdio.h>
#define MMM_P(x)_C(z) (0x0C + (((x)-1)*3 + ((z)-1))*4)
#define OFFSET (0xF0000 + 0xe800)
int main()
{
int i;
int j;
unsigned int x;
for (i=1; i<6; i++)
{
for (j=1; j<4; j++)
{
x = OFFSET + MMM_P(i)_C(j);
printf("Port %i Register %i addr : %08X\n", i,j,x);
}
printf("\n");
}
return 0;
}
This program, with a more traditional looking macro definition does compile...Code:junk2.c: In function ‘main’:
junk2.c:15: error: ‘z’ undeclared (first use in this function)
junk2.c:15: error: (Each undeclared identifier is reported only once
junk2.c:15: error: for each function it appears in.)
junk2.c:15: error: called object ‘_C(<erroneous-expression>)’ is not a function
junk2.c:15: error: expected ‘;’ before ‘_C’
... and gives the expected output.Code:#include <stdio.h>
#define MMM_P_C(x,z) (0x0C + (((x)-1)*3 + ((z)-1))*4)
#define OFFSET (0xF0000 + 0xe800)
int main()
{
int i;
int j;
unsigned int x;
for (i=1; i<6; i++)
{
for (j=1; j<4; j++)
{
x = OFFSET + MMM_P_C(i,j);
printf("Port %i Register %i addr : %08X\n", i,j,x);
}
printf("\n");
}
return 0;
}
The strange macro construct does not prevent the program compiling if it is not used.Code:Port 1 Register 1 addr : 000FE80C
Port 1 Register 2 addr : 000FE810
Port 1 Register 3 addr : 000FE814
Port 2 Register 1 addr : 000FE818
Port 2 Register 2 addr : 000FE81C
Port 2 Register 3 addr : 000FE820
Port 3 Register 1 addr : 000FE824
Port 3 Register 2 addr : 000FE828
Port 3 Register 3 addr : 000FE82C
Port 4 Register 1 addr : 000FE830
Port 4 Register 2 addr : 000FE834
Port 4 Register 3 addr : 000FE838
Port 5 Register 1 addr : 000FE83C
Port 5 Register 2 addr : 000FE840
Port 5 Register 3 addr : 000FE844
Well, there you go.
That is exactly what I'd expect from the two code-snippets you show - that is, that one of them contains an invalid macro. I suspect also that you will find a macrosomeplace in your code - this may NOT be in the same header-file that you have the MMM macro tho' (e.g. it may be in the file that USES the latter macro, and it may even differ from one function to another!Code:#define _Z(z) int z = something;
[Obviously, the code-snippet I've shown here is absolutely a guess, and it may well be quite different in all sorts of ways - but I'm 100% sure that such a macro definition exists, and that it most likely declares a local variable!]
--
Mats
I've just done a grep on my entire Linux tree structure, (and as it caters for about 40 different architectures - that is a lot!), looking for ' _C(' and it returned nothing.
If there was a second macro declaring z how would the value of the control register offset get into the calculation? This is an obvious requirement after all.
Since this is a relatively new chip, it is entirley possible this code has never been used before and that it is a bug. This afternoon, I will be recompiling the OS with some new device drivers that I am still working on. I will try to use the macro there and see if the kernel build fails.
We have been using the chip in another product, but that did not require the Ethernet switch to be programmed, it was using the chip's PCI bridge to control IEEE1394, (FireWire (tm)), chips.
So, could you tell us which driver this belongs to, as you've already explained that it's the Linux kernel that it belongs to?
--
Mats
I get these errors from a kernel build when I try to use the "strange" macro construction in a device driver. If there is another macro/function it is sufficiently well hidden that a regular OS make can't find it.
Code:drivers/dxl/cardid.c: In function `cardid_ioctl':
drivers/dxl/cardid.c:173: warning: implicit declaration of function `_C'
drivers/dxl/cardid.c:173: error: `z' undeclared (first use in this function)
drivers/dxl/cardid.c:173: error: (Each undeclared identifier is reported only once
drivers/dxl/cardid.c:173: error: for each function it appears in.)
drivers/dxl/cardid.c:173: error: called object is not a function
drivers/dxl/cardid.c:173: error: syntax error before "_C"
It is not a driver, that is what I am writing! It is the addresses, register offsets and bit masks for configuration of the onboard 5 port ethernet switch on a Micrel KS8695PX chip. This, and dozens of other header files came with the developers kit I think. The guy that built it into our "buildroot" embedded Linux system no longer works here so I am not 100% sure.Quote:
So, could you tell us which driver this belongs to
LOL. Yes, it compiles and builds fine until I try and use it, at that point, it gives the errors I posted. Sorry, should have been clearer. I am using other values from the same header without issue, only when I try to obtain the offset to a port control register using the macro does it fail.
Ehm, yes, and there's something broken somewhere, either the macro itself is broken, or there's a missing _C macro somewhere (perhaps there used to be one somewhere else and it got lost at some point). I'd contact the producer of that driver, as it appears that the source isn't on the web - if I google for dxl/cardid.c, it comes up with very little useful stuff.
--
Mats
I'd be suprised if it came up with anything at all, cardid.c is one of my drivers and I only wrote it last week! The header comes with the developers kit for the KS8695PX chip.
I will write a patch for the header and submit it to the maintainer of that particular branch of the kernel.