自己动手写处理器之第二阶段(2)——Verilog HDL简介

将陆续上传本人写的新书《自己动手写处理器》(尚未出版),今天是第六篇,我尽量每周四篇

2.3 Verilog HDL简介

本书实现的OpenMIPS处理器是使用Verilog HDL编写的,所以本章接下来的几节将介绍Verilog HDL的一些基本知识,包括语法、结构等。因为本书并不是一本讲授Verilog HDL的专门书籍,所以此处介绍的内容并不是Verilog HDL的全部,只是一些基础知识,以及在OpenMIPS处理器实现过程中会使用到的知识。读者如果对Verilog HDL有进一步了解的需求,可以参考相关书籍,这方面有许多非常优秀的书籍。笔者推荐《数字系统设计与Verilog HDL(第4版)》,本章关于Verilog
HDL的介绍也部分参考了该书。

Verilog HDL由GDA(Gateway Design Automation)公司的Phil Moorby于1983年首创,之后,Moorby又设计了Verilog-XL仿真器,Verilog-XL仿真器大获成功,也使得Verilog HDL得到推广使用。1989年,Cadence收购了GDA,1990年,Cadence公开发布了Verilog HDL,并成立了OVI(Open Verilog International)组织,专门负责Verilog HDL的发展。由于Verilog
HDL具有简洁、高效、易用、功能强等优点,逐渐为众多设计者所接受和喜爱。其发展经历了几个关键节点。

  • 1995年,Verilog HDL成为IEEE标准,称为IEEE Standard 1364-1995(Verilog-1995)。
  • 2001年,IEEE Standard 1364-2001(Verilog-2001)获得通过,其对Verilog-1995做了扩充和增强,另外,修改了一些语法结构,使之更易于使用。
  • 2002年,为了使综合器输出的结果和基于IEEE Standard 1364-2001的仿真和分析工具的结果相一致,推出了IEEE 1364[1].1-2002标准,其对Verilog HDL的RTL级综合定义了一系列的建模准则。
  • 2005年,Verilog HDL再次进行了更新,即IEEE Standard 1364-2005(Verilog-2005)。该版本只是对上一版本的细微修正。这个版本还包括了一个相对独立的新部分,即Verilog-AMS (Analog and Mixed-Signal),这个扩展使得传统的Verilog HDL可以对集成模拟和混合信号的系统进行建模。

Verilog HDL具有下述特点。

(1)Verilog HDL是在C语言的基础上发展而来的,就语法结构而言,Verilog HDL继承了C语言的很多语法结构,两者有许多相似之处。

(2)既适于可综合的电路设计,也可胜任电路与系统的仿真。

(3)能在多个层次上对所设计的系统加以描述,从开关级、门级、寄存器传输级(RTL)到行为级,都可以胜任,同时Verilog HDL不对设计规模施加任何限制。

(4)灵活多样的电路描述风格,可进行行为描述,也可进行结构描述;支持混合建模,一个设计中的各个模块可以在不同的设计层次上建模和描述。

(5)内置多种基本逻辑门,如and、or和nand等,可方便的进行门级结构描述;内置多种开关级元件,如pmos、nmos和cmos等,可进行开关级的建模。

(6)用户定义原语(UDP)创建的灵活性。用户定义的原语既可以是组合逻辑,也可以是时序逻辑;通过编程语言接口(PLI)机制可进一步扩展Verilog HDL语言的描述能力。

2.4 Verilog HDL中模块的结构

Verilog程序的基本设计单元是“模块”(Module),一个模块有其特定的结构,如图2-6所示。Verilog的模块完全定义在module与endmodule关键字之间,每个模块包括四个主要部分:模块声明、端口定义、数据类型说明和逻辑功能描述。

如下是一个实现32位加法器的模块。有两个输入信号in1、in2,两者相加的结果通过out输出。

module add32(in1, in2, out);            // 模块声明

      input   in1, in2;                 // 端口定义,此处是输入端口
      output  out;                      // 端口定义,此处是输出端口

      wire[31:0] in1, in2, out;         // 数据类型说明,此处都是wire型

      assign out = in1 + in2;           // 逻辑功能描述

endmodule

下面结合该加法器的例子,对Module的基本结构进行说明。

      1、模块声明

模块声明包括模块名字,以及输入、输出端口列表,其格式如下。

module 模块名(端口1, 端口2, 端口3……);

      2、端口定义

明确说明模块端口的方向(输入、输出、双向等),其格式如下。

input   端口1, 端口2, 端口3 ……;          // 输入端口
output  端口1, 端口2, 端口3 ……;          // 输出端口
inout   端口1, 端口2, 端口3 ……;          // 双向端口

      3、数据类型说明

对模块中所有用到的信号(包括端口信号、节点信号等)都必须进行数据类型的定义。Verilog HDL提供了各种信号类型,下面是几种定义信号类型的例子。各数据类型的具体含义将在2.5.2节详述。

reg   a;                   // 定义信号a的数据类型为reg型
wire[31:0] out ;           // 定义信号out的数据类型为32位wire型

对于端口,可以将数据类型说明与端口定义放在一条语句中完成,于是,上文的32位加法器可以改为如下形式。

module add32(in1, in2, out);

      input  wire[31:0] in1, in2;   // 将端口定义与类型说明放在一条语句
      output wire[31:0] out;

      assign out = in1 + in2;

endmodule

对于端口,还可以将端口定义、数据类型说明都放在模块声明中,而不再放在模块内部,于是,上文的32位加法器还可以改为如下形式,更为简便。

// 将端口定义、数据类型说明放在模块声明中
module add32(input  wire[31:0]  in1,
input  wire[31:0]  in2,
output wire[31:0]  out); 

      assign out = in1 + in2;

endmodule

      4、逻辑功能描述

模块中最核心的部分就是逻辑功能描述,可以有多种方法在模块中描述和定义逻辑功能。几种基本方法如下,将在2.6节详述。

  • 用assign持续赋值语句定义
  • 用always过程块定义
  • 调用元件(也称为元件例化)

2.5 Verilog HDL基本要素

2.5.1 常量

Verilog中的常量(Constant)有三种:整数、实数、字符串。在OpenMIPS的实现过程中只使用到了整数常量,所以,此处也仅介绍整数常量。其格式如图2-7所示。

一些整数常数的例子如下。

8'b11000101      // 宽度为8位的二进制数,数值为11000101,等于十进制的197
8'h8a            // 宽度为8位的十六进制数,数值为8a,等于十进制的138
5'o27            // 宽度为5位的八进制数,数值为27,等于十进制的23
4'd10            // 宽度为4位的十进制数,数值为10

如果没有明确指明进制,那么默认是十进制。

2.5.2 变量声明与数据类型

Verilog中变量声明的格式如图2-8所示。只有数据类型、变量名是必要的,其他部分都可以省略。如果省略符号和位宽,那么根据数据类型设置为默认值。如果省略元素数,那么默认声明元素数为1。

数据类型可以是net型、variable型,分别介绍如下。

      1、net型变量

net型相当于硬件电路中各种物理连接,其特点是输出的值紧跟输入值的变化而变化。net型变量包括多种类型,如表2-1所示。

本书在实现OpenMIPS处理器的时候只使用到了其中的wire类型。wire是最常用的net型变量,Verilog HDL模块中的输入、输出信号在没有明确指定数据类型时,都被默认为wire型。wire型信号可以用作任何表达式的输入,也可以用作assign语句和实例元件的输出,如前文中的32位加法器对out信号的赋值。对于综合器而言,wire型变量的取值可为0、1、X、Z,其中0表示低电平、逻辑0;1表示高电平、逻辑1;X表示不确定或未知的逻辑状态;Z表示高阻态。如果没有赋值,默认为高阻态Z。

      2、variable型变量

variable型变量是可以保存上次写入数据的数据类型,一般对应硬件上的一个触发器或锁存器等存储元件,但并不是绝对的,在综合器综合的时候,会根据其被赋值的情况来具体确定是映射成连线还是映射为存储元件。variable型变量也包括多种类型,如表2-2所示。本书在实现OpenMIPS处理器的时候只使用到了其中的reg类型。

variable型变量必须在过程语句(initial或always)中实现赋值,这种赋值方式称为过程赋值。将在2.6节详述。

2.5.3 向量

图2-8变量声明格式中的位宽如果为1,那么对应的变量为标量,如果不为1,那么对应的变量为向量,默认为标量。向量的位宽用下面的形式定义。

[MSB : LSB]

冒号左边的数字表示向量的最高有效位MSB(Most Significant Bit),冒号右边的数字表示向量的最低有效位LSB(Least Significant Bit)。例如。

wire [3:0]  bus;    // 4位的wire型向量bus,其中bus[3]是最高位,bus[0]是最低位
reg  [31:5] ra;     // 27位的reg型向量ra,其中ra[31]是最高位,ra[5]是最低位
reg  [0:7]  rc;     // 8位的reg型向量rc,其中rc[0]是最高位,rc[7]是最低位

向量有两种,一种是向量类向量,一种是标量类向量,可以使用关键字区分,如下。

wire vectored [7:0]  databus;   // 使用关键字vectored,表示是向量类向量
reg  scalared [31:0] rega;      // 使用关键字scalared,表示是标量类向量

