1000 diag does OTA SC,C to output command then waits up to 150 msec for flag to set then does SFC SC to check for command reject HP 13037 Disc Controller Simulation =================================== The current HP disc simulator library, contained in the files hp_disclib.c and hp_disclib.h, attempts to provide an abstraction of the HP 13037 MAC disc controller usable by the HP 1000 and 3000 simulators. While it works adequately with the HP 1000 simulator, attempting to adapt it to the 3000 simulator proves difficult. The problem is the extensive knowledge of the inner workings of the controller that is required to implement the CPU interface. This is due to the requirement to separate controller-specific actions from interface-specific actions and that the separation is weak. Ideally, the interface would know (and need to know) nothing about the controller other than that provided by the controller port to the interface. --------------------------- Current Controller Overview --------------------------- The current library exports these operational routines that are called by the interface: - dl_prepare_command - dl_start_command - dl_end_command - dl_poll_drives - dl_service_drive - dl_service_controller - dl_service_timer - dl_idle_controller - dl_clear_controller Commands are executed by scheduling events. The events are processed in phases; a typical command has a start phase, a data transfer phase, and an end phase. In addition to the CPU interface routine, a simulator must provide service routines for eight disc units, one controller unit, and one timer unit. A disc command is initiated by calling dl_prepare_command, which validates the command and determines if parameters are required before the command may begin. An invalid command (bad opcode or unit number) sets the appropriate status and returns FALSE. A valid command is set up and returns TRUE. If parameters are required, the interface obtains and loads them into the sector buffer before calling dl_start_command. When the interface receives a parameter word, it schedules the controller unit to receive the parameter. The interface controller service routine is responsible for copying the parameter value into the sector buffer and requesting another word if more parameters are required. Once all of the parameters are stored, or if dl_prepare_command indicated that no parameters were required, the interface calls dl_start_command to begin execution. Calling dl_start_command checks drive status and returns NULL if the drive is unavailable (e.g., not ready). If the drive is ready, the command is initiated, e.g., by obtaining the cylinder, head, and sector values from the command buffer. Commands that do not access the drive, e.g., Request Status, return a pointer to the controller unit. Commands that do access the drive return a pointer to the indicated unit. The caller is responsible for scheduling the unit. Commands that transfer data (e.g., Read) request DMA or channel operation during data phase processing within the interface unit service routine; the library is not involved. The library service routines may abort a command by calling dl_end_command, e.g., for a seek check or an end of cylinder. This routine is also called by the library for normal completion. The interface calls dl_end_command only to abort for a data overrun. The interface detects command completion by noting when the controller status changes from busy to waiting. At command completion, the interface is responsible for polling itself for a pending command and polling the drives for a pending attention request. The dl_poll_drives routine checks each drive and returns TRUE or FALSE, depending on whether or not a drive is requesting attention. While the controller is waiting for another command, a 1.8 second timer is running on the timer unit. If the timer expires before a command is initiated, then the controller is forcibly idled and the file mask is cleared. If an interface provides a means of idling the controller, e.g., via an Amigo Clear, the dl_idle_controller routine may be called directly. The interface may perform a hard clear on the controller by calling dl_clear_controller. This idles not only the controller but all disc units as well. Each interface service routine must first call the corresponding library service routine (dl_service_drive, dl_service_controller, or dl_service_timer) and then undertake any interface-specific actions required by the current command and phase. ----------------------------- 1000/3000 Hardware Provisions ----------------------------- The 13037 controller is designed to interface either to an HP 1000 or an HP 3000 computer. These systems have differing I/O control -- the 1000 uses direct I/O to set up commands and DCPC to transfer data, whereas the 3000 uses programmed I/O via the selector both to set up commands and to transfer data. As a result, the interface mechanisms to control the respective mechanisms are different. Consequently, the controller presents both 1000 and 3000 control information to the interface, which uses one or the other, depending on its design. For example, the controller asserts a STDFL signal to set the data flag on the 1000 interface and a RQSRV signal to assert a service request on the 3000 interface. In simulation, the library leaves all of the control to the interface. This provides a system-agnostic controller, but it requires the interface to have intimate knowledge of the state of the controller so that it can apply the correct operations, as those operations are command- and phase-dependent. --------------------------------- A Revised Interface/Control Model --------------------------------- An improvement over the current approach would be to model the signals presented to and returned from the controller. In particular, the controller would instruct the interface, via a returned set of signals, to perform the actions appropriate to the current command and phase, without the interface being aware of the controller state. Two communication paths would be required. First, the interface must notify the controller when it has a command or data available, e.g., a seek command or its parameters. It would do this by calling a controller routine and passing a set of signals that correspond to the Interface Flag Bus and a data word that corresponds to the Interface Data Bus. The routine would return a set of signals that correspond to the decoded Interface Function Bus and a data word that corresponds to the Interface Data Bus. Second, the controller must notify the interface when it requires interface action or has data to return to the interface, e.g., a seek completion event or data available from a read command. As with the first path, the controller would pass signals and data corresponding to the Interface Function Bus and Interface Data Bus. With the interface in a passive role, the responsibility for servicing the drive, controller, and timer events would return to the controller. Indeed, the interface need have no knowledge of unit events. Instead, the controller would handle all events and simply call the interface when the latter needed to take some action as directed by the controller. Because the controller would have to call the interface asynchronously in response to events, the interface would have to provide a callback routine whose address was passed in the controller state variable (CSV) structure. A difficulty is that the CSV is not indicated by an event service call, which is supplied only with a unit pointer. This would then require that the unit contain a CSV pointer. Alternately, the requirement to define interface event handlers could be retained for the purpose of calling the controller event handlers and passing the appropriate CSV pointer. In this implementation, the interface callback would not be needed, as the controller event handler could perform the same function by returning the signals and data. ------------------ Interface Flag Bus ------------------ The interface flag bus delivers status from the CPU interface to the controller, as follows: Code 1000 3000 Description ----- ---- ---- ---------------- * CLEAR Y Y Hard Clear CMRDY Y Y Command Ready DTRDY Y Y Data Ready EOD Y Y End of Data OVRUN Y Y Read Overrun XFRNG - Y Transfer No Good INTOK Y Y Interrupt OK * Actually, CLEAR is a separate signal line, not considered as part of the flag bus. Note that the 1000 interface does not assert XFRNG. ---------------------- Interface Function Bus ---------------------- The IFN bus issues commands from the controller to the CPU interface. The 1000 and 3000 interfaces respond to different function sets, as follows: Bus Code 1000 3000 Description --- ----- ---- ---- -------------------- 00 STINT Y Y Interrupt Request 01 STDFL Y - Information Request 02 IFPRF Y - Pre-Fetch Command 03 SELIF Y Y Select Interface 04 SRTRY - Y Set Retry Counter 05 DVEND Y Y Device End 06 - - - (unused) 07 RQSRV - Y Request Service 10 - - - (unused) 11 DSCIF Y Y Disconnect Interface 12 WRTIO - Y Write TIO Register 13 IFOUT Y Y Data Out to Disc 14 IFIN Y Y Data In from Disc 15 IFGTC Y Y Get Command 16 BUSY Y - Set/Clear Busy Flag 17 - - (unused) The CPU interface implements IFPRF by gating the command word onto the data bus in response to ENID (Enable Interface Drivers); IFPRF is not decoded explicitly. Commands are issued sequentially, but a given operation may generate multiple commands. For example, a normal Read termination generates STDFL, WRTIO, BUSY, and RQSRV. Note that the 1000 interface ORs STINT, STDFL, and DVEND to set the CPU flag. ----------------------- 7905/06 disc gates Protect Upper Disc and Protect Lower Disc switches with the output of the head register on the disc, so that the "protect" status reflects the position of the switch associated with the last head number stored. Implementing this would involve storing protect status in the unit STA word and setting it as part of each disc command (for discovery later in write and request status commands). The 7905/06 supports PROTECT=UPPER and PROTECT=LOWER, whereas the 7920/25 supports only PROTECT. In the unit flags, the latter would set both U and L bits. In processing, the UPPER bit would protect heads 0-1, and the LOWER bit would protect all heads >= 2 (so 2 on the 7905, 2-3 on the 7906, 2-4 on the 7920, and 2-8 on the 7925). ----------------------- IFN_IBUS dl_controller (CVPTR, UPTR, FLAGS, DATA) put dl_controller call: - in PCONTSTB - in READSTB (or IOI) - in WRITESTB (or IOO) - in unit service call all actions based on controller state and input/output signals - MAC uses 8 disc units (units 0-7) + 1 controller unit (unit 8) - ICD uses 4 disc/controller units (units 0-3) - commands that do not specify a unit (e.g., Set File Mask) execute entirely on the controller unit - commands that take parameters (e.g., Seek) execute on the controller unit to get the parameters and then on the specified unit for the remainder of the operation - each command employs one or more phases that are tracked on the unit in use - phase transitions are coincident with a call or return to the interface with distinct returned function sets for each phase - Writes call UNIT, STRT1, WSTA1, assert IFOUT, WRONE, EWRF1, SECTR, WSTA1, loop until done - UNIT subroutine - calls USTAT to check for unit <= 10; Unit Unavailable if not - checks hold bit; if set and not by this CPU, goes to poll loop - sets RAM for this drive and hold bit - calls BSYST to set BUSY and IFGTC - disconnects all drives and connects drive if unit if <= 7 - calls GSTAT to get drive status - STRT1/2 subroutine - calls DECS to check for EOC and seek if required - calls SWAIT to wait for seek completion - calls VRFY to verify the preceding sector - calls INCS to inrement back to the proper sector - WSTA1 subroutine - if the P bit is on for the verified sector, FORMAT must be on, else Protected Track - if the PROTECT switch is on, Status 2 Phases: - idle - parameter (in or out) - seek - rotate - data - intersector - end ------------------ Controller Signals ------------------ Poll Loop > IFGTC (flush) > BUSY 0 > DSCIF > SELIF <--loop < XFRNG < CMRDY (exec) > DSCIF - (test for attn) - -->loop Drive Attention > SELIF < XFRNG < CMRDY (attn3) > IFPRF - --> exec1 - (attn3) > STINT Command Wait < XFRNG <--loop < CMRDY -->loop > IFPRF - -->cvect Get Parameter > STDFL < DTRDY (loop until true) > IFOUT data < XFRNG Put Value < DTRDY (loop until false) > IFIN data < XFRNG > STDFL General Error > WRTIO (statr) > BUSY 0 > STINT - --> cmd wait Retryable Error > WRTIO > BUSY 0 > DVEND > RQSRV - --> cmd wait -------- Commands -------- Cold_Load_Read > BUSY 1 > IFGTC - --> seek1 > SRTRY 0 - --> rboot Recalibrate > BUSY 1 > IFGTC - (recal) > WRTIO (statr) - --> poll loop > IFGTC (flush) > BUSY 0 Seek > BUSY 1 > IFGTC - --> get (cyl) - --> get (hdsec) - --> seek1 > WRTIO (statr) > RQSRV - --> poll loop > IFGTC (flush) > BUSY 0 Request_Status > BUSY 1 > IFGTC - --> put (statr) - --> put (stat2) < DTRDY (loop until true) < XFRNG > WRTIO > BUSY 0 > RQSRV - --> cmd wait Request_Sector_Address > BUSY 1 > IFGTC - --> put (sector) < XFRNG > WRTIO (statr) > BUSY 0 > RQSRV - --> cmd wait Read > BUSY 1 > IFGTC - --> dstrt > IFIN < XFRNG - --> read3 - IBUS = data, IFCLK; loop for sector < OVRUN - --> ewrf1 < EOD - --> loop for next sector if not done - clear IFIN > STDFL < XFRNG > WRTIO > BUSY 0 > RQSRV - --> cmd wait Read_Full_Sector > BUSY 1 > IFGTC - --> dstrt > IFIN < XFRNG - --> read3 - IBUS = data, IFCLK; loop for sector < OVRUN - --> ewrfs < EOD - --> loop for next sector if not done - clear IFIN > STDFL < XFRNG > WRTIO > BUSY 0 > RQSRV - --> cmd wait Verify > BUSY 1 > IFGTC - --> get - read sector without transferring data (IFVLD = 0) < OVRUN - check for sector error - --> look for next sector if not done - clear IFIN > STDFL < XFRNG > WRTIO > BUSY 0 > RQSRV - --> cmd wait Write > BUSY 1 > IFGTC > IFOUT < XFRNG - --> wrone - data = IBUS, IFCLK; loop for sector - --> ewrf1 < EOD - clear IFOUT > STDFL < XFRNG > WRTIO > BUSY 0 > RQSRV - --> cmd wait Write_Full_Sector > BUSY 1 > IFGTC > IFOUT < XFRNG - --> wrone - data = IBUS, IFCLK; loop for sector - --> ewrfs < EOD - clear IFOUT > STDFL < XFRNG > WRTIO > BUSY 0 > RQSRV - --> cmd wait Clear > BUSY 1 > IFGTC > WRTIO (statr) > BUSY 0 > STDFL - --> cmd wait Initialize > BUSY 1 > IFGTC > IFOUT < XFRNG - --> wrone - data = IBUS, IFCLK; loop for sector < EOD - clear IFOUT > STDFL < XFRNG > WRTIO > BUSY 0 > RQSRV - --> cmd wait Address_Record > BUSY 1 > IFGTC - --> get (cyl) - --> get (hdsec) < XFRNG > WRTIO (statr) > BUSY 0 > RQSRV - --> cmd wait Request_Syndrome > BUSY 1 > IFGTC --> put (status) --> put (cylinder) --> put (head-sector) --> put (pattern 1) --> put (pattern 2) --> put (pattern 3) < XFRNG > WRTIO (statr) > BUSY 0 > RQSRV - --> cmd wait Read_With_Offset > BUSY 1 > IFGTC - --> get > RQSRV > STDFL - --> dstrt > IFIN < XFRNG - --> read3 - IBUS = data, IFCLK; loop for sector < OVRUN < EOD - --> loop for next sector if not done - clear IFIN > STDFL < XFRNG > WRTIO > BUSY 0 > RQSRV - --> cmd wait Set_File_Mask > BUSY 1 > IFGTC > SRTRY < XFRNG > WRTIO > BUSY 0 > STDFL - --> cmd wait Invalid_Opcode > IFGTC - --> general error Read_Without_Verify > BUSY 1 > IFGTC - --> read1 Load_TIO_Register > BUSY 1 > IFGTC - --> get > WRTIO > RQSRV > BUSY 0 - --> cmd wait Request_Disc_Address > BUSY 1 > IFGTC --> put (cylinder) --> put (head-sector) < XFRNG > WRTIO > BUSY 0 > RQSRV - --> cmd wait End - --> poll loop > IFGTC (flush) > BUSY 0 Wakeup > BUSY 1 > IFGTC > WRTIO (statr) > BUSY 0 > STDFL - --> cmd wait ------------- Clear Command ------------- HP1000: - CLC EOD = 1 - IOO CMRDY = 1 dl_controller (CMRDY | EOD, Clear) -> BUSY BUSY_1 -> BUSY = 1 activate controller - dl_service_controller (CMRDY | EOD, 0) -> IFGTC | WRTIO | FREE | STDFL + status_1 IFGTC -> CMRDY = 0, EOD = 0 BUSY_0 -> BUSY = 0 STDFL -> STF HP3000: - 040000 Control (no wait) - 005000 Clear PCMD1 -> CHANACK | CHANSR DTRDY = 0 PCONTSTB -> CHANACK CMRDY = 1 dl_controller (CMRDY, Clear) -> BUSY (cntl) Idle_Phase -> End_Phase, wait = end_delay service_controller dl_controller (NOFLAG, 0) -> IFGTC | WRTIO | FREE | STDFL + status_1 (cntl) End_Phase -> Idle_Phase, wait = 0 WRTIO -> status_word = status_1 IFGTC -> CHANSR CMRDY = 0 - 034000 End with Interrupt - 177777 (status) PSTATSTB -> CHANACK + status_word ---------------------- Request Status Command ---------------------- HP1000: - CLC EOD = 1 - IOO CMRDY = 1 dl_controller (CMRDY | EOD, Request_Status) -> BUSY_1 | IFGTC BUSY_1 -> BUSY = 1 IFGTC -> CMRDY = 0, EOD = 0 activate controller - dl_service_controller (NOFLAGS, 0) -> IFIN | STDFL + status_1 IFIN -> fifo = buffer DTRDY = 1 STDFL -> STF - IOI outbound = fifo DTRDY = 0 dl_controller (NOFLAGS, 0) -> IFIN | STDFL + status_2 IFIN -> fifo = buffer DTRDY = 1 STDFL -> STF - IOI outbound = fifo DTRDY = 0 dl_controller (NOFLAGS, 0) -> WRTIO | FREE | RQSRV + status_1 BUSY_0 -> BUSY = 0 HP3000: - 040001 Control (wait) - 001400 Request Status PCMD1 -> CHANACK | CHANSR DTRDY = 1 PCONTSTB -> CHANACK CMRDY = 1 dl_controller (CMRDY | DTRDY, Request_Status) -> BUSY | IFGTC (cntl) Idle_Phase -> Parameter_Phase, wait = 0 IFGTC -> CHANSR CMRDY = 0 - 077776 Read (2 words) - nnnnnn (address) TOGGLEINXFER -> CHANACK DTRDY = 0 dl_controller (NOFLAGS, 0) -> IFIN | STDFL + status_1 (cntl) Parameter_Phase -> Parameter_Phase, wait = 0 IFIN -> CHANSR buffer_word = status_1 DTRDY = 1 PREADSTB -> CHANACK outbound = buffer_word (status 1) DTRDY = 0 dl_controller (NOFLAGS, 0) -> IFIN | STDFL + status_2 (cntl) Parameter_Phase -> Parameter_Phase, wait = 0 IFIN -> CHANSR buffer_word = status_2 DTRDY = 1 PREADSTB -> CHANACK outbound = buffer_word (status 2) DTRDY = 0 dl_controller (NOFLAGS, 0) -> WRTIO | FREE | RQSRV + status_1 (cntl) Parameter_Phase -> Idle_Phase, wait = 0 WRTIO -> status_word = status_1 RQSRV -> CHANSR - 034000 End with Interrupt - 177777 (status) PSTATSTB -> CHANACK + status_word ---- Seek ---- HP1000: - CLC EOD = 1 - IOO CMRDY = 1 dl_controller (CMRDY | EOD, Seek) -> BUSY_1 | IFGTC | STDFL BUSY_1 -> BUSY = 1 IFGTC -> CMRDY = 0, EOD = 0 STDFL -> STF - IOO buffer = fifo DTRDY = 1 dl_controller (DTRDY, data) -> IFOUT | STDFL IFOUT -> DTRDY = 0 STDFL -> STF - IOO buffer = fifo DTRDY = 1 dl_controller (DTRDY, data) -> IFOUT | WRTIO | RQSRV | FREE + status_1 IFOUT -> DTRDY = 0 Note that STF must occur after any programmed CLF (e.g., OTA sc,C). HP3000: - 040000 Control (no wait) - 001000 Seek PCMD1 -> CHANACK | CHANSR DTRDY = 0 PCONTSTB -> CHANACK CMRDY = 1 dl_controller (CMRDY, Seek) -> BUSY | IFGTC | STDFL (cntl) Idle_Phase -> Parameter_Phase, wait = 0 IFGTC -> CHANSR CMRDY = 0 - 067776 Write (2 words) - nnnnnn (address) TOGGLEOUTXFER -> CHANACK | CHANSR PWRITESTB -> CHANACK buffer_word = inbound (cylinder) DTRDY = 1 dl_controller (DTRDY, data) -> IFOUT | STDFL (cntl) Parameter_Phase -> Parameter_Phase, wait = 0 IFOUT -> CHANSR DTRDY = 0 PWRITESTB -> CHANACK buffer_word = inbound (head/sector) DTRDY = 1 dl_controller (DTRDY, data) -> IFOUT | WRTIO | RQSRV | FREE + status_1 (cntl) Parameter_Phase -> Idle_Phase, wait = 0 (disc) Idle_Phase -> Seek_Phase, wait = seek_delay IFOUT -> CHANSR DTRDY = 0 WRTIO -> status_word = status_1 RQSRV -> CHANSR - 030000 End - 177777 (status) PSTATSTB -> CHANACK + status_word - service_drive dl_controller (NOFLAG, 0) -> WRTIO | FREE | STINT (disc) Seek_Phase -> Idle_Phase, wait = 0 STINT -> request_interrupt -------------- Cold Load Read -------------- HP3000: - 040000 Control (no wait) - 000000 Cold Load Read PCMD1 -> CHANACK | CHANSR DTRDY = 0 PCONTSTB -> CHANACK CMRDY = 1 dl_controller (CMRDY, Cold Load Read) -> BUSY | IFGTC (cntl) Idle_Phase -> Parameter_Phase, wait = 0 IFGTC -> CHANSR CMRDY = 0 - 070000 Read (4096 words) - nnnnnn (address) TOGGLEINXFER -> CHANACK DTRDY = 0 dl_controller (NOFLAGS, 0) -> SRTRY + retry_value (cntl) Parameter_Phase -> Idle_Phase, wait = 0 (disc) Idle_Phase -> Seek_Phase, wait = seek_delay SRTRY retry_counter = retry_value service_drive dl_controller (NOFLAG, 0) -> IFIN + data_word (disc) Seek_Phase -> Data_Phase, wait = data_delay IFIN -> CHANSR buffer_word = data_word DTRDY = 1 PREADSTB -> CHANACK outbound = buffer_word DTRDY = 0 dl_controller (NOFLAG, 0) -> NOFUNCTION service_drive dl_controller (NOFLAG, 0) -> IFIN + data_word (disc) Data_Phase -> Data_Phase, wait = data_delay IFIN -> CHANSR buffer_word = data_word DTRDY = 1 PREADSTB -> CHANACK outbound = buffer_word DTRDY = 0 dl_controller (NOFLAG, 0) -> NOFUNCTION service_drive dl_controller (NOFLAG, 0) -> IFIN + data_word (disc) Data_Phase -> Data_Phase, wait = data_delay IFIN -> CHANSR buffer_word = data_word DTRDY = 1 [4093 more words] PREADSTB | TOGGLEINXFER | EOT -> CHANACK outbound = buffer_word DTRDY = 0 dl_controller (NOFLAG, 0) -> NOFUNCTION EOT -> EOD service_drive dl_controller (EOD, 0) -> STDFL | WRTIO | FREE | RQSRV + status_1 (disc) Data_Phase -> End_Phase, wait = data_delay WRTIO -> status_word = status_1 RQSRV -> CHANSR - 034000 End with Interrupt - 177777 (status) PSTATSTB -> CHANACK + status_word