Fluid Edge Themes

  GEMscript and Virtual Machines  If you've been using GEMstudio, you’re probably familiar with our programming language, GEMscript. We designed GEMscript to be a user-friendly, C-like language with the intention of enabling a “write once, run anywhere” approach. This means it can be used seamlessly across all our platforms, including GEMplayer on PC and various hardware devices.  GEMscript is a VM (virtual machine) based language, meaning your code gets compiled into "bytecode" and runs in a VM interpreter instead of being compiled down to native machine code. This allows us to achieve our goal of running the same compiled code across multiple platforms. Additionally, by sandboxing GEMscript from our OS in a VM, we avoid some pitfalls of writing in native C, such as unsafe memory access and code execution. However, there’s a big trade-off when using VMs: speed.  It's no secret that VMs are generally slower than native machine code. Therefore, optimizing VMs is crucial, particularly for limited hardware where every bit of speed counts. So, I rolled up my sleeves and started exploring ways to optimize our code. And guess what? I found a neat and easy trick for speeding up opcode dispatch. This article discusses the performance improvements I achieved by optimizing opcode dispatch in GEMscript using GCC's "Label as Value" feature, demonstrating a significant speed increase.  Enhancing VM Performance: Speeding Up Opcode Dispatch  When I started looking at our VM, I realized that focusing on opcode dispatch could yield significant performance gains. Efficient opcode dispatch is key to faster execution because it reduces the overhead of interpreting each instruction in the VM. Let me put this in simpler terms:  Imagine you’ve got a list of vocabulary words to study. One way to do this is by using the index at the back of a dictionary. For each word, you:  Look up the page number in the index.  Flip to the correct page to read about the word.  Return to the index for the next word.  Doing this for each word is straightforward but slow and tedious.  Now, imagine using index cards with all the vocabulary words printed in order. For each word, you:  Read the information on the top index card.  Move instantly to the next card.  No more flipping back and forth! This method is much faster and more efficient, even though it takes a bit of effort upfront to set up the index cards. Similarly, by optimizing our opcode dispatch, we can make our VM run instructions more quickly and efficiently.  The Basics of VM Interpreting  Let’s start with a basic interpreter loop for a VM, which is similar to using the dictionary index method. Instead of a list of vocabulary words, we have bytecode, which is a list of opcodes in memory. Instead of searching an index for the correct page, we use a switch statement, with each opcode case representing a different operation. This switch case is in a loop, so we repeatedly look for our current opcode and execute it until we run out of opcodes. Here’s a simple example in C:  void runVM(){     uint32_t pc

  As a seasoned firmware engineer, I've encountered my fair share of perplexing bugs. But few have been as challenging and enlightening as an insidious SDRAM initialization bug I stumbled upon in the free software provided by a prominent chip manufacturer. In this blog post, I'll take you through the journey of how this bug was discovered, the process of unraveling its mysteries, and the eventual triumph of fixing it.   The Discovery  I was tasked with starting to develop for a new MPU, so I bought three identical evaluation kits. The kits didn't come with a display, but they did have a connector so a display could be added, which I did. I downloaded the MPU manufacturer's suite of software for bare metal since we were not interested in running Linux on this particular MPU. The free suite of software included startup code, many examples using the individual peripherals found on the MPU, as well as drivers for those peripherals. This evaluation kit had external SDRAM and included software to initialize the specific SDRAM used on the kit. Everything seemed straightforward, and I got our software running on the MPU quickly thanks to the included suite of software. Everything seemed to be working well, so I was getting ready to hand off one of the three evaluation kits to another developer.   As a quick sanity check I put the same software that was working fine on my evaluation kit onto the second kit and to my dismay, the system behaved erratically. The LCD sometimes showed garbage (seldomly in the same locations), there were lockups at random times, and the kit generally exhibited unpredictable behavior. The bad behavior didn’t show up immediately but would usually happen within a minute of powering up.   The Investigation  Since I had a third development kit, I put the same code on this kit and noticed similar behavior to the second kit. It appeared that I had been lucky that I chose the initial kit first. I then did a longer-term test on that first kit just to confirm that it didn't have the same issues. But, regardless of how long it was running, this first kit worked perfectly every time.  I went back to the second and third kits and confirmed they consistently showed bad behavior at random times, usually within the first minute of powering the system up. Sometimes, the hardest part of debugging a problem is being able to consistently get it to exhibit the problem, so in some respects, I was in a good position to start debugging. Unfortunately, even though the kits were consistently exhibiting bad behavior, it was seldom the same error at the same time.   When starting to debug, before making any code changes, I usually create a feature branch in git so I can always return to a state where the error was known to exist. The next step I take is to observe as much as I can about when and how the bug appears. Before blindly debugging, I like to get as much information