as anyone who did it knows, describing memory mapped peripherals is tedious. as far as I know, qemu support ends at defining memory regions and implementing the read/write callbacks for all accesses in this region.
while implementing several Cortex-M devices (currently STM32F103, F107, F407, F429 are functional for blinky projects), I experimented with some solutions to automate this. the model that I stopped at uses a hierarchy of objects that goes down to register bitfield level (peripheral/register/bitfield). the central object in this hierarchy is the peripheral register, which holds a value (up to 64-bits), and its content is automatically retrieved/updated by read/writes, based on accurate vendor bitmasks. in addition, each register may have two user actions defined, a pre-read action, to load the register with an external value, and a post-write action, to forward the value to an external device, or to implement register interdependencies. these actions are obviously user functions, registered as callbacks. the other objects, the peripheral and bitfield, are more or less helpers. the bitfield is a simple object, that stores a mask and a shift value, to retrieve a bitfield value; it is always a register child. the peripheral is more or less a container of registers; it implements the logic to forward read/writes to the appropriate register. registers and bitfields are defined with arrays of Info structures; special functions traverse these structures and create the objects. an example of a read only 32-bit word register: { .desc = "Port input data register (GPIOx_IDR)", .name = "idr", .offset_bytes = 0x08, .reset_value = 0x00000000, .reset_mask = 0xFFFF0000, .access_flags = PERIPHERAL_REGISTER_32BITS_WORD, .readable_bits = 0x0000FFFF, .rw_mode = REGISTER_RW_MODE_READ, }, an example of a register with bitfields defined: { .desc = "RCC PLL configuration register (RCC_PLLCFGR)", .name = "pllcfgr", .offset_bytes = 0x04, .reset_value = 0x24003010, .bitfields = (RegisterBitfieldInfo[] ) { { .name = "pllm", .desc = "PLL division factor", .first_bit = 0, .width_bits = 6, }, { .name = "plln", .desc = "PLL multiplication factor", .first_bit = 6, .width_bits = 9, }, { .name = "pllp", .desc = "Main PLL (PLL) division factor", .first_bit = 16, .width_bits = 2, }, { .name = "pllsrc", .desc = "Main PLL (PLL) clock source", .first_bit = 22, }, { .name = "pllq", .desc = "Main PLL (PLL) division factor", .first_bit = 24, .width_bits = 4, }, { }, }, }, accessing registers and reading bitfields is straightforward: peripheral_register_write_value(odr, new_value); .... pllm = register_bitfield_read_value(state->f4.fld.pllcfgr.pllm); this model has several advantages: - increased emulation accuracy; the functionality is fully and uniformly implemented for all objects, for example byte and unaligned accesses are implemented for all registers; read/write masks allow to affect only the desired bits; etc - increased readability; the definitions are descriptive, not hidden inside code - opens the door to automatically generate the MCU definitions possible disadvantages: - a small overhead, hopefully not impacting general performances this model is currently implemented and functional in my branch, and it was used to implement the needed STM32 peripherals (RCC, GPIO, FLASH, PWR) for the STM32F1 and STM32F4 families, and will probably be part of the new GNU ARM Eclipse QEMU release, scheduled in 2-3 weeks. however I would consider it still experimental, since I already have new ideas that I would like to experiment in future versions. regards, Liviu