Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

destination type 'u32' has size 4 but source type has size 5 #5

Open
lynaghk opened this issue Apr 14, 2021 · 5 comments
Open

destination type 'u32' has size 4 but source type has size 5 #5

lynaghk opened this issue Apr 14, 2021 · 5 comments

Comments

@lynaghk
Copy link
Owner

lynaghk commented Apr 14, 2021

When trying to write to a register, I'm getting a size mismatch.

HW.tim3.cr1.write(.{ .cen = .enabled });

gives the error:

./src/hw/stm32f0x0.zig:29:39: error: destination type 'u32' has size 4 but source type 'hw.stm32f0x0.cr1_val' has size 5
            self.raw_ptr.* = @bitCast(u32, value);
@compileLog(@bitSizeOf(HW.tim3.cr1_val));
@compileLog(@sizeOf(HW.tim3.cr1_val));

gives 32 and 5.

So I guess it's an alignment issue?
Not sure how to fix.

Here's the definition, just FYI:

///General-purpose-timers
pub const tim3 = struct {

    //////////////////////////
    ///CR1
    pub const cr1_val = packed struct {
        ///CEN [0:0]
        ///Counter enable
        cen: packed enum(u1) {
            ///Counter disabled
            disabled = 0,
            ///Counter enabled
            enabled = 1,
        } = .disabled,
        ///UDIS [1:1]
        ///Update disable
        udis: packed enum(u1) {
            ///Update event enabled
            enabled = 0,
            ///Update event disabled
            disabled = 1,
        } = .enabled,
        ///URS [2:2]
        ///Update request source
        urs: packed enum(u1) {
            ///Any of counter overflow/underflow, setting UG, or update through slave mode, generates an update interrupt or DMA request
            any_event = 0,
            ///Only counter overflow/underflow generates an update interrupt or DMA request
            counter_only = 1,
        } = .any_event,
        ///OPM [3:3]
        ///One-pulse mode
        opm: packed enum(u1) {
            ///Counter is not stopped at update event
            disabled = 0,
            ///Counter stops counting at the next update event (clearing the CEN bit)
            enabled = 1,
        } = .disabled,
        ///DIR [4:4]
        ///Direction
        dir: packed enum(u1) {
            ///Counter used as upcounter
            up = 0,
            ///Counter used as downcounter
            down = 1,
        } = .up,
        ///CMS [5:6]
        ///Center-aligned mode
        ///selection
        cms: packed enum(u2) {
            ///The counter counts up or down depending on the direction bit
            edge_aligned = 0,
            ///The counter counts up and down alternatively. Output compare interrupt flags are set only when the counter is counting down.
            center_aligned1 = 1,
            ///The counter counts up and down alternatively. Output compare interrupt flags are set only when the counter is counting up.
            center_aligned2 = 2,
            ///The counter counts up and down alternatively. Output compare interrupt flags are set both when the counter is counting up or down.
            center_aligned3 = 3,
        } = .edge_aligned,
        ///ARPE [7:7]
        ///Auto-reload preload enable
        arpe: packed enum(u1) {
            ///TIMx_APRR register is not buffered
            disabled = 0,
            ///TIMx_APRR register is buffered
            enabled = 1,
        } = .disabled,
        ///CKD [8:9]
        ///Clock division
        ckd: packed enum(u2) {
            ///t_DTS = t_CK_INT
            div1 = 0,
            ///t_DTS = 2 × t_CK_INT
            div2 = 1,
            ///t_DTS = 4 × t_CK_INT
            div4 = 2,
        } = .div1,
        _unused10: u22 = 0,
    };
    ///control register 1
    pub const cr1 = Register(cr1_val).init(0x40000400 + 0x0);
...
@jamii
Copy link

jamii commented Apr 15, 2021

[nix-shell:~]$ cat test.zig
pub const s1 = packed struct {
    a: u8,
    b: u16,
};

pub const s2 = packed struct {
    a: u8,
    b: u17,
};

pub fn main() void {
    @compileLog(@sizeOf(s1));
    @compileLog(@sizeOf(s2));
}

[nix-shell:~]$ tmp/zig-linux-x86_64-0.8.0-dev.1860+1fada3746/zig run ./test.zig
| 3
| 5

@jamii
Copy link

jamii commented Apr 15, 2021

Yeah, looks like a bug.

@rbino
Copy link

rbino commented Apr 15, 2021

While debugging the same issue in svd4zig I ended up doing this.This seems to derive from ziglang/zig#2627.

Empirically, it seems the problem arises when portions of registers "cross" 16-bit boundaries. Just to be on the safe side, my code splits unused bits on 8-bit boundaries (I only do this on unused portions since I would like to keep everything else as-is). This is not always the case though (see last test below, which crosses the boundary but gets the correct size).

Here are some tests based on the example above that try to move around some stuff to see what happens:

const assert = @import("std").debug.assert;

pub const fail_orig = packed struct {
    cen: packed enum(u1) {
        disabled = 0,
        enabled = 1,
    } = .disabled,
    udis: packed enum(u1) {
        enabled = 0,
        disabled = 1,
    } = .enabled,
    urs: packed enum(u1) {
        any_event = 0,
        counter_only = 1,
    } = .any_event,
    opm: packed enum(u1) {
        disabled = 0,
        enabled = 1,
    } = .disabled,
    dir: packed enum(u1) {
        up = 0,
        down = 1,
    } = .up,
    cms: packed enum(u2) {
        edge_aligned = 0,
        center_aligned1 = 1,
        center_aligned2 = 2,
        center_aligned3 = 3,
    } = .edge_aligned,
    arpe: packed enum(u1) {
        disabled = 0,
        enabled = 1,
    } = .disabled,
    ckd: packed enum(u2) {
        div1 = 0,
        div2 = 1,
        div4 = 2,
    } = .div1,
    // Initial failing example, this unused piece crosses the 16-bit boundary
    // The struct has size 5
    _unused10: u22 = 0,
};

pub const ok = packed struct {
    cen: packed enum(u1) {
        disabled = 0,
        enabled = 1,
    } = .disabled,
    udis: packed enum(u1) {
        enabled = 0,
        disabled = 1,
    } = .enabled,
    urs: packed enum(u1) {
        any_event = 0,
        counter_only = 1,
    } = .any_event,
    opm: packed enum(u1) {
        disabled = 0,
        enabled = 1,
    } = .disabled,
    dir: packed enum(u1) {
        up = 0,
        down = 1,
    } = .up,
    cms: packed enum(u2) {
        edge_aligned = 0,
        center_aligned1 = 1,
        center_aligned2 = 2,
        center_aligned3 = 3,
    } = .edge_aligned,
    arpe: packed enum(u1) {
        disabled = 0,
        enabled = 1,
    } = .disabled,
    ckd: packed enum(u2) {
        div1 = 0,
        div2 = 1,
        div4 = 2,
    } = .div1,
    // Splitting this so that it doesn't go over 16-bit boundary
    // The struct has size 4
    _unused10: u6 = 0,
    _unused16: u16 = 0,
};

pub const fail_invert = packed struct {
    cen: packed enum(u1) {
        disabled = 0,
        enabled = 1,
    } = .disabled,
    udis: packed enum(u1) {
        enabled = 0,
        disabled = 1,
    } = .enabled,
    urs: packed enum(u1) {
        any_event = 0,
        counter_only = 1,
    } = .any_event,
    opm: packed enum(u1) {
        disabled = 0,
        enabled = 1,
    } = .disabled,
    dir: packed enum(u1) {
        up = 0,
        down = 1,
    } = .up,
    cms: packed enum(u2) {
        edge_aligned = 0,
        center_aligned1 = 1,
        center_aligned2 = 2,
        center_aligned3 = 3,
    } = .edge_aligned,
    arpe: packed enum(u1) {
        disabled = 0,
        enabled = 1,
    } = .disabled,
    ckd: packed enum(u2) {
        div1 = 0,
        div2 = 1,
        div4 = 2,
    } = .div1,
    // If we invert them, they cross the 16-bit boundary again
    // The struct has size 5
    _unused16: u16 = 0,
    _unused10: u6 = 0,
};

pub const fail_2bit_cross = packed struct {
    cen: packed enum(u1) {
        disabled = 0,
        enabled = 1,
    } = .disabled,
    udis: packed enum(u1) {
        enabled = 0,
        disabled = 1,
    } = .enabled,
    urs: packed enum(u1) {
        any_event = 0,
        counter_only = 1,
    } = .any_event,
    opm: packed enum(u1) {
        disabled = 0,
        enabled = 1,
    } = .disabled,
    dir: packed enum(u1) {
        up = 0,
        down = 1,
    } = .up,
    cms: packed enum(u2) {
        edge_aligned = 0,
        center_aligned1 = 1,
        center_aligned2 = 2,
        center_aligned3 = 3,
    } = .edge_aligned,
    arpe: packed enum(u1) {
        disabled = 0,
        enabled = 1,
    } = .disabled,
    ckd: packed enum(u2) {
        div1 = 0,
        div2 = 1,
        div4 = 2,
    } = .div1,
    _unused10: u5 = 0,
    // Here we minimize the crossing part, only these 2 bits (15-16) are crossing
    // The struct has size 5
    _unused15: u2 = 0,
    _unused16: u15 = 0,
};

pub const fail_minimal_cross = packed struct {
    _stuff1: u8,
    // This crosses the 16-bit boundary
    // This struct has size 5 
    _stuff2: u24,
};

pub const ok_but_cross = packed struct {
    _stuff1: u7,
    // This crosses the 16-bit boundary too
    // But the struct has size 4
    _stuff2: u25,
};

test "register size" {
  assert(@sizeOf(fail_orig) == 5);
  assert(@sizeOf(ok) == 4);
  assert(@sizeOf(fail_invert) == 5);
  assert(@sizeOf(fail_2bit_cross) == 5);
  assert(@sizeOf(fail_minimal_cross) == 5);
  assert(@sizeOf(ok_but_cross) == 4);
}

@jamii
Copy link

jamii commented Apr 15, 2021

Thanks for the workaround @rbino

@lynaghk
Copy link
Owner Author

lynaghk commented Apr 16, 2021

Yikes. Good research @rbino! Not sure how I want to resolve this --- either impl a workaround in my register generation or wait until it gets fixed upstream. Will leave this open until then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants