现在的位置: 首页 > FPGA开发 > 正文

sdram 控制器代码(使用VHDL编写)

2018年03月30日 FPGA开发 ⁄ 共 14082字 ⁄ 字号 sdram 控制器代码(使用VHDL编写)已关闭评论

最近一直想着学学视频处理方面的知识,选定的平台是altera的一款FPGA,摄像头用的OV7670,输出显示为VGA,打算目前不先跑软核,完全使用FPGA处理,首先面临的问题就是ov7670的初始化,0v7670的通讯是IIC的,先写了一个IIC Master控制器,见前面一篇文章《IIC总线—-iic master使用VHDL的FPGA实现》 ,这篇文章只是实现了标准的IIC通信接口,ov7670的寄存器配置还需要另写文件控制,这部分就是一直循环的写7670的寄存器,没有什么可说的,摄像头初始化完成之后,接下来就是考虑摄像头的数据怎么存储的问题了,只有先存下来才能对数据进行处理,板子上有16M 的SDRAM,很适合做数据缓冲,看了一些SDRAM时序方面的文章并参考网上大神的代码,写了个sdram控制器,可突发传输。写了个TB简单测试了一下,波形如下:

sdram_wr

sdram_wr

sdram_wr_rd

sdram_wr_rd

最后下到板子上,用Signaltap嵌入式逻辑分析仪测试了一下实际的读写信号:

wave

wave

下面说一下sdram控制器的文件

分为三部分:控制模块、命令模块、数据读写模块。

控制模块:主要是sdram初始化、状态机的控制、一些时钟信号的产生;

命令模块:根据控制模块的状态,发出实际的sdram控制命令;

数据读写模块:根据控制模块的状态,完成实际数据的读写。

下面把代码贴一下:

sdram_ctrl模块


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

library work;
use work.inc_type.all;

--SDRAM状态控制模块
--SDRAM初始化以及定时刷新、读写控制

entity sdram_ctrl is
generic
(
TRP_CLK_CNT : positive := 4; --TRP=18ns预充电有效周期
TRFC_CLK_CNT : positive := 6; --TRC=60ns自动预刷新周期
TMRD_CLK_CNT : positive := 6; --模式寄存器设置等待时钟周期
TRCD_CLK_CNT : positive := 2; --RCD=18ns行选通周期
TCL_CLK_CNT : positive := 3; --潜伏期TCL_CLK=3个CLK,在初始化模式寄存器中可设置
TDAL_CLK_CNT : positive := 3 --写入等待
);
port
(
i_clk : in std_logic; -- 系统时钟
i_rst_n : in std_logic; -- 复位信号,低电平有效

i_sdram_wr_req : in std_logic; -- 系统写SDRAM请求信号
i_sdram_rd_req : in std_logic; -- 系统读SDRAM请求信号
i_sdram_wr_byte : in std_logic_vector(8 downto 0);-- 突发写SDRAM字节数(1-256个)
i_sdram_rd_byte : in std_logic_vector(8 downto 0);-- 突发读SDRAM字节数(1-256个)
o_sdram_wr_ack : out std_logic; -- 系统写SDRAM响应信号,作为wrFIFO的输出有效信号
o_sdram_rd_ack : out std_logic; -- 系统读SDRAM响应信号

o_end_rdburst : out std_logic;
o_end_wrburst : out std_logic;

o_sdram_busy : out std_logic;

o_init_state : out sdram_init_state;
o_work_state : out sdram_work_state;
o_clk_cnt : out integer range 0 to 512;
o_sdram_init_down : out std_logic; -- 系统初始化完毕信号
o_sys_r_wn : out std_logic -- SDRAM读/写控制信号
);
end entity sdram_ctrl;

architecture behave of sdram_ctrl is

----------------内部信号定义--------------
signal work_state : sdram_work_state; --SDRAM读写状态机
signal init_state : sdram_init_state; --SDRAM初始化状态机

signal sys_r_wn : std_logic;

signal sdram_init_down : std_logic;

