FC2カウンター FPGAの部屋 CMOSカメラから画像を入力してディスプレイへ出力9(モデルの説明)
fc2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

CMOSカメラから画像を入力してディスプレイへ出力9(モデルの説明)

CMOSカメラ・ディスプレイ表示回路もだいぶシミュレーションが進んできた。今日は、モデルがどのように書いてあるかを書こうと思う。
最初にもう一度、全体のブロック図を下に示す。
Camera_Disp_4_090921.png

全体のブロック図のなかのCMOSカメラとSRAMのモデルを作成した。

まずはCMOSカメラのモデルから。CMOSカメラのモデルはモードが固定となっている。本当のCMOSカメラはI2Cでモードを設定することができるが、デフォルトの設定のまま使用するので、モデルもデフォルトの640x480ピクセル、30フレーム、UYVYデータ出力に固定されている。下のVHDLコードがCMOSカメラモデルだ。

-- OV7640_Model.vhd-- 現在のOV7640の仕様機能のみをモデル化する
-- Yデータは0から+1していく。UデータはFFから2つずつ減算(つまりずっと奇数)、VデータはFEから2つずつ減算(つまりずっと偶数)
-- clkに対してpclkは5ns程度遅延させる

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

entity OV7640_Model is
    port(
        clk : in std_logic; -- クロック入力
        n_reset : in std_logic; -- リセット入力
        vsync : out std_logic;
        href : out std_logic;
        pclk : out std_logic;
        ydata : out std_logic_vector(7 downto 0)
        -- sio_c : in std_logic;
        -- sio_d : inout std_logic
    );
end OV7640_Model;

architecture BEHAVIOR of OV7640_Model is
constant H_ACTIVE_VIDEO : integer := 640;
constant H_BLANK_SPACE : integer := 124;
constant H_SUM : integer := H_ACTIVE_VIDEO + H_BLANK_SPACE;

constant V_ACTIVE_VIDEO : integer := 480;
constant V_FRONT_PORCH : integer := 31;
constant V_SYNC_PULSE : integer := 3;
constant V_BACK_PORCH : integer := 11;
constant V_SUM : integer := V_ACTIVE_VIDEO + V_FRONT_PORCH + V_SYNC_PULSE + V_BACK_PORCH;

signal h_count : unsigned(10 downto 0); -- 水平カウンタ
signal v_count : unsigned(9 downto 0); -- 垂直カウンタ
signal u_data : unsigned(7 downto 0);
signal v_data : unsigned(7 downto 0);
signal y_data : unsigned(7 downto 0);
signal reset : std_logic;
signal vsync_node : std_logic;
signal ydata_node : std_logic_vector(7 downto 0);
signal pclock : std_logic;
signal href_node : std_logic;
begin
    reset <= not n_reset;
    pclock <= transport clk after 5 ns;
    pclk <= pclock;
    
    -- 水平タイミング生成
    process(reset, pclock) begin
        if reset='1' then
            h_count <= (others => '0');
        elsif pclock'event and pclock='1' then
            if h_count(10 downto 1) < H_SUM-1 then
                h_count <= h_count + 1;
            else -- CMOSカメラはUYなどと2クロックで1画素のため
                h_count <= (others => '0');
            end if;
        end if;
    end process;
    href_node <= '1' when h_count<H_ACTIVE_VIDEO and v_count<V_ACTIVE_VIDEO else '0';
    href <= href_node after 1 ns;
    
    -- 垂直タイミング
    process(reset, pclock) begin
        if reset='1' then
            v_count <= unsigned(CONV_STD_LOGIC_VECTOR((V_ACTIVE_VIDEO + V_FRONT_PORCH)-1,10));
        elsif pclock'event and pclock='1' then
            if h_count(10 downto 0)=H_SUM-1 then
                if v_count < V_SUM-1 then
                    v_count <= v_count + 1;
                else
                    v_count <= (others => '0');
                end if;
            end if;
        end if;
    end process;
    
    -- vsync の出力タイミング
    process(v_count) begin
        if v_count>=(V_ACTIVE_VIDEO + V_FRONT_PORCH)-1 then
            if v_count<V_ACTIVE_VIDEO + V_FRONT_PORCH + V_SYNC_PULSE then
                vsync_node <= '1';
            else
                vsync_node <= '0';
            end if;
        else
            vsync_node <= '0';
        end if;
    end process;
    vsync <= vsync_node after 1 ns;
    
    -- ydata の出力
    process(reset, pclock) begin -- Yデータ
        if reset='1' then
            y_data <= (others => '0');
        elsif pclock'event and pclock='1' then
            if h_count(10 downto 1)<H_ACTIVE_VIDEO and h_count(0)='1' then -- 水平の表示区間で2クロックに1回カウントアップ
                y_data <= y_data + 1;
            elsif h_count=0 and v_count=0 then -- 1フレーム描画の最初にリセット
                y_data <= (others => '0');
            end if;
        end if;
    end process;
    
    process(reset, pclock) begin -- Uデータ
        if reset='1' then
            u_data <= (others => '1');
        elsif pclock'event and pclock='1' then
            if h_count(10 downto 1)<H_ACTIVE_VIDEO and h_count(1 downto 0)=0 then -- 水平の表示区間で4クロックに1回-2
                u_data <= u_data - 2;
            elsif h_count=0 and v_count=0 then -- 1フレーム描画の最初にリセット
                u_data <= (others => '1');
            end if;
        end if;
    end process;
    
    process(reset, pclock) begin -- Vデータ
        if reset='1' then
            v_data <= X"FE";
        elsif pclock'event and pclock='1' then
            if h_count(10 downto 1)<H_ACTIVE_VIDEO and h_count(1 downto 0)=2 then -- 水平の表示区間で4クロックに1回-2
                v_data <= v_data - 2;
            elsif h_count=0 and v_count=0 then -- 1フレーム描画の最初にリセット
                v_data <= X"FE";
            end if;
        end if;
    end process;

    process(h_count, y_data, u_data, v_data) begin
        if h_count(10 downto 1)<H_ACTIVE_VIDEO then -- 表示区間
            case h_count(1 downto 0) is
                when "00" =>
                    ydata_node <= std_logic_vector(u_data);
                when "01" =>
                    ydata_node <= std_logic_vector(y_data);
                when "10" =>
                    ydata_node <= std_logic_vector(v_data);
                when others => -- "11"
                    ydata_node <= std_logic_vector(y_data);
            end case;
        else
            ydata_node <= (others => '0');
        end if;
    end process;
    ydata <= ydata_node after 1 ns;
end BEHAVIOR;


当然ながら、h_countとv_countを持って、水平と垂直をカウントしている。それでVSYNCとHREFを出力する。シミュレーション用に最初の方にVSYNCが出て、その後にHREFが出るようにしている。そうでないとSYNCHRONIZERで同期するのに時間がかかりすぎてしまう。
Yデータは0からカウントアップ、UデータはUデータはFFから2つずつ減算(つまりずっと奇数)、VデータはFEから2つずつ減算(つまりずっと偶数)となる。これはシミュレーションの時にわかりやすくするため。つまり、Yではなく間違ってU, Vを受け取っていても、すぐにわかるようにしている。
clkに対してpclkは5ns程度遅延させている。

次に昨日も少し書いたが、SRAMのモデル。最初は256KX8を2つインスタンシエーションしていたが、メモリを食いすぎるので、64KX2を2つに変更した。わざわざbit_vector にしたのだが、やはりメモリを食うのか?下にVHDLソースを示す。

-- IS61LV25616_model.vhd
--
-- IS61LV25616のモデル。タイミング関係は全く考慮せずに動作だけをモデル化する
-- 256KB入れると厳しいので、1/4にした

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

library modelsim_lib;
use modelsim_lib.util.all;

entity IS61LV25616 is
    port(
        mem_data : inout std_logic_vector(15 downto 0);    -- SRAMのmemory data
        n_mem_we : in std_logic; -- SRAMのmemory write enable
        n_mem_rd : in std_logic; -- SRAMのmemory read enable
        n_mem_cs : in std_logic; -- SRAMのchip select 0
        n_mem_lowerB : in std_logic; -- SRAMのmemory lower byte enable
        n_mem_upperB : in std_logic; -- SRAMのmemory upper byte enable
        mem_addr : in std_logic_vector(17 downto 0) -- SRAMのmemory address
    );
end IS61LV25616;

architecture RTL of IS61LV25616 is
type SRAM256K is array (0 to 65535) of bit_vector(7 downto 0);
signal sram_upper : SRAM256K;
signal sram_lower : SRAM256K;
signal mem_addr_int : integer range 0 to 65535;
signal mem_data_node : std_logic_vector(15 downto 0);
signal mem_data_node2 : std_logic_vector(15 downto 0);
signal master_sync : std_logic;

begin
    mem_addr_int <= CONV_INTEGER(mem_addr(15 downto 0));
    
    -- Write Upper
    process(n_mem_we, n_mem_cs, n_mem_upperB, mem_data, mem_addr_int) begin
        if n_mem_we='0' and n_mem_cs='0' and n_mem_upperB='0' then
            sram_upper(mem_addr_int) <= To_bitvector(mem_data(15 downto 8));
        end if;
    end process;
    
    -- Write Lower
    process(n_mem_we, n_mem_cs, n_mem_lowerB, mem_data, mem_addr_int) begin
        if n_mem_we='0' and n_mem_cs='0' and n_mem_lowerB='0' then
            sram_lower(mem_addr_int) <= To_bitvector(mem_data(7 downto 0));
        end if;
    end process;
    
    -- -- Read Upper
    -- process(n_mem_cs, n_mem_rd, n_mem_upperB, mem_addr_int) begin
        -- if n_mem_cs='0' and n_mem_rd='0' and n_mem_upperB='0' then
            -- mem_data_node(15 downto 8) <= To_stdlogicvector(sram_upper(mem_addr_int));
        -- else
            -- mem_data_node(15 downto 8) <= (others => 'Z');
        -- end if;
    -- end process;
    
    -- -- Read Lower
    -- process(n_mem_cs, n_mem_rd, n_mem_lowerB, mem_addr_int) begin
        -- if n_mem_cs='0' and n_mem_rd='0' and n_mem_lowerB='0' then
            -- mem_data_node(7 downto 0) <= To_stdlogicvector(sram_lower(mem_addr_int));
        -- else
            -- mem_data_node(7 downto 0) <= (others => 'Z');
        -- end if;
    -- end process;
    
    -- init_signal_spy でIS61LV25616_instの下にmaster_syncを持ってくる
    process begin
        init_signal_spy("../camdispcntrl_sram_inst/master_sync", "master_sync", 1, -1);
        wait;
    end process;
        
    -- データの流れを見やすいように上位のデータは55から+1, 下位のデータはAAから+1して、出力
    process(master_sync, n_mem_we) begin -- Read Upper
        if master_sync='1' then
            mem_data_node2(15 downto 8) <= x"55";
        elsif n_mem_we'event and n_mem_we='1' then
            if n_mem_cs='0' and n_mem_upperB='0' then
                mem_data_node2(15 downto 8) <= mem_data_node2(15 downto 8) + 1;
            end if;
        end if;
    end process;
    mem_data_node(15 downto 8) <= mem_data_node2(15 downto 8) when n_mem_cs='0' and n_mem_rd='0' and n_mem_upperB='0' else (others => 'Z');
        
    process(master_sync, n_mem_we) begin -- Read Lower
        if master_sync='1' then
            mem_data_node2(7 downto 0) <= x"AA";
        elsif n_mem_we'event and n_mem_we='1' then
            if n_mem_cs='0' and n_mem_lowerB='0' then
                mem_data_node2(7 downto 0) <= mem_data_node2(7 downto 0) + 1;
            end if;
        end if;
    end process;
    mem_data_node(7 downto 0) <= mem_data_node2(7 downto 0) when n_mem_cs='0' and n_mem_rd='0' and n_mem_lowerB='0' else (others => 'Z');
        
    mem_data <= mem_data_node after 1 ns;
end RTL;


CMOSカメラの転送レートはディスプレイ表示回路の半分なので、RGB出力は最初の垂直フレームはずっと0のままになってしまう。そうすると40msくらいシミュレーションしないとだめということになる。それかクロックを驚異的に速めるという方法もあるな~。
そこでSRAMモデルの方でReadの時に、SRAMに書き込まれたデータではなく、自分で生成したデータを出して回路をデバックしようと思った。(昨日の引用)つまり、データの流れを見やすいように上位のデータは55から+1, 下位のデータはAAから+1して、出力することにした。

