首页 > 其他分享 >FPGA入门笔记011_B——搭建串口收发与存取双口RAM简易应用系统

FPGA入门笔记011_B——搭建串口收发与存取双口RAM简易应用系统

时间:2024-04-08 19:13:33浏览次数:16  
标签:双口 Tx Clk FPGA Rx Send Done 串口 Rst

1、实验现象

​ 通过串口发送数据到 FPGA 中,FPGA 接收到数据后将数据存储在双口 ram 的一段连续空间中,通过 Quartus II 软件提供的 In-System Memory Content Editor 工具查看 RAM 中接收到的数据。当需要时,按下设计好的按键 ,则 FPGA 将 RAM 中存储的数据通过串口发送出去。

2、知识点:

​ 1、Altera 公司 Cyclone IV 系列器件的内部结构。

​ 2、存储器(RAM) IP 核的使用。

​ 3、In-System Memory Content Editor 工具查看内存的使用。

​ 4、串口收发+按键+双口 RAM 组成的简易系统设计。

3、系统模块功能划分及接口设计

​ 在设计前,先进行整体功能划分。本工程划分为如下几个模块:

​ (1)串口接收模块(UART_Byte_Rx);

​ (2)按键消抖模块(Key_Filter);

​ (3)RAM模块(dpram);

​ (4)串口发送模块(UART_Byte_Tx);

​ (5)控制模块(CTRL)

​ 整个系统框图如图 1 所示。

image-20240407161301492

图1——串口收发与存储双口RAM简易应用系统整体框图

​ 明确了系统的整体模块架构之后,即可以开始进行顶层文件设计。

4、顶层文件设计

​ 新建工程后,先将已编写好的模块设计文件添加进工程中,并新建一个以名为 UART_DPRAM.v 的设计文件保存在 rtl 下,并设置为顶层文件。

​ 这样对照图1 用 Top-Down 的设计方式就可以先把顶层文件写出。结构图左边的端口 为 input 类型,右边的为 output 类型,内部连线均为 wire 型。然后例化各个模块,这里将波特率设置为 9600bps。其模块接口列表如表 1 所示。

image-20240408104844051

表1——UART_DPRAM模块接口列表

​ 代码如下:

module UART_DPRAM(
    Clk,
    Rst_n,

    Key_in,

    Rs232_Rx,
    Rs232_Tx
);

    input Clk;
    input Rst_n;
    input Key_in;

    input Rs232_Rx;
    output Rs232_Tx;

    wire [7:0]rx_data;
    wire [7:0]tx_data;

    wire key_flag;
    wire key_state;

    wire Rx_Done;
    wire Tx_Done;

    wire wren;
    wire [7:0]wraddress;
    wire [7:0]rdaddress;

    wire Send_En;

    uart_byte_tx uart_byte_tx(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .Send_En(Send_En),
        .Data_Byte(tx_data),
        .Baud_Set(3'd0),

        .Rs232_Tx(Rs232_Tx),
        .Tx_Done(Tx_Done),
        .UART_state()
    );

    uart_byte_rx uart_byte_rx(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .Baud_Set(3'd0),
        .Rs232_Rx(Rs232_Rx),

        .Data_Byte(rx_data),
        .Rx_Done(Rx_Done)
    );

    key_filter key_filter(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .key_in(Key_in),

        .key_flag(key_flag),
        .key_state(key_state)
    );

    dpram dpram(
        .clock(Clk),
        .data(rx_data),
        .rdaddress(rdaddress),
        .wraddress(wraddress),
        .wren(wren),

        .q(tx_data)
    );

    CTRL CTRL(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .key_flag(key_flag),
        .key_state(key_state),
        .Rx_Done(Rx_Done),
        .Tx_Done(Tx_Done),

        .wren(wren),
        .wraddress(wraddress),
        .rdaddress(rdaddress),
        .Send_En(Send_En)
    );

endmodule

5、控制模块设计

​ 现在编写本系统的控制模块 CTRL.v,模块的接口可参照系统结构图写出。

module CTRL(
    Clk,
    Rst_n,
    key_flag,
    key_state,
    Rx_Done,
    Tx_Done,

    wren,
    wraddress,
    rdaddress,
    Send_En
);
    
    input Clk;
    input Rst_n;
    input key_flag;
    input key_state;
    input Rx_Done;
    input Tx_Done;

    output wren;
    output reg [7:0]wraddress;
    output reg [7:0]rdaddress;
    output reg Send_En;

endmodule

​ 为了实现 FPGA 接收到数据后将数据存储在双口 ram 的一段连续空间中的功能,需要设计一个可以实现写地址数据自加的控制逻辑,且其控制信号为串口接收模块输出的 Rx_Done 信号。每来一个 Rx_Done 也就是每接收成功一字节数,地址数进行加一。

	assign wren = Rx_Done;	//将wren与Rx_Done相连,Rx_Done直接控制dpram的数据写入

//控制写数据地址信号
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        wraddress <= 8'd0;
    else if(Rx_Done)
        wraddress <= wraddress + 1'b1;	//写地址加一,延迟一拍,数据自动加入到dpram中
    else
        wraddress <= wraddress;

​ 当按下按键,FPGA 将 RAM 中存储的数据通过串口发送出去。也就是实现按键按下即启动连续读操作,再次按下即可暂停读操作。

    reg do_send;    //输出标志信号

//控制读数据地址信号
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        do_send <= 1'd0;
else if(key_flag && !key_state)	//当按键按下时
        do_send <= ~do_send;
    else
        do_send <= do_send;

    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        rdaddress <= 8'd0;
else if(do_send && Tx_Done)	//当"do_send == 1'b1"且"Tx_Done == 1'b1"(1字节的数据发送完成)
        rdaddress <= rdaddress + 8'd1;
    else
        rdaddress <= rdaddress;

​ 在上一节中仿真双端口 RAM 时发现其输出tx_data会延迟两个系统时钟周期。这是为了保证数据变化稳定之后才进行数据输出,所以将驱动 Send_En 的信号接两级寄存器进行延迟两拍。当 按键按下后启动一次发送,然后判断上一字节是否发送结束,是则进行下一字节发送,否则不进行下一次发送。

	//加入两个寄存器"r0_send_done","r1_send_done",延迟两拍
    reg r0_send_done,r1_send_done;

    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)begin
        r0_send_done <= 1'b0;
        r1_send_done <= 1'b0;
    end
    else begin
        r0_send_done <= (do_send && Tx_Done);
        r1_send_done <= r0_send_done;
    end

	//Send_En控制何时发送数据
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        Send_En <= 1'b0;
    else if(r1_send_done)
        Send_En <= 1'b1;
else if(key_flag && !key_state)		//第一次按下按键时,Send_En为1
        Send_En <= 1'b1;
    else
        Send_En <= 1'b0;

​ 编译无误后,可以在 RTL viewer 中查到如图 2 所示的顶层结构图,可与实验之初设计的系统结构图进行对比。

image-20240408112502805

图2——系统顶层 RTL viewer

​ 通过对比可以验证本工程逻辑设计输出的顶层 RTL 结构图和系统结构图的设想是一致的。

6、激励创建及仿真测试

​ 为了进行仿真测试,接下来介绍仿真测试激励文件的编写。这里由于使用了按键消抖模块,因此须将之前创建的 key_modle.v 仿真模型加入到工程中。新建 UART_DPRAM_tb.v 文 件除了例化各模块,其激励文件可以直接复制串口发送模块的测试激励文件 uart_byte_tx.v 产生激励信号 Rs232_Rx。再次进行分析和综合直至没有错误以及警告,并保存到 testbench 文件夹下。

`timescale 1ns/1ns
`define clk_period 20

module UART_DPRAM_tb;

    reg Clk;
    reg Rst_n;
    wire Key_in;

    wire Rs232_Rx;
    wire Rs232_Tx;

    reg Send_En;
    reg [7:0]Data_Byte;
    wire [2:0]Baud_Set;
    wire Tx_Done;
    reg press;

    assign Baud_Set = 3'd0;

    UART_DPRAM UART_DPRAM(
        .Clk(Clk),
        .Rst_n(Rst_n),

        .Key_in(Key_in),

        .Rs232_Rx(Rs232_Rx),
        .Rs232_Tx(Rs232_Tx)
    );

    uart_byte_tx uart_byte_tx(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .Send_En(Send_En),
        .Data_Byte(Data_Byte),
        .Baud_Set(Baud_Set),

        .Rs232_Tx(Rs232_Rx),
        .Tx_Done(Tx_Done),
        .UART_state()
    );

    key_model key_model(
        .press(press),
        .key(Key_in)
    );

    initial Clk = 1'b1;
    always#(`clk_period/2) Clk = ~Clk;

    initial begin
        //发送模块激励
        Rst_n = 1'b0;
        press = 1'b0;
        Data_Byte = 8'b0;
        Send_En = 1'b0;

        #(`clk_period*20 + 1)   //让复位信号不与时钟边沿对其,更好观察时序关系
        Rst_n = 1'b1;

        #(`clk_period*50)
        Data_Byte = 8'haa;  //第一次,发送"8'haa"
        Send_En = 1'b1;
        #`clk_period
        Send_En = 1'd0;

        @(posedge Tx_Done)  //当Tx_Done信号出现上升沿时,进行接下来的语句

        #(`clk_period*5000)
        Data_Byte = 8'h55;  //第二次,发送"8'h55"
        Send_En = 1'b1;
        #`clk_period
        Send_En = 1'd0;

        @(posedge Tx_Done)  //当Tx_Done信号出现上升沿时,进行接下来的语句

        #(`clk_period*5000)
        Data_Byte = 8'h33;  //第三次,发送"8'h33"
        Send_En = 1'b1;
        #`clk_period
        Send_En = 1'd0;

        @(posedge Tx_Done)  //当Tx_Done信号出现上升沿时,进行接下来的语句

        #(`clk_period*5000)
        Data_Byte = 8'haf;  //第四次,发送"8'haf"
        Send_En = 1'b1;
        #`clk_period
        Send_En = 1'd0;

        @(posedge Tx_Done)  //当Tx_Done信号出现上升沿时,进行接下来的语句

        #(`clk_period*5000)

        //按下一次按键
        press = 1'b1;
        #(`clk_period*3)
        press = 1'b0;

        #(`clk_period*2500000)

        $stop;

    end

endmodule

​ 这里将按键的仿真模型加仿真脚本后进行功能仿真就可以看到如图 3 所示的波形文件。每当写入地址加一时数据均可以有效的写入,按键按下后每当一次输出结束后读地址也进行加一,实现数据输出。

image-20240408154612957

图3——功能仿真波形文件

​ 放大波形文件的数据发送部分,如图 4 所示。可看出在第一次给出写使能信号后, 将数据 aa 写入地址 0,写入成功后写入地址加一。tx_data 之所以会在没有 Send_en 时数据会更新,是因为读取地址在复位时地址初始值为 0,在成功写入数据 aa 后,自然就会显示更新,但是 Rs232_Tx 不会有数据更新。延迟两个系统时钟周期是因为 RAM IP 本身的特性。 可以放大后面数据写入的过程,tx_data 的值并不会更新。

image-20240408154419781

图4——数据接收及写入部分波形

​ 放大波形文件的发送部分,如图 5 和图 6 所示。可看出每当 Send_en 有效后均 会进行一字节数据的发送。传输完激励中的四个数据后即输出 0,这是由于这里将 RAM 定 为 256 宽度,只用的前四个,没有用到的数据即全为 0。且可以看出 Send_en 与数据发送严 格对齐,与设计预期相符。

image-20240408155213658

图5——数据读取及发送部分波形

image-20240408155241739

图6——数据读取及发送部分波形

​ 以上,仿真部分的验证工作就顺利完成。

7、板级验证

​ 经过上述工作,所有代码都已经设计完毕,接下来介绍板级测试和验证方法:

​ (1)按照 AC620/AC6102/AC609 开发板的引脚分配表分配正确的引脚, 本设计实例针 对各开发板的管脚分配如下表所示:

image-20240408185438625

(2)分配引脚后进行全编译无误后,下载进开发板,打开串口助手依次输入 11、22、aa、 dd、34、67。按下按键 S0 后即可看见数据源源不断的发送到串口上位机软件,再次按下即 停止。且可以计算得每一个循环的数据量自动补 0 到 256 个字节,与设计的 RAM 存储量相 符合,如图 7 所示

image-20240408185505575

图7——串口助手接收数据显示

(3)按下 FPGA 复位申请并发送新的数据 68、76、ff,可以看出,RAM 中已有的数据并 不会消除,只是会加入新的数据,且一个循环数据量也是自动补 0 到 256 字节,如图8所示。

image-20240408185553337

图8——串口助手接收数据显示

标签:双口,Tx,Clk,FPGA,Rx,Send,Done,串口,Rst
From: https://www.cnblogs.com/little55/p/18122329

相关文章

  • 基于FPGA的数据采集、编码、通讯和存储系统设计(即FPGA+RTL8211千兆以太网+SD卡存储+RT
    介绍一个小项目,加强对FPGA相关接口的整体把握。硬件及软件代码梳理:硬件系统的主要功能框图,其中FPGA作为处理单元,实现了包括电流和电压的采集、千兆以太网通讯、SD卡本地数据存储和串口通讯等。已经过板级测试,测试包含:千兆网通讯收发测试、AD采集的数据验证、SD卡存储验证......
  • 【FPGA基础】Latch基础
    “本文主要转载自https://zhuanlan.zhihu.com/p/93133247,高屋建瓴。其中关于如何会产生latch的部分,转载自https://zhuanlan.zhihu.com/p/34408492?from_voters_page=true。”在FPGA的设计中,避免使用锁存器是几乎所有FPGA工程师的共识,Xilinx和Altera也在手册中提示大家要慎用......
  • PS端通过串口输出helloword
    创建工程:创建一个RTL工程:选择器件:而我用的zedboard的SOC型号:XC7Z020CLG484-1创建PS工程:第1步:对设计进行命名:等待一会后出现如下的界面:第2步:选择sources第3步:添加IP:第4步:搜索ZYNQ的PS部分的IP核:等待一段时间后此界面的放缩方法:①Ctrl+鼠标滚轮②左键......
  • 单芯片国产ARM+FPGA,复旦微FMQL20SM工业核心板正式发布!
      ......
  • 基于EP4CE6F17C8的FPGA矩阵键盘实例
    一、电路模块1、数码管开发板板载了6个数码管,全部为共阳型,原理图如下图所示,段码端引脚为DIG[0]~DIG[7]共8位(包含小数点),位选端引脚为SEL[0]~SEL[5]共6位。端口均为低电平有效。其实物图如下所示。数码管引脚分配见下表。2、时钟晶振开发板板载了一个50MHz的有源晶振,为系统......
  • FPGA入门笔记011_A——嵌入式块RAM的使用
    1、Cyclone-II系列FPGA内部结构图1——Altera公司Cyclone-II系列FPGA内部结构​ 如上图所示是Altera公司Cyclone-II系列FPGA内部结构,个模块作用如下:​ PLL锁相环—对时钟进行管理。​ IOEs—管脚单元,配置管脚,设置输入输出。​ 逻辑阵列—实现组合、时序逻辑。​ 块RAM......
  • 基于EP4CE6F17C8的FPGA可调校数码管时钟实例
    一、电路模块1、数码管开发板板载了6个数码管,全部为共阳型,原理图如下图所示,段码端引脚为DIG[0]~DIG[7]共8位(包含小数点),位选端引脚为SEL[0]~SEL[5]共6位。端口均为低电平有效。其实物图如下所示。数码管引脚分配见下表。2、时钟晶振开发板板载了一个50MHz的有源晶振,为系统......
  • 如何读懂串口通讯波形图(TTL,RS232,UART)
    考考你,这是什么字符串,提示串口通讯,8N1 ......
  • 基于EP4CE6F17C8的FPGA键控灯实例
    一、电路模块1、LED开发板板载了4个用户LED发光二极管,其原理图如下所示,当FPGA的引脚输出为逻辑0时,LED会熄灭。输出为逻辑1时,LED被点亮。其实物图如下所示。LED的引脚分配见下表。2、时钟晶振开发板板载了一个50MHz的有源晶振,为系统提供时钟。其实物图如下所示。时......
  • STM32学习(六)USART串口协议
    一、通信接口1.通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统。2.通信协议:制定通信的规则,通信双方按照协议规则进行数据收发。STMF103C8T6支持以下通信协议 如果需要蓝牙无线遥控功能,陀螺仪加速度计测量姿态的功能,STM32没有,只能通过外挂芯片完成,这时就需要将......