***********************************************************************************
Hytec 8601 Step Motor Controller Asyn Driver Notes

Author: Jim Chen, Hytec Electronics Ltd
		jim.chen@hytec-electronics.co.uk

First Initial: 12/NOV/2010
Last updated: 29/FEB/2012

Modification note:
22/FEB/2012 - corrected memory offset calculation errors reported by Ernest Williams SLAC.
29/FEB/2012 - added coverage for all cases when setting up the MEMOFFS for 8002 carrier.

This driver is based on "model 3" motor asyn driver defined by Mark Rivers. 
***********************************************************************************

Support modules
===============

1). asyn driver version asyn4-13-1 or later.

2). ipac module version ipac-2.11 plus one of the Hytec carrier card drivers such as 
	drvHy8002.c for VxWorks or RTEMS under VME64x with 8002/8003/8004 carriers, 
	drvHyLinuxCarrier.c for Linux with IOC9010/PCIe6335/uTCA7002 carriers, 
	drvHyRTEMSCarrier.c for RTEMS with IOC9010 blade.

3). devLib2 version 2.1 or later if using RTEMS on IOC9010 blade.

4). motor module version later than motorR6-5-1. Currently the 'model 3' version can be 
	downloaded from subversion https://subversion.xor.aps.anl.gov/synApps/motor/trunk.
	This will be formally released some stage on the official EPICS website.

5). EPICS core R3.14.8.2 or later.

6). RTEMS R4.9.4 or later.


Hytec 8002/8003/8004 carrier card configuration
===============================================

This board is a 6U VME64x carrier card. It provides four single size IP slots and 
is configurable for many parameters. It supports interrupt level from 0 to 7. Any 
IP cards can be enabled/disabled interrupt. The clock can be set to either 8MHz or 
32MHz. For IPAC Memory space if required, the base address can be defined automatically 
by geographical addressing when VME64x crate is used. For other type of crates, 
the base address can be set by configuring a series of jumpers on the board or by 
passing parameter to the memory offset register in the ipacAddCarrier call.

The IPAC Carrier Driver for this board is found in the file drvHy8002.c which 
implements two commands ipacAddHy8002 and Hy8002CarrierInfo. The ipacAddHy8002 
command is used to add a Hytec 8002/8003 board to the system. The Hy8002CarrierInfo 
reports hardware information for a specified board or all boards in the system if 
the parameter passed down is 0xFFFF. These commands are registered by the registrar 
routine Hy8002Registrar to add them to the iocsh and link the driver into a final 
IOC executable, for which it must be listed in the IOC's .dbd file thus:

	registrar(Hy8002Registrar)


1). Configuration Command and Parameter
--------------------------------------

- int ipacAddHYy8002(const char *cardParams);

The parameter string should comprise two (2) to six (6) parameters which are comma 
separated. The first two are mandate and have to be separated only by one comma. 
The others are key/value pairs and are optional as per the conditions of the operating
system used. The format is defined as

s,i,IPMEM=d,IPCLCK=d,ROAK=d,MEMOFFS=d

where d is a decimal integer number.
s		defines the VME slot number of the carrier card. Valid number is 2 ~ 21.
		this number MUST be the same as the VME slot number where the carrier 
		card is plugged in. 
i		defines the interrupt level. Valid number is 0 ~ 7.
IPMEM=d defines the maximum memory size of the IP module. The valid values are 1, 2, 
		4 or 8 that represent 1MB, 2MB, 4MB or 8MB respectively. Default is 1. For
		majority Hytec ADCs, DACs such as 8401, 8414, 8417, 8402 and 8415 etc, they 
		all have 2MB on board. The user can choose to use either 1MB or 2MB. None of
		the Hytec IPs has more than 2MB on board memory. Other vendors might have.
IPCLCK=d 	defines the clock that its value has to be either 8 for 8MHz or 32 for 
		32Mhz. Default is 8.
ROAK=d 	if d =1, it defines carrier card to release the interrupt upon the 
		acknowledgment. If d=0, the interrupt is released by user interrupt 
		service routine. Default is 0.
MEMOFFS=d	this parameter defines the A32 memory access base address. "d" is a decimal
		number that represents the offset (the upper WORD) of the A32 base address. 
		It is needed when any of the two statements below is true:

		A. The operating system either VxWorks or RTEMS has defined a non-zero
		VME_A32_MSTR_LOCAL macro in the system config.h file.

		B. The VME crate is not geographical addressing	facilitated. In such a system,
		user must use this to set up base address for A32. 
		
		For a VME crate that is geographical addressing facilitated, and the system
		defines a non-zero VME_A32_MSTR_LOCAL macro, then it is needed. But if
		VME_A32_MSTR_LOCAL macro is defined as 0, then this is optional. Setting this 
		the driver will turn off the carrier card geographical addressing by setting 
		a bit in the CSR. 
				
Note:   MEMOFFS has nothing to do with A16 base address formation. A16 base address
		is determined either by geographical addressing or by carrier board on board 
		jumpers settings. For VME crate that is not geographical addressing facilitated, 
		both 8002 and 8004 carriers need to use on board jumpers (J6~J10) to set up 
		(On 8004 carrier, moving the jumpers to "manual" positions). When the VME 
		crate is geographical addressing facilitated, for 8002 carrier, A16 base 
		address is determined by the crate geographical addressing facility, i.e. 
		determined automatically by the slot number where the carrier is plugged in 
		(the actually A16 base address is determined as vmeslotnumber << 11). But for 
		8004 carrier, user can have a choice to either use the on board jumpers to
		manually set up (moving jumpers J6~J10 away from "auto" to "manual" positions)
		or let the the geographical addressing to determine it (keep all J6 ~ J10 to 
		"auto" position).  
				
Calcualtion of MEMOFFS:

As mentioned above, MEMOFFS setting represents the upper WORD of A32 VME address. Two 
things need to be considered when doing the calculation: IPMEM setting and the the  
VME_A32_MSTR_LOCAL macro definition in the config.h file of the BSP.

IPMEM defines the memory size per IP card. Either 8002 or 8004 has up to 4 IPs so
the carrier memory size is 4 times of the IP memory. The minimum size of an IP, which 
is also the default setting (if IPMEM is not defined), is 1MB. Hence the MEMOFFS 
should start from address line A22 as shown below

MEMOFFS BIT 15  14  13  12  11  10  09  08  07  06  05  04  03  02  01  00
           A31 A30 A29 A28 A27 A26 A25 A24 A23 A22   0   0   0   0   0   0

The MEMOFFS must be defined such that any two carriers in the same crate shuoldn't 
have overlapped memory area. So when IPMEM changes, the starting address line for 
calculating MEMOFFS changes as well as illustrated below: 

	IPMEM	memory per IP	carrier memory size		starting address line
	  1			 1MB				4MB						A22
	  2			 2MB				8MB						A23
	  4			 4MB				16MB					A24
	  8			 8MB				32MB					A25

all address lines below this starting address must be 0.

VME_A32_MSTR_LOCAL macro is the system base and it just needs to be added on to the 
calculation.

In principle, calculating MEMOFFS doesn't have to correspond it to VME slot number 
that the carrier is plugged in. The only thing matters is as said that any two MEMOFFS 
settings for any two carriers in the same crate should not overlap. Yet associating 
the VME slot number in the calculation just makes better logical sense and fits the 
natual of human being's thinking. Some examples are shown below.

Let's assume IPMEM=1 (the default setting), this gives 4MB memory space for a 8002 
carrier so starting address line is A22. The remaining must be 0.      

MEMOFFS BIT 15  14  13  12  11  10  09  08  07  06  05  04  03  02  01  00
           A31 A30 A29 A28 A27 A26 A25 A24 A23 A22   0   0   0   0   0   0

For a carrier in VME slot 2, we can define its A32 base address as 0x00400000,  
plus VME_A32_MSTR_LOCAL. For VME slot 3, it could be 0x00800000 plus VME_A32_MSTR_LOCAL 
and for VME slot 4, it could be 0x00C00000 plus VME_A32_MSTR_LOCAL and so forth. 

Assuming VME_A32_MSTR_LOCAL is 0x20000000, then for VME slot 4, the calculated base 
address should be 0x00C00000 + 0x20000000 = 0x20C000000. Hence the MEMOFFS = 8384 
(decimal, i.e. 0x20C0). For slot 5, the derived base address could be 0x01000000 + 
0x20000000 = 0x21000000. Hence the MEMOFFS = 8448 (decimal, i.e. 0x2100) and so forth.

Now if IPMEM=2, this gives 8MB memory space for each 8002 carrier so the starting address 
line is A23. The remaining must be 0.

MEMOFFS BIT 15  14  13  12  11  10  09  08  07  06  05  04  03  02  01  00
           A31 A30 A29 A28 A27 A26 A25 A24 A23   0   0   0   0   0   0   0

