Note:
This is Not the same thing that has been asked many times. Yes I have read the many many posts about casting to void. None of those questions resulted in the answer I suspect is true here.
Background info:
Embedded C. This is specifically related to memory mapped volatile pointers. In other words, peripheral registers.
I came across the following line in a routine that involves writing to an I2C peripheral:
(void) I2C1->SR2;
I2C1 is #defined as a struct * to volatile memory.
So the result of this line is NOT to "avoid a compiler warning" as is the answer on all the searches I did here. It is in fact causing the compiler to read that register (since it's volatile) and then throw it away. This register has flags in it. The act of reading the register causes the flags to clear.
Now this is pretty important since the goal was to clear the flags not just avoid some compiler warning!
What has me worried however, is that at some level of optimization or maybe a different compiler, this code will get optimized away. That is my question:
Will this get optimized away or is there a way to guarantee it won't be optimized away?
I put all the relevant code together below:
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
#define APB1PERIPH_BASE PERIPH_BASE
#define I2C1_BASE (APB1PERIPH_BASE + 0x5400)
#define I2C1 ((I2C_TypeDef *) I2C1_BASE)
typedef struct
{
__IO uint16_t CR1; /*!< I2C Control register 1, Address offset: 0x00 */
uint16_t RESERVED0; /*!< Reserved, 0x02 */
__IO uint16_t CR2; /*!< I2C Control register 2, Address offset: 0x04 */
uint16_t RESERVED1; /*!< Reserved, 0x06 */
__IO uint16_t OAR1; /*!< I2C Own address register 1, Address offset: 0x08 */
uint16_t RESERVED2; /*!< Reserved, 0x0A */
__IO uint16_t OAR2; /*!< I2C Own address register 2, Address offset: 0x0C */
uint16_t RESERVED3; /*!< Reserved, 0x0E */
__IO uint16_t DR; /*!< I2C Data register, Address offset: 0x10 */
uint16_t RESERVED4; /*!< Reserved, 0x12 */
__IO uint16_t SR1; /*!< I2C Status register 1, Address offset: 0x14 */
uint16_t RESERVED5; /*!< Reserved, 0x16 */
__IO uint16_t SR2; /*!< I2C Status register 2, Address offset: 0x18 */
uint16_t RESERVED6; /*!< Reserved, 0x1A */
__IO uint16_t CCR; /*!< I2C Clock control register, Address offset: 0x1C */
uint16_t RESERVED7; /*!< Reserved, 0x1E */
__IO uint16_t TRISE; /*!< I2C TRISE register, Address offset: 0x20 */
uint16_t RESERVED8; /*!< Reserved, 0x22 */
__IO uint16_t FLTR; /*!< I2C FLTR register, Address offset: 0x24 */
uint16_t RESERVED9; /*!< Reserved, 0x26 */
} I2C_TypeDef;
Somewhere down in a function....
(void) I2C1->SR2;
Thanks in advance for any help. This site has been a great resource for newbies like me.
The volatile keyword is the portable way to prevent memory accesses from being optimized away and/or reordered. It should be noted that the proper use of the volatile keyword makes casting the results of the expression to (void) unnecessary. For example, let's say I've typedef'd a structure and have an instance of that structure.
typedef struct
{
int a;
int b;
}
SomeStruct;
SomeStruct test;
The following code will cause the compiler to complain, "warning: expression result unused"
SomeStruct *vptr = &test;
vptr->a;
I can avoid the warning by casting the result to (void), but then the compiler is free to optimize away the read.
SomeStruct *vptr = &test;
(void) vptr->a;
However, if I declare the pointer as volatile and don't cast to (void), I won't get a warning and the compiler will not optimize away the read.
volatile SomeStruct *vptr = &test;
vptr->a;
The moral of the story is that if you are using the volatile keyword, you should not cast expressions to (void). That will only suppress warnings that would otherwise identify missing or incorrect use of the volatile keyword.
Related
I'm currently trying to read a bitmap file header using the following code:
#include <fstream>
using namespace std;
struct BitMapFileHeader {
uint16_t bfType;
uint32_t bfSize;
uint16_t bfReserved1;
uint16_t bfReserved2;
uint32_t bfOffBits;
} file_header;
int main() {
ifstream fin;
fin.open("input.bmp", ios::binary);
fin.read(reinterpret_cast<char*>(&file_header), sizeof(file_header));
}
The first few bytes of my file are 42 4D 36 53 07 00 00 00. file_header.bfType shows the correct value of 19778contained in the 1st and 2nd bytes, however filder_header.bfSize shows 7, which is stored in the 5th byte and it seems to skip the 3rd and 4th bytes.
It appears that I have encountered an issue that was mentioned in the comment of the second answer of this question. Despite identifying the issue, I have no clue how to resolve it. Can someone point me in the right direction?
Edit: some do not recommend packing struct members due to performance issues with unaligned memory access. Apart from populating each struct member one by one, is there any way to avoid this performance penalty?
Use __attribute__ ((packed)) after struct:
struct BitMapFileHeader {
uint16_t bfType;
uint32_t bfSize;
uint16_t bfReserved1;
uint16_t bfReserved2;
uint32_t bfOffBits;
} __attribute__ ((packed)) file_header;
The issue is that the struct is not byte-packed, thus there is padding added between the members.
Since you are using Visual C++, use #pragma pack to byte-pack the struct, along with the push and pop set of #pragmas to limit the byte-packing to the struct you're interested in:
#pragma pack(push)
#pragma pack(1)
struct BitMapFileHeader {
uint16_t bfType;
uint32_t bfSize;
uint16_t bfReserved1;
uint16_t bfReserved2;
uint32_t bfOffBits;
} file_header;
#pragma pack(pop)
I have a union that looks similar to the following:
typedef
union _thing
{
struct thing_indiv {
uint16_t one:5;
uint16_t two:4;
uint16_t three:5;
uint16_t four:5;
uint16_t five:6;
uint16_t six:6;
uint16_t seven:6;
uint16_t eight:7;
uint16_t nine:4;
uint16_t ten:5;
uint16_t eleven:6;
uint16_t twelve:5;
uint16_t thirteen:5;
uint16_t fourteen:4;
uint16_t fifteen:2;
uint16_t unused:5;
} __attribute__((packed)) thing_split;
uint8_t thing_comb[10];
} thing;
But it doesn't behave how I expect. I want to assign bytes to thing.thing_comb and retrieve the relevant items from thing.thing_split.
For example, if thing_comb = { 0xD6, 0x27, 0xAD, 0xB6. ..} I would expect thing.thing_split.one to contain 0x1A (the 5 most significant bits of 0xD6, but it does not, it contains 0x16, the 5 least significant bits. I declared each of the fields as uint16_t to keep gcc from complaining about crossing byte boundaries (I experience the same behavior with uint8_t).
Is there a way to lay out this struct to obtain this behavior?
First, type punning with an union in C++ is Undefined Behaviour.
Second, the Compiler is free to do anything it wants with a bitfield. It is not forced to lay it out like you want it to.
You need to use regular bit-packing with bitshifts to obtain the behaviour you want.
I had a similar question not so long ago:
How to use bitfields that make up a sorting key without falling into UB?
I'm currently struggling with uart snippet codes from embedded UART program.
Then I came across what I can't undersatnd when I analysing code.
Q1. In case of using "union" in "struct". what is the benefit and what purpose to use like this?
#define __IO volatile
typedef struct {
union {
__IO uint32_t RR;
__IO uint32_t TR;
__IO uint32_t DL;
__IO uint32_t RR_TR_DL;
};
union {
__IO uint32_t DH;
__IO uint32_t IR;
__IO uint32_t DH_IER;
};
} UART_TypeDef;
Q2. In case of using "union" in "struct" in "struct". what is the benefit and what purpose to use like this?
typedef struct {
union {
struct{
__IO uint32_t CTRLR0;
__IO uint32_t SSI_COMP_VERSION;
union {
__IO uint32_t DR;
__IO uint32_t DR0;
};
__IO uint32_t DR1;
__IO uint32_t RSVD_2;
};
uint8_t RESERVED[0x1000];
};
} SSI_TypeDef;
The first case is basically "aliasing" of the field names. The UART_TypeDef type consists of two uint32_t fields, the first which can be referred to as any of RR, TR, DL or RR_TR_DL. Ditto for the second field, which can be DH, IR or DH_IER.
The second case, SSI_TypeDef, is similar in respect to the inner unions, consisting of three uint32_t fields, CTRLR0/SSI_COMP_VERSION, DR/DR0 and DR1/RSVD_2 (in all cases, either name can be used for the fields).
But the structure as a whole is sized at 4K, due to the unioning with uint8_t RESERVED[0x1000].
The aliasing is useful if, for example, the same underlying field can be accessed as either RR or TR, depending on context. For example, a device may have different behaviour depending on whether you read or write the location.
Say, for example, that you write to a given address (a memory mapped I/O operation) to indicate to the other end that you are read-ready (able to receive data). Further assume that reading that exact same location will let you know whether you're able to transmit.
First, let's set up said memory mapped I/O address (say it's at 0xf000):
UART_TypeDef *utd = (UART_TypeDef *)0xf000; // very shifty :-)
Now both these statement refer to the same memory address:
int transmitReady = utd->TR; // Can I transmit?
utd->RR = 1; // Tell other end it can send.
Being able to use distinct names for the same underlying thing can aid readability.
I am using eclipse with cygwin. The application is 64bit. In cygwin the structure is defined as :
struct addrinfo {
int ai_flags; /* input flags */
int ai_family; /* address family of socket */
int ai_socktype; /* socket type */
int ai_protocol; /* ai_protocol */
socklen_t ai_addrlen; /* length of socket address */
char *ai_canonname; /* canonical name of service location */
struct sockaddr *ai_addr; /* socket address of socket */
struct addrinfo *ai_next; /* pointer to next in list */
};
The sizeof(addrinfo) result is 48. The size of socketlen_t is 4 bytes. The int type size is 4 bytes. The pointer is 8 bytes in the 64 bits application. The total bytes is 44(4 ints = 16 bytes, socket_len = 4 bytes, 3 pointers = 24; 20+4+24 = 44). I am wondering what the missing 4 bytes for? Are they for padding? I thought 44 bytes do not need to be aligned. Any thought?
Thanks for the answer in advance.
It's padding, after the socklen_t. The next variable in the struct is a pointer, which is 64-bits in length, and will (in this case) be aligned to 64-bits as well. Note however that padding is dependent on architecture and compiler settings; it happens here, but is not guaranteed to always happen.
Note that since you are sharing this struct with the operating system, you should NOT try to change the padding yourself (most compilers allow this using compiler switches and/or pragmas). The OS is expecting this struct with a certain amount of padding included. If you fail to provide it, all the pointers at the end of the struct will have their values misinterpreted.
It's "struct member alignment", the /Zp flag
Data structure alignment,
Microsoft documentation
i am new in operating system development ,i found a semi simple operating system to help as start,that operating system is "Test Os kernel" ,i download and i run it using bochs,so i decide to go to next level and try to compile ,and actually i am using MinGW Developer Studio 2.05,and also my operating system is vista not linux.
when i tried to compile the project ,i receive the following segment error:
kern\.\memory\gdt.c:113: error: conflicting types for 'GDT_set_descriptor'
kern\.\memory\gdt.c:85: error: previous implicit declaration of 'GDT_set_descriptor' was here
kern\.\memory\gdt.c:145: error: conflicting types for 'GDT_install'
kern\.\memory\gdt.c:93: error: previous implicit declaration of 'GDT_install' was here
kern\kernel.c: In function `_start':
kern\kernel.c:29: warning: implicit declaration of function `main'
kern\kernel.c: In function `main':
kern\kernel.c:36: warning: unused variable `cmd'
tos.exe - 4 error(s), 38 warning(s)
and here is the file contains the error:
#include "../../kernel_components/libc/types.h"
/*
1-gdtdescriptor structure : the feilds of a gdt descriptor
2-gdt itelf is an array of gdtdescriptors, it contains all the gdtdescriptors
3-gdtpointer is a structure that indicates the place of the gdt in memory, this pointer is needed by the gdt register,
*/
#define GDTSIZE 16 //only 16 segments are mapped by the GDT
/*!
* gdtDescriptor : each gdt descriptor represents a segment in memory
*/
typedef struct gdtDescriptor
{
u16 segmentLimit_0_15; //the first 16bit of the segment limit (its length)
u16 segmentBase_0_15; //the first 16bit of the segment Base (its beginning)
u8 segmentBase_16_23; //8bits in the midle for the segment base (its length)
u8 segmentType : 4; //data segment or code segment or stack segment
u8 segmentOrSystemCall :1;//to revise the name of this after you know what it does
u8 Ring:2; //ring0 is kernel segment and ring=3 for user mode segment,this used for protection
u8 presentOrSwaped:1; //to indicate if the segment is present in memory or it has been swaped to the HDD
u8 segmentLimit_16_19:4;
u8 NotUsed:2; //this 2 bits feild is not used , so just put it to 0
u8 instructionSize:1; //instructionSize=1 for 32bit instructions
u8 limitUnit:1; //0 if the limit is expressed in bytes or 1 if the limit is in pages
u8 segmentBase_24_31;
} __attribute__ ((packed)); //__attribute__((packed)) so that the compiler doesn't change this structure and optimize it
struct gdtDescriptor GDT[GDTSIZE];//the global descriptor table,is an array of gdtDescriptors (segment entries)
/*!
* the folowing are used to indicate the type of segment, we use afrendly names instead of using binary parameters
* they are used as parameters of the function GDT_set_descriptor(.....)
*/
#define segmentType_CS 0xB //the segment is a code segment
#define segmentType_DS 0x3 //to indicate that the segment being created is a data segment
#define kernelMode 0x0 //ring0, a segment used to contain kernel code
#define userMode 0x3 //ring3, used for ordinary applications that dont belong to the lernel
#define presentInMemory 0x1 //the segment is present in memory or is swapped to a backbone (a disk for exmple)
#define _32bitInstructions 0x1 //instructions in the segment are 32 bit instructions
#define _16bitInstructions 0x0
#define limitInBytes 0x0 //the limit of the segment is expressed in bytes (limit=0x100 means 0x100 bytes)
#define limitInPages 0x1 //(limit=0x100 means 0x100 pages with each page can be 4k or 4M (on intel!)
/*!
* gdtPointer : the gdt pointer is used to indicate the place of the gdt in memory
*/
typedef struct gdtPointer
{
u16 limit;
u32 base;
} __attribute__ ((packed));
u32 GDT_descriptors_number=0; //the number of descriptors in the GDT
struct gdtPointer gdtr;
/*!
* GDT_init : initialize the gdt table : insert the code and data segment and copy the gdt to a new place
*/
void GDT_init()
{
//the first GDT is null
**===============ERROR 2** GDT_set_descriptor(1,0x0,0xfffff,segmentType_CS,0x1,kernelMode,presentInMemory,_32bitInstructions,limitInPages);
GDT_set_descriptor(2,0x0,0xfffff,segmentType_DS,0x1,kernelMode,presentInMemory,_32bitInstructions,limitInPages);
//initialize the GDT pointer
gdtr.base=GDT;
gdtr.limit = sizeof(struct gdtDescriptor)*GDTSIZE;
//make this GDT the system GDT by loading it to tne gdtr regiter
GDT_install(); **===============ERROR 4**
}
/*!
* GDT_set_descriptor : set a GDT descriptor,
* u32 descriptor : witch descriptor to set (the descriptor 0 is not used),1 means the first descriptor
u8 segmentType : 4; //data segment or code segment or stack segment
u8 segmentOrSystemCall :1;//to revise the name of this after you know what it does
u8 Ring:2; //ring0 is kernel segment and ring=3 for user mode segment,this used for protection
u8 presentOrSwaped:1; //to indicate if the segment is present in memory or it has been swaped to the HDD
u8 segmentLimit_16_19:4;
u8 NotUsed:2; //this 2 bits feild is not used , so just put it to 0
u8 instructionSize:1; //instructionSize=1 for 32bit instructions
u8 limitUnit:1; //0 if the limit is expressed in bytes or 1 if the limit is in pages
u32 segmentBase : adresse of the beginnig of the segment
u32 limit :size of the descriptor, if limitUnit=limitInBytes so this limit is expressed in byte not in pages
*
*/
void GDT_set_descriptor(u32 descriptor,u32 base,u32 limit,u8 segmentType,u8 segmentOrSystemCall,u8 ring,u8 presentOrSwaped, u8 instructionSize, u8 limitUnit)
{**===============ERROR 1**
struct gdtDescriptor gd;
//set gdt base
gd.segmentBase_0_15=(base & 0xffff);
gd.segmentBase_16_23=((base>>16) & 0xff);
gd.segmentBase_24_31=((base>>24) & 0xff);
//set gdt limit
gd.segmentLimit_0_15= (limit & 0xffff);
gd.segmentLimit_16_19=((limit>>16) & 0xf);
//other descriptor bits
gd.segmentType=segmentType;
gd.segmentOrSystemCall=segmentOrSystemCall;
gd.Ring=ring;
gd.presentOrSwaped=presentOrSwaped;
gd.NotUsed=0;
gd.instructionSize=instructionSize;
gd.limitUnit=limitUnit;
//add this gd descriptor to the GDT table
GDT[descriptor]=gd;
}
/*!
* GDT_install : load the new GDT to the gdtr register, that means
*/
void GDT_install()**===============ERROR 3**
{
//we need assembly here ! to make the GDTR register points our new GDT
asm("lgdt (gdtr)");
}
/*!
* GDT_install : load the new GDT to the gdtr register, that means
*/
void GDT_load_descriptor(u32 descriptor)
{
//this function must be edited so that we can add a code descriptor or a data descriptor
//for exemple GDT_load_descriptor_CS(2) this will load the second descriptor as a CS segment ie cs<--descriptor(2)
}
4 errors i noftify the places inside the code ===============ERROR number
so what could be the problem?
The problem is that the definition of GDT_set_descriptor occurs after it's referenced.
If a function is called without a preceding definition or declaration, the compiler makes certain assumptions ("implicit declaration") -- these correspond to the default assumptions made in the original C language implementation: return value assumed to be integer, integer-type arguments smaller than "int" are promoted to int, etc. In this case, the compiler later encounters an actual definition of the function that has different return value and arguments. The error is basically telling you "I assumed that but now you're telling me this".
Easy to fix. Either:
(a) invert the order of functions in the source so that the called function appears earlier in the file than the calling code.
(b) Provide forward declarations of the functions near the top of the file (or as is typically done, via a separate header file).
(a) is usually less work, but (b) is often a more general solution -- especially if you anticipate needing to call the respective function from multiple source modules, it's often easiest to put the declaration into a header.