简介

NIOS II是一个建立在FPGA上的嵌入式软核处理器,除了可以根据需要任意添加已经提供的外设外,用户还可以通过定制用户逻辑外设和定制用户指令来实现各种应用要求。这节我们就来研究如何定制基于Avalon总线的用户外设。

SOPC Builder提供了一个元件编辑器,通过这个元件编辑器我们就可以将我们自己写的逻辑封装成一个SOPC Builder元件了。下面,我们就以PWM实验为例,详细介绍一下定制基于Avalon总线的用户外设的过程。

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

Avalon-MM接口是内存映射系统下的用于主从设备之间的读写的接口,下图就是一个基于Avalon-MM的主从设备系统。而我们这节需要做的就是下图高亮部分。他的地位与UART,RAM Controller等模块并驾齐驱的。

Avalon-MM接口有很多特点,其中最大的特点就是根据自己的需求自由选择信号线,不过里面还是有一些要求的。建议大家在看本文之前,先看一遍《Avalon Interface Specifications》,这样就能对Avalon-MM接口有一个整体的了解。

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

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

构建HDL

我们这一节是PWM为例,所以首先,我们要构建一个符合Avalon-MM Slave接口规范的可以实现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内部还包括使能控制寄存器、周期设定寄存器以及占空比设置寄存器。设计中将各寄存器映射成Avalon Slave端口地址空间内一个单独的偏移地址。没个寄存器都可以进行读写访问,软件可以读回寄存器中的当前值。寄存器及偏移地址如下:

寄存器名 

偏移量 

访问属性 

描述 

clock_divide_reg

00

读/写

设定PWM输出周期的时钟数

duty_cycle_reg

01

读/写

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

control_reg

10

读/写

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

