Fungerar va_arg med float* ?

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Fungerar va_arg med float* ?

Inlägg av DanielM »

Jag försöker lösa differentialsekvationer på ARM processorer då jag håller på med att lösa diskreta Algebraiska Riccati Ekvationer där P är lösningen.

\(P = A^\top P A -(A^\top P B)(R + B^\top P B)^{-1}(B^\top P A) + Q.\)

Jag tänkte att jag ger er .c kod och ett arbetsexempel till att börja med. Detta exempel löser alltså ett massa-dämpare-fjäder system med hjälp av fjärde ordningen av runge-kutta.

Det som är unik med denna kod är att den använder sig av variadic. Detta betyder att man kan variabelt ha utökade argument om så önskas. Jajjemän, precis som Matlab och Python så stöder C detta ända sedan tidernas begynnelse från ANSI C (1989). :mrgreen:

Men ett problem! Jag får hardfault när jag kommer till denna kodrad - Varför då? Kan inte variadic köra floatpekare? Min kompilator är GCC för ARM. För MSVC så fungerar det utan problem.

Kod: Markera allt

matrices[i] = va_arg(args, float*);
Körbart exempel här: https://onlinegdb.com/2c6Q1hR-ZB

Kod: Markera allt

/******************************************************************************

                            Online C Compiler.
                Code, Compile, Run and Debug C program online.
Write your code in this editor and press "Run" button to compile and execute it.

*******************************************************************************/


#include <string.h>						/* For memcpy, memset etc */
#include <stdio.h>						/* For printf */
#include <stdlib.h>						/* Standard library */
#include <math.h>						/* For sqrtf */
#include <float.h>						/* Required for FLT_EPSILON, FLT_MAX, FLT_MIN */
#include <stddef.h>						/* Requried for size_t,  NULL etc... */
#include <time.h> 						/* For srand, clock */
#include <stdarg.h>                     /* For ... arguments */

void print(const float A[], const size_t row, const size_t column) {
	size_t i, j;
	for (i = 0; i < row; i++) {
		for (j = 0; j < column; j++) {
			printf("%0.7f\t", *(A++));
		}
		printf("\n");
	}
	printf("\n");
}

void mul(const float A[], const float B[], float C[], const size_t row_a, const size_t column_a, const size_t column_b){
    
    /* Decleration */
	size_t i, j, k;

	/* Data matrix */
	const float* data_a, * data_b;

	for (i = 0; i < row_a; i++) {
		/* Then we go through every column of b */
		for (j = 0; j < column_b; j++) {
			data_a = A;
			data_b = &B[j];

			C[0] = 0.0f; /* Reset */
			/* And we multiply rows from a with columns of b */
			for (k = 0; k < column_a; k++) {
				*C += data_a[0] * data_b[0];
				data_a++;
				data_b += column_b;
			}
			C++; /* ;) */
		}
		A += column_a;
	}
}

 /*
  * Fourth-order Runge–Kutta method.
  * Runge-Kutta method is not the best ODE solver, but it's the best ODE solver for fixed step time iteration.
  * h - Step time
  * Y[iterations*N] - Output
  * y[N] - Initial state vector
  * N - Dimension for y-vector
  * odefun(const float t, float y[], const float* matrices[], const size_t rows[], const size_t columns[])
  * ... = const float matrixA[], const size_t rowA, const size_t columnA, const float matrixB[], const size_t rowB, const size_t columnB, const float matrixC[], const size_t rowC, const size_t columnC, etc...
  */