signal sdram_ref_req : std_logic; -- SDRAM自刷新请求信号
signal sdram_ref_ack : std_logic; -- SDRAM自刷新请求应答信号

--延时相关
signal delay_cnt : std_logic_vector(31 downto 0); --(15 downto 0);
signal delay_down : std_logic; -- 上电后200us输入稳定期结束标志位

signal refresh_delay_cnt: std_logic_vector(14 downto 0);

signal rst_clk_cnt_n : std_logic; --时钟计数复位信号
signal clk_cnt : integer range 0 to 512; --时钟计数

signal end_rdburst : std_logic;
signal end_wrburst : std_logic;
-----------------------------------------

begin

-- 200us延时 1s
process(i_clk, i_rst_n)
begin
if i_rst_n = '0' then
delay_cnt <= (others=>'0');
delay_down <= '0';
elsif rising_edge(i_clk) then
if delay_cnt < conv_std_logic_vector(100000000, 16) then --(20000, 16)
delay_cnt <= delay_cnt + 1;
else
delay_down <= '1';
end if;
end if;
end process;

--SDRAM的初始化操作状态机
process(i_clk, i_rst_n)
begin
if i_rst_n = '0' then
init_state <= i_nop;
sdram_init_down <= '0';
elsif rising_edge(i_clk) then
case init_state is
when i_nop =>
if delay_down = '1' then
init_state <= i_pre; --上电复位后200us结束则进入下一状态
end if;
when i_pre =>
init_state <= i_trp; --预充电状态
when i_trp =>
if clk_cnt = TRP_CLK_CNT then
init_state <= i_ar1;
else
init_state <= i_trp;
end if;
when i_ar1 =>
init_state <= i_trf1; --第1次自刷新
when i_trf1 =>
if clk_cnt = TRFC_CLK_CNT then --等待第1次自刷新结束,TRFC_CLK个时钟周期
init_state <= i_ar2;
else
init_state <= i_trf1;
end if;
when i_ar2 =>
init_state <= i_trf2; --第2次自刷新
when i_trf2 =>
if clk_cnt = TRFC_CLK_CNT then --等待第2次自刷新结束,TRFC_CLK个时钟周期
init_state <= i_mrs;
else
init_state <= i_trf2;
end if;
when i_mrs =>
init_state <= i_tmrd; --模式寄存器设置(MRS)
when i_tmrd =>
if clk_cnt = TMRD_CLK_CNT then --等待模式寄存器设置完成,TMRD_CLK个时钟周期
init_state <= i_down;
else
init_state <= i_tmrd;
end if;
when i_down =>
sdram_init_down <= '1';
when others =>
init_state <= i_nop;
end case;
end if;
end process;

--产生SDRAM时序操作的延时
process(i_clk, i_rst_n)
begin
if i_rst_n = '0' then
clk_cnt <= 0;
elsif rising_edge(i_clk) then
if rst_clk_cnt_n = '0' then
clk_cnt <= 0;
else
clk_cnt <= clk_cnt + 1;
end if;
end if;
end process;

--计数器控制逻辑
process(i_rst_n, init_state, work_state, clk_cnt, i_sdram_wr_byte, i_sdram_rd_byte )
begin
if i_rst_n = '0' then
end_rdburst<= '0';
end_wrburst<= '0';
else
case init_state is
when i_nop =>
rst_clk_cnt_n <= '0';
when i_pre =>
rst_clk_cnt_n <= '1'; --预充电延时计数启动
when i_trp =>
if clk_cnt = TRP_CLK_CNT then --等待预充电延时计数结束后,清零计数器
rst_clk_cnt_n <= '0';
else
rst_clk_cnt_n <= '1';
end if;
when i_ar1 | i_ar2=>
rst_clk_cnt_n <= '1'; --自刷新延时计数启动
when i_trf1 | i_trf2 =>
if clk_cnt = TRFC_CLK_CNT then --等待自刷新延时计数结束后,清零计数器
rst_clk_cnt_n <= '0';
else
rst_clk_cnt_n <= '1';
end if;
when i_mrs =>
rst_clk_cnt_n <= '1'; --模式寄存器设置延时计数启动
when i_tmrd =>
if clk_cnt = TMRD_CLK_CNT then --等待自刷新延时计数结束后,清零计数器
rst_clk_cnt_n <= '0';
else
rst_clk_cnt_n <= '1';
end if;
when i_down =>
case work_state is
when w_idle =>
rst_clk_cnt_n <= '0';

