wiki:MainAvrSimulationLoop

Main AVR simulation loop

There are different approaches to implement simulation of the digital systems. Single simulation loop is among them. It has its own advantages:

  • Easy to implement, maintain and understand in code
  • It plays a role of simulator-wide "clock source"
  • No synchronization with a CPU where simulator is executed on needed

and drawbacks:

  • Single threaded by design (despite the fact that operations within single iteration can be performed in parallel)
  • No interactive mode

Generally speaking, simulation process can be performed faster or significantly slower on the different CPUs. It depends on many factors including firmware quality, version of the simulator, number of models loaded, etc. You don't have to assume the same execution time scale of your firmware as it would be on a real hardware.

The main loop is placed in MSIM_SimulateAVR() in simcore.c. It is important to understand that each iteration represents both "rise" and "fall" of a "clock cycle". Note that all significant changes performed during the rise part. Fall is recorded into a dump file only.

        /* Main simulation loop. Each iteration represents both rise (R) and
         * fall (F) of the microcontroller's clock pulse. It's necessary
         * to dump CLK_IO to the timing diagram in a pulse-accurate way.
         *
         *                          MAIN LOOP ITERATIONS
         *            R     F     R     F     R     F     R     F     R
         *           /     /     /     /     /     /     /     /     /
         *          |           |           |           |           |
         *          |_____      |_____      |_____      |_____      |_____
         *          |     |     |     |     |     |     |     |     |     |
         * CLK_IO   |     |     |     |     |     |     |     |     |     |
         *          |     |_____|     |_____|     |_____|     |_____|     |__
         *          |           |           |           |           |
         *          |           |___________|           |___________|
         *          |           |           |           |           |
         * CLK_IO/2 |           |           |           |           |
         *          |___________|           |___________|           |________
         *          |           |           |           |           |
         *          |           |           |           |           |
         */
        while (1) {
                /* tick models */
                ...
                /* tick MCU peripherals */
                ...
                /* execute instruction from firmware */
                ...
        }

Simulation can be terminated by setting MCU state. There are two different cases: expected termination and an incorrect one because of the unexpected conditions, test failure, etc:

                /* The main simulation loop can be terminated by setting
                 * MCU state to AVR_MSIM_STOP. The primary (and maybe only)
                 * source of this state setting is a command from debugger.
                 */
                if (!mcu->ic_left) {
                        if (mcu->state == AVR_MSIM_STOP) {
                                break;
                        } else if (mcu->state == AVR_MSIM_TESTFAIL) {
                                ret_code = 1;
                                break;
                        }
                }

It is possible for mcusim to wait for any command from connected GDB client in a stopped mode. It is useful to debug firmware loaded into simulated MCU:

                /* Wait for request from GDB in MCU stopped mode */
                if (!firmware_test && !mcu->ic_left &&
                                (mcu->state == AVR_STOPPED) &&
                                MSIM_RSPHandle()) {
                        if (vcd_f)
                                fclose(vcd_f);
                        return 1;
                }

Lua models and MCU timers (if available) are ticked during the "rise":

                /* Tick peripherals written in Lua */
                MSIM_TickLuaPeripherals(mcu);
                /* Tick timers (MCU-defined!) */
                if (mcu->tick_timers)
                        mcu->tick_timers(mcu);

Dump file is also updated during the "rise" if only we're able to accommodate a number of clock cycles passed since the beginning of a simulation:

                /* Dump registers to VCD */
                if (vcd_f && !tick_ovf)
                        MSIM_VCDDumpFrame(vcd_f, mcu, tick, CLK_RISE);

Mcusim fetches and executes instructions in the main loop. It is generally hard to say how should simulator perform multi-cycle instructions correctly. It just skips required number of cycles before finishing instruction completely (rjmp from decoder.c which occupies 2 clock cycles, for example):

                /* Decode next instruction. It's usually hard to say in
                 * which state the MCU registers will be between neighbor
                 * cycles of a multi-cycle instruction. This talk may be
                 * taken into account:
                 * https://electronics.stackexchange.com/questions/132171/
                 *      what-happens-to-avr-registers-during-multi-
                 *      cycle-instructions,
                 * but this change of LSB and MSB can be MCU-specific and
                 * not a general way of how it really works. Detailed
                 * information can be obtained directly from Atmel, but
                 * there is no intention to do this in order not to
                 * unveil their secrets. However, any details they're ready
                 * to share are highly welcome.
                 *
                 * Simulator doesn't guarantee anything special
                 * here either. The only thing you may rely on is instruction
                 * which will be completed _after all_ of these cycles
                 * required to finish instruction itself.
                 */
                if ((mcu->ic_left || mcu->state == AVR_RUNNING ||
                                mcu->state == AVR_MSIM_STEP) &&
                                MSIM_StepAVR(mcu)) {
                        if (vcd_f)
                                fclose(vcd_f);
                        return 1;
                }
static void exec_rjmp(struct MSIM_AVR *mcu, unsigned int inst)
{
        /* RJMP - Relative Jump */
        int c;

        SKIP_CYCLES(mcu, 1, 1);

        c = inst & 0x0FFF;
        if (c >= 2048)
                c -= 4096;
        mcu->pc = (unsigned long)(((long) mcu->pc) + (c + 1) * 2);
}
Last modified 5 weeks ago Last modified on Oct 10, 2018, 11:20:26 AM