AVR128Dx - Assembler access to mapped flash program memory

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

Flash memory can be easily accessed with these controllers because it can be mapped in blocks in the data space.

After the reset, the 4th block (96-128K) is preset for this, which starts from address $ c000 and is always mapped

in the 32K data space area from 8000H.

 

So far I have been doing it this way in code space:

 

.org $c000

 

F_DATASTR:  .DB "STRING",$0a

 

.equ DATASTR    = (F_DATASTR*2)-$10000

 

The string can now be easily accessed in the program:

 

lds r16,DATASTR  ;load first sign in r16.

 

or

 

ldi ZL,low(DATASTR)

ldi ZH,high(DATASTR)

ldd r16,Z+0

 

My question: Can the access be formulated more simply?

This way the correct address has to be predefined for all data.

Laborious...

Last Edited: Mon. Dec 6, 2021 - 10:07 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sounds like you need a linker (tool for placing data and code automatically).

 

Maybe consider using avr-as rather than avrasm2 ?

 

BTW even if you want to do all the code in Asm it can often make sense to actually define the variable/data in C:

const __flash char F_DATASTR[] = "STRING\n";

Build that alongside the .S files and then just refer t F_DATASTR[] from within the Asm.

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

clawson wrote:
Build that alongside the .S files and then just refer t F_DATASTR[] from within the Asm.

 

That would just be another form of bureaucracy.

That just makes it different but not better indecision

 

Are there any other suggestions?

Last Edited: Mon. Dec 6, 2021 - 10:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1


GermanFranz wrote:
That just makes it different but not better
But is IS better because you can just type:

const __flash char F_DATASTR1[] = "FIRST STRING\n";
const __flash char F_DATASTR2[] = "SECOND STRING\n";
const __flash char F_DATASTR3[] = "THIRD STRING\n";
const __flash char F_DATASTR4[] = "FOURTH STRING\n";

and the linker will place them in memory (adjacent but not overlapping) without any intervention from you having to worry about their placement.

 

Something like:

and

resulting in:

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

But your code use LPM. (the good old way).

 

OP wants to use flash that is memory mapped.

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

clawson wrote:

But it IS better because you can just type

 

Thanks clawson for your solution.

 

Unfortunately it doesn't convince me. I'm looking for a simpler solution here to access mapped flash.

That means no additional files, tools, no additional mixing with c language elements too.

I would also like to stay at avrasm2.

 

Now I only need, easy to understand,

F_DATASTR:  .DB "STRING",$0a

.equ DATASTR    = (F_DATASTR*2)-$10000

 

P.S. It could of course be that it really couldn't be easier.

I had seen a macro solution before, but it had its pitfalls.

Last Edited: Tue. Dec 7, 2021 - 11:25 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Maybe I'm missing something, but I don't see the advantage of putting anything in a C file here.

 

clawson wrote:
But is IS better because you can just type: ...

and the linker will place them in memory (adjacent but not overlapping) without any intervention from you having to worry about their placement.

 

Why is that better than:

 

F_DATASTR1:  .DB "STRING1",$0a
F_DATASTR2:  .DB "STRING2",$0a
F_DATASTR3:  .DB "STRING3",$0a
F_DATASTR4:  .DB "STRING4",$0a

GermanFranz's method seems reasonable to me, but I would use a macro instead of manually creating two separate labels DATASTR and F_DATASTR.

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

bigmessowires wrote:
but I would use a macro instead

 

what could the macro look like?

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

Trying rhis on the m4809, is this what you mean?   

F_DATASTR:  .DB "STRING",$0a
F_DATASTR1: .DB "Whatever",$0a 

.equ constant = $C000 ; Change this to your offset/displacement
                      ; $10000  ? 
 ;----------------------------------
 ;USE:
 ;   pointZtoflash DATA_Label
 ;--------------------------------
.macro pointZtoflash ;          	
		
	   ldi ZL, LOW((@0*2)-(constant))
	   ldi ZH, HIGH((@0*2)-(constant))
        
.endmacro
 ;----------------------------------
 ;USE:
 ;   pointAndGet DATA_Label
 ;--------------------------------
.macro pointAndGet ;  
		
	   ldi ZL, LOW((@0*2)-(constant))
	   ldi ZH, HIGH((@0*2)-(constant))
           ldd r16, Z+0	 

.endmacro



start: 
			                            
		 pointZtoflash F_DATASTR	
		 ldd r16, Z+0	          ; load first byte of Data
		 pointZtoflash F_DATASTR1
		 ldd r16, Z+0	
		 nop
		 nop
		 nop
		 pointAndGet F_DATASTR
		 pointAndGet F_DATASTR1

		 												
while_1: 
			rjmp while_1

