ディジタル回路 第1回 ガイダンス、CMOSの基本回路

計算機構成 第8回
POCOの構造とVerilog記述
テキスト第5章-第6章
情報工学科
天野英晴
今日の目標はPOCOのVerilog記述
の理解
• 論理合成と性能評価に入る前に、POCOの
プログラム、Verilog記述について確認してお
く
• 今までのPOCO関係の演習で出してないの
があれば、今日中にやっておこう
• フルスクラッチシリーズは後に機会を設ける
ので今日やらなくて良い
POCOの全体構成
‘0’
2:0
00 01 10
extext11
pcsel
THB
ADD
00
01
10
S Y
A
+
zero
‘1’
+
0
1
zero
7:0
comsel
10:0
00
7:0
pcjr
B
rf_a
rf_b
alu_bsel
01
10
ext ext0
aadr
10:8
PC
‘7’
0
1
badr
cadr
rf_c
casel
rf_csel
00
01
rwe
10
7:5
idatain
ddatain
iaddr
…
命令メモリ
ddataout
データメモリ
…
daddr
we
もう決まっちゃってる所
‘0’
2:0
00 01 10
+
zero
‘1’
+
1
0
PC
extext11
pcsel
zero
7:0
THB
ADD
10:0
00
01
10
S Y
rfile rfile_1(.clk(clk),
A .a(rf_a),
B .aadr(rd),
.b(rf_b), .badr(rs),
alu_bsel
comsel
00 .we(rwe));
01 10
.c(rf_c), .cadr(cadr),
7:0
pcjr
rf_a
assign {opcode,rd,rs,func} = idatain;
10:8
assign imm=idatain[7:0];
‘7’
rf_b
ext ext0
aadr
0
1
badr
cadr
rf_c
casel
rf_csel
assign ddataout = rf_a;
00
01
rwe
10
assign daddr = rf_b;
7:5
idatain
ddatain
assign we = st_op;
iaddr
…
assign iaddr = pc;
命令メモリ
ddataout
データメモリ
…
daddr
we
データパスの記述
‘0’
assign com =
(addi_op|laddiu_op) ? `ALU_ADD:
(ldi_op|ldiu_op)?`ALU_LD: func[2:0];
2:0
00 01 10
extext11
pcsel
THB
ADD
00
01
10
S Y
A
+
zero
‘1’
+
alu_b
pcjr
1
0 assign
zero
7:0
comsel
10:0
=
(addi_op|addiu_op)?
{ {8{imm[7]}},imm} :
PC
(addiu_opldiu_op)?
{8’b0,imm}: rf_b;
B
00
7:0
rf_a
rf_b
alu_bsel
01
10
ext ext0
aadr
10:8
‘7’
0
1
badr
cadr
rf_c
casel
rf_csel
00
01
rwe
10
7:5
idatain=
assign cadr
jal_op?
3’b111 : rd;
ddatain
assign rwe = ld_op | alu_op | ldi_op |
ldiu_op | addi_op | addiu_op..|jal_op;
iaddr
…
命令メモリ
assign rf_c =
ld_op ? ddatin:
ddataout
jal_op ? pc+1: alu_y;
データメモリ
…
daddr
we
pc周辺
‘0’
2:0
00 01 10
pcsel
extext11
THB
ADD
00
01
10
S Y
A
+
‘1’
+
0
1
pcjr
PC
B
always @(posedge clk or negedge rst_n)
alu_bsel
comsel
zero
begin
7:0
10:0
00 01 10
zero if(!rst_n) pc <= 0;
else if ((bez_op & rf_a == 16’b0) | (bnz_op & rf_a !=ext
16’b0)
|
ext0
7:0
rf_a
rf_b
(bpl_op & ~rf_a[15])
| (bmi_op & rf_a[15]))
pc<=pc+{{8imm[7]}},imm}+1;
aadr
10:8
else if (jmp_op| jal_op)
badr
0
cadr
pc <= pc +{{5{idatain[10]}},idatain[10:0]}}+1;
1
‘7’
else if(jr_op)
rf_c
rwe
pc <= rf_a;
rf_csel
casel
00 01 10
else
pc<=pc+1;
7:5
end
idatain
ddatain
iaddr
…
命令メモリ
ddataout
データメモリ
…
daddr
we
なぜPC周辺のみ図とマッチしないか?
• always文の中はif文やcase文が使えて便利なの
でなるべく中で判断までやってしまう
• 他の部分でも、マルチプレクサの制御信号などは明
示的に書いていない
→今回の書き方は、テキストの図に忠実に、しかし以
下を例外とした
マルチプレクサの制御信号は明示的に書かない
→ 書いても面倒なだけなので、、
レジスタに対する書き込み制御は always文内に書く
→ 無理に外に出すより読みやすいので、、
pocoz.v:図に完全に忠実に書いた
記述(decode.vの後半)
assign we = st_op;
assign rwe = ld_op | alu_op | ldi_op | ldiu_op | addi_op | addiu_op | ldhi_op
| jal_op ;
assign rf_csel = ld_op ? 2'b01 : jal_op ? 2'b10: 2'b00 ;
assign alu_bsel = (addi_op | ldi_op) ? 2'b01 :
(addiu_op | ldiu_op) ? 2'b10 :
ldhi_op? 2'b11 : 2'b00;
assign comsel = (addi_op | addiu_op) ? 2'b01 :
(ldi_op | ldiu_op | ldhi_op) ? 2'b10 : 2'b00;
assign casel = jal_op;
assign pcjr = jr_op;
assign pcsel = (bez_op & zero | bnz_op & ~zero |
bpl_op & ~mi | bmi_op & mi) ? 2'b01 :
jmp_op | jal_op ? 2'b10 : 2'b00;
endmodule
pocoz.v:図に完全に忠実に書いた
記述 モジュール decode.v
include "def.h"
module decode(
input [`OPCODE_W-1:0] opcode,
input [`OPCODE_W-1:0] func,
input zero, mi,
output [1:0] alu_bsel, comsel, rf_csel, pcsel,
output we, rwe, casel, pcjr);
wire st_op, bez_op, bnz_op, bmi_op, bpl_op, addi_op, ld_op, alu_op;
wire ldi_op, ldiu_op, ldhi_op, addiu_op, jmp_op, jal_op, jr_op, jalr_op;
assign st_op = (opcode == `OP_REG) & (func == `F_ST);
assign ld_op = (opcode == `OP_REG) & (func == `F_LD);
assign jr_op = (opcode == `OP_REG) & (func == `F_JR);
assign jalr_op = (opcode == `OP_REG) & (func == `F_JALR);
assign alu_op = (opcode == `OP_REG) & (func[4:3] == 2'b00);
assign ldi_op = (opcode == `OP_LDI);
assign ldiu_op = (opcode == `OP_LDIU);
assign addi_op = (opcode == `OP_ADDI);
assign addiu_op = (opcode == `OP_ADDIU);
assign ldhi_op = (opcode == `OP_LDHI);
assign bez_op = (opcode == `OP_BEZ);
assign bnz_op = (opcode == `OP_BNZ);
assign bpl_op = (opcode == `OP_BPL);
assign bmi_op = (opcode == `OP_BMI);
assign jmp_op = (opcode == `OP_JMP);
assign jal_op = (opcode == `OP_JAL);
pocoz.vの本体
assign {opcode, rd, rs, func} = idatain;
assign imm = idatain[`IMM_W-1:0];
decode decode_1 (.opcode(opcode), .func(func), .zero(zero), .mi(mi),
マルチプレクサの記述が
.alu_bsel(alu_bsel), .comsel(comsel), .rf_csel(rf_csel),
.pcsel(pcsel), .we(we), .rwe(rwe), .casel(casel), .pcjr(pcjr));
完全に図とマッチしている
assign alu_b = alu_bsel==2'b01 ? {{8{imm[7]}},imm} :
alu_bsel == 2'b10 ? {8'b0,imm} :
alu_bsel == 2'b11 ? {imm, 8'b0} : rf_b;
assign com = comsel==2'b01 ? `ALU_ADD:
comsel==2'b10 ? `ALU_THB: func[`SEL_W-1:0];
assign rf_c = rf_csel==2'b01 ? ddatain : rf_csel==2'b10 ? pc+1 : alu_y;
assign cadr = casel ? 3'b111 : rd;
alu alu_1(.a(rf_a), .b(alu_b), .s(com), .y(alu_y));
rfile rfile_1(.clk(clk), .a(rf_a), .aadr(rd), .b(rf_b), .badr(rs),
.c(rf_c), .cadr(cadr), .we(rwe));
pocoz.vの本体
assign zero = rf_a == 16'b0;
assign mi = rf_a[15];
assign pcadd = pcsel == 2'b01 ? {{8{imm[7]}},imm} :
pcsel == 2'b10 ? {{5{idatain[10]}},idatain[10:0]}: 0;
assign pcnext = pcjr ? rf_a: pc+pcadd+1;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) pc <= 0;
else
pc <= pcnext;
end
pc周辺も分離して書くこと
は可能だが見やすいとは
いえない
今回の記述の特徴
• 出力信号依存の書き方
• 全ての出力を分離して記述している
モジュールA
モジュールA・B
モジュールB
まとめて書ければ可読性が向上する
(かもしれない)
AとBに共通性があっても
分離して書かなければならない
function文
• うまく使うと非常に分かりやすく書ける
• しかし、落とし穴がある
– 変数スコープが曖昧
– 代入文は前後関係が生じる
– 出力をまとめる必要がある
• この授業では使わないが興味があればどう
ぞ
pocof.vを参照
function [7:0] decode(
input [`OPCODE_W-1:0] op,
内部変数をregで定義
input [`OPCODE_W-1:0] fu);
reg [1:0] alu_bsel, comsel, rf_csel;
reg rwe, casel;
begin
デフォルトの値を最初に書
alu_bsel = 2'b00;
comsel = 2'b00;
いてしまう
rf_csel = 2'b00;
rwe = 1'b0;
casel = 1'b0;
case (op)
`OP_REG:
if(fu==`F_LD) begin
rwe = 1'b1;
rf_csel = 2'b01; end
その後は後に書いたものが
else if(fu[4:3] == 2'b00)
前のを打ち消す
rwe = 1'b1;
…
pocof.vを参照
`OP_LDI: begin
rwe = 1'b1;
comsel = 2'b10;
alu_bsel = 2'b01; end
`OP_LDIU: begin
rwe = 1'b1;
comsel = 2'b10;
alu_bsel = 2'b10; end
`OP_ADDI: begin
rwe = 1'b1;
命令毎に特徴的な出力の
comsel = 2'b01;
動きをまとめて書ける
alu_bsel = 2'b01; end
`OP_ADDIU: begin
rwe = 1'b1;
comsel = 2'b01;
alu_bsel = 2'b10; end
`OP_LDHI: begin
rwe = 1'b1;
comsel = 2'b10;
alu_bsel = 2'b11; end
`OP_JAL: begin
rwe = 1'b1;
casel = 1'b1;
rf_csel = 2'b10; end
endcase
decode = {alu_bsel, comsel, rf_csel, rwe, casel};
end
最後に全体の信号をくっつ
endfunction
けて出力
pocof.vを参照
`OP_LDI: begin
rwe = 1'b1;
comsel = 2'b10;
alu_bsel = 2'b01; end
`OP_LDIU: begin
rwe = 1'b1;
comsel = 2'b10;
alu_bsel = 2'b10; end
`OP_ADDI: begin
rwe = 1'b1;
命令毎に特徴的な出力の
comsel = 2'b01;
動きをまとめて書ける
alu_bsel = 2'b01; end
`OP_ADDIU: begin
rwe = 1'b1;
comsel = 2'b01;
alu_bsel = 2'b10; end
`OP_LDHI: begin
rwe = 1'b1;
comsel = 2'b10;
alu_bsel = 2'b11; end
`OP_JAL: begin
rwe = 1'b1;
casel = 1'b1;
rf_csel = 2'b10; end
endcase
decode = {alu_bsel, comsel, rf_csel, rwe, casel};
end
最後に全体の信号をくっつ
endfunction
けて出力
pocof.vの本体
assign {alu_bsel, comsel, rf_csel, rwe, casel} =
decode(opcode,func);
外部の信号線に
関係付けを行う
• always文をレジスタ以外にも使う書き方でも
同様の多入力、多出力的な書き方が可能
• 一時、論理合成の効率が良かったため
FPGAベンダーが推奨したため、広がった
• しかし入門者は使わないほうが良い
ディスプレースメント付きレジスタ
間接指定
LDD rd,n(ra): rd ← (n+ra)
01110 ddd aaa nnnnn
STD rd,n(ra): (n+ra) ← rd
01111 ddd aaa nnnnn
• レジスタ間接指定の一種
• レジスタとn(ディスプレースメント)を加えた値が実効
アドレスになる
• ループアンローリングができるので、RISCではこの
方式が一般的
• POCOの今までのタイプと異なる
演習
LDDとSTDを実装せよ。
• eximem.datにテストプログラムがあるのでこ
れをimem.datにコピーして用いよ
• shapaは対応していないので注意!
• def.hには加えてある
• 変更箇所が多く構造とVerilog記述の関係を
良く理解しないとできない
• gtkwaveを使って論理的にバグを追い詰める
こと