In this video we will go over how to provide special information to the compiler with attributes and pragmas. Embedded platforms require special alignment and orientation to be efficient for execution and for space. We often use the word as our default data storage because they're the most efficient unit a processor can operate on. We may also align our data types on specific memory alignments - byte aligned, half word aligned or word aligned - depending on the type. However, to explicitly set this type of behavior, we actually have to give the compiler special commands to handle this. This is done through compiler attributes and pragmas. When the compiler is given a C program file, it will have to translate from the high level language to the architecture-specific language like assembly. Data types and traditional C keywords tell the compiler some needed information on how to perform this translation. However, it may not always do exactly what you think. It may optimize or remove certain aspects of your code to make it more efficient. In order to enforce certain types of implementations for your software, you need to provide the compiler with some specific instructions; not only at compile time, but also inline with your code to indicate how it should handle various parts of your software. First, let's talk about attributes. Attributes allow us to tell the compiler some specific details about how to compile certain parts of code. These attributes can be applied to variables, structures, structure members and functions. Unfortunately, attributes are a feature of the compiler; they are not part of the C standard language. This means they are not portable between different compilers.Attributes can specify a variety of information to the compiler, including details on how to align, pad, pack, or even how to alter function-calling conventions. We do this by applying the compiler keyword attribute to these various functions and variables. This requires you to use the double underscores and the double parentheses. Inside the parentheses, we'll actually specify a keyword about the attribute. Let's discuss alignment concepts. We can specify alignments for a particular type of memory by using the aligned attribute. Alignment attributes must be a power of two. By itself, align will base the data on its type, meaning it will align by the size of the data. However, you can specify the number of bytes it should align to. For instance, here we have a U int8 variable foo. It is being provided an aligned attribute at a word boundary. We specify this by putting a 4 inside the aligned keyword parentheses, which means to align any 4 byte boundary or word boundary. We can extend this concept to a structure. Here we have a structure with three data members. Two are bytes and one was a word. Depending on how we declare the structure, we can actually alter the size of data that gets allocated in memory. The structure at a minimum requires 6 bytes to account for each individual data type. If each member in the structure were aligned to the word "boundary," we could end up allocating 12 bytes. We can specify that type of alignment by putting the aligned attribute on each individual member with an alignment size of 4. We can align the structure itself in memory. That would mean that we could end up allocating 16 bytes of memory. We can specify the alignment by applying the attribute to the end of the structure definition. This is because the types inside would be aligned to a word boundary given the size of 12 bytes. Twelve is not a power of two; therefore the next closest size would be 16. Alternatively, we can request that a structure gets packed into memory. To pack this structure, we will add our attribute string to the end of the structure definition with the keyword "packed." This will force the structure to implement, in the minimum amount of memory, 6 bytes. This will allow us to be more efficient with memory, but at the cost of extra execution cycles to interact and manipulate this packed structure. Now let's look at applying attributes to functions. Here we have a function that has a specific attribute called "always in line" and a new C keyword called "inline." The inline C keyword tells the compiler to skip calling this function like a normal call. Instead, it copies the contents of the function to the location where the call would have existed. This avoids the overhead associated with calling and returning from a function. Unfortunately, with optimizations, the compiler may ignore this inline keyword request. Thus, we have added an attribute telling the compiler to always inline this function regardless of optimizations. We'll talk more about the inline keyword in later lectures. Our last example involves functions and pragmas. Let us say you have a particular application that requires code to operate a certain way and you do not want the compiler to alter the behavior based on optimizations; or potentially, you want to specify a particular optimization. Here we can use a pragma to tell the compiler to add an option to GCC when compiling this region of code. We need to push the option into GCC, use it and then we pop it out of the option list and we move on to other parts of the file. We start with the push pragma command to GCC. We follow the GCC option we wanted to add. In this case, we specify the word "optimize" and a level of optimization. Here we are specifying a O0 level - Level Optimization Zero. We then pop the option back off when we want to disable its use before moving on to other code. Even though these attributes are regularly used in embedded software development, they do introduce some portability issues between compilers and sometimes even architectures, depending on the specific attribute or pragma. Because of this, there is often some preprocessor directives that allow us to remove attributes depending on the type of compiler we're using. In our case we use the GNUC GCC compiler. We usually refer to this compiler in our source files with a preprocessor defined underscore underscore GNUC underscore underscore. Here we create a preprocessor switch that defines the attribute keyword as "nothing" for a compiler that is not using GNUC. We just simply need to enable GNUC file to compile time to utilize the attributes. We were only able to discuss a very few number of attributes and pragmas in this video, but there are many more. These features allow us to give the compiler some more details about your program and how it should be compiled. However, these features are compiler-dependent and do not port. Regardless, these are powerful features to give us some more fine control to optimize our applications in both size and execution for an embedded system.