void rk4args(const size_t iterations, const float h, float Y[], float y[], const size_t N, void (*odefun)(float, float*, float**, const size_t*, const size_t*), const size_t number_of_pointers, ...) {
	/* Variables */
	size_t i, j;

	/* Constants */
	const size_t length = N * sizeof(float);

	/* Create vectors */
	float* k1 = (float*)malloc(length);
	float* k2 = (float*)malloc(length);
	float* k3 = (float*)malloc(length);
	float* k4 = (float*)malloc(length);

	/* Temporary vectors */
	float* yt = (float*)malloc(length);

	/* Variables */
	float t = 0.0f;

	/* Initial output */
	memcpy(Y, y, length);

	/* Variable arguments */
	va_list args;
	va_start(args, number_of_pointers);

	/* Pointers */
	float** matrices = (float**)malloc(number_of_pointers * sizeof(float*));
	size_t* rows = (size_t*)malloc(number_of_pointers * sizeof(size_t));
	size_t* columns = (size_t*)malloc(number_of_pointers * sizeof(size_t));

	/* Get the arguments */
	for (i = 0; i < number_of_pointers; i++) {
		matrices[i] = va_arg(args, float*);
		rows[i] = va_arg(args, const size_t);
		columns[i] = va_arg(args, const size_t);
	}

	/* Perform Runge-Kutta */
	for (i = 1; i < iterations; i++) {
		/* Receive old output */
		for (j = 0; j < N; j++) {
			y[j] = Y[(i - 1) * N + j];
		}

		/* First statement */
		memcpy(yt, y, length);
		odefun(t, yt, matrices, rows, columns);
		for (j = 0; j < N; j++) {
			k1[j] = yt[j] * h;
		}

		/* Second statement */
		memcpy(yt, y, length);
		for (j = 0; j < N; j++) {
			yt[j] += k1[j] / 2.0f;
		}
		odefun(t + h / 2.0f, yt, matrices, rows, columns);
		for (j = 0; j < N; j++) {
			k2[j] = yt[j] * h;
		}

		/* Third statement */
		memcpy(yt, y, length);
		for (j = 0; j < N; j++) {
			yt[j] += k2[j] / 2.0f;
		}
		odefun(t + h / 2.0f, yt, matrices, rows, columns);
		for (j = 0; j < N; j++) {
			k3[j] = yt[j] * h;
		}

		/* Fourth statement */
		memcpy(yt, y, length);
		for (j = 0; j < N; j++) {
			yt[j] += k3[j];
		}
		odefun(t + h, yt, matrices, rows, columns);
		for (j = 0; j < N; j++) {
			k4[j] = yt[j] * h;
		}

		/* Save output */
		for (j = 0; j < N; j++) {
			Y[i * N + j] = y[j] + (k1[j] + 2.0f * k2[j] + 2.0f * k3[j] + k4[j]) / 6.0f;
		}

		/* Update t */
		t += h;
	}

	/* Free */
	free(k1);
	free(k2);
	free(k3);
	free(k4);
	free(yt);
	free(rows);
	free(columns);
	free(matrices);

	/* Close */
	va_end(args);
}

 /* Constants */
#define N 2
#define h 0.1f
#define row_a 2
#define column_a 2
#define row_b 2
#define column_b 1
#define row_u 1
#define column_u 1
#define ITERATIONS 200

/* Create ODE equation */
void odefun(const float t, float y[], float* matrices[], const size_t rows[], const size_t columns[]) {
    /* Get the matrices */
    float* A = matrices[0];
    float* B = matrices[1];
    float* u = matrices[2];

    /* Get the sizes */
    size_t row_A = rows[0];
    size_t column_A = columns[0];
    size_t row_B = rows[1];
    size_t column_B = columns[1];
    size_t row_U = rows[2];
    size_t column_U = columns[2];

    /* Solve y = A*y + B*u */
    float Ay[2];
    float Bu[2];
    mul(A, y, Ay, row_A, column_A, 1);
    mul(B, u, Bu, row_B, column_B, 1);
    size_t i;
    for (i = 0; i < row_A; i++) {
        y[i] = Ay[i] + Bu[i];
    }
}

int main() {
    clock_t start, end;
    float cpu_time_used;
    start = clock();

    /* Declare intial state */
    float y0[N] = { 0 };

    /* Declare output */
    float Y[ITERATIONS * N];

    /* Declare number of pointers */
    const size_t number_of_pointers = 3;
    const float k = 5.0f;    /* Spring constant [N/m] */
    const float b = 1.4f;    /* Damper constant [Ns/m] */
    const float m = 2.0f;    /* Mass [kg] */
    const float F = 8.0f;    /* Force [N] */
    const float A[row_a * column_a] = { 0, 1, -k / m, -b / m };
    const float B[row_b * column_b] = { 0, 1 / m };
    const float u[row_u * column_u] = { F };

    /* Perform Runge-Kutta 4:th order with extended arguments ... of const float[], const size_t, const size_t etc... */
    rk4args(ITERATIONS, h, Y, y0, N, odefun, number_of_pointers, A, (const size_t)row_a, (const size_t)column_a, B, (const size_t)row_b, (const size_t)column_b, u, (const size_t)row_u, (const size_t)column_u);

    end = clock();
    cpu_time_used = ((float)(end - start)) / CLOCKS_PER_SEC;
    printf("\nTotal speed  was %f\n", cpu_time_used);

    /* Plot */
    print(Y, ITERATIONS, N);

    return EXIT_SUCCESS;
}
Findecanor
Inlägg: 1038
Blev medlem: 2 juli 2010, 23:04:07

Re: Fungerar va_arg med float* ?

Inlägg av Findecanor »

Vilken slags ARM-plattform? Om du kör på ett litet litet inbyggt system kanske malloc() egentligen inte finns, och alltid ger NULL.
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Fungerar va_arg med float* ?

Inlägg av DanielM »

Det är STM32. Malloc finns.
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Fungerar va_arg med float* ?

Inlägg av DanielM »

Nu har jag hittat ett fel här!
Under körningen så blir k3 NULL....
Har jag verkligen förbrukat upp alla mina 8 kB i RAM redan???

Tror jag vet den misstänkta!

Kod: Markera allt

