数字集成电路设计-7-一个简单cpu的设计,实现,仿真与综合
引言
经过对OpenRISC近一年的分析与研究,在此过程中我们了解了计算机体系结构设计相关的主要概念,重要的技术,和基本思想。我觉的,现在我们有必要练练手了。
本小节,我们将设计一个简单的cpu,包括ISA的设计,模块的划分,RTL实现,编写asm汇编程序,用modelsim进行仿真,以及用quartusII的综合。
1,计算器与计算机
我认为,在EDVAC计算机之前的计算机,都可认为是计算器。
原因是,冯诺依曼对EDVAC分析时提出了二进制运算和加入存储部件,而在这之前的计算机是没有存储功能的,比如我们要计算(1+2)&(3+4),如果是采用计算器,其运算步骤如下:
a,先用计算器算出1+2的结果3,然后人脑自己记住这个数。
b,再用计算器计算出3+4的结果7,人脑也自己记住这个数。
c,最后用计算器算出3&7的结果3。
如果采用计算机,其运算过程如下:
首先我们需要写一段程序,假设程序放在程序存储器的地址0x0处,数据1,2,3,4分别放在数据存储器的55,56,57,58四个地址。
程序的执行过程如下:
a,将data_mem的0x55处的数据放到r1。
b,将data_mem的0x56处的数据放到r2。
c,执行add r2,r1,结果放在r2里面。
d,将r2的内容写入到data_mem的0x60这个地址。
e,将data_mem的0x57处的数据放到r3。
f,将data_mem的0x58处的数据放到r4。
g,执行add r4,r3,结果放在r4里面。
h,将r4的内容写入到data_mem的0x61这个地址。
i,将data_mem的0x60处的数据放到r5。
j,将data_mem的0x61处的数据放到r6。
k,执行and r6,r5,结果放在r6里面。
l,将r6的内容写入到data_mem的0x62这个地址,最终得到计算结果。
我们可以看出,如果用计算器计算,只需三步就可以完成,但是如果用计算机的话需要12步才能完成。那是不是用计算机的效率低呢?今天计算机的蓬勃发展使答案不言而喻。
原因就是只要实现写好程序,用计算机的整个计算过程不用人为干预。
我想这正是计算机发展的根本原因之所在,就是计算机的出现是对人的很大解放。我们只要按照一定的方式写好程序,然后交给计算机,计算机会自动完成任务,而我们的手就可以干些其他的事情了!
2,架构设计
1>整体设计
通过上面的例子,我们可以体会到计算机的好处,下面我们就动手设计一个cpu,来完成1+2的计算。
关于计算机体系结构,我们之前说过的内容已经够多了。这里只说明以下几点:
a,我们采用harvard结构,即,指令和数据的总线是独立的。
b,流水线,我们暂时不采用流水设计,但是在最后,我给出了五级流水时的数据通路设计框架。
c,关于指令集,由于是学习目的,我们只实现基本的访存指令,运算指令和分支指令。运算不支持乘除和浮点。关于具体的指令细节,请参考附录。每条指令为16-bit。
d,为了对我们设计的cpu进行仿真和验证,我们需要设计一个简单的soc才行,这个soc只包含指令存储器,cpu内核,数据存储器。
e,core内总线为8-bit。这就有一个问题,core外是8-bit,但是分支指令的目的地址为11-bit,所以如果超过8-bit,就会有问题,暂时还没解决。
下面是soc的整体架构图:我们给他取个名字吧,就叫 tiny_soc,小cpu就简单的称她为tiny_core。
2>模块划分
cpu core的结构如下:
整个cpu core由数据通路和控制通路和调试单元组成。
其中数据通路包括:
PC的产生模块:genpc
运算模块:alu,在alu的前面是对操作数多选一的一个mux。
寄存器堆:regfile模块
还有栈:stack。
数据通路受控制通路模块ctrl_path模块的控制,控制通路负责指令的解码,并产生对应的控制信号。
调试单元,由于只是学习目的,调试单元最简化,只输出当前的PC值和当前的指令内容两个信息。
3,模块划分与接口定义
整体的架构设计完成后,我们就需要进一步的细化了,这时,需要定义具体的模块名称,模块功能,一旦功能确定之后我们就可以确定具体的模块接口信号了。
如果模块功能过大,我们需要拆分成更小的模块,这就是top-down的设计方法。关于设计方法学(top-down,bottom-up),很多资料里都有介绍,这里就不再赘述了。
一个完整的工程,和做理论研究不同,需要处理很多实现细节,下面,我们介绍一下,其中比较重要的部分:
1>genpc模块
这里面需要考虑三点:上电复位PC默认值为多少?正常指令执行时PC如何变化?遇到分支指令时PC如何变化?
关于上电默认值,我们可以通过一个define语句来设定,允许用户后期修改方便。
关于正常指令的指令,PC是加1还是加2还是加4,这要看指令存储器的访问方式,我们的指令存储器是每个地址放一条指令,每条指令是2个字节(16-bit),所以我们只要PC加1就可以了。
关于遇到分支指令,我们直接将控制通路经过解码的跳转地址赋给PC即可。
genpc模块的C语言伪代码如下:
genpc module pseudo codeif(rst)
{pc= boot_addr;
}
else
{if(branch){pc = branch_target;}else{pc = pc +1;}
}
2>alu模块
alu模块,大家都很熟悉了,是执行单元部件,负责运算指令的运算工作。
这个模块的输入信号是有控制通路的解码出来的操作数和操作码,输出信号就是运算结果。
需要说明的是,这个模块可以完全是组合逻辑电路。
3>rf模块
register file模块,从物理上来说,就是一个block ram。
从逻辑上来说,这个模块是对软件程序员是透明的,寄存器堆和指令集是软件和硬件的交互接口。
4>stack
stack(栈),是用来处理分支指令时,存放PC的值的,比如,我们在处理子程序调用时,需要先将当前的PC+1压栈,等到遇到子程序返回指令时使用。
栈的特点是LIFO(last in first out),这一点与heap(堆)不同。
5>ctrl_path模块
控制通路负责将genpc模块产生的地址处的指令进行解码,并产生对应的操作数,操作码和控制型号。这部分信号比较多一点。
6>tiny_soc
为了测试这个cpu内核,我们需要搭一个最小系统,包括指令只读存储器insn_rom模块,里面存放机器码。
由于是harvard结构,所以还需要一个数据存储器ram模块,相当于内存。
当然,如果想外挂其他I/O外设,我们只需要定义其地址空间即可,需要说明的是I/O外设的地址空间不能与RAM重叠,各个I/O外设之间也不能重叠。
RAM和I/O外设之间可通过一个arbiter与cpu core实现数据交互。
当然,如果存放指令的地方不止一个,也需要一个指令arbiter。
4,RTL实现
在完成模块划分,接口定义,仔细分析考虑模块间时序之后,如果没有什么问题,我们就可以进行编码工作了。
编码,需要注意的是编码一定要规范,信号命名,代码注释什么的,尽量要仔细。这里直接给出RTL代码(verilog HDL)
按照自上而下的顺序依次给出:
1>tiny_soc顶层模块:soc_top
/*
*
* file name : soc_top.v
* author : Rill
* date : 2013-08-11
*
*/`timescale 1ns / 1psmodule soc_top
(
input clk,
input rst
);wire read_e;
wire write_e;
wire [7:0] port_addr;
wire [7:0] core_in;
wire [7:0] core_out;
wire [15:0] instruction;
wire [10:0] inst_addr;wire [15:0] debug_insn;
wire [10:0] debug_pc;insn_rom insn_rom
(
.clk (clk),
.rst (rst),
.address (inst_addr),
.instruction (instruction)
);core core
(
.clk (clk),
.rst (rst),.read_e (read_e),
.write_e (write_e),
.port_addr (port_addr),
.data_in (core_in),
.data_out (core_out),
.inst_addr (inst_addr),
.instruction (instruction),
.debug_pc (debug_pc),
.debug_insn (debug_insn)
);ram ram
(
.clk (clk),
.rst (rst),.wr (write_e),
.rd (read_e),
.addr (port_addr),
.din (core_out),
.dout(core_in)
);endmodule
2>指令存储器:insn_rom
/*
*
* file name : insn_rom.v
* author : Rill
* date : 2013-08-11
*
*/module insn_rom
(
input clk,
input rst,
input [10:0] address,
output reg [15:0] instruction
);//(* RAM_STYLE="BLOCK" *)
reg [15:0] rom [2047:0];always @(posedge clk)
beginif(rst)beginrom[0] <= 16'h5801;//0: jmp startrom[1] <= 16'h1101;//1:start mov r1,1rom[2] <= 16'h1202;//2: mov r2,2rom[3] <= 16'h3220;//3: add r2,r1rom[4] <= 16'h2237;//4: str r2,55rom[5] <= 16'h5806;//5: jmp endrom[6] <= 16'h5806;//6:end jmp end*/endelsebegininstruction <= rom[address];end
endendmodule
3>数据存储器:ram
*
*
* file name : ram.v
* author : Rill
* date : 2013-08-11
*
*/module ram(input clk,input rst,input [7:0] din,input [7:0] addr,output reg [7:0] dout,input wr,input rd);(* RAM_STYLE="DISTRIBUTED" *)reg [7:0] ram [255:0];always @(posedge clk)beginif(rst)begindout <= 8'b0;ram[0] = 0;ram[1] = 1;ram[2] = 2;ram[32] = 32;ram[64] = 64;endelsebeginif (wr)ram[addr] <= din;else if(rd)dout <= ram[addr];endendendmodule
4>CPU核心:core
/*
*
* file name : core.v
* author : Rill
* date : 2013-08-11
*
*/module core
(input clk,input rst,output [7:0] port_addr,output read_e,output write_e,input [7:0] data_in,output [7:0] data_out,output [10:0] inst_addr,input [15:0] instruction,output [10:0] debug_pc,//debug i/foutput [15:0] debug_insn
);wire z,c;
wire insel;
wire we;
wire [2:0] raa;
wire [2:0] rab;
wire [2:0] wa;
wire [2:0] opalu;
wire [2:0] sh;
wire selpc;
wire ldpc;
wire ldflag;
wire [10:0] ninst_addr;
wire selk;
wire [7:0] KTE;
wire [10:0] stack_addr;
wire wr_en, rd_en;
wire [7:0] imm;
wire selimm;control_path control_path
(
.clk (clk),
.rst (rst),
.instruction (instruction),
.z (z),
.c (c),
.port_addr (port_addr),
.write_e (write_e),
.read_e (read_e),
.insel (insel),
.we (we),
.raa (raa),
.rab (rab),
.wa (wa),
.opalu (opalu),
.sh (sh),
.selpc (selpc),
.ldpc (ldpc),
.ldflag (ldflag),
.naddress (ninst_addr),
.selk (selk),
.KTE (KTE),
.stack_addr (stack_addr),
.wr_en (wr_en),
.rd_en (rd_en),
.imm (imm),
.selimm (selimm)
);data_path data_path_i
(
.clk (clk),
.rst (rst),
.data_in (data_in),
.insel (insel),
.we (we),
.raa (raa),
.rab (rab),
.wa (wa),
.opalu (opalu),
.sh (sh),
.selpc (selpc),
.selk (selk),
.ldpc (ldpc),
.ldflag (ldflag),
.wr_en (wr_en),
.rd_en (rd_en),
.ninst_addr (ninst_addr),
.kte (KTE),
.imm (imm),
.selimm (selimm),
.data_out (data_out),
.inst_addr (inst_addr),
.stack_addr (stack_addr),
.z (z),
.c (c)
);debug debug
(
.pc_in (inst_addr),
.insn_in (instruction),.pc (debug_pc),
.insn (debug_insn)
);endmodule
5>调试单元:debug
/*
*
* file name : debug.v
* author : Rill
* date : 2013-08-11
*
*/module debug
(
input [10:0] pc_in,
input [15:0] insn_in,output [10:0] pc,
output [15:0] insn
);assign pc = pc_in;
assign insn = insn_in;endmodule
6>控制通路:control_path
/*
*
* file name : control_path.v
* author : Rill
* date : 2013-08-11
*
*/module control_path
(input clk,input rst,input [15:0] instruction,input z,input c,output reg [7:0] port_addr,output reg write_e,output reg read_e,output reg insel,output reg we,output reg [2:0] raa,output reg [2:0] rab,output reg [2:0] wa,output reg [2:0] opalu,output reg [2:0] sh,output reg selpc,output reg ldpc,output reg ldflag,output reg [10:0] naddress,output reg selk,output reg [7:0] KTE,input [10:0] stack_addr,output reg wr_en, rd_en,output reg [7:0] imm,output reg selimm);parameter fetch= 5'd0;
parameter decode= 5'd1;parameter ldi= 5'd2;
parameter ldm= 5'd3;
parameter stm= 5'd4;
parameter cmp= 5'd5;
parameter add= 5'd6;
parameter sub= 5'd7;
parameter andi= 5'd8;
parameter oor= 5'd9;
parameter xori= 5'd10;
parameter jmp= 5'd11;
parameter jpz= 5'd12;
parameter jnz= 5'd13;
parameter jpc= 5'd14;
parameter jnc= 5'd15;
parameter csr= 5'd16;
parameter ret= 5'd17;parameter adi= 5'd18;
parameter csz= 5'd19;
parameter cnz= 5'd20;
parameter csc= 5'd21;
parameter cnc= 5'd22;
parameter sl0= 5'd23;
parameter sl1= 5'd24;
parameter sr0= 5'd25;
parameter sr1= 5'd26;
parameter rrl= 5'd27;
parameter rrr= 5'd28;
parameter noti= 5'd29;parameter nop= 5'd30;wire [4:0] opcode;
reg [4:0] state;assign opcode=instruction[15:11];always@(posedge clk or posedge rst)
beginif (rst)beginstate<=decode;endelsebegincase (state)fetch: beginstate<=decode;enddecode: beginif(opcode >=ldi && opcode <=nop)state <= opcode;//state just is the opcode nowelsestate <= nop;enddefault:state<=fetch;endcaseendend always@(*)
beginport_addr<=0;write_e<=0;read_e<=0;insel<=0;we<=0;raa<=0;rab<=0;wa<=0;opalu<=4;sh<=4;selpc<=0;ldpc<=1;ldflag<=0;naddress<=0;selk<=0;KTE<=0;wr_en<=0;rd_en<=0;imm<=0;selimm<=0;case (state)fetch: beginldpc<=0;enddecode: beginldpc<=0;if (opcode==stm)beginraa<=instruction[10:8];port_addr<=instruction[7:0];endelse if (opcode==ldm)beginwa<=instruction[10:8];port_addr<=instruction[7:0];endelse if (opcode==ret)beginrd_en<=1;endendldi: beginselk<=1;KTE<=instruction[7:0];we<=1;wa<=instruction[10:8];endldm: beginwa<=instruction[10:8];we<=1;read_e<=1;port_addr<=instruction[7:0];endstm: beginraa<=instruction[10:8];write_e<=1;port_addr<=instruction[7:0];endcmp: beginldflag<=1;raa<=instruction[10:8];rab<=instruction[7:5];opalu<=6;endadd: beginraa<=instruction[10:8];rab<=instruction[7:5];wa<=instruction[10:8];insel<=1;opalu<=5;we<=1;endsub: beginraa<=instruction[10:8];rab<=instruction[7:5];wa<=instruction[10:8];insel<=1;opalu<=6;we<=1;endandi: beginraa<=instruction[10:8];rab<=instruction[7:5];wa<=instruction[10:8];insel<=1;opalu<=1;we<=1;endoor: beginraa<=instruction[10:8];rab<=instruction[7:5];wa<=instruction[10:8];insel<=1;opalu<=3;we<=1;endxori: beginraa<=instruction[10:8];rab<=instruction[7:5];wa<=instruction[10:8];insel<=1;opalu<=2;we<=1;endjmp: beginnaddress<=instruction[10:0];selpc<=1;ldpc<=1;endjpz: if (z)beginnaddress<=instruction[10:0];selpc<=1;ldpc<=1;endjnz: if (!z)beginnaddress<=instruction[10:0];selpc<=1;ldpc<=1;endjpc: if (c)beginnaddress<=instruction[10:0];selpc<=1;ldpc<=1;endjnc: if (!c)beginnaddress<=instruction[10:0];selpc<=1;ldpc<=1;endcsr: beginnaddress<=instruction[10:0];selpc<=1;ldpc<=1;wr_en<=1;endret: beginnaddress<=stack_addr;selpc<=1;ldpc<=1;endadi: beginraa<=instruction[10:8];wa<=instruction[10:8];imm<=instruction[7:0];selimm<=1;insel<=1;opalu<=5;we<=1;end csz: if (z)beginnaddress<=instruction[10:0];selpc<=1;ldpc<=1;wr_en<=1;endcnz: if (!z)beginnaddress<=instruction[10:0];selpc<=1;ldpc<=1;wr_en<=1;endcsc: if (c)beginnaddress<=instruction[10:0];selpc<=1;ldpc<=1;wr_en<=1;endcnc: if (!c)beginnaddress<=instruction[10:0];selpc<=1;ldpc<=1;wr_en<=1;endsl0: begin raa<=instruction[10:8];wa<=instruction[10:8];insel<=1;sh<=0;we<=1;endsl1: begin raa<=instruction[10:8];wa<=instruction[10:8];insel<=1;sh<=5;we<=1;endsr0: begin raa<=instruction[10:8];wa<=instruction[10:8];insel<=1;sh<=2;we<=1;endsr1: begin raa<=instruction[10:8];wa<=instruction[10:8];insel<=1;sh<=6;we<=1;end rrl: begin raa<=instruction[10:8];wa<=instruction[10:8];insel<=1;sh<=1;we<=1;end rrr: begin raa<=instruction[10:8];wa<=instruction[10:8];insel<=1;sh<=3;we<=1;endnoti: beginraa<=instruction[10:8];wa<=instruction[10:8];insel<=1;opalu<=0;we<=1;endnop: beginopalu<=4;end endcase
endendmodule
7>数据通路:data_path
/*
*
* file name : data_path.v
* author : Rill
* date : 2013-08-11
*
*/module data_path
(input clk,input rst,input [7:0] data_in,input insel,input we,input [2:0] raa,input [2:0] rab,input [2:0] wa,input [2:0] opalu,input [2:0] sh,input selpc,input selk,input ldpc,input ldflag,input wr_en, rd_en,input [10:0] ninst_addr,input [7:0] kte,input [7:0] imm,input selimm,output [7:0] data_out,output [10:0] inst_addr,output [10:0] stack_addr,output z,c
);wire [7:0] regmux, muximm;
wire [7:0] portA, portB;wire [7:0] shiftout;assign data_out=shiftout;genpc genpc
(
.clk (clk),
.rst (rst),.ldpc (ldpc),
.selpc (selpc),
.ninst_addr (ninst_addr),.inst_addr (inst_addr)
);alu_mux alu_mux
(
.selimm (selimm),
.imm (imm),
.portB (portB),.muximm (muximm)
);alu alu
(
.a (portA),
.b (muximm),
.opalu (opalu),
.ldflag (ldflag),
.zero (z),
.carry (c),
.sh (sh),
.dshift (shiftout)
);stack stack
(
.clk (clk),
.rst (rst),
.wr_en (wr_en),
.rd_en (rd_en),
.din (inst_addr),
.dout (stack_addr)
);regfile_mux regfile_mux
(
.insel (insel),
.selk (selk),
.shiftout (shiftout),
.kte (kte),
.data_in (data_in),.regmux (regmux)
);regfile regfile
(
.datain (regmux),
.clk (clk),
.we (we),
.wa (wa),
.raa (raa),
.rab (rab),
.porta (portA),
.portb (portB)
);endmodule
8>程序计算器:genpc
/*
*
* file name : genpc.v
* author : Rill
* date : 2013-08-11
*
*/`define boot_addr 0 //boot address after power onmodule genpc
(
input clk,
input rst,input ldpc,
input selpc,
input [10:0] ninst_addr,output [10:0] inst_addr
);reg [10:0] pc;assign inst_addr=pc;always@(posedge clk or posedge rst)
beginif (rst)pc <=`boot_addr;elseif (ldpc) if(selpc)pc<=ninst_addr;elsepc<=pc+1;
endendmodule
9>运算单元:alu ,alu_mux
/*
*
* file name : alu_mux.v
* author : Rill
* date : 2013-08-11
*
*/module alu_mux
(
input selimm,
input [7:0] imm,
input [7:0] portB,output [7:0] muximm
);assign muximm = selimm? imm : portB;//result : imm if ldi insn,portb if ldm insnendmodule
/*
*
* file name : alu.v
* author : Rill
* date : 2013-08-11
*
*/module alu
(
input [7:0] a,
input [7:0] b,
input [2:0] opalu,
input ldflag,
output zero,
output carry,
input [2:0] sh,
output reg [7:0] dshift
);reg [7:0] resu;assign zero=ldflag?(resu==0):1'b0;assign carry=ldflag?(a<b):1'b0;always@(*)case (opalu)0: resu <= ~a;1: resu <= a & b;2: resu <= a ^ b;3: resu <= a | b;4: resu <= a;5: resu <= a + b;6: resu <= a - b;default: resu <= a + 1;endcasealways@*case (sh)0: dshift <= {resu[6:0], 1'b0};1: dshift <= {resu[6:0], resu[7]};2: dshift <= {1'b0, resu[7:1]};3: dshift <= {resu[0], resu[7:1]};4: dshift <= resu;5: dshift <= {resu[6:0], 1'b1};6: dshift <= {1'b1, resu[7:1]};default: dshift <= resu;endcaseendmodule
10>寄存器堆:regfile,regfile_mux
/*
*
* file name : regfile_mux.v
* author : Rill
* date : 2013-08-11
*
*/module regfile_mux
(
input insel,
input selk,
input [7:0] shiftout,
input [7:0] kte,
input [7:0] data_in,output [7:0] regmux
);wire [7:0] muxkte;assign regmux=insel? shiftout : muxkte;
assign muxkte=selk? kte : data_in;endmodule
/*
*
* file name : regfile.v
* author : Rill
* date : 2013-08-11
*
*/module regfile(input [7:0] datain,input clk, we,input [2:0] wa,input [2:0] raa,input [2:0] rab,output [7:0] porta,output [7:0] portb);reg [7:0] mem [7:0];//r0 ~r255always@(posedge clk)
beginmem[0]<=0;//r0 always is 0if(we)mem[wa]<=datain;
end assign porta=mem[raa];
assign portb=mem[rab];endmodule
11>栈:stack
/*
*
* file name : stack.v
* author : Rill
* date : 2013-08-11
*
*/module stack(input clk,input rst,input wr_en,input rd_en,input [10:0] din,output [10:0] dout);(* RAM_STYLE="DISTRIBUTED" *)
reg [3:0] addr;
reg [10:0] ram [15:0];assign dout = ram[addr] +1;always@(posedge clk)
beginif (rst)addr<=0;else begin if (wr_en==0 && rd_en==1) //leerif (addr>0)addr<=addr-1;if (wr_en==1 && rd_en==0) //guardarif (addr<15)addr<=addr+1;end
endalways @(posedge clk)
beginif (wr_en)ram[addr] <= din;
endendmodule
5,modelsim仿真
1>编写testbench
要进行仿真,需要编写对应的testbench,由于咱们这个cpu很简单,所以测试激励也很简单,代码如下:
/*
*
* file name : tiny_soc_tb.v
* atthor : Rill
* date : 2013-08-11
*
*/`timescale 1ns / 1psmodule tiny_soc_tb;reg clk;
reg rst;always #5 clk = ~clk;initial
begin#0 clk = 0;rst = 0;#15 rst = 1;#10 rst = 0;#1000$stop;
endsoc_top soc_top
(
.clk (clk),
.rst (rst)
);endmodule
2>编写汇编代码及手动汇编
当然还要编写其汇编代码,如下:
然后我们要手动汇编成机器码,指令集都是自己定义的,所以是没有现成的compiler,只能手动汇编了,还好手动汇编要比手动反汇编轻松多了(之前,我们手动反汇编过OpenRISC的启动代码)。
汇编完成后,我们将机器码放到指令存储器里,如下,共七条指令。
3>仿真结果
完成上面的工作之后,我们就可以用仿真工具进行仿真了,下面是我用modelsim仿真的结果。
从波形可以清晰的看出七条指令的执行过程。在运算完成后拉高write_e信号并将1+2的运算结果3写到了ram地址是55(0x37)的地方。
4>代码仿真覆盖率
由于这个测试汇编代码很短,我们有必要了解一下这个仿真激励运行过程中到底测试了多少代码,下面是code coverage报告截图。
需要说明的是上面的截图并不完整,下面是完整的代码覆盖率报告。
Coverage Report Summary Data by fileFile: E:/work/tiny_soc/bench/tiny_soc_tb.vEnabled Coverage Active Hits % Covered---------------- ------ ---- ---------Stmts 11 11 100.0Branches 0 0 100.0Conditions 0 0 100.0Fec Conditions 0 0 100.0Expressions 0 0 100.0Fec Expressions 0 0 100.0File: E:/work/tiny_soc/rtl/core/alu.vEnabled Coverage Active Hits % Covered---------------- ------ ---- ---------Stmts 20 7 35.0Branches 16 3 18.7Conditions 0 0 100.0Fec Conditions 0 0 100.0Expressions 8 2 25.0Fec Expressions 8 2 25.0File: E:/work/tiny_soc/rtl/core/alu_mux.vEnabled Coverage Active Hits % Covered---------------- ------ ---- ---------Stmts 1 1 100.0Branches 2 1 50.0Conditions 0 0 100.0Fec Conditions 0 0 100.0Expressions 0 0 100.0Fec Expressions 0 0 100.0File: E:/work/tiny_soc/rtl/core/control_path.vEnabled Coverage Active Hits % Covered---------------- ------ ---- ---------Stmts 160 48 30.0Branches 60 18 30.0Conditions 3 1 33.3Fec Conditions 4 2 50.0Expressions 0 0 100.0Fec Expressions 0 0 100.0File: E:/work/tiny_soc/rtl/core/genpc.vEnabled Coverage Active Hits % Covered---------------- ------ ---- ---------Stmts 4 4 100.0Branches 6 6 100.0Conditions 0 0 100.0Fec Conditions 0 0 100.0Expressions 0 0 100.0Fec Expressions 0 0 100.0File: E:/work/tiny_soc/rtl/core/regfile.vEnabled Coverage Active Hits % Covered---------------- ------ ---- ---------Stmts 4 4 100.0Branches 2 2 100.0Conditions 0 0 100.0Fec Conditions 0 0 100.0Expressions 0 0 100.0Fec Expressions 0 0 100.0File: E:/work/tiny_soc/rtl/core/regfile_mux.vEnabled Coverage Active Hits % Covered---------------- ------ ---- ---------Stmts 1 1 100.0Branches 4 4 100.0Conditions 0 0 100.0Fec Conditions 0 0 100.0Expressions 0 0 100.0Fec Expressions 0 0 100.0File: E:/work/tiny_soc/rtl/core/stack.vEnabled Coverage Active Hits % Covered---------------- ------ ---- ---------Stmts 7 4 57.1Branches 12 5 41.6Conditions 6 2 33.3Fec Conditions 8 2 25.0Expressions 0 0 100.0Fec Expressions 0 0 100.0File: E:/work/tiny_soc/rtl/insn_rom.vEnabled Coverage Active Hits % Covered---------------- ------ ---- ---------Stmts 9 9 100.0Branches 2 2 100.0Conditions 0 0 100.0Fec Conditions 0 0 100.0Expressions 0 0 100.0Fec Expressions 0 0 100.0File: E:/work/tiny_soc/rtl/ram.vEnabled Coverage Active Hits % Covered---------------- ------ ---- ---------Stmts 9 8 88.8Branches 6 5 83.3Conditions 0 0 100.0Fec Conditions 0 0 100.0Expressions 0 0 100.0Fec Expressions 0 0 100.0
6,综合
完成了仿真之后,我们就可以综合一下,在FPGA上验证了,下面是我用quartusII 12.0综合的结果。
soc_top模块:
core模块:
data_path模块:
7,改进及优化
咱们设计现在这个小cpu core的目的是为了学习,在于了解cpu内部的运行机制,与软件的交互过程,没有考虑性能,功耗,面积等问题。
下面,我给出了采用五级流水的数据通路的模块划分和接口定义,如下所示,感兴趣的兄弟可以试着实现她。
8,future work
1,pipeline,将core流水化
2,增加乘法,除法指令。咱们前面介绍过4-bit乘法器和除法器的设计:http://blog.csdn.net/rill_zhen/article/details/9700155
3,fix bug
4,设计tiny_core的assembler,这样就不用手动汇编了。
9,小结
本小节,我们试着设计了一个简单的cpu,并编写了可综合的外部基本模块和不可综合的测试激励模块,然后用quartusII进行了综合,最后还给出了优化后的数据通路结构。
我想,通过本小节的内容,应该对cpu的工作机制和软件与硬件的关系有了更深入的感觉了。
需要说明的是,我的水平有限,再加上时间很短(两周),所以这里面肯定还有很多问题,和需要改进的地方,如果感兴趣,可以把代码down下来,先用modelsim仿真一下,然后再写个I/O controller,比如uart的控制器,挂到数据总线上,如果PC机能收到tiny_soc发出的“hello world”,那是多么令人激动的一件事情啊。
附录:tiny_core的指令集
说明:
rrr:代表寄存器。
aaa和bbb分别代表寄存器A和寄存器B,其中寄存器A是目的寄存器。
iiiiiiii:代表8-bit的立即数。
xxx:代表忽略。
ttt:11 bit的跳转/分支地址。
此外,这个指令集其实很早就开始弄了,最早是去年上半年开始的,现在是最新版本。
数字集成电路设计-7-一个简单cpu的设计,实现,仿真与综合相关推荐
- 数字集成电路设计-1-用一个mux和一个inv实现异或
引言 今天遇到一个问题,如题,想了十来分钟都没有想出来,现在想出来了,觉得挺有意思. 1.1 verilog实现 module xor_rill ( input a, input b,output z ...
- 数字集成电路设计(一、Verilog HDL数字集成电路设计方法概述)
文章目录 集成电路发展 HDL产生 HDL分类 Verilog HDL的发展 Verilog HDL与VHDL Verilog HDL在数字集成电路设计中的优点 组合逻辑电路原理图设计和Verilog ...
- 数字集成电路设计(六、Verilog HDL高级程序设计举例)
文章目录 1. 数字电路系统设计的层次化描述方式 1.1 Bottom-Up 设计方法 1.2 Top-Down 设计方法 2. 典型电路设计 2.1 加法器树乘法器 2.1.1 改进为两级流水线4位 ...
- 数字集成电路设计-12-状态机的四种写法
引言 在实际的数字电路设计中,状态机是最常用的逻辑,而且往往是全部逻辑的核心部分,所以状态机的质量,会在比较大的程度上影响整个电路的质量. 本小节我们通过一个简单的例子(三进制脉动计数器)来说明一下状 ...
- 【笔记】 数字集成电路设计(一)
[笔记] 数字集成电路设计 书籍:<数字集成电路--电路.系统与设计>(第二版) 第一章 引论 1. 数字设计中需解决的问题 摩尔定律 技术突破才能推动摩尔定律 特征尺寸 28nm是传统制 ...
- 入门数字集成电路设计系列(一)——Modelsim安装及破解过程记录
入门数字集成电路设计系列(一) #Modelsim破解教程(记录) Modelsim是由 Mentor Graphics 公司开发的软件,它是世界最优秀的Verilog HDL语言仿真工具,是FPGA ...
- 数字集成电路设计简介及设计方法
简介 数字集成电路(Digital Integrated Circuits,DIC)是一种能够处理数字信号的电路.它由多个数字逻辑电路元件组成,包括逻辑门.寄存器.计数器.加法器.乘法器等.数字集成电 ...
- 数字集成电路设计的流程1
数字集成电路设计的流程 芯片验证属于前端 每个阶段的工作 使用的EDA工具,Linux系统(lincense) 三家软件公司,Mentor(questasim).Synopsys(VCS).Caden ...
- VerilogHDL(1)数字集成电路设计方法概述
一.数字集成电路设计方法概述 2.什么是硬件描述语言,其主要的作用是什么? 硬件描述语言是一种用形式化方式来描述数字电路和系统的语言. 它的主要作用是:数字电路系统的设计者利用这种语言可以从上层到下层 ...
最新文章
- 后深度学习时代的一大研究热点?论因果关系及其构建思路
- RDKit | 基于机器学习的化合物活性预测模型
- Jmeter使用笔记之意料之外的
- 集成Swagger(API)---SpringBoot
- 巧用“傍术”选择陈列点
- mysql php示例,PHP操作MYSQL简单示例
- centos mysql lujin_MySQL中文转换成拼音的函数[zt]
- 看了这份《算法中文手册》笔记,就再也不怕字节了~
- eureka多了一个莫名其妙的服务_这些手游服务器全部飘红,每一个服务器都人多到爆满...
- 工作中不要扯虎皮吓唬人
- 移动通信发展史与5G技术的探索
- the little schemer 学习
- Distributed System: DFS系列 -- NFS, AFS HDFS(GFS)
- java配置dsf,基于Spring-DM实现分布式服务框架(DSF)(一)
- 18 余数相同问题 c语言,2018年国考行测备考之简单两招教你搞定余数问题
- 如何利用ArcGIS探究环境与生态因子对水体、土壤、大气污染物等?
- 设置VLC播放器进行RTSP推流桌面(共享桌面)
- Scrapy 爬虫 官方 中文文档 开发手册
- Eclipse导入epf文件,一键更改背景及代码颜色。
- Google Earth Engine(GEE)——一个免费下载Landsat影像的APP
热门文章
- mysql中新建数据库create table的COLLATE是什么?
- 我的世界基岩版种子和java版种子_我的世界:值得珍藏3个种子,号称MC最“完美”的三座山脉!...
- Google gflags使用说明
- 表示自己从头开始的句子_形容从头开始的句子
- openssh8.6默认不支持公钥ssh(gogs无法使用)
- Fiddler抓包工具如何设置过滤域名
- 代刷网html统计代码,【QQ代刷网】前台代码大全
- java中stu是啥,Java 面试:对象克隆
- Arduino ESP8266固件bin升级四种方法
- OpenCV 学习笔记(Watershed)