April 25, 2015
Am Sat, 25 Apr 2015 11:38:45 +0000
schrieb "Martin Nowak" <code@dawg.eu>:

> On Saturday, 25 April 2015 at 05:07:04 UTC, Jens Bauer wrote:
> > I hope to find a good way to use import for microcontroller libraries, so it'll be easy for everyone. I'm thinking about something like ...
> >
> > import mcu.stm32f439.all
> 
> I think that belongs in the makefile/dub.json as
> -version=STM32F439.
> Then you could simply import mcu.gpio or mcu.spi.

We need a better / clever generic approach to solve 'board configuration' issues. Here version blocks work but you might also want to specify other configuration options (clock speed if static, ...) and we don't have -D to define constants.

I think we could use 'reverse' imports but I'm not sure if it's a horrible hack or a great idea:



When compiling library code:

// board.di (Only 'runtime' config variables)
-------------------------------------------------
module board.config;
__gshared const int configA;
__gshared const int configB;
-------------------------------------------------

//library code: lib.d
-------------------------------------------------
import board.config;

void doFoo() // Function => runtime value
{
    auto a = configA;
}

void templateFoo()() // Template => ctfe value
{
    auto b = ctfeConfigA; //This value is not in .di => error if
    //template is instantiated in the library
}
-------------------------------------------------

gdc lib.d -Ipath/for/board.di -o lib.o


User code:
// board.d ('runtime' + 'ctfe' config variables)
-------------------------------------------------
module board.config;
__gshared const int configA = 42;
__gshared const int configB = 42;
enum ctfeConfigA = 42;
-------------------------------------------------

gdc lib.o board.d
April 25, 2015
On Saturday, 25 April 2015 at 11:38:46 UTC, Martin Nowak wrote:
> On Saturday, 25 April 2015 at 05:07:04 UTC, Jens Bauer wrote:
>> I hope to find a good way to use import for microcontroller libraries, so it'll be easy for everyone. I'm thinking about something like ...
>>
>> import mcu.stm32f439.all
>
> I think that belongs in the makefile/dub.json as -version=STM32F439.
> Then you could simply import mcu.gpio or mcu.spi.

I've had the same thoughts myself, but I do not like forcing the user to modify the Makefile.
(I currently have a -complicated- Makefile system, which does not require the Makefile to be modified)
I don't want to require the user to edit the linker-script either (though we're not really speaking about that; my point is that I want it to be simple and easy for D-newcomers; whether D is their first or 2nd language).
It could be a good solution to use version(Ethernet), version(SPI5), version(LTDC) and version(SAI2) ... to enable interrupt handlers in the modules.

-But as I have not really worked with modules yet, there are still a lot of unknowns - I'm only guessing here.
April 25, 2015
On Saturday, 25 April 2015 at 11:50:19 UTC, Martin Nowak wrote:
> On Saturday, 25 April 2015 at 07:31:45 UTC, Jens Bauer wrote:
> Static constructors are possible if you strip down ModuleInfo (requires compiler hacking).
> You should care about that stuff last. It's way more important to make things work without dynamic memory allocation first.

I agree and thanks for reminding me; I don't want to lose my focus. ;)

