1
0
mirror of https://github.com/livingcomputermuseum/UniBone.git synced 2026-04-28 04:46:19 +00:00
Files
2019-12-11 08:08:22 +01:00

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