前回の実験で、ACPが期待通りに動かない環境を作ることができました。この環境に設定を追加してACPを有効にします。Cyclone Vのマニュアルに記載されているACPの有効化に必要な設定は次のとおりです。
(Cyclone V Hard Processor System Technical Reference Manual 2015.05.04の9-29)
- リード時はARCACHEのアトリビュートがMMUページ・テーブルのアトリビュートと一致すること
- リード時はARUSER[0] = 1かつARCACHE[1] = 1という条件が必要
- ライト時はAWCACHEのアトリビュートがMMUページ・テーブルのアトリビュートと一致すること
- ライト時はAWUSER[0] = 1かつAWCACHE[1] = 1という条件が必要
- Cortex-A9 MPCoreは次の設定が必要
- Snoop Control Unitを有効にする
- コヒーレント・メモリはCacheable/Shareableに設定する
- ACTLRレジスタのSMPビットをセットする。
これらの設定をプログラムに追加すると、次のようになります。
#include <stdio.h> #include <stdlib.h> #include "alt_cache.h" #include "alt_mmu.h" #include "pplib/pl_address_table.h" // Determine size of an array #define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0])) // MMU Page table - 16KB aligned at 16KB boundary static uint32_t __attribute__ ((aligned (0x4000))) alt_pt_storage[4096]; static void * alt_pt_alloc(const size_t size, void * context) { return context; } ALT_STATUS_CODE alt_pt_init(void) { // Populate the page table with sections (1 MiB regions). ALT_MMU_MEM_REGION_t regions[] = { // Memory area: 1 GiB { .va = (void *)0x00000000, .pa = (void *)0x00000000, .size = 0x40000000, .access = ALT_MMU_AP_PRIV_ACCESS, .attributes = ALT_MMU_ATTR_WBA, // .shareable = ALT_MMU_TTB_S_NON_SHAREABLE, .shareable = ALT_MMU_TTB_S_SHAREABLE, .execute = ALT_MMU_TTB_XN_DISABLE, .security = ALT_MMU_TTB_NS_SECURE }, // Device area: Everything else { .va = (void *)0x40000000, .pa = (void *)0x40000000, .size = 0xc0000000, .access = ALT_MMU_AP_PRIV_ACCESS, .attributes = ALT_MMU_ATTR_DEVICE_NS, .shareable = ALT_MMU_TTB_S_NON_SHAREABLE, .execute = ALT_MMU_TTB_XN_ENABLE, .security = ALT_MMU_TTB_NS_SECURE } }; ALT_STATUS_CODE status = ALT_E_SUCCESS; uint32_t * ttb1 = NULL; if (status == ALT_E_SUCCESS) { status = alt_mmu_init(); } if (status == ALT_E_SUCCESS) { size_t reqsize = alt_mmu_va_space_storage_required(regions, ARRAY_COUNT(regions)); if (reqsize > sizeof(alt_pt_storage)) { status = ALT_E_ERROR; } } if (status == ALT_E_SUCCESS) { status = alt_mmu_va_space_create(&ttb1, regions, ARRAY_COUNT(regions), alt_pt_alloc, alt_pt_storage); } if (status == ALT_E_SUCCESS) { status = alt_mmu_va_space_enable(ttb1); } return status; } ALT_STATUS_CODE alt_pt_uninit(void) { if (alt_mmu_disable() != ALT_E_SUCCESS) { printf("DEBUG[PT]: Failure on line %d.\n", __LINE__); } return ALT_E_SUCCESS; } // from alt_cache.c static inline __attribute__((always_inline)) uint32_t actlr_read_helper(void) { uint32_t actlr; #if defined(__ARMCOMPILER_VERSION) __asm("MRC p15, 0, %[actlr], c1, c0, 1" : [actlr] "=r" (actlr)); #elif defined(__ARMCC_VERSION) __asm("MRC p15, 0, actlr, c1, c0, 1"); #else __asm("MRC p15, 0, %0, c1, c0, 1" : "=r" (actlr)); #endif return actlr; } static inline __attribute__((always_inline)) void actlr_write_helper(uint32_t actlr) { #if defined(__ARMCOMPILER_VERSION) __asm("MCR p15, 0, %[actlr], c1, c0, 1" : : [actlr] "r" (actlr)); #elif defined(__ARMCC_VERSION) __asm("MCR p15, 0, actlr, c1, c0, 1"); #else __asm("MCR p15, 0, %0, c1, c0, 1" : : "r" (actlr)); #endif } #define DDR_ADRS 0x20000000 #define ACP_ADRS 0x80000000 | DDR_ADRS unsigned int fpga_mem_read(unsigned int addr){ int stat = 0; PP_VTX_TOP_ADRS = addr; PP_VTX_DMA_CTRL = 1; // dma start stat = PP_VTX_DMA_CTRL; while ((stat & 0x100) == 0) { // wait for dma end stat = PP_VTX_DMA_CTRL; } return PP_VTX_DMA_DATA; } int main(int argc, char **argv) { int i; unsigned int ref,val,actlr; printf("ACP test\n"); alt_pt_init(); alt_cache_system_enable(); actlr = actlr_read_helper(); actlr |= 0x40; // set SMP bit(bit6) actlr_write_helper(actlr); printf("ACTLR %08x\n",actlr); (*(volatile unsigned int *)(0xfffec000)) = 1; printf("SCU Control Register %08x\n",(*(volatile unsigned int *)(0xfffec000))); PP_AXI_MASTER_CONFIG = 0x010f010f; for (i=0;i<10;i++) { printf("TEST: %d\n",i); ref = i | i << 8 | i << 16 | i << 24; (*(volatile unsigned int *)(DDR_ADRS)) = ref; printf(" CPU write val %08x\n",(*(volatile unsigned int *)(DDR_ADRS))); val = fpga_mem_read(DDR_ADRS); printf(" DDR read value %x\n",val); if (val != ref) printf(" error\n"); val = fpga_mem_read(ACP_ADRS); printf(" ACP read value %x\n",val); if (val != ref) printf(" error\n"); } }
- FPGA内のDMACがドライブするAxUSERとAxCACHEは147行目で指定しています。具体的にはARUSERとAWUSERは1に、ARCACHEとAWCACHEはすべて1(0xf)に設定しています(レジスタで設定できるように回路を設計している)。
- Snoop Control Unitの有効化は145行目です。マニュアルによると、0xfffec000にSCUがマッピングされています。SCUのマニュアルを参照すると、0番地のビット0が有効化ビットなので、0xfffec000に1を設定します(Cyclone VのマニュアルにはSCUのレジスタマップは記載されていません)。
- ACTLRレジスタのSMPビットをセットしてるのは、141~143行目です。SMPはビット6です。ACTLRレジスタのリード/ライトには、alt_cache.cのヘルパー関数を利用しました。
- コヒーレント・メモリをCacheable/Shareableに設定しているのは、29行目と31行目です。ALT_MMU_ATTR_WBAやALT_MMU_TTB_S_SHAREABLEの意味は、alt_mmu.hのソースコード内にコメントとして記載されています。
ALT_MMU_ATTR_WBA = 0x13, /*!< Inner/Outer Write-Back, Write Allocate, Shareability determined by [S] bit */
ALT_MMU_ATTR_WBAでキャッシュが有効になるようです。ALT_MMU_TTB_S_SHAREABLEは次のように記載されています。
typedef enum ALT_MMU_TTB_S_e { ALT_MMU_TTB_S_NON_SHAREABLE = 0, /*!< Non-Shareable address map */ ALT_MMU_TTB_S_SHAREABLE = 1 /*!< Shareable address map */ } ALT_MMU_TTB_S_t;
なお、alt_mmu.hはSoC EDSインストールディレクトリの/embedded/ip/altera/hps/altera_hps/hwlib/includeに格納されています。
実行結果
ACPウィンドウをリードした時には、キャッシュに留まっているCPUのライト・データが期待通りに読み出せていることがわかります。
U-Boot SPL 2013.01.01 (Jan 20 2016 - 07:19:29) BOARD : Altera SOCFPGA Cyclone V Board CLOCK: EOSC1 clock 25000 KHz CLOCK: EOSC2 clock 25000 KHz CLOCK: F2S_SDR_REF clock 0 KHz CLOCK: F2S_PER_REF clock 0 KHz CLOCK: MPU clock 925 MHz CLOCK: DDR clock 400 MHz CLOCK: UART clock 100000 KHz CLOCK: MMC clock 50000 KHz CLOCK: QSPI clock 3613 KHz RESET: WARM SDRAM: Initializing MMR registers SDRAM: Calibrating PHY SEQ.C: Preparing to start memory calibration SEQ.C: CALIBRATION PASSED SDRAM: 1024 MiB ALTERA DWMMC: 0 reading hello-mkimage.bin reading hello-mkimage.bin ACP test ACTLR 00000244 SCU Control Register 00000001 TEST: 0 CPU write val 00000000 DDR read value 0 ACP read value 0 TEST: 1 CPU write val 01010101 DDR read value 0 error ACP read value 1010101 TEST: 2 CPU write val 02020202 DDR read value 0 error ACP read value 2020202 TEST: 3 CPU write val 03030303 DDR read value 0 error ACP read value 3030303 TEST: 4 CPU write val 04040404 DDR read value 0 error ACP read value 4040404 TEST: 5 CPU write val 05050505 DDR read value 0 error ACP read value 5050505 TEST: 6 CPU write val 06060606 DDR read value 0 error ACP read value 6060606 TEST: 7 CPU write val 07070707 DDR read value 0 error ACP read value 7070707 TEST: 8 CPU write val 08080808 DDR read value 0 error ACP read value 8080808 TEST: 9 CPU write val 09090909 DDR read value 0 error ACP read value 9090909