目次

PicoBlaze

 XilinxのFPGAには、SpartanII以上であれば8ビットの
 マイクロコントローラPicoBlazeをインプリメントして
 使えます。

 PicoBlazeを利用すると、LCD、A/D変換器、D/A変換器
 シリアル通信等のプロセッサを利用した方が、簡便に
 実現できるシステムを、より短時間で実現できます。

 PicoBlazeのハードウエア構成は、以下となっています。



 プログラミングモデルで見ると汎用レジスタ、入力、出力
 メモリがあるシンプルな構造です。

 入力、出力は、各々256バイトあるので、入力→処理→出力が
 単純になるように構成すると、超高速プロセッサになります。

 演算をレジスタとレジスタ、レジスタとメモリに限定して
 いるので、完全なRISCタイプではないですが、RISCタイプ
 に近い構成になっています。

 PicoBlazeの開発手順は、以下。
  1. ファームウエアを、アセンブリ言語で記述
  2. アセンブリ言語をアセンブラでアセンブルし、ROMモジュールに展開
  3. ROMモジュールとPicoBlazeのコンポーネントをプロジェクトに追加
  4. 論理合成、配置配線を実行
 FPGAでシステムを構築する場合、最初の段階で  ファームウエア作成とROMモジュール作成が付加  されたイメージになります。  アセンブリ言語のニモニックは、以下。  ニモニックの内容から、フラグはZ、Cだけに限定されています。  大部分の命令は、オペランドにレジスタ、レジスタとレジスタと  即値の組み合わせで構成されています。  Z80やAVRのプログラムをアセンブリ言語で組んだ経験があれば  簡単にマスターできると思います。  PicoBlazeの動作を確認するため、入力から8ビットを取得し  反転後、出力に8ビット出力するアセンブリ言語プログラム  を作成しました。ブロック図で見ると簡単です。  アセンブリ言語プログラムは、以下。 ;************************ ; program ptst0.psm ;************************ ;++++++++++++++++++++++++ ; redefine register name ;++++++++++++++++++++++++ NAMEREG s0,GR0 NAMEREG s1,GR1 ; NAMEREG s2,GR2 ; NAMEREG s3,GR3 ; NAMEREG s4,GR4 ; NAMEREG s5,GR5 ; NAMEREG s6,GR6 ; NAMEREG s7,GR7 ; NAMEREG s8,GR8 ; NAMEREG s9,GR9 ; NAMEREG sA,GRA ; NAMEREG sB,GRB ; NAMEREG sC,GRC ; NAMEREG sD,GRD ; NAMEREG sE,GRE ; NAMEREG sF,GRF ;++++++++++++++++++++++++ ; I/O address ;++++++++++++++++++++++++ CONSTANT PIN,00 CONSTANT POUT,00 ;++++++++++++++++++++++++ ; entry address ;++++++++++++++++++++++++ ADDRESS 000 ;++++++++++++++++++++++++ ; set 0xff ;++++++++++++++++++++++++ INIT: LOAD GR1,FF ;++++++++++++++++++++++++ ; main loop ;++++++++++++++++++++++++ MAIN: ; get data from input port INPUT GR0,PIN ; inverse XOR GR0,GR1 ; put data to output port OUTPUT GR0,POUT JUMP MAIN  PicoBlazeのアセンブリ言語では、数値は16進数2けたで  指定します。プログラムは、アドレス0から実行するので  「ADDRESS」擬似命令で指定します。  アセンブル、リンクは、「KCPSM3.EXE」を利用します。  このアセンブラを含めた開発ツールは、XilinxのPicoBlazeの  ページからダウンロードします。  ダウンロードするには、XilinxのWebサイトでユーザー登録が  必要になります。  会費やライセンス料を徴収されないので、ユーザー登録して  様々な情報を取得できるようにしておいた方がよいでしょう。  今回はSpartan3に関連するZip形式ファイルをダウンロードし  展開すると以下のディレクトリに必要ファイルが格納されて  いました。  アセンブル、リンクには、次のファイルが必要です。 KCPSM3.EXE ROM_form.coe ROM_form.vhd ROM_form.v kcpsm3.ngc  Zip形式ファイルを展開してできたディレクトリのサブ  ディレクトリの中に、これらのファイルは含まれている  ので、対象とするFPGAのプロジェクトファイルの中に  コピーして使います。  アセンブリ言語のソースコードを、アセンブルするには  以下のようにします。  アセンブラは、超高速で動くので、I/Oリダイレクトを使い  メッセージをファイルに入れ、エラーその他の情報を確認  できるようにします。  I/Oリダイレクトで格納された情報は、次のようになります。 KCPSM3 v1.30. Ken Chapman (Xilinx-UK) 2005 The assembler for KCPSM3 Programmable State Machine PASS 1 - Reading input PSM file ;++++++++++++++++++++++++ ; redefine register name ;++++++++++++++++++++++++ NAMEREG s0,GR0 NAMEREG s1,GR1 ; NAMEREG s2,GR2 ; NAMEREG s3,GR3 ; NAMEREG s4,GR4 ; NAMEREG s5,GR5 ; NAMEREG s6,GR6 ; NAMEREG s7,GR7 ; NAMEREG s8,GR8 ; NAMEREG s9,GR9 ; NAMEREG sA,GRA ; NAMEREG sB,GRB ; NAMEREG sC,GRC ; NAMEREG sD,GRD ; NAMEREG sE,GRE ; NAMEREG sF,GRF ;++++++++++++++++++++++++ ; I/O address ;++++++++++++++++++++++++ CONSTANT PIN,00 CONSTANT POUT,00 ;++++++++++++++++++++++++ ; entry address ;++++++++++++++++++++++++ ADDRESS 000 ;++++++++++++++++++++++++ ; set 0xff ;++++++++++++++++++++++++ INIT: LOAD GR1,FF DISABLE INTERRUPT ;++++++++++++++++++++++++ ; main loop ;++++++++++++++++++++++++ MAIN: ; get data from input port INPUT GR0,PIN ; inverse XOR GR0,GR1 ; put data to output port OUTPUT GR0,POUT JUMP MAIN PASS 2 - Testing Instructions ;++++++++++++++++++++++++ ; redefine register name ;++++++++++++++++++++++++ NAMEREG s0, GR0 NAMEREG s1, GR1 ; NAMEREG s2,GR2 ; NAMEREG s3,GR3 ; NAMEREG s4,GR4 ; NAMEREG s5,GR5 ; NAMEREG s6,GR6 ; NAMEREG s7,GR7 ; NAMEREG s8,GR8 ; NAMEREG s9,GR9 ; NAMEREG sA,GRA ; NAMEREG sB,GRB ; NAMEREG sC,GRC ; NAMEREG sD,GRD ; NAMEREG sE,GRE ; NAMEREG sF,GRF ;++++++++++++++++++++++++ ; I/O address ;++++++++++++++++++++++++ CONSTANT PIN, 00 CONSTANT POUT, 00 ;++++++++++++++++++++++++ ; entry address ;++++++++++++++++++++++++ ADDRESS 000 ;++++++++++++++++++++++++ ; set 0xff ;++++++++++++++++++++++++ INIT: LOAD GR1, FF DISABLE INTERRUPT ;++++++++++++++++++++++++ ; main loop ;++++++++++++++++++++++++ MAIN: ; get data from input port INPUT GR0, PIN ; inverse XOR GR0, GR1 ; put data to output port OUTPUT GR0, POUT JUMP MAIN PASS 3 - Resolving addresses and line labels 000 ;++++++++++++++++++++++++ 000 ; redefine register name 000 ;++++++++++++++++++++++++ 000 NAMEREG s0, GR0 000 NAMEREG s1, GR1 000 ; NAMEREG s2,GR2 000 ; NAMEREG s3,GR3 000 ; NAMEREG s4,GR4 000 ; NAMEREG s5,GR5 000 ; NAMEREG s6,GR6 000 ; NAMEREG s7,GR7 000 ; NAMEREG s8,GR8 000 ; NAMEREG s9,GR9 000 ; NAMEREG sA,GRA 000 ; NAMEREG sB,GRB 000 ; NAMEREG sC,GRC 000 ; NAMEREG sD,GRD 000 ; NAMEREG sE,GRE 000 ; NAMEREG sF,GRF 000 ;++++++++++++++++++++++++ 000 ; I/O address 000 ;++++++++++++++++++++++++ 000 CONSTANT PIN, 00 000 CONSTANT POUT, 00 000 ;++++++++++++++++++++++++ 000 ; entry address 000 ;++++++++++++++++++++++++ 000 ADDRESS 000 000 ;++++++++++++++++++++++++ 000 ; set 0xff 000 ;++++++++++++++++++++++++ 000 INIT: 000 LOAD GR1, FF 001 DISABLE INTERRUPT 002 ;++++++++++++++++++++++++ 002 ; main loop 002 ;++++++++++++++++++++++++ 002 MAIN: 002 ; get data from input port 002 INPUT GR0, PIN 003 ; inverse 003 XOR GR0, GR1 004 ; put data to output port 004 OUTPUT GR0, POUT 005 JUMP MAIN PASS 4 - Resolving Operands 000 ;++++++++++++++++++++++++ 000 ; redefine register name 000 ;++++++++++++++++++++++++ 000 NAMEREG s0, GR0 000 NAMEREG s1, GR1 000 ; NAMEREG s2,GR2 000 ; NAMEREG s3,GR3 000 ; NAMEREG s4,GR4 000 ; NAMEREG s5,GR5 000 ; NAMEREG s6,GR6 000 ; NAMEREG s7,GR7 000 ; NAMEREG s8,GR8 000 ; NAMEREG s9,GR9 000 ; NAMEREG sA,GRA 000 ; NAMEREG sB,GRB 000 ; NAMEREG sC,GRC 000 ; NAMEREG sD,GRD 000 ; NAMEREG sE,GRE 000 ; NAMEREG sF,GRF 000 ;++++++++++++++++++++++++ 000 ; I/O address 000 ;++++++++++++++++++++++++ 000 CONSTANT PIN, 00 000 CONSTANT POUT, 00 000 ;++++++++++++++++++++++++ 000 ; entry address 000 ;++++++++++++++++++++++++ 000 ADDRESS 000 000 ;++++++++++++++++++++++++ 000 ; set 0xff 000 ;++++++++++++++++++++++++ 000 INIT: 000 LOAD GR1, FF 001 DISABLE INTERRUPT 002 ;++++++++++++++++++++++++ 002 ; main loop 002 ;++++++++++++++++++++++++ 002 MAIN: 002 ; get data from input port 002 INPUT GR0, PIN 003 ; inverse 003 XOR GR0, GR1 004 ; put data to output port 004 OUTPUT GR0, POUT 005 JUMP MAIN PASS 5 - Writing reformatted PSM file ptst0.fmt PASS 6 - Writing assembler log file ptst0.log PASS 7 - Writing coefficient file ptst0.coe PASS 8 - Writing VHDL memory definition file ptst0.vhd PASS 9 - Writing Verilog memory definition file ptst0.v PASS 10 - Writing System Generator memory definition file ptst0.m PASS 11 - Writing memory definition files ptst0.hex ptst0.dec ptst0.mem KCPSM3 successful. KCPSM3 complete.  アセンブラは、15種類のファイルを生成すると参考資料に  書かれています。  VHDLでFPGA内部の回路を記述したとしても、VerilogHDL用ファイルも  生成されます。ISEでは、VHDL、VerilogHDLで記述した内容を、混在  させても構わないので、ファイルは残しておきます。  アセンブル、リンクが成功すると、プロジェクトにKCPSM3関係の  ファイルが出来ているので、追加していきます。  次の2つのVHDLコードを追加しました。 kcpsm3.vhd ptst0.vhd  ptst0.vhdは、アセンブル、リンクで生成されたVHDLコードです。  KCPSM3が生成するVHDLコードは、ROMへのアクセス処理を記述した  マイクロプログラムになっています。  PicoBlazeは、1ワードが18ビットのプロセッサなので、18ビットの  中に、命令、アドレス、数値、レジスタ等の情報が埋込まれています。  これらの情報を利用して、実際に動くのはkcpsm3.vhdに記述した回路  になります。  kcpsm3.vhdのエンティティを眺めてみます。 entity kcpsm3 is Port ( address : out std_logic_vector(9 downto 0); instruction : in std_logic_vector(17 downto 0); port_id : out std_logic_vector(7 downto 0); write_strobe : out std_logic; out_port : out std_logic_vector(7 downto 0); read_strobe : out std_logic; in_port : in std_logic_vector(7 downto 0); interrupt : in std_logic; interrupt_ack : out std_logic; reset : in std_logic; clk : in std_logic); end kcpsm3;  エンティティの信号名から、わかることをリストしてみます。  FPGA内部に、PicoBlazeを入れる場合、componentで利用を宣言し  インスタンス指定で実体を作ってから、信号線を割当てます。  component指定   -- picoblaze component   component kcpsm3 is   port ( address : out std_logic_vector( 9 downto 0);   instruction : in std_logic_vector(17 downto 0);   port_id : out std_logic_vector( 7 downto 0);   write_strobe : out std_logic;   out_port : out std_logic_vector(7 downto 0);   read_strobe : out std_logic;   in_port : in std_logic_vector(7 downto 0);   interrupt : in std_logic;   interrupt_ack : out std_logic;   reset : in std_logic;   clk : in std_logic   );   end component ;   -- firmware component   component ptst0 is   port (   address : in std_logic_vector(9 downto 0);   instruction : out std_logic_vector(17 downto 0);   clk : in std_logic   );   end component ;  インスタンス指定   -- picoblaze instance   XPROCESSOR : kcpsm3 port map (   address => iADR ,   instruction => iInst ,   port_id => iPID ,   write_strobe => iWRSTB ,   out_port => iXDout ,   read_strobe => iRDSTB ,   in_port => iXDin ,   interrupt => iINTREQ ,   interrupt_ack => iINTACK ,   reset => iRST ,   clk => iCLK   );   -- firmware ROM instance   PTST0inst : ptst0 port map (   address => iADR ,   instruction => iInst ,   clk => iCLK   );  システム全体として動作するためのVHDLコードは、以下。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity picotst0 is port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- 48MHz -- input XDin : in std_logic_vector(7 downto 0) ; -- output XDout : out std_logic_vector(7 downto 0) ; -- interrupt XIREQ : in std_logic ; XIACK : out std_logic ; -- monitor clock MCLK : out std_logic --; ) ; end picotst0; architecture Behavioral of picotst0 is -- picoblaze component component kcpsm3 is port ( address : out std_logic_vector( 9 downto 0); instruction : in std_logic_vector(17 downto 0); port_id : out std_logic_vector( 7 downto 0); write_strobe : out std_logic; out_port : out std_logic_vector(7 downto 0); read_strobe : out std_logic; in_port : in std_logic_vector(7 downto 0); interrupt : in std_logic; interrupt_ack : out std_logic; reset : in std_logic; clk : in std_logic ); end component ; -- firmware component component ptst0 is port ( address : in std_logic_vector(9 downto 0); instruction : out std_logic_vector(17 downto 0); clk : in std_logic ); end component ; -- CONSTANT CNTMAX : integer := 47 ; CONSTANT CNTHALF : integer := 24 ; -- clock divider signal iCNT : integer range 0 to CNTMAX ; signal iCLK : std_logic ; -- input signal iXDin : std_logic_vector(7 downto 0) ; -- output signal iXDout : std_logic_vector(7 downto 0) ; -- PicoBlaze signal iADR : std_logic_vector(9 downto 0); signal iInst : std_logic_vector(17 downto 0); signal iPID : std_logic_vector(7 downto 0) ; signal iRDSTB : std_logic ; signal iWRSTB : std_logic ; signal iINTREQ : std_logic ; signal iINTACK : std_logic ; signal iRST : std_logic ; begin -- input iRST <= not nRESET ; iINTREQ <= XIREQ ; -- output XIACK <= iINTACK ; -- monitor clock out MCLK <= iCLK ; -- clock divider -- generate 1 MHz process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iCNT <= 0 ; elsif rising_edge(CLOCK) then if ( iCNT = CNTMAX ) then iCNT <= 0 ; else iCNT <= iCNT + 1 ; end if ; end if ; end process ; iCLK <= '1' when ( iCNT < CNTHALF ) else '0' ; -- input process (iCLK) begin if rising_edge(iCLK) then if ( iRDSTB = '1' and iPID = X"00" ) then iXDin <= XDin ; end if ; end if ; end process ; -- output process (iCLK) begin if rising_edge(iCLK) then if ( iWRSTB = '1' and iPID = X"00" ) then XDout <= iXDout ; end if ; end if ; end process ; -- picoblaze instance XPROCESSOR : kcpsm3 port map ( address => iADR , instruction => iInst , port_id => iPID , write_strobe => iWRSTB , out_port => iXDout , read_strobe => iRDSTB , in_port => iXDin , interrupt => iINTREQ , interrupt_ack => iINTACK , reset => iRST , clk => iCLK ); -- firmware ROM instance PTST0inst : ptst0 port map ( address => iADR , instruction => iInst , clk => iCLK ); end Behavioral;  入出力処理に、ポートのIDとストローブ信号を使います。  割込み関係の信号は、使わなくても、ダミーで外部に接続  しておきます。  UCFファイルの内容は、以下。 # system NET "nRESET" LOC = "P73" ; NET "CLOCK" LOC = "P55" ; # input NET "XDin<7>" LOC = "P10" ; NET "XDin<6>" LOC = "P8" ; NET "XDin<5>" LOC = "P7" ; NET "XDin<4>" LOC = "P6" ; NET "XDin<3>" LOC = "P5" ; NET "XDin<2>" LOC = "P4" ; NET "XDin<1>" LOC = "P2" ; NET "XDin<0>" LOC = "P1" ; # output NET "XDout<7>" LOC = "P20" ; NET "XDout<6>" LOC = "P18" ; NET "XDout<5>" LOC = "P17" ; NET "XDout<4>" LOC = "P15" ; NET "XDout<3>" LOC = "P14" ; NET "XDout<2>" LOC = "P13" ; NET "XDout<1>" LOC = "P12" ; NET "XDout<0>" LOC = "P11" ; # interrupt NET "XIREQ" LOC = "P23" ; NET "XIACK" LOC = "P24" ; # monitor clock NET "MCLK" LOC = "P141" ;  PicoBlaze内蔵FPGAの動作は、次のようにテストしました。  8ビットのスイッチとLEDを接続して、スイッチの状態を  入力し、反転後、出力します。  写真では、わかりにくいですが、DIPスイッチが下がっている  ところに対応したLEDが点灯しています。

