Verilog学习笔记

————来源北京交大李金城老师《Verilog零基础入门》状态机代码设计与仿真讲座整理

第10讲 状态机代码设计与仿真(串口指令处理器)_哔哩哔哩_bilibili

一、串口指令处理器功能框图

 电路图:

 二、代码实现

1、串口接收模块(UART_RXer)


/****串口协议简介***************
1、串口发送端口空闲时为高电平;
2、发送端口拉低表示数据传送即将开始;
3、字节数据低位先发;
4、字节发送后拉高,表示字节传送结束;
5、字节位宽可以不为8;
6、常用波特率有4800、9600、115200等。
*********************************/

//状态规划:
//状态1: 空闲识别
//状态2: 等起始位
//状态3: 收b0
//状态4: 收b1
//状态5: 收b2
//状态6: 收b3
//状态7: 收b4
//状态8: 收b5
//状态9: 收b6
//状态10:收b7
//......

//串口数据接收(波特率为4800,系统时钟频率24MHz)
module UART_RXer(
							clk,
							rst,
							RX,
							data_out,
							en_data_out
							);

input					clk;         //时钟
input					rst;         //复位
input					RX;          //串口输入
output[7:0]		        data_out;	 //接收字节输出(8位)
output				    en_data_out; //输出使能,每接收完成一个字节,产生一个同步脉冲

reg[7:0]			data_out;//字节输出
reg[7:0]			state;//主状态机

reg[12:0]			con;//用于计录比特宽度,有效值不超过7500
reg[3:0]			con_bits;//用于记录比特数
reg					RX_delay;//RX的延时
reg					en_data_out;//输出使能

always @(posedge clk or negedge rst)
if(~rst)begin
		state<=0;con<=0;con_bits<=0;RX_delay<=0;
		data_out<=0;en_data_out<=0;
end
else begin
		RX_delay<=RX; //这里赋值得到:RX_delay为RX延时一个时钟周期的副本
		case (state)
			0://等空闲
			begin
				//5000:传一个比特位的时钟周期数(T/bit),
				//5000=时钟24000000/波特率4800
				if(con==5000-1) begin
					con<=0;
				end
				else begin
					con<=con+1;
				end
				
				if(con==0) begin
					if(RX) begin //确定是空闲状态
						con_bits<=con_bits+1;					
					end
					else begin
						con_bits<=0;							
					end
				end
				
				if(con_bits==12) begin
					state<=1; //能连续12位高电平,确认为空闲状态
				end
				
			end
			1://等起始位
			begin
				//如果RX的反向和其延时一个时钟周期后相与后还是高电平,
				//则确认到了起始位
				en_data_out<=0;//复位使能
				if(~RX&RX_delay) begin //找到下降沿
					state<=2;
				end
			end
			2://收最低位b0
			begin
				if(con==7500-1) begin
					//1.5*(T/bit),即1.5*5000=7500
					con<=0;
					data_out[0]<=RX;
					state<=3;
				end
				else begin
					con<=con+1;
				end
			end
			3://收b1
			begin
				if(con==5000-1) begin
					//1(T/bit),即1*5000=5000,下同
					con<=0;
					data_out[1]<=RX;
					state<=4;
				end
				else begin
					con<=con+1;
				end
			end
			4://收b2
			begin
				if(con==5000-1) begin
					con<=0;
					data_out[2]<=RX;
					state<=5;
				end
				else begin
					con<=con+1;
				end
			end
			5://收b3
			begin
				if(con==5000-1) begin
					con<=0;
					data_out[3]<=RX;
					state<=6;
				end
				else begin
					con<=con+1;
				end
			end
			6://收b4
			begin
				if(con==5000-1) begin
					con<=0;
					data_out[4]<=RX;
					state<=7;
				end
				else begin
					con<=con+1;
				end
			end
			7://收b5
			begin
				if(con==5000-1) begin
					con<=0;
					data_out[5]<=RX;
					state<=8;
				end
				else begin
					con<=con+1;
				end	
			end
			8://收b6
			begin
				if(con==5000-1) begin
					con<=0;
					data_out[6]<=RX;
					state<=9;
				end
				else begin
					con<=con+1;
				end
			end
			9://收b7
			begin
				if(con==5000-1) begin
					con<=0;
					data_out[7]<=RX;
					state<=10;
				end
				else begin
					con<=con+1;
				end
			end
			10://产生使能脉冲
			begin
				en_data_out<=1;//产生使能脉冲尖
				state<=1;				
			end
			
			default://意外状态
			begin
				state<=0;con<=0;con_bits<=0;en_data_out<=0;
			end
		endcase
end

endmodule

