Project

General

Profile

armt32semihostrun.obf : take into account size of globals

Added by Runar Tenfjord about 1 year ago

With reference to the runtime for armt32 I wonder how to
take into account the global variables which will be placed
in ram?

I adjusted a bit the initialization of the heap, but this ony takes
into account what is in the assembler file:

; heap start
.data _heap_start

    .alignment    4
    .reserve    4
    .require    _init_heap

.initdata _init_heap
    .alignment    4

    ldr        r0, [pc, offset (heap)]
    ldr     r1, [pc, offset (start)]
    str        r1, [r0, 0]
    b        skip
heap:   .qbyte    @_heap_start
start:  .qbyte  extent (@ram)
skip:

I run this test code:

MODULE Test;

IMPORT SYSTEM;

TYPE
    STRING = POINTER TO ARRAY OF CHAR;

VAR
    global, a, b, c, d : INTEGER;

PROCEDURE Run ;
VAR   
    s : STRING;
BEGIN
    global := 123456789;
    NEW(s, 25);
    TRACE(s);
    DISPOSE(s);
    TRACE(s);
    TRACE(global);
    TRACE(SYSTEM.ADR(global));
    TRACE(SYSTEM.ADR(a));
    TRACE(SYSTEM.ADR(b));
    TRACE(SYSTEM.ADR(c));
    TRACE(SYSTEM.ADR(d));
END Run;

BEGIN
    Run;
END Test.

The allocation works fine, but there is an overlap with the globals:

qemu-system-arm -M mps2-an386  -semihosting -nographic -device loader,file=build/test.rom,addr=0x00000000
test.mod:17:11: note: 's' = 20000004
test.mod:19:11: note: 's' = 00000000
test.mod:20:11: note: 'global' = 25
test.mod:21:21: note: 'SYSTEM.ADR (global)' = 20000000
test.mod:22:21: note: 'SYSTEM.ADR (a)' = 20000004
test.mod:23:21: note: 'SYSTEM.ADR (b)' = 20000008
test.mod:24:21: note: 'SYSTEM.ADR (c)' = 2000000C
test.mod:25:21: note: 'SYSTEM.ADR (d)' = 20000010

Is there some obvious fix for this?


Replies (14)

RE: armt32semihostrun.obf : take into account size of globals - Added by Florian Negele about 1 year ago

The _ram data section only exists such that the linker knows where to place all following data sections for global variables. With the attached patch, the heap initialisation can reference the _trailer section instead, which will be placed at the end of all other data sections, see map file.

RE: armt32semihostrun.obf : take into account size of globals - Added by Runar Tenfjord about 1 year ago

Thank you.

Now about 400 tests run on the simulated QEMU armt32 environment.

I have only some small test failures to investigate, which could likely
be error in the code.

The modified runtime:

; Only works on Cortex-M profile ARMv6-M and ARMv7-M
; Uses only Thumb1 16bit instructions to support Coretex-M0/M0+
; Flash/memory origin and size must be changed to values for target device.
.code vector
    .required
    .origin 0x00000000 ; flash

    .qbyte 0x20004000 ; stack = ram top
    .qbyte extent (@vector) + 1 ; +1 for Thumb flag

    #repeat 15
        .qbyte    @handle + 1 ; +1 for Thumb flag
    #endrep

.code handle
    .alignment    4

    loop:    b.n    loop

.data ram
    .required
    .origin    0x20000000 ; ram start

; last section
.trailer _trailer

; standard abort function
.code abort
    .alignment    4

    ldr.n    r0, offset (SYS_EXIT) + offset (SYS_EXIT) % 4
    ldr.n    r1, offset (ADP_Stopped_ApplicationExit) + offset (ADP_Stopped_ApplicationExit) % 4
    bkpt.n   0xab
loop:
    b.n    loop

    .align    4
SYS_EXIT:    .qbyte    0x18
ADP_Stopped_ApplicationExit: .qbyte    0x20026

; standard _Exit function
.code _Exit
    .alignment    4

    bl       @abort

; standard getchar function
.code getchar
    .alignment    4

    ldr.n    r0, offset (SYS_READC) + offset (SYS_READC) % 4
    mov      r1, 0x00
    bkpt.n   0xab
    bx.n     lr

    .align    4
SYS_READC:    .qbyte    0x07

; standard free function
.code free
    .alignment    4

    bx.n    lr

