how does u-boot maps peripheral memory? - u-boot

I am confused how the registers(for ex: for uart controller) are mapped in u-boot?
Are they memory mapped in DDR? If yes How?
If not then what is the meaning of "peripheral addresses are memory mapped" in U-Boot?

Welcome to Stackoverflow.
The registers are mapped to RAM memory address space at hardware level using different type of interconnects in the SoC. e.g Lets take the example of OMAP 35x series and see how UART registers are mapped to the RAM memory address space of the SoC and how we may access them in U-Boot.
In the Technical Reference Manual for OMAP 35x, Table 2-3. L4-Core Memory Space Mapping, we see that base address for UART1 is 0x4806A000 and the size (of RAM that it occupies) is 4KB.
It means that registers for UART1 are mapped to RAM the memory space in a 4KB region starting at this address.
To access a register of UART1, we need to know its offset from the base address to get its actual address. These offsets can be found in Table 17-39 UART IrDA CIR Register Summary.
Once we know the actual address we make a pointer to that register using
unsigned int * (base_address + offset)
Once we have the pointer we can de-reference it to read/write the register. e.g
To read a register in a variable 'x' we use:
unsigned int x;
x = *(unsigned int * (base_address + offset));
Writing a register is also similar
*(unsigned int *(base_address + offset)) = some_value;
To get a better understanding of how memory mapping is implemented at hardware level, skim through the Chapter 2, 'Memory Mapping', of TRM.

Related

Reading value from memory location (with 24-bit address) in 8-bit microcontroller

I thought it should be as simple as:
uint32_t getCrc(void)
{
uint32_t expectedCrc = *(uint32_t*)0x27FF0;
return expectedCrc;
}
And if the memory location has the following value:
Then the expectedCrc should equal to 0xECD8743D
But surprisingly, the value is: 0x82828282
I tried defining a pointer to uint32_t and assign the memory address to it as following:
uint32_t getCrc(void)
{
uint32_t *ptr = (uint32_t*)0x27FF0;
uint32_t expectedCrc = *ptr;
return expectedCrc;
}
But the value of the pointer itself was 0xFFFF and expectedCrc equal 0x82828282
I found these two values in a different memory address:
I also tried the same with char *ptr = (char*)0x27FF0 but it gave the same values.
Finally, I tried to check what is the size of a pointer to char in this controller using uint8_t size = sizeof(char*); and the answer was 0xb0 which equals 176.
I think it has something to do with the 24-bit memory address and the CPU architecture. I'm working on stm8 controller.
Could someone explain why does this happen?
UPDATE
I tried replacing the address 0x27FF0 with 0xFFF0 and it worked fine. So the problem is with the long address. I want to write the CRC value in the last address to avoid over-writing it with the code itself in case the program went bigger. How can I handle this?
From the Cosmic compiler datasheet https://www.cosmic-software.com/pdf/cxstm8_pd.pdf
cxstm8 provides 2 different memory models depending on the size of the application.
For applications smaller that 64k, the “section 0” memory model provides the best code density by defaulting function calls and pointers to 2 bytes.
For applications bigger than 64k, the standard memory model provides the best flexibility for using easily the linear addressing space. Each model comes with its own set of libraries.
This may be the cause for your problem. If you want to access the memory location of above 16 bit address directly you need to use the correct memory model.
As #Raje answered that it is about the memory models, I did further reading in the COSMIC user's guide and I found the following:
The STM8 compiler supports two memory models for application
larger than 64K, allowing you to choose the most efficient behavior
depending on your processor configuration and your application. All
these models allow the code to be larger than 64K and then function
pointers are defaulted to #far pointers (3 bytes). Data pointers are
defaulted to #near pointers (2 bytes) unless explicitly declared with
the #far modifier.
Therefore, the solution was to add #far to the pointer's type as following:
uint32_t calculatedCrc = 0;
expectedCrc = *(#far uint32_t*)0x27FF0;
This got the problem solved.

How large is the virtual address space of a program?

I was reading Operating Systems: Three Easy Pieces. To learn how the virtual address space for a program look like, I run the following code.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
printf("location of code : %p\n", (void *) main);
printf("location of heap : %p\n", (void *) malloc(1));
int x = 3;
printf("location of stack : %p\n", (void *) &x);
return x;
}
Its output is:
location of code : 0x564eac1266fa
location of heap : 0x564ead8e5670
location of stack : 0x7fffd0e77e54
Why the code segment's location is 0x564eac1266fa? What does so large a (virtual) space before it use for? Why doesn't it start from or near 0x0)
And, why the program's virtual address is so large?(from the stack location, it's 48 bits wide) What's the point of it?
The possible virtual address space organizations are defined by the hardware you are using, specifically the MMU it supports. The OS may then use any organization that the hardware can be coerced into using, but generally it just uses it directly (possibly with some subsetting), as that is most efficient.
The x86_64 architecture defines a 48-bit virtual address space1, and most OSes reserve half of that for system use, so user programs see a 47 bit address space. Within that address space, most OSes will randomize the addresses used for any given program, so as to make exploiting bugs in the programs harder.
1Strictly speaking, the architecture defines a 64-bit virtual address space, but then reserves all addresses that do not have the top 17 bits all 0 or all 1.
You are barking up the wrong tree with what you are trying to do here. A process has multiple stacks, may have multiple heaps, and main might not be the start of the code. Viewing an address space as a code segment, stack segment, heap segment, ... as horrible operating systems books do is only going to get you confused.
Because of logical addressing, the memory mapped into the address space does not have to be contiguous.
Why the code segment's location is 0x564eac1266fa? What does so large a (virtual) space before it use for? Why doesn't it start from or near 0x0)
The start of code in your process would well be at 0x564eac1266f8. The fact that you have a high address does not mean the lower addresses have been mapped into the process address space.
And, why the program's virtual address is so large?(from the stack location, it's 48 bits wide) What's the point of it?
Stacks generally start high and grow low.

