def closeSockets(self): """Allows you to manually close any sockets that may have been opened.""" try: self._socket.close() chipsLog.debug("Socket closed successfully.") except: chipsLog.warn("Error closing socket!")
def queueWrite(self, name, dataU32, addrOffset = 0): """Create a register write (RMW-bits) transaction element and add it to the transaction queue. This works in the same way as a normal write(), except that many can be queued into a packet and dispatched all at once rather than individually. Run the queued transactions with queueRun(). Only single-register reads/writes can be queued. Block reads/writes, etc, cannot be queued. """ if len(self._queuedRequests) < ChipsBus.MAX_QUEUED_REQUESTS: dataU32 = dataU32 & 0xffffffff # Ignore oversize input. chipsLog.debug("Write queued: dataU32 = 0x" + uInt32HexStr(dataU32) + " to register '" + name + "' with addrOffset = 0x" + uInt32HexStr(addrOffset)) addrTableItem = self.addrTable.getItem(name) # Get the details of the relevant item from the addr table. if not addrTableItem.getWriteFlag(): raise ChipsException("Write transaction creation error: write is not allowed on register '" + addrTableItem.getName() + "'.") self._queuedRequests.append(self._createRMWBitsTransactionElement(addrTableItem, dataU32, addrOffset)) self._queuedAddrTableItems.append(addrTableItem) self._queuedIsARead.append(False) else: chipsLog.warning("Warning: transaction not added to queue as transaction queue has reached its maximum length!\n" + "\tPlease either run or clear the transaction queue before continuing.\n")
def _makeAndRunTransaction(self, requestsList): """Constructs, runs and then returns a completed transaction from the given requestsList requestsList: a list of TransactionElements (i.e. requests from client to the hardware). Notes: _makeAndRunTransaction will automatically prepend one byte-order transaction. """ # Construct the transaction and serialise it - we prepend four byte-order transactions in # order to ensure we meet minimum Ethernet payload requirements, else funny stuff happens. transaction = Transaction.constructClientTransaction(requestsList, self._hostAddr) transaction.serialiseRequests() chipsLog.debug("Sending packet now."); try: # Send the transaction self._socketSend(transaction) except socket.error as socketError: raise ChipsException("A socket error occurred whilst sending the IPbus transaction request packet:\n\t" + str(socketError)) try: # Get response transaction.serialResponses = self._socket.recv(ChipsBus.SOCKET_BUFFER_SIZE) except socket.error as socketError: raise ChipsException("A socket error occurred whilst getting the IPbus transaction response packet:\n\t" + str(socketError)) chipsLog.debug("Received response packet."); transaction.deserialiseResponses() transaction.doTransactionChecks() # Generic transaction checks self._transactionId = 1 # TEMPORARY IPBUS V2.x HACK! Reset the transaction ID to 1 for each packet. return transaction
def queueWrite(self, name, dataU32, addrOffset = 0): """Create a register write (RMW-bits) transaction element and add it to the transaction queue. This works in the same way as a normal write(), except that many can be queued into a packet and dispatched all at once rather than individually. Run the queued transactions with queueRun(). Only single-register reads/writes can be queued. Block reads/writes, etc, cannot be queued. """ if len(self._queuedRequests) < ChipsBus.MAX_QUEUED_REQUESTS: dataU32 = dataU32 & 0xffffffff # Ignore oversize input. chipsLog.debug("Write queued: dataU32 = 0x" + uInt32HexStr(dataU32) + " to register '" + name + "' with addrOffset = 0x" + uInt32HexStr(addrOffset)) addrTableItem = self.addrTable.getItem(name) # Get the details of the relevant item from the addr table. if not addrTableItem.getWriteFlag(): raise ChipsException("Write transaction creation error: write is not allowed on register '" + addrTableItem.getName() + "'.") # self._queuedRequests.append(self._createRMWBitsTransactionElement(addrTableItem, dataU32, addrOffset)) # self._queuedAddrTableItems.append(addrTableItem) # self._queuedIsARead.append(False) self._queuedRequests.append(self._createWriteTransactionElement(addrTableItem, [dataU32], addrOffset)) self._queuedAddrTableItems.append(addrTableItem) self._queuedIsARead.append(False) else: chipsLog.warning("Warning: transaction not added to queue as transaction queue has reached its maximum length!\n" + "\tPlease either run or clear the transaction queue before continuing.\n")
def _handleGetReservedAddrInfoRequest(self, request): chipsLog.debug("Reserved Address Info transaction requested") # Response header is the request header but with the Info Code field changed to INFO_CODE_RESPONSE responseHeader = IPbusHeader.updateInfoCode(request.getHeader(), IPbusHeader.INFO_CODE_RESPONSE) responseHeader = IPbusHeader.updateWords(responseHeader, 2) # Returning zeros for the response body, as no real idea what else it should be. return TransactionElement.makeFromHeaderAndBody(responseHeader, [0,0])
def _blockOrFifoWrite(self, name, dataList, addrOffset, isFifo = False): """Common code for either a block write or a FIFO write.""" depth = len(dataList) addrTableItem = self.addrTable.getItem(name) # Get the details of the relevant item from the addr table. if addrTableItem.getMask() != 0xffffffff: raise ChipsException("Block/FIFO write error: cannot perform block or FIFO write on a masked register address!") if depth == 0: chipsLog.warn("Ignoring block/FIFO write to register '" + name + "': dataList is empty!"); return elif depth > ChipsBus.MAX_BLOCK_TRANSFER_DEPTH: return self._oversizeBlockOrFifoWrite(name, dataList, addrOffset, isFifo) try: if not addrTableItem.getWriteFlag(): raise ChipsException("Write transaction creation error: write is not allowed on register '" + addrTableItem.getName() + "'.") # create and run the transaction and get the response self._makeAndRunTransaction( [self._createWriteTransactionElement(addrTableItem, dataList, addrOffset, isFifo)] ) except ChipsException as err: raise ChipsException("Block/FIFO write error on register '" + name + "':\n\t" + str(err)) chipsLog.debug("Block/FIFO write success! " + str(depth) + " 32-bit words were written to '" + name + "' (addrOffset=0x" + uInt32HexStr(addrOffset) + ")")
def queueWrite(self, name, dataU32, addrOffset = 0): """Create a register write (RMW-bits) transaction element and add it to the transaction queue. This works in the same way as a normal write(), except that many can be queued into a packet and dispatched all at once rather than individually. Run the queued transactions with queueRun(). Only single-register reads/writes can be queued. Block reads/writes, etc, cannot be queued. """ if len(self._transactionQueue) <= 50: dataU32 = dataU32 & 0xffffffff # Ignore oversize input. chipsLog.debug("Write queued: dataU32 = 0x" + uInt32HexStr(dataU32) + " to register '" + name + "' with addrOffset = 0x" + uInt32HexStr(addrOffset)) item = self.addrTable.getItem(name) # Get the details of the relevant item from the addr table. try: self._checkRWFlag(item, 0, 1) except ChipsException, err: raise ChipsException("Write error on register '" + name + "':\n\t" + str(err)) self._transactionQueue.append(self._createRMWBitsTransactionElement(item, dataU32, addrOffset)) self._transactionItemQueue.append(item) self._transactionRWFlagQueue.append(0)
def _oversizeBlockOrFifoWrite(self, name, dataList, addrOffset, isFifo): """Handling for a block write which is too big for the hardware to handle in one go""" chipsLog.debug( "Write depth too large for single packet... will automatically split write over many packets" ) depth = len(dataList) remainingTransactions = depth offsetMultiplier = 1 if isFifo: offsetMultiplier = 0 while remainingTransactions > ChipsBus.MAX_BLOCK_TRANSFER_DEPTH: self._blockOrFifoWrite( name, dataList[( depth - remainingTransactions):(depth - remainingTransactions) + ChipsBus.MAX_BLOCK_TRANSFER_DEPTH], addrOffset + ((depth - remainingTransactions) * offsetMultiplier), isFifo) remainingTransactions -= ChipsBus.MAX_BLOCK_TRANSFER_DEPTH self._blockOrFifoWrite( name, dataList[(depth - remainingTransactions):], addrOffset + ((depth - remainingTransactions) * offsetMultiplier), isFifo)
def _oversizeBlockOrFifoRead(self, name, depth, addrOffset, isFifo): """Handles a block or FIFO read that's too big to be handled by a single UDP packet""" chipsLog.debug( "Read depth too large for single packet... will automatically split read over many packets" ) remainingTransactions = depth result = [] offsetMultiplier = 1 if isFifo: offsetMultiplier = 0 while remainingTransactions > ChipsBus.MAX_BLOCK_TRANSFER_DEPTH: print "REMAINING=", remainingTransactions result.extend( self._blockOrFifoRead( name, ChipsBus.MAX_BLOCK_TRANSFER_DEPTH, addrOffset + ((depth - remainingTransactions) * offsetMultiplier), isFifo)) remainingTransactions -= ChipsBus.MAX_BLOCK_TRANSFER_DEPTH print "REMAINING: rest=", remainingTransactions result.extend( self._blockOrFifoRead( name, remainingTransactions, addrOffset + ((depth - remainingTransactions) * offsetMultiplier), isFifo)) return result
def serveForever(self): """The only function any user of this class needs to run! Receives, acts on, and responds to UDP control packets as the Mini-T (or similar hardware) would. Packets are received by the main thread and queued for action and response by a second thread. """ chipsLog.info("Dummy Hardware UDP Server starting") # This starts the packet "act and respond" handler thread self.start() while not self.stopServing: try: data, addr = self._socket.recvfrom(DummyHardware.SOCKET_BUFFER_SIZE) except KeyboardInterrupt: chipsLog.warning("\nKeyboard interrupt (ctrl-c) received whilst waiting for incoming UDP packet") self._stopServingAndJoinThreads() return except: chipsLog.warning("\nException caught whilst waiting for incoming UDP packet") self._stopServingAndJoinThreads() return if not data: chipsLog.warning("Socket received an empty packet from " + repr(addr) + \ ". Assuming socket now closed.\nTerminating dummy hardware server...") self._stopServingAndJoinThreads() return else: chipsLog.debug("\nReceived packet from " + repr(addr)) transaction = Transaction.constructHostTransaction(data, addr) self._transaction_queue.put(transaction)
def _handleReadRequest(self, request): requestHeader = request.getHeader() words = IPbusHeader.getWords(requestHeader) baseAddr = request.getBody()[0] chipsLog.debug("Read requested on Addr=0x" + uInt32HexStr(baseAddr)) # Response header is the request header but with the Info Code field changed to INFO_CODE_RESPONSE responseHeader = IPbusHeader.updateInfoCode( requestHeader, IPbusHeader.INFO_CODE_RESPONSE) # The (baseAddr & 0xffffffff) forces baseAddr to be in unsigned form (i.e. 0xfffffffc, say, rather than -4) if ( baseAddr & 0xffffffff ) == 0xffffffff: # A read on this register is a Dummy Hardware Reset Request. chipsLog.info( "** Dummy Hardware reset request received! Zeroing all registers. **" ) self._registers.clear() responseBody = [] appendToResponseBody = responseBody.append # This is for a speed optimisation for offset in range(words): currentReg = baseAddr + offset # Create these registers if they don't already exist. if currentReg not in self._registers: self._registers[currentReg] = 0 appendToResponseBody(self._registers[currentReg]) return TransactionElement.makeFromHeaderAndBody( responseHeader, responseBody)
def queueRead(self, name, addrOffset = 0): """Create a read transaction element and add it to the transaction queue. This works in the same way as a normal read(), except that many can be queued into a packet and dispatched all at once rather than individually. Run the queued transactions with queueRun(). Only single-register reads/writes can be queued. Block reads/writes, etc, cannot be queued. """ if len(self._transactionQueue) <= 50: chipsLog.debug("Read queued: register '" + name + "' with addrOffset = 0x" + uInt32HexStr(addrOffset)) item = self.addrTable.getItem(name) # Get the details of the relevant item from the addr table. try: self._checkRWFlag(item) except ChipsException, err: raise ChipsException("Read error on register '" + name + "':\n\t" + str(err)) self._transactionQueue.append(self._createReadTransactionElement(item, 1, addrOffset)) self._transactionItemQueue.append(item) self._transactionRWFlagQueue.append(1)
def _blockOrFifoRead(self, name, depth, addrOffset, isFifo = False): """Common code for either a block read or a FIFO read.""" if depth <= 0: chipsLog.warn("Ignoring read with depth = 0 from register '" + name + "'!") return if depth > ChipsBus.MAX_BLOCK_TRANSFER_DEPTH: return self._oversizeBlockOrFifoRead(name, depth, addrOffset, isFifo) addrTableItem = self.addrTable.getItem(name) # Get the details of the relevant item from the addr table. if addrTableItem.getMask() != 0xffffffff: raise ChipsException("Block/FIFO read error: cannot perform block or FIFO read on a masked register address!") try: if not addrTableItem.getReadFlag(): raise ChipsException("Read transaction creation error: read is not allowed on register '" + addrTableItem.getName() + "'.") # create and run the transaction and get the response transaction = self._makeAndRunTransaction( [self._createReadTransactionElement(addrTableItem, depth, addrOffset, isFifo)] ) except ChipsException as err: raise ChipsException("Block/FIFO read error on register '" + name + "':\n\t" + str(err)) blockReadResponse = transaction.responses[-1] # Block read response will be last in list chipsLog.debug("Block/FIFO read success! Register '" + name + "' (addrOffset=0x" + uInt32HexStr(addrOffset) + ") was read successfully." ) return blockReadResponse.getBody().tolist()
def queueClear(self): """Clears the current queue of transactions""" chipsLog.debug("Clearing transaction queue") self._transactionQueue = [] self._transactionItemQueue = [] self._transactionRWFlagQueue =[]
def _handleGetReservedAddrInfoRequest(self, request): chipsLog.debug("Reserved Address Info transaction requested") # Response header is the request header but with the Info Code field changed to INFO_CODE_RESPONSE responseHeader = IPbusHeader.updateInfoCode( request.getHeader(), IPbusHeader.INFO_CODE_RESPONSE) responseHeader = IPbusHeader.updateWords(responseHeader, 2) # Returning zeros for the response body, as no real idea what else it should be. return TransactionElement.makeFromHeaderAndBody(responseHeader, [0, 0])
def queueClear(self): """Clears the current queue of transactions""" chipsLog.debug("Clearing transaction queue") self._queuedRequests = [] self._queuedAddrTableItems = [] self._queuedIsARead =[]
def closeSockets(self): """Allows you to manually close any sockets that may have been opened.""" if self._connectedSocket != None: try: self._connectedSocket.close() chipsLog.debug("Connected TCP socket closed successfully.") except: chipsLog.debug("Error closing connected TCP socket!") DummyHardwareBase.closeSockets(self)
def _handleWriteRequest(self, request): requestHeader = request.getHeader() words = IPbusHeader.getWords(requestHeader) requestBody = request.getBody() baseAddr = requestBody[0] chipsLog.debug("Write requested on Addr=0x" + uInt32HexStr(baseAddr)) # Response header is the request header with direction bit changed responseHeader = IPbusHeader.updateDirection(requestHeader, 1) for offset in range(words): currentReg = baseAddr + offset self._registers[currentReg] = requestBody[offset + 1] return TransactionElement.makeFromHeaderAndBody(responseHeader)
def serialise(self, transactionElementList): '''Serialises a list of transaction elements into an ASCII string for transmission''' if chipsLog.level <= logging.DEBUG: msg = "\nSerialising the following packet content:\n" msg += reprTransactionElementList(transactionElementList) chipsLog.debug(msg) allTransactionsArray = array('I', [0x200000f0]) # TEMPORARY IPBUS V2.x HACK! Add a packet header of [0x200000f0] to the beginning of each packet. extendFunc = allTransactionsArray.extend for element in transactionElementList: extendFunc(element.getAll()) if self._doByteReorder: allTransactionsArray.byteswap() return allTransactionsArray.tostring()
def _handleWriteRequest(self, request): requestHeader = request.getHeader() words = IPbusHeader.getWords(requestHeader) requestBody = request.getBody() baseAddr = requestBody[0] chipsLog.debug("Write requested on Addr=0x" + uInt32HexStr(baseAddr)) # Response header is the request header but with the Info Code field changed to INFO_CODE_RESPONSE responseHeader = IPbusHeader.updateInfoCode(requestHeader, IPbusHeader.INFO_CODE_RESPONSE) for offset in range(words): currentReg = baseAddr + offset self._registers[currentReg] = requestBody[offset + 1] return TransactionElement.makeFromHeaderAndBody(responseHeader)
def serialise(self, transactionElementList): '''Serialises a list of transaction elements into an ASCII string for transmission''' if chipsLog.level <= logging.DEBUG: msg = "\nSerialising the following packet content:\n" msg += reprTransactionElementList(transactionElementList) chipsLog.debug(msg) allTransactionsArray = array('I') extendFunc = allTransactionsArray.extend for element in transactionElementList: extendFunc(element.getAll()) if self._doByteReorder: allTransactionsArray.byteswap() return allTransactionsArray.tostring()
def _handleFifoWriteRequest(self, request): requestHeader = request.getHeader() requestBody = request.getBody() fifoAddr = requestBody[0] chipsLog.debug("FIFO write requested on Addr=0x" + uInt32HexStr(fifoAddr)) # Response header is the request header with direction bit changed responseHeader = IPbusHeader.updateDirection(requestHeader, 1) # Obviously we don't really have a FIFO, we just have a single register at the address of the supposed # FIFO. So, whatever the last value written into the FIFO is, this will be the value this register will # take. We ignore all the previous "writes" to the FIFO. self._registers[fifoAddr] = requestBody[-1] return TransactionElement.makeFromHeaderAndBody(responseHeader)
def _handleWriteRequest(self, request): requestHeader = request.getHeader() words = IPbusHeader.getWords(requestHeader) requestBody = request.getBody() baseAddr = requestBody[0] chipsLog.debug("Write requested on Addr=0x" + uInt32HexStr(baseAddr)) # Response header is the request header but with the Info Code field changed to INFO_CODE_RESPONSE responseHeader = IPbusHeader.updateInfoCode( requestHeader, IPbusHeader.INFO_CODE_RESPONSE) for offset in range(words): currentReg = baseAddr + offset self._registers[currentReg] = requestBody[offset + 1] return TransactionElement.makeFromHeaderAndBody(responseHeader)
def _handleReadModifyWriteSumRequest(self, request): requestBody = request.getBody() addr = requestBody[0] addend = requestBody[1] # The value we add to the existing value chipsLog.debug("Read/Modify/Write-sum requested on Addr=0x" + uInt32HexStr(addr)) # Create the register if it doesn't already exist. if addr not in self._registers: self._registers[addr] = 0 updatedValue = (self._registers[addr] + addend) & 0xffffffff self._registers[addr] = updatedValue # Response header is the request header with direction bit changed responseHeader = IPbusHeader.updateDirection(request.getHeader(), 1) return TransactionElement.makeFromHeaderAndBody(responseHeader, [updatedValue])
def _oversizeBlockOrFifoWrite(self, name, dataList, addrOffset, isFifo): """Handling for a block write which is too big for the hardware to handle in one go""" chipsLog.debug("Write depth too large for single packet... will automatically split write over many packets") depth = len(dataList) remainingTransactions = depth offsetMultiplier = 1 if isFifo: offsetMultiplier = 0 while remainingTransactions > ChipsBus.MAX_BLOCK_TRANSFER_DEPTH: self._blockOrFifoWrite(name, dataList[(depth - remainingTransactions):(depth - remainingTransactions) + ChipsBus.MAX_BLOCK_TRANSFER_DEPTH], addrOffset + ((depth - remainingTransactions) * offsetMultiplier), isFifo) remainingTransactions -= ChipsBus.MAX_BLOCK_TRANSFER_DEPTH self._blockOrFifoWrite(name, dataList[(depth - remainingTransactions):], addrOffset + ((depth - remainingTransactions) * offsetMultiplier), isFifo)
def _handleReadModifyWriteBitsRequest(self, request): requestBody = request.getBody() addr = requestBody[0] andTerm = requestBody[1] # The and term is the bitwise complement of the register mask (i.e. mask = ~andTerm) orTerm = requestBody[2] chipsLog.debug("Read/Modify/Write-bits requested on Addr=0x" + uInt32HexStr(addr)) # Create the register if it doesn't already exist. if addr not in self._registers: self._registers[addr] = 0 updatedValue = (self._registers[addr] & andTerm) | (orTerm) self._registers[addr] = updatedValue # Response header is the request header with direction bit changed responseHeader = IPbusHeader.updateDirection(request.getHeader(), 1) return TransactionElement.makeFromHeaderAndBody(responseHeader, [updatedValue])
def _handleFifoWriteRequest(self, request): requestHeader = request.getHeader() requestBody = request.getBody() fifoAddr = requestBody[0] chipsLog.debug("FIFO write requested on Addr=0x" + uInt32HexStr(fifoAddr)) # Response header is the request header but with the Info Code field changed to INFO_CODE_RESPONSE responseHeader = IPbusHeader.updateInfoCode( requestHeader, IPbusHeader.INFO_CODE_RESPONSE) # Obviously we don't really have a FIFO, we just have a single register at the address of the supposed # FIFO. So, whatever the last value written into the FIFO is, this will be the value this register will # take. We ignore all the previous "writes" to the FIFO. self._registers[fifoAddr] = requestBody[-1] return TransactionElement.makeFromHeaderAndBody(responseHeader)
def blockRead(self, name, depth=1, addrOffset=0): """Block read (not for masked registers!). Returns a list of the read results (32-bit numbers). The blockRead() transaction runs straight away - it cannot be queued. name: the register name of the register you want to read from. depth: the number of 32-bit reads deep you want to go from the start address. (i.e. depth=3 will return a list with three 32-bit values). addrOffset: optional - provide a 32-bit word offset if you wish. Notes: Use the depth and addrOffset at your own risk! No checking is done to see if these values are remotely sensible! """ chipsLog.debug("Block read requested: register '" + name + "' with addrOffset = 0x" + uInt32HexStr(addrOffset) + " and depth = " + str(depth)) return self._blockOrFifoRead(name, depth, addrOffset, False)
def _handleFifoReadRequest(self, request): requestHeader = request.getHeader() words = IPbusHeader.getWords(requestHeader) fifoAddr = request.getBody()[0] chipsLog.debug("FIFO read requested on Addr=0x" + uInt32HexStr(fifoAddr)) # Response header is the request header with direction bit changed responseHeader = IPbusHeader.updateDirection(requestHeader, 1) # Create the register if they don't already exist in our memory space if fifoAddr not in self._registers: self._registers[fifoAddr] = 0 # Obviously we don't really have a FIFO, so we'll just have to return the value stored # at the FIFO's address many times... value = self._registers[fifoAddr] responseBody = [value for iReads in range(words)] return TransactionElement.makeFromHeaderAndBody(responseHeader, responseBody)
def blockWrite(self, name, dataList, addrOffset=0): """Block write (not for masked registers!). The blockWrite() transaction runs straight away - it cannot be queued. name: the register name of the register you want to read from. dataList: the list of 32-bit values you want writing. The size of the list determines how deep the block write goes. addrOffset: optional - provide a 32-bit word offset if you wish. Notes: Use this at your own risk! No checking is currently done to see if you will be stomping on any other registers if the dataList or addrOffset is inappropriate in size! """ chipsLog.debug("Block write requested: register '" + name + "' with addrOffset = 0x" + uInt32HexStr(addrOffset) + " and depth = " + str(len(dataList))) return self._blockOrFifoWrite(name, dataList, addrOffset, False)
def _oversizeBlockOrFifoRead(self, name, depth, addrOffset, isFifo): """Handles a block or FIFO read that's too big to be handled by a single UDP packet""" chipsLog.debug("Read depth too large for single packet... will automatically split read over many packets") remainingTransactions = depth result =[] offsetMultiplier = 1 if isFifo: offsetMultiplier = 0 while remainingTransactions > ChipsBus.MAX_BLOCK_TRANSFER_DEPTH: result.extend(self._blockOrFifoRead(name, ChipsBus.MAX_BLOCK_TRANSFER_DEPTH, addrOffset + ((depth - remainingTransactions) * offsetMultiplier), isFifo)) remainingTransactions -= ChipsBus.MAX_BLOCK_TRANSFER_DEPTH result.extend(self._blockOrFifoRead(name, remainingTransactions, addrOffset + ((depth - remainingTransactions) * offsetMultiplier), isFifo)) return result
def _handleReadModifyWriteSumRequest(self, request): requestBody = request.getBody() addr = requestBody[0] addend = requestBody[1] # The value we add to the existing value chipsLog.debug("Read/Modify/Write-sum requested on Addr=0x" + uInt32HexStr(addr)) # Create the register if it doesn't already exist. if addr not in self._registers: self._registers[addr] = 0 updatedValue = (self._registers[addr] + addend) & 0xffffffff self._registers[addr] = updatedValue # Response header is the request header but with the Info Code field changed to INFO_CODE_RESPONSE responseHeader = IPbusHeader.updateInfoCode( request.getHeader(), IPbusHeader.INFO_CODE_RESPONSE) return TransactionElement.makeFromHeaderAndBody( responseHeader, [updatedValue])
def _makeAndRunTransaction(self, requestsList): """Constructs, runs and then returns a completed transaction from the given requestsList requestsList: a list of TransactionElements (i.e. requests from client to the hardware). Notes: _makeAndRunTransaction will automatically prepend one byte-order transaction. """ # Construct the transaction and serialise it - we prepend four byte-order transactions in # order to ensure we meet minimum Ethernet payload requirements, else funny stuff happens. transaction = Transaction.constructClientTransaction(requestsList, self._hostAddr) transaction.serialiseRequests() chipsLog.debug("Sending packet now."); try: # Send the transaction self._socketSend(transaction) except socket.error, socketError: raise ChipsException("A socket error occurred whilst sending the IPbus transaction request packet:\n\t" + str(socketError))
def queueRun(self): """Runs the current queue of single register read or write transactions and returns two lists. The first contains the values read and the second contains the values written. Note: Only single-register reads/writes can be queued. Block reads/writes, etc, cannot be queued. """ chipsLog.debug("Running all queued transactions") requestQueueLength = len(self._queuedRequests) readResponse = [] writeResponse = [] try: transaction = self._makeAndRunTransaction(self._queuedRequests) except ChipsException, err: self.queueClear() raise ChipsException("Error while running queued transactions:\n\t" + str(err))
def fifoRead(self, name, depth=1, addrOffset=0): """Non-incrementing block read (not for masked registers!). Returns list of the read results. Reads from the same address the number of times specified by depth The fifoRead() transaction runs straight away - it cannot be queued. name: the register name of the register you want to read from. depth: the number of 32-bit reads you want to perform on the FIFO (i.e. depth=3 will return a list with three 32-bit values). addrOffset: optional - provide a 32-bit word offset if you wish. Notes: Use the depth and addrOffset at your own risk! No checking is done to see if these values are remotely sensible! """ chipsLog.debug("FIFO read (non-incrementing block read) requested: register '" + name + "' with addrOffset = 0x" + uInt32HexStr(addrOffset) + " and depth = " + str(depth)) return self._blockOrFifoRead(name, depth, addrOffset, True)
def queueRun(self): """Runs the current queue of single register read or write transactions and returns two lists. The first contains the values read and the second contains the values written. Note: Only single-register reads/writes can be queued. Block reads/writes, etc, cannot be queued. """ chipsLog.debug("Running all queued transactions") requestQueueLength = len(self._queuedRequests) readResponse = [] writeResponse = [] try: transaction = self._makeAndRunTransaction(self._queuedRequests) except ChipsException, err: self.queueClear() raise ChipsException( "Error while running queued transactions:\n\t" + str(err))
def _handleReadModifyWriteBitsRequest(self, request): requestBody = request.getBody() addr = requestBody[0] andTerm = requestBody[ 1] # The and term is the bitwise complement of the register mask (i.e. mask = ~andTerm) orTerm = requestBody[2] chipsLog.debug("Read/Modify/Write-bits requested on Addr=0x" + uInt32HexStr(addr)) # Create the register if it doesn't already exist. if addr not in self._registers: self._registers[addr] = 0 updatedValue = (self._registers[addr] & andTerm) | (orTerm) self._registers[addr] = updatedValue # Response header is the request header but with the Info Code field changed to INFO_CODE_RESPONSE responseHeader = IPbusHeader.updateInfoCode( request.getHeader(), IPbusHeader.INFO_CODE_RESPONSE) return TransactionElement.makeFromHeaderAndBody( responseHeader, [updatedValue])
def _handleFifoReadRequest(self, request): requestHeader = request.getHeader() words = IPbusHeader.getWords(requestHeader) fifoAddr = request.getBody()[0] chipsLog.debug("FIFO read requested on Addr=0x" + uInt32HexStr(fifoAddr)) # Response header is the request header but with the Info Code field changed to INFO_CODE_RESPONSE responseHeader = IPbusHeader.updateInfoCode( requestHeader, IPbusHeader.INFO_CODE_RESPONSE) # Create the register if they don't already exist in our memory space if fifoAddr not in self._registers: self._registers[fifoAddr] = 0 # Obviously we don't really have a FIFO, so we'll just have to return the value stored # at the FIFO's address many times... value = self._registers[fifoAddr] responseBody = [value for iReads in range(words)] return TransactionElement.makeFromHeaderAndBody( responseHeader, responseBody)
def _makeAndRunTransaction(self, requestsList): """Constructs, runs and then returns a completed transaction from the given requestsList requestsList: a list of TransactionElements (i.e. requests from client to the hardware). Notes: _makeAndRunTransaction will automatically prepend one byte-order transaction. """ # Construct the transaction and serialise it - we prepend four byte-order transactions in # order to ensure we meet minimum Ethernet payload requirements, else funny stuff happens. transaction = Transaction.constructClientTransaction( requestsList, self._hostAddr) transaction.serialiseRequests() chipsLog.debug("Sending packet now.") try: # Send the transaction self._socketSend(transaction) except socket.error, socketError: raise ChipsException( "A socket error occurred whilst sending the IPbus transaction request packet:\n\t" + str(socketError))
def deserialise(self, packetPayloadString): '''Deserialises a packet payload ASCII string into list of transaction elements''' # 1) Unpack the string into unsigned integers try: rawU32Array = array('I', packetPayloadString) # Unpack string to an unsigned 32-bit array except Exception as err: raise ChipsException("Deserialisation error:\n\t" + str(err)) # 2) Debug output of the raw packet if chipsLog.level <= logging.DEBUG: msg = "\nRaw received packet content is:\n" for u32 in rawU32Array: msg += " 0x" + uInt32HexStr(u32) + "\n" chipsLog.debug(msg) # 3) Detect if we need to do a byte-reorder firstWord = rawU32Array[0] firstWordMasked = firstWord & 0xf00000f0 # This selects only the bits relevant for detecting the byte ordering if firstWordMasked == 0x200000f0: chipsLog.debug("Packet header detected: no byte-reorder is required.") elif firstWordMasked == 0xf0000020: chipsLog.debug("Packet header detected: a byte-reorder will be performed.") self._doByteReorder = True else: chipsLog.warn("Warning: No packet header (or unknown protocol version)! First word = 0x" + uInt32HexStr(firstWord) + ". Will hope for the best!...") # 4) Do the byte-reorder if necessary if self._doByteReorder: rawU32Array.byteswap() # 5) Now deserialise into a list of TransactionElements transactionElementList = [] # The list all the deserialised transaction elements will go into appendToTransactionElementList = transactionElementList.append # This is needed for a speed optimisation iU32 = 1 # Index for moving through the rawU32Array # TEMPORARY IPBUS V2.x HACK! Skip the first word which is now a packet ID rawU32ArraySize = len(rawU32Array) while iU32 < rawU32ArraySize: expectedBodySize = getExpectedBodySize(rawU32Array[iU32]) if rawU32ArraySize - iU32 - 1 - expectedBodySize < 0: raise ChipsException("Deserialisation error: packet not correctly formatted " \ "or does not contain the expected amount of data!") appendToTransactionElementList(TransactionElement(rawU32Array[iU32: iU32 + 1 + expectedBodySize])) iU32 += (1 + expectedBodySize) # 6) Debug output of deserialised packet content if chipsLog.level <= logging.DEBUG: msg = "\nDeserialised packet content is:\n" \ + reprTransactionElementList(transactionElementList) chipsLog.debug(msg) return transactionElementList
def queueRun(self): """Runs the current queue of single register read or write transactions and returns two lists. The first contains the values read and the second contains the values written. Note: Only single-register reads/writes can be queued. Block reads/writes, etc, cannot be queued. """ chipsLog.debug("Running all queued transactions") # A check is performed to ensure the lists containing the transaction # queue information are of equal length if len(self._transactionQueue) != len(self._transactionItemQueue) or len(self._transactionQueue) != len(self._transactionRWFlagQueue): raise ChipsException("Error while running queued transactions: elements missing from transaction list!") transactionQueueLength = len(self._transactionQueue) readResponse = [] writeResponse = [] try: transaction = self._makeAndRunTransaction(self._transactionQueue) except ChipsException, err: self.queueClear() raise ChipsException("Error while running queued transactions:\n\t" + str(err))
def _handleReadRequest(self, request): requestHeader = request.getHeader() words = IPbusHeader.getWords(requestHeader) baseAddr = request.getBody()[0] chipsLog.debug("Read requested on Addr=0x" + uInt32HexStr(baseAddr)) # Response header is the request header with direction bit changed responseHeader = IPbusHeader.updateDirection(requestHeader, 1) # The (baseAddr & 0xffffffff) forces baseAddr to be in unsigned form (i.e. 0xfffffffc, say, rather than -4) if (baseAddr & 0xffffffff) == 0xffffffff: # A read on this register is a Dummy Hardware Reset Request. chipsLog.info("** Dummy Hardware reset request received! Zeroing all registers. **") self._registers.clear() responseBody = [] appendToResponseBody = responseBody.append # This is for a speed optimisation for offset in range(words): currentReg = baseAddr + offset # Create these registers if they don't already exist. if currentReg not in self._registers: self._registers[currentReg] = 0 appendToResponseBody(self._registers[currentReg]) return TransactionElement.makeFromHeaderAndBody(responseHeader, responseBody)
def serveForever(self): """The only function any user of this class needs to run! Receives, acts on, and responds to UDP control packets as the Mini-T (or similar hardware) would. Packets are received by the main thread and queued for action and response by a second thread. """ chipsLog.info("Dummy Hardware UDP Server starting") # This starts the packet "act and respond" handler thread self.start() while not self.stopServing: try: data, addr = self._socket.recvfrom( DummyHardware.SOCKET_BUFFER_SIZE) except KeyboardInterrupt: chipsLog.warning( "\nKeyboard interrupt (ctrl-c) received whilst waiting for incoming UDP packet" ) self._stopServingAndJoinThreads() return except: chipsLog.warning( "\nException caught whilst waiting for incoming UDP packet" ) self._stopServingAndJoinThreads() return if not data: chipsLog.warning("Socket received an empty packet from " + repr(addr) + \ ". Assuming socket now closed.\nTerminating dummy hardware server...") self._stopServingAndJoinThreads() return else: chipsLog.debug("\nReceived packet from " + repr(addr)) transaction = Transaction.constructHostTransaction(data, addr) self._transaction_queue.put(transaction)
def _actAndRespond(self, transaction): """Performs the required action and returns the response for a given transaction""" self._transactionCounter += 1 chipsLog.debug("*** Performing transaction #" + str(self._transactionCounter) + " ***") try: transaction.deserialiseRequests() for request in transaction.requests: transaction.appendResponse(self._requestTypeHandlerMap[IPbusHeader.getTypeId(request.getHeader())](request)) transaction.serialiseResponses() chipsLog.debug("Sending response packet") self._socketSend(transaction) chipsLog.debug("Response packet sent!") chipsLog.debug("*** Transaction #" + str(self._transactionCounter) + " completed! ***\n") except ChipsException, err: chipsLog.error("ERROR! Transaction #" + str(self._transactionCounter) + " could not be successfully processed:\n\t" + str(err))
def _actAndRespond(self, transaction): """Performs the required action and returns the response for a given transaction""" self._transactionCounter += 1 chipsLog.debug("*** Performing transaction #" + str(self._transactionCounter) + " ***") try: transaction.deserialiseRequests() for request in transaction.requests: transaction.appendResponse( self._requestTypeHandlerMap[IPbusHeader.getTypeId( request.getHeader())](request)) transaction.serialiseResponses() chipsLog.debug("Sending response packet") self._socketSend(transaction) chipsLog.debug("Response packet sent!") chipsLog.debug("*** Transaction #" + str(self._transactionCounter) + " completed! ***\n") except ChipsException, err: chipsLog.error("ERROR! Transaction #" + str(self._transactionCounter) + " could not be successfully processed:\n\t" + str(err))
def queueRun(self): """Runs the current queue of single register read or write transactions and returns two lists. The first contains the values read and the second contains the values written. Note: Only single-register reads/writes can be queued. Block reads/writes, etc, cannot be queued. """ chipsLog.debug("Running all queued transactions") requestQueueLength = len(self._queuedRequests) readResponse = [] writeResponse = [] try: transaction = self._makeAndRunTransaction(self._queuedRequests) except ChipsException as err: self.queueClear() raise ChipsException("Error while running queued transactions:\n\t" + str(err)) for i in range(requestQueueLength): addrTableItem = self._queuedAddrTableItems[i] if len(transaction.responses[0].getBody()) > 0: transactionResponse = transaction.responses[i - requestQueueLength].getBody()[0] & 0xffffffff transactionResponse = addrTableItem.shiftDataFromMask(transactionResponse) else: transactionResponse = 0 if self._queuedIsARead[i]: readResponse.append(transactionResponse) chipsLog.debug("Read success! Register '" + addrTableItem.getName() + "' returned: 0x" + uInt32HexStr(transactionResponse)) else: writeResponse.append(transactionResponse) chipsLog.debug("Write success! Register '" + addrTableItem.getName() + "' assigned: 0x" + uInt32HexStr(transactionResponse)) self.queueClear() response = [readResponse, writeResponse] return response
transaction = Transaction.constructClientTransaction(requestsList, self._hostAddr) transaction.serialiseRequests() chipsLog.debug("Sending packet now."); try: # Send the transaction self._socketSend(transaction) except socket.error, socketError: raise ChipsException("A socket error occurred whilst sending the IPbus transaction request packet:\n\t" + str(socketError)) try: # Get response transaction.serialResponses = self._socket.recv(ChipsBus.SOCKET_BUFFER_SIZE) except socket.error, socketError: raise ChipsException("A socket error occurred whilst getting the IPbus transaction response packet:\n\t" + str(socketError)) chipsLog.debug("Received response packet."); transaction.deserialiseResponses() transaction.doTransactionChecks() # Generic transaction checks self._transactionId = 1 # TEMPORARY IPBUS V2.x HACK! Reset the transaction ID to 1 for each packet. return transaction def _initSocketCommon(self, localPort): """Performs common socket initialisation (i.e. common to UDP + TCP)""" if localPort != None: localAddr = ("", localPort) self._socket.bind(localAddr) self._socket.settimeout(1)
# Send the transaction self._socketSend(transaction) except socket.error, socketError: raise ChipsException( "A socket error occurred whilst sending the IPbus transaction request packet:\n\t" + str(socketError)) try: # Get response transaction.serialResponses = self._socket.recv( ChipsBus.SOCKET_BUFFER_SIZE) except socket.error, socketError: raise ChipsException( "A socket error occurred whilst getting the IPbus transaction response packet:\n\t" + str(socketError)) chipsLog.debug("Received response packet.") transaction.deserialiseResponses() transaction.doTransactionChecks() # Generic transaction checks self._transactionId = 1 # TEMPORARY IPBUS V2.x HACK! Reset the transaction ID to 1 for each packet. return transaction def _initSocketCommon(self, localPort): """Performs common socket initialisation (i.e. common to UDP + TCP)""" if localPort != None: localAddr = ("", localPort) self._socket.bind(localAddr) self._socket.settimeout(1)
class SerDes(object): '''Class for serialising/deserialising IPbus transaction data Can serialise a list of TransactionElements into a string, or deserialise a string into a list of TransactionElements. The deserialisation stage checks the byte-ordering by looking for a byte-order header, and deals with the byte-reordering as appropriate. If a byte-reorder was required on the deserialisation stage, then the serialisation stage will also perform a reorder in order to respond correctly. Byte-reordering is off by default, as it's obviously a waste of CPU. ''' def __init__(self): '''Constructor - no arguments''' object.__init__(self) self._doByteReorder = False def serialise(self, transactionElementList): '''Serialises a list of transaction elements into an ASCII string for transmission''' if chipsLog.level <= logging.DEBUG: msg = "\nSerialising the following packet content:\n" msg += reprTransactionElementList(transactionElementList) chipsLog.debug(msg) allTransactionsArray = array( 'I', [0x200000f0] ) # TEMPORARY IPBUS V2.x HACK! Add a packet header of [0x200000f0] to the beginning of each packet. extendFunc = allTransactionsArray.extend for element in transactionElementList: extendFunc(element.getAll()) if self._doByteReorder: allTransactionsArray.byteswap() return allTransactionsArray.tostring() def deserialise(self, packetPayloadString): '''Deserialises a packet payload ASCII string into list of transaction elements''' # 1) Unpack the string into unsigned integers try: rawU32Array = array('I', packetPayloadString ) # Unpack string to an unsigned 32-bit array except Exception, err: raise ChipsException("Deserialisation error:\n\t" + str(err)) # 2) Debug output of the raw packet if chipsLog.level <= logging.DEBUG: msg = "\nRaw received packet content is:\n" for u32 in rawU32Array: msg += " 0x" + uInt32HexStr(u32) + "\n" chipsLog.debug(msg) # 3) Detect if we need to do a byte-reorder firstWord = rawU32Array[0] firstWordMasked = firstWord & 0xf00000f0 # This selects only the bits relevant for detecting the byte ordering if firstWordMasked == 0x200000f0: chipsLog.debug( "Packet header detected: no byte-reorder is required.") elif firstWordMasked == 0xf0000020: chipsLog.debug( "Packet header detected: a byte-reorder will be performed.") self._doByteReorder = True else: chipsLog.warn( "Warning: No packet header (or unknown protocol version)! First word = 0x" + uInt32HexStr(firstWord) + ". Will hope for the best!...") # 4) Do the byte-reorder if necessary if self._doByteReorder: rawU32Array.byteswap() # 5) Now deserialise into a list of TransactionElements transactionElementList = [ ] # The list all the deserialised transaction elements will go into appendToTransactionElementList = transactionElementList.append # This is needed for a speed optimisation iU32 = 1 # Index for moving through the rawU32Array # TEMPORARY IPBUS V2.x HACK! Skip the first word which is now a packet ID rawU32ArraySize = len(rawU32Array) while iU32 < rawU32ArraySize: expectedBodySize = getExpectedBodySize(rawU32Array[iU32]) if rawU32ArraySize - iU32 - 1 - expectedBodySize < 0: raise ChipsException("Deserialisation error: packet not correctly formatted " \ "or does not contain the expected amount of data!") appendToTransactionElementList( TransactionElement(rawU32Array[iU32:iU32 + 1 + expectedBodySize])) iU32 += (1 + expectedBodySize) # 6) Debug output of deserialised packet content if chipsLog.level <= logging.DEBUG: msg = "\nDeserialised packet content is:\n" \ + reprTransactionElementList(transactionElementList) chipsLog.debug(msg) return transactionElementList
def serveForever(self): """The only function any user of this class needs to run! Receives, acts on, and responds to TCP control packets as the Mini-T (or similar hardware) would. Packets are received by the main thread and queued for action and response by a second thread. """ chipsLog.info("Dummy Hardware TCP Server starting") # This starts the packet "act and respond" handler thread self.start() try: chipsLog.debug("Awaiting connection...") self._connectedSocket, addr = self._socket.accept() # TCP chipsLog.debug("Client connection accepted.") except KeyboardInterrupt: chipsLog.warning("\nKeyboard interrupt (ctrl-c) received whilst waiting for a TCP connection") self._stopServingAndJoinThreads() return while not self.stopServing: try: data = self._connectedSocket.recv(DummyHardware.SOCKET_BUFFER_SIZE) # TCP except KeyboardInterrupt: chipsLog.warning("\nKeyboard interrupt (ctrl-c) received whilst waiting for incoming TCP packet") self._stopServingAndJoinThreads() return except: chipsLog.warning("\nException caught whilst waiting for incoming TCP packet") self._stopServingAndJoinThreads() return if not data: chipsLog.debug("TCP socket received an empty packet from " + repr(addr) + ": assuming connection closed.") try: chipsLog.debug("Awaiting new connection...") self._connectedSocket, addr = self._socket.accept() # TCP chipsLog.debug("Client connection accepted.") continue except KeyboardInterrupt: chipsLog.warning("\nKeyboard interrupt (ctrl-c) received whilst waiting for a TCP connection") self._stopServingAndJoinThreads() return chipsLog.debug("\nReceived TCP packet from " + repr(addr)) transaction = Transaction.constructHostTransaction(data, addr) self._transaction_queue.put(transaction) chipsLog.info("Dummy Hardware TCP Server stopping.")
def _handleByteOrderRequest(self, request): chipsLog.debug("Byte-order transaction requested") # Response header is the request header with direction bit changed responseHeader = IPbusHeader.updateDirection(request.getHeader(), 1) return TransactionElement.makeFromHeaderAndBody(responseHeader)
def serveForever(self): """The only function any user of this class needs to run! Receives, acts on, and responds to TCP control packets as the Mini-T (or similar hardware) would. Packets are received by the main thread and queued for action and response by a second thread. """ chipsLog.info("Dummy Hardware TCP Server starting") # This starts the packet "act and respond" handler thread self.start() try: chipsLog.debug("Awaiting connection...") self._connectedSocket, addr = self._socket.accept() # TCP chipsLog.debug("Client connection accepted.") except KeyboardInterrupt: chipsLog.warning( "\nKeyboard interrupt (ctrl-c) received whilst waiting for a TCP connection" ) self._stopServingAndJoinThreads() return while not self.stopServing: try: data = self._connectedSocket.recv( DummyHardware.SOCKET_BUFFER_SIZE) # TCP except KeyboardInterrupt: chipsLog.warning( "\nKeyboard interrupt (ctrl-c) received whilst waiting for incoming TCP packet" ) self._stopServingAndJoinThreads() return except: chipsLog.warning( "\nException caught whilst waiting for incoming TCP packet" ) self._stopServingAndJoinThreads() return if not data: chipsLog.debug("TCP socket received an empty packet from " + repr(addr) + ": assuming connection closed.") try: chipsLog.debug("Awaiting new connection...") self._connectedSocket, addr = self._socket.accept() # TCP chipsLog.debug("Client connection accepted.") continue except KeyboardInterrupt: chipsLog.warning( "\nKeyboard interrupt (ctrl-c) received whilst waiting for a TCP connection" ) self._stopServingAndJoinThreads() return chipsLog.debug("\nReceived TCP packet from " + repr(addr)) transaction = Transaction.constructHostTransaction(data, addr) self._transaction_queue.put(transaction) chipsLog.info("Dummy Hardware TCP Server stopping.")
class ChipsBusBase(object): """Common Hardware Interface Protocol System Bus (CHIPS-Bus) abstract base-class Allows you to communicate with and control devices running Jeremy Mans's, et al, IP-based uTCA control system firmware. This base class represents the part of the ChipsBus code that is protocol-agnostic. Protocol-specific concrete classes, using either UDP or TCP, derive from this. The bus assumes 32-bit word addressing, so in a 32-bit address space up to 2^34 bytes in total can be addressed. """ IPBUS_PROTOCOL_VER = 2 # I.e. IPbus Protocol v1.4 SOCKET_BUFFER_SIZE = 32768 # Max UDP/TCP socket buffer size in bytes for receiving packets. MAX_TRANSACTION_ID = 4095 # The maximum value the transaction ID field can go up to. # Max depth of a block read or write before bridging the read/write over multiple requests. # Note that for UDP the max IPBus packet size cannot exceed 368 32-bit words (1472 bytes), or # it'll fail due to reaching the max Ethernet packet payload size (without using Jumbo Frames). # If you are Jumbo-Frames capable, then this number should not exceed 2000. Note that the # jumbo-frames firmware uses a 8192-byte buffer, so we can't make use of the full 9000 byte # Jumbo Frame anyway. MAX_BLOCK_TRANSFER_DEPTH = 255 # Temporary hack to get IPbus v2.0 compatible code working # The max size of the request queue (note: current API excludes ability to queue block transfer requests) MAX_QUEUED_REQUESTS = 80 def __init__(self, addrTable, hostIp, hostPort, localPort=None): """ChipsBus abstract base-class constructor addrTable: An instance of AddressTable for the device you wish to communicate with. hostIP: The IP address of the device you want to control, e.g. the string '192.168.1.100'. hostPort: The network port number of the device you want to control. localPort: If you wish to bind the socket to a particular local port, then specify the the local port number here. The default (None) means that the socket will not bind to any specific local port - an available port be found when it comes to sending any packets. """ object.__init__(self) self._transactionId = 1 self.addrTable = addrTable self._hostAddr = (hostIp, hostPort) self._queuedRequests = [] # Request queue self._queuedAddrTableItems = [ ] # The corresponding address table item for each request in the request queue self._queuedIsARead = [ ] # This holds a True if the corresponding request in _queuedRequests is a read, or a False if it's a write. def queueRead(self, name, addrOffset=0): """Create a read transaction element and add it to the transaction queue. This works in the same way as a normal read(), except that many can be queued into a packet and dispatched all at once rather than individually. Run the queued transactions with queueRun(). Only single-register reads/writes can be queued. Block reads/writes, etc, cannot be queued. """ if len(self._queuedRequests) < ChipsBus.MAX_QUEUED_REQUESTS: chipsLog.debug("Read queued: register '" + name + "' with addrOffset = 0x" + uInt32HexStr(addrOffset)) addrTableItem = self.addrTable.getItem( name ) # Get the details of the relevant item from the addr table. if not addrTableItem.getReadFlag(): raise ChipsException( "Read transaction creation error: read is not allowed on register '" + addrTableItem.getName() + "'.") self._queuedRequests.append( self._createReadTransactionElement(addrTableItem, 1, addrOffset)) self._queuedAddrTableItems.append(addrTableItem) self._queuedIsARead.append(True) else: chipsLog.warning( "Warning: transaction not added to queue as transaction queue has reached its maximum length!\n" + "\tPlease either run or clear the transaction queue before continuing.\n" ) def queueWrite(self, name, dataU32, addrOffset=0): """Create a register write (RMW-bits) transaction element and add it to the transaction queue. This works in the same way as a normal write(), except that many can be queued into a packet and dispatched all at once rather than individually. Run the queued transactions with queueRun(). Only single-register reads/writes can be queued. Block reads/writes, etc, cannot be queued. """ if len(self._queuedRequests) < ChipsBus.MAX_QUEUED_REQUESTS: dataU32 = dataU32 & 0xffffffff # Ignore oversize input. chipsLog.debug("Write queued: dataU32 = 0x" + uInt32HexStr(dataU32) + " to register '" + name + "' with addrOffset = 0x" + uInt32HexStr(addrOffset)) addrTableItem = self.addrTable.getItem( name ) # Get the details of the relevant item from the addr table. if not addrTableItem.getWriteFlag(): raise ChipsException( "Write transaction creation error: write is not allowed on register '" + addrTableItem.getName() + "'.") # self._queuedRequests.append(self._createRMWBitsTransactionElement(addrTableItem, dataU32, addrOffset)) # self._queuedAddrTableItems.append(addrTableItem) # self._queuedIsARead.append(False) self._queuedRequests.append( self._createWriteTransactionElement(addrTableItem, [dataU32], addrOffset)) self._queuedAddrTableItems.append(addrTableItem) self._queuedIsARead.append(False) else: chipsLog.warning( "Warning: transaction not added to queue as transaction queue has reached its maximum length!\n" + "\tPlease either run or clear the transaction queue before continuing.\n" ) def queueRun(self): """Runs the current queue of single register read or write transactions and returns two lists. The first contains the values read and the second contains the values written. Note: Only single-register reads/writes can be queued. Block reads/writes, etc, cannot be queued. """ chipsLog.debug("Running all queued transactions") requestQueueLength = len(self._queuedRequests) readResponse = [] writeResponse = [] try: transaction = self._makeAndRunTransaction(self._queuedRequests) except ChipsException, err: self.queueClear() raise ChipsException( "Error while running queued transactions:\n\t" + str(err)) for i in range(requestQueueLength): addrTableItem = self._queuedAddrTableItems[i] if len(transaction.responses[0].getBody()) > 0: transactionResponse = transaction.responses[ i - requestQueueLength].getBody()[0] & 0xffffffff transactionResponse = addrTableItem.shiftDataFromMask( transactionResponse) else: transactionResponse = 0 if self._queuedIsARead[i]: readResponse.append(transactionResponse) chipsLog.debug("Read success! Register '" + addrTableItem.getName() + "' returned: 0x" + uInt32HexStr(transactionResponse)) else: writeResponse.append(transactionResponse) chipsLog.debug("Write success! Register '" + addrTableItem.getName() + "' assigned: 0x" + uInt32HexStr(transactionResponse)) self.queueClear() response = [readResponse, writeResponse] return response