For a carrier in VME slot 2, we could define its A32 base address as 0x00800000 
plus VME_A32_MSTR_LOCAL. For VME slot 3, the base address could be 0x01000000 plus 
VME_A32_MSTR_LOCAL and for VME slot 4, the base address could be 0x01800000 plus 
VME_A32_MSTR_LOCALand so forth. 

Assuming VME_A32_MSTR_LOCAL is still 0x20000000, then for VME slot 4, the calculated 
base address should be 0x01800000 + 0x20000000 = 0x218000000. Hence the MEMOFFS = 8576 
(decimal, i.e. 0x2180). For slot 5, the derived base address could be 0x02000000 + 
0x20000000 = 0x22000000. Hence the MEMOFFS = 8704 (decimal, i.e. 0x2200) and so forth.



- int Hy8002CarrierInfo(int carrier);

where 'carrier' is the registered carrier number in the system. If it is specified, 
this function prints out the specified carrier hardware information. If carrier = 0xFFFF, 
then all carriers' hardware information will be printed out. 


2). Configuration Examples
-------------------------

ipacAddHy8002("3,2") 

This indicates that the carrier is in slot 3 and the interrupt level is set to 2. 
IP memory uses default 1MB. Clock uses default 8MHz. RORA as default. use geographical 
addressing etc.

ipacAddHy8002("5,4,IPMEM=1,IPCLCK=8,ROAK=1,MEMOFFS=256 ") 

Here the slot is 5, interrupt level is 4. IP memory size is 1MB, clock uses 8MHz. 
Use ROAK but not using geographic addressing. The memory offset is 256 which means its 
base address is 0x01000000 assuming the VME_A32_MSTR_LOCAL is set to 0x00000000.

3). Interrupt Commands Supported
-------------------------------

The interrupt level can be set by the second parameter of the ipacAddHy8002 routine. 
Individual IP module can be set to generate interrupt or not. The commands supported 
for ipmIrqCmd are illustrated below.  

cmd			Value Returned
ipac_irqGetLevel	Carrier interrupt level (0 ~ 7)
ipac_irqEnable		0 = OK
ipac_irqDisable		0 = OK
ipac_irqPoll		>0 if the interrupt line is active, else 0
(other commands)	S_IPAC_notImplemented


Hy8601 IP Asyn Driver Usage
============================

1). Configuration shell command
-------------------------------

The configuration ioc shell command takes the form below.

int Hytec8601Configure(char *portName,
				  	epicsUInt16 numAxes,
				  	epicsUInt16 movingPollPeriod,
					epicsUInt16 idlePollPeriod,
				  	epicsUInt16 cardnum,
                 	epicsUInt16 ip_carrier,
                 	epicsUInt16 ipslot,
                 	epicsUInt16 vector,
                 	epicsUInt16 useencoder,
                 	epicsDouble encoderRatio0,
                 	epicsDouble encoderRatio1,
                 	epicsDouble encoderRatio2,
                 	epicsDouble encoderRatio3)

Where:
    (1) portName   asyn port name
    (2) numAxes    number of axes 
    (3) movingPollPeriod  status polling time period when motor is moving in ms
    (4) idlePollPeriod    status polling time period when motor is stopped in ms
    (5) cardnum    Arbitrary card number to assign to this controller (currently not used)
    (6) ip_carrier which previously configured IP carrier in the IOC
    (7) ipslot     which IP Slot on carrier card (0=A etc.)
    (8) vector     which Interrupt Vector (0 - Find One ?)
    (9) useencoder - bit0 for axis0, bit1 for axis1, bit2 for axis2 and bit3 for axis3. 
				   Other bits not used. 1=use encoder, 0=don't use encoder !
    (10) encoderRatio0  - axis0 hardware encoder ratio
    (11) encoderRatio1  - axis1 hardware encoder ratio
    (12) encoderRatio2  - axis2 hardware encoder ratio
    (13) encoderRatio3  - axis3 hardware encoder ratio

Example:

	Hytec8601Configure("Hy8601", 4, 500, 1000, 0, IPAC0, 0, 70, 15, 0.25, 0.25, 0.25, 0.25)

This configures the 8601 card with 
	port name: Hy8601
	4 axes
	poll status every 500ms when moving
	poll status every 1000ms when  stopped
	card number = 0 
	carrier serial number = 0
	ipslot = site A
	interrupt vector = 70
	all 4 axes use encoder
	all 4 axes have quardrature encoder hence their ratios are 0.25

Please note, since the parameters in the function is more than 10, in VxWorks, the start up 
script loading will complain about " too many parameters..." In this case, adding

	iocsh()

before this routine in start up script will solve the problem.


2). Database definition file
----------------------------

Database definition file is: HytecMotorDriver.dbd.

3). Databases
-------------

Two databases can be loaded:

	dbLoadRecords("db/basic_asyn_motor.db")
	dbLoadRecords("db/HytecMotorControl.db", "dev=IP8601,area=TEST,locn=LAB,PORT=Hy8601")

Where:
	basic_asyn_motor.db is the motor record database. It initialises 4 axes.
	HytecMotorControl.db is the special controls for Hy8601 motor controller. It contains few extra asyn records 
		including POWER, BRAKE controls, synchronous read of absolute position and enocder position and firmware
		version etc as below.

	record(bo,"$(dev):$(area):$(locn):POWER") {
	    field(DTYP,"asynInt32")
	    field(OUT,"@asyn($(PORT) 0)HYTEC_POWER")
	    field(VAL, "1")
	    field(ONAM, "On")
	    field(ZNAM, "Off")
	}

	record(bo,"$(dev):$(area):$(locn):BRAKE") {
	    field(DTYP,"asynInt32")
	    field(OUT,"@asyn($(PORT) 0)HYTEC_BRAKE")
	    field(VAL, "1")
	    field(ZNAM, "Set")
	    field(ONAM, "Rls")
	}

	record(ai,"$(dev):$(area):$(locn):POSN") {
	    field(PINI, "YES")
	    field(DTYP,"asynInt32")
	    field(INP,"@asyn($(PORT) 0)MOTOR_POSITION")
	    field(SCAN, "1 second")
	}

	record(ai,"$(dev):$(area):$(locn):EN_POSN") {
	    field(PINI, "YES")
	    field(DTYP,"asynInt32")
	    field(INP,"@asyn($(PORT) 0)MOTOR_ENCODER_POSITION")
	    field(SCAN, "1 second")
	}
	
	record(ai,"$(dev):$(area):$(locn):FIRMWARE_VERSION") {
    	field(PINI, "YES")
    	field(DTYP,"asynInt32")
    	field(INP,"@asyn($(PORT) 0)HYTEC_FWVERSION")
    	field(SCAN, "1 second")
	}

There are other records in the HytecMotorControl.db which can return the positions synchronously etc. 

These positions are raw values in terms of the "counts". These records can be read by "ai" records 
and can be converted to engineering values by EGUF, EGUL etc.

Notes:

	- The power control uses AUX1 of the 8601 CSR. As it indicates in the record above, command

		caput $(dev):$(area):$(locn):POWER 1

	  means power "On" and it sets the AUX1 bit, i.e. AUX1 output high (1).

		caput $(dev):$(area):$(locn):POWER 0

	  means power "Off" so it clears AUX1 bit, i.e. AUX1 output low (0). Command

	- The brake control uses AUX2 of the 8601 CSR. As it indicates in the record above, command

		caput $(dev):$(area):$(locn):BRAKE 1

	  means to release the brake. It clears AUX2 bit, i.e. AUX2 output low (0). Command

		caput $(dev):$(area):$(locn):BRAKE 0

	  means set the brake. It sets the AUX2 bit, i.e. AUX2 output high (1).

	- The firmware version record returns the IP card PCB board issue number and the firmware version
	
		caget $(dev):$(area):$(locn):FIRMWARE_VERSION
		
	  will return something like '2210' for example, whereas the first 2 is the PCB issue number and 
	  210 is the firmware version.
	
4). To build an example application 
-----------------------------------

To build an example to test the 8601 asyn driver, use EPICS makeBaseApp.pl script to create 
the example as usual. Then modify the following files to include the driver module(s).

	- <TOP>/configure/RELEASE

		Change the EPICS_BASE and SUPPORT to the proper directories.
		Add ASYN, IPAC and MOTOR moudles:
		
		ASYN=$(SUPPORT)/asyn/asyn4-14
		IPAC=$(SUPPORT)/ipac/ipac-2.11
		MOTOR=<the motor module dir>/motorR6-5-2
		
	- <TOP>xxxApp/src/Makefile

		In the Makefile of the example src, add following lines:
		
		example_DBD += asyn.dbd
		example_DBD += motorSupport.dbd
		example_DBD += motorRecord.dbd
		example_DBD += drvIpac.dbd
		example_DBD += HytecMotorDriver.dbd
		 
		example_LIBS += HytecMotor
		example_LIBS += motor
		example_LIBS += Ipac
		example_LIBS += asyn
		

	- <TOP>/xxxApp/Db/Makefile

		Copy HytecMotorControl.db to the Db directory and add the following lines in the Makefile of 
		the Db directory:
		
		DB += basic_asyn_motor.db
		DB += HytecMotorControl.db
		
	- Build the application from <TOP>
	
	- Modify st.cmd in iocBoot/iocexample as per next section and run the start up script from here.

