一、Fpga--PLL锁相环的使用
通过PLL锁相环IP的使用,可以输入时钟进行分频,倍频,也可以产生多路时钟输出信号。
外部提供晶振的原因:①Fpga电路大多数需要时序分析
②晶振在Fpga内部占空间大,所以放置于外部PCB板上
Uart异步传输速率不能很快的原因:考虑到工程实际问题,假如时钟周期大(传输速率慢的话),采取到数据电平变化的边沿时刻的概率会小很多,这样数据也很准确,通常uart适用于2M.
二、Uart 发送
2.1 uart_byte_tx(字节发送)
2.1.1模块设计框图
2.1.2 Verilog设计及仿真
`timescale 1ns / 1ps
//
// Description: UART 字节发送
//
module uart_tx_byte(
Clk ,
Rst_n ,
data_byte ,
send_en ,
Baud_Set ,
uart_tx ,
Tx_Done ,
uart_state
);
input Clk ; //模块全局时钟输入,50M
input Rst_n ; //复位信号输入,低有效
input [7:0] data_byte ; //待传输8bit数据
input send_en ; //发送使能
input [2:0] Baud_Set ; //波特率设置
output reg uart_tx ; //串口输出信号
output reg Tx_Done ; //1byte数据发送完成标志
output reg uart_state ; //发送数据状态
localparam START_BIT = 1'b0;
localparam STOP_BIT = 1'b1;
reg bps_clk ; //波特率时钟
reg [15:0] div_cnt ; //分频计数器
reg [15:0] bps_DR ; //分频计数最大值
reg [3:0] bps_cnt ; //波特率时钟计数器
reg [7:0] data_byte_reg; //data_byte寄存后数据
//******************** UART发送状态标志 ****************** \\
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
uart_state <= 1'b0;
else if(send_en)
uart_state <= 1'b1;
else if(bps_cnt >= 4'd11)
uart_state <= 1'b0;
else
uart_state <= uart_state;
end
//******************** 输入数据锁存 ****************** \\
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
data_byte_reg <= 8'd0;
else if(send_en)
data_byte_reg <= data_byte;
else
data_byte_reg <= data_byte_reg;
end
//******************** 波特率时钟的生成模块 ****************** \\
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_DR <= 16'd5207;
else begin
case(Baud_Set)
0:bps_DR <= 16'd5207; //波特率为9600 1_000_000_000/9600/20
1:bps_DR <= 16'd2603; //波特率为19200
2:bps_DR <= 16'd1301; //波特率为38400
3:bps_DR <= 16'd867; //波特率为57600
4:bps_DR <= 16'd433; //波特率为115200
default:bps_DR <= 16'd5207;
endcase
end
end
//counter
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
div_cnt <= 16'd0;
else if(uart_state)begin
if(div_cnt >= bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
end
// bps_clk gen
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1) //由于UART发送是从bps_cnt =1 发起始位,而不是从0,因此为了尽快进入发送模块,我们计数器不计满+1,等于一时+1
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
end
//******************** Uart的数据发送模块 ****************** \\
//bps counter
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_cnt <= 4'd0;
else if(bps_cnt >= 4'd11)
bps_cnt <= 4'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
end
//数据发送
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
uart_tx <= 1'b1;
else begin
case(bps_cnt)
0:uart_tx <= 1'b1;
1:uart_tx <= START_BIT; // 从1开始,避免了起始位0与空闲态1冲突
2:uart_tx <= data_byte_reg[0];
3:uart_tx <= data_byte_reg[1];
4:uart_tx <= data_byte_reg[2];
5:uart_tx <= data_byte_reg[3];
6:uart_tx <= data_byte_reg[4];
7:uart_tx <= data_byte_reg[5];
8:uart_tx <= data_byte_reg[6];
9:uart_tx <= data_byte_reg[7];
10:uart_tx <= STOP_BIT;
default:uart_tx <= 1'b1;
endcase
end
end
//数据发送完成标志
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
Tx_Done <= 1'b0;
else if(bps_cnt >= 4'd11)
Tx_Done <= 1'b1;
else
Tx_Done <= 1'b0;
end
endmodule
由上图可知,uart发送先发低位再发高位(aa 10101010)
三、Uart接收
3.1 uart_byte_rx (字节接收)
3.1.1模块设计框图
3.1.2 Verilog设计及仿真
我们接收数据(1起始位+8数据位+1停止位)总共接受10个数据,为避免电磁等干扰,对每一个数据进行16等分,对数据中间进行采样7次,然后比较得出真值,因此总共需160个小数据段,因此bps_clk需要161个这样才能进行保证数据的完整性。
verilog 文件
`timescale 1ns / 1ps
//
// Dependencies: UART字节接收
//
module uart_byte_rx(
Clk ,
Reset_n ,
Baud_Set,
uart_rx ,
Data ,
Rx_Done
);
input Clk ;
input Reset_n ;
input [2:0] Baud_Set;
input uart_rx ;
output reg[7:0] Data ;
output reg Rx_Done ;
reg [1:0] uart_rx_r ;
reg RX_EN ;
reg [8:0] Bps_DR ;
reg [8:0] div_cnt ;
reg bps_clk_16x ;
reg [7:0] bps_cnt ;
//reg width name number/depth
reg [2:0] r_data[7:0];
reg [2:0] sta_bit;
reg [2:0] sto_bit;
wire pedge_uart_rx;
// assign pedge_uart_rx = ((uart_rx_r[1] == 0) && (uart_rx_r[0] == 1));
wire nedge_uart_rx;
// assign nedge_uart_rx = ((uart_rx_r[1] == 1) && (uart_rx_r[0] == 0));
//******************* 边沿检测(下降沿/上升沿)模块 ********************* \\
always@(posedge Clk,negedge Reset_n)begin
if(!Reset_n)
uart_rx_r <= 'd0;
else begin
uart_rx_r[0] <= uart_rx;
uart_rx_r[1] <= uart_rx_r[0] ;
end
end
assign pedge_uart_rx = (uart_rx_r == 2'b01);
assign nedge_uart_rx = (uart_rx_r == 2'b10);
//******************* Uart数据接收状态标志 ********************* \\
always@(posedge Clk or negedge Reset_n)begin
if(!Reset_n)
RX_EN <= 0;
else if(nedge_uart_rx)
RX_EN <= 1;
else if((bps_cnt == 'd160 && bps_clk_16x) || (sta_bit >= 4))// 起始位异常不产生RX_EN
RX_EN <= 0;
else
RX_EN <= RX_EN;
end
//******************* 采样时钟(bps_clk_16x)产生模块 ********************* \\
always@(posedge Clk or negedge Reset_n)begin
if(!Reset_n)
Bps_DR <= 16'd324;
else begin
case(Baud_Set)
0:Bps_DR <= 16'd324; //波特率为9600 1_000_000_000/9600/20 /16
1:Bps_DR <= 16'd162; //波特率为19200
2:Bps_DR <= 16'd80 ; //波特率为38400
3:Bps_DR <= 16'd53 ; //波特率为57600
4:Bps_DR <= 16'd26 ; //波特率为115200
default:Bps_DR <= 16'd324;
endcase
end
end
//counter
always@(posedge Clk or negedge Reset_n)begin
if(!Reset_n)
div_cnt <= 0;
else if(RX_EN)begin
if(div_cnt >= Bps_DR)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 0;
end
// bps_clk_16x gen
always@(posedge Clk or negedge Reset_n)begin
if(!Reset_n)
bps_clk_16x <= 1'b0;
else if(div_cnt == 16'd1)
bps_clk_16x <= 1'b1;
else
bps_clk_16x <= 1'b0;
end
//******************** Uart的数据接收模块 ****************** \\
always@(posedge Clk or negedge Reset_n)begin
if(!Reset_n)
bps_cnt <= 0;
else if(RX_EN)begin
if(bps_clk_16x)begin
if(bps_cnt >= 160)
bps_cnt <= 0;
else
bps_cnt <= bps_cnt + 1'b1;
end
else
bps_cnt <= bps_cnt;
end
else
bps_cnt <= 0;
end
//数据接收
always@(posedge Clk or negedge Reset_n)begin
if(!Reset_n) begin
sta_bit <= 0;
sto_bit <= 0;
r_data[0] <= 0;
r_data[1] <= 0;
r_data[2] <= 0;
r_data[3] <= 0;
r_data[4] <= 0;
r_data[5] <= 0;
r_data[6] <= 0;
r_data[7] <= 0;
end
else if(bps_clk_16x)begin
case(bps_cnt)
0:begin
sta_bit <= 0;
sto_bit <= 0;
r_data[0] <= 0;
r_data[1] <= 0;
r_data[2] <= 0;
r_data[3] <= 0;
r_data[4] <= 0;
r_data[5] <= 0;
r_data[6] <= 0;
r_data[7] <= 0;
end
5,6,7,8,9,10,11:sta_bit <= sta_bit + uart_rx;
21,22,23,24,25,26,27: r_data[0] <= r_data[0] + uart_rx;
37,38,39,40,41,42,43: r_data[1] <= r_data[1] + uart_rx;
53,54,55,56,57,58,59: r_data[2] <= r_data[2] + uart_rx;
69,70,71,72,73,74,75: r_data[3] <= r_data[3] + uart_rx;
85,86,87,88,89,90,91: r_data[4] <= r_data[4] + uart_rx;
101,102,103,104,105,106,107: r_data[5] <= r_data[5] + uart_rx;
117,118,119,120,121,122,123: r_data[6] <= r_data[6] + uart_rx;
133,134,135,136,137,138,139: r_data[7] <= r_data[7] + uart_rx;
149,150,151,152,153,154,155: sto_bit <= sto_bit + uart_rx;
default:;
endcase
end
end
//数据判断
always@(posedge Clk or negedge Reset_n)begin
if(!Reset_n)
Data <= 0;
else if(bps_clk_16x && (bps_cnt == 160))begin
Data[0] <= (r_data[0] >= 4)?1'b1:1'b0;
Data[1] <= (r_data[1] >= 4)?1'b1:1'b0;
Data[2] <= (r_data[2] >= 4)?1'b1:1'b0;
Data[3] <= (r_data[3] >= 4)?1'b1:1'b0;
Data[4] <= (r_data[4] >= 4)?1'b1:1'b0;
Data[5] <= (r_data[5] >= 4)?1'b1:1'b0;
Data[6] <= (r_data[6] >= 4)?1'b1:1'b0;
Data[7] <= (r_data[7] >= 4)?1'b1:1'b0;
end
end
// always@(posedge Clk or negedge Reset_n)
// if(!Reset_n)
// Data <= 0;
// else if(bps_clk_16x && (bps_cnt == 159))begin
// Data[0] <= r_data[0][2];
// Data[1] <= r_data[1][2];
// Data[2] <= r_data[2][2];
// Data[3] <= r_data[3][2];
// Data[4] <= r_data[4][2];
// Data[5] <= r_data[5][2];
// Data[6] <= r_data[6][2];
// Data[7] <= r_data[7][2];
// end
//******************** Uart接受完成标志 ****************** \\
always@(posedge Clk or negedge Reset_n)begin
if(!Reset_n)
Rx_Done <= 0;
else if(bps_cnt == 'd160 && bps_clk_16x)
Rx_Done <= 1;
else
Rx_Done <= 0;
end
endmodule
这边注意RX-done 以及RX_EN拉低的条件 。
TB文件
`timescale 1ns / 1ps
module uart_byte_rx_tb();
reg Clk ;
reg Reset_n ;
reg [2:0] Baud_Set;
reg uart_rx ;
wire [7:0] Data ;
wire Rx_Done ;
uart_byte_rx uart_byte_rx_u0(
.Clk (Clk ),
.Reset_n (Reset_n ),
.Baud_Set ( 'd4 ),
.uart_rx (uart_rx ),
.Data (Data ),
.Rx_Done (Rx_Done )
);
initial Clk = 0;
always #10 Clk = ~Clk;
initial begin
Reset_n = 0;
#200;
Reset_n = 1;
#20;
uart_tx_byte(8'haa);
#90000//@(posedge Rx_Done);
#5000;
uart_tx_byte(8'h55);
#90000;
#5000;
$stop;
end
task uart_tx_byte;
input [7:0]tx_data;
begin
uart_rx = 1;
#20;
uart_rx = 0;
#8680;
uart_rx = tx_data[0];
#8680;
uart_rx = tx_data[1];
#8680;
uart_rx = tx_data[2];
#8680;
uart_rx = tx_data[3];
#8680;
uart_rx = tx_data[4];
#8680;
uart_rx = tx_data[5];
#8680;
uart_rx = tx_data[6];
#8680;
uart_rx = tx_data[7];
#8680;
uart_rx = 1;
#8680;
end
endtask
endmodule
四、Uart串口控制LED灯闪烁
4.1 设计思路分析
4.2 verilog设计及仿真
4.2.1 .v文件
以下分别为cmd,LED以及top顶层代码:
`timescale 1ns / 1ps
//
// Description: 控制命令转换模块:将接受的8字节数据进行缓存,通过判断输出周期/控制的控制字给予LED模块
//
module uart_cmd(
Clk ,
Reset_n ,
rx_data ,
rx_done ,
ctrl ,
time_set,
);
input Clk ;
input Reset_n ;
input [7:0] rx_data ;
input rx_done ;
output reg [7:0] ctrl ;
output reg [31:0] time_set;
reg [7:0] data_str [7:0];
reg r_rx_done;
//***************** 字节缓存模块 *****************\\
always @(posedge Clk or negedge Reset_n) begin
if(!Reset_n)begin
data_str[7] <= 'd0;
data_str[6] <= 'd0;
data_str[5] <= 'd0;
data_str[4] <= 'd0;
data_str[3] <= 'd0;
data_str[2] <= 'd0;
data_str[1] <= 'd0;
data_str[0] <= 'd0;
end
else if(rx_done) begin
data_str[7] <= rx_data ;
data_str[6] <= data_str[7];
data_str[5] <= data_str[6];
data_str[4] <= data_str[5];
data_str[3] <= data_str[4];
data_str[2] <= data_str[3];
data_str[1] <= data_str[2];
data_str[0] <= data_str[1];
end
else begin
data_str[7] <= data_str[7];
data_str[6] <= data_str[6];
data_str[5] <= data_str[5];
data_str[4] <= data_str[4];
data_str[3] <= data_str[3];
data_str[2] <= data_str[2];
data_str[1] <= data_str[1];
data_str[0] <= data_str[0];
end
end
//***************** 帧结构判断输出周期/控制 控制字 *****************\\
always @(posedge Clk or negedge Reset_n) begin
if(!Reset_n)
r_rx_done <= 'd0;
else
r_rx_done <= rx_done;
end
always @(posedge Clk or negedge Reset_n) begin
if(!Reset_n)begin
ctrl <= 'd0;
time_set <= 'd0;
end
else if(r_rx_done)begin
if((data_str[0] == 8'h55)&&(data_str[1]==8'hA5)&&(data_str[7]==8'hF0))begin
time_set[31:24] <= data_str[2];
time_set[23:16] <= data_str[3];
time_set[15: 8] <= data_str[4];
time_set[ 7: 0] <= data_str[5];
ctrl <= data_str[6];
end
else begin
time_set <= time_set;
ctrl <= ctrl;
end
end
else begin
time_set <= time_set;
ctrl <= ctrl ;
end
end
endmodule
`timescale 1ns / 1ps
//
// Description: led亮灭闪烁
//
module led_flash(
Clk ,
Reset_n ,
Time ,
ctrl ,
led
);
input Clk ;
input Reset_n ;
input [7:0] ctrl ;
input [31:0] Time ;
output reg led ;
reg[31:0] time_cnt;
always @(posedge Clk or negedge Reset_n) begin
if(!Reset_n)
time_cnt <= 'd0;
else if(time_cnt >= Time -1)
time_cnt <= 'd0;
else
time_cnt <= time_cnt + 1;
end
reg[2:0] led_cnt;
always @(posedge Clk or negedge Reset_n) begin
if(!Reset_n)
led_cnt <= 'd0;
else if(time_cnt >= Time -1)begin
if(led_cnt >= 7)
led_cnt <= 'd0;
else
led_cnt <= led_cnt + 1;
end
else
led_cnt <= led_cnt;
end
always @(posedge Clk or negedge Reset_n) begin
if(!Reset_n)
led <= 'd0;
else begin
case(led_cnt)
0:led <= ctrl[0];
1:led <= ctrl[1];
2:led <= ctrl[2];
3:led <= ctrl[3];
4:led <= ctrl[4];
5:led <= ctrl[5];
6:led <= ctrl[6];
7:led <= ctrl[7];
default:led <= 'd0;
endcase
end
end
endmodule
`timescale 1ns / 1ps
module uart_led_top(
Clk ,
Reset_n ,
uart_rx ,
led
);
input Clk ;
input Reset_n ;
input uart_rx ;
output led ;
wire [7:0] Data ;
wire Rx_Done ;
wire [7:0] ctrl ;
wire [31:0] time_set;
uart_byte_rx uart_byte_rx_u0(
.Clk (Clk ),
.Reset_n (Reset_n ),
.Baud_Set ('d4 ),
.uart_rx (uart_rx ),
.Data (Data ),
.Rx_Done (Rx_Done )
);
uart_cmd uart_cmd_u0(
.Clk (Clk ),
.Reset_n (Reset_n ),
.rx_data (Data ),
.rx_done (Rx_Done ),
.ctrl (ctrl ),
.time_set (time_set )
);
led_flash led_flash_u0(
.Clk (Clk ),
.Reset_n (Reset_n ),
.Time (time_set ),
.ctrl (ctrl ),
.led (led )
);
endmodule
4.2.2 tb文件及仿真
`timescale 1ns / 1ps
module uart_led_tb();
reg Clk ;
reg Reset_n ;
wire Led ;
reg uart_rx ;
wire [31:0] delay_time ;
uart_led_top uart_led_top_u0(
.Clk (Clk ),
.Reset_n (Reset_n ),
.uart_rx (uart_rx ),
.led (Led )
);
parameter Baud_Set = 3'd4;
assign delay_time = (Baud_Set == 3'd0) ? 20'd104166:
(Baud_Set == 3'd1) ? 20'd52083:
(Baud_Set == 3'd2) ? 20'd26041:
(Baud_Set == 3'd3) ? 20'd17361:
20'd8680;
initial Clk = 1;
always#10 Clk = ~Clk;
initial begin
Reset_n = 0;
uart_rx = 1;
#201;
Reset_n = 1;
#200;
uart_tx_byte(8'h55);
#(delay_time*10);
uart_tx_byte(8'ha5);
#(delay_time*10);
uart_tx_byte(8'h55);
#(delay_time*10);
uart_tx_byte(8'ha5);
#(delay_time*10);
uart_tx_byte(8'h00);
#(delay_time*10);
uart_tx_byte(8'h00);
#(delay_time*10);
uart_tx_byte(8'hc3);
#(delay_time*10);
uart_tx_byte(8'h50);
#(delay_time*10);
uart_tx_byte(8'hAA);
#(delay_time*10);
uart_tx_byte(8'hf0);
#(delay_time*10);
uart_tx_byte(8'h55);
#(delay_time*10);
uart_tx_byte(8'ha5);
#(delay_time*10);
uart_tx_byte(8'h9a);
#(delay_time*10);
uart_tx_byte(8'h78);
#(delay_time*10);
uart_tx_byte(8'h56);
#(delay_time*10);
uart_tx_byte(8'h34);
#(delay_time*10);
uart_tx_byte(8'h12);
#(delay_time*10);
uart_tx_byte(8'hf1);
#(delay_time*10);
#15000000;
$stop;
end
task uart_tx_byte;
input [7:0]tx_data;
begin
uart_rx = 1;
#20;
uart_rx = 0;
#delay_time;
uart_rx = tx_data[0];
#delay_time;
uart_rx = tx_data[1];
#delay_time;
uart_rx = tx_data[2];
#delay_time;
uart_rx = tx_data[3];
#delay_time;
uart_rx = tx_data[4];
#delay_time;
uart_rx = tx_data[5];
#delay_time;
uart_rx = tx_data[6];
#delay_time;
uart_rx = tx_data[7];
#delay_time;
uart_rx = 1;
#delay_time;
end
endtask
endmodule
关于:rx_done
r_rx_done:这里由于最高位的缓存数据与输入数据间存在一个时钟周期的延迟,因此需要对
rx_done 信号打一拍后再作为条件去判断缓存中的数据。
4.3 板级试验
指令多发,有效指令为 55 A5 02 FA F0 80 F0 F0。因此 LED 按照亮亮亮亮灭灭灭(8’b11110000,0xF0)的规律,每 1s 变化一次,每 8 次变化为一组,不断循环变化。 与操作对应的板级现象吻合。亮四秒灭四秒,可以正确显示操作Led灯。