手把手教你写UART(verilog)

最近工作用uart用的比较多,为了让自己更好的掌握这个协议,写了这篇文章,解读了uart程序的编写过程(程序参考了米联客的教程)。

最基础的概念

        UART是用来让两个设备之间传输数据的协议,毕竟我不能直接给你一串01序列,你肯定不知道我表达的意思是啥。通信涉及到这么几个基本的问题:什么时候开始发,什么时候结束?在接收数据的过程中,接收方以什么速率接收?

        不同的协议有不同的解决方式,比如I2C,和SPI,他们会利用一个时钟线来解决这些问题,用数据线确定具体的传输数据,在UART中没有时钟线,那只能靠数据线来解决这些问题了,也就是通过下面这些东西:

        波特率:由于UART没有时钟线,那么就通信双方必须规定传输的速度是多少,比如每秒发10个符号,那么波特率就是10。

        起始位:UART定义当数据总线由高电平变低电平就是开始了,有人可能会疑惑,假如传数据的时候先发了一个1,下一个发0,不会被误判为起始信号?不会的,谜底就在谜面上,因为这是传数据的时候,要传完才会重新根据(1-0)来判断起始位。

        数据位:一个数据占用一个波特率时钟,一般是一次发8个数据位。

        停止位:因为起始时是需要通过高电平拉低来判断的,所以很自然传输完一个字节的数据,就得把数据线拉高对吧。所以一般数据传输结束后会保持1/2个高电平,或者没有新的数据了一直高也行。

        时序图:

发送端代码解析

        开局一根发信号数据线,首先我们试着写一个周期的数据发送。

   reg [7:0]data_send;  //要发送的数据

   reg uart_out;            //开局的线

   reg [3:0]count=4'b0;

   always @(posedge clk)begin

       if(count==0)uart_out<=1;                       //最开始是高电平
 
       else if(count==1)uart_out<=0;                  //拉低信号就是起始位

       else if(count<10)uart_out<=data_send[count-2]; //依次发送8个数据 

       else if(count==10) uart_out<=1;                //最后拉高作为停止位

       count<=count+1;

   end

        这部分程序能按UART要求的时序把数据发出去。但里面有很多问题。

        首先,它是按系统时钟的速率发的,UART要求的速率是一些固定的速率,比如9600,115200等。(当然如果你自己把接收端也写了,两边统一一下那就随便你定速率了)所以我们首先得把时钟信号变为相应波特率的时钟,那么就需要一个分频的系数:

        BAUD_DIV=时钟频率/波特率。程序如下:

        Interger BAUD_DIV=10416; //这是在100M的时钟下,设置9600波特率的分频系数。

        wire bps_en;//设置一个使能信号,当计数器到达分频系数就使能。

        reg[13:0] baud_count;//使能计数器

        always @(posedge clk)begin

                if(baud_count<BAUD_DIV)baud_count<=baud_count+1;

                else  baud_count<=0;

        end

         assign       bps_en= (baud_count==BAUD_DIV);//计数器达到分频系数,这个信号放到发送信号程序的判断条件里面,就可以按这个波特率发信号了。

       以上就是最基本的发数据的函数了,按照一定的波特率,一定的发数据的顺序(起始--数据位--终止位)。 除此之外,根据实际的应用,可能要需要加一些别的信号线。比如程序一开始就运行直接开始输出数据了,是不是加一个开启信号,比如是否要记录发了多少bit数据,发了多少组数据了,发送状态是正忙还是空闲等等。

        这里的发送状态是否正忙这个信号线还是挺重要的,当你发送多个周期的数据的时候,需要通过这个信号来切换,当它正忙转为不忙的时候,你就知道:奥,一个8bit的数据传输完了,那我输入的数据可以改变了。

        这段代码可以这样写:

wire uart_busy;
assign uart_busy==(count>0 &count<10)

        这个写的肯定是有问题的,但只是表达一下这个意思,可以通过传输过程的计数器来对这个信号线赋值。

        下面是一个比较完整的发送部分程序,除了上面提到的那几个信号线,还有个数据发送请求,也就是告诉它什么时候开始传数据,看完上面的部分应该会比较好理解了。

`timescale 1ns / 1ns//仿真时间刻度/精度
module uiuart_tx#
(
parameter integer BAUD_DIV = 10416 //设置采样系数 (时钟/采样率-1)
)
(
input clk_i, //系统时钟输入
input uart_rstn_i, //系统复位输入
input uart_wreq_i, //发送数据请求
input [7:0] uart_wdata_i, //发送数据
output uart_wbusy_o, //发送状态忙,代表正在发送数据
output uart_tx_o //uart tx 发送总线
);
localparam UART_LEN = 4'd10; //设置 uart 发送的 bit 数量为 10,代表 1bit 起始位,8bits 数据,1bit 停止位
wire bps_en ; //发送使能
reg uart_wreq_r = 1'b0; //寄存一次 uart_wreq_i
reg bps_start_en = 1'b0; //波特率计数器启动使能,也是发送启动使能
reg [13:0] baud_div = 14'd0; //波特率计数器
reg [9 :0] uart_wdata_r = 10'h3ff; //寄存 uart_wreq_i
reg [3 :0] tx_cnt = 4'd0; //计数发送了多少 bits
assign uart_tx_o = uart_wdata_r[0]; //总线上的数据,始终是 uart_wdata_r[0]
assign uart_wbusy_o = bps_start_en; //总线忙标志,即是 bps_start_en 为有效,即当总线忙于发送,总线忙
// 发送使能
assign bps_en = (baud_div == BAUD_DIV); //产生一次发送使能信号,条件是 baud_div == BAUD_DIV,波特率计数
达成
//波特率计数器
always@(posedge clk_i )begin
if((uart_rstn_i== 1'b0) || (uart_wreq_i==1'b1&uart_wreq_r==1'b0))begin
baud_div <= 14'd0;
end
else begin
if(bps_start_en && baud_div < BAUD_DIV) //bps_start_en 的信号拉高,表示开始发送
baud_div <= baud_div + 1'b1; //且 baud_div < BAUD_DIV 波特率计算,未达到波特率
baud_div+1
else
baud_div <= 14'd0; //达到清零
end
end
always@(posedge clk_i)begin
uart_wreq_r <= uart_wreq_i; //寄存一次 uart_wreq_i 信号
end
//当 uart_wreq_i 从低电平变为高电平,启动发送
always@(posedge clk_i)begin
if(uart_rstn_i == 1'b0)
bps_start_en <= 1'b0; //复位,计数清零
else if(uart_wreq_i==1'b1&uart_wreq_r==1'b0) //uart_wreq_i 上升沿激活
bps_start_en <= 1'b1; //激活后将 bps_start_en 拉高,传输开始
else if(tx_cnt == UART_LEN) //tx_cnt 用于计数当前发送的 bits 数量,当达到预定值 UART_LEN
bps_start_en <= 1'b0; //将 bps_start_en 拉低,传输结束
else
bps_start_en <= bps_start_en;
end
//发送 bits 计数器
always@(posedge clk_i)begin
if(((uart_rstn_i== 1'b0) || (uart_wreq_i==1'b1&uart_wreq_r==1'b0))||(tx_cnt == 10))//当复位、启动
发送、发送完成,重置 tx_cnt
tx_cnt <=4'd0;
else if(bps_en && (tx_cnt < UART_LEN)) //tx_cnt 计数器,每发送一个 bit 加 1
tx_cnt <= tx_cnt + 1'b1;
end
//uart 发送并串移位控制器
always@(posedge clk_i)begin
if((uart_wreq_i==1'b1&uart_wreq_r==1'b0)) //当发送请求有效,寄存需要发送的数据到 uart_wdata_r
uart_wdata_r <= {1'b1,uart_wdata_i[7:0],1'b0};//寄存需要发送的数据,包括 1bit 起始位,8bits 数据,
1bit 停止位
else if(bps_en && (tx_cnt < (UART_LEN - 1'b1))) //shift 9 bits
uart_wdata_r <= {uart_wdata_r[0],uart_wdata_r[9:1]}; //并串转换,将并行数据依次传输
else
uart_wdata_r <= uart_wdata_r;
end
endmodule

    仿真验证  

        这段代码用计数器做了一个复位,用状态机控制输入数据的变化,可以根据自己的需要更改状态3的程序。

module top(

    );
    
localparam      SYS_TIME   =  'd10;//时钟周期,以ns为单位
reg 			 sysclk_i;			//系统时钟

initial begin	  //仿真初始化
sysclk_i =0;
 #2000000 $finish;			
end
 
always #(SYS_TIME/2) sysclk_i = ~sysclk_i;	  //产生主时钟



wire uart_tx_o  ;  // UART 串口发送总线
wire     uart_rstn_i; //内部同步复位
wire     uart_wbusy;  //UART发送驱动器正忙
reg      t1s_dly_en;  //1S延迟
reg[1:0] S_UART_TX;   //UART 发送状态机
reg[1:0] tx_index;    //发送index计数器
reg      uart_wreq;   //UART发送请求  
reg[7:0] uart_wdata;  //UART发送数据寄存器 
reg[7:0] uart_tx_buf[0:2]; //发送缓存
reg [15:0]rst_cnt = 16'd0; //复位计数器


assign uart_rstn_i = rst_cnt[15]; //复位

//上电通过计数器计数,实现复位
always @(posedge sysclk_i)begin
    rst_cnt <= (rst_cnt[15] == 1'b0) ? (rst_cnt + 1'b1) : rst_cnt ;
end


//数据发送状态机
always @(posedge sysclk_i)begin
    if(uart_rstn_i==1'b0)begin //初始化uart_tx_buf,为hello fpga等字符共计12 BYTES,以及其他寄存器
        uart_tx_buf[0]  <=8'b10000001;
        uart_tx_buf[1]  <=8'b10101010;
        uart_tx_buf[2]  <=8'd5;

        uart_wdata      <= 8'd0;
        uart_wreq       <= 1'b0;
        S_UART_TX       <= 2'd0;
        tx_index        <= 2'd0;
    end
    else begin
        case(S_UART_TX)
        0:begin
            if(!uart_wbusy)begin//如果UART发送驱动器不忙
                uart_wdata <= uart_tx_buf[tx_index];//准备发送数据,发送tx_index所指向的数据
                uart_wreq <= 1'b1; //设置uart_wreq为高电平,请求发送数据
            end
            else begin //当总线忙
                uart_wreq <= 1'b0; //重置uart_wreq
                S_UART_TX <= 2'd1; //进入下一状态
            end
        end
      1:begin//该状态等待总线空闲
            S_UART_TX <= (uart_wbusy == 1'b0) ? 2'd2: S_UART_TX;
        end 
        2:begin//更新tx_index计数器
            if(tx_index < 3)begin //每一帧发送12个字节
                tx_index <= tx_index + 1'b1; //tx_index 加计数
                S_UART_TX  <= 2'd0; //进入下一状态
            end
            else begin //如果tx_index==11 代表所有数据发送完毕
                tx_index   <= 2'd0; //重置tx_index
                S_UART_TX  <= 2'd3; //下一状态
            end 
        end
        3: S_UART_TX <= S_UART_TX;   
              
        endcase
    end   
    
end

//例化UART 发送驱动器模块
uiuart_tx#
(
.BAUD_DIV(100000000/115200-1)  //波特率计算    BAUD_DIV = 系统时钟/波特率-1
)
uart_tx_u 
(
.clk_i(sysclk_i),//系统时钟输入
.uart_rstn_i(uart_rstn_i), //系统复位输入
.uart_wreq_i(uart_wreq), //UART发送(写)数据请求
.uart_wdata_i(uart_wdata), //UART发送(写)数据
.uart_wbusy_o(uart_wbusy),//UART发送驱动器忙
.uart_tx_o(uart_tx_o) //UART 发送串行总线
);
endmodule

仿真结果

可以看到,那根开局的线就是按我们想要的时序发送数据的。

接收端

        说完发送端我们再来看看接收端,它也是靠着一根数据线来接收数据的,时序和发送端一致,速率也一致。所以这里就不一点点的讲解了

module uart_rx#
(
 parameter integer  BAUD_DIV     = 10416  //波特率分频参数,BAUD_DIV=系统时钟频率/波特率-1 比如100M系统时钟,波特率115200 BAUD_DIV= 100_000_000/115200-1
)
(
input clk_i, //系统时钟输入
input uart_rx_rstn_i,//系统复位输入
input uart_rx_i,//uart rx 总线信号输入
output [7:0] uart_rdata_o,//uart rx接收到的数据输出
output uart_rvalid_o// uart rx 接收数据有效信号,当为1的时候uart_rdata_o数据有效
);

localparam  BAUD_DIV_SAMP = (BAUD_DIV/8)-1;                            //多次采样,按照波特率系数的八分之一进行采样

wire bps_en       ; //波特率使能信号
wire samp_en      ; //采样使能信号
wire bit_cap_done ; //uart rx总线信号采样有效数据完成
wire uart_rx_done ; //uart 1byte 接收完成
wire bit_data     ; //接收的1bit数据
wire uart_rx_int  ; //uart_rx_int的启动信号检测,当变为低电平,代表可能存在起始位(UART 起始位为低电平)

reg [13:0]  baud_div = 14'd0;//波特率分频计数器
reg [13:0]  samp_cnt = 14'd0;//采样计数器
reg [4 :0]  uart_rx_i_r = 5'd0;//异步采集多次寄存
reg [3 :0]  bit_cnt=4'd0;//bit 计数器
reg [3 :0]  cap_cnt=4'd0;//cap 计数器
reg [4 :0]  rx_bit_tmp = 5'd0;//rx_bit_tmp用于多次采样,通过计算采样到高电平次数和低电平次数,判断本次采样是高电平还是低电平
reg [7 :0]  rx_data = 8'd0;//数据接收寄存器

reg bps_start_en_r = 1'b0;
reg bit_cap_done_r = 1'b0;
reg bps_start_en,start_check_done,start_check_failed;

assign bps_en       =   (baud_div == (BAUD_DIV - 1'b1));                     //完成一次波特率传输信号
assign samp_en      =   (samp_cnt == (BAUD_DIV_SAMP - 1'b1 ));               //完成一次波特率采样信号
assign bit_cap_done =   (cap_cnt  == 3'd7);//采样计数
assign uart_rx_done =   (bit_cnt  == 9)&&(baud_div == BAUD_DIV >> 1);//当停止位开始,提前半停止位,发送uart_rx_done信号,以便提前准备进入下一个数据的接收

assign bit_data     =   (rx_bit_tmp < 5'd15) ? 0 : 1; //rx_bit_tmp用于多次采样,通过计算采样到高电平次数和低电平次数,判断本次采样是高电平还是低电平,提高抗干扰能力
//连续5次信号拉低,判断开始传输
assign uart_rx_int  =   uart_rx_i_r[4] | uart_rx_i_r[3] | uart_rx_i_r[2] | uart_rx_i_r[1] | uart_rx_i_r[0];
assign uart_rdata_o   =   rx_data;
assign uart_rvalid_o  =   uart_rx_done;   

//波特率计数器
always@(posedge clk_i)begin
    if(bps_start_en && baud_div < BAUD_DIV)                 //baud_div计数,目标值BAUD_DIV 
        baud_div <= baud_div + 1'b1;
    else 
        baud_div <= 14'd0;
end

//8bit采样使能,8倍波特率采样,也就是这个计数器,用于产生8倍过采样
always@(posedge clk_i)begin
    if(bps_start_en && samp_cnt < BAUD_DIV_SAMP)             //bps_start_en高电平有效,开始对bit进行采样,samp_cnt以8倍于波特率速度对每个bit采样
        samp_cnt <= samp_cnt + 1'b1;                         //samp_cnt计数+1       
    else 
        samp_cnt <= 14'd0;                                   //samp_cnt计数清零
end

//uart rx bus asynchronous to Synchronous
always@(posedge clk_i)begin 
    uart_rx_i_r <= {uart_rx_i_r[3:0],uart_rx_i};             //uart_rx_i的数据存入uart_rx_i_r进行缓存
end

//uart接收启动检查
always@(posedge clk_i)begin
    if(uart_rx_rstn_i == 1'b0 || uart_rx_done || start_check_failed) //bps_start_en拉低的三种情况,复位、接收完成、校验失败
        bps_start_en    <= 1'b0;                                               //接收结束
    else if((uart_rx_int == 1'b0)&(bps_start_en==1'b0))//当判断到uart_rx_int == 1'b0,并且总线之前空闲(bps_start_en==1'b0,代表总线空闲)
        bps_start_en    <= 1'b1;//使能波特率计数器使能
end

//uart接收启动使能
always@(posedge clk_i)begin
        bps_start_en_r    <= bps_start_en;                              //bps_start_en信号打一拍,方便后续上升沿捕捉
end

always@(posedge clk_i)begin
    if(uart_rx_rstn_i == 1'b0 || start_check_failed)begin//当系统复位,或者start_check_failed,重置start_check_done和start_check_failed
        start_check_done    <= 1'b0;
        start_check_failed  <= 1'b0;
    end    
    else if(bps_start_en == 1'b1&&bps_start_en_r == 1'b0) begin//当检测到start信号,也重置start_check_done和start_check_failed
        start_check_done    <= 1'b0;
        start_check_failed  <= 1'b0;
    end
    else if((bit_cap_done&&bit_cap_done_r==1'b0)&&(start_check_done == 1'b0))begin//第一个波特率采样,用于判断是否一个有效的起始位,如果不是有效的,start_check_failed设置为1
        start_check_failed <= bit_data ? 1'b1 : 1'b0;
        start_check_done   <= 1'b1;//不管是否start_check_failed==1,都会设置start_check_done=1,但是start_check_failed==1,会下一个系统时钟重置start_check_done=0
    end     
end

//bits 计数器
always@(posedge clk_i)begin
    if(uart_rx_rstn_i == 1'b0 || uart_rx_done || bps_start_en == 1'b0)//复位、接收完成、或者总线空闲(bps_start_en == 1'b0),重置bit_cnt
        bit_cnt   <= 4'd0;                                                    
    else if(bps_en)//每一个bps_en有效,加1
        bit_cnt <= bit_cnt + 1'b1;  // bit_cnt计数器用于计算当前采样了第几个bit 
end

//8次过采样,提高抗干扰
always@(posedge clk_i)begin
    if(uart_rx_rstn_i == 1'b0 || bps_en == 1'b1 || bps_start_en == 1'b0) begin //当uart_rx_rstn_i=0或者bps_en=1或者bps_start_en==0,重置cap_cnt和rx_bit_tmp
        cap_cnt     <= 4'd0;
        rx_bit_tmp  <= 5'd15; 
    end
    else if(samp_en)begin//bit采样使能
        cap_cnt     <= cap_cnt + 1'b1;//cap_cnt用于记录了当前是第几次过采样,1个bit采样8次
        rx_bit_tmp  <= uart_rx_i_r[4] ? rx_bit_tmp + 1'b1 :  rx_bit_tmp - 1'b1;   //多次采样,如果是高电平+1,如果是低电平-1,最终看本次bit采样结束rx_bit_tmp如果小于15代表是低电平
    end                                                                                   
end

//寄存一次bit_cap_done,用于产生高电平触发脉冲下面用到
always@(posedge clk_i)
    bit_cap_done_r <= bit_cap_done;


always@(posedge clk_i)begin
    if(uart_rx_rstn_i == 1'b0 || bps_start_en == 1'b0)//当复位或者总线空闲,重置rx_data
        rx_data  <= 8'd0;  
    else if(start_check_done&&(bit_cap_done&&bit_cap_done_r==1'b0)&&bit_cnt < 9)//当start_check_done有效,并且bit_cnt<9,每次bit_cap_done有效,完成一次移位寄存
        rx_data  <= {bit_data,rx_data[7:1]};                                         //串并转换,将数据存入rx_data 中,共8位
end

endmodule

仿真结果

仿真代码如下

module uart_top_tb();
 
localparam		BPS 	     = 'd115200		;			        //波特率
localparam 		CLK_FRE    = 'd100_000_000	;	        //系统频率
localparam    CLK_TIME   = 'd1000_000_000/CLK_FRE; //计算系统时钟周期,以ns为单位
localparam		BIT_TIME   = 'd1000_000_000/BPS ;	   //计算出传输每个bit所需要的时间以ns为单位
localparam    NUM_BYTES  = 3;               //需要发送的BYTES

reg                    sysclk_p;			      //系统时钟
reg                    uart_rstn;           //系统复位
reg                    bsp_clk ;            //波特率时钟
reg                    uart_tx;             //uart 数据总线发送,接到UART接收模块的uart_rx
wire [7:0]             uart_rx_data_o;      //uart 接收到有效数据
wire                   uart_rvalid_o;       //uart 接收数据有效信号

reg [8*NUM_BYTES-1:0]  uart_send_data;      //需要发送的数据
reg [7:0]              uart_send_data_r;    //寄存每次需要发送的BYTE

integer i,j;

//例化模块
uiuart_rx uart_top_inst
(
.clk_i(sysclk_p),
.uart_rx_rstn_i(uart_rstn),
.uart_rx_i(uart_tx),
.uart_rdata_o(uart_rx_data_o),
.uart_rvalid_o(uart_rvalid_o)
);

//仿真初始化
initial begin	

//初始化REG寄存器
sysclk_p =0;
uart_rstn = 0;
bsp_clk  = 0;  
uart_tx  = 1;
i=0;
j=0;

uart_send_data   =0;
uart_send_data_r =0;

#1000;//延迟1000ns
uart_rstn =1; //复位完成
#1000;//延迟1000ns

uart_send_data[(0*8) +: 8] = 8'b1001_0101;//初始化需要发送的第1个BYTE
uart_send_data[(1*8) +: 8] = 8'b0000_0101;//初始化需要发送的第2个BYTE
uart_send_data[(2*8) +: 8] = 8'b1000_0100;//初始化需要发送的第3个BYTE

//uart tx 发送数据
  for(i=0; i<NUM_BYTES;i=i+1)
  begin

      uart_send_data_r = uart_send_data[(i*8) +: 8];//寄存需要发送的数据到寄存器
      $display("uart_send_data : 0x%h",uart_send_data_r);//打印准备发送的数据

      @(posedge bsp_clk);  //发送起始位1bit
      uart_tx = 1'b0;

      for(j=0;j<8;j=j+1)begin//发送数据8bits
      @(posedge bsp_clk);  //发送
      uart_tx = uart_send_data_r[j];
      end

       @(posedge bsp_clk);//发送停止位1bit
       uart_tx = 1'b1;  

  end
       @(posedge bsp_clk); 
       #200 $finish;			
end
 
always #(CLK_TIME/2) sysclk_p = ~sysclk_p;	  //产生主时钟
always #(BIT_TIME/2) bsp_clk  = ~bsp_clk;		//产生波特率时钟
 


endmodule

写在最后

       希望对大家有所帮助,欢迎留言讨论,一起进步~

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
UART(通用异步收发传输器)是一种常用的串行通信协议,它使用了异步传输方式,可以用于电脑与其他设备之间的数据传输。在实现UART硬件时,可以使用Verilog进行设计和描述。下面是一个简单的UART通信模块的Verilog代码示例: ```verilog module uart ( input clk, input rst, input [7:0] data_in, input tx_en, output reg [7:0] data_out, output reg rx_dv, output reg rx_err ); // 定义状态机状态 parameter IDLE = 2'b00; parameter START = 2'b01; parameter DATA = 2'b10; parameter STOP = 2'b11; // 定义状态机输入和输出 reg [1:0] state_reg, state_next; reg [7:0] data_in_reg; reg tx_en_reg; reg [3:0] count_reg; reg [7:0] data_out_reg; reg rx_dv_reg, rx_err_reg; // 定义状态机逻辑 always @(posedge clk, posedge rst) begin if (rst) begin state_reg <= IDLE; data_in_reg <= 8'd0; tx_en_reg <= 1'b0; count_reg <= 4'd0; data_out_reg <= 8'd0; rx_dv_reg <= 1'b0; rx_err_reg <= 1'b0; end else begin state_reg <= state_next; data_in_reg <= data_in; tx_en_reg <= tx_en; count_reg <= (count_reg == 4'd15) ? 4'd0 : count_reg + 1; data_out_reg <= data_out; rx_dv_reg <= rx_dv; rx_err_reg <= rx_err; end end // 定义状态机状态转移 always @(*) begin state_next = state_reg; case (state_reg) IDLE: begin if (!tx_en_reg) begin state_next = IDLE; end else begin state_next = START; count_reg <= 4'd0; end end START: begin if (count_reg == 4'd8) begin state_next = DATA; count_reg <= 4'd0; end else begin state_next = START; count_reg <= count_reg + 1; end end DATA: begin if (count_reg == 4'd8) begin state_next = STOP; count_reg <= 4'd0; end else begin state_next = DATA; count_reg <= count_reg + 1; end end STOP: begin state_next = IDLE; end endcase end // 定义rx_dv输出 always @(posedge clk) begin if (state_reg == STOP) begin rx_dv <= 1'b1; end else begin rx_dv <= 1'b0; end end // 定义rx_err输出 always @(posedge clk) begin if (state_reg == STOP && data_in_reg != data_out_reg) begin rx_err <= 1'b1; end else begin rx_err <= 1'b0; end end // 定义data_out输出 always @(posedge clk) begin if (state_reg == DATA) begin data_out <= data_in_reg; end else if (state_reg == STOP) begin data_out <= 8'd0; end else begin data_out <= data_out_reg; end end endmodule ``` 上述代码示例中定义了一个UART通信模块,其中包括了发送和接收数据的状态机逻辑。使用Verilog可以实现这个通信模块的设计和描述,从而实现UART串行通信协议的硬件实现。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值