Initializing variable at address zero in C

This may be a pretty basic question. I understand that there is a C convention to set the value of null pointers to zero. Is it possible that you can ever allocate space for a new variable in Windows, and the address of that allocated space happens to be zero? If not, what usually occupies that address region?
On MS-DOS the null pointer is a fairly valid pointer and due to the OS running in real mode it was actually possible to overwrite the 0x0 address with garbage and corrupt the kernel. You could do something like:
int i;
unsigned char* ptr = (unsigned char *)0x0;
for(i = 0; i < 1024; i++)
ptr[i] = 0x0;
Modern operating systems (e.g. Linux, Windows) run in protected mode which never gives you direct access to physical memory.
The processor will map the physical addresses to virtual addresses that your program will make use of.
It also keeps track of what you access and dare you touch something not belonging to you will you be in trouble (your program will segfault). This most definitely includes trying to dereference the 0x0 address.
When you "set the value of a pointer to zero" as in
int *p = 0;
it will not necessarily end up pointing to physical address zero, as you seem to believe. When a pointer is assigned a constant zero value (or initialized with it), the compiler is required to recognize that situation and treat it in a special way. The compiler is required to replace that zero with implementation-dependent null-pointer value. The latter does not necessarily point to zero address.
Null pointer value is supposed to be represented by a physical address that won't be used for any other purpose. If in some implementation physical address zero is a usable address, then such implementation will have to use a different physical address to represent null pointers. For example, some implementation might use address 0xFFFFFFFF for that purpose. In such implementation the initialization
int *p = 0;
will actually initialize p with physical 0xFFFFFFFF, not with physical zero.
P.S. You might want to take a look at the FAQ: http://c-faq.com/null/index.html, which is mostly dedicated to exactly that issue.
The value 0 has no special meaning. It is a convention to set a pointer to 0 and the C compiler has to interpret it accordingly. However, there is no connection to the physical address 0 and in fact, that address can be a valid address. In many systems though the lower adresses are containing hardware related adresses, like interrupt vectors or other. On the Amiga for example, the address 4 was the entry point into the operating system, which is also an arbitrary decision.
If the address of allocated space is zero, there is insufficient memory available. That means your variable could not be allocated.
The address at 0x0 is where the CPU starts executing when you power it on. Usually at this address there's a jump to the BIOS code and IIRC the first 64K (or more) are reserved for other tasks (determined by the BIOS/UEFI). It's an area which is not accessbile by an application.
Given that it should be clear that you cannot have a variable at address 0x0 in Windows.