end_rdburst<= '0';
end_wrburst<= '0';
when w_active =>
rst_clk_cnt_n <= '1';
when w_trcd =>
if clk_cnt = TRCD_CLK_CNT - 1 then
rst_clk_cnt_n <= '0';
else
rst_clk_cnt_n <= '1';
end if;
when w_cl =>
if clk_cnt = TCL_CLK_CNT - 1 then
rst_clk_cnt_n <= '0';
else
rst_clk_cnt_n <= '1';
end if;
when w_rd =>
if clk_cnt = conv_integer(i_sdram_rd_byte) - 4 then
end_rdburst <= '1';
elsif clk_cnt = conv_integer(i_sdram_rd_byte) + 2 then
rst_clk_cnt_n <= '0' ;
else
rst_clk_cnt_n <= '1' ;
end if;
when w_rwait =>
if clk_cnt = TRP_CLK_CNT then
rst_clk_cnt_n <= '0';
else
rst_clk_cnt_n <= '1';
end if;
when w_wd =>
if clk_cnt = conv_integer(i_sdram_wr_byte) - 1 then
end_wrburst<= '1';
rst_clk_cnt_n <= '0';
else
rst_clk_cnt_n <= '1';
end if;
when w_tdal =>
if clk_cnt = TDAL_CLK_CNT then
rst_clk_cnt_n <= '0';
else
rst_clk_cnt_n <= '1';
end if;
when w_trfc =>
if clk_cnt = TRFC_CLK_CNT then
rst_clk_cnt_n <= '0';
else
rst_clk_cnt_n <= '1';
end if;
when others =>
rst_clk_cnt_n <= '0';
end case;
end case;
end if;
end process;


--7.32us计时,每60ms全部8192行存储区进行一次自刷新(存储体中电容的数据有效保存期上限是64ms)
process(i_clk, i_rst_n)
begin
if i_rst_n = '0' then
refresh_delay_cnt <= (others=>'0');
elsif rising_edge(i_clk) then
if refresh_delay_cnt < conv_std_logic_vector(732, 15) then
refresh_delay_cnt <= refresh_delay_cnt + 1;
else
refresh_delay_cnt <= (others=>'0');
end if;
end if;
end process;
process(i_clk, i_rst_n)
begin
if i_rst_n = '0' then
sdram_ref_req <= '0';
elsif rising_edge(i_clk) then
if refresh_delay_cnt = conv_std_logic_vector(731, 15) then
sdram_ref_req <= '1'; --产生自刷新请求
elsif sdram_ref_ack = '1' then
sdram_ref_req <= '0'; --已响应自刷新
end if;
end if;
end process;


