Project 4 A Pulse-Width Modulator IP Block

Creating and programming a PWM circuit to control LED brightness



This project presents the design and implementation a more involved hardware IP block – a pulse-width modulator (PWM) signal generator. A PWM circuit creates a continuous sequence of pulses, with each pulse staring at a regular interval, and then remaining asserted for a programmable length of time. The length (or width) of the pulse in each interval can be changed, and in any given “pulse window”, the ratio of signal high time to signal low time (or “duty cycle”) establishes the information carried by the signal.

Figure 1. PWM signals and definitions

PWM signals are useful in many settings: they can be filtered with a simple passive filter (one resistor and one capacitor) to create an analog voltage; they can directly drive an LED, and the perceived brightness of the LED will be proportional to the pulse high time; they can switch motor-control power FETs on and off to set DC motor speed; and there are many other uses as well.

Figure 2. PWM signals, filtering, and applications


PWM circuits are often used as simple, inexpensive DACs (Digital to Analog Converters) to create low-to-medium frequency analog voltage signals from a single digital signal. The switching digital waveform can be filtered with a low-pass filter to remove the higher frequency waveform components that originate from the pulse frequency “carrier” signal. Note the “information” in the PWM pulse train is carried by the pulse widths, and not the pulse frequency. The higher frequency “pulse frequency” is just a carrier to communicate the pulse widths, and it is intended to be filtered out.

In the case of driving an LED, the higher-frequency information is filtered out by the human eye, and not by an electronic circuit (note the human eye “bioware” can only respond to stimuli up to about 50Hz).

When creating an analog voltage output from a PWM signal, the pulse frequency should be at least 10 times higher than the output filter 3dB frequency (and preferably 100 times higher) to minimize switching noise in the output waveform. As examples, to create an analog voltage that could drive a speaker with up to 10KHz of bandwidth, a pulse frequency of at least 100Kz is required, and 500KHz would be even better. To create a bias voltage on an analog amplifier, a 1KHz pulse frequency could be used together with a 10Hz low-pass filter.

When driving an LED, the pulse frequency should be greater than 60 Hz so that LED flickering/blinking is not observable. If the pulse frequency is between 60Hz and perhaps 10KHz, the perceived LED brightness will be in direct proportion to the PWM’s duty cycle – that is, the longer the waveform is high during any pulse window, the brighter the LED will appear. (Note – if too high a pulse frequency is used, the LED may not achieve full brightness during a given pulse).

In this project, you will develop a PWM IP block to control LED brightness. The PWM circuit must be accessible/controllable from software running on the ARM, so it must instantiated as an AXI-connected peripheral device. You must create a hardware definition file to configure the processor and FPGA with your circuits, and then write software to program the PWM circuit with appropriate parameters.

When creating a PWM waveform, you must consider the frequency and resolution required by the application. The frequency is the rate at which new pulses are started, and the resolution is the number of different pulse-widths possible during one pulse window (for example, an 8-bit resolution allows for 256 different step sizes, or about .4% of duty cycle per step). Once the resolution is established, the duty cycle can be set by selecting a pulse width. To create a variable output waveform, the duty cycle can be changed in every single pulse window. To create a static output voltage, the duty cycle can be set once, and then used in every pulse window.

For any given main clock input, there is a trade-off between frequency and resolution. For example, consider a system with a 10MHz clock. If a 10-bit resolution is needed (60dB of dynamic range), then the maximum attainable frequency is 10MHz / 2^10, or a little less than 10Khz. At that frequency, an output waveform with about 1KHz of bandwidth could be constructed. If more bandwidth is needed, say up to 4KHz, then the PWM frequency would need to be around 40KHz, and that would require cutting the resolution to 8 bits (check the math!).

In a typical PWM peripheral circuit, the pulse frequency and duty cycle are set by a values in programmable registers. The pulse frequency is created using a comparator that resets a pulse-window counter, and the duty cycle is set by a comparator that drives the output waveform low whenever the count value is greater than the duty cycle value. A main clock divider is sometimes includes as well (as shown below), but if no main clock divider is included, larger values can be used in the frequency and duty cycle registers to define the same waveform.

Many microcontrollers use PWM circuits to create low-bandwidth analog signals because they require only a single pin and a simple external filter. Most microcontroller PWM signal generators can be programmed to create a pulse frequency in the 1KHz to 100KHz range, and most use an 8-bit or 16-bit value to define the pulse width (so, pulse widths from 256 to 65,384 can be defined).

Figure 3. An example circuit for creating a PWM signal

