Skip to content
This repository has been archived by the owner on Feb 17, 2024. It is now read-only.

RaspberryPi Pico W support #69

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/hal.zig
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub const i2c = @import("hal/i2c.zig");
pub const time = @import("hal/time.zig");
pub const uart = @import("hal/uart.zig");
pub const usb = @import("hal/usb.zig");
pub const cyw43 = @import("hal/cyw43.zig");

pub const clock_config = clocks.GlobalConfiguration.init(.{
.ref = .{ .source = .src_xosc },
Expand Down
1 change: 1 addition & 0 deletions src/hal/cyw43.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub const PioSpi = @import("cyw43/PioSpi.zig").PioSpi;
156 changes: 156 additions & 0 deletions src/hal/cyw43/PioSpi.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
const std = @import("std");
const rp2040 = @import("microzig").hal;
const gpio = rp2040.gpio;
const Pio = rp2040.pio.Pio;
const StateMachine = rp2040.pio.StateMachine;

pub const PioSpi = @This();

cs: gpio.Pin,
pio: Pio,
sm: StateMachine,

const logger = std.log.scoped(.pio_spi);

const program = blk: {
@setEvalBranchQuota(5000);
break :blk rp2040.pio.assemble(
\\ .program prog
\\ .side_set 1
\\ .wrap_target
\\ write_begin:
\\ out pins, 1 side 0
\\ jmp x-- write_begin side 1
\\ set pindirs, 0 side 0
\\ nop side 0
\\ read_begin:
\\ in pins, 1 side 1
\\ jmp y-- read_begin side 0
\\ wait 1 pin 0 side 0
\\ irq 0 side 0
\\ .wrap
, .{}).get_program_by_name("prog");
};

pub fn init(pio: Pio, sm: StateMachine, cs: gpio.Pin, dio: gpio.Pin, clk: gpio.Pin) !PioSpi {
pio.gpio_init(dio);
pio.set_input_sync_bypass(dio, true);
dio.set_schmitt(true);
dio.set_pull(null);
dio.set_drive_strength(.@"12mA");
dio.set_slew_rate(.fast);

pio.gpio_init(clk);
clk.set_drive_strength(.@"12mA");
clk.set_slew_rate(.fast);

const offset = try pio.add_program(program);
std.debug.assert(offset == 0);

pio.sm_init(sm, offset, .{
.clkdiv = .{ .int = 5 },
.shift = .{
.autopush = true,
.autopull = true,
.in_shiftdir = .left,
.out_shiftdir = .left,
},
.pin_mappings = .{
.out = .{
.base = @intFromEnum(dio),
.count = 1,
},
.set = .{
.base = @intFromEnum(dio),
.count = 1,
},
.side_set = .{
.base = @intFromEnum(clk),
.count = 1,
},
.in_base = @intFromEnum(dio),
},
.exec = .{
.wrap = program.wrap.?,
.wrap_target = program.wrap_target.?,
.side_set_optional = program.side_set.?.optional,
.side_pindir = program.side_set.?.pindir,
},
});

pio.sm_set_pindirs(sm, &.{ clk, dio }, .out);
pio.sm_set_pins(sm, &.{ clk, dio }, 0);

pio.sm_set_enabled(sm, true);

return .{
.cs = cs,
.pio = pio,
.sm = sm,
};
}

pub fn read(self: *PioSpi, out: []const u32, in: []u32) !void {
self.cs.put(0);
defer self.cs.put(1);

const write_bits = out.len - 1;
const read_bits = in.len * 32 + 32 - 1;

logger.debug("write={}({}) read={}({})", .{
std.fmt.fmtSliceHexLower(std.mem.sliceAsBytes(out)),
write_bits,
std.fmt.fmtSliceHexLower(std.mem.sliceAsBytes(in)),
read_bits,
});

self.pio.sm_set_enabled(self.sm, false);

self.pio.sm_set_pindir(self.sm, .out);
self.pio.sm_set_x(self.sm, write_bits);
self.pio.sm_set_y(self.sm, read_bits);
self.pio.sm_jmp(self.sm, program.wrap_target.?);

self.pio.sm_set_enabled(self.sm, true);

for (out) |v| {
self.pio.sm_blocking_write(self.sm, v);
}

for (in) |*v| {
v.* = self.pio.sm_blocking_read(self.sm);
}

const status = self.pio.sm_blocking_read(self.sm);

logger.debug("status = {}", .{status});
}

pub fn write(self: *PioSpi, out: []const u32) !void {
self.cs.put(0);

const write_bits = out.len * 32 - 1;
const read_bits = 32 - 1;

logger.debug("write={}({})", .{
std.fmt.fmtSliceHexLower(std.mem.sliceAsBytes(out)),
write_bits,
});

self.pio.sm_set_enabled(self.sm, false);

self.pio.sm_set_pindir(self.sm, .out);
self.pio.sm_set_x(self.sm, write_bits);
self.pio.sm_set_y(self.sm, read_bits);
self.pio.sm_jmp(self.sm, program.wrap_target.?);

self.pio.sm_set_enabled(self.sm, true);

for (out) |v| {
self.pio.sm_blocking_write(self.sm, v);
}
const status = self.pio.sm_blocking_read(self.sm);

logger.debug("status = {}", .{status});
self.cs.put(1);
}
30 changes: 30 additions & 0 deletions src/hal/gpio.zig
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,43 @@ pub const Pin = enum(u5) {
}
}

pub inline fn set_drive_strength(gpio: Pin, strength: DriveStrength) void {
const pads_reg = gpio.get_pads_reg();

pads_reg.modify(.{
.DRIVE = .{
.value = switch (strength) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you're going to do this, you might as well do an @intFromEnum() and assign it to raw.

.@"2mA" => .@"2mA",
.@"4mA" => .@"4mA",
.@"8mA" => .@"8mA",
.@"12mA" => .@"12mA",
},
},
});
}