Accessing values from i2c address

You can take a look at this website: http://www.hitechnic.com/cgi-bin/commerce.cgi?preadd=action&key=NSK1042 to get a better understanding of what I'm talking about. For example, the website reads: the i2c address of the sensor is 0x10 and the table of the values there reads:
Address Type Contents
00 – 07H chars Serial Version Number
43H byte Sensor 1 DC Signal Strength
How can I access these values in C? Thanks.
These registers can be memory mapped. A few things you'll need to do:
map the device's physical memory to your programs address space
declare any pointers to this region as volatile
The volatile keyword will stop the compiler from "optimizing" the program to be incorrect. e.g. by assuming that reads to the same memory location will yield the same result because the program hasn't written to it.
The easy part of this is to declare a struct such that all the offsets are the same as the device and that each part has the right size.
ie
struct hitech {
char serial_version[8];
char manufacturer[8];
/* etc */
};
volatile struct hitech *my_device;
The second part is working out where the device is mapped. If it's plugged in to your computer you should be able to see this. You might need to do one of the following: mmap the device's physical address. Or just write my_device = 0x< address >. Or a combination of the two.
From the website:
"The I2C address of the IRSeeker V2 sensor is 0x10"
So you want to write 0x10 above for my_device.
Then you'll need to compile for the correct micro-controller and load your program at the correct location as firmware.
You'd be better off using their programming language.
Assuming they're not supplying an SDK for you to access these values:
// I'm assuming these are read-only, hence the "const"
const char *g_serialVersionNumber = (const char *)0x00; // be careful not to access more than 8 bytes
const unsigned char *g_sensor1DCSignalStrength = (const unsigned char *)0x43;
void main()
{
printf("Serial version number: %s\n", g_serialVersionNumber);
printf("Sensor 1 DC Signal Strength: %d\n", *g_sensor1DCSignalStrength);
}

C pointers and the physical address

I'm just starting C. I have read about pointers in various books/tutorials and I understand the basics. But one thing I haven't seen explained is what are the numbers.
For example:
int main(){
int anumber = 10;
int *apointer;
apointer = &anumber;
printf("%i", &apointer);
}
may return a number like 4231168. What does this number represent? Is it some storage designation in the RAM?
Lots of PC programmer replies as always. Here is a reply from a generic programming point-of-view.
You will be quite interested in the actual numerical value of the address when doing any form of hardware-related programming. For example, you can access hardware registers in a computer in the following way:
#define MY_REGISTER (*(volatile unsigned char*)0x1234)
This code assumes you know that there is a specific hardware register located at address 0x1234. All addresses in a computer are by tradition/for convenience expressed in hexadecimal format.
In this example, the address is 16 bits long, meaning that the address bus on the computer used is 16-bits wide. Every memory cell in your computer has an address. So on a 16-bit address bus you could have a maximum of 2^16 = 65536 addressable memory cells.
On a PC for example, the address would typically be 32 bits long, giving you 4.29 billion addressable memory cells, ie 4.29 Gigabyte.
To explain that macro in detail:
0x1234 is the address of the register / memory location.
We need to access this memory location through a pointer, so therefore we typecast the integer constant 0x1234 into an unsigned char pointer = a pointer to a byte.
This assumes that the register we are interested in is 1 byte large. Had it been two bytes large, we would perhaps have used unsigned short instead.
Hardware registers may update themselves at any time (their contents are "volatile"), so the program can't be allowed to make any assumptions/optimizations of what's stored inside them. The program has to read the value from the register at every single time the register is used in the code. To enforce this behavior, we use the volatile keyword.
Finally, we want to access the register just as if it was a plain variable. Therefore the * is added, to take the contents of the pointer.
Now the specific memory location can be accessed by the program:
MY_REGISTER = 1;
unsigned char var = MY_REGISTER;
For example, code like this is used everywhere in embedded applications.
(But as already mentioned in other replies, you can't do things like this in modern PCs, since they are using something called virtual addressing, giving you a slap on the fingers should you attempt it.)
It's the address or location of the memory to which the pointer refers. However, it's best if you regard this as an opaque quantity - you are never interested in the actual value of the pointer, only that to which it refers.
How the address then relates to physical memory is a service that the system provides and actually varies across systems.
That's a virtual address of anumber variable. Every program has its own memory space and that memory space is mapped to the physical memory. The mapping id done by the processor and the service data used for that is maintained by the operating system. So your program never knows where it is in the physical memory.
It's the address of the memory1 location where your variable is stored. You shouldn't care about the exact value, you should just know that different variables have different addresses, that "contiguous memory" (e.g. arrays) has contiguous addresses, ...
By the way, to print the address stored in a pointer you should use the %p specifier in printf.
Notice that I did not say "RAM", because in most modern OSes the "memory" your process sees is virtual memory, i.e. an abstraction of the actual RAM managed by the OS.
A lot of people told you, that the numeric value of a pointer will designate its address. This is one way how implementations can do it, but it is very important, what the C standard has to say about pointers:
The nil pointer has always numeric value 0 when operated on in the C programming language. However the actual memory containing the pointer may have any value, as long as this special, architecture dependent value is consistently treated nil, and the implementation takes care that this value is seen as 0 by C source code. This is important to know, since 0 pointers may appear as a different value on certain architectures when inspected with a low level memory debugger.
There's no requirement whatsoever that the values of the pointer are in any way related to actual addresses. They may be as well abstract identifiers, resolved by a LUT or similar.
If a pointer addresses an array, the rules of pointer arithmetic must hold, i.e. int array[128]; int a, b; a = (int)&array[120]; b = (int)&array[100]; a - b == 20 ; array + (a-b) == &array[20]; &array[120] == (int*)a
Pointer arithmetic between pointers to different objects is undefined and causes undefined behaviour.
The mapping pointer to integer must be reversible, i.e. if a number corresponds to a valid pointer, the conversion to this pointer must be valid. However (pointer) arithmetic on the numerical representation of pointers to different objects is undefined.
Yes, exactly that - it's the address of the apointer data in memory. Local variable such as anumber and apointer will be allocated in your program's stack, so it will refer to an address in the main() function's frame in the stack.
If you had allocated the memory with malloc() instead it would refer to a position in your program's heap space. If it was a fixed string it may refer to a location in your program's data or rodata (read-only data) segments instead.
in this case &apointer represent the address in RAM memory of the pointer variable apointer
apointer is the "address" of the variable anumber. In theory, it could be the actual physical place in RAM where the value of anumber is stored, but in reality (on most OS'es) it's likely to be a place in virtual memory. The result is the same though.
It's a memory address, most likely to the current location in your program's stack. Contrary to David's comment, there are times when you'll calculate pointer offsets, but this is only if you have some kind of array that you are processing.
It's the address of the pointer.
"anumber" takes up some space in RAM, the data at this spot contains the number 10.
"apointer" also takes up some space in RAM, the data at this spot contains the location of "anumber" in RAM.
So, say you have 32 bytes of ram, addresses 0..31
At e.g. position 16 you have 4 bytes, the "anumber" value 10
At e.g. position 20 you have 4 bytes, the "apointer" value 16, "anumber"'s position in RAM.
What you print is 20, apointer's position in RAM.
Note that this isn't really directly in RAM, it's in virtual address space which is mapped to RAM. For the purpose of understanding pointers you can completely ignore virtual address space.
it is not the address of the variable anumber that is printed but it is the address of the pointer which gets printed.look carefully.had it been just "apointer",then we would have seen the address of the anumber variable.

Resources