首页 > 其他分享 >cpu设计和实现(取指)

cpu设计和实现(取指)

时间:2022-11-23 15:03:08浏览次数:66  
标签:clk 取指 ce pc inst rst 设计 cpu out


        cpu设计的本质是数字电路的设计。要是没有verilog、vhdl这些语言,那么剩下来使用的方法基本只有卡诺图这一种了。在数字电路中,有两种基本的电路,一种是逻辑电路,一种是时序电路。两者的区别在于,逻辑电路随着输入而变化,而时序电路只有在时钟边沿触发的时候才能变化,其他时刻都会保持这一电路。

        听上去或许有一点拗口,我们可以通过一个示例代码来进行说明。假设有这么一个文件test.v,

module test(clk, rst, in, out_a, out_b);

input wire clk;
input wire rst;
input wire in;

output wire out_a;
output reg out_b;

assign out_a = in;

always@(posedge clk or posedge rst) begin
if(rst)
out_b <= 1'b0;
else
out_b <= in;
end

endmodule

        从上面的代码可以看出来,有5个信号,3个是输入信号,2个是输出信号。输入信号中,一个时钟clk,一个复位rst,一个输入信号in。输出信号中,一个是组合逻辑输出out_a,一个时序逻辑输出out_b。对于时序逻辑out_b,可以看到在复位发生的时候,信号输出为0,其他时刻只有在时钟上升沿的时候,电路才会把信号in传递给out_b。

        为了测试test.v,我们需要编译一个激励模块test_tb.v,

`timescale 1ns/1ps
module test_tb();

reg clock;
reg rst;
reg in;
wire out_a;
wire out_b;

initial begin
clock = 1'b0;
forever #5 clock = ~clock;
end

initial begin
rst = 1'b0;
#1 rst = 1'b1;
#11 rst = 1'b0;
#1000 $stop;
end

initial begin
in = 1'b1;
#17 in = 1'b0;
#19 in = 1'b1;
#21 in = 1'b0;
end

test test0(
.clk(clock),
.rst(rst),
.in(in),
.out_a(out_a),
.out_b(out_b)
);

initial
begin
$dumpfile("hello.vcd");
$dumpvars(0, test_tb);
end


endmodule

        在激励模块中,我们定义了clk、rst、in,下面就观察out_a和out_b是如何变化的了。借助于iverilog和gtkwave工具,结果我们可以分析下,

cpu设计和实现(取指)_sed

        从上面的信号不难看出,out_a和in的信号是时刻保持一致的。而对于信号out_b而言,在复位这一段时间,一直保持在0的状态。等到复位结束之后,在第一个clk上升沿到来的时候,此时in处在1的状态,那么out_b翻转为1。接着,in又继续调整为0,但是此刻out_b具有记忆功能,并且只在clk上升沿到来的时候处理信号,所以out_b继续保持为1,直到下一个clk时刻发现in已经是0的情况下,才会跟随in调整为0。

        上面这段代码只是一个插曲。这段代码保存在github上,​​https://github.com/feixiaoxing/design_mips_cpu/tree/master/exercise​​。

        回归正题,今天讨论的主要是取指这个部分。顾名思义,那就应该有两个部分,一个是pc地址的生成,一个是rom指令的读取。本次代码主要参考了《自己动手写cpu》这本书,有兴趣的朋友可以找来看看。

        首先看一下pc_reg.v,

module pc_reg(

input wire clk,
input wire rst,
output reg[5:0] pc,
output reg ce
);



always @(posedge clk)
begin
if(rst == 1'b1) begin
ce <= 1'b0;
end else begin
ce <= 1'b1;
end
end


always @(posedge clk)
begin
if(ce == 1'b0) begin
pc <= 6'h00;
end else begin
pc <= pc + 1'b1;
end
end

endmodule

         代码中主要有pc和ce两个寄存器。pc当然是指令地址的意思,而ce则是chip enable的意思。从代码上看,pc只有ce不是0的时候,才会开始自增。而ce变成1,需要等到rst结束之后的第一个clk上升沿才会变成1,因此pc也会顺延一个clk,才会开始地址自增。

module rom (

input wire ce,
input wire[5:0] addr,
output reg[31:0] inst
);

reg[31:0] rom[63:0];
initial $readmemh ( "rom.data", rom );

always@(*)
if(ce == 1'b0) begin
inst <= 32'h0;
end else begin
inst <= rom[addr];
end

endmodule

        取指令这部分代码是组合逻辑的代码,这从always(*)可以看的出来。需要稍微注意的是其中initial的部分,在真实的asic芯片中,也会存在把一部分代码固化在chip上的情况。当然这里使用了readmemh读取指令文件,主要还是为了测试的方便。指令文件rom.data就是文本文件,

00000000
01010101
02020202
03030303
04040404
05050505

        准备好了pc_reg.v和rom.v之后,接下来就应该将二者组合在一起了,生成inst_fetch.v,

module inst_fetch(
input wire clk,
input wire rst,
output wire[31:0] inst_o
);

wire[5:0] pc;
wire rom_ce;

pc_reg pc_reg0(
.clk(clk),
.rst(rst),
.pc(pc),
.ce(rom_ce)
);

rom rom0(
.ce(rom_ce),
.addr(pc),
.inst(inst_o)
);

endmodule

        这部分的代码也不复杂,最主要的工作就是创建pc和rom_ce连线,这样可以将两个module示例串联在一起。当然,为了测试还需要编写一个激励模inst_fetch_tb.v,

`timescale 1ns/1ps
module inst_fetch_tb;

