串口通信是将并行数据转换为串行数据进行传输,常见的电平信号有TTL、RS-232、RS-485。盘古50K使用的是CP2102芯片,可以直接通过USB接口与PC进行串口通信。
对于我们常用的CH340芯片,CP2102在学习时使用的较少。因此第一步是对其驱动进行安装。
CP210x USB to UART Bridge VCP Drivers - Silicon Labs (silabs.com)
驱动直接在官网下载即可,之后直接点开进行安装。
之后便是串口通信的verilog代码。分为发送和接收两个部分。
(1)发送模块
`
module uart_tx
(
input wire sys_clk,
input wire sys_rst_n,
input wire [7:0] tx_data,
input wire tx_flag,
output reg tx
);
parameter BAUD_CNT_MAX = 16'd5028;
reg work_en;
reg [15:0] baud_cnt;
reg bit_flag;
reg [3:0] bit_cnt;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
work_en <= 1'b0;
else if(tx_flag == 1'b1)
work_en <= 1'b1;
else if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
work_en <= 1'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
baud_cnt <= 16'b0;
else if((work_en == 1'b1) && (baud_cnt<= BAUD_CNT_MAX - 1'b1))
baud_cnt <= baud_cnt + 1'b1;
else
baud_cnt <= 16'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
bit_flag <= 1'b0;
else if(baud_cnt == 1'b1)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
bit_cnt <= 4'b0;
else if((bit_flag == 1'b1) && (bit_cnt <= 4'd8))
bit_cnt <= bit_cnt + 1'b1;
else if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
bit_cnt <= 4'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
tx <= 1'b1;
else if((bit_flag == 1'b1) && (bit_cnt == 4'd0))
tx <= 1'b0;
else if((bit_flag == 1'b1) && (bit_cnt >= 4'd1) && (bit_cnt <= 4'd8))
tx <= tx_data[bit_cnt - 1];
else if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
tx <= 1'b1;
endmodule
(2)接收模块
`
module uart_rx
(
input wire sys_clk,
input wire sys_rst_n,
input wire rx,
output reg [7:0] rx_data,
output reg rx_flag
);
parameter BAUD_CNT_MAX = 16'd5028;
reg reg1;
reg reg2;
reg reg3;
reg start_flag;
reg work_en;
reg [15:0] baud_cnt;
reg bit_flag;
reg [3:0] bit_cnt;
reg [7:0] rx_data_temp;
reg rx_flag_temp;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
reg1 <= 1'b1;
else
reg1 <= rx;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
reg2 <= 1'b1;
else
reg2 <= reg1;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
reg3 <= 1'b1;
else
reg3 <= reg2;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
start_flag <= 1'b0;
else if((reg2 == 1'b0) && (reg3 == 1'b1))
start_flag <= 1'b1;
else
start_flag <= 1'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
work_en <= 1'b0;
else if((reg3 == 1'b0) && (start_flag == 1'b1))
work_en <= 1'b1;
else if((bit_cnt == 4'd9) && (bit_flag == 1'b1))
work_en <= 1'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
baud_cnt <= 16'b0;
else if((work_en == 1'b1) && (baud_cnt <= BAUD_CNT_MAX - 1'b1))
baud_cnt <= baud_cnt + 1'b1;
else
baud_cnt <= 16'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
bit_flag <= 1'b0;
else if(baud_cnt == (BAUD_CNT_MAX >> 1))
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
bit_cnt <= 4'b0;
else if(bit_flag == 1'b1)
bit_cnt <= bit_cnt + 1'b1;
else if(bit_cnt >= 4'd10)
bit_cnt <= 4'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
rx_data_temp <= 8'b0;
else if((bit_flag == 1'b1) && (bit_cnt >= 4'd1) && (bit_cnt <= 4'd8))
rx_data_temp <= {reg3,rx_data_temp[7:1]};
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
rx_flag_temp <= 1'b0;
else if((bit_cnt == 4'd9) && (bit_flag == 1'b1))
rx_flag_temp <= 1'b1;
else
rx_flag_temp <= 1'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
rx_data <= 8'b0;
else if(rx_flag_temp == 1'b1)
rx_data <= rx_data_temp;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
rx_flag <= 1'b0;
else if(rx_flag_temp == 1'b1)
rx_flag <= 1'b1;
else
rx_flag <= 1'b0;
endmodule
(3)顶层模块
串口回环实验就是将串口所接收到的数据再发送出去,因此顶层模块就是将接收模块和发送模块串联起来。
`
module rs232
(
input wire sys_clk,
input wire sys_rst_n,
input wire rx,
output wire tx
);
wire [7:0] rx_data;
wire rx_flag;
uart_rx uart_rx_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.rx (rx ),
.rx_data (rx_data ),
.rx_flag (rx_flag )
);
uart_tx uart_tx_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.tx_data (rx_data ),
.tx_flag (rx_flag ),
.tx (tx )
);
endmodule
查看RTL视图
和预期结构相吻合,再通过仿真验证。
先编写仿真文件
`
`timescale 1ns/1ns
module tb_rs232();
reg sys_clk;
reg sys_rst_n;
reg rx;
wire tx;
initial begin
sys_clk <= 1'b0;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk <= ~sys_clk;
initial begin
#200
rx_task(8'd0);
rx_task(8'd1);
rx_task(8'd2);
rx_task(8'd3);
rx_task(8'd4);
rx_task(8'd5);
rx_task(8'd6);
rx_task(8'd7);
rx_task(8'd8);
rx_task(8'd9);
end
task rx_task
(
input [7:0] rx_data
);
integer i;
for(i=0;i<10;i=i+1)
begin
case (i)
0: rx <= 1'b0;
1: rx <= rx_data[0];
2: rx <= rx_data[1];
3: rx <= rx_data[2];
4: rx <= rx_data[3];
5: rx <= rx_data[4];
6: rx <= rx_data[5];
7: rx <= rx_data[6];
8: rx <= rx_data[7];
9: rx <= 1'b1;
endcase
#(5028*20);
end
endtask
rs232 rs232_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.rx (rx ),
.tx (tx )
);
endmodule
使用ModelSim进行仿真,得到仿真波形
通过下载验证,观察现象
发送数据与接收数据一致,符合实验要求