0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cyclone® 10 LP Nios II から EPCQ128A を読み書きしてみた

0
Last updated at Posted at 2025-11-29

FPGA勉強中です

・Cyclone 10 LP Evaluation Kit で EPCQ128A を Nios II から直接読み書きする仕組みを検証しました。
・実際にセクタ消去 → 1バイト書き込み → 読み出し検証を行い、Flash のユーザ領域アクセスが正常に動作することを確認しました。

前回のベース

・前回使用したnios2のサンプルの記事です。

・それでは本題です

FPGA側の作業

・ベースは、前回のnios2のhelloworldサンプルを使います。

Platform Designerの設定

IPを追加する

前回のnios2サンプルと同じIP構成で、epcq_controller2を追加します。
IPカタログよりEPCQで検索して出てくる「epcq_controller2」を追加します。

IPを配線する

nios2のサンプルと同じ配線に対して、EPCQ Controller IIとの接続を追加します。
・EPCQ Controller II の clockをシステムクロックclk_0.clk(50MHz)と接続
・EPCQ Controller II の reset をシステムリセット接続
・EPCQ Controller II の avl_csr をNios II の data_master に接続
・EPCQ Controller II の avl_mem をNios II の data_master に接続
・EPCQ Controller II の csrとmemのオフセットアドレスを変更する。(競合していたため)

・以下のような配線になります。
image.png

オンチップメモリのサイズを拡張しておく

・nios2サンプルでは16KBで問題なかったですが、EPCQ Controllerを追加するとサイズを超えてしまうようです。
・16KB⇒32KBに変更します。
image.png

その他設定スクショ

・ベクタ設定は、オンチップメモリに設定しました。
image.png
・nios2は「e」でOK
image.png
・EPCQ ControllerのデバイスタイプはEPCQ128Aを選択
image.png

エラーが出ないことを確認して「Generate HDL ⇒ 閉じます」

Quartusの設定

設定を1か所だけ確認

・Assignments⇒device⇒Device and Pin Options で [Use Configuration Device]にチェック
・EPCQ128Aを選択します。

image.png

トップファイル

・niosのサンプルとほとんど同じです

module  NiosII_Hw_Dev_C10 (
	
	// Reset and Clocks
	input              C10_CLK50M,
   input              C10_RESETN, 

	// LED and Push Button
	input     [3:0]    USER_PB,
	output    [3:0]    USER_LED
	 );
	 
	 wire [2:0] led;
	 
	 // Push button to turn on LED
	 assign USER_LED[0] = USER_PB[0];
	 
	 // LED ON represent counter binary value of 1
	 assign USER_LED[3:1] = ~led;
	 
	 // Instantiate nios2e
	 nios2e u0 (
		 .clk_clk                                (C10_CLK50M),                      
		 .reset_reset_n                          (C10_RESETN),                
		 .led_external_connection_export         (led)
	 );
	 
endmodule

フルコンパイル、オブジェクトの作成

・フルコンパイルしてsofを作成します。
・sofからjicを作成します。
・jicを評価ボードに書き込みます。

ファーム側の作成

続いてnios2に載せるファームを作ります。

フォルダ構成

・以下のフォルダ構成になるようにsoftware以下のフォルダを作成します。
top_project/
├─ nios2e.sopcinfo
└─ software/
├─ nios_write_test/
└─ nios_write_test_bsp/

BSPを生成

・BSPフォルダに移動

cd /cygdrive/d/workspace/cyc10/nios_hello_read/top_project/software/nios_read_test_bsp

・BSPを生成

nios2-bsp hal . \
  ../../nios2e.sopcinfo \
  --cpu-name nios2e \
  --set hal.enable_small_c_library true \
  --set hal.enable_reduced_device_drivers true \
  --set hal.max_file_descriptors 4

・コマンド nios2-bsp-editorでGUIで設定してもOKです。

アプリ側 Makefile 生成

・フォルダに移動します。

cd ../nios_write_test

・フォルダにコードを置きます。
・サンプルコードは、EPCQのセクターに対して、消去前データ確認⇒消去⇒消去後データ確認⇒書き込み⇒書き込み後データ確認を行うサンプルコードになります。

