July 15, 2014
Johannes Pfau:

> And how do you think peek/poke are easier to implement in this regard?

Let's ask Walter.


> Well as long as those priorities are clearly communicated.

Most of the design of D is not geared toward embedded computing. It could be adapted and improved for those purposes, but surely they aren't its main target.

Bye,
bearophile
July 15, 2014
On 7/15/2014 2:17 PM, Johannes Pfau wrote:
> Well as long as those priorities are clearly communicated. If you tell
> me 'we don't care about embedded programming' then I'll shut up and move
> back to C.

Rest assured we definitely do care about embedded systems programming.


> But 'D is a systems programming language for low level tasks' and 'we
> don't want to introduce a type qualifier for low level programming, but
> @nogc is just fine' don't go together. This leaves all contributors and
> devs hoping to see D on embedded systems in uncertainty.

It isn't about whether we want to introduce a type qualifier for low level programming or not. It is about what is the best solution for MMIO for low level programming.

Note that D already supports inp() and outp() as compiler intrinsics, adding peek() and poke() will complement them nicely:

  http://dlang.org/phobos/core_bitop.html#.inp
July 15, 2014
On 7/15/2014 2:28 PM, bearophile wrote:
> Johannes Pfau:
>
>> And how do you think peek/poke are easier to implement in this regard?
>
> Let's ask Walter.

peek/poke should be implemented as compiler intrinsics that are then mapped onto whatever the backend does best for 'volatile' accesses.

July 15, 2014
On 7/15/2014 2:22 PM, Johannes Pfau wrote:
> The DIP may seem complicated cause it considers overloading,
> transitivity and similar stuff. In practice almost nobody will use
> these features. But if I had not specified them Walters first question
> would have been "How does it interact with overloading" ;-)

You're right that almost nobody would use those features. But they'd have to be defined, documented, implemented and tested, like they are in C++. It negatively impacts users in that D becomes a larger, more complex language with next to no corresponding benefit.

(Just grep for 'shared' in the D source code and see how pervasive it is.)

This is why I propose that peek/poke have a much better cost/benefit ratio.

July 15, 2014
On Tuesday, 15 July 2014 at 20:45:20 UTC, Johannes Pfau wrote:
> Am Tue, 15 Jul 2014 12:48:21 -0700
> schrieb Walter Bright <newshound2@digitalmars.com>:
>> 
>> 3. if you really hate peek/poke calls appearing in the code, you can
>> use UFCS to make them look like variables
>
> But you cant do REGISTER.peek() |= 0b1;
>

You can create a function which returns a struct that wraps your desired read-modify-write semantics, e.g.:

auto myvolatile(size_t* addr)
{
  static struct VolatileWrapper
  {
     size_t* addr;
     // read-modify-write
     auto opOpAssign(string op)(size_t rhs) { /*...*/ }
     // write
     auto opAssign(size_t rhs) { addr.poke(rhs); return this; }
     // read
     auto rval() { return addr.peek(); }
     alias rval this;
  }
  return VolatileWrapper(addr);
}

REGISTER.myvolatile() |= 0b1;

Taking it further, I think you could implement something that looked like this:

enum : VolatileWrapper
{
  TIMER0 = VolatileWrapper(REGISTER),
  // etc
}

Don't get me wrong, I'm in favor of volatile, it's just that that counter-argument isn't extremely convincing.
July 15, 2014
On Tuesday, 15 July 2014 at 21:28:24 UTC, Walter Bright wrote:
> I don't think this is the right argument. It isn't about embedded systems should be acknowledged with a qualifier. I see it as about how often does MMIO logic appear in an actual embedded program? In the embedded systems I've written, it only appears in a handful of places. It is not pervasive.
>
> BTW, immutable data in D is designed to be placed in ROM for embedded systems. So there's already a qualifier :-)

I just wrote some embedded code for a Bluetooth LE
microcontroller. The MCU in question has no less than 25 separate
peripheral systems, each with its own set of registers, which use
MMIO for configuration and operation. Just one of the header
files provided by the chip vendor for this SoC defines 524
volatile variables. I'm sure that this complexity is barely
anything compared to, say, the SoCs found in smart phones.

Of course, one confines MMIO accesses to one layer of the
program. Even so, that layer can be very broad given the sheer
amount of stuff it has to touch - potentially many thousands of
lines of code. With the code leverage that D has, I would not be
surprised if this made up 10% or more of an embedded program's
size.

Also, there are many ways to misconfigure these peripherals and
produce an effect physically similar to a compiler-eliminated
access. A single omission of peek() or poke() could take hours to
track down if one is unfortunate. If one is having trouble, for
example, with the bluetooth radio, it could be due to:

  - An illegal configuration amongst the many registers touching
BTLE
  - A legal configuration which is not accepted by the other
device (e.g. causes a timeout)
  - The chip is damaged
  - The other device is damaged
  - The other device has a bug
  - The compiler eliminated access to the BTLE control registers

