www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - volatile struct definition for peripheral control registers

reply Josh Holtrop <jholtrop gmail.com> writes:
I'm working on an embedded project writing some device drivers to 
access peripheral control registers as MMIO addresses. Due to D's 
lack of a `volatile` keyword, I'm trying to come up with a way to 
define these peripheral control register structures in a way that 
doesn't require the user of the structure to have to remember to 
call `volatileStore()` or `volatileLoad()` for each register 
access separately and I want the interface to be as clean as 
possible.

In C I would have just declared the entire structure to be 
`volatile` since the entire thing lives in MMIO address range, 
and I suppose I'm trying to achieve something close to that...



Declare a `Volatile(T)` struct template:

```d
module myproj.volatile;

import core.volatile;

struct Volatile(T)
{
     T v;

     T get()
     {
         return volatileLoad(&v);
     }

     void opAssign(T v)
     {
         volatileStore(&this.v, v);
     }
}
```

Use as:

```d
import myproj.volatile;

struct S
{
     Volatile!uint reg;
     Volatile!uint reg2;
     struct
     {
         Volatile!uint subreg;
     };
}

void vtest()
{
     S * s = cast(S *)0xDEADBEEF;
     while (s.reg.get == 0)
     {
         s.subreg = 1;
     }
}

```

This works but requires that ugly .get call rather than just 
accessing it directly.



```d
module myproj.volatile;

public import core.volatile;

template volatile_field(string type, string name)
{
     mixin("private "~type~" v_"~name~";");
     mixin("public  property "~type~" "~name~"() { return 
volatileLoad(&v_"~name~"); }");
     mixin("public  property void "~name~"("~type~" v) { 
volatileStore(&v_"~name~", v); }");
}

template volatile_ubyte(string name)
{
     mixin volatile_field!("ubyte", name);
}

template volatile_ushort(string name)
{
     mixin volatile_field!("ushort", name);
}

template volatile_uint(string name)
{
     mixin volatile_field!("uint", name);
}

template volatile_ulong(string name)
{
     mixin volatile_field!("ulong", name);
}
```

```d
import myproj.volatile;

struct S
{
     mixin volatile_uint!"reg";
     mixin volatile_uint!"reg2";
     struct
     {
         mixin volatile_uint!"subreg";
     };
}

void vtest()
{
     S * s = cast(S *)0xDEADBEEF;
     while (s.reg == 0)
     {
         s.subreg = 1;
     }
}
```

This works and I like from the user side being able to access 
each field as if it was just the normal field. However, declaring 
the fields is a bit uglier syntax.




of the easier user syntax to read/write the fields.

I am wondering:


.get() call? I tried with opCast() but that requires the user to 
do an explicit cast which I felt was even worse than the .get

declaration cleaner than `mixin volatile_uint!"reg"`?
1. Is there any other way of achieving what I'm trying to achieve 
different from these two methods?
Dec 02 2024
parent reply =?UTF-8?Q?Ali_=C3=87ehreli?= <acehreli yahoo.com> writes:
On 12/2/24 11:10 AM, Josh Holtrop wrote:

 This works but requires that ugly .get call rather than just accessing
 it directly.
'alias this' may help: https://dlang.org/spec/struct.html#alias-this All you need to do is to add the following line to the Volatile template: alias get this; Ali
Dec 02 2024
parent reply Josh Holtrop <jholtrop gmail.com> writes:
On Monday, 2 December 2024 at 19:30:06 UTC, Ali Çehreli wrote:
 On 12/2/24 11:10 AM, Josh Holtrop wrote:

 This works but requires that ugly .get call rather than just
accessing
 it directly.
'alias this' may help: https://dlang.org/spec/struct.html#alias-this All you need to do is to add the following line to the Volatile template: alias get this; Ali
If I use `alias this` I don't know how I can get `volatileLoad()` to be called.
Dec 02 2024
next sibling parent Salih Dincer <salihdb hotmail.com> writes:
On Monday, 2 December 2024 at 20:27:23 UTC, Josh Holtrop wrote:
 
 If I use `alias this` I don't know how I can get 
 `volatileLoad()` to be called.
Please allow me to illustrate the point with a simple example so that you can visualize it: ```d struct FixedStr(size_t capacity) { char[capacity] data = ' ';  char[] buff;  size_t titleLength;  this(string _data) { buff = data[]; this ~= _data;    titleLength = _data.length; } alias opReturn this; property auto opReturn() inout    => data[titleLength..$]; // cuts off the title auto ref opOpAssign(string op)(string arr) if(op == "~") => buff.put(arr); // add in the data } import std.range : chunks, put; import std.string : format; import std.stdio : writeln; void main() { enum text = "sixtwoone";  auto test = FixedStr!22("Nums : ");  assert(is(typeof(test) == struct)); // not a string ------------^  foreach(num; text.chunks(3)) { test ~= num.format!"%s, "; } test.writeln; // six, two, one,  test[5..8].writeln; // two } ``` Let's have a 2-function struct as above. In fact, the functions do not have a feature that is open to direct use. There's not even an overload like `opReturn()`, it's my made :) To rotate a fixed string (char[]) by cropping it from the beginning (open it to the outside world) and to add data sequentially (via `std.range.put`) to its limit. But we also want to use it like the familiar string (e.g. line 2 output: "two"). And that's what we do easily with `alias ... this`. However, in this example, it is necessary to use property and inout; keep in mind... It's also a good idea to keep this in mind: https://dlang.org/changelog/2.100.0.html#alias_this_assignment SDB 79
Dec 02 2024
prev sibling parent reply =?UTF-8?Q?Ali_=C3=87ehreli?= <acehreli yahoo.com> writes:
On 12/2/24 12:27 PM, Josh Holtrop wrote:

   alias get this;
If I use `alias this` I don't know how I can get `volatileLoad()` to be called.
Note that alias this is used with your 'get' function, which still calls volatileLoad(). Ali
Dec 02 2024
parent reply Josh Holtrop <jholtrop gmail.com> writes:
On Monday, 2 December 2024 at 22:44:24 UTC, Ali Çehreli wrote:
 On 12/2/24 12:27 PM, Josh Holtrop wrote:

   alias get this;
If I use `alias this` I don't know how I can get
`volatileLoad()` to be
 called.
Note that alias this is used with your 'get' function, which still calls volatileLoad(). Ali
Ah... very nice. I completely missed that "alias this" could be used with a function. This works great and lets me use a nicer syntax to declare the fields. Thank you both. :)
Dec 02 2024
parent Josh Holtrop <jholtrop gmail.com> writes:
Just to follow up, I introduced the `Volatile` struct to my 
project and had to add a couple extra functions to make 
everything happy:

```d
module myproj.volatile;

import core.volatile;

struct Volatile(T)
{
     private T value;

     public  property T read()
     {
         return volatileLoad(&value);
     }
     alias read this;

     public void opAssign(T value)
     {
         volatileStore(&this.value, value);
     }

     public T opUnary(string s)()
     {
         T v = read();
         mixin(s ~ "v;");
         volatileStore(&this.value, v);
         return v;
     }

     public T opOpAssign(string s)(T rhs)
     {
         T v = read();
         mixin("v " ~ s ~ "= rhs;");
         volatileStore(&this.value, v);
         return v;
     }
}
```

So far it is working great!
Dec 02 2024