5). Start up script example 
---------------------------

The following example is for an IOC that uses RTEMS R4.9.4, MVME5500 processor board.

	# Change directory to TOP of application
	cd("../..")
	iocBoot=pwd()
	ld( "bin/RTEMS-mvme5500/MotionControl.obj")
	#ld < bin/vxWorks-ppc604_long/MotionControl.munch	#for VxWorks

	## Set common environment variables
	#< all/pre_st.cmd
	epicsEnvSet("IOC_NAME",  "MC02")
	epicsEnvSet("LOCA_NAME", "B025")
	epicsEnvSet("ENGINEER", "Condamoor, Shantha")

	epicsEnvSet( "EPICS_CA_MAX_ARRAY_BYTES", "30000")

	# Register all support components
	dbLoadDatabase( "dbd/MotionControl.dbd")
	MotionControl_registerRecordDeviceDriver( pdbbase) 

	bspExtVerbosity=0

	###########################################################
	# Configure Hytec 8002 carriers
	#   8002 carrier VME slot: 3
	#	INT level: 5
	#	Memory per IP: 1MB
	#	Memory mapping offset: 8384
	# ===================================================================
	#The typical output on beatnik looks like this:
	# Cexp@till35>BSP_VMEOutboundPortsShow()
	# Tsi148 Outbound Ports:
	# Port  VME-Addr   Size       PCI-Adrs   Mode:
	# 0:    0x20000000 0x0e000000 0x90000000 A32, SUP, D32, SCT
	# 1:    0x00000000 0x00ff0000 0x9f000000 A24, SUP, D32, SCT
	# 2:    0x00000000 0x00010000 0x9fff0000 A16, SUP, D32, SCT
	# 7:    0x00000000 0x01000000 0x9e000000 CSR, SUP, D32, SCT
	# ===================================================================

	# A32 space configured to start here:  0x20000000
	# Tell the carrier not to use geographic addressing
	IPAC0=ipacAddHy8002("3,5,IPMEM=1,MEMOFFS=8384")

	#
	# Hytec MDS-8 8601 driver setup parameters:
	# int Hytec8601Configure(char *portName,
	#				  epicsUInt16 numAxes,
	#				  epicsUInt16 movingPollPeriod,
	#				  epicsUInt16 idlePollPeriod,
	#				  epicsUInt16 cardnum,
	#                 epicsUInt16 ip_carrier,
	#                 epicsUInt16 ipslot,
	#                 epicsUInt16 vector,
	#                 epicsUInt16 useencoder,
	#                 epicsDouble encoderRatio0,
	#                 epicsDouble encoderRatio1,
	#                 epicsDouble encoderRatio2,
	#                 epicsDouble encoderRatio3)
	#     (1) portName   asyn port name
	#     (2) numAxes    number of axes 
    #	  (3) movingPollPeriod  status polling time period when motor is moving in ms
    #	  (4) idlePollPeriod    status polling time period when motor is stopped in ms
	#     (5) cardnum    Arbitrary card number to assign to this controller, not used
	#     (6) ip_carrier which previously configured IP carrier in the IOC
	#     (7) ipslot     which IP Slot on carrier card (0=A etc.)
	#     (8) vector     which Interrupt Vector (0 - Find One ?)
	#     (9) useencoder - bit0 for axis0, bit1 for axis1, bit2 for axis2 and bit3 for axis3. 
	#					   Other bits not used. 1=use encoder, 0=don't use encoder !
	#     (10) encoderRatio0  - axis0 hardware encoder ratio
	#     (11) encoderRatio1  - axis1 hardware encoder ratio
	#     (12) encoderRatio2  - axis2 hardware encoder ratio
	#     (13) encoderRatio3  - axis3 hardware encoder ratio

	Hytec8601Configure("Hy8601", 4, IPAC0, 0, 0, 70, 0, 0.25, 0.25, 0.25, 0.25)


	# ========================================================
	# New Hytec Motor Databases
	# =======================================================
	dbLoadRecords("db/basic_asyn_motor.db")
	dbLoadRecords("db/HytecMotorControl.db", "dev=IP8601,area=TEST,locn=LAB,PORT=Hy8601")
	# ========================================================

	iocInit()




		


