2012年8月17日金曜日
有機ELディスプレイ搭載スイッチIS-C15ANP4をPicoBlazeで制御しよう
先日からピコピコとPicoBlazeを触っていたわけですが、某所で日本開閉器工業社製の有機ELディスプレイ搭載スイッチIS-C15ANP4をPicoBlazeから制御したいと依頼を受けました。
メーカのウェブからダウンロードできる資料には、初期化時に使用するパラメータが列挙されています。このスイッチのディスプレイ・コントローラには、SOLOMON SYSTECH社のSSD1331が搭載されているようです。初期化コードをスイッチに送ってやるだけでディスプレイ・コントローラの初期化は完了です。後は、データを送ればそのまま絵として表示されます。
さて、今回のPicoBlazeのデザインは、先のLEDをピコピコさせたものをそのまま流用しました。
という事はBit Bangで制御するのね、ヒドイ!
ということで、トップのデザインです。
回路はLX-9 MicroBlazeのPMODのピンに超適当に配線です。
さて、後はI/Oをバンバン打つだけです。
もう完全にPicoBlazeの使い方としてはおかしいですが、第一段階としては気にしません。
上記のコードで「赤」、「緑」、「青」、「白」と順に表示を繰り返す事ができます。
[フレーム]
リソースの使用率ですが、以下のようになりました。
Bit Bangはあんまりなので、SPIモジュールを作ってやってデータを渡すのが良いでしょう。
データを渡して、転送開始入力信号と転送完了出力信号が出てくるようなモジュールです。
フォントなどを内蔵すればもっと手軽に表示が楽しめそう。
上記を雛型にすればこういった拡張も簡単です。
ちなみに、今回はBit Bangの基本動作を外部のロジック・アナライザで確認した後、セットアップとホールド、クロックサイクルを見ながらアセンブラ・コードをチューニングし、波形が期待通りに出力されている事を確認した後で、最後にIS-C15ANP4を接続するという形で作業しました。(ChipScope使えよという感じです。)
PicoBlazeのアセンブラも、最初のバージョンより気が利くようになっていて、一度立ち上げてプログラム・ファイル名を入力しておけば「R」をタイプするだけで再コンパイルできるようになっています。
なので、アセンブルの後で生成されたVHDLををプロジェクトにコピーするバッチファイルを作っておけば、「アセンブル→再インプリメンテーション」の流れをスムーズにこなす事ができます。
こんな感じで(超適当だけど)半田付けを含めて小1時間ほどでPicoBlazeからIS-C15ANP4に絵が出せるようになりました。
念のために書いておくと、IS-C15ANP4には決められた電源シーケンスがあります。
製品に使用する場合、きちんとシーケンスを守るように設計しましょう。
最後にグリーンな表示。
ちなみに、先ほどの初期化コードの場合、2バイトで1ピクセルを表現するようになっています。
BとRが5ビット、Gが6ビットで、1バイト目がB[4:0] + G[5:3]、2バイト目がG[2:0] + R[4:0]です。
メーカのウェブからダウンロードできる資料には、初期化時に使用するパラメータが列挙されています。このスイッチのディスプレイ・コントローラには、SOLOMON SYSTECH社のSSD1331が搭載されているようです。初期化コードをスイッチに送ってやるだけでディスプレイ・コントローラの初期化は完了です。後は、データを送ればそのまま絵として表示されます。
さて、今回のPicoBlazeのデザインは、先のLEDをピコピコさせたものをそのまま流用しました。
という事はBit Bangで制御するのね、ヒドイ!
ということで、トップのデザインです。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity main is
Port (
oled_ss : out std_logic;
oled_res : out std_logic;
oled_dc : out std_logic;
oled_sck : out std_logic;
oled_sdi : out std_logic;
clk : in std_logic;
sw1 : in std_logic);
end main;
architecture RTL of main is
component kcpsm6
generic( hwbuild : std_logic_vector(7 downto 0) := X"00";
interrupt_vector : std_logic_vector(11 downto 0) := X"3FF";
scratch_pad_memory_size : integer := 64);
port ( address : out std_logic_vector(11 downto 0);
instruction : in std_logic_vector(17 downto 0);
bram_enable : out std_logic;
in_port : in std_logic_vector(7 downto 0);
out_port : out std_logic_vector(7 downto 0);
port_id : out std_logic_vector(7 downto 0);
write_strobe : out std_logic;
k_write_strobe : out std_logic;
read_strobe : out std_logic;
interrupt : in std_logic;
interrupt_ack : out std_logic;
sleep : in std_logic;
reset : in std_logic;
clk : in std_logic);
end component;
component c15anp4
generic( C_FAMILY : string := "S6";
C_RAM_SIZE_KWORDS : integer := 1;
C_JTAG_LOADER_ENABLE : integer := 0);
Port ( address : in std_logic_vector(11 downto 0);
instruction : out std_logic_vector(17 downto 0);
enable : in std_logic;
rdl : out std_logic;
clk : in std_logic);
end component;
signal address : std_logic_vector(11 downto 0);
signal instruction : std_logic_vector(17 downto 0);
signal port_id : std_logic_vector(7 downto 0);
signal in_port : std_logic_vector(7 downto 0);
signal out_port : std_logic_vector(7 downto 0);
signal read_strobe : std_logic;
signal write_strobe : std_logic;
signal port000 : std_logic_vector(7 downto 0);
begin
processor: kcpsm6
generic map ( hwbuild => X"00",
interrupt_vector => X"3FF",
scratch_pad_memory_size => 64)
port map(
address => address,
instruction => instruction,
port_id => port_id,
interrupt => '0',
write_strobe => write_strobe,
out_port => out_port,
read_strobe => read_strobe,
in_port => in_port,
sleep => '0',
reset => '0',
clk => clk);
program: c15anp4
generic map( C_FAMILY => "S6",
C_RAM_SIZE_KWORDS => 1,
C_JTAG_LOADER_ENABLE => 0)
port map( address => address,
instruction => instruction,
enable => '1',
clk => clk);
out_ratch : process(clk) is
begin
if clk'event and clk='1' then
if write_strobe='1' then
if port_id ="00000000" then
port000 <= out_port;
end if;
end if;
end if;
end process out_ratch;
in_ratch : process(clk) is
begin
if clk'event and clk='1' then
if read_strobe='1' then
if port_id ="00000000" then
in_port(0) <= not sw1;
end if;
end if;
end if;
end process in_ratch;
oled_ss <= port000(0);
oled_res <= port000(1);
oled_dc <= port000(2);
oled_sck <= port000(3);
oled_sdi <= port000(4);
end RTL;
回路はLX-9 MicroBlazeのPMODのピンに超適当に配線です。
NET "oled_ss" LOC = D18;
NET "oled_res" LOC = D17;
NET "oled_dc" LOC = G14;
NET "oled_sck" LOC = F14;
NET "oled_sdi" LOC = F15;
さて、後はI/Oをバンバン打つだけです。
; =========================================================
; Port000[0] : SS- : Slave Select (Low Active)
; Port000[1] : RES : Reset (Low Active)
; Port000[2] : D/C : Data/Command (Data=High, Command=Low)
; Port000[3] : SCK : Serial Clock (Up Edge Trigger)
; Port000[4] : SDI : Serial Data
; =========================================================
; ---------------------------------------------------------
; Register Name
; ---------------------------------------------------------
; SPI Data Register
NAMEREG s0, SPIDATA
; Port Data Register
NAMEREG s1, PORTDAT
; Delay Counter No.1
NAMEREG s2, DLYCNT1
; Delay Counter No.2
NAMEREG s3, DLYCNT2
; Delay Counter No.3
NAMEREG s4, DLYCNT3
; Send Counter No.1
NAMEREG s5, SNDCNT1
; Send Counter No.2
NAMEREG s6, SNDCNT2
; Temporary Register No.1
NAMEREG s7, TEMP_I
; Temporary Register No.2
NAMEREG s8, TEMP_J
; ---------------------------------------------------------
; Constant Variables
; ---------------------------------------------------------
CONSTANT PORT000, 00
CONSTANT OLED_SS, 01
CONSTANT OLED_RES, 02
CONSTANT OLED_DC, 04
CONSTANT OLED_SCK, 08
CONSTANT OLED_SDI, 10
MAIN:
CALL INIT
TESTLOOP:
CALL INIT_ADDR
CALL FILLR
CALL LONGDELAY
CALL INIT_ADDR
CALL FILLG
CALL LONGDELAY
CALL INIT_ADDR
CALL FILLB
CALL LONGDELAY
CALL INIT_ADDR
CALL FILLW
CALL LONGDELAY
JUMP TESTLOOP
INIT_ADDR:
; ---------------------------------------------------------
; Setup Column Address
; ---------------------------------------------------------
LOAD SPIDATA, 15
CALL SEND_CMD
LOAD SPIDATA, 10
CALL SEND_CMD
LOAD SPIDATA, 4F
CALL SEND_CMD
; ---------------------------------------------------------
; Setup Row Address
; ---------------------------------------------------------
LOAD SPIDATA, 75
CALL SEND_CMD
LOAD SPIDATA, 00
CALL SEND_CMD
LOAD SPIDATA, 2F
CALL SEND_CMD
RETURN
FILLR:
LOAD TEMP_I, 30
FILLR1:
LOAD TEMP_J, 40
FILLR2:
; ---------------------------------------------------------
; Byte0 = B[4:0] + G[5:3]
; Byte1 = G[2:0] + R[4:0]
; ---------------------------------------------------------
LOAD SPIDATA, 00
CALL SEND_DAT
LOAD SPIDATA, 1F
CALL SEND_DAT
SUB TEMP_J, 01
JUMP NZ, FILLR2
SUB TEMP_I, 01
JUMP NZ, FILLR1
RETURN
FILLG:
LOAD TEMP_I, 30
FILLG1:
LOAD TEMP_J, 40
FILLG2:
; ---------------------------------------------------------
; Byte0 = B[4:0] + G[5:3]
; Byte1 = G[2:0] + R[4:0]
; ---------------------------------------------------------
LOAD SPIDATA, 07
CALL SEND_DAT
LOAD SPIDATA, C0
CALL SEND_DAT
SUB TEMP_J, 01
JUMP NZ, FILLG2
SUB TEMP_I, 01
JUMP NZ, FILLG1
RETURN
FILLB:
LOAD TEMP_I, 30
FILLB1:
LOAD TEMP_J, 40
FILLB2:
; ---------------------------------------------------------
; Byte0 = B[4:0] + G[5:3]
; Byte1 = G[2:0] + R[4:0]
; ---------------------------------------------------------
LOAD SPIDATA, F8
CALL SEND_DAT
LOAD SPIDATA, 00
CALL SEND_DAT
SUB TEMP_J, 01
JUMP NZ, FILLB2
SUB TEMP_I, 01
JUMP NZ, FILLB1
RETURN
FILLW:
LOAD TEMP_I, 30
FILLW1:
LOAD TEMP_J, 40
FILLW2:
; ---------------------------------------------------------
; Byte0 = B[4:0] + G[5:3]
; Byte1 = G[2:0] + R[4:0]
; ---------------------------------------------------------
LOAD SPIDATA, FF
CALL SEND_DAT
LOAD SPIDATA, FF
CALL SEND_DAT
SUB TEMP_J, 01
JUMP NZ, FILLW2
SUB TEMP_I, 01
JUMP NZ, FILLW1
RETURN
INIT:
; =========================================================
; SS- : Slave Select (Low Active)
; RES : Reset (Low Active)
; D/C : Data/Command (Data=High, Command=Low)
; SCK : Serial Clock (Up Edge Trigger)
; SDI : Serial Data
; =========================================================
; ---------------------------------------------------------
; Reset Active
; ---------------------------------------------------------
LOAD PORTDAT, 00
OUTPUT PORTDAT, PORT000
CALL DELAY
; ---------------------------------------------------------
; Reset Inactive
; ---------------------------------------------------------
LOAD PORTDAT, OLED_RES
OUTPUT PORTDAT, PORT000
CALL DELAY
; ---------------------------------------------------------
; Setup Column Address
; ---------------------------------------------------------
LOAD SPIDATA, 15
CALL SEND_CMD
LOAD SPIDATA, 10
CALL SEND_CMD
LOAD SPIDATA, 4F
CALL SEND_CMD
; ---------------------------------------------------------
; Setup Row Address
; ---------------------------------------------------------
LOAD SPIDATA, 75
CALL SEND_CMD
LOAD SPIDATA, 00
CALL SEND_CMD
LOAD SPIDATA, 2F
CALL SEND_CMD
; ---------------------------------------------------------
; Setup Contrast Color A
; ---------------------------------------------------------
LOAD SPIDATA, 81
CALL SEND_CMD
LOAD SPIDATA, 19
CALL SEND_CMD
; ---------------------------------------------------------
; Setup Contrast Color B
; ---------------------------------------------------------
LOAD SPIDATA, 82
CALL SEND_CMD
LOAD SPIDATA, 14
CALL SEND_CMD
; ---------------------------------------------------------
; Setup Contrast Color C
; ---------------------------------------------------------
LOAD SPIDATA, 83
CALL SEND_CMD
LOAD SPIDATA, 24
CALL SEND_CMD
; ---------------------------------------------------------
; Master Current Control
; ---------------------------------------------------------
LOAD SPIDATA, 87
CALL SEND_CMD
LOAD SPIDATA, 0F
CALL SEND_CMD
; ---------------------------------------------------------
; Set Display Start Line
; ---------------------------------------------------------
LOAD SPIDATA, A1
CALL SEND_CMD
LOAD SPIDATA, 00
CALL SEND_CMD
; ---------------------------------------------------------
; Set Display Offset
; ---------------------------------------------------------
LOAD SPIDATA, A2
CALL SEND_CMD
LOAD SPIDATA, 10
CALL SEND_CMD
; ---------------------------------------------------------
; Set Display Mode
; ---------------------------------------------------------
LOAD SPIDATA, A4
CALL SEND_CMD
; ---------------------------------------------------------
; Set Multiplex Ratio
; ---------------------------------------------------------
LOAD SPIDATA, A8
CALL SEND_CMD
LOAD SPIDATA, 2F
CALL SEND_CMD
; ---------------------------------------------------------
; Dim Mode Setting
; ---------------------------------------------------------
LOAD SPIDATA, AB
CALL SEND_CMD
LOAD SPIDATA, 12
CALL SEND_CMD
LOAD SPIDATA, 0C
CALL SEND_CMD
LOAD SPIDATA, 14
CALL SEND_CMD
LOAD SPIDATA, 12
CALL SEND_CMD
; ---------------------------------------------------------
; Set Master Configuration
; ---------------------------------------------------------
LOAD SPIDATA, AD
CALL SEND_CMD
LOAD SPIDATA, 8E
CALL SEND_CMD
; ---------------------------------------------------------
; Phase Period Adjustment
; ---------------------------------------------------------
LOAD SPIDATA, B1
CALL SEND_CMD
LOAD SPIDATA, 44
CALL SEND_CMD
; ---------------------------------------------------------
; Display Clock Divider
; ---------------------------------------------------------
LOAD SPIDATA, B3
CALL SEND_CMD
LOAD SPIDATA, A0
CALL SEND_CMD
; ---------------------------------------------------------
; Enable Linear Gray Scale
; ---------------------------------------------------------
LOAD SPIDATA, B9
CALL SEND_CMD
; ---------------------------------------------------------
; Set Precharge Level
; ---------------------------------------------------------
LOAD SPIDATA, BB
CALL SEND_CMD
LOAD SPIDATA, 12
CALL SEND_CMD
; ---------------------------------------------------------
; Set VCOMH
; ---------------------------------------------------------
LOAD SPIDATA, BE
CALL SEND_CMD
LOAD SPIDATA, 28
CALL SEND_CMD
; ---------------------------------------------------------
; Color Depth = 64K
; ---------------------------------------------------------
LOAD SPIDATA, A0
CALL SEND_CMD
LOAD SPIDATA, 70
CALL SEND_CMD
; ---------------------------------------------------------
; Set Display Normal
; ---------------------------------------------------------
LOAD SPIDATA, AF
CALL SEND_CMD
RETURN
SEND_DAT:
; =========================================================
; SS- : Slave Select (Low Active)
; RES : Reset (Low Active)
; D/C : Data/Command (Data=High, Command=Low)
; SCK : Serial Clock (Up Edge Trigger)
; SDI : Serial Data
; =========================================================
SEND_DAT_SLAVE_ACTIVE:
LOAD PORTDAT, 00
OR PORTDAT, OLED_RES
OR PORTDAT, OLED_DC
SEND_DAT_SETUP:
LOAD SNDCNT1, 08
LOAD SNDCNT2, SPIDATA
SEND_DAT_LOOP:
SL0 SNDCNT2
JUMP C, SEND_DAT_H
JUMP SEND_DAT_L
SEND_DAT_H:
; ---------------------------------------------------------
; SDI=HIGH, SCK=LOW
; ---------------------------------------------------------
LOAD PORTDAT, 00
OR PORTDAT, OLED_RES
OR PORTDAT, OLED_DC
OR PORTDAT, OLED_SDI
OUTPUT PORTDAT, PORT000
CALL DELAY
; ---------------------------------------------------------
; SDI=HIGH, SCK=HIGH
; ---------------------------------------------------------
LOAD PORTDAT, 00
OR PORTDAT, OLED_RES
OR PORTDAT, OLED_DC
OR PORTDAT, OLED_SCK
OR PORTDAT, OLED_SDI
OUTPUT PORTDAT, PORT000
CALL DELAY
; ---------------------------------------------------------
; NEXT
; ---------------------------------------------------------
JUMP SEND_DAT_NEXT
SEND_DAT_L:
; ---------------------------------------------------------
; SDI=LOW, SCK=LOW
; ---------------------------------------------------------
LOAD PORTDAT, 00
OR PORTDAT, OLED_RES
OR PORTDAT, OLED_DC
OUTPUT PORTDAT, PORT000
CALL DELAY
; ---------------------------------------------------------
; SDI=LOW, SCK=HIGH
; ---------------------------------------------------------
LOAD PORTDAT, 00
OR PORTDAT, OLED_RES
OR PORTDAT, OLED_DC
OR PORTDAT, OLED_SCK
OUTPUT PORTDAT, PORT000
CALL DELAY
; ---------------------------------------------------------
; NEXT
; ---------------------------------------------------------
JUMP SEND_DAT_NEXT
SEND_DAT_NEXT:
SUB SNDCNT1, 01
JUMP NZ, SEND_DAT_LOOP
SEND_DAT_END:
LOAD PORTDAT, 00
OR PORTDAT, OLED_RES
OR PORTDAT, OLED_DC
OR PORTDAT, OLED_SS
OUTPUT PORTDAT, PORT000
RETURN
SEND_CMD:
; =========================================================
; SS- : Slave Select (Low Active)
; RES : Reset (Low Active)
; D/C : Data/Command (Data=High, Command=Low)
; SCK : Serial Clock (Up Edge Trigger)
; SDI : Serial Data
; =========================================================
SEND_CMD_SLAVE_ACTIVE:
LOAD PORTDAT, 00
OR PORTDAT, OLED_RES
SEND_CMD_SETUP:
LOAD SNDCNT1, 08
LOAD SNDCNT2, SPIDATA
SEND_CMD_LOOP:
SL0 SNDCNT2
JUMP C, SEND_CMD_H
JUMP SEND_CMD_L
SEND_CMD_H:
; ---------------------------------------------------------
; SDI=HIGH, SCK=LOW
; ---------------------------------------------------------
LOAD PORTDAT, 00
OR PORTDAT, OLED_RES
OR PORTDAT, OLED_SDI
OUTPUT PORTDAT, PORT000
CALL DELAY
; ---------------------------------------------------------
; SDI=HIGH, SCK=HIGH
; ---------------------------------------------------------
LOAD PORTDAT, 00
OR PORTDAT, OLED_RES
OR PORTDAT, OLED_SCK
OR PORTDAT, OLED_SDI
OUTPUT PORTDAT, PORT000
CALL DELAY
; ---------------------------------------------------------
; NEXT
; ---------------------------------------------------------
JUMP SEND_CMD_NEXT
SEND_CMD_L:
; ---------------------------------------------------------
; SDI=LOW, SCK=LOW
; ---------------------------------------------------------
LOAD PORTDAT, 00
OR PORTDAT, OLED_RES
OUTPUT PORTDAT, PORT000
CALL DELAY
; ---------------------------------------------------------
; SDI=LOW, SCK=HIGH
; ---------------------------------------------------------
LOAD PORTDAT, 00
OR PORTDAT, OLED_RES
OR PORTDAT, OLED_SCK
OUTPUT PORTDAT, PORT000
CALL DELAY
; ---------------------------------------------------------
; NEXT
; ---------------------------------------------------------
JUMP SEND_CMD_NEXT
SEND_CMD_NEXT:
SUB SNDCNT1, 01
JUMP NZ, SEND_CMD_LOOP
SEND_CMD_END:
LOAD PORTDAT, 00
OR PORTDAT, OLED_RES
OR PORTDAT, OLED_SS
OUTPUT PORTDAT, PORT000
RETURN
DELAY:
LOAD DLYCNT1, 01
DELAY1:
LOAD DLYCNT2, 01
DELAY2:
SUB DLYCNT2, 01
JUMP NZ, DELAY2
SUB DLYCNT1, 01
JUMP NZ, DELAY1
RETURN
LONGDELAY:
LOAD DLYCNT1, 80
LONGDELAY1:
LOAD DLYCNT2, FF
LONGDELAY2:
LOAD DLYCNT3, FF
LONGDELAY3:
SUB DLYCNT3, 01
JUMP NZ, LONGDELAY3
SUB DLYCNT2, 01
JUMP NZ, LONGDELAY2
SUB DLYCNT1, 01
JUMP NZ, LONGDELAY1
RETURN
もう完全にPicoBlazeの使い方としてはおかしいですが、第一段階としては気にしません。
上記のコードで「赤」、「緑」、「青」、「白」と順に表示を繰り返す事ができます。
リソースの使用率ですが、以下のようになりました。
Bit Bangはあんまりなので、SPIモジュールを作ってやってデータを渡すのが良いでしょう。
データを渡して、転送開始入力信号と転送完了出力信号が出てくるようなモジュールです。
フォントなどを内蔵すればもっと手軽に表示が楽しめそう。
上記を雛型にすればこういった拡張も簡単です。
ちなみに、今回はBit Bangの基本動作を外部のロジック・アナライザで確認した後、セットアップとホールド、クロックサイクルを見ながらアセンブラ・コードをチューニングし、波形が期待通りに出力されている事を確認した後で、最後にIS-C15ANP4を接続するという形で作業しました。(ChipScope使えよという感じです。)
PicoBlazeのアセンブラも、最初のバージョンより気が利くようになっていて、一度立ち上げてプログラム・ファイル名を入力しておけば「R」をタイプするだけで再コンパイルできるようになっています。
なので、アセンブルの後で生成されたVHDLををプロジェクトにコピーするバッチファイルを作っておけば、「アセンブル→再インプリメンテーション」の流れをスムーズにこなす事ができます。
こんな感じで(超適当だけど)半田付けを含めて小1時間ほどでPicoBlazeからIS-C15ANP4に絵が出せるようになりました。
念のために書いておくと、IS-C15ANP4には決められた電源シーケンスがあります。
製品に使用する場合、きちんとシーケンスを守るように設計しましょう。
最後にグリーンな表示。
ちなみに、先ほどの初期化コードの場合、2バイトで1ピクセルを表現するようになっています。
BとRが5ビット、Gが6ビットで、1バイト目がB[4:0] + G[5:3]、2バイト目がG[2:0] + R[4:0]です。
ラベル:
Break time,
FPGA,
IS-C15ANP4,
LX9 MicroBoard,
OLED,
PicoBlaze,
Xilinx
2012年8月15日水曜日
PicoBlazeでピコピコしよう (Xilinx社のデバイスが搭載されたFPGAボードをジャンク箱で眠らせている人にもお勧め)
PicoBlazeってなぁに?
PicoBlazeとは、Xilinxが提供しているソフト・マクロで構成されたマイクロ・コントローラです。最大4K命令の実行が可能で、命令長は18ビット固定、1命令あたり2クロック・サイクルで処理されます。
PicoBlazeは、非常にシンプルな8ビット・プロセッサです。
I/Oは自分で構成する事ができ、入出力それぞれ独立した8ビットのポート番号を持ちます。
割り込みなんて1系統しかありません。
最大クロック周波数は、デバイスとデザイン(配置配線、タイム・スペック)に依存するものの、Spartan-6(-2)で105MHz、Spartan-6(-3)で138MHz、Vertex-6(-3)で240MHzが得られます。
とはいえ、パフォーマンスはさほど重要なポイントではありません。
というか、パフォーマンス目当てでこんな物を使ってはいけません。
FPGAとプロセッサを並行して使う事で、論理記述言語のみで処理する場合に比べて、格段に設計が楽になったりします。(当然ですが、全てのケースでそうだなんて言いません。そういう場合もあるというだけです。)
プロセッサの場合、シーケンシャルな処理を記述するのは簡単で、単に処理を上から順に書くだけで済みます。(実行はFPGAに比べて格段に遅いけどね。)
FPGAでシーケンシャルな処理を構成する場合、ステートマシンを設計したり、ジェネリックで色々なパラメータを上位から渡せるようにした後でそれらを上位でパイプラインを構成して組み合わせたりと、何だかんだを手作業でやる事になるのが通例です。
「FPGAで構成したクロック単位でキビキビ動作する回路と、PicoBlaze上で動作するゆるーいソフトウェアを組み合わせる事で、従来にないメリットが得る」という使い方が、PicoBlazeの美味しいところです。
PicoBlazeはEDKなど有償ライセンスなしで開発できるのも嬉しいところ。
自宅のプチ・プロジェクトでも安心して使えるのがPicoBlazeなのです。
PicoBlazeのリソースをダウンロードする
PicoBlazeのリソースは、Xilinx社のウェブからダウンロードする事ができます。ダウンロードにはサイン・インが必要です。
http://www.xilinx.com/ipcenter/processor_central/picoblaze/member/index.htm
初めにダウンロードする場合、登録が必要になっています。
ターゲットデバイスを選択して、住所などを入力します。
ライセンス情報が表示されます。
登録が完了するとラウンジに入室です。
ここからお目当てのデバイスのリソースをダウンロードします。
既に7シリーズ用のリソースもあります。
今回はLX-9 MicroBoardで使用するので、KCPSM6をダウンロードしました。
Release4の中身は以下のようになっていました。
どんな風に開発するの?
PicoBlazeの開発フローを簡単に説明します。- PicoBlaze向けのソフトウェアをアセンブラで記述する。
- 記述したソフトウェアをアセンブルして、プログラムROM用のモジュールを得る。
- プログラムROM用のモジュールとPicoBlazeソフト・コアをプロジェクトに追加する。
- シンセサイズとインプリメンテーションを実行する。
まぁ、ざっくり言ってしまうと、FPGAの開発フローの前段に余計な物がくっついたという感じです。
PicoBlazeプロセッサ向けに追加されたフローは、アセンブラの記述とアセンブルの実行です。
このプロセスで得られるのは、PicoBlazeソフト・コア本体と、自分で記述したソフトウェアのインストラクションが格納されたモジュールです。後は、ROMとソフト・コア本体、そして自分が欲しい周辺回路を論理記述言語で接続して使用するだけです。
命令格納モジュール(下図で言うyour_program)とPicoBlaze(下図で言うkcpsm6)の関係は以下です。プロセッサ・コアからアドレスを与えられた命令格納モジュールが、命令をプロセッサ・コアに引き渡す構成です。
命令格納モジュール(下図で言うyour_program)とPicoBlaze(下図で言うkcpsm6)の関係は以下です。プロセッサ・コアからアドレスを与えられた命令格納モジュールが、命令をプロセッサ・コアに引き渡す構成です。
使用イメージを簡単に掴む為に、実際にインプリメンテーションを通した後の様子を回路図で見てみましょう。
上記を見ておわかりのように、ユーザ回路の一部にインストラクション格納モジュールとPicoBlaze本体が挟まっただけの構成です。
本当に適当な例(さっきの回路)
ここで、本当に適当な例(さっきの回路)を挙げてみましょう。
トップはsw1の状態を入力ポートのビット0に渡し、出力ポートのビット3から0をport_idが0の時にラッチしてLEDを点灯させているだけの回路です。
入力ポートをどのように使用するのか、出力ポートをどのように使用するのか、についてはプロセッサ上で動作するプログラムに任されています。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity main is
Port (
led1 : out std_logic;
led2 : out std_logic;
led3 : out std_logic;
led4 : out std_logic;
clk : in std_logic;
sw1 : in std_logic);
end main;
architecture RTL of main is
component kcpsm6
generic( hwbuild : std_logic_vector(7 downto 0) := X"00";
interrupt_vector : std_logic_vector(11 downto 0) := X"3FF";
scratch_pad_memory_size : integer := 64);
port ( address : out std_logic_vector(11 downto 0);
instruction : in std_logic_vector(17 downto 0);
bram_enable : out std_logic;
in_port : in std_logic_vector(7 downto 0);
out_port : out std_logic_vector(7 downto 0);
port_id : out std_logic_vector(7 downto 0);
write_strobe : out std_logic;
k_write_strobe : out std_logic;
read_strobe : out std_logic;
interrupt : in std_logic;
interrupt_ack : out std_logic;
sleep : in std_logic;
reset : in std_logic;
clk : in std_logic);
end component;
component led_toggle
generic( C_FAMILY : string := "S6";
C_RAM_SIZE_KWORDS : integer := 1;
C_JTAG_LOADER_ENABLE : integer := 0);
Port ( address : in std_logic_vector(11 downto 0);
instruction : out std_logic_vector(17 downto 0);
enable : in std_logic;
rdl : out std_logic;
clk : in std_logic);
end component;
signal address : std_logic_vector(11 downto 0);
signal instruction : std_logic_vector(17 downto 0);
signal port_id : std_logic_vector(7 downto 0);
signal in_port : std_logic_vector(7 downto 0);
signal out_port : std_logic_vector(7 downto 0);
signal read_strobe : std_logic;
signal write_strobe : std_logic;
signal port000 : std_logic_vector(7 downto 0);
begin
processor: kcpsm6
generic map ( hwbuild => X"00",
interrupt_vector => X"3FF",
scratch_pad_memory_size => 64)
port map(
address => address,
instruction => instruction,
port_id => port_id,
interrupt => '0',
write_strobe => write_strobe,
out_port => out_port,
read_strobe => read_strobe,
in_port => in_port,
sleep => '0',
reset => '0',
clk => clk);
program_rom: led_toggle
generic map( C_FAMILY => "S6",
C_RAM_SIZE_KWORDS => 1,
C_JTAG_LOADER_ENABLE => 0)
port map( address => address,
instruction => instruction,
enable => '1',
clk => clk);
out_ratch : process(clk) is
begin
if clk'event and clk='1' then
if write_strobe='1' then
if port_id ="00000000" then
port000 <= out_port;
end if;
end if;
end if;
end process out_ratch;
in_ratch : process(clk) is
begin
if clk'event and clk='1' then
if read_strobe='1' then
if port_id ="00000000" then
in_port(0) <= not sw1;
end if;
end if;
end if;
end process in_ratch;
led1 <= port000(0);
led2 <= port000(1);
led3 <= port000(2);
led4 <= port000(3);
end RTL;
次にプロセッサ上のプログラムです。
レジスタは16レジスタが2バンクある構成になっています。
デフォルト・バンクは'A'です。
これらのレジスタは、ユーザが自由に使用する事ができます。
単にs0とかs1とかだと分かりにくいのですが、NAMEREGを使う事で名前でレジスタにアクセスする事ができます。
NAMEREG s0, TEMP
NAMEREG s1, DLYCNT1
NAMEREG s2, DLYCNT2
NAMEREG s3, PVAL000
CONSTANT PORT000, 00
INIT:
LOAD PVAL000, 00
LOOP:
CALL DELAY
INPUT TEMP, PORT000
COMPARE TEMP, 01
JUMP Z, LOOP
LOOP1:
CALL DELAY
INPUT TEMP, PORT000
COMPARE TEMP, 00
JUMP Z, LOOP1
LOOP2:
ADD PVAL000, 01
OUTPUT PVAL000, PORT000
JUMP LOOP
DELAY:
LOAD DLYCNT1, FF
DELAY1:
LOAD DLYCNT2, FF
DELAY2:
SUB DLYCNT2, 01
JUMP NZ, DELAY2
SUB DLYCNT1, 01
JUMP NZ, DELAY1
RETURN
上記の例では、スイッチを押すたびにプロセッサ上のレジスタの値をインクリメントし、インクリメントした値を8ビットのポートに出力するようにしました。
[フレーム]
今回は非常に単純な例を示したので、「そんなのVHDLだけで良い」と言われかねませんが、プロセッサによる並列処理を介在させる事でハードウェア側の設計を単純にする事ができる例があります。I2CやSPIなど、同じプロトコルでも複数の種類があるような場合や、そもそもステートがあまりに多い場合にも、全体制御をプロセッサにさせる事で、ハードウェア側の設計が楽になる事は間違いありません。(ここでは性能について一切触れませんが。)
また、ソフト・コア・プロセッサが動作するというだけで、少しワクワクした気分になるのは自分だけではないでしょう。無償で得られる開発環境で、チクチクピコピコPicoBlazeいじりなんていうのも、たまには面白いのではないでしょうか。
ラベル:
Break time,
FPGA,
LX9 MicroBoard,
PicoBlaze,
Spartan-6,
Xilinx
2011年9月16日金曜日
LX9 MicroBoardでSoCを体験しよう!
米国Avnet社のLX9サポートページには、使い始めるのに必要な資料が殆ど揃っています。
今日はその中から面白そうな物をピックアップして試してみることにしました。
LX9で遊びたい事の一つはやはりSoC(System On Chip)ということになるでしょう。
そこで登場するのが「uC/OS-III Example Application - Software 301 for the Spartan-6 LX9 MicroBoard」というチュートリアルです。
今日はその中から面白そうな物をピックアップして試してみることにしました。
LX9で遊びたい事の一つはやはりSoC(System On Chip)ということになるでしょう。
そこで登場するのが「uC/OS-III Example Application - Software 301 for the Spartan-6 LX9 MicroBoard」というチュートリアルです。
このチュートリアルを辿ればLX9を使ってSoCに関連する一連の作業(の一部)を楽しむことができます。
- ハードウェア定義ファイル(system.xml)を読み込んでハードウェアプロジェクトを作成する。
- ハードウェアプロジェクトを指定して新しいボードサポートパッケージプロジェクトを作成する。
- ソフトウェアプロジェクトを作成する。
この資料では、Xilinx Software Development Kitを使ったRTOS上でのLEDチカチカを行います。
まずはXilinx Software Development Kitを起動します。
資料に従って作業するとワークスペースには形態の異なる3つのプロジェクトが出来上がります。
例えば、以下のケースでは、上からハードウェアプロジェクト、ボードサポートパッケージ、ソフトウェアプロジェクトです。
Xilinx ToolsからProgram FPGAを選択して、予め用意されたビットストリームファイルを使って回路を定義します。
ダイアログにはハードウェアプロジェクトを作成する時に与えたxmlによって必要な値が与えられていますので、特に何も入力する必要はないはずです。
ちなみに、このビットストリームを流し込む事によってボード上のLEDをチカチカさせるための回路やレジスタが定義され、プログラムのブート待ち状態にすることができます。
やってしまいがちな誤りが、このビットストリームを流し込む前にプログラムのデバッグを始めようとしてしまうこと。それは動きませんです。
Doneを確認したらプログラムを動作させてみましょう。
これで仲良く並んだ2つのLEDが適当な間隔で点滅することが確認できるはずです。
ふむふむと動作を楽しんだ後は、コードを見て楽しみましょう。
BSPには主にMicroBlazeのレジスタを操作するインターフェースを定義したヘッダファイルが配置されています。
OS側はプロセッサ周り、ライブラリ、カーネル、アプリケーションと綺麗に階層化されていますね。
このように、従来では色々な環境整備に時間がかかりがちだったSoC体験を、いとも簡単に実現してしまうのがLX9 MicroBoardなのです。
ラベル:
Digilent,
LX9 MicroBoard,
Xilinx
2011年9月14日水曜日
LX9のXilinx SDKからの書き込み
LX9にビットファイルを流し込む方法
LX9は何と言ってもお手軽にSoCを試す事が出来るのが魅力の1つです。Xilinx SDKから書き込む場合には予め設定が必要なのですが、どっからどうすんだ?となった方のために整理してみましょう。
まずはhttp://www.digilentinc.com/Products/Detail.cfm?NavPath=2,66,768&Prod=DIGILENT-PLUGINからプラグインをダウンロードします。
今日現在ではISE 11, ISE 12, ISE 13に対するプラグインが完備されていました。
後は付属のPDFに従ってインストールすれば完了!です。(え?)
プラグインをISEのプラグインディレクトリにコピーする作業の実行です。
後は各種ツールでの設定を残すのみです。
(このページの設定において、シリアルナンバー指定は割愛しています。詳しくは付属のPDFをご覧ください。)
例えば、Xilinx SDKの場合。
iMPACTの場合。
こんな感じで設定することでJTAG接続ができるようになります。
ラベル:
Digilent,
LX9 MicroBoard,
Xilinx
2011年9月3日土曜日
AVNET Spartan-6 FPGA LX9 MicroBoardの電源を入れてみる
付属する2つのUSBケーブル
LX9 MicroBoardには2つのUSBケーブルが付属しています。1つは延長ケーブルになっていて、「Direct USB Programming」用のUSBコネクタに接続します。
このコネクタは主にFPGAをプログラムするときに使用し、ボード上のAtmel AT90USB162に接続されています。
もう1つのUSBケーブルは小さい方のUSBコネクタに接続して使用します。
こちらはボード上のSilicon Laboratories CP2102に接続されています。
LX9 MicroBoardに予め書き込まれたデザインはMicroBlazeを使ったものになっていて、CP2102経由でブートの様子を見ることができるようです。
今回はこれを試してみましょう。
使っているOSによって、CP2102に対するデバイスドライバが必要になることがあります。
ちなみにWindows7の場合には特別なインストールなしで使用することができます。
ブートさせる
シリアルポート(115200/8/n/1)を開いてSW4を押すとリセットがかかるようになっています。
ブートした時の様子は以下のようになります。
私は他人の作ったコマンドを見るのが大好きです。
ラベル:
LX9 MicroBoard,
Xilinx
2011年9月2日金曜日
AVNET Spartan-6 FPGA LX9 MicroBoardの開発環境をセットアップする
安価なFPGA開発プラットフォーム
FPGA評価ボードと言えば一万円前後の物が簡単に入手できるような贅沢な時代です。Spartan-6 FPGA LX9 MicroBoardはソフトコアマイクロプロセッサ開発環境を付属して一万円を切る値段です。
実はこの評価ボードとEDKの特別パッケージなんて噂もあったので購入を控えていましたが、限定的なSDKでも良いかということで購入に踏み切ることにしました。
市場に出回るFPGA評価ボードの中ではかなり小さい部類に入ります。
従来の評価ボードにあるような機能がかなり盛り込まれています。
ソフトコアマイクロプロセッサを動作させる事を念頭に置いたDDR SDRAMもあります。
開発環境のセットアップ
開発環境のセットアップはXilinx社のツールに慣れている人であれば戸惑うことはありません。
が、このボードが初めてのXilinxという方は少し戸惑うかもしれません。
何せセットアップのドキュメントが・・・どこにあるの?という感じです。
参考までに幾つかのシーンをピックアップしてポイントを示しておきます。
まず、LX9 MicroBoardには開発ツールのライセンスが付いてきます。
これは「Spartan-6 LX9 MicroBoard Kit - ISE Design Suite: WebPack and SDK Device Locked, Node-Locked License」という名称が付けられています。
パッケージを開けると付いてくる「重要!(IMPORTANT!)」と書かれた養子に書いてあるコードがユーザ固有のライセンスコードです。これは間違っても捨てないように。
付属していたISEのバージョンは12.4でした。
インストールするエディションを選択する画面が出ます。
これは「ISE Design Suite : System Edition」が正解です。
インストールするオプションの選択が促されると思います。
私はとりあえず全部入れておきました。
LX9 MicroBoardにはRJ-45が付いています。
ネットワークを絡めた何かができるはずです。
インストーラもネットワークのあれこれに備えてネットワークパケットキャプチャツールのインストールを促してきます。
この後ライセンスを登録して完了です。
まず、Xilinx社のサイトで自分用のアカウントを取得しておいてください。
これは無償でできます。
ツールのインストールが完了するとライセンスマネージャが起動します。
ライセンスを登録する場合、以下のようにライセンスマネージャ経由でウェブサイトにアクセスすることで簡単に行なえます。
ログインしていない場合、ログイン画面が表示されます。
ログインするとライセンスを入れる画面がブラウザに表示されるはずです。
ここに箱に同梱されていたライセンス番号を入力し、「Redeem Now」を押します。
ノードライセンスの生成画面が表示されます。
ノードロックのハードウェア固有情報は「ディスク」と「ネットワーク」から選択できます。
過去にライセンスを取得した事がある場合、複数のノードが表示されますので、今回のライセンスを使って開発するマシンを選んで下さい。
生成が終わるとライセンスファイルがメールで送られてきます。
これを先ほどのライセンスマネージャで読み込んで完了です。
ライセンスの状態が気になればライセンスマネージャでいつでも確認することができます。
今回のライセンスには「Xilinx Software Development Kit」のライセンスも含まれるようです。
気になる人はこちらも起動して問題ないことを確認しましょう。
まとめ
今回は勢いで購入したLX9 MicroBoardの開発環境セットアップについて触れました。 EDKのシュリンク版っぽいものは、デバイスロックだけが制限かと思っていました。 ですが、実際にインストールしてみると1年の限定ライセンスのように見えますね。 このあたりはどうなんでしょうか? 個人的に期間限定ライセンスには何の魅力も感じないので少し気になります。ライセンス周りは意外に面倒ですね。
このあたり、私は考えすぎるとやらなくなるので、細かいことを気にせずにザクザクやるようにしています。
今回の記事もかなり適当ですので、参考にする場合にはくれぐれも自己責任でお願いします。
何があっても責任は取れません。
ラベル:
LX9 MicroBoard,
Xilinx
登録:
コメント (Atom)