• project2007模板下载 > nios从入门到精通
  • nios从入门到精通

    免费下载 下载该文档 文档格式:PDF   更新时间:2007-08-08   下载次数:0   点击次数:1
    文档基本属性
    文档语言:Simplified Chinese
    文档格式:pdf
    文档作者:2009-2013年中国乙二醇行业市场调查与发展前景预测报告|中国市场报告网
    关键词:2009-2013年中国乙二醇行业市场调查与发展前景预测报告
    主题:《2009-2013年中国乙二醇行业市场调查与发展前景预测报告》
    备注:报告名称:《2009-2013年中国乙二醇行业市场调查与发展前景预测报告》 报告编号:19259A 咨询电话:4006-12-8668、010-60877066、010-60877067、010-60877068 网址:http://www.360BaoGao.com/2009-04/2009_2013yierchunshichangdiaochaBaoGao.html
    点击这里显示更多文档属性
    NIOS 设计从入门到精通
    Rev 1.2 April 1, 2007
    版本修订
    Rev. 0.1 0.2 1.0 2.0 2.1 Author Tenglong Tenglong Tenglong Tenglong Tenglong Date 2007-1-6 2007-2-6 2007-3-12 2007-4-2 2007-4-10 Description First Draft with Sopc_ep2c8V1 Fit to Sopc_ep2c8V2 Fit to SOPC_EP2C8V3 Fit to SOPC_EP2C8 Delete the timer struct
    2
    目录
    NIOS 设计从入门到精通.................................................. 1 目录 .................................................................. 3 总体概述 .............................................................. 5
    第一节,学习平台概述 ............................................................5
    一,软件平台................................................................ 5 二,硬件平台................................................................ 6
    第二节,NIOS概述 ................................................................7
    一,第一代Nios嵌入式处理器.................................................. 7 二,第二代Nios嵌入式处理器.................................................. 7 三,Nios II处理器的优点特性................................................. 7
    第一章:实验板电路..................................................... 9
    第一节,实验板特点: ............................................................9 第二节,存储电路 ................................................................9 第三节,配置电路 ...............................................................11 第四节,按键及LED电路 ..........................................................13 第五节,LCD Module接口电路 .....................................................14 第六节,EEPROM及Buzzer电路 .....................................................14 第七节,PS/2 及VGA接口电路 .....................................................16 第八节,红外发射及接收电路 .....................................................17 第九节,RS232 接口电路 .........................................................18 第十节,时钟及锁相环电路 .......................................................18 第十一节,I/O分配 ..............................................................19 第十二节,电源电路 .............................................................20
    第二章:逻辑部分实验.................................................. 22
    第一节,七段数码管显示实验: ...................................................22 第二节,蜂鸣器演奏实验: .......................................................27 第三节,红外发射及接收实验: ...................................................31 第四节,通过I2C总线控制E2PROM实验: ............................................32 第五节,8 色VGA显示字符实验: ..................................................34 第六节,PS/2 键盘接口及RS232 通讯实验: .........................................35 第七节,PLL(锁相环)的使用: ..................................................37
    第三章:NIOS基础实验.................................................. 42
    第一节,流水灯实验: (本实验最好独立完成) ......................................42 第二节,JTAG UART通讯实验 ......................................................55
    3
    第三节,LCM(LCD MODULE)显示实验 ................................................64 第四节,按键中断实验: .........................................................66 第五节,计数显示实验 ...........................................................70 第六节,建立带Flash的NIOS II系统及配置方法: ...................................72 第七节,定时器编程 .............................................................76 第八节,I2C Controller IP Core的使用 ...........................................81
    第四章:基于HAL的设备控制............................................. 84
    第一节,文件系统 ...............................................................85 第二节,字符设备 ...............................................................86 第三节,时间设备 ...............................................................88 第四节,Flash设备 ..............................................................90 第五节,DMA的使用 ..............................................................95 第六节,只读文件子系统 ........................................................100
    第五章:软硬件协同设计实例讲解....................................... 101 Nios设计精通篇 ...................................................... 102 第六章:Nios II Flash Programmer..................................... 102
    第一节,在SOPC Builder下定制目标板: ..........................................102 第二节,NIOS II闪存编程器的使用 ...............................................104 第三节,协控制器EPM240 的工作原理 .............................................114
    第七章:通过实例讲解IP Core 的设计过程............................... 117
    第一节,简介: ................................................................117 第二节,SOPC设备设计流程 ......................................................118 第三节,设计实例一: ..........................................................120 第四节,设计实例二: ..........................................................127
    第八章:综合设计示例................................................. 133 第九章:常用HAL API参考.............................................. 134
    第一节,文件类 ................................................................134 第二节,时间类 ................................................................135 第三节,Flash设备 .............................................................137 第四节,DMA设备 ...............................................................138 第五节,其他常用函数 ..........................................................141
    第十章:常见问题与解答............................................... 142
    第一节,Quartus软件编译类 .....................................................142 第二节,Nios II IDE开发环境 ...................................................142
    4
    总体概述
    第一节,学习平台概述 一,软件平台
    1, Quartus II: 6.0+sp1+sp2 Quartus II 为 ALTERA 公司取代大家所熟悉的最通用工具的 MAX+PLUS II 软件的升 级版本, MAX+PLUS II 在 2000 年已经停止更新了, Quatrus 目前已经更新到 6.0 版本, 而 里面集成了很多非常有用的工具,如 SOPC BUILDER 等,这个工具相比其他同类 EDA 开发 工具, MAX+PLUS II 一样仍是最好用的. 和 MAX+PLUS II 用户可以非常方便的转入 QUARTUS II 工具的使用,因为用户可以选择和 MAX+PLUS II 一样的操作界面,况且有大量的中文 说明可以帮助您更加详细的了解 QUARTUS II 的使用. 好了,您该问了,为什么一定要使用 QUARTUS II 呢 MAX+PLUS II 不是很好用么 对的,MAX+PLUS II 的确非常好用,但大家也都知道,现代技术的飞速发展,使得老一 代的工具很难适应新技术,新产品的需要;这里我们将为什么要使用 QUARTUS II 工具大 体上总结为如下几点: 支持更多更广泛的新器件,新的器件在 MAX+PLUS II 中已经不再支持了; 更多,更先进的功能的加入以及快速的升级:比如 SOPC BUILDER; 更多 IP CORE 的支持; 更多功能的加强:比如综合,布线等功能的加强;
    2, Nios II IDE:6.0 full +sp1
    这个工具是为在 NIOS II 上开发软件工程而定制的工具,类似单片机的软件开发环 境,但设置非常的简单,在 NIOS II IDE 中,您可以完成所有软件开发任务,如工程管 理,编辑和编译,调式,以及闪存器件编程等.在第三章的 NIOS 实验中我们再做详细 的介绍.在这里要说的是 NIOS II IDE 6.0 版本的开发软件需要 QUARTUS II6.0 版本的 支持,也就是说 QUARTUS II 的其他低级版本不能支持 NIOS II IDE 6.0 软件开发环境. 一般都是将 QUARTUS II 和 NIOS II IDE 安装在同一个目录下,建议放在 D:\altera 目 录下.
    5
    二,硬件平台
    以下实验使用的是 SOPC 技术联盟 SOPC_EP2C8 实验开发平台,该平台使用的是性价比较高的 Cyclone II 系列 FPGA 芯片 EP2C8Q208C8,以及丰富的外围设备,详细特性如下: Cyclone II 主控芯片: EP2C8Q208 主芯片+4Mb 配置芯片 8Mbytes SDRAM: SANSUNG SDRAM 4Mbytes Flash: 选用 Intel Flash 芯片,方便 Flash 软件编程 大功率输出电源: 方便用户使用板上电源作扩展应用(2 次开发) 2 个晶振时钟电路:已有 50MHz 一个,预留一个 4 个用户按键及 1 个 Reset 按键:满足用户最小需求 16*2 字符液晶显示模块:显示字符及 ASCII 码 4 位七段数码管:用于数字显示 8 个 LEDs:用于调试及状态显示 E2PROM 芯片:练习 I2C 设备控制 红外发射及接收模块:练习红外发射及接收 RS232 串口电路:系统通讯接口 PS/2 接口电路:用于键盘设备的控制 VGA 接口(8 色)电路:用于简单逻辑实现 VGA 显示 1 个 24 针扩展接口: 方便用户外接其他模块或用于 2 次开发 板上 BBII 下载电缆: 更好的电气连接特性 SD 卡接口:可练习 SD 卡的读取数据 使用 PI5C3384 作为 IO 接口缓冲:以便支持 3.3V 和 5V 外设
    6
    第二节,NIOS概述
    一,第一代Nios嵌入式处理器
    第一代的 Nios 已经体现出了嵌入式软核的强大优势, 但还不够完善. 它没有提供软件开发的集 成环境,用户需要在 Nios SDK Shell 中以命令行的形式执行软件的编译,运行,调试,程序的编辑, 编译,调试都是分离的,而且还不支持对项目的编译.这对用户来说不够方便,还需要功能更为强 大的软核处理器和开发环境.
    二,第二代Nios嵌入式处理器
    2004 年 6 月,Altera 公司在继全球范围内推出 Cyclone II 和 Stratix II 器件系列后又推出 了这些新款 FPGA 系列的 Nios II 嵌入式处理器.Nios II 嵌入式处理器和 Cyclone II FPGA 组合, 使得 Nios 嵌入式处理器在 Cyclone II 中具有超过 100DMIP 的性能,允许设计者在很短的时间内构 建一个完整的可编程系统,风险和成本比中小规模的 ASIC 小. Nios II 系列嵌入式处理器使用 32 位的指令集结构,完全与二进制代码兼容,它是建立在第一 代 16 位 Nios 处理器的基础之上的,定位于广泛的嵌入式应用.Nios II 处理器系列包括了三种内核 ---快速的(NiosII/f) ,经济的(Nios II/e)和标准的(NiosII/s)内核,每种都针对不同的性能 范围和成本. 使用 Altera 的 Quartus II 软件, SOPC Builder 工具以及 Nios II 集成开发环境 (IDE) , 用户可以轻松地将 Nios II 处理器嵌入到系统中. Altera 推出的 Nios II 系列嵌入式处理器扩展了目前世界上最流行的软核嵌入式处理器的性 能, Nios II 嵌入到 Altera 的所有 FPGA 中, 把 例如 StratixII, Stratix, ycloneII,Cyclone, APEX,ACEX 和 HardCopy 系列器件中,用户可以获得超过 200 DMIPS 的性能,用户可以从三种处理器以及超过 60 个的 IP 核中选择所需要的,Nios II 系统为用户提供了最基本的多功能性,设计师可以以此来创建 一个最适合他们需求的嵌入式系统.下表为三种内核的比较:
    三,Nios II处理器的优点特性 1. 可定制特性集
    采用 Nios II 处理器,将不会局限于预先制造的处理器技术,而是根据自定的标准处理器,按 照需要选择合适的外设,存储器和接口.此外,还可以轻松集成自特有的功能,使设计具有独特的 竞争优势.
    7
    2. 配置系统性能
    所需要的处理器,应该能够满足当前和今后的设计性能需求.由于今后发展具有不确定性,因此, Nios II设计人员必须能够更改其设计,加入多个Nios II CPU,定制指令集,硬件加速器,以达到 新的性能目标. 采用Nios II处理器, 可以通过AvalonTM交换架构来调整系统性能, 该架构是Altera 的专有互联技术,支持多种并行数据通道,实现大吞吐量应用.
    3. 低成本实现
    在选择处理器时,为了实现需要的功能,可能要购买比实际所需数量多的处理器,也可能为了 节省成本,而不得不购买比实际需要数量少的处理器.低成本,可定制Nios II处理器能够帮助您 解决这一难题.采用Nios II处理器,可以根据需要,设置功能,在价格低至 35 美分的Cyclone II FPGA等低成本Altera器件中实施.在单个FPGA中实现处理器,外设,存储器和I/O接口,可以降低 系统总体成本.
    4. 产品生存周期管理 :
    为实现一个成功的产品,需要将其尽快推向市场,增强其功能特性以延长使用时间,避免出现 处理器逐渐过时.可以在短时间内,将 Nios II 嵌入式处理器由最初概念设想转为系统实现.这种 基于 Nios II 处理器的系统具有永久免版税设计许可,完全经得起时间考验.此外,由于在 FPGA 中实现软核处理器,因此可以方便实现现场硬件和软件升级,产品能够符合最新的规范,具备最新 特性.
    5. 无与伦比的灵活性
    Nios II 具有完全可定制和重新配置特性,所实现的产品可满足现在和今后的需求.
    6. 定制指令
    Nios II 处理器定制指令扩展了 CPU 指令集,提高对时间要求严格的软件运行速度,从而使开 发人员能够提高系统性能. 采用定制指令, 可以实现传统处理器无法达到的最佳系统性能. Nios II 系列处理器支持多达 256 条的定制指令,加速通常由软件实现的逻辑和复杂数学算法.
    7. 硬件加速
    专用硬件加速器可以作为 FPGA 中的定制协处理器,协助 CPU 同时处理多个数据块.如图 5 中 的循环冗余编码实例,采用硬件加速器处理 64K 字节缓冲比软件速度快 530 倍.SOPC Builder 含 有一个输入向导,帮助开发人员将其加速逻辑和 DMA 通道引入系统. 要想较为详细的了解NIOS,可以访问ALTERA的官方网站:www.altera.com.cn; 也可以到www.sopc.net.cn 网站上和大家进行交流
    8
    第一章:实验板电路
    这章主要讲述 FPGA 系统设计的硬件原理方面的知识,在这里主要想说的是,电路可以自己设 计,也可以参照别人的设计来更改自己的设计;但对于比较典型的应用电路,用户最好不要再别出 心裁,那样会给后期的调试,以及应用程序的编写带来很大不便.
    第一节,实验板特点:
    本平台主芯片使用了 Cyclone II 系列芯片 EP2C8Q208C8,具有 8256 个 LEs,可以满足更多,更 大的系统需求; 个 18*18 位乘法器, 18 可以实现数字信号处理 (DSP 功能)2 个增强型锁相环 ; (PLLs) , 能够提供先进的时钟管理能力,如频率合成,可编程移相,外部时钟输出,可编程占空比,锁定检 测,可编程带宽,输入时钟扩频和支持高速差分输入输出时钟信号;具有 138 个用户 I/O,能够满 足大多数系统需求. 使用了 1 个 16 位 SDRAM 内存,组建成一个片外 8Mbytes 系统内存电路; 配备了 4Mbytes 的 Flash,用来保存用户数据,系统工程等; 24 个扩展用户 I/O 接口:方便用户进行产品验证,功能验证,二次开发等 使用 EPCS4 作为配置芯片; 丰富的外围设备,供用户进行高级设计; 选用大功率电源芯片来保障系统稳定工作; 在此硬件平台上可以使系统频率达到 110MHz,但建议使用的系统频率为 85MHz,这样会使系统 能更稳定的运行;
    第二节,存储电路
    存储部分分为动态存储与静态存储, 所谓动态存储就是指 RAM 等这些程序运行空间 (相当于电 脑的内存) ,而静态存储则指 Flash 这类存储器(相当于电脑的硬盘) ;SOPC_EP2C8 使用了 1 个 16 位的 SDRAM 组建成 8Mbytes 的片外存储电路,方便用户构建大内存系统. SDRAM
    SDRAM 是"Synchronous Dynamic random access memory"的缩写,意思是"同步动态随机存储器", 就是我们平时所说的"同步内存", 从理论上说,SDRAM 与 CPU 频率同步,共享一个时钟周期.目前, 最新的 SDRAM 的存储速度已高达 5 纳秒. 本系统使用的是 Samsung 公司出产的 K4S641632H,此芯片为 64Mbit 即 8Mbytes 的容量,宽度为 16 位,其典型的电路连接方式如下图 1.2.1 所示:
    9
    图 1.2.1 SDRAM 典型连接图 FLASH Flash 即闪速存储器, 一般用于 SOPC 系统的程序存放和需要掉电保存的数据存放, 但是, Flash 的读操作比 SRAM 慢,写速度更加缓慢(相对于 SRAM 而言) .一般在 Nios 系统启动后,在 Nios 的 Boot 程序把存放在 Flash 中的程序复制到 SRAM 后,再运行. 由于对于不同厂家的 Flash 的擦写时序往往是不一样的,Nios 只支持部分常用 Flash,对于 不支持的 Flash 类型,就只能由 Nios 系统的设计者自己完成相关 Flash 擦写子程序的编写,需要 自己定义 Flash 组件. 现在,大部分 Flash 支持 CFI(公共 Flash 接口)命令集,只要有个支持 CFI 的 Flash 组件, 就可以支持大部分的 Flash,不需要再自己定制 Flash 组件了. 一般情况下,Flash 与 SRAM 都挂接在同一个 Avalon 三态总线桥上,共用一条三态总线. Flash 使用的是 Intel 的 4Mbytes 的存储器,Flash 的使用已经相当广泛,在此只介绍我们所 使用的 Flash 使用方式,我们选用的是 Intel 生产的 TE28F320J3C,关于芯片的详细资料可以参考 光盘中的数据手册, 此芯片可选用 8 位或 16 位数据宽度, 我们选用的是 8 位数据宽度的连接方式, 所以将 BYTE_n 管脚通过下拉电阻接地,在使用时,也是使此管脚输出底电平;WP_n 输出高电平, RESET_n 与 FPGA 没有相连,RY.BY_n 信号可以不考虑;OE_n 信号是 Flash 输出使能信号,也就是 FPGA 读 Flash 的控制信号; RW_n (WE_n) 是写使能信号, 也就是 FPGA 对 Flash 的写控制信号; CS_n 是片选信号;功能管脚部分电路图如下图 1.2.2 所示
    10
    图 1.2.2 Flash 特殊管脚连接图
    第三节,配置电路
    配置电路是负责 FPGA 配置数据的保存,上电配置的电路,Altera 为 NIOS 系统的配置提供了 三种可行方案: 第一是软硬件数据全部存储在 EPCS 这类串行配置芯片中 (也可以将软件工程存放在 FPGA 内部的 ROM 中,编译后只有硬件配置数据) ; 第二是将硬件数据存放在 EPCS 中,而将软件工程存放在 Flash 中; 第三种则是将软硬件工程全部存放在 Flash 中; 下面介绍一下这几种配置方案的优缺点及适用范围: 第一种电路优点是结构简单;缺点是 EPCS 的容量小,且价格高;适用于软件工程小的方 案; 第二种的优点是可以在 Flash 中存放更大的软件工程及用户数据; 缺点在于也使用了价格 高昂的 EPCS 芯片,且配置方案单一;适用范围较广; 第三种的优点是可多重配置, 扩展性强, 系统安全性增强, 可以避免使用价格高昂的 EPCS 串行配置器;缺点是需要一个 CPLD 做控制器,使得电路结构复杂;适用范围广; 本实验平台上使用的 EPCS 串行配置器为 EPCS4,其容量为 4Mbits;电路如图 1.3.1 所示,AS 接口电路如图 1.3.2 所示.
    11
    图 1.3.1 串行配置器电路图
    图 1.3.2 AS 接口电路
    12
    第四节,按键及LED电路
    按键和 LEDs 是一般系统的输入输出设备,在 NIOS II 系统中按键的使用比 LED 的使用要复杂 得多,但 Nios II 系统中已经提供了非常丰富的函数,这样,只要用户了解这些函数的使用,就能 够非常方便的使用这些设备; 本系统提供了一个 CPU 软核重起按键,4 个用户按键,4 个 LEDs 及一个七段数码管,其电路 分别如图 1.4.1,图 1.4.2,图 1.4.3,图 1.4.4 所示;
    图 1.4.1 CPU 重起按键
    图 1.4.2 用户按键
    图 1.4.3 用户 LEDs
    13
    图 1.4.4 七段数码管
    第五节,LCD Module接口电路
    SOPC_EP2C8 板使用的是很常见的 16*2 字符液晶,能够使用 SOPC Builder 中的控制器来控制, 其电路如下图 1.5.1 所示:
    图 1.5.1 字符液晶模块电路 其中控制信号是单向的,而数据信号是双向的,并且由 RW(读写)信号来控制数据流动的方 向;这是由于在 Nios II 系统中控制字符液晶进行显示时,要求得知液晶当前的状态是否空闲,空 闲时继续写,忙时等待;
    第六节,EEPROM及Buzzer电路
    SOPC_EP2C8 板使用的 EEPROM 的型号是 24C02,容量为 2KBits,加入此模块主要是为了给用户 练习 I2C 总线控制使用,既可以练习使用简单逻辑控制,也可以练习在 Nios 系统中使用我们提供 的 I2C 总线控制器来控制;其电路原理图如下图 1.6.1 所示:
    14
    VCC3.3
    R44
    R45
    1
    4.7K
    1
    4.7K SCLK
    2
    U13
    2
    I2c_clk
    SDA
    1 2 3 6 7
    A0 A1 A2 SCLK
    SDA
    5 VCC3.3
    I2c_sda
    VCC WP GND
    24C02/SO
    8 4
    图 1.6.1 EEPROM 原理图电路 蜂鸣器是比较简单的发声器件, 控制蜂鸣器并不复杂, 它会根据输入其频率的不同而发出不同 的声音.其电路原理图如下图 1.6.2 所示:
    P119
    B1
    BUZZER
    图 1.6.2 Buzzer 电路原理图
    15
    第七节,PS/2 及VGA接口电路
    PS/2 接口是常用的输入设备接口,能够使用鼠标和键盘来作为系统数据的输入;其电路原理 图如下图 1.7.1 所示:
    VCC_3V 1 1
    R14 4.7K
    R15 4.7K
    SMFerrite Lk1
    2
    2
    1 1
    SMFerrite
    2 2
    Lk2
    PS2_CLK PS2_DATA
    PS2_CLK PS2_TATA
    KEYBOARD
    K1 S
    1 2 3 4 5 6
    VCC_5V
    PS2-6PIN
    图 1.7.1
    PS/2 接口电路
    本开发平台上所使用的 VGA 为简单的 8 色产生电路, 能够在 CRT 显示器上进行字符显示, 为初 学者了解显示原理提供了良好的平台;其电路原理如下图 1.7.2 所示:
    R111 R112 33 33
    VGA 15 5 10 4 9 3 8 2 7 1 6 16 17
    R113 R114 R115 33 33 33
    VGA_VS VGA_HS
    1 VSYNC 1 HSYNC
    2 2
    14 13 12 11
    VGA
    1 1 1
    2 2 2
    VGA_B VGA_G VGA_R
    VGA_B VGA_G VGA_R
    VGA
    图 1.7.2
    8 色 VGA 显示接口电路
    16
    第八节,红外发射及接收电路
    红外发射及接收是常用的短距离内无线数据收发方法之一, 目前应用广泛, 在本实验平台上使 用的是 940nm 红外收发设备,其电路原理图如下图 1.8.1,1.8.2 所示:
    图 1.8.1 红外接收电路
    VCC_5V 2
    R47 100R
    11
    IRDA_TX1 IrDA_TX
    940nm 园头 直插 %5
    R48
    2 IrDA_TX
    IrDA_TX
    1
    1K
    2
    Q2 QNPN
    图 1.8.2
    940nm 红外发射电路
    17
    第九节,RS232 接口电路
    RS232 是常用通信接口,即可以用于对外围设备的通信,也可以用来和主机进行系统通信,其 电路原理如下图 1.9.1 所示:
    VCC3_3
    R 10 R 12
    VCC_5V
    C48 C49
    1
    1
    P1 1 5 9 4 8 3 7 2 6 1
    2
    +
    3 0 0R 3 0 0R
    +
    0.1uF
    U8
    0.1uF
    0.1uF
    2 1
    2 1
    2
    1
    C51
    +
    Dout
    Din
    0.1uF
    1 3 4 5 11 10 12 9 15
    C1+ C1C2+ C2-
    VDD VCC
    2 16
    +
    C50
    2
    1 2
    10 11
    1
    2
    2
    RED
    GREEN
    RS232_T1 RS232_R1
    RS232_T1 RS232_R1
    T1IN T1OUT T2IN T2OUT R1OUT R1IN R2OUT R2IN GND
    max232
    14 7 13 8 6 1
    C52
    +
    D Connector 9
    VEE
    0.1uF
    图 1.9.1
    RS232 接口电路
    第十节,时钟及锁相环电路
    时钟是系统必不可少的一部分,几乎所有的工作都依赖时钟来完成,SOPC_EP2C8 板采用的 EP2C8Q208 芯片具有 2 个锁相环, 为多频率要求的系统提供了极大的方便; 时钟输入从 CLK0 到 CLK7 共 18 个输入,分两组:CLK0 到 CLK3 为一组,CLK4 道 CLK7 为一组;由图 1.10.1 所示电路可知, 利用板上晶振可以使 FPGA 的 2 个 PLL 同时工作, PLL1 可以有两个时钟输入; 且 也可以使用备用时 钟作为 PLL2 的时钟输入;这样就非常方便的满足了不同的系统需求.
    18
    2
    图 1.10.1 晶振所提供的时钟信号
    第十一节,I/O分配
    FPGA 的 I/O 口很多,如何合理的分配,就需要设计者在设计之前将各 bank 的 I/O 功能进行合 理的划分,其中不但要考虑 I/O 电压的设置,还要考虑特殊管脚的使用;下面就拿"威龙"核心板 的 I/O 分配来做具体的解释. SOPC_EP2C8 核心板使用的是 CycloneII 系列芯片 Ep2c8Q208,此芯片具有 4 个 bank, 每一个 bank 可以用不同的 I/O 电压,但本系统设计只使用了 3.3V 一种 I/O 电压. SOPC_EP2C8 核心板上各 Bank 分配如下图 1.11.1 所示.
    19
    I2C(24C02)
    IrDA_TX & Buzzer & Segment
    BANK2
    Segment
    VS1 B A N K 1 BANK4 SDRAM U63 Irda_Rx B A N K 3 FLASH U4
    LEDs
    图 1.11.1 SOPC_EP2C8 板各 Bank 分配
    第十二节,电源电路
    良好的电源设计是系统稳定运行的保障, 在系统设计之前要估算整个系统的整体功率, 然后在 进行电源芯片的选型; 本系统设计使用了 3 种电源,FPGA 芯片使用了 2 种电源,分别为 3.3V I/O 电源和 1.2V 核心 电源;由于 FPGA 的功率较大,再加上其他外围设备的需求,本实验开发平台上选用的 3.3V 电源芯 片是 MIC29302,该芯片能最大输出电流为 3A,完全满足本实验平台的需求;1.2V 电源芯片是 FAN1589,最大输出电流为 2.7A;3.3V 和 1.2V 的电源电路分别如下图 1.12.1 和图 1.12.2 所示:
    20
    图 1.12.1 3.3V 电源电路
    图 2.12.2 1.2V 电源电路
    21
    第二章:逻辑部分实验
    以下所有实验代码,工程文件以及相关文档等都在开发板所附光盘中.由于本实验平台主要是 为了进行 Nios II 系统开发,所以基础逻辑实验部分只是围绕本实验平台外围硬件设备展开,整理 后主要有以下几个实验:
    第一节,七段数码管显示实验:
    1,实验目标:A,了解数码管发光原理 B,熟悉 QUARTUS II 软件的使用
    C,了解 Verilog HDL 代码的相关语句 2,实验原理: 七段数码管和普通发光二极管的发光原理一样,为了进行直观显示而将普通发光 二极管封装在一起,能够进行 16 进制数字显示;有共阳极和共阴极之分,共阳极就是 此实验平台所使用的连接方式,在控制端输入底电平的时候就发光,在输入高电平的 时候就不发光; 3,实验内容:用按键控制的方法将七段数码管点亮 4,实验步骤: 由于此实验是我们的第一个实验,可能有些用户没有用过 QuartusII 开发工具,那么在 此试验中就给出详细的开发步骤: (1) ,打开 Quartus,建立新工程文件,点击 New>>New Project Wizard…,如下图 2.1.1 所示,在弹出的对话框中输入工程的路径及工程名称,如图 2.2.2;
    图 2.2.1
    建立新工程
    22
    图 2.2.2 输入工程名称
    (2) ,点击 New>>Verilog HDL File,如下图 2.2.3 所示:
    图 2.2.3 (3) ,编写如下代码: //顶层模块 module segment( clk, rst_n, sw0, sw1, sw2, sw3, //output hc_cp, hc_si ); input input input output output clk; rst_n;
    建立工程文件
    sw0,sw1,sw2,sw3; //Active low hc_cp; hc_si;
    reg [3:0]data; always @ (posedge clk or negedge rst_n) if (!rst_n) data <= 4'hf; else if (sw0 == 1'b0)
    23
    data <= 4'd0; else if (sw1 == 1'b0) data <= 4'd1; else if (sw2 == 1'b0) data <= 4'd2; else if (sw3 == 1'b0) data <= 4'd3; // ------------------------------------------------------------------// 例化 hc164 的驱动程序 // ------------------------------------------------------------------hc164_driver hc164_driver_inst( .clk .rst_n .led .dot .seg_value .hc_cp .hc_si ); endmodule // hc164_driver 模块 module hc164_driver( clk, input input input input input output output reg reg reg reg [5 :0] [6:0] [3:0] [3 :0] [3 :0] [15:0] reg rst_n, led, clk; rst_n; led; dot; seg_value; hc_cp; hc_si; tx_cnt; hex2led; hc_data_34bit; hc_data_31bit;
    24
    ( clk ), ( rst_n ), ( data ), ( data ), ( {4{data}} ), ( hc_cp ), ( hc_si )
    dot,
    seg_value,
    hc_cp,
    hc_si
    );
    //HC164 Clock input active Rising edges //HC164 Data input //hex-to-seven-segment decoder output
    wire wire
    [15:0] [15:0]
    hc_data = {led,hc_data_34bit,hex2led[6:2], hex2led[1:0] }; hc_data_inv = { hc_data[0],
    hc_data_31bit,
    hc_data[1],hc_data[2],hc_data[3], hc_data[4], hc_data[5],hc_data[6],hc_data[7], hc_data[8], hc_data[9],hc_data[10],hc_data[11], hc_data[12], hc_data[13], hc_data[14], hc_data[15] reg [15:0] clk_cnt; always @ ( posedge clk or negedge rst_n ) if ( !rst_n ) clk_cnt <= 16'd0; else clk_cnt <= clk_cnt + 1'b1; reg [1:0] seg_led_num; always @ ( posedge clk or negedge rst_n ) if (!rst_n ) seg_led_num <= 2'b00; else if ( clk_cnt == 16'hFFFF ) seg_led_num <= seg_led_num + 1'b1; reg [3:0] hex; };
    always @ ( * ) case ( seg_led_num ) 2'b00: hex = seg_value[15:12]; 2'b01: hex = seg_value[11:8]; 2'b10: hex = seg_value[7:4]; 2'b11: hex = seg_value[3:0]; endcase always @ ( * ) begin case (hex) 4'h1 4'h2 4'h3 4'h4 4'h5 4'h6 4'h7 4'h8 : hex2led = 7'b0010_100; : hex2led = 7'b1011_011; : hex2led = 7'b1011_110; : hex2led = 7'b0111_100; : hex2led = 7'b1101_110; : hex2led = 7'b1101_111; : hex2led = 7'b1010_100; : hex2led = 7'b1111_111;
    25
    //数值 //1 //2 //3 //4 //5 //6 //7 //8
    4'h9 4'hA 4'hB 4'hC 4'hD 4'hE 4'hF endcase end
    : hex2led = 7'b1111_100; : hex2led = 7'b1111_101; : hex2led = 7'b0101_111; : hex2led = 7'b1100_011; : hex2led = 7'b0011_111; : hex2led = 7'b1101_011; : hex2led = 7'b1101_001;
    //9 //A //b //C //d //E //F //0
    default : hex2led = 7'b1110_111;
    always @ ( * ) case ( seg_led_num ) 2'b00:hc_data_34bit[3:0] = 4'b0111; 2'b01:hc_data_34bit[3:0] = 4'b1011; 2'b10:hc_data_34bit[3:0] = 4'b1101; 2'b11:hc_data_34bit[3:0] = 4'b1110; endcase always @ ( * ) case ( seg_led_num ) 2'b00:hc_data_31bit = dot[3]; 2'b01:hc_data_31bit = dot[2]; 2'b10:hc_data_31bit = dot[1]; 2'b11:hc_data_31bit = dot[0]; endcase always @ ( posedge clk or negedge rst_n ) if (!rst_n ) tx_cnt <= 6'd0; else if ( clk_cnt[15] ) tx_cnt <= 6'd0; else if ((!clk_cnt[15]) && (tx_cnt <= 6'd32 )) tx_cnt <= tx_cnt + 1'b1; always @ ( posedge clk or negedge rst_n ) if (!rst_n) hc_cp <= 1'b0; else if ( clk_cnt[15] ) hc_cp <= 1'b0; else if ((!clk_cnt[15]) && (tx_cnt < 6'd32 )) hc_cp <= !hc_cp; assign hc_si = hc_data_inv[tx_cnt[4:1]];
    26
    endmodule (4) ,编译后,进行管脚约束,如下图所示:
    (5) ,编译后,将配置文件 key_segment.sof 下载到 FPGA 中进行验证.
    第二节,蜂鸣器演奏实验:
    1,实验目标:A,了解蜂鸣器发声原理 B,进一步熟悉 Verilog 代码
    C,了解数控分频器的工作原理 2,实验原理: 根据蜂鸣器输入信号频率的不同决定了其发声不同的原理,来设计一个由数 控分频器控制 Buzzer 发声的简单实验. 数控分频器的预置值由乐曲的音调的值来决 定,从而间接的控制 Buzzer 的发声频率. 3,实验内容:让蜂鸣器演奏预置的乐曲 4,实验步骤:本实验有两个文件组成,一个为顶层接口模块,另一个为工作模块;顶层接口 文件.编译后只有 3 个外接信号,clk 为系统时钟输入,频率为 50MHz; rst_n 为系 统复位信号,底电平有效,接到 sw8 按键上;sp 为输出控制信号,接到蜂鸣器的输
    27
    入管脚. //顶层文件的 Verilog HDL 逻辑描述如下: `timescale 1ns/1ns module song_top( clk, rst_n, sp ); input input output clk; rst_n; sp; clk_cnt1; //分频计数器 //在时钟上升沿触发 // //Reset 的时候将计数器清零 //系统时钟 //reset //蜂鸣器输出 50MHz 低电平有效
    reg [23:0]
    always @ (posedge clk) if (!rst_n ) clk_cnt1 <= 24'd0; else clk_cnt1 >Symbol…,如下图 2.7.2 所示:
    37
    图 2.7.2 加入新元件 (2) ,在弹出的对话框中,选择如下图 2.7.3 所示的 I/O;
    图 2.7.3 选择 PLL 所在目录 (3) ,在该目录下选择 altpll,双击加入,在弹出的如下图 2.7.4 所示对话框中,选择语言 及输出路径:
    图 2.7.4 语言选择设置
    38
    (4) ,电击 Next,到如下图 2.7.5 所示对话框中,设置芯片系列,速度等级,输入时钟频率 以及锁相环的类型等信息;板上使用的时钟为 50MHz;
    图 2.7.5 设置芯片参数 (5) ,电击 Next,出现如下图 2.7.6 所示对话框,其中"pllena"信号是用来使能锁相环的控 制信号,高电平有效; "areset"为复位信号,高电平有效;当其有效时,锁相环的计数器清零; "locked"为输出锁相环状态信号,当锁相环锁定时,输出 VCC;
    图 2.7.6 锁相环输入输出管脚设定
    39
    (6) ,点击 Next,得到如下图 2.7.8 所示对话框,这是在差分时钟输入时选用的,我们使用的 是 50MHz 的有源晶振,所以不选用此项;
    图 2.7.8 差分输入选项 (7)点击 Next,进入如下图 2.7.8 所示对话框,设定 C0 的输出频率,相移及占空比;
    图 2.7.8 设定 C0 的输出频率,相位及占空比 (8) ,单击 Next,进入如下图 2.7.9 所示对话框,选中 Use this clock 后,可以对其进行设 定;和 C0 一样,此信号是内部时钟信号;在本例中设定为 25MHz
    图 2.7.9 设定 C1 对话框
    40
    (9) ,单击 Next,对 C2 进行设定,使其相位移动-62 度,如下图 2.7.10 所示:
    图 2.7.10 设定 C2 对话框 (10) ,再一直单击 Next,最后点击 Finish,将其加入到顶层文件中,如下图 2.7.11 所示 :
    altpll2
    inclk0
    inclk0 f requency : 50.000 MHz Operation Mode: Normal Clk Ratio Ph (dg) DC (%) c0 c1 c2 3/2 1/2 3/2 0.00 0.00 -62.00 50.00 50.00 50.00
    c0 c1 c2
    inst3
    Cy clone II
    图 2.7.11
    加入到系统中的锁相环
    在上图 2.7.11 种可以看出此锁相环的相关信息,包括输出时钟频率,输出时钟频率, 输出时钟的占空比及相移等. 下一章中每一个实例都是用了锁相环, 用户可根据不同的应用 环境来相应的设置其参数.
    41
    第三章:NIOS基础实验
    第一节,流水灯实验: (本实验最好独立完成)
    1. 2. 3. 4. 实验目标:A,掌握基本的开发流程 实验原理:请参考本章相关内容 实验内容:将 8 位 LED 灯点亮,进行流水灯控制 实验步骤:由于这是我们的第一个实验,所以从第一步硬件平台搭建开始,系统的为 B,熟悉 QUARTUS II 软件的使用
    C,熟悉 NIOS II IDE 开发环境
    大家讲解一下,以方便大家熟悉 NIOS 开发的整体开发流程.一般都有以下几个步骤: 1) 在 QUARTUS II 中建立工程; 2) 用 SOPC BUILDER 建立 NIOS 系统模块; 3) 在 QUARTUS II 中的图形编辑界面中进行管脚连接,锁定工作; 4) 编译工程后下载到 FPGA 中; 5) 在 NIOS II IDE 中根据硬件建立软件工程 6) 编译后,经过简单设置下载到 FPGA 中进行调试,验证 下面就根据以上的步骤进行一次全程开发:建议先看 QUARTUS II 使用说明 第一步:硬件部分设计: (1) ,在 D:\altera\works\下建立一个 hello_led 文件夹,注意目录中不能有空格或中文; (2) ,打开 QUARTUS II ,点击 FILE 下拉菜单下的 New project Wizard…会弹出如图 3.1.1 对话框:
    图 3.1.1 (3) ,然后输入工程存放目录,或点击工程路径右面的按钮设置工程存放目录,在第二栏中输入工 程名称,我们这里输入为 led;之后点击 Finish,对话框会消失,此时已经建立好了 LED 工程文件; (4) ,点击 Assignments 菜单中的 Device,选择芯片 EP2C8Q208C8 如下图 3.1.2 所示:
    42
    图 3.1.2
    选择器件
    (5) ,点击 Tools 下拉菜单下的 Sopc Builder 工具,出现如图 3.1.3 所示对话框:
    图 3.1.3 设定名称 (6) ,在系统名称(System Name)中填写为 nios32,选择语言后点击 OK,在 Device Family 中选 择所要使用芯片的系列,我们所用的是 Cyclone II,更改系统频率为 75MHz,在 Board 中暂时先不
    43
    做选择,使用 Unspecified Board;如下图 3.1.4 所示:
    图 3.1.4 设定芯片及系统时钟 (7) ,在左面元件池中选择元件:我们这个设 需要使用 NIOS 32BIT CPU, 调试串口, PIO, led 先选择如右图 3.1.5 所示的 Nios Processor, NIOS 处理器,双击后会弹出如右图 3.1.6 所 框; 图 3.1.5 选择 Nios II 计 工 程 RAM , 首 这 个 是 示 对 话
    图 3.1.6 NIOS II 设置 在 JTAG Debug Module 栏中选择 level 1,点击 Finish 按钮后返回 SOPC Builder 窗口,将 CPU_0 重新命名为 CPU 如下图 3.1.7 所示:
    44
    图 3.1.7 重命名 CPU 注意:对模块命名要遵循如下规则: 名字最前面应该使用英文; 能使用的字符只有英文字母,数字和"_"; 不能连续使用"_"符号,在名字的最后也不能使用"_" . (8) ,添加 JTAG UART Interface:此接口为 NIOS II 系统嵌入式处理器新添加的接口元件,通过 它,可以在 PC 主机和 SOPC Builder 系统之间进行串行字符流通新,主要用来调试,下载数据等, 也可以作为标准输出,输入来使用; A. 选择 Communication→JTAG UART 加入,会出现设置向导,如下图 3.1.8 所示:
    45
    图 3.1.8 C. 将 jtag_uart_0 重命名为 jtag_uart.
    加入 JTAG UART
    B. 保持默认选项,点击 Finish,返回 SOPC Builder 窗口. (9) ,添加内部 RAM:RAM 为程序运行空间,类似于电脑的内存,此空间越大越有利;在 ep2c8 这 个元件中建议使用 4Kbytes; A 选择 Memory→On-Chip Memory,双击加入,会出现如下图 3.1.9 所示设置向导:
    图 3.1.9 设置内部 RAM 作为系统内存 B 设置后如上图所示,点击 重新
    Finish, 返回到 SOPC Builder 窗口, 命名为 RAM; (10) ,加入 led_pio:此元件为 IO 口, 片机中的 IO 口类似, 用户可以根据需要 设置选项; A. 选择 Other→PIO,双击加入, 会出现如右图 3.1.10 所示设置 向导: B. 选择 Output ports only,点击
    46
    和单 配置
    Finish,重新命名为 Led_pio; 图 3.1.10 加入 LED_PIO (11) ,指定基地址和分配中断号:SOPC Builer 会给用户的 Nios II 系统模块分配默认的基 地址,用户也可以更改这些默认地址. 选择 System 下拉菜单中的 Auto-Assign Base Adress; 选择 System 下拉菜单中的 Auto- Assign IRQs; (12) ,系统设置:选择 More "CPU" Settings,按照下图 3.1.11 所示对系统进行设置;
    图 3.1.11 设置系统运行空间 (13) ,生成系统模块: 选择 System Generation 栏,如下图 3.1.12 所示: 在 System Genetation 中选中 HDL 选项,如果安装了 ModelSim 软件并需要仿真设计, 可以选择 Simulation 选项.
    图 3.1.12 生成 CPU
    47
    点击 Generation,SOPC Builder 根据用户设定不同,而在生成的过程中所执行的操 作不同,在系统生成后点击"Exit"退出 SOPC Builder. (14) ,将刚才生成的模块以图标形式添加到 BDF 文件中:在 SOPC Builder 生成的过程中,会生 成系统模块的图标(Symbol) ,可以将该图标像其他 Quartus II 图标一样添加到当前项目的 BDF 文件中.步骤如下: 单击 File→New,弹出如下图 3.1.13 所示对话框:
    图 3.1.13 加入原理图输入文件 选择 Block Diagram/Schematic File,如上图所示,点击 OK; 在图中单击右键,选择 Inset→Sybol,如下图 3.1.14 所示:
    48
    图 3.1.14 加入系统顶层文件
    在如下图 3.1.15 所示的弹出对话框中选择 Project→nios32,双击加入;
    图 3.1.15 加入锁相环 点击保存,系统会自动命名为 LED,不要对此文件名再作更改;
    之后再加入锁相环: 锁相环能够为 我们提供多个精确的系统时钟频 率, 在如右图 3.1.16 所示的 IO 文 件夹下选择 altpll,双击进入锁相 环的设置向导,根据向导设置好 Pll 后加入;具体步骤请参照第二 章中第七节相关内容. 此处需要注 意的是:
    图 3.1.16 加入 PLL
    49
    将模块与输入输出接口相连,连接后如下图 3.1.17 所示:
    图 3.1.17
    顶层文件图
    锁定管脚,将我们提供的 2C8V1_PIN.tcl 放到现在工程的目录下,然后选择 Tools→Tcl Scripts,会弹出如下图 3.1.18 所示对话框,选择 2C8V1_PIN,然后点击 RUN,管教约束就会 自动加入了;
    图 3.1.18 运行 TCL 脚本文件对管脚进行锁定 编译工程:选择 Process→Start Compilation 命令对工程进行编译; (15) ,配置 FPGA:将编译生成的 SOF 文件下载到目标板上,选择 Tools→Programmer 目标文件下 载,选择 JTAG 模式,通过 J24 口对 FPGA 进行配置,如图 3.1.19 所示:
    50
    图 3.1.19 下载配置文件 第二步:软件部分设计: (1) ,打开 NIOS II IDE,选择 File→New→Project,会弹出如下图 3.1.20 所示对话框:
    图 3.1.20 添加新工程 ( 2 ) 选 择 C/C++ Application , 如 上 图 所 示 ; 点 击 Next , 会 弹 出 图 3.1.21 对 话 框 : ,
    51
    图 3.1.21 选择现有工程实例 (3) ,在 Select Project Template 中选择 Hello LED;在 SOPC Builder System 中选择我们刚 才建立好的工程,CPU 栏会自动选择,如上图所示,点击 Finish,系统会自动生成一个循环点 亮 LED 的软件工程;代码如下:
    #include "system.h" #include "altera_avalon_pio_regs.h" #include "alt_types.h" int main (void) __attribute__ ((weak, alias ("alt_main"))); int alt_main (void) { alt_u8 led = 0x2; alt_u8 dir = 0; volatile int i; while (1) { if (led & 0x81) { dir = (dir ^ 0x1); } if (dir) { led = led >> 1; } else
    52
    { led = led << 1; } IOWR_ALTERA_AVALON_PIO_DATA(LED_PIO_BASE, led); i = 0; while (i<200000) i++; } return 0; }
    (4) ,右键单击工程,选择 Build Project,会弹出如下图 3.1.22 所示信息:
    图 3.1.22 编译软件工程 (5) ,选择 RUN→RUN…,系统会自动探测 JTAG 连接电缆,并弹出如下图 3.1.23 所示对话框:
    图 3.1.23 自动探测电缆 (6)在 Main 列表中的 Project 中选择我们刚才建立的工程 Hello_led_0,在 Target Connection , 中选择所要使用的下载电缆,若使用我们提供的电缆,应选择 ByteBlasterII[LPT1],如下图 3.1.24 所示:
    53
    图 3.1.24 选择电缆,设置通信接口 (7) ,其他部分使用默认选项,点击 RUN 后,目标板上的灯就会出现逐个灭的状态,到此我们简 单的一个流水灯控制完成了. 5,实验总结: 谈谈对软硬件开发流程的认识; 谈谈本实验所使用的硬件资源; 根据软硬件进行思考, 如何让灯出现逐个点亮的流水控制 更改软件如何实现 不改软 件而只改硬件又如何实现呢
    54
    第二节,JTAG UART通讯实验
    实验目标:A,实现计算机和 Nios II 系统通信 B,进一步熟悉 NIOS II IDE 开发环境; C,了解 NIOS II IDE 相关设置选项 D,简单了解相关头文件作用 实验原理: 计算机和 NIOS II 系统通信有多种方式,而 JTAG UART 通信是在 NIOS II 系统中非常容易使用的一种方式,因为 JTAG UART 在 NIOS II 中是一个标准的输入输出设备, 这为我们调试程序提供了极大的方便,建议调试 NIOS 系统使用 JTAG UART 通信方式,而系统 (设备) 间通信使用 RS232 方式; 它和 RS232 串口通信非常类似, 只是它使用的是 JTAG 接口, 它的通信方式如图 3.2.1 所示:
    图 3.2.1 JTAG UART 通信方式原理图 实验内容:在 NIOS II IDE 的控制台窗口显示字符串 实验步骤:这是我们的第二个实验了,所以具体的步骤就不再作详细的介绍了,只在 关键的地方给大家做解释说明;这个实验在实验一的基础上又加入了 SDRAM 作为系统程序运 行空间;所以在此我们介绍一下在 SOPC Builder 中加入 SDRAM 的详细过程: (1) ,在 SOPC Builder 窗口中,选择 Memory→SDRAM Controller,双击弹出对话框中,在 Data Width 中选择 16;Chip Selects 中选 1;Banks 中选 4;Row 选 12,Column 选 8;设置 好后如图 3.2.2 所示;
    55
    图 3.2.2 Sdram 参数设置对话框 (2) ,单击 Next,在出现的对话框中设置时序参数如下图 3.2.3 所示:
    56
    图 3.2.3 Sdram 参数设置时序部分 (3) ,设置好后如下图 3.2.4 所示,点击生成后生成 CPU;
    图 3.2.4 系统构架 (4) ,连接管脚,添加约束如下图 3.2.5 所示,编译后配置到 FPGA 中.
    57
    图 3.2.5 锁定管脚,添加约束 软件设计: 一,从 Nios II 系统输出信息到 PC 机上: (1) ,在上一个实验的软件设计部分中的第二步骤里,我们选择工程模板时选择 Hello world small 这个工程;其代码如下:
    #include int main() { printf("Hello from Nios II!\n"); return 0; }
    (2) ,右键单击工程,选择 Properties,在弹出的窗口中选择 C/C++ Build,如下图 3.2.6 所示:
    图 3.2.6 设置编译优化级别 (3), 在上图所示的 Tool Settings 列表框中点击 Nios II Compiler→General,在优化级 别 (Optimization Levels) 栏中有几种不同的设置为 None, Optimize (-01) Optimize more(-02), , Optimize most(-03)和 Optimize Size(-0s) ;这主要是在编译时对程序代码的优化设置,此工程 中可以选择 Optimize Size(-0s) ; (4) ,右键单击工程,选择 System Library Properties,在弹出的窗口中选择 C/C++ Build, 同样选择 Tool Settings 中的 Nios II Compiler→General,也是选择 Optimize Size(-0s),如 下图 3.2.7 所示:
    58
    图 3.2.7 设置库文件优化级别 (5) ,在左边的列表中选择 System Library,会弹出如下图 3.2.8 所示对话框:
    图 3.2.8 设置标准输出接口设备 (6) ,在左面的 stdout,stderr,stdin 选项框中选择 jtag_uart,将 Small C Library 选项 框和 Reduced device drivers 选项框选中;确保在右面的 Program memory,Read-only data memory,Read/write data memory,Heap memory,Stack memory 选项框中为 RAM; 点击 OK 后,完成设置工作; (7) ,右键单击工程选择 Build Project 进行编译; (8) ,将上一个实验的硬件工程文件下载到 FPGA 中,在 IDE 窗口中选择 RUN→RUN,系统会自 动探测下载电缆及弹出对话框如下图 3.2.9 所示:
    59
    图 3.2.9 自动探测电缆 (9) ,点击 Main 下对话框右面的 Browse,会弹出如下图 3.2.10 所示对话框:
    图 3.2.10 选择工程文件 (10) ,选择我们刚才建立的工程文件,hello_JTAG_UART,点击 OK 后再点击 RUN,将软件工程 下载到目标板中进行运行, 然后在你的控制台 (Console) 上就会有 Hello from Nios II! 这句话显示出来了,如下图 3.2.11 所示:
    60
    图 3.2.11 察看控制台显示 练习:试着从 NIOS II 中输出一段中文到控制台上进行显示; 二,从 PC 机输出给 Nios II 系统: (1) ,根据以上步骤将软件代码更改如下:
    #include #include "system.h" #include "altera_avalon_pio_regs.h" #include "alt_types.h" #define static void TestLED( void ); static void TestLED( void ) { alt_u8 led = 0x2; alt_u8 dir = 0; int j; volatile int i; for (j=0;j> 1; led = led << 1;
    } } }
    if (dir)
    IOWR_ALTERA_AVALON_PIO_DATA(LED_PIO_BASE, led);
    while (i<200000) i++; } return ; } int main() { static int ch = 97; printf("-------------------------------------------\n"); printf("Please input characters in console: \n"); printf("'g':run leds \n"); printf("Other characters except 'g':nothing to do \n"); printf("'q':exit \n"); printf("-------------------------------------------\n"); while((ch = getchar())!='q') { if(ch=='g') { printf("LEDs begin run...\n"); TestLED(); printf("LEDs run over.\n"); } } return 0; } }
    (2),在这里先简单介绍一下各头文件的作用,这个头文件包含了标准输入,输 出,错误函数库;"system.h",这个文件描述了每个设备并给出了以下一些详细信息:设备 的硬件配置,基地址,中断优先级,设备的符号名称,用户不需要编辑 system.h 文件,此文 件由 HAL 系统库自动生成,其内容取决于硬件配置和用户在 IDE 中设置的系统库属性; " altera_avalon_pio_regs.h " 这 个 文 件 是 通 用 I/O 口 与 高 层 软 件 之 间 的 接 口 , IOWR_ALTERA_AVALON_PIO_DATA(LED_PIO_BASE, led)这个函数就是在此文件中定义的,此函 数的功能为将数值(led)赋给以 LED_PIO_BASE 为基地址的用户自定义的 I/O 口上,也就是 将 led 这个值赋给我们硬件中 LED 灯所接的 FPGA 管脚上;"alt_types.h"头文件定义了数 据类型,如下表所示: 类型 alt_8 alt_u8 alt_16 alt_u16 alt_32 有符号 8 位整数 无符号 8 位整数 有符号 16 位整数 无符号 16 位整数 有符号 32 位整数
    62
    说明
    alt_u32
    无符号 32 位整数
    (3) ,右键单击工程,选择 System Library Properties,在弹出的对话框中的左边列表中选 择 System Library,将 Small C Library 的选项去掉,如下图 3.2.12 所示:
    图 3.2.12 设置包含库 (4) ,其他设置不变,编译后下载到目标板上,当在控制台窗口输入 g 时,目标板上的 LED 灯就会出现循环灭的现象; 5,实验总结与思考: 将 Small C library 选项选中后编译会出现什么现象 若在软件中加入 printf("Hello JTAG UART!\n"),语句又会出现什么现象 若再加入 printf("您输入的是'%c';\n",ch),这样的一条语句呢 将软件代码更改如下代码执行,观察控制台及 LED 的输出现象:
    int main() { static int ch = 97; printf("-------------------------------------------\n"); printf("Please input characters in console: \n"); printf("'g':run leds \n"); printf("Other characters except 'g':nothing to do \n"); printf("'q':exit \n"); printf("-------------------------------------------\n"); while((ch = getchar())!='q') { // printf("what you input is '%c';\n",ch); if(ch=='g') { printf("LEDs begin run...\n"); TestLED(); printf("LEDs run over.\n"); } } return 0;
    63
    }
    根据实验二你学到了什么
    第三节,LCM(LCD MODULE)显示实验
    实验目标:A,加深理解开发流程 B,熟悉 SOPC Builder 工具的使用
    C,熟悉 LCM 模块的使用;D,熟悉 LCM 的软件操作语句; 实验原理: 液晶是常用显示输出设备, 其种类繁多; 根据显示大小划分, 16*2, 有 128*64, 128*128 等.而我们用的是 16*2 的字符液晶.关于更多显示原理,请用户自行参 考http://www.sziec.com/zs.htm 实验内容: 在 LCM 上显示字符串 实验步骤: (一) ,硬件搭建: 硬件需求:Nios CPU,JTAG UART,Sdram,Lcd_display; (1)建立新工程, , 打开 SOPC Builder 和以前的系统一样加入 CPU, JTAG UART 和 SDRAM; (2) 选择 Display→Character LCD,双击加入, , 重命名为 lcd_display; 如下图 3.3.1 所示.
    64
    图 3.3.1 系统结构 (3) ,生成 CPU,建立顶层文件,加入锁相环,再加入管脚及其约束,如下图 3.3.2 所示,最后编译后配置到 FPGA 中.
    图 3.3.2 硬件顶层文件 (二) ,软件设计: (1) ,打开 Nios II IDE 建立基于刚刚生成的 CPU 的新工程,选择空工程;然后键入如 下代码: #include "alt_types.h" #include #include #include "system.h" #include "sys/alt_irq.h" #include "altera_avalon_pio_regs.h" void main() { FILE *lcd;
    65
    lcd = fopen("/dev/lcd_display", "w"); fprintf(lcd, "SOPC-EP2C8\n"); fprintf(lcd, "development kit "); printf("\nIf you can see \" SOPC-EP2C8 development kit\" on the LCD, It works ok.\n"); usleep(2000000); fclose( lcd ); return ; } (2),设置 JTAG UART 为 Stdout 编译后下载到硬件中运行,注意察看 LCD Module 以及 Console(控制台)上出现的字符.
    第四节,按键中断实验:
    1, 实验目标:A,了解简单按键设计及其编程; B,熟悉相关 IO 操作函数 C,了解中断原理 2, 实验原理:PIO 按照功能可以分为:输入 IO,输出 IO,三态 IO;PIO 也是通过 Avalon 总线 与 Nios 进行相连,如下图 3.4.1 所示:
    双向口 Tri NIOS CPU PIO
    输出口 Output
    输入口 Input
    图 3.4.1
    Tri,Input,Output
    PIO
    3, 4,
    实验内容:A,对按键进行验证,通过控制台输出验证信息; 实验步骤: 一,在 SOPC 中加入各元件并设置好各地址,试验四的 CPU 只是在试验三的基础上增加了一个 输入 PIO,在元件池中选择 Other→PIO,双击加入,弹出如图 3.4.2 所示对话框,选择
    66
    Input ports only; 然后点击 Input Options 栏, 弹出图 3.4.3 所示对话框, Edge Capture 在 Register 下选取 Synchronously Capture,选择 Either Edge;在 Interrupt 下选取 Genetate IRQ,选择 Edge;点击 Finish,将其命名为 button_pio; 二,其它设置同实验三设置,生成 CPU 后锁定
    管脚并编译,生成配置文件后下载到 FPGA 中; 图 3.4.2 选择 IO 模式
    #include "alt_types.h" #include #include "system.h" #include "sys/alt_irq.h" #include "altera_avalon_pio_regs.h" volatile int edge_capture; static void handle_button_interrupts(void* context, alt_u32 id) { volatile int* edge_capture_ptr = (volatile int*) context; *edge_capture_ptr = IORD_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE); IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE, 0); } static void init_button_pio()
    67
    图 3.4.3 选择触发,中断模式
    三,在 NIOS II IDE 下,根据刚生成的硬件建立一个空工程文件,然后编写软件代码如下:
    {
    void* edge_capture_ptr = (void*) &edge_capture; IOWR_ALTERA_AVALON_PIO_IRQ_MASK(BUTTON_PIO_BASE, 0xf); IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE, 0x0); alt_irq_register( BUTTON_PIO_IRQ, edge_capture_ptr, handle_button_interrupts );
    } static void TestButtons( void ) { alt_u8 buttons_tested; alt_u8 all_tested; int last_tested; init_button_pio(); buttons_tested = 0x0; all_tested = 0xf; edge_capture = 0; last_tested = 0xffff; printf("\nThe test will be end when all buttons have been pressed.\n"); while ( buttons_tested != all_tested ) { if (last_tested == edge_capture) { else { last_tested = edge_capture; switch (edge_capture) {case 0x1: printf("\nButton 1 (SW0) Pressed.\n"); buttons_tested = buttons_tested | 0x1; break; case 0x2: printf("\nButton 2 (SW1) Pressed.\n"); buttons_tested = buttons_tested | 0x2; break; case 0x4: printf("\nButton 3 (SW2) Pressed.\n"); buttons_tested = buttons_tested | 0x4; break; case 0x8: printf("\nButton 4 (SW3) Pressed.\n"); buttons_tested = buttons_tested | 0x8; break; } } } IOWR_ALTERA_AVALON_PIO_IRQ_MASK(BUTTON_PIO_BASE, 0x0); printf ("\nAll Buttons (SW0-SW3) pressed.\n"); usleep(2000000); return;
    68
    continue;
    }
    } static void MenuBegin( alt_8 *title ) { printf("\n\n"); printf("----------------------------------\n"); printf("Nios II Board Diagnostics\n"); printf("----------------------------------\n"); printf("%s",title); } int main() { MenuBegin("Main Menu:"); TestButtons(); return 0; }
    main 主函数部分使用了两个自定义函数,较容易理解,但下面这些函数不容易理解,因为他 们和硬件有很大相关性:
    IOWR_ALTERA_AVALON_PIO_IRQ_MASK(BUTTON_PIO_BASE, 0xf); IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE, 0x0); IORD_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE); alt_irq_register(BUTTON_PIO_IRQ, edge_capture_ptr, handle_button_interrupts )
    在文件"altera_avalon_pio_regs.h"中有如下定义
    #define IOWR_ALTERA_AVALON_PIO_IRQ_MASK(base, data) #define IOWR_ALTERA_AVALON_PIO_EDGE_CAP(base, data) #define IORD_ALTERA_AVALON_PIO_EDGE_CAP(base) IOWR(base, 2, data) IOWR(base, 3, data) IORD(base, 3)
    第一个函数是使能中断函数,是按位来势能的,比如 0xf 表示四位全部使能,而 0x7 表示使能 低 3 位中断; 第二个函数是设置边沿捕获寄存器函数, 用来重新设定寄存器的值; 一般在读取之后会重新设 定为 0; 第三个函数是读取边沿捕获寄存器函数,用来读取寄存器的值; 下面是 alt_irq_register 函数的原形,此函数用来声明 ISR,在软件使用 IRS 之前一定要先 声明;
    extern int alt_irq_register (alt_u32 id, void* context, void (*irq_handler)(void*, alt_u32));
    一般在开发按键中断程序时,handle_button_interrupts()和 init_button_pio()这两个函 数直接使用,不用再编辑. 将程序编辑完之后,先进行设置,主要是选中 Small C Library 和 Reduce device drives, 将 stdout 设置为 JTAG_UART 然后进行编译,下载到实验板进行验证.
    69
    功能为:当用户每按完一次按键之后,就会有信息从控制台反馈回来,同一个按键按的再多 也只有在第一次按时有信息显示;当 4 个按键全部被按完后系统功能结束. 5,总结: A,谈谈对按键中断的认识程度; B,根据实验代码总结按键中断流程; C,更改本实验代码,将功能更改为:每按一次按键,系统都有信息反馈,4 个 按键全部被按过后结束系统功能;
    第五节,计数显示实验
    1, 实验目标: A:综合运用显示输出设备 C:简单了解 RS232 串口通信 2, 实验原理: 本节实验主要使用了 LCM, Segment, LEDs 以及 Button, 可以参考前几节实验相关内容, 而在此只介绍七段数码管的控制原理. 七段数码管的硬件电路原理图请参考第一章第四节相关内容, 3, 实验内容: 本实验是显示 count 的计数值(从 0 到 FF) ,显示终端为 LEDs,七段数码管,16*2 液 晶以及通过 RS232 串口连接的 PC 机的超级终端;显示终端选择为 SW0 到 SW3 四个按键,当 SW0 按下时,只在 LEDs 上进行显示;当 SW1 键按下时,只在七段数码管上进行显示;当 SW2 键按下时,只在 LCM 上进行显示;当 SW3 键按下时,会同是在 LEDs,七段数码管,LCM 以及 串口终端上进行显示. 4, 实验步骤 (一) ,硬件平台的搭建 (1) 在本实验中需要使用的外围硬件有: , 按键, LEDs, 七段数码管, 16*2 液晶以及 RS232 接口,当然还要使用 Sdram 作为系统内存; (2) ,在 SOPC Builder 中,加入各模块,下面只简单介绍七段数码管的加入:由于在本 实验平台上使用的是一位的七段数码管, 所以 I/O 口只使用 8 个, 设置好的七段数码 管接口如下图 3.5.1 所示: B:深入了解中断编程
    70
    图 3.5.1
    七段数码管控制设置
    (3) ,设置好后的系统构架如下图 3.5.2 所示:
    图 3.5.2
    系统组建构架
    (4) 将逻辑部分 hc164_driver.v 添加到本工程,并生成 hc164_driver 的 bsf 文件,然后 在顶层框图中加入 74HC164 控制器,将 seven_seg_pio 连接到 74HC164_driver 的低八位,如下图 3.5.3 所示:
    71
    图 3.5.3 segment 的连接图 (5) ,生成硬件配置文件后,下载到 FPGA 中. (二) ,软件设计 (1) ,打开 Nios II IDE 开发环境,根据刚刚建立的硬件 CPU 新建工程文件,在工程模板中 选择 Count Binary,如下图 3.5.4 所示:
    图 3.5.4 选工程模板 (2) 修改代码中的函数:SEVEN_SEG_PIO_BASE如下: ,
    //unsigned int data = segments[hex & 15] | (segments[(hex >> 4) & 15] Device & Pin Options… ->Dual-Purpose Pins->nCEO 改为"Use as regular I/O",然后点确定,再重新编译一次. 九,生成硬件文件,并下载到 FPGA 中.
    73
    图 3.6.1 硬件构架
    图 3.6.2 地址分配 B,建立软件工程 一,打开 Nios II IDE,根据刚刚生成的 CPU 来建立系统工程,和实验一一样建立一个简单 的流水灯控制程序; 二,编译此程序,通过后下载到实验板上验证一下; 三,验证成功后,在 IDE 窗口下打开 Tools,点击 Flash Programmer,同样系统会自动检测 电缆; 四,在 Main 栏下,点击 Browse,找到刚刚建立的软件工程,将 FPGA 配置文件下载到 Flash 中的选项选中,将硬件映射选为 user:U5+0XC00000;如下图 3.6.3 所示: (此功能在
    74
    SOPC_EP2C8 板上不可用;在 VA_EP2C35 板上可用)
    图 3.6.3
    Flash Program 设置对换框
    五,在目标连接栏下选中你所使用的电缆,之后点击 Program Flash 进行 Flash 编程; 如果在控制台上有如下输出,则说明下载操作成功:整个过程大概 20 秒钟左右;
    #! /bin/sh # # This file was automatically generated by the Nios II IDE Flash Programmer. # # It will be overwritten when the flash programmer options change. # cd E:/project/ep2c8_v3/release/DEMO/NiosII/flash/software/flash_hello_led/Debug # Creating .flash file for the project $SOPC_KIT_NIOS2/bin/elf2flash --base=0x00000000 --end=0x3fffff --reset=0x0 --inp ut=flash_hello_led.elf --output=cfi_flash.flash --boot=$SOPC_KIT_NIOS2/component s/altera_nios2/boot_loader_cfi.srec # Programming flash with the project $SOPC_KIT_NIOS2/bin/nios2-flash-programmer --base=0x00000000 cfi_flash.flash Using cable "ByteBlasterII [LPT1]", device 1, instance 0x00 Resetting and pausing target processor: OK
    75
    : Checksumming existing contents 00000000 : Reading existing contents
    Checksummed/read 128kB in 5.9s 00000000 ( 0%): Erasing Erased 128kB in 1.2s (106.6kB/s) 00000000 ( 0%): Programming Programmed 1KB +127KB in 4.7s (27.2KB/s) Device contents checksummed OK Leaving target processor paused
    六,将软件下载到 FLASH 后,接下来我们在将 Nios 的硬件 led.pof 文件加载到 EPCS 芯片中. 5,总结: 将系统断电然后上电,系统就能够自动启动了,板上状态灯的使用可以参考第二章第三 节配置电路部分;用户可以自行编写其它程序来验证从 Flash 中加载软,硬件工程;
    第七节,定时器编程
    1,实验目标: A,熟悉 Nios 中定时器的相关设置; B,了解定时器中各寄存器作用; C,在 NiosII EDS 开发环境下对 Timer 进行编程; 2,实验原理: 概述:Sopc Builder 中的定时器是一个 32 位降计数器,在 IDE 软件开发中主要通过向几个相 关的寄存器进行读写操作来控制该定时器. Nios 软件开发控制定时器的操作流程一般包含以下几个步骤: 设置定时器的定时周期; 向定时器的控制寄存器中的 start 位或 stop 位写 1 来开起或停止定时器工作; 向定时器的控制寄存器中的定时中断使能位(ito)写 1 或 0 来使能或禁止定 时器中断; 向定时器的控制寄存器中的 cont 位写 1 或 0 来设置定时器连续工作或单次工
    76
    作 设置定时器的计时周期,主要是分别向定时器的两个 16 位寄存器(periodl)和 (periodh)写入一个 32 位预置数的低 16 位和高 16 位数值. 与定时器相关的寄存器还有两个快照(snapshot)寄存器:snapl 和 snaph.读取这两 个寄存器中的值可以获取定时器内部的向下计数器当前计数值的低 16 位和高 16 位数值. 如 果向 snapl 和 snaph 这两个寄存器进行写操作, 将使这两个寄存器重新装载计数器当前计数 值的高,低 16 位数值. 如果在 SOPC Builder 中设置了定时器模块使用"Watchdog"看门狗配置方式,那么定 时器能够输出一个系统复位信号给系统复位逻辑,如果这时还使用 Writeable period 和 Start/Stop control bits 寄存器功能, 还可以更改看门狗定时器周期以及停止看门狗计时. 其内部结构如下图 3.7.1 所示:
    图 3.7.1 Timer 内部结构图 定时器寄存器定义: 此定时器主要包含 6 个寄存器,它们分别是状态寄存器 status,控制寄存器 control, 周期寄存器 periodl 和 periodh,快照寄存器 snapl 和 snaph.表 3.7.1 列出了这些寄存器的 读写方式及相关说明. 表 3.7.1 定时器寄存器定义 偏移量 0 1 名称 Status Control R/W 15 RW RW
    77
    说明/位描述 ……… 3 stop 2 1 run start cont 0 to ito
    2 3 4 5
    Periodl Periodh Snapl Snaph
    RW RW RW RW
    定时器周期低 16 位 定时器周期高 16 位 定时器内部计数器低 16 位快照 定时器内部计数器高 16 位快照
    1,状态寄存器 status 低 2 位有效.如上表,第 0 位是 to 位,当定时器内部计数器下降计数到 0 时,to 位被置 1.如果使用中断方式操作定时器,那么当定时器定时完成时,to 位为 1 并将一直保持到用户 手动对其清零. Status 寄存器的第 1 位是 run 位,其值为 1 时表示定时器内部计数器正在计数,为 0 时 则表示计数器停止工作.要使定时器重新开始或停止工作(即内部计数器开始或停止计数) , 则需要向 control 寄存器的 stop 或 start 位写 1.Run 位不受 status 寄存器的写操作影响. 2,控制寄存器 control 低 4 位有效.如上表,第 0 位是 ito 位,定时器中断使能时 ito 位置 1,当定时器计数 到 0 将产生一个中断请求信号(定时器的 IRQ 输出为 1) .如果 ito 位置 0,定时器中断禁止, IRQ 输出信号始终为 0. Control 寄存器的第 1 位是 cont 位,当定时器一次定时完成,内部计数器重新装载预置 的定时周期计数.如果 cont 位为 1,则计数器就会继续开始计数,定时器连续不断地循环定 时直到 control 寄存器的 stop 位为 1;如果 cont 位为 0,计数器在重新装载完预置的定时周 期计数后停止工作,定时器完成一次定时后停止. Control 寄存器的第 2 位是 start 位, start 位写 1 后定时器内部计数器立即启动向下 向 计数,定时器开始工作.start 位是一个事件位,即只有向 start 位进行写 1 操作时定时器才 开始工作.向 start 写 0 对定时器没有影响,也就是不能停止其工作.如果在向 start 位写 1 之前定时器已经被停止了,那么此时定时器将接着上次停止的计数值向下计数. Control 寄存器的第 3 位是 stop 位, stop 位写 1 将会停止定时器内部计数器工作, 向 从 而暂停定时.Stop 位也是一个事件位,只由向 stop 写 1 操作时才能停止定时器工作,而向 stop 位写 0 对定时器没有影响. 3,periodl 和 periodh 寄存器 它们分别存放定时器的 32 位预置计数数值的低 16 位和高 16 位. 考虑到定时器内部计数 器工作方式(预置数为 0 算一个时钟周期) ,定时器的实际定时周期要比预置的定时周期多一 个时钟周期.定时器内部计数器在以下两种情况下将重新装载预置的定时周期计数: 1. 用户程序中对 periodl 和 periodh 寄存器进行写操作时; 2. 内部计数器下降到 0 时; 要注意的是,向 periodl 和 periodh 寄存器进行写操作时,定时器内部计数将会自动停 止,因此启动定时器之前应先设置好定时器周期,先设置定时周期再启动定时器. 4,snapl 和 snaph 寄存器 这两个寄存器分别存放定时器内部计数器当前计数值的低 16 位和高 16 位数值.要读取
    78
    当前定时器内部计数器的计数值,应当先向 snapl 和 snaph 两个寄存器之一进行写操作(装 载当前计数值数值) ,然后再读取这两个寄存器中的数值. 当启用看门狗功能的时候,软件一旦开启了定时器,就无法将其结束,当看门狗内部计 数器为 0 时,定时器将产生一个复位信号输出到 Nios 系统复位端.向定时器的 perildl 或 periodh 寄存器进行写操作(写入数据大小无关) ,将对看门狗内部计数器进行复位.因此要 防止看门狗系统复位,就必须连续不断地对 periodl 和 periodh 寄存器进行写操作. 3,实验内容: 设计程序,使系统每隔一秒打印输出一行文本. 4,实验步骤: 此实验要求用户对 Nios 整体开发流程较为熟悉, 如果不熟悉请参考前几个实验, 在此只 讲述关键步骤; 一,生成带有 timer 功能的硬件系统; 二,根据硬件建立软件工程; 三,包含头文件,编写软件代码,主要代码如下 这样就能在 Stdout 设备上每隔 1S 输出一次文本; 5,实验总结 要想获得 Timer Core 的更详细信息,请查看其 Datasheet(在 SOPC Builder 中右键单击 此模块就可以了解此模块的详细信息) ;
    #include "alt_types.h" #include #include "system.h" #include "sys/alt_irq.h" #include "altera_avalon_timer_regs.h" #define alt_cpu_freq 85000000 int main(void) { int t = 0; //计数变量 //设置定时周期 IOWR_ALTERA_AVALON_TIMER_PERIODL(TIMER_0_BASE, (short)(alt_cpu_freq&0x0000ffff)); IOWR_ALTERA_AVALON_TIMER_PERIODH(TIMER_0_BASE, (short)((alt_cpu_freq>>16)&0x0000ffff)); //开启定时器,并设定为循环工作 OWR_ALTERA_AVALON_TIMER_CONTROL(TIMER_0_BASE, ALTERA_AVALON_TIMER_CONTROL_START_MSK + ALTERA_AVALON_TIMER_CONTROL_CONT_MSK); while(1) { if( IORD_ALTERA_AVALON_TIMER_STATUS(TIMER_0_BASE) &
    79
    ALTERA_AVALON_TIMER_STATUS_TO_MSK ) { // 1s 定时完成,输出文本 printf("One second passed! (%d)\n",t++); //向状态寄存器执行写操作,清除定时器溢出状态 IOWR_ALTERA_AVALON_TIMER_STATUS(TIMER_0_BASE, 0x0); } } return 0; }
    80
    第八节,I2C Controller IP Core的使用
    1,实验目标: A,简单了解 IP Core 的工作原理; B,熟悉 I2C IP Core 的使用; C,简单了解驱动程序代码; D,扩展应用; 2,实验原理: 此 IP Core 是从 Opencore 网站上下载的 Wishbone 总线协议核, 经过简单修改再使用 SOPC Builder 将其转换为 Avalon 总线设备(component) ,最后通过编写接口函数达到再 NIos IDE 开发环境中使用 C 语言控制的目的. 此核的内部详细结构,可以通过其说明文档进行了解,其内部结构如下图 3.8.1 所示:
    时钟产生 模块
    时钟设置 寄存器 命令 寄存器 状态 寄存器 数据传输 寄存器 数据接收 寄存器
    此 IP Core 内部的各寄存器可以通过 Avalon 总线进行访问,用户可以通过软件程序(C 语言)设置其工作速度模式,设置其工作方式等;下面分别简单描述各寄存器及模块的功能: (1) ,时钟设置寄存器:用来设置通讯速率,16 位,其值与系统工作频率及 I2C 总线速 度有关, 如系统速度为 75MHz, I2C 的通讯速度为 100KHz, 则时钟设置寄存器的值为 prescale = (75000000/(5*100000)-1). (2) ,时钟产生模块:时钟产生模块产生 4 倍 SCL 频率的时钟信号,它为位传输控制模块
    A V A L O N I N T E R F A C E
    字节传输 控制模块
    字节传输 控制模块
    数据移位 寄存器
    图 3.8.1 I2C IP Core 内部结构框图
    81
    中所有同步动作提供触发信号. (3) ,命令寄存器:命令寄存器决定是否在总线上产生各种时许信号,是否读/写数据等. (4) ,状态寄存器:状态寄存器用来显示当前总线的状态,例如是否接收到从节点的应答 信号,是否忙,是否在传递数据等. (5) ,数据传输寄存器:数据传输寄存器用于保存等待传输的数据.当传递从节点地址信 息时,前 7 位保存从节点地址,最后一位保存读写命令;当传递普通数据时,8 位保存一个字 节数据. (6) ,数据接收寄存器:用于保存通过 I2C 总线接收到的最后一个字节内容. (7) ,字节传输控制模块:用于以字节为单位控制 I2C 总线的数据传输.这个模块按照命 令寄存器设置的内容将数据传输寄存器的内容传递到 I2C 总线的接收端,或者从 I2C 总线发送 端接收数据并保存到数据接收寄存器中. (8) ,位传输控制模块:以位为单位进行 I2C 总线的数据传输和产生 I2C 协议命令(如开 始,停止,重复开始等) .字节传输控制模块控制位传输控制模块的各种动作.例如读取一个字 节数据,位传输控制模块要执行 8 个读的命令. ,数据移位寄存器:数据移位寄存器保存的数据总是和当前的数据传输相关的.例如 (9) 在进行读操作时,主节点通过移位寄存器依次通过 SDA 获得来自 I2C 发送端的数据,完成后数 据拷贝到数据接收寄存器中. 在写操作时, 数据传输寄存器中的数据拷贝到数据移位寄存器中, 然后依次通过SDA将数据传输到 I2C 总线的接收端. 用户可以不用了解核内部的具体结构,只用知道如何使用此 IP Core 就可以了.用户可以 查看\oc_i2c_master\inc 文件夹下的 oc_i2c.h 及 oc_i2c.c 接口及驱动文件了解其详细情况. 3,实验步骤: (1) ,将 oc_iic_controller IP 核放入\altera\quartus60\sopc_builder\components 文件夹下,这样在打开 SOPC Builder 窗口的时候,就会看在左边的元件池中看到此 IP Core; (2) ,在上一个试验的基础上再加入此 IP Core,生成硬件文件并下载到 FPGA 中; (3) ,打开 Nios II IDE 开发环境,建立工程,代码如下(在附带的 IP Core 中有实例,包括软 硬件工程,用户可自行参考) #include "i2c_ep2c8.h" int main() { int i=0; char data='f';unsigned char printf("Hello I2C!\n"); init_i2c(); printf("Initial the i2c device\n"); //i2c_write(0x50, 0x20, 0x16); //printf("What you input is 0x16\n"); data=i2c_read(0x50, 0x22);
    82
    data2='t';
    data2=i2c_read(0x50, 0x20); printf("%d,%c\n",data2,data2); printf("%d,%c\n",data,data); FILE * lcd; #ifdef LCD_DISPLAY_NAME lcd = fopen("/dev/lcd_display", "w"); fprintf(lcd, "%c%s %c%s %c%s Waiting...\n", ESC, ESC_TOP_LEFT, ESC, ESC_CLEAR, ESC, ESC_COL1_INDENT5); fprintf(lcd, "%c%s %d,%c\n", ESC, ESC_COL2_INDENT5, data,data); #endif while(i<255) { data2=i2c_read(0x50,i); printf("Address%d is %d\n",i,data2); i++; } return 0; }
    83
    第四章:基于HAL的设备控制
    HAL(Hardware abstract layer)系统库是一个轻量级的运行时间环境,为编程与底层硬件 进行通信提供了一个简单设备驱动程序接口.HAL 应用编程接口(API)集成在 ANSI C 的标准库里, 可调用常用的 C 库函数访问设备和文档,例如 printf(),fopen(),fwrite()等等. HAL 充当 Nios II 处理器系统的支持板封装(board_support package),为嵌入式系统外设提 供一个兼容的接口.SOPC Builder 与 Nios II 集成开发环境(IDE)的紧密综合可以自动生成 HAL 系 统库.SOPC Builder 生成硬件库之后,Nios II 的 IDE 随之生成与该硬件配置对应的自定义 HAL 系统库;此外,硬件配置里的变化会自动传递影响到 HAL 设备驱动程序配置,而且消除由于底层硬 件的微小变化引起的程序缺陷(bugs). HAL 设备驱动程序抽象在应用程序和设备驱动程序软件之间划出了一条清晰的界限. 该驱动程 序抽象提高了应用程序代码的重用性, 有效的抵抗底层硬件的变化; 此外对与现有外设设备相兼容 的新硬件外设进行写操作是非常容易的. HAL 系统库提供以下的服务: 集成 newlib ANSI C 标准库—提供常用 C 标准库函数 设备驱动程序—可访问系统里的任意设备 HAL API—为 HAL 服务提供兼容的标准接口,例如设备接入,中断处理以及警报设备. 系统初始化—在 main()主函数前执行处理器和运行环境的初始化任务. 设备初始化—在 main()主函数前示例和初始化系统里的每一装置. 基于 HAL 的系统层,从硬件级层到用户程序层,如下图所示 3-1 所示:
    84
    可支持的外设
    Altera 为 Nios II 处理器系统提供很多可应用的外设.大部分 Altera 外设提供 通过 HAL API 访问硬件的 HAL 驱动.以下的 Altera 外设提供完全的 HAL 支持: 字符模式设备: UART 核 JTAG UART 核 LCD 16207 显示控制器 闪存器件 公共 flash 接口,适应于 flash 芯片 Altera 的 EPCS 串行配置设备控制器 文件子系统 只读 zip 文件系统 定时器设备 定时器核 DMA 设备 DMA 控制核 以太网设备 LAN91C111 以太网 MAC/PHY 控制器(需要工作在操作系统中)
    第一节,文件系统
    HAL 提供操作字符模式设备和数据文件的文件系统的概念.可以在该文件系统里通过使用 newlib(例如 fopen(),fclose(),fread()等函数)提供的 C 标准库文件 I/O 函数,或使用 HAL 系 统库提供的 UNIX 类型文件 I/O 函数对文件进行访问.
    85
    HAL 为文件操作提供以下的 UNIX 类型函数:详细信息请参考第十章 close() fstat() ioctl() isatty() lseek() open() read() stat() write() 文件子系统在 HAL 文件系统中作为挂载点, 如果访问在挂载点下的文件, 可以直接访问该文件 子系统.例如如果 zip 文件子系统挂在/mount/zipfs0,则可以调用 fopen()函数访问与 zipfs 文 件子系统下的相关文件 myfile,路径为/mount/zipfs0/myfile. 类似地,字符模式设备在 HAL 文件系统中作为节点.惯例上 system.h 文件在 SOPC builder 的硬件器件名称前加前缀/dev/来定义设备节点地.例如,SOPC builder 中的 UART 外设 uart1 在 system.h 中用/dev/uart1 表示. 没有当前目录的概念, 访问所有文件都要绝对路径. 下面的代码是只读 zip 文件子系统 rozipfs 读取字符的程序,rozipfs 在 HAL 文件系统中作为节点. 例子:从文件子系统读字符 #include #include #include #define BUF_SIZE (10) int main(void) { FILE* fp; Char buffer[BUF_SIZE]; fp=fopen("/mount/rozipfs/test","r"); if(fp==NULL) { Printf ("Cannot open file.\n"); Exit (1); } fread (buffer, BUF_SIZE, 1, fp); fclose (fp); return 0; }
    第二节,字符设备
    字符模式设备是指串行发送和接收字符的硬件外设,例如通用异步收发机(UART).在 HAL 文件
    86
    系统里字符模式设备作为节点.通常程序为设备名提供文件描述符,然后通过使用 file.h 定义的 ANSI C 文件操作符对字符进行写读操作.HAL 也支持标准输入输出和标准错误输出的概念,允许 在程序中调用 stdio.h I/O 函数. 标准输入,标准输出&标准错误输出 实现简单的 I/O 控制台的最简单的方法就是使用标准输入(stdin),标准输出(stdout),标准 错误输出(stderr).HAL 系统库可以在幕后管理 stdin,stdout 和 stderr,这样不用明确操作文件 描述符就可以通过信道进行字符的发送和接收.例如,系统库用 printf()的输出作为标准输出, 用 perror()作为标准错误输出.下面的代码是经典的 Hello World 程序.在 Nios II IDE 编译后 该程序会向调用 stdout 函数的设备发送字符. 例子:Hello World #include Int main () {printf ("Hello World!"); return 0; } 当使用 UNIX 类型的 API 时,可使用下面的文件描述符:STDIN_FILENO,STDOUT_FILENO 和 STDERR_FILENO,在 unistd.h 中分别访问 stdin,stdout 和 stderr. 一般访问字符模式设备 访问字符模式设备(除了 stdin,stdout 和 stderr 之外)跟打开文件和写文件一样容易.下面 的代码就是向名为 uart1 的 UART 进行写信息的代码. 例子:向 UART 进行写字符 #include #include int main (void) {char* msg= "hello world"; FILE* fp; fp= fopen ("/dev/uart1","w"); if (fp) { fprintf(fp, "%s", msg); fclose (fp); } return 0; }
    87
    /dev/null 所有系统都包含设备/dev/null. 对/dev/null 进行写操作是无效的,数据会丢失./dev/null 在系统启动时作为安全 I/O 重定向.在应用中,可以对不必要的数据进行此设备操作.该设备纯粹 是软件构造,与系统的物理硬件是没有任何联系的.
    第三节,时间设备
    定时器是计算时钟 ticks 以及产生周期中断请求的硬件外设, 可供与时间有关的设备使用, 例 如 HAL 系统时钟,警报,time-of-day 和时间度量.Nios II 处理器系统必须有定时器硬件才能应 用定时器设备. HAL API 提供两种定时器驱动:允许警报设备的系统时钟驱动和高分辨率时间度量的时间标记 驱动.特殊的定时器外设可以表现为其中的一种,但不能同时代表这两种. HAL 提供以下可以执行的标准 UNIX 函数:gettimeofday(),settimeofday()和 times().作 为访问定时器的 HAL 专用 API 函数定义在 sys/alt_alarm.h 和 sys/alt_timestamp.h 里. HAL 系统时钟 HAL 系统时钟设备提供周期性的"心跳" ,每一跳动引起系统时钟的增加.系统时钟设备在指 定的时间里执行函数,从而获得定时信息.可以在 Nios II IDE 里设置系统库属性将特定的硬件 定时器外设作为系统时钟设备. 系统时钟用单位 tick 度量时间.对于软硬件都涉及的嵌入工程师,是不会混淆 HAL 系统时钟 与同步于 Nios II 处理器硬件的时钟信号的.HAL 系统时钟周期比硬件系统时钟要长. 通过调用 alt_nticks()函数可以获得系统时钟当前值.该函数返回从复位到现在系统时钟的 消逝时间.调用 alt_ticks_per_second()可以得到系统时钟速率,ticks/s.当创建系统时钟实例 时 HAL 定时器对 tick 频率初始化. 标准 UNIX gettimeofday()函数可以获得当前时间.不过首先要调用 settimeofday()校准时 间,此外调用 times()函数可获得逝去 ticks 的信息.这些都定义在 time.h 里. 报警设备 可以在特定时间内应用 HAL 报警设备执行登记了的函数. 调用 alt_alarm_start()函数登记报 警设备. int alt_alarm_start ( alt_alarm* alarm, alt_u32
    88
    nticks,
    alt_u32 (*callback) (void* context), void* context); 在逝去的 nticks 之后调用 callback 函数. 当调用 callback 函数时, 输入参数 context 作为 callback 函数的输入参数. 输入参数 alarm 指向的结构通过调用 alt_alarm_start()函数进行初始 化,故不必在对其初始化了. callback 函数对报警设备复位,返回到下一个调用该函数为止之间的 ticks 数量值.返回值 为 0 表示停止报警,不过可以调用 alt_alarm_stop()函数手动停止报警. 下面的代码段是关于每秒进行周期 callback 时如何登记 alarm. 例子:使用周期的 Alarm Callback 函数 #include #include #include "sys/alt_alarm.h" #include "alt_types.h" /* * callback 函数 */ alt_u32 my_alarm_callback (void* context) {/*每秒一次调用该函数*/ Return alt_ticks_per_second(); } … /*alt_alarm 必须保持报警设备的占空比*/ Static alt_alarm alarm; … If (alt_alarm_start(&alarm, Alt_ticks_per_second(), My_alarm_callback, NULL)<0) {printf ("No system clock available\n");} 高分辨率的时间度量 有时需要测量比 HAL 系统时钟 ticks 精确度更高的时间间隔. HAL 利用时间标记设备提供高分 辨率的定时函数.时间标记设备是单调递增计数器,故可抽样得到定时信息.在系统中 HAL 只支持 一个时间标记设备. 当时间标记设备存在时,alt_timestamp_start()和 alt_timestamp()函数才能使用.Altera 提供的时间标记设备利用定时器,而定时器可以在 Nios II IDE 的系统库属性里选择. 调用 alt_timestamp_start()函数启动计数器运行. 接着调用 alt_timestamp()函数返回时间 标记计数器的当前值.然后再调用 alt_timestamp_start()使计数器复位为 0.没定义当计数器到 达(2^32-1)时时间标记设备的状态. 调用 alt_timestamp_freq()函数可以获得时间标记计数器增加的速率.该速率是典型的硬件
    89
    频率, Nios II 处理器运行在此频率下, 通常为每秒百万个周期. 时间标记设备在 alt_timestamp.h 的头文件里定义的. 下面的代码段是关于如何用时间标记设备测量代码执行时间. 例子:使用时间标记设备测量代码执行时间 #include #include "sys/alt_timestamp.h" #include "alt_types.h" int main (void) { alt_u32 time1; alt_u32 time2; alt_u32 time3; if (alt_timestamp_start () <0) {printf ("No timestamp device available\n"); } else {time1 = alt_timestamp(); func1();/*函数 1 监控*/ time2 = alt_timestamp(); func2();/*函数 2 监控*/ time2 = alt_timestamp(); printf ("time in func1 = %u ticks\n", (unsigned int) (time2-time1)); printf ("time in func2 = %u ticks\n", (unsigned int) (time3-time2)); printf ("Number of ticks per second = %u\n", (unsigned int) alt_timestamp_freq()); } return 0; }
    第四节,Flash设备
    HAL 为非易失性闪存提供普通设备模式.闪存用专门的可编程协议存储数据.HAL API 提供函
    90
    数向闪存写数据.例如可用这些函数执行基于闪存的文件子系统. HAL API 还提供函数向闪存读数据,尽管不是必需的.对大部分闪存来说,当进行读数据时,程序 将闪存空间看作简单的存储器,不需要调用专用的 HAL API 函数.如果闪存读数据时需要专门的 协议,例如 Altera 串行配置器件 EPCS,必需使用 HAL API 进行读写数据. 本节为闪存设备模式描述 HAL API.以下的两个 APIS 提供不同等级的访问闪存的方法: 简单闪存访问—简单的 API,可向闪存写读缓存,不用保护其它闪存擦除模块先前的 内容. 高级闪存访问—程序的高级函数,对个别模块写操作和擦除操作具有控制权.该功能 在管理文件子系统中通常很需要的. 可访问闪存的 API 函数是在 sys/alt_flash.h 中定义的. 简单闪存访问 接 口 包 含 : alt_flash_open_dec() , alt_write_flash() , alt_read_flash() 和 alt_flash_close_dev().Page4-13 关于"例子:使用简单的闪存 API 函数"的代码展示在一个 代码实例中使用了所有的函数.可调用 alt_flash_open_dec()函数打开闪存,返回一个处理闪存 设备的文件.该函数有一个单参数.该单参数是闪存设备的名称,在 system.h 中定义的. 一旦获得一个句柄,可以调用 alt_write_flash()函数对闪存进行写数据操作.用法如下: int alt_write_flash(alt_flash_fd* fd, int offset, const void* src_addr, int length) 调用此函数对闪存进行写操作,而闪存是通过句柄 fd,offset 字节对闪存识别的.写进去的 数据的地址是由 src_addr 指针指向的,length 表示写进数据的数量. alt_read_flash()函数从闪存里读数据.用法如下: int alt_read_flash (alt_flash_fd* fd, int offset, void* dest_addr, int length) 调用此函数对闪存进行读操作,闪存是通过句柄 fd,offset 字节对闪存识别的.dest_addr 指向写进去的数据的位置,length 表示读数据的数量.对大部分闪存来说,可以以标准存储器方 式访问内容,而不需要调用 alt_read_flash()函数. Alt_flash_close_dev()函数关闭设备的,有一个句柄,用法如下: void alt_flash_close_dev(alt_flash_fd* fd) 下面的代码关于用简单的闪存 API 函数访问名为/dev/ext_flash 的闪存设备,定义在
    91
    system.h 中. 例子:使用简单闪存 API 函数 #include #include #include "sys/alt_flash.h" #define BUF_SIZE1024 int main () {alt_flash_fd* fd; int ret_code; char source[BUF_SIZE]; char dest[BUF_SIZE]; /*初始化源缓存为 0xAA*/ memset(source, 0xa, BUF_SIZE); fd = alt_flash_open_dev("/dev/ext_flash"); if (fd) { ret_code = alt_write_flash(fd, 0, source, BUF_SIZE); if (!ret_code) { ret_code = alt_read_flash(fd, 0, dest, BUF_SIZE); if (!ret_code) {/* *成功. *此时,闪存全为 0xa,而且应该已经读到 dest 里 */ } } alt_flash_close_dev(fd); } else { printf("Can't open flash device\n"); } return 0; } 模块擦除或丢失 闪存一般划分为几块. 向块进行写数据之前需要擦除块里的内容. 在这种情况下并没有保护块 现有的内容.如果不是将数据写在块的边界,那写操作会引起意想不到的数据丢失(擦除).如果要 保护现有的闪存内容,那使用细颗粒闪存函数,详细请参考 page4-15 的"细颗粒闪存访问" . 表 4-3 显示了使用简单闪存访问函数进行写操作时引起的数据丢失情况.表 4-3 的例子里 的 8K 字节闪存分为 2 个 4K 字节块,首先向首地址为 0x0000 的闪存写入 5K 字节全 0xAA 的数据, 然后向地址 0x1400 写入 2K 字节全 0xBB 的数据.在第一次写成功后(时刻 t(2)),闪存为 5K 字节
    92
    全 0xAA 数据,其它空间为空(例如为 0xFF);接着在第二次开始写数据但还没写数据到块二时,块 已经被擦除了.即在 t(3)的这个时刻,闪存为 4K 字节全 0xAA 和 4K 字节全 0xFF;在第二次写操作 完成后,即 t(4)时刻,地址为 0x1000 的 2K 字节全 0xFF 丢失了. 表 4-3.写闪存以及引起不必要数据丢失的例子 时刻 t(0) 时刻 t(1) 时刻 t(2) 时刻 t(3) 时刻 t(4) 地址 模块 第一次写 第一次写操作 第二次写操作 数据之前 擦除模块 写入数据 擦除模块 写入数据 之后 1 之后 之后 2 之后 0x0000 1 FF AA AA AA 0x0400 1 FF AA AA AA 0x0800 1 FF AA AA AA 0x0c00 1 FF AA AA AA 0x1000 2 FF AA FF FF(1) 0x1400 2 FF FF FF BB 0x1800 2 FF FF FF BB 0x1c00 2 FF FF FF FF 注:(1)在第二次写操作的过程中被擦除,变为 FF. 细颗粒闪存访问 下面提供对向最小颗粒间距的闪存写数据进行完全控制的三个附加的函数: alt_get_flash_info(),alt_erase_flash_block()和 alt_write_flash_block(). 由于闪存的特点,不能在块内对某一地址进行擦除,擦除必须擦除整块(例如设为全 1).向闪 存进行写操作只能将位从 1 变为 0;若要将位从 0 变 1,只能将整块进行擦除操作.因此,若要改 变块内某一位置而保持周围内容不变,需要将块内的所有内容读到缓冲器里,改变缓冲器的值,擦 除闪存块, 然后将整个块大小的缓冲写回闪存里. 细颗粒闪存访问函数允许进行上面的闪存块级的 操作. alt_get_flash_info()函数可获得以下信息: 擦除区域的数目, 每个区域被擦除块的数目和每块的 大小.用法如下: Int alt_get_flash_info(alt_flash_fd* fd, flash_region** info, int* number_of_regions)
    如果调用成功的话,number_of_regions 指向的地址返回值为闪存被擦除区域的数目.指针 info 指向第一次描述的 flash_region. flash_region 结构是在 sys/alt_flash_types.h 里定义的,用法如下: typedef struct flash_region { int offset;/*从闪存的开始到此区域的偏移量*/ int region_size;/*区域的大小*/ int number_of_blocks;/*该区域块的数目*/ int block_size;/*每块的大小*/
    93
    }flash_region; 通过调用 alt_get_flash_info()获得信息,可以对闪存单独的块进行擦除或编程操作. alt_erase_flash()可擦除闪存里的单块.用法如下: int alt_erase_flash_block(alt_flash_fd* fd, int offset, int length) fd 作为识别闪存的句柄,offset 从闪存的开始作为识别块的标志,length 表示块的大小. alt_write_flash_block()对闪存单块进行写操作,用法如下: int alt_write_flash_block(alt_flash_fd* fd, int block_offset, int data_offset, const void *data, int length) 该函数由句柄 fd 识别闪存, block_offset 从闪存的开始作为对块进行写操作的标志, 函数将 length 字节的数据从 data 所指向的地址写到 data_offset 字节的位置. 这些编程和擦除函数不执行地址的检查,也不检验写操作是否跨过下一块,故必须传递有效 的信息进行块的编程或擦除操作. 下面的代码是应用细颗粒闪存访问函数的示例. 例子:应用细颗粒闪存访问 API 函数 #include #include "sys/alt_flash.h" #define BUF_SIZE 100 int main (void) { flash_region* regions; alt_flash_fd* fd; int number_of_regions; int ret_code; char write_data[BUF_SIZE]; /*设置 write_data 为全 0xa*/ memset(write_data, 0xA, BUF_SIZE); fd = alt_flash_open_dev(EXT_FLASH_NAME); if (fd) {ret_code = alt_get_flash_info(fd, ®ion, &number_of_regions); if (number_of_regions && (region->offset == 0)) {/*擦除块一*/ ret_code =alt_erase_flash_block(fd, regions->offset, regions->block_size);
    94
    if (ret_code) { /*从 write_data 向闪存块一 *写 BUF_SIZE 字节 */ ret_code = alt_write_flash_block(fd, regions->offset, regions->offset+0x100, write_data, BUF_SIZE); } } } return 0; }
    第五节,DMA的使用
    HAL 为直接存储器存取(DMA)提供设备抽象模式.这些外设对大量数据从数据源到终端进行处 理.源端和终端可以是存储器或其它设备,例如以太网接入. 在 HAL DMA 设备模式下,DMA 事务分为两类:发送和接收.因此 HAL 提供两种驱动执行发送信 道和接收信道. 发送信道将数据放在源端缓冲器里, 然后传输到终端; 而接收信道从装置接收数据, 并将数据放在终端缓冲器里. 虽然软件依赖于底层硬件的执行, 但可以访问这两个端点的其中之一. DMA 基本的传输模式如图 4-2 所示.将数据从一存储器拷贝到另一存储器必须经过接收和发 送 DMA 信道.
    95
    在 sys/alt_dma.h 中定义访问 DMA 设备的 API.DMA 对物理存储器的内容进行操作,因此在读 写数据时,要考虑高速缓存的影响, DMA 发送信道 进 行 DMA 发送时,首先要通过句柄排队向 DMA 发送器发送传输请求.句柄通过函数 alt_dma_txchan_open()获得.该函数有单参数,用设备的名字命名,在 system.h 里定义的. 下面的代码关于如何获取 DMA 发送设备 dma_0 的句柄. 例子:获取 DMA 设备的文件句柄 #include #include "sys/alt_dma.h" int main (void) { alt_dma_txchan tx; tx = alt_dma_txchan_open ("/dev/dma_0"); if (tx == NULL) { /*Error*/ } else
    96
    { /*Success*/ } return 0; } 可以调用 alt_dma_txchan_send()函数运用该句柄提出发送请求.用法如下: typedef void (alt_txchan_done) (void* handle); int alt_dma_txchan_send (alt_dma_txchan dma, const void* from, alt_u32 length, alt_txchan_done* done, void* handle); 调用 alt_dma_txchan_send()函数向信道 dma 发送传输请求,从 from 指向的地址开始传输 length 字节的数据,当所有 DMA 事务完成时,函数返回.返回值指出请求是否成功排队.负的返 回值表示请求失败.当事务完成时,调用参数为 handle 的支持用户函数 done 表示确定. 操 作 DMA 传 输 信 道 还 提 供 额 外 的 两 个 函 数 : alt_dma_txchan_space() 和 alt_dma_txchan_ioctl().其中 alt_dma_txchan_space()函数返回另外的排队向设备发送请求的 数目.而 alt_dma_txchan_ioctl()执行特别的传输设备的操作. 若要用 Altera Avalon DMA 设备与硬件进行传输(非存储器-存储器传输),可调用 alt_dma_txchan_ioctl() 函 数 , 并 将 请 求 参 数 设 置 为 ALT_DMA_TX_ONLY_ON( 请 查 阅 page10-17 的"alt_dma_txchan_ioctl()") DMA 接收信道 DMA 接收信道工作方式与 DMA 发送信道相似. 调用 alt_dma_rxchan_open()函数获取 DMA 接收 信道的句柄, 然后使用 alt_dma_rxchan_prepare()函数提出接收请求, alt_dma_rxchan_prepare() 函数的用法如下: Typedef void(alt_rxchan_done)(void* handle, void* data); Int alt_dma_rxchan_prepare(alt_dma_rxchan void* alt_u32 alt_rxchan_done* void* dma, data, length, done, handle);
    调用该函数向信道 dma 提出接收请求, data 所指的地址开始放 length 字节的数据. 在 当所有 DMA 事务完成时,函数返回.返回值指出请求是否成功排队.负的返回值表示请求失败.当事务完 成时,调用参数为 handle 的支持用户函数 done 表示确定以及指针指向接收的数据. 操 作 DMA 接 收 信 道 还 提 供 额 外 的 两 个 函 数 : alt_dma_rxchan_space() 和
    97
    alt_dma_rxchan_ioctl(). 若要用 Altera Avalon DMA 设备与硬件进行接收(非存储器-存储器传输)时,可调用 alt_dma_rxchan_ioctl()函数,并将请求参数设置为 ALT_DMA_RX_ONLY_ON(请查阅 page10-11 的 "alt_dma_rxchan_ioctl()"). alt_dma_rxchan_depth() 返 回 值 为 排 队 向 设 备 提 出 接 收 请 求 的 数 目 的 最 大 值 . alt_dma_rxchan_ioctl()执行特别的接收设备的操作. 下面的代码关于提出 DMA 接收请求的完全实例应用程序,执行 main()直到接收完毕. 例子:DMA 接收操作
    #include #include #include #include "sys/alt_dma.h" #include "alt_types.h" /*使用标记提示事务的完成*/ volatile int dma_complete =0; /*当事务完成后调用函数*/ void dma_done (void* handle, void* data) {dma_complete = 1; } int main (void) {alt_u8 buffer[1024]; alt_dma_rxchan rx; /*获取句柄*/ if((rx = alt_dma_rxchan_open("/dev/dma_0"))==NULL) {pringtf ("Error: failed to open device\n"); exit (1); } else {/*提出接收请求*/ if(alt_dma_rxchan_prepare(rx,buffer,1024,dma_done,NULL)<0) { printf("Error:failed to post receive request\n"); exit(1); }/*等待到事务完成*/ while(!dma_complete); printf("Transaction complete\n");
    alt_dma_rxchan_close(rx); } return 0; } 存储器-存储器 DMA 操作 将数据从一存储器缓存拷贝到另一存储器缓存涉及到接收和发送 DMA 设备. 下面的代码关于发
    98
    送接收请求后提出传输请求从而实现存储器-存储器 DMA 的传输 例子:将数据从一存储器拷贝到另一存储器
    #include #include #include "sys/alt_dma.h" #include "system.h" static volatile int rx_done = 0; /* * Callback function that obtains notification that the data has * been received. */ static void done (void* handle, void* data) { rx_done++; } int main (int argc, char* argv[], char* envp[]) { int rc; alt_dma_txchan txchan; alt_dma_rxchan rxchan; void* tx_data = (void*) 0x901000; /* pointer to data to send */ void* rx_buffer = (void*) 0x902000; /* pointer to rx buffer */ /* Create the transmit channel */ if ((txchan = alt_dma_txchan_open("/dev/dma_0")) == NULL) { printf ("Failed to open transmit channel\n"); exit (1); } /* Create the receive channel */ if ((rxchan = alt_dma_rxchan_open("/dev/dma_0")) == NULL) { printf ("Failed to open receive channel\n"); exit (1); } /* Post the transmit request */ if ((rc = alt_dma_txchan_send (txchan, tx_data, 128, NULL, NULL)) < 0) { printf ("Failed to post transmit request, reason = %i\n", rc);
    99
    exit (1); } /* Post the receive request */ if ((rc = alt_dma_rxchan_prepare (rxchan, rx_buffer, 128, done, NULL)) < 0) { printf ("Failed to post read request, reason = %i\n", rc); exit (1); } /* wait for transfer to complete */ while (!rx_done); printf ("Transfer successful!\n"); return 0; }
    第六节,只读文件子系统
    HAL 普通设备模式允许应用标准 C 库的 I/O 函数对存储在联合的媒体里的数据进行访问.例 如 Altera 的只读 zip 文件提供对存储在闪存里的文件可以进行只读访问. 文件子系统在已知的挂载点下对所有 I/O 访问进行操作.例如,如果文件子系统挂在 /mnt/rozipfs 下 , 那 所 有 在 该 目 录 下 的 文 件 访 问 都 可 以 直 接 访 问 该 文 件 子 系 统 , 例 如 fopen("/mnt/rozipfs/myfile","r"). 与字符模式设备相似,在文件子系统中使用 file.h 定义的 C 文件 I/O 函数对文件进行操作, 例如 fopen()和 fread().查阅"HAL API 参考"可以得到更多的应用这些函数的信息. 使用方法:只读文件子系统在 Nios II IDE 图形用户窗口下使用,用户不用编辑它的源代码,用 户只需要在 IDE 下像包含元件一样包含它既可,用户必须要指定如下参数来配置文件子系统: 指定文件子系统所要放入的 Flash 元件的名称 Flash 的偏移量 在 HAL 文件系统中文件子系统的挂载点的名称,例如:如果文件子系统挂在 /mnt/rozipfs 下, 那所有在该目录下的文件访问都可以直接访问该文件子系统, 如 fopen("/mnt/rozipfs/myfile","r"). 你想使用 ZIP 文件的名称, 在用户使用文件子系统之前, 必须将它导入 到 NIOS II IDE 系统库工程中.
    100
    第五章:软硬件协同设计实例讲解
    101
    Nios设计精通篇 第六章:Nios II Flash Programmer
    第一节,在SOPC Builder下定制目标板:
    定制目标板主要的任务是在 NIOS II IDE 环境下用 Flash Programmer 对 Flash,EPCS 器件进 行编成控制, 包括硬件配置数据存储在用户区域或是安全区域的地址设置等; 所以只针对这一功能 作详细介绍 步骤: 1, 使用 OrCad 原理图绘制工具将你的 PCB 板的原理图生成 wirelist.dll 文件, 如果不能生成 也没关系,可以复制其他板子的 wirelist.dll 文件,将其内容更改如下: <<>> AM29LV128D EPCS16 EP2C35F672C8 U5 U59 U62 TSOP56 SOIC16P220W 672FBGA
    此文件是针对"威龙"核心板做的更改,可以更具不同的目标板,不同的元件标示,封装等作相应 的更改; 2,打开 SOPC Builder 工具,点击 File 下拉列表下的 New Board Description,再点击 Next, 到如下图 6.1.1 所示窗口:
    102
    图 6.1.1 导入 Netlist 文件窗口 3, 点击 Netlist 栏下的 Browse, 找到刚刚制作的 wirelist.dll 文件, 系统会自动辨认出 Fpga 器件,以及 Fpga 器件的系列;再点击 Next; 4,在 Flash Memorie 中添加 Flash,分别添加 U5(CFI Flash)和 U59(EPCS) ,如下图所示: 5,在 Hardware Images 中分别设置硬件配置数据存储地址,设置后如下图 6.1.2 所示:
    图 6.1.2 设置 Flash 6,切换到 Files 列表,对文件命名并保存,如下图 6.1.3 所示:
    103
    图 6.1.3 保存文件
    第二节,NIOS II闪存编程器的使用
    许多对 Nios处理器的设计采用有闪存存储器的试验板,闪存用来存储 FPGA 配置和/或者 NiosII 程序.NiosII 开发套件包含一种方便的烧写闪存的方法.任何接到 FPGA 上的,适应通用闪 存接口(CFI)的闪存设备都可以通过 NiosII 的综合开发环境(IDE)的闪存编程器来烧写.
    CFI 是一个闪存接口规范,它对不同卖家出售的闪存设备提供一个通用接口.只要闪存符合这个规范,它 就可以被一条有某些具体参数的特殊的指令查询,然后正常的进行访问.通过这个过程,NiosII IDE 闪存编程 器就可以烧写任意类型的内容到任意单个 CFI 适应闪存设备,或者多个分支设备. 作为 CFI 闪存的附加功能,NiosII IDE 闪存编程器也可以烧写任意的接到 FPGA 上的 Altera的 EPCS 串行 配置器件. 这一章包含三部分主要内容: 1. NiosII 闪存编程器概述:这一部分描述闪存编程器的设计,元件,和常规选项. 2. 使用 NiosII 闪存编程器烧写闪存:这一部分指导你使用 NiosII IDE 烧写闪存的步骤. 3. 在 SOPC Builder 系统中初始化闪存:这一部分讨论使用 SOPC Builder 在一个新的硬件系统中例化闪 存以便可以通过 NiosII IDE 闪存编程器来烧写闪存. 如果你只需要通过 NiosII 的闪存编程器来烧写 Altera Nios 开发板上的闪存,参考 1-7 页的"烧写内 容到闪存" .这一章的内容是闪存编程器操作的背景,同时也是第二章 NiosII 闪存编程器与用户板的接口 的前言叙述.
    104
    NiosII 闪存编程器概述
    这一部分是有关 NiosII 闪存编程器的概述,主要内容包括: NiosII 闪存编程器是如何工作的 闪存编程器设计 目标板 闪存内容的分类 引导选项
    NiosII 闪存编程器是如何工作的
    闪存编程器通过两个步骤将数据烧写进闪存.第一步将一个具体的闪存编程器设计配置到 FPGA.一旦配置 FPGA 成功,闪存编程器启动(主机上运行的)收集适当的闪存内容文件并且把他们发送到 FPGA 上运行的闪存编 程器设计.然后闪存编程器设计将这些内容烧写进 flash.见图 6.2.1 和图 6.2.2.
    图 6.2.1 为步骤 1,闪存编程器成功的将闪存编程器设计下载到 FPGA.
    图 6.2.2 为步骤 2,闪存编程器成功的将闪存内容发送到在 FPGA 上运行中的闪存编 程器设计.然后,闪存编程器设计将内容烧写进闪存器件.当闪存烧写完成以后,你 可以重复自由的用任何设计配置 FPGA.
    闪存编程器设计
    闪存编程器设计是 NiosII 闪存编程器的一个关键部分, 它是一个很小的 FPGA 设计, 它包含一个 SOPC Builer 系统包括小型化的硬件和必要的固件进行以下操作: 通过 JTAG 接口与主机进行通信 烧写主机提供的数据进闪存
    105
    不同的电路板往往采用不同的闪存器件,不同的引脚输出和不同的 Altera FPGA 类型.由于这些原因,每 个闪存编程器设计都是板面具体化的因此不能被用于不同的电路板. 如果使用一个客户电路板, 该电路板的设计者必须创建一个电路板的闪存编程器设计并且将它提供给要烧写 电路板上的闪存的人. 对于所有的 Altera Nios 开发套件其闪存编程器设计都已经包含在 NiosII 开发套件的安装路径下. 例如对 于 StratixEP1S10 Nios 开 发 电 路 板 的 闪 存 编 程 器 设 计 位 置 为 如 下 的 路 径 : /components/altera_nios_dev_board_stratix_1s10/system 每个闪存编程器设计都包含以下元件: 1. Nios II CPU 2. JTAG UART 3. 主动串行存储器接口,如果你的 FPGA 接了 EPCS 串行配置器件的时采用. 4. 三态桥 5. CFI-适应性闪存接口 6. 系统 ID 外围 7. 在片存储器,用于固件和缓冲区 对于使用闪存编程器,被开发的实际系统中没有必要包含 NiosII CPU.只要闪存适当的接在 FPGA 上.它 就可以通过 NiosII IDE 闪存编程器烧写,不管设计中是不是有 NiosII CPU.
    目标板
    一个"目标板"是 SOPC Builder 用于决定你所采用的电路板的某些特征的一组文件.其中的某些特征关乎 到闪存是如何连接到 FPGA 上的,使得目标板对于闪存编程器是非常重要的.以下是包含在目标板中的信息: 对于接到电路板的 FPGA 上的每片闪存芯片参考标识. 在闪存编程器设计中闪存器件的基地址. 主机上电路板闪存编程器设计文件的路径,或者 SRAM 项目文件(.sof) . 参考标识是一种在开发设计和闪存编程器设计之间在独立的闪存器件之间保持通道的方法.电路板闪存器 件可能会被指定不同的设计名称和设计基地址, 但是他们的参考标识总是一样的. 例如, "U5" 是一个 Altera Nios 开发板上的闪存器件的参考标识. 为了给你的 SOPC Builder 系统选择一个目标板, 先从 Quartus Tools>SOPC Builder) ( 中打开 SOPC Builder, 然后在 SOPC Builder 窗口的上面的 Target 下拉框中选择你正在使用的电路板的目标板. 当采用客户电路板(就是不是 Altera 设计的板)时,你的电路板的目标板可能不会出现在 Target 下拉框 中除非已经创建了该电路板的目标板.你必须制作一个目标板并且把它提供给要在该板上建立 SOPC Builder 系统的人.更多信息,参考第二章 NiosII 闪存编程器与用户板的接口.
    闪存内容分类
    烧写如闪存的内容可以分为三类:
    106
    软件:软件一般以.elf 格式文件存在,由 NiosII IDE 生成.将软件烧写进闪存允许 NiosII 处理
    器可以在复位以后通过闪存引导启动. 因为从闪存运行软件会相当慢, 所以闪存编程器也允许你将 一段引导复制器程序放在你的软件的前面. 这个引导复制器在你运行目标设备的时候将你的软件从 闪存中拷贝到 RAM 中;然后跳转到 RAM 的地址,在这里软件可以运行的更快. FPGA 配置数据:FPGA 配置数据总是以.sof 文件的形式出现.NiosII 闪存编程器允许你将 FPGA 配 置烧写进闪存.如果使用配置控制器,例如 Altera Nios 开发板就是这样,FPGA 可以在上电复位 的时候由闪存进行配置. 未定内容:未定内容可以是你想烧写进闪存的任意类型的二进制数据.例如图像,声音等等. 闪存文件 闪存文件是由数据文件按照闪存的格式格式化后的来的, 只有这样他们才能被 NiosII IDE 闪存编程器 读取.虽然他们实际上执行的是工业标准 SREC 的文件,但是闪存文件是通过.flash 扩展名来识别的.
    引导选项
    从 CFI 闪存引导 从 EPCS 串行配置器件引导 引导复制器程序 从 CFI 闪存引导 Altera Nios 开发板采用一片 Altera EPM7128AE CPLD 芯片来作为配置控制器.在上电复位时,配置控 制器会自动查看闪存中的 FPGA 配置数据,如果该数据存在,自动从闪存中配置 FPGA. 一旦 FPGA 被配置成功.该配置可能包含一个复位地址在闪存地址空间的 NiosII 处理器.这种情况下, NiosII 处理器也会从闪存引导启动. NiosII 闪存编程器能够烧写 FPGA 配置数据和软件到闪存中的任意地址.因此整个系统,包括硬件和软 件,都可以从 CFI 闪存器件引导启动. 更多的有关Altera Nios开发板配置控制器的信息,参考Nios Development Board Reference Manuals, www.altera.com. 从EPCS串行配置器引导 如果使用了Altera EPCS串行配置器,该系统也可以从串行配置器进行硬件和软件的引导.NiosII IDE 闪存编程器能够将FPGA配置数据和软件内容烧写进EPCS串行配置器.在EPCS器件中闪存编程器首先检查 FPGA配置数据的大小,然后将软件内容接在配置数据的后面. 从一个EPCS串行配置器件引导启动需要整个系统在SOPC Builder设计中时包含一个EPCS串行闪存控制 器元件.这个元件包含很小数量的ROM,用于引导启动.如果NiosII处理器的复位地址被设置到该EPCS串行 闪存控制器的基地址,这少量的附加在控制器上的在片ROM在FPGA配置时被初始化为引导复制器.在CPU复 位时,处理器从在片ROM运行引导复制器,从EPCS串行配置器重新配置软件到相应地址,而这些地址就是那 些未定存储器,例如RAM,SDRAM,在片RAM等的地址,.elf文件被链接到这些地址上.然后引导复制器跳转 到重新配置位置的软件并且开始执行软件. 引导复制器程序 当烧写软件到闪存时(.elf) ,你可以在你的软件烧写如闪存时自动插入一小段引导复制器程序,以便 这个系统从闪存引导启动. 这个引导复制器将会重新将你的软件配置到在.elf文件中链接到的地址.然后跳转到该地址.通常该
    107
    地址是那些未定存储器,例如RAM,SDRAM,在片RAM等的地址.
    NiosII IDE可以根据.elf文件中的链接将软件不同部分配置到相应的地址.对于更多信息参考
    Editing Project Properties in the Nios II IDE Software Tutorial.
    是否插入引导复制器是由elf2flash函数命令决定的,主要取决于这些参数--base, --end, --reset, 和在.elf文档中软件所链接到的位置. elf2flash函数命令当, 并且只有当复位地址在闪存中但是.elf文件 被链接到一个闪存以外的地址时就会在软件前面插入一段引导复制器程序. 例如,如果在SOPC Builder中你的闪存存储器的映射地址为(0x0-0x7FFFFF) ,并且在SOPC Builder 中你已经链接你的软件到外部RAM的首地址0x800000, elf2flash就会在地址:0x0插入一个引导复制器,然后 是你的软件程序.复位以后,引导复制器重新配置你的软件,在执行软件前将软件从闪存放到基地址是 0x800000的RAM中.
    烧写内容到闪存
    这一部分讨论用两种方式来烧写三种类型的内容到闪存中: NiosII IDE 命令行函数
    使用NiosII IDE
    首选的烧写闪存的方式是采用NiosII IDE.NiosII IDE采用的是"使用命令行函数"相同的命令行 函数;然而,在NiosII IDE中这些过程都是自动的,选项也被提供了易于使用的图形接口. 以下叙述了使用NiosII IDE烧写闪存的步骤: 1. 从SOPC Builder中的System Generationg或者从Start菜单中,打开NiosII IDE. 2. 建立一个软件工程. 3. 选择 Tools>Flash Programmer
    108
    图6.3.3 4. 5.
    闪存编程设置
    单击New(左下脚) 选中Program flash memory with software project以便将工程的.elf文件烧写到闪存中. a) 检查列表中的软件工程是不是正确 b) 选中Build project and dependents (if required) before launching. 6. 选中Program FPGA configuration data into hardware-image region of flash memory以便 将FPGA配置文件烧写到闪存中. a) 选择硬件设计FPGA配置.sof文件. b) 为烧写配置数据选择硬件镜像地址偏移量. 7. 选择Program flash memory with a file以便烧写其他二进制文件到闪存 a) 单击Browse 选择你想烧写进闪存的文件 b) 选择你想烧写闪存存储器器件 c) 在闪存存储器器件的地址范围内输入地址偏移量,以便烧写文件. 8. 单击Apply..Apply按钮会变成灰色如果没有改变任何设置的话,这是正常的. 9. 单击Program Flash 如果NiosII IDE不能探测到你的JTAG电缆设置,Program Flash按钮就会变成灰色不能操作. 单击Target Connection栏安装JTAG电缆.
    109
    NiosII IDE首先用闪存编程器设计配置FPGA以便目标板正常工作,然后烧写电路板上的闪存器件.
    闪存烧写原本
    在NiosII IDE自动烧写闪存的时候, 它同时创建了一个原本以便以命令行的形式烧写闪存. 当你单击如 "使 用NiosII IDE"第1-7页,第7步的Apply按钮时,就会在软件工程的路径下生成一个名为 program_.sh的原本文件.例如,如果你的工程名称叫做hello_world_0你用默认的 Debug配置编译工程,闪存烧写原本就是hello_world_0\Debug\program_hello_world_0.sh. 你可以通过在NiosII的软件开发工具箱外壳 (SDK) 中通过命令行运行这个原本, 改变路径到原本的路径下, 然后键入: ./program_hello_world_0.sh. 包含 ./是必要的因为解释程序需要通过它将原本解释为可执行 的.
    使用命令行函数
    在某些情况下,或许你希望使用命令行来代替NiosII IDE来烧写闪存.为了使这种选择性成为可能,以下 由NiosII IDE闪存编程器调用的的函数可以在NiosII SDK外壳中通过命令行函数的方式进行调用.这四个函数所 在的位置是/bin,这部分内容列出了这些函数命令以及他们的功能.
    elf2flash 命令
    elf2flash 命令获取一个.elf格式的软件文件,然后将它转换为可以被烧写进闪存的闪存文件.这个 命令可以选择性的插入一段引导复制器到闪存文件中,以便在软件运行前将软件复制到RAM中. 作为软件创建过程的一部分,这个命令可能已经被运行过.你可以查看软件工程的发行版本路 径看看是否你的软件工程的闪存文件已经被创建了. 使用elf2flash命令的典型的选项列表如下.在NiosII SDK外壳中键入elf2flash --help可以显示该命 令的所有选项. elf2flash选项: --input= 输入.elf文件的文件名 --output= 输出闪存文件的文件名 --base= 系统闪存的基地址(不是闪存编程器设计) --reset= CPU复位地址(在SOPC Builder中设置) --boot= 引导复制器SREC文件的文件名 --epcs 如果.elf内容被烧写进一个EPCS串行配置器件,则需要设置该项. --flash=
    110
    .elf文件将要被烧写进闪存器件的参考标识.
    sof2flash命令
    这个sof2flash命令获取一个.sof格式的FPGA配置文件并且把它转换成一个可以 被烧写进闪存的闪存文件. 使用sof2flash命令的典型的选项列表如下.在NiosII SDK外壳中键入sof2flash --help可以显示该命令的所有选项. sof2flash 选项 --input= 输入.sof文件的文件名 --output= 输出闪存文件的文件名 --offset= .sof内容烧写进闪存时,在闪存中的地址偏移量. --epcs 如果该部分内容被烧写进一个EPCS串行配置器件,则需要设置该项. --flash= .sof文件将要被烧写进闪存器件的参考标识.
    bin2flash命令
    bin2flash可以将任意二进制的数据文件转换为可以被闪存编程器烧写进闪存的 闪存文件. 使用bin2flash命令的典型的选项列表如下.在NiosII SDK外壳中键入bin2flash --help可以显示该命令的所有选项. bin2flash 选项 --input= 输入文件的文件名. --output= 输出闪存文件的文件名. --base= 在系统中闪存的基地址. --location= 该闪存文件烧写进闪存时,在闪存中的地址偏移量. --epcs 如果该部分内容被烧写进一个EPCS串行配置器件,则需要设置该项. --flash= 将要被烧写的闪存器件的参考标识.
    nios2-flash-programmer 命令
    nios2-flash-programmer 命令获取一个由任意以上转换命令生成的闪存文件, 然后将其烧写进指定的闪存器件.nios2-flash-programmer 命令可以烧写系统中任 意的与CFI兼容的闪存和EPCS串行配置器件. 使用nios2-flash-programmer命令的典型的选项列表如下.在NiosII SDK外壳中 键入nios2-flash-programmer --help可以显示该命令的所有选项. nios2-flash-programmer选项
    111
    --base= 在电路板闪存编程器设计中CFI兼容闪存和EPCS串行闪存控制器元件的基地址. --epcs 如果你要烧写EPCS串行配置器,则需要设置该项. --input= 你想烧写入闪存的文件的文件名. --sof= 闪存编程器设计的.sof文件的名称.
    在SOPC Builder系统中例化闪存
    要烧写闪存, 你必须首先确定你的硬件系统作的合适以便闪存编程器获取它所需 要的所有的信息. 在SOPC Builder中创建系统的第一步就是,在SOPC Builder中的System Contents 栏的顶端选择一个合适的目标板.见图1-4.该目标板包含一下信息. 接到FPGA上的闪存的个数 闪存器件独立的电路板参考标识 每个闪存器件的基地址 该类型电路板FPGA闪存编程器设计的位置
    图6.3.4
    选择目标板
    如果你为你的系统添加CFI闪存存储器或者EPCS串行闪存控制器,将会分别看到相应的添加向导.在元 件向导中,图6.3.5和6.3.6,你必须为每个闪存器件选择一个电路板参考标识.这些电路板参考标识将系统 元件和电路板上真实闪存器件联系起来.如果你选择的目标板上只有一个闪存器件.
    112
    这个闪存器件的参考标识将会在CFI兼容Flash Memory元件向导中自动选中,你就不用更改它了. 即使想要用NiosII闪存编程器烧写闪存,你的最终设计也无需一定含有闪存元件.然而,对于该 电路板的闪存编程器设计,必须为每一个你想用闪存编程器烧写的闪存器件添加一个闪存元件.使用闪 存编程器时,即使在最终设计的SOPC Builder系统中没有包含闪存元件,闪存编程器也能够烧写附加的 闪存器件.
    图6.3.5 选择Flash
    图6.3.6 选择EPCS串行配置器
    选择新的目标板
    现在你已经创建了一个新的目标板.你可以选择这个新目标板作为你的原始设计,这个原始设 计就可以烧写缓存. 当mk_target_baord命令创建了目标板时,目标板就会自动被假如到SOPC Builder的元件搜 索路径以便其他工程可以从SOPC Builder中调用.然而,如果你移动了目标板的位置,它就不会被 搜索到.打开SOPC Builder(Tools>SOPC Builder)和选择SOPC Builder Setup(File菜单), 可以检查和编辑SOPC Builder的搜索路径.Component/Kit Library Search Path 文本框中包含除 了SOPC Builder安装路径以外的元件和目标板搜索路径,这些路径和SOPC Builder安装路径在打开
    113
    SOPC Builder时会被搜索. 按照以下步骤可以在你的设计中例化一个新的目标板. 1. 在QuartusII中打开当前原始工程 2. 通过选择Tools>SOPC Builder打开SOPC Builder 3. 选中System Contents栏 4. 从Target下拉列表中选中你的新目标板 5. 选则System Genenration栏 6. 单击Genenrate 7. 当生成完成,单击Exit 8. 如果被询问是否要对模块刚才在SOPC Builder中的更改进行更新时,点击Yes. 9. 选择File>Save 10.Processing>Start Compilation 现在已经准备好可以开始烧写闪存. 假如以上所有的步骤都作的准确无误, 那么现在 你就能烧写闪存了.
    第三节,协控制器EPM240 的工作原理
    在大多数嵌入式系统中都使用 Flash 器件来存储软件工程, 用户数据以及文件等信息. FPGA 在 系统设计中也不例外,同样也使用了 Flash 设备,但不同的是,Flash 中除了存储一般数据外,还 存储 FPGA 的配置数据;这样就不得不使用协控制器来完成数据加载过程,而在"威龙"核心板上 就使用了 MAX II 系列器件 EPM240 这款芯片. 所有 Altera 的开发板上大部分都使用了协控制器,所不同的是,Altera 使用的是 MAX 系列 EPM7128 或者 EPM7256 这两种芯片,这些芯片的区别不是本节讲述范畴,本节所要讲的是协控制器 的工作原理与流程. 主要针对"威龙"核心板来进行讲解, "威龙"核心板使用的协控制器内部有 5 大功能模块, 分别为:Flash 控制模块,并行转串行模块,配置时钟产生模块,状态输出模块以及配置控制逻辑 模块,其整个系统构架如下图 6.3.1 所示:
    114
    A[23..0 Externa l Flash D[7..0] Controlle Reset_n
    Max II EPM240 Configuration controller Flash Controlle r Flash Parallel to serial Data DATA0 Dclk
    EPCS16 n c s Msel[1..0 Config_done Status_n Config_n Recinfig_req A s d o
    CycloneII Ep2c35
    Configuratio n Clock
    Configuratio n Control Configuratio n Status
    LEDs
    图 6.3.1 "威龙"板协控制器系统连接图 "威龙"核心板使用的是 AMD 的 Flash,在 Flash 中存有用户配置数据和安全配置数据,分别 存放在 0x00C00000 和 0x00E00000 这两个区域; 协控制器工作流程如下图 6.3.2 所示:
    115
    图 6.3.2 请参阅 Altera 相关内容.
    协控制器工作流程图
    协控制器的工作可以根据需要进行修改,比如进行多重配置,多个设备的配置等.详细情况
    116
    第七章:通过实例讲解IP Core 的设计过程
    第一节,简介:
    这章描述开发 SOPC 元件的设计流程,由于 NiosII 是一个在 Fpga 内部的软核处理器,定制其 外设就比较容易, 下面就根据定制 I2C 控制器来说明具体的定制 SOPC Builder 设备 (以下简称 SOPC 设备)的步骤. 这章大致分以下三个内容: SOPC 设备设计流程:主要介绍元件设计方法,一般结构等; 设计实例 (PWM) 根据实例进行详细讲解, : 使用已经开发好的代码封装成为在 SOPC Builder 下可以使用的设备; 元件的使用:介绍如何使用我们的 SOPC 设备,以及共享给其他设计者; SOPC Builder 的结构成分及其编辑器: 在 SOPC Builder 下,用户可以使用 SOPC Builder Component 编辑器创建或者更改用户自定义 设备,一旦用户自定义设备成功后,就可以像使用其它 SOPC Builder 设备一样来使用这些设备, 用户还可以将这些设备共享给其他的使用者,这样就大大减少了重复工作;一般一个 SOPC 设备由 以下部分组成: 硬件文件:由 VHDL 或 Verilong HDL 编写的具有一定通用性的代码; 软 件 文 件 : 由 C 语 言 头 文 件 定 义 的 元 件 寄 存 器 影 射 文 件 ( _reg.h ) , 以 及 控 制 设 备的高级程序代码(即设备驱动程序) ; 设备描述文件(class.ptf) :此元件定义了 SOPC 设备的结构,以及系统所需要的一些信 息;此文件是由编辑器自动生成,用户不需要对此文件进行编辑; 当用户定义好这些文件后, 就可以使用 SOPC Builder 编辑器对其进行导入, 将其封装成在 SOPC Builder 下可以使用的设备;当用户更新设计后,也可以用编辑器对其进行更改; 在做用户自定义设备之前,用户要有以下相关技术知识: a) 熟练掌握使用 SOPC Builder 进行系统设计; b) 了解 SOPC Builder 的结构组成; c) Avlaon 总线接口的概念及相关知识; 用户要想亲自练习,就要做如下准备工作: 硬件设计文件:本章所使用的设计文件存放在随书配送的光盘中; 本章设计所使用的软件为:QuartusII6.0,NIOS II 6.0; 威龙核心板及 Byteblaster II 下载电缆;
    117
    第二节,SOPC设备设计流程
    一个 Avalon 从设备的开发一般有以下几个步骤: 确定硬件功能; 如果要使用一个处理器的话,要指定应用程序接口(API)来访问和控制硬件; 根据软,硬件需求,定义 Avalon 接口来提供合适的控制机制以及足够的带宽; 用 VHDL 或 Verilong HDL 代码开发的硬件文件; 单独测试硬件文件来检验控制正确性; 为软件定义 C 头文件来影射寄存器列表; 使用编译器将软硬件文件封装成 SOPC 设备; 在 SOPC Builder 中简单的验证; 使用 NIOS II 测试硬件寄存器的访问; 如果要使用 CPU 来控制,编写软件驱动; 反复的验证,改进; 设计一个完整的系统来验证; 进行系统级验证,如果需要的话还要进行不断的改进; 完成 SOPC 设备开发,发布给更多的使用者; 硬件设计: 像开发其它逻辑设计一样, 开发 SOPC 设备同样也是根据规格书, 不断的反复修改完善的过程; 但 SOPC 设备的结构功能一般由以下几部分组成: 工作逻辑(Task Logic):工作逻辑部分时 SOPC 设备的基本工程部分; 寄存器文件 (Register Files) 寄存器文件是工作逻辑部分与外界沟通的桥梁, : Avalon 总线接口通过访问寄存器来控制工作逻辑单元, Avalon 通过地址偏移来访问不同的寄 存器; Avalon 总线接口:通过 Avalong 总线接口,系统能够访问 SOPC Builder 设备的内部 寄存器, 并对其进行读写控制来达到控制工作逻辑单元; 在使用 Avalon 总线接口的时 候要考虑以下因素: 传送数据的宽度 传送数据的带宽要求 接口主要是为了控制还是传送数据---是零星的传送还是连续的传送 与系统中其他硬件比较,此设备是慢还是快 下图 7.2.1 是一个典型的 Avalon 从接口设备内部结构图:
    118
    图 7.2.1 Avalon 从设备内部结构图 软件设计: 如果是想用 CPU 来控制 SOPC 设备,那么还要编写软件,以便在系统中对设备的调用,控制等; 首先必须要编写寄存器影射文件 (_reg.h) 此文件是使处理器能够对 SOPC 设备中的每一个寄存器 , 进行合理的访问;下面的实例是 UART 设备的寄存器影射文件:
    #include #define IOADDR_ALTERA_AVALON_TIMER_STATUS(base) #define IORD_ALTERA_AVALON_TIMER_STATUS(base) #define IOWR_ALTERA_AVALON_TIMER_STATUS(base, data) #define ALTERA_AVALON_TIMER_STATUS_TO_MSK #define ALTERA_AVALON_TIMER_STATUS_TO_OFST #define ALTERA_AVALON_TIMER_STATUS_RUN_MSK #define ALTERA_AVALON_TIMER_STATUS_RUN_OFST #define IOADDR_ALTERA_AVALON_TIMER_CONTROL(base) #define IORD_ALTERA_AVALON_TIMER_CONTROL(base) #define IOWR_ALTERA_AVALON_TIMER_CONTROL(base, data) #define ALTERA_AVALON_TIMER_CONTROL_ITO_MSK #define ALTERA_AVALON_TIMER_CONTROL_ITO_OFST #define ALTERA_AVALON_TIMER_CONTROL_CONT_MSK #define ALTERA_AVALON_TIMER_CONTROL_CONT_OFST #define ALTERA_AVALON_TIMER_CONTROL_START_MSK #define ALTERA_AVALON_TIMER_CONTROL_START_OFST #define ALTERA_AVALON_TIMER_CONTROL_STOP_MSK #define ALTERA_AVALON_TIMER_CONTROL_STOP_OFST __IO_CALC_ADDRESS_NATIVE(base, 0) IORD(base, 0) IOWR(base, 0, data) (0x1) (0) (0x2) (1) __IO_CALC_ADDRESS_NATIVE(base, 1) IORD(base, 1) IOWR(base, 1, data) (0x1) (0) (0x2) (1) (0x4) (2) (0x8) (3)
    此文件的描述了硬件寄存器的详细情况, 这样就为更高一层的使用带来了极大的方便, 用户可 以在此基础上开发更灵活的设备驱动函数等;
    119
    验证 SOPC 设备: 在设计好后还需要对 SOPC 设备进行整体验证,首先是要对各单元进行单独验证,将各个单元 逐级验证之后,在进行整体验证工作.
    第三节,设计实例一:
    这节用脉宽调制(PWM)设计作为实例来详细的讲解整个 SOPC 设备的开发流程,此设备只有一 个 Avalon 从接口; 在这节,我们将按照如下步骤来进行: 安装设计文件 深入理解 SOPC 设备设计流程 将设计文件封装成为 SOPC 设备 例化 SOPC 设备 在 SOPC Builder 中建立带此设备的系统并下载到目标板上验证 在 NIOS II IDE 中使用 安装设计文件 将 PWM.zip 文件解压缩后放到 D:\altera\works\Project\pwm 目录下, 之后打开此文 件,会发现其文件结构如下表 7.3.1 所示: Table 7.3.1 PWM 设计文件目录 文件名
    /pwm_hw
    描述
    包含硬件设计文件 pwm_task_logic.v PWM 核功能逻辑部分 pwm_register_file.v PWM 核寄存器部分 pwm_avalon_interface.v PWM 核 avalon 总线接口 /pwm_sw 包含 C 文件,用来连接软件与硬件 /inc 低级硬件接口的头文件夹 avalon_slave_pwm_regs.h 定义可以访问的 PWM 内部寄存器 /HAL HAL 驱动文件文件夹 /inc HAL 驱动头文件夹 altera_avalon_pwm_routines.h 定义了函数头文件 /src 包含了驱动函数 altera_avalon_pwm_routines.c 定义了函数体 /test_software 例子文件夹 hello_altera_avalon_pwm.c 实例文件夹 理解 SOPC 设备设计流程 这个 PWM 的功能是输出占空比可以调节的波形,其特性要求如下: 工作逻辑要和时钟同步运行;
    120
    工作逻辑使用 32 位计数器对系统时钟进行分频, 然后输出适合 PWM 输出的占空比可调 的波形; 处理器要能控制 PWM 的工作状态,能够对其进行读写控制; 用寄存器的值来调节 PWM 的周期和占空比; 主机能够中断 PWM 的输出; 工作逻辑模块具有以下一些特性 PWM 模块由输入时钟(clk) ,输出信号(pwm_n) ,使能位,32 位计数器和一个 32 位比 较器组成; 32 位计数器决定 PWM 信号的周期; 比较器比较计数器和 duty_cycle 的值来决定 PWM 信号的占空比输出; 当计数器的值少于或等于 duty_cycle 的值则 PWM 信号为 0,其它的时候为 1; 其内部结构如图 7.3.1 所示:
    图 7.3.1 PWM 内部逻辑图 寄存器模块提供了主机与 PWM 模块的信息交互,它使得主机能够访问并设置其存器的值,工 作逻辑模块也是通过寄存器的值来工作的,PWM 内部有 3 个寄存器,见表 7.3.2 表 7.3.2 寄存器与地址影射表 寄存器名 Clock_divide Duty_cycle enable Reserved 偏移量 00 01 10 11 访问方式 读/写 读/写 读/写 描述 在 PWM 输出一个周期内计数值 PWM 输出为低时的时钟周期数 使能位,1 有效
    读写寄存器仅需要一个时钟周期,这对 Avalon 接口的 wait-states 信号有影响; Avalon 接口是 PWM 模块与系统通信的通道,此模块中的 Avalon 接口是 Avalon 从端口,具有
    121
    以下特性: 与 Avalon 从端口时钟同步; 可读,可写; 读和写都要求 0 等待时间,因为寄存器在一个时钟内就能够对主机的访问做出反应; 对读和写信号没有建立,保持约束; 读延时是不需要的,所有传送都是在一个时钟内完成,否则会影响系统性能; 由于连接的是寄存器而不是 Memory,所以要使用本地地址阵列; 表 7.3.3 列出了 PWM 的接口和 Avalon 从端口接口信号的类型,宽度,方向以及做了简单描述 表 7.3.3 PWM 信号及 Avalon 从端口信号类型 HDL 中信号名 clk resetn Avalon_chip_select address write Write_data read Read_data 了这些函数名与作用; 表 7.3.4 PWM 驱动函数 函数名 Altera_avalon_pwm_init() Altera_avalon_pwm_enable() Altera_avalon_pwm_disable() Altera_avalon_pwm_change_duty_cycle() 将设计文件封装成 SOPC 设备 这里介绍使用 SOPC Builder component editor 工具将设计文件封装 SOPC Builder 下 可用的设备,按如下步骤操作: 10. 打开 SOPC Builder,点击 File 下拉列表中的 New Component 开始设备编辑; 11. 点 击 Next 到 HDL Files 栏 , 点 击 Add HDL File , 找 到 刚 才 的 目 录 D:\altera\works\Project\pwm\pwm_source\pwm_hw,将该文件下的 HDL 文件都 选中,单击 OK,此时编译器会自动分析这些文件; 12. 选择 pwm_avalon_interface.v 为顶层文件,如下图 x7.3.2 所示:
    122
    Avalon 信号 clk Reset_n Chipselect address write writedata read readdata
    位宽 1 1 1 2 1 32 1 32
    方向 Input Input Input Input Input Input Input 时钟信号
    描述 重起信号,低有效 片选信号 地址选择信号 写使能信号 写数据信号 读使能信号
    output 读数据信号
    在此实例中的例子中既给出了寄存器影射文件头文件也给出了设备驱动函数,表 7.3.4 列出
    作用 初始化 PWM 设备 使能 PWM,使其开始工作 终止 PWM 工作 改变 PWM 输出波形占空比
    13. 点击 Next, Signals 栏下,此时在列表中列出了顶层文件中所包含的所有 I/O 到 信号; 14. 根据图 7.3.3,将信号类型进行修改; 15. 点击 Next,到 Interfaces 栏,按照图 7.3.4 进行设置; 16. 点击 Next,到 SW Files 栏,点击 Add SW File,到\pwm_sw\HAL\inc 目录下加 入 altera_avalon_pwm_routines.h ; 到 \pwm_sw\HAL\src 目 录 下 加 入 altera_avalon_pwm_routines.c ; 到 \pwm_sw\inc altera_avalon_pwm_regs.h; 17. 点击 Next,到 Component Wizard 下,在此可以更改 Component 的名称,版本, 分组这三个属性,如图 7.3.5 所示;更改后点击 Finish 目 录 下 加 入
    图 7.3.2 加入硬件
    123
    图 7.3.3 信号处理
    图 7.3.4 接口处理
    124
    图 7.3.5 保存文档 这时,打开工程目录,就会发现一个叫 pwm_avalon_interface 的文件夹,这个文件夹就是刚 才生成的 SOPC 设备 例化 SOPC 设备 由于以上生成的 SOPC 设备只有在刚才打开的工程中可见,而如果要想使其像其他设备一样无 论在哪个工程下都可以用,就需要使用 SOPC Builder Setup 工具将其例化,但在此我介绍一种最 方便的使用方法,那就是将刚刚建立好的设备文件夹,也就是 pwm_avalon_interface 这个文件夹 直接复制到 D:\altera\quartus60\sopc_builder\components 这个文件夹下, 那么就可以像其它设 备一样使用此设备了.如下图 7.3.6 所示, (我将 Quartus 和 NIOS IDE 装在 D:\altera_6 这个文件 夹下) ;
    125
    图 7.3.6 将 pwm 设备文件加入到系统中 在 SOPC Builder 中建立带此设备的系统并下载到目标板上验证 由于此设备是基于 NIOS II 软核控制的, 所以要使用带有 NIOS II 的系统来对此设备进行控制; 在/test_software 这个文件夹下的 hello_altera_avalon_pwm.c 为控制 PWM 的实例工程文件; 现在可以制作一个带有 PWM 设备的硬件平台, PWM 输出的端口连接到一个 LED 上, 将 这样就能 通过 PWM 输出的信号控制 LED 的亮度, 也就是能够对 LED 进行渐亮或渐灭控制; 这个设备的使用和 其他 SOPC 设备一样;步骤如下: 建立一新工程,打开 SOPC Builder 并加入 CPU,JTAG_UART,RAM,PWM 等; 生成 CPU,并将 PWM 输出信号连接到一个 LED 灯上; 锁定管脚并编译,然后下载到试验板上; 打开 NIOS II IDE,根据刚才的 CPU 建立一个空工程,然后将 hello_altera_avalon_pwm.c 工程文件加入到此工程中,编译后下载; 观察结果; 根据以上步骤如果成功则在控制台上看到如下输出:
    Hello from the PWM test program. The starting values in the PWM registers are: Period = 0 Duty cycle = 0 Notice the pulsing LED on the development board.
    126
    第四节,设计实例二:
    本节介绍如何将其它总线的设备转换为 Avalon 总线的设备,而我们最容易得到的免费 IP 为 Opencore(www.opencores.org)网站上的,而此网站上的 IP 一般为支持 wishbone 总线协议,所 以这在这章根据实例来介绍如何将 wishbone 总线上的 IP 转换为支持 avalon 总线的 SOPC 设备; 首 先 到 Opencore 网 站 上 下 载 I2C 的 IP core , 下 载 后 得 到 3 个 VHDL 文 件 (i2c_master_bit_ctrl.vhd,i2c_master_byte_ctrl.vhd,i2c_master_top.vhd)和,它的 PDF 文档文件(i2c_specs.pdf) ,存放到名为 opencores_i2c_master 文件夹下;建立一个 Avalon 总线 接口的头文件 oc_i2c_master.vhd, 再建立一个寄存器映射文件 i2c_reg.h 按照表 7.3.1 将这些文 件分类如表 7.4.1: Table 7.4.1 文件名
    /hdl
    I2C 设计文件目录 描述
    包含应将文件 i2c_master_bit_ctrl.vhd 按位传送 i2c_master_byte_ctrl.vhd 按字节传送 i2c_master_top.vhd Wishbone 总线的顶层文件 oc_i2c_master.vhd Avalon 总线的顶层文件 /inc 包含头文件 i2c_reg.h 寄存器映射头文件, /doc 包含文档文件 i2c_specs.pdf 可参照的文档 下面就根据 Avalon 总线接口规范, 并参考 i2c_specs.pdf 文档说明, wishbone 总线的接口 将 转换为 Avalon 总线接口,oc_i2c_master.vhd 文件的代码如下:
    LIBRARY ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; --use ieee.std_logic_unsigned.all; --use ieee.numeric_std.all; ENTITY oc_i2c_master IS PORT ( ------封装加工,将 wishbone 总线的信号转换成 Avalon 总线接口类型;
    scl_pad_io : INOUT STD_LOGIC; sda_pad_io : INOUT STD_LOGIC; wb_ack_o : OUT STD_LOGIC; wb_adr_i : IN STD_LOGIC_VECTOR (2 DOWNTO 0); wb_clk_i : IN STD_LOGIC; wb_cyc_i : IN STD_LOGIC; 127
    wb_dat_i : IN STD_LOGIC_VECTOR(31 DOWNTO 0); wb_dat_o : OUT STD_LOGIC_VECTOR(31 DOWNTO 0); wb_err_o : OUT STD_LOGIC; wb_rst_i : IN STD_LOGIC; wb_stb_i : IN STD_LOGIC; wb_we_i : IN STD_LOGIC;
    wb_inta_o : OUT STD_LOGIC ); END oc_i2c_master;
    ARCHITECTURE behavior OF oc_i2c_master IS SIGNAL scl_pad_i : STD_LOGIC; SIGNAL scl_pad_o : STD_LOGIC; SIGNAL scl_padoen_o : STD_LOGIC; SIGNAL sda_pad_i : STD_LOGIC; SIGNAL sda_pad_o : STD_LOGIC; SIGNAL sda_padoen_o : STD_LOGIC; SIGNAL arst_i : STD_LOGIC; SIGNAL temp_wb_adr_i : unsigned(2 downto 0);
    COMPONENT i2c_master_top PORT ( -- wishbone signals(wisnbone 接口信号) wb_clk_i wb_rst_i arst_i wb_adr_i wb_dat_i wb_dat_o wb_we_i wb_stb_i wb_cyc_i wb_ack_o : in : in : in : in : in std_logic; std_logic; std_logic; unsigned(2 downto 0); -- master clock input(主输入时钟) -- synchronous active high reset(同步重起信号,高电平有效) -- asynchronous reset(异步重起信号) -- lower address bits(3 位地址接口)
    std_logic_vector(7 downto 0); -- Databus input(8 位数据接口)
    : out std_logic_vector(7 downto 0); -- Databus output(8 位数据输出) : in : in : in std_logic; std_logic; std_logic; -- Write enable input(写使能输入) -- Strobe signals / core select signal(核选择信号) -- Valid bus cycle input(有效的总线输入时钟信号) -- Bus cycle acknowledge output(应答信号) -- interrupt request output signal(中断请求信号)
    : out std_logic;
    wb_inta_o : out std_logic;
    128
    -- i2c lines(I2C 器件接口) scl_pad_i scl_pad_o scl_padoen_o 效) sda_pad_i sda_pad_o sda_padoen_o ); END COMPONENT; : in std_logic; -- i2c data line input(I2C 数据输入) -- i2c data line output(I2C 数据输出) -- i2c data line output enable, active low(I2C 数据输出使能,低有效) : in std_logic; -- i2c clock line input(I2C 时钟输入信号) -- i2c clock line output(I2C 时钟输出信号) -- i2c clock line output enable, active low(I2C 时钟输出使能,低有
    : out std_logic; : out std_logic;
    : out std_logic; : out std_logic
    BEGIN
    temp_wb_adr_i wb_clk_i, => wb_rst_i, => arst_i, => temp_wb_adr_i, => wb_dat_i(7 downto 0), => wb_dat_o(7 downto 0), => wb_we_i, => wb_stb_i, => wb_cyc_i, => wb_ack_o,
    wb_inta_o => wb_inta_o,
    -- i2c lines scl_pad_i scl_pad_o scl_padoen_o sda_pad_i sda_pad_o => scl_pad_i, => scl_pad_o, => scl_padoen_o, => sda_pad_i, => sda_pad_o, 129
    sda_padoen_o );
    => sda_padoen_o
    arst_i <= '1'; scl_pad_io sda_pad_io <= 'Z' WHEN ((scl_padoen_o) /= '0') ELSE <= 'Z' WHEN ((sda_padoen_o) /= '0') ELSE scl_pad_o; sda_pad_o;
    scl_pad_i <= scl_pad_io; sda_pad_i <= sda_pad_io; END behavior;
    编辑 i2c_reg.h 文件内容如下:
    #ifndef __I2C_H #define __I2C_H #include // Overall status and control #define IOWR_I2C_PRERLO(data) #define IOWR_I2C_PRERHI(data) IOWR(OPENCORES_I2C_MASTER_0_BASE, 0, data) IOWR(OPENCORES_I2C_MASTER_0_BASE, 1, data)
    #define IOWR_I2C_CTR(data) IOWR(OPENCORES_I2C_MASTER_0_BASE, 2, data) #define IOWR_I2C_TXR(data) IOWR(OPENCORES_I2C_MASTER_0_BASE, 3, data) #define IOWR_I2C_CR(data) #define IORD_I2C_PRERLO #define IORD_I2C_PRERHI IOWR(OPENCORES_I2C_MASTER_0_BASE, 4, data) IORD(OPENCORES_I2C_MASTER_0_BASE, 0) IORD(OPENCORES_I2C_MASTER_0_BASE, 1)
    #define IORD_I2C_CTR IORD(OPENCORES_I2C_MASTER_0_BASE, 2) #define IORD_I2C_RXR IORD(OPENCORES_I2C_MASTER_0_BASE, 3) #define IORD_I2C_SR IORD(OPENCORES_I2C_MASTER_0_BASE, 4)
    #define I2C_CR_STA 0x80 #define I2C_CR_STO 0x40 #define I2C_CR_RD 0x20 #define I2C_CR_WR 0x10 #define I2C_CR_ACK 0x08 #define I2C_CR_IACK 0x01 #define I2C_SR_TIP 0x2 void init_i2c(void); void i2c_write(unsigned char address, unsigned char reg, unsigned char data); unsigned char i2c_read(unsigned char address, unsigned char reg); void i2c_wait_tip(void);
    130
    #endif /* __I2C_H */
    然后同本章上一实例一样, 打开 SOPC Builder 使用 New Component 向导将此 IP 转换成 SOPC 设 备; 转换接口按照下表 7.4.2 进行设置: Table 7.4.2 I2C 设计文件目录 Avalon 总线接口 元件接口
    wb_inta_o wb_we_i
    irq write wb_stb_i chipselect wb_rst_i always0 wb_err_o chipselect wb_dat_o readdata wb_dat_i writedata wb_cyc_i wishbone_err wb_clk_i clk wb_adr_i address wb_ack_o waitrequest_n sda_pad_io export scl_pad_io export 按照表 7.4.2 在 Compenent Editor 下对信号进行设置,设置完成后如下图 7.4.1 所示;在 Interface 下拉栏中按照图 7.4.2 所示进行设置;
    图 7.4.1 设置信号类型
    131
    图 7.4.2 设置接口类型 再 SW files 中加入 i2c_reg.h 文件,之后保存文件;此时这个 SOPC 设备在刚刚建立的工程目 录下,然后将其拷贝到\quartus60\sopc_builder\components 这个目录下就能想其它设备一样使 用了. 此SOPC设备已经共享到www.soopc.net.cn 网站上, 包含简单的设备驱动; 用户可以自行下载, 免费使用.
    132
    第八章:综合设计示例
    第一节,介绍 第二节,液晶显示原理 第三节,图形显示 第三节,驱动编程—各种显示函数的编写以及验证
    133
    第九章:常用HAL API参考
    这一章描述了常用基于HAL(hardware abstraction layer )API(application programming interface)的函数,每一个函数都给出了C语言原形和简短的介绍,在第四章中有大部分函数的详 细使用情况.
    第一节,文件类
    1,close() 函数原型: 头文件: 参数说明: 描述: 返回值: 2,open() 函数原型: 头文件: 参数说明: 描述: 返回值: 3,read() 函数原型: 头文件: 参数说明: 描述: 返回值: 4,write() 函数原型: 头文件: 参数说明: 描述: 返回值: int close (int fd) fd,描述符 标准的UNIX函数close(),关闭文件描述符fd(即文件指针) 成功返回0,反之为-1
    int open (const char* pathname, int flags, mode_t mode) pathname, 路径名;flags,O_RDONLY或O_WRONLY 或O_RDWR,分别对应着只读,只写, 或读写操作;mode,使用许可说明 打开文件或设备,返回一个文件描述符(读写中使用的非负整数) 成功则返回文件描述符,否则返回-1
    int read(int fd, void *ptr, size_t len) fd文件指针;ptr为读数据的位置指针,len读数据的长度,单位为字节 从文件或设备中读取数据 成功返回读取的字节数,否则返回-1
    int write(int fd, const void *ptr, size_t len) fd,文件指针;ptr为写数据的位置指针,len为写数据的长度,单位为字节 向文件或设备写入数据 成功返回被写入的字节数,否则返回-1
    134
    第二节,时间类
    1,alt_alarm_start() 函数原型: int alt_alarm_start (alt_alarm* alarm, alt_u32 nticks, alt_u32 (*callback) (void* context), void* context) 头文件: 参数说明: 描述: 返回值: 成功返回0,否则返回负数;如果系统时钟无效,那么此函数功能亦无效. 2,alt_alarm_stop() 函数原型: int alt_alarm_stop (alt_alarm* alarm) 头文件: 参数说明: 描述: 返回值: --3,alt_nticks() 函数原型: alt_u32 alt_nticks(void) 头文件: 参数说明: --描述: --返回值: 返回从系统重起到当前时间内时钟信号的振动次数,当系统时钟无效时返回0 4,alt_sysclk_init() 函数原型: int alt_sysclk_init (alt_u32 nticks) 头文件: 参数说明: 时钟每秒振动的次数 描述: 确认系统时钟驱动是否存在 返回值: 成功为0,否则返回一负值;如果系统时钟已经被登记,则调用此函数无效 5,alt_tick() 函数原型: void alt_tick (void) 头文件: 参数说明: --描述: 只有系统时钟驱动能够调用此函数,在系统时钟振动产生时此函数通知系统 返回值: ---
    6,alt_ticks_per_second()
    135
    函数原型: 头文件: 参数说明: 描述: 返回值:
    alt_u32 alt_ticks_per_second (void) ----返回系统时钟每秒振动的次数,没有系统时钟时则返回0
    7,gettimeofday() 函数原型: int gettimeofday (struct timeval *ptimeval, struct timezone *ptimezone) 头文件: 参数说明: 描述: 此函数是为了获得当前时钟的时间结构,此函数被调用时,同时settimeofday()函 数也被调用;由此函数返回的时间结构是不可靠的. 返回值: 成功为0,否则为-1,如果调用失败,标准输出会显示出错的原因 8,settimeofday() 函数原型: int settimeofday (const struct timeval *t, const struct timezone *tz) 头文件: 参数说明: 描述: 当gettimeofday()函数被调用时此函数同时被调用 返回值 成功则返回0,否则为-1,调用此函数不会失败 9,times() 函数原型: 头文件: 参数说明: 描述:
    返回值:
    clock_t times (struct tms *buf) buf,结构体指针 此函数是为了兼容newlib,它返回的是自重起到当前时钟振动的次数;tms的数据结 构为: typedef struct { clock_t tms_utime; clock_t tms_stime; clock_t tms_cutime; clock_t tms_cstime; }; tms_utime: CPU索取用户指令的执行时间; tms_stime: CPU索取由系统表示的过程的执行时间; tms_cutime: tms_utime和tms_cutime的所有子进程时间之和; tms_cstime: tms_stime和tms_cstime的所有子进程时间之和; 返回时钟数,没有时钟则返回0
    136
    10,usleep() 函数原型: int usleep (int us) 头文件: 参数说明: us,时间,单位为微秒 描述: 等待 us 微秒,即延时us微秒 返回值: 成功返回0,反之为-1,有错误发生显示错误发生原因
    第三节,Flash设备
    1,alt_flash_close_dev() 函数原型: void alt_flash_close_dev(alt_flash_fd* fd) 头文件: 参数说明: fd为Flash类型指针 描述: 关别Flash设备 返回值: --2,alt_flash_open_dev() 函数原型: alt_flash_fd* alt_flash_open_dev(const char* name) 头文件: 参数说明: name为用户在SOPC Builder中定义的Flash名称 描述: 用来打开flash设备.一旦打开,函数alt_write_flash()用来写入,函数 alt_read_flash()用来读取数据,或者使用函数alt_get_flash_info(), alt_erase_flash_block(), alt_write_flash_block(),控制单个模块 返回值: 返回0表示失败,成功为任意其他值 3,alt_erase_flash_block() 函数原型: int alt_erase_flash_block(alt_flash_fd* fd, int offset, int length) 头文件: 参数说明: fd为Flash类型文件指针;offset为偏移量;length为擦除的flash模块的长度 描述: 擦除单独的一个flash模块 返回值: 成功返回0,否则返回为负数 4,alt_get_flash_info() 函数原型: int alt_get_flash_info(alt_flash_fd* fd, flash_region** info, int* number_of_regions) 头文件: 参数说明: fd flash设备;info指向flash_region结构体的指针;number_of_regions为区域的 数量 描述: 得到擦除flash区域的细节
    137
    返回值: 成功返回0,否则返回负数 5,alt_read_flash() 函数原型: int alt_read_flash(alt_flash_fd* fd, int offset, void* dest_addr, int length) 头文件: 参数说明: fd 为Flash类指针;offset为偏移量;dest_addr目标地址指针;length为需要读的 字节长度 描述: 从flash偏移量为offset字节开始读取数据,写入到目标地址dest_addr中 返回值: 成功返回0,否则返回非0 6,alt_write_flash() 函数原型: int alt_write_flash(alt_flash_fd* fd, int offset, const void* src_addr, int length) 头文件: 参数说明: fd为Flash类指针;offset 偏移量;src_addr源地址;length字节长度 描述: 从src_addr这个地址中读取length字节写入到Flash中地址边移量为offset这个地址 中 返回值: 成功返回0,否则返回非0 7,alt_write_flash_block() 函数原型: int alt_write_flash_block(alt_flash_fd* fd, int block_offset, int data_offset, const void *data, int length) 头文件: 参数说明: fd为Flash类指针; block_offset为块地址偏移量; data_offset起始写数据的偏移量; length为要写数据的长度 描述: 写数据到被擦除过的Flash扇区 返回值: 成功返回0,否则返回非0
    第四节,DMA设备
    1, alt_dma_rxchan_close() 函数原型: int alt_dma_rxchan_close (alt_dma_rxchan rxchan) 头文件: 参数说明: rxchan为接收信道 描述: 函数 alt_dma_rxchan_close ()通知系统:应用程序已经完成DMA接收信道rxchan,
    138
    返回值:
    目前执行是成功的 成功返回为0,否则为-1
    2, alt_dma_rxchan_depth() 函数原型: alt_u32 alt_dma_rxchan_depth(alt_dma_rxchan dma) 头文件: 参数说明: 描述: 函数alt_dma_rxchan_depth ()返回传送到特别DMA的最大数量(深度)的接收请求 返回值: DMA的最大数量 3, alt_dma_rxchan_ioctl() 函数原型: int alt_dma_rxchan_ioctl (alt_dma_rxchan dma, int req, void* arg) 头文件: 参数说明: dma直接存储器名, req为请求操作的列举, arg由请求决定 描述: 通过DMA接收信道执行设备的具体I/O操作 返回值: 成功返回请求具体值,否则返回为负数 4, alt_dma_rxchan_open() 函数原型: alt_dma_txchan alt_dma_txchan_open (const char* name) 头文件: 参数说明: name为常数字符指针,如/dev/dma_0 描述: 为DMA接收信道获得一个alt_dma_rxchan描述符 返回值: 成功返回非0,否则返回为0 5, alt_dma_rxchan_prepare() 函数原型: int alt_dma_rxchan_prepare (alt_dma_rxchan dma, void* data, alt_u32 length, alt_rxchan_done* done, void* handle) 头文件: 参数说明: dma使用的信道;data接收数据位置的指针;length最大的接收数据长度;done一旦 数据被接收,调用返回函数;handle,非透明值传到done 描述: 发送一个接收请求到DMA接收信道 返回值: 成功返回0,否则返回为负数 6, alt_dma_rxchan_reg() 函数原型: int alt_dma_rxchan_reg (alt_dma_rxchan_dev* dev) 头文件: 参数说明: dev接收信道设备名 描述: 给系统寄存DMA接收信道
    139
    返回值:
    成功返回0,否则返回为负数
    7, alt_dma_txchan_close() 函数原型: int alt_dma_rxchan_close (alt_dma_rxchan rxchan) 头文件: 参数说明: txchan发送信道名 描述: 通知系统:应用程序已经完成DMA发送信道txchan 返回值: 成功返回0,否则返回为负数 8, alt_dma_txchan_ioctl() 函数原型: int alt_dma_txchan_ioctl (alt_dma_txchan dma, int req, void* arg) 头文件: 参数说明: dma直接存储器名;req为请求操作的列举;arg请求的额外参数,由请求决定 描述: 通过DMA发送信道执行设备的具体I/O操作 返回值: 成功返回请求具体值,否则返回为负数 9, alt_dma_txchan_open() 函数原型: alt_dma_txchan alt_dma_txchan_open (const char* name) 头文件: 参数说明: name为常数字符指针,如/dev/dma_ 描述: 为DMA发送信道获得一个alt_dma_rxchan描述符 返回值: 成功返回非0,否则返回为0 10,alt_dma_txchan_reg() 函数原型: int alt_dma_txchan_reg (alt_dma_txchan_dev* dev) 头文件: 参数说明: dev接收信道设备名 描述: 给系统寄存DMA发送信道 返回值: 成功返回0,否则返回为负数 11,alt_dma_txchan_send() 函数原型: int alt_dma_txchan_send (alt_dma_txchan dma, const void* from, alt_u32 length, alt_txchan_done* done, void* handle) 头文件: 参数说明: dma使用的信道;data接收数据位置的指针;length最大的接收数据长度;done一旦 数据被接收,调用返回函数;handle,非透明值传到done 描述: 发送一个发送请求到DMA发送信道
    140
    返回值:
    发送成功返回0,反之返回为负数
    12,alt_dma_txchan_space() 函数原型: int alt_dma_txchan_space (alt_dma_txchan dma) 头文件: 参数说明: dma 直接存储器名 描述: 返回被传送到具体DMA发送信道的发送请求数目 返回值: 返回发送请求数目
    第五节,其他常用函数
    IORD_16DIRECT(BASE, OFFSET) 从地址位置为BASE+OFFSET的寄存器中直接读取16Bit的数据 IORD_8DIRECT(BASE, OFFSET) 从地址位置为BASE+OFFSET的寄存器中直接读取8Bit的数据 IOWR_32DIRECT(BASE, OFFSET, DATA) 往地址位置为BASE+OFFSET的寄存器中直接写入32Bit的数据 IOWR_16DIRECT(BASE, OFFSET, DATA) 往地址位置为BASE+OFFSET的寄存器中直接写入16Bit的数据 IOWR_8DIRECT(BASE, OFFSET, DATA) 往地址位置为BASE+OFFSET的寄存器中直接写入8Bit的数据 IORD(BASE, REGNUM) 从基地址为BASE的设备中读取偏移量为REGNUM的寄存器里面的值. 寄存器的值在地址总线的范围之 内. IOWR(BASE, REGNUM, DATA) BASE为基地址,往偏移量为REGNUM寄存器中写入数据.寄存器的值在地址总线的范围之内. IORD_32DIRECT(BASE, OFFSET) BASE为寄存器的基地址,OFFSET为寄存器的的偏移量. 从地址位置为BASE+OFFSET的寄存器中直接读取32Bit的数据 IORD_16DIRECT(BASE, OFFSET) 从地址位置为BASE+OFFSET的寄存器中直接读取16Bit的数据 IORD_8DIRECT(BASE, OFFSET) 从地址位置为BASE+OFFSET的寄存器中直接读取8Bit的数据 IOWR_32DIRECT(BASE, OFFSET, DATA) 往地址位置为BASE+OFFSET的寄存器中直接写入32Bit的数据 IOWR_16DIRECT(BASE, OFFSET, DATA)
    141
    往地址位置为BASE+OFFSET的寄存器中直接写入16Bit的数据 IOWR_8DIRECT(BASE, OFFSET, DATA) 往地址位置为BASE+OFFSET的寄存器中直接写入8Bit的数据
    第十章:常见问题与解答
    第一节,Quartus软件编译类
    1,为什么在同样的硬件条件下,用别人编辑好的工程正常工作,而自己编译就通不过 答:这种情况大部分是因为设置的问题,包括器件设置以及编译设置.可根据具体的出错原因 来修改设置(包括编译设置和器件设置). 2,
    第二节,Nios II IDE开发环境
    1,出现如下错误如何解决 Using cable "USB-Blaster [USB-0]", device 1, instance 0x00 Pausing target processor: not responding. Resetting and trying again: FAILED Leaving target processor paused 答:原因:1,管脚锁定错误;2,晶振没工作(没有时钟信号);3,CPU 复位信号被拉低; 2,大家帮我看看,我在下下载运行时出现了这个问题是什么原因,该如何解决
    Using cable "USB-Blaster [USB-0]", device 1, instance 0x00 Pausing target processor: OK Initializing CPU cache (if present) OK Downloading 00000020 ( 0%) Downloaded 62KB in 0.9s (68.8KB/s) Verifying 00000020 ( 0%) Verify failed between address 0x20 and 0xF4BF Leaving target processor paused
    答:内存问题,此时用户一般使用了外部的存储器,如 Sdram;数据能下进去,而运行不起来,一般出现此类 问题是因为 Sdram 的时钟相位问题,请修改 Sdram 的时钟相位,这与您的硬件电路有关,我设计的板子一般设置 为"-63".
    142
  • 下载地址 (推荐使用迅雷下载地址,速度快,支持断点续传)
  • 免费下载 PDF格式下载
  • 您可能感兴趣的
  • project2007模板  project2007免费下载  project2007下载  project2007中文下载  project2007天空下载  project2007下载破解  微软project2007下载  下载project2007软件  msproject2007下载