FPGA 之 SOPC 系列(七)NIOS II 高级技术_FPGA技术江湖的博客-程序员秘密

技术标签: fpga  FPGA学习系列  SOPC  

今天给大侠带来今天带来FPGA 之 SOPC 系列第七篇,NIOS II 高级技术,希望对各位大侠的学习有参考价值,话不多说,上货。

 

 

本篇是有关SOPC的深入设计,帮助读者掌握如何定制用户指令。定制用户逻辑外设和定制用户指令是使用Nios II嵌入式软核处理器的SOPC系统的重要特性,用户还可以通过定制用户逻辑外设和定制用户指令来实现各种应用要求,同时介绍了Nios II C语言至硬件加速编译器(C2H)。

以下为本篇的目录简介:

7.1 定制基于Avalon的用户外设

7.2 定制Nios II用户指令

7.3 Nios II C语言至硬件加速编译器(C2H)简介

 

 

7.1 定制基于Avalon的用户外设

 

 

+

 

 

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

 

用户定制SOPC Builder元件的开发流程

(1)指定硬件功能

(2)指定微处理器访问和控制该硬件的应用程序接口

(3)定义一个AVALON接口:提供正确的控制机制、足够的吞吐性能

(4)采用VHDL或Verilog编写硬件设计

(5)单独测试硬件设计

(6)编写C头文件,定义寄存器映射

(7)使用元件编辑器将硬件和软件文件打包成一个元件

(8)例化元件为SOPC系统的一个模块

(9)使用NIOSII处理器测试元件的寄存器级访问

(10)编写元件的驱动程序

(11)反复改进元件的设计:硬件、软件、元件更新

(12)编译完整的包含一个或多个该元件的SOPC系统

(13)执行系统级的验证,若必要,进行反复设计

(14)完成元件设计,发布共享元件

 

定制用户外设简介

下图为带Avalon Slave端口的典型元件组成框图:

带Avalon Slave端口的典型元件组成框图

 

在这里我们将详细的通过一个控制LED灯亮度的元件来介绍基于AVALON总线的用户外设的过程。

LED灯亮度原理:

PWM输出一个占空比可调的方波。当一个周期11个时钟,高电平输出7个时钟时的PWM输出波形如下图所示。

PWM输出波形

 

PWM设计说明:

本实例的PWM是按下列要求设计的:

1.任务逻辑按一个简单时钟进行同步操作。

2.可以使用微控制器(Nios II)来设置PWM的周期和占空比的值。因此要提供一个可对PWM寄存器进行读写的接口和控制逻辑。

3.定义寄存器来存储PWM周期和占空比的值。

4.微控制器可以通过控制寄存器的禁止位来关闭PWM输出。

 

元件内部还包括使能控制寄存器、周期设定寄存器以及占空比设置寄存器。设计中我们将各寄存器映射成AVALON SLAVE端口地址空间内一个单独的偏移地址。每个寄存器都可以进行读写访问,软件可以读回寄存器中的当前值,寄存器及偏移地址设定如下:

 

构建一个符合AVALON-MM slave 接口规范的可以实现我们功能的时序逻辑,在这里,我们利用VERILOG语言来编写。在程序中会涉及到AVALON信号,我们把这些要用到的信号陈列如下。

 

代码如下:

