* * *

Author Topic: AVR microcontroller unit format  (Read 10346 times)

ccrause

  • Full Member
  • ***
  • Posts: 129
AVR microcontroller unit format
« on: November 03, 2017, 08:57:41 pm »
I am busy regenerating the port/register and bit field definitions for AVR microcontrollers.  While I'm at it I thought of reformatting the units so that bit definitions appear directly after the relevant register, similar to GCC style.  Since register locations are specified as variables at absolute addresses and bit definitions are specified as constants this leads to mixing of var/const definitions which kind of seem un-Pascal like. Is there any reasonable objection against this style?
Example:
Code: Pascal  [Select]
  1. var
  2.   TCNT0: byte absolute $46;  // Timer/Counter0
  3.   OCR0A: byte absolute $47;  // Timer/Counter0 Output Compare Register
  4.   OCR0B: byte absolute $48;  // Timer/Counter0 Output Compare Register
  5.   GPIOR1: byte absolute $4A;  // General Purpose I/O Register 1
  6.   GPIOR2: byte absolute $4B;  // General Purpose I/O Register 2
  7.   SPCR: byte absolute $4C;  // SPI Control Register
  8. const
  9.   SPR0 = $00;  // SPI Clock Rate Selects
  10.   SPR1 = $01;  // SPI Clock Rate Selects
  11.   CPHA = $02;  // Clock Phase
  12.   CPOL = $03;  // Clock polarity
  13.   MSTR = $04;  // Master/Slave Select
  14.   DORD = $05;  // Data Order
  15.   SPE = $06;  // SPI Enable
  16.   SPIE = $07;  // SPI Interrupt Enable
  17.  
  18. var
  19.   SPSR: byte absolute $4D;  // SPI Status Register
  20. const
  21.   SPI2X = $00;  // Double SPI Speed Bit
  22.   WCOL = $06;  // Write Collision Flag
  23.   SPIF = $07;  // SPI Interrupt Flag

Leledumbo

  • Hero Member
  • *****
  • Posts: 7958
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: AVR microcontroller unit format
« Reply #1 on: November 04, 2017, 09:16:35 am »
I don't see any un-Pascalism there, looks OK to me.

Thaddy

  • Hero Member
  • *****
  • Posts: 6899
Re: AVR microcontroller unit format
« Reply #2 on: November 04, 2017, 10:47:21 am »
No, not at all. I approach it basically in the same way: declaring with absolute.
But if you want to get rid of the var, you can also do:
Code: Pascal  [Select]
  1. {$push}{$writebleconst on} // or {$J+}
  2. const
  3.   TCNT0: byte absolute $46;  // Timer/Counter0
  4.   OCR0A: byte absolute $47;  // Timer/Counter0 Output Compare Register
  5.   OCR0B: byte absolute $48;  // Timer/Counter0 Output Compare Register
  6.   GPIOR1: byte absolute $4A;  // General Purpose I/O Register 1
  7.   GPIOR2: byte absolute $4B;  // General Purpose I/O Register 2
  8.   SPCR: byte absolute $4C;  // SPI Control Register
  9.   SPR0 = $00;  // SPI Clock Rate Selects
  10.   SPR1 = $01;  // SPI Clock Rate Selects
  11.   CPHA = $02;  // Clock Phase
  12.   CPOL = $03;  // Clock polarity
  13.   MSTR = $04;  // Master/Slave Select
  14.   DORD = $05;  // Data Order
  15.   SPE = $06;  // SPI Enable
  16.   SPIE = $07;  // SPI Interrupt Enable
  17.   SPSR: byte absolute $4D;  // SPI Status Register
  18.   SPI2X = $00;  // Double SPI Speed Bit
  19.   WCOL = $06;  // Write Collision Flag
  20.   SPIF = $07;  // SPI Interrupt Flag
  21. {$pop}

