Featured image of post セキュリティキャンプ応募課題晒し(いまさら)

セキュリティキャンプ応募課題晒し(いまさら)

セキュリティキャンプ2024 S02 CPU自作ゼミ 応募課題晒し

こんにちは。WhatACotton です。書きたい書きたいと言って全然書いてこなかった課題晒しをしていきたいと思います。(少し酔いながら書いているので多少内容がブレるかもしれませんがご愛嬌で)

私がセキュリティキャンプ 2024 で受講したゼミは、開発ゼミ S02 FPGA を用いた RISC-V CPU 自作ゼミ という CPU を自作し、それを FPGA ボードに実装して動かしてみるというゼミでした。その内容についても後日書こうと思います。

あと、確認しろと言われればそうなのですが、特に他の方の応募課題晒しは見ていないので一風違ったものになっている可能性があります。ご了承ください()

# 問 1

# 問題

RISC-V の特徴の一つとして、命令セットアーキテクチャ (ISA) がロイヤリティフリーなため、RISC-V の命令セットに対して独自の命令を追加した CPU を設計しても、ISA のライセンスとして問題がないという点があります。このため、RISC-V を推進している様々な組織が命令セットを提案し、有用なものは拡張命令セットとして規格化されています。 例えば最近、規格化された拡張命令の一覧は以下のページに記載されています。 https://wiki.riscv.org/display/HOME/RISC-V+Specification+Status これらの中で興味深いと思うものがあるならば、その理由とともに説明してください。

これらの中に特に興味深いと思うものが無いのであれば、あなたが RISC-V に命令を追加する場合、どのような命令(群)を追加してみたいかを、その命令の仕様 (ニーモニック、フォーマット、動作) とともに説明してください。 既に規格化・提案されているものと重複していても構いません 逆に命令が多すぎるのでサブセットでも良いのでは?と思う場合は、RV32I をベースとしてどの命令を減らすのかとともに、減らすことによる利点・欠点を説明してください。

# 回答

https://jira.riscv.org/browse/RVS-653 この仕様は ACPI のハードウェアの操作をソフトウェアに変換する規格です。この規格は UEFI や SBI にも関連する規格で、かなり複雑なことをしていたので面白いと感じました。具体的にはシャットダウンや、スリープモードをソフトウェア的に実装できるのはすごいと思います。

ACPIFFH の FFH とは Functional Fixed Hardware の略のことで、arm の FFH に関する仕様書を見ると、 “This refers to software(SW) operations that replace a hardware(HW) finction.” とあり、ハードウェアで今まで行われていたことをソフトウェアで行うということで批准されたものです。

ドキュメントがあるので、当然 arm には実装されているらしく、更に、x86 で調べてみると、Chromium で使われているサードパーティー製のライブラリの中に、x86 の命令セットを見つけることができ、acpi.h を見てみると、FFH について定義されている行を見つけました。 以上から、他の CPU でも実装されている仕様が RISC-V でも取り入れられたということがわかりました。

UEFI でも FFH に関する記述を見つけることができました。ここでは ACPI の説明の中に FFH についての言及を見つけることができます。 “ACPI defines the fixed hardware low-level interfaces as a means to convey to the system OEM the minimum interfaces necessary to achieve a level of capability and quality for motherboard configuration and system power management.” OEM に対して電源管理の最小限のインターフェースを提供するものであるということがわかります。

ここまで調べてきて、まずハードウェアで通常電源管理をしているところに、ソフトウェアでそのインターフェースを用意しているということが面白いと思いました。実際に x86 や arm が実装している部分を、RISC-V でも実装が進んできているので、RISC-V もどんどん汎用 CPU に近づいていると思いました。

UEFI のドキュメント: https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#functional-fixed-hardware arm のドキュメント: https://developer.arm.com/documentation/den0048/latest/ chromium での記述: https://chromium.googlesource.com/chromiumos/third_party/coreboot/+/firmware-parrot-2685.B/src/arch/x86/include/arch/acpi.h#72

# 感想

この回答は今の自分が改めて見てみると、すこし面白くはあるが、ISA としての規格や、その実装についての説明がなかったので、今ひとつの回答だったなと思います。というのも、この回答では具体的にどのような命令セットでどのような命令列でどのように実行されるかについては今ひとつ説明がないからです。

