Simulating embedded systems response, similar to tht of an MCU
A feedback controller in most cases would be implemented digitally inside a micro-controller. In a lot of cases it can be hard to take into account the effects of ADC sampling and delays caused by the processing inside the micro-controller.
In this blog I demonstrate how these effects can be effectively simulated. I take the example of ATmega328p (the MCU present in Arduino uno) and simulate the response when it is using ADC readings to drive a PWM output(ADC reading determines the duty cycle o the PWM). I will not be simulating all the registers and load a binary (something which soft-wares like micro-chip studio do). I will be creating a simulation which gives the response similar to the one one would have when implemented in the MCU).
I will be using ngspice as the software for simulations and an Xspice code model will be created for the purpose of simulation.
First I show the interface specification file of the model.
The clock pin is the driving clock of the controller adc_in is the adc input pin and pwm is the output pin which drives the pwm output.
The functioning of the model is defined in the cfunc.mod file.
Model definition
Every MCU instruction is executed with a clock cycle. In this section I describe how is the clock implemented for the model. From a circuit point off view, clock is given as a square wave (PULSE in ngspice). I order to detect an edge of the clock, a differentiator is implemented. If the output of the differentiator crosses a certain peak then that means an edge has been detected.
the exact c code is :
The last if statement would return true only when there is an edge of the clock.
MCU registers
The micro-controller is modeled as a separate structure :-
The ADCL is the data register of ADC where the digitized value is stored. PC is the program counter. TCNT the timer counter for pwm, gp_register is a general purpose register. The MCU is initialized as a static variable :
ADC
In this section I will describe the ADC of the model. For this example I am assuming that the ADC of ATmega is in "free running" mode where it takes 13 clock cycles for a single conversion. (for more details refer to chapter 23 of ATmega328p datasheet). In this mode the next conversion on the next clock edge itself. There is no need of external edge or changing the ADSC bit.
![]() |
ADC of Atmega in free running mode (taken from datasheet) |
In the code this would look like :
mcu.ADCL is the data register, a2d is the value that will be stored on completion of adc conversion cycle. (note that the value captured /sampled now will be stored in register after 13 cycles). adc_counter updated on each clock edge to keep track of the adc clock cycles.
PWM model
For this example we take the pwm timer to be in fast pwm mode. Where the output is set (high) on compare match with a compare register and cleared on a timer reset (when it reaches 0xFF). (refer to chapter 14 of atmega datasheet).
![]() |
8 bit timer in fast pwm mode |
here also the compare match register and the counter register can be modeled by 8 bit unsigned integers. This can simply be modeled in c code as follows :-
pwm_o is the output bit of pwm.
Updating the compare register
This is the tricky part. We need to update the output compare register of pwm (output_comp) with the data register of adc (adc_data) in an infinite loop. In c code this would be as simple as writing :
However when the MCU actually executes this line of code it will be executing three instruction :
These three instructions are modeled as separate functions.
As would be expected in a real MCU, after each instruction the program counter is updated. The jump instruction makes the jump to the start and R0 is modeled by the gp_register (general purpose register). The entire program is modeled as an array of these three functins :
Each instruction will be executed in every clock cycle depending on the prog counter value.
The instruction itself will update the prog. counter (mcu.PC) as in the instruction functions, and in the next clock cycle the corresponding instruction will be executed.
The entire cfunc.mod file is given below :
Results :
Compiling the above xspice code model with ngspice source (with name atmega_adc_pwm) and running the following examle file :
we get the following reslt :
Increasing the adc input voltage increases the pwm duty cycle. :
for v= 5 for example :
Conclusion:
This
simulation can take into account ADC sampling rate and delays due to
processing within the processor. (for example in a buck converter the
delays will effect the response time of the converter).
In this post I have demonstrated a method by which an MCU can be accurately simulated. This is a crude and quick fix method. A better way (which would also require some more work) would be to simulate all the registers and the entire memory of an MCU and "load" a binary and execute each instruction and update the corresponding registers (and pins).
I am also working on an entire model for an MCU. If you are also interested in simulations o embedded systems and they can aid in your project, do contact me:
mail : chittoraharsh98@gmail.com
fb : https://www.facebook.com/harsh.chittroa/
LinkedIn : https://www.linkedin.com/in/harsh-chittora-346540149/
Comments
Post a Comment