FC2カウンター FPGAの部屋 XSTのBlock RAMへのロジックのマップを確かめる3
FC2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

XSTのBlock RAMへのロジックのマップを確かめる3

この記事は今日の早朝に公開したのだが、間違っていたのでいったん取り下げ、修正してから再掲載した。

”XSTのBlock RAMへのロジックのマップを確かめる2”でサンプルの組み合わせ回路(加算器)のBlock RAMへのマップについて試してみた。
今回は簡単なステートマシンをBRAMへマップしてみる。ステートマシンをBRAMへマップしたいというのが今回の目的だ。
かなり複雑な制御をすることを考えると、PicoBlaze, MicroBlazeのようなプロセッサでやる方法がある。MicroBlazeの方がC言語でプログラムも組めるし、これで出来たらと思っていたが、” ISEからEDKを内部制御用プロセッサとして使いたい”で動作クロックが合わないことに気が付いた。
次にステートマシンでやる方法があるが、どうもステートマシンを組む時に大きくなると動作周波数のことを考えてしまう。そこで昔のプロセッサのマイクロプログラム方式のようにROMにステートマシンをビルトインできると動作周波数的には良いかな?と思っている。
そこでbram_mapを使って、ステートマシンが、どうやったらBRAMにマップされるかを探ってみることにした。サンプルとしてはトランプカードのうちの1枚を引くゲームのステートマシンを対象としてみた。これはボタンを押している時は、ステートマシンが回って1~dを繰り返し、ボタンを離したところで、そのステートにとどまり、引いたトランプのカードを表示するというものだ。
そのステートマシンのVerilogファイルの一部を下に示す。

`default_nettype none
`timescale 1ns / 1ps

// 1から13までのトランプのカードを表すステートマシン, Verilog2001

(* bram_map="yes" *)
module cards_state_machine(
    input wire reset_sw,
    input wire clk,
    input wire draw,
    input wire draw_ena,
    output reg [3:0] card
);

    parameter        card_one    = 13'b0_0000_0000_0001,
                    card_two    = 13'b0_0000_0000_0010,
                    card_three    = 13'b0_0000_0000_0100,
                    card_four    = 13'b0_0000_0000_1000,
                    card_five    = 13'b0_0000_0001_0000,
                    card_six    = 13'b0_0000_0010_0000,
                    card_seven    = 13'b0_0000_0100_0000,
                    card_eight    = 13'b0_0000_1000_0000,
                    card_nine    = 13'b0_0001_0000_0000,
                    card_ten    = 13'b0_0010_0000_0000,
                    card_jack    = 13'b0_0100_0000_0000,
                    card_queen    = 13'b0_1000_0000_0000,
                    card_king    = 13'b1_0000_0000_0000;
    reg [12:0] current_state, next_state;
    
    always @(posedge clk) begin
        if (reset_sw)
            current_state <= card_one;
        else
            current_state <= next_state;
    end
            
    always @* begin
        case (current_state)
            card_one : begin
                card <= 4'd1;
                if (draw && draw_ena)
                    next_state <= card_two;
                else
                    next_state <= card_one;
            end
            card_two : begin
                card <= 4'd2;
                if (draw && draw_ena)
                    next_state <= card_three;
                else
                    next_state <= card_two;
            end
            card_three : begin
                card <= 4'd3;
                if (draw && draw_ena)
                    next_state <= card_four;
                else
                    next_state <= card_three;
            end
            card_four : begin
                card <= 4'd4;
                if (draw & draw_ena)
                    next_state <= card_five;
                else
                    next_state <= card_four;
            end
            card_five : begin
                card <= 4'd5;
                if (draw && draw_ena)
                    next_state <= card_six;
                else
                    next_state <= card_five;
            end


module宣言の前に(* bram_map="yes" *) をつけて、このステートマシンをBRAMにマップしようとしている。これをインプリメントして見てみた。論理合成ではステートが直線的に遷移するだけなので、ワンホットからグレイコードに変換されている。

Analyzing FSM for best encoding.
Optimizing FSM on signal with gray encoding.
---------------------------
State | Encoding
---------------------------
0000000000001 | 0000
0000000000010 | 0001
0000000000100 | 0011
0000000001000 | 0010
0000000010000 | 0110
0000000100000 | 0111
0000001000000 | 0101
0000010000000 | 0100
0000100000000 | 1100
0001000000000 | 1101
0010000000000 | 1111
0100000000000 | 1110
1000000000000 | 1010
---------------------------


下にインプリメント終了後のFPGA Editorの画面を示す。
bram_map_7_090113.png

List1ウインドウに選択されているのがステートマシンで、BRAMにマップされていないのがわかる。上のステートマシンはalways文を2つ使って、上のalways文でクロックが立ち上がったら次のステートを現在のステートに代入している。下のalways文で組み合わせ回路で次のステートを決定している。ステートマシンには、こういう書き方とalways文を1つ使用して書く書き方があると思う。それを下に示す。

`default_nettype none
`timescale 1ns / 1ps

// 1からKINGまでのトランプカードを表すステートマシン,Direct Verilog2001

(* bram_map="yes" *)
module cards_state_machine(
    input wire reset_sw,
    input wire clk,
    input wire draw,
    input wire draw_ena,
    output reg [3:0] card
);

    parameter        card_one    = 13'b0_0000_0000_0001,
                    card_two    = 13'b0_0000_0000_0010,
                    card_three    = 13'b0_0000_0000_0100,
                    card_four    = 13'b0_0000_0000_1000,
                    card_five    = 13'b0_0000_0001_0000,
                    card_six    = 13'b0_0000_0010_0000,
                    card_seven    = 13'b0_0000_0100_0000,
                    card_eight    = 13'b0_0000_1000_0000,
                    card_nine    = 13'b0_0001_0000_0000,
                    card_ten    = 13'b0_0010_0000_0000,
                    card_jack    = 13'b0_0100_0000_0000,
                    card_queen    = 13'b0_1000_0000_0000,
                    card_king    = 13'b1_0000_0000_0000;
    reg [12:0] current_state;
    
    always @(posedge clk) begin
        if (reset_sw) begin
            card <= 4'd1;
            current_state <= card_one;
        end else begin
            case (current_state)
                card_one : begin
                    if (draw && draw_ena) begin
                        current_state <= card_two;
                        card <= 4'd2;
                    end
                end
                card_two : begin
                    if (draw && draw_ena) begin
                        current_state <= card_three;
                        card <= 4'd3;
                    end
                end
                card_three : begin
                    if (draw && draw_ena) begin
                        current_state <= card_four;
                        card <= 4'd4;
                    end
                end
                card_four : begin
                    if (draw && draw_ena) begin
                        current_state <= card_five;
                        card <= 4'd5;
                    end
                end
                card_five : begin
                    if (draw && draw_ena) begin
                        current_state <= card_six;
                        card <= 4'd6;
                    end
                end


こちらの方がデルタ時間的にはかかりそうだが、動作としては同じだと思う。
これでインプリメントしてみた。やはり論理合成でワンホットがグレイコードに変換されている。FPGA Editorで見てみると、
bram_map_13_090114.png

上のList1ウインドウを見ると、ステートマシンがBRAMにマップされている。
bram_map_14_090114.png
bram_map_15_090114.png

上の図がBRAMの入力、下がBRAMの出力だ。これを見ると入力に出力のbinaryも、draw_node, draw_ena, reset_swも入っている。binaryはステートと一緒に入っている。どっちかだけで良いのでは?と思うが。。。
とにかく2番目の方法でステートマシンを書いて、moduleの前にbram_mapディレクティブを書くとBRAMにマップされることがわかった。


下に示すのが、Verilogコードを間違って公開したコードだ。これだと、cardの値が出るまでにステートの遷移から1クロック遅れてしまう。下図参照
bram_map_11_090114.png

うまく行く上のVerilogコードだときちんとcardの値とステートの遷移が合っている。
bram_map_12_090114.png

`default_nettype none
`timescale 1ns / 1ps

// 1からKINGまでのトランプカードを表すステートマシン,Direct Verilog2001

(* bram_map="yes" *)
module cards_state_machine(
    input wire reset_sw,
    input wire clk,
    input wire draw,
    input wire draw_ena,
    output reg [3:0] card
);

    parameter        card_one    = 13'b0_0000_0000_0001,
                    card_two    = 13'b0_0000_0000_0010,
                    card_three    = 13'b0_0000_0000_0100,
                    card_four    = 13'b0_0000_0000_1000,
                    card_five    = 13'b0_0000_0001_0000,
                    card_six    = 13'b0_0000_0010_0000,
                    card_seven    = 13'b0_0000_0100_0000,
                    card_eight    = 13'b0_0000_1000_0000,
                    card_nine    = 13'b0_0001_0000_0000,
                    card_ten    = 13'b0_0010_0000_0000,
                    card_jack    = 13'b0_0100_0000_0000,
                    card_queen    = 13'b0_1000_0000_0000,
                    card_king    = 13'b1_0000_0000_0000;
    reg [12:0] current_state;
    
    always @(posedge clk) begin
        if (reset_sw)
            current_state <= card_one;
        else begin
            case (current_state)
                card_one : begin
                    card <= 4'd1;
                    if (draw && draw_ena)
                        current_state <= card_two;
                end
                card_two : begin
                    card <= 4'd2;
                    if (draw && draw_ena)
                        current_state <= card_three;
                end
                card_three : begin
                    card <= 4'd3;
                    if (draw && draw_ena)
                        current_state <= card_four;
                end
                card_four : begin
                    card <= 4'd4;
                    if (draw && draw_ena)
                        current_state <= card_five;
                end
                card_five : begin
                    card <= 4'd5;
                    if (draw && draw_ena)
                        current_state <= card_six;
                end


こちらの方がデルタ時間的にはかかりそうだが、動作としては同じだと思う。
これでインプリメントしてみた。やはり論理合成でワンホットがグレイコードに変換されている。FPGA Editorで見てみると、
bram_map_8_090114.png

上のList1ウインドウを見ると、ステートマシンがBRAMにマップされている。
bram_map_9_090114.png
bram_map_10_090114.png

上の図がBRAMの入力、下がBRAMの出力だ。これを見るとアドレス入力に出力のbinaryも入っている。ステートと一緒に入っている。どっちかだけで良いのでは?と思うが。。。(間違ってるほうはdraw_node, draw_ena, reset_sw はアドレス入力に入っていない)
  1. 2009年01月14日 20:59 |
  2. その他のXilinxのツールについて
  3. | トラックバック:0
  4. | コメント:2

コメント

合成でビットの割付を変えてしまうことがあるのですね。知りませんでした。
ところで、current_stateは,右クリック->表示の選択->enum選択で、ステート名で表示できます。
  1. 2009/01/15(木) 21:41:51 |
  2. URL |
  3. たっく #-
  4. [ 編集 ]

たっくさん

Veritakはそれでステート名を表示できるのですね。ModelSimは右クリックからRadixを選択して簡単にステート名を表示できないですよね?
これもVeritakのメリットですね。
  1. 2009/01/15(木) 21:52:49 |
  2. URL |
  3. marsee #-
  4. [ 編集 ]

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
https://marsee101.blog.fc2.com/tb.php/1010-d6965d10
この記事にトラックバックする(FC2ブログユーザー)