初接触到CPU(处理器)的实现,有很多东西需要先学习一下,才能了解其中的原理,更好地实现它。首先,你需要深入了解MIPS指令集,理解其各个指令的执行过程;其次,你需要掌握Verilog语言的使用,理解模块化思想,有点类似C++,不是很难。
首先,我们先开始学习之旅。简单地学习,是为了后面更流畅地动作。
▶MIPS32指令集架构简介
学习链接:http://blog.csdn.net/leishangwen/article/details/37738343
学习重点:
- 数据类型
- 寄存器
- 指令格式
- 指令集
▶了解PLD与HDL
学习链接:http://blog.csdn.net/leishangwen/article/details/37760199
学习重点:
- PLD 可编程逻辑器件
- HDL 硬件描述语言(与PLD关系)
▶Verilog HDL语言的学习
学习链接:http://blog.csdn.net/leishangwen/article/details/37814749
学习重点:
- Module介绍
- 基本要素:常量、 变量声明与数据类型、 运算符
笔记:
net型变量之wire
wire是最常用的net型变量,Verilog HDL模块中的输入、输出信号在没有明确指定数据类型时,都被默认为wire型。wire型信号可以用作任何表达式的输入,也可以用作assign语句和实例元件的输出,如前文中的32位加法器对out信号的赋值。对于综合器而言,wire型变量的取值可为0、1、X、Z,其中0表示低电平、逻辑0;1表示高电平、逻辑1;X表示不确定或未知的逻辑状态;Z表示高阻态。如果没有赋值,默认为高阻态Z。variable型变量
variable型变量是可以保存上次写入数据的数据类型,一般对应硬件上的一个触发器或锁存器等存储元件,但并不是绝对的,在综合器综合的时候,会根据其被赋值的情况来具体确定是映射成连线还是映射为存储元件。
variable型变量必须在过程语句(initial或always)中实现赋值,这种赋值方式称为过程赋值
常用类型为reg类型。
▶补充知识点
Verilog语句中的assign、initial和always的区别于使用
与C语言不通是,在Verilog module中的所有过程块(如initial块和always块)、连续赋值语句(如assign语句)都是并行的。在同一module中这三者出现的先后顺序与执行顺序没有关系。
这三种语句常被用来赋值。当用于赋值时,有很大的区别。
assign相当于连线,一般是将一个变量的值不间断地赋值给另一个变量,就像把这两个变量连在一起,所以习惯性的当做连线用,比如把一个模块的输出给另一个模块当输入。 assign的功能属于组合逻辑的范畴,应用范围可概括为以下三点:
(1)持续赋值;
(2)连线;
(3)对wire型变量赋值,wire是线网,相当于实际的连接线,如果要用assign直接连接,就用wire型变量。wire型变量的值随时变化。
其实以上三点是相通的。
//eg:
wire[8:0] reg; //reg占8bit
assign reg = 8'b01010011; //赋值
assign reg = 8'hC4; //赋值 十六进制,只需要两位就够了
assign可以独立于过程块而存在于module的功能定义部分,即不能在always过程块中使用assign语句。
initial和always属于过程赋值语句,常用于给variable型变量赋值。
其中,initial块从仿真0时刻开始执行,在整个仿真过程中只执行一次;initial块从仿真0时刻开始执行,在整个仿真过程中只执行一次。如果一个模块中包括了若干个initial块,则这些initial块从仿真0时刻开始并发执行,且每个块的执行是各自独立的。如果在块内包含了多条行为语句,那么需要将这些语句组成一组,一般式使用关键字begin和end将他们组合在一个块语句;如果块内只有一条语句,则不必使用begin和end。例如:
`tiemscale 1ns / 1ps
module example(
input wire data1,
input wire data2,
output reg out1,
output reg out2
);
reg test;
initial begin //需要使用begin和end语句
out1 = data1;
out2 = data2;
end
inital
#50 test = out1 + out2;//#50,延时50ns再执行(具体下面会解释)
initial //不需要使用begin和end语句
test = out1 - out2;
endmodule
三个语句块同时在0时刻开始执行,但是由于有延时,所以执行结果如下:
时间 执行语句
0 out1 = data1;
0 out2 = data2;
0 test = out1 - out2;
50 test = out1 + out2;
always语句包括的所有行为语句构成了一个always语句块。该always语句块从仿真0时刻开始执行其中的行为语句;最后一条执行完成后,再开始执行其中的第一条语句,如此往复循环,直到整个仿真结束。因此,always语句块常用于对数字电路中一组反复执行的活动进行建模。以一个例子作为说明:
module(
input wire data1,
input num;
output data
);
always(posedge clk) begin
if(num > 1) data = data1 + 1;
else data = data1 - 1;
end
endmodule
对于以上程序,主要用了下面的模型:
always(敏感信息)begin
//your expression
end
clk = 1时,就会触发always语句块的执行(posedge代表上升沿)。直到整个程序退出运行为止。
always的使用方法类似于initial,只不过多了一个循环执行的属性。
模块的定义和引用及宏定义
例如,我定义一个名叫example的模块,并在test中引用它:
example.v
module example(
input wire[5:0] data1,
output reg[5:0] data2
);
integer i;
always(posedge clk) begin
for(i = 0; i < data1; i++)
data2 = data1 + 1;
end
endmodule
test.v
`include "example.v" //不在同一个文件夹下,需要注明路径
module test; //无参数传入时,可以这样写
wire[5:0] Mydata1;
wire Mydata2;
assign Mydata1 = 1'b1;
//方法一
example exa(Mydata1, Mydata2);
/**
*参数需要严格对应。即Mydata1要想赋值给data1,只能放在第一个位置
**/
//方法二
example exa(
.data1(Mydata1),//data1来自example.v,Mydata1则来自test.v
.data2(Mydata2)
);
/**
*参数不需要严格对应。
*即Mydata1要想赋值给data1,可以放在任意位置。所以,该方法还可以写成:
*example exa(
* .data2(Mydata2),
* .data1(Mydata1)
* );
*另外,参数数量也可以不严格对应。
**/
endmudule
需要注意的是,“ ` ”符号,需要在英文状态下,按下Tab建上方对应的键,即标有“ ~ ’ ”这两个符号的键;“ ’ ”符号则是英文状态下,按下双引号符号键即可。前者用于头文件引用,后者用于定义常量。
关于宏定义的使用,也举个例子:
`define Add 6'b000000
module test;
wire[6:0] tmp;
assign tmp = `Add;
endmodule
注意定义和引用方式。
关于时间
大家在使用Verilog语言时,一定都见过下面的句子:
`timescale 1ns/1ps
这个句子是什么意思呢?先看几个例子:
`timescale 1ns / 100ps
#50 clk = ~clk; //50ns,时钟信号改变一次。即由1变为0,或者由0变为1
#50.12 clk ~clk; //50.1ns,时钟信号改变一次
#50.1 clk = ~ckl; //50.1ns,时钟信号改变一次
由此可见,它的含义是:时间单位 / 单位精度。
学习就学习到这里了,接下来,我们就可以尝试去实现CPU了。