Embedded - 10: Hands Off, Baby

Episode Date: July 17, 2013

Jen Costillo (@r0b0ts0nf1r3) joins Elecia White to discuss the secret parts of C, keywords that only embedded software engineers seem to know about. They talk about interviewing and why these keywords... make good questions for finding folks who use the language to its full potential. On the show they mention a list of embedded interview questions with answers. (Note: Elecia's book has many excellent interview questions and what interviewers look for when they ask them.) Producer Christopher White sends along a more concise introduction to the often unused register keyword. 

Transcript
Discussion (0)
Starting point is 00:00:00 Hello, I'm Eliseo White. Welcome to Making Invented Systems. If you love gadgets, especially making them, you're in the right place. Today we're going to talk about the important parts of the language that many programmers never really understand. Happily, Jen Castillo is back to share her wisdom with us. Hello, it's good to be back. It's been a busy couple of weeks, so... How have you been, Jen? Well, like I said, very, very busy.
Starting point is 00:00:32 I've just given notice at my current job, my current very safe, warm, huggy-type place, and I'm going to go out into the cold, unyielding world of startups. Unyielding world of startups, but that means you get a better title, right? Damn straight. But you emailed me to say you want to talk about explosions, right? Okay, okay, now she's laughing because that was all about volatile. We're going to talk about C and C++, though the concepts happen in other languages, and even Java has a volatile keyword. It means mostly the same thing. For the most part,
Starting point is 00:01:13 it's going to be C and C++ and volatile. According to Google, the definition of the word means easily evaporated at normal temperatures, and the synonyms are changeable, fixable, and inconstant, which actually makes a lot of sense for the keyword in programming languages. Was there no alternate definition like sublimate or something like that? Because it also sounds like sublimate. But anyway. And, well, there were a couple other definitions involving fire and explosions, which was how we got started on explosions.
Starting point is 00:01:48 How I got started on explosions. What does volatile mean to you, Jen? Great. Today's going to be a giggle fest. It means everything to me. It's probably the most common keyword I use in my embedded firmware coding practice. Well, I mean, other than void and int and all. Well, okay, so those are other keywords. But in terms of, it's the fact that we use volatile is so central to how we can even
Starting point is 00:02:22 declare ourselves as firmware engineers. It's something that is often asked in firmware engineering interviews. is so central to how we can even declare ourselves as firmware engineers. It's something that is often asked in firmware engineering interviews. What does volatile mean and how do you use it? I certainly have asked it myself and I've been asked it more times than I can count. You ask it too, don't you? Of course I ask that because if they don't know how to use it, what it's used for. What do you look for in an answer? Okay.
Starting point is 00:02:48 I look for effectively three answers. One, the first thing is, do they know what it means? Usually people understand that, hey, it's to denote to the compiler that this should always be checked. Do not assume whatever the last value is is correct. And do not optimize out the last value is correct and do not optimize out reading this value whenever possible. Because it might change somewhere else in the code. Right. And most firmware engineers who get that far, they also understand that, you know, that the reason why this is, is because they're using this in a hardware register, such as,
Starting point is 00:03:21 you know, that serial port you're trying to get in the data. You're waiting for this bit to go high. The status register. Right. And you don't want to optimize that out. And so they always go, after that, they always know, hey, I used it when I was writing a UART driver or something similar, something hardware register centric. But that's not all that it does. No, no. hardware register centric but that's not all that it does no no because if a variable changes in an
Starting point is 00:03:46 interrupt that could also uh that should also be marked volatile a global variable changes in an interrupt because it may you may have while waiting for serial to be done and waiting for serial to be done as a variable that's changed in your serial driver, then you need to mark that volatile. Otherwise, the compiler is going to go, yeah, this never changes because in this loop, you don't change it. And so it needs to know not to optimize it out, not to put it in a regular processor register.
Starting point is 00:04:18 So let's talk about one example of what that would mean. So you've already used UART. What's common is you keep reading in data from your ISR, from your UART, and then you're taking that data and you're putting it into a circular buffer. Well, you need to update the pointer to that circular buffer. You need to push that data into the circular buffer. That way, your main task loop can look through that data and use it however it needs to. And so this is a simple, the UART's putting data into the circular buffer and your main loop is reading it out.
Starting point is 00:04:51 Right. So first thing you're going to want, obviously, volatile for all those hardware registers, but also for the circular buffer, the index where it's pointing, you also need to make that volatile. So in addition to the UART registers, the hardware ones, the index into the circular buffer, and does the circular buffer itself need to be volatile?
Starting point is 00:05:16 I would say generally I would make that the case. Because it's better safe than sorry on this one. Yeah. You might as well make the compiler not optimize these variables out and you maybe lose just a little smidgen of cpu processing cycles in an effort not to make it make them wrong i mean that's the problem so let's let's say you needed a volatile variable and you didn't make it so and then you uh so i had the the is the you are ready instead let's let's talk about the circular buffer let's say you didn't have the
Starting point is 00:05:53 index be volatile yep and you're going through and you're reading stuff out and this is in your isr is this in your task loop in In my task loop. Okay. And so I'm at the end and now I'm going to wait for some signal that says the buffer's full again. And then I get an interrupt and it puts 10 bytes into the buffer. And so my loop starts going and then it reads out how many are in, but it keeps reading out how many are in there. And the compiler just eliminates that while there is stuff left over because it doesn't know that anything can change outside the loop. Right. The code was written in such a way. So when you have the assembly, when you look at the actual listing file, all're going to see is like hey we already know what this value is we already set that that that index up here last time we read it was all good no one else is touching this we're gangbusters
Starting point is 00:06:54 exactly uh and so most of the keywords we're going to talk about today have to do with telling the compiler something special is happening. Even when you have like int and character array or double or float, that's still all about telling the compiler what it was you meant when you said I want a variable. It's like adjectives in English. It's expressing what you meant and trying to be as clear as possible. Oh, I like variable descriptions as adjectives.
Starting point is 00:07:30 I like that. It helps me cast things. I don't. I hate English. So there is also a register keyword. Well, before we go on to that, actually, I want to follow up with an additional. So we talked about two ways to use volatile. Oh, I thought we hit the third with in variables. Okay, so we have registers and variables. What else? Registers and variables. But and we talked about
Starting point is 00:07:59 in regards to when you're in your ISR. Well, let's, you know, extend that a little bit. And remember that, hey, maybe we're not writing ISRs, but anytime that we have, let's say, two tasks and they share information, that information also needs to be volatile. Especially if you're working with a lightweight real-time operating system. Correct.
Starting point is 00:08:19 Hard toss. Then you may not have the big signaling and semaphore systems you get with a with a i want to say a real operating system that's not correct so if you if you're if you're basically have your own effective system or art house that you've developed um you know it could it could be you know it could just be you know time sharing um or as a single task loop and round robin between them. But at the end of the day, those variables are being shared. And so especially if you still have an interrupt or something like that,
Starting point is 00:08:52 you need to make sure that everything's always checked. Yeah, that makes sense. I'd forgotten about that. I don't usually like RTOSs. It's a bigger, you know, for most people who are thinking about it this way, when I ask this question and that's the answer that they give me, usually these are people who are writing Linux drivers or more used to working within the kernel.
Starting point is 00:09:12 That makes sense. And then, so that's what you look for with volatile. It's a little tough asking that as an interview question because it is pure knowledge. It isn't how are they thinking? It's do they actually know what they're doing? Yeah. Yeah. And it really does tell you a lot about what level of experience that they've had and depth of experience that they've had. And sometimes you'll end up with a really good story about a bug they were tracking down.
Starting point is 00:09:40 Yes. Those are the good interviews. The ones that it's not only here's what I know, but here's how I've used it. Yeah, or didn't use it and found out I should have. Right. Why don't we put volatile on everything? Jen's looking at me like, because we don't. That's the stupidest question I've ever heard. I don't need to read everything.
Starting point is 00:10:04 Like, for example, if I have a local variable in a function. Int i. Exactly. That's exactly what I was thinking. Maybe I don't need to do that. It's not going to be, you know, no one's going to come and swoop in and change that value or certainly shouldn't. It's a way of protecting us against someone swooping in and changing things on us. Well, it's more than that. If you have int i and you're doing like a four i equals zero to
Starting point is 00:10:31 a hundred loop, your i probably, your variable i is not going to be in memory. It's not going to put it in RAM and RAM has some time to access. Maybe it's only one or two clock cycles. Maybe it's no clock cycles and it's free. But a lot of times RAM is a little slower than using a processor register. So your variable I is going to go into a processor register like R0. And then you'll be able, it will go much, much faster. And we want the optimizer to work. We want the optimizer to work we want the optimizer to eliminate i if it doesn't have to do i plus plus and then i minus minus and then i plus plus it
Starting point is 00:11:12 can just reorder things a little bit we want it to go ahead and and optimize out what it can optimizers are fantastic but we need to make sure we we know what can and can't be optimized and what should go to a memory location all the time you said the reason to use volatile for hardware registers yeah that's because you never you are status register is never ever going to be a register that can be randomly selected. It's never going to be R31. It always has to go to a hardware address location. And so that's why that has to be volatile. And that's why you don't want everything to be volatile is because if you can use those registers, you want to.
Starting point is 00:11:57 It'd be so slow if you didn't, if you couldn't use, if you put volatile in front of everything and you didn't use those registers. But what you have done, Elle, is set us up to talk about register, which we've pretty much already talked about in your example. Well, and that's kind of register. Nobody ever uses that keyword anymore. Yeah, I can't think of the last time I used it,
Starting point is 00:12:20 but I know I've used it. I think the last time I used it was type def or pound define register to volatile. I wouldn't normally do that. Oh, I did that backwards. I want to replace all of the register keywords with the volatile keyword. So that would be pound define. It's hard to write code if you aren't typing.
Starting point is 00:12:48 Pound define register to volatile. And that would replace all of my register keywords with volatile. Sometimes it's nice to know which way you're using the volatile keyword. Are you using it because it is a hardware register or because it is a shared variable? And I don't usually do that now. It's all just volatile. And I let the person who comes after me swim shared variable. Right. And I don't usually do that now. It's all just volatile. And I let the person who comes after me swim for themselves. Right.
Starting point is 00:13:09 The only time I can think of that I maybe should have used register or I was pretty much effectively using register, but we didn't have a C-Stack setup, so I had to do everything with all the available registers to determine the size of RAM and basically the rows and columns to figure out that setup. That would have been nice, but I didn't have a C-stack to do that. Anyway.
Starting point is 00:13:35 I remember using it when I was programming on 8051s, and the compiler wasn't smart enough. We had a very, very old version of the Kyle compiler, and it didn't know when its default was to not put variables in registers because it only had like six or some stupidly small number. And so it would usually read the variables out of memory, and I wanted it to be able to use the fast register accesses so that was one one where we really did put register and what that meant was
Starting point is 00:14:12 please put this variable into this register uh i'm not even sure registers are properly c99 supported keyword anymore i haven't like i said i haven't tried i haven't, like I said, I haven't tried, I haven't found a reason to use it in such a long time, but I'm sure if you're trying to optimize in the case that you mentioned earlier, I can totally see that. So let's see.
Starting point is 00:14:39 What else is on our list of keywords? Const. Oh, our producer really wanted to talk about const versus const star versus const star const. What else is on our list of keywords? Const. Oh, our producer really wanted to talk about const versus const star versus const star const. I don't want to talk about that to you. I know we have an obligation to the public to talk about const.
Starting point is 00:15:02 One thing I could... So there's... So there's one thing I was so there's so there's there's one thing I was going to say you know there's a difference between const, int, star blah and no no let's go back what does const mean at all
Starting point is 00:15:20 I'm going to pretend like I'm a new graduate I'm going to be gonna I'm gonna pretend like I'm a new graduate I'm gonna be like const means constant yes indeed const means constant oh man I have heard that answer and actually you know I have some suggestions for the audience who you know if you really want to read up on that I believe that Dan Sachs has an article about this. In addition, the Embedded Boot Camp, there's this really great article. It's 0x10 embedded questions, embedded interview questions. And one of those questions is the const. Like, what does this one mean?
Starting point is 00:16:04 What does that mean? And they do a pretty reasonable treatment, albeit it's very compact and dense in information. But what I was going to say is, yes, cons doesn't just read mean only. But once you assign a value to that variable, it's not going to change. And yet, it does get memory allocated for it by the compiler. So it can be passed around as a variable, unlike if you did a pound define of the same information.
Starting point is 00:16:33 For example, if you did const my number of dogs and it was equal to 2, but if you did pound define number of dogs equal to they can they can't be used in the same way even though that information is effectively the same also because it does allocate memory for it you can have a pointer to it that can be used later and say you have a const to a string um you know my my name is Elysia, then if you had pound defined that string, every place where you used the pound define, it would put that in memory, where if you use a const, it's a variable, it's used once, there's only one memory allocation for it. And you can't change it
Starting point is 00:17:22 because it's probably allocated to read-only memory, but at least it isn't in your code 97 times and your code space doesn't bloat. So const is really useful for that. In addition, if you wanted to have a const pointer as a way to tell the programmer, such that they don't need to go into the linker map, where the offset of a big piece of, a big chunk of data is like, this is where we're keeping the application code. That's an excellent way to do that. So that would be a const variable that might actually be set by the linker. I want to talk a little bit about pragmas, and so
Starting point is 00:18:07 let's skip to that. Most people know about pragma pack, but let's explain anyway. Okay. You can pack variables to usually structure variables so that they take up as little space as possible. For example, if you had a structure that was a Uint8, a Uint16, a Uint32, and then a Uint8 again, the compiler can reorder those and put them however they want and make it so that if it's a 32-bit processor, it accesses each one on 32-bit boundaries, which makes it a little bit faster for processing.
Starting point is 00:18:47 But if you were transferring that memory to something else, to an EEPROM or across a serial port as just a string of bytes, if it has reordered them or switched around their sizes to make it more efficient for itself, then now the other side isn't going to get it. Or it's not going to be aligned. The data is not going to come out the way you expect. And so pragma pack tells it how it needs to be aligned.
Starting point is 00:19:16 It might need to be aligned on byte boundaries. It might need to be aligned on 2-bit byte boundaries. It just depends on what you put there. That said, if you use, if you, if you use PAC and things aren't aligned quite the way you expect, you might end up with, what's the word, big gaps in your, in your system, or, or it might reorder. For example, let's say you're going to send out, you have this protocol, and you decide to put in a structure, and let's say for the structure that you have,
Starting point is 00:19:51 it's a little out of order, so that 32-bit word doesn't quite fall on a nice four-byte boundary. It might scoot it up and around, or it might not be scooted around, and they might put... Oh, man. Yeah, no, I see where you're going. Given what I said,
Starting point is 00:20:09 the start of a 32-bit variable starts on the third byte of the packet if we're in pragma pack one. And so that's kind of bad. That's not easy for the compiler or the processor to access. It has to do shifts and masks and all that. And we can hide behind C and it'll do it for us.
Starting point is 00:20:31 But it is a little inefficient. On the other hand, if we didn't have pragma packing, it was the default pragma packer, the default packing for a 32-bit processor, then that first Uint is going to be 32 bits, the U16 is going to be 32 bits, the 32 bits is going to be 32 bits, and the U8 is going to be 32 bits. And so now we've used just a ton more memory.
Starting point is 00:20:56 And if we try to transfer that to a 16-bit processor with the same structure, it's going to do something different, and that's bad. Particularly if you're going to use a mem copy to copy those bytes into the outgoing hardware peripheral, you'll end up with some spaces or you'll end up with something, something else that you that you did not intend. So I would say use pack cautiously, make sure that any protocols that you design kind of have packing in mind. So don't have to deal with this this type of thought exercise yes um pack is very good for making things small in ram but it's very bad
Starting point is 00:21:35 for making things efficient yeah well there's there's that triangle you know a lot of things come with a cost triangle and on on one corner of the triangle, there should be the amount of RAM it takes. On another, it should be the code size. And then on the third one, it should be processing power. And so if you want something that is efficient as possible, you have to choose what you mean by efficient. And you could have something be low RAM and low code space,
Starting point is 00:22:04 but the chances are it's going to be high processing power. And if you want something to be super, super, super efficient for processing power, then you should throw some RAM and some code space in there. Absolutely. That's just kind of, you know, how optimizations work in general. There are some easy ways to fake that out. And I always think that using the inborn compiler optimizer is really useful. But you should also make sure that you specify what kind of optimization because there's usually an option for the optimizer for space. Or cycles or RAM, yeah.
Starting point is 00:22:43 The optimizer needs to know the same way we do I had lunch with a programmer who said that optimizations were awful and turned them off on every opportunity and I stopped I actually thought that was a joke I can't imagine that, sure it's a little harder to debug and sure you have to understand what the processor is doing what the compiler is doing but if you ever want your device's batteries to last longer,
Starting point is 00:23:09 crank up the optimization, start learning how it works. Well, so what was there? Naturally, you asked them, why, why, why are you so against optimizing? Because it was difficult to debug in the compiler or the debugger would jump around and the variables wouldn't be visible in the debugger. That was because those variables had been optimized out. They'd put into registers. Right. And yes, that is true.
Starting point is 00:23:34 If you optimize, especially if you have it on a very high level of optimizing, like level two or I think, did they ever go up to three? I don't think so. Oh, let's crank this baby all the way to 11. I'm going to optimize it out of existence. I'm going to go back to work right now and I'm going to go see whether I can crank it up to 11
Starting point is 00:23:51 and see what happens. I'm sure none of the variables will make any sense. But if you're one of those people who is really uncomfortable with the optimizer, who thinks it's not worth your time to do this, there's a couple of things you can do. One, you can selectively, you know, during your debug, just say, hey, just put a use pragma to stop optimization around that particular
Starting point is 00:24:12 function. There's usually that if you check your compiler options. In addition, you can always turn on your listings when you compile. And what it'll do is it'll interweave the C code and the assembly. And this is excellent practice for anyone who ever thinks they'll have to write assembly. Even for those of you who don't, I love the listing functions. I love the listing functions too because I get a chance to sit down
Starting point is 00:24:37 without the stress of having the debugger on and going and someone looking over my shoulder wondering why this bug is still there. And I sit down, I go, okay, this register is doing this. That's what it, you know, it's now I. This one, this register over here is my Metallica rules variable. And this is how they're interacting. This is how it's going to loop.
Starting point is 00:24:56 Oh, and here's where it's optimizing. Being able to sit down and print out a function as god awful and ugly as it looks in assembly is incredibly useful for you to to become more comfortable with optimizing and understanding that optimizer's little quirks understanding your compiler's quirks and your optimizers right i totally agree and the few times that i still write assembly usually what i do is i write what i want and see and then i read the assembly and then i tweak my c so that it's a little I write what I want in C, and then I read the assembly, and then I tweak my C so that it's a little closer to what I want in assembly. And then I
Starting point is 00:25:30 compile and check the list and read the assembly again until I'm pretty sure I'm as far as I can go with the compiler. And then if I still can find a couple more ways to make it just a little faster, a little better, I can do that. But I don't start with a blank page and just whip out assembly. No, that's, that's, I think I do a similar version, which is I write the C version and then I interweave the assembly in between. So it's easier for myself and future coders of America to read it later on. So that's, that's another keyword. Um, and that's very compiler dependent, but almost all of the embedded compilers have it. The assembly? Yeah. Yes. Usually it's ASM and then a parentheses and double quotes, and then your assembly goes in there and double quotes and parentheses.
Starting point is 00:26:15 And it's usually a single line. Yeah, but you don't have to. I've seen it slashed out, backslash, so that it can go multiple lines, but I think single line looks better. Yeah, I think single line is much easier to read. But this is one of those things that you need to go back into your compiler user's guide. So pragma, all the pragma items in there, all the intrinsics, like if there's a memory copy or a memory access that's specific to your particular processor, it should be in this compiler user's guide well disable and enable interrupts is often an intrinsic and a single command that is in at least the cortex m's that i've been using oh yeah have that and i you know
Starting point is 00:26:59 i was writing my own disable function and then i grepped the example code and look there it is and look it's written much better than I had and look it's already kind of done for me so it's worth getting to know that yeah so I actually I was going to tell you one of my little tricks from a long time ago was to use the assembly keyword or intrinsic to write in my own software breakpoints if that that was already this yes if it was already part of the assembly instruction set so i believe arms have it nips has it um i believe there's some version of it in power pc the ti processors i've used it all have had it right and so this this be really useful, especially if your system doesn't have, your debugger doesn't have a lot of fancy features or has a limit on how many debug breakpoints you can have. Many processors, you can only have two or with the RMMs, I think it's four hard breakpoints.
Starting point is 00:28:02 Yes. And that's because they will only support hardware breakpoints. So this is an easy way. If you're at a company or you're doing your own thing and you can't spend several thousand dollars to get a full-featured debugger, doing those hardware breakpoints or writing the assembly code to put in those hardware breakpoints or soft breakpoints
Starting point is 00:28:24 can be really useful for you to just go, hey, we got to this point. We're looping here. We can't go past it until I effectively change the program counter to the next instruction. Well, and we used to do that for asserts all the time. We'd go ahead and put asserts into our code and you we'd put in the uh i think it's asm and then you you quote it and it's bkpt0 yeah and uh and then it would stop if the variable was out of range and yet if we went to, we could even leave that in because if you don't have a debugger attached, it goes on.
Starting point is 00:29:07 Yeah. Yeah. So I think on MIPS it's SDBP. I keep thinking on PowerPC it's EIEIO, but I think that's wrong. It is. It's something like that. I remember it was pretty funny. Yeah.
Starting point is 00:29:19 Yeah. Everyone thinks that's really funny. But yeah, these are the little things that can really make you look really good when it's looking down on your project. Oh, yeah. And having a breakpoint that you can just put in software is really, really nice. Because then you don't have to, you know, you can even have if this variable equals one, two, three, and then that is your error condition. And now you don't have to try to fake your error condition anymore. You just wait for it to happen and poof, you have it. I totally agree with that.
Starting point is 00:29:57 I noticed one thing in our discussion that we didn't do. And I'm sure that's, you know, either the people are still listening, begging for us to talk about this one keyword. But we didn't do. And I'm sure that's, you know, either the people are still listening, begging for us to talk about this one keyword, but we didn't. We never finished a const, so I'm not sure they're really expecting much at this point. You know what we didn't talk about? Static.
Starting point is 00:30:17 Yeah. Oh, well. We were totally going to get to static because that's another interview question. Oh, God, yes. That's like my first interview question and then volatile. You're just mean. No, I'm not mean. So, okay. So I've been asked this question. I have asked it. What do you look for? What do you, what do you want to hear? I want to hear, I want to hear them explain to me how they use it in three different
Starting point is 00:30:48 ways. But I also want them to know that it's, it's limiting. Its purpose is to limit the scope of that variable or function to the, to that particular area. And I look for them to explain how that's done in three different ways. Okay so let's see if I put static in front of a function you know static void my function that means that that function my function is only available in this file no other file has access to it. And that's specific to C? Oh that that is, yeah, yes. Most of these are specific to C. Some of them are even specific to compilers, but that one, that allows for encapsulation. It means that nobody else can play with this function. And it means that only I can, only this file has access. It's, it's, it's, it's embedded firmware's way of pretending like we're a full-featured C++
Starting point is 00:31:50 language, but we're only C. That's how I view it. That's how I view it. Going to C++, it's very much like the private keyword. It's a private function if you do it the way that I just said. You put it in front of a function name. But it's got the same aspect for a file. It's like putting a private in front of a class variable. So if you have a global variable in a file that's my age, into my age, if you put a static in front of that, anything inside that file can access that variable. But other files can't. Absolutely. Okay, so that's two.
Starting point is 00:32:29 So those are both like encapsulation methods. They're ways of making it private. Making a module look classy and interfaceable, or not interfaceable, as the case may be. Yeah, it's hiding, hiding stuff. Now the last thing is a variable, a static variable inside of a function. That one I wish they'd renamed
Starting point is 00:32:52 because that's so broken. Yeah, because if I set it initially to zero and then we go back, so okay, so what happens is every time you go into that function, whatever you last left my variable, it will still be, you know, if you last left it at three, it will, you return there, it'll be three. Let's frame it first.
Starting point is 00:33:11 So let's say you have a function. Let's say it's my function again. And whether it's static or not, the function, if you have your list of variables, you're declaring and you have int i. And you know, that's probably going to go into register, register like we said especially if it's incrementing through an index and then you have this other variable static how many times has this function been called and you set that to zero so static int how many times this function been called equals zero and right above it you have int i equals zero. And those
Starting point is 00:33:45 two variables are going to do something very, very different for you. Because when you call the function the first time, they're both going to be zero. And then if you increment your i to be a bazillion, the next time you call the function, it's going to come in and it's going to be zero. Because int going to be zero because int i equals zero. That's what that means. But static, static, if you come into that function and you increment it once, then the next time you come into that function, it's going to be one, even though it says static int equals count number of variables equals zero, it equals one one and the next time you come in if you keep incrementing it's going to equal two and three and four and it's it's a great way to count how
Starting point is 00:34:31 many times you you've called the functions so so given that so for those who understand yes it it can certainly help you you know count the number of times you've been in there it can help you if you have some sort of weird state flag like if you had like well the number of times you've been in there. It can help you if you have some sort of weird state flag. Well, a lot of times I've used it for, is this the first time I've called this function? Yes. Does this need to be initialized? Run this first time, yeah. Our producer says that instead of that, for that function of static, the function variable version, they should have called it sticky.
Starting point is 00:35:01 Because that's what it is. It makes the variable sticky. Yeah. they should have called it sticky because that's what it is it's it it makes the variable sticky yeah um but you know one of the things that i that always makes me uncomfortable is the fact that you know we you know when you have static my variable equal to zero yeah inside of inside of function and you have it equal to zero i always have that uncomfortable reading you know when i read it i always feel uncomfortable i'm like it's. I'm like, it's always going to be zero. It's always going to be zero. But it's not. I know. I know. But, but for the reader, to me, it's so uncomfortable. That is a hidden global variable. I don't like them. I admit I do use them occasionally for the, is this the first time, but overall,
Starting point is 00:35:43 I would rather just slap that variable to the top of the function, call it a global inside this file, and put static up there. It has the same effect for RAM. It has the same effect for memory. Your compiler does essentially the same thing to it. What it means is that all of your other functions
Starting point is 00:36:02 can modify it, but if you're a good, you know, you put some comments in that says only this function can modify it. Hands it means is that all of your other functions can modify it, but if you're a good, you know, you put some comments in that says only this function can modify it. Hands off, baby. Yeah. Hands off, baby. That needs to be a keyword. Then it's safer because it's not a hidden global variable. It's a very upfront and in your face global. Yeah. And i would generally agree with you except for the fact that people cannot be trusted well there's that i i have met those people who use my global variables to store their stupid data didn't they know it was my global
Starting point is 00:36:37 variable l's global variable i would make that an array so you can have multiple pieces of data. Oh, it's a whole structure, baby. How often do you use the keyword static in your job, assuming you're spending the day programming? I use it all the time. I don't think I have used, I don't think I have written a whole code file without it in forever. Right. Volatile and static are where it's at. I don't always use volatile in every file because sometimes if I'm writing like algorithm files,
Starting point is 00:37:11 then those don't need the protections. But static, static every file because I like having little helper functions and those helper functions nobody else should need. Right. If you, I mean, when we were in college and we were just trying to get our machine problems done, I'm sure we could care less about using static. But now that we're grownups and now we have to make sure that all our modules interface nicely with everyone else's
Starting point is 00:37:40 and we don't want to have to, you know on the on the user that they have to read the entire file and know what everything does it's really important for us to to make our header files as accessible and clean as possible without you know making them have to find hidden gems in the actual dot c file that's true if a function isn't a static function, that's kind of my API. And all of my files, even the ones that are deep, deep inside my system, have a layer of API-ness that are to be exposed. And I don't like it when I come to these systems where global variables are shared with lots of different files and functions are called from here to there and the organization maybe they all start with EEPROM or UART but things go everywhere and everybody calls everything it it makes it creeps me out unfortunately within you know I I say this a lot to to the people around me when they when they when they laugh at at my C code and they laugh at what I say about making things clean
Starting point is 00:38:46 and distinct and having APIs because most firmware engineers, one, we're about 10 to 15 years behind what everyone else is doing. And we're usually the only people on our project where there's only one firmware engineer. So we're not code reviewing each we have very rarely do we see anyone else's code unless we've been brought in to to save the project so we're just you know we're we don't think about clean design we don't we don't think about things from a from us a computer science point of view well there was that triangle and it had ram and and code space and cpu cycles and nowhere on there did it have able to be maintained right but that that was assumed to me that that that's like a higher priority than optimization is that in a year someone someone
Starting point is 00:39:40 even other than me should be able to go back and fix it or change it or use it. Or repurpose it. Exactly. But, you know, unfortunately that assumes that your hardware engineer hasn't moved on to bigger and better chips or bigger and better hardware peripherals that require you to basically chuck an entire module. You really want to, you know, it's nice when we can actually take a module that we've used in the past and repurpose it for more stuff. But to be honest with you,
Starting point is 00:40:11 the number of times that I've been able to write a beautiful module and then use it in another project and it not being consulting related has been very few times. Really? I have a circular buffer library that I just kind of carry with me all the time
Starting point is 00:40:26 because it's pretty bug-free at this point. I just mentally end up writing a new one each time, but it's always effectively the same. Well, now that I've realized you have to draw a picture in order to make good circular buffer functions, then it's gotten a lot better. But I like that one,. I wrote it on my own time. And I have a debug library that you can turn on and off debugging levels that I tend to drag
Starting point is 00:40:55 around. But I'm just finishing a project where we're porting probably 60% of the code. And I started to rewrite parts of it until I realized just how much code they already had. And then I went searching for what they had, but it wasn't well documented. And it was, oh, it's so spaghetti. And there's the decision of do I port the spaghetti or do I rewrite it into something more usable? The new guy who's taking over the project
Starting point is 00:41:24 is very C++-y and is even more wigged out about the spaghettiness than I am. Because I mean, it's legible. They tried in some areas to keep the spaghetti down, but other parts, it looks like the spaghetti came up in the worst possible way. It's a balance. I like to reuse code and I find I do a fair amount of it, but it was easier when I was a pure software engineer. Well, you know, we're talking about spaghetti code here. Maybe one thing that we could do to give to the audience to help limit the spaghetti code is talking about macros. You know, in the spaghetti code, one of the macros is code generation. It reads a text file and then generates a bunch of C code by having different definitions for the macro that is
Starting point is 00:42:15 defined in the text file. It's hideous. Never do that. Whoever you are, if you did that, please stop. Use Python, use C to generate your code and then compile it. Don't use the preprocessor to generate your code. Okay, I'm sorry. That was not what you wanted. You didn't really mean rant on that one. I'm sorry. So let's take it back to macros. Oh, yeah, macros.
Starting point is 00:42:34 Macros are wonderful. Pound to find is my friend. What can a macro do for us? Well, let's see. The reason that the macro min, you know, when you have A, B, is often implemented is because there is a very short way to do that. But it's ugly. That trinary operator with the question mark in the colon, it's helpful to the compiler, but I hate seeing it. It just isn't the way my brain works. And I always wonder if the first one is true or the second one is true. So, you know, I do that.
Starting point is 00:43:11 I do the min and then it's type independent. That's one of the great things about macros. If you want a macro to tell you to square something, you could square a car. It doesn't matter. Or or char however you want to say that it makes you make it sound like it's a little bit like a like template classes in a way or or operator overloading in a way oh yeah i mean you can't you can't macro uh the star the asterisk to anything that would be kind of fun. Code obfuscation at its best. Oh my God. You can macro pound define if to be else.
Starting point is 00:43:52 Yeah, it would be horrible and mean. I'm sorry I was making a face. Or just pound define if to nothing and then all of your if statements run whether you meant them to or not. Don't do that. I'm sorry. I'm hurting Jack. You'm really big of my face so let's talk about the basics basics are about when you'd actually want okay so so
Starting point is 00:44:15 macros versus type def uh type def so if you if you have a macro for it it's effectively replacing a snippet of code however if you pound if you if you use type def to say like hey cast all in all ints to to cast uh whenever we say uh that hurts baby and replace that with an int, that's just replacing one word for another. That happens in the preprocessor stage. No checking happens. It's as though you used an awk or Python or Perl or whatever your favorite scripting language script
Starting point is 00:45:00 to just go through and replace everything. And define is effectively doing the same thing there too. No, no. Define is replacing everything. Wait, typedef is? Typedef is more of an alias. Yes. And macro, a pound define is a true redefinition.
Starting point is 00:45:18 Yes. And some of the other things that you should be aware of on macros are put parentheses around stuff in your macro because you never know what people are going to pass into your macro well for my min function min a b it would take those a and b as parameters and return whichever one was the lesser you can pass you know i plus plus I++. And now the question is, well, I++, you should never pass to that because it's accessed multiple times. Yes, that too. But you could pass I++.
Starting point is 00:45:53 And if you don't have the proper parentheses around each variable, it could go really bad because now you have the plus three kind of hanging out there. So if you have min A, B, you know, every time you use A, you should have parentheses around it. Every time you use B, you should have parentheses around it. And when you finish with that whole macro definition, you're going to have a lot of parentheses. And that's okay. Parentheses are cheap. If you're having trouble finding the parentheses key, just let me know.
Starting point is 00:46:20 I'll buy you a pack of 10. I want curly braces. Yes. Curly braces are also cheap. Yes, yes. Single line if statements still need curly braces. In my book. Okay.
Starting point is 00:46:38 So one of the bad things about pound define, I mean, with the whole redefinition, there's no checking. But also we talked earlier about const and pound define i mean with the whole redefinition there's no there's no checking but also we talked earlier about const and pound define every time you define something and you put it in your code it's actually replacing it's putting that code in your code yeah it is really the script replace and so it can take space it can take processing power if you are putting in something that is secretly using a lot of cycles it can generate confusing errors that will be difficult to track down yes because they're inside your macro and when you're using your debugger it's a it's like a single step and all you say is like max and you're like yeah well max is just blah
Starting point is 00:47:26 yeah it's a function you can't debug it's a function you can't yeah because you have to go up and see and then you have to mentally go like well i'm just putting in these two variables just like we talked about before if i do i plus plus why is i suddenly this or why how did i get to be 12 even though all i did was a min variable and it started out being one right yeah although i question how you wrote max if it's suddenly 12 and it was you would put it at one incredibly inefficiently that's the other thing is that you know you're using macros and maybe you didn't read you didn't read your macro or read whoever wrote that macro. So I would say use them carefully because they can have unintended consequences. But they can also be very fast. Yeah.
Starting point is 00:48:12 For example, the min function, if you used a real function, then you have to push your registers onto a stack. You go to the new function. You do the function stuff and you pop the registers off the stack, which is a lot of function, you do the function stuff, and you pop the registers off the stack, which is a lot of work just to find the minimum of two variables. And if you have to do that for floats and doubles and ints and uints, you end up with 25 functions when really you could do a macro and it's simple. Right. You let the compiler do what it does best is figure figure out what the hell you were trying to talk about to begin with let's see um actually you know we also didn't talk about inline well inline i mean i don't see it used all that often anymore
Starting point is 00:49:00 it's still a way of telling the compiler this function is super short. Go ahead and replicate it everywhere you want to. It's a lot like a macro except it still has typejacking. Yeah. And some compilers I've used you can debug an inline and some you can't. Depends on how it puts that together. It's kind of like optimizing. It's confusing. It is a little yeah. But a lot of times with the inlines, you don't have the push and pops either. It really is just copying that one line of code to wherever that function is called. Right. But hopefully it's going to be more than one line of code for your inline. Well, you know, those four lines of code. Right. We use really big lines. You jack the font size up to like 25.
Starting point is 00:49:51 But inline is another way of really getting to know your compiler and knowing what it's going to do and checking that list file. And we talked a little bit about pragma pack, but we need to talk about other pragmas. No. One of the pragmas that i see a lot for embedded systems has to do with memory spaces especially for people uh who need to put certain pieces of code in certain places if you're writing a bootloader the bootloader has to go in one place and the application code needs to go in another and so you have to tell the code what's what and if there are variables that are passed from one to another,
Starting point is 00:50:27 one area of code to another, they may need to be hard-coded at a certain location. They may need a near-far pointer to be generated. Well, sometimes there's the at symbol that says put this variable at this location in memory, and sometimes there's a pragma that will do the same thing. That is pretty compiler dependent. And it's also useful even for functions.
Starting point is 00:50:54 I mean, the bootloader code may need to be in a separate area, but also some functions may need to run in RAM if RAM has fewer wait states, if it's faster than other code space. So certainly knowing not only how your pointers are working, but also how your memory works if you are writing functions or if you're doing embedded systems where you're moving things around in interesting ways. The one that I see most often for pragma is pragma interrupt.
Starting point is 00:51:26 But that wholly depends on what compiler you're using and what chip you're using. But don't be surprised if you ever have to use pragma just to do an interrupt. And sometimes there's an interrupt keyword, but that's very compiler dependent. Yeah. Which is why we highly recommend reading your compiler's user's guide. There is something special about knowing a tool in your toolbox. Last time Jen was a guest, we talked about voltmeters, and listening back to that show,
Starting point is 00:51:56 you can hear the affection we have for our voltmeters, for our particular ones. We each had them. And programming languages are a very sophisticated tool. I like knowing the oddities. I like occasionally reading some of the generated assembly. The list files are magical. And I do it sometimes to see if I'm smarter than the optimizer.
Starting point is 00:52:21 But that means you have to understand the language. So if you're one of those people who only read the first half of the book for whatever language you have, seriously, go back. Read the good stuff. It's towards the end. Read the appendices. The neat, the really neat stuff starts long after the conditional and loops are described. So what's one of your favorite keywords that you found in the appendix? Oh, volatile. And not only because it is so very, very useful,
Starting point is 00:52:54 but because it's a good word. I mean, situations are volatile. Chemicals are volatile. Fourth of July is a volatile holiday. I just, it's a great word. Pea stocks are volatile. Yes. What about you?
Starting point is 00:53:07 What's your favorite hidden gem? That one's a difficult one. If I was going to talk about something that was, first I'm going to give you like kind of like a basic one in, in, in, within C and C++. And those are going to be the error and warning. Oh, pound error and pound warning. Yeah, because those can prevent you from doing a lot of stupid stuff. Ah, yes.
Starting point is 00:53:30 Yes, I have one project where it's a pound warning. You have compiled with JTAG enabled. If this is a production build, shoot yourself in the foot again, and I will not save you. Well, you could type that. But you could type something like, for example, you're just trying to figure out which files are included in your build and which ones aren't.
Starting point is 00:53:52 You can always just put a pound error, and you'll find out really quickly if that file is being included. I know that's kind of a cheating way of doing that, but if you have a very complex, let's say you're in Android, and you're just trying to figure out which file it's pulling from based on whatever lunch option you have. I know there's lots of Android people out there that are listening to this, I'm sure. And just being able to put in a pound error can save you hours of trying to figure out what the hell all these different make files are trying
Starting point is 00:54:25 to include into your project. And because the pound warnings happen in the order the preprocessor gets them, you can use it to figure out what order your header files are happening. And pound error is great for if you are looking for a define and if that define doesn't exist, say hardware version. Or it's different than when you expected it to be. You can say, look, I can't compile because I don't know what it was you meant. I don't know what hardware version you're running, so I'm not going to generate anything for you.
Starting point is 00:54:53 Right. So if you thought you were going to be compiling Bob's electric pants, but then later on it turned into Trudy's electric skirt, you can use that pound warning to basically alert you to that fact. Yeah. Pound warning. You don't have pants on. You don't? Okay. So that's my favorite C ones. But if we're going to talk about peculiar ones that I've come across, it has to be in C sharp. And, you know, so, so many times you, you know, you see, C and C++ people out there, you're writing code, you're using pointers all willy nilly. And now you have to write an app in Java or C sharp sharp that's going to interface with your c tool that you've made to interact with the rest of your product and so using c sharp and you've used a pointer and you get this
Starting point is 00:55:56 really peculiar warning that says whatever you're doing you're using a pointer and it's unsafe oh unsafe pointers right because pointers are unsafe, but that's okay because we're grown-ups and we can handle it. Right. In the C and C++ world, we are grown-ups. We've been doing this for ages, but because, well, we're humans and we make lots of mistakes, computers have deemed that unsafe. And so they want to make sure that they're told when unsafe code is being written. So there's actually a keyword in C Sharp that's called unsafe.
Starting point is 00:56:33 And so that's like the contract that you sign when you're about to go bungee jumping that says, I recognize that if I die, it's my own fault. So the unsafe keyword is my contract to the compiler that says, I recognize that I am destroying the safety of your system. Yeah, basically, that I could create hilarious memory leaks. I could malloc and alloc and, well, totally forget. Forget to type free. So, yeah, so don't be surprised if you're in C, C++ world
Starting point is 00:57:15 and you're writing lots of pointer-intensive stuff and then you need to do something in C Sharp to interface it and you get these weird errors that talk about how unsafe you are and you don't know what to do, just type the keyword unsafe ahead of it and you'll be okay. We could add that to C, but we'd have to keep typing it over and over and over again. Well, thank you for chatting with me. I do hope you'll be back soon. I hope I will be back soon in one piece. Yes, that startup is going to be exciting.
Starting point is 00:57:50 Yay. Thank you to Christopher White for producing the show. It isn't an easy job, but he's really good at it. Thank you to listener Nick for your kind words and offer of production help. I really appreciate it. And if you'd like to help or be on the show or tell us your favorite part of a language or ask questions or just say hello,
Starting point is 00:58:09 hit that contact link at embedded.fm or email us at show at makingembeddedsystems.com.

There aren't comments yet for this episode. Click on any sentence in the transcript to leave a comment.