1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 03:29:45 +00:00

Extend I2C state machine.

This commit is contained in:
Thomas Harte 2024-03-17 21:55:19 -04:00
parent 2712d50e05
commit 7b1f800387
4 changed files with 102 additions and 41 deletions

View File

@ -14,7 +14,11 @@ void Bus::set_data(bool pulled) {
set_clock_data(clock_, pulled);
}
bool Bus::data() {
return data_;
bool result = data_;
if(peripheral_bits_) {
result |= !(peripheral_response_ & 0x80);
}
return result;
}
void Bus::set_clock(bool pulled) {
@ -25,43 +29,81 @@ bool Bus::clock() {
}
void Bus::set_clock_data(bool clock_pulled, bool data_pulled) {
const bool has_new_bit = clock_pulled && !clock_;
const bool prior_clock = clock_;
const bool prior_data = data_;
clock_ = clock_pulled;
data_ = data_pulled;
// TODO: "Stop condition: SDA goes high after SCL (?)".
// Advance peripheral input from peripheral if clock
// transitions from high to low.
if(peripheral_bits_ && prior_clock && !clock_) {
--peripheral_bits_;
peripheral_response_ <<= 1;
}
if(has_new_bit) {
// Test for a start bit.
//
// "Start condition: SDA goes low before SCL".
if(input_count_ < 0 && data_) {
input_count_ = 0;
}
// Accumulate if started.
if(input_count_ >= 0) {
const auto capture_bit = [&]() {
if(prior_clock && !clock_) {
input_ = uint16_t((input_ << 1) | (data_pulled ? 0 : 1));
++input_count_;
}
};
// Test for meaning.
switch(phase_) {
case Phase::AwaitingAddress:
if(input_count_ == 9) {
printf("Calling %02x?", uint8_t(input_));
// Check for stop condition at any time.
// "A LOW-to-HIGH transition of the data line
// while the clock is HIGH is defined as the STOP condition".
if(prior_data && !data_ && !clock_) {
printf("Stopped\n");
phase_ = Phase::AwaitingStart;
}
auto pair = peripherals_.find(uint8_t(input_));
if(pair != peripherals_.end()) {
active_peripheral_ = pair->second;
// TODO: device found; pull data low for the next clock.
}
switch(phase_) {
case Phase::AwaitingStart:
// "A HIGH-to-LOW transition of the data line while
// the clock is HIGH is defined as the START condition"
if(!prior_data && data_ && !clock_) {
phase_ = Phase::CollectingAddress;
input_count_ = 0;
input_ = 0;
printf("Waiting for [remainder of] address\n");
}
break;
case Phase::CollectingAddress:
capture_bit();
if(input_count_ == 8) {
printf("Addressed %02x?\n", uint8_t(input_));
auto pair = peripherals_.find(uint8_t(input_));
if(pair != peripherals_.end()) {
active_peripheral_ = pair->second;
peripheral_response_ = 0;
peripheral_bits_ = 2;
phase_ = Phase::AwaitingByte;
printf("Waiting for byte\n");
} else {
phase_ = Phase::AwaitingStart;
printf("No device\n");
}
break;
}
break;
case Phase::CollectingData:
break;
}
case Phase::AwaitingByte:
if(data_ && clock_) {
printf("Beginning byte\n");
phase_ = Phase::CollectingByte;
input_count_ = 0;
input_ = 0;
}
break;
case Phase::CollectingByte:
capture_bit();
if(input_count_ == 8) {
printf("Got byte %02x?\n", uint8_t(input_));
phase_ = Phase::AwaitingByte;
}
break;
}
}

View File

@ -40,11 +40,15 @@ private:
int input_count_ = -1;
Peripheral *active_peripheral_ = nullptr;
uint16_t peripheral_response_ = 0xffff;
int peripheral_bits_ = 0;
enum class Phase {
AwaitingAddress,
CollectingData,
} phase_ = Phase::AwaitingAddress;
AwaitingStart,
CollectingAddress,
AwaitingByte,
CollectingByte,
} phase_ = Phase::AwaitingStart;
};
}

View File

@ -72,6 +72,10 @@ static_assert(BitMask<15, 14>::value == 49152);
namespace Archimedes {
struct CMOSRAM: public I2C::Peripheral {
};
/// Models a half-duplex serial link between two parties, framing bytes with one start bit and two stop bits.
struct HalfDuplexSerial {
static constexpr uint16_t ShiftMask = 0b1111'1110'0000'0000;
@ -541,15 +545,15 @@ struct Interrupts {
return true;
case 0x335'0018 & AddressMask:
logger.error().append("TODO: latch B write");
logger.error().append("TODO: latch B write: %02x", value);
return true;
case 0x335'0040 & AddressMask:
logger.error().append("TODO: latch A write");
logger.error().append("TODO: latch A write: %02x", value);
return true;
case 0x335'0048 & AddressMask:
logger.error().append("TODO: latch C write");
logger.error().append("TODO: latch C write: %02x", value);
return true;
case 0x336'0000 & AddressMask:
@ -576,6 +580,8 @@ struct Interrupts {
irq_a_.status = IRQA::SetAlways | IRQA::PowerOnReset;
irq_b_.status = 0x00;
fiq_.status = 0x80; // 'set always'.
i2c_.add_peripheral(&cmos_, 0xa0);
}
private:
@ -612,6 +618,7 @@ private:
// The I2C bus.
I2C::Bus i2c_;
CMOSRAM cmos_;
};
/// Primarily models the MEMC.

View File

@ -215,8 +215,8 @@
4B228CDB24DA41890077EF25 /* ScanTarget.metal in Sources */ = {isa = PBXBuildFile; fileRef = 4B228CDA24DA41880077EF25 /* ScanTarget.metal */; };
4B2530F4244E6774007980BF /* fm.json in Resources */ = {isa = PBXBuildFile; fileRef = 4B2530F3244E6773007980BF /* fm.json */; };
4B25B5F925BD083C00362C84 /* DiskIIDrive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B80CD6D2568A82600176FCC /* DiskIIDrive.cpp */; };
4B2A1CD52BA62066004496CE /* I2C.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A1CD32BA62066004496CE /* I2C.cpp */; };
4B2A1CD62BA62066004496CE /* I2C.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A1CD32BA62066004496CE /* I2C.cpp */; };
4B2A1CDC2BA775C5004496CE /* I2C.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A1CD92BA775C5004496CE /* I2C.cpp */; };
4B2A1CDD2BA775C5004496CE /* I2C.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A1CD92BA775C5004496CE /* I2C.cpp */; };
4B2A332D1DB86821002876E3 /* OricOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B2A332B1DB86821002876E3 /* OricOptions.xib */; };
4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53911D117D36003C6002 /* CSAudioQueue.m */; };
4B2B3A4B1F9B8FA70062DABF /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3A471F9B8FA70062DABF /* Typer.cpp */; };
@ -1353,8 +1353,8 @@
4B24095A1C45DF85004DA684 /* Stepper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Stepper.hpp; sourceTree = "<group>"; };
4B2530F3244E6773007980BF /* fm.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = fm.json; sourceTree = "<group>"; };
4B262BFF29691F55002EC0F7 /* PersonalityTraits.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PersonalityTraits.hpp; sourceTree = "<group>"; };
4B2A1CD32BA62066004496CE /* I2C.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = I2C.cpp; path = I2C/I2C.cpp; sourceTree = "<group>"; };
4B2A1CD42BA62066004496CE /* I2C.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = I2C.hpp; path = I2C/I2C.hpp; sourceTree = "<group>"; };
4B2A1CD92BA775C5004496CE /* I2C.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = I2C.cpp; sourceTree = "<group>"; };
4B2A1CDA2BA775C5004496CE /* I2C.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = I2C.hpp; sourceTree = "<group>"; };
4B2A332C1DB86821002876E3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/OricOptions.xib"; sourceTree = SOURCE_ROOT; };
4B2A3B5A29993DFA007CE366 /* Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Storage.hpp; sourceTree = "<group>"; };
4B2A3B5B29995FF6007CE366 /* LineBuffer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LineBuffer.hpp; sourceTree = "<group>"; };
@ -2810,6 +2810,15 @@
path = "FM Synthesis";
sourceTree = "<group>";
};
4B2A1CDB2BA775C5004496CE /* I2C */ = {
isa = PBXGroup;
children = (
4B2A1CD92BA775C5004496CE /* I2C.cpp */,
4B2A1CDA2BA775C5004496CE /* I2C.hpp */,
);
path = I2C;
sourceTree = "<group>";
};
4B2A538F1D117D36003C6002 /* Audio */ = {
isa = PBXGroup;
children = (
@ -4825,13 +4834,12 @@
4B595FAA2086DFBA0083CAA8 /* AudioToggle */,
4B4A762D1DB1A35C007AAE2E /* AY38910 */,
4B302181208A550100773308 /* DiskII */,
4B2A1CDB2BA775C5004496CE /* I2C */,
4B4B1A39200198C900A0F866 /* KonamiSCC */,
4BC23A212467600E001A6030 /* OPx */,
4BF0BC6E2973318E00CCA2B5 /* RP5C01 */,
4B0ACBFF237756EC008902D0 /* Serial */,
4BB0A6582044FD3000FB3688 /* SN76489 */,
4B2A1CD32BA62066004496CE /* I2C.cpp */,
4B2A1CD42BA62066004496CE /* I2C.hpp */,
);
name = Components;
path = ../../Components;
@ -5880,7 +5888,7 @@
4B055A9E1FAE85DA0060FFFF /* G64.cpp in Sources */,
4B055AB81FAE860F0060FFFF /* ZX80O81P.cpp in Sources */,
4B055AB01FAE86070060FFFF /* PulseQueuedTape.cpp in Sources */,
4B2A1CD62BA62066004496CE /* I2C.cpp in Sources */,
4B2A1CDD2BA775C5004496CE /* I2C.cpp in Sources */,
4B0F1C1D2604EA1000B85C66 /* Keyboard.cpp in Sources */,
4B055AAC1FAE85FD0060FFFF /* PCMSegment.cpp in Sources */,
4BB307BC235001C300457D33 /* 6850.cpp in Sources */,
@ -6076,7 +6084,7 @@
4B4518A11F75FD1C00926311 /* D64.cpp in Sources */,
4BCE0052227CE8CA000CA200 /* DiskIICard.cpp in Sources */,
4BF0BC68297108D600CCA2B5 /* MemorySlotHandler.cpp in Sources */,
4B2A1CD52BA62066004496CE /* I2C.cpp in Sources */,
4B2A1CDC2BA775C5004496CE /* I2C.cpp in Sources */,
4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */,
4BD67DCB209BE4D700AB2146 /* StaticAnalyser.cpp in Sources */,
4BB4BFB922A4372F0069048D /* StaticAnalyser.cpp in Sources */,