Memory mapped hardware registers

Do you have a question? Post it now! No Registration Necessary

Translate This Thread From English to

Threaded View

I'm developping board support code for an embedded m68k system. The actual
MCU is a MC68331 that has a CPU32 core (more or less a 68020). This MCU
define hardware registers at specific memory locations. My toolchain is
composed of GCC 3.4.3 and binutils 2.15.

It possible to define a variable that would be placed at specific location
using gcc or linker script hacks ? It would avoid using a pointer to access
a register. I don't care if this is non portable and/or gcc/binutils

For example, the CPU32 has the SIMCR 16-bits register placed at 0xFFFA00.

typedef unsigned short word;

/* SIMCR (0xYFFA00): SIM Configuration */
typedef struct _SIMCR {
    word exoff     : 1;
    word frzsw     : 1;
    word frzbm     : 1;
    word _UNUSED_1 : 1;
    word slven     : 1;
    word _UNUSED_2 : 1;
    word shen      : 2;
    word supv      : 1;
    word mm        : 1;
    word _UNUSED_3 : 2;
    word iarb      : 4;

How can I place a single SIMCR structure at 0xFFFA00 ? I guess some linker
magic is involved ? Also, the actual variable must be left uninitialized;
you don't want init code to overwrite the register with zeros as this will
crash the MCU.


Simon Paradis
simon _dot_ paradis _at_ usherbrooke _dot_ ca

Re: Memory mapped hardware registers
I suppose you are using µCLinux. As there is no memory management unit,
you can access the hardware as well in Kernel as in user mode.

Why not just load a pointer with the appropriate address:

  struct _SIMCR *psimcr

  psimcr = (struct _SIMCR *) 0xYFFA00;
  psimcr -> exoff = 0x11;

BTW.: You are the first person I meet using the GNU compiler with an
68K. I might be intending to do that (for a non-Linux project). So I
have some questions. What debugger are you using ? Does this work OK ?


Re: Memory mapped hardware registers
I got it after messing around. I defined all 8 or 16 bit registers as
bitfields like that:

/* CSPAR1 (0xYFFA46): Chip Select Pin Assignement Register 1 */
typedef struct _CSPAR1 {
    WORD _UNUSED_1 : 6;
    WORD cspa14    : 2;
    WORD cspa13    : 2;
    WORD cspa12    : 2;
    WORD cspa11    : 2;
    WORD cspa10    : 2;

Then I built a monster struct having the *exact* layout as written in
Motorola documentation. The struct has attribute packed: it's a gcc
extension that disable all struct member alignement. The _uXX bytes are
unused bytes in the SIM area. They exist to make sure registers are properly
placed in memory.

typedef struct _SIM_REGISTERS
    /*           15..............8     7...............0 */
    /* ------------------------------------------------- */
    /* YFFA00 */           SIMCR   simcr;
    /* YFFA02 */           WORD    simtr;
    /* YFFA04 */           SYNCR   syncr;
    /* YFFA06 */ BYTE    _u1;          RSR     rsr;
    /* YFFA08 */           WORD    simtre;
    /* YFFA0A */ BYTE    _u2;          BYTE    _u3;
    /* YFFA0C */ BYTE    _u4;          BYTE    _u5;
    /* YFFA0E */ BYTE    _u6;          BYTE    _u7;
    /* YFFA10 */ BYTE    _u8;          PORTE0  porte0;
    /* YFFA12 */ BYTE    _u9;          PORTE1  porte1;
    /* YFFA14 */ BYTE    _u10;         DDRE    ddre;

    ... many more registers snipped ..

} __attribute__ ((__packed__)) SIM_REGISTERS;

extern volatile SIM_REGISTERS sim;

Then in sim_registers.c, you allocate a single global SIM_REGISTER:

    volatile SIM_REGISTERS sim;

The variable is volatile because those registers can change anytime abd some
bits always reads as 0 even if set them to 1. This 'sim' thing results in a
so-called COMMON symbol (ie uninitialized symbol).

In the sections statement of the linker script, you add:

    /* SIM (System Integration Module) Registers. This maps the
     * sim variable from bsp/registers/sim_registers.c at the
     * MCU SIM memory area.
    .sim_registers 0xFFFA00 :

That section must be before bss in the linker script because otherwise, the
sim variable could end up in .bss because typically .bss includes all COMMON

And there you go. The global sim structure match Motorola's doc. The C code
can access
registers directly with no pointers like that.

#include <sim_registers.h>

int main(void)
    return sim.simcr.iarb;

If you look at the resulting S-Record (code begins at 0x3000)


you see no data/code loaded at FFFA00 which is what you want: registers do
not get overwritten. This is similar to .bss sections: the data there takes
no space in the executable but gets zeroed-out in RAM by startup code. Here,
I just don't zero out the register area. If you see lots of
S10FFFA00xxxxxxx, you didn't get it right, the CPU will crash when loading
the code.

To verify you didn't screw up, you can run 'nm --size' on sim_registers.o or
look at the linker produced map file. The sim variable should be the same
size as the SIM area (128 bytes). The map file will show the placement:

.sim_registers  0x00fffa00       0x80
 COMMON         0x00fffa00       0x80
                                  0x0 (size before relaxing)
                0x00fffa00                sim

Looking at asm code generated by the compiler from the test C code above,
you see the direct access (the sim.simcr.iarb field is in the 4 low order
bits of the second byte of the sim area). So the asm code does what's
expected. Apparently, m68k gcc aligns bit fields from MSB to LSB.

 link.w %a6,#0
 move.b sim+1,%d0
 moveq #15,%d1
 and.l %d1,%d0
 unlk %a6

Now that's quite non-portable code :-) You can do these tricks for stuff
like interrupt vectors and every memory mapped registers.

Simon Paradis
simon _dot_ paradis _at_ usherbrooke _dot_ ca

Quoted text here. Click to load it

Re: Memory mapped hardware registers
Quoted text here. Click to load it

Right. Nonetheless reverse ingeneering the ways of the compiler is
dangerous. The position of bit fields in the variables is implementation
depending (and maybe undefined at all) and so they can change with a new
version of GCC or (even with the next compiler run). Though that
supposedly is very unlikely.


Re: Memory mapped hardware registers
Quoted text here. Click to load it

IMHO it would be more straight forward to place the sim in it's own section

     volatile SIM_REGISTERS sim __attribute__((section(".sim_reg")));

should do this.

Now in the linker script you can allocate this section .sim_reg at the
appropriate address without any problems with COMMON.


Re: Memory mapped hardware registers
Yes that's true.

Also, I was putting all those register in a big struct with packed
attribute. That was a bad idea since I was already taking care of alignement
myself. When you specify packed attribute; the compiler generates two 8 bit
load when loading a 16bit int presumably because you can't load a word from
odd memory addresses.

I removed the packed attribute because I know no such invalid load would
occur and now the compiler load words in a single pass.

It's quite informative to read the asm output sometimes.

Quoted text here. Click to load it

Re: Memory mapped hardware registers
On Sun, 23 Jan 2005 21:32:09 +0100, Michael Schnell

Quoted text here. Click to load it


I have used the 68K GNU compiler on a project where we previously used
the Crosscode compiler. The 68K GNU Compiler generates MUCH better
code than the Crosscode compiler. You need to get a version of the gcc
toolset which includes the patches made by Peter Barada. This includes
a patch for the support of the interrupt attribute with the 68K, which
enables the writing of interrupt handlers in C. Normally these patches
refer to the Coldfire toolset, but this is the same toolset as for the
m68K - just specify the actual CPU used. e.g -m68060 for the 68060.
I use insight or gdb+DDD as the debugger. I modified an existing
gdb-stub for our hardware, and can source level debug over the
serial port. The actual code runs in RAM (copied at startup) so
breakpoints etc. are not a problem.

Hope this helps
   Anton Erasmus

Re: Memory mapped hardware registers
Thanks !


Site Timeline