From 8ff33a0be16f454242ad76249b0bc3ed9642eaff Mon Sep 17 00:00:00 2001 From: Joerg Hoppe Date: Fri, 16 Aug 2019 19:04:12 +0200 Subject: [PATCH] Infrastructure for emulated CPUs: Bus arbitrator, Interrupt fielding processor --- 10.01_base/2_src/arm/mailbox.cpp | 14 +- 10.01_base/2_src/arm/unibusadapter.cpp | 197 ++++++++++++------ 10.01_base/2_src/arm/unibusadapter.hpp | 12 +- 10.01_base/2_src/pru1/pru1_main_test.c | 8 +- 10.01_base/2_src/pru1/pru1_main_unibus.c | 50 +++-- .../pru1/pru1_statemachine_arbitration.c | 149 ++++++++++--- .../pru1/pru1_statemachine_arbitration.h | 13 +- 10.01_base/2_src/pru1/pru1_statemachine_dma.c | 34 +-- .../2_src/pru1/pru1_statemachine_intr.c | 4 + 10.01_base/2_src/pru1/pru1_timeouts.c | 2 +- 10.01_base/2_src/pru1/pru1_timeouts.h | 3 +- 10.01_base/2_src/shared/mailbox.h | 39 +++- 10.02_devices/2_src/cpu.cpp | 17 +- 10.02_devices/2_src/cpu.hpp | 5 +- 10.02_devices/3_test/rl02/xxdp.cmd | 2 +- 10.03_app_demo/2_src/menu_devices.cpp | 35 ++-- 16 files changed, 412 insertions(+), 172 deletions(-) diff --git a/10.01_base/2_src/arm/mailbox.cpp b/10.01_base/2_src/arm/mailbox.cpp index 4e0d5fb..57dc419 100644 --- a/10.01_base/2_src/arm/mailbox.cpp +++ b/10.01_base/2_src/arm/mailbox.cpp @@ -65,7 +65,7 @@ int mailbox_connect(void) { void mailbox_print(void) { printf("INFO: Content of mailbox to PRU:\n" - "arm2pru: req=0x%x, resp=0x%x\n", mailbox->arm2pru_req, mailbox->arm2pru_resp); + "arm2pru: req=0x%x\n", mailbox->arm2pru_req); } /* simulate simple register accesses: @@ -101,17 +101,25 @@ void mailbox_test1() { /* start cmd to PRU via mailbox. Wait until ready * mailbox union members must have been filled. */ -uint32_t xxx; -void mailbox_execute(uint8_t request) { +//uint32_t xxx; +bool mailbox_execute(uint8_t request) { // write to arm2pru_req must be last memory operation __sync_synchronize(); while (mailbox->arm2pru_req != ARM2PRU_NONE) ; // wait to complete mailbox->arm2pru_req = request; // go! + + // wait until ACKed + while (mailbox->arm2pru_req == request); + /* do { xxx = mailbox->arm2pru_req; } while (xxx != ARM2PRU_NONE); + while (mailbox->arm2pru_req != ARM2PRU_NONE) ; // wait until processed + */ + // result false = error + return (mailbox->arm2pru_req == ARM2PRU_NONE) ; } diff --git a/10.01_base/2_src/arm/unibusadapter.cpp b/10.01_base/2_src/arm/unibusadapter.cpp index d8b3a9f..d5e7904 100644 --- a/10.01_base/2_src/arm/unibusadapter.cpp +++ b/10.01_base/2_src/arm/unibusadapter.cpp @@ -303,7 +303,7 @@ void unibusadapter_c::requests_cancel_scheduled(void) { pthread_mutex_lock(&req->complete_mutex); req->complete = true; pthread_cond_signal(&req->complete_cond); - pthread_mutex_unlock(&req->complete_mutex); + pthread_mutex_unlock(&req->complete_mutex); } } } @@ -399,7 +399,7 @@ void unibusadapter_c::request_execute_active_on_PRU(unsigned level_index) { // assert(mailbox->events.event_dma == 0); // previous signal must have been processed // DEBUG("request_execute_active_on_PRU() DMA: ->active = dma_request %p, start = 0%06o, control=%u, wordcount=%u, data=0%06o ...", dmareq, // mailbox->dma.startaddr, (unsigned)mailbox->dma.control, (unsigned)mailbox->dma.wordcount, (unsigned)mailbox->dma.words[0]); - + mailbox->dma.cur_status = 0; // device DMA, not by CPU mailbox_execute(ARM2PRU_DMA); // scheduling is fast, on complete there's a signal. dmareq->executing_on_PRU = true; @@ -493,7 +493,8 @@ void unibusadapter_c::request_active_complete(unsigned level_index) { pthread_mutex_lock(&tmprq->complete_mutex); tmprq->complete = true; pthread_cond_signal(&tmprq->complete_cond); - pthread_mutex_unlock(&tmprq->complete_mutex); + pthread_mutex_unlock(&tmprq->complete_mutex); + } // Request a DMA cycle from Arbitrator. @@ -549,11 +550,12 @@ void unibusadapter_c::DMA(dma_request_c& dma_request, bool blocking, uint8_t uni // DEBUG("device DMA start: %s @ %06o, len=%d", unibus->control2text(unibus_control), unibus_addr, wordcount); if (blocking) { pthread_mutex_lock(&dma_request.complete_mutex); - // DMA() is blocking: Wait for request to finish. - while (!dma_request.complete) { - int res = pthread_cond_wait(&dma_request.complete_cond, &dma_request.complete_mutex); - assert(!res); - } + // DMA() is blocking: Wait for request to finish. + // pthread_mutex_lock(&dma_request.mutex); + while (!dma_request.complete) { + int res = pthread_cond_wait(&dma_request.complete_cond, &dma_request.complete_mutex); + assert(!res) ; + } pthread_mutex_unlock(&dma_request.complete_mutex); } } @@ -686,6 +688,56 @@ void unibusadapter_c::cancel_INTR(intr_request_c& intr_request) { } +// do DATO/DATI as master CPU. +// no NPR/NPG/SACK request, but waiting for BUS idle +// result: success, else BUS TIMOUT +void unibusadapter_c::cpu_DATA_transfer(dma_request_c& dma_request, uint8_t unibus_control, + uint32_t unibus_addr, uint16_t *buffer) { + timeout_c timeout; + bool success; + cpu_data_transfer_request = &dma_request; + + // request is not queued, so only request.mutex used + + pthread_mutex_lock(&cpu_data_transfer_request->complete_mutex); + cpu_data_transfer_request->complete = false; + + mailbox->dma.startaddr = unibus_addr; + mailbox->dma.control = unibus_control; + mailbox->dma.wordcount = 1; + + // Copy outgoing data into mailbox device_DMA buffer + if (UNIBUS_CONTROL_ISDATO(unibus_control)) + memcpy((void*) mailbox->dma.words, buffer, 2); + + // do the transfer. Wait until concurrent device DMA/INTR complete + do { + while (mailbox->arbitrator.device_BBSY) + timeout.wait_us(100); + mailbox->arbitrator.cpu_BBSY = true; // mark as "initiated by CPU, not by device" + success = mailbox_execute(ARM2PRU_DMA); + // a device may have acquired the bus concurrently, + // then ARM2PRU_DMA is answered with an error + // infinite time may have passed after that check above + } while (!success); + // wait for PRU complete event + //pthread_mutex_lock(&cpu_data_transfer_request->complete_mutex); + //pthread_mutex_unlock(&cpu_data_transfer_request->complete_mutex); + pthread_mutex_lock(&cpu_data_transfer_request->complete_mutex); + // DMA() is blocking: Wait for request to finish. + // pthread_mutex_lock(&dma_request.mutex); + while (!cpu_data_transfer_request->complete) { + int res = pthread_cond_wait(&cpu_data_transfer_request->complete_cond, &cpu_data_transfer_request->complete_mutex); + assert(!res) ; + } + pthread_mutex_unlock(&cpu_data_transfer_request->complete_mutex); + + // Copy incoming data from mailbox DMA buffer + if (unibus_control == UNIBUS_CONTROL_DATI) + memcpy(buffer, (void *) mailbox->dma.words, 2); + +} + // set state of INIT void unibusadapter_c::worker_init_event() { unsigned device_handle; @@ -799,63 +851,80 @@ void unibusadapter_c::worker_deviceregister_event() { } // called by PRU signal when DMA transmission complete -void unibusadapter_c::worker_dma_chunk_complete_event() { - priority_request_level_c *prl = &request_levels[PRIORITY_LEVEL_INDEX_NPR]; - bool more_chunks; - // Must run under pthread_mutex_lock(&requests_mutex) ; +// Called for device DMA() chunk, +// or cpu_DATA_transfer() +void unibusadapter_c::worker_dma_chunk_complete_event(bool cpu_DATA_transfer) { - dma_request_c *dmareq = dynamic_cast(prl->active); + if (cpu_DATA_transfer) { + // cpu_DATA_transfer() started independent of request_levels table, + // prl->active == NULL + cpu_data_transfer_request->success = (mailbox->dma.cur_status == DMA_STATE_READY); - assert(dmareq != NULL); - dmareq->unibus_end_addr = mailbox->dma.cur_addr; // track emnd of trasnmission, eror position - unsigned wordcount_transferred = dmareq->wordcount_completed_chunks() - + mailbox->dma.wordcount; - assert(wordcount_transferred <= dmareq->wordcount); - if (mailbox->dma.control == UNIBUS_CONTROL_DATI) { - // guard against buffer overrun - // PRU read chunk data from UNIBUS into mailbox - // copy result cur_DMA_wordcount from mailbox->DMA buffer to cur_DMA_buffer - memcpy(dmareq->chunk_buffer_start(), (void *) mailbox->dma.words, - 2 * mailbox->dma.wordcount); - } - if (mailbox->dma.cur_status != DMA_STATE_READY) { - // failure: abort remaining chunks - dmareq->success = false; - more_chunks = false; - } else if (wordcount_transferred == dmareq->wordcount) { - // last chunk completed - dmareq->success = true; - more_chunks = false; - } else { - // more data to transfer: next chunk. - dmareq->chunk_unibus_start_addr = mailbox->dma.cur_addr + 2; - // dmarequest remains prl->active and ->busy + // signal to DMA() or INTR() + cpu_data_transfer_request->complete = true; // close to signal + pthread_mutex_unlock(&cpu_data_transfer_request->complete_mutex); - // re-activate this request, or choose another with higher slot priority, - // inserted in parallel (interrupt this DMA) - prl->active = NULL; - request_activate_lowest_slot(PRIORITY_LEVEL_INDEX_NPR); - - request_execute_active_on_PRU(PRIORITY_LEVEL_INDEX_NPR); - more_chunks = true; - // DEBUG("DMA chunk: %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], dmareq->success ? "OK" : "TIMEOUT"); - - } - if (!more_chunks) { - DEBUG("DMA ready: %s @ %06o..%06o, wordcount %d, data=%06o, %06o, ... %s", - unibus->control2text(dmareq->unibus_control), dmareq->unibus_start_addr, - dmareq->unibus_end_addr, dmareq->wordcount, dmareq->buffer[0], - dmareq->buffer[1], dmareq->success ? "OK" : "TIMEOUT"); - - // clear from schedule table of this level - request_active_complete(PRIORITY_LEVEL_INDEX_NPR); - - // check and execute DMA on other priority_slot + // concurrent to this DATA transfer a device may have requested DMA. if (request_activate_lowest_slot(PRIORITY_LEVEL_INDEX_NPR)) request_execute_active_on_PRU(PRIORITY_LEVEL_INDEX_NPR); + } else { + priority_request_level_c *prl = &request_levels[PRIORITY_LEVEL_INDEX_NPR]; + bool more_chunks; + // Must run under pthread_mutex_lock(&requests_mutex) ; + + dma_request_c *dmareq = dynamic_cast(prl->active); + + assert(dmareq != NULL); + dmareq->unibus_end_addr = mailbox->dma.cur_addr; // track emnd of trasnmission, eror position + unsigned wordcount_transferred = dmareq->wordcount_completed_chunks() + + mailbox->dma.wordcount; + assert(wordcount_transferred <= dmareq->wordcount); + if (mailbox->dma.control == UNIBUS_CONTROL_DATI) { + // guard against buffer overrun + // PRU read chunk data from UNIBUS into mailbox + // copy result cur_DMA_wordcount from mailbox->DMA buffer to cur_DMA_buffer + memcpy(dmareq->chunk_buffer_start(), (void *) mailbox->dma.words, + 2 * mailbox->dma.wordcount); + } + if (mailbox->dma.cur_status != DMA_STATE_READY) { + // failure: abort remaining chunks + dmareq->success = false; + more_chunks = false; + } else if (wordcount_transferred == dmareq->wordcount) { + // last chunk completed + dmareq->success = true; + more_chunks = false; + } else { + // more data to transfer: next chunk. + dmareq->chunk_unibus_start_addr = mailbox->dma.cur_addr + 2; + // dmarequest remains prl->active and ->busy + + // re-activate this request, or choose another with higher slot priority, + // inserted in parallel (interrupt this DMA) + prl->active = NULL; + request_activate_lowest_slot(PRIORITY_LEVEL_INDEX_NPR); + + request_execute_active_on_PRU(PRIORITY_LEVEL_INDEX_NPR); + more_chunks = true; + // DEBUG("DMA chunk: %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], dmareq->success ? "OK" : "TIMEOUT"); + + } + if (!more_chunks) { + DEBUG("DMA ready: %s @ %06o..%06o, wordcount %d, data=%06o, %06o, ... %s", + unibus->control2text(dmareq->unibus_control), dmareq->unibus_start_addr, + dmareq->unibus_end_addr, dmareq->wordcount, dmareq->buffer[0], + dmareq->buffer[1], dmareq->success ? "OK" : "TIMEOUT"); + + // clear from schedule table of this level + request_active_complete(PRIORITY_LEVEL_INDEX_NPR); + + // check and execute DMA on other priority_slot + if (request_activate_lowest_slot(PRIORITY_LEVEL_INDEX_NPR)) + request_execute_active_on_PRU(PRIORITY_LEVEL_INDEX_NPR); + } } } @@ -866,9 +935,9 @@ void unibusadapter_c::worker_intr_complete_event(uint8_t level_index) { // Must run under pthread_mutex_lock(&requests_mutex) ; priority_request_level_c *prl = &request_levels[level_index]; - // if 1.st opcode of an ISR is a clear of INTR condition, - // cancle_INTR() may be called in worker_deviceregsiter_event() - // then the request is aloready remoced ferom schedule table. + // if 1st opcode of an ISR is a "clear of INTR" condition, + // cancel_INTR() may be called in worker_deviceregister_event() + // then the request is already removed from schedule table. //assert(prl->active); // clear from schedule table of this level @@ -986,7 +1055,7 @@ void unibusadapter_c::worker(unsigned instance) { if (mailbox->events.event_dma) { any_event = true; pthread_mutex_lock(&requests_mutex); - worker_dma_chunk_complete_event(); + worker_dma_chunk_complete_event(mailbox->events.event_dma_cpu_transfer); pthread_mutex_unlock(&requests_mutex); mailbox->events.event_dma = 0; // PRU may re-raise and change mailbox now } diff --git a/10.01_base/2_src/arm/unibusadapter.hpp b/10.01_base/2_src/arm/unibusadapter.hpp index c4d8bbc..0f16f23 100644 --- a/10.01_base/2_src/arm/unibusadapter.hpp +++ b/10.01_base/2_src/arm/unibusadapter.hpp @@ -53,15 +53,18 @@ public: class unibusadapter_c: public device_c { private: - // handle arbitration for each of the 5 request levels in parallel + // handle arbitration for each of the 5 device request levels in parallel priority_request_level_c request_levels[PRIORITY_LEVEL_COUNT]; + // access of master CPU to memory not handled via priority arbitration + dma_request_c *cpu_data_transfer_request ; // needs no link to CPU + pthread_mutex_t requests_mutex; void worker_init_event(void); void worker_power_event(void); void worker_deviceregister_event(void); - void worker_dma_chunk_complete_event(void); + void worker_dma_chunk_complete_event(bool cpu_DATA_transfer); void worker_intr_complete_event(uint8_t level_index); void worker(unsigned instance) override; // background worker function @@ -99,8 +102,9 @@ public: uint32_t unibus_addr, uint16_t *buffer, uint32_t wordcount); void INTR(intr_request_c& intr_request, unibusdevice_register_t *interrupt_register, uint16_t interrupt_register_value); - void cancel_INTR(intr_request_c& intr_request) ; - + void cancel_INTR(intr_request_c& intr_request); + + void cpu_DATA_transfer(dma_request_c& dma_request, uint8_t unibus_control, uint32_t unibus_addr, uint16_t *buffer); void print_shared_register_map(void); diff --git a/10.01_base/2_src/pru1/pru1_main_test.c b/10.01_base/2_src/pru1/pru1_main_test.c index a698e93..7aa8f92 100644 --- a/10.01_base/2_src/pru1/pru1_main_test.c +++ b/10.01_base/2_src/pru1/pru1_main_test.c @@ -30,7 +30,7 @@ from d:\RetroCmp\dec\pdp11\UniBone\91_3rd_party\pru-c-compile\pru-software-support-package\examples\am335x\PRU_gpioToggle Test GPIO, shared mem and interrupt a) waits until ARM writes a value to mailbox.arm2pru_req - b) ACKs the value in mailbox.arm2pru_resp, clears arm2pru_req + b) ACKs with clear of arm2pru_req c) toggles 1 mio times GPIO, with delay as set by ARM d) signal EVENT0 e) goto a @@ -82,12 +82,6 @@ void main(void) { while (1) { // display opcode (active for one cycle // __R30 = (mailbox.arm2pru_req & 0xf) << 8; - /* - mailbox.arm2pru_resp = mailbox.arm2pru_req ; - __R30 = (mailbox.arm2pru_resp & 0xf) << 8; - mailbox.arm2pru_resp = mailbox.arm2pru_req ; - } - */ /*** Attention: arm2pru_req (and all mailbox vars) change at ANY TIME * - ARM must set arm2pru_req as last operation on mailbox, * memory barrier needed. diff --git a/10.01_base/2_src/pru1/pru1_main_unibus.c b/10.01_base/2_src/pru1/pru1_main_unibus.c index 5ec1305..6d0c201 100644 --- a/10.01_base/2_src/pru1/pru1_main_unibus.c +++ b/10.01_base/2_src/pru1/pru1_main_unibus.c @@ -36,7 +36,7 @@ from d:\RetroCmp\dec\pdp11\UniBone\91_3rd_party\pru-c-compile\pru-software-support-package\examples\am335x\PRU_gpioToggle Test GPIO, shared mem and interrupt a) waits until ARM writes a value to mailbox.arm2pru_req - b) ACKs the value in mailbox.arm2pru_resp, clears arm2pru_req + b) ACKs with clear of arm2pru_req c) toggles 1 mio times GPIO, with delay as set by ARM d) signal EVENT0 e) goto a @@ -126,7 +126,8 @@ void main(void) { // fast: a complete slave data cycle if (!sm_data_slave_state) sm_data_slave_state = (statemachine_state_func) &sm_slave_start; - while ((sm_data_slave_state = sm_data_slave_state()) && !mailbox.events.event_deviceregister) + while ((sm_data_slave_state = sm_data_slave_state()) + && !mailbox.events.event_deviceregister) // throws signals to ARM, // Acess to interna lregsitres may may issue AMR2PRU opcode, so exit loop then ;// execute complete slave cycle, then check NPR/INTR @@ -196,19 +197,32 @@ void main(void) { mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done break; case ARM2PRU_DMA: - // request INTR, arbitrator must've been selected with ARM2PRU_ARB_MODE_* - sm_arb.request_mask |= PRIORITY_ARBITRATION_BIT_NP; - // sm_arb_worker() evaluates this,extern Arbitrator raises Grant, excution starts in future loop - // end of DMA is signaled to ARM with signal + if (mailbox.arbitrator.cpu_BBSY) { + // ARM CPU emulation did a single word DATA transfer with cpu_DATA_transfer() + if (mailbox.arbitrator.device_BBSY) { + // ARM started cpu DATA transfer, but arbitration logic + // GRANTed a device request in the mean time. Tell ARM. + mailbox.arm2pru_req = PRU2ARM_DMA_CPU_TRANSFER_BLOCKED; // error + mailbox.arbitrator.cpu_BBSY = false; + } + // start bus cycle + sm_data_master_state = (statemachine_state_func) &sm_dma_start; + } else { + // Emulated device: raise request for emulated or physical Arbitrator. + // request DMA, arbitrator must've been selected with ARM2PRU_ARB_MODE_* + sm_arb.request_mask |= PRIORITY_ARBITRATION_BIT_NP; + // sm_arb_worker() evaluates this,extern Arbitrator raises Grant, excution starts in future loop + // end of DMA is signaled to ARM with signal - /* TODO: speed up DMA - While DMA is active: - - SACK active: no GRANT forward necessary - no arbitration necessary - - INIT is monitored: no DC_LO/INIT monitoring necessary - - no scan for new ARM2PRU commands: ARM2PRU_DMA is blocking - - smaller chunks ? - */ + /* TODO: speed up DMA + While DMA is active: + - SACK active: no GRANT forward necessary + no arbitration necessary + - INIT is monitored: no DC_LO/INIT monitoring necessary + - no scan for new ARM2PRU commands: ARM2PRU_DMA is blocking + - smaller chunks ? + */ + } mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done break; case ARM2PRU_INTR: @@ -231,10 +245,10 @@ void main(void) { case ARM2PRU_INTR_CANCEL: // cancels an INTR request. If already Granted, the GRANT is forwarded, // and canceled by reaching a "SACK turnaround terminator" or "No SACK TIMEOUT" in the arbitrator. - sm_arb.request_mask &= ~ mailbox.intr.priority_arbitration_bit ; + sm_arb.request_mask &= ~mailbox.intr.priority_arbitration_bit; // no completion event, could interfer with othe INTRs? mailbox.arm2pru_req = ARM2PRU_NONE; // done - break ; + break; case ARM2PRU_INITPULSE: if (!sm_init_state) sm_init_state = (statemachine_state_func) &sm_init_start; @@ -247,8 +261,8 @@ void main(void) { break; case ARM2PRU_HALT: mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done - __halt() ; // LA: trigger on timeout of REG_WRITE - break ; + __halt(); // LA: trigger on timeout of REG_WRITE + break; } } diff --git a/10.01_base/2_src/pru1/pru1_statemachine_arbitration.c b/10.01_base/2_src/pru1/pru1_statemachine_arbitration.c index deba29a..bd7cfe9 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_arbitration.c +++ b/10.01_base/2_src/pru1/pru1_statemachine_arbitration.c @@ -59,7 +59,7 @@ if PRU detects BGIN which was requests, it "blocks the GRANT" )sets SACK and transmit the INT (BG*) or becomes "no interrupt request while NPR transfer active!" - Meaning: bus masterchip acquired by NPG may not be used to transmit an + Meaning: bus mastership acquired by NPG may not be used to transmit an INTR vector. Device may take bus if BBSY==0 && SSYN==0 && NPG==0 @@ -68,8 +68,8 @@ BBSY is set before SACK is released. SACK is relased imemdiatley after BBSY, enabling next arbitration in parallel to curretn data transfer "Only the device with helds SACk asserted can assert BBSY - - + + Several arbitration "workers" which set request, monitor or generate GRANT signals and allocate SACK. Which worker to use depends on wether a physical PDP-11 CPU is Arbitrator, @@ -85,20 +85,24 @@ #include "pru1_utils.h" #include "mailbox.h" +#include "pru1_timeouts.h" #include "pru1_buslatches.h" -#include "pru1_statemachine_arbitration.h" +#include "pru1_statemachine_arbitration.h" statemachine_arbitration_t sm_arb; /********** NPR/NPG/SACK arbitrations **************/ -// to be called on INIT signal: abort the abritration rpcoess +// to be called on INIT signal: abort the arbitration process void sm_arb_reset() { // cleanup: clear all REQUESTS and SACK buslatches_setbits(1, PRIORITY_ARBITRATION_BIT_MASK | BIT(5), 0); sm_arb.request_mask = 0; sm_arb.bbsy_wait_grant_mask = 0; + + sm_arb.arbitrator_grant_mask = 0; + timeout_cleanup(TIMEOUT_SACK); } /* sm_arb_workers_*() @@ -114,7 +118,8 @@ void sm_arb_reset() { uint8_t sm_arb_worker_none() { // Unconditionally forward GRANT IN to GRANT OUT uint8_t grant_mask = buslatches_getbyte(0) & PRIORITY_ARBITRATION_BIT_MASK; // read GRANT IN - buslatches_setbits(0, PRIORITY_ARBITRATION_BIT_MASK, ~grant_mask) ; + buslatches_setbits(0, PRIORITY_ARBITRATION_BIT_MASK, ~grant_mask) + ; // ignore BR* INTR requests, only ack DMA. if (sm_arb.request_mask & PRIORITY_ARBITRATION_BIT_NP) { @@ -124,10 +129,10 @@ uint8_t sm_arb_worker_none() { return 0; } -/* worker_client: +/* worker_client: Issue request to extern Arbitrator (PDP-11 CPU). Watch for GRANTs on the bus signal lines, then raise SACK. - Wait for current bus master to release bus => Wait for BBSY clear. + Wait for current bus master to release bus => Wait for BBSY clear. Then return GRANTed request. "Wait for BBSY clear" may not be part of the arbitration protocol. But it guarantees caller may now issue an DMA or INTR. @@ -140,7 +145,7 @@ uint8_t sm_arb_worker_client() { ; // read GRANT IN lines from CPU (Arbitrator). Only one at a time may be active // Arbitrator asserts SACK is inactive - // latch[0]: BG signals are inverted, "get" is non-inverting: bit = active signal. + // latch[0]: BG signals are inverted, "get" is non-inverting: bit = active signal. // "set" is inverting! grant_mask = buslatches_getbyte(0) & PRIORITY_ARBITRATION_BIT_MASK; // read GRANT IN // forward un-requested GRANT IN to GRANT OUT for other cards on neighbor slots @@ -148,27 +153,17 @@ uint8_t sm_arb_worker_client() { ; if (sm_arb.bbsy_wait_grant_mask == 0) { - // State 1: Wait For GRANT: + // State 1: Wait For GRANT: // process the requested grant for an open requests. grant_mask &= sm_arb.request_mask; if (grant_mask) { - // one of our requests was granted: + // one of our requests was granted: // set SACK // AND simultaneously Clear granted requests BR*/NPR // BIT(5): SACK mask and level - buslatches_setbits(1, (PRIORITY_ARBITRATION_BIT_MASK & sm_arb.request_mask) | BIT(5), - ~grant_mask | BIT(5)) + buslatches_setbits(1, (PRIORITY_ARBITRATION_BIT_MASK & sm_arb.request_mask) | BIT(5), + ~grant_mask | BIT(5)) ; - /* - buslatches_setbits(1, BIT(5), BIT(5)); - // Arbitrator now clears GRANT IN - - // clear granted requests BR* / NPR on UNIBUS - // "~": disable BR for set BG - buslatches_setbits(1, PRIORITY_ARBITRATION_BIT_MASK & sm_arb.request_mask, ~grant_mask) - ; - */ - // clear granted requests internally sm_arb.request_mask &= ~grant_mask; @@ -190,13 +185,117 @@ uint8_t sm_arb_worker_client() { } /* "worker_master" - Act as Arbitrator. + Act as Arbitrator, Interrupt Fielding Processor and Client + Is assumed to be on first slot, so BG*IN/NPGIN lines are ignored + BR/NPR are set in device request, as in worker_client() + Grant highest of requests, if SACK deasserted. Execute UNIBUS priority algorithm: - Grant DMA request, if present - GRANT BR* in descending priority, when CPU execution level allows . + - Cancel GRANT, if no device responds with SACK within timeout period */ + +// decode set of requests lines to highest INTR level +// BR4 = 0x01 -> 4, BR5 = 0x02 -> 5, etc. +// Index only PRIORITY_ARBITRATION_INTR_MASK, [0] invalid. +static const uint8_t requests_2_highests_intr[16] = { // + /*0000*/9, /*0001*/4, /*0010*/5, /*0011*/5, + /*0100*/6, /*0101*/6, /*0110*/6, /*0111*/6, + /*1000*/7, /*1001*/7, /*1010*/7, /*0011*/7, + /*1100*/7, /*1101*/7, /*1110*/7, /*0111*/7 }; + uint8_t sm_arb_worker_master() { - return 0; + /******* arbitrator logic *********/ + uint8_t intr_request_mask; + uint8_t latch1val = buslatches_getbyte(1); + + if (latch1val & BIT(5)) { + // SACK set by a device + // priority arbitration disabled, remove GRANT. + sm_arb.arbitrator_grant_mask = 0; + + // CPU looses now access to UNIBUS after current cycle + mailbox.arbitrator.device_BBSY = 1; // DATA section used by device now + + timeout_cleanup(TIMEOUT_SACK); + } else if (latch1val & PRIORITY_ARBITRATION_BIT_NP) { + // device NPR +PRU_DEBUG_PIN_PULSE_100NS ; + if (sm_arb.arbitrator_grant_mask == 0) { +PRU_DEBUG_PIN_PULSE_100NS ; + + // no 2nd device's request may modify GRANT before 1st device acks with SACK + sm_arb.arbitrator_grant_mask = PRIORITY_ARBITRATION_BIT_NP; + timeout_set(TIMEOUT_SACK, MILLISECS(ARB_MASTER_SACK_TIMOUT_MS)); + } + } else if ((intr_request_mask = latch1val & PRIORITY_ARBITRATION_INTR_MASK)) { + // device BR4,BR5,BR6 or BR7 + if (sm_arb.arbitrator_grant_mask == 0) { + // no 2nd device's request may modify GRANT before 1st device acks with SACK + // GRANT request depending on CPU priority level + // find highest request line + uint8_t requested_intr_level = requests_2_highests_intr[intr_request_mask]; + + // compare against cpu run level 4..7 + if (requested_intr_level > mailbox.arbitrator.cpu_priority_level) { + // GRANT request, set GRANT line: + // BG4 is signal bit maskl 0x01, etc ... + sm_arb.arbitrator_grant_mask = BIT(requested_intr_level - 4); + timeout_set(TIMEOUT_SACK, MILLISECS(ARB_MASTER_SACK_TIMOUT_MS)); + } + } + } else if (sm_arb.arbitrator_grant_mask && timeout_reached(TIMEOUT_SACK)) { + // no SACK, no requests, but GRANTs: SACK timeout? + sm_arb.arbitrator_grant_mask = 0; + mailbox.arbitrator.device_BBSY = 0; // started by SACK, but UNIBUS DATA section not used by device + timeout_cleanup(TIMEOUT_SACK); + } + + /***** client device logic *****/ + // Always update UNIBUS BR/NPR lines, are ORed with requests from other devices. + buslatches_setbits(1, PRIORITY_ARBITRATION_BIT_MASK, sm_arb.request_mask) + ; + + // read GRANT IN lines from CPU (Arbitrator). Only one at a time may be active + // Arbitrator asserts SACK is inactive + uint8_t grant_mask = sm_arb.arbitrator_grant_mask; + // forward GRANTs not requested by UniBone devices to other cards on neighbor slots + buslatches_setbits(0, PRIORITY_ARBITRATION_BIT_MASK & ~sm_arb.request_mask, ~grant_mask) + ; + + // GRANTs for UniBone internal devices are not visible on UNIBUS + // (emualted GRANT OUT - GRANT IN connections) + if (sm_arb.bbsy_wait_grant_mask == 0) { + // State 1: Wait For GRANT: + // process the requested grant for an open requests. + grant_mask &= sm_arb.request_mask; + if (grant_mask) { + // one of our requests was granted: + // set SACK + // AND simultaneously Clear granted requests BR*/NPR + // BIT(5): SACK mask and level + buslatches_setbits(1, (PRIORITY_ARBITRATION_BIT_MASK & sm_arb.request_mask) | BIT(5), + ~grant_mask | BIT(5)) + ; + + // clear granted requests internally + sm_arb.request_mask &= ~grant_mask; + // Arbitrator should remove GRANT now. Data section on Bus still BBSY + sm_arb.bbsy_wait_grant_mask = grant_mask; // next is State 2: wait for BBSY clear + } + return 0; // no GRANT for us, or wait for BBSY + } else { + // wait for BBSY to clear + if (buslatches_getbyte(1) & BIT(6)) + return 0; // BBSY still set + grant_mask = sm_arb.bbsy_wait_grant_mask; + sm_arb.bbsy_wait_grant_mask = 0; // Next State is 1 + + return grant_mask; // signal what request we got granted. + } + // UNIBUS DATA section is indepedent: MSYN, SSYN, BBSY may still be active. + // -> DMA and INTR statemachine must wait for BBSY. + } diff --git a/10.01_base/2_src/pru1/pru1_statemachine_arbitration.h b/10.01_base/2_src/pru1/pru1_statemachine_arbitration.h index 84f8cd5..3e2dd5d 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_arbitration.h +++ b/10.01_base/2_src/pru1/pru1_statemachine_arbitration.h @@ -28,6 +28,10 @@ #include +// Arbitration master restes GRANT if devcie does not respond with +// SACK within this period. +#define ARB_MASTER_SACK_TIMOUT_MS 10 + // a priority-arbitration-worker returns a bit mask with the GRANT signal he recognized typedef uint8_t (*statemachine_arb_worker_func)(); @@ -43,9 +47,14 @@ typedef struct { // for BBSY clear. // 0: not waitong for BBSY. // != saves GRANTed reqiest and idnicates BBSY wait state - uint8_t bbsy_wait_grant_mask ; + uint8_t bbsy_wait_grant_mask; + + /*** master ****/ + // only used wif working as Arbitrator/Interupt Fielding Processor + uint8_t ifs_priority_level; // priority level of Interrupt Fielding processor (CPU) + + uint8_t arbitrator_grant_mask; // GRANT line set by master - } statemachine_arbitration_t; /* receives a grant_mask with 1 bit set and returns the index of that bit diff --git a/10.01_base/2_src/pru1/pru1_statemachine_dma.c b/10.01_base/2_src/pru1/pru1_statemachine_dma.c index c9af48d..f49316e 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_dma.c +++ b/10.01_base/2_src/pru1/pru1_statemachine_dma.c @@ -84,6 +84,8 @@ static statemachine_state_func sm_dma_state_99(void); // startaddr, wordcount, cycle, words[] ? // "cycle" must be UNIBUS_CONTROL_DATI or UNIBUS_CONTROL_DATO // Wait for BBSY, SACK already held asserted +// Sorting between device and CPU transfers: +// IF a device DMA is busy mailbox.device_BBSY is set. statemachine_state_func sm_dma_start() { // assert BBSY: latch[1], bit 6 // buslatches_setbits(1, BIT(6), BIT(6)); @@ -100,15 +102,14 @@ statemachine_state_func sm_dma_start() { } /* -// wait for BBSY deasserted, then assert -static statemachine_state_func sm_dma_state_1() { - if (buslatches_getbyte(1) & BIT(6)) - return (statemachine_state_func) &sm_dma_state_1; // wait - buslatches_setbits(1, BIT(6), BIT(6)); // assert BBSY - return (statemachine_state_func) &sm_dma_state_1; -} -*/ - + // wait for BBSY deasserted, then assert + static statemachine_state_func sm_dma_state_1() { + if (buslatches_getbyte(1) & BIT(6)) + return (statemachine_state_func) &sm_dma_state_1; // wait + buslatches_setbits(1, BIT(6), BIT(6)); // assert BBSY + return (statemachine_state_func) &sm_dma_state_1; + } + */ // place address and control bits onto bus, also data for DATO // If slave address is internal (= implemented by UniBone), @@ -329,16 +330,21 @@ static statemachine_state_func sm_dma_state_99() { buslatches_setbits(4, 0x3f, 0); // remove BBSY: latch[1], bit 6 buslatches_setbits(1, BIT(6), 0); - // SACK already de-asserted at wordcount==1 - timeout_cleanup(TIMEOUT_DMA); - mailbox.dma.cur_status = final_dma_state; // signal to ARM - timeout_cleanup(TIMEOUT_DMA); + timeout_cleanup(TIMEOUT_DMA); + + // device or cpu cycle ended: now CPU may become UNIBUS master again + mailbox.events.event_dma_cpu_transfer = mailbox.arbitrator.cpu_BBSY ; + mailbox.arbitrator.device_BBSY = false; + mailbox.arbitrator.cpu_BBSY = false; + + // SACK already de-asserted at wordcount==1 + mailbox.dma.cur_status = final_dma_state; // signal to ARM // signal to ARM mailbox.events.event_dma = 1; // ARM is clearing this, before requesting new DMA. - // no concurrent ARP+PRU access + // no concurrent ARM+PRU access PRU2ARM_INTERRUPT ; diff --git a/10.01_base/2_src/pru1/pru1_statemachine_intr.c b/10.01_base/2_src/pru1/pru1_statemachine_intr.c index ba169cd..55edfe6 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_intr.c +++ b/10.01_base/2_src/pru1/pru1_statemachine_intr.c @@ -33,6 +33,7 @@ #include #include +#include //#include "devices.h" #include "mailbox.h" @@ -97,6 +98,9 @@ static statemachine_state_func sm_intr_state_2() { // deassert BBSY buslatches_setbits(1, BIT(6), 0); + // device cycle ended: now CPU may become UNIBUS master again + mailbox.arbitrator.device_BBSY = false ; + // SACK already removed // signal to ARM which INTR was completed diff --git a/10.01_base/2_src/pru1/pru1_timeouts.c b/10.01_base/2_src/pru1/pru1_timeouts.c index b89371a..06939b7 100644 --- a/10.01_base/2_src/pru1/pru1_timeouts.c +++ b/10.01_base/2_src/pru1/pru1_timeouts.c @@ -79,7 +79,7 @@ void timeout_set(uint32_t *target_cycles_var, uint32_t delta_cycles) { timeouts_active++; // now one more active } -// msut be called, if timeout polled anymore for "timeout-reached() +// must be called, if timeout not polled anymore for "timeout-reached() void timeout_cleanup(uint32_t *target_cycles_var) { if (*target_cycles_var > 0) { *target_cycles_var = 0; diff --git a/10.01_base/2_src/pru1/pru1_timeouts.h b/10.01_base/2_src/pru1/pru1_timeouts.h index 717a533..4ce669a 100644 --- a/10.01_base/2_src/pru1/pru1_timeouts.h +++ b/10.01_base/2_src/pru1/pru1_timeouts.h @@ -29,12 +29,13 @@ #include // predefined timeouts -#define TIMEOUT_COUNT 3 +#define TIMEOUT_COUNT 4 // fixed pointers #define TIMEOUT_DMA (&timeout_target_cycles[0]) #define TIMEOUT_INIT (&timeout_target_cycles[1]) #define TIMEOUT_POWERCYCLE (&timeout_target_cycles[2]) +#define TIMEOUT_SACK (&timeout_target_cycles[3]) // cycle end count for each active timeoput. extern uint32_t timeout_target_cycles[TIMEOUT_COUNT]; diff --git a/10.01_base/2_src/shared/mailbox.h b/10.01_base/2_src/shared/mailbox.h index aa0fa9d..1ab0200 100644 --- a/10.01_base/2_src/shared/mailbox.h +++ b/10.01_base/2_src/shared/mailbox.h @@ -46,10 +46,11 @@ #define ARM2PRU_ARB_MODE_CLIENT 12 // DMA with arbitration by external Arbitrator #define ARM2PRU_ARB_MODE_MASTER 13 // DMA as Arbitrator #define ARM2PRU_DMA 14 // DMA with selcted arbitration -#define ARM2PRU_INTR 15 // INTR with arbitration by external Arbitrator -#define ARM2PRU_INTR_CANCEL 16 // clear INTR which has been requested -#define ARM2PRU_DDR_FILL_PATTERN 17 // fill DDR with test pattern -#define ARM2PRU_DDR_SLAVE_MEMORY 18 // use DDR as UNIBUS slave memory +#define PRU2ARM_DMA_CPU_TRANSFER_BLOCKED 15 // possible result of ARM2PRU_DMA +#define ARM2PRU_INTR 16 // INTR with arbitration by external Arbitrator +#define ARM2PRU_INTR_CANCEL 17 // clear INTR which has been requested +#define ARM2PRU_DDR_FILL_PATTERN 18 // fill DDR with test pattern +#define ARM2PRU_DDR_SLAVE_MEMORY 19 // use DDR as UNIBUS slave memory // possible states of DMA machine #define DMA_STATE_READY 0 // idle @@ -106,10 +107,30 @@ typedef struct { uint8_t data_8_15; } mailbox_buslatch_test_t; + +// data for bus arbitrator +typedef struct { + // arbitrator.device_BBSY indicates a device wants or has acquired the UNIBUS + // cpu DATA transfer must be delayed until == 00 + // set by arbitration logic + uint8_t device_BBSY ; + + // Command by ARM on DMA start: DATA transfer as CPU, else as device + uint8_t cpu_BBSY ; + + uint8_t cpu_priority_level ; // Priority level of CPU, visible in PSW. 7,6,5,4 <4. + + uint8_t _dummy1 ; // keep 32 bit borders + +} mailbox_arbitrator_t ; + + // data for a requested DMA operation typedef struct { // take care of 32 bit word borders for struct members - uint8_t cur_status; // 0 = idle, 1 = running, 2 = timeout error + uint8_t cur_status; // 0 = idle, 1 = DMA running, 2 = timeout error + // 0x80: set on start to indicate CPU access + uint8_t control; // cycle to perform: only DATO, DATI allowed uint16_t wordcount; // # of remaining words transmit/receive, static // ---dword--- @@ -167,12 +188,12 @@ typedef struct { After that, mailbox_dma_t is updated and signal raised. */ uint8_t event_dma; // trigger flag + uint8_t event_dma_cpu_transfer ; // 1: ARM must process DMa as compelted cpu DATA transfer /*** Event priority arbitration data transfer complete After ARM2PRU_INTR, one of BR4/5/6/7 NP was requested, granted, and the data transfer was handled as bus master. */ - uint8_t _dummy1; // ---dword--- uint8_t event_intr; // trigger flag: 1 = one of BR4,5,6,7 vector on UNIBUS uint8_t event_intr_level_index; // 0..3 -> BR4..BR7 @@ -193,11 +214,13 @@ typedef struct { // generic request/response flags uint32_t arm2pru_req; - uint32_t arm2pru_resp; // physical location of shared DDR memory. PDP-11 memory words. volatile ddrmem_t *ddrmem_base_physical; + mailbox_arbitrator_t arbitrator; + + // set by PRU, read by ARM on event mailbox_events_t events; @@ -230,7 +253,7 @@ extern volatile mailbox_t *mailbox; void mailbox_print(void); int mailbox_connect(void); void mailbox_test1(void); -void mailbox_execute(uint8_t request); +bool mailbox_execute(uint8_t request); #else // included by PRU code diff --git a/10.02_devices/2_src/cpu.cpp b/10.02_devices/2_src/cpu.cpp index 3664a7b..b3669e1 100644 --- a/10.02_devices/2_src/cpu.cpp +++ b/10.02_devices/2_src/cpu.cpp @@ -51,9 +51,7 @@ cpu_c::cpu_c() : default_base_addr = 0; // none default_intr_vector = 0; default_intr_level = 0; - priority_slot.value = 1 ; - - dma_request.set_priority_slot(priority_slot.value); + priority_slot.value = 0; // not used // init parameters runmode.value = false; @@ -91,24 +89,25 @@ extern "C" { // Result: 1 = OK, 0 = bus timeout int cpu_dato(unsigned addr, unsigned data) { uint16_t wordbuffer = (uint16_t) data; - unibusadapter->DMA(the_cpu->dma_request, true, UNIBUS_CONTROL_DATO, addr, &wordbuffer, 1); - return the_cpu->dma_request.success; + unibusadapter->cpu_DATA_transfer(the_cpu->data_transfer_request, UNIBUS_CONTROL_DATO, addr, &wordbuffer); + return the_cpu->data_transfer_request.success ; } int cpu_datob(unsigned addr, unsigned data) { uint16_t wordbuffer = (uint16_t) data; // TODO DATOB als 1 byte-DMA ! - unibusadapter->DMA(the_cpu->dma_request, true, UNIBUS_CONTROL_DATOB, addr, &wordbuffer, 1); - return the_cpu->dma_request.success; + unibusadapter->cpu_DATA_transfer(the_cpu->data_transfer_request, UNIBUS_CONTROL_DATOB, addr, &wordbuffer); + return the_cpu->data_transfer_request.success ; } int cpu_dati(unsigned addr, unsigned *data) { uint16_t wordbuffer; - unibusadapter->DMA(the_cpu->dma_request, true, UNIBUS_CONTROL_DATI, addr, &wordbuffer, 1); +unibusadapter->cpu_DATA_transfer(the_cpu->data_transfer_request, UNIBUS_CONTROL_DATI, addr, &wordbuffer); *data = wordbuffer; // printf("DATI; ba=%o, data=%o\n", addr, *data) ; - return the_cpu->dma_request.success; + + return the_cpu->data_transfer_request.success ; } } diff --git a/10.02_devices/2_src/cpu.hpp b/10.02_devices/2_src/cpu.hpp index 4ea43b0..802bd85 100644 --- a/10.02_devices/2_src/cpu.hpp +++ b/10.02_devices/2_src/cpu.hpp @@ -29,6 +29,7 @@ using namespace std; #include "utils.hpp" +#include "unibusadapter.hpp" #include "unibusdevice.hpp" extern "C" { #include "cpu20/11.h" @@ -46,8 +47,8 @@ public: cpu_c(); ~cpu_c(); - // CPU accesses memory actively - dma_request_c dma_request = dma_request_c(this); + // used for DATI/DATO, operated by unibusadapter + dma_request_c data_transfer_request = dma_request_c(this); bool on_param_changed(parameter_c *param) override; // must implement diff --git a/10.02_devices/3_test/rl02/xxdp.cmd b/10.02_devices/3_test/rl02/xxdp.cmd index ff63562..cf78eb4 100644 --- a/10.02_devices/3_test/rl02/xxdp.cmd +++ b/10.02_devices/3_test/rl02/xxdp.cmd @@ -24,7 +24,7 @@ p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" p powerswitch 1 # power on, now in "load" state -p image scratch2.rl02 # mount image file with test pattern +p image scratch0.rl02 # mount image file with test pattern p runstopbutton 1 # press RUN/STOP, will start #.end diff --git a/10.03_app_demo/2_src/menu_devices.cpp b/10.03_app_demo/2_src/menu_devices.cpp index 61f0af8..966dbb6 100644 --- a/10.03_app_demo/2_src/menu_devices.cpp +++ b/10.03_app_demo/2_src/menu_devices.cpp @@ -122,10 +122,10 @@ void application_c::menu_devices(bool with_CPU) { char *s_choice; char s_opcode[256], s_param[3][256]; - // with_CPU: the emulated CPU is answering BR and NPR requests. + // with_CPU: PRU Arbitrator is answering BR and NPR requests. if (with_CPU) - arbitration_mode = unibus_c::ARBITRATION_MODE_NONE; -// arbitration_mode = unibus_c::ARBITRATION_MODE_MASTER; + arbitration_mode = unibus_c::ARBITRATION_MODE_MASTER; +// arbitration_mode = unibus_c::ARBITRATION_MODE_NONE; else arbitration_mode = unibus_c::ARBITRATION_MODE_CLIENT; @@ -138,6 +138,10 @@ void application_c::menu_devices(bool with_CPU) { // now PRU executing UNIBUS master/slave code, physical PDP-11 CPU as arbitrator required. buslatches_output_enable(true); + // without PDP-11 CPU no INIT after power ON was generated. + // Devices may trash the bus lines. + unibus->init(); + unibusadapter->enabled.set(true); // 2 demo controller @@ -229,9 +233,9 @@ void application_c::menu_devices(bool with_CPU) { printf("d Deposit octal value into named device register\n"); printf("e Examine single device register (regno decimal)\n"); printf("e Examine all device registers\n"); - printf("e Examine octal UNIBUS address.\n"); - printf("d Deposit octal val into UNIBUS address.\n"); } + printf("e Examine octal UNIBUS address.\n"); + printf("d Deposit octal val into UNIBUS address.\n"); if (DL11->enabled.value) { printf( "dl11 rcv [] inject characters as if DL11 received them.\n"); @@ -413,10 +417,12 @@ void application_c::menu_devices(bool with_CPU) { p->parse(sval); print_params(cur_device, p); } - } else if (unibuscontroller && !strcasecmp(s_opcode, "d") && n_fields == 3) { + } else if (!strcasecmp(s_opcode, "d") && n_fields == 3) { uint32_t addr; uint16_t wordbuffer; - unibusdevice_register_t *reg = unibuscontroller->register_by_name(s_param[0]); + unibusdevice_register_t *reg = NULL ; + if (unibuscontroller) + reg = unibuscontroller->register_by_name(s_param[0]); if (reg) // register name given addr = reg->addr; else @@ -437,14 +443,14 @@ void application_c::menu_devices(bool with_CPU) { printf("DEPOSIT %06o <- %06o\n", addr, wordbuffer); if (timeout) printf("Bus timeout at %06o.\n", mailbox->dma.cur_addr); - } else if (unibuscontroller && !strcasecmp(s_opcode, "e") && n_fields <= 2) { + } else if (!strcasecmp(s_opcode, "e") && n_fields <= 2) { bool timeout; uint32_t addr; - - unibusdevice_register_t *reg; - if (n_fields == 2) { // single reg number given + unibusdevice_register_t *reg = NULL ; + if (n_fields == 2) { // single reg number or address given uint16_t wordbuffer; // exam single word - reg = unibuscontroller->register_by_name(s_param[0]); + if (unibuscontroller) + reg = unibuscontroller->register_by_name(s_param[0]); if (reg) addr = reg->addr; else @@ -452,7 +458,7 @@ void application_c::menu_devices(bool with_CPU) { timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATI, addr, &wordbuffer, 1); printf("EXAM %06o -> %06o\n", addr, wordbuffer); - } else { // list all regs + } else if (n_fields == 1 && unibuscontroller) { // list all regs unsigned wordcount = 0; // default: no EXAM uint16_t wordbuffer[MAX_REGISTERS_PER_DEVICE]; addr = unibuscontroller->base_addr.value; // all device registers @@ -471,6 +477,9 @@ void application_c::menu_devices(bool with_CPU) { timeout = false; printf("Device has no UNIBUS registers.\n"); } + } else { + // no device: no "display all" + show_help = true; } if (timeout) printf("Bus timeout at %06o.\n", mailbox->dma.cur_addr);