SSブログ

PSoC 3 で、倍増器を作った [PSoC]このエントリーを含むはてなブックマーク#

倍増器の実験回路

前回の FIFO の実験では、 FIFO に入れたデータがそのまま FIFO から出てきました。 このデータを途中で加工してやると、計算機が出来上がります。 今回は、入れたデータの値の二倍の値のデータが出てくる、名付けて倍増器を作ります。

データフロー図

倍増器データフロー

今回は、データパスの動作をデータフローで表現しました。 左端にある "state" が、データパスを制御するステートマシンの状態です。 そして、橙色の破線が、クロックタイミングを表現しています。 つまり、この倍増器の場合、 FIFO0 にデータが到着してから FIFO1 にデータが出てくるまで、4クロックを要するという事になります。


  • IDLE

    この図では省略されていますが、 IDLE 状態では、 FIFO0 へのデータの到着を待っています。 データパスから出力される制御信号 f0_blk_stat (f0_empty) が、データの到着を知らせてくれます。 データが到着したら、次の GET 状態に遷移します。

  • GET

    データが到着したら、1クロックで、二つの作業を同時に行います。

    一つは、 ALU を使って、アキュムレータ A0 の値を更新することです。 この時に使用される演算は、 A0 XOR A0 で、 A0 をゼロにクリアします。 XOR 演算は、ビットごとの排他的論理和を表現しています。 この XOR 演算に同じ値を与えると、与えた値がどのような値でも、結果が "0" になるという性質を利用したものです。

    もうひとつの作業は、 FIFO0 に到着したデータをデータレジスタ D0 にコピーすることです。 制御信号 d0_load をアサートすると、 FIFO0 から D0 にデータがコピーされます。 この図では、 "d" というデータがコピーされてきます。 単に2倍の値を計算するためであれば、アキュムレータにデータをコピーするという選択肢もありますが、ここでは、後の拡張を考慮してデータレジスタを使用しています。

    GET 状態の次は、 ADD 状態に遷移します。

  • ADD

    この状態では、 ALU を使用して A0 + D0 の演算を行い、 A0 に格納します。 GET 状態で、 A0 にはゼロ、 D0 には "d" が格納されましたので、この状態では A0 に "d" が格納されることになります。 次は、 DOUBLE 状態に遷移します。

  • DOUBLE

    この状態では、 ALU を使用して A0 + A0 の演算を行い、 A0 に格納します。 A0 の値は、 "2d" となります。 つまり、このステップでは、 A0 の値が2倍されたことになります。 次は、 WAIT 状態に遷移します。

  • WAIT

    この図では省略されていますが、 WAIT 状態では、データパスの制御信号 f1_blk_stat (f1_full) を使って FIFO1 にデータを書き込めるようになるまで、待ち合わせを行います。 FIFO1 が受け入れ可能な状態になったら、 PUT 状態に遷移します。

  • PUT

    この状態では、データパスの制御信号 f1_load をアサートすることで、 A0 の値 "2d" を ALU を介して FIFO1 に書き込みます。

    FIFO1 の書き込みには、 A0 の値を直接書き込む機能もあるのですが、今回は、 ALU を経由する方法を採用しています。 これは、 FIFO1 への書き込み経路が、データパスの機能上、動的に設定できる項目ではないという事情があるためです。 ALU を介する設定を使用することで、柔軟にデータの経路を決めることができます。

    データを FIFO1 に書き込んだら、次のデータの到着を待つため、 IDLE 状態に遷移します。

倍増器の実装

以上、倍増器の説明をデータフローで解説しました。 あとは、倍増器の実装を Verilog で記述していくだけです。

//`#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/18/2013 at 21:08
// Component: DoublerAdd
module DoublerAdd (
	input   clock,
	input   en,
	input   reset
);

//`#start body` -- edit after this line, do not edit this line

// State code declaration
localparam      ST_IDLE     = 3'b000;
localparam      ST_GET      = 3'b001;
localparam      ST_ADD      = 3'b010;
localparam      ST_DOUBLE   = 3'b011;
localparam      ST_WAIT     = 3'b100;
localparam      ST_PUT      = 3'b101;

// Datapath function declaration
localparam      CS_IDLE     = 3'b000;
localparam      CS_CLEAR    = 3'b001;
localparam      CS_ADD      = 3'b010;
localparam      CS_DOUBLE   = 3'b011;

