Can Julia access Memory locations or Registers directly

Hi all, I’m after some advice on low level memory access.
What is the best way to access (read or write) to individual registers in an embedded application with Julia?
Say I want to read address 0x6000 0000 plus an offset from there to confirm a pin driver configuration. Is there a Julia instruction to read an address directly?

1 Like

unsafe_read might be what you are looking for.

Nice lead! I’ll check it out, thanks.

unsafe_load seems suitable for this.

1 Like

If you want to use pointer, then

offset = ...
p = Ptr{Nothing}(0x60000000 + offset) # build a void* pointer
Base.unsafe_convert(Ptr{...},p) # convert the pointer to a given type
Base.unsafe_load(p) # load the value from the pointer

If you want to read or write registers directly, I guess the best you can do is to use LLVM’s assembler expressions, check LLVM’s documentation.

1 Like

And to use these from Julia, you can write your own llvmcall expressions, or use LLVM.jl. An example from CUDA.jl:

using LLVM
using LLVM.interop

dynamic_smem_size() = @asmcall("mov.u32 \$0, %dynamic_smem_size;", "=r", true, UInt32, Tuple{})
1 Like

Thanks Chen,
I tried p = Ptr{Nothing}(0x60000000 + offset) but this gives a Method error: no method matching Ptr{Nothing}(::UInt32)

But p=Ptr{Nothing}() is accepted, and returns Ptr{Nothing} @0x0000000000000000

If it works elsewhere, is there a package required?

Thanks Tim,
I’ll put LLVM in my “plan B” tray while I try out the unsafe_ Ptr permutations.

Looks like you’re working on a 64bit system where a pointer is of 64 bits ( it has the same size as a 64bit integer and can be safely casted from a 64bit integer, but not from a 32bit integer), so you need to use Ptr{Nothing}(0x6000000000000000) instead.

But…p=Ptr{Nothing}(0x0000000060000000) is accepted and returns
Ptr{Nothing} @0x0000000060000000

Its a Jetson Nano.

You’re right, that was accepted.

Progress so far…

julia> offset = 0x3230

julia> p=Ptr{Nothing}(0x0000000060000000 + offset)
Ptr{Nothing} @0x0000000060003230

julia> pc=Base.unsafe_convert(Ptr{UInt64},p)
Ptr{UInt64} @0x0000000060003230

julia> regval=Base.unsafe_load(pc)

signal (11): Segmentation fault
in expression starting at REPL[4]:1
unsafe_load at ./pointer.jl:105 [inlined]
unsafe_load at ./pointer.jl:105
unknown function (ip: 0x7f62395faf)
Allocations: 2650 (Pool: 2639; Big: 11); GC: 0
Segmentation fault (core dumped)

&Dumped, as expected.&

Maybe you should first test whether the equivalent C program works, like:

int main(){
    unsigned long a = 0x0000000060003230;
    unsigned long *p = (unsigned long *)(a);
    return 0;

I guess the problem here is that we need system calls to perform these IO actions (that is, confirm a pin driver configuration), instead of directly load values from the memory. Or the memory address is the mapped one…
It would be easier if you can just wrap these kinds of low level codes in C and provide the utility as a dynamic library, so you can directly call it from Julia instead of twisting pointers directly in Julia…