Maybe you can also group the vars that are now typed consts and have only those between the writable consts stuff. {$J+} is a local directive. Don't forget push/pop to restore the original flag.
Something like:
Code: Pascal  [Select]
  1. const
  2.   SPR0 = $00;  // SPI Clock Rate Selects
  3.   SPR1 = $01;  // SPI Clock Rate Selects
  4.   CPHA = $02;  // Clock Phase
  5.   CPOL = $03;  // Clock polarity
  6.   MSTR = $04;  // Master/Slave Select
  7.   DORD = $05;  // Data Order
  8.   SPE = $06;  // SPI Enable
  9.   SPIE = $07;  // SPI Interrupt Enable
  10.   SPI2X = $00;  // Double SPI Speed Bit
  11.   WCOL = $06;  // Write Collision Flag
  12.   SPIF = $07;  // SPI Interrupt Flag
  13. {$push}{$j+}
  14.   TCNT0: byte absolute $46;  // Timer/Counter0
  15.   OCR0A: byte absolute $47;  // Timer/Counter0 Output Compare Register
  16.   OCR0B: byte absolute $48;  // Timer/Counter0 Output Compare Register
  17.   GPIOR1: byte absolute $4A;  // General Purpose I/O Register 1
  18.   GPIOR2: byte absolute $4B;  // General Purpose I/O Register 2
  19.   SPCR: byte absolute $4C;  // SPI Control Register
  20.   SPSR: byte absolute $4D;  // SPI Status Register
  21. {$pop}
