mirror of
https://github.com/livingcomputermuseum/UniBone.git
synced 2026-04-28 04:46:19 +00:00
337 lines
13 KiB
C
337 lines
13 KiB
C
/* pru1_main_unibus.c: main loop with mailbox cmd interface. UNIBUS devices with opt. phys. PDP-11 CPU.
|
|
|
|
Copyright (c) 2018-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.
|
|
|
|
|
|
28-mar-2019 JH split off from "all-function" main
|
|
12-nov-2018 JH entered beta phase
|
|
|
|
Master and slave functionality for UNIBUS devices.
|
|
Assumes a physical PDP-11 CPU is working as Arbitrator for
|
|
NPR/NG/SACK and BR/BG/SACK.
|
|
Needed if UniBone runs in a system running PDP-11 CPU
|
|
and simulated or physical devices do DMA or INTR.
|
|
|
|
Separated from "all-function" main() because of PRU code size limits.
|
|
Application has to load this into PRU1 depending on system state.
|
|
|
|
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 with clear of arm2pru_req
|
|
c) toggles 1 mio times GPIO, with delay as set by ARM
|
|
d) signal EVENT0
|
|
e) goto a
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <pru_cfg.h>
|
|
#include "resource_table_empty.h"
|
|
|
|
#include "pru1_utils.h"
|
|
#include "pru1_timeouts.h"
|
|
|
|
#include "pru_pru_mailbox.h"
|
|
#include "mailbox.h"
|
|
#include "ddrmem.h"
|
|
#include "iopageregister.h"
|
|
|
|
#include "pru1_buslatches.h"
|
|
#include "pru1_statemachine_arbitration.h"
|
|
#include "pru1_statemachine_dma.h"
|
|
#include "pru1_statemachine_data_slave.h"
|
|
#include "pru1_statemachine_intr_master.h"
|
|
#include "pru1_statemachine_intr_slave.h"
|
|
|
|
// supress warnigns about using void * as function pointers
|
|
// sm_slave_state = (statemachine_state_func)&sm_data_slave_start;
|
|
// while (sm_slave_state = sm_slave_state()) << usage
|
|
#pragma diag_push
|
|
#pragma diag_remark=515
|
|
|
|
/***
|
|
3 major states executed in circular 1- 2- 3 order.
|
|
|
|
1. "SLAVE":
|
|
UniBone monitoring BUS traffic as slave for DATI/DATO cycles
|
|
Watch UNIBUS for CPU access to emulated memory/devices
|
|
High speed not necessary: Bus master will wait with MSYN if UniBone not responding.
|
|
wathcin BG/BPG signals, catching requested GRANts and forwardinf
|
|
other GRANTS
|
|
- monitoring INIT and AC_LO/DC_LO
|
|
- watching fpr AMR2PRU commands
|
|
2. "BBSYWAIT": UniBone got PRIORITY GRAMT, has set SACK and released BR/NPR
|
|
waits for current BUS master to relaeasy BBSY (ony DATI/DATO cycle max)
|
|
- SACK active: no GRANT forward necessary, no arbitration necessary
|
|
- INIT is monitored by DMA statemachine: no DC_LO/INIT monitoring necessary
|
|
3. "MASTER": UniBone is Bus master: transfering INTR vector or doing DMA
|
|
- Own timing: transfer DMA data block or INTR vector with master cycles
|
|
- SACK active: no GRANT forward necessary, no arbitration necessary
|
|
- INIT is monitored by DMA statemachine: no DC_LO/INIT monitoring necessary
|
|
*/
|
|
|
|
void main(void) {
|
|
// state function pointer for different state machines
|
|
statemachine_arb_worker_func sm_device_arb_worker = &sm_arb_worker_device;
|
|
statemachine_state_func sm_data_slave_state = NULL;
|
|
statemachine_state_func sm_data_master_state = NULL;
|
|
statemachine_state_func sm_intr_slave_state = NULL;
|
|
// these are function pointers: could be 16bit on PRU?
|
|
|
|
bool emulate_cpu = false;
|
|
|
|
/* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */
|
|
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
|
|
|
|
timeout_init();
|
|
|
|
// clear all tables, as backup if ARM fails todo
|
|
iopageregisters_init();
|
|
|
|
buslatches_reset(); // all deasserted, caches cleared
|
|
|
|
/* ARM must init mailbox, especially:
|
|
mailbox.arm2pru_req = ARM2PRU_NONE;
|
|
mailbox.events.eventmask = 0;
|
|
mailbox.events.initialization_signals_prev = 0;
|
|
mailbox.events.initialization_signals_cur = 0;
|
|
*/
|
|
|
|
sm_arb_reset();
|
|
|
|
while (true) {
|
|
uint8_t arm2pru_req_cached;
|
|
uint8_t cpu_grant_mask; // GRANts generated by physical or emulated CPU
|
|
|
|
if (sm_data_master_state == NULL) {
|
|
|
|
// State 1 "SLAVE"
|
|
|
|
// DATA or INTR for CPU?
|
|
|
|
// fast: a complete slave data cycle
|
|
if (!sm_data_slave_state)
|
|
sm_data_slave_state = (statemachine_state_func) &sm_data_slave_start;
|
|
while ((sm_data_slave_state = sm_data_slave_state())
|
|
&& EVENT_IS_ACKED(mailbox, deviceregister))
|
|
// throws signals to ARM,
|
|
// Acess to internal registers may issue AMR2PRU opcode, so exit loop then
|
|
;// execute complete slave cycle, then check NPR/INTR
|
|
|
|
// signal INT or PWR FAIL to ARM
|
|
// before arb_worker(), so BR/NPR requests are canceled on INIT
|
|
do_event_initializationsignals();
|
|
|
|
if (!emulate_cpu) {
|
|
// Fast forward device requests GRANTed by physical CPU.
|
|
// (On 11/84 possible SACK timeout when blocked by device register event?)
|
|
// Only one GRANT at a time may be active, else arbitrator malfunction.
|
|
// Arbitrator asserts SACK is inactive
|
|
// latch[0]: BG signals are inverted, "get" is non-inverting: bit = active signal.
|
|
// "set" is inverting!
|
|
cpu_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
|
|
sm_arb.device_forwarded_grant_mask = cpu_grant_mask
|
|
& ~sm_arb.device_request_signalled_mask;
|
|
buslatches_setbits(0, PRIORITY_ARBITRATION_BIT_MASK, ~sm_arb.device_forwarded_grant_mask)
|
|
;
|
|
// "A device may not accept a grant (assert SACK) after it passes the grant"
|
|
}
|
|
|
|
// Priority Arbitration
|
|
// Delay INTR or DMA while BUS halted via SSYN.
|
|
// ARM may start DMA within deviceregister event!
|
|
if (EVENT_IS_ACKED(mailbox, deviceregister)) {
|
|
// execute one of the arbitration workers
|
|
|
|
if (emulate_cpu) {
|
|
// handle GRANT/SACK/BBSY for emulated devices
|
|
cpu_grant_mask = sm_arb_worker_cpu(); // GRANT device requests
|
|
// do not read GRANT signals from UNIBUS, BG/NPGOUT not visible for
|
|
// emulated devices
|
|
// sm_arb.device_forwarded_grant_mask = 0 ;
|
|
} // else GRANTEd by physical CPU, see above
|
|
|
|
uint8_t granted_request = sm_device_arb_worker(cpu_grant_mask); // devices process GRANT
|
|
// sm_device_arb_worker()s include State 2 "BBSYWAIT".
|
|
// So now SACK maybe set, even if grant_mask is still 0
|
|
|
|
if (granted_request & PRIORITY_ARBITRATION_BIT_NP) {
|
|
sm_data_master_state = (statemachine_state_func) &sm_dma_start;
|
|
// can data_master_state be overwritten in the midst of a running data_master_state ?
|
|
// no: when running, SACK is set, no new GRANTs
|
|
} else if (granted_request & PRIORITY_ARBITRATION_INTR_MASK) {
|
|
// convert bit in grant_mask to INTR index
|
|
uint8_t idx = PRIORITY_ARBITRATION_INTR_BIT2IDX(granted_request);
|
|
// now transfer INTR vector for interupt of GRANted level.
|
|
// vector and ARM context have been setup by ARM before ARM2PRU_INTR already
|
|
sm_intr_master.vector = mailbox.intr.vector[idx];
|
|
sm_intr_master.level_index = idx; // to be returned to ARM on complete
|
|
|
|
sm_data_master_state = (statemachine_state_func) &sm_intr_master_start;
|
|
}
|
|
}
|
|
} else {
|
|
// State 3 "MASTER"
|
|
// we have been GRANTed bus mastership and are doing DMA or INTR
|
|
// SACK held here -> no further arbitration
|
|
// INTR is only 1 cycle, DMA has SACK set all the time, arbitration
|
|
// prohibited then.
|
|
|
|
sm_data_master_state = sm_data_master_state(); // execute only ONE state ,
|
|
// else DMA blocks will block processing of other state machines
|
|
// throws signals to ARM, causes may issue mailbox.arm2pru_req
|
|
}
|
|
|
|
if (emulate_cpu) {
|
|
// Receive INTR from physical or emulated devices, and signal ARM.
|
|
if (!sm_intr_slave_state)
|
|
sm_intr_slave_state = (statemachine_state_func) &sm_intr_slave_start;
|
|
sm_intr_slave_state = sm_intr_slave_state();
|
|
/*
|
|
while ((sm_intr_slave_state = sm_intr_slave_state())
|
|
&& EVENT_IS_ACKED(mailbox, intr_slave))
|
|
;
|
|
*/
|
|
}
|
|
|
|
// process ARM commands in master and slave mode
|
|
// standard operation may be interrupt by other requests
|
|
if (arm2pru_req_cached = mailbox.arm2pru_req) {
|
|
// not ARM2PRU_NONE
|
|
switch (arm2pru_req_cached) {
|
|
case ARM2PRU_NOP: // needed to probe PRU run state
|
|
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
|
|
break;
|
|
case ARM2PRU_ARB_MODE_NONE: // ignore SACK condition
|
|
// from now on, ignore INTR requests and allow DMA request immediately
|
|
sm_device_arb_worker = &sm_arb_worker_none;
|
|
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
|
|
break;
|
|
case ARM2PRU_ARB_MODE_CLIENT:
|
|
// request DMA from external Arbitrator
|
|
sm_device_arb_worker = &sm_arb_worker_device;
|
|
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
|
|
break;
|
|
case ARM2PRU_DMA:
|
|
// different arbitration for device and CPU memory access.
|
|
|
|
// request DMA, arbitrator must've been selected with ARM2PRU_ARB_MODE_*
|
|
if (mailbox.dma.cpu_access) {
|
|
// Emulated CPU: no NPR/NPG/SACK protocol
|
|
sm_arb.cpu_request = 1;
|
|
//PRU_DEBUG_PIN0_PULSE(50) ; // CPU20 performace
|
|
} else {
|
|
// Emulated device: raise request for emulated or physical Arbitrator.
|
|
sm_arb.device_request_mask |= PRIORITY_ARBITRATION_BIT_NP;
|
|
}
|
|
// request not put on bus for CPU memory access
|
|
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
|
|
break;
|
|
case ARM2PRU_INTR:
|
|
// request INTR, arbitrator must've been selected with ARM2PRU_ARB_MODE_*
|
|
// start one INTR cycle. May be raised in midst of slave cycle
|
|
// by ARM, if access to "active" register triggers INTR.
|
|
sm_arb.device_request_mask |= mailbox.intr.priority_arbitration_bit;
|
|
// sm_device_arb_worker() evaluates this, extern Arbitrator raises Grant,
|
|
// vector of GRANted level is transfered with statemachine sm_intr_master
|
|
|
|
// Atomically change state in a device's associates interrupt register.
|
|
// The Interupt Register is set immediately. No wait for INTR GRANT,
|
|
// INTR level may be blocked.
|
|
if (mailbox.intr.iopage_register_handle)
|
|
deviceregisters.registers[mailbox.intr.iopage_register_handle].value =
|
|
mailbox.intr.iopage_register_value;
|
|
mailbox.arm2pru_req = ARM2PRU_NONE; // done
|
|
// end of INTR is signaled to ARM with signal
|
|
break;
|
|
case ARM2PRU_INTR_CANCEL:
|
|
// cancels one or more INTR requests. If already Granted, the GRANT is forwarded,
|
|
// and canceled by reaching a "SACK turnaround terminator" or "No SACK TIMEOUT" in the arbitrator.
|
|
sm_arb.device_request_mask &= ~mailbox.intr.priority_arbitration_bit;
|
|
// no completion event, could interfer with other INTRs?
|
|
mailbox.arm2pru_req = ARM2PRU_NONE; // done
|
|
break;
|
|
case ARM2PRU_ARB_GRANT_INTR_REQUESTS:
|
|
if (emulate_cpu) {
|
|
mailbox.arbitrator.ifs_intr_arbitration_pending = true;
|
|
// also blocks ARM thread
|
|
}
|
|
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
|
|
break;
|
|
case ARM2PRU_INITALIZATIONSIGNAL_SET:
|
|
switch (mailbox.initializationsignal.id) {
|
|
case INITIALIZATIONSIGNAL_ACLO:
|
|
// assert/deassert ACLO
|
|
buslatches_setbits(7, BIT(4), mailbox.initializationsignal.val? BIT(4):0);
|
|
break;
|
|
case INITIALIZATIONSIGNAL_DCLO:
|
|
// assert/deassert DCLO
|
|
buslatches_setbits(7, BIT(5), mailbox.initializationsignal.val? BIT(5):0);
|
|
break;
|
|
case INITIALIZATIONSIGNAL_INIT:
|
|
// assert/deassert INIT
|
|
buslatches_setbits(7, BIT(3), mailbox.initializationsignal.val? BIT(3):0);
|
|
break;
|
|
}
|
|
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
|
|
break;
|
|
case ARM2PRU_CPU_ENABLE:
|
|
// bool flag much faster to access than shared mailbox member.
|
|
if (emulate_cpu != mailbox.cpu_enable) {
|
|
emulate_cpu = mailbox.cpu_enable;
|
|
sm_arb_reset(); // new arbitration algorithms
|
|
}
|
|
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
|
|
break;
|
|
case ARM2PRU_BUSLATCH_SET: { // set a mux register
|
|
// and read back
|
|
// don't feed "volatile" vars into buslatch_macros !!!
|
|
uint8_t reg_sel = mailbox.buslatch.addr & 7;
|
|
uint8_t bitmask = mailbox.buslatch.bitmask;
|
|
uint8_t val = mailbox.buslatch.val;
|
|
buslatches_setbits(reg_sel, bitmask, val);
|
|
mailbox.buslatch.val = buslatches_getbyte(reg_sel);
|
|
}
|
|
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
|
|
break;
|
|
case ARM2PRU_BUSLATCH_GET: {
|
|
// don't feed "volatile" vars into buslatch_macros !!!
|
|
uint8_t reg_sel = mailbox.buslatch.addr & 7;
|
|
mailbox.buslatch.val = buslatches_getbyte(reg_sel);
|
|
}
|
|
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
|
|
break;
|
|
case ARM2PRU_HALT:
|
|
mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done
|
|
__halt(); // LA: trigger on timeout of REG_WRITE
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
// never reached
|
|
}
|
|
|
|
#pragma diag_pop
|
|
|