// Wire declaration
wire[2: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             d0_load;        // LOAD D0 from F0
reg             f1_load;        // LOAD F1

// State machine behavior
reg [2: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_ADD;
        ST_ADD:
                state_reg <= ST_DOUBLE;
        ST_DOUBLE:
                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;
            d0_load = 1'b0;
            f1_load = 1'b0;
        end
        ST_GET: begin
            addr    = CS_CLEAR;
            d0_load = 1'b1;
            f1_load = 1'b0;
        end
        ST_ADD: begin
            addr    = CS_ADD;
            d0_load = 1'b0;
            f1_load = 1'b0;
        end
        ST_DOUBLE: begin
            addr    = CS_DOUBLE;
            d0_load = 1'b0;
            f1_load = 1'b0;
        end
        ST_WAIT: begin
            addr    = CS_IDLE;
            d0_load = 1'b0;
            f1_load = 1'b0;
        end
        /*ST_PUT*/ default: begin
            addr    = CS_IDLE;
            d0_load = 1'b0;
            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 : Do nothing*/
    `CS_ALU_OP__XOR, `CS_SRCA_A0, `CS_SRCB_A0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM1:  CLEAR : A0<=ALU<=0;*/
    `CS_ALU_OP__ADD, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM2:  ADD : A0<=ALU<=A0+D0;*/
    `CS_ALU_OP__ADD, `CS_SRCA_A0, `CS_SRCB_A0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM3:  DOUBLE : A0<=ALU<=A0+A0;*/
    `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(d0_load),
        /*  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

ステートマシンのそれぞれの状態に応じて、データパスの構成が変更されていきますが、これは、データパスの制御信号 cs_addr に与えるアドレスで指定することができます。

おおまかには、前回の8段 FIFO の実装を踏襲したものになっています。

データパスの設定

CGRAM の設定

データパスの CGRAM は、 Reg0 から Reg3 までの4つの設定を使っています。 それぞれの設定には、 IDLE, GET, ADD, DOUBLE の名前がつけてあり、データフロー図と Verilog 記述に対応させています。

F1 INSEL の設定

そのほかの設定では、 F1 INSEL を ALU に設定して、 FIFO1 への書き込みを ALU から行うようにしています。

倍増器へ書き込むテストプログラム

動作確認を行うために、テストプログラムを作成しました。 前回の FIFO を扱うプログラムと同様に FIFO0 に値を書き込み、 FIFO1 から値を読み出しています。

/* ========================================
 *
 * 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 = 0;
    
    CR1_Write(1);
    CY_SET_REG8(Doubler_dp_u0__F0_REG, w++);
    for (i = 0; i < 16; i++) {
        CY_SET_REG8(Doubler_dp_u0__F0_REG, w++);
        v[ i] = CY_GET_REG8(Doubler_dp_u0__F1_REG);
    }

    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(Doubler_dp_u0__F0_REG, i++);
    CY_SET_REG8(Doubler_dp_u0__F0_REG, i++);

    /* CyGlobalIntEnable; */ /* Uncomment this line to enable global interrupts. */
    for(;;)
    {
        CY_SET_REG8(Doubler_dp_u0__F0_REG, i++);
        w = CY_GET_REG8(Doubler_dp_u0__F1_REG);
    }
}

/* [] END OF FILE */

前回同様、 0 から 15 までの値を FIFO0 に書き込みながら、 FIFO1 から演算結果を取り出し、 LCD に表示します。 最初の部分をよく見るとわかりますが、 FIFO0 への書き込みが、1バイトだけ先行しています。 これは、 FIFO0 に書き込んでから FIFO1 にデータが準備できるまで、6クロック必要になるので、単純な書き込みと読み出しでは、処理が間に合わないためです。 書き込みを先行させることで、確実に処理が終わってから値を読み出す、いわゆるパイプライン構成にすることができました。

シフタを使った実装

シフタを使ったデータフロー図

データパスに内蔵されている ALU は、さらに複雑な計算を行わせることができます。 ここでは、 ALU 演算の出口部分にあるシフタを利用して、元の値を二倍にします。

このデータフロー図は、最初のデータフロー図とよく似ていますが、二つの状態 ADD と DOUBLE がひとつの状態 STEP1 にまとめられています。 これは、 ALU の出力にシフタと呼ばれる仕掛けがあるからです。 シフタは、 ALU の出力を1ビットだけ、右または左にシフトします。 値は、右にシフトすると 1/2 になり、左にシフトすると2倍になります。 つまり、2倍の値を得るために ALU の加算を使う必要がありません。

STEP1 状態では、 A0 と D0 の値を足して、シフタで2倍にして、 A0 に格納しています。 結果として、 A0 には、 D0 の値 "d" の2倍の値が入るというわけです。


ALU の設定

ALU の設定も3種類に減りました。 シフタの操作は、 SHIFT カラムで決定します。

プロジェクトアーカイブ

この記事を書くために作成したプロジェクトは、このファイルの拡張子を ZIP に変更して展開すると再現できます。

関連文献


nice!(0)  コメント(0)  トラックバック(0)  このエントリーを含むはてなブックマーク#

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

トラックバックの受付は締め切りました

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。