Figuring out the ATmega644 UART

Topics related to the API, programming discussions & questions, coding tips, bugs, etc. should go here.
Post Reply
User avatar
Jubatian
Posts: 1561
Joined: Thu Oct 01, 2015 9:44 pm
Location: Hungary
Contact:

Figuring out the ATmega644 UART

Post by Jubatian »

So I started to explore the matter a little, how the ATmega's UART possibly works at low-level.

Very soon it revealed that this wouldn't be easy to emulate. For a start, the peripheral is more complex than SPI, even when used just as an ordinary UART. Now add that CunningFellow started to put cycle-accurate tasks on it...

Fiddling around with the RLE mode demo and how it relates timing, I found a likely implementation, would need further work for confirming, but it seems rather reasonable.

CunningFellow in the RLE mode sets the baud rate, and immediately after that, puts a character on the transmitter. Setting the baud rate (low) is odd, but likely it is there for a reason, that the mode didn't work properly without it. Why that could be? Examining the timing and the settings of the UART, it is set up for 16 samples for a bit, 7 bits per character + 1 start + 1 stop, which would make 9 bits * 16 samples * (8 + 1) baudrate, which is 1296 cycles. But it actually delays 1304 cycles there, 8 cycles more. Just why?

With a bit of reasoning, the baud rate generator should actually be a timer, started by writing the baud rate register if the UART is already enabled. So this timer could be running, dividing the main clock by 9 in this case. That could explain the 8 cycles experienced there, with an additional penalty cycle for the baud rate register write (as those are STS instructions, so the interrupt actually happens 1306 cycles from the baud rate register write, not 1305). Since 9 is not a divisor of 1820 (a scanline takes this many clocks), without setting the baud rate register again and again, it just wouldn't work.

Just a theory for now, but it should be something along the lines of this.

Cycle-accurate UART won't be a simple thing to accomplish.

CunningFellow: If you could provide details on findings while you did this, or possibly even code to experiment with, that could make figuring out the UART's cycle-accurate behavior easier!
CunningFellow
Posts: 1445
Joined: Mon Feb 11, 2013 8:08 am
Location: Brisbane, Australia

Re: Figuring out the ATmega644 UART

Post by CunningFellow »

This is the comments from my code. This was done 12 months ago so other parts are from my memory.

I also didn't know why 1304 clocks (with the given baud rate/data length) but worked it out experimentally.

When the ISR was triggered I made a copy of the return address on the stack and printf'd that to the NTSC TV screen.

A few experiments with code that would trigger in a series of nops and some cycle counting counting had it worked out at 1304.

I guess you could do something similar with a range of different baud rates, data lengths, Stop bits and try work out a relationship.

Code: Select all

.global render_lines_rle
render_lines_rle:

	ldi		r16, 0b00001000
	sts		_SFR_MEM_ADDR(UBRR0L), r16		; Trigger the UART Baud Divider.  THIS is the timing critical line not "STS UDR0"
	sts		_SFR_MEM_ADDR(UDR0), r16		; 1304 clocks after this a UART TX Complete interrupt will trigger
											; This is what ends the scanline (rather than spedning time cycle/pixel counting)
											; If everything has gone well then the interrupt will trigger on one of two
											; valid points and everything will stay cycle perfect.
											; If something has gone wrong and the RLE code is malformed then the interrupt
											; will trigger somewhere else
											; The two valid entry points are just after the "OUT PORTC" below.
											; See the labels "Valid_USR0_Interrupt_End_Point_"

	ldd		r16, Y+1						; check to see if current RLE RAM points to 0xNN, 0xFE token (note that 0xFE only
                                            ;    only means "blank lines" in the first token of a line.  It can also mean a
                                            ;    run of 129 pixels in the special case of a run of 255 pixels which is encoded
                                            ;    as "Run of 126 pixels + run of 129 pixels"
	cpi		r16, 0xFE						; And if it has jump to "blank lines"
	breq	render_lines_rle_blank_line
	cp		r10, r11						; Check to see if we have lost the race with the electron beam
	brsh	render_lines_rle_lost_race		; and deal with it
CunningFellow
Posts: 1445
Joined: Mon Feb 11, 2013 8:08 am
Location: Brisbane, Australia

Re: Figuring out the ATmega644 UART

Post by CunningFellow »

Oh - And yes - I have to restart the UART "timer" every scanline. This is not only because the period is 1304 (not 1820) but also because it does not self-retrigger like the output compare timer.
Post Reply