1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-05 10:28:58 +00:00

Starts a transition to bus-level knowledge of SCSI-specific bus timing thresholds.

The idea being that bus attachees need not all count time for themselves. They can be very plain finite state machines.

New semantics are not yet implemented within the Bus. The plan is to do that, remove the internal counting of time within the NCR, then adjust the Target to be more explicitly stateful.
This commit is contained in:
Thomas Harte 2019-08-31 21:44:22 -04:00
parent 25956bd90f
commit 310c722cc0
4 changed files with 74 additions and 15 deletions

View File

@ -31,7 +31,7 @@ void Bus::set_device_output(size_t device, BusState output) {
state_ & 0xff,
(state_ & Line::Parity) ? 'p' : '-',
(state_ & Line::SelectTarget) ? 's' : '-',
(state_ & Line::Attention) ? 'a' : '-',
(state_ & Line::Attention) ? 't' : '-',
(state_ & Line::Control) ? 'c' : '-',
(state_ & Line::Busy) ? 'b' : '-',
(state_ & Line::Acknowledge) ? 'a' : '-',
@ -42,7 +42,7 @@ void Bus::set_device_output(size_t device, BusState output) {
);
for(auto &observer: observers_) {
observer->scsi_bus_did_change(this, state_);
observer->scsi_bus_did_change(this, state_, 0.0);
}
}

View File

