单周期CPU——verilog语言实现
Posted b1ing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单周期CPU——verilog语言实现相关的知识,希望对你有一定的参考价值。
一. 实验内容
设计一个单周期CPU,要求:
1. 实现MIPS的20条指令
2. 在该CPU上实现斐波那契函数
计算机每执行一条指令都可分为三个阶段进行。即取指令(IF)——>分析指令(ID)——>执行指令(EXE)
取指令:根据程序计数器PC中的指令地址,从存储器中取出一条指令,同时,根据控制信号,决定选择某个来源的指令地址作为下一条指令的地址。
分析指令:对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,从而产生相应的操作控制信号,用于驱动执行状态中的各种操作。
执行指令:根据指令译码得到的操作控制信号,具体地执行指令动作。
二. 20条指令的情况
R指令
I指令
J指令
三. 取指令(IF)的相关模块代码
//PC module PC( input [31:0] next_addr, input rst, input clk, output reg [31:0] addr ); always @(posedge clk) begin if(rst==1‘b1) begin addr<=next_addr; end else begin addr<=32‘b0; end end initial $monitor($time,,"PC:addr=%h",addr); endmodule //ROM module rom( input [31:0] addr, output [31:0] data ); reg[31:0] romdata; always @(*) case(addr[31:2]) 4‘h0:romdata=32‘b10001100000000110000000000100000; //lw $0,$3,32 * 4‘h1:romdata=32‘b00110100000100000000000000000010; //ori $0,$16,2 * 4‘h2:romdata=32‘b00000000000000111000100000100101; //or $0,$3,$17 * 4‘h3:romdata=32‘b00110100000100110000000000000001; //ori $0,$19,1 * 4‘h4:romdata=32‘b00110100000001000000000000000001; //ori $0,$4,1 * 4‘h5:romdata=32‘b00010010001100110000000000001011; //beq $17,$19,11 * 4‘h6:romdata=32‘b00000000000001000100000000100101; //or $0,$4,$8 * 4‘h7:romdata=32‘b00100010011100110000000000000001; //addi $19,$19,1 * 4‘h8:romdata=32‘b00110100000001000000000000000001; //ori $0,$4,1 * 4‘h9:romdata=32‘b00010010001100110000000000000111; //beq $17,$19,7 * 4‘ha:romdata=32‘b00000000000001000100100000100101; //or $0,$4,$9 * 4‘hb:romdata=32‘b00000001000010010010000000100000; //add $8,$9,$4 * 4‘hc:romdata=32‘b00000000000010010100000000100101; //or $0,$9,$8 * 4‘hd:romdata=32‘b00000000000001000100100000100101; //or $0,$4,$9 * 4‘he:romdata=32‘b00100010000100000000000000000001; //addi $16,$16,1 * 4‘hf:romdata=32‘b00010110000100011111111111111011; //bne $16,$17,-5 * default:romdata=32‘b10101100000001000000000000010000; //sw $0,$4,16 endcase assign data=romdata; initial $monitor($time,,"rom:romdata=%h",romdata); endmodule //Selector module selector( input [31:0] addr0, input [31:0] addr1, input [31:0] addr2, input [31:0] addr3, input [1:0] pcsource, output reg [31:0] next_addr ); always @(*) begin case(pcsource) 2‘b00: begin next_addr=addr0; end 2‘b01: begin next_addr=addr1; //bne,beq end 2‘b10: begin next_addr=addr2; //j end 2‘b11: begin next_addr=addr3; //jal,jr end endcase end initial $monitor($time,,"selector: pcsource=%h, next_addr=%h",pcsource,next_addr); endmodule
四. 所使用的控制信号
pcindex: pc值的来源
ram2reg: 是否将数据从RAM写入到寄存器中,=1为是,否则为否
ramWE: 是否写内存,=1为是,否则为否
aluOP: ALU的运算类型
regWE: 是否写寄存器,=1为是,否则为否
imm: 是否产生立即数,=1为是,否则为否
shift: 是否移位,=1为是,否则为否
isrt: 目的寄存器地址,=1选择rt,否则选择rd
sign_ext: 立即数拓展,=1为符号数拓展,否则为零拓展
jal: 是否调用子程序跳转,=1为是,否则为否
五. 各指令对应产生的控制信号
pcindex | ram2reg | ramWE | aluOP | regWE | imm | shift | isrt | sign_ext | jal | |
add | 0 | 0 | 0 | 0001 | 1 | 0 | 0 | 0 | 0 | 0 |
sub | 0 | 0 | 0 | 0010 | 1 | 0 | 0 | 0 | 0 | 0 |
and | 0 | 0 | 0 | 0011 | 1 | 0 | 0 | 0 | 0 | 0 |
or | 0 | 0 | 0 | 0100 | 1 | 0 | 0 | 0 | 0 | 0 |
xor | 0 | 0 | 0 | 0101 | 1 | 0 | 0 | 0 | 0 | 0 |
sll | 0 | 0 | 0 | 0110 | 1 | 0 | 1 | 1 | 0 | 0 |
srl | 0 | 0 | 0 | 0111 | 1 | 0 | 1 | 1 | 0 | 0 |
sra | 0 | 0 | 0 | 1000 | 1 | 0 | 1 | 1 | 0 | 0 |
jr | 10 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
addi | 0 | 0 | 0 | 0001 | 1 | 1 | 0 | 1 | 1 | 0 |
andi | 0 | 0 | 0 | 0011 | 1 | 1 | 0 | 1 | 0 | 0 |
ori | 0 | 0 | 0 | 0100 | 1 | 1 | 0 | 1 | 0 | 0 |
xori | 0 | 0 | 0 | 0101 | 1 | 1 | 0 | 1 | 0 | 0 |
lw | 0 | 1 | 0 | 0001 | 1 | 1 | 0 | 1 | 1 | 0 |
sw | 0 | 0 | 1 | 0001 | 0 | 1 | 0 | 1 | 1 | 0 |
beq | 00/01 | 0 | 0 | 0010 | 0 | 0 | 0 | 0 | 1 | 0 |
bne | 00/01 | 0 | 0 | 0010 | 0 | 0 | 0 | 0 | 1 | 0 |
lui | 0 | 0 | 0 | 1001 | 1 | 1 | 0 | 1 | 0 | 0 |
j | 11 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
jal | 11 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
六. 分析指令(ID)相关模块代码
//ID module ID( input [31:0] instrument, output reg [5:0] opcode, output reg [5:0] func, output reg [4:0] rs, output reg [4:0] rt, output reg [4:0] rd, output reg [4:0] sa, output reg [15:0] immediate, output reg [25:0] addr ); always @(*) begin opcode=instrument[31:26]; rs=5‘b0; rt=5‘b0; rd=5‘b0; sa=5‘b0; immediate=16‘b0; addr=25‘b0; case(opcode) 6‘b000000: //R类型 begin func=instrument[5:0]; sa=instrument[10:6]; rd=instrument[15:11]; rt=instrument[20:16]; rs=instrument[25:21]; end 6‘b001000,6‘b001100,6‘b001101,6‘b001110,6‘b100011,6‘b101011,6‘b000100,6‘b000101,6‘b001111: begin immediate=instrument[15:0]; rt=instrument[20:16]; rs=instrument[25:21]; end 6‘b000010,6‘b000011: begin addr=instrument[25:0]; end default: rs=5‘b00000; endcase end endmodule //CU module CU( input [5:0] opcode, input [5:0] func, input z, output reg [1:0] pcindex, output reg ram2reg, output reg ramWE, output reg [3:0] aluOP, output reg regWE, output reg imm, output reg shift, output reg isrt, output reg sign_ext, output reg jal ); always @(*) begin //设置默认值 shift=1‘b0; ram2reg=1‘b0; ramWE=1‘b0; regWE=1‘b0; imm=1‘b0; isrt=1‘b0; sign_ext=1‘b0; pcindex=2‘b00; aluOP=4‘b0000; jal=1‘b0; case(opcode) //R指令 6‘b000000: begin case(func) 6‘b100000: //add指令 begin aluOP=4‘b0001; regWE=1‘b1; end 6‘b100010: //sub指令 begin aluOP=4‘b0010; regWE=1‘b1; end 6‘b100100: //and指令 begin aluOP=4‘b0011; regWE=1‘b1; end 6‘b100101: //or指令 begin aluOP=4‘b0100; regWE=1‘b1; end 6‘b100110: //xor指令 begin aluOP=4‘b0101; regWE=1‘b1; end 6‘b000000: //sll指令 begin aluOP=4‘b0110; regWE=1‘b1; shift=1‘b1; isrt=1‘b1; end 6‘b000010: //srl指令 begin aluOP=4‘b0111; regWE=1‘b1; shift=1‘b1; isrt=1‘b1; end 6‘b000011: //sra指令 begin aluOP=4‘b1000; regWE=1‘b1; shift=1‘b1; isrt=1‘b1; end 6‘b001000: //jr指令 begin pcindex=2‘b10; end endcase end //I指令 6‘b001000: //addi指令 begin aluOP=4‘b0001; imm=1‘b1; regWE=1‘b1; sign_ext=1‘b1; isrt=1‘b1; end 6‘b001100: //andi指令 begin aluOP=4‘b0011; imm=1‘b1; regWE=1‘b1; isrt=1‘b1; end 6‘b001101: //ori指令 begin aluOP=4‘b0100; imm=1‘b1; regWE=1‘b1; isrt=1‘b1; end 6‘b001110: //xori指令 begin aluOP=4‘b0101; imm=1‘b1; regWE=1‘b1; isrt=1‘b1; end 6‘b100011: //lw指令 begin ram2reg=1‘b1; aluOP=4‘b0001; imm=1‘b1; regWE=1‘b1; sign_ext=1‘b1; isrt=1‘b1; end 6‘b101011: //sw指令 begin ramWE=1‘b1; aluOP=4‘b0001; imm=1‘b1; sign_ext=1‘b1; isrt=1‘b1; end 6‘b000100: //beq指令 begin aluOP=4‘b0010; sign_ext=1‘b1; if(z==1‘b1) begin pcindex=2‘b01; end end 6‘b000101: //bne指令 begin aluOP=4‘b0010; sign_ext=1‘b1; if(z==1‘b0) begin pcindex=2‘b01; end end 6‘b001111: //lui指令 begin regWE=1‘b1; imm=1‘b1; isrt=1‘b1; aluOP=4‘b1001; end //J指令 6‘b000010: //j指令 begin pcindex=2‘b11; end 6‘b000011: //jal指令 begin jal=1‘b1; regWE=1‘b1; pcindex=2‘b11; end endcase end initial $monitor($time,,"CU:imm=%b,op=%h, isrt=%h, pcindex=%h, regWE=%h, shift=%h, ram2reg=%h, ramWE=%h ",imm,aluOP,isrt,pcindex,regWE,shift,ram2reg,ramWE); endmodule
七. 总体电路图
八. 执行指令(EXE)相关模块的代码
//selector_5 module selector_5( input [4:0] a, input [4:0] b, input choice, output [4:0] f ); assign f=(choice==1‘b0)?a:b; initial $monitor($time,,"selector_5: a=%h, b=%h, f=%h",a,b,f); endmodule //selector_32 module selector_32( input [31:0] a, input [31:0] b, input choice, output [31:0] f ); assign f=(choice==1‘b0)?a:b; initial $monitor($time,,"selector_32: f=%h",f); endmodule //ext_imm module ext_imm( input [15:0] immediate, input sign_ext, output [31:0] imm ); //sign_ext为1时,有符号拓展;为0时,0拓展 assign imm=(sign_ext==0)?{{16{1‘b0}},immediate}:{{16{immediate[15]}},immediate}; initial $monitor($time,,"ext_imm: imm=%h",imm); endmodule //alu_add_4 module alu_add_4( input [31:0] a, output [31:0] f ); assign f=a+32‘b0100; initial $monitor($time,,"alu_add_4:f=%h",f); endmodule //registers module registers( input clk, input oc, input [4:0] raddr1, input [4:0] raddr2, input [4:0] waddr, input [31:0] wdata, input we, output reg [31:0] rdata1, output reg [31:0] rdata2 ); reg[31:0] regts[1:31]; always @(*) begin if(oc==1‘b1) begin rdata1=32‘bz; end else if(raddr1==5‘b00000) begin rdata1=32‘b0; end else begin rdata1=regts[raddr1]; end $monitor($time,,"Read R %d data1=%d",raddr1,rdata1); end always @(*) begin if(oc==1‘b1) begin rdata2=32‘bz; end else if(raddr2==5‘b00000) begin rdata2=32‘b0; end else begin rdata2=regts[raddr2]; end $monitor($time,,"Read R %d data2=%d",raddr2,rdata2); end always @(posedge clk) begin #1 if((we==1‘b1)&&(waddr!=5‘b00000)) begin regts[waddr]<=wdata; $monitor($time,,"write %h to data=%d",waddr,wdata); end end endmodule //isimm module isimm( input [31:0] rt, input [31:0] immediate, input imm, output [31:0] b ); assign b=imm?immediate:rt; initial $monitor($time,,"isimm:b=%h",b); endmodule //ALU module alu( input [31:0] a, input [31:0] b, input [3:0] op, output [31:0] f, output z ); reg [31:0]result; always@(*) begin case(op) 4‘b0000:result=32‘b0; 4‘b0001:result=a+b; 4‘b0010:result=a-b; 4‘b0011:result=a&b; 4‘b0100:result=a|b; 4‘b0101:result=a^b; 4‘b0110:result=b<<a; 4‘b0111:result=b>>a; 4‘b1000:result=$signed(b)>>>a; 4‘b1001:result={b,{16{1‘b0}}}; default:result=32‘b0; endcase end assign f=result; assign z=~(|result); initial $monitor($time,,"alu:a:%h,b:%h,op:%h,",a,b,op); initial $monitor($time,,"alu:f:%h,z:%b",f,z); endmodule //IOManager module IOManager( input [5:0] addr, input [31:0] din, output [31:0] dout, input we, input clk, input [3:0] switch, output reg[31:0] displaydata ); reg [31:0] indata,outdata; wire [31:0] ramdout; wire ramWE; wire enable; assign enable=(|addr[5:4]); ram dram(addr[3:0],din,clk,enable,ramWE,ramdout); assign dout=(addr[5]==1‘b1)?{{28{1‘b0}},switch}:ramdout; assign ramWE=we&(~addr[4]); always @(posedge clk) begin if((addr[4]==1‘b1)&&we) displaydata<=din; end initial $monitor($time,,"IOM: addr=%h, din=%h, dout=%h",addr,din,dout); initial $monitor($time,,"IOM: switch=%h, displaydata=%h",switch,displaydata); endmodule //ram module ram( input [3:0] addr, input [31:0] wdata, input clk, input ce, input we1, output reg[31:0] rdata ); reg [31:0] ram[0:30]; always @(*) begin if(ce==1‘b0) begin rdata=ram[addr]; end end always @(posedge clk) begin #1 if((ce==1‘b0)&&(we1==1‘b1)) begin ram[addr]<=wdata; end end initial $monitor($time,,"ram: ram=%h,rdata=%h",ram[addr],rdata); endmodule //left module left( input [31:0] a, output [31:0] b ); assign b=a<<2; endmodule //alu_add_b module alu_add_b( input [31:0] addr, input [31:0] immediate, output [31:0] f ); assign f=addr+immediate; initial $monitor($time,,"alu_add_b: addr=%h, immediate=%h, f=%h ",addr,immediate,f); endmodule //ext_adder module ext_adder( input [25:0] address, output [27:0] f ); assign f={address,{2{1‘b0}}}; initial $monitor($time,,"ext_addr: address=%h, f=%h ",address,f); endmodule //mixaddr module mixAddr( input [31:0] addr, input [27:0] address, output [31:0] f ); assign f={{addr[31:28]},address}; initial $monitor($time,,"mixAddr: addr=%h, address=%h, f=%h",addr,address,f); endmodule
九. TOP文件代码
1 module top( 2 input clk, 3 input rst, 4 input [4:0] n, 5 output [31:0] result 6 ); 7 8 wire [31:0] next_addr; 9 wire [31:0] addr,addr0,addr1,addr2,addr3; 10 wire [31:0] immediate; 11 wire [27:0] fAddress; 12 wire [31:0] instrument; 13 wire [5:0] opcode,func; 14 wire [4:0] rs,rt,rd,sa; 15 wire [15:0] imm; 16 wire [25:0] address; 17 wire z; 18 wire [1:0] pcindex; 19 wire ram2reg,ramWE,regWE,isimm,shift,isrt,sign_ext,jal; 20 wire [3:0] aluOP; 21 wire [4:0] waddr; 22 wire [4:0] tmp_waddr; 23 wire [4:0] regs31=5‘b11111; 24 wire [31:0] wdata,rdata1,rdata2,f,tmp_f; 25 wire oc=1‘b0; 26 wire [31:0] a,b; 27 wire [31:0] dout; 28 wire [31:0] lImmediate; 29 wire [31:0] fsa; 30 31 PC myPC(next_addr,rst,clk,addr); 32 rom myRom(addr,instrument); 33 ID myID(instrument,opcode,func,rs,rt,rd,sa,imm,address); 34 CU myCU(opcode,func,z,pcindex,ram2reg,ramWE,aluOP,regWE,isimm,shift,isrt,sign_ext,jal); 35 ext_imm myExtImm(imm,sign_ext,immediate); 36 selector_5 mySelector5_1(rd,rt,isrt,tmp_waddr); //目的寄存器是否rt 37 selector_5 mySelector5_2(tmp_waddr,regs31,jal,waddr); //是否调用子程序调转wa 38 alu_add_4 myAdd4(addr,addr0); 39 selector_32 mySelector32_1(f,addr0,jal,wdata); //是否调用子程序调转wd 40 registers myRegs(clk,oc,rs,rt,waddr,wdata,regWE,rdata1,rdata2); 41 isimm myIsImm(rdata2,immediate,isimm,b); 42 assign fsa={{27{1‘b0}},sa}; 43 selector_32 mySelector32_2(rdata1,fsa,shift,a); //选择sa和rs数据 44 alu myAlu(a,b,aluOP,tmp_f,z); 45 IOManager myIOM(tmp_f,rdata2,dout,ramWE,clk,n,result); 46 selector_32 mySelector32_3(tmp_f,dout,ram2reg,f); //是否将数据从ram中写入reg 47 left myLeft(immediate,lImmediate); 48 alu_add_b myAddb(addr0,lImmediate,addr1); 49 ext_adder myExtAddr(address,fAddress); 50 mixAddr myMixAddr(addr,fAddress,addr3); 51 selector mySelector(addr0,addr1,rdata1,addr3,pcindex,next_addr); 52 53 54 initial 55 $monitor($time,,"top: n=%h, result=%h",n,result); 56 57 endmodule
以上是关于单周期CPU——verilog语言实现的主要内容,如果未能解决你的问题,请参考以下文章