Logical Errors
When all syntax errors have been eliminated the program will assemble successfully, and the hex file will be created. However, this does not necessarily mean that it will function correctly when downloaded to the chip; in fact, it probably won’t! Usually there will be logical errors, particularly when learning the programming method. Mistakes in the program functional sequence or syntax will prevent it operating as required. For instance, the wrong register may be operated on or a loop may execute correctly, but the wrong number of times. There may also be ‘run-time’ errors, that is, mistakes in the program logic that only show up when the program is actually executed. A typical run-time error is ‘Stack Overflow’, which is caused by CALLing a subroutine, but failing to use RETURN at the end of the process. This is not detected by the assembler.
Simulation
Once the program has been successfully assembled, it could be tested by downloading to the hardware and running it. If there are logical errors, and the output is incorrect, the source code must be modified, rebuilt, downloaded and tested again. In the early days of microprocessors this was the only option, but it is time consuming and inefficient. The only way round this was the use of an emulator system, which plugged into the processor socket in place of the MPU and allowed register monitoring, single stepping and breakpoints to be used. Such emulator systems were expensive, and the program read-only memory (ROM) still had to be reprogrammed out of circuit each time the source code was corrected, unless this was also emulated.
Some form of virtual testing of the program sequence was therefore desirable, to eliminate logical errors before downloading. The rise in the use of microcontrollers meant that, in small systems at least, the program memory, processor and peripheral interfaces were all on one chip, which could be simulated in software as a complete package. This allowed the program logic to be tested and modified more quickly, with all the internal registers displayed while tracing through the program for the correct sequence of operations. Source code debugging allows the execution point reached in the source code to be identified while single stepping through the program. Step-over and breakpoint setting allow execution of selected portions of the program at full speed to reach the problem areas or the code quickly.
Thus, a major advantage of the microcontroller over an equivalent discrete microprocessor system is that the design of the chip is fixed, so a full simulation model can be supplied for each device. The Microchip simulator tool MPSIM allows windows to be opened to show the source code, machine code, registers, simulated input, timing checks and so on. The MPLAB development system, including the assembler and simulator, has always been provided free by Microchip to encourage the development of the market for its chips.
Proteus VSM provides an alternative debugging environment, which is integral with ISIS schematic capture. It can be run independently or as a debugging tool from within MPLAB. The latter arrangement is preferred for more complex assembler and C applications, when the MPLAB project management tools are required. For simple assembler programs, MPLAB is not required. The schematic is entered using the ISIS graphics editor, and the program written using the integrated source code editor. It is assembled using the same Microchip MPASM assembler as provided in MPLAB integrated development environment (IDE), which is included in the Proteus package.
The resulting HEX machine code program file is then attached to the MCU. The program can then be run and debugged in a source code window, which includes three single stepping options and simple breakpoints. The procedure for interactive testing in Proteus VSM is detailed in Appendix E.
Program Testing in MPLAB
The simulator must model the operation of the selected microcontroller as completely as possible. The user must be able to provide the inputs that would occur in the actual system, and to monitor the effect on relevant registers, especially the outputs. The program will need to be started and stopped at critical points, single stepped to check the sequence of operations, and timing measured. All possible input events and sequences must be anticipated and tested, to ensure that no unforeseen problems arise when the application is in use.
In MPLAB, with the source code loaded and assembled, select ‘Debugger’, ‘Select Tool’ and ‘MPLAB SIM’ from the main menu. The debugging toolbar should appear with the buttons:
• RUN: Execute the program.
• HALT: Stop the program with the current execution point indicated.
• ANIMATE: Run the program in auto-step mode.
• STEP INTO: Execute program one instruction at a time, including subroutines.
• STEP OVER: Single step current routine, but execute subroutines at full speed.
• STEP OUT: Exit from the current subroutine at full speed and wait.
• RESET: Start again at the top of the program.
These controls allow the source code to be debugged using the single stepping options with breakpoints. Various windows can be opened via the View and Debugger menus, which assist with debugging in MPSIM. The most commonly used are described below (see Figure 9.1).
Edit Window (Open/New File Buttons)
The Edit window is used to create and subsequently modify the source code, PROG1.ASM, as a text file. Ensure it is saved in a project folder with the rest of the assembler fileset, as further simulation files will be added. When the program has been assembled and is then run from the debug controls and halted, the current instruction is indicated in the source code window by a green arrow. Breakpoints can be inserted by double-clicking on the line required, causing a red marker to appear in the margin.
Special Function Register Window (View Menu)
All the special function registers (SFRs) are displayed in this window in hex, binary and decimal (right-click on the column headings bar to select the format). We may want to read
The functions of these registers have been described in Chapters 5 and 6. More complex chips have additional SFRs, and the address of a register given here may be different. For example, the 16F887 has ports AeE, using registers 05e09, so the electrically erasable programmable read-only memory (EEPROM) access registers are moved to bank 2, address 10Ch and 10Dh. The SFRs displayed will therefore change according to the chip selected in Configure, Select Device.
Watch Window (View Menu)
A watch window allows selected registers to be displayed, that is, only those of interest in a particular application. SFRs and user-labeled registers are added separately, and the numerical format for each can be selected individually. This allows, for example, counters to be displayed in decimal, and port and status bits in binary.
Simulator Stimulus (Debugger Menu)
In most applications, a sequence of inputs needs to be generated to test the response to all possible combinations. In MPLAB, a workbook is created to specify and store the simulated inputs. The simplest method is to use the Asynch (asynchronous input) table; each input pin is assigned a row in a stimulus table, and is operated manually during the course of the simulation. Alternatively, a schedule of input changes may be created which allow the same test sequence to be generated each time the program is run using the Pin/Register Actions tab.
Stopwatch (Debugger Menu)
The stopwatch window records the simulated time elapsed and number of instructions executed. It can be zeroed to measure intervals between events, for example, the delay created by a software loop, or the period of an output pulse. The MCU clock rate must be entered via the Debugger, Settings dialogue to match the oscillator frequency to be used in hardware.
Trace Window (View Menu)
As the program is executed, the trace window displays a disassembled version of each line with the corresponding machine code and addresses, so the changes can be checked and recorded. At the same time, the original source code is displayed in a lower window.
Logic Analyzer (View Menu)
This displays the input and output bits individually or in groups on a timebase display, as would be seen on an oscilloscope. This gives a much more immediate view of the system performance and allows event timing to be more easily checked.
Setting up MPSIM
The setup for a typical application, MOT1, will now be described step by step. If necessary, assemble the program, using the Project, Quickbuild option, or rebuild the project using the Build All button. Ensure that the correct processor is selected, via the Configure, Select Device dialogue (16F84A for the MOT1 project). At the same time, it is advisable to set the chip fuses via Configure, Configuration Bits. For MOT1, uncheck ‘Configuration Bits set in code’, selecting oscillator ¼ RC, watchdog timer off, power-up timer on and code protection off. Set the processor clock frequency via Debugger, Settings, Osc/Trace (100 kHz for MOT1).
The program can now be run and stopped to make sure the simulator is working. When halted, the current execution point is indicated in the margin of the source code window. We now need to set up the simulator so that the relevant information is displayed and the correct program function can be confirmed, or logical errors corrected. Simply running the program in the simulator does not generally provide enough information to confirm its correct operation, let alone to debug it. Single stepping allows the program to be executed one instruction at time; the registers can then be checked for the right contents as the execution progresses.
In the Watch window, use Add SFR to display the PCL, WREG, PORTA and TRISA registers, and Add Symbol to display the Count and Timer registers using the buttons. Select and right- click on PORTA, select Properties from the drop-down menu and change the display format to binary so the state of the individual bits can be seen. Repeat for TRISA. In the same dialogue, change the format of the Count and Timer registers to decimal.
Testing with Asynchronous Inputs
We are aiming for a setup as shown in Figure 9.1. The simplest method of simulating inputs is the asynchronous stimulus. Single-bit inputs are changed by the user via on-screen buttons while the program is executed in single-step mode. The source code, Stopwatch, Stimulus, Watch and Trace windows are displayed.
The trace facility (View, Simulator Trace) provides a comprehensive record of program execution by displaying the program line from the list file alongside the source and destination register address and contents (SA, SD, DA, DD) after each instruction, as well as the number of cycles completed. This can be saved, printed and studied. The original source code is also displayed in the lower pane when single stepping.
To set up the simulated inputs, select Debugger, Stimulus, New Workbook and the Asynch tab. In the Pin/SFR column, select RA2, RA3 and RA4, and Toggle mode for each in the Action column. Add a comment stating its function. Set all the inputs high by clicking on each fire button and stepping once. Check that the port A input bits are set high in the SFR window.
Reset the program and single step through the initialization sequence. Step through the initialization sequence to the label ‘start’ and check that the register ‘Count’ is initialized correctly. With the ‘run’ input high, the program should wait at the ‘start’ label. Now clear
input RA4 by hitting the ‘Fire’ button, and step to the start of the delay sequence. Once in the delay sequence, single stepping is not helpful, so we will now use breakpoints to test the main loop. Set a breakpoint at the ‘cycle’ label by double-clicking in the source code line number margin; a red marker appears. Set a breakpoint at the BCF instruction as well. Reset the program and run to the first breakpoint.
Zero the stopwatch, run to the next breakpoint and note the time (15 ms). Zero the stopwatch again and run to the first breakpoint. The time should be similar (16 ms), indicating a mark/ space ratio (MSR) of approximately 50%. At the same time, check that RA0 is toggling each time, which indicates that the output PWM signal is present.
The up and down control can now be tested. Hit the ‘Up’ button (RA2) and check that the Count value increments for each output cycle. Disable ‘Up’ (high) and enable ‘Down’ (low). The Count value should decrement. Now disable the breakpoints (right-click) and run the program. The Count value should go down to 1 and stop there. Now enable the ‘Up’ input and check that Count goes to its maximum value (255) and stops there. With the breakpoints reinstated, the stopwatch can be used to check the minimum and maximum MSR values.
The single-step facility has two options, ‘step into’ and ‘step over’. Step into means execute all the instructions including those in subroutines. As we have seen above, the delay sequence is not suitable for single stepping as it is repetitive. If the delay is in a subroutine, as in BIN4, step over can be used; this will step through the current routine, but will run any subroutines called at full speed. This allows each program block to be tested separately. However, as the delays in MOT1 are not subroutines, breakpoints are used to allow them to run at full speed.
Testing with Scheduled Inputs
Testing a program can be automated using a stimulus workbook. The state of an input or file register is changed at a particular step in the program, and the same test sequence can then be applied each time the simulation is run, making the testing quicker and easier, particularly in more complex applications.
The workbook is opened via Debugger, Stimulus, New Workbook. Select the ‘Pin/Register Actions’ in the workbook window, and ‘Click here to Add Signals’. The output (RA0) and input (RA2,3,4) bits are added to the active window, and the bit state is changed at specific times, measured in instruction cycles. The stimulus sequence used for MOT1 can be seen in the stimulus window in Figure 9.2.
The simulator logic analyzer in the view menu will provide a time-based trace of changes in individual input/output (I/O) register bits. The Channels button opens a dialogue where the required bits are selected for display. In order to record the outputs over a suitable time period, a break must be set after a suitable number of cycles. This can be setup in the Debugger,
Settings dialogue, by checking the Break on Trace Buffer Full box and setting the buffer size to, say, 1000k for the stimulus inputs scheduled in the workbook as above. This provides enough time for the inputs to take effect, over a period of about 10 s.
A trace of the output at RA0 is obtained as shown with these settings (it appears solid at full scale). It can be seen that it is switched on when RA4 (run input) is low, and off when RA4 is high. RA3 (down) low decreases the MSR and RA2 (up) increases it. To see the effect of these inputs on the output waveform, the zoom controls must be used to expand the waveform in a selected area. The overall timescale has to be adjusted so that the duty cycle has enough time to reach the minimum and maximum values. Some experimentation is needed to get the right combination of overall timescale and input timing. The scheduled test allows exactly the same test sequence to be used each time, and a record kept if required, at the cost of increased setup time.