//仿真模块---testbench of UART_RXer ----
`timescale 1ns/10ps

module UART_RXer_tb;
	reg					clk,rst;
	wire				RX;
	wire[7:0]		data_out;
	wire				en_data_out;

	reg[25:0]		RX_send;//里面装有串口字节发送数据

	assign			RX=RX_send[0];//连接RX

	reg[12:0]		con;

	UART_RXer UART_RXer( //同名例化
								clk,
								rst,
								RX,
								data_out,
								en_data_out
								);
										
	initial begin
						clk<=0;rst<=0;con<=0;
						RX_send<={1'b1,8'haa,1'b0,16'hffff};//位拼接构造一个1+8+1+16=26位串口数据
				#17			rst<=1;
				#4000000	$stop;
	end									

	always #5 clk<=~clk;

	always @(posedge clk) begin
		if (con==5000-1) begin
			con<=0;
		end
		else begin
			con<=con+1;
		end

		if(con==0) begin
			//让RX_send向右循环移位,模拟16+1+8+1串口数据
			RX_send[24:0]<=RX_send[25:1];
			RX_send[25]<=RX_send[0];
		end
	end

endmodule

2、串口发送模块(UART_TXer) 

//8位串口数据发送,先低位后高位(波特率为4800,系统时钟频率24MHz)
//TX为串口输出端口
//rdy为空闲标志,字节发送时rdy为高
//data_in为准备发送的字节
//en_data_in为字节发送使能端口,高使能
//
//状态规划:
//状态1: 等待发送使能,如果en_data_in为1,填充发送寄存器。
//状态2: 发送寄存器右移,逐个位发送,完成后跳回等待发送使能
//        (位拼接发送寄存器{1,data_in,0},右移发送寄存器,最低位为TX)
//
module  UART_TXer(
	                 clk,
			         rst,
	                 data_in,
	                 en_data_in,
	                 TX,
	                 rdy
                  );
                  
	input            clk;
	input            rst;
	input[7:0]       data_in;     		//准备发送的8位数据
	input            en_data_in;  		//发送使能
	output           TX;  				//输出
	output           rdy;         		//空闲标志 0表示空闲

	reg[1:0]         state;       		//主状态机寄存器
	reg[9:0]         send_buf;     		//发送寄存器,相当于机枪弹夹
	reg[12:0]        con;          		//用于计算波特周期;

	assign TX=send_buf[0];  //连接TX,相当于弹夹当前子弹,(组合逻辑,TX一直指向send_buf[0])

	reg[9:0]         send_flag; 		//判断右移结束标记,用于跟踪发送进度;
	reg              rdy;

	always@(posedge clk or negedge rst )
	if (~rst )begin
		 state<=0;send_buf<=1;con<=0;rdy<=0;
		 send_flag<=10'b10_0000_0000; //最高位为1,其他位为0
	end
  else begin
      case(state)
      0://等待使能信号
	      begin 
		        if (en_data_in) begin
			        send_buf={1'b1,data_in,1'b0};//构造发送缓冲区(弹夹)
			        send_flag<= 10'b10_0000_0000;
			        rdy<=1;
			        state<=1;
		        end
	      end
      
      1://串口发送,寄存器右移
	      begin 
	          if (con==5000-1) begin 
	             con<=0;
	          end
	          else begin 
	             con<=con+1;
	          end
	          
	          //0-4999期间TX保持低位0,相当于发送一个起始位
	          
	          if (con==5000-1) begin 
	             send_buf[8:0]<=send_buf[9:1];  //右移,子弹上膛
	             send_flag[8:0]<=send_flag[9:1];//同步send_buf右移
	          end
	          
	          if (send_flag[0]) begin 
	          	 //右移到剩最后一位,数据发送完毕,此时send_buf[0]=1,即TX=1,置空闲状态
	             rdy<=0;
	             state<=0;
	          end
	      end

      endcase
  end

endmodule

//仿真模块-----testbench  of UART_TXer-----
`timescale 1ns/10ps

module UART_TXer_tb();
	reg            clk,rst;
	reg[7:0]       data_in;
	reg            en_data_in;

	wire            TX;
	wire            rdy;

	UART_TXer  UART_TXer(
	                      .clk(clk),
	                      .rst(rst),
	                      .data_in(data_in),
	                      .en_data_in(en_data_in),
	                      .TX(TX),
	                      .rdy(rdy)
	                 	);
	                  
	initial begin
	        clk<=0;rst<=0;en_data_in<=0;
	        data_in<=8'h5a; //0101 1010
	    #17 rst<=1;
	    #30 en_data_in<=1;
	    #10 en_data_in<=0;
	    #2000000 $stop;

	end
	always #5 clk<=~clk;

endmodule

3、指令处理器模块(cmd_pro)

//串口指令处理器///
//cmd_pro指令集格式:
//每次连续接收三个字节,第一字节为指令CMD,第二字节为操作数A,第三字节为操作数B
//指令集如下:
//CMD					操作
//8'h0a					A+B
//8'h0b					A-B
//8'h0c					A&B
//8'h0d					A|B
//
//状态规划:
//状态0: 接收指令,遇见en_din_pro为1,则接收数
//状态1: 接收操作数A
//状态2: 接收操作数B
//状态3: 指令译码,根据指令计算
//状态4: 输出使能,返回指令执行结果
//
//串口指令处理模块
module  cmd_pro(
                 clk,
		         rst,
                 din_pro,
                 en_din_pro,
                 dout_pro,
                 en_dout_pro,
                 rdy
                  );

input            clk;
input            rst;

input[7:0]       din_pro;    					//指令和数据输入端口
input            en_din_pro; 					//输入使能
output[7:0]      dout_pro;   					//指令执行结果
output           en_dout_pro;					//指令输出使能
input            rdy;        					//串口发送空闲模块标志,0表示空闲

reg              en_dout_pro;

reg[3:0]         state;     					//主状态机寄存器
reg[7:0]         cmd_reg,A_reg,B_reg;	        //存放指令,操作数A和B
reg[7:0]         dout_pro;

parameter        add_ab=8'h0a;				//加操作
parameter        sub_ab=8'h0b;				//减操作
parameter        and_ab=8'h0c;				//与操作
parameter        or_ab =8'h0d;				//或操作

always@(posedge clk or negedge rst )
   if(~rst )begin
	   state<=0;
	   cmd_reg<=0;A_reg<=0;B_reg<=0;dout_pro<=0;
	   en_dout_pro<=0;
   end

   else begin 
      case(state)
      0://等收令
        begin
	        en_dout_pro<=0;
	        if(en_din_pro)begin
		        cmd_reg<=din_pro; //收指令
		        state<=1; 
	        end
        end
      1://收A
        begin
          if(en_din_pro)begin
            A_reg<=din_pro; //收操作数A
            state<=2;
          end
        end 
      2://收B
        begin
          if(en_din_pro)begin
            B_reg<=din_pro; //收操作数B
            state<=3;
          end
        end 
      3://指令译码和执行
        begin
          case(cmd_reg)
	          add_ab:begin dout_pro<=A_reg+B_reg;  end
	          sub_ab:begin dout_pro<=A_reg-B_reg;  end
	          and_ab:begin dout_pro<=A_reg&B_reg;  end
	          or_ab: begin dout_pro<=A_reg|B_reg;  end
          endcase
          state<=4;          
        end
      4://发送指令,执行结果;
	      begin 
	        if(~rdy)begin  //如果发送端是空闲状态
	          en_dout_pro<=1;
	          state<=0;
	        end
	      end
      default://其他状态
		    begin
			    state <=0;
			    en_dout_pro<=0;
		    end
      endcase
   end

endmodule

4、串口收发顶层模块(UART_top)

//串口指令处理器
//中间连接信号以cmd_pro模块为参考
//
module UART_top(
                 clk,
                 rst,
                 RX,
                 TX
				    );
 
input            clk;				//时钟
input            rst;				//复位
input            RX;				//串口输入
output           TX;				//串口输出

wire[7:0]        din_pro;			//指令和数据输入端口
wire             en_din_pro;		//输入使能
wire[7:0]        dout_pro;			//指令执行结果
wire             en_dout_pro;		//指令输出使能
wire             rdy;				//空闲标志 0表示空闲

UART_RXer  UART_RXer(
                .clk(clk),
                .rst(rst),
                .RX(RX),
                .data_out(din_pro),
                .en_data_out(en_din_pro)
                ); 

UART_TXer  UART_TXer(
                .clk(clk),
                .rst(rst),
                .data_in(dout_pro),
                .en_data_in(en_dout_pro),
                .TX(TX),
                .rdy(rdy)
                );

cmd_pro  cmd_pro(
                .clk(clk),
                .rst(rst),          
                .din_pro(din_pro),
                .en_din_pro(en_din_pro),
                .dout_pro(dout_pro),
                .en_dout_pro(en_dout_pro),
                .rdy(rdy)
                );

endmodule


//仿真模块---testbench of UART_top---
//模拟接收数据RX包含3个字节数据,其中,第一个为操作码,另两个为操作数
//观察发送输出结果是否正确
//
`timescale 1ns/10ps

module UART_top_tb;
	reg							 clk,rst;
	wire						 RX;
	wire						 TX;
	
	reg[45:0]		RX_send;		//里面装有串口字节发送数据
	assign			RX=RX_send[0];	//连接RX

	reg[12:0]		con;
	
	UART_top UART_top(
		                 clk,
		                 rst,
		                 RX,
		                 TX
						);
								 
	initial begin
					clk<=0;rst<=0;
					//位拼接构造一个3*(1+8+1)+16=46位串口数据
					//例如:RX接收的是'加操作'指令h0a,操作数为h50、h22,
					//结果:TX应是h72='b0111_0010
					RX_send<={1'b1,8'h22,1'b0,1'b1,8'h50,1'b0,1'b1,8'h0a,1'b0,16'hffff};
					con<=0;
		#17			rst<=1;
		#4000000	$stop;
	end

	always #5 clk<=~clk;

	always @(posedge clk) begin
		if (con==5000-1) begin
			con<=0;
		end
		else begin
			con<=con+1;
		end

		if(con==0) begin
			//让RX_send向右循环移位,模拟串口进来的数据
			RX_send[44:0]<=RX_send[45:1];
			RX_send[45]<=RX_send[0];
		end
	end

endmodule

三、ModelSim仿真(UART_top_tb)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值