Menu Close

SOC系列教程 SOC编程和EMIO的使用

前面我们新建立了helloworld的项目,实现了SOC的初始化和环境配置。

这里我们需要新建一个EMIO的vivado项目,在helloworld的基础上进行修改,避免全部重新配置:

    • 重新拷贝vivado文件夹,拷贝到新建的文件夹下,比如ARM_SOC_SEG 。
    • 注意ARM_SOC_TOP整个拷贝,不要修改文件夹ARM_SOC_TOP名称。

增加PS端GPIO接口:

    • 双击最小系统的,编辑MIO Configuration,展开GPIO,选择EMIO ,并指定宽度为32(即32个IO),见右图。
    • 注意确认Bank1电压为1.8V,对应原理图BANK501的1.8V电压。
    • 增加外部GPIO口:需要在GPIO_O管脚上点击+号,扩展,然后右键点击GPIO_O,GPIO_I,GPIO_T选择make external,生产对外接口GPIO_I_0[31:0], GPIO_O_0[31:0], GPIO_T_0[31:0],如下图所示。
    • 右键点击Validate Design。

%title插图%num

%title插图%num

    • 在source中选择arm_soc_i,右键选择create HDL Wrapper,如下图。
    • Vivado会自动更新wrapper的verilog文件,如右边三图。
    • 备注:如果生成v文件不正确,可能需要删除原有wrapper文件,右键点击Reset output files,关闭重新打开Vivado,右键点击Generate output files,重新右键选择create HDL Wrapper,这样才能确保正确的自动更新verilog文件。

%title插图%num

%title插图%num %title插图%num %title插图%num

    • 在第二章的基础上,修改arm_soc_top文件中,更新arm_soc_wrapper模块在top层接口,连接wrapper接口到LED输出。注意arm_soc_wrapper.v文件是自动生成,不用修改。
    • arm_soc_top模块定义部分修改如下,对外只增加了8位LED输出,代码修改如下:

inout DDR_we_n,

// user added

inout [7:0]EGPIO_OUT,

// user added

inout FIXED_IO_ddr_vrn,

    • 将EGPIOIO_INOUT[7:0]连接对应到开发板上的8个LED。主程序增加8位LED输出,连接所有GPIO口到wire,部分代码修改如下:

// user added

wire [31:0] EGPIO_tri_i;

wire [31:0] EGPIO_tri_o;

wire [31:0] EGPIO_tri_t;

assign EGPIO_OUT [7:0]=EGPIO_tri_o [7:0]; // only 8 bit LED

// user added

    • arm_soc_wrapper已经增加了32位GPIO输出,需要在arm_soc_top做对应修改,部分代码修改如下:

.DDR_reset_n(DDR_reset_n),

.DDR_we_n(DDR_we_n),

// user added

.GPIO_O_0(EGPIO_tri_o[31:0]),

.GPIO_I_0(EGPIO_tri_i[31:0]),

.GPIO_T_0(EGPIO_tri_t[31:0]),

// user added

.FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn),

.FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp),

硬件电路如下页图3-2:功能介绍如下:

    • 与FPGA相接的网络名称为GPIO_DIP_SW0--GPIO_DIP_SW7,IO的电平为1.5V
    • 当FPGA的IO为输出时,LED 0-7指示FPGA IO的输出状态,(此时无论开关处于什么状态,LED的指示只与FPGA的IO输出状态相关)
    • 当FPGA的IO为输入时,LED仅指示开关的状态(由于每个MOS管与FPGA及开关相连的节点都有一个4.7K的下拉电阻)
    • 更详细的设计参考BM7030 原理图

%title插图%num

8位LED

ARM驱动接口

直插开关与FPGA管脚的映射表

名称 GPIO_

DIP_

SW0

GPIO_

DIP_

SW1

GPIO_

DIP_

SW2

GPIO_

DIP_

SW3

GPIO_

DIP_

SW4

GPIO_

DIP_

SW5

GPIO_

DIP_

SW6

GPIO_

DIP_

SW7

FPGA

PIN

A17 E8 C6 B9 B6 H6 H7 G9

修改约束文件:

    • 修改arm_soc_top.xdc文件,对应FPGA_IO管脚与开关,LED的映射关系添加约束到文件中

接着执行

    • Run Synthesis,
    • Run Implementation
    • Generate Bitstream,
    • Export Hardware –Include bitstream

修改约束文件代码如下:

set_property PACKAGE_PIN A17 [get_ports EGPIO_OUT[0]]

set_property IOSTANDARD LVCMOS15 [get_ports EGPIO_OUT[0]]

set_property PACKAGE_PIN E8 [get_ports EGPIO_OUT[1]]

set_property IOSTANDARD LVCMOS15 [get_ports EGPIO_OUT[1]]

set_property PACKAGE_PIN C6 [get_ports EGPIO_OUT[2]]

set_property IOSTANDARD LVCMOS15 [get_ports EGPIO_OUT[2]]

set_property PACKAGE_PIN B9 [get_ports EGPIO_OUT[3]]

set_property IOSTANDARD LVCMOS15 [get_ports EGPIO_OUT[3]]

set_property PACKAGE_PIN B6 [get_ports EGPIO_OUT[4]]

set_property IOSTANDARD LVCMOS15 [get_ports EGPIO_OUT[4]]

set_property PACKAGE_PIN H6 [get_ports EGPIO_OUT[5]]

set_property IOSTANDARD LVCMOS15 [get_ports EGPIO_OUT[5]]

set_property PACKAGE_PIN H7 [get_ports EGPIO_OUT[6]]

set_property IOSTANDARD LVCMOS15 [get_ports EGPIO_OUT[6]]

set_property PACKAGE_PIN G9 [get_ports EGPIO_OUT[7]]

set_property IOSTANDARD LVCMOS15 [get_ports EGPIO_OUT[7]]

这里IOSTANDARD 为什么采用LVCMOS15,与所在的BANK有关,BM7030由于PL端的IO有限,因此将DDR3(PL)所在的BANK剩余管脚作为LED,SWITCH的输入、输出人机界面的接口,请注意在使用过程中电平转换。

进入SDK环境,如果需要清空sdk文件,操作如下:

    • 删除ARM_SOC_TOP.sdk文件夹中的所有文件
    • 重新Export Hardware –Include bitstream

%title插图%num

    • File New Application project
    • Project name设置为arm_soc_seg,点击next,如下图
    • 仍然选择Hello World模板,参考第二章的操作

%title插图%num

    • 在sdk中设置不自动编译,这样修改后不会反复编译项目
    • 进入项目,右键点击helloworld.c,重命名为zynq_7_led.c,如下两个图
    • 后面就可以编写c文件来进行软硬件操作。

%title插图%num

%title插图%num

    • PS端有三种GPIO:MIO、EMIO、AXI_GPIO。其中MIO和EMIO是直接挂在PS上的GPIO。
    • AXI_GPIO是通过AXI总线挂在PS上的GPIO上。
    • MIO在zynq上的管脚是固定的(而且多数已经被分配为固定功能,如UART,Enet , USB,SPI,SD CARD等),而EMIO,是通过PL部分扩展的,所以使用EMIO时候需要在约束文件中分配管脚,所以设计EMIO的程序时需要生成PL部分的bit文件,烧写到FPGA中。(注意:不能整体将MIO设成EMIO,否则SOC的原有功能将会受到影响,只能将本次设计中没有用到MIO映射成EMIO)
    • MIO共占54bit,分布在GPIO BANK0, BANK1上,其中BANK0 32个MIO ,MIO0-31, BANK1 有22个MIO,MIO32-53. 注意:这里的BANK是GPIO的逻辑BANK与实际物理的BANK(如 BANK500,BANK501等)不同,其中MIO占用IO号为0-53。
    • 而EMIO占64bit。分布在GPIO BANK2,BANK3上,每个BANK上有32个GPIO,EMIO占用IO号为54-117,这个编号是仅接着MIO的编号进行的。
    • 无论是EMIO还是MIO都属于PS上的IO,直接由PS操作。在调用头文件,只调用#include “xgpiops.h”即可,而在调用AXI_GPIO时,则需要#include “xgpio.h”。

%title插图%num

MIO或EMIO可以有两种操作方式:

    • 一种是基于管脚(PIN) 的操作,对应一组操作函数。
    • 另一种是基于 BANK的操作,对应另一种操作函数。
    • 两种操作函数都在”xgpiops.h”中定义,”xgpiops.c”是实现函数。
    • MIO pin number 0-53(54个)
    • EMIO pin number 54-117(64个)
    • 举例如下:

int i=0; for (i=0;i<8;i++)

{

XGpioPs_SetDirectionPin(&psGpioInstancePtr,54+i,1);

GpioPs_WritePin(&psGpioInstancePtr,54+i, 1);

}

设置所有LED管脚为输出,且点亮8个灯。

/* Pin APIs in xgpiops.c */ 对Pin的API操作,区别在于有“Pin”

u32 XGpioPs_ReadPin(XGpioPs *InstancePtr, u32 Pin);

//读取指定管脚号对应的输入值

void XGpioPs_WritePin(XGpioPs *InstancePtr, u32 Pin, u32 Data);

//写入对应管脚号的值,32位Data,只有最低位有效