Good question!... I assume you have the mechanism to inc the address as needed. 

I reserve my right to assemble!
Brawndo's got what plants crave... It's got electrolytes!

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

Plexd wrote:
Trying rhis on the m4809, is this what you mean?   

 

It looks good, thanks Plexd.

But I remember already having problems with a macro solution.  I will try it as soon as possible and report here.

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

With the AVR128Dxxx, the Flash can be used with LD/ST in 32K sections as part of 
the CPU data space... Sort of like bank switching in some of the PIC MCUs. You have
to switch "banks" by writing to the NVMC_CTRLB bits 4,5 FLMAP. I'm pretty sure (99%)
that the macros I posted can't work. I'm going to be looking in to this because I want to 

use the chips (and they're available). Plenty of flash and RAM and a bit faster with good

peripherals. I have no SIMULATOR for this chip with AS7, I will check MPLABX.

                                                                                           Hang in there

 

This works in MPLABX sim for this example.

.include "AVR128DA48def.inc"     
.def zero = r2
.def one = r3 
.def tmp = r18 
.def count = r19
.equ SREG = CPU_SREG
.cseg
.org 0  rjmp start

F_DATASTR:  .DB "STRING",$0a
F_DATASTR1: .DB "Whatever",$0a 
		
;----------------------------------------
;      Point And Get with Z reg	example
; USE:		
; pagz Data_label, Data_Element	
; E.G.:		
;	pagz F_DATASTR,5      	; gets the "G" in "STRING", puts in r18	
;----------------------------------------		
.macro pagz ; point and get with Z reg

		ldi ZL, LOW(@0*2 + 0x8000)     ;  
		ldi ZH,HIGH(@0*2 + 0x8000)
		ldd r18, Z + @1	     
.endmacro
		
start:         
		 ser tmp
	         out VPORTA_DIR,tmp
		 ldi XL,LOW(F_DATASTR*2+ 0x8000)     ; goto data mapped memmory space
		 ldi XH,HIGH(F_DATASTR*2 + 0x8000)   ; can it be done?
lp:
		 ld tmp,x+                  ; loops "STRING" into PORTA
		 cpi tmp,0x0a               ; compare to $0a
		 breq end_lp
		 
		 out VPORTA_OUT,tmp         ; display on PORTA
		 rjmp lp		 
end_lp:			 
		 pagz F_DATASTR,4           ; gets the "N" in "STRING"  
		 out VPORTA_OUT,tmp         ; display on PORTA
		 
		 pagz F_DATASTR1,0      	
                 out VPORTA_OUT,tmp         ; display on PORTA		 
while_1: 
			rjmp while_1

But does it work on hardware?

I reserve my right to assemble!
Brawndo's got what plants crave... It's got electrolytes!

Last Edited: Mon. Dec 20, 2021 - 09:59 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Plexd wrote:
You have
to switch "banks" by writing to the NVMC_CTRLB bits 4,5 FLMAP. I'm pretty sure (99%)
that the macros I posted can't work.

 

Thanks, the macros work brilliantly for me because I only simply use the default top 96-128K flash section for fixed data.

I have created 3 macros for loading the pointer registers with the data addresses there:

 

.macro ldx
        ldi     XL,low((@0*2)-$10000)
        ldi     XH,high((@0*2)-$10000)
.endmacro

.macro ldy
        ldi     YL,low((@0*2)-$10000)
        ldi     YH,high((@0*2)-$10000)
.endmacro

.macro ldz
        ldi     ZL,low((@0*2)-$10000)
        ldi     ZH,high((@0*2)-$10000)
.endmacro

Now X, Y or Z can simply be loaded with the address via ldx/ldy/ldz F_DATASTR. Perfect. It works.

Last Edited: Mon. Dec 20, 2021 - 11:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good information to know, I've been wondering about this for over a year now... Just got my Snap debugger working and I only have the m4809 chip but I think the devices are similar... Good Luck!

I reserve my right to assemble!
Brawndo's got what plants crave... It's got electrolytes!

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

I only have the m4809 chip but I think the devices are similar.

The 4809 is capable of mapping ALL of its flash into the RAM space, so you don't need to worry about the banking.  (but the macros should work similarly)

(presumably, this is why the 4809 has such an "odd" (not a power of 2) flash size.  It's about the max that will fit nicely into the 64kbyte address space.)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
.macro ldx
        ldi     XL,low((@0*2)-$10000)
        ldi     XH,high((@0*2)-$10000)
.endmacro

That's pretty clean, but it's a bit chip-specific.

I did a bit of work to make the macros more general, so that they can be used on other chips without modification.

I THINK these should work ok on everything form ATmega808 to AVRxxxDyzz; I added a macro "lastFlashData" to transparently do the .org to the "last page" of flash...

(tested by object code inspection, though...)

 

;
; mappedflash.asm
;
; Created: 12/20/2021 12:49:00 AM
; Author : billw
; Released to the public domain

.macro lastFlashData
; Arguments: None
; Set the location (.org) to the last page of mappable flash.
; This is either the last PROGMEM_SIZE bytes of flash, or just
; the current location (if some code already occupues that page)
; this should work with parts that can map ALL their flash into
; the data space, or parts that can only map 32k.
;
.cseg   ; make sure we're in "code" memory.
.if (((PROGMEM_END+1) - MAPPED_PROGMEM_SIZE)/2) > PC
; if code has NOT encroached on the last mappable window of flash
;   then put flash data in that last page.
.org (FLASHEND+1) - (MAPPED_PROGMEM_SIZE/2)
.endif
; if code DOES use last page, just continue flash data at .
.endm

.macro ldxmf  ; load X from mapped flash
; Arguments: <flashaddr>
;  load the address of the label into X.
;  The label should be in cseg, within the section of flash that
;  is mapped to the "mapped flash" section of the data space.
;  (actually setting up the mapping must be done elsewhere.)
;
        ldi     XL,low((@0*2))
        ldi     XH,high(MAPPED_PROGMEM_START + ((@0*2) & (MAPPED_PROGMEM_START-1)))
.endmacro

.macro ldymf  ; load Y from mapped flash
; Arguments: <flashaddr>
;  load the address of the label into Y.
        ldi     YL,low((@0*2))
		ldi     YH,high(MAPPED_PROGMEM_START + ((@0*2) & (MAPPED_PROGMEM_START-1)))
;        ldi     YH,high((@0*2) & 0xFFFF)
.endmacro

.macro ldzmf  ; load Z from mapped flash
; Arguments: <flashaddr>
;  load the address of the label into Z.
        ldi     ZL,low((@0*2))
		ldi     ZH,high(MAPPED_PROGMEM_START + ((@0*2) & (MAPPED_PROGMEM_START-1)))
.endmacro

.macro ldsmf  ; load register direct from mapped flash (LDS)
; Arguments: <register>, <flashaddr>
	  lds @0, MAPPED_PROGMEM_START + (((@1)*2) & (MAPPED_PROGMEM_START-1))
.endm

.macro ldsmfo ; load register direct form mapped flash, with offset.
; Arguments: <register>, <flashaddr>, <offset>
; this permits loading a byte from an odd flash address (since all cseg
;   labels are even address (word addressing)
	  lds @0, MAPPED_PROGMEM_START + ((@2+((@1)*2)) & (MAPPED_PROGMEM_START-1))
.endm

.cseg
.org 0
vectors:
    rjmp start
    rjmp noint
noint: rjmp noint

.listmac

; test cases
start:
	ldxmf mystring
    ldymf otherst
	ldzmf s1
	ldsmf r1, d1
	ldsmfo r2, d1, 1
	rjmp start

fdata:
	lastFlashData
    d1: .db 1,2,3,4
    s1: .db "string",0,0
	mdata: .dd 0x1234, 0xdeadbeef, 0, 1, 2, 3, 6789
	mystring: .db "hello world\n"
	otherst: .db "another string"

 

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

westfw wrote:
but it's a bit chip-specific.

 

Yes it is, I don't actually use any other controller myself.

For this reason, I would have preferred to consider the flash section setup rather than other controllers wink

 

But your macros are very useful, especially the last one loading any register directly. Thanks for the addition.

 

Now you could expand the macro programming and use of macros endlessly. Personally, I don't like that because it makes asm-code more complicated overall. With this in mind, using simple solution

 

GermanFranz wrote:

F_DATASTR:  .DB "STRING",$0a

.equ DATASTR    = (F_DATASTR*2)-$10000

 

still has its charm: Lds Reg, DATASTR (+ x) works just as well, without any new code words... 

Last Edited: Wed. Dec 22, 2021 - 06:49 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

you could expand the macro programming and use of macros endlessly. Personally, I don't like that

Me either, once you get beyond short and/or standard functions, or things that don't affect overall readability much.

I hate it when someone writes "assembly language" that isn't readable by other people who are familiar with the assembly language for that CPU.

 

That said, I wish C had a macro processor as good as the gnu assembler's (one that could actually be symbol and math aware, rather than just text substitution), and I once wrote a macro that turned fortran-style "format" strings into asciz strings (column one carriage control characters converted to the similar ascii chars - '1' to 'formfeed' and similar.)  I don't think I ever used it, though; just an interesting exercise for the "repeat for every character in the argument" feature.