@ -12,6 +12,9 @@
#include <limits>
#include <vector>
#include "../../../ClockReceiver/ClockReceiver.hpp"
#include "../../../ClockReceiver/ClockingHintSource.hpp"
namespace SCSI {
typedef int BusState;
@ -51,9 +54,46 @@ enum Line: BusState {
Request = 1 << 17,
};
#define us(x) (x) / 1000000.0
#define ns(x) (x) / 1000000000.0
/// The minimum amount of time a SCSI device must wait after asserting ::Busy
/// until the data bus can be inspected to see whether arbitration has been won.
constexpr double ArbitrationDelay = us(1.7);
/// The maximum amount of time a SCSI device can take from a detection that the
/// bus is free until it asserts ::Busy and its ID for the purposes of arbitration.
constexpr double BusSetDelay = us(1.1);
/// The maximum amount of time a SCSI device is permitted to take to stop driving
/// all bus signals after: (i) the release of ::Busy ushering in a bus free phase;
/// or (ii) some other device has asserted ::Select during an arbitration phase.
constexpr double BusClearDelay = ns(650.0);
/// The minimum amount of time to wait for the bus to settle after changing
/// "certain control signals". TODO: which?
constexpr double BusSettleDelay = ns(450.0);
/// The minimum amount of time a SCSI must wait from detecting that the bus is free
/// and asserting ::Busy if starting an arbitration phase.
constexpr double BusFreeDelay = ns(100.0);
/// The minimum amount of time required for deskew of "certain signals". TODO: which?
constexpr double DeskewDelay = ns(45.0);
/// The minimum amount of time that reset must be held for.
constexpr double ResetHoldTime = us(25.0);
/// The maximum amount of time that propagation of a SCSI bus signal can take between
/// any two devices.
constexpr double CableSkew = ns(10.0);
#undef ns
#undef us
class Bus {
public:
/*!
Adds a device to the bus, returning the index it should use
to refer to itself in subsequent calls to set_device_output.
@ -71,7 +111,12 @@ class Bus {
BusState get_state();
struct Observer {
virtual void scsi_bus_did_change(Bus *, BusState new_state) = 0;
/// Reports to an observer that the bus changed from a previous state to @c new_state,
/// along with the time since that change was observed. The time is in seconds, and is
/// intended for comparison with the various constants defined at namespace scope:
/// ArbitrationDelay et al. Observers will be notified each time one of the thresholds
/// defined by those constants is crossed.
virtual void scsi_bus_did_change(Bus *, BusState new_state, double time_since_change) = 0;
};
/*!
Adds an observer.

View File

@ -142,7 +142,7 @@ template <typename Executor> class Target: public Bus::Observer, public Responde
Executor executor_;
// Bus::Observer.
void scsi_bus_did_change(Bus *, BusState new_state) final;
void scsi_bus_did_change(Bus *, BusState new_state, double time_since_change) final;
// Responder
void send_data(std::vector<uint8_t> &&data, continuation next) final;
@ -166,6 +166,12 @@ template <typename Executor> class Target: public Bus::Observer, public Responde
} phase_ = Phase::AwaitingSelection;
BusState bus_state_ = DefaultBusState;
void set_device_output(BusState state) {
expected_control_state_ = state & (Line::Control | Line::Input | Line::Message);
bus_.set_device_output(scsi_bus_device_id_, state);
}
BusState expected_control_state_ = DefaultBusState;
void begin_command(uint8_t first_byte);
std::vector<uint8_t> command_;
size_t command_pointer_ = 0;

View File

@ -13,7 +13,7 @@ template <typename Executor> Target<Executor>::Target(Bus &bus, int scsi_id) :
bus.add_observer(this);
}
template <typename Executor> void Target<Executor>::scsi_bus_did_change(Bus *, BusState new_state) {
template <typename Executor> void Target<Executor>::scsi_bus_did_change(Bus *, BusState new_state, double time_since_change) {
/*
"The target determines that it is selected when the SEL# signal
and its SCSI ID bit are active and the BSY# and I#/O signals
@ -25,10 +25,18 @@ template <typename Executor> void Target<Executor>::scsi_bus_did_change(Bus *, B
if(new_state & Line::Reset) {
phase_ = Phase::AwaitingSelection;
bus_state_ = DefaultBusState;
bus_.set_device_output(scsi_bus_device_id_, bus_state_);
set_device_output(bus_state_);
return;
}
// Check for an unexpected change of SCSI state.
/* if((phase_ > Phase::Command) && (new_state & (Line::Control | Line::Input | Line::Message)) != expected_control_state_) {
phase_ = Phase::AwaitingSelection;
bus_state_ = DefaultBusState;
set_device_output(bus_state_);
return;
}*/
switch(phase_) {
/*
While awaiting selection the SCSI target is passively watching the bus waiting for its ID
@ -44,7 +52,7 @@ template <typename Executor> void Target<Executor>::scsi_bus_did_change(Bus *, B
command_.resize(0);
command_pointer_ = 0;
bus_state_ |= Line::Busy; // Initiate the command phase: request a command byte.
bus_.set_device_output(scsi_bus_device_id_, bus_state_);
set_device_output(bus_state_);
}
break;
@ -88,7 +96,7 @@ template <typename Executor> void Target<Executor>::scsi_bus_did_change(Bus *, B
default: break;
}
bus_.set_device_output(scsi_bus_device_id_, bus_state_);
set_device_output(bus_state_);
break;
case Phase::ReceivingData:
@ -107,7 +115,7 @@ template <typename Executor> void Target<Executor>::scsi_bus_did_change(Bus *, B
bus_state_ |= Line::Request;
break;
}
bus_.set_device_output(scsi_bus_device_id_, bus_state_);
set_device_output(bus_state_);
break;
case Phase::SendingData:
@ -127,7 +135,7 @@ template <typename Executor> void Target<Executor>::scsi_bus_did_change(Bus *, B
bus_state_ = (bus_state_ & ~0xff) | data_[data_pointer_];
break;
}
bus_.set_device_output(scsi_bus_device_id_, bus_state_);
set_device_output(bus_state_);
break;
}
}
@ -198,7 +206,7 @@ template <typename Executor> void Target<Executor>::send_data(std::vector<uint8_
phase_ = Phase::SendingData;
data_ = std::move(data);
data_pointer_ = 0;
bus_.set_device_output(scsi_bus_device_id_, bus_state_);
set_device_output(bus_state_);
}
template <typename Executor> void Target<Executor>::receive_data(size_t length, continuation next) {
@ -207,7 +215,7 @@ template <typename Executor> void Target<Executor>::receive_data(size_t length,
phase_ = Phase::ReceivingData;
data_.resize(length);
data_pointer_ = 0;
bus_.set_device_output(scsi_bus_device_id_, bus_state_);
set_device_output(bus_state_);
}
template <typename Executor> void Target<Executor>::send_status(Status, continuation next) {
@ -215,7 +223,7 @@ template <typename Executor> void Target<Executor>::send_status(Status, continua
bus_state_ &= ~(Line::Control | Line::Input | Line::Message);
bus_state_ |= Line::Input | Line::Control;
phase_ = Phase::SendingStatus;
bus_.set_device_output(scsi_bus_device_id_, bus_state_);
set_device_output(bus_state_);
}
template <typename Executor> void Target<Executor>::send_message(Message, continuation next) {
@ -223,7 +231,7 @@ template <typename Executor> void Target<Executor>::send_message(Message, contin
bus_state_ &= ~(Line::Control | Line::Input | Line::Message);
bus_state_ |= Line::Message | Line::Control;
phase_ = Phase::SendingMessage;
bus_.set_device_output(scsi_bus_device_id_, bus_state_);
set_device_output(bus_state_);
}
template <typename Executor> void Target<Executor>::end_command() {
@ -232,5 +240,5 @@ template <typename Executor> void Target<Executor>::end_command() {
// Release all bus lines and return to awaiting selection.
phase_ = Phase::AwaitingSelection;
bus_state_ = DefaultBusState;
bus_.set_device_output(scsi_bus_device_id_, bus_state_);
set_device_output(bus_state_);
}