void XGpioPs_SetDirectionPin(XGpioPs *InstancePtr, u32 Pin, u32 Direction);//指定对应管脚号的管脚方向,0输入,1为输出

u32 XGpioPs_GetDirectionPin(XGpioPs *InstancePtr, u32 Pin);

//读取该管脚的方向

void XGpioPs_SetOutputEnablePin(XGpioPs *InstancePtr, u32 Pin, u32 OpEnable);
//设置该输出管脚的使能,0 disable,1 enable

u32 XGpioPs_GetOutputEnablePin(XGpioPs *InstancePtr, u32 Pin);

//读取该管脚的使能值

下边列出了基于BANK操作的一组函数,对应关系如下:

举例如下:

While(1)

{

XGpioPs_SetDirection(&psGpioInstancePtr,2, 0xff);

XGpioPs_Write(&psGpioInstancePtr, 2, 1);

}

设置所有Bank2管脚为输出,所有管脚为高,点亮所有LED。

/* Bank APIs in xgpiops.c */ 对Bank的API操作,区别在于没有“Pin”,功能说明参考pin的API说明

u32 XGpioPs_Read(XGpioPs *InstancePtr, u8 Bank);

void XGpioPs_Write(XGpioPs *InstancePtr, u8 Bank, u32 Data);

void XGpioPs_SetDirection(XGpioPs *InstancePtr, u8 Bank, u32 Direction);

u32 XGpioPs_GetDirection(XGpioPs *InstancePtr, u8 Bank);

void XGpioPs_SetOutputEnable(XGpioPs *InstancePtr, u8 Bank, u32 OpEnable);

u32 XGpioPs_GetOutputEnable(XGpioPs *InstancePtr, u8 Bank);

void XGpioPs_GetBankPin(u8 PinNumber,u8 *BankNumber, u8 *PinNumberInBank);

XGpioPs.C结构体的定义( XGpioPs_Config ):

typedef struct {

u16 DeviceId; /**< Unique ID of device */

//GPIO的ID,一共有118个,54个MIO,64个EMIO

u32 BaseAddr; /**< Register base address */

//PS端GPIO的初始物理地址,对应ID为0的物理地址

} XGpioPs_Config;

XGpioPs.C中的结构体定义( XGpioPs):

typedef struct {

XGpioPs_Config GpioConfig; /**< Device configuration */

u32 IsReady; /**< Device is initialized and ready */

XGpioPs_Handler Handler; /**< Status handlers for all banks */

void *CallBackRef; /**< Callback ref for bank handlers */

u32 Platform; /**< Platform data */

u32 MaxPinNum;/**< Max pins in the GPIO device */

u8 MaxBanks; /**< Max banks in a GPIO device */

} XGpioPs

XGpioPs举例:

{GpioConfig={DeviceId=0x0000, BaseAddr=0xe000a000},

IsReady=0x11111111,

Handler=0x001022c8,

CallBackRef=0x00000000,

Platform=0x00000004,

MaxPinNum=0x00000076,

MaxBanks=0x04}

#include <stdio.h>

#include “platform.h”

#include “xil_printf.h”

#include “xgpiops.h“ //头文件

int main()

{ init_platform();//平台初始化

static XGpioPs psGpioInstancePtr;

XGpioPs_Config *GpioConfigPtr;

//定义PS指针,和GPIO指针初始值

GpioConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);

//配置GPIO指针值和物理地址,DeviceId=0x0000, BaseAddr=0xe000a000

//参考system.hdf硬件描述文件文件

if (GpioConfigPtr == NULL)

return XST_FAILURE;

int xStatus;

xStatus = XGpioPs_CfgInitialize(&psGpioInstancePtr, GpioConfigPtr, GpioConfigPtr->BaseAddr);

//EMIO的初始化,定义118个GPIO,4个BANK及其他PS参数

if (XST_SUCCESS != xStatus)

print(“PS GPIO INIT FAILED \n\r”);}

基于PIN number的编程实现例程:

1)添加头文件

#include “xgpiops.h“

2)将八位LED输出设计成约每秒中,奇数和偶数交替点亮。

3)总共3种状态:

(1)LED 0,2,4,8 点亮,

(2)LED 1,3,5,7 点亮,

(3)全灭的状态

注意:注释的部分顺便介绍了MIO的用法,如MIO pin 7

//定义延时函数

static void delay(int dly)

{

int i, j;

for (i = 0; i < dly; i++) {

for (j = 0; j < 0xffff; j++) {

;

}

}

}

static XGpioPs psGpioInstancePtr; //定义PS的GPIO指针,如果用到MIO和EMIO也只要定义这一个就行

