近跟著老師學習Verilog,做了中科大的練習題,將答案記錄一下
Q62-99
題在哪兒
Q100 寄存器堆模塊
題目描述
在RV32I中,寄存器堆指32個通用寄存器的集合,具有專門的讀寫端口,可并發(fā)訪問不同寄存器。
我們用5位數代表寄存器的端口號,需要注意的是:當待寫入寄存器端口號為0時,往x0寫入的數據總是被丟棄,因為x0寄存器恒為0,不能對x0寄存器的值進行修改。設置x0寄存器,既可以提供常量0(比如RISC-V用sub rd, x0, rs來實現neg取負數指令),也可以提供一個可以丟棄結果的場所(比如RISC-V使用addi x0, x0, 0實現nop空指令)。
當A1有意義時,其對應指令中的rs1,即第15到19位;同理,當有意義時,A2對應指令中的rs2,即第20到24位;A3對應指令中的rd,即第7到11位。
輸入格式
1.時鐘信號clk 2.位寬為5的待讀取寄存器端口號A1 3.位寬為5的待讀取寄存器端口號A2 4.位寬為5的待寫入寄存器端口號A3 5.位寬為32的待寫入數據WD 6.位寬為1的寄存器寫使能信號WE
輸出格式文章來源:http://www.zghlxwxcb.cn/news/detail-780395.html
1.位寬為32的從A1對應的寄存器中讀出的數據RD1 2.位寬為32的從A2對應的寄存器中讀出的數據RD2
代碼
module top_module(
input clk,
input [4:0] A1,A2,A3,
input [31:0] WD,
input WE,
output [31:0] RD1,RD2
);
reg [31:0] reg_file[0:31];
//初始化寄存器堆
integer i;
initial
begin
for(i=0;i<32;i=i+1) reg_file[i] = 0;
end
//寫入寄存器
always@(posedge clk)
begin
/*待填*/
if(WE&A3!=0) //A3==0的情況排除?X0一直為0
reg_file[A3]= WD;
else ;
end
//讀取寄存器
assign RD1 = reg_file[A1]/*待填*/;
assign RD2 = reg_file[A2]/*待填*/;
endmodule
Q101 程序計數器模塊
題目描述
當執(zhí)行一條指令時,首先需要根據PC中存放的指令地址,將指令由內存取到指令寄存器中,此過程稱為取指。
?
PC全稱ProgramCounter(程序計數器),它是計算機處理器中的寄存器,包含當前正在執(zhí)行的指令的地址。
一般情況下,當指令被獲取,程序計數器存儲的地址加四(一條指令有32位,為4字節(jié))。當遇到跳轉指令(jal,jalr)或者滿足分支跳轉指令(所有B類指令)的跳轉條件時,則將程序計數器的內容設置為轉移指令規(guī)定的地址。
輸入格式
1.時鐘信號clk 2.復位鍵rst,當rst為1時將PC置零 3.位寬為1的跳轉信號JUMP,當JUMP為1時將PC設為JUMP_PC 4.位寬為32的待跳轉的地址JUMP_PC
輸出格式
位寬為32的當前周期需要執(zhí)行指令的地址pc
代碼
module top_module(
input clk,
input rst,
input JUMP,
input [31:0] JUMP_PC,
output reg [31:0] pc);
wire [31:0] pc_plus4;
assign pc_plus4 = pc + 32'h4;
//計算PC
always@(posedge clk or posedge rst)
begin
/*待填*/
if(rst)
pc<=0;
else if(JUMP)
pc<=JUMP_PC;
else
pc<=pc_plus4;
end
endmodule
Q102 立即數擴展模塊
題目描述
通過上圖可知,不同類型的指令,立即數的位置不同,我們需要從原始的指令中提取出順序正確的立即數,并依照指令的類型對立即數做相應擴展。
通過了解各指令的具體作用和指令格式,我們能得到下表:
注:符號位擴展就是對不足32位的數,在高位擴展符號位至32位的操作(符號位是1則填1,符號位為0則填0)
輸入格式
位寬為32的指令inst
輸出格式
經過擴展的32位立即數out
代碼
module top_module(
input [31:0] inst,
output reg [31:0] out
);
wire [6:0] opcode;
assign opcode= inst[6:0];
//立即數擴展
always@(*)
begin
case(opcode)
/*待填*/
7'b0010111:out<={inst[31:12],12'b0};
7'b0110111:out<={inst[31:12],12'b0};
7'b1100011:out<={{20{inst[31]}},inst[7],inst[30:25],inst[11:8],1'b0};
7'b1101111:out<={{12{inst[31]}},inst[19:12],inst[20],inst[30:21],1'b0};
7'b1100111:out<={{12{inst[31]}},inst[19:12],inst[20],inst[30:21],1'b0};
7'b0000011:out<={{20{inst[31]}},inst[31:20]};
7'b0100011:out<={{20{inst[31]}},inst[31:25],inst[11:7]};
7'b0010011:out<={{20{inst[31]}},inst[31:20]};
default:out<=32'h0;
endcase
end
endmodule
Q103 分支判斷模塊
題目描述
RV32I基礎指令集中提供了6條B型指令,分支判斷模塊負責判斷跳轉條件是否滿足,需要分支跳轉時產生分支信號。
如上圖,Type是代表不同比較類型的3位編碼,其與輸入輸出的關系如下表:
注:Type并不等價于B類指令中的funct3(功能碼),只是控制器處理指令后輸出的一個信號。因為beq的funct3為000,而我們在編寫代碼時,把0作為默認值;當信號為0時,我們不做操作,這樣可以有利于增強代碼的容錯率。
輸入格式
1.位寬為32的(從寄存器中讀取的)待比較數REG1 2.位寬為32的(從寄存器中讀取的)待比較數REG2 3.位寬為3的Type,用于區(qū)分兩個待比較數比較的類型
輸出格式
位寬為1的分支跳轉信號BrE
代碼
module top_module(
input [31:0] REG1,
input [31:0] REG2,
input [2:0] Type,
output reg BrE
);
wire signed [31:0] signed_REG1;
wire signed [31:0] signed_REG2;
wire unsigned [31:0] unsigned_REG1;
wire unsigned [31:0] unsigned_REG2;
assign signed_REG1 = REG1;
assign signed_REG2 = REG2;
assign unsigned_REG1 = REG1;
assign unsigned_REG2 = REG2;
always@(*)
begin
case(Type)
/*待填*/
3'b010:BrE<=(signed_REG1==signed_REG2?1:0);
3'b011:BrE<=(signed_REG1!=signed_REG2?1:0);
3'b100:BrE<=(signed_REG1<signed_REG2?1:0);
3'b101:BrE<=(signed_REG1>=signed_REG2?1:0);
3'b110:BrE<=(unsigned_REG1<unsigned_REG2?1:0);
3'b111:BrE<=(unsigned_REG1>=unsigned_REG2?1:0);
default:BrE<=1'b0;
endcase
end
endmodule
Q104 ALU模塊
題目描述
ALU全稱Arithmetic and Logic Unit,即算數邏輯單元,是數字計算機中執(zhí)行加、減等算術運算,執(zhí)行與、或等邏輯運算,以及執(zhí)行比較、移位、傳送等操作的功能部件。
ALU模塊如上圖,其中func是區(qū)分操作類型的4位編碼,其與輸入輸出的對應如下表:
注:上表只是一種設計,設計不唯一
輸入格式
1.位寬為32的源操作數SrcA 2.位寬為32的源操作數SrcB 3.位寬為4的func,用于區(qū)分操作類型
輸出格式
位寬為32的運算結果ALUout
代碼
module top_module(
input [31:0] SrcA,SrcB,
input [3:0] func,
output reg [31:0] ALUout
);
wire signed [31:0] signed_a;
wire signed [31:0] signed_b;
wire unsigned [31:0] unsigned_a;
wire unsigned [31:0] unsigned_b;
assign unsigned_a = SrcA;
assign unsigned_b = SrcB;
assign signed_a = SrcA;
assign signed_b = SrcB;
always@(*)
begin
case(func)
/*待填*/
4'b0000:ALUout<=signed_a+signed_b;
4'b1000:ALUout<=signed_a-signed_b;
4'b0001:ALUout<=signed_a<<signed_b[4:0];
4'b0010:ALUout<=signed_a<signed_b?1:0;
4'b0011:ALUout<=unsigned_a<unsigned_b?1:0;
4'b0100:ALUout<=signed_a^signed_b;
4'b0101:ALUout<=signed_a>>signed_b[4:0];
4'b1101:ALUout<=signed_a>>>signed_b[4:0];
4'b0110:ALUout<=signed_a|signed_b;
4'b0111:ALUout<=1'b0;
4'b1001:ALUout<=1'b0;
4'b1010:ALUout<=1'b0;
4'b1011:ALUout<=1'b0;
4'b1100:ALUout<=1'b0;
4'b1110:ALUout<=signed_b;
4'b1111:ALUout<=1'b0;
endcase
end
endmodule
Q105 存儲器
題目描述
存儲器模塊包括指令存儲器和數據存儲器,其中指令存儲器用于存放指令,只讀;數據存儲器用于存放數據,可讀可寫;指令存儲器按字節(jié)編址,按字讀??;數據存儲器按字節(jié)編址,可以完成對字、半字、字節(jié)的對齊存取。
因為指令存儲器的容量有限(本實驗中為16KB,可存儲4K條指令),指令長度為32bit(即4B),因此為了判斷地址是否有效,我們需要判斷im_addr的31至14位以及1至0位是否為0。當im_addr有效時,im_dout輸出地址對應的指令,否則輸出全0。
區(qū)分讀取類型的3位信號dm_rd_ctrl與讀取類型的關系如下:
區(qū)分寫入類型的2位信號dm_wr_ctrl與寫入類型的關系如下:
數據寫入時,由于需要對齊,數據不能跨字(即不能跨越兩個32bit的單元)。在代碼中,我們采用了四位的輔助信號byte_en,對于待覆寫的32bit的單元mem[dm_addr[13:2]],我們可以把其看作四個一字節(jié)(8bit)寬度的單位,對應byte_en的四位,對應位為1表示該字節(jié)需要覆寫,反之不需要,具體如下:
本系列實驗使用哈佛體系結構設計,指令和數據分開存儲,但地址空間是重疊的,所以設計verilog代碼時可以合并成一個模塊。
輸入格式
1.位寬為32bit的地址im_addr 2.位寬為32bit的地址dm_addr 3.位寬為32bit的寫入數據dm_din 4.區(qū)分讀取類型的3位信號dm_rd_ctrl 5.區(qū)分寫入類型的2位信號dm_wr_ctrl;
輸出格式
1.位寬為32bit的指令im_dout 2.位寬為32bit的讀出數據dm_dout文章來源地址http://www.zghlxwxcb.cn/news/detail-780395.html
代碼
module top_module(
input clk,
input [31:0] im_addr,
output [31:0] im_dout,
input [2:0] dm_rd_ctrl,
input [1:0] dm_wr_ctrl,
input [31:0] dm_addr,
input [31:0] dm_din,
output reg [31:0] dm_dout
);
reg [3:0] byte_en;
reg [31:0] mem[0:4095];
reg [31:0] mem_out;
integer i;
initial
begin
for(i=0;i<4095;i=i+1) mem[i] = 0;
end
initial
begin
$readmemh("./problem/inst.dat",mem);
end
assign im_dout = ((|im_addr[31:14]==0) &(|im_addr[1:0]==0))?mem[im_addr[13:2]]:32'b0;/*待填*/
//由于不能跨單位讀取數據,地址最低兩位的數值決定了當前單位能讀取到的數據,即mem_out
always@(*)
begin
case(dm_addr[1:0])
2'b00: mem_out = mem[dm_addr[13:2]][31:0];
2'b01: mem_out = {8'h0,mem[dm_addr[13:2]][31:8]};
2'b10: mem_out = {16'h0,mem[dm_addr[13:2]][31:16]};
2'b11: mem_out = {24'h0,mem[dm_addr[13:2]][31:24]};
endcase
end
always@(*)
begin
case(dm_rd_ctrl)
/*待填*/
3'b001: dm_dout = {{24{mem_out[7]}},mem_out[7:0]};
3'b010: dm_dout = {24'h0,mem_out[7:0]};
3'b011: dm_dout = {{16{mem_out[15]}},mem_out[15:0]};
3'b100: dm_dout = {16'h0,mem_out[15:0]};
3'b101: dm_dout = mem_out;
default: dm_dout=32'b0;
endcase
end
always@(*)
begin
if(dm_wr_ctrl == 2'b11)
byte_en = 4'b1111;
else if(dm_wr_ctrl == 2'b10)
begin
if(dm_addr[1] == 1'b1)
byte_en = 4'b1100;
else
byte_en = 4'b0011;
end
else if(dm_wr_ctrl == 2'b01)
begin
case(dm_addr[1:0])
2'b00: byte_en = 4'b0001;
2'b01: byte_en = 4'b0010;
2'b10: byte_en = 4'b0100;
2'b11: byte_en = 4'b1000;
endcase
end
else
byte_en = 4'b0000;
end
always@(posedge clk)
begin
if((byte_en != 1'b0)&&(dm_addr[30:12]==19'b0))
begin
case(byte_en)
/*待填*/
4'b0001: mem[dm_addr[13:2]][7:0] =dm_din[7:0];
4'b0010: mem[dm_addr[13:2]][15:8] =dm_din[15:8];
4'b0100: mem[dm_addr[13:2]][24:16] =dm_din[24:16];
4'b1000: mem[dm_addr[13:2]][31:17] =dm_din[31:17];
4'b0011: mem[dm_addr[13:2]][15:0] =dm_din[15:0];
4'b1100: mem[dm_addr[13:2]][31:16] =dm_din[31:16];
4'b1111: mem[dm_addr[13:2]] =dm_din;
default: ;
endcase
end
end
endmodule
到了這里,關于中科大OJ Verilog 在線評測題解 100-105的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!