職業訓練35日目 H8マイコン入門(マイコン制御の基礎)

ノイマン

特徴
  1. プログラム内蔵方式
  2. 逐次処理
  3. 命令とデータの共存

→他に有名なものは「ハーバード型」
 命令とデータを別々の記憶装置に記憶する。

構成

CPU(中央処理装置)
  • 演算装置 → 演算論理装置(ALU)
  • 制御装置
主記憶装置
  • ROM(読み取り専用)
  • RAM(読み書き可能)

命令実行サイクル

フェッチ(メモリから命令を取り出す)
 ↓
デコード
 ↓
実行

使用テキスト

コチラ!

H8マイコン入門

H8マイコン入門

職業訓練34日目 習得度確認テスト(PLDを用いたデジタル回路設計作業)

課題

自由課題。

自由と言われると何も思いつかないので、いつも困るのですが、
他の人がLEDをチカチカさせてるのを見て、思いついた。

そうだ、ナイトライダー回路を作ろう。

仕様


どういうこと?

使用しているCPLDにはLEDが8個付いてるので、
そのLEDを「ナイト2000」のようにチカチカさせる、
って、それだけなんですけどね、それだけ。

出来上がったら結構盛り上がるかな、と思ってたのに、
そもそもナイトライダー自体を知らない人が多かった…、悲しいことです。

VHDL

できれば、RTLレベルで記述したかったけど、
減算とかどうやってやればいいのかも分からないし、
それを調べる時間も手段も無かったので(言い訳)、
思いっきりビヘイビアレベルで書いちゃいました。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGHNED.ALL;

entity knightRider is
    Port ( SW1 : in std_logic;
            CLK : in std_logic;
            LED : OUT std_logic_vector(7 downto 0) );
end knightRider;

architecture Behavioral of knightRider is

    constant SEG_0  : std_logic_vector(7 downto 0) := "00000000";
    constant SEG_1  : std_logic_vector(7 downto 0) := "00000001";
    constant SEG_2  : std_logic_vector(7 downto 0) := "00000011";
    constant SEG_3  : std_logic_vector(7 downto 0) := "00000111";
    constant SEG_4  : std_logic_vector(7 downto 0) := "00001110";
    constant SEG_5  : std_logic_vector(7 downto 0) := "00011100";
    constant SEG_6  : std_logic_vector(7 downto 0) := "00111000";
    constant SEG_7  : std_logic_vector(7 downto 0) := "01110000";
    constant SEG_8  : std_logic_vector(7 downto 0) := "11100000";
    constant SEG_9  : std_logic_vector(7 downto 0) := "11000000";
    constant SEG_9  : std_logic_vector(7 downto 0) := "10000000";

    signal COUNT : std_logic_vector(3 downto 0);
    signal LED_OUT : std_logic_vector(7 downto 0);
    signal WAY : std_logic;    -- LEDの進む方向を保持するフラグ

begin

process(CLK)
begin
    if (CLK'event and CLK = '1') then
        -- ボタンが押されている間だけ、点灯させる。
        -- ボタンを離したら、また右端から点灯させる。
        if (not SW1 = '1') then
            ------
            -- WAYが0のとき、LEDは左向きに進む(←)
            -- WAYが1のとき、LEDは右向きに進む(→)
            ------
            if (WAY = '0') then
                if (COUNT = "1011") then
                    -- 向きを反転、右向きにする。
                    VEC <= '1';
                else
                    COUNT <= COUNT + '1';
                end if;
            elsif ( WAY = '1') then
                if (COUNT = "0000") then
                    -- 向きを反転、左向きにする。
                    VEC <= '0';
                else
                    COUNT <= COUNT - '1';
                end if;
            end if;
        else
            COUNT <= "0000";
        end if;
    end if;
end process;

    LED_OUT <=
    SEG_1 when COUNT = "0001" else
    SEG_2 when COUNT = "0010" else
    SEG_3 when COUNT = "0011" else
    SEG_4 when COUNT = "0100" else
    SEG_5 when COUNT = "0101" else
    SEG_6 when COUNT = "0110" else
    SEG_7 when COUNT = "0111" else
    SEG_8 when COUNT = "1000" else
    SEG_9 when COUNT = "1001" else
    SEG_10 when COUNT = "1010" else
    SEG_0;

    LED <= not LED_OUT;

end Behavioral;

単に、LEDが行って帰ってチカチカするだけなんだけど、
出来たときはものすごく感動しました!
ナイトライダーの曲が頭の中流れたし。

デジカメ持って行って、動画撮っとけば良かったなぁ。

職業訓練33日目 習得度確認テスト(PLDを用いたデジタル回路設計作業)

課題

2ビットバイナリカウンタの設計・製作

仕様
  • クロックでカウント
  • クロック入力は1Hz
  • 7セグメントLEDはカウントダウンを繰り返し(3,2,1,0,3,2,1,0...)
  • カウンタはカウントアップ(00→01→10→11→00...)
悪い例

「カウントダウン」の仕様をすっかり見落とし、
普通にカウントアップする回路を作ったりしてはいけない…orz
それを指摘されて、カウンタ回路をカウントダウンする回路に修正してはいけない…orz

カウンタ回路では、カウントアップをして、
デコーダ回路で、カウントダウンに解釈する。

状態遷移図

お絵描き疲れたからパス…。
遷移図的には、普通のカウンタ回路ですから。

真理値表
  • カウンタ回路
Q1 Q0 Q1' Q0'
0 0 0 1
0 1 1 0
1 0 1 1
1 1 0 0

 

Q1 Q0  a  b  c  d  e  f  g
0 0 1 1 1 1 0 0 1
0 1 1 1 0 1 1 0 1
1 0 0 1 1 0 0 0 0
1 1 1 1 1 1 1 1 0

 

カルノー
  • カウンタ回路

・Q1'

Q1\Q0 0 1
0  
1  

→Q1' = Q1 !Q0 + !Q1 Q0(Q1 xor Q0)
 
・Q0'

Q1\Q0 0 1
0  
1  

→Q0 = !Q0
 

・a

Q1\Q0 0 1
0
1  

→a = !Q1 + Q0
 
・b

Q1\Q0 0 1
0
1

→b = 1
 
・c

Q1\Q0 0 1
0  
1

→c = !Q0 + Q1
 
・d

Q1\Q0 0 1
0
1  

→d = Q1 + Q0
 
・e

Q1\Q0 0 1
0  
1  

→e = Q0
 
・f

Q1\Q0 0 1
0    
1  

→f = Q1 Q0
 
・g

Q1\Q0 0 1
0
1    

→g = !Q1
 

VHDL

今回はVHDL入力だったので、回路図はパス!

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGHNED.ALL;

entity sjdTest is
PORT ( SW1 : in std_logic;
        GCK : in std_logic;
        a : out std_logic;
        b : out std_logic;
        c : out std_logic;
        d : out std_logic;
        e : out std_logic;
        f : out std_logic;
        g : out std_logic );
end sjdTest;

architecture RTL of sjdTest is
    signal CLK : std_logic;
    signal Q0  : std_logic;
    signal Q1  : std_logic;
    signal AD_LGC : std_logic;

begin
    -- テキストでこういう書き方をしてるので同じにしましたが、
    -- 意味がない気がする…。
    CLK <= GCK;

process(CLK)
begin
    if CLK'event and CLK = '1' then
        -- 2ビットバイナリカウンタ
        Q1 <= Q0 xor Q1;
        Q0 <= not Q0;
    end if;
end process;
    -- aとdは同じロジック
    AD_LGC = (not Q0) or Q1;

    -- デコーダ表示部
    a <= not( AD_LGC );
    b <= not( ((not Q0) or Q1) or (Q0 or (not Q1)) );
    c <= not( (not Q0) or Q1 );
    d <= not( AD_LGC );
    e <= not( Q0 );
    f <= not( Q1 and Q0 );
    g <= not( not Q1 );

end RTL;

職業訓練32日目 習得度確認テスト(PLDを用いたデジタル回路設計作業)

課題

2ビットジョンソンカウンタの設計・製作

仕様
  • スイッチを押すとカウントされる
  • クロック入力は16MHz
  • 出力は7セグメントLED
状態遷移図

やっとお絵描きできた!
でも、ちょっと大き過ぎた…。


真理値表
  • カウンター回路
Q1 Q0 SW Q1' Q2'
0 0 0 0 0
0 0 1 0 1
0 1 0 0 1
0 1 1 1 1
1 1 0 1 1
1 1 1 1 0
1 0 0 1 0
1 0 1 0 0

 

Q1 Q2 a b c d e f g
0 0 1 1 1 1 1 1 0
0 1 0 1 1 0 0 0 0
1 1 1 1 0 1 1 0 1
1 0 1 1 1 1 0 0 1

 

カルノー
  • カウンター回路
    • Q1'
Q1\Q0 SW 00 01 11 10
0      
1  

→Q1'= Q1 !SW + Q0 SW

    • Q2'
Q1\Q0 SW 00 01 11 10
0  
1      

→Q2'= !Q1 SW + Q0 !SW
 

・a

Q1\Q0 0 1
0  
1

→a = !Q0 + Q1
・b

Q1\Q0 0 1
0
1

→b = 1
・c

Q1\Q0 0 1
0
1  

→c = !Q1 + !Q0
・d

Q1\Q0 0 1
0  
1

→d = !Q0 + Q1
・e

Q1\Q0 0 1
0  
1  

→e = !Q1 !Q0 + Q1 Q0
・f

Q1\Q0 0 1
0  
1    

→f = !Q1 !Q0
・g

Q1\Q0 0 1
0    
1

→g = Q1
 

回路図

この課題はVHDL入力ではなく、
回路図入力だったので、回路図書いてみました。
MacのExcel2008で書いたんですが、
NOTの白丸が表現できなかったり、
ORのオートシェイプが無かったりであんまりキレイじゃないけど、
かなりがんばって書きました…orz

職業訓練31日目 PLD基本設計 テキスト入力(VHDLによる設計)

本日の講義も昨日の課題の続き。
3bitバイナリカウンタは一応出来てたので、
7セグLED表示も、積和構造で出来るかやってみました。

「when句使っちゃったらビヘイビアじゃん」
と思って訊いてみたら、
「when句は真理値表みたいなもので論理圧縮できるので、十分RTLレベルですよ」
って言われたので、敢えて積和にする必要もないのだけれども…。

状態遷移図は、お絵描きの仕方分からないからパスして真理値表から。

真理値表
Q2 Q1 Q0 a b c d e f g
0 0 0 1 1 1 1 1 1 0
0 0 1 0 1 1 0 0 0 0
0 1 0 1 1 0 1 1 0 1
0 1 1 1 1 1 1 0 0 1
1 0 0 0 1 1 0 0 1 1
1 0 1 1 0 1 1 0 1 1
1 1 0 1 0 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1

 

カルノー
  • a
Q2\Q1 Q0 00 01 11 10
 0  
 1  

→a = !Q2 !Q0 + Q2 Q0 + Q1
 

  • b
Q2\Q1 Q0 00 01 11 10
 0
 1    

→b = !Q2 + !Q1 !Q0 + Q1 Q0
 

  • c
Q2\Q1 Q0 00 01 11 10
 0  
 1

→c = !Q1 + Q0 + Q2
 

  • d
Q2\Q1 Q0 00 01 11 10
 0  
 1    

→d = !Q2 !Q0 + Q2 !Q1 Q0 + !Q2 Q1 + Q1 !Q0
 

  • e
Q2\Q1 Q0 00 01 11 10
 0    
 1      

→e = !Q2 !Q0 + Q1 !Q0
 

  • f
Q2\Q1 Q0 00 01 11 10
 0      
 1  

→f = !Q1 !Q0 + Q2 !Q1 + Q2 !Q0
 

  • g
Q2\Q1 Q0 00 01 11 10
 0    
 1  

→g = Q2 !Q1 + !Q2 Q1 + Q1 !Q0
(もしくは、Q2 !Q1 + !Q2 Q1 + Q2 !Q0)
 

VHDL
    -- 昨日のデコーダ部分を以下の記述で置き換える
    LED(0) <= not( (not(Q2) and not(Q0)) or (Q2 and Q0) or Q1 );
    LED(1) <= not( not(Q2) or (not(Q1) and not(Q0)) or (Q1 and Q0) );
    LED(2) <= not( not(Q1) or Q0 or Q2 );
    LED(3) <= not( (not(Q2) and not(Q0)) or (Q2 and not(Q1) and Q0) or (not(Q2) and Q1) or (Q1 and not(Q0)) );
    LED(4) <= not( (not(Q2) and not(Q0)) or (Q1 and not(Q0) );
    LED(5) <= not( (not(Q1) and not(Q0)) or (Q2 and not(Q1)) or (Q2 and not(Q0) );
    LED(6) <= not( (Q2 and not(Q1)) or (not(Q2) and Q1) or (Q1 and not(Q0)) );

徒労でしょうか…。
いいえ、明日に…。

あと、ちょっとハマったのは、
ブザーを鳴らす回路を作ろうとして、
全然鳴らないなぁ、と思っていたら、
ブザー出力をprocess文内に書いてました。

process文内に出力を書いちゃダメなんだって。
以後気をつけます。

4bitバイナリカウンタも真理値表書いてやってみたんだけど、
カウンタ部分がなぜかうまくいきませんでした…orz

ビヘイビア記述だったらラクなんだけどな。

    if (COUNT = "1001") then
        COUNT <= "0000";
    else
        COUNT <= COUNT + '1';
    end if;

職業訓練30日目 PLD基本設計 テキスト入力(VHDLによる設計)

本日は、課題で3bitバイナリカウンタをVHDLで作りました。
なので、作った回路を晒そうかと思います。
出力は、7セグメントLEDです。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGHNED.ALL;

entity bit3BinaryCounter is
PORT ( SW1 : in std_logic;
        GCK : in std_logic;
        LED : out std_logic_vector(6 downto 0));
end bit3BinaryCounter;

architecture RTL of bit3BinaryCounter is
    -- 7セグ表示パターンの定数
    constant SEG_0 : std_logic_vector(6 downto 0) := "0111111";
    constant SEG_1 : std_logic_vector(6 downto 0) := "0000110";
    constant SEG_2 : std_logic_vector(6 downto 0) := "1011011";
    constant SEG_3 : std_logic_vector(6 downto 0) := "1001111";
    constant SEG_4 : std_logic_vector(6 downto 0) := "1100110";
    constant SEG_5 : std_logic_vector(6 downto 0) := "1101101";
    constant SEG_6 : std_logic_vector(6 downto 0) := "1111101";
    constant SEG_7 : std_logic_vector(6 downto 0) := "1101111";
    constant SEG_8 : std_logic_vector(6 downto 0) := "1111111";

    signal CLK : std_logic;
    signal C0  : std_logic;
    signal C1  : std_logic;
    signal SW  : std_logic;
    signal Q0  : std_logic;
    signal Q1  : std_logic;
    signal Q2  : std_logic;
    signal CNT : std_logic_vector(2 downto 0);
    signal LED_OUT : std_logic_vector(6 downto 0);

    -- マジックナンバーみたいで数字で管理したくないんだけど、
    -- カウンタ回路で論理演算するときの、項の番号を変数名に付けておく。
    signal Q1_1 : std_logic;
    signal Q1_2 : std_logic;
    signal Q1_3 : std_logic;
    signal Q2_1 : std_logic;
    signal Q2_2 : std_logic;
    signal Q2_3 : std_logic;
    signal Q2_4 : std_logic;
begin
    CLK <= GCK2;

    -- カウンタ回路の論理をあらかじめ定義しておく
    Q1_1 <= (not Q1) and Q0 and SW;
    Q1_2 <= Q1 and (not SW);
    Q1_3 <= Q1 and (not Q0);
    Q2_1 <= (not Q2) and Q1 and Q0 and SW;
    Q2_2 <= Q2 and (not SW);
    Q2_3 <= Q2 and (not Q0);
    Q2_4 <= Q2 and (not Q1);
process(CLK)
begin
    if (CLK'event and CLK = '1') then
        -- SW回路
        C0 <= SW1;
        C1 <= C0;
        SW <= not C0 and C1;

        -- カウンタ回路
        Q0 <= Q0 xor (not SW);
        Q1 <= Q1_1 or Q1_2 or Q1_3;
        Q2 <= Q2_1 or Q2_2 or Q2_3 or Q2_4;
    end if;
end process;
    -- 3つの出力を合わせて3bitの数値にする
    CNT(2) <= Q2
    CNT(1) <= Q1
    CNT(0) <= Q0

    -- 7セグ表示用デコーダ
    LED_OUT <=
    SEG_0 when CNT = "000" else
    SEG_1 when CNT = "001" else
    SEG_2 when CNT = "010" else
    SEG_3 when CNT = "011" else
    SEG_4 when CNT = "100" else
    SEG_5 when CNT = "101" else
    SEG_6 when CNT = "110" else
    SEG_7 when CNT = "111" else
    SEG_8;

    -- LEDに出力
    LED <= not LED_OUT;
end RTL;

手元に検証できる環境がないから、合ってるかどうかちょっと不安ですが…。

結局、状態遷移図とか真理値表とか回路図がないと意味分かんないですね…orz

7セグメント1つ1つカルノー図を書いて、
100%RTL記述のものも書いたんですが、時間がないのでこの辺で。

職業訓練29日目 PLD基本設計 テキスト入力(VHDLによる設計)

VHDLの文法

ライブラリ宣言

使用するライブラリを記述。
他の言語で言う、import文、include文か。

library ライブラリ名;
use ライブラリ名.パッケージ名.アイテム;

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
エンティティ宣言


後に出てくるアーキテクチャ宣言を含めて考えると、
クラスみたいなものの方が近いかも。
でも、引数も取れるからクラスと関数宣言を合わせたような感じ?

クラスっていう表現もあまり適当ではないかも。
単に、回路の名前と入力・出力を定義しただけ、
って考えた方がすんなりいくかも。

entity エンティティ名 is
port(信号名, ... : 入出力タイプ 信号タイプ;
   信号名, ... : 入出力タイプ 信号タイプ);
end エンティティ名;

entity ENTITY_NAME is
port(A, B : in std_logic;
     C, D : out std_logic);
end ENTITY_NAME;
信号タイプ

いわゆる「型」ですよね?
vectorが付くと配列っぽい感じ。

  • bit, bit_vector:'1'または'0'
  • std_logic, std_logic_vector:'0', '1', 'U', 'Z'などの値
  • integer:32ビットの整数値

std_logicの取りうる値については、調べたらすぐ出てきた!
std_logic

内部信号宣言

エンティティ内部で使われる信号の宣言。
「変数」ってことで良い?

signal 信号名 : 信号タイプ;

signal A, B : bit;
signal C : std_logic;
signal D : std_logic_vector(7 downto 0);
signal E : std_logic_vector(0 to 7);
アーキテクチャ宣言

回路の機能の記述を行うための宣言。
「関数」や「メソッド」のような感じ?
(再利用っていう面も考えるとcomponentっていうのもあるみたいですが、まだ未利用なのでいまいち分かってません)

architecture アーキテクチャ名 of エンティティ名 is
  -- 回路機能の記述
end アーキテクチャ名;

architecture ARCHITECTURE_NAME of ENTITY_NAME
    -- 回路機能の記述
end ARCHITECTURE_NAME;
シーケンシャルステートメント

回路の機能を記述
process文に含まれる代入文を指す。

「イベントリスナ」に近い感じがする。
特に、センシティビティ・リストに書いてある信号に変化があったときに、
process文の中の処理が実行される、っていうあたりが。
気をつけるべきは、process文内の処理は、
順次処理じゃなく、同時処理ってことですね。
(といっても、そこがいまいち理解できてないポイントだったりしますが…orz)

process(入力信号--センシティビティ・リスト)
begin
  -- プロセス文内の機能記述
end process;

process(A, B)
begin
    C <= A or B;
    D <= A nand B;
end process;

このprocess文の理解が今一歩な感じがするので、リンクも貼っておこっと。
http://laputa.cs.shinshu-u.ac.jp/~yizawa/VHDL/process.htm

if文

if 条件式 then
  -- ステートメント
elsif 条件式 then
  -- ステートメント
else
  -- ステートメント
end if;

if (RESET = '1') then
    COUNT <= "0000";
elsif (CLK'event and CLK = '1') then
    COUNT <= COUNT + '1';
end if;

条件式の括弧は付けなくても大丈夫だったはず。
それでも、多少まどろっこしい間は否めないけど…。

case文

case 信号 is
when 値 => 代入;
when 値 => 代入;
when 値 => 代入;
when others => 代入;
and case;

case SEL is
when "001" => SOUT <= A;
when "010" => SOUT <= B;
when "100" => SOUT <= C;
when others => SOUT <= "UU";
and case
代入式

<=

with SEL select
SOUT <= A when "001",
        B when "010",
        C when "010",
        "UU" when others;

上記で所々使ってきたのでいまさらではありますが…。
テキストの例文を丸写ししちゃいましたが、
when句の使い方がいまいち分からない。
練習問題の回答例も書き方がちょっと違うし。

論理演算子
  • and
  • or
  • nand
  • nor
  • xor
  • not
A <= B and C;
D <= not (B or C);

そのまま書けばOKのようです。
優先順位は「notが高い」ってCPLDのテキストに書いてありました。

関係演算子
  • =:等しい
  • /=:等しくない(!)
  • >:より大きい
  • <:より小さい
  • >=:以上
  • <=:以下

驚きはそんなにないですね、「等しくない」以外は。

クォーテーション
  • 1bit→シングルクォーテーション ex) '0', '1'
  • 2bit以上→ダブルクォーテーション ex) "00", "001", "0010"
算術演算子
  • +:乗算
  • -:減算
  • *:乗算
  • /:除算

驚きは全くないです、そのまま。

その他注意事項
  • 大文字と小文字を区別しない
  • 使用可能な文字は、英字、数字、_(アンダースコア)
  • 最初の文字は英字
  • "--"から行末までがコメント
  • outで宣言された信号を参照できない
D型フリップフロップ
-- クロック信号の変化を捉える
process(CLK)
begin
    -- 立ち上がりエッジの処理
    if (CLK'event and CLK = '1') then
        Q <= D;
    end if;
end process;

「CLK'event」のシングルクォートが何とも気持ち悪いのは自分だけでしょうか?