【FPGA黑金开发板】NIOSII那些事儿--基于AVALON总线的IP定制(十七)
声明:本文为转载作品,版权归本博文作者所有,如需转载,请注明出处http://www.cnblogs.com/kingst/
简介
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 [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)
beginclock_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:beginclock_divide_reg_selected<=0;duty_cycle_reg_selected<=0;control_reg_selected<=0;endendcase
end //写PWM输出周期的时钟数寄存器
always @ (posedge clk or negedge reset_n)
beginif(reset_n==1'b0)clock_divide_reg=0;elsebeginif(write & chipselect & clock_divide_reg_selected)beginif(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];endend
end//写PWM周期占空比寄存器
always @ (posedge clk or negedge reset_n)
beginif(reset_n==1'b0)duty_cycle_reg=0;elsebeginif(write & chipselect & duty_cycle_reg_selected)beginif(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];endend
end//写控制寄存器
always @ (posedge clk or negedge reset_n)
beginif(reset_n==1'b0)control_reg=0;elsebeginif(write & chipselect & control_reg_selected)beginif(byteenable[0])control_reg=writedata[0];endend
end//读寄存器
always @ (address or read or clock_divide_reg or duty_cycle_reg or control_reg or chipselect)
beginif(read & chipselect)case(address)2'b00:readdata<=clock_divide_reg;2'b01:readdata<=duty_cycle_reg;2'b10:readdata<=control_reg;default:readdata=32'h8888;endcase
end//控制寄存器
assign pwm_enable=control_reg;//PWM功能部分
always @ (posedge clk or negedge reset_n)
beginif(reset_n==1'b0)PWM_counter=0;elsebeginif(pwm_enable)beginif(PWM_counter>=clock_divide_reg)PWM_counter<=0;elsePWM_counter<=PWM_counter+1;endelsePWM_counter<=0;end
end always @ (posedge clk or negedge reset_n)
beginif(reset_n==1'b0)PWM_out<=1'b0;elsebeginif(pwm_enable)beginif(PWM_counter<=duty_cycle_reg)PWM_out<=1'b1;elsePWM_out<=1'b0;endelsePWM_out<=1'b0;end
endendmodule
上面的程序保存好以后,命名为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 = 1;//将pwm指向PWM_0_BASE首地址
PWM *pwm = (PWM *)PWM_0_BASE;
//对pwm进行初始化,divi最大值为232-1。pwm->divi = 1000;pwm->duty = 0;pwm->enable = 1;//通过不断的改变duty值来改变LED一个周期亮灯的时间长短while(1){if(dir > 0){if(pwm->duty < pwm->divi)pwm->duty += 100;elsedir = 0;}else{if(pwm->duty > 0)pwm->duty -= 100;elsedir = 1;}usleep(100000);}return 0;
}
【FPGA黑金开发板】NIOSII那些事儿--基于AVALON总线的IP定制(十七)相关推荐
- 【连载】【FPGA黑金开发板】NIOS II那些事儿--硬件开发(一)
声明:本文为原创作品,版权归黑金动力社区(http://www.heijin.org)所有,如需转载,请注明出处http://www.cnblogs.com/kingst/ 前言 从今天开始,NIO ...
- FPGA黑金开发板mini版新鲜出炉!!!
功能描述: 1 – EP2C5Q208C ALTERA FPGA芯片 2 –64Mbit SDRAM 3 –2M*8bit FLASH 4 - EPCS1配置芯片 5 - 20M有源晶振 6 ...
- FPGA黑金开发板第一帖
从今天起,此博客将专注于NIOS II的研究,以及对FPGA黑金开发板的技术支持.如果大家有任何的意见及建议,请留言.
- FPGA黑金开发板勘误
FPGA黑金开发板(型号OSH-2-8)已发现两处勘误: 核心板上的红框的扩展口(图1所示)有两处丝印标注错误,图2中69脚和70脚标注错位,图3中的84脚和86脚指示错位,大家使用时请注意.
- FPGA黑金开发板 CYCLONE IV核心板全新上市!!!!
FPGA: EP4CE15F17C8N SDRAM: 256Mbit (16M*16bit) SRA ...
- 【连载】【FPGA黑金开发板】NIOSII那些事儿--USB主机模式(二十一)
声明:本文为原创作品,版权归本博文作者所有,如需转载,请注明出处http://www.cnblogs.com/kingst/ 上一节,我们讲了USB的设备模式,可以实现计算机与黑金开发板的数据通信.这 ...
- 【连载】【FPGA黑金开发板】Verilog HDL那些事儿--12864(ST7565P)液晶驱动(十三)...
声明:本文为原创作品,版权归akuei2及黑金动力社区(http://www.heijin.org)共同所有,如需转载,请注明出处http://www.cnblogs.com/kingst/ 4.2 ...
- 【连载】【FPGA黑金开发板】Verilog HDL那些事儿--12864(ST7565P)液晶驱动(十三)
声明:本文转载于http://www.cnblogs.com/kingst,版权归akuei2及黑金动力社区(http://www.heijin.org)共同所有. 4.2 实验十二:12864(ST ...
- 【连载】【FPGA黑金开发板】Verilog HDL那些事儿--数码管电路驱动(八)
声明:本文为原创作品,版权归akuei2及黑金动力社区(http://www.heijin.org)共同所有,如需转载,请注明出处http://www.cnblogs.com/kingst/ 3.1 ...
最新文章
- Openlayers4中实现动态线效果
- vue使用总结-生命周期篇
- Scenario 7 – HP C7000 VC FlexFabric Tunneled VLANs and SUS A/A vSphere
- matlab红外图像温度提取,一种基于红外热图的温度提取方法.pdf
- HTML progress控件
- 华为云数据库TaurusDB性能挑战赛,50万奖金等你来拿!
- python语言基本认识_Python基础语法合集——了解python
- 查看ssis执行日志_SSIS包日志记录概述
- jfreechart柱状图java mysql_时序时空数据库MySQL再升级,兼容MySQL协议
- usb启动计算机boss设置方法,技嘉主板bios设置usb启动(图文教程)
- 人工智能的安全、伦理和隐私问题
- 备份自查-微博错误代码
- python属于什么类型的选修课_河北大学python选修课00次作业
- Hive面试题考点-整理
- IDA + GDBServer实现iPhone程序远程调试 (手机远程)
- marplotlib坐标轴ax
- mavens使用阿里云国内私服下载
- 能量收集认知传感器网络——论文结构扫盲
- 计算机如何区别指令和数据 ?
- CF:C. Rumor