gpt4 book ai didi

interrupt - Lego Mindstorm EV3 上的裸机中断处理(TexasInstruments Sitara AM1808 SoC)

转载 作者:行者123 更新时间:2023-12-01 14:37:31 25 4
gpt4 key购买 nike

对于一个大学项目,我们的项目团队想要为 Lego Mindstorm EV3 平台编写一个裸机操作系统。尽管我们做了很多研究和测试,但我们遇到了一个无法解决的中断处理问题。

这是平台的详细信息:
CPU:ARM926EJ-S(ARMv5架构)
SoC:德州仪器 Sitara AM1808

SoC 简短文档:http://www.ti.com/lit/ds/symlink/am1808.pdf
SoC 技术引用手册:http://www.ti.com/lit/ug/spruh82a/spruh82a.pdf

以下是我们尝试做的事情以及我们如何尝试做的事情:
我们想要初始化一个定时器中断,它应该每毫秒触发一次,以获得系统滴答声。我们操作系统的各种其他组件(例如软件-I2C-管理等)都需要此刻度。由于我们的团队对裸机编程相当陌生,我们以 TexasInstruments 为例来初始化 SoC 的定时器中断和中断 Controller 。此示例是 StarterWare 的一部分,旨在在 AM1808 评估板上运行。由于 Lego Mindstorm EV3 使用相同的 SoC,它也应该适用于我们。请参阅以下源代码:

startup.S:我们程序的入口点(基于 TI 提供的源代码)

#include "hw_aintc.h"
#include "soc_AM1808.h"

.global Entry
.global _stack
.global _bss_start
.global _bss_end
.global start_boot
.global _Reset
.global IRQHandler
.global FIQHandler
.global AbortHandler
.global SWIHandler
.global UndefInstHandler
.global CPUAbortHandler

@************************ Internal Definitions ********************************
@
@ Define the stack sizes for different modes. The user/system mode will use
@ the rest of the total stack size
@
.set UND_STACK_SIZE, 0x8
.set ABT_STACK_SIZE, 0x8
.set FIQ_STACK_SIZE, 0x8
.set IRQ_STACK_SIZE, 0x500
.set SVC_STACK_SIZE, 0x8

@
@ to set the mode bits in CPSR for different modes
@
.set MODE_USR, 0x10
.set MODE_FIQ, 0x11
.set MODE_IRQ, 0x12
.set MODE_SVC, 0x13
.set MODE_ABT, 0x17
.set MODE_UND, 0x1B
.set MODE_SYS, 0x1F

.equ I_F_BIT, 0xC0
.equ ADDR_HIPVR1, SOC_AINTC_0_REGS + AINTC_HIPVR(0)
.equ ADDR_HIPVR2, SOC_AINTC_0_REGS + AINTC_HIPVR(1)
.equ MASK_SWI_NUM, 0xFF000000

@**************************** Code Seection ***********************************
.text

@
@ This code is assembled for ARM instructions
@
.code 32
@******************************************************************************
@
@******************************************************************************
@
@ The reset handler sets up the stack pointers for all the modes. The FIQ and
@ IRQ shall be disabled during this. Then, clearthe BSS sections, switch to the
@ main() function.
@
Entry:
@
@ Set up the Stack for Undefined mode
@
LDR r0, =_stack @ Read the stack address
MSR cpsr_c, #MODE_UND|I_F_BIT @ switch to undef mode
MOV sp,r0 @ write the stack pointer
SUB r0, r0, #UND_STACK_SIZE @ give stack space
@
@ Set up the Stack for abort mode
@
MSR cpsr_c, #MODE_ABT|I_F_BIT @ Change to abort mode
MOV sp, r0 @ write the stack pointer
SUB r0,r0, #ABT_STACK_SIZE @ give stack space
@
@ Set up the Stack for FIQ mode
@
MSR cpsr_c, #MODE_FIQ|I_F_BIT @ change to FIQ mode
MOV sp,r0 @ write the stack pointer
SUB r0,r0, #FIQ_STACK_SIZE @ give stack space
@
@ Set up the Stack for IRQ mode
@
MSR cpsr_c, #MODE_IRQ|I_F_BIT @ change to IRQ mode
MOV sp,r0 @ write the stack pointer
SUB r0,r0, #IRQ_STACK_SIZE @ give stack space
@
@ Set up the Stack for SVC mode
@
MSR cpsr_c, #MODE_SVC|I_F_BIT @ change to SVC mode
MOV sp,r0 @ write the stack pointer
SUB r0,r0, #SVC_STACK_SIZE @ give stack space
@
@ Set up the Stack for USer/System mode
@
MSR cpsr_c, #MODE_SYS|I_F_BIT @ change to system mode
MOV sp,r0 @ write the stack pointer

