zynq7020AMP(LINUX+裸機)核間中斷 SGI
阿新 • • 發佈:2020-07-27
zynq 7020的核間中斷SGI
手裡有塊7020的開發板
想做zynq的核間中斷的原始驅動力是最開始做amp的測試(一個核跑linux +一個核跑裸機)
關於amp的實現方式賽靈思提供了
ug1186即openamp
xapp1087兩種方式,這兩個文件在賽靈思的官網都可以下到
從版本管理的角度來說,個人認為openamp的框架要好於xapp1087提供的方式,將裸核的韌體作為一個特殊的應用版本管起來就行了,並且remoteproc和rpmsg都是linux的成熟框架,事實用起來也沒什麼問題
看到xapp1078後,由此想到,自己實現一個通過ocm或者ddr一片特殊區域實現一個自己的核間通訊,即兩核通過共享記憶體交換資料,通過sgi軟中斷來通知對方,這樣一來可以作為rpmsg的方案備選,二來通訊的資料量什麼的都可控一些
想起來比較容易,也比較清晰,但是做起來,想查查資料,發現少的可憐,
主要的難點在於核間中斷,記憶體的讀寫,沒什麼說的,主要用到是sgi
如果雙核都各自跑裸機程式,實際上也變得簡單了,問題是,我是一個核跑linux,一個核跑裸機,神煩
最後把gic的程式碼擼了擼,沒擼太明白,但是不幸中的萬幸,嘗試了嘗試,實現了,過程不多說,直接上結果
sgi一共支援最多16個,這裡把0給了cpu0,1給了裸核
linux這端需要加的程式碼
static void icc_kick(void) { printk("icc interrupt \r\n"); printk("!!!!!!!!!!!!!!!!!!!!\r\n"); } ssize_t irq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u8 irqnum = buf[0] - '0'; if(irqnum >= 16) dev_err(dev, "Invalid soft IRQ num %u\n", irqnum); else gic_raise_softirq(cpumask_of(1), irqnum); return count; } static DEVICE_ATTR_WO(irq); //我為了省事就直接加在remoteproc的節點下了,如果有需要就自己寫個節點往裡一加就行了, //這塊就是生成個irq的節點,不太明白的看看sys檔案系統,就行了 static int zynq_remoteproc_probe(struct platform_device *pdev) { .... ret = set_ipi_handler(0, icc_kick, "icc kick");//可以從裝置樹獲取 ret = device_create_file(&local->rproc->dev, &dev_attr_irq); if (ret) { dev_err(&pdev->dev, "device_create_file %s %d\n", dev_attr_irq.attr.name, ret); goto attr_up_err; } attr_up_err: device_remove_file(&local->rproc->dev, &dev_attr_irq) .... }
裸機這邊icc.c
/* * Copyright (c) 2014, Mentor Graphics Corporation * All rights reserved. * * Copyright (C) 2017 Xilinx, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1\. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2\. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3\. Neither the name of Mentor Graphics Corporation nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /************************************************************************************** * This is a sample demonstration application that showcases usage of rpmsg * This application is meant to run on the remote CPU running bare-metal code. * It echoes back data that was sent to it by the master core. * * The application calls init_system which defines a shared memory region in * MPU settings for the communication between master and remote using * zynqMP_r5_map_mem_region API,it also initializes interrupt controller * GIC and register the interrupt service routine for IPI using * zynqMP_r5_gic_initialize API. * * Echo test calls the remoteproc_resource_init API to create the * virtio/RPMsg devices required for IPC with the master context. * Invocation of this API causes remoteproc on the bare-metal to use the * rpmsg name service announcement feature to advertise the rpmsg channels * served by the application. * * The master receives the advertisement messages and performs the following tasks: * 1\. Invokes the channel created callback registered by the master application * 2\. Responds to remote context with a name service acknowledgement message * After the acknowledgement is received from master, remoteproc on the bare-metal * invokes the RPMsg channel-created callback registered by the remote application. * The RPMsg channel is established at this point. All RPMsg APIs can be used subsequently * on both sides for run time communications between the master and remote software contexts. * * Upon running the master application to send data to remote core, master will * generate the payload and send to remote (bare-metal) by informing the bare-metal with * an IPI, the remote will send the data back by master and master will perform a check * whether the same data is received. Once the application is ran and task by the * bare-metal application is done, master needs to properly shut down the remote * processor * * To shut down the remote processor, the following steps are performed: * 1\. The master application sends an application-specific shut-down message * to the remote context * 2\. This bare-metal application cleans up application resources, * sends a shut-down acknowledge to master, and invokes remoteproc_resource_deinit * API to de-initialize remoteproc on the bare-metal side. * 3\. On receiving the shut-down acknowledge message, the master application invokes * the remoteproc_shutdown API to shut down the remote processor and de-initialize * remoteproc using remoteproc_deinit on its side. * **************************************************************************************/ #include "xil_printf.h" #include "openamp/open_amp.h" #include "rsc_table.h" #include "icc.h" #define LPRINTF(format, ...) xil_printf(format, ##__VA_ARGS__) #define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) #define INTC_DEVICE_ID XPAR_PS7_SCUGIC_0_DEVICE_ID XScuGic InterruptController; /* Instance of the Interrupt Controller */ u32 SGI_INTR; int SGI_trigered; void SGI0_INTR_ID_ISR (void); #define DELAY 50000000 #define COMM_VAL (*(volatile unsigned long *)(0xFFFF8000)) #define COMM_TX_FLAG (*(volatile unsigned long *)(0xFFFF9000)) #define COMM_TX_DATA (*(volatile unsigned long *)(0xFFFF9004)) #define COMM_RX_FLAG (*(volatile unsigned long *)(0xFFFF9008)) #define COMM_RX_DATA (*(volatile unsigned long *)(0xFFFF900C)) int uart_init(void) { u32 RegValue; /* ps7_clock_init_data_3_0 */ RegValue = Xil_In32(0xF8000008); RegValue &= ~0x0000FFFF; RegValue |= 0x0000DF0D; Xil_Out32(0xF8000008, RegValue); /* Only Enable Uart Reference Clock */ RegValue = Xil_In32(0xF8000154); RegValue &= ~0x00003F33; RegValue |= 0x00000A03; Xil_Out32(0xF8000154, RegValue); RegValue = Xil_In32(0xF800012C); RegValue &= ~0x01FFCCCD; RegValue |= 0x017D444D; Xil_Out32(0xF800012C, RegValue); RegValue = Xil_In32(0xF8000004); RegValue &= ~0x0000FFFF; RegValue |= 0x0000767B; Xil_Out32(0xF8000004, RegValue); } u32 SetupSGIIntrSystem(XScuGic *IntcInstancePtr,Xil_InterruptHandler Handler, u32 DeveiceId, u32 SgiIntr, u32 CpuNo) { int Status; XScuGic_Config *IntcConfig; IntcConfig = XScuGic_LookupConfig(DeveiceId); if (NULL == IntcConfig) { return XST_FAILURE; } Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,IntcConfig->CpuBaseAddress); if (Status != XST_SUCCESS) { return XST_FAILURE; } XScuGic_SetPriorityTriggerType(IntcInstancePtr, SgiIntr,0xd0, 0x3); Status = XScuGic_Connect(IntcInstancePtr, SgiIntr, (Xil_ExceptionHandler)Handler,0); if (Status != XST_SUCCESS) { return XST_FAILURE; } XScuGic_Enable(IntcInstancePtr, SgiIntr); XScuGic_InterruptMaptoCpu(IntcInstancePtr,CpuNo,SgiIntr); return XST_SUCCESS; } void ExceptionSetup(XScuGic *IntcInstancePtr) { /* * Initialize the exception table */ Xil_ExceptionInit(); /* * Register the interrupt controller handler with the exception table */ Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, IntcInstancePtr); /* * Enable non-critical exceptions */ Xil_ExceptionEnable(); } void SGI1_INTR_ID_ISR (void) { LPRINTF("CPU0: The software interrupt0 has been triggered\n\r"); SGI_trigered=1; } /*-----------------------------------------------------------------------------* * Application entry point *-----------------------------------------------------------------------------*/ int main(void) { int Status; SGI_INTR=SGI1_INTR_ID; SGI_trigered=0; uart_init(); Status = SetupSGIIntrSystem(&InterruptController,(Xil_ExceptionHandler)SGI1_INTR_ID_ISR, INTC_DEVICE_ID, SGI_INTR,CPU_NO1); if (Status != XST_SUCCESS) { return XST_FAILURE; } ExceptionSetup(&InterruptController); LPRINTF("Starting application...\r\n"); /* Suspend processor execution */ while(1) { while(SGI_trigered==0) { LPRINTF("CPU1 runing...\n\r"); sleep(2); } SGI_trigered=0; for(int i=0;i<DELAY;i++); LPRINTF("CPU0: Trigger interrupt1 to CPU1\n\r"); for(int i=0;i<DELAY;i++); Status = XScuGic_SoftwareIntr(&InterruptController,SGI0_INTR_ID,0x1<<CPU_NO0); if (Status != XST_SUCCESS) { return XST_FAILURE; } } return Status; }
icc.h
#include "xscugic.h"
#include "xil_io.h"
#ifndef SRC_ZYNQ_SGI_H_
#define SRC_ZYNQ_SGI_H_
/*Register ICDSGIR setting*/
#define SGI0_INTR_ID 0
#define SGI1_INTR_ID 1
#define SGI2_INTR_ID 2
#define SGI3_INTR_ID 3
#define SGI4_INTR_ID 4
#define SGI5_INTR_ID 5
#define SGI6_INTR_ID 6
#define SGI7_INTR_ID 7
#define SGI8_INTR_ID 8
#define SGI9_INTR_ID 9
#define SGI10_INTR_ID 10
#define SGI11_INTR_ID 11
#define SGI12_INTR_ID 12
#define SGI13_INTR_ID 13
#define SGI14_INTR_ID 14
#define SGI15_INTR_ID 15
/*TargetListFilter bits[25:24]
* 0b00: send the interrupt to the CPU interfaces
specified in the CPUTargetList field
0b01: send the interrupt to all CPU interfaces
except the CPU interface that requested the
interrupt
0b10: send the interrupt on only to the CPU
interface that requested the interrupt
0b11: reserved*/
#define TRIGGER_SELECTED 0x00000000
#define TRIGGER_SELF 0x02000000
#define TRIGGER_OTHER 0x01000000
/*CPUTargetList bits[23:16]
When TargetListFilter is 0b00, defines the CPU
interfaces the Distributor must send the interrupt
to.
Each bit refers to the corresponding CPU
interface.*/
#define CPU_NO0 0
#define CPU_NO1 1
#define CPU_ID_LIST 0x00010000
/*ICDSGIR SGI觸發暫存器地址*/
#define ICDSGIR 0xF8F01F00
#define ICDIPTR 0xF8F01800
/* SGI中斷初始化函式
* 該函式初始化指定的SGI中斷到本CPU上並繫結對應的ISR
* 引數說明:
* XScuGic *IntcInstancePtr 中斷控制器例項
* Xil_InterruptHandler Handler ISR函式名
* u32 INTC_DEVICE_ID 中斷控制器裝置號
* u32 SGI_INTR SGI中斷號
* CPU_NO 運行當前程式的CPU號
*/
u32 SetupSGIIntrSystem(XScuGic *IntcInstancePtr,Xil_InterruptHandler Handler,
u32 INTC_DEVICE_ID,u32 SGI_INTR,u32 CPU_NO);
/* Exception初始化函式
* 引數說明:
* XScuGic *IntcInstancePtr 中斷控制器例項
*/
void ExceptionSetup(XScuGic *IntcInstancePtr);
/* SGI觸發函式
* 該函式觸發指定的SGI到指定的CPU上
* 引數說明:
* u32 CPUNO 目標CPU號
* u32 INTR SGI中斷號
* u32 TargetSelect 選擇過濾,該引數決定了CPUNO是否有效
* TRIGGER_SELECTED 0x00000000 根據CPUNO選擇觸發的CPU
* TRIGGER_SELF 0x02000000 觸發自身,此時CPUNO無效
* TRIGGER_OTHER 0x01000000 觸發除了自身的其他CPU,此時CPUNO無效
*/
void SGITriggerTargetCPU(u32 CPUNO,u32 INTR,u32 TargetSelect);
/* 設定中斷觸發的目標CPU
* 引數說明:
* u32 INTR 中斷號
* u32 CPUID 目標CPU的ID號
*/
void SetINTRTargetCPU(u32 INTR,u32 CPUID);
#endif /* SRC_ZYNQ_SGI_H_ */
上機測一下
左側uart是linux右側uart是裸核,效果槓槓的
轉發註明出處,謝謝