RS232串口通信实验Verilog
RS232串口通信实验几乎是入门必做实验,这是本人学习过程中做的练习。首先是分频模块,分频模块是学习了OpenCore上的uart2bus项目的。http://opencores.org/project,uart2bus
该模块接收任意频率的输入频率(clk_i),输出频率(记为clk_o)由baud_freq_i和baud_limit_i根据以下公式计算,使用时首先需要根据输入频率和输出频率计算出baud_freq_i和baud_limit_i这两个参数。
[tex] baud\_freq = \frac{clk\_o}{GCD(clk\_i, clk\_o)}[/tex]
[tex] baud\_limit = \frac{clk\_i}{GCD(clk\_i, clk\_o)} - baud\_freq [/tex]
公式里的GCD(Greatest Common Divisor)表示取2个数的最大公约数。
这里系统时钟频率为50MHz,串口频率为9600,通常我们是在中间进行采样。因此分频模块分出来的频率为16*9600。模块如下:
/* * this module has been changed to receive the baud rate * dividing counter from registers. * the two registers should be calculated as follows: * first register: * baud_freq = 16*baud_rate / gcd(global_clock_freq, 16*baud_rate) * second register: * baud_limit = (global_clock_freq / * gcd(global_clock_freq, 16*baud_rate)) - baud_freq */ module baud_gen(clk_i, rst_i, ce16_o, baud_freq_i, baud_limit_i); input clk_i; input rst_i; output ce16_o; input [11:0] baud_freq_i; input [15:0] baud_limit_i; reg ce16_o; reg [15:0] count; always @(posedge clk_i or negedge rst_i) begin if (!rst_i) count <= 16'h0; else if (count >= baud_limit_i) count <= count - baud_limit_i; else count <= count + baud_freq_i; end always @(posedge clk_i or negedge rst_i) begin if (!rst_i) ce16_o <= 1'b0; else if (count >= baud_limit_i) ce16_o <= 1'b1; else ce16_o <= 1'b0; end endmodule
分频模块的仿真波形如下,上面是50M的输入时钟,下面是分频后的,需要注意的是,分频后的时钟的占空比不是1:1的,每一个9600Hz的周期中产生16个高电平,高电平维持一个20ns(50MHz的一个周期)
计算baud_freq和baud_limitC程序如下,程序中的输入频率clk_i为50MHZ,输出频率为9600*16。
#include <stdio.h> int GCD(int a, int b) { if (b == 0) return a; else return GCD(b, a%b); } int main() { int clk_i = 50*1000*1000; int clk_o = 9600*16; int gcd = GCD(clk_i, clk_o); int baud_freq = clk_o/gcd; int baud_limit = (clk_i/gcd) - baud_freq; printf("baud_freq: 0x%x, baud_limit: 0x%x\n", baud_freq, baud_limit); return 0; }
然后是发送模块:
module uart_tx(clk_i, rst_i, ce16_i, ser_o, tx_data_i, new_tx_i, tx_int_o, tx_busy_o); input clk_i; input rst_i; input ce16_i; output ser_o; input [7:0] tx_data_i; input new_tx_i; output tx_int_o; output tx_busy_o; reg ser_o; reg tx_int_o; reg tx_busy_o; reg [3:0] count; reg [3:0] bit_count; reg [8:0] tx_data; wire ce1_end; assign ce1_end = (count == 4'b1111) & ce16_i; always @(posedge clk_i or negedge rst_i) begin if (!rst_i) count <= 4'h0; else if (tx_busy_o & ce16_i) count <= count + 1'b1; else if (!tx_busy_o & ce16_i) count <= 1'b0; end always @(posedge clk_i or negedge rst_i) begin if (!rst_i) tx_busy_o <= 1'b0; else if (!tx_busy_o & new_tx_i) tx_busy_o <= 1'b1; else if (tx_busy_o & ce1_end & (bit_count == 4'd9)) tx_busy_o <= 1'b0; end always @(posedge clk_i or negedge rst_i) begin if (!rst_i) bit_count <= 4'h0; else if (tx_busy_o & ce1_end) bit_count <= bit_count + 1'b1; else if (!tx_busy_o) bit_count <= 4'h0; end always @(posedge clk_i or negedge rst_i) begin if (!rst_i) tx_data <= 9'h0; else if (!tx_busy_o) tx_data <= {tx_data_i, 1'b0}; else if (tx_busy_o & ce1_end) tx_data <= {1'b1, tx_data[8:1]}; end always @(posedge clk_i or negedge rst_i) begin if (!rst_i) ser_o <= 1'b1; else if (tx_busy_o) ser_o <= tx_data[0]; else ser_o <= 1'b1; end always @(posedge clk_i or negedge rst_i) begin if (!rst_i) tx_int_o <= 1'b0; else if (tx_busy_o & ce1_end & (bit_count == 4'd9)) tx_int_o <= 1'b1; else tx_int_o <= 1'b0; end endmodule
接收模块:
module uart_rx(clk_i, rst_i, ce16_i, ser_i, rx_data_o, rx_int_o); input clk_i; input rst_i; input ce16_i; input ser_i; output [7:0] rx_data_o; output rx_int_o; reg [7:0] rx_data_o; reg rx_int_o; reg [7:0] rx_data; reg [1:0] ser_in; reg [3:0] count; reg [3:0] bit_count; reg rx_busy; wire ce1_mid; wire ce1_end; always @(posedge clk_i or negedge rst_i) begin if (!rst_i) ser_in <= 2'b11; else ser_in <= {ser_in[0], ser_i}; end always @(posedge clk_i or negedge rst_i) begin if (!rst_i) count <= 4'h0; else if ((rx_busy | (ser_in[1] == 1'b0)) & ce16_i) count <= count + 1'b1; else if (!rx_busy & ce16_i) count <= 4'h0; end assign ce1_mid = ((count == 4'b0111) & ce16_i); assign ce1_end = ((count == 4'b1111) & ce16_i); always @(posedge clk_i or negedge rst_i) begin if (!rst_i) rx_busy <= 1'b0; else if (!rx_busy & ce1_mid) rx_busy <= 1'b1; else if (rx_busy & ce1_end & (bit_count == 4'd9)) rx_busy <= 1'b0; end always @(posedge clk_i or negedge rst_i) begin if (!rst_i) bit_count <= 4'h0; else if (rx_busy & ce1_mid) bit_count <= bit_count + 1'b1; else if (!rx_busy) bit_count <= 4'h0; end always @(posedge clk_i or negedge rst_i) begin if (!rst_i) rx_data <= 8'h0; else if (ce1_mid) rx_data <= {ser_in[1], rx_data[7:1]}; end always @(posedge clk_i or negedge rst_i) begin if (!rst_i) begin rx_data_o <= 8'h0; rx_int_o <= 1'b0; end else if (ce1_end & (bit_count == 4'd8)) begin rx_data_o <= rx_data; rx_int_o <= 1'b1; end else rx_int_o <= 1'b0; end endmodule
顶层模块:
module uart_loop_top(clk_i, rst_i, ser_i, ser_o); input clk_i; input rst_i; input ser_i; output ser_o; // baud rate generator parameters for 9600 baud on 50MHz clock `define D_BAUD_FREQ 12'h30 `define D_BAUD_LIMIT 16'h3CD9 wire ce16; wire [11:0] baud_freq; wire [15:0] baud_limit; wire [7:0] rx_data; reg [7:0] rx_reg; reg start_tx; wire tx_int; wire tx_busy; assign baud_freq = `D_BAUD_FREQ; assign baud_limit = `D_BAUD_LIMIT; baud_gen baud_gen1(.clk_i(clk_i), .rst_i(rst_i), .ce16_o(ce16), .baud_freq_i(baud_freq), .baud_limit_i(baud_limit)); uart_rx uart_rx1(.clk_i(clk_i), .rst_i(rst_i), .ce16_i(ce16), .ser_i(ser_i), .rx_data_o(rx_data), .rx_int_o(rx_int)); uart_tx uart_tx1(.clk_i(clk_i), .rst_i(rst_i), .ce16_i(ce16), .ser_o(ser_o), .tx_data_i(rx_reg), .new_tx_i(start_tx), .tx_int_o(tx_int), .tx_busy_o(tx_busy)); always @(posedge clk_i or negedge rst_i) begin if (!rst_i) rx_reg <= 8'h0; else if (rx_int & ~tx_busy) rx_reg <= rx_data; end always @(posedge clk_i or negedge rst_i) begin if (!rst_i) start_tx <= 1'b0; else if (rx_int & ~tx_busy) start_tx <= 1'b1; else start_tx <= 1'b0; end endmodule