SEI Stops Program Execution

Go To Last Post
26 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello all,

So I've a bit of problem that for the life of me, I cannot find an answer to or what may be happening.

I am using an ATtiny2313 which I have clocked at 20MHz using an external crystal. For clarity, I am building this using ASM because I need exact timing for this project.

When I enable SEI, the entire program just halts at that point except for the clock cycle immediately following the SEI command. Anything after that never happens. Also, if I run the program in Atmel Studio 6 with F5 to debug, adding a breakpoint on the first line immediately after SEI, will break the program. Anything after that, will not break the program (it never gets called).

There's three lines commented in the code explaining what is happening (or rather not happening).

To further explaining what I eventually want to achieve with this is to output a stable VGA signal. However, that is not the problem.

Thank you guys.

; =========================================================
; == VGA Test                                            ==
; == Author: Jamison Jerving                             ==
; ==   Date: August 4, 2013                              ==
; =========================================================

.org 0x0000
	rjmp reset

.org 0x0004
	rjmp video

; =========================================================
; == Called when a reset happens.                        ==
; =========================================================

reset:

	; Setup the stack pointer.
	ldi r16,low(ramend)
	out spl,r16

	; Enable Timer1 and WGM12 to count up to OCR1A.
	ldi r16,(1 << cs10 | 1 << wgm12)
	sts tccr1b,r16

	; Set the Output Compare Interrupt Enable A for Timer1.
	ldi r16,(1 << ocie1a)
	sts timsk,r16

	; Set OCR1A to count up to 65k
	ldi r16,low(65535)
	sts ocr1al,r16
	ldi r16,high(65535)
	sts ocr1ah,r16

	; Setup Port B.
	ldi r16,0b0000_1111
	out ddrb,r16

	; Enable Global Interrupts
	sei		; <--- All program execution, except the immediate next clock cycle,
			;      seems to stop after SEI is called. If I comment this line out,
			;	   The program in main executes just fine.

; =========================================================
; == Main Program Loop                                   ==
; =========================================================

ldi yl,0				; With SEI enabled, adding a breakpoint here, the program breaks in debug. Any line after this never breaks.
ldi yh,0

main:

	; ON
	ldi r16,0			; With SEI enabled, adding a breakpoint here, the program never seems to reach this line.
	cp yl,r16
	cpc yh,r16
	brne skip1
		sbi portb,2
	skip1:

	; OFF
	ldi r16,low(32767)
	ldi r17,high(32767)
	cp yl,r16
	cpc yh,r17
	brne skip2
		cbi portb,2
	skip2:

	adiw yl,1

	rjmp main

; =========================================================
; == Video Interrupt Timer                               ==
; =========================================================

video:

	; Save the status register.
	in r16,sreg
	push r16
	
	ldi xl,tcnt1l
	ldi xh,tcnt1h

	; ON
	ldi r16,0
	cp xl,r16
	cpc xh,r16
	brne skip3
		sbi portb,3
	skip3:

	; OFF
	ldi r16,low(32767)
	ldi r17,high(32767)
	cp xl,r16
	cpc xh,r17
	brne skip4
		cbi portb,3
	skip4:

	; Restore the status register.
	pop r16
	out sreg,r16

	reti

My digital portfolio: www.jamisonjerving.com

My game company: www.polygonbyte.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The datasheet gives a word address for the vector table, but I believe the assembler ORG directive expects a byte address in all cases.

If that is the issue, the code is jumping to the vector for INT1, not TIMER1 COMPA.
The correct argument for ORG would be 8.

Last Edited: Sun. Aug 4, 2013 - 08:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

the assembler ORG directive expects a byte address in all cases.

Your thinking of avr-as, the Atmel assembler uses word addresses in ORG. Of course it's better to ORG to the label.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Got ya Cliff. I assumed avr-as, my bad! :oops:

Beyond that, it still sounds like a non-initialized vector is being called, if I read the OP correctly.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you both for the input. As per your first post, mikericetga, I changed the second ORG to 0x0008. Now the main program runs flashing my green LED on Port B, pin 2. However, the Timer1 Compare A Interrupt does not seem to be called at all (I have a red LED on Port B, pin 3, which should flash). In fact, the main routine is a running a lot slower it seems than it should. When commenting out SEI, again, the green LED flashes must faster, as it should.

