3623 字
18 分钟
计组实验期末

Pasted image 20251116190550

测试指令共分11段(Test_all.coe,已载入初始工程rom中),其中,每段由实验讲义中的12条指令另加跳转测试指令12条,共24条。

由拨码开关sw_i选择起始地址进入,具体如下。

beqbnebltbgebltubgeujaljalrsllsrlsra
Sw_i
[5:2]
00000001001000110100010101100111100010011010

例如sw_i[5:2]=0101,则对应于bgeu测试指令段。

Sll,srl,sra均为r型指令,作为保底,不要与slli,srli,srai

// 起始地址选择参考样例
`timescale 1ns / 1ps
module PC(clk, rst, NPC, PCwr,PC,sw_i);
input clk;
input rst;
input [31:0] NPC;
input wire PCwr;
input [3:0] sw_i;
output reg [31:0] PC;
always @(posedge clk, negedge rst)
//added for final test 2024.12.19
//choose the test code init address
//PC initialization
if (!rst) begin
case (sw_i)
4'b0000:PC <= 32'h0000_0000; //beq
4'b0001:PC <= 32'h0000_0080; //bne
4'b0010:PC <= 32'h0000_0100; //blt
4'b0011:PC <= 32'h0000_0180; //bge
4'b0100:PC <= 32'h0000_0200; //bltu
4'b0101:PC <= 32'h0000_0280; //bgeu
4'b0110:PC <= 32'h0000_0300; //jal
4'b0111:PC <= 32'h0000_037c; //jalr
4'b1000:PC <= 32'h0000_03f0; //sll
4'b1001:PC <= 32'h0000_0410; //srl
4'b1010:PC <= 32'h0000_0430; //sra
endcase
// PC <= 32'h0000_0000;
end
else
if (PCwr==1'b0)//sw_i[1]= 1,
PC <= NPC;
endmodule

容易出错的地方#

  • PC,rom_addr的复位时机要同时(要么都异步复位(写在敏感列表),要么都同步复位(不写在敏感列表)),否则复位会出现部分复位(复位后: A->E->F->…)
  • Ctrl中指令解析错误,要严格检查解析Opcode,Funct是否正确;以及生成信号是否有遗漏
  • WD的赋值采取组合逻辑的呈现:always@(*)(reg) 还是 assign(wire)
    • 前者可能要使用到块内初始化,这引入了多重赋值和时序竞争的可能性
    • 后者直接定义了 WD 与其右侧信号之间的逻辑关系,没有内部的初始化或覆盖步骤,而且连续赋值消除了 always 块内多重赋值带来的所有时序竞争或仿真器对非阻塞赋值的特殊处理,从而确保在任何观察点,WD 都反映了 MUX 逻辑的正确输出
    • 但一般简单情形下都适用,这里也是
  • 一般主模块下首先默认是用wire,wire具有快的性质
  • 模块例化出口端口要用wire连接
  • 指令是否有效,观察alu很容易判断:如果运算结果不应该为0,但为0(因为设置了ALUOp的case:default:C为0),则说明Ctrl模块下该指令的解析出错或生成ALUOp信号遗漏了该指令

代码#

下面代码忽略U型指令:lui, auipc以及除了加法的其他运算(or,ori,slt等)

代码亮点#

  • alu module中把branch指令的各种标志用Zero统一起来
  • 使用 PC和NPC module独立处理PC

核心代码#

SCPU_TOP

`timescale 1ns / 1ps
// 37条指令
module SCPU_TOP(
input clk,
input rstn, // 低电平有效,按下后为0
input [15:0] sw_i,
output [7:0] disp_seg_o,
output [7:0] disp_an_o
);
reg [31:0] clkdiv;
wire Clk_CPU;
always @(posedge clk or negedge rstn) begin
if(!rstn) clkdiv <= 0;
else clkdiv <= clkdiv + 1'b1;
end
assign Clk_CPU = (sw_i[15])?clkdiv[27]:clkdiv[25]; // 2^27 和 2^25分频
reg [63:0] display_data;
reg [5:0] led_data_addr;
reg [63:0] led_disp_data;
parameter LED_DATA_NUM = 19;
reg [63:0] LED_DATA[18:0];
initial begin
LED_DATA[0] = 64'hC6F6F6F0C6F6F6F0;
LED_DATA[1] = 64'hF9F6F6CFF9F6F6CF;
LED_DATA[2] = 64'hFFC6F0FFFFC6F0FF;
LED_DATA[3] = 64'hFFC0FFFFFFC0FFFF;
LED_DATA[4] = 64'hFFA3FFFFFFA3FFFF;
LED_DATA[5] = 64'hFFFFA3FFFFFFA3FF;
LED_DATA[6] = 64'hFFFF9CFFFFFF9CFF;
LED_DATA[7] = 64'hFF9EBCFFFF9EBCFF;
LED_DATA[8] = 64'hFF9CFFFFFF9CFFFF;
LED_DATA[9] = 64'hFFC0FFFFFFC0FFFF;
LED_DATA[10] = 64'hFFA3FFFFFFA3FFFF;
LED_DATA[11] = 64'hFFA7B3FFFFA7B3FF;
LED_DATA[12] = 64'hFFC6F0FFFFC6F0FF;
LED_DATA[13] = 64'hF9F6F6CFF9F6F6CF;
LED_DATA[14] = 64'h9EBEBEBC9EBEBEBC;
LED_DATA[15] = 64'h2737373327373733;
LED_DATA[16] = 64'h505454EC505454EC;
LED_DATA[17] = 64'h744454F8744454F8;
LED_DATA[18] = 64'h0062080000620800;
end
always @(posedge Clk_CPU or negedge rstn) begin
if(!rstn) begin
led_data_addr <= 6'd0;
led_disp_data <= 64'b1;
end
else if(sw_i[0] == 1'b1) begin
if(led_data_addr == LED_DATA_NUM) begin
led_data_addr <= 6'd0;
led_disp_data <= LED_DATA[0];
end
else begin
led_disp_data <= LED_DATA[led_data_addr];
led_data_addr <= led_data_addr + 1'b1;
end
end
else led_data_addr <= led_data_addr;
end
wire [31:0] instr;
reg [31:0] reg_data;
reg [31:0] alu_disp_data;
reg [31:0] dmem_data;
// PC
wire [31:0] PC;
wire [31:0] NPC;
wire PCwr = ~sw_i[1];
wire [2:0] NPCOp;
// ROM
reg [5:0] rom_addr;
// RF
wire RegWrite;
wire [31:0] WD;
wire [31:0] RD1,RD2;
reg [5:0] reg_addr; // 显示用
// ALU
wire [31:0] A,B;
wire [4:0] ALUOp;
wire [31:0] aluout;
wire Zero;
reg [2:0] alu_addr;
// DM
wire MemWrite;
wire [5:0] dm_addr;
wire [31:0] dm_din;
wire [2:0] DMType;
wire [31:0] dm_dout;
reg [6:0] dmem_addr; // 显示用
// Ctrl
wire [5:0] EXTOp;
wire [1:0] WDSel;
wire ALUSrc;
// EXT
wire [31:0] immout;
always @(sw_i) begin
if(sw_i[0] == 0) begin
case(sw_i[14:11])
4'b1000 : display_data <= instr;
4'b0100 : display_data <= reg_data;
4'b0010 : display_data <= alu_disp_data;
4'b0001 : display_data <= dmem_data;
default : display_data <= instr;
endcase
end
else display_data = led_disp_data;
end
// 例化显示模块
seg7x16 u_seg7x16(
.clk(clk),
.rstn(rstn),
.disp_mode(sw_i[0]),
.i_data(display_data),
.disp_seg_o(disp_seg_o),
.disp_an_o(disp_an_o)
);
///////////////////////////////////////////////////////
// 例化PC_Unit module 时序逻辑
PC_Unit U_PC(.clk(Clk_CPU),.rst(~rstn),.NPC(NPC),.PCwr(PCwr),.PC(PC));
// 例化NPC_Unit module 组合逻辑
NPC_Unit U_NPC(.PC(PC),.NPCOp(NPCOp),.IMM(immout),.aluout(aluout),.NPC(NPC));
// 例化ROM模块
dist_mem_im U_IM(
.a(rom_addr),
.spo(instr)
);
// 每个CLK_CPU获得一个新指令
// 关注上升沿:上升沿PC更新为NPC,所以rom_addr也应更新为NPC以取下条指令
always @(posedge Clk_CPU or negedge rstn) begin
if(!rstn) rom_addr <= 6'b0;
else if(sw_i[1] == 1'b0) rom_addr <= NPC >> 2; // rom以1为单位,指令以4为单位,所以除以4以映射rom
else rom_addr <= rom_addr;
end
//Decode
wire [6:0] Op = instr[6:0];
wire [6:0] Funct7 = instr[31:25];
wire [2:0] Funct3 = instr[14:12];
wire [4:0] rs1 = instr[19:15];
wire [4:0] rs2 = instr[24:20];
wire [4:0] rd = instr[11:7];
wire [4:0] iimm_shamt= instr[24:20];
wire [11:0] iimm = instr[31:20]; // jalr, load, itype
wire [11:0] simm = {instr[31:25],instr[11:7]};
wire [11:0] bimm = {instr[31],instr[7],instr[30:25],instr[11:8]};
wire [19:0] uimm = instr[31:12]; // lui, auipc
wire [19:0] jimm = {instr[31],instr[19:12],instr[20],instr[30:21]}; // jal
// 例化ctrl模块 组合逻辑
ctrl u_ctrl(
.Op(Op),
.Funct7(Funct7),
.Funct3(Funct3),
.Zero(Zero),
.RegWrite(RegWrite),
.MemWrite(MemWrite),
.EXTOp(EXTOp),
.ALUOp(ALUOp),
.ALUSrc(ALUSrc),
.NPCOp(NPCOp),
.DMType(DMType),
.WDSel(WDSel)
);
// 例化EXT模块 组合逻辑
EXT U_EXT(
.iimm_shamt(iimm_shamt),
.iimm(iimm),
.simm(simm),
.bimm(bimm),
.uimm(uimm),
.jimm(jimm),
.EXTOp(EXTOp),
.immout(immout)
);
// 赋值
// ALU_DM->RF RF_WD
// WDSel FromALU 2'b00
// WDSel FromMEM 2'b01
// WDSel FromPC 2'b10
//always @(*) begin
// WD <= 32'h0;
// case(WDSel)
// 00: WD <= aluout;
// 01: WD <= dm_dout;
// 10: WD <= PC + 4;
// default: WD <= 32'h0;
// endcase
//end
// WDSel FromALU 2'b00
// WDSel FromMEM 2'b01
// WDSel FromPC 2'b10
assign WD = (WDSel == 2'b10) ? (PC + 4) :
(WDSel == 2'b01) ? dm_dout :
(WDSel == 2'b00) ? aluout : 32'h00000000;
// RF->ALU ALU_A_B
assign A = RD1;
assign B = (ALUSrc == 1'b0)?RD2:immout;
// ALU->DM DM_addr_din
assign dm_addr = aluout;
assign dm_din = RD2;
// 例化RF模块 时序逻辑
RF U_RF(
.clk(Clk_CPU),
.rstn(rstn),
.RFWr(RegWrite),
.sw_i(sw_i),
.A1(rs1),
.A2(rs2),
.A3(rd),
.WD(WD),
.RD1(RD1),
.RD2(RD2)
);
// 循环显示32个寄存器的内容
always @(posedge Clk_CPU or negedge rstn) begin
if(!rstn) begin
reg_addr <= 5'b0;
reg_data <= 32'b0;
end
else if(sw_i[13] == 1'b1)begin
reg_addr <= reg_addr + 1'b1;
reg_data <= U_RF.rf[reg_addr];
end
end
/////////////////////////////////////////////////////
// 例化alu模块 组合逻辑
alu U_alu(
.A(A),
.B(B),
.ALUOp(ALUOp),
.C(aluout),
.Zero(Zero)
);
// 循环显示A B Zero四个值
always @(posedge Clk_CPU or negedge rstn) begin
if(!rstn) alu_addr <= 3'b0;
else alu_addr <= alu_addr + 1'b1;
case(alu_addr) // 下面用普通reg接收signed数据,只会数据位复制
3'b001:alu_disp_data <= U_alu.A;
3'b010:alu_disp_data <= U_alu.B;
3'b011:alu_disp_data <= U_alu.C;
3'b100:alu_disp_data <= U_alu.Zero;
default:alu_disp_data <= 32'hFFFFFFFF;
endcase
end
//////////////////////////////////////////////////////
// 例化dm模块 时序逻辑
dm U_DM(
.clk(Clk_CPU),
.DMWr(MemWrite),
.addr(dm_addr),
.din(dm_din),
.DMType(DMType),
.dout(dm_dout)
);
// 循环显示Data Memory内容
parameter DM_DATA_NUM = 16;
always @(posedge Clk_CPU or negedge rstn) begin
if(!rstn) begin
dmem_addr <= 7'b0;
dmem_data <= 32'hFFFFFFFF;
end
else if(sw_i[11] == 1'b1) begin
dmem_addr <= dmem_addr + 1'b1;
dmem_data <= U_DM.dmem[dmem_addr][7:0];
if(dmem_addr == DM_DATA_NUM) begin // DM_DATA_NUM是不能取到的,所以此时马上切为0
dmem_addr <= 7'd0;
dmem_data <= 32'hFFFFFFFF;
end
end
end
endmodule

PC

module PC_Unit(
input clk,
input rst,
input [31:0] NPC,
input PCwr,
output reg [31:0] PC
);
// 由于主模块下各rstn复位都是异步复位,所以关于rstn的同时集体复位,这里应该采取异步复位,所以敏感列表里应有rst
// 否则只有在posedge clk下rst为1才复位,则为同步复位
always @(posedge clk or posedge rst) begin
if(rst) PC <= 32'b0;
else if(PCwr) PC <= NPC;
end
endmodule

NPC

`define NPC_PLUS4 3'b000
`define NPC_BRANCH 3'b001
`define NPC_JUMP 3'b010
`define NPC_JALR 3'b100
module NPC_Unit(
input [31:0] PC,
input [2:0] NPCOp,
input [31:0] IMM,
input [31:0] aluout,
output reg [31:0] NPC
);
always @(*) begin
case(NPCOp)
`NPC_PLUS4: NPC = PC + 4; // default
`NPC_BRANCH: NPC = PC + IMM; // branch
`NPC_JUMP: NPC = PC + IMM; // jal
`NPC_JALR: NPC = aluout; // jalr
default: NPC = PC + 4;
endcase
end
endmodule

ctrl

module ctrl(
input [6:0] Op,
input [6:0] Funct7,
input [2:0] Funct3,
input Zero,
output RegWrite,
output MemWrite,
output [5:0] EXTOp,
output [4:0] ALUOp,
output [2:0] NPCOp,
output ALUSrc,
output [2:0] DMType,
output [1:0] WDSel // MemtoReg
);
// R_type 10条
wire rtype = ~Op[6] & Op[5] & Op[4] & ~Op[3] & ~Op[2] & Op[1] & Op[0]; //0110011
wire i_add = rtype & ~Funct3[2] & ~Funct3[1] & ~Funct3[0] & ~Funct7[5]; // add 0000000 000
wire i_sub = rtype & ~Funct3[2] & ~Funct3[1] & ~Funct3[0] & Funct7[5]; // sub 0100000 000
wire i_sll = rtype & ~Funct3[2] & ~Funct3[1] & Funct3[0]; // sll 0000000 001
wire i_slt = rtype & ~Funct3[2] & Funct3[1] & ~Funct3[0]; // slt 0000000 010
wire i_sltu = rtype & ~Funct3[2] & Funct3[1] & Funct3[0]; // sltu 0000000 011
wire i_xor = rtype & Funct3[2] & ~Funct3[1] & ~Funct3[0]; // xor 0000000 100
wire i_srl = rtype & Funct3[2] & ~Funct3[1] & Funct3[0] & ~Funct7[5]; // srl 0000000 101
wire i_sra = rtype & Funct3[2] & ~Funct3[1] & Funct3[0] & Funct7[5]; // sra 0100000 101
wire i_or = rtype & Funct3[2] & Funct3[1] & ~Funct3[0]; // or 0000000 110
wire i_and = rtype & Funct3[2] & Funct3[1] & Funct3[0]; // and 0000000 111
// i_l type load 5条
wire itype_l = ~Op[6] & ~Op[5] & ~Op[4] & ~Op[3] & ~Op[2] & Op[1] & Op[0]; //0000011
wire i_lb = itype_l & ~Funct3[2] & ~Funct3[1] & ~Funct3[0]; //lb 000
wire i_lh = itype_l & ~Funct3[2] & ~Funct3[1] & Funct3[0]; //lh 001
wire i_lw = itype_l & ~Funct3[2] & Funct3[1] & ~Funct3[0]; //lw 010
wire i_lbu = itype_l & Funct3[2] & ~Funct3[1] & ~Funct3[0]; //lbu 100
wire i_lhu = itype_l & Funct3[2] & ~Funct3[1] & Funct3[0]; //lh 101
// i_r type 涉及Reg与imm运算,ALU with immediate 10条
wire itype_r = ~Op[6] & ~Op[5] & Op[4] & ~Op[3] & ~Op[2] & Op[1] & Op[0]; //0010011
wire i_addi = itype_r & ~Funct3[2] & ~Funct3[1] & ~Funct3[0]; // addi 000 func3
wire i_slti = itype_r & ~Funct3[2] & Funct3[1] & ~Funct3[0]; // slti 010 func3
wire i_sltiu = itype_r & ~Funct3[2] & Funct3[1] & Funct3[0]; // sltiu 011 func3
wire i_slli = itype_r & ~Funct3[2] & ~Funct3[1] & Funct3[0]; // slli 001
wire i_srli = itype_r & Funct3[2] & ~Funct3[1] & Funct3[0] & ~Funct7[5]; // srli 101 0000000
wire i_srai = itype_r & Funct3[2] & ~Funct3[1] & Funct3[0] & Funct7[5]; // srai 101 0100000
wire itype_shamt = i_slli | i_srli | i_srai;
// jalr
wire i_jalr = Op[6] & Op[5] & ~Op[4] & ~Op[3] & Op[2] & Op[1] & Op[0]; //1100111
// s format store 3条
wire stype = ~Op[6] & Op[5] & ~Op[4] & ~Op[3] & ~Op[2] & Op[1] & Op[0];//0100011
wire i_sb = stype & ~Funct3[2] & ~Funct3[1] & ~Funct3[0]; // sb 000
wire i_sh = stype & ~Funct3[2] & ~Funct3[1] & Funct3[0]; // sh 001
wire i_sw = stype & ~Funct3[2] & Funct3[1] & ~Funct3[0]; // sw 010
// B_type 6条
wire btype = Op[6] & Op[5] & ~Op[4] & ~Op[3] & ~Op[2] & Op[1] & Op[0]; //1100011
wire i_beq = btype & ~Funct3[2] & ~Funct3[1] & ~Funct3[0]; // beq 000
wire i_bne = btype & ~Funct3[2] & ~Funct3[1] & Funct3[0]; // bne 001
wire i_blt = btype & Funct3[2] & ~Funct3[1] & ~Funct3[0]; // blt 100
wire i_bge = btype & Funct3[2] & ~Funct3[1] & Funct3[0]; // bge 101
wire i_bltu = btype & Funct3[2] & Funct3[1] & ~Funct3[0]; // bltu 110
wire i_bgeu = btype & Funct3[2] & Funct3[1] & Funct3[0]; // bgeu 111
// J_type 1条
wire jtype = Op[6] & Op[5] & ~Op[4] & Op[3] & Op[2] & Op[1] & Op[0]; //1101111
wire i_jal = jtype; // jal
// U_type 2条
wire utype = ~Op[6] & Op[4] & ~Op[3] & Op[2] & Op[1] & Op[0]; //0X10111
wire i_lui = utype & Op[5]; // lui 0110111
wire i_auipc = utype & ~Op[5]; // auipc 0010111
assign RegWrite = rtype | itype_r | itype_l | i_jal | i_jalr; // register write (含跳转返回地址)
assign MemWrite = stype; // memory write
//`define EXT_CTRL_ITYPE_SHAMT 6'b100000
//`define EXT_CTRL_ITYPE 6'b010000
//`define EXT_CTRL_STYPE 6'b001000
//`define EXT_CTRL_BTYPE 6'b000100
//`define EXT_CTRL_UTYPE 6'b000010
//`define EXT_CTRL_JTYPE 6'b000001
assign EXTOp[5] = itype_shamt;
assign EXTOp[4] = itype_l | (itype_r & ~itype_shamt) | i_jalr;
assign EXTOp[3] = stype;
assign EXTOp[2] = btype;
assign EXTOp[1] = i_lui | i_auipc;
assign EXTOp[0] = i_jal;
//`define ALUOp_nop 5'b00000
//`define ALUOp_sll 5'b00001
//`define ALUOp_srl 5'b00010
//`define ALUOp_sra 5'b00101
//`define ALUOp_add 5'b00011
//`define ALUOp_beq 5'b00100
//`define ALUOp_bne 5'b01000
//`define ALUOp_blt 5'b01100
//`define ALUOp_bge 5'b10000
//`define ALUOp_bltu 5'b10100
//`define ALUOp_bgeu 5'b11000
assign ALUOp[0] = i_add | i_addi | stype | itype_l | i_jalr | i_sll | i_sra | i_slli | i_srai;
assign ALUOp[1] = i_add | i_addi | stype | itype_l | i_jalr | i_srl | i_srli;
assign ALUOp[2] = i_beq | i_blt | i_bltu | i_sra | i_srai;
assign ALUOp[3] = i_bne | i_blt | i_bgeu;
assign ALUOp[4] = i_bge | i_bltu | i_bgeu;
//`define NPC_PLUS4 3'b000
//`define NPC_BRANCH 3'b001
//`define NPC_JUMP 3'b010
//`define NPC_JALR 3'b100
assign NPCOp[0] = btype & Zero;
assign NPCOp[1] = i_jal;
assign NPCOp[2] = i_jalr;
assign ALUSrc = itype_r | itype_l | stype | i_jal | i_jalr; // ALU B is from instruction immediate
// ALU_DM->RF RF_WD
// WDSel FromALU 2'b00
// WDSel FromMEM 2'b01
// WDSel FromPC 2'b10
assign WDSel[0] = itype_l;
assign WDSel[1] = i_jal | i_jalr;
// dm_word 3'b000
// dm_halfword 3'b001
// dm_halfword_unsigned 3'b010
// dm_byte 3'b011
// dm_byte_unsigned 3'b100
assign DMType[2] = i_lbu; // bu
assign DMType[1] = i_lb | i_sb | i_lhu; // hu和b
assign DMType[0] = i_lh | i_sh | i_lb | i_sb; // h和b
endmodule

alu

`define ALUOp_nop 5'b00000
`define ALUOp_sll 5'b00001
`define ALUOp_srl 5'b00010
`define ALUOp_sra 5'b00101
`define ALUOp_add 5'b00011
`define ALUOp_beq 5'b00100
`define ALUOp_bne 5'b01000
`define ALUOp_blt 5'b01100
`define ALUOp_bge 5'b10000
`define ALUOp_bltu 5'b10100
`define ALUOp_bgeu 5'b11000
module alu(
input signed [31:0] A,B,
input [4:0] ALUOp,
output reg signed [31:0] C,
output reg Zero
);
always @(*) begin
C = 32'b0;
case(ALUOp)
`ALUOp_sll: C = A << B[4:0];
`ALUOp_srl: C = A >> B[4:0];
`ALUOp_sra: C = A >>> B[4:0];
`ALUOp_add: C = A + B;
`ALUOp_beq: C = {31'b0,(A!=B)};
`ALUOp_bne: C = {31'b0,{A==B}};
`ALUOp_blt: C = {31'b0,(A>=B)};
`ALUOp_bge: C = {31'b0,(A<B)};
`ALUOp_bltu: C = {31'b0,($unsigned(A)>=$unsigned(B))};
`ALUOp_bgeu: C = {31'b0,{$unsigned(A)<$unsigned(B)}};
default:C = 32'b0;
endcase
Zero = (C == 0)?1:0; // 阻塞赋值,需等C算出来,而不是同步
end
endmodule

EXT

// 立即数扩展
`define EXT_CTRL_ITYPE_SHAMT 6'b100000
`define EXT_CTRL_ITYPE 6'b010000
`define EXT_CTRL_STYPE 6'b001000
`define EXT_CTRL_BTYPE 6'b000100
`define EXT_CTRL_UTYPE 6'b000010
`define EXT_CTRL_JTYPE 6'b000001
module EXT(
input [4:0] iimm_shamt,
input [11:0] iimm,
input [11:0] simm,
input [11:0] bimm,
input [19:0] uimm,
input [19:0] jimm,
input [5:0] EXTOp,
output reg [31:0] immout
);
always @(*) begin
case (EXTOp)
`EXT_CTRL_ITYPE_SHAMT: immout <= {27'b0,iimm_shamt[4:0]}; // I-type 立即数 (用于 slli/srli/srai)
`EXT_CTRL_ITYPE: immout <= {{20{iimm[11]}}, iimm[11:0]}; // I-type 立即数 (用于 addi, lw, jalr 等)
`EXT_CTRL_STYPE: immout <= {{20{simm[11]}}, simm[11:0]}; // S-type 立即数 (用于 sw 等)
`EXT_CTRL_BTYPE: immout<= {{19{bimm[11]}},bimm[11:0],1'b0}; // B-type 立即数 (用于 beq 等)
`EXT_CTRL_UTYPE: immout <= {uimm[19:0], 12'b0}; // U-type 立即数 (用于 lui, auipc)
`EXT_CTRL_JTYPE: immout<= {{11{jimm[19]}},jimm[19:0],1'b0}; // J-type 立即数 (用于 jal)
default: immout <= 32'b0;
endcase
end
endmodule

次要代码#

seg7x16

module seg7x16(
input clk,
input rstn,
input disp_mode,
input [63:0] i_data,
output [7:0] disp_seg_o,
output [7:0] disp_an_o
);
// 2^15分频
reg [14:0] cnt;
wire seg7_clk;
always @(posedge clk or negedge rstn) begin
if(!rstn) cnt <= 0;
else cnt <= cnt + 1'b1;
end
assign seg7_clk = cnt[14];
// 8选1
reg [2:0] seg7_addr;
always @(posedge seg7_clk or negedge rstn) begin
if(!rstn) seg7_addr <= 0;
else seg7_addr <= seg7_addr + 1'b1;
end
// 选中的数码管使能信号
reg [7:0] o_sel_r;
always @(*) begin
case(seg7_addr)
7 : o_sel_r = 8'b01111111;
6 : o_sel_r = 8'b10111111;
5 : o_sel_r = 8'b11011111;
4 : o_sel_r = 8'b11101111;
3 : o_sel_r = 8'b11110111;
2 : o_sel_r = 8'b11111011;
1 : o_sel_r = 8'b11111101;
0 : o_sel_r = 8'b11111110;
endcase
end
//
reg [63:0] i_data_store;
always @(posedge clk or negedge rstn) begin
if(!rstn) i_data_store <= 0;
else i_data_store <= i_data;
end
reg [7:0] seg_data_r;
always @(*) begin
if(disp_mode == 1'b0) begin // 字符显示模式
case(seg7_addr)
0 : seg_data_r = i_data_store[3:0];
1 : seg_data_r = i_data_store[7:4];
2 : seg_data_r = i_data_store[11:8];
3 : seg_data_r = i_data_store[15:12];
4 : seg_data_r = i_data_store[19:16];
5 : seg_data_r = i_data_store[23:20];
6 : seg_data_r = i_data_store[27:24];
7 : seg_data_r = i_data_store[31:28];
default: seg_data_r = 8'hFF; // 默认全空,防止latch
endcase
end
else begin // 图形显示模式
case(seg7_addr)
0 : seg_data_r = i_data_store[7:0];
1 : seg_data_r = i_data_store[15:8];
2 : seg_data_r = i_data_store[23:16];
3 : seg_data_r = i_data_store[31:24];
4 : seg_data_r = i_data_store[39:32];
5 : seg_data_r = i_data_store[47:40];
6 : seg_data_r = i_data_store[55:48];
7 : seg_data_r = i_data_store[63:56];
default: seg_data_r = 8'hFF; // 默认全空,防止latch
endcase
end
end
reg [7:0] o_seg_r;
always @(posedge clk or negedge rstn) begin
if(!rstn) o_seg_r <= 8'hFF;
else if(disp_mode == 1'b0) begin // 字符模式
case(seg_data_r)
4'h0 : o_seg_r <= 8'hC0;
4'h1 : o_seg_r <= 8'hF9;
4'h2 : o_seg_r <= 8'hA4;
4'h3 : o_seg_r <= 8'hB0;
4'h4 : o_seg_r <= 8'h99;
4'h5 : o_seg_r <= 8'h92;
4'h6 : o_seg_r <= 8'h82;
4'h7 : o_seg_r <= 8'hF8;
4'h8 : o_seg_r <= 8'h80;
4'h9 : o_seg_r <= 8'h90;
4'hA : o_seg_r <= 8'h88;
4'hB : o_seg_r <= 8'h83;
4'hC : o_seg_r <= 8'hC6;
4'hD : o_seg_r <= 8'hA1;
4'hE : o_seg_r <= 8'h86;
4'hF : o_seg_r <= 8'h8E;
default : o_seg_r <= 8'hFF;
endcase
end
else o_seg_r <= seg_data_r;
end
assign disp_an_o = o_sel_r;
assign disp_seg_o = o_seg_r;
endmodule

RF

module RF(
input clk,
input rstn,
input RFWr,
input [15:0] sw_i,
input [4:0] A1,A2,A3,
input [31:0] WD,
output [31:0] RD1,RD2
);
reg [31:0] rf[31:0]; // reg [31:0]为数据类型,rf[31:0]为数组,32位宽,32个寄存器
integer i;
always @(posedge clk or negedge rstn) begin
if(!rstn) begin
for(i=0;i<32;i=i+1) rf[i] <= i;
end
else if(RFWr && (!sw_i[1]) && (A3 != 5'd0)) rf[A3] <= WD; // 正常模式下且RegWrite有效,写rd
end
// 读rs1和rs2
assign RD1 = (A1 != 0)?rf[A1]:0;
assign RD2 = (A2 != 0)?rf[A2]:0;
endmodule

dm

`define dm_word 3'b000
`define dm_halfword 3'b001
`define dm_halfword_unsigned 3'b010
`define dm_byte 3'b011
`define dm_byte_unsigned 3'b100
module dm(
input clk,
input DMWr,
input [5:0] addr,
input [31:0] din,
input [2:0] DMType,
output reg [31:0] dout
);
reg [7:0] dmem[6:0]; // 8位宽数据,7个单元,reg [7:0]为数据类型,dmem[6:0]为数组
integer i;
initial begin
for(i=0;i<7;i=i+1) dmem[i] = 8'h00;
end
// 写操作
always @(posedge clk) begin
if(DMWr) begin
case(DMType)
`dm_byte:dmem[addr] <= din[7:0];
`dm_halfword:begin
dmem[addr] <= din[7:0];
dmem[addr+1] <= din[15:8];
end
`dm_word:begin
dmem[addr] <= din[7:0];
dmem[addr+1] <= din[15:8];
dmem[addr+2] <= din[23:16];
dmem[addr+3] <= din[31:24];
end
endcase
end
end
// 读操作
always @(*) begin
case(DMType)
`dm_byte:dout = {{24{dmem[addr][7]}},dmem[addr][7:0]};
`dm_byte_unsigned:dout = {24'b0,dmem[addr][7:0]};
`dm_halfword:dout = {{16{dmem[addr+1][7]}},dmem[addr+1][7:0],dmem[addr][7:0]};
`dm_halfword_unsigned:dout = {16'b0,dmem[addr+1][7:0],dmem[addr][7:0]};
`dm_word:dout = {dmem[addr+3][7:0],dmem[addr+2][7:0],dmem[addr+1][7:0],dmem[addr][7:0]};
default:dout = {dmem[addr+3][7:0],dmem[addr+2][7:0],dmem[addr+1][7:0],dmem[addr][7:0]};
endcase
end
endmodule
计组实验期末
https://fuwari.vercel.app/posts/计组实验期末/
作者
Echo_Kang
发布于
2025-12-23
许可协议
CC BY-NC-SA 4.0