UART TX 모듈 설계
- 신호 개요
- DIN [7:0]: 데이터 버스로부터 받는 8비트의 parallel 데이터묶음
- Send: 데이터 전송의 시작을 명령하는 신호
- Dout: RX로 전달되는 serial 데이터
- Busy: UART TX가 동작중임을 나타내는 신호
- ParitySelect: Odd Parity를 사용할지 Even Parity를 사용할지 선택하는 신호
- 작동 방식
- 8비트의 parallel 데이터를 버스로부터 수신한다.
- send 신호가 들어오면 stop bit - parity bit - data - start bit 순으로 구성된 데이터 11비트를 dout으로 전송한다.
[ uart_tx.v ]
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/07/23 14:28:21
// Design Name:
// Module Name: uart_tx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module uart_tx(
input rst,
input clk,
input [7:0] din,
input send,
input parity,
output busy,
output reg dout
);
reg [1:0] state;
reg [3:0] bit_cnt;
reg [10:0] data;
wire [7:0] DIN;
assign DIN = din;
wire PARITY;
assign PARITY = parity;
always @(posedge clk) begin
if(rst) dout = 1 ;
end
localparam [1:0] idle = 2'b00,
load = 2'b01,
transmit = 2'b10,
stop = 2'b11;
localparam odd = 1'b0, even = 1'b1;
always@(posedge clk) begin
case (state)
idle: begin
dout = 1;
if(send) state = load;
else state = idle;
end
load: begin
data = {1'b1, PARITY, DIN[7:0], 1'b0}; //stop bit - parity - data - start bit
state = transmit;
end
transmit:begin
if(bit_cnt < 12) begin
dout = data [bit_cnt];
bit_cnt = bit_cnt + 1;
end
else state = stop;
end
stop: begin
dout = 1'b1;
state = idle;
end
endcase
end
endmodule
코드 수정 1: FSM의 State가 1clk마다 넘어가는 현상 해결
- 수정사항: idle 상태에서 입력 신호인 DIN이 있을 때에만 데이터를 입력 후 넘어가도록 코드를 수정하였다.
- 위의 문제를 해결하기 위해 다음과 같이 코드를 변경하였으나 load상태에서 FSM이 변하지 않아 din 신호가 unknown이 아니고 send 신호가 1이면 load 상태로 변하도록 코드를 수정하였다.
코드 수정 2: send 신호가 들어오면 load로 넘어가고, 이 때 din 신호에 따라 data에 입력하도록 코드 수정
코드 수정 3: din으로 들어오는 데이터를 매 clock마다 DIN으로 전달하도록 하고, DIN이 X값이 아닐 때 로드하도록 수정
- DIN이 X가 아닌 경우 그 값은 0 이상이므로 해당 조건을 이용하여 코드를 수정하였다.
- 위 수정사항 적용 결과 DIN의 데이터가 data 내에 정상적으로 전달된 것을 확인할 수 있다.
- 동작이 끝난 이후 상태가 넘어갈 때 존재하지 않는 data[12]가 dout으로 출력되어 x값(빨간색)으로 출력되는 문제가 발생했다.
- 또한 idle 상태에서는 dout이 1로 출력되어야 하는데 0으로 출력되는 문제가 발생했다.
코드 수정 4: dout이 unknown(x)으로 출력되는 오류 수정
- 아래와 같이 if문 내의 조건이 bit_cnt < 12이었던 것을 bit_cnt<11로 수정하여 bit_cnt가 11까지만 증가하도록 수정하였다.
- 그 결과, 아래와 같이 bit_cnt가 11까지만 작동하고, 각 clk마다 DOUT 신호로 data의 값(start bit~stop bit)이 정확히 전달되었다. 그러나 여전히 dout신호가 idle에서 1로 바뀌지 않는 문제가 남아있다.
dout신호로 start bit 0이 전달된 이후 LSB부터 MSB까지 전달된다. 그 후로 Parity Bit와 Stop Bit가 각각 전달되었다.
코드 수정 5: idle 상태에서 DIN이 unknown이 아니고, send 신호가 들어올 때 load를 실행하도록 조건 변경
- idle에서 dout을 1로 초기화하고, 위의 조건에서만 load를 실행하도록 조건을 변경하였다.
- dout은 transfer을 제외한 상태에서는 항상 1로 출력되도록 코드를 수정했다.
- 또한 current state와 next state를 구분하여 fsm 작성 시 발생하는 딜레이를 줄이기 위해 state 변수만 두고 이를 기반으로 fsm을 동작하도록 코드를 수정하였다.
- 그 결과, 다음과 같이 transfer 동작 외에는 항상 DOUT이 출력되고, DOUT은 Start bit,Data, Parity Bit, Stop Bit를 모두 순서대로 출력한다.
[ 최종 uart_tx.v ]
`timescale 1ns / 1ps
module uart_tx(
input rst,
input clk,
input [7:0] din,
input send,
input parity,
output reg busy,
output reg dout
);
//reg [1:0] currstate;
reg [1:0] state;
reg [3:0] bit_cnt = 4'b0;
reg [10:0] data;
reg [7:0] DIN;
reg PARITY;
localparam [1:0] idle = 2'b00,
load = 2'b01,
transmit = 2'b10,
stop = 2'b11;
localparam odd = 1'b0, even = 1'b1;
always @(posedge clk) begin
if(rst) begin dout <= 1'b1; busy <= 1'b0; state <= idle; end
end
always @(posedge clk) begin
//currstate <= nextstate;
PARITY <= parity;
DIN <= din;
end
always@(posedge clk) begin
case (state)
//idle
idle: begin
busy = 1'b0;
dout = 1'b1;
data = 11'b0;
bit_cnt = 0;
if((DIN>=0)&&send) begin
//dout = 0;
state = load;
end
else state = idle;
end
//load data from din
load: begin
if(DIN >= 0) begin
data = {1'b1, PARITY, DIN, 1'b0}; //stop bit - parity - data - start bit
if (send) state = transmit;
end else state = load;
end
//transmit
transmit:begin
if(bit_cnt < 11) begin
busy = 1;
dout = data [bit_cnt];
bit_cnt = bit_cnt + 1;
end
else begin dout = 1'b1; state = idle; end
end
//stop
/*stop: begin
dout = 1'b1;
nextstate = idle;
end*/
endcase
end
endmodule
UART TX와 Baud Rate Generator을 함께 시뮬레이션하여 Enable 신호 확인
uart tx와 baud_rate_gen.v를 uart_top으로 묶어 테스트벤치 실행 결과 enable 신호가 생성되지 않는 오류가 발생했다.
- 아래 테스트벤치 확인 후 Dout이 Baud_Rate이 아닌 clock에 맞춰 전송되는 오류를 확인했다.
아래 글에서 작성한 Baud_Rate_gen 코드를 수정한다. 이번에는 output으로 Baud_Rate를 출력하는 코드를 추가한다.
UART(Universal Asynchronous Receiver/Transmitter)
UART란?UART(Universal Asynchronous Receiver/Transmitter, 범용 비동기화 송수신기)는 데이터 버스로부터 병렬로 받은 데이터를 직렬 방식으로 전환하여 다른 UART로 전송하는 컴퓨터 하드웨어의 한 종류이다.U
semicon-circuit.tistory.com
위 글을 참고하여 bit_cnt_std번의 clk마다 Baud_Rate 신호를 trigger해줌으로써 BaudRate 신호를 생성할 수 있다. enable 신호는 이 Baud Rate신호를 기준으로 생성할 수도 있다.
Baud Rate를 생성하고, 이에 따라 enable을 동작시키기 위해 코드 수정
- baud_rate_gen.v 파일에서 clk_cnt 변수를 만들고, bit 변수와 cnt 변수를 제거하여 clk_cnt가 bit_cnt_std의 8배일 때마다 baud_rate를 1로 만들고, 16배가 되면 baud_rate를 0으로 하여 신호를 생성하도록 수정하였다.
- bit_cnt_std의 16배가 되면 baud_rate가 0으로 바뀌며 이 때 enable 신호를 내보내서 over sampling을 수행할 수 있도록 수정하였다.
baud_rate_gen.v
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/07/23 10:22:39
// Design Name:
// Module Name: baud_rate_gen
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module baud_rate_gen(
input clk,
input rst,
output enable,
output baud_rate_signal
);
parameter clk_freq = 125_000_000;
parameter baud_rate_freq = 9600;
parameter bit_cnt_std = clk_freq/(baud_rate_freq*16);
//reg [15:0] bit = 16'd0;
//reg [12:0] cnt = 13'd0;
reg [19:0] clk_cnt = 12'd0;
reg baud_rate = 1'b0;
assign baud_rate_signal = baud_rate;
reg en;
assign enable = en;
always@(posedge clk) clk_cnt = clk_cnt+1;
always@(posedge clk)begin
if(rst) begin
en = 0;
//bit = 0;
//cnt = 0;
end
else begin
if(clk_cnt > bit_cnt_std*8) begin
baud_rate = 1;
//enable to 1 only 1 clock
// if((bit_cnt_std*8 + 1>clk_cnt) && !baud_rate) en= 1;
// else en = 0;
if(clk_cnt > bit_cnt_std*16)begin
baud_rate = 0;
en = 1;
clk_cnt = 0;
end
end
// if(((bit+8) % 16) == 0) begin
// en = 1;
// bit = 0;
// cnt = 0;
// end
else en = 0;
end //else end
end
endmodule
UART TX가 Baud Rate Generator로부터 Baud Rate를 받고, 이에 맞추어 Dout을 출력하도록 코드 수정
- bit_cnt 변수는 TX에서 Dout으로 데이터를 내보내는 transmit 상태에서만 증가한다. 이는 Baud Rate의 상승 에지에 맞춰 증가시키면 Baud Rate에 맞게 데이터를 전송할 수 있다.
- top module에서 위 블록 다이어그램과 같이 Baud Rate Generator에서 UART TX로 Baud Rate 신호를 전달할 수 있도록 다음과 같이 input과 output을 변경하였다.
- 실행 결과: TX로부터 11비트의 데이터가 dout으로 baud rate에 맞추어 정상적으로 출력된다.
[ uart_tx.v ]
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/07/23 14:28:21
// Design Name:
// Module Name: uart_tx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module uart_tx(
input rst,
input clk,
input baud_rate,
input [7:0] din,
input send,
input parity,
output reg busy,
output reg dout
);
//reg [1:0] currstate;
reg [1:0] state;
reg [3:0] bit_cnt = 4'b0;
reg [10:0] data;
reg [7:0] DIN;
reg PARITY;
localparam [1:0] idle = 2'b00,
load = 2'b01,
transmit = 2'b10,
stop = 2'b11;
always @(posedge clk) begin
if(rst) begin dout <= 1'b1; busy <= 1'b0; state <= idle; end
end
always @(posedge baud_rate) begin
if(state == transmit) bit_cnt = bit_cnt+1;
end
always @(posedge clk) begin
//currstate <= nextstate;
PARITY <= parity;
DIN <= din;
end
always@(posedge clk) begin
case (state)
//idle
idle: begin
busy = 1'b0;
dout = 1'b1;
data = 11'b0;
bit_cnt = 0;
if((DIN>=0)&&send) begin
//dout = 0;
state = load;
end
else state = idle;
end
//load data from din
load: begin
if(DIN >= 0) begin
data = {1'b1, PARITY, DIN, 1'b0}; //stop bit - parity - data - start bit
if (send) state = transmit;
end else state = load;
end
//transmit
transmit:begin
if(bit_cnt < 11) begin
busy = 1;
dout = data [bit_cnt];
end
else begin dout = 1'b1; state = idle; end
end
//stop
/*stop: begin
dout = 1'b1;
nextstate = idle;
end*/
endcase
end
endmodule
[ uart_top.v ]
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/07/28 13:53:22
// Design Name:
// Module Name: uart_top
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module uart_top(
input RST,
input CLK,
input SEND,
input BUSY,
input PARITY,
input [7:0] DIN,
output DOUT,
output ENABLE
);
wire baudrate;
baud_rate_gen #(.clk_freq (13020), .baud_rate_freq (1)) BAUD_RATE_GEN(
.clk (CLK),
.rst (RST),
.enable (ENABLE),
.baud_rate_signal (BAUDRATE)
);
uart_tx UART_TX(
.rst (RST),
.clk (CLK),
.baud_rate (BAUDRATE),
.din (DIN),
.send (SEND),
.parity (PARITY),
.busy (BUSY),
.dout (DOUT)
);
endmodule
코드 수정
data의 첫 비트가 출력될 때 baud rate가 0부터 시작하여 enable 신호에 맞게 첫 비트를 읽을 수 없는 오류 수정
- Baud Rate의 초기값이 0으로 시작하여 발생하는 오류로 확인했고, 초기값을 1로 하여 신호를 생성함으로 문제를 해결했다.
Data의 크기와 Parity Bit의 크기가 달라져도 정상 작동하도록 코드 수정
- UART에서는 최대 9비트의 데이터를 전달할 수 있고, Parity Bit는 1Bit와 2Bit 두 가지 방법으로 사용 가능하므로, 이를 자유롭게 변경할 수 있도록 다음과 같이 parameter을 선언하고 실행 조건을 변경했다.
parameter로 설정한 값을 input으로 변경하면 실행 시 데이터 크기와 정지 비트 크기를 입력으로 하여 직접 설정할 수 있다. - 위 동작 시 din의 크기는 최대 9비트가 되어야 하고, stop bit도 최대 2비트가 되어야 하므로 input의 din의 크기를 변경하여 코드를 작성해야 한다.
- data bit의 크기와 stop bit의 크기가 달라지는 경우 이 크기 또한 input으로 설정하여 동작시킬 수 있다.
- 위 부분은 RX 완료 이후 시도해볼 예정
'자습시간 > Verilog' 카테고리의 다른 글
UART(Universal Asynchronous Receiver/Transmitter) (1) | 2024.07.30 |
---|---|
UART 설계(3) - UART RX 설계 / 최종 설계 완료 (0) | 2024.07.29 |
UART 설계(1) - Baud Rate Generator 설계 (0) | 2024.07.22 |
Verilog 자료형 (0) | 2024.07.13 |