Ethernet is stopped after using global variables in STM32 - c

I am using STM32H735ZGTx_ LQFP144.
I have done Ethernet configuration according to this link https://controllerstech.com/stm32-ethernet-1-connection/
If I am not using any global in any of the source file, I am not facing any issue, Ethernet is working fine.
Whenever I declare and use global variables or static variables, I am facing some issue and the Ethernet is stopped.
I am getting different errors based on the global variables size, as follows
char buff[5] = "123"; //initialized
Error : Hard Fault error
char buff[100]; // uninitialized
Error: ssertion "pc>custom_free_function != NULL" failed at line 767 in../Middlewares/Third_Party/LwIP/sr/core/pbuf.c
char buff[200]: //uninitialized
Error:
Assertion "pbuf_free: p->ref > 0" failed at line 753 in../Middlewares/Third_Party/LwIP/src/core/pbuf.c
Error: Assertion "mem_trim: legal memory" failed at line 721 in ../Middlewares/Third_Party/LwIP/src/core/mem.c
Changing the variable size is changing the errors.
I'm new to STM, please help me with this issue.
I have checked the address of global variables, all are stored in RAM_D1.
In the hard fault, I noticed that, it's causing error when it is in pbuf_free() function.

I have found that, some of the STM32H7x series have the ethernet issues.
Although we configured the address in CubeMX in ethernetif.c but we need to place RX_POOL buffer at specific RAM.
/* USER CODE BEGIN 2 */
#if defined ( __GNUC__ ) /* GNU Compiler */
__attribute__((section(".Rx_PoolSection"))) extern u8_tmemp_memory_RX_POOL_base[];
#endif
/* USER CODE END 2 */
Modify the linkerscript (*.ld) that the ETH descriptors and buffers are located in D2 SRAM. Also it is recommended to place all RAM to RAM_D1. In STM32CubeMX generated project, the "_FLASH" suffix linkerscript should be modified, which is used by default
(e.g.: STM32H750XBHx_FLASH.ld). The "_RAM" suffix linkerscript is template for executing code from internal RAM memory.
} >RAM_D1
/* Modification start */
.lwip_sec (NOLOAD) :
{
. = ABSOLUTE(0x30040000);
*(.RxDecripSection)
. = ABSOLUTE(0x30040060);
*(.TxDecripSection)
/* Rx_Pool section must be added in linker */
. = ABSOLUTE(0x30040200);
*(.Rx_PoolSection)
} >RAM_D2
After these modifications, my issue was resolved.
Rx_Pool stores the pbufs, but due to improper alignment it was over writing some of the pbuf pointers.
Note: If you are storing Rx_Pool in some other ram or address, the alignment must be proper, or else it wil over write again.

Related

`.bss' is not within region `ram' error - esp32 ulp risv-v

I'm trying to copy array elements from one array to another.
One of the arrays uses a int32_t index to track the current index.
int32_t pres_log_last_5_readings_raw[5];
int32_t final_arr_pressure[100];
int32_t final_array_index = 0;
for (int i = 0; i < 4; i++)
{
final_arr_pressure[final_array_index] = pres_log_last_5_readings_raw[i]; // This line will compile without error if the next line is commented
final_array_index++;
}
If the line is active I will get this error:
/Users/user/.espressif/tools/riscv32-esp-elf/esp-2022r1-11.2.0/riscv32-esp-elf/bin/../lib/gcc/riscv32-esp-elf/11.2.0/../../../../riscv32-esp-elf/bin/ld: address 0x10dc of ulp_main section `.bss' is not within region `ram'
/Users/user/.espressif/tools/riscv32-esp-elf/esp-2022r1-11.2.0/riscv32-esp-elf/bin/../lib/gcc/riscv32-esp-elf/11.2.0/../../../../riscv32-esp-elf/bin/ld: address 0x10dc of ulp_main section `.bss' is not within region `ram'
collect2: error: ld returned 1 exit status
I've tried many variations but nothing seems to work, I'm not sure if the problem is with my C skills or with the esp idf compiler.
**
Solved:
**
The right answer is indeed me running out if memory, reducing the size of the arrays to 50 fixed the problem.
I do need to mention that I should have increased the memory allocation for slow rtc memory from 4kb to 8kb in idf.py menuconfig! This would solve the problem without changing the size to 50 (in my case).
Thanks everyone
This error message says that your uninitialized static storage data (stored in the .bss section) is too big. Basically, you ran out of RAM. You need to reduce the number and size of your variables.

