Auto-Allocate to a specific RAM Area in GCC/C - c

Sorry for my english, its a bit hard for me to explain what exactly i would need.
I'm making some extra code into existing binarys using the GCC compiler.
In this case, its PowerPC, but it should not really matter.
I know, where in the existing binary i have free ram available (i dumped the full RAM to make sure) but i need to define each RAM address manually, currently i am doing it like this:
// #ram.h
//8bit ram
uint8_t* xx1 = (uint8_t*) 0x807F00;
uint8_t* xx2 = (uint8_t*) 0x807F01;
//...and so on
// 16bit ram
uint16_t* xxx1 = (uint16_t*) 0x807F40;
uint16_t* xxx2 = (uint16_t*) 0x807F42;
//...and so on
// 32bit ram
uint32_t* xxxx1 = (uint32_t*) 0x807FA0;
uint32_t* xxxx2 = (uint32_t*) 0x807FA4;
//...and so on
And im accessing my variables like this:
void __attribute__ ((noinline)) silly_demo_function() {
#include "ram.h"
if (*xxx2>*xx1) {
*xxx3 = *xxx3 + *xx1;
}
return;
}
But this gets really boring, if i want to patch my code into another existing binary, where the location of available/free/unused ram can be fully different, or if im replacing/removing some value in the middle. I am using 8, 16 and 32bit variables.
Is there a way, i can define an area like 0x807F00 to 0x00808FFF, and allocate my variables on the fly, and the compiler will allocate it inside my specific location?

I suspect that the big problem here is that those addresses are memory mapped IO (devices) and not RAM; and should not be treated as RAM.
Further, I'd say that you probably should be hiding the "devices that aren't RAM" behind an abstract layer, a little bit like a device driver; partly so that you can make sure that the compiler complies with any constraints caused by it being IO and not RAM (e.g. treated as volatile, possibly honoring any access size restrictions, possibly taking care of any cache coherency management); partly so that you/programmers know what is normal/fast/cached RAM and what isn't; partly so that you can replace the "device" with fake code for testing; and partly so that it's all kept in a single well defined area.
For example; you might have a header file called "src/devices.h" that contains:
#define xx1_address 0x807F00
..and the wrapper code might be a file called "src/devices/xx1.c" that contains something like:
#include "src/devices.h"
static volatile uint8_t * xx1 = (uint8_t*) xx1_address;
uint8_t get_xx1(void) {
return *xx1;
}
void set_xx1(uint8_t x) {
*xx1 = x;
}
However; depending on what these devices actually are, you might need/want some higher level code. For example, maybe xx1 is a temperature sensor and it doesn't make any sense to try to set it, and you want it to scale that raw value so it's "degrees celsius", and the highest bit of the raw value is used to indicate an error condition (and the actual temperature is only 7 bits), so the wrapper might be more like:
#include "src/devices.h"
#define xx1_offset -12.34
#define xx1_scale 1.234
static volatile uint8_t * xx1 = (uint8_t*) xx1_address;
float get_xx1_temperature(void) {
uint8_t raw_temp = *xx1;
if(raw_temp * 0x80 != 0) {
/* Error flag set */
return NAN;
}
/* No error */
return (raw_temp + xx1_offset) * xx1_scale;
}

In the meanwhile, i figured it out.
Its just as easy as defining .data, .bsss and .sbss in the linker directives.
6 Lines of code and its working like a charm.

Related

STM32, variable stored in flash could not be updated in other file

