PSoCでCPUからデータパスに並列で値を渡す

Send values from PSoC CPU to Datapath in parallel
 
前回でデータパス編集ツールの基本を学びましたので、その応用編と参ります。

やりとりする方法はいくつかありますが、比較的易しい、並列入力(Parallel in)を使ったデータ転送を取り上げます。まとまった値を一度に転送することができ、プログラムとしても理解しやすいですが、8ビット値以外は扱えません。

cy_psoc3_dp #(.cy_dpconfig( { // 設定レジスタ } )) dp/* 名称(APIとして使用) */( // 入出力データ .clk(clock), .po(led_out) ); 入出力データの「po」にレジスタ(もしくはワイヤ)を指定すると、クロックごとにA0レジスタの値がここに反映されます。

以上を踏まえて、フルスクラッチで7セグLEDドライバーを作ってみましょう。プロジェクトを新規作成して、シンボルウィザードを使ってカスタムコンポーネントを追加します。出力端子のプロパティ画面の「Bit Range」を「7:0」にすると8本のワイヤを並列に扱うことができるようになります。
Verilogファイルに含まれる「cy_psoc3_dp」は、データパス設定ツールのほぼ初期値のままで、使用しない入出力パラメーターは削除しています。
7seg.v
`include "cypress.v"

module pa_out (
	input   clock,
	output[7:0] led_out
);

cy_psoc3_dp #(.cy_dpconfig(
{
    `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: */
    `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, /*CFGRAM1:  */
    `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, `SC_PI_DYN_DS,
    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(
    .clk(clock),
    .cs_addr(3'b000),
    .po(led_out)
);

endmodule
あとはC言語から対象コンポーネントのA0レジスタを直接制御すればデータパスに値を設置することができます。しかしながら、都度「CY_SET_REG8(led_out_dp__A0_REG, v)」のようにレジスタ書き込みマクロをプログラムに逐次記述してしまうと、設計者以外にはそれが何のことであるかがさっぱり伝わりません。なので、API(生成されるアプリソースファイルのひな形)を用意することで、その問題に対応することにしましょう。

プロジェクト「Components」タブにある、カスタムコンポーネントを右クリックして「Add Component Item」を選び、一覧から「API Header File」を選んでテンプレートファイルを作成します。
テンプレートファイル用マクロには「CY_MAJOR_VERSION」などいくつかの組み込みパラメーターがありますが、基本的に「`$INSTANCE_NAME`」をAPIの名称に含ませると考えましょう。

この例では、7セグLEDの数値に応じたa~gランプの定義と、値を設置するAPIのひな形を登録しています。どのピンがどのランプにつながるかはメーカーにより異なってくるため、こちらはプログラム側で定義することにしています。
pa_out_api.h
#ifndef __`$INSTANCE_NAME`_H__
#define __`$INSTANCE_NAME`_H__

#ifndef __PA_OUT_DEFINED__
#define __PA_OUT_DEFINED__

#define SEG_0   (SEG_LA | SEG_LB | SEG_LC | SEG_LD | SEG_LE | SEG_LF)
#define SEG_1   (SEG_LB | SEG_LC)
#define SEG_2   (SEG_LA | SEG_LB | SEG_LD | SEG_LE | SEG_LG)
#define SEG_3   (SEG_LA | SEG_LB | SEG_LC | SEG_LD | SEG_LG)
#define SEG_4   (SEG_LB | SEG_LC | SEG_LF | SEG_LG)
#define SEG_5   (SEG_LA | SEG_LC | SEG_LD | SEG_LF | SEG_LG)
#define SEG_6   (SEG_LA | SEG_LC | SEG_LD | SEG_LE | SEG_LF | SEG_LG)
#define SEG_7   (SEG_LA | SEG_LB | SEG_LC | SEG_LF)
#define SEG_8   (SEG_LA | SEG_LB | SEG_LC | SEG_LD | SEG_LE | SEG_LF | SEG_LG)
#define SEG_9   (SEG_LA | SEG_LB | SEG_LC | SEG_LD | SEG_LF | SEG_LG)

#endif

#define `$INSTANCE_NAME`_SetValue(v) CY_SET_REG8(`$INSTANCE_NAME`_dp__A0_REG, v)

#endif
これで7セグドライバーの作成は完了です。続いてメインプログラムの作成に移りましょう。

トップデザインはこのようになります。出力ピンの設定で「Mapping→Display as bus」にチェックを入れることで、コンポーネントと接続することができるようになります。
なおこの場合は、指定できるピンもバス単位となり、割り当ても固定となります。
並列データから独立した線を引き出したい場合はassignを使います。
paltosingle.v
module pa_out (
    input  clock,
    output led1,
    output led2,
    output led3
);

reg [7:0] led_out;

assign led1 = led_out[2];
assign led2 = led_out[4];
assign led3 = led_out[6];

endmodule
こちらは余談ですが、「Component Catalog」にある「Off-Chip」ライブラリーを使うと、簡単な回路図をデザインとして埋め込むことができます。これはプログラムを使用する人にとって見やすくするためのもので、コンパイルやマイコンの実行結果には一切影響しません。
こちらはソフトウェアサンプルです。一定回数ごとにLED点灯の配置を変更し、10回ごとにドットLEDを明滅させています。SEG_LA~SEG_LGの値はお使いの7セグLEDのピン配置に応じて値を変えてください。
7seg_main.c
#include "project.h"

// LED number -> Pin's position
#define SEG_LA   0b00000001
#define SEG_LB   0b00000010
#define SEG_LC   0b00000100
#define SEG_LP   0b00001000
#define SEG_LD   0b00010000
#define SEG_LE   0b00100000
#define SEG_LF   0b10000000
#define SEG_LG   0b01000000

const uint8_t seg_pos[] =
{
    SEG_0, SEG_1, SEG_2, SEG_3, SEG_4,
    SEG_5, SEG_6, SEG_7, SEG_8, SEG_9
};

int main(void)
{
    CyGlobalIntEnable;

    int count = 0;
    uint8_t n = 0, pt = 0;

    for(;;)
    {
        count++;
        if(count == 5000000){
            count = 0;
            pa_out_1_SetValue(seg_pos[n] | pt);
            n++;
            if(n >= 10){
                n = 0;
                pt = (pt) ? 0x00 : SEG_LP;
            }
        }
    }
}

2019/06/15