ただ、自分としてはこの回答に至った経緯としては、初めて RISC-V というアーキテクチャの開発におけるコミュニティに触れたということもあり、そもそも自分自身が RISC-V についての理解がそこまで足りていなかったというところがあると思います。この頃は RISC-V がモジュール化されており、その中で批准されているものとされていないものがあるということについてそこまで理解していなかったため、しょうがなかったといえばそうかもしれません。

私がこの回答を行う上で気をつけたところは、自分の知識がそこまで深くないことを自覚して、できるだけ多くの資料に基づいた回答を行ったところです。何も資料を参考にせずにタラタラとし持論を述べただけではなんの説得力もないので、できるだけ色々な資料を漁ってテーマに基づいた言及を探しに行くことに焦点を当てて回答を作成していきました。

# 問 2

# 問題

今までに使ったことがある CPU の命令セット (x86_64, Arm, PIC, AVR, RL78, RX, RISC-V, etc… なんでも OK) について自由に記述してください。

# 回答

「アルカディアでゲームを作ろう」というニコニコ動画の動画を見ながら、バイナリエディタで機械語を打ったことがあります。アルカディアはバンダイから 1983 年に発売された家庭用ゲーム機で、その中には Signetics2650 という CPU が入っており、アルカディアのエミュレータを動かしながら、ゲームのカセットとしてバイナリを読ませることで動作させます。私は動画を参考にしながら、文字の色を変えたり少しずつ元のコードから変更を加えることで遊んでいました。

また、x86_64 のアセンブリを授業で少し触りました。例えば、x86_64 では rax の中の下位 32bit が eax という別の名前として名付けられているなどの特徴があり、そのような Signetics2650 には存在していなかったハードウェア依存の仕様があり、面白いと感じました。また、x86 でできることが Signetics2650 よりもあまりにも多かったので、技術の進歩をとても強く感じました。

その他、Cra2yPierr0t さんの教育用 CPU である jacaranda-8 を読んで Verilog で CPU がどのように作られていくのかということを知り、jacaranda-8 はとても分かりやすい命令セットだったので勉強になりました。 CPU 命令セットとは言い難いかもしれませんが、Turing Complete という NAND ゲートから CPU を自作していくというゲームがあり、CPU を自作するステージまでクリアし、自作アセンブリを作るところまで到達しました。 最近この 2 つを通して、実際にどうやって CPU を実装していくかについて大まかな流れを掴むことができました。

# 感想

この回答は自分自身の状況からしては大分書けている回答だったと思います。CPU 自作ゼミに応募しようとしていたのに、そもそものアセンブラや機械語について全然触れてきていませんでした。第一段落の Signetics2650 のプロセッサについての話はもともとニコニコ動画で遭遇した動画から派生した知識なので、本当にたまたまで、第2段落の話は大学の講義、「コンピュータとプログラミング」において、x86_64 プロセッサのアセンブリを触ったところから Signetics2650 の話につなげていったという感じです。

また、jacaranda-8 については課題を解いていく上で色々なものを調べていくうちにたどり着いたものでして、これに関しては、課題の意図である、そもそも今までどのようなプロセッサに触れてきたことがあるのかということについては本題からそれてしまうような回答にはなっていたとは思いますが、色々なことを調べていくうちに、CPU についての理解を大分深めることができたのでとても良い経験だったと思います。また、キャンプ中には jacaranda-8 の開発者の一人であるHeppoko yuki氏とも会うことができ、実際にテープアウトした jacaranda-8 を見せていただきました。本当に実物を見て感動しました。

jacaranda8(本物)

# 問 3

# 問題

本講義では、基本的な RISC-V CPU コアを FPGA に実装して動作を確認したのち、余裕があれば受講者の実装したい機能を実装する時間を設ける予定です。 RISC-V CPU の内部、もしくは外部に実装してみたい機能・回路について説明してください。

# 回答

base64 エンコード・デコードをしてくれる命令セットを実装してみたいです。ハードウェアで実装する意義の一つとして、高速に動作させるというものがあると思うので大量に base64 でエンコード・デコードしたくなったときに使えたら面白いなと思います。 固定長のエンコーダは以下の様に実装できましたが、可変長のエンコーダは作れるのか気になります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
module ENCODER_64(
input wire [7:0] CI0,
input wire [7:0] CI1,
input wire [7:0] CI2,
output wire [7:0] CO0,
output wire [7:0] CO1,
output wire [7:0] CO2,
output wire [7:0] CO3
);

assign CO0[7:6] = 2'b00;
assign CO0[5:0] = CI0[7:2];
assign CO1[7:6] = 2'b00;
assign CO1[5:4] = CI0[1:0];
assign CO1[3:0] = CI1[7:4];
assign CO2[7:6] = 2'b00;
assign CO2[5:2] = CI1[3:0];
assign CO2[1:0] = CI2[7:6];
assign CO3[7:6] = 2'b00;
assign CO3[5:0] = CI2[5:0];
endmodule

# 感想

この回答は自分としてもそこまで悪くない回答なのではないかと思います。ちなみに途中まで実装してみたところは大学の先輩であるところのlaplaさんから、具体的に課題の段階からある程度実装してみたら良いのではないかとアドバイスを受けたので、実際に実装してみたという経緯があります。また先輩からは、自分の回答の日本語の不適切なところや、ぎこちないところの添削をしてもらいました。本当に感謝しています。

# 問 4

# 問 4-a

# 問題

以下のポート定義を持つ、0 から 65535 の 範囲 ( 0 および 65535 を含む) の値を順に、下位ビット側からバイト単位で出力するモジュール mem_read を作成してください。 値は 0 から 65535 の 16 ビットの範囲の値なので、出力するには 2 サイクル必要になります。例えば、 1022 は 16 進数で 0x03fe なので、下位バイト 0xfe を先に出力し、次のサイクルで上位バイト 0x03 を出力します。値を出力する 2 サイクルの間は out_valid1 を出力して、値が有効であることを表してください。

また、値一つ分、つまり 2 バイト分を出力し終えた次のサイクルは 1 サイクルの間 out_valid0 を出力して、値が無効とし、値の出力を行わないでください。

記述内容としては、付属のテストを使って Icarus Verilog でテストをパスすることを目標とします。 (どういう考え方をしたか評価しますので、テストをパスしない内容でもご応募いただければと思います) 逆にテストが足りないと思ったり、付属のテストベンチが使いにくいと思ったのであれば、追加していただいても構いません。

使用可能な SystemVerilog の言語機能は Icarus Verilog でシミュレーションが可能な範囲 (-g2012 オプションを指定して使える SystemVerilog 2012 のサブセット) とします。 また、記述したコードの動作について日本語で説明してください。

信号のタイミング例はテストベンチに同梱の mem_read/waveform.png を参考にしてください。

# 回答

まずは仕様をまとめました。

  • 4 つのステートがある
    • カウンタに値を加算するステート
      • 無条件で
        • out_valid を 0 にする
        • カウンタを加算
        • 下位ビットステートへ
    • 下位ビットを出力するステート
      • カウンタが 65536 だったら
        • out_valid を 0 にする
        • 終了ステートへ
      • カウンタが 65536 ではなかったら
        • out_valid を 1 にする
        • 下位ビットを出力する
        • 上位ビットステートへ
    • 上位ビットを出力するステート
      • 無条件で
        • out_valid を 1 にする
        • 上位ビットを出力する
        • 加算ステートへ
    • 終了ステート
      • 無条件で
        • out_valid を 0 にする
        • 値を出力しない
  • リセットするとき下位ビット出力から実行されるように指定する

実装は以下のようになりました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

`default_nettype none
	module mem_read (
	input wire clock, // クロック入力
	input wire reset, // Active Highの同期リセット入力
	output logic out_valid, // 出力VALID
	output logic [7:0] out_data // 出力データ
);

    reg valid;
    reg [3:0]r_state;
    reg [7:0]out;
    reg [16:0] count;
    assign out_data = out;
    assign out_valid = valid;
    always @(posedge clock)begin
        if(reset)begin
            valid <= 8'h00;
            r_state <= 4'b0010;
            count <= 1'b0;
            out <= 1'b0;
        end else begin
            if(r_state[0])begin
                valid <= 1'b0;
                count <= count + 1'b1;
                r_state <= 4'b0010;//下位ビットステートへ
            end else if(r_state[1])begin
                if(count == 17'h10000)begin
                    r_state <= 4'b1000;//終了ステートへ
                end else begin
                    valid <= 1'b1;
                    out <= count[7:0];
                    r_state <= 4'b0100;//上位ステートへ
                end
            end else if(r_state[2])begin
                valid <= 1'b1;
                out <= count[15:8];
                r_state <= 4'b0001;//加算ステートへ
            end else if(r_state[3])begin
                valid <= 1'b0;
                out <= 1'b0;
            end
        end
    end
endmodule

`default_nettype wire

