跳转至

实验平台的顶层项目

第八章 实验平台的顶层项目

实验平台的顶层项目是一个Vivado的工程项目。教学实验平台提供的顶层项目匹配了实验平台的硬件环境,可以提供同学们直接使用。顶层项目规定了整个FPGA芯片的输入输出,可以直接看到硬件的连线情况。顶层项目也规定了约束,这样同学们自己可以不用写约束文件,直接用顶层项目的约束文件即可。顶层项目还提供了一个仿真的测试环境,包括外围的各个硬件的仿真模块,这样同学们不需要硬件平台也可以进行开发和测试。注意,硬件开发中仿真是非常重要的技能,先在仿真器中跑通能够大大减少调试的时间。

顶层项目文件的位置

Thinpad的顶层项目文件可以从下面的网址中下载:

git clone https://github.com/thu-cs-lab/thinpad_top.git

这里面的项目文件就可以直接打开了。

文件项目的顶层文件(Verilog源代码)是:

thinpad_top\thinpad_top.srcs\sources_1\new\thinpad_top.v

如何在Windows下面打开源代码文件

从git库下载下来的顶层项目文件的编码是utf-8的,在Windows下面直接使用Vivado打开会出现乱码(Linux下面不会)。这是因为Windows下面的默认编码页是GBK(或者GB2312,或者GB-18030,都是兼容的)。

需要用别的编辑器给重新用GBK编码给保存一下。可以使用任意一种支持多种编码的编辑器就可以。VS Code可以达到这个目的。

打开这个文件,使用control+shit+p打开菜单,输入encoding,选择change file encoding,就可以完成转码操作。

下面是使用VSCode打开utf-8编码的文件,然后转存为GBK编码的过程。

使用Vivado打开,看到的是乱码。

直接使用VSCode打开,看到的文字是正常的。

输入control+shift+p,打开菜单,输入encoding,点击Change File Encoding。

选择Save with Encoding

选择GBK编码(或者GB18030编码,它们是兼容的。)

再使用Vivado打开的时候就可以看到正常的文字,不是乱码了。

上述过程完成了thinpad_top.v的文件编码的转换。另外一个需要转码的文件是srcs/sim_1/new/tb.sv文件,这个是仿真模块文件,构造了教学实验板的仿真环境。

下面是使用VSCode打开一个为GBK编码文件的过程。

VSCode默认是utf-8编码的,打开GBK编码的文件为乱码。换一种编码方式打开就可以看到正常的文字。

打开文件,发现是乱码。输入ctrl+shift+p,输入encoding,点击Change File Encoding。

选择Reopen with Encoding

选择使用Simplified Chinese(GB2312)打开,或者使用GBK打开,两者在常用字上是兼容的。

此时就可以看到正常的文字。

代码中的一些相关内容解释