--SDRAM的读写以及自刷新操作状态机
process(i_clk, i_rst_n)
begin
if i_rst_n = '0' then
work_state <= w_idle;
elsif rising_edge(i_clk) then
case work_state is
when w_idle => --初始化空闲状态
if sdram_ref_req = '1' and sdram_init_down = '1' then --定时自刷新请求
work_state <= w_ar;
sys_r_wn <= '1';
elsif i_sdram_wr_req = '1' and sdram_init_down = '1' then --写SDRAM请求
work_state <= w_active;
sys_r_wn <= '0';
elsif i_sdram_rd_req = '1' and sdram_init_down = '1' then --读SDRAM请求
work_state <= w_active;
sys_r_wn <= '1';
else
work_state <= w_idle;
sys_r_wn <= '1';
end if;
---------------------------------------------------------
when w_active => --行有效状态
if TRCD_CLK_CNT = 0 then
if sys_r_wn = '1' then
work_state <= w_read;
else
work_state <= w_write;
end if;
else
work_state <= w_trcd;
end if;
when w_trcd => --行有效等待
if clk_cnt = TRCD_CLK_CNT - 1 then
if sys_r_wn = '1' then
work_state <= w_read;
else
work_state <= w_write;
end if;
end if;
----------------------------------------------------------
when w_read => --读数据状态
work_state <= w_cl;
when w_cl => --等待潜伏期
if clk_cnt = TCL_CLK_CNT - 1 then
work_state <= w_rd;
end if;
when w_rd => --读数据
if clk_cnt = conv_integer(i_sdram_rd_byte) + 2 then
work_state <= w_idle;
end if;
when w_rwait => --读完成后的预充电等待状态
if clk_cnt = TRP_CLK_CNT then
work_state <= w_idle;
end if;
-----------------------------------------------------------
when w_write => --写数据状态
work_state <= w_wd;
when w_wd => --写数据
if clk_cnt = conv_integer(i_sdram_wr_byte) - 1 then
work_state <= w_tdal;
end if;
when w_tdal => --等待写数据并自刷新结束
if clk_cnt = TDAL_CLK_CNT then
work_state <= w_idle;
end if;
------------------------------------------------------------
when w_ar => --自动刷新状态
work_state <= w_trfc;
when w_trfc => --自刷新等待
if clk_cnt = TRFC_CLK_CNT then
work_state <= w_idle;
end if;
------------------------------------------------------------
when others =>
work_state <= w_idle;
end case;
end if;
end process;


--IO Connect
o_sdram_init_down <= sdram_init_down;
o_sys_r_wn <= sys_r_wn;
o_clk_cnt <= clk_cnt;

o_end_rdburst <= end_rdburst;
o_end_wrburst <= end_wrburst;

o_init_state <= init_state;
o_work_state <= work_state;

sdram_ref_ack <= '1' when (work_state = w_ar) else '0';

o_sdram_busy <= '0' when (sdram_init_down = '1' and work_state = w_idle) else '1';

--要提前一个时钟才能写入
--写SDRAM响应信号
o_sdram_wr_ack <= '1' when (((work_state = w_trcd) and (sys_r_wn = '0')) or
(work_state = w_write) or
((work_state = w_wd) and (clk_cnt < (conv_integer(i_sdram_wr_byte) - 2)))) else '0';
--读SDRAM响应信号
o_sdram_rd_ack <= '1' when ((work_state = w_rd) and (clk_cnt >= 1) and (clk_cnt < conv_integer(i_sdram_rd_byte) + 1)) else '0';

end architecture behave;

sdram_cmd模块


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

library work;
use work.inc_type.all;

entity sdram_cmd is
port
(
i_clk : in std_logic; -- 系统时钟
i_rst_n : in std_logic; -- 复位信号,低电平有效

o_sdram_cke : out std_logic;
o_sdram_cs_n : out std_logic;
o_sdram_ras_n : out std_logic;
o_sdram_cas_n : out std_logic;
o_sdram_we_n : out std_logic;
o_sdram_ba : out std_logic_vector(1 downto 0);
o_sdram_addr : out std_logic_vector(12 downto 0);

i_end_rdburst : in std_logic;
i_end_wrburst : in std_logic;

i_sys_wraddr : in std_logic_vector(23 downto 0);
i_sys_rdaddr : in std_logic_vector(23 downto 0);
i_sdram_wr_byte : in std_logic_vector(8 downto 0);-- 突发写SDRAM字节数(1-256个)
i_sdram_rd_byte : in std_logic_vector(8 downto 0);-- 突发读SDRAM字节数(1-256个)

i_init_state : in sdram_init_state;
i_work_state : in sdram_work_state;
--i_clk_cnt : in integer range 0 to 200;
i_sys_r_wn : in std_logic -- SDRAM读/写控制信号
);

end entity sdram_cmd;

architecture behave of sdram_cmd is

------------------signal-----------------------------
-- SDRAM commands combine SDRAM inputs: cke, cs, ras, cas, we.
subtype cmd_type is unsigned(4 downto 0);

