Merge branch 'merge_lpc'

wch-ch32v003
Felix "xq" Queißner 9 months ago
commit c703e15706

@ -0,0 +1,19 @@
Copyright (c) 2022 Zig Embedded Group Contributors
This software is provided 'as-is', without any express or implied warranty. In
no event will the authors be held liable for any damages arising from the use
of this software.
Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject to
the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim
that you wrote the original software. If you use this software in a product, an
acknowledgment in the product documentation would be appreciated but is not
required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

@ -0,0 +1,7 @@
= NXP LPC Hardware Support Package
Please see https://github.com/ZigEmbeddedGroup/lpcboot[lpcboot] as well
== What version of Zig to use
Right now we are following https://ziglang.org/download/[master], but once 0.11.0 is released, we will be switching to the latest stable version of Zig.

@ -0,0 +1,95 @@
const std = @import("std");
fn path(comptime suffix: []const u8) std.Build.LazyPath {
return .{
.cwd_relative = comptime ((std.fs.path.dirname(@src().file) orelse ".") ++ suffix),
};
}
const hal = .{
.source_file = path("/src/hals/LPC176x5x.zig"),
};
pub const chips = struct {
pub const lpc176x5x = .{
.preferred_format = .elf,
.chip = .{
// TODO: Separate over those chips, this is not generic!
.name = "LPC176x5x",
.cpu = .cortex_m3,
.memory_regions = &.{
.{ .offset = 0x00000000, .length = 512 * 1024, .kind = .flash },
.{ .offset = 0x10000000, .length = 32 * 1024, .kind = .ram },
.{ .offset = 0x2007C000, .length = 32 * 1024, .kind = .ram },
},
.register_definition = .{
.json = path("/src/chips/LPC176x5x.json"),
},
},
.hal = hal,
.binary_post_process = postprocess,
};
};
pub const boards = struct {
pub const mbed = struct {
pub const lpc1768 = .{
.preferred_format = .hex,
.chip = chips.lpc176x5x.chip,
.hal = hal,
.board = .{
.name = "mbed LPC1768",
.url = "https://os.mbed.com/platforms/mbed-LPC1768/",
.source_file = path("/src/boards/mbed_LPC1768.zig"),
},
.binary_post_process = postprocess,
};
};
};
/// Post-processes an ELF file to add a checksum over the first 8 words so the
/// cpu will properly boot.
fn postprocess(b: *std.Build, input: std.Build.LazyPath) std.Build.LazyPath {
const patchelf = b.addExecutable(.{
.name = "lpc176x5x-patchelf",
.root_source_file = path("/src/tools/patchelf.zig"),
});
const patch = b.addRunArtifact(patchelf);
patch.addFileArg(input);
return patch.addOutputFileArg("firmware.elf");
}
pub fn build(b: *std.Build) void {
_ = b;
// const optimize = b.standardOptimizeOption(.{});
// inline for (@typeInfo(boards).Struct.decls) |decl| {
// if (!decl.is_pub)
// continue;
// const exe = microzig.addEmbeddedExecutable(b, .{
// .name = @field(boards, decl.name).name ++ ".minimal",
// .source_file = .{
// .path = "test/programs/minimal.zig",
// },
// .backing = .{ .board = @field(boards, decl.name) },
// .optimize = optimize,
// });
// exe.installArtifact(b);
// }
// inline for (@typeInfo(chips).Struct.decls) |decl| {
// if (!decl.is_pub)
// continue;
// const exe = microzig.addEmbeddedExecutable(b, .{
// .name = @field(chips, decl.name).name ++ ".minimal",
// .source_file = .{
// .path = "test/programs/minimal.zig",
// },
// .backing = .{ .chip = @field(chips, decl.name) },
// .optimize = optimize,
// });
// exe.installArtifact(b);
// }
}

