Commit 21bb5eeb authored by Benoit Rat's avatar Benoit Rat

WBNode: improve syncing with blovck syncing

parent 7f13fe62
......@@ -72,6 +72,7 @@ WBField::WBField(WBReg *pReg,
if(nfb>0) this->type|=WBF_TM_FIXED_POINT;
this->nfb=nfb;
this->desc=desc;
this->forceSync=false;
this->checkOverflow=true;
TRACE_P_DEBUG("%s type=0x%0x nfb=%d, dVal=%f",name.c_str(),type,nfb,iniVal);
......@@ -242,6 +243,21 @@ bool WBField::convert(float *pVal, bool to_value)
{
return (pReg)?regCvt(pVal,&(pReg->data),to_value):false;
}
/**
* Shortcut to setup the register to be sync with the device ASAP.
*
* \note Once the corresponding register flag toSync is true it is not possible to reset
* it unless calling to WBReg::sync() method. Calling to WBField::sync() will not reset this
* flag.
*
* \return true if everything okay. otherwirse false if pointer on register is unvalid.
*/
bool WBField::setToSync()
{
TRACE_CHECK_PTR(pReg,false);
pReg->toSync=true;
return true;
}
/**
* Shortcut function to return a float from the data in the field
......@@ -281,7 +297,7 @@ uint32_t WBField::getU32() const
bool WBField::sync(WBMemCon *con, WBAccMode amode)
{
bool ret=true;
uint32_t value;
uint32_t oldval, value;
//Perform some check
if(pReg==NULL) return false;
......@@ -291,12 +307,14 @@ bool WBField::sync(WBMemCon *con, WBAccMode amode)
if(amode & WB_AM_W)
{
//Get current value
ret &=con->mem_access(pReg->getOffset(true),&value,false); //Read WB from dev
TRACE_P_DEBUG("ret=%d value=%0x",ret,value);
value=(pReg->data & mask) | (value & ~mask); //Update only our field
TRACE_P_DEBUG("ret=%d value=%0x",ret,value);
ret &=con->mem_access(pReg->getOffset(true),&value,true); //Write WB to dev
TRACE_P_DEBUG("ret=%d value=%0x",ret,value);
ret &=con->mem_access(pReg->getOffset(true),&oldval,false); //Read WB from dev
value=(pReg->data & mask) | (oldval & ~mask); //Update only our field
TRACE_P_DEBUG("%-10s (@0x%08X) ret=%d old=0x%x new=0x%x",getCName(),pReg->getOffset(true),ret,oldval,value);
if(oldval != value || forceSync)
{
ret &=con->mem_access(pReg->getOffset(true),&value,true); //Write WB to dev
TRACE_P_DEBUG("%-10s (@0x%08X) ret=%d value=0x%0x",getCName(),pReg->getOffset(true),ret,value);
}
}
//then read from dev
if(amode & WB_AM_R)
......@@ -340,6 +358,7 @@ WBReg::WBReg(WBNode *pPrtNode,const std::string &name, uint32_t offset, const st
this->offset=offset;
this->used_mask=0;
this->data=0;
this->toSync=false;
pPrtNode->appendReg(this);
......@@ -367,9 +386,10 @@ WBReg::~WBReg()
*
*
* \param[in] fld A pointer
* \param[in] toSyncInit Tell if this field need to be sync at initialization
* \return true if it was possible to add it, false otherwise.
*/
bool WBReg::addField(WBField *fld)
bool WBReg::addField(WBField *fld, bool toSyncInit)
{
const WBReg* fld_reg=fld->getReg();
if(fld==NULL || fld_reg==NULL || this!=fld_reg)
......@@ -390,6 +410,7 @@ bool WBReg::addField(WBField *fld)
//append field mask to used mask of the whole register
used_mask|=fld->getMask();
this->toSync|=toSyncInit;
return true;
}
......@@ -411,6 +432,8 @@ const WBField* WBReg::getField(const std::string& name) const
/**
* Synchronize the 32bits of the WBReg
*
* \note This method is also the only way to reset the toSync flag
*
* \param[in] con A pointer to a valid Memory Connector object.
* \param[in] amode Access mode (R, W, R/W)
* \return true if everything is okay, false otherwise
......@@ -430,6 +453,7 @@ bool WBReg::sync(WBMemCon* con, WBAccMode amode)
{
ret &= con->mem_access(this->getOffset(true),&data,false); //Read WB from dev
}
if(toSync) toSync=(ret==false); //Keep trying to sync if return was false
return ret;
}
......@@ -664,6 +688,78 @@ bool WBNode::sync(WBMemCon* con, WBAccMode amode, uint32_t dma_dev_offset)
}
return ret;
}
/**
* Sync WBNode using internal memory
*
* This method is similar as WBNode::sync(WBMemCon,WBAccMode,uint32_t) method
* except that here have direct access to the internal/kernel buffer that is going
* to be used with the WBMemCon.
*
* If we want to partially sync the memory with our WBNode this is the method
* to use. For example if we want to read only the the words (32bits) 0x10 to 0x16
* from the internal buffer to our WBNode we should perform the following:
*
* <code>
* uint32_t *pData32, r_bsize;
* //First setup the internal buffer from the device
* pCon->mem_block_access(pNode->getAddress(),pNode->getLastReg()->getOffset()/sizeof(uint32_t),false);
* r_bsize=pCon->get_block_buffer(&pData32,false);
* //Then read from the internal buffer to the WBNode only words [0x10-0x16]
* pNode->sync(pData32,0x6,WB_AM_R,0x10);
* </code>
*
* \param[in] pData32 Pointer on an internal buffer.
* \param[in] bsize Size of the buffer that we want to R/W in bytes
* \param[in] amode The operation mode (R,W,RW)
* \param[in] doffset Offset on the data buffer and its correspondence in registers of WBNode (bytes).
* \return true if everything ok, false otherwise.
*
*/
bool WBNode::sync(uint32_t* pData32, uint32_t bsize, WBAccMode amode, uint32_t doffset)
{
bool ret=true;
uint32_t prh_bsize;
TRACE_CHECK_PTR(pData32,false);
//Check if the latest register has the latest size.
prh_bsize=getLastReg()->getOffset()+4;
bsize=std::min(prh_bsize-doffset,bsize);
TRACE_CHECK_VA((doffset+4)<=prh_bsize,false,
"Size overflow: offset=%d+4 > prh_bsize=%d",
doffset,prh_bsize);
TRACE_P_DEBUG("%s 0x%08X + [0x%x,0x%X] (DataBuff)",getCName(),(uint32_t)pData32,doffset,doffset+bsize);
//first write to buffer
if(amode & WB_AM_W)
{
//Fill it with the data of all registers
for(std::map<uint32_t,WBReg*>::iterator ii=registers.begin(); ii!=registers.end(); ++ii)
{
if(doffset <= (*ii).first && (*ii).first <= doffset+bsize)
pData32[(*ii).first/sizeof(uint32_t)]=((*ii).second)->getData();
}
}
//then read from buffer
if(amode & WB_AM_R)
{
//Extract each value to the corresponding register
for(std::map<uint32_t,WBReg*>::iterator ii=registers.begin(); ii!=registers.end(); ++ii)
{
if(doffset <= (*ii).first && (*ii).first <= doffset+bsize)
{
((*ii).second)->data=pData32[(*ii).first/sizeof(uint32_t)];
TRACE_P_VDEBUG("%20s @0x%08X (%02d) <= 0x%x",((*ii).second)->getCName(),
((*ii).second)->getOffset(true),(*ii).first/sizeof(uint32_t),
((*ii).second)->getData());
}
}
}
return ret;
}
/**
......
......@@ -146,6 +146,9 @@ public:
float getFloat() const;
uint32_t getU32() const;
bool setToSync();
void setForceSync(bool val=true) { this->forceSync=val; };
bool sync(WBMemCon *con, WBAccMode amode=WB_AM_RW);
......@@ -169,6 +172,7 @@ protected:
uint8_t type; //!< Type of data
uint8_t nfb; //!< Number of fraction bits
std::string desc; //!< Description
bool forceSync; //!< Force sync even if we the same value
bool checkOverflow; //!< Limit overflow during FP conversion
private:
......@@ -193,7 +197,7 @@ public:
uint32_t getOffset(bool absolute=false) const;
bool sync(WBMemCon *con, WBAccMode amode=WB_AM_RW);
bool addField(WBField *fld);
bool addField(WBField *fld, bool toSyncInit=false);
const WBField* getField(const std::string& name) const;
const WBField* operator[](const std::string& name) const { return this->getField(name); }
......@@ -201,6 +205,8 @@ public:
const std::vector<WBField*> getFields() const { return fields; } //!< Get a vector on the belonging WBField
const WBNode* getPrtNode() const { return pPrtNode; } //!< Get the parent WBNode
void setToSync() { toSync=true; } //!< Set this register to be sync ASAP
bool isToSync() const { return toSync; } //!< Check if the register need to be sync ASAP
uint32_t getData() const { return data; } //!< Get the data
const std::string& getName() const { return this->name; } //!< Get the name
const char *getCName() const { return this->name.c_str(); } //!< Get the name in "C" format for printf function
......@@ -213,6 +219,7 @@ protected:
uint32_t offset; //!< The offset relative to WBNode
uint32_t data; //!< The corresponding data
uint32_t used_mask; //!< The mask used by other WBField
bool toSync; //!< Boolean that tell if this register need to be sync ASAP
private:
WBNode *pPrtNode;
......@@ -241,6 +248,7 @@ public:
bool sync(WBMemCon *con, WBAccMode amode=WB_AM_RW);
bool sync(WBMemCon *con, WBAccMode amode, uint32_t dma_dev_offset=WB_NODE_MEMBCK_OWNADDR);
bool sync(uint32_t* pData32, uint32_t length, WBAccMode amode, uint32_t doffset=0);
int getID() const { return this->ID; } //!< Get unique ID of WBNode
uint32_t getAddress() const { return this->address; } //!< Get Address of WBnode
......
......@@ -46,15 +46,20 @@ asynWBPortDrvr::asynWBPortDrvr(const char *portName,int max_nprm)
1, /* Autoconnect */
0, /* Default priority */
0), /* Default stack size*/
pMemCon(NULL), driverName(portName)
pRoot(NULL), pMemCon(NULL), driverName(portName)
{
this->pRoot=NULL;
//Reserve our space for param list
AsynWBField afld;
afld.pFld=NULL;
afld.syncmode=AWB_SYNC_DERIVED; //when not define we derive
fldPrms = std::vector<AsynWBField>(max_nprm,afld);
//Create the only generic parameters to block sync or not (0: NoneBlock, 1:BlockRead, 2:BlockWrite, 3: All block)
createParam("AWBPD_BlockSync", asynParamInt32,&P_BlkSyncIdx);
setIntegerParam(P_BlkSyncIdx,0);
syncNow=WB_AM_RW;
}
/**
......@@ -74,6 +79,56 @@ asynWBPortDrvr::~asynWBPortDrvr()
pRoot=NULL;
}
/**
* Synchronize parameters that have been setup internally but not sync to the peripheral
*
* This is use in two case:
* - At initialization, when a WBField has been set with a non default value (see constructor)
* we need to send this parameters to our device so that it takes this value at the beginning of
* execution.
* - When we want to block all the parameters so that
*
* \param[in] amode Access mode (R, W, R/W)
* \return Returns a asynSuccess if everything is okay.
*/
asynStatus asynWBPortDrvr::syncPending(WBAccMode amode)
{
WBField* fld;
WBReg *reg=NULL;
AsynStatusObj status = asynSuccess;
//First find which fldParams needs to be sync
std::vector<int> tmpList;
for(int i=0;i<fldPrms.size();i++)
{
fld=fldPrms[i].pFld;
if(fld && fld->getReg() && fld->getReg()->isToSync())
{
tmpList.push_back(i);
}
}
//Then obtain the registers of these field params
for(int i=0;i<tmpList.size();i++)
{
fld=fldPrms[tmpList[i]].pFld;
reg=(WBReg*)fld->getReg();
//Sync them
if(reg->isToSync())
{
TRACE_P_DEBUG("Syncing Reg>: %s (@0x%08X) 0x%08x",
reg->getCName(),reg->getOffset(true),reg->getData());
status&=reg->sync(pMemCon,amode);
//Finally update the
}
if(fld->getType() && WBField::WBF_TM_FIXED_POINT)
setDoubleParam(tmpList[i],fld->getFloat());
else
setIntegerParam(tmpList[i],fld->getU32());
}
return status;
}
/**
* Create a asyn parameter and link it to a WB field
*
......@@ -151,6 +206,19 @@ asynStatus asynWBPortDrvr::createParam(const char* name, WBField* fld,int *pInde
return status;
}
/**
* Create parameters for internal use of the IOC
*
* This parameters will not be synchronized with our device. This function override the standard
* asynPortDriver::createParam(), and only add the syncmode to AWB_SYNC_PRMLIST or AWB_SYNC_DERIVED
*
* \param[in] name The name of this parameter
* \param[in] type The type of parameters A WBField that is going to be link with the parameter
* \param[in] syncmode Select in which mode we want to sync. As we don't communicate with the device we can not set AWB_SYNC_DEVICE or AWB_SYNC_WBSTRUCT
* \return Returns a asynSuccess if everything is okay. Otherwise asynParamAlreadyExists if the parameter already exists, or asynBadParamIndex if
* adding this parameter would exceed the size of the parameter list and asynError is the syncmode is not valid.
* \see AsynWBSync for the type of synchronization
*/
asynStatus asynWBPortDrvr::createParam(const char* name, asynParamType type,int *pIndex,int syncmode)
{
int tmp;
......@@ -209,7 +277,7 @@ asynStatus asynWBPortDrvr::readInt32(asynUser* pasynUser, epicsInt32* value)
// Fetch the parameter string name for possible use in debugging
getParamName(function, &paramName);
TRACE_P_DEBUG("#%02d %s",function,paramName);
TRACE_P_VDEBUG("#%02d %s",function,paramName);
//Derived if need
if(aWF.syncmode==AWB_SYNC_DERIVED) return asynDisabled;
......@@ -222,7 +290,7 @@ asynStatus asynWBPortDrvr::readInt32(asynUser* pasynUser, epicsInt32* value)
TRACE_CHECK_PTR(aWF.pFld->getReg(),asynError);
//Sync from device memory
if(aWF.syncmode==AWB_SYNC_DEVICE)
if(aWF.syncmode==AWB_SYNC_DEVICE && (syncNow & WB_AM_R))
aWF.pFld->sync(pMemCon,WB_AM_R);
//Convert the WBField to a float
......@@ -282,7 +350,7 @@ asynStatus asynWBPortDrvr::readFloat64(asynUser* pasynUser,
// Fetch the parameter string name for possible use in debugging
getParamName(function, &paramName);
TRACE_P_DEBUG("#%02d %s",function,paramName);
TRACE_P_VDEBUG("#%02d %s",function,paramName);
//Derived if need
if(aWF.syncmode==AWB_SYNC_DERIVED) return asynDisabled;
......@@ -295,7 +363,7 @@ asynStatus asynWBPortDrvr::readFloat64(asynUser* pasynUser,
TRACE_CHECK_PTR(aWF.pFld->getReg(),asynError);
//Sync WBField using the connector from device memory
if(aWF.syncmode==AWB_SYNC_DEVICE)
if(aWF.syncmode==AWB_SYNC_DEVICE && (syncNow & WB_AM_R))
aWF.pFld->sync(pMemCon,WB_AM_R);
//Convert the WBField to a float
......@@ -352,7 +420,7 @@ asynStatus asynWBPortDrvr::writeInt32(asynUser* pasynUser, epicsInt32 value)
// Fetch the parameter string name for possible use in debugging
getParamName(function, &paramName);
TRACE_P_VDEBUG("#%02d %s=> %d (sync=%d)",function,paramName,value,aWF.syncmode);
TRACE_P_DEBUG("#%02d %s=> %d (sync=%d)",function,paramName,value,aWF.syncmode);
if(aWF.syncmode==AWB_SYNC_DERIVED) return asynDisabled;
......@@ -374,17 +442,32 @@ asynStatus asynWBPortDrvr::writeInt32(asynUser* pasynUser, epicsInt32 value)
aWF.pFld->getReg()->getData());
//Finally sync WBField using the connector to memory
ret=aWF.pFld->sync(pMemCon,WB_AM_W);
if(ret==false) return asynError;
if(syncNow & WB_AM_W)
{
ret=aWF.pFld->sync(pMemCon,WB_AM_W);
if(ret==false) return asynError;
}
else aWF.pFld->setToSync();
TRACE_P_VVDEBUG("===>>>>>>>>>>>>>>@0x%08X %s.%s : %08x",
aWF.pFld->getReg()->getOffset(true),
aWF.pFld->getReg()->getCName(),aWF.pFld->getCName(),
aWF.pFld->getReg()->getData());
}
else if(function==P_BlkSyncIdx)
{
//When setting back to zeros we need to sync pending registers
WBAccMode syncMode=(WBAccMode)((~value) & WB_AM_RW); //syncMode is the inverse of block value
if(value==0 && syncNow!=WB_AM_RW) status = this->syncPending(syncMode);
TRACE_P_INFO("Syncing Mode: Old=%s%s (%d) => New=%s%s (%d) ... block value=%d",
(syncNow&WB_AM_R)?"R":"", (syncNow& WB_AM_W)?"W":"", syncNow,
(syncMode&WB_AM_R)?"R":"", (syncMode& WB_AM_W)?"W":"",syncMode,value);
syncNow=syncMode;
}
// Set the parameter in the parameter library
status = (asynStatus) setIntegerParam(function, value);
if((syncNow & WB_AM_W) || aWF.syncmode!=AWB_SYNC_DEVICE)
status = (asynStatus) setIntegerParam(function, value);
//Do callbacks so higher layers see any changes
......@@ -440,12 +523,21 @@ asynStatus asynWBPortDrvr::writeFloat64(asynUser* pasynUser,
aWF.pFld->convert(&f32val,false);
//Finally sync WBField using the connector to memory
ret=aWF.pFld->sync(pMemCon,WB_AM_W);
if(ret==false) return asynError;
if(syncNow & WB_AM_W)
{
ret=aWF.pFld->sync(pMemCon,WB_AM_W);
if(ret==false) return asynError;
}
else aWF.pFld->setToSync();
//And readback from value
aWF.pFld->convert(&f32val,true);
value=f32val;
}
// Set the parameter in the parameter library
status = (asynStatus) setDoubleParam(function, value);
if((syncNow & WB_AM_W) || aWF.syncmode!=AWB_SYNC_DEVICE)
status = (asynStatus) setDoubleParam(function, value);
//Do callbacks so higher layers see any changes
status = (asynStatus) callParamCallbacks();
......@@ -490,7 +582,7 @@ asynStatus asynWBPortDrvr::writeOctet(asynUser *pasynUser, const char *value, si
//Write to the device
if(aWF.syncmode==AWB_SYNC_DEVICE)
{
TRACE_P_WARNING("Writing String to device is not implemented");
TRACE_P_WARNING("Writing String to device is not yet implemented");
return asynError;
}
......@@ -515,7 +607,7 @@ asynStatus asynWBPortDrvr::writeOctet(asynUser *pasynUser, const char *value, si
* \param[out] eomReaseon ???
*/
asynStatus asynWBPortDrvr::readOctet(asynUser *pasynUser, char *value, size_t maxChars,
size_t *nActual, int *eomReason)
size_t *nActual, int *eomReason)
{
int function = pasynUser->reason;
asynStatus status = asynSuccess;
......@@ -531,7 +623,7 @@ asynStatus asynWBPortDrvr::readOctet(asynUser *pasynUser, char *value, size_t ma
//Write to the device
if(aWF.syncmode==AWB_SYNC_DEVICE)
{
TRACE_P_WARNING("Writing String to device is not implemented");
TRACE_P_WARNING("Reading String to device is not yet implemented");
return asynError;
}
......
......@@ -81,6 +81,7 @@ public:
bool isValid() { return pRoot!=NULL; } //!< return true if the child class has been properly setup()
protected:
asynStatus syncPending(WBAccMode amode=WB_AM_RW);
asynStatus createParam(WBField *fld, int *index=NULL,int syncmode=AWB_SYNC_DEVICE);
asynStatus createParam(const char *name, WBField *fld, int *index=NULL, int syncmode=AWB_SYNC_DEVICE);
asynStatus createParam(const char *name, asynParamType type,int *index=NULL,int syncmode=AWB_SYNC_PRMLIST);
......@@ -90,9 +91,10 @@ protected:
WBNode *pRoot; //!< pointer on the WB tree structure.
WBMemCon* pMemCon; //!< generic pointer on the memory connector.
private:
std::vector<AsynWBField> fldPrms;
private:
std::string driverName;
int P_BlkSyncIdx, syncNow;
};
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment