近跟着老师学习Verilog,做了中科大的练习题,将答案记录一下
Q106 控制器
题目描述
控制器模块负责产生各种控制信号。
与寄存器堆模块相关的:
1.rf_wr_en:1位使能信号,连接到寄存器堆的写使能端口(WE)。
查RISC-V手册可得,需要写寄存器的指令有U型指令,J型指令,I型指令,R型指令。
2.rf_wr_sel:2位选择信号,控制寄存器堆写数据的选择。其与指令的关系如下:
与pc模块相关的:
do_jump:一位控制信号,当指令为jalr或者jal时为1。当指令为jal或者jalr或者B型指令且满足分支条件时,pc模块的JUMP信号为1。
与分支跳转模块相关的:
BrType:三位,指明分支指令类型。由于之前我们为了提高代码容错率,没有直接使用B型指令的功能码作为控制信号,而是设置BrType为0时不进行操作,所以信号与指令对应如下:
与ALU模块相关:
1.alu_a_sel:ALU模块SrcA端口的选择信号;
2.alu_b_sel:ALU模块SrcB端口的选择信号;
通过查RISC-V中文手册可得:
指令为R型或I型或S型时,ALU模块输入SrcA端口连接寄存器输出端口RD1,即ALU模块其中一个操作数为寄存器rs1中的值;
指令为R型时,ALU模块输入SrcB端口连接寄存器输出端口RD2,即ALU模块另一个操作数为寄存器rs2中的值;
(ps:并不是说指令里有rs2就代表alu_b_sel为0,如B型指令里有rs2,但比较过程是在branch模块进行的,不是经过ALU模块)
3.alu_ctrl:四位,控制ALU模块运算类型的信号,具体如下:
(ps:load型指令即lb,lh,lw,lbu,lhu)
与存储器模块相关:
1.dm_rd_ctrl:三位,指明数据存储器读方式。具体如下:
2.dm_wr_ctrl:两位,指明数据存储器写方式。具体如下:
输入格式
32位的指令inst
输出格式
1.位宽为1bit的rf_wr_en:寄存器堆的写使能信号 2.位宽为2bit的rf_wr_sel:输入寄存器堆的数据的选择信号 3.位宽为1bit的do_jump:无条件跳转信号 4.位宽为3bit的BrType:分支指令类型的控制信号 5.位宽为1bit的alu_a_sel:ALU模块SrcA端口的选择信号 6.位宽为1bit的alu_b_sel:ALU模块SrcB端口的选择信号 7.位宽为4bit的alu_ctrl:ALU模块运算类型的控制信号 8.位宽为3bit的dm_rd_ctrl:存储器读类型的控制信号 9.位宽为2bit的dm_wr_ctrl:存储器写类型的控制信号
代码
module top_module(
input [31:0] inst,
output rf_wr_en,
output reg [1:0] rf_wr_sel,
output do_jump,
output reg [2:0] BrType,
output alu_a_sel,
output alu_b_sel,
output reg [3:0] alu_ctrl,
output reg [2:0] dm_rd_ctrl,
output reg [1:0] dm_wr_ctrl
);
wire [6:0] opcode;
wire [2:0] funct3;
wire [6:0] funct7;
wire is_lui;
wire is_auipc;
wire is_jal;
wire is_jalr;
wire is_beq;
wire is_bne;
wire is_blt;
wire is_bge;
wire is_bltu;
wire is_bgeu;
wire is_lb;
wire is_lh;
wire is_lw;
wire is_lbu;
wire is_lhu;
wire is_sb;
wire is_sh;
wire is_sw;
wire is_addi;
wire is_slti;
wire is_sltiu;
wire is_xori;
wire is_ori;
wire is_andi;
wire is_slli;
wire is_srli;
wire is_srai;
wire is_add;
wire is_sub;
wire is_sll;
wire is_slt;
wire is_sltu;
wire is_xor;
wire is_srl;
wire is_sra;
wire is_or;
wire is_and;
wire is_add_type;
wire is_u_type;
wire is_jump_type;
wire is_b_type;
wire is_r_type;
wire is_i_type;
wire is_s_type;
assign opcode = inst[6:0];
assign funct7 = inst[31:25];
assign funct3 = inst[14:12];
assign is_lui = (opcode == 7'h37) ;
assign is_auipc= (opcode == 7'h17) ;
assign is_jal = (opcode == 7'h6F) ;
assign is_jalr = (opcode == 7'h67) && (funct3 ==3'h0) ;
assign is_beq = (opcode == 7'h63) && (funct3 ==3'h0) ;
assign is_bne = (opcode == 7'h63) && (funct3 ==3'h1) ;
assign is_blt = (opcode == 7'h63) && (funct3 ==3'h4) ;
assign is_bge = (opcode == 7'h63) && (funct3 ==3'h5) ;
assign is_bltu = (opcode == 7'h63) && (funct3 ==3'h6) ;
assign is_bgeu = (opcode == 7'h63) && (funct3 ==3'h7) ;
assign is_lb = (opcode == 7'h03) && (funct3 ==3'h0) ;
assign is_lh = (opcode == 7'h03) && (funct3 ==3'h1) ;
assign is_lw = (opcode == 7'h03) && (funct3 ==3'h2) ;
assign is_lbu = (opcode == 7'h03) && (funct3 ==3'h4) ;
assign is_lhu = (opcode == 7'h03) && (funct3 ==3'h5) ;
assign is_sb = (opcode == 7'h23) && (funct3 ==3'h0) ;
assign is_sh = (opcode == 7'h23) && (funct3 ==3'h1) ;
assign is_sw = (opcode == 7'h23) && (funct3 ==3'h2) ;
assign is_addi = (opcode == 7'h13) && (funct3 ==3'h0) ;
assign is_slti = (opcode == 7'h13) && (funct3 ==3'h2) ;
assign is_sltiu= (opcode == 7'h13) && (funct3 ==3'h3) ;
assign is_xori = (opcode == 7'h13) && (funct3 ==3'h4) ;
assign is_ori = (opcode == 7'h13) && (funct3 ==3'h6) ;
assign is_andi = (opcode == 7'h13) && (funct3 ==3'h7) ;
assign is_slli = (opcode == 7'h13) && (funct3 ==3'h1) && (funct7 == 7'h00);
assign is_srli = (opcode == 7'h13) && (funct3 ==3'h5) && (funct7 == 7'h00);
assign is_srai = (opcode == 7'h13) && (funct3 ==3'h5) && (funct7 == 7'h20);
assign is_add = (opcode == 7'h33) && (funct3 ==3'h0) && (funct7 == 7'h00);
assign is_sub = (opcode == 7'h33) && (funct3 ==3'h0) && (funct7 == 7'h20);
assign is_sll = (opcode == 7'h33) && (funct3 ==3'h1) && (funct7 == 7'h00);
assign is_slt = (opcode == 7'h33) && (funct3 ==3'h2) && (funct7 == 7'h00);
assign is_sltu = (opcode == 7'h33) && (funct3 ==3'h3) && (funct7 == 7'h00);
assign is_xor = (opcode == 7'h33) && (funct3 ==3'h4) && (funct7 == 7'h00);
assign is_srl = (opcode == 7'h33) && (funct3 ==3'h5) && (funct7 == 7'h00);
assign is_sra = (opcode == 7'h33) && (funct3 ==3'h5) && (funct7 == 7'h20);
assign is_or = (opcode == 7'h33) && (funct3 ==3'h6) && (funct7 == 7'h00);
assign is_and = (opcode == 7'h33) && (funct3 ==3'h7) && (funct7 == 7'h00);
assign is_add_type = is_auipc | is_jal | is_jalr | is_b_type | is_s_type
| is_lb | is_lh | is_lw | is_lbu | is_lhu | is_add | is_addi ;
assign is_u_type = is_lui | is_auipc ;
assign is_jump_type= is_jal ;
assign is_b_type = is_beq | is_bne | is_blt | is_bge | is_bltu | is_bgeu ;
assign is_r_type = is_add | is_sub | is_sll | is_slt | is_sltu | is_xor
| is_srl | is_sra | is_or | is_and ;
assign is_i_type = is_jalr | is_lb | is_lh | is_lw | is_lbu | is_lhu
| is_addi | is_slti | is_sltiu | is_xori | is_ori | is_andi
| is_slli | is_srli | is_srai ;
assign is_s_type = is_sb | is_sh | is_sw ;
//rf_wr_en
assign rf_wr_en = is_u_type|is_jump_type|is_i_type|is_r_type; /*待填*/
//[1:0]rf_wr_sel
always@(*)
begin
/*待填*/
if(is_jalr|is_jal)
rf_wr_sel=2'b01;
else if(is_addi | is_slti | is_sltiu | is_xori | is_ori | is_andi
| is_slli | is_srli | is_srai|is_r_type|is_u_type)
rf_wr_sel=2'b10;
else if(is_lb | is_lh | is_lw | is_lbu | is_lhu)
rf_wr_sel=2'b11;
else
rf_wr_sel=2'b00;
end
//do_jump
assign do_jump = is_jump_type|is_jalr|is_jal;/*待填*/
//[2:0]BrType
always@(*)
begin
case({is_beq, is_bne, is_blt, is_bge, is_bltu, is_bgeu})
6'b100000: BrType=3'b010;
6'b010000: BrType=3'b011;
6'b001000: BrType=3'b100;
6'b000100: BrType=3'b101;
6'b000010: BrType=3'b110;
6'b000001: BrType=3'b111;
6'b000000: BrType=3'b000;
endcase
end
//alu_a_sel
assign alu_a_sel = is_s_type|is_i_type|is_r_type;/*待填*/
//alu_b_sel
assign alu_b_sel = ~is_r_type ;/*待填题干要求是R型实际波形是反的*/
//alu_ctrl
always@(*)
begin
/*待填*/
case({(is_auipc|is_jal|is_jalr|is_b_type|is_s_type|is_lb|is_lh|is_lw|is_lbu|is_lhu|is_add|is_addi),is_sub,(is_sll|is_slli),(is_srl|is_srli),(is_sra|is_srai),(is_slt|is_slti),(is_sltu|is_sltiu),(is_xor|is_xori),(is_or|is_ori),(is_and|is_andi),is_lui})
11'b10000000000: alu_ctrl=4'b0000;
11'b01000000000: alu_ctrl=4'b1000;
11'b00100000000: alu_ctrl=4'b0001;
11'b00010000000: alu_ctrl=4'b0101;
11'b00001000000: alu_ctrl=4'b1101;
11'b00000100000: alu_ctrl=4'b0010;
11'b00000010000: alu_ctrl=4'b0011;
11'b00000001000: alu_ctrl=4'b0100;
11'b00000000100: alu_ctrl=4'b0110;
11'b00000000010: alu_ctrl=4'b0111;
11'b00000000001: alu_ctrl=4'b1110;
endcase
end
//[2:0]dm_rd_ctrl
always@(*)
begin
/*待填*/
case({is_lb,is_lbu,is_lh,is_lhu,is_lw})
5'b10000: dm_rd_ctrl=3'b001;
5'b01000: dm_rd_ctrl=3'b010;
5'b00100: dm_rd_ctrl=3'b011;
5'b00010: dm_rd_ctrl=3'b100;
5'b00001: dm_rd_ctrl=3'b101;
default:dm_rd_ctrl=3'b000;
endcase
end
//[1:0]dm_wr_ctrl
always@(*)
begin
/*待填*/
case({is_sb,is_sh,is_sw})
3'b100: dm_wr_ctrl=2'b01;
3'b010: dm_wr_ctrl=2'b10;
3'b001: dm_wr_ctrl=2'b11;
default:dm_wr_ctrl=2'b00;
endcase
end
endmodule
Q107 RISC-V单周期CPU
题目描述
我们先后完成了寄存器堆模块,程序计数器模块,立即数扩展模块,分支跳转模块,ALU模块,存储器模块,控制器模块的verilog代码,在这个题目中,我们只需要在顶层模块调用子模块(定义子模块的文件已经存在于OJ系统的后台中了),就可以实现单周期的CPU了。
输入格式
1.时钟周期clk 2.复位键rst
输出格式
代码
module top_module(
input clk,
input rst
);
wire clk;
wire rst;
wire [31:0] inst;
wire [1:0] rf_wr_sel;
reg [31:0] rf_wd;
wire rf_wr_en;
wire [31:0] rf_rd1,rf_rd2;
wire [31:0] pc;
wire [31:0] pc_plus4;
wire do_jump;
wire JUMP;
wire [31:0] imm_out;
wire [2:0] comp_ctrl;
wire BrE;
wire alu_a_sel;
wire alu_b_sel;
wire [31:0] alu_a,alu_b,alu_out;
wire [3:0] alu_ctrl;
wire [2:0] dm_rd_ctrl;
wire [1:0] dm_wr_ctrl;
wire [31:0] dm_dout;
always@(*)
begin
case(rf_wr_sel)
2'b00: rf_wd = 32'h0;
2'b01: rf_wd = pc_plus4;
2'b10: rf_wd = alu_out;
2'b11: rf_wd = dm_dout;
default:rf_wd = 32'h0;
endcase
end
assign pc_plus4 = pc + 32'h4;
assign JUMP = BrE || do_jump;
assign alu_a = alu_a_sel ? rf_rd1 : pc ;
assign alu_b = alu_b_sel ? imm_out : rf_rd2 ;
reg_file reg_file0(
.clk (clk),
.A1 (inst[19:15]),//rs1
.A2 (inst[24:20]),//rs2
.A3 (inst[11:7]),//rd
.WD (rf_wd),
.WE (rf_wr_en),//1位使能信号,连接到寄存器堆的写使能端口(WE)
.RD1 (rf_rd1),
.RD2 (rf_rd2)
);
pc pc0(
.clk (clk),
.rst (rst),
.JUMP (JUMP),
.JUMP_PC (alu_out),//应该有问题
.pc (pc)
);
imm imm0(
.inst (inst),
.out (imm_out)
);
branch branch0(
.REG1 (rf_rd1),//寄存器的输出
.REG2 (rf_rd2),//寄存器的输出
.Type (comp_ctrl),//接控制器的输出
.BrE (BrE)
);
alu alu0(
.SrcA (alu_a),
.SrcB (alu_b),
.func (alu_ctrl),//运算控制
.ALUout (alu_out)
);
mem mem0(
.clk (clk),
.im_addr (pc),//输入应该是PC
.im_dout (inst),//输出指令
.dm_rd_ctrl (dm_rd_ctrl),
.dm_wr_ctrl (dm_wr_ctrl),
.dm_addr (alu_out),//一般接ALU的结果
.dm_din (rf_rd2),//一般接寄存器
.dm_dout (dm_dout)//给寄存器输入选择器
);
ctrl ctrl0(
.inst (inst),
.rf_wr_en (rf_wr_en),
.rf_wr_sel (rf_wr_sel),
.do_jump (do_jump),
.BrType (comp_ctrl),//输出给分支控制
.alu_a_sel (alu_a_sel),
.alu_b_sel (alu_b_sel),
.alu_ctrl (alu_ctrl),
.dm_rd_ctrl (dm_rd_ctrl),
.dm_wr_ctrl (dm_wr_ctrl)
);
endmodule