That makes only the typed consts writable for just that block and other type consts will remain not writable. (or writable if $J+ was already active. That's where the push pop comes in.)
But basically your code is fine.
« Last Edit: November 04, 2017, 11:08:04 am by Thaddy »
Ada's daddy wrote this:"Fools are my theme, let satire be my song."

kupferstecher

  • Full Member
  • ***
  • Posts: 218
Re: AVR microcontroller unit format
« Reply #3 on: November 04, 2017, 11:22:35 am »
Is there any reasonable objection against this style?
In my feeling its better to seperate the register and the bit definitions. With this its easier to get an overview of the available registers and compare them to the datasheet. Mixing with the bit-definitions makes it more cryptic in my opinion, especially for beginnners. A way to keep it structured probably would be to still group registers within their peripherial and list bit definitions below. E.g. all UART-registers, then all UART-bit definitions. But that means quite some efforts if its not ready in the source.

Is there a technical reason you want to do it that way? Probably extracting the definitions from the GCC-header? I checked the assembler header, there the register and bit definitions are seperated.


Short extract:
Code: [Select]
; ***** I/O REGISTER DEFINITIONS *****************************************
; NOTE:
; Definitions marked "MEMORY MAPPED"are extended I/O ports
; and cannot be used with IN/OUT instructions
.equ SREG = 0x3f
.equ SPL = 0x3d
.equ SPH = 0x3e
.equ OCR0 = 0x3c
.equ GICR = 0x3b
.equ GIFR = 0x3a

[...]

; ***** BIT DEFINITIONS **************************************************

; ***** EEPROM ***********************
; EEDR - EEPROM Data Register
.equ EEDR0 = 0 ; EEPROM Data Register bit 0
.equ EEDR1 = 1 ; EEPROM Data Register bit 1
.equ EEDR2 = 2 ; EEPROM Data Register bit 2
.equ EEDR3 = 3 ; EEPROM Data Register bit 3
.equ EEDR4 = 4 ; EEPROM Data Register bit 4
.equ EEDR5 = 5 ; EEPROM Data Register bit 5
.equ EEDR6 = 6 ; EEPROM Data Register bit 6
.equ EEDR7 = 7 ; EEPROM Data Register bit 7

; EECR - EEPROM Control Register
.equ EERE = 0 ; EEPROM Read Enable
.equ EEWE = 1 ; EEPROM Write Enable
[...]

Thaddy

  • Hero Member
  • *****
  • Posts: 6899
Re: AVR microcontroller unit format
« Reply #4 on: November 04, 2017, 11:42:28 am »
Yes, you suggest a better order too.
But in that case (it is nitpicking) you can also use a bitpacked record or bitpacked set for the subsequent bits, which makes it even more readable and pascal like.
Ada's daddy wrote this:"Fools are my theme, let satire be my song."

kupferstecher

  • Full Member
  • ***
  • Posts: 218
Re: AVR microcontroller unit format
« Reply #5 on: November 04, 2017, 11:57:14 am »
you can also use a bitpacked record or bitpacked set for the subsequent bits
That discussion was here, before. While bitpacking makes the access to bits easier and clearer, the access to the complete register is getting more complicated.
I prefer the "standard" way, where no cryptic errors could occur because you try to assign an integer value to a bitpacked type.

Thaddy

  • Hero Member
  • *****
  • Posts: 6899
Re: AVR microcontroller unit format
« Reply #6 on: November 04, 2017, 12:24:24 pm »
The access to the register can be very nicely done using a bitpacked variant record.... Or an advanced record  Anyway, I do it like in my first code example above.

Any example adapted from the wiki:
Code: Pascal  [Select]
  1.     TRegisterBits = bitpacked record
  2.      Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7: Boolean;// replace BitX with bit name/meaning
  3.     end;
  4.  
  5.      TAVRRegister = bitpacked record
  6.      case boolean of
  7.      false:(register:byte);
  8.      true:(Bit:TRegisterBits);
  9.     end;
  10.  

http://wiki.freepascal.org/Bit_manipulation
« Last Edit: November 04, 2017, 12:29:11 pm by Thaddy »
Ada's daddy wrote this:"Fools are my theme, let satire be my song."

ccrause

  • Full Member
  • ***
  • Posts: 129
Re: AVR microcontroller unit format
« Reply #7 on: November 04, 2017, 03:30:01 pm »
Is there a technical reason you want to do it that way? Probably extracting the definitions from the GCC-header? I checked the assembler header, there the register and bit definitions are seperated.
I want to regenerate the AVR microcontroller units to fix up some mixed up bit field names, so I am thinking of ways in which the presentation of the information can be improved.  Currently the register and bit definitions are separate like you suggested.  For me the current layout doesn't provide much insight, if I didn't know the datasheet names of bit fields for a register I would most probably not find them at all.

I've looked at how GCC does it and that convention seems a bit better for me.

ccrause

  • Full Member
  • ***
  • Posts: 129
Re: AVR microcontroller unit format
« Reply #8 on: November 04, 2017, 04:09:41 pm »
The access to the register can be very nicely done using a bitpacked variant record.... Or an advanced record  Anyway, I do it like in my first code example above.

Any example adapted from the wiki:
Code: Pascal  [Select]
  1.     TRegisterBits = bitpacked record
  2.      Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7: Boolean;// replace BitX with bit name/meaning
  3.     end;
  4.  
  5.      TAVRRegister = bitpacked record
  6.      case boolean of
  7.      false:(register:byte);
  8.      true:(Bit:TRegisterBits);
  9.     end;
  10.  
So this looks like an elegant option, but do I then have to create a new type for each config register with unique bit names?
Code: Pascal  [Select]
  1. type
  2.   TADCSRA = bitpacked record
  3.   case boolean of
  4.   false:(
  5.       ADPS: 0..7;
  6.       ADIE,
  7.       ADIF,
  8.       ADATE,
  9.       ADSC,
  10.       ADEN: 0..1);
  11.   true:
  12.     (b: byte);
  13.   end;
  14.  
  15. var
  16.   ADCSRA_: TADCSRA absolute $7A;
The benefit of using bitpacked records are not that clear to me ( I am not familiar with the use of bitpacked records):
Code: Pascal  [Select]
  1.   ADCSRA_.ADPS := 6; // prescaler = 64
  2.   ADCSRA_.ADIE := 0;
  3.   ADCSRA_.ADATE := 0;
  4.   ADCSRA_.ADEN := 1; // enable ADC
  5.  
  6.   // alternative
  7.   ADCSRA_.b := (6 shl ADPS0) or (1 shl ADEN); // prescaler = 64, enable ADC
vs.
Code: Pascal  [Select]
  1.   ADCSRA := (6 shl ADPS0) or (1 shl ADEN); // prescaler = 64, enable ADC
So in the end it seems like it gives the user a convenient way of seeing the bit names, but then some extra code is necessary to define the record structures. Also in many use cases one would want to configure all bits in a register in one instruction so then then one still needs the bit constants, am I understanding this correctly?

Thaddy

  • Hero Member
  • *****
  • Posts: 6899
Re: AVR microcontroller unit format
« Reply #9 on: November 04, 2017, 04:21:40 pm »
The whole point of my example was to show you that all those shifts are not necessary.... AND you can set the whole byte at once. In C speak: it is a union, not a record.... 8-)
Ada's daddy wrote this:"Fools are my theme, let satire be my song."

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 6540
Re: AVR microcontroller unit format
« Reply #10 on: November 04, 2017, 04:25:11 pm »
It depends on the architecture. The bit notation expresses more what you want, and it might be easier to transform to (atomic) read/modify/write bit instructions (if the architecture has some). Example 8-bit and 16-bit pic/dspic.

The shift option is better if you have an architecture that works with SET and CLR registers to erase bits (like PIC32 (general MIPS?)).

I like the bits notation more though. It is more readable than the one with shifts.

ccrause

  • Full Member
  • ***
  • Posts: 129
Re: AVR microcontroller unit format
« Reply #11 on: November 04, 2017, 04:41:29 pm »
The bit notation expresses more what you want
I'm not seeing the picture (or less poetically the code) here.  If I want to set a specific bit then I agree the above bitpacked record syntax makes it very clear what is meant.  What I don't understand is the use case where I want to set more than one bit in one statement, it seems to me that I need to revert to OR'ing bit shifts together to construct a byte which then is assigned to a byte variable.  I guess I'm asking what the alternative "bitpacked" way is to express the following in such a way that equivalent compact code gets generated:
Code: Pascal  [Select]
  1.   ADCSRA_.b := (6 shl ADPS0) or (1 shl ADEN);

Thaddy

  • Hero Member
  • *****
  • Posts: 6899
Re: AVR microcontroller unit format
« Reply #12 on: November 04, 2017, 04:59:18 pm »
packed Bit sets...
Then you can use include, exclude, basically all set operations on the register, which again makes it more readable.
But Marco is right: not every architecture is suited for that. (your AVR is, I think)
« Last Edit: November 04, 2017, 05:01:53 pm by Thaddy »
Ada's daddy wrote this:"Fools are my theme, let satire be my song."

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 6540
Re: AVR microcontroller unit format
« Reply #13 on: November 04, 2017, 05:07:00 pm »
The bit notation expresses more what you want
I'm not seeing the picture (or less poetically the code) here.  If I want to set a specific bit then I agree the above bitpacked record syntax makes it very clear what is meant.  What I don't understand is the use case where I want to set more than one bit in one statement, it seems to me that I need to revert to OR'ing bit shifts together to construct a byte which then is assigned to a byte variable. 

Yes. For arbitrary values. If most bits are boolean though, you might make it readable simply by having masks rather than shifts. The PIC32 (Microchip XC16) headers define it simply all. So both the bitpacked records, _POSITION for shift values and _MASK for the corresponding mask.  (1 shl xx)

The bits registers on all microchip systems have -bits suffixed, and the straight name is reserved for the as a whole integer (byte,word,dword, depending on arch) register.

ADCSRAbits.ADPS0=1; // not atomic on PIC32, atomic on PIC8/dspic
ADCSRA= yy;    // sets to 00010000 or whatever.
ADCSRASET = yy ;    // atomic set on PIC32, sets to xxx1xxxx
ADCSRACLR = yy;     // atomic clear on PIC32, sets to xxx0xxxx

where yy = 1 shl ADPS0_POSITION or yy = ADPS0_MASK would all work. Combinations too.

The SET and CLR options are an architecture feature. These registers have a different address.


Edson

  • Hero Member
  • *****
  • Posts: 938
Re: AVR microcontroller unit format
« Reply #14 on: November 04, 2017, 06:59:20 pm »
There was a similar discussion http://forum.lazarus.freepascal.org/index.php/topic,36374.0.html for Microchip compiler PicPas https://github.com/t-edson/PicPas
Just for information currently PicPas, support bit access, using fields for basic types:

Code: Pascal  [Select]
  1. TRISA := $FF;
  2. TRISA.bit1 := 1;
  3. STATUS.bit0 := STATUS.bit1;
  4. word_variable.high.bit7 := byte_variable.bit7;

But this a language constructions that the compiler implements and optimize for this architecture.
Lazarus 1.6 - FPC 3.0.0 - x86_64-win64 on  Windows 7

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus