QuickLogic EOS S3のハードマクロFIFOを使う
EOS S3はPSoC3/5LPと比較すると、アーキテクチャの差があるので単純比較できないものの、およそ3~4.5倍ほどの論理ゲート規模を有します。
それはそうと、ロジック規模だけで優劣をつけてしまうので有れば15年以上前のCPLDと同等になってしまいます。それ以上の価値としてEOS S3の特徴は、小規模組み込みデバイスとしては大きめのRAMが搭載されていてサブモジュールごとに割り振られている点です。Cortex-M4Fには512KB、eFPGAには8kBのRAMが割り振られています。
FPGA側から8KBのRAMはハードマクロRAMまたはFIFOとしてインスタンスできます。8kBはちょっと少ないんじゃないかという感じもしますが、EOS S3はセンサーマネジメントSoCなので、FPGA IPでなんでも作り込むような古典的思想ではなく、飽くまでセンサーIFのみを用意するにとどめ、取得したデータはDMAでCortex-M4F側のSRAMに転送する前提なのでこれはこれでいいのかなと。DMAのDestinationはCortex-M4FサブモジュールのSRAMだけでなくSPIマスタにも切り変え可能なため、IoT RAMのような高速大容量SPI RAMがDestination側で使えたらいいなと考えているところです。
下記の順番でこの記事は構成されています。
使用可能なハードマクロ
FPGAが利用できるハードマクロは
- RAM (8 x 8Kbits)
- FIFO (8 x 8Kbits)
- 乗算器 (32bit x 32bit x 2)
です。小規模FPGAらしく必要最小限でとどめていて好印象。
ハードマクロFIFOのインスタンス
Design example Using FIFOsにハードマクロFIFOのインスタンス事例と、VerilogのexampleコードYour-Installed_Path/quicklogic-arch-defs/tests/fifo_test
があります。Wishbone bus readyなコードになっているので、自作プロジェクト内で動作できるように若干の修正は行いますが、ほぼそのまま使おうとおもいます。CPU側のコードだけ追記するような感じです。
FIFOは8kbit、16kbitのブロックを使用することができ、それぞれのブロックを並列、直列接続することによってビット長を増やしたり、単純にFIFO深さをn倍にしたりすることもできます。
下記例は8kbitのFIFOを1ブロック分インスタンスしています。
(前略) FIFO_8K_BLK # (.data_depth_int(data_depth_int),.data_width_int(data_width_int),.reg_rd_int(reg_rd_int),.sync_fifo_int(sync_fifo_int) ) FIFO_INST ( .DIN(DIN), .PUSH(PUSH), .POP(POP), .Fifo_Push_Flush(Fifo_Push_Flush), .Fifo_Pop_Flush(Fifo_Pop_Flush), .Push_Clk(Push_Clk), .Pop_Clk(Pop_Clk),.PUSH_FLAG(PUSH_FLAG), .POP_FLAG(POP_FLAG), .Push_Clk_En(Push_Clk_En), .Pop_Clk_En(Pop_Clk_En), .Fifo_Dir(Fifo_Dir), .Async_Flush(Async_Flush), .Almost_Full(Almost_Full), .Almost_Empty(Almost_Empty), .DOUT(DOUT));
それぞれの信号の詳細は、FIFO Usage — QuickLogic-FPGA-Toolchainを参照ください。
Cortex-M4F側のプログラム
Exampleでは3つ、タイプの異なるFIFOが用意されています。
メモリマップのとおり、構造体を用意しました。
fpga/inc/fpga_fifoctrl.h
typedef struct fpga_fifoctrl { uint32_t device_id; // 0x00 uint32_t rev_num; // 0x04 uint32_t gpio_in; // 0x08 uint32_t gpio_out; // 0x0C uint32_t gpio_oe; // 0x10 uint32_t reserved1[60-1]; uint32_t fifo1_acc; // 0x100 uint32_t fifo1_flags; // 0x104 uint32_t reserved2[63-1]; uint32_t fifo2_acc; // 0x200 uint32_t fifo2_flags; // 0x204 uint32_t reserved3[127-1]; uint32_t fifo3_acc; // 0x400 uint32_t fifo3_flags; // 0x404 } fpga_fifoctrl_t; fpga_fifoctrl_t* fifoctrl_regs = (fpga_fifoctrl_t*)(FPGA_PERIPH_BASE);
このままだとちょっとアクセスが面倒な感じがするので、それぞれのFIFOやフラグにアクセスできるように関数を用意します。
// FIFO APIs uint32_t fpga_getflag(uint8_t ch); uint32_t fpga_getfifo(uint8_t ch); void fpga_setfifo(uint8_t ch, uint32_t value);
フラグ取得API,FIFO読み込みAPI,FIFO書き込みAPIです。
main関数で下記の手順を実行するテストコードを書きます。
- ライト動作
- 特定のChannelのFIFOに深さ512だけ書き込む
- リード動作
- Channel1~3まで上記ステップを繰り返し
コードです。
src/main.c
// (中略) // test each FIFOs(FIFO1~3) for (uint8_t ch=FIFO_CH1 ; ch<=FIFO_CH3 ; ch++) { dbg_str("\r\n\r\n------------------ CHANNEL "); dbg_int(ch); dbg_str(" ------------------"); for(uint32_t i=0 ; i<512 ; i++) { fpga_setfifo(ch,i); } for(uint32_t i=0 ; i<512 ; i++) { dbg_str("\r\nstatus = 0x"); dbg_hex32(fpga_getflag(ch)); dbg_str("....fifo = 0x"); dbg_hex32(fpga_getfifo(ch)); } // 最後のFIFOをReadした後(=FIFOはすべてEmpty)のFlagを確認する dbg_str("\r\nstatus = 0x"); dbg_hex32(fpga_getflag(ch)); dbg_str(".\r\n"); }
動作確認
QuickFeatherにUSB-UARTを接続していると動作確認ができます。
------------------ CHANNEL 1 ------------------ status = 0x00000f80....fifo = 0x00000000 status = 0x00000e8f....fifo = 0x00000001 status = 0x00000e0e....fifo = 0x00000002 status = 0x00000e0e....fifo = 0x00000003 status = 0x00000e0e....fifo = 0x00000004 (略) status = 0x00008102....fifo = 0x000001ff status = 0x00008001. ------------------ CHANNEL 2 ------------------ status = 0x00000e03....fifo = 0x00000000 status = 0x00000d02....fifo = 0x00000001 status = 0x00000d02....fifo = 0x00000002 status = 0x00000d02....fifo = 0x00000003 status = 0x00000d02....fifo = 0x00000004 (略) status = 0x00000302....fifo = 0x000001fc status = 0x00000202....fifo = 0x000001fd status = 0x00000202....fifo = 0x000001fe status = 0x00008102....fifo = 0x000001ff status = 0x00008001. ------------------ CHANNEL 3 ------------------ status = 0x00000f80....fifo = 0x00000000 status = 0x00000e8f....fifo = 0x00000001 status = 0x00000e0e....fifo = 0x00000002 status = 0x00000e0e....fifo = 0x00000003 (略) status = 0x00000302....fifo = 0x000001fb status = 0x00000302....fifo = 0x000001fc status = 0x00000202....fifo = 0x000001fd status = 0x00000202....fifo = 0x000001fe status = 0x00008102....fifo = 0x000001ff status = 0x00008001.
Channel1、FIFOリード動作の初めの部分
深さ512分だけFIFOを書き込んだ後からスタートしています。Status(Flag1)=0x0f80ですので、PUSH,POPフラグともにFullを示しています。
Almost Fullフラグも立っています。
Channel1、FIFOリード動作の終盤部分
Status(Flag1)=0x81xxとなっておりAlmost Emptyフラグが立っていることがわかります。
コード全体
QORC-SDKプロジェクト、
qorc-sdk/qf_apps
フォルダ内で、以下のプロジェクトをcloneしてください。
$ git clone -b v0.0.2 https://github.com/panda5mt/qf_wbfpga_pio.git