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.
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
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)
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 |