調歩同期方式シリアル通信の受信 IP を Vitis HLS で作成することにした。
Xilinx 社のUART IP として AXI Uartlite があるが、これはボーレートを自分で自由に決めることができない。
PIC マイコンで生成された調歩同期方式シリアル通信データをなるべく高速に受けたいというモチベーションがあるのだが、どうもいつも使っているボーレートにすることが難しいので、任意にボーレートを決定したいためだ。今回は、500kbps のボーレートで通信したい。
今回は、96 MHz のクロックを使用して、それを 12 分周すると 8 MHz になって、ちょうど 500 kHz x 16 となる。つまり、12 分周 x 16 分周でやってみよう。

調歩同期方式シリアル通信については、以前のブログ記事”シリアルインターフェース(RS-232C)の説明”を見て欲しい。ここで、サンプリングするクロックが 500kHz の 16 倍になっている場合が今回の事例となる。そう 12 分周 x 16 分周の 16 分周の部分がサンプリング・クロック周期だ。

さて、ソースコードを貼っておく。
まずは、 uart_rx.h から。

// uart_rx.h
// 2021/02/16 by marsee
//

#ifndef __UART_RX_H__
#define __UART_RX_H__

#include <ap_int.h>
#include <hls_stream.h>

#define DIV_8M  12
#define DIV_500k 16

int uart_rx(hls::stream<ap_uint<1> >& rxst, ap_uint<8>& rx_data);

#endif


ソースコードの uart_rx.cpp を貼っておく。

// uart_rx.cpp
// 2021/02/12 by marsee
// 動作周波数は 96 MHz, 8 MHz x 12
// 32000000/(16*(3+1)) = 500kbps, サンプル周波数 500k x 16 = 8 MHz

#include "uart_rx.h"

bool det_zero_bit(hls::stream<ap_uint<1> >& rxst){
    ap_uint<1> rx;

    LOOP_DZB1: for(int i=0; i<DIV_8M; i++){
        LOOP_DZB2: for(int j=0; j<(DIV_500k/2-1); j++){
#pragma HLS PIPELINE II=1 rewind
            rxst >> rx;
        }
    }
    if(rx == 0)
        return(false); // 0 検出
    else
        return(true);
}

ap_uint<8> det_data_byte(hls::stream<ap_uint<1> >& rxst){
    ap_uint<8> data = 0;
    ap_uint<1> rx;

    LOOP_DDB1: for(int k=0; k<8; k++){
        LOOP_DDB2: for(int i=0; i<DIV_8M; i++){
            LOOP_DDB3: for(int j=0; j<DIV_500k; j++){
#pragma HLS PIPELINE II=1 rewind
                rxst >> rx;
                if(i==(DIV_8M-1) && j==(DIV_500k-1)){
                    data >>= 1;
                    if(rx == 1)
                        data |= (ap_uint<8>)128;
                }
            }
        }
    }
    return(data);
}

int check_stop_bit(hls::stream<ap_uint<1> >& rxst){
    int ret_val;
    ap_uint<1> rx;

    LOOP_CSB1: for(int i=0; i<DIV_8M; i++){
        LOOP_CSB2: for(int j=0; j<DIV_500k; j++){
#pragma HLS PIPELINE II=1 rewind
            rxst >> rx;
            if(i==(DIV_8M-1) && j==(DIV_500k-1)){
                if(rx == 0)
                    ret_val = 1;
                else
                    ret_val = 0;
            }
        }
    }
    return(ret_val);
}

int uart_rx(hls::stream<ap_uint<1> >& rxst, ap_uint<8>& rx_data){
#pragma HLS INTERFACE ap_hs port=rx_data
#pragma HLS INTERFACE ap_ctrl_hs port=return

    int result;
    ap_uint<1> rx;

    LOOP_WAIT: do{
#pragma HLS LOOP_TRIPCOUNT avg=1 max=1 min=1
        rxst >> rx;
        if(rx == 0)
            result = det_zero_bit(rxst);
        else
            result = true;
    }while(result);

    rx_data = det_data_byte(rxst);

    return(check_stop_bit(rxst));
}


テストベンチの uart_rx_tb.cpp を貼っておく。

// uart_rx_tb.cpp
// 2021/02/16 by marsee
// 動作周波数は 96 MHz, 8 MHz x 12
// 32000000/(16*(3+1)) = 500kbps, サンプル周波数 500k x 16 = 8 MHz

#include "uart_rx.h"

int rx_data_gen(ap_uint<8> rxdata, hls::stream<ap_uint<1> >& rxst){
    // start bit
    for(int i=0; i<DIV_8M; i++){
        for(int j=0; j<DIV_500k; j++){
            rxst << 0;
        }
    }

    // data bit
    for(int k=0; k<8; k++){
        for(int i=0; i<DIV_8M; i++){
            for(int j=0; j<DIV_500k; j++){
                rxst << (rxdata & 1);
                if(i==DIV_8M-1 && j==DIV_500k-1){
                    rxdata >>= 1;
                }
            }
        }
    }

    // stop bit
    for(int i=0; i<DIV_8M; i++){
        for(int j=0; j<DIV_500k; j++){
            rxst << 1;
        }
    }
    return(0);
}

int main(){
    hls::stream<ap_uint<1> > rxst;
    ap_uint<8> rxd0, rxd1;

    for(int i=0; i<DIV_8M; i++){
        for(int j=0; j<DIV_500k; j++){
            rxst << 1;
        }
    }
    rx_data_gen(0x55, rxst);
    rx_data_gen(0xAA, rxst);

    uart_rx(rxst, rxd0);
    uart_rx(rxst, rxd1);
    printf("Receive Data = %x, %x\n", (unsigned int)rxd0, (unsigned int)rxd1);

    return(0);
}


Vitis HLS 2020.2 で uart_rx プロジェクトを作成した。

uart_rx プロジェクトを作成する途中の New Vitis HLS Project ダイアログの Project Configuration 画面を示す。
ac05dac6.png


Solution Configuration 画面を示す。 Clock Period に 96MHz を設定し、Part は Zybo Z7-20 に設定した。
98d09823.png


ソースコードやテストベンチを設定した後のuart_rx プロジェクトを示す。
dc021740.png