1. 原理及实验目的介绍
1.1 实验目的
实验的目的旨在使用sdram作为中间媒介,在sdram上读写以在显示器上显示图像的目的。
1.2 实验原理
1.2.1 SDRAM型号介绍
这里我们使用的sdram为Hynix公司生产的型号为HY57V281620F的 芯片,其存储空间可以分为4Bank x 2M x16Bit,其中4Bank指的是该芯片有4个bank区间,2M指的是每个bank有2M的存储地址,列地址A0~A8,行地址是 A0~A11,16bit指的是每个地址空间可以存16bit的数据,因此该芯片的总存储容量为128Mbit。
1.2.2 SDRAM的运行机制 :
SDRAM中使用的电容器来存储数据,但这些电容器不是永久性的存储介质。电容器中的电荷会逐渐泄漏,导致存储的信息消失。为了防止数据丢失,需要定期刷新操作(ref)。sdram和外界只有两个交流的接口
1. cmd[17:0] : 用于控制sdram的一些操作过程指令
cmd组成 : {Cs_n,Ras_n,Cas_n,We_n,Ba,Addr}
具体代表的功能如下:
2. 代码及功能框架设计:
2.1 功能框架 :
我们在设计了五个模块分别是 :
- sdram 初始化模块
- switch sdram状态跳转仲裁模块
- sdram刷新模块
- write 写sdram的模块
- read 读sdram的模块
2.2 代码框架
2.2.1 ini_module
对于sdram来说,在上电之后优先要进行的就是初始化的过程。在完成初始化之后,我们才可以进行后续的刷新和读写。具体的需要在这个过程中加入以下的几个步骤:
比较重要的是对于模式寄存器的配置:(MRS)具体如下:
在本次实验中我们使用突发读和突发写,并且顺行突发,并且单次突发长度为16bits
具体代码如下:
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2023 All rights reserved
// -----------------------------------------------------------------------------
// Author : XIBO WU (Gatsby) wuxibo2023@163.com
// File : sdram_init.v
// Create : 2023-12-08 14:55:32
// Revise : 2023-12-15 15:07:25
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
/*----------------------------------------------------------------------------------------------------
sdram初始化
cmd真值表 cs_n ras_n cas_n we_n ba[1:0] addr[11] addr[10],addr[9:0]
NOP 0 1 1 1 X X X X
PRLL 0 0 1 0 X x 1 X
REF 0 0 0 1 X X X X
MR模式寄存器 {ba[1:0],addr[11:0]} = {00,00,000,011,0,010}
-----------------------------------------------------------------------------------------------------*/
module sdram_ini(
input wire i_clk, //50MHZ
input wire i_rst,
output reg o_ini_end_flag,
output reg[17:0] o_ini_cmd
);
parameter TRP = 2,
TRC = 4,
TMRD = 2;
parameter DEY_200US = 9999,
PALL_CNT = 10000,
REF_ST1 = 10001+TRP,
REF_ST2 = 10002+TRP+TRC,
MRD_CNT = 10003+TRP+TRC+TRC,
END_INI_CNT = 10003+TRP+TRC+TRC+TMRD;
parameter NOP = 18'h1c000,//18'b01_1100_0000_0000_0000
PALL = 18'h08400,//18'b00_1000_0100_0000_0000
REF = 18'h04000,//18'b00_0100_0000_0000_0000
MR = 18'h00032;//18'b00_0000_0000_0011_0010
reg[15:0] ini_cnt;
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
ini_cnt <= 'd0;
end
else if (ini_cnt == END_INI_CNT)begin
ini_cnt <= 'd0;
end
else if(o_ini_end_flag == 1'b0) begin
ini_cnt <= ini_cnt + 'd1;
end
else begin
ini_cnt <= ini_cnt;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
o_ini_end_flag <= 1'b0;
end
else if (ini_cnt >= END_INI_CNT)begin
o_ini_end_flag <= 1'b1;
end
else begin
o_ini_end_flag <= o_ini_end_flag;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
o_ini_cmd <= NOP;
end
else if (ini_cnt <= DEY_200US)begin
o_ini_cmd <= NOP;
end
else if(ini_cnt == PALL_CNT) begin
o_ini_cmd <= PALL;
end
else if(ini_cnt > PALL_CNT && ini_cnt < REF_ST1) begin
o_ini_cmd <= NOP;
end
else if(ini_cnt == REF_ST1) begin
o_ini_cmd <= REF;
end
else if(ini_cnt > REF_ST1 && ini_cnt < REF_ST2) begin
o_ini_cmd <= NOP;
end
else if(ini_cnt == REF_ST2) begin
o_ini_cmd <= REF;
end
else if(ini_cnt > REF_ST2 && ini_cnt < MRD_CNT) begin
o_ini_cmd <= NOP;
end
else if(ini_cnt == MRD_CNT)begin
o_ini_cmd <= MR;
end
else if(ini_cnt > MRD_CNT)begin
o_ini_cmd <= NOP;
end
end
endmodule
2.2.2 sd_main_ctrl
这个模块是sdram的控制模块,我们在这个模块中,控制sdram的状态机跳转。具体的状态机跳转如下:
对于这个sdram芯片我们需要每15.625us进行一次刷新,所以我们在读写的过程中如果发生刷新的请求,我们必须中断我们的读写过程来进行刷新的操作。我们需要注意的是我们跳转回仲裁状态机的过程中,有两个key_reg,一个是data_end,一个ref_break_end。data_end,表示数据已经传输完成了,ref_break_end表示中断了进行刷新。
2.2.3 sdram_ref
代码如下:
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2024 All rights reserved
// -----------------------------------------------------------------------------
// Author : XIBO WU (Gatsby) wuxibo2023@163.com
// File : sdram_main_ctrl.v
// Create : 2023-12-13 10:04:05
// Revise : 2024-01-04 18:25:28
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
module sdram_main_ctrl(
input wire i_clk,
input wire i_vclk,
input wire i_rst,
output wire [17:0] o_cmd,
output wire [15:0] io_dq,
output wire [7:0] o_vga,
output wire o_hsync,
output wire o_vsync,
input wire i_wfifo_en,
input wire [7:0] i_wfifo_data
);
parameter IDLE = 6'b000001;
parameter INI = 6'b000010;
parameter SW = 6'b000100;
parameter REF = 6'b001000;
parameter READ = 6'b010000;
parameter WRITE = 6'b100000;
reg [5:0] state;
//INI
wire ini_flag;
wire ini_end;
//REF
wire ref_req;
wire ref_en;
wire ref_end;
//READ
wire read_data_end;
wire read_ref_break_end;
wire r_en;
wire r_req;
reg [15:0] r_dq;
wire vga_start;
wire vga_rd_en;
wire [7:0] vga_rd_data;
//WRITE
wire write_data_end;
wire write_ref_break_end;
wire w_req;
wire [15:0] w_dq;
// wire wfifo_en;
// wire [7:0] wfifo_data;
wire w_en; //写使能状态跳转
wire oe; //output enable
wire [17:0] ref_cmd,ini_cmd,w_cmd,r_cmd;
assign ini_flag = (state == INI) ? 1'b1 : 1'b0; //此处和课程不一样
assign ref_en = (ref_req && state == SW);
assign w_en = (w_req == 1'b1) && (ref_req == 1'b0) && (state == SW);
assign r_en = (w_req == 1'b0 && ref_req == 1'b0 && r_req == 1'b1 && state == SW);
assign o_cmd[17:14] = ref_cmd[17:14] & ini_cmd[17:14] & w_cmd[17:14] & r_cmd[17:14];
assign o_cmd[13:0] = ref_cmd[13:0] | ini_cmd[13:0] | w_cmd[13:0] | r_cmd[13:0];
assign oe = (state == WRITE) ? 1'b1 : 1'b0;
assign o_dq = oe ? w_dq : 16'dz;
always @(posedge i_clk) begin
r_dq <= io_dq;
end
always @(posedge i_clk or posedge i_rst) begin
if (i_rst == 1'b1) begin
state <= IDLE;
end
else begin case(state)
IDLE : begin
state <= INI;
end
INI : begin
if(ini_end == 1'b1) begin
state <= SW;
end
end
SW : begin
if(ref_en == 1'b1) begin
state <= REF;
end
else if(w_req == 1'b1 && ref_req == 1'b0) begin
state <= WRITE;
end
else if(r_req == 1'b1 && ref_req == 1'b0 && w_req == 1'b0) begin
state <= READ;
end
end
REF : begin
if(ref_end == 1'b1) begin
state <= SW;
end
end
WRITE : begin
if(write_data_end == 1'b1 || write_ref_break_end == 1'b1) begin
state <= SW;
end
end
READ : begin
if(read_data_end == 1'b1 || read_ref_break_end == 1'b1) begin
state <= SW;
end
end
endcase
end
end
sdram_ini inst_sdram_ini (
.i_clk (i_clk),
.i_rst (i_rst),
.o_ini_end_flag (ini_end),
.o_ini_cmd (ini_cmd)
);
sdram_ref inst_sdram_ref (
.i_clk (i_clk),
.i_rst (i_rst),
.i_ini_end (ini_end),
.i_ref_en (ref_en),
.o_ref_end (ref_end),
.o_ref_req (ref_req),
.o_ref_cmd (ref_cmd)
);
sdram_wr inst_sdram_wr (
.i_clk (i_clk),
.i_rst (i_rst),
.o_w_req (w_req),
.i_wfifo_en (wfifo_en),
.i_wfifo_data (wfifo_data),
.i_w_en (w_en),
.i_ref_req (ref_req),
.o_write_ref_break_end (write_ref_break_end),
.o_write_data_end (write_data_end),
.o_w_cmd (w_cmd),
.o_w_dq (w_dq)
);
sdram_rd inst_sdram_rd (
.i_clk (i_clk),
.i_vclk (i_vclk),
.i_rst (i_rst),
.o_r_req (r_req),
.i_r_en (r_en),
.i_ref_req (ref_req),
.o_read_ref_break_end (read_ref_break_end),
.o_read_data_end (read_data_end),
.o_r_cmd (r_cmd),
.i_r_dq (r_dq),
.o_vga_start (vga_start),
.i_vga_rd_en (vga_rd_en),
.o_vga_rd_data (vga_rd_data)
);
//vga显示,显示行和列 ,还有对应各自的像素的数值8bits
VGA_TIMING inst_VGA_TIMING (
.sclk (i_vclk),
.rst_n (vga_start),
.po_vga (o_vga),
.po_de (po_de),
.po_v_sync (o_vsync),
.po_h_sync (o_hsync),
.rgb_pixel (vga_rd_data),
.rd_fifo_en (vga_rd_en)
);
endmodule
2.2.4 sdram_wr
sdram在写的过程比较重要需要考虑的是如何应对ref的刷新进程,并且能在刷新结束之后,继续自己的之前的写入过程。具体我们要实现的流程是我们通过串口把要写的数据写入我们设定好的fifo中,之后写入sdram的时候再从这个fifo读取数据。
状态机如下:
中间涉及一个比较重要的地方就是,本次实验我们只写入同一个bank,所以bank的地址都为0,我们具体要选择写入的列由WRITE中cmd的后几位决定,我们具体要写入的行由ACTIVE状态中,我们激活的行来决定。
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2024 All rights reserved
// -----------------------------------------------------------------------------
// Author : XIBO WU (Gatsby) wuxibo2023@163.com
// File : sdram_wr.v
// Create : 2023-12-27 17:12:11
// Revise : 2024-01-02 17:32:39
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
/*----------------------------------------------------------------------------------------------------
sdram初始化
cmd真值表 cs_n ras_n cas_n we_n ba[1:0] addr[11] addr[10],addr[9:0]
NOP 0 1 1 1 X X X X
PRLL 0 0 1 0 X x 1 X
REF 0 0 0 1 X X X X
WR 0 1 0 0 V V V V
ACT 0 0 1 1 V V V V
MR模式寄存器 {ba[1:0],addr[11:0]} = {00,00,000,011,0,010}
-----------------------------------------------------------------------------------------------------*/
module sdram_wr (
input wire i_clk,
input wire i_rst,
output wire o_w_req,
input wire i_wfifo_en,
input wire [7:0] i_wfifo_data,
input wire i_w_en,
input wire i_ref_req,
output wire o_write_ref_break_end,
output wire o_write_data_end,
output wire [17:0] o_w_cmd,
output wire [15:0] o_w_dq
);
//sdram一行的大小位512*16 bits 所以当fifo的大小位1024*8bits的时候就可以启动fifo的读取
parameter IDLE = 5'b0_0001;
parameter WREQ = 5'b0_0010;
parameter ACTIVE = 5'b0_0100;
parameter WRITE = 5'b0_1000;
parameter PREC = 5'b1_0000;
parameter WR = 18'b01_0000_0000_0000_0000;
parameter ACT = 18'b01_0000_0000_0000_0000;
parameter NOP = 18'h1c000;
parameter PALL = 18'h08400;//18'b00_1000_0100_0000_0000
reg [4:0] state;
reg sd_ram_start;
reg [2:0] act_cnt;
reg act_end;
wire[1:0] bank_addr;
reg [11:0] sd_row_cnt;
reg sd_row_end;
reg [1:0] burst_cnt;
reg [7:0] burst_col_cnt;
reg img_end;
reg rd_fifo_en;
wire[15:0] rd_fifo_data;
wire[9 :0] rd_data_count;
reg sd_row_end_flag;
reg [3:0] pre_cnt;
reg pre_end;
reg ref_break;
reg [17:0] cmd;
wire ref_break_end;
wire full,empty;
//assign
assign o_w_dq = rd_fifo_data;
assign o_write_ref_break_end = (state == PREC) & ref_break & pre_end;
assign o_write_data_end = (state == PREC) & sd_row_end_flag & pre_end;
assign o_w_cmd = cmd;
assign bank_addr = 2'b00;
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
state <= IDLE;
end
else case (state)
IDLE : begin
if(sd_ram_start == 1'b1)
state <= WREQ;
end
WREQ : begin
if(i_w_en == 1'b1)
state <= ACTIVE;
end
ACTIVE : begin
if(act_end == 1'b1)
state <= WRITE;
end
WRITE: begin
if(sd_row_end == 1'b1 || (burst_cnt == 'd3 && i_ref_req == 1'b1))
state <= PREC;
end
PREC: begin
if(pre_end == 1'b1 && sd_row_end_flag == 1'b1)
state <= IDLE;
else if(ref_break == 1'b1 && pre_end == 1'b1)
state <= WREQ;
end
default : state <= IDLE;
endcase
end
//IDLE
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
sd_ram_start <= 1'b0;
end
else if(state != IDLE) begin
sd_ram_start <= 1'b0;
end
else if (state == IDLE && rd_data_count >= 'd510) begin
sd_ram_start <= 1'b1;
end
end
//WREQ
assign o_w_req = (state == WREQ);
//ACTIVE
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
act_cnt <= 'd0;
end
else if(state == ACTIVE && act_cnt == 'd3) begin
act_cnt <= 'd0;
end
else if (state == ACTIVE) begin
act_cnt <= act_cnt + 'd1;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
act_end <= 'd0;
end
else if (act_cnt == 'd2) begin
act_end <= 1'b1;
end
else begin
act_end <= 1'b0;
end
end
//WRITE
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
burst_cnt <= 'd0;
end
else if (state == WRITE) begin
burst_cnt <= burst_cnt + 'd1;
end
else begin
burst_cnt <= 'd0;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
burst_col_cnt <= 'd0;
end
else if (sd_row_end == 1'b1) begin
burst_col_cnt <= 'd0;
end
else if (state == WRITE && burst_cnt == 'd3) begin
burst_col_cnt <= burst_col_cnt + 'd1;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
sd_row_end <= 1'b0;
end
else if (burst_col_cnt == 'd127 && burst_cnt == 'd2 && state == WRITE) begin
sd_row_end <= 1'b1;
end
else begin
sd_row_end <= 1'b0;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
sd_row_cnt <= 'd0;
end
else if (sd_row_cnt == 'd299 && sd_row_end == 1'b1) begin
sd_row_cnt <= 'd0;
end
else if (sd_row_end == 1'b1)begin
sd_row_cnt <= sd_row_cnt + 'd1;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
img_end <= 1'b0;
end
else if (sd_row_cnt == 'd299 && burst_cnt == 'd2) begin
img_end <= 1'b1;
end
else begin
img_end <= 1'b0;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
rd_fifo_en <= 1'b0;
end
else if (state == WRITE) begin
rd_fifo_en <= 1'b1;
end
else begin
rd_fifo_en <= 1'b0;
end
end
//PRECHARGE
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
sd_row_end_flag <= 1'b0;
end
else if (state == PREC && pre_cnt == 'd8) begin
sd_row_end_flag <= 1'b0;
end
else if (state == WRITE && sd_row_end == 1'b1) begin
sd_row_end_flag <= 1'b1;
end
else begin
sd_row_end_flag <= sd_row_end_flag;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
pre_cnt <= 'd0;
end
else if (state == PREC && pre_cnt == 'd8) begin
pre_cnt <= 'd0;
end
else if (state == PREC) begin
pre_cnt <= pre_cnt + 'd1;
end
else begin
pre_cnt <= pre_cnt;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
pre_end <= 1'b0;
end
else if (state == PREC && pre_cnt == 'd7) begin
pre_end <= 1'b1;
end
else begin
pre_end <= 1'b0;
end
end
//PREC & REF
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
ref_break <= 1'b0;
end
else if (state == PREC && pre_cnt == 'd8) begin
ref_break <= 1'b0;
end
else if (burst_cnt == 'd3 && i_ref_req == 1'b1 && sd_row_end != 1'b1 && state == WRITE) begin
ref_break <= 1'b1;
end
else begin
ref_break <= ref_break;
end
end
//ALL
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
cmd <= NOP;
end
else if (state == ACTIVE && act_cnt == 'd0) begin
cmd <= {ACT[17:14],bank_addr,sd_row_cnt};
end
else if(state == WRITE && burst_cnt == 'd0) begin
cmd <= {WR[17:14],bank_addr,{3'b000,burst_col_cnt,2'b00}};
end
else if(state == PREC && sd_row_end == 1'b1) begin
cmd <= PALL;
end
else begin
cmd <= NOP;
end
end
asfifo_w2048x8_r1024x16 wrbuffer (
.wr_clk(i_clk), // input wire wr_clk
.rd_clk(i_clk), // input wire rd_clk
.din(i_wfifo_data), // input wire [7 : 0] din
.wr_en(i_wfifo_en), // input wire wr_en
.rd_en(rd_fifo_en), // input wire rd_en
.dout(rd_fifo_data), // output wire [15 : 0] dout
.full(full), // output wire full
.empty(empty), // output wire empty
.rd_data_count(rd_data_count) // output wire [9 : 0] rd_data_count
);
endmodule
2.2.5 sdram_rd
sdram的读取是整个状态机优先级最低的一个阶段,我们要读取的时候要保证,我们这个过程中没有进行刷新的写入的操作,读取的流程如下,我们从sdram读取数据之后,我们保存数据到我们的fifo中,因为此次实验,我们要做的事VGA的图像显示,所以说,我们在这个阶段就用vga模块读取这个fifo里面的数据,最后显示出来。
状态机如下:
这个状态机和写状态机十分相像,但是一些触发条件有一些细化的区别,我们读取的行列也和写一样,ACTIVE来激活我们要读取的行,然后用READ阶段的cmd来读取我们所需要的列。
代码如下:
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2024 All rights reserved
// -----------------------------------------------------------------------------
// Author : XIBO WU (Gatsby) wuxibo2023@163.com
// File : sdram_rd.v
// Create : 2024-01-03 11:41:34
// Revise : 2024-01-04 18:13:02
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
/*----------------------------------------------------------------------------------------------------
sdram初始化
cmd真值表 cs_n ras_n cas_n we_n ba[1:0] addr[11] addr[10],addr[9:0]
NOP 0 1 1 1 X X X X
PRLL 0 0 1 0 X x 1 X
REF 0 0 0 1 X X X X
WR 0 1 0 0 V V V V
RD 0 1 0 1 V V V V
ACT 0 0 1 1 V V V V
MR模式寄存器 {ba[1:0],addr[11:0]} = {00,00,000,011,0,010}
-----------------------------------------------------------------------------------------------------*/
module sdram_rd (
input wire i_clk,
input wire i_vclk, //25mHz vga CLK 查vga相关的文档可以知道
input wire i_rst,
output wire o_r_req,
input wire i_r_en,
input wire i_ref_req,
output wire o_read_ref_break_end,
output wire o_read_data_end,
output wire [17:0] o_r_cmd,
input wire [15:0] i_r_dq,
output wire o_vga_start,
input wire i_vga_rd_en,
output wire [7:0] o_vga_rd_data
);
//sdram一行的大小位512*16 bits 所以当fifo的大小位1024*8bits的时候就可以启动fifo的读取
parameter IDLE = 5'b0_0001;
parameter RREQ = 5'b0_0010;
parameter ACTIVE = 5'b0_0100;
parameter READ = 5'b0_1000;
parameter PREC = 5'b1_0000;
parameter WR = 18'b01_0000_0000_0000_0000;
parameter RD = 18'b01_0100_0000_0000_0000;
parameter ACT = 18'b01_0000_0000_0000_0000;
parameter NOP = 18'h1c000;
parameter PALL = 18'h08400;//18'b00_1000_0100_0000_0000
reg [4:0] state;
reg sd_read_start;
reg [2:0] act_cnt;
reg act_end;
wire[1:0] bank_addr;
reg [11:0] sd_row_cnt;
reg sd_row_end;
reg [1:0] burst_cnt;
reg [7:0] burst_col_cnt;
reg img_end;
reg rd_fifo_en;
wire[15:0] rd_fifo_data;
wire[10:0] rd_data_count;
wire[10:0] wr_data_count;
reg sd_row_end_flag;
reg [3:0] pre_cnt;
reg pre_end;
reg ref_break;
reg [17:0] cmd;
wire ref_break_end;
wire wfifo_en;
wire [7:0] wfifo_data;
reg [4:0] read_state_dly;
reg vga_fifo_ready;
wire full,empty;
//assign
assign o_r_dq = rd_fifo_data;
assign o_read_ref_break_end = (state == PREC) & ref_break & pre_end;
assign o_read_data_end = (state == PREC) & sd_row_end_flag & pre_end;
assign o_r_cmd = cmd;
assign bank_addr = 2'b00;
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
state <= IDLE;
end
else begin case (state)
IDLE : begin
if(sd_read_start == 1'b1)
state <= RREQ;
end
RREQ : begin
if(i_r_en == 1'b1)
state <= ACTIVE;
end
ACTIVE : begin
if(act_end == 1'b1)
state <= READ;
end
READ: begin
if(sd_row_end == 1'b1 || (burst_cnt == 'd3 && i_ref_req == 1'b1))
state <= PREC;
end
PREC: begin
if(pre_end == 1'b1 && sd_row_end_flag == 1'b1)
state <= IDLE;
else if(ref_break == 1'b1 && pre_end == 1'b1)
state <= RREQ;
end
default : state <= IDLE;
endcase
end
end
//IDLE
//当fifo中的大小小于512的时候 才需要继续写入 这是一个阈值的问题
//当fifo的有效数据太多的时候,我们应该优先考虑的是把fifo中的数据发送给VGA
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
sd_read_start <= 1'b0;
end
else if(state != IDLE) begin
sd_read_start <= 1'b0;
end
else if (state == IDLE && wr_data_count <'d512) begin
sd_read_start <= 1'b1;
end
end
//RREQ
assign o_r_req = (state == RREQ);
//ACTIVE
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
act_cnt <= 'd0;
end
else if(state == ACTIVE && act_cnt == 'd3) begin
act_cnt <= 'd0;
end
else if (state == ACTIVE) begin
act_cnt <= act_cnt + 'd1;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
act_end <= 'd0;
end
else if (act_cnt == 'd2) begin
act_end <= 1'b1;
end
else begin
act_end <= 1'b0;
end
end
//READ
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
burst_cnt <= 'd0;
end
else if (state == READ) begin
burst_cnt <= burst_cnt + 'd1;
end
else begin
burst_cnt <= 'd0;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
burst_col_cnt <= 'd0;
end
else if (sd_row_end == 1'b1) begin
burst_col_cnt <= 'd0;
end
else if (state == READ && burst_cnt == 'd3) begin
burst_col_cnt <= burst_col_cnt + 'd1;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
sd_row_end <= 1'b0;
end
else if (burst_col_cnt == 'd127 && burst_cnt == 'd2 && state == READ) begin
sd_row_end <= 1'b1;
end
else begin
sd_row_end <= 1'b0;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
sd_row_cnt <= 'd0;
end
else if (sd_row_cnt == 'd299 && sd_row_end == 1'b1) begin
sd_row_cnt <= 'd0;
end
else if (sd_row_end == 1'b1)begin
sd_row_cnt <= sd_row_cnt + 'd1;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
img_end <= 1'b0;
end
else if (sd_row_cnt == 'd299 && burst_cnt == 'd2) begin
img_end <= 1'b1;
end
else begin
img_end <= 1'b0;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
rd_fifo_en <= 1'b0;
end
else if (state == READ) begin
rd_fifo_en <= 1'b1;
end
else begin
rd_fifo_en <= 1'b0;
end
end
//PRECHARGE
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
sd_row_end_flag <= 1'b0;
end
else if (state == PREC && pre_cnt == 'd8) begin
sd_row_end_flag <= 1'b0;
end
else if (state == READ && sd_row_end == 1'b1) begin
sd_row_end_flag <= 1'b1;
end
else begin
sd_row_end_flag <= sd_row_end_flag;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
pre_cnt <= 'd0;
end
else if (state == PREC && pre_cnt == 'd8) begin
pre_cnt <= 'd0;
end
else if (state == PREC) begin
pre_cnt <= pre_cnt + 'd1;
end
else begin
pre_cnt <= pre_cnt;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
pre_end <= 1'b0;
end
else if (state == PREC && pre_cnt == 'd7) begin
pre_end <= 1'b1;
end
else begin
pre_end <= 1'b0;
end
end
//PREC & REF
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
ref_break <= 1'b0;
end
else if (state == PREC && pre_cnt == 'd8) begin
ref_break <= 1'b0;
end
else if (burst_cnt == 'd3 && i_ref_req == 1'b1 && sd_row_end != 1'b1 && state == READ) begin
ref_break <= 1'b1;
end
else begin
ref_break <= ref_break;
end
end
//ALL
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
cmd <= NOP;
end
else if (state == ACTIVE && act_cnt == 'd0) begin
cmd <= {ACT[17:14],bank_addr,sd_row_cnt};
end
else if(state == READ && burst_cnt == 'd0) begin
cmd <= {RD[17:14],bank_addr,{3'b000,burst_col_cnt,2'b00}};
end
else if(state == PREC && sd_row_end == 1'b1) begin
cmd <= PALL;
end
else begin
cmd <= NOP;
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
read_state_dly <= 4'b0;
end
else begin
read_state_dly <= {read_state_dly[3:0],state[3]};
end
end
always @(posedge i_clk) begin
if (i_rst == 1'b1) begin
vga_fifo_ready <= 1'b0;
end
else if(rd_data_count > 648)begin
vga_fifo_ready <= 1'b1;
end
end
//VGAVGAVGA
assign wfifo_data = i_r_dq;
assign wfifo_en = read_state_dly[4];
assign o_vga_start = vga_fifo_ready;
//vga必须等fifo缓冲了一定数据之后才能读数据
asfifo_w2048x16r4096x8 rdbuffer (
.wr_clk(clk), // input wr_clk
.rd_clk(i_vclk), // input rd_clk -> 此处是vga读取fifo的数据来显示
.din(wfifo_data), // input [15 : 0] din
.wr_en(wfifo_en), // input wr_en
.rd_en(i_vga_rd_en), // input rd_en
.dout(o_vga_rd_data), // output [7 : 0] dout
.full(full), // output full
.empty(empty), // output empty
.rd_data_count(rd_data_count), // output [10 : 0] rd_data_count
.wr_data_count(wr_data_count) // output [10 : 0] wr_data_count
);
endmodule
3. 顶层功能及框架描述
3.1 顶层功能描述:
本次实验中,我们要显示的是通过串口写入sdram中,然后再通过读取sdram显示到显示器上。我们通过matlab解析一个图片的RGB内容,rgb的单像素内容为8bits,我们用串口发送这些数据,到fifo中。等到能够写满bank一行的时候,我们开始写操作。但在没开始写操作的时候,我们的读操作已经开始读取sdram了,这个时候读出来的就是白条,等到我们把sdram写完的时候,这些白条的位置就会被相应的数据覆盖。
下面看一下顶层的代码:
顶层的代码之留下了和sdram和串口,以及VGA相应行列RGB数据的相应接口
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2024 All rights reserved
// -----------------------------------------------------------------------------
// Author : XIBO WU (Gatsby) wuxibo2023@163.com
// File : top_sdram.v
// Create : 2023-12-08 17:08:26
// Revise : 2024-01-04 18:26:14
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
module top_sdram(
input i_clk,
input i_rst_n,
input i_rx,
inout [15 : 0] io_Dq,
output [11 : 0] o_Addr,
output [1 : 0] o_Ba,
output o_sdclk,
output o_Cke,
output o_Cs_n,
output o_Ras_n,
output o_Cas_n,
output o_We_n,
output [7:0] o_vga,
output o_hsync,
output o_vsync,
output [1: 0] o_Dqm
);
wire rst;
wire sclk;
wire vclk;
wire [7:0] po_data;
wire po_flag;
assign o_Dqm = 2'b00;
assign rst = ~i_rst_n;
assign o_Cke = 1'b1;
assign o_sdclk = i_clk;//50 Mhz
clk_gen inst_clk_gen (
// Clock out ports
.clk_50mhz(sclk), // output clk_50mhz
.clk_25mhz(vclk), // output clk_25mhz
// Clock in ports
.clk_in1(i_clk)
); // input clk_in1
sdram_main_ctrl inst_sdram_main_ctrl (
.i_clk (sclk),
.i_vclk(vclk),
.i_rst (rst),
.o_cmd ({o_Cs_n,o_Ras_n,o_Cas_n,o_We_n,o_Ba,o_Addr}),
.o_dq (io_Dq),
.o_vga (o_vga),
.o_hsync(o_hsync),
.o_vsync(o_vsync),
.i_wfifo_en (po_flag),
.i_wfifo_data (po_data)
);
uart_rx inst_uart_rx (
.sclk (sclk),
.rst_n (i_rst_n),
.rx (rx),
.po_data (po_data),
.po_flag (po_flag)
);
endmodule
4. 结语
这里面还有VGA_TIMING模块还有uart_rx模块,在这里我们就不做展示了,后面的时候,后面在其他的文章里,会专门写串口和vga的部分的代码,这些都是可以复用的模块,后面会进行使用。文章来源:https://www.toymoban.com/news/detail-797676.html
我们这次做的实验到这里就结束了文章来源地址https://www.toymoban.com/news/detail-797676.html
到了这里,关于【FPGA开发】SDRAM的读写VGA显示的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!