1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-30 04:50:08 +00:00
CLK/Components/Serial/Line.cpp

214 lines
6.7 KiB
C++
Raw Normal View History

//
// SerialPort.cpp
// Clock Signal
//
// Created by Thomas Harte on 12/10/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
#include "Line.hpp"
2021-11-24 21:28:18 +00:00
#include <cassert>
#include <limits>
using namespace Serial;
template <bool include_clock>
void Line<include_clock>::set_writer_clock_rate(HalfCycles clock_rate) {
clock_rate_ = clock_rate;
}
template <bool include_clock>
void Line<include_clock>::advance_writer(HalfCycles cycles) {
if(cycles == HalfCycles(0)) return;
const auto integral_cycles = cycles.as_integral();
remaining_delays_ = std::max(remaining_delays_ - integral_cycles, Cycles::IntType(0));
if(events_.empty()) {
write_cycles_since_delegate_call_ += integral_cycles;
if(transmission_extra_) {
transmission_extra_ -= integral_cycles;
if(transmission_extra_ <= 0) {
transmission_extra_ = 0;
if constexpr (!include_clock) {
update_delegate(level_);
}
}
}
} else {
while(!events_.empty()) {
if(events_.front().delay <= integral_cycles) {
cycles -= events_.front().delay;
write_cycles_since_delegate_call_ += events_.front().delay;
const auto old_level = level_;
auto iterator = events_.begin() + 1;
while(iterator != events_.end() && iterator->type != Event::Delay) {
level_ = iterator->type == Event::SetHigh;
if constexpr(include_clock) {
update_delegate(level_);
}
++iterator;
}
events_.erase(events_.begin(), iterator);
if constexpr (!include_clock) {
if(old_level != level_) {
update_delegate(old_level);
}
}
// Book enough extra time for the read delegate to be posted
// the final bit if one is attached.
if(events_.empty()) {
transmission_extra_ = minimum_write_cycles_for_read_delegate_bit();
}
} else {
events_.front().delay -= integral_cycles;
write_cycles_since_delegate_call_ += integral_cycles;
break;
}
}
}
}
template <bool include_clock>
void Line<include_clock>::write(bool level) {
if(!events_.empty()) {
events_.emplace_back();
events_.back().type = level ? Event::SetHigh : Event::SetLow;
} else {
level_ = level;
transmission_extra_ = minimum_write_cycles_for_read_delegate_bit();
}
}
template <bool include_clock>
template <bool lsb_first, typename IntT> void Line<include_clock>::write_internal(HalfCycles cycles, int count, IntT levels) {
remaining_delays_ += count * cycles.as_integral();
auto event = events_.size();
events_.resize(events_.size() + size_t(count)*2);
while(count--) {
events_[event].type = Event::Delay;
events_[event].delay = int(cycles.as_integral());
IntT bit;
if constexpr (lsb_first) {
bit = levels & 1;
levels >>= 1;
} else {
constexpr auto top_bit = IntT(0x80) << ((sizeof(IntT) - 1) * 8);
bit = levels & top_bit;
levels <<= 1;
}
events_[event+1].type = bit ? Event::SetHigh : Event::SetLow;
event += 2;
}
}
template <bool include_clock>
void Line<include_clock>::write(HalfCycles cycles, int count, int levels) {
write_internal<true, int>(cycles, count, levels);
}
template <bool include_clock>
template <bool lsb_first, typename IntT> void Line<include_clock>::write(HalfCycles cycles, IntT value) {
write_internal<lsb_first, IntT>(cycles, 8 * sizeof(IntT), value);
}
template <bool include_clock>
void Line<include_clock>::reset_writing() {
remaining_delays_ = 0;
events_.clear();
}
template <bool include_clock>
bool Line<include_clock>::read() const {
return level_;
}
template <bool include_clock>
void Line<include_clock>::set_read_delegate(ReadDelegate *delegate, Storage::Time bit_length) {
read_delegate_ = delegate;
if constexpr (!include_clock) {
assert(bit_length > Storage::Time(0));
read_delegate_bit_length_ = bit_length;
read_delegate_bit_length_.simplify();
write_cycles_since_delegate_call_ = 0;
}
}
template <bool include_clock>
void Line<include_clock>::update_delegate(bool level) {
// Exit early if there's no delegate, or if the delegate is waiting for
// zero and this isn't zero.
if(!read_delegate_) return;
if constexpr (!include_clock) {
const int cycles_to_forward = write_cycles_since_delegate_call_;
write_cycles_since_delegate_call_ = 0;
if(level && read_delegate_phase_ == ReadDelegatePhase::WaitingForZero) return;
// Deal with a transition out of waiting-for-zero mode by seeding time left
// in bit at half a bit.
if(read_delegate_phase_ == ReadDelegatePhase::WaitingForZero) {
time_left_in_bit_ = read_delegate_bit_length_;
time_left_in_bit_.clock_rate <<= 1;
read_delegate_phase_ = ReadDelegatePhase::Serialising;
}
// Forward as many bits as occur.
Storage::Time time_left(cycles_to_forward, int(clock_rate_.as_integral()));
const int bit = level ? 1 : 0;
int bits = 0;
while(time_left >= time_left_in_bit_) {
++bits;
if(!read_delegate_->serial_line_did_produce_bit(this, bit)) {
read_delegate_phase_ = ReadDelegatePhase::WaitingForZero;
if(bit) return;
}
time_left -= time_left_in_bit_;
time_left_in_bit_ = read_delegate_bit_length_;
}
time_left_in_bit_ -= time_left;
} else {
read_delegate_->serial_line_did_produce_bit(this, level);
}
}
template <bool include_clock>
Cycles::IntType Line<include_clock>::minimum_write_cycles_for_read_delegate_bit() {
if(!read_delegate_) return 0;
return 1 + (read_delegate_bit_length_ * unsigned(clock_rate_.as_integral())).template get<int>();
}
//
// Explicitly instantiate the meaningful instances of templates above;
// this class uses templates primarily to keep the interface compact and
// to take advantage of constexpr functionality selection, not so as
// to be generic.
//
template class Serial::Line<true>;
template class Serial::Line<false>;
template void Line<true>::write<true, uint8_t>(HalfCycles, uint8_t);
template void Line<true>::write<false, uint8_t>(HalfCycles, uint8_t);
template void Line<true>::write<true, uint16_t>(HalfCycles, uint16_t);
template void Line<true>::write<false, uint16_t>(HalfCycles, uint16_t);
template void Line<true>::write<true, uint32_t>(HalfCycles, uint32_t);
template void Line<true>::write<false, uint32_t>(HalfCycles, uint32_t);
template void Line<true>::write<true, uint64_t>(HalfCycles, uint64_t);
template void Line<true>::write<false, uint64_t>(HalfCycles, uint64_t);
template void Line<false>::write<true, uint8_t>(HalfCycles, uint8_t);
template void Line<false>::write<false, uint8_t>(HalfCycles, uint8_t);
template void Line<false>::write<true, uint16_t>(HalfCycles, uint16_t);
template void Line<false>::write<false, uint16_t>(HalfCycles, uint16_t);
template void Line<false>::write<true, uint32_t>(HalfCycles, uint32_t);
template void Line<false>::write<false, uint32_t>(HalfCycles, uint32_t);
template void Line<false>::write<true, uint64_t>(HalfCycles, uint64_t);
template void Line<false>::write<false, uint64_t>(HalfCycles, uint64_t);