如果没有明确指出,那么默认是标量类向量。本书也只用到了标量类向量,对标量类向量可以任意选中其中的一位或相邻几位,分别称为位选择(bit-select)和域选择(part-select)。例如。

A = rega[6];        // 位选择,将向量rega的其中一位赋值给变量A
B = rega[5:2];      // 域选择,将向量rega的第5、4、3、2位赋值给变量B

在OpenMIPS的实现过程中,使用到了存储器,存储器可看做是二维的向量。如下就是一个存储器的定义,定义了一个深度为64,每个元素宽度为32bit的存储器。

reg [31:0] mem[63:0];  // mem是深度为64,字长为32bit的存储器

2.5.4 运算符

Verilog HDL中定义的运算符包括:算术运算符、逻辑运算符、位运算符、关系运算符、等式运算符、缩位运算符、移位运算符、条件运算符和位拼接运算符。详情如表2-3所示。

表2-3中的大部分运算都很好理解,本书不再详释,只做如下几点说明。

(1)等式运算符中的“==”与“===”的区别是:对于“==”运算,参与比较的两个操作数必须逐位相等,其结果才为1,如果某些值是不定态X或高阻态Z,那么得到的结果是不定值X;而对于“===”运算,则要求对参与运算的操作数中为不定态X或高阻态Z的位也进行比较,两个操作数必须完全一致,其结果才为1,否则结果为0。例如。

reg [4:0] a = 5'b11x01;
reg [4:0] b = 5'b11x01;

针对上面的a、b,“a==b”的返回结果为X,而“a===b”的返回结果为1。

(2)缩位运算符与位运算的运算符号、逻辑运算法则都是一样的,但是缩位运算符是对单个操作数进行与、或、异或的递推运算,它放在操作数的前面,能够将一个矢量减为一个标量。例如。

reg [3:0] a;
b = &a;           // 等效于b = ((a[0] & a[1]) & a[2]) & a[3]

而位运算需要对两个操作数按对应位分别进行逻辑运算,例如。

wire [3:0] a = 4'b0011;
wire [3:0] b = 4'b0101;
那么a&b = 4'b0001,a|b = 4'b0111

(3)位拼接运算符:用来将两个或多个信号的某些位拼接起来。其格式如下。

{比特序列0, 比特序列1,…… }

例如,在进行加法运算时,可将和与进位输出拼接在一起使用。

input  [3:0] ina,inb;             // 加法输入
output [3:0] sum;                 // 加法的和
output cout;                      // 进位
assign {cout, sum} = ina + inb;   // 将和与进位拼接在一起

位拼接还可以用来重复信号的某些位,其格式如下。

{重复次数{被重复数据}}

利用上面的功能,可以实现对信号的符号扩展,例如。

 //将Data的符号位进行扩展,s_data = {Data[7],Data[7],Data[7],Data[7],Data}
wire [7:0]  Data;
wire [11:0] s_data;
s_data = {{4{Data[7]}},Data};

(4)运算符的优先级如图2-9所示。

自己动手写处理器之第二阶段(2)——Verilog HDL简介

时间: 2024-10-12 04:11:40

自己动手写处理器之第二阶段(2)——Verilog HDL简介的相关文章

自己动手写处理器之第二阶段(3)——Verilog HDL行为语句

将陆续上传本人写的新书<自己动手写处理器>(尚未出版),今天是第七篇,我尽量每周四篇 2.6 Verilog HDL行为语句 2.6.1 过程语句 Verilog定义的模块一般包括有过程语句,过程语句有两种:initial.always.其中initial常用于仿真中的初始化,其中的语句只执行一次,而always中语句则是不断重复执行的.此外,always过程语句是可综合的,initial过程语句是不可综合的.       1.always过程语句 always过程语句的格式如图2-10所示.

自己动手写处理器之第二阶段(1)——可编程逻辑器件与PLD电路设计流程

将陆续上传本人写的新书<自己动手写处理器>(尚未出版),今天是第五篇,我尽量每周四篇 通过上一章的介绍,读者应该知道CPU内部有一些基本的电路,比如:译码电路.运算电路.控制电路,此外还有一些寄存器等.这些电路怎么实现呢?当然可以通过一大堆分立的元器件实现,实际上在2008年,美国加州的游戏开发人士Steve Chamberlin就自己制造了一款8位CPU,耗时18个月,花费1000美元,总共使用了1253条线缆,如图2-1所示,Steve Chamberlin为它起了一个十分贴切的名字--B

自己动手写处理器之第二阶段(4)——电路设计举例

将陆续上传本人写的新书<自己动手写处理器>(尚未出版),今天是第八篇,我尽量每周四篇 2.7电路设计举例 本节将设计一个简化的处理器取指令电路,通过这个例子体会Verilog HDL的使用. 处理器内部一般有一个PC寄存器,其中存储指令地址,正常运行过程中,PC的值会随时间增加,同时从指令存储器中取出对应地址的指令.所以,本节实现的处理器取指令电路,包含两部分:PC模块.指令存储器.       1.PC模块的设计与实现 PC模块的功能就是给出取指令地址,同时每个时钟周期取指令地址递增.其接口

自己动手写处理器之第二阶段(5)——ModelSim电路仿真

将陆续上传本人写的新书<自己动手写处理器>(尚未出版),今天是第九篇,我尽量每周四篇 2.8 仿真 上一节实现了一个简化的处理器取指电路,需要通过仿真以验证其功能是否正确,直观的仿真思路就是:给出一个时钟信号,上述电路会在每个时钟信号上升沿将取指地址加1,同时从指令存储器中取出一条指令,观察取指地址是否依次递增,同时观察取出的指令是否是存储器中取指地址对应的指令,如果都符合,那么上述取指电路就实现正确.此处涉及到两个问题. 1.如何在指令存储器中存储指令,也就是指令存储器初始化问题. 2.如何

自己动手写处理器之第四阶段(1)——第一条指令ori的实现

将陆续上传本人写的新书<自己动手写处理器>(尚未出版),今天是第11篇,我尽量每周四篇 第4章 第一条指令ori的实现 前面几章介绍了很多预备知识,也描绘了即将要实现的OpenMIPS处理器的蓝图,各位读者是不是早已摩拳擦掌,迫切希望一展身手了,好吧,本章我们将实现OpenMIPS处理器的第一条指令ori,为什么选择这条指令作为我们实现的第一条指令呢?答案就两个字--简单,指令ori用来实现逻辑"或"运算,选择一条简单的指令有助于我们排除干扰,将注意力集中在流水线结构的实现

自己动手写处理器之第一阶段(3)——MIPS32指令集架构简介

将陆续上传本人写的新书<自己动手写处理器>(尚未出版),今天是第四篇,我尽量每周四篇 1.4 MIPS32指令集架构简介 本书设计的处理器遵循MIPS32 Release 1架构,所以本节介绍的MIPS32指令集架构指的就是MIPS32 Release 1. 1.4.1 数据类型 指令的主要任务就是对操作数进行运算,操作数有不同的类型和长度,MIPS32提供的基本数据类型如下. 位(b):长度是1bit. 字节(Byte):长度是8bit. 半字(Half Word):长度是16bit. 字(

自己动手写处理器之第一阶段(2)——MIPS指令集架构的演变

将陆续上传本人写的新书<自己动手写处理器>(尚未出版),今天是第三篇,我尽量每周四篇 MIPS指令集架构自上世纪80年代出现后,一直在进行着更新换代,从最初的MIPS I到MIPS V,发展到可支持扩展模块的MIPS32.MIPS64系列,再到集成代码压缩技术的microMIPS32.microMIPS64.每个MIPS ISA都是其前一个的超集,没有任何遗漏,只有增加新的功能.       1.MIPS Ⅰ 提供加载/存储.计算.跳转.分支.协处理及其它特殊指令.该指令集架构用于最初的MIP

自己动手写处理器之第一阶段(1)——计算机的简单模型、架构、指令集

将陆续上传本人写的新书<自己动手写处理器>(尚未出版),今天是第二篇,我尽量每周四篇 第1章 处理器与MIPS 时间开始了! --胡风 · 1949 让我们以一句诗意的话,开始本书的阅读. 时间从1971年11月15日开始,那一天,Intel发布了世界上第一款单芯片微处理器4004. 1.1 计算机的简单模型 计算机很复杂,可以听歌.看电影.上网.玩游戏,内部是怎么工作的,这个问题太可怕了,太复杂了. 计算机很简单,只有加.减.乘.除.逻辑.移位.转移.存储.加载等几类可以做的操作,太简单了.

自己动手写编译器之序

<自己动手写编译器.链接器> 因为工作的关系,我经常和各企业的技术负责人交流.话题谈着谈着常常会转到他们目前共同的难题--技术人员招聘.这时不少人都会感慨,中国能做系统软件开发的技术人员太少,这方面的人太难找了.随着中国企业的发展,做系统和平台的需求不断增加,这种供需矛盾将越来越明显. 究其原因,很容易想到的是我们的高校教育.课程设置.美国顶尖大学计算机系基础课程教学里都非常重视项目实践,操作系统课往往要真的开发一个像模像样的操作系统原型,编译器课也真的要自己设计并实现一门有创新性的小语言--