SDRAM : 即同步动态随机存储器(Synchronous Dynamic Random Access Memory), 同步是指其时钟频率与对应控制器(CPU/FPGA)的系统时钟频率相同,并且内部命令 的发送与数据传输都是以该时钟为基准;动态是指存储阵列需要不断的刷新来保证数据不丢失;随机指数据的读取和写入可以随机指定地址,而不是必须按照严格的线性次序变化。
SDRAM 使用电容的电荷存储特性存储数据,而 SRAM使用CMOS晶体管存储数据,这决定了SDRAM的运行功耗要远远低于SRAM。由于使用晶体管存储数据,要能够正确的存储一位数据,需要最少6个晶体管。因此,从芯片面积上来说,单片SRAM芯片的容量不可能做到很高。
通常情况下,SDRAM 是拥有四个 BANK 的动态刷新存储器,存储器工作在 3.3V 的电 压下,拥有一个同步接口,SDRAM 的所有信号都在时钟信号的上升沿被寄存。对于 SDRAM 的读写是以突发的方式进行的,对 sdram 的获取(读或者写)是从一个指定的地址开始,并按照编程好的数量(长度)的地址,以编程好的数据顺序读写数据。
教程:
SDR SDRAM: SDRAM设计教程之SDRAM结构深入剖析 altera (b站小梅哥教程)
DDR:小梅哥Xilinx ACX720 FPGA开发板自学教程V2.2.4 xilinx
DDR2:小梅哥DDR2简明教程V1.1 altera (b站小梅哥教程)
DDR3:blibli-小梅哥Xilinx FPGA基础入门到项目应用培训教程 xilinx
存取原理:先通过行地址打开行地址线,后通过列地址线打开列地址,便实现把电容与数据线连通,实现读/写操作。
打开行地址需要tRCD时间,后才能打开列地址。tRCD可查手册,计算需要多少个对应频率的时钟的延迟,进行设计。
打开列地址后,如果是读操作,需要CL(CAS READ LATENCY)个时钟周期才会出数据,如果是写操作,则数据需要同时送出,不用CL。
不同型号的sdram:命令、时序参数有区别
引脚:
对于sdram的控制,通过CS_N、RAS_N、CAS_N、WE_N四个引脚来控制,故记这四个引脚组成的状态为控制命令cmd。
通过这个表来设置cmd。
控制sdram只需要结合时序图和命令表,通过线性序列机和状态机来设计就可以。
1.上电初始化:通过计数器使sdram处于nop状态 不少于100us后,发出一个precharge命令,经过tRP时间(一直处于nop,下同),发出一个autorefresh命令,经过tRFC时间,再autorefresh一次,经过tRFC时间,便可发出load mode register命令配置模式寄存器。
通常都需要通过配置模式寄存器来设计突发类型(Burst type:即有两种模式);突发长度(burst length:即每次读或写的数据长度),输出周期间隔(CAS:即发出命令后经过几个周期开始出数据)具体模式如下图解释:
模式寄存器设置规则如下:
2.读/写命令:
器件手册给出了包括读/写在内的多种命令的时序图,根据时序图进行设计即可,比如常用的:
类似IIC协议和SPI协议,依旧是状态机加线性序列机的形式来是实现。最底层用状态机实现每种状态需要的操作,中间层用线性序列机实现一个完整的读/写/其他操作需要经过几种状态的跳转。故先定义状态和命令cmd。
注意,设计时仍然需要注意时钟问题,给sdram的时钟是在上升沿读取数据,所以输出的数据在上升沿应该是稳定的,具体可以通过设计与读写两个时钟的移相来实现。
- module sdram_driver(
- input clk ,
- input rst_n ,
-
- input write_sig ,
- input read_sig ,
- input initial_sig ,
- input [12:0]write_addr ,
- input [1:0]write_bank ,
- input [12:0]read_addr ,
- input [1:0]read_bank ,
-
- output reg [3:0]cmd ,
- output reg [1:0]bank ,
- output reg [12:0]addr ,
- output reg [11:0]mode ,
- output reg cke_ctrl ,
- output reg [1:0]dqm_ctrl ,
- output reg initial_done ,
- output reg [15:0]read_data ,
- output reg trans_done ,
- inout [15:0]DQ
- );
-
- //状态参数
- localparam nop1 = 4'b1000 ; //空闲态:禁止命令
- localparam nop2 = 4'b0111 ; //空闲态:空命令
- localparam active = 4'b0011 ; //选bank与行
- localparam read = 4'b0101 ; //选bank和列,启动突发读 ,最后一位为屏蔽位
- localparam write = 4'b0100 ; //选bank和列,启动突发读 ,最后一位为屏蔽位
- localparam burst_terminate = 4'b0110 ; //突发中止
- localparam precharge = 4'b0010 ; //关闭行,预充电
- localparam refresh = 4'b0001 ; //刷新
- localparam load_mode_register = 4'b0000 ; //a0 - a11 tmrd
- // mode register
- localparam M9_BL_mode = 1'b0 ; //length
- localparam M9_SL_mode = 1'b1 ; //single
- localparam M87_SO_mode = 2'b00 ;
- localparam M654_CAS_2 = 3'b010 ;//CL
- localparam M654_CAS_3 = 3'b011 ;//CL
- localparam M3_seq = 1'b0 ;
- localparam M3_intl = 1'b1 ;
- localparam M210_BL_1 = 3'b000 ;//be able to replace single read/write (M9)
- localparam M210_BL_2 = 3'b001 ;
- localparam M210_BL_4 = 3'b010 ;
- localparam M210_BL_8 = 3'b011 ;
- localparam M210_BL_full = 3'b111 ;
- // read with auto precharge ?
- localparam auto_precharge = 1 ;
- wire rd_with_auto , wr_with_auto ;
- assign rd_with_auto = auto_precharge ;
- assign wr_with_auto = auto_precharge ;
- reg [15:0]cmd_cnt ;
- always@( posedge clk or negedge rst_n )
- if(!rst_n)
- begin
- cmd_cnt <= 0 ;
- cmd <= nop2 ;
- bank <= 0 ;
- addr <= 0 ;
- mode <= 0 ;
- cke_ctrl <= 0 ;
- dqm_ctrl <= 0 ;
- trans_done <= 0 ;
- initial_done<= 0 ;
- read_data <= 0 ;
- end
- else if(initial_sig)
- begin
- cmd_cnt <= cmd_cnt + 1 ;
- case(cmd_cnt)
- 1:cmd <= nop2 ;//delay > 100 us
- 10005: begin cke_ctrl <= 1 ; end
- 10010: begin cmd <= precharge ; addr[10] = 1 ; end //precharge all banks : tRP >= 20ns (2T)
- 10011: begin cmd <= nop2 ; end
- 10015: begin cmd <= refresh ; cke_ctrl <= 1 ; end //auto refresh : >= 2T(RFC) ; T(RFC) >= 66ns 1T(auto)(256Mb) = 8192
- 10016: begin cmd <= nop2 ; end
- 10026: begin cmd <= refresh ; cke_ctrl <= 1 ; end //
- 10027: begin cmd <= nop2 ; end //
- 10040: begin cmd <= load_mode_register ; bank <= 0 ; mode <= { 2'b00 , M9_BL_mode , M87_SO_mode , M654_CAS_3 , M3_seq , M210_BL_4 } ;end //set mode :t(mrd) = 2t(ck) = 2*6 = 12 ns
- 10041: begin cmd <= nop2 ; end
- 10045: begin initial_done <= 1 ; end
- 10046: begin cmd_cnt <= 0 ; end
- default:cmd <= nop2 ;
- endcase
- end
- else if(initial_done)
- begin
- if(write_sig)
- begin
- if(wr_with_auto)
- begin
- cmd_cnt <= cmd_cnt + 1 ;
- case(cmd_cnt)
- 1:begin cmd <= active ; bank <= write_bank ; addr <= write_addr ; end
- 4:begin cmd <= write ; bank <= write_bank ; addr <= {write_addr[12:11],1'b1,write_addr[9:0]}; end
- 10:begin trans_done <= 1 ; cmd_cnt <= 0 ;end
- default:cmd<=nop2;
- endcase
- end
- else if(~wr_with_auto)
- begin
- cmd_cnt <= cmd_cnt + 1 ;
- case(cmd_cnt)
- 1:begin cmd <= active ; bank <= write_bank ; addr <= write_addr ; end
- 4:begin cmd <= write ; bank <= write_bank ; addr <= {write_addr[12:11],1'b1,write_addr[9:0]}; end
- 9:begin cmd <= precharge ; addr[10] = 1 ;end
- 12:begin trans_done <= 1 ; cmd_cnt <= 0 ;end
- default:cmd<=nop2;
- endcase
- end
- end
- else if(read_sig)
- begin
- if( rd_with_auto )
- begin
- cmd_cnt <= cmd_cnt + 1 ;
- case(cmd_cnt)
- 1:begin cmd <= active ; bank <= read_bank ; addr <= read_addr ; end
- 4:begin cmd <= read ; bank <= read_bank ; addr <= {read_addr[12:11],1'b1,read_addr[9:0]}; end //addr10 control auto charge
- 9:begin read_data <= DQ ; end
- 10:begin read_data <= DQ ; end
- 11:begin read_data <= DQ ; end
- 12:begin read_data <= DQ ; end
- 13:begin trans_done<= 1 ; cmd_cnt <= 0 ; end
- default:begin cmd <= nop2 ;read_data <= DQ ;end
- endcase
- end
- else if( ~rd_with_auto )
- begin
- cmd_cnt <= cmd_cnt + 1 ;
- case(cmd_cnt)
- 1:begin cmd <= active ; bank <= read_bank ; addr <= read_addr ; end
- 4:begin cmd <= read ; bank <= read_bank ; addr <= {read_addr[12:11],1'b0,read_addr[9:0]}; end //addr10: control auto charge
- 9:begin read_data <= DQ ; end
- 10:begin read_data <= DQ ; end
- 11:begin read_data <= DQ ; cmd <= precharge ; addr[10] = 1 ; end //precharge
- 12:begin read_data <= DQ ; end
- 14:begin trans_done<= 1 ; cmd_cnt <= 0 ; end
- default:cmd <= nop2 ;
- endcase
- end
- end
- else
- begin
- cmd <= nop2 ;
- cmd_cnt <= 0 ;
- trans_done<= 0 ;
- end
- end
- else
- begin
- cmd_cnt <= 0 ;
- end
-
-
-
-
- endmodule
- //-75 100MHz CL = 2 setup = 1.5 hold = 0.8 上升沿读写数据//
- // 100MHz : T = 10ns //
- // tRP >= 20ns ; T(RFC) >= 66ns ; t(mrd) = 12 ns
- // 这里实现 普通的写和读 ;有特殊要求的根据手册进行cmd修改即可
-
- module sdram_ctrl(
- input fpga_clk ,
- input rst_n ,
-
- input write_sig ,
- input read_sig ,
- input [12:0]write_addr ,
- input [1:0]write_bank ,
- input [15:0]write_data ,
- input [12:0]read_addr ,
- input [1:0]read_bank ,
-
- output trans_done ,
- output [15:0]read_data ,
- output sclk ,
- output CKE ,
- output CS_N ,
- output RAS_N ,
- output CAS_N ,
- output WE_N ,
- output [1:0]DQM ,//高电平时屏蔽
- output [12:0]SA ,//A12 - A0
- output [1:0]BA ,
- inout [15:0]DQ
- );
-
- wire clk_100m;
- wire clk_100m_90shift ;
- pll pll_inst(
- .inclk0(fpga_clk),
- .c0(clk_100m),
- .c1(clk_100m_90shift)
- );
- assign sclk = clk_100m_90shift ;
-
- //read / write signal catch
- reg write_sig_reg ;
- always@(posedge fpga_clk or negedge rst_n)
- if(!rst_n)
- write_sig_reg <= 0 ;
- else
- write_sig_reg <= write_sig ;
-
- reg read_sig_reg ;
- always@(posedge fpga_clk or negedge rst_n)
- if(!rst_n)
- read_sig_reg <= 0 ;
- else
- read_sig_reg <= read_sig ;
-
- reg write_signal ;
- reg read_signal ;
- always@(posedge fpga_clk or negedge rst_n)
- if(!rst_n)
- write_signal <= 0 ;
- else if( write_sig & (!write_sig_reg ) )
- write_signal <= 1 ;
- else if(trans_done)
- write_signal <= 0 ;
-
- always@(posedge fpga_clk or negedge rst_n)
- if(!rst_n)
- read_signal <= 0 ;
- else if( read_sig & (!read_sig_reg ) )
- read_signal <= 1 ;
- else if(trans_done)
- read_signal <= 0 ;
-
- // initial sdram
- reg [5:0]initial_cnt ;
- wire initial_done ;
- always@(posedge clk_100m or negedge rst_n)
- if(!rst_n)
- initial_cnt <= 0 ;
- else if( initial_cnt < 50 )
- initial_cnt <= initial_cnt + 1 ;
- else
- initial_cnt <= initial_cnt ;
-
- reg initial_sig ;
- always@(posedge clk_100m or negedge rst_n)
- if(!rst_n)
- initial_sig <= 0 ;
- else if(initial_cnt == 48)
- initial_sig <= 1 ;
- else if(initial_done)
- initial_sig <= 0 ;
-
-
- wire [3:0]cmd ;
- wire [1:0]bank ;
- wire [12:0]addr ;
- wire [11:0]mode ;
- wire cke_ctrl ;
- wire [1:0]dqm_ctrl ;
- sdram_driver sdram_driver_inst(
- .clk(clk_100m) ,
- .rst_n(rst_n) ,
-
- .write_sig(write_signal) ,
- .read_sig(read_signal) ,
- .initial_sig(initial_sig),
- .write_addr(write_addr) ,
- .write_bank(write_bank) ,
- .read_addr(read_addr) ,
- .read_bank(read_bank) ,
-
- .cmd(cmd) ,
- .bank(bank) ,
- .addr(addr) ,
- .mode(mode) ,
- .cke_ctrl(cke_ctrl) ,
- .dqm_ctrl(dqm_ctrl) ,
- .initial_done(initial_done),
- .read_data(read_data) ,
- .trans_done(trans_done) ,
- .DQ(DQ)
- );
-
- sdram_underlying_logic sdram_underlying_logic_inst(
- .clk(clk_100m) ,
- .rst_n(rst_n) ,
-
- .mode(mode),//A11 -A0
- .cmd(cmd) ,
- .cke_ctrl(cke_ctrl) ,
- .bank(bank) ,
- .addr(addr) ,
- .write_data(write_data) ,
- .dqm_ctrl(dqm_ctrl),
-
-
- .CKE(CKE) ,
- .CS_N(CS_N) ,
- .RAS_N(RAS_N) ,
- .CAS_N(CAS_N) ,
- .WE_N(WE_N) ,
- .DQM(DQM) ,//高电平时屏蔽
- .SA(SA) ,//A12 - A0
- .BA(BA) ,
- .DQ(DQ)
- );
-
-
- endmodule
- module sdram_underlying_logic(
- input clk ,
- input rst_n ,
-
- input [11:0]mode,//A11 -A0
- input [3:0]cmd ,
- input cke_ctrl ,
- input [1:0]bank ,
- input [12:0]addr ,
- input [15:0]write_data ,
- input [1:0]dqm_ctrl,
-
- output reg CKE ,
- output reg CS_N ,
- output reg RAS_N ,
- output reg CAS_N ,
- output reg WE_N ,
- output reg[1:0]DQM ,//高电平时屏蔽
- output reg[12:0]SA ,//A12 - A0
- output reg [1:0]BA ,
- inout [15:0]DQ
- );
-
- reg DQ_ctrl ;
- reg [15:0]DQ_out ;
- assign DQ = (DQ_ctrl ? DQ_out : 16'hzzzz) ;///
- //状态参数
- localparam nop1 = 10'b1000000000 ; //空闲态:禁止命令
- localparam nop2 = 10'b0100000000 ; //空闲态:空命令
- localparam active = 10'b0010000000 ; //选bank与行
- localparam read = 10'b0001000000 ; //选bank和列,启动突发读 ,最后一位为屏蔽位
- localparam write = 10'b0000100000 ; //选bank和列,启动突发读 ,最后一位为屏蔽位
- localparam burst_terminate = 10'b0000010000 ; //突发中止
- localparam precharge = 10'b0000001000 ; //关闭行,预充电
- localparam auto_refresh = 10'b0000000100 ; //自动刷新
- localparam self_refresh = 10'b0000000010 ; //自刷新
- localparam load_mode_register = 10'b0000000001 ; //加载模式寄存器
- reg [9:0]state ;//状态机
- always@(posedge clk or negedge rst_n)
- if(!rst_n)
- state <= nop2 ;
- else if( cmd[3] )
- state <= nop1 ;
- else if( cmd == 4'b0111 )
- state <= nop2 ;
- else if( cmd == 4'b0011 )
- state <= active ;
- else if( cmd == 4'b0101 )
- state <= read ;
- else if( cmd == 4'b0100 )
- state <= write ;
- else if( cmd == 4'b0110 )
- state <= burst_terminate ;
- else if( cmd == 4'b0010 )
- state <= precharge ;
- else if( cmd == 4'b0001 )
- begin
- if(cke_ctrl)
- state <= auto_refresh ;
- else
- state <= self_refresh ;
- end
- else if( cmd == 4'b0000 )
- state <= load_mode_register ;
-
-
- reg [7:0]state_cnt ;
- always@(posedge clk or negedge rst_n)
- if(!rst_n)
- begin
- state_cnt <= 0 ;
- CKE <= 1 ;
- CS_N <= 1 ;
- RAS_N <= 1 ;
- CAS_N <= 1 ;
- WE_N <= 1 ;
- BA <= 0 ;
- SA <= 0 ;
- DQM <= 0 ;
- DQ_ctrl <= 1 ;//获取数据线的控制权
- DQ_out <= 0 ;
- end
- else
- begin
- case(state)
- nop1 : begin CS_N <= 1 ; end
- nop2 : begin CS_N <= 0 ; RAS_N <= 1 ; CAS_N <= 1 ; WE_N <= 1 ; DQ_out <= write_data ;end
- active: begin CS_N <= 0 ; RAS_N <= 0 ; CAS_N <= 1 ; WE_N <= 1 ; SA <= addr ; BA <= bank ;end
- read : begin CS_N <= 0 ; RAS_N <= 1 ; CAS_N <= 0 ; WE_N <= 1 ; SA <= addr ; BA <= bank ; DQM <= dqm_ctrl ; DQ_ctrl <= 0 ; end
- write : begin CS_N <= 0 ; RAS_N <= 1 ; CAS_N <= 0 ; WE_N <= 0 ; SA <= addr ; BA <= bank ; DQM <= dqm_ctrl ; DQ_out <= write_data ;end
- precharge : begin CS_N <= 0 ; RAS_N <= 0 ; CAS_N <= 1 ; WE_N <= 0 ; SA[10] <= addr[10] ; BA <= bank ;DQ_ctrl <= 1 ; end//sa[10] = 1 :对所有bank预充电 ; sa[10] = 0 : 对ba1 ba0 选中的bank预充电
- auto_refresh : begin CS_N <= 0 ; RAS_N <= 0 ; CAS_N <= 0 ; WE_N <= 1 ; CKE <= 1 ; end
- self_refresh : begin CS_N <= 0 ; RAS_N <= 0 ; CAS_N <= 0 ; WE_N <= 1 ; CKE <= 0 ; end
- load_mode_register : begin CS_N <= 0 ; RAS_N <= 0 ; CAS_N <= 0 ; WE_N <= 0 ; SA <= { 1'b0 , mode } ; end
- default:;
- endcase
- end
-
-
-
-
- endmodule
- `timescale 1ns/1ns
- module sdram_ctrl_tb();
-
- reg fpga_clk ;
- reg rst_n ;
- reg write_sig ;
- reg read_sig ;
- reg [12:0]write_addr ;
- reg [1:0] write_bank ;
- reg [15:0]write_data ;
- reg [12:0]read_addr ;
- reg [1:0] read_bank ;
-
- wire sclk ;
- wire CKE ;
- wire CS_N ;
- wire RAS_N ;
- wire CAS_N ;
- wire WE_N ;
- wire [1:0]DQM ;//高电平时屏蔽
- wire [12:0]SA ;//A12 - A0
- wire [1:0]BA ;
- wire [15:0]DQ;
- wire [15:0]read_data;
- wire trans_done;
- wire [3:0]cmd_state ;
- sdram_ctrl sdram_ctrl_inst(
- .fpga_clk(fpga_clk) ,
- .rst_n(rst_n) ,
-
- .write_sig(write_sig) ,
- .read_sig(read_sig) ,
- .write_addr(write_addr) ,
- .write_bank(write_bank) ,
- .write_data(write_data) ,
- .read_addr(read_addr) ,
- .read_bank(read_bank) ,
-
- .trans_done(trans_done) ,
- .read_data(read_data) ,
- .sclk(sclk) ,
- .CKE(CKE) ,
- .CS_N(CS_N) ,
- .RAS_N(RAS_N) ,
- .CAS_N(CAS_N) ,
- .WE_N(WE_N) ,
- .DQM(DQM) ,
- .SA(SA) ,
- .BA(BA) ,
- .DQ(DQ)
- );
-
- mt48lc16m16a2 mt48lc16m16a2_inst(
- .Dq(DQ),
- .Addr(SA),
- .Ba(BA),
- .Clk(sclk),
- .Cke(CKE),
- .Cs_n(CS_N),
- .Ras_n(RAS_N),
- .Cas_n(CAS_N),
- .We_n(WE_N),
- .Dqm(DQM)
- );
-
- pulldown(DQ[15]);
- pulldown(DQ[14]);
- pulldown(DQ[13]);
- pulldown(DQ[12]);
- pulldown(DQ[11]);
- pulldown(DQ[10]);
- pulldown(DQ[9]);
- pulldown(DQ[8]);
- pulldown(DQ[7]);
- pulldown(DQ[6]);
- pulldown(DQ[5]);
- pulldown(DQ[4]);
- pulldown(DQ[3]);
- pulldown(DQ[2]);
- pulldown(DQ[1]);
- pulldown(DQ[0]);
- assign cmd_state = {CS_N,RAS_N,CAS_N,WE_N} ;
-
- initial fpga_clk = 0 ;
- always #10 fpga_clk = !fpga_clk ;
- initial begin
- rst_n = 0 ;
- write_sig = 0 ;
- read_sig = 0 ;
- write_addr = 0 ;
- write_bank = 0 ;
- write_data = 0 ;
- read_addr = 0 ;
- read_bank = 0 ;
- #201 ;
- rst_n = 1 ;
- #102000;
- #200 ;
- write_addr = 13'd2 ;
- write_bank = 2'd1;
- write_data = 16'h5a5a ;
- write_sig = 1;
- #70;
- write_data = 16'h5a5b ;
- #10;
- write_data = 16'h5a5c ;
- #10;
- write_data = 16'h5a5d ;
- #200 ;
- write_sig = 0;
- #5000;
- read_addr = 13'd2 ;
- read_bank = 2'd1;
- read_sig = 1 ;
- #200 ;
- read_sig = 0 ;
- #10000;
- $stop ;
-
- end
-
-
-
- endmodule
1.上点初始化
2.写数据(带自动预充电)
3.读数据(带自动预充电)
读出来的数据,DQ的最后一个数据只有7.6ns,不知道是什么原因。
但是移相适当仍然可以读取。
接下来对AC609板载的SDRAM:w9812g6kh进行设计,并进行板级验证。
通过查看w9812g6kh芯片的器件手册,发现它的读/写/充电/等命令设置均与MT48LC16M16A2的规则相同,只是这两个芯片容量不一样,w9812g6kh为128M,只有12个地址引脚,而MT48LC16M16A2为256M,有13个地址引脚。所以程序上只需要修改地址引脚即可。
为了上板验证程序的可行性,仿照IIC协议的测试方法,用uart-sdram联合测试,通过上位机发送读写数据来控制FPGA对w9812g6kh进行读写,此处仍需要建立通信帧,规则与IIC协议笔记差不多,如下:
//***************** ******************//
// 串口波特率:115200 //
// 通信帧格式: //
// AA AA HA LA RW DA DA 55 //
// 其中,AA AA 为帧头;55为帧尾; //
// HA、LA为存储器地址 //
// RW为读写控制,读:5A ;写:A5 //
// DA为要写入的数据,读的时候可为任意值 //
// ********************************* //
sdram的控制程序设置为BL=1的模式,(使用其他BL只需修改对应的线性序列机和模式寄存器即可)
- //***************** ******************//
- // 串口波特率:115200 //
- // 通信帧格式: //
- // AA AA HA LA RW DA DA 55 //
- // 其中,AA AA 为帧头;55为帧尾; //
- // HA、LA为存储器地址 //
- // RW为读写控制,读:5A ;写:A5 //
- // DA为要写入的数据,读的时候可为任意值 //
- // ********************************* //
-
-
- module uart_sdram(
- input fpga_clk ,
- input rst_n ,
-
- input uart_rx ,
- output rx_done ,
- output uart_tx ,
-
- output sclk ,
- output CKE ,
- output CS_N ,
- output RAS_N ,
- output CAS_N ,
- output WE_N ,
- output [1:0]DQM ,//高电平时屏蔽
- output [11:0]SA ,//A12 - A0
- output [1:0]BA ,
- inout [15:0]DQ
-
- );
-
-
- //receive uart data
- //wire rx_done ;
- wire [7:0]rx_data ;
- uart_receive uart_rec_inst(
- .clk(fpga_clk) ,
- .reset(rst_n) ,
- .baud_rate('d5) ,//115200
- .uart_rx(uart_rx),
- .data(rx_data) ,
- .rx_done(rx_done)
- );
- //Shift register
- reg [7:0]rec_shift_reg[7:0] ;
- reg rw_flag ;
- wire trans_done ;
- always@(posedge fpga_clk or negedge rst_n)
- if(!rst_n)
- begin
- rec_shift_reg[0] <= 0 ;
- rec_shift_reg[1] <= 0 ;
- rec_shift_reg[2] <= 0 ;
- rec_shift_reg[3] <= 0 ;
- rec_shift_reg[4] <= 0 ;
- rec_shift_reg[5] <= 0 ;
- rec_shift_reg[6] <= 0 ;
- rec_shift_reg[7] <= 0 ;
- rw_flag <= 0 ;
- end
- else if(rx_done)
- begin
- rec_shift_reg[0] <= rec_shift_reg[1] ;
- rec_shift_reg[1] <= rec_shift_reg[2] ;
- rec_shift_reg[2] <= rec_shift_reg[3] ;
- rec_shift_reg[3] <= rec_shift_reg[4] ;
- rec_shift_reg[4] <= rec_shift_reg[5] ;
- rec_shift_reg[5] <= rec_shift_reg[6] ;
- rec_shift_reg[6] <= rec_shift_reg[7] ;
- rec_shift_reg[7] <= rx_data ;
- rw_flag <= 1 ;
- end
- else if(trans_done)
- rw_flag <= 0 ;
- reg [15:0]write_addr ;
- reg [15:0]read_addr ;
- reg [15:0]write_data ;
- reg [15:0]read_data ;
- wire [15:0]rd_data ;
- reg write_pulse ;
- reg read_pulse ;
- sdram_ctrl sdram_ctrl_inst(
- .fpga_clk(fpga_clk) ,
- .rst_n(rst_n) ,
-
- .write_sig(write_pulse) ,
- .read_sig(read_pulse) ,
- .write_addr(write_addr[11:0]) ,
- .write_bank(write_addr[15:14]) ,
- .write_data(write_data) ,
- .read_addr(read_addr[11:0]) ,
- .read_bank(read_addr[15:14]) ,
-
- .trans_done(trans_done) ,
- .read_data(rd_data) ,
- .sclk(sclk) ,
- .CKE(CKE) ,
- .CS_N(CS_N) ,
- .RAS_N(RAS_N) ,
- .CAS_N(CAS_N) ,
- .WE_N(WE_N) ,
- .DQM(DQM) ,//高电平时屏蔽
- .SA(SA) ,//A12 - A0
- .BA(BA) ,
- .DQ(DQ)
- );
- wire uart_send_done ;
- reg uart_send_en ;
- uart_send uart_send(
- .clk(fpga_clk),
- .reset(rst_n),
- .data(read_data[7:0]),
- .send_en(uart_send_en),
- .baud_rate(5),//115200
- .uart_tx(uart_tx),
- .tx_done(uart_send_done)
- );
- //Parsing frames
- localparam frames_header = 8'hAA ;
- localparam frames_tail = 8'h55 ;
- localparam read_option = 8'h5A ;
- localparam write_option = 8'hA5 ;
- reg [15:0]wr_cnt ;
- reg [15:0]rd_cnt ;
- always@(posedge fpga_clk or negedge rst_n)
- if(!rst_n)
- begin
- write_addr <= 0 ;
- read_addr <= 0 ;
- write_data <= 0;
- read_data <= 0 ;
- write_pulse <= 0 ;
- read_pulse <= 0 ;
- wr_cnt <= 0 ;
- rd_cnt <= 0 ;
- end
-
- else if( rw_flag )
- begin
- if ( (rec_shift_reg[0] == frames_header) && (rec_shift_reg[1] == frames_header) && (rec_shift_reg[7] == frames_tail) )
- begin
- if(rec_shift_reg[4] == write_option )
- begin
- write_addr <= { rec_shift_reg[2] , rec_shift_reg[3] } ;
- write_data <= {rec_shift_reg[5],rec_shift_reg[6]} ;
- wr_cnt <= wr_cnt + 1 ;
- //write signal
- if( wr_cnt < 3 )
- write_pulse <= 1 ;
- else if(trans_done)
- begin
- wr_cnt <= 0 ;
-
- end
- else
- write_pulse <= 0 ;
- end
- else if ( rec_shift_reg[4] == read_option )
- begin
- read_addr <= { rec_shift_reg[2] , rec_shift_reg[3] } ;
- //read signal
- rd_cnt <= rd_cnt + 1 ;
- if(trans_done)
- begin
- read_data <= rd_data ;
- read_pulse <= 0 ;
- rd_cnt <= 0 ;
-
- end
- else if(rd_cnt <3)
- read_pulse <= 1 ;
- else
- read_pulse <= 0 ;
- end
- end
- end
- else
- begin
- write_pulse <= 0 ;
- read_pulse <= 0 ;
- wr_cnt <= 0 ;
- rd_cnt <= 0 ;
- end
-
- always@(posedge fpga_clk or negedge rst_n)
- if(!rst_n)
- uart_send_en <= 0 ;
- else if(trans_done && (rec_shift_reg[4] == read_option) )
- uart_send_en <= 1 ;
- else if(uart_send_done)
- uart_send_en <= 0 ;
-
- endmodule
fpga向sdram芯片写入数据16‘h1234,并返回低位8’h34。