From 310c722cc09e874082a76299a9f3cf89b05b5490 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 31 Aug 2019 21:44:22 -0400 Subject: [PATCH] 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. --- Storage/MassStorage/SCSI/SCSI.cpp | 4 +- Storage/MassStorage/SCSI/SCSI.hpp | 47 ++++++++++++++++++- Storage/MassStorage/SCSI/Target.hpp | 8 +++- .../MassStorage/SCSI/TargetImplementation.hpp | 30 +++++++----- 4 files changed, 74 insertions(+), 15 deletions(-) diff --git a/Storage/MassStorage/SCSI/SCSI.cpp b/Storage/MassStorage/SCSI/SCSI.cpp index ab858c009..7490d32a0 100644 --- a/Storage/MassStorage/SCSI/SCSI.cpp +++ b/Storage/MassStorage/SCSI/SCSI.cpp @@ -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); } } diff --git a/Storage/MassStorage/SCSI/SCSI.hpp b/Storage/MassStorage/SCSI/SCSI.hpp index 9e36fd586..e602d0e5b 100644 --- a/Storage/MassStorage/SCSI/SCSI.hpp +++ b/Storage/MassStorage/SCSI/SCSI.hpp @@ -12,6 +12,9 @@ #include #include +#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. diff --git a/Storage/MassStorage/SCSI/Target.hpp b/Storage/MassStorage/SCSI/Target.hpp index 4878b657a..2b1a0aaca 100644 --- a/Storage/MassStorage/SCSI/Target.hpp +++ b/Storage/MassStorage/SCSI/Target.hpp @@ -142,7 +142,7 @@ template 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 &&data, continuation next) final; @@ -166,6 +166,12 @@ template 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 command_; size_t command_pointer_ = 0; diff --git a/Storage/MassStorage/SCSI/TargetImplementation.hpp b/Storage/MassStorage/SCSI/TargetImplementation.hpp index d5faa359c..98d1b24ad 100644 --- a/Storage/MassStorage/SCSI/TargetImplementation.hpp +++ b/Storage/MassStorage/SCSI/TargetImplementation.hpp @@ -13,7 +13,7 @@ template Target::Target(Bus &bus, int scsi_id) : bus.add_observer(this); } -template void Target::scsi_bus_did_change(Bus *, BusState new_state) { +template void Target::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 void Target::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 void Target::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 void Target::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 void Target::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 void Target::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 void Target::send_data(std::vector void Target::receive_data(size_t length, continuation next) { @@ -207,7 +215,7 @@ template void Target::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 void Target::send_status(Status, continuation next) { @@ -215,7 +223,7 @@ template void Target::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 void Target::send_message(Message, continuation next) { @@ -223,7 +231,7 @@ template void Target::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 void Target::end_command() { @@ -232,5 +240,5 @@ template void Target::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_); }