table of Contents
Four, design input
As shown in the figure, the mind map contains the state of the design, function design, and signal design. Based on this design, write the code.
1. Main module: instantiate button module
/*************串口通信********************/
module serial_port(
input clk ,//50M时钟
input rst_n ,//复位
input [ 2: 0] key_in ,//按键输入
input rx_data ,//计算机端串口数据接收
output reg tx_data ,//FPGA发送数据
output reg [ 7: 0] led //[3:0]是板上LED0-LED3,[5:4]是RS232的Tx和Rx端的led指示灯,[7:6]是can的Rxd和Txd端的led指示灯
);
parameter time_1bit = 13'd5208 ;//1bit数据计数次数
parameter data = 8 'hff ;//FPGA发送数据时循环发送数据
reg [ 1: 0] state ;//状态,包含3个状态,空闲、PC向FPGA发送数据、FPGA向PC发送数据
reg [ 12: 0] cnt0 ;//发送1bit数据时钟数的计数,9600bps为1/9600s=104166.66ns=5208时钟周期
reg [ 3: 0] cnt1 ;//发送数据计数
reg flag ;//可以计数指示信号
reg data_in_vld ;//数据输入使能
wire add_cnt0 ;//计数器cnt0加一条件
wire end_cnt0 ;//计数器cnt0结束条件
wire add_cnt1 ;//计数器cnt1加一条件
wire end_cnt1 ;//计数器cnt1结束条件
wire neg_flag ;//始接收数据指示信号
reg rx_data_ff1 ;//rx_data打一拍,防止亚稳态
reg rx_data_ff2 ;//rx_data打两拍,防止亚稳态
reg rx_data_ff3 ;//再打一拍,获取rx_data_ff2的下降沿neg_flag
wire [ 9: 0] tx_data_f ;//带有起始位和停止位的数据
wire [ 3: 0] key_vld ;//按键输出
key_move(
.clk (clk) ,//50M时钟
.rst_n (rst_n) ,//复位
.key_in (key_in) ,//按键输入
.key_vld (key_vld) //按键输出
);
//状态输出
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)
state <= 2'b00;
else if(key_vld==3'b110)
state <= 2'b00;
else if(key_vld==3'b101)
state <= 2'b01;
else if(key_vld==3'b011)
state <= 2'b10;
else
state <= state;
end
//cnt0计数
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1'b1;
end
end
assign add_cnt0 = (state==2'b01 || state==2'b10) && flag==1;
assign end_cnt0 = add_cnt0 && cnt0==time_1bit-1;
cnt1计数
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
cnt1 <= 0;
end
else if(add_cnt1) begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1'b1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1==10-1;
flag输出,flag==1时计数
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)
flag <= 0;
else if(state==2'b01&&neg_flag || state==2'b10&&data_in_vld==1)
flag <= 1;
else if(end_cnt1)
flag <= 0;
else
flag <= flag;
end
//数据输入使能
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)
data_in_vld <= 1;
else if(state==2'b10 && flag==1)
data_in_vld <= 0;
else if(state==2'b10 && flag==0)
data_in_vld <= 1;
end
//发送一个数据帧
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
tx_data <= 1;
end
else if(state==2'b10 && flag==1 && cnt0==0) begin
tx_data <= tx_data_f[cnt1];
end
end
assign tx_data_f = {1'b1,data,1'b0};
//打两拍,防止亚稳态,同时捕获下降沿
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rx_data_ff1 <= 1;
rx_data_ff2 <= 1;
rx_data_ff3 <= 1;
end
else begin
rx_data_ff1 <= rx_data ;
rx_data_ff2 <= rx_data_ff1;
rx_data_ff3 <= rx_data_ff2;
end
end
assign neg_flag = rx_data_ff2==0 && rx_data_ff3==1;
//输出led信号
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
led <= 8'hff;
end
else if(state==2'b01 && flag==1 && cnt0==time_1bit/2 && cnt1>0)begin
led[cnt1-1] <= rx_data_ff2;
end
end
endmodule
2. Key debounce module
/**********按键按下,输出一个有用脉冲信号******/
module key_move(
input clk ,//50M时钟
input rst_n ,//复位
input [ KEY_W-1: 0] key_in ,//按键输入
output reg [ KEY_W-1: 0] key_vld //按键输出
);
parameter DATA_W = 20 ;
parameter KEY_W = 3 ;
parameter TIME_20MS = 1_000_000 ;
reg [DATA_W-1:0] cnt ;//计数器计数20ms,防止抖动
wire add_cnt ;//计数器加一条件
wire end_cnt ;//计数器结束条件
reg flag ;//计数条件
reg [KEY_W-1 :0] key_in_ff1 ;//按键输入打两拍,防止亚稳态
reg [KEY_W-1 :0] key_in_ff0 ;//按键输入打一拍
//计数器,计数20ms防止抖动
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
cnt <= 20'b0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 20'b0;
else
cnt <= cnt + 1'b1;
end
else begin
cnt <= 0;
end
end
assign add_cnt = flag==1'b0 && (&key_in_ff1==0);
assign end_cnt = add_cnt && cnt == TIME_20MS - 1;
//计数条件
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
flag <= 1'b0;
end
else if(end_cnt)begin
flag <= 1'b1;
end
else if(&key_in_ff1==1)begin
flag <= 1'b0;
end
end
//按键输入打两拍,防止亚稳态
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_in_ff0 <= {
{KEY_W}{1'b1}};
key_in_ff1 <= {
{KEY_W}{1'b1}};
end
else begin
key_in_ff0 <= key_in ;
key_in_ff1 <= key_in_ff0;
end
end
//按键按下,输出一个有用脉冲信号
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_vld <= {
{KEY_W}{1'b1}};
end
else if(end_cnt)begin
key_vld <= key_in_ff1;
end
else begin
key_vld <= {
{KEY_W}{1'b1}};
end
end
endmodule
Five, simulation test
Write simulation files and test their functions. Do not add the button module to test.
// Generated on "04/07/2020 11:04:56"
// Verilog Test Bench template for design : serial_port
//
// Simulation tool : ModelSim (Verilog)
//
`timescale 1 ns/ 1 ns
module serial_port_tb();
// constants
// test vector input registers
reg clk;
reg [2:0] key_in;
reg rst_n;
reg rx_data;
// wires
wire [7:0] led;
wire tx_data;
parameter clk_period = 20;
// assign statements (if any)
serial_port i1 (
// port map - connection between master ports and signals/registers
.clk(clk),
.key_in(key_in),
.led(led),
.rst_n(rst_n),
.rx_data(rx_data),
.tx_data(tx_data)
);
initial clk=0;
always #(clk_period/2) clk=~clk;
initial begin
//复位
#2;
rst_n = 0;
key_in = 3'b111;
rx_data = 1;
#(clk_period*5);
rst_n = 1;
#(clk_period*3);
//空闲状态
key_in = 3'b110;
#(clk_period);
key_in = 3'b111;
#(clk_period*50);
//状态1,接收PC端数据
key_in = 3'b101;
#(clk_period);
key_in = 3'b111;
#(clk_period*20);
//发送一个数据帧10'b0111111111
rx_data = 0;
#(clk_period*5208);
rx_data = 1;
#(clk_period*5208*9);
//发0;
#(clk_period*5208);
rx_data = 0送第二个数据帧
rx_data = ;
#(clk_period*5208*4);
rx_data = 1;
#(clk_period*5208);
rx_data = 0;
#(clk_period*5208);
rx_data = 1;
#(clk_period*5208);
rx_data = 0;
#(clk_period*5208);
rx_data = 1;
#(clk_period*5208);
//状态2,FPGA发送数据
key_in = 3'b011;
#(clk_period*5208*40);
$stop;
end
endmodule
The simulation graphics are as follows:
Six, lower board test
1. PC sends data to FPGA
Press button 2 to enter the state of PC sending data to FPGA.
Send data 8'h00, 8 LEDs should all light up.
Send data 8'hff, 8 LEDs should all be off.
Send data 8'ha0, 8 LEDs should light up partly.
2. FPGA sends data to PC
Press button 3, you can see the serial port receives the cyclic data sent by FPGA.
to sum up
Serial communication mainly understands its protocol and understands its data transmission frame format, which is actually very simple.