FPGA学习历程(七):命令解析模块与读写FIFO

以下内容学习自开源骚客教程: SDRAM那些事儿第一季。至此,SDRAM那些事儿第一季教程 —— 小结 的各模块设计细节的修缮与补完已经全部完成。

一、命令解析模块

1.1 模块时序图

  整完 SDRAM 再来整这个可太简单了(虽然 SDRAM 仔细理解以后也没觉得特别复杂):
在这里插入图片描述

1.2 模块源码

1.2.1 cmd_decode.v

module cmd_decode
(
    // system signals
    input 				    sclk	    ,       // 板载系统时钟 50MHz
	input                   s_rst_n     ,       // 复位信号,低电平有效
    
    // from UART_RX module
    input                   uart_flag   ,
    input           [7:0]   uart_data   ,
    
    //
    output  wire            wr_trig     ,
    output  wire            rd_trig     ,
    output  wire            wfifo_wr_en ,
    output  wire    [7:0]   wfifo_data          // 可有可无
);

/**************************************************************************/
/***************** Define Parameter and Internal Signals ******************/
/**************************************************************************/
localparam      REC_NUM_END     =   'd4 ;

reg     [2:0]               rec_num     ;
reg     [7:0]               cmd_reg     ;

/**************************************************************************/
/******************************* Main Code ********************************/
/**************************************************************************/
// rec_num
always  @(posedge sclk or negedge s_rst_n)
begin
    if(s_rst_n == 1'b0)
        rec_num <=  'd0;
    else if(uart_flag == 1'b1 && rec_num == 'd0 && uart_data == 8'haa)
        rec_num <=  'd0;
    else if(uart_flag == 1'b1 && rec_num == REC_NUM_END)
        rec_num <=  'd0;
    else if(uart_flag == 1'b1)
        rec_num <= rec_num + 1'b1;
end

// cmd_reg
always  @(posedge sclk or negedge s_rst_n)
begin
    if(s_rst_n == 1'b0)
        cmd_reg <= 8'h00;
    else if(rec_num <= 'd0 && uart_flag == 1'b1)
        cmd_reg <= uart_data;
end

assign wr_trig  =   (rec_num == REC_NUM_END && cmd_reg == 8'h55) ? uart_flag : 1'b0;
assign rd_trig  =   (rec_num == 'd0 && uart_data == 8'haa) ? uart_flag : 1'b0;
assign wfifo_wr_en  =   (rec_num >= 'd1) ? uart_flag : 1'b0;
assign wfifo_data  =   uart_data;

endmodule 

1.2.2 tb_cmd_decode.v

`timescale 1ns/1ns

module tb_cmd_decode;

reg             sclk;
reg             s_rst_n;

reg             uart_flag;
reg     [7:0]   uart_data;

wire            wr_trig;
wire            rd_trig;
wire            wfifo_wr_en;
wire    [7:0]   wfifo_data;

initial begin
    sclk = 1;
    s_rst_n <= 0;
    #100                    // 延时 100ns
    s_rst_n <= 1;           // 释放复位信号
end

always  #5  sclk = ~sclk;   // 模拟系统时钟,延时 5ns 翻转一次,10ns 一个周期

initial
begin
    uart_flag <= 0;
    uart_data <= 0;
    
    #200
    uart_flag <= 1;
    uart_data <= 8'h55;     // 写命令
    #10
    uart_flag <= 0;
    
    #200
    uart_flag <= 1;
    uart_data <= 8'h12;
    #10
    uart_flag <= 0;
    
    #200
    uart_flag <= 1;
    uart_data <= 8'h34;
    #10
    uart_flag <= 0;
    
    #200
    uart_flag <= 1;
    uart_data <= 8'h56;
    #10
    uart_flag <= 0;
    
    #200
    uart_flag <= 1;
    uart_data <= 8'h78;
    #10
    uart_flag <= 0;
    
    #200
    uart_flag <= 1;
    uart_data <= 8'haa;     // 读命令
    #10
    uart_flag <= 0;
end

cmd_decode cmd_decode_inst
(
    // system signals
    .sclk	        (sclk),         // 板载系统时钟 50MHz
	.s_rst_n        (s_rst_n),      // 复位信号,低电平有效
    
    // from UART_RX module
    .uart_flag      (uart_flag),
    .uart_data      (uart_data),
    
    //
    .wr_trig        (wr_trig),
    .rd_trig        (rd_trig),
    .wfifo_wr_en    (wfifo_wr_en),
    .wfifo_data     (wfifo_data)    // 可有可无
);

endmodule 

1.3 Modelsim仿真

  改一改 run.do 文件:

##create work library
vlib work

vlog	"./tb_cmd_decode.v"
vlog	"../../rtl/cmd_decode.v"

vsim	-voptargs=+acc work.tb_cmd_decode

# Set the window types
view wave
view structure
view signals

add wave -divider {tb_cmd_decode}
add wave tb_cmd_decode/*
add wave -divider {cmd_decode}
add wave tb_cmd_decode/cmd_decode_inst/*
.main clear

run 10us

  仿真波形如下图:
在这里插入图片描述

二、读写FIFO与整体仿真

2.1 模块时序图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2 使用FIFO IP核

  打开 Quartus II 软件,选择菜单栏 Tools ⟶ \longrightarrow MegaWizard Plug-In Manager:
在这里插入图片描述
  检索要生成的 IP 核,并指定存放位置,之后会进入到 IP 核属性的配置页面:
在这里插入图片描述
  第一页,右侧配置 FIFO 的深度和数据位宽,中部可选择读写是否公用时钟信号:
在这里插入图片描述
  第二页可以选择是否使用一些标志位和信息,比如满标志、空标志以及 FIFO 已经存储的数据数量:
在这里插入图片描述
  最后一页可以选择生成例化模板文件:
在这里插入图片描述
  之后就可以在设计中例化 FIFO 模块了。

2.3 Modelsim仿真

因为最终源码占用的篇幅太大,而且 SDRAM 那些事儿第一季教程里设计出来的 SDRAM 控制器本身实际上并不完善(属于定制型而非 IP 核式的),所以博主最后权衡许久,还是感觉贴上一堆让人不明所以的改动后源码不如仔细分析一次整体的仿真时序图更有实际价值一些。

2.3.1 uart_rx 与 cmd_decode

在这里插入图片描述
  这两个模块之间的连接非常顺利,uart_rx 模块接收到的数据和指令在 cmd_decode 模块里成功实现了分离与解析(8’h55 后计入 4 个数据触发写入,8’haa触发读出)。

2.3.2 sdram_write 与 sdram_top

在这里插入图片描述
  接收到 cmd_decode 模块发送的写入信号,sdram_write 模块进行正常的写操作(要想正确地将数据写入 SDRAM,必须保证 wfifo 的读使能、读数据和 SDRAM 的写数据、列地址信号对齐)。
在这里插入图片描述
  写完以后 sdram_write 模块的状态机恢复到 IDLE 状态,sdram_top 模块下的状态机恢复到 ARBIT 状态。

2.3.3 sdram_read 与 sdram_top

在这里插入图片描述
  写触发信号之后紧接的就是读触发信号。sdram_read 模块的工作机制与 sdram_write 模块完全相同,只是这里需要额外注意 rfifo 的写使能信号与 SDRAM 的读数据对齐(如果没有对齐,强行延拍对齐也可以,毕竟 CL = 3 是硬性限制)。
在这里插入图片描述
  同样在读完以后 sdram_write 模块的状态机恢复到 IDLE 状态,sdram_top 模块下的状态机恢复到 ARBIT 状态。

2.3.4 uart_tx

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  uart_tx 模块调整的幅度稍大一些,也是因为数据对齐的问题。主要是需要保证每次 rfifo 的读使能只从 rfifo 里读出一个数据,发完再读下一个数据。

2.3.5 Modelsim调试栏输出

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值