* * *

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

Thaddy

  • Hero Member
  • *****
  • Posts: 6911
Re: AVR microcontroller unit format
« Reply #15 on: November 04, 2017, 09:01:11 pm »
I prefer the "standard" way, where no cryptic errors could occur because you try to assign an integer value to a bitpacked type.

A bit <sic> late but: can you give an example?....... ((That would be either wrong or the error is correct. I can see in the future....)

Assigning an integer to a byte?... yeah, right! (that rhymes).
Ada's daddy wrote this:"Fools are my theme, let satire be my song."

avra

  • Hero Member
  • *****
  • Posts: 1366
    • Additional info
Re: AVR microcontroller unit format
« Reply #16 on: November 04, 2017, 09:33:39 pm »
Code: Pascal  [Select]
  1.   ADCSRA_.b := (6 shl ADPS0) or (1 shl ADEN);
When I saw this my first thought was that I should better stick with GCC. I can live with such code since it might at the end be more efficient, but I do not like it. I also prefer plain ADCSRA over c-ish ADCSRA_ whenever possible. Please don't kill me for being honest and so subjective on this.

I prefer bitpacked variant records with named bits, with or without type helpers, and bitsets. Something like Thaddy and Marco have already mentioned. I know it's a lot of work that needs to be repeated for each CPU (and there are many), but benefit is more pascalish syntax. Take a look at this discussion:
http://forum.lazarus.freepascal.org/index.php/topic,36374.msg242412.html#msg242412
http://forum.lazarus.freepascal.org/index.php/topic,36374.msg242438.html#msg242438
http://forum.lazarus.freepascal.org/index.php/topic,36374.msg242464.html#msg242464
http://forum.lazarus.freepascal.org/index.php/topic,36374.msg242293.html#msg242293
http://forum.lazarus.freepascal.org/index.php/topic,36374.msg242628.html#msg242628
http://forum.lazarus.freepascal.org/index.php/topic,36374.msg242713.html#msg242713

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?
Yes. Let the user decide between comfort and efficiency based on his use case.
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

ccrause

  • Full Member
  • ***
  • Posts: 129
Re: AVR microcontroller unit format
« Reply #17 on: November 04, 2017, 09:37:25 pm »
Thanks for all the ideas Thaddy, Marco and Edson.  If I cater for all the variations I've picked up (since there isn't one correct way to do this) then the following is required for one register with its associated bit definitions:
Code: Pascal  [Select]
  1. type
  2.   TBitField = 0..1;  // move to shared location
  3.  
  4.   TADCSRA_set = set of (ADPS0, ADPS1, ADPS2, ADIE, ADIF, ADATE, ADSC, ADEN);
  5.  
  6.   TADCSRA = bitpacked record
  7.   case boolean of
  8.   false:(
  9.       ADPS: 0..7;
  10.       ADIE,
  11.       ADIF,
  12.       ADATE,
  13.       ADSC,
  14.       ADEN: TBitField);
  15.   true:
  16.     (asByte: byte);
  17.   end;
  18.  
  19. var
  20.   ADCSRA: byte absolute $7A;
  21.   ADCSRA_bits: TADCSRA absolute ADCSRA;
  22.   ADCSRA_set: TADCSRA_set absolute ADCSRA;
  23.  
  24. const
  25.   ADPS0_mask = 1;
  26.   ADPS1_mask = 2;
  27.   ADPS2_mask = 4;
  28.   ADIE_mask = 8;
  29.   ADIF_mask = 16;
  30.   ADATE_mask = 32;
  31.   ADSC_mask = 64;
  32.   ADEN_mask = 128;
This would allow all of the following variations:
Code: Pascal  [Select]
  1.   // A - Bit fields
  2.   ADCSRA_bits.ADPS := 6;
  3.   ADCSRA_bits.ADEN := 1;
  4.  
  5.   // B - Bit sets
  6.   ADCSRA_set := [ADPS1, ADPS2, ADEN];
  7.  
  8.   // C - Variant record as byte
  9.   ADCSRA_.asByte := ADPS1_mask or ADPS2_mask or ADEN_mask;
  10.  
  11.   // D - Straight value assignment to byte
  12.   ADCSRA := ADPS1_mask or ADPS2_mask or ADEN_mask;
So variation A is very clear, but generates 8 instructions and should only be used when manipulating or inspecting one bit at a time (in my opinion).  Variations B, C & D compile to exactly the same two instructions, hence it comes down to personal preference.
Now the variant record seems superfluous if a regular byte variable is also declared, so I would drop the asByte bit of the bitpacked record.

Does this appraoch seem excessively verbose?

kupferstecher

  • Full Member
  • ***
  • Posts: 218
Re: AVR microcontroller unit format
« Reply #18 on: November 04, 2017, 09:53:32 pm »
Thaddy, the variant record is what I was missing.

So in your example a register ABC would be accessible as follows?
 ABC.Register:= 123;
or
 ABC.Bit.Bit1:= true;

Quote from: ccrause link=topic=38809.msg264889#msg264889
What I don't understand is the use case where I want to set more than one bit in one statement, [..]
Code: Pascal  [Select]
  1.   ADCSRA_.b := (6 shl ADPS0) or (1 shl ADEN);
I think this is only possible if the bits are defined also as constants and doing the shift operation (or the mask). Right?
Would it even be possible to use the same names in the bitpacked record and in the constants?

The shift option is better if you have an architecture that works with SET and CLR registers to erase bits (like PIC32 (general MIPS?)).
The AVR has commands for setting and clearing a single bit in the IO-registers.

ADCSRAbits.ADPS0=1; // not atomic on PIC32, atomic on PIC8/dspic
ADCSRA= yy;    // sets to 00010000 or whatever.
So there are two names for the same register (ADCSRAbits and ADCSRA) and two names for the same bit (ADPS0 and ADPS0_POSITION)? I think thats quite confusing (although the naming seems ok)...

ccrause

  • Full Member
  • ***
  • Posts: 129
Re: AVR microcontroller unit format
« Reply #19 on: November 04, 2017, 09:55:44 pm »
Yes. Let the user decide between comfort and efficiency based on his use case.
I started this thread because I was trying to keep the unit structure as simple as possible so that it could be easy to follow, but it appears as if there is much interest in having a few alternatives.  Since including the currently discussed options into the units will not increase compiled code size I guess that it is acceptable to include a whole bunch of declarations.
Quote
I also prefer plain ADCSRA over c-ish ADCSRA_
No problem, I used the underscore to distinguish between the existing register definition and the alternative declaration in the old example.  See my later example for alternative names.  Of course if I include declarations for different approaches then I need to generate unique names somehow.

ccrause

  • Full Member
  • ***
  • Posts: 129
Re: AVR microcontroller unit format
« Reply #20 on: November 04, 2017, 10:06:19 pm »
Would it even be possible to use the same names in the bitpacked record and in the constants?
...
So there are two names for the same register (ADCSRAbits and ADCSRA) and two names for the same bit (ADPS0 and ADPS0_POSITION)? I think thats quite confusing (although the naming seems ok)...
Indeed,I was also a bit lost for names, but Marco's suggestion of rather using the mask instead of the bit location makes sense in the conventional use case so I changed the regular bit namess to e.g. ADPS0_mask and set the value to 1 shl ADPS0.  No more name clashes and one doesn't need to calculate a mask on the fly for assignment to a register.

avra

  • Hero Member
  • *****
  • Posts: 1366
    • Additional info
Re: AVR microcontroller unit format
« Reply #21 on: November 05, 2017, 01:51:44 am »
Now the variant record seems superfluous if a regular byte variable is also declared, so I would drop the asByte bit of the bitpacked record.
I would keep AsByte and add AsSet. If you define something like this:

Code: Pascal  [Select]
  1. {$MODESWITCH TYPEHELPERS}
  2.  
  3. type
  4.   TBitField = 0..1;
  5.  
  6.   TAvrByteHelper = type helper for Byte
  7.     function  GetBit(Index: Byte): Boolean;
  8.     procedure SetBit(Index: Byte; Value: Boolean);
  9.     property  Bit[Index: Byte]: Boolean read GetBit write SetBit;
  10.   end;
  11.  
  12.   TADCSRA_bits = (ADPS0, ADPS1, ADPS2, ADIE, ADIF, ADATE, ADSC, ADEN);
  13.   TADCSRA_set  = set of TADCSRA_bits;
  14.  
  15.   TADCSRA = bitpacked record
  16.   case byte of
  17.   0:(
  18.       ADPS: 0..7;
  19.       ADIE,
  20.       ADIF,
  21.       ADATE,
  22.       ADSC,
  23.       ADEN: TBitField);
  24.   1:(
  25.       AsByte: byte);
  26.   2:(
  27.       AsSet: TADCSRA_set);
  28.   end;
  29.  
  30.   TMCUCSR_bits = (PORF, EXTRF, BORF, WDRF, JTRF, ISC2 = 6 {skip dummy bit 5}, JTD);
  31.   TMCUCSR_set  = set of TMCUCSR_bits;
  32.  
  33.   TMCUCSR = bitpacked record
  34.   case byte of
  35.   0:(
  36.       PORF,
  37.       EXTRF,
  38.       BORF,
  39.       WDRF,
  40.       JTRF,
  41.       DummyBit5,
  42.       ISC2,
  43.       JTD: TBitField);
  44.   1:(
  45.       AsByte: byte);
  46.   2:(
  47.       AsSet: TMCUCSR_set);
  48.   end;
  49.  
  50.  
  51. const
  52.   ADCSRA_ADDR = $7A;
  53.   MCUCSR_ADDR = $54;
  54.  
  55. var
  56.   ADCSRA_: byte           absolute ADCSRA_ADDR;
  57.   ADCSRA: TADCSRA         absolute ADCSRA_ADDR;
  58.   MCUCSR_: byte           absolute MCUCSR_ADDR;
  59.   MCUCSR: TMCUCSR         absolute MCUCSR_ADDR;    
  60.  
  61. implementation
  62.  
  63. function TAvrByteHelper.GetBit(Index: Byte): Boolean;
  64. begin
  65.   Result := ((Self shr Index) and $01) <> 0;
  66. end;
  67.  
  68. procedure TAvrByteHelper.SetBit(Index: Byte; Value: Boolean);
  69. begin
  70.   if Value then
  71.     Self := Self or ($01 shl Index)
  72.   else
  73.     Self := Self and not ($01 shl Index);
  74. end;  
  75.  

then you could have all these:

Code: Pascal  [Select]
  1.    ADCSRA.ADPS := %110;
  2.    ADCSRA.ADEN := 1;
  3.    MCUCSR_.Bit[3] := true;
  4.    MCUCSR_.Bit[Byte(PORF)] := true;
  5.    MCUCSR.AsByte.Bit[7] := true;
  6.    MCUCSR.ISC2 := 1;
  7.  
  8.    MCUCSR.AsSet := [WDRF, JTD, JTRF] + [JTD, ISC2];
  9.    ADCSRA.AsSet := [ADPS1, ADEN];
  10.    ADCSRA.AsSet := ADCSRA.AsSet + [ADIF, ADATE];
  11.    Include(ADCSRA.AsSet, ADPS0);
  12.    Exclude(ADCSRA.AsSet, ADPS0);
  13.  
  14.    ADCSRA.AsByte := (1 shl byte(ADPS1)) or (1 shl byte(ADPS2));
  15.    ADCSRA_ := (1 shl byte(ADPS1)) or (1 shl byte(ADPS2));

I like just few of these possibilities, but they are all there for consumation ;-)
« Last Edit: November 05, 2017, 02:04:26 am by avra »
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 6542
Re: AVR microcontroller unit format
« Reply #22 on: November 05, 2017, 04:51:31 am »
I like Crause's syntax more,  but would kill the _ so  ADCSRAbits and  ADCSRAset.  But it also depends on how AVR does this in typical sources. My conventions were mostly PIC.
« Last Edit: November 05, 2017, 04:53:09 am by marcov »

ccrause

  • Full Member
  • ***
  • Posts: 129
Re: AVR microcontroller unit format
« Reply #23 on: November 05, 2017, 07:55:49 am »
Thanks avra, I guess your approach could be called the all-in-one record which nicely puts all variants in one record definition.  My perception of the downside is that the user code then is a bit more verbose since one has to explicitly reference the variant in the record.
But it also depends on how AVR does this in typical sources.
So I thought of grabbing a typical bit of GCC style C code, but then stumbled onto this thread which started off as a demonstration of macro gymnastics but then contains this interesting bit ::):
Quote from: david.prentice
There are many different schemes for describing special_bits in special function registers. Personally, I like the bitfield_structure used by PIC (and others).

It is a historic thing. I suspect that the early compilers were not clever enough. Hence we are stuck with the bit_position scheme.
Since we are dealing with a "smart" compiler, it seems more natural then to use bit masks and eliminate all the (1 shl bitname) fluff.  I will need to get used to these alternatives, I've been using avr-gcc for a number of years now and obviously need to relearn some Pascal alternatives.

kupferstecher

  • Full Member
  • ***
  • Posts: 218
Re: AVR microcontroller unit format
« Reply #24 on: November 05, 2017, 11:45:55 am »
The advantage of the bitpacked record that the IDE can list the bit names is significant, but I still don't like the multiple names for the same thing.
I came up with an alternative approach. It doesn't mean its my favourite, but I want to put it to discussion.

The idea is to use the "standard" way and additionally provide the bit-values through class constants. The bits then can be accessed through the class name ("T" + register name), so the IDE can list the according bits. Usage example:
Code: Pascal  [Select]
  1. UCSRA:= (1 shl UDRE);        //"traditional" way
  2. UCSRA:= (1 shl TUCSRA.UDRE); //using class constant

and with type helpers:
Code: Pascal  [Select]
  1. UCSRA.SetBit(TUCSRA.UDRE, true);
  2. UCSRA.SetBit(UDRE, true);

Advantage: Its backwards compatible and the bit names are always the same, so the type of the register can be typed to get the bits-list and deleteded again for readability.

EDIT: here the implementation of the class constants:
Code: Pascal  [Select]
  1. Type TUCSRA = object
  2.   const RXC = 7; // USART Receive Complete
  3.   const TXC = 6; // USART Transmitt Complete
  4.   const UDRE = 5; // USART Data Register Empty
  5.   const FE = 4; // Framing Error
  6.   const DOR = 3; // Data overRun
  7.   const UPE = 2; // Parity Error
  8.   const U2X = 1; // Double the USART transmission speed
  9.   const MPCM = 0; // Multi-processor Communication Mode
  10. end;
« Last Edit: November 05, 2017, 11:49:33 am by kupferstecher »

Thaddy

  • Hero Member
  • *****
  • Posts: 6911
Re: AVR microcontroller unit format
« Reply #25 on: November 05, 2017, 11:56:27 am »
You can even do this and work by index:
Code: Pascal  [Select]
  1. Type TUCSRA = bitpacked object
  2.   const RXC = 7; // USART Receive Complete
  3.   const TXC = 6; // USART Transmitt Complete
  4.   const UDRE = 5; // USART Data Register Empty
  5.   const FE = 4; // Framing Error
  6.   const DOR = 3; // Data overRun
  7.   const UPE = 2; // Parity Error
  8.   const U2X = 1; // Double the USART transmission speed
  9.   const MPCM = 0; // Multi-processor Communication Mode
  10. end;

Yes, objects can be bitpacked too  :D
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 #26 on: November 05, 2017, 12:19:11 pm »
Thaddy, how to use it then?

Thaddy

  • Hero Member
  • *****
  • Posts: 6911
