13.9 ターゲットとのやりとり

フラッシュプログラミングを実行するには、プログラミングメソッドはターゲットにアクセスする必要がある場合があります。

フラッシュプログラマは、そのために DTSL API へのアクセスを提供しており、プログラミングメソッドはクラス FlashMethodv1getConnection() 関数によって接続を確立することができます。
この関数は、プログラミングメソッドの setup() 関数で呼び出します。例えば、DS-5デバッガからのように、既に接続が開いている場合、以下が再使用されます。
def setup(self):
    # connect to core
    self.conn = self.getConnection()

DS-5 では、例として flash_example-FVP-A9x4 が提供されています。この例は、DS-5 を使用したフラッシュデバイスの 2 通りのプログラミング方法を示しています。1 つは、Keil フラッシュメソッドを使用する方法で、もう 1 つは Jython で記述されたカスタムフラッシュメソッドを使用する方法です。便宜上、Cortex-A9x4 FVP モデル (DS-5 に付属) はターゲットデバイスとして使用されます。この例は、新しいフラッシュアルゴリズムを作成するためのテンプレートとして利用できます。例に付属の readme.html には、例の使用方法に関する基本的な情報が記載されています。

コアへのアクセス

ターゲットとのやりとりする際、コアへの接続を開くことが必要になる場合があります。既に開いている接続がデバッガにある場合、新しい接続を確立できないことがあります。必要な場合にのみ接続を開くためにユーティリティ関数 ensureDeviceOpen() が提供されています。接続が開いており、teardown() 関数でのプログラミング後に閉じる必要がある場合、この関数は true を返します。
コアのレジスタとメモリにアクセスするには、コアを停止する必要があります。そのためには、ensureDeviceStopped() 関数を使用します。
def setup(self):
    # connect to core & stop
    self.conn = self.getConnection()
    coreName = self.getParameter("coreName")
    self.dev = self.conn.getDeviceInterfaces().get(coreName)
    self.deviceOpened = ensureDeviceOpen(self.dev)
    ensureDeviceStopped(self.dev)
 
def teardown(self):
    if self.deviceOpened:
        # close device connection if opened by this script
        self.dev.closeConn()

メモリの読み出し/書き込み

コアのメモリは、デバイスオブジェクト (IDevice) の memWrite() 関数、memFill() 関数、memRead() 関数を使用してアクセスできます。
from com.arm.rddi import RDDI
from com.arm.rddi import RDDI_ACC_SIZE
from jarray import zeros
 
...
 
    def program(self):
        ...
        self.dev.memFill(0, addr, RDDI_ACC_SIZE.RDDI_ACC_WORD,
                         RDDI.RDDI_MRUL_NORMAL, False, words, 0)
        self.dev.memWrite(0, addr, RDDI_ACC_SIZE.RDDI_ACC_WORD,
                          RDDI.RDDI_MRUL_NORMAL, False, len(buf), buf)
        ...
 
    def verify(self):
        ...
        readBuf = zeros(len(buf), 'b')
        self.dev.memRead(0, addr, RDDI_ACC_SIZE.RDDI_ACC_WORD,
                         RDDI.RDDI_MRUL_NORMAL, len(readBuf), readBuf)
        ...
device_memory では、メソッドコードを明確にするためのユーティリティルーチンが提供されています。
from flashprogrammer.device_memory import writeToTarget, readFromTarget
 
...
 
    def program(self):
        ...
        writeToTarget(self.dev, address, buf)
        ...
 
    def verify(self):
        ...
        readBuf = readFromTarget(self.dev, addr, count)
        ...

レジスタの読み出しと書き込み

コアのレジスタは IdeviceregReadList() 関数を使用して読み出し、regWriteList() 関数を使用して書き込みます。

