2)摘自《开拓者 Nios II开拓指南》关注官方微旗子暗记"大众年夜众号,获取更多资料:正点原子
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/index.html

第八章EPCS IP核

Altera 公司的 FPGA 芯片大多采取 EPCS 系列存储器作为配置数据的存储空间,带 Avalon
接口的 EPCS IP 核许可 Nios II 系统访问 EPCS 串行配置器件。Altera 供应集成到 Nios II 硬件
抽象层(HAL)系统库的驱动程序,许可用户利用 HAL 运用程序接口(API)来读写 EPCS 器
件。本章我们通过对 EPCS 的读写操作来学习 EPCS IP 核的利用。
本章包括以下几个部分:
8.1 简介
8.2 实验任务
8.3 硬件设计
8.4 软件设计
8.5 下载验证
简介
我们的开拓者开拓板利用的 Flash 为采取 SPI 协议的串行 Flash,大小为 16Mbit。内部分
为 32 个扇区(sector),每个扇区有 256 页(page),每页有 256 个字节。该 Flash 兼容 EPCS,
可以利用 EPCS IP 核,通过 Nios II 系统对 Flash 实行以下操作:
(1) 在 Flash 器件中存储程序代码。EPCS 掌握器自带 Boot-Loader 代码,因此 Nios II 系统
许可用户在 Flash 器件中存储程序代码。
(2) 存储非易失落性数据,例如串行号、NIC 号和其他须要长久储存的数据。
(3) 管理 FPGA 配置数据。Flash 可存储 FPGA 的配置数据,并在上电时自动完成对 FPGA
的配置。具有网络接口的嵌入式系统可从网络上吸收新的 FPGA 配置数据,并通过 EPCS 掌握
器将新的配置数据下载到 EPCS 串行配置器件中。下面我们先来看一下 EPCS IP 核和 EPCS 芯
片的连接框图,如下图所示:
图 8.1.1 EPCS掌握器IP核连接到外部EPCS芯片的构造框图
从上面的这个构造框图中,我们可以看到 EPCS Flash 器件的存储空间被分成了两个独立
的区域:配置存储空间和通用存储空间。配置存储空间紧张用于存储 FPGA 配置数据,如果
FPGA 配置数据没有填满全体 EPCS 器件,那么剩下空间可以存储用户非易失落性数据即通用存
储空间。
EPCS 掌握器包含一个用于存储 Bootloader 程序的片上 ROM 存储器,当 EPCS 掌握器与
Cyclone 和 Cyclone II 器件一起利用时,EPCS 掌握器须要 512 字节的 Bootloader 的 ROM 存储
空间。当 EPCS 掌握器与 Cyclone III、Cyclone IV、Stratix II 等器件一起利用时,EPCS 掌握器
须要 1024 字节(也便是 1KB)的 Bootloader 的 ROM 存储空间。如果把 Nios II 处理器的复位
地址放在 EPCS 掌握器 IP 核的基址处,可以让 Nios II 处理器从 EPCS 掌握器 IP 核开始勾引,
在这种情形下,复位后 CPU 首先实行勾引 EPCS 掌握器的 Bootloader ROM 中的代码,把存储
在 EPCS 中通用内存区域的数据复制到指定的 RAM 存储器,然后把系统掌握权转移给存储在
RAM 中的程序。实现这些操作的程序代码无需用户编写,由 Nios II SBT for Eclipse 软件自动
天生。Nios II SBT for Eclipse 软件供应了编程 EPCS 的工具并编译产生用于存储在 EPCS 中文
件的程序代码。
EPCS 掌握器有一个 Avalon-MM 从接口,这个接供词给访问 Bootloader 代码和掌握寄存
器的能力。下表给出了 EPCS IP 核的寄存器描述。
图 8.1.2 EPCS IP核的寄存器描述
从 EPCS IP 核的寄存器描述表格中我们可以看出:
(1) 偏移地址 0x00~0xff:这 256 个字(32bits)是专用于存储 Bootloader 代码,当 Nios II
系统的复位地址指向 EPCS 掌握器时,处理器便会从 EPCS 掌握器基地址开始的 256 个字存储
空间读取 Bootloader 代码。并且 EPCS 掌握器包含有一个中断旗子暗记,当把程序代码全部加载到
指定的 RAM 中时产生中断要求。
(2) 偏移地址 0x100-0x106:这 7 个字节是 EPCS 掌握和数据寄存器,由于 Altera 没有公
布 EPCS IP 核的寄存器用法,要访问 EPCS 器件,用户必须利用 Altera 供应的 HAL 驱动程序。
现在我们来看一下 Qsys 中 EPCS IP 核有哪些配置选项。打开 Qsys 后,我们在 Library 下的搜索框中输入“flash”如下图所示:
图 8.1.3 搜索flash
双击红框所圈的 EPCS/EPCQx1 Serial Flash Controller,弹出如下图所示界面:
图 8.1.4 EPCS FLASH IP核的配置选项
对付配置选项只有“Automatically select dedicated active serial interface, if supported.”这一
选项可选,由于我们的开拓者开拓板利用 JTAG 接口进行下载配置,以是也可以不选,选上也
不会有影响。其它的不须要任何设置,我们只要将 EPCS IP 核添加到 Qsys 软件中就可以利用
了。
实验任务
本章的实验任务是利用官方供应的 EPCS IP 核实现对 EPCS Flash 的读写操作。
硬件设计
本章实验的硬件框架如下图所示:
图 8.3.1 EPCS IP核实验的硬件框架图
图中, clk IP 核的时钟频率设置为 50MHz。而新添加的 EPCS IP 核,保持默认设置即可。
须要把稳的是,此时 Nios II IP 核的复位向量 Reset Vector 和非常向量 Exception
Vector 的设置如下图所示:
图 8.3.2 Nios II的复位向量和非常向量设置
现在我们在顶层代码中对 Qsys 系统进行例化,如下所示:
1 module qsys_epcs_rw(
2 //module clock
3 input sys_clk , // 时钟旗子暗记
4 input sys_rst_n , // 复位旗子暗记(低有效)
5
6 //EPCSinterface
7 output epcs_flash_dclk , // EPCS FLASF 的驱动时钟
8 output epcs_flash_sce , // 片选旗子暗记
9 output epcs_flash_sdo , // 数据输出
10 input epcs_flash_data0 // 数据输入
11
12 //user interface
13 );
14
15 //
16 // main code
17 //
18
19 //例化 EPCS 硬件框架
20 qsys_EPCSu_qsys_epcs(
21 .clk_clk (sys_clk ), // 驱动时钟
22 .reset_reset_n (sys_rst_n ), // 复位旗子暗记(低有效)
23 .epcs_flash_dclk (epcs_flash_dclk ), // EPCS FLASF 的驱动时钟
24 .epcs_flash_sce (epcs_flash_sce ), // 片选旗子暗记
25 .epcs_flash_sdo (epcs_flash_sdo ), // 数据输出
26 .epcs_flash_data0 (epcs_flash_data0) // 数据输入
27 );
28
29 endmodule
从顶层代码可以看到,我们紧张完成对 EPCS 硬件框架的例化。个中管脚分配如下:
表8.3.1EPCS IP核实验管脚分配
软件设计
在先容代码之前,我们先理解一些关键信息。
创建好软件工程后,我们打开 system.h,找到 EPCS 配置模块,如下图所示:
图 8.4.1 EPCS配置模块
要把稳红框中的 EPCS_FLASH_NAME,我们会用到它。接下来我们认识一下几个用于
EPCS_FLASH 的函数:
◆ 打开 flash 函数:alt_flash_open_dev()
函数的功能为打开 Flash,原型为
alt_flash_fd alt_flash_open_dev(const char name);
name 是 Flash 的名字,便是上面提到的 EPCS_FLASH_NAME,返回值的类型是 alt_flash_fd
类型的句柄,该类型在 alt_flash.h 文件中声明:返回值为 0 表示打开成功,否则不堪利。
获取 Flash 信息函数:alt_epcs_flash_get_info()
函数的功能为获取 Flash 信息,原型为
int alt_epcs_flash_get_info(alt_flash_fd fd, flash_region info,int
number_of_regions );
个中alt_flash_fd fd为Flash的句柄,int number_of_regions为region的数量,flash_region
info 为 Flash 的信息,是一种构造体类型,包含的信息如下
typedef struct flash_region
{
int offset;
//地址偏移
int region_size;
//region 大小
int number_of_blocks; //Block 数量
int block_size;
//Block 大小
}flash_region;
◆ 块擦除函数:alt_epcs_flash_erase_block()
函数的功能为擦除选择块,原型为
int alt_epcs_flash_erase_block(alt_flash_dev flash_info, int block_offset);
alt_flash_dev flash_info 为函数的句柄,同 alt_flash_fd fd,int block_offset 为块偏移量,
即要擦除的块在 flash 中的地址。
◆ Flash 写函数:alt_epcs_flash_write()
函数的功能为向 Flash 指定地址写入数据,原型为
int alt_epcs_flash_write(alt_flash_dev flash_info, int offset,const void src_addr,
int length);
int offset 为写入的起始偏移地址,const void src_addr 为写入数据的存放地址,int length
为写入数据的字节长度。
◆ Flash 读函数:alt_epcs_flash_read()
函数的功能为从 Flash 指定地址读取数据,原型为
int alt_epcs_flash_read(alt_flash_dev flash_info, int offset,void dest_addr, int length);
int offset 指定读取 Flash 数据的偏移地址,void dest_addr 为读取后的数据存放的地址,
int length 指定读取的字节长度。
现在我们来看一下本实验的软件工程代码,如下:
1 #include <stdio.h>
2 #include <string.h>
3 #include "system.h"
4 #include "sys/alt_flash.h"
5 #include "altera_avalon_epcs_flash_controller.h" //EPCS 干系函数头文件
6
7 #define DATA_SIZE 100
8
9 int main(void)
10 {
11 flash_region regions;
12 alt_flash_fd fd;
13 int number_of_regions;
14 int i,ret_code;
15 char data_wr[DATA_SIZE];
16 char data_rd[DATA_SIZE];
17
18 printf("写入 flash 的数据 ");
19 for (i=0;i<DATA_SIZE;i++){
20 data_wr[i] = i; //初始化 data_wr 数组
21 printf("%d,",data_wr[i]); //将 data_wr 数组中的值打印到掌握台
22 }
23 printf("\n");
24 //打开 EPCS_FLASH 器件,获取 EPCS_FLASH 器件句柄
25 fd = alt_flash_open_dev(EPCS_FLASH_NAME);
26 if(!fd){
27 printf("Can't open flash device\n");
28 } else{
29 //成功打开 EPCS_FLASH 器件,并获取 EPCS_FLASH 器件信息
30 ret_code = alt_epcs_flash_get_info(fd, ®ions, &number_of_regions);
31 }
32
33 if(!ret_code){
34 //擦除第九个 Block 的内容
35 alt_epcs_flash_erase_block(fd,regions->offset+(regions->block_size)8);
36 //把 data_wr 数组的数据写入第九个 Block
37 alt_epcs_flash_write (fd,regions->offset+(regions->block_size)8,
38 data_wr,DATA_SIZE);
39 //读取写入第九个 Block 的数据
40 lt_epcs_flash_read(fd,regions->offset+(regions->block_size)8,data_rd,DATA_SIZE);
41
42 printf("从 flash 读取到的数据 ");
43 for(i=0;i<DATA_SIZE;i++){
44 printf("%d,",data_rd[i]);
45 }
46 printf("\n");
47 } else{
48 printf("Can't getEPCSflash device info"); //没有得到 EPCS_FLASH 信息
49 }
50
51 alt_flash_close_dev(fd); //关闭 EPCS_FLASH 器件
52 return 0;
53 }
在代码中,首先宏定义了 DATA_SIZE,由来指定向 Flash 写入和读取数据的长度,然后初
始化写入 Flash 数据的数组 data_wr。接着我们获取 EPCS_FLASH 的句柄,并判断是否得到。
如果精确得到,就读取 EPCS_FLASH 的器件信息,如果没有,就打印相应的信息。在得到器
件信息的情形下,我们向第 9 块 block 写入 data_wr 数据。写入数据之前,先擦除该块的信息,
写入完成后,读取写入的信息放入 data_rd 数据中并打印到掌握台。末了关闭 FLASH。
下载验证
讲完了软件工程,接下来我们就将该实验下载至我们的开拓者开拓板进行验证,首先我们须要在 Quartus II 软件中将 qsys_epcs_rw.sof 文件下载至我们的开拓者开拓板,qsys_epcs_rw.sof
下载完成后,我们还须要在 Eclipse 软件中将 qsys_epcs_rw.elf 文件下载至我们的开拓者开拓
板,sdram_rw.elf 下载完成往后,我们的 C 程序将会在我们的开拓者开拓板上实行,末了,在
Nios II Console 界面显示出代码运行结果,如下图所示。我们可以看到从 Flash 读取到的数据
和写入 Flash 的数据同等。至此,我们的 EPCS IP 核实验就完成了。
图 8.5.1 实验结果






