文章目录
一、 任务
- 了解用Logisim(或Logisim Revolution、LogicCircuit)从低层门电路开始、逐步设计手工画电路实现一个简单自定义的简单8位CPU的方法和过程。试着改用Verilog 编程设计一个 简单周期CPU框架,能够仿真运行若干类 简单的加减法、逻辑运算、跳转指令等指令。
- 了解8051单片机。安装Proteus8软件,设计一个LED流水灯电路,分别用C语言和 51汇编语言编程,实现流水灯正常工作。并解释电路原理图设计、代码设计的工作原理。提交任务时须提交Proteus电路和Keil 源码、可执行Hex文件。 思考下一步你打算模拟实现哪部分8051指令集(以要测试的简单小程序汇编代码依据),将小汇编程序逐条语句手工地翻译为二进制程序数据。
二、 过程
2.1 简单自定义的简单16*16RAM
2.1.1 1 bit memory
需要用到:
-
与非门
-
输入输出
如图绘制:
效果为:
1)当s为1时,输出o与输入i保持一致,相当于把i的数值存到了o处;
2)当s为0时,输入i的变化不影响输出o,输出o的值保持原来的值不变。
可以绘制完可以测试一下
封装
2.1.2 8bit memory
添加八个1 bit memeory
添加8位宽输入输出
连接电路图
可以使用总线拆分器
调整位宽
封装
2.1.3 8-bit Enable
需要用到:
8个与门
1个8位输入
1个1位输入
1个8位输出
封装
2.1.4 寄存器组装
封装
2.1.5 3-8译码器
需要:
与门 8个
输入 3个
非门 3个
输出 8个
连线
封装
2.1.6 存储器地址寄存器
与寄存器相似,但是e管脚要设置为常量1
常量可以在这找到
封装
2.1.7 4x16Decoder
跟38译码器类似,如图构建
封装
2.1.8 内存RAM
- 构建一个存储单元
需要用到8-bit Register
如图连接:
封装
- 构建整个256RAM
首先搭建框架:
连接16x16个RAM单元
封装
2.2 用Verilog 编程设计一个 简单周期CPU框架,能够仿真运行若干类 简单的加减法、逻辑运算、跳转指令等指令。
2.2.1 PCADD4
- PCADD4
module PCadd4(PC_o,PCadd4);
input [31:0] PC_o;//偏移量
output [31:0] PCadd4;//新指令地址
CLA_32 cla32(PC_o,4,0, PCadd4, Cout);
endmodule
- CLA_32
module CLA_32(X,Y,Cin,S,Cout);
input[31:0]X,Y;
input Cin;
output[31:0]S;
output Cout;
wire Cout0,Cout1,Cout2,Cout3,Cout4,Cout5,Cout6;
CLA_4 add0(X[3:0],Y[3:0],Cin,S[3:0],Cout0);
CLA_4 add1(X[7:4],Y[7:4],Cout0,S[7:4],Cout1);
CLA_4 add2(X[11:8],Y[11:8],Cout1,S[11:8],Cout2);
CLA_4 add3(X[15:12],Y[15:12],Cout2,S[15:12],Cout3);
CLA_4 add4(X[19:16],Y[19:16],Cout3,S[19:16],Cout4);
CLA_4 add5(X[23:20],Y[23:20],Cout4,S[23:20],Cout5);
CLA_4 add6(X[27:24],Y[27:24],Cout5,S[27:24],Cout6);
CLA_4 add7(X[31:28],Y[31:28],Cout6,S[31:28],Cout);
endmodule
- CLA_4
module CLA_4(X,Y,Cin,S,Cout);
input [3:0]X,Y;
output Cout;
input Cin;
output [3:0]S;
and i0(Y_3,X[3],Y[3]);
or i1(X_3,X[3],Y[3]);
and i2(Y_2,X[2],Y[2]);
or i3(X_2,X[2],Y[2]);
and i4(Y_1,X[1],Y[1]);
or i5(X_1,X[1],Y[1]);
and i6(Y_0,X[0],Y[0]);
or i7(X_0,X[0],Y[0]);
not i01(Y_31,Y_3);
nand i02(Y_32,X_3,Y_2);
nand i03(Y_33,X_3,X_2,Y_1);
nand i04(Y_34,X_3,X_2,X_1,Y_0);
nand i05(Y_35,X_3,X_2,X_1,X_0,Cin);
nand i00(Cout,Y_31,Y_32,Y_33,Y_34,Y_35);//Cout的输出门级电路实现
not i_2(Y__3,Y_3);
and i21(Y_21,Y__3,X_3);
not i22(Y_22,Y_2);
nand i23(Y_23,X_2,Y_1);
nand i24(Y_24,X_2,X_1,Y_0);
nand i25(Y_25,X_2,X_1,X_0,Cin);
nand i26(Y_26,Y_22,Y_23,Y_24,Y_25);
xor i20(S[3],Y_21,Y_26);//S3的输出门级电路实现
not i_1(Y__2,Y_2);
and i11(Y_11,Y__2,X_2);
not i12(Y_12,Y_1);
nand i13(Y_13,X_1,Y_0);
nand i14(Y_14,X_1,X_0,Cin);
nand i15(Y_15,Y_12,Y_13,Y_14);
xor i10(S[2],Y_11,Y_15);//S2的输出门级电路实现
not i_0(Y__1,Y_1);
and i51(Y_51,Y__1,X_1);
not i52(Y_52,Y_0);
nand i53(Y_53,X_0,Cin);
nand i54(Y_54,Y_52,Y_53);
xor i50(S[1],Y_51,Y_54);//S1的输出门级电路
not i41(Y__0,Y_0);
and i42(Y_4,Y__0,X_0);
xor i40(S[0],Y_4,Cin);//S0的输出门级电路
endmodule
2.2.2 PC
module PC(Clk,Reset,Result,Address);
input Clk;//时钟
input Reset;//是否重置地址。0-初始化PC,否则接受新地址
input[31:0] Result;
output reg[31:0] Address;
initial begin
Address <= 0;
end
always @(posedge Clk or negedge Reset)
begin
if (Reset==0) //如果为0则初始化PC,否则接受新地址
begin
Address <= 0;
end
else
begin
Address = Result;
end
end
endmodule
2.2.3 INSTMEM
module INSTMEM(Addr,Inst);//指令存储器
input[31:0]Addr;
//状态为'0',写指令寄存器,否则为读指令寄存器
output[31:0]Inst;
wire[31:0]Rom[31:0];
assign Rom[5'h00]=32'h20010008;//addi $1,$0,8 $1=8
assign Rom[5'h01]=32'h3402000C;//ori $2,$0,12 $2=12
assign Rom[5'h02]=32'h00221820;//add $3,$1,$2 $3=20
assign Rom[5'h03]=32'h00412022;//sub $4,$2,$1 $4=4
assign Rom[5'h04]=32'h00222824;//and $5,$1,$2 $5=8
assign Rom[5'h05]=32'h00223025;//or $6,$1,$2 $6=12
assign Rom[5'h06]=32'h14220002;//bne $1,$2,2
assign Rom[5'h07]=32'hXXXXXXXX;
assign Rom[5'h08]=32'hXXXXXXXX;
assign Rom[5'h09]=32'h10220002;// beq $1,$2,2
assign Rom[5'h0A]=32'h0800000D;// J 0D
assign Rom[5'h0B]=32'hXXXXXXXX;
assign Rom[5'h0C]=32'hXXXXXXXX;
assign Rom[5'h0D]=32'hAD02000A;// sw $2 10($8) memory[$8+10]=10
assign Rom[5'h0E]=32'h8D04000A;//lw $4 10($8) $4=12
assign Rom[5'h0F]=32'h00221826;//xor $3,$1,$2
assign Rom[5'h10]=32'h00021900;//sll $3,$2,4
assign Rom[5'h11]=32'h00021902;//srl $3,$2,4
assign Rom[5'h12]=32'h00021903;//sra $3,$2,4
assign Rom[5'h13]=32'h30470009;//andi $7,$2,9
assign Rom[5'h14]=32'h382300EF;//xori $3,$1,0xef
assign Rom[5'h15]=32'h3C011234;//lui $1,0x1234
assign Rom[5'h16]=32'h0C00001A;//Jal 1A
assign Rom[5'h17]=32'h0800001A;// J 1A
assign Rom[5'h18]=32'hXXXXXXXX;
assign Rom[5'h19]=32'hXXXXXXXX;
assign Rom[5'h1A]=32'h03E00008;//Jr 16
assign Rom[5'h1B]=32'hXXXXXXXX;
assign Rom[5'h1C]=32'hXXXXXXXX;
assign Rom[5'h1D]=32'hXXXXXXXX;
assign Rom[5'h1E]=32'hXXXXXXXX;
assign Rom[5'h1F]=32'hXXXXXXXX;
assign Inst=Rom[Addr[6:2]];
endmodule
2.2.4 DATAMEM
module DATAMEM(Addr,Din,Clk,We,Dout);
input [31:0]Addr,Din;
input Clk,We;
output [31:0]Dout;
reg [31:0]ram[0:31];
integer i;
initial begin
for ( i = 0 ; i <= 31 ; i = i + 1)
ram [i] = i;
end
always @ (posedge Clk) begin
if (We) ram[Addr[6:2]] <= Din;
end
assign Dout = ram[Addr[6:2]];
endmodule
2.2.5 SHIFTER32_L2
module SHIFTER32_L2(X,Sh);
input [31:0] X;
output [31:0] Sh;
parameter z=2'b00;
assign Sh={X[29:0],z};
endmodule
2.2.6 SHIFTER_COMBINATION
module SHIFTER_COMBINATION(X,PCADD4,Sh);
input [25:0] X;
input [31:0] PCADD4;
output [31:0] Sh;
parameter z=2'b00;
assign Sh={PCADD4[31:28],X[25:0],z};
endmodule
2.2.7 MUX4X32
module MUX4X32(A0, A1, A2, A3, S, Y);
input [31:0] A0, A1, A2, A3;
input [1:0] S;
output [31:0] Y;
function [31:0] select;
input [31:0] A0, A1, A2, A3;
input [1:0] S;
case(S)
2'b00: select = A0;
2'b01: select = A1;
2'b10: select = A2;
2'b11: select = A3;
endcase
endfunction
assign Y = select (A0, A1, A2, A3, S);
endmodule
2.2.8 MUX2X5
module MUX2X5(A0,A1,S,Y);
input [4:0]A0,A1;
input S;
output [4:0] Y;
function [4:0] select;
input [4:0] A0,A1;
input S;
case(S)
1'b0:select=A0;
1'b1:select=A1;
endcase
endfunction
assign Y=select(A0,A1,S);
endmodule
2.2.9 EXT16T32
module EXT16T32(X,Se,Y);
input [15:0]X;
input Se;
output [31:0]Y;
wire [31:0]E0,E1;
wire [15:0]e={16{X[15]}};
parameter z=16'b0;
assign E0={z,X};
assign E1={e,X};
MUX2X32 i(E0,E1,Se,Y);
endmodule
2.2.10 MUX2X32
module MUX2X32(A0,A1,S,Y);
input [31:0]A0,A1;
input S;
output [31:0]Y;
function [31:0]select;
input [31:0]A0,A1;
input S;
case(S)
1'b0:select=A0;
1'b1:select=A1;
endcase
endfunction
assign Y = select(A0,A1,S);
endmodule
2.2.11 CONUNIT
module CONUNIT(
input [5:0]Op,
input [5:0]Func,
input Z,
output Regrt,
output Se,
output Wreg,
output Aluqb,
output [3:0]Aluc,
output Wmem,
output [1:0]Pcsrc,
output Reg2reg,
output shift,
output j
);
wire i_add = (Op == 6'b000000 & Func == 6'b100000)?1:0;
wire i_sub = (Op == 6'b000000 & Func == 6'b100010)?1:0;
wire i_and = (Op == 6'b000000 & Func == 6'b100100)?1:0;
wire i_or = (Op == 6'b000000 & Func == 6'b100101)?1:0;
wire i_xor = (Op == 6'b000000 & Func == 6'b100110)?1:0;
wire i_sll = (Op == 6'b000000 & Func == 6'b000000)?1:0;
wire i_srl = (Op == 6'b000000 & Func == 6'b000010)?1:0;
wire i_sra = (Op == 6'b000000 & Func == 6'b000011)?1:0;
wire i_jr = (Op == 6'b000000 & Func == 6'b001000)?1:0;
//R
wire i_addi = (Op == 6'b001000)?1:0;
wire i_andi = (Op == 6'b001100)?1:0;
wire i_ori = (Op == 6'b001101)?1:0;
wire i_xori = (Op == 6'b001110)?1:0;
wire i_lw = (Op == 6'b100011)?1:0;
wire i_sw = (Op == 6'b101011)?1:0;
wire i_beq = (Op == 6'b000100)?1:0;
wire i_bne = (Op == 6'b000101)?1:0;
wire i_lui = (Op == 6'b001111)?1:0;
//I
wire i_j = (Op == 6'b000010)?1:0;
wire i_jal = (Op == 6'b000011)?1:0;
assign Wreg = i_add|i_sub|i_and|i_or|i_xor|i_sll|i_srl|i_sra|i_addi|i_andi|i_ori|i_or|i_xori|i_lw|i_lui|i_jal;
assign Regrt = i_addi|i_andi|i_ori|i_xori|i_lw|i_sw|i_lui|i_beq|i_bne|i_j|i_jal;
assign Reg2reg = i_add|i_sub|i_and|i_or|i_xor|i_sll|i_srl|i_sra|i_addi|i_andi|i_ori|i_xori|i_sw|i_beq|i_bne|i_j|i_jal;
assign Aluqb = i_add | i_sub | i_and | i_or | i_xor | i_sll | i_srl | i_sra | i_beq | i_bne |i_j;
assign Se = i_addi | i_lw | i_sw | i_beq | i_bne;
assign Aluc[3] = i_sra;
assign Aluc[2] = i_xor |i_lui | i_sll | i_srl | i_sra |i_xori;
assign Aluc[1] = i_and | i_or | i_lui | i_srl | i_sra | i_andi | i_ori;
assign Aluc[0] = i_sub | i_ori | i_or | i_sll | i_srl |i_sra| i_beq | i_bne;
assign Wmem = i_sw;
assign Pcsrc[0] = (i_beq&Z) | (i_bne&~Z) | i_jal | i_j;
assign Pcsrc[1] = i_j | i_jr | i_jal;
assign shift=i_sll | i_srl | i_sra;
assign j=i_jal | i_jr;
endmodule
2.2.12 REGFILE
module REGFILE(Ra,Rb,D,Wr,We,Clk,Clrn,Qa,Qb);
input [4:0]Ra,Rb,Wr;
input [31:0]D;
input We,Clk,Clrn;
output [31:0]Qa,Qb;
wire [31:0]Y;
wire [31:0]Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0;
DEC5T32E dec(Wr,We,Y);
REG32 reg32(D,Y,Clk,Clrn,Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0);
MUX32X32 select1(Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0,Ra,Qa);
MUX32X32 select2(Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0,Rb,Qb);
endmodule
2.2.13 ALU
module ALU(X,Y,Aluc,R,Z);
input[31:0]X,Y;
input[3:0]Aluc;
output[31:0]R;
output Z;
wire[31:0]d_as,d_and,d_or,d_xor,d_lui,d_sh,d;
ADDSUB_32 as32(X,Y,Aluc[0],d_as);
assign d_and=X&Y;
assign d_or=X|Y;
assign d_xor=X^Y;
assign d_lui={Y[15:0],16'h0};
SHIFTER shift(Y,X[10:6],Aluc[3],Aluc[1],d_sh);
MUX6X32 select(d_and,d_or,d_xor,d_lui,d_sh,d_as,Aluc[3:0],R);
assign Z=~|R;
endmodule
2.2.14 CPU
module CPU(Clk,Reset,Addr,Inst,Qa,Qb,ALU_R,NEXTADDR,D);
input Clk,Reset;
output [31:0] Inst,NEXTADDR,ALU_R,Qb,Qa,Addr,D;
wire [31:0]Result,PCadd4,EXTIMM,InstL2,EXTIMML2,D1,X,Y,Dout,mux4x32_2,R;
wire Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem,shift,j;
wire [3:0]Aluc;
wire [1:0]Pcsrc;
wire [4:0]Wr,Wr1;
PC pc(Clk,Reset,Result,Addr);
PCadd4 pcadd4(Addr,PCadd4);
INSTMEM instmem(Addr,Inst);
CONUNIT conunit(Inst[31:26],Inst[5:0],Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg,shift,j);
MUX2X5 mux2x5_1(Inst[15:11],Inst[20:16],Regrt,Wr1);
MUX2X5 mux2x5_2(Wr1,31,j,Wr);
EXT16T32 ext16t32(Inst[15:0],Se,EXTIMM);
SHIFTER_COMBINATION shifter1(Inst[25:0],PCadd4,InstL2);
SHIFTER shifter2(EXTIMM,2,0,0,EXTIMML2);
REGFILE regfile(Inst[25:21],Inst[20:16],D,Wr,Wreg,Clk,Reset,Qa,Qb);
MUX2X32 mux2x32_1(EXTIMM,Qb,Aluqb,Y);
MUX2X32 mux2x32_2(Qa,Inst,shift,X);
ALU alu(X,Y,Aluc,R,Z);
DATAMEM datamem(R,Qb,Clk,Wmem,Dout);
MUX2X32 mux2x32_3(Dout,R,Reg2reg,D1);
MUX2X32 mux2x32_4(D1,PCadd4,j,D);
CLA_32 cla_32(PCadd4,EXTIMML2,0,mux4x32_2,Cout);
MUX4X32 mux4x32(PCadd4,mux4x32_2,Qa,InstL2,Pcsrc,Result);
assign NEXTADDR=Result;
assign ALU_R=R;
endmodule
2.2.15 Test测试代码
module test();
reg Clk,Reset;
wire [31:0] Inst,NEXTADDR,ALU_R,Qb,Qa,Addr,D;
wire [31:0]Result,PCadd4,EXTIMM,InstL2,EXTIMML2,X,Y,Dout,mux4x32_2,R,D1;
wire Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem,shift,j;
wire [3:0]Aluc;
wire [1:0]Pcsrc;
wire [4:0]Wr,Wr1;
initial begin
Clk=0;
Reset=0;
#5
Reset<=1;
end
always #5 Clk=~Clk;
PC pc(Clk,Reset,Result,Addr);
PCadd4 pcadd4(Addr,PCadd4);
INSTMEM instmem(Addr,Inst);
CONUNIT conunit(Inst[31:26],Inst[5:0],Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg,shift,j);
MUX2X5 mux2x5_1(Inst[15:11],Inst[20:16],Regrt,Wr1);
MUX2X5 mux2x5_2(Wr1,31,j,Wr);
EXT16T32 ext16t32(Inst[15:0],Se,EXTIMM);
SHIFTER_COMBINATION shifter1(Inst[25:0],PCadd4,InstL2);
SHIFTER shifter2(EXTIMM,2,0,0,EXTIMML2);
REGFILE regfile(Inst[25:21],Inst[20:16],D,Wr,Wreg,Clk,Reset,Qa,Qb);
MUX2X32 mux2x32_1(EXTIMM,Qb,Aluqb,Y);
MUX2X32 mux2x32_2(Qa,Inst,shift,X);
ALU alu(X,Y,Aluc,R,Z);
DATAMEM datamem(R,Qb,Clk,Wmem,Dout);
MUX2X32 mux2x32_3(Dout,R,Reg2reg,D1);
MUX2X32 mux2x32_4(D1,PCadd4,j,D);
CLA_32 cla_32(PCadd4,EXTIMML2,0,mux4x32_2,Cout);
MUX4X32 mux4x32(PCadd4,mux4x32_2,Qa,InstL2,Pcsrc,Result);
assign NEXTADDR=Result;
assign ALU_R=R;
endmodule
2.2.16 运行结果
2.2 C51流水灯
1. 创建工程
点击新建
选择工程目录
然后一直点下一步知道出现一下页面,选择如下选项:
完成,在红圈处编写代码
2. 开始仿真
① 添加电元件
本次需要用到的元件:LED-YELLOW、RES
然后点击左侧边栏的LED-YELLOW,并将他旋转之后,再点击幕布添加到原理图内:
然后依次添加剩余7个LED和电阻
点击电阻的"30k"修改阻值
点击左侧总线模式,然后单击幕布,拖动到目标位置双击,添加总线:
点击左侧结点连接管脚如图:
添加电源:
如果自带电源请删除掉:
最后通过LBL给直线上编号,让支线根据编号对应:
② 使用Keil来编写代码
Ⅰ准备工作
下载Keil
Ⅱ 创建项目
点击顶栏创建项目
这个界面选择“Mincrochip”的"AT89C51"
选是
左上角新建文件
Ⅲ 写入代码
复制以下代码:
#include <reg51.h>
#include <intrins.h>
void delay_ms(int a)
{
int i,j;
for(i=0;i<a;i++)
{
for(j=0;j<1000;j++) _nop_();
}
}
void main(void)
{
while(1)
{
P0=0xfe;
delay_ms(50);
P0=0xfd;
delay_ms(50);
P0=0xfb;
delay_ms(50);
P0=0xf7;
delay_ms(50);
P0=0xef;
delay_ms(50);
P0=0xdf;
delay_ms(50);
P0=0xbf;
delay_ms(50);
P0=0x7f;
delay_ms(50);
}
}
保存代码为main.c
在左侧边栏Source Group 1 文件夹右键添加main.c
Ⅳ 编译
点击魔法棒,在output栏勾选该选项,点ok
点击编译生成两个头文件
③ 仿真
双击Proteus界面的芯片
选择Keil代码目录下的.hex文件
点击运行
运行效果
参考链接
1.https://blog.csdn.net/qq_44040327/article/details/116395694
2. https://blog.csdn.net/lxr0106/article/details/133191034?spm=1001.2014.3001.5501
3. https://blog.csdn.net/qq_42840665/article/details/114003321