第5章 CHAPTER 5 FPGA数字系统综合专题 5.1专题一: 频率计设计与实现 1. 专题介绍 完成频率计设计,并在Atlys开发板上实现和验证。 2. 专题目标 进一步熟悉和掌握数字系统设计的一般方法和流程。 了解应用于数字系统的UART串口。 熟悉PicoBlaze硬件,设计PicoBlaze输入输出和中断,掌握PicoBlaze汇编代码的写法。 掌握有限状态机的设计与实现方法。 3. 专题步骤 (1) 明确频率计的设计目标。 (2) 顶层模块设计,即明确顶层模块应包含哪些功能子模块。 (3) 各子模块设计。 (4) 各部分代码设计。 (5) 综合、实现并下载验证。 5.1.1明确设计目标 本专题的目标是设计并实现一个频率计,它测量被测信号(由FPGA内部的Signal_Gen模块产生)的频率,而将频率值由UART串口传送到PC,由PC进行显示。 图51是以上设想的示意图。可见,图中问号部分就是我们设计和实现的主要内容。 图51 5.1.2顶层模块设计 数字逻辑系统的设计一般采用自顶向下的设计方法。首先设计顶层模块,将顶层模块中的分模块功能和它们之间的连接关系、信号流向确定下来; 再针对各分模块进行细化设计,直到细化后的模块已经比较简单而易实现; 最后,实现各个模块、进行装配并进入调试环节。 图52给出了频率计实现的一个方案(顶层模块)。 图52 对这个框图的理解,应从理顺一些主要信号的因果关系开始。至于信号的时序细节,在这一阶段只能了解得比较粗略,各分模块的信号时序还有待细化(一般在分模块的设计中使用时序图进行表达)。下面将频率计方案的主要信号梳理一下。 图52中,由中断信号产生模块获取秒信号的下降沿(也就是用于显示的0.1s的起始时刻),并产生一个不小于两个时钟周期宽度的脉冲,送至PicoBlaze的中断输入。这引起PicoBlaze中断,在中断处理程序中,PicoBlaze通过读端口读取计数器产生的5个BCD码。 计数器产生的个十百千万共5位十进制数,其BCD码一共是20位(bit)。PicoBlaze的输入端口只有8位,无法一次读入20位数据。因此要使用多路器对数据进行选择,由PicoBlaze控制选择。 PicoBlaze的输出口也只有8位,将20位数据发送出去需要多次发送。另外,需要设计PicoBlaze输出端口的逻辑,来满足串口模块接收数据的时序要求。 5.1.3秒信号发生器的设计与实现 秒信号发生器用于选取1s内的被测信号,提供给计数器,计数器将计数结果传送给后续电路进行处理和显示。 考虑到对信号计数后,还需要一定时间来处理和显示,可在秒信号的高电平时间对信号进行计数,而在低电平时间进行处理和显示。实际的秒信号如图53。 图53 数字系统中,通常对高频数字信号分频得到频率较低(1MHz以下)的数字信号。在Xilinx公司的FPGA中,DCM可以产生高频的数字时钟信号。因此,可用DCM产生一个时钟信号,再使用分频器分频来产生秒信号。 通常,采用目标频率整数倍的时钟信号(FPGA中的时钟资源)作为基准。本例中,将20MHz时钟信号二分频后得到10MHz信号(非时钟资源); 再将10MHz信号经过六次十分频得到10Hz信号,这六次分频所采用的是更高频率的时钟信号(100MHz); 最后,对10Hz信号进行十一分频,得到秒信号。那么,处理和显示时间为0.1s(已足够完成处理和显示)。 产生秒信号的系统框图如图54。 图54 1. 二分频模块的设计与验证 双击桌面上Xilinx ISE图标,启动ISE软件。单击File菜单中的New Project选项,在弹出的对话框中输入工程名half并指定工程路径。单击Next按钮进入下一页,选择所使用的芯片及综合、仿真工具。选用Spartan6 XC6SLX45芯片,采用CSG324封装。另外,选择Verilog作为默认的硬件描述语言。再单击Next按钮进入下一页,这里显示了新建工程的信息,确认无误后,单击Finish按钮就可以建立一个完整的工程了。 在工程管理区任意位置右击,在弹出的菜单中选择New Source命令,选择Verilog Module输入,并输入Verilog文件名(最好与工程名同名)。单击Next按钮进入端口定义对话框,这一步可以略过,在源程序中再行添加。单击Next按钮进入下一步,单击Finish按钮完成创建。这样,ISE就会自动创建一个Verilog模块的模板,并且在源代码编辑区打开。简单的注释、模块和端口定义已经自动生成,接下来需要将代码编写完整。 参考代码: module half(inputSignal_clk, inputrst, output reg Signal ); always @( posedge Signal_clk ) begin if( rst ) Signal <= 0; else Signal <= !Signal; end endmodule 接着,对模块进行综合。在Hierarchy栏中,选中half.v,双击Processes栏中的SynthesizeXST开始综合。综合结束后,SynthesizeXST旁的图标若为绿色打勾,表示没有错误和警告,综合通过; 若为黄色惊叹号,表示综合通过但存在一些问题,这些问题以警告方式出现; 若为红色打叉,表示综合未通过,存在错误。在Errors和Warnings栏中,可以看到具体的错误和警告条目。 综合通过后,就可以进行仿真了。在工程管理区将view设置为Simulation。再右击,并在弹出的菜单中选择New Source,在对话框中选择Verilog Test Fixture,输入测试文件名half_test,单击“下一步”按钮,这时工程中所有的模块名都会显示出来,在其中选择要进行测试的模块。单击Next按钮,再单击Finish按钮,ISE会在源代码编辑区自动生成测试模块的代码。我们看到,ISE已经自动生成了基本的信号并对被测模块做了例化。添加代码,完成测试。 initial begin // Initialize Inputs Signal_clk = 0; rst = 1; // Wait 210 ns for global reset to finish #210rst = 0; // Add stimulus here end always #25 Signal_clk = ~Signal_clk; 完成测试文件后,确认工程管理区中view选项设置为Simulation,这时在Processes栏会显示与仿真有关的进程,双击Simulate Behavioral Model,ISE将启动ISE Simulator,可以得到仿真结果,如图55所示。 图55 从图55中可以看出,Signal信号的频率是时钟的一半。需要注意的是,Signal是普通信号,而不是时钟,不使用时钟资源。在后续的分频链中,都是将信号分频而非时钟。 2. 十分频模块设计与仿真 双击桌面上Xilinx ISE图标,启动ISE软件。单击File菜单中的New Project选项,在弹出的对话框中输入工程名Div_10并指定工程路径。单击Next按钮进入下一页,选择所使用的芯片及综合、仿真工具。选用Spartan6 XC6SLX45芯片,采用CSG324封装。另外,选择Verilog作为默认的硬件描述语言。再单击Next按钮进入下一页,这里显示了新建工程的信息,确认无误后,单击Finish按钮建立工程。 在工程管理区任意位置右击,在弹出的菜单中选择New Source命令,选择Verilog Module输入,并输入Verilog文件名(最好与工程名同名)。单击Next按钮进入端口定义对话框,这一步可以略过,在源程序中再行添加。单击Next进入下一步, 单击Finish按钮完成创建。这样,ISE就会自动创建一个Verilog模块的模板,并且在源代码编辑区打开。简单的注释、模块和端口定义已经自动生成,接下来需要将代码编写完整。 十分频模块代码: module Div_10( inputclk, inputrst, inputSignal_In, output Div_Out ); reg [3:0] Cnt; reg Signal_Buf; wire Signal_Up; always @( posedge clk ) begin Signal_Buf <= Signal_In; end assignSignal_Up = Signal_In & !Signal_Buf; always @( posedge clk ) begin if( rst ) Cnt <= 0; else if( Signal_Up ) begin if( Cnt == 4'd9 ) Cnt <= 0; else Cnt <= Cnt + 1; end end assign Div_Out = ( Cnt == 4'd9); endmodule 接着,对模块进行综合。在Hierarchy栏中,选中Div_10.v,双击Processes栏中的SynthesizeXST开始综合。 在工程管理区将view设置为Simulation并右击,并在弹出的菜单中选择New Source,在对话框中选择Verilog Test Fixture,输入测试文件名Div_10_test,单击“下一步”按钮,这时工程中所有的模块名都会显示出来,在其中选择要进行测试的模块。 单击Next按钮,再单击Finish按钮,ISE会在源代码编辑区自动生成测试模块的代码。我们看到,ISE已经自动生成了基本的信号并对被测模块做了例化。 3. 秒信号发生器合成与验证 在ISE软件中新建一个工程,命名为Second_Gen 。工程中选用Spartan6 XC6SLX45芯片,封装为CSG324。另外,选择Verilog作为默认的硬件描述语言。在工程中新建Verilog顶层模块,命名为Second_Gen。 在工程管理区任意位置右击,单击Add Copy of Source,找到Step2中half.v文件所在的路径并选中,将half.v加入到本工程。在Hierarchy栏中可以看到,half.v与Second_Gen.v没有从属关系。 在Second_Gen.v的endmodule前添加以下代码: half U1( .Signal_clk( clk_20M ), .rst( rst ), .Signal( Signal_10M ) ); 并保存。此时观察Hierarchy栏中的层次关系可以发现,half.v成为Second_Gen.v的一个子模块。调用关系及信号连接关系如 图56。 图56 与实物元件类比,half是被调用模块名,相当于元件名。U1是模块的实例化名,相当于元件序号。 添加十分频模块和十一分频模块。 (1) 在Second_Gen.v中模块名后面的括号中添加模块的输入输出信号: inputclk, inputrst, output Second (2) 在括号后添加模块的内部信号: wire Signal_100M; wire Signal_20M; wire Signal_10M; wire Signal_1M; wire Signal_100K; wire Signal_10K; wire Signal_1K; wire Signal_100; wire Signal_10; (3) 依照half模块的添加方法,在Sencond_Gen.v中添加6个十分频模块和1个十一分频模块。 在工程管理区任意位置右击,单击New Source,在Select Source Type栏中选择IP,在File Name栏中输入My_DCM,单击Next按钮。在View by Function栏中找到FPGA Features and Design并展开,找到Clocking下的Clocking Wizard并选中,单击Next 按钮。再单击Filish按钮。 在Clocking Wizard的Page 1,保持默认选项,单击Next按钮。在Page 2,将CLK_OUT1的Output Freq的Requested的值设为20,将CLK_OUT2的Output Freq的Requested的值设为100,单击Next按钮。后面的步骤均保持默认,单击Generate按钮。 在Second.v中添加DCM组件。 (1) 在Hierarchy栏中,选中My_DCM模块,在Processes栏中双击View HDL Instantiatuon Template,在打开的文档My_DCM.veo中选择Begin Cut here for INSTANTIATION Template和End INSTANTIATION Template之间的文本并复制,在Second_Gen.v中粘贴。 (2) 完成Second_Gen.v中My_DCM模块的信号连接。 接着,对这个工程进行综合。综合过程包含了语法检查和逻辑规则检查。若存在错误或警告,应进行排错。 要实现设计,还需要为模块中的输入/输出信号添加管脚约束,这就需要在工程中添加UCF文件。在工程管理区右击, 单击New Source,选择ImplementationConstraints File,出现一个空白的约束文件(文件名与工程同名),就可以为设计添加各种约束。 在Second_Gen.v文件中添加: NET "clk" LOC = L15; NET "rst" LOC = T15; NET "Second" LOC = U18; NET "clk" IOSTANDARD = LVCMOS33; NET "rst" IOSTANDARD = LVCMOS33; NET "Second" IOSTANDARD = LVCMOS33; 在Hierarchy栏中选中Second_Gen.v,在Processes栏双击Implementation Design选项,就可以自动完成实现步骤。 至此,信号发生器设计完成。 5.1.4中断信号产生模块的设计 PicoBlaze提供一个单路的中断输入信号。PicoBlaze在复位后处于禁止中断的状态,只有运行了ENABLE INTERRUPT指令才能使能中断相应。之后若关闭中断,需要在程序中加入DISABLE INTERRPT指令。 中断允许时,INTERRUPT输入信号为高电平且保持两个时钟周期以上,才会产生中断事件。中断事件会强行停止当前程序的执行(执行完正在执行的指令后,保存断点地址和标志位),并执行一条CALL 3FF指令。3FF是程序存储器的最后地址,所以通常会在3FF地址处放一条跳转指令,跳转到中断处理程序的首地址去执行。 中断处理程序以RETURNI ENABLE(中断返回后允许中断)或RETURNI DISABLE(中断返回后不允许中断)指令返回被中断的程序继续执行,返回的过程实际上是断点的恢复(即将断点地址存入指令指针寄存器并恢复标志位)。 中断响应后,INTERRUPT_ACK信号会立即有效,用于清零中断请求触发器,防止中断响应后再次发生中断。PicoBlaze的中断电路如图57所示。 图57 秒信号的低电平期间,PicoBlaze要做两件事,一个是读取数据并发送,一个是清零计数器。这两个任务都要在中断处理程序当中完成。因此,秒信号的下降沿到来后,就应立即请求中断。 这样,就需要一个中断信号产生模块。这个模块的输入是秒信号,输出是中断请求信号。图58给出了中断请求信号与秒信号之间的时序关系(PicoBlaze要求中断请求信号宽度不小于两个时钟周期)。 图58 这个模块的实现,可以使用有限状态机。状态机的时钟应和PicoBlaze处理器的时钟相同。该状态机的状态图如图59所示。 中断响应流程,如图510所示。 PicoBlaze的中断响应流程包含了以下步骤: ① 开中断。默认情形下,中断是关闭的,可通过运行ENABLE INTERRUPT指令来打开中断。 ② 中断请求。在运行INPUT S1,01指令时发生了中断请求。 ③ 中断响应。运行完INPUT S1,01指令后,强行停止当前程序的运行,对断点进行保护(即将ADD S0,S1的地址以及标志位Z和C保存到堆栈中),并以指令CALL 3FF跳转到3FF地址处运行。 图59 图510 ④ 3FF地址处为一条跳转指令,跳转到中断处理程序去运行。 ⑤ 中断处理。 ⑥ 中断返回。恢复断点(包括指令指针、标志位等)并继续执行ADD S0,S1指令。 根据图510所示状态图,完成中断程序Verilog设计和中断响应的汇编设计。 5.1.5多路器和PicoBlaze的输入端口 观察图57中多路器和PicoBlaze连接部分后会发现,它们的连接关系与图511的结构非常接近(图511是PicoBlaze的输入端口示意图)。 图511 从图511中我们可以得到以下信息: (1) 输入端口是8位的,且输入数据将存入寄存器sX。 (2) PORT_ID表示输入设备的编号(地址),8位,最多支持256个外设,地址来自寄存器或取立即数。 (3) 一次输入需要IN_PORT、READ_STROBE和PORT_ID三组信号同时作用来完成。 (4) 有时需要外部(对FPGA芯片来说,是内部)逻辑来完成输入数据的锁存。 图511中FPGA Logic中左侧的元件(梯形)就是一个多路器,它的选择控制端(其下方有箭头的实线)来自于PicoBlaze的端口地址PORT_ID。那么,选择多路器不同的输入,对应于PicoBlaze的不同的输入设备地址。也就是说,PicoBlaze读不同的地址,就可以读到不同的输入数据,如图512所示。 图512 计数器的输出为5组BCD码,所以需要PORT_ID中的3位来确定地址。 另外,需通过对PicoBlaze输入时序的分析来确定多路器的逻辑,主要是判定在PicoBlaze读之前多路器的输出是否稳定,如 图513所示。 图513 从图513中,我们可以得到以下信息。 (1) 系统在时钟上升沿的同步下工作。 (2) 执行一条INPUT指令的耗时是2个周期。 (3) 一条输入指令的位宽是18位。 (4) 指令“INPUT s0,(s7)”表示读地址为(s7)的外设并存到寄存器s0。 (5) READ_ STROBE信号用于通知FPGA逻辑: 此时可将数据放置于PicoBlaze的输入端口。 5.1.6串口和PicoBlaze的输出端口 PicoBlaze的输出端口结构如图514所示。 图514 从图514可以得到以下信息: (1) 输出端口是8位的,且输出数据来自寄存器sX。 (2) PORT_ID从字面理解应是端口编号(地址),8位,最多支持256个外设,地址也来自寄存器或取立即数。 (3) 一次输出需要OUT_PORT、WRITE_STROBE和PORT_ID三组信号同时作用来完成。 (4) 还需要外部(对FPGA芯片来说,是内部)的逻辑来完成数据的锁存。 再来看输出端口的时序图(如图515)。 图515 从图515我们可以得到以下信息: (1) 系统在时钟上升沿的同步下工作。 (2) 执行一条OUTPUT指令的耗时是2个周期。 (3) 一条指令的位宽是18位。 (4) 指令“OUTPUT s0,65”表示将寄存器s0的值输出到65端口。 (5) WRITE_ STROBE信号用于通知FPGA逻辑: 有数据来自PicoBlaze的输出端口。 (6) 需要一个触发器来锁存OUT_PORT[7:0]信号。 这样,我们可以根据以上信息设计一个逻辑来完善PicoBlaze端口功能,如图516端口锁存器所示。 图516 1. PicoBlaze汇编编写 PicoBlaze软核开发中,使用汇编语言来编写程序代码。我们来看PicoBlaze的指令集。 如PicoBlaze指令有7类: 程序控制类指令、算术指令、中断指令、逻辑指令、存储指令、移位指令和输入输出指令。 编写好的psm汇编代码,用kcpsm3编译,生成v文件即可使用的专题中。 2. 分析输出uart原理 本专题使用给定的串口模块实现将数据传输到PC。串口模块uart_tx由3个子模块组成: 波特率产生模块Band_Gen.v、串口发送模块kcuart_tx.v和字符缓冲模块bbfifo_16x8.v。其层次结构和RTL原理图如图517所示。注意,RTL图中,模块左边的信号是输入,模块右边的信号是输出。 图517 串口模块uart_tx的时序如图518所示。从图518中可以看出,信号write_buffer用于启动串口发送数据。而在write_buffer信号有效前,应使发送数据data_in有效。 图518 将图518和PicoBlaze的输出时序图相比较,容易发现write_buffer信号和PicoBlaze的输出锁存信号Write_Strobe作用相似。可以利用这一点,将Write_Strobe与PicoBlaze的port_id中某些位运算后 连接到write_buffer(串口与PicoBlaze的某些输出地址关联),或将Write_Strobe与write_buffer直接相连(串口与PicoBlaze的所有输出地址关联)。 3. 建立输出模块 新建一个工程,命名为Out_Port_Test,器件的型号及封装与之前实验相同(型号为xc6slx45封装为3csg324)。 分析PicoBlaze端口功能,确定需要使用哪些端口。 address和instruction: 读取汇编程序所需的地址和指令接口,必须连接。 out_port: 输出数据。 write_strobe: 输出数据有效,这里需要用它来锁存数据(至LED)。 port_id: 本例只有一个外设,故此接口可悬空。 in_port和read_strobe: 输入端口,对本例来说不需要,但建议in_port接0。 interrupt和interrupt_ack: 中断相关端口,也不需要,但建议interrupt接0。 reset : 复位,为1时复位,本例中可接0。 可得出端口的具体连接,其示意见图519。 图519 图519中(参见显示器),绿色表示对外(整个PicoBlaze模块之外)需要连接的信号,蓝色表示此信号需要连接但不连接到模块之外,红色表示此信号可悬空。 编写相应的代码,将上一步生成的v文件添加到工程中综合,生成RTL原理图,如图520所示。 图520 4. 添加PicoBlaze外围电路 (1) 在Out_Port_Test工程中添加DCM模块,其输出频率为50MHz。 (2) 在Out_Port_Test工程中添加练习一产生的Verilog模块Out_Latch.v。 (3) 在Out_Port_Test工程中新建Verilog模块,命名为Out_Port_Test.v(顶层模块,与工程同名)。在顶层模块中逐个调用my_PicoBlaze模块、DCM模块、Out_Latch模块,并完成信号的连接。 (4) 综合并观察所生成的RTL原理图(其结构与图521相同)。 图521 5.1.76PicoBlaze的软件设计 秒信号生成了中断申请信号,送给PicoBlaze。秒信号利用时钟管理器生成。那么,读取BCD码、通过串口发送和清零等动作应该在中断处理程序中完成。 用以生成PicoBlaze的汇编代码: CONSTANT UART_write_port,05 CONSTANT WAN_port,01 CONSTANT QIAN_port,02 CONSTANT BAI_port,03 CONSTANT SHI_port,04 CONSTANT GE_port,05 CONSTANT Clear_Cnt,06 NAMEREG sF, UART_data cold_start: ENABLE INTERRUPT loop:JUMP loop ; isr: INPUT UART_data,WAN_port OUTPUT UART_data, UART_write_port INPUT UART_data,QIAN_port OUTPUT UART_data, UART_write_port INPUT UART_data,BAI_port OUTPUT UART_data, UART_write_port INPUT UART_data,SHI_port OUTPUT UART_data, UART_write_port INPUT UART_data,GE_port OUTPUT UART_data, UART_write_port ; LOAD UART_data,character_space OUTPUT UART_data, UART_write_port ; INPUT UART_data,Clear_Cnt RETURNI ENABLE ; ADDRESS 3FF JUMP isr ; CONSTANT character_space, 20 5.1.8验证 (1) 下载后将连接Atlys开发板PROG位置的USB线连接到Atlys开发板UART位置的USB口上,并安装驱动。 (2) 在PC上运行串口精灵软件,将串口波特率设为38400Baud。 (3) 单击串口精灵中的运行,可观察到接收界面中的频率值。 5.2专题二: Atlys开发板的AC97固件设计 1. 专题介绍 了解基于Atlys开发板AC97固件的实现。 2. 专题目标 (1) 了解FPGA基础。 (2) 了解FIR滤波器。 (3) 掌握FPGA架构与设计流程。 (4) 滤波系统完整实现。 3. 专题步骤 (1) 设计任务。 (2) FIR滤波器简述。 (3) FPGA架构与设计流程。 (4) 滤波系统完整实现。 5.2.1设计任务 本书主要设计任务是设计一个FIR滤波器,从CD质素的音乐(48kHz)中滤除4kHz的噪声,滤波器的参数设置如下: Fs=48kHz,FPASS1=2000Hz,PSTOP1=3800Hz,FSTOP2=4200Hz, FPASS2=6000Hz,APASS1=APASS2=1dB,ASTOP=60dB 本书介绍将设计的滤波器开发成外设IP核,将其例化到处理系统中。系统通过板上的编解码芯片和AC97控制器获得一段立体声音乐,经过带阻滤波,最后输出到耳机。 5.2.2滤波器简述 1. 数字滤波器概述 数字滤波主要作用是要抑制干扰,其是数字信号处理中的重要组成部分。FIR滤波器作为数字信号处理应用中的一个重要的应用课题,在相当长的时间里,其选择都是数字信号处理器。而FPGA具有十分灵活的可编程逻辑,随着性能的不断增强,同时其具有查找表结构、可重构特性、可并行处理、流水线操作等特点,这些使得基于FPGA的数字滤波器的设计日益广泛。目前,随着数字信号处理技术的不断进步与深亚微米集成电路工艺的快速发展,数字滤波器正在逐步替代传统的模拟滤波器,发展成为一种最主要的滤波器系统。数字滤波器之所以被看好是因为数字滤波器相比模拟滤波器具有以下优点。 (1) 数字滤波器具有很高的精度,甚至模拟滤波器理论上都不能达到数字滤波器所能达到的性能,这显然是非常重要的。 (2) 数字滤波器具有很高的信噪比。这主要因为数字滤波器以数字器件执行运算,从而避免了各种电路噪声的影响,不会像模拟滤波器那样受到元件参数的影响,现代深亚微米技术使得数字设计在集成度方面可以更好实现,这也是从另一方面更有益于系统的集成,也有益于获得更高的信噪比。 (3) 数字滤波器可靠性很高。这主要是因为数字滤波器避免了电子元件的电路特性随着时间、温度、电压、电流等变化而变化所带来的影响。在数字电路工作环境下,数字滤波器具有非常高的可靠性; 相比之下模拟滤波器受到电子元件电路特性的影响便显得稍差了。此外,数字滤波器还可以轻易满足幅度和相位线性的严格要求,易在硬件上实现,易于和数字信号处理系统集成。 然而,数字滤波器的处理能力受到系统采样频率的限制,根据奈奎斯特采样定理,输入信号频率分量必须小于滤波器1/2采样频率的分量,否则会因“混叠”而无法正常工作。所以在某些场合,模拟滤波器仍然是很重要的器件。 2. IIR和FIR数字滤波器 线性时不变(LTI)滤波器是最常见的数字滤波器系统,其原理为线性卷积运算。LTI数字滤波器分为无限长冲击响应(IIR)滤波器和有限长冲击响应(FIR)滤波器两类。与IIR滤波器相比,FIR滤波器的优点是可以在设计任意幅频特性的同时,保持严格的线性相位特性。线性相位特性对于图像处理、语言信号处理、高质量音频处理等一些性能要求较高的系统是非常重要的,所以FIR滤波器在现代信号处理领域更受欢迎,得到了更广泛的应用。要提一下的是,达到同样的衰减特性时,FIR滤波器的阶数应高于IIR滤波器。 FIR滤波器的实现包括软件实现方案与硬件实现方案。在非实时系统与低速系统中,可在DSP或CPU上用软件实现FIR滤波算法,其特点是虽然实现方法简单但是实时性比较差。在实时性要求较高的系统中,此种方法难以满足设计需求,常需要采用硬件实现。常用于硬件实现的器件有DSP器件、定制ASIC芯片、FPGA等。随着可编程逻辑器件高速发展,容量与速度均有增加,FPGA的并行处理特质使得其非常适合在实时性要求比较高或计算量比较大的场合组建硬件滤波系统,用来实现FIR滤波器。 3. 数字滤波器的原理结构 FIR滤波器的实质是输入采样序列与滤波器系数序列进行卷积,从而得到输出序列,长度为N的FIR滤波器(阶数为N-1)包括N个抽头h(n),其数学表达式为: y(n)= ∑N-1i=0h(n)·x(n-i) 其中x(n)为输入序列,y(n)为输出序列,h(n)为单位采样响应。 FIR滤波器的基本结构如图522所示。 图522 FIR滤波器的传递函数为: H(z)=∑N-1n=0h(n)z-n 其中,h(0)、h(1)…h(N-1)为系统的单位冲击响应。 4. FIR数字滤波器的实现 本设计最终要实现从48kHz的CD音乐中滤除4kHz噪声信号,采用带阻型滤波,滤波器的参数为: N=59,Fs=48kHz,FPASS1=2kHz,FSTOP1=3.8kHz,FSTOP2=4.2kHz,FPASS2=6kHz,APASS1=APASS2=1dB,ASTOP=60dB。 利用MATLAB里的FDATool工具实现这个滤波器,用该软件可以得到滤波器的系数,设置如图523所示。 图523 FIR带阻滤波器幅频响应如图524所示,两个截止频率之间所有成分幅度衰减高于60dB,信号通过此滤波器时,4kHz处于这个频段之中,其幅值衰减将超过60dB,便被成功抑制掉。而两个通带边缘频率之间的频率成分便是有效信号也受到一定衰减影响的部分,显然这段频率跨度越小,衰减越小越好。 图524 5.2.3FPGA架构 1. 设计平台介绍 Atlys开发板 Atlys FPGA开发板是一块基于Xilinx Spartan6 LX45 FPGA的功能强大的数字电路开发平台。其板载外围集成了Xilinx Spartan6 LX45 FPGA芯片、128MByte DDR2存储阵列、16MByte×4 SPI FLASH、10\100\1000以太网接口、HDMI视频输入输出、AC97音频编解码器、100MHz CMOS晶振、USBUART和USBPROG等接口以及GPIO外设(8个LED灯,6个按钮,8个滑动开关)等。配置如图525所示。 图525 Atlys开发平台在基于嵌入式处理器构建完整的数字应用系统时是个理想的选择。Atlys的编程下载方式选择也很多样,通过Digilent Adept软件可以实现Atlys开发板的通信和编程下载。此外,通过Digilent Plugin for Xilinx Tools,也可以使用Xilinx自带的iMPACT实现Atlys开发板的电路编程下载。 LM4550 AC97音频编解码器 LM4550 AC97音频编解码器包含4个音频插孔,耳机输出(J7),LINE OUT(J5),LINE IN(J4)和麦克风MIC(J6),麦克风插孔是单声道的,所有其他接口均为立体声。音频支持18位数据位、48kHz采样频率,音频输入和音频输出的采样率可以不同。结构如图526所示。 图526 LM4550 AC97音频编解码器符合AC97 2.1版标准,连接成初级编解码器(ID1=0,ID0=0)。AC97编解码器的控制和数据信号如表51所示。 表51 信 号 名 称引脚类型 SDO N16 LVCMOS33 BITCLK L13 LVCMOS33 SDI T18 LVCMOS33 SYNC U17 LVCMOS33 RESET T17 LVCMOS33 2. FPGA设计流程概述 FPGA多使用4(6)输入的查找表(LUT),每一个LUT可以看成一个有4(6)位地址线的16×1(64×1)的RAM。FPGA芯片主要构成有: 可编程输入输出单元(IOB)、可配置逻辑块(CLB)、时钟管理模块、嵌入式块RAM(BRAM)、丰富的布线资源、底层内嵌功能单元、内嵌专用硬核,每个部分都具有各自具体的功能。目前,主流FPGA都是基于SRAM工艺的,还有一些采用FLASH或熔丝与反熔丝技术的军用和宇航级的FPGA。 基于FPGA的设计是指利用FPGA芯片作为硬件基础,借助相关的EDA开发软件和编程工具,实现具有一定功能的数字电路。开发流程一般包括电路功能设计、设计输入、功能仿真、综合、综合后仿真、实现与布局布线、时序仿真与验证、板级验证以及芯片编程与调试等主要步骤。 本设计中使用的软件平台为Xilinx EDA软件: 2012.3 Vivado HLS,V14.3 EDK(XPS & SDK),V14.3 ISE Foundation Software。 Vivado HLS Vivado HLS是Xilinx的高层次综合解决方案,它综合C、C++和System C代码形成Verilog和VHDL RTL结构,并将其封装集成到Vivado IP集成器里。Vivado高层次综合速度非常快,可以在短短几分钟处理成千上万的C代码,提供了设计探究的可能,可以通过性能、资源和功耗指标去微调架构,也可以在函数引入约束和指令,创造不同的架构。Vivado HLS工程的实现框图如图527所示。 图527 ISE Design Tools Project Navigator Project Navigator创建的工程主要用来进行逻辑设计,设计语言可为Verilog或VHDL,此外还可选择原理图输入等其他 方式。其通常包括设计输入、行为仿真、综合、实现和下载调试等过程。 本实验中主要通过Project Navigator创建工程,并添加所生成的RTL文件用来进行ISIM仿真以观测滤波器模块的时序逻辑。 EDK EDK用于嵌入式系统的设计,使用C或C++语言,其包括XPS(Xilinx Platform Studio)和SDK(Xilinx Software Development Kit),前者用于构建嵌入式硬件平台,后者专门用于开发应用软件。EDK的主要开发步骤有: 利用BSB创建硬件平台、添加IP Core以及用户自定义外设、生成仿真文件并测试硬件系统、生成硬件比特流、开发软件系统、合并软硬件比特流、下载和在线调试等。 滤波器系统设计流程 在Xilinx相应的软件和FPGA硬件平台的基础上,FIR滤波处理系统的具体设计流程如图528所示。 图528 根据图528可知,滤波系统完整设计流程大约可按软硬件使用划分为以下几步: Vivado HLS中C代码到VHDL RTL的综合实现; Project Navigator中的ISIM仿真; Vivado HLS中生成Pcore; XPS中创建处理器系统; SDK中开发应用程序; 硬件实现。 5.2.4FPGA设计流程 1. Vivado HLS中C到VHDL RTL的综合实现 滤波器函数的C语言实现 fir.c文件内容如图529所示。 图529 FIR滤波器将x设定为抽样输入,y指向计算的抽样输出,两者类型data_t。滤波器参数放在从文件fir_coef.dat装载的类型为coef_t的数组c中。使用顺序算法,在类型为acc_t的变量acc中计算抽样输出的累积值。 关于数据类型的具体定义如图530所示。 图530 头文件包括ap_cint.h,所以用户自定义的任意精度的字节宽度可以被使用。它还定义了抽头数N、抽样数目(测试平台中)以及数据类型coef_t、data_t和acc_t。coef_t和data_t是16位短整型,使用的参数是16位有符号短整型,来自编解码芯片的抽样信号也是16位宽的。由于该算法迭代(乘法和积累)超过59个抽头,有可能位增长6比特,所以acc_t被定义为int38。由于acc_t比抽样信号和参数宽,使用前要先计算它,如fir.c的第16、18和21行。 测试平台C语言描述 fir_test.c文件如图531所示。 图531 注意到测试平台以写模式打开了fir_impulse.dat,发出一个脉冲信号(第一抽样值为0x8000,其余为零)并将输出的抽样值保存在文件中。 Vivado HLS中C到VHDL RTL的综合实现 图532 启动Vivado HLS,Start→All Programs→Xilinx Design Tools→Vivado 2012.3→Vivado HLS。新建工程并命名为fir_prj,保存目录设置为D:\xup\lab。进入设置向导,在源文件添加/移除的窗口中,输入fir作为函数名(与源文件相符,必须命名为fir以便能顺利综合)并添加fir.c和fir_coef.dat。在测试平台添加/移除的窗口中添加fir_test.c。实现方案配置,保留方案名solution1和时钟周期10,不确定度的空白档将默认取值0.125。部件选择按照Atlys的标准应该设置为如下: Family: Spartan6,SubFamily: Spartan6,Package: csg324,Speed Grade: -2,选择XC6SLX45CSG3242。 成功创建工程后打开fir.c,运行Active Solution进行综合,综合结束,一些报告文件将会生成,综合的结果也在其中显示。综合报告给出了本实验设计的性能和资源的评估、延迟以及顶层接口信号。具体可以从报告中看到的内容如图532所示,这里只看一下性能评估(Performance Estimates)和接口信息(Interface Summary),分别见图533和图534。 图533 图534 报告里包含用户设定、速度(如latency、trip count)、面积(FFs、LUTs)、功耗、接口等信息。 由图533可见,时钟周期9.78ns,延时为175个时钟周期。 接口信息显示目前ap_clk,y,x三个信号有对应对象fir,y,x。 选择fir.c,将PIPELINE流水线指令应用到loop上去。重新综合结束,生成新的综合报告如图535所示。注意延迟减少到64个时钟周期,接口信息不变。 图535 进行C/RTL协同仿真,协同仿真生成和编译一些文件,并对设计进行仿真。在控制窗口可以观察这个过程。完成后,RTL仿真报告成功,延迟仍为64,接口信息不变。 2. Project Navigator中的ISIM仿真 滤波器仿真激励源 此文件用来进行仿真,通过对输入信号的控制来得到自己想要的仿真效果,根据仿真波形分析时序逻辑。 Project Navigator中的ISIM仿真 启动Project Navigator,新建工程并命名为fir_projnav。器件设定如图536所示。 图536 Family: Spartan6,Device: XC6SLX45,Package: CSG324,Speed: -2, Preferred Language: VHDL 新建完毕后,将刚才用Vivado HLS生成的VHDL文件全部添加到工程中,并将fir设置为顶层模块。添加testbench文件,运行行为仿真,仿真执行4000ns,当ap_done为高电平时, 输出滤波器参数,参数输出见图537是正确的。 图537 3. Vivado HLS中生成Pcore 重新回到Vivado HLS,打开fir.c,选择Directive标签,右键单击x,打开指令编辑对话框,设置如图538,其中元数据metadata框内,输入bus_bundle fir_io,使输入输出通过名为fir_io的AXI4Lite适配器联系在一起。 图538 类似地,对输出信号y做同样的设置。 对顶层模块fir应用Resource指令,变量名字显示为return,元数据也设定为bus_bundle fir_io。以上步骤将为信号x、y、ap_start、 ap_valid、ap_done和ap_idle信号创建地址映射。作为核上独立的端口,ap_start、 ap_valid、ap_done和ap_idle信号依次产生,前提是,不对顶层模块fir应用Resource指令。这些端口再通过GPIO P核在处理系统中被连通。 对P核适配时,将x、y以及顶层模块fir的Direcive类型都设置为Resource,选用AXI4LiteS并作相同命名,以便将输入输出端口连接。 对设计重新综合,通过Export RTL生成Pcore。展开impl文件夹,观察到在Pcores文件夹(如图539所示)下,创建了fir_top_v1_00_a,它将在开发处理系统的过程中被使用,包括数据、网表和其他子文件。 图539 4. XPS中创建处理器系统 通过XPS的Base System Bulider创建以Single MicroBlaze软核处理器为基础的AXI系统: 频率100MHz,本地内存16KB,只选外设RS232_Uart_1(AXI UARTLITE,115200 baud rate,8 Data bits,no interrupt,no parity)。使用IP Catalog添加其他满足需要的IP(ac97_0),将由Vivado HLS生成的P核的文件夹放在pcores下,在XPS中重新扫描用户数据库,找到新创建的P核(fir_top)并添加(fir_left、fir_right)。 ac97核的结构图如图540所示。新创建的P核结构图如图541所示。 图540 图541 Ports设置,确认ac97_0下的BITCLK、RESET_N、SDATA_IN、SDATA_OUT和SYNC连接到外部端口。并将S_AXI_ACLK与信号clock_generator_0: CLKOUT0相连。将同样的时钟信号给fir_left和fir_right的SYS_CLK端口,SYS_RESET端口设为proc_sys_reset_0: Interconnect_aresetn。地址映射表如图542所示。 图542 修改约束文件system.ucf,给ac97定义引脚,I/O标准均为LVCMOS33: NET ac97_0_BITCLK_pin LOC = "L13"|IOSTANDARD = "LVCMOS33"; NET ac97_0_RESET_N_pin LOC = "T17"|IOSTANDARD = "LVCMOS33"; NET ac97_0_SDATA_IN_pin LOC = "T18"|IOSTANDARD = "LVCMOS33"; NET ac97_0_SDATA_OUT_pin LOC = "N16"|IOSTANDARD = "LVCMOS33"; NET ac97_0_SYNC_pin LOC = "U17"|IOSTANDARD = "LVCMOS33"; Generate Bitstream,生成system.bit文件。 5. SDK中开发应用程序 XPS中的bit文件生成后,选择命令Export Hardware Design to SDK以启动SDK软件。可以在overview查看处理器的地址映射( 图543)与设计中的IP块(图544)。 新建一个无需任何库支持的standalone_bsp_0软件平台工程。库生成器将在后台运行,产生xparameters.h头文件。新建C工程命名为TestApp,使用Empty Application工程模块。将testapp.c和xfir_fir_io.h通过import添加到testapp.c工程中,编译生成TestApp.elf。 6. 硬件实现 连接Atlys的usbuart到电脑的USB端口,用音频线连接Atlys的Linein到电脑的耳机输出口,连接耳机到Atlys的Lineout或HPOUT接口。连接Atlys电源适配器,给Atlys板上电。新建一个终端连接,在弹出的窗口中作如图545所示的设置。 图543 图544 图545 然后选择命令Program FPGA,选择之前生成的system.bit和system_bd.bmm两个文件。同时选择刚生成的TestApp.elf来初始化块RAM,单击Program。Data2Mem工具将bit文件、BMM文件和SDK生成的elf文件整合到一起,生成带有软核功能的比特流文件: download.bit。然后断开终端连接,将USB连接至Atlys的USBPROG接口,用Adept软件将该文件下载到FPGA中。之后再将线接至usbuart接口,重新建立一个终端,设置和上面一样。 在计算机中以循环模式播放加噪wav音乐,通过音频线传输到板上进入FPGA滤波处理,从耳机口输出已滤除4kHz单音噪声的音乐。在终端界面中,键入“i”,显示滤波器系数,如图546和图547所示; 键入“b”,输出未滤波前的原始加噪音乐; 键入“f”,输出滤波后的无噪音乐。至此验证了整个系统,将Atlys板断电,退出EDK。 7. 本系统实现思路总结 以FPGA为系统硬件核心,在Atlys Spartan6 FPGA开发平台上,采用软、硬件结合的方式,本书提出的是一种具有灵活配置、实时性好、易于扩展等优点的FIR数字滤波系统,它的系统框图如图548所示。 8. 设计总结 图549为下载时的实物图,电源线连至电源口,USB连接至USBPROG接口,用音频线连接AC97编码器的Linein与计算机的耳机输出接口,外放音箱或耳机接至AC97编码器的耳机接口。 图550是调试时的实物图,与下载时唯一区别是,计算机的USB接至Atlys板上的USBUART口,而不USBPROG口,这是为了通过这个口在PC上创建终端连接,从而控制Atlys的动作。 图546 图547 图548 图549 图550 最终顺利实现了预想的效果,播放加了4kHz噪声的CD音乐传输到Atlys上进行滤波处理后,通过耳机播放时,没有了刺耳的4kHz的噪声,音乐质量明显提高,滤波系统性能基本达到要求。 5.3专题三: Linux系统搭建与移植 1. 专题介绍 在Zedboard上搭建一个简单的Linux系统,继而在Zedboard上移植一个有图形界面的Linaro Ubuntu。 2. 专题目标 了解Zedboard的硬件情况。 制作Zedboard的Linux引导,配置编译Zedboard的Linux内核,学习制作ramdisk根文件系统,测试完成的Linux系统。 学习为Ubuntu配置硬件,学习编译Ubuntu的Linux内核,学习为Zedboard上的Ubuntu搭建文件系统,测试Zedboard上的Ubuntu Linux。 3. 专题步骤 (1) 了解Zedboard。 (2) 生成Linux启动引导文件。 (3) 编译Linux内核。 (4) 制作ramdisk根文件系统。 (5) 测试系统,以及控制GPIO。 (6) 为Linaro Ubuntu配置硬件。 (7) 编译Linux内核。 (8) 生成设备树文件。 (9) 给SD卡分区。 (10) 拷贝Linaro Ubuntu文件系统。 (11) 连接计算机,启动测试Ubuntu。 5.3.1Zedboard简述 如图551所示的Zedboard是基于Xilinx Zynq的低成本开发板,可以运行Linux、Android及Windows等系统。此外,可扩展接口使得用户可以方便访问PS和PL。 Zynq将ARM处理系统和与可编程逻辑完美地结合在一起,创建独特强大的设计。Zedboard以zynq7Z020为核心,分为PS和PL两个部分。PS(processingsystem)部分包括zynq内的两个 CortexA9 硬核,搭载2片256MB共512MB的DDR3内存,另外还有与MIO直接相连的外设,如USBUART,CortexA9 USBOTG,Enet,SDcard,Quadflash等。PL(programable logic)部分包括zynq的可编程逻辑块以及与之相连的一些外设例如VGA,HDMI,8个LED和8个开关等。 图551 5.3.2生成Linux启动引导文件boot.bin boot.bin文件的生成依赖三个文件: XPS硬件工程生成的bit文件(可以通过generate bitstream生成),xsdk生成的first stage bootloader文件和uboot文件。 (1) bit格式文件可以在xps中由generate bitstream功能生成。 (2) Fsbl文件生成办法是,在xps中把硬件编好后,输出(export)到SDK,在SDK中,建立new project,OS platform选standalone,在提供的参考工程中选择fsbl模板,完成。Sdk会自动根据xps的硬件配置生成fsbl文件,大致的名称为fsbl.elf. (3) Uboot文件生成办法, 在Xilinxwiki有ubootxlnx的详细说明: http://www.wiki.xilinx.com/U-boot http://www.wiki.xilinx.com/Build+U-Boot 下载Xilinx官方编辑过的ubootxlnx.git包,命令如下: ~ $:git clone git://git.xilinx.com/u-boot-xlnx.git 选择分支,选择的分支要对应Xilinx ISE的版本。命令如下: ~$:cd u-boot-xlnx && git checkout -b xilinx-14.2-build1-trd xilinx-14.2\-build1-trd 其中14.2build1可根据实际情况替换成14.1build1,14.3build2。 为Zedboard配置uboot编译选项,命令如下: ~ $:make distclean && make zynq_zed_config Zynq_zed_config的具体配置选项可以在include/configs/zynq_zed.h中查看到。 编译生成目标 ~ $:make -j 编译完成后,拷贝目录下的uboot文件,重命名成uboot.elf,这个就是我们需要的最终文件,使用file命令可以查看文件的格式: ~ $:file u-boot u-boot: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), statically linked, not stripped 准备好了这三个文件后,就可以使用Xilinx SDK自带的create zynq boot image功能生成boot.bin文件了。打开SDK, 单击菜单栏上xilinx tools和create zynq boot image,选择create new bif file,依次添加3个文件,设置目标目录,完成,就可以生成uboot.bin和uboot.mcs文件。把uboot.bin文件重命名成boot.bin,就是我们需要的引导文件了。 5.3.3编译Linux内核 生成引导文件之后,另外两个重要的文件就是内核和设备树文件。Xilinx提供打过补丁的Linux内核,可以由如下命令获取: ~ $:git clone git://git.xilinx.com/linux-xlnx.git 选择14.2分支(也可以选择其他分支例如14.1、14.3等): ~ $:git checkout -b xilinx-14.2-build1-trd xilinx-14.2-build1-trd 拷贝Xilinx已经预先设置好的内核设置选项: ~ $:make ARCH=arm xilinx_zynq_defconfig 正式开始编译内核: ~$:make ARCH=arm 编译完成后,在./arch/arm/boot目录下会产生zImage文件,就是我们需要的内核了。 另外,对于Zedboard来说,由于存在PL模块,需要一个设备树文件,来给系统提供相应的PL地址等信息。 在$KERNEL/arch/arm/boot/dts文件夹下,提供有很多现成对应不同硬件设置的dts的文件,可以在这些dts文件的基础上根据实际需要修改,最后由这些dts文件生成需要的设备树文件的命令是: ~$: $KERNEL/scripts/dtc/dtc -O dtb -I dts -o devicetree.dtb devicetree.dts 其中devicetree.dts是指要转换的dts文件,是可以阅读的文件,生成的dtb文件是二进制格式,机器可以阅读的格式,生成的dtb文件需要重命名成devicetree.dtb文件。 5.3.4制作ramdisk根文件系统 生成内核引导文件和内核后,要正常运行起系统,只缺少一个根文件系统了。在最简Linux系统下,可以制作一个ramdisk文件系统。 Xilinx官方合作服务商Avnet提供了一个简单的32MB ramdisk制作教程,由busybox,dropbear和相关的启动文件,系统设置文件组成。 (1) busybox是一个集成了一百多个最常用Linux命令和工具的软件,包含了一些简单的工具和一些更大、更复杂的工具,虽然可能对这些工具的高级功能有所删减,但是常用的必需的功能完全具备。有些人将busybox称为Linux工具里的瑞士军刀。busybox就好像是个工具箱,它集成了Linux的许多工具和命令,并对这些工具和命令尽可能地压缩以减小体积,满足嵌入式的要求,它将许多具有共性的小版本的UNIX工具结合到一个单一的可执行文件。这样的集合可以替代大部分PC上的常用工具,适用于小型尤其是嵌入式系统。 busybox的安装如下。 busybox的git网站上提供了busybox包,可以clone得到: ~$:git clone git://git.busybox.net/busybox 以默认的arm选项配置busybox: ~$:make ARCH=arm defconfig 对默认的编译选项进行一些修改: ~$:make menuconfig 这是一个简单的图形化的编译选项选择界面,与Linux内核的make menuconfig类似,在里面可以按照需求增加或者删减busybox的一些功能,其中,需要设置编译后busybox输出的目录,如下: Busybox Settings--> Installation Options--> BusyBox installation prefix--> 设置为ramdisk的文件夹目录,例如~/rootfs。 正式编译: ~$:make ARCH=arm install 编译结束后,在ramdisk根目录文件夹下(例~/rootfs)可以看到,已经生成了/bin /sbin等文件夹,里面有非常多的命令工具,而且它们全都是指向busybox这个可执行程序的链接。 (2) dropbear是一个开源的相对较小的SSH(security shell,安全shell)服务器和客户端。它运行在基于POSIX的各种平台,是特别有用于“嵌入”式的Linux(或其他Unix)系统,功能是提供安全shell服务,就是为用户提供安全的登录,验证用户的权限。 Dropbear的安装如下。 到dropbear官网下载最新的源代码包,例如作者下载的最新源码包是dropbear0.53.1.tar.gz. 解压到当前目录: ~$:tar -zxvf dropbear-0.53.1.tar.gz 配置编译选项: ~$: ./configure --prefix=~/rootfs --host=arm-xilinx-linux-gnueabi --disable-zlib \ LDFLAGS="-Wl,--gc-sections" CFLAGS="-ffunction-sections\ -fdata-sections -Os" ~$:make PROGRAMS="dropbear dbclient dropbearkey scp \ dropbearconvert" MULTI=1 strip 编译安装: ~$:make install ~$:ln -s sbin/dropbear ~/rootfs/usr/bin/scp(创建超链接scp服务由dropbear来提供) 安装完毕之后,可以在rootfs文件夹下发现sbin/dropbear可执行程序,不是指向busybox的链接。 生成dropbear可执行程序之后还没有结束,需要产生一些用户登录的密钥等信息,以备用户安全登录时验证: ~$:mkdir etc etc/dropbear Avnet公司提供了一份已经可以使用的dropbear配置(在Zedboard网站上可以下载,里面相关章节附件里),可以直接拷贝到~/rootfs中。 到这已经可以使用了,但是由于密钥是avnet提供的,不是很安全。有需要的话,可以自己重新生成密钥: ~$:dropbearkey -t rsa -f dropbear_rsa_host_key ~$:dropbearkey -t dss -f dropbear_dss_host_key 把生成的密钥文件拷贝覆盖原来的文件就可以了。 安装完busybox和dropbear之后ramdisk文件系统基本就完成了,剩下的工作就是添加一些必要的文件夹和系统必需的文件了。 (3) 补充其他必要文件。 创建系统根目录必要的文件目录: ~$: mkdir dev etc/init.d mnt opt proc root sys tmp var var/log var/www 建立库文件链接: ~$: ln -s /lib/libz.so.1.2.7 /lib/libz.so ~$: ln -s /libz.so.1.2.7 /libz.so.1 ~$: ln -s /usr/lib/libcrypto.so.1.0.0 /usr/lib/libcrypto.so 复制一些xilinx arm工具包提供的库: $cp /opt/CodeSourcery/Sourcery_CodeBench_Lite_for_Xilinx_\ GNU_Linux/arm-xilinx-linux-gnueabi/libc/lib/* lib $cp /opt//CodeSourcery/Sourcery_CodeBench_Lite_for_Xilinx_\ GNU_Linux/arm-xilinx-linux-gnueabi/libc/usr/lib/* usr/lib $cp /opt//CodeSourcery/Sourcery_CodeBench_Lite_for_Xilinx_\ GNU_Linux/arm-xilinx-linux-gnueabi/libc/sbin/* sbin $cp /opt//CodeSourcery/Sourcery_CodeBench_Lite_for_Xilinx_\ GNU_Linux/arm-xilinx-linux-gnueabi/libc/usr/bin/* usr/bin 对库文件做尽可能的压缩: $arm-xilinx-linux-gnueabi-strip lib/* $arm-xilinx-linux-gnueabi-strip usr/lib/* 编辑系统挂载文件fstab: $gedit etc/fstab 内容如下: LABEL=//tmpfsdefaults00 none/dev/ptsdevptsgid=5,mode=62000 none/procprocdefaults00 none/syssysfsdefaults00 none/tmptmpfsdefaults00 编辑启动文件inittab: $ gedit etc/inittab 内容如下: ::sysinit:/etc/init.d/rcS # /bin/ash # # Start an askfirst shell on the serial ports ttyPS0::respawn:-/bin/ash # What to do when restarting the init process ::restart:/sbin/init # What to do before rebooting ::shutdown:/bin/umount -a -r 建立系统密钥文件(此密钥由Avnet提供): $ gedit etc/passwd 内容如下: root:$1$qC.CEbjC$SVJyqm.IG.gkElhaeM.FD0:0:0:root:/root:/bin/sh 建立启动初始化文件: $ gedit etc/init.d/rcS 内容如下: #!/bin/sh echo "Starting rcS..." echo "++ mount -t mount -t mount -t Mounting filesystem" proc none /proc sysfs none /sys tmpfs none /tmp echo "++ Setting up mdev" echo /sbin/mdev > /proc/sys/kernel/hotplug mdev -s mkdir -p /dev/pts mkdir -p /dev/i2c mount -t devpts devpts /dev/pts echo "++ Starting telnet daemon" telnetd -l /bin/sh echo "++ Starting http daemon" httpd -h /var/www echo "++ Starting ftp daemon" tcpsvd 0:21 ftpd ftpd -w /& echo "++ Starting dropbear (ssh) daemon" dropbear echo "rcS Complete" 修改rcS文件和文件系统的权限: $ sudo chmod 755 etc/init.d/rcS $ sudo chown -R root * $ sudo chgrp -R root * 到此,ramdisk根文件系统已经全部完成,把这个文件系统做成ramdisk就可以使用了。 注: 这个ramdisk是按照avnet官网教程做的,由于第3步把Xilinx提供的库文件全部拷贝了,实际完成后的大小超过32MB,无法压缩成32MB的ramdisk,其解决办法是删去一些没有用的文件,比如/usr/lib/下有个locale文件夹,放的各种语言文件,删去了就小了很多,差不多满足了32MB的要求。 (4) 把文件系统做成ramdisk。 $ dd if=/dev/zero of=~/ramdisk32M.image bs=1024 count=32768 $ mke2fs -F ramdisk32M.image -L "ramdisk" -b 1024 -m 0 $ tune2fs ramdisk32M.image -i 0 $ mkdir ramdisk $ sudo mount -o loop ramdisk32M.image ramdisk/ $ sudo cp -R _rootfs/* ramdisk $ sudo umount ramdisk/ $ gzip -v9 ramdisk32M.image 5.3.5测试系统,控制GPIO 经过以上的步骤,一个基于ramdisk的Linux系统已经完成了。拷贝以上步骤生成的boot.bin,devicetree.dtb,zImage,ramdisk32M.image到SD卡fat32分区插入Zedboard的SD卡插槽,连接Zedboard电源,连接Zedboard的USBUART口到计算机,给Zedboard加电,打开串口监视软件minicom,过十几秒钟(Zedboard在开机自检和根据boot.bin配置PS,PL部分),OLED屏左右的两个指示灯亮,串口 输出信息,最后出现如图552状态。 图552 1. 在Linux系统下操作PS部分的GPIO OLED屏幕右边有个LED灯LD9,以及两个按键BTN8,BTN9,它们都是属于PS部分的,可以在运行起来的Linux系统下直接操作。 2. 操作LED灯LD9 查看Zedboard官方手册(例ZedBoard_HW_UG_v1_9.pdf),LD9小灯占用的是PS模块的MIO7,在Zedboard板上也做了MIO7的标注,在XPS中也可以看到MIO7是只能输出不能做输入的一个引脚。因此对它的操作如下: 把MIO7设置为export口: ~$: echo 7 > /sys/class/gpio/export 小灯是只输出不输入设备,设置方向为输出: ~$: echo out > /sys/class/gpio/gpio7/direction 给小灯的状态赋值,1亮0灭: ~$: echo 1 > /sys/class/gpio/gpio7/value 3. 操作按键BTN8 BTN9 与操作MIO的LED类似,不同之处只是按键是只输入不输出的设备。在Zedboard板上,BTN8、BTN9的旁边也标注了MIO50和MIO51 。值得注意的是,在XPS环境下,Xilinx默认是把MIO50、MIO51作为I2C接口引脚的。 把MIO50、MIO51设置为export口: echo 50 > /sys/class/gpio/export echo 51 > /sys/class/gpio/export 设置为只输入不输出的设备: echo in > /sys/class/gpio/gpio50/direction echo in > /sys/class/gpio/gpio51/direction 读取按键状态: cat /sys/class/gpio/gpio50/value /sys/class/gpio/gpio51/value 当按下按键时,相对应的按键读取到的数值会变成1,松开后为0。 4. 在Linux系统下操作PL部分的GPIO 由于PL部分的GPIO不是直接连到MIO上,所以PS部分不能直接操作,需要添加相应的驱动模块。Avnet公司给操作8个LED小灯做了示例。 在Avnet的Zedboard培训教程上可以找到小灯驱动模块补丁,该补丁的文件名如下:0001XilinxARMDriverforLEDbrightnessdevice.patch. 给内核打上Avnet提供的PL部分LED的驱动补丁: ~$: cd linux-xlnx ~$: git apply 0001-Xilinx-ARM-Driver-for-LED-brightness-device.patch 选上LED模块选项并编译: ~$: make ARCH=arm 这时会出现询问是否编译进内核/编译为模块/不编译,三个询问依次选择yes yes module,也可以直接在.config文件中修改: ~$: vim .config 把相关选项改成如下所示: CONFIG_PL=y CONFIG_PL_DEBUG=y CONFIG_LEDBRIGHTNESS=m 编译完成后,拷贝arch/arm/boot/zImage和drivers/pl/ledbrightness.ko文件(这里新编译出的zImage和前面的zImage一样都是可以用的)。 编译出带LED设备地址说明的设备树文件,只有在设备树文件中列出的设备才能被内核识别并操作。 ~$: mvzedboard-2cpu-667mhz-512mb-timer-uart-sdhci-usb-eth-staticip-32Mramdisk.dts devicetree.dts ~$: gedit devicetree.dts 按照里面的格式,加入几行内容,保存。 ( led-brightness@41200000 { compatible = "avnet,led-brightness"; reg = <0x41200000 0x20>; }; ) 重新生成dtb文件: ~$: ./scripts/dtc/dtc -O dtb -I dts -o devicetree.dtb devicetree.dts 以上步骤完成之后,把zImage文件和led_brightness.ko,devicetree.dtb文件拷进SD卡,从SD卡启动。 由于ramdisk是根目录,并且led_brightness是编译为模块而不是直接嵌入内核,因此需要挂载SD卡fat32分区并装载led_brightness驱动。 ~$: mount /dev/mmcblk0p1 /mnt/ ~$: insmod /mnt/led-brightness.ko 就会在dev目录下出现/dev/ledbrightnesss设备。 改变LED灯的亮度: cd /mnt/driver_test_bench ./run_led_brightness_test_bench.sh 1 5.3.6为Linaro Ubuntu配置硬件 在Analog网站上可以下载到提供的硬件配置,此时下载到的文件名是cf_adv7511_zed_edk_14_4_2013_02_05.tar.gz,已经做好了adv7511芯片和可编程逻辑块GPU等资源的硬件设置,直接generate bitstream就可以使用,如果希望自己从零开始做出来的话,在zedboard.org网站上Reference Designs板块上有个Building a Zynq Video Design from Scratch教程,提供了非常详细的实现视频设计的方法,按照它的做法做出来也是可以用的。限于本书的篇幅及为了方便,这里直接使用analog_device_inc提供的硬件配置,有时间可以照此详细地做一遍。 生成Generate bitstream之后,生成system.bit,及export to sdk,生成对应的fbsl,加上原先的uboot.elf,使用xilinx_tools的create zynq boot image功能产生boot.bin文件。需要注意的是,由于现在使用一个真正的ext4文件系统作为根文件系统而非ramdisk,因此需要对uboot做改动,在~/ubootxlnx/include/configs/目录下编辑zynq_zed.h头文件,去掉 fatload mmc 0 0x800000 ramdisk.img.gz; 这一行,重新编译出就可以。Xilinx最新提供的版本默认使用uImage内核和uramdisk根文件系统,还改了这个设置的位置到zynq_common.h中了,在common.h文件中做类似修改就可以。uImage和uramdisk是带uboot信息头的zImage和ramdisk,前两者可以由后两者方便产生,生成uramdisk的部分可以见xilinx wiki。 5.3.7编译Linux内核 按照analog网站的指导,下载Linux内核 ~$: git clone git://github.com/analogdevicesinc/linux.git ubuntu ~$: cd ubuntu 选择xcomm_zynq分支,这个分支是带hdmi显示模块的内核分支: ~$: git checkout xcomm_zynq ~$: make ARCH=arm distclean 配置为analog提供的adv7511的内核编译选项: ~$: make ARCH=arm zync_xcomm_adv7511_defconfig 这里比教程上要多一步,设置config文件中: CONFIG_XILINX_FIXED_DEVTREE_ADDR=y 原因是后来启动时出现了以下错误: Error: unrecognized/unsupported machine ID (r1 = 0x0fb71dd0) Available machine support: ID (hex) NAME 00000d32 Xilinx Zynq Platform Please check your kernel config and/or bootloader 注: 原因网上说是Linux内核和uboot的版本不同导致的,在如下网页 可以找到解决办法http://ez.analog.com/message/87877。 编译内核: ~$: make ARCH=arm 5.3.8生成设备树devicetree ~$: make ARCH=arm zynq-zed-adv7511.dtb ~$: rename zynq-zed-adv7511.dtb devicetree.dtb 在这个设备树dts文件当中,也可以注意到根文件系统的变化,有一行: bootargs = "console=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlyprintk rootfstype=ext4 rootwait devtmpfs.mount=0"; 使用ramdisk文件系统时,这一行是这样的: bootargs = "console=ttyPS0,115200 root=/dev/ram rw initrd=0x1100000,32M ip=:::::eth0:dhcp earlyprintk"; 以上设定了信息的串口输出、根文件系统设备、类型、IP等信息。 5.3.9给SD卡分区 Linux下可以用gparted,fdisk等分区工具给SD卡分区,要求分为两个主分区,一个是fat32格式,存放boot.bin,devicetree.dtb,zImage等文件给Zedboard读取(Xilinx为Zedboard烧录的最初始的保密的那段代码只支持读取fat32格式的分区),另外一个是ext4分区,存放linaroubuntu的文件,作为系统的根文件系统。 5.3.10拷贝Linaro Ubuntu文件系统 Linaro Ubuntu是一个开源项目,主要是致力于Ubuntu,Android在嵌入式以及开发板上的实现,它提供的Lianro Ubuntu可以用在Zedboard板上。在 http://releases.linaro.org/11.12/ubuntu/oneiric-images/ubuntu-desktop 可以找到很多不同时间版本的Linaro Ubuntu版本,这里下载使用analog推荐的linaropreciseubuntudesktop20120626247.tar.gz. 可以使用analog推荐的命令解压复制到SD卡ext4分区(挂载路径为/media/rootfs): ~$: sudo tar --strip-components=3 -C /media/rootfs -xzpf linaro-o-ubuntu-\ desktop-tar-20111219-0.tar.gz binary/boot/filesystem.dir 作者推荐解压后使用rsync命令: ~$: gunzip linaro-precise-ubuntu-desktop-20120626-247.tar.gz ~$: tar -xvf linaro-ubuntu linaro-precise-ubuntu-desktop-20120626-247.tar ~$:cd linaro-ubuntu/binary/boot/filesystem.dir/ ~$: sudo rsync -a ./ /media/rootfs ~$: sudo rsync -a ./ /media/rootfs ~$: sudo rsync -a ./ /media/rootfs 最后一个命令多重复几遍,是因为SD卡速度慢,复制根文件系统需要的时间比较长,很容易在拷贝文件的时候发生错误 。 注: 作者开始拷的两遍都缺少文件或者没有拷贝正确,以致于无法启动,不知道原因,费了很多时间。使用rsync同步命令多次可以保证文件拷贝的正确性。 5.3.11连接计算机屏幕,启动测试Ubuntu 将制作完成的SD卡插入Zedboard SD插槽(fat32分区三个文件: boot.bin ,zImage ,devicetree.dtb ,ext4分区是linaro的根文件系统),连接电源,UART,加电,经过大约几分钟启动,可以验证效果。