Last active
July 23, 2018 19:20
-
-
Save lisovy/a87110f55d59dcc8dee0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Multiplatform programming | |
========================= | |
Issues to be aware of: | |
* Integer types sizes (int -- 16 or 32 bits? long -- 32 or 64 bits?) | |
- Storing pointers into integer types | |
(unsigned long on 64-bit Linux vs. 64-bit Windows) | |
* Endianess (big, little) | |
* Memory access width (byte access, word access) | |
* Unaligned memory access | |
Interacting with HW in C | |
======================== | |
Device drivers interact with hardware by reading/writing to its | |
registers (i.e. register region). | |
Following few paragraphs describe what we should watch out when | |
doing so in C. | |
Possible issues: | |
* Some HW registers accessible only through IO ports (no MMIO) | |
http://wiki.osdev.org/I/O_Ports | |
* Endiannes (when accessing 16/32 registers) | |
https://en.wikipedia.org/wiki/Endiannes | |
* Compiler optimizing out our HW/register access | |
http://stackoverflow.com/questions/7083482/ | |
/how-to-prevent-compiler-optimization-on-a-small-piece-of-code | |
Possible solution: use volatile keyword | |
* Compiler reordering instruction sequence | |
Possible solution: "Compiler memory barrier" | |
Accessing to registers using specified functions only | |
(e.g. ioread32() in linux kernel) | |
* CPU reordering instruction execution (out-of-order execution) | |
Possible solution: "CPU memory barrier" | |
https://en.wikipedia.org/wiki/Memory_barrier | |
* When using overlay structures, think about default field alignment | |
Possible solution: use __attribute__ ((packed)); | |
* Beware unaligned access on some architectures | |
Possible solution: use __attribute__ ((aligned (XX))) | |
* C code readability/reusability | |
Don't do following: | |
https://elixir.bootlin.com/linux/v3.6.9/source/drivers/media/video/gspca/ov519.c#L1650 | |
/* Settings for (color) OV7620 camera chip */ | |
static struct ovcamchip_regvals regvals_init_7620[] = { | |
{ 0x12, 0x80 }, /* reset */ | |
{ 0x00, OV7620_DFL_GAIN }, | |
{ 0x01, 0x80 }, | |
{ 0x02, 0x80 }, | |
{ 0x03, OV7620_DFL_SAT }, | |
{ 0x06, OV7620_DFL_BRIGHT }, | |
{ 0x07, 0x00 }, | |
{ 0x0c, 0x24 }, | |
{ 0x0c, 0x24 }, | |
{ 0x0d, 0x24 }, | |
/* ... 45 lines of this stuff removed ... */ | |
{ 0x74, 0x00 }, | |
{ 0x75, 0x8e }, | |
{ 0x76, 0x00 }, | |
{ 0x77, 0xff }, | |
{ 0x78, 0x80 }, | |
{ 0x79, 0x80 }, | |
{ 0x7a, 0x80 }, | |
{ 0x7b, 0xe2 }, | |
{ 0x7c, 0x00 }, | |
{ 0xff, 0xff }, /* END MARKER */ | |
}; | |
################################################################################ | |
# How to make register access readable # | |
################################################################################ | |
Define offset address of each register | |
Add this offset to base address | |
Use specific functions for writing/reading (they will handle endiannes | |
issues when handling 16/32 registers and disable reordering | |
/* BAR0 regs */ | |
#define ADCTRL_reg 0x0 | |
#define ADLO_reg 0x0 | |
#define ADHI_reg 0x1 | |
iowrite8((chan) | ADCTRL_DEFAULT | ADCTRL_BIP | ADCTRL_RNG, devpriv->BAR0_io + ADCTRL_reg); | |
dat = ioread8(devpriv->BAR0_io + ADLO_reg); | |
dat |= (ioread8(devpriv->BAR0_io + ADHI_reg) << 8); | |
################################################################################ | |
Make an overlay structure | |
Positions of fields in this structure will represent relative position of | |
registers inside the "register region"; | |
Use a pointer to this structure; Set the pointer address to register region | |
(base address) | |
Use specific functions for accessing 16/32 fields -- because of endianness OR | |
Define the struct to be compiled correctly for particular endianning | |
typedef volatile struct sciBase | |
{ | |
uint32_t GCR0; /**< 0x0000 Global Control Register 0 */ | |
uint32_t GCR1; /**< 0x0004 Global Control Register 1 */ | |
uint32_t GCR2; /**< 0x0008 Global Control Register 2 */ | |
uint32_t SETINT; /**< 0x000C Set Interrupt Enable Register */ | |
uint32_t CLRINT; /**< 0x0010 Clear Interrupt Enable Register */ | |
uint32_t SETINTLVL; /**< 0x0014 Set Interrupt Level Register */ | |
uint32_t CLRINTLVL; /**< 0x0018 Set Interrupt Level Register */ | |
uint32_t FLR; /**< 0x001C Interrupt Flag Register */ | |
uint32_t INTVECT0; /**< 0x0020 Interrupt Vector Offset 0 */ | |
uint32_t INTVECT1; /**< 0x0024 Interrupt Vector Offset 1 */ | |
uint32_t LENGTH; /**< 0x0028 Format Control Register */ | |
uint32_t BAUD; /**< 0x002C Baud Rate Selection Register */ | |
#if ((__little_endian__ == 1) || (__LITTLE_ENDIAN__ == 1)) | |
uint8_t ED; /**< 0x0033 Emulation Register */ | |
uint32_t : 24U; | |
uint8_t RD; /**< 0x0037 Receive Data Buffer */ | |
uint32_t : 24U; | |
uint8_t TD; /**< 0x003B Transmit Data Buffer */ | |
uint32_t : 24U; | |
#else | |
uint32_t : 24U; | |
uint8_t ED; /**< 0x0033 Emulation Register */ | |
uint32_t : 24U; | |
uint8_t RD; /**< 0x0037 Receive Data Buffer */ | |
uint32_t : 24U; | |
uint8_t TD; /**< 0x003B Transmit Data Buffer */ | |
#endif | |
uint32_t FUN; /**< 0x003C Pin Function Register */ | |
uint32_t DIR; /**< 0x0040 Pin Direction Register */ | |
uint32_t DIN; /**< 0x0044 Pin Data In Register */ | |
uint32_t DOUT; /**< 0x0048 Pin Data Out Register */ | |
uint32_t SET; /**< 0x004C Pin Data Set Register */ | |
uint32_t CLR; /**< 0x0050 Pin Data Clr Register */ | |
uint32_t ODR; /**< 0x0054: Pin Open Drain Output Enable Register */ | |
uint32_t PD; /**< 0x0058: Pin Pullup/Pulldown Disable Register */ | |
uint32_t PSL; /**< 0x005C: Pin Pullup/Pulldown Selection Register */ | |
} sciBASE_t; | |
#define sciREG ((sciBASE_t *)0xFFF7E500U) | |
/** - bring SCI out of reset */ | |
sciREG->GCR0 = 1U; | |
/** - Disable all interrupts */ | |
sciREG->CLRINT = 0xFFFFFFFFU; | |
sciREG->CLRINTLVL = 0xFFFFFFFFU; | |
/** - global control 1 */ | |
sciREG->GCR1 = (1 << 25) /* enable transmit */ | |
| (1 << 24) /* enable receive */ | |
| (1 << 5) /* internal clock (device has no clock pin) */ | |
| ((1-1) << 4) /* number of stop bits */ | |
| (0 << 3) /* even parity, otherwise odd */ | |
| (0 << 2) /* enable parity */ | |
| (1 << 1); /* asynchronous timing mode */ | |
/** - set baudrate */ | |
sciREG->BAUD = 520; /* baudrate */ | |
/** - tranmision length */ | |
sciREG->LENGTH = 8 - 1; /* length */ | |
/** - set SCI pins functional mode */ | |
sciREG->FUN = (1 << 2) /* tx pin */ | |
| (1 << 1) /* rx pin */ | |
| (0); /* clk pin */ | |
/** - set interrupt level */ | |
sciREG->SETINTLVL = (0 << 26) /* Framing error */ | |
| (0 << 25) /* Overrun error */ | |
| (0 << 24) /* Pariry error */ | |
| (0 << 9) /* Receive */ | |
| (1 << 8) /* Transmit */ | |
| (0 << 1) /* Wakeup */ | |
| (0); /* Break detect */ | |
################################################################################ | |
Access register region as an array of bytes | |
Use datatype of particular size to match registers size | |
volatile uint32 *_hwreg; | |
enum VRegister { | |
VTCTRL = 0x0000/4, /* Beware of the /4 ! */ | |
VTSTATUS = 0x0008/4, | |
VTFRTIMER = 0x1048/4, | |
VTEICS = 0x1520/4, | |
VTEIMS = 0x1524/4, | |
VTEIMC = 0x1528/4, | |
VTEIAC = 0x152C/4, | |
VTEIAM = 0x1530/4, | |
VTEICR = 0x1580/4, | |
VTEITR0 = 0x1680/4, | |
VTEITR1 = 0x1684/4, | |
VTEITR2 = 0x1688/4, | |
VTIVAR = 0x1700/4, | |
VTIVAR_MISC = 0x1740/4, | |
} | |
_hwreg[VTIVAR] = 0x00008080; | |
_hwreg[VTIVAR_MISC] = 0x81; | |
_hwreg[VTEIAC] = 3; | |
_hwreg[VTEIAM] = 3; | |
assert(_hwreg[RDT0] == 0); | |
... however this does not solve the reordering or endiannes issue | |
################################################################################ | |
How to name flag values, shifts, offsets in correct way? | |
To be done... |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment