lynxeyedの電音鍵盤

MBDとFPGAと車載で使うデバイスの備忘録

RA8基板でOctal RAMと通信する(前編:Rawデータの送受信)

RA8

前回、基板を修正し、無事実装済み基板が到着しました。

RA8E1はSDRAMバスがないもののOSPIメモリを接続することができます。
FSP/RASCを使えば何も考えなくても一発動作...はなかなか難しいので地道にやっていきます。

(注) この記事を記述している時点で未知な部分が大いにあります。ご了承ください。
業務でHyperRAMを使用する際は(削除) こんなブログ読んでる暇あるなら (削除ここまで)RAMベンダーとルネサスの両社のFAEからサポートをもらった方が良いと思います。

とりあえず、今回はHyperRAMがマイコンと正しく結線されているか確認します。
下記の動作をクリアできれば少なくとも配線は間違っていないといえます。

方法は

  • 電源投入後からレジスタの固定値を読む(ベンダーIDなど)
  • ステータスなど値が変化するレジスタを読み書きして、期待した値や挙動になったか確認する
  • モリー空間のどこかに数バイトデータを書き、読み出せるか試す
  • 電源断 → 電源再投入して再現するか確認(電源断せずに続行すると前回の結果がRAMに残っているだけの場合がある)
主な特徴(括弧内はメリット)
  • 512Mbit(大容量)
  • xSPI octal Interface(xSPI Profile2.0準拠。Renesas RASC/FSPでの扱いがxSPI Profile2.0 Extended(いわゆるHyperBUS)に比べて楽)
  • 固定レイテンシ(設定が楽:デフォルト設定ならばLC x 2=14固定)
  • HYPERRAM 2.0(1.0より新しい。しらんけど)
デメリット

動作確認

まずデータシートから各コマンドを確認し、コマンドを投げて期待する結果がHyperRAMから来るか確認します。ここではospi_raw_transでHyperRAMとデータ送受信をしますが、実態はR_OSPI_B_DirectTransferのラッパーとして振る舞っているだけです。

#define HYPERRAM_BASE_ADDR ((void *)0x90000000U) /* Device on CS1 */
// COMMAND SET(infineon S80KS5123)
// #define <COMMAND> <CODE> <CA-DATA> | <ADDRESS(bytes)> | <Latency cycles> | <Data (bytes)>
#define OSPI_B_COMMAND_RESET_ENABLE (0x6666) // 8-0-0 | 0 | 0 | 0
#define OSPI_B_COMMAND_RESET (0x9999) // 8-0-0 | 0 | 0 | 0
#define OSPI_B_COMMAND_READ_ID (0x9F9F) // 8-8-8 | 0x00(4bytes) | 3-7 | (4bytes)
#define OSPI_B_COMMAND_POWER_DOWN (0xB9B9) // 8-0-0 | 0 | 0 | 0
#define OSPI_B_COMMAND_READ (0xEEEE) // 8-8-8 | (4bytes) | 3-7 | 1 to \infty
#define OSPI_B_COMMAND_WRITE (0xDEDE) // 8-8-8 | (4bytes) | 3-7 | 1 to \infty
#define OSPI_B_COMMAND_WRITE_ENABLE (0x0606) // 8-0-0 | 0 | 0 | 0
#define OSPI_B_COMMAND_WRITE_DISABLE (0x0404) // 8-0-0 | 0 | 0 | 0
#define OSPI_B_COMMAND_READ_REGISTER (0x6565) // 8-8-8 | (4bytes) | 3-7 | (2bytes)
#define OSPI_B_COMMAND_WRITE_REGISTER (0x7171) // 8-8-8 | (4bytes) | 0 | (2bytes)
fsp_err_t ospi_raw_trans(spi_flash_direct_transfer_t *p_trans,
 uint32_t command, uint8_t cmd_len,
 uint32_t address, uint8_t addr_len,
 uint32_t data, uint8_t data_len,
 uint8_t dummy_cycle, spi_flash_direct_transfer_dir_t dir)
{
 fsp_err_t err = FSP_SUCCESS;
 // Example raw transfer
 p_trans->command = command;
 p_trans->command_length = cmd_len;
 p_trans->address = address;
 p_trans->address_length = addr_len;
 p_trans->data_length = data_len;
 p_trans->data = data;
 p_trans->dummy_cycles = dummy_cycle; // The configurable latency cycle should be set as minus 1 when Profile 2.0
 err = R_OSPI_B_DirectTransfer(&g_ospi0_ctrl, p_trans, dir);
 return err;
}

固定値をリードする

