Interrupt handler on C doesn't work after one interrupt [duplicate] - c

This question already has answers here:
Creating a C function without compiler generated prologue/epilogue & RET instruction?
(3 answers)
Closed 3 years ago.
I'm trying to implement keyboard interrupt handler using C and QEMU. But when I execute the program my handler print only one character. After that the handler doesn't work at all.
My IDT setup:
struct IDT_entry {
unsigned short int offset_lowerbits;
unsigned short int selector;
unsigned char zero;
unsigned char type_attr;
unsigned short int offset_higherbits;
};
void setup_idt() {
struct IDT_entry IDT[256];
unsigned long keyboard_address;
unsigned long idt_address;
unsigned long idt_ptr[2];
keyboard_address = (unsigned long) keyboard_handler;
IDT[0x21].offset_lowerbits = keyboard_address & 0xffff;
IDT[0x21].selector = 0x8;
IDT[0x21].zero = 0;
IDT[0x21].type_attr = 0x8e;
IDT[0x21].offset_higherbits = (keyboard_address & 0xffff0000) >> 16;
/*
PIC1 PIC2
Commands 0x20 0xA0
Data 0x21 0xA1
*/
// ICW1 - init
outb(0x20, 0x11);
outb(0xA0, 0x11);
// ICW2 - reset offset address if IDT
// first 32 interrpts are reserved
outb(0x21, 0x20);
outb(0xA1, 0x28);
// ICW3 - setup cascading
outb(0x21, 0b0);
outb(0xA1, 0b0);
// ICW4 - env info
outb(0x21, 0b00000011);
outb(0xA1, 0b00000011);
// init finished
// disable IRQs except IRQ1
outb(0x21, 0xFD);
outb(0xA1, 0xff);
idt_address = (unsigned long)IDT;
idt_ptr[0] = (sizeof (struct IDT_entry) * 256) + ((idt_address & 0xffff) << 16);
idt_ptr[1] = idt_address >> 16;
__asm__ __volatile__("lidt %0" :: "m" (*idt_ptr));
__asm__ __volatile__("sti");
}
My keyboard handler:
// Variables for printing ==
unsigned int location = 0;
char* vga = (char*)0xb8000;
char letter;
// =========================
void keyboard_handler() {
if (inb(0x64) & 0x01 && (letter = inb(0x60)) > 0) {
vga[location] = keyboard_map[letter];
vga[location+1] = 0x4;
location += 2;
}
outb(0x20, 0x20);
// __asm__ __volatile__("iret");
}
Main function (it is executed from my asm bootloader):
void kmain() {
setup_idt();
for (;;) {}
}
I think the problem is in "iret" instruction. Without it my kernel prints at least something (only one charachter, like I said before). But when I execute asm volatile("iret"); QEMU prints some garbage and then clear it after every keystroke ("SeaBios ..."). What do I have to do?
Thank you!

