串口控制器及驱动实现

UART控制器及驱动的实现

verilog新手一枚~本文讲述uart控制器及驱动的实现,并在zynq7020平台进行实验验证。文中附带verilog实现的uart控制器全部源码,附带linux驱动关键部分源码

开发环境

开发板ZYNQ PI extend R2 2022A版 (zynq7020)
开发主机Win11 and ubuntu20.04
IDE版本Xilinx Vitis IDE v2021.1.0 (64-bit) Vivado v2021.1 (64-bit)
uboot源码Xilinx官方
linux源码Xilinx官方

使用verilog实现UART控制器

UART原理

在这里插入图片描述

  • 8位数据,起始位低,结束位高
  • 在中间稳定位置进行数据采样
  • 检测到下降沿记作起始开始

发送模块实现

设计状态机如下:

  • counter0为波特率计数器
  • counter1为发送位数计数器
  • 检测到send_go上升沿,开始发送数据
  • 第一个always块中进行状态切换
  • 第二个always块中执行状态动作
module uart_send_byte(
    input wire rstn,                /* 复位 */
    input wire clk,                 /* 时钟 */
    input wire[19:0] baud_rate,     /* 波特率 */
    input wire send_go,             /* 开始发送 */
    input wire[7:0] byte,           /* 发送字节 */
    output reg tx,                  /* tx信号 */
    output reg tx_down              /* 发送完成 */
    );

parameter SYS_CLK = 100_000_000;
parameter MCNT1 = 8;

parameter STATE_IDLE	= 2'b00;    /* 空闲状态 */
parameter STATE_START	= 2'b01;    /* 发送起始位 */
parameter STATE_DATA	= 2'b10;    /* 发送数据 */
parameter STATE_STOP	= 2'b11;    /* 发送结束位 */

reg[31:0] counter0;                 /* 波特率计数 */
reg[3:0] counter1;                  /* 字节计数 */
reg[1:0] state;                     /* 状态 */

initial begin
    counter0 <= 0;
    counter1 <= 0;
    state <= STATE_IDLE;
    tx <= 1;
end

reg en_data0;
reg en_data1;
wire en_posedge;
/* 检测send_go信号 */
always @(posedge clk or negedge rstn) begin
    if (!rstn) begin
        en_data0 <= 0;
        en_data1 <= 0;
    end
    else begin
        en_data0 <= send_go;
        en_data1 <= en_data0;
    end
end

assign en_posedge = en_data0 & (~en_data1);

/* 波特率计数器 */
always @(posedge clk or negedge rstn) begin
    if (!rstn)
        counter0 <= 0;
    else if(counter0 == (SYS_CLK / baud_rate) - 1)
        counter0 <= 0;
    else if (state == STATE_IDLE)
        counter0 <= 0;
    else
        counter0 <= counter0 + 1;
end

/* 发送位计数器 */
always @(posedge clk or negedge rstn) begin
    if (!rstn)
        counter1 <= 0;
    else if (counter0 == (SYS_CLK / baud_rate) - 1) begin
        if(counter1 == MCNT1 - 1)
            counter1 <= 0;
        else if (state == STATE_DATA)
            counter1 <= counter1 + 1;
        else
            counter1 <= 0;
    end
    else
        counter1 <= counter1;
end

/* 切换状态 */
always @(posedge clk or negedge rstn) begin
    if (!rstn) begin
        state <= STATE_IDLE;
        tx_down <= 1;
    end
    else begin
        case (state)
        STATE_IDLE:
            if (en_posedge) begin
                tx_down <= 0;
                state <= STATE_START;
            end
        STATE_START: begin
            if (counter0 == (SYS_CLK / baud_rate) - 1)
                state <= STATE_DATA;
        end
        STATE_DATA:
            if (counter1 == MCNT1 - 1 && counter0== (SYS_CLK / baud_rate) - 1)
                state <= STATE_STOP;
        STATE_STOP:
            if (counter0 == (SYS_CLK / baud_rate) - 1) begin
                state <= STATE_IDLE;
                tx_down <= 1;
            end
        endcase
    end
end

/* 执行状态 */
always @(posedge clk or negedge rstn) begin
    if (!rstn)
        tx <= 1;
    else
        case (state)
        STATE_IDLE:
            tx <= 1;
        STATE_START: begin
            tx <= 0;
        end
        STATE_DATA:
            tx <= byte[counter1];
        STATE_STOP: begin
            tx <= 1;
        end
        endcase
end

endmodule

接收模块实现

设计状态机如下:

  • counter0为波特率计数器
  • counter1为接收字节计数器
  • DETECT_START状态下,检测到下降沿进入START_CHECK
  • START_CHECK成功进行READ_DATA,否则继续DETECT_START
  • READ_DATA完毕进入STOP_CHECK
  • STOP_CHECK成功拉高down,否则拉高err
/* 对于强干扰的环境,UART的接收需要进行16倍过采样;取中间789三次结果,两次以上为1则为1,两次以上为0则为0
 * 异步信号(使用两个时钟)容易出现亚稳态:
 * 什么是亚稳态:D触发器输入信号在数据窗口期内发生变化,会导致输出进入亚稳态,可能发送震荡,并最终随机稳定在高电平或者低电平
 * 亚稳态的存在可能会影响其他逻辑对该信号值的判断
 * 通过2级或者更多的D触发器打拍的方式,降低亚稳态的传播,让后续逻辑使用的该信号正常
 * 100MHz时钟时,第一级D触发器有出现亚稳态的可能,第二级D触发器出现亚稳态的概率很小,第三级D触发器出现亚稳态概率非常小
 */

module uart_read_byte(
    input wire rstn,                /* 复位 */
    input wire clk,                 /* 时钟 */
    input wire rx,                  /* rx信号 */
    input wire[19:0] baud_rate,     /* 波特率 */
    output reg rx_down,             /* rx一字节完成 */
    output reg[7:0] data,           /* 接收的数据 */
    output reg err                  /* 接收错误 */
    );

parameter SYS_CLK = 100_000_000;
parameter MCNT1 = 8;

parameter STATE_DETECT_START = 0;   /* 检测起始位下降沿 */
parameter STATE_START_CHECK = 1;    /* 校验起始位电平是否正确 */
parameter STATE_READ_DATA = 2;      /* 读取数据 */
parameter STATE_STOP_CHECK = 3;     /* 检验结束位 */

reg[31:0] counter0;                 /* 波特率计数 */
reg[2:0] counter1;                  /* 接收字节计数 */
reg[1:0] state;                     /* 接受状态 */
reg[7:0] temp_data;                 /* 存储接收数据 */
reg rx_temp;                        /* 存储rx的值 */

/* 波特率计数 */
always @(posedge clk or negedge rstn) begin
    if (!rstn)
        counter0 <= 0;
    else if (state == STATE_DETECT_START)
        counter0 <= 0;
    else if (counter0 == (SYS_CLK / baud_rate) - 1)
        counter0 <= 0;
    else
        counter0 <= counter0 + 1;
end

/* 接收位计数 */
always @(posedge clk or negedge rstn) begin
    if (!rstn)
        counter1 <= 0;
    else if (state == STATE_READ_DATA)
        if (counter0 == (SYS_CLK / baud_rate) - 1)
            if (counter1 == MCNT1 - 1)
                counter1 <= 0;
            else
                counter1 <= counter1 + 1;
        else
            counter1 <= counter1;
    else
        counter1 <= 0;
end

reg[2:0] start_data;
wire start_negedge;
/* 检测下降沿起始位,打2拍防止亚稳态 */
always @(posedge clk or negedge rstn) begin
    if (!rstn)
        start_data <= 0;
    else
        if (state == STATE_DETECT_START) begin
            start_data[0] <= rx;
            start_data[2:1] <= start_data[1:0];
        end
        else begin
            start_data <= 0;
        end
end

assign start_negedge = start_data[2] & (~start_data[1]);

/* 状态判断 */
always @(posedge clk or negedge rstn) begin
    /* rstn中尽量减少非必须的复位赋值,有利于综合布线 */
    if (!rstn)
        state <= STATE_DETECT_START;
    else
        case (state)
        STATE_DETECT_START:
            if (start_negedge)
                state <= STATE_START_CHECK;
        STATE_START_CHECK:
            if (counter0 == (SYS_CLK / baud_rate) / 2)
                rx_temp <= rx;
            else if (counter0 == (SYS_CLK / baud_rate) - 1) begin
                if (rx_temp == 0)
                    state <= STATE_READ_DATA;
                else
                    state <= STATE_DETECT_START;
            end
            else
                state <= STATE_START_CHECK;
        STATE_READ_DATA:
            if (counter1 == MCNT1 - 1 && counter0 == (SYS_CLK / baud_rate) - 1)
                state <= STATE_STOP_CHECK;
        STATE_STOP_CHECK:
            if (counter0 == (SYS_CLK / baud_rate) / 2)
                state <= STATE_DETECT_START;
        endcase
end

/* 状态执行 */
always @(posedge clk or negedge rstn) begin
    if (!rstn) begin
        rx_down <= 0;
        data <= 0;
        err <= 0;
    end
    else
        case (state)
        STATE_DETECT_START: 
            temp_data <= 0;
        STATE_START_CHECK: begin
            rx_down <= 0;
            err <= 0;
        end
        STATE_READ_DATA:
            if (counter0 == (SYS_CLK / baud_rate) / 2)
                temp_data[counter1] <= rx;
        STATE_STOP_CHECK:
            /* 停止位只使用一半的时间,提前进行检测起始位,防止因为频率误差导致起始位未检测到 */
            if (counter0 == (SYS_CLK / baud_rate) / 2) begin
                err <= rx ? 0 : 1;
                rx_down <= 1;
                data <= temp_data;
            end
        endcase
end

endmodule

增加接收fifo

  • 增加位宽8,深度32的fifo
  • uart_read_byte的rx_down上升沿时将数据写入
module uart_read_fifo(
    input rstn,
    input clk,
    input en,
    input wire[19:0] baud_rate, /* 波特率 */
    input fifo_rd_en,
    output wire[7:0] rx_date,
    input rx,
    output wire[4:0] rx_fifo_count,
    output wire[3:0] rx_fifo_state,
    output rx_ack
    );

wire rx_down;
wire rx_err;
wire[7:0] rx_date_temp;

/* 检测rx_down上升沿 */
reg[1:0] rx_down_data;
wire read_posedge;
always @(negedge clk or negedge rstn) begin
    if (!rstn)
        rx_down_data <= 0;
    else begin
        rx_down_data[0] <= rx_down;
        rx_down_data[1] <= rx_down_data[0];
    end
end

assign read_posedge = rx_down_data[0] & (~rx_down_data[1]);

fifo_generator fifo_generator_uart_rx(
    .clk(clk),
    .srst(~rstn),
    .din(rx_date_temp),
    .wr_en(read_posedge && (~rx_err) && en),
    .rd_en(fifo_rd_en),
    .dout(rx_date),
    .full(rx_fifo_state[3]),
    .almost_full(rx_fifo_state[2]),
    .wr_ack(rx_ack),
    .empty(rx_fifo_state[1]),
    .almost_empty(rx_fifo_state[0]),
    .data_count(rx_fifo_count)
);

uart_read_byte uart_read_byte_obj0(
    .rstn(rstn),
    .clk(clk),
    .rx(rx),
    .baud_rate(baud_rate),
    .rx_down(rx_down),
    .data(rx_date_temp),
    .err(rx_err)
    );

endmodule

增加发送fifo

  • 增加位宽8,深度32的fifo
  • 通过状态机,将fifo中的数据通过uart_send_byte模块发送
module uart_send_fifo(
    input rstn,
    input clk,
    input en,
    input wire[19:0] baud_rate,     /* 波特率 */
    input fifo_wr_en,
    input wire[7:0] tx_date,
    output tx,
    output wire[4:0] tx_fifo_count,
    output wire[3:0] tx_fifo_state
    );

parameter STATE_WAIT_DATA = 0;      /* 等待FIFO中有数据 */
parameter STATE_START_SEND = 1;     /* 开始发送数据 */
parameter STATE_WAIT_SEND = 2;      /* 等待发送完成 */
parameter STATE_READ_FIFO = 3;      /* 读FIFO中的数据 */

reg[1:0] state;
wire tx_down;
reg rd_en_reg;
reg sd_go_reg;
wire[7:0] dout;

/* 判断状态 */
always @(posedge clk or negedge rstn) begin
    if (!rstn)
        state <= STATE_WAIT_DATA;
    else begin
        case (state)
        STATE_WAIT_DATA: begin
            /* fifo不为空 */
            if (tx_fifo_state[1] == 0)
                state <= STATE_READ_FIFO;
        end
        STATE_READ_FIFO:
                state <= STATE_START_SEND;
        STATE_START_SEND:
            if (tx_down == 0)
                state <= STATE_WAIT_SEND;
        STATE_WAIT_SEND:
            if (tx_down == 1)
                state <= STATE_WAIT_DATA;
        endcase
    end
end

/* 执行状态 */
always @(posedge clk or negedge rstn) begin
    if (!rstn) begin
        rd_en_reg <= 0;
        sd_go_reg <= 0;
    end
    else begin
        case (state)
        STATE_WAIT_DATA:
            ;
        STATE_READ_FIFO:
            rd_en_reg <= 1;
        STATE_START_SEND: begin
            rd_en_reg <= 0;
            sd_go_reg <= 1;
        end
        STATE_WAIT_SEND: begin
            sd_go_reg <= 0;
            rd_en_reg <= 0;
        end
        
        endcase
    end
end

fifo_generator fifo_generator_uart_tx(
    .clk(clk),
    .srst(~rstn),
    .din(tx_date),
    .wr_en(fifo_wr_en && en),
    .rd_en(rd_en_reg),
    .dout(dout),
    .full(tx_fifo_state[3]),
    .almost_full(tx_fifo_state[2]),
    .empty(tx_fifo_state[1]),
    .almost_empty(tx_fifo_state[0]),
    .data_count(tx_fifo_count)
);

uart_send_byte uart_send_byte_obj0(
    .rstn(rstn),
    .clk(clk),
    .baud_rate(baud_rate),
    .send_go(sd_go_reg),
    .byte(dout),
    .tx(tx),
    .tx_down(tx_down)
    );

endmodule

优化中断

  • 为减少CPU中断开销,当串口接收到多个包,只上报一次中断
  • 包个数不够产生中断时,上报超时中断
module interrupt(
  input rstn,
  input clk,
  input pkg_ack,
  input[14:0] item_num,
  input[15:0] time_out,
  input period,
  output irq
);

parameter SYS_CLK = 100_000_000;
reg[14:0] counter0;
reg[31:0] counter1;
reg irq_reg;

/* pkg_ack计数 */
always @(negedge clk or negedge rstn) begin
  if (!rstn)
    counter0 <= 0;
  else if (counter1 == ((SYS_CLK / 1_000_000) * time_out) - 1 || counter0 == item_num - 1)
    counter0 <= 0;
  else if (pkg_ack)
      counter0 <= counter0 + 1;
    else
      counter0 <= counter0;
end

/* 定时器,控制timeout */
always @(posedge clk or negedge rstn) begin
  if (!rstn)
    counter1 <= 0;
  else if (counter0 || period) begin
    if (counter1 == ((SYS_CLK / 1_000_000) * time_out) - 1 || counter0 == item_num - 1)
      counter1 <= 0;
    else
      counter1 <= counter1 + 1;
  end
  else
    counter1 <= 0;
