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..3e2677e92 100644 --- a/port/stmicro/stm32/src/hals/STM32F429.zig +++ b/port/stmicro/stm32/src/hals/STM32F429.zig @@ -24,7 +24,8 @@ const microzig = @import("microzig"); 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 +51,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")) {