開発日記

Erlangをダラダラ書きます。

カウンタ回路(Chisel)

カウンタ回路の用途

  • イベントのカウント

  • 時間の間隔を定義

時間の間隔を定義?

例えば,クロックのサイクル数をカウントして,指定の時間間隔が経過したら,動作トリガを起動

Chiselの実装例

3サイクル後,0に戻るカウンタ回路

val countReg = RegInit(0.U(32.W))

countReg := Mux(countReg === 2.U, 0.U, countReg + 1.U)

レジスタはクロックの立ち上がりエッジで入力を取り組む.

0 -> 1 -> 2 -> このタイミングで0に戻したい.ので

countReg === 2.U

と設定する.

countRegの出力が2のときにリセットすれば,3サイクルを数えたことになる.

# 豆知識

Chiselでは「レジスタ信号」と「組み合わせ論理信号」を区別するために,レジスタ名の後ろにRegをつける習慣がある.

キャメルケースとサフィックスで分かりやすくしている.

テスト・シミュレーションのメモ(System verilog)

だんだん完成に近づいていく.

`timescale

`timescale 1ns / 100ps

`default_nettype

`timescale 1ns / 100ps
`default_nettype none
...
`default_nettype wire

module

`timescale 1ns / 100ps
`default_nettype none

module testcore();
    ...
endmodule

`default_nettype wire

clk, rst

`timescale 1ns / 100ps
`default_nettype none

module testcore();
    logic clk;
    wire rst = 1;
endmodule

`default_nettype wire

その他信号

`timescale 1ns / 100ps
`default_nettype none

module testcore();
    logic clk;
    wire rst = 1;

    logic foo;
    logic bar;

    wire [31:0] tmp1;
    wire [31:0] tmp2 [31:0];
    wire complete;
endmodule

`default_nettype wire

その他定数

`timescale 1ns / 100ps
`default_nettype none

module testcore();
    logic clk;
    wire rst = 1;

    logic foo;
    logic bar;

    wire [31:0] tmp1;
    wire [31:0] tmp2 [31:0];
    wire complete;

    int maxclocks = 10000000;
    int i, j, k, l;
endmodule

`default_nettype wire

テストするモジュールの宣言

`timescale 1ns / 100ps
`default_nettype none

module testcore();
    logic clk;
    wire rst = 1;

    logic foo;
    logic bar;

    wire [31:0] tmp1;
    wire [31:0] tmp2 [31:0];
    wire complete;

    int maxclocks = 10000000;
    int i, j, k, l;

    moduleA _moduleA(clk, rst, foo, bar, tmp1, tmp2, complete);
endmodule

`default_nettype wire

initial

`timescale 1ns / 100ps
`default_nettype none

module testcore();
    logic clk;
    wire rst = 1;

    logic foo;
    logic bar;

    wire [31:0] tmp1;
    wire [31:0] tmp2 [31:0];
    wire complete;

    int maxclocks = 10000000;
    int i, j, k, l;

    moduleA _moduleA(clk, rst, foo, bar, tmp1, tmp2, complete);

    initial begin
        ...
    end
endmodule

`default_nettype wire

$display

`timescale 1ns / 100ps
`default_nettype none

module testcore();
    logic clk;
    wire rst = 1;

    logic foo;
    logic bar;

    wire [31:0] tmp1;
    wire [31:0] tmp2 [31:0];
    wire complete;

    int maxclocks = 10000000;
    int i, j, k, l;

    moduleA _moduleA(clk, rst, foo, bar, tmp1, tmp2, complete);

    initial begin
        $display("------------ start ---------------");
    end
endmodule

`default_nettype wire

logic宣言した信号初期化

`timescale 1ns / 100ps
`default_nettype none

module testcore();
    logic clk;
    wire rst = 1;

    logic foo;
    logic bar;

    wire [31:0] tmp1;
    wire [31:0] tmp2 [31:0];
    wire complete;

    int maxclocks = 10000000;
    int i, j, k, l;

    moduleA _moduleA(clk, rst, foo, bar, tmp1, tmp2, complete);

    initial begin
        $display("------------ start ---------------");
        clk = 0;
        foo = 0;
        bar = 0;
    end
endmodule

`default_nettype wire

クロック生成

`timescale 1ns / 100ps
`default_nettype none

module testcore();
    logic clk;
    wire rst = 1;

    logic foo;
    logic bar;

    wire [31:0] tmp1;
    wire [31:0] tmp2 [31:0];
    wire complete;

    int maxclocks = 10000000;
    int i, j, k, l;

    moduleA _moduleA(clk, rst, foo, bar, tmp1, tmp2, complete);

    initial begin
        $display("------------ start ---------------");
        clk = 0;
        foo = 0;
        bar = 0;
        
        for (i=1; i<=maxclocks; i++) begin
            for (j=0; j<2; j++) begin
                clk = ~clk;
            end
        end
    end
endmodule

`default_nettype wire

クロックごとの処理

`timescale 1ns / 100ps
`default_nettype none

module testcore();
    logic clk;
    wire rst = 1;

    logic foo;
    logic bar;

    wire [31:0] tmp1;
    wire [31:0] tmp2 [31:0];
    wire complete;

    int maxclocks = 10000000;
    int i, j, k, l;

    moduleA _moduleA(clk, rst, foo, bar, tmp1, tmp2, complete);

    initial begin
        $display("------------ start ---------------");
        clk = 0;
        foo = 0;
        bar = 0;
        
        for (i=1; i<=maxclocks; i++) begin
            if (i == 1000) begin
                foo = 1;
            end
            if (i == 2000) begin
                foo = 0;
            end

            if (i == 3000) begin
                bar = 1;
            end
            if (i == 4000) begin
                bar = 0;
            end

            for (j=0; j<2; j++) begin
                clk = ~clk;
            end

            if (complete) begin
                break;
            end
        end
    end
endmodule

`default_nettype wire

遅延追加

`timescale 1ns / 100ps
`default_nettype none

module testcore();
    logic clk;
    wire rst = 1;

    logic foo;
    logic bar;

    wire [31:0] tmp1;
    wire [31:0] tmp2 [31:0];
    wire complete;

    int maxclocks = 10000000;
    int i, j, k, l;

    moduleA _moduleA(clk, rst, foo, bar, tmp1, tmp2, complete);

    initial begin
        $display("------------ start ---------------");
        clk = 0;
        foo = 0;
        bar = 0;
        
        for (i=1; i<=maxclocks; i++) begin
            if (i == 1000) begin
                foo = 1;
            end
            if (i == 2000) begin
                foo = 0;
            end

            if (i == 3000) begin
                bar = 1;
            end
            if (i == 4000) begin
                bar = 0;
            end

            for (j=0; j<2; j++) begin
                #10
                clk = ~clk;
            end

            if (complete) begin
                break;
            end
        end
       
    end
endmodule

`default_nettype wire

デバッグ出力

`timescale 1ns / 100ps
`default_nettype none

module testcore();
    logic clk;
    wire rst = 1;

    logic foo;
    logic bar;

    wire [31:0] tmp1;
    wire [31:0] tmp2 [2:0];
    wire complete;

    int maxclocks = 10000000;
    int i, j, k, l;

    moduleA _moduleA(clk, rst, foo, bar, tmp1, tmp2, complete);

    initial begin
        $display("------------ start ---------------");
        clk = 0;
        foo = 0;
        bar = 0;
        
        for (i=1; i<=maxclocks; i++) begin
            if (i == 1000) begin
                foo = 1;
            end
            if (i == 2000) begin
                foo = 0;
            end

            if (i == 3000) begin
                bar = 1;
            end
            if (i == 4000) begin
                bar = 0;
            end

            for (j=0; j<2; j++) begin
                #10
                clk = ~clk;
            end

            if (complete) begin
                break;
            end
        end
       
        $display("finish clk    :%5d", i);
        $display("tmp1          :%5d", tmp1);
        $display("tmp2          :%5d, %5d, %5d", tmp2[0], tmp2[1], tmp2[2]);
    end
endmodule

`default_nettype wire