end

/* 输出中断 */
always @(posedge clk) begin
  if (counter1 == ((SYS_CLK / 1_000_000) * time_out) - 1 || counter0 == item_num - 1) begin
    irq_reg <= 1;
  end
  else
    irq_reg <= 0;
end

assign irq = irq_reg;

endmodule

使用Xillybus Lite连接PL & PS

Xillybus Lite 是一个简单的套件,用于通过在 Linux 下运行的 user space 程序轻松访问 logic fabric ( PL ) 中的 registers

Xillybus IP使用

Xillybus Lite读写时序如下:
在这里插入图片描述
Xillybus Lite连接如下:
在这里插入图片描述

xillybus读写及寄存器定义

  • 增加一个程序版本寄存器,方便核对程序版本
  • uart控制器寄存器依次为:
    • 0x00 串口使能寄存器
    • 0x04 串口读写控制寄存器
    • 0x08 串口数据寄存器
    • 0x0c 串口发送波特率寄存器
    • 0x10 串口发送fifo数据个数寄存器
    • 0x14 串口发送fifo状态寄存器
    • 0x18 串口接收波特率寄存器
    • 0x1c 串口接收fifo数据个数寄存器
    • 0x20 串口接收fifo状态寄存器
    • 0x24 串口中断控制寄存器
module xillybus_rw(
  input wire rstn,
  input wire user_clk,
  input wire [31:0]user_addr,
  input wire [31:0]user_wren,
  input wire user_rden,
  output reg [31:0]user_rd_data,
  input wire [31:0]user_wr_data,
  input wire [3:0]user_wstrb,
  output wire uart_tx,
  input wire uart_rx,
  output wire[7:0] led,
  output wire irqf2p_0,
  output wire irqf2p_1,
  output wire irqf2p_10,
  output wire irqf2p_11,
  output wire irqf2p_12,
  output wire irqf2p_13,
  output wire irqf2p_14,
  output wire irqf2p_15,
  output wire irqf2p_2,
  output wire irqf2p_3,
  output wire irqf2p_4,
  output wire irqf2p_5,
  output wire irqf2p_6,
  output wire irqf2p_7,
  output wire irqf2p_8,
  output wire irqf2p_9
);

/* OFFSET:0x00 版本信息 */
parameter VERSION = 32'h23121504; //程序版本

/* OFFSET:0x10 串口模块 */
reg uart_flag_tx_reg;
reg uart_flag_rx_reg;
reg[1:0] uart_en_reg;             /* 00:0x00 串口使能寄存器 */
reg[31:0] uart_ctr_reg;           /* 01:0x04 串口读写控制寄存器*/
reg[31:0] uart_data_reg;          /* 02:0x08 串口数据寄存器 */
wire[31:0] uart_data_rx_wire;
reg[19:0] uart_tx_baud_rate;      /* 03:0x0c 串口发送波特率寄存器 */
wire[31:0] fifo_count_tx_wire;    /* 04:0x10 串口发送fifo数据个数寄存器 */
wire[31:0] uart_state_tx_wire;    /* 05:0x14 串口发送fifo状态寄存器 */
reg[19:0] uart_rx_baud_rate;      /* 06:0x18 串口接收波特率寄存器 */
wire[31:0] fifo_count_rx_wire;    /* 07:0x1c 串口接收fifo数据个数寄存器 */
wire[31:0] uart_state_rx_wire;    /* 08:0x20 串口接收fifo状态寄存器 */
reg[31:0] uart_int_ctr_reg;       /* 09:0x24 串口中断控制寄存器 */
wire rx_pkg_ack;

always @(posedge user_clk) begin
  if (!rstn) begin
    uart_flag_rx_reg <= 0;
    uart_flag_tx_reg <= 0;
    uart_int_ctr_reg <= 32'h0010_03e8;
    uart_tx_baud_rate <= 9600;
    uart_rx_baud_rate <= 9600;
  end
  else begin
    if (user_wren) begin
      /* 15:0 共6MByte可访问 */
      case (user_addr[15:2])
      4:uart_en_reg <= user_wr_data;
      5: begin
        uart_ctr_reg <= user_wr_data;
        if (user_wr_data & (1 << 31))
          uart_flag_rx_reg <= 1;
        else if (user_wr_data & (1 << 30))
          uart_flag_tx_reg <= 1;
      end
      6:uart_data_reg <= user_wr_data;
      7:uart_tx_baud_rate <= user_wr_data;
      10:uart_rx_baud_rate <= user_wr_data;
      13:uart_int_ctr_reg <= user_wr_data;
      endcase
    end
    else begin
      uart_flag_rx_reg <= 0;
      uart_flag_tx_reg <= 0;
    end
    if (user_rden) begin
      case (user_addr[15:2])
      0:user_rd_data <= VERSION;
      4:user_rd_data <= uart_en_reg;
      5:user_rd_data <= uart_ctr_reg;
      6: begin
        user_rd_data <= uart_data_rx_wire;
      end
      7:user_rd_data <= uart_tx_baud_rate;
      8:user_rd_data <= fifo_count_tx_wire;
      9:user_rd_data <= uart_state_tx_wire;
      10:user_rd_data <= uart_rx_baud_rate;
      11:user_rd_data <= fifo_count_rx_wire;
      12:user_rd_data <= uart_state_rx_wire;
      13:user_rd_data <= uart_int_ctr_reg;
      default: user_rd_data <= 0;
      endcase
    end
  end
end

uart_send_fifo uart_send_fifo_obj0(
    .rstn(rstn),
    .clk(user_clk),
    .en(uart_en_reg[0]),
    .baud_rate(uart_tx_baud_rate),
    .fifo_wr_en(uart_flag_tx_reg),
    .tx_date(uart_data_reg),
    .tx(uart_tx),
    .tx_fifo_count(fifo_count_tx_wire),
    .tx_fifo_state(uart_state_tx_wire)
    );

uart_read_fifo uart_read_fifo_obj0(
    .rstn(rstn),
    .clk(user_clk),
    .en(uart_en_reg[1]),
    .baud_rate(uart_rx_baud_rate),
    .fifo_rd_en(uart_flag_rx_reg),
    .rx_date(uart_data_rx_wire),
    .rx(uart_rx),
    .rx_fifo_count(fifo_count_rx_wire),
    .rx_fifo_state(uart_state_rx_wire),
    .rx_ack(rx_pkg_ack)
    );

interrupt interrupt_obj0(
  .rstn(rstn),
  .clk(user_clk),
  .pkg_ack(rx_pkg_ack),
  .item_num(uart_int_ctr_reg[30:16]),
  .time_out(uart_int_ctr_reg[15:0]),
  .period(uart_int_ctr_reg[31]),
  .irq(irqf2p_0)
);

endmodule

linux下UART驱动编写

轮询发送

  • 简单起见,uart控制器未设计发送中断,所以使用轮询发送