>> That means 'new' and 'delete' / 'malloc' and 'free' must be able to handle multiple RAM locations (because there's also external SRAM and external SDRAM).
>
> IIRC then the C/C++ malloc would simply can your sbrk

Heh, I didn't know about sbrk before I almost finished writing mine.
This resulted in that mine can be used side-by-side with the standard malloc.
(I named mine m_malloc and m_free).

> implementation, so it only supports a single heap, which should be the external if available.

In C++, it's possible to override new and delete on a per-class basis, I'd expect that this is possible in D as well, is this correct ?
-If it is, then I think there's no problem, because then the class can handle special cases, when it knows it's working with the DMA.

Allocation could default to SDRAM if SDRAM exists; then external SRAM, then CCMRAM and finally fallback to Local on-chip SRAM.
(Note: any external RAM needs to be initialized by the microcontroller's firmware, before the external RAM can be used, I do not know if that could be a problem with the linker-generated static constructors).
April 25, 2015
On Saturday, 25 April 2015 at 17:11:22 UTC, Johannes Pfau wrote:
> Am Sat, 25 Apr 2015 11:38:45 +0000
> schrieb "Martin Nowak" <code@dawg.eu>:
>
>> On Saturday, 25 April 2015 at 05:07:04 UTC, Jens Bauer wrote:
>> >
>> > import mcu.stm32f439.all
>> 
>> I think that belongs in the makefile/dub.json as -version=STM32F439.
>> Then you could simply import mcu.gpio or mcu.spi.
>
> We need a better / clever generic approach to solve 'board
> configuration' issues. Here version blocks work but you might also
> want to specify other configuration options (clock speed if
> static, ...) and we don't have -D to define constants.

I actually attempted to use version on the exception vector list, but as you cannot do this ...

@isr_vector VectorFunc[] g_pfnVectors = [
	cast(VectorFunc)&_stack,
	&Reset_Handler,
version(USB){
	&USBActivity_IRQHandler,
} version(CAN){
	&CANActivity_IRQHandler,
}
	];

... I ditched the idea. There might be a good way for doing this, so that the number of startup files can be reduced, and perhaps allow for increased convenience.

One thing that's tedious, is when the vendor replaces a single handler in a later device implementation.
I can't seem to make a chain of weak aliases, eg..

SPI0_Interrupt() defaults to defaultExceptionHandler()
SPI_Interrupt() defaults to SPI0_Interrupt()

implementing SPI_Interrupt() would then automatically use this in the exception vector, but if also implementing SPI0_Interrupt(), thene SPI0_Interrupt() would have higher priority and SPI_Interrupt() should be discarded.
If neither SPI0_Interrupt() nor SPI_Interrupt() is implemented, then they would fallback to defaultExceptionHandler().

-I could not get such an 'alias-chain' working; perhaps it's just because I got confused, perhaps it's because the compiler does not support it. - I don't know yet. ;)

If an alias-chain was possible, I think the startup files could become fairly pretty, and there wouldn't be problems with using the wrong handler name on chips where the vendors decided to rename the handler functions within one chip-family (I won't tell anyone who you are, NXP - I promise!).
April 25, 2015
On Saturday, 25 April 2015 at 17:04:18 UTC, Jens Bauer wrote:

> I think volatileLoad and volatileStore are intended for this (please correct me if my understanding is wrong).
>
Yes. Actually I am not sure whether they already exist in gdc or not.

Try to write for example
regs.cmdr |= 0x20
with these functions and guess how many users will move to another language.

April 25, 2015
On Saturday, 25 April 2015 at 17:58:59 UTC, Timo Sintonen wrote:
> On Saturday, 25 April 2015 at 17:04:18 UTC, Jens Bauer wrote:
>
>> I think volatileLoad and volatileStore are intended for this (please correct me if my understanding is wrong).
>>
> Yes. Actually I am not sure whether they already exist in gdc or not.
>
> Try to write for example
> regs.cmdr |= 0x20
> with these functions and guess how many users will move to another language.

Ah, I get the point now. :)

I don't want to start another volatile discussion, but to me it seems an attribute would not be a bad idea.
-And for completeness... read-only, write-only, read/write and perhaps even 'prohibited access'. I recall that something was marked prohibited in some way in a library once; I forgot how they did it, though.
April 25, 2015
Am Sat, 25 Apr 2015 18:31:45 +0000
schrieb "Jens Bauer" <doctor@who.no>:

> On Saturday, 25 April 2015 at 17:58:59 UTC, Timo Sintonen wrote:
> > On Saturday, 25 April 2015 at 17:04:18 UTC, Jens Bauer wrote:
> >
> >> I think volatileLoad and volatileStore are intended for this (please correct me if my understanding is wrong).
> >>
> > Yes. Actually I am not sure whether they already exist in gdc or not.
> >
> > Try to write for example
> > regs.cmdr |= 0x20
> > with these functions and guess how many users will move to
> > another language.
> 
> Ah, I get the point now. :)
> 
> I don't want to start another volatile discussion, but to me it
> seems an attribute would not be a bad idea.
> -And for completeness... read-only, write-only, read/write and
> perhaps even 'prohibited access'. I recall that something was
> marked prohibited in some way in a library once; I forgot how
> they did it, though.

volatileLoad is not in gdc yet. I've written the code some months ago but I need to update it and then it needs to be reviewed.

Always using volatileLoad/Store is annoying. The solution is to write a wrapper:


Volatile!T: http://dpaste.dzfl.pl/dd7fa4c3d42b

Volatile!size_t value;
value += 1;
assert(value == 1);



Register wrapper: http://dpaste.dzfl.pl/3e6314714541 Register definition:

enum Level : ubyte
{
    low = 0,
    high = 1
}

enum fields = [
    Field("PIN0", 0, 0, true, "Level", Access.readWrite),
    Field("PIN1", 1, 1, true, "Level", Access.readWrite),
    Field("TEST", 2, 4, false, "ubyte", Access.readWrite)];

mixin(generateRegisterType!ubyte("PORT", fields));
pragma(address, 0x25) extern __gshared PORTRegister PORTB;

Usage:
    auto b = PORTB.load();
    PORTB.toggle!"PIN0";
    PORTB.PIN0 = Level.low;
    writeln(PORTB.PIN0);
    PORTB.TEST = 0b000;


The remaining problem is performance. (With optimization the generated code is as good as equivalent C code. However, we need to avoid size overhead: e.g. struct initializers and the opX functions shouldn't generate functions in the executable, though tha can be fixed with the linker)
April 25, 2015
On Saturday, 25 April 2015 at 19:33:05 UTC, Johannes Pfau wrote:
> Am Sat, 25 Apr 2015 18:31:45 +0000
> schrieb "Jens Bauer" <doctor@who.no>:
>> 
>> I don't want to start another volatile discussion, but to me it seems an attribute would not be a bad idea.
>> -And for completeness... read-only, write-only, read/write and perhaps even 'prohibited access'. I recall that something was marked prohibited in some way in a library once; I forgot how they did it, though.
>
> volatileLoad is not in gdc yet. I've written the code some months ago
> but I need to update it and then it needs to be reviewed.

I might be able to do a few tests, but I don't feel qualified for code reviewing at this time. ;)

> Always using volatileLoad/Store is annoying. The solution is to write a wrapper:

I was hoping for something in that direction.

> Volatile!size_t value;

{snip}

> Usage:
>     auto b = PORTB.load();
>     PORTB.toggle!"PIN0";
>     PORTB.PIN0 = Level.low;
>     writeln(PORTB.PIN0);
>     PORTB.TEST = 0b000;

It is much more convenient to use it this way, yes; I think it will cover most needs.
Regarding performance: If a real high time-critical speed is needed, people would probably use assembly code anyway. ;)
April 26, 2015
On Saturday, 25 April 2015 at 19:33:05 UTC, Johannes Pfau wrote:
>
> volatileLoad is not in gdc yet. I've written the code some months ago
> but I need to update it and then it needs to be reviewed.

It's officially in 2.067.0 for anyone who's wondering.

>
> Volatile!T: http://dpaste.dzfl.pl/dd7fa4c3d42b
>
> Volatile!size_t value;
> value += 1;
> assert(value == 1);
>
>
>
> Register wrapper: http://dpaste.dzfl.pl/3e6314714541
> Register definition:
>
> enum Level : ubyte
> {
>     low = 0,
>     high = 1
> }
>
> enum fields = [
>     Field("PIN0", 0, 0, true, "Level", Access.readWrite),
>     Field("PIN1", 1, 1, true, "Level", Access.readWrite),
>     Field("TEST", 2, 4, false, "ubyte", Access.readWrite)];
>
> mixin(generateRegisterType!ubyte("PORT", fields));
> pragma(address, 0x25) extern __gshared PORTRegister PORTB;
>
> Usage:
>     auto b = PORTB.load();
>     PORTB.toggle!"PIN0";
>     PORTB.PIN0 = Level.low;
>     writeln(PORTB.PIN0);
>     PORTB.TEST = 0b000;
>

That's some nice code! and really leveraging D to great effect.  I know that Volatile!(T) took some engineering to get right, so it would be nice to have that as an "official" type IMO.

>
> The remaining problem is performance. (With optimization the generated
> code is as good as equivalent C code. However, we need to avoid size
> overhead: e.g. struct initializers and the opX functions shouldn't
> generate functions in the executable, though tha can be fixed with the
> linker)

I'm not sure I follow how the linker can solve this.  Could you elaborate?

Mike
April 26, 2015
On Saturday, 25 April 2015 at 16:32:50 UTC, Timo Sintonen wrote:
> On Saturday, 25 April 2015 at 11:56:55 UTC, Martin Nowak wrote:
>
> I have not yet needed anything from libc/phobos in my programs.

I think there's a few gems that can be cherry-picked out of Phobos, especially for metaprogramming:

std.traits
std.typecons
std.typetuple

There are also a couple things in these libraries that might be useful for programming in general:

std.conv
std.string
std.array

Mike