HyperRAM S80KS5123のID0とID1は固定値になっています。(データシートの6.2 Device identification registersを参照)
ID0の下位4bitは製造メーカ0x06(= infineon)固定となっています。Die0をリードすれば0x0F96が読めます。
またID1はデバイスタイプで0x01 (= HYPERRAM 2.0)固定です。
読んでみましょう。Read Registerコマンド(0x65)でリードしてもいいですが、ここはRead IDコマンド(0x9F)でID0,1を同時に取得してみます。

 // read ID0/ID1
 err = ospi_raw_trans(&g_ospi0_trans,
 OSPI_B_COMMAND_READ_ID, 2,
 0x00000000, 4,
 0, 4,
 15, SPI_FLASH_DIRECT_TRANSFER_DIR_READ);
 if (FSP_SUCCESS != err)
 {
 xprintf("[OSPI] direct transfer error!\n");
 return;
 }
 xprintf("ID0/ID1=0x%08x\n", g_ospi0_trans.data);
結果

下記のようになります。

ID0/ID1=0x0100960f
エンディアンの問題

RenesasのOSPIの設計上というか、ARMバスの仕様で、メモリ空間を読み出す際はリトルエンディアンになります。infineonのHyperRAMレジスタはビッグエンディアンで読み書きされます。というわけで、バイト単位でシャッフルされているように見えます。
正しくは

ID0/ID1=0x0F96_0001

です。いまのところレジスタのRWはあまりしないと思うので、手を加えないことにします。

メモリ空間も読み書きする

メモリ空間をライトする場合はWrite Enableコマンドを最初に発行します。またレジスタをライトする場合は都度Write Enableを発行することになります。

// write enable
 err = ospi_raw_trans(&g_ospi0_trans,
 OSPI_B_COMMAND_WRITE_ENABLE, 2,
 0x00000000, 0,
 0, 0,
 0, SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE);
 if (FSP_SUCCESS != err)
 {
 xprintf("[OSPI] direct transfer error!\n");
 return;
 }
 // write memory
 err = ospi_raw_trans(&g_ospi0_trans,
 OSPI_B_COMMAND_WRITE, 2,
 0x00000080, 4,
 0xDEADBEEF, 4,
 15, SPI_FLASH_DIRECT_TRANSFER_DIR_WRITE);
 if (FSP_SUCCESS != err)
 {
 xprintf("[OSPI] direct transfer error!\n");
 return;
 }
 // read CR0
 // 余計なコマンドを途中に挿入して、「ライト内容が送受信バッファに残っていて、記録できていないのに偶々リードだけうまくいってしまったバグ」の回避
 err = ospi_raw_trans(&g_ospi0_trans,
 OSPI_B_COMMAND_READ_REGISTER, 2,
 0x00000004, 4,
 0x00, 2,
 15, SPI_FLASH_DIRECT_TRANSFER_DIR_READ);
 if (FSP_SUCCESS != err)
 {
 xprintf("[OSPI] direct transfer error!\n");
 return;
 }
 xprintf("CR0=0x%04x\n", g_ospi0_trans.data);
 // read memory
 err = ospi_raw_trans(&g_ospi0_trans,
 OSPI_B_COMMAND_READ, 2,
 0x00000080, 4,
 0x00, 4,
 15, SPI_FLASH_DIRECT_TRANSFER_DIR_READ);
 if (FSP_SUCCESS != err)
 {
 xprintf("[OSPI] direct transfer error!\n");
 return;
 }
 xprintf("Data=0x%08x\n", g_ospi0_trans.data);
結果
CR0=0x2f8f <- ダミー読み出し
Data=0xdeadbeef <- 欲しいデータ

今回使用したプロジェクト

git clone http://github.com/panda5mt/RA8E1_prj -b HYPERRAM_TRIAL2 --depth 1 

次回

後編ではRA8E1メモリマップ空間にHyperRAMメモリを展開します。また今回実装していない、AutoCalibrateも実装予定です。

あとがき

HYPERRAMといわれているもの、種類が多すぎて*2この先どこまで生き残るのか、この先もデファクトになるのか謎な感じもします。カオスになってる

*1 :S27KL0641はNRNDデバイスなんですけど....

*2 :hyperbusやOctal SPIバスが混在しているし、デバイスによりステータスなどの取得方法が異なる

RA8E1基板を作る(その3:エラー修正)

RA8

間違い探し

いろいろ出てきた問題を修正した基板を2週間前に出していたのですが、仕上がったようです。
VCC_DCDCやそもそもVCL部分のバターンが激細になっていたものを直しました。

実装メーカから写真が来ました。



前回の基板と間違い探しゲームができそうなくらいしか変更はありません。

あとHyperRAMについては謎のフォースが働き、別デバイスになっております。
ちゃんと動くかなぁ。HyperRAMがこの基板の存在意義なので動いてほしいところ

RA8E1をEthernetにつなげる(その2:UDP通信)

RA8

前回、LAN8720A PHYの物理的接続とEthernetフレームの送受信を確認しました。
今回はLwIPで高レイヤーの通信を行ないたいと思います。

LwIPを使う際のFSP設定

といってもRASC(FSP)ですでにLwIPはポートされており、必要なものをメニューで選択してほぼ終わりです。
こんな記事書かなくて(見なくて)いいよね、と思うくらいには楽に作成できます。
下記のような構成を取ると思います。

気をつけるべき事は出てきたエラーを消す設定をすること、動作させるFreeRTOSスレッドの後方互換性をEnableにしておくことくらいでしょうか。

  • 動作予定のFreeRTOSスレッド内にて
    • Enable Backward Compatibility=Enabled
  • g_ether0の割り込み要因EESRの下記イベントをONにする
    • RFOF,RDE,FR,TC

その他必要な設定もする

ソースコード

UDP ペイロードを用意し、ポート9000番でブロードキャスト送信します。
DHCPサーバに接続されている前提ですが、PC-基板間のクロス接続であったり、DHCPが存在していないor動かしてはいけない場合があるのでAutoIPも動かしています。DHCP待機タイムアウトは20秒です。

#include "lwip/init.h"
#include "lwip/netif.h"
#include "lwip/timeouts.h"
#include "lwip/udp.h"
#include "lwip/dhcp.h"
#include "lwip/autoip.h"
#define UDP_PORT_DEST 9000
void main_thread1_entry(void *pvParameters)
{
 FSP_PARAMETER_NOT_USED(pvParameters);
 // LAN8720A Reset
 R_BSP_PinAccessEnable();
 R_BSP_PinWrite(LAN8720_nRST, BSP_IO_LEVEL_LOW);
 vTaskDelay(pdMS_TO_TICKS(300));
 R_BSP_PinWrite(LAN8720_nRST, BSP_IO_LEVEL_HIGH);
 vTaskDelay(pdMS_TO_TICKS(300));
 xprintf("[ETH] LAN8720A Ready\n");
 struct netif netif;
 ip_addr_t ipaddr;
 ip_addr_t netmask;
 ip_addr_t gw;
 IP_ADDR4(&ipaddr, 0, 0, 0, 0); // IPADDR_ANY
 IP_ADDR4(&netmask, 0, 0, 0, 0); // IPADDR_ANY
 IP_ADDR4(&gw, 0, 0, 0, 0); // IPADDR_ANY
 lwip_init();
 netif_add(&netif, &ipaddr, &netmask, &gw, &g_lwip_ether0_instance, rm_lwip_ether_init, netif_input);
 netif_set_default(&netif);
 netif_set_up(&netif);
 netif_set_link_up(&netif);
 dhcp_start(&netif);
 // DHCP待機
 for (int i = 0; i < 2000; i++)
 {
 sys_check_timeouts();
 if (netif.ip_addr.addr != 0)
 {
 xprintf("[LwIP] DHCP assigned IP: %s\n", ip4addr_ntoa(&netif.ip_addr));
 break;
 }
 vTaskDelay(pdMS_TO_TICKS(10));
 }
 // while (netif.ip_addr.addr == 0)
 // ;
 // xprintf("[LwIP] DHCP assigned IP1: %s\n", ip4addr_ntoa(&netif.ip_addr));
 // if DHCP is not valid, AUTOIP will Start
 if (netif.ip_addr.addr == 0)
 {
 xprintf("[LwIP] DHCP failed. Using AutoIP.\n");
 autoip_start(&netif);
 while (netif.ip_addr.addr == 0)
 {
 sys_check_timeouts();
 vTaskDelay(pdMS_TO_TICKS(100));
 }
 xprintf("[LwIP] AutoIP assigned IP: %s\n", ip4addr_ntoa(&netif.ip_addr));
 }
 // UDP通信準備
 const char *message = "Hello from RA8E1 UDP";
 struct udp_pcb *pcb = udp_new();
 if (!pcb)
 {
 xprintf("[UDP] udp_new failed\n");
 return;
 }
 ip_addr_t broadcast_ip;
 broadcast_ip.addr = (netif.ip_addr.addr & netif.netmask.addr) | ~netif.netmask.addr;
 for (int i = 0; i < 100; i++)
 {
 struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, strlen(message), PBUF_RAM);
 if (!p)
 {
 xprintf("[UDP] pbuf_alloc failed\n");
 break;
 }
 memcpy(p->payload, message, strlen(message));
 err_t err = udp_sendto(pcb, p, &broadcast_ip, UDP_PORT_DEST);
 if (err == ERR_OK)
 {
 xprintf("[UDP] #%d sent OK\n", i + 1);
 }
 else
 {
 xprintf("[UDP] #%d send failed: %d\n", i + 1, err);
 }
 pbuf_free(p);
 vTaskDelay(pdMS_TO_TICKS(20));
 }
 udp_remove(pcb);
 while (1)
 {
 vTaskDelay(pdMS_TO_TICKS(1000));
 }
}

Wiresharkで確認

ちゃんとIPを取得できているようです。コンソールからも確認しました。

START
[ETH] LAN8720A Ready
[LwIP] DHCP assigned IP: 192.168.10.101
[UDP] #1 sent OK
[UDP] #2 sent OK
[UDP] #3 sent OK
[UDP] #4 sent OK
[UDP] #5 sent OK
[UDP] #6 sent OK
[UDP] #7 sent OK
[UDP] #8 sent OK
......

タイムアウトはそれぞれの環境で設定すればいいのではないかと思います。タイムアウト後はリンクローカルアドレスが割り振られます。

プロジェクト全体

[フレーム]github.com

git clone http://github.com/panda5mt/RA8E1_prj -b RA8E1_UDP --depth 1 

これから

あとは本丸、hyperRAMをしばくのみ.....

RA8E1をEthernetにつなげる(その1:PHY LAN8720Aの疎通確認)

RA8

前回USB-CDCからprintfができるようになりました。
デバッグできる環境が整ってきたのでEthernet PHYも使えるようにしていきます。

長い記事になってしまったので30秒で読める要約をここに書きます

  • LAN8720A使った
  • PHYからリファレンスクロック50MHz(REF50CLK)が出力されるならRASC(FSP)の設定はReference clock=Disabled
  • イーサーネット送信フレーム長が極端に短い場合、実際には完了しているのに送信完了割り込みが発生しないことがある

おわり。

PHYの接続確認

今回はPHYにLAN8720Aを使用しています。

  • メリット
    • 安価でピン数が少ない
    • RMII専用なので切り替え設定が不要
    • ピンのプルアップダウンで設定が完了(初期化コマンドいらない)
    • リセット後即動作できる
    • クロック源の選択(25MHzクリスタル or 50MHz発振器)ができる
  • デメリット
    • リセットをちゃんとしないと動作しない事がある(データシート参照)
    • MIIとの切り替えはできない
    • 複数個(3個以上)の使用にはあまり向いてない
    • クロック源の選択が面倒

今回の用途には最適です。デメリットは今回ほぼありません。MII要らないし、複数個使わないし。

ハードウェアの留意点

ソフトでの初期化は要りません。その代わり、ハードウェアのピンストラップを正しく行なう必要があります。*1
なお、今回のLAN8720Aのピン設定は下記です。よくあるLAN8720A中華PHYボードとは若干設定や配線が違うのでご注意ください。
MODE[2:0] = b'111です。10BASE-T/100BASE-TX,全二重通信,オートネゴシエーション対応です。

また、PHYAD0をプルダウンしているのでPHY-LSI Address = 0x00となります。
また下記の配線により内蔵1.2VレギュレータON,25MHzクリスタルモード(リファレンスクロックピン:REFCLKO=50MHz出力モード)になります。
ここが唯一めんどくさいところ

(注記) RJ45に接続されている2つのLEDのカソードは両方ともGNDへ

ソフトウェアの留意点

RASC(FSP)を使ってRA8E1と接続設定します。
簡単設定なのですが、DMAと割り込みを使うのでそれなりに注意も必要です。
50MHzリファレンスクロック(REF50CLK)はPHYから供給されるのでDisable

User own PHY

現在使用しているFSP v.5.9.0は下記のPHYターゲット初期化コードが整備されています。

  • KSZ8091RNB
  • KSZ8041
  • DP83620
  • ICS1894

残念ながらLAN8720Aはリストにありません

  • User own PHY

を選びます。初期化コードは用意されません。
もちろんLAN8720Aは無くても動きますが予期せぬリセットなどで必要になるかもしれないので、用意しておいた方が無難です。
初期化APIを自前で用意したら下記項目に関数名を記述しておきます。

  • Port Custom Initfunction
  • Port Custom Link Partner Abirity Get Function


これらの関数の詳細は下記です。

初期化コードether_phy_target_lan8720a_initialize()

初期化コードは無くても動きますが、一応割り込みレジスタとBasic Statusレジスタを空読みします。
割り込みフラグや不確定要因で1になってしまっている予期せぬフラグをクリアするためです(Clear on read)

リンクアビリティとは10M/100M/(1G)等通信速度と半/全二重通信の切り替え可能性のようです。
他のRMII-PHYのコード見てもオートネゴシエーション対応デバイスは何もせずにtrueを返しているのでそれに倣います。MODE[2:0] = b'111に設定しているので問題は無いでしょう。知らんけど。

#define ETHER_PHY_REG_BASIC_CONTROL (0)
#define ETHER_PHY_REG_BASIC_STATUS (1)
#define ETHER_PHY_REG_PHY_ID_1 (2)
#define ETHER_PHY_REG_PHY_ID_2 (3)
#define ETHER_PHY_REG_INTERRUPT_FLAG (29)
#define ETHER_PHY_REG_INTERRUPT_MASK (30)
// ....
void ether_phy_target_lan8720a_initialize(ether_phy_instance_ctrl_t *p_instance_ctrl)
{
 uint32_t reg = 0x00;
 R_ETHER_PHY_Read(p_instance_ctrl, ETHER_PHY_REG_BASIC_STATUS, &reg); // read basic status reg
 R_ETHER_PHY_Read(p_instance_ctrl, ETHER_PHY_REG_INTERRUPT_FLAG, &reg); // clear interrupt
} /* End of function ether_phy_targets_initialize() */
bool ether_phy_target_lan8720_is_support_link_partner_ability(ether_phy_instance_ctrl_t *p_instance_ctrl,
 uint32_t line_speed_duplex)
{
 FSP_PARAMETER_NOT_USED(p_instance_ctrl);
 FSP_PARAMETER_NOT_USED(line_speed_duplex);
 /* This PHY-LSI supports half and full duplex mode. */
 return true;
} /* End of function ether_phy_targets_is_support_link_partner_ability() */

配線MDIO/MDCが接続されているか確認のため、ユーザーコード側で下記を入れてもいいと思います。
LAN8720Aの場合PHY_ID1レジスタ(0x02)をリードすると必ず0x07(Microchip社のOUI:Orgenization Unique Identifier)が読めます。

 uint32_t reg = 0;
 R_ETHER_PHY_Read(&g_ether_phy0_ctrl, 0x02, &reg);
 
 if(reg == 0x07) // PHY_ID1=7 : Microchip
 return true; 
 else 
 return false; 

もし、0xFF,0x00など異なる値が読めた場合、MDIO/MDC/REF50CLKのいずれかに問題があります。
多くの場合、配線ミスか、FSP設定r_ether_phy->Reference ClockEnabledになっているのが原因です。Disabledにして再度確認。
逆に言うとここで正しい値が読めれば目の前はゴールです。あとすこし。

イーサーネットフレームを送信・受信する

参考になるのは公式のサンプル
[フレーム]renesas.github.io
の下の方にサンプルがあります。
(注記)ソースコードのコメントを見る限り、このサンプルの記法はちょっと古いらしく、直す必要があります。
少し調査をしてソースコードをなおします。

割り込み

本格的に使うには割り込みはほぼ必須でしょう。
FSPのPHY interruptの項目を見るとEESR,EECRとあります。この項目をクリックしさらに詳細を見ると難解な何らかの略称とそれを有効化するチェックマークが出現します。意味不明なのでこの項目の詳細を調べるためデータシートを見ます。

RA8E1グループユーザーズマニュアル ハードウェア編の28.2.6 EESR:ETHERC/EDMACステータスレジスタの項目です。
https://www.renesas.com/ja/document/mah/ra8e1-group-users-manual-hardware?r=25566640

割り込みはEDMACによりイーサーネットフレームの送信(TC)と受信が完了(FR)したときに発生させたいのでEESRTCおよびFRにチェックを入れておきます。

改修したソースコード

MACアドレスはAA:BB:CC:DD:EE:FFにしています。

