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