; standard malloc function
.code malloc
    .alignment    4

    ldr.n   r2, offset (heap) + offset (heap) % 4
    ldr.n    r0, [r2, 0]
    ldr.n    r3, [sp, 0]
    add.n    r3, r3, r0
    str.n    r3, [r2, 0]
    bx.n    lr

heap:    .qbyte    @_heap_start

; heap start
.data _heap_start

    .alignment    4
    .reserve    4
    .require    _init_heap

.initdata _init_heap
    .alignment    4

    ldr        r0, [pc, offset (heap)]
    ldr     r1, [pc, offset (start)]
    str        r1, [r0, 0]
    b        skip
heap:   .qbyte    @_heap_start
start:  .qbyte  extent (@_trailer)
skip:

; standard putchar function
.code putchar
    .alignment    4

    ldr.n    r0, offset (SYS_WRITEC) + offset (SYS_WRITEC) % 4
    mov      r1, sp
    bkpt.n   0xab
    ldr.n     r0, [r1]
    bx.n     lr

    .align    4
SYS_WRITEC:    .qbyte    0x03

RE: armt32semihostrun.obf : take into account size of globals - Added by Florian Negele about 1 year ago

I have successfully tested your runtime against the test suites provided by the ECS. Out of interest, could you provide an example of a failing test?

RE: armt32semihostrun.obf : take into account size of globals - Added by Runar Tenfjord about 1 year ago

Good.
I need to dig a bit and reduce this to smaller examples
if this should not be related to a code error from my side.

RE: armt32semihostrun.obf : take into account size of globals - Added by Runar Tenfjord about 1 year ago

Not sure this is an error or not.
But the following code behaves somewhat different
when comparing values:

MODULE test;

IMPORT SYSTEM, Out IN OBL;

TYPE
    WORD = SYSTEM.ADDRESS;
    WSET = SYSTEM.SET;

CONST
    WORDSIZE = SIZE(WORD);

(**  Hash value of string (64/32bit FNV-1a) *)
PROCEDURE Hash* (src- : ARRAY OF CHAR): LENGTH;
CONST
    HSTART = SEL(WORDSIZE = 4, 0811C9DC5H, 0CBF29CE484222325H);
    HFACTOR = SEL(WORDSIZE = 4, 01000193H, 0100000001B3H);
VAR
    i : LENGTH;
    hash : LENGTH;
