// // SerialPort.cpp // Clock Signal // // Created by Thomas Harte on 12/10/2019. // Copyright © 2019 Thomas Harte. All rights reserved. // #include "Line.hpp" #include #include using namespace Serial; template void Line::set_writer_clock_rate(const HalfCycles clock_rate) { clock_rate_ = clock_rate; } template void Line::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 void Line::write(const 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 template void Line::write_internal( const HalfCycles cycles, int count, const 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 void Line::write(const HalfCycles cycles, const int count, const int levels) { write_internal(cycles, count, levels); } template template void Line::write(const HalfCycles cycles, const IntT value) { write_internal(cycles, 8 * sizeof(IntT), value); } template void Line::reset_writing() { remaining_delays_ = 0; events_.clear(); } template bool Line::read() const { return level_; } template void Line::set_read_delegate( ReadDelegate *const delegate, [[maybe_unused]] const 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 void Line::update_delegate(const 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; while(time_left >= time_left_in_bit_) { 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 Cycles::IntType Line::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(); } // // 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; template class Serial::Line; template void Line::write(HalfCycles, uint8_t); template void Line::write(HalfCycles, uint8_t); template void Line::write(HalfCycles, uint16_t); template void Line::write(HalfCycles, uint16_t); template void Line::write(HalfCycles, uint32_t); template void Line::write(HalfCycles, uint32_t); template void Line::write(HalfCycles, uint64_t); template void Line::write(HalfCycles, uint64_t); template void Line::write(HalfCycles, uint8_t); template void Line::write(HalfCycles, uint8_t); template void Line::write(HalfCycles, uint16_t); template void Line::write(HalfCycles, uint16_t); template void Line::write(HalfCycles, uint32_t); template void Line::write(HalfCycles, uint32_t); template void Line::write(HalfCycles, uint64_t); template void Line::write(HalfCycles, uint64_t);