Re: AVR microcontroller unit format
« Reply #27 on: November 05, 2017, 04:15:23 pm »
Like this, but now I integrated and improved on AVRA's idea that was very good in my opinion..
I believe this is the simplest and shortest  approach:
[EDIT]
Code: Pascal  [Select]
  1. program testme3;
  2. {$mode objfpc}{$bitpacking on}{$packset 1}{$packenum 1}{$modeswitch typehelpers}
  3. {.$define ADCSRA_REAL} //define for real processor
  4. {$I-}
  5. type
  6.   TADCSRA_pins = (ADPS0, ADPS1, ADPS2, ADIE, ADIF, ADATE, ADSC, ADEN);
  7.   TADCSRA_set  = bitpacked set of TADCSRA_pins;
  8.   TADCSRA_range = ADPS0..ADEN;
  9.  
  10.   TADCSRA_sethelper = type helper for TADCSRA_set
  11.     procedure PinHi(const pin:TADCSRA_pins);inline;
  12.     procedure PinLo(const pin:TADCSRA_pins);inline;
  13.     procedure Toggle(const pin:TADCSRA_pins);inline;
  14.     function IsOn(const pin:TADCSRA_pins):Boolean;inline;
  15.     function IsOff(const pin:TADCSRA_pins):Boolean;inline;
  16.     function AsByte:Byte;inline;
  17.     function AsString:string;
  18.     function AsBin:string;
  19.   end;
  20.  
  21.   procedure TADCSRA_sethelper.PinHi(const pin:TADCSRA_pins);inline;
  22.   begin
  23.     include(self,pin);
  24.   end;
  25.  
  26.   procedure TADCSRA_sethelper.PinLo(const pin:TADCSRA_pins);inline;
  27.   begin
  28.     exclude(self,pin);
  29.   end;
  30.  
  31.   procedure TADCSRA_sethelper.Toggle(const pin:TADCSRA_pins);inline;
  32.   begin
  33.     //if pin in self then exclude(self,pin) else include(self,pin);
  34.     self := self >< [pin] //same as above but more fun for noobs <sadistic grin>
  35.   end;
  36.  
  37.   function TADCSRA_sethelper.IsOn(const pin:TADCSRA_pins):Boolean;inline;
  38.   begin
  39.     Result := pin in self;
  40.   end;
  41.  
  42.   function TADCSRA_sethelper.IsOff(const pin:TADCSRA_pins):Boolean;inline;
  43.   begin
  44.     Result := not (pin in self);
  45.   end;
  46.  
  47.   function TADCSRA_sethelper.AsByte:byte;inline;  
  48.   begin
  49.     result := byte(self);
  50.   end;
  51.  
  52.   function BitAsString(pin:TADCSRA_pins):string;
  53.   begin
  54.     writestr(result,pin:6);
  55.   end;
  56.  
  57.   function TADCSRA_sethelper.AsString:string;
  58.   var
  59.     t:TADCSRA_pins;
  60.   begin
  61.     result :='';
  62.     for t in self do
  63.       result+=BitAsString(t);
  64.   end;  
  65.  
  66.   function TADCSRA_sethelper.AsBin:string;
  67.   begin
  68.      Result :=Binstr(byte(self),8);
  69.   end;
  70. /////////////////////////////////////////////////
  71. // example code
  72. /////////////////////////////////////////////////
  73. const
  74.  TADCSRA = $7A;
  75.  
  76. var
  77.    ADCSRA:TADCSRA_set {$ifdef ADCSRA_REAL}absolute TADCSRA{$else}=[]{$endif};
  78. begin
  79.    writeln(ADCSRA.IsOn(ADPS0));
  80.    writeln(ADCSRA.AsString);
  81.    ADCSRA.PinHi(ADPS0);
  82.    writeln(ADCSRA.IsOff(ADPS0));
  83.    writeln(ADCSRA.AsString);
  84.    ADCSRA.PinHi(ADPS1);
  85.    writeln(ADCSRA.AsString);
  86.    ADCSRA.PinHi(ADPS2);
  87.    writeln(ADCSRA.AsString);
  88.    ADCSRA.PinHi(ADIE);
  89.    writeln(ADCSRA.AsString);
  90.    writeln(ADCSRA.AsBin);
  91.    ADCSRA.PinHi(ADIF);
  92.    writeln(ADCSRA.AsString);
  93.    ADCSRA.PinHi(ADATE);
  94.    writeln(ADCSRA.AsString);
  95.    ADCSRA.PinHi(ADSC);
  96.    writeln(ADCSRA.AsString);
  97.    ADCSRA.PinHi(ADEN);
  98.    writeln(ADCSRA.AsBin);
  99.    writeln(ADCSRA.AsString);
  100.    ADCSRA.PinLo(ADPS0);
  101.    writeln(ADCSRA.AsString);
  102.    ADCSRA.PinLo(ADPS1);
  103.    writeln(ADCSRA.AsString);
  104.    writeln(ADCSRA.AsBin);
  105.    ADCSRA.PinLo(ADPS2);
  106.    writeln(ADCSRA.AsString);
  107.    ADCSRA.PinLo(ADIE);
  108.    writeln(ADCSRA.AsString);
  109.    ADCSRA.PinLo(ADIF);
  110.    writeln(ADCSRA.AsString);
  111.    ADCSRA.PinLo(ADATE);
  112.    writeln(ADCSRA.AsString);
  113.    ADCSRA.PinLo(ADSC);
  114.    writeln(ADCSRA.AsString);
  115.    ADCSRA.PinLo(ADEN);
  116.    writeln(ADCSRA.AsString);
  117.    writeln(ADCSRA.AsBin);
  118.    ADCSRA.Toggle(ADIE);
  119.    writeln(ADCSRA.IsOn(ADIE));
  120.    ADCSRA.Toggle(ADIE);
  121.    writeln(ADCSRA.IsOn(ADIE));
  122.    ADCSRA.Toggle(ADIE);
  123.    writeln(ADCSRA.IsOn(ADIE));
  124.    ADCSRA.Toggle(ADIE);
  125.    writeln(ADCSRA.IsOn(ADIE));
  126. end.

This reduces the whole shebang to just the bit set.
Really easy to implement for the rest too. See this as a template to show the techniques.

The code should be really efficient...
« Last Edit: November 05, 2017, 09:31:21 pm by Thaddy »
Ada's daddy wrote this:"Fools are my theme, let satire be my song."

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 6542
Re: AVR microcontroller unit format
« Reply #28 on: November 05, 2017, 04:47:46 pm »

  procedure TADCSRA_sethelper.Toggle(pin:TADCSRA_pins);inline;
  begin
    if pin in self then exclude(self,pin) else include(self,pin);
  end;

Btw many 8 and 16-bitters have a dedicated atomic bit-toggle instruction, usually triggered by some intrinsic like __builtintoggle(LATA,1) for PIC, doing it in one instruction, and not 4. (though on the faster models you get a cycle penalty for the r/m/w operation)

Thaddy

  • Hero Member
  • *****
  • Posts: 6911
Re: AVR microcontroller unit format
« Reply #29 on: November 05, 2017, 05:44:06 pm »
@Marco
 what about:
Code: Pascal  [Select]
  1.   procedure TADCSRA_sethelper.Toggle(pin:TADCSRA_pins);inline;
  2.   begin
  3.     //if pin in self then exclude(self,pin) else include(self,pin);
  4.     self := self >< [pin]  // this is hardcore Pascal.... don't use unless you understand it when you read it....
  5.   end;

I have to check what it generates....but usually that should be a maximum of two operands for symmetric difference.
In this case I used it as the toggle.
[edit]
on arm that does this in both cases, given ADIE:
Code: Text  [Select]
  1.         tst     r0,#8
  2.         movne   r2,#1
  3.         moveq   r2,#0

Which means the compiler sees the symmetric difference as if/then/else at least on arm.

(Aside: finally a case again where i could use symmetric difference in reasonably normal code.. :P :D 8-) O:-)).
I have no real hardware available atm. Maybe someone can test what it really does?

On arm it should generate eor (which it does in a simpler example), a single instruction over three registers.

« Last Edit: November 05, 2017, 08:13:07 pm by Thaddy »
Ada's daddy wrote this:"Fools are my theme, let satire be my song."

 

Recent

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