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 画面を示す。
Solution Configuration 画面を示す。 Clock Period に 96MHz を設定し、Part は Zybo Z7-20 に設定した。
ソースコードやテストベンチを設定した後のuart_rx プロジェクトを示す。
コメント