In the circuit above, the register values could be programmed directly to define a given PWM waveform. In a more abstract (better) software interface, a frequency value could be passed into a subroutine (in Hertz), and a duty cycle value could be passed as a percentage value. The Hertz value would be used to compute the PWM frequency register value, and the percentage would be used to compute the duty cycle value register. Note that the values are not completely independent – if the PWM frequency is set too high, the PWM duty cycle range is limited.


1. Design and implement a Verilog PWM Module

Create a module in Verilog that generates a Pulse-Width-Modulated output signal which is a function of the input clock and a 10-bit duty-value input. Use slide switches for setting the duty cycle of the output waveform. For clocking circuit can use Blackboard’s 100MHz FPGA clock (you don’t need to configure the Zynq processing system for this requirement. Using the full width of a 10-bit window, you can generate a 97KHz signal (by decreasing the window width you can get faster frequency at the cost of duty cycle granularity). You can also divide the clock going into the module for more control of frequency. A reasonable frequency might be in the range of 20KHz. Use the output of your PWM module to drive an LED or RGB LED on the blackboard. Explain to your TA how changing the duty cycle of the output waveform affects the LED.

For later requirements you can implement configurable window width and a configurable clock divider, however you don’t need to control them in this requirement. It is acceptable to use a fixed window width and clock frequency for this requirement.

2. Create an AXI-connected PWM Controller to control the color of an RGB LED

Integrate your PWM module from requirement 1 into an AXI4 connected IP core. Instantiate 3 PWM circuits within the core, each with a 10-bit duty value driven from a slave register. Add an ‘enable’ input to each PWM module which can enable disable the output (when the enable is high, the module output drives the channel’s LED, when the enable is low, the output is always zero). As with requirement 1 you can used a fixed clock frequency for the PWM clock and a fixed window width. Hook up the outputs of the IP core to the 3 channels of an RGB LED on the blackboard.

Write a basic driver to enable/disable PWM channels and write/read duty values from each channel. Use your basic functions to make a function rgb_set_color(uint8_t R, uint8_t G, uint8_t B) This function should set the values of the respective red, green, and blue channels of an RGB LED module. As 24-bit color (8 bits per color channel) is a web standard you can use a hex code color choose to set the LED color to something you like. You will need to convert the 8-bit color channel data to the 10-bit value to drive the module input, this can be accomplished with a simple bitshift (Make sure to check your destination data type can hold 10-bits or you will lose data!!).

Write a program that displays a sequence of four different colors on an RGB LED (A boiler plate loop is provided), delay for a reasonable amount of time between color changes. Use colors that are impossible to achieve when driving the RGB’s with DC values. To impress your TA for extra credit you can use timing loops with your functions to smoothly ramp between different the colors. The more impressive your color patterns are, the more extra credit you could earn!

A potential register definition is shown in the table below. It is recommended you allocate extra AXI registers when you create the IP core in Vivado’s IP packager as you may need them for requirement 3.

Address Name Bits used Function
BASE_ADDR+0 RGB_DUTY_R 9:0 10-bit PWM duty value for Red Channel.
BASE_ADDR+4 RGB_DUTY_G 9:0 10-bit PWM duty value for Green Channel.
BASE_ADDR+8 RGB_DUTY_B 9:0 10-bit PWM duty value for Blue Channel.
BASE_ADDR+12 RGB_PWM_CTL 2:0 Enable/Disable output separately for each channel. If a Channel’s bit is 1 the output of its pwm signal drives the output. If a Channel’s bit is 0, the output is always zero

You can use this loop code in your program to get a head start.

//Note: You can't use these values to get full credit, as they would be driving the LEDs all on or all off
uint8_t red_seq[4] = {0,0,255,255};
uint8_t	green_seq[4] = {0,255,255,0};
uint8_t blue_seq[4] = {255,0,255,0};

//RGB control loop
	for(int i=0;i<4;i++)
		//set color of LED
		//delay for a noticable amount of time


3. Create an AXI-PWM Controller that controls Duty Cycle, Window Size, and Clock Frequency

Modify your PWM module so you can:

  1. Dynamically change the pwm counter’s clock frequency.
  2. Change the window width of a PWM period (change how many clock periods make up a cycle).

Use additional AXI registers to control these parameters from the zynq processor. You only need to implement configurable window width and clock frequency for a single channel, all others can be driven with constant values.

Write a program to show your TA that you can set the frequency of a channel both coarsely (through use of a programmable divider) and finely ( through adjusting the window width). A program demonstrating this might set the clock divider so the channel visibly blinks and then adjust the period to illustrate a slow ramp up and down in blinking speed.

PWM Timing Parameters

You can use a configurable power of 2 divider to divide the clock into the counter of your PWM module, Provided below is a verilog module you can use as a divider. The module divides the input clock frequency by 2^(N+1).

//power-of-2 clock divider
//output clock frequency can be chosen through the input 'sel'
//output clock freqeuncy is clk_in/(2^(sel+1))
module conf_div (
        input clk_in,
        input rst,
        input [3:0] sel,
        output reg clk_out

        wire cl_sel;
        reg [15:0] count;

        assign cl_sel = count[sel];

        always @(posedge clk_in)

        always @(posedge clk_in)
                clk_out <= cl_sel;

Please not that as the output clock is not a true clock, caution should be exercised when implementing designs with this divider. For the requirement of driving a PWM module’s it will not need to drive logic and the tools will most likely route the clock to nearby CLBs. For larger and/or more complex designs, it is recommended you use the actual clocking resources on the board or using clock select pulsing for timing.

For the above module when N=0, the divider value is 2^1: half the input frequency, when N is 15, the value is 2^16 (which is 65536). The provided divder, supplied with 50MHz clock, will output 25MHz down to about 763 Hz, which will be the bit clock into your pwm counter. You can add extra bits if you need slower clock speed but as all of the module’s counter bits muxed into the output bit, if your input bit width is too large your design may fail timing.

A 10-bit counter has 1024 values before rolling over, thus for a pwm module with an input clock of 50MHz, a divider value of 2^16, and a full 10-bit window width, the output PWM frequency would be (50e6/2^16)/1024, about 0.75Hz. If the window width was set to 256 width the same clock frequency, the resulting output frequency would be about 3Hz. With the lowest output frequency, 25MHz, with the same window widths the PWM frequencies would be about 97.6KHz and ~24.4KHz for 256 and 1024 clocks per window, respectively. Therefore, a 10-bit PWM module with a configurable window width and a power of 2 divider can a very wide range of frequencies without even going below 8-bit resolution.

The PWM window period can be calculated using: (W_width * 1/(F_clock / 2^(div+1))), which is the number of clock cycles in a window, W_width, multiplied by the divider output clock period, 1/(F_clock / 2^(div+1) (inverse of the divided frequency). The frequency of your PWM cycle is of course just the inverse of the period. You can observe that if you increase the window width, you also decrease the period and vice versa. As changing the window width also changes the resolution of your PWM signal (how many discrete duty cycles values you can program), you will need to trade of error from the desired pwm frequency and resolution. In some applications resolution may be more important than an incorrect frequency, and in others timing may be more critical. It is up to the programmer to choose the optimal parameters for their application.


1. Generate PWM in the FPGA to Control Rotary Servomotors

Create a PWM controller that has programmable parameters which are set by the zynq processor. Hook up the output of the controller to the signal input wire of a servomotor; You can use one of the servo headers on the blackboard to power a servo and also connect to a servo’s signal input. Write a program that configures your PWM module with suitable parameters for pulses that can control servomotors.

To achieve the timing necessary you may need to change both the clock frequency and window size. Using a power of 2 divider, you will only be able to get your clock frequency so close to your desired PWM period. You will then have to adjust the window width to fine tune your frequency to match the 20ms window for servo pulses. You work out possible frequencies by plugging values into the timing equations for your system, as there are 2 possible parameters, you may want to constrain one before solving for the other. Remember that your window width and divider values have to be integers.

Using Servomotors

2. Generate Simple Waveforms using the PWM module

If you hook the output of a PWM module to the headphone output on the blackboard, you can generate audio signals. When the frequency of the PWM signal is in audible range and the duty cycle is not 100% or 0%, a speaker or headphone hooked up to the output jack will generate audible tones.

For audio the pitch is largely based off of the 1st harmonic, or fundamental frequency. You can set the fundamental frequency by setting the pulse frequency of your output. Other frequency content in the pulse makes up the timbre, you can hear a difference by changing the duty cycle of your waveform. Different periodic waveforms may have the same fundamental frequency (same period) but can sound very different due the frequency content of the waveform; Look up examples for how different sine, square, and triangle waves sound at the same frequency.

By changing the frequency of your output you can control the pitch. As the PWM module is in complete processor control, you can set both the frequency of the tone as well as how long its played. You can try sequencing different pitches for different lengths to create a song to impress your TA. Get creative!