iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 27
0

0. 前言

經過上篇苦戰後,截至昨日為止,已經說明完整個OpenOCD的架構/實作,
從上層Target支援開始,一路到底層FTDI-based Adapter支援的整個流程!

接下來的幾天中(鐵人賽大概剩下約莫4篇左右),讓我們來研究一下其他好玩的東西!
今天主要會探討OpenOCD中,對"Flash"的支援!

讓我們開始吧!
  
  
  

1. NOR Flash Overview

先來看一下NOR Flash基本的架構:

http://wiki.csie.ncku.edu.tw/NORNAND.png
---引用自成大資工 Wiki - Flash

然後剩下的就請自行參考Wiki

不過這邊要提一下主要的特點:

  • 支援隨機存取: 讀取方式比較簡單,給Address就可以讀Data
  • 支援XIP Mode: 基本上就是可以直接在上面執行程式,系統中不用外掛RAM來跑Code
  • Erase/Program以Block為一個單位!
  • ....剩下不重要XD

以之前使用的Arty開發版為例,上面就內建16MB的Quad-SPI Flash(Micron N25Q128A)

https://www.xilinx.com/content/xilinx/en/products/boards-and-kits/arty/_jcr_content/mainParsys/xilinxtabs2/tab-hardware/xilinximage.img.png/1503511073525.png
---引用自
Artix-7 35T Arty FPGA Evaluation Kit

經後範例中出現的Flash也是以這顆為主!!
好! 報告完畢!
  
  
  

2. OpenOCD Flash Support

在這節中,要先說明一下Flash中Memory的組成,基本上可以參考下圖:

http://wiki.csie.ncku.edu.tw/111.png
---引用自成大資工 Wiki - Flash

一塊Flash裡面會切成一個或多個"Bank";
一個"Bank"中,裡面還會再切成多個"Sector"!
每個Sector中會有自己的大小、Offset(跟Flash起始位置間的偏移)、狀態等等!
另外針對一個"Bank",還要有一套"Flash Driver",提供一系列的函式用來驅動(讀取/寫入/初始化)這個Bank!
基本上OpenOCD就是遵循這個觀念在設計整體架構!

首先,我們來看一下,一個"Bank"所需要包含的東西,
請參考(src/flash/nor/core.h):

struct flash_bank {
    const char *name;

    struct target *target; /**< Target to which this bank belongs. */

    struct flash_driver *driver; /**< Driver for this bank. */
    void *driver_priv; /**< Private driver storage pointer */

    int bank_number; /**< The 'bank' (or chip number) of this instance. */
    uint32_t base; /**< The base address of this bank */
    uint32_t size; /**< The size of this chip bank, in bytes */

    int chip_width; /**< Width of the chip in bytes (1,2,4 bytes) */
    int bus_width; /**< Maximum bus width, in bytes (1,2,4 bytes) */

    /** Erased value. Defaults to 0xFF. */
    uint8_t erased_value;

    /** Default padded value used, normally this matches the  flash
     * erased value. Defaults to 0xFF. */
    uint8_t default_padded_value;

    /**
     * The number of sectors on this chip.  This value will
     * be set intially to 0, and the flash driver must set this to
     * some non-zero value during "probe()" or "auto_probe()".
     */
    int num_sectors;
    /** Array of sectors, allocated and initialized by the flash driver */
    struct flash_sector *sectors;

    /**
     * The number of protection blocks in this bank. This value
     * is set intially to 0 and sectors are used as protection blocks.
     * Driver probe can set protection blocks array to work with
     * protection granularity different than sector size.
     */
    int num_prot_blocks;
    /** Array of protection blocks, allocated and initilized by the flash driver */
    struct flash_sector *prot_blocks;

    struct flash_bank *next; /**< The next flash bank on this chip */
};

主要內容如下:

  • name: 基本上就是幫這個Bank給個名稱,方面之後用name找bank
  • driver: 這邊放"Flash Driver",操作都是從Bank找到Flash Driver後,再到底層去處理
  • base/size: 就是放這個Bank的Base Address和大小
  • num_sectors: 旗下Sectors的總數
  • sectors: 每個Sector都有自己的狀態,這邊用一個Array存放指向這些Sector的指標

再來我們看一下"Sector"所包含的東西,請參考(src/flash/nor/core.h):

struct flash_sector {
    /** Bus offset from start of the flash chip (in bytes). */
    uint32_t offset;
    /** Number of bytes in this flash sector. */
    uint32_t size;
    /**
     * Indication of erasure status: 0 = not erased, 1 = erased,
     * other = unknown.  Set by @c flash_driver_s::erase_check.
     *
     * Flag is not used in protection block
     */
    int is_erased;
    /**
     * Indication of protection status: 0 = unprotected/unlocked,
     * 1 = protected/locked, other = unknown.  Set by
     * @c flash_driver_s::protect_check.
     *
     * This information must be considered stale immediately.
     * A million things could make it stale: power cycle,
     * reset of target, code running on target, etc.
     *
     * If a flash_bank uses an extra array of protection blocks,
     * protection flag is not valid in sector array
     */
    int is_protected;
};

