本设计是一个范例,nios通过读写寄存器来完成Verilog实现的逻辑功能。本利以PWM为例子,以后可以将逻辑功能改复杂。
PWM设计分为3个部分,一是功能逻辑设计--Verilog语言实现;二是寄存器定义;三是Avalon接口设计


此例中,制作成SOPC IP时,可以选择为 动态地址对齐--Dynamic Bus sizing
若Verilog程序中没有byteenable,则可以设置为静态地址对齐--Native Address Alignment
一般,内存类(RAM,FLASH)常设为动态地址对齐,寄存器型的常设为静态地址对齐。IORD,IOWR主要用于静态机制对齐的读取,IORD_32DIRECT,IORD_16DIRECT,IORD_8DIRECT;IOWR_32DIRECT,IOWR_16DIRECT,IOWR_8DIRECT主要用于动态对齐的读取
io.h文件中看函数的宏定义:
#define IORD_32DIRECT(BASE, OFFSET) \
__builtin_ldwio (__IO_CALC_ADDRESS_DYNAMIC ((BASE), (OFFSET)))
#define IORD(BASE, REGNUM) \
__builtin_ldwio (__IO_CALC_ADDRESS_NATIVE ((BASE), (REGNUM)))
Verilog 程序
module PWM(clk,reset_n,chipselect,address,write,writedata,read,byteenable,readdata,PWM_out);
input clk;
input reset_n;
input chipselect;
input [1:0]address;
input write;
input [31:0] writedata;
input read;
input [3:0] byteenable;
output [31:0] readdata;
output PWM_out;
reg [31:0] clock_divide_reg;
reg [31:0] duty_cycle_reg;
reg control_reg;
reg clock_divide_reg_selected;
reg duty_cycle_reg_selected;
reg control_reg_selected;
reg [31:0] PWM_counter;
reg [31:0] readdata;
reg PWM_out;
wire pwm_enable;
//地址译码
always @ (address)
begin
clock_divide_reg_selected=0;
duty_cycle_reg_selected=0;
control_reg_selected=0;
case(address)
2'b00:clock_divide_reg_selected=1;
2'b01:duty_cycle_reg_selected=1;
2'b10:control_reg_selected=1;
default:
begin
clock_divide_reg_selected=0;
duty_cycle_reg_selected=0;
control_reg_selected=0;
end
endcase
end
//写PWM输出周期的时钟数寄存器
always @ (posedge clk or negedge reset_n)
begin
if(reset_n==1'b0) clock_divide_reg<=0;
else
begin
if(write & chipselect & clock_divide_reg_selected)
begin
if(byteenable[0]) clock_divide_reg[7:0]<=writedata[7:0];
if(byteenable[1]) clock_divide_reg[15:8]<=writedata[15:8];
if(byteenable[2]) clock_divide_reg[23:16]<=writedata[23:16];
if(byteenable[3]) clock_divide_reg[31:24]<=writedata[31:24];
end
end
end
//写PWM周期占空比寄存器
always @ (posedge clk or negedge reset_n)
begin
if(reset_n==1'b0) duty_cycle_reg<=0;
else
begin
if(write & chipselect & duty_cycle_reg_selected)
begin
if(byteenable[0]) duty_cycle_reg[7:0]<=writedata[7:0];
if(byteenable[1]) duty_cycle_reg[15:8]<=writedata[15:8];
if(byteenable[2]) duty_cycle_reg[23:16]<=writedata[23:16];
if(byteenable[3]) duty_cycle_reg[31:24]<=writedata[31:24];
end
end
end
//写控制寄存器
always @ (posedge clk or negedge reset_n)
begin
if(reset_n==1'b0) control_reg<=0;
else
begin
if(write & chipselect & control_reg_selected)
begin
if(byteenable[0]) control_reg<=writedata[0];
end
end
end
//读寄存器
always @ (address or read or clock_divide_reg or duty_cycle_reg or control_reg or chipselect)
begin
if(read & chipselect)
case(address)
2'b00:readdata<=clock_divide_reg;
2'b01:readdata<=duty_cycle_reg;
2'b10:readdata<=control_reg;
default:readdata<=32'hFFFFFFFF;
endcase
end
//控制寄存器
assign pwm_enable=control_reg;
//PWM功能部分
always @ (posedge clk or negedge reset_n)
begin
if(reset_n==1'b0) PWM_counter<=0;
else
begin
if(pwm_enable)
begin
if(PWM_counter>=clock_divide_reg-1) PWM_counter<=0;
else PWM_counter<=PWM_counter+1;
end
else
PWM_counter<=0;
end
end
always @ (posedge clk or negedge reset_n)
begin
if(reset_n==1'b0) PWM_out<=1'b0;
else
begin
if(pwm_enable)
begin
if(PWM_counter<=duty_cycle_reg-1) PWM_out<=1'b1;
else PWM_out<=1'b0;
end
else PWM_out<=1'b0;
end
end
endmodule
硬件测试程序
module PWM_tb;
reg clk,reset_n,chipselect;
reg[1:0] address;
reg write;
reg[31:0] writedata;
wire[31:0] readdata;
reg read;
reg[3:0] byteenable;
wire PWM_out;
initial
begin
clk=0;reset_n=0;chipselect=0;byteenable=1;
#100 reset_n=1;
#20 chipselect=1; //写clock_divide_reg寄存器为10
address=0;
writedata=10;
#20 write=1;
#20 chipselect=0;
#20 chipselect=1; //写duty_cycle_reg寄存器为5
address=1;
writedata=5;
#20 write=1;
#20 chipselect=0;
#20 chipselect=1; //写control_reg寄存器为1,使能PWM输出
address=2;
writedata=1;
#20 write=1;
#20 chipselect=0;
address=3; //是的寄存器使能全为0
#500
#20 chipselect=1; //写control_reg寄存器为0,禁止PWM输出,清零PWM寄存器
address=2;
writedata=0;
#20 write=0;
#20 chipselect=0;
#20 chipselect=1; //写duty_cycle_reg寄存器为3
address=1;
writedata=3;
#20 write=1;
#20 chipselect=0;
#20 chipselect=1; //写control_reg寄存器为1,使能PWM输出
address=2;
writedata=1;
#20 write=1;
#20 chipselect=0;
end
always #10 clk=!clk;
PWM exam1(
.clk(clk),
.reset_n(reset_n),
.chipselect(chipselect),
.address(address),
.write(write),
.writedata(writedata),
.read(read),
.byteenable(byteenable),
.readdata(readdata),
.PWM_out(PWM_out)
);
endmodule
nios 程序测试:
程序1:
#include <stdio.h>
#include <unistd.h>
#include "system.h"
#include <io.h>
int main()
{
int duty,divi;
//将pwm指向PWM_0_BASE首地址
char x;
IOWR(PWM_MY_BASE,0, 1000); //divi
IOWR(PWM_MY_BASE,1, 0); //duty
IOWR(PWM_MY_BASE,2, 1); //enable
// pwm->divi = 1000;
// pwm->duty = 0;
// pwm->enable = 1;
//通过不断的改变duty值来改变LED一个周期亮灯的时间长短
printf("请输入+或者-,来改变频率\n");
while(1)
{
divi=IORD(PWM_MY_BASE,0);
duty=IORD(PWM_MY_BASE,1);
x=getchar();
if(x=='+')
{
// printf("占空比增加\n");
if(duty < divi) duty += 200;
}
else if(x=='-')
{
// printf("占空比减小\n");
if(duty > 0) duty -= 200;
}
IOWR(PWM_MY_BASE,1, duty); //duty
usleep(100000);
}
return 0;
}
程序2:
#include <unistd.h>
#include "system.h"
//根据寄存器的偏移量,我们定义一个结构体PWM
typedef struct{
volatile unsigned int divi;
volatile unsigned int duty;
volatile unsigned int enable;
}PWM;
int main()
{
int dir = 1;
//将pwm指向PWM_0_BASE首地址
PWM *pwm = (PWM *)PWM_MY_BASE;
pwm->divi = 1000;
pwm->duty = 0;
pwm->enable = 1;
//通过不断的改变duty值来改变LED一个周期亮灯的时间长短
while(1)
{
if(dir > 0)
{
if(pwm->duty < pwm->divi) pwm->duty += 100;
else dir = 0;
}
else
{
if(pwm->duty > 0) pwm->duty -= 100;
else dir = 1;
}
usleep(100000);
}
return 0;
}