본문 바로가기

Verilog Projects

Avalon Bus : PWM

Development Environment.

Program : Quartus Prime 18.1 Lite Edition, Eclipse Mars.2 Release(4.5.2)

Tool : Modelsim 10.5b Starter Edition

FPGA : Cyclone V

Hardware Devices : De1-SoC Board.

Language : Verilog2001.


Objective.

Control PWM and adjust LED brightness through Avalon Interface.


Why?

PWM은 Pulse Width Modulation의 약어로 period와 duty ratio를 조절하여 HIGH, LOW 상태에 따른 Pulse의 폭을 조절한다. 이를 활용하여 LED, Power 또는 Motor의 속도 등을 제어할 수 있으며 이산 신호를 다루는 디지털 회로에서 아날로그 신호를 모방할 수 있게 된다.


Design step.

1. Block Diagram.

2. Verilog code with Avalon interface.

3. Modelsim Simulation.

4. verification with De1-SoC Board.


1. Block Diagram.

Pulse Width Modulation

 

Byteenable에 맞게 div, duty reg 선언, 출력을 각 비트별로 받을 수 있도록 off 선언, div와 duty의 입력 데이터 활성화 신호 divide, duty enable 선언, enable은 write_n과 address로 출력. div와 duty 데이터만큼 동작하는 counter 설계.


1. Verilog Code with Avalon Interface.

 

module pwm (
   input               clk         ,
   input               clr_n       ,
   input      [31:0]   address     ,
   input      [ 3:0]   byteenable  ,
   input               read        ,
   input               write_n     ,
   input      [31:0]   writedata   ,
   output              waitrequest ,
   output     [31:0]   readdata    ,
   output     [ 7:0]   pwm_out      
);

   reg    [ 7:0]    div3, div2, div1, div0;
   reg    [ 7:0]   duty3,duty2,duty1,duty0;
                     
   reg    [31:0]   counter;
   reg             off;
   reg    [31:0]   rd_data;
   
   wire             div3_ena, div2_ena, div1_ena, div0_ena;
   wire            duty3_ena,duty2_ena,duty1_ena,duty0_ena;
   
   always@(posedge clk, negedge clr_n)begin
      if(!clr_n)begin
         div3  <= 0;
         div2  <= 0;
         div1  <= 0;
         div0  <= 0;
         duty3 <= 0;
         duty2 <= 0;
         duty1 <= 0;
         duty0 <= 0;
      end
      else begin
         if(div3_ena)
            div3 <= writedata[31:24];
         else 
            div3 <= div3;
         if(div2_ena)
            div2 <= writedata[23:16];
         else
            div2 <= div2;
         if(div1_ena)
            div1 <= writedata[15: 8];
         else 
            div1 <= div1;
         if(div0_ena)
            div0 <= writedata[ 7: 0];
         else
            div0 <= div0;
      
         if(duty3_ena)
            duty3 <= writedata[31:24];
         else 
            duty3 <= duty3;
         if(duty2_ena)
            duty2 <= writedata[23:16];
         else
            duty2 <= duty2;
         if(duty1_ena)
            duty1 <= writedata[15: 8];
         else 
            duty1 <= duty1;
         if(duty0_ena)
            duty0 <= writedata[ 7: 0];
         else
            duty0 <= duty0;
      end
   end
   
   always@(posedge clk, negedge clr_n)begin
      if(!clr_n)
         counter <= 0;
      else if(counter >= {div3, div2, div1, div0})
         counter <= 0;
      else 
         counter <= counter + 1;
   end

   always@(posedge clk, negedge clr_n)begin
      if(!clr_n)
         off <= 0;
      else if(counter >= {duty3, duty2, duty1, duty0})
         off <= 1;
      else if(counter == 0)
         off <= 0;
      else
         off <= off;
   end
   
   always@(address or div3 or div2 or div1 or div0 or duty3 or duty2 or duty1 or duty0) begin
      if(!address)
         rd_data <= { div3,  div2,  div1,  div0};
      else
         rd_data <= {duty3, duty2, duty1, duty0};
   end

   assign readdata = rd_data;
   
   assign div3_ena  = !write_n & !address;
   assign div2_ena  = !write_n & !address;
   assign div1_ena  = !write_n & !address;
   assign div0_ena  = !write_n & !address;
   assign duty3_ena = !write_n &  address;
   assign duty2_ena = !write_n &  address;
   assign duty1_ena = !write_n &  address;
   assign duty0_ena = !write_n &  address;
   assign pwm_out[0] = !off;
   assign pwm_out[1] = !off;
   assign pwm_out[2] = !off;
   assign pwm_out[3] = !off;
   assign pwm_out[4] = !off;
   assign pwm_out[5] = !off;
   assign pwm_out[6] = !off;
   assign pwm_out[7] = !off;

endmodule

2. Modelsim Simulation.

 

`timescale 1 ns / 1 ns 

module tb_Avalon_Model_pwm();
   reg            clk         ;        
   reg            reset       ;        
   
   wire  [31:0]   address     ;     
   wire  [ 3:0]   byteenable  ;  
   wire           read        ;
   wire           write       ;        
   wire           waitrequest ;  
   wire  [31:0]   readdata    ;     
   wire  [31:0]   writedata   ;  
   wire  [ 7:0]   pwm_out     ;

   Avalon_Model uAvalon_Model_0(   //   Avalon_Write(32'h0000, 32'hf);
      .clk        (clk        ),   //   Avalon_Write(32'h0001, 32'h8);
      .reset      (reset      ),   //   Avalon_Read(32'h0002);
      .address    (address    ),   //   Avalon_Read(32'h0003);
      .byteenable (byteenable ),
      .read       (read       ),
      .write      (write      ),
      .waitrequest(waitrequest),
      .readdata   (readdata   ),
      .writedata  (writedata  )
   );
   
   pwm uPwm_0 (
      .clk        (clk        ), 
      .clr_n      (!reset     ),
      .address    (address    ),
      .byteenable (byteenable ),
      .read       (read       ),
      .write_n    (!write     ),
      .writedata  (writedata  ),
      .waitrequest(waitrequest),
      .readdata   (readdata   ),
      .pwm_out    (pwm_out    )
   ); 
      
   initial fork
      clk_gen_task();
      reset_task();
   join
   
   task clk_gen_task;
      begin
         clk = 1'b0;
         forever #10 clk = ~clk;
      end
   endtask
   
   task reset_task;
      begin
         reset = 1'b0;
         repeat(1) @(posedge clk);
         reset = 1'b1;
         repeat(1) @(posedge clk);
         reset = 1'b0;
      end
   endtask
   
endmodule

 

vlib work
vlog Avalon_Model.v tb_Avalon_Model_pwm.v pwm.v
vsim work.tb_Avalon_Model_pwm
view wave

add wave -radix unsigned /clk          
add wave -radix unsigned /reset           
add wave -radix unsigned /address      
add wave -radix unsigned /byteenable   
add wave -radix unsigned /read         
add wave -radix unsigned /write     
add wave -radix unsigned /waitrequest
add wave -radix hex      /readdata  
add wave -radix hex      /writedata 
add wave -radix unsigned /pwm_out
add wave -radix unsigned /tb_Avalon_Model_pwm/uPwm_0/counter
add wave -radix unsigned /tb_Avalon_Model_pwm/uPwm_0/clr_n
add wave -radix unsigned /tb_Avalon_Model_pwm/uPwm_0/writedata
add wave -radix unsigned /tb_Avalon_Model_pwm/uPwm_0/off
add wave -radix unsigned /tb_Avalon_Model_pwm/uPwm_0/write_n
add wave -radix unsigned /tb_Avalon_Model_pwm/uPwm_0/address
add wave -radix unsigned /tb_Avalon_Model_pwm/uPwm_0/div3
add wave -radix unsigned /tb_Avalon_Model_pwm/uPwm_0/div2
add wave -radix unsigned /tb_Avalon_Model_pwm/uPwm_0/div1
add wave -radix unsigned /tb_Avalon_Model_pwm/uPwm_0/div0
add wave -radix unsigned /tb_Avalon_Model_pwm/uPwm_0/duty3
add wave -radix unsigned /tb_Avalon_Model_pwm/uPwm_0/duty2
add wave -radix unsigned /tb_Avalon_Model_pwm/uPwm_0/duty1
add wave -radix unsigned /tb_Avalon_Model_pwm/uPwm_0/duty0
add wave -radix unsigned /tb_Avalon_Model_pwm/uPwm_0/pwm_out

run 1000 ns

 

0 - 500ns
500 - 1000ns

 


3. Verification with De1-SoC Board & Eclipse.

1) Add Custom Component.

Top-level Module, Signal Type, Interface 설정에 주의

 

2) System Configuration.

Clk, CPU(Nios II Processor), Memory, JTAG(Debugging), My_pwm_0(Custom Component), Key(PIO)

Platform Designer

Generate HDL > create sopcinfo, qip File. 

 

3) Compilation.

1. 프로젝트에 qip File 추가하고 Platform Designer의 Instantation Template Copy 후 Analysis & Synthesis.

 

module Mark_2 (
   input          clk      ,
   input          reset_n  ,  
   input  [3:0]   key      ,
   output [7:0]   pwm_out   
);
   niosII_system niosII_system_u0 (
      .clk_clk                        (clk    ),      
      .key_external_connection_export (key    ), 
      .reset_reset_n                  (reset_n),               
      .my_pwm_0_conduit_end_export    (pwm_out)
   );
endmodule

 

2. Pin Planner Setting

 

 

3. Full Compilation.

Compilation > Assembler > create .sof

 

 

4) Programmer.

 

 

5) Verification with Eclipse.

 

#include <stdio.h>
#include "altera_avalon_pio_regs.h"
#include "altera_avalon_pwm_regs.h"
#include "system.h"

int main()
{ 
   unsigned char v;
   
   IOWR_ALTERA_AVALON_PWM_DIVIDER(MY_PWM_0_BASE,0xFF);
   IOWR_ALTERA_AVALON_PWM_DUTY(MY_PWM_0_BASE,0xFF);

   printf("LET'S GI RIT!");
   v = 0;
   while (1){
      IOWR_ALTERA_AVALON_PWM_DUTY(MY_PWM_0_BASE,v);
         v++;
         usleep(2000);
   }
   return 0;
}

 

 

 

#include <stdio.h>
#include "altera_avalon_pio_regs.h"
#include "altera_avalon_pwm_regs.h"
#include "system.h"

#define NONE_PRESSED 0xF
#define DEBOUNCE 30000

int main()
{ 
   int buttons, button_val;
   int print_cnt;

   IOWR_ALTERA_AVALON_PWM_DIVIDER(MY_PWM_0_BASE,0xFF);
   IOWR_ALTERA_AVALON_PWM_DUTY(MY_PWM_0_BASE,0xFF);

   printf("\n\tNios II PWM Lab\n\n");
   printf("\tPlease press button 1 to 4 on kit to adjust LED intensity:\n");

   button_val = 0;
   while (1)
   {
      print_cnt = 0;
      buttons = IORD_ALTERA_AVALON_PIO_DATA(KEY_BASE);

      if(buttons != NONE_PRESSED)  {
         usleep (DEBOUNCE);
         while (buttons != NONE_PRESSED)  {
            buttons = IORD_ALTERA_AVALON_PIO_DATA(KEY_BASE);
         }
      
      usleep (DEBOUNCE);
      button_val++;

         if(button_val > 3) {
            button_val = 0;
         }
         print_cnt = 1;
      }

   switch (button_val)  {
      case 0:
         IOWR_ALTERA_AVALON_PWM_DUTY(MY_PWM_0_BASE,0xcc);
         if (print_cnt == 1)
            printf("\tLevel 4 intensity\n");
         break;
      case 3:
         IOWR_ALTERA_AVALON_PWM_DUTY(MY_PWM_0_BASE,0x99);
         if (print_cnt == 1)
            printf("\tLevel 3 intensity\n");
         break;
      case 2:
         IOWR_ALTERA_AVALON_PWM_DUTY(MY_PWM_0_BASE,0x66);
         if (print_cnt == 1)
            printf("\tLevel 2 intensity\n");
         break;
      case 1:
         IOWR_ALTERA_AVALON_PWM_DUTY(MY_PWM_0_BASE,0x33);
         if (print_cnt == 1)
            printf("\tLevel 1 intensity\n");
         break;
      default:
         IOWR_ALTERA_AVALON_PWM_DUTY(MY_PWM_0_BASE,0xFF);
         break;
      }
   }
   return 0;
}

 

 

 

 

'Verilog Projects' 카테고리의 다른 글

Morse Code Generator.  (0) 2023.12.17
Avalon Bus : GPIO  (0) 2023.11.12
Avalon Bus Modeling.  (1) 2023.11.12
UART.  (0) 2023.11.08
8-Bit Multiplier.  (0) 2023.10.28