diff --git a/10.01_base/2_src/arm/unibusadapter.cpp b/10.01_base/2_src/arm/unibusadapter.cpp index d97314e..fe136de 100644 --- a/10.01_base/2_src/arm/unibusadapter.cpp +++ b/10.01_base/2_src/arm/unibusadapter.cpp @@ -50,6 +50,7 @@ #include #include #include +#include // TEST //#include // sleep() @@ -66,11 +67,58 @@ using namespace std; #include "iopageregister.h" #include "unibusadapter.hpp" +dma_request_c::dma_request_c( + uint8_t unibus_control, + uint32_t unibus_addr, + uint16_t* buffer, + uint32_t wordcount) : + _unibus_control(unibus_control), + _unibus_addr(unibus_addr), + _unibus_end_addr(0), + _buffer(buffer), + _wordcount(wordcount), + _isComplete(false), + _success(false) +{ + +} + +dma_request_c::~dma_request_c() +{ + +} + +irq_request_c::irq_request_c( + unsigned level, + unsigned vector) : + _level(level), + _vector(vector) +{ + +} + +irq_request_c::~irq_request_c() +{ + +} + +void* bus_worker( + void *context) +{ + unibusadapter_c* bus = reinterpret_cast(context); + bus->dma_worker(); + return nullptr; +} + unibusadapter_c *unibusadapter; // another Singleton // is registered in device_c.list ... order of static constructor calls ??? unibusadapter_c::unibusadapter_c() : - device_c() { + device_c(), + _busWakeup_cond(PTHREAD_COND_INITIALIZER), + _requestFinished_cond(PTHREAD_COND_INITIALIZER), + _busWorker_mutex(PTHREAD_MUTEX_INITIALIZER) + { unsigned i; log_label = "UNAPT"; @@ -82,14 +130,33 @@ unibusadapter_c::unibusadapter_c() : line_INIT = false; line_DCLO = false; + // + // Start bus worker thread + // + pthread_attr_t attribs; + pthread_attr_init(&attribs); + + int status = pthread_create( + &_busWorker_pthread, + &attribs, + &bus_worker, + reinterpret_cast(this)); + + if (status != 0) + { + FATAL("Failed to start unibus worker thread. Status 0x%x", status); + } } + bool unibusadapter_c::on_param_changed(parameter_c *param) { UNUSED(param); return true ; } -void unibusadapter_c::on_power_changed(void) { +void unibusadapter_c::on_power_changed(void) +{ + } void unibusadapter_c::on_init_changed(void) { @@ -106,6 +173,14 @@ void unibusadapter_c::worker_init_event() { device->init_asserted = line_INIT; device->on_init_changed(); } + + // Clear bus request queues + /* + pthread_mutex_lock(&_busWorker_mutex); + _dmaRequests.clear(); + _irqRequests.clear(); + pthread_mutex_unlock(&_busWorker_mutex); + */ } void unibusadapter_c::worker_power_event() { @@ -118,6 +193,14 @@ void unibusadapter_c::worker_power_event() { device->power_down = line_DCLO; device->on_power_changed(); } + + // Clear bus request queues + /* + pthread_mutex_lock(&_busWorker_mutex); + _dmaRequests.clear(); + _irqRequests.clear(); + pthread_mutex_unlock(&_busWorker_mutex); + */ } // process DATI/DATO access to active device registers @@ -463,111 +546,213 @@ bool unibusadapter_c::request_INTR_active(const char *error_info) { return false; } -// request a DMA cycle. +// Invoke a DMA transfer. // unibus_control = UNIBUS_CONTROL_DATI or _DATO -bool unibusadapter_c::request_DMA(unibusdevice_c *device, uint8_t unibus_control, - uint32_t unibus_addr, uint16_t *buffer, unsigned wordcount) { -// TODO: if another DMA or INTR is active: put request in queue - UNUSED(device); - if (request_DMA_active(__func__) || request_INTR_active(__func__)) - return false; +bool unibusadapter_c::request_DMA( + uint8_t unibus_control, + uint32_t unibus_addr, + uint16_t *buffer, + uint32_t wordcount) { - mailbox->dma.startaddr = unibus_addr; - mailbox->dma.control = unibus_control; - mailbox->dma.wordcount = wordcount; + // + // Acquire bus mutex; append new request to queue. + // bus worker will wake and service the request in due time. + // + dma_request_c request( + unibus_control, + unibus_addr, + buffer, + wordcount); - // save params of current transaction - cur_DMA_unibus_control = unibus_control; - cur_DMA_buffer = buffer; - cur_DMA_wordcount = wordcount; + pthread_mutex_lock(&_busWorker_mutex); + _dmaRequests.push(&request); + pthread_cond_signal(&_busWakeup_cond); + pthread_mutex_unlock(&_busWorker_mutex); - if (unibus_control == UNIBUS_CONTROL_DATO) { - // copy data into mailbox->DMA buffer - memcpy((void*) mailbox->dma.words, buffer, 2 * wordcount); - } DEBUG("DMA start: %s @ %06o, len=%d", unibus->control2text(unibus_control), unibus_addr, wordcount); - // start! - mailbox->arm2pru_req = ARM2PRU_DMA; - // PRU now changes state - return true; -} - -void unibusadapter_c::request_INTR(unibusdevice_c *device, unsigned level, unsigned vector) { -// TODO: if another DMA or INTR is active: put request in queue - UNUSED(device); - - // it is not an error if the INTR (at same level) is still pending - // a device may re-raise its interrupt, an interrupt may remain pending for years. - if (request_DMA_active(__func__)) - return; - if (request_INTR_active(NULL)) - return; - - switch (level) { - case 4: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B4; - break; - case 5: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B5; - break; - case 6: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B6; - break; - case 7: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B7; - break; - default: - ERROR("Request_INTR(): Illegal priority %u, aborting", level); - return; + // + // Wait for request to finish. + // + pthread_mutex_lock(&_busWorker_mutex); + while (!request.IsComplete()) + { + pthread_cond_wait(&_requestFinished_cond, &_busWorker_mutex); } - mailbox->intr.vector = vector; - - // start! - mailbox->arm2pru_req = ARM2PRU_INTR; - // PRU now changes state + pthread_mutex_unlock(&_busWorker_mutex); + return request.GetSuccess(); } -// device wants to know state of its requests -// also checks for completion if the single current DMA or INTR. -// to be called by device.worker() -// result: false = not yet finished, true = complete, -// error: return NXM status -bool unibusadapter_c::complete_DMA(unibusdevice_c *device, uint32_t *unibus_end_addr, -bool *error) { -// TODO: access correct request in queue - UNUSED(device); +void unibusadapter_c::dma_worker() +{ - // rely on RL11 to check for completion and sorting DMA/INTR requests. - if (request_DMA_active(NULL)) - return false; + while(true) + { + dma_request_c* dmaReq = nullptr; + irq_request_c irqReq(0,0); + + // + // Wait for the next request. + // + pthread_mutex_lock(&_busWorker_mutex); + while(_dmaRequests.empty() && _irqRequests.empty()) + { + pthread_cond_wait( + &_busWakeup_cond, + &_busWorker_mutex); + } - if (cur_DMA_unibus_control == UNIBUS_CONTROL_DATI) { - // data were read - // copy result cur_DMA_wordcount from mailbox->DMA bufuffer to cur_DMA_buffer - memcpy(cur_DMA_buffer, (void *) mailbox->dma.words, 2 * cur_DMA_wordcount); + // + // We have a request: prioritize IRQ over DMA, dequeue from the requisite + // queue and get to work. + // + if (!_irqRequests.empty()) + { + irq_request_c const& req = _irqRequests.front(); + irqReq = req; + _irqRequests.pop(); + } + else + { + dmaReq = _dmaRequests.front(); + _dmaRequests.pop(); + } + pthread_mutex_unlock(&_busWorker_mutex); + + + // Sanity check: Should be no active DMA requests on the PRU. + assert (!request_DMA_active(nullptr)); + + // If there's an IRQ still active, wait for it to finish. + // TODO: find a way to avoid having to do this. + timeout_c timer; + while (request_INTR_active(nullptr)) + { + timer.wait_us(50); + } + + if (dmaReq) + { + // We do the DMA transfer in chunks so we can handle arbitrary buffer sizes. + // (the PRU mailbox has limited space available.) + // Configure the DMA transfer. + + uint32_t maxTransferSize = 512; + + uint32_t wordCount = dmaReq->GetWordCount(); + uint32_t unibusAddr = dmaReq->GetUnibusAddr(); + uint32_t bufferOffset = 0; + + while (wordCount > 0) + { + uint32_t chunkSize = std::min(maxTransferSize, wordCount); + + mailbox->dma.startaddr = unibusAddr + bufferOffset * 2; + mailbox->dma.control = dmaReq->GetUnibusControl(); + mailbox->dma.wordcount = chunkSize; + + // Copy outgoing data into maibox DMA buffer + if (dmaReq->GetUnibusControl() == UNIBUS_CONTROL_DATO) + { + memcpy( + (void*)mailbox->dma.words, + dmaReq->GetBuffer() + bufferOffset, + 2 * chunkSize); + } + + // + // Start the PRU: + mailbox->arm2pru_req = ARM2PRU_DMA; + + // + // Wait for the transfer to complete. + // TODO: we're polling the mailbox; is there a more efficient way to do this? + timeout_c timeout; + while (request_DMA_active(NULL)) + { + timeout.wait_us(50); + } + + if (dmaReq->GetUnibusControl() == UNIBUS_CONTROL_DATI) + { + // Copy data read from mailbox to user's buffer. + memcpy( + dmaReq->GetBuffer() + bufferOffset, + (void *)mailbox->dma.words, + 2 * chunkSize); + } + + wordCount -= chunkSize; + bufferOffset += chunkSize; + } + + dmaReq->SetUnibusEndAddr(mailbox->dma.cur_addr); + dmaReq->SetSuccess(mailbox->dma.cur_status == DMA_STATE_READY); + + assert(dmaReq->GetUnibusAddr() + dmaReq->GetWordCount() * 2 == mailbox->dma.cur_addr + 2); + + // + // Signal that the request is complete. + // + pthread_mutex_lock(&_busWorker_mutex); + dmaReq->SetComplete(); + pthread_cond_signal(&_requestFinished_cond); + pthread_mutex_unlock(&_busWorker_mutex); + } + else + { + // Handle interrupt request + switch(irqReq.GetInterruptLevel()) + { + case 4: + mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B4; + break; + + case 5: + mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B5; + break; + + case 6: + mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B6; + break; + + case 7: + mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B7; + break; + + default: + ERROR("Request_INTR(): Illegal priority %u, aborting", irqReq.GetInterruptLevel()); + return; + } + + mailbox->intr.vector = irqReq.GetVector(); + + // start! + mailbox->arm2pru_req = ARM2PRU_INTR; + // PRU now changes state + } } - - *unibus_end_addr = mailbox->dma.cur_addr; - - *error = mailbox->dma.cur_status != DMA_STATE_READY; - DEBUG("DMA ready: %s @ %06o..%06o, wordcount %d, data=%06o, %06o, ... %s", - unibus->control2text(mailbox->dma.control), mailbox->dma.startaddr, - mailbox->dma.cur_addr, mailbox->dma.wordcount, mailbox->dma.words[0], - mailbox->dma.words[1], *error ? "TIMEOUT" : "OK"); - - return true; } -// result: false = not yet finished, true = complete, -bool unibusadapter_c::complete_INTR(unibusdevice_c *device) { -// TODO: access correct request in queue - UNUSED(device); +void unibusadapter_c::request_INTR(uint32_t level, uint32_t vector) { + // + // Acquire bus mutex; append new request to queue. + // bus worker will wake and service the request in due time. + // + irq_request_c request( + level, + vector); - // rely on RL11 to check for completion and sorting DMA/INTR requests. - return request_INTR_active(NULL); + pthread_mutex_lock(&_busWorker_mutex); + _irqRequests.push(request); + pthread_cond_signal(&_busWakeup_cond); + pthread_mutex_unlock(&_busWorker_mutex); + + // + // And we're done. + // } // debugging: print PRU sharead regsster map diff --git a/10.01_base/2_src/arm/unibusadapter.hpp b/10.01_base/2_src/arm/unibusadapter.hpp index 674adab..5de5821 100644 --- a/10.01_base/2_src/arm/unibusadapter.hpp +++ b/10.01_base/2_src/arm/unibusadapter.hpp @@ -28,17 +28,67 @@ #define _UNIBUSADAPTER_HPP_ #include +#include #include "iopageregister.h" #include "unibusdevice.hpp" +class dma_request_c +{ +public: + dma_request_c( + uint8_t unibus_control, + uint32_t unibus_addr, + uint16_t *buffer, + uint32_t wordcount); + + ~dma_request_c(); + + uint8_t GetUnibusControl() { return _unibus_control; } + uint32_t GetUnibusAddr() { return _unibus_addr; } + uint16_t* GetBuffer() { return _buffer; } + uint32_t GetWordCount() { return _wordcount; } + uint32_t GetUnibusEndAddr() { return _unibus_end_addr; } + void SetUnibusEndAddr(uint32_t end) { _unibus_end_addr = end; } + + bool IsComplete() { return _isComplete; } + bool GetSuccess() { return _success; } + + void SetComplete() { _isComplete = true; } + void SetSuccess(bool success) { _success = success; } + +private: + + uint8_t _unibus_control; + uint32_t _unibus_addr; + uint32_t _unibus_end_addr; + uint16_t* _buffer; + uint32_t _wordcount; + + bool _isComplete; + bool _success; +}; + +class irq_request_c +{ +public: + irq_request_c( + uint32_t level, + uint32_t vector); + + ~irq_request_c(); + + uint32_t GetInterruptLevel() { return _level; } + uint32_t GetVector() { return _vector; } + +private: + uint32_t _level; + uint32_t _vector; +}; + + // is a device_c. need a thread (but no params) class unibusadapter_c: public device_c { -private: - // save params of current DMA transaction - volatile uint8_t cur_DMA_unibus_control; // DATI? DATO? - uint16_t *cur_DMA_buffer; - volatile unsigned cur_DMA_wordcount; public: unibusadapter_c(); @@ -59,21 +109,28 @@ public: void worker_power_event(void) ; void worker_deviceregister_event(void) ; void worker(void) override; // background worker function - + void dma_worker(void); // background DMA worker + bool register_device(unibusdevice_c& device); void unregister_device(unibusdevice_c& device); bool request_DMA_active(const char *error_info) ; bool request_INTR_active(const char *error_info) ; - bool request_DMA(unibusdevice_c *device, uint8_t unibus_control, uint32_t unibus_addr, - uint16_t *buffer, unsigned wordcount); - void request_INTR(unibusdevice_c *device, unsigned level, unsigned vector); - bool complete_DMA(unibusdevice_c *device, uint32_t *unibus_end_addr, bool *error); - bool complete_INTR(unibusdevice_c *device); + bool request_DMA(uint8_t unibus_control, uint32_t unibus_addr, + uint16_t *buffer, uint32_t wordcount); + void request_INTR(uint32_t level, uint32_t vector); void print_shared_register_map(void); +private: + + std::queue _dmaRequests; + std::queue _irqRequests; + pthread_t _busWorker_pthread; + pthread_cond_t _busWakeup_cond; + pthread_cond_t _requestFinished_cond; + pthread_mutex_t _busWorker_mutex; }; extern unibusadapter_c *unibusadapter; // another Singleton diff --git a/10.01_base/2_src/arm/unibusdevice.cpp b/10.01_base/2_src/arm/unibusdevice.cpp index 9083323..8c1ab9f 100644 --- a/10.01_base/2_src/arm/unibusdevice.cpp +++ b/10.01_base/2_src/arm/unibusdevice.cpp @@ -135,7 +135,7 @@ void unibusdevice_c::reset_unibus_registers() { // set an UNIBUS interrupt condition with intr_vector and intr_level void unibusdevice_c::interrupt(void) { // delegate to unibusadapter_c - unibusadapter->request_INTR(this, intr_level.value, intr_vector.value); + unibusadapter->request_INTR(intr_level.value, intr_vector.value); // WARNING("unibusdevice_c::interrupt() TODO: generated interrupt!"); } diff --git a/10.02_devices/2_src/mscp_server.cpp b/10.02_devices/2_src/mscp_server.cpp index dca257a..3f2a506 100644 --- a/10.02_devices/2_src/mscp_server.cpp +++ b/10.02_devices/2_src/mscp_server.cpp @@ -122,7 +122,7 @@ mscp_server::Poll(void) pthread_mutex_unlock(&polling_mutex); - //timer.wait_us(100); + // timer.wait_us(100); if (_abort_polling) @@ -156,7 +156,8 @@ mscp_server::Poll(void) ControlMessageHeader* header = reinterpret_cast(message->Message); - DEBUG("Message opcode 0x%x rsvd 0x%x mod 0x%x unit %d, ursvd 0x%x, ref 0x%x", + DEBUG("Message size 0x%x opcode 0x%x rsvd 0x%x mod 0x%x unit %d, ursvd 0x%x, ref 0x%x", + message->MessageLength, header->Word3.Command.Opcode, header->Word3.Command.Reserved, header->Word3.Command.Modifiers, @@ -240,7 +241,7 @@ mscp_server::Poll(void) message->Word1.Info.Credits = 0; } - timer.wait_us(250); + //timer.wait_us(250); // // Post the response to the port's response ring. @@ -251,7 +252,7 @@ mscp_server::Poll(void) } // Hack: give interrupts time to settle before doing another transfer. - timer.wait_us(2500); + //timer.wait_us(2500); // // Go around and pick up the next one. @@ -295,28 +296,32 @@ mscp_server::GetUnitStatus( uint32_t Reserved0; uint64_t UnitIdentifier; uint32_t MediaTypeIdentifier; - uint16_t Reserved1; uint16_t ShadowUnit; - uint16_t GroupSize; + uint16_t Reserved1; uint16_t TrackSize; - uint16_t Reserved2; + uint16_t GroupSize; uint16_t CylinderSize; + uint16_t Reserved2; uint32_t RCTStuff; }; #pragma pack(pop) + INFO("MSCP GET UNIT STATUS drive %d", unitNumber); + + // Adjust message length for response + message->MessageLength = sizeof(GetUnitStatusResponseParameters) + + HEADER_SIZE; + + mscp_drive_c* drive = GetDrive(unitNumber); if (nullptr == drive || !drive->IsAvailable()) { + INFO("Returning UNIT OFFLINE"); return STATUS(Status::UNIT_OFFLINE, 3); // unknown -- todo move to enum } - // Adjust message length for response - message->MessageLength = sizeof(GetUnitStatusResponseParameters) + - HEADER_SIZE; - GetUnitStatusResponseParameters* params = reinterpret_cast( GetParameterPointer(message)); @@ -333,8 +338,8 @@ mscp_server::GetUnitStatus( // or cylinders to speak of (no seek times, etc.) // params->TrackSize = 1; // one block per track, per aa-l619a-tk. - params->GroupSize = 0; - params->CylinderSize = 0; + params->GroupSize = 1; + params->CylinderSize = 1; // // Since we do no bad block replacement (no bad blocks possible in a disk image file) @@ -343,7 +348,7 @@ mscp_server::GetUnitStatus( // the RCT are present. // params->RCTStuff = 0x01000001; - + if (drive->IsOnline()) { return STATUS(Status::SUCCESS, 0); @@ -399,20 +404,25 @@ mscp_server::Online( }; #pragma pack(pop) + INFO("MSCP ONLINE drive %d", unitNumber); + + // Adjust message length for response + message->MessageLength = sizeof(OnlineResponseParameters) + + HEADER_SIZE; + mscp_drive_c* drive = GetDrive(unitNumber); if (nullptr == drive || !drive->IsAvailable()) { + INFO("Returning UNIT OFFLINE"); return STATUS(Status::UNIT_OFFLINE, 3); // unknown -- todo move to enum } - + + bool alreadyOnline = drive->IsOnline(); + drive->SetOnline(); - // Adjust message length for response - message->MessageLength = sizeof(OnlineResponseParameters) + - HEADER_SIZE; - OnlineResponseParameters* params = reinterpret_cast( GetParameterPointer(message)); @@ -422,9 +432,12 @@ mscp_server::Online( params->UnitIdentifier = drive->GetUnitID(); params->MediaTypeIdentifier = drive->GetMediaID(); params->UnitSize = drive->GetBlockCount(); - params->VolumeSerialNumber = 0; // We report no serial - - return STATUS(Status::SUCCESS, 0); // TODO: subcode "Already Online" + params->VolumeSerialNumber = 1; // We report no serial + params->Reserved0 = 0; + params->Reserved1 = 0; + + return STATUS(Status::SUCCESS | + (alreadyOnline ? SuccessSubcodes::ALREADY_ONLINE : SuccessSubcodes::NORMAL), 0); } uint32_t @@ -446,6 +459,11 @@ mscp_server::SetControllerCharacteristics( reinterpret_cast( GetParameterPointer(message)); + INFO("MSCP SET CONTROLLER CHARACTERISTICS"); + + // Adjust message length for response + message->MessageLength = sizeof(SetControllerCharacteristicsParameters) + + HEADER_SIZE; // // Check the version, if non-zero we must return an Invalid Command // end message. @@ -494,14 +512,7 @@ mscp_server::SetUnitCharacteristics( // TODO: handle Set Write Protect modifier - mscp_drive_c* drive = GetDrive(unitNumber); - - // Check unit - if (nullptr == drive || - !drive->IsAvailable()) - { - return STATUS(Status::UNIT_OFFLINE, 3); - } + INFO("MSCP SET UNIT CHARACTERISTICS drive %d", unitNumber); // TODO: mostly same as Online command: should share logic. #pragma pack(push,1) @@ -523,6 +534,15 @@ mscp_server::SetUnitCharacteristics( message->MessageLength = sizeof(SetUnitCharacteristicsResponseParameters) + HEADER_SIZE; + mscp_drive_c* drive = GetDrive(unitNumber); + // Check unit + if (nullptr == drive || + !drive->IsAvailable()) + { + INFO("Returning UNIT OFFLINE"); + return STATUS(Status::UNIT_OFFLINE, 3); + } + SetUnitCharacteristicsResponseParameters* params = reinterpret_cast( GetParameterPointer(message)); @@ -558,12 +578,17 @@ mscp_server::Read( ReadParameters* params = reinterpret_cast(GetParameterPointer(message)); - DEBUG("MSCP READ unit %d pa o%o count %d lbn %d", + DEBUG("MSCP READ unit %d chan o%o pa o%o count %d lbn %d", unitNumber, + params->BufferPhysicalAddress >> 24, params->BufferPhysicalAddress & 0x00ffffff, params->ByteCount, params->LBN); + // Adjust message length for response + message->MessageLength = sizeof(ReadParameters) + + HEADER_SIZE; + mscp_drive_c* drive = GetDrive(unitNumber); // Check unit @@ -597,17 +622,12 @@ mscp_server::Read( params->ByteCount, diskBuffer.get()); - - // Adjust message length for response - message->MessageLength = sizeof(ReadParameters) + - HEADER_SIZE; - // Set parameters for response. // We leave ByteCount as is (for now anyway) // And set First Bad Block to 0. (This is unnecessary since we're // not reporting a bad block, but we're doing it for completeness.) params->LBN = 0; - + return STATUS(Status::SUCCESS,0); } @@ -632,12 +652,17 @@ mscp_server::Write( WriteParameters* params = reinterpret_cast(GetParameterPointer(message)); - DEBUG("MSCP WRITE unit %d pa o%o count %d lbn %d", + DEBUG("MSCP WRITE unit %d chan o%o pa o%o count %d lbn %d", unitNumber, + params->BufferPhysicalAddress >> 24, params->BufferPhysicalAddress & 0x00ffffff, params->ByteCount, params->LBN); + // Adjust message length for response + message->MessageLength = sizeof(WriteParameters) + + HEADER_SIZE; + mscp_drive_c* drive = GetDrive(unitNumber); // Check unit @@ -671,10 +696,6 @@ mscp_server::Write( params->ByteCount, memBuffer.get()); - // Adjust message length for response - message->MessageLength = sizeof(WriteParameters) + - HEADER_SIZE; - // Set parameters for response. // We leave ByteCount as is (for now anyway) // And set First Bad Block to 0. (This is unnecessary since we're diff --git a/10.02_devices/2_src/mscp_server.hpp b/10.02_devices/2_src/mscp_server.hpp index d861eb6..571bfe1 100644 --- a/10.02_devices/2_src/mscp_server.hpp +++ b/10.02_devices/2_src/mscp_server.hpp @@ -15,7 +15,7 @@ class mscp_drive_c; #define GET_FLAGS(status) (((status) >> 8) & 0xff) #define MAX_CREDITS 14 -#define INIT_CREDITS 32 +#define INIT_CREDITS 1 // TODO: Dependent on little-endian hardware // @@ -95,6 +95,16 @@ enum Status DIAGNOSTIC_MESSAGE = 0x1f }; +enum SuccessSubcodes +{ + NORMAL = 0x0, + SPIN_DOWN_IGNORED = 0x20, + STILL_CONNECTED = 0x40, + DUPLICATE_UNIT_NUMBER = 0x80, + ALREADY_ONLINE = 0x100, + STILL_ONLINE = 0x200, +}; + enum MessageTypes { Sequential = 0, diff --git a/10.02_devices/2_src/rk11.cpp b/10.02_devices/2_src/rk11.cpp index 659d062..e454eb9 100755 --- a/10.02_devices/2_src/rk11.cpp +++ b/10.02_devices/2_src/rk11.cpp @@ -88,6 +88,8 @@ rk11_c::rk11_c() : RKDB_reg->reset_value = 0; RKDB_reg->writable_bits = 0x0000; // read only + _rkda_drive = 0; + // // Drive configuration: up to eight drives. // @@ -127,8 +129,7 @@ void rk11_c::dma_transfer(DMARequest &request) { // Write FROM buffer TO unibus memory, IBA on: // We only need to write the last word in the buffer to memory. - unibusadapter->request_DMA( - this, + request.timeout = !unibusadapter->request_DMA( UNIBUS_CONTROL_DATO, request.address, request.buffer + request.count - 1, @@ -139,8 +140,7 @@ void rk11_c::dma_transfer(DMARequest &request) // Read FROM unibus memory TO buffer, IBA on: // We read a single word from the unibus and fill the // entire buffer with this value. - unibusadapter->request_DMA( - this, + request.timeout = !unibusadapter->request_DMA( UNIBUS_CONTROL_DATI, request.address, request.buffer, @@ -153,8 +153,7 @@ void rk11_c::dma_transfer(DMARequest &request) if (request.write) { // Write FROM buffer TO unibus memory - unibusadapter->request_DMA( - this, + request.timeout = !unibusadapter->request_DMA( UNIBUS_CONTROL_DATO, request.address, request.buffer, @@ -163,8 +162,7 @@ void rk11_c::dma_transfer(DMARequest &request) else { // Read FROM unibus memory TO buffer - unibusadapter->request_DMA( - this, + request.timeout = !unibusadapter->request_DMA( UNIBUS_CONTROL_DATI, request.address, request.buffer, @@ -172,20 +170,6 @@ void rk11_c::dma_transfer(DMARequest &request) } } - // And wait for completion. - while(true) - { - timeout.wait_us(50); // Stolen from RL11 - uint32_t last_address = 0; - if (unibusadapter->complete_DMA( - this, - &last_address, - &request.timeout)) - { - break; - } - } - // If an IBA DMA read from memory, we need to fill the request buffer // with the single word returned from memory by the DMA operation. if (request.iba && !request.write) diff --git a/10.02_devices/2_src/rl11.cpp b/10.02_devices/2_src/rl11.cpp index 54ad1b9..8346ad4 100644 --- a/10.02_devices/2_src/rl11.cpp +++ b/10.02_devices/2_src/rl11.cpp @@ -718,7 +718,7 @@ void RL11_c::state_readwrite() { //logger.debug_hexdump(LC_RL, "Read data between disk access and DMA", // (uint8_t *) silo, sizeof(silo), NULL); // start DMA transmission of SILO into memory - unibusadapter->request_DMA(this, UNIBUS_CONTROL_DATO, unibus_address, silo, + error_dma_timeout = !unibusadapter->request_DMA(UNIBUS_CONTROL_DATO, unibus_address, silo, dma_wordcount); } else if (function_code == CMD_WRITE_CHECK) { // read sector data to compare with sector data @@ -726,26 +726,20 @@ void RL11_c::state_readwrite() { // logger.debug_hexdump(LC_RL, "Read data between disk access and DMA", // (uint8_t *) silo, sizeof(silo), NULL); // start DMA transmission of memory to compare with SILO - unibusadapter->request_DMA(this, UNIBUS_CONTROL_DATI, unibus_address, silo_compare, + error_dma_timeout = !unibusadapter->request_DMA(UNIBUS_CONTROL_DATI, unibus_address, silo_compare, dma_wordcount); } else if (function_code == CMD_WRITE_DATA) { // start DMA transmission of memory into SILO - unibusadapter->request_DMA(this, UNIBUS_CONTROL_DATI, unibus_address, silo, + error_dma_timeout = !unibusadapter->request_DMA(UNIBUS_CONTROL_DATI, unibus_address, silo, dma_wordcount); } change_state(RL11_STATE_RW_WAIT_DMA); break; case RL11_STATE_RW_WAIT_DMA: - // wait for DMA to complete, start read of next sector + // Complete DMA, move to next sector - // data late DLT does never happen - if (!unibusadapter->complete_DMA(this, &unibus_address, (bool *) &error_dma_timeout)) { - timeout.wait_us(50); // 50us, but is more because of granularity - break; // DMA still in progress - } - - unibus_address += 2; // was last address, is now next to fill + unibus_address += dma_wordcount * 2; // if timeout: addr AFTER illegal address (verified) update_unibus_address(unibus_address); // set addr msb to cs diff --git a/10.02_devices/2_src/uda.cpp b/10.02_devices/2_src/uda.cpp index 2bc9a09..60c1665 100644 --- a/10.02_devices/2_src/uda.cpp +++ b/10.02_devices/2_src/uda.cpp @@ -148,7 +148,7 @@ void uda_c::worker(void) case InitializationStep::Step1: // Wait 100uS, set SA. - timeout.wait_us(10); + timeout.wait_us(100); INFO("Transition to Init state S1."); // @@ -163,44 +163,42 @@ void uda_c::worker(void) case InitializationStep::Step2: // Wait 100uS, set SA. - timeout.wait_us(100); + timeout.wait_ms(100); INFO("Transition to Init state S2."); // update the SA read value for step 2: // S2 is set, unibus port type (0), SA bits 15-8 written // by the host in step 1. - _sa = 0x1000 | (_step1Value >> 8); + _sa = 0x1000 | ((_step1Value >> 8) & 0xff); update_SA(); - Interrupt(); break; case InitializationStep::Step3: // Wait 100uS, set SA. - timeout.wait_us(100); + timeout.wait_ms(100); INFO("Transition to Init state S3."); // Update the SA read value for step 3: // S3 set, plus SA bits 7-0 written by the host in step 1. _sa = 0x2000 | (_step1Value & 0xff); update_SA(); - Interrupt(); break; case InitializationStep::Step4: + // Clear communications area, set SA INFO("Clearing comm area at 0x%x.", _ringBase); INFO("resp 0x%x comm 0x%x", _responseRingLength, _commandRingLength); // TODO: -6 and -8 are described; do these always get cleared or only // on VAXen? ZUDJ diag only expects -2 and -4 to be cleared... - for(uint32_t i = 0; - i < (_responseRingLength + _commandRingLength) * sizeof(Descriptor) + 8; + i < (_responseRingLength + _commandRingLength) * sizeof(Descriptor) + 4; i += 2) { - DMAWriteWord(_ringBase - 4 + i, 0x0); + DMAWriteWord(_ringBase + i - 4, 0x0); } // @@ -208,7 +206,6 @@ void uda_c::worker(void) // to indicate that the port owns them. // - Descriptor blankDescriptor; blankDescriptor.Word0.Word0 = 0; blankDescriptor.Word1.Word1 = 0; @@ -220,7 +217,7 @@ void uda_c::worker(void) GetResponseDescriptorAddress(i), sizeof(Descriptor), reinterpret_cast(&blankDescriptor)); - } + } INFO("Transition to Init state S4."); // Update the SA read value for step 4: @@ -233,9 +230,9 @@ void uda_c::worker(void) case InitializationStep::Complete: INFO("Transition to Init state Complete. Initializing response ring."); - _sa = 0x0; - update_SA(); - + //_sa = 0x0; + //update_SA(); + // // Set the ownership bit on all descriptors in the response ring // to indicate that the port owns them. @@ -282,7 +279,7 @@ uda_c::on_after_register_access( // to initiate polling..." if (_initStep == InitializationStep::Complete) { - //INFO("Request to start polling."); + INFO("Request to start polling."); _server->InitPolling(); } } @@ -325,9 +322,9 @@ uda_c::on_after_register_access( _responseRingLength = (1 << ((value & 0x700) >> 8)); _commandRingLength = (1 << ((value & 0x3800) >> 11)); - DEBUG("Step1: 0x%x", value); - DEBUG("resp ring 0x%x", _responseRingLength); - DEBUG("cmd ring 0x%x", _commandRingLength); + INFO("Step1: 0x%x", value); + INFO("resp ring 0x%x", _responseRingLength); + INFO("cmd ring 0x%x", _commandRingLength); // Move to step 2. StateTransition(InitializationStep::Step2); @@ -347,7 +344,7 @@ uda_c::on_after_register_access( _ringBase = value & 0xfffe; _purgeInterruptEnable = !!(value & 0x1); - DEBUG("Step2: 0x%x", value); + INFO("Step2: 0x%x", value); // Move to step 3 and interrupt as necessary. StateTransition(InitializationStep::Step3); break; @@ -366,7 +363,7 @@ uda_c::on_after_register_access( // [ringbase+0]. _ringBase |= ((value & 0x7fff) << 16); - DEBUG("Step3: 0x%x", value); + INFO("Step3: 0x%x", value); // Move to step 4 and interrupt as necessary. StateTransition(InitializationStep::Step4); break; @@ -407,6 +404,10 @@ uda_c::on_after_register_access( // start the controller running. // StateTransition(InitializationStep::Complete); + // The VMS bootstrap expects SA to be zero IMMEDIATELY + // after completion. + _sa = 0; + update_SA(); } else { @@ -560,7 +561,7 @@ uda_c::GetNextCommand(void) // DMAWriteWord( _ringBase - 4, - 0xff); + 0xffff); // // Raise the interrupt @@ -709,7 +710,7 @@ uda_c::PostResponse( // DMAWriteWord( _ringBase - 2, - 0xff); + 0xffff); // // Raise the interrupt @@ -844,51 +845,13 @@ uda_c::DMAWrite( size_t lengthInBytes, uint8_t* buffer) { - bool timeout = false; - timeout_c timer; - assert ((lengthInBytes % 2) == 0); - // Retry the transfer to work around lower-level DMA issues - while(true) - { - if(!unibusadapter->request_DMA_active("uda r") && - !unibusadapter->request_INTR_active("uda w")) - { - unibusadapter->request_DMA( - this, + return unibusadapter->request_DMA( UNIBUS_CONTROL_DATO, address, reinterpret_cast(buffer), lengthInBytes >> 1); - - // Wait for completion - uint32_t last_address = 0; - while(!unibusadapter->complete_DMA( - this, - &last_address, - &timeout)) - { - timer.wait_us(50); - } - - if (!timeout) - { - // Success! - // timer.wait_us(250); // also a hack - return true; - } - else - { - INFO(" DMA WRITE FAILED, RETRYING."); - } - } - - // Try again - timer.wait_us(100); - } - - return false; } /* @@ -911,50 +874,18 @@ uda_c::DMARead( memset(reinterpret_cast(buffer), 0xc3, bufferSize); - bool timeout = false; - timeout_c timer; + bool success = unibusadapter->request_DMA( + UNIBUS_CONTROL_DATI, + address, + buffer, + lengthInBytes >> 1); - // We retry the transfer to work around lower-level DMA issues - while(true) - { - timeout = false; - - if(!unibusadapter->request_DMA_active("uda r") && - !unibusadapter->request_INTR_active("uda w")) - { - unibusadapter->request_DMA( - this, - UNIBUS_CONTROL_DATI, - address, - buffer, - lengthInBytes >> 1); - - uint32_t last_address = 0; - // Wait for completion - while (!unibusadapter->complete_DMA( - this, - &last_address, - &timeout)) - { - timer.wait_us(50); - } - - if (!timeout) - { - // success! - // timer.wait_us(250); - break; - } - else - { - INFO("DMA READ FAILED (addr o%o length o%o, lastaddr o%o RETRYING", address, lengthInBytes, last_address); - } - } - - // Try again - timer.wait_us(100); + if (success) + { + return reinterpret_cast(buffer); + } + else + { + return nullptr; } - - // timer.wait_us(250); - return reinterpret_cast(buffer); } diff --git a/10.03_app_demo/2_src/makefile b/10.03_app_demo/2_src/makefile index 8f54fda..2a39f9a 100644 --- a/10.03_app_demo/2_src/makefile +++ b/10.03_app_demo/2_src/makefile @@ -93,6 +93,7 @@ OBJECTS = $(OBJDIR)/application.o \ $(OBJDIR)/rk05.o \ $(OBJDIR)/uda.o \ $(OBJDIR)/mscp_server.o \ + $(OBJDIR)/mscp_drive.o \ $(OBJDIR)/storagedrive.o \ $(OBJDIR)/storagecontroller.o \ $(OBJDIR)/demo_io.o \ @@ -203,6 +204,9 @@ $(OBJDIR)/uda.o : $(DEVICE_SRC_DIR)/uda.cpp $(DEVICE_SRC_DIR)/uda.hpp $(OBJDIR)/mscp_server.o : $(DEVICE_SRC_DIR)/mscp_server.cpp $(DEVICE_SRC_DIR)/mscp_server.hpp $(CC) $(CCFLAGS) $< -o $@ +$(OBJDIR)/mscp_drive.o : $(DEVICE_SRC_DIR)/mscp_drive.cpp $(DEVICE_SRC_DIR)/mscp_drive.hpp + $(CC) $(CCFLAGS) $< -o $@ + $(OBJDIR)/storagedrive.o : $(BASE_SRC_DIR)/storagedrive.cpp $(BASE_SRC_DIR)/storagedrive.hpp $(CC) $(CCFLAGS) $< -o $@ diff --git a/10.03_app_demo/2_src/menu_devices.cpp b/10.03_app_demo/2_src/menu_devices.cpp index 636d539..4078925 100644 --- a/10.03_app_demo/2_src/menu_devices.cpp +++ b/10.03_app_demo/2_src/menu_devices.cpp @@ -72,10 +72,10 @@ void menus_c::menu_devices(void) { unibus->emulation_logic_start(); // PRU is active UNIBUS node // 2 demo controller - demo_io_c demo_io; + //demo_io_c demo_io; //demo_regs_c demo_regs; // mem at 160000: RT11 crashes? - cpu_c cpu; + // cpu_c cpu; // create RL11 + drives RL11_c RL11; // instantiates also 4 RL01/02 drives @@ -89,8 +89,8 @@ void menus_c::menu_devices(void) { // Create UDA50 uda_c UDA50; - demo_io.install(); - demo_io.worker_start(); + // demo_io.install(); + //demo_io.worker_start(); //demo_regs.install(); //demo_regs.worker_start(); @@ -105,8 +105,8 @@ void menus_c::menu_devices(void) { UDA50.install(); UDA50.worker_start(); - cpu.install(); - cpu.worker_start(); + // cpu.install(); + // cpu.worker_start(); while (!ready) { @@ -337,24 +337,24 @@ void menus_c::menu_devices(void) { cout << "Error : " << e.what() << "\n"; } } // ready - cpu.worker_stop(); - cpu.uninstall(); + // cpu.worker_stop(); + // cpu.uninstall(); - RL11.worker_stop(); - RL11.disconnect_from_panel(); - RL11.uninstall(); + //RL11.worker_stop(); + //RL11.disconnect_from_panel(); + //RL11.uninstall(); RK05.worker_stop(); RK05.uninstall(); - UDA50.worker_stop(); - UDA50.uninstall(); + // UDA50.worker_stop(); + // UDA50.uninstall(); //demo_regs.worker_stop(); //demo_regs.uninstall(); - demo_io.worker_stop(); - demo_io.uninstall(); + // demo_io.worker_stop(); + // demo_io.uninstall(); if (unibus->arbitration_active) unibus->emulation_logic_stop(); // undo