16位CPU怎么做,DIY大神给你支支招儿
状态机基本上与系统所有的组件都连接到一起了,因为上面所说的所有动作的执行,都需要状态机的控制,状态机其实就是由一部分触发器构成的记忆电路和另外一部 分组合逻辑构成的次态译码电路构成,还有根据当前状态和输入进行译码的部分用于控制各个部件,下面是教科书上的典型FSM结构:
而我们用的状态机状态转移图如下:
因为这个处理器设计的很简单,所以没有出现很多状态,当处理器经历完以上的状态之后,处理器就执行完了一条指令。
有的CISC的处理器用微指令进行控制,作用和状态机相近,这种结构出现在一些比较古老的处理器上,因为那个时候的设计工具和方法没有现在的先进,所以往往 改动硬件是困难的和高成本的,所以用微指令的话,做好了硬件的结构,要是需要改动只要修改微指令就好了,而现在的电子技术很发达,设计工具也很完备,所以 就有很多直接通过硬连线实现的处理器。
好马配好鞍,有了处理器,我们就得给它配上一个好的程序,下面我们就用自己设计的处理器进行求和,从1加到100,因为我们没有设计编译器,也没有设计汇编器,所以程序只能用机器码写出,示例程序如下:
我们不妨先写出程序的汇编代码:
mov [ADDR],r0;r0 = 0
mov r1,100
lop:add r2,r1
sub r1,1
cmp r1,0
jz ext
mov r4,4
jmp r4(lop)
ext:mov [ADDR],r2
jmp $
先将内存中存放数据的地址清零,这样才能存放等下送来的结果,然后将r1寄存器存入循环次数(也就是求和的上限)。然后再将r1的值加到r2中来,r2其实就是放求和的寄存器,最后我们会将r2中的值送到内存中的某个地址存放的。
然 后将r1减去1,看看是否为0?如果为0则说明求和结束了,如果不是0则说明还要继续,结束后程序就跳到ext部分将结果存放到内存中某个地址(例子中给 的是49152也就是二进制的1100000000000000b),最后jmp $是为了让程序停在这一行,防止程序跑飞(跑飞的程序危害很大!有可能吧数据当代码或者把代码当数据!)
转换成VerilogHDL语言如下:
module memory
(
input [15:0] addr,
inout [15:0] data,
input rw
);
reg [15:0] data_ram[0:16'b1111_1111_1111_1111];
integer i;
initial begin
for (i = 0; i <= 16'b1111_1111_1111_1111; i = i + 1)
data_ram[i] = $random();
data_ram[0] = 16'b1000000100000000; //mov [ADDR],r0;r0 = 0
data_ram[1] = 16'b1100000000000000; //ADDR
data_ram[2] = 16'b1000000010001000; //mov r1,100
data_ram[3] = 100; //100
//data_ram[2] = 16'b1110011001000000;
data_ram[4] = 16'b0010000100010001; //lop:add r2,r1
data_ram[5] = 16'b1110000011001000; //sub r1,1
data_ram[6] = 16'b0000000000000001; //1
data_ram[7] = 16'b1110000000001000; //cmp r1,0
data_ram[8] = 16'b0000000000000000; //0
data_ram[9] = 16'b1110011010000000; //jz ext
data_ram[10] = 16'b0000000000000011; //+3 offset(ext)
data_ram[11] = 16'b1000000010100000;//mov r4,4
data_ram[12] = 16'b0000000000000100;
data_ram[13] = 16'b0110011001100000;//jmp r4(lop)
data_ram[14] = 16'b1000000100000010;//ext:mov [ADDR],r2
data_ram[15] = 16'b1100000000000000;//ADDR
data_ram[16] = 16'b1110011001000000;//jmp $
data_ram[17] = 16'b1111111111111110;//-2 offset($)
/*data_ram[0] = 16'b1000000010000000; //mov r0,imm
data_ram[1] = 16'b0011111111111111; //imm
data_ram[2] = 16'b0000000001111000; //mov r7,r0
data_ram[3] = 16'b1000000010011000; //mov r3,0
data_ram[4] = 16'b0000000000000000;
data_ram[5] = 16'b1000000010100000; //mov r4,code of jmp r5
data_ram[6] = 16'b0110011001101000; //jmp r5
data_ram[7] = 16'b0000000101011100; //mov [r3],r4
data_ram[8] = 16'b1000000011110000; //mov r6,[0]
data_ram[9] = 16'b0000000000000000; //[0]
data_ram[10]= 16'b1000000100000110; //mov [255],r6
data_ram[11]= 16'b0000000011111111;
data_ram[12]= 16'b0110011001011000; //jmp r3
*/
end
always @ (addr or rw or data)
if (rw)
data_ram[addr] = data;
assign data = rw ? 16'hzzzz : data_ram[addr];
endmodule
设计中CPU外围还需要一个内存设备(Memory),我用HDL对其建模,初始化的时候每个内存地址上对应的数据都初始化为随机的,然后只有从0开始的一系列地址被初始化为我写的代码,机器码对应的汇编指令在注释中已经给出。
然后是结果,结果应该是r2从0变化到5050(1+2+3+......+100=5050)
而r1则从100变化到0,变化到0后程序将进入死循环,停止在jmp $那一条。这是仿真开始的时候:
大家可以看到初始化后,d0~d7都变成了0,这是r0~r7寄存器的Q端,而state_current和state_next则是状态机的现态和状态机 的次态,cpu的各个部件都通过这个状态机受到控制。状态名出现的顺序和上面的FSM Viewer的连线顺序是一样的。
而且大家可以看到,d2从0变化到了0x64也就是十进制100,说明已经执行了第一次加法了。
再来看看仿真结束:
这时候d1变化到了0而d2变化到了0x13ba(十进制的5050),说明程序已经在我们设计的处理器里面运行并且成功的得出了结果!
最后给出一些我用到的指令(跟x86的很像):
add dst,src 将src和dst相加并且送到dst寄存器中
mov [addr],src 将src的值送到以addr位地址的内存单元
sub dst,src 将dst减去src并且送到dst中去
cmp dst,src 将dst减去src 然后不送到dst中 只改变标志位
jz dst 当zf=1时(即上次的算术操作结果为0)则跳转到dst中去
最后再提一下:
我是用synplify综合的电路,然后用debussy+modelsim仿真的。
图片新闻
最新活动更多
-
11月28日立即报名>>> 2024工程师系列—工业电子技术在线会议
-
11月29日立即预约>> 【上海线下】设计,易如反掌—Creo 11发布巡展
-
11月30日立即试用>> 【有奖试用】爱德克IDEC-九大王牌安全产品
-
即日-12.5立即观看>> 松下新能源中国布局:锂一次电池新品介绍
-
12月19日立即报名>> 【线下会议】OFweek 2024(第九届)物联网产业大会
-
即日-12.26火热报名中>> OFweek2024中国智造CIO在线峰会
发表评论
请输入评论内容...
请输入评论/评论长度6~500个字
暂无评论
暂无评论