主题文档

编写个超简单的cpu-k8凯发

fpga的处理能力固然强大,但在进行程序化的任务时,用状态机来实现有时就显得不如cpu写程序那么简洁。在fpga里面也可以用逻辑来搭出简单的cpu,并固化一小段代码去实现特定的功能。考虑下最简单的cpu是什么样子呢?

最少,需要有读取程序(指令),并执行指令的过程。指令存放在一块内存当中,cpu每步取一条指令来执行,根据读出的指令内容,内部的状态发生转变——比如寄存器按指令要求进行运算,比如访问外部的端口(或总线)。指令是一个编码,描述这一步需要做的事情;执行指令的过程就是状态转移的过程。我实验的这个超简单cpu是这样:

业界

上图中,pc是program counter,就是程序计数器,选择rom中程序执行的地址。opr用来存放当前的指令,它的内容从rom中读到。寄存器还有a寄存器和r0~r7寄存器,用来计算和存放结果,另外还有一个1-bit的“零"标志位zflag,是给条件转移指令用的。当然,若只是里面的寄存器变来变去,这个cpu就没有实用价值了,所以还有一个输入端口,以及一个输出端口,用来和寄存器a交换数据。

设计指令字长为8-bit,寄存器宽度也为8-bit。每条指令都是从rom中读8-bit,可以最多有256种不同的指令,当然指令中能编码立即数,所以指令不会有那么多种。我给这个cpu设计了14条指令:

业界

跳转指令有2条,无条件转移和z条件转移,转移范围为5-bit相对地址,即-16~ 15。

带立即数指令有4条,因为指令才8-bit,立即数只好分配4-bit了。装入a寄存器的高4位或低4位,以及与a做加减法。

r0~r7寄存器只能与a寄存器进行copy和比较操作。

影响zflag标志的指令有位测试指令testb, 比较指令comp和加减法指令。

指令空间并没有用完,可以根据需要再补充指令。

用verilog语言来写这个cpu的状态转移部分:

  1. module coderom(addr, data);

  2. input [9:0] addr;

  3. output reg [7:0] data;

  4. always @(addr) begin

  5. case(addr)

  6. 0 : data = 8'h80; // loadal 0

  7. 1 : data = 8'h90; // loadah 0

  8. 2 : data = 8'h01; // out a

  9. 3 : data = 8'ha1; // adda #1

  10. 4 : data = 8'h40; // mov r0, a

  11. 5 : data = 8'h00; // in a

  12. 6 : data = 8'h77; // testb a,7

  13. 7 : data = 8'h50; // mov a, r0

  14. 8 : data = 8'hdb; // jumpz 4

  15. 9 : data = 8'hf8; // jump 2

  16. default: data=8'h00;

  17. endcase

  18. end

  19. endmodule

这个程序不干啥有价值的,就是检测到输入端口第7位为高时,循环加一计数,输出到端口点led.

顶层模块,将rom和cpu连起来:

  1. module cpu_top(clk, porti, porto);

  2. input clk;

  3. input [7:0] porti;

  4. output [7:0] porto;

  5. wire [7:0] rom_q;

  6. wire [9:0] rom_addr;

  7. cpu0 minicpu(.clk(clk),

  8. .porti(porti),

  9. .porto(porto),

  10. .ibus(rom_q),

  11. .iaddr(rom_addr));

  12. coderom rom(

  13. .addr(rom_addr),

  14. .data(rom_q));

  15. endmodule

点击阅读原文可与本文作者交流、沟通

以上图文内容均是eeworld论坛网友:cruelfox 原创,在此感谢。

the end
本文已经被浏览: 次,获赞:  次
网站地图