目次
前
次
GBC処理変更
従来のGameBoyCamera処理は、全画面データを入力後
必要な内容を抽出していました。
これで、どの程度の時間が必要になっていたのかを
大まかに計算してみます。
- データ取得時間=16秒 128x128(16kbyte)に1ラインを128msで、その128倍
- 2値化処理時間=2秒
18秒近くかかっていては、マシンを止めて動かさない限り
移動できません。
また、16kバイトのメモリを外部に用意するのは面倒です。
この処理時間を短縮する検討を始めます。
目標設定しないと、効果も期待できないので、3つのゴールを考えます。
- メモリは、1kバイトまで許可
- メモリは、外付けしない
- データ取得時間=2秒以内
ゴールを一つ一つクリアしていくプロセスを説明します。
メモリを1kバイトに制限
GBCから出力されるデータは、128x128(16kbyte)です。
計算で、出力されてくるデータ1/16にすれば達成できます。
移動しながら、画像データを取得するので、画像データの
全ライン中、偶数ラインだけを抽出し、128x64(8kbyte)に
します。
1ラインは128ピクセルあるので、偶数ピクセルだけを抽出し
64x64(4kbyte)にします。
これまでの処理で、1/4までにデータが圧縮されます。
実際の画像は、どんな具合に見えるのかをTcl/Tkを利用し
テストしました。
元画像は、以下のものを利用しました。
圧縮すると、以下の画像になります。
肉眼で見る限り、充分認識できる画像になります。
1ピクセルを8ビットで扱うので、4kbyteになっていますが
1ピクセルを1ビットに変換すると、512バイトになります。
これで、ゴールを達成できます。
1ピクセルを8ビットから1ビットに変換すると、次の画像
になります。(見やすいよう、少し拡大)
実際に利用した画像データは、PGM形式なので、Tcl/Tkでこの
変換フィルタプログラムを作成し、検証しました。
128x128を64x64のPGM形式に変換するコードです。
(UNIX、WindowsのGUIアプリケーションにしました。)
#!/bin/sh
wm title . "PGM converter"
# clear file names
set fname ""
set pname ""
set tname ""
# add menu on TopLevel
. configure -menu .mnuTop
menu .mnuTop
# add sub form
.mnuTop add cascade -label File -underline 0 -menu .mnuTop.file
menu .mnuTop.file -tearoff no
# add sub menu "Open"
.mnuTop.file add command -label "Open" -command {OpenText}
# add sub menu "Save"
.mnuTop.file add command -label "Save" -command {SaveImage}
# add sub menu "Quit"
.mnuTop.file add command -label "Quit" -command {exit}
# set window size
set w 1000
set h 1000
label .lblTxt -textvariable fname
label .lblPgm -textvariable pname
pack .lblTxt
pack .lblPgm
#######################
# Open Text procedure
#######################
proc OpenText { } {
global fname pname tname
# set file types
set ftype {{ "pgm Files" .pgm} { "All Files" * }}
# get file name from built-in tool
set fname [tk_getOpenFile -filetypes $ftype -parent . ]
# create file name
set xlist [split $fname "."]
set xlist [split $xlist "/"]
set xlist [lindex $xlist end]
set pname [lindex $xlist 0]
set ext "r"
# text file name
set tname "$pname$ext.txt"
# pgm file name
set pname "$pname$ext.pgm"
}
#######################
# Save Image procedure
#######################
proc SaveImage { } {
global fname pname tname
# open files
set fd_in [open $fname "r"]
set fd_out [open $pname "w"]
set fd_out_txt [open $tname "w"]
# handling header (mode)
gets $fd_in sbuf
puts $fd_out "P2"
# handling header (size)
gets $fd_in sbuf
puts $fd_out "64 64"
# handling header (mode)
gets $fd_in sbuf
puts $fd_out "255"
# handling context
set line 0
while { [gets $fd_in sbuf] >= 0 } {
# judge even line
set sbuf [string trim $sbuf " "]
# judge even line
if { [expr $line & 1] == 0 } {
# initialize
set xbuf ""
set i 0
# get even pixel
foreach e $sbuf {
# judge and store
if { [expr $i & 1] == 0 } {
set xbuf "$xbuf $e"
}
# increment
incr i
}
# store
puts $fd_out $xbuf
puts $fd_out_txt $xbuf
}
# increment
incr line
}
# close
close $fd_in
close $fd_out
close $fd_out_txt
# message
tk_messageBox -type ok -message "reduce pgm_file pgm_file(text_file)"
}
64x64のテキストデータを、2値化画像に変換するコードです。
2値化したままでは、画像表示できないので、0と255に変換
した値をPGM形式で保存します。
(UNIX、WindowsのGUIアプリケーションにしました。)
#!/bin/sh
wm title . "PGM generator extended"
# clear file names
set fname ""
set pname ""
set fd_in ""
set fd_out ""
# add menu on TopLevel
. configure -menu .mnuTop
menu .mnuTop
# add sub form
.mnuTop add cascade -label File -underline 0 -menu .mnuTop.file
menu .mnuTop.file -tearoff no
# add sub menu "Open"
.mnuTop.file add command -label "Open" -command {OpenText}
# add sub menu "Save"
.mnuTop.file add command -label "Save" -command {SaveImage}
# add sub menu "Quit"
.mnuTop.file add command -label "Quit" -command {exit}
# declare image file
image create photo input_image -file $fname
# set window size
set w 1000
set h 1000
label .lblTxt -textvariable fname
label .lblPgm -textvariable pname
pack .lblTxt
pack .lblPgm
#######################
# Open Text procedure
#######################
proc OpenText { } {
global fname pname
# set file types
set ftype {{ "text Files" .txt} { "All Files" * }}
# get file name from built-in tool
set fname [tk_getOpenFile -filetypes $ftype -parent . ]
# create file name
set xlist [split $fname "."]
set xlist [split $xlist "/"]
set xlist [lindex $xlist end]
set fname [lindex $xlist 0]
set ext "_r"
set pname "$fname$ext.pgm"
set fname "$fname.txt"
}
#######################
# Save Image procedure
#######################
proc SaveImage { } {
global fname pname
# open files
set fd_in [open $fname "r"]
set fd_out [open $pname "w"]
# save header
puts $fd_out "P2"
puts $fd_out "64 64"
puts $fd_out "255"
# justify context
while { [gets $fd_in sbuf] >= 0 } {
# trim
set tmp [string trimright $sbuf " "]
# get maximum and minimum
set max 0
set min 255
foreach e $sbuf {
# trim
set val [string trimleft $e "0"]
# compare
if { $val > $max } {
set max $val
}
if { $val < $min } {
set min $val
}
}
# calculate average
set avr [expr ($max + $min) / 2]
# generate code
set obuf ""
foreach e $sbuf {
# trim
set val [string trimleft $e "0"]
# compare
if { $val < $avr } {
set obuf "$obuf 0"
} else {
set obuf "$obuf 255"
}
}
puts $fd_out $obuf
}
# close
close $fd_in
close $fd_out
# message
tk_messageBox -type ok -message "convert text_file pgm_file"
}
メモリは内蔵
128ピクセルx128ラインを64ピクセルx64ラインに変換すると
64ピクセルを2値化して、8バイトx64ラインになります。
計算すると、512バイトになるので1kバイトの配列がある
マイコン内蔵SRAMに保存できます。
2kバイト以上のSRAMをもつマイコンであれば、GBCの制御
を含め、対応できると考えられます。
データ取得時間2秒以内
マイコン内蔵A/Dコンバータを利用する限り、GBCが出力する
Voutをアナログからデジタルの2値変換する処理は、高速に
ならないと判断し、FPGAを利用することにしました。
利用したFPGAは、XilinxのSpartan3、200kゲートです。
大阪のヒューマンデータから購入しました。
GBCで利用している人工網膜チップのアナログ出力電圧Voutは
最大で2Vです。上のボードは、3.3VのI/Oインタフェースを
もつので、Voutを直結することにします。
Voutは、アナログ値なのでデジタル2値に変換するために
可変抵抗器とシリコンダイオードを利用します。
可変抵抗器のトリマーを回し、スレショルドレベルを調整します。
FPGAとGBC、マイクロコンピュータのインタフェースを考えます。
GBCは、+5Vを電源電圧としていますが、入力電圧は2.2V以上をHと
認識するので、FPGAの出力を直結します。
GBCの出力は、Hレベルを4.5V以上としているので、抵抗による分圧
で、3.0Vまで引き下げます。
マイクロコンピュータからの信号で、GBCに対しての制御信号を
出力します。3つのトリガー信号を使います。
- RTRG リセットトリガー
- PTRG パラメータ設定トリガー
- GTRG 画像取得トリガー
GBCの出力した画像データを2値化するとして、保存している
16384ビットのデータを、シリアルインタフェースで出力させます。
この機能のために必要な信号を定義します。
- TxD 16384ビットのデータ出力
- STRG ビットデータ取得トリガー
FPGAが、どういう状態であるのかを示すSTATUS信号を
用意します。
入出力信号が確定したので、VHDLコードを記述します。
entityは、以下とします。
entity gbcx is
generic (
TOPX : integer := 3 ;
RMAX : integer := 6 ;
TOPY : integer := 13 ;
YMAX : integer := 5000 --;
);
port(
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ;
-- trigger
RTRG : in std_logic ;
PTRG : in std_logic ;
GTRG : in std_logic ;
-- status
STATUS : out std_logic_vector(1 downto 0) ;
-- input from GBC
GREAD : in std_logic ;
VOUT : in std_logic ;
-- Game Boy Camera control
XRST : out std_logic ;
XCK : out std_logic ;
SIN : out std_logic ;
LOAD : out std_logic ;
START : out std_logic ;
-- serial interface
STRG : in std_logic ;
STxD : out std_logic --;
);
end gbcx;
FPGAの内部を考えます。
GBC、シリアルインタフェースを利用する場合、クロックが必要です。
この2クロックは、異なる周波数としたいので、クロックジェネレータ
を定義して、それを使い回します。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity clkgenx is
generic (
TOPX : integer ;
RMAX : integer --;
);
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ;
-- output
CLKOUT : out std_logic -- ;
);
end clkgenx;
architecture Behavioral of clkgenx is
signal iSCNT : std_logic_vector(TOPX-1 downto 0);
signal iCLK : std_logic ;
begin
-- output
CLKOUT <= iCLK ;
-- divider
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSCNT <= (others => '0') ;
iCLK <= '0' ;
elsif rising_edge(CLOCK) then
if ( conv_integer(iSCNT) = RMAX ) then
iSCNT <= (others => '0') ;
iCLK <= not iCLK ;
else
iSCNT <= iSCNT + '1' ;
end if ;
end if ;
end process ;
end Behavioral;
利用しているボードにあるマスタークロックは48MHzなので
GBCを動かすために4MHzを生成し、それから500kHzを作ります。
CLKX : clkgenx generic map (TOPX,RMAX) port map (nRESET,CLOCK,iMCLK);
iMCLKは、4MHzとなるので、Johnsonカウンタを利用し500kHzを作ります。
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iSTATE <= "00" ;
elsif rising_edge(iMCLK) then
case conv_integer(iSTATE) is
when 0 => iSTATE <= "01" ;
when 1 => iSTATE <= "11" ;
when 3 => iSTATE <= "10" ;
when 2 => iSTATE <= "00" ;
when others =>
iSTATE <= "00" ;
end case ;
end if ;
end process ;
Johnsonカウンタを利用すると、iSTATEのどちらのビットも
500kHzを生成します。
Johnsonカウンタの2ビットを利用し、他ブロックでも使える
信号を作っておきます。
iSTATE(1)は、iXCKそのものに出来ます。
GBCにリセット信号やパラメータを与える場合は、XCKの
立上りエッジを使うので、セットアップタイムを満たす
ように、論理値を出力するタイミングをiVALIDで指定
します。また、Voutを捕まえるためのトリガーをiLTRG
で生成します。
iVALID <= '1' when ( iSTATE = "01" ) else '0' ;
iLTRG <= '1' when ( iSTATE = "11" ) else '0' ;
マイクロコンピュータからの指示で、リセット、パラメータ
設定、画像取得を制御したいので、シンクロナイザをつけて
対応します。
シンクロナイザは、シフトレジスタを利用します。
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iRTRG_SFT <= "000" ;
iPTRG_SFT <= "000" ;
iGTRG_SFT <= "000" ;
iSTRG_SFT <= "000" ;
elsif rising_edge(iMCLK) then
iRTRG_SFT <= iRTRG_SFT(1 downto 0) & RTRG ;
iPTRG_SFT <= iPTRG_SFT(1 downto 0) & PTRG ;
iGTRG_SFT <= iGTRG_SFT(1 downto 0) & GTRG ;
iSTRG_SFT <= iSTRG_SFT(1 downto 0) & STRG ;
end if ;
end process ;
iRTRG <= '1' when ( iRTRG_SFT = "011" or iRTRG_SFT = "001" ) else '0' ;
iPTRG <= '1' when ( iPTRG_SFT = "011" or iPTRG_SFT = "001" ) else '0' ;
iGTRG <= '1' when ( iGTRG_SFT = "011" or iGTRG_SFT = "001" ) else '0' ;
iSTRG <= '1' when ( iSTRG_SFT = "011" or iSTRG_SFT = "001" ) else '0' ;
外部信号をシフトレジスタに順次入れていき、立上がりの
エッジをつかまえます。
各々信号の機能は、以下とします。
- RTRG リセット
- PTRG パラメータ設定
- GTRG 画像入力
- STRG ビットデータ出力
これらのトリガー信号を利用してシーケンサ(ステートマシン)を
動かします。各シーケンサを動かすための同期信号に別途設けます。
リセット、パラメータ設定、画像入力は、これらの監督シーケンサ
(マスターシーケンサ)を用意し、サブシーケンサで対応します。
トリガーを検出すると、マスターは、サブに
トリガーを与え、終了フラグを待ちます。
マスターシーケンサは、以下としました。
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iMSTATE <= 0 ;
elsif rising_edge(CLOCK) then
case iMSTATE is
-- wait trigger
when 0 => if ( iRTRG = '1' ) then
-- reset
iMSTATE <= 1 ;
elsif ( iPTRG = '1' ) then
-- set parameters
iMSTATE <= 3 ;
elsif ( iGTRG = '1' ) then
-- get image
iMSTATE <= 5 ;
else
-- stay
iMSTATE <= 0 ;
end if ;
-- enable reset action
when 1 => iMSTATE <= 2 ;
-- exit reset action
when 2 => iMSTATE <= 7 ;
-- enable parameter set sequencer
when 3 => iMSTATE <= 4 ;
-- wait parameter set sequencer complete flag
when 4 => if ( iPFLAG = '1' ) then
iMSTATE <= 7 ;
end if ;
-- enable get image sequencer
when 5 => iMSTATE <= 6 ;
-- wait get image sequencer
when 6 => if ( iGFLAG = '1' ) then
iMSTATE <= 7 ;
end if ;
-- return first state
when 7 => iMSTATE <= 0 ;
when others =>
iMSTATE <= 0 ;
end case ;
end if ;
end process ;
リセットシーケンサは、トリガーを受けたなら
XCKの立上がエッジの前に、信号を出力します。
内部は、正論理で動かします。
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iRSTATE <= "00" ;
iXRST <= '0' ;
elsif rising_edge(CLOCK) then
case conv_integer(iRSTATE) is
-- wait trigger
when 0 => iXRST <= '0' ;
if ( iRTRGX = '1' ) then
iRSTATE <= "01" ;
else
iRSTATE <= "00" ;
end if ;
-- judge wait trigger
when 1 => if ( iVALID = '1' ) then
-- enable reset signal
iXRST <= '1' ;
iRSTATE <= "11" ;
else
iRSTATE <= "01" ;
end if ;
-- judge wait trigger
when 3 => if ( iVALID = '1' ) then
-- disable
iXRST <= '0' ;
iRSTATE <= "10" ;
else
iRSTATE <= "11" ;
end if ;
-- return first state
when 2 => iRSTATE <= "00" ;
when others =>
iXRST <= '0' ;
iRSTATE <= "00" ;
end case ;
end if ;
end process ;
iXCK、iXRST信号は、バッファとインバータを介して出力します。
XCK <= iSTATE(1) ; -- iXCK
XRST <= not iXRST ;
使いやすくするため、別のVHDLコードにしました。
------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity rstx is
port(
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ;
-- trigger
TRG : in std_logic ;
-- enable
ENABLE : in std_logic ;
-- output
XRST : out std_logic -- ;
);
end rstx;
architecture Behavioral of rstx is
signal iRSTATE : std_logic_vector(1 downto 0) ;
signal iXRST : std_logic ;
begin
-- reset sequencer
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iRSTATE <= "00" ;
iXRST <= '0' ;
elsif rising_edge(CLOCK) then
case conv_integer(iRSTATE) is
-- wait trigger
when 0 => iXRST <= '0' ;
if ( TRG = '1' ) then
iRSTATE <= "01" ;
else
iRSTATE <= "00" ;
end if ;
-- judge wait trigger
when 1 => if ( ENABLE = '1' ) then
-- enable reset signal
iXRST <= '1' ;
iRSTATE <= "11" ;
else
iRSTATE <= "01" ;
end if ;
-- judge wait trigger
when 3 => if ( ENABLE = '1' ) then
-- disable reset signal
iXRST <= '0' ;
iRSTATE <= "10" ;
else
iRSTATE <= "11" ;
end if ;
-- return first state
when 2 => iRSTATE <= "00" ;
when others =>
iXRST <= '0' ;
iRSTATE <= "00" ;
end case ;
end if ;
end process ;
XRST <= iXRST ;
end Behavioral;
------------------------------------------------------------
GBCに渡すパラメータは、FPGA内部にメモリ領域を定義し
リセット時に設定します。
マスターシーケンサからのトリガーで、順次パラメータを
出力します。
メモリ領域の指定は、以下とします。
type PARAMP is array(0 to 8) of std_logic_vector(10 downto 0);
signal iPARAMS : PARAMP ;
アドレス3ビット、パラメータ8ビットなので、11ビット幅の
メモリ領域を用意し、9個の配列としておきます。
メモリ領域最終ブロックを常に出力するよう、シーケンサを
回します。この処理を別のVHDLコードで定義し、呼び出して
使います。
パラメータ設定シーケンサは、以下となります。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity psetx is
port(
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ;
-- trigger
TRG : in std_logic ;
-- enable
ENABLE : in std_logic ;
-- flag
FLAG : out std_logic ;
-- output
SIN : out std_logic ;
LOAD : out std_logic -- ;
);
end psetx;
architecture Behavioral of psetx is
signal iPSTATE : integer range 0 to 14 ;
signal iPPTR : integer range 0 to 8 ;
signal iSIN : std_logic ;
signal iLOAD : std_logic ;
signal iFLAG : std_logic ;
type PARAMP is array(0 to 7) of std_logic_vector(10 downto 0);
signal iPARAMS : PARAMP ;
signal iPARAMSX : std_logic_vector(10 downto 0) ;
begin
-- output
SIN <= iSIN ;
LOAD <= iLOAD ;
FLAG <= iFLAG ;
-- sequencer
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iPSTATE <= 0 ;
iPPTR <= 0 ;
-- set paramters
iPARAMS(0) <= "000" & X"80" ; -- register #0
iPARAMS(1) <= "001" & X"03" ; -- register #1
iPARAMS(2) <= "010" & X"00" ; -- register #2
iPARAMS(3) <= "011" & X"30" ; -- register #3
iPARAMS(4) <= "100" & X"01" ; -- register #4
iPARAMS(5) <= "101" & X"00" ; -- register #5
iPARAMS(6) <= "110" & X"01" ; -- register #6
iPARAMS(7) <= "111" & X"21" ; -- register #7
iPARAMSX <= "000" & X"FF" ; -- temporary
elsif rising_edge(CLOCK) then
case iPSTATE is
-- wait trigger
when 0 => if ( TRG = '1' ) then
iPSTATE <= 1 ;
end if ;
-- judge pointer
when 1 => if ( iPPTR > 7 ) then
iPSTATE <= 14 ;
else
iPSTATE <= 2 ;
iPARAMSX <= iPARAMS(iPPTR) ;
end if ;
-- set data)
when 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 =>
if ( ENABLE = '1' ) then
iPSTATE <= iPSTATE + 1 ;
end if ;
-- update pointer
when 13 => iPPTR <= iPPTR + 1 ;
iPSTATE <= 1 ;
-- return first state
when 14 => iPSTATE <= 0 ;
iPPTR <= 0 ;
when others =>
iPSTATE <= 0 ;
iPPTR <= 0 ;
end case ;
end if ;
end process ;
iSIN <= iPARAMSX(10) when ( iPSTATE = 2 ) else
iPARAMSX(9) when ( iPSTATE = 3 ) else
iPARAMSX(8) when ( iPSTATE = 4 ) else
iPARAMSX(7) when ( iPSTATE = 5 ) else
iPARAMSX(6) when ( iPSTATE = 6 ) else
iPARAMSX(5) when ( iPSTATE = 7 ) else
iPARAMSX(4) when ( iPSTATE = 8 ) else
iPARAMSX(3) when ( iPSTATE = 9 ) else
iPARAMSX(2) when ( iPSTATE = 10 ) else
iPARAMSX(1) when ( iPSTATE = 11 ) else
iPARAMSX(0) when ( iPSTATE = 12 ) else
'0' ;
iLOAD <= '1' when ( iPSTATE = 12 ) else '0' ;
iFLAG <= '1' when ( iPSTATE = 14 ) else '0' ;
end Behavioral;
GBCから送られてくる画像データは、アナログ出力でVoutを使います。
2値化処理を外部回路でやるとして、内部には16384ビットのメモリを
用意して、格納しておきたいもの。
そこで、DualPortMemoryを利用することに。
DualPortMemoryは、2つのポートをもち、各ポートでランダムアクセス
が可能になります。GBCから入力したデータを使う場合、GBCとのデータ
処理を気にせずに、画像データを取りだして処理できるようにします。
利用するSpartan3の中には、DualPortMemoryのライブラリが用意されて
いるので、Aポートを1ビットごとにアドレスを付けたメモリとして
Bポートを4ビットごとにアドレスを付けたメモリとしてアクセスできる
記述を選びました。
XilinxのISEWebPackのLanguage Templateから必要な記述を
見つけて、次のように信号を定義しました。
signal iGPTR : std_logic_vector(14 downto 0) ;
signal iGDO : std_logic_vector(0 downto 0);
signal iGDI : std_logic_vector(0 downto 0);
signal iGEN : std_logic ;
signal iGSSR : std_logic ;
signal iGWE : std_logic ;
signal iTPTR : std_logic_vector(12 downto 0) ;
signal iTDO : std_logic_vector(3 downto 0);
signal iTDI : std_logic_vector(3 downto 0);
signal iTEN : std_logic ;
signal iTSSR : std_logic ;
signal iTWE : std_logic ;
RAMB16_S1_S4_inst : RAMB16_S1_S4 port map (
DOA => iGDO , -- Port A 1-bit Data Output
DOB => iTDO , -- Port B 4-bit Data Output
ADDRA => iGPTR(13 downto 0) , -- Port A 14-bit Address Input
ADDRB => iTPTR(11 downto 0) , -- Port B 12-bit Address Input
CLKA => iMCLK , -- Port A Clock
CLKB => iTCLK , -- Port B Clock
DIA => iGDI , -- Port A 1-bit Data Input
DIB => iTDI , -- Port B 4-bit Data Input
ENA => iGEN , -- Port A RAM Enable Input
ENB => iTEN , -- PortB RAM Enable Input
SSRA => iGSSR , -- Port A Synchronous Set/Reset Input
SSRB => iTSSR , -- Port B Synchronous Set/Reset Input
WEA => iGWE , -- Port A Write Enable Input
WEB => iTWE -- Port B Write Enable Input
);
DualPortMemoryのAポートを使って、GBCからの画像データを
メモリ領域に保存します。
単純なシーケンサ処理でまとめました。
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iGSTATE <= 0 ;
iSTART <= '0' ;
iGPTR <= (others => '0') ;
elsif rising_edge(iMCLK) then
case iGSTATE is
-- wait trigger
when 0 => iSTART <= '0' ;
if ( iGTRGX = '1' ) then
iGSTATE <= 1 ;
end if ;
-- enable START
when 1 => if ( iVALID = '1' ) then
iSTART <= '1' ;
iGSTATE <= 2 ;
end if ;
-- disable START
when 2 => if ( iVALID = '1' ) then
iGSTATE <= 3 ;
iGPTR <= (others => '0') ;
end if ;
-- judge READ = '1'
when 3 => if ( GREAD = '1' ) then
iGSTATE <= 4 ;
end if ;
-- store
when 4 => if ( iSTATE = "10" ) then
iGSTATE <= 5 ;
end if ;
-- judge and increment
when 5 => if ( iGPTR > 16383 ) then
iGSTATE <= 6 ;
else
iGPTR <= iGPTR + '1' ;
iGSTATE <= 4 ;
end if ;
-- return frist state
when 6 => iGSTATE <= 0 ;
-- default
when others =>
iGSTATE <= 0 ;
end case ;
end if ;
end process ;
Aポートからのデータは、バッファiGDI(0)経由でクロックiMCLKに
同期してメモリ領域に書き込まれていきます。
ライブラリ仕様にのっとって、他の信号の論理値を決めます。
iGEN <= '1' ; -- Port A RAM Enable Input
iGSSR <= '1' ; -- Port A Synchronous Set/Reset Input
iGWE <= '1' ; -- Port A Write Enable Input
Bポートからのデータは、シリアルインタフェースで出力します。
トリガーを与えられたなら、16384ビットのデータを4ビットずつ
'0'〜'9'、'A'〜'F'に変換して出力することに決めました。
4ビットの数値を1文字のASCIIコードに変換する
VHDLコードを定義します。
4入力8出力の組合せ回路で、単純に定義しました。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity ascdecx is
port(
-- input
Din : in std_logic_vector(3 downto 0) ;
-- output
Dout : out std_logic_vector(7 downto 0) -- ;
);
end ascdecx;
architecture Behavioral of ascdecx is
begin
-- decoder
Dout <= X"30" when ( Din = "0000" ) else -- 0
X"31" when ( Din = "0001" ) else -- 1
X"32" when ( Din = "0010" ) else -- 2
X"33" when ( Din = "0011" ) else -- 3
X"34" when ( Din = "0100" ) else -- 4
X"35" when ( Din = "0101" ) else -- 5
X"36" when ( Din = "0110" ) else -- 6
X"37" when ( Din = "0111" ) else -- 7
X"38" when ( Din = "1000" ) else -- 8
X"39" when ( Din = "1001" ) else -- 9
X"41" when ( Din = "1010" ) else -- A
X"42" when ( Din = "1011" ) else -- B
X"43" when ( Din = "1100" ) else -- C
X"44" when ( Din = "1101" ) else -- D
X"45" when ( Din = "1110" ) else -- E
X"46" when ( Din = "1111" ) else -- F
X"20" ;
end Behavioral;
トリガーを与えられたなら、一気にデータを出力する仕様で
シーケンサを定義します。
process (nRESET,iTCLK)
variable iTPTRX : integer ;
begin
if ( nRESET = '0' ) then
iTSTATE <= 0 ;
iTPTR <= (others => '0') ;
iGSDATN <= X"0" ;
iTDAT <= X"FF" ;
elsif rising_edge( iTCLK ) then
iTPTRX := conv_integer(iTPTR) ;
case iTSTATE is
-- wait trigger
when 0 => if ( iSTRG = '1' ) then
iTSTATE <= 1 ;
iTPTR <= (others => '0') ;
end if ;
-- judge counter
when 1 => if ( iTPTR > 4095 ) then
iTSTATE <= 10 ;
else
iTSTATE <= 2 ;
-- get data
iGSDATN <= iTDO;
end if ;
-- convert ASCII code (both upper nibble)
when 2 => iTSTATE <= 3 ;
-- latch
when 3 => iTSTATE <= 4 ;
iTDAT <= iGSUPDN ;
-- enable trigger
when 4 => iTSTATE <= 5 ;
iTPTR <= iTPTR + 1 ;
-- judge complete flag
when 5 => if ( iTTFLAG = '1' ) then
iTSTATE <= 6 ;
iGSDATN <= iTDO ;
end if ;
-- latch
when 6 => iTSTATE <= 7 ;
iTDAT <= iGSUPDN ;
-- enable trigger
when 7 => iTSTATE <= 8 ;
-- judge complete flag
when 8 => if ( iTTFLAG = '1' ) then
iTSTATE <= 9 ;
end if ;
-- loop
when 9 => iTSTATE <= 1 ;
iTPTR <= iTPTR + 1 ;
-- set delimiter
when 10 => iTSTATE <= 11 ;
iTDAT <= X"0D" ;
-- enable trigger
when 11 => iTSTATE <= 12 ;
-- judge complete flag
when 12 => if ( iTTFLAG = '1' ) then
iTSTATE <= 13 ;
end if ;
-- return first state
when 13 => iTSTATE <= 0 ;
-- default
when others =>
iTSTATE <= 0 ;
end case ;
end if ;
end process ;
iTTRG <= '1' when ( iTSTATE = 4 ) else
'1' when ( iTSTATE = 7 ) else
'1' when ( iTSTATE = 11 ) else
'0' ;
iTFLAG <= '1' when ( iTSTATE = 13 ) else '0' ;
DualPortMemoryのBポートから、4ビットデータを順次
取得して出力します。
8ビットデータをTxDに出力する処理は、別のVHDLコードで
実現しました。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity stxdx is
port(
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ;
-- trigger
TRG : in std_logic ;
-- flag
FLAG : out std_logic ;
-- input
Din : in std_logic_vector(7 downto 0) ;
-- output
TxD : out std_logic -- ;
);
end stxdx;
architecture Behavioral of stxdx is
signal iTxSTATE : integer range 0 to 11 ;
begin
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iTxSTATE <= 0 ;
elsif rising_edge(CLOCK) then
case iTxSTATE is
-- wait trigger
when 0 => if ( TRG = '1' ) then
iTxSTATE <= 1 ;
end if ;
-- start bit
when 1 => iTxSTATE <= iTxSTATE + 1 ;
-- data
when 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 =>
iTxSTATE <= iTxSTATE + 1 ;
-- set flag
when 10 => iTxSTATE <= iTxSTATE + 1 ;
-- return first state
when 11 => iTxSTATE <= 0 ;
-- default
when others =>
iTxSTATE <= 0 ;
end case ;
end if ;
end process ;
TxD <= '0' when ( iTxSTATE = 1 ) else
Din(0) when ( iTxSTATE = 2 ) else
Din(1) when ( iTxSTATE = 3 ) else
Din(2) when ( iTxSTATE = 4 ) else
Din(3) when ( iTxSTATE = 5 ) else
Din(4) when ( iTxSTATE = 6 ) else
Din(5) when ( iTxSTATE = 7 ) else
Din(6) when ( iTxSTATE = 8 ) else
Din(7) when ( iTxSTATE = 9 ) else
'1' ;
FLAG <= '1' when ( iTxSTATE = 11 ) else '0' ;
end Behavioral;
8ビットのデータを入れて、トリガーを与えると
スタートビットとストップビットで挟んだ8ビット
を出力します。
全体では、10ビットとしておき、転送終了後フラグで
呼出し側に通知します。
GBCから出力されるVoutの2値化のために使ったVHDLコード
は単純で、次のように短いものです。
------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity gbctst is
Port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ;
-- input
GVOUT : in std_logic ;
-- output
LEDOUT : out std_logic_vector(3 downto 0) --;
);
end gbctst;
architecture Behavioral of gbctst is
signal iCNT : integer range 0 to 18000 ;
signal iLEDOUT : std_logic_vector(3 downto 0) ;
signal iGVOUT : std_logic ;
begin
-- input
iGVOUT <= GVOUT ;
-- output
LEDOUT <= not iLEDOUT ;
-- divider
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iCNT <= 0 ;
iLEDOUT <= "0000" ;
elsif rising_edge( CLOCK ) then
if ( iCNT = 18000 ) then
iCNT <= 0 ;
iLEDOUT <= iLEDOUT(2 downto 0) & iGVOUT ;
else
iCNT <= iCNT + 1 ;
end if ;
end if ;
end process ;
end Behavioral;
------------------------------------------------------------
動作は、単純で1HzでVOUTに相当する電圧をキャッチし
LEDに点灯するだけですが、この電圧値をOPアンプで
作った可変電圧出力装置でいろいろ変えました。
この実験で得られたデータは、3.3VのCMOSデバイスでは
0.8V前後にHとLの境界がありました。1.0V以上であれば
CPLD/FPGAは、Hと判定します。
0.6V以下では、Lと判定します。
実験には、CPLDのCoolRunnerIIを載せたCPLDボードを使いました。
目次
前
次