#define ETHER_EXAMPLE_MAXIMUM_ETHERNET_FRAME_SIZE (1514)
#define ETHER_EXAMPLE_TRANSMIT_ETHERNET_FRAME_SIZE (1514)
#define ETHER_EXAMPLE_SOURCE_MAC_ADDRESS 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
#define ETHER_EXAMPLE_DESTINATION_MAC_ADDRESS 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
#define ETHER_EXAMPLE_FRAME_TYPE 0x00, 0x2E
#define ETHER_EXAMPLE_PAYLOAD 'I', '\'', 'm', ' ', 'R', 'A', '8', 'E', '1', '.', \
' ', 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', \
'l', 'd', '!', '!', ' ', 'H', 'E', 'L', 'L', 'O', \
' ', 'W', 'O', 'R', 'L', 'D', '!', ' ', 'R', 'e', \
'n', 'e', 's', 'a', 's'
#define ETHER_EXAMPLE_FLAG_ON (1U)
#define ETHER_EXAMPLE_FLAG_OFF (0U)
#define ETHER_EXAMPLE_ALIGNMENT_32_BYTE (32)
static volatile uint32_t g_example_receive_complete = 0;
static volatile uint32_t g_example_transfer_complete = 0;
static volatile uint32_t g_example_link_on = 0;
#define PHY_BCR_RESET (1 << 15)
#define PHY_BCR_AUTONEGO_EN (1 << 12)
#define PHY_BCR_RESTART_AUTONEGO (1 << 9)
__attribute__((aligned(32))) uint8_t gp_send_data_internal[ETHER_EXAMPLE_TRANSMIT_ETHERNET_FRAME_SIZE] = {
 ETHER_EXAMPLE_DESTINATION_MAC_ADDRESS, /* Destination MAC address */
 ETHER_EXAMPLE_SOURCE_MAC_ADDRESS, /* Source MAC address */
 ETHER_EXAMPLE_FRAME_TYPE, /* Type field */
 ETHER_EXAMPLE_PAYLOAD /* Payload value (46byte) */
 // after bytes, filled with zeros
};
void ether_example_callback(ether_callback_args_t *p_args)
{
 switch (p_args->event)
 {
 case ETHER_EVENT_TX_COMPLETE:
 // xprintf("[ISR] TX COMPLETE.\n");
 g_example_transfer_complete = 1;
 break;
 case ETHER_EVENT_RX_COMPLETE:
 // xprintf("[ISR] RX COMPLETE.\n");
 g_example_receive_complete = 1;
 break;
 case ETHER_EVENT_LINK_ON:
 // xprintf("[ISR] LINK ON.\n");
 g_example_link_on = 1;
 break;
 case ETHER_EVENT_LINK_OFF:
 // xprintf("[ISR] LINK OFF.\n");
 g_example_link_on = 0;
 break;
 default:
 xprintf("[ISR] Event: %d\n", p_args->event);
 break;
 }
}
/* Main Thread1 entry function */
/* pvParameters contains TaskHandle_t */
void main_thread1_entry(void *pvParameters)
{
 FSP_PARAMETER_NOT_USED(pvParameters);
 // START:LAN8720A Reset
 R_BSP_PinAccessEnable();
 R_BSP_PinWrite(LAN8720_nRST, BSP_IO_LEVEL_LOW); // Reset LAN8720
 xprintf("GPIO = L\n");
 vTaskDelay(pdMS_TO_TICKS(300));
 R_BSP_PinWrite(LAN8720_nRST, BSP_IO_LEVEL_HIGH); // Start LAN8720
 xprintf("GPIO = H\n");
 vTaskDelay(pdMS_TO_TICKS(300));
 // END:LAN8720A Reset
 fsp_err_t err = FSP_SUCCESS;
 err = R_ETHER_Open(&g_ether0_ctrl, &g_ether0_cfg);
 assert(FSP_SUCCESS == err);
 xprintf("[ETH] OPEN.\n");
 // Check Link ON
 g_example_link_on = 0;
 do
 {
 err = R_ETHER_LinkProcess(&g_ether0_ctrl);
 if (err == FSP_SUCCESS || g_example_link_on == 1)
 {
 g_example_link_on = 1;
 break;
 }
 } while (g_example_link_on != 1);
 xprintf("LINK ON\n");
 g_example_transfer_complete = 0;
 /* Set user buffer to TX descriptor and enable transmission. */
 err = R_ETHER_Write(&g_ether0_ctrl, (void *)gp_send_data_internal, sizeof(gp_send_data_internal));
 if (FSP_SUCCESS == err)
 {
 /* Wait for the transmission to complete. */
 /* Data array should not change in zero copy mode until transfer complete. */
 while (ETHER_EXAMPLE_FLAG_ON != g_example_transfer_complete)
 {
 ;
 }
 }
 xprintf("[ETH]Write OK!\n");
 /* Get receive buffer from RX descriptor. */
 static uint8_t *p_read_buffer_nocopy;
 uint32_t read_data_size = 0;
 g_example_receive_complete = 0;
 err = R_ETHER_Read(&g_ether0_ctrl, (void *)&p_read_buffer_nocopy, &read_data_size);
 xprintf("[ETH] RCV result:%d\n", err);
 assert(FSP_SUCCESS == err);
 /* Process received data here */
 if (FSP_SUCCESS == err)
 {
 /* Wait for the transmission to complete. */
 /* Data array should not change in zero copy mode until transfer complete. */
 while (ETHER_EXAMPLE_FLAG_ON != g_example_receive_complete)
 {
 ;
 }
 }
 vTaskDelay(pdMS_TO_TICKS(1));
 xprintf("[ETH]RCV OK!\n");
 /* Release receive buffer to RX descriptor. */
 err = R_ETHER_BufferRelease(&g_ether0_ctrl);
 assert(FSP_SUCCESS == err);
 // /* Disable transmission and receive function and close the ether instance. */
 R_ETHER_Close(&g_ether0_ctrl);
 xprintf("[ETH]Close.\n");
 while (1)
 {
 vTaskDelay(pdMS_TO_TICKS(1000));
 }
}