#include <stdio.h>
#include "system.h"
#include "io.h"
#include "alt_types.h"
#include "altera_epcq_controller2_regs.h"

/* ==== EPCQ Controller base addresses (system.h に記載) ==== */
#define EPCQ_CSR_BASE   EPCQ_CONTROLLER2_0_AVL_CSR_BASE
#define EPCQ_MEM_BASE   EPCQ_CONTROLLER2_0_AVL_MEM_BASE

/* ==== テスト設定 ==== */
#define TEST_SECTOR_OFFSET   0x00400000u   /* EPCQ 内物理オフセット (64KB境界) */
#define TEST_DATA_LEN        256          /* 書き込むバイト数 */

/* ---- プロトタイプ ---- */
static void wait_for_ready(void);
static void epcq_erase_sector(alt_u32 offset);
static void epcq_write_byte(alt_u32 offset, alt_u8 data);
static void dump_bytes(const char *title, alt_u32 offset, int len);
static int  verify_pattern(alt_u32 offset, int len);

/* ===================================================================== */
/* メイン                                                                  */
/* ===================================================================== */
int main(void)
{
    printf("===== EPCQ R/W TEST START =====\n");
    printf("CSR Base: 0x%08X\n", (unsigned int)EPCQ_CSR_BASE);
    printf("MEM Base: 0x%08X\n", (unsigned int)EPCQ_MEM_BASE);
    printf("Test offset: 0x%08X (mapped = 0x%08X)\n",
           (unsigned int)TEST_SECTOR_OFFSET,
           (unsigned int)(EPCQ_MEM_BASE + TEST_SECTOR_OFFSET));

    /* 事前状態をダンプ */
    dump_bytes("Before ERASE", TEST_SECTOR_OFFSET, 16);

    /* セクタ消去 */
    printf("\n-- Sector ERASE --\n");
    epcq_erase_sector(TEST_SECTOR_OFFSET);
    dump_bytes("After ERASE", TEST_SECTOR_OFFSET, 16);

    /* テストパターン書き込み (0x00,0x01,...0xFF) */
    printf("\n-- BYTE PROGRAM (0..255) --\n");
    for (alt_u32 i = 0; i < TEST_DATA_LEN; i++) {
        epcq_write_byte(TEST_SECTOR_OFFSET + i, (alt_u8)i);
    }

    /* 書き込み後の先頭16バイトダンプ */
    dump_bytes("After PROGRAM", TEST_SECTOR_OFFSET, 16);

    /* 全 256 バイトを検証 */
    int ok = verify_pattern(TEST_SECTOR_OFFSET, TEST_DATA_LEN);
    if (ok) {
        printf("\n[RESULT] Verify OK : pattern 0x00..0xFF matched.\n");
    } else {
        printf("\n[RESULT] Verify NG : data mismatch detected.\n");
    }

    printf("===== EPCQ R/W TEST END =====\n");
    return ok ? 0 : 1;
}

/* ===================================================================== */
/* EPCQ ビジー待ち                                                         */
/* ===================================================================== */
static void wait_for_ready(void)
{
    alt_u32 status;
    while (1) {
        status = IORD_32DIRECT(EPCQ_CSR_BASE,
                               ALTERA_EPCQ_CONTROLLER2_STATUS_REG);
        if ((status & ALTERA_EPCQ_CONTROLLER2_STATUS_WIP_MASK) == 0) {
            break;  /* WIPビットが0になれば完了 */
        }
        /* 適当にウェイト (CPU負荷軽減用、無くても動くが一応入れておく) */
        for (volatile int i = 0; i < 1000; i++) {
            /* nop */
        }
    }
}

/* ===================================================================== */
/* セクタ消去                                                              */
/*  offset : EPCQ内物理オフセット (0x10000境界推奨)                      */
/* ===================================================================== */
static void epcq_erase_sector(alt_u32 offset)
{
    /* 1. Write Enable */
    IOWR_32DIRECT(EPCQ_CSR_BASE,
                  ALTERA_EPCQ_CONTROLLER2_MEM_OP_REG,
                  ALTERA_EPCQ_CONTROLLER2_MEM_OP_WRITE_ENABLE_CMD);
    wait_for_ready();

    /* 2. Sector Erase
       MEM_OP レジスタの [31:8] にアドレス上位を詰める仕様なので、
       (offset >> 8) を 0xFFFFFF00 マスクで与える */
    IOWR_32DIRECT(EPCQ_CSR_BASE,
                  ALTERA_EPCQ_CONTROLLER2_MEM_OP_REG,
                  ALTERA_EPCQ_CONTROLLER2_MEM_OP_SECTOR_ERASE_CMD |
                  ((offset >> 8) & 0xFFFFFF00));
    wait_for_ready();
}