module led_control(    clk,    reset_n,    chipselect,    address,    write,    writedata,    read,    byteenable,    readdata,    led_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  led_out;     reg    [31:0]  clock_divide_reg;  reg    [31:0]  duty_cycle_reg;  reg    control_reg;  reg    duty_cycle_reg_selected;  reg    clock_divide_reg_selected;  reg    control_reg_selected;  reg    [31:0]  led_counter;  reg    [31:0]  readdata;  reg    led_out;  wire  led_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  //写led输出周期的时钟数寄存器  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//写led周期占空比寄存器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  endend//写控制寄存器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  endend//读寄存器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'h8888;    endcaseend//控制寄存器assign led_enable=control_reg;//led功能部分always @(posedge clk or negedge reset_n)begin  if(reset_n==1'b0)    led_counter=0;  else  begin    if(led_enable)    begin      if(led_counter>=clock_divide_reg)        led_counter<=0;      else        led_counter<=led_counter+1;    end    else      led_counter<=0;  endendalways @(posedge clk or negedge reset_n)begin  if(reset_n==1'b0)    led_out<=1'b0;  else  begin    if(led_enable)    begin      if(led_counter<=duty_cycle_reg)        led_out<=1'b1;      else        led_out<=1'b0;    end    else      led_out<=1'b0;  endend endmodule

 

在工程里添加好这个.v文件后,命名为led_control.v并将其存放到工程目录下就OK

接下来我们开始添加这个元件了,我们在Q2里打开sopc builder,进入后,点击file->new component 。

 

点击后,如下图,我们点击ADD,添加进来我们建立的那个.V文件。加入后,稍微等一下,系统开始对它的端口进行分析了,当出现NOERROR时,说明就OK了。关闭它就行。点击NEXT,我们可以看到,我们的那些端口信号都出现了。

 

根据功能要求来配置这些信号,其中,INTERFACE是AVALON接口类型了。SIGNAL TYPE指的是各个AVALON接口类型下的信号类型。好在系统已经分析好了,只有一个LED_OUT需要改动,因为它的朝向不是avalon模块了,改完后如下。

 

改完后点击NEXT,找到slave addressing把它改成native,意思就是地址对齐的选项,选择为静态地址对齐,其他的地方默认。其他的选项说明一下这个TIMIING部分,led的avalon slave端口与avalon slave端口时钟信号同步,读写的建立时间保持时间为0,因为读、写寄存器仅需要一个时钟周期,所以读写为0等待不需要延时。点击NEXT,在GROUP建立一个名称,叫MYIP,那么这个元件就放到这个组里了。

点击FINISH,后点击YES,就会生成一个led_control_hw.tcl脚本文件,回到sopc builder界面,在左侧中的myip中可以找到led_control元件了。

 

添加路径,这个路径设置是为了让SOPC BUILDER可以找到led_control.v的位置。不然的话,下次进入SOPC BUILDER时,这个元件可能无效。操作是在tool->options,点击右侧的ip search path,接下来的操作一目了然。

  • 自动地址分配

  • 分配中断

  • 管脚分配

  • 编译

 

编译好后查看一下system.h的变化情况,我们可以发现,多出来一个ledcontrol部分了下面给出测试代码:

#include<unistd.h>#include"system.h"//typedef struct{
                       volatile unsigned int divi;                    volatile unsigned int duty;                    volatile unsigned int enable;                }LED_CONTROL;   int main(){
       int dir=1;    //    LED_CONTROL *led_control=(LED_CONTROL*)LED_CONTROL_BASE;        //    led_control->divi=1000;    led_control->duty=0;    led_control->enable=1;        while(1){
           if(dir>0){
               if(led_control->duty<led_control->divi)                led_control->duty+=100;            else                dir=0;            }            else{
                   if(led_control->duty>0)                    led_control->duty-=100;                else                    dir=1;            }            usleep(100000);        }                   return 0;}           

 

7.2 定制Nios II用户指令

 

 

+

 

 

用户定制指令:将一个包含多条标准指令的指令序列减少为硬件实现的一条指令

1)NIOSII处理器配置向导提供了图形化界面添加封装用户定制指令;

2)NIOSII支持256条定制指令;

3)NIOSII IDE在system.h中为每条定制指令产生一个宏,用户在应用程序中通过调用宏访问定制指令。

 

Nios II定制指令综述

组合逻辑指令结构框图如下图所示:

组合逻辑指令结构框图

 

多周期指令结构框图如下图所示:

多周期指令结构框图

 

扩展指令结构框图如下图所示:

扩展指令结构框图

 

带内部寄存器的乘加指令结构框图如下图所示:

带内部寄存器的乘加指令结构框图

 

定制指令实现方式

定制指令支持多种设计文件,包括:Verilog HDL, VHDL, EDIF netlist file, Quartus II Block Design File (.bdf), 和Verilog Quartus Mapping File (.vqm)。

 

具体实现方法有:

1.导入HDL文件实现定制指令;

2.通过DSP Builder实现定制指令加速模块;

3.直接使用SOPC Builder中自带的定制指令。

 

简易步骤如下:

(1)打开NIOSII CPU的定制指令设置页;

 

(2)添加用户定制指令设计文件;

 

(3)发布用户定制指令

 

(4)将定制指令加入系统,完成定制指令添加。

 

7.3 Nios II C语言至硬件加速编译器(C2H)简介

 

 

+

 

 

  • C2H是能够提升对时间性能要求较高的ANSIC函数的工具,它将这些函数转换为FPGA中的硬件加速器。

  • C2H支持标准ANSI C代码,可加速实现多种应用程序,提高其运行效率,包括访问片内、外部存储器和外设等。

  • C2H帮助Nios II用户以最少的资源占用来达到提高系统性能的目的。

 

Nios II C2H编译器设计流程非常简单,编写好应用程序后,用户需要做的工作首先是分析软件代码,确定出现性能瓶颈的函数,然后在Nios II IDE中高亮显示所需的函数,右键单击加速便可以生成自动链接至软件流程的硬件加速器。

 

 

FPGA 之 SOPC 系列第七篇就到这里结束,下一篇将带来第八篇,程序固化相关内容。各位大侠,明天见!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_40310273/article/details/106524065

智能推荐

老大们,谁是“过渡技术”?_Coder李海波的博客-程序员秘密

