· 

SPIハードをFPGAで-MachXO2編(1)

日曜技術者としてはIntel(Altera)のFPGAもAMD(Xilinx)のFPGAも巨大すぎる。ロジックが多いのはいいけどパッケージがでかい。パッケージがちいさくてもBGAとかじゃ素人には扱えない。そこでLattice。Lattice Semiconductorは小規模ラインアップが多いのでホビーユーザには魅力的。ユーザ登録とかライセンス入手とかが面倒だけどまぁしょがない。

Lattice Diamondのマニュアルはあまりないのでここで一通りやってみて記録する。

すべてうまくやって、Diamondを起動。

File-->New-->Project...

Next

適当に入力してNext(Max10の教科書のクセでFPGAとしているが、何でもいいんだと思う)。

あとで追加するのでここではなにもせずにNext。

今回は秋月で売っているMachXO2-256HCなので、そう選びます。

ここはこれでいいのかわからんけど、デフォがこうなっているので、そのままNext。

こうなるので、Finish。

トップレベルのモジュールを作るので、File-->New-->File...

VHDLで作る。

ちなみにLattice Diamondではプロジェクト内のコードにおいてどれがトップレベルかどうかは関係はない。どこからも参照されないポートがあるモジュールはおのずとトップレベルって感じになっている。

MySPIをプロジェクトのフォルダにコピペする。

Lattice DiamondはVHDLの拡張子はvhdlではなくvhdなので、変更する。

MySPIを追加。

いっぺんに選べる。

トップレベルモジュールにするつもりで作成したFPGA.vhdをダブルクリックして開く。

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
library machxo2;
use machxo2.all;

ENTITY FPGA IS
        PORT (
                clkout : out STD_LOGIC
        );
END FPGA;

ARCHITECTURE fpga_body OF FPGA IS
        SIGNAL res_n : STD_LOGIC;
        SIGNAL por_n : INTEGER RANGE 0 TO 1;
        SIGNAL por_count : INTEGER RANGE 0 TO 65535;
        CONSTANT POR_MAX : INTEGER := 127;
        SIGNAL clkdiv_count : INTEGER RANGE 0 TO 65535;
        CONSTANT CLKDIV_MAX : INTEGER := 16;
        
        component OSCH
                generic(
                        NOM_FREQ: string:="66.5"
                );
                port(
                        STDBY: in std_logic;
                        OSC: out std_logic;
                        SEDSTDBY: out std_logic
                );
        end component;
        SIGNAL clk66p5 : STD_LOGIC;
        SIGNAL clk2p078125 : STD_LOGIC;

        COMPONENT MySPI IS
                PORT (
                        NSEND : IN STD_LOGIC_VECTOR(2 DOWNTO 0);
                        NCLKDIV : IN STD_LOGIC_VECTOR(2 DOWNTO 0);
                        CLKIN : IN STD_LOGIC;
                        CLKOUT : OUT STD_LOGIC;
                        CLKSYS : IN STD_LOGIC;
                        BUSY : OUT STD_LOGIC;
                        INIT : IN STD_LOGIC;
                        WDATA : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
                        RDATA : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
                        OUTP : OUT STD_LOGIC;
                        INP : IN STD_LOGIC;
                        EN : IN STD_LOGIC
                );
        END COMPONENT MySPI;
        
        signal NSEND : std_logic_vector(2 downto 0):="111";
        signal NCLKDIV : std_logic_vector(2 downto 0):="111";
        signal INIT : std_logic:='1';
        signal WDATA : std_logic_vector(7 downto 0):="01010110";
        signal MISO : std_logic:='1';
        signal EN : std_logic:='1';
        
        signal SCK : std_logic;
        signal BUSY : std_logic;
        signal RDATA : std_logic_vector(7 downto 0);
        signal MOSI : std_logic;

BEGIN

        uOSCH : component OSCH
        generic map(
                NOM_FREQ=>"66.5"
        )
        port map(
                STDBY=>'0',
                OSC=>clk66p5,
                SEDSTDBY=>OPEN
        );

        MySPI_inst : COMPONENT MySPI PORT MAP(
                NSEND => NSEND,
                NCLKDIV => NCLKDIV,
                CLKIN => clk2p078125,
                CLKOUT => SCK,
                CLKSYS => clk66p5,
                BUSY => BUSY,
                INIT => INIT,
                WDATA => WDATA,
                RDATA => RDATA,
                OUTP => MOSI,
                INP => MISO,
                EN => EN
        );

        --internal reset
        PROCESS (clk66p5) BEGIN
                IF clk66p5'event AND clk66p5 = '1' THEN
                        IF por_count = POR_MAX THEN
                                por_n <= 1;
                                por_count <= por_count;
                        ELSE
                                por_n <= 0;
                                por_count <= por_count + 1;
                        END IF;
                END IF;
        END PROCESS;
        res_n <= '1' WHEN por_n = 1 ELSE '0';
        
        process(clk66p5,clkdiv_count) begin
                if clk66p5'event and clk66p5='1' then
                        clkdiv_count<=clkdiv_count+1;
                        if clkdiv_count=CLKDIV_MAX then
                                clkdiv_count<=0;
                        end if;
                        if clkdiv_count=0 then
                                if clk2p078125='1' then
                                        clk2p078125<='0';
                                else
                                        clk2p078125<='1';
                                end if;
                        end if;
                end if;
        end process;
        clkout<=clk2p078125;

END;

いったん、動かさない。MachXO2には内蔵オシレータがあるんだけど、それを使って生成したクロックがちゃんと出ることだけ確認してみる。

Processタブを表示して、Lattice Synthesis Engineを右クリックしてRUN。論理合成する。

成功と言っている。

例によって回路図を見てみる。

まぁ変なことにはなってなさそう。

ピンを割り当てるツールを表示する。

雑にpin1に約2MHzになるようにしたクロックを出力する。ってして保存。

Lattice Programmerで書き込むときはJEDEC Fileというものがあればいいらしい。JEDEC Fileを右クリックしてRUN。

秋月の回路図を参考にしてこんなものを作りました。今回は秋月のFT2232Hモジュールで書き込みます。こんなもので書き込めちゃうのもLattice FPGAのいいところ。ドライバはFTDIのが認識されていればいいんだけど、うちではなぜかMichrochipってなっていたので、削除してFTDIのドライバをインストール。

で、今はこうなっている。

これを押してLattice Programmerを立ち上げる。

ちゃんとつながっていれば、自動的に認識してくれる。

ここいらへんをポチポチ押して、IC名を設定する。

ファイルは拡張子jedのものを選択。

まぁこうなる。

Programボタンを押す。

なんかやってる。

成功裏に終わったっぽい。

成功裏に終わったっぽい。

実測で1.96MHz。まぁちゃんとできているんだろう。

次回はMySPIを動かしてSPIを出してみる。MAX10と比較して回路規模が小さいので、マイコンを埋め込んで動かすことができるのかどうか、、、外部からマイコンで動かすのか、、、はたまたもうちょっと大きいデバイスで試すことになるのか、、、