Hi
Since the next goal is an official release (no longer Beta) for the Luminary Micro project I am presently working on ensuring compatibility between the various family classes and how the actual device (one of presently 138 possibilities) can be (fairly) easily configured. So I am also taking the opportunity to make last fundamental changes before the project gets stricter version control - making such things a bit trickier.
I have found that the System Control device capabilities are proving a good way to do this. Since it is not practical to configure and test every possible device, several have been selected, which are sort of umbrella parts for various others with only minor differences (one UART more, or less, no I2C rather than with I2C, etc). When a specific device has been chosen, which deviates from the reference part, the user can effectively copy the Device Capability register values from its specific user manual and put these into a new device structure (in LM3Sxxxx.h). The project uses the structure to determine which peripherals, how much SRAM and FLASH, or which ports are actually available and configures itself fairly independently.
At the same time I have added a new port macro.
As you probably know it is possible to change only certain bits on a port, or read only certain ones (a form of bit masking). This is achieved by the port access address, where the bits A9..A2 define which port bits are masked (either modified or returned). The following macro is useful when fastest possible port access speed is required, although it only has advantages when the port address and the mask values are fixed and so the address can be calculated at compile time - otherwise the address calculation will cancel any speed advantages.
First a typical method of setting a bit (or bits) without affecting others on the same port:
GPIODATA_F |= 0x02; // set bit 2
The following show the assembler code to do this
4A05 ldr r2, [pc, #0x014]
6813 ldr r3, [r2, #0x00]
F0830301 orr r3, r3, #0x00000001
6013 str r3, [r2, #0x00]
The following code does the same thing:
_WRITE_PORT_MASK(F, 0x02, 0x02);
using the macro:
#define _WRITE_PORT_MASK(ref, value, mask) *(unsigned long *)(GPIO_PORT_##ref##_BLOCK + 4*mask) = value
The resulting assembler looks like this:
4B03 ldr r3, [pc, #0x00C]
6019 str r1, [r3, #0x00]
In fact the improvement is greater when more than one bit has to be changed, where some need to be set high and some low. As a comparison:
GPIODATA_F |= 0x02; // set bit 2
GPIODATA_F &= ~0x04; // clear bit 3
or GPIODATA_F = ((GPIODATA_F | 0x02) | (GPIODATA_F & ~0x04));
against
_WRITE_PORT_MASK(F, 0x02, 0x06);
It should be clear that this will save at least another instruction...
When reading there is also a chance to speed things up by avoiding the need to mask bits.
Eg.
Value = (GPIODATA_F & 0x08); // read a port and mask bit 3
becomes
Value = _READ_PORT_MASK(F, 0x08);
using this macro:
#define _READ_PORT_MASK(ref, mask) *(unsigned long *)(GPIO_PORT_##ref##_BLOCK + 4*mask)
In this case the mask is not achieved with an additional AND instruction since the result is automatically masked in the hardware.
Finally, the newer classes (Dust-Devil and Tempest) offer a single cycle port write through a different memory aperture. The project automatically selects the optimum aperture based on the device used and so doesn't use the legacy ports. This assumes that no Beta projects have specific timing needs where the slower accesses need to be retained.
Regards
Mark
P.S. The reason why I never used this earlier was due to a complication in the simulator. But, I managed to overcome this now!