Keil uVision - Atmel SAM3U Read Unique Identifier

I've been trying to read the Unique Identifier (UID) from a Atmel SAM3U MCU, but it's proven more difficult than it needs to be to make it happen. Does anyone have any examples or can suggest how to read it properly? Whenever I do, I wait in a do while loop (like the documentation states) for the EEFC (Flash ROM) status register to change states, but it never does so the MCU is then stuck in a loop.
Here is the code I'm using
// must run this from SRAM
__attribute__((section(".ARM.__at_0x20080000"))) void Get_Unique_ID(unsigned int *pdwUniqueID)
{
Efc *p_efc;
unsigned int status;
// clear the array
pdwUniqueID[0] = 0;
pdwUniqueID[1] = 0;
pdwUniqueID[2] = 0;
pdwUniqueID[3] = 0;
// send the Start Read Unique Identifier command (STUI) by writing the Flash Command Register with the STUI command
p_efc->EEFC_FCR = EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FCMD_STUI;
// wait for the Flash Programming Status Register (EEFC_FSR) to fall
do { status = p_efc->EEFC_FSR; }
while ((status & EEFC_FSR_FRDY) == EEFC_FSR_FRDY);
// the Unique Identifier is located in the first 128 bits of the Flash memory mapping
pdwUniqueID[0] = *(unsigned int *)IFLASH0_ADDR;
pdwUniqueID[1] = *(unsigned int *)(IFLASH0_ADDR + 4);
pdwUniqueID[2] = *(unsigned int *)(IFLASH0_ADDR + 8);
pdwUniqueID[3] = *(unsigned int *)(IFLASH0_ADDR + 12);
// to stop the Unique Identifier mode, the user needs to send the Stop Read unique Identifier
// command (SPUI) by writing the Flash Command Register with the SPUI command
p_efc->EEFC_FCR = EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FCMD_SPUI;
// when the Stop Read Unique Unique Identifier command (SPUI) has been performed
// the FRDY bit in the Flash Programming Status Register (EEFC_FSR) rises
do { status = p_efc->EEFC_FSR; }
while ((status & EEFC_FSR_FRDY) != EEFC_FSR_FRDY);
}
Note that __attribute__((section(".ARM.__at_0x20080000"))) isn't the best method to dynamically assign this function to SRAM via the linker and any suggestions on how to make it more dynamic would be appreciated.
SOLVED The problem was the chips I had were fake so SAM-BA was returning whatever was at the SRAM buffer address it specified. It's a bug in SAM-BA since if it received 0x00000000, it should give an error or warning message and then stop reading. Do not buy fake chips from China!
Thanks.
I don't believe p_efc is correctly initialized.
You create a pointer to a Efc datastructure which thus points to something.
You then write something to somewhere and are expect it to work.
Efc *p_efc;
p_efc->EEFC_FCR = EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FCMD_STUI;
My guess would be that you need to intialize it to the correct EEFC base address. The datasheet has the following to say:
The SAM3U4 (256 Kbytes internal Flash
version) embeds two EEFC (EEFC0 for Flash0 and EEFC1 for Flash1)
whereas the SAM3U2/1 embeds one EEFC.
So depending on your MCU version you need to address EEFC0 or EEFC1. I'm assuming that you use libopencm3 but this will work for any other library. Look for the EEFC location define. Following the defines/files/links we get to this page, it tells us to point our Efc pointer to EEFC0_BASE or EEFC1_BASE. I would advise you to use the EEFC0 and EEFC1 defines though as it makes your code portabler.
So your code should work if your Efc is located in EEFC0 if you do:
Efc *p_efc = EEFC0;

Migrating from armcc to armclang causes section type conflict

Since ARM Keil µVision has changed their default compiler from armcc to armclang, I am trying to migrate an existing project. Most of the new errors generated by simply switching the compiler have been easily cleaned up with help from their migration guides. But I cannot seem to fix the following error:
Display/icons.c(54): error: 'iconBitMapArray' causes a section type conflict with 'pNULL'
XRAM_VAR GUI_CONST_STORAGE GUI_BITMAP *iconBitMapArray[] = // bitmaps for icons
^
Display/icons.c(44): note: declared here
XRAM_VAR GUI_CONST_STORAGE GUI_BITMAP pNULL = // used for returning a pointer to a NULL image
Here is the relevant code:
#define XRAM_VAR __attribute__((section("XRAM_DATA")))
XRAM_VAR uint32_t icon_status_internal;
XRAM_VAR GUI_CONST_STORAGE GUI_BITMAP pNULL = // used for returning a pointer to a NULL image
{
0, /* XSize */
0, /* YSize */
0, /* BytesPerLine */
0, /* BitsPerPixel */
0, /* Pointer to picture data (indices) */
0 /* Pointer to palette */
};
XRAM_VAR GUI_CONST_STORAGE GUI_BITMAP *iconBitMapArray[] = // bitmaps for icons
{
&pNULL,
&bmIcon1,
&bmIcon2,
// ...etc.
};
The variable icon_status_internal also causes the same error.
There are several similar questions on here and the error seems to come from trying to place zero initialized data into the same section as data that is initialized to non-zero values. The flag, -fno-zero-initialized-in-bss is mentioned a lot. I tried adding that to the compiler string and the linker string but that did not help.
On page 8 of the migration guide, there are two possible solutions they suggest when using Link-Time Optimization (LTO). We are not using LTO but I tried them both anyways. I tried changing the memory assignment from default to our external RAM.
This also did not help. The other suggestion was to add the section name to the scatter file which was already there.
; Just the variables declared using __attribute__((section("XRAM_DATA")))
RW_XRAM1 0x70000000 0x00010000 { ; External Ram
*.o(XRAM_DATA)
}
When using armcc the variables are placed in a Data type section in the section labeled "XRAM_DATA" without any special compiler options as can be seen in the .map file:
pNULL 0x70000000 Data 20 icons.o(XRAM_DATA)
icon_status_internal 0x70000014 Data 4 icons.o(XRAM_DATA)
iconBitMapArray 0x70000018 Data 352 icons.o(XRAM_DATA)
How do I get all of these variables to be placed in a Data type section in my external RAM when using armclang like armcc did? Are there any other workarounds that I can try? Do I need to create seperate bss and data sections in my external RAM? What else can I try?

Copying a function to RAM

I am working on a Embedded application which has a power on initialization routine which is followed by a infinite loop which contains resident program(residing in ROM memory) and a remote program(residing in RAM memory). something like below shown code
main()
{
por(); // power on initialization
while(1)
{
if(Flag == Resident) // enum Flag{Resident,Remote};
ResidentProgram();
else
RemoteProgram(); // program which needs to placed in RAM area of memory
}
}
Here in the por() routine i want to copy the RemoteProgram() in to RAM. later if needed i will call the change to the Flag to Remote. After that i want the program to be executed from RAM location
Here i am not able to place RemoteProgram() function in RAM. kindly help
Using linker scripts we will be able to place the function at desired address only before runtime but not during runtime. here i want to place it during runtime. can anyone help solving this problem. also i dont want to disturb locations occupied by other variables.
My suggestion (from a position of at least some experience) is to do it the way operating systems do it: use a proper object file format, and load it yourself.
You cannot expect to be able to, in C, copy an already-compiled function around, since you won't have access to any basic information about the code in the function (not even its size, much less any relocation information).
So, embed the code as an ELF or some other format, and write an ELF loader which can load the data to the desired location. Being "on the inside" of the format will let you properly implement relocation if needed.
Your program needs to know where in read only memory your function code is located. One way to do that is to place the code in a 'named section'. Your function needs to be compiled so that it will run correctly at the ram location you are going to copy it to. So the function has a load location and a runtime location. Then you can write some C code to copy the function code from it's load location to it's runtime location.
Precisely how you do all of this, as has already been said, is platform and tool chain specific. Here is one method:
C Source code:
#include <string.h>
void copy_code_to_ram()
{
extern char _my_code_in_ram_start_space[];
extern char _my_code_in_ram_end_space[];
extern char _my_code_in_ram_start[];
memcpy(_my_code_in_ram_start, _my_code_in_ram_start_space, _my_code_in_ram_end_space - _my_code_in_ram_start_space);
}
int i;
#pragma code_seg("my_code_in_ram")
void function_in_sram()
{
i++;
}
#pragma code_seg()
void main()
{
copy_code_to_ram();
function_in_sram();
while (1);
}
Linker script extracts:
MEMORY
{
rom: origin = 0XFFF00000, length = 0X100000
ram: origin = 0X00000000, length = 0X800000
}
SECTIONS
{
____CODE :
{
<additional script here>
__my_code_in_ram_start_space = .;
. += sizeof(my_code_in_ram);
__my_code_in_ram_end_space = .;
} > rom
__my_code_in_ram LOAD(__my_code_in_ram_start_space) :
{
__my_code_in_ram_start = .;
*(my_code_in_ram)
__my_code_in_ram_end = .;
} > ram
<additional script here>
}
A video which shows a toolchain specific way of generating this C code and linker script might be useful:
http://www.crossware.com/coldfire/CCodeInRamVideo

Does Linux kernel have main function?

I am learning Device Driver and Kernel programming.According to Jonathan Corbet book we do not have main() function in device drivers.
#include <linux/init.h>
#include <linux/module.h>
static int my_init(void)
{
return 0;
}
static void my_exit(void)
{
return;
}
module_init(my_init);
module_exit(my_exit);
Here I have two questions :
Why we do not need main() function in Device Drivers?
Does Kernel have main() function?
start_kernel
On 4.2, start_kernel from init/main.c is a considerable initialization process and could be compared to a main function.
It is the first arch independent code to run, and sets up a large part of the kernel. So much like main, start_kernel is preceded by some lower level setup code (done in the crt* objects in userland main), after which the "main" generic C code runs.
How start_kernel gets called in x86_64
arch/x86/kernel/vmlinux.lds.S, a linker script, sets:
ENTRY(phys_startup_64)
and
phys_startup_64 = startup_64 - LOAD_OFFSET;
and:
#define LOAD_OFFSET __START_KERNEL_map
arch/x86/include/asm/page_64_types.h defines __START_KERNEL_map as:
#define __START_KERNEL_map _AC(0xffffffff80000000, UL)
which is the kernel entry address. TODO how is that address reached exactly? I have to understand the interface Linux exposes to bootloaders.
arch/x86/kernel/vmlinux.lds.S sets the very first bootloader section as:
.text : AT(ADDR(.text) - LOAD_OFFSET) {
_text = .;
/* bootstrapping code */
HEAD_TEXT
include/asm-generic/vmlinux.lds.h defines HEAD_TEXT:
#define HEAD_TEXT *(.head.text)
arch/x86/kernel/head_64.S defines startup_64. That is the very first x86 kernel code that runs. It does a lot of low level setup, including segmentation and paging.
That is then the first thing that runs because the file starts with:
.text
__HEAD
.code64
.globl startup_64
and include/linux/init.h defines __HEAD as:
#define __HEAD .section ".head.text","ax"
so the same as the very first thing of the linker script.
At the end it calls x86_64_start_kernel a bit awkwardly with and lretq:
movq initial_code(%rip),%rax
pushq $0 # fake return address to stop unwinder
pushq $__KERNEL_CS # set correct cs
pushq %rax # target address in negative space
lretq
and:
.balign 8
GLOBAL(initial_code)
.quad x86_64_start_kernel
arch/x86/kernel/head64.c defines x86_64_start_kernel which calls x86_64_start_reservations which calls start_kernel.
arm64 entry point
The very first arm64 that runs on an v5.7 uncompressed kernel is defined at https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L72 so either the add x13, x18, #0x16 or b stext depending on CONFIG_EFI:
__HEAD
_head:
/*
* DO NOT MODIFY. Image header expected by Linux boot-loaders.
*/
#ifdef CONFIG_EFI
/*
* This add instruction has no meaningful effect except that
* its opcode forms the magic "MZ" signature required by UEFI.
*/
add x13, x18, #0x16
b stext
#else
b stext // branch to kernel start, magic
.long 0 // reserved
#endif
le64sym _kernel_offset_le // Image load offset from start of RAM, little-endian
le64sym _kernel_size_le // Effective size of kernel image, little-endian
le64sym _kernel_flags_le // Informative flags, little-endian
.quad 0 // reserved
.quad 0 // reserved
.quad 0 // reserved
.ascii ARM64_IMAGE_MAGIC // Magic number
#ifdef CONFIG_EFI
.long pe_header - _head // Offset to the PE header.
This is also the very first byte of an uncompressed kernel image.
Both of those cases jump to stext which starts the "real" action.
As mentioned in the comment, these two instructions are the first 64 bytes of a documented header described at: https://github.com/cirosantilli/linux/blob/v5.7/Documentation/arm64/booting.rst#4-call-the-kernel-image
arm64 first MMU enabled instruction: __primary_switched
I think it is __primary_switched in head.S:
/*
* The following fragment of code is executed with the MMU enabled.
*
* x0 = __PHYS_OFFSET
*/
__primary_switched:
At this point, the kernel appears to create page tables + maybe relocate itself such that the PC addresses match the symbols of the vmlinux ELF file. Therefore at this point you should be able to see meaningful function names in GDB without extra magic.
arm64 secondary CPU entry point
secondary_holding_pen defined at: https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L691
Entry procedure further described at: https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L691
Fundamentally, there is nothing special about a routine being named main(). As alluded to above, main() serves as the entry point for an executable load module. However, you can define different entry points for a load module. In fact, you can define more than one entry point, for example, refer to your favorite dll.
From the operating system's (OS) point of view, all it really needs is the address of the entry point of the code that will function as a device driver. The OS will pass control to that entry point when the device driver is required to perform I/O to the device.
A system programmer defines (each OS has its own method) the connection between a device, a load module that functions as the device's driver, and the name of the entry point in the load module.
Each OS has its own kernel (obviously) and some might/maybe start with main() but I would be surprised to find a kernel that used main() other than in a simple one, such as UNIX! By the time you are writing kernel code you have long moved past the requirement to name every module you write as main().
Hope this helps?
Found this code snippet from the kernel for Unix Version 6. As you can see main() is just another program, trying to get started!
main()
{
extern schar;
register i, *p;
/*
* zero and free all of core
*/
updlock = 0;
i = *ka6 + USIZE;
UISD->r[0] = 077406;
for(;;) {
if(fuibyte(0) < 0) break;
clearsig(i);
maxmem++;
mfree(coremap, 1, i);
i++;
}
if(cputype == 70)
for(i=0; i<62; i=+2) {
UBMAP->r[i] = i<<12;
UBMAP->r[i+1] = 0;
}
// etc. etc. etc.
Several ways to look at it:
Device drivers are not programs. They are modules that are loaded into another program (the kernel). As such, they do not have a main() function.
The fact that all programs must have a main() function is only true for userspace applications. It does not apply to the kernel, nor to device drivers.
With main() you propably mean what main() is to a program, namely its "entry point".
For a module that is init_module().
From Linux Device Driver's 2nd Edition:
Whereas an application performs a single task from beginning to end, a module registers itself in order to serve future requests, and its "main" function terminates immediately. In other words, the task of the function init_module (the module's entry point) is to prepare for later invocation of the module's functions; it's as though the module were saying, "Here I am, and this is what I can do." The second entry point of a module, cleanup_module, gets invoked just before the module is unloaded. It should tell the kernel, "I'm not there anymore; don't ask me to do anything else."
Yes, the Linux kernel has a main function, it is located in arch/x86/boot/main.c file. But Kernel execution starts from arch/x86/boot/header.S assembly file and the main() function is called from there by "calll main" instruction.
Here is that main function:
void main(void)
{
/* First, copy the boot header into the "zeropage" */
copy_boot_params();
/* Initialize the early-boot console */
console_init();
if (cmdline_find_option_bool("debug"))
puts("early console in setup code.\n");
/* End of heap check */
init_heap();
/* Make sure we have all the proper CPU support */
if (validate_cpu()) {
puts("Unable to boot - please use a kernel appropriate "
"for your CPU.\n");
die();
}
/* Tell the BIOS what CPU mode we intend to run in. */
set_bios_mode();
/* Detect memory layout */
detect_memory();
/* Set keyboard repeat rate (why?) and query the lock flags */
keyboard_init();
/* Query Intel SpeedStep (IST) information */
query_ist();
/* Query APM information */
#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
query_apm_bios();
#endif
/* Query EDD information */
#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
query_edd();
#endif
/* Set the video mode */
set_video();
/* Do the last things and invoke protected mode */
go_to_protected_mode();
}
While the function name main() is just a common convention (there is no real reason to use it in kernel mode) the linux kernel does have a main() function for many architectures, and of course usermode linux has a main function.
Note the OS runtime loads the main() function to start an app, when an operating system boots there is no runtime, the kernel is simply loaded to a address by the boot loader which is loaded by the MBR which is loaded by the hardware. So while a kernel may contain a function called main it need not be the entry point.
See Also:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633559%28v=vs.85%29.aspx
Linux kernel source:
x86: linux-3.10-rc6/arch/x86/boot/main.c
arm64: linux-3.10-rc6/arch/arm64/kernel/asm-offsets.c

Resources