割込み処理

 PicoBlazeはマイクロコンピュータなので、割込みが使えます。  タイマー割込みで、LEDの点灯パターンを変えていくプログラムを  作成しました。ブロック図は、以下。  ソースコードは、次のように記述しました。 ;++++++++++++++++++++++++ ; scratch SRAM ;++++++++++++++++++++++++ CONSTANT IFLAG,00 ; interrupt flag CONSTANT STACK,3F ;++++++++++++++++++++++++ ; I/O address ;++++++++++++++++++++++++ CONSTANT LED,00 ; LED port CONSTANT AFF,01 ; acknowledge ;++++++++++++++++++++++++ ; entry address ;++++++++++++++++++++++++ ADDRESS 000 ;++++++++++++++++++++++++ ; initialize ;++++++++++++++++++++++++ INIT: ; clear XOR s0,s0 XOR sC,sC ; reset flag STORE s0,IFLAG ; ENABLE INTERRUPT ; LOAD s1,01 ;++++++++++++++++++++++++ ; main loop ;++++++++++++++++++++++++ MAIN: ; get interrupt flag LOAD s2,IFLAG ; masking AND s2,s1 ; judge TEST s2,01 JUMP NZ,MAIN1 ; update LED OUTPUT sC,LED ; update value ADD sC,01 MAIN1: ; JUMP MAIN ;+++++++++++++++++++++++++++ ; Interrupt Service Routine ;+++++++++++++++++++++++++++ ISR: ; push STORE s0,STACK ; set flag LOAD s0,01 STORE s0,IFLAG ; return acknowledge OUTPUT s0,AFF XOR s0,s0 OUTPUT s0,AFF ; pop FETCH s0,STACK ; RETURNI ENABLE ;+++++++++++++++++++++++++ ; Interrupt Entry Address ;+++++++++++++++++++++++++ ADDRESS 3FF JUMP ISR  割込み要求は、Interrupt信号を使います。  割込みはひとつだけで、受付けるとアドレス3FFに記述された  命令を実行します。3FFは、プログラムROMの最終アドレスに  なっているので、処理プログラム(割込みハンドラ)に分岐  するようにコードを記述します。  割込みハンドラでフラグを設定し、Interrupt信号を'H'から'L'に  落とします。割込みから戻るときに、RETURNI命令を使いますが  次の割込みを受付けられるように、オペランドをENABLEにします。  MAIN処理では、フラグを調べてセットされていればリセットします。  フラグがセットされているときには、カウンタ値をLEDに出力し  カウンタ値を更新します。  PicoBlazeのプログラムを作ったなら、割込みを扱う外部回路を  VHDLコードで定義していきます。  クロック分周器   システムクロックは48MHzなので、1MHzまで分周してPicoBlazeの   動作クロックにします。 CONSTANT CNTMAX : integer := 47 ; CONSTANT CNTHALF : integer := 24 ; signal iCNT : integer range 0 to CNTMAX ; signal iCLK : std_logic ; -- clock divider (generate 1MHz) process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iCNT <= 0 ; elsif rising_edge(CLOCK) then if ( iCNT = CNTMAX ) then iCNT <= 0 ; else iCNT <= iCNT + 1 ; end if ; end if ; end process ; iCLK <= '1' when ( iCNT < CNTHALF ) else '0' ;   PicoBlazeの動作クロック48MHzを分周して1kHzを生成し   シーケンサの動作クロックにします。 CONSTANT CCNTMAX : integer := 47_999 ; CONSTANT CCNTHALF : integer := 24_000 ; signal iCCNT : integer range 0 to CCNTMAX ; signal iCCLK : std_logic ; -- clock divider (generate 1kHz) process (nRESET,iCLK) begin if ( nRESET = '0' ) then iCCNT <= 0 ; elsif rising_edge(iCLK) then if ( iCNT = CNTMAX ) then iCCNT <= 0 ; else iCCNT <= iCCNT + 1 ; end if ; end if ; end process ; iCCLK <= '1' when ( iCCNT < CNTHALF ) else '0' ;   1kHzを分周して2Hzを生成します。 CONSTANT SCNTMAX : integer := 499 ; CONSTANT SCNTHALF : integer := 250 ; signal iSCNT : integer range 0 to SCNTMAX ; signal iSCLK : std_logic ; -- clock divider (generate 2Hz) process (nRESET,iCCLK) begin if ( nRESET = '0' ) then iSCNT <= 0 ; elsif rising_edge(iCCLK) then if ( iSCNT = SCNTMAX ) then iSCNT <= 0 ; else iSCNT <= iSCNT + 1 ; end if ; end if ; end process ; iSCLK <= '1' when ( iSCNT < SCNTHALF ) else '0' ;  シンクロナイザ   シーケンサを動かすためにトリガーを使いますが、2Hzを   シフトレジスタに入れ、rising_edgeをつかまえます。 signal iSFT : std_logic_vector(2 downto 0) ; signal iTRG : std_logic ; -- synchronizer (clock 1kHz) process (nRESET,iCCLK) begin if ( nRESET = '0' ) then iSFT <= "000" ; elsif rising_edge(iCCLK) then iSFT <= iSFT(1 downto 0) & iSCLK ; end if ; end process ; iTRG <= '1' when ( iSFT = "011" ) else '0' ;  シーケンサ   PicoBlazeにInterrupt信号を与えるために、シーケンサを   利用します。Interrupt信号をフラグとして扱い、フラグを   リセットするカラクリを利用しているので、シーケンサを   使います。 signal iSTATE : std_logic_vector(1 downto 0) ; signal iRTRG : std_logic ; signal iINTERUPPT : std_logic ; -- sequencer (clock 1kHz) process (nRESET,iCCLK) begin if ( nRESET = '0' ) then iSTATE <= "00" ; elsif rising_edge(iCCLK) then case conv_integer(iSTATE) is -- wait trigger when 0 => if ( iTRG = '1' ) then iSTATE <= "01" ; else iSTATE <= "00" ; end if ; -- impress wait when 1 => iSTATE <= "11" ; -- wait reset flag when 3 => if ( iRTRG = '1' ) then iSTATE <= "10" ; else iSTATE <= "11" ; end if ; -- return first state when 2 => iSTATE <= "00" ; -- default when others => iSTATE <= "00" ; end case ; end if ; end process ; iINTERUPPT <= iSTATE(0) ;  まとめると、次のVHDLコードとなります。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity tstpint is port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- 48MHz -- interrupt XIACK : out std_logic ; -- LED output LOUT : out std_logic_vector(7 downto 0) --; ) ; end tstpint; architecture Behavioral of tstpint is -- picoblaze component component kcpsm3 is port ( address : out std_logic_vector( 9 downto 0); instruction : in std_logic_vector(17 downto 0); port_id : out std_logic_vector( 7 downto 0); write_strobe : out std_logic; out_port : out std_logic_vector(7 downto 0); read_strobe : out std_logic; in_port : in std_logic_vector(7 downto 0); interrupt : in std_logic; interrupt_ack : out std_logic; reset : in std_logic; clk : in std_logic ); end component ; -- firmware component component ttt is port ( address : in std_logic_vector(9 downto 0); instruction : out std_logic_vector(17 downto 0); clk : in std_logic ); end component ; -- CONSTANT CNTMAX : integer := 47 ; CONSTANT CNTHALF : integer := 24 ; CONSTANT CCNTMAX : integer := 47_999 ; CONSTANT CCNTHALF : integer := 24_000 ; CONSTANT SCNTMAX : integer := 499 ; CONSTANT SCNTHALF : integer := 250 ; -- clock divider (generate 1MHz) signal iCNT : integer range 0 to CNTMAX ; signal iCLK : std_logic ; -- clock divider (generate 1kHz) signal iCCNT : integer range 0 to CCNTMAX ; signal iCCLK : std_logic ; -- clock divider (generate 2Hz) signal iSCNT : integer range 0 to SCNTMAX ; signal iSCLK : std_logic ; -- synchronizer (clock 1kHz) signal iSFT : std_logic_vector(2 downto 0) ; signal iTRG : std_logic ; -- sequencer (clock 1kHz) signal iSTATE : std_logic_vector(1 downto 0) ; signal iRTRG : std_logic ; signal iINTERUPPT : std_logic ; -- input to PicoBlaze signal iXDin : std_logic_vector(7 downto 0) := X"00" ; -- output from PicoBlaze signal iXDout : std_logic_vector(7 downto 0) ; -- PicoBlaze signal iADR : std_logic_vector(9 downto 0); signal iInst : std_logic_vector(17 downto 0); signal iPID : std_logic_vector(7 downto 0) ; signal iRDSTB : std_logic := '0' ; signal iWRSTB : std_logic ; signal iINTACK : std_logic ; signal iRST : std_logic ; -- buffer signal iLOUT : std_logic_vector(7 downto 0) ; begin -- input iRST <= not nRESET ; -- output XIACK <= iINTACK ; LOUT <= not iLOUT ; -- clock divider (generate 1MHz) process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iCNT <= 0 ; elsif rising_edge(CLOCK) then if ( iCNT = CNTMAX ) then iCNT <= 0 ; else iCNT <= iCNT + 1 ; end if ; end if ; end process ; iCLK <= '1' when ( iCNT < CNTHALF ) else '0' ; -- clock divider (generate 1kHz) process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iCCNT <= 0 ; elsif rising_edge(CLOCK) then if ( iCNT = CNTMAX ) then iCCNT <= 0 ; else iCCNT <= iCCNT + 1 ; end if ; end if ; end process ; iCCLK <= '1' when ( iCCNT < CNTHALF ) else '0' ; -- clock divider (generate 2Hz) process (nRESET,iCCLK) begin if ( nRESET = '0' ) then iSCNT <= 0 ; elsif rising_edge(iCCLK) then if ( iSCNT = SCNTMAX ) then iSCNT <= 0 ; else iSCNT <= iSCNT + 1 ; end if ; end if ; end process ; iSCLK <= '1' when ( iSCNT < SCNTHALF ) else '0' ; -- synchronizer (clock 1kHz) process (nRESET,iCCLK) begin if ( nRESET = '0' ) then iSFT <= "000" ; elsif rising_edge(iCCLK) then iSFT <= iSFT(1 downto 0) & iSCLK ; end if ; end process ; iTRG <= '1' when ( iSFT = "011" ) else '0' ; -- sequencer (clock 1kHz) process (nRESET,iCCLK) begin if ( nRESET = '0' ) then iSTATE <= "00" ; elsif rising_edge(iCCLK) then case conv_integer(iSTATE) is -- wait trigger when 0 => if ( iTRG = '1' ) then iSTATE <= "01" ; else iSTATE <= "00" ; end if ; -- impress wait when 1 => iSTATE <= "11" ; -- wait reset flag when 3 => if ( iRTRG = '1' ) then iSTATE <= "10" ; else iSTATE <= "11" ; end if ; -- return first state when 2 => iSTATE <= "00" ; -- default when others => iSTATE <= "00" ; end case ; end if ; end process ; iINTERUPPT <= iSTATE(0) ; -- PicoBlaze output process (nRESET,iCLK) begin if ( nRESET = '0' ) then iRTRG <= '0' ; elsif rising_edge(iCLK) then if ( iWRSTB = '1' ) then if ( iPID = X"00" ) then iLOUT <= iXDout ; end if ; if ( iPID = X"01" ) then iRTRG <= iXDout(0) ; end if ; end if ; end if ; end process ; -- picoblaze instance XPROCESSOR : kcpsm3 port map ( address => iADR , instruction => iInst , port_id => iPID , write_strobe => iWRSTB , out_port => iXDout , read_strobe => iRDSTB , in_port => iXDin , interrupt => iINTERUPPT , interrupt_ack => iINTACK , reset => iRST , clk => iCLK ); -- firmware ROM instance TTTinst : ttt port map ( address => iADR , instruction => iInst , clk => iCLK ); end Behavioral;  LEDの点灯パターンを出力するので、UCFの  内容は、単純にしました。 # system NET "nRESET" LOC = "P73" ; NET "CLOCK" LOC = "P55" ; # output NET "LOUT<7>" LOC = "P129" ; NET "LOUT<6>" LOC = "P130" ; NET "LOUT<5>" LOC = "P131" ; NET "LOUT<4>" LOC = "P132" ; NET "LOUT<3>" LOC = "P135" ; NET "LOUT<2>" LOC = "P137" ; NET "LOUT<1>" LOC = "P140" ; NET "LOUT<0>" LOC = "P141" ; # interrupt NET "XIACK" LOC = "P24" ;  Spartan3の基板でテスト。  ダウンロードしたファイルの中に、割込みを  扱うアセンブリ言語コードがありました。 ;Interrupt example ; CONSTANT waveform_port, 02 ;bit0 will be data CONSTANT counter_port, 04 CONSTANT pattern_10101010, AA NAMEREG sA, interrupt_counter ; start: LOAD interrupt_counter,00 ;reset interrupt counter LOAD s2,pattern_10101010 ;initial output condition ENABLE INTERRUPT ; drive_wave: OUTPUT s2,waveform_port LOAD s0,07 ;delay size loop: SUB s0,01 ;delay loop JUMP NZ,loop XOR s2,FF ;toggle waveform JUMP drive_wave ; ADDRESS 2B0 int_routine: ADD interrupt_counter,01 ;increment counter OUTPUT interrupt_counter,counter_port RETURNI ENABLE ; ADDRESS 3FF ;set interrupt vector JUMP int_routine  このアセンブリ言語コードから、割込みが発生した  ときには、アドレス3FFの命令を実行することを把握  できました。  Z80CPUのモード1割込みでは、割込み発生で38hから  命令を実行します。この仕組みに似ているなという  印象をもちました。  相似な仕組みを掴まえてしまうと、プログラムを作成  しやすくなりました。  割込みで実行開始アドレスが唯一であるとき、複数の  割込みを使いたい場合、フラグを用意して並べておき  それらをINPUT命令で読み込んで、判定します。  ARM、PICでは、割込みの実行開始アドレスが唯一なので  割込みハンドラの中で、フラグを見て、対応する処理を  実行します。  ARMの場合、次のように割込みハンドラになります。 void IRQ_Handler(void) __irq { volatile UBYTE ch ; /* judge UART receive interruption */ if ( (IRQSTA & UART_BIT) == UART_BIT ) { /* judge */ if ( COMSTA0 & 1 ) { /* clear flag */ ch = COMRX ; *(sbuf+sindex) = ch ; sindex++ ; if ( ch == '\r' ) { sindex = 0 ; uflag = ON ; } } } /* judge timer0 interruption (10us) */ if ( (IRQSTA & RTOS_TIMER_BIT) == RTOS_TIMER_BIT ) { /* clear timer1 interrupt flag */ T0CLRI = 0xff ; /* increment */ tflag = ON ; } }

