【逻辑学习笔记】uart_tx模块-改进

改进说明:

最早的第一版:使用baud_clk,没有FIFO

第二版:使用FIFO,在FIFO的wr_clk是用户的高速时钟,FIFO的rd_clk是uart_tx_engine的低速baud_clk。

功能

用户端接口为FIFO接口,用户将要发送数据写入FIFO,该UART_TX模块将并行数据转成串行数据,依次发出。串口的“串”字,就是这个意思,并转串,通过一根信号线把数据发送出去

需求分析

关于FIFO

  • FIFO的位宽:是否可以让用户配置,8bit是默认的,如果可以让用户可配置,有时候32bit可能用起来更方便
  • FIFO的深度(容量):考虑到常见应用场景:
    1. 打印:一次打印一句话,20个单词,约140个字母char(8bit),深度可以选择256,容量为256×8bit;如果考虑到一次打印句子较多(如3句话)或一句较长,容量可以设置为1024×8bit。
    2. 通信:跟上层应用关系较大,可以让用户端等,深度无特别要求
  • 两边的时钟:FIFO写端口,为用户端口,写时钟为高速的用户时钟,如50MHz;FIFO读端口,为模块内部端口,读时钟为低速的UART处理时钟,即波特率的1倍时钟(发送端不需要16倍)
  • FWFT:让read latency=0

关于架构

  • uart_tx为顶层
  • 底层由FIFO和uart_tx_engine组成

接口

信号名

方向

含义

reset

input

模块复位信号

fifo_wr_clk

input

用户端接口时钟

fifo_din

Input

用户端要发送并行数据

fifo_wr_en

Input

用户端写使能

fifo_full

output

用户端FIFO满标志

fifo_wr_ack

output

用户端写响应状态

fifo_overflow

output

用户端FIFO写溢出标志

baud_clk

input

发送波特率的时钟

uart_txd

output

发送的串行数据

实现思路

uart_tx_engine设计

  • 功能:若现在空闲,就判断FIFO状态,若不空,就取一个字节,开始发送;发送完再去判断FIFO状态
  • 设计要点:
    1. 由于没有分支,可以不用状态机,而采用可控线性序列机(计数器控制)
    2. 时钟采用波特率时钟,这个时钟由外部传递,系统顶层通过mmcm产生,节省整个系统的资源,由于波特率速率太低,低于mmcm的最低输出频率(约4M),需要自己写时钟分频的逻辑

 

调试过程中总结

  • 整个的思路可以先用时序图画出来,这样思路很清晰
  • 写代码时,对于一些判断条件可以先不用想的特别清楚,先写出来,仿真时,自然就能发现问题,然后再修改,这样效率高一些
  • 善于用计数器控制状态

调试中已解决问题的总结:

  • 为什么我的FIFO复位信号不起作用?——因为我给FIFO复位时,FIFO的一个端口时钟也被复位了。FIFO复位时,必须时钟要有,所以逻辑设计时,整个逻辑的时钟(比如用clk_wiz)是不能复位的
  • 为什么FIFO的full一直是1?——因为复位的后,不能马上使用FIFO,有一个间隔(NO ACCESS ZONE),手册pg057是说是复位宽度+60个最慢的时钟周期——这里需要实测一下
  • FIFO的复位信号宽度多长才是有效的?——对于异步复位(两边时钟不同时就是异步复位),需要至少3个最慢时钟周期

 

目前模块问题

reset信号不好用

  • 原因:因为reset要同时复位FIFO,FIFO的复位需要至少3个最慢时钟周期,对于波特率来说这样很长;而且复位后需要等6-7个波特率时钟周期,FIFO才能使用,这个时间也太长了。
  • 解决思路:
    1. 提高FIFO_RD_CLK频率——这样对现在代码结构改动比较大;
    2. FIFO复位只在上电/重新加载/重大错误时进行,复位时间长一些——这也是佐证为什么上电加载好后,很多模块需要很久才能ready的原因,如mmcm需要ms级别。