I use a STM32F411RE.
Since I've no more memory in my RAM. I decided to store large variable in my flash. For that I created a section in section.ld.
.large_buffer: ALIGN(4)
{
. = ALIGN(4) ;
*(.large_buffer.large_buffer.*)
. = ALIGN(4) ;
} >FLASH
In the main.c file, I declare the variable as follow :
uint8_t buffer[60 * 200] __attribute__ ((section(".large_buffer"), used));
At this point everything is OK, the buffer is not stocked in the RAM (bss), I can access it and rewrite it.
buffer[25] = 42;
printf("%d\n", buffer[25]); // 42
The problem comes when I want to edit the variable from an other file.
main.c
uint8_t buffer[60 * 200] __attribute__ ((section(".large_buffer"), used));
int main()
{
myFunc(buffer);
}
other.c
myFunc(uint8_t* buffer)
{
buffer[25] = 42;
printf("%d\n", buffer[25]); // 0
}
buffer never change in another file (passed as parameter).
Did I miss something ?
You cannot write to flash memory the same way as you write to RAM, because of physical design of flash memories. To be exact you need to erase sector/page (let's say ~ 1-4kB, it's specified in your MCU datasheet). The reason is that flash are made that they retain state even if they're not powered, whenever you want to change any bit from value 0 -> 1 you need to erase whole sector (After erase all of bits will be set to 1).
So you cannot use Flash as data memory, what you could do is use Flash as storing variables that are const (read-only) value, so any look-up tables will perfectly fit in there (usually compilers when you stat variable to const will put them inside of flash). How to write to flash you can read in Reference Manual of your MCU.

mbed-OS porting to TivaC TM4123, Trouple with dynamic interrupt handling

Recently I'm trying to port mbed-OS to Tiva-C launchpad TM4C123, I am facing problem with file supplied by mbed which is cmsis_nvic.c and cmsis_nvic.h
This module is supposed to dynamically allocate the interrupt handler of OS timer to addressable function.(Or as far as I understand).
What happen is, The software jumps to "Hard Fault Handler" after executing the following line
vectors[i] = old_vectors[i];
Here's files which I use
#include "cmsis_nvic.h"
#define NVIC_RAM_VECTOR_ADDRESS (0x02000000) // Vectors positioned at start of RAM
#define NVIC_FLASH_VECTOR_ADDRESS (0x0) // Initial vector position in flash
void NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) {
uint32_t *vectors = (uint32_t*)SCB->VTOR;
uint32_t i;
// Copy and switch to dynamic vectors if the first time called
if (SCB->VTOR == NVIC_FLASH_VECTOR_ADDRESS) {
uint32_t *old_vectors = vectors;
vectors = (uint32_t*)NVIC_RAM_VECTOR_ADDRESS;
for (i=0; i<NVIC_NUM_VECTORS; i++) {
vectors[i] = old_vectors[i];
}
SCB->VTOR = (uint32_t)NVIC_RAM_VECTOR_ADDRESS;
}
vectors[IRQn + 16] = vector;
}
uint32_t NVIC_GetVector(IRQn_Type IRQn) {
uint32_t *vectors = (uint32_t*)SCB->VTOR;
return vectors[IRQn + 16];
}
And here is cmsis_nvic.h
#ifndef MBED_CMSIS_NVIC_H
#define MBED_CMSIS_NVIC_H
#define NVIC_NUM_VECTORS (154) // CORE + MCU Peripherals
#define NVIC_USER_IRQ_OFFSET 16
#include "cmsis.h"
#ifdef __cplusplus
extern "C" {
#endif
void NVIC_SetVector(IRQn_Type IRQn, uint32_t vector);
uint32_t NVIC_GetVector(IRQn_Type IRQn);
#ifdef __cplusplus
}
#endif
#endif
and I am calling
NVIC_SetVector(IRQn_Type IRQn, uint32_t vector)
from file us_ticker.c like this
NVIC_SetVector(TIMER0A_IRQn, (uint32_t)us_ticker_irq_handler);
(my compiler is ARM GCC, I am using CDT for building, And GDB openOCD for debugging, and integrated all those tools on Eclipse)
Can anyone please let me know what is going wrong here? or at least where should I debug or read to help me solve this problem???
UPDATE
I figured out part of the problem, The vector is not pointing to the first address of the target SRAM which should be
#define NVIC_RAM_VECTOR_ADDRESS (0x20000000)
instead of
#define NVIC_RAM_VECTOR_ADDRESS (0x02000000)
So now when calling NVIC_SetVector , the function is executed. But then when enabling the interrupt, Software still jumps to Hard Fault, I guess(just guessing or might be part of solution) that the defines in the header file are not configured correctly, Can someone explain to me what do they mean? and how to calculate the number of vector addresses? and what is the USER OFFSET?
I have solved this issue, here's what I have found
1- NVIC_RAM_VECTOR_ADDRESS wasn't the first address of my target RAM which should be `0x20000000'
2- Linker file should be update so that the stack pointer shouldn't write over the new copied vector table. So shift the RAM address by number of bytes that vector table should occupy.
3-(THE MAIN CAUSE) inside function NVIC_SetVector, i was declared as uint32_t and then compared to less than 255 pre-processor value. So compile get confused by comparing uint32_t with uint8_t, by adding UL to the pre-processor value, it solved the whole issue.

