SSブログ

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

前回は、データパスを使って、加算器を作りました。 加算器は、延々と足し算をするだけなので、使用するデータパスの設定は一つだけで、変更はしていません。 今回は、 データパスの設定を2つ使って、分周器を作成します。

Verilog で書いた分周器

Verilog での実装

今回は、こんな回路を作成しました。 まず、400Hz のクロックを 100 分周し、 4Hz のパルスを作成します。 このパルスを T-FF に通して 2Hz 50% デューティーの信号を生成して LED を点滅させます。

Control Reg は、分周器の起動時期を決めるために追加されています。

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 04/28/2013 at 18:32
// Component: DividerUdb
module DividerUdb (
	output  tc,
	input   clock,
	input   enable,
	input   reset
);
	parameter PERIOD = 100;

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

    wire[7:0]   count;      // counter value
    wire        zero;       // A0==0 (z0)
    
    // Divide counter behavior
    reg[7:0]    count_reg;
    always @(posedge reset or posedge clock) begin
        if (reset) begin
            count_reg <= (PERIOD - 1);
        end else if (!enable) begin
            count_reg <= (PERIOD - 1);
        end else if (zero) begin
            count_reg <= (PERIOD - 1);
        end else begin
            count_reg <= count - 1;
        end
    end
    
    // Signal assignment
    assign      zero = (count[7:0] == 8'd0);
    assign      tc = zero & enable;
    assign      count = count_reg[7:0];

//`#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

ちょっとばかり難しくなりましたが、まだまだ、大したことはありません。 wire 宣言のあと、カウンタの動作を決めるステートメントが続いています。 ここで定義されているカウンタの振る舞いは、2つだけです。

LOAD

カウンタの初期値を設定します。

このカウンタは、ダウンカウンタとして設計されています。 初期値から数えて "0" に達するまで、カウントダウンする仕組みです。 そのため、分周器の周期は、ここで与えた初期値より1大きくなります。 (PERIOD -1) を与えているのは、このためです。

LOAD の条件は、リセット時、ディセーブル時、そして、カウンタが "0" に達した時の3つです。

DECREMENT

カウンタをデクリメントします。

カウンタは、その値が "0" に達するまでデクリメントを繰り返します。

最後の部分にある assign ステートメントで、制御信号を生成しています。

zero は、カウンタの値が "0" に達したことを示す信号です。 分周器の動作時、この信号は、1クロック周期だけアサートされます。

tc (Terminal Count) は、分周器の出力です。 分周器の動作時には、 zero と同じ動きをしますが、ディセーブル時にはアサートされません。 この信号は、単純にマスクされているだけなので、グリッチを発生する可能性があります。

count の定義は、レジスタとワイヤを分離して、問題が発生しないようにするための工夫です。

テストプログラムは、このようになっています。

/* ========================================
 *
 * 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()
{
    /* Place your initialization/startup code here (e.g. MyInst_Start()) */
    CR1_Write(1);

    /* CyGlobalIntEnable; */ /* Uncomment this line to enable global interrupts. */
    for(;;)
    {
        /* Place your application code here. */
    }
}

/* [] END OF FILE */

Control Reg を操作して、分周器をイネーブルするだけです。 これで、分周器は動作を開始します。

データパスで書いた分周器

データパスによる実装

次は、 Verilog で書いたものをデータパスで書き換えます。 "DividerDp" の記述は、以下の通りです。

//`#start header` -- edit after this line, do not edit this line
// ========================================
//
// Copyright YOUR COMPANY, THE YEAR
// All Rights Reserved
// UNPUBLISHED, LICENSED SOFTWARE.
//
// CONFIDENTIAL AND PROPRIETARY INFORMATION
// WHICH IS THE PROPERTY OF your company.
//
// ========================================
`include "cypress.v"
//`#end` -- edit above this line, do not edit this line
// Generated on 04/28/2013 at 19:07
// Component: DividerDp
module DividerDp (
	output  tc,
	input   clock,
	input   enable,
	input   reset
);

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

    localparam  CS_LOAD = 3'd0;
    localparam  CS_DECREMENT = 3'd1;
    
    wire[2:0]   addr;
    wire        zero;

    // Specify DATAPATH behavior
    assign      addr = (zero|~enable)?(CS_LOAD):(CS_DECREMENT);
    
    // TC output assignment
    assign      tc = zero & enable;

