简介

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输出

程序如下:

001 module PWM(
002     clk,
003     reset_n,
004     chipselect,
005     address,
006     write,
007     writedata,
008     read,
009     byteenable,
010     readdata,
011     PWM_out);
012   
013 input clk;
014 input reset_n;
015 input chipselect;
016 input [1:0]address;
017 input write;
018 input [31:0] writedata;
019 input read;
020 input [3:0] byteenable;
021 output [31:0] readdata;
022 output PWM_out;
023   
024 reg [31:0] clock_divide_reg; 
025 reg [31:0] duty_cycle_reg; 
026 reg control_reg;
027 reg clock_divide_reg_selected;
028 reg duty_cycle_reg_selected;
029 reg control_reg_selected;
030 reg [31:0] PWM_counter;
031 reg [31:0] readdata;
032 reg PWM_out;
033 wire pwm_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 @ (posedge clk or negedge reset_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 @ (posedge clk or negedge reset_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 @ (posedge clk or negedge reset_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 @ (address or read or clock_divide_reg or duty_cycle_reg or control_reg or chipselect)
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 assign pwm_enable=control_reg;
125   
126 //PWM功能部分
127 always @ (posedge clk or negedge reset_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 @ (posedge clk or negedge reset_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,并将其存放到工程目录下。

硬件设置

接下来,我们就通过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测试代码,

01 #include <unistd.h>
02 #include "system.h"
03   
04 //根据寄存器的偏移量,我们定义一个结构体PWM
05 typedef struct{
06     volatile unsigned int divi;
07     volatile unsigned int duty;
08     volatile unsigned int enable;
09 }PWM;
10   
11 int main()
12 {
13 int dir = 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->duty < pwm->divi)
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     return 0;   
41 }

基于AVALON总线的IP核定制 PWM相关推荐

  1. niosII的那些事--基于AVALON总线的IP核定制

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

  2. 【FPGA黑金开发板】NIOSII那些事儿--基于AVALON总线的IP定制(十七)

    声明:本文为转载作品,版权归本博文作者所有,如需转载,请注明出处http://www.cnblogs.com/kingst/ 简介 NIOS II是一个建立在FPGA上的嵌入式软核处理器,除了可以根据 ...

  3. FPGA逻辑设计回顾(13)RAM以及ROM的IP核定制以及关键参数

    文章目录 前言 RAM IP的定制 Xilinx的IP定制位置 Block RAM的定制过程 第一页 第二页 第三页 第四页 第五页 Block RAM的延迟讨论 ROM IP核的定制 总结 前言 本 ...

  4. 开发自定义AXI总线外设IP核——以LED和开关为例

    http://www.eefocus.com/nightseas/blog/12-10/287343_15762.html ZedBoard学习手记(二) 开发自定义AXI总线外设IP核--以LED和 ...

  5. 如何基于Avalon总线完成QSYS IP定制

    文章目录 0. 为什么要定制QSYS IP 1. 规划IP的硬件功能 2. 定义恰当的Avalon接口 3. RTL设计 4. 使用IP编辑器封装IP 5. 编写用于描述寄存器的C头文件和IP驱动文件 ...

  6. 基于vivado的fir ip核的重采样设计与实现

    创建vivado工程 1. 首先打开vivado,创建一个新的project(勾选create project subdirectory选项),并将工程命填为firfilter. 2.选择工程创建的类 ...

  7. 基于ISE的QDR IP核调用与硬件自检

    平台:ISE(IP核用法同VIVADO) 语言:VHDL(Verilog用法类似) FPGA型号:V6-315T,ffg1156-1 QDR型号:GS8342D08GE-300I(类似) XILINX ...

  8. FPGA设计心得(9)基于DDS IP核的任意波形发生器设计

    博文目录 写在前面 正文 设计要求 IP核配置 定制输出数据位宽 定制相位位宽(或频率分辨率) 输出频率 输出正余弦选择以及数据格式 其他设置 电路设计 行为仿真 参考资料 交个朋友 写在前面 数据手 ...

  9. 通过Clocking Wizard定制和生成一个IP核(MMCM)(Virtex7)(ISE版)

    目录 定制过程 准备进入定制页面 第一页 Clocking features 第二页 第三页 Selecting Optional Ports 第四页 第五页 第六页 定制过程 准备进入定制页面 首先 ...

最新文章

  1. Xgboost调参小结
  2. python学习之 字符串前'r'的用法
  3. mysql主主同步配置_MySQL 主主同步配置步骤
  4. Codeforces Round #650 (Div. 3)(A-C)
  5. 滴滴升级“极速拼车”:未拼成可享折扣 拼成更便宜
  6. 每天一道剑指offer-对称的二叉树
  7. 【离散数学】集合论 第四章 函数与集合(6) 三歧性定理、两集合基数判等定理(基数的比较)、Cantor定理
  8. java课程设计小组分工_java课程设计---团体
  9. 用 Java 实现的八种常用排序算法
  10. 机器学习笔记之概率图模型(八)信念传播(Belief Propagation,BP)(基于树结构)
  11. 使用Snakemake搭建分析流程
  12. 2022中国开发者影响力年度榜单揭晓,华为、阿里、腾讯等入选年度开源贡献企业 | 美通社头条...
  13. 使用python实现用微信远程控制电脑
  14. Django入门(一)
  15. 13款用于拍摄全景照片的iOS应用
  16. TA-Lib介绍安装及使用教程
  17. GEE哨兵二号去云不成功的原因(代码修改)
  18. 剑指Offer(十九):顺时针打印矩阵
  19. 造梦无双服务器维护12月17日,造梦无双Online官网版
  20. SVN使用教程(Linux)

热门文章

  1. 说说学习中的那点小焦虑
  2. C语言保留小数相关问题
  3. vue el-input的多种验证
  4. 雷柏MT750S鼠标使用总结(驱动|连接|模式|续航)
  5. Python中的bytes与bytearray详解
  6. 苹果 App Store账号申请和证书申请发布app
  7. 《MySQL必修课:存储引擎大揭秘!InnoDB和MyISAM究竟谁更强?》
  8. [4G5G专题-51]:物理层-正交频分复用OFDM, AxC, IQ,
  9. uaa权限scope
  10. Markdown快速入门和扩展内容(Typora)