### 调用 UART_RX 和 UART_TX模块的方法 在 Verilog 或 VHDL 中设计顶层模块并调用 `UART_RX` 和 `UART_TX` 两个子模块时,需要遵循硬件描述语言的标准接口定义方式。以下是两种语言的具体实现方法。 #### Verilog 实现 在 Verilog 中,通过实例化的方式将子模块嵌入到顶层模块中。假设 `UART_RX` 接收数据,而 `UART_TX` 发送数据,则可以按照如下方式进行顶层设计: ```verilog module top_module ( input wire clk, // 系统时钟 input wire rst_n, // 复位信号 (低电平有效) inout wire rx_data_pin, // RX 数据引脚 inout wire tx_data_pin, // TX 数据引脚 output reg [7:0] received_data, output reg data_ready, input wire send_enable, input wire [7:0] transmit_data ); // 定义内部信号 wire uart_rx_done; wire [7:0] uart_received_data; // 实例化 UART_RX 模块 uart_rx u_uart_rx ( .clk(clk), .rst_n(rst_n), .rx(rx_data_pin), // 连接到外部 RX 引脚 .data_out(uart_received_data), .done(uart_rx_done) // 表明接收完成 ); always @(posedge clk or negedge rst_n) begin if (!rst_n) begin received_data <= 8'b0; // 初始化接收到的数据 data_ready <= 1'b0; // 初始状态无新数据准备好 end else if (uart_rx_done) begin received_data <= uart_received_data; // 更新接收到的数据 data_ready <= 1'b1; // 设置标志表示有新的数据可用 end else begin data_ready <= 1'b0; // 清除标志 end end // 实例化 UART_TX 模块 uart_tx u_uart_tx ( .clk(clk), .rst_n(rst_n), .tx(tx_data_pin), // 连接到外部 TX 引脚 .send_en(send_enable), // 控制发送使能 .data_in(transmit_data) // 待发送的数据输入 ); endmodule ``` 上述代码展示了如何在顶层模块中实例化 `UART_RX` 和 `UART_TX` 并连接它们的端口[^1]。 #### VHDL 实现 对于 VHDL 的情况,可以通过组件声明和映射来实现相同的功能。以下是一个可能的设计结构: ```vhdl library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity Top_Module is Port ( clk : in std_logic; -- 系统时钟 rst_n : in std_logic; -- 复位信号 (低电平有效) rx_data_pin : inout std_logic; -- RX 数据引脚 tx_data_pin : inout std_logic; -- TX 数据引脚 received_data : out std_logic_vector(7 downto 0); data_ready : out std_logic; send_enable : in std_logic; transmit_data : in std_logic_vector(7 downto 0) ); end Top_Module; architecture Behavioral of Top_Module is signal uart_rx_done : std_logic := '0'; signal uart_received_data : std_logic_vector(7 downto 0) := (others => '0'); begin -- 组件声明:UART_RX U_UART_RX : entity work.UART_RX port map( clk => clk, rst_n => rst_n, rx => rx_data_pin, -- 连接到外部 RX 引脚 data_out => uart_received_data, done => uart_rx_done -- 表明接收完成 ); process(clk, rst_n) begin if (rst_n = '0') then received_data <= (others => '0'); -- 初始化接收到的数据 data_ready <= '0'; -- 初始状态无新数据准备好 elsif rising_edge(clk) then if (uart_rx_done = '1') then received_data <= uart_received_data; -- 更新接收到的数据 data_ready <= '1'; -- 设置标志表示有新的数据可用 else data_ready <= '0'; -- 清除标志 end if; end if; end process; -- 组件声明:UART_TX U_UART_TX : entity work.UART_TX port map( clk => clk, rst_n => rst_n, tx => tx_data_pin, -- 连接到外部 TX 引脚 send_en => send_enable, -- 控制发送使能 data_in => transmit_data -- 待发送的数据输入 ); end Behavioral; ``` 此代码片段说明了如何利用 VHDL 的实体和架构机制,在顶层模块中集成 `UART_RX` 和 `UART_TX` 子模块,并处理其交互逻辑[^2]。 #### 注意事项 - **同步复位**:确保所有寄存器都具有相同的复位行为。 - **时序约束**:根据目标 FPGA/ASIC 设备的要求设置合适的时钟频率及时序参数。 - **测试平台验证**:建议开发配套的仿真测试平台以验证功能正确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值