4 ms·
> #elif SIZEOF_SIZE_T == 2 // LOL Fun fact - for a while, this was true for the third most popular processor architecture in the world, one that most people ha
by rkangel 4y ago
> #elif SIZEOF_SIZE_T == 2 // LOL
Fun fact - for a while, this was true for the third most popular processor architecture in the world, one that most people haven't heard of.
CSR (the bluetooth chip maker) was originally a spin-out from Cambridge Consultants, and their chips were for years based on a Cambridge Consultants processer design called XAP. As CSR had made billions of devices, XAP was likely right up there in terms of number of processors made.
XAP was unusual in a couple of ways (at least unusual compared to the fairly universal modern design). Here's a fun set of statements that all evaluate to true:
sizeof(uint8_t) == 1 // So far so normal, this is required by the C standard
sizeof(uint16_t) == 1 // Surprise! BTW, uint16_t is defined as "unsigned int"
sizeof(void *) == 1 // Weird
sizeof(void (*)(void)) == 2 // Yes, function pointers are not the same size as data pointers
This is all completely legal according to the C standard. Fun fact, the size of a byte isn't "8 bits", it's technically "the minimum addressable size on your platform"[1]. On every modern processor design I know of, that is an 8 bit quantity, but the XAP only allowed you to address 16 bits at a time. Presumably this was for efficiency - the XAP was very low gate count for a fairly capable processor (at the time). 16 bits was also the size that the instruction set operated on, the "natural size for calculation" as described in the C spec, so that's the size of an int. This means that sizeof(int) == 1, which is a surprise to most people. It also means that you can't do tricks like "cast a uint16_t to an array of 2 uint8_ts" (which is non-portable also for endian reasons anyway).
The other unusual aspect is that it's a Harvard architecture, which separates the address space for code and data. Code size being large, this meant function pointers were 24 bit, whereas data pointers were 16. Code that assumed it could cast a function pointer to "void *" to put it in callback context could get a nasty surprise (usually a subtle one, because it only broke if the function was late in the address space).
This was what I worked on early in my career, it was a great education in writing C that was actually portable. If it ran on PC and XAP, then it would likely run on anything!
[1] This is why picky/anal engineers will refer to "octets" in a network protocol rather than bytes. Bytes don't have a meaning except in the context of execution, which obviously doesn't apply to a network protocol.
- vlovich123 4y agoWhat’s also interesting was that the processor was actually a 32 bit processor iirc (at least the later ones). The 16 bit variant is purely the VM mode they run “application” code on which emulates the environment of an old CPU (my guess anyway - not sure why the VM was limited to 16bits). The Kalimba was equally weird with sizeof(int) = 1 being 24 bit and sizeof (long) was 48bit. I ported an ECDSA library to it for Pixel Buds 1 because the XAP was too slow. It was really really hard to get that to work properly and writing a simulated environment to make sure I emulated the math correctly with masking and whatnot. It was such an annoying architecture to work with that all the senior SW leads for Pixel Buds fought extra hard to avoid Qualcomm’s solution. Their evolution for the next set of chips to compete with Apple’s W1 saw them go down a weird path where they doubled down on getting rid of GCC and instead using their home grown compiler (none of their shit ran natively on Linux not Mac) and similarly weird architecture decisions (forget all the details now). Our job was made easier in that they couldn’t actually deliver a W1 competitor. Their best was exposing each bud as a separate device which would have been a terrible experience and they could only improve that experience for Android if we mainlined their weird decisions into Android (sorry - no). By comparison BESTech delivered a proper competitor design to the W1 (transparent sniffing and hand off) and their SW architecture was totally sane (ARM chip + gcc for sure + FreeRTOS if I recall correctly). Much better partners than Qualcomm.
- rkangel 4y ago> What’s also interesting was that the processor was actually a 32 bit processor iirc (at least the later ones). The 16 bit variant is purely the VM mode they run “application” code on which emulates the environment of an old CPU (my guess anyway - not sure why the VM was limited to 16bits). I was writing code natively for the processor - it was a XAP5 and natively 16 bit. Possibly CSR later moved to XAP6 (which was 32-bit) and kept application code portable using the VM. Kalimba was my first experience writing hand-coded assembly - I was implementing sample rate conversion for a hearing aid manufacturer. It was good fun - a dedicated multiply accumulate that meant you could do filtering in a single instruction per sample.
- vlovich123 4y ago
- kazinator 4y agoI have worked with such things myself; for instance most recently, at, Broadcom I had worked on ARM-based VoIP phones which had a Ceva Teaklite DSP, which has 16 bit bytes (and memory access with weird byte order that is neither big nor little endian). If we had to be a bit more portable to include such systems, we could test on bit instead of byte sizes, e.g. #if SIZEOF_SIZE_T * CHAR_BIT == 16
- deleted 4y ago[deleted]
- consp 4y agoI also remember the 8051 based TI bluetooth chips which also have some "weird" things going on and has a harvard like architecture just with more memory types. I raise you three types: program memory is also 24bit (using both one 8bit and the 16bit register) and 16bit "external" memory and 256 bytes of mapped registers. I don't know if the compiler actually does this as different types and how it internally handles it. Maybe someone can elaborate on that.
- mid-kid 4y agoIt's also 16 bit for the AVR architecture used on Arduino devices.
- rkangel 4y agoIs it? Cool! I haven't done anything with an Arduino for a while, and only used larger AVRs.
- magicalhippo 4y ago> sizeof(uint16_t) == 1 > This is all completely legal according to the C standard. Is it? I don't have access to the standard, but from secondary sources[1] it seems not? unsigned integer type with width of exactly 8, 16, 32 and 64 bits respectively (provided if and only if the implementation directly supports the type) If it is, it kinda defeats the whole purpose of uint16_t and friends. [1]: https://en.cppreference.com/w/c/types/integer https://en.cppreference.com/w/c/types/integer
- dan-robertson 4y agoIt has 16 bits but the units of sizeof are not bytes – they are ‘the minimum difference between two addressable things’ which on that architecture is two bytes.
- tjoff 4y agoIsn't that a byte? So rather, a byte is 16 bits.
- actionfromafar 4y agoSure. A byte is two octets. In cases like these it pays to be very precise.
- rkangel 4y agoI can't remember the precise wording in the standard , but sizeof() is effectively "how many of the minimum addressable thing do you need to store this thing". The "minimum addressable thing" is called a byte, and on XAP that is a 16-bit value. Storing a uint8_t wastes a 8 bits, because there isn't anything smaller than an int to put it in.
- magicalhippo 4y agoRight, I was assuming the bytes were 8 bits[1] given sizeof(uint8_t) == 1, ie since it had uint8_t. Seems really weird to have uint8_t but have CHAR_BIT > 8. [1]: https://en.cppreference.com/w/c/language/sizeof https://en.cppreference.com/w/c/language/sizeof
- unwind 4y agoCool. So on that platform CHAR_BIT was 16. I still wonder if those statements should use the plain non-typed sizes, or if no such were available. I can live with sizeof(char) == 1 sizeof(short) == 1 sizeof(void *) == 1 all being true with CHAR_BIT equal to 16, but it seems pointless to support uint8_t if it's not truly 8 bits. Too far from my language-lawyer mood now to dig deeper. :)
- rkangel 4y agoIt turns out you're right, and that's how I've actually written these examples in the past. A sibling comment provided a citation, and that I should really have been writing uint_leastXX_t: https://en.cppreference.com/w/c/types/integer https://en.cppreference.com/w/c/types/integer
- defrost 4y ago> On every modern processor design I know of, that is an 8 bit quantity, Serious throughput DSP chips don't sully themselves with a mere 8 bits, they're designed to do 32 bit and more FFT pipelines that modular index multiple vectors, fetch, multiply, add, and store every clock cycle. Eg: the Texas Instruments TMS320C54x has CHAR_BIT 16 ( Table 7-1 page 192 [1] ) Other modern DSP family chips have CHAR_BIT 32 .. they're for numerics not ASCII text processing. [1] https://www.ti.com.cn/cn/lit/ug/spru103g/spru103g.pdf https://www.ti.com.cn/cn/lit/ug/spru103g/spru103g.pdf