PSoC 3 で、 FIFO バッファを作った [PSoC]
データパスには、4段の FIFO が二本入っています。 今回は、この二本の FIFO を接続して8段の FIFO を構成します。
FIFO の使い方
データパスが内蔵している FIFO は、原則として、 CPU などが接続されるバスとデータパスのレジスタを仲介します。 CPU がデータを書込んでデータパスのレジスタに引き渡すものを "Input mode" と、データパスで生成されたデータを CPU に引き渡すものを "Output mode" と呼んでいます。 前回のジェネレータで使用したのは、 "Output mode" の FIFO です。
- Input mode
-
"Input mode" では、 CPU などを使ってバスから FIFO に書き込んだデータをアキュムレータ (Ax) またはデータレジスタ (Dx) に引き渡します。 アキュムレータに渡す場合には、データパスの設定 "Ax WR SRC" で FIFO (Fx) を指定します。 また、データレジスタに渡す場合には、外部制御信号 dx_load の立ち上がりを使用します。
このモードでは、二つのフラグ fx_bus_stat と fx_blk_stat が出力されます。 fx_bus_stat は、 CPU などのバス側の制御に通知を行うための信号で、 FIFO が FULL である時にアサートされます。 このフラグのネゲートにより、新たな値を書込むことが可能である事を示します。 また、 fx_blk_stat は、データパス側の制御に通知を行うための信号で、 FIFO が EMPTY である時にアサートされます。 このフラグのネゲートにより、次のデータを取り出すことが可能である事を示します。
- Output mode
-
"Output mode" では、アキュムレータまたは ALU の出力の値を FIFO の入力とします。 どの値を入力するかは、データパスの設定 "Fx INSEL" で指定します。 データを取り込むタイミングは、外部制御信号 fx_load の立ち上がりを使います。
このモードでも、二つのフラグ fx_bus_stat と fx_blk_stat が出力されますが、 "Input mode" の時とは意味が逆になっています。 fx_bus_stat は、 CPU などのバス側の制御に通知を行うための信号で、 FIFO が EMPTY である時にアサートされます。 また、 fx_blk_stat は、データパス側の制御に通知を行うための信号で、 FIFO が FULL である時にアサートされます。
8段 FIFO の実装
今回の実験では、 FIFO0 を "Input mode" で使用し、 FIFO1 を "Output mode" で使用します。 そして、これらの FIFO の制御を行うステートマシンで FIFO 間のデータ転送を行います。
//`#start header` -- edit after this line, do not edit this line // ======================================== // // Copyright noritan.org, 2013 // All Rights Reserved // UNPUBLISHED, LICENSED SOFTWARE. // // CONFIDENTIAL AND PROPRIETARY INFORMATION // WHICH IS THE PROPERTY OF NORITAN.ORG. // // ======================================== `include "cypress.v" //`#end` -- edit above this line, do not edit this line // Generated on 05/05/2013 at 21:01 // Component: Fifo8Ntan01 module Fifo8Ntan01 ( input clock, input en, input reset ); //`#start body` -- edit after this line, do not edit this line // State code declaration localparam ST_IDLE = 2'b00; localparam ST_GET = 2'b01; localparam ST_WAIT = 2'b11; localparam ST_PUT = 2'b10; // Datapath function declaration localparam CS_IDLE = 3'b000; localparam CS_FEED = 3'b001; // Wire declaration wire[1:0] state; // State code wire f0_empty; // F0 is EMPTY wire f1_full; // F1 is FULL // Pseudo register reg[2:0] addr; // Datapath function reg f1_load; // LOAD control to F1 // State machine behavior reg [1:0] state_reg; always @(posedge reset or posedge clock) begin if (reset) begin state_reg <= ST_IDLE; end else casez (state) ST_IDLE: if (en & !f0_empty) begin state_reg <= ST_GET; end ST_GET: state_reg <= ST_WAIT; ST_WAIT: if (!f1_full) begin state_reg <= ST_PUT; end /*ST_PUT*/ default: state_reg <= ST_IDLE; endcase end assign state = state_reg; // Internal control signals always @(state) begin casez (state) ST_IDLE: begin addr = CS_IDLE; f1_load = 1'b0; end ST_GET: begin addr = CS_FEED; f1_load = 1'b0; end ST_WAIT: begin addr = CS_IDLE; f1_load = 1'b0; end /*ST_PUT*/ default: begin addr = CS_IDLE; f1_load = 1'b1; end endcase end cy_psoc3_dp8 #(.cy_dpconfig_a( { `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, `CS_CMP_SEL_CFGA, /*CFGRAM0: IDLE*/ `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, `CS_SHFT_OP_PASS, `CS_A0_SRC___F0, `CS_A1_SRC_NONE, `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, `CS_CMP_SEL_CFGA, /*CFGRAM1: FEED: A0:=F0*/ `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, `CS_CMP_SEL_CFGA, /*CFGRAM2: */ `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, `CS_CMP_SEL_CFGA, /*CFGRAM3: */ `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, `CS_CMP_SEL_CFGA, /*CFGRAM4: */ `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, `CS_CMP_SEL_CFGA, /*CFGRAM5: */ `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, `CS_CMP_SEL_CFGA, /*CFGRAM6: */ `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0, `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE, `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA, `CS_CMP_SEL_CFGA, /*CFGRAM7: */ 8'hFF, 8'h00, /*CFG9: */ 8'hFF, 8'hFF, /*CFG11-10: */ `SC_CMPB_A1_D1, `SC_CMPA_A1_D1, `SC_CI_B_ARITH, `SC_CI_A_ARITH, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL, `SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_DEFSI, `SC_SI_A_DEFSI, /*CFG13-12: */ `SC_A0_SRC_ACC, `SC_SHIFT_SL, 1'h0, 1'h0, `SC_FIFO1__A0, `SC_FIFO0_BUS, `SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN, `SC_FB_NOCHN, `SC_CMP1_NOCHN, `SC_CMP0_NOCHN, /*CFG15-14: */ 10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX, `SC_FIFO_LEVEL,`SC_FIFO__SYNC,`SC_EXTCRC_DSBL, `SC_WRK16CAT_DSBL /*CFG17-16: */ } )) dp( /* input */ .reset(reset), /* input */ .clk(clock), /* input [02:00] */ .cs_addr(addr), /* input */ .route_si(1'b0), /* input */ .route_ci(1'b0), /* input */ .f0_load(1'b0), /* input */ .f1_load(f1_load), /* input */ .d0_load(1'b0), /* input */ .d1_load(1'b0), /* output */ .ce0(), /* output */ .cl0(), /* output */ .z0(), /* output */ .ff0(), /* output */ .ce1(), /* output */ .cl1(), /* output */ .z1(), /* output */ .ff1(), /* output */ .ov_msb(), /* output */ .co_msb(), /* output */ .cmsb(), /* output */ .so(), /* output */ .f0_bus_stat(), /* output */ .f0_blk_stat(f0_empty), /* output */ .f1_bus_stat(), /* output */ .f1_blk_stat(f1_full) ); //`#end` -- edit above this line, do not edit this line endmodule //`#start footer` -- edit after this line, do not edit this line //`#end` -- edit above this line, do not edit this line
ステートマシンは、四つの状態で構成されています。
- IDLE
- f0_empty (f0_blk_stat) を監視して、 FIFO0 にデータが入るのを待ちます。 ALU は、何もしません。
- GET
- ALU を駆動して、 FIFO0 の値をアキュムレータ A0 に取り出します。
- WAIT
- f1_full (f1_blk_stat) を監視して、 FIFO1 に空きができるのを待ちます。 アキュムレータ A0 には、 GET で FIFO0 から取り出した値が入ったままになっています。
- PUT
- f1_load をアサートして、アキュムレータ A0 の値を FIFO1 に転送します。
これら一連の動作を経て、1バイトのデータが FIFO0 から FIFO1 に転送されていきます。 FIFO1 が FULL の状態で FIFO0 にデータが到着した場合には、状態 WAIT で待ち合わせを行います。
データパスの設定
データパスは、 Reg0 と Reg1 をそれぞれ IDLE と FEED に割り当てています。 IDLE は何もしない状態、 FEED は FIFO0 から A0 にデータを取り出す状態です。
FIFO1 へは、アキュムレータ A0 の値が送り込まれます。 この設定を行うのが、 "F1 INSEL" です。 データパスの設定で、変更されたのは、以上の二点です。
FIFO へアクセスするテストプログラム
動作試験のために作成されたテストプログラムは、今回も、最初の16バイトの出力を LCD に表示させるものとしました。
/* ======================================== * * Copyright noritan.org, 2013 * All Rights Reserved * UNPUBLISHED, LICENSED SOFTWARE. * * CONFIDENTIAL AND PROPRIETARY INFORMATION * WHICH IS THE PROPERTY OF NORITAN.ORG. * * ======================================== */ #include <device.h> void main() { uint8 i = 0; uint8 v[16]; volatile uint8 w; CR1_Write(1); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); v[ 0] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =0 v[ 1] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =1 v[ 2] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =2 v[ 3] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =3 v[ 4] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =4 v[ 5] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =5 v[ 6] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =6 v[ 7] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =7 v[ 8] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =4 v[ 9] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =4 CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); v[10] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =8 CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); v[11] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =9 CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); v[12] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =10 CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); v[13] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =11 v[14] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =12 v[15] = CY_GET_REG8(Fifo_dp_u0__F1_REG); // =13 LCD_Start(); LCD_Position(0, 0); for (i = 0; i < 8; i++) { LCD_PrintInt8(v[i]); } LCD_Position(1, 0); for (i = 8; i < 16; i++) { LCD_PrintInt8(v[i]); } CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); /* CyGlobalIntEnable; */ /* Uncomment this line to enable global interrupts. */ for(;;) { CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); CY_SET_REG8(Fifo_dp_u0__F0_REG, i++); w = CY_GET_REG8(Fifo_dp_u0__F1_REG); w = CY_GET_REG8(Fifo_dp_u0__F1_REG); w = CY_GET_REG8(Fifo_dp_u0__F1_REG); w = CY_GET_REG8(Fifo_dp_u0__F1_REG); } } /* [] END OF FILE */
初期化を終えたら、まず、 0 から 7 までの値を FIFO に書き込みます。 書込まれたデータは、 0 から 3 までが FIFO1 に、 4 から 7 までが FIFO0 に格納されます。
次にデータを10回読み出します。 FIFO には、8個しか値が入っていませんので、 0 から 7 までの値が出て行ったあとは、ゴミデータが読み出されます。 この例では、 4 という値が出ています。 本来は、 EMPTY フラグ (f1_blk_stat) を実装して、フラグを見ながら値を読み出していくのが、正しい使い方になります。
その後は、数個ずつ FIFO への書き込みと読み出しを行って、順番通りの値が読み出されている事を確認しています。
メインループでは、4個ずつデータの書き込みと読み出しを行っています。 デバッガのステップ動作で、読み出された値を確認することができます。
プロジェクトアーカイブ
この記事を書くために作成したプロジェクトは、このファイルの拡張子を ZIP に変更して展開すると再現できます。
関連文献
シリーズ最強!PSoC 3ボード+デバッグ・ボード: あのPSoCが生まれ変わった!アナログもディジタルも一新 (トライアルシリーズ)
- 作者: 古平 晃洋
- 出版社/メーカー: CQ出版
- 発売日: 2012/10/23
- メディア: 単行本
コメント 0