[Edit]
So upon further look, it seems changing ORG 0x0008, the label video is now being called properly (as nothing was executing within that block before). However, the entire program is running extremely slow and the code I'm using to flash the LED on port 3 does not seems to work.

My digital portfolio: www.jamisonjerving.com

My game company: www.polygonbyte.com

Last Edited: Sun. Aug 4, 2013 - 09:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Also, if I run the program in Atmel Studio 6 with F5 to debug, adding a breakpoint on the first line immediately after SEI, will break the program. Anything after that, will not break the program (it never gets called).


Why don't you simply step through the code?

Also, if you haven't already, hunt up what is said about SEI -- something like "the first instruction after global interrupts are enabled is always carried out"?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
Quote:

Also, if I run the program in Atmel Studio 6 with F5 to debug, adding a breakpoint on the first line immediately after SEI, will break the program. Anything after that, will not break the program (it never gets called).


Why don't you simply step through the code?

Also, if you haven't already, hunt up what is said about SEI -- something like "the first instruction after global interrupts are enabled is always carried out"?


I'm sorry, I should have mentioned that in my original post. According to the manual for the ATtiny2313 (and probably most AVRs I would assume), the next instruction immediately following SEI is carried out. However, that was not the problem that the nothing at all was being called after SEI.

I do believe mikericetag seems to have hit the nail on the head. Changing the vector address to 0x0008 seems to show that both my main routine label is called and the video routine label is called.

I guess I don't fully understand why 0x0008 worked though. According to the datasheet, they specifically list the vector address as 0x0004 for the "Timer/Counter1 Compare Match A" interrupt, whereas 0x0008 is the "USART0 Data Register Empty" interrupt. Am I missing something about how they describe these addresses?

Thanks.

My digital portfolio: www.jamisonjerving.com

My game company: www.polygonbyte.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
For clarity, I am building this using ASM because I need exact timing for this project.

Programming in ASM for clarity never does it for me. Your problem is interesting though, but life is to short for debugging asm code.
I would suggest programming in C for clarity (ignoring any accuracy issue for the moment) and just get it working.
Once you can compile it and it runs, optimize the resultant asm code to get the accuracy you require.

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

LDEVRIES wrote:
Programming in ASM for clarity never does it for me..... life is to short for debugging asm code.

I would suggest programming in C for clarity...and just get it working.

Once you can compile it and it runs, optimize the resultant asm code to get the accuracy you require.


This is good advice.

regards
Greg

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jamison wrote:
I guess I don't fully understand why 0x0008 worked though. According to the datasheet, they specifically list the vector address as 0x0004 for the "Timer/Counter1 Compare Match A" interrupt, whereas 0x0008 is the "USART0 Data Register Empty" interrupt. Am I missing something about how they describe these addresses?
Thanks.

The vector table shows WORD addresses, not BYTE address.

It would be a good idea to carefully re-check that your code is correct and not accidentally enabling some other interrupt besides the one you want.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jamison wrote:
When I enable SEI, the entire program just halts at that point ...

First, the .lss file shows

.equ	OC1Aaddr	= 0x0004	; Timer/Counter1 Compare Match A

so I think your

.org 0x0004
   rjmp video

is correct.
When you changed it to 0x0008 (USART), the green LED blinked because the ISR never fired.

Note that in your ISR, you use R16 and R17, but you don't preserve them first (push & pop them).
Your main loop also uses R16 and R17.
When the interrupt fires, R16 & R17 are corrupted and the Green LED doesn't blink.

About the ISR, the interrupt occurs when the counter reaches a certain value. But the ISR then checks the value of the counter for other values that never match. Thus the Red LED never blinks.

Instead, just have the ISR toggle the LED on/off.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As I said above, the whole point of using a label is so you don't have to fanny about working out whether it is 0x0004 or 0x0008. Just use:

.org 0x0000
   rjmp reset

.org OC1Aaddr
   rjmp video 

As it happens tn2313def.inc contains:

.equ	OC1Aaddr	= 0x0004	; Timer/Counter1 Compare Match A

So it is 0x0004.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

However, that was not the problem that the nothing at all was being called after SEI.

No, that isn't true. You said that when the breakpoint was at the instruction following the SEI, the breakpoint was hit.