static void uart_start_tx(struct uart_port *port)
{
	struct dev_priv *priv;
	struct peng_dev *p_pdev;
	struct circ_buf *xmit;
	struct tty_port *tport;
	u32 state, n;
	priv = container_of(port, struct dev_priv, port);
	p_pdev = &priv->pdev;
	tport = &port->state->port;
	xmit = &port->state->xmit;
	dev_dbg(priv->pdev.p_dev, "uart_start_tx\n");
	if (uart_tx_stopped(port))
		return;
	if (uart_circ_empty(&port->state->xmit))
		return;
	n = PDEV_CSR_READ(p_pdev, PREG_REG, UART_TX_FIFO_COUNT);
	state = PDEV_CSR_READ(p_pdev, PREG_REG, UART_TX_FIFO_STATE);
	/* UART_TX_FIFO_COUNT满或空时都为0,需要根据UART_TX_FIFO_STATE来判断是否满 */
	if (state & UART_FIFO_STATE_FULL)
		return;
	n = UART_FIFO_SIZE - n;
	while (n) {
		if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
			dev_dbg(p_pdev->p_dev, "uart_circ_empty\n");
			/* 发送完成 */
			break;
		} else {
			dev_dbg(p_pdev->p_dev, "T:0x%02x", xmit->buf[xmit->tail]);
			priv->txBytCnt++;
			n--;
			spin_lock(&port->lock);
			PDEV_CSR_WRITE(p_pdev, PREG_REG, UART_DATA, (u32)xmit->buf[xmit->tail]);
			PDEV_CSR_WRITE(p_pdev, PREG_REG, UART_CTL, UART_CTL_TX);
			spin_unlock(&port->lock);
			/* 调整循环缓冲的位置 */
			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
			/* 发送的数据量加1 */
			port->icount.tx++;
		}
	}
	/* 等待发送完成 */
	while(1) {
		state = PDEV_CSR_READ(p_pdev, PREG_REG, UART_TX_FIFO_STATE);
		if(state & UART_FIFO_EMPTY)
			break;
		mdelay(1);
	}
	/* 如果循环缓冲里面的数据小于WAKEUP_CHARS(256),则唤醒之前阻塞的发送进程 */
	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
		uart_write_wakeup(port);
}

中断接收

  • 通过IRQ_F2P,PL将中断发送给PS
  • 中断中将FIFO中缓存的数据提交给TTY层
static irqreturn_t dev_isr(int irq, void *dev_id)
{
	u32 n, state;
	u8 data;
	struct dev_priv *priv;
	struct peng_dev *p_pdev;
	struct uart_port *port;
	struct tty_port *tport;
	struct circ_buf *xmit;
	priv = (struct dev_priv *)dev_id;
	p_pdev = &priv->pdev;
	port = &priv->port;
	tport = &port->state->port;
	xmit = &port->state->xmit;
	
	spin_lock(&port->lock);
	priv->nRxIntCount++;
	n = PDEV_CSR_READ(p_pdev, PREG_REG, UART_RX_FIFO_COUNT);
	state = PDEV_CSR_READ(p_pdev, PREG_REG, UART_RX_FIFO_STATE);
	if (state & UART_FIFO_STATE_FULL)
		n = UART_FIFO_SIZE;
	dev_dbg(p_pdev->p_dev, "rx,n=%d\n", n);
	mb();
	/* 接收fifo中字节数 */
	priv->rxBytCnt += n;
	while (n--) {
		/* 将收到的数据提交给TTY层 */
		PDEV_CSR_WRITE(p_pdev, PREG_REG, UART_CTL, UART_CTL_RX);
		data = PDEV_CSR_READ(p_pdev, PREG_REG, UART_DATA);
		if (!uart_handle_sysrq_char(port, data))
			uart_insert_char(port, priv->intStatus, 0, data, TTY_NORMAL);
		dev_dbg(p_pdev->p_dev, "R:0x%02x", data);
	}
	tty_flip_buffer_push(tport);
	spin_unlock(&port->lock);

	return IRQ_HANDLED;
}

控制器功能测试

接收测试

输入命令查看uart数据

cat /dev/ttyFpga0

电脑串口助手发送51字节数据(超过fifo深度)
在这里插入图片描述

左侧为zynq控制台,右侧为电脑串口助手

  • 51字节产生4次中断,中断优化符合预期
  • 右侧发送数据和左侧接收一致,符合预期

发送测试

输入命令发送uart数据,(数据大小超过fifo深度)

echo asdfghjklasdfghjklasdfghjklasdfghjklasdfghjkl > /dev/ttyFpga0

在这里插入图片描述

  • 左侧发送数据和右侧接收一致,符合预期

关注博主公众号,优质文章不断更新
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

最好有梦想~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值