序列发送逻辑设计

今天学习了序列发送逻辑的设计,具体目标为:实现模块发送序列数据,每发送一个数据后,延时50us,再继续发送下一个数据,发送的数据可以自定义

一、数据发送模块

对于控制数据发送的模块,要求具有数据并行输入端使能端数据发送端等端口,具体代码参考如下:

module data_send(
	Clk,
	Rst_n,
	en,
	data,
	tx_done,
	tx
);
	input Clk;				//时钟
	input Rst_n;			//复位端
	input en;				//使能端
	input [7:0] data;		//数据输入端
	
	output reg tx_done;		//发送完成标志
	output reg tx;			//数据发送端

	reg [16:0] cnt;	//内部计数器

	//这个always块用于计数
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		cnt <= 0;
	else if(en)begin
		if(cnt == 399)	//每隔8us计满一次
			cnt <= 0;
		else
			cnt <= cnt + 1'b1; 
	end
	else
		cnt <= 0;

	//这个always块用于在特定计数值时将数据传出
   always@(posedge Clk or negedge Rst_n)
   if(!Rst_n)
		tx <= 0;
   else begin
       case(cnt)
           0:  tx <= data[0];//0us、第0位数据
           49: tx <= data[1];//1us、第1位数据
           99: tx <= data[2];//2us、第2位数据
           149:tx <= data[3];//3us、第3位数据
           199:tx <= data[4];//4us、第4位数据
           249:tx <= data[5];//5us、第5位数据
           299:tx <= data[6];//6us、第6位数据
           349:tx <= data[7];//7us、第7位数据
           default:tx <= tx;
       endcase
	end

	//这个always块用于给tx_done信号赋值
   always@(posedge Clk or negedge Rst_n)
   if(!Rst_n)
       tx_done <= 0;
   else if(cnt == 399)
       tx_done <= 1;
   else if(cnt == 0)
       tx_done <= 0;

endmodule

1.代码解读

整个模块仅仅用到了3个always语句块,可以说是比较简单的一个模块,但其所蕴含的设计思想是很重要的。

①内部计数器

该模块内部具有一个周期为8us的定时器,对于8位的数据,每1us输出一位数据。

②数据发送

在内部寄存器cnt计数到一定值时,把数据data的各位数据给到输出端tx。

③发送完毕标志

定时器每计满一个周期,将发送完毕标志位tx_done拉高,否则拉低。

2.仿真

编写testbench如下:

`timescale 1ns/1ps

module cnt_test3_tb;

    reg Clk;
    reg Rst_n;
    reg en;
    reg [7:0] data;
    wire tx;
    wire tx_done;

    test cnt_test3(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .en(en),
        .data(data),
        .tx_done(tx_done),
        .tx(tx)
    );

    initial Clk = 1;
    always #10 Clk = ~Clk;

    initial begin
        Rst_n = 0;
        data = 0;
        en = 0;
        #201;
        Rst_n = 1;
        #200;
        en = 1;//开始发送
        data = 8'h12;
        @(posedge tx_done);//等待信号tx_done的上升沿到来
        en = 0;
        #50000;

        en = 1;//开始发送
        data = 8'h56;
        @(posedge tx_done);//等待信号tx_done的上升沿到来
        en = 0;
        #50000;

        en = 1;//开始发送
        data = 8'haf;
        @(posedge tx_done);//等待信号tx_done的上升沿到来
        en = 0;
        #50000;

        $stop;
    end
endmodule

仿真波形如图在这里插入图片描述
在这个实验中,延时50us是由testbench控制的,即模块本身不能控制两次数据发送的时间间隔。如果想要由模块本身自动发送并延时,则需要用到下面介绍的步骤。

二、自动发送设计

1.设计思路

在原有的发送模块外部再加上一个控制模块,结合发送模块中的en和tx_done信号,可以实现上电自动发送。设计图如下:在这里插入图片描述
如图所示,数据发送模块的使能信号和数据输入均由控制模块给出,而发送完毕标志信号返回给控制模块,体现了一种“控制、反馈”的思想。

2.控制模块

在进行控制模块编写之前我们要具备一定的数字电路知识储备,最重要的是关于状态机的编写。根据两个模块之间的联系,我们可以画出控制部分的状态转换图。

①状态转换图

在这里插入图片描述
图中有下划线的式子表示转换条件,无下划线的表示信号转变。控制模块在三个状态之间跳变,我们人为规定为S0:等待状态S1:发送状态S2:发送完毕状态。控制部分代码给出如下:

module ctrl(
	Clk,
    Rst_n,
    En,
    Data,
	Tx_Done
);

	input Clk,Rst_n;
	output reg En;
	output [7:0] Data;
	
	input Tx_Done;
	
	reg [1:0] state;//用一个寄存器来表示状态
	
	localparam S0 = 2'd0;
	localparam S1 = 2'd1;
	localparam S2 = 2'd2;//定义三个状态常数,推荐使用格雷码
	
	reg cnt_en;
	reg [16:0] cnt;
	reg dly_done;
	
	assign Data = 8'ha5;

	//这个always块用于控制状态机从S0->S1->S2的切换
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin
		state <= S0;
		En <= 1'd0;
		cnt_en <= 0;
	end
	else begin
		case(state)
			S0://等待状态
				begin
					En <= 1'd1;
					state <= S1;
				end
			S1://发送状态
				begin
					if(Tx_Done)begin
						En <= 0;
						cnt_en <= 1;
						state <= S2;
					end
					else begin
						En <= 1;
						cnt_en <= 0;
						state <= S1;
					end
				end
			S2://发送完毕等待状态
				begin
					if(dly_done)begin
						state <= S0;
						cnt_en <= 0;
					end
					else begin
						state <= S2;
						cnt_en <= 1;
					end
				end
			default:begin
				state <= S0;
				En <= 1'd0;
				cnt_en <= 0;
			end
      endcase
	end
	
	
    //这个always块用于控制数据发送完毕后的50us延时
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin
		cnt <= 0;
		dly_done <= 0;
	end
	else if(cnt_en)begin
		if(cnt == 2497)begin
			cnt <= 0;
			dly_done <= 1;
		end
		else begin
			cnt <= cnt + 1'b1;
			dly_done <= 0;
		end
	end
	else begin
		cnt <= 0;
		dly_done <= 0;
	end
	
endmodule

控制模块比较复杂,需要根据自己画出的状态转换图来编写,感兴趣可以自行学习相关知识。

3.顶层模块

将两个模块设计好后,需要一个顶层模块来将它们连接起来,顶层模块写起来比较简单,代码如下:

module data_send_top(
    Clk,
    Rst_n,
    Tx
);
    input Clk;
    input Rst_n;
    output Tx;

    wire En;
    wire [7:0] data;
    wire tx_done;

    data_send data_send(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .en(En),
        .data(data),
        .tx_done(tx_done),
        .tx(Tx)
    );

    ctrl ctrl(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .En(En),
        .Data(data),
        .Tx_Done(tx_done)
);

endmodule

4.仿真

根据顶层模块编写测试激励文件如下:

`timescale 1ns/1ps

module data_send_tb;

    reg Clk;
    reg Rst_n;
    wire tx;

    data_send_top data_send_top(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.Tx(tx)
	);
	
    initial Clk = 1;
    always #10 Clk = ~Clk;

    initial begin
        Rst_n = 0;
        #200;
        Rst_n = 1;
        #255000;

        $stop;
    end
endmodule

用Modelsim进行仿真,得到波形如下:在这里插入图片描述
满足设计需求。

三、小结

今天的学习主要是借序列发送逻辑这一任务提高了对状态机书写的熟练程度。后面的学习中会涉及到越来越复杂的设计,掌握状态机的书写是十分有必要的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值