`default_nettype none 这是建议的做法。在Verilog中,所有没有被定义的标记label都被默认认为是wire类型的。但是,这种默认的行为是非常危险的,比如在信号名字上出现拼写错误不会被探测出来。因此,建议在所有的源文件的开始加上这一句,取消默认行为。

下面的代码是7段数码管的输出,比较简单:

module SEG7_LUT (oSEG1, iDIG);
    input   wire[3:0]   iDIG;  // 输入数据
    output  wire[7:0]   oSEG1; // 7段数码管的输出
    reg     [6:0]       oSEG;
    always @(iDIG) begin
        case(iDIG)
        4'h1: oSEG = 7'b1110110;    // ---t----
        4'h2: oSEG = 7'b0100001;    // |      |
        4'h3: oSEG = 7'b0100100;    // lt    rt
        4'h4: oSEG = 7'b0010110;    // |      |
        4'h5: oSEG = 7'b0001100;    // ---m----
        4'h6: oSEG = 7'b0001000;    // |      |
        4'h7: oSEG = 7'b1100110;    // lb    rb
        4'h8: oSEG = 7'b0000000;    // |      |
        4'h9: oSEG = 7'b0000110;    // ---b----
        4'ha: oSEG = 7'b0000010;
        4'hb: oSEG = 7'b0011000;
        4'hc: oSEG = 7'b1001001;
        4'hd: oSEG = 7'b0110000;
        4'he: oSEG = 7'b0001001;
        4'hf: oSEG = 7'b0001011;
        4'h0: oSEG = 7'b1000000;
        endcase
    end
    assign oSEG1 = {~oSEG,1'b0}; // 1点亮,0不点亮
endmodule

错误和警告

编译出现的错误和警告是非常正常的现象,大部分情况下是对Verilog不熟悉造成的,和一般编程一样,可以依据不同的情况进行处理。

下面的几个错误是教师在进行实验的时候曾经出现过的错误和解决办法,同学们还是需要依据具体的情况来解决。

错误:

原因:凡是在always中赋值的信号都要声明为reg,reg最终也不一定综合成register。

警告:

对应于这一语句

set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_11M0592_IBUF]

警告说明的是:没用到的约束。

可能会出现下面的错误:

ERROR: [DRC NSTD-1] Unspecified I/O Standard: 12 out of 12 logical ports use I/O standard (IOSTANDARD) value 'DEFAULT', instead of a user assigned specific value. This may cause I/O contention or incompatibility with the board power or connectivity affecting performance, signal integrity or in extreme cases cause damage to the device or the components to which it is connected. To correct this violation, specify all I/O standards. This design will fail to generate a bitstream unless all logical ports have a user specified I/O standard value defined. To allow bitstream creation with unspecified I/O standard values (not recommended), use this command: set_property SEVERITY {Warning} [get_drc_checks NSTD-1]. NOTE: When using the Vivado Runs infrastructure (e.g. launch_runs Tcl command), add this command to a .tcl file and add that file as a pre-hook for write_bitstream step for the implementation run. Problem ports: RxD_data[7:0], RxD, RxD_clear, RxD_data_ready, and clk.

ERROR: [DRC UCIO-1] Unconstrained Logical Port: 12 out of 12 logical ports have no user assigned specific location constraint (LOC). This may cause I/O contention or incompatibility with the board power or connectivity affecting performance, signal integrity or in extreme cases cause damage to the device or the components to which it is connected. To correct this violation, specify all pin locations. This design will fail to generate a bitstream unless all logical ports have a user specified site LOC constraint defined. To allow bitstream creation with unspecified pin locations (not recommended), use this command: set_property SEVERITY {Warning} [get_drc_checks UCIO-1]. NOTE: When using the Vivado Runs infrastructure (e.g. launch_runs Tcl command), add this command to a .tcl file and add that file as a pre-hook for write_bitstream step for the implementation run. Problem ports: RxD_data[7:0], RxD, RxD_clear, RxD_data_ready, and clk.

原因是:

顶层的信号名称跟约束文件中不一样。

所以它认为这个信号没有被约束。

一个解决办法:从项目中删除无关的文件(使用一个空的thinpad_top项目,重新拷贝编写的代码)。

[DRC LUTLP-1] Combinatorial Loop Alert: 1 LUT cells form a combinatorial loop. This can create a race condition. Timing analysis may not be accurate. The preferred resolution is to modify the design to remove combinatorial logic loops. If the loop is known and understood, this DRC can be bypassed by acknowledging the condition and setting the following XDC constraint on any one of the nets in the loop: 'set_property ALLOW_COMBINATORIAL_LOOPS TRUE [get_nets <myHier/myNet>]'. One net in the loop is dpy1_OBUF[3]. Please evaluate your design. The cells in the loop are: dpy1_OBUF[7]_inst_i_2.

问题:发现了组合逻辑环路

一个解决办法:always \$*,这里面列出需要响应的信号,要不然会出问题,这种情况下不要用\$*。

锁相环电路

这里最后一部分介绍一下锁相环电路,可以用于生成不同频率的时钟。锁相环电路在前面几个实验中不是必须的(但是建议可以用上,以便对后面的实验带来帮助。)下面的一段文字摘自wikipedia。

锁相环电路(PLL: Phase-locked loops)是一种利用反馈(Feedback)控制原理实现的频率及相位的同步技术,其作用是将电路输出的时钟与其外部的参考时钟保持同步。当参考时钟的频率或相位发生改变时,锁相回路会检测到这种变化,并且通过其内部的反馈系统来调节输出频率,直到两者重新同步,这种同步又称为"锁相"(Phase-locked)。

关于锁相环电路的原理可以参考网络上的内容或者其它的教科书,在实验中,只需要使用锁相环电路来生成所需的时钟信号即可。

生成特定的时钟信号的原因是所有的电路都是有延迟的,而在处理器中一个节拍中的所有的电路元件的信号需要稳定之后才能够进入下一个节拍。在实验环境中,SRAM的延迟大约为20ns,正好是50MHz的频率。因此如果是直接用50MHz的频率运行处理器的话,电信号是不能稳定的,必须要使用分频的方式来接入低一点频率的信号。

在顶层项目文件中有实例的代码,需要仔细阅读以下的代码。

// PLL分频示例
wire locked, clk_10M, clk_20M;
pll_example clock_gen (
  // Clock in ports
  .clk_in1(clk_50M),  // 外部时钟输入
  // Clock out ports
  .clk_out1(clk_10M), // 时钟输出1,频率在IP配置界面中设置
  .clk_out2(clk_20M), // 时钟输出2,频率在IP配置界面中设置
  // Status and control signals
  .reset(reset_btn), // PLL复位输入
  .locked(locked)    // PLL锁定指示输出,"1"表示时钟稳定,
                     // 后级电路复位信号应当由它生成(见下)
);

reg reset_of_clk10M;
// 异步复位,同步释放,将locked信号转为后级电路的复位reset_of_clk10M
always@(posedge clk_10M or negedge locked) begin
    if(~locked) reset_of_clk10M <= 1'b1;
    else        reset_of_clk10M <= 1'b0;
end
always@(posedge clk_10M or posedge reset_of_clk10M) begin
    if(reset_of_clk10M)begin
        // Your Code
    end
    else begin
        // Your Code
    end
end

上面的模块进行了分频的演示,输入是50MHz,输出是两个时钟信号,一个是10MHz的信号,一个是20MHz的信号。这样在后续的电路中就可以使用这两个时钟信号了。

需要注意的是,锁相环电路的输出中有一个.locked(locked)的信号。这个信号说明输出的时钟是否稳定。在后续的电路中,必须要依赖这个信号来使用时钟。上述的代码生成reset_of_clk10M中指明了这一点。这样,后续代码中只需要这个信号即可。

always@(posedge clk_10M or posedge reset_of_clk10M) begin
    if(reset_of_clk10M)begin
        // Your Code,这一段写初始化的代码,一般即为状态机的初始状态。
    end
    else begin
        // Your Code,这一段写正常的工作代码,驱动状态机的执行。
    end
end

下面看一下如何生成新的时钟信号:

双击clock_gen:

进入时钟配置环境,其它不用动,直接进入output即可:

可以加一路的时钟信号输出,设置为5MHz即可:

可以看到,该器件增加了一路的输出:clk_out3:

生成所需的IP核:

完成:

下面就可以修改源代码,增加一路时钟输出了。

之后的电路接入对应的信号,就可以依据所需要的频率执行。


最后更新: 2021年10月25日