How to do computations with addresses at compile/linking time?

I wrote some code for initializing the IDT, which stores 32-bit addresses in two non-adjacent 16-bit halves. The IDT can be stored anywhere, and you tell the CPU where by running the LIDT instruction.
This is the code for initializing the table:
void idt_init(void) {
/* Unfortunately, we can't write this as loops. The first option,
* initializing the IDT with the addresses, here looping over it, and
* reinitializing the descriptors didn't work because assigning a
* a uintptr_t (from (uintptr_t) handler_func) to a descr (a.k.a.
* uint64_t), according to the compiler, "isn't computable at load
* time."
* The second option, storing the addresses as a local array, simply is
* inefficient (took 0.020ms more when profiling with the "time" command
* line program!).
* The third option, storing the addresses as a static local array,
* consumes too much space (the array will probably never be used again
* during the whole kernel runtime).
* But IF my argument against the third option will be invalidated in
* the future, THEN it's the best option I think. */
/* Initialize descriptors of exception handlers. */
idt[EX_DE_VEC] = idt_trap(ex_de);
idt[EX_DB_VEC] = idt_trap(ex_db);
idt[EX_NMI_VEC] = idt_trap(ex_nmi);
idt[EX_BP_VEC] = idt_trap(ex_bp);
idt[EX_OF_VEC] = idt_trap(ex_of);
idt[EX_BR_VEC] = idt_trap(ex_br);
idt[EX_UD_VEC] = idt_trap(ex_ud);
idt[EX_NM_VEC] = idt_trap(ex_nm);
idt[EX_DF_VEC] = idt_trap(ex_df);
idt[9] = idt_trap(ex_res); /* unused Coprocessor Segment Overrun */
idt[EX_TS_VEC] = idt_trap(ex_ts);
idt[EX_NP_VEC] = idt_trap(ex_np);
idt[EX_SS_VEC] = idt_trap(ex_ss);
idt[EX_GP_VEC] = idt_trap(ex_gp);
idt[EX_PF_VEC] = idt_trap(ex_pf);
idt[15] = idt_trap(ex_res);
idt[EX_MF_VEC] = idt_trap(ex_mf);
idt[EX_AC_VEC] = idt_trap(ex_ac);
idt[EX_MC_VEC] = idt_trap(ex_mc);
idt[EX_XM_VEC] = idt_trap(ex_xm);
idt[EX_VE_VEC] = idt_trap(ex_ve);
/* Initialize descriptors of reserved exceptions.
* Thankfully we compile with -std=c11, so declarations within
* for-loops are possible! */
for (size_t i = 21; i < 32; ++i)
idt[i] = idt_trap(ex_res);
/* Initialize descriptors of hardware interrupt handlers (ISRs). */
idt[INT_8253_VEC] = idt_int(int_8253);
idt[INT_8042_VEC] = idt_int(int_8042);
idt[INT_CASC_VEC] = idt_int(int_casc);
idt[INT_SERIAL2_VEC] = idt_int(int_serial2);
idt[INT_SERIAL1_VEC] = idt_int(int_serial1);
idt[INT_PARALL2_VEC] = idt_int(int_parall2);
idt[INT_FLOPPY_VEC] = idt_int(int_floppy);
idt[INT_PARALL1_VEC] = idt_int(int_parall1);
idt[INT_RTC_VEC] = idt_int(int_rtc);
idt[INT_ACPI_VEC] = idt_int(int_acpi);
idt[INT_OPEN2_VEC] = idt_int(int_open2);
idt[INT_OPEN1_VEC] = idt_int(int_open1);
idt[INT_MOUSE_VEC] = idt_int(int_mouse);
idt[INT_FPU_VEC] = idt_int(int_fpu);
idt[INT_PRIM_ATA_VEC] = idt_int(int_prim_ata);
idt[INT_SEC_ATA_VEC] = idt_int(int_sec_ata);
for (size_t i = 0x30; i < IDT_SIZE; ++i)
idt[i] = idt_trap(ex_res);
}
The macros idt_trap and idt_int, and are defined as follows:
#define idt_entry(off, type, priv) \
((descr) (uintptr_t) (off) & 0xffff) | ((descr) (KERN_CODE & 0xff) << \
0x10) | ((descr) ((type) & 0x0f) << 0x28) | ((descr) ((priv) & \
0x03) << 0x2d) | (descr) 0x800000000000 | \
((descr) ((uintptr_t) (off) & 0xffff0000) << 0x30)
#define idt_int(off) idt_entry(off, 0x0e, 0x00)
#define idt_trap(off) idt_entry(off, 0x0f, 0x00)
idt is an array of uint64_t, so these macros are implicitly cast to that type. uintptr_t is the type guaranteed to be capable of holding pointer values as integers and on 32-bit systems usually 32 bits wide. (A 64-bit IDT has 16-byte entries; this code is for 32-bit).
I get the warning that the initializer element is not constant due to the address modification in play.
It is absolutely sure that the address is known at linking time.
Is there anything I can do to make this work? Making the idt array automatic would work but this would require the whole kernel to run in the context of one function and this would be some bad hassle, I think.
I could make this work by some additional work at runtime (as Linux 0.01 also does) but it just annoys me that something technically feasible at linking time is actually infeasible.
Related: Solution needed for building a static IDT and GDT at assemble/compile/link time - a linker script for ld can shift and mask to break up link-time-constant addresses. No earlier step has the final addresses, and relocation entries are limited in what they can represent in a .o.
The main problem is that function addresses are link-time constants, not strictly compile time constants. The compiler can't just get 32b binary integers and stick that into the data segment in two separate pieces. Instead, it has to use the object file format to indicate to the linker where it should fill in the final value (+ offset) of which symbol when linking is done. The common cases are as an immediate operand to an instruction, a displacement in an effective address, or a value in the data section. (But in all those cases it's still just filling in 32-bit absolute address so all 3 use the same ELF relocation type. There's a different relocation for relative displacements for jump / call offsets.)
It would have been possible for ELF to have been designed to store a symbol reference to be substituted at link time with a complex function of an address (or at least high / low halves like on MIPS for lui $t0, %hi(symbol) / ori $t0, $t0, %lo(symbol) to build address constants from two 16-bit immediates). But in fact the only function allowed is addition/subtraction, for use in things like mov eax, [ext_symbol + 16].
It is of course possible for your OS kernel binary to have a static IDT with fully resolved addresses at build time, so all you need to do at runtime is execute a single lidt instruction. However, the standard
build toolchain is an obstacle. You probably can't achieve this without post-processing your executable.
e.g. you could write it this way, to produce a table with the full padding in the final binary, so the data can be shuffled in-place:
#include <stdint.h>
#define PACKED __attribute__((packed))
// Note, this is the 32-bit format. 64-bit is larger
typedef union idt_entry {
// we will postprocess the linker output to have this format
// (or convert at runtime)
struct PACKED runtime { // from OSdev wiki
uint16_t offset_1; // offset bits 0..15
uint16_t selector; // a code segment selector in GDT or LDT
uint8_t zero; // unused, set to 0
uint8_t type_attr; // type and attributes, see below
uint16_t offset_2; // offset bits 16..31
} rt;
// linker output will be in this format
struct PACKED compiletime {
void *ptr; // offset bits 0..31
uint8_t zero;
uint8_t type_attr;
uint16_t selector; // to be swapped with the high16 of ptr
} ct;
} idt_entry;
// #define idt_ct_entry(off, type, priv) { .ptr = off, .type_attr = type, .selector = priv }
#define idt_ct_trap(off) { .ct = { .ptr = off, .type_attr = 0x0f, .selector = 0x00 } }
// generate an entry in compile-time format
extern void ex_de(); // these are the raw interrupt handlers, written in ASM
extern void ex_db(); // they have to save/restore *all* registers, and end with iret, rather than the usual C ABI.
// it might be easier to use asm macros to create this static data,
// just so it can be in the same file and you don't need cross-file prototypes / declarations
// (but all the same limitations about link-time constants apply)
static idt_entry idt[] = {
idt_ct_trap(ex_de),
idt_ct_trap(ex_db),
// ...
};
// having this static probably takes less space than instructions to write it on the fly
// but not much more. It would be easy to make a lidt function that took a struct pointer.
static const struct PACKED idt_ptr {
uint16_t len; // encoded as bytes - 1, so 0xffff means 65536
void *ptr;
} idt_ptr = { sizeof(idt) - 1, idt };
/****** functions *********/
// inline
void load_static_idt(void) {
asm volatile ("lidt %0"
: // no outputs
: "m" (idt_ptr));
// memory operand, instead of writing the addressing mode ourself, allows a RIP-relative addressing mode in 64bit mode
// also allows it to work with -masm=intel or not.
}
// Do this once at at run-time
// **OR** run this to pre-process the binary, after link time, as part of your build
void idt_convert_to_runtime(void) {
#ifdef DEBUG
static char already_done = 0; // make sure this only runs once
if (already_done)
error;
already_done = 1;
#endif
const int count = sizeof idt / sizeof idt[0];
for (int i=0 ; i<count ; i++) {
uint16_t tmp1 = idt[i].rt.selector;
uint16_t tmp2 = idt[i].rt.offset_2;
idt[i].rt.offset_2 = tmp1;
idt[i].rt.selector = tmp2;
// or do this swap in fewer insns with SSE or MMX pshufw, but using vector instructions before setting up the IDT may be insane.
}
}
This does compile. See a diff of the -m32 and -m64 asm output on the Godbolt compiler explorer. Look at the layout in the data section (note that .value is a synonym for .short, and is 16 bits.) (But note that the IDT table format is different for 64-bit mode.)
I think I have the size calculation correct (bytes - 1), as documented in http://wiki.osdev.org/Interrupt_Descriptor_Table. Minimum value 100h bytes long (encoded as 0x99). See also https://en.wikibooks.org/wiki/X86_Assembly/Global_Descriptor_Table. (lgdt size/pointer works the same way, although the table itself has a different format.)
The other option, instead of having the IDT static in the data section, is to have it in the bss section, with the data stored as immediate constants in a function that will initialize it (or in an array read by that function).
Either way, that function (and its data) can be in a .init section whose memory you re-use after it's done. (Linux does this to reclaim memory from code and data that's only needed once, at startup.) This would give you the optimal tradeoff of small binary size (since 32b addresses are smaller than 64b IDT entries), and no runtime memory wasted on code to set up the IDT. A small loop that runs once at startup is negligible CPU time. (The version on Godbolt fully unrolls because I only have 2 entries, and it embeds the address into each instruction as a 32-bit immediate, even with -Os. With a large enough table (just copy/paste to duplicate a line) you get a compact loop even at -O3. The threshold is lower for -Os.)
Without memory-reuse haxx, probably a tight loop to rewrite 64b entries in place is the way to go. Doing it at build time would be even better, but then you'd need a custom tool to run the tranformation on the kernel binary.
Having the data stored in immediates sounds good in theory, but the code for each entry would probably total more than 64b, because it couldn't loop. The code to split an address into two would have to be fully unrolled (or placed in a function and called). Even if you had a loop to store all the same-for-multiple-entries stuff, each pointer would need a mov r32, imm32 to get the address in a register, then mov word [idt+i + 0], ax / shr eax, 16 / mov word [idt+i + 6], ax. That's a lot of machine-code bytes.
One way would be to use an intermediate jump table that is located at a fixed address. You could initialize the idt with addresses of the locations in this table (which will be compile time constant). The locations in the jump table will contain jump instructions to the actual isr routines.
The dispatch to an isr will be indirect as follows:
trap -> jump to intermediate address in the idt -> jump to isr
One way to create a jump table at a fixed address is as follows.
Step 1: Put jump table in a section
// this is a jump table at a fixed address
void jump(void) __attribute__((section(".si.idt")));
void jump(void) {
asm("jmp isr0"); // can also be asm("call ...") depending on need
asm("jmp isr1");
asm("jmp isr2");
}
Step 2: Instruct the linker to locate the section at a fixed address
SECTIONS
{
.so.idt 0x600000 :
{
*(.si.idt)
}
}
Put this in the linker script right after the .text section. This will make sure that the executable code in the table will go into a executable memory region.
You can instruct the linker to use your script as follows using the --script option in the Makefile.
LDFLAGS += -Wl,--script=my_script.lds
The following macro gives you the address of the location which contains the jump (or call) instruction to the corresponding isr.
// initialize the idt at compile time with const values
// you can find a cleaner way to generate offsets
#define JUMP_ADDR(off) ((char*)0x600000 + 4 + (off * 5))
You will then initialize the idt as follows using modified macros.
// your real idt will be initialized as follows
#define idt_entry(addr, type, priv) \
( \
((descr) (uintptr_t) (addr) & 0xffff) | \
((descr) (KERN_CODE & 0xff) << 0x10) | \
((descr) ((type) & 0x0f) << 0x28) | \
((descr) ((priv) & 0x03) << 0x2d) | \
((descr) 0x1 << 0x2F) | \
((descr) ((uintptr_t) (addr) & 0xffff0000) << 0x30) \
)
#define idt_int(off) idt_entry(JUMP_ADDR(off), 0x0e, 0x00)
#define idt_trap(off) idt_entry(JUMP_ADDR(off), 0x0f, 0x00)
descr idt[] =
{
...
idt_trap(ex_de),
...
idt_int(int_casc),
...
};
A demo working example is below, which shows dispatch to a isr with a non-fixed address from a instruction at a fixed address.
#include <stdio.h>
// dummy isrs for demo
void isr0(void) {
printf("==== isr0\n");
}
void isr1(void) {
printf("==== isr1\n");
}
void isr2(void) {
printf("==== isr2\n");
}
// this is a jump table at a fixed address
void jump(void) __attribute__((section(".si.idt")));
void jump(void) {
asm("jmp isr0"); // can be asm("call ...")
asm("jmp isr1");
asm("jmp isr2");
}
// initialize the idt at compile time with const values
// you can find a cleaner way to generate offsets
#define JUMP_ADDR(off) ((char*)0x600000 + 4 + (off * 5))
// dummy idt for demo
// see below for the real idt
char* idt[] =
{
JUMP_ADDR(0),
JUMP_ADDR(1),
JUMP_ADDR(2),
};
int main(int argc, char* argv[]) {
int trap;
char* addr = idt[trap = argc - 1];
printf("==== idt[%d]=%p\n", trap, addr);
asm("jmp *%0\n" : :"m"(addr));
}

