I'm sorry for opening such a topic; I've heard it's not liked a lot, but I think it might be necessary.

I'm not asking for a 'volatile' keyword, but rather to find out what the right thing to use is. After reading a few different threads related to microcontrollers, I started wondering how to program the following in D:

1: System level drivers, which writes directly to hardware registers (any architecture, PC/i386 [Linux, Windows, others], Atari ST, IBM BladeCenter, etc.)
2: Interrupts that needs to share variables.

1) is what we basically need on microcontrollers. If it's possible to write a driver in D, which has no problems with accessing hardware, then it should be possible to do it for any microcontroller as well.

2) shared variables could be used for interrupts, but what happens if the TLS is disabled; will shared variables work ? -Interrupts are not threads.

Regarding (1), because marking a variable 'shared' is not enough (it allows instructions to be moved around), Johannes already made a volatileLoad and volatileStore, which will be usable for microcontrollers, though for convenience, it requires writing additional code. -But this solution ... I do not know if it would work, when writing a driver for Debian running on a i586 platform or PowerMac G3 for instance.

If variable 'alice' and variable 'bob' are both shared, and reading from 'bob', then writing to 'alice'; would instructions be moved around, so reading from 'bob' could actually occur after writing to 'alice' ?

Imagine that in the microcontroller world, there's a *bunch* of hardware registers. The best thing a microcontroller knows, is to read/write hardware registers; often only a few bits at a time - but definitely also 8-, 16- and 32-bit values.

Thus you will most likely not be able to find a single piece of source-code for a microcontroller, where it does not access hardware. This means: Hardware is used often, and reading/writing hardware registers should not take up a lot of space in the source code.


For those, who are unfamiliar with such code, here's a silly example in C on how it might look. The following code changes the clock-frequency of the microcontroller, so it runs at 100 MHz:

---8<-----8<-----8<-----
uint32_t setupCoreClock()
{
        /* By default, we run at 100MHz, however,
         * our PLL clock frequency is 300 MHz.
         * We'd like to run 400MHz! */

LPC_SC->SCS = SCS_MainOscillatorEnable; /* enable Main Oscillator */
        if(LPC_SC->SCS & SCS_MainOscillatorEnable)
        {
                while(0 == (LPC_SC->SCS & SCS_MainOscillatorStatus)){}
        }

        LPC_SC->CCLKCFG = CCLK_Divider - 1;

        LPC_SC->PCLKSEL0 = 0;
        LPC_SC->PCLKSEL1 = 0;

        LPC_SC->CLKSRCSEL = PLL0_ClockSource;

        LPC_SC->PLL0CFG = PLL0_Configuration;
        LPC_SC->PLL0FEED = 0xaa;
        LPC_SC->PLL0FEED = 0x55;

        LPC_SC->PLL0CON = PLL_Enable;
        LPC_SC->PLL0FEED = 0xaa;
        LPC_SC->PLL0FEED = 0x55;
        while(!(LPC_SC->PLL0STAT & PLL0_Lock)){} /* Wait for PLOCK0 */

        LPC_SC->PLL0CON = PLL_Enable | PLL_Connect;
        LPC_SC->PLL0FEED = 0xaa;
        LPC_SC->PLL0FEED = 0x55;
while((LPC_SC->PLL0STAT & (PLL0_EnabledFlag | PLL0_ConnectedFlag)) != (PLL0_EnabledFlag | PLL0_ConnectedFlag)){} /* Wait until connected */

        return(F_CCLK);
}
--->8----->8----->8-----

Everything you see above, which starts with 'LPC_' are hardware register accesses. Not counting the curly braces, nearly every line of the code access hardware registers.

Things like LPC_SC are pointers to structures, which contains a load of volatile keywords; some prohibit reading, some prohibit writing, some allow both, some allow neither.

Most code for ARM Cortex-M looks like the above, because the silicon vendors just modifies the stock (template) source code, which they receive from ARM. Thus there's some convenience, but not a lot. Bitfields are not provided by ARM, so it propagates down to the vendor, which do not supply bitfields either. However, byte/halfword/word access (8/16/32-bit) are sometimes provided through anonymous unions, which is very helpful.

So back to what originally triggered this post; this is what the question is actually about: If writing a driver for <platform X>, how is reading/writing hardware registers usually done in D ?

Reply via email to