图 13.4.7 设置CPU1板级支持包
在弹出的界面中,点击ps7_cortexa9_1,在extra_compiler_flags一栏的Value下,补充“-DUSE_AMP=1”,设置完成后点击“OK”按钮,如下图所示:
图 13.4.8 设置双核启动
接下来编写CPU0的用户代码。在cpu0_uart/src目录上右键,选择New->Source File,在弹出的对话框中Source file一栏我们输入文件名“cpu0_uart.c” ,然后点击“Finish”。
我们在新建的cpu0_uart.c文件中输入本次实验的代码。代码的主体部分如下所示:
- 1 #include "xparameters.h"
- 2 #include "xscugic.h"
- 3 #include "xil_printf.h"
- 4 #include "xil_exception.h"
- 5 #include "xil_mmu.h"
- 6 #include "stdio.h"
- 7
- 8 //宏定义
- 9 #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //中断ID
- 10 #define SHARE_BASE 0xffff0000 //共享OCM首地址
- 11 #define CPU1_START_ADDR 0xfffffff0 //CPU1开始地址
- 12 #define CPU1_START_MEM 0x10000000 //CPU1程序开始地址
- 13
- 14 #define SOFT_INTR_ID_TO_CPU0 0 //软件中断号 0 ,范围:0~15
- 15 #define SOFT_INTR_ID_TO_CPU1 1 //软件中断号 1 ,范围:0~15
- 16 #define CPU1_ID 2 //CPU1 ID,0bxxxxxx1x指向CPU1
- 17 //"SEV"指令唤醒CPU1并跳转至相应的程序
- 18 #define sev() __asm__("sev")
- 19
- 20 //函数声明
- 21 void start_cpu1();
- 22 void cpu0_intr_init(XScuGic *intc_ptr);
- 23 void soft_intr_handler(void *CallbackRef);
- 24
- 25 //全局变量
- 26 XScuGic Intc; //中断控制器驱动程序实例
- 27 int rec_freq_flag = 0; //接收到呼吸灯频率设置的标志
- 28 int freq_gear; //频率档位
- 29
- 30 //CPU0 main函数
- 31 int main()
- 32 {
- 33 //S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0
- 34 Xil_SetTlbAttributes(SHARE_BASE,0x14de2); //禁用OCM的Cache属性
- 35
- 36 //启动CPU1
- 37 start_cpu1();
- 38 //CPU0中断初始化
- 39 cpu0_intr_init(&Intc);
- 40 while(1){
- 41 if(rec_freq_flag == 0){
- 42 xil_printf("This is CPU0,Please input the numbers 1~5 to change "
- 43 "breath led frequency\r\n");
- 44 scanf("%d",&freq_gear);
- 45 if(freq_gear >= 1 && freq_gear <=5){
- 46 xil_printf("You input number is %d\r\n",freq_gear);
- 47 //向共享的地址中写入输入的数据
- 48 Xil_Out8(SHARE_BASE,freq_gear);
- 49 //给CPU1触发中断
- 50 XScuGic_SoftwareIntr(&Intc,SOFT_INTR_ID_TO_CPU1,CPU1_ID);
- 51 rec_freq_flag = 1;
- 52 }
- 53 else{
- 54 xil_printf("Error,The number range is 1~5\r\n");
- 55 xil_printf("\r\n");
- 56 }
- 57 }
- 58 }
- 59 return 0 ;
- 60 }
在主函数中,首先通过Xil_SetTlbAttributes()函数禁用Cache缓存,以维护两个CPU访问OCM一致性的问题。其中输入参数SHARE_BASE为共享内存的基地址,这个基地址在lscript.ld可以查找到,即ps7_ram0的基地址或者ps_ram_1的基地址,本次实验使用ps7_ram_1作为两个CPU通信的共享内存。
然后通过start_cpu1()函数启动CPU1,需要注意的是,如果只是通过JTAG模式下载程序的话,是可以不编写这个启动函数,这个函数仅在固化程序的时候起作用。
接下来通过cpu0_intr_init()函数对CPU0的中断进行初始化,以响应CPU1的中断。
最后是一个while(1)的无限循环。首先判断rec_freq_flag(接收到呼吸灯频率设置的标志)的值是否为0,如果等于0,此时打印字符来提示用户输入数据,输入值的范围为1~5,输入的值越大,呼吸灯的呼吸频率就越高。当用户输入正确的值之后,打印用户输入的值,并通过Xil_Out8()函数将数值写入OCM存储器中。然后通过XScuGic_SoftwareIntr()函数向CPU0触发中断。其中函数第二参数为软件中断号,第三个参数为CPU1的ID。由UG585文档可以查询到,0bxxxxxxx1 指向CPU0,0bxxxxxx1x指向CPU1,程序中将CPU1的ID设置为2。
触发CPU1中断后,将rec_freq_flag(接收到呼吸灯频率设置的标志)设置为1,while循环停止接收用户输入的数据。
如果用户输入的值不是1~5之间的数字,则返回错误,用户重新输入数字。
我们在程序中,除了main()函数之外,另外还编写了三个函数:start_cpu1()、cpu0_intr_init()和soft_intr_handler()。代码如下所示:
- 62 //启动CPU1,用于固化程序
- 63 void start_cpu1()
- 64 {
- 65 //向 CPU1_START_ADDR(0Xffffffff0)地址写入 CPU1 的访问内存基地址
- 66 Xil_Out32(CPU1_START_ADDR, CPU1_START_MEM);
- 67 dmb(); //等待内存写入完成
- 68 sev(); //通过"SEV"指令唤醒CPU1并跳转至相应的程序
- 69 }
- 70
- 71 //CPU0中断初始化
- 72 void cpu0_intr_init(XScuGic *intc_ptr)
- 73 {
- 74 //初始化中断控制器
- 75 XScuGic_Config *intc_cfg_ptr;
- 76 intc_cfg_ptr = XScuGic_LookupConfig(INTC_DEVICE_ID);
- 77 XScuGic_CfgInitialize(intc_ptr, intc_cfg_ptr,
- 78 intc_cfg_ptr->CpuBaseAddress);
- 79 //设置并打开中断异常处理功能
- 80 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
- 81 (Xil_ExceptionHandler)XScuGic_InterruptHandler, intc_ptr);
- 82 Xil_ExceptionEnable();
- 83
- 84 XScuGic_Connect(intc_ptr, SOFT_INTR_ID_TO_CPU0,
- 85 (Xil_ExceptionHandler)soft_intr_handler, (void *)intc_ptr);
- 86
- 87 XScuGic_Enable(intc_ptr, SOFT_INTR_ID_TO_CPU0); //CPU0软件中断
- 88 }
- 89
- 90 //软件中断函数
- 91 void soft_intr_handler(void *CallbackRef)
- 92 {
- 93 xil_printf("This is CPU0,Soft Interrupt from CPU1\r\n");
- 94 xil_printf("\r\n");
- 95 rec_freq_flag = 0;
- 96 }
代码中第接下来通过start_cpu1()函数启动CPU1,启动函数的代码如代码中第62至第69行代码所示。需要注意的是,如果只是通过JTAG模式下载程序的话,可以不编写这个启动函数,这个函数仅在固化程序的时候起作用。启动CPU1的方法在UG585文档中有着详细的介绍,即通过Xil_Out32()函数向CPU1的开始地址写入CPU1的DDR3内存地址,等待内存写入完成,然后通过“SEV”指令唤醒CPU1并跳转至相应的程序,UG585文档中启动CPU1的描述如下图所示:
图 13.4.9 启动CPU1
cpu0_intr_init()函数用户对CPU0的中断进行初始化。程序将中断函数设置为soft_intr_handler(),软件中断号的范围是0~15之间,这里设置为0。
而soft_intr_handler()函数为CPU1触发CPU0中断的软件中断函数,当CPU0进入中断后,打印字符并将rec_freq_flag设置为0,以重新接收用户输入的呼吸灯频率。
程序设计完成后,按快捷键Ctrl S保存main.c文件,工具会自动进行编译。编译完成后控制台(Console)中会出现提示信息“Build Finished”,同时在应用工程的Binaries目录下可以看到生成的elf文件。
接下来编写CPU1的用户代码。在cpu1_led/src目录上右键,选择New->Source File,在弹出的对话框中Source file一栏我们输入文件名“cpu1_led.c”,然后点击“Finish”。
我们在新建的cpu1_led.c文件中输入本次实验的代码。代码的主体部分如下所示:
- 1 #include "xparameters.h"
- 2 #include "xscugic.h"
- 3 #include "xil_printf.h"
- 4 #include "xil_exception.h"
- 5 #include "xil_mmu.h"
- 6 #include "stdio.h"
- 7 #include "breath_led_ip.h"
- 8
- 9 //宏定义
- 10 #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //中断ID
- 11 #define SHARE_BASE 0xffff0000 //共享OCM首地址
- 12 #define SOFT_INTR_ID_TO_CPU0 0 //软件中断号 0 ,范围:0~15
- 13 #define SOFT_INTR_ID_TO_CPU1 1 //软件中断号 1 ,范围:0~15
- 14 #define CPU0_ID 1 //CPU1 ID,0bxxxxxxx1指向CPU0
- 15
- 16 #define LED_IP_BASEADDR XPAR_BREATH_LED_IP_0_S0_AXI_BASEADDR //LED IP基地址
- 17 #define LED_IP_REG0 BREATH_LED_IP_S0_AXI_SLV_REG0_OFFSET //LED IP寄存器地址0
- 18 #define LED_IP_REG1 BREATH_LED_IP_S0_AXI_SLV_REG1_OFFSET //LED IP寄存器地址1
- 19
- 20 //函数声明
- 21 void cpu1_intr_init(XScuGic *intc_ptr);
- 22 void soft_intr_handler(void *CallbackRef);
- 23
- 24 //全局变量
- 25 XScuGic Intc; //中断控制器驱动程序实例
- 26 int soft_intr_flag = 0; //软件中断的标志
- 27 int freq_gear; //频率档位
- 28
- 29 //CPU1 main函数
- 30 int main()
- 31 {
- 32 int freq_step = 0;
- 33 //S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0
- 34 Xil_SetTlbAttributes(SHARE_BASE,0x14de2); //禁用OCM的Cache属性
- 35
- 36 //CPU1中断初始化
- 37 cpu1_intr_init(&Intc);
- 38 //打开呼吸灯
- 39 BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR, LED_IP_REG0, 1);
- 40 while(1){
- 41 if(soft_intr_flag){
- 42 freq_gear = Xil_In8(SHARE_BASE); //从共享OCM中读出数据
- 43 xil_printf("CUP1 Received data is %d\r\n",freq_gear) ;
- 44 switch(freq_gear){
- 45 case 1 : freq_step = 20;break;
- 46 case 2 : freq_step = 50;break;
- 47 case 3 : freq_step = 100;break;
- 48 case 4 : freq_step = 200;break;
- 49 case 5 : freq_step = 500;break;
- 50 default : freq_step = 50;break;
- 51 }
- 52 //设置呼吸灯频率,最高位为1,设置有效
- 53 BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR,LED_IP_REG1,(0x80000000|freq_step));
- 54 //给给CPU0触发中断
- 55 XScuGic_SoftwareIntr(&Intc,SOFT_INTR_ID_TO_CPU0,CPU0_ID);
- 56 soft_intr_flag = 0;
- 57 }
- 58 }
- 59 return 0 ;
- 60 }
在主函数中,同样是先通过Xil_SetTlbAttributes()函数禁用Cache缓存,以维护两个CPU访问OCM一致性的问题。然后通过cpu1_intr_init()函数用户对CPU1的中断进行初始化。中断初始化完成后,通过LED_IP_mWriteReg()函数打开呼吸灯,此时呼吸灯的频率是以默认的频率进行渐变的。
接下来同样是一个无限循环while(1),实现了根据从OCM读取到的值,来控制呼吸灯的频率。当接收到CPU0的软件中断后(soft_intr_flag的值为1),从OCM读取数据的函数为Xil_In8(),根据读取到的值,通过LED_IP_mWriteReg()函数设置输出呼吸灯的频率。这里将呼吸灯的频率共分为5档,用户输入的值越大,呼吸灯的频率步长越大,呼吸灯的呼吸频率就越大。
最后通过XScuGic_SoftwareIntr()函数给CPU0触发中断,并设置soft_intr_flag(软件中断的标志)为0。
我们在程序中,除了main()函数之外,另外还编写了两个函数:cpu1_intr_init()和soft_intr_handler()。代码如下所示:
- 62 //CPU1中断初始化
- 63 void cpu1_intr_init(XScuGic *intc_ptr)
- 64 {
- 65 //初始化中断控制器
- 66 XScuGic_Config *intc_cfg_ptr;
- 67 intc_cfg_ptr = XScuGic_LookupConfig(INTC_DEVICE_ID);
- 68 XScuGic_CfgInitialize(intc_ptr, intc_cfg_ptr,
- 69 intc_cfg_ptr->CpuBaseAddress);
- 70 //设置并打开中断异常处理功能
- 71 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
- 72 (Xil_ExceptionHandler)XScuGic_InterruptHandler, intc_ptr);
- 73 Xil_ExceptionEnable();
- 74
- 75 XScuGic_Connect(intc_ptr, SOFT_INTR_ID_TO_CPU1,
- 76 (Xil_ExceptionHandler)soft_intr_handler, (void *)intc_ptr);
- 77
- 78 XScuGic_Enable(intc_ptr, SOFT_INTR_ID_TO_CPU1); //CPU1软件中断
- 79 }
- 80
- 81 //软件中断函数
- 82 void soft_intr_handler(void *CallbackRef)
- 83 {
- 84 xil_printf("This is CUP1,Soft Interrupt from CPU0\r\n") ;
- 85 soft_intr_flag = 1;
- 86 }
cpu1_intr_init()函数对CPU1的中断进行初始化。程序将中断函数设置为soft_intr_handler(),软件中断号的范围是0~15之间,这里设置为0。
soft_intr_handler()函数为CPU0触发CPU1中断的软件中断函数,当CPU1进入中断后,打印字符并将soft_intr_flag的值设置为1,从而在main()函数中设置呼吸灯的频率。
程序设计完成后,按快捷键Ctrl S保存main.c文件,工具会自动进行编译。编译完成后控制台(Console)中会出现提示信息“Build Finished”,同时在应用工程的Binaries目录下可以看到生成的elf文件。
13.5下载验证
首先我们将下载器与领航者底板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用Mini USB连接线将开发板左侧的USB_UART接口与电脑连接,用于串口通信。最后连接开发板的电源,并打开电源开关。
在SDK软件下方的SDK Terminal窗口中点击右上角的加号设置并连接串口。
右击应用程序cpu0_uart(或者cpu1_led),选择Run As→Run Configurations,在弹出的界面中双击Xilinx C/C application(System Debug),即对下载的选项进行设置。
在Target Setup选项页中选中“Reset entire system”,此时“Program FPGA”也会自动选中,如下图所示:
图 13.5.1 “Target Setup”设置
将选项也切换至“Application”,把“ps_cortexa9_1”也选中,如下图所示: