FC2カウンター FPGAの部屋 Avalon-MMスレーブペリフェラル1(ダイナミック点灯7セグLED)
FC2ブログ

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

FPGAの部屋

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

Avalon-MMスレーブペリフェラル1(ダイナミック点灯7セグLED)

FPGA技術No.5の備州長船さんの第3章 THE AVALONM@STER(Avalon-MMスレーブペリフェラルの作成方法)を参考に、ダイナミック7セグメントLED用のAvalon-MMスレーブペリフェラルを試しに作成中だ。
まずはVerilogファイルの作成をした。トップをAvalonMM_Slave_Dyna7seg.vとしてAvalonバスのRead、Writeを実装して、7セグメントLEDのダイナミック点灯回路も実装した。ダイナミック点灯の周波数は1KHz、これは以前から使用しているFreqdiv.vを使用した。7セグメントLEDのデコーダーには、これまた以前から使用しているB27segDec.vを使用した。それぞれのソースを示す。
まずは、AvalonMM_Slave_Dyna7seg.v

// Dynamic 7segment Display test
//    avs_s1_address = 0 : seven_value0(下位5ビットのみ使用)
//    avs_s1_address = 1 : seven_value1(下位5ビットのみ使用)
//    avs_s1_address = 2 : seven_value2(下位5ビットのみ使用)
//    avs_s1_address = 3 : seven_value3(下位5ビットのみ使用)
//    avs_s1_address = 4 : seven_value4(下位5ビットのみ使用)
//    avs_s1_address = 5 : seven_value5(下位5ビットのみ使用)
//    avs_s1_address = 6 : seven_value6(下位5ビットのみ使用)
//    avs_s1_address = 7 : seven_value7(下位5ビットのみ使用)
//  +16をするとDPが点灯


`default_nettype none

module AvalonMM_Slave_Dyna7seg(
    input    wire        csi_global_reset,    // リセット
    input    wire        csi_global_clk,        // クロック
    input    wire[2:0]    avs_s1_address,        // アバロンバス
    input    wire        avs_s1_read,        // アバロンバス
    output    reg[31:0]    avs_s1_readdata,    // アバロンバス
    input    wire        avs_s1_write,        // アバロンバス
    input    wire[31:0]    avs_s1_writedata,    // アバロンバス
    output    wire[7:0]    seven_seg_digit,    // 外部出力、7セグメントLEDデジット
    output    reg            seven_seg_a,        // 外部出力、7セグメントLEDセグメント
    output    reg            seven_seg_b,        // 外部出力、7セグメントLEDセグメント
    output    reg            seven_seg_c,        // 外部出力、7セグメントLEDセグメント
    output    reg            seven_seg_d,        // 外部出力、7セグメントLEDセグメント
    output    reg            seven_seg_e,        // 外部出力、7セグメントLEDセグメント
    output    reg            seven_seg_f,        // 外部出力、7セグメントLEDセグメント
    output    reg            seven_seg_g,        // 外部出力、7セグメントLEDセグメント
    output    reg            seven_seg_dp        // 外部出力、7セグメントLEDセグメント
);
    reg[7:0] seven_value [7:0]; // 7セグメントの表示
    reg[31:0] avs_s1_readdata_node;
    reg[7:0] drive_led; // 7セグLEDを指定するカウント
    wire disp_ena;
    reg[3:0] led_binary;
    wire[6:0] segdecout;
    
    
    // avalon bus write
    always @(posedge csi_global_clk, posedge csi_global_reset) begin : SEVEN_VALUE_PROCESS
        integer i; // ローカル変数iの宣言
        
        if (csi_global_reset) begin
            for (i=0; i<8 ; i=i+1) begin
                seven_value[i] <= 8'd0;
            end
        end else begin
            if (avs_s1_write) begin
                case(avs_s1_address)
                    3'b000 :
                        seven_value[0] <= avs_s1_writedata[7:0];
                    3'b001 :
                        seven_value[1] <= avs_s1_writedata[7:0];
                    3'b010 :
                        seven_value[2] <= avs_s1_writedata[7:0];
                    3'b011 :
                        seven_value[3] <= avs_s1_writedata[7:0];
                    3'b100 :
                        seven_value[4] <= avs_s1_writedata[7:0];
                    3'b101 :
                        seven_value[5] <= avs_s1_writedata[7:0];
                    3'b110 :
                        seven_value[6] <= avs_s1_writedata[7:0];
                    3'b111 :
                        seven_value[7] <= avs_s1_writedata[7:0];
                endcase
            end
        end
    end
    
    // avalon bus read
    always @* begin
        avs_s1_readdata[31:8] = 24'd0;
        case (avs_s1_address)
            3'b000 :
                avs_s1_readdata[7:0] <= seven_value[0];
            3'b001 :
                avs_s1_readdata[7:0] <= seven_value[1];
            3'b010 :
                avs_s1_readdata[7:0] <= seven_value[2];
            3'b011 :
                avs_s1_readdata[7:0] <= seven_value[3];
            3'b100 :
                avs_s1_readdata[7:0] <= seven_value[4];
            3'b101 :
                avs_s1_readdata[7:0] <= seven_value[5];
            3'b110 :
                avs_s1_readdata[7:0] <= seven_value[6];
            default : // 3'b111
                avs_s1_readdata[7:0] <= seven_value[7];
        endcase
    end
    
    // LED選択用シフトレジスタ
    always @(posedge csi_global_clk) begin
        if (csi_global_reset)
            drive_led <= 8'b1111_1110;
        else begin
            if (disp_ena) begin
                if (drive_led[7]==1'b0) // 最後の次はdrive_led[0]を0にする
                    drive_led <= 8'b1111_1110;
                else
                    drive_led <= {drive_led[6:0], 1'b1}; // 0を1ビットシフト
            end
        end
    end
    assign seven_seg_digit = drive_led;
    
    // LEDリフレッシュ周波数分周器
    FreqDiv FreqDiv_inst(
        .clk(csi_global_clk),
        .reset(csi_global_reset),
        .disp_ena(disp_ena)
    );
        
    // 7セグメント・デコーダ
    B27segDec B27segDec_inst(
        .binary(led_binary),
        .enable(1'b1),
        .segdecout(segdecout)
    );
    
    // 7セグメント・デコーダに入れるバイナリデータの選択と出力の選択
    always @(posedge csi_global_clk) begin : SELECTED_BINAREY_DATA
        integer i; // ローカル変数の宣言
        
        if (csi_global_reset) begin
            led_binary <= 4'd0;
            {seven_seg_dp, seven_seg_a, seven_seg_b, seven_seg_c, seven_seg_d, seven_seg_e, seven_seg_f, seven_seg_g} <= -1;
        end else begin
            for(i=0; i<8; i=i+1) begin
                if (drive_led[i]==1'b0) begin
                    led_binary <= seven_value[i];
                    {seven_seg_dp, seven_seg_a, seven_seg_b, seven_seg_c, seven_seg_d, seven_seg_e, seven_seg_f, seven_seg_g} <= {~seven_value[i][4], ~segdecout}; // 0で点灯なのでsegdecoutを反転する
                end
            end
        end
    end
endmodule


(2009/09/04 更新:AvalonMM_Slave_Dyna7seg.vのseven_seg_digit出力に信号がつながっていなかったので、追加しました。詳しくは”Avalon-MMスレーブペリフェラル4(Nios2 IDEで7セグLEDをテスト)”を参照ください)

次はFreqdiv.v

//入力クロックをダイナミック点灯時の周波数(1KHz)に分周する
// 1KHz clock
// マスタークロックを1KHzのdisp_enaに分周します。
// クロック周波数をclk_frequencyにKHz単位で設定してください。defaultで50MHzです。
// lcntが16ビットになっているので、clk_frequencyが65536以上の場合はビットを増やしてください。
// sync resetに変更

`default_nettype none
`timescale 1ns / 1ps

module FreqDiv(clk, reset, disp_ena);
    input clk, reset;
    output disp_ena;
    wire clk, reset;
    wire disp_ena;
    
    reg [20:0] lcnt;
    
    parameter clk_frequency = 50000;
    
    always @(posedge clk) begin
        if (reset)
            lcnt <= 0;
        else if (lcnt==clk_frequency)
            lcnt <= 0;
        else
            lcnt <= lcnt + 1;
    end
    
    assign disp_ena = (lcnt==clk_frequency);
endmodule


最後にB27segDec.v

// Frequncy Divider for Dynamic Lighting
// 1KHz clock
// マスタークロックを1KHzのdisp_enaに分周します。
// クロック周波数をclk_frequencyにKHz単位で設定してください。defaultで50MHzです。
// Binary to 7 segment LED Decoder
// バイナリデータを7セグメントLED用のデータに変換します。
// enableを0にすると消灯。
// 点灯するセグメントを1で表しているが、実際には0で点灯するので上のファイルで反転しています。


`default_nettype none
`timescale 1ns / 1ps

module B27segDec(binary, enable, segdecout);
    input [3:0] binary;
    input enable;
    output [6:0] segdecout;
    
    wire [3:0] binary;
    wire enable;
    reg [6:0] segdecout;
    
    always @* begin
        if (enable)
            case (binary)
                4'h0 :
                    segdecout = 7'b1111110;
                4'h1 :
                    segdecout = 7'b0110000;
                4'h2 :
                    segdecout = 7'b1101101;
                4'h3 :
                    segdecout = 7'b1111001;
                4'h4 :
                    segdecout = 7'b0110011;
                4'h5 :
                    segdecout = 7'b1011011;
                4'h6 :
                    segdecout = 7'b1011111;
                4'h7 :
                    segdecout = 7'b1110000;
                4'h8 :
                    segdecout = 7'b1111111;
                4'h9 :
                    segdecout = 7'b1111011;
                4'hA :
                    segdecout = 7'b1110111;
                4'hB :
                    segdecout = 7'b0011111;
                4'hC :
                    segdecout = 7'b1001110;
                4'hD :
                    segdecout = 7'b0111101;
                default : // F
                    segdecout = 7'b1000111;
            endcase
        else
            segdecout = 7'b1111111;
    end
endmodule


これらをシミュレーションするために、テストベンチAvalonMM_Slave_Dyna7seg_tb.vを作成してVeritakでシミュレーションしてみた。
テストベンチAvalonMM_Slave_Dyna7seg_tb.vを下に示す。

// AvalonMM_Slave_Dyna7seg_tb

`default_nettype none
`timescale 1ns / 1ps

module AvalonMM_Slave_Dyna7seg_tb;
    reg            csi_global_reset;    // リセット
    reg            csi_global_clk;        // クロック
    reg[2:0]    avs_s1_address;        // アバロンバス
    reg            avs_s1_read;        // アバロンバス
    wire[31:0]    avs_s1_readdata;    // アバロンバス
    reg         avs_s1_write;        // アバロンバス
    reg[31:0]    avs_s1_writedata;    // アバロンバス
    wire[7:0]    seven_seg_digit;    // 外部出力、7セグメントLEDデジット
    wire        seven_seg_a;        // 外部出力、7セグメントLEDセグメント
    wire        seven_seg_b;        // 外部出力、7セグメントLEDセグメント
    wire        seven_seg_c;        // 外部出力、7セグメントLEDセグメント
    wire        seven_seg_d;        // 外部出力、7セグメントLEDセグメント
    wire        seven_seg_e;        // 外部出力、7セグメントLEDセグメント
    wire        seven_seg_f;        // 外部出力、7セグメントLEDセグメント
    wire        seven_seg_g;        // 外部出力、7セグメントLEDセグメント
    wire        seven_seg_dp;        // 外部出力、7セグメントLEDセグメント

    parameter CLK_PERIOD = 20;
    
    defparam AvalonMM_Slave_Dyna7seg_inst.FreqDiv_inst.clk_frequency = 5; // シミュレーション用にカウント数を小さくする
    AvalonMM_Slave_Dyna7seg AvalonMM_Slave_Dyna7seg_inst(
        .csi_global_reset(csi_global_reset),
        .csi_global_clk(csi_global_clk),
        .avs_s1_address(avs_s1_address),
        .avs_s1_read(avs_s1_read),
        .avs_s1_readdata(avs_s1_readdata),
        .avs_s1_write(avs_s1_write),
        .avs_s1_writedata(avs_s1_writedata),
        .seven_seg_digit(seven_seg_digit),
        .seven_seg_a(seven_seg_a),
        .seven_seg_b(seven_seg_b),
        .seven_seg_c(seven_seg_c),
        .seven_seg_d(seven_seg_d),
        .seven_seg_e(seven_seg_e),
        .seven_seg_f(seven_seg_f),
        .seven_seg_g(seven_seg_g),
        .seven_seg_dp(seven_seg_dp)
    );
    
    initial begin
                csi_global_reset = 1'b1;
        #100    csi_global_reset = 1'b0;
    end
    
    always begin
       #(CLK_PERIOD/2)    csi_global_clk = 1'b1 ;
       #(CLK_PERIOD/2)    csi_global_clk = 1'b0 ;
    end
    
    task AVALON_BUS_WRITE;
        input[2:0]    write_address;
        input[31:0]    write_data;
        
        begin
            @(posedge csi_global_clk); // 次のクロックの立ち上がり
            #1;
            avs_s1_write = 1'b1; // Write
            avs_s1_address = write_address;
            avs_s1_writedata = write_data;
            @(posedge csi_global_clk); // 次のクロックの立ち上がり
            #1;
            avs_s1_write = 1'b0;
        end
    endtask
    
    task AVALON_BUS_READ;
        input[2:0]    read_address;
        
        begin
            @(posedge csi_global_clk); // 次のクロックの立ち上がり
            #1;
            avs_s1_address = read_address;
            @(posedge csi_global_clk); // 次のクロックの立ち上がり
            #1;
        end
    endtask
    
    initial begin
        avs_s1_address = 3'd0;
        avs_s1_read = 1'b0;
        avs_s1_write = 1'b0;
        avs_s1_writedata = 32'd0;
        
        #100;
        AVALON_BUS_WRITE(3'd0, 32'h0000_0001);
        AVALON_BUS_WRITE(3'd1, 32'h0000_0002);
        AVALON_BUS_WRITE(3'd2, 32'h0000_0003);
        AVALON_BUS_WRITE(3'd3, 32'h0000_0004);
        AVALON_BUS_WRITE(3'd4, 32'h0000_0005);
        AVALON_BUS_WRITE(3'd5, 32'h0000_0006);
        AVALON_BUS_WRITE(3'd6, 32'h0000_0007);
        AVALON_BUS_WRITE(3'd7, 32'h0000_0008);
        #100;
        AVALON_BUS_READ(3'd0);
        AVALON_BUS_READ(3'd1);
        AVALON_BUS_READ(3'd2);
        AVALON_BUS_READ(3'd3);
        AVALON_BUS_READ(3'd4);
        AVALON_BUS_READ(3'd5);
        AVALON_BUS_READ(3'd6);
        AVALON_BUS_READ(3'd7);
        #500;
        $stop;
    end
endmodule


シミュレーションした結果の波形を下に示す。
AvalonMMslave7seg_1_090826.png

次はSOPC Builderでコンポーネントの追加をする予定。
  1. 2009年08月26日 05:20 |
  2. SOPC Builder
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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