@ -0,0 +1,84 @@
pub const chip = @import("chip");
pub const micro = @import("microzig");
pub const clock_frequencies = .{
.cpu = 100_000_000, // 100 Mhz
};
pub fn debug_write(string: []const u8) void {
const clk_pin = micro.Pin("DIP5");
const dat_pin = micro.Pin("DIP6");
const clk = micro.core.experimental.Gpio(clk_pin, .{ .mode = .output, .initial_state = .low });
const dat = micro.core.experimental.Gpio(dat_pin, .{ .mode = .output, .initial_state = .low });
clk.init();
dat.init();
micro.debug.busy_sleep(1_000);
for (string) |c| {
comptime var i: usize = 128;
inline while (i > 0) : (i = i >> 1) {
if ((c & i) != 0) {
dat.write(.high);
} else {
dat.write(.low);
}
clk.write(.high);
micro.debug.busy_sleep(1_000);
clk.write(.low);
micro.debug.busy_sleep(1_000);
}
}
dat.write(.low);
clk.write(.low);
}
pub const pin_map = .{
// Onboard-LEDs
.@"LED-1" = "P1.18",
.@"LED-2" = "P1.20",
.@"LED-3" = "P1.21",
.@"LED-4" = "P1.23",
.LED_LINK = "P1.25",
.LED_SPEED = "P1.26",
// Ethernet
.@"TD+" = "P1.0",
.@"TD-" = "P1.1",
.@"RD+" = "P1.9",
.@"RD-" = "P1.10",
// USB
.@"D+" = "P0.29",
.@"D-" = "P0.30",
// GPIO pins
.DIP5 = "P0.9",
.DIP6 = "P0.8",
.DIP7 = "P0.7",
.DIP8 = "P0.6",
.DIP9 = "P0.0",
.DIP10 = "P0.1",
.DIP11 = "P0.18",
.DIP12 = "P0.17",
.DIP13 = "P0.15",
.DIP14 = "P0.16",
.DIP15 = "P0.23",
.DIP16 = "P0.24",
.DIP17 = "P0.25",
.DIP18 = "P0.26",
.DIP19 = "P1.30",
.DIP20 = "P1.31",
.DIP21 = "P2.5",
.DIP22 = "P2.4",
.DIP23 = "P2.3",
.DIP24 = "P2.2",
.DIP25 = "P2.1",
.DIP26 = "P2.0",
.DIP27 = "P0.11",
.DIP28 = "P0.10",
.DIP29 = "P0.5",
.DIP30 = "P0.4",
};

File diff suppressed because it is too large Load Diff

@ -0,0 +1,209 @@
const std = @import("std");
const micro = @import("microzig");
const peripherals = micro.chip.peripherals;
const GPIO = peripherals.GPIO;
const PINCONNECT = peripherals.PINCONNECT;
const UART0 = peripherals.UART0;
const UART1 = peripherals.UART1;
const UART2 = peripherals.UART2;
const UART3 = peripherals.UART3;
const SYSCON = peripherals.SYSCON;
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,
func01 = 0b01,
func10 = 0b10,
func11 = 0b11,
};
pub fn parse_pin(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.";
if (spec[0] != 'P')
@compileError(invalid_format_msg);
const index = std.mem.indexOfScalar(u8, spec, '.') orelse @compileError(invalid_format_msg);
const _port: comptime_int = std.fmt.parseInt(u3, spec[1..index], 10) catch @compileError(invalid_format_msg);
const _pin: comptime_int = std.fmt.parseInt(u5, spec[index + 1 ..], 10) catch @compileError(invalid_format_msg);
const sel_reg_name = std.fmt.comptimePrint("PINSEL{d}", .{(2 * _port + _pin / 16)});
const _regs = struct {
const name_suffix = std.fmt.comptimePrint("{d}", .{_port});
const pinsel_reg = @field(PINCONNECT, sel_reg_name);
const pinsel_field = std.fmt.comptimePrint("P{d}_{d}", .{ _port, _pin });
const dir = @field(GPIO, "DIR" ++ name_suffix);
const pin = @field(GPIO, "PIN" ++ name_suffix);
const set = @field(GPIO, "SET" ++ name_suffix);
const clr = @field(GPIO, "CLR" ++ name_suffix);
const mask = @field(GPIO, "MASK" ++ name_suffix);
};
return struct {
pub const port: u3 = _port;
pub const pin: u5 = _pin;
pub const regs = _regs;
const gpio_mask: u32 = (1 << pin);
pub const Targets = PinTarget;
};
}
pub fn route_pin(comptime pin: type, function: PinTarget) void {
var val = pin.regs.pinsel_reg.read();
@field(val, pin.regs.pinsel_field) = @intFromEnum(function);
pin.regs.pinsel_reg.write(val);
}
pub const gpio = struct {
pub fn set_output(comptime pin: type) void {
pin.regs.dir.raw |= pin.gpio_mask;
}
pub fn set_input(comptime pin: type) void {
pin.regs.dir.raw &= ~pin.gpio_mask;
}
pub fn read(comptime pin: type) micro.gpio.State {
return if ((pin.regs.pin.raw & pin.gpio_mask) != 0)
micro.gpio.State.high
else
micro.gpio.State.low;
}
pub fn write(comptime pin: type, state: micro.gpio.State) void {
if (state == .high) {
pin.regs.set.raw = pin.gpio_mask;
} else {
pin.regs.clr.raw = pin.gpio_mask;
}
}
};
pub const uart = struct {
pub const DataBits = enum(u2) {
five = 0,
six = 1,
seven = 2,
eight = 3,
};
pub const StopBits = enum(u1) {
one = 0,
two = 1,
};
pub const Parity = enum(u2) {
odd = 0,
even = 1,
mark = 2,
space = 3,
};
pub const CClkDiv = enum(u2) {
four = 0,
one = 1,
two = 2,
eight = 3,
};
};
pub fn Uart(comptime index: usize, comptime pins: micro.uart.Pins) type {
if (pins.tx != null or pins.rx != null)
@compileError("TODO: custom pins are not currently supported");
return struct {
const UARTn = switch (index) {
0 => UART0,
1 => UART1,
2 => UART2,
3 => UART3,
else => @compileError("LPC1768 has 4 UARTs available."),
};
const Self = @This();
pub fn init(config: micro.uart.Config) !Self {
micro.debug.write("0");
switch (index) {
0 => {
SYSCON.PCONP.modify(.{ .PCUART0 = 1 });
SYSCON.PCLKSEL0.modify(.{ .PCLK_UART0 = @intFromEnum(uart.CClkDiv.four) });
},
1 => {
SYSCON.PCONP.modify(.{ .PCUART1 = 1 });
SYSCON.PCLKSEL0.modify(.{ .PCLK_UART1 = @intFromEnum(uart.CClkDiv.four) });
},
2 => {
SYSCON.PCONP.modify(.{ .PCUART2 = 1 });
SYSCON.PCLKSEL1.modify(.{ .PCLK_UART2 = @intFromEnum(uart.CClkDiv.four) });
},
3 => {
SYSCON.PCONP.modify(.{ .PCUART3 = 1 });
SYSCON.PCLKSEL1.modify(.{ .PCLK_UART3 = @intFromEnum(uart.CClkDiv.four) });
},
else => unreachable,
}
micro.debug.write("1");
UARTn.LCR.modify(.{
// 8N1
.WLS = @intFromEnum(config.data_bits),
.SBS = @intFromEnum(config.stop_bits),
.PE = if (config.parity != null) @as(u1, 1) else @as(u1, 0),
.PS = if (config.parity) |p| @intFromEnum(p) else @intFromEnum(uart.Parity.odd),
.BC = 0,
.DLAB = 1,
});
micro.debug.write("2");
// TODO: UARTN_FIFOS_ARE_DISA is not available in all uarts
//UARTn.FCR.modify(.{ .FIFOEN = .UARTN_FIFOS_ARE_DISA });
micro.debug.writer().print("clock: {} baud: {} ", .{
micro.clock.get().cpu,
config.baud_rate,
}) catch {};
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;
UARTn.DLL.modify(.{ .DLLSB = @as(u8, @truncate(regval >> 0x00)) });
UARTn.DLM.modify(.{ .DLMSB = @as(u8, @truncate(regval >> 0x08)) });
UARTn.LCR.modify(.{ .DLAB = 0 });
return Self{};
}
pub fn can_write(self: Self) bool {
_ = self;
return (UARTn.LSR.read().THRE == 1);
}
pub fn tx(self: Self, ch: u8) void {
while (!self.can_write()) {} // Wait for Previous transmission
UARTn.THR.raw = ch; // Load the data to be transmitted
}
pub fn can_read(self: Self) bool {
_ = self;
return (UARTn.LSR.read().RDR == 1);
}
pub fn rx(self: Self) u8 {
while (!self.can_read()) {} // Wait till the data is received
return UARTn.RBR.read().RBR; // Read received data
}
};
}

