初めに
著者はCPUの自作が初めてなので嘘を書いているかもしれません.RISC-Vに用意されている命令をすべて実装したわけではありません.
また,実装したすべての命令について動作確認を行ったわけではないため,動作は保証しません.
もし間違いがありましたら指摘してくださると幸いです.
ハードとソフト
- ハード
- PYNQ-Z2
- ソフト
- Vivado v2022.2
visc vとは
ざっっっっくり言うとCPUの一種です.
OSSです.
ISAが何種類かあって,公開されています.
rv32iとは
RISC-VのISAのひとつ.もっとも単純なものです.
機械語は32ビットです.
全体の構成
回路構成をクラス図っぽく書くとこんな感じです.
rom
にプログラムが保存され,pc
(プログラムカウンタ)に応じた機械語を出力します.
decorder
は機械語を翻訳してオペコードやイミディエイトデータなどを取り出します.
alu
では受け取ったオペコードやイミディエイトデータなどから処理内容を判断して,自身が持つレジスタから値を読み取ったり書き込んだりします.
コード
ブロックデザインとファイル構成は以下のようになっています.
なおar
はデバッグ用にpc
を出力しているだけです.
System Verilog Header
データ型とかインターフェースとかいろいろ定義.
ちなみにインターフェースは名前空間内に定義できないそうです.私はこれ知らなくてしばらくハマりました.
register.svh
レジスタとプログラムカウンタに関するデータ型や定数の定義をしてます.
RISC-Vではレジスタが32個用意されますので,レジスタのアドレスは5ビットになります.
また,符号付き変数の大小を比較するために最上位ビットに1を足した結果を返す関数を定義しています.
`ifndef REGISTER_SVH
`define REGISTER_SVH
package register;
// レジスタのアドレス
typedef logic[4:0] addr_t;
// レジスタのデータ型
typedef logic[31:0] register_t;
// プログラムカウンタのデータ型
typedef logic[31:0] pc_t;
// 初期化する値
localparam register_t REGISTER_ZERO = 32'b0;
// プログラムカウンタのリセット値
localparam pc_t PC_ZERO = 32'b0;
// 一度にインクリメントされるプログラムカウンタのサイズ
localparam pc_t PC_RUG = 32'd4;
// 符号付き比較のため,最上位ビットに1を足す
function register_t remove_sign(
input register_t in
);
remove_sign = 32'h8000_0000 + in;
endfunction
endpackage
`endif
led.svh
Lチカに関する定義.
Lチカのために使用する変数値を保存するレジスタのアドレスを定義しています.
`ifndef LED_SVH
`define LED_SVH
`include "register.svh"
// Lチカのための定義
package led;
// 出力するLEDの値を保存するアドレス
localparam register::addr_t LED_ADDR = 5'h1f;
// Lチカの周期を保存するアドレス
localparam register::addr_t CYCLE_ADDR = 5'h01;
// 現在のカウントを保存するアドレス
localparam register::addr_t CNT_ADDR = 5'h02;
endpackage
`endif
decorde.svh
オペコードやイミディエイトデータなどのデータ型や,取り出したデータを伝達するためのインターフェースを定義しています.
また,符号付きイミディエイトデータを作成するために2の補数を取得する関数を定義しています.
RISC-Vにはfunct3
,funct7
というデータがあり,命令によってはオペコードとこれらの値で動作が決定します.なおRISC-VにはR type
など命令がタイプ分けされていますが,回路を作成するうえでは重要な知識ではないのでcommand_type_enum
とかいらなかったかも知れないです.あった方がソースは分かりやすくなると思いますが.
`ifndef DECORDE_SVH
`define DECORDE_SVH
`include "register.svh"
package decorde;
// 機械語のタイプ
typedef logic[2:0] command_type_t;
typedef logic[6:0] opcode_t;
typedef logic[2:0] funct3_t;
typedef logic[6:0] funct7_t;
// 機械語のタイプの列挙
typedef enum command_type_t {
NONE,
R_TYPE,
I_TYPE,
S_TYPE,
B_TYPE,
U_TYPE,
J_TYPE
} command_type_enum;
// オペコードから機械語のタイプを返す
function command_type_t get_command_type(
input logic[6:0] opcode
);
unique case (opcode)
7'b0110011: get_command_type = R_TYPE;
7'b1100111: get_command_type = I_TYPE;
7'b0000011: get_command_type = I_TYPE;
7'b0010011: get_command_type = I_TYPE;
7'b0100011: get_command_type = S_TYPE;
7'b1100011: get_command_type = B_TYPE;
7'b0110111: get_command_type = U_TYPE;
7'b0010111: get_command_type = U_TYPE;
7'b1101111: get_command_type = J_TYPE;
default: get_command_type = NONE;
endcase
endfunction
// 2の補数を取得
function register::register_t get_complement(
input register::register_t in
);
get_complement = ~in + 1;
endfunction
endpackage
// 実行形式コマンドのインターフェース
interface command_if;
decorde::opcode_t opcode;
register::addr_t rs1;
register::addr_t rs2;
register::addr_t rd;
decorde::funct3_t funct3;
decorde::funct7_t funct7;
register::register_t imm_raw;
register::register_t imm_signed;
decorde::command_type_t command_type;
// 機械語をデコードして送る側
modport master(
output opcode,
output rs1, output rs2, output rd,
output funct3, output funct7,
output imm_raw, output imm_signed,
output command_type
);
// デコードされた機械語を受け取る側
modport slave(
input opcode,
input rs1, input rs2, input rd,
input funct3, input funct7,
input imm_raw, input imm_signed,
input command_type
);
endinterface
`endif
machine.svh
レジスタのアドレスなどから生の機械語を作成する関数.
rom
モジュールで使用します.
実際に使用したのはこの中のいくつかだけですがたくさんの命令について関数を作りましたので結構長いです.
`ifndef MACHINE_SVH
`define MACHINE_SVH
`include "decorde.svh"
`include "register.svh"
// 命令と変数から機械語を生成する
package machine;
// 生の機械語
typedef logic[31:0] machine_t;
// R type
// rs1とrs2で指定したアドレスから値を二つ取り出し,演算結果をrdで指定したアドレスに格納する
function machine_t ADD(
input register::addr_t rs1,
input register::addr_t rs2,
input register::addr_t rd
);
ADD = {7'b000_0000, rs2, rs1, 3'b000, rd, 7'b0110011};
endfunction
function machine_t SUB(
input register::addr_t rs1,
input register::addr_t rs2,
input register::addr_t rd
);
SUB = {7'b010_0000, rs2, rs1, 3'b000, rd, 7'b0110011};
endfunction
function machine_t SLL(
input register::addr_t rs1,
input register::addr_t rs2,
input register::addr_t rd
);
SLL = {7'b000_0000, rs2, rs1, 3'b001, rd, 7'b0110011};
endfunction
function machine_t SLT(
input register::addr_t rs1,
input register::addr_t rs2,
input register::addr_t rd
);
SLT = {7'b000_0000, rs2, rs1, 3'b010, rd, 7'b0110011};
endfunction
function machine_t SLTU(
input register::addr_t rs1,
input register::addr_t rs2,
input register::addr_t rd
);
SLTU = {7'b000_0000, rs2, rs1, 3'b011, rd, 7'b0110011};
endfunction
function machine_t XOR(
input register::addr_t rs1,
input register::addr_t rs2,
input register::addr_t rd
);
XOR = {7'b000_0000, rs2, rs1, 3'b100, rd, 7'b0110011};
endfunction
function machine_t SRL(
input register::addr_t rs1,
input register::addr_t rs2,
input register::addr_t rd
);
SRL = {7'b000_0000, rs2, rs1, 3'b101, rd, 7'b0110011};
endfunction
function machine_t SRA(
input register::addr_t rs1,
input register::addr_t rs2,
input register::addr_t rd
);
SRA = {7'b010_0000, rs2, rs1, 3'b101, rd, 7'b0110011};
endfunction
function machine_t OR(
input register::addr_t rs1,
input register::addr_t rs2,
input register::addr_t rd
);
OR = {7'b000_0000, rs2, rs1, 3'b110, rd, 7'b0110011};
endfunction
function machine_t AND(
input register::addr_t rs1,
input register::addr_t rs2,
input register::addr_t rd
);
AND = {7'b000_0000, rs2, rs1, 3'b111, rd, 7'b0110011};
endfunction
// I type
// rs1で指定したアドレスの値とイミディエイトデータからの演算結果をrdで指定したアドレスに格納する
function machine_t JALR(
input register::addr_t rs1,
input register::addr_t rd,
input register::register_t imm
);
JALR = {imm[11:0], rs1, 3'b000, rd, 7'b1100111};
endfunction
function machine_t LB(
input register::addr_t rs1,
input register::addr_t rd,
input register::register_t imm
);
LB = {imm[11:0], rs1, 3'b000, rd, 7'b0000011};
endfunction
function machine_t LH(
input register::addr_t rs1,
input register::addr_t rd,
input register::register_t imm
);
LH = {imm[11:0], rs1, 3'b001, rd, 7'b0000011};
endfunction
function machine_t LW(
input register::addr_t rs1,
input register::addr_t rd,
input register::register_t imm
);
LW = {imm[11:0], rs1, 3'b010, rd, 7'b0000011};
endfunction
function machine_t LBU(
input register::addr_t rs1,
input register::addr_t rd,
input register::register_t imm
);
LBU = {imm[11:0], rs1, 3'b100, rd, 7'b0000011};
endfunction
function machine_t LHU(
input register::addr_t rs1,
input register::addr_t rd,
input register::register_t imm
);
LHU = {imm[11:0], rs1, 3'b101, rd, 7'b0000011};
endfunction
function machine_t ADDI(
input register::addr_t rs1,
input register::addr_t rd,
input register::register_t imm
);
ADDI = {imm[11:0], rs1, 3'b000, rd, 7'b0010011};
endfunction
function machine_t SLTI(
input register::addr_t rs1,
input register::addr_t rd,
input register::register_t imm
);
SLTI = {imm[11:0], rs1, 3'b010, rd, 7'b0010011};
endfunction
function machine_t SLTIU(
input register::addr_t rs1,
input register::addr_t rd,
input register::register_t imm
);
SLTIU = {imm[11:0], rs1, 3'b011, rd, 7'b0010011};
endfunction
function machine_t XORI(
input register::addr_t rs1,
input register::addr_t rd,
input register::register_t imm
);
XORI = {imm[11:0], rs1, 3'b100, rd, 7'b0010011};
endfunction
function machine_t ORI(
input register::addr_t rs1,
input register::addr_t rd,
input register::register_t imm
);
ORI = {imm[11:0], rs1, 3'b110, rd, 7'b0010011};
endfunction
function machine_t ANDI(
input register::addr_t rs1,
input register::addr_t rd,
input register::register_t imm
);
ANDI = {imm[11:0], rs1, 3'b111, rd, 7'b0010011};
endfunction
// S type
// 使ってない.メモリに関する命令.
function machine_t SB(
input register::addr_t rs1,
input register::addr_t rs2,
input register::register_t imm
);
SB = {imm[11:5], rs2, rs1, 3'b000, imm[4:0], 7'b0100011};
endfunction
function machine_t SH(
input register::addr_t rs1,
input register::addr_t rs2,
input register::register_t imm
);
SH = {imm[11:5], rs2, rs1, 3'b001, imm[4:0], 7'b0100011};
endfunction
function machine_t SW(
input register::addr_t rs1,
input register::addr_t rs2,
input register::register_t imm
);
SW = {imm[11:5], rs2, rs1, 3'b010, imm[4:0], 7'b0100011};
endfunction
// B type
// rs1とrs2の値を色んな手段で比較して,その結果で次のプログラムカウンタが決まる
function machine_t BEQ(
input register::addr_t rs1,
input register::addr_t rs2,
input register::register_t imm
);
BEQ = {imm[12], imm[10:5], rs2, rs1, 3'b000, imm[4:1], imm[11], 7'b1100011};
endfunction
function machine_t BNE(
input register::addr_t rs1,
input register::addr_t rs2,
input register::register_t imm
);
BNE = {imm[12], imm[10:5], rs2, rs1, 3'b001, imm[4:1], imm[11], 7'b1100011};
endfunction
function machine_t BLT(
input register::addr_t rs1,
input register::addr_t rs2,
input register::register_t imm
);
BLT = {imm[12], imm[10:5], rs2, rs1, 3'b100, imm[4:1], imm[11], 7'b1100011};
endfunction
function machine_t BGE(
input register::addr_t rs1,
input register::addr_t rs2,
input register::register_t imm
);
BGE = {imm[12], imm[10:5], rs2, rs1, 3'b101, imm[4:1], imm[11], 7'b1100011};
endfunction
function machine_t BLTU(
input register::addr_t rs1,
input register::addr_t rs2,
input register::register_t imm
);
BLTU = {imm[12], imm[10:5], rs2, rs1, 3'b110, imm[4:1], imm[11], 7'b1100011};
endfunction
function machine_t BGEU(
input register::addr_t rs1,
input register::addr_t rs2,
input register::register_t imm
);
BGEU = {imm[12], imm[10:5], rs2, rs1, 3'b111, imm[4:1], imm[11], 7'b1100011};
endfunction
// U type
// rdに,immで指定した値を代入する.
function machine_t LUI(
input register::addr_t rd,
input register::register_t imm
);
LUI = {imm[31:12], rd, 7'b0110111};
endfunction
function machine_t AUIPC(
input register::addr_t rd,
input register::register_t imm
);
AUIPC = {imm[31:12], rd, 7'b0010111};
endfunction
// J type
// ジャンプ命令.
function machine_t JAL(
input register::addr_t rd,
input register::register_t imm
);
JAL = {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'b1101111};
endfunction
endpackage
`endif
System Verilog
CPUの動作となる部分.
cpu_sv.sv
デコーダーとaluを繋げてます.
`include "decorde.svh"
`include "register.svh"
`include "machine.svh"
module cpu_sv(
input clk,
input resetn,
output register::pc_t pc,
input machine::machine_t machine,
output [3:0] led
);
// インターフェースの定義
command_if command();
// モジュールの宣言
decorder decorder (
.clk(clk), .resetn(resetn),
.machine(machine), .command(command)
);
alu alu (
.clk(clk), .resetn(resetn),
.command(command),
.pc(pc), .led(led)
);
endmodule
decorder.sv
生の機械語からオペコードやイミディエイトデータなどを取り出します.
`include "decorde.svh"
`include "machine.svh"
module decorder(
input clk,
input resetn,
input machine::machine_t machine,
command_if.master command
);
always_comb begin
command.opcode = machine[6:0];
command.command_type = decorde::get_command_type(machine[6:0]);
unique case (decorde::get_command_type(machine[6:0]))
decorde::R_TYPE: begin
command.rs1 = machine[19:15];
command.rs2 = machine[24:20];
command.rd = machine[11:7];
command.funct3 = machine[14:12];
command.funct7 = machine[31:25];
command.imm_raw = 32'b0;
command.imm_signed = 32'b0;
end
decorde::I_TYPE: begin
command.rs1 = machine[19:15];
command.rs2 = 5'b0;
command.rd = machine[11:7];
command.funct3 = machine[14:12];
command.funct7 = 7'b0;
command.imm_raw = {20'b0, machine[31:20]};
if (machine[31]) begin
command.imm_signed = {20'hfffff, machine[31:20]};
end else begin
command.imm_signed = {20'b0, machine[31:20]};
end
end
decorde::S_TYPE: begin
command.rs1 = machine[19:15];
command.rs2 = machine[24:20];
command.rd = 5'b0;
command.funct3 = machine[14:12];
command.funct7 = 7'b0;
command.imm_raw = {20'b0, machine[31:25], machine[11:7]};
if (machine[31]) begin
command.imm_signed = {20'hfffff, machine[31:25], machine[11:7]};
end else begin
command.imm_signed = {20'b0, machine[31:25], machine[11:7]};
end
end
decorde::B_TYPE: begin
command.rs1 = machine[19:15];
command.rs2 = machine[24:20];
command.rd = 5'b0;
command.funct3 = machine[14:12];
command.funct7 = 7'b0;
command.imm_raw = {19'b0, machine[31], machine[7], machine[30:25], machine[11:8], 1'b0};
if (machine[31]) begin
command.imm_signed = {19'h7ffff, machine[31], machine[7], machine[30:25], machine[11:8], 1'b0};
end else begin
command.imm_signed = {19'b0, machine[31], machine[7], machine[30:25], machine[11:8], 1'b0};
end
end
decorde::U_TYPE: begin
command.rs1 = 5'b0;
command.rs2 = 5'b0;
command.rd = machine[11:7];
command.funct3 = 3'b0;
command.funct7 = 7'b0;
command.imm_raw = {machine[31:12], 12'b0};
command.imm_signed = {machine[31:12], 12'b0};
end
decorde::J_TYPE: begin
command.rs1 = 5'b0;
command.rs2 = 5'b0;
command.rd = machine[11:7];
command.funct3 = 3'b0;
command.funct7 = 7'b0;
command.imm_raw = {11'b0, machine[31], machine[19:12], machine[20], machine[30:21], 1'b0};
if (machine[31]) begin
command.imm_signed = {11'h7ff, machine[31], machine[19:12], machine[20], machine[30:21], 1'b0};
end else begin
command.imm_signed = {11'b0, machine[31], machine[19:12], machine[20], machine[30:21], 1'b0};
end
end
default: begin
command.rs1 = 5'b0;
command.rs2 = 5'b0;
command.rd = 5'b0;
command.funct3 = 3'b0;
command.funct7 = 7'b0;
command.imm_raw = 32'b0;
command.imm_signed = 32'b0;
end
endcase
end
endmodule
alu.sv
decorder.svから受け取ったデータで指定された命令を実行します.
なお,rではアドレス0の値は0固定なので,こちらでもrd
が0ならレジスタには0を入れるようにしています.
`include "decorde.svh"
`include "led.svh"
module alu(
input clk,
input resetn,
command_if.slave command,
output register::pc_t pc,
output [3:0] led
);
// 内部レジスタ
register::register_t [31:0] x = {
register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,
register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,
register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,
register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,
register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,register::REGISTER_ZERO,
register::REGISTER_ZERO,register::REGISTER_ZERO
};
always_ff @(posedge clk) begin
if (!resetn) begin
pc <= register::PC_ZERO;
for (logic [5:0] i = 0; i < 32; i++) begin
x[i] <= register::REGISTER_ZERO;
end
end else begin
unique case (command.command_type)
decorde::R_TYPE: begin
pc <= pc + register::PC_RUG;
unique case (command.opcode)
7'b0110011: begin
unique case (command.funct3)
3'b000: begin
// ADD
if (command.funct7 == 7'b0) begin
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : x[command.rs1] + x[command.rs2];
end
// SUB
else if (command.funct7 == 7'b0100000) begin
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : x[command.rs1] - x[command.rs2];
end
// fail
else begin
;
end
end
3'b001: begin
// SLL
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : x[command.rs1] << x[command.rs2];
end
3'b010: begin
// SLT
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : register::remove_sign(x[command.rs1]) < register::remove_sign(x[command.rs2]) ? 32'b1 : 32'b0;
end
3'b011: begin
// SLTU
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : x[command.rs1] < x[command.rs2] ? 32'b1 : 32'b0;
end
3'b100: begin
// XOR
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : x[command.rs1] ^ x[command.rs2];
end
3'b101: begin
// SRL
if (command.funct7 == 7'b0) begin
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : x[command.rs1] >> x[command.rs2];
end
// SRA
else if (command.funct7 == 7'b0100000) begin
if (x[command.rs1][31] && command.rd != 5'b0) begin
for (logic [5:0] i = 0; i < 31; i++) begin
if (i < x[command.rs2]) begin
x[command.rd][31 - i - 1] <= 1'b1;
end else begin
x[command.rd][31 - i - 1] <= x[command.rs1][31 - i - x[command.rs2] + 1];
end
end
x[command.rd][31] <= 1'b1;
end else begin
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : x[command.rs1] >> x[command.rs2];
end
end
// fail
else begin
;
end
end
3'b110: begin
// OR
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : x[command.rs1] | x[command.rs2];
end
3'b111: begin
// AND
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : x[command.rs1] & x[command.rs2];
end
default: begin
// fail
;
end
endcase
end
default: begin
// fail
;
end
endcase
end
decorde::I_TYPE: begin
unique case (command.opcode)
7'b1100111: begin
// JALR
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : register::REGISTER_ZERO;
pc <= x[command.rs1] + command.imm_signed;
end
7'b0000011: begin
pc <= pc + register::PC_RUG;
unique case (command.funct3)
3'b000: begin
// LB
if (command.rd != 5'b0) begin
x[command.rd][7:0] <= x[command.rs1][7:0];
for (logic [5:0] i = 31; i > 7; i--) begin
x[command.rd][i] <= x[command.rs1][7];
end
end else begin
x[command.rd] <= register::REGISTER_ZERO;
end
end
3'b001: begin
// LH
if (command.rd != 5'b0) begin
x[command.rd][15:0] <= x[command.rs1][15:0];
for (logic [5:0] i = 31; i > 15; i--) begin
x[command.rd][i] <= x[command.rs1][15];
end
end else begin
x[command.rd] <= register::REGISTER_ZERO;
end
end
3'b010: begin
// LW
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : x[command.rs1];
end
3'b100: begin
// LBU
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : {24'b0, x[command.rs1][7:0]};
end
3'b101: begin
// LHU
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : {16'b0, x[command.rs1][15:0]};
end
default: begin
// fail
;
end
endcase
end
7'b0010011: begin
pc <= pc + register::PC_RUG;
unique case (command.funct3)
3'b000: begin
// ADDI
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : x[command.rs1] + command.imm_signed;
end
3'b010: begin
// SLTI
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : register::remove_sign(x[command.rs1]) < register::remove_sign(command.imm_signed) ? 32'b1 : 32'b0;
end
3'b011: begin
// SLTIU
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : x[command.rs1] < command.imm_signed ? 32'b1 : 32'b0;
end
3'b100: begin
// XORI
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : x[command.rs1] ^ command.imm_signed;
end
3'b110: begin
// ORI
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : x[command.rs1] | command.imm_signed;
end
3'b111: begin
// ANDI
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : x[command.rs1] & command.imm_signed;
end
default: begin
// fail
;
end
endcase
end
7'b1110011: begin
// ?
;
end
default: begin
// fail
;
end
endcase
end
decorde::S_TYPE: begin
// メモリに関する命令なのでオミット
;
end
decorde::B_TYPE: begin
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : register::REGISTER_ZERO;
unique case (command.opcode)
7'b1100011: begin
unique case(command.funct3)
3'b000: begin
// BEQ
if (x[command.rs1] == x[command.rs2]) begin
pc <= pc + command.imm_signed;
end else begin
pc <= pc + register::PC_RUG;
end
end
3'b001: begin
// BNE
if (x[command.rs1] != x[command.rs2]) begin
pc <= pc + command.imm_signed;
end else begin
pc <= pc + register::PC_RUG;
end
end
3'b100: begin
// BLT
if (register::remove_sign(x[command.rs1]) < register::remove_sign(x[command.rs2])) begin
pc <= pc + command.imm_signed;
end else begin
pc <= pc + register::PC_RUG;
end
end
3'b101: begin
// BGE
if (register::remove_sign(x[command.rs1]) >= register::remove_sign(x[command.rs2])) begin
pc <= pc + command.imm_signed;
end else begin
pc <= pc + register::PC_RUG;
end
end
3'b110: begin
// BLTU
if (x[command.rs1] < x[command.rs2]) begin
pc <= pc + command.imm_signed;
end else begin
pc <= pc + register::PC_RUG;
end
end
3'b111: begin
// BGEU
if (x[command.rs1] >= x[command.rs2]) begin
pc <= pc + command.imm_signed;
end else begin
pc <= pc + register::PC_RUG;
end
end
default: begin
// fail
;
end
endcase
end
default: begin
// fail
;
end
endcase
end
decorde::U_TYPE: begin
unique case (command.opcode)
7'b0110111: begin
// LUI
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : command.imm_raw;
pc <= pc + register::PC_RUG;
end
7'b0010111: begin
// AUIPC
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : command.imm_signed + pc;
pc <= pc + register::PC_RUG;
end
default: begin
// fail
;
end
endcase
end
decorde::J_TYPE: begin
unique case (command.opcode)
7'b1101111: begin
// JAL
x[command.rd] <= command.rd == 5'b0 ? register::REGISTER_ZERO : pc + register::PC_RUG;
pc <= command.imm_signed + pc;
end
default: begin
// fail
;
end
endcase
end
default: begin
// fail
;
end
endcase
end
end
assign led = x[led::LED_ADDR][3:0];
endmodule
rom_sv.sv
プログラムを格納したファイルです.
プログラムカウンタのインクリメントは4ずつ行われるので,2ビットずらしています.
`include "register.svh"
`include "decorde.svh"
`include "machine.svh"
`include "led.svh"
module rom_sv(
input clk,
input resetn,
output machine::machine_t machine,
input register::pc_t pc
);
// Lチカの周期
localparam integer CYCLE = 50_000_000;
// 命令の数
localparam integer COMMAND_NUM = 7;
machine::machine_t machines[0:COMMAND_NUM - 1] = {
// 各変数の初期化
machine::ORI(5'b0, led::LED_ADDR, 32'h5), // LEDの初期値は0101
machine::LUI(led::CYCLE_ADDR, CYCLE), // 周期は上で定義したもの(下位12ビットは捨てられる)
machine::ORI(5'b0, led::CNT_ADDR, 32'b0), // カウンタの初期値
// カウントアップ
machine::ADDI(led::CNT_ADDR, led::CNT_ADDR, 32'h2), // 2カウント(下の比較で1クロック使うため)
machine::BGEU(led::CYCLE_ADDR, led::CNT_ADDR, decorde::get_complement(register::PC_RUG)), // 周期とカウントを比較し,ひとつ前の命令に戻るか一つ進むか決める
// 点灯状態反転
machine::XORI(led::LED_ADDR, led::LED_ADDR, 32'hf), // NOT命令がないので1とXORさせる
// カウントを元に戻す
machine::JAL(5'b0, decorde::get_complement(register::PC_RUG * 4)) // 4つ戻って,カウンタに0を代入する
};
always_comb begin
if (pc >= {COMMAND_NUM, 2'b0}) begin
machine = register::REGISTER_ZERO;
end else begin
machine = machines[{2'b0, pc[31:2]}];
end
end
endmodule
Verilog HDL
RTLモジュールをブロックデザインに追加するためのラッパーなので大したコードはないです.
cpu.v
module cpu(
input clk,
input resetn,
output [31:0] pc,
input [31:0] machine,
output [3:0] led
);
cpu_sv cpu (
.clk(clk), .resetn(resetn),
.pc(pc), .machine(machine), .led(led)
);
endmodule
rom.v
module rom(
input clk,
input resetn,
input [31:0] pc,
output [31:0] machine
);
rom_sv rom (
.clk(clk), .resetn(resetn),
.pc(pc), .machine(machine)
);
endmodule
実行結果
ちなみにタイミングバイオレーションが出ますが一応動作します.