# 問 4-b

# 問題

4-A で作成したモジュールに、モジュールの出力を受け取れるかどうかを表す 1 ビットの信号 out_ready を追加してください。 out_valid かつ out_ready1 のサイクルのみ、 out_data の出力が更新されます。 ただし、4-A と違い out_valid1 にするサイクルは 4 サイクルに 1 度にする必要はありません。

信号のタイミング例はテストベンチに同梱の mem_read_ready/waveform.png を参考にしてください。

# 回答

4-B の仕様は以下のようになりました。

  • 4 つのステート
    • カウンタに加算するステート
      • 無条件
        • 下位ビットへ
        • out_valid 0
        • カウンタを加算
    • 下位ビットを出力するステート
      • カウンタが 65536 だったら
        • out_valid 0
        • 終了ステートへ
      • out_valid かつ out_ready が 1 のとき
        • out_valid 1
        • 下位ビットを出力
        • 上位ビットへ
    • 上位ビットを出力するステート
      • out_valid かつ out_ready が 1 のとき
        • 上位ビットを出力
        • out_valid 1
        • 加算ステートへ
    • 終了するステート
      • out_valid 0
      • out_data 0
  • リセットするとき下位ビット出力から実行されるように指定する

実装は以下のようになりました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
`default_nettype none
module mem_read (
	input wire clock, // クロック入力
	input wire reset, // Active Highの同期リセット入力
	input wire out_ready, // 出力READY
	output logic out_valid, // 出力VALID
	output logic [7:0] out_data // 現在のステート
);
reg [3:0]r_state;
reg [16:0]count;
reg valid;
reg [7:0]out;
assign out_valid = valid;
assign out_data = out;
always @(posedge clock) begin
	if(reset)begin
		r_state <= 4'b0010;
		valid <= 1'b1;
		count <= 17'd0;
		out <= 8'd0;
	end else begin
	if(r_state[0])begin
	//加算するステート
		if(valid&out_ready)begin
			count <= count + 1'b1;
			valid <= 1'b0;
			r_state <= 4'b0010;//下位ビットステートへ
		end
		end else if(r_state[1]) begin
		//下位ビットを出力するステート
			if(count==17'h10000)begin
			valid <= 1'b0;
			r_state <= 4'b1000;//終了ステートへ
		end else begin
			valid <= 1'b1;
			out <= count[7:0];
			r_state <= 4'b0100;//上位ビットステートへ
		end
		end else if(r_state[2])begin
			//上位ビットを出力するステート
			if(valid&out_ready)begin
				out <= count[15:8];
				valid <= 1'b1;
				r_state <= 4'b0001;//加算ステートへ
			end
		end else if(r_state[3])begin
			//終了ステート
			valid <= 1'b0;
			out <= 1'b0;
		end
	end
end
endmodule
`default_nettype wire

# 感想

このコードに至るまでは、なかなかの時間を要しました。サークルの OB の先輩から verilog の書き方のセオリーを教えていただき、その後自分で仕様を決めて実装していったところ最終的にこのコードになりました。適当に変化を加えてテストベンチを1から通していくよりも、一つ一つ丁寧に仕様を決めて実装したほうがコードもきれいになるし、時間もかからないということを身を持って実感しました。

この課題を解いたことで自分のコンピュータにおける設計についての考え方が随分変わったと思います。

ちなみにこの問題はキャンプ中での CPU からの入出力と、それと接続するためのプロトコルにおいてとても重要な役割を果たしていることがわかり、感動しました。

# まとめ

いかがだったでしょうか。独学なりにかなり頑張ったつもりです。また verilog のセオリーなどに関してはまた気が向いたらまとめてみようかと思います。(自分にそこまでの知識があるかはわからないですが w) このブログを読んで来年度以降の受講生に参考になればと思います。また、軽くなら相談も乗れると思いますので、気軽に連絡をくださると幸いです。

