From 0e0c4df6fef696b8318f60292a49bbd47d0ba6fd Mon Sep 17 00:00:00 2001 From: RecursiveError Date: Mon, 14 Sep 2026 02:13:36 -0300 Subject: [PATCH 1/2] Add support for MCX-A156 chip --- examples/nxp/mcx/build.zig | 2 + examples/nxp/mcx/src/mcxa156.zig | 23 + port/nxp/mcx/build.zig | 116 ++++- port/nxp/mcx/src/boards/frdm_mcxa156.zig | 0 port/nxp/mcx/src/mcxa_15x/hal.zig | 4 + port/nxp/mcx/src/mcxa_15x/hal/clkout.zig | 33 ++ port/nxp/mcx/src/mcxa_15x/hal/gpio.zig | 118 +++++ port/nxp/mcx/src/mcxa_15x/hal/port.zig | 63 +++ port/nxp/mcx/src/mcxa_15x/hal/syscon.zig | 522 +++++++++++++++++++++++ 9 files changed, 873 insertions(+), 8 deletions(-) create mode 100644 examples/nxp/mcx/src/mcxa156.zig create mode 100644 port/nxp/mcx/src/boards/frdm_mcxa156.zig create mode 100644 port/nxp/mcx/src/mcxa_15x/hal.zig create mode 100644 port/nxp/mcx/src/mcxa_15x/hal/clkout.zig create mode 100644 port/nxp/mcx/src/mcxa_15x/hal/gpio.zig create mode 100644 port/nxp/mcx/src/mcxa_15x/hal/port.zig create mode 100644 port/nxp/mcx/src/mcxa_15x/hal/syscon.zig diff --git a/examples/nxp/mcx/build.zig b/examples/nxp/mcx/build.zig index 0606d651c..e4534f01f 100644 --- a/examples/nxp/mcx/build.zig +++ b/examples/nxp/mcx/build.zig @@ -13,9 +13,11 @@ pub fn build(b: *std.Build) void { const mb = MicroBuild.init(b, mz_dep) orelse return; const frdm_mcxa153 = mb.ports.mcx.chips.mcxa153; + const frdm_mcxa156 = mb.ports.mcx.chips.mcxa156; const frdm_mcxn947 = mb.ports.mcx.chips.mcxn947; const available_examples = [_]Example{ + .{ .name = "mcxa156", .target = frdm_mcxa156, .file = "src/mcxa156.zig" }, .{ .name = "mcxa153_blinky", .target = frdm_mcxa153, .file = "src/mcxa153_blinky.zig" }, .{ .name = "mcxn947_blinky", .target = frdm_mcxn947, .file = "src/mcxn947_blinky.zig" }, .{ .name = "gpio_input", .target = frdm_mcxa153, .file = "src/gpio_input.zig" }, diff --git a/examples/nxp/mcx/src/mcxa156.zig b/examples/nxp/mcx/src/mcxa156.zig new file mode 100644 index 000000000..0e8e2b361 --- /dev/null +++ b/examples/nxp/mcx/src/mcxa156.zig @@ -0,0 +1,23 @@ +pub const std_options = microzig.std_options(.{}); + +const microzig = @import("microzig"); +const std = @import("std"); +const hal = microzig.hal; +const Port = hal.port.Port; +const clkout = hal.clkout; + +const PORT4: Port = @fromBackingInt(@intCast(4)); + +comptime { + _ = microzig.export_startup(); +} + +pub fn main() !void { + PORT4.init(); + PORT4.configure_pin(2, .{ .MUX = 1 }); + clkout.enable(.SLOW_CLK, 0); + try hal.syscon.clock_init(.{ + .frohf = .{ .freq = .@"96Mhz", .fro_hf_div = 1 }, + .ahb_div = 4, + }); +} diff --git a/port/nxp/mcx/build.zig b/port/nxp/mcx/build.zig index 1e1d91af4..36987d34c 100644 --- a/port/nxp/mcx/build.zig +++ b/port/nxp/mcx/build.zig @@ -4,12 +4,27 @@ const microzig = @import("microzig/build-internals"); const Self = @This(); chips: struct { + //MCX-A 153/152/143/142/132/133 mcxa153: *const microzig.Target, + + //MCX-A 154/155/156/144/145/146 + //mcxa144: *const microzig.Target, TODO + //mcxa145: *const microzig.Target, TODO + //mcxa146: *const microzig.Target, TODO + mcxa154: *const microzig.Target, + mcxa155: *const microzig.Target, + mcxa156: *const microzig.Target, + + //MCX-N low density + //mcxn256: *const microzig.Target, //TODO + + //MCX-N HIGH density mcxn947: *const microzig.Target, }, boards: struct { frdm_mcxa153: *const microzig.Target, + frdm_mcxa156: *const microzig.Target, frdm_mcxn947: *const microzig.Target, }, @@ -18,6 +33,69 @@ pub fn init(dep: *std.Build.Dependency) ?Self { const mcux_soc_svd = b.lazyDependency("mcux-soc-svd", .{}) orelse return null; + const chip_mcxa154: microzig.Target = .{ + .dep = dep, + .preferred_binary_format = .elf, + .zig_target = .{ + .cpu_arch = .thumb, + .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m33 }, + .os_tag = .freestanding, + .abi = .eabihf, + }, + .chip = .{ + .name = "MCXA154", + .register_definition = .{ .svd = mcux_soc_svd.path("MCXA154/MCXA154.xml") }, + .memory_regions = &.{ + .{ .tag = .flash, .offset = 0x00000000, .length = 256 * 1024, .access = .rx }, + .{ .tag = .ram, .offset = 0x20000000, .length = 64 * 1024, .access = .rw }, + .{ .tag = .ram, .offset = 0x2001E000, .length = 8 * 1024, .access = .rw, .name = "ECC" }, + }, + }, + .hal = .{ .root_source_file = b.path("src/mcxa_15x/hal.zig") }, + }; + + const chip_mcxa155: microzig.Target = .{ + .dep = dep, + .preferred_binary_format = .elf, + .zig_target = .{ + .cpu_arch = .thumb, + .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m33 }, + .os_tag = .freestanding, + .abi = .eabihf, + }, + .chip = .{ + .name = "MCXA155", + .register_definition = .{ .svd = mcux_soc_svd.path("MCXA155/MCXA155.xml") }, + .memory_regions = &.{ + .{ .tag = .flash, .offset = 0x00000000, .length = 512 * 1024, .access = .rx }, + .{ .tag = .ram, .offset = 0x20000000, .length = 96 * 1024, .access = .rw }, + .{ .tag = .ram, .offset = 0x2001E000, .length = 8 * 1024, .access = .rw, .name = "ECC" }, + }, + }, + .hal = .{ .root_source_file = b.path("src/mcxa_15x/hal.zig") }, + }; + + const chip_mcxa156: microzig.Target = .{ + .dep = dep, + .preferred_binary_format = .elf, + .zig_target = .{ + .cpu_arch = .thumb, + .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m33 }, + .os_tag = .freestanding, + .abi = .eabihf, + }, + .chip = .{ + .name = "MCXA156", + .register_definition = .{ .svd = mcux_soc_svd.path("MCXA156/MCXA156.xml") }, + .memory_regions = &.{ + .{ .tag = .flash, .offset = 0x00000000, .length = 1024 * 1024, .access = .rx }, + .{ .tag = .ram, .offset = 0x20000000, .length = 64 * 1024, .access = .rw }, + //.{ .tag = .ram, .offset = 0x2001E000, .length = 8 * 1024, .access = .rw, .name = "ECC" }, + }, + }, + .hal = .{ .root_source_file = b.path("src/mcxa_15x/hal.zig") }, + }; + const chip_mcxa153: microzig.Target = .{ .dep = dep, .preferred_binary_format = .elf, @@ -63,14 +141,36 @@ pub fn init(dep: *std.Build.Dependency) ?Self { }; return .{ - .chips = .{ .mcxa153 = chip_mcxa153.derive(.{}), .mcxn947 = chip_mcxn947.derive(.{}) }, - .boards = .{ .frdm_mcxa153 = chip_mcxa153.derive(.{ - .board = .{ - .name = "FRDM Development Board for MCX A153", - .url = "https://www.nxp.com/part/FRDM-MCXA153", - .root_source_file = b.path("src/boards/frdm_mcxa153.zig"), - }, - }), .frdm_mcxn947 = chip_mcxn947.derive(.{ .board = .{ .name = "FRDM Development Board for MCX N947", .url = "https://www.nxp.com/part/FRDM-MCXN947", .root_source_file = b.path("src/boards/frdm_mcxn947.zig") } }) }, + .chips = .{ + .mcxa153 = chip_mcxa153.derive(.{}), + .mcxa154 = chip_mcxa154.derive(.{}), + .mcxa155 = chip_mcxa155.derive(.{}), + .mcxa156 = chip_mcxa156.derive(.{}), + .mcxn947 = chip_mcxn947.derive(.{}), + }, + .boards = .{ + .frdm_mcxa153 = chip_mcxa153.derive(.{ + .board = .{ + .name = "FRDM Development Board for MCX A153", + .url = "https://www.nxp.com/part/FRDM-MCXA153", + .root_source_file = b.path("src/boards/frdm_mcxa153.zig"), + }, + }), + .frdm_mcxn947 = chip_mcxn947.derive(.{ + .board = .{ + .name = "FRDM Development Board for MCX N947", + .url = "https://www.nxp.com/part/FRDM-MCXN947", + .root_source_file = b.path("src/boards/frdm_mcxn947.zig"), + }, + }), + .frdm_mcxa156 = chip_mcxa156.derive(.{ + .board = .{ + .name = "FRDM Development Board for MCX A156", + .url = "https://www.nxp.com/part/FRDM-MCXA156", + .root_source_file = b.path("src/boards/frdm_mcxa156.zig"), + }, + }), + }, }; } diff --git a/port/nxp/mcx/src/boards/frdm_mcxa156.zig b/port/nxp/mcx/src/boards/frdm_mcxa156.zig new file mode 100644 index 000000000..e69de29bb diff --git a/port/nxp/mcx/src/mcxa_15x/hal.zig b/port/nxp/mcx/src/mcxa_15x/hal.zig new file mode 100644 index 000000000..a4063cbd8 --- /dev/null +++ b/port/nxp/mcx/src/mcxa_15x/hal.zig @@ -0,0 +1,4 @@ +pub const syscon = @import("hal/syscon.zig"); +pub const clkout = @import("hal/clkout.zig"); +pub const port = @import("hal/port.zig"); +pub const gpio = @import("hal/gpio.zig"); diff --git a/port/nxp/mcx/src/mcxa_15x/hal/clkout.zig b/port/nxp/mcx/src/mcxa_15x/hal/clkout.zig new file mode 100644 index 000000000..2a136d60c --- /dev/null +++ b/port/nxp/mcx/src/mcxa_15x/hal/clkout.zig @@ -0,0 +1,33 @@ +const microzig = @import("microzig"); +const syscon = @import("syscon.zig"); +const MRCC0 = microzig.chip.peripherals.MRCC0; + +const Clock_Source = enum(u3) { + FRO_12M = 0, + FRO_HF_DIV = 1, + CLK_IN = 2, + CLK_16K = 3, + SLOW_CLK = 6, + no_Clock = 7, +}; + +pub fn enable(src: Clock_Source, div: u4) void { + syscon.unlock_clock_configuration(); + defer syscon.freeze_clock_configuration(); + + MRCC0.MRCC_CLKOUT_CLKSEL.modify_one("MUX", @fromBackingInt(@intCast(@backingInt(src)))); + + MRCC0.MRCC_CLKOUT_CLKDIV.write(.{ + .DIV = div, + .HALT = .OFF, + .RESET = .OFF, + }); + + MRCC0.MRCC_CLKOUT_CLKDIV.write(.{ + .DIV = div, + .HALT = .ON, + .RESET = .ON, + }); + + while (MRCC0.MRCC_CLKOUT_CLKDIV.read().UNSTAB == .OFF) {} +} diff --git a/port/nxp/mcx/src/mcxa_15x/hal/gpio.zig b/port/nxp/mcx/src/mcxa_15x/hal/gpio.zig new file mode 100644 index 000000000..b225774d1 --- /dev/null +++ b/port/nxp/mcx/src/mcxa_15x/hal/gpio.zig @@ -0,0 +1,118 @@ +const microzig = @import("microzig"); +const syscon = @import("./syscon.zig"); + +const chip = microzig.chip; + +pub fn num(comptime n: u2, comptime pin: u5) GPIO { + return @fromBackingInt(@as(u8, n) << 5 | pin); +} + +pub const GPIO = enum(u8) { + _, + + pub fn init(comptime gpio: GPIO) void { + const tag = switch (gpio.get_n()) { + 0 => .GPIO0, + 1 => .GPIO1, + 2 => .GPIO2, + 3 => .GPIO3, + 4 => .GPIO4, + else => unreachable, + }; + + syscon.reset_release(tag); + syscon.enable_clock(tag); + } + + pub fn put(gpio: GPIO, output: u1) void { + const regs = gpio.get_regs(); + const old: u32 = regs.PDOR.raw; + const new = @as(u32, output) << gpio.get_pin(); + + regs.PDOR.write_raw(old & ~gpio.get_mask() | new); + } + + pub fn get(gpio: GPIO) bool { + const regs = gpio.get_regs(); + + return regs.PDIR.raw >> gpio.get_pin() & 1 != 0; + } + + pub fn toggle(gpio: GPIO) void { + const regs = gpio.get_regs(); + const old: u32 = regs.PTOR.raw; + + regs.PTOR.write_raw(old | gpio.get_mask()); + } + + pub fn set_direction(gpio: GPIO, direction: Direction) void { + const regs = gpio.get_regs(); + const old: u32 = regs.PDDR.raw; + const new = @as(u32, @backingInt(direction)) << gpio.get_pin(); + + regs.PDDR.write_raw(old & ~gpio.get_mask() | new); + } + + pub fn set_interrupt_config(gpio: GPIO, trigger: InterruptConfig) void { + const regs = gpio.get_regs(); + const irqc = @as(u32, @backingInt(trigger)) << 16; + const isf = @as(u32, 1) << 24; + + regs.ICR[gpio.get_pin()].write_raw(irqc | isf); + } + + pub fn get_interrupt_flag(gpio: GPIO) bool { + const regs = gpio.get_regs(); + + return regs.ISFR0.raw >> gpio.get_pin() & 1 != 0; + } + + pub fn clear_interrupt_flag(gpio: GPIO) void { + const regs = gpio.get_regs(); + const old: u32 = regs.ISFR0.raw; + + regs.ISFR0.write_raw(old | gpio.get_mask()); + } + + fn get_regs(gpio: GPIO) *volatile chip.types.peripherals.GPIO0 { + return switch (gpio.get_n()) { + 0 => chip.peripherals.GPIO0, + 1 => chip.peripherals.GPIO1, + 2 => chip.peripherals.GPIO2, + 3 => chip.peripherals.GPIO3, + 4 => chip.peripherals.GPIO4, + else => unreachable, + }; + } + + inline fn get_n(gpio: GPIO) u3 { + return @intCast(@backingInt(gpio) >> 5); + } + + inline fn get_pin(gpio: GPIO) u5 { + return @intCast(@backingInt(gpio) & 0x1f); + } + + inline fn get_mask(gpio: GPIO) u32 { + return @as(u32, 1) << gpio.get_pin(); + } +}; + +const Direction = enum(u1) { in, out }; + +const InterruptConfig = enum(u4) { + disabled = 0, + dma_rising_edge = 1, + dma_falling_edge = 2, + dma_either_edge = 3, + flag_rising_edge = 5, + flag_falling_edge = 6, + flag_either_edge = 7, + interrupt_logic_zero = 8, + interrupt_rising_edge = 9, + interrupt_falling_edge = 10, + interrupt_either_edge = 11, + interrupt_logic_one = 12, + active_high_trigger_output_enable = 13, + active_low_trigger_output_enable = 14, +}; diff --git a/port/nxp/mcx/src/mcxa_15x/hal/port.zig b/port/nxp/mcx/src/mcxa_15x/hal/port.zig new file mode 100644 index 000000000..1d2dbb0e5 --- /dev/null +++ b/port/nxp/mcx/src/mcxa_15x/hal/port.zig @@ -0,0 +1,63 @@ +const microzig = @import("microzig"); +const syscon = @import("./syscon.zig"); +const gpio = @import("./gpio.zig"); + +const PORT_T = microzig.chip.types.peripherals.PORT0; + +//TODO ADD ALL PIN CONFG +pub const PinConfig = struct { + MUX: u3, +}; + +const PIN = @FieldType(microzig.chip.types.peripherals.PORT0, "PCR0"); + +pub fn num(comptime n: u2) Port { + return @fromBackingInt(n); +} + +pub const Port = enum(u3) { + _, + + pub fn init(comptime port: Port) void { + const tag = switch (@backingInt(port)) { + 0 => .PORT0, + 1 => .PORT1, + 2 => .PORT2, + 3 => .PORT3, + 4 => .PORT4, + else => @panic("INVALID PORT"), + }; + + syscon.reset_release(tag); + syscon.enable_clock(tag); + } + + pub fn get_regs(self: Port) *volatile PORT_T { + const val: u3 = @backingInt(self); + + if (val > 4) @panic("INVALID PORT"); + + return switch (val) { + 0 => microzig.chip.peripherals.PORT0, + 1 => @ptrCast(microzig.chip.peripherals.PORT1), + 2 => @ptrCast(microzig.chip.peripherals.PORT2), + 3 => @ptrCast(microzig.chip.peripherals.PORT3), + 4 => @ptrCast(microzig.chip.peripherals.PORT4), + else => unreachable, + }; + } + + pub fn configure_pin(self: Port, pin: u5, config: PinConfig) void { + const regs = self.get_regs(); + const pin_regs: *volatile PIN = @ptrFromInt(@as(usize, @intFromPtr(regs)) + (0x80 + (4 * @as(usize, pin)))); + + pin_regs.modify_one("LK", .lk0); + defer pin_regs.modify_one("LK", .lk1); + + pin_regs.modify_one("MUX", @fromBackingInt(@intCast(config.MUX))); + } + + pub fn get_gpio(comptime port: Port, comptime pin: u5) gpio.GPIO { + return gpio.num(@backingInt(port), pin); + } +}; diff --git a/port/nxp/mcx/src/mcxa_15x/hal/syscon.zig b/port/nxp/mcx/src/mcxa_15x/hal/syscon.zig new file mode 100644 index 000000000..b1e4c89f8 --- /dev/null +++ b/port/nxp/mcx/src/mcxa_15x/hal/syscon.zig @@ -0,0 +1,522 @@ +const microzig = @import("microzig"); + +const chip = microzig.chip; + +var clocks: Clock_Frequencies = .{}; + +pub inline fn unlock_clock_configuration() void { + chip.peripherals.SYSCON.CLKUNLOCK.write(.{ .UNLOCK = .ENABLE }); +} + +pub inline fn freeze_clock_configuration() void { + chip.peripherals.SYSCON.CLKUNLOCK.write(.{ .UNLOCK = .FREEZE }); +} + +pub fn enable_clock(comptime peripheral: Peripheral) void { + unlock_clock_configuration(); + defer freeze_clock_configuration(); + + switch (peripheral.cc()) { + 0 => chip.peripherals.MRCC0.MRCC_GLB_CC0_SET.write_raw( + @as(u32, @bitCast(chip.peripherals.MRCC0.MRCC_GLB_CC0_SET)) | peripheral.mask(), + ), + 1 => chip.peripherals.MRCC0.MRCC_GLB_CC1_SET.write_raw( + @as(u32, @bitCast(chip.peripherals.MRCC0.MRCC_GLB_CC1_SET)) | peripheral.mask(), + ), + } +} + +pub fn disable_clock(comptime peripheral: Peripheral) void { + unlock_clock_configuration(); + defer freeze_clock_configuration(); + + switch (peripheral.cc()) { + 0 => chip.peripherals.MRCC0.MRCC_GLB_CC0_CLR.write_raw( + @as(u32, @bitCast(chip.peripherals.MRCC0.MRCC_GLB_CC0_CLR)) | peripheral.mask(), + ), + 1 => chip.peripherals.MRCC0.MRCC_GLB_CC1_CLR.write_raw( + @as(u32, @bitCast(chip.peripherals.MRCC0.MRCC_GLB_CC1_CLR)) | peripheral.mask(), + ), + } +} + +pub fn reset_release(comptime peripheral: Peripheral) void { + unlock_clock_configuration(); + defer freeze_clock_configuration(); + + switch (peripheral.cc()) { + 0 => chip.peripherals.MRCC0.MRCC_GLB_RST0_SET.write_raw( + @as(u32, @bitCast(chip.peripherals.MRCC0.MRCC_GLB_RST0_SET)) | peripheral.mask(), + ), + 1 => chip.peripherals.MRCC0.MRCC_GLB_RST1_SET.write_raw( + @as(u32, @bitCast(chip.peripherals.MRCC0.MRCC_GLB_RST1_SET)) | peripheral.mask(), + ), + } +} + +pub fn reset_assert(comptime peripheral: Peripheral) void { + unlock_clock_configuration(); + defer freeze_clock_configuration(); + + switch (peripheral.cc()) { + 0 => chip.peripherals.MRCC0.MRCC_GLB_RST0_CLR.write_raw( + @as(u32, @bitCast(chip.peripherals.MRCC0.MRCC_GLB_RST0_CLR)) | peripheral.mask(), + ), + 1 => chip.peripherals.MRCC0.MRCC_GLB_RST1_CLR.write_raw( + @as(u32, @bitCast(chip.peripherals.MRCC0.MRCC_GLB_RST1_CLR)) | peripheral.mask(), + ), + } +} + +pub const Peripheral = enum { + // MRCC_GLB_CC0, MRCC_GLB_ACC0, MRCC_GLB_RST0 + INPUTMUX0, + I3C0, + CTIMER0, + CTIMER1, + CTIMER2, + CTIMER3, + CTIMER4, + FREQME, + UTICK0, + DMA, + AOI0, + CRC0, + EIM0, + ERM0, + AOI1, + FLEXIO0, + LPI2C0, + LP12C1, + LPSPI0, + LPSPI1, + LPUART0, + LPUART1, + LPUART2, + LPUART3, + LPUART4, + USB0, + QDC0, + QDC1, + FLEXPWM0, + FLEXPWM1, + + // MRCC_GLB_CC1, MRCC_GLB_ACC1, MRCC_GLB_RST1 + OSTIMER0, + ADC0, + ADC1, + CMP1, + DAC0, + OPAMP0, + + PORT0, + PORT1, + PORT2, + PORT3, + PORT4, + FLEXCAN0, + LPI2C2, + LPI2C3, + GPIO0, + GPIO1, + GPIO2, + GPIO3, + GPIO4, + + fn cc(comptime peripheral: Peripheral) u1 { + return switch (peripheral) { + .INPUTMUX0, + .I3C0, + .CTIMER0, + .CTIMER1, + .CTIMER2, + .CTIMER3, + .CTIMER4, + .FREQME, + .UTICK0, + .DMA, + .AOI0, + .CRC0, + .EIM0, + .ERM0, + .AOI1, + .FLEXIO0, + .LPI2C0, + .LP12C1, + .LPSPI0, + .LPSPI1, + .LPUART0, + .LPUART1, + .LPUART2, + .LPUART3, + .LPUART4, + .USB0, + .QDC0, + .QDC1, + .FLEXPWM0, + .FLEXPWM1, + => 0, + + .OSTIMER0, + .ADC0, + .ADC1, + .CMP1, + .DAC0, + .OPAMP0, + .PORT0, + .PORT1, + .PORT2, + .PORT3, + .PORT4, + .FLEXCAN0, + .LPI2C2, + .LPI2C3, + .GPIO0, + .GPIO1, + .GPIO2, + .GPIO3, + .GPIO4, + => 1, + }; + } + + fn mask(comptime peripheral: Peripheral) u32 { + return switch (peripheral) { + .INPUTMUX0 => 0, + .I3C0 => 1, + .CTIMER0 => 1 << 2, + .CTIMER1 => 1 << 3, + .CTIMER2 => 1 << 4, + .CTIMER3 => 1 << 5, + .CTIMER4 => 1 << 6, + .FREQME => 1 << 7, + .UTICK0 => 1 << 8, + .DMA => 1 << 10, + .AOI0 => 1 << 11, + .CRC0 => 1 << 12, + .EIM0 => 1 << 13, + .ERM0 => 1 << 14, + .AOI1 => 1 << 16, + .FLEXIO0 => 1 << 17, + .LPI2C0 => 1 << 18, + .LP12C1 => 1 << 19, + .LPSPI0 => 1 << 20, + .LPSPI1 => 1 << 21, + .LPUART0 => 1 << 22, + .LPUART1 => 1 << 23, + .LPUART2 => 1 << 24, + .LPUART3 => 1 << 25, + .LPUART4 => 1 << 26, + .USB0 => 1 << 27, + .QDC0 => 1 << 28, + .QDC1 => 1 << 29, + .FLEXPWM0 => 1 << 30, + .FLEXPWM1 => 1 << 31, + .OSTIMER0 => 0, + .ADC0 => 1, + .ADC1 => 1 << 2, + .CMP1 => 1 << 4, + .DAC0 => 1 << 5, + .OPAMP0 => 1 << 6, + .PORT0 => 1 << 7, + .PORT1 => 1 << 8, + .PORT2 => 1 << 9, + .PORT3 => 1 << 10, + .PORT4 => 1 << 11, + .FLEXCAN0 => 1 << 12, + .LPI2C2 => 1 << 13, + .LPI2C3 => 1 << 14, + .GPIO0 => 1 << 20, + .GPIO1 => 1 << 21, + .GPIO2 => 1 << 22, + .GPIO3 => 1 << 23, + .GPIO4 => 1 << 24, + }; + } +}; + +const MRCC0 = microzig.chip.peripherals.MRCC0; +const scg0 = microzig.chip.peripherals.SCG0; +const spc0 = microzig.chip.peripherals.SPC0; +const FMU = microzig.chip.peripherals.FMU0; +const syscon = microzig.chip.peripherals.SYSCON; + +pub const SOSC_Range = enum(u2) { + @"8Mhz-16Mhz", + @"16Mhz-25Mhz", + @"25Mhz-40Mhz", + @"40Mhz-50Mhz", +}; + +///Selects the source for the external reference clock. This bit selects which clock is output from the SOSC +///into the SCG, whether from the crystal oscillator or from an external clock input. +pub const EREFS = enum(u1) { + ///reference clock selected. + external, + + ///crystal oscillator of OSC selected. + internal, +}; + +pub const System_Clock = enum(u3) { + SOSC = 1, + SIRC, + FRIC, + ROSC, +}; + +pub const FIRC_FREQ = enum(u3) { + @"48Mhz" = 1, + @"64Mhz" = 3, + @"96Mhz" = 5, + @"192Mhz" = 7, +}; + +pub const Drive_Mode = enum(u2) { + MidDriver = 1, + StandartDriver = 2, +}; + +//TODO add trim config +pub const SOSC_Config = struct { + freq_hz: usize = 8_000_000, + erefs: EREFS, + valid_int_en: bool = false, + monitor_en: bool = false, + stop_mode_en: bool = true, +}; + +//TODO: add trim confg +pub const FROLF_Config = struct { + en_periph_gate: bool = true, +}; + +pub const FROHF_Config = struct { + fro_hf_div: u4 = 0, + en_slow_periph_gate: bool = true, + en_fast_periph_gate: bool = true, + stop_en: bool = true, + error_int_en: bool = false, + firc_ready_int_en: bool = false, + freq: FIRC_FREQ = .@"48Mhz", +}; +pub const Power_Config = struct { + driver: Drive_Mode = .MidDriver, +}; + +pub const Clock_Config = struct { + socs: ?SOSC_Config = null, + frohf: ?FROHF_Config = FROHF_Config{}, + frolf: ?FROLF_Config = FROLF_Config{}, + power: Power_Config = .{}, + clock_source: System_Clock = .FRIC, + ahb_div: u8 = 0, + slow_clock_en: bool = true, +}; + +pub const Clock_Frequencies = struct { + clk_16k_0: usize = 16_384, + clk_16k_1: usize = 16_384, + clk_in: usize = 0, + fro_hf: usize = 0, + fro_hf_div: usize = 0, + clk_48m: usize = 0, + fro_12m: usize = 0, + clk_1m: usize = 1_000_000, + main_clk: usize = 0, + cpu_clk: usize = 0, + system_clk: usize = 0, + slow_clk: usize = 0, + + pub fn maxForMode(mode: Drive_Mode) Clock_Frequencies { + return switch (mode) { + .MidDriver => .{ + .clk_16k_0 = 16_384, + .clk_16k_1 = 16_384, + .clk_in = 50_000_000, + .fro_hf = 96_000_000, + .fro_hf_div = 48_000_000, + .clk_48m = 48_000_000, + .fro_12m = 12_000_000, + .clk_1m = 1_000_000, + .main_clk = 96_000_000, + .cpu_clk = 48_000_000, + .system_clk = 48_000_000, + .slow_clk = 12_000_000, + }, + .StandartDriver => .{ + .clk_16k_0 = 16_384, + .clk_16k_1 = 16_384, + .clk_in = 50_000_000, + .fro_hf = 192_000_000, + .fro_hf_div = 96_000_000, + .clk_48m = 48_000_000, + .fro_12m = 12_000_000, + .clk_1m = 1_000_000, + .main_clk = 192_000_000, + .cpu_clk = 96_000_000, + .system_clk = 96_000_000, + .slow_clk = 24_000_000, + }, + }; + } + + pub fn validate(self: Clock_Frequencies, mode: Drive_Mode) !void { + const max_values = maxForMode(mode); + + inline for (@typeInfo(@This()).@"struct".field_names) |name| { + const current = @field(self, name); + const max_allowed = @field(max_values, name); + + if (current > max_allowed) { + return error.Overflow; + } + } + } +}; + +pub fn clock_init(comptime config: Clock_Config) !void { + const inner_clocks = comptime calc_clock(config); + unlock_clock_configuration(); + inner_init(inner_clocks, config); + freeze_clock_configuration(); + clocks = inner_clocks; +} + +fn calc_clock(comptime config: Clock_Config) Clock_Frequencies { + var out_clk = Clock_Frequencies{}; + if (config.socs) |ext| { + out_clk.clk_in = ext.freq_hz; + } + if (config.frohf) |hf| { + out_clk.fro_hf = switch (hf.freq) { + .@"192Mhz" => 192_000_000, + .@"96Mhz" => 96_000_000, + .@"64Mhz" => 64_000_000, + .@"48Mhz" => 48_000_000, + }; + + if (hf.en_slow_periph_gate) { + out_clk.clk_48m = 48_000_000; + } + out_clk.fro_hf_div = out_clk.fro_hf / (hf.fro_hf_div + 1); + } + + if (config.socs) |_| { + @compileError("SOCS IS NOT SUPPORTED YET"); + } + + switch (config.clock_source) { + .FRIC => { + if (out_clk.fro_hf == 0) @compileError("MAIN CLOCK USE FRO_HF BUT FRO_HF IS DISABLED IN THIS CONFIG"); + out_clk.main_clk = out_clk.fro_hf; + }, + .SIRC => { + if (out_clk.fro_12m == 0) @compileError("MAIN CLOCK USE FRO_LF BUT FRO_LF IS DISABLED IN THIS CONFIG"); + out_clk.main_clk = out_clk.fro_12m; + }, + else => { + @compileError("MODE: " ++ @tagName(config.clock_source) ++ " IS NOT SUPPORTED YET"); + }, + } + + out_clk.cpu_clk = out_clk.main_clk / (config.ahb_div + 1); + out_clk.system_clk = out_clk.cpu_clk; + out_clk.slow_clk = out_clk.cpu_clk / 4; + out_clk.validate(config.power.driver) catch @compileError("INVALID CLOCK CONFIG"); + return out_clk; +} + +fn inner_init(comptime clks: Clock_Frequencies, config: Clock_Config) void { + //First put the hardware in a safe state: + safe_init(); + power_and_flash(clks.cpu_clk, config.power); + + //enable clocks + enable_clocks(config); + + //set main clk + syscon.AHBCLKDIV.modify_one("DIV", config.ahb_div); + while (syscon.AHBCLKDIV.read().UNSTAB == .ONGOING) {} + + scg0.RCCR.modify_one("SCS", @fromBackingInt(@intCast(@backingInt(config.clock_source)))); + while (@backingInt(scg0.CSR.read().SCS) != @backingInt(config.clock_source)) {} +} + +fn safe_init() void { + //set SIRC as the main clk, fro12_hf is always valid no matter the actual flash and power config + //also, FRO_HF cannot be configured while used as the main_clk + scg0.SIRCCSR.modify_one("LK", .WRITE_ENABLED); + + scg0.SIRCCSR.modify_one("SIRC_CLK_PERIPH_EN", .ENABLED); + + while (scg0.SIRCCSR.read().SIRCVLD == .DISABLED_OR_NOT_VALID) {} + + if (scg0.SIRCCSR.read().SIRCERR == .ERROR_DETECTED) @panic("CLOCK INIT FAIL"); + + scg0.RCCR.modify_one("SCS", .SIRC); + while (scg0.CSR.read().SCS != .SIRC) {} + scg0.SIRCCSR.modify_one("LK", .WRITE_DISABLED); +} + +//TODO ALLOW FOR LP_CONFG and add bandgap/DS checks +fn power_and_flash(cpu_clk: usize, power_cfg: Power_Config) void { + //enable bandgap + + //no config for DS and bandgap yet, so force normal mode + spc0.ACTIVE_CFG.modify_one("BGMODE", .BGMODE01); + spc0.ACTIVE_CFG.modify_one("CORELDO_VDD_DS", .NORMAL); + + spc0.ACTIVE_CFG.modify_one("CORELDO_VDD_LVL", @fromBackingInt(@intCast(@backingInt(power_cfg.driver)))); + while (spc0.SC.read().BUSY == .BUSY_YES) {} + FMU.FCTRL.modify_one("RWSC", get_flash_wait_states(power_cfg.driver, cpu_clk)); + + spc0.SRAMCTL.modify_one("VSM", @fromBackingInt(@intCast(@backingInt(power_cfg.driver)))); + spc0.SRAMCTL.modify_one("REQ", .REQ_YES); + while (spc0.SRAMCTL.read().ACK == .ACK_NO) {} + spc0.SRAMCTL.modify_one("REQ", .REQ_NO); +} + +fn enable_clocks(config: Clock_Config) void { + //SOCS and ROSC not supported yet, SIRC already enabled + + scg0.FIRCCSR.modify_one("LK", .WRITE_ENABLED); + scg0.FIRCCSR.modify_one("FIRCTREN", .DISABLED); + + if (config.frohf) |hf| { + scg0.FIRCCSR.write(.{ + .FIRC_FCLK_PERIPH_EN = @fromBackingInt(@intCast(@intFromBool(hf.en_fast_periph_gate))), + .FIRC_SCLK_PERIPH_EN = @fromBackingInt(@intCast(@intFromBool(hf.en_slow_periph_gate))), + .FIRCACC_IE = @fromBackingInt(@intCast(@intFromBool(hf.firc_ready_int_en))), + .FIRCERR_IE = @fromBackingInt(@intCast(@intFromBool(hf.error_int_en))), + .FIRCSTEN = @fromBackingInt(@intCast(@intFromBool(hf.stop_en))), + .LK = .WRITE_ENABLED, //leep lk open + .FIRCACC = .NOT_ENABLED_OR_NOT_VALID, //not modify + }); + scg0.FIRCCSR.modify_one("FIRCTREN", .ENABLED); + scg0.FIRCCFG.modify_one("FREQ_SEL", @fromBackingInt(@intCast(@backingInt(hf.freq)))); + + while (scg0.FIRCCSR.read().FIRCVLD == .NOT_ENABLED_OR_NOT_VALID) {} + if (scg0.FIRCCSR.read().FIRCERR == .ERROR_DETECTED) @panic("CLOCK INIT FAIL"); + + //apply fro_hf_div + MRCC0.MRCC_FRO_HF_DIV_CLKDIV.modify_one("DIV", hf.fro_hf_div); + while (MRCC0.MRCC_FRO_HF_DIV_CLKDIV.read().UNSTAB == .OFF) {} + } + scg0.FIRCCSR.modify_one("LK", .WRITE_DISABLED); +} + +fn get_flash_wait_states(mode: Drive_Mode, cpu_clk: usize) u4 { + switch (mode) { + .MidDriver => return if (cpu_clk >= 24_000_000) 1 else 0, + .StandartDriver => { + if (cpu_clk <= 30_000_000) { + return 0; + } else if (cpu_clk <= 60_000_000) { + return 1; + } else if (cpu_clk <= 90_000_000) { + return 2; + } else return 3; + }, + } +} From a98e7229ac425d15c55949777d996f8869f370ea Mon Sep 17 00:00:00 2001 From: RecursiveError Date: Mon, 14 Sep 2026 22:57:33 -0300 Subject: [PATCH 2/2] Add support for MCXA156 chip and example blinky application - Updated build configuration to include MCXA156 board. - Created new blinky example for MCXA156 with RGB LED control. - Enhanced memory region definitions for MCXA156. - Implemented HAL for OSTIMER and added time management functions. - Exposed RGB LED pins as GPIOs in the board configuration. --- examples/nxp/mcx/build.zig | 5 +- examples/nxp/mcx/src/mcxa156/blinky.zig | 23 ++++++++ .../nxp/mcx/src/{ => mcxa156}/mcxa156.zig | 1 + port/nxp/mcx/build.zig | 6 +- port/nxp/mcx/src/boards/frdm_mcxa156.zig | 45 +++++++++++++++ port/nxp/mcx/src/mcxa_15x/hal.zig | 11 ++++ port/nxp/mcx/src/mcxa_15x/hal/ostimer.zig | 56 +++++++++++++++++++ port/nxp/mcx/src/mcxa_15x/hal/syscon.zig | 8 +-- 8 files changed, 146 insertions(+), 9 deletions(-) create mode 100644 examples/nxp/mcx/src/mcxa156/blinky.zig rename examples/nxp/mcx/src/{ => mcxa156}/mcxa156.zig (96%) create mode 100644 port/nxp/mcx/src/mcxa_15x/hal/ostimer.zig diff --git a/examples/nxp/mcx/build.zig b/examples/nxp/mcx/build.zig index e4534f01f..d1aaafe26 100644 --- a/examples/nxp/mcx/build.zig +++ b/examples/nxp/mcx/build.zig @@ -13,11 +13,12 @@ pub fn build(b: *std.Build) void { const mb = MicroBuild.init(b, mz_dep) orelse return; const frdm_mcxa153 = mb.ports.mcx.chips.mcxa153; - const frdm_mcxa156 = mb.ports.mcx.chips.mcxa156; const frdm_mcxn947 = mb.ports.mcx.chips.mcxn947; + const frdm_mcxa156 = mb.ports.mcx.boards.frdm_mcxa156; + const available_examples = [_]Example{ - .{ .name = "mcxa156", .target = frdm_mcxa156, .file = "src/mcxa156.zig" }, + .{ .name = "mcxa156_blinky", .target = frdm_mcxa156, .file = "src/mcxa156/blinky.zig" }, .{ .name = "mcxa153_blinky", .target = frdm_mcxa153, .file = "src/mcxa153_blinky.zig" }, .{ .name = "mcxn947_blinky", .target = frdm_mcxn947, .file = "src/mcxn947_blinky.zig" }, .{ .name = "gpio_input", .target = frdm_mcxa153, .file = "src/gpio_input.zig" }, diff --git a/examples/nxp/mcx/src/mcxa156/blinky.zig b/examples/nxp/mcx/src/mcxa156/blinky.zig new file mode 100644 index 000000000..af14db45d --- /dev/null +++ b/examples/nxp/mcx/src/mcxa156/blinky.zig @@ -0,0 +1,23 @@ +const std = @import("std"); +const microzig = @import("microzig"); +const hal = microzig.hal; +const board = microzig.board; + +const time = hal.time; + +pub const std_options = microzig.std_options(.{}); + +comptime { + _ = microzig.export_startup(); +} + +pub fn main() !void { + board.Digital_RGB_Led.init(); + + while (true) { + for (0..7) |rgb| { + board.Digital_RGB_Led.set_color(@fromBackingInt(@intCast(rgb))); + time.sleep_ms(1000); + } + } +} diff --git a/examples/nxp/mcx/src/mcxa156.zig b/examples/nxp/mcx/src/mcxa156/mcxa156.zig similarity index 96% rename from examples/nxp/mcx/src/mcxa156.zig rename to examples/nxp/mcx/src/mcxa156/mcxa156.zig index 0e8e2b361..8a153bbe4 100644 --- a/examples/nxp/mcx/src/mcxa156.zig +++ b/examples/nxp/mcx/src/mcxa156/mcxa156.zig @@ -20,4 +20,5 @@ pub fn main() !void { .frohf = .{ .freq = .@"96Mhz", .fro_hf_div = 1 }, .ahb_div = 4, }); + while (true) {} } diff --git a/port/nxp/mcx/build.zig b/port/nxp/mcx/build.zig index 36987d34c..cbbc10825 100644 --- a/port/nxp/mcx/build.zig +++ b/port/nxp/mcx/build.zig @@ -82,15 +82,15 @@ pub fn init(dep: *std.Build.Dependency) ?Self { .cpu_arch = .thumb, .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m33 }, .os_tag = .freestanding, - .abi = .eabihf, + .abi = .eabi, }, .chip = .{ .name = "MCXA156", .register_definition = .{ .svd = mcux_soc_svd.path("MCXA156/MCXA156.xml") }, .memory_regions = &.{ .{ .tag = .flash, .offset = 0x00000000, .length = 1024 * 1024, .access = .rx }, - .{ .tag = .ram, .offset = 0x20000000, .length = 64 * 1024, .access = .rw }, - //.{ .tag = .ram, .offset = 0x2001E000, .length = 8 * 1024, .access = .rw, .name = "ECC" }, + .{ .tag = .ram, .offset = 0x20000000, .length = 120 * 1024, .access = .rw }, + .{ .tag = .ram, .offset = 0x2001E000, .length = 8 * 1024, .access = .rw, .name = "ECC" }, }, }, .hal = .{ .root_source_file = b.path("src/mcxa_15x/hal.zig") }, diff --git a/port/nxp/mcx/src/boards/frdm_mcxa156.zig b/port/nxp/mcx/src/boards/frdm_mcxa156.zig index e69de29bb..024492647 100644 --- a/port/nxp/mcx/src/boards/frdm_mcxa156.zig +++ b/port/nxp/mcx/src/boards/frdm_mcxa156.zig @@ -0,0 +1,45 @@ +const microzig = @import("microzig"); +const hal = microzig.hal; +//TODO: expose RGB Led pins as PWM + +const Color = enum(u3) { + // RGB LED is active-low + off = 0b111, + blue = 0b110, + green = 0b101, + cyan = 0b100, + red = 0b011, + magenta = 0b010, + yellow = 0b001, + white = 0b000, +}; + +const RGB_Pin = packed struct(u3) { + B: u1, + G: u1, + R: u1, +}; + +//expose the RGB led pins as GPIOs +pub const Digital_RGB_Led = struct { + const port3 = hal.port.num(3); + const R = port3.get_gpio(12); + const G = port3.get_gpio(13); + const B = port3.get_gpio(0); + + pub fn init() void { + port3.init(); + R.init(); + R.set_direction(.out); + G.set_direction(.out); + B.set_direction(.out); + } + + pub fn set_color(c: Color) void { + const P: RGB_Pin = @bitCast(c); + + R.put(P.R); + G.put(P.G); + B.put(P.B); + } +}; diff --git a/port/nxp/mcx/src/mcxa_15x/hal.zig b/port/nxp/mcx/src/mcxa_15x/hal.zig index a4063cbd8..45eea642f 100644 --- a/port/nxp/mcx/src/mcxa_15x/hal.zig +++ b/port/nxp/mcx/src/mcxa_15x/hal.zig @@ -2,3 +2,14 @@ pub const syscon = @import("hal/syscon.zig"); pub const clkout = @import("hal/clkout.zig"); pub const port = @import("hal/port.zig"); pub const gpio = @import("hal/gpio.zig"); +pub const ostime = @import("hal/ostimer.zig"); + +pub const time = struct { + pub const get_time_since_boot = ostime.get_time_since_boot; + pub const sleep_ms = ostime.sleep_ms; + pub const sleep_us = ostime.sleep_us; +}; + +pub fn init() void { + ostime.init(); +} diff --git a/port/nxp/mcx/src/mcxa_15x/hal/ostimer.zig b/port/nxp/mcx/src/mcxa_15x/hal/ostimer.zig new file mode 100644 index 000000000..09a4f7387 --- /dev/null +++ b/port/nxp/mcx/src/mcxa_15x/hal/ostimer.zig @@ -0,0 +1,56 @@ +const microzig = @import("microzig"); +const syscon = @import("syscon.zig"); + +const time = microzig.drivers.time; + +const OSTIMER0 = microzig.chip.peripherals.OSTIMER0; +const MRCC0 = microzig.chip.peripherals.MRCC0; + +//TODO: allow Hal config to enable Time counting on Debug and set/clear ACC +pub fn init() void { + + //disable and reset timer + syscon.disable_clock(.OSTIMER0); + syscon.reset_assert(.OSTIMER0); + syscon.reset_release(.OSTIMER0); + + //set clk_1m as input_clk + //TODO: check if FRO_12m is set in sleep mode + + syscon.unlock_clock_configuration(); + MRCC0.MRCC_OSTIMER0_CLKSEL.modify_one("MUX", @fromBackingInt(@intCast(2))); + syscon.freeze_clock_configuration(); + + syscon.enable_clock(.OSTIMER0); +} + +pub fn get_time_since_boot() time.Absolute { + const sec = microzig.interrupt.enter_critical_section(); + defer sec.leave(); + + //sev inst saves a snapshot of OSTIMER.EVCounter on OSTIMER.CAPTURE + microzig.cpu.sev(); + + //read current timer Gray value + var gray: u42 = OSTIMER0.CAPTURE_L.read().CAPTURE_VALUE; + gray |= @as(u42, OSTIMER0.CAPTURE_H.read().CAPTURE_VALUE) << 32; + + //simple gray to bin convert + var b = gray; + b ^= b >> 1; + b ^= b >> 2; + b ^= b >> 4; + b ^= b >> 8; + b ^= b >> 16; + b ^= b >> 32; + return @fromBackingInt(@intCast(b)); +} + +pub fn sleep_ms(time_ms: u32) void { + sleep_us(time_ms * 1000); +} + +pub fn sleep_us(time_us: u64) void { + const end_time = time.make_timeout_us(get_time_since_boot(), time_us); + while (!end_time.is_reached_by(get_time_since_boot())) {} +} diff --git a/port/nxp/mcx/src/mcxa_15x/hal/syscon.zig b/port/nxp/mcx/src/mcxa_15x/hal/syscon.zig index b1e4c89f8..3159c9774 100644 --- a/port/nxp/mcx/src/mcxa_15x/hal/syscon.zig +++ b/port/nxp/mcx/src/mcxa_15x/hal/syscon.zig @@ -182,8 +182,8 @@ pub const Peripheral = enum { fn mask(comptime peripheral: Peripheral) u32 { return switch (peripheral) { - .INPUTMUX0 => 0, - .I3C0 => 1, + .INPUTMUX0 => 1, + .I3C0 => 1 << 1, .CTIMER0 => 1 << 2, .CTIMER1 => 1 << 3, .CTIMER2 => 1 << 4, @@ -212,8 +212,8 @@ pub const Peripheral = enum { .QDC1 => 1 << 29, .FLEXPWM0 => 1 << 30, .FLEXPWM1 => 1 << 31, - .OSTIMER0 => 0, - .ADC0 => 1, + .OSTIMER0 => 1, + .ADC0 => 1 << 1, .ADC1 => 1 << 2, .CMP1 => 1 << 4, .DAC0 => 1 << 5,