目录
前面已经对SDRAM原理及控制器进行了讲解,以及仿真,下边将进行板级验证。
一、验证方法
使用的是小梅哥的AC620开发板,验证是使用按键将设定的8组16位数据存储到SDRAM中,随后将SDRAM中数据存储到FIFO中,当FIFO不为空时,再将数据通过串口显示到电脑端,查看接收到的数据是否与所发的数据一致。
验证这里不再做详细的说明,供学习参考。
二、实现
主要有以下几个模块:PLL时钟倍频模块、按键消抖模块、同步FIFO模块、SDRAM控制器模块和串口发送模块。
由于SDRAM控制器采用的是100MHz时钟频率,为了同步时钟,将其他模块和其他代码都使用100MHz,有些模块使用前面博客写的,前面写的是用50MHz频率,这里需要略做修改。直接看代码:
1、同步FIFO模块
//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 PHF的CSDN
//File name: syn_fifo.v
//Last modified Date: 2020/5/18
//Last Version:
//Descriptions: Synchronize fifo 12*100(位宽*深度)
//-------------------------------------------------------------------
module sync_fifo
#(parameter WIDTH = 12, //缓存的数据宽度
parameter DEPTH = 100, //缓存的数据深度
parameter MAX_DEPTH_BIT = 7) //可设置的最大深度位数7,即最大深度为2^7-1
(
input clk , //系统时钟50MHz
input rst_n , //系统复位
input wrreq , //写使能
input [ WIDTH-1: 0] data , //写数据
input rdreq , //读使能
output reg [ WIDTH-1: 0] q , //读数据
output empty , //空信号
output full , //满信号
output half , //半满信号
output reg [MAX_DEPTH_BIT-1:0] usedw //fifo中剩余数据个数
);
reg [ WIDTH-1: 0] fifo_rom [ DEPTH-1: 0]; //fifo存储单元
reg [ MAX_DEPTH_BIT-1: 0] wr_p ; //写指针
reg [ MAX_DEPTH_BIT-1: 0] rd_p ; //读指针
//当写使能且数据不满时写入数据
always @(posedge clk )begin
if(wrreq && !full) begin
fifo_rom[wr_p] <= data;
end
else begin
fifo_rom[wr_p] <= fifo_rom[wr_p];
end
end
//写指针,当写使能且数据不满时,指针加一;当指针指向最大深度且有写使能时指针归零
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
wr_p <= 0;
end
else if(wrreq && !full) begin
if(wr_p==DEPTH-1 && wrreq)
wr_p <= 0;
else
wr_p <= wr_p + 1'b1;
end
end
//读指针,,当读使能且数据不空时,指针加一,当指针指向最大深度且有读使能时指针归零
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rd_p <= 0;
end
else if(rdreq && !empty) begin
if(rd_p==DEPTH-1 && rdreq)
rd_p <= 0;
else
rd_p <= rd_p + 1'b1;
end
end
//读出数据,在读使能且数据不为空时读出数据
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
q <= 0;
end
else if(rdreq && !empty) begin
q <= fifo_rom[rd_p];
end
end
//fifo中数据个数
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
usedw <= 0;
end
else if((wrreq && !full && !rdreq) || (wrreq && rdreq && empty)) begin
usedw <= usedw + 1'b1;
end
else if(!wrreq && !empty && rdreq)begin
usedw <= usedw - 1'b1;
end
end
//满、空、半满标志
assign full = usedw==DEPTH;
assign empty = usedw==0;
assign half = usedw==DEPTH/2;
endmodule
2、按键消抖模块
// Company :
// Engineer :
// -----------------------------------------------------------------------------
// https://blog.csdn.net/qq_33231534 PHF's CSDN blog
// -----------------------------------------------------------------------------
// Create Date : 2020-09-04 15:16:47
// Revise Data : 2020-09-04 15:17:04
// File Name : key_filter.v
// Target Devices : XC7Z015-CLG485-2
// Tool Versions : Vivado 2019.2
// Revision : V1.1
// Editor : sublime text3, tab size (4)
// Description : 按键消抖模块
module key_filter(
input clk ,//系统时钟100MHz
input rst_n ,//系统复位
input key_in ,//按键输入,低为按下
output reg key_flag ,//输出一个脉冲按键有效信号
output reg key_state //输出按键状态,1为未按下,0为按下
);
localparam IDLE = 4'b0001 ;//空闲状态,读取按键按下的下降沿,读取到下降沿转到下一个状态
localparam FILTER1 = 4'b0010 ;//计数20ms状态,计数结束转到下一个状态
localparam STABLE = 4'b0100 ;//数据稳定状态,等待按键松开上升沿,读取到上升沿转到下一个状态
localparam FILTER2 = 4'b1000 ;//计数20ms状态,计数结束转到空闲状态
parameter TIME_20MS = 21'd2000_000 ;
reg [ 3: 0] state_c ;//寄存器改变状态
reg [ 3: 0] state_n ;//现在状态
wire IDLE_to_FILTER1 ;//IDLE状态转到FILTER1状态条件
wire FILTER1_to_STABLE;//FILTER1状态转到STABLE状态条件
wire STABLE_to_FILTER2;//STABLE状态转到FILTER2状态条件
wire FILTER2_to_IDLE ;//FILTER2状态转到IDLE状态条件
reg key_in_ff0 ;
reg key_in_ff1 ;
reg key_in_ff2 ;
wire key_in_pos ;//检测上升沿标志
wire key_in_neg ;//检测下降沿标志
reg [ 20: 0] cnt ;
wire add_cnt ;
wire end_cnt ;
//状态机第一段,状态转换
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
//状态机第二段,状态转换条件
always @(*)begin
case(state_c)
IDLE :begin
if(IDLE_to_FILTER1)
state_n = FILTER1;
else
state_n = state_c;
end
FILTER1:begin
if(FILTER1_to_STABLE)
state_n = STABLE;
else
state_n = state_c;
end
STABLE :begin
if(STABLE_to_FILTER2)
state_n = FILTER2;
else
state_n = state_c;
end
FILTER2:begin
if(FILTER2_to_IDLE)
state_n = IDLE;
else
state_n = state_c;
end
default:state_n = IDLE;
endcase
end
//状态转换条件
assign IDLE_to_FILTER1 = key_in_neg ;
assign FILTER1_to_STABLE = state_c==FILTER1 && end_cnt;
assign STABLE_to_FILTER2 = key_in_pos ;
assign FILTER2_to_IDLE = state_c==FILTER2 && end_cnt;
//打两拍,防止亚稳态
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_in_ff0 <= 1;
key_in_ff1 <= 1;
key_in_ff2 <= 1;
end
else begin
key_in_ff0 <= key_in;
key_in_ff1 <= key_in_ff0;
key_in_ff2 <= key_in_ff1;
end
end
//下降沿和上升沿检测
assign key_in_pos = (state_c==STABLE) ?(key_in_ff1 && !key_in_ff2):1'b0;
assign key_in_neg = (state_c==IDLE) ?(!key_in_ff1 && key_in_ff2):1'b0;
//计数20ms
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1'b1;
end
else begin
cnt <= 0;
end
end
assign add_cnt = state_c==FILTER1 || state_c==FILTER2;
assign end_cnt = add_cnt && cnt== TIME_20MS-1;
//key_flag按键按下输出一个脉冲信号
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_flag <= 0;
end
else if(state_c==FILTER1 && end_cnt) begin
key_flag <= 1;
end
else begin
key_flag <= 0;
end
end
//key_state按键按下状态信号
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_state <= 1;
end
else if(state_c==STABLE || state_c==FILTER2) begin
key_state <= 0;
end
else begin
key_state <= 1;
end
end
endmodule
3、串口发送模块
// Company :
// Engineer :
// -----------------------------------------------------------------------------
// https://blog.csdn.net/qq_33231534 PHF's CSDN blog
// -----------------------------------------------------------------------------
// Create Date : 2020-07-31 11:19:07
// Revise Data : 2020-07-31 11:19:07
// File Name : uart_tx.v
// Target Devices : XC7Z015-CLG485-2
// Tool Versions : Vivado 2019.2
// Revision : V1.1
// Editor : sublime text3, tab size (4)
// Description : uart串口发送模块,可设置波特率(4800bps/9600bps/19200bps/38400bps/57600bps/115200bps)
module uart_tx(
input clk ,//100MHz
input rst_n ,
input send_en ,
input [7:0] send_data ,
input [2:0] baud_set ,
output reg tx_data ,
output reg tx_done ,
output reg uart_state
);
parameter SEND_DATA_NUM = 10; //send data numbers
reg [14:0] baud_cnt ;
reg [14:0] cnt_1bit ;
wire add_cnt_1bit;
wire end_cnt_1bit;
reg [3:0] cnt_8bit ;
wire add_cnt_8bit;
wire end_cnt_8bit;
reg [7:0] send_data_f ;
wire [9:0] data_out;
always @(*)begin
case(baud_set)
3'd0:baud_cnt = 15'd20833 ; //4800bps
3'd1:baud_cnt = 15'd10416 ; //9600bps
3'd2:baud_cnt = 15'd5208 ; //19200bps
3'd3:baud_cnt = 15'd2604 ; //38400bps
3'd4:baud_cnt = 15'd1736 ; //57600bps
3'd5:baud_cnt = 15'd868 ; //115200bps
default:baud_cnt = 15'd868 ; //default 115200bps
endcase
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
uart_state <= 0;
end
else if (send_en) begin
uart_state <= 1;
end
else if (tx_done) begin
uart_state <= 0;
end
else begin
uart_state <= uart_state;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_1bit <= 0;
end
else if (add_cnt_1bit) begin
if (end_cnt_1bit) begin
cnt_1bit <= 0;
end
else begin
cnt_1bit <= cnt_1bit + 1'b1;
end
end
else begin
cnt_1bit <= 0;
end
end
assign add_cnt_1bit = uart_state==1;
assign end_cnt_1bit = add_cnt_1bit && cnt_1bit==baud_cnt-1;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_8bit <= 0;
end
else if (add_cnt_8bit) begin
if (end_cnt_8bit) begin
cnt_8bit <= 0;
end
else begin
cnt_8bit <= cnt_8bit + 1'b1;
end
end
end
assign add_cnt_8bit = end_cnt_1bit;
assign end_cnt_8bit = add_cnt_8bit && cnt_8bit==SEND_DATA_NUM-1;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
send_data_f <= 0;
end
else if (send_en) begin
send_data_f <= send_data;
end
else begin
send_data_f <= send_data_f;
end
end
assign data_out = {1'b1,send_data_f,1'b0};
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
tx_data <= 1;
end
else if (uart_state==1 && cnt_1bit==0) begin
tx_data <= data_out[cnt_8bit];
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
tx_done <= 0;
end
else if (uart_state==1 && end_cnt_8bit) begin
tx_done <= 1;
end
else begin
tx_done <= 0;
end
end
endmodule
4、SDRAM控制器模块
为了文章完整性,把他放上来。
// Company :
// Engineer :
// -----------------------------------------------------------------------------
// https://blog.csdn.net/qq_33231534 PHF's CSDN blog
// -----------------------------------------------------------------------------
// Create Date : 2020-09-18 14:20:22
// Revise Data : 2020-09-20 15:30:16
// File Name : SDRAM_control.v
// Target Devices : XC7Z015-CLG485-2
// Tool Versions : Vivado 2019.2
// Revision : V1.1
// Editor : sublime text3, tab size (4)
// Description : SDRAM控制器,支持读写冲突长度为8,cas为3
module SDRAM_control(
input clk ,//100MHZ
input rst_n ,//复位
input wr_en ,//写使能信号
input [15:0] wr_data ,//写数据
input rd_en ,//读使能信号
input [1:0] bank_addr ,//bank地址
input [11:0] row_addr ,//行地址
input [8:0] col_addr ,//列地址
output reg [15:0] rd_data ,//读出的数据
output reg rd_data_vld ,//读出数据有效位
output wire wr_data_vld ,//写入数据有效位
output wire wdata_done ,//写数据结束标志
output wire rdata_done ,//读数据结束标志
output wire sdram_clk ,//SDRAM时钟信号
output reg [3:0] sdram_commond ,//{cs,ras,cas,we}
output wire sdram_cke ,//时钟使能信号
output reg [1:0] sdram_dqm ,//数据线屏蔽信号
output reg [11:0] sdram_addr ,//SDRAM地址线
output reg [1:0] sdram_bank ,//SDRAM bank选取
inout wire[15:0] sdram_dq //SDRAM数据输出输入总线
);
//延时
localparam TWAIT_200us = 15'd20000 ;//上电等待时间
localparam TRP = 2'd3 ;//预充电周期
localparam TRC = 4'd10 ;//自刷新周期
localparam TRSC = 2'd3 ;//加载模式寄存器周期
localparam TRCD = 2'd2 ;//激活命令周期
localparam TREAD_11 = 4'd11 ;//burst=8,cas=3
localparam TWRITE_8 = 4'd8 ;//burst=8
localparam AUTO_REF_TIME= 11'd1562 ;
//状态
localparam NOP = 3'd0 ;
localparam PRECHARGE = 3'd1 ;
localparam REF = 3'd2 ;
localparam MODE = 3'd3 ;
localparam IDLE = 3'd4 ;
localparam ACTIVE = 3'd5 ;
localparam WRITE = 3'd6 ;
localparam READ = 3'd7 ;
//操作命令
localparam NOP_CMD = 4'b0111 ;
localparam PRECHARGE_CMD= 4'b0010 ;
localparam REF_CMD = 4'b0001 ;
localparam MODE_CMD = 4'b0000 ;
localparam ACTIVE_CMD = 4'b0011 ;
localparam WRITE_CMD = 4'b0100 ;
localparam READ_CMD = 4'b0101 ;
//初始化阶段地址线
localparam ALL_BANK = 12'b01_0_00_000_0_000;//预充电地址线
localparam MODE_CONFIG = 12'b00_0_00_011_0_011;//配置模式寄存器时地址线
wire nop_to_pre_start ;
wire pre_to_ref_start ;
wire pre_to_idle_start ;
wire ref_to_mode_start ;
wire ref_to_idle_start ;
wire ref_to_ref_start ;
wire mode_to_idle_start ;
wire idle_to_active_start ;
wire idle_to_ref_start ;
wire active_to_write_start ;
wire active_to_read_start ;
wire write_to_pre_start ;
wire read_to_pre_start ;
reg [2:0] state_c ;
reg [2:0] state_n ;
wire sdram_dq_en ;
wire[15:0] sdram_dq_r ;
reg rd_data_vld_ff0 ;
reg rd_data_vld_ff1 ;
reg rd_data_vld_ff2 ;
reg rd_data_vld_ff3 ;
reg [10:0] auto_ref_cnt ;
wire add_auto_ref_cnt;
wire end_auto_ref_cnt;
reg ref_req ;
wire init_done ;
reg init_flag ;
reg [14:0] cnt0 ;
wire add_cnt0 ;
wire end_cnt0 ;
reg [14:0] x ;
reg [3:0] ref_cnt1 ;
wire add_cnt1 ;
wire end_cnt1 ;
reg flag_rd ;
reg flag_wr ;
assign sdram_clk = ~clk;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state_c <= NOP;
end
else begin
state_c <= state_n;
end
end
always @(*)begin
case(state_c)
NOP :begin
if (nop_to_pre_start) begin
state_n = PRECHARGE;
end
else begin
state_n = state_c;
end
end
PRECHARGE:begin
if (pre_to_ref_start) begin
state_n = REF;
end
else if (pre_to_idle_start) begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
REF:begin
if (ref_to_mode_start) begin
state_n = MODE;
end
else if (ref_to_idle_start) begin
state_n = IDLE;
end
else if (ref_to_ref_start) begin
state_n = REF;
end
else begin
state_n = state_c;
end
end
MODE:begin
if (mode_to_idle_start) begin
state_n = IDLE;
end
else begin
state_n =state_c;
end
end
IDLE:begin
if (idle_to_active_start) begin
state_n = ACTIVE;
end
else if (idle_to_ref_start) begin
state_n = REF;
end
else begin
state_n = state_c;
end
end
ACTIVE:begin
if (active_to_write_start) begin
state_n = WRITE;
end
else if (active_to_read_start) begin
state_n = READ;
end
else begin
state_n = state_c;
end
end
WRITE:begin
if(write_to_pre_start)begin
state_n = PRECHARGE;
end
else begin
state_n = state_c;
end
end
READ:begin
if (read_to_pre_start) begin
state_n = PRECHARGE;
end
else begin
state_n = state_c;
end
end
default:state_n = IDLE;
endcase
end
assign nop_to_pre_start = (state_c==NOP && end_cnt0);
assign pre_to_ref_start = (state_c==PRECHARGE && end_cnt0 && init_flag==1);
assign pre_to_idle_start = (state_c==PRECHARGE && end_cnt0 && init_flag==0);
assign ref_to_mode_start = (state_c==REF && init_flag==1 && end_cnt1);
assign ref_to_idle_start = (state_c==REF && init_flag==0 && end_cnt0);
assign ref_to_ref_start = (state_c==REF && init_flag==1 && end_cnt0 && ref_cnt1<7);
assign mode_to_idle_start = (state_c==MODE && init_flag==1 && end_cnt0);
assign idle_to_active_start = (state_c==IDLE && (wr_en || rd_en) && ref_req==0);
assign idle_to_ref_start = (state_c==IDLE && ref_req==1);
assign active_to_write_start = (state_c==ACTIVE && end_cnt0 && flag_wr);
assign active_to_read_start = (state_c==ACTIVE && end_cnt0 && flag_rd);
assign write_to_pre_start = (state_c==WRITE && end_cnt0);
assign read_to_pre_start = (state_c==READ && end_cnt0);
//命令控制字 sdram_commond = {CS,RAS,CAS,WE};
always @(posedge clk or negedge rst_n)begin
if (!rst_n) begin
sdram_commond <= NOP_CMD;
end
else if (nop_to_pre_start || write_to_pre_start || read_to_pre_start) begin
sdram_commond <= PRECHARGE_CMD;
end
else if (pre_to_ref_start || ref_to_ref_start || idle_to_ref_start) begin
sdram_commond <= REF_CMD;
end
else if (ref_to_mode_start) begin
sdram_commond <= MODE_CMD;
end
else if (idle_to_active_start) begin
sdram_commond <= ACTIVE_CMD;
end
else if (active_to_write_start) begin
sdram_commond <= WRITE_CMD;
end
else if (active_to_read_start) begin
sdram_commond <= READ_CMD;
end
else begin
sdram_commond <= NOP_CMD;
end
end
//cke信号保持拉高
assign sdram_cke = 1;
//dqm信号
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sdram_dqm <= 2'b11;
end
else if(init_done)begin
sdram_dqm <= 2'b00;
end
end
//地址线
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sdram_addr <= 12'b0;
end
else if(nop_to_pre_start || write_to_pre_start || read_to_pre_start)begin
sdram_addr <= ALL_BANK;
end
else if(ref_to_mode_start)begin
sdram_addr <= MODE_CONFIG;
end
else if(idle_to_active_start)begin
sdram_addr <= row_addr;
end
else if (active_to_read_start || active_to_write_start) begin
sdram_addr <= {3'b000,col_addr};
end
else begin
sdram_addr <= 12'b0;
end
end
//sdram_bank
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sdram_bank <= 2'b00;
end
else if (idle_to_active_start || active_to_write_start || active_to_read_start) begin
sdram_bank <= bank_addr;
end
else begin
sdram_bank <= 2'b00;
end
end
//sdram_dq
assign sdram_dq_en = (state_c==WRITE) ? 1'b1 : 1'b0;
assign sdram_dq = sdram_dq_en ? sdram_dq_r : 16'hzzzz;
assign sdram_dq_r = wr_data;
assign wr_data_vld = state_c==WRITE;
assign wdata_done = write_to_pre_start;
assign rdata_done = read_to_pre_start;
always @(posedge clk or negedge rst_n)begin
if (!rst_n) begin
rd_data <= 16'd0;
end
else begin
rd_data <= sdram_dq;
end
end
// assign rd_data = state_c==READ ? sdram_dq:16'hzzzz;
//读有效标志
always @(posedge clk or negedge rst_n)begin
if (!rst_n) begin
rd_data_vld_ff0 <= 0;
end
else if (active_to_read_start) begin
rd_data_vld_ff0 <= 1;
end
else if (state_c==READ && cnt0==TREAD_11-4) begin
rd_data_vld_ff0 <= 0;
end
end
always @(posedge clk or negedge rst_n)begin
if (!rst_n) begin
rd_data_vld_ff1 <= 0;
rd_data_vld_ff2 <= 0;
rd_data_vld_ff3 <= 0;
rd_data_vld <= 0;
end
else begin
rd_data_vld_ff1 <= rd_data_vld_ff0;
rd_data_vld_ff2 <= rd_data_vld_ff1;
rd_data_vld_ff3 <= rd_data_vld_ff2;
rd_data_vld <= rd_data_vld_ff3;
end
end
//刷新请求计数
always @(posedge clk or negedge rst_n)begin
if (!rst_n) begin
auto_ref_cnt <= 0;
end
else if (add_auto_ref_cnt) begin
if (end_auto_ref_cnt) begin
auto_ref_cnt <= 0;
end
else begin
auto_ref_cnt <= auto_ref_cnt + 1'b1;
end
end
end
assign add_auto_ref_cnt = init_flag==0;
assign end_auto_ref_cnt = (add_auto_ref_cnt && auto_ref_cnt==AUTO_REF_TIME-1);
//ref_req 刷新请求
always @(posedge clk or negedge rst_n)begin
if (!rst_n) begin
ref_req <= 0;
end
else if (end_auto_ref_cnt) begin
ref_req <= 1;
end
else if (state_c==IDLE && ref_req==1) begin
ref_req <= 0;
end
end
//初始化标志
assign init_done = (state_c==MODE && end_cnt0);
always @(posedge clk or negedge rst_n)begin
if (!rst_n) begin
init_flag <= 1;
end
else if (init_done) begin
init_flag <= 0;
end
end
always @(posedge clk or negedge rst_n)begin
if (!rst_n) begin
cnt0 <= 0;
end
else if (add_cnt0) begin
if (end_cnt0) begin
cnt0 <= 0;
end
else begin
cnt0 <= cnt0 + 1'b1;
end
end
end
assign add_cnt0 = state_c!=IDLE;
assign end_cnt0 = add_cnt0 && cnt0==x-1'b1;
always @(*)begin
case(state_c)
NOP : x = TWAIT_200us;
PRECHARGE : x = TRP;
REF : x = TRC;
MODE : x = TRSC;
ACTIVE : x = TRCD;
WRITE : x = TWRITE_8;
READ : x = TREAD_11;
default : x = 0;
endcase
end
//初始化自刷新8个周期计数
always @(posedge clk or negedge rst_n)begin
if (!rst_n) begin
ref_cnt1 <= 0;
end
else if (add_cnt1) begin
if (end_cnt1) begin
ref_cnt1 <= 0;
end
else begin
ref_cnt1 <= ref_cnt1 + 1'b1;
end
end
end
assign add_cnt1 = (state_c==REF && init_flag==1 && end_cnt0);
assign end_cnt1 = (add_cnt1 && ref_cnt1== 8-1);
//读写信号标志
always @(posedge clk or negedge rst_n)begin
if (!rst_n) begin
flag_rd <= 0;
end
else if (state_c==IDLE && rd_en && ref_req==0) begin
flag_rd <= 1;
end
else if (pre_to_idle_start && flag_rd==1) begin
flag_rd <= 0;
end
end
always @(posedge clk or negedge rst_n)begin
if (!rst_n) begin
flag_wr <= 0;
end
else if (state_c==IDLE && wr_en && rd_en==0 && ref_req==0) begin //读优先级高于写优先级
flag_wr <= 1;
end
else if (pre_to_idle_start && flag_wr==1) begin
flag_wr <= 0;
end
end
endmodule
5、顶层模块
// Company :
// Engineer :
// -----------------------------------------------------------------------------
// https://blog.csdn.net/qq_33231534 PHF's CSDN blog
// -----------------------------------------------------------------------------
// Create Date : 2020-09-23 09:29:09
// Revise Data : 2020-09-24 09:32:26
// File Name : SDRAM_control_test_top.v
// Target Devices : XC7Z015-CLG485-2
// Tool Versions : Vivado 2019.2
// Revision : V1.1
// Editor : sublime text3, tab size (4)
// Description :
\
module SDRAM_control_test_top(
input clk ,//50MHz
input rst_n ,
input key_in ,
output tx_data ,
output sdram_clk ,//SDRAM时钟信号100MHz
output [3:0] sdram_commond ,//{cs,ras,cas,we}
output sdram_cke ,//时钟使能信号
output [1:0] sdram_dqm ,//数据线屏蔽信号
output [11:0] sdram_addr ,//SDRAM地址线
output [1:0] sdram_bank ,//SDRAM bank选取
inout [15:0] sdram_dq //SDRAM数据输出输入总线
);
wire key_flag ;
reg rdreq ;
wire[15:0] q ;
wire empty ;
reg send_en ;
reg [7:0] send_data ;
wire tx_done ;
reg wr_en ;
reg [15:0] wr_data ;
reg rd_en ;
reg [1:0] bank_addr ;
reg [11:0] row_addr ;
reg [8:0] col_addr ;
wire[15:0] rd_data ;
wire rd_data_vld ;
wire wr_data_vld ;
wire wdata_done ;
wire rdata_done ;
reg add_flag ;
reg [2:0] cnt ;
wire add_cnt ;
wire end_cnt ;
reg rd_fifo_flag;
reg uart_cnt ;
wire clk_100M ;
pll_clk inst_pll_clk (.inclk0(clk), .c0(clk_100M));
key_filter inst_key_filter (
.clk (clk_100M),
.rst_n (rst_n),
.key_in (key_in),
.key_flag (key_flag),
.key_state ()
);
sync_fifo #(
.WIDTH(16),
.DEPTH(24),
.MAX_DEPTH_BIT(5)
) inst_sync_fifo (
.clk (clk_100M),
.rst_n (rst_n),
.wrreq (rd_data_vld),
.data (rd_data),
.rdreq (rdreq),
.q (q),
.empty (empty),
.full (),
.half (),
.usedw ()
);
SDRAM_control inst_SDRAM_control
(
.clk (clk_100M),
.rst_n (rst_n),
.wr_en (wr_en),
.wr_data (wr_data),
.rd_en (rd_en),
.bank_addr (bank_addr),
.row_addr (row_addr),
.col_addr (col_addr),
.rd_data (rd_data),
.rd_data_vld (rd_data_vld),
.wr_data_vld (wr_data_vld),
.wdata_done (wdata_done),
.rdata_done (rdata_done),
.sdram_clk (sdram_clk),
.sdram_commond (sdram_commond),
.sdram_cke (sdram_cke),
.sdram_dqm (sdram_dqm),
.sdram_addr (sdram_addr),
.sdram_bank (sdram_bank),
.sdram_dq (sdram_dq)
);
uart_tx inst_uart_tx
(
.clk (clk_100M),
.rst_n (rst_n),
.send_en (send_en),
.send_data (send_data),
.baud_set (3'd1),
.tx_data (tx_data),
.tx_done (tx_done),
.uart_state ()
);
always @(posedge clk_100M or negedge rst_n) begin
if (!rst_n) begin
wr_en <= 0;
end
else if (key_flag) begin
wr_en <= 1;
end
else begin
wr_en <= 0;
end
end
localparam wr_data_ini = 16'd100;
always @(posedge clk_100M or negedge rst_n) begin
if (!rst_n) begin
wr_data <= 0;
end
else if (key_flag) begin
wr_data <= wr_data_ini + 4'd8;
end
else if (wr_data_vld) begin
wr_data <= wr_data + 1'b1;
end
end
always @(posedge clk_100M or negedge rst_n) begin
if (!rst_n) begin
bank_addr <= 2'b00;
row_addr <= 12'd0;
col_addr <= 9'd0;
end
else if (key_flag) begin
bank_addr <= 2'b00;
row_addr <= row_addr + 1'b1;
col_addr <= col_addr + 4'd8;
end
else begin
bank_addr <= bank_addr;
row_addr <= row_addr;
col_addr <= col_addr;
end
end
always @(posedge clk_100M or negedge rst_n) begin
if (!rst_n) begin
add_flag <= 0;
end
else if (wdata_done) begin
add_flag <= 1;
end
else if (end_cnt) begin
add_flag <= 0;
end
end
always @(posedge clk_100M or negedge rst_n) begin
if (!rst_n) begin
cnt <= 0;
end
else if (add_cnt) begin
if (end_cnt) begin
cnt <= 0;
end
else begin
cnt <= cnt + 1'b1;
end
end
end
assign add_cnt = add_flag;
assign end_cnt = add_cnt && cnt==5;
always @(posedge clk_100M or negedge rst_n) begin
if (!rst_n) begin
rd_en <= 0;
end
else if (end_cnt) begin
rd_en <= 1;
end
else begin
rd_en <= 0;
end
end
always @(posedge clk_100M or negedge rst_n) begin
if (!rst_n) begin
rd_fifo_flag <= 0;
end
else if (empty==0 && uart_cnt==0) begin
rd_fifo_flag <= 1;
end
else if (tx_done && uart_cnt==1) begin
rd_fifo_flag <= 0;
end
end
always @(posedge clk_100M or negedge rst_n) begin
if (!rst_n) begin
rdreq <= 0;
end
else if (empty==0&&rd_fifo_flag==0) begin
rdreq <= 1;
end
else begin
rdreq <= 0;
end
end
always @(posedge clk_100M or negedge rst_n) begin
if (!rst_n) begin
send_en <= 0;
end
else if (rdreq || (tx_done&&uart_cnt==0)) begin
send_en <= 1;
end
else begin
send_en <= 0;
end
end
always @(posedge clk_100M or negedge rst_n) begin
if (!rst_n) begin
uart_cnt <= 0;
end
else if (tx_done) begin
uart_cnt <= ~uart_cnt;
end
else if (rdreq) begin
uart_cnt <= 0;
end
end
always @(posedge clk_100M or negedge rst_n) begin
if (!rst_n) begin
send_data <= 8'd0;
end
else if (rdreq) begin
send_data <= q[15:8];
end
else if(tx_done&&uart_cnt==0) begin
send_data <= q[7:0];
end
end
endmodule
三、仿真及上板测试
1、仿真代码
`timescale 1ns/1ns
module SDRAM_control_test_top_tb (); /* this is automatically generated */
reg rst_n;
reg clk;
localparam clk_period = 20;
// (*NOTE*) replace reset, clock, others
reg key_in;
wire tx_data;
wire sdram_clk;
wire [3:0] sdram_commond;
wire sdram_cke;
wire [1:0] sdram_dqm;
wire [11:0] sdram_addr;
wire [1:0] sdram_bank;
wire [15:0] sdram_dq;
SDRAM_control_test_top inst_SDRAM_control_test_top
(
.clk (clk),
.rst_n (rst_n),
.key_in (key_in),
.tx_data (tx_data),
.sdram_clk (sdram_clk),
.sdram_commond (sdram_commond),
.sdram_cke (sdram_cke),
.sdram_dqm (sdram_dqm),
.sdram_addr (sdram_addr),
.sdram_bank (sdram_bank),
.sdram_dq (sdram_dq)
);
sdram_model_plus #(
.addr_bits(12),
.data_bits(16),
.col_bits(9),
.mem_sizes(2*1024)
) inst_sdram_model_plus (
.Dq (sdram_dq),
.Addr (sdram_addr),
.Ba (sdram_bank),
.Clk (sdram_clk),
.Cke (sdram_cke),
.Cs_n (sdram_commond[3]),
.Ras_n (sdram_commond[2]),
.Cas_n (sdram_commond[1]),
.We_n (sdram_commond[0]),
.Dqm (sdram_dqm),
.Debug (1'b1)
);
initial #5 clk = 1;
always #(clk_period/2) clk = ~clk ;
initial begin
#1;
rst_n = 0;
key_in = 1;
#(clk_period*20);
rst_n = 1;
#(clk_period*20);
key_in = 0;
#(clk_period*2500000);
key_in = 1;
#(clk_period*800000);
$stop;
end
endmodule
仿真图:
板子上按键按下时,串口接收数据如图:代码中发送数据为:006Ch、006Dh、006Eh、006Fh、0070h、0071h、0072h、0073h。