Initialize array starting from specific address in memory - C programming

Do you have idea how to initialize array of structs starting from specific address in memory (not virtual, physical DDR memory). I am working on implementation of TxRx on SoC (ARM-FPGA). Basically ARM (PS) and FPGA (PL) communicate to each other by using shared RAM memory. Currently I am working on transmitter side, so I need to constantly load packets that I get from MAC layer to memory, then my Tx reads data and sends it in air. To achieve this I want to implement circular FIFO buffer on (ARM) side, in way that I can store up to 6 packets into buffer and send them one by one, in same time loading other packets on places of already sent packages. Because I need to use specific memory addresses I am interested is it possible to initialize array of structure that will be stored on specific addresses in memory. For example I want that my array starts at adress 0x400000 and ends at address 0x400000 + MaximumNumberOfPackets x SizeOfPackets I know how to do it for one instantiate of structure for example like this:
buffer_t *tmp = (struct buffer_t *)234881024;
But how to do it for array of structures?
A pointer to a single struct (or int, float, or anything else) is inherently a pointer to an array of them. The pointer type provides the sizeof() value for an array entry, and thus allows pointer arithmetic to work.
Thus, given a struct buffer you can simply do
static struct buffer * const myFIFO = (struct buffer *) 0x40000
and then simply access myFIFO as an array
for (size_t i = 0; i < maxPackets; ++i)
{
buffer[i].someField = initialValue1;
buffer[i].someOtherField = 42;
}
This works just the way you expect.
What you can't do (using pure standard C) is declare an array at a particular address like this:
struct buffer myFIFO[23] # 0x400000;
However, your compiler may have extensions to allow it. Many embedded compilers do (after all, that's often how they declare memory-mapped device registers), but it will be different for every compiler vendor, and possibly for every chip because it is a vendor extension.
GCC does allow it for AVR processors via an attribute, for example
volatile int porta __attribute__((address (0x600)));
But it doesn't seem to support it for an ARM.
Generally #kdopen is right but for arm you should create an entry in MEMORY section linker script that shows to linker where is your memory:
MEMORY
{
...
ExternalDDR (w) : ORIGIN = 0x400000, LENGTH = 4M
}
And than, when you are declaring variable just use the
__attribute__((section("ExternalDDR")))
I found the way how to do it. So could I do it like this. I set this into linker script:
MEMORY {
ps7_ddr_0_S_AXI_BASEADDR : ORIGIN = 0x00100000, LENGTH = 0x1FF00000
ps7_ram_0_S_AXI_BASEADDR : ORIGIN = 0x00000000, LENGTH = 0x00030000
ps7_ram_1_S_AXI_BASEADDR : ORIGIN = 0xFFFF0000, LENGTH = 0x0000FE00
DAC_DMA (w) : ORIGIN = 0xE000000, LENGTH = 64K
}
.dacdma : {
__dacdma_start = .;
*(.data)
__dacdma_end = .;
} > DAC_DMA
And then I set this into code
static buffer_t __attribute__((section("DAC_DMA"))) buf_pool[6];

Why does using a structure in C program cause Link error

I am writing a C program for a 8051 architecture chip and the SDCC compiler.
I have a structure called FilterStructure;
my code looks like this...
#define NAME_SIZE 8
typedef struct {
char Name[NAME_SIZE];
} FilterStructure;
void ReadFilterName(U8 WheelID, U8 Filter, FilterStructure* NameStructure);
int main (void)
{
FilterStructure testStruct;
ReadFilterName('A', 3, &testFilter);
...
...
return 0;
}
void ReadFilterName(U8 WheelID, U8 Filter, FilterStructure* NameStructure)
{
int StartOfName = 0;
int i = 0;
///... do some stuff...
for(i = 0; i < 8; i++)
{
NameStructure->Name[i] = FLASH_ByteRead(StartOfName + i);
}
return;
}
For some reason I get a link error "?ASlink-Error-Could not get 29 consecutive bytes in internal RAM for area DSEG"
If I comment out the line that says FilterStructure testStruct; the error goes away.
What does this error mean? Do I need to discard the structure when I am done with it?
The message means that your local variable testStruct couldn't be allocated in RAM (or DSEG that should be DATA SEGMENT of your binary), since your memory manager couldn't find 29 consecutive bytes to allocate it.
This is strange since your struct should be 8 bytes long.. but btw it's nothing to do with discarding the structure, this seems a memory management problem.. I don't know 8051 specs so well but it should be quite limited right?
EDIT: looking at 8051 specs it seems it just has 128 bytes of RAM. This can cause the problem because the variable, declared as a local, is allocated in internal RAM while you should try to allocate it on an external RAM chip if it's possible (using the address/data bus of the chip), but I'm not sure since this kind of microcontroller shouldn't be used to do these things.
you've run out of memory....by the looks of it.
try moving it out as a global variable, see if that makes it better.
Just a guess: 8051 has only 128 or 256 bytes of "internal RAM". Not so much... It can use part of it as stack and part for registers. Maybe your "large" (8 bytes!!!) structure on the stack forces the compiler to reserve too much stack space inside the internal memory. I suggest to have a look into the linker map file, maybe you can "rearrange" the memory partition. The massage says "consecutive bytes", so perhaps there is still enough space availabe, but it's fragmented.
Bye

Resources