avalon总线与接口.docx

上传人:b****5 文档编号:12051894 上传时间:2023-04-16 格式:DOCX 页数:23 大小:485.46KB
下载 相关 举报
avalon总线与接口.docx_第1页
第1页 / 共23页
avalon总线与接口.docx_第2页
第2页 / 共23页
avalon总线与接口.docx_第3页
第3页 / 共23页
avalon总线与接口.docx_第4页
第4页 / 共23页
avalon总线与接口.docx_第5页
第5页 / 共23页
点击查看更多>>
下载资源
资源描述

avalon总线与接口.docx

《avalon总线与接口.docx》由会员分享,可在线阅读,更多相关《avalon总线与接口.docx(23页珍藏版)》请在冰豆网上搜索。

avalon总线与接口.docx

avalon总线与接口

Avalon总线IP核的定制-----(深入了解软件编程的奥秘)

NIOSII是一个建立在FPGA上的嵌入式软核处理器,除了可以根据需要任意添加已经提供的外设外,用户还可以通过定制用户逻辑外设和定制用户指令来实现各种应用要求。

这节我们就来研究如何定制基于Avalon总线的用户外设。

SOPCBuilder提供了一个元件编辑器,通过这个元件编辑器我们就可以将我们自己写的逻辑封装成一个SOPCBuilder元件了。

下面,我们就以PWM实验为例,详细介绍一下定制基于Avalon总线的用户外设的过程。

我们要将的PWM是基于Avalon总线中的AvalonMemoryMappedInterface(Avalon-MM),而Avalon总线还有其他类型的设备,比如AvalonStreamingInterface(Avalon-ST)、AvalonMemoryMappedTristateInterface等等,在这里我就不详细叙述了,需要进一步了解的请参考ALTERA公司的《AvalonInterfaceSpecifications》(mnl_avalon_spec.pdf)。

Avalon-MM接口是内存映射系统下的用于主从设备之间的读写的接口,下图就是一个基于Avalon-MM的主从设备系统。

而我们这节需要做的就是下图高亮部分。

他的地位与UART,RAMController等模块并驾齐驱的。

Avalon-MM接口有很多特点,其中最大的特点就是根据自己的需求自由选择信号线,不过里面还是有一些要求的。

建议大家在看本文之前,先看一遍《AvalonInterfaceSpecifications》,这样就能对Avalon-MM接口有一个整体的了解。

下图为Avalon-MM外设的一个结构图,

理论的就说这些,下面我们举例来具体说明,让大家可以更好的理解。

构建HDL

我们这一节是PWM为例,所以首先,我们要构建一个符合Avalon-MMSlave接口规范的可以实现PWM功能的时序逻辑,在这里,我们利用Verilog语言来编写。

在程序中会涉及到Avalon信号,在这里,我们说明一下这些信号(其中,方向以从设备为基准)

HDL中的信号

Avalon信号类型

宽度

方向

描述

clk

clk

1

input

同步时钟信号

reset_n

reset_n

1

input

复位信号,低电平有效

chipselect

chipselect

1

input

片选信号

address

address

2

input

2位地址,译码后确定寄存器offset

write

write

1

input

写使能信号

writedata

writedata

32

input

32位写数据值

read

read

1

input

读时能信号

byteenable

byteenable

1

input

字节使能信号

readdata

readdata

32

output

32位读数据值

此外,程序中还包括一个PWM_out信号,这个信号是PWM输出,不属于Avalon接口信号。

PWM内部还包括使能控制寄存器、周期设定寄存器以及占空比设置寄存器。

设计中将各寄存器映射成AvalonSlave端口地址空间内一个单独的偏移地址。

没个寄存器都可以进行读写访问,软件可以读回寄存器中的当前值。

寄存器及偏移地址如下:

寄存器名

偏移量

访问属性

描述

clock_divide_reg

00

读/写

设定PWM输出周期的时钟数

duty_cycle_reg

01

读/写

设定一个周期内PWM输出低电平的始终个数

control_reg

10

读/写

使能和关闭PWM输出,为1时使能PWM输出

程序如下:

viewsource

print?

001

modulePWM(

002

clk,

003

reset_n,

004

chipselect,

005

address,

006

write,

007

writedata,

008

read,

009

byteenable,

010

readdata,

011

PWM_out);

012

013

inputclk;

014

inputreset_n;

015

inputchipselect;

016

input[1:

0]address;