內容很簡單啦:

  • offset: 跟Flash起始位置間的偏移量
  • size: 這個Sector的大小 (注意: Sector大小可能不同,請參照文件)
  • is_erased: Flash在寫入之前,要先進行Erase,這邊用來記錄這個Sector有沒有被清理過!
  • is_protected: 用來保護這個Sector,防止被Erase/Program
      
      
      

3. OpenOCD Flash Commands

底下會介紹幾個在OpenOCD中常用到的Flash Commands!
  
  

3.1 flash bank <name> <driver> <base> <size> <chip_width> <bus_width> <target> [driver_options]

這個Command主要用在定義一個Flash內的Bank,基本內容如下:

  • name: 給的方便使用的名稱
  • driver: 需要使用哪套Flash Driver來使用這塊Bank
  • base: Bank的Based Address
  • size: Bank的大小,可以不指定,但要補上0
  • chip_width: 用來標示這個Chip的頻寬,目前大部分的Driver都不需要指定
  • bus_width: 用來標示Data bus的頻寬,目前大部分的Driver都不需要指定
  • target: 用來標示這個Bank屬於哪個Target
  • driver_options: 如果Flash Driver需要其他參數,可以接在後面這邊

使用方式可以參考底下範例:

set _CHIPNAME riscv
set _TARGETNAME $_CHIPNAME.cpu

flash bank spi0 fespi 0x40000000 0 0 0 $_TARGETNAME 0x20004000

這邊標示出,有個Bank叫spi0,需要使用fespi來驅動,
Bank的位置在0x40000000!

最後面的0x20004000則是fespi需要的參數,後面會提到!
  
  

3.2 flash write_image [erase] [unlock] filename [offset] [type]

這個Command主要用在燒錄Flash上,主要參數如下:

  • erase: 表示在Program這個Flash之前,要先將Sector做Erase
  • unlock: 表示在Program這個Flash之前,要先將Sector做unlock
  • filename: Image的檔名
  • offset: 燒錄的偏移量
  • type: Image的檔案類型,OpenOCD支援以下幾種type
    • bin: Binary二進位檔案
    • elf: ELF檔案
    • ....其他我也不常用!

使用方式可以參考底下範例:

flash write_image erase hello.elf

  
  

3.3 program filename [verify] [reset] [exit] [offset]

這個Command,是我比較常用到,用來燒錄Flash的Command,
基本上他把燒錄的動作簡化,並可以再燒錄完畢後執行指定的動作!

內容如下:

  • filename: 指定Image的檔案名稱
  • verify: 在燒錄完畢後,是否需要驗證燒錄的資料
  • reset: 燒錄完畢後,將Target進行Reset
  • exit: 燒錄完畢後,直接結束OpenOCD
  • offset: 燒錄的偏移量

使用方式可以參考底下範例:

program hello.bin verify reset exit

這個實作也蠻有趣的,主要是用一個TCL檔包裝這個Commmand,
請參考(src/flash/startup.tcl):

proc program {filename args} {
    set exit 0

    foreach arg $args {
        if {[string equal $arg "verify"]} {
            set verify 1
        } elseif {[string equal $arg "reset"]} {
            set reset 1
        } elseif {[string equal $arg "exit"]} {
            set exit 1
        } else {
            set address $arg
        }
    }

    # make sure init is called
    if {[catch {init}] != 0} {
        program_error "** OpenOCD init failed **" 1
    }

    # reset target and call any init scripts
    if {[catch {reset init}] != 0} {
        program_error "** Unable to reset target **" $exit
    }

    # start programming phase
    echo "** Programming Started **"
    if {[info exists address]} {
        set flash_args "$filename $address"
    } else {
        set flash_args "$filename"
    }

    if {[catch {eval flash write_image erase $flash_args}] == 0} {  ###譯註: 燒錄
        echo "** Programming Finished **"
        if {[info exists verify]} {
            # verify phase
            echo "** Verify Started **"
            if {[catch {eval verify_image $flash_args}] == 0} { ###譯註: 驗證
                echo "** Verified OK **"
            } else {
                program_error "** Verify Failed **" $exit
            }
        }

        if {[info exists reset]} {
            # reset target if requested
            # also disable target polling, we are shutting down anyway
            poll off
            echo "** Resetting Target **"
            reset run                       ###譯註: Reset Target
        }
    } else {
        program_error "** Programming Failed **" $exit
    }

    if {$exit == 1} {
        shutdown                            ###譯註: 關閉OpenOCD
    }
    return
}

  
  
  

99. 結語

今天簡單的介紹了一下Flash相關的背景知識,明天將會深入探討,
Sifive中,Flash Driver的實作和背後原理!
  
  
  

參考資料

  1. OpenOCD User's Guide
  2. 成大資工 Wiki - Flash

上一篇
Day 26: 您不可不知的FT2232H (3/3) - MPSSE & JTAG Control
下一篇
Day 28: 高手不輕易透露的技巧(2/2) - Flash Driver & Target Burner
系列文
系統架構秘辛:了解RISC-V 架構底層除錯器的秘密!30

尚未有邦友留言

立即登入留言