|
The uint32_t type (the clue is in the name) is 32 bits wide, or 4 bytes. The uint8_t is 1 byte. When you add 1 to a pointer it will increment it by the size of the object(s) pointed at. So a pointer to a uint32_t will increment by 4 bytes when adding 1, in order to point to the next variable of that type. Pointer arithmetic in C and C++ has always done this.
|
|
|
|
|
Vaclav_ wrote: if I use uint32_t adding 1 results in pointer being incremented by 4. If I use uint8_t same process increments the pointer by 1.
Pointer arithmetic in C.
Software rusts. Simon Stephenson, ca 1994. So does this signature. me, 2012
|
|
|
|
|
Quote: If uint32 is 4 bytes wide - why did they name it "32"?
4 bytes each of 8 bits = 4 x 8 = 32 bits .. its unsigned and a type .... so uint32_t
8 bytes of 8 bits = 8 x 8 = 64 bits .... so uint64_t also means 8 bytes.
Unlike int, long etc the sizes in bits and bytes are fixed in stone on every C compiler that meets the standard.
So we can basically say uint32_t is always 4 bytes, uint64_t is always 8 bytes on any C/C++ compiler
Yes correct it adds by 4 and as EVERYTHING on the PI 1,2 or 3 is aligned to 4 bytes they are an ARM RISC processor
and unaligned access is less than desirable. It is actually illegal and not predictable on many earlier ARM CPU's.
On your Pi3 which is 4 x Cortexa53 cores unaligned access is generally configured to raise an error exception but
can handle it if required. Now here is the big but, there is a MASSIVE penalty for unaligned access not just from the
CPU but the caches don't work properly.
The bottom line however is every hardware register is aligned to 4 bytes (that is it's divisible by 4) and can only
be written with 4 bytes at once (even in 64 bit mode). So the behaviour of uint32_t pointer addition is desirable and
ideal.
There is one further thing we generally do to make it compatible to for 64bit code for the Pi3 which is we declare
the pointers and/or structs representing hardware as align 4 with a GCC attribute. A typical sample will look like
this
struct __attribute__((__packed__, aligned(4))) SystemTimerRegisters {
uint32_t ControlStatus;
uint32_t TimerLo;
uint32_t TimerHi;
uint32_t Compare0;
uint32_t Compare1;
uint32_t Compare2;
uint32_t Compare3;
};
The reason is that the cortexa53 in AARCH64 mode natively runs everything in 8 byte alignment which it calls a word. It does however
have the ability to do half word (4 byte writes) and the aligned(4) is the way to tell it to use half word access. The packed
attribute is because without that the struct will have uint32_t spaced every 8 bytes (so 4 bytes uint32_t then 4 byte gap)
A volatile pointer to a hardware register is similarly written with an aligned 4
#define GPIO ((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + 0x200000))
So things written in this way work with 32bit and 64bit compilers for the Pi3.
It's not important on 32bit compilers but 64 bit compilers produce invalid code without it.
I program mainly in baremetal on the Pi .. so no O/S at all we just put our files on the blank SD card
GitHub - LdB-ECM/Raspberry-Pi: My public Baremetal Raspberry Pi code[^]
In vino veritas
modified 23-May-18 13:36pm.
|
|
|
|
|
Thanks Leon
it always makes more sense after your posts.
And as far as my project goes I got too confident and ended up with a mess , as always.
Luckily I had a backup so no lost.
If you recall I had some issues with memory mapping.
Ill have to prove this to myself , but it seems that when the memory map uses /dev/gpiomem I cannot map other devices such as SPI. Makes sense when you pointed out that each "/dev" has its own file.
I think it is just the way the BCM library tries to be "universal" and does everything in one file.
I actually only need SPI,I2C and GPIO.
And I have a class which handless the GPIO nicely.
Cheers
Vaclav
|
|
|
|
|
leon de boer wrote: 4 bytes each of 8 bits = 4 x 8 = 32 bits .. its unsigned and a type .... so uint32_t
8 bytes of 8 bits = 8 x 8 = 64 bits .... so uint64_t also means 8 bytes.
Unlike int, long etc the sizes in bits and bytes are fixed in stone on every C compiler that meets the standard.
The size of "uint32_t" is fixed at four bytes on compilers which implement uint8_t. A conforming compiler where "char" is 16 bits (e.g. because it targets a platform where memory can only be written in 16-bit chunks) and either "int" or "long" is a 32-bit two's-complement integer without padding bits must, however, define type `uint16_t` with a size of 1 and `uint32_t` with a size of two.
|
|
|
|
|
What you have said is misleading you are just talking about a compiler that use the clause that says
Quote: Various implementations of C and C++ reserve 8, 9, 16, 32, or 36 bits for the storage of a byte
So a byte in your case is defined as 16 bits and that is why the uint8_t type doesn't exist for you.
So yes a uint32_t in your case would be 2 bytes (because your byte is 16 bits) and all you have proven is what we have long argued to the standards committee that C has butchered the word byte it should have been called register. I mean all you have is a 16 bit register that can't break to 8 bits that is creating the problem.
I have always wanted to see a C compiler with 9 bit bytes because I think that would make for some real fun
So lets reword it uint32_t is always 4 x uint8_t assuming both are implemented because we have to avoid using the word byte because it's been butchered in C.
In vino veritas
modified 5-Jun-18 3:12am.
|
|
|
|
|
I don't think "register" is a good term to describe a system's smallest addressable storage unit. If you don't like "byte", I'd suggest "smallest addressable storage unit" would be a precise term.
In any case, I was responding to the claim that the size of a uint32_t is "set in stone" at four bytes for all conforming implementations, which a reasonable person might interpret as saying that sizeof (uint32_t) is required to be 4.
I think it's rather silly that the Standard defines no category of programs between Strictly Conforming programs, a category so narrow as to be almost useless, and Conforming programs, a category so broad as to be essentially meaningless, and only defines two categories of implementations, either of which would be allowed (because of the "One Program" loophole) to behave in arbitrary fashion when given almost any program. If the Standard sought to actually categorize things usefully, separating out common-integer-size implementations from weird-integer-size implementations would make a lot of sense. Unfortunately, the authors of the Standard seem to go out of their way to avoid suggesting that some implementations should be considered inferior to others.
|
|
|
|
|
In the following code, I tried to return a 2D local array from the getArray function and display it in the main function.
I use the following line to display the matrix
cout << " " << *(*(ptr+i*COL)+j) but it is not displayed.
Does anyone know how to display the array?
#include <iostream>
#define ROW 3
#define COL 4
using namespace std;
int main()
{
int **ptr;
int **getArray();
ptr = getArray();
cout << "\n Array tab[][] in main(): " << endl << endl;
for (int i=0; i<ROW; i++)
{
for (int j=0; j<COL; j++)
cout << " " << *(*(ptr+i*COL)+j);
cout << endl;
}
return 0;
}
int **getArray()
{
static int tab[ROW][COL] = {
11, 12, 13, 14,
21, 22, 23, 24,
31, 32, 33, 34,
};
return (int**)tab;
}
|
|
|
|
|
Try this:
for (int j=0; j<COL; j++)
cout << " " << (int)*(ptr+i+j) << endl;
cout << endl;
|
|
|
|
|
Why this line?
cout << " " << (int)*(ptr+i+j) << endl;
Can you give me more details ?
Why do you need a type-casting conversion ?
|
|
|
|
|
Multidimensional arrays declared the way you have are just an abstraction for programmers,
since the same results can be achieved with a simple array, by multiplying its indices:
int jimmy [3][5]; // is equivalent to
int jimmy [15]; // (3 * 5 = 15)
In effect GetArray returns a 1D pointer array and you could have simply returned it as int* as it's really a big 1 dimensional array
You returned int** so the typecast is to deal with it but it would have been simpler like this
int *getArray()
{
static int tab[ROW][COL] = {
11, 12, 13, 14,
21, 22, 23, 24,
31, 32, 33, 34,
};
return (int*)tab;
}
Then the access becomes more obvious
int *ptr = getArray();
cout << "\n Array tab[][] in main(): " << endl << endl;
for (int i = 0; i<ROW; i++)
{
for (int j = 0; j<COL; j++)
cout << " " << ptr[i*COL + j];
cout << endl;
}
In vino veritas
|
|
|
|
|
For some reason, cout was printing the numbers in hex rather than decimal.
|
|
|
|
|
The return type for getArray needs to be int (*getArray())[COL]; . An expression of type T [M][N] decays to type T (*)[N] , not T ** .
So your code needs to be
int main()
{
int (*ptr)[N];
int (*getArray())[N];
...
}
int (*getArray())[N]
{
...
return tab;
}
|
|
|
|
|
How does one know which Dll and Functions to include in the program ?
|
|
|
|
|
Hi,
Your question is unclear. If you are missing some dependencies then you can use Dependency Walker[^] to get the list.
You can also use DUMPBIN[^] to get a list of dependencies.
Best Wishes,
-David Delaune
|
|
|
|
|
You know which functions to add by deciding what your code needs to do and studying the documentation for those features. The documentation will also show which DLL contains which functions. If you are talking about the C library then see C Run-Time Library Reference[^].
|
|
|
|
|
|
" volatile " keyword instructs the compiler NOT to optimize the code.
It is commonly used in interrupts. Fine.
The library code I am using was apparently written with intent to use ether interrupts or multiprocessor hardware. Fine.
The following snippet class variables are declared as " volatile uint32_t *".
But the <b>code does not perform simple addition</b> - it always returns "1".
It fails even when declared as local variable - see "TEST".
Am I missing something ?
<pre lang="c++">
bcm2835_peripherals = (uint32_t*) BCM2835_PERI_BASE;
bcm2835_pads = bcm2835_peripherals + BCM2835_GPIO_PADS / 4;
bcm2835_clk = bcm2835_peripherals + BCM2835_CLOCK_BASE / 4;
bcm2835_gpio = (uint32_t*)bcm2835_peripherals + BCM2835_GPIO_BASE / 4;
volatile uint32_t *TEST = bcm2835_peripherals + BCM2835_GPIO_BASE / 4;
cout <<"bcm2835_peripherals "<<bcm2835_peripherals<< endl;
cout <<"BCM2835_GPIO_BASE / 4 "<< hex << BCM2835_GPIO_BASE / 4<< endl;
cout <<"bcm2835_gpio "<< hex << +bcm2835_gpio<< endl;
cout <<" TEST cm2835_gpio "<< hex << TEST << endl;
</pre>
Thanks for any comments.
Cheers Vaclav
|
|
|
|
|
volatile uint32_t *TEST
You are declaring TEST as a pointer, try it without the asterisk. But either way we do not know the values of bcm2835_peripherals or BCM2835_GPIO_BASE .
BTW please unclick the checkbox at the bottom which says "Treat my content as plain text, not as HTML", so your code blocks get formatted properly and are clearer to read.
|
|
|
|
|
Sorry for the format issue.
These lines just add two pointers / addresses , and it is the addition which is failing when the resulting pointer is set to "1".
The values of individual terms is irrelevant - the function is.
It works fine - produces correct sum of terms / addresses without "volatile" keyword.
Would there be a conflict if the terms are not declared as "volatile" ?
|
|
|
|
|
Vaclav_ wrote: The values of individual terms is irrelevant On the contrary, they are the most important pieces of information. If you are adding two values and the answer is 1, then we need to know those values.
|
|
|
|
|
OK, but I cannot copy it from IDE when its runing on remote.
I am not sure I can post screen shots here.
I'll get back to you soon.
Here is a copy of the cout debugging without volatile
bcm2835_periopherals 0x2000000
BCM28356_GPIO_BASE / 4 80000
bcm2836_gpio 0x20200000
Here is something which MAY explain the issue.
<a href="https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html">Using the GNU Compiler Collection (GCC): Volatiles</a>[<a href="https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html" target="_blank" title="New Window">^</a>]
-- modified 17-May-18 11:56am.
|
|
|
|
|
Vaclav_ wrote: Here is something which MAY explain the issue.
https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html
I don't think that's the issue here, but it does represent something I wish the Standard would address. Although there may be some platforms and application fields for which gcc's behavior would be reasonable in a quality compiler, there are many purposes for which it is not. On many platforms, it is possible for an access to a volatile location to trigger operations that usefully affect other storage (e.g. starting an "in-place" background I/O operation). For an implementation to be suitable for systems-programming on such a platform, it must provide a way of ensuring that such operations are sequenced relative to other operations on non-qualified storage. An implementation can support systems programmings on such platforms without requiring the use of special directives by treating volatile accesses as triggering a call to an unknown function, and I would suggest that quality implementations for such platforms should provide an option to treat them in such fashion.
Unfortunately, even though the Standard has to date expressly avoided quality-of-implementation issues, the authors of gcc seem to think either that the Standard fully describes everything necessary to make something a quality implementation, or that users should not expect gcc to behave like a quality implementation when any of its optimizations are enabled.
While there might some cases where it might be unnecessarily expensive to treat volatile accesses as sequenced relative to non-qualified accesses to objects that would be accessible by outside code, in most cases the cost would be negligible, and would be less than the cost of adding "volatile" qualifiers and accesses everywhere else that would otherwise be necessary to ensure correct semantics.
When the Standard was written, it may have been reasonable to expect compiler writers to exercise good judgment about how quality compilers intended for various purposes should be expected to behave in circumstances beyond those mandated by the Standard, and for programmers to be reliant upon compiler writers' sound judgment. Such expectation and reliance are no longer tenable. If the authors of the Standard don't want to mandate that all compilers treat "volatile" more strongly, they should at minimum specify a predefined macro to allow programmers to say something like:
#if !(__STDC_VOLATILE_SEMANTICS >= 3)
#error "This compiler, as configured, cannot handle the necessary volatile semantics."
#endif
and then expect that every compiler will either process their code with required semantics or refuse to process it altogether. Compiler writers that find such semantics impractical would not be required to support them, but would merely be required to refrain from defining __STDC_VOLATILE_SEMANTICS with a value indicating such support. Code which includes the above test and relies on such semantics would be less portable than code which does neither, but its behavior would be defined on all systems which process it.
|
|
|
|
|
In this case, 'volatile' refers to the object to which it is pointing, not the pointer itself.
The intent of this code confuses me; why are items being cast to be a pointer?
|
|
|
|
|
As far as I can tell - these are memory addresses and are being passed to functions.
The author of the code does not tell much about WHY he does things this way.
What I have gather so far - the application writes into "user space" (?) (Linux term) memory, not directly to hardware. The OS does the actual "writing" to hardware.
|
|
|
|