Verilog Examples - PWM or programmable clock duty cycle


We will now try yo generate a square with that has width of the positive or negative cycle - programmable. Also called PWM, it finds many application, the most recent one is in controlling the intensity of the backlight of an LCD screen. If the period on the ON time is much larger than the period of the off time, the LCD backlight brightness is high. If the period of ON time is much smaller than the period of the OFF time, the LCD brightness will be low. By programming the relative values of the ON and OFF time , we can generate the variable screen intensity.

Let us now formally state the problem



Problem - Generate a square wave with programmable ON (logic 1) and OFF (logic 0) intervals. An interval is defined as one clock period. The durations of the intervals are specified by two 4-bit control signals - call these rise and fall. Design a programmable square-wave generator circuit. It will also have a reset input. Whenever , any of the 4 rise or any of the 4 fall inputs change that output should change. The module obviously has a clock in.

Solution -

This is the main code clock.v



  1. // referencedesigner.com
  2. // Example of a Pulse Width Modulation
  3. // Or a square wave with programmable positive and negative width
  4.  
  5. module sqwaveGen(clk,reset,rise,fall,clk_out);
  6. input wire clk;
  7. input wire reset;
  8. output wire clk_out;
  9. input wire [3:0] rise;
  10. input wire [3:0] fall;
  11. reg [3:0] count, count_on, count_off;
  12. reg pos_or_neg;
  13.  
  14.  
  15. always @(posedge clk, negedge reset) begin
  16. if(~reset) begin
  17.  
  18. count<= 0;
  19. count <= 0;
  20. pos_or_neg <=1;
  21.  
  22. end
  23. else if ( (pos_or_neg ==1) )
  24. begin
  25. if ((count == count_on-1) )
  26. begin
  27. count <=0;
  28. pos_or_neg <=0;
  29. end
  30. else
  31. count <= count+1;
  32. end
  33. else if ( (pos_or_neg ==0) )
  34. begin
  35. if ((count == count_off-1) )
  36. begin
  37. count <=0;
  38. pos_or_neg <=1;
  39. end
  40. else
  41. count <= count+1;
  42. end
  43. end
  44.  
  45. always @(rise, fall)
  46. begin
  47. count_on <= rise;
  48. count_off <= fall;
  49. end
  50.  
  51. assign clk_out = pos_or_neg;
  52.  
  53. endmodule
  54.  
  55.  
  56.  




Here is the test bench clocktb.v

  1. `timescale 1ns/1ns
  2. module sqwavegentb;
  3. input clk_out;
  4. output reg clk,reset;
  5. output reg [3:0] rise,fall;
  6. sqwaveGen sqwave(clk,reset,rise,fall,clk_out);
  7. initial
  8. clk = 1'b0;
  9. always
  10. #20 clk = ~clk;
  11. initial
  12. begin
  13. $monitor($time,"clk = %b,reset = %b,clk_out = %b,rise = %b,fall = %b",clk,reset,clk_out,rise,fall);
  14. reset=0;
  15. #50 reset = 1;
  16. rise = 4'b0011;
  17. fall = 4'b0100;
  18. #2000 rise = 4'b0101;
  19. #10000 $finish;
  20. end
  21. initial
  22. begin
  23. $dumpfile ("sqwavegentb.vcd");
  24. $dumpvars (0,sqwavegentb);
  25. end
  26. endmodule
  27.  
  28.  
  29.  


Here is the result of the simulation when rise = 3 and fall = 4


the result of the simulation when rise = 5 and fall = 4

Explanation

1. Whenever the required input changes, it is stored in two registers count_on and count_off

input wire [3:0] rise; 
input wire [3:0] fall;
..
..
always @(rise, fall)
begin
count_on <= rise;
count_off <= fall;	
end


We now count for for count_on and keep the value of pos_or_neg at 1 while it is counting using a counter count. Once count reaches the value count_on, we toggle pos_or neg, we reset, count to 0 and then we start counting again till it reaches, count_off.

Rest of the code should be self explanatory.