$finish

`timescale 1ns / 100ps
`default_nettype none

module testcore();
    logic clk;
    wire rst = 1;

    logic foo;
    logic bar;

    wire [31:0] tmp1;
    wire [31:0] tmp2 [2:0];
    wire complete;

    int maxclocks = 10000000;
    int i, j, k, l;

    moduleA _moduleA(clk, rst, foo, bar, tmp1, tmp2, complete);

    initial begin
        $display("------------ start ---------------");
        clk = 0;
        foo = 0;
        bar = 0;
        
        for (i=1; i<=maxclocks; i++) begin
            if (i == 1000) begin
                foo = 1;
            end
            if (i == 2000) begin
                foo = 0;
            end

            if (i == 3000) begin
                bar = 1;
            end
            if (i == 4000) begin
                bar = 0;
            end

            for (j=0; j<2; j++) begin
                #10
                clk = ~clk;
            end

            if (complete) begin
                break;
            end
        end
       
        $display("finish clk    :%5d", i);
        $display("tmp1          :%5d", tmp1);
        $display("tmp2          :%5d, %5d, %5d", tmp2[0], tmp2[1], tmp2[2]);

        $display("--------------finish---------------");
        $finish;
    end
endmodule

`default_nettype wire

情報科学における直交性とは?

システム内の要素の多寡を指し示す慣用的な用語

  • どの要素も本質的には同じように振る舞うならば直交性が高いと表現する.

ja.wikipedia.org

CISCは命令の直交性が高い

  • CISCは,任意の演算で、どのアドレッシングモードでも使えるため,「命令の直交性が高い」と言える.

アセンブラ言語が用いられていた頃には豊富なアドレッシングモードを備えて,命令の直交性を備えることが良いと考えられていた.1970年代後半,IBMなどの研究で,実際に使用されているプログラムを解析したところ,複数の処理を一気に行う高機能な命令や,いわゆる直交性のある,命令とアドレッシングモードの組み合わせの大部分は実際のプログラムでは使われていないことが判明した.)

ja.wikipedia.org

RISCレジスタの直交性が高い

ja.wikipedia.org

アドレッシング・モード RV32Iの例

レジスタ間接(ベースオフセット/レジスタオフセット)

ロード命令

1. 符号拡張1バイトロード

レジスタrs1に符号付き符号拡張即値を加えた値を有効アドレスとして1バイトロードして,符号拡張してレジスタrdに書き込む.

ld rd, offset(rs1)
2. ゼロ拡張1バイトロード

レジスタrs1に符号付き拡張即値を加えた値を有効アドレスとして1バイトロードして,ゼロ拡張してレジスタrdに書き込む.

lbu rd, offset(rs1)
3. 符号拡張2バイトロード

レジスタrs1に符号付き拡張即値を加えた値を有効アドレスとして2バイトロードして,符号拡張してレジスタrdに書き込む.

lh rd, offset(rs1)
4. ゼロ拡張2バイトロード

レジスタrs1に符号付き拡張即値を加えた値を有効アドレスとして2バイトロードして,ゼロ拡張してレジスタrdに書き込む.

lhu rd, offset(rs1)
5. 4バイトロード(32bitだから符号拡張なし)

レジスタrs1に符号付き拡張即値を加えた値を有効アドレスとして4バイトロードしてレジスタrdに書き込む.

lhu rd, offset(rs1)

参考文献

qiita.com

アドレッシング・モード

アドレッシング・モードとは?

  • メモリを参照する方式(メモリアドレスの指定の方法)

参照したアドレスはなんて呼ぶ?

  • 有効アドレス(Effective Address)

アドレッシング・モードを構成する要素は?

ベースポインタ/ベースレジスタ
  • アクセスすべきメモリアドレスを格納するレジスタ

  • メモリブロックの先頭アドレスを保持して,オフセット(ディスプレースメント)を利用してメモリブロック内の任意のアドレスを指定するのにも使われる.

即値/イミディエイト
  • 直接アドレス

  • オフセット(ディスプレースメント)

命令用アドレッシング・モードの種類?

絶対アドレス方式
PC相対方式
  • 次の命令のアドレス(PC)にオペランドとして指定したオフセット値を加えたものを有効PCアドレスとする方式

  • 分岐命令でよく見られる.なぜなら,分岐やループは比較的近いアドレスへの分岐するので,絶対アドレスを指定するよりもPCからの相対アドレスで十分.

レジスタ間接方式

データ用アドレッシング・モードの種類?

レジスタ
ベース+オフセット(ディスプレースメント)
  • ベースレジスタの内容に,オフセット値を加えた値を有効アドレスとする.
即値
暗黙
  • 昔はアキュムレータという演算対象となるたった一つのレジスタに暗黙にアクセスしていた.

ja.wikipedia.org

ja.wikipedia.org

RV32E(16ビット圧縮命令)を例に

ロードストア命令

www.aps-web.jp

CISC

  • アドレッシング・モードにレジスタ間接+即値のほかに,レジスタの中の値を使ったり(間接アドレッシング)

  • 参照の前後にインクリメント/デクリメントしたりと多彩

RISC

  • 基本的にRISCはメモリをアクセスする番地を指定するのに,汎用レジスタ+オフセット(レジスタ間接)

  • アドレスの計算は基本的に整数演算命令で行う

keisanki.at.webry.info

メモリとHexファイルの話

FPGAを触っていると,バイナリをHexファイルに変換して読み込みたいときがある.

(例えば,RISC-Vを自作しているときとか..FPGA内蔵メモリにHexファイルを埋め込みたい..

今回は32bit命令を扱うメモリを例に考える.

Hexファイルの形式

1. 1行4バイト

1行4バイトで作成したHexファイルをメモリに埋め込んだとき,このメモリにはどうアクセスする?

32bit命令の例:

命令1
0   00912a23
---------------------------------------
命令2
1   02010413
--------------------------------------
命令3
2   fea42623
--------------------------------------
3   fec42703
4   00100793
5   00e7c663
 .
 .

PCは基本的には4ずつカウントアップする.(ちなみにPCは現在実行している命令のメモリアドレスが格納されている.)

このPCに合わせてアクセスすると3行分の命令を毎回飛ばしてしまう.それは困る.

実装1
  • PCに格納されているアドレスの下位2ビットを無視することで,本来 0 -> 4 -> 8 -> 12 ... となるPCを 0 -> 1 -> 2 -> 3 ... と解釈できる.これでメモリ内の命令を読み飛ばすことはなくなる.よし.
inst <= mem[pc[ADDR_SIZE:2]];

2. 1行1バイト

1行1バイトで作成したHexファイルをメモリに埋め込んだとき,このメモリにはどうアクセスする?

32bit命令の例:

命令1
0   00
1   91
2   2a
3   23
---------------------------
命令2
4   02
5   01
6   04
7   13
 .
 .

PCは現在実行している命令のメモリアドレスの先頭を指している.0番地を指しているときは「0番地, 1番地, 2番地, 3番地」で32bit命令を作りたい.

実装2
  • PCからのオフセットを用いて32bit命令を作成する.

  • トルエンディアンの場合, メモリアドレスの先頭は「LSB」であることに注意.つまり3番地から順番にビット連接する.

リトルエンディアンの場合:
inst <= { mem[pc+3], mem[pc+2], mem[pc+1], mem[pc] };