Finally, the compiler could have eliminated access to other
registers that had a side-effect on the BTLE code (for example,
eliminating configuration of the memory protection unit). These
would probably be incredibly pernicious because the true error is
not in the code that has broken, and could feasibly be in any of
a large number of systems. I certainly would not have a lot of
fun scanning thousands of lines of code for a mistaken omission
of peek() or poke().

So in my opinion, and without prescribing the exact solution,
volatility is an important enough concept in embedded to merit a
standard solution that can be mechanically verified.
July 16, 2014
On 7/15/2014 4:52 PM, Steve Sobel wrote:
> So in my opinion, and without prescribing the exact solution,
> volatility is an important enough concept in embedded to merit a
> standard solution that can be mechanically verified.

There is another way. The peek() and poke() functions are primitive. You can wrap a "pointer to volatile" in its own type, and in that type, control access to the pointer so it cannot be used outside of peek/poke.

For example, off the top of my head:

struct VolatilePointerToUint {
  private:
    size_t ptr;
  public:
    this(size_t ptr) { this.ptr = ptr; }
    uint read() { return peek(cast(uint*)ptr); }
    void write(uint value) { poke(cast(uint*)ptr, value); }
}

You'd probably wish to flesh this out a bit more, but it's the general idea. It's a zero cost abstraction. D has very capable abilities to create types that are restricted versions of other types - this should be explored and exhausted before considering language extensions.
July 16, 2014
On Tuesday, 15 July 2014 at 21:57:56 UTC, Walter Bright wrote:

> Rest assured we definitely do care about embedded systems programming.

That's most encouraging to read! D has a lot of potential for embedded programming, we just need to be thrown a few bones.

> It isn't about whether we want to introduce a type qualifier for low level programming or not. It is about what is the best solution for MMIO for low level programming.

Exactly, and I think a type qualifier is the right tool for the job.  I was all in favor of peek/poke after our brief conversation at DConf, but DIP62 sold me on a type qualifier... see below.

> Note that D already supports inp() and outp() as compiler intrinsics, adding peek() and poke() will complement them nicely:

OT:  Isn't inp/outp and Intel-only thing, though?

DIP62 addressed why peek/poke are not the right tool for the job in the "why a type qualifier" section.  In summary, one would never want to access volatile memory with non-volatile semantics, and a type qualifier is the only proposal I've seen that enforces that:  peek/poke intrinsics do not.

The Volatile!(T) workaround is proof that peek/poke is not the right tool.  The Volatile!(T) workaround is a jerry-rig on top of peek/poke to provide what a 'volatile' type qualifier would provide, and is necessary because the language lacks the proper tool.

From what I've read in this thread, I suspect you favor peek/poke due to the complexity it introduces into the implementation. If a 'volatile' type qualifier was a trivial implementation, and did not introduce complexity into implementation, would your position be different?

I and the others in favor of DIP62 will likely concede the following:
* Workarounds exist
* The workarounds are not overly burdensome
* 'volatile' as defined by DIP62 would not be a pervasive feature of the language with high leverage.  It is only used in the low level hardware abstractions (i.e. systems programming)

...but that's beside the point.

I remember watching a talk you gave where you compared code with an airplane, and how when you get it right, it just looks like it wants to fly.  I don't know how many people got that, but I sure did.

peek/poke delegates responsibility to the programmer, as at every usage s/he must make sure they employ it properly to beat their code into flying.  Volatile!(T) is an inelegant lever to make peek/poke more less prone to disobedience.

A type qualifier is an elegant tool for modeling one's hardware because at every usage, it already knows how, and wants to fly.


Mike
July 16, 2014
I've been jawboning about peek/poke for years - finally decided to implement it.

  https://issues.dlang.org/show_bug.cgi?id=13138
July 16, 2014
On 7/15/2014 6:03 PM, Mike wrote:
> OT:  Isn't inp/outp and Intel-only thing, though?

Of course. But it's still used on embedded systems.


> DIP62 addressed why peek/poke are not the right tool for the job in the "why a
> type qualifier" section.  In summary, one would never want to access volatile
> memory with non-volatile semantics, and a type qualifier is the only proposal
> I've seen that enforces that:  peek/poke intrinsics do not.

See my reply to Steve Sobel.


>  From what I've read in this thread, I suspect you favor peek/poke due to the
> complexity it introduces into the implementation. If a 'volatile' type qualifier
> was a trivial implementation, and did not introduce complexity into
> implementation, would your position be different?

Unfortunately, it isn't trivial, so your point is moot. Even if it were, "trivial" is not a good reason to put a feature in a language. The language must not become a kitchen sink grab-bag of trivia. Go has demonstrated there is considerable value in having a small set of orthogonal features instead.


> I remember watching a talk you gave where you compared code with an airplane,
> and how when you get it right, it just looks like it wants to fly.  I don't know
> how many people got that, but I sure did.

You're the only one that likes my airplane metaphors :-)


> A type qualifier is an elegant tool for modeling one's hardware because at every
> usage, it already knows how, and wants to fly.

Type qualifiers are not elegant. Type qualifiers are fairly burdensome to the user.