首页 > 其他分享 >【数字IC&FPGA项目】AHB_UART-FIFO控制器设计

【数字IC&FPGA项目】AHB_UART-FIFO控制器设计

时间:2024-12-23 09:03:22浏览次数:6  
标签:AHB begin last FPGA UART rx FIFO uart

【数字IC&FPGA项目】AHB_UART-FIFO控制器设计

实现一个带FIFO的UART收发控制器,并挂在AHB接口上,分为AHB接口和控制模块、发送FIFO、UART发送器、接收FIFO、UART接收器、波特率分频器模块:

AHBUART

各部分实现功能:

  1. UART发送器:从发送FIFO中读取一个字节的数据(8bit),进行并/串转换并发送到TX引脚
  2. UART接收器:从RX引脚接收异步串行信号,串/并转换为一个字节,打入接收FIFO
  3. FIFO:采用同步FIFO设计,但是实现效果类似于异步,以TX为例,AHB总线时钟HCLK速率写入FIFO,以波特率读出。这样做的好处时,需要发送数据时,处理器可以用快时钟先将数据打入FIFO作为缓冲,然后UART再慢慢读出,这期间处理器就可以去做别的事情了,不用一直处在等待UART接收数据的阶段。

参考书籍:《ARM Cortex-M0全可编程SoC原理及实现》

1 详细设计

1.1 同步FIFO

参考【数字IC】同步FIFO设计详解(含源码) - 知乎,并做出了一些改进。部分关键代码:

读/写控制逻辑
// 写控制逻辑
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        w_ptr <= 0;
    end
    else if (wr && !full) begin
        w_ptr <= w_ptr + 1;
    end
end
// 读控制逻辑
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        r_ptr <= 0;
    end
    else if (rd && !empty) begin
        r_ptr <= r_ptr + 1;
    end
end
对Dual-RAM读/写数据
// 直接assign数据到线上,通过使能来控制是否取用该数据
assign r_data = mem_array[r_ptr];

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        for (i = 0; i < 2**AWIDTH; i = i + 1) begin
            mem_array[i] <= 0;
        end
    end
    else if (wr && !full) begin
        mem_array[w_ptr] <= w_data;
    end
end
读写指针比较逻辑(empty/full信号产生逻辑)

这里采取间接比较法,通过保存FIFO中的数据个数elem_cnt产生empty/full信号。

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        elem_cnt <= 0;
    end
    else begin
        // 多个if式的逻辑要判断全面,确保尽量是互斥关系,比如这里不能是wr & !full
        if (wr & !rd & !full)
            elem_cnt <= elem_cnt + 1;
        else if (!wr & rd & !empty)
            elem_cnt <= elem_cnt - 1;
        else if(wr & rd & !full & !empty)
            elem_cnt <= elem_cnt;
        else
            elem_cnt <= elem_cnt;
    end
end

assign empty = (elem_cnt == 0) ? 1 : 0;
assign full = (elem_cnt == 2**AWIDTH) ? 1 : 0;
1.2 uart_rx设计

网上讲解UART的代码很多了,这里只放出个人认为最关键的部分。uart_rx关键代码在于将异步通信信号同步到系统时钟,以及串/并转换状态机的实现。

同步器部分
// 同步器, 通过打拍采样异步信号rx
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        rx_d0 <= 0;
        rx_d1 <= 0;
        rx_d2 <= 0;
    end
    else begin
        rx_d0 <= uart_rxd;
        rx_d1 <= rx_d0;
        rx_d2 <= rx_d1;
    end
end

// 下降沿捕获
assign start_en = ~rx_d1 & rx_d2 & ~rx_busy;

其中rx_d0rx_d1充当同步器,用来消除亚稳态。rx_d2rx_d1配合用来捕获rx信号的下降沿,即检测起始位。

发送FSM
// FSM
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        state <= idle_st;
        b_reg <= 0, count_reg <= 0, data_reg <= 0;
    end
    else begin
        state <= next_state;
        b_next <= b_reg;			// 波特率计数器, 计满16个为一个波特周期
        count_next <= count_reg;	// 数据位计数器
        data_reg <= data_next;		// 寄存8bit待接收的字符
    end
end

always @(*) begin
    next_state = state;
    b_next = b_reg, count_next = count_reg, data_next = data_reg;
    uart_rx_done = 0;

    case (state)
        idle_st: begin
            if(start_en) begin
                next_state = start_st;
                b_next = 0;
            end
        end
        start_st: begin
            if(b_tick)
                // 从起始位中间计数,确保每次在电平持续中间对数据进行采样
                if(b_reg == 4'd7) begin		
                    next_state = data_st;
                    b_next = 0, count_next = 0;
                end
                else
                    b_next = b_reg + 4'd1;
        end
        data_st: begin
            if(b_tick)
                if(b_reg == 4'd15) begin
                    next_state = data_st;
                    b_next = 0;
                    data_next = {rx_d2, data_reg[7:1]};		// 采样数据
                    if (count_next == 8 - 1)    // 8个数据位
                        next_state = stop_st;
                    else
                        count_next = count_reg + 3'd1;
                end
                else
                    b_next = b_reg + 4'd1;
        end
        stop_st: begin
            if(b_tick)
                if(b_reg == 4'd15) begin
                    next_state = idle_st;
                    uart_rx_done = 1;
                end
                else
                    b_next = b_reg + 4'd1;
        end
    endcase
end
1.3 uart_tx设计

与uart_rx比较类似,也是通过一个FSM状态机进行数据发送,需要注意的是截止位的处理。没有计满16个周期, 而是提前拉低了1/16个波特率周期,确保没有tx周期略小于rx周期,否则大量数据连续传输时可能会发送丢包

case(state)
    idle_st: ...
    start_st: ...
    data_st: ...
    stop_st: //send stop bit
    begin
        tx_next = 1'b1;
        if(b_tick) begin
            // 没有计满16个周期, 而是提前拉低了1/16个波特率周期
            // 确保没有tx周期略小于rx周期
            // 否则大量数据连续传输时可能会发送丢包
            if(b_reg == 14)   //one stop bit
            begin
                next_state = idle_st;
                uart_tx_done = 1'b1;
            end
            else
                b_next = b_reg + 1;

        end
    end
    ...
endcase
1.4 总线接口设计

总线接口一般处理好几件事:

  1. 内存地址(Memory Map)映射。这里通过判断地址位的最低两位来判断操作

    HADDR[7:0]HWRITE操作
    0x001HWDATA[7:0]=>uart_wdata
    0x000HRADATA[7:0]<=uart_rdata
    0x040HRADATA[7:0]<=status
  2. 判断写数据/写使能

    assign uart_wr_en = last_HTRANS[1] & last_HWRITE & last_HSEL & (last_HADDR[7:0] == 8'h00);
    assign uart_rd_en = last_HTRANS[1] & ~last_HWRITE & last_HSEL & (last_HADDR[7:0] == 8'h00);
    

    写数据时直接采样总线数据到uart_wdata线上, 直到写使能后打入FIFO

    assign uart_wdata = HWDATA[7:0];
    
  3. 读数据,输出HRDATA和HREADYOUT

下面是整体的代码:

assign HREADYOUT = ~tx_full;

// 写使能
assign uart_wr_en = last_HTRANS[1] & last_HWRITE & last_HSEL & (last_HADDR[7:0] == 8'h00);
// 写数据(一直采样总线数据, 直到写使能后打入FIFO)
assign uart_wdata = HWDATA[7:0];

// 读使能
assign uart_rd_en = last_HTRANS[1] & ~last_HWRITE & last_HSEL & (last_HADDR[7:0] == 8'h00);
// 读出状态/数据
assign HRDATA = (last_HADDR[7:0] == 8'h00) ? {24'b0, uart_rdata} : {24'b0, status};
assign status = {6'b0, tx_full, rx_empty};

// 中断: 收到数据时, 跳转到软件中断服务程序,接收串口数据
assign uart_irq = ~rx_empty;

2 仿真结果

2.1 FIFO部分

对FIFO模块单独进行读写测试:

FIFO测试

整体测试:在SoC完成复位、初始化等操作后,开始将字符串"TEST"依次打入发送FIFO

  1. 处理器选中UART控制器外设所在地址0x53000000
  2. 处理器发送’T’所对应的ASCII码0x00000054
  3. 写入FIFO(FIFO中的写指针w_ptr <= w_ptr + 1),此时empty拉高,通知UART_TX开始从FIFO取数,但是由于UART发送非常慢,因此需要等待tx_done才正式取走FIFO的顶部字符,这期间FIFO的读指针r_ptr是不会变的(否则又将拉低empty导致误触发)。
  4. 将"TEST"的剩余字符依次打入FIFO
FIFO局部波形_note
2.2 UART部分

对UART单独进行回环验证测试:

UART测试

整体测试:

  1. 从FIFO中读取待发送的字符’T’,寄存在data_reg寄存器中
  2. 波特率时钟生成:根据波特率周期,对HCLK进行分频产生b_tick,每16个b_tick发送一个bit
  3. 开始串口发送,发送寄存的数据,data_reg依次右移并发送最低位
  4. 波特周期计数器count_reg从0~7计数
  5. count_reg计满后发送停止位,然后拉高tx_done
  6. 从FIFO中读取并处理"TEST"后面的字符。
UART波形_note
2.3 AHB2UART收发器整体测试

对比FIFO相关信号(图中黄色)和UART相关信号(图中蓝色),可以很明显的发现使用FIFO的优势:处理器很快就将数据打入了FIFO,然后就可以去其他的事了。UART再慢慢从FIFO中取用。

需要注意的是尽管这里FIFO的写入和读出速率看似不一样,行为上类似于异步FIFO,但仍然是在一个时钟下控制的(即快时钟HCLK),因此它仍然是一个同步FIFO。UART是通过每发送完一个字符,拉高tx_done来使能FIFO的读操作,控制FIFO的读出,因此波特率是多少,读出的速率就是多少,看似是以波特率来读出数据的。

FIFO-UART波形_note

3 上板验证

复位发送"TEST"到电脑,

然后将进入循环等待模式,将接收数据回环发射。

UART串口助手

总结

FIFO通常是用来增加系统的elasticity(弹性)的,也可以简单理解为数据缓冲区。

总的来说,几乎对于任何一个片上系统,AHB总线、UART、FIFO都是基础但不可或缺的外设,作为一个入门的练手项目,还是有很大的收获的。

标签:AHB,begin,last,FPGA,UART,rx,FIFO,uart
From: https://blog.csdn.net/CSDN__cyl/article/details/144656883

相关文章

  • 快速入门指南:玩转硬件加速器(GPU, TPU, FPGA)
    这是一篇以HTML语言呈现的文章。在网页上实现复杂排版和交互通常需要用到更多的技术,如CSS与JavaScript。为了简化的解释,我们将通过一个基本的HTML结构来概述“快速入门指南:玩转硬件加速器(GPU、TPU、FPGA)”。###快速入门指南:玩转硬件加速器(GPU,TPU,FPGA)快速入门指南:掌握GPU......
  • 找不到quartz.dll文件或quartz.dll文件丢失该怎么办?
    在大部分情况下出现我们运行或安装软件,游戏出现提示丢失某些DLL文件或OCX文件的原因可能是原始安装包文件不完整造成,原因可能是某些系统防护软件将重要的DLL文件识别为可疑,阻止并放入了隔离单里,还有一些常见的DLL文件缺少是因为系统没有安装齐全的微软运行库,还有部分情况是因为......
  • 动画图解嵌入式常见的通讯协议:SPI、I²C、UART、红外
    文章下方附学习资源,自助领取。1SPI传输 ▲图1SPI数据传输 ▲图1.2SPI数据传输(2)  ▲图1.3SPI时序信号2I²C传输  ▲图1.2.1I2C总线以及寻址方式3年嵌入式物联网学习资源整理分享:C语言、Linux开发、数据结构;软件开发,STM32单片机、ARM硬......
  • 【教程4>第4章>第11节】通过FPGA实现带频偏基带数据的FFT变换
    本课程学习效果预览 欢迎订阅FPGA/MATLAB/Simulink系列教程《★教程1:matlab入门100例》《★教程2:fpga入门100例》《★教程3:simulink入门60例》《★教程4:FPGA/MATLAB/Simulink联合开发入门与进阶X例》目录1.软件版本2.理论简介3.通过FPGA实现带频偏基带数据的F......
  • 【教程4>第4章>第12节】通过FPGA实现基于FFT变换的频偏估计和补偿
    本课程学习效果预览 欢迎订阅FPGA/MATLAB/Simulink系列教程《★教程1:matlab入门100例》《★教程2:fpga入门100例》《★教程3:simulink入门60例》《★教程4:FPGA/MATLAB/Simulink联合开发入门与进阶X例》目录1.软件版本2.理论简介3.通过FPGA实现基于FFT变换的频偏......
  • 【硬件测试】基于FPGA的2FSK调制解调系统开发与硬件片内测试,包含信道模块,误码统计模
    1.算法仿真效果本文是之前写的文章 基于FPGA的2FSK调制解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR_fpga2fsk-CSDN博客 的硬件测试版本。 在系统在仿真版本基础上增加了ila在线数据采集模块,vio在线SNR设置模块,数据源模块。 硬件ila测试结......
  • szfpga ANLOGIC AL-LINK安路下载器
    1.概述   ANLOGIC AL-LINK是用于国产FPGAANLOGIC安路的芯片编程设备。使用TD软件来下载烧录板载芯片。并且速度最大可以支持6Mb/s,极速完成下载和波形调试功能。相比USBCABLE速度更快,在大容量的FPGA更稳定烧录和调试。   安路下载器接口定义:TCKGND......
  • 如何实现FPGA固件远程升级
    一、功能介绍在实际的项目开发中,为了便于产品的后续维护和代码的功能升级,往往需要实现FPGA固件的远程升级。FPGA固件升级可以由很多种方式,比如使用外部单片机、ARM等控制芯片接收上位机下发的FPGA固件信息,并将其写入FPGA的配置Flash中。如果我们的实际电路上并没有单片机、ARM等......
  • 004---FPGA在线调试(一)---内嵌的逻辑分析仪(ILA)
    文章目录摘要文章主要介绍fpga内嵌的逻辑分析仪(ILA)的使用方法。以led工程为例,介绍几种方法,观察内部信号timer_cnt和led的变化。一、ILAip核二、MARKDEBUG摘要文章主要介绍fpga内嵌的逻辑分析仪(ILA)的使用方法。以led工程为例,介绍几种方法,观察内部信号timer_cnt......
  • 人脸修复与增强腾讯开源项目GFPGAN介绍
    GFPGAN简述GFPGAN(GenerativeFacialPriorGAN)是一种基于生成对抗网络(GAN)的面部图像修复与增强模型。它由腾讯ARCLab的研究团队开发,目的是以高效和高质量的方式修复低分辨率、受损或老化的人脸图像,同时保留其真实感和身份一致性。GFPGAN是当前图像处理领域的热门......