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.
wch-ch32v003
Riccardo Binetti 2 years ago committed by GitHub
parent ac19b7de8e
commit 8720973005
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,6 @@
const std = @import("std"); const std = @import("std");
const micro = @import("microzig.zig"); const micro = @import("microzig.zig");
const chip = @import("chip");
/// An enumeration of clock sources. /// An enumeration of clock sources.
pub const Source = enum { pub const Source = enum {
@ -10,6 +11,9 @@ pub const Source = enum {
cpu, 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. /// Is `true` when microzig has a clock frequency available.
/// Clock can be provided by several clock sources /// Clock can be provided by several clock sources
pub const has_clock = (clock_source_type != void); 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. /// 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 { pub fn ensure() void {
if (!has_clock) 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. /// Returns the Clocks struct, with all clock domains frequencies in hertz.
pub inline fn get() u32 { pub inline fn get() Clocks {
ensure(); ensure();
return @field(clock_source_type, freq_decl_name); 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 no_clock_source_type = opaque {};
const clock_source_type = if (@hasDecl(micro.app, freq_decl_name)) const clock_source_type = if (@hasDecl(micro.app, freq_decl_name))

@ -1,6 +1,8 @@
pub const chip = @import("chip"); pub const chip = @import("chip");
pub const cpu_frequency = 16_000_000; pub const clock_frequencies = .{
.cpu = 16_000_000,
};
pub const pin_map = .{ pub const pin_map = .{
// Port A // Port A

@ -1,7 +1,9 @@
pub const chip = @import("chip"); pub const chip = @import("chip");
pub const micro = @import("microzig"); 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 { pub fn debugWrite(string: []const u8) void {
const clk_pin = micro.Pin("DIP5"); const clk_pin = micro.Pin("DIP5");

@ -1,8 +1,6 @@
pub const chip = @import("chip"); pub const chip = @import("chip");
pub const micro = @import("microzig"); pub const micro = @import("microzig");
pub const cpu_frequency = 16_000_000;
pub const pin_map = .{ pub const pin_map = .{
// LED cross, connected to GPIOD bits 12..15 // LED cross, connected to GPIOD bits 12..15
// N orange // N orange

@ -11,6 +11,12 @@ const Port = enum(u8) {
D = 3, D = 3,
}; };
pub const clock = struct {
pub const Domain = enum {
cpu,
};
};
pub fn parsePin(comptime spec: []const u8) type { 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."; 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(); const Self = @This();
fn computeDivider(baud_rate: u32) !u12 { 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; const divider = ((pclk + (8 * baud_rate)) / (16 * baud_rate)) - 1;
return std.math.cast(u12, divider) catch return error.UnsupportedBaudRate; return std.math.cast(u12, divider) catch return error.UnsupportedBaudRate;
} }
fn computeBaudRate(divider: u12) u32 { 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 { pub fn init(config: micro.uart.Config) !Self {

@ -5,7 +5,15 @@ const regs = chip.registers;
pub usingnamespace chip; 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) { pub const PinTarget = enum(u2) {
func00 = 0b00, func00 = 0b00,
@ -156,11 +164,11 @@ pub fn Uart(comptime index: usize) type {
//UARTn.FCR.modify(.{ .FIFOEN = .UARTN_FIFOS_ARE_DISA }); //UARTn.FCR.modify(.{ .FIFOEN = .UARTN_FIFOS_ARE_DISA });
micro.debug.writer().print("clock: {} baud: {} ", .{ micro.debug.writer().print("clock: {} baud: {} ", .{
micro.clock.get(), micro.clock.get().cpu,
config.baud_rate, config.baud_rate,
}) catch {}; }) catch {};
const pclk = micro.clock.get() / 4; const pclk = micro.clock.get().cpu / 4;
const divider = (pclk / (16 * config.baud_rate)); const divider = (pclk / (16 * config.baud_rate));
const regval = std.math.cast(u16, divider) orelse return error.UnsupportedBaudRate; const regval = std.math.cast(u16, divider) orelse return error.UnsupportedBaudRate;

@ -43,6 +43,24 @@ const regs = chip.registers;
pub usingnamespace chip; pub usingnamespace chip;
pub const cpu = @import("cpu"); 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 { 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."; 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. // 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 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? // 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; regs.USART1.BRR.raw = usartdiv;
// Above, ignore the BRR struct fields DIV_Mantissa and DIV_Fraction, // Above, ignore the BRR struct fields DIV_Mantissa and DIV_Fraction,
// those seem to be for another chipset; .svd file bug? // those seem to be for another chipset; .svd file bug?

@ -39,11 +39,22 @@ const regs = chip.registers;
pub usingnamespace chip; 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 // Default clock frequencies after reset, see top comment for calculation
// TODO: these would need to change when we support multiple clock configurations pub const clock_frequencies = .{
pub const ahb_frequency = 16_000_000; .cpu = 16_000_000,
pub const apb1_frequency = 16_000_000; .ahb = 16_000_000,
pub const apb2_frequency = 16_000_000; .apb1 = 16_000_000,
.apb2 = 16_000_000,
};
pub fn parsePin(comptime spec: []const u8) type { 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."; 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: 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 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? // 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; @field(regs, usart_name).BRR.raw = usartdiv;
// enable USART, and its transmitter and receiver // enable USART, and its transmitter and receiver

@ -27,6 +27,23 @@ const regs = chip.registers;
pub usingnamespace chip; 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 { 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."; const invalid_format_msg = "The given pin '" ++ spec ++ "' has an invalid format. Pins must follow the format \"P{Port}{Pin}\" scheme.";

Loading…
Cancel
Save