void rk4args(const size_t iterations, const float h, float Y[], float y[], const size_t N, void (*odefun)(float, float*, float**, const size_t*, const size_t*), const size_t number_of_pointers, ...) {
	/* Variables */
	size_t i, j;

	/* Constants */
	const size_t length = N * sizeof(float);

	/* Create vectors */
	float* k1 = (float*)malloc(length);
	float* k2 = (float*)malloc(length);
	float* k3 = (float*)malloc(length);
	if(k3 == NULL){
		y[0] = 1;
		return;
	}
	float* k4 = (float*)malloc(length);
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Fungerar va_arg med float* ?

Inlägg av DanielM »

Tydligen så har jag slut på RAM. Därav felet.
Får väll deklarera statiskt då.
hawkan
Inlägg: 3382
Blev medlem: 14 augusti 2011, 10:27:40

Re: Fungerar va_arg med float* ?

Inlägg av hawkan »

Ja 8 kbyte som räcker till så mycket.
Räkna igenom hur mycket minne du förbrukar. Det finns väl något sätt.
Går det inte dynamiskt lär det inte gå statiskt heller.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46872
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Fungerar va_arg med float* ?

Inlägg av TomasL »

Dynamisk allokering är väl något man skall undvika så lång det går, framförallt i inbäddade system. Jag lärde mig en gång att dynamisk allokering är förbjudet i inbäddade system.
Har för mig att MISRA dessutom förbjuder det.
Med statisk allokering, får man förhoppningsvis en kompilatorvarning om man allokerar för mycket.
Findecanor
Inlägg: 1038
Blev medlem: 2 juli 2010, 23:04:07

Re: Fungerar va_arg med float* ?

Inlägg av Findecanor »

Ja, försöka att allockera statiskt, med maximal storlek på arrayerna från början.

Du kanske också har stött på fragmentering eller onödig padding.
Du skulle också kunna försöka allockera allt i en klump.
T.ex om du ska ha fyra arrayer:

Kod: Markera allt

float *a = (float *)malloc(N * sizeof(float) * 4);
float *b = a + N;
float *c = b + N;
float *d = c + N;
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46872
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Fungerar va_arg med float* ?

Inlägg av TomasL »

Grejjen är väl, att man måste ha 1000% koll på tillgängligt RAM, om man använder dynamisk allokering, i alla lägen.
Eftersom inbäddade system är kraftigt begränsade, måste man då räkna på alla scenarier, för att se att man inte allokerar för mycket, vilket inte alltid är helt lätt.
Därför skall man aldrig någonsin använda dynamisk allokering i inbäddade system.
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Fungerar va_arg med float* ?

Inlägg av DanielM »

TomasL skrev: 9 februari 2025, 20:17:24 Dynamisk allokering är väl något man skall undvika så lång det går, framförallt i inbäddade system. Jag lärde mig en gång att dynamisk allokering är förbjudet i inbäddade system.
Har för mig att MISRA dessutom förbjuder det.
Med statisk allokering, får man förhoppningsvis en kompilatorvarning om man allokerar för mycket.
Förbjudet är det inte att använda dynamisk allokering. I så fall vore funktionen bortblockad från C :mrgreen: Men jag förstår riskerna med dynamisk minnesallokering.

Jag har börjat med att exkludera .c filer från mitt projekt. Då har dom gått från blodröd till grön. Men nu måste jag titta mera på vad jag kan göra.
Statisk minnesallokering låter som en lösning. Men då kommer jag behöva skriva om mycket av min kod. Finns det inget smart sätt man kan göra för att få kompilatorn att tolka malloc på något annat sätt än dynamisk allokering?

En lösning som skulle fungera för mig är att skriva om t.ex.

Kod: Markera allt

float* matris = (float*)malloc(row * column * sizeof(float)); 
Till
float matrix[5*5]; // Om row == column == 5
Är det då Flash som förbrukas då?
Skärmbild 2025-02-10 000338.png
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46872
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Fungerar va_arg med float* ?

Inlägg av TomasL »

Förbjudet är det inte att använda dynamisk allokering. I så fall vore funktionen bortblockad från C :mrgreen: Men jag förstår riskerna med dynamisk minnesallokering.
Enligt MISRA, en standard som alla borde följa, är det inte tillåtet med dynamis allokering.

Direktiv 4.12 i MISRA C:2012, denna regel finns i samtliga utgåvor av MISRA
Dir 4.12 Dynamic memory allocation shall not be used
Category Required

Amplification
This rule applies to all dynamic memory allocation packages including:
• Those provided by The Standard Library;
• Third-party packages.
hawkan
Inlägg: 3382
Blev medlem: 14 augusti 2011, 10:27:40

Re: Fungerar va_arg med float* ?

Inlägg av hawkan »

Skrivet innan TomasLs svar.
Nej det är RAM som används i båda fallen. Både stack och heap använder ram.
Kolla hur mycket minne som reserverats för heap, där malloced minne tas från.
Förbjudet är det väl inte, men använd inte malloc/free ändå. Uppenbarligen
är det ett problem i detta fallet, trots din förståelse av problemet med malloc.
Är det alltid en viss storlek på dina matriser så speca storleken som du gör,
fast med pre-processor variabler för storlek.

Matriser, matrisoperationer och flyttal är ökända minnesslukare.
8 kB är pyttelite för sånt och man får verkligen anstränga sej för att få plats.
Sedan att iterera fram en lösning det är heller inte bra för det blir variation
i hur lång tid det tar. Så du har lite utmaningar med detta.
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Fungerar va_arg med float* ?

Inlägg av DanielM »

Kan man deklarera en array i en funktion på Flash?
Typ om jag skriver

Kod: Markera allt

static float A[5*5];
Eller måste jag använda nyckelordet const också? Då blir arrayen oanvändbar för mig.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46872
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Fungerar va_arg med float* ?

Inlägg av TomasL »

Nej, du kan naturligtvis inte ha variabler i flashminnet.
Konstanter hamnar i Flash, variabler måste av naturliga skäl ligga i RAM.
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Fungerar va_arg med float* ?

Inlägg av DanielM »

Hmm....
Jag har en pekararray i en struktur. Den initialiseras bara en gång. Sedan aldrig mer.
Skriv svar