@
@ Clear the BSS section here
@
Clear_Bss_Section:

LDR r0, =_bss_start @ Start address of BSS
LDR r1, =(_bss_end - 0x04) @ End address of BSS
MOV r2, #0
Loop:
STR r2, [r0], #4 @ Clear one word in BSS
CMP r0, r1
BLE Loop @ Clear till BSS end


@
@ Enter the start_boot function. The execution still happens in system mode
@
Enter_main:
LDR r10,=start_boot @ Get the address of start_boot
MOV lr,pc @ Dummy return
BX r10 @ Branch to start_boot

@ Interrupt Handler from exceptionhandler.S (provided by TI) - Note: the following code
@ will not be reached since start_boot contains an endless loop at it's end
@******************************************************************************
@* Function Definition of SWI Handler
@******************************************************************************
@
@ The SWI Handler switches to system mode if the SWI number is 458752. If the
@ SWI number is different, no mode switching will be done. No other SWI are
@ handled here
@
SWIHandler:
STMFD r13!, {r0-r1, r14} @ Save context in SVC stack
LDR r0, [r14, #-4] @ R0 points to SWI instruction
BIC r0, r0, #MASK_SWI_NUM @ Get the SWI number
CMP r0, #458752
MRSEQ r1, spsr @ Copy SPSR
ORREQ r1, r1, #0x1F @ Change the mode to System
MSREQ spsr_cf, r1 @ Restore SPSR
LDMFD r13!, {r0-r1, pc}^ @ Restore registers from IRQ stack

@******************************************************************************
@* Function Definition of IRQ Handler
@******************************************************************************
@
@ The IRQ handler jumps to the ISR of highest priority pending IRQ. The address
@ is taken from the HIPVR2 register, which contains the ISR address of highest
@ pending IRQ. This handler doesnot support nesting.
@
IRQHandler:
STMFD r13!, {r0-r3, r12, r14} @ Save context in IRQ stack
LDR r0, =ADDR_HIPVR2 @ R0 points to address of HIPVR2
LDR r1, [r0] @ R1 contains address of ISR
ADD r14, pc, #0 @ Save return address in LR
LDR pc, [r1] @ Go to ISR (still in IRQ mode)
LDMFD r13!, {r0-r3, r12, r14} @ Restore registers from IRQ stack
SUBS pc, r14, #0x4 @ Return to program before IRQ

@******************************************************************************
@* Function Definition of FIQ Handler
@******************************************************************************
@
@ The FIQ Handler jumps to the ISR of the highest priority pending FIQ. The
@ address is taken from HIPVR1, which contains the ISR address of the highest
@ pending FIQ. This handler doesnot support nesting
@
FIQHandler:
@
@ Save the required context in FIQ stack.
@
STMFD r13!, {r0-r3, r12, r14} @ Save context in FIQ stack
LDR r0, =ADDR_HIPVR1 @ R0 points to address of HIPVR1
LDR r1, [r0] @ R1 contains address of ISR
ADD r14, pc, #0 @ Save return address in LR
LDR pc, [r1] @ Go to ISR (still in FIQ mode)
LDMFD r13!, {r0-r3, r12, r14} @ Restore registers from FIQ stack
SUBS pc, r14, #0x4 @ Return to program state before FIQ

@******************************************************************************
@* Function Definition of Abort/Undef Handler
@******************************************************************************
@
@ The Abort handler goes to the C handler of abort mode. Note that the undefined
@ instruction is not handled separately.
@ if nothing is done in the abort mode, the execution enters infinite loop.
@
AbortHandler:
UndefInstHandler:
@
@ Disable all the interrupts
@
MRS r0, cpsr @ Read from CPSR
ORR r0, r0, #0xC0 @ Clear the IRQ and FIQ bits
MSR cpsr, r0 @ Write to CPSR
ADD r14, pc, #0 @ Store the return address
LDR pc, =CPUAbortHandler @ Go to C handler

test.c:我们的应用程序和TI提供的初始化代码
#include <stdio.h>
#include <stdlib.h>
#include <systick.h>
#include <mytypes.h>
#include "cpu.h"

extern void Entry(void);
extern void UndefInstHandler(void);
extern void SWIHandler(void);
extern void AbortHandler(void);
extern void IRQHandler(void);
extern void FIQHandler(void);

// This is our own IRQ-Handler - it is not called either, we just wanted to check if the assembler code was the source of the problem
void irqHandler(void) {
unsigned int* isr_pointer = *(unsigned int *)*((unsigned int*)0xFFFEF604);
printf("ISR Handler called: %#10x\n", isr_pointer);
typedef void func(void);
func* f = (func*) isr_pointer;
f();
}

void c_entry() {
unsigned int counter = 1;
systick_init();
U32 localSystick = systick_get_ms();
// printf will write debug output to a UART port connected to a PC
printf("Current tick: %u (%u)\n", localSystick, counter);
do {
++counter;
localSystick = systick_get_ms();
printf("Current tick: %u (%u)\n", localSystick, counter);
} while (1);
}


// Code from Startup.c (TI example)
#include "hw_syscfg0_AM1808.h"
#include "hw_syscfg1_AM1808.h"
#include "hw_pllc_AM1808.h"
#include "hw_ddr2_mddr.h"
#include "soc_AM1808.h"
#include "evmAM1808.h"
#include "hw_types.h"
#include "psc.h"

#define E_PASS 0
#define E_FAIL -1

static void CopyVectorTable(void);
void BootAbort(void);

static unsigned int const vecTbl[16]=
{
// Primary vector Table entries
(unsigned int)Entry,
(unsigned int)UndefInstHandler,
(unsigned int)SWIHandler,
(unsigned int)AbortHandler,
(unsigned int)AbortHandler,
0xE59FF010,
(unsigned int)IRQHandler,
(unsigned int)FIQHandler,
// Secondary Vector Table entires
(unsigned int)Entry,
(unsigned int)UndefInstHandler,
(unsigned int)SWIHandler,
(unsigned int)AbortHandler,
(unsigned int)AbortHandler,
0xE59FF010,
(unsigned int)IRQHandler,
(unsigned int)FIQHandler
};


/**
* \brief Boot strap function which enables the PLL(s) and PSC(s) for basic
* module(s)
*
* \param none
*
* \return None.
*
* This function is the first function that needs to be called in a system.
* This should be set as the entry point in the linker script if loading the
* elf binary via a debugger, on the target. This function never returns, but
* gives control to the application entry point
**/
unsigned int start_boot(void)
{
printf("start_boot called\n");
SysCfgRegistersLock();

/* Disable write-protection for registers of SYSCFG module. */
SysCfgRegistersUnlock();

/* Initialize the vector table with opcodes */
CopyVectorTable();

c_entry();

while(1);
}


static void CopyVectorTable(void)
{
printf("CopyVectorTable called\n");
// According to the AM1808 Technical Reference Manual (Page 88), the vector table has to be located at 0xFFFF0000
unsigned int *dest = (unsigned int *)0xFFFF0000;
unsigned int *src = (unsigned int *)vecTbl;
unsigned int count;

for(count = 0; count < sizeof(vecTbl)/sizeof(vecTbl[0]); count++)
{
dest[count] = src[count];
}
}


void BootAbort(void)
{
printf("BootAbort called");
while (1);
}

systick.c:这里我们初始化定时器中断和中断 Controller
/*
* This provides a 1000Hz tick for the system.
*
* We're using the AMT1808 Hardware Timer 2
*
* See also: timerCounter.c in TI/examples/evmAM1808/timer
*/

#include "soc_AM1808.h"
#include "hw_syscfg0_AM1808.h"
#include "interrupt.h"
#include "timer.h"
#include "evmAM1808.h"
#include "cpu.h"

#include "systick.h"

#include <stdio.h>

#define TMR_PERIOD_LSB32 (0x07FFFFFF)
#define TMR_PERIOD_MSB32 (0x0)

static volatile U32 systick_ms;

/* ISR, called 1000 times per second */
void
systick_isr_C(void)
{
/* Disable the timer interrupt */
TimerIntDisable(SOC_TMR_2_REGS, TMR_INT_TMR12_NON_CAPT_MODE);

printf("ISR called");

/* Clear the interrupt statusIntChannelSet(SYS_INT_TIMR2_ALL, 0); in AINTC */
IntSystemStatusClear(SYS_INT_TIMR2_ALL);
TimerIntStatusClear(SOC_TMR_2_REGS, TMR_INT_TMR12_NON_CAPT_MODE);

++systick_ms;

/* Enable the timer interrupt */
TimerIntEnable(SOC_TMR_2_REGS, TMR_INT_TMR12_NON_CAPT_MODE);
}


U32
systick_get_ms(void)
{
return systick_ms;
}


void
systick_init(void)
{
/* Setup timer for 64 bit mode */
/* Configuration of Timer */
TimerConfigure(SOC_TMR_2_REGS, TMR_CFG_64BIT_CLK_INT);

/* Set the 64 bit timer period */
TimerPeriodSet(SOC_TMR_2_REGS, TMR_TIMER12, TMR_PERIOD_LSB32);
TimerPeriodSet(SOC_TMR_2_REGS, TMR_TIMER34, TMR_PERIOD_MSB32);

/* Set up the ARM Interrupt Controller for generating timer interrupt */
/* Initialize AINTC and register timer interrupt */
IntAINTCInit();

/* Register the Timer ISR */
IntRegister(SYS_INT_TIMR2_ALL, systick_isr_C);

/* Set the channel number for Timer interrupt, it will map to IRQ */
IntChannelSet(SYS_INT_TIMR2_ALL, 2);

/* Enable IRQ for ARM (in CPSR)*/
IntMasterIRQEnable();

/* Enable AINTC interrupts in GER */
IntGlobalEnable();

/* Enable IRQ in AINTC */
IntIRQEnable();

/* Enable timer interrupts in AINTC */
IntSystemEnable(SYS_INT_TIMR2_ALL);

/* Enable the timer interrupt */
TimerIntEnable(SOC_TMR_2_REGS, TMR_INT_TMR12_NON_CAPT_MODE);

/* Start the timer */
TimerEnable(SOC_TMR_2_REGS, TMR_TIMER12, TMR_ENABLE_CONT);

// Deactivate interrupts on CPU - for test purposes
//IntMasterIRQDisable();
}

所需的所有其他源代码文件(例如用于控制中断 Controller )是 TI 的 StarterWare 代码的未修改版本(它提供驱动程序来控制 SoC 的各种硬件组件,如中断 Controller 、定时器等)。我可以在这里添加它们,但我不确定它们是否有用。我查看了它们,对我来说,这段代码似乎没有发生什么奇怪的事情。

我们使用 EV3 的 Uboot bootloader 运行我们的程序。此引导加载程序已经是默认固件的一部分。它将位于 SD 卡上的程序加载到 0xC1000000(映射到 EV3 的 RAM 的地址)并跳转程序的入口点(汇编代码中的“Entry:”)。到目前为止没有问题,代码编译并按预期执行。

这就是问题所在:
中断也按预期触发。每次执行程序时,在 c_entry() 中循环 455 个循环后,这种情况就会发生。但是一旦触发中断,程序就会停止而没有任何错误或其他东西。中断服务例程(systick.c 中的 systick_isr_C)和中断处理程序(汇编代码中的 IRQHandler 或者如果我们在 test.c 中设置我们自己的处理程序 irqHandler)都不会被调用。我们的猜测是,这个问题是由于中断向量表可能在错误的位置引起的,尽管 0xFFFF0000 是手册中描述的地址(这个地址映射到 ARM CPU 本地 RAM)。

如果我们停用 CPU 上的中断处理(使用 systick.c 中的最后一行代码 --> systick_init())并检查指示中断被手动触发的寄存器,一切正常。在这种情况下,我们直接触发 irqHandler 函数。我们使用以下代码执行此操作:
unsigned int* isr_pointer = *(unsigned int *)*((unsigned int*)0xFFFEF604);
if (isr_pointer != 0) {
irqHandler();
}

我们还尝试将 irqHandler 设置为所有可能中断的默认处理程序(只是为了确保没有触发其他中断)。这并没有导致任何其他结果。

不幸的是,我们目前没有 JTAG 适配器,这将允许我们在触发中断时检查寄存器(例如程序计数器)。我们预计很快就会开始,但现在我们必须在没有人的情况下工作。

有谁知道可能是什么问题,或者为什么我们的中断处理程序根本没有被调用?

我试图尽可能详细地描述这个问题,但如果我能提供任何可能有用的进一步信息,我很乐意这样做。

感谢您提前提供任何提示。

最佳答案

经过更多的研究和实验,我们能够解决这个问题。
如果有人对解决方案感兴趣,这就是导致问题的原因:

问题一:
中断向量未正确初始化。与 TexasInstruments 示例相反,向量不应包含中断处理程序的地址。相反,它应该包含机器代码操作。将程序计数器更改为正确地址的任何操作都有效,例如B ...(分支)或 LDR pc,...(加载程序计数器)。

这是解决此问题的新代码:

在启动.S中:

ExceptionHandler:
B Entry
B ExceptionHandler
B ExceptionHandler
B ExceptionHandler
B ExceptionHandler
B ExceptionHandler
B IRQHandler
B ExceptionHandler

在 test.c 中:
/* Copy the vector table to it's destination at 0xFFFF0000 - this address is specified in the board's manual. */
static void CopyVectorTable(void)
{
unsigned int *dest = (unsigned int *)0xFFFF0000;
/* The address of the assembler exception vector */
unsigned int *addrExceptionHandler = (unsigned int *)ExceptionHandler;
int i;

/* We only set vector 1 to 7 and leave the Reset vector in peace */
/* Important: don't set the address of the vectors - we need to write the operation (i.e. the machine code) to that location */
for (i = 1; i < 8; ++i) {
dest[i] = addrExceptionHandler[i];
}

/* This code is required in order for the branch instructions (B ...) to work */
for (; i < 2048; ++i) {
dest[i] = addrExceptionHandler[i];
}
}

问题2:
用汇编程序编写的 IRQ-Handler 无法确定相应 ISR 的正确地址。但是可以调用我们想要的任何 C 函数。所以我们决定将调用正确 ISR 的任务委托(delegate)给我们自己用 C 编写的 IRQ-Handler(参见 test.c --> irqHandler)。

在启动.S中:
IRQHandler:
STMFD r13!, {r0-r3, r12, r14} @ Save context in IRQ stack
ADD r14, pc, #0 @ Save return address in LR
@ For whatever reason, we are unable to get the correct address of the ISR in this assembler code
@ So we just call our C handler which has a static address in RAM and let it handle the interrupt
@LDR pc, =systick_isr_C @ Works as well, but is not dynamic - we could only handle 1 interrupt
LDR pc, =irqHandler
LDMFD r13!, {r0-r3, r12, r14} @ Restore registers from IRQ stack
SUBS pc, r14, #0x4 @ Return to program before IRQ

这解决了问题。也许这可以帮助将来的某个人,所以我想我会发布我们的解决方案。

关于interrupt - Lego Mindstorm EV3 上的裸机中断处理(TexasInstruments Sitara AM1808 SoC),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33420591/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com