1
0
mirror of synced 2026-01-11 23:42:44 +00:00

Merge pull request #3 from kej715/master

Expand implementation of FEI protocol and make it interoperable with DtCyber, the CDC mainframe simulator
This commit is contained in:
andrastantos 2022-06-16 09:34:51 -07:00 committed by GitHub
commit bca82a2542
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 439 additions and 346 deletions

View File

@ -7,218 +7,335 @@
/////////////////////////////////////////////////////////
// IopConcentrator_c
/////////////////////////////////////////////////////////
void IopConcentrator_c::MasterClear(bool aValue) {
if (mState != State_InMasterClear && aValue) {
mLogger << setloglevel(LogLevel_IoActivity) << "Going in to master clear" << std::endl;
mState = State_InMasterClear;
void IopConcentrator_c::StartListen() {
mLogger << setloglevel(LogLevel_IoActivity) << "Listen on port " << DecPrinter(mPort) << std::endl;
try {
mListenSocket = std::make_shared<boost::asio::ip::tcp::acceptor>(mIoService, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), mPort), true);
mConnectionSocket = std::make_shared<boost::asio::ip::tcp::socket>(mIoService);
}
catch (boost::system::system_error &Ex) {
throw Generic_x() << "Failed to create listen socket: " << Ex.code().message();
}
}
void IopConcentrator_c::StartAccept() {
mLogger << setloglevel(LogLevel_IoActivity) << "Start accept on port " << DecPrinter(mPort) << std::endl;
mListenSocket->async_accept(*mConnectionSocket, boost::bind(&IopConcentrator_c::AcceptHandler, this, boost::asio::placeholders::error));
mIoService.poll();
}
void IopConcentrator_c::AcceptHandler(const boost::system::error_code& aError) {
if (aError) {
mLogger << setloglevel(LogLevel_IoActivity) << "Accept failed: " << aError.message() << std::endl;
CloseConnSocket();
return;
}
if (mState == State_InMasterClear && !aValue) {
// Falling edge on master clear: reset state...
mLogger << setloglevel(LogLevel_IoActivity) << "Comming out of master clear" << std::endl;
mState = State_ClearingBuffers;
mFromCrayData.clear();
mToCrayData.clear();
mToCrayIdx = 0;
mToCrayLast = true;
mToCrayPacketIdx = 0;
mLogger << setloglevel(LogLevel_IoActivity)
<< "Connection from " << mConnectionSocket->remote_endpoint( ).address( ).to_string( )
<< std::endl;
try {
mConnectionSocket->set_option(boost::asio::socket_base::keep_alive(true));
boost::asio::ip::tcp::no_delay option(true);
mConnectionSocket->set_option(option);
mAccepted = true;
ClearBuffers();
StartReceive();
mIoService.poll();
}
catch (boost::system::system_error &Ex) {
mLogger << setloglevel(LogLevel_IoActivity) << "Failed to start receive: " << Ex.code().message() << std::endl;
CloseConnSocket();
}
}
bool IopConcentrator_c::HasData() {
switch (mState) {
case State_InMasterClear: return false;
case State_ClearingBuffers: return false;
case State_Operational: return mToCrayData.size() > 0 && mToCrayIdx < mToCrayData.size();
default: CRAY_ASSERT(false);
void IopConcentrator_c::ClearBuffers() {
ClearReceiveBuffers();
ClearTransmitBuffers();
}
void IopConcentrator_c::ClearReceiveBuffers() {
mToCrayData.clear();
mToCrayIdx = 0;
mReceiveBuf.consume(mReceiveBuf.size());
}
void IopConcentrator_c::ClearTransmitBuffers() {
mFromCrayData.clear();
mTransmitBufIdx = 0;
}
void IopConcentrator_c::CloseConnSocket() {
if (mAccepted) {
mLogger << setloglevel(LogLevel_IoActivity) << "Close connection" << std::endl;
boost::system::error_code IgnoredError;
mConnectionSocket->close(IgnoredError);
mAccepted = false;
}
throw Generic_x("Unreachable code");
StartAccept();
}
void IopConcentrator_c::ProcessReceivedBytes() {
size_t i;
size_t size = mReceiveBuf.size();
while (size > 0) {
const uint8_t *data = boost::asio::buffer_cast<const uint8_t *>(mReceiveBuf.data());
switch (mRcvState) {
case State_RcvLcpPduLength: // process 4-byte PDU length value
if (size < 4) return;
mPduLength = (data[0] << 24)
| (data[1] << 16)
| (data[2] << 8)
| data[3];
mReceiveBuf.consume(4);
size -= 4;
mRcvState = State_RcvLcpPduContent;
mLogger << setloglevel(LogLevel_IoTrace) << "Received LCP PDU length " << DecPrinter(mPduLength) << std::endl;
break;
case State_RcvLcpPduContent: // process LCP PDU content
if (size < mPduLength) return;
mSubsegmentCount = data[4];
mLastRcvdMessageCode = data[6];
i = 0;
while (mPduLength > 0) {
CInt_t word = 0;
for (size_t n = 0; n < 8; n++) word = (word << 8) | data[i++];
mToCrayData.push_back(word);
size -= 8;
mPduLength -= 8;
}
mReceiveBuf.consume(i);
mLogger << setloglevel(LogLevel_IoTrace) << "MC: " << OctPrinter(mLastRcvdMessageCode)
<< ", NSSG: " << DecPrinter(mSubsegmentCount) << std::endl;
mLogger << setloglevel(LogLevel_IoTrace) << DecPrinter(mToCrayData.size())
<< " total upline QWords queued" << std::endl;
mRcvState = (mSubsegmentCount > 0) ? State_RcvSegPduLength : State_RcvLcpPduLength;
break;
case State_RcvSegPduLength: // process 4-byte PDU length value
if (size < 4) return;
mPduLength = (data[0] << 24)
| (data[1] << 16)
| (data[2] << 8)
| data[3];
mReceiveBuf.consume(4);
size -= 4;
mRcvState = State_RcvSegPduContent;
mLogger << setloglevel(LogLevel_IoTrace) << "Received Segment PDU length " << DecPrinter(mPduLength) << std::endl;
break;
case State_RcvSegPduContent: // process segment PDU content
if (size < mPduLength) return;
if (mLastRcvdMessageCode == McLogon) {
mSegmentSize = ((data[6] << 8) | data[7]) * 8;
mLogger << setloglevel(LogLevel_IoTrace) << "Subsegment size: "
<< DecPrinter(mSegmentSize) << " bytes / "
<< DecPrinter(mSegmentSize/8) << " QWords"
<< std::endl;
}
i = 0;
while (mPduLength > 0) {
CInt_t word = 0;
for (size_t n = 0; n < 8; n++) word = (word << 8) | data[i++];
mToCrayData.push_back(word);
size -= 8;
mPduLength -= 8;
}
mReceiveBuf.consume(i);
mLogger << setloglevel(LogLevel_IoTrace) << DecPrinter(mToCrayData.size())
<< " total upline QWords queued" << std::endl;
mSubsegmentCount -= 1;
mRcvState = (mSubsegmentCount > 0) ? State_RcvSegPduLength : State_RcvLcpPduLength;
break;
default:
mParent.GetLogger() << setloglevel(LogLevel_Error) << SideEffectIndent
<< "ERROR: Invalid receive state" << std::endl;
MasterClear();
return;
break;
}
}
}
void IopConcentrator_c::StartReceive() {
mLogger << setloglevel(LogLevel_IoActivity) << "Start receive" << std::endl;
boost::asio::async_read(*mConnectionSocket, mReceiveBuf, boost::asio::transfer_at_least(1),
boost::bind(&IopConcentrator_c::ReadHandler, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void IopConcentrator_c::ReadHandler(const boost::system::error_code& aError, std::size_t bytesTransferred) {
if (aError) {
mLogger << setloglevel(LogLevel_IoActivity) << "Read failed: " << aError.message() << std::endl;
CloseConnSocket();
return;
}
mLogger << setloglevel(LogLevel_IoTrace) << DecPrinter(bytesTransferred) << " bytes received" << std::endl;
StartReceive();
}
void IopConcentrator_c::StartTransmit() {
mTransmitBufs[mTransmitBufIdx].consume(mTransmitBufs[mTransmitBufIdx].size());
size_t wordCount = mFromCrayData.size();
size_t byteCount = wordCount * 8;
switch (mXmtState) {
case State_XmtLcpPduStart:
mTransmitBufs[mTransmitBufIdx].sputc(0);
mTransmitBufs[mTransmitBufIdx].sputc(0);
mTransmitBufs[mTransmitBufIdx].sputc(0);
mTransmitBufs[mTransmitBufIdx].sputc(BytesPerLCP);
mXmtState = State_XmtLcpPduContent;
// fall through
case State_XmtLcpPduContent:
for (size_t w = 0; w < wordCount; w++) {
CInt_t word = mFromCrayData[w];
size_t shiftCount = 56;
for (size_t b = 0; b < 8; b++) {
mTransmitBufs[mTransmitBufIdx].sputc((char)((word >> shiftCount) & 0xff));
shiftCount -= 8;
}
}
mLastXmitMessageCode = (mFromCrayData[0] >> 8) & 0xff;
mSubsegmentCount = (mFromCrayData[0] >> 24) & 0xff;
mXmtState = (mSubsegmentCount > 0) ? State_XmtSegPduStart : State_XmtLcpPduStart;
break;
case State_XmtSegPduStart:
if (mLastXmitMessageCode == McStart) {
mTransmitBufs[mTransmitBufIdx].sputc((char)((byteCount >> 24) & 0xff));
mTransmitBufs[mTransmitBufIdx].sputc((char)((byteCount >> 16) & 0xff));
mTransmitBufs[mTransmitBufIdx].sputc((char)((byteCount >> 8) & 0xff));
mTransmitBufs[mTransmitBufIdx].sputc((char)(byteCount & 0xff));
}
else {
mTransmitBufs[mTransmitBufIdx].sputc((char)((mSegmentSize >> 24) & 0xff));
mTransmitBufs[mTransmitBufIdx].sputc((char)((mSegmentSize >> 16) & 0xff));
mTransmitBufs[mTransmitBufIdx].sputc((char)((mSegmentSize >> 8) & 0xff));
mTransmitBufs[mTransmitBufIdx].sputc((char)(mSegmentSize & 0xff));
}
mBytesTransmitted = 0;
mXmtState = State_XmtSegPduContent;
// fall through
case State_XmtSegPduContent:
for (size_t w = 0; w < wordCount; w++) {
CInt_t word = mFromCrayData[w];
size_t shiftCount = 56;
for (size_t b = 0; b < 8; b++) {
mTransmitBufs[mTransmitBufIdx].sputc((char)((word >> shiftCount) & 0xff));
shiftCount -= 8;
}
}
mBytesTransmitted += byteCount;
if (mBytesTransmitted >= mSegmentSize || mLastXmitMessageCode == McStart) {
mSubsegmentCount -= 1;
mXmtState = (mSubsegmentCount > 0) ? State_XmtSegPduStart : State_XmtLcpPduStart;
}
break;
default:
mParent.GetLogger() << setloglevel(LogLevel_Error) << SideEffectIndent
<< "ERROR: Invalid transmit state" << std::endl;
MasterClear();
return;
break;
}
mFromCrayData.clear();
mLogger << setloglevel(LogLevel_IoTrace) << "Start transmitting "
<< DecPrinter(wordCount) << " QWords/" << DecPrinter(byteCount) << " bytes to front end." << std::endl;
try {
boost::asio::async_write(*mConnectionSocket, mTransmitBufs[mTransmitBufIdx],
boost::bind(&IopConcentrator_c::WriteHandler, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
mTransmitBufIdx = (mTransmitBufIdx + 1) % MaxTransmitBufs;
}
catch (boost::system::system_error &Ex) {
mLogger << setloglevel(LogLevel_IoActivity) << "Failed to start write: " << Ex.code().message() << std::endl;
CloseConnSocket();
return;
}
}
void IopConcentrator_c::WriteHandler(const boost::system::error_code& aError, std::size_t bytesTransferred) {
if (aError) {
mLogger << setloglevel(LogLevel_IoActivity) << "Write failed: " << aError.message() << std::endl;
CloseConnSocket();
return;
}
mLogger << setloglevel(LogLevel_IoTrace) << DecPrinter(bytesTransferred) << " bytes transmitted to front end." << std::endl;
}
void IopConcentrator_c::MasterClear() {
mLogger << setloglevel(LogLevel_IoActivity) << "Master clear" << std::endl;
CloseConnSocket();
ClearBuffers();
mRcvState = State_RcvLcpPduLength;
mPduLength = 0;
mXmtState = State_XmtLcpPduStart;
mBytesTransmitted = 0;
mSegmentSize = 0;
}
CInt_t IopConcentrator_c::GetData() {
CInt_t Data = 0;
if(mToCrayIdx < mToCrayData.size()) {
Data = mToCrayData[mToCrayIdx];
++mToCrayIdx;
/* if (mToCrayIdx == mToCrayData.size()) {
if (!mToCrayLast) {
SetupNextTransfer();
} else {
mToCrayData.clear();
mToCrayIdx = 0;
mToCrayLast = true;
}
}*/
mLogger << setloglevel(LogLevel_IoActivity) << "Sending data: " << HexPrinter(Data) << std::endl;
if (mToCrayIdx < mToCrayData.size()) {
Data = mToCrayData[mToCrayIdx++];
mLogger << setloglevel(LogLevel_IoActivity) << "Receive " << HexPrinter(Data) << std::endl;
if (mToCrayIdx >= mToCrayData.size()) {
mToCrayData.clear();
mToCrayIdx = 0;
}
} else {
mLogger << setloglevel(LogLevel_IoActivity) << "Sending empty data: " << HexPrinter(Data) << std::endl;
mLogger << setloglevel(LogLevel_IoActivity) << "Sending empty data " << HexPrinter(Data) << std::endl;
}
return Data;
}
void IopConcentrator_c::ProcessFromCrayLcp() {
mFromCrayLcp = FrontEndLcp_c(mFromCrayData);
mLogger << setloglevel(LogLevel_IoActivity) << "Processing CRAY message: " << OctPrinter(mFromCrayLcp.GetMessageCode()) << std::endl;
switch (mFromCrayLcp.GetMessageCode()) {
case FrontEndLcp_c::MessageCode_Restart: {
/* FrontEndLcp_c Control;
Control.SetSourceId(mFrontEndId);
Control.SetDestinationId(mMainframeId);
Control.SetNumberOfSubsegments(0);
Control.SetMessageNumber(1);
Control.SetMessageCode(FrontEndLcp_c::MessageCode_Control);
Control.SetDataBitsInSegment(0000);
mLogger << setloglevel(LogLevel_IoActivity) << "Sending reply of: " << DecPrinter(Control.GetData().size()) << " QWORDS" << std::endl;
SetToCrayData(Control.GetData(), true);*/
/* In reality we only send a logon message once a client is connected, not right after the restart. The logic is this:
- Upon power-up, restart initializes the concentrator and opens it up for network connections
- Upon connection of a client, logon is sent (this is only accepted on a fully booted system
At least this is what the logic *should be*. However if I don't send a logon, I get a timeout. If I send a control message, I get some other error...
*/
//mMainframeId = mFromCrayLcp.GetSourceId();
FrontEndLcp_c Logon;
Logon.SetSourceId(mFrontEndId);
Logon.SetDestinationId(mMainframeId);
Logon.SetNumberOfSubsegments(1);
Logon.SetMessageNumber(++mMessageNumber);
Logon.SetMessageCode(FrontEndLcp_c::MessageCode_Logon);
Logon.SetDataBitsInSegment(0200); // It is 0200 (2x64) in the documentation, thought the message actually supposed to have 6 QWORDS in it...
mLogger << setloglevel(LogLevel_IoActivity) << "Sending reply of: " << DecPrinter(Logon.GetData().size()) << " QWORDS" << std::endl;
SetToCrayData(Logon.GetData(), false);
}
break;
case FrontEndLcp_c::MessageCode_MessageError: {
std::cout << "Front-end error received: " << OctPrinter(mFromCrayLcp.GetMessageSubcode()) << " for message: " << DecPrinter(mFromCrayLcp.GetMessageNumber()) << std::endl;
mLogger << setloglevel(LogLevel_IoActivity) << "Front-end error received: " << OctPrinter(mFromCrayLcp.GetMessageSubcode()) << " for message: " << DecPrinter(mFromCrayLcp.GetMessageNumber()) << std::endl;
if (mFromCrayLcp.GetMessageSubcode() == 0251) {
std::cout << "Front-end re-sending logon..." << std::endl;
// Resend logon...
FrontEndLcp_c Logon;
Logon.SetSourceId(mFrontEndId);
Logon.SetDestinationId(mMainframeId);
Logon.SetNumberOfSubsegments(1);
Logon.SetMessageNumber(++mMessageNumber);
Logon.SetMessageCode(FrontEndLcp_c::MessageCode_Logon);
Logon.SetDataBitsInSegment(0200); // It is 0200 (2x64) in the documentation, thought the message actually supposed to have 6 QWORDS in it...
mLogger << setloglevel(LogLevel_IoActivity) << "Sending reply of: " << DecPrinter(Logon.GetData().size()) << " QWORDS" << std::endl;
SetToCrayData(Logon.GetData(), false);
}
}
break;
default: CRAY_ASSERT(false);
}
size_t IopConcentrator_c::GetDataSize() {
return mToCrayData.size() - mToCrayIdx;
}
void IopConcentrator_c::SetupNextTransfer() {
switch (mFromCrayLcp.GetMessageCode()) {
case FrontEndLcp_c::MessageCode_Restart: {
std::vector<CInt_t> Data;
Data.resize(6,0);
// STYP DBSZ CKSZ MIST MOST MAST MSSG SSGZ
Data[0] = 2ULL << 0 | 0ULL << 2 | 0ULL << 8 | 1ULL << 16 | 1ULL << 24 | 2ULL << 32 | 255ULL << 40 | 255ULL << 48;
// MRE SLOT ISLT VARS FRCE MTE DSZ
Data[1] = 1ULL << 0 | 1ULL << 8 | 1ULL << 9 | 1ULL << 10 | 0ULL << 15 | 15ULL << 16 | 0ULL << 40;
// STOP
//Data[4] = 0ULL; //TODO: This should be the password, but what is it???
mLogger << setloglevel(LogLevel_IoActivity) << "Sending data segment of : " << DecPrinter(Data.size()) << " QWORDS" << std::endl;
SetToCrayData(Data, true);
}
break;
default: CRAY_ASSERT(false);
}
bool IopConcentrator_c::HasData() {
return mToCrayIdx < mToCrayData.size();
}
bool IopConcentrator_c::IsReceptionComplete() {
return (mRcvState == State_RcvLcpPduLength || mRcvState == State_RcvSegPduLength) && HasData() == false && mPduLength <= 0;
}
void IopConcentrator_c::Poll() {
mIoService.poll();
}
void IopConcentrator_c::SetData(CInt_t aData) {
mLogger << setloglevel(LogLevel_IoActivity) << "Receiving data: " << HexPrinter(aData) << std::endl;
switch (mState) {
case State_ClearingBuffers:
if ((aData & 0xffff000000000000ULL) == 0x0000000000000000ULL) {
BufferClearComplete();
}
break;
case State_Operational:
mFromCrayData.push_back(aData);
break;
default: break;
}
mLogger << setloglevel(LogLevel_IoActivity) << "Transmit " << HexPrinter(aData) << std::endl;
mFromCrayData.push_back(aData);
}
void IopConcentrator_c::ProcessFromCrayData() {
mLogger << setloglevel(LogLevel_IoActivity) << "Processing CRAY data of " << DecPrinter(mFromCrayData.size()) << " QWORDs" << std::endl;
if (mFromCrayData.size() == FrontEndLcp_c::LcpSize) ProcessFromCrayLcp();
mFromCrayData.clear();
}
void IopConcentrator_c::TransferCompleted() {
if (!mToCrayLast) {
mLogger << setloglevel(LogLevel_IoActivity) << "Setting up next transfer" << std::endl;
SetupNextTransfer();
} else {
/* mLogger << setloglevel(LogLevel_IoActivity) << "Setting up poll reply" << std::endl;
FrontEndLcp_c PollReply;
PollReply.SetSourceId(mFrontEndId);
PollReply.SetDestinationId(mMainframeId);
PollReply.SetNumberOfSubsegments(0);
PollReply.SetMessageNumber(0);
PollReply.SetMessageCode(0);
PollReply.SetDataBitsInSegment(0);
SetToCrayData(PollReply.GetData(), true);*/
mLogger << setloglevel(LogLevel_IoActivity) << "Start transmitting " << DecPrinter(mFromCrayData.size())
<< " QWords to front end" << std::endl;
if (mAccepted) {
StartTransmit();
}
}
void IopConcentrator_c::BufferClearComplete() {
mLogger << setloglevel(LogLevel_IoActivity) << "State transition to State_Operational" << std::endl;
CRAY_ASSERT(mState == State_ClearingBuffers);
mState = State_Operational;
mFromCrayData.clear();
mToCrayData.clear();
mToCrayIdx = 0;
mToCrayLast = true;
mToCrayPacketIdx = 0;
}
void IopConcentrator_c::LogState(const char *aHeader) const {
switch (mState) {
case State_InMasterClear: mLogger << setloglevel(LogLevel_IoActivity) << aHeader << " State: State_InMasterClear" << std::endl; break;
case State_ClearingBuffers: mLogger << setloglevel(LogLevel_IoActivity) << aHeader << " State: State_ClearingBuffers" << std::endl; break;
case State_Operational: mLogger << setloglevel(LogLevel_IoActivity) << aHeader << " State: State_Operational" << std::endl; break;
default: CRAY_ASSERT(false);
else {
mFromCrayData.clear();
}
}
void IopConcentrator_c::Tick() {
/* if (mState == State_Operational) {
++mPollTimer;
if (mPollTimer == mPollDelay) {
mPollTimer = 0;
if (!HasData()) {
mLogger << setloglevel(LogLevel_IoActivity) << "Setting up poll reply" << std::endl;
FrontEndLcp_c PollReply;
PollReply.SetSourceId(mFrontEndId);
PollReply.SetDestinationId(mMainframeId);
PollReply.SetNumberOfSubsegments(0);
PollReply.SetMessageNumber(0);
PollReply.SetMessageCode(0);
PollReply.SetDataBitsInSegment(0);
SetToCrayData(PollReply.GetData(), true);
} else {
mLogger << setloglevel(LogLevel_IoActivity) << "Not setting up poll reply mToCrayData.size()=" << DecPrinter(_ToCrayData.size()) << " _ToCrayIdx=" << DecPrinter(_ToCrayIdx) << std::endl;
}
}
}*/
ProcessReceivedBytes();
if (++mPollCnt > 100) {
Poll();
mPollCnt = 0;
}
}
boost::asio::io_service IopConcentrator_c::mIoService;
/////////////////////////////////////////////////////////
// IopConcentratorChannel_c
/////////////////////////////////////////////////////////
IopConcentratorChannel_c::IopConcentratorChannel_c(class IopConcentrator_c &aParent, const Configuration_c &aConfig, size_t aChannelIdx, bool aAllowOut, bool aAllowIn):
mParent(aParent),
mAllowOut(aAllowOut),
mAllowIn(aAllowIn),
mAllowOut(aAllowOut),
mChannelIdx(aChannelIdx),
mChannelActive(false)
mParent(aParent)
{
CRAY_ASSERT(aAllowOut ^ aAllowIn);
mTransferDelay = aConfig.get<size_t>("TransferDelay", 10);
@ -226,83 +343,81 @@ IopConcentratorChannel_c::IopConcentratorChannel_c(class IopConcentrator_c &aPar
}
IopInt_t IopConcentratorChannel_c::DoIo(IopIoFunction_t aFunction, IopInt_t aData) {
mChannelActive = true; // TODO: this is not what should happen: this is set from the other side, when the destination address is written
//mParent.LogState("DoIo begin");
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << (mAllowIn?"CONCI":"CONCO") << " DoIo with function code: " << OctPrinter(aFunction) << " data: " << HexPrinter(aData) << std::endl;
switch (aFunction) {
case 000:
case 000: // Clear Channel
mInActive = false;
mOutActive = false;
mDone = false;
return 0;
case 001:
case 001: // Enter Local Memory Address, Start Transfer
mIoMemoryAddr = aData >> 2; // Clear the lowest two bits
if (mAllowIn) {
if (mParent.HasData()) {
mInActive = true;
mOutActive = false;
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << "Initiating transfer of " << DecPrinter(mTransferSize) << " QWords" << " from Concentrator to I/O memory address " << HexPrinter(mIoMemoryAddr,4) << std::endl;
mDone = false;
} else {
mInActive = false;
mOutActive = false;
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << "Data transfer request with no data pending: " << DecPrinter(mTransferSize) << " QWords" << " from Concentrator to I/O memory address " << HexPrinter(mIoMemoryAddr,4) << std::endl;
mDone = false;
}
mInActive = true;
mOutActive = false;
mParent.GetLogger() << setloglevel(LogLevel_IoActivity)
<< "Start transferring " << DecPrinter(mTransferSize) << " QWords"
<< " with " << DecPrinter(mParent.GetDataSize()) << " available"
<< " from front end to I/O memory address " << HexPrinter(mIoMemoryAddr,4)
<< std::endl;
mDone = false;
}
if (mAllowOut) {
mInActive = false;
mOutActive = true;
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << "Initiating transfer of " << DecPrinter(mTransferSize) << " QWords" << " from I/O memory address " << HexPrinter(mIoMemoryAddr,4) << " to Concentrator" << std::endl;
mParent.GetLogger() << setloglevel(LogLevel_IoActivity)
<< "Start transferring " << DecPrinter(mTransferSize) << " QWords"
<< " from I/O memory address " << HexPrinter(mIoMemoryAddr,4)
<< " to front end" << std::endl;
mDone = false;
}
mTransferDelayCounter = mTransferDelay;
return 0;
case 002:
case 002: // Enter Parcel Count
mTransferSize = aData >> 2;
return 0;
case 003:
case 003: // Clear Error Flag
// Clear parity error bits for CIA and sequence error flags for COA
return 0;
case 004:
// Would set active to false, but that's not supported yet
case 004: // (CIA) Clear Ready Waiting Flog | (COA) Set/Clear External Control Signals
if (mAllowIn) {
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << "CONCI function 4 with value: " << HexPrinter(aData,4) << std::endl;
//mChannelActive = false;
mReadyWaiting = false;
}
if (mAllowOut) {
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << "CONCO function 4 with value: " << HexPrinter(aData,4) << std::endl;
mWriteDisconnect = (aData & (1 << 8)) != 0;
mHoldDisconnect = (aData & (1 << 9)) != 0;
/* if ((aData & (1 << 4)) != 0) {
// This bit seems to be set once the buffer clear is complete
mParent.BufferClearComplete();
}*/
if ((aData & (1 << 8)) != 0) {
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << "would be sending disconnect to concentrator!" << std::endl;
if (mWriteDisconnect) {
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << "would be sending disconnect to concentrator" << std::endl;
}
mHoldDisconnect = ((aData & (1 << 9)) != 0);
mParent.MasterClear((aData & (1 << 14)) != 0);
if ((aData & (1 << 14)) != 0) mParent.MasterClear();
}
return 0;
case 006:
case 006: // Clear Interrupt Enable Flag
mInterruptEnabled = false;
return 0;
case 007:
case 007: // Set Interrupt Enable Flag
mInterruptEnabled = true;
return 0;
case 010:
case 010: // Read Local Memory Address
mParent.GetLogger() << setloglevel(LogLevel_IoActivity)
<< (mAllowIn ? "CONCI" : "CONCO")
<< " function 010 returning: " << HexPrinter(mIoMemoryAddr << 2, 4)
<< std::endl;
return mIoMemoryAddr << 2;
case 011:
if (mAllowIn) aData = mParent.HasData() ? (1 << 15) : 0; // Would return parity error bits in the low-order four bits - for now we won't signal ready waiting as it is an endless loop during initialization
case 011: // Read Ready Waiting / Error Flags
if (mAllowIn) aData = mReadyWaiting ? (1 << 15) : 0; // Would return parity error bits in the low-order four bits
if (mAllowOut) aData = 0; // Would return sequence error and 4-bit channels none of which is supported
if (mAllowIn) {
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << "CONCI function 011 returning: " << HexPrinter(aData,4) << std::endl;
}
if (mAllowOut) {
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << "CONCO function 011 returning: " << HexPrinter(aData,4) << std::endl;
}
return aData;
CRAY_ASSERT(false);
return 0;
mParent.GetLogger() << setloglevel(LogLevel_IoActivity)
<< (mAllowIn ? "CONCI" : "CONCO")
<< " function 011 returning: " << HexPrinter(aData,4)
<< std::endl;
return aData;
default:
mParent.GetLogger() << setloglevel(LogLevel_Error) << SideEffectIndent << "ERROR: Invalid function code for channel" << std::endl;
return 0;
@ -315,25 +430,17 @@ IopBit_t IopConcentratorChannel_c::GetInterrupt() {
}
void IopConcentratorChannel_c::Tick() {
/* if ((mInActive || mOutActive) && mChannelActive) {
if (mTransferDelayCounter > 0) {
--mTransferDelayCounter;
return;
}
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << "Transfer resumed" << std::endl;
}*/
if (mAllowIn) mParent.Tick(); // Forward ticks to concentrator, but only once per tick (i.e. from the input channel only)
if (mAllowIn) mParent.Tick(); // Forward ticks to concentrator from the input channel only
while ((mInActive || mOutActive) && mChannelActive) {
while (mInActive || mOutActive) {
CRAY_ASSERT(!mDone);
if (mOutActive) {
CInt_t Data = SwapBytesInWords(mParent.GetParent().IoMemAccessByType<uint64_t>(mIoMemoryAddr));
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << "Sending data " << HexPrinter(Data) << " to concentrator!" << std::endl;
mParent.SetData(SwapBytes(Data));
}
if (mInActive) {
if (mParent.HasData() == false) return;
CInt_t Data = SwapBytes(mParent.GetData());
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << "Receiving data " << HexPrinter(Data) << " from concentrator!" << std::endl;
mParent.GetParent().IoMemAccessByType<uint64_t>(mIoMemoryAddr) = SwapBytesInWords(Data);
}
if (mIoMemoryAddr != 0x3fff) {
@ -344,16 +451,15 @@ void IopConcentratorChannel_c::Tick() {
// We're exploiting the fact that this can underflow.
mTransferSize = (mTransferSize - 1) & 0x3fff;
if (mTransferSize == 0 || (mInActive && !mParent.HasData())) {
if (mTransferSize == 0 || (mAllowIn && mParent.IsReceptionComplete())) {
if (!mHoldDisconnect && mOutActive) {
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << "would be sending disconnect to concentrator!" << std::endl;
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << "would be sending disconnect to concentrator" << std::endl;
}
mInActive = false;
mOutActive = false;
mDone = true;
mParent.GetLogger() << setloglevel(LogLevel_IoActivity) << "Transfer completed" << std::endl;
if (mAllowOut) mParent.ProcessFromCrayData();
if (mAllowIn) mParent.TransferCompleted();
}
}
}

View File

@ -24,7 +24,17 @@ public:
virtual IopBit_t GetInterrupt() override;
virtual size_t GetChannelIdx() const override { return mChannelIdx; }
virtual void MasterClear() override { mDone = false; mInActive = false; mOutActive = false; mInterruptEnabled = true; mIoMemoryAddr = 0; mTransferSize = 0; mChannelActive = false; mHoldDisconnect = false; }
virtual void MasterClear() override {
mDone = false;
mHoldDisconnect = false;
mInActive = false;
mInterruptEnabled = true;
mIoMemoryAddr = 0;
mOutActive = false;
mReadyWaiting = false;
mTransferSize = 0;
mWriteDisconnect = false;
}
virtual void Tick() override;
virtual bool NeedsTick() override { return true; }
virtual void Dump(size_t aIdent=0) override {}
@ -35,103 +45,66 @@ protected:
bool mAllowIn;
bool mAllowOut;
bool mDone;
bool mInActive;
bool mOutActive;
bool mInterruptEnabled;
bool mChannelActive;
bool mHoldDisconnect;
size_t mTransferDelayCounter;
size_t mTransferDelay;
size_t mChannelIdx;
bool mDone;
bool mHoldDisconnect;
bool mInActive;
bool mInterruptEnabled;
bool mOutActive;
bool mReadyWaiting;
size_t mTransferDelay;
size_t mTransferDelayCounter;
bool mWriteDisconnect;
};
class FrontEndLcp_c {
public:
static const size_t LcpSize = 6;
FrontEndLcp_c() {
mData.resize(6,0);
}
explicit FrontEndLcp_c(const std::vector<CInt_t> &aData): mData(aData) {
CRAY_ASSERT(aData.size() == LcpSize);
}
std::vector<CInt_t> &GetData() { return mData; }
const std::vector<CInt_t> &GetData() const { return mData; }
#ifdef CREATE_FIELD
#error CREATE_FIELD is alreadt defined
#endif
#define CREATE_FIELD(aType,aName,aEntry,aStartBit,aEndBit) \
aType Get##aName() { return (aType)GetBits(mData[aEntry], 63-aStartBit, 63-aEndBit); } \
void Set##aName(aType aValue) { mData[aEntry] = SetBits(mData[aEntry], 63-aStartBit, 63-aEndBit, aValue); }
CREATE_FIELD(uint16_t, DestinationId, 0, 0, 15)
CREATE_FIELD(uint16_t, SourceId, 0, 16, 31)
CREATE_FIELD(uint8_t, NumberOfSubsegments, 0, 32, 39)
CREATE_FIELD(uint8_t, MessageNumber, 0, 40, 47)
CREATE_FIELD(uint8_t, MessageCode, 0, 48, 55)
CREATE_FIELD(uint8_t, MessageSubcode, 0, 56, 63)
CREATE_FIELD(uint8_t, RequestPending, 1, 0, 0)
CREATE_FIELD(uint8_t, StreamNumber, 1, 4, 7)
CREATE_FIELD(uint32_t, SegmentNumber, 1, 8, 31)
CREATE_FIELD(uint32_t, DataBitsInSegment, 1, 32, 63)
#undef CREATE_FIELD
uint8_t GetInputStreamCtrlByte(uint8_t aStrmIdx) { return (uint8_t)GetBits(mData[3], aStrmIdx*8, aStrmIdx*8+7); }
void SetInputStreamCtrlByte(uint8_t aStrmIdx, uint8_t aValue) { SetBits(mData[3], aStrmIdx*8, aStrmIdx*8+7, aValue); }
uint8_t GetOutputStreamCtrlByte(uint8_t aStrmIdx) { return (uint8_t)GetBits(mData[4], aStrmIdx*8, aStrmIdx*8+7); }
void SetOutputStreamCtrlByte(uint8_t aStrmIdx, uint8_t aValue) { SetBits(mData[4], aStrmIdx*8, aStrmIdx*8+7, aValue); }
//TODO: This list is highly incomplete. I'll have to finish it up if/when we implement a full front-end interface
enum MessageCodes_e {
MessageCode_Logon = 0001,
MessageCode_Logoff = 0003,
MessageCode_Start = 0004,
MessageCode_Restart = 0005,
MessageCode_DatasetHeader = 0006,
MessageCode_DatasetSegment = 0007,
MessageCode_Control = 0011,
MessageCode_MessageError = 0012,
MessageCode_DatasetTransferRequest = 0013,
MessageCode_DatasetTransferReply = 0014,
MessageCode_EnterLogfileRequest = 0015,
MessageCode_EnterLogfileReply = 0016
};
protected:
std::vector<CInt_t> mData;
};
#define BytesPerLCP 48
#define MaxTransmitBufs 16
#define WordsPerLCP 6
/*
* Key message codes
*/
#define McLogon 001
#define McStart 004
#define McControl 011
class IopConcentrator_c: public IopPeripheral_i {
public:
enum RcvStates_e {
State_RcvLcpPduLength,
State_RcvLcpPduContent,
State_RcvSegPduLength,
State_RcvSegPduContent
};
enum XmtStates_e {
State_XmtLcpPduStart,
State_XmtLcpPduContent,
State_XmtSegPduStart,
State_XmtSegPduContent
};
explicit IopConcentrator_c(const Configuration_c &aConfig, IopCpu_c &aParent):
mParent(aParent),
mLogger(aConfig, "CONC"),
mAccepted(false),
mBytesTransmitted(0),
mConnectionSocket(nullptr),
mInputChannel(*this,aConfig,aConfig.get<size_t>("InputChannelIdx"),false,true),
mLastRcvdMessageCode(0),
mLastXmitMessageCode(0),
mListenSocket(nullptr),
mLogger(aConfig, "CONC"),
mOutputChannel(*this,aConfig,aConfig.get<size_t>("OutputChannelIdx"),true,false),
mState(State_InMasterClear),
mPollDelay(aConfig.get<uint32_t>("PollDelay", 10)),
mPollTimer(0),
mMainframeId(0),
mParent(aParent),
mPduLength(0),
mPort(aConfig.get<uint16_t>("Port",9000)),
mRcvState(State_RcvLcpPduLength),
mSegmentSize(0),
mToCrayIdx(0),
mToCrayLast(true),
mToCrayPacketIdx(0),
mMessageNumber(0)
mTransmitBufIdx(0),
mXmtState(State_XmtLcpPduStart)
{
std::string SidStr =aConfig.get<std::string>("SID", "FE");
CRAY_ASSERT(SidStr.length() == 2);
mFrontEndId = ((uint16_t)SidStr[0]) << 8 | ((uint16_t)SidStr[1]) << 0;
mMainframeId = 0x4331; // This is 'C1' but how should he concentrator know about it? It's not the SID of the RESET request...
mLogger.SetParent(mParent.GetLogger());
MasterClear(false);
StartListen();
MasterClear();
}
void MasterClear(bool aValue);
CLogger_c &GetLogger() { return mLogger; }
bool HasData();
CInt_t GetData();
void SetData(CInt_t aData);
void ProcessFromCrayData();
void TransferCompleted();
void ClearBuffers();
virtual const IopChannel_i &GetChannel(size_t aIdx) const override {
switch (aIdx) {
case 0: return mInputChannel;
@ -141,44 +114,58 @@ public:
throw Generic_x("Unreachable code");
}
virtual size_t GetChannelCnt() const override { return 2; }
IopCpu_c &GetParent() { return mParent; }
void BufferClearComplete();
enum States_e {
State_InMasterClear,
State_ClearingBuffers,
State_Operational
};
States_e GetState() const { return mState; }
void LogState(const char *aHeader) const;
void Tick();
virtual PeripheralType_e GetType() const override { return PeripheralType_e::Other; }
virtual void GetStatus(StatusReport_c &aStatus, PeripheralType_e aFilter, bool aLongFormat) const override {}
virtual void RegisterCommands(CommandHooks_t &aHooks)override {}
CInt_t GetData();
size_t GetDataSize();
CLogger_c &GetLogger() { return mLogger; }
virtual std::string GetName() const override { return "CONC"; }
IopCpu_c &GetParent() { return mParent; }
virtual void GetStatus(StatusReport_c &aStatus, PeripheralType_e aFilter, bool aLongFormat) const override {}
virtual PeripheralType_e GetType() const override { return PeripheralType_e::Other; }
bool HasData();
bool IsReceptionComplete();
void MasterClear();
void ProcessFromCrayData();
virtual void RegisterCommands(CommandHooks_t &aHooks)override {}
void SetData(CInt_t aData);
void Tick();
protected:
void ProcessFromCrayLcp();
void SetupNextTransfer();
void SetToCrayData(std::vector<CInt_t> &aData, bool aIsLast, size_t aPacketIdx = 0) {
mToCrayData = aData; mToCrayIdx = 0; mToCrayLast = aIsLast; mToCrayPacketIdx = aPacketIdx;
mLogger << setloglevel(LogLevel_IoActivity) << "Setting up data tansfer mToCrayData.size()=" << DecPrinter(mToCrayData.size()) << " mToCrayLast=" << (mToCrayLast?"true":"false") << std::endl;
}
void AcceptHandler(const boost::system::error_code& aError);
void ClearReceiveBuffers();
void ClearTransmitBuffers();
void CloseConnSocket();
void Poll();
void ProcessReceivedBytes();
void ReadHandler(const boost::system::error_code& aError, std::size_t bytesTransferred);
void StartAccept();
void StartListen();
void StartReceive();
void StartTransmit();
void WriteHandler(const boost::system::error_code& aError, std::size_t bytesTransferred);
bool mAccepted;
size_t mBytesTransmitted;
std::shared_ptr<boost::asio::ip::tcp::socket> mConnectionSocket;
std::vector<CInt_t> mFromCrayData;
FrontEndLcp_c mFromCrayLcp;
IopConcentratorChannel_c mInputChannel;
static boost::asio::io_service mIoService;
uint8_t mLastRcvdMessageCode;
uint8_t mLastXmitMessageCode;
std::shared_ptr<boost::asio::ip::tcp::acceptor> mListenSocket;
mutable CLogger_c mLogger;
IopConcentratorChannel_c mOutputChannel;
IopCpu_c &mParent;
size_t mPduLength;
size_t mPollCnt;
uint16_t mPort;
boost::asio::streambuf mReceiveBuf;
RcvStates_e mRcvState;
size_t mSegmentSize;
size_t mSubsegmentCount;
std::vector<CInt_t> mToCrayData;
size_t mToCrayIdx;
bool mToCrayLast;
size_t mToCrayPacketIdx;
uint32_t mPollDelay;
uint32_t mPollTimer;
IopCpu_c &mParent;
IopConcentratorChannel_c mInputChannel;
IopConcentratorChannel_c mOutputChannel;
mutable CLogger_c mLogger;
States_e mState;
uint16_t mFrontEndId;
uint16_t mMainframeId;
uint8_t mMessageNumber;
size_t mTransmitBufIdx;
boost::asio::streambuf mTransmitBufs[MaxTransmitBufs];
XmtStates_e mXmtState;
};
#endif // __IOP_CONCENTRATOR_H__