If you compile without optimization, asm("iret") will probably run while the stack pointer is still pointing at a saved EBP value, because -fno-omit-frame-pointer is the default and the cleanup epilogue happens after the last C statement of the function.
Or it could be pointing at other saved registers. Anyway, tricking the compiler and jumping out of an inline asm statement is never going to be safe (unless you use asm goto to maybe jump to a C label inside the function, but that doesn't solve your problem).
Also, the C calling convention allows functions to clobber EAX, ECX, EDX, and the FPU state. Even if you did manage to hack an iret into your function, it would corrupt the state of the code that was interrupted. GCC will use SSE/x87 to implement _Atomic int64_t load/store in 32-bit mode, and for copying large objects, unless you compile with -mgeneral-regs-only
Also see #MichaelPetch's answer on the linked duplicate: Creating a C function without compiler generated prologue/epilogue & RET instruction? for more interesting points, and some non-GCC info.
There are 2 solutions here:
write a pure-asm wrapper that saves the call-clobbered regs, calls your C function, then returns with iret
declare your function with __attribute__((interrupt)) to tell GCC it's an interrupt handler. The gcc manual's x86 function attributes has an example.
x86 support for that attribute is somewhat recent compared to traditionally-embedded ISAs like ARM, but modern GCC does know how emit functions that preserve all regs and end with iret. But you still need -mgeneral-regs-only.
See also https://wiki.osdev.org/Interrupt_Service_Routines#GCC_.2F_G.2B.2B which tells you the same thing as this answer.
(It also suggests an evil hack with pushad / popad; leave; iret which only works with optimzation disabled. I would not recommend that if you can possibly use a newer GCC that supports the interrupt attribute.)
The earlier parts of the wiki page cover the general problems with trying to use your own iret, so you can see what the total asm (compiler-generated + yours) would look like for your attempt.

Related

Optimising C code for small size - sharing static variables?

I have two functions, both are similar to this:
void Bit_Delay()
{
//this is a tuned tight loop for 8 MHz to generate timings for 9600 baud
volatile char z = 12;
while(z)
{
z++;
z++;
z++;
z++;
z -= 5;
}
}
(The second function is analogous instead it uses 18 instead of 12 for the counter).
The code works flawlessly as it is (with z appearing locally to each function internally), but I'm trying to cram a little more functionality into my executable before I hit the (horribly) limited FLASH memory available.
My thought was to promote the z variable to be a global one (a volatile static). Because these two functions are effectively atomic operations (it's a single-threaded CPU and there are no interrupts at play to interfere), I figured that these two functions could share the single variable, thus saving a tiny bit of stack manipulation.
This didn't work. It is clear that the compiler is optimising-out much of the code related to z completely! The code then fails to function properly (running far too fast), and the size of the compiled binary drops to about 50% or so.
I realised that I needed the z variable to be marked volatile to prevent the compiler from removing code it knows is counting a fixed (and thus reducible to a constant) number each time.
Question:
Can I optimise this any further, and trick the compiler into keeping both functions intact? I'm compiling with "-Os" (optimise for small binary).
Here's the entire program verbatim for those playing along at home...
#include <avr/io.h>
#define RX_PIN (1 << PORTB0) //physical pin 3
#define TX_PIN (1 << PORTB1) //physical pin 1
void Bit_Delay()
{
//this is a tuned tight loop for 8 MHz to generate timings for 9600 baud
volatile char z = 12;
while(z)
{
z++;
z++;
z++;
z++;
z -= 5;
}
}
void Serial_TX_Char(char c)
{
char i;
//start bit
PORTB &= ~TX_PIN;
Bit_Delay();
for(i = 0 ; i < 8 ; i++)
{
//output the data bits, LSB first
if(c & 0x01)
PORTB |= TX_PIN;
else
PORTB &= ~TX_PIN;
c >>= 1;
Bit_Delay();
}
//stop bit
PORTB |= TX_PIN;
Bit_Delay();
}
char Serial_RX_Char()
{
char retval = 0;
volatile char z = 18; //1.5 bits delay
//wait for idle high
while((PINB & RX_PIN) == 0)
{}
//wait for start bit falling-edge
while((PINB & RX_PIN) != 0)
{}
//1.5 bits delay
while(z)
{
z++;
z++;
z++;
z++;
z -= 5;
}
for(z = 0 ; z < 8 ; z++)
{
retval >>= 1; //make space for the new bit
retval |= (PINB & RX_PIN) << (8 - RX_PIN); //get the bit and store it
Bit_Delay();
}
return retval;
}
int main(void)
{
CCP = 0xd8; //protection signature for clock registers (see datasheet)
CLKPSR = 0x00; //set the clock prescaler to "div by 1"
DDRB |= TX_PIN;
PORTB |= TX_PIN; //idle high
while (1)
Serial_TX_Char(Serial_RX_Char() ^ 0x20);
}
The target CPU is an Atmel ATTiny5 microcontroller, the code above uses up 94.1% of the FLASH memory! If you connect to the chip using a serial port at 9600 Baud, 8N1, you can type characters in and it returns them with bit 0x20 flipped (uppercase to lowercase and vice-versa).
This is not a serious project of course, I'm just experimenting to see how much functionality I could cram into this chip. I'm not going to bother with rewriting this in assembly, I seriously doubt I could do a better job than GCC's optimiser!
EDIT
#Frank asked about the IDE / compiler I'm using...
Microchip Studio (7.0.2542)
The "All Options" string that is passed to the compiler avr-gcc...
-x c -funsigned-char -funsigned-bitfields -DDEBUG -I"C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.8.332\include" -Os -ffunction-sections -fdata-sections -fpack-struct -fshort-enums -g2 -Wall -mmcu=attiny5 -B "C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.8.332\gcc\dev\attiny5" -c -std=gnu99 -MD -MP -MF "$(#:%.o=%.d)" -MT"$(#:%.o=%.d)" -MT"$(#:%.o=%.o)"
I question the following assumption:
This didn't work. It is clear that the compiler is optimising-out much of the code related to z completely! The code then fails to function properly (running far too fast), and the size of the compiled binary drops to about 50% or so.
Looking at https://gcc.godbolt.org/z/sKdz3h8oP, it seems like the loops are actually being performed, however, for whatever reason each z++, when using a global volatile z goes from:
subi r28,lo8(-(1))
sbci r29,hi8(-(1))
ld r20,Y
subi r28,lo8((1))
sbci r29,hi8((1))
subi r20,lo8(-(1))
subi r28,lo8(-(1))
sbci r29,hi8(-(1))
st Y,r20
subi r28,lo8((1))
sbci r29,hi8((1))
to:
lds r20,z
subi r20,lo8(-(1))
sts z,r20
You will need to recalibrate your 12, 18, and 5 constants to get your baud rate correct (since fewer instructions are executed in each loop), but the logic is there in the compiled version.
To be clear: This looks really weird to me, the local volatile version is clearly not being compiled correctly. I did find an old gcc bug along these lines: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=33970, but it seems to not cover the local variable case.
Can I optimise this any further,
Of course; code like this is extremely expensive — and fagile:
volatile char z = 12;
while(z)
{
z++;
z++;
z++;
z++;
z -= 5;
}
It's expensive because your are asking for code bloat just to waste some cycles. And it's fragile because minimal changes to the code might change the timings. Apart from that it triggers stack frames because local volatile vars will live in the stack frame.
To make things worse, you are using volatile char z as a loop variable!
Why to use _delay_ms and friends
AVR-LibC provides delay routines like _delay_us and delay_ms in <util/delay.h>. Advantage is that:
A specified amount of time is wasted, which is passed as a parameter. (The routines might waste more real time than expected if interrupts are on.)
Code size is minimal due to inline assembly, compiler built-ins like __builtin_avr_delay_cycles and code folding.
No more need for magic numbers like 12 or 18 in your code.
The delay time must evaluate to a compile-time constant, and optimization must be turned on1. Canonical use case is to compute delay time using F_CPU, baud rate etc. Suppose we want to delay x * 1/BAUD seconds, which is x * 1000000 / BAUD µs.
So let's change Bit_Delay to the following code, where we add -D F_CPU=8000000 to the command line options so that the delay routines have it available:
#define BAUD 9600
__attribute__((always_inline))
static inline void Bit_Delay (double x)
{
_delay_us (x * 1000000 / BAUD);
}
Then use it like Bit_Delay (1). Changing the 4 usages to that, the code size drops from 480 Bytes to 360 Bytes.
Also adjusting the wait for 1.5 bits to Bit_Delay (1.5) and fixing the loop variable to not be volatile, the code size drops to 180 Bytes.
Functions Serial_RX_Char and Serial_TX_Char are just called once statically, so the compiler can inline them provided we make them static. This reduces the code size further to 170 Bytes, 44 bytes of which are start-up code and vector table. Moreover, we do no more need stack frames (which were triggered by local volatile vars), and the function calls are inlined, which saves RAM. Not unimportant on a device like ATtiny5 with just 32 Bytes of RAM.
FYI, the (inlined) delay code is compiled as:
ldi r20,lo8(208)
ldi r21,hi8(208)
1: subi r20,1
sbci r21,0
brne 1b
nop
Magic 208 is basically F_CPU / BAUD / 4 folded by the compiler, where the division by 4 is because one turn of the delay loop takes 4 ticks.
Why not to use _delay_ms and friends
Busy-waiting is a waste of time and energy. For very short timing issues it might be in order, longer busy-wait may destroy timings of other parts of the code, because it blocks their execution. If possible, use timers + interrupts for that purpose; it saved energy (when sleep can be used when idling) and does not delay execution of unrelated code.
1So that code folding works as expected. Otherwise, the delay code will complain:
util/delay.h:112:3: warning: Compiler optimizations disabled; functions from <util/delay.h> won't work as designed

C program stops execution when assigning variable

I'm not sure if this is happening when assigning a variable specifically but when debugging the assembly code, the compiler executes RJMP $+0000 where it hangs the program.
EDIT: I added included libraries if that's relevant
#define __DELAY_BACKWARD_COMPATIBLE__
#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/delay.h>
#include <stdint.h>
void ReadTemp(uint8_t address){
ADCSRA = ADCSRA | 0b10000111; //enable ADC, CLK/128 conversion speed
ADMUX = ADMUX | 0b01000000; //Use internal 2.56V Vref and PA0 as input, right-hand justified
ADCSRA |= (1 << ADSC); //start conversion
while(!(ADCSRA & (1 << ADIF))) {} // wait until process is finished;
uint8_t low_value = ADC & 0x00FF;
// or low_value = ADCL;
uint8_t high_value = ADC & 0xFF00; //problem here
...
}
I don't know what any of this is doing, but I do see an error in the bitwise math.
uint8_t low_value = ADC & 0x00FF;
uint8_t high_value = ADC & 0xFF00; //problem here
low_value and high_value are both 8 bits (uint8_t). I am going to go out on a limb here and say ADC is 16 bit. For high_value, you are anding ADC with 0xFF00 then truncating the value to 8 bit. high_value will always be zero.
What should be done is:
uint8_t high_value = (ADC & 0xFF00) >> 8;
This will grab the left byte of ADC and shift it right by 8 bits then assign it to the high_value byte storage giving you the correct value.
How you are doing low_value is correct. As a matter of fact, you could simply do:
uint8_t low_value = ADC;
There is something somewhat suboptimal here:
uint8_t low_value = ADC & 0x00FF;
You are reading ADC, which is a 16-bit register, implemented as a pair
of 8-bit registers. As shown in the disassembly, this requires two in
instructions, one per byte. And then you are just throwing away one of
those bytes. You may think that the compiler is smart enough to avoid
reading the byte it is going to discard right away. Unfortunately, it
cannot do that, as I/O registers are declared as volatile. The
compiler is forced to access the register as many times as the source
code does.
If you just want the low byte, you should read only that byte:
uint8_t low_value = ADCL;
You then wrote:
uint8_t high_value = ADC & 0xFF00;
As explained in the previous answer, high_value will be zero. Yet the
compiler will have to read the two bytes again, because the I/O register
is volatile. If you want to read the high byte, read ADCH.
But why would you want to read those two bytes one by one? Is it to put
them back together into a 16-bit variable? If this is the case, then
there is no need to read them separately. Instead, just read the 16-bit
register in the most straight-forward fashion:
uint16_t value = ADC;
A long time ago, gcc didn't know how to handle 16-bit registers, and
people had to resort to reading the bytes one by one, and then gluing
them together. You may still find very old example code on the Internet
that does that. Today, there is absolutely no reason to continue
programming this way.
Then you wrote:
//problem here
Nope, the problem is not here. That is not what generated the rjmp
instruction. The problem is probably right after that line, in the code
you have chosen not to post. You have some bug that manifests itself
only when optimizations are turned on. This is typical of code that
produces undefined behavior: works as expected with optimizations off,
then does weird “unexplainable” things when you enable optimizations.

Keyboard interrupt handler not working in system iso

I'm trying to write an operating system using OSDev and others. Now, I'm stuck making a keyboard interrupt handler. When I compile my OS and run the kernel using qemu-system-i386 -kernel kernel/myos.kernel everything works perfectly. When I put everything into an ISO image and try to run it using qemu-system-i386 -cdrom myos.iso, it restarts when I press a key. I think it's caused by some problems in my interrupt handler or a bad IDT entry.
My keyboard handler (AT&T syntax):
.globl keyboard_handler
.align 4
keyboard_handler:
pushal
cld
call keyboard_handler_main
popal
iret
My main handler in C:
void keyboard_handler_main(void) {
unsigned char status;
char keycode;
/* write EOI */
write_port(0x20, 0x20);
status = read_port(KEYBOARD_STATUS_PORT);
/* Lowest bit of status will be set if buffer is not empty */
if (status & 0x01) {
keycode = read_port(KEYBOARD_DATA_PORT);
if(keycode < 0)
return;
if(keycode == ENTER_KEY_CODE) {
printf("\n");
return;
}
printf("%c", keyboard_map[(unsigned char) keycode]);
}
}
C function I use to load the:
void idt_init(void)
{
//unsigned long keyboard_address;
unsigned long idt_address;
unsigned long idt_ptr[2];
auto keyboard_address = (*keyboard_handler);
IDT[0x21].offset_lowerbits = keyboard_address & 0xffff;
IDT[0x21].selector = KERNEL_CODE_SEGMENT_OFFSET;
IDT[0x21].zero = 0;
IDT[0x21].type_attr = INTERRUPT_GATE;
IDT[0x21].offset_higherbits = (keyboard_address & 0xffff0000) >> 16;
/* Ports
* PIC1 PIC2
*Command 0x20 0xA0
*Data 0x21 0xA1
*/
write_port(0x20 , 0x11);
write_port(0xA0 , 0x11);
write_port(0x21 , 0x20);
write_port(0xA1 , 0x28);
write_port(0x21 , 0x00);
write_port(0xA1 , 0x00);
write_port(0x21 , 0x01);
write_port(0xA1 , 0x01);
write_port(0x21 , 0xff);
write_port(0xA1 , 0xff);
idt_address = (unsigned long)IDT ;
idt_ptr[0] = (sizeof (struct IDT_entry) * IDT_SIZE) + ((idt_address & 0xffff) << 16);
idt_ptr[1] = idt_address >> 16 ;
load_idt(idt_ptr);
printf("%s\n", "loadd");
}
Files are organized the same way as OSDev's Meaty Skeleton. I do have a different bootloader.
Based on experience I believed that this issue was related to a GDT not being set up. Often when someone says interrupts work with QEMU's -kernel option but not a real version of GRUB it is often related to the kernel developer not creating and loading their own GDT. The Mulitboot Specification says:
‘GDTR’
Even though the segment registers are set up as described above, the ‘GDTR’ may be invalid, so the OS image must not load any segment registers (even just reloading the same values!) until it sets up its own ‘GDT’.
When using QEMU with the -kernel option the GDTR is usually valid but it isn't guaranteed to be that way. When using a real version of GRUB (installed to a hard drive, virtual image, ISO etc) to boot you may discover that the GDTR isn't in fact valid. The first time you attempt to reload any segment register with a selector (even if it is the same value) it may fault. When using interrupts the code segment (CS) will be modified which may cause a triple fault and reboot.
As well the Multiboot Specification doesn't say which selectors point to code or data descriptors. Since the layout of the GDT entries is not known or guaranteed by the Multiboot specification it poses a problem for filling in the IDT entries. Each IDT entry needs to specify a specific selector that points to a code segment.
The Meaty Skeleton tutorial on OSDev doesn't set up interrupts, nor do they modify any of the segment registers so that code likely works with QEMU's -kernel option and a real version of GRUB. Adding IDT and interrupt code on top of the base tutorial probably lead to the failure you are seeing when booting with GRUB. That tutorial should probably make it clearer that you should set up your own GDT and not rely on the one set up by the Multiboot loader.

Measuring clock cycle count on cortex m7

I have been measuring clock cycle count on the cortex m4 and would now like to do it on the cortex m7. The board I use is STM32F746ZG.
For the m4 everything worked with:
volatile unsigned int *DWT_CYCCNT;
volatile unsigned int *DWT_CONTROL;
volatile unsigned int *SCB_DEMCR;
void reset_cnt(){
DWT_CYCCNT = (volatile unsigned int *)0xE0001004; //address of the register
DWT_CONTROL = (volatile unsigned int *)0xE0001000; //address of the register
SCB_DEMCR = (volatile unsigned int *)0xE000EDFC; //address of the register
*SCB_DEMCR = *SCB_DEMCR | 0x01000000;
*DWT_CYCCNT = 0; // reset the counter
*DWT_CONTROL = 0;
}
void start_cnt(){
*DWT_CONTROL = *DWT_CONTROL | 0x00000001 ; // enable the counter
}
void stop_cnt(){
*DWT_CONTROL = *DWT_CONTROL & 0xFFFFFFFE ; // disable the counter
}
unsigned int getCycles(){
return *DWT_CYCCNT;
}
The problem is that the DWT_CTRL register isn't changed when I run on the m7 and remains 0x40000000 instead of changing to 0x40000001 so the cycle count is always zero. From what I have read in other posts it seems like you need to set the FP_LAR register to 0xC5ACCE55 to be able to change DWT_CTRL.
I added these defines (have tried both FP_LAR_PTR addresses below):
#define FP_LAR_PTR ((volatile unsigned int *) 0xe0000fb0) //according to reference
//#define FP_LAR_PTR ((volatile unsigned int *) 0xe0002fb0) //according to guy on the internet
// Lock Status Register lock status bit
#define DWT_LSR_SLK_Pos 1
#define DWT_LSR_SLK_Msk (1UL << DWT_LSR_SLK_Pos)
// Lock Status Register lock availability bit
#define DWT_LSR_SLI_Pos 0
#define DWT_LSR_SLI_Msk (1UL << DWT_LSR_SLI_Pos)
// Lock Access key, common for all
#define DWT_LAR_KEY 0xC5ACCE55
and this function:
void dwt_access_enable(unsigned int ena){
volatile unsigned int *LSR;
LSR = (volatile unsigned int *) 0xe0000fb4;
uint32_t lsr = *LSR;;
//printf("LSR: %.8X - SLI MASK: %.8X\n", lsr, DWT_LSR_SLI_Msk);
if ((lsr & DWT_LSR_SLI_Msk) != 0) {
if (ena) {
//printf("LSR: %.8X - SLKMASK: %.8X\n", lsr, DWT_LSR_SLK_Msk);
if ((lsr & DWT_LSR_SLK_Msk) != 0) { //locked: access need unlock
*FP_LAR_PTR = DWT_LAR_KEY;
printf("FP_LAR directly after change: 0x%.8X\n", *FP_LAR_PTR);
}
} else {
if ((lsr & DWT_LSR_SLK_Msk) == 0) { //unlocked
*FP_LAR_PTR = 0;
//printf("FP_LAR directly after change: 0x%.8X\n", *FP_LAR_PTR);
}
}
}
}
When I call the uncommented print I get 0xC5ACCE55 but when I printed it after the return of the function I get 0x00000000 and I have no idea why. Am I on the right track or is this completely wrong?
Edit: I think it also would be good to mention that I have tried without all the extra code in the function and only tried to change the LAR register.
BR
Gustav
Looking at the docs again, I'm now incredibly suspicious of a typo or copy-paste error in the ARM TRM. 0xe0000fb0 is given as the address of ITM_LAR, DWT_LAR and FP_LSR (and equivalently for *_LSR). Since all the other ITM registers are in page 0xe0000000, it looks an awful lot like whoever was responsible for that part of the Cortex-M7 documentation took the Cortex-M4 register definitions, added the new LAR and LSR to the ITM page, then copied them to the DWT and FPB pages updating the names but overlooking to update the addresses.
I'd bet my dinner that you're unwittingly unlocking ITM_LAR (or the real FP_LAR), and DWT_LAR is actually at 0xe0001fb0.
EDIT by dwelch
Somebody owes somebody a dinner.
hexstring(GET32(0xE0001FB4));
hexstring(GET32(0xE0001000));
hexstring(GET32(0xE0001004));
hexstring(GET32(0xE0001004));
PUT32(0xE000EDFC,0x01000000);
hexstring(GET32(0xE0001FB4));
hexstring(GET32(0xE0001000));
hexstring(GET32(0xE0001004));
hexstring(GET32(0xE0001004));
PUT32(0xE0001000,0x40000001);
hexstring(GET32(0xE0001FB4));
hexstring(GET32(0xE0001000));
hexstring(GET32(0xE0001004));
hexstring(GET32(0xE0001004));
PUT32(0xE0001FB0,0xC5ACCE55);
PUT32(0xE0001000,0x40000001);
hexstring(GET32(0xE0001FB4));
hexstring(GET32(0xE0001000));
hexstring(GET32(0xE0001004));
hexstring(GET32(0xE0001004));
output
00000000
00000000
00000000
00000000
00000003
40000000
00000000
00000000
00000003
40000000
00000000
00000000
00000001
40000001
0000774F
0000B311
The table in the TRM is funny looking and as the other documentation shows you add the 0xFB0 and 0xFB4 to the base, the rest of the DWT for the Cortex-M7 is 0xE0001xxx and indeed it appears that the LAR and LSR are ate 0xE0001FB0 and 0xE0001FB4.
I would advise against creating your own register definitions when they are defined as part of the CMSIS - to do so requires that both the documentation and your interpretation of it are correct. In this case it appears that the documentation is indeed incorrect, but that the CMSIS headers are correct. It is a lot easier to validate the CMSIS headers automatically than it is to verify the documentation is correct, so I would trust the CMSIS every time.
I am not sure what register FP_LAR might refer to, but your address assignment refers to ITM_LAR, but it seems more likely that you intended DWT_LAR which Cortex-M4 lacks.
Despite my advice to trust it, CMSIS 4.00 omits to define masks for DWT_LSR/SWT_LAR, but I believe they are identical to the corresponding ITM masks.
Note also that the LAR is a write-only register - any attempt to read it is meaningless.
Your code using CMSIS would be:
#include "core_cm7.h" // Applies to all Cortex-M7
void reset_cnt()
{
CoreDebug->DEMCR |= 0x01000000;
DWT->CYCCNT = 0; // reset the counter
DWT->CTRL = 0;
}
void start_cnt()
{
DWT->CTRL |= 0x00000001 ; // enable the counter
}
void stop_cnt()
{
DWT->CTRL &= 0xFFFFFFFE ; // disable the counter
}
unsigned int getCycles()
{
return DWT->CYCCNT ;
}
// Not defined in CMSIS 4.00 headers - check if defined
// to allow for possible correction in later versions
#if !defined DWT_LSR_Present_Msk
#define DWT_LSR_Present_Msk ITM_LSR_Present_Msk
#endif
#if !defined DWT_LSR_Access_Msk
#define DWT_LSR_Access_Msk ITM_LSR_Access_Msk
#endif
#define DWT_LAR_KEY 0xC5ACCE55
void dwt_access_enable( unsigned ena )
{
uint32_t lsr = DWT->LSR;;
if( (lsr & DWT_LSR_Present_Msk) != 0 )
{
if( ena )
{
if ((lsr & DWT_LSR_Access_Msk) != 0) //locked: access need unlock
{
DWT->LAR = DWT_LAR_KEY;
}
}
else
{
if ((lsr & DWT_LSR_Access_Msk) == 0) //unlocked
{
DWT->LAR = 0;
}
}
}
}

Simple code confusion about define directive parameter

I'm trying to learn C to program this small routine on a Texas Instruments MSP430. Could you help me understand the ((unsigned char *) 0x0023) part?
I'm having issues understanding this middle portion of this Define directive. I've tried looking this up but found nothing on the ((unsigned char *) 0x0023) portion. This looks like a type cast but its not casting anything.
My major concern is the 0x0023 (decimal 35). Is this just a unsigned char pointer with 35 bits?
Code:
#define P1IFG_ptr ((unsigned char *) 0x0023) unsigned char result;
Any help is really appreciated and thank you in advance.
((unsigned char *) 0x0023)
Is a pointer to address 0x23
I think there's a missing newline in your code sample...
On the MSP430 this is the port P1 interrupt flag register:
Each PxIFGx bit is the interrupt flag for its corresponding I/O pin
and is set when the selected input signal edge occurs at the pin. All
PxIFGx interrupt flags request an interrupt when their corresponding
PxIE bit and the GIE bit are set. Each PxIFG flag must be reset with
software. Software can also set each PxIFG flag, providing a way to
generate a software initiated interrupt. Bit = 0: No interrupt is
pending Bit = 1: An interrupt is pending Only transitions, not static
levels, cause interrupts. If any PxIFGx flag becomes set during a Px
interrupt service routine, or is set after the RETI instruction of a
Px interrupt service routine is executed, the set PxIFGx flag
generates another interrupt. This ensures that each transition is
acknowledged.
You can read from this register, e.g.:
unsigned char result;
result = *P1IFG_ptr;
Or write to it, e.g.:
*P1IFG_ptr = 1;

Resources