XGpioPs_Config *GpioConfigPtr; //XGpioPs 结构体中还包含一个结构体,查bsp中的h文件

int xStatus;

//static int iPinNumber = 7;

/*Led LD9 is connected to MIO pin 7*/

static int iPinNumberEMIO = 54;

GpioConfigPtr =XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);

//设置管脚的方向,并使能

int i=0;

for (i=0;i<8;i++)

{ XGpioPs_SetDirectionPin(&psGpioInstancePtr,54+i,1); //设置管脚的方向,配置EMIO输出方向,0输入1输出

XGpioPs_SetOutputEnablePin(&psGpioInstancePtr,54+i,1); //使能LED管脚

} }

while(1)

{ //Pin 操作

for(i=0;i<8;i++)

{ XGpioPs_WritePin(&psGpioInstancePtr,54+i,i+1); }

delay(1000); //LED0,2,4,6点亮

for(i=0;i<8;i++)

{ XGpioPs_WritePin(&psGpioInstancePtr,54+i,i); }

delay(1000); //LED1,3,5,7点亮,交替亮

for(i=0;i<8;i++)

{ XGpioPs_WritePin(&psGpioInstancePtr,54+i,0); }

delay(1000); //全熄灭

}

编译运行,并观察BM7030开发板上LED的运行状态。可以按F11和F6进行Debug单步运行检查问题,观察变化。

改写程序为基于BANK寻址的控制方式:

保持FPGA_SOC 的Verilog设置不变,修改software部分,进行编译,观察运行结果:

//Bank 操作

XGpioPs_SetDirection(&psGpioInstancePtr,2, 0xff); //Bank2 都 设为输出

XGpioPs_Write(&psGpioInstancePtr, 2, 0xff); //通过Bank2都设为高,点亮所有灯,11111111

delay(1000);

XGpioPs_Write(&psGpioInstancePtr, 2, 0x55); // 奇数灯亮,01010101

delay(1000);

XGpioPs_Write(&psGpioInstancePtr, 2, 0xaa); //偶数灯亮,10101010

delay(1000);

XGpioPs_Write(&psGpioInstancePtr, 2, 0); //全熄灭

delay(1000);

在第三章的基础上,修改arm_soc_top文件中和约束文件,增加调试接口,修改如下:

arm_soc_top模块定义部分修改如下,原有EGPIO_OUT修改为EGPIO_INOUT,代码修改如下: inout DDR_we_n,

// user added

inout [7:0]EGPIO_INOUT,

// user added

inout FIXED_IO_ddr_vrn,

    • 连接所有GPIO口到wire,为了在vivado的观察信号的波形输出,增加调试mark_debug,详细步骤请参照BM7030 FPGA 设计使用部分
    • 部分代码修改如下:

// user added

(*mark_debug=”true”*)wire [31:0] EGPIO_tri_o;

(*mark_debug=”true”*)wire [31:0] EGPIO_tri_i;

(*mark_debug=”true”*)wire [31:0] EGPIO_tri_t;

//GPIO wire connection

arm_soc_top模块定义部分修改如下,所有输出EGPIO_INOUT端口一共32位修改为可以输入可以输出,修改如下:

genvar i;

generate

for (i=0;i<32;i=i+1) begin

assign EGPIO_INOUT=EGPIO_tri_t?1’bZ:EGPIO_tri_o;

//GPIO as output wire connection

assign EGPIO_tri_i= EGPIO_INOUT;

//GPIO as input wire connection

end

endgenerate

    • 单步调试,观察运行结果:
    • (1) 设置GPIO的方向为输出,对应软硬件如图3-4,3-5所示:
    • 注意: XGpioPs_SetDirection(&psGpioInstancePtr,2, 0xff);

EGPIO_T[31:0]: 输出为FFFFFF00)

XGpioPs_Write(&psGpioInstancePtr, 2, 0x55);

%title插图%num

%title插图%num

%title插图%num

%title插图%num

单步调试,观察运行结果:

(3)XGpioPs_Write(&psGpioInstancePtr, 2, 0xAA);

(4)XGpioPs_SetDirection(&psGpioInstancePtr,2, 0x00);

// XGpioPs_SetOutputEnable(&psGpioInstancePtr, 2,0×00);

k=XGpioPs_Read(&psGpioInstancePtr, 2);

xil_printf(“switch value,k= %x\n\r”,k);

(5)观察 SDK Terminal 的运行结果,是否与开关的状态一致。

%title插图%num

%title插图%num

%title插图%num

%title插图%num

%title插图%num

实验结果:EMIO输出状态时,流水灯奇数偶数交替亮,输入状态时,串口可以获取开关变量的值。

Posted in SoC, SoC, 教材与教案

发表评论

相关链接