/* hp3000_sel.c: HP 3000 30030C Selector Channel simulator | |
Copyright (c) 2016-2017, J. David Bryan | |
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 THE | |
AUTHOR 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. | |
Except as contained in this notice, the name of the author shall not be used | |
in advertising or otherwise to promote the sale, use or other dealings in | |
this Software without prior written authorization from the author. | |
SEL HP 3000 Series III Selector Channel | |
05-Sep-17 JDB Changed REG_A (permit any symbolic override) to REG_X | |
10-Oct-16 JDB Renumbered debug flags to start at 0 | |
Added port_read_memory, port_write_memory macros | |
11-Jul-16 JDB Change "sel_unit" from a UNIT to an array of one UNIT | |
30-Jun-16 JDB Reestablish active_dib pointer during sel_initialize | |
08-Jun-16 JDB Corrected %d format to %u for unsigned values | |
16-May-16 JDB abort_channel parameter is now a pointer-to-constant | |
21-Mar-16 JDB Changed uint16 types to HP_WORD | |
23-Sep-15 JDB First release version | |
27-Jan-15 JDB Passes the selector channel diagnostic (D429A) | |
10-Feb-13 JDB Created | |
References: | |
- HP 3000 Series II/III System Reference Manual | |
(30000-90020, July 1978) | |
- Stand-Alone HP 30030B/C Selector Channel Diagnostic | |
(30030-90011, July 1978) | |
- HP 3000 Series III Engineering Diagrams Set | |
(30000-90141, Apr-1980) | |
The HP 30030C Selector Channel provides high-speed data transfer between a | |
device and main memory. While several interfaces may be connected to the | |
selector channel bus, only one transfer is active at a time, and the channel | |
remains dedicated to that interface until the transfer is complete. The | |
channel contains its own memory port controller, so transfers to and from | |
memory bypass the I/O Processor. | |
Interfaces must have additional hardware to be channel-capable, as the | |
channel uses separate control and data signals from those used for direct | |
I/O. In addition, the multiplexer and selector channels differ somewhat in | |
their use of the signals, so interfaces are generally designed for use with | |
one or the other (the Selector Channel Maintenance Board is a notable | |
exception that uses jumpers to indicate which channel to use). | |
The transfer rate of the Series III selector channel is poorly documented. | |
Various rates are quoted in different publications: a 1.9 MB/second rate in | |
one, a 2.86 MB/second rate in another. Main memory access time is given as | |
300 nanoseconds, and the cycle time is 700 nanoseconds. | |
Once started by an SIO instruction, the channel executes I/O programs | |
independently of the CPU. Program words are read, and device status is | |
written back, directly via the port controller. | |
32-bit I/O program words are formed from a 16-bit I/O control word (IOCW) and | |
a 16-bit I/O address word (IOAW) in this general format: | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| C | order | X | control word 1/word count | IOCW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| control word 2/status/address | IOAW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
Most orders are fully decoded by bits 1-3, but a few use bit 4 to extend the | |
definition where bits 4-15 are not otherwise used. I/O programs always | |
reside in memory bank 0. The current I/O program pointer resides in word 0 | |
of the Device Reference Table entry for the active interface. | |
The Jump and Jump Conditional orders use this format: | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| - | 0 0 0 | C | - - - - - - - - - - - | IOCW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| jump target address | IOAW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
...where C is 0 for an unconditional jump and 1 for a conditional jump. An | |
unconditional jump is handled entirely within the channel. A conditional | |
jump asserts the SETJMP signal to the interface. If the interface returns | |
JMPMET, the jump will occur; otherwise, execution continues with the next | |
program word. | |
The Return Residue order uses this format: | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| - | 0 0 1 0 | - - - - - - - - - - - | IOCW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| residue of word count | IOAW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
The remaining word count from the last transfer will be returned in the IOAW | |
as a two's-complement value. If the transfer completed normally, the | |
returned value will be zero. | |
The Set Bank order uses this format: | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| - | 0 0 1 1 | - - - - - - - - - - - | IOCW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| - - - - - - - - - - - - | bank | IOAW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
This establishes the memory bank to be used for subsequent Write or Read | |
orders. Program addresses always use bank 0. | |
The Interrupt order uses this format: | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| - | 0 1 0 | - - - - - - - - - - - - | IOCW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| - - - - - - - - - - - - - - - - | IOAW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
The SETINT signal is asserted to the interface for this order. | |
The End and End with Interrupt orders use this format: | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| - | 0 1 1 | I | - - - - - - - - - - - | IOCW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| device status | IOAW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
...where I is 0 for an End and 1 for an End with Interrupt. The PSTATSTB | |
signal is asserted to the interface to obtain the device status, which is | |
stored in the IOAW location. If the I bit is set, SETINT will also be | |
asserted, | |
The Control order uses this format: | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| - | 1 0 0 | control word 1 | IOCW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| control word 2 | IOAW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
Both control words are sent to the interface. The full IOCW containing | |
control word 1 is sent with the PCMD1 signal asserted. It is followed by the | |
IOAW with PCONTSTB asserted. | |
The Sense order uses this format: | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| - | 1 0 1 | - - - - - - - - - - - - | IOCW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| device status | IOAW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
The PSTATSTB signal is asserted to the interface to obtain the device status, | |
which is stored in the IOAW location. | |
The Write and Read orders use these formats: | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| C | 1 1 0 | negative word count to write | IOCW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| C | 1 1 1 | negative word count to read | IOCW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| transfer address | IOAW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
The C bit is the "data chain" flag. If it is set, then this transfer is a | |
continuation of a previous Write or Read transfer. This is used to | |
circumvent the transfer size limitation inherent in the 12-bit word count | |
allocated in the IOCW. For single transfers larger than 4K words, multiple | |
contiguous Write or Read orders are used, with all but the last order having | |
their data chain bits set. | |
In simulation, IOCW bits 1-4 are used to index into a 16-element lookup table | |
to produce the final I/O order (because some of the orders define IOCW bit 4 | |
as "don't care", there are only thirteen distinct orders). | |
Channel-capable interfaces connect via the selector channel bus and request | |
channel service by asserting the CHANSR signal. An interface may initiate an | |
I/O program on the channel only if the channel is not busy with a transfer | |
involving another interface. The SIO instruction microcode tests status bit | |
0 ("SIO OK") from the interface before starting the I/O program. This bit | |
reflects the SIOENABLE signal from the channel, which is true only when the | |
channel is idle. If the channel is busy, the SIO instruction sets CCG and | |
returns without disturbing the channel. | |
The channel uses double-buffering for both data and I/O program words, | |
allowing concurrent channel/interface and channel/memory transfers. | |
Prefetching of the next I/O program word increases channel speed by reducing | |
the wait on memory transfers. | |
In simulation, an interface is implicitly connected to the selector channel | |
bus by calling the "sel_assert_REQ" routine. This initiates the transfer | |
between the device number of the interface and the channel. The | |
"service_request" field in the DIB is not used (it must be set to the | |
SRNO_UNUSED value). | |
The channel simulator provides these global objects: | |
t_bool sel_is_idle | |
TRUE if the selector channel is idle; FALSE otherwise. Corresponds to | |
the hardware SIOENABLE signal and reflects the value of the Selector | |
Active flip-flop. Used by device interfaces to qualify their SIO OK | |
status bits. | |
sel_assert_REQ (DIB *) | |
Called by the device interface while processing a DSTARTIO signal to | |
request that the selector channel begin an SIO operation, or called at | |
any time while the channel is active to abort the operation. Corresponds | |
to asserting the REQ signal. If the channel is idle, it initializes the | |
channel and starts the sequencer. If the channel is active, it aborts | |
the I/O program execution and idles the channel. | |
sel_assert_CHANSR (DIB *) | |
Called by the device controller to request service from the selector | |
channel asynchronously. Corresponds to asserting the CHANSR signal. | |
Typically called from a device service routine; device controller | |
interface routines return the CHANSR signal to request service | |
synchronously. Sets the service_request flag in the DIB and sets | |
sel_request. | |
t_bool sel_request | |
TRUE if an interface is requesting service from the selector channel or | |
the channel is servicing an internal request; FALSE otherwise. | |
Corresponds to the CHANSR signal received by the channel. Used by the | |
CPU to determine if a request is pending and the selector channel service | |
routine should be called. | |
sel_initialize (void) | |
Called in the instruction execution prelude to allow devices to be | |
reassigned or reset. If a device is under channel control, the routine | |
reestablishes the device number for the channel. It also sets | |
sel_request TRUE if the device has a service request pending or FALSE | |
otherwise. | |
sel_service (uint32) | |
Called to service a request from the device interface or an internal | |
request from the selector channel. Executes one or more channel cycles | |
for the associated interface. Used by the CPU to run the selector | |
channel. | |
Implementation notes: | |
1. In hardware, the installed memory size must be set with jumper S3 on the | |
Selector Channel Register PCA. In simulation, the size is determined by | |
using the MEMSIZE macro that tests the "capac" field of the CPU unit. | |
2. Multiple channels and interleaved memory operation and its associated | |
configuration jumpers are not supported. | |
3. The selectable trigger/freeze and error logging register features of the | |
Selector Channel Control PCA are not supported. Debug tracing may be | |
enabled to determine why an I/O program aborted. | |
3. The selector channel must execute more than one I/O order per CPU | |
instruction in order to meet the timing requirements of the diagnostic. | |
The timing is modeled by establishing a count of channel clock pulses at | |
poll entry and then executing orders until the count is exhausted. If | |
the clock count was exceeded, the excess count is saved and then | |
subtracted from the next entry's count, so that the typical execution | |
time is preserved over a number of entries. | |
*/ | |
#include "hp3000_defs.h" | |
#include "hp3000_cpu_ims.h" | |
#include "hp3000_io.h" | |
#include "hp3000_mem.h" | |
/* Program constants. | |
The selector channel clock period is 175 nanoseconds. The channel runs | |
concurrently with the CPU, which executes instructions in an average of 2.57 | |
microseconds, so multiple cycles are executed per CPU instruction. | |
In simulation, the channel is called from the instruction execution loop | |
after every instruction, and sometimes additionally within instructions that | |
have long execution times (e.g., MOVE). The number of event ticks that have | |
elapsed since the last call are passed to the channel; this determines the | |
number of channel cycles to execute. | |
Implementation notes: | |
1. The number of cycles consumed by the channel for various operations are | |
educated guesses. There is no documentation available that details the | |
cycle timing. | |
2. In simulation, the Wait Sequence exists separately from the Transfer | |
Sequence only to avoid cancelling the SR wait timer for each word | |
transferred. It is reported as a Transfer Sequence cycle. | |
*/ | |
#define SR_WAIT_TIMER mS (1000) /* 1000 millisecond SR wait timer */ | |
#define NS_PER_CYCLE 175 /* each clock cycle is 175 nanoseconds */ | |
#define CYCLES_PER_FETCH 6 | |
#define CYCLES_PER_PREFETCH 1 | |
#define CYCLES_PER_EXECUTE 1 | |
#define CYCLES_PER_RELOAD 3 | |
#define CYCLES_PER_READ 4 | |
#define CYCLES_PER_WRITE 4 | |
#define CYCLES_PER_EVENT (uint32) (USEC_PER_EVENT * 1000 / NS_PER_CYCLE) | |
#define CNTR_MASK 0007777u /* word counter count mask */ | |
#define CNTR_MAX 0007777u /* word counter maximum value */ | |
typedef enum { /* selector channel sequencer state */ | |
Idle_Sequence, | |
Fetch_Sequence, | |
Execute_Sequence, | |
Wait_Sequence, | |
Transfer_Sequence, | |
Reload_Sequence | |
} SEQ_STATE; | |
static const char *const seq_name [] = { /* indexed by SEQ_STATE */ | |
"Idle", | |
"Fetch", | |
"Execute", | |
"Transfer", /* the wait sequence is reported as a transfer sequence */ | |
"Transfer", | |
"Reload" | |
}; | |
static const char *const action_name [] = { /* indexed by SEQ_STATE */ | |
NULL, /* no loading occurs in the Idle_Sequence */ | |
"loaded", /* loads occur in the Fetch_Sequence */ | |
"prefetched", /* prefetches occur in the Execute_Sequence */ | |
NULL, /* no loading occurs in the Wait_Sequence */ | |
"prefetched", /* prefetches occur in the Transfer_Sequence */ | |
"loaded" /* loads occur in the Reload_Sequence */ | |
}; | |
/* Debug flags */ | |
#define DEB_CSRW (1u << 0) /* trace channel command initiations and completions */ | |
#define DEB_PIO (1u << 1) /* trace programmed I/O commands */ | |
#define DEB_STATE (1u << 2) /* trace state changes */ | |
#define DEB_SR (1u << 3) /* trace service requests */ | |
/* Memory access macros */ | |
#define port_read_memory(c,o,v) mem_read (&sel_dev, c, o, v) | |
#define port_write_memory(c,o,v) mem_write (&sel_dev, c, o, v) | |
/* Channel global state */ | |
t_bool sel_is_idle = TRUE; /* TRUE if the channel is idle */ | |
t_bool sel_request = FALSE; /* TRUE if the channel sequencer is to be invoked */ | |
/* Channel local state */ | |
static SEQ_STATE sequencer = Idle_Sequence; /* the current sequencer execution state */ | |
static SIO_ORDER order; /* the current SIO order */ | |
static DIB *active_dib; /* a pointer to the participating interface's DIB */ | |
static uint32 device_index; /* the index into the device table */ | |
static t_bool prefetch_control; /* TRUE if the IOCW should be prefetched */ | |
static t_bool prefetch_address; /* TRUE if the IOAW should be prefetched */ | |
static uint32 device_number; /* the participating interface's device number */ | |
static uint32 bank; /* the transfer bank register */ | |
static uint32 word_count; /* the transfer word count register */ | |
static HP_WORD program_counter; /* the I/O program counter */ | |
static HP_WORD control_word; /* the current IOCW */ | |
static HP_WORD control_buffer; /* the prefetched IOCW */ | |
static HP_WORD address_word; /* the current IOAW */ | |
static HP_WORD address_buffer; /* the prefetched IOAW */ | |
static HP_WORD input_buffer; /* the input data word buffer */ | |
static HP_WORD output_buffer; /* the output data word buffer */ | |
static FLIP_FLOP rollover; /* SET if the transfer word count rolls over */ | |
static int32 excess_cycles; /* the count of cycles in excess of allocation */ | |
/* Channel local SCP support routines */ | |
static t_stat sel_timer (UNIT *uptr); | |
static t_stat sel_reset (DEVICE *dptr); | |
/* Channel local utility routines */ | |
static void end_channel (DIB *dibptr); | |
static SIGNALS_DATA abort_channel (const char *reason); | |
static void load_control (HP_WORD *value); | |
static void load_address (HP_WORD *value); | |
/* Channel SCP data structures */ | |
/* Unit list */ | |
static UNIT sel_unit [] = { | |
{ UDATA (&sel_timer, 0, 0), SR_WAIT_TIMER } | |
}; | |
/* Register list */ | |
static REG sel_reg [] = { | |
/* Macro Name Location Width Offset Flags */ | |
/* ------ ------ --------------- ----- ------ ----------------- */ | |
{ FLDATA (IDLE, sel_is_idle, 0) }, | |
{ FLDATA (SREQ, sel_request, 0) }, | |
{ DRDATA (DEVNO, device_number, 8), PV_LEFT }, | |
{ DRDATA (EXCESS, excess_cycles, 32), PV_LEFT }, | |
{ DRDATA (INDEX, device_index, 32), PV_LEFT | REG_HRO }, | |
{ DRDATA (SEQ, sequencer, 3) }, | |
{ ORDATA (ORDER, order, 4) }, | |
{ FLDATA (ROLOVR, rollover, 0) }, | |
{ FLDATA (PFCNTL, prefetch_control, 0) }, | |
{ FLDATA (PFADDR, prefetch_address, 0) }, | |
{ ORDATA (BANK, bank, 4), PV_LEFT }, | |
{ DRDATA (WCOUNT, word_count, 12) }, | |
{ ORDATA (PCNTR, program_counter, 16), REG_FIT }, | |
{ ORDATA (CNTL, control_word, 16), REG_FIT }, | |
{ ORDATA (CNBUF, control_buffer, 16), REG_FIT }, | |
{ ORDATA (ADDR, address_word, 16), REG_FIT }, | |
{ ORDATA (ADBUF, address_buffer, 16), REG_FIT }, | |
{ ORDATA (INBUF, input_buffer, 16), REG_X | REG_FIT }, | |
{ ORDATA (OUTBUF, output_buffer, 16), REG_X | REG_FIT }, | |
{ NULL } | |
}; | |
/* Debugging trace list */ | |
static DEBTAB sel_deb [] = { | |
{ "CSRW", DEB_CSRW }, /* channel command initiations and completions */ | |
{ "PIO", DEB_PIO }, /* programmed I/O commands executed */ | |
{ "STATE", DEB_STATE }, /* channel state changes executed */ | |
{ "SR", DEB_SR }, /* service requests received */ | |
{ "DATA", DEB_MDATA }, /* I/O data accesses to memory */ | |
{ NULL, 0 } | |
}; | |
/* Device descriptor */ | |
DEVICE sel_dev = { | |
"SEL", /* device name */ | |
sel_unit, /* unit array */ | |
sel_reg, /* register array */ | |
NULL, /* modifier array */ | |
1, /* number of units */ | |
8, /* address radix */ | |
PA_WIDTH, /* address width */ | |
1, /* address increment */ | |
8, /* data radix */ | |
16, /* data width */ | |
NULL, /* examine routine */ | |
NULL, /* deposit routine */ | |
&sel_reset, /* reset routine */ | |
NULL, /* boot routine */ | |
NULL, /* attach routine */ | |
NULL, /* detach routine */ | |
NULL, /* device information block pointer */ | |
DEV_DEBUG, /* device flags */ | |
0, /* debug control flags */ | |
sel_deb, /* debug flag name array */ | |
NULL, /* memory size change routine */ | |
NULL /* logical device name */ | |
}; | |
/* Channel global routines */ | |
/* Initialize the channel. | |
This routine is called in the CPU instruction execution prelude to allow the | |
device number of the participating interface to be reassigned. It also sets | |
up the service request value from the device DIB. This allows the device | |
state to be changed during a simulation stop. | |
Implementation notes: | |
1. The active DIB pointer is restored from the device context to support | |
resuming after a SAVE and RESTORE is performed. | |
2. In simulation, we allow the device number to be changed during a | |
simulation stop, so this routine must recover it from the device. | |
Normally, the device number register would be reset from the device | |
number field in the DIB. However, the SCMB may be spoofing the device | |
number, and it is this spoofed number that must be restored. To do this, | |
we first assert the DEVNODB signal to the interface. The SCMB will | |
respond to the DEVNODB signal, as it supports connection to the | |
multiplexer channel. Devices that connect only to the selector channel | |
will not respond to DEVNODB, returning an outbound value of zero. In | |
this case, we use the DIB field to obtain the device number. | |
*/ | |
void sel_initialize (void) | |
{ | |
SIGNALS_DATA outbound; | |
if (sel_is_idle == FALSE) { /* if the channel is controlling a device */ | |
active_dib = (DIB *) sim_devices [device_index]->ctxt; /* then restore the active DIB pointer */ | |
outbound = active_dib->io_interface (active_dib, DEVNODB, 0); /* see if the device responds to DEVNODB */ | |
if (IODATA (outbound) > 0) /* if it does (e.g., the SCMB) */ | |
device_number = IODATA (outbound) / 4; /* then use the returned device number */ | |
else /* otherwise */ | |
device_number = active_dib->device_number; /* use the device number from the DIB */ | |
sel_request = active_dib->service_request; /* restore the service request state */ | |
} | |
return; | |
} | |
/* Start an I/O program. | |
This routine is called by a device interface in response to a Start I/O (SIO) | |
instruction to request that the selector channel begin an I/O program. It | |
corresponds in hardware to asserting the REQ signal. | |
If REQ is asserted while the channel is servicing the interface, the channel | |
aborts the transfer. This occurs when an interface decides to terminate a | |
transfer, for example when an error retry count has expired or a device has | |
become not ready. | |
Implementation notes: | |
1. Both the HP 3000 Series II/III System Reference Manual and the HP 3000 | |
Series III Reference/Training manuals say that the selector channel | |
asserts a DEVNODB signal as part of the REQ processing. This is | |
incorrect; the DEVNODB line is tied inactive by the channel, per the | |
Selector Channel Control PCA schematic. Instead, the channel expects the | |
device number, multiplied by four, to be present on the SRn bus during | |
REQ signal assertion, when it is loaded into the device number register. | |
Selector channel devices gate their device numbers onto SR6-13 when an | |
SIO instruction is decoded. | |
*/ | |
void sel_assert_REQ (DIB *dibptr) | |
{ | |
if (sel_is_idle) { /* if the channel is idle then set it up */ | |
dprintf (sel_dev, DEB_CSRW, "Device number %u asserted REQ for channel initialization\n", | |
dibptr->device_number); | |
sel_is_idle = FALSE; /* the channel is now busy */ | |
sel_request = TRUE; /* set the request flag */ | |
sequencer = Fetch_Sequence; /* initialize the sequencer */ | |
bank = 0; /* set the bank to bank 0 */ | |
word_count = 0; /* clear the word counter */ | |
rollover = CLEAR; /* and the word count rollover flip-flop */ | |
excess_cycles = 0; /* clear the excess cycle count */ | |
device_index = 0; /* find the device index */ | |
/* corresponding to */ | |
while ((DIB *) sim_devices [device_index]->ctxt != dibptr) /* the active DIB pointer */ | |
device_index = device_index + 1; /* to aid later restoration */ | |
active_dib = dibptr; /* save the interface's DIB pointer */ | |
device_number = dibptr->device_number; /* and set the device number register */ | |
port_read_memory (absolute, device_number * 4, /* read the initial program counter from the DRT */ | |
&program_counter); | |
} | |
else { /* otherwise abort the transfer in progress */ | |
dprintf (sel_dev, DEB_CSRW, "Device number %u asserted REQ for channel abort\n", | |
device_number); | |
end_channel (dibptr); /* idle the channel */ | |
sim_cancel (&sel_unit [0]); /* and cancel the CHANSR timer */ | |
} | |
return; | |
} | |
/* Request channel service. | |
This routine is called by a device interface to request service from the | |
channel. It is called either directly by the interface or indirectly by the | |
channel in response to a CHANSR signal returned by the interface. A direct | |
call is needed for asynchronous assertion, e.g., in response to an event | |
service call. Synchronous assertion, i.e., in response to an interface call, | |
is made by returning the CHANSR to the channel. The routine corresponds in | |
hardware to asserting the CHANSR signal on the selector channel bus. | |
Sets the service_request flag in the DIB and sets the sel_request flag to | |
cause the channel sequencer to be invoked. | |
*/ | |
void sel_assert_CHANSR (DIB *dibptr) | |
{ | |
dprintf (sel_dev, DEB_SR, "Device number %u asserted CHANSR\n", | |
device_number); | |
dibptr->service_request = TRUE; /* set the service request flag in the interface */ | |
sel_request = TRUE; /* and the selector request flag */ | |
return; | |
} | |
/* Invoke the channel sequencer in response to a service request. | |
This routine is called in the CPU instruction execution loop to service a | |
channel request, asserted either by the participating interface, or | |
generated internally by the channel. It executes one or more channel cycles | |
for the associated device interface and resets the service request flag in | |
the DIB before exiting. The routine is called after every instruction, and | |
sometimes additionally within instructions that have long execution times | |
(e.g., MOVE). The number of event ticks that have elapsed since the last | |
call are passed in; this determines the number of channel cycles available to | |
execute. | |
The selector channel clock period is 175 nanoseconds. The channel runs | |
concurrently with the CPU, which executes instructions in an average of 2.57 | |
microseconds, so multiple cycles are executed per CPU instruction. | |
On entry, the routine executes the next state in the transfer for the | |
currently participating interface. The number of channel clock counts | |
consumed for the specified state execution is subtracted from the number of | |
clock counts available. If more time remains, and the service request is | |
still active, another channel cycle is run for the interface. | |
The actions for the orders are: | |
Transfer Last | |
SIO Order State Type Word Signals Asserted | |
--------- -------- -------- ---- ------------------------------- | |
sioJUMP Execute -- -- (none) | |
sioJUMPC Execute -- -- SETJMP | |
sioRTRES Execute -- -- (none) | |
sioSBANK Execute -- -- (none) | |
sioINTRP Execute -- -- SETINT | |
sioEND Execute -- -- TOGGLESIOOK | PSTATSTB | |
sioENDIN Execute -- -- TOGGLESIOOK | PSTATSTB | SETINT | |
sioCNTL Execute -- -- PCMD1 | CHANSO | |
" Transfer -- -- PCONTSTB | |
sioSENSE Execute -- -- PSTATSTB | |
sioWRITE Execute -- -- TOGGLEOUTXFER | |
Transfer Normal No PWRITESTB | |
Transfer Normal Yes PWRITESTB | EOT | |
Transfer DEVEND No EOT | TOGGLEOUTXFER | |
Transfer DEVEND Yes (none) | |
sioWRITEC Execute -- -- TOGGLEOUTXFER | |
Transfer Normal No PWRITESTB | |
Transfer Normal Yes PWRITESTB | EOT | TOGGLEOUTXFER | |
Transfer DEVEND No EOT | |
Transfer DEVEND Yes (none) | |
sioREAD Execute -- -- TOGGLEINXFER | READNEXTWD | |
Transfer Normal No PREADSTB | READNEXTWD | |
Transfer Normal Yes PREADSTB | EOT | READNEXTWD | |
Transfer Devend No EOT | TOGGLEINXFER | |
Transfer DEVEND Yes (none) | |
sioREADC Execute -- -- TOGGLEINXFER | READNEXTWD | |
Transfer Normal No PREADSTB | READNEXTWD | |
Transfer Normal Yes PREADSTB | EOT | TOGGLEINXFER | |
Transfer Devend No EOT | |
Transfer DEVEND Yes (none) | |
A fundamental difference between the multiplexer and selector channels is | |
that the latter needs an external service request (i.e., CHANSR assertion) | |
only for operations on the interface. All other channel operations apply an | |
internal service request and so occur automatically without CHANSR being | |
asserted. In simulation, sel_request is set TRUE during channel | |
initialization and is only cleared when the channel is waiting for a response | |
from the interface during a control, read, or write operation. The following | |
signal assertions to the interface must return CHANSR to permit the channel | |
sequencer to advance: | |
- PCMD1 | |
- PCONTSTB | |
- TOGGLEINXFER | READNEXTWD | |
- PREADSTB | |
- TOGGLEOUTXFER | |
- PWRITESTB | |
Because the channel is dedicated to an interface for the duration of a | |
transfer, a non-responding interface would tie up the channel forever. To | |
prevent this, the channel starts a one-millisecond timer whenever it is | |
waiting for the interface to assert CHANSR. If the timer expires, the | |
transfer is aborted, and the channel is freed. The channel also checks for | |
CHANACK in response to CHANSO assertion to the interface and will terminate | |
(but not abort) the transfer if the interface fails to return it. | |
To maintain the maximum transfer rate across chained read or write transfers, | |
the channel will attempt to prefetch the next set of I/O Control and Address | |
words during the current data transfer. The two memory reads are interleaved | |
between successive channel data transfers, but only if the input or output | |
data buffers are both empty (read) or full (write), respectively. This will | |
normally occur unless the device is using all available channel bandwidth; | |
the SCMB in high speed mode is an example of a device that asserts SR | |
continuously. | |
The selector channel diagnostic checks for the expected prefetching. With | |
the SCMB in slow mode, both the control and address prefetches will occur | |
within three (read) or five (write) I/O cycles. In fast mode, prefetches are | |
locked out during a write operation, and only the control word prefetch | |
occurs during a read operation. | |
Implementation notes: | |
1. Conceptually, the selector channel has three internal states: initiate, | |
fetch, and execute. The initiator sequence begins with a REQ | |
assertion from the interface and sets up the I/O program for execution. | |
The fetch sequence obtains the IOCW and IOAW. The execute sequence | |
generates the signals needed for each I/O order. | |
In hardware, the Selector Channel Sequencer PCA contains several state | |
machines (initiator, fetch, prefetch, buffer transfer, etc.) that provide | |
substates. In simulation, the prefetch, transfer, and reload actions are | |
given separate states, rather than making them substates of the execute | |
state. | |
2. In hardware, a ten-microsecond CHANACK timer is started when CHANSO is | |
asserted to the interface. In simulation, a timer is not needed; | |
instead, the interface must return CHANACK to avoid a CHANACK timeout | |
error. | |
3. In hardware, the CHANACK and CHANSR timers run on every channel cycle. | |
In simulation, CHANACK is tested only during a read or write transfer, | |
and CHANSR is tested only at the beginning of a read or write transfer, | |
and at the beginning and end of a control transfer. This limits overhead | |
while passing the diagnostic and providing some measure of protection. | |
4. In hardware, there are two input and two output buffers. This enables | |
memory transfers to overlap with interface data transfers. In | |
simulation, only one of each buffer is implemented, and memory transfers | |
occur synchronously with interface transfers. | |
5. In hardware, prefetches of the IOCW and IOAW are interleaved with channel | |
memory accesses and only occur when the port controller is idle. In | |
simulation, prefetches are not counted against the sequencer execution | |
time. Normal fetches, by contrast, wait for memory access and so the | |
time taken is counted. | |
6. The default label in the Execute Sequence switch statement is necessary | |
to quiet a warning that inbound_signals may be used uninitialized, even | |
though all cases are covered. | |
*/ | |
void sel_service (uint32 ticks_elapsed) | |
{ | |
HP_WORD inbound_data, outbound_data; | |
INBOUND_SET inbound_signals; | |
SIGNALS_DATA outbound; | |
int32 cycles; | |
uint32 return_address; | |
cycles = CYCLES_PER_EVENT - excess_cycles; /* decrease the cycles available by any left over */ | |
while (sel_request && cycles > 0) { /* execute as long as a request and cycles remain */ | |
outbound = IORETURN (NO_SIGNALS, 0); /* initialize in case we don't call the interface */ | |
dprintf (sel_dev, DEB_STATE, "Channel entered the %s sequence with %d clock cycles remaining\n", | |
seq_name [sequencer], cycles); | |
switch (sequencer) { /* dispatch based on the selector state */ | |
case Idle_Sequence: /* if the selector is idle */ | |
sel_request = FALSE; /* then the request is invalid */ | |
break; | |
case Fetch_Sequence: | |
sim_cancel (&sel_unit [0]); /* cancel the CHANSR timer */ | |
load_control (&control_word); /* load the IOCW */ | |
load_address (&address_word); /* and the IOAW */ | |
cycles = cycles - 2 * CYCLES_PER_READ /* and count the accesses */ | |
- CYCLES_PER_FETCH; /* and the fetch sequence */ | |
order = IOCW_ORDER (control_word); /* save the current order */ | |
if (control_word & IOCW_DC /* if the data chain bit is set */ | |
&& order != sioREADC && order != sioWRITEC) /* but the order isn't a chained order */ | |
outbound = abort_channel ("an illegal order"); /* then abort the channel program */ | |
else /* otherwise the order is valid */ | |
sequencer = Execute_Sequence; /* and execution is next */ | |
break; | |
case Execute_Sequence: | |
switch (order) { /* dispatch based on the I/O order */ | |
case sioJUMPC: | |
inbound_signals = SETJMP | CHANSO; | |
break; | |
case sioRTRES: | |
inbound_signals = NO_SIGNALS; /* no interface call is needed */ | |
if (rollover == SET) /* if the count terminated */ | |
outbound = IORETURN (NO_SIGNALS, 0); /* then return a zero count */ | |
else /* otherwise return the two's-complement remainder */ | |
outbound = IORETURN (NO_SIGNALS, | |
IOCW_COUNT (word_count)); | |
break; | |
case sioINTRP: | |
inbound_signals = SETINT | CHANSO; | |
break; | |
case sioEND: | |
inbound_signals = TOGGLESIOOK | PSTATSTB | CHANSO; | |
break; | |
case sioENDIN: | |
inbound_signals = TOGGLESIOOK | PSTATSTB | SETINT | CHANSO; | |
break; | |
case sioCNTL: | |
inbound_signals = PCMD1 | CHANSO; | |
sel_request = FALSE; /* wait until the interface requests the next word */ | |
break; | |
case sioSENSE: | |
inbound_signals = PSTATSTB | CHANSO; | |
break; | |
case sioWRITE: | |
case sioWRITEC: | |
inbound_signals = TOGGLEOUTXFER | CHANSO; | |
word_count = IOCW_WCNT (control_word); /* load the word count */ | |
sel_request = FALSE; /* wait until the interface requests the next word */ | |
break; | |
case sioREAD: | |
case sioREADC: | |
inbound_signals = TOGGLEINXFER | READNEXTWD | CHANSO; | |
word_count = IOCW_WCNT (control_word); /* load the word count */ | |
sel_request = FALSE; /* wait until the interface requests the next word */ | |
break; | |
default: /* needed to quiet warning about inbound_signals */ | |
case sioJUMP: /* these orders do not need */ | |
case sioSBANK: /* to call the interface */ | |
inbound_signals = NO_SIGNALS; | |
break; | |
} /* end switch */ | |
if (inbound_signals) { /* if there are signals to assert */ | |
outbound = active_dib->io_interface (active_dib, /* then pass them to the interface */ | |
inbound_signals, | |
control_word); | |
if ((outbound & CHANACK) == NO_SIGNALS) { /* if CHANACK was not returned */ | |
dprintf (sel_dev, DEB_SR, "Device number %u CHANACK timeout\n", | |
device_number); | |
end_channel (active_dib); /* terminate the channel program */ | |
dprintf (sel_dev, DEB_CSRW, "Channel program ended\n"); | |
break; | |
} | |
} | |
switch (order) { /* dispatch based on the I/O order */ | |
case sioJUMP: | |
program_counter = address_word; /* load the program counter with the new address */ | |
sequencer = Fetch_Sequence; /* next state is Fetch */ | |
break; | |
case sioJUMPC: | |
if (outbound & JMPMET) /* if the jump condition is true */ | |
program_counter = address_word; /* then load the program counter with the new address */ | |
sequencer = Fetch_Sequence; /* the next state is Fetch */ | |
break; | |
case sioRTRES: | |
case sioEND: | |
case sioENDIN: | |
case sioSENSE: | |
outbound_data = IODATA (outbound); /* get the status or residue to return */ | |
return_address = program_counter - 1 & LA_MASK; /* point at the second of the program words */ | |
port_write_memory (absolute, return_address, outbound_data); /* save the word */ | |
cycles = cycles - CYCLES_PER_WRITE; /* and count the access */ | |
dprintf (sel_dev, DEB_PIO, "Channel stored IOAW %06o to address %06o\n", | |
outbound_data, return_address); | |
if (order == sioEND || order == sioENDIN) { /* if it's an End or End with Interrupt order */ | |
end_channel (active_dib); /* then terminate the program */ | |
dprintf (sel_dev, DEB_CSRW, "Channel program ended\n"); | |
} | |
else /* otherwise the program continues */ | |
sequencer = Fetch_Sequence; /* with the fetch state */ | |
break; | |
case sioSBANK: | |
bank = IOAW_BANK (address_word); /* set the bank number register */ | |
sequencer = Fetch_Sequence; /* the next state is Fetch */ | |
break; | |
case sioINTRP: | |
sequencer = Fetch_Sequence; /* the next state is Fetch */ | |
break; | |
case sioCNTL: | |
prefetch_control = FALSE; /* prefetching is not used */ | |
prefetch_address = FALSE; /* for the Control order */ | |
sim_activate (&sel_unit [0], sel_unit [0].wait); /* start the SR timer */ | |
sequencer = Wait_Sequence; /* and check for a timeout */ | |
break; | |
case sioWRITE: | |
case sioWRITEC: | |
prefetch_control = (order == sioWRITEC); /* enable prefetching */ | |
prefetch_address = (order == sioWRITEC); /* if the order is chained */ | |
sim_activate (&sel_unit [0], sel_unit [0].wait); /* start the SR timer */ | |
sequencer = Wait_Sequence; /* and check for a timeout */ | |
break; | |
case sioREAD: | |
case sioREADC: | |
prefetch_control = (order == sioREADC); /* enable prefetching */ | |
prefetch_address = (order == sioREADC); /* if the order is chained */ | |
if (prefetch_control) { /* if control word prefetching is enabled */ | |
load_control (&control_buffer); /* then prefetch the next IOCW into the buffer */ | |
cycles = cycles - CYCLES_PER_PREFETCH; /* and count the sequencer time */ | |
prefetch_control = FALSE; /* mark the job done */ | |
} | |
sim_activate (&sel_unit [0], sel_unit [0].wait); /* start the SR timer */ | |
sequencer = Wait_Sequence; /* and check for a timeout */ | |
break; | |
} /* end switch */ | |
cycles = cycles - CYCLES_PER_EXECUTE; /* count the sequencer time */ | |
break; | |
case Wait_Sequence: | |
sim_cancel (&sel_unit [0]); /* cancel the SR timer */ | |
sequencer = Transfer_Sequence; /* continue with the transfer sequence */ | |
/* fall into the Transfer_Sequence */ | |
case Transfer_Sequence: | |
if (order == sioCNTL) { /* if this is a Control order */ | |
inbound_data = address_word; /* then supply the control word */ | |
inbound_signals = PCONTSTB | CHANSO; /* to the interface */ | |
sel_request = FALSE; /* wait until the interface confirms receipt */ | |
} | |
else if (order == sioREAD || order == sioREADC) { /* otherwise if this is a Read or Read Chained order */ | |
inbound_data = 0; /* then no value is needed */ | |
inbound_signals = PREADSTB | CHANSO; /* by the interface */ | |
if (word_count == CNTR_MAX) /* if the word count is exhausted */ | |
if (order == sioREADC) /* then if the order is chained */ | |
inbound_signals |= EOT | READNEXTWD; /* then continue the transfer block */ | |
else /* otherwise */ | |
inbound_signals |= EOT | TOGGLEINXFER; /* end the transfer block */ | |
else /* otherwise the transfer continues */ | |
inbound_signals |= READNEXTWD; /* with the next word */ | |
sel_request = FALSE; /* wait until the interface confirms receipt */ | |
} | |
else { /* otherwise it's a Write or Write Chained order */ | |
if (port_read_memory (dma, /* if the memory read */ | |
TO_PA (bank, address_word), /* from the specified bank and offset */ | |
&input_buffer)) { /* succeeds */ | |
cycles = cycles - CYCLES_PER_READ; /* then count the access */ | |
inbound_data = input_buffer; /* get the word to supply */ | |
inbound_signals = PWRITESTB | CHANSO; /* to the interface */ | |
if (word_count == CNTR_MAX) /* if the word count is exhausted */ | |
if (order == sioWRITEC) /* then if the order is chained */ | |
inbound_signals |= EOT; /* then continue the transfer block */ | |
else /* otherwise */ | |
inbound_signals |= EOT | TOGGLEOUTXFER; /* end the transfer block */ | |
sel_request = FALSE; /* wait until the interface confirms receipt */ | |
} | |
else { /* otherwise the memory read failed */ | |
outbound = abort_channel ("a memory read error"); /* so abort the transfer */ | |
break; /* and skip the interface call */ | |
} | |
} | |
cycles = cycles - CYCLES_PER_EXECUTE; /* count the sequencer time */ | |
outbound = active_dib->io_interface (active_dib, inbound_signals, inbound_data); /* call the interface */ | |
if (sel_is_idle) /* if the interface aborted the transfer */ | |
break; /* then terminate processing now */ | |
if ((outbound & CHANSR) == NO_SIGNALS) /* if the interface did not assert a service request */ | |
if (prefetch_control) { /* then if control word prefetching is enabled */ | |
load_control (&control_buffer); /* then prefetch the next IOCW into the buffer */ | |
cycles = cycles - CYCLES_PER_PREFETCH; /* and count the sequencer time */ | |
prefetch_control = FALSE; /* mark the job done */ | |
} | |
else if (prefetch_address) { /* otherwise if address word prefetching is enabled */ | |
load_address (&address_buffer); /* then prefetch the next IOAW into the buffer */ | |
cycles = cycles - CYCLES_PER_PREFETCH; /* and count the sequencer time */ | |
prefetch_address = FALSE; /* mark the job done */ | |
} | |
if (order == sioCNTL) { /* if this is a Control order */ | |
sim_activate (&sel_unit [0], sel_unit [0].wait); /* then start the SR timer */ | |
sequencer = Fetch_Sequence; /* and the next state is Fetch */ | |
} | |
else { /* otherwise it's a Write or Read (Chained) order */ | |
if (outbound & DEVEND) { /* if the device ended the transfer */ | |
if (word_count < CNTR_MAX) { /* then if the transfer is incomplete */ | |
inbound_signals = EOT | CHANSO; /* then assert EOT to end the transfer */ | |
if (order == sioREAD) /* if the order is Read and not chained */ | |
inbound_signals |= TOGGLEINXFER; /* then terminate the input block */ | |
else if (order == sioWRITE) /* otherwise if the order is Write and not chained */ | |
inbound_signals |= TOGGLEOUTXFER; /* then terminate the output block */ | |
active_dib->io_interface (active_dib, inbound_signals, 0); /* tell the interface */ | |
} | |
sequencer = Reload_Sequence; /* the next state is Reload */ | |
} | |
else if (order == sioREAD /* otherwise the transfer was successful */ | |
|| order == sioREADC) { /* and if this is a Read or Read Chained order */ | |
output_buffer = IODATA (outbound); /* then pick up the returned data word */ | |
if (port_write_memory (dma, /* if the memory write */ | |
TO_PA (bank, address_word), /* to the specified bank and offset */ | |
output_buffer)) /* succeeds */ | |
cycles = cycles - CYCLES_PER_WRITE; /* then count the access */ | |
else { /* otherwise the memory write failed */ | |
outbound = abort_channel ("a memory write error"); /* so abort the transfer */ | |
break; /* and skip the address and count update */ | |
} | |
} | |
address_word = address_word + 1 & LA_MASK; /* increment the transfer address */ | |
word_count = word_count + 1 & CNTR_MASK; /* and the word count */ | |
if (word_count == 0) { /* if the word count is exhausted */ | |
rollover = SET; /* then set the rollover flip-flop */ | |
sequencer = Reload_Sequence; /* and load the next I/O program word */ | |
} | |
} | |
break; | |
case Reload_Sequence: | |
if (order == sioWRITEC || order == sioREADC) { /* if the current order is chained */ | |
if (prefetch_control) { /* and the IOCW has not been prefetched yet */ | |
load_control (&control_buffer); /* then load it now */ | |
cycles = cycles - CYCLES_PER_READ; /* and count the memory access */ | |
} | |
if (prefetch_address) { /* if the IOAW has not be prefetched yet */ | |
load_address (&address_buffer); /* then load it now */ | |
cycles = cycles - CYCLES_PER_READ; /* and count the memory access */ | |
} | |
if (prefetch_control || prefetch_address) /* if both words had not been prefetched */ | |
cycles = cycles - CYCLES_PER_FETCH; /* then count as a fetch sequence */ | |
else /* otherwise */ | |
cycles = cycles - CYCLES_PER_RELOAD; /* count as a reload sequence */ | |
if ((control_word ^ control_buffer) & IOCW_SIO_MASK) /* if the next order isn't the same type */ | |
outbound = abort_channel ("an invalid chained order"); /* then an invalid order abort occurs */ | |
else { /* otherwise the next order is OK */ | |
control_word = control_buffer; /* so copy the control and address values */ | |
address_word = address_buffer; /* from the buffers */ | |
order = IOCW_ORDER (control_word); /* get the new order */ | |
word_count = IOCW_WCNT (control_word); /* and word count */ | |
rollover = CLEAR; /* clear the word count rollover flip-flop */ | |
prefetch_control = (control_word & IOCW_DC) != 0; /* enable prefetching */ | |
prefetch_address = (control_word & IOCW_DC) != 0; /* if the new order is chained */ | |
sequencer = Transfer_Sequence; /* the next state is Transfer */ | |
} | |
} | |
else /* otherwise an unchained order ends the transfer */ | |
sequencer = Fetch_Sequence; /* so proceed directly to Fetch */ | |
break; | |
} /* end switch */ | |
if (outbound & INTREQ) /* if an interrupt request was asserted */ | |
iop_assert_INTREQ (active_dib); /* then set it up */ | |
if (sel_is_idle == FALSE) /* if the channel is still running */ | |
if (outbound & CHANSR) /* then if the interface requested service */ | |
sel_assert_CHANSR (active_dib); /* then set it up */ | |
else /* otherwise */ | |
active_dib->service_request = FALSE; /* clear the current service request */ | |
else /* otherwise the channel has stopped */ | |
sim_cancel (&sel_unit [0]); /* so cancel the CHANSR timer */ | |
} /* end while */ | |
if (cycles > 0) /* if we exited to wait for a service request */ | |
excess_cycles = 0; /* then do a full set of cycles next time */ | |
else /* otherwise we ran over our allotment */ | |
excess_cycles = - cycles; /* so reduce the next poll by the overage */ | |
return; | |
} | |
/* Channel local SCP support routines */ | |
/* Service the channel service request timer. | |
The CHANSR timer is started whenever the channel is waiting for a service | |
request from the participating interface. Because the selector channel is | |
dedicated to a single interface until the end of the I/O program, if that | |
interface were to malfunction and not respond, the channel would be tied up | |
forever. | |
Normally, the timer is cancelled as soon as CHANSR is returned from the | |
interface. If this service routine is entered, it means that CHANSR is | |
taking too long, so the I/O program is aborted, and the channel is idled, so | |
that it is available for other devices. | |
*/ | |
static t_stat sel_timer (UNIT *uptr) | |
{ | |
SIGNALS_DATA outbound; | |
outbound = abort_channel ("a CHANSR timeout"); /* abort the transfer in progress */ | |
if (outbound & INTREQ) /* if an interrupt request was asserted */ | |
iop_assert_INTREQ (active_dib); /* then set it up */ | |
return SCPE_OK; | |
} | |
/* Device reset. | |
This routine is called for a RESET or RESET SEL command. It is the | |
simulation equivalent of the IORESET signal, which is asserted by the front | |
panel LOAD and DUMP switches. | |
For this interface, IORESET is identical to the internal Clear Logic signal. | |
A reset does not clear any of the registers. | |
*/ | |
static t_stat sel_reset (DEVICE *dptr) | |
{ | |
rollover = CLEAR; /* clear the word count rollover flip-flop */ | |
sel_is_idle = TRUE; /* the channel is now inactive */ | |
sel_request = FALSE; /* clear the request flag */ | |
sequencer = Idle_Sequence; /* stop the sequencer */ | |
return SCPE_OK; | |
} | |
/* Channel local utility routines */ | |
/* End the channel I/O program. | |
The channel program ends, either normally via an sioEND or sioENDIN order, or | |
abnormally via a REQ or timeout abort. The program counter is written back | |
to the DRT, and the channel is idled by performing a Clear Logic operation. | |
Implementation notes: | |
1. The memory write cycle time need not be counted, as the channel will be | |
terminating unconditionally. | |
*/ | |
static void end_channel (DIB *dibptr) | |
{ | |
port_write_memory (absolute, device_number * 4, /* write the program counter back to the DRT */ | |
program_counter); | |
dibptr->service_request = FALSE; /* clear any outstanding device service request */ | |
sel_reset (&sel_dev); /* perform a Clear Logic operation */ | |
return; | |
} | |
/* Abort the transfer in progress. | |
If an internal channel error occurs (e.g., a memory read or write failure, | |
due to an invalid address), the channel asserts the XFERERROR signal to the | |
interface. The interface will clear its logic and assert REQ to the channel | |
to complete the abort. | |
*/ | |
static SIGNALS_DATA abort_channel (const char *reason) | |
{ | |
dprintf (sel_dev, DEB_CSRW, "Channel asserted XFERERROR for %s\n", | |
reason); | |
return active_dib->io_interface (active_dib, XFERERROR | CHANSO, 0); /* tell the interface that the channel has aborted */ | |
} | |
/* Load the I/O Control Word. | |
The first of the two I/O program words is loaded into the channel register | |
pointed to by "value". The program counter points at the location to read | |
and is incremented after retrieving the value. This routine is called both | |
for a normal fetch and for a prefetch. | |
*/ | |
static void load_control (HP_WORD *value) | |
{ | |
port_read_memory (absolute, program_counter, value); /* read the IOCW from memory */ | |
dprintf (sel_dev, DEB_PIO, "Channel %s IOCW %06o (%s) from address %06o\n", | |
action_name [sequencer], *value, | |
sio_order_name [IOCW_ORDER (*value)], program_counter); | |
program_counter = program_counter + 1 & LA_MASK; /* increment the program counter */ | |
return; | |
} | |
/* Load the I/O Address Word. | |
The second of the two I/O program words is loaded into the channel register | |
pointed to by "value". The program counter points at the location to read | |
and is incremented after retrieving the value. This routine is called both | |
for a normal fetch and for a prefetch. | |
*/ | |
static void load_address (HP_WORD *value) | |
{ | |
port_read_memory (absolute, program_counter, value); /* read the IOAW from memory */ | |
dprintf (sel_dev, DEB_PIO, "Channel %s IOAW %06o from address %06o\n", | |
action_name [sequencer], *value, program_counter); | |
program_counter = program_counter + 1 & LA_MASK; /* increment the program counter */ | |
return; | |
} |