Implement RTC #1
|
@ -318,7 +318,6 @@ const Gpio = struct {
|
||||||
const Clock = struct {
|
const Clock = struct {
|
||||||
const This = @This();
|
const This = @This();
|
||||||
|
|
||||||
cmd: Command,
|
|
||||||
writer: Writer,
|
writer: Writer,
|
||||||
reader: Reader,
|
reader: Reader,
|
||||||
state: State,
|
state: State,
|
||||||
|
@ -343,7 +342,7 @@ const Clock = struct {
|
||||||
|
|
||||||
const State = union(enum) {
|
const State = union(enum) {
|
||||||
Idle,
|
Idle,
|
||||||
CommandInput,
|
Command,
|
||||||
Write: Register,
|
Write: Register,
|
||||||
Read: Register,
|
Read: Register,
|
||||||
};
|
};
|
||||||
|
@ -499,72 +498,6 @@ const Clock = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Command = struct {
|
|
||||||
buf: u8,
|
|
||||||
i: u4,
|
|
||||||
|
|
||||||
fn write(self: *Command, value: u1) void {
|
|
||||||
const idx = @intCast(u3, self.i);
|
|
||||||
self.buf = (self.buf & ~(@as(u8, 1) << idx)) | @as(u8, value) << idx;
|
|
||||||
self.i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(self: *Command) void {
|
|
||||||
self.buf = 0;
|
|
||||||
self.i = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn isFinished(self: *const Command) bool {
|
|
||||||
return self.i >= 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn getCommand(self: *const Command) u8 {
|
|
||||||
// If High Nybble is 0x6, no need to switch the endianness
|
|
||||||
if (self.buf >> 4 & 0xF == 0x6) return self.buf;
|
|
||||||
|
|
||||||
// Turns out reversing the order of bits isn't trivial at all
|
|
||||||
// https://stackoverflow.com/questions/2602823/in-c-c-whats-the-simplest-way-to-reverse-the-order-of-bits-in-a-byte
|
|
||||||
var ret = self.buf;
|
|
||||||
ret = (ret & 0xF0) >> 4 | (ret & 0x0F) << 4;
|
|
||||||
ret = (ret & 0xCC) >> 2 | (ret & 0x33) << 2;
|
|
||||||
ret = (ret & 0xAA) >> 1 | (ret & 0x55) << 1;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handleCommand(self: *const Command, rtc: *Clock) State {
|
|
||||||
const command = self.getCommand();
|
|
||||||
log.debug("RTC: Handling Command 0x{X:0>2} [0b{b:0>8}]", .{ command, command });
|
|
||||||
|
|
||||||
const is_write = command & 1 == 0;
|
|
||||||
const rtc_register = @truncate(u3, command >> 1 & 0x7);
|
|
||||||
|
|
||||||
if (is_write) {
|
|
||||||
return switch (rtc_register) {
|
|
||||||
0 => blk: {
|
|
||||||
rtc.reset();
|
|
||||||
break :blk .Idle;
|
|
||||||
},
|
|
||||||
1 => .{ .Write = .Control },
|
|
||||||
2 => .{ .Write = .DateTime },
|
|
||||||
3 => .{ .Write = .Time },
|
|
||||||
6 => blk: {
|
|
||||||
rtc.irq();
|
|
||||||
break :blk .Idle;
|
|
||||||
},
|
|
||||||
4, 5, 7 => .Idle,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return switch (rtc_register) {
|
|
||||||
1 => .{ .Read = .Control },
|
|
||||||
2 => .{ .Read = .DateTime },
|
|
||||||
3 => .{ .Read = .Time },
|
|
||||||
0, 4, 5, 6, 7 => .Idle, // Do Nothing
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Data = extern union {
|
const Data = extern union {
|
||||||
sck: Bit(u8, 0),
|
sck: Bit(u8, 0),
|
||||||
sio: Bit(u8, 1),
|
sio: Bit(u8, 1),
|
||||||
|
@ -590,7 +523,6 @@ const Clock = struct {
|
||||||
|
|
||||||
fn init(ptr: *This, cpu: *Arm7tdmi, gpio: *const Gpio) void {
|
fn init(ptr: *This, cpu: *Arm7tdmi, gpio: *const Gpio) void {
|
||||||
ptr.* = .{
|
ptr.* = .{
|
||||||
.cmd = .{ .buf = 0, .i = 0 },
|
|
||||||
.writer = .{ .buf = 0, .i = 0, .count = 0 },
|
.writer = .{ .buf = 0, .i = 0, .count = 0 },
|
||||||
.reader = .{ .i = 0, .count = 0 },
|
.reader = .{ .i = 0, .count = 0 },
|
||||||
.state = .Idle,
|
.state = .Idle,
|
||||||
|
@ -616,22 +548,23 @@ const Clock = struct {
|
||||||
if (cache.sck.read()) {
|
if (cache.sck.read()) {
|
||||||
if (!cache.cs.read() and value.cs.read()) {
|
if (!cache.cs.read() and value.cs.read()) {
|
||||||
log.debug("RTC: Entering Command Mode", .{});
|
log.debug("RTC: Entering Command Mode", .{});
|
||||||
self.state = .CommandInput;
|
self.state = .Command;
|
||||||
self.cmd.reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break :blk @truncate(u4, value.raw);
|
break :blk @truncate(u4, value.raw);
|
||||||
},
|
},
|
||||||
.CommandInput => blk: {
|
.Command => blk: {
|
||||||
if (!value.cs.read()) log.err("RTC: Expected CS to be set during {}, however CS was cleared", .{self.state});
|
if (!value.cs.read()) log.err("RTC: Expected CS to be set during {}, however CS was cleared", .{self.state});
|
||||||
|
|
||||||
// If SCK rises, sample SIO
|
// If SCK rises, sample SIO
|
||||||
if (!cache.sck.read() and value.sck.read()) {
|
if (!cache.sck.read() and value.sck.read()) {
|
||||||
self.cmd.write(@boolToInt(value.sio.read()));
|
self.writer.push(@boolToInt(value.sio.read()));
|
||||||
|
|
||||||
|
if (self.writer.finished()) {
|
||||||
|
self.state = self.processCommand(self.writer.buf);
|
||||||
|
self.writer.reset();
|
||||||
|
|
||||||
if (self.cmd.isFinished()) {
|
|
||||||
self.state = self.cmd.handleCommand(self);
|
|
||||||
log.debug("RTC: Switching to {}", .{self.state});
|
log.debug("RTC: Switching to {}", .{self.state});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -707,4 +640,48 @@ const Clock = struct {
|
||||||
self.cpu.bus.io.irq.game_pak.set();
|
self.cpu.bus.io.irq.game_pak.set();
|
||||||
self.cpu.handleInterrupt();
|
self.cpu.handleInterrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn processCommand(self: *This, raw_command: u8) State {
|
||||||
|
const command = blk: {
|
||||||
|
// If High Nybble is 0x6, no need to switch the endianness
|
||||||
|
if (raw_command >> 4 & 0xF == 0x6) break :blk raw_command;
|
||||||
|
|
||||||
|
// Turns out reversing the order of bits isn't trivial at all
|
||||||
|
// https://stackoverflow.com/questions/2602823/in-c-c-whats-the-simplest-way-to-reverse-the-order-of-bits-in-a-byte
|
||||||
|
var ret = raw_command;
|
||||||
|
ret = (ret & 0xF0) >> 4 | (ret & 0x0F) << 4;
|
||||||
|
ret = (ret & 0xCC) >> 2 | (ret & 0x33) << 2;
|
||||||
|
ret = (ret & 0xAA) >> 1 | (ret & 0x55) << 1;
|
||||||
|
|
||||||
|
break :blk ret;
|
||||||
|
};
|
||||||
|
log.debug("RTC: Handling Command 0x{X:0>2} [0b{b:0>8}]", .{ command, command });
|
||||||
|
|
||||||
|
const is_write = command & 1 == 0;
|
||||||
|
const rtc_register = @truncate(u3, command >> 1 & 0x7);
|
||||||
|
|
||||||
|
if (is_write) {
|
||||||
|
return switch (rtc_register) {
|
||||||
|
0 => blk: {
|
||||||
|
self.reset();
|
||||||
|
break :blk .Idle;
|
||||||
|
},
|
||||||
|
1 => .{ .Write = .Control },
|
||||||
|
2 => .{ .Write = .DateTime },
|
||||||
|
3 => .{ .Write = .Time },
|
||||||
|
6 => blk: {
|
||||||
|
self.irq();
|
||||||
|
break :blk .Idle;
|
||||||
|
},
|
||||||
|
4, 5, 7 => .Idle,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return switch (rtc_register) {
|
||||||
|
1 => .{ .Read = .Control },
|
||||||
|
2 => .{ .Read = .DateTime },
|
||||||
|
3 => .{ .Read = .Time },
|
||||||
|
0, 4, 5, 6, 7 => .Idle, // Do Nothing
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue