一乐电子

 找回密码
 请使用微信账号登录和注册会员

QQ登录

只需一步,快速开始

微信扫码登录

手机号码,快捷登录

手机号码,快捷登录

搜索
查看: 10198|回复: 2

用户定制外设

[复制链接]
发表于 2009-1-2 17:43 | 显示全部楼层 |阅读模式
本节讲述开发一个定制的SOPC Builder元件的设计流程,从创建定制外设、集成外设到 系统中,最后将其下载到硬件。讲述定制外设的开发流程之后,以Altera公司提供的一个设计实例作为练习。该实例展示了开发一个只具有Avalon从接口的外设。用户使用Altera提供的HDL设计,将其打包到成一个SOPC Builder元件,然后在系统中例化它。然后将工程编译下载到开发板上。最后介绍怎样共享用户定制外设,这样用户设计的外设可以为其它的系统和设计者使用。 - j! k5 L2 H7 q
        SOPC Builder提供元件编辑器使用户可以创建和编辑自己的SOPC Builder元件。
) o! k" U6 ?& C( I( C
  • 典型地,一个元件有下面的部分构成:
  • 硬件文件:描述元件硬件的HDL模块。
  • 软件文件:定义元件寄存器映射的C语言的头文件,元件的驱动程序。
元件描述文件(class.ptf):定义元件的结构,提供给SOPC Builder将该元件集成到系统中必要的信息。元件编辑器根据用户提供的软件和硬件文件以及用户在元件编辑器的图形用户界面指定的参数自动产生这个文件。 ( O5 @1 n! k8 ?2 P
        在创建完描述元件的硬件和软件文件之后, 用户使用元件编辑器将这些文件打包成一个SOPC Builder元件。如果用户更新了硬件或软件文件,用户还可以使用元件编辑器对这个元件进行再次的编辑。 5 l8 b* H2 k0 ^9 V1 z  A% W
8.2.1 元件开发流程
! a0 x) k, u7 o) \6 l: W# x- o; C        本节介绍元件开发流程,包括硬件和软件两方面,这里介绍只有一个Avalon从接口的元件设计流程, 而其设计流程的步骤可以很容易地推广到具有主端口的元件或者多个主端口和从端口的元件。   G! p) j7 y$ e6 Y8 N6 q
从外设的典型的设计顺序包括如下的步骤: 2 ?5 V) s0 y( }, |" d
1. 指定硬件功能。 : I( X5 j: D9 ]
2. 如果微处理器要控制该元件,指定访问和控制该硬件的应用程序接口 (API) 。 % a+ _7 r1 O& H. L2 x
3. 根据硬件和软件的要求,定义一个Avalon 接口,该接口要提供:
6 |2 F* y3 o) H        a. 正确的控制机制。
1 I+ R  {2 l: N: a, y! l3 T0 C        b. 足够的吞吐量性能。
+ C1 `$ q! m* ~4. 采用Verilog或者VHDL编写硬件设计。. 9 a- y. L% n$ C2 W
5. 单独测试元件的硬件,验证操作的正确性。
4 |6 Y0 m* C1 f+ j6. 编写C头文件,为软件定义硬件层次的寄存器映射。 . ~! N1 F1 N, J; B
7. 使用元件编辑器将硬件和软件文件打包成一个元件。 $ L' f3 O& D! J+ s, K  V7 d+ f
8. 例化元件为一个SOPC Builder系统的模块。
8 w" ^* [8 r6 O) ^3 p9. 使用Nios II处理器来测试对元件的寄存器级的访问,用户可以执行硬件的测试,或者是进行HDL仿真。
9 A2 A/ }9 I. G1 L+ ]10.如果微处理器要控制该元件,编写元件的驱动程序。 , z2 X  L, h$ y. W: J2 c
11. 根据元件的在系统的行为,反复改进元件的设计: 8 s# W* k$ m8 K' M9 V
        a. 硬件的改进和调整。 ! m/ B/ V4 I  V
        b. 软件的改进和调整。 : w, u6 _- y9 S: s
        c. 使用元件编辑器更新元件。
. V3 A0 R  r. A7 o12.编译完整的含有一个或多个该元件的SOPC Builder系统。
, P+ P' I! C; S9 H, @1 u4 _8 T13. 执行系统级的验证,如果必要进行进一步的反复改进。
& J" w- y+ [/ y% T$ T* v14. 完成元件的设计,发布该元件,其他设计者可以重用该元件。 ( _0 A! `3 u/ g& C1 B& B- C
主外设的设计的过程与之类似,只是软件开发方面有所差异。 + J0 E2 x9 J1 A
8.2.2 硬件设计
% U: M# Y! K* n2 W        同任何的逻辑设计过程一样,SOPC Builder元件的硬件开发在需求分析阶段之后开始。当用户根据需求说明编写和验证HDL逻辑时,其过程是一个迭代的过程。
) q2 a0 Y, Z! I6 Y9 f/ O典型元件的结构包括下面的功能模块:
5 q! z4 Z4 G0 B' y( U1 {, z7 X5 L% w; m( q1 H% F6 b! _
  • 任务逻辑(Task Logic)——任务逻辑实现元件基本的功能。任务逻辑是和设计相关的。
  • 寄存器文件(Register File)—— 寄存器文件为任务逻辑内部的信号同外部通信提供了一条通路,反之亦然。寄存器文件映射内部的节点为可寻址的地址偏移量,Avalon接口可对其进行读写访问。
  • Avalon 接口—— Avalon接口提供标准的寄存器文件的Avalon前端。接口可以使用任意的Avalon信号类型,以访问寄存器文件和支持任务逻辑所需要的传输。下面的因素影响 Avalon接口:
           要传输的数据宽度是多少?   @; h( t; N+ `
           数据传输需要的吞吐量的要求是多少?
( p& e' O4 ]7 H$ k0 b           该接口主要是为了控制还是数据? 即传输是零星的,还是连续的突发传输? ( U# G) q1 L. F) e
           硬件相对系统中其它的元件是快速还是低速元件? 8 p- X5 F! I6 p' A3 {
图8-16为一个具有一个Avalon从端口的典型元件的原理图。
, z, x! W3 {4 w5 a" |9 e8.2.3 软件设计 ( h9 Y2 I) D  k! Z
        如果用户想要微处理器来控制用户的元件,用户则必须提供软件文件,软件文件定义了该元件的软件视图。在最小化的软件设计中,用户必须定义每个从端口的寄存器映射,以便处理器可以访问。元件编辑器允许用户将C语言的头文件和元件打包在一起以定义硬件的软
! s0 }# C, ]" @; X: h件视图。典型情况下,头文件声明读和写元件寄存器的宏函数,寄存器的地址相对于分配给元件的符号化的基地址。 下面的例子是从Altera提供的用于Nios II处理器的UART元件的寄存器映射摘录的一部分。
1 K  Q$ [  K  Y例:元件的寄存器映射
2 B: B4 D8 R& D6 k0 Z( \) G: W6 n#include <io.h>
2 ?9 k/ i- q! o+ C#define IOADDR_ALTERA_AVALON_TIMER_STATUS(base) __IO_CALC_ADDRESS_NATIVE(base, 0)
8 b, u# O4 v9 _, M) K7 @2 F#define IORD_ALTERA_AVALON_TIMER_STATUS(base) IORD(base, 0) * y! T& v' `  f; U
#define IOWR_ALTERA_AVALON_TIMER_STATUS(base, data) IOWR(base, 0, data) 6 w4 L  t" p( B8 {; o
#define ALTERA_AVALON_TIMER_STATUS_TO_MSK (0x1)
3 x5 j& E' K7 ^' b6 u8 o#define ALTERA_AVALON_TIMER_STATUS_TO_OFST (0)
" G+ X% t. i; f8 d$ C#define ALTERA_AVALON_TIMER_STATUS_RUN_MSK (0x2)   ~8 A2 y" L& ~2 A2 H
#define ALTERA_AVALON_TIMER_STATUS_RUN_OFST (1) ; A6 }6 Z1 L# Y2 x0 H' ?) U
#define IOADDR_ALTERA_AVALON_TIMER_CONTROL(base) __IO_CALC_ADDRESS_NATIVE(base, 1)
9 @3 ~' l# c" r% k. e9 C9 \! F#define IORD_ALTERA_AVALON_TIMER_CONTROL(base) IORD(base, 1)
5 }, w# E4 U7 L) m5 e( H#define IOWR_ALTERA_AVALON_TIMER_CONTROL(base, data) IOWR(base, 1, data)
" |, T4 p0 b7 i' E#define ALTERA_AVALON_TIMER_CONTROL_ITO_MSK (0x1)
; L8 D, |8 i0 e& q#define ALTERA_AVALON_TIMER_CONTROL_ITO_OFST (0)   m; k3 l/ q5 C1 W% t  ~. w4 ?
#define ALTERA_AVALON_TIMER_CONTROL_CONT_MSK (0x2) . r  n2 J, u4 ], O; E; z
#define ALTERA_AVALON_TIMER_CONTROL_CONT_OFST (1) 8 c" D3 g7 K/ g1 t# t7 ], B6 U
#define ALTERA_AVALON_TIMER_CONTROL_START_MSK (0x4)
9 G) E4 s" F8 F#define ALTERA_AVALON_TIMER_CONTROL_START_OFST (2) % n5 z2 R, i! [) Y% x
#define ALTERA_AVALON_TIMER_CONTROL_STOP_MSK (0x8)
- ]/ h6 T4 {% z2 z#define ALTERA_AVALON_TIMER_CONTROL_STOP_OFST (3)
( _( }; d  v, F2 U        软件驱动程序抽象了元件的硬件细节,所以软件可以在一个高的层次上访问元件。驱动函数,提供给软件访问硬件的API。软件的要求根据元件的需要而有不同。最普遍的函数类型初始化硬件、读数据和写数据。驱动程序和目标处理器是相关的,元件编辑器允许用户方便地打包软件驱动程序到 $ _3 b6 Y' ?' H4 p" v# R
HAL,Nios II 处理器开发工具将使用HAL。提供驱动程序给其它的处理器,用户必须满足目标处理器的开发工具的需要。
& d# W, Q9 n0 m+ r) y7 [4 K- N        为Nios II HAL编写驱动程序的更多信息,参阅Nios II Software Developer's Handbook。查看Altera提供的元件的软件文件也是很有帮助的。Nios II开发包提供很多元件,用户可以用作参考。路径为<Nios II kit path>/components/ ; O# `: G$ |% ]+ m4 S) y
8.2.4 验证元件   m0 l. Z/ d- P: r& g
        当用户完成越来越多的设计,用户可以以增量方式验证元件。通常,用户首先以一个单元来验证硬件逻辑(这可能包括很多小的验证阶段),然后在系统中验证元件。1 u/ R7 F% G" U8 I
单元验证 / a' I: M* L4 V& n4 |2 Q
        单独测试任务逻辑模块,用户使用喜欢的验证方法,如行为级或寄存器传输级(RTL)的仿真工具。类似地,用户使用自己擅长的验证工具来验证所有的元件逻辑,包括寄存器文件和Avalon接口。在使用元件编辑器将HDL文件打包成一个元件之后,Nios II开发包提供一个易用的方法来仿真元件的读和写的操作。使用Nios II处理器的强大的仿真环境,用户可以编写C代码让Nios II处理器向用户的元件发起读写传输。结果可以在ModelSim仿真器或是硬件上看到, 如Nios 开发板。 读者可以参阅AN351: Simulating Nios II Embedded Processor Designs获取更多的信息。 / w( J5 ^$ t. W% B* q  y8 E% B
系统级的验证 7 X5 G  e( }9 U2 t( F& W
        当用户将HDL文件打包成一个元件之后,用户可以在系统中例化该元件,并且验证整个系统模块的功能。 SOPC Builder对RTL仿真器系统级的验证提供支持, 如ModelSim。 当SOPCBuilder为系统级验证产生一个测试平台,仿真环境性能主要取决于系统中包含的元件。
* @6 y# N1 M5 q( z8.2.5 设计实例:脉宽调制器从外设
* E; d+ d, }% G: k" @( ?7 U4 H        本节介绍脉冲宽度调制器(PWM)的设计实例,来介绍在系统中创建和例化元件的步骤。 5 F, c! h: L- P9 i
该元件只有一个Avalon从端口。
3 r$ [4 N' l* n9 E: b$ g6 y: [下面将介绍如下的步骤: 1 a& z$ c+ i0 S- ~: d6 X

+ `+ C2 O% Y- p: ?0 @
  • 安装设计文件。
  • 查看设计实例的说明。
  • 将设计文件打包成一个SOPC Builder元件。
  • 在硬件上例化元件。
  • 在Quartus II中编译硬件设计,然后下载设计到目标板上。
  • 使用Nios II软件测试硬件。
1. 安装设计文件
9 F: Q6 x% ^. x; }7 p9 ]+ N            首先安装Nios II 开发工具,并且从Altera网站下载PWM设计实例。本节中使用的硬件设计是基于Nios II开发工具包中的standard硬件设计实例。当安装设计文件时,不要在目录名中使用空格。如果路径包含有空格,SOPC Builder可能不能访问设计文件。 * _# c4 f: V0 C! ~+ O1 ^! A) W
建立设计环境要做如下的工作: + p4 ?' k' f( b% m0 T" i% K1 W- |
1. 解压PWM 压缩文件到一个目录。本节用<PWM design files>目录来表示该目录。 # {+ j" U4 B2 T; M$ A
2. 在用户的本地计算机文件系统中,进入目录:<Nios II kit path>/examples/<verilog or vhdl>/<board version>/standard。每个开发板都有VHDL和Verilog版本的设计。
# ]& Z0 \# T9 `- h4 S0 n3. 拷贝standard目录到一个新的位置。 这样可以避免, 破坏原始的设计。 本节通过<Quartus II project> 目录来指代该目录。
: L! L) `3 ~, E4 K0 r6 U/ y3 N4 s2. 查看设计的说明 : V1 Y1 b  J; g
本部分讨论PWM设计实例的设计说明,会给出如下主题的细节:
/ c7 f$ u0 E" ]6 p, G  I, {6 Y7 D6 G$ g2 `& V* V
  • PWM 设计文件。
  • 功能说明。
  • PWM任务逻辑。
  • 寄存器文件。
  • Avalon接口。
  • 软件API。
典型的设计流程中,是由设计者来制定元件的行为。
: X; {. m( N7 j7 YPWM设计文件 3 `3 B+ s$ _3 X( L5 O6 c- \
表8-4列出了<PWM design files>目录中提供的内容。 $ d  U5 x6 }+ Y0 D% Q1 W
功能说明
, \/ o4 h( b2 K8 H+ u/ {1 U3 yPWM元件输出调制占空比的方波,基本的脉宽波形如图8-17。
本例中的PWM的功能有如下的要求:
  m2 |9 J( c( L
  • 任务逻辑按照一个单时钟同步工作。
  • 任务逻辑使用32位的计数器提供PWM周期和占空周比的一个合适的范围。 主处理器负责设置PWM 周期和占空比的值。 这就需要对控制逻辑有一个读/写接口。
  • 寄存器单元来保存PWM周期和占空比。
  • 主处理器可以使用使能控制位来停止PWM的输出。
PWM 任务逻辑 . E' d/ u. p7 m. G5 k* Y# y2 L
  • PWM的任务逻辑具有如下的特征:
  • PWM任务逻辑包含一个输入时钟(clk),一个输出信号(pwm_out),一个使能位,一个32bit的模n的计数器,一个32位的比较器。
  • clk驱动32位的模n计数器建立pwm_out信号的周期。
  • 比较器比较模n计数器的当前值与占空比来决定pwm_out的输出。
  • 当计数器的当前值小于或等于占空比的值,pwm_out输出逻辑0;否则输出逻辑1。
图8-18给出了PWM任务逻辑的结构

4 S0 u/ D) h3 i+ H& A: `        寄存器文件提供对使能位、模n的值和占空比的值的访问。如图8-18。 设计将每个寄存器映射到一个Avalon从端口地址空间中唯一的偏移地址。
! Q, F# n* L$ a每个寄存器具有读和写访问,这意味着软件可以读取之前写入寄存器中的值。这种选择是以牺牲硬件资源为代价来获得软件上的便利。用户也可以设计寄存器为只是可写的,这会节省片上的逻辑资源,但是软件就不能读取寄存器的值。表8-5给出了寄存器文件和偏移量映射。为了支持3个寄存器,要求两位的地址编码。这将导致第四个寄存器为保留。
要读写寄存器只需要一个时钟周期,这影响Avalon接口的等待周期。
) K& Y5 E# v7 ^  [Avalon接口 & w6 W) f* A/ b5 W7 |1 b
PWM元件的Avalon接口需要一个从端口,使用了Avalon信号中的一个小的信号集合来处理寄存器的读和写的传输。元件的Avalon从端口具有如下的特性:
' V! Q1 l, l$ h2 g6 a
  • PWM从端口是与Avalon从端口时钟同步的。
  • PWM的从端口是可读和可写的。
  • PWM从端口的读写传输具有零等待周期, 因为寄存器能够在一个时钟周期内相应传输。
  • PWM的从端口读写传输没有建立时间和保持时间的要求。
  • PWM从端口没有读延迟的要求,因为所有的传输可以在一个时钟周期内完成。
  • PWM从端口使用本地地址对齐方式,因为从端口是连接到寄存器而不是存储设备。
表8-6列出了实现传输属性需要的信号类型,也列出了在HDL文件中定义的信号名
软件API ! y8 j1 G) D6 g, b! `) R
PWM设计实例提供定义寄存器映射的头文件和PWM在Nios II 处理器系统中的驱动程序。表8-4列出了这些文件,表8-7给出了驱动函数。
3. 打包设计文件为SOPC Builder元件 0 K( a/ i* p! i# z. ^
        这部分,用户使用SOPC Builder元件编辑器将设计文件打包成一个SOPC Builder元件。用户要执行如下的操作: : U6 O2 a* ]6 F/ n' F9 g4 c
(一)打开Quartus II 工程,启动元件编辑器。 7 k4 k" D6 Q" X9 H& c( F
(二)配置元件编辑器每个页面的设置。 3 l/ A2 @' V( Z4 y& F
(三)保存元件。   H# c& J8 ?6 A! Z7 O( N; t2 j
(一)打开Quartus II 工程,启动元件编辑器。执行如下的步骤: 0 Y4 A6 ^. \/ {$ `; {$ h0 i
1. 启动Quartus II软件。 ; H) {4 ~" u$ t8 y
2. 打开<Quartus II project>目录中的 standard.qpf文件。 : C1 r- P5 G, u$ ^; q% y% Y2 g
3. 选择Tools菜单中的SOPC Builder。出现SOPC Builder图形用户界面,显示一个现成的包含一个Nios II处理器和一些元件的设计实例。
- }1 N2 D6 H$ {6 s! h- d2 Y4. 在File菜单中选择New Component。 元件编辑器的图形用户界面出现, 显示的是Introduction页,如图8-19。在Introduction页面中,介绍了元件编辑器的工具,创建元件需要的文件,将元件用于其它工程的方法,以及获得元件编辑器更多细节的方法。
(二)配置元件编辑器每个页面的设置。 2 [- k: b: M9 A5 |4 R
a.HDL文件页面
0 ^; L) s6 }7 Z9 ^8 m& r8 X$ y% V      这部分是将HDL文件同元件联系起来。执行下面的步骤: 2 R  d& o' \& K5 Y, }0 J
1. 在图8-19中点击HDL Files页,出现图8-20的页面。元件编辑器的每个页面上都提供在屏幕的信息,介绍怎样使用每一个页面。点击页面左上角的三角形来查看使用指导。
. B7 d/ m4 b8 s6 c9 H: D0 j2. 点击Add HDL File。
; W; m6 Y* G8 A1 ?3. 切换到<PWM design files>/pwm_hw目录。三个Verilog的HDL文件存放这个目录。
: V2 y2 f! m: i/ |# p4. 选中这三个HDL文件,点击Open。 返回HDL Files页。元件编辑器立即分析每一个文件,从每个文件读取I/O信号和参数信息。
2 f. [  p. V5 {2 W5. 确保所有文件的Simulation和Synthesis复选框都已被选中,如图8-21。这表示每个文件都适于仿真和综合的设计流程。 / j8 H( M# r, d9 _  ]' W
6. 选择pwm_avalon_interfave.v: pwm_avalon_interface在Top Level Module下拉框中,来指定顶层模块,如图8-21。此时,元件编辑器图形用户界面会显示错误消息,暂时忽略这些信息,后面的步骤会解决这些问题。 8 b: J. J9 r! C( U* g7 f. J, l
b. 信号页面
4 v1 O' d% r5 G1 f        对于顶层HDL模块中的每个I/O信号,用户必须将其信号名映射到一个有效的Avalon信号类型。这些工作在Signals页中进行。元件编辑器自动填写其在顶层HDL文件中发现的信号信息。如果一个信号名同Avalon信号类型同名(如write或address),则元件编辑器自动分配信号的类型。如果元件编辑器不能判断信号类型,则将信号分配成export类型。执行如下的步骤进行元件I/O信号的定义:
- p7 I3 y) x# I6 n; j  ?$ r1. 点击Signals页,顶层HDL模块pwm_avalon_interface中的所有I/O信号自动出现。
/ M* Q) B( H/ D3 ]% V- U2. 如图8–22所示来分配所有信号的类型。要改变某个值,点击Signal Type单元格,显示一个下拉列表,选择一个新的信号类型。在图8-22中,将pwm_out信号分配为export类型,因为它不是Avalon信号。它是SOPC Builder系统的一个输出信号。当正确地分配每个信号类型之后,错误信息就应该消失了。
c.接口页面
' \6 i  B" ]' x接口页使用能够户配置元件上所有的Avalon接口的属性。PWM元件只有一个Avalon接口。执行下面的步骤进行Avalon从端口的配置: 3 g6 j# c2 w* [8 O+ R5 I$ D3 ^
1. 点击Interfaces页 tab。元件编辑器显示一个默认的Avalon从端口,该端口是元件编辑器基于元件设计的顶层I/O信号创建的。 ( N$ `& ^( ]  q% |: A: C, X; Q
2. 在Name域中键入control_slave重命名该从端口,当用在SOPC Builder中户例化该元件时,该端口名出现在SOPC Builder 图形用户界面中。 . D; S) ]: j! W  f* N, n) C
3. 如表8-8所示修改control_slave接口的设置。图8-23给出了正确的设置的Interfaces页。
d.软件文件页
. j% _8 D. Q" O( |8 M+ ]        SW Files页使用户将软件文件同元件联系起来,并指定它们的使用方法。PWM设计实
/ Q6 K2 L. k6 E1 C7 H4 z% \0 Q例提供了定义寄存器映射的头文件和驱动程序。执行下面的步骤将软件文件引入到元件中: $ x9 J7 R1 T6 w$ `' K( E
1. 点击SW Files页。 4 J/ M+ }0 U7 J3 j
2. 点击Add SW File,出现打开对话框。 & Z: \  s, B( X; c7 D
3. 切换到<PWM design files>/pwm_sw/inc目录。
' J" x3 A) _8 x6 N( X, A, t5 n4. 选择altera_avalon_pwm_regs.h文件,点击Open。 9 H% O3 I2 i, A  n! n2 b
5. 点击altera_avalon_pwm_regs.h的Type单元格,改变文件类型。下拉列表中,选择Registers (inc/)。 # I/ o8 ~9 n. A
6. 重复2~5的步骤,添加<PWM design files>/pwm_sw/HAL/inc/altera_avalon_pwm_routines.h文件,设置其为HAL (HAL/inc/)类型。 $ a, ?+ u: A  e$ V3 f
7. 重复2~5的步骤,添加<PWM design files>/pwm_sw/HAL/src/altera_avalon_pwm_routines.c文件,将其设置为HAL (HAL/src/)类型。
- F7 }3 t) X# t, k* P1 [$ B% ~图8–24显示了SW Files页的正确的设置。
e.元件向导页 " C) g( |4 F% s! O% y
元件向导页允许用户控制在SOPC Builder例化元件时,添加向导的样式。执行下面的步骤配置元件向导的样式:
, o! z; _2 \  \; f. Q" M* E1. 点击Component Wizard页。
2. 对于本例,不改变元件名,元件版本和元件组的默认值。 8 {6 v6 n. J5 f9 E3 |( k
3. 在Parameters栏中,在clock_divide_reg_init的Tooltip单元格键入:Initial PWM Period After Reset。
0 C4 ]* m' R( y- d+ {# B. Z4. 在clock_cycle_reg_init的Tooltip单元格中键入: Initial Duty Cycle After Reset。 如图8-25
, L5 ]/ b# `. W+ b: R  U/ I( V5. 点击Preview the Wizard 预览元件SOPC Builder例化时的元件向导的样式,如图8-26 所示。
6. 关闭预览窗口。
(三) 保存元件 5 H* `( C' |) V4 B2 r; x
执行下面的步骤保存元件,并且退出元件编辑器:
# p4 T3 E! g) T8 ?  X6 i1. 在图8-25中点击Finish,出现对话框,提示为元件创建文件,如图8-27。 # O/ Y* _* ^+ v' A( `2 f- \. V
2. 点击Yes保存文件。元件编辑器保存这些文件到<Quartus II project>目录中一个子目录。元件编辑器关闭,返回SOPC Builder图形用户界面。 * p5 w. F: e* {! I& f
3. 在Unkown组中可用的元件列表中找到pwm_avalon_interface新元件。用户可以在SOPC Builder系统中例化该元件。
4. 例化元件
+ }8 W1 I5 D$ R; s        此时,新的元件已经可以在SOPC Builder系统中例化了。元件的使用是和设计相关的, 是基于系统的需要的。剩下的步骤演示例化和测试元件的一种可能的方法。然而,该元件在系统中使用方法数目是没有限制的这部分用户将添加新的PWM元件到系统中,重新编译硬件设计,重新配置FPGA。包括如下的步骤: * l: U( B+ ^- o! ?/ v; p
1. 添加一个PWM硬件到SOPC Builder系统,重新生成系统。
" e6 h$ O# }+ E/ ~9 E  A$ a1 O0 ]9 x2. 修改Quartus II设计,连接PWM 输出到FPGA的一个引脚。
2 X, o; X0 {5 C; n( \, u3. 编译Quartus II设计,使用新的硬件镜像文件配置FPGA。 & k' B- c6 w7 I6 t5 v/ g5 s$ W
添加PWM元件到SOPC Builder系统,执行下面的步骤建立SOPC Builder的元件搜索路径:
* g6 |' }2 u' V9 H/ Z! x% l1. 在SOPC Builder图形用户界面中,在File菜单中选择SOPC Builder Setup。
( T. M, P2 E+ {2. 在Component/Kit Library Search Path中输入<Quartus II project>目录的路径。如果已经有一些路径存在,使用"+"来分隔这些路径。如图8-28所示。 ) Z3 A: L! `( {
3. 点击OK。
  R9 C) M3 ^/ E0 \. q上面的这些步骤使得元件的软件文件对Nios II IDE可见。 这些步骤对Quartus II 软件v4.2和Nios II IDE v1.1是必须的。
执行下面的步骤将PWM元件添加到SOPC Builder系统: ) g  l2 @: |0 ?3 E  H, ~
1. 在SOPC Builder System Contents页,在Unkown组中选择pwm_avalon_interface,然后点击Add。PWM元件的配置向导出现,如图8-26。如果用户需要,可以修改配置界面中的参数。参数会影响PWM控制寄存器的复位状态。 2 y- ^# C( }* ^2 H
2. 点击Finish。返回到SOPC Builder System Contents页,元件pwm_avalon_interface_0出现在活动的外设的列表中。
0 O  F" `1 u! ^% g  ]3. 右键单击pwm_avalon_interface_0 , 选择Rename, 键入z_pwm_0 的名字, 然后按Enter键,如图8-29。
. F6 D1 D6 q9 o2 p4. 点击Generate开始生成系统。
2 R# R' v! F& \2 E8 o$ {* H5. 系统生成成功之后,退出SOPC Builder,返回到Quartus II。
5. 在Quartus II中编译硬件设计,然后下载设计到目标板上。
6 U6 w! y( j+ D+ a  Z0 `( n* F        此时, 用户已经创建了一个使用PWM元件的SOPC Builder系统, 用户必须更新Quartus II工程才能使用PWM 的输出。.   B; C* D% x) w
standard.bdf文件是顶层的原理图设计文件,BDF包含SOPC Builder系统模块的一个符号,名字为std_<FPGA>,这里<FPGA> 代表的是目标板上的 FPGA,如std_2C35。在之前的步骤中,用户添加PWM元件,则系统模块会产生新的输出。所以要更新系统模块的符号,并将PWM 的输出连接到FPGA的一个引脚。
8 j. z3 u4 a2 i; y1. 在Quartus II中,打开standard.bdf文件。 + Y' B/ j" N  e& m; i, h
2. 在standard.bdf中右键单击符号std_<FPGA> ,然后选择Update Symbol or Block。更新符号或模块的对话框出现。
7 ?, H: n7 Z; q+ v- M8 K3. 选择Selected Symbol(s) or Block(s),如图8-30所示。
0 q# Q6 U1 _- S" m, d0 _4. 点击OK关闭对话框。std_<FPGA> 符号已经被更新了,现在已经有了一个新的输出端口名为pwm_out_from_the_z_pwm_0。SOPC Builder为所有系统模块上所有的I/O端口创建唯一的名字,名字是将元件设计文件中的信号名同系统模块中元件的实例名。 3 w/ ~: u5 A9 c. q
5. 删除连接到port out_port_from_the_led_pio[7..0]端口的引脚。 ! X8 [& v6 I: F
6. 创建一个新的引脚命名为LEDG[0]。 $ `/ ^1 |2 N' S9 p9 K
7. 将新的引脚连接到pwm_out_from_the_z_pwm_0端口。现在可以对硬件进行编译了。
编译硬件和下载到目标板的步骤在第二章已经讲解过,这里就不做赘述了。
: ~* R6 H) e) h+ f6. 在Nios II软件中使用硬件 # `7 u6 P! L6 x, E" W$ j4 Z
        PWM设计实例是基于Nios II处理器,用户必须在Nios II处理器中执行软件来验证PWM硬件。设计实例的设计文件提供了C语言的测试程序,该层序使用PWM的输出点亮LED,该程序会让PWM占空比渐变。测试程序访问硬件使用寄存器映射和调用驱动函数的方式。关于软件工程的创建、 源文件和头文件的引入以及程序的调试和下载等内容在第六章已经介绍过了,这里就跳过了。程序的运行会使开发板上的LED重复的亮灭,但是周期是渐变的。
8.2.6 共享元件
- h6 N2 h( R1 k        当用户使用元件编辑器创建了一个元件, SOPC Builder在当前的Quartus II工程目录中自动地保存该元件。为了促进设计的重用,用户可以在不同的工程中使用元件,用户也可以将自己的元件同其它设计者分享。
1 ?9 k6 F7 {4 N. X执行下面的步骤,可以分享一个元件:
' I' Z1 n  f% v7 W5 Q1. 在用户的计算机文件系统中, 将元件的目录移到一个Quartus II 工程目录之外的一个位置。例如,可以创建一个目录c:\my_component_library 来保存用户的定制元件。目录路径中不能包含空格。 4 }* F6 C- G" v8 D5 C
2. 在SOPC Builder中,在File菜单中选择SOPC Builder Setup。出现SOPC Builder Setup对话框, 在该对话框中用户可以指定SOPC Builder到哪去找元件的文件。 5 Q$ L9 h; j: Z$ V
3. 在Component/Kit Library Search Path里添加包含元件目录的目录。例如,元件的目录为c:\my_component_library\pwm_avalon_interface\,加入路径c:\my_component_library。如果已经有路径存在,使用"+"来分隔路径名,如图8-28所示。 : e7 A, V; N1 J/ |) u
4. 点击OK。

* i2 r3 O* J6 h) U  w5 l" o
6 C9 W  ^- l6 D[ 本帖最后由 kenson 于 2009-1-2 20:12 编辑 ]
 楼主| 发表于 2009-1-2 17:50 | 显示全部楼层

C2H Compiler 的使用

Nios II IDE Quartus ( [* J6 d/ J8 {5 K! i% Q" J
Nios II C-to-Hardware加速(C2H) 编译器是使得用户可以直接从ANSI C源代码创建定制外设加速器。硬件加速器是在硬件中实现C函数的逻辑模块,通常可以提高执行效率一个数量级。使用C2H编译器,用户可以在以Nios II 处理器为目标处理器的C程序中开发和调试一个算法,然后将C代码转化成在FPGA实现的硬件加速器。
% k5 J/ z4 d3 R7 w7 o* ]6 M. N        C2H编译器通过将特定的C函数实现为硬件加速器,来改善Nios II程序的性能。C2H编译器不是将C作为设计语言来产生任意的硬件系统的工具。 C2H编译器建立在下面的前提之上: * [  p" b; ~- v! ]- ~" S
# x4 G7 m' v& u
  • ANSI C的语法足以描述计算密集或是存储器访问频繁的任务。
  • C-to-hardware工具一定不能打乱已有的软件和硬件开发流程。
基于以上的前提,C2H编译器的设计方法论提供下面的特性:   Q5 n! w# d' B9 t6 j0 T1 w1 w2 p
! B9 I" E1 M& Y! r! Y0 w2 _9 o
  • 兼容ANSI C—— C2H编译器对ANSI C代码进行操作,支持大部分的C构造,包括指针、数组、结构、全局和局部变量、循环和子函数调用。C2H编译器不需要特殊的语法或者库函数来指定硬件的结构。
  • 直接的C到硬件映射—— C2H编译器将C语法的每一个元素映射成一个定义的硬件结构,赋予用户对硬件加速器的结构的控制。
  • 同Nios II IDE集成——用户通过Nios II IDE控制C2H编译器,不必去学习使用C2H编译器的环境。
  • 基于SOPC Builder和Avalon交换架构—— C2H编译器使用SOPC Builder将硬件加速器连接到Nios II系统中。C2H加速器成为一个Nios II系统中的一个元件。SOPC Builder自动地产生Avalon交换架构将加速器连接到系统,省去了手动集成硬件加速器的时间。
  • 报告生成的结果—— C2H编译器产生一个详细的报告:包括硬件结构、资源使用和吞吐量。
C2H编译器生成的硬件加速器有下面的特性: ! m- u# {$ K5 Y2 {6 r4 z8 n) c
2 r; s9 X$ d  U& E
  • 并行时序—— C2H编译器认可并行发生的事件。独立的声明在硬件上是同时执行的。
  • 直接存储器访问—— 加速器可以同时和Nios II处理器访问的相同的存储器。
  • 循环流水线—— C2H编译器基于存储器访问延迟和并行执行的代码大小,将循环的逻辑实现流水线化。
  • 存储器访问流水线—— C2H编译器采用流水线来访问存储器以降低持仓前延迟的效应。
8.3.1 C2H 概念
5 m9 ]  R( v! ?+ V. ^        这部分介绍一些支撑C2H编译器的基本概念。这些概念帮助读者更好地理解C2H编译怎样工作,以及怎样来获得更好的结果。
2 c4 U1 U" W) ^( }, S  p1. 简单和易于使用C2H编译器将对现有的设计流程的影响降到最小。 生成硬件加速器的流程和为加速器连接软件使用熟悉的Nios II IDE和 SOPC      Builder设计工具。在Nios II IDE中编译的时候,用户可以指定是否一个C函数是作为处理器的指令还是硬件加速器进行编译。C2H编译器在后台 $ p9 [; M$ K2 I' c
调用其它的工具来处理硬件和软件的集成工作。特别地,C2H编译器在后台自动执行那个下面的任务:
) B* b0 ~4 U- _0 f$ M1. 调用SOPC Builder指定硬件加速器怎样连接到系统,然后生成系统硬件。 , I& {$ ~6 u2 O: p- I* T
2. 调用Quartus II 编译硬件设计,生成FPGA配置文件。 5 A3 x7 j6 J( l- F5 G1 y
2. 快速的反复设计以找到最优的软硬件分配比例 C2H编译器允许用户在C代码中方便地更改硬件和软件的分界线,不需要很多的额外的 7 J0 [. ~! i8 w6 y9 w) M) y+ `' O  M8 \
        设计工作。因此,用户可以很自由地进行重复的设计,实验多种结构。使用HDL文件来编写硬件加速器则需要很多的时间去创建逻辑设计和将加速器集成到系统中去。 功能和性能需求的改变将会对设计实践产生很大的影响。
+ J0 R; @, @3 Y        使用C2H编译器,用户可以加速必要的函数来获得需要的性能。用户可以通过简单的编辑C的源代码来平衡性能和资源的使用。通过这些可用的工具, 获得渴望的系统性能的过程经历了深远的改变。 设计时间从创建、接口、调试硬件向完善算法实现和寻求最优的系统结构倾斜。 : n' Z( r3 u* ~; i* I! q
3. 加速对性能影响大的代码
" j7 l) g; v8 kC2H编译器只转化用户指定的代码。 典型的程序中包含对性能影响大的代码以及其它的代码。对性能影响大的代码通常是重复和简单的,但是消耗了处理器执行的大部分时间。这 些代码占用处理器来计算数值或移动数据,或进行以上的两种工作。硬件资源最好用来加速对性能影响大的函数,而不是将整个程序转换成硬件。 ) ]+ r$ f* S+ L+ Y
4. C2H 编译器工作在函数级 0 f) O; d& }" y
        用户想要加速的代码必须是一个独立的C函数。C2H编译器将选中的函数内部的所有代码转化成硬件加速器模块。如果加速的函数中调用了子函数, C2H编译器也将子函数转化成硬件加速器。因此,用户必须注意,是否子函数也是适合C2H加速的。如果用户想要加速的代码,不再一个函数中,那么要将这些代码放到一个函数中去。转换成的硬件加速器只完成处理器密集的任务, 而不是处理器可以高效实现的设置和控制等任务。
; r. w+ g" h7 ^; Z% l$ J( @5. 系统结构 1 y5 h9 D+ Q# P+ {! Z
图8-31显示了一个简单的Nios II 处理器系统的结构,其中包含一个硬件加速器。
/ k: j/ t1 r0 ]7 T) @6 r, k! Z/ R        SOPC Builder自动将加速器逻辑以SOPC Builder的元件集成到系统中。系统中可以有多 个加速器。 加速器是和Nios II处理器分离的, 但是可以访问Nios II处理器可以访问的存储器。用户能够在SOPC Builder中.手动配置加速器和系统的连接。 ( }* G7 P% j, o7 I+ D0 O
6. 硬件加速器的生成 1 R+ V. ~% n8 X8 a% y! s! m& }
        C2H编译流程同传统的C编译器有很多共同的特征,但是声明的时序、优化和对象生成是不同的。当生成硬件加速器时,C2H编译器做如下的工作:   N3 ?, ~9 `' p2 L1 V, h! E% M
1. 使用GNU GCC预处理器分析代码。
9 S: n$ R9 R; _% G2. 创建数据关联图。
  ]" x# u7 h7 Z4 \3. 执行相应优化。 : D$ X) R2 p1 O& s7 j% W+ n2 F1 }
4. 确定执行每项操作的最佳次序。 7 t& {! W/ c, V+ `5 f
5. 生成硬件加速器的目标文件。该目标文件是可综合的HDL文件。 # a( L0 \" ?4 _2 K5 }
6. 生成C打包函数,该函数隐藏了Nios II处理器同硬件加速器之间交互的细节。该函数在软件连接的时候替换原来的词C函数。
9 r% @$ B$ l+ a- H, X生成的加速器逻辑包含如下内容: # }* H2 E* I3 b$ I, V& y" {

& \- {2 \' r6 q+ q4 J! ^1 U
  • 一个或多个状态机用来管理C函数定义的操作的执行顺序。在任何一个时钟周期,任意数目的计算和存储器访问可能同时发生,这是由状态机管理。
  • 一个或多个Avalon主端口,用于读取和存储状态机需要的数据。一个Avalon从端口和一组存储器映射寄存器,允许处理器设置、启动和停止硬件加速器。
7. 从C语法到硬件结构的一对一映射 ) i; G3 E2 r. L3 {8 y* U. `, ^
        C2H编译器将C语法的每一个元素映射为一个对等的硬件结构,C2H编译器使用直接翻译法则,基于输入的C代码直接例化硬件资源。一旦熟悉了C2H编译器的映射,用户可以通过改变C的源程序来控制生成的硬件结构。 2 w: O0 q0 t2 b. H: V: Y1 k
下面是C2H编译器转换C到硬件的例子:
- r4 c' B4 f/ {9 L& ~6 V3 h
/ q, }. h0 f4 H/ a5 l2 P
  • 数学运算符(如+、-、*、>>) 转换成硬件的等效电路(如加法、减法、乘法和移位电路)。
  • 循环(如for、while、do-while)转换成重复循环中的操作的状态机,知道循环条件不满足。
  • 指针和数组访问(如*p、array[j])变成Avalon主端口,主端口和处理器访问相同存的储 器。
  • 不依赖之前的操作的结果的声明,被尽可能得向前调度,以允许最大程度的并行执行。
  • 加速函数调用的子函数也使用相同的映射规则被转换成硬件。C2H 编译器只创建一个 子函数的实例,不管子函数被调用多少次。将被加速的C代码放到一个子函数中,提供 了一个在加速器内部创建共享的硬件资源的方法。
        当C2H编译器基于资源共享可以减少资源的使用时,C2H编译器执行某些优化。参阅 Nios II C2H Compiler User Guide中的C-to-Hardware Mapping Reference章节获得更多的C2H 编译器映射细节。 - m4 j& [9 d' `- R7 z/ {& g4 N( E
8. 性能依赖存储器访问时间
6 `7 [' \' ^% C- M        在处理器上运行的应用的性能瓶颈依赖处理器执行指令的速度。 存储器访问时间影响执行时间,但是指令和数据缓存最小化处理器等待存储器访问的时间。借助C2H硬件加速器,性能瓶颈发生了深远的改变。应用的性能瓶颈主要取决于存储器延迟和带宽。硬件加速器逻辑要给每个并行操作提供数据,如果硬件不能快速访问存储器,硬件将暂停等到数据,从而降低性能和效率。
  g2 h3 q7 d! `. a# r& c) n        要从硬件加速器获得最高性能通常包括检查系统存储器拓扑和数据流, 然后进行修改以减少或消除存储器瓶颈。例如,如果用户的C代码随机访问存储器在低速SDRAM中一个大的缓冲器的中数据,性能会因为在SDRAM中不断的地址切换而降低。用户可以减轻这种瓶颈,首先将数据拷贝到片上RAM,然后允许硬件加速器访问快速、低延迟的RAM。注意用户可以创建DMA硬件加速器来加速拷贝操作。
6 n2 {* `  Q- A0 f8.3.2 适合硬件加速的 C 代码
5 K* g4 J1 J+ C9 t2 r1 p        本部分讨论判断C代码是否适合C2H编译器进行加速的指导方针。
6 }& ?' e( \) Z1. 理想的加速候选
9 x% P  h8 a4 y( r) U' C最小的代码量而消耗了CPU的大部分时间的C代码是最佳的加速候选。这些代码有如下特性: ! b$ f- J: x0 `5 S& d
/ P0 T: x7 j) h
  • 包含相对小而简单的循环或是包含嵌套的循环。
  • 对一组数据反复操作,每次对数据进行一个或多个操作,然后存储结果。
        这种重复任务的例子包括存储器拷贝和修改、 校验和计算、 数据加密、 解密和滤波操作。在以上的情形中,C代码对一组数据重复很多次操作,在每次重复过程中,要执行一或多个存储器读或写操作。 ' I/ N0 \, U# k2 g
下面的例子演示了一个执行校验和计算的函数,这段代码摘录自TCP/IP协议栈,校验和计算的数据范围是网络协议栈的数据。校验和计算是IP协议栈中典型的耗时的部分,因为所有的接收和发送的数据必须要被验证,这要求处理器循环计算所有的字节。 % a* C& L! P6 }( I+ C9 J6 {% v
例:校验和计算
" r1 C5 \% d- R1 k! Ku16_t standard_chksum(void *dataptr, int len) 2 m8 b5 l) y- D1 H
{ 3 [' ]* f# x( u; a9 f* M5 Z
u32_t acc; ! I$ v8 n2 j, `) A4 }- {, r0 B0 o5 E
/* Checksum loop: iterate over all data in buffer */ 7 c- f8 m& ~) L% g# |% j6 t6 L
for(acc = 0; len > 1; len -= 2) { ) J. k! E* x6 w, z3 ]; f( d
acc += *(u16_t *)dataptr; ( n! P, E  v5 R5 \
dataptr = (void *)((u16_t *)dataptr + 1); , B  w' T" |, j
} ! J7 m" k+ f) t: [# p0 |  [. V
/* Handle odd buffer lengths */
& y; R  Z2 H$ H2 g$ [if (len == 1) { # q) s% F0 q; P! }$ Z
acc += htons((u16_t)((*(u8_t *)dataptr)&0xff)<< 8); 2 _& ^2 q; n6 V( W+ w7 j9 c
} - D4 T- F) l/ z+ T  i2 x6 M. [
/* Modify result for IP stack needs */ 3 E. G9 o& h* U/ O  R
acc = (acc >> 16) + (acc & 0xffffUL);
( R) J! n# D3 m, e! M& O) Q# Gif ((acc & 0xffff0000) != 0) {
  S5 {& O! M' h0 a) ?$ b/ aacc = (acc >> 16) + (acc & 0xffffUL);
& S1 ]  L+ o0 S} - I, N1 x; b9 k( ]
return (u16_t)acc;
" p. [0 F% S8 C}
: ]' D) P4 W8 k# E" G/ m& ~加速上面的函数会对计算时间有很大的影响,尤其是花在循环上的时间。其余的代码每次调用执行一次,加速循环外的这些代码作用不大,除非standard_chksum()函数被另一个函数调用, 而那个函数也是一个很好的加速候选。 这段代码最有效的硬件加速器应是只替代for循环部分。想要只加速for循环,需要将循环部分定义成一个单独的函数。
. E  A2 B. k1 u3 D2. 不好的加速候选 , ^3 a; K: a3 u( K  m$ ?' Y/ _; _
        加速某些代码会对性能产生负面的影响,或者不可接受地增加资源的使用,甚至以上两种同时效果出现。
( i4 }% Z% c+ H  [- ]5 m6 _( M使用下面的指导来确定不对其进行加速的函数:
6 p/ r3 `( m; b# F; k
7 F  J9 ^. E4 N! M
  • 包含很多顺序的操作,不能形成一个循环的代码,不是加速的好候选。处理器可以高效 地执行这样的操作。
  • 如果代码包含C2H编译器不支持的语法的话,代码不能被加速。例如浮点运算和递归函 数。更多信息请参阅Nios II C2H Compiler User Guide中ANSI C Compliance and Unsupported Constructs章节。
调用系统和运行时库函数的代码是不好的加速候选。例如,加速printf()或malloc()函数没有好处。这些函数的代码中包含一组复杂的顺序操作,不包括对性能大的循环。也有一些例外情况,例如:
0 ?3 B% B/ t% B9 H
* D" U+ s& J7 Q- K1 }
  • 一些有经验的C程序员经常会将迭代的算法打开,实现为一组顺序的操作,为了能更好地配合C编译器工作。如果用户能够重新组合代码将其形成循环,那么对代码加速会有很好的性能提升。
  • 一个内部的循环可能包含很多复杂顺序操作,如果对其加速会消耗很多逻辑资源。这就出现一个权衡,如果处理器在该循环花费了不可接受的时间,可能就值得使用硬件资源来加速整个循环。
  • 一些运行时库函数本质上是迭代的。这样的函数包括普通的数据搬移和缓冲器集合函数,如memcpy()或memset()。
3. 理解代码以发现加速的良机
( B* R/ O1 T3 r2 `/ h" X        使用C2H编译器获得最优的结果的最好的方法是理解自己的代码, 去发现最关键的循环在哪里。如果用户从头自己编写代码,用户很可能会直到代码的关键部分。如果用户从已有的代码开始,使用C2H 编译器获得的性能提高的程度主要取决于用户分析和理解代码的程度。不管哪种情况,Nios II IDE的概况(profiling)特性可以帮助用户确定处理器在何处花费了大部分时间。 只是通过观察代码来确定关键的循环是很困难的, 因为程序经常指示在几行代码上花费了大部分的时间。 能够精确定位处理器在哪部分代码花费了大部分时间的唯一方法是对应用
  y# m6 H  w! ^- n做概况分析,然后检查瓶颈函数。参阅Altera的文档AN 391: Profiling Nios II Systems来获得更多信息。
4 p7 i6 K2 l# Y; y4 ?8.3.3 C2H 编译器设计流程
, t5 m) R& m  x) w1 v( N        这部分讨论Nios II C2H编译器设计流程。会结合一个设计实例,一步一步地介绍创建自己的硬件加速器的过程。 设计实例的软件多次执行数据拷贝的函数。通过加速数据拷贝函数,可以获得10多倍的性能改进。获得的硬件加速器是具有DMA的硬件模块,可以在没有处理器干预的情况下拷
( J: Z2 x/ S7 }& B* s贝数据。C2H编译器的设计流程从一个或多个成功编译的C文件开始。在开始使用C2H编译器加速函数之前,用户必须:
8 J0 `; m4 z$ w& t9 L$ x. m% U0 }* A
! _3 `" Y- P* ^
  • 确认需要加速的函数。
  • 以Nios II处理器为目标处理器调试函数。
一、典型的设计流程
( ?0 k) g' z5 }7 F9 [6 p9 E% d使用C2H编译器来加速函数的典型设计流程包括下面的步骤: 5 Z: Y$ A5 `: D" p, K$ w
1. 使用C语言开发和调试应用或算法。 ) |2 E) Q- W/ f  w: {7 [: f' e/ K
2. 概况代码来确认要加速的部分代码。 3 x' f* T. g& C0 I/ W
3. 将想要加速的代码形成一个单独的C函数。 9 I1 u# A# j: h% c5 A  {% a' T8 a8 s5 a
4. 在Nios II IDE中指定要加速的函数。   O3 a; B6 [6 H- s) I" ?- E+ P
5. 在Nios II IDE中重新编译工程。 - s/ l/ G7 T9 o0 Z3 f- s
6. 概况(profile)结果。 " M* N4 R- z1 B+ B7 f
7. 如果结果没有达到设计要求,修改C源代码和系统结构(例如,存储器拓扑)。 & K4 @7 ~1 w1 h/ T% E, r0 r
8. 返回到步骤5,重复开始下面的步骤。
$ A. B9 w# t+ w. k4 |0 d( n. Y. Z典型的C2H编译器设计流程一个反复迭代的过程,比较性能是否达到设计要求,修改C 代码来改进结果。如果C代码没有针对C2H编译器进行优化,第一次加速迭代的结果不会很明显地改进性能。接下来的迭代,修改C代码以产生最优的硬件结构,通常最后的结果比第
/ R# m$ p2 p" _一次的结果有很大的改进。   F  P) U# E6 B9 F9 Q
二、软件要求 - }5 t6 p' Z. `+ P/ G
C2H编译器是Nios II Embedded Design Suite (EDS)一部分。需要用户安装的软件有Quartus II,用户可以从Altera网站下载Quartus II网络版和Nios II EDS的试用版。在设计过程中,会用到下面的工具: 5 T1 r5 n8 }, S& c0 A
; s+ W7 A5 i( k7 U& M" V6 t
  • Nios II (IDE)——控制函数加速的选项。加速的结果也在Nios II IDE中产生。生成的文件是可执行的文件 (.elf)。 C2H编译器在后台调用SOPC Builder和Quartus II重新生成Nios II系统和更新FPGA配置文件。
  • SOPC Builder——SOPC Builder管理C2H逻辑的生成和Avalon交换架构,将硬件加速器连接到处理器。在软件编译的过程中, Nios II IDE能够在后台调用SOPC Builder来更新更新硬件加速器和将其集成到Nios II硬件设计中去。输出文件是一组硬件描述语言文件 (.v或.vhd)和SOPC Builder系统文件(.ptf)。该系统文件定义了用户的系统:Nios II处理器内核、外设、加速器、片上存储器和片外存储器接口。
  • Quartus II software——Quartus II编译和综合由C2H编译器和SOPC Builder工具产生的HDL文件,还有Quartus II工程中的其它定制逻辑。在软件编译过程中,Nios II IDE在后台调用Quartus II 编译Quartus II工程。会产生FPGA配置文件(.sof),其中包含具有硬件加速器的更新的Nios II系统。
三、设计实例
: y4 L7 a' e* t2 ]* Q8 D- y2 M" L; A. j这部分通过设计实例来指导读者使用C2H编译器加速函数的过程。 用户将使用提供的设计实例的文件在Nios II IDE创建一个软件工程,加速一个函数,观察性能改进。从C的源文件开始,到包含加速的函数的应用运行结束。其中的步骤包括 ( A; r3 M1 L, W7 \2 n) ~
1. 建立硬件工程的硬件。 ' d8 ~9 O: }  d7 {0 t& l4 R
2. 创建工程的软件。
+ B  V: T& |+ v3. 只以软件来运行工程。
1 U3 P& c1 a4 o' \) a( D4. 创建和配置硬件加速器。
. I9 c+ u: v2 c" F. L7 ^& ?5. 重新编译工程。 ' \  o8 B1 i/ J( c2 v% H: D
6. 观察报告文件中的结果。 " M- y2 c0 x* B  P
7. 观察SOPC Builder中的硬件加速器。 ( `- {5 b2 N: X% h" N
建立硬件工程和软件工程的步骤在前面的章节已经讲过,在这里只是简单介绍。硬件的设计是基于Nios II EDS提供的standard硬件的设计。软件设计文件是C 文件,文件名为dma_c2h_tutorial.c,该文件可以从Altera网站下载。 用户可以在任何一款Altera提供的开发
- {" l. ], {+ R$ d; m  H板上运行该设计。dma_c2h_tutorial.c 文件包含两个函数: 7 P  u6 [0 e, |
7 ~. s$ U4 f% Y; p
  • do_dma() ——–该函数是用户将要加速的函数,它执行大块存储器拷贝。do_dma() 需要一个源地址指针,一个目的地址指针和一个拷贝的比特数。当以硬件实现该函数,do_dma()和DMA拷贝逻辑类似。
do_dma()的原型如下:
7 s4 s) ]3 u8 |( pint do_dma( int * __restrict__ dest_ptr, int * __restrict__ source_ptr, int length )
8 ~- U0 T' _% V" l! _# X
! x7 v8 H7 C0 P- P6 V. Z, {
  • main() ——main()调用do_dma()和测量需要多少时间,以便用户可以比较软件实现和硬件加速器。
main() 函数执行下面的工作:
, k7 }! ^8 B2 C1. 在主存储器中分配两个1 MB 缓冲。 0 G) w; M/ X7 D: K$ \& v
2. 用递增的数值填充源缓冲。
1 Y5 @# l9 s& J! q, u/ C3.使用全0x0填充目的缓冲。 % u+ w6 Z. y( E: S: p8 B
4. 调用do_dma()函数100次。 . Q8 Z; R' a- R: t6 x2 |
5. 检查拷贝的数据,确认没有错误。 0 d6 W( q6 i$ B" ?) k! J
6. 释放以上分配的两个缓冲。 ' `: N: Z/ @! h% T- K$ i
在调用do_dma()函数的循环附近有定时器函数来测量完成拷贝操作所需要的时间。 在应用完成之后,在IDE的控制台会显示执行拷贝操作用了多少毫秒。 ; k$ I0 s7 {6 {
1. 建立工程硬件
; U+ `4 P! e& ?9 j% d+ b建立工程硬件执行下面的步骤:
- u* G& _) {+ u- t; |; O1. 连接Nios开发板的电源,并且使用Altera的下载线将开发板与计算机相连。
# t- S( H$ G8 V& z5 C& v2. 建立硬件工程的目录。 ! G3 K) P" ?1 O/ o' ^  [
a. 找到用户开发板的standard 硬件设计实例,例如,Cyclone II的Nios 开发板的Verilog 文件,在<Nios II EDS install path>/examples/verilog/ niosII_cycloneII_2c35/standard目录。 7 m" Q& D/ A& H0 j! m
b. 拷贝standard 目录,并且将拷贝的目录命名为c2h_tutorial_hw。
- R, S: S9 X! U4 A: e5 B0 G: e9 j3 s3. 启动Quartus II。 . I5 W8 o- T: J/ a2 |
4. 打开c2h_tutorial_hw目录中的Quartus II工程standard.qpf。 / t7 u$ ?& T( [" A) m% E; _
5. 配置Nios开发板上的FPGA。
" F) e3 i; w9 P9 t+ _a. 在Tools菜单点击Programmer。Programmer中出现FPGA配置文件standard.sof。 0 z! j5 L. F7 ?
b. 选中Program/Configure复选框。.
. y/ o$ W6 H6 T5 r3 rc. 点击Start,下载配置文件到FPGA。 - h6 e9 u6 `: J1 A. _' H
2. 创建软件工程 . |% F, `/ |( c
建立软件工程执行下面的步骤: - |/ k6 m5 a* N, K
1. 启动Nios II IDE。
" K* @0 {- R' u1 ]9 @% I! [( T$ V2. 创建一个新的C/C++应用工程,工程名起为c2h_tutorial_sw,选择Blank Project工程模板,目标硬件选为c2h_tutorial_hw目录中的 SOPC Builder系统文件。点击Finish IDE产生一个新的工程c2h_tutorial_sw和一个新的系统库工程c2h_tutorial_sw_syslib。 & S0 \$ ~) i8 d; O
3. 从Altera网站下载软件文件dma_c2h_tutorial.c。 ( y" v+ M; C- e3 i. p, A  T1 x
4. 引入C文件dma_c2h_tutorial.c到c2h_tutorial_sw工程中,最简单的方法就是采用外部的文件管理工具,如Windows Explorer,将该文件移到Nios II IDE中的C/C++ 工程视图的
# ^' d2 w) v( t! _  D" Yc2h_tutorial_sw 文件夹中。   c8 c1 P. V/ e4 f  [
3. 只以软件运行该工程
7 ]5 T& s' F- R; ?" a5 d; `4 u运行该工程,执行如下步骤: + G- A3 i4 X$ C, k4 m/ {
1. 在C/C++ 工程视图中,右键单击c2h_tutorial_sw工程,然后选择Run As,之后点击Nios II Hardware。Nios II IDE需要几分钟时间来编译和运行程序。 7 K: @  a: M2 ]
2. 在控制台视图中观察执行时间。在控制台试图中可以看到如下的信息:
. i0 x) K3 v5 x8 @' @  z# YThis simple program copies 1048576 bytes of data from a source buffer to a destination buffer.
/ n8 N" u8 p8 q  T7 E* aThe program performs 100 iterations of the copy operation, and calculates the time spent. Copy beginning SUCCESS: Source and destination data match. Copy verified. Total time: 39330 ms 可以看到以软件来运行该工程需要39330毫秒,这个数值因用户选用的开发板不同而可能不同。
0 I, H! t+ T2 ]5 Y/ i4. 创建和配置硬件加速器 % `: I5 `1 ^5 g1 g( f2 e
为do_dma()函数创建硬件加速器,创建硬件加速器执行下面的步骤: 6 d% i, v7 H+ x. F
1. 在 Nios II IDE编辑器中打开dma_c2h_tutorial.c文件。 9 |% E, C5 o/ z- Q
2. 在源文件中,双击do_dma()函数名选中该函数。 % a3 X0 i' _6 K  r3 p( `& e+ N5 T
3.右键单击do_dma,然后点击Accelerate with the Nios II C2H Compiler,如图8-32所示。 C2H视图出现在Nios II IDE窗口的底部,如图8-33所示。, A! h% X# h7 G0 `0 r4 {
6 U4 e( u. m5 {+ E' b
本例中,为了简单化,do_dma()函数和应用其它的代码存在于同一个文件中。然而,一个好的做法是将要加速的函数单独形成一个文件。工程的makefile不能确定一个文件的哪个部分被改变了,因此,如果一个被加速的函数同其它未被加速的代码共存于一个文件,假设用户只编辑了未加速的代码,C2H编译器也会浪费时间在重新编译加速器上。 * t& H0 ]+ ]7 A
4. 设置新的加速器的编译选项,如图8-34所示。
. Y" c, }& a5 Xa. 在图8-34中点击+图标,在C2H视图中展开c2h_tutorial_sw。
- E* i: R: j$ H+ m" p2 mb. 选中Build software, generate SOPC Builder system, and run Quartus II compilation。 当用户在Nios II IDE中编译工程, 该选项会使C2H编译器在后台调用SOPC Builder和Quartus II 以生成新的FPGA配置文件。 9 b+ e* |0 k9 u! w
c. 在C2H中展开do_dma()。 ( x! r& N! d0 H1 t# e: {$ _
d. 在do_dma()下,选择Use hardware accelerator in place of software implementation. Flushdata cache before each call。
7 y- m" u8 W1 M2 A% `: q在运行时,该选项使得程序激活do_dma()的加速器硬件。使用该选项,C2H的wrapper函数在激活硬件加速器之前会刷新处理器数据缓存。wrapper函数需要在激活硬件加速器之前刷新数据缓存, 如果处理器具有数据缓存, 并且处理器和加速的函数写相同的存储器, 不刷新缓存可能会导致一致性问题。
5. 重新编译工程 . `" t* G, r3 C2 z3 i1 h! l# W" H$ U  G
重新编译工程执行如下的步骤:
( `& G( `+ Z/ f; X在C/C++ 工程视图中,右键单击c2h_tutorial_sw,然后点击Build Project。编译的过程需要20多分钟,取决于用户的计算机性能和目标FPGA。在后台,Nios II IDE执行下面的任务: 6 j$ n! c- @, |' s4 S/ Q9 q
1. 启动C2H编译器分析do_dma()函数,生成硬件加速器,生成C wrapper函数。 . o. Z# X/ A  W1 b6 T: r
2. 调用SOPC Builder连接加速器到SOPC Builder系统。编译过程修改SOPC Builder系统文件 (.ptf),将新的加速器作为一个元件加入到系统中。
: \0 X& j! V, M' M: t, e3. 调用Quartus II编译硬件工程,生成FPGA配置文件。 ! ~0 B) i! m' B3 m0 n
4. 重新C/C++ 应用工程,将加速器wrapper函数连接入应用。进度信息在控制台视图中显示,编译过程创建下面的文件: 8 R) S3 j: b# m0 ]" O7 f, V
  • accelerator_c2h_tutorial_sw_do_dma.v (or .vhd)——该文件是被加速的函数的HDL代码。该文件保存在Quartus II工程目录中,该文件名遵循格式:accelerator_<IDE project name>_<function name>。该文件在Nios II IDE中不可见。
  • alt_c2h_do_dma.c ——该文件是加速器的wrapper函数,保存在软件工程的Debug或Release目录,名字遵循格式:alt_c2h_<function>.c。
6. 在报告文件中观察结果
* c/ }4 ^. F& K4 y- @; f        C2H编译器在C2H视图中产生一个详细的编译报告。编译报告包含硬件加速器性能和资 源使用的信息,用户可以使用这些信息来为C2H编译器优化C代码。
. |, t4 e3 U6 }) K- Y: \5 L下面介绍报告文件的主要特性,查看报告执行下面的步骤:
9 J) u- u6 a8 b( w. w1. 在Nios II IDE中点击C2H视图,用户可以双击C2H页来在全屏模式下观看报告。
6 _: T- b4 G' d% p2. 在C2H视图中,展开c2h_tutorial_sw、do_dma()、Build report。 对于有多个加速器的设计,在C2H视图中列出的每个函数下都出现一个编译报告。 ! G) P8 C3 n2 \, D
3. 展开Glossary部分。 这部分定义报告中使用的术语。
4. 展开Resources部分和所有的子部分,如图8-35所示。Resources部分列出了硬件加速器所有的主端口。每个主端口对应源代码中的一个指针参照。本例中有两个主端口:一个是对应读指针*source_ptr;一个对应写指针*dest_ptr。
5. 展开Performance 部分和所有的子部分。如图8-36所示。
9 H0 f6 y8 x: K) v9 wPerformance部分显示加速函数中的每个循环的性能特性。有两个标准来表征一个循环的性能:循环延迟和每个循环迭代的周期数(cycles per loop-iteration——CPLI)。循环延迟是需要填充流水线的时钟周期数。CPLI是假设流水线被填充并且没有延迟发生,要完成一次循环所需要的时钟周期数。 通常为了更好的加速器性能优化应用就是要减少循环延迟和CPLI。 6 B/ o' l; X; |5 X/ K
参阅Accelerating Nios II Systems with the C2H Compiler Tutorial来获得有关优化C2H编译器结果的更多信息。
7. 在SOPC Builder中查看加速器
1 J) [6 w3 c& x' O在C2H编译器添加硬件加速器到用户的SOPC Builder系统之后,加速器出现在SOPCBuilder中。 要在SOPC Builder中查看新添加的加速器,执行如下的步骤: . O4 F+ g" l1 r- O* f. x2 O
1. 返回到Quartus II窗口。 0 f+ N2 [, o) j9 R2 A9 ^
2. 在Tools菜单点击SOPC Builder...打开SOPC Builder。
* B. F$ D8 I5 {  Z3. 在System Contents页注意到新的元件accelerator_c2h_tutorial_sw_do_dma,位于活跃的元件的列表底部,如图8-37所示。 1 u2 f. L0 x: K0 O3 M) p& ^
4. 关闭SOPC Builder。 9 N' J* V# V8 b1 ?8 w: C' E& Y
用户不能在在SOPC Builder修改加速器, 必须在Nios II IDE中移出或修改它。 当在Nios IIIDE中使用C2H编译工程时,要关闭SOPC Builder。如果SOPC Builder没有关闭,在SOPCBuilder窗口中显示的系统会变成过时,因为C2H编译器会在后台覆盖SOPC Builder系统文件(.ptf)。
8.运行有加速器的工程
% a1 k4 c- g. W' X2 U用户现在可以运行被加速的工程了,执行如下的步骤: 2 \7 z- A; k3 c+ W2 `/ B! |- l
1. 返回到Quartus II窗口。
% h) u+ U4 a! d5 Y8 c: w2 [2. 使用新的FPGA配置文件配置FPGA,该文件包含硬件加速器。 7 {9 a! O* K2 F/ Q1 V
3. 返回到Nios II IDE窗口。 $ o0 ~  F4 Q0 s% _; U) A4 @
4. 在C/C++ 工程视图,右键单击c2h_tutorial_sw工程,指向Run As,然后点击Nios IIHardware。Nios II IDE下载程序到开发板运行。 : n8 g' I% |* M! Z1 A
5. 在控制台视图观察执行时间。在控制台视图中可以看到如下的信息: 4 C; U" p$ |# p/ D4 a8 ~0 H
This simple program copies 1048576 bytes of data from a source buffer to a destination buffer. & }/ @" j3 W; f9 P' e
The program performs 100 iterations of the copy operation, and calculates the time spent. Copy beginning SUCCESS: Source and destination data match. Copy verified. Total time: 5010 ms
+ U" x+ q$ L4 u- u% d可以看到有硬件加速器工程运行时间大为减少了,该结果根据不同的开发板可能不同。 + r; T9 ~0 r% ^- g: j, Y
9. 移除加速器 ) E! w' W8 W! U& w4 A; ?' a; G9 b
用户可以从设计中移除加速器,在Nios II IDE中执行如下的步骤:
. T% i& H) C9 Y) N2 q1. 在C2H视图中右键单击函数名,然后点击Remove C2H Accelerator,如图8-38所示。
* h- S4 c% K' L, ?: J; @# W2. 在Nios II IDE中重新编译工程。 9 f, x8 v; ?, |- F% ^: ]  {) c$ v
移除加速器会从SOPC Builder中移出加速器元件,并且用原来的未加速的函数替代C2H软件wrapper函数。必须在Nios II IDE中使用Remove C2H Accelerator命令来移除加速器。不要在SOPC Builder中手动来删除元件。
回复

使用道具 举报

 楼主| 发表于 2009-1-2 17:52 | 显示全部楼层

用户定制指令

Quartus II Nios II , u5 x1 Z$ L% E* H# v: c8 F
使用Altera Nios II 嵌入式处理器,系统设计者可以通过添加定制指令到Nios II指令集中,来加速处理对时间要求苛刻的软件算法。使用定制指令,用户可以将一个包含多条标准指令的指令序列减少为硬件实现的一条指令。 用户可以在很多的应用中使用这个特性, 例如,优化数字信号处理的软件的内部循环、信息包头的处理和计算密集的应用。Nios II 配置向导提供了图形化的用户界面用来添加多达256的定制指令到Nios II处理器。定制指令逻辑直接连接到Nios II 算术逻辑单元(ALU),如图8-1所示。
0 c4 b5 P3 ]9 z9 L1 p/ g. `5 y* V( a
本节包括如下的内容: * U: J: \: C3 R1 ~' d& |
        &#151; Nios II 定制指令的特性。
9 f' G3 R: V: Z  z9 ?: Y+ B2 L        &#151; 实现定制指令的软硬件要求。 ; |% x. Z, _3 a0 b5 [5 h
        &#151; 定制指令的体系结构类型的定义。
8.1.1 定制指令综述
$ w2 ]- W7 Q- K* f        使用Nios II定制指令,用户可以利用FPGA的灵活性来满足系统性能的需要。定制指令允许用户添加定制功能到Nios II处理器的ALU。 & n  h: r- q) Q' a
        Nios II 定制指令是在处理器的数据路径上与ALU紧邻的定制逻辑模块。定制指令提供给用户通过裁剪Nios II处理器内核来满足特定应用需求的能力。用户具有将软件算法转化成定制的硬件逻辑模块来进行加速处理的能力。因为,很容易改变基于FPGA的Nios II处理器的设计,在设计过程中定制指令提供了简单的方法来测试软硬件的权衡。
' `9 P# M  \4 x3 u( `# z        图8-2是Nios II定制指令的硬件结构图。Nios II定制指令逻辑的基本操作是从dataa和/或datab端口接收输入,在result端口驱动输出,输出是由用户生成的定制指令逻辑产生的。
/ m5 N& N! `9 D! I- Z- m        Nios II处理器支持不同的定制指令体系结构类型。图8–2 给出了用于不同的体系结构类型的另外的端口。不是所有端口都是需要的,有些端口只有在用于实现特定的定制指令时才存在。图8–2也显示了一个可选的与外部逻辑的接口。该接口允许用户与Nios II处理器数据路径之外的系统资源相接口。
        Nios II定制指令软件接口很简单,而且抽象了定制指令的细节。对于每一条定制指令,Nios II IDE在系统头文件system.h中产生一个宏。用户在C或C++ 应用程序中如同一个函数一样调用宏。用户不需要编写汇编程序来访问定制指令。当然,在Nios II 处理器汇编语言程序中也可以调用定制指令。
) y9 `% N' \/ B3 `9 K: u* t8.1.2 定制指令体系结构的类型
$ z& E$ u5 _* b+ J        Nios II支持不同的定制指令体系结构来满足不同应用的要求。体系结构从简单的、单时钟周期组合指令结构到扩展的可变长度的、多时钟周期定制指令体系结构。选择的体系结构决定了硬件接口。表8–1给出了定制指令体系结构的类型、应用和硬件接口。
1. 组合逻辑定制指令体系结构
0 T. |; y: v) ^! y2 t1 e        组合逻辑定制指令体系结构包括一个能在一个时钟周期完成的逻辑模块。图8–3为组合逻辑定制指令体系结构的结构图。
图8–3 组合逻辑定制指令结构图使用了dataa和datab端口作为输入,在result端口驱动输出结果。因为逻辑可以在一个时钟周期内完成,所以不需要控制端口。组合逻辑必需的端口是result端口。dataa和datab端口是可选的。在定制指令需要输入操作数才有这两个端口。如果定制指令只需要一个输入端口,使用dataa。 " i7 v9 U# j" y5 \! g3 j' J
2. 多时钟周期定制指令体系结构 6 ]/ V1 s  e" c; y" Q
        多时钟周期的定制指令包括一个需要2个或更多时钟周期才能完成操作的逻辑模块。对于多时钟周期定制指令需要控制端口。图8–4显示了多时钟周期定制指令的结构图。
多时钟周期定制指令可以在固定或可变的时钟周期数内完成: 9 g! z- O/ S) y8 L( V
  • 固定长度:在系统生成时用户指定需要的时钟周期数。
  • 可变长度:在握手方案中使用start和done端口来决定定制指令何时完成。
4 u7 S: v/ [3 S% Q; k
在表8–2中,对于多时钟周期的定制指令clk、clk_en和reset端口是必需的,而start、done、dataa、datab和result端口是可选的。只有定制指令的功能需要它们时才存在。
+ K$ t( i) W8 h0 s. ]下面描述多时钟周期定制指令硬件端口的操作细节。图8–5给出了多时钟周期定制指令的时序图。 & ^9 g5 ?8 g$ U' ~3 Y
  • 在第一个时钟周期,当ALU发出定制指令,处理器置start端口为高电平——有效,这时dataa和datab 端口具有有效的值,而且在定制指令执行的期间一直保持有效。
  • 固定或可变长度的定制指令端口操作:
         固定长度:处理器置start有效,等待一个指定的时钟周期数,然后读result。对于n个周期的操作,定制指令逻辑模块必须在start端口有效之后的第n-1时钟上升沿提供有效的数据。
2 d2 @8 H5 e: w8 q/ i       可变长度:处理器一直等到done端口有效,done端口为高电平有效。处理器在done有效之后的时钟沿读result端口。定制指令模块必须done端口为有效的同一个时钟周期向result端口上提供数据。
: M0 T$ I! g5 u; Q1 ]
  • Nios II系统时钟提供给定制指令模块的clk端口,Nios II系统主reset提供给高电平有效的reset端口。reset端口只有当整个Nios II系统复位才有效。
  • 定制指令模块必须将高电平有效的clk_en端口处理成传统的时钟使能信号,当clk_en无效时,忽略clk。
  • 定制指令模块的端口中不是表0-2中的定制指令的端口都是和外部逻辑的接口。
  • 用户可以进一步优化多时钟周期指令,可以是通过实现扩展的内部寄存器文件定制指令,或者是创建有外部接口的定制指令。
3. 扩展定制指令体系结构
" P* q8 ]0 H0 Q8 K) j* K        扩展定制指令体系结构允许一个定制指令实现几个不同的操作,扩展的定制指令使用N域来指定逻辑模块执行哪个操作。指令的N域的字宽度可达8比特,使得一个定制指令可以实现多达256不同的操作。
' I! G8 ^% P; J% p        图8-6是扩展定制指令的结构图,可以实现位交换、比特交换和半字交换的操作。图8-6 的定制指令对从dataa 端口接收到的数据进行交换操作,它使用2比特宽度的n端口来选择多路复用器的输出,决定提供给result端口哪个输出。n端口的输入直接来自定制指令的N域字。这个例子中的逻辑是非常简单的,用户可以基于N域来实现任何用户想要的功能选择。
        扩展定制指令可以是组合指令和多时钟周期指令,要实现扩展指令只要添加一个n端口到用户的定制指令逻辑。n端口的宽度由定制指令逻辑能够执行的操作数目决定。扩展的定制指令占用多个定制指令索引。例如,图8-6中的定制指令占用4个索引,因为n是2个比特的宽度。 因此, 当该指令在Nios II系统中实现之后, Nios II系统还剩下256 - 4 = 252 可用的索引。 & Y2 p+ X% K# K: M
        n端口的行为同dataa端口类似。当在时钟的上升沿,start为有效时,处理器提供给n端口信号,n端口在定制指令执行的期间保持稳定不变。所有其它的定制指令端口操作保持不变。 5 f# s$ c4 g3 k% T' @. `
4. 内部寄存器文件定制指令体系结构 $ i8 e2 W; ~' g/ o! e2 E* D
        Nios II处理器允许定制指令逻辑访问其内部寄存器文件,这提供给用户指定定制指令从Nios II 处理器寄存器文件或是从定制指令本身的寄存器文件读操作数的灵活性。而且,定制指令可以写结果到定制指令的本地寄存器文件而不是Nios II处理器寄存器文件。内部寄存器访问定制指令使用readra、 readrb和writerc来决定I/O访问发生在Nios II处理器文件还是内部寄存器文件。并且,端口a、b和c指定从哪个内部寄存器读数据以及写数据到哪个寄存器。例如,如果readra为有效(即,从内部寄存器读),a提供了内部寄存器文件的索引。 更多的Nios 定制指令实现的信息参考Nios II Processor Reference Handbook中 Instruction Set Reference章节。图8-7显示了一个简单的乘加定制指令逻辑。
        当readrb为无效时,定制指令逻辑对dataa和datab相乘,然后将结果存在accumulate(累加)寄存器 。Nios II处理器可以将结果读出。通过将readrb置为有效,处理器可以将累加器中的值读出来,作为乘法器的输入。表8–3列出了内部寄存器文件定制指令的端口。只用当定制指令的功能需要时,才使用这些可选的端口。
readra、readrb、writerc和a、b和c端口的行为同dataa类似。当start端口有效,处理器在时钟的上升沿提供readra、readrb、writerc、a、b和c。所有的端口在定制指令执行的过程保持不变。为了确定如何处理寄存器文件I/O,定制指令逻辑读高电平有效的readra、readrb和 writerc端口。定制指令逻辑使用a、b和c端口作为寄存器文件索引。当readra或者readrb无效时,定制指令逻辑忽略相应的a或b端口。当writerc为无效时,处理器忽略result端口上的值。其它的定制指令端口的操作是相同的。 + \0 x) x* k' P: q6 a/ S
5. 外部接口定制指令体系结构 - D1 `% R. d" H
        图8-8显示Nios II定制指令允许用户添加一个同处理器数据路径之外的逻辑进行通信的接口。在系统生成时,任何的不被看作为定制指令端口的接口会出现在SOPC Builder顶层模块中,外部逻辑可以对其访问。因为定制指令逻辑能够访问处理器外部的存储器,这样就可以扩展定制指令逻辑的功能。
        图8-8显示的是一个具有外部存储器接口的多时钟周期定制指令。
* p5 @2 o, q# F- I" R- N        定制指令逻辑可以执行不同的任务,例如,存储中间结果,或者读存储器来控制定制指 令操作。可选的外部接口也提供了一个数据流入和流出处理器的专用的接口。例如,定制指 令逻辑能够直接将处理器寄存器文件的数据传递给外部的FIFO存储器缓冲,而不是通过处理器数据总线。
7 k: J" ]4 }( S/ s. i6 V8.1.3 软件接口
( [! g: r, G" e% T+ m& b- ~        Nios II定制指令的软件接口从应用代码抽象了逻辑的实现细节,在编译过程中,Nios IIIDE生成允许应用代码访问定制指令的宏。这一节介绍定制指令软件接口的细节,包括如下的内容:
7 U5 {& a" N4 Q/ @& G" M
  • 定制指令例子。
  • 内嵌函数和用户定义的宏。
1. 定制指令例子
: p9 N8 |0 {* M; x7 z8 j0 W- q例8-1显示了system.h头文件中的一部分,定义了位交换定制指令的宏。这个例子使用一个32位的输入,只执行一个功能。
6 p0 g: ?9 L$ P  D- ^例8-1,位交换宏定义
# _9 `& ]) {! O6 g" U7 Q9 W8 e# \#define ALT_CI_BSWAP_N 0x00 8 M0 z* Z) T0 s0 T% P) c1 h
#define ALT_CI_BSWAP(A) __builtin_custom_ini(ALT_CI_BSWAP_N,(A)) " o. g- A& J, p$ ]% m
ALT_CI_BSWAP_N被定义为0x0,作为定制指令的索引。ALT_CI_BSWAP(A)宏被映射到一个只需要一个参数的gcc内嵌函数。 + N/ m' b3 f" R8 S
例8-2演示的是在应用代码中使用位交换定制指令。
- e5 a, r# i6 Q' ?例8-2,位交换指令的使用 / r! y& B! d- O5 w# T
1. #include "system.h"
5 p. ~0 y- {  [4 K5 k" Y2.
2 @# g8 }$ G' a. Q2 y2 S- G) M3.
- L0 \& f8 z' ~* ?- f# F, C6 M4. int main (void) # g) a7 Z* w- J2 x6 u# |$ f/ Q! t! x
5. { 6 C1 a, N; W6 {+ @! A
6. int a = 0x12345678; 1 R# m" ]. O' }5 K$ ]$ {2 B% W+ ?  T
7. int a_swap = 0;
- _) y! o8 O7 V( _4 ~0 h8. : G% J2 d1 a0 y! c. ^. h
9. a_swap = ALT_CI_BSWAP(a); 2 N9 W3 P: c+ o* {
10. return 0; 0 Y/ W8 r3 x; o. y9 E
11.}
0 G+ C9 K: r3 z' c$ q, H* m" ]在例8–2中,system.h 文件被包含,其中有宏的定义。该例声明了两个整数,a和a_swap。 ; D, k. U7 ?9 b( T% H3 ~% k
a作为输入传递给位交换定制指令,结果赋给a_swap。例8–2可以体现大部分应用使用定制指令的方法。
Nios II IDE在宏的定义中只使用了C的整数类型。有时,应用需要使用整数之外的其它输入类型,因此,需要传递期望的整数之外的类型返回值。 用户能够为Nios II 定制指令定义定制的宏,允许其它的32位的输入类型同定制指令接口。
6 T, J& t8 y1 h( w& F2. 内嵌函数和用户定义的宏 * G' f* C6 t: o9 E5 A( ~
        Nios II处理器使用gcc内嵌函数来映射定制指令。通过使用内嵌函数,软件可以使用非整数类型的定制指令。共有52个唯一定义的内嵌函数来提供支持类型的不同组合。内嵌函数名具有下面的格式:
( l' x" \0 i2 V6 H__builtin_custom_<return type>n<parameter types> 8 G6 C2 f' C0 t+ `0 C4 W4 b: S% p
表8-4为定制指令支持的32位的参数和返回值的类型,和在内嵌函数中使用的缩写。
例8–3 内嵌函数 9 i) B: x9 h8 {# K( o% T; v
void __builtin_custom_nf (int n, float dataa);   A1 N7 F/ ?, V
float __builtin_custom_fnp (int n, void * dataa); 8 Z2 i! a% r/ ?! K' P3 _( |7 V
在例8–3中,_builtin_custom_nf函数需要一个整形(int)和一个浮点型(float)作为输入,不返回结果;相反,_builtin_custom_fnp函数需要一个指针作为输入,返回一个浮点数。
) y, B4 T" U' a" o* r5 ]/ y例8–4显示了应用中使用的用户定义的定制指令的宏。 % u$ Q% Q3 j4 c5 @& k
例8–4 定制指令宏的使用
+ l" Y2 w! P* L3 j1. /* define void udef_macro1(float data); */
* ?" e: Q# _- ^0 L9 C2. #define UDEF_MACRO1_N 0x00 2 W! z  T7 W; _7 `, _1 h6 o
3. #define UDEF_MACRO1(A) __builtin_custom_nf(UDEF_MACRO1_N, (A)); 9 S! Y- T$ i9 Z+ R+ n
4. /* define float udef_macro2(void *data); */
: \. @  c8 d/ d( e7 \3 w  Z5. #define UDEF_MACRO2_N 0x01 " C4 V6 I3 ^3 J, C3 t' Z0 f
6. #define UDEF_MACRO2(B) __builtin_custom_fnp(UDEF_MACRO2_N, (B));
( H; a* h+ M$ x) U7 A4 T5 P7. + O; F2 N  K9 t  u
8. int main (void) 8 l( G! H: U2 ^9 {# x6 t
9. {
$ N& c+ U$ J& E1 c: `* C! u10. float a = 1.789;
- g& l0 C% {  V- |7 P; B11. float b = 0.0;
4 D- [" d1 P/ r" ]1 t0 q2 ^12. float *pt_a = &a; ' P# ^# U5 y$ ]+ j" d8 @3 m
13. 6 M7 ~* o  F7 U
14. UDEF_MACRO1(a);
) {4 Y, n: |  D7 O) f15. b = UDEF_MACRO2((void *)pt_a);
0 e2 @& I* D) O! d. p: j3 q  l16. return 0; ; P6 s6 e% x! Y, s! j, k8 N
17. }
在第2到第6行,声明了用户定义的宏,并映射到相应的内嵌函数。宏UDEF_MACRO1需要一个浮点型的输入,不返回任何值。宏UDEF_MACRO2需要一个指针作为输入,返回一个浮点型的值。第14和15行显示这两个用户定义的宏的使用。 : W9 L& |% r( T; Y4 K9 t
8.1.4 实现 Nios II 定制指令
3 o4 P0 }$ h$ L  R' o        本节介绍使用SOPC Builder元件编辑器实现Nios II定制指令的过程。元件编辑器使用户能够创建新的SOPC Builder元件,包括Nios II定制指令。有关SOPC Builder元件编辑器的更多信息,参阅Quartus II Handbook Volume 4:SOPC Builder Component Editor章节。
# u: A0 L  M$ ?, ]1. 在 SOPC Builder中实现定制指令的硬件实现Nios II定制指令需要下面的步骤: ( N1 C" _& `# O
  • 打开Nios II CPU的定制指令设置窗口。
  • 添加定制指令的设计文件。
  • 配置定制指令的端口。
  • 设置元件组的名字。
  • 生成SOPC Builder 系统,在Quartus II软件中进行编译。
(一)打开Nios II CPU的定制指令设置窗口 & c( o; I! Z0 n" u6 K  P5 v1 W
1. 打开SOPC Builder系统   f: U) p0 ~8 }  C/ D5 o
2. 在Altera SOPC Builder System Contents中选择Nios II处理器。 8 J& x; q5 T& }+ Z& ~# f
3. 在Module菜单中点击Edit….,Nios II配置向导窗口出现。
1 ?3 C" u- g1 {" l' L4. 点击Custom Instructions页,出现如图8-9的窗口。 * X0 C! D5 {. k) \
5. 在8-9的窗口中点击Import…,出现如图8-10的窗口。
(二)添加顶层设计文件
1 i1 A9 K( v8 g1. 在图8-10窗口中,点击Add;
* N1 p8 Q  V$ g' P2. 切换到相应的目录,选择硬件设计的文件,设计文件可以是HDL文件、EDIF文件或BDF
- d$ m2 A3 }7 t- l' t0 l) ~文件,本例使用的是BDF文件,是实现CRC编码的逻辑,如图8-11所示,点击打开。 1 `, l8 k/ S% o- _
3. 设计文件会出现在图8-12所示的窗口中,软件会自动识别处顶层模块,本例显示的是crc。 - w& \5 `/ x: y+ I$ g6 m- \% F
4. 在图8-12中点击read port-list from files,将定制指令端口列出来,如图8-13。
(三)发布定制指令
8 Q5 Q2 f& z0 T5 Q6 M& h; F1. 在图 8-13中点击 Next,进入图 8-14 的 Publish页面。 : i/ [2 Q! X, r4 d' ]
2. 在 Component Name 栏中输入 crc,在 group 栏中输入 custom instruction。 、 , p  u/ S& G" {3 `5 `+ ]) s: G* i
3. 点击窗口底部的 Add to library,将定制指令逻辑添加到定制指令库中,如图 8-15 所示。
图8-15 加入到定制指令库中的定制指令
6 z$ M1 d; k# s: S5 z8 M+ W6 \(四)将定制指令加入系统
. j" q" }9 B* C0 L/ i1. 在图 8-15 的窗口中,选中 crc 定制指令,然后点击 Add,即将其加入到系统中,如图 & z- f4 b- S# y# k* Z
8-16。也可以在图 8-14 中,点击 Add to System,进行添加。
. B2 Q8 J9 `0 a; K" Z# {. ?2. 在图 8-16的窗口中点击 Finish,完成定制指令的添加。 / I  c6 f5 h/ r" x1 Z
(五) 生成系统
) H0 T, N6 m; m+ e: V! v8 J7 b/ a1 @1. 生成 SOPC Builder系统。
! [8 j, o0 E7 H' w* n; N. h$ v0 u2. 在 Quartus II 软件中对整个工程进行编译。
回复

使用道具 举报

本版积分规则

QQ|一淘宝店|手机版|商店|一乐电子 ( 粤ICP备09076165号 ) 公安备案粤公网安备 44522102000183号

GMT+8, 2026-4-17 18:33 , Processed in 0.040172 second(s), 20 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表