diff --git a/10.01_base/2_src/arm/priorityrequest.cpp b/10.01_base/2_src/arm/priorityrequest.cpp new file mode 100644 index 0000000..309645f --- /dev/null +++ b/10.01_base/2_src/arm/priorityrequest.cpp @@ -0,0 +1,107 @@ +/* priorityrequest.cpp: DMA or iNTR request of an device + + Copyright (c) 2019 Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + jul-2019 JH start: multiple parallel arbitration levels + */ + +#include +#include + +#include "unibusdevice.hpp" +#include "priorityrequest.hpp" + +priority_request_c::priority_request_c(unibusdevice_c *device) { + this->log_label = "REQ"; + this->device = device; + this->complete = false; + this->executing_on_PRU = false; + this->slot = 0xff; // uninitialized, asserts() if used + complete_mutex = PTHREAD_MUTEX_INITIALIZER; + //complete_cond = PTHREAD_COND_INITIALIZER; // PRU signal notifies request on completeness +} + +priority_request_c::~priority_request_c() { + // not used, but need some virtual func for dynamic_cast() +} + +void priority_request_c::set_priority_slot(uint8_t priority_slot) { + assert(priority_slot > 0); // 0 reserved + assert(priority_slot < PRIORITY_SLOT_COUNT); + unibusdevice_c *ubdevice = unibusdevice_c::find_by_request_slot(priority_slot); + if (ubdevice && ubdevice != this->device) { + WARNING("Slot %u already used by device %s", (unsigned) priority_slot, ubdevice->name.value.c_str()); + } + this->slot = priority_slot; + // todo: check for collision with all other devices, all other requests +} + +// create invalid requests, is setup by unibusadapter +dma_request_c::dma_request_c(unibusdevice_c *device) : + priority_request_c(device) { + this->level_index = PRIORITY_LEVEL_INDEX_NPR; + this->success = false; + // register request for device + if (device) { + device->dma_requests.push_back(this); + } +} + +dma_request_c::~dma_request_c() { + if (device) { + // find and erase from device's request list + std::vector::iterator it = std::find(device->dma_requests.begin(), + device->dma_requests.end(), this); + device->dma_requests.erase(it); + } +} + +// create invalid requests, is setup by unibusadapter +intr_request_c::intr_request_c(unibusdevice_c *device) : + priority_request_c(device) { + // convert UNIBUS level 4,5,6,7 to internal priority, see REQUEST_INDEX_* + this->level_index = 0xff; // uninitialized, asserts() if used + this->vector = 0xffff; // uninitialized, asserts() if used + this->signal_level = 0; + if (device) + device->intr_requests.push_back(this); +} + +intr_request_c::~intr_request_c() { + if (device) { + // find and erase from device's request list + std::vector::iterator it = std::find(device->intr_requests.begin(), + device->intr_requests.end(), this); + device->intr_requests.erase(it); + } +} + +void intr_request_c::set_level(uint8_t level) { + assert(level >= 4 && level <= 7); + this->level_index = level - 4; // one of PRIORITY_LEVEL_INDEX_* +} + +void intr_request_c::set_vector(uint16_t vector) { + assert((vector & 3) == 0); // multiple of 2 words + this->vector = vector; +} + diff --git a/10.01_base/2_src/arm/priorityrequest.hpp b/10.01_base/2_src/arm/priorityrequest.hpp new file mode 100644 index 0000000..41c86e5 --- /dev/null +++ b/10.01_base/2_src/arm/priorityrequest.hpp @@ -0,0 +1,183 @@ +/* priorityrequest.hpp: DMA or iNTR request of an device + + Copyright (c) 2019 Joerg Hoppe + j_hoppe@t-online.de, www.retrocmp.com + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + jul-2019 JH start: multiple parallel arbitration levels + */ + +/* + Handling priorities of Arbitration Requests: + 1. Priority of arbitration levels + Ascending priority: INTR BR4,5,6,7, DMA NPR + => 5 Priority Arbitration Levels encoded with index 0..4 + 2. Priority within one request level + Priority for Requests of same level given by backplane slot. + Backplane closests to CPU is granted first => highest priority + + So priority of a request is given by two coordinates: level and slot. + + Implementation: + all 5 levels are handled in parallel: priority_request_level_c [5]) + In each level, a refernece array[slot] holds open requests. + For fast determination of lowest active slot, a bitarray + marks active slots. + */ +#ifndef _PRIORITYREQUEST_HPP_ +#define _PRIORITYREQUEST_HPP_ + +#include +#include + +#include "logsource.hpp" + +// linear indexes for different UNIBUS arbitration levels +#define PRIORITY_LEVEL_INDEX_BR4 0 +#define PRIORITY_LEVEL_INDEX_BR5 1 +#define PRIORITY_LEVEL_INDEX_BR6 2 +#define PRIORITY_LEVEL_INDEX_BR7 3 +#define PRIORITY_LEVEL_INDEX_NPR 4 +#define PRIORITY_LEVEL_COUNT 5 + +#define PRIORITY_SLOT_COUNT 32 // backplane slot numbers 0..31 may be used + +class unibusdevice_c; + +// (almost) abstract base class for dma and intr requests +class priority_request_c: public logsource_c { + friend class intr_request_c; + friend class dma_request_c; + friend class unibusadapter_c; +private: + unibusdevice_c *device; // this device owns the request + // maybe NULL, in request is not used for device meualtion + // (test console EXAM, DEPOSIT for example). + + // internal priority index of a request level, see REQUEST_INDEX_* + uint8_t level_index; // is BR4567,NPR level - 4 + + uint8_t slot; // backplane slot which triggered request +public: + // better make state variables volatile, accessed by unibusadapter::worker + volatile bool executing_on_PRU; // true between schedule to PRU and compelte signal + volatile bool complete; + + // PRU -> signal -> worker() -> request -> device. INTR/DMA + pthread_mutex_t complete_mutex; + //pthread_cond_t complete_cond; // PRU signal notifies request on completeness + + priority_request_c(unibusdevice_c *device); + virtual ~priority_request_c(); // not used, but need dynamic_cast + + void set_priority_slot(uint8_t slot); + uint8_t get_priority_slot(void) { + return slot; + } +}; + +class dma_request_c: public priority_request_c { + friend class unibusadapter_c; +public: + dma_request_c(unibusdevice_c *device); + + ~dma_request_c(); + // const for all chunks + uint8_t unibus_control; // DATI,DATO + uint32_t unibus_start_addr; + uint32_t unibus_end_addr; + uint16_t* buffer; + uint32_t wordcount; + + // DMA transaction are divided in to smaller DAT transfer "chunks" + uint32_t chunk_max_words; // max is PRU capacity PRU_MAX_DMA_WORDCOUNT (512) + uint32_t chunk_unibus_start_addr; // current chunk + uint32_t chunk_words; // size of current chunks + + volatile bool success; // DMA can fail with bus timeout + + // return ptr to chunk pos in buffer + uint16_t *chunk_buffer_start(void) { + return buffer + (chunk_unibus_start_addr - unibus_start_addr) / 2; + } + + // words already transfered in previous chunks + uint32_t wordcount_completed_chunks(void) { + return (chunk_unibus_start_addr - unibus_start_addr) / 2; + } + +}; + +struct unibusdevice_register_struct; +// forward + +class intr_request_c: public priority_request_c { + friend class unibusadapter_c; +public: + enum interrupt_edge_enum { + INTERRUPT_EDGE_NONE, INTERRUPT_EDGE_RAISING, INTERRUPT_EDGE_FALLING + }; +private: + uint16_t vector; // PDP-11 interrupt vector + + // optionally register, with which a device signals presence of interrupt condition + struct unibusdevice_register_struct *interrupt_register; + uint16_t interrupt_register_value; + + // static level of some device INTR signal. + // raising edge calculated with edge_detect() + bool signal_level; + +public: + intr_request_c(unibusdevice_c *device); + + ~intr_request_c(); + + void set_level(uint8_t level); + void set_vector(uint16_t vector); + uint8_t get_level(void) { + return level_index + 4; + } + uint8_t get_vector(void) { + return vector; + } + + // service for device logic: calculate change of static INTR condition + void edge_detect_reset() { + signal_level = 0; + } + + // detect raising edge of interrupt level + enum interrupt_edge_enum edge_detect(bool new_signal_level) { + if (signal_level == new_signal_level) + return INTERRUPT_EDGE_NONE; + else { + // change: which edge? + signal_level = new_signal_level; + if (signal_level) + return INTERRUPT_EDGE_RAISING; + else + return INTERRUPT_EDGE_FALLING; + } + } + +}; + +#endif