From 7a2397b3be614d5c4add19c32fe6a736d7232019 Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Mon, 27 Apr 2026 17:19:33 +0200 Subject: [PATCH 1/2] [stm32] Add all peripherals in enums.zig This makes it so that all peripherals always get added, so this should be good for almost any chip. Only thing that needs to be done is potentially add more indexed peripherals. This commit also fixes the peripheral index to work on chips where the peripherals are not coincidentally in order. --- port/stmicro/stm32/src/hals/STM32F103.zig | 1 + port/stmicro/stm32/src/hals/STM32F303.zig | 2 +- port/stmicro/stm32/src/hals/STM32F429.zig | 8 +- port/stmicro/stm32/src/hals/common/enums.zig | 93 ++- port/stmicro/stm32/src/hals/common/rcc.zig | 603 +++++++++++++++++++ port/stmicro/stm32/src/hals/common/util.zig | 62 +- 6 files changed, 709 insertions(+), 60 deletions(-) create mode 100644 port/stmicro/stm32/src/hals/common/rcc.zig diff --git a/port/stmicro/stm32/src/hals/STM32F103.zig b/port/stmicro/stm32/src/hals/STM32F103.zig index cbde20545..6c81fb625 100644 --- a/port/stmicro/stm32/src/hals/STM32F103.zig +++ b/port/stmicro/stm32/src/hals/STM32F103.zig @@ -15,6 +15,7 @@ pub const backup = @import("./STM32F103/backup.zig"); pub const rtc = @import("./STM32F103/rtc.zig"); pub const dma = @import("./STM32F103/dma.zig"); pub const time = @import("./STM32F103/time.zig"); +pub const enums = @import("./common/enums.zig"); const util = @import("./common/util.zig"); //temporary solution diff --git a/port/stmicro/stm32/src/hals/STM32F303.zig b/port/stmicro/stm32/src/hals/STM32F303.zig index 669f746fa..67a11adb8 100644 --- a/port/stmicro/stm32/src/hals/STM32F303.zig +++ b/port/stmicro/stm32/src/hals/STM32F303.zig @@ -4,7 +4,7 @@ pub const i2c = @import("STM32F303/i2c.zig"); pub const spi = @import("STM32F303/spi.zig"); pub const pins = @import("STM32F303/pins.zig"); pub const dma = @import("./STM32F303/dma.zig"); -pub const enums = @import("./common//enums.zig"); +pub const enums = @import("./common/enums.zig"); pub const systick_timer = @import("./common/systick_timer.zig"); pub const systick = @import("./common/systick.zig"); diff --git a/port/stmicro/stm32/src/hals/STM32F429.zig b/port/stmicro/stm32/src/hals/STM32F429.zig index 698d0a256..447a28b22 100644 --- a/port/stmicro/stm32/src/hals/STM32F429.zig +++ b/port/stmicro/stm32/src/hals/STM32F429.zig @@ -21,10 +21,12 @@ //! TODO: add more clock calculations when adding Uart const microzig = @import("microzig"); +const mmio = microzig.mmio; const peripherals = microzig.chip.peripherals; const RCC = peripherals.RCC; -const Digital_IO = microzig.drivers.base.Digital_IO; +pub const pins = @import("./common/pins_v2.zig"); +pub const enums = @import("./common/enums.zig"); pub const pins = @import("./common/pins_v2.zig"); @@ -50,9 +52,7 @@ pub const rcc = struct { const util = @import("common/util.zig"); // Any peripheral that must be enable in RCC. - pub const Peripherals = util.create_peripheral_enum(&.{ - "GPIO", - }); + pub const Peripherals = enum.Peripherals; ///configure the power and clock registers before enabling the RTC ///this function also can be called from `rtc.enable()` diff --git a/port/stmicro/stm32/src/hals/common/enums.zig b/port/stmicro/stm32/src/hals/common/enums.zig index 9668f84de..7ce5f0f58 100644 --- a/port/stmicro/stm32/src/hals/common/enums.zig +++ b/port/stmicro/stm32/src/hals/common/enums.zig @@ -2,48 +2,32 @@ const util = @import("util.zig"); const microzig = @import("microzig"); // Any peripheral that must be enable in RCC. -pub const Peripherals = util.create_peripheral_enum(&.{ - "DMA", - "USBRAM", - "FLASH", - "CRC", - "SDIO", - "AFIO", - "GPIO", - "ADC", - "TIM", - "SPI", - "USART", - "WWDG", - "UART", - "I2C", - "CAN", - "BKP", - "PWR", - "DAC", - "RTC", - "USB", - "LPUART", -}); - -pub const UART_Type = util.sub_peripheral_enum(Peripherals, &.{ "USART", "UART", "LPUART" }, null); -pub const I2C_Type = util.sub_peripheral_enum(Peripherals, &.{"I2C"}, null); -pub const SPI_Type = util.sub_peripheral_enum(Peripherals, &.{"SPI"}, null); -pub const DMA_Type = util.sub_peripheral_enum(Peripherals, &.{"DMA"}, null); -pub const TIMGP16_Type = util.sub_peripheral_enum(Peripherals, &.{"TIM"}, "TIM_GP16"); -pub const ADC_Type = util.sub_peripheral_enum(Peripherals, &.{"ADC"}, null); +pub const Peripherals = util.create_peripheral_enum(); + +pub const UART_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{ "USART", "UART", "LPUART" }, null); +pub const I2C_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"I2C"}, null); +pub const SPI_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"SPI"}, null); +pub const DMA_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"DMA"}, null); +pub const TIMGP16_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"TIM"}, "TIM_GP16"); +pub const ADC_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"ADC"}, null); +pub const CAN_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"CAN"}, null); +pub const SAI_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"SAI"}, null); +pub const I2S_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"I2S"}, null); + +fn is_peripheral(comptime val: anytype) bool { + inline for ([_]type{ UART_Type, I2C_Type, SPI_Type, DMA_Type, TIMGP16_Type, ADC_Type, CAN_Type, SAI_Type, I2S_Type }) |T| { + if (@TypeOf(val) == T) { + return true; + } + } + return false; +} pub fn to_peripheral(comptime val: anytype) Peripherals { - return switch (@TypeOf(val)) { - UART_Type, - I2C_Type, - SPI_Type, - DMA_Type, - TIMGP16_Type, - ADC_Type, - => @as(Peripherals, @fromBackingInt(@backingInt(val))), - else => @panic("Value must be one of the sur peripheral enum define below"), - }; + if (is_peripheral(val)) { + return @as(Peripherals, @fromBackingInt(@backingInt(val))); + } + @panic("Value must be one of the sur peripheral enum define below"); } pub fn get_regs(comptime T: type, comptime val: anytype) *volatile T { @@ -51,12 +35,27 @@ pub fn get_regs(comptime T: type, comptime val: anytype) *volatile T { return @field(microzig.chip.peripherals, @tagName(periph_enum)); } +fn is_sub_peripheral(comptime val: anytype) bool { + inline for ([_]type{ UART_Type, I2C_Type, SPI_Type, DMA_Type, ADC_Type, CAN_Type, SAI_Type, I2S_Type }) |T| { + if (@TypeOf(val) == T) { + return true; + } + } + return false; +} + +fn get_field_index(comptime T: type, comptime val: T) u32 { + inline for (0.., @typeInfo(T).@"enum".field_values) |i, value| { + if (value == @backingInt(val)) { + return i; + } + } +} + +// This function is basically useless, it doesn't even give the number of the paripheral pub fn base_perihperal_index(comptime val: anytype) u32 { - return switch (@TypeOf(val)) { - UART_Type => @backingInt(val) - @backingInt(Peripherals.USART1), - I2C_Type => @backingInt(val) - @backingInt(Peripherals.I2C1), - SPI_Type => @backingInt(val) - @backingInt(Peripherals.SPI1), - DMA_Type => @backingInt(val) - @backingInt(Peripherals.DMA1), - else => @panic("Index peripheral is only for multiple index peripherals"), - }; + if (is_sub_peripheral(val)) { + return get_field_index(@TypeOf(val), val); + } + @panic("Index peripheral is only for multiple index peripherals"); // TODO: Whats against implementing this for all? } diff --git a/port/stmicro/stm32/src/hals/common/rcc.zig b/port/stmicro/stm32/src/hals/common/rcc.zig new file mode 100644 index 000000000..86367268d --- /dev/null +++ b/port/stmicro/stm32/src/hals/common/rcc.zig @@ -0,0 +1,603 @@ +const std = @import("std"); +const microzig = @import("microzig"); +const enums = @import("enums.zig"); +const util = @import("util.zig"); +const ClockTree = @import("ClockTree"); +const Tree = @field(ClockTree, microzig.config.chip_name); +const power = microzig.hal.power; + +pub const Config = Tree.Config; +const peripherals = microzig.chip.peripherals; +const peripheralTypes = microzig.chip.types.peripherals; +const RCC = peripherals.RCC; +const FLASH = peripherals.FLASH; +const flashType = if (@TypeOf(FLASH) == *volatile peripheralTypes.flash_f1.FLASH) + peripheralTypes.flash_f1 +else if (@TypeOf(FLASH) == *volatile peripheralTypes.flash_f4.FLASH) + peripheralTypes.flash_f4 +else + @compileError("Unknown FLASH"); + +const ClockInitError = error{ + HSETimeout, + LSETimeout, +}; + +pub const Peripherals = enums.Peripherals; + +pub const ResetReason = enum { + low_power, + window_watchdog, + independent_watchdog, + POR_or_PDR, + NRST, +}; + +pub const Bus = chipdetails.Bus; + +//default clock config +var current_clocks: Tree.ClockOutput = blk: { + const out = Tree.get_clocks(.{}) catch unreachable; + break :blk out.clock; +}; + +//NOTE: procedural style or loop through all elements of the struct? +///Configures the system clocks +///NOTE: to configure the backup domain clocks (RTC) it is necessary to enable it through the power +///register before configuring the clocks +pub fn apply(comptime config: Tree.Config) ClockInitError!Tree.ClockOutput { + const out_data = comptime Tree.get_clocks(config) catch unreachable; + try apply_internal(out_data.config); + current_clocks = out_data.clock; + return out_data.clock; +} + +fn apply_internal(config: Tree.OutputConfig) ClockInitError!void { + const latency: flashType.LATENCY = @fromBackingInt(@backingInt(config.FLatency)); + const prefetch = config.flags.PREFETCH_ENABLE; + secure_enable(); + set_flash(latency, prefetch); + + if (config.flags.EnableHSE) { + const timout = @as(usize, config.HSE_Timout); + try enable_hse(config.flags.HSEByPass, config.flags.EnbaleCSS, timout); + } else { + disable_hse(); + } + + set_peripherals_prescaler(get_prescaler_config(config)); + + if (config.flags.PLLUsed) { + config_pll(get_pll_config(config)); + enable_pll(); + } else { + disable_pll(); + } + + //BACKUP DOMAIN CLOCK CONFIG + + if (config.flags.EnableLSE) { + const timeout = @as(usize, config.LSE_Timout); + const bypass = config.flags.LSEByPass; + try enable_lse(timeout, bypass); + } else { + disable_lse(); + } + + set_lsi(config.flags.LSIUsed); + + config_rtc(get_rtc_config(config)); + + config_mco(get_mco_config(config)); + + //SYSTEM CLOCK CONFIG + config_system_clock(@fromBackingInt(@backingInt(config.SYSCLKSource))); + + //in case of HSI not used, we have to disable it here + //becuse the system clock configuration 'secure_enable' enables it by default + calib_hsi(@intCast(config.HSICalibrationValue)); + set_hsi(config.flags.HSIUsed); +} + +//force HSI Clock and clear any clock configs +pub fn secure_enable() void { + set_hsi(true); + + RCC.BDCR.raw = 0; + RCC.CFGR.raw = 0; + while (RCC.CFGR.read().SWS != .HSI) { + asm volatile (""); + } + + RCC.CR.modify(.{ + .PLLON = 0, + .HSEBYP = 0, + .HSEON = 0, + .CSSON = 0, + }); +} + +// WHY is this part of the RCC? Esp since for example ADC is not +pub inline fn set_flash(latency: flashType.LATENCY, prefetch: bool) void { + FLASH.ACR.modify_one("LATENCY", latency); + + const prfetch_en_reg = if (@TypeOf(FLASH) == *volatile peripheralTypes.flash_f1.FLASH) + "PRFTBE" + else if (@TypeOf(FLASH) == *volatile peripheralTypes.flash_f4.FLASH) + "PRFTEN" + else + @compileError("Unknown FLASH"); + FLASH.ACR.modify_one(prfetch_en_reg, @intFromBool(prefetch)); +} + +pub fn enable_hse(bypass: bool, css: bool, timeout: ?usize) ClockInitError!void { + const max_wait: u32 = blk: { + if (timeout) |val| { + if (val != 0) { + break :blk val; + } + } + break :blk std.math.maxInt(usize); + }; + var ticks: usize = calc_wait_ticks(max_wait - 1); + + RCC.CR.modify(.{ + .HSEON = 1, + .HSEBYP = @intFromBool(bypass), + .CSSON = @intFromBool(css), + }); + while (RCC.CR.read().HSERDY == 0) { + if (ticks == 0) return error.HSETimeout; + ticks -= 1; + asm volatile (""); + } +} + +pub inline fn disable_hse() void { + RCC.CR.modify(.{ .CSSON = 0 }); + RCC.CR.modify(.{ .HSEON = 0 }); +} + +pub const set_peripherals_prescaler = util.generateRCCPeripheralApply(chipdetails.prescalerFields); +const get_prescaler_config = util.generateRCCPeripheralConvert(chipdetails.prescalerFields); + +pub const config_pll = util.generateRCCPeripheralApply(chipdetails.pllFields); +const get_pll_config = util.generateRCCPeripheralConvert(chipdetails.pllFields); + +pub fn enable_pll() void { + RCC.CR.modify(.{ .PLLON = 1 }); + while (RCC.CR.read().PLLRDY == 0) { + asm volatile (""); + } +} + +pub fn disable_pll() void { + RCC.CR.modify(.{ .PLLON = 0 }); + while (RCC.CR.read().PLLRDY != 0) { + asm volatile (""); + } +} + +fn enable_lse(timeout: ?usize, bypass: bool) ClockInitError!void { + const max_wait: u32 = blk: { + if (timeout) |val| { + if (val != 0) { + break :blk val; + } + } + break :blk std.math.maxInt(u32); + }; + var ticks: usize = calc_wait_ticks(max_wait - 1); + + RCC.BDCR.modify_one("LSEBYP", @intFromBool(bypass)); + RCC.BDCR.modify(.{ .LSEON = 1 }); + while (RCC.BDCR.read().LSERDY == 0) { + if (ticks == 0) return error.LSETimeout; + ticks -= 1; + asm volatile (""); + } +} + +pub inline fn disable_lse() void { + RCC.BDCR.modify(.{ .LSEON = 0 }); +} + +fn set_lsi(on: bool) void { + RCC.CSR.modify(.{ .LSION = @intFromBool(on) }); + if (on) { + while (RCC.CSR.read().LSIRDY == 0) { + asm volatile (""); + } + } +} + +///after the reset, the BDRD becomes read_only until access is released by the power register +///this function can also be called from `backup.reset()` +pub fn reset_backup_domain() void { + RCC.BDCR.modify(.{ .BDRST = 1 }); + for (0..5) |i| { + std.mem.doNotOptimizeAway(i); + } + RCC.BDCR.modify(.{ .BDRST = 0 }); +} + +const get_rtc_config = util.generateRCCPeripheralConvert(chipdetails.rtcields); + +pub fn config_rtc(config: util.generateRCCPeripheralConfig(chipdetails.rtcields)) void { + // RCC_BDCR is read-only + const enableBKPRegs: []const Peripherals = if (@TypeOf(RCC) == *volatile peripheralTypes.rcc_f1.RCC) + &[_]Peripherals{ .PWR, .BKP } + else + &[0]Peripherals{}; + + //enable backup domain write acess + inline for (enableBKPRegs) |reg| { + enable_clock(reg); + } + power.backup_domain_protection(false); + if (config.RTCSEL == .HSE) { + reset_backup_domain(); //HSE as RTC source requires full reset of the bkp domain + power.backup_domain_protection(false); + } + + util.generateRCCPeripheralApply(chipdetails.rtcields)(config); + power.backup_domain_protection(true); + + // Disable and reset clocks to avoid potential conflicts with the main application + inline for (enableBKPRegs) |reg| { + disable_clock(reg); + reset_clock(reg); + } +} + +pub const config_mco = util.generateRCCPeripheralApply(chipdetails.mcoFields); +const get_mco_config = util.generateRCCPeripheralConvert(chipdetails.mcoFields); + +fn config_system_clock(system_clock: chipdetails.rccType.SW) void { + RCC.CFGR.modify(.{ .SW = system_clock }); + while (true) { + const sws = RCC.CFGR.read().SWS; + if (sws == system_clock) break; + asm volatile (""); + } +} + +///configure the HSI calibration value +pub fn calib_hsi(calib: usize) void { + //secure_enable has already started the HSE + const trim: u5 = @truncate(calib); + RCC.CR.modify(.{ .HSITRIM = trim }); + + //wait for the HSI to stabilize + for (0..16) |_| { + asm volatile (""); + } +} + +pub fn set_hsi(on: bool) void { + RCC.CR.modify(.{ .HSION = @intFromBool(on) }); + if (on) { + while (RCC.CR.read().HSIRDY == 0) { + asm volatile (""); + } + } +} + +///configure the power and clock registers before enabling the RTC +///this function also can be called from `rtc.enable()` +pub fn enable_rtc(on: bool) void { + RCC.BDCR.modify(.{ .RTCEN = @intFromBool(on) }); +} + +///backup domain is not reset with the rest of the system +///so this function can be used to check if the RTC is already running. +pub fn rtc_running() bool { + return RCC.BDCR.read().RTCEN != 0; +} + +pub fn set_clock(comptime peri: Peripherals, state: u1) void { + const peri_name = @tagName(peri); + const field = peri_name ++ "EN"; + if (util.match_name(peri_name, &.{"RTC"})) { + enable_rtc(state != 0); + return; + } + + const rcc_register_name = comptime chipdetails.peripheral_to_en_reg(peri_name); + + @field(RCC, rcc_register_name).modify_one(field, state); +} + +pub fn reset_clock(comptime peri: Peripherals) void { + const peri_name = @tagName(peri); + const field = peri_name ++ "RST"; + const rcc_register_name = comptime chipdetails.peripheral_to_rst_reg(peri_name); + + @field(RCC, rcc_register_name).modify_one(field, 1); + @field(RCC, rcc_register_name).raw = 0; +} + +pub fn enable_clock(comptime peri: Peripherals) void { + set_clock(peri, 1); +} + +pub fn disable_clock(comptime peri: Peripherals) void { + set_clock(peri, 0); +} + +pub fn enable_all_clocks() void { + //enable all clocks + inline for (chipdetails.enRegs) |reg| { + @field(RCC, reg).raw = std.math.maxInt(u32); + } +} + +pub fn disable_all_clocks() void { + //disable all clocks + inline for (chipdetails.enRegs) |reg| { + @field(RCC, reg).raw = 0; + } +} + +///Reset all periferals of the specified bus to they default state. +///NOTE: this function does not effect the ENR registers. +pub fn reset_bus(bus: Bus) void { + //first write 1 to all bits to reset them + //then write 0 to all bits to release the reset + //this is necessary because the reset bits are not self-clearing + inline for (@typeInfo(Bus).@"enum".field_names, @typeInfo(Bus).@"enum".field_values) |name, value| { + if (@backingInt(bus) == value) { + @field(RCC, name ++ "RSTR").raw = std.math.maxInt(u32); + @field(RCC, name ++ "RSTR").raw = 0; + } + } +} + +pub inline fn get_sys_clk() u32 { + return current_clocks.SysCLKOutput; +} + +inline fn calc_wait_ticks(val: usize) usize { + const corrent_clock: usize = @intCast(current_clocks.SysCLKOutput); + const ms_per_tick = corrent_clock / 1000; + return ms_per_tick * val; +} + +pub fn enable_dma(index: enums.DMA_Type) void { + inline for (@typeInfo(Bus).@"enum".field_names, @typeInfo(Bus).@"enum".field_values) |name, value| { + if (@backingInt(index) == value) { + @field(RCC, chipdetails.DMAENReg).modify_one(name ++ "EN", 1); + } + } +} + +// TODO: Implement get_clock + +///This function is called internally by the HAL, the RESET value should only be read after the RESET +///read the Reset value through the global variable hal.RESET +pub fn get_reset_reason() ResetReason { + const flags = RCC.CSR.read(); + const rst: ResetReason = blk: { + // TODO: Add BORRSTF for stm32f4 + if (flags.PINRSTF == 1) break :blk ResetReason.NRST; + if (flags.PORRSTF == 1) break :blk ResetReason.POR_or_PDR; + if (flags.SFTRSTF == 1) break :blk ResetReason.low_power; + if (flags.IWDGRSTF == 1) break :blk ResetReason.independent_watchdog; + if (flags.WWDGRSTF == 1) break :blk ResetReason.window_watchdog; + if (flags.LPWRRSTF == 1) break :blk ResetReason.low_power; + break :blk ResetReason.POR_or_PDR; + }; + + RCC.CSR.modify(.{ .RMVF = 1 }); + return rst; +} + +const chipdetails = if (@TypeOf(RCC) == *volatile peripheralTypes.rcc_f1.RCC) + details_rcc_f1 +else if (@TypeOf(RCC) == *volatile peripheralTypes.rcc_f4.RCC) + details_rcc_f4 +else + @compileError("Unknown RCC"); + +const details_rcc_f1 = struct { + const rccType = peripheralTypes.rcc_f1; + + const Bus = enum { + // TODO: Why not? To me it seems they can be reset, albeit only ATHMAC and OTGFS + // AHB, //AHB cannot be reset by software + APB1, + APB2, + }; + + const DMAENReg = "AHBENR"; + + //TODO: Add STM32F105/7 devices peri + const prescalerFields: []const util.rccPeripheralField = + &[_]util.rccPeripheralField{ + .{ .reg = "CFGR", .name = "USBPRE", .type = rccType.USBPRE, .optional = true, .rccName = "USBPrescaler" }, + .{ .reg = "CFGR", .name = "ADCPRE", .type = rccType.ADCPRE, .optional = true, .rccName = "ADCPresc" }, + .{ .reg = "CFGR", .name = "PPRE2", .type = rccType.PPRE, .optional = true, .rccName = "APB2CLKDivider" }, + .{ .reg = "CFGR", .name = "PPRE1", .type = rccType.PPRE, .optional = true, .rccName = "APB1CLKDivider" }, + .{ .reg = "CFGR", .name = "HPRE", .type = rccType.HPRE, .optional = true, .rccName = "AHBCLKDivider" }, + }; + const pllFields: []const util.rccPeripheralField = + &[_]util.rccPeripheralField{ + .{ .reg = "CFGR", .name = "PLLSRC", .type = rccType.PLLSRC, .rccName = "PLLSourceVirtual" }, + .{ .reg = "CFGR", .name = "PLLMUL", .type = rccType.PLLMUL, .rccName = "PLLMUL" }, + .{ .reg = "CFGR", .name = "PLLXTPRE", .type = rccType.PLLXTPRE, .rccName = "HSEDivPLL" }, + }; + const rtcields: []const util.rccPeripheralField = + &[_]util.rccPeripheralField{ + .{ .reg = "BDCR", .name = "RTCSEL", .type = rccType.RTCSEL, .rccName = "RTCClockSelection", .default = .{ .value = @ptrCast(&rccType.RTCSEL.DISABLE), .unless = "RTCEnable" } }, + }; + const mcoFields: []const util.rccPeripheralField = + &[_]util.rccPeripheralField{ + .{ .reg = "CFGR", .name = "MCOSEL", .type = rccType.MCOSEL, .rccName = "RCC_MCOSource", .default = .{ .value = @ptrCast(&rccType.MCOSEL.DISABLE), .unless = "MCOEnable" } }, + }; + + const enRegs: []const []const u8 = .{ + "AHBENR", + "APB1ENR", + "APB2ENR", + }; + + inline fn peripheral_to_en_reg(peri_name: [:0]const u8) []const u8 { + return if (util.match_name(peri_name, &.{ + "DMA", + "FLASH", + "CRC", + "FSMC", + "SDIO", + })) "AHBENR" else if (util.match_name(peri_name, &.{ + "AFIO", + "GPIO", + "ADC", + "TIM1", + "SPI1", + "USART1", + })) "APB2ENR" else "APB1ENR"; + } + + inline fn peripheral_to_rst_reg(peri_name: [:0]const u8) []const u8 { + return if (util.match_name(peri_name, &.{ + "AFIO", + "GPIO", + "ADC", + "TIM1", + "SPI1", + "USART1", + })) "APB2RSTR" else if (util.match_name(peri_name, &.{ + "TIM", + "WWDG", + "SPI", + "USART", + "UART", + "I2C", + "CAN", + "BKP", + "PWR", + "DAC", + })) "APB1RSTR" else @panic("No reset possible for this peripheral"); + } +}; + +const details_rcc_f4 = struct { + const rccType = peripheralTypes.rcc_f4; + const Bus = enum { + AHB1, + AHB2, + AHB3, + APB1, + APB2, + }; + + const DMAENReg = "AHB1ENR"; + + const prescalerFields: []const util.rccPeripheralField = + &[_]util.rccPeripheralField{ + .{ .reg = "CFGR", .name = "MCO2PRE", .type = rccType.MCOPRE, .rccName = "RCC_MCODiv2" }, + .{ .reg = "CFGR", .name = "MCO1PRE", .type = rccType.MCOPRE, .rccName = "RCC_MCODiv1" }, + .{ .reg = "CFGR", .name = "PPRE2", .type = rccType.PPRE, .rccName = "APB2CLKDivider" }, + .{ .reg = "CFGR", .name = "PPRE1", .type = rccType.PPRE, .rccName = "APB1CLKDivider" }, + .{ .reg = "CFGR", .name = "HPRE", .type = rccType.HPRE, .rccName = "AHBCLKDivider" }, + .{ .reg = "DCKCFGR", .name = "TIMPRE", .type = rccType.TIMPRE, .rccName = "RCC_TIM_PRescaler_Selection" }, + }; + const pllFields: []const util.rccPeripheralField = + &[_]util.rccPeripheralField{ + .{ .reg = "PLLCFGR", .name = "PLLSRC", .type = rccType.PLLSRC, .rccName = "PLLSourceVirtual" }, + .{ .reg = "PLLCFGR", .name = "PLLN", .type = rccType.PLLN, .rccName = "PLLN", .rccIsEnum = false }, + }; + const rtcields: []const util.rccPeripheralField = + &[_]util.rccPeripheralField{ + .{ .reg = "BDCR", .name = "RTCSEL", .type = rccType.RTCSEL, .rccName = "RCC_RTC_Clock_SourceVirtual", .default = .{ .value = @ptrCast(&rccType.RTCSEL.DISABLE), .unless = "RTCEnable" } }, + }; + const mcoFields: []const util.rccPeripheralField = + &[_]util.rccPeripheralField{ + .{ .reg = "CFGR", .name = "MCO2SEL", .type = rccType.MCO2SEL, .optional = true, .rccName = "RCC_MCO2Source", .shouldConfigFlag = "MCO2Config" }, + .{ .reg = "CFGR", .name = "MCO1SEL", .type = rccType.MCO1SEL, .optional = true, .rccName = "RCC_MCO1Source", .shouldConfigFlag = "MCO1Config" }, + }; + + const enRegs: []const []const u8 = .{ + "AHB1ENR", + "APB1ENR", + }; + + inline fn peripheral_to_en_reg(peri_name: [:0]const u8) []const u8 { + return if (util.match_name(peri_name, &.{ + "OTGHSULPI", + "OTGHS", + "ETHMACPTP", + "ETHMACRX", + "ETHMACTX", + "ETHMAC", + "DMA2D", + "DMA2", + "DMA1", + "CCMDATARAM", + "BKPSRAM", + "CRC", + "GPIOK", + "GPIOJ", + "GPIOI", + "GPIOH", + "GPIOG", + "GPIOF", + "GPIOE", + "GPIOD", + "GPIOC", + "GPIOB", + "GPIOA", + })) "AHB1ENR" else if (util.match_name(peri_name, &.{ + "DAC", + "PWD", + "CAN2", + "CAN1", + "I2C3", + "I2C2", + "I2C1", + "UART5", + "UART4", + "UART3", + "UART2", + "SPI3", + "SPI2", + "TIM14", + "TIM13", + "TIM12", + "TIM7", + "TIM6", + "TIM5", + "TIM4", + "TIM3", + "TIM2", + })) "APB1ENR" else @panic("Unknown peripheral"); + } + // TODO: Should this be the same as above?? + inline fn peripheral_to_rst_reg(peri_name: [:0]const u8) []const u8 { + return if (util.match_name(peri_name, &.{ + "OTGHSULPI", + "OTGHS", + "ETHMACPTP", + "ETHMACRX", + "ETHMACTX", + "ETHMAC", + "DMA2D", + "DMA2", + "DMA1", + "CCMDATARAM", + "BKPSRAM", + "CRC", + "GPIOK", + "GPIOJ", + "GPIOI", + "GPIOH", + "GPIOG", + "GPIOF", + "GPIOE", + "GPIOD", + "GPIOC", + "GPIOB", + "GPIOA", + })) "AHB1ENR" else @panic("No reset possible for this peripheral"); + } +}; diff --git a/port/stmicro/stm32/src/hals/common/util.zig b/port/stmicro/stm32/src/hals/common/util.zig index 8125cd2f9..8b9468e6b 100644 --- a/port/stmicro/stm32/src/hals/common/util.zig +++ b/port/stmicro/stm32/src/hals/common/util.zig @@ -15,32 +15,56 @@ pub fn match_name(heystack: []const u8, needles: []const []const u8) bool { return false; } -pub fn create_peripheral_enum(comptime bases_name: []const []const u8) type { +pub fn create_peripheral_enum() type { var field_names: []const []const u8 = &.{}; var field_values: []const usize = &.{}; @setEvalBranchQuota(10_000); for (@typeInfo(peripherals).@"struct".decl_names, 0..) |decl_name, i| { - if (match_name(decl_name, bases_name)) { - field_names = field_names ++ .{decl_name}; - field_values = field_values ++ .{i}; - } + field_names = field_names ++ .{decl_name}; + field_values = field_values ++ .{i}; } return @Enum(usize, .exhaustive, field_names, field_values[0..]); } +fn sorted_field_indices(comptime fields: []const [:0]const u8) [fields.len]usize { + var _fields: [fields.len]usize = undefined; + for (0..fields.len) |i| { + _fields[i] = i; + } + + const f: fn (void, usize, usize) bool = struct { + fn s(_: void, a: usize, b: usize) bool { + const aname = fields[a]; + const bname = fields[b]; + const an = std.mem.trimEnd(u8, aname, "0123456789"); + const bn = std.mem.trimEnd(u8, bname, "0123456789"); + const ai = std.fmt.parseInt(u8, aname[an.len..aname.len], 10) catch 0; + const bi = std.fmt.parseInt(u8, bname[bn.len..bname.len], 10) catch 0; + if (ai == bi) { + return std.mem.order(u8, aname, bname) == .lt; + } + return ai < bi; + } + }.s; + std.mem.sortUnstable(usize, &_fields, {}, f); + return _fields; +} + pub fn sub_peripheral_enum(comptime T: type, comptime keep_name: []const []const u8, match_type: ?[]const u8) type { var field_names: []const []const u8 = &.{}; var field_values: []const usize = &.{}; - @setEvalBranchQuota(10_000); + + @setEvalBranchQuota(30_000); const info = @typeInfo(T).@"enum"; - for (info.field_names, info.field_values) |field_name, field_value| { + for (sorted_field_indices(info.field_names)) |i| { + const field_name = info.field_names[i]; + const field_value = info.field_values[i]; if (match_name(field_name, keep_name)) { if (match_type) |match| { const type_name = @typeName(@TypeOf(@field(peripherals, field_name))); _ = std.mem.indexOf(u8, type_name, match) orelse continue; } - field_names = field_names ++ .{field_name}; field_values = field_values ++ .{field_value}; } @@ -49,6 +73,28 @@ pub fn sub_peripheral_enum(comptime T: type, comptime keep_name: []const []const return @Enum(usize, .exhaustive, field_names, field_values[0..]); } +fn num_nonempty_types(comptime size: usize, comptime T: [size]type) usize { + var i: usize = 0; + for (T) |t| { + if (t != @Enum(usize, .exhaustive, &[0][]const u8{}, &[0]usize{})) { + i += 1; + } + } + return i; +} + +pub fn filter_empty_types(comptime size: usize, comptime T: [size]type) [num_nonempty_types(size, T)]type { + var ret: [num_nonempty_types(size, T)]type = undefined; + var i: usize = 0; + for (T) |t| { + if (t != @Enum(usize, .exhaustive, &[0][]const u8{}, &[0]usize{})) { + ret[i] = t; + i += 1; + } + } + return ret; +} + pub fn load_timer_interrupt(handler: *const fn () callconv(.c) void) microzig.cpu.InterruptOptions { var int_op: microzig.cpu.InterruptOptions = .{}; if (@hasField(microzig.cpu.InterruptOptions, "TIM2")) { From 95033c683e5bb1792acca228bcfcddf7418319c5 Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Thu, 15 Oct 2026 13:26:25 +0200 Subject: [PATCH 2/2] Remove last unused import (in this PR) --- port/stmicro/stm32/src/hals/STM32F429.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/port/stmicro/stm32/src/hals/STM32F429.zig b/port/stmicro/stm32/src/hals/STM32F429.zig index 447a28b22..3e2677e92 100644 --- a/port/stmicro/stm32/src/hals/STM32F429.zig +++ b/port/stmicro/stm32/src/hals/STM32F429.zig @@ -21,7 +21,6 @@ //! TODO: add more clock calculations when adding Uart const microzig = @import("microzig"); -const mmio = microzig.mmio; const peripherals = microzig.chip.peripherals; const RCC = peripherals.RCC;