· 

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

 MachXO2で自作SPIロジックを動かしたい。MachXO2は規模が小さいので、いったん外部マイコンで制御してみる。LatticeからはMICO8というマイコンコアが提供されているが、LCMXO2-256HC-4TG100Cはあまりにも小さく、組み込めるかどうかわからない、、、というなか、MICO8にチャレンジする勇気がわかないので、いったん外部マイコンでやる。配線がうっとおしいけど。

手順はトップレベルモジュールのコードを書く前まで前回と全く同じ。

プロジェクトを作る。

プロジェクト名はいつも通りFPGA、、、LatticeではWelcome画面でプロジェクト名しか出ないので、実はこれだとかなり不便。Welcome画面が全くの無駄。メニューから開くの一択。まぁいいんです。

ソースは後で入れるので何も追加せず。

デバイスはこう。

とりあえずデフォのまま。

フィニッシュー。

こうなる。

トップレベルモジュールを追加する。

FPGAという名のVHDL。

FPGA.vhdファイルができて開いている状態になる。

MySPIをコピペする。

MySPIをプロジェクトに追加する。

複数ファイルを一気に選べる。

FileListからInputFilesのFPGA.vhdをダブルクリックして開く。

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

ENTITY FPGA IS
        PORT (
                PIO0 : in std_logic_vector(7 downto 0);
                PIO1 : in std_logic_vector(7 downto 0);
                PIO2 : out std_logic_vector(7 downto 0);
                PIO3 : out std_logic_vector(7 downto 0);
                SCK : OUT STD_LOGIC;
                MOSI : OUT STD_LOGIC;
                MISO : IN STD_LOGIC;
                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.50"
                );
                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 MySPI_CONF : STD_LOGIC_VECTOR(7 DOWNTO 0);
        SIGNAL MySPI_STAT : STD_LOGIC_VECTOR(7 DOWNTO 0);
        SIGNAL MySPI_WDATA : STD_LOGIC_VECTOR(7 DOWNTO 0);
        SIGNAL MySPI_RDATA : STD_LOGIC_VECTOR(7 DOWNTO 0);
        
        --signal PIO0 : std_logic_vector(7 downto 0);
        --signal PIO1 : std_logic_vector(7 downto 0);
        --signal PIO2 : std_logic_vector(7 downto 0);
        --signal PIO3 : std_logic_vector(7 downto 0);

BEGIN

        uOSCH : component OSCH
        generic map(
                NOM_FREQ=>"66.50"
        )
        port map(
                STDBY=>'0',
                OSC=>clk66p5,
                SEDSTDBY=>OPEN
        );
        
        MySPI_CONF<=PIO0;
        MySPI_WDATA<=PIO1;
        MySPI_STAT(7 downto 1)<="0000000";
        PIO2<=MySPI_STAT;
        PIO3<=MySPI_RDATA;
        
        MySPI_inst : COMPONENT MySPI PORT MAP(
                NSEND => MySPI_CONF(6 DOWNTO 4),
                NCLKDIV => MySPI_CONF(3 DOWNTO 1),
                CLKIN => clk2p078125,
                CLKOUT => SCK,
                CLKSYS => clk66p5,
                BUSY => MySPI_STAT(0),
                INIT => MySPI_CONF(0),
                WDATA => MySPI_WDATA,
                RDATA => MySPI_RDATA,
                OUTP => MOSI,
                INP => MISO,
                EN => MySPI_CONF(7)
        );

        --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;

まぁ、こんな感じで。MAX10では内部signalとしてNiosコアと接続したPIO0,1,2,3を外に出します。

、、、そういえばクロック安定待ちの信号を全く使ってない。まぁいいや。

では論理合成。プロセスタブを開いて、Lattice Synthesis Engineを右クリックしてRun。

とりあえず成功したけど、クロック条件が満足できていないところがあるらしい。後で考えることにする。

回路を見てみる。

まぁうまくいっているように見える。緑色になっているのは、警告が出ている部分だからかどうかはわからん。

どのピンにどの機能を割り当てるか検討するのでPackageを見る。データシート見るよりはやい。

TopView/BottomViewを切り替えるには、DiamondのメニューViewにTopViewとBottomViewがあるので、選択すればいい。

今回マイコンは外付けでやるので、うちにコロがっていたATmega128を使う。何しろたくさんのピンが必要なので。こちらを使ってArduino化する。英語だけどやり方も書いてある。とはいえたくさんのピンを同時に制御するのでArduinoらしくないコードを書くことになると思うんですが、、、

MegaCoreのGitページの下のほうにピン配があるけど、こんなんです。PAとPCがフルでそろっているので、MySPI_CONFとMySPI_WDATAはそれぞれこれを使う。RDATAはまぁあとでロジアナで見るとして、BUSYはPD0にでもつなぐことにする。

すなわち、こんな感じになる。

Spreadsheet Viewというのでピン割り当てする。

まぁこんな感じで。このピン割り当てだけはIntelのQuartusより使いやすい。

ここで、Ctrl+Sを押して保存する。

書き込むファイルを生成する。

とりあえずうまくいったっぽい。

Programmerを起動。

基本的には自動的にライターを見つけてくれるけど、見つけてくれなかったらいちおうDetectCableを押してみて何とかする。

で、プログレス表示がしばらくあった後、

こうなるので、Programボタンを押す。

こうなって、

こうなる。まぁうまくいったっぽい。

まずはマイコンがちゃんと出していて、SPI出力がちゃんと出ているかどうかという観点で確認します。

ブツを結線。Crazy、、、

裏はもっとCrazy。まぁ人は誰しもそんなもんでしょ。

ロジアナにはこんなものを挿しています。配線の信号をコソ見する。ICクリップで全部つまむのはしんどいから。

uint8_t MYSPI_CONFS[] = {
    0x7A, 0x72, 0x70};
uint8_t MYSPI_WDATA[] = {
    0x56, 0x81, 0x55};

void setup()
{
    DDRA = 0xFF;
    DDRC = 0xFF;
    DDRD &= 0xFE;
}
void loop()
{
    uint8_t stat,rd;
    uint8_t iset;

    for (iset = 0; iset < 3; iset++)
    {
        PORTA = MYSPI_CONFS[iset] | 0x81;
        PORTC = MYSPI_WDATA[iset];
        PORTA = MYSPI_CONFS[iset] | 0x80;
        PORTA = MYSPI_CONFS[iset] | 0x81;
        stat = (PIND & 0x01);
        while (stat & 0x01)
        {
            stat = (PIND & 0x01);
        }
        //rd = PINB;
    }
}

マイコン側のソフトはこんな感じ。MAX10でやったときと内容は同じ。

こうなる。

基本的にはうまくいっているんだけど、ときどき出力がおかしい。

配線が長いせいなのか、論理合成で出ていた警告が関係しているのか、ピン割り当てがイケてないのか、、、今日のところはもう掘るのはよそう。

ひところの半導体不足が解消されてきたかと思っていたら再び入手が難しくなっている。MachXO2シリーズでも秋月で手に入るLCMXO2-256HC-4TG100C以外は全く手に入らない。ホビーユーザが半導体を入手できないのはいいとして、インフラや生活に必要なものの半導体の生産枠は増やしてほしい。

 

先週、更新できなかった。訳あってアクリル板を切ったり穴開けたりArduinoをねじ止めしたりの作業があったので。まぁこの日曜技術者ブログは気分でやってますので、むしろここ最近しっかり更新していたほうが珍しいのです。

 

昨日はきついお仕事があったので今日はまたもやほとんど起きていない。朝飯-->二度寝-->昼飯-->昼寝-->家のこと色々-->そして今これ書いたってわけ。そろそろラクできないもんやろか。