017

inputwrite;

018

input[31:

0]writedata;

019

inputread;

020

input[3:

0]byteenable;

021

output[31:

0]readdata;

022

outputPWM_out;

023

024

reg[31:

0]clock_divide_reg;

025

reg[31:

0]duty_cycle_reg;

026

regcontrol_reg;

027

regclock_divide_reg_selected;

028

regduty_cycle_reg_selected;

029

regcontrol_reg_selected;

030

reg[31:

0]PWM_counter;

031

reg[31:

0]readdata;

032

regPWM_out;

033

wirepwm_enable;

034

035

//地址译码

036

always@(address)

037

begin

038

clock_divide_reg_selected<=0;

039

duty_cycle_reg_selected<=0;

040

control_reg_selected<=0;

041

case(address)

042

2'b00:

clock_divide_reg_selected<=1;

043

2'b01:

duty_cycle_reg_selected<=1;

044

2'b10:

control_reg_selected<=1;

045

default:

046

begin

047

clock_divide_reg_selected<=0;

048

duty_cycle_reg_selected<=0;

049

control_reg_selected<=0;

050

end

051

endcase

052

end

053

054

//写PWM输出周期的时钟数寄存器

055

always@(posedgeclkornegedgereset_n)

056

begin

057

if(reset_n==1'b0)

058

clock_divide_reg=0;

059

else

060

begin

061

if(write&chipselect&clock_divide_reg_selected)

062

begin

063

if(byteenable[0])

064

clock_divide_reg[7:

0]=writedata[7:

0];

065

if(byteenable[1])

066

clock_divide_reg[15:

8]=writedata[15:

8];

067

if(byteenable[2])

068

clock_divide_reg[23:

16]=writedata[23:

16];

069

if(byteenable[3])

070

clock_divide_reg[31:

24]=writedata[31:

24];

071

end

072

end

073

end

074

075

//写PWM周期占空比寄存器

076

always@(posedgeclkornegedgereset_n)

077

begin

078

if(reset_n==1'b0)

079

duty_cycle_reg=0;

080

else

081

begin

082

if(write&chipselect&duty_cycle_reg_selected)

083

begin

084

if(byteenable[0])

085

duty_cycle_reg[7:

0]=writedata[7:

0];

086

if(byteenable[1])

087

duty_cycle_reg[15:

8]=writedata[15:

8];

088

if(byteenable[2])

089

duty_cycle_reg[23:

16]=writedata[23:

16];

090

if(byteenable[3])

091

duty_cycle_reg[31:

24]=writedata[31:

24];

092

end

093

end

094

end

095

096

//写控制寄存器

097

always@(posedgeclkornegedgereset_n)

098

begin

099

if(reset_n==1'b0)

100

control_reg=0;

101

else

102

begin

103

if(write&chipselect&control_reg_selected)

104

begin

105

if(byteenable[0])

106

control_reg=writedata[0];

107

end

108

end

109

end

110

111

//读寄存器

112

always@(addressorreadorclock_divide_regorduty_cycle_regorcontrol_regorchipselect)

113

begin

114

if(read&chipselect)

115

case(address)

116

2'b00:

readdata<=clock_divide_reg;

117

2'b01:

readdata<=duty_cycle_reg;

118

2'b10:

readdata<=control_reg;

119

default:

readdata=32'h8888;

120

endcase

121

end

122

123

//控制寄存器

124

assignpwm_enable=control_reg;

125

126

//PWM功能部分

127

always@(posedgeclkornegedgereset_n)

128

begin

129

if(reset_n==1'b0)

130

PWM_counter=0;

131

else

132

begin

133

if(pwm_enable)

134

begin

135

if(PWM_counter>=clock_divide_reg)

136

PWM_counter<=0;

137

else

138

PWM_counter<=PWM_counter+1;

139

end

140

else

141

PWM_counter<=0;

142

end

143

end

144

145

always@(posedgeclkornegedgereset_n)

146

begin

147

if(reset_n==1'b0)

148

PWM_out<=1'b0;

149

else

150

begin

151

if(pwm_enable)

152

begin

153

if(PWM_counter<=duty_cycle_reg)

154

PWM_out<=1'b1;

155

else

156

PWM_out<=1'b0;

157

end

158

else

159

PWM_out<=1'b0;

160

end

161

end

162

163

endmodule

上面的程序保存好以后,命名为PWM.v,并将其存放到工程目录下。

硬件设置

接下来,我们就通过SOPCBuilder,来建立PWM模块了。

首先,打开Quartus软件,进入SOPCBuilder。

进入后,点击下图红圈处

点击后,如下图所示,点击Next,

点击后,如下图所示,点击下图红圈处,将我们刚才建立的PWM.v加进来。

(我将PWM。

v放到了工程目录下的pwm文件夹下)

加入后,系统会对PWM.v文件进行分析,如下图所示,出现红圈处的文字,说明分析成功,点击close,关闭对话框。

然后点击Next,如下图所示,通过下图,我们可以看到,PWM.v中的信号都出现在这里面了。

我们可以根据我们的功能要求来配置这些信号,其中,Interface是Avalon接口类型,它包括Avalon-MM、Avalon-ST、AvalonMemoryMappedTristateInterface等等。

SignalType指的是各个Avalon接口类型下的信号类型。

PWM.v中的信号我们已经在前面都介绍过了,大家按照上面的要求设置就可以了。

默认情况只有PWM_out需要改动,如下图示红圈处设置,

其中,Interface在下拉菜单中选择下图红圈处所示的选项。

上面的选项都设置好以后,点击Next,如下图所示,我们通过下图红圈处的下拉条向下拉

拉到下图所示位置停止,我们将红圈处的改选为NATIVE,这个地方就是地址对齐的选项,我们选择为静态地址对齐。

其他的地方都默认,不需要改动。

这里面还有很多选项,其中Timing部分需要说明一下,PWM的AvalonSlave端口与AvalonSlave端口时钟信号同步,读/写时的建立很保持时间为0,因为读、写寄存器仅需要一个时钟周期,所以读/写时为0等待切不需要读延时。

接着点击Next,如下图所示,其中红圈处需要注意,这个地方需要可以建立新组,然后在SOPCBuilder中体现出来。

点击Finish后,会出现下面的对话框,点击Yes,就会生成一个PWM_hw.tcl脚本文件,大家可以打开看一下,里面放置的是刚才我们配置PWM时候的配置信息。

上面都完成以后,我们回到了SOPCBuilder界面,我们在左侧边栏中可以找到下图所示的红圈处

大家看到了吧,MyIP就是我们刚才建立的group。

双击PWM,我们建立PWM模块,如下图

点击Finish,完成建立。

这里还需要设置一步,点击下图红圈处

点击后,如下图所示,点击IPSerarchPath,然后点击Add,添加PWM.v所在位置的路径

添加后,如下图所示

点击Finish完成。

设置这个选项是为了让SOPCBuilder可以找到PWM.v的位置。

不然就会出现下次你进入SOPCBuilder的时候PWM模块无效的问题。

接下来的工作就是自动分配地址,分配中断,编译,等待......

编译好以后,我们回到Quartus软件界面,我们可以看到,PWM出现了,我将它接到了一个LED上了,我们可以通过PWM改变LED的亮度,实现LED渐亮渐灭的过程。

接下来又是编译,等待.....

做好硬件部分工作以后,我们打开NIOSIDE,开始软件编程部分。

软件开发

首先对工程重新编译一次,Ctril+B,等待......

编译好以后,我们来看一下system.h的变化情况,我们可以发现,多出来PWM部分了。

下面是PWM测试代码,

viewsource

print?

01

#include

02

#include"system.h"

03

04

//根据寄存器的偏移量,我们定义一个结构体PWM

05

typedefstruct{

06

volatileunsignedintdivi;

07

volatileunsignedintduty;

08

volatileunsignedintenable;

09

}PWM;

10

11

intmain()

12

{

13

intdir=1;

14

15

//将pwm指向PWM_0_BASE首地址

16

PWM*pwm=(PWM*)PWM_0_BASE;

17

//对pwm进行初始化,divi最大值为232-1。

18

pwm->divi=1000;

19

pwm->duty=0;

20

pwm->enable=1;

21

22

//通过不断的改变duty值来改变LED一个周期亮灯的时间长短

23

while

(1){

24

if(dir>0){

25

if(pwm->dutydivi)

26

pwm->duty+=100;

27

else

28

dir=0;

29

}

30

else{

31

if(pwm->duty>0)

32

pwm->duty-=100;

33

else

34

dir=1;

35

}

36

37

usleep(100000);

38

}

39

40

return0;

41

}

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 初中教育 > 英语

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1