EmbeddedRelated.com
Forums
The 2026 Embedded Online Conference

Language feature selection

Started by Don Y March 5, 2017
On 11/03/17 13:58, Niklas Holsti wrote:
> On 17-03-10 23:32 , Paul Rubin wrote: >> Jacob Sparre Andersen <jacob@jacob-sparre.dk> writes: >>> The Ada variant is: >>> Some_Variable : Some_Type with Address => #16#dead_beef#; > > A small nit-pick: as the Ada type System.Address is "private" (that is, > opaque), it is necessary to apply the To_Address function to the integer > literal: > > ... with Address => To_Address (16#dead_beef#); > >> Do you need something like a volatile declaration? Does Ada have that? > > Yes. This can be specified by adding "Volatile" to the declaration: > > Some_Variable : Some_Type > with Volatile, Address => To_Address (16#dead_beef#); > > However, for I/O registers it is often desirable to ensure atomic reads > and writes by using the aspect "Atomic" instead of "Volatile". The > Atomic aspect implies the Volatile aspect, so it is not necessary to > specify both. >
Interesting - in C11 and C++11 there is an "atomic" qualifier, but it does not imply "volatile". It is fine for the compiler to optimise away an atomic read or write if it is not volatile - but /if/ it makes the access, it must be done atomically.
On 2017-03-12 7:15 PM, Tom Gardner wrote:
> That's why the necessity of having "language lawyers" to unravel the > arcane complexity of modern C/C++ and tools implies that modern C/C++ > is part of the problem as much as it is part of the solution.
Having been at quite a few WG-14 meetings. C/C++ issues are a combination of some level of compatibility to old tools and dealing with new emerging code generation requirements. Thrown in for good measure was C "Conventional wisdom" (Everyone knows that C does "this") It is hard to advance a language with that type of background. Many things are, "implementation defined", it is difficult to extend the language and many legacy items remained obscure. The open source folks for the most part didn't participate and created their own variation. Conforming test suites have been for the most part have been commercial products that are very useful in creating conforming compilers but the implementation defined and conventional wisdom aspects tends to impact the language portability. C isn't is particularly portable in many applications. w..
On 13/03/17 18:14, Walter Banks wrote:
> On 2017-03-12 7:15 PM, Tom Gardner wrote: >> That's why the necessity of having "language lawyers" to unravel the >> arcane complexity of modern C/C++ and tools implies that modern C/C++ >> is part of the problem as much as it is part of the solution. > > Having been at quite a few WG-14 meetings. C/C++ issues are a > combination of some level of compatibility to old tools and dealing with > new emerging code generation requirements. Thrown in for good measure > was C "Conventional wisdom" (Everyone knows that C does "this")
I've heard similar tales from other people; they seemed all too plausible then - and now.
> It is hard to advance a language with that type of background. Many > things are, "implementation defined", it is difficult to extend the > language and many legacy items remained obscure.
Yes indeed :( Castles built on sand.
> The open source folks for the most part didn't participate and created > their own variation. Conforming test suites have been for the most part > have been commercial products that are very useful in creating > conforming compilers but the implementation defined and conventional > wisdom aspects tends to impact the language portability.
Oh.... hell.
> C isn't is particularly portable in many applications.
Agreed. I'm surprised proponents still claim it is portable without adding the necessary caveats. IMNSHO C isn't going to scale well for future massively parallel systems. There will be less awkward languages, but even then it will be interesting to see whether the necessary primitives concepts will be part of the language (e.g. Transputer/Occam, XMOS/xC, Erlang) or library (e.g. Java and Doug Lea's concurrency library). To a great extent I don't care, so long as when I'm using it there are no unpleasant surprises in the tools. (There are enough of those in the applications!)
I have been reading this thread (which has wandered all over the place) because
I'm interested in language design for embedded systems.  One nice thing about
no longer being "gainfully employed" is that one has time to experiment.  I
have developed my own programming language for implementing operating systems
and drivers.  It is a front-end to LLVM and is pretty complete.

<shameless-plug>
Sources for the compiler (written in itself) at
 https://github.com/bagel99/esl
Sources for lots of drivers and network stuff at
 https://github.com/bagel99/blocks-os
</shameless-plug>

Here's how I approached the device driver question: at what hardware address is
the device?  First defining the device register layout:

    type UartRegs:
    {   dr:
	{   data: _uint8;		// .00-07
	    fe:    boolean;		// .08
	    pe:    boolean;		// .09
	    be:    boolean;		// .10
	    oe:    boolean;		// .11
	}: packed, lsb, bits(32), in, out;
     	lcrh:
	{   brk:   boolean;		// .00
	    par:   (NONE,ODD,_,EVEN);	// .01-02
	    stop:  (S1,S2);		// .03
	    fen:   boolean;		// .04
	    wlen:  (B5,B6,B7,B8);	// .05-06
	    sps:   boolean;		// .07
	}: packed, lsb, bits(32), out;
    ...
    };
    // packed - squeeze all the bits into the smallest width
    // lsb - the fields start at the least significant bit
    // bits[32] - extend the size to 32 bits with zeros
    // out - like volatile, but side effects only happen on writes
    // in - like volatile, but side effects only happon on reads

Then define the hardware address one of three ways:

    var uart0: UartRegs: external(0x4000_0000);
    // uart0 address is 0x40000000

    var uart1: UartRegs: external("UART1");
    // linker supplies address constant UART1=xxx

    const uart2: @UartRegs = 0x4000_2000;
    // define a constant pointer to the device

And assigning device bits without using masks:

    uart0.lcrh = {.brk=false, .par=NONE, .stop=S1, .wlen=B8 };
    // fields not specified take the zero value


Grant Edwards <invalid@invalid.invalid> writes:
> On 2017-03-10, Jacob Sparre Andersen <jacob@jacob-sparre.dk> wrote: >> Don Y <blockedofcourse@foo.invalid> writes:
>>> What *single* (non-traditional) language feature do you find most >>> valuable in developing code? (and, applicable language if unique to >>> *a* language or class of languages) >> >> In my daily work I would say strong typing (as done in Ada and >> SPARK). > > When you say "strong typing" you are also assuming "static typing"?
Yes. Strong, static typing. Jacob -- "Good enough for physics" -- Ridcully
Don Y <blockedofcourse@foo.invalid> writes:
> On 3/10/2017 12:03 PM, Jacob Sparre Andersen wrote: >> Don Y <blockedofcourse@foo.invalid> writes:
>>> What *single* (non-traditional) language feature do you find most >>> valuable in developing code? (and, applicable language if unique to >>> *a* language or class of languages) >> >> In my daily work I would say strong typing (as done in Ada and >> SPARK). >> >> When I'm doing embedded systems, being able to declare >> representations for a type (especially for enumerations) is very >> valuable. > > Yes. Though I want a mechanism by which I can "override" the type of > the object in this reference (even if that has to be done by invoking > a user-defined "cast operator").
Luckily that is a part of Ada (but not SPARK).
> I think it takes a bit of "experience" (time in the trenches) to truly > appreciate strong typing.
That is probably true.
> There are also often significant conflicts trying to make "general > purpose" languages that can address application development AND > operating system/driver development; the concepts that make sense in > one domain don't always directly translate to the other.
Yes. In Ada this is in part handled by "profiles", where a run-time can be designed only to support a specific subset of the language. This means that you can target even rather small 8-bit MCUs with an Ada compiler, if you don't need the full language. As an example, one of my colleagues recently built a controller for a toy lighthouse. The executable is around 500 bytes in total (without playing around with compiler options). His estimate is that he probably could do the same in 200-300 bytes of assembler, so there is a cost to using a high-level language. The good part is that there are also benefits.
>> What really made me say "wow" to a programming language feature was >> when I first read about having tasking built into a language (and not >> as an add-on library) in a book about Ada 83. > > The flip side of this is that it requires more of the run-time; a > greater set of constraints on the environment in which the code can > execute.
Yes.
> Increasingly, languages seem to want to bring their own sandbox to the > party -- in the guise of "helping" the developer. If you can't use > things "as is", then you're faced with a BIGGER problem as the > language designer may not have planned for the sandbox to be > dissociated from the code generator.
Yes. But since Ada is intended for systems programming there are limits to how closed the sandbox can be, even if you can see the run-time as a kind of sandbox. The big challenge with using limited run-times (all the way down to zero-footprint) is that it also limits which libraries you can reuse. Jacob -- "Even god needs a bus to get there."
On 17-03-13 22:17 , Brian G. Lucas wrote:
> I have been reading this thread (which has wandered all over the place) because > I'm interested in language design for embedded systems. One nice thing about > no longer being "gainfully employed" is that one has time to experiment. I > have developed my own programming language for implementing operating systems > and drivers. It is a front-end to LLVM and is pretty complete. > > <shameless-plug> > Sources for the compiler (written in itself) at > https://github.com/bagel99/esl > Sources for lots of drivers and network stuff at > https://github.com/bagel99/blocks-os > </shameless-plug>
Hm. In the spirit of language ecumenics and comparison, I translate your example to Ada, below. I try to use your identifiers as such, although Ada convention would use Title_Style capitalisation.
> Here's how I approached the device driver question: at what hardware address is > the device? First defining the device register layout: > > type UartRegs: > { dr: > { data: _uint8; // .00-07 > fe: boolean; // .08 > pe: boolean; // .09 > be: boolean; // .10 > oe: boolean; // .11 > }: packed, lsb, bits(32), in, out; > lcrh: > { brk: boolean; // .00 > par: (NONE,ODD,_,EVEN); // .01-02 > stop: (S1,S2); // .03 > fen: boolean; // .04 > wlen: (B5,B6,B7,B8); // .05-06 > sps: boolean; // .07 > }: packed, lsb, bits(32), out; > ... > };
-- The "dr" register type: type dr_t is record data : interfaces.unsigned_8; fe : boolean; pe : boolean; be : boolean; oe : boolean; end record with volatile, size => 32, bit_order => system.low_order_first; -- -- I would not usually make the _type_ volatile, because -- that could slow down operations on all variables of -- the type, even if they are not mapped to control -- registers. I would mark as volatile (or atomic) only -- the variables mapped to registers. -- Its structure on the bit level (I could also have said -- just "pack", but this is safer and more explicit): for dr_t use record data at 0 range 0 .. 7; fe at 0 range 8 .. 8; pe at 0 range 9 .. 9; be at 0 range 10 .. 10; oe at 0 range 11 .. 11; end record -- The "lchr" register type(s): type par_t is (NONE, ODD, EVEN); for par_t use (NONE => 0, ODD => 1, EVEN => 3); type stop_t is (S1, S2); -- Or it could be: -- type stop_t is range 1 .. 2; -- for compilers that support offset encoding. type wlen_t is (B5, B6, B7, B8); -- Or it could be: -- type wlen_t is range 5 .. 8; -- for compilers that support offset encoding. type lcrh_t is record brk : boolean; par : par_t; stop : stop_t; fen : boolean; wlen : wlen_t; sps : boolean; end record with volatile, size => 32, bit_order => system.low_order_first; for lchr_t use record brk at 0 range 0 .. 0; par at 0 range 1 .. 2; stop at 0 range 3 .. 3; fen at 0 range 4 .. 4; wlen at 0 range 5 .. 6; sps at 0 range 7 .. 7; end record; -- The combination: type UartRegs is record dr : dr_t; lchr : lchr_t; end record with pack; -- The "pack" aspect corresponds to your "packed". -- Of course we could also give a bit-level lay-out -- specification for UartRegs, as above.
> Then define the hardware address one of three ways: > > var uart0: UartRegs: external(0x4000_0000); > // uart0 address is 0x40000000
uart0 : UartRegs with address => to_address (16#4000_0000#);
> var uart1: UartRegs: external("UART1"); > // linker supplies address constant UART1=xxx
uart1 : UartRegs with link_name => "UART1";
> const uart2: @UartRegs = 0x4000_2000; > // define a constant pointer to the device
type UartRegs_ref is access all UartRegs; uart2 : constant UartRegs_ref := to_pointer (to_address (16#4000_2000#)); -- (I omitted some boilerplate here; the function -- "to_pointer" is a generic one, and must be -- instantiated for each necessary pointer type.)
> And assigning device bits without using masks: > > uart0.lcrh = {.brk=false, .par=NONE, .stop=S1, .wlen=B8 }; > // fields not specified take the zero value
uart0.lchr := ( brk => false, par => NONE, stop => S1, wlen => B8, others => false); -- All fields must be listed, or an "others" can be used. -- Bit of luck here that all the "others" fields are -- of the same type (boolean) which is required for the -- "others" to be legal. In summary, the only semantic difference is that the Ada "volatile" aspect cannot be separated into "in" and "out" parts, as in your language. Syntactically Ada needs more lines, because enumerations and sub-records must be defined as types. Does your language use "structural equivalence" for types? That is, if one register field is defined as the enumeration (A, B, C), and another register field is also defined as the enumeration (A, B, C), are these fields of the same type? -- Niklas Holsti Tidorum Ltd niklas holsti tidorum fi . @ .
On 3/13/2017 1:40 PM, Jacob Sparre Andersen wrote:
>> I think it takes a bit of "experience" (time in the trenches) to truly >> appreciate strong typing. > > That is probably true.
I suspect it is true of many aspects of "programming" that aren't apparent until you've been exposed to a fair bit of code (esp stuff authored by OTHERS!) In school ("university" for right-ponders), many of the issues that were brought up as "worthy goals" in language design seemed somewhat arbitrary (at that time). Over time, I've been chagrined at how many of those comments/lessons have gained new voice! "Ahhh.... *that's* what they meant!" It is also significant to consider the nature of the "coder" in the calculus. How high do you want to raise the bar -- and to what gain?
>> There are also often significant conflicts trying to make "general >> purpose" languages that can address application development AND >> operating system/driver development; the concepts that make sense in >> one domain don't always directly translate to the other. > > Yes. In Ada this is in part handled by "profiles", where a run-time can > be designed only to support a specific subset of the language. This > means that you can target even rather small 8-bit MCUs with an Ada > compiler, if you don't need the full language.
But, isn't this effectively not-including certain "modules"? Or, will the parser then note that you've attempted to use something that is not supported?
> As an example, one of my colleagues recently built a controller for a > toy lighthouse. The executable is around 500 bytes in total (without > playing around with compiler options). His estimate is that he probably > could do the same in 200-300 bytes of assembler, so there is a cost to > using a high-level language. The good part is that there are also > benefits. > >>> What really made me say "wow" to a programming language feature was >>> when I first read about having tasking built into a language (and not >>> as an add-on library) in a book about Ada 83. >> >> The flip side of this is that it requires more of the run-time; a >> greater set of constraints on the environment in which the code can >> execute. > > Yes. > >> Increasingly, languages seem to want to bring their own sandbox to the >> party -- in the guise of "helping" the developer. If you can't use >> things "as is", then you're faced with a BIGGER problem as the >> language designer may not have planned for the sandbox to be >> dissociated from the code generator. > > Yes. But since Ada is intended for systems programming there are limits > to how closed the sandbox can be, even if you can see the run-time as a > kind of sandbox. > > The big challenge with using limited run-times (all the way down to > zero-footprint) is that it also limits which libraries you can reuse.
Correct -- unless you can impose a discipline on the library designers. I prefer leaner languages because they give me more flexibility in how I address certain aspects (problems?) brought about by the language's model without dictating how things *must* be done. E.g., it was easy to make reentrant "standard libraries" (without changing the calling semantics) for C decades ago because there was no significant runtime "integration" of those libraries with the "execution environment".
On Sun, 12 Mar 2017 18:08:52 -0700 (PDT), jim.brakefield@ieee.org
wrote:

>On Saturday, March 11, 2017 at 4:54:54 PM UTC-6, upsid...@downunder.com wrote: >> On Sat, 11 Mar 2017 14:20:27 -0800 (PST), jim.brakefield@ieee.org >> wrote: >> >> >On Saturday, March 11, 2017 at 7:42:00 AM UTC-6, Niklas Holsti wrote: >> >> On 17-03-11 04:46 , jim.brakefield@ieee.org wrote: >> >> > On Sunday, March 5, 2017 at 8:43:28 PM UTC-6, Don Y wrote: >> >> >> A quick/informal/UNSCIENTIFIC poll: >> >> >> >> >> >> What *single* (non-traditional) language feature do you find most >> >> >> valuable in developing code? (and, applicable language if unique >> >> >> to *a* language or class of languages) >> >> > >> >> > A plug for array operators: as in Numpy, IDL/PV~wave, APL and Julia. >> >> > That is: array and vector operators baked into the language. >> >> >> >> In a language that allows operator overloading, programmers can define >> >> their own array operators. >> >> >> >> > I've found that programming at this level yields shorter programs with >> >> > less debugging: You windup making your data structures and algorithms >> >> > use the fewest operators possible/practical. >> >> >> >> I agree that array operators are useful, but only for relatively simple >> >> cases such as the basic arithmetic operations on arrays. However, taking >> >> it to the APL extreme with complex vector/matrix restructurings (outer >> >> products, lamination, ...) can create code that is hard for others to >> >> understand. >> >> >> >> > There is a theoretical vantage point for this style of programming >> >> > (which I call "programming in the large" as opposed to "programming >> >> > in the small"): >> >> >> >> You may call it that, but I hope you know that most people understand >> >> these large/small terms differently; see >> >> https://en.wikipedia.org/wiki/Programming_in_the_large_and_programming_in_the_small. >> >> >> >> >> >> -- >> >> Niklas Holsti >> >> Tidorum Ltd >> >> niklas holsti tidorum fi >> >> . @ . >> > >> >]> You may call it that, but I hope you know that most people understand >> >]> these large/small terms differently; see >> > >> >Was not aware of this definition. Tend to consider this type of "programming in the large" as solving an organizational problem and an architectural problem. >> > >> >Another form of programming in the large is characterized by provisioning a complete 64-bit computer: e.g. one with a complete 64-bit address space. >> > >> >]> I agree that array operators are useful, but only for relatively simple >> >]> cases such as the basic arithmetic operations on arrays. However, taking >> >]> it to the APL extreme with complex vector/matrix restructurings (outer >> >]> products, lamination, ...) can create code that is hard for others to >> >]> understand. >> > >> >My experience was with scientific programming, so yes this argument has some validity. Am still convinced that it is a useful exercise: Push low level details down into the operators and data structures, consider the various ways of doing this so that the high level operators (that need to be written) emerge. >> > >> >Also consider programming well with "array" operators to require greater experience, know-how and good judgement than doing low level coding. >> > >> >Jim Brakefield >> >> FORTRAN had complex number support from the beginning. >> >> The array support has been added more recently. It seems that array >> support was added ,more recently.. IMHO Fortran is still a viable >> option for solving mathematical problems after recent updates. > >]> FORTRAN had complex number support from the beginning. >Fortran IV did not have a complex number type?
FORTRAN IV definitively had COMPLEX data type.
> >Had expected Fortran 90 to compete well against C/C++. >What happened? > >Julia uses 1 origin subscripts, same as Fortran. > It can be difficult to convert a Fortran program to C/C++/etc: >e.g, preserving correctness while converting from >1 origin to 0 origin subscripts.
The opposite conversion is trivial from stupid old C/C++ to Fortran 77 and later. integer A (0:3, 0:4, 0:5) thus you can (optionally) define the lower li,it for each array dimension. If I understood correctly, recent Fortran versions also allow your own operators like defining an operator such as .dot. (dot product) and then you could write something like Complex A, B C = A .dot. B Regarding Julia, is it possible to overload various operators to a single Unicode character such as the center dot ? While this might be useful for some common operators, the new Fortran .operator. syntax might be more versatile and readable.
On Sat, 11 Mar 2017 12:53:46 -0700, Don Y
<blockedofcourse@foo.invalid> wrote:

>(is there any reason why a TV *can't* do its own speech recognition without >farming that task out to some remote server? really??)
Well, yes. The fairly high* quality speech recognition we see today depends on access to a large and evolving** database of things people have actually said and written. The modern speech recognition systems are yet another example that brute force is a much more viable approach to many problems than people used to think. *FSVO "high" **IOW, "Watch Game of Thrones" would be well understood now simply because that would be, in whole or in part, a common query. A few years ago, before the series hit, it would have been much shakier.
The 2026 Embedded Online Conference