mirror of
				https://github.com/TomHarte/CLK.git
				synced 2025-11-04 00:16:26 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			219 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			219 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
//
 | 
						|
//  SerialPort.cpp
 | 
						|
//  Clock Signal
 | 
						|
//
 | 
						|
//  Created by Thomas Harte on 12/10/2019.
 | 
						|
//  Copyright © 2019 Thomas Harte. All rights reserved.
 | 
						|
//
 | 
						|
 | 
						|
#include "Line.hpp"
 | 
						|
 | 
						|
#include <cassert>
 | 
						|
#include <limits>
 | 
						|
 | 
						|
using namespace Serial;
 | 
						|
 | 
						|
template <bool include_clock>
 | 
						|
void Line<include_clock>::set_writer_clock_rate(const 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(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 <bool include_clock>
 | 
						|
template <bool lsb_first, typename IntT> void Line<include_clock>::write_internal(
 | 
						|
	const 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(const HalfCycles cycles, const int count, const int levels) {
 | 
						|
	write_internal<true, int>(cycles, count, levels);
 | 
						|
}
 | 
						|
 | 
						|
template <bool include_clock>
 | 
						|
template <bool lsb_first, typename IntT> void Line<include_clock>::write(const HalfCycles cycles, const 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 *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 <bool include_clock>
 | 
						|
void Line<include_clock>::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 <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);
 |