constant CMD_ACTIVATE : cmd_type := "10011";
constant CMD_PRECHARGE : cmd_type := "10010";
constant CMD_WRITE : cmd_type := "10100";
constant CMD_READ : cmd_type := "10101";
constant CMD_MODE : cmd_type := "10000";
constant CMD_NOP : cmd_type := "10111";
constant CMD_REFRESH : cmd_type := "10001";
constant CMD_INIT : cmd_type := "01111";
constant CMD_BURST_STOP : cmd_type := "10110";
-----------------------------------------------------

signal sys_addr : std_logic_vector(23 downto 0); --读写SDRAM时地址暂存器,(bit23-22)L-Bank地址:(bit21-9)为行地址,(bit8-0)为列地址

signal sdram_cmd : cmd_type;

signal sdram_ba : std_logic_vector(1 downto 0);
signal sdram_addr : std_logic_vector(12 downto 0);
-----------------------------------------------------
begin

process(i_clk, i_rst_n)
begin
if i_rst_n = '0' then
sdram_cmd <= CMD_INIT; --INIT COMMAND
sdram_ba <= "11";
sdram_addr <= "1" & x"fff";
elsif rising_edge(i_clk) then
case i_init_state is
when i_nop | i_trp | i_trf1 | i_trf2 | i_tmrd =>
sdram_cmd <= CMD_NOP; -- NOP COMMAND
sdram_ba <= "11";
sdram_addr <= "1" & x"fff";
when i_pre =>
sdram_cmd <= CMD_PRECHARGE; -- PRECHARGE COMMAND
sdram_ba <= "11";
sdram_addr <= "1" & x"fff";
when i_ar1 | i_ar2 =>
sdram_cmd <= CMD_REFRESH; -- AOTO REFRESH COMMAND
sdram_ba <= "11";
sdram_addr <= "1" & x"fff";
when i_mrs => --模式寄存器设置,可根据实际需要进行设置
sdram_cmd <= CMD_MODE; -- LODE MODE REGISTER COMMAND
sdram_ba <= "00"; --操作模式设置
sdram_addr <=
"000" & --操作模式设置
"0" & --操作模式设置(这里设置为A9=0,即突发读/突发写)
"00" & --操作模式设置({A8,A7}=00),当前操作为模式寄存器设置
"011" & -- CAS潜伏期设置(这里设置为3,{A6,A5,A4}=011)()
"0" & --突发传输方式(这里设置为顺序,A3=b0)
"111"; --突发长度(这里设置为256,{A2,A1,A0}=111)
when i_down =>
case i_work_state is
when w_idle | w_trcd | w_cl | w_trfc | w_tdal =>
sdram_cmd <= CMD_NOP; -- NOP COMMAND
sdram_ba <= "11";
sdram_addr <= "1" & x"fff";
when w_active =>
sdram_cmd <= CMD_ACTIVATE; --ACTIVE COMMAND
sdram_ba <= sys_addr(23 downto 22); --L-Bank地址
sdram_addr <= sys_addr(21 downto 9); --行地址
when w_read =>
sdram_cmd <= CMD_READ; -- READ COMMADN
sdram_ba <= sys_addr(23 downto 22); --L-Bank地址
sdram_addr <=
"0010" & -- A10=1,设置写完成允许预充电
sys_addr(8 downto 0); --列地址
when w_rd =>
if i_end_rdburst = '1' then
sdram_cmd <= CMD_BURST_STOP; -- BURST STOP
else
sdram_cmd <= CMD_NOP; -- NOP COMMAND
sdram_ba <= "11";
sdram_addr <= "1" & x"fff";
end if;
when w_write =>
sdram_cmd <= CMD_WRITE; -- WRITE COMMAND
sdram_ba <= sys_addr(23 downto 22); --L-Bank地址
sdram_addr <=
"0010" & -- A10=1,设置写完成允许预充电
sys_addr(8 downto 0); --列地址
when w_wd =>
if i_end_wrburst = '1' then
sdram_cmd <= CMD_BURST_STOP; -- BURST STOP
else
sdram_cmd <= CMD_NOP; -- NOP COMMAND
sdram_ba <= "11";
sdram_addr <= "1" & x"fff";
end if;
when w_ar =>
sdram_cmd <= CMD_REFRESH; -- AOTO REFRESH
sdram_ba <= "11";
sdram_addr <= "1" & x"fff";
when others =>
sdram_cmd <= CMD_NOP; -- NOP COMMAND
sdram_ba <= "11";
sdram_addr <= "1" & x"fff";
end case;
when others =>
sdram_cmd <= CMD_NOP; -- NOP COMMAND
sdram_ba <= "11";
sdram_addr <= "1" & x"fff";
end case;