/* ===================================================================== */
/* 1バイト書き込み                                                         */
/*  offset : EPCQ内物理オフセット                                         */
/*  data   : 書き込む1バイト                                               */
/* ===================================================================== */
static void epcq_write_byte(alt_u32 offset, alt_u8 data)
{
    /* Write Enable */
    IOWR_32DIRECT(EPCQ_CSR_BASE,
                  ALTERA_EPCQ_CONTROLLER2_MEM_OP_REG,
                  ALTERA_EPCQ_CONTROLLER2_MEM_OP_WRITE_ENABLE_CMD);
    wait_for_ready();

    /* メモリマップ経由で1バイト書き込み */
    IOWR_8DIRECT(EPCQ_MEM_BASE + offset, 0, data);

    /* 書き込み完了待ち */
    wait_for_ready();
}

/* ===================================================================== */
/* 先頭 n バイトをダンプ                                                   */
/* ===================================================================== */
static void dump_bytes(const char *title, alt_u32 offset, int len)
{
    printf("\n%s (offset 0x%08X):\n", title, (unsigned int)offset);

    for (int i = 0; i < len; i++) {
        alt_u8 v = IORD_8DIRECT(EPCQ_MEM_BASE + offset + i, 0);
        printf("%02X ", (unsigned int)v);
    }
    printf("\n");
}

/* ===================================================================== */
/* 0x00,0x01,... のパターン検証                                           */
/* ===================================================================== */
static int verify_pattern(alt_u32 offset, int len)
{
    for (int i = 0; i < len; i++) {
        alt_u8 v = IORD_8DIRECT(EPCQ_MEM_BASE + offset + i, 0);
        if (v != (alt_u8)i) {
            printf("  MISMATCH at +0x%08X : read=0x%02X, expect=0x%02X\n",
                   offset + i, (unsigned int)v, (unsigned int)(alt_u8)i);
            return 0;
        }
    }
    return 1;
}

・Makefile を生成します

nios2-app-generate-makefile \
  --bsp-dir ../nios_write_test_bsp \
  --src-files nios_write_test.c

・その後makeでビルドすると、elfが生成されます。

elfをダウンロード

・jicを書いた後、電源を再投入しておきます。
・別のターミナルを「nios2-terminal」コマンドで立ち上げておきます。
・以下のコマンドで、正常に書き込めたら別ターミナルに結果が出力されます。

nios2-download -r -g nios_write_test.elf

・オプションの-rはリセット、-gは動作開始の意味です。2つつけるのがポイントです。

・以下のように表示されれば、消去⇒書き込み⇒読み出しが意図通りできたことがわかります。

===== EPCQ R/W TEST START =====
CSR Base: 0x10000000
MEM Base: 0x11000000
Test offset: 0x00400000 (mapped = 0x11400000)

Before ERASE (offset 0x00400000):
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

-- Sector ERASE --

After ERASE (offset 0x00400000):
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF

-- BYTE PROGRAM (0..255) --

After PROGRAM (offset 0x00400000):
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

[RESULT] Verify OK : pattern 0x00..0xFF matched.
===== EPCQ R/W TEST END =====

まとめ

・Cyclone 10 LP Evaluation Kit で EPCQ128A を Nios II から直接読み書きする仕組みを検証しました。
・Platform Designer で EPCQ Controller II を構成し、CSR/MEMアドレスを合わせて Nios II から制御できました。
・実際にセクタ消去 → 1バイト書き込み → 読み出し検証を行い、Flash のユーザ領域アクセスが正常に動作することを確認しました。
・EPCQ128A を FPGAコンフィグ兼ユーザデータ領域として扱えることがわかりました。今後は、FPGA書き換えなどをやっていこうと思います。

今日はここまで

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?