|
My preferred way of interacting with memory mapped peripherals is overlaying nested structures and using bitfields. Autocomplete works, you don't have to care about masking things, it's easy to document and makes for very readable code at the point of use. For devices with multiple instances of submodules with gaps between them, you literally put the gaps into the structs to match the stride of the instances. In my experience the transistor-touchers pretty much always do this intentionally in a way that allows for an array-of-structs memory overlay because it makes for easy address decoding on their end. ARM's recommendation is super conservative. If you know what the code actually does you aren't going to have a problem using any kind of struct or bitfields. Using packed structs isn't really a good idea but it's hardly applicable: none of the fields are going to be unaligned on a peripheral anyway. Bitfield accesses are just basic read-modify-writes. The time to be careful is when you've got stuff like multiple bits which all need to be set in one write e.g. when a write triggers some action immediately. Here's a cut down version of a DMA driver I made with just the structs shown. code:
code:
code:
|
# ? Mar 7, 2019 13:28 |
|
|
# ? Jun 9, 2024 05:27 |
|
The careful reader will note that the recommendation in those docs is to avoid using packed structs to make unaligned fields, which is a pretty good idea. Avoiding packed structs entirely is an overly-conservative reading.
|
# ? Mar 7, 2019 13:42 |
|
Jabor posted:The careful reader will note that the recommendation in those docs is to avoid using packed structs to make unaligned fields, which is a pretty good idea. Avoiding packed structs entirely is an overly-conservative reading.
|
# ? Mar 7, 2019 14:36 |
|
Is it wrong and how wrong is it to handle an enum like an int? Example:code:
|
# ? Mar 7, 2019 16:22 |
|
Volguus posted:Is it wrong and how wrong is it to handle an enum like an int? Example: Is this C or C++? I think this is fine C, though I'd put the incrementing in a helper function, and maybe check for/warn about negative values. In C++ you can use a strongly typed enum to guarantee you don't pass a random integer as an enum and specify what the underlying type is.
|
# ? Mar 7, 2019 16:42 |
|
Jeffrey of YOSPOS posted:Is this C or C++? I think this is fine C, though I'd put the incrementing in a helper function, and maybe check for/warn about negative values. In C++ you can use a strongly typed enum to guarantee you don't pass a random integer as an enum and specify what the underlying type is. It's C++ . I was just wondering if there could be any problems with casting the enum values to an int and viceversa that I'm not aware of.
|
# ? Mar 7, 2019 17:17 |
|
Spatial posted:My preferred way of interacting with memory mapped peripherals is overlaying nested structures and using bitfields. Autocomplete works, you don't have to care about masking things, it's easy to document and makes for very readable code at the point of use. Depending on the hardware and compiler, using bitfields can go hideously wrong - if hardware is expecting all memory accesses to be four bytes, and the compiler turns your bitfield access into a one byte write (as it's perfectly entitled to do), the hardware might ignore it or do something stupid e: it can also trip you up if the register makes the hardware do something when you write back a bit you read, or when reading has side effects, and all the other screwy things HW designers like to do Qwertycoatl fucked around with this message at 18:47 on Mar 7, 2019 |
# ? Mar 7, 2019 18:39 |
|
The right way to do this while still getting the convenience of bit-fields is to make the actual volatile field an int32 or whatever, but provide functions that take and return a more expressive type. That would, of course, require the slightest amount of thought about code structure instead of producing excuses to wildly rend your garments and curse the sun, so I am not surprised that it is a solution that has never occurred to system programmers in 40 years of programming.
|
# ? Mar 7, 2019 19:50 |
|
The sun doesn't curse itself you know.
|
# ? Mar 7, 2019 20:06 |
|
Qwertycoatl posted:The sun doesn't curse itself you know. Should have picked OSF/1 instead imo
|
# ? Mar 7, 2019 21:00 |
|
Qwertycoatl posted:Depending on the hardware and compiler, using bitfields can go hideously wrong - if hardware is expecting all memory accesses to be four bytes, and the compiler turns your bitfield access into a one byte write (as it's perfectly entitled to do), the hardware might ignore it or do something stupid Always gotta watch out for the special stuff with side effects though. My favourite is when you get a register that means completely different things when read versus when written. e: Actually no the absolute worst is bastards who make READING a register kick off some hardware action. Inevitably you will want to debug it and the debugger will constantly read it, and you won't realise it for four hours. Spatial fucked around with this message at 22:00 on Mar 7, 2019 |
# ? Mar 7, 2019 21:50 |
|
rjmccall posted:The right way to do this while still getting the convenience of bit-fields is to make the actual volatile field an int32 or whatever, but provide functions that take and return a more expressive type.
|
# ? Mar 7, 2019 21:59 |
|
Spatial posted:e: Actually no the absolute worst is bastards who make READING a register kick off some hardware action. Inevitably you will want to debug it and the debugger will constantly read it, and you won't realise it for four hours. Yeah we have a couple of fifos where you pop them by reading them, and a ton of counter registers that are clear on read
|
# ? Mar 7, 2019 22:17 |
|
Spatial posted:e: Actually no the absolute worst is bastards who make READING a register kick off some hardware action. Inevitably you will want to debug it and the debugger will constantly read it, and you won't realise it for four hours. Christ yes this. This was among my very first professional experiences debugging hardware (a SPI peripheral's FIFO access register which popped off the FIFO when it was read) and also my very most recent experience debugging hardware, just yesterday in fact (a USB peripheral with clear-on-read bits in the interrupt status register). It keeps happening
|
# ? Mar 7, 2019 23:17 |
|
Has anyone tried using Kvasir? It's supposed to be some sort of metaprogramming library for microcontroller I/O and registers. I was considering it but the last commit was over a year ago so I assume the developer just got bored and left.
|
# ? Mar 8, 2019 04:50 |
|
qsvui posted:Has anyone tried using Kvasir? It's supposed to be some sort of metaprogramming library for microcontroller I/O and registers. I was considering it but the last commit was over a year ago so I assume the developer just got bored and left. It appears to be completely undocumented. e: Oh, not completely. But "doc" just has dolor sit amet. And there's still very little information on how to use it, and I'd hate to debug it/anything using it. Qwertycoatl fucked around with this message at 07:45 on Mar 8, 2019 |
# ? Mar 8, 2019 07:36 |
|
Jeffrey of YOSPOS posted:Yeah - if your hardware has registers that don't meet the alignment requirements of your cpu, you've got bigger problems than what sort of code your compiler outputs to access those registers. You're not wrong. I think it's the DWC HDMI component of the whole video ...thing that has the byte aligned registers. Ie four registers per word. Remember ARM can't do byte accesses. I had a little time to check the compiler I'm using actually supports bit fields in C. It does. I could use GCC but that's a whole new can of worms. When I have a little time I might write a better test program to ensure it behaves as expected. All I did last night was write some bits then read and print them. e:I was concerned about code portability being an issue using bitfields, until my dumb brain realised that this code is tied to the architecture anyway because it's driver code for ARM specific hardware on an ARM specific OS. General_Failure fucked around with this message at 03:51 on Mar 9, 2019 |
# ? Mar 9, 2019 03:29 |
|
Anyone have any thoughts about state machine libraries? My initial research turned up boost MSM, boost statechart, and an experimental boost lib called SML. SML has C++14 features and supposedly fixes some compile time and other issues that pop up with MSM. SML even has example code to print out the state machine as plantUML markup. I’ve implemented some simple examples with SML and it seems to work fine but I’m interested if anyone has any thoughts.
|
# ? Mar 9, 2019 16:54 |
|
The 2d graphics guys are talking audio now : http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1386r0.pdf
|
# ? Mar 9, 2019 17:09 |
|
General_Failure posted:You're not wrong. I think it's the DWC HDMI component of the whole video ...thing that has the byte aligned registers. Ie four registers per word. Remember ARM can't do byte accesses.
|
# ? Mar 9, 2019 17:30 |
|
What do you mean ARM can't do byte accesses, is that only for MMIO?
|
# ? Mar 9, 2019 17:45 |
|
It is fully possible to support byte writes in a peripheral it usually just isn't done. It depends entirely on the peripheral and the bus to it. Often the write interface with the MCU is the equivalent of this psuedocode:code:
General_Failure: For a large unaligned buffer of indeterminate length you will probably have a much easier time if you do the work in an array and then copy it into the peripheral memory when you're finished.
|
# ? Mar 9, 2019 18:16 |
|
fritz posted:The 2d graphics guys are talking audio now : http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1386r0.pdf It's like the committee has a fetish for the C# / Java standard libraries.
|
# ? Mar 9, 2019 20:04 |
|
fritz posted:The 2d graphics guys are talking audio now : http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1386r0.pdf I guess "why not Boost.EquivalentLibrary" is going to be a required section in every standard library proposal from here until the end of time. Boost is good, don't get me wrong, but there are even more reasons you might not want to use Boost.Thing than there are for not using std::thing, and they often have very little to do with the quality of the libraries and more to do with the qualities of C++. Also, I hear there is a special place in hell for library developers who make boost classes part of their public API so the entire drat thing bleeds into any linking application (looking at you, Pixar...).
|
# ? Mar 9, 2019 22:53 |
|
pseudorandom name posted:What do you mean ARM can't do byte accesses, is that only for MMIO? It just can't. It has LDRB and STRB which are pretty worthless because they only deal with the LSB. It's not a whole lot different from going code:
ARM (besides very old versions) can only do word aligned accesses. It's up to the programmer or the language to extrapolate the data from there. Thankfully it's only the DWC HDMI component which has packed byte sized registers. Fun fact: By default the Sunxi SOCs have the address registers obfuscated and read disabled for the DWC HDMI component. Thankfully someone worked out how to descramble and write enable that IO range. Apparently the vendor released BSP linux kernel uses it still scrambled for some reason. Spatial posted:
On the OS side of things, like where I mentioned where I started writing a struct and realised that it can have a variable amount of data on the end, that's the result of a function which I need to populate being passed a pointer to a data structure by the OS. Working with it as an array would be by far the easiest method.
|
# ? Mar 10, 2019 00:09 |
|
I don't know ARM mnemonics or assembly syntax so that's all meaningless to me, so I'll rephrase: are you saying that ARM pulled a DEC Alpha and it isn't possible to implement the C programming language or multithreading on ARM processors?
|
# ? Mar 10, 2019 00:42 |
|
pseudorandom name posted:it isn't possible to implement the C programming language or multithreading That seems an exaggeration, to say the least. The C programming language was implemented on far, far more exotic architectures, and two of the most notable operating systems ported to the Alpha (VMS and Windows NT) had a thread-oriented scheduler at a time when process-oriented schedulers were the norm
|
# ? Mar 10, 2019 00:58 |
|
GF is being misleadingly anal, of course they can do byte accesses. But there's still 32 data wires on the bus. So the simplest way to physically implement it - and therefore cheapest, fastest, smallest way - is to mask off the unwanted high bits from the data the MCU gets from the bus. In the case of a memory mapped hardware peripheral that doesn't deal with bytes, byte addressing doesn't make any sense under this scheme. It won't even have the lowest 2 bits of the address physically connected to it. But this is a limitation of the peripheral not the ARM core. The peripheral designer can support byte accesses if they want, but then they'd have to support a more advanced bus standard, it can't pass through certain bus bridges, it uses more power, it's slower, and it makes the device physically larger and more expensive. Hardware designers are absolutely allergic to all of these things which is why it's rarely done.
|
# ? Mar 10, 2019 02:22 |
|
Spatial posted:GF is being misleadingly anal quote:
Random copypaste from the i.MX6 manual. Page 1551. These are few registers from the section which is also relevant to the hardware I'm using. I trimmed a few fields out for readability. First field is absolute address. Last field is register width. I haaven't written anything meaningful for this section of the hardware yet, so if I am misinterpreting it, please tell me. code:
|
# ? Mar 10, 2019 03:17 |
|
The C and C++ memory models make certain guarantees which amount to requiring stores to even a non-atomic object to only write to that specific object (unless the object is a bit-field). So an architecture which couldn't express an eight-bit store without doing a non-atomic read-modify-write sequence would be unable to implement the C specification with an eight-bit char. But it's hard to imagine why an architecture wouldn't be able to express that unless it literally didn't have an eight-bit store instruction. I'm not a processor architect, so my understanding here gets a little sketchy (and even if it was accurate once, it might be badly out of date), but I believe it's very common for this all to be managed in units of full cache lines: at most one core can own the right to have unpublished stores to a cache line, so any core which wants to perform a store has to wait for the current owner to publish its stores, which means that the owner can do arbitrarily-complex transformations with perfect atomicity as long as they're internal to that one cache line. Claiming ownership of a whole cache line and then changing only one byte of it is not fundamentally different from claiming ownership and then changing only four or eight bytes of it; very few processors have instructions that could set a whole cache line at once anyway. Now what happens when you're talking about I/O devices, that I don't really know. rjmccall fucked around with this message at 04:20 on Mar 10, 2019 |
# ? Mar 10, 2019 03:53 |
|
OK, so the answer to my original question is "Yes, of course ARM can do byte accesses, the restrictions are on MMIO."
|
# ? Mar 10, 2019 04:01 |
|
rjmccall posted:
If you want to perform operations on bits / bytes you have have to use bitwise ops and shifts / rotates. You know, now I have to look under the hood at how C is handling the bitfields. This is interesting. I'm not trying to argue with anyone. I just came here initially because I want to write the driver in C to avoid what is to most a cryptic wall of assembly, but dealing with bits in registers, and blocks of registers with an odd layout was something that I'm struggling with.
|
# ? Mar 10, 2019 05:07 |
|
The ARM core and bus support byte granularity, it's just that most peripherals don't. It's done with byte strobes. These are write enable signals that tell the device being addressed which bytes in the data should be written. On a 32-bit bus there are four of these - for STRB only the least significant is set. For STRH two are set, and for STR all four are set.
|
# ? Mar 10, 2019 07:24 |
|
fritz posted:The 2d graphics guys are talking audio now : http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1386r0.pdf This is significantly less insane than the 2d graphics proposal. The API at least resembles something you could write a real program with and it's not as overly-simplified as I expected.
|
# ? Mar 10, 2019 08:46 |
|
fritz posted:The 2d graphics guys are talking audio now : http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1386r0.pdf Call me when they propose a physics engine instead.
|
# ? Mar 10, 2019 17:47 |
|
Spatial posted:The ARM core and bus support byte granularity, it's just that most peripherals don't. Interesting. I went digging quickly because I'd never heard of STRH. Makes sense it would exist. Only found it for aarch64 so far but it makes sense it'd exist for 32 bit. For people reading in hex that's STRB xx xx xx nn STRW xx xx nn nn STR nn nn nn nn The irritation is when register maps exist like the one I copypasted an excerpt from a few posts back which has the registers arranged like dd bb cc aa. Totally doable but at odds with how registers are normally arranged for ARM architecture. In that case my instinct tells me to test whether C has predictable results when treating a pointer as an array of uint8_t and then do it that way. Incidentally I stole a few minutes last night to view the disassembly of the test program I did for bitfields with the compiler I'm using. In it's own horrible way it was just using bitmasking and the barrel shifter to handle the fields. No surprises there. Hopefully I'll stop writing dead end programs and actually get on with the driver now. Thanks for the help.
|
# ? Mar 11, 2019 00:00 |
|
Good luck. I know the pain all too well! Btw if you're fiddling with bitfields in assembly, check out the instructions BFI, BFC and UBFX/SBFX. They have an index/width based interface rather than having to deal with masking and shifting.
|
# ? Mar 11, 2019 12:33 |
|
rjmccall posted:very few processors have instructions that could set a whole cache line at once anyway. LDM/STM on Arm and the x86 string instructions come to mind...
|
# ? Mar 11, 2019 17:26 |
|
hackbunny posted:The C programming language was implemented on far, far more exotic architectures, The C programming language was only standardised in 1989 (and the standard has evolved since), though. That there was a language called 'C' on a Symbolics machine does not mean it's compliant with the C standard as of TYOOL 2019. Also, are we sure the output of Symbolics C actually directly ran on the bare hardware? In their position I'd probably write a compiler targeting a VM of some sort that presented a nice C-ish interface. It's not like you're using it for writing the kernel on a Lisp Machine.
|
# ? Mar 11, 2019 17:42 |
|
|
# ? Jun 9, 2024 05:27 |
|
feedmegin posted:LDM/STM on Arm and the x86 string instructions come to mind... What about AVX-512, that must be whole cache lines right? 64 bytes per load/store?
|
# ? Mar 11, 2019 18:07 |