How do I handle an interrupt for an ARM microcontroller using codewarrior?? In the HCs08 processors I do this, for example:
interrupt VectorNumber_Vsci1rx void ISR_name(void)
{
.....Do something......
}
after enable interrupts, of course...
But I don't know how to handle these interrupts in ARM processors using codewarrior or KDS
I was using a Kinetis K20 (K20DX128VLH5) with Codewarrior for MCU and used this approach. In order to declare a certain method as the handler for an event, find the class kinetis_sysinit.c that should be auto-generated with any project. There you'll find a method that looks like the following code I posted. This is the interrupt vector table. If you're using something from the K20 Sub-Family, download the K20 Sub-Family reference manual. Starting on page 62 of the ref manual you can find which vector corresponds to which module.
NOTE: The process may differ depending on what type of Kinetis you're using (K20 vs K40 vs K60 etc). Look to the device's reference manual to find out what interrupt vectors correspond to what.
In the following code you can see I added in a few methods of my own to handle GPIO, UART and timer interrupts.
/* The Interrupt Vector Table */
void (* const InterruptVector[])() __attribute__ ((section(".vectortable"))) =
{
/* Processor exceptions */
(void(*)(void)) &_estack, // Vector 0
__thumb_startup, // Vector 1
NMI_Handler, // Vector 2
HardFault_Handler,
MemManage_Handler,
BusFault_Handler,
UsageFault_Handler,
0,
0,
0,
0,
SVC_Handler,
DebugMonitor_Handler,
0,
PendSV_Handler,
SysTick_Handler,
/* Interrupts */
Default_Handler, // Vector 16
Default_Handler, // Vector 17
Default_Handler, // Vector 18
Default_Handler, // Vector 19
Default_Handler, // Vector 20
Default_Handler,
Default_Handler,
Default_Handler,
Default_Handler,
Default_Handler,
Default_Handler,
Default_Handler,
Default_Handler,
Default_Handler,
Default_Handler, // Vector 30
Default_Handler,
(tIsrFunc)uart_status_handler, // 32 is UART0 status sources
Default_Handler, // 33 is UART0 error sources
Default_Handler,
Default_Handler, // Vector 35
Default_Handler,
Default_Handler,
Default_Handler,
Default_Handler,
Default_Handler, // Vector 40
Default_Handler,
Default_Handler,
Default_Handler,
Default_Handler,
Default_Handler, // Vector 45
Default_Handler,
Default_Handler,
Default_Handler,
Default_Handler,
Default_Handler, // Vector 50
Default_Handler,
Default_Handler,
Default_Handler,
Default_Handler,
(tIsrFunc)timer_handler, // Vector 55, LPTMR
Default_Handler, //(tIsrFunc)portA_button_handler, // Vector 56, port A pin detect
Default_Handler,
(tIsrFunc)portC_button_handler, // Vector 58, port C pin detect
Default_Handler,
Default_Handler,
Default_Handler,
};
In the same class, be sure to also declare your methods externally. Right below the typedef struct for tIsrFunc towards the top, write something like this:
extern void timer_handler(void);
extern void portC_button_handler(void);
extern void uart_status_handler(void);
What version of CodeWarrior are you using? Which toolset are you using? If you're using the GCC toolset, the interrupt names are different and are weakly tied to a default handler. Just create an interrupt with the correct name and it will work. If you aren't using the GCC toolset, you have to edit the arrays in kinetis_sysinit.c.
I don’t have Codewarrior or KDE by hand. But all ARM-Controllers which follow the ARM-CMSIS Standard are going this way.
All the interrupt Handlers of the controller are already defined but with __weak linkage. So, all you have to do is to create a function with the same Name.
For example compare the File devices\MK22F51212\arm\startup_MK22F51212.s from the MCUXpresso SDK for the FRDM-K22 Board:
https://mcuxpresso.nxp.com/en/dashboard
Related
I'm currently working on an ECG project and I'm having some difficulties using the Waveshare High-Precision AD/DA Board (which has an ADS1256 ADC and is meant for use with RPI) with the Terasic DE10-Nano Kit.
I'm using an Altera SPI Master peripheral in Qsys with the following settings:
SCLK rate: 20000
Data Width: 8 bits
Shift Direction: MSB first
Clock Polarity: 0
Clock Phase: 1
No synchronizer stages
For the remaining required signals I'm using Avalon PIO:
DRDY (Input)
RST (output)
PWR (output)
Additionally, there's a Nios2 CPU which makes use of alt_avalon_spi_command to send SPI commands and IOWR_ALTERA_AVALON_PIO_DATA to control the PIO.
The issue I'm experiencing, is that the DRDY signal is never asserted (it's expected to go LOW before being able to read data). I understand this signal should be used with a pull-up resistor; I've tried the following, neither of which worked:
Configure internal pull up on DE10-Nano GPIO pin
Use external pull up resistors (10K/56K)
I understand I should be able to read the Chip ID of the ADS1256, prior to configuring it. I first reset the device, as per:
https://github.com/waveshare/High-Precision-AD-DA-Board/blob/master/Jetson%20nano/ADS1256/C/obj/ADS1256.c#L39
/* RESET */
IOWR_ALTERA_AVALON_PIO_DATA( WAVESHARE_ADS_RST_BASE, HIGH );
usleep(200);
IOWR_ALTERA_AVALON_PIO_DATA( WAVESHARE_ADS_RST_BASE, LOW );
usleep(200);
IOWR_ALTERA_AVALON_PIO_DATA( WAVESHARE_ADS_RST_BASE, HIGH );
Then I wait for DRDY to go LOW and this never seems to happen, regardless of the above-mentioned setup for the pull up resistor:
alt_u8 ADS1256_wait_DRDY( void ) {
for( int i=0; i<50; i++ ) {
alt_u8 drdy = ADS1256_DRDY_get_level();
if ( drdy == LOW ) {
printf( "DRDY asserted\n" );
return 0;
} else {
usleep( DELAY_DRDY );
}
}
printf( "Timeout: DRDY not asserted.\n" );
return -1;
}
I'm probing using a Hobby Components Logic Analyser. The pin connections are elaborated below:
AD/DA RPI PIN DE10-Nano Location Standard HDL Signal Direction Logic Analyzer
==========================================================================================================
3v3 3v3 3v3
GND GND GND
MOSI 19 (GPIO 10) GPIO_0(0) PIN_V12 3.3V LVTTL SPI_MOSI OUT D0
MISO 21 (GPIO 9) GPIO_0(2) PIN_W12 3.3V LVTTL SPI_MISO IN D1
SCK 23 (GPIO 11) GPIO_0(4) PIN_D8 3.3V LVTTL SPI_SCK OUT D2
P3 (CS_PIN) 15 (GPIO 22) GPIO_0(1) PIN_E8 3.3V LVTTL SPI_SS_n(0)* OUT D3
P1 (RST) 12 (GPIO 18) GPIO_0(3) PIN_D11 3.3V LVTTL ADS_RST OUT D4
P0 (DRDY) 11 (GPIO 17) GPIO_0(6) PIN_AE15 3.3V LVTTL WAVESHARE_DRDY IN D5
The logic analyser output is as following:
PulseView
Any help on how to set this up is much appreciated. Apologies if my post is not great, I'm fairly new to posting on these forums.
Some of the pin assignments were not correct. After fixing these and testing with a different microcontroller instead of the AD/DA board, the DRDY is asserted, but the issue persists with the waveshare board. It looks like a board specific issues, so I've contacted the manufacturer for assistance.
I'm trying to use SysTick_Handler in SW4STM32 for Linux, but whenever the SysTick interrupt is triggered, execution jumps to somewhere in system memory. By my understanding, it should jump into the void SysTick_Handler(void) that I declared, or failing that, into Default_Handler declared in startup_stm32.s where the interrupt vector table is defined. I set a break point in my SysTick_Handler, but it is never reached. In the code below, it gets through init_systick() and stays in the endless for loop if I don't include SysTick_CTRL_TICKINT_Msk, as expected, but when I do include it, the debugger tells me it ends up somewhere around address 0x1fffda7c.
main.c:
#include "stm32f0xx.h"
volatile uint32_t ticks = 0;
void SysTick_Handler(void) {
ticks++;
}
void init_systick(void) {
SysTick->LOAD = 43999;
SCB->SHP[1] |= 0x40000000L;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;
}
int main(void)
{
init_systick();
for(;;);
}
I verified from the .map file that the linker is using the declared SysTick_Handler instead of Default_Handler.
I also tried the following variation to use the standard peripheral library for setup, along with other interrupt priority values, with the same results:
#include "stm32f0xx.h"
volatile uint32_t ticks = 0;
void SysTick_Handler(void) {
ticks++;
}
void init_systick(void) {
SysTick_Config(44000);
NVIC_EnableIRQ(SysTick_IRQn);
NVIC_SetPriority(SysTick_IRQn, 0);
}
int main(void)
{
init_systick();
for(;;);
}
This shouldn't be relevant, but since the target doesn't have a timing crystal, I also modified void SetSysClock(void) in system_stm32f0xx.c to use the HSI clock and PLL, which appears to be working correctly:
static void SetSysClock(void)
{
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_HSI;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI) ;
FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
RCC->CR &= ~RCC_CR_PLLON;
while (RCC->CR & RCC_CR_PLLRDY) ;
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_PLLMUL & ~RCC_CFGR_PLLSRC) | RCC_CFGR_PLLMUL11; // PLL takes 8 MHz HSI / 2 as input
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY)) ;
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) ;
}
-- EDIT: More info requested in the comments --
It's an M0 core, so it doesn't have vector table relocation. From the reference manual section 2.5 (page 44):
Unlike Cortex ® M3 and M4, the M0 CPU does not support the vector table relocation.
Address 0x00000000 should be mapped either to FLASH memory at 0x08000000, system memory at 0x1fffd800, or to SRAM at 0x20000000. The memory at address 0x00000000 matches system memory at 0x1fffd800, even though SYSCFG_CFGR1 MEM_MODE is set to 00, which should map FLASH memory there. Main FLASH memory at address 0x08000000 contains the correct vector table, but address 0x00000000 is populated with address 0x1fffd99d for the SysTick vector (and all other non-NULL vectors except the reset vector, which is 0x1fffdc41); the vectors shown by the debugger at address 0x00000000 are consistent with the observed behavior. All of this information was collected while paused at a breakpoint at address 0x08000298 (a correct position in FLASH memory where the correct code has been loaded), before executing the interrupt.
The VTOR is referenced in the arm-v6 reference manual. It is a good idea to check this.
https://static.docs.arm.com/ddi0419/d/DDI0419D_armv6m_arm.pdf
0xE000ED08 VTOR RW 0x00000000a Vector Table Offset Register, VTOR on page B3-231
That reference manual is for the arm-v6M architecture which is the architecture for cortex-m0 processors. This is the bible. Most of the generic cortex-m0 features of the stm line will not be mentioned in stm refrenece manuals, just in the arm arm.
I'll reiterate, check the VTOR.
And make sure you are building for the right line of STM32F030!
The STM32F030x4 and STM32F030x6 micros have a different memory map than the STM32F030x8.
This sounds like there might be a problem with the linker file.
Can you verify that your linker file has something that looks like the following? (if it is a default file it will probably be far more complex).
. = 0;
.text 0 :
{
*(.vector);
crt0*(.text*);
main*(.text*);
*(.text*);
} > flash
Basically what this is saying is that the 'text' (code) of the program starts at address 0x0, and the first thing to put there is the vector table, followed by startup code, main, and then other code.
You will also then want to check that you have some file specifying this vector table's contents that also agrees it should be at address 0x0. This example is from an ATSAMD21E18A.
.section .vector, "a", %progbits
.equ stack_base, 0x20004000
.word stack_base
.word reset_handler
.word nmi_handler
.word hardfault_handler
.word 0
// ...
.word 0
.word systick_handler
// ...
The important thing is that it is marked as the section vector which the linker file will try to put at 0x0. For a processor with VTOR (like the M0+), this table might be specified in C without any special markings, and its location won't matter, but since you don't have VTOR, you need to make sure the linker knows to put this section right at 0x0.
jonnconn is correct. VTOR is the issue. I just wanted to add to his post (but cannot because my reputation isn't high enough) that I ran into this issue recently with a new CubeMx project.
VTOR is set to 0 on reset. VTOR is initialized in low level init before main. SystemInit in system_stm32l4xx.c (or similar for your part) seems to only initialize VTOR if USER_VECT_TAB_ADDRESS is defined. But CubeMx had this commented out. I had to uncomment it (it is in the same file), and leave VECT_TAB_OFFSET as 0. Afterwards, VTOR config was correctly set to FLASH_BASE (assuming VECT_TAB_SRAM was left undefined).
According the datasheet, that region is the system memory (for built-in bootloader). You may try two things:
Double check BOOTx pins to make sure the MCU loads FLASH instead of the system memory.
Make sure you assigned SCB->VTOR to the correct address of your own interrupt vector table.
I've been trying to look up information on this to no avail. Can someone explain what the curly braces do here? I'm trying to find or create the ISR function for the transmit/receive IRQ, however I am stuck here.
/** Interrupt vectors for the ENET peripheral type */
#define ENET_Transmit_IRQS { ENET_Transmit_IRQn }
#define ENET_Receive_IRQS { ENET_Receive_IRQn }
In this case ENET_Transmit_IRQn is the 83rd vector in the IRQ vector and the Rx is consecutively, 84th.
What are the curly braces for? My only logical argument is that these vectors are in a typedef enum IRQ (structure? It doesn't say struct though), and essentially it is the same as
#define ENET_Transmit_IRQS 83
P.S. This is for a Kinetis K66
EDIT: I just found 1 line where it is being used.
/*! #brief Pointers to enet transmit IRQ number for each instance. */
static const IRQn_Type s_enetTxIrqId[] = ENET_Transmit_IRQS;
Where IRQn_Type is
typedef enum IRQn {
...
ENET_1588_Timer_IRQn = 82, /**< Ethernet MAC IEEE 1588 Timer Interrupt */
ENET_Transmit_IRQn = 83, /**< Ethernet MAC Transmit Interrupt */
ENET_Receive_IRQn = 84, /**< Ethernet MAC Receive Interrupt */
ENET_Error_IRQn = 85, /**< Ethernet MAC Error and miscelaneous Interrupt */
...
} IRQn_Type;
I think I understand this now with the help of Eugene (thanks!!). It has curly braces because it is being used as an initializer for an array.
I'm developing a toy unix clone and I'm trying to wire up my interrupts properly. I've run into a problem where my Keyboard IRQ (IRQ 1) fires just once even after I properly acknowledge it and so on. I've enabled the PIT interrupt as well, to double check if my ACKs are going to the PIC okay, and that seems to work fine. (fires multiple times)
One catch with interrup.s is that I'm passing the struct register_t on the stack (by value) and the compiler was trashing that after it returns from the C interrupt handler. Surprisingly, the only value that was getting trashed was the value on the top of the stack (The data segment register in this case) and I've verified that the rest of the values in the stack look okay by printing the stack before and after the call to the interrupt handler occurs. I've added a temporary work-around to fix this issue, but I will clean this up later.
I've also verified that software interrupts work fine by triggering int $3 multiple times.
Any advice is appreciated! Here's the code:
interrupt.s
.macro ISR_NOERRCODE int_no # A macro for ISRs that don't push an error code
.global isr\int_no
isr\int_no:
cli
push $0
push $\int_no
jmp isr_irq_common_stub
.endm
.macro ISR_ERRORCODE int_no # A macro for ISRs that do push an error code
.global isr\int_no
isr\int_no:
cli
push $\int_no
jmp isr_irq_common_stub
.endm
.macro IRQ irq_no, isr_map # A macro for IRQs from the PIC
.global irq\irq_no
irq\irq_no:
xchgw %bx, %bx
cli
push $0 # Error code
push $\isr_map # Interrupt number
jmp isr_irq_common_stub
.endm
ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
ISR_NOERRCODE 4
ISR_NOERRCODE 5
ISR_NOERRCODE 6
ISR_NOERRCODE 7
ISR_ERRORCODE 8 # ISR 8 pushes error code onto stack
ISR_NOERRCODE 9
ISR_ERRORCODE 10 # ISR 10 - 14 push error codes onto stack
ISR_ERRORCODE 11
ISR_ERRORCODE 12
ISR_ERRORCODE 13
ISR_ERRORCODE 14
ISR_NOERRCODE 15
ISR_NOERRCODE 16
ISR_ERRORCODE 17
ISR_NOERRCODE 18
ISR_NOERRCODE 19
ISR_NOERRCODE 20
ISR_NOERRCODE 21
ISR_NOERRCODE 22
ISR_NOERRCODE 23
ISR_NOERRCODE 24
ISR_NOERRCODE 25
ISR_NOERRCODE 26
ISR_NOERRCODE 27
ISR_NOERRCODE 28
ISR_NOERRCODE 29
ISR_ERRORCODE 30
ISR_NOERRCODE 31
IRQ 0, 32
IRQ 1, 33
IRQ 2, 34
IRQ 3, 35
IRQ 4, 36
IRQ 5, 37
IRQ 6, 38
IRQ 7, 39
IRQ 8, 40
IRQ 9, 41
IRQ 10, 42
IRQ 11, 43
IRQ 12, 44
IRQ 13, 45
IRQ 14, 46
IRQ 15, 47
# This is in isr.c
.extern isr_irq_handler
# This is our common isr stub. It saves the processor state, sets up for kernel
# mode segments, calls the C-level fault handler, and finally restores the stack
# frame
isr_irq_common_stub:
pusha # Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
mov %ds, %ax # Lower 16-bits of eax = ds.
push %eax # save the data segment descriptor
mov $0x10, %ax # load the kernel data segment descriptor
mov %ax, %ds # Right now, we dont really have to do this
mov %ax, %es # but after we enter the user mode, the segment
mov %ax, %fs # registers will be different (0x18? and 0x20?)
mov %ax, %gs
call isr_irq_handler
# This does not work because the structure value we passed earlier
# is being messed up by the compiler. It does not preserve the previous eax
# we pushed on to the stack.
pop %eax
mov $0x10, %ax # reload the original data segment descriptor
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
popa # Pops edi,esi,ebp...
add $8, %esp # Cleans up the pushed error code and pushed ISR number
sti
iret # pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
isr.h
#ifndef __isr_h
#define __isr_h
#include <stdint.h>
struct Registers {
uint32_t ds;
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
uint32_t int_no, err_code;
uint32_t eip, cs, eflags, useresp, ss;
} __attribute__((packed));
typedef struct Registers register_t;
typedef void (*isr_t)(registers_t);
void register_interrupt_handler(uint8_t n, isr_t handler);
void isr_irq_handler(register_t regs);
#endif
isr.c
#include <kernel/isr.h>
#include <kernel/pic.h>
#include <stdio.h>
#include <log.h>
#include <kernel/tty.h>
isr_t interrupt_handlers[256];
void register_interrupt_handler(uint8_t n, isr_t handler)
{
interrupt_handlers[n] = handler;
}
void isr_irq_handler(register_t regs)
{
printf("Received ISR/IRQ: %d\n", regs.int_no);
if (interrupt_handlers[regs.int_no]) {
interrupt_handlers[regs.int_no]();
}
if (regs.int_no >= 32 && regs.int_no <= 47) {
pic_send_eoi(regs.int_no - 32);
}
return;
}
pic.c
#include <kernel/pic.h>
#include <asm.h>
#define PIC1 0x20 /* IO base address for master PIC */
#define PIC2 0xA0 /* IO base address for slave PIC */
#define PIC1_COMMAND PIC1
#define PIC1_DATA (PIC1+1)
#define PIC2_COMMAND PIC2
#define PIC2_DATA (PIC2+1)
#define PIC_EOI 0x20 /* End-of-interrupt command code */
#define ICW1_ICW4 0x01 /* ICW4 (not) needed */
#define ICW1_SINGLE 0x02 /* Single (cascade) mode */
#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */
#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */
#define ICW1_INIT 0x10 /* Initialization - required! */
#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */
#define ICW4_AUTO 0x02 /* Auto (normal) EOI */
#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */
#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */
#define ICW4_SFNM 0x10 /* Special fully nested (not) */
#define PIC_READ_IRR 0x0a /* OCW3 irq ready next CMD read */
#define PIC_READ_ISR 0x0b /* OCW3 irq service next CMD read */
#define PIC1_OFFSET 0x20
#define PIC2_OFFSET 0x28
static void pic_mask(int pic_num, uint16_t mask);
static uint16_t pic_get_irq_reg(int ocw3);
static uint16_t pic_get_irr(void);
static uint16_t pic_get_isr(void);
void setup_remap_pics()
{
uint8_t a1, a2;
// Save existing masks
a1 = inb(PIC1_DATA);
a2 = inb(PIC2_DATA);
outb(PIC1_COMMAND, ICW1_INIT);
io_wait();
outb(PIC2_COMMAND, ICW1_INIT);
io_wait();
outb(PIC1_DATA, PIC1_OFFSET); // We're remapping the PICs interrupt codes from (0x07-0x7F) to (offset, offset + 8)
io_wait();
outb(PIC2_DATA, PIC2_OFFSET);
io_wait();
outb(PIC1_DATA, 4); // Tell the master PIC that there is a slave PIC at IRQ2 (00000100)
io_wait();
outb(PIC2_DATA, 2); // Tell the slave pic it's cascade identity (00000010)
io_wait();
outb(PIC1_DATA, ICW4_8086);
io_wait();
outb(PIC2_DATA, ICW4_8086);
io_wait();
// Restore saved masks
outb(PIC1_DATA, a1);
outb(PIC2_DATA, a2);
enable_interrupts();
// Mask everything except the keyboard, timer
pic_mask(1, 0xFD);
pic_mask(2, 0xFF);
}
static void pic_mask(int pic_num, uint16_t mask) {
uint16_t port = (pic_num == 1) ? PIC1_DATA : PIC2_DATA;
outb(port, mask);
}
// MARK :- Helpers
void pic_send_eoi(uint8_t irq)
{
if (irq >= 8) {
outb(PIC2_COMMAND, PIC_EOI);
}
printf("Sending EOI for IRQ: %d, EOI: %x, to CMD: %x\n", irq, PIC_EOI, PIC1_COMMAND);
// Always signal PIC1 that an interrupt has been handled
// because it's the PIC that forwards PIC2's irqs as well.
outb(PIC1_COMMAND, PIC_EOI);
}
static uint16_t pic_get_irq_reg(int ocw3)
{
/* OCW3 to PIC CMD to get the register values. PIC2 is chained, and
* represents IRQs 8-15. PIC1 is IRQs 0-7, with 2 being the chain */
outb(PIC1_COMMAND, ocw3);
outb(PIC2_COMMAND, ocw3);
return (inb(PIC2_COMMAND) << 8) | inb(PIC1_COMMAND);
}
/* Returns the combined value of the cascaded PICs irq request register */
static uint16_t pic_get_irr(void)
{
return pic_get_irq_reg(PIC_READ_IRR);
}
/* Returns the combined value of the cascaded PICs in-service register */
static uint16_t pic_get_isr(void)
{
return pic_get_irq_reg(PIC_READ_ISR);
}
I don't see any keyboard ISR code, and that could well be where your problem is. I'm guessing your are using the PC/AT style interface to interact with the KB controller (ports 60, 61, 64 if I remember correctly). There are one or more OUTs to one (or more) of these ports before they'll generate another interrupt - if memory serves.
In general any hardware device will require attention after it generates an interrupt. The PIC generating a time tick interrupt is an exception.
In the way of advice, I have a few suggestions:
Inspect the compiler code that you think is incorrectly trashing the AX register and/or the register_t structure (I'm not entirely sure from the OP which you are saying is being trashed). Identify exactly how you think the compiler is incorrect. I suspect the compiler is not incorrect, but rather that it is just tricky to call a C function from an ISR. In doing that in the past I had to go through a number of iterations to get it right.
Come up with a debugging scheme where you can view recent events.
a. One method is to use the fixed locations in the VGA screen memory (I'm assuming you are working in text mode at this point) where you can write single characters that indicate what your program is doing. A few characters at the end of the top line in particular, let's say 4. The 4 characters represent the 4 most recent events. When an event (an interrupt or a call from a user mode program to the kernel as examples) occurs you move the 3 most recent chars to the left (or right) and then put the newest char in the "most recent" slot. When you're program is working normally you'll see those locations on the screen as kind of a blur as they rapidly get updated due to various events. However if your program hangs the characters will stop changing and you'll see the 4 most recent events, even if you can't break in with a debugger.
b. Create a circular in memory trace log. Each entry in the log represents an event but can include details about the event like register values, or program state. If you break in with a debugger you can view the log as either bytes, words, dwords, and decode the events in your head. Or if you have a debugger that allows custom extensions, then write an extensions that supports displaying and querying the log for events of interest.
FWIW, I implemented a V86 monitor (old Intel terminology for virtualized real mode hypervisor) and so these suggestions come from experience. These suggestions apply to dev testing your kernel on both bare metal and to a somewhat lesser extent using a VM as your dev test platform.
With a VM and the right hypervisor, you will have access to a debugger specifically designed to debug a VM. Virtual Box, for example, has such a debugger. Windbg can be used on Hyper-V, although it was very slow when I tried it last. However someone wrote an extension that made kernel debugging of Hyper-V VMs much quicker.
I have an ARM stm32f107 chip. I'm porting a project from IAR to GCC
IAR provides the following functions to enable and disable interrupts:
#define __disable_interrupt() ...
#define __enable_interrupt() ...
How do I enable / disable interrupt for my chip using GCC?
When developing for the STM32, RM0008 is your best friend. From Section 10.2.4 on page 199:
To generate the interrupt, the interrupt line should be configured and
enabled. This is done by programming the two trigger registers with
the desired edge detection and by enabling the interrupt request by
writing a ‘1’ to the corresponding bit in the interrupt mask register.
So you need to set the appropriate mask bits in the appropriate registers. For external interrupts, that's the EXTI_IMR and EXTI_EMR registers. There are many others.
I can't answer for ARM but the same function in Coldfire boils down to setting/clearing the Interrupt Priority Level masking register in the CPU. Setting it to the highest number disables/ignores all but non-maskable, setting it to 0 enables all (YMMV).
Worth noting that it's handy to read-back the value when "disabling" and restore when "enabling" to ensure that stacked interrupts don't break each other:
ipl = DisableInts(); // Remember what the IPL was
<"Risky" code happens here>
EnableInts(ipl); // Restore value
This is useful when twiddling interrupt masks, which may cause spurious interrupts, or doing stuff that shouldn't be interrupted.
Functions come out as:
uint8 DisableInts(void)
{
return(asm_set_ipl(7));
}
uint8 EnableInts(uint8 ipl)
{
return(asm_set_ipl(ipl));
}
Both of which map to this asm:
asm_set_ipl:
_asm_set_ipl:
/* Modified for CW7.2! */
link A6,#-8
movem.l D6-D7,(SP)
move.l D0,D6 /* save argument */
move.w SR,D7 /* current sr */
move.l D7,D0 /* prepare return value */
andi.l #0x0700,D0 /* mask out IPL */
lsr.l #8,D0 /* IPL */
andi.l #0x07,D6 /* least significant three bits */
lsl.l #8,D6 /* move over to make mask */
andi.l #0x0000F8FF,D7 /* zero out current IPL */
or.l D6,D7 /* place new IPL in sr */
move.w D7,SR
movem.l (SP),D6-D7
//lea 8(SP),SP
unlk A6
rts
The ARM Documentation says that _enable_irq(); compiles to “CPSIE I” that means Clear All Masks. On the other hand _disable_irq(); compiles to Set Mask.