Now, >>something<< happens after that. As I mentioned, a simple step-step-step should reveal what, whether on the simulator or ICE (I can't see that we know which).

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The above said, what >>is<< likely to be happening here? So I scanned the posted program and indeed it would appear that >>something<< should happen...

-- Perhaps show the complete program, including the chip-include.

-- 16-bit register operations...

Quote:
Accessing 16-bit Registers
...
To do a 16-bit write, the high byte must be written before the low byte. For a 16-bit read, the low byte must be read before the high byte. ...

Quote:

; Set OCR1A to count up to 65k
ldi r16,low(65535)
sts ocr1al,r16
ldi r16,high(65535)
sts ocr1ah,r16

-- STS/LDS vs. IN/OUT -- OCR1AH/L are within IN/OUT range. Who knows what the STS is doing. (See the AVR app note for the LOAD/STORE macros AVR001)

-- I'm a little confused about the intent. Setting a compare match at 0xFFFF? Do you want 64k ticks, or one less?

-- As the compare-match interrupt is being used, the ISR (at least the way I read it) is a bit strange. As the timer is counting at /1, the 0x0000 and 0x3fff compares will never be true, will they?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

-- R16 is used as "scratch" in both the main line and ISR. But it is not protected from the interrupt firing. (The ISR needs to push it first and restore later. Or the ISR needs a scratch register just for the ISR. Same with R17.)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Explain what you actually want to do.

Currently your "video" ISR() just toggles PORTB.3
Nothing much else as far as I can see. (apart from trashing R24/R25/R16/R17 irretrievably)

You can set/clear OC1x pins completely in hardware without even using interrupts. So you get zero-jitter.

Atomiczombie is the expert with video. Look at his posts / projects.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

including the chip-include.

Sadly such things are a thing of the past in AS6. Remember it passes the equivalent of a .include on the command line (from the project target selection) so the source itself does not need .include. This means that in future we will see less and less example code that is self-documenting about which target it is intended for. :-( (well done Atmel - not!).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually, we have the same mystery with C code.
We can't tell which chip from or

We can only guess by looking at PORT and pin references.
This can 'suggest' mega48 family or mega164 family or perhaps a 'big' TQFP-64 or TQFP-100 chip.

Some of the mega8535/163/169 ... wierdos require careful scrutiny of SFR names.

Life is easier if posters just say which AVR.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually, we have the same mystery with C code.
We can't tell which chip from or

We can only guess by looking at PORT and pin references.
This can 'suggest' mega48 family or mega164 family or perhaps a 'big' TQFP-64 or TQFP-100 chip.

Some of the mega8535/163/169 ... wierdos require careful scrutiny of SFR names.

Life is easier if posters just say which AVR.

David.

p.s. On an Off-Topic note. How can you tell which LPC17xx chip is used in Code-Red ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Sadly such things are a thing of the past in AS6. Remember it passes the equivalent of a .include on the command line (from the project target selection) so the source itself does not need .include.

I'm not much of an AS6 person, so wasn't aware of that.

So, the rephrase of the point...

-- Tell which processor model is the assembly target, and how the build results verify that.

Something like that? In a quick scan, that code would build cleanly for e.g. Mega48 family and Mega164 family. But surely not give the expected run-time behaviour for a Tiny2313 target.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

ldi xl,tcnt1l
ldi xh,tcnt1h


Surely not what is intended?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So there's quite a few too many previous replies for me to respond to them all, so I will try to summarize a response.

But first, I will first say that I clearly do not have very much knowledge in ASM or even electronics for that matter. This is a hobby project for me and albeit I may be way in over my head, I am will willing to accept the challenge.

I certainly have learned a lot from this post and that I have made quite a few mistakes in my original code. The code I posted was exactly what was loaded onto the controller.

theusch:
Thank you for pointing out the information about the HIGH byte needing to be written first. This is something I have not discovered yet and makes a lot of sense. Also, I used STS because that is what I saw was used in the LucidScience project for VGA (of which I will not post the link because I am unaware if that is acceptable or not).

The R16/R17 registers are not storing any permanent/vital information. Does this still mean they need to be pushed onto the stack first during an interrupt? Even though they are only used as temporary registers to move data?

david.prentice:
This was simply a test to verify my interrupts were working properly. The end goal of learning electronics for me is to eventually build a retro game console with VGA out. I understand that goal may be far ahead of me.

Those asking what processor/target I am targeting: I stated that clearly in my original post.

Also, I was clearly not understanding that the WGM12 flag counts UP to OCR1A and THEN fires the Output Compare Interrupt when it reaches that number. For some reason, I thought it was counting to OCR1A but firing every tick (1, 2, 3, ...).

I definitely appreciate all the feedback I have received and I have learned a lot about what I've done wrong.

Thanks. [Also: Sorry for the massive most!!]

My digital portfolio: www.jamisonjerving.com

My game company: www.polygonbyte.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Those asking what processor/target I am targeting: I stated that clearly in my original post.

But are you indeed >>building<< for that target?

Quote:

So there's quite a few too many previous replies for me to respond to them all

Like, single-stepping through the SEI and following to see what is really happening?

I'm out.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, you can have scratch registers that are only used in an ISR(). However this means that you can never use them in the foreground code.

Personally, I would start with C. It is far easier to just say:

ISR(TIM1_COMPA_vect)
{
    count++;
    if (count == 32767) do_something();
    if (count == 65535) do_anotherthing();
}

The C compiler will look after the fiddly bits.

When you have more experience / confidence you can re-write the ISR() in ASM.

There are several AVR-specific behaviours that are easier to learn in C. e.g. read SFR16 lo-hi and write hi-lo.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
Quote:

Those asking what processor/target I am targeting: I stated that clearly in my original post.

But are you indeed >>building<< for that target?

Quote:

So there's quite a few too many previous replies for me to respond to them all

Like, single-stepping through the SEI and following to see what is really happening?

I'm out.

Yes, of course. When I created the AS6 project, I selected the AVR controller I am using, which also automatically adds the required include file.

I did step through the code and it showed that the main routine was being called but not the ISR. I apologize if I did not explain that part. As I've said, there is clearly much that I have to learn about microcontroller programming with assembly.

david.prentice wrote:
Yes, you can have scratch registers that are only used in an ISR(). However this means that you can never use them in the foreground code.

Personally, I would start with C. It is far easier to just say:

ISR(TIM1_COMPA_vect)
{
    count++;
    if (count == 32767) do_something();
    if (count == 65535) do_anotherthing();
}

The C compiler will look after the fiddly bits.

When you have more experience / confidence you can re-write the ISR() in ASM.

There are several AVR-specific behaviours that are easier to learn in C. e.g. read SFR16 lo-hi and write hi-lo.

David.

David, thanks for explaining in more detail about using registers between the ISR and the rest of the program.

I have been doing microcontroller programming (albeit very little at a time) with C++ for about 6 months now. The reason I am doing this with ASM is to 1) understand more about the architecture of microcontrollers 2) from what I've been reading, to display a stable VGA image, it requires exact clock cycle timing, and that needs to be done with ASM 3) because I'm having fun with it.