reg clock;
reg rst;
wire[31:0] inst;

initial begin
clock = 1'b0;
forever #10 clock = ~clock;
end

initial begin
rst = 1'b1;
#195 rst = 1'b0;
#1000 $stop;
end


initial
begin
$dumpfile("hello.vcd");
$dumpvars(0, inst_fetch_tb);
end

inst_fetch inst_fetch0(
.clk(clock),
.rst(rst),
.inst_o(inst)
);

endmodule

        激励模块中只要正常给出clk和rst即可,主要就是看输出的inst中,有没有和我们之前期望的一样,可以在inst寄存器当中出现rom.data保存的那些指令数据。如果有,则代表我们的设计是正确的。反之,则代表verilog代码还是有问题的。所有的这些文件都准备妥当之后,就可以用iverilog和gtkwave仿真测试了,

cpu设计和实现(取指)_上升沿_02

        从信号仿真来看,整个设计还是符合要求的。首先rst一直处于复位的时候,ce为0。等rst结束,ce在第一个clk上升沿的时候调整为1。ce调整为1后,pc在下一个clk上升沿的时候开始自增。此外,由于rom.v是一个组合逻辑,所以pc发生改变之后,inst立马就发生了变化。所以从一开始inst是0x00000000之后,随着pc的改变,指令也开始一个一个读进来了。

       

标签:clk,取指,ce,pc,inst,rst,设计,cpu,out
From: https://blog.51cto.com/feixiaoxing/5881302

相关文章

  • java和设计模式(行为模式)
        和构建模式、结构模式相比较,行为模式的内容要多一些。在设计模式中,行为模式强调的是类和对象之间的交互关系。它更多强调的是,在特定的行为场景种,使用哪一种设计......
  • java和设计模式(结构模式)
        在设计模式中,有一类设计模式是比较有意思的,但是关注的人不多。这就是结构模式。如果说创建模式的重点是如何创建出实例对象,那么结构模式的特点就是利用类、示例......
  • java和设计模式(创建模式)
        有过软件开发经验的同学都知道,软件开发功能怎么都能完成。但是如果需要在软件上面不断做需求变更和重构,这就变得好复杂了。对于这些变更,如果只是硬编码去解决,那......
  • 基于XQ6657Z35-EVM开发平台上TI TMS320C6657 TLV320AIC3206音频设计
    XQ6657Z35-EVM评估板是基于TI双核DSPTMS320C6657和XilinxZynqSoC处理器XC7Z035设计的多核异构平台,由核心板与底板架构组成。​SOM-XQ6657Z35核心板资源框图TMS320C6657......
  • C语言和设计模式(总结篇)
      设计模式的书相信很多人都看过。对于设计模式这样一种方法,相信不同的人有不同的理解。我在这里写的博客只是我个人对设计模式的粗浅认识。文中肯定存在很多的不足和不......
  • C语言和设计模式(之开篇)
      关于软件设计方面的书很多,比如《​​重构​​​》,比如《​​设计模式​​》。至于软件开发方式,那就更多了,什么极限编程、精益方法、敏捷方法。随着时间的推移,很多的......
  • 软件设计模式白话文系列(十四)策略模式
    1、模式描述定义一个算法的系列,将其各个分装,并且使他们有交互性。策略模式使得算法在用户使用的时候能独立的改变。在Java中,从JDK1.8开始支持函数式编程,就是策略模式......
  • 【数据库系统原理与设计】(五)关系数据理论与模式求精
    五. 关系数据理论与模式求精5.1问题提出 1. 数据冗余导致的问题: 冗余存储:信息被重复存储,导致浪费大量存储空间更新异常:当重复信息的一个副本被修改,所有副本都必......
  • 随想录(设计软件模块的接口)
       开发软件是一件复杂而且辛苦的工作,不同的模块之间的逻辑需要考虑,应用层与底层的关系也需要考虑。模块之间的关系处理不好,就会给软件的编写质量带来影响。当然不管软......
  • 七大设计原则
    单一职责最简单的,但是却是最难得高内聚,低耦合的延伸属性和行为向着模块预先定义的功能内聚模块的名字非常重要里氏代换原则鱼能游,鲨鱼能游,可以。反之∶鲨鱼有牙齿,......