/*********************************************************/
// 모듈:Uart
// 파일이름: uart.v
// 버젼 : 1.0
// 날짜 : 2026.05.26
// 저자 : 탁 형옥
// 코딩 언어 : verilog
// 문제 내용 (해결해야 할 목표):
gpt 의 팹리스 커리에 있는 Uart 를 설계해본다.
// 요구되는 언어 기술 (어려운 점):
수신 데이터에 반구간 표본 Tick 신호를 만들기
BAUD_RATE을 50 MHz 까지 올려본다. (clk=100MHz일때에)
// 목표와 기술을 도달하기 위한 알아야 할 기술(문법), 어려운 점, 문제점 극복 과정:
수신 데이터에 반구간 표본 Tick 신호를 만들기위해 카운터를 하나 더 두었다.
// 결과 도출:
행위적 동작 시뮬레이션 및 타이밍 시뮬레이션에서는 BAUD_RATE=50Mbps 일때에 데이터 정상 송수신함
// 기본 목표 과제를 한층 더 깊이 있게 하기 위한 생각:
// 이용 가능한 분야:
// 기술과 트렌드:
// 설명 :
//
/*********************************************************/
module uart_tx
#(
parameter CLK_FREQ = 100_000_000,
parameter BAUD_RATE = 115200
)
(
input wire clk,
input wire rstn,
input wire [7:0] data_in,
input wire send,
output reg tx,
output reg busy
);
localparam BAUD_DIV = CLK_FREQ / BAUD_RATE;
localparam SEND=1'b1, IDLE=1'b0;
reg [11:0] cnt;
reg [10:0] s_r;
reg parity;
reg ST;
wire [0:7] rdata ={data_in[0],data_in[1],data_in[2],data_in[3],data_in[4],data_in[5],data_in[6],data_in[7]};
wire clk_en;
assign clk_en = (cnt==(BAUD_DIV-1)) ?1'b1 :1'b0;
always @(posedge clk)
begin
if(rstn==1'b0)
begin
cnt<=0;
end
else if(cnt<BAUD_DIV)
cnt<=cnt+1'b1;
else
begin
cnt<=0;
// fabric_generated clock clk_b <=~clk_b; not to be recomend
end
end
reg [4:0] scnt;
always @(posedge clk or negedge rstn)
begin
if(rstn==1'b0)
begin
ST<=IDLE;
busy<=1'b0;
end
else if(clk_en==1'b1)
begin
case(ST)
IDLE:
begin
if(send)
begin
parity = ^data_in;
s_r<={1'b0,rdata[0:7],parity,1'b0};
ST<=SEND;
tx<=1'b1;
scnt<=0;
end
else
begin
ST<=IDLE;
tx<=1'b1;
end
busy<=1'b0;
end
SEND:
begin
{tx,s_r}<={s_r,1'b1};
if(scnt<10)
scnt<=scnt+1'b1;
else
ST<=IDLE;
busy<=1'b1;
end
endcase
end
end
endmodule
module uart_rx
#(
parameter CLK_FREQ = 100_000_000,
parameter BAUD_RATE = 115200
)
(
input wire clk,
input wire rstn,
input wire rx,
output reg [7:0] data_out,
output reg valid
);
localparam BAUD_DIV = CLK_FREQ / BAUD_RATE ;
localparam HALF_BAUD_DIV = BAUD_DIV / 2;
parameter IDLE=3'b000, START=3'b001, DATA=3'b010, STOP=3'b011, DONE=3'b100, CHECK=3'b101;
reg [2:0] st,nstate;
reg [9:0] cnt;
reg [3:0] rcnt;
wire h_bclk_en;
assign h_bclk_en= (cnt==HALF_BAUD_DIV) || (pcnt==HALF_BAUD_DIV);
always @(posedge clk)
begin
if(rstn==1'b0)
st<=IDLE;
else if(h_bclk_en==1'b1)
st<=nstate;
end
always @(posedge clk)
begin
if(rx==1'b0)
cnt<=0;
else if(cnt<BAUD_DIV)
cnt<=cnt+1'b1;
else
cnt<=0;
end
reg [10:0] pcnt;
reg rParity;
always @(posedge clk)
begin
if(rx==1'b1)
pcnt<=0;
else if(pcnt<BAUD_DIV)
pcnt<=pcnt+1'b1;
else
pcnt<=0;
end
always @*
begin
case(st)
IDLE: begin
if(rx==1'b0)
nstate<=START;
else
nstate<=IDLE;
end
START:begin
nstate<=DATA;
end
DATA: begin
if(rcnt<8)
nstate<=DATA;
else
nstate<=CHECK;
end
CHECK: begin
nstate<=STOP;
end
STOP: begin
nstate<=DONE;
end
DONE: begin
nstate<=IDLE;
end
default: begin nstate<= IDLE; end
endcase
end
reg [7:0] rdata_out;
always @*
begin
data_out={rdata_out[0],rdata_out[1],rdata_out[2],rdata_out[3],rdata_out[4],rdata_out[5],rdata_out[6],rdata_out[7]};
end
always @(posedge clk)
begin
if(rstn==1'b0)
rcnt<=0;
else if(h_bclk_en==1'b1 && ((st==DATA)|| (st==START)))
begin
rcnt<=rcnt+1'b1;
end
// else if(h_bclk_en==1'b1 && (st==CHECK))
else if(h_bclk_en==1'b1 && (st==DONE))
begin
valid <= !(^rdata_out ^ rParity);
// valid <=1'b0;
rcnt<=0;
end
else
valid <= 1'b0;
end
always @(posedge clk)
begin
if(rstn==1'b0)
rdata_out <= 8'hff;
else if(h_bclk_en==1'b1 && ((st==DATA)|| (st==START)))
begin
if(rcnt<8)
rdata_out <= {rdata_out[6:0],rx};
end
if(rcnt==8)
rParity<=rx;
end
endmodule
`timescale 1ns/100ps
module tb_uart_rx;
localparam CLK_FREQ = 100_000_000;
localparam BAUD_RATE = 115200;
reg clk;
reg rstn;
reg [7:0] data_in;
reg send;
wire tx;
wire busy;
wire rx;
wire [7:0] data_out;
wire valid;
uart_tx #(.CLK_FREQ(100_000_000),
.BAUD_RATE(115200)) TX
(
.clk(clk),
.rstn(rstn),
.data_in(data_in),
.send(send),
.tx(tx),
.busy(busy)
);
uart_rx
#(
.CLK_FREQ (100_000_000),
.BAUD_RATE (115200)
) RX
(
.clk(clk),
.rstn(rstn),
.rx(rx),
.data_out(data_out),
.valid(valid)
);
assign rx=tx;
always #5 clk=~clk;
initial
begin
clk=1'b0; rstn=1'b1;
#500; rstn=1'b0;
#100 rstn=1'b1; data_in=8'hA1;
@(posedge TX.clk_en); send=1'b1;
@(posedge TX.clk_en); send=1'b0;
@(negedge TX.busy);
data_in=8'hA2;
@(posedge TX.clk_en);
@(posedge TX.clk_en); send=1'b1;
@(posedge TX.clk_en); send=1'b0;
@(negedge TX.busy);
data_in=8'hA5;
@(posedge TX.clk_en);
@(posedge TX.clk_en); send=1'b1;
@(posedge TX.clk_en); send=1'b0;
@(negedge TX.busy);
data_in=8'hA4;
@(posedge TX.clk_en);
@(posedge TX.clk_en); send=1'b1;
@(posedge TX.clk_en); send=1'b0;
@(negedge TX.busy);
end
endmodule