程序如下:

     module PWM(
clk,
reset_n,
chipselect,
address,
write,
writedata,
read,
byteenable,
readdata,
PWM_out); input clk;
input reset_n;
input chipselect;
input [:]address;
input write;
input [:] writedata;
input read;
input [:] byteenable;
output [:] readdata;
output PWM_out; reg [:] clock_divide_reg;
reg [:] duty_cycle_reg;
reg control_reg;
reg clock_divide_reg_selected;
reg duty_cycle_reg_selected;
reg control_reg_selected;
reg [:] PWM_counter;
reg [:] readdata;
reg PWM_out;
wire pwm_enable; //地址译码
always @ (address)
begin
clock_divide_reg_selected<=;
duty_cycle_reg_selected<=;
control_reg_selected<=;
case(address)
'b00:clock_divide_reg_selected<=1;
'b01:duty_cycle_reg_selected<=1;
'b10:control_reg_selected<=1;
default:
begin
clock_divide_reg_selected<=;
duty_cycle_reg_selected<=;
control_reg_selected<=;
end
endcase
end //写PWM输出周期的时钟数寄存器
always @ (posedge clk or negedge reset_n)
begin
if(reset_n=='b0)
clock_divide_reg=;
else
begin
if(write & chipselect & clock_divide_reg_selected)
begin
if(byteenable[])
clock_divide_reg[:]=writedata[:];
if(byteenable[])
clock_divide_reg[:]=writedata[:];
if(byteenable[])
clock_divide_reg[:]=writedata[:];
if(byteenable[])
clock_divide_reg[:]=writedata[:];
end
end
end //写PWM周期占空比寄存器
always @ (posedge clk or negedge reset_n)
begin
if(reset_n=='b0)
duty_cycle_reg=;
else
begin
if(write & chipselect & duty_cycle_reg_selected)
begin
if(byteenable[])
duty_cycle_reg[:]=writedata[:];
if(byteenable[])
duty_cycle_reg[:]=writedata[:];
if(byteenable[])
duty_cycle_reg[:]=writedata[:];
if(byteenable[])
duty_cycle_reg[:]=writedata[:];
end
end
end //写控制寄存器
always @ (posedge clk or negedge reset_n)
begin
if(reset_n=='b0)
control_reg=;
else
begin
if(write & chipselect & control_reg_selected)
begin
if(byteenable[])
control_reg=writedata[];
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)
'b00:readdata<=clock_divide_reg;
'b01:readdata<=duty_cycle_reg;
'b10:readdata<=control_reg;
default:readdata='h8888;
endcase
end //控制寄存器
assign pwm_enable=control_reg; //PWM功能部分
always @ (posedge clk or negedge reset_n)
begin
if(reset_n=='b0)
PWM_counter=;
else
begin
if(pwm_enable)
begin
if(PWM_counter>=clock_divide_reg)
PWM_counter<=;
else
PWM_counter<=PWM_counter+;
end
else
PWM_counter<=;
end
end always @ (posedge clk or negedge reset_n)
begin
if(reset_n=='b0)
PWM_out<='b0;
else
begin
if(pwm_enable)
begin
if(PWM_counter<=duty_cycle_reg)
PWM_out<='b1;
else
PWM_out<='b0;
end
else
PWM_out<='b0;
end
end endmodule

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

硬件设置

接下来,我们就通过SOPC Builder,来建立PWM模块了。首先,打开Quartus软件,进入SOPC Builder。进入后,点击下图红圈处

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

点击后,如下图所示,点击下图红圈处,将我们刚才建立的PWM.v加进来。(我将PWM。v放到了工程目录下的pwm文件夹下)

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

然后点击Next,如下图所示,通过下图,我们可以看到,PWM.v中的信号都出现在这里面了。我们可以根据我们的功能要求来配置这些信号,其中,Interface是Avalon接口类型 ,它包括Avalon-MM、Avalon-ST、Avalon Memory Mapped Tristate Interface等等。Signal Type指的是各个Avalon接口类型下的信号类型。PWM.v中的信号我们已经在前面都介绍过了,大家按照上面的要求设置就可以了。默认情况只有PWM_out需要改动,如下图示红圈处设置,

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

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

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

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

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

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

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

大家看到了吧,MyIP就是我们刚才建立的group。双击PWM,我们建立PWM模块,如下图

点击Finish,完成建立。

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

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

添加后,如下图所示

点击Finish完成。设置这个选项是为了让SOPC Builder可以找到PWM.v的位置。不然就会出现下次你进入SOPC Builder的时候PWM模块无效的问题。

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

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

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

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

软件开发

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

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

下面是PWM测试代码,

     #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 = ; //将pwm指向PWM_0_BASE首地址
PWM *pwm = (PWM *)PWM_0_BASE;
//对pwm进行初始化,divi最大值为232-1。
pwm->divi = ;
pwm->duty = ;
pwm->enable = ; //通过不断的改变duty值来改变LED一个周期亮灯的时间长短
while(){
if(dir > ){
if(pwm->duty < pwm->divi)
pwm->duty += ;
else
dir = ;
}
else{
if(pwm->duty > )
pwm->duty -= ;
else
dir = ;
} usleep();
} return ;
}

每天进步一点点------SOPC的Avalon-MM IP核(二) AVALON总线的IP核定制的更多相关文章

  1. 每天进步一点点------SOPC的Avalon-MM IP核(一) avalon总线的信号时序

    在SOPC中自定义外设时.可以设置avalon总线的信号时序,以满足外设的要求.一般情况下,可以设为: 其中setup为read和write信号之前,address和writedata信号提前建立的时 ...

  2. 每天进步一点点------SOPC PIO (一)

    最近想使用Nios II里的并口PIO口进行双向操作,即需要输出的时候设置为输出方向,需要输入的时候设置为输入方向.在这期间,因为没认真仔细阅读参考文档,走了一点点的弯路.下面就简单的介绍下并行输入/ ...

  3. Avalon MM 总线

    对于Avalon Master来讲,Address信号代表一个字节(8-bit)的地址.Address的值必须与字节的宽度对齐,如果要对某个word的byte写,需要首先使用byteenable信号: ...

  4. 每天进步一点点------SOPC的uC/OS-II应用(一)

    uC/OS-II(又名Micro C/OS)是基于嵌入式系统的完整的,可移植.可固化.可裁剪的可剥夺型实时内核,其已经广泛应用在航空飞行器.医疗设备.工业控制等可靠性和稳定性要求较高的场合.该内核的代 ...

  5. 每天进步一点点------SOPC的Avalon-MM IP核(三) LCD1602 IP定制

    注:Avalon信号类型命名参考图 /********************************************************************************* ...

  6. 每天进步一点点------SOPC的Avalon-MM IP核(四) KEY_LED IP定制

    /********************************************************************************* * Company : * Eng ...

  7. 每天进步一点点------SOPC TIMER (一)

    寄存器图 可以通过操作以下的寄存器来实现对timer(定时器)内核的操作(仅描述32位计数器) 状态寄存器: TO(timeout) :计数器计数到0时,该位置1,之后TO位的值会保持,直到手动清零, ...

  8. 每天进步一点点——Linux中的线程局部存储(二)

    转载请说明出处:http://blog.csdn.net/cywosp/article/details/26876231     在Linux中另一种更为高效的线程局部存储方法,就是使用keyword ...

  9. 每天进步一点点------创建Microblaze软核(二)

    第四步 进入Platform Studio操作界面通过向导创建软核后,进入到PlatformStudio——内核开发环境.Platform Studio主界面如下图. 在Ports项中,右键点击RS2 ...

随机推荐

  1. DataGrid 的DataSource重新加载数据

    DataGrid 的DataSource重新加载数据,若直接重新给DataSource赋值是没有效果的,若只是修改原有数据中的单个值,此方法有效,但是针对完全不一样的数据直接重新赋值的方式是无效的,此 ...

  2. 不同页面获取同一cookie变量值不同的问题及解决方法

    在使用cookie时发现不同页面获取到的同一个cookie变量的值不同,本篇博客介绍其中一种情况的解决方法,通过设置path的方法可使得在同一个网站下获取的cookie变量一致. 问题描述 在www. ...

  3. 字符串匹配算法--暴力匹配(Brute-Force-Match)C语言实现

    一.前言 暴力匹配(Brute-Force-Match)是字符串匹配算法里最基础的算法,虽然效率比较低,但胜在方便理解,在小规模数据或对时间无严格要求的情况下可以考虑. 二.代码 #include & ...

  4. OpenGL 编程指南 (5.1)

    1.OpenGL支持同时使用多个纹理单元,使用GL_TEXTUREi进行标识,使用前需要先激活对应的纹理单元,默认GL_TEXTURE0是激活绑定的. void glActiveTexture(GLe ...

  5. Weather

    Scientists say a lot about the problems of global warming and cooling of the Earth. Indeed, such nat ...

  6. lua学习,笔者自用

    标识符与关键字A:常量用全大写和下划线,eg: My_ACCOUNTB: 变量的第一个字母小写,eg: strNumberC: 全局变量第一个字母用小写g表示,eg: gMyAcountD: 函数名第 ...

  7. 2019牛客多校第五场B generator 十进制快速幂

    generator 1 题意 给出\(x_0,x_1,a,b\)已知递推式\(x_i=a*x_{i-1}+b*x_{i-2}\),出个n和mod,求\(x_n\) (n特别大) 分析 比赛的时候失了智 ...

  8. 【Python】文本进度条

    1.0代码: import time#引入time库 scale=10#文本进度条宽度 print("------执行开始------") for i in range(scale ...

  9. 【原】librtmp源码详解

    “悟已往之不谏,知来者之可追”.后悔做了这么久的直播,却不曾理解rtmp协议的实现原理,现在意识到了这个问题,特此补救.同时谨以此文纪念曾经的雷霄骅同学,感谢他对音视频领域做出的卓越贡献和引领. 1. ...

  10. JASON

    JSON 数据格式 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.JSON采用完全独立于语言的文本格式,这些特性使JSON成为理想的数据交换语言.易于人 ...