mirror of
https://github.com/livingcomputermuseum/UniBone.git
synced 2026-02-27 01:00:00 +00:00
Infrastructure for emulated CPUs: Bus arbitrator, Interrupt fielding processor
This commit is contained in:
@@ -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) ;
|
||||
}
|
||||
|
||||
@@ -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<dma_request_c *>(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<dma_request_c *>(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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// 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
|
||||
|
||||
@@ -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
|
||||
;
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
//#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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -29,12 +29,13 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
// 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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 <regname> <val> Deposit octal value into named device register\n");
|
||||
printf("e <regname> Examine single device register (regno decimal)\n");
|
||||
printf("e Examine all device registers\n");
|
||||
printf("e <addr> Examine octal UNIBUS address.\n");
|
||||
printf("d <addr> <val> Deposit octal val into UNIBUS address.\n");
|
||||
}
|
||||
printf("e <addr> Examine octal UNIBUS address.\n");
|
||||
printf("d <addr> <val> Deposit octal val into UNIBUS address.\n");
|
||||
if (DL11->enabled.value) {
|
||||
printf(
|
||||
"dl11 rcv [<wait_ms>] <string> 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);
|
||||
|
||||
Reference in New Issue
Block a user