BEGIN
    hash := LENGTH(HSTART);
    i := 0;
    WHILE (i < LEN(src)) & (src[i] # 00X) DO
        hash := LENGTH(WSET(src[i]) / WSET(hash)); (* XOR *)
        hash := hash * LENGTH(HFACTOR);
        INC(i)
    END;
    RETURN hash
END Hash;

BEGIN
    IF SIZE(SYSTEM.ADDRESS) = 8 THEN
        Out.Address(Hash('foobar')); Out.Ln; (* Expec 085944171F73967E8H *)
        TRACE(Hash('foobar') = 085944171F73967E8H);
    ELSE
        Out.Address(Hash('foobar')); Out.Ln; (* Expec 0BF9CF968H *)
        TRACE(Hash('foobar') = 0BF9CF968H); (* FALSE? *)
        TRACE(Hash('foobar') = LENGTH(0BF9CF968H));
    END;

END test.

The constant 0BF9CF968H on 32bit platforms is perhaps evaluated as 64bit
and the sign bit created the difference?

RE: armt32semihostrun.obf : take into account size of globals - Added by Florian Negele about 1 year ago

Yes exactly. A binary operation like the equal relation always uses the smallest numeric type that includes both operands.

RE: armt32semihostrun.obf : take into account size of globals - Added by Runar Tenfjord about 1 year ago

This goes into infinite loop on the armt32 platform, but works OK and prints the expected
output of '-123' on the 64bit Win10 platform:

MODULE test;

IMPORT SYSTEM, Out IN OBL;

TYPE
    STRING = POINTER TO ARRAY OF CHAR;
    Writer = RECORD END;
    StringWriter = RECORD(Writer)
        str : STRING;
        pos : LENGTH;
    END;

VAR s : STRING;

PROCEDURE Length (str-: ARRAY OF CHAR) : LENGTH;
VAR i: LENGTH;
BEGIN
    i := 0; WHILE (i < LEN(str)) & (str[i] # 00X) DO INC(i) END;
    RETURN i
END Length;

PROCEDURE (VAR w : Writer) WriteChar*(ch : CHAR);
BEGIN END WriteChar;

PROCEDURE (VAR s : StringWriter) WriteChar(ch : CHAR);
VAR n: LENGTH;
BEGIN
    ASSERT(s.pos < LEN(s.str^) - 1);
    n := Length(s.str^);
    s.str^[n + 0] := ch;
    s.str^[n + 1] := 00X;
    INC(s.pos, 1);
END WriteChar;

PROCEDURE Format(VAR Writer : Writer; value : HUGEINT; width: LENGTH);
VAR
    val, x : HUGECARD;
    i, len, digits, left : LENGTH;
    str : ARRAY 20 OF CHAR;
BEGIN
    IF value = MIN(HUGEINT) THEN (* -MIN(HUGEINT) does not exists *)
        val := 9223372036854775808
    ELSE
        val := ABS(value);
    END;
    len := 0; x := val;
    REPEAT INC(len); x := x DIV 10 UNTIL x = 0;
    digits := len;
    IF value < 0 THEN INC(len) END;
    left := width - len;
    WHILE left > 0 DO Writer.WriteChar(' '); DEC(left) END;
    IF value < 0 THEN Writer.WriteChar('-') END;
    i := digits;
    REPEAT
        DEC(i);
        str[i] := CHR(ORD('0') + val MOD 10);
        val := val DIV 10;
    UNTIL val = 0;
    i := 0;
    WHILE (i < digits) DO Writer.WriteChar(str[i]); INC(i) END;
END Format;

PROCEDURE FormatInteger(VAR dst: STRING; value : HUGEINT; width: LENGTH);
VAR writer : StringWriter;
BEGIN
    writer.str := dst;
    writer.pos := Length(dst^);
    Format(writer, value, width);
    dst := writer.str;
END FormatInteger;

BEGIN
    NEW(s, 25);
    s[0] := 00X;
    FormatInteger(s, -123, 0);
    TRACE(s^);
    DISPOSE(s);
END test.

RE: armt32semihostrun.obf : take into account size of globals - Added by Florian Negele about 1 year ago

There was an alignment issue regarding HUGEINT types. The attached patch should fix it. Thanks for reporting.

align.patch (629 Bytes) align.patch

RE: armt32semihostrun.obf : take into account size of globals - Added by Runar Tenfjord about 1 year ago

Confirmed to fix the problem.

This goes into infinite loop on the armt32 platform, but works OK on the 64bit Win10 platform:

MODULE test;

IMPORT SYSTEM, Out IN OBL;

TYPE
    DATETIME = HUGEINT;

CONST
    ERROR      = MIN(HUGEINT);

VAR
    dt : DATETIME;
    MonthDays: ARRAY 2, 12 OF INTEGER;
    ShiftedMonthDays: ARRAY 12 OF INTEGER;

PROCEDURE IsLeapYear (year : INTEGER) : BOOLEAN;
BEGIN RETURN (year MOD 4 = 0) & ((year MOD 100 # 0) OR (year MOD 400 = 0))
END IsLeapYear;

PROCEDURE RataDie (year, month, day : INTEGER): HUGEINT;
VAR mdays : INTEGER;
BEGIN
    IF month < 3 THEN DEC(year) END;
    mdays := ShiftedMonthDays[month - 1];
    RETURN day + mdays + 365*year + year DIV 4 - year DIV 100 + year DIV 400 - 306;
END RataDie;

PROCEDURE TryEncodeDate (VAR date : DATETIME; year,month,day : INTEGER) : BOOLEAN;
VAR ret : BOOLEAN;
BEGIN
    ret := (year > 0) & (year < 10000) & (month > 0) & (month < 13) & (day > 0) 
           & (day <= MonthDays[INTEGER(IsLeapYear(year)), month - 1]);
    IF ret THEN
        date := 86400000 * RataDie(year, month, day);
    END;
    RETURN ret;
END TryEncodeDate;

PROCEDURE EncodeDate (year, month, day : INTEGER): DATETIME;
VAR date : DATETIME;
BEGIN
    IF ~TryEncodeDate(date, year, month, day) THEN date := ERROR END;
    RETURN date;
END EncodeDate;

PROCEDURE TryEncodeDateTime (VAR datetime : DATETIME; year, month, day, hour, min, sec, msec : INTEGER) : BOOLEAN;
VAR ret : BOOLEAN;
BEGIN
    ret := TryEncodeDate(datetime, year, month, day);
    IF ret THEN
        ret := (hour >= 0) & (hour < 24) & (min >= 0) & (min < 60) & (sec >= 0) & (sec < 60)
               & (msec >= 0) & (msec < 1000);
        IF ret THEN
            datetime := datetime + msec + 1000*(sec + 60*min + 3600*hour);
        END;
    END;
    RETURN ret;
END TryEncodeDateTime;

PROCEDURE EncodeDateTime(year, month, day, hour, min, sec, msec: INTEGER): DATETIME;
VAR datetime : DATETIME;
BEGIN
    IF ~TryEncodeDateTime(datetime, year, month, day, hour, min, sec, msec) THEN
        datetime := ERROR;
    END;
    RETURN datetime;
END EncodeDateTime;

BEGIN
    MonthDays[0,0] := 31;  MonthDays[0,1] := 28; MonthDays[0,2] := 31; MonthDays[0,3] := 30;
    MonthDays[0,4] := 31;  MonthDays[0,5] := 30; MonthDays[0,6] := 31; MonthDays[0,7] := 31;
    MonthDays[0,8] := 30;  MonthDays[0,9] := 31; MonthDays[0,10] := 30; MonthDays[0,11] := 31;
    MonthDays[1,0] := 31;  MonthDays[1,1] := 29; MonthDays[1,2] := 31; MonthDays[1,3] := 30;
    MonthDays[1,4] := 31;  MonthDays[1,5] := 30; MonthDays[1,6] := 31; MonthDays[1,7] := 31;
    MonthDays[1,8] := 30;  MonthDays[1,9] := 31; MonthDays[1,10] := 30; MonthDays[1,11] := 31;
    ShiftedMonthDays[0] := 306; ShiftedMonthDays[1] := 337; ShiftedMonthDays[2] := 0;
    ShiftedMonthDays[3] := 31;  ShiftedMonthDays[4] := 61; ShiftedMonthDays[5] := 92;
    ShiftedMonthDays[6] := 122; ShiftedMonthDays[7] := 153; ShiftedMonthDays[8] := 184;
    ShiftedMonthDays[9] := 214; ShiftedMonthDays[10] := 245; ShiftedMonthDays[11] := 275;

    dt := EncodeDateTime(2019,11,15,16,43,20,0);
    TRACE(dt);
END test.

RE: armt32semihostrun.obf : take into account size of globals - Added by Florian Negele about 1 year ago

Thanks for reporting. This is an issue that needs a little more time to fix. Use this in the mean time:

INC (datetime, msec + 1000*(sec + 60*min + 3600*hour));

RE: armt32semihostrun.obf : take into account size of globals - Added by Runar Tenfjord about 1 year ago

Very good. With the fixes now in place 494 test
runs successful. Some 20 tests due to the issue
above where disabled.

On the todo list is floating point support which need
more work.

You might consider adding this runtime to the next
release, as this could be very useful and would cover
a large number of MCUs in use today.

I there is something I need to do formally to
make that happen, just give me a note.

RE: armt32semihostrun.obf : take into account size of globals - Added by Florian Negele about 1 year ago

We might consider it. How would you call such a runtime? Please note that it should support physical hardware, but apart from the semihosting routines and the simplistic memory management there is nothing much left except for the boot vector.

RE: armt32semihostrun.obf : take into account size of globals - Added by Runar Tenfjord about 1 year ago

It could be called "armt32v7mrun.obf" after the identification
ARMv7-M, which is the ARMv7 architecture and M profile
covering M3, M4 & M7 MCUs and supporting Thumb-1 & Thumb-2.

If later Thumb-1 only is supported by the compiler there could be
a runtime called "armt32v6mrun.obf" after the identification ARMv6-M,
which is the ARMv6 architecture and M profile covering M0 & M0+ MCUs

The instruction to call the semihost interface "BKPT 0xAB" is tied to
ARMv6-M & ARMv7-M.

RE: armt32semihostrun.obf : take into account size of globals - Added by Runar Tenfjord about 1 year ago

If you want to tie the runtime/examples to a specific board the STM32F407G-DISC1
from ST is a great and accessible board.

This board has the possibility to be simulated with graphics, led and buttons
by the qemu-system-gnuarmeclipse emulator (legacy version) which emulates
a lot of peripherals not covered by mainstream QEMU.

    (1-14/14)