ニモニックコンバータ

 PicoBlazeのアセンブリ言語ニモニックを覚えるのは面倒  なので、Pythonを利用し、Z80ニモニックから変換します。  Z80のアセンブラでエラーがなければ、Z80のシミュレータを  利用し、PicoBlazeの動作をシミュレートできます。  数種に分類し、変換処理を考えます。  Program Control   左右に、PicoBlaze、Z80のニモニックを並べてみます。 [PicoBlaze] [Z80]    JUMP aaa JP aaa    JUMP Z,aaa JP Z,aaa    JUMP NZ,aaa JP NZ,aaa    JUMP C,aaa JP C,aaa    JUMP NC,aaa JP NC,aaa    CALL aaa CALL aaa    CALL Z,aaa CALL Z,aaa    CALL NZ,aaa CALL NZ,aaa    CALL C,aaa CALL C,aaa    CALL NC,aaa CALL NC,aaa    RETURN RET    RETURN Z RET Z    RETURN NZ RET NZ    RETURN C RET C    RETURN NC RET NC   ニモニックのJP、RETURNを、JUMP、RETURNに   変換するだけで充分とわかります。  Input/Output   左右に、PicoBlaze、Z80のニモニックを並べてみます。 [PicoBlaze] [Z80]    INPUT sX,pp IN A,(pp)    INPUT sX,(sY) IN A,(C) IN B,(C) IN C,(C) IN D,(C) IN E,(C) IN H,(C)    OUTPUT sX,pp OUT (pp),A    OUTPUT sX,(sY) OUT (C),A OUT (C),B OUT (C),C OUT (C),D OUT (C),E OUT (C),H   Z80では、レジスタをI/Oポインタにする場合   レジスタはCと固定されているので、それに   合わせます。汎用レジスタは、A、B、C、D、E   H、Lなので、7レジスタをsA、sB、sC、sD、sE   sFに対応させます。Lレジスタを使わないように   します。  Logical   左右に、PicoBlaze、Z80のニモニックを並べてみます。 [PicoBlaze] [Z80]    LOAD sX,kk LD A,kk LD B,kk LD C,kk LD D,kk LD E,kk LD H,kk    AND sX,kk AND kk    OR sX,kk OR kk    XOR sX,kk XOR kk    TEST sX,kk BIT kk,A BIT kk,B BIT kk,C BIT kk,D BIT kk,E BIT kk,H    LOAD sX,(sY) LD A,A LD A,B LD A,C LD A,D LD A,E LD A,H LD B,A LD B,B LD B,C LD B,D LD B,E LD B,H LD C,A LD C,B LD C,C LD C,D LD C,E LD C,H LD D,A LD D,B LD D,C LD D,D LD D,E LD D,H LD E,A LD E,B LD E,C LD E,D LD E,E LD E,H LD H,A LD H,B LD H,C LD H,D LD H,E LD H,H    AND sX,(sY) AND A AND B AND C AND D AND E AND H    OR sX,(sY) OR A OR B OR C OR D OR E OR H    XOR sX,(sY) XOR A XOR B XOR C XOR D XOR E XOR H    TEST sX,(sY)   論理演算の結果格納レジスタは、レジスタA固定なので   s0を汎用レジスタのA、B、C、D、E、H、Lの内、7レジ   スタをsA、sB、sC、sD、sE、sFに対応させます。   Lレジスタは使いません。  Shift and Rotate   PicoBlazeのシフト、ローテート命令は、少し変わって   いますが、動作を考えると納得できます。 SR0 shift right with padding '0' SR1 shift right with padding '1' SRX shift right with carry SRA arithmetic shift right SL0 shift left with padding '0' SL1 shift left with padding '1' SLX shift left with carry SLA arithmetic shift left RR rotate right RL rotate left   シフトで空いたビットに0を入れるか1を入れるか   を区別して操作できます。また、算術シフトで数値   の符号を変えないようにもできます。   さらに、キャリーフラグを含めてのシフトができる   ように工夫されています。   左右に、PicoBlaze、Z80のニモニックを並べてみます。 [PicoBlaze] [Z80]    SR0 sX SRL A SRL B SRL C SRL D SRL E SRL H    SR1 sX    SRX sX    SRA sX SRA A SRA B SRA C SRA D SRA E SRA H RR sX RR A RR B RR C RR D RR E RR H    SL0 sX    SL1 sX    SLX sX    SLA sX SLA A SLA B SLA C SLA D SLA E SLA H RL sX RL A RL B RL C RL D RL E RL H   Z80の場合、シフト、ローテートともにキャリーを使うか   使わないかで命令を分けてあるので、完全に対応させる   ことはできません。  Arithmetic   左右に、PicoBlaze、Z80のニモニックを並べてみます。 [PicoBlaze] [Z80]    ADD sX,kk ADD A,kk    ADDCY sX,kk ADC A,kk    SUB sX,kk SUB kk    SUBCY sX,kk SBC kk    COMPARE sX,kk CP kk    ADD sX,sY ADD A ADD B ADD C ADD D ADD E ADD H    ADDCY sX,sY ADC A ADC B ADC C ADC D ADC E ADC H    SUB sX,sY SUB A SUB B SUB C SUB D SUB E SUB H    SUBCY sX,sY SBC A SBC B SBC C SBC D SBC E SBC H    COMPARE sX,sY CP A CP B CP C CP D CP E CP H   Z80の加算、減算、比較の対象の一方は、レジスタA固定   なので、sXをsAとします。sYは、汎用レジスタのA、B、C   D、E、H、Lの内、7レジスタをsA、sB、sC、sD、sE、sFに   対応させます。Lレジスタは使いません。  Storage   左右に、PicoBlaze、Z80のニモニックを並べてみます。 [PicoBlaze] [Z80]    FETCH sX,ss LD A,(ss)    FETCH sX,(sY) LD A,(HL) LD A,(IX) LD A,(IY) LD B,(HL) LD B,(IX) LD B,(IY) LD C,(HL) LD C,(IX) LD C,(IY) LD D,(HL) LD D,(IX) LD D,(IY) LD E,(HL) LD E,(IX) LD E,(IY) LD H,(HL) LD H,(IX) LD H,(IY)    STORE sX,ss LD (ss),A    STORE sX,(sY) LD (HL),A LD (IX),A LD (IY),A LD (HL),B LD (IX),B LD (IY),B LD (HL),C LD (IX),C LD (IY),C LD (HL),D LD (IX),D LD (IY),D LD (HL),E LD (IX),E LD (IY),E LD (HL),H LD (IX),H LD (IY),H   Z80では、メモリポインタは、BC、DE、HL、IX、IYに   なりますが、命令に依存しない3レジスタペアのHL   IX、IYを利用します。HL、IX、IYをs7、s8、s9に対応   させます。   FETCHは、「行って取ってくる」という意味なので   メモリからレジスタに転送するLD命令を利用します。   STOREは、「行って保存する」という意味なので、レジ   スタ値をメモリに転送するLD命令を利用します。  Interrupt   左右に、PicoBlaze、Z80のニモニックを並べてみます。 [PicoBlaze] [Z80]    RETURNI ENABLE RETI    RETURNI DISABLE    ENABLE INTERRUPT EI    DISABLE INTERRUPT DI   Z80には、「RETURNI DISABLE」に相当する命令はないので   変換せずに、対応するニモニックに置換します。  PicoBlazeとZ80のニモニック対応がわかったので、Pythonで  スクリプトを作成します。 (under construction)
目次

inserted by FC2 system