第5章计算机组成部件实验 本章共设计了5个实验,大部分实验由几个小实验组成。 为了方便学生实验,与本书配套的资源包中有大部分实验的相关文件,包括初始设计文件、仿真文件和约束文件。本章的资源默认下载后解压到C:\sysclassfiles\orgnization的相关文件夹中。 如果开设了专门的计算机组成课程设计,则本章内容只作为计算机组成原理课内小实验。第6章作为计算机组成课程设计内容。如果没有专门开设计算机组成课程设计,则本章内容可以和第6章内容合并,作为计算机组成原理实验内容。 5.1加减法器的设计 在4.4节设计了一个加法器,计算机组成原理课程中引入了补码的概念后,实际上可以用加法器来做减法,而无须专门设计减法器。本节所有实验的初始文档均在C:\sysclassfiles\orgnization\Ex_1中。 5.1.1可变位宽的加减法器IP核的设计 1. 实验目的 (1) 进一步理解原码、补码的概念。 (2) 学会用加法器做减法的方法。 (3) 进一步理解无符号数进位与借位、有符号数溢出的判断方法以及符号位和结果为0标志赋值方法。 2. 实验内容 使用Verilog HDL语言实现一个可适应从4~32位数据运算的加减法器,其中被操作数为a,操作数为b,加减法控制信号为sub,当sub为1时做减法,为0时做加法。另外输出为运算结果sum,进位/借位标志cf,有符号数溢出标志ovf,符号标志sf以及结果为0标志zf。将该加减法器封装成IP核addsub。 3. 实验预习 复习IP核封装的方法,同时复习原码、补码的概念,有符号数溢出的判断方法等。理解下面的基本原理。 1) 关于无符号数的进位CF 如果是加法,则CF就是二进制运算的进位位。 由于减法是将减数取反加1(求补)后(假设减数b求补后为subb)与被减数a相加,因此,当够减的时候反而有进位,不够减的时候反而无进位(其实是无符号数溢出),因此,CF需要在进位位基础上取反才能表示借位(实际上是让CF=1表示被减数a小于减数b,CF=0表示被减数a大于减数b)。 因此CF在做加法时不将加法进位取反,做减法时要将加法进位取反。 2) 关于有符号数的溢出问题(OF) 在计算机组成原理中对于有符号数溢出有一个规则就是两数相加最高位和次高位都进位或者都不进位的时候没有溢出,否则就有溢出。但这必须看最高位和次高位是不是有进位。下面换一个简单的方法来判断。 (1) 对于加法。 有符号数加法溢出的规则其实很简单,就是两个正数相加得到负数,或者两个负数相加得到正数的时候,有符号数加法就溢出了。 (2) 对于减法。 有符号数减法溢出的规则也很简单: 一个正数减去一个负数得到一个负数或者一个负数减去一个正数得到一个正数的时候,就产生了溢出。 考虑一下: 要用加法器做减法,因此会将减数b取补后的subb作为加数和被加数a相加,因此,最终,判断溢出应该是判断a和subb相加的溢出规则。 如果在做加法时让subb=b,在做减法时用subb=b的补数,那么最终无论加法或减法,都化作了sum=a+subb,因此只需要判断a和subb相加的溢出规则。 请读者按照加法溢出规则,请给出OF的逻辑表达式。 4. 实验步骤 本实验的资源包在C:\sysclassfiles\orgnization\Ex_1\addsub中给出了初始设计文件addsub.v和仿真文件addsub_sim.v,供读者完善和使用。 (1) 创建项目,完成设计。 创建addsub项目,设计文件中模块名为addsub。 读者可以通过完善资源包中如下的初始设计来实现addsub模块。 1.`timescale 1ns / 1ps 2.module addsub 3.#(parameter WIDTH=8)//指定数据宽度参数,默认值是8 4.( 5.input [(WIDTH-1):0] a,// 被操作数,位宽由参数WIDTH决定 6.input [(WIDTH-1):0] b, // 操作数,位宽由参数WIDTH决定 7.inputsub, //=1为减法 8.output [(WIDTH-1):0] sum, //结果 9.output cf, // 进位标志 10.output ovf,// 溢出标志 11.output sf, // 符号标志 12.output zf// 为0标志 13.); 14.wire [(WIDTH-1):0] subb,subb1; 15.wire cf2;// 进位 16.assign subb1=b ^ {WIDTH{sub}};// 对于减法是取反 17.assign subb=subb1 + sub;// 对于减法是加1(对于减法,subb就是b的补) 18.// 添加自己的代码 19.… 20.endmodule (2) 仿真验证。 加减法器设计好后,用资源包中如下的仿真程序进行仿真。 1.`timescale 1ns / 1ps 2.module addsub_sim(); 3.// input 4.reg [31:0] a=32'd16;//0x10+0x0c 5.reg [31:0] b=32'd12; 6.reg sub=0; 7.//output 8.wire [31:0] sum; 9.wire cf; 10.wire ovf; 11.wire sf; 12.wire zf; 13.// initial 14.addsub #(32) U (a,b,sub,sum,cf,ovf,sf,zf);  // 实例化成32位 15.initial begin 16.#200 sub=1;  //0x10-0x0c 17.#200 begin a=32'h7f; b=32'h2; sub=0; end  //0x7f+2 18.#200 begin a=32'hff; b=32'h2; sub=0; end  //0xff+2 19.#200 begin a=32'h7fffffff; b=32'h2; sub=0; end  //0x7fffffff+2 20.#200 begin a=32'h16; b=32'h17; sub=1; end  //0x16-0x17 21.#200 begin a=32'hffff; b=32'h1; sub=0; end  //0xffff+1 22.#200 begin a=32'hffffffff; b=32'h1; sub=0; end  //0xffffffff+1 23.end 24.endmodule 图51是仿真后的波形图,读者可以对照一下。注意计算的结果和标志信号是否正确。 图51addsub仿真图 (3) 封装成IP核。 5. 思考与拓展 为什么初始设计文档中对于数b求补要采用异或的办法取反呢? 5.1.28位加减法器的设计 1. 实验目的 通过实验,加深对IP核使用的熟练程度,同时在实验板上体验加减法器。 2. 实验内容 使用Verilog HDL语言以及5.1.1节中实现的加减法器IP核,实现一个8位的加减法器addsub8。 3. 实验预习 认真复习IP核的调用方法。 4. 实验步骤 本实验的资源包在C:\sysclassfiles\orgnization\Ex_1\addsub8中给出了初始设计文件addsub8.v、仿真文件addsub8_sim.v和约束文件addsub8.xdc,供读者完善和使用。 (1) 创建项目,完成设计。 创建addsub8项目,设计文件中模块名为addsub8。 将addsub核导入到项目中,在设计文件中定义好端口,并实例化该IP核。 (2) 仿真验证。 用资源包中如下的仿真程序进行仿真,可以获得如图52所示的仿真波形图,读者对照一下。 1.`timescale 1ns / 1ps 2.module addsub_sim( ); 3.// input 4.reg [7:0] a=8'h16;  //0x16+0x12 5.reg [7:0] b=8'h12; 6.reg sub=0; 7.//output 8.wire [7:0] sum; 9.wire cf; 10.wire ovf; 11.wire sf; 12.wire zf; 13.// initial 14.addsub8 U (a,b,sub,sum,cf,ovf,sf,zf); 15.initial begin 16.#200 sub=1;  //0x16-0x12 17.#200 begin a=8'h7f; b=8'h2; sub=0; end  //0x7f+2 18.#200 begin a=8'hff; b=8'h2; sub=0; end  //0xff+2 19.#200 begin a=8'h16; b=8'h17; sub=1; end  //0x16=0x17 20.#200 begin a=8'hfe; b=8'hff; sub=1; end  //0xfe-0xff 21.end 22.endmodule 图52addsub8的仿真波形 (3) 下载到实验板上进行验证。 其中输入a[7]~a[0]分别接SW15~SW8, b[7]~b[0]分别接SW7~SW0, sub接SW23, sum[7]~sum[0]分别接GLD7~GLD0。ovf接YLD7,cf接YLD6,sf接YLD5,zf接GLD4,详见表51。 表51addsub8的引脚分配 信号器件引脚信号器件引脚 a[7]SW15AB6b[7]SW7U6 a[6]SW14AB7b[6]SW6W5 a[5]SW13V7b[5]SW5W6 a[4]SW12AA6b[4]SW4U5 a[3]SW11Y6b[3]SW3T5 a[2]SW10T6b[2]SW2T4 a[1]SW9R6b[1]SW1R4 a[0]SW8V5b[0]SW0W4 sum[7]GLD7F21ovfYLD7M17 sum[6]GLD6G22cfYLD6M16 sum[5]GLD5G21sfYLD5M15 sum[4]GLD4D21zfYLD4K16 sum[3]GLD3E21subSW23Y9 sum[2]GLD2D22 sum[1]GLD1E22 sum[0]GLD0A21 5. 思考与拓展 自行设计其他的仿真文件,对addsub8进行更多的验证。 5.2乘法器的设计 乘法器是CPU中基本的运算部件之一,下面将用不同的方法设计乘法器。本节所有实验在资源包中提供的初始文档均在C:\sysclassfiles\orgnization\Ex_2中。 5.2.1无符号数乘法器的设计 1. 实验目的 (1) 掌握原码一位乘法的原理。 (2) 学会用迭代算法实际设计与实现一个数据宽度可变的无符号乘法器IP核。 2. 实验内容 使用Verilog HDL语言行为级描述方法,根据原码一位乘法的原理,实现乘数和被乘数数据宽度在4~32位之间可变的无符号数乘法器mulu,并请通过mulu_sim.v文件,仿真验证无符号数乘法器。最后,请将无符号数乘法器封装成IP核。 3. 实验预习 理解下面的原码一位乘法原理。 1) 从手算乘法到原码一位乘法 二进制乘法的手算算法和十进制乘法一样,下面假设计算X×Y=1010×1101。 1010 ×1101 1010 0000 1010 1010 10000010 从手算乘法不难看到两个二进制无符号数相乘的算法可以描述如下。 (1) 令X=被乘数,位宽为N,Y=乘数,位宽为N,且Y=yN-1×2N-1+…+y2×22+y1×21+y0×20。 (2) 设置CNT=位宽N,设置积P=0,P的位宽是2N,将X的位宽高位0扩展到2N位。 (3) 判断当前乘数最低位y0是否为1,如果为1,则令P=P+X,否则转步骤(4)。 (4) X=X左移一位,Y=Y逻辑右移1位。 (5) CNT=CNT-1,判断CNT是否为0,如果不为0,则转到(3),否则结束。 根据这个算法很容易得到手算算法的Verilog语言描述的电路如下: 1.module mulu_hand 2.#(parameter WIDTH=8) 3.( 4.input [WIDTH-1:0] a, 5.input [WIDTH-1:0] b, 6.output reg [WIDTH*2-1:0] c 7.); 8.integer cnt; 9.reg [WIDTH-1:0] y,t; 10.reg [WIDTH*2-1:0] x,p; 11.always @(*) 12.begin 13.t={WIDTH{1'b0}}; 14.x={t,a};// 扩展被乘数到2N位 15.p={WIDTH*2{1'b0}};  // 积初始化为0 16.y=b; 17.for(cnt=0; cnt> 1;   // 乘数右移 23.end 24.c=p; 25.end 26.endmodule 请读者在Vivado中实现该模块并仿真看其结果是否正确。 2) 算法的改进 上面的方法功能上是正确的,但由于乘积的位数是被乘数和乘数位数的2倍,因此需要一个位宽是2N的加法器,另外,这个方法需要用到左移和右移两种移位寄存器。 为了提高效率,计算机中对上述乘法进行了改进。考察一下两个4位无符号数X×Y的计算过程的推导。假设Y=y3×23+y2×22+y1×21+y0×20,则 X×Y=X×y3×23+X×y2×22+X×y1×21+X×y0×20 =24×(X×y3×2-1+X×y2×2-2+X×y1×2-3+X×y0×2-4) 注意,上述推导式中把一个4位整数分成了两个部分,其中下画线部分已经成了一个纯小数。而24×的作用就是把这个纯小数再还原成整型数。因为24×只改变了小数点的位置,因此在考察乘法算法的操作过程中不考虑这个部分,因为算法操作过程本身也不考虑小数点的位置。 将X×y3×2-1+X×y2×2-2+X×y1×2-3+X×y0×2-4这部分再进行推导,得 X×y3×2-1+X×y2×2-2+X×y1×2-3+X×y0×2-4 =2-1×(X×y0×2-3+X×y1×2-2+X×y2×2-1+X×y3) =2-1×(2-1×(X×y0×2-2+X×y1×2-1+X×y2)+X×y3) =2-1×(2-1×(2-1×(X×y0×2-1+X×y1)+X×y2)+X×y3) =2-1×(2-1×(2-1×(2-1×(0+X×y0)+X×y1)+X×y2)+X×y3) 假设P0=0,则可以得出以下推导过程 P1=2-1×(P0+X×y0) P2=2-1×(P1+X×y1) P3=2-1×(P2+X×y2) P4=2-1×(P3+X×y3) 不失一般性,不难得出部分积Pi的推导公式为 Pi+1=2-1×(Pi+X×yi),i=0,1,2,…,N-1 在二进制中,×2-1就是右移一位。因此,可以得出无符号数原码一位乘法的操作步骤。 (1) 令X=被乘数,位宽为N,Y=乘数,位宽为N,且Y=yN-1×2N-1+…+y2×22+y1×21+y0×20。 (2) 设置CNT=位宽N,设置部分积P=0,P的位宽是N,设置加法进位位为CY=0。 (3) 判断当前乘数最低位y0是否为1,如果为1,则令{CY,P}=P+X,否则转步骤(4)。 (4) {CY,P,Y}联合右移一位,CNT=CNT-1。 (5) 判断CNT是否为0,如果不为0,则转到(3),否则结束。P是积的高N位,Y中是积的低N位。 用X×Y=1010×1101来举例如下: CNTCPY说明 4000001101P0=0 +1010 Y0=1,+X 01010{CY,P,Y}联合右移一位 001010110得P1 3001010110Y1=1,{CY,P,Y}联合右移一位 000101011得P2 2+1010Y2=1,+X 01100{CY,P,Y}联合右移一位 001100101得P3 1+1010Y3=1,+X 10000{CY,P,Y}联合右移一位 010000010得P4 0结束,结果是10000010 4. 实验步骤 本实验的资源包在C:\sysclassfiles\orgnization\Ex_2\mulu中给出了初始设计文件mulu.v和仿真文件mulu_sim.v,供读者完善和使用。 (1) 创建项目,完成设计。 创建mulu项目,设计文件中模块名为mulu。 读者可以通过利用上面改进的算法完善资源包中如下的初始设计来实现mulu模块。 1.`timescale 1ns / 1ps 2.module mulu 3.#(parameter WIDTH=8) 4.( 5.input [WIDTH-1:0] a, 6.input [WIDTH-1:0] b, 7.output reg [WIDTH*2-1:0] c 8.); 9. 10.integer cnt; 11.reg [WIDTH-1:0] x,y,p; 12.reg cy; 13. 14.always @(*) 15.begin 16.//添加自己的代码 17. 18.end 19.endmodule 请读者根据上面的原码一位乘法算法自行完善设计的其他部分。 (2) 仿真验证。 下面给出一个mulu_sim.v文件,作为仿真程序的一个案例,文件代码如下: 1.`timescale 1ns / 1ps 2.module mulu_sim(); 3.// input 4.reg [31:0] a=32'd9; 5.reg [31:0] b=32'd12;//9×12 6.// output 7.wire [63:0] c; 8.mulu #(32) u(a,b,c); 9.initial begin 10.#400 a=32'd6;  //6×12 11.#400 b=32'd5;  //6×5 12.end 13.endmodule 图53是上述仿真文件仿真出来的波形图。图中数字的显示采用的是十进制(这只需要右击信号名,然后在弹出的菜单中选择显示的进制数为十进制即可)。 图53mulu模块仿真波形 (3) 封装IP核。 将mulu模块封装成IP核,封装IP核的方法读者可以参考前面章节。 5. 思考与拓展 利用上面封装的数据宽度可变的无符号数乘法器IP核,采用元件例化的方法实现一个8位无符号乘法器mulux8,并下载到Minisys实验板上进行验证。被乘数a[7:0]接SW15~SW8,乘数b[7:0]接SW7~SW0。乘积c[15:0]接YLD7~YLD0,GLD7~GLD0,如表52所示。 表52mulux8的引脚分配 信号器件引脚信号器件引脚 a[7]SW15AB6c[15]YLD7M17 a[6]SW14AB7c[13]YLD6M16 a[5]SW13V7c[11]YLD5M15 a[4]SW12AA6c[9]YLD4K16 a[3]SW11Y6c[7]YLD3L16 a[2]SW10T6c[5]YLD2L15 a[1]SW9R6c[3]YLD1L14 a[0]SW8V5c[1]YLD0J17 续表 信号器件引脚信号器件引脚 b[7]SW7U6c[14]GLD7F21 b[6]SW6W5c[12]GLD6G22 b[5]SW5W6c[10]GLD5G21 b[4]SW4U5c[8]GLD4D21 b[3]SW3T5c[6]GLD3E21 b[2]SW2T4c[4]GLD2D22 b[1]SW1R4c[2]GLD1E22 b[0]SW0W4c[0]GLD0A21 在C:\sysclassfiles\orgnization\Ex_2\mulux8中给出了初始设计文件和约束文件,供读者完善和使用。 5.2.2有符号数乘法器的设计 1. 实验目的 (1) 掌握利用原码乘法器完成有符号数乘法的原理。 (2) 学会迭代算法实际设计与实现一个数据宽度可变的有符号乘法器。 2. 实验内容 使用Verilog HDL语言行为级描述方法,根据原码乘法的原理,实现乘数和被乘数数据宽度在4~32位之间可变的有符号数乘法器mul,并请通过编写mul_sim文件,仿真验证有符号数乘法器。最后,请将有符号乘法器封装成IP核。 3. 实验预习 mul和mulu的最大区别是操作数和结果都是有符号数,由于机器内负数都是用补码表示的,因此要分别判断被乘数和乘数,如果是负数,要对它们求补,得到原码,再将数值位进行原码一位乘法运算。运算出来结果后,还要根据被乘数和乘数的符号位特点判断结果是否为负数,如果是负数,需要对结果求一次补。在做原码一位乘的时候,依然要注意进位位要参与到右移运算中。 请思考,乘法器分为有符号乘法器和无符号乘法器,有没有必要为加减法运算也设计有符号和无符号两种加减法器? 4. 实验步骤 本实验的资源包在C:\sysclassfiles\orgnization\Ex_2\mul中给出了初始设计文件mul.v和仿真文件mul_sim.v,供读者完善和使用。 (1) 创建项目,完成设计。 创建mul项目,设计文件中模块名为mul。 读者可以通过完善资源包中如下的初始设计来实现mul模块。 1.module mul 2.#(parameter WIDTH=8) 3.( 4.input [WIDTH-1:0] a,  // 被乘数 5.input [WIDTH-1:0] b,  // 乘数 6.output reg [WIDTH*2-1:0] c  // 乘积 7.); 8. 9.integer cnt;  // 循环变量 10.reg [WIDTH-1:0] x,y,p;  // 存放被乘数、乘数和部分积 11.reg sign,cy;  // 进位位 12. 13.always @(*) 14.begin 15.cy=0; 16.x=a; 17.y=b; 18.p={WIDTH{1'b0}}; 19.if(a[WIDTH-1]==1) // 被乘数是负数的处理 20.begin 21.x=~x; 22.x=x + 1'b1; 23.end 24.… // 乘数是负数的处理、原码一位乘、结果符号的处理 25.end 26. endmodule (2) 仿真验证。 下面给出一个mul_sim.v文件,作为仿真程序的一个案例,文件代码如下: 1.`timescale 1ns / 1ps 2.module mul_sim( ); 3.// input 4.reg [31:0] a=32'd9; 5.reg [31:0] b=32'd12; 6.// output 7.wire [63:0] c; 8.mul #(32) u(a,b,c); 9.initial begin 10.#400 a=32'hfffffffe;   // -2 11.#400 b=32'hfffffffe;  // -2 12.end 13.endmodule 仿真后得到如图54所示的波形,图中数据的显示已经设置为有符号十进制数。 图54mul的仿真波形图 在C:\sysclassfiles\orgnization\Ex_6\mul中给出了初始设计文件和仿真文件,供读者完善和使用。 (3) 封装IP核。 将mul模块封装成IP核。 5. 思考与拓展 利用上面封装的数据宽度可变的有符号数乘法器IP核,采用元件例化的方法实现一个8位有符号数乘法器mulx8,并下载的Minisys实验板上进行验证。被乘数a[7:0]接SW15~SW8,乘数b[7:0]接SW7~SW0,乘积c[15:0]接GLD7~GLD0,YLD7~YLD0。mulx8的引脚分配如表53所示。 表53mulx8的引脚分配 信号器件引脚信号器件引脚 a[7]SW15AB6b[7]SW7U6 a[6]SW14AB7b[6]SW6W5 a[5]SW13V7b[5]SW5W6 a[4]SW12AA6b[4]SW4U5 a[3]SW11Y6b[3]SW3T5 a[2]SW10T6b[2]SW2T4 a[1]SW9R6b[1]SW1R4 a[0]SW8V5b[0]SW0W4 c[15]GLD7F21c[14]YLD7M17 c[13]GLD6G22c[12]YLD6M16 c[11]GLD5G21c[10]YLD5M15 c[9]GLD4D21c[8]YLD4K16 c[7]GLD3E21c[6]YLD3L16 c[5]GLD2D22c[4]YLD2L15 c[3]GLD1E22c[2]YLD1L14 c[1]GLD0A21c[0]YLD0J17 在C:\sysclassfiles\orgnization\Ex_2\mulx8中给出了初始设计文件和约束文件,供读者完善和使用。 5.2.3利用Vivado自带的乘法器IP核进行乘法器的设计 1. 实验目的 (1) 能够利用Vivado自带的乘法器IP核设计所需的乘法器。 (2) 对几种实现方法进行比较。 2. 实验内容 使用Verilog HDL语言的元件例化方法,运用Vivado自带的乘法器IP核实现一个8位的无符号数乘法器xmulux8。 3. 实验预习 认真阅读乘法器IP核的相关文档。该文档可以在IP Catalog中找到Multiplier,双击弹出如图55所示的窗口,单击左上角的,就可打开相关文档。 图55无符号乘法器设置 4. 实验步骤 本实验的资源包在C:\sysclassfiles\orgnization\Ex_2\xmulux8中给出了初始设计文件xmulux8.v、仿真文件xmulux8_sim.v和约束文件xmulux8.xdc,供读者完善和使用。 1) 创建一个新的项目 创建xmulux8项目,设计文件中模块名为xmulux8。 2) 调入和设置IP核 (1) 双击IP Catalog,并在打开的IP核库中选择Vivado Repository→Math Functions→Multipliers→Multiplier命令,如图56所示。 (2) 在打开的乘法器IP核配置的Basic选项卡,如图55所示,设置为8位无符号数乘法器。 (3) 在打开的乘法器IP核配置的Output and Control选项卡,如图57所示,设置流水级为0(不采用流水乘法器)。 (4) 单击OK按钮,生成IP核。 3) 实例化IP核 在初始设计文件xmulux8.v中实例化mulux8。 图56导入乘法器IP核 图57组合逻辑乘法器配置 4) 仿真验证 采用资源包中如下的仿真程序进行仿真。 1.`timescale 1ns / 1ps 2.module xmulux8_sim( ); 3.// input 4.reg [7:0] a=8'd9;//9×12 5.reg [7:0] b=8'd12; 6.// output 7.wire [15:0] c; 8.xmulux8 u(a,b,c); 9.initial begin 10.#400 a=8'd6;  //6×12 11.#400 b=8'd5;  //6×5 12.#400 begin a=8'd125; b=8'd8; end  //125×8 13.end 14.endmodule 仿真后得到如图58所示的波形。 图58xmulux8的仿真结果 5) 综合、实现、生成比特流、下载 下载到Minisys实验板上进行验证。被乘数a[7:0]接SW15~SW8,乘数b[7:0]接SW7~SW0,乘积c[15:0]接YLD7~YLD0,GLD7~GLD0,如表54所示。 表54xmulux8的引脚分配 信号器件引脚信号器件引脚 a[7]SW15AB6b[7]SW7U6 a[6]SW14AB7b[6]SW6W5 a[5]SW13V7b[5]SW5W6 a[4]SW12AA6b[4]SW4U5 a[3]SW11Y6b[3]SW3T5 a[2]SW10T6b[2]SW2T4 a[1]SW9R6b[1]SW1R4 a[0]SW8V5b[0]SW0W4 c[15]GLD7F21c[14]YLD7M17 c[13]GLD6G22c[12]YLD6M16 c[11]GLD5G21c[10]YLD5M15 续表 信号器件引脚信号器件引脚 c[9]GLD4D21c[8]YLD4K16 c[7]GLD3E21c[6]YLD3L16 c[5]GLD2D22c[4]YLD2L15 c[3]GLD1E22c[2]YLD1L14 c[1]GLD0A21c[0]YLD0J17 5. 思考与拓展 (1) 设计一个非流水的8位有符号数乘法器。 使用Verilog HDL语言的元件例化方法,运用Vivado自带的乘法器IP核实现一个8位的有符号数乘法器xmulx8,并下载的Minisys实验板上进行验证。被乘数a[7:0]接SW15~SW8,乘数b[7:0]接SW7~SW0,乘积c[15:0]接YLD7~YLD0,GLD7~GLD0。mulx8的引脚分配如表55所示。 表55xmulx8的引脚分配 信号器件引脚信号器件引脚 a[7]SW15AB6b[7]SW7U6 a[6]SW14AB7b[6]SW6W5 a[5]SW13V7b[5]SW5W6 a[4]SW12AA6b[4]SW4U5 a[3]SW11Y6b[3]SW3T5 a[2]SW10T6b[2]SW2T4 a[1]SW9R6b[1]SW1R4 a[0]SW8V5b[0]SW0W4 c[15]GLD7F21c[14]YLD7M17 c[13]GLD6G22c[12]YLD6M16 c[11]GLD5G21c[10]YLD5M15 c[9]GLD4D21c[8]YLD4K16 c[7]GLD3E21c[6]YLD3L16 c[5]GLD2D22c[4]YLD2L15 c[3]GLD1E22c[2]YLD1L14 c[1]GLD0A21c[0]YLD0J17 在C:\sysclassfiles\orgnization\Ex_6\xmulx8中给出了初始设计文件、仿真文件和约束文件,供读者完善和使用。 (2) 流水型乘法器设计。 学有余力的同学可以使用Verilog HDL语言的元件例化方法,运用Vivado自带的乘法器IP核实现流水型的乘法器,并进行一些分析。 5.3除法器的设计 除法器也是CPU中基本的运算部件之一,下面将用不同的方法设计除法器。本节所有实验在资源包中提供的初始文档均在C:\sysclassfiles\orgnization\Ex_3中。 5.3.1无符号数除法器的设计 1. 实验目的 (1) 掌握不恢复余数除法的原理。 (2) 学会用Vivado的IP核,用PLL方法设计所需时钟。 (3) 设计不恢复余数的无符号数除法器。 2. 实验内容 使用Verilog HDL语言行为级描述方法,根据不恢复余数除法的原理,实现被除数数据宽度在8、16、32位之间可变的无符号数除法器divu,并请通过编写divu_sim文件,仿真验证无符号数除法器。最后,请将无符号数除法器封装成IP核。 3. 实验预习 理解不恢复余数除法的原理和步骤。 假设[A]原=AS.A1A2A3…An,[B]原=BS.B1B2B3…Bn/2,Q是A/B的商([Q]原=QS.Q1Q2Q3…Qn),R是A/B的余数([R]原=RS.R1R2R3…Rn/2)。其中AS、BS、QS和RS分别是被除数、除数、商和余数的符号位,无符号数除法求商的步骤如下。 (1) 设3个寄存器regq、regb和regr分别放部分商(以及部分被除数)、除数和部分余数。开始的时候令regq=被除数,regb=除数,regr=0。 (2) 将余数regr左移1位,最低位用被除数regq的最高位补上的数减去除数regb得到新余数。 (3) 如果新余数为正(符号位为0),则上商1,部分被除数regq左移1位,最低位放入商1,同时将新余数左移1位,最低位用部分被除数regq的最高位补上的数减去除数regb得到新的新余数,跳转到(4); 如果新余数为负(符号位为1),则上商0,部分被除数regq左移1位,最低位放入商0,同时将新余数左移1位,最低位用被除数regq的最高位补上的数加上除数regb得到新的新余数,跳转到(4)。 (4) 用新的新余数作为余数,如果循环次数没到结束的时候,则转(3); 否则转(5)。 (5) 部分商regq就是商,如果最后的新余数为正数,则新余数reqr就是余数,否则reqr加上除数reqb的值作为余数。 注意: 这里的regq最开始放的是被除数,随着步骤的循环,被除数逐渐左移出regq,而低位不断被新的商补上,新的商也跟着被除数左移而左移,指导循环结束时被除数被彻底移除regq,而商完整的占据regq,因此regq既叫作部分被除数,也叫作部分商。 这里的循环次数是被除数的位宽。 4. 实验步骤 本实验的资源包在C:\sysclassfiles\orgnization\Ex_3\divu中给出了初始设计文件divu.v和仿真文件divu_sim.v,供读者完善和使用。 (1) 创建项目,完成设计。 创建divu项目,设计文件中模块名为divu。 读者可以利用不恢复余数除法算法完善资源包中如下的初始设计来实现divu模块。 1.`timescale 1ns / 1ps 2.module divu 3. #(parameter WIDTH=8) 4.( 5.input [WIDTH-1:0] a,// 被除数 6.input [WIDTH/2-1:0] b,  // 除数 7.input clk, 8.input start,  // 启动除法操作(只保持一个时钟,启动后就变为无效) 9.input resetn, 10.output [WIDTH-1:0] q,  // 商 11.output [WIDTH/2-1:0] r,   // 余数 12.output reg busy   // 正在做除法 13.); 14. 15.reg [WIDTH-1:0] regq;  // 放过程中的部分商和部分被除数 16.reg [WIDTH/2-1:0] regb;  // 放除数 17.reg [WIDTH/2-1:0] regr;  // 放过程中的余数 18.reg [WIDTH-2:0] count;  // 循环计数器 19.reg r_sign; 20.wire [WIDTH/2:0] suboradd;  // 加或减去余数 21.//添加自己的代码 22.… 23.endmodule 其中,被除数为a,除数为b,商为q,余数为r(b和r的宽度是被除数的一半,商的数据宽度同被除数)。作为辅助信号,有时钟信号clk,启动除法操作的信号start,复位信号resetn,另外,除法器应该有一个输出信号busy,当除法运算开始时,该信号为高电平,除法运算结束后,busy转为低电平。 (2) 仿真验证。 下面的代码是仿真文件的一个案例,读者可以在divu_sim.v中找到它。仿真后得到如图59所示的波形。 1.`timescale 1ns / 1ps 2.module divu_sim(); 3.// input 4.reg [31:0] a=32'd16; 5.reg [15:0] b=16'd4; 6.reg clk=0; 7.reg start=0; 8.reg resetn=0; 9.// output 10.wire [31:0] q; 11.wire [15:0] r; 12.wire busy; 13.divu #(32) u(a,b,clk,start,resetn,q,r,busy); 14.initial begin 15. #30 begin resetn=1;start=1;end 16. #50 start=0; 17. #1400 begin start=1; a=32'd18;b=16'd5;end 18. #50 start=0; 19.end 20.always#20clk=~clk; 21.endmodule 图59divu的仿真波形 图59中的数据都已经以无符号十进制数来表示,除法的结果在busy信号无效时出现在总线上。 (3) 封装IP核。 将divu模块封装成IP核。封装IP核的方法读者可以参考前面章节,这里数据宽度不是一个范围,而是3个具体值,这种参数的设置方法如图510所示。 图510参数值是几个固定值的设置方法 5. 思考与拓展 利用上面封装的数据宽度可变的无符号数除法器IP核divu,采用元件例化的方法实现一个16位无符号数除法器divux16,并下载到Minisys实验板上进行验证。被除数a[15:0]接SW23~SW8,除数b[7:0]接SW7~SW0,商q[15:0]接YLD7~YLD0,GLD7~GLD0,余数r[7:0] 接RLD7~RLD0,clk接Y18,resetn接P20,start接S4按键开关。具体的引脚分配如表56所示。 表56divux16的引脚分配 信号器件引脚信号器件引脚 a[15]SW23Y9q[15]YLD7M17 a[14]SW22W9q[14]YLD6M16 a[13]SW21Y7q[13]YLD5M15 a[12]SW20Y8q[12]YLD4K16 a[11]SW19AB8q[11]YLD3L16 a[10]SW18AA8q[10]YLD2L15 a[9]SW17V8q[9]YLD1L14 a[8]SW16V9q[8]YLD0J17 a[7]SW15AB6q[7]GLD7F21 a[6]SW14AB7q[6]GLD6G22 a[5]SW13V7q[5]GLD5G21 a[4]SW12AA6q[4]GLD4D21 a[3]SW11Y6q[3]GLD3E21 a[2]SW10T6q[2]GLD2D22 a[1]SW9R6q[1]GLD1E22 a[0]SW8V5q[0]GLD0A21 b[7]SW7U6r[7]RLD7K17 b[6]SW6W5r[6]RLD6L13 b[5]SW5W6r[5]RLD5M13 b[4]SW4U5r[4]RLD4K14 b[3]SW3T5r[3]RLD3K13 b[2]SW2T4r[2]RLD2M20 b[1]SW1R4r[1]RLD1N20 b[0]SW0W4r[0]RLD0N19 clk时钟源Y18resetnS6P20 startS4P4 由于Minisys板上的时钟是100MHz,本设计需要降频至50MHz,为了获得一个稳定的降频时钟,本实验采用Vivado自带的PLL时钟来做降频。单击IP Catalog,并在打开的IP核库中双击Vivado Repository→FPGA Features and Design→Clocking→Clocking Wizard命令,如图511所示。 设置时钟IP核的时候,在Clocking Options选项卡中的Primitive选择PLL,Clocking Features选择Frequency Synthesis和Phase Alignment,Jitter Optimization中选择Balanced,如图512所示。 在Output Clocks选项卡,clk_out1行的Requested中改为50,Clocking Feedback的Source中选择Automatic Control OnChip,在Enable Optional Input中去掉reset和locked前面的勾,如图513所示。 图511选择时钟IP核 图512设置Clocking Options 图513设置Output Clocks 将前面做好的divu IP核调进来,在divux16.v中实例化这两个IP核,注意判断除数为0的情况,另外还要注意启动除法器需要start信号有效而busy信号无效时才行。 divux16模块的端口定义如下: 1.module divux16( 2.input [15:0] a, 3.input [7:0] b, 4.input clk, 5.input start, 6.input reset, 7.output [15:0] q, 8.output [7:0] r 9.); 10.… 在C:\sysclassfiles\orgnization\Ex_3\divux16中给出了初始设计文件和约束文件,供读者完善和使用。 5.3.2有符号数除法器的设计 1. 实验目的 (1) 掌握不恢复余数除法的原理。 (2) 巩固负数补码的表示方法。 (3) 理解不恢复余数的有符号数除法器的原理。 2. 实验内容 使用Verilog HDL语言行为级描述方法,根据不恢复余数除法的原理实现被除数数据宽度在8、16、32位之间可变的有符号数除法器div,并请通过编写div_sim文件,仿真验证有符号数除法器。最后,请将有符号数除法器封装成IP核。 3. 实验预习 巩固不恢复余数除法的算法。有符号数除法与无符号数除法的最大区别是要考虑商的符号,如果被除数和除数的符号相同,则商为正数,否则商为负数。 有符号除法可以在无符号不恢复余数除法的基础上做改进,最简单的办法是将被除数和除数中的负数转换成负数,然后用无符号不恢复余数除法算出结果。这样算出的结果都是正数,然后在根据商的符号做调整,如果商的符号位是1(负数),则对商求补即可。 4. 实验步骤 本实验的资源包在C:\sysclassfiles\orgnization\Ex_3\div中给出了初始设计文件div.v和仿真文件div_sim.v,供读者完善和使用。 (1) 创建项目,完成设计。 创建div项目,设计文件中模块名为div。 读者可以利用不恢复余数除法算法完善资源包中如下的初始设计来实现div模块。 1.`timescale 1ns / 1ps 2.module div 3. #(parameter WIDTH=8) 4.( 5.input [WIDTH-1:0] a, 6.input [WIDTH/2-1:0] b, 7.input clk, 8.input start, 9.input resetn, 10.output [WIDTH-1:0] q,// 商 11.output [WIDTH/2-1:0] r,// 余数 12.output reg busy // 正在做除法 13.); 14. 15.//添加自己的代码 16.… 17.endmodule 其中,被除数为a,除数为b(宽度是被除数的一半),商为q,余数为r(b和r的宽度是被除数的一半,商的数据宽度同被除数)。作为辅助信号,有时钟信号clk,启动除法操作的信号start,复位信号resetn。另外,除法器应该有一个输出信号busy,当除法运算开始时,该信号为高电平,除法运算结束后,busy转为低电平。商和余数的符号规则是,被除数和除数符号相同,商的符号为正,否则商的符号为负。余数的符号同被除数。 (2) 仿真验证。 下面的代码是仿真文件的一个案例,读者可以在div_sim.v中找到它。 1.module div_sim(); 2.// input 3.reg [31:0] a=32'd16; 4.reg [15:0] b=16'd4; 5.reg clk=0; 6.reg start=0; 7.reg resetn=0; 8.// output 9.wire [31:0] q; 10.wire [15:0] r; 11.wire busy; 12.div #(32) u(a,b,clk,start,resetn,q,r,busy); 13.initial begin 14. #30 begin resetn=1;start=1;end 15. #50 start=0; 16. #1400 begin start=1; a=32'd18;b=16'd5;end 17. #50 start=0; 18. #1400 begin start=1; a=32'hffffffee;b=16'd5;end 19. #50 start=0; 20. #1400 begin start=1; a=32'd18;b=16'hfffb;end 21. #50 start=0; 22.end 23.always#20clk=~clk; 24.endmodule (3) 封装IP核。 将div模块封装成IP核,封装IP核的方法读者可以参考前面章节。 5. 思考与拓展 利用上一个实验中封装的数据宽度可变的有符号数除法器IP核div,采用元件例化的方法实现一个16位有符号数除法器divx16,并下载到Minisys实验板上进行验证。被除数a[15:0]接SW23~SW8,除数b[7:0]接SW7~SW0,商q[15:0]接GLD7~GLD0,YLD7~YLD0,余数r[7:0]接RLD7~RLD0,clk接Y18,resetn接P20,start接到S4按键开关。具体的引脚分配如表57所示。 表5716位有符号数除法器的引脚分配 信号器件引脚信号器件引脚 a[15]SW23Y9q[15]YLD7M17 a[14]SW22W9q[14]YLD6M16 a[13]SW21Y7q[13]YLD5M15 a[12]SW20Y8q[12]YLD4K16 a[11]SW19AB8q[11]YLD3L16 a[10]SW18AA8q[10]YLD2L15 a[9]SW17V8q[9]YLD1L14 a[8]SW16V9q[8]YLD0J17 a[7]SW15AB6q[7]GLD7F21 a[6]SW14AB7q[6]GLD6G22 a[5]SW13V7q[5]GLD5G21 a[4]SW12AA6q[4]GLD4D21 续表 信号器件引脚信号器件引脚 a[3]SW11Y6q[3]GLD3E21 a[2]SW10T6q[2]GLD2D22 a[1]SW9R6q[1]GLD1E22 a[0]SW8V5q[0]GLD0A21 b[7]SW7U6r[7]RLD7K17 b[6]SW6W5r[6]RLD6L13 b[5]SW5W6r[5]RLD5M13 b[4]SW4U5r[4]RLD4K14 b[3]SW3T5r[3]RLD3K13 b[2]SW2T4r[2]RLD2M20 b[1]SW1R4r[1]RLD1N20 b[0]SW0W4r[0]RLD0N19 clk时钟源Y18resetnS6P20 startS4P4 divx16模块的端口定义如下: 1.module divx16( 2.input [15:0] a, 3.input [7:0] b, 4.input clk, 5.input start, 6.input reset, 7.output [15:0] q, 8.output [7:0] r 9.); 10.… 在C:\sysclassfiles\orgnization\Ex_3\divx16中给出了初始设计文件和约束文件,供读者完善和使用。 5.4运算器的设计 运算器是微处理器中的一个重要部件,负责完成算数运算、逻辑运算、移位运算等功能。本节的主要任务就是用不同的方法设计能进行多种运算的运算器。本节所有实验在资源包中提供的初始文档均在C:\sysclassfiles\orgnization\Ex_4中。 5.4.18位运算器的设计 1. 实验目的 (1) 前面设计的IP核进行综合应用。 (2) 初步理解指令格式。 (3) 学会设计运算器的控制逻辑。 2. 实验内容 利用前面章节设计好的可变数据位数的加减法器IP核、可配置输入端数目和数据位宽的与、或、非、异或门IP核、多路选择器1P核以及8位桶形移位器IP核作为基本元件,利用Verilog HDL的结构化描述方法与数据流描述方法完成一个8位的运算器alu8_verilog,其功能如表58所示。其中op[3:0]是操作码,输入的8位操作数分别是a[7:0]和b[7:0],运算结果是res[7:0],另外输出还有进位标志cf,有符号数溢出标志of,结果为0标志zf以及有符号数符号标志sf。 表588位运算器的功能定义 op[3]op[2]op[1]op[0]功能 00×0res=a+b,输出有cf,of,zf,sf 00×1res=a-b,输出有cf,of,zf,sf 0100res=a and b,输出有cf=0,of=0,zf,sf 0101res=a or b,输出有cf=0,of=0,zf,sf 0110res=~a,输出有cf=0,of=0,zf,sf 0111res=a xor b,输出有cf=0,of=0,zf,sf 1×00res=a逻辑右移b[2:0]位,cf=0,of=0,zf,sf 1×01res=a算数右移b[2:0]位,cf=0,of=0,zf,sf 1×1×res=a左移b[2:0]位,cf=0,of=0,zf,sf 3. 实验预习 认真复习可变数据位数的加减法器IP核、可配置输入端数目和数据位宽的与、或、非、异或门IP核、多路选择器1P核以及8位桶形移位器IP核的实现方法,尤其是它们的端口定义,以便实例化的时候能进行正确的连接。 4. 实验步骤 本实验的资源包在C:\sysclassfiles\orgnization\Ex_4\alu8_verilog中给出了初始设计文件alu8_verilog.v、仿真文件alu8_verilog_sim.v和约束文件alu8_verilog.xdc,供读者完善和使用。 1) 创建项目 创建alu8_verilog项目,设计文件中模块名为alu8_verilog。 2) 导入IP核 (1) 将以前做过的IP核调入到项目的IP核库中。 (2) 通过IP Catalog将需要使用的IP核以8位数据宽度,两输入形式添加到project中。 3) 设计好8选1的选择端的逻辑电路 8选1的选择端只有3位,因此,要将4位的op转换成3位的选择端SEL[2:0]。表59中定义了SEL[2:0]各种取值的含义。请读者自行设计SEL各位的逻辑表达式,然后用数据流方式描述。表中SEL为010和011两个码没有操作,可以将其中一个SEL码分给桶形移位器,注意桶形移位器的端口定义,它自己能够选择是哪种移位。 表59SEL[2:0]的含义 SEL[2:0]含义SEL[2:0]含义 000选加减法运算100选与运算结果 001选移位运算结果101选或运算结果 010—110选非运算结果 011—111选异或运算结果 4) 利用结构化描述方式,将各个IP模块的信号进行连接。 读者可以在如下的初始设计文件alu8_verilog.v的基础上加以完善。 1.`timescale 1ns / 1ps 2.module alu8_verilog( 3.input [7:0] a, 4.input [7:0] b, 5.input [3:0] op, 6.output [7:0] res, 7.output cf, 8.output ovf, 9.output zf, 10.output sf 11.); 12.wire [7:0] res1,res2,res3,res4,res5,res6; 13.wire cf1,of1,sf1,zf1; 14.wire [2:0] sel; 15.//sel[2:0] 16.//000 addsub 17.//001 shift 18.//010 -- 19.//011 -- 20.//100 and 21.//101 or 22.//110 not 23.//111 xor 24.//添加自己的代码 25.… 26. 27.endmodule 5) 进行仿真 用下面的仿真文件,可以得到如图514所示的波形。 1.module alu8_verilog_sim( ); 2.// input 3.reg [7:0] a=8'h16; 4.reg [7:0] b=8'h12; 5.reg [3:0] op=4'b0000; // 加法 6.//output 7.wire [7:0] res; 8.wire cf; 9.wire ovf; 10.wire sf; 11.wire zf; 12.// initial 13.alu8_verilog U (.a(a),.b(b),.op(op),.res(res),.cf(cf), 14..ovf(ovf),.sf(sf),.zf(zf)); 15.initial begin 16.#200 op=4'b0001; // 减法 17.#200 begin a=8'h7f; b=8'h2; op=4'b0000; end 18.#200 begin a=8'hff; b=8'h2; op=4'b0000; end 19.#200 begin a=8'h16; b=8'h17; op=4'b0001; end 20.#200 begin a=8'hf0; b=8'h0f; op=4'b0100; end// 与 21.#200 begin a=8'hf0; b=8'h0f; op=4'b0101; end// 或 22.#200 begin a=8'hf0; b=8'h0f; op=4'b0110; end // 非 23.#200 begin a=8'hff; b=8'hff; op=4'b0111; end // 异或 24.#200 begin a=8'hff; b=8'h03; op=4'b1000; end//逻辑右移 25.#200 begin a=8'hff; b=8'h03; op=4'b1001; end//算数右移 26.#200 begin a=8'hff; b=8'h03; op=4'b1010; end // 左移 27.end 28.endmodule 图5148位运算器仿真波形图 6) 综合、实现、并下载到Minisys实验板上 信号与引脚的对应关系是: a[7:0]接SW15~SW8,b[7:0]接SW7~SW0,op[3:0]接SW23~SW20,res[7:0]接GLD7~GLD0,cf接RLD4,of接RLD5,zf接RLD6,sf接RLD7。各信号对应的板上器件及其引脚如表510所示。 表510运算器alu8_verilog的引脚分配 信号器件引脚信号器件引脚 a[7]SW15AB6b[7]SW7U6 a[6]SW14AB7b[6]SW6W5 a[5]SW13V7b[5]SW5W6 a[4]SW12AA6b[4]SW4U5 a[3]SW11Y6b[3]SW3T5 a[2]SW10T6b[2]SW2T4 a[1]SW9R6b[1]SW1R4 a[0]SW8V5b[0]SW0W4 res[7]GLD7F21op[3]SW23Y9 续表 信号器件引脚信号器件引脚 res[6]GLD6G22op[2]SW22W9 res[5]GLD5G21op[1]SW21Y7 res[4]GLD4D21op[0]SW20Y8 res[3]GLD3E21sfRLD7K17 res[2]GLD2D22zfRLD6L13 res[1]GLD1E22ofRLD5M13 res[0]GLD0A21cfRLD4K14 5. 思考与拓展 读者尝试自己定义操作码,让运算器除了能做上述运算外,还能做乘法和除法(可能需要改变乘积和被除数的位宽)。 5.4.2用Block Design设计8位运算器 1. 实验目的 (1) 进一步熟悉Block Design的设计方法。 (2) 设计一个能进行算术运算(加、减法)、逻辑运算(与、或、非、异或)和移位运算(左移、逻辑右移、算术右移)的8位运算器。 2. 实验内容 利用前面章节设计好的可变数据位数的加减法器IP核、与、或、非、异或门IP核、8位桶形移位器IP核以及8选1多路选择器IP核作为基本元件,利用Vivado的Block Design完成一个8位的运算器alu8_bk。相关的功能、引脚对应等参见5.4.1节。 3. 实验预习 认真复习Block Design的设计方法。 认真复习可变数据位数的加减法器IP核、可配置输入端数目和数据位宽的与、或、非、异或门IP核、多路选择器1P核以及8位桶形移位器IP核的实现方法,尤其是它们的端口定义。 4. 实验步骤 本实验的资源包在C:\sysclassfiles\orgnization\Ex_4\alu8_blk中给出了仿真文件alu8_bk_sim.v和约束文件alu8_bk_wrapper.xdc,供读者使用。 (1) 创建项目。 创建alu8_blk项目。 (2) 导入IP核。 将以前做过的IP核调入到项目的IP核库中。 (3) 设计好8选1的选择端的逻辑电路。 根据表59中的设计好逻辑表达式,并调用基本门电路的IP核,实现这些逻辑表达式。 (4) 用线将各个IP模块的信号连接起来。 (5) 进行仿真、综合、实现和下载。 注意: 在使用Block Design时可能需要将一个总线拆分成单独的一根根信号线,这时需要用到Vivado Repository→Basic Elements→Slice IP核; 有时需要将几个信号线组合成一个总线,此时需要用到Vivado Repository→Basic Elements→Concat IP核。 5. 思考与拓展 认真比较一下用Verilog HDL语言和用Block Design设计电路的各自优缺点。 5.5存储器的扩展 存储器是计算机系统中非常重要的存放数据的器件,在计算机系统中,存储器主要有RAM和ROM(外存除外),本节主要利用Vivado自带的存储器IP核来做存储器的扩展实验。本节所有实验在资源包中提供的初始文档均在C:\sysclassfiles\orgnization\Ex_5中。 5.5.1使用IP核和存储器位扩展技术设计存储器 1. 实验目的 (1) 掌握利用Vivado的IP核设计和实现RAM存储器的方法。 (2) 掌握存储器位扩展的方法。 (3) 学会初始化存储器。 (4) 熟悉存储器的存取方法。 2. 实验内容 (1) 通过Vivado工具,利用它的IP核,构建4个16×2位的存储器。 (2) 利用位扩展技术将上述4个存储器组成1个16×8位的存储器。 (3) 通过仿真验证。 (4) 将设计封装成IP核。 3. 实验预习 认真复习计算机组成原理中关于存储器操作的相关内容以及存储器的位扩展技术。由于FPGA芯片内部是有Block RAM的,为了充分利用芯片资源,所以实验中采用了Vivado的IP核来组成基础存储单元,其访问方法大致和常用存储芯片类似。但也希望读者能够阅读Vivado存储器IP核的相关文档。 4. 实验步骤 本实验的资源包在C:\sysclassfiles\orgnization\Ex_5\ram16x8中给出了初始设计文件ram16x8.v、仿真文件ram16x8_sim.v和约束文件ram16x8.xdc,供读者完善和使用。 1) 创建一个项目ram16x8 根据4.1节的实验步骤创建项目ram16x8。 2) 创建RAM初始化文件 在ram16x8文件夹下,用记事本创建4个文件,分别是ram16x21.coe、ram16x22.coe、ram16x23.coe和ram16x24.coe。内容相同,均如下: memory_initialization_radix=2; memory_initialization_vector= 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 该文件中,“memory_initialization_radix=2”表明数据是按照二进制方式给出的,“memory_initialization_vector=00……”给出具体的数据(16个2位的数据组成16×2的存储体)。 3) 利用IP核创建16×2位的存储器 图515Project Manager (1) 在图515中双击IP Catalog。 (2) 在打开的IP Catalog窗口中双击图516中高亮部分(Block Memory Generator)。 (3) 按照图517所示设置Basic选项卡,注意,Component Name为ram16x2_1,选择单端口RAM,采用最小面积算法。 (4) 按照图518所示设置Port A Options选项卡,数据位宽是2,共16个数据,形成16×2的存储单元,操作模式是写优先,端口具有允许端的使能信号,还有一个写允许信号,去掉Primitives Output Register前面的勾。 (5) 按照图519所示设置Other Options选项卡。主要是要设置初始化文件,这里初始化文件指向刚刚建立的ram16x21.coe。 (6) 单击OK按钮,在图520中单击Generate,生成IP核后(可能需要的时间比较长),在图521中单击OK按钮。 (7) 按照上述方法分别生成ram16x2_2、ram16x2_3和ram16x2_4这3个存储器,初始化文件分别是ram16x22.coe、ram16x23.coe和ram16x24.coe。 4) 完善Verilog文件ram16X8.v 其端口定义如下: 1.module ram16x8( 2.input clk, //时钟信号 3.input we,   //写使能 4.input en,   //使能信号 5.input [3:0] addr,   //地址线(16个存储单元,因此只要4根地址线) 6.input [7:0] datain,  // 输入数据线 7.output [7:0] dataout  // 输出数据线 8.); 图516IP Catalog 窗口 图517Basic选项卡 图518Port A Options选项卡 图519Other Options选项卡 图520产生IP核 图521产生IP核后的对话框 在该文件中元件例化4个16×2位模块,利用计算机组成原理课程中学到的存储器位扩展的方法扩展出一个16×8位的存储器。所谓存储器位扩展,就是将若干片位数较少的存储芯片通过并联增加位数,从而得到给定位宽的存储器。图522是位扩展结构示意图。 图522位扩展结构示意图 5) 设计仿真文件,对16×8位的存储器进行仿真测试 采用下面的ram16x8_sim.v仿真程序就可以得到图523所示的仿真波形。 1.module ram16x8_sim(); 2.//input 3.reg clk=0; 4.reg en=0; 5.reg we=0; 6.reg [3:0] addr=4'b0000; 7.reg [7:0] din=8'h00; 8.//output 9.wire [7:0] dout; 10.//instantiate the Unit under test 11.ram16x8 ut( 12..clk(clk), 13..we(we), 14..en(en), 15..addr(addr), 16..datain(din), 17..dataout(dout) 18.); 19.initial begin 20.#100begin we=1;en=1; addr=4'b0011; din=8'b10101010; end;//写地址3 21.#100begin addr=4'b0100; din=8'b01010101; end;  //写地址4 22.#100 begin addr=4'b0101; din=8'b10100101; end;  //写地址5 23.#100begin addr=4'b0110; din=8'b01011010; end;  //写地址6 24.#100begin we=0; en=0; addr=4'b0011; end;  //不允许操作 25.#100addr=4'b0100;//下面换地址,因为不允许操作,所以读出数据不变 26.#100addr=4'b0101; 27.#100addr=4'b0110; 28.#100en=1;//允许操作 29.#100addr=4'b0011;   //读地址3 30.#100addr=4'b0100;   //读地址4 31.#100addr=4'b0101;   //读地址5 32.#100addr=4'b0110;   //读地址6 33.#100addr=4'b0000;   //读地址0 34.#100begin en=0; addr=4'b0100; end;  //不允许操作 35.end 36.always#5clk=~clk; 37.endmodule 图52316×8存储器仿真波形 5. 思考与拓展 如果不用Vivado提供的IP核,采用REG型两维数组也可以做存储器,用这种方式实现一个16×8位的存储器,与本实验的方法相比,资源利用上有什么不同? 5.5.2使用IP核和存储器字扩展技术设计存储器 1. 实验目的 (1) 掌握存储器字扩展的方法。 (2) 学会初始化存储器。 (3) 熟悉存储器的存取方法。 2. 实验内容 (1) 通过Vivado工具,利用它的IP核,构建4个16×8位的存储器。 (2) 利用字扩展技术将上述4个存储器组成1个64×8位的存储器。 (3) 通过仿真、下板验证。 3. 实验预习 认真复习计算机组成原理中关于存储器操作的相关内容以及存储器的字扩展技术。由于FPGA芯片内部是有Block RAM的,为了充分利用芯片资源,所以实验中采用了Vivado的IP核来组成基础存储单元,其访问方法大致和常用存储芯片类似。但也希望读者能够阅读Vivado存储器IP核的相关文档。 4. 实验步骤 本实验的资源包在C:\sysclassfiles\orgnization\Ex_5\ram64x8中给出了初始设计文件ram64x8.v、仿真文件ram64x8_sim.v和约束文件ram64x8.xdc,供读者完善和使用。 (1) 创建一个项目ram64×8。 根据4.1节的实验步骤创建项目ram64x8。 (2) 创建RAM初始化文件。 在ram64x8文件夹下,用记事本创建4个文件,分别是ram16x81.coe、ram16x82.coe、ram16x83.coe和ram16x84.coe。内容相同,均为: memory_initialization_radix=16; memory_initialization_vector= 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00; 该文件中,memory_initialization_radix=16; 表明数据是按照十六进制给出的,memory_initialization_vector=00……给出具体的数据。 (3) 利用IP核创建16×8位的存储器。 (4) 完善Verilog HDL文件ram64x8.v。 其端口定义如下: 1.module ram64x8( 2.input clk,//时钟信号 3.input en, //使能信号 4.input we,//写信号 5.input [5:0] addr,//地址信号 6.input [7:0] din, //输入信号 7.output reg [7:0] dout //输出信号 8.); 所谓字扩展技术是指位数不变,容量扩充。图524是字扩展结构的示意图。 图524字扩展结构的示意图 (5) 设计仿真文件,对64×8位的存储器进行仿真测试。 采用以下仿真程序进仿真: 1.module ram64x8_sim(); 2.//input 3.reg clk=0; 4.reg en=0; 5.reg we=0; 6.reg [5:0] addr=6'd0; 7.reg [7:0] din=8'h00; 8.//output 9.wire [7:0] dout; 10.//instantiate the Unit under test 11.ram64x8 ut( 12..clk(clk), 13..we(we), 14..en(en), 15..addr(addr), 16..din(din), 17..dout(dout) 18.); 19.initial begin 20.#100begin we=1;en=1; addr=6'd10; din=8'b10101010; end; 21.#100begin addr=6'd20; din=8'b01010101; end; 22.#100begin addr=6'd40; din=8'b10100101; end; 23.#100begin addr=6'd60; din=8'b01011010; end; 24.#100begin we=0; en=0; addr=6'd10; end; 25.#100addr=6'd20; 26.#100addr=6'd40; 27.#100addr=6'd60; 28.#100en=1; 29.#100addr=6'd10; 30.#100addr=6'd20; 31.#100addr=6'd40; 32.#100addr=6'd60; 33.#100addr=6'd0; 34.#100begin en=0; addr=6'd20; end; 35.end 36.always#5clk=~clk; 37.endmodule 上面的仿真文件可以得到如图525所示的波形图。 图52564×8RAM仿真波形图 (6) 下板验证。 请按照表511所示分配引脚,然后实现、生成bit文件并下载到实验板上进行验证。 表511RAM 64×8的引脚分配 信号器件引脚信号器件引脚 datain[7]SW7U6dataout[7]GLD7F21 datain[6]SW6W5dataout[6]GLD6G22 datain[5]SW5W6dataout[5]GLD5G21 datain[4]SW4U5dataout[4]GLD4D21 datain[3]SW3T5dataout[3]GLD3E21 datain[2]SW2T4dataout[2]GLD2D22 datain[1]SW1R4dataout[1]GLD1E22 datain[0]SW0W4dataout[0]GLD0A21 addr[6]SW20Y8clkSW23Y9 addr[5]SW19AB8enSW22W9 addr[4]SW18AA8weSW21Y7 addr[3]SW17V8 addr[2]SW16V9 addr[1]SW15AB6 addr[0]SW14AB7 5. 思考与拓展 考虑用很小规模的存储模块通过位扩展和字扩展获得更大规模存储器的方法。