From 8720973005c8eb8888a5ff73a2af27074dd576c0 Mon Sep 17 00:00:00 2001 From: Riccardo Binetti Date: Sun, 3 Jul 2022 01:59:14 +0200 Subject: [PATCH] Add support for multiple clock domains (#53) While this does not automate clock configuration yet, at least it allows using microzig with applications that configure the clock themselves and then declare the speeds of the various domains. Without this, all peripheral depending on different clock domanins (e.g. Uart or I2C on STM32 devices) could only work with the reset frequency. --- src/core/clock.zig | 12 +++++++---- .../boards/arduino-nano/arduino-nano.zig | 4 +++- .../boards/mbed-lpc1768/mbed-lpc1768.zig | 4 +++- .../stm32f4discovery/stm32f4discovery.zig | 2 -- src/modules/chips/atmega328p/atmega328p.zig | 10 +++++++-- src/modules/chips/lpc1768/lpc1768.zig | 14 ++++++++++--- src/modules/chips/stm32f303/stm32f303.zig | 20 +++++++++++++++++- src/modules/chips/stm32f407/stm32f407.zig | 21 ++++++++++++++----- src/modules/chips/stm32f429/stm32f429.zig | 17 +++++++++++++++ 9 files changed, 85 insertions(+), 19 deletions(-) diff --git a/src/core/clock.zig b/src/core/clock.zig index a7d8b64..71056bd 100644 --- a/src/core/clock.zig +++ b/src/core/clock.zig @@ -1,5 +1,6 @@ const std = @import("std"); const micro = @import("microzig.zig"); +const chip = @import("chip"); /// An enumeration of clock sources. pub const Source = enum { @@ -10,6 +11,9 @@ pub const Source = enum { cpu, }; +/// A struct containing the frequency in hertz for each clock domain +pub const Clocks = std.enums.EnumFieldStruct(chip.clock.Domain, u32, null); + /// Is `true` when microzig has a clock frequency available. /// Clock can be provided by several clock sources pub const has_clock = (clock_source_type != void); @@ -30,16 +34,16 @@ pub const source: Source = switch (clock_source_type) { /// Ensures that microzig has a clock available. This will @compileError when no clock is available, otherwise, it will be a no-op. pub fn ensure() void { if (!has_clock) - @compileError("microzig requires the clock frequency to perform this operation. Please export a const or var cpu_frequency from your root file that contains the cpu frequency in hertz!"); + @compileError("microzig requires the clock frequency to perform this operation. Please export a const or var clock_frequencies from your root file that contains the clock frequency for all chip clock domains in hertz!"); } -/// Returns the current cpu frequency in hertz. -pub inline fn get() u32 { +/// Returns the Clocks struct, with all clock domains frequencies in hertz. +pub inline fn get() Clocks { ensure(); return @field(clock_source_type, freq_decl_name); } -const freq_decl_name = "cpu_frequency"; +const freq_decl_name = "clock_frequencies"; const no_clock_source_type = opaque {}; const clock_source_type = if (@hasDecl(micro.app, freq_decl_name)) diff --git a/src/modules/boards/arduino-nano/arduino-nano.zig b/src/modules/boards/arduino-nano/arduino-nano.zig index 16ade81..96490f8 100644 --- a/src/modules/boards/arduino-nano/arduino-nano.zig +++ b/src/modules/boards/arduino-nano/arduino-nano.zig @@ -1,6 +1,8 @@ pub const chip = @import("chip"); -pub const cpu_frequency = 16_000_000; +pub const clock_frequencies = .{ + .cpu = 16_000_000, +}; pub const pin_map = .{ // Port A diff --git a/src/modules/boards/mbed-lpc1768/mbed-lpc1768.zig b/src/modules/boards/mbed-lpc1768/mbed-lpc1768.zig index 03a5fbb..6c5ff9d 100644 --- a/src/modules/boards/mbed-lpc1768/mbed-lpc1768.zig +++ b/src/modules/boards/mbed-lpc1768/mbed-lpc1768.zig @@ -1,7 +1,9 @@ pub const chip = @import("chip"); pub const micro = @import("microzig"); -pub const cpu_frequency: u32 = 100_000_000; // 100 MHz +pub const clock_frequencies = .{ + .cpu = 100_000_000, // 100 Mhz +}; pub fn debugWrite(string: []const u8) void { const clk_pin = micro.Pin("DIP5"); diff --git a/src/modules/boards/stm32f4discovery/stm32f4discovery.zig b/src/modules/boards/stm32f4discovery/stm32f4discovery.zig index 8fdf824..7703b1d 100644 --- a/src/modules/boards/stm32f4discovery/stm32f4discovery.zig +++ b/src/modules/boards/stm32f4discovery/stm32f4discovery.zig @@ -1,8 +1,6 @@ pub const chip = @import("chip"); pub const micro = @import("microzig"); -pub const cpu_frequency = 16_000_000; - pub const pin_map = .{ // LED cross, connected to GPIOD bits 12..15 // N orange diff --git a/src/modules/chips/atmega328p/atmega328p.zig b/src/modules/chips/atmega328p/atmega328p.zig index 43022a8..9375a6d 100644 --- a/src/modules/chips/atmega328p/atmega328p.zig +++ b/src/modules/chips/atmega328p/atmega328p.zig @@ -11,6 +11,12 @@ const Port = enum(u8) { D = 3, }; +pub const clock = struct { + pub const Domain = enum { + cpu, + }; +}; + pub fn parsePin(comptime spec: []const u8) type { const invalid_format_msg = "The given pin '" ++ spec ++ "' has an invalid format. Pins must follow the format \"P{Port}{Pin}\" scheme."; @@ -94,14 +100,14 @@ pub fn Uart(comptime index: usize) type { const Self = @This(); fn computeDivider(baud_rate: u32) !u12 { - const pclk = micro.clock.get(); + const pclk = micro.clock.get().cpu; const divider = ((pclk + (8 * baud_rate)) / (16 * baud_rate)) - 1; return std.math.cast(u12, divider) catch return error.UnsupportedBaudRate; } fn computeBaudRate(divider: u12) u32 { - return micro.clock.get() / (16 * @as(u32, divider) + 1); + return micro.clock.get().cpu / (16 * @as(u32, divider) + 1); } pub fn init(config: micro.uart.Config) !Self { diff --git a/src/modules/chips/lpc1768/lpc1768.zig b/src/modules/chips/lpc1768/lpc1768.zig index 2ca08d7..93a88a4 100644 --- a/src/modules/chips/lpc1768/lpc1768.zig +++ b/src/modules/chips/lpc1768/lpc1768.zig @@ -5,7 +5,15 @@ const regs = chip.registers; pub usingnamespace chip; -pub const cpu_frequency: u32 = 100_000_000; // 100 MHz +pub const clock = struct { + pub const Domain = enum { + cpu, + }; +}; + +pub const clock_frequencies = .{ + .cpu = 100_000_000, // 100 Mhz +}; pub const PinTarget = enum(u2) { func00 = 0b00, @@ -156,11 +164,11 @@ pub fn Uart(comptime index: usize) type { //UARTn.FCR.modify(.{ .FIFOEN = .UARTN_FIFOS_ARE_DISA }); micro.debug.writer().print("clock: {} baud: {} ", .{ - micro.clock.get(), + micro.clock.get().cpu, config.baud_rate, }) catch {}; - const pclk = micro.clock.get() / 4; + const pclk = micro.clock.get().cpu / 4; const divider = (pclk / (16 * config.baud_rate)); const regval = std.math.cast(u16, divider) orelse return error.UnsupportedBaudRate; diff --git a/src/modules/chips/stm32f303/stm32f303.zig b/src/modules/chips/stm32f303/stm32f303.zig index 4bbfb0f..4b8ff37 100644 --- a/src/modules/chips/stm32f303/stm32f303.zig +++ b/src/modules/chips/stm32f303/stm32f303.zig @@ -43,6 +43,24 @@ const regs = chip.registers; pub usingnamespace chip; pub const cpu = @import("cpu"); + +pub const clock = struct { + pub const Domain = enum { + cpu, + ahb, + apb1, + apb2, + }; +}; + +// Default clock frequencies after reset, see top comment for calculation +pub const clock_frequencies = .{ + .cpu = 8_000_000, + .ahb = 8_000_000, + .apb1 = 8_000_000, + .apb2 = 8_000_000, +}; + pub fn parsePin(comptime spec: []const u8) type { const invalid_format_msg = "The given pin '" ++ spec ++ "' has an invalid format. Pins must follow the format \"P{Port}{Pin}\" scheme."; @@ -170,7 +188,7 @@ pub fn Uart(comptime index: usize) type { // if the board doesn't configure e.g. an HSE external crystal. // TODO: Do some checks to see if the baud rate is too high (or perhaps too low) // TODO: Do a rounding div, instead of a truncating div? - const usartdiv = @intCast(u16, @divTrunc(micro.board.cpu_frequency, config.baud_rate)); + const usartdiv = @intCast(u16, @divTrunc(micro.clock.get().apb1, config.baud_rate)); regs.USART1.BRR.raw = usartdiv; // Above, ignore the BRR struct fields DIV_Mantissa and DIV_Fraction, // those seem to be for another chipset; .svd file bug? diff --git a/src/modules/chips/stm32f407/stm32f407.zig b/src/modules/chips/stm32f407/stm32f407.zig index 689e77d..17f4039 100644 --- a/src/modules/chips/stm32f407/stm32f407.zig +++ b/src/modules/chips/stm32f407/stm32f407.zig @@ -39,11 +39,22 @@ const regs = chip.registers; pub usingnamespace chip; +pub const clock = struct { + pub const Domain = enum { + cpu, + ahb, + apb1, + apb2, + }; +}; + // Default clock frequencies after reset, see top comment for calculation -// TODO: these would need to change when we support multiple clock configurations -pub const ahb_frequency = 16_000_000; -pub const apb1_frequency = 16_000_000; -pub const apb2_frequency = 16_000_000; +pub const clock_frequencies = .{ + .cpu = 16_000_000, + .ahb = 16_000_000, + .apb1 = 16_000_000, + .apb2 = 16_000_000, +}; pub fn parsePin(comptime spec: []const u8) type { const invalid_format_msg = "The given pin '" ++ spec ++ "' has an invalid format. Pins must follow the format \"P{Port}{Pin}\" scheme."; @@ -227,7 +238,7 @@ pub fn Uart(comptime index: usize) type { // TODO: We assume the default OVER8=0 configuration above (i.e. 16x oversampling). // TODO: Do some checks to see if the baud rate is too high (or perhaps too low) // TODO: Do a rounding div, instead of a truncating div? - const usartdiv = @intCast(u16, @divTrunc(apb1_frequency, config.baud_rate)); + const usartdiv = @intCast(u16, @divTrunc(micro.clock.get().apb1, config.baud_rate)); @field(regs, usart_name).BRR.raw = usartdiv; // enable USART, and its transmitter and receiver diff --git a/src/modules/chips/stm32f429/stm32f429.zig b/src/modules/chips/stm32f429/stm32f429.zig index 737a83f..d40d92b 100644 --- a/src/modules/chips/stm32f429/stm32f429.zig +++ b/src/modules/chips/stm32f429/stm32f429.zig @@ -27,6 +27,23 @@ const regs = chip.registers; pub usingnamespace chip; +pub const clock = struct { + pub const Domain = enum { + cpu, + ahb, + apb1, + apb2, + }; +}; + +// Default clock frequencies after reset, see top comment for calculation +pub const clock_frequencies = .{ + .cpu = 16_000_000, + .ahb = 16_000_000, + .apb1 = 16_000_000, + .apb2 = 16_000_000, +}; + pub fn parsePin(comptime spec: []const u8) type { const invalid_format_msg = "The given pin '" ++ spec ++ "' has an invalid format. Pins must follow the format \"P{Port}{Pin}\" scheme.";