Long 型ではなく、整数型の値のみを渡すように注意する必要があります。
これらのレジスタには、数値の ID を使用してアクセスします。これらの ID はターゲット固有です。例えば、R0 は Cortex-A デバイス上ではレジスタ 1 ですが、Cortex-M デバイス上ではレジスタ 0 です。
execution.py は、レジスタ名を数値にマップし、名前で読み出しまたは書き込みできるようにする関数です。
  • writeRegs(device, regs) は、複数のレジスタをデバイスに書き込みます。regs はペア (名前、値) のリストです。
    以下に例を示します。
    writeRegs (self.dev, [ ("R0", 0), ("R1", 1234), ("PC", 0x8000) ]
    この例は、R0R1、および PC (R15) を設定します。
  • readReg(device, reg) は、名前付きレジスタを読み出します。
    以下に例を示します。
    value = readReg ("R0")
    この例は、R0 を読み取り、その値を返します。

コアでのコードの実行

コアは、go() 関数および stop() 関数によって開始および停止できます。ブレークポイントは、setSWBreak() 関数または setHWBreak() 関数を使用して設定でき、clearSWBreak() 関数または clearHWBreak() 関数を使用してクリアできます。ブレークポイントに到達するまでに時間がかかることがあるため、スクリプトはブレークポイントに到達してコアが停止するまで、ターゲットへのそれ以上のアクセスを控え、待機する必要があります。
execution.py では、ターゲットでのコードの実行のためのユーティリティメソッドが提供されています。
  • コアの停止を要求して停止ステータスイベントの受信を待つには、タイムアウトになる前にイベントが受信されない場合、エラーを発行します。
    stopDevice(device, timeout=1.0):
  • デバイスのステータスを確認し、停止していない場合に stopDevice() を呼び出すには、次のようにします。
    ensureDeviceStopped(device, timeout=1.0):
  • コアを開始してその停止を待つ場合、タイムアウトになるまでにそれが停止しなければ、コアの停止を強制し、エラーを発行します。呼び出し元は、レジスタを適切に設定し、ブレークポイントまたはベクタキャッチを設定し、目的のアドレスでコアが停止するようにする必要があります。
    runAndWaitForStop(device, timeout=1.0):
  • ソフトウェアブレークポイントを addr に設定する場合、runAndWaitForStop() を呼び出すことによりコアを開始してその停止を待ちます。呼び出し元は、レジスタを適切に設定する必要があります。
    runToBreakpoint(device, addr, bpFlags = RDDI.RDDI_BRUL_STD, timeout=1.0):
フラッシュプログラミングアルゴリズムは、ターゲット自体で実行される関数として実装されることがよくあります。これらの関数は、パラメータがレジスタを通過して渡される場合、パラメータを取ることがあります。
funcCall() では、メソッドは AAPCS に従う関数 (制限付き) を呼び出すことができます。
  • レジスタ R0-R3 内の最初 4 つまでのパラメータが渡されます。
  • これ以上のパラメータはすべてスタックを使用して渡されます。
  • 32 ビットまでの整数型またはポインタ型のパラメータがサポートされています。浮動小数点または 64 ビットの整数型はサポートされません。
  • 演算結果は R0 に返されます。
上記を使用してデータを RAM に書き込むことにより、フラッシュプログラミングをシミュレートすることができます。example_method_1.py を参照してください。これにより、以下が可能です。
  • setup() でターゲットに接続する。
  • デスティネーション RAM を 0 で埋めて消去をシミュレートする。
  • 作業 RAM のライトバッファにデータを書き込む。
  • ライトバッファからデスティネーション RAM にデータをコピーするルーチンを実行する。
  • デスティネーション RAM からの読み出しにより書き込みを検証する。

プログラミングアルゴリズムイメージのターゲットへのロード

プログラミングアルゴリズムは、.elf イメージにコンパイルされることがよくあります。
FlashMethodv1.locateFile() は、例えばパラメータからファイルを検索し、接頭辞 FDB:// を絶対パスに解決します。
symfile.py は、クラス SymbolFileReader を提供します。このクラスにより、プログラミングメソッドはイメージファイルをロードして、シンボルの場所を取得できます。例えば、関数の場所を取得するには、次のようにします。
        # load the algorithm image
        algorithmFile = self.locateFile(self.getParameter('algorithm'))
        algoReader = SymbolFileReader(algorithmFile)
 
        # Find the address of the Program() function
        funcInfo = algoReader.getFunctionInfo()['Program']
        programAddr = funcInfo['address']
        if funcInfo['thumb']:
            # set bit 0 if symbol is thumb
            programAddr |= 1
image_loader.py は、イメージをターゲットにロードするためのルーチンを提供します。
        # load algorithm into working RAM
        algoAddr = self.ramAddr + 0x1000 # allow space for stack, buffers etc
        loadAllCodeSegmentsToTarget(self.dev, algoReader, algoAddr)
アルゴリズムバイナリが場所に関係なくリンクされている場合、シンボルのアドレスはロードアドレスの相対アドレスとなり、そのターゲットでコードを実行する際にはこのオフセットを適用する必要があります。
        programAddr += algoAddr
        args = [ writeBuffer, destAddr, pageSize ]
        funcCall(self.dev, programAddr, args, self.stackTop)

進行状況レポート

フラッシュプログラミングによりプロセスの速度が低下する場合があるため、進行状況レポート機能があることが望まれます。メソッドは、operationStarted() を呼び出すことで、これを行うことができます。これにより、次の関数を持つオブジェクトが返されます。
  • progress() - レポート済みの進行状況を更新します。
  • complete() - 操作を完了済み (成功または失敗) としてレポートします。
進行状況レポートを、前の例の program() 関数に追加することができます。
    def program(self, regionID, offset, data):
        # calculate the address to write to
        region = self.getRegion(regionID)
        addr = region.getAddress() + offset
         
        # Report progress, assuming erase takes 20% of the time, program 50%
        # and verify 30%
        progress = self.operationStarted(
            'Programming 0x%x bytes to 0x%08x' % (data.getSize(), addr),
            100)
 
        self.doErase(addr, data.getSize())
        progress.progress('Erasing completed', 20)
 
        self.doWrite(addr, data)
        progress.progress('Writing completed', 20+50)
 
        self.doVerify(addr, data)
        progress.progress('Verifying completed', 20+50+30)
 
        progress.completed(OperationResult.SUCCESS, 'All done')
 
        # register values have been changed
        return TargetStatus.STATE_LOST
上記の例には、各フェーズの終了時にのみレポートする大まかな進行状況レポートのみが含まれます。各サブタスクで進行状況モニターを使用できるようにすることで、より効果的な解決案を実現することができます。subOperation() はチャイルド進行状況モニターを作成します。
エラーが発生したら進行状況モニターで completed() が呼び出されるように注意する必要があります。進行状況モニターの作成後は、コードの前後に try: except: ブロックを配置することをお勧めします。
    import java.lang.Exception
    def program(self, regionID, offset, data):
        progress = self.operationStarted(
            'Programming 0x%x bytes to 0x%08x' % (data.getSize(), addr),
            100)
        try:
            # Do programming
        except (Exception, java.lang.Exception), e:
            # exceptions may be derived from Java Exception or Python Exception
            # report failure to progress monitor & rethrow
            progress.completed(OperationResult.FAILURE, 'Failed')
            raise

import java.lang.Exception - インポートを省略して Java の例外がスローされた場合、Java のネームスペースが見つからないことを示す紛らわしいエラーレポートを Jython から受け取ることがあります。さらに、エラーのソースとして示された python 行の場所が正しくありません。

キャンセル

長期間実行されているフラッシュ操作を中断する場合、プログラミングメソッドで isCancelled() を呼び出して、操作がキャンセルされたかどうかを確認することができます。True が返された場合、メソッドはプログラミングを停止します。

その場合も、teardown() 関数が呼び出されます。

メッセージ

プログラミングメソッドは、以下の関数を呼び出すことでメッセージをレポートできます。
  • warning() - 警告メッセージをレポートします。
  • info() - 情報メッセージをレポートします。
  • debug() - デバッグメッセージをレポートします。通常は表示されません。

ファイルの検索と解決

FlashMethodv1.locateFile() は、例えばパラメータからファイルを検索し、接頭辞 FDB:// を絶対パスに解決します。
これは、DS-5 で設定されたすべてのコンフィギュレーションデータベースのすべてのフラッシュサブディレクトリのパスを検索します。
以下に例を示します。
<DS5_INSTALL_DIR>/sw/debugger/configdb/Flash/
c:\MyDB\Flash

エラー処理

エラーが発生すると例外がスローされます。プログラミングメソッドによる API 呼び出しからのエラーは、com.arm.debug.flashprogrammer.FlashProgrammerException となります (またはこれから派生します)。メソッドは、Python の raise キーワードを使用してエラーをレポートすることもできます。例えば、検証が失敗した場合は次のようにします。
# compare contents
res = compareBuffers(buf, readBuf)
if res != len(buf):
    raise FlashProgrammerRuntimeException, "Verify failed at address:%08x" %
(addr + res)
プログラミングメソッドで、例外がスローされた場合にクリーンアップが実行されることを確認する必要がある場合、次のコードによりテンプレートが生成されます。
    import java.lang.Exception
         ...
        try:
            # Do programming
        except (Exception, java.lang.Exception), e:
            # exceptions may be derived from Java Exception or Python Exception
            # report failure to progress monitor & rethrow
             
            # Handle errors here
             
            # Rethrow original exception
            raise
        finally:
            # This is always executed on success or failure
            # Close resources here
使用例については、進行状況ハンドラのセクションを参照してください。

import java.lang.Exception - インポートを省略して Java の例外がスローされた場合、Java のネームスペースが見つからないことを示す紛らわしいエラーレポートを Jython から受け取ることがあります。さらに、エラーのソースとして示された python 行の場所が正しくありません。

外部ツールの実行

一部のターゲットには、既にスタンドアロンのフラッシュプログラミングツールがあります。このツールを呼び出すための DS-5デバッガプログラミングメソッドを作成して、ロードするイメージのパスに渡すことができます。以下の例は、実際のフラッシュプログラミングツールの代わりに fromelf ツールを使用して、これを実行する方法を示しています。
from flashprogrammer.flash_method_v1 import FlashMethodv1
from com.arm.debug.flashprogrammer.IProgress import OperationResult
from com.arm.debug.flashprogrammer import TargetStatus
import java.lang.Exception
import subprocess
 
class RunProgrammer(FlashMethodv1):
    def __init__(self, methodServices):
        FlashMethodv1.__init__(self, methodServices)
 
 
    def program(self, regionID, offset, data):
 
        progress = self.operationStarted(
            'Programming 0x%x bytes with command %s' % (data.getSize(), ' '.join(cmd)),
            100)
        try:
            # Get the path of the image file
            file = data.getUnderlyingFile().getCanonicalPath()
         
            cmd = [ 'fromelf', file ]
            self.info("Running %s" % ' '.join(cmd))
         
     # run command
     proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
     out, err = proc.communicate()
      
     # pass command output to user as info message
     self.info(out)
         
            progress.progress('Completed', 100)
            progress.completed(OperationResult.SUCCESS, 'All done')
 
        except (Exception, java.lang.Exception), e:
            # exceptions may be derived from Java Exception or Python Exception
            # report failure to progress monitor & rethrow
            progress.completed(OperationResult.FAILURE, 'Failed')
            raise
             
        return TargetStatus.STATE_RETAINED
os.environ は、ターゲットのツールチェインの場所などの環境変数をルックアップするために使用できます。
programmerTool = os.path.join(os.environ['TOOLCHAIN_INSTALL'], 'flashprogrammer')

セットアップとテアダウン

フラッシュコンフィギュレーションファイルで、フラッシュプログラミングの前後にスクリプトが実行されるように指定できます。これらはセットアップスクリプトとテアダウンスクリプトと呼ばれ、セットアップタグとテアダウンタグを使用して定義します。セットアップスクリプトにより、ターゲットはフラッシュプログラミングの準備ができた状態になります。
これには以下の 1 つ以上が含まれる場合があります。
  • ターゲットをリセットする。
  • 割り込みを無効にする。
  • フラッシュプログラミングを妨げる可能性のあるペリフェラルを無効にする。
  • DRAM をセットアップする。
  • フラッシュコントロールを有効にする。
  • クロックを適切に設定する。
テアダウンスクリプトは、フラッシュプログラミングの後にターゲットを使用可能な状態に戻します。
どちらの場合も、ターゲットの再設定が必要になることがあります。リセットベクタのコアを停止するには、以下のコードを使用します。

このサンプルコードは、コアで RSET ベクタキャッチ機能がサポートされていることを前提としています。
def setup(client, services):
    # get a connection to the core
    conn = services.getConnection()
    dev = conn.getDeviceInterfaces().get("Cortex-M3")
    ensureDeviceOpen(dev)
    ensureDeviceStopped(dev)
     
    dev.setProcBreak("RSET")
    dev.systemReset(0)
    # TODO:wait for stop!
    dev.clearProcBreak("RSET")

フラッシュメソッドパラメータを提供するその他の方法

フラッシュコンフィギュレーションファイルでは、XML にエンコードされたフラッシュ領域情報とフラッシュパラメータ情報を提供できます。ただし、一部のメソッドでは、この情報をフラッシュアルゴリズム自体から抽出する必要がある場合があります。
プログラミングメソッドでは、getDefaultRegions()getDefaultParameters() の 1 組のクラスメソッドをオーバーライドして、メソッドのアドレス領域とパラメータを持つフラッシュコンフィギュレーションファイル(ある場合)の情報を拡張できます。
getDefaultParameters().
from com.arm.debug.flashprogrammer import FlashRegion
 
...
class ProgrammingMethod(FlashMethodv1):
...
 
    def getDefaultRegions(self):
        return [ FlashRegion(0x00100000, 0x10000), FlashRegion(0x00200000, 0x10000) ]
 
    def getDefaultParameters(self):
        params = {}
        params['param1'] = "DefaultValue1"
        params['param2'] = "DefaultValue2"
        return params
上記のコードでは、0x001000000x00200000 の 2 つの 64kB 領域を定義します。このメソッドによって提供される領域は、コンフィギュレーションファイルでデバイスの領域が指定されていない場合にのみ使用されます。上記のコードは、2 つの追加パラメータを定義します。これらのパラメータは、フラッシュコンフィギュレーションのパラメータに追加されます。パラメータが両方で定義されている場合は、フラッシュコンフィギュレーションファイルのデフォルト値が使用されます。この領域およびパラメータの情報は(上記の例のようにハードコード化するのではなく)アルゴリズムバイナリ自体から抽出できます。Keil アルゴリズムイメージには、デバイスによって保護される領域およびデバイスのプログラミングパラメータを定義するデータ構造が含まれています。Keil プログラミングメソッドは、アルゴリズムバイナリ(コンフィギュレーションファイルのパラメータにより指定)をロードし、この情報を抽出して、これらの呼び出しで返します。
非機密扱いPDF file icon PDF 版ARM DUI0446WJ
Copyright © 2010-2015 ARM.All rights reserved.