#define IO_STATUS_CMD_MASK 0x70000000 #define IO_STATUS_CMD_BIT 28 #define IO_STATUS_FLAG_MASK 0x01 while (*pio_status & IIO_STATUS_FLAG) {}; *pio_status=(*pio_status & ~IO_STATUS_CMD_MASK) | ((command << IO_STATUS_CMD_BIT) & IO_STATUS_CMD_MASK); Probably, some of the brackets are superfluous here, if you remember the sequence of operations, but I have always held the opinion that brackets again do not cost anything at the execution stage, and it is easier to put an extra pair than remember the corresponding priority tables or God forbid be wrong. What do we see? In the first line, we tell the compiler that three consecutive bits (theoretically, there may be fields of non-consecutive bits, but this is already very similar to a perversion, but we are still normal people) form a single field. #define DATA_SET(ADR,MASK,BIT,DATA) (ADR)=((ADR) & ~(MASK)) | (((DATA) <<(BIT)) & (MASK)) DATA_SET(*pio_status,IO_STATUS_CMD_MASK,IO_STATUS_CMD_BIT,command); Since the post is not over, this solution has flaws - show them. First, the first two lines are obviously interconnected; nevertheless, they exist differently from each other, and we must monitor their own correspondence. It is easy to write a macro, which of the bit mask will make a bit mask with a single low bit. #define LOWBIT(MASK) (((~(MASK)-1)) & (MASK)) #define LOWBIT(MASK) (~(MASK << 1) & (MASK)) #define LOWBIT(MASK) (~(MASK)+1) & (MASK) , harder (I did not succeed), a beautiful macro that highlights the number of the least significant bit. But such a bit mask is enough for us, and the code for transmitting the value macro will change slightly #define DATA_SET(ADR,MASK,DATA) (ADR)=((ADR) & ~(MASK)) | (((DATA) * LOWBIT(MASK)) & (MASK)) DATA_SET(*pio_status,IO_STATUS_CMD_MASK,command); An attentive reader will ask, what about efficiency? In the old version there was a shift, which in ARM is done per clock, and in the new multiplication (and, as is required for reading, and division)? Well, if we set constant values, the macro will collapse, and if the command field is a variable? Fortunately, compilers are designed by truly talented people, and if we include at least the optimization level of the average, then multiplication and division by a constant from one bit turns into an appropriate number of shifts (at least for me). This (or similar) version of the code is widely distributed and presented in various libraries of programs for working with device registers (BSP). From the point of view of the author, his need to use a macro to refer to the field (I don’t even want to consider the option of directly using line 5 of the first example in the text is simply monstrous) and the need to carefully monitor the mask values ​​used in macros, which is not make offensive type errors DATA_SET(*pio_status,IO_STATUS_FLAG_MASK,command); , because the compiler cannot verify anything for us. Of course, you can write another macro #define IO_CMD_SET(DATA) DATA_SET(*pio_status,IO_STATUS_CMD_MASK,DATA) IO_CMD_SET(command); , but very quickly there will be a bit of such macros and the possibility of an error will arise again Another disadvantage is the lack of type checking, that is, an operation like IO_CMD_SET(36); the compiler will not cause any doubts, although the result of execution may unpleasantly surprise you. #pragma bitfields = disjoint_types // , #pragma bitfields = reversed // , Then the structure of the register in question can be described as follows: #pragma bitfields=reverse typedef struct { unsigned :1; // (31) unsigned code:3; // (30..28) unsigned : 27; // 27 (27..1) unsigned flag:1; // (0 - ) } tIO_STATUS; #pragma bitfields=default volatile tIO_STATUS * const pio_status = (tIO_STATUS *) (IO_ADR); Note: the penultimate line of the example returns the field placement mode to its default value, in case there are further fragments, the authors of which did not concern themselves with the inclusion of the pragma before their definition (but we are not the same, we are prudent). We note the disadvantage of this method - we must use our hands to calculate the length of the fields to fill in (more on this later) and then proceed to enumerate the merits: we don’t have to define any masks and bits (the compiler does everything for us), moreover, it also checks the recorded in the field constant data on validity. Register fields are now accessed by standard language tools. while (pio_status->flag) {}; poi_status->code=3; , moreover, autocompletion and type checking, that is, the operator pio_status->code=34; will receive a warning from the compiler. *pio_status=(*pio_status & ~(IO_STATUS_CMD_MASK | IO_STATUS_FLAG_MASK)) | ((command << IO_STATUS_CMD_BIT) & IO_STATUS_CMD_MASK) | (IO_STATUS_FLAG_MASK * flagset); in no way can be recommended for use due to maintenance difficulties. However, if there is a need for them, that is, such simultaneous access to the fields is required by the features of the external device, then constructions like #define WORD(adr) *(int*)(adr) WORD(pio_status)=WORD(pio_status) & ... will allow us to extend the rope to the required size, although personally in such a situation I would prefer to create a temporary structure, modify it, and then send it to the register, tIO_SATUS tmp; tmp=*pio_status; // tmp.code=command; tmp.flag=flagset; *pio_status=tmp; but sometimes this may be unacceptable for reasons of efficiency. Another note is that if you need such operators, certain difficulties will arise if you describe some fields in the structure as const #pragma bitfields=reverse typedef struct { unsigned :1; // (31) unsigned code:3; // (30..28) unsigned : 27; // 26 (27..2) unsigned const readflag:1; // (1) unsigned flag:1; // (0 - ) } tIO_STATUS; #pragma bitfields=default volatile tIO_STATUS * const pio_status = (tIO_STATUS *) (IO_ADR); tIO_STATUS tmp={0,0,1}; // , tmp=*pio_status; // *(int*)(&tmp)=*(int*)pio_device; // tmp.code=3; tmp.flag=1; *(int*)(pio_device)=*(int*)(&tmp); That looks a bit clumsy. REGISTR(tIO_STATUS,code[30..28],readflag[1],flag[0]) and significantly simplified the work of the programmer by creating an appropriate description of the structure for later use (those who programmed in MASM met similar macros). And here an unpleasant surprise awaited me - the C preprocessor is not a macro language, since it lacks a number of necessary possibilities. It really is just a preprocessor and its text processing abilities are very, very limited. At first I did not believe in such a discovery, and for about an hour I considered myself an idiot, unable to find information. Then I considered the authors as idiots on the Internet for an hour and tried using perverted ways to construct the macro I needed. In principle, perhaps it can still be done under the condition of a two-pass preprocessor, but it will look awful. Well, and then I realized that the preprocessor developers did not set themselves the task of creating a macro language (it is not clear why, but they know better), so that they are all great and I am not so bad either. Nevertheless, the problem is not solved - it is possible to use external preprocessors such as M4 or PowerShell, it is possible to use scripting languages ​​like Perl, you can even have your own minimal preprocessor in the form of a Java module or even an executable file, but all of these are crutches and do not seem beautiful and, most importantly, convenient by decision. If I misunderstood something, tell me in the comments.Source: https://habr.com/ru/post/221459/
All Articles