If anyone has information on whether this is wrong and that it can actually be done with just C++, that would be great and I would love to read any article or tutorial confirming as such.

Again, thank you for the information.

Jamison

My digital portfolio: www.jamisonjerving.com

My game company: www.polygonbyte.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I did step through the code and it showed that the main routine was being called but not the ISR.

I doubt very much that you did that.

First, tell how you did this stepping. Did you use debugWire, or the AVRstudio simulator?

So, you got to the "main" and step-step-step. You got to the SEI and hit step. Then what happened? You got to the instruction after the SEI? And then when you hit step again, where did it go? Somewhere? No-where? As you said in the title that it "stops program execution", doing the next step did ... nothing? And when you hit it again? And when you hit it again?

The code as presented has some major flaws to work cleanly. It certainly won't work as intended. It very well might be cascading output compare interrupts as there will be a compare-match at 0x0000 since OCR1A never gets set properly. This can give the appearance of "stopping" the mainline as only one instruction will be carried out for each ISR invocation.

I was going to plop the code into the simulator and see for myself. But I decided it would be silly to do that given the major flaws:
-- STS to OCR1AL/H
-- Wrong high/low byte order
-- LDI at the comparison address vs. LDS
-- Comparison(s) that can never be true, even with the proper loading of the comparison information
-- Improper use of the temporary registers (R16/R17) in the ISR without saving

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.