留意点

  • Ethernetフレームは送信が完了していて、Wireshark等でも確認ができているのに割り込みが発生しないことがある

少なめのバイト数だと割り込みが発生しないことが高確率で起こります。この件は引き続き調査中。いまはMAXの1514バイトに固定してあります。

Wiresharkで確認

今回のプロジェクト

[フレーム]github.com

git clone http://github.com/panda5mt/RA8E1_prj -b RA8E1_PHY --depth 1 

*1 :なおソフトウェアでハードウェア初期設定を書き換えることは可能です。詳しくはLAN8720AデータシートのBASIC CONTROL REGISTERの項目をご覧ください

RA8E1基板の動作確認(基板修正とUSB-CDCの実装)

RA8

到着したので早速触っていきます。
RA8触り始めてまだ日が浅いのに見切り発車で基板を起こしてしまったので不安しかない。

検品

ハードウェア要修正部分の洗い出し

配線を間違っていたり、間違っていなくとも別の部品を使用する方が良かったり、そういう問題点を洗い出しておきます。

  • VCC_DCDC

VCC_DCDCとVCCは短絡する必要があります。普通にわすれていて電源投入後まったく動きませんでした。次回の製造依頼の時に直しましょう。
https://www.renesas.com/ja/document/apn/25539431

  • P201/MDピン

MDピンはBootloader突入契機になるピンです。Reset時にLowにしておくとBootloader待機モードになります。
シーケンスをざっと見ていてプッシュスイッチを採用したのですが、ジャンパーピンで対応した方がよさそうです。
MD=Lowのままでも書き込み完了まで終わります。プッシュスイッチだと、書き込みが始まるまで押し続ける必要があり何かと不便です。
https://www.renesas.com/ja/document/apn/renesas-boot-firmware-ra8e1-mcu-group?r=25566640
上記PDFの4.4.3が該当します。

MD=Lowの状態でリセット後、ホストから0x00を3回受信すると、0x00(ACK)をホストに返信し、通信初期化が完了します。この後はMD=Lowである必要はなくなります。逆に、必ずしもHighにする必要もないようです。
この後ホストが0x55(Generic Code)を送信すると0xC6(Boot code)をホストに返信します。
この後コマンド受信フェーズに移行し、バイナリ書き込み、イレースコマンドなどを受け付けるようになります。

修正基板


大改修じゃなくて良かった...

開発に向けての修正

ソフトウェア/ファームウェアの修正など

  • SRECファイル

VSCodeで開発しているのですがelfファイルとintelHexファイルが生成されます。
Renesas Flash Programmerを使用する場合SRECファイルが必要だと思われます。*1
hexファイルの解釈がちょっと違う模様。
GeneratedSrc.cmakeのadd_custom_targetセクションを下記のように修正しました。

add_custom_target(obj_copy ALL
 COMMAND ${CMAKE_OBJCOPY} ${PROJECT_NAME}.elf -O ihex ${PROJECT_NAME}.ihex
 COMMAND ${CMAKE_OBJCOPY} ${PROJECT_NAME}.elf -O srec -j .text -j .data ${PROJECT_NAME}.srec
 COMMENT "Creating Intel Hex file and SREC file in ${PROJECT_BINARY_DIR}"
)
  • printf周り

FPB-RA8E1はオンボードJ-LinkデバッガがUSB-UARTの役割も担っていました。そのためRA8のSCI9にアクセスするとUSBシリアルとして振舞ってくれました。今回はそういうものはありません。RA8E1自体にUSB-CDC(USBシリアル機能)を実装した方が今後は便利だと思います。
RASCを使えばあまり苦労せず実装できますが、FreeRTOSベースで複数のスレッドからprintfへアクセスする場合はちょっと大変そうだな...とネットを徘徊したところ、こんなものを発見。

https://community.renesas.com/mcu/ra/f/forum/18983/freertos-with-usb-pcdc-driver-on-r_usb_pcdc

RA4用ですが、FSPは全RAシリーズと互換性があるので問題なし。
USB-CDC用に専用スレッドを用意し、別スレッドから1文字をQueueで送るようにしています。

void putchar_ra8usb(uint8_t c)
{
 static bool skip = false;
 if (skip == true)
 return;
 uint8_t p[2];
 p[0] = c;
 p[1] = '0円';
 // skip if usb is not active
 if (xQueueSend(xQueueMes, p, pdMS_TO_TICKS(1000)) != pdPASS)
 {
 skip = true;
 }
 return;
}

xprintfの出力関数として結合したいので、最初に

xdev_out(putchar_ra8usb)

とすればOK。
ただ、ターミナルが接続されていない場合に動かなくなってしまうため、タイムアウトを設定しています。
もう少しまともな実装を考えられればいいけど、取り急ぎの実装です。