end if;
end process;

---------IO Connect-------------
sys_addr <= i_sys_rdaddr when i_sys_r_wn = '1' else i_sys_wraddr; --读/写地址总线切换控制

(o_sdram_cke,o_sdram_cs_n,o_sdram_ras_n,o_sdram_cas_n,o_sdram_we_n) <= sdram_cmd;
--o_sdram_cke <= sdram_cmd(4);
--o_sdram_cs_n <= sdram_cmd(3);
--o_sdram_ras_n <= sdram_cmd(2);
--o_sdram_cas_n <= sdram_cmd(1);
--o_sdram_we_n <= sdram_cmd(0);

o_sdram_ba <= sdram_ba;
o_sdram_addr <= sdram_addr;
----------------------------------


end architecture behave;

sdram_wr 模块


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

library work;
use work.inc_type.all;

entity sdram_wr is

port
(
i_clk : in std_logic; -- 系统时钟
i_sdram_clk : in std_logic;
i_rst_n : in std_logic; -- 复位信号,低电平有效

i_sdram_date : in std_logic_vector(15 downto 0);
o_sdram_data : out std_logic_vector(15 downto 0);

io_sdram_dq : inout std_logic_vector(15 downto 0);
o_sdram_clk : out std_logic;

i_work_state : in sdram_work_state
-- i_clk_cnt : in integer range 0 to 200
);

end entity sdram_wr;

architecture behave of sdram_wr is

signal sdr_din : std_logic_vector(15 downto 0); --突发数据写寄存器
signal sdr_dout : std_logic_vector(15 downto 0); --突发数据读寄存器

signal sdr_dlink : std_logic; -- SDRAM数据总线输入输出控制

begin

----------------------------数据写入控制---------------------------------------------
--将待写入数据送到SDRAM数据总线上
process(i_clk, i_rst_n)
begin
if i_rst_n = '0' then
sdr_din <= (others => '0'); --突发数据写寄存器复位
elsif rising_edge(i_clk) then
if (i_work_state = w_write) or (i_work_state = w_wd) then
sdr_din <= i_sdram_date; --连续写入存储在wrFIFO中的256个16bit数据
end if;
end if;
end process;

--产生双向数据线方向控制逻辑
process(i_clk, i_rst_n)
begin
if i_rst_n = '0' then
sdr_dlink <= '0';
elsif rising_edge(i_clk) then
if (i_work_state = w_write) or (i_work_state = w_wd) then
sdr_dlink <= '1';
else
sdr_dlink <= '0';
end if;
end if;
end process;
----------------------------------------------------------------------------------

----------------------------数据读出控制--------------------------------------------
--将数据从SDRAM读出
process(i_clk, i_rst_n)
begin
if i_rst_n = '0' then
sdr_dout <= (others => '0'); --突发数据读寄存器复位
elsif rising_edge(i_clk) then
if (i_work_state = w_rd) then
sdr_dout <= io_sdram_dq; --连续读出8B的16bit数据存储到rdFIFO中
end if;
end if;
end process;
----------------------------------------------------------------------------------

io_sdram_dq <= sdr_din when sdr_dlink = '1' else (others => 'Z');
o_sdram_clk <= i_sdram_clk;
o_sdram_data <= sdr_dout;

end architecture behave;

×