UARTのloopback回路
実機でテストするための回路としてrxで受信したデータをtxへそのまま送信するループバック回路を作成する。UARTの送受信はUARTモジュールを利用する。 パラメータ
input clk
input reset
input rx
output tx
状態の設計
__idle__
(受信するまで待機)
if uart.rx_empty == false
__read_data__ へ遷移
__read_data__
(データをレジスタへ格納)
r_data_next <= r_data
__read_uart__へ遷移
__read_uart___
rd_uart_next <= 1
__write_data__へ遷移
__wrte_data__
w_data_next <= r_data_reg
__write_uart__へ遷移
__write_uart__
wr_uart_next <= 1
__idle__へ遷移
UART送信に利用するuartモジュールの入出力
input clk
input reset
input rd_uart
input wr_uart
input rx
input (7:0) w_data
input (10:0) dvsr
output tx_full,
output rx_empty
output tx
output (7:0) r_data
コード
code:loopback.sv
// iverilog -g 2012 -s loopback_test *.sv && ./a.out
module loopback(
input logic clk,
input logic reset,
input logic rx,
output logic tx
);
typedef enum {idle, read_data, read_uart, write_data, write_uart} loopback_status;
loopback_status loopback_status_reg, loopback_status_next;
logic rd_uart_reg, rd_uart_next;
logic wr_uart_reg, wr_uart_next;
logic 7:0 r_data, r_data_reg, r_data_next; logic 7:0 w_data_reg, w_data_next; logic rx_empty, rx_empty_reg;
uart ut (
.clk(clk),
.reset(reset),
.rd_uart(rd_uart_reg),
.wr_uart(wr_uart_reg),
.rx(rx),
.w_data(w_data_reg),
.dvsr(11'd651),
.rx_empty(rx_empty),
.tx(tx),
.r_data(r_data)
);
always_ff @(posedge clk, posedge reset) begin
if (reset)
begin
loopback_status_reg <= idle;
rd_uart_reg <= 0;
wr_uart_reg <= 0;
w_data_reg <= 0;
r_data_reg <= 0;
rx_empty_reg <= 1;
end
else
begin
loopback_status_reg <= loopback_status_next;
rd_uart_reg <= rd_uart_next;
wr_uart_reg <= wr_uart_next;
w_data_reg <= w_data_next;
r_data_reg <= r_data_next;
rx_empty_reg <= rx_empty;
end
end
always_comb begin
loopback_status_next = loopback_status_reg;
rd_uart_next = 0;
wr_uart_next = 0;
case (loopback_status_reg)
idle: begin
if (~rx_empty_reg)
loopback_status_next = read_data;
end
read_data: begin
loopback_status_next = read_uart;
r_data_next = r_data;
end
read_uart: begin
loopback_status_next = write_data;
rd_uart_next = 1;
end
write_data: begin
loopback_status_next = write_uart;
w_data_next = r_data_reg;
end
write_uart: begin
loopback_status_next = idle;
wr_uart_next = 1;
end
endcase
end
endmodule
code:loopback_testbench.sv
`timescale 1ns/1ps
module loopback_test();
logic clk, reset, rx, tx;
loopback dut (
.clk(clk),
.reset(reset),
.rx(rx),
.tx(tx)
);
// 5nsごとにclkを反転することで100MHzのクロックを生成
clk <= ~clk;
initial begin
$dumpfile("loopback.vcd");
$dumpvars(0, dut);
clk = 0;
reset = 0;
rx = 1;
reset = 1;
reset = 0;
/** 受信のテスト **/
// 以下のデータビット(0b01010101 = 0x55)を受信
// 1, 0, 1, 0, 1, 0, 1, 0
rx = 0; // スタートビット
rx = 1;
rx = 0;
rx = 1;
rx = 0;
rx = 1;
rx = 0;
rx = 1;
rx = 0;
rx = 1; // ストップビット
// 以下のデータビット(0b00000000 = 0x00)を受信
// 1, 0, 1, 0, 1, 0, 1, 0
rx = 0; // スタートビット
rx = 0;
rx = 0;
rx = 0;
rx = 0;
rx = 0;
rx = 0;
rx = 0;
rx = 0;
rx = 1; // ストップビット
end
endmodule
波形を確認
実行して波形を確認してみる。
$ iverilog -g 2012 -s loopback_testbench *.sv && ./a.out
rxで送信した波形がいい感じにtxに返ってきてるよ!
https://gyazo.com/aa5775c412898548045df0c9596edffc
実機で動かしてみる
以下の制約ファイルを使って実機でloopbackを動かしてみる。
code:top.xdc
# Clock signal
create_clock -period 10.000 -name sys_clk_pin -waveform {0.000 5.000} -add get_ports clk ## Configuration options, can be used for all designs
実機へ書き込んだのち、TeraTermで実機とシリアル接続する。
https://gyazo.com/c34b04b45782dcc7f7faf291e8e5fe48
キーボードからAを送りまくってみる。
https://gyazo.com/4d32a552a2e9fdcf059b6efbf5709fc1
いい感じに動いてそう。