Verilog UART(1):TX模块
最近重新学verilog,拿着开发板做一些小实验。先写了一个串口收发器模块。先是Tx部分。主要学习了状态机和画波形图。觉得还是要尽量先画波形图
,然后再开始写代码,不然在仿真调试上要花更多时间。
UART一帧数据的格式:
- 空闲状态为高电平1
- 起始位为0
- 然后是8位数据,(可能有第9位奇偶校验)
- 结束位是1
输入:
- clk:时钟
- tx_data_valid:开始传输&输入数据有效
- tx_data:需要传输的1字节数据
输出:
- uart_tx:串口TX信号
- tx_idle_flag:指示传输完成
时序:
- 分为4个状态,IDLE-START-TX_BYTE-STOP
- baud_cnt是波特率的计数器,从0数到NUM,例如,
- bit_cnt对发送的bit0~7计数,会在一个clk周期计数到8,,但是不影响功能,所以就不管了。
- tx_idle_flag:因为用了时序逻辑,比state==idle延迟1拍,不知道要不要改成组合逻辑?
顺便,时序图是用Wavedrom画的,根据文本生成波形图,比较方便,比如上面这个图的代码如下:
{signal: [
{name: 'clk', wave: 'p..|..|..|..|..|..|.'},
{name: 'baud_cnt',wave: '22222222222222222222',
data:['','', '0','...','NUM', '0','...','NUM',
'0','...','NUM', '0','...','NUM','0','...','NUM','0','...','NUM']},
{name: 'tx_data_valid', wave: '010.................'},
{name: 'tx_data', wave: '03x.................',
data:['din']},
{name: 'tx_data_latch', wave: 'x.3.................', data:['din']},
{name: 'state', wave: '2.2..2........2..2..',
data:['idle','start', 'tx_byte', 'stop', 'idle']},
{name: 'bit_cnt', wave: '0....2..2..2..30....',
data:['0','...', '7', '8']},
{name: 'uart_tx', wave: '1.0..2..2..2..2..1..',
data:['bit0','...', 'bit7', 'stop']},
{name: 'tx_idle_flag', wave: '1..0..............1.',
data:['bit0','...', 'bit7', 'stop']}
]}
4状态的版本
上板验证ok。
module uart_tx
#(
parameter BAUD_RATE = 115200,
parameter CLOCK_FREQ = 50 //MHz
)
(
input clk,
input rst_n,
input tx_data_valid,
input [7:0] tx_data,
output reg tx_pin,
output reg tx_idle_flag
);
parameter BAUD_CYCLE = CLOCK_FREQ * 1000000 / BAUD_RATE - 1;
reg [7:0] tx_data_latch;
//保存tx_data
always @(posedge clk) begin
if(!rst_n)
tx_data_latch <= 8'd0;
else if(tx_data_valid && state == IDLE )
//只在IDLE保存发送的1字节数据,其他时候不保存
tx_data_latch <= tx_data;
else
tx_data_latch <= tx_data;
end
//波特率计数器
reg [15:0] baud_cnt;
always @(posedge clk) begin
if(!rst_n)
baud_cnt <= 16'd0;
else if(baud_cnt == BAUD_CYCLE || next_state != state)
// 在切换状态、或是达到计数值时清零
baud_cnt <= 16'd0;
else
baud_cnt <= baud_cnt + 1'b1;
end
always @(posedge clk) begin
if(!rst_n)
tx_idle_flag <= 1'b1;
else if(state == IDLE)
//IDLE状态可以收下一位数据
tx_idle_flag <= 1'b1;
else
tx_idle_flag <= 1'b0;
end
reg [3:0] bit_cnt;
localparam BIT_NUM = 7;
reg [3:0] state;
reg [3:0] next_state;
localparam IDLE = 4'd0;
localparam START= 4'd1;
localparam TX_BYTE= 4'd2;
localparam STOP= 4'd3;
always @(posedge clk) begin
if(!rst_n)
state <= IDLE;
else
state <= next_state;
end
always @(*) begin
if(!rst_n)
next_state <= IDLE;
else
case(state)
IDLE:
if(tx_data_valid)
next_state <= START;
else
next_state <= IDLE;
START:
if(baud_cnt == BAUD_CYCLE)
next_state <= TX_BYTE;
else
next_state <= START;
TX_BYTE:
if(baud_cnt == BAUD_CYCLE && bit_cnt == BIT_NUM)
next_state <= STOP;
else
next_state <= TX_BYTE;
STOP:
if(baud_cnt == BAUD_CYCLE)
next_state <= IDLE;
else
next_state <= STOP;
default: next_state <= IDLE;
endcase
end
//发送的bit计数
always @(posedge clk) begin
if(!rst_n)
bit_cnt <= 4'd0;
else if(state == TX_BYTE)
if(baud_cnt == BAUD_CYCLE)
bit_cnt <= bit_cnt + 1'b1;
else
bit_cnt <= bit_cnt;
else
bit_cnt <= 4'd0;
end
//发送的数据
//这里根据状态用组合逻辑实现
always @(*) begin
if(!rst_n)
tx_pin = 1'b1;
else
case(state)
IDLE: tx_pin = 1'b1;
START: tx_pin = 1'b0; //起始位
TX_BYTE: tx_pin = tx_data_latch[bit_cnt]; //低位在前
STOP: tx_pin = 1'b1; //结束位
default: tx_pin = 1'b1;
endcase
end
endmodule
10状态的版本
最先是给每bit都写了一个状态,功能一样,就是代码比较长。。
(原来verilog可以用cpp来高亮。。虽然有些关键字不行)
module uart_tx
#(
parameter BAUD_RATE = 115200,
parameter CLOCK_FREQ = 50 //MHz
)
(
input clk,
input rst_n,
input tx_data_valid,
input [7:0] tx_data,
output reg tx_pin,
output reg tx_idle_flag
);
parameter BAUD_CYCLE = CLOCK_FREQ * 1000000 / BAUD_RATE - 1;
reg [7:0] tx_data_latch;
//保存tx_data
always @(posedge clk) begin
if(!rst_n)
tx_data_latch <= 8'd0;
else if(tx_data_valid && state == IDLE )
//只在IDLE保存发送的1字节数据,其他时候不保存
tx_data_latch <= tx_data;
else
tx_data_latch <= tx_data;
end
//波特率计数器
reg [15:0] baud_cnt;
always @(posedge clk) begin
if(!rst_n)
baud_cnt <= 16'd0;
else if(baud_cnt == BAUD_CYCLE || next_state != state)
// 在切换状态、或是达到计数值时清零
baud_cnt <= 16'd0;
else
baud_cnt <= baud_cnt + 1'b1;
end
always @(posedge clk) begin
if(!rst_n)
tx_idle_flag <= 1'b1;
else if(state == IDLE)
//IDLE状态可以收下一位数据
tx_idle_flag <= 1'b1;
else
tx_idle_flag <= 1'b0;
end
reg [3:0] state;
reg [3:0] next_state;
localparam IDLE = 4'd9;
localparam START= 4'd10;
localparam BIT0= 4'd0;
localparam BIT1= 4'd1;
localparam BIT2= 4'd2;
localparam BIT3= 4'd3;
localparam BIT4= 4'd4;
localparam BIT5= 4'd5;
localparam BIT6= 4'd6;
localparam BIT7= 4'd7;
localparam STOP= 4'd8;
always @(posedge clk) begin
if(!rst_n)
state <= IDLE;
else
state <= next_state;
end
always @(*) begin
if(!rst_n)
next_state <= IDLE;
else
case(state)
IDLE:
if(tx_data_valid)
next_state <= START;
else
next_state <= IDLE;
START:
if(baud_cnt == BAUD_CYCLE)
next_state <= BIT0;
else
next_state <= START;
BIT0:
if(baud_cnt == BAUD_CYCLE)
next_state <= BIT1;
else
next_state <= BIT0;
BIT1:
if(baud_cnt == BAUD_CYCLE)
next_state <= BIT2;
else
next_state <= BIT1;
BIT2:
if(baud_cnt == BAUD_CYCLE)
next_state <= BIT3;
else
next_state <= BIT2;
BIT3:
if(baud_cnt == BAUD_CYCLE)
next_state <= BIT4;
else
next_state <= BIT3;
BIT4:
if(baud_cnt == BAUD_CYCLE)
next_state <= BIT5;
else
next_state <= BIT4;
BIT5:
if(baud_cnt == BAUD_CYCLE)
next_state <= BIT6;
else
next_state <= BIT5;
BIT6:
if(baud_cnt == BAUD_CYCLE)
next_state <= BIT7;
else
next_state <= BIT6;
BIT7:
if(baud_cnt == BAUD_CYCLE)
next_state <= STOP;
else
next_state <= BIT7;
STOP:
if(baud_cnt == BAUD_CYCLE)
next_state <= IDLE;
else
next_state <= STOP;
default: next_state <= IDLE;
endcase
end
//发送的数据
//这里根据状态用组合逻辑实现
always @(*) begin
if(!rst_n)
tx_pin = 1'b1;
else
case(state)
IDLE: tx_pin = 1'b1;
START: tx_pin = 1'b0; //起始位
BIT0: tx_pin = tx_data_latch[0]; //低位在前
BIT1: tx_pin = tx_data_latch[1];
BIT2: tx_pin = tx_data_latch[2];
BIT3: tx_pin = tx_data_latch[3];
BIT4: tx_pin = tx_data_latch[4];
BIT5: tx_pin = tx_data_latch[5];
BIT6: tx_pin = tx_data_latch[6];
BIT7: tx_pin = tx_data_latch[7];
STOP: tx_pin = 1'b1; //结束位
default: tx_pin = 1'b1;
endcase
end
endmodule