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

Posted by 独孤求真 2010年9月21日 20:24