@ -0,0 +1,66 @@
const std = @import("std");
pub fn main() !u8 {
const argv = try std.process.argsAlloc(std.heap.page_allocator);
defer std.process.argsFree(std.heap.page_allocator, argv);
if (argv.len != 3) {
std.log.err("usage: lpc-patchelf <input> <output>", .{});
return 1;
}
const input_file_name = argv[1];
const output_file_name = argv[2];
try std.fs.Dir.copyFile(
std.fs.cwd(),
input_file_name,
std.fs.cwd(),
output_file_name,
.{},
);
var file = try std.fs.cwd().openFile(output_file_name, .{ .mode = .read_write });
defer file.close();
const header = try std.elf.Header.read(file);
var iter = header.program_header_iterator(file);
while (try iter.next()) |phdr| {
if (phdr.p_type != std.elf.PT_LOAD) {
continue;
}
if (phdr.p_paddr != 0) {
// std.log.warn("LOAD program header is not located at address 0x00000000!", .{});
break;
}
const boot_sector_items = 8;
const boot_sector_size = @sizeOf([boot_sector_items]u32);
if (phdr.p_filesz < boot_sector_size) {
std.log.warn("boot header is too small! Expected {} bytes, but sector only has {} bytes!", .{
boot_sector_size,
phdr.p_filesz,
});
continue;
}
try file.seekTo(phdr.p_offset);
var reader = file.reader();
var writer = file.writer();
var checksum: u32 = 0;
var i: usize = 0;
while (i < boot_sector_items - 1) : (i += 1) {
const item = try reader.readIntLittle(u32);
checksum -%= item;
}
try writer.writeIntLittle(u32, checksum);
}
return 0;
}

@ -0,0 +1,5 @@
const micro = @import("microzig");
pub fn main() void {
// This function will contain the application logic.
}
Loading…
Cancel
Save