赞
踩
使用verilog实现uart协议,能够和pc进行通信,实现串口回环功能,各参数设置如下:
系统时钟为50M,115200波特率下,每一个bit占50M/115200 = 434时钟周期。
停止位任意即不考虑停止位。代码追求简洁,能用就行。
注意:uart协议是LSB优先的
接收模块从pc端接收到异步的串行信号,解析为valid信号+一字节并行数据,传递到其他模块,回环的话,直接传递到发送模块。
流程:
代码:
module uart_rx( input clk, // 50M input rst_n, // uart side input rx, // host side output reg valid, output reg [7:0] data ); //---------------------------------信号声明----------------------------------- localparam BAUD_RATE = 115200; localparam BAUD_CNT_MAX = (50_000_000 / BAUD_RATE) - 1; // rx打两拍,第三拍检测下降沿 reg [2:0] rx_shift; // 计数器使能 reg cnt_ena; // baud计数器 reg [12:0] baud_cnt; // bit计数器 reg [3:0] bit_cnt; //--------------------------------------------------------------------------- // rx打两拍,第三拍检测下降沿 always @(posedge clk, negedge rst_n) begin if(!rst_n) rx_shift <= 3'b111; else rx_shift <= {rx, rx_shift[2:1]}; end // 计数器使能 always @(posedge clk, negedge rst_n) begin if(!rst_n) cnt_ena <= 0; else if(~rx_shift[1] & rx_shift[0])// rx下降沿 cnt_ena <= 1'b1; else if(baud_cnt == BAUD_CNT_MAX && bit_cnt == 4'd8) cnt_ena <= 1'b0; end // baud cnt always @(posedge clk, negedge rst_n) begin if(!rst_n) baud_cnt <= 0; else if(cnt_ena) begin if(baud_cnt == BAUD_CNT_MAX) baud_cnt <= 0; else baud_cnt <= baud_cnt + 13'd1; end end // bit cnt always @(posedge clk, negedge rst_n) begin if(!rst_n) bit_cnt <= 0; else if(cnt_ena && baud_cnt == BAUD_CNT_MAX) begin if(bit_cnt == 4'd8) bit_cnt <= 0; else bit_cnt <= bit_cnt + 4'd1; end end // output valid always @(posedge clk, negedge rst_n) begin if(!rst_n) valid <= 0; else if(baud_cnt == BAUD_CNT_MAX && bit_cnt == 4'd8) valid <= 1'b1; else valid <= 1'b0; end // output data always @(posedge clk, negedge rst_n) begin if(!rst_n) data <= 0; else if(cnt_ena && baud_cnt == (BAUD_CNT_MAX / 2)) // 在每个bit中间采样进入移位寄存器 data <= {rx_shift[0], data[7:1]}; end endmodule
对该模块进行简单的波形仿真,对其发送三次数据,分别是8’h01, 8’hab, 8’hff, tb如下:
`timescale 1ps/1ps module uart_rx_tb; reg clk = 1'b1; always #10 clk = ~clk; reg rst_n = 1'b0; reg rx = 1'b1; wire valid; wire [7:0] data; integer i = 0; initial begin #30 rst_n = 1'b1; #10; #10000; receieve_data(8'h01); receieve_data(8'hab); receieve_data(8'hff); #100 $stop(2);// $finish; end task receieve_data; input [7:0] sim_data; begin // 起始位 rx <= 1'b0; #(20 * 434); // 8bit数据 for(i = 0; i < 8; i = i + 1) begin rx <= sim_data[i]; #(20 * 434); end // 停止位 rx <= 1'b1; #(20 * 434); #1000; end endtask uart_rx inst_uart_rx ( .clk (clk), .rst_n (rst_n), .rx (rx), .valid (valid), .data (data) ); endmodule
波形图如下,可以观察到,三次valid拉高时的data正好对应了8’h01, 8’hab, 8’hff:
发送模块接收内部的valid+data,转化为uart串行信号通过io输出。
流程:
代码:
module uart_tx( input clk, // 50M input rst_n, // host side input valid, input [7:0] data, // uart side output tx ); //------------------------------------信号声明--------------------------- localparam BAUD_RATE = 115200; localparam BAUD_CNT_MAX = (50_000_000 / BAUD_RATE) - 1; // 数据寄存 + 起始位 reg [8:0] data_start_reg; // 计数器使能 reg cnt_ena; // baud计数器 reg [12:0] baud_cnt; // bit计数器 reg [3:0] bit_cnt; //---------------------------------------------------------------------- // 数据寄存 + 起始位 always @(posedge clk, negedge rst_n) begin if(!rst_n) data_start_reg <= 0; else if(valid) data_start_reg <= {data, 1'b0}; end // cnt_ena always @(posedge clk, negedge rst_n) begin if(!rst_n) cnt_ena <= 0; else if(valid) cnt_ena <= 1'b1; else if(baud_cnt == BAUD_CNT_MAX && bit_cnt == 4'd8) cnt_ena <= 0; end // baud_cnt always @(posedge clk, negedge rst_n) begin if(!rst_n) baud_cnt <= 0; else if(cnt_ena) begin if(baud_cnt == BAUD_CNT_MAX) baud_cnt <= 0; else baud_cnt <= baud_cnt + 13'd1; end end // bit_cnt always @(posedge clk, negedge rst_n) begin if(!rst_n) bit_cnt <= 0; else if(cnt_ena && baud_cnt == BAUD_CNT_MAX) begin if(bit_cnt == 4'd8) bit_cnt <= 0; else bit_cnt <= bit_cnt + 4'd1; end end // tx assign tx = cnt_ena ? data_start_reg[bit_cnt] : 1'b1; endmodule
对该模块进行简单的波形仿真,让其发送三次数据,分别是8’h01, 8’hab, 8’hff, tb如下:
`timescale 1ps/1ps module uart_tx_tb; reg clk = 1'b1; always #10 clk = ~clk; reg rst_n = 1'b0; reg valid = 1'b0; reg [7:0] data = 8'd0; wire tx; initial begin #30 rst_n = 1'b1; #10; #10000; uart_send(8'h01); uart_send(8'hab); uart_send(8'hff); #100 $stop(2);// $finish; end task uart_send; input [7:0] send_data; begin valid <= 1'b1; data <= send_data; #20; valid <= 1'b0; data <= 8'd0; #(20 * 434 * 10); // 10baud #1000; end endtask uart_tx inst_uart_tx ( .clk (clk), .rst_n (rst_n), .valid (valid), .data (data), .tx (tx) ); endmodule
波形如下,可以看到,valid信号后,tx端会将数据转化为uart串行数据发送出去,由于data只持续1时钟周期,所以图片看不清楚,三次valid对应的data分别是8’h01, 8’hab, 8’hff:
顶层模块就不细说了,将接收模块的valid+data接到发送模块的valid+data即可
串口软件使用正点原子的XCOM,发送字符串hello world!,接收如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。