mirror of
				https://github.com/TomHarte/CLK.git
				synced 2025-11-04 00:16:26 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			141 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			141 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
//
 | 
						|
//  SerialPort.cpp
 | 
						|
//  Clock Signal
 | 
						|
//
 | 
						|
//  Created by Thomas Harte on 12/10/2019.
 | 
						|
//  Copyright © 2019 Thomas Harte. All rights reserved.
 | 
						|
//
 | 
						|
 | 
						|
#include "Line.hpp"
 | 
						|
 | 
						|
using namespace Serial;
 | 
						|
 | 
						|
void Line::set_writer_clock_rate(HalfCycles clock_rate) {
 | 
						|
	clock_rate_ = clock_rate;
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
				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;
 | 
						|
					++iterator;
 | 
						|
				}
 | 
						|
				events_.erase(events_.begin(), iterator);
 | 
						|
 | 
						|
				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;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void Line::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();
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void Line::write(HalfCycles cycles, int count, int 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());
 | 
						|
		events_[event+1].type = (levels&1) ? Event::SetHigh : Event::SetLow;
 | 
						|
		levels >>= 1;
 | 
						|
		event += 2;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void Line::reset_writing() {
 | 
						|
	remaining_delays_ = 0;
 | 
						|
	events_.clear();
 | 
						|
}
 | 
						|
 | 
						|
bool Line::read() {
 | 
						|
	return level_;
 | 
						|
}
 | 
						|
 | 
						|
void Line::set_read_delegate(ReadDelegate *delegate, Storage::Time bit_length) {
 | 
						|
	read_delegate_ = delegate;
 | 
						|
	read_delegate_bit_length_ = bit_length;
 | 
						|
	read_delegate_bit_length_.simplify();
 | 
						|
	write_cycles_since_delegate_call_ = 0;
 | 
						|
}
 | 
						|
 | 
						|
void Line::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;
 | 
						|
 | 
						|
	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;
 | 
						|
}
 | 
						|
 | 
						|
Cycles::IntType Line::minimum_write_cycles_for_read_delegate_bit() {
 | 
						|
	if(!read_delegate_) return 0;
 | 
						|
	return 1 + (read_delegate_bit_length_ * static_cast<unsigned int>(clock_rate_.as_integral())).get<int>();
 | 
						|
}
 |