Is there a way of allowing the ARM RealView/Keil linker to select from more than one but not all regions?
I can use the .ANY specifier to allow the linker free reign, and I can use a *(section_name) specifier to locate specific objects to a single region, but I would like to be able to have the linker select from several suitable regions.
To make it clear why this is desirable, I am using an STM32F4xx microcontroller with three distinct but contiguous SRAM regions on separate busses, and a further core-coupled memory region which is not bit-bandable or accessible by DMA. I want to use the smaller SRAM region for DMA to avoid bus contention and improve performance and deterministic behaviour. This region is inconveniently located between two larger regions and I want to be able to use either of the larger regions for bit banding while allowing the CCM memory be used freely for any purpose other than DMA or bit-banding. The regions are currently specified as follows:
RW_IRAM0 CCM_START CCM_SIZE
{
.ANY (+RW +ZI)
*(core_coupled_ram)
}
; SRAM1 112Kbytes (Buses: I, D, S, DMA)
RW_IRAM1 SRAM1_START SRAM1_SIZE
{
.ANY (+RW +ZI)
}
; SRAM2 16Kbytes (Buses: S, DMA)
RW_IRAM2 SRAM2_START SRAM2_SIZE
{
*(dma_ram)
}
; SRAM3 64Kbytes (Buses: S, DMA)
RW_IRAM3 SRAM3_START SRAM3_SIZE
{
.ANY (+RW +ZI)
}
Adding *(bitbandable_ram) to SRAM1 and SRAM3 is rejected by the linker with:
Error: L6223E: Ambiguous selectors found for ctransmissionschedulertask.o(bitbandable_ram) from Exec regions RW_IRAM1 and RW_IRAM3.
Similarly creating an overlapping region does not work:
; SRAM 192Kbytes
RW_IRAM_ALL SRAM_START (SRAM1_SIZE + SRAM2_SIZE + SRAM3_SIZE)
{
.ANY (+RW +ZI)
*(bitbandable_ram)
}
reporting:
Error: L6221E: Execution region RW_IRAM1 with Execution range [0x20000000,0x200045e0) overlaps with Execution region RW_IRAM_ALL with Execution range [0x20000000,0x20018608).
Currently my less-than-ideal solutions are:
to nominate only one region for bit-banding,
to make all the contiguous SRAM a single region (allowing DMA operations may occur in any memory making it less deterministic) thus:
; SRAM 192Kbytes
RW_IRAM_ALL SRAM_START (SRAM1_SIZE + SRAM2_SIZE + SRAM3_SIZE)
{
.ANY (+RW +ZI)
*(bitbandable_ram)
*(dma_ram)
}
Removing the .ANY selector from the CCM region requiring object to be explicitly located there using the core_coupled_ram named section.
Related
I am working on pulling data from Retroarch via JSON format using software called Gamehook.
I am trying to add support for the PSX with the Beetle PSX HW core.
The issue is that even with extensive searching through the core and retroarch's source code, I cannot find a proper memory map for it. I have tried adding the ranges from the two sources below, which gives me a "no memory map defined" error.
http://www.raphnet.net/electronique/psx_adaptor/Playstation.txt
0x8000_0000 0x801f_ffff Kernel and User Memory Mirror (2 Meg) Cached
0xa000_0000 0xa01f_ffff Kernel and User Memory Mirror (2 Meg) Uncached
0x0000_0000-0x0000_ffff Kernel (64K)
0x0001_0000 0x001f_ffff User Memory (1.9 Meg)
0x1f80_0000-0x1f80_03ff Scratch Pad (1024 bytes)
and
https://psx-spx.consoledev.net/memorymap/
KUSEG KSEG0 KSEG1
00000000h 80000000h A0000000h 2048K Main RAM (first 64K reserved for BIOS)
1F000000h 9F000000h BF000000h 8192K Expansion Region 1 (ROM/RAM)
1F800000h 9F800000h -- 1K Scratchpad (D-Cache used as Fast RAM)
1F801000h 9F801000h BF801000h 8K I/O Ports
1F802000h 9F802000h BF802000h 8K Expansion Region 2 (I/O Ports)
1FA00000h 9FA00000h BFA00000h 2048K Expansion Region 3 (SRAM BIOS region for DTL cards)
1FC00000h 9FC00000h BFC00000h 512K BIOS ROM (Kernel) (4096K max)
FFFE0000h (in KSEG2) 0.5K Internal CPU control registers (Cache Control)
Looking at consoleinfo.c in the Retroarch source code, it defines the memory regions for Playstation as.
/* ===== PlayStation ===== */
/* http://www.raphnet.net/electronique/psx_adaptor/Playstation.txt */
static const rc_memory_region_t _rc_memory_regions_playstation[] = {
{ 0x000000U, 0x00FFFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Kernel RAM" },
{ 0x010000U, 0x1FFFFFU, 0x010000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
};
static const rc_memory_regions_t rc_memory_regions_playstation = { _rc_memory_regions_playstation, 2 };
And the definition for rc_memory_region_t is
typedef struct rc_memory_region_t {
unsigned start_address; /* first address of block as queried by RetroAchievements */
unsigned end_address; /* last address of block as queried by RetroAchievements */
unsigned real_address; /* real address for first address of block */
char type; /* RC_MEMORY_TYPE_ for block */
const char* description; /* short description of block */
}
rc_memory_region_t;
I have asked in the Retroarch discord server and they have unfortunately given me conflicting information. Such as, I want to use the RetroAcheievements memory address or that I need to find out how Retroarch deals with memory addressing of the cores.
With that second portion, I have looked at the entire source code in depth and have only found the memory addressing information here.
So my question comes down to, does anyone know either how to find the mappable memory addresses from the cores in Retroarch or does anyone happen to know the mappable memory addresses from the cores in Retroarch?
The way you write a program for a dual-core MCU is that each core executes its own separate binary.
I want to create a shared static variables in common source file for which static allocations will be managed by the linker, so that the addresses of individual variables are the same from the perspective of the two cores. Is there any way to achieve this without explicity assigning addresses?
Already I created overlapping memory region in linker scripts for both cores,
/* Specify the memory areas */
MEMORY
{
RAM4_D3_NO_CACHE (xrw) : ORIGIN = 0x38000000, LENGTH = 8K
}
.ram4_d3_non_cachable :
{
. = ALIGN(8);
_sram4_d3_non_cachable = .;
*(.ram4_d3_non_cachable)
*(.ram4_d3_non_cachable*)
. = ALIGN(8);
_eram4_d3_non_cachable = .;
} >RAM4_D3_NO_CACHE AT> FLASH
but then location of each symbol is different (as expected from two independent linking processes per binary):
.ram4_d3_non_cachable
0x0000000038001fc0 0x9c build/ic_queue.o
0x0000000038001fc0 isInit
0x0000000038001fc4 m4ToM7
0x0000000038001fd0 descriptors
0x0000000038002050 m7ToM4
*(.ram4_d3_non_cachable*)
.ram4_d3_non_cachable
0x0000000038001fc0 0x9c build/ic_queue.o
0x0000000038001fc0 m4ToM7
0x0000000038001fcc m7ToM4
0x0000000038001fd8 descriptors
0x0000000038002058 isInit
*(.ram4_d3_non_cachable*)
In my linker file, I have the following memory sections.
MEMORY
{
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k
mram (rwx) : ORIGIN = 0xA0000010, LENGTH = 1M
}
The actual address of the mram peripheral starts at 0xA0000000. In a C file I can write to a specific memory address as
(*(uint32_t *)(void *)0xA0000000) = 0xaabbccdd;
Will this cause any problems?
The space specified in the linker script defines where and what the linker can locate in the defined memory regions. It will not locate anything to the "hole" you have left at 0xA0000000 to 0xA000000F because it is not aware of it.
In that sense it is "safe" in that the linker will not attempt to use that space. It is entirely in the control of your code - you have taken responsibility for that region by not giving it to the linker. And indeed the statement:
(*(uint32_t *)(void *)0xA0000000) = 0xaabbccdd;
will write a 32 bit value to that location. The point is neither the compiler nor the linker will prevent you from doing what you will in that region.
What is less plausible is LENGTH = 1M. That would make your mram 0x100010 bytes long (i.e. 1M + 0x10). That is a problem because the linker is free to locate objects in the region 0x100000 to 0x10000F. The consequences of that depend on your hardware, but quite possibly it will wrap into the region 0x100000 to 0x10000F that you have attempted to hide from the linker. I would imagine that you need LENGTH = 1M - 0x10 or LENGTH = 0x0FFFF0.
Now while you can absolve the linker from managing that region in order to manage it in your code, it may not be the best approach. It would be better to create a linker symbol at the required absolute address.
So given:
MEMORY
{
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k
mram (rwx) : ORIGIN = 0xA0000000, LENGTH = 1M
}
Your would create a linker symbol in mram:
SECTIONS
{
...
.mram :
{
reserved_mram = 0 ; /* address is zero offset from .mram1 */
. += 0x10 ; /* create 16 byte "hole" at address reserved_mram */
... /* other mram linker assignment follows */
} < mram
...
}
Then in your code you should be able to declare:
extern uint32_t reserved_mram[] ; // 4 x 32-bit word array.
And through reserved_mram you can access the memory at 0xA00000000 symbolically and the code is always in sync with the linker script so you can relocate the space easily without introducing a conflict.
Of course there is no bounds checking and no size information - you still need to confine your access to reserved_mram[0] to reserved_mram[3].
You might alternatively create a separate symbol for each location (with meaningful names specific to your application):
.mram :
{
reserved_mram1 = 0 ;
. += 4 ;
reserved_mram2 = . ;
. += 4 ;
reserved_mram3 = . ;
. += 4 ;
reserved_mram4 = . ;
. += 4 ;
... /* other mram linker usage follows */
} < mram
Then in your code:
extern uint32_t reserved_mram1 ;
extern uint32_t reserved_mram2 ;
extern uint32_t reserved_mram3 ;
extern uint32_t reserved_mram4 ;
Another alternative; you might create an independent section for the region, then create variables within it using __attribute__((section(.xxx))) directives in the code. e.g:
MEMORY
{
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k
mram1 (rwx) : ORIGIN = 0xA0000000, LENGTH = 0x10
mram2 (rwx) : ORIGIN = 0xA0000010, LENGTH = 0xFFFF0
}
SECTIONS
{
...
.mram1 :
{
*(.mram1)
} < mram1
...
}
Then in your code:
uint32_t my_mram_data __attribute__ ((section (".mram1"))) ;
The variable my_mram_data will be created somewhare in .mram1, but the linker decides where. The advantage here is that you can create arbitrary variables in the code without modifying the linker script, and if you attempt to allocate to .mram1 more data than is available you will get a linker error.
Note that linker script syntax is arcane and varies between linkers - I am unassuming this relates to the GNU linker? But my linker foo is strictly on demand (i.e. I figure it out when I need to) and I make no claim that any of the above is complete or correct, or even the only possible solutions - regard it as illustrative, and refer to the linker documentation for accurate information.
I want to implement a stack usage monitor for my NRF52840-Mikrocontroller with Segger Embedded Studio.
To monitor the maximum stack usage, I need some information during runtime like the end address of the .bss segment, which is the start of my free memory.
My approach is, to fill the ram from the .tbss section until to the stackpointer with a magic word.
During runtime, the stack will grow and will overwrite my magic words with data.
In a cyclic check, I am able to dedicate the end of the stack. From that information, I can derive the approximate stack usage.
Is it possible to get the addresses from the picture below during runtime in my c (or ASM) Program?
Here is a part of my .map file, where for example the symbol __bss_start is defined. Is it possible to access this symbol from c code?
*(COMMON)
0x0000000020020ec4 __bss_end__ = (__bss_start__ + SIZEOF (.bss))
0x000000000001b8c8 __bss_size__ = SIZEOF (.bss)
0x0000000020020ec4 __bss_load_end__ = __bss_end__
0x0000000000000001 . = ASSERT (((__bss_start__ == __bss_end__) || ((__bss_end__ - __RAM_segment_start__) <= __RAM_segment_size__)), error: .bss is too large to fit in RAM memory segment)
0x0000000020020ec4 __tbss_load_start__ = ALIGN (__bss_end__, 0x4)
Thanks for your help, I have solved the Problem now.
To access the symbols of the .map file during runtime, I used the following code:
extern char __bss_end__;
int main()
{
char * bss = &__bss_end__;
}
After this line of code, the bss variable contains the start address of the bss-section.
With this code, I am able to get the addresses of the RAM segments during runtime to monitor my stack usage.
I want to find the physical address of a variable defined in a user-space process? Is there any way to do it using root privileges?
#include "stdio.h"
#include "unistd.h"
#include "inttypes.h"
uintptr_t vtop(uintptr_t vaddr) {
FILE *pagemap;
intptr_t paddr = 0;
int offset = (vaddr / sysconf(_SC_PAGESIZE)) * sizeof(uint64_t);
uint64_t e;
// https://www.kernel.org/doc/Documentation/vm/pagemap.txt
if ((pagemap = fopen("/proc/self/pagemap", "r"))) {
if (lseek(fileno(pagemap), offset, SEEK_SET) == offset) {
if (fread(&e, sizeof(uint64_t), 1, pagemap)) {
if (e & (1ULL << 63)) { // page present ?
paddr = e & ((1ULL << 54) - 1); // pfn mask
paddr = paddr * sysconf(_SC_PAGESIZE);
// add offset within page
paddr = paddr | (vaddr & (sysconf(_SC_PAGESIZE) - 1));
}
}
}
fclose(pagemap);
}
return paddr;
}
As partially answered before, normal programs should not need to worry about physical addresses as they run in a virtual address space with all its conveniences. Furthermore, not every virtual address has a physical address, the may belong to mapped files or swapped pages. However, sometimes it may be interesting to see this mapping, even in userland.
For this purpose, the Linux kernel exposes its mapping to userland through a set of files in the /proc. The documentation can be found here. Short summary:
/proc/$pid/maps provides a list of mappings of virtual addresses together with additional information, such as the corresponding file for mapped files.
/proc/$pid/pagemap provides more information about each mapped page, including the physical address if it exists.
This website provides a C program that dumps the mappings of all running processes using this interface and an explanation of what it does.
First, why would you want to do this? The purpose of modern VM systems is to remove the application programmer from the complexity of physocal memory layout. Gving them each their own uniform address space to make their life easyer.
If you did want to do this you would almost certanly need to use a kernel module. Get the virtual address of the variable in the normal way, use this to index into the processes page tables and read the value you find(the physical address of the frame). Then add the page offset to get the complete physical address. Note you wont be able to use this address while paging is enabled.
(If your lucky you may be able to get the frame address of a VM region from the /proc file system and thus wouldnt require to write a kernel module.)