概要
はじめに
Microchip社のPolarFire® SoCは、不揮発性で低消費電力のミッドレンジSoC FPGAで、64ビットのRISC-V ISAを5コア搭載したプロセッサーと低消費電力FPGAを組み合わせたチップです。
この記事は、私自身の備忘録も兼ねて、PolarFire® SoCのMSS(Microprocessor Subsystem)における各種ペリフェラルの活用を目指して執筆しています。試行錯誤しながら実験を進めているため、内容に不備や不足がある可能性もございます。その際は、何卒ご容赦いただけますと幸いです。

改定履歴
| 公開/変更日 | 変更内容 |
| 25/1/31 | 初版 メモリ共有の方法 |
| 25/6/11 | タイトル修正 |
外部リンク
| タイトル | リンク |
| PolarFire SoCプロダクトページ (Microchip) | PolarFire® SoC FPGAs | Microchip Technology |
| PolarFire SoC Discoveryキット (Microchip) | PolarFire® SoC Discovery Kit | Microchip Technology |
| PolarFire SoC MSS テクニカルリファレンスマニュアル | PolarFire SoC MSS Technical Reference Manual (microchip.com) |
| Microprocessor Subsystem (MSS) User’s Guide | PolarFire SoC MSS Technical Reference Manual (microchip.com) |
| PolarFire SoCプロダクトページ (GitHub) | PolarFire-SoC · GitHub |
| ベアメタルプロジェクト (Github) | GitHub – polarfire-soc/polarfire-soc-bare-metal-examples: Bare metal example software projects for PolarFire SoC |
| GPIO Bare Metal Driver (Github) | polarfire-soc-documentation/bare-metal-embedded-software/bare-metal-driver-user-guides/polarfire-soc-mss-driver-user-guides/mss-gpio/mss-gpio-driver-user-guide.md at master · polarfire-soc/polarfire-soc-documentation · GitHub |
3.構成
前回の記事と同じく、PolarFire® SoC Discovery Kitを使用し、動作を確認していきます。
PolarFire® SoC Discovery Kit | Microchip Technology

1.コア間のメモリ共有方法
0.はじめに
ベースプロジェクトは前回の記事と同様に「mpfs-gpio-interrupt」を使用します。
1.リンカースクリプトの設定
①リンカースクリプトを開きます

②メモリは以下の様に割り当てられております。
| 領域名 | 先頭アドレス | サイズ | 説明 |
| envm | 0x20220100 | 128k – 0x100 | envm(Embedded Non-Volatile Memory)は不揮発性メモリで、通常プログラムコード(ファームウェア)が格納される領域です。 |
| switch_code_dtim | 0x01001C00 | 1k | クロック切り替え時に使用されるコードを配置するため |
| dtim | 0x01000000 | 7k | データ用のメモリ領域で、データまたはスタック領域として使用されます。 |
| e51_itim | 0x01800000 | 28k | E51コア用の命令およびデータメモリ(ITIM: Instruction Tightly Integrated Memory) |
| u54_x_itim | 0x01808000, 0x01810000, 0x01818000, 0x01820000 | 28k | 各U54コアに割り当てられたITIM |
| l2lim | 0x08000000 | 512k | L2キャッシュを統合した共有メモリ領域で、データや共有メモリとして利用されます |
| scratchpad | 0x0A000000 | 512k | 汎用のオンチップRAM |
| ddr_cached_32bit | 0x80000000 | 128M | 高速アクセスが求められるコードやデータの格納に使用 キャッシュが有効のためデータの一貫性が重要な場合は注意が必要 |
| ddr_non_cached_32bit | 0xC0000000 | 128M | 周辺機器(DMAコントローラなど)が直接アクセスするメモリとして利用。キャッシュが無効なので、データの一貫性を保つ必要がある場合に使用 |
| ddr_wcb_32bit | 0xD0000000 | 256M | 書き込みパフォーマンスを向上させるためのメモリ領域。 グラフィック処理や連続したデータ書き込みが発生する場合に最適です。 |
| ddr_cached_38bit | 0x1000000000 | 896M | 38ビットアドレス空間でキャッシュが有効なメモリ。大規模なデータセットや複雑なアプリケーション用。38ビットアドレス空間を使用するため、メモリ管理ユニット(MMU)が必要です。 |
ddr_non_cached_38bit | 0x1400000000 | 896M | 周辺機器やリアルタイム処理に使用。 キャッシュが無効なので、データの一貫性が保たれます。 |

④共有変数用のセクションをリンカースクリプトに追加します。このセクションを l2lim セクション内に配置します。
.shared_data : ALIGN(0x10)
{
__shared_data_start = .;
*(.shared_data)
. = ALIGN(0x10);
__shared_data_end = .;
} > l2lim
⑤また共有メモリをゼロクリアするため、 .bss の一部としてゼロクリアさせます。
.bss : ALIGN(0x10)
{
__bss_start = .;
*(.bss .bss.* .gnu.linkonce.b.*)
*(.shared_data)
. = ALIGN(0x10);
__bss_end = .;
} > l2lim
2.変数宣言
共有変数を配置するには、ソースコードで変数に特定のセクションを指定する必要があります。
u54_1.cの先頭で宣言します。

__attribute__((section(".shared_data")))
volatile uint32_t shared_var1;
__attribute__((section(".shared_data")))
volatile uint32_t shared_var2[10];
3.ソースファイルの変更
①src > application > hart1 > u54_1.cをダブルクリックで開きます。
②125行目あたりに//ここから挿入 ~ //ここまでをペーストします。
SYSREG->GPIO_INTERRUPT_FAB_CR = 0xFFFFFFFFUL;
PLIC_SetPriority_Threshold(0);
for (int_num = 0u; int_num <= GPIO2_NON_DIRECT_PLIC; int_num++)
{
PLIC_SetPriority(GPIO0_BIT0_or_GPIO2_BIT0_PLIC_0 + int_num, 2u);
}
//ここから挿入
uint64_t led_cnt;
MSS_GPIO_config(GPIO1_LO, MSS_GPIO_9, MSS_GPIO_OUTPUT_MODE);
while(1u)
{
led_cnt ++;
if (led_cnt < 500000)
{
MSS_GPIO_set_output(GPIO1_LO, MSS_GPIO_9, 1u);
// led_cnt = 1;
}
else if (led_cnt < 1000000)
{
MSS_GPIO_set_output(GPIO1_LO, MSS_GPIO_9, 0u);
// led_cnt = 0;
}
else
{
led_cnt = 0;
}
shared_var1 = led_cnt;
}
//ここまで
MSS_GPIO_init(GPIO2_LO);
for (cnt = 9u; cnt< 23u; cnt++)
{
MSS_GPIO_config(GPIO2_LO,
cnt,
MSS_GPIO_OUTPUT_MODE);
}
MSS_GPIO_config(GPIO2_LO, MSS_GPIO_26,
③src > application > hart1 > u54_2.cをダブルクリックで開きます。
④22行目からのメインコードを以下の様に書き換えます。
デフォルトではコアを低電力状態にし、メインコア(e51.c)からの起動を待つようになっていますので
コメントアウトします。
ループ内で icount = shared_var1; の様に共有メモリにアクセスします。
void u54_2(void)
{
uint64_t hartid = read_csr(mhartid);
volatile uint32_t icount = 0U;
/* Clear pending software interrupt in case there was any.
Enable only the software interrupt so that the E51 core can bring this
core out of WFI by raising a software interrupt. */
// clear_soft_interrupt();
// set_csr(mie, MIP_MSIP);
/* Put this hart in WFI */
// do
// {
// __asm("wfi");
// }while(0 == (read_csr(mip) & MIP_MSIP));
/* The hart is out of WFI, clear the SW interrupt. Here onwards application
* can enable and use any interrupts as required */
// clear_soft_interrupt();
// __enable_irq();
while (1U)
{
icount = shared_var1;
if (0x100000U == icount)
{
icount = 0U;
}
}
/* never return */
}
なお、コアが同時に書き込むなど競合が発生する可能性がありますので、セマフォなどでアクセス制御をするべきだと思います
編集後記
正直なところ、まだわからないことが多く、手探りで実験を進めている状態です。ドキュメントは多く揃っているものの、その分資料やページの量が膨大で初めて扱う内容も多いため、情報の整理に苦労しています。

コメント