# 補遺

ちなみに、問 4-a で、確かテストベンチも通っていたと思うのですが、完全にセオリーとかを知らなかった状態での自分のコードを以下においておきます。verilog の仕様的に自動で回路が吐かれるところまでゲートレベルで実装しようとしていて、自分の中でもかなり初期の実装であることがわかります。wire の宣言なども一つ一つされており、かなり初期段階のコードだと思います。ただ、自分なりにかなり考えてやっていたのであえてこのコードも公開することにしました。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
`default_nettype none
module mem_read (
    input wire clock, // クロック入力
    input wire reset, // Active Highの同期リセット入力
    output logic out_valid, // 出力VALID
    output logic [7:0] out_data // 出力データ
);

    reg finished=1'b0;//サイクルが終了したかの判定
    reg [7:0] invalid;//未定義値の定義
    //stepの定義
    //step0:下位ビットの出力
    //step1:上位ビットの出力
    //step2:無効な値の出力
    wire stp0;
    wire stp1;
    wire stp2;
    //ステップのカウント
    STEPPER STP(
        .clock(clock),
        .reset(reset),
        .step0(stp0),
        .step1(stp1),
        .step2(stp2)
    );
    wire [15:0] count;//カウント
    wire cout;//キャリーの有無

    //1クロックごとにカウントを増やす
    ADDING_BYTES ADD(
        .clock(clock),
        .reset(reset),
        .add(stp2),
        .out(count),
        .cout(cout)
    );

    //値の出力 クロックの立ち上がりに出力
    always @(posedge clock)begin
        if(~finished) begin
            if (stp0)begin
            //step0の処理
            //下位ビットの出力
            out_data <= count[7:0];
            //有効
            out_valid <= 1'b1;
            end else if (stp1) begin
            //step1の処理
            //上位ビットの出力
            out_data <= count[15:8];
            //有効
            out_valid <= 1'b1;
            end else if(stp2) begin
            //step2の処理
            //無効な値の出力
            out_data <= invalid;
            out_valid <= 1'b0;
            if(cout) begin
                //キャリーがある=最大までカウントしたということなので終了フラグを立てる
                finished <= 1'b1;
            end
        end
        end else begin
        //終了処理
        out_valid <= 1'b0;
        out_data <= invalid;
        end
    end
endmodule
    //クロックごとにstepを進めるモジュール
    //B問題を考慮して遅延のみではなくメモリを用いた実装を行った
module STEPPER(
input wire clock,
input wire reset,
output wire step0,
output wire step1,
output wire step2
);
    wire w_0;
    wire w_1;
    wire w_2;
    wire w_3;
    wire w_4;
    wire w_5;
    //常にsaveはtrueにしているが、ここを変えることでstepの進行を制御できる
    MEMORY MEM0(
        .clock(clock),
        .reset(reset),
        .save(1'b1),
        .value(~w_0),
        .out(w_1)
    );

    MEMORY MEM1(
        .clock(clock),
        .reset(reset),
        .save(1'b1),
        .value(w_1),
        .out(w_2)
    );

    MEMORY MEM2(
        .clock(clock),
        .reset(reset),
        .save(1'b1),
        .value(w_2),
        .out(w_0)
    );

    //1クロック前との差分を取る
    DELAY DLY0(
        .clock(clock),
        .reset(reset),
        .i_data(~w_0),
        .o_data(w_3)
    );

    DELAY DLY1(
        .clock(clock),
        .reset(reset),
        .i_data(w_1),
        .o_data(w_4)
    );

    DELAY DLY2(
        .clock(clock),
        .reset(reset),
        .i_data(w_2),
        .o_data(w_5)
    );

    assign step0 = w_2^w_5;
    assign step1 = w_3^(~w_0);
    assign step2 = w_4^w_1;
endmodule

//メモリモジュール
module MEMORY(
input wire clock,
input wire reset,
input wire save,
input wire value,
output wire out
);
    wire w_0;
    wire w_1;
    wire w_2;
    wire w_3;
    //値を保存するかどうか
    SWITCH_GATE SWC0(
        .in0 (save ),
        .in1 (value ),
        .out (w_1 )
    );
    assign w_2 = w_0 | w_1;
    //値を保存する
    DELAY DELAY0(
        .clock (clock ),
        .reset (reset ),
        .i_data (w_2 ),
        .o_data (w_3 )
    );
    //値を出力
    SWITCH_GATE SWC1(
        .in0 (~save ),
        .in1 (w_3 ),
        .out (w_0 )
    );
    assign out = w_3;
    endmodule

//スイッチゲート
module SWITCH_GATE(
input wire in0,
input wire in1,
output wire out
);
    //値の入出力を切り替える
    assign out =(in0)?in1:1'b0;
    endmodule

    //遅延モジュール
    module DELAY(
        input wire clock,
        input wire reset,
        input wire i_data,
        output wire o_data
    );

    //1クロック遅延させる
    reg r_data;
    assign o_data = r_data;
    always @(posedge clock) begin
        if(reset) begin
        r_data <= 1'b0;
        end else begin
        r_data <= i_data;
        end
    end
endmodule

//16ビットの加算器
//半加算器を16個使って実装
module ADDING_BYTES(
input wire clock,
input wire reset,
input wire add,
output reg [15:0] out,
output wire cout
);
    wire [15:0] w_0;
    always @(posedge clock) begin
        if(reset) begin
        out <= 16'h0000;
        end else if(add) begin
        out <= w_0;
        end
    end
    wire c_0;
    wire c_1;
    wire c_2;
    wire c_3;
    wire c_4;
    wire c_5;
    wire c_6;
    wire c_7;
    wire c_8;
    wire c_9;
    wire c_10;
    wire c_11;
    wire c_12;
    wire c_13;
    wire c_14;
    wire c_15;
    HALF_ADDER HA0(
        .in0(out[0]),
        .in1(1'b1),
        .out(w_0[0]),
        .car(c_0)
    );
    HALF_ADDER HA1(
        .in0(out[1]),
        .in1(c_0),
        .out(w_0[1]),
        .car(c_1)
    );
    HALF_ADDER HA2(
        .in0(out[2]),
        .in1(c_1),
        .out(w_0[2]),
        .car(c_2)
    );
    HALF_ADDER HA3(
        .in0(out[3]),
        .in1(c_2),
        .out(w_0[3]),
        .car(c_3)
    );
    HALF_ADDER HA4(
        .in0(out[4]),
        .in1(c_3),
        .out(w_0[4]),
        .car(c_4)
    );
    HALF_ADDER HA5(
        .in0(out[5]),
        .in1(c_4),
        .out(w_0[5]),
        .car(c_5)
    );
    HALF_ADDER HA6(
        .in0(out[6]),
        .in1(c_5),
        .out(w_0[6]),
        .car(c_6)
    );
    HALF_ADDER HA7(
        .in0(out[7]),
        .in1(c_6),
        .out(w_0[7]),
        .car(c_7)
    );
    HALF_ADDER HA8(
        .in0(out[8]),
        .in1(c_7),
        .out(w_0[8]),
        .car(c_8)
    );
    HALF_ADDER HA9(
        .in0(out[9]),
        .in1(c_8),
        .out(w_0[9]),
        .car(c_9)
    );
    HALF_ADDER HA10(
        .in0(out[10]),
        .in1(c_9),
        .out(w_0[10]),
        .car(c_10)
    );
    HALF_ADDER HA11(
        .in0(out[11]),
        .in1(c_10),
        .out(w_0[11]),
        .car(c_11)
    );
    HALF_ADDER HA12(
        .in0(out[12]),
        .in1(c_11),
        .out(w_0[12]),
        .car(c_12)
    );
    HALF_ADDER HA13(
        .in0(out[13]),
        .in1(c_12),
        .out(w_0[13]),
        .car(c_13)
    );
    HALF_ADDER HA14(
        .in0(out[14]),
        .in1(c_13),
        .out(w_0[14]),
        .car(c_14)
    );
    HALF_ADDER HA15(
        .in0(out[15]),
        .in1(c_14),
        .out(w_0[15]),
        .car(c_15)
    );
    assign cout = c_15;//最上位ビットのキャリー
endmodule

//半加算器
module HALF_ADDER(
input wire in0,
input wire in1,
output wire out,
output wire car
);
    wire w_0;
    wire w_1;
    assign out = in0^in1;
    assign car = in0 & in1;
endmodule

`default_nettype wire
comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。