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
使い方
まずはシステムクロックをサンプリング用のクロックへ分周するための値をdvsrへセットする
dvsr = システムクロック / (サンプリング回数 * ボーレート)
(例)
システムクロックが100MHz、UARTの通信速度が9600Hz、サンプリング回数が16回の場合
100,000,000(100Mhz) / (16 * 9600) = 651
dvsrの値は651となる
w_dataに送信したい値をセットしてwr_uartに1を立てると、txを通じてデータが送信される
rxから受信した値はFIFOバッファへと蓄えられ、バッファの先頭の値r_dataへ出力される。rd_uartに1を立てるとFIFOバッファの先頭のバイト値が破棄されて次のバイト値がr_dataへ出力される。
リポジトリ
参考
コード
code:uart.sv
// UART
// iverilog -g 2012 -s uart_testbench uart.sv uart_rx.sv uart_tx.sv baud_gen.sv fifo.sv register_file.sv uart_testbench.sv && ./a.out
module uart(
input logic clk,
input logic reset,
input logic rd_uart,
input logic wr_uart,
input logic rx,
output logic tx_full,
output logic rx_empty,
output logic tx,
);
logic s_tick;
logic tx_done_tick;
logic txfifo_empty;
logic urx_rx_done_tick;
baud_gen bg (
.clk(clk),
.reset(reset),
.dvsr(dvsr),
.tick(s_tick)
);
.clk(clk),
.reset(reset),
.wr(wr_uart),
.rd(tx_done_tick),
.w_data(w_data),
.r_data(txfifo_rdata),
.full(tx_full),
.empty(txfifo_empty)
);
uart_tx utx (
.clk(clk),
.reset(reset),
.tx_start(~txfifo_empty),
.s_tick(s_tick),
.din(txfifo_rdata),
.tx_done_tick(tx_done_tick),
.tx(tx)
);
.clk(clk),
.reset(reset),
.wr(urx_rx_done_tick),
.rd(rd_uart),
.w_data(urx_dout),
.r_data(r_data),
.empty(rx_empty)
);
uart_rx urx (
.clk(clk),
.reset(reset),
.s_tick(s_tick),
.rx(rx),
.dout(urx_dout),
.rx_done_tick(urx_rx_done_tick) // あとで直す
);
endmodule
code:uart_testbench.sv
`timescale 1ns/1ps
module uart_testbench();
logic clk, reset;
logic 7:0 w_data, r_data; logic tx, rx;
logic rd_uart, wr_uart;
logic tx_full, rx_empty;
uart dut (
.clk(clk),
.reset(reset),
.rd_uart(rd_uart),
.wr_uart(wr_uart),
.rx(rx),
.w_data(w_data),
.dvsr(dvsr),
.tx_full(tx_full),
.rx_empty(rx_empty),
.tx(tx),
.r_data(r_data)
);
initial begin
$dumpfile("uart.vcd");
$dumpvars(0, dut);
clk = 0;
rx = 1;
rd_uart = 0;
wr_uart = 0;
w_data = 8'd0;
dvsr = 11'd651; // 100MHz / (16サンプリング * 9600Hz)
reset = 1;
reset = 0;
/** 送信のテスト **/
// 送信データをセット
w_data = 8'h55;
wr_uart = 1;
wr_uart = 0;
// 送信データをセット
w_data = 8'hAA;
wr_uart = 1;
wr_uart = 0;
// FIFOバッファがいっぱいになるまでデータを書き込む
// 0xF0を書き込み(これはOK)
w_data = 8'hF0;
wr_uart = 1;
wr_uart = 0;
// 0x0Fを書き込み(これはOK)
w_data = 8'h0F;
wr_uart = 1;
wr_uart = 0;
// 0x00を書き込み(これはOK)
w_data = 8'h00;
wr_uart = 1;
wr_uart = 0;
// 0xFFを書き込み(これはOK)
w_data = 8'hFF;
wr_uart = 1;
wr_uart = 0;
// 0x00を書き込み(FIFOバッファがいっぱいなので、これは出力されない)
w_data = 8'h00;
wr_uart = 1;
wr_uart = 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; // ストップビット
// 以下のデータビット(0b01010101 = 0xAA)を受信
// 0, 1, 0, 1, 0, 1, 0, 1
rx = 0; // スタートビット
rx = 0;
rx = 1;
rx = 0;
rx = 1;
rx = 0;
rx = 1;
rx = 0;
rx = 1;
// 以下のデータビット(0b00000000 = 0x00)を受信
rx = 0; // スタートビット
rx = 0;
rx = 0;
rx = 0;
rx = 0;
rx = 0;
rx = 0;
rx = 0;
rx = 0;
rx = 1; // ストップビット
// 受信FIFOの先頭の値を取り除く(55からAAになる)
rd_uart = 1;
rd_uart = 0;
// 受信FIFOの先頭の値を取り除く(AAから00になる)
rd_uart = 1;
rd_uart = 0;
// 受信FIFOの先頭の値を取り除く(00も取り除き、不定値が返る)
rd_uart = 1;
rd_uart = 0;
end
// 5nsごとにclkを反転することで100MHzのクロックを生成
clk <= ~clk;
endmodule
波形を確認
$ iverilog -g 2012 -s uart_testbench uart.sv uart_rx.sv uart_tx.sv baud_gen.sv fifo.sv register_file.sv uart_testbench.sv && ./a.out
パッと見それっぽく動いてそう。
https://gyazo.com/b9d393a9085f2958f538038440cbc896