pub inline fn set_slew_rate(gpio: Pin, rate: SlewRate) void {
const pads_reg = gpio.get_pads_reg();

pads_reg.modify(.{ .SLEWFAST = switch (rate) {
.slow => 0,
.fast => 1,
} });
}

pub inline fn set_direction(gpio: Pin, direction: Direction) void {
switch (direction) {
.in => SIO.GPIO_OE_CLR.raw = gpio.mask(),
.out => SIO.GPIO_OE_SET.raw = gpio.mask(),
}
}

pub fn set_schmitt(gpio: Pin, enabled: bool) void {
const pads_reg = gpio.get_pads_reg();

pads_reg.modify(.{ .SCHMITT = @intFromBool(enabled) });
}

/// Drive a single GPIO high/low
pub inline fn put(gpio: Pin, value: u1) void {
switch (value) {
Expand Down
150 changes: 150 additions & 0 deletions src/hal/pio.zig
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,134 @@ pub const Pio = enum(u1) {
});
}

pub fn sm_set_pindir(self: Pio, sm: StateMachine, dir: gpio.Direction) void {
self.sm_exec(sm, .{
.payload = .{
.set = .{
.data = @intFromEnum(dir),
.destination = .pindirs,
},
},
.delay_side_set = 0,
.tag = .set,
});
}

pub fn sm_set_pindirs(self: Pio, sm: StateMachine, pins: []const gpio.Pin, dir: gpio.Direction) void {
const sm_regs = self.get_sm_regs(sm);

const pinctrl_saved = sm_regs.pinctrl.raw;
defer sm_regs.pinctrl.raw = pinctrl_saved;

for (pins) |pin| {
sm_regs.pinctrl.modify(.{
.SET_BASE = @intFromEnum(pin),
.SET_COUNT = 1,
});

self.sm_exec(sm, .{
.payload = .{
.set = .{
.data = @intFromEnum(dir),
.destination = .pindirs,
},
},
.delay_side_set = 0,
.tag = .set,
});
}
}

pub fn sm_set_pins(self: Pio, sm: StateMachine, pins: []const gpio.Pin, state: u1) void {
const sm_regs = self.get_sm_regs(sm);

const pinctrl_saved = sm_regs.pinctrl.raw;
defer sm_regs.pinctrl.raw = pinctrl_saved;

for (pins) |pin| {
sm_regs.pinctrl.modify(.{
.SET_BASE = @intFromEnum(pin),
.SET_COUNT = 1,
});

self.sm_exec(sm, .{
.payload = .{
.set = .{
.data = state,
.destination = .pins,
},
},
.delay_side_set = 0,
.tag = .set,
});
}
}

pub fn sm_set_y(self: Pio, sm: StateMachine, value: u32) void {
self.sm_blocking_write(sm, value);
self.sm_exec(sm, .{
.payload = .{
.out = .{
.bit_count = 0,
.destination = .y,
},
},
.delay_side_set = 0,
.tag = .out,
});
}

pub fn sm_set_x(self: Pio, sm: StateMachine, value: u32) void {
self.sm_blocking_write(sm, value);
self.sm_exec(sm, .{
.payload = .{
.out = .{
.bit_count = 0,
.destination = .x,
},
},
.delay_side_set = 0,
.tag = .out,
});
}

pub fn sm_get_y(self: Pio, sm: StateMachine) u32 {
self.sm_exec(sm, .{
.payload = .{
.in = .{
.bit_count = 0,
.source = .y,
},
},
.delay_side_set = 0,
.tag = .out,
});
return self.sm_blocking_read(sm);
}

pub fn sm_jmp(self: Pio, sm: StateMachine, address: u5) void {
self.sm_exec(sm, Instruction{
.tag = .jmp,

.delay_side_set = 0,
.payload = .{
.jmp = .{
.address = address,
.condition = .always,
},
},
});
}

pub fn set_input_sync_bypass(self: Pio, pin: gpio.Pin, enabled: bool) void {
const pio_regs = self.get_regs();
if (enabled) {
pio_regs.INPUT_SYNC_BYPASS |= pin.mask();
} else {
pio_regs.INPUT_SYNC_BYPASS &= ~pin.mask();
}
}

pub fn sm_is_tx_fifo_full(self: Pio, sm: StateMachine) bool {
const regs = self.get_regs();
const txfull = regs.FSTAT.read().TXFULL;
Expand All @@ -330,6 +458,28 @@ pub const Pio = enum(u1) {
self.sm_write(sm, value);
}

pub fn sm_is_rx_fifo_empty(self: Pio, sm: StateMachine) bool {
const regs = self.get_regs();
const rxempty = regs.FSTAT.read().RXEMPTY;
return (rxempty & (@as(u4, 1) << @intFromEnum(sm))) != 0;
}

pub fn sm_get_rx_fifo(self: Pio, sm: StateMachine) *volatile u32 {
const regs = self.get_regs();
const fifos: *volatile [4]u32 = @ptrCast(&regs.RXF0);
return &fifos[@intFromEnum(sm)];
}

pub fn sm_read(self: Pio, sm: StateMachine) u32 {
const fifo_ptr = self.sm_get_rx_fifo(sm);
return fifo_ptr.*;
}

pub fn sm_blocking_read(self: Pio, sm: StateMachine) u32 {
while (self.sm_is_rx_fifo_empty(sm)) {}
return self.sm_read(sm);
}

pub fn sm_set_enabled(self: Pio, sm: StateMachine, enabled: bool) void {
const regs = self.get_regs();

Expand Down