看到孟老大这篇《谁不是“过渡技术”》http://blog.csdn.net/myan/archive/2006/11/20/1398915.aspx ,很感慨啊。我认为一是我们还是经常会用到所谓“过渡技术”,只是我们已经习惯去使用它。当年有位兄弟用汇编写了一个学籍管理系统,很牛了,现在用C#、Java等很容易就做出来了。以前很难完成的事情,现在比较容易就能做到,只能说是技术进步了。但是技术的

jffs2_scan_eraseblock_jffs2文件系统恢复_brucetein的博客-程序员秘密

有关 jffs2_scan_eraseblock 问题小结2008-10-04 16:35总结前面遇到的问题: 1.有关类似: mtd->read(0x44 bytes from 0x68cf44) returned ECC errorjffs2_get_inode_nodes(): CRC failed on node at 0x0068c684: Read 0xe8b9

用JDBC操作数据库出现的问题_jdbc访问数据库产生的问题_■sunshine■的博客-程序员秘密

起因是在操作ResultSet结果集的时候出现的问题问题的原因是操作ResultSet结果集需数据库连接来操作数据库,我出现这个的原因就是在处理ResultSet结果集的时候提前把数据库连接给关闭了

在STM32上对EV1527等无线编码格式的C程序编码实现_1527编码_laoleizi的博客-程序员秘密

测调 西安.老雷子 2020年6月1日软件平台 WINDOWS Keil uVision,STM32 ST-LINK硬件平台 STM32S108C8B6 通用32开发板调试发射端: 蜂鸟灵T1 输入需要用MCU进行编码,利于指定编码接收端: 蜂鸟灵R1 输出有五种模式,其中模式5就是串口直接输出编码的编码模式 EV1527 一、 系统调试硬件结构说明发射端硬件示意图 接收端硬件示意图二、 如何对位进行编码先简单了解一下编码中如何把二进制体现出来,如下图,芯片本身调制解调不是调整频率而是采用

2021年第四届“中青杯”A题 汽车组装车间流水线物料配送问题(超详细思路及代码)_车间行车送料算法_KeepLearners的博客-程序员秘密

2021年第四届“中青杯”A题 汽车组装车间流水线物料配送问题(超详细思路及代码)题目背景:汽车制造冲压、焊接、涂装和总装四大关键工艺。总装过程中物料的配送补充,是本次题目的核心。物料补充工作称为运输,一般由拖车完成。拖车接收到任务指令后,需要进行如下操作:1)取料:前往目标物料的存储位,将物料装载至拖车;2)配送:将物料运送至目标工位和卸载。需要注意的是,在不超过小车容量的情况下,多个任务可以同时进行。问题 1:某总装车间内有两条并行的流水线,其布局如图1所示,其道路关键节点和工作点见附件1。该车

随便推点

(二)Xray-进阶使用方法_xray高级_面包and牛奶的博客-程序员秘密

Xray的进阶的使用方法 作为一个新接触信息安全专业的菜鸟,我又开始了叙述我的艰辛之路了(鹅鹅鹅)!第一个入门链接先放这了4. 关于Xray的代理问题(包括firefox )5. 配置Xray的扫描证书6. 关于Xray的实战(包括网址推荐)4.关于Xray的代理大致分为两大类。一类是使用Google的Blink浏览器内核,这类包括Google Chrome,Opera,Microsoft Edge,百度,搜狗等;另一类就是Firefox的Gecko了,众所周知火狐一向比较麻烦的 ~~(我认为

pip install PyInstaller安装失败 no such option: --no-user_no such option: -n_camille靖的博客-程序员秘密

以管理员方式运行程序一、安装1、安装pywin32 pip命令安装:pip install pywin32(推荐) pip命令安装:pip install pywin32-ctypes2、安装Pyinstaller  pip命令安装:pip install PyInstaller如果还出现 no such option: --no-user,改为提示的命令行.:D:...

php 正则 中文英文,php 验证只能输入汉字、英语、数字的正则表达式_Baharim Bostan的博客-程序员秘密

收藏了正则表达式。可以验证只能输入数字、汉字、英语。分开验证了也可以整合一起验证。但是我是拆分开好了。比较好使。可以单独的验证。经过本人测试可以使用的哦!下面就是php 验证只能输入汉字、英语、数字的代码了if(preg_match('/^[0-9]+$/',$str)){echo '值能输入数字';}if(preg_match('/^[a-zA-Z]+$/',$str)){echo '只能输入英...

Docker 容器设置自启动_docker 自启动容器_hu_wenjie的博客-程序员秘密

容器自启动分为两种情况一为新建容器时配置自启参数docker run --restart=always 容器id 或 容器名称二为已存在的容器配置自启docker update --restart=always 容器id 或 容器名称

根据16进制输出所有汉字_weixin_34236869的博客-程序员秘密

转自http://www.phpweblog.net/fuyongjie/archive/2009/03/13/6384.html&lt;? php$begin = hexdec ( " 4e00 " ); // 16进制转化为10进制 $end = hexdec ( " 9fa5 " );$a = ' [" ' ;for ( $i = $be...