Proton BASIC Compiler - The Beginner Guide to Simulation using MPLab's SIM

  • Pic® Basic

  • The Beginner Guide to Simulation using MPLab® SIM

    The Beginners Guide to Simulation using MPLab® SIM

    By Harm de Vries (hadv215 on the forum)


    This article was written because it appears not many Proton® users are aware of this powerful tool.

    The intended audience for this article is users who want to learn how to use this tool for simulation and already have some experience programming and simulating and/or debugging firmware.

    Knowing how to read a datasheet is important because the datasheets contain valuable information that is needed to get the most of this tool, but for this article you won't have to read it because everything is explained either in the code or in this article.

    Note that almost everything in this article is available in the Help system of MPLab® except, of course, the Proton® related stuff.

    Many thanks to John Drew and John G Barrat for reviewing this document.


    The document "Using the Proton Compiler within MPLAB IDE" that comes with the compiler (See: “Help/Documents/Using Proton with MPLAB” in the Compiler IDE) contains a lot of information on how to make these two work together. This article can be regarded as an extension to this document with the aim of explaining how to use SIM.
    Please follow the guidelines from the document mentioned because it will save you a lot of hassle when you want to start SIM for Proton® programs.

    In this article

    The structure of this article is as follows:
    • Part 1
      • A short description of the MPLab® environment that includes SIM
      • The basic features of SIM
      • Importing a Proton® program
      • Setting up SIM
      • Adding Watches
      • Setting Breakpoints
      • Running SIM
      • Using Stimulus
      • Using the Stopwatch
      • Proton® specifics
    • Part 2
      • The example
    • Part 3
      • The inevitable Tips & Tricks
    Thanks to John Drew for taking the time and effort to read this and give me feedback.

    Part 1: MPLab® by MicroChip®

    What is MPLab®

    MPLab® is the development environment supplied by MicroChip® for free. You can download it from the website ( ).
    This development environment can be used to develop programs for all PIC® devices.
    It includes an assembler, MPAsm, and a basic C-compiler.

    This article only deals with the SIM part of this environment and is based on version 8.92. Older versions may be used, but not all features may be available in older versions.
    MPLabX® is not mentioned in this article since I find version 8.x easier to work with for the purpose of simulation.

    What is SIM

    SIM is the integrated simulator.
    You can reach it from the main menu, but read on a while if you've never used it before.

    Simulation versus Debugging

    It may look a bit theoretical, but the difference is really important.
    Simulating means 'pretending'. The firmware is not active, it's just a tool pretending to run the firmware.
    For real debugging it is necessary that the firmware is actually loaded into the device and all its connections are present.
    Simulation also means that the functionality that can be simulated is limited. AD conversion, USART operation and other 'real life' events will not be handled.

    For real debugging you'll need a real debugger like the PicKit or ICD.

    The basic features

    The basic features of SIM are
    • simulating the program as it is executed by the microcontroller
    • breakpoints to halt the execution
    • inspecting and modifying values of Special Function Registers and variables
    • modifying the state of I/O pins
    • measuring the time elapsed between breakpoints

    Importing a Proton® program

    SIM can be used for Proton® programs if you let the compiler generate a so-called COF file.
    The compiler will do this for you if you include "Declare Create_Coff = On" in your source.
    After compiling you will find a file called "filename.COF" in the same directory as the other files of your project.
    This file contains all the information SIM needs to 'understand' your program.

    Importing a program:
    First open MBLab®, if there is a previous, unrelated, workspace opened you must close this first.
    Drag "filename.COF" from its directory to the workspace.
    You won't see it, but it will be there. If you have the "Output" window open it will tell you it has been loaded.
    Use "File>Open..." to open the program file "filename.BAS" (your source) if it has not been loaded by MPLab® already.
    You will see the source in an edit window.
    Depending on the version of MPLab® you are using it may load the "filename.COF" together with the "filename.BAS" or vice versa. Things also depend on having Proton® installed as a toolsuite.

    Setting up SIM

    Step 1: identify the device type

    "Configure>Select Device...".
    You will find a dropdown combobox on the top left side of the dialog, select the correct device type and press "Ok".

    Step 2: select the debugger

    Since MPLab® supports a number of debuggers you will have to select SIM to be the one to use.
    "Debugger>Select Tool", click MPLAB SIM

    Step 3: configure SIM

    You'll see a window containing a number of tabsheets.

    Select the tabsheet called "Osc / Trace".
    Change the Processor Frequency to reflect the frequency the physical device will use. Notice the "Units".
    This is important if you want to do time measurements.
    Deselect the checkbox "Trace All" for now.

    Select the tabsheet called "Animation / Realtime Updates".
    Draw the slider in "Animate step time" to the left (fastest).

    Press "Ok" to close this window.

    Add Watches

    Watches enable you to see what happens inside variables and Special Function Registers.
    And not only see, you can also modify the actual values.

    You can open the Watch window using "View>Watch".
    You'll now have the Watch window opened somewhere in the MPLab® desktop.

    At the top of the window you see two dropdown comboboxes. The left one showing Special Function Registers, the right one Symbols from your program.
    (If you don't see any Symbols reload the COF by dragging it to the desktop again.)
    Press on any of the triangles pointing down to add either an SFR or a Symbol.

    Select a Symbol you recognize from the source.
    Press "Add Symbol".
    The symbol will appear in the grid including the address and the current value.
    Right-click the name of the symbol and a settings dialog will appear.
    This will show the Name, the Size, the Format, a checkbox for Signed, the Byte Order (only for multi-byte symbols) and the Memory location.
    You can change most of these to your liking.

    The dialog also contains a tabsheet "Preferences".
    If you select this you can select what best fits your needs.
    You will not want to change "Float Format".
    For "Default Type Format" I advise to use "Decimal".
    An important one is "Expand SFR Bitfields" at the bottom.
    If you check this one the Watch window will allow you to expand the Special Function Registers to show the state of the individual bits.

    Press "OK".

    Select the dropdown containing the Special Function Registers.
    Select "INTCON" and press "Add SFR".
    You now have INTCON added to the list and a expand icon shown in front of the name.
    Press it to see all individual bits.
    Sorry about the defaults you selected earlier, bits are hexadecimal for some reason I don't fathom.
    You can have each SFR or Symbol more than once in the Watch list.
    This may come in handy if you want to use different formats without having to change the format again and again.
    As you can see you can have up to 4 panes in the Watch window.

    An important thing to know about the Watch is that whenever a value changes its value becomes red.

    Setting Breakpoints

    Breakpoints are what simulation and debugging is all about.
    When a breakpoint is encountered SIM will pause execution, allowing you to inspect all kind of values in the Watch or perform time measurements. The simulator stops before executing the line the breakpoint is on.
    Breakpoints are set in the source window.
    Position your mouse in the gutter and doubleclick.
    A solid red circle containing a "B" will be shown indicating the breakpoint.
    You can clear it by doubleclicking it again.

    Please note that you can not set a breakpoint on just any line.
    A line that does not represent actual instructions, like a line containing only a label or comments, will not work. It will show the breakpoint at first, but it will notify that it can't be resolved as soon as you start SIM.

    Running SIM

    After selecting SIM as the tool to use a toolbar is added to the IDE.

    This toolbar contains 8 buttons, from left to right
    • Run (F9)
    • Halt (F5)
    • Animate
    • Step Into (F7)
    • Step Over (F8)
    • Step Out
    • Reset
    • Breakpoints (F2)


    The first thing you'll want to do is set one or more breakpoints.
    Press the Run button and sit back.
    SIM will start executing the code and halt on the first breakpoint it encounters.
    You won't see it doing anything, it just halts on each breakpoint it encounters.
    At this moment you can take a look in the Watch window to see the actual values of the Symbols you have selected.
    To continue press Run again.


    Animate is like Run but you will see it stepping through the lines.
    You can slow it down or speed it up using the "Debugger>Settings" dialog, tabsheet "Animation / Realtime Updates".


    For some reason you may want to pause execution during Animation
    Press Halt.

    Step Into

    Instead of using Run or Animate you may step through the code yourself.
    Or you may want to step through the code after Run/Animate was halted.
    Pressing this button will increment a line on each press.
    It gives you a line-to-line insight of the effect of the code on the Symbols in Watch.

    Step Over

    DelayMs xxx essentially is a bore to step through.
    Trust me, it works.
    Same goes for a subroutine of your own that you have checked already.
    That's the time to use Step Over. It will just execute the next line without showing you the details.

    Step Out

    So you just pressed Step Into instead of Step Over to avoid that DelayMS xxx and you're in the middle of it but want to get out.
    Press Step Out. SIM will execute the code without you seeing it until it reaches a Return statement.


    Of course you can Run or Animate or Step until you're back at the start of the program, but your variables probably have changed.
    Time to use Reset.
    It will basically stop SIM so you can start afresh with Run, Animate or Step from the start of the program.


    This will show you all breakpoints, allowing you to remove them or add one or more.
    It will also allow you to disable a breakpoint and enable it again. This is handy if you don't want to remove/set the breakpoints each time you need to.

    Modifying values

    One of the most powerful features is that you can modify values in the Watch window.
    Just place your cursor on the value and click it. You can now edit the value.

    Take some time to consider what this means, you can actually change the flow of your program or you can set that specific index to a totally different value.

    Using Stimulus

    Stimulus allows you to change the state of I/O pins during simulation.
    Suppose you want to step through the code when a pin has changed from High to Low, on the press of a button for example.

    "Debugger>Stimulus>New workbook".

    I will only explain the basic part of this tool and that is the "Asynch" mode. The reason for this is that a tool should be simple to learn and use and, in my humble opinion, the other modes do not add to the simplicity.
    You are presented with a grid showing
    • Fire: press this to activate this stimulus
    • Pin/SFR: here you can select which pin of which SFR is involved
    • Action: a choice of "Set High", "Set Low", "Toggle", "Pulse High" and "Pulse Low".

    (The other features like "Width" and "Units" are for you to find out.)
    I usually only use "Set High" and "Set Low". I don't use "Toggle" because then I'll have to remember what state it was in the first place and I don't use "Pulse" because you will have to specify the number of cycles or time of the duration of the pulse.
    By the way, pressing the "Help" button does not work in my version, you have to go to the "Help" menu.
    When you filled in a number of these rows you can use the "Fire" cell of the line to execute the action.

    Now of course you could have an SFR like PORTB in the Watch and change pin 3 manually, but this is a bit easier.

    Using the Stopwatch

    So you want to know the time it takes to execute the code between breakpoints?
    Use the Stopwatch : "Debugger>StopWatch".
    You see the Processor Frequency you selected in the Settings.
    The most important button is "Zero", this will reset both the number of Instruction Cycles and the Time display.

    Go back to the source and set one or more breakpoints, press Run and sit back.
    When the first breakpoint is encountered the Stopwatch will show you the number of instruction cycles and the time elapsed.
    Press Run again and wait for the breakpoint. Look at the values and press "Zero". Run again.

    I use this tool often in programs containing timer interrupts because it allows me to tweak the code so that the interrupts occur at the interval I want it to be. It is also useful in programs that contain time critical sections where if needed I can use Assembler statements instead of Basic.
    Besides that it comes in handy to understand the time some typical Proton® instructions (like 'Print At') take.

    Proton® specifics

    Proton® has a number of possibilities that Assembler and C do not have or that are implemented in a way that's quite different.
    A good example of this is the Array.
    The declaration 'Dim anArray [10] as byte' will result in a number of variables that address each element of the array separately (VARIABLE ANARRAY#y, where y is the index of the element).
    You will find these in the Symbols part of the Watch, allowing you to inspect each separate element.

    Something similar goes for Proton® Symbols that refer to bits of a variable or SFR (Symbol GIE = INTCON.7). If you select this in the Symbol part of Watch you will find "GIE (bit7)" and the Address is that of INTCON.

    Part 2: The example

    The example I want to present is one that deals with timer interrupts.
    The program is for a PIC18F242 using Timer0 and a Timer1.
    It not only serves to demonstrate SIM but also how interrupts work as I explained in a separate article.

    It does not matter if you don't have any knowledge of this device, you don't have to download the datasheet to be able to understand what is happening.

    The functionality

    The program uses Timer0 to have a very fast clock that is used for dimming a number of leds on PORTB.
    The dimming process can be enabled/disabled using a button on PORTC.
    Timer1 is used as a timer that at this stage serves no other purpose than to show timer interrupts to run next to each other.

    What you will learn

    • how to use the Watch to see the variables and Special Function Registers
    • how to measure the PWM frequency on PORTB and how to tweak that
    • the effect of modifying a variable in the Watch window
    • the effect of enabling and disabling the interrupt for Timer0

    Setting up

    Follow the steps mentioned in "Setting up SIM" earlier in this document, using a PIC18F242 and set the Processor Frequency to 20 MHz.

    Import the program

    The SIMDemo.BAS is inside the zip file that comes with this article This zip file also contains the steps that are presented here in the format of a checklist that may make executing the steps easier.
    Unzip the archive and load SIMDemo.BAS into the Proton® IDE. Hit F9 to compile.
    Open the MPLab® IDE and make sure no workspace is open.
    Open the Output Window: "View>Output".
    Drag the file "SIMDemo.COF" to the desktop of the MPLab® IDE, a confirmation will be shown in the Output window.
    With a number of versions the file "SIMDemo.BAS" will be opened as well, if this is not the case with you, open it using "File>Open...".

    Add Watches

    Open the Watch and add the following Special Function Registers
    • INTCON
    • T0CON
    • T1CON
    • PIE1
    • PIR1
    • TMR0
    • TMR1
    • LATB
    • PORTC (or aButton)
    Add the following Symbols
    • iLoop
    • cPWM1, cPWM2 and cPWM3
    • iPWM3
    • OnOff
    • aButton

    Set the Breakpoints and open the StopWatch

    Set a Breakpoint
    • on the first 'real' line after "InitProgram"
    • on the line in MyISR containing "Dec iLoop"
    Open the StopWatch from the "Debugger" menu.

    Run SIM

    Part 1: What happens when a breakpoint is encountered due to a Timer0 interrupt?

    Press the "Run" button in the toolbar or press F9 and wait till it halts on the first line in "InitProgram".
    Notice the values of the SFRs and variables.
    Now use the "Step Into" button in the toolbar or press F7 to proceed to the last line in "InitProgram" and see how the values of the variables and Special Function Registers change.
    Since you stepped through the program these changes should make sense to you.

    It will halt on the line in MyISR containing the breakpoint you set there.
    Notice the TMR0IF bit of INTCON to read "1" (0x01 actually).
    We've got ourselves an interrupt!

    Take a look at the value of TMR0. You would expect it to be 0 because an overflow means the value was incremented from 255 to 0, but it will be slightly higher because a number of instructions were executed during "Context Restore".
    Take a look at the StopWatch, you'll notice the number of instruction cycles that passed and the time elapsed so far. That should be microseconds.
    Press "Zero" in the StopWatch and press "Run" again and wait until it halts again in MyISR.
    Now you know the time Timer0 needs for the next overflow.

    Part 2: Monitor the output pins on LATB.
    Remove/disable the breakpoint after the first line in "InitProgram".
    "Reset", "Run".

    On software PWM
    This program uses software PWM to dim three leds. Dimming means that the duty cycle of the PWM is changed by the software. This effect is obtained by switching the led on the pin on and off. The higher the duty cycle the longer it stays on and the brighter it will appear.
    Dimming leds also means the leds have to be switched pretty fast (at least 60 Hertz) else the human eye will detect flicker.

    The led on the first pin will start with a duty cycle of 70%, the second 50% and the third 30%.
    A full cycle takes 10 steps.
    The variable holding the internal duty cycle of each led is decremented each step until it reaches zero and stays zero until a new cycle is started, then the start up values are used again.
    Take a look at the code in MyISR for Timer0 and see if you understand what happens there. I added some comment to clarify things.

    Now you are going to look at the pins driving the leds and the way they change depending on the variable determining the duty cycle.

    Ensure the line containing "Dec iLoop" in MyISR still has an enabled breakpoint.
    "Reset", "Run".
    Now take a look at the SFR LATB. Expand it so you can see all the bits.
    Notice that bits 0, 1 and 2 are "0" (the rest does not matter).
    Take a look at the variable iLoop. It should be red and read 1.
    "Step Into" and let SIM step to the line after "Dec iLoop".
    Step further and notice iLoop is actually 0.
    Step further and you'll come to the line that will re-initialise some variables.
    Step through the lines doing so and notice the variables cPWM1, cPWM2 and cPWM3 being assigned the values of iPWM1 - 3.
    Step further and you'll come to the part where the program decides if a pin should be set or cleared.
    Since iPWM1 - 3 all are higher then 0 these 3 bits in LATB should be set one after the other.
    Verify this.

    Speed up tip: Use extra breakpoints
    Instead of pressing "Step Into" for a large number of times you can add a breakpoint at the line that follows the check of the iPWMx variable being 0. Since you still will pass the earlier breakpoint on each interrupt you better disable that one.
    Don't start with tricks like this, first check that the basic mechanism work.

    As soon as iPWM3 reaches 0 the corresponding bit will be cleared.
    "Run" and "Step Into" a number of times to verify this.
    Repeat "Run" and "Step Into" to check that after a number of loops all relevant bits of LATB are 0.
    Repeat "Step Into" to check that iLoop changes to 0 again, thus re-initialising iPWM1-3 and the story starts repeating itself.

    Part 3: modify values of variables using Watch
    This is a variant of part 2.
    "Halt" SIM if it is still running.
    Change iPWM3 to 0.
    Run the basic steps in part 2 and notice that LATB, 1 will never be set.

    Part 4: Using Stimulus to simulate a button thereby enabling or disabling an interrupt
    The program contains a variable "OnOff" that determines if the leds will be lighted or that they will be shut off.
    Pressing the button toggles the state. It starts with 'lights on', when pressed it will be 'lights off' and so on.

    When "OnOff" = 1 the Timer0 interrupt will be enabled, when "OnOff" = 0 the Timer0 interrupt will be disabled and the lights will be shut off.

    Open a new Stimulus workbook.
    Click "Action" in the first line, select RC0, select "Set High"
    Click "Action" in the second line, select RC0, select "Set Low".
    Clear/disable all breakpoints.
    Set/enable a breakpoint on the line containing "If aButton = 1 Then".
    "Run" and wait for SIM to halt on that line.
    Check the value of bit 0 of PORTC or "aButton", it should be 0 (meaning no press).
    Simulate the button to be pressed by clicking the first line in the Stimulus window (the one saying "Set High").
    Now you won't see a change because SIM does not show changes when it is halted. It will be visible though after "Step Into".
    But you will see a notification in the Output window (if you have this open).
    "Step Into" and verify that PORTC.0 is "1" and that SIM is entering the code for Button to be 1.
    "Step Into" a couple of times and notice the value of "OnOff" changes to 0 as do the TMR0IF and TMR0IE bits in INTCON and TMR0H/L.
    The reason I clear TMR0On first is that it just might be possible that the next instruction executed would result in a Timer0 interrupt and I don't want that. The reason for clearing TMR0IE is that I want to disable the Timer0 interrupt. The reason for clearing TMR0H/L is that I want a fresh cycle when the Timer0 interrupt is enabled again.
    Also notice the value of "iLoop" being set to 1 to ensure it will rollover as soon as the Timer0 interrupt is enabled again.
    Now click the second line in the Stimulus to clear RC0 again. (In a real program I would of course have used some kind of debouncing.)
    Clear/disable the breakpoint on the line containing "If aButton = 1 Then".
    Set/enable a breakpoint on the line in MyISR containing "Dec iLoop "
    "Run" and wait for SIM to halt.
    It won't. You'll just see the progress bar flashing.
    "Halt" and notice that the StopWatch (if you still had it active) will show a lot of processor cycles being executed.
    Why is this? Well if you press "Run" SIM will only halt on a breakpoint and since Timer0 was the only reason for a breakpoint to be encountered nothing happened.
    "Animate". Now you see SIM happily hopping through the lines of the While-Wend loop.

    Set/enable the breakpoint on the line containing "If aButton = 1 Then" again.
    "Run" and SIM will halt at that line.
    Verify that PORTC.0 is zero.
    Click "Action" in the first line (the line with "Set High")
    Press "Step Into" and check that variable "OnOff" changes from 0 to 1 and TMR0IE bit of INTCON is set.
    Click "Action" in the second line (the line with "Set Low")
    Check the value of iLoop. It should be 1 but if it isn't change it yourself.
    Check that the breakpoint in MyISR is still there.
    Press "Run" and at some time SIM will stop at the breakpoint in MyISR.
    That's because you enabled the Timer0 interrupt again.

    Part 5: Timer1 running on
    All the time you used SIM the Timer1 interrupt was running. You may have noticed it in the Watch window.
    Since the program has been running some time without SIM halting Timer1 may be at a considerable value.
    To be sure the Timer1 interrupt works as expected execute the following steps.
    Clear/disable all breakpoints.
    Set/enable a breakpoint in MyISR on the line containg "TMR1IF = 0".
    Press "Reset" and set/enable a Breakpoint on the line in "InitProgram" that contains "GIE = 1".
    Push "Run".
    In Watch change the value of TMR1 to read something like 65000. Remember Timer1 is a 16 bit Timer so its register is a word.
    Press "Zero" on the StopWatch.
    Set/enable a breakpoint in the line in MyISR containing "If TMR1IF = 1 Then".
    Press "Run" and wait till SIM halts in MyISR.
    Take a look at the StopWatch: see the amount of processor cycles executed and the time elapsed. That's what you get if you use a 16 bit timer.
    In the meantime Timer0 was running and dimming the leds.

    This part of the demo was intended to show you that timer interrupts are very powerful features of the PIC® that allow tasks to be executed almost 'in parallel' to the main program.

    Part 3: Tips & Tricks

    1: Never use too many breakpoints at a time.
    Breakpoints may be very confusing. If you have a lot of them in various parts of your program you will be interrupted more often than you like. The effect may be that you loose track of what you were doing in the first place.
    As a rule of thumb only use breakpoints in the functionality you want to inspect. What goes for programming goes for simulation and debugging: break it up in small functional parts.
    The 'Breakpoints' button in the SIM toolbar allows you to add, delete, enable and disable breakpoints.

    2: Handling mega loops.
    The number of steps in the example program is only 10. In my actual programs I use much higher values like 127 or even 255.
    But if you want to simulate this and have to press a button on every interrupt you'll soon get fed up.
    There are two approaches to circumvent this:
    - use smaller values since functionality like this will just work as well with 10 steps
    - modify the current values using Watch to make sure the flow of operation can be tested with less button presses.
    Note however that you should NEVER do this if you're not very sure it is safe.
    Another option is to use the Advanced mode of Stimulus. You'll have to find out yourself how to achieve what you want to.

    3: You can modify and recompile your program using the Proton® IDE while it is open in MPLab®. When you return to MPLab® a notification will be shown that the file has changed. If you select to re-load it the COF file will also be re-loaded.

    4: Read the datasheet

    5: Document your program. Especially all configuration details and the way subroutines work.
    Take special care of inline documentation after making changes, the documentation may no longer be correct

    6: Read the datasheet again

    7: Document the things you think are obvious at this moment, they will not be that after six months.