Memset with volatile pointer, syntax correct please?

So I think I got my global assembly working with a vector table for interrupts :slight_smile: But it seems like the following generates an interrupt when I call clear(). But I´m not sure because I trying to debug over serial from the pinePhone and it is all a bit tricky.

So first I wanted to check I got the syntax right please? since I have several of these 4k registers to work with. Related, is there a way I can see the generated assembly (aarch64 if that makes a difference for tooling). I´m wondering if I should also just add a chunk of assembly to do these simple clears.

Or maybe there is a better Zig way to do this?

And finally just some feedback, this forum is running very slow for me recently. Significant lag on typing. I´m asahi linux fedora kde icecat based, but I havent really updated much recently whilst bogged down in IRQ assembly mud :slight_smile: Actually I am quite liking armv8! reminds me of my 68000 days hacking the Amiga :slight_smile:

pub fn Reg4k(comptime addr: u32) type {
    const size4k = 0x1000;
    return struct {
        const ptr: *volatile [size4k]u8 = @ptrFromInt(addr);

        pub fn clear() void {
            serial.logU32ln(addr);
            @memset(ptr[0..size4k], 0);
        }
    };
}

you dont need to slice ptr since its already a pointer to an array with that size.

Other than that it seems fine.

I can’t tell you if it’s generating an interrupt.

But you can check the generated assembly with objdump assuming you’re on linux/bsd/mac, I’m sure there are similar tools out there for other platforms.
objdump --syms | grep clear: to find the symbols
objdump --disassemble [symbol]: to get the assembly

if it is generating the wrong instructions, I think inline assembly would be the only way to fix it.

Thanks! So I didn´t have symbols, but added a few NOPs to locate the clear(). Nothing fancy, just a bit of address manipulation and a call to memset as you´d expect. I somehow expected some memory barriers, but I trust it more than my code :slight_smile:

So next I tried adding some global assembly, just a loop zero´ing the bytes, probably not my best assembly, but it works! Working out why is a bit beyond my rusty old brain. I also tried @memset(ptr, 0);

Might just be another phone memory access quirk, I already had this one: Strange multi-dimensional array issue

Thanks anyway, onwards again :slight_smile:

1 Like

if you’re not building in debug mode they were probably stripped, for future reference you can add .strip = false in module options to change that.

It´s all a bit fragile and only runs in ReleaseSmall so far. Now that I am up and running again I´ll remove all the debug and try again to work out why Debug crashes. Good to know about the strip option! Thanks.

So after a big debug dump, and arm´ed :wink: with my new global assembly armv8 based UART debugging tools, I was able to try again the .Debug build mode.

I tracked the first crash down to something quite interesting:

pub const DSI_PIXEL_PH = packed struct(u32) {
    DT: u6 = 0,
    VC: u2 = 0,
    WC: u16 = 0,
    //WCL: u8 = 0,
    //WCH: u8 = 0,
    ECC: u8 = 0,
};

Writing a value to WC, CPU goes down the toilet (crash), but if I instead write the upper and lower halves of the 16bit WC value, the code works! Well until the next crash (which I still need to flush out).

Oh and this is before I use a volatile pointer to poke that value into a register.

.ReleaseSmall doesn’t block the loo :wink: and works fine. Sorry for British toilet humor!

So with my global assembly UART debugging, I was also finally able to get a custom panic handler working over serial.

The next ‘debug build’ crash was just a memcpy complaining about size mismatch. So finally I can build in debug mode! :slight_smile:

I did go back to make sure the strange 4k @memset, and packed struct hacks were not hiding a panic. They are not :frowning: so that might be a problem in the aarch64 generated code, but I am not yet skilled enough with armv8 assembly to debug what is going on there.

I also no longer use github, so I am not able to log a possible bug there, but I’ll post back here again if I make further progress.

PS Is there a way to work around the memcpy length mismatch other than byte-by-byte ‘for’ copy that I am now using. I have a fixed 256byte register, and copy maybe 20 different length fixed byte ‘command sequences’. So the lengths of all are always known. memcpy documentation says ‘if two lengths are provided, they must be equal.’

can you elaborate on your issue with memcpy

1 Like

Here are the relevant bits:

    const dest: *volatile [256]u8 = @ptrFromInt(DSI_TX_REG);

    pub fn send(packet: []const u8) void {
    //works
    for (packet, 0..) |b, i| {
        dest[i] = b;
    }
    //size error
    @memcpy(dest, packet);

dest is a fixed size 256 byte register, and the packets vary in size. Maybe I got the syntax wrong here? but anyway, they are always going to be different sized. Example packet:

 
    const packetSETVDC = &[_]u8{ 0x15, 0xBC, 0x4E, 0x35 };
    ...
    
    const STARTUP_SEQUENCE: []const []const u8 = &[_][]const u8{
        packetSETEXTC,
        packetSETVDC,
        ...
    }
 
    pub export fn zigInitLCD() void {
        for (STARTUP_SEQUENCE) |step| {
            dsi.send(step);
        }
    }

I also just tried a byte-by-byte copy using assembly, and that failed in a similar way to how the memcpy failed in releaseSmall. Hard to tell because so many steps and there seems to be a timing error writing to the LCD that might be part of the issue. ie. maybe the asm and memcpy are too fast.

Anyway, the Zig byte-by-byte copy works, so please dont waste time on this. It was more to flag up the strange packed struct error in case there was some Zig page alignment issue for aarch64. Thanks for the interest.

@memcpy(dest[0..packet.len], packet)

Both this, and your workaround, have the assumption that packet is never larger than dest, but I’m sure you were aware of that.

Compiler probably turned your loop into a memcpy anyway.

Thanks, so that fixed the size panic :slight_smile:

Compiler probably turned your loop into a memcpy anyway.

There is some difference, the loop works; .Debug or .ReleaseSmall

The memCopy only works for .releaseSmall. No panic now in .Debug, but LCD fails.

I think I found the cause of that strange packed struct crash I mentioned earlier!

The objdump symbol search wasnt working (linux), but I managed to extract the problematic code into a smaller binary and got the following disassembly for the 2 approaches:

<     40097684: d10013a8        sub     x8, x29, #0x4
<     40097688: b81fc3bf        stur    wzr, [x29, #-4]
<     4009768c: 91000509        add     x9, x8, #0x1
<     40097690: 52888448        mov     w8, #0x4422
<     40097694: 79000128        strh    w8, [x9]
---
>     40097684: b81fc3bf        stur    wzr, [x29, #-4]
>     40097688: 52800448        mov     w8, #0x22
>     4009768c: 381fd3a8        sturb   w8, [x29, #-3]
>     40097690: 52800888        mov     w8, #0x44
>     40097694: 381fe3a8        sturb   w8, [x29, #-2]

I’m new to armv8, but the ref. manual says STRH has a halfword alignment check. It mentions control via a bit flag in ‘SCTLR.A or HSCTLR.A is 0’ and elsewhere I found mention about needing MMU setup… lots to learn!

So this sounds like I just have some assembly missing when the PinePhone boots and I switch EL levels. I was hoping u-boot had done the complex stuff :slight_smile:

Anyway my positive result today is that with debug removed, the device is now booting ~2s, thank-you Zig :slight_smile:

2 Likes