UART
: Universal Asynchronous Receiver/Transmitter
직렬 통신을 위한 하드웨어 장치 또는 인터페이스
- 비동기 통신 : 클럭 신호 없이 데이터를 전송한다 [Master가 없다].
- 송신기와 수신기는 독립적으로 동작하며, 클럭 신호 대신 baudrate[전송 속도]를 설정하여 동작한다.
- 직렬 전송 : 데이터가 한 비트씩 순차적으로 전송된다. LSB → MSB 순서로 전송한다.
- 데이터 프레이밍 : 시작 비트, 데이터 비트, 패리티 비트[옵션], 정지 비트로 구성된다.
데이터 프레이밍에서 봤듯이 Start Bit, Data Bit, Stop Bit가 전달된다.
위의 그림과 아래의 코드에서는 Parity Bit를 포함하지 않는다.
- IDLE : 1 신호를 보내는 1 bit를 계속 보내며 Data를 보내고 있지 않는다는 신호이다.
- START : Data를 보내겠다는 시작 신호로 0 신호를 보내는 1 bit를 보낸다. 1의 신호를 받다가 0을 받음으로써 신호가 구별된다.
- DATA : 8bit Data 중 LSB부터 8개의 bit를 차례대로 보낸다.
- STOP : Data를 모두 보냈다는 신호로 1의 신호 1bit를 보낸다.
- Parity bit : 데이터 전송 중 오류를 감지하기 위한 비트. 데이터 전체 비트 수를 짝수 또는 홀수로 맞춘다.
- 비트 수를 확인하여 예상한 비트 개수와 다르면 오류를 감지한다.
- 오류를 감지하지만, 어떤 비트가 잘못되었는지 알 수는 없다.
uart.v
[baudrate : 9600]
module uart (
input clk,
input reset,
input start,
input [7:0] tx_data,
output tx_done,
output txd
);
buadrate_generator U_BR_Gen(
.clk(clk),
.reset(reset),
.br_tick(br_tick)
);
transmitter U_TxD (
.clk(clk),
.reset(reset),
.br_tick(br_tick),
.start(start),
.data(tx_data),
.tx_done(tx_done),
.tx(txd)
);
endmodule
module buadrate_generator (
input clk,
input reset,
output br_tick
);
reg [$clog2(100_000_000 / 9600)-1:0] counter_reg, counter_next;
reg tick_reg, tick_next;
assign br_tick = tick_reg;
always @(posedge clk, posedge reset) begin
if (reset) begin
counter_reg <= 0;
tick_reg = 1'b0;
end else begin
counter_reg <= counter_next;
tick_reg = tick_next;
end
end
always @(*) begin
if(counter_reg == 100_000_000 / 9600 -1) begin
// if(counter_reg == 10-1)begin // for simulation
counter_next = 0;
tick_next = 1'b1;
end else begin
counter_next = counter_reg + 1;
tick_next = 1'b0;
end
end
endmodule
module transmitter (
input clk,
input reset,
input br_tick,
input start,
input [7:0] data,
output tx_done,
output tx
);
localparam IDLE = 0, START = 1, STOP = 10;
localparam D0 = 2, D1 = 3, D2 = 4, D3 = 5, D4 = 6, D5 = 7, D6 = 8, D7 = 9;
reg [3:0] state, state_next;
reg [7:0] r_data; //data 임시 저장 공간
reg tx_reg, tx_next;
reg tx_done_reg, tx_done_next;
assign tx = tx_reg;
assign tx_done = tx_done_reg;
// state register
always @(posedge clk, posedge reset) begin
if (reset) begin
state <= IDLE;
tx_reg <= 1'b0;
tx_done_reg <= 1'b0;
end else begin
state <= state_next;
tx_reg <= tx_next;
tx_done_reg <= tx_done_next;
end
end
// next state combinational logic
always @(*) begin
state_next = state;
case (state)
IDLE: if (start) state_next = START;
START: if (br_tick) state_next = D0;
D0: if (br_tick) state_next = D1;
D1: if (br_tick) state_next = D2;
D2: if (br_tick) state_next = D3;
D3: if (br_tick) state_next = D4;
D4: if (br_tick) state_next = D5;
D5: if (br_tick) state_next = D6;
D6: if (br_tick) state_next = D7;
D7: if (br_tick) state_next = STOP;
STOP: if (br_tick) state_next = IDLE;
endcase
end
// output combinational logic
always @(*) begin
tx_next = tx_reg;
tx_done_next = 1'b0;
case (state)
IDLE: tx_next = 1'b1;
START: begin
tx_next = 1'b0;
r_data = data;
end
D0: tx_next = r_data[0];
D1: tx_next = r_data[1];
D2: tx_next = r_data[2];
D3: tx_next = r_data[3];
D4: tx_next = r_data[4];
D5: tx_next = r_data[5];
D6: tx_next = r_data[6];
D7: tx_next = r_data[7];
STOP: begin
tx_next = 1'b1;
if(state_next == IDLE) tx_done_next = 1'b1;
end
endcase
end
endmodule
- DATA State에서 D0부터 D7까지 r_data를 직접 입력해주는 방식이다.
button.v
`timescale 1ns / 1ps
module button (
input clk,
input in,
output out
);
localparam N = 8; // shift 횟수
reg [N-1 : 0] q_reg, q_next;
always @(posedge clk) begin
q_reg <= q_next;
end
// next state logic
always @(q_reg, in) begin
q_next = {in, q_reg[N-1:1]};
end
// output logic
assign out = (&q_reg[N-1:1] & ~q_reg[0]);
endmodule
uart_test
`timescale 1ns / 1ps
module uart_test(
input clk,
input reset,
input btn_start,
output txd
);
wire w_btn_start;
button U_Btn_start(
.clk(clk),
.in(btn_start),
.out(w_btn_start)
);
uart U_UART_TX (
.clk(clk),
.reset(reset),
.start(w_btn_start),
.tx_data(8'h41), // 'A'
.tx_done(),
.txd(txd)
);
endmodule
tb_uart
`timescale 1ns / 1ps
module tb_uart();
reg clk;
reg reset;
reg start;
reg [7:0] tx_data;
wire tx_done;
wire txd;
uart dut (
.clk(clk),
.reset(reset),
.start(start),
.tx_data(tx_data),
.tx_done(tx_done),
.txd(txd)
);
always #5 clk = ~clk;
initial begin
clk = 1'b0;
reset = 1'b1;
start = 1'b0;
tx_data = 0;
end
initial begin
#20 reset = 1'b0;
#20 tx_data = 8'haf; start = 1'b1;
#10 start = 1'b0;
end
endmodule
simulation
constraint
## Clock signal
set_property -dict { PACKAGE_PIN W5 IOSTANDARD LVCMOS33 } [get_ports { clk }]; #IO_L12P_T1_MRCC_34 ,Sch=CLK100MHZ
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports clk]
##Buttons
set_property -dict { PACKAGE_PIN U18 IOSTANDARD LVCMOS33 } [get_ports { reset }]; #IO_L18N_T2_A11_D27_14 ,Sch=BTNC
set_property -dict { PACKAGE_PIN T18 IOSTANDARD LVCMOS33 } [get_ports { btn_start }]; #IO_L17N_T2_A13_D29_14 ,Sch=BTNU
##USB-RS232 Interface
#set_property -dict { PACKAGE_PIN B18 IOSTANDARD LVCMOS33 } [get_ports { RsRx }]; #IO_L19P_T3_16 ,Sch=UART_RXD_IN
set_property -dict { PACKAGE_PIN A18 IOSTANDARD LVCMOS33 } [get_ports { txd }]; #IO_L19N_T3_VREF_16 ,Sch=UART_TXD_OUT
video
- btn_start 버튼을 누르면 'A'가 나타난다.
uart.hw
`timescale 1ns / 1ps
module uart(
input clk,
input reset,
input start,
input [7:0] tx_data,
output tx_done,
output txd
);
wire br_tick;
buadrate_generator U_BR_Gen(
.clk(clk),
.reset(reset),
.br_tick(br_tick)
);
transmitter U_TxD(
.clk(clk),
.reset(reset),
.br_tick(br_tick),
.start(start),
.data(tx_data),
.tx_done(tx_done),
.tx(txd)
);
endmodule
module buadrate_generator(
input clk,
input reset,
output br_tick
);
reg[$clog2(100_000_000/9600)-1 : 0] counter_reg, counter_next;
reg tick_reg, tick_next;
assign br_tick = tick_reg;
always @(posedge clk, posedge reset) begin
if(reset) begin
counter_reg <= 0;
tick_reg <= 1'b0;
end else begin
counter_reg <= counter_next;
tick_reg <= tick_next;
end
end
always @(*) begin
counter_next = counter_reg;
//if (counter_reg == 100_000_000/9600 -1) begin // button
if (counter_reg == 10-1) begin //for simulation
counter_next = 0;
tick_next = 1'b1;
end else begin
counter_next = counter_reg + 1;
tick_next = 1'b0;
end
end
endmodule
module transmitter (
input clk,
input reset,
input br_tick,
input start,
input [7:0] data,
output tx_done,
output tx
);
localparam IDLE = 0, START = 1, DATA = 2, STOP = 3;
reg [1:0] state, state_next;
reg [7:0] r_data;
reg tx_reg, tx_next;
reg tx_done_reg, tx_done_next;
reg [2:0] bit_count, bit_count_next;
assign tx = tx_reg;
assign tx_done = tx_done_reg;
//start register
always @(posedge clk, posedge reset) begin
if (reset) begin
state <= IDLE;
tx_reg <= 1'b0;
tx_done_reg <= 1'b0;
bit_count <= 3'b000;
end else begin
state <= state_next;
tx_reg <= tx_next;
tx_done_reg <= tx_done_next;
bit_count <= bit_count_next;
end
end
//next state combinational logic
always @(*) begin
state_next = state;
bit_count_next = bit_count;
case(state)
IDLE : if(start) state_next = START;
START : if(br_tick) state_next = DATA;
DATA : if(br_tick) begin
if(bit_count == 7) begin
bit_count_next = 3'b000;
state_next = STOP;
end else begin
bit_count_next = bit_count + 1;
end
end
STOP : if(br_tick) state_next = IDLE;
endcase
end
//output combinational logic
always @(*) begin
tx_next = tx_reg;
tx_done_next = 1'b0;
//r_data = 0;
case(state)
IDLE : tx_next = 1'b1;
START : begin
tx_next = 1'b0;
r_data = data;
end
DATA : tx_next = r_data[bit_count];
STOP : begin
tx_next = 1'b1;
if(state_next == IDLE) tx_done_next = 1'b1;
end
endcase
end
endmodule
- 앞의 uart.v와 달리 bit_count를 사용하여 0부터 7까지 총 8번 반복해서 r_data의 bit를 보내는 방식이다.
schemaitc
simulation
video
- 버튼을 누를 때마다 'A'가 나타난다.
'[하만]세미콘 아카데미 > verilog' 카테고리의 다른 글
0517 uart_tx_rx (0) | 2024.06.22 |
---|---|
0517 uart_tx (0) | 2024.06.22 |
0516 upCounter_FSM (0) | 2024.06.22 |
0514 upCounter_FND_FSM (0) | 2024.06.22 |
0514 upCounter_Fnd_moore_machine (0) | 2024.06.22 |