| 2019年7月10日

PSoCのプログラマブル論理回路は非常にタイトで、32bitレジスタを複数用意して制御しようとしただけでも許容値を超えてしまいます。そのようなリソース量を節約するための方法として、PSoC内蔵の7bitカウンタモジュールを使うというのがあります。
cy_psoc3_count7 #(.cy_period(7'b111), .cy_route_ld(1), .cy_route_en(1))
DataCounter(
    .clock(clock),
    .reset(1'b0),
    .load(count_reset),
    .enable(1'b1),
    .count(count),
    .tc(data_tc)
);
cy_period周期。カウンターの値がこの値を上回ると、値はリセットされます。
cy_route_ldloadパラメーターを使用するかどうか(1で使用)
cy_route_enenableパラメーターを使用するかどうか
clockカウンターを増やすために使用するクロック
resetカウンターのハードウェアをリセットする
loadカウンターの値をリセットする。resetと違い、カウンター値以外に影響はしない
enableカウンターの有効状態。cy_route_enが0ならこの値は無視される
countここで指定したレジスタに現在のカウンター値が格納される
tcカウンターが一巡した場合に、このレジスタに1が格納される


カウンターを有効にするに当たり注意しておきたいのが、Verilog内だけではなくC言語(CPU)からも有効にさせる命令を実行させなければいけない点です(英語ドキュメントには具体例が言及されているのですが、日本語ドキュメントでは抜け落ちています)。

カウンターを有効にするには「`$INSTANCE_NAME`_[関数で指定した名前]__CONTROL_AUX_CTL_REG」の6ビット目をHighにします。
#define `$INSTANCE_NAME`_enable()  *((reg8*)`$INSTANCE_NAME`_DataCounter__CONTROL_AUX_CTL_REG) |= 0x20
このサンプルでは8カウント目に一巡するとdata_tc値がHighになるので、そのたびにLEDが明滅を繰り返します。また、ボタンとcount_resetは直結されており、ボタンを押している間は常にカウンター値がリセットされるため、その間は全く動いていないように見えます。
`include "cypress.v"

module ctr (
    output  result,
    input   clock,
    input   count_reset
);

wire data_tc;
reg [6:0] count; 
reg result_reg;
 
cy_psoc3_count7 #(.cy_period(7'b111),.cy_route_ld(1),.cy_route_en(1))
DataCounter(
    .clock(clock),
    .reset(1'b0),
    .load(count_reset),
    .enable(1'b1),
    .count(count),
    .tc(data_tc)
);
    
always @(posedge clock)
begin
    if(data_tc)
    begin
        result_reg = ~result_reg;
    end
end

assign result = result_reg;

endmodule
 | 2019年6月28日

カスタムコンポーネントのDMA定義ファイル(*.cydmacap)のLocationタグで「direction="destination"」を指定すると、CPUのメモリー領域からデータパスへのDMAを使った転送が行えるようになります。
<?xml version="1.0" encoding="us-ascii"?>
<DMACapability>
  <Category name="led_out" 
            enabled="true" 
            bytes_in_burst="1"
            bytes_in_burst_is_strict="true" 
            spoke_width="1" 
            inc_addr="false" 
            each_burst_req_request="true">
    <Location name="`$INSTANCE_NAME`_dma_ptr" enabled="true" direction="destination"/>
  </Category>
</DMACapability>
私が調べた限り、データパス(ペリフェラル)への転送は最大16bitまでで、32bitはサポートされていないようなので、ここでは8bit DMAの例を紹介します。
DMA転送ではFIFOバッファを使うことが推奨されています。しかしながら、FIFOバッファからのAレジスタへのコピーで1サイクルが使用されるため、LEDの点滅など非常に低い周期での出力となると、タイミングがずれる原因となります。ここではその解決法の一例として、データパスレジスタを制御する高速なクロックと、GPIOの出力を制御する低速なクロックを分けて実装しています。
`include "cypress.v"

module led_out (
    input   clock,
    input   sync,
    output  led_out,
    output  dma
);

localparam PG_IDLE  = 3'b000;
localparam PG_COPY  = 3'b001;
localparam PG_SHIFT = 3'b010;
localparam PG_WAIT  = 3'b011;

reg[2:0] pg_state;
reg[3:0] bit_count;

wire f0_not_full;
wire f0_is_empty;

reg sync_edge;
reg shift_out;
reg led_out_reg;

always @(posedge clock)
begin
    case(pg_state)
    PG_IDLE:
    begin
        if(~f0_is_empty)
            pg_state <= PG_COPY;
    end
    
    PG_COPY:
    begin
        bit_count <= 4'd0;
        pg_state <= PG_SHIFT;
        sync_edge <= sync;
    end
    
    PG_SHIFT:
    begin
        led_out_reg <= shift_out;
        bit_count <= bit_count + 4'd1;
        pg_state <= (bit_count[3]) ? PG_IDLE : PG_WAIT;
    end
    
    PG_WAIT:
    begin
        if(sync_edge != sync)
        begin
            sync_edge <= sync;
            pg_state <= PG_SHIFT;
        end
    end

    endcase
end

assign led_out = led_out_reg;
assign dma = f0_not_full;

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:A0<=F0*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP___SL, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM2:SHIFT 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, /*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_ASYNC,`SC_EXTCRC_DSBL,
    `SC_WRK16CAT_DSBL /*CFG17-16:*/
}
)) dp(
    .reset(1'b0),
    .clk(clock),
    .cs_addr(pg_state),
    .route_si(1'b0),
    .route_ci(1'b0),
    .f0_load(1'b0),
    .f1_load(1'b0),
    .d0_load(1'b0),
    .d1_load(1'b0),
    .so(shift_out),
    .f0_bus_stat(f0_not_full),
    .f0_blk_stat(f0_is_empty)
);

endmodule
FIFO入力時において、バッファが満杯でないときは、データパスのf0_bus_stat値がHighになります。そこで、dma出力にこの値を紐付けることで、DMAによる転送が完了したタイミングを伝えています。A0レジスタにコピーしてからは、8bit分シフトアウトを実行し、はみ出した値はGPIOにそのまま出力されます。
DMAウィザードではSourceにメモリー領域であるSRAMを、Destinationには上記のデザイン図のように配置したDMAコンポーネントを指定します。

PSoCではIndexed DMAという手法によって、複数のTDを連結させることができ、待機中のTD領域にデータを書き込んでいくことでダブルバッファとして機能します。「Number of TDs」の値を変更して、複数のDMAデータを設定してみましょう。
各TDには異なるバッファアドレスを与えます。
FIFOバッファに空きが出るごとに配置したInterruptコンポーネントを介して割り込みが発生します。この割り込み関数内でCyDmaChStatusで取得できる値が現在のTDの保有するIDなので、待機中となっているもう片方のバッファに任意のデータをコピーします。
#include "project.h"

uint8_t send_buffer_pos;
uint8_t send_buffer[2][24];
const uint8_t send_buffer_data[] = {
    0b11111111, 0b00000000, 0b11111111, 0b00000000,
    0b11110000, 0b11110000, 0b11110000, 0b11110000,
    0b11001100, 0b11001100, 0b11001100, 0b11001100,
    0b10101010, 0b10101010, 0b10101010, 0b10101010,
    0b11001100, 0b11001100, 0b11001100, 0b11001100,
    0b11110000, 0b11110000, 0b11110000, 0b11110000,
};

/* Defines for led_dma */
#define led_dma_BYTES_PER_BURST 1
#define led_dma_REQUEST_PER_BURST 1
#define led_dma_SRC_BASE (CYDEV_SRAM_BASE)
#define led_dma_DST_BASE (CYDEV_PERIPH_BASE)

/* Variable declarations for led_dma */
/* Move these variable declarations to the top of the function */
uint8 led_dma_Chan;
uint8 led_dma_TD[2];

void InitDma()
{
    /* DMA Configuration for led_dma */
    led_dma_Chan = led_dma_DmaInitialize(led_dma_BYTES_PER_BURST, led_dma_REQUEST_PER_BURST, 
        HI16(led_dma_SRC_BASE), HI16(led_dma_DST_BASE));
    led_dma_TD[0] = CyDmaTdAllocate();
    led_dma_TD[1] = CyDmaTdAllocate();
    CyDmaTdSetConfiguration(led_dma_TD[0], 24, led_dma_TD[1], led_dma__TD_TERMOUT_EN | CY_DMA_TD_INC_SRC_ADR);
    CyDmaTdSetConfiguration(led_dma_TD[1], 24, led_dma_TD[0], led_dma__TD_TERMOUT_EN | CY_DMA_TD_INC_SRC_ADR);
    CyDmaTdSetAddress(led_dma_TD[0], LO16((uint32)send_buffer[0]), LO16((uint32)led_out_dma_ptr));
    CyDmaTdSetAddress(led_dma_TD[1], LO16((uint32)send_buffer[1]), LO16((uint32)led_out_dma_ptr));
    CyDmaChSetInitialTd(led_dma_Chan, led_dma_TD[0]);
    CyDmaChEnable(led_dma_Chan, 1);   
}

void CopyDmaBuffer()
{
    uint8_t cid;
    CyDmaChStatus(led_dma_Chan, &cid, NULL);
    
    uint8_t bpos = (cid == led_dma_TD[1]) ? 0 : 1; 
    memcpy(send_buffer[bpos], send_buffer_data, 24);
}

CY_ISR(OnInterruptLed)
{
    CopyDmaBuffer();
}

int main(void)
{
    CyGlobalIntEnable;
    
    memcpy(send_buffer[0], send_buffer_data, 24);
    memcpy(send_buffer[1], send_buffer_data, 24);
   
    InitDma();
    led_dma_isr_StartEx(OnInterruptLed);

    for(;;)
    {
    }
}
このプログラムでは同じデータを延々と上書きコピーしているだけので、ダブルバッファの意味は薄いのであしからず。
 | 2019年6月21日

前回のシフト挿入の逆を行えば、CPU(C言語)で代入した32bitレジスト値をデータコンポーネントから出力させることができます。

C言語で対象のアドレスに値を代入するとデータコンポーネントの「f0_bus_stat」がHighになります。厳密にはf0_bus_statの4bit全てがHighになっているかを調べる必要がありますが、レジスタへの代入は一度に行われているので、1bitだけのチェックでも問題ありません。

データパスの「A0 WR SRC=F0」によってA0レジスタに値をコピーしてから、左シフトを行うと、各データブロックのはみ出したビットは「cy_psoc3_dp」の「so」パラメータに記録されます。出力された4bitのうち、so[3]が最上位ビットなので、この値を32bit分読み取ると、C言語で入力した32bit値を全て読み取ることになります。
`include "cypress.v"

module sreg_out (
    output  isr,
    output  data,
    input   clock
);

localparam PG_IDLE    = 3'b000;
localparam PG_COPY_F0 = 3'b001;
localparam PG_SHIFTOUT = 3'b010;

reg [2:0] pg_state;
reg [3:0] so_32;
reg [4:0] out_count;
reg f0_empty;
reg f0_not_empty;

always @(posedge clock)
begin
    case(pg_state)
    PG_IDLE:
    begin
        if(f0_not_empty)
        begin
            pg_state = PG_COPY_F0;
        end
    end
    PG_COPY_F0:
    begin
        out_count = 5'd0;
        pg_state = PG_SHIFTOUT;
    end
    PG_SHIFTOUT:
    begin
        out_count = out_count + 5'd1;
        if(out_count == 5'd0)
        begin
            pg_state = PG_IDLE;
        end
    end
    endcase
end

assign isr = f0_empty;
assign data = so_32[3];

cy_psoc3_dp32 #(
.cy_dpconfig_a({
}),
.cy_dpconfig_b({
}),
.cy_dpconfig_c({
}),
.cy_dpconfig_d({
}))dp(
    /*  input              */  .clk(clock),
    /*  input   [02:00]    */  .cs_addr(pg_state),
    /*  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  [03:00]    */  .f0_bus_stat(f0_not_empty),
    /*  output  [03:00]    */  .f0_blk_stat(f0_empty),
    /*  output  [03:00]    */  .f1_bus_stat(),
    /*  output  [03:00]    */  .f1_blk_stat(),
    /*  output  [03:00]    */  .so(so_32)
);

endmodule

今回のサンプルでは、32bit分シフトが完了するごとに割り込み信号を発生させ、そのタイミングで新しい値を代入しています。
値がそのままピン出力の状態になるため、LEDの点滅期間が短くなる→長くなるが繰り返されます。
#include "project.h"

int ppos = 0;

const uint32_t patterns[] = {
    0b11111111000000001111111100000000,
    0b11110000111100001111000011110000,
    0b11001100110011001100110011001100,
    0b10101010101010101010101010101010,
    0b11001100110011001100110011001100,
    0b11110000111100001111000011110000,
};

CY_ISR(OnInterruptSO)
{
    sreg_out_SetValue(patterns[ppos]);
    ppos++;
    if(ppos == 6) ppos = 0;
}

int main(void)
{
    CyGlobalIntEnable;

    so_isr_StartEx(OnInterruptSO);

    for(;;)
    {
    }
}
 | 2019年6月19日

PSoCのデータパスは8bitサイズですが、複数のデータパスを連結することで最大32bit幅を持つレジスタを取り扱うことができます。

16bit幅以上を扱うデータパスで、任意の値をレジスタに格納する最も単純な方法は、シフト入力による1bit単位での生成です。このテクニックを身につければ、効率の良さは別にして、様々なデジタル信号をハードウェアレベルで扱えるようになるでしょう。

新規プロジェクトでカスタムコンポーネントを追加し、Verilogファイルを作成し、データパス設定ツールで32bitデータパスを作成します。
設定ツールには「.cy_dpconfig_a」から「.cy_dpconfig_d」までの4つのデータパスが作成されます。まずはこれらを連結しましょう。PSoCの公式ドキュメントでは、データパスを連結するためのモデルケースが提供されています。
この図を参考に「cy_dpconfig_b」と「cy_dpconfig_c」の値をそれぞれ「CI SELB/CI SELA→CHAIN」「CHAIN1/CHAIN0→CHAIN」「CHAIN FB→CHAIN」「CHAIN CMSB→CHAIN」「SI SELB/SI SELA→CHAIN」に、「cy_dpconfig_d」をの値をそれぞれ「CI SELB/CI SELA→CHAIN」「CHAIN1/CHAIN0→CHAIN」「CHAIN FB→CHAIN」「CHAIN CMSB→NOCHN」「SI SELB/SI SELA→CHAIN」に設定します。
32bit値のため、中間ブロックに位置する「UDB_b」の設定は2回適用されていることに気をつけてください。

「SHIFT」の値を「SL」にすると、SRCA(この場合はA0)に対して左シフト演算が行われます。
このとき、シフト演算が行われたSRCAに新しく挿入されるビット値を「SI SELA」で指定できます。「DEFSI」なら「DEF SI」で指定した値(DEF_1なら"1")、「REGIS」なら直前の演算で追い出された(キャリーアウト)値が、「CHAIN」なら連結されているデータパスのビット値になります(つまり、シフトアウトした値が次のデータパスに移動する)。

「ROUTE」を選択するとroute_siの値が適用されるというのが肝です。早い話、シフト演算を実行するときにroute_siに紐付けたレジスタ値を変更すれば、任意の値を生成できるというわけです。

こちらはシフト演算で任意の32bit値を作成するサンプルです。一定周期ごとに1をシフト挿入して、5ビット値のcountがオーバーフローして0になる(=32回)タイミングでFIFO処理を実行させています。
`include "cypress.v"

module shift_reg (
    output  isr,
    input   clock
);

localparam REG_IDLE  = 3'd0;
localparam REG_SHIFT = 3'd1;

reg f0_load_flag;
reg si_value;
reg[4:0] bit_count;
reg[2:0] reg_state;

reg[3:0] step;
reg[4:0] step_count;

always @(posedge clock)
begin
case(reg_state)
    REG_IDLE:
    begin
        f0_load_flag = 1'b0;
        
        step_count = step_count + 1'b1;
        if(step_count == step)
        begin
            si_value = 1'b1;
            step_count = 1'b0;
            step = step + 1'b1;
        end
        else
        begin
            si_value = 1'b0;
        end
        
        reg_state = REG_SHIFT;
    end
    REG_SHIFT:
    begin
        bit_count = bit_count + 5'd1;
        f0_load_flag = (bit_count == 5'd0);
        reg_state = REG_IDLE;
    end
endcase
end

cy_psoc3_dp32 #(
.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___SL, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM1:  SHIFT*/
    `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_ROUTE, /*CFG13-12:   */
    `SC_A0_SRC_ACC, `SC_SHIFT_SL, 1'h0,
    1'h0, `SC_FIFO1_BUS, `SC_FIFO0__A0,
    `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:   */
}),
.cy_dpconfig_b({
    `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___SL, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM1:  SHIFT*/
    `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_CHAIN,
    `SC_CI_A_CHAIN, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL,
    `SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_CHAIN,
    `SC_SI_A_CHAIN, /*CFG13-12:   */
    `SC_A0_SRC_ACC, `SC_SHIFT_SL, 1'h0,
    1'h0, `SC_FIFO1_BUS, `SC_FIFO0__A0,
    `SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_CHNED,
    `SC_FB_CHNED, `SC_CMP1_CHNED,
    `SC_CMP0_CHNED, /*CFG15-14:   */
        10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX,
    `SC_FIFO__EDGE,`SC_FIFO_ASYNC,`SC_EXTCRC_DSBL,
    `SC_WRK16CAT_DSBL /*CFG17-16:   */
}),
.cy_dpconfig_c({
    `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___SL, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM1:  SHIFT*/
    `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_A0_D1, `SC_CMPA_A1_D1, `SC_CI_B_CHAIN,
    `SC_CI_A_CHAIN, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL,
    `SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_CHAIN,
    `SC_SI_A_CHAIN, /*CFG13-12:   */
    `SC_A0_SRC_ACC, `SC_SHIFT_SL, 1'h0,
    1'h0, `SC_FIFO1_BUS, `SC_FIFO0__A0,
    `SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_CHNED,
    `SC_FB_CHNED, `SC_CMP1_CHNED,
    `SC_CMP0_CHNED, /*CFG15-14:   */
        10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX,
    `SC_FIFO__EDGE,`SC_FIFO_ASYNC,`SC_EXTCRC_DSBL,
    `SC_WRK16CAT_DSBL /*CFG17-16:   */
}),
.cy_dpconfig_d({
    `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___SL, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM1:  SHIFT*/
    `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_A0_D1, `SC_CMPA_A1_D1, `SC_CI_B_CHAIN,
    `SC_CI_A_CHAIN, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL,
    `SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_CHAIN,
    `SC_SI_A_CHAIN, /*CFG13-12:   */
    `SC_A0_SRC_ACC, `SC_SHIFT_SL, 1'h0,
    1'h0, `SC_FIFO1_BUS, `SC_FIFO0__A0,
    `SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN,
    `SC_FB_CHNED, `SC_CMP1_CHNED,
    `SC_CMP0_CHNED, /*CFG15-14:   */
        10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX,
    `SC_FIFO__EDGE,`SC_FIFO_ASYNC,`SC_EXTCRC_DSBL,
    `SC_WRK16CAT_DSBL /*CFG17-16:   */
}))dp(
    .clk(clock),
    .cs_addr(reg_state),
    .route_si(si_value),
    .route_ci(1'b0),
    .f0_load(f0_load_flag),
    .f1_load(1'b0),
    .d0_load(1'b0),
    .d1_load(1'b0),
    .f0_bus_stat(isr),
    .f0_blk_stat(),
    .f1_bus_stat(),
    .f1_blk_stat()
);

endmodule
なお、対象のレジスタを32bit値として取得するには「CY_GET_REG32」マクロを使います。
#ifndef __`$INSTANCE_NAME`_H__
#define __`$INSTANCE_NAME`_H__

#define `$INSTANCE_NAME`_GetValue() CY_GET_REG32(`$INSTANCE_NAME`_dp_u0__F0_REG)
    
#endif
 | 2019年6月18日

PSoCの標準DMAコンポーネントとやりとりする手段を用意さえすれば、カスタムコンポーネントでDMAによるデータ転送を実装することができます。

標準DMAコンポーネントに自身の仕様を伝えるためには、「DMA Capability」というXMLで構成された宣言ファイルを作成して、コンポーネントに追加する必要があります。このファイルを作成するには、「Add Component Item」より「DMA Capability」を選択します。
<?xml version="1.0" encoding="us-ascii"?>
<DMACapability>
  <Category name="pa_in_fifo" 
            enabled="true" 
            bytes_in_burst="1"
            bytes_in_burst_is_strict="true" 
            spoke_width="1" 
            inc_addr="false" 
            each_burst_req_request="true">
    <Location name="`$INSTANCE_NAME`_DMA_PTR" enabled="true" direction="source"/>
  </Category>
</DMACapability>
内容
Categoryタグパラメーターを格納する。複数記述すれば、DMAコンポーネントで個別の値を適用させることができるようになる。
nameカテゴリーを識別するために使う名称
enabledこのカテゴリーを利用可能にする
bytes_in_burst一度(バースト)に転送するメモリーサイズ(byte単位)
bytes_in_burst_is_strictbytes_in_burstの値を強制し、ユーザーにオプション指定をできないようにさせる。
spoke_widthレジスタのサイズ(byte単位)。例えば標準FFタイマーコンポーネントは16bitサイズなので"2"が適用されている
inc_addr転送ごとにアドレスの値を増やしていくか
each_burst_req_requestバースト転送ごとにDMAの要求を行うか。falseなら初回だけ要求を出し、あとは自動で転送が行われる。
Locationタグ転送元/転送先の定義を記述
name対象のアドレス名。APIヘッダファイルで定義したものを使う。
enabledこのアドレスを使用可能にする
directionアドレスが転送元(Source)か転送先(Destination)か、あるいは両方(Both)か

並列入力のサンプルではレジストが8bitなので、上記コードの値を使用しています。

APIヘッダーではFIFOレジスタのアドレスポインタを取得するマクロを定義します。
#ifndef __`$INSTANCE_NAME`_H__
#define __`$INSTANCE_NAME`_H__

#define `$INSTANCE_NAME`_DMA_PTR ((reg8*)`$INSTANCE_NAME`_dp__F0_REG)
    
#endif
トップデザインの編集ではカスタムコンポーネントとInterruptコンポーネントの間にDMAコンポーネントを挟みます。これにより、DMAコンポーネントによる転送が完了するとNRQ信号が発生し、割り込みが実行されます。
DRQ(DMAリクエスト)端子を表示するにはDMAコンポーネントの「Hardware Request」を「Disable」以外にします。「Hardware Termination」を有効にすると、DMA転送の中止信号を受け取るための端子が表示されます。

「Hardware Request」のオプションには「Derived」「Rising Edge」「Level」の3つがあります。イベントなど不定期に転送が実行される場合は「Rising Edge」を、I2Sのように継続的に転送が実行される場合は「Level」を選択するとよいでしょう。「Derived」はPSoCが最適な接続を選びますが、カスタムコンポーネントでは原則として「Rising Edge」が適用されます。

デザインが完了したら次はC言語の記述といくわけですが、PSoC CreatorにはDMAに特化したコード生成ツールが用意されているので、こちらを積極的に使っていきましょう。
まずは、コードを生成する元になるプロジェクトとDMAコンポーネントを選びます。
続いて転送元と転送先を指定します。自作のコンポーネントがリストに無かったり、「Set Manually」で希望する値を指定できないようであれば、対象の「cydmacap」ファイルが正しい状態にあるかを確認しましょう。
「Number of TDs」の「TD」は「トランザクションディスクリプタ」を意味し、ここに転送情報が格納されます。これを複数個作成し、それぞれを連結することでDMAチェーンを作成し、ダブルバッファのように扱うことができます。

「Loop」を選択すると、転送アドレスが末端に到達すると先頭アドレスへと自動で戻り、転送が続行されます。「Single Chain」であれば、末端に到達すると転送が終了します。

プログラム実行中にどのTDにデータが転送されているかを知るには「CyDmaChStatus」を使用します(第2引数に現在のTD番号が代入される)。

最後に個々のTDの設定を行います。
TD#TDの番号
Endianエンディアンの変換(バイトスワップ)
Enable trqtrq信号の受信による転送中止の許可
Enable nrq転送完了時にnrq信号を送信するかどうか
LengthDMAが転送するバイトサイズ
Source転送元アドレス
Inc転送元アドレスの値を増やしていくかどうか
Destination転送先アドレス
Inc転送先アドレスの値を増やしていくかどうか
Auto Nextプログラムの指示なしに自動で次のTDを実行するか
Next TD次に連結するTDの番号。最後であれば「End」を選択

これでDMAの初期化コードが生成されるので、メインコードにこの文章を貼り付けます。ただし、define文や永続的に使用する変数がごっちゃになっているので、適切に振り分ける必要があります。
/* Defines for pa_in_dma */
#define pa_in_dma_BYTES_PER_BURST 1
#define pa_in_dma_REQUEST_PER_BURST 1
#define pa_in_dma_SRC_BASE (CYDEV_PERIPH_BASE)
#define pa_in_dma_DST_BASE (CYDEV_SRAM_BASE)

/* Variable declarations for pa_in_dma */
/* Move these variable declarations to the top of the function */
uint8 pa_in_dma_Chan;
uint8 pa_in_dma_TD[1];

uint8 res_buffer[32];

void InitDma()
{
    /* DMA Configuration for pa_in_dma */
    pa_in_dma_Chan = pa_in_dma_DmaInitialize(pa_in_dma_BYTES_PER_BURST, pa_in_dma_REQUEST_PER_BURST, 
        HI16(pa_in_dma_SRC_BASE), HI16(pa_in_dma_DST_BASE));
    pa_in_dma_TD[0] = CyDmaTdAllocate();
    CyDmaTdSetConfiguration(pa_in_dma_TD[0], 32, pa_in_dma_TD[0], pa_in_dma__TD_TERMOUT_EN | CY_DMA_TD_INC_DST_ADR);
    CyDmaTdSetAddress(pa_in_dma_TD[0], LO16((uint32)pa_in_DMA_PTR), LO16((uint32)res_buffer));
    CyDmaChSetInitialTd(pa_in_dma_Chan, pa_in_dma_TD[0]);
    CyDmaChEnable(pa_in_dma_Chan, 1);
}
あとはInterruptコンポーネントの初期化関数を追加すれば完成です。表示される内容は以前のサンプルと同じですが、32バイト単位で一度に表示されていきます。
CY_ISR(OnIntrupptPaInDma)
{
    char str[10];
    for(int n = 0; n < 32; n++){
        sprintf(str, "%x ", res_buffer[n]);
        UART_PutString(str);
    }
}

int main(void)
{
    CyGlobalIntEnable;

    isr_dma_StartEx(OnIntrupptPaInDma);
    InitDma();
    
    UART_Start();

    for(;;)
    {
    }
}
 | 2019年6月18日

これまではデータパスのA0レジスタの値を扱ってきましたが、データパスにはFIFOバッファが備わっており、これらに値を蓄積することで滞りないデータの送受信ができます。特に、連続的なメモリー転送を行うDMAを利用するのであればFIFOの実装は不可欠です。並列入力のサンプルを前回に作ったので、これをFIFOバッファに対応させてみましょう。

FIFOを制御するにはF0/F1レジスタを使用します。データパス設定ツールでVerilogファイルを開いたら、「F0 INSEL」の値を「A0」に変更しましょう。これでA0レジスタがF0レジスタにコピーされるようになります。
F0レジスタへの挿入は「f0_load」で指定したレジスタがHighのときに、クロックごとに実行されます。FIFOバッファにデータ存在するとき、「f0_bus_stat」に指定したレジスタの値がHighになるので、これを出力ワイヤに紐付けることで、いつデータを取得すればよいかを知ることができます。
`include "cypress.v"

module pa_in_fifo (
    input   clock,
    output  isr
);

localparam RG_IDLE = 3'b000;
localparam RG_COPY = 3'b001;
reg [2:0]rg_state;

reg[7:0] pa_in_reg;
reg[1:0] val_type;
reg load_fifo;

always @(posedge clock)
begin
case(rg_state)
    RG_IDLE:
    begin
        load_fifo = 1'b0;
        rg_state = RG_COPY;
    end
    
    RG_COPY:
    begin
        case(val_type)
        2'b00:
        begin
            pa_in_reg = 8'b11111111;
        end
        2'b01:
        begin
            pa_in_reg = 8'b11110000;
        end
        2'b10:
        begin
            pa_in_reg = 8'b11001100;
        end
        2'b11:
        begin
            pa_in_reg = 8'b10101010;
        end
        endcase
        val_type = val_type + 1'b1;
        load_fifo = 1'b1;
        rg_state = RG_IDLE;
    end
endcase
end

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:IDLE*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_ENBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM1:COPY PI to A0*/
    `CS_ALU_OP_PASS, `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:*/
    `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_EN,
    1'h0, `SC_FIFO1_BUS, `SC_FIFO0__A0,
    `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(rg_state),
    .f0_load(load_fifo),
    .f0_bus_stat(isr),
    .pi(pa_in_reg)
);

endmodule
あとは、取得元レジスタをA0からF0に置き換えれば完成です。
#ifndef __`$INSTANCE_NAME`_H__
#define __`$INSTANCE_NAME`_H__

#define `$INSTANCE_NAME`_GetValue() CY_GET_REG8(`$INSTANCE_NAME`_dp__F0_REG)

#endif
#include "project.h"
#include <stdio.h>

CY_ISR(OnIntrupptPaIn)
{
    char str[10];
    sprintf(str, "%x ", pa_in_fifo_GetValue());
    UART_PutString(str);
}

int main(void)
{
    CyGlobalIntEnable;

    isr_dma_StartEx(OnIntrupptPaIn);
    
    UART_Start();

    for(;;)
    {
    }
}
また、並列出力サンプル(7セグドライバー)をFIFOに対応させるには、データパス設定ツールで「A0 WR SRC」の値を「F0」にすれば演算結果をF0レジスタに出力されるので、あとはAPIの参照先レジスタを「CY_SET_REG8(`$INSTANCE_NAME`_dp__F0_REG, 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___F0, `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

 | 2019年6月17日

データパスの並列出力は「po」パラメーターの指定だけで実装できましたが、データパスからCPUレジスタに並列でデータを送るには若干の設定が必要です。

データパス設定ツールを起動したら「PI DYN」の箇所を右クリックして表示されるメニューより「Enable Bit」を選択し、このレジスタ値を変更できるようにします。入力できるようになったら、選択肢より「EN」を選びます。これにより、演算処理実行時にA0もしくはA1レジスタにPIに指定した値がコピーされるようになります。
並列入力ができるコンポーネントのサンプルを作成してみます。PSoCプロジェクトを作成してコンポーネントアイテムを追加したら、演算処理のタイミングを制御するためのクロック入力端子と、値が変更されたことをC言語側で検知できるようにするための割り込み信号出力端子を追加します。
ここからVerilogファイルを作成したら、データパス設定ツールでこのファイルを読み込み、「PI DYN」および「Reg1」の値を変更します。
「Reg1」では「CFB_EN」が「ENBL」になっており、これにより、この番号が実行されるときはALUにPIが格納されるようになります。ただし、それだけではCPUレジスタには反映されないので「A0 WR SRC」を「ALU」にしてA0にコピーするように加えて指示します。

このVerilogサンプルでは、クロックごとにcs_addrへの値を切り替え、1番(アドレス1)を実行する時点で割り込み信号をオンにして、C言語側でイベントを受け取るようにしています。
`include "cypress.v"

module pa_in (
    input   clock,
    output  isr
);

localparam RG_IDLE = 3'b000;
localparam RG_COPY = 3'b001;
reg [2:0]rg_state;

reg[7:0] pa_in_reg;
reg[1:0] val_type;
reg isr_reg;

always @(posedge clock)
begin
case(rg_state)
    RG_IDLE:
    begin
        isr_reg = 1'b0;
        rg_state = RG_COPY;
    end
    
    RG_COPY:
    begin
        case(val_type)
        2'b00:
        begin
            pa_in_reg = 8'b11111111;
        end
        2'b01:
        begin
            pa_in_reg = 8'b11110000;
        end
        2'b10:
        begin
            pa_in_reg = 8'b11001100;
        end
        2'b11:
        begin
            pa_in_reg = 8'b10101010;
        end
        endcase
        val_type = val_type + 1'b1;
        isr_reg = 1'b1;
        rg_state = RG_IDLE;
    end
endcase
end

assign isr = isr_reg;

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:IDLE*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_ENBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM1:COPY PI to 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, `SC_PI_DYN_EN,
    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(rg_state),
    .pi(pa_in_reg)
);

endmodule
C言語でA0レジスタの値を取得するAPIヘッダー例です。
#ifndef __`$INSTANCE_NAME`_H__
#define __`$INSTANCE_NAME`_H__

#define `$INSTANCE_NAME`_GetValue() CY_GET_REG8(`$INSTANCE_NAME`_dp__A0_REG)

#endif
メインプログラムの作成に移ります。UART出力の速度に合わせるため、カスタムコンポーネントのクロックソースでは、周波数を指定倍率で分周する「Freq Divider」を介して、「10Hz / 100」という極めて低い周期を与えています。isrには割り込み処理を実装するためのコンポーネントである「System→Interrupt」を接続します。
割り込みをC言語で実装する方法として、「syapicallbacks.h」と「isr_*.c(生成された割り込みコンポーネントのソースファイル)」の「/* Place your Interrupt code here. */」と表記されている箇所にグローバル関数を組み込む方法と、「isr_*_StartEx(生成された割り込みコンポーネントの関数)」にCY_ISRマクロを使ったコールバック関数を指定する方法があります。単純な比較で言えば、StartExを使った割り込みの方がコード量が少なくて済み、扱いやすいと思います。

このプログラムでは割り込みが発生するたびに、UARTへコンポーネントに格納されているA0レジスタの値をバイナリ文字列として表示します。
#include "project.h"

CY_ISR(OnIntrupptPaIn)
{
    uint8_t v = pa_in_GetValue();
    for(int i = 0; i < 8; i++){
        UART_PutChar((v & 0x80) ? '1' : '0');
        v <<= 1;
    }
}

int main(void)
{
    CyGlobalIntEnable;
    
    isr_pa_in_StartEx(OnIntrupptPaIn);
    UART_Start();

    for(;;)
    {
    }
}
 | 2019年6月17日

前回でデータパス編集ツールの基本を学びましたので、その応用編と参ります。

やりとりする方法はいくつかありますが、比較的易しい、並列入力(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」は、データパス設定ツールのほぼ初期値のままで、使用しない入出力パラメーターは削除しています。
`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のひな形を登録しています。どのピンがどのランプにつながるかはメーカーにより異なってくるため、こちらはプログラム側で定義することにしています。
#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を使います。
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のピン配置に応じて値を変えてください。
#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年6月15日

PSoCのCPUとPLD(プログラマブル論理回路)とデータをやりとりするには、データパスと呼ばれるPSoC独自のシステムを使う必要があります。レジスタをアセンブラのように扱ったり、20箇所以上の設定をビットとして指定したりと、この仕組みを完全に理解しようとすると相当な勉強量が必要になるため、ここではコピー&ペーストですぐに活用できるように重要な点だけ紹介したいと思います。

データパスはVerilogファイルに「cy_psoc3_dp」というPSoC独自の関数を追加することで実装します。しかしながら、この関数に関連する項目をひとつひとつ入力していくとなると大変な労力を要します。その負担を軽減するため、PSoC Creatorには2種類の入力支援ツールが用意されています。

ひとつは「UDB document」で、基本的な値をGUIベースで指定でき、また、覚え書きとしての状態遷移図を作成する機能も付いています(プログラムには影響しない)。

UDB Documentを使うには、プロジェクトの右クリックメニューより「Add Component Item」を選び、一覧より「UDB document」を指定します。
「cyudb」編集画面が表示されたら、左側にある「DP」をこの空白箇所にドラッグ&ドロップします。
実行アドレスとそれに対して実行される処理の一覧が緑色の表として示されています。このリストをダブルクリックすることで編集ができます。

例えば、以下の設定だと、「clkが立ち上がったとき、cs_addrが0番(アドレスが0)であればA0レジスタの値とD0レジスタの値を足し、その計算結果(ALU_out)をA0レジスタに格納する」ことになります。
設定内容はすぐにVerilogファイルとして反映され、その内容は下部にあるタブを切り替えることで確認できます。
このファイルを直接編集することはできないため、独自のプログラムを追加する場合は、このコードを別のファイルへ貼り付ける必要があります。

もうひとつは「Datapath Configuration Tool」を使う方法です。各設定を表形式にして、レジスタ数値を文字列定数に置き換えただけのような見た目ですが、手入力で全て行うよりかは効率的です。

Datapath Configuration ToolはVerilogファイルに含まれる関数を抜き出し、それを編集するツールなので、使うには元となるVerilogファイルが必要となります。プロジェクトで「.v」ファイルを作成したら、PSoC Creatorメニューの「Tools→Datapath Config Tool」を選んでツールを起動します。

空のリストが表示されたら、「File→Open」で先の.vファイルを開きます。
ツールで編集しているときは、元の.vファイルを直接編集してはいけません。ツールの内容を反映させてしまうと内容が上書きされてしまい、これまでのテキストがリセットされてしまいます。

データパスを追加するにはメニューの「Edit→New Datapath」を選びます。次のウィンドウでは作成する変数と名称を記入します。ご察しの通り、最後の数値はレジスタに使用するビット数を示していますが、ここではそれらの基底となってる「cy_psoc3_dp」を選んでいます。
先ほどUDB Documentツールで作成した同等の命令を実装する場合は、以下のように値を変更します。
編集が終わったら保存処理を忘れずに。
 | 2019年6月15日