このようにモデルを書いた。次はシミュレーション波形の説明をしたい。

#今日は、子供会の旅行でディズニーシーに行きます。子供会の幹事なので行くのですが、家の子供は誰も行かないで夫婦だけです。(変ですよね)ウォーキングしてきます。。。

(2009/10/13:追記)
IS61LV25616_model.vhd が間違っていたので修正しました。
(2009/10/14:修正)
 OV7640_Model.vhd のv_countが2倍になっていたので修正。
  1. 2009年10月11日 05:21 |
  2. 画像処理
  3. | トラックバック:0
  4. | コメント:5

コメント

お願いします

突然すいません!!
今、CMOSカメラから画像を入力してディスプレイへ出力という、まったく同じテーマで卒研をしているのですが、I2Cでモードを設定することができません。
YUV信号をRGB信号の565に設定したいです。
FPGAはまったくの初心者なので困り果てています。
できれば、教えてほしいのですが。
お願いします。
  1. 2009/12/04(金) 11:20:34 |
  2. URL |
  3. 電子 #-
  4. [ 編集 ]

電子さん、こんにちは。

いまから、FPGA初心者で初歩からやるとなると、卒論発表に間に合いませんね。私と同じ構成の基板なんでしょうか?I2Cでモードを設定することができないと言っても、原因は幾つも考えられますね。
CQ出版で出版されていたデザイン・ウェーブ・マガジン誌の2007年10月号にそのものズバリが載っていますので、購入されてはいかがでしょうか?
http://www.cqpub.co.jp/DWM/contents/0119/dwm011900850.pdf

FPGA初心者で、今から卒論で1から回路をつくるということは、かなり無理があると思われますので、指導教員にご相談されることをおすすめします。
  1. 2009/12/04(金) 11:39:43 |
  2. URL |
  3. marsee #f1oWVgn2
  4. [ 編集 ]

返信ありがとうございます。

すいません説明不足で、、
FPGAの基本的なことはできます。
とはいっても授業でストップウォッチを設計した程度で。泣
後は紹介していただいた本にのっている回路と基板を実際に使用しています。
そして、その参考書は指導教員にコピーをいただきました。
I2Cの設定方法は理屈では理解しているつもりなんですが。。。
クロックとアドレスを指定してデータを入力するタイミングなどが、どのように記述したらいいのか、、、、、
実際にいざプログラムに入るとお手上げ状態なんで、、、
C言語だといけそうなんですがFPGAは難しいです!泣

一方的なお願いですいません。
  1. 2009/12/04(金) 16:09:29 |
  2. URL |
  3. 電子 #-
  4. [ 編集 ]

すいません訂正です

http://www.cqpub.co.jp/dwm/contents/0117/dwm011700520.pdf#search='VGA CMOSカメラ・モジュール'
この参考書でした、、、
  1. 2009/12/04(金) 16:16:26 |
  2. URL |
  3. 電子 #-
  4. [ 編集 ]

I2Cはぐぐってもいろいろ情報があります。それにDWMの2007年11月号にCDがついていて、サンプルプロジェクトがついていたはずです。それのVHDLコードを読めばI2Cの使い方がわかるはずというか、I2C通信をするモジュールがついていますよ。(FM_I2C_CTRL.vhd) それを使ってください。その使い方がわからないのでしたら、指導教員の方にお聞きになってはいかがでしょうか?
もしそれで分からないようでしたら、厳しいようですが、私が無料でサポートできるレベルに達していないということになると思います。
遠隔デバックは難しいものです。十分なサポートはできません。それに指導教員の方がどういうご意向かわかりませんし。。。身近な方に聞かれるのが良いと思います。
  1. 2009/12/04(金) 17:14:14 |
  2. URL |
  3. marsee #f1oWVgn2
  4. [ 編集 ]

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
https://marsee101.blog.fc2.com/tb.php/1254-bd4567ed
この記事にトラックバックする(FC2ブログユーザー)