cy_psoc3_dp8 #(.cy_dpconfig_a(
{
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC___D0, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM0: LOAD D0 into A0*/
    `CS_ALU_OP__DEC, `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, /*CFGRAM1: DECREMENT 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, /*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_BUS, `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[2:0]),
        /*  input                   */  .route_si(1'b0),
        /*  input                   */  .route_ci(1'b0),
        /*  input                   */  .f0_load(1'b0),
        /*  input                   */  .f1_load(1'b0),
        /*  input                   */  .d0_load(1'b0),
        /*  input                   */  .d1_load(1'b0),
        /*  output                  */  .ce0(),
        /*  output                  */  .cl0(),
        /*  output                  */  .z0(zero),
        /*  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(),
        /*  output                  */  .f1_bus_stat(),
        /*  output                  */  .f1_blk_stat()
);

//`#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

データパスを使った場合の記述は、データパスインスタンスとその入出力制御信号で構成されています。 データパスインスタンスは、前回は "reset" と "clock" だけを使用していましたが、今回は "cs_addr" と "z0" も使用しています。

"cs_addr" は、データパスの振る舞いを選択するためのものです。 用意された振る舞いは、 "LOAD" と "DECREMENT" の2つです。 これを、 Verilog での記述と同様に "enable" と "zero" の状態によって切り替えます。

"z0" は、カウンタが "0" に達したかを示すために使用します。 カウンタは、データパス内の "A0" アキュムレータで実装されています。 "z0" 出力は、この "A0" の値が "0" に達したかどうかをデータパスの振る舞いに関係なく示すものです。

このデータパスインスタンスも、 PSoC Creator の "Datapath Config Tool" で作成しています。

データパスの設定

設定 "Reg0" が "LOAD" 動作に相当します。 この設定では、 "D0" に入っている初期値を直接 "A0" に入れます。

設定 "Reg1" は "DECREMENT" 動作に相当します。 "A0" の内容を ALU でデクリメントして、 "A0" に入れます。

データパスで設定を行った箇所は、この2行だけです。 他のレジスタなどは、全く使用していません。

テストプログラムは、以下の通りです。

/* ========================================
 *
 * 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()
{
    /* Place your initialization/startup code here (e.g. MyInst_Start()) */
    CY_SET_REG8(Divider_dp_u0__D0_REG, 99);     // Set PERIOD value
    CR1_Write(1);
    
    /* CyGlobalIntEnable; */ /* Uncomment this line to enable global interrupts. */
    for(;;)
    {
        /* Place your application code here. */
    }
}

/* [] END OF FILE */

Control Reg でイネーブルしているのは、 Verilog で書いたものと同じですが、それに加えて、周期を設定する行が追加されています。 データパスを使用した場合、あらかじめ初期値を "D0" に入れておかなくてはなりません。 通常は、 _Start() などの API を作成しておいて、 main() 関数から呼び出しますが、今回も横着をして API は、作成していません。 その代わりに、データパス内のレジスタ "D0" を直接操作しています。 100分周する場合の初期値には、 "99" を使用します。

消費資源の比較

今回も Verilog を使用した場合とデータパスを使用した場合で、消費資源を比較してみました。

Verilog での実装
Resource Type                 : Used : Free :  Max :  % Used
============================================================
Digital domain clock dividers :    1 :    7 :    8 :  12.50%
Analog domain clock dividers  :    0 :    4 :    4 :   0.00%
Pins                          :    4 :   68 :   72 :   5.56%
UDB Macrocells                :   10 :  182 :  192 :   5.21%
UDB Unique Pterms             :   22 :  362 :  384 :   5.73%
UDB Total Pterms              :   23 :      :      : 
UDB Datapath Cells            :    0 :   24 :   24 :   0.00%
UDB Status Cells              :    0 :   24 :   24 :   0.00%
UDB Control Cells             :    1 :   23 :   24 :   4.17%
            Control Registers :    1 
DMA Channels                  :    0 :   24 :   24 :   0.00%
Interrupts                    :    0 :   32 :   32 :   0.00%
データパスでの実装
Resource Type                 : Used : Free :  Max :  % Used
============================================================
Digital domain clock dividers :    1 :    7 :    8 :  12.50%
Analog domain clock dividers  :    0 :    4 :    4 :   0.00%
Pins                          :    4 :   68 :   72 :   5.56%
UDB Macrocells                :    2 :  190 :  192 :   1.04%
UDB Unique Pterms             :    2 :  382 :  384 :   0.52%
UDB Total Pterms              :    2 :      :      : 
UDB Datapath Cells            :    1 :   23 :   24 :   4.17%
UDB Status Cells              :    0 :   24 :   24 :   0.00%
UDB Control Cells             :    1 :   23 :   24 :   4.17%
            Control Registers :    1 
DMA Channels                  :    0 :   24 :   24 :   0.00%
Interrupts                    :    0 :   32 :   32 :   0.00%

このように、マクロセルの消費が大幅に減ったことがわかります。

タイミング余裕の比較

今回の実装は、順序回路で組んであるので、クロック周波数が高くなると、タイミング制約を満たしません。 PSoC Creator のタイミングレポートを確認してみました。

Verilog での実装
Clock Domain Nominal Frequency Required Frequency Maximum Frequency Violation
CyILO CyILO 1.000 kHz 1.000 kHz N/A
CyIMO CyIMO 3.000 MHz 3.000 MHz N/A
CyMASTER_CLK CyMASTER_CLK 24.000 MHz 24.000 MHz N/A
Clock_400Hz CyMASTER_CLK 400.000  Hz 400.000  Hz 64.123 MHz
CyBUS_CLK CyMASTER_CLK 24.000 MHz 24.000 MHz 108.108 MHz
CyPLL_OUT CyPLL_OUT 24.000 MHz 24.000 MHz N/A
データパスでの実装
Clock Domain Nominal Frequency Required Frequency Maximum Frequency Violation
CyILO CyILO 1.000 kHz 1.000 kHz N/A
CyIMO CyIMO 3.000 MHz 3.000 MHz N/A
CyMASTER_CLK CyMASTER_CLK 24.000 MHz 24.000 MHz N/A
Clock_400Hz CyMASTER_CLK 400.000  Hz 400.000  Hz 42.838 MHz
CyBUS_CLK CyMASTER_CLK 24.000 MHz 24.000 MHz 45.354 MHz
CyPLL_OUT CyPLL_OUT 24.000 MHz 24.000 MHz N/A

このように、 Verilog で書いた場合には 64MHz での動作も可能だが、データパスで書いた場合には、 42MHz が最大周波数になってしまいます。 信号伝達経路を確認した所、データパスの入力 "cs_addr" のセットアップ時間がもっとも厳しいようです。 このあたりは、 PSoC が内蔵する UDB の構成に関わってきますので、いたしかた無いところでしょうか。

Timer でも、同じことができる

Timer での実装

ここまでは、カスタムコンポーネントを作成して分周器を構成してきました。 おなじ機能は、 Timer コンポーネントでも実現することができます。

これまでの構成と同様に enable 入力を外部に持たせて、 Control Reg で制御するようにしてあります。

/* ========================================
 *
 * 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()
{
    /* Place your initialization/startup code here (e.g. MyInst_Start()) */
    Divider_Start();
    CR1_Write(1);

    /* CyGlobalIntEnable; */ /* Uncomment this line to enable global interrupts. */
    for(;;)
    {
        /* Place your application code here. */
    }
}

/* [] END OF FILE */

テストプログラムも、初期化を行うだけで終わりです。

消費資源を調べてみました。

Resource Type                 : Used : Free :  Max :  % Used
============================================================
Digital domain clock dividers :    1 :    7 :    8 :  12.50%
Analog domain clock dividers  :    0 :    4 :    4 :   0.00%
Pins                          :    4 :   68 :   72 :   5.56%
UDB Macrocells                :    3 :  189 :  192 :   1.56%
UDB Unique Pterms             :    2 :  382 :  384 :   0.52%
UDB Total Pterms              :    3 :      :      : 
UDB Datapath Cells            :    1 :   23 :   24 :   4.17%
UDB Status Cells              :    1 :   23 :   24 :   4.17%
            StatusI Registers :    1 
UDB Control Cells             :    1 :   23 :   24 :   4.17%
            Control Registers :    1 
DMA Channels                  :    0 :   24 :   24 :   0.00%
Interrupts                    :    0 :   32 :   32 :   0.00%

先にデータパスを使って較正した場合に比べて、ちょっとだけ多めに資源を使っています。 これは、カスタムコンポーネントでは取り入れていないステータスレジスタを装備している事などが原因となっています。 標準コンポーネントを使っていて、どうしても資源が足りなくなってきたら、省資源に徹したカスタムコンポーネントを作成するのも、手段の一つかもしれません。 まあ、あまりお勧めはできませんが。

プロジェクトアーカイブ

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

関連書籍


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

nice! 0

コメント 0

コメントを書く

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

トラックバック 0

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

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