コード

USB-CDC経由でprintfされる以外の変更はありません。カメラのピクセルデータを16進で垂れ流します。
[フレーム]github.com

git clone http://github.com/panda5mt/RA8E1_prj -b RA8E1_USB_CDC_2 --depth 1 

次回

IwIPやhyperRAMの動作確認を行ないます。

*1 :hexの場合書き込みに失敗してしまいます

Renesas RA8E1基板実装が完了した

RA8

実装会社から完了の報告と写真が来ました。
確認はGW明けになりそうですが。

特にフットプリントミスもなさそうです。

これから

UDP通信部分とhyperRAMのRW挙動を確認します。
特に現時点でRA8のOSPIコントローラにhyperRAMを接続した際の挙動は未知数なので探っていかなければなりません。

ARM64 Linux SBCでRenesas RA8E1のコンパイルをする

RA8

前回まで,サンプルコードの作成や基板の設計をしました.
コンパイラはx64マシン上でE2StudioやVSCodeなりで十分だとは思うのですが,
最近は高性能なSBCがそろってる事,オンサイトでのコンパイルが必要な場面もある事を踏まえ記述しています.
逐次的なチュートリアルを書くのでは無く,留意した点だけ書き留めます.

ネックとなるのが

  • LLVM-Clang
  • RASCの呼び出しを止める

くらいでしょうか.ARM-Heliumを使いたいためコンパイラllvm-clangになります.でもあまりハードルは高くありません.
スクリプトの変更はほんの数行です.頻繁にSBCでのコンパイルを行うようであれば,フラグやスイッチを記述するといいかもしれません.
なお,操作は遠隔でVSCodeからSSHでSBCに入る前提ですのであしからず.

前提条件

プロジェクト
  • RASCが生成したVSCodeプロジェクトが完成していること

x86マシンのWIn/Linux環境またはMacVScodeコンパイルできる状態になっていれば問題ないはずです.

SBC
  • Radxa ROCK5(LPDDR4X 32GB)

[フレーム]radxa.com

32GB必須というわけではありません.4~8GB以上あればいいのでは?
ラズパイでもそこまで違いは無いと思います.

コンソール

LLVM導入

  • LLVM Embedded Toolchain for Arm

ルネサスが指定しているバージョンは v18.1.3
です。

参考:
https://www.renesas.com/en/document/apn/high-performance-ra8-mcu-using-arm-cortex-m85-core-helium?r=25566640

今後、FSPのバージョンが上がり、対応ツールチェーンが変わった場合は、都度指定されたバージョンに読み替えてください。
この記事を書いている現時点でも上記ツールチェーン最新バージョンは19.1.5です。最新かに関わりなくFSPが指定しているツールチェーンを導入します。

導入方法は下記から
https://learn.arm.com/install-guides/llvm-embedded/

スクリプト手直し等

  1. RASCを呼び出さない様にGeneratedSrc.cmakeのPre-build、Post-buildステップをコメントアウトします。
  2. 前節で導入ずみのLLVM コンパイラのPATHを絶対パスで記述します.llvm.cmakeset(ARM_TOOLCHAIN_PATH "/your/llvm-toolchain/dir/")を修正します。
  3. VSCode ExtensionのRenesas Build Utilitiesを導入しておきます

コンパイル

[F7]キーで実行。
コンパイルが成功するとbuild/ディレクトリにバイナリが生成されます.

おまけ:CLIでビルドする

Raspberry Pi 3A+などRAMがかなり限られているSBCの場合,VSCodeの使用は,バックグラウンドで動作するデーモンがそこそこあるようで快適とは到底言いがた場合があります.CLIだとスムーズにビルドできる可能性があります.下記に例を示します./your/project/dir/は適宜読み替えてください.

cd RA8E1_prj/ # プロジェクトのディレクトリトップへ
rm -rf build/ # build/ディレクトリ一旦削除
mkdir build # 再作成
cd build/
# ビルド環境構築
cmake -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -DCMAKE_TOOLCHAIN_FILE:FILEPATH=/your/project/dir/RA8E1_prj/cmake/llvm.cmake -DARM_TOOLCHAIN_PATH:STRING= --no-warn-unused-cli -S/your/project/dir/RA8E1_prj -B/your/project/dir/RA8E1_prj/build/Debug -G "Unix Makefiles"
# ビルド
cmake --build /your/project/dir/RA8E1_prj/build/Debug --config Debug --target all --

上記コマンドの場合,build/DebugフォルダにelfやインテルHEXが生成されます.

引用をストックしました

引用するにはまずログインしてください

引用をストックできませんでした。再度お試しください

限定公開記事のため引用できません。

読者です 読者をやめる 読者になる 読者になる

AltStyle によって変換されたページ (->オリジナル) /