从零开始的 Verilog 生活

Vivado的使用

以实验书中给出的加法器文件为例

1. Design Source 中添加 adder.v 文件
  1. 打开 Vivado,创建一个新项目。

  2. 在项目中,右键点击 Design Sources,选择 Add Sources,然后选择 Add or create design sources

  3. 点击 Create File,输入文件名 adder.v,点击 OK

  4. adder.v 文件中编写代码

添加好文件以后,文件会产生向导,输入输出向导。但是这部分代码最终的目的是在代码段中生成程序的输入输出端口,理论上来说代码处编写即可,在文件中添加下面的内容

 module adder(
     input  [31:0] operand1,  //定义端口32位的输入
     input  [31:0] operand2,
     input         cin,  //来自低位的进位
     output [31:0] result,
     output        cout  //向高位的进位
     );
     assign {cout,result} = operand1 + operand2 + cin; //输出形式
 ​
 endmodule
2. 综合(Synthesis)

综合是将高层次的设计描述(如 Verilog 或 VHDL 代码)转换为低层次的逻辑门级网表(Gate-level Netlist)的过程。主要任务:

  • 逻辑优化:将代码转换为逻辑单元

  • 语法和逻辑的检查

  • 生成网表:生成中间文件(EDIF或NGC格式)

3. 实现(Implementation)

实现是将综合生成的逻辑网表映射到具体的 FPGA 硬件资源上,并生成最终的比特流文件(Bitstream)的过程。主要任务是:

  • 布局布线:将逻辑网表中的逻辑单元映射到 FPGA 的物理资源上(如 CLB、DSP、BRAM 等),并完成信号的路由。

  • 时序优化:确保设计的布局仍能满足时序要求

  • 生成比特流:生成可以下载到FPGA的配置文件

4. 检验逻辑是否正确的方式:
(1)仿真 创建虚拟信号(Simulation Sources)
  1. 右键点击 Simulation Sources,选择 Add Sources,然后选择 Add or create simulation sources

  2. 点击 Create File,输入文件名 testbench.v,点击 OK

  3. testbench.v 文件中编写代码

添加仿真文件,默认文件名为testbench.v .仿真文件也是Verilog文件,仿真文件自己内部构造输入,不需要对外添加。刚刚创建好以后,adder.v 是顶部文件,在testbench.v 文件中添加下面的内容:

 `timescale 1ns / 1ps   //仿真单位时间为1ns,精度为1ps 董老师给出的代码中没有这一行
 module testbench;
     // Inputs 这里的reg定义的变量以及wire输出的变量是为了后续的 adder 模块的输入输出
     reg [31:0] operand1;
     reg [31:0] operand2;
     reg cin;
     // Outputs
     wire [31:0] result;
     wire cout;
     // Instantiate the Unit Under Test (UUT) 给adder起了个别名 uut
     //这里的顺序要和adder中的一样 作用是实例化adder。
     //语法 .port_name1(external_signal1) 。实例化的时候,如果实例化了多个对象,就要起别名。
     //也可以位置端口连接,但是连接顺序必须与模块定义中的端口顺序完全一致
     adder uut (
         //模块定义的端口名称(外部信号(可以是reg、wire等信号)) operand1是之前adder文件中的,而括号内的是这个文件中的
         .operand1(operand1), 
         .operand2(operand2), 
         .cin(cin), 
         .result(result), 
         .cout(cout)
     );
     initial begin  //寄存器中的一定要有值,因此要先初始化
         // Initialize Inputs
         operand1 = 0;
         operand2 = 0;
         cin = 0;
         // Wait 100 ns for global reset to finish
         #100;
         // Add stimulus here
     end
     always #10 operand1 = $random;  //$random为系统任务,产生一个随机的32位数
     always #10 operand2 = $random;  //#10 表示等待10个单位时间(10ns),即每过10ns,赋值一个随机的32位数
     always #10 cin = {$random} % 2; //加了拼接符,{$random}产生一个非负数,除2取余得到0或1
 endmodule

仿真文件编写完毕以后,Simulation Souces 变成了顶层模块,走仿真的时候一定要是顶层模块。

注意:每次写完一个文件都跑一遍综合和实现。运行完以后,点击Simulation 中的 Run Simulation 进行仿真,出现波形图。进行缩放,拖动滚轮,右键 Radix 选择展示的进制

(2)试验箱

根据曾经的描述,可以画出定点实验加法 顶层模块图

观察 lcd_module.v 文件

顶层模块文件
  1. 右键点击 Design Sources,选择 Add Sources,然后选择 Add or create design sources

  2. 点击 Create File,输入文件名 adder_display.v,点击 OK

  3. adder_display.v 文件中编写代码

Design Source 中添加顶层模块文件。顶层模块代码如下,adder_display.v 代码,需要注意的是输入输出定义好,以及始终和 reset 信号。

 module adder_display(
     //时钟与复位信号,
     input clk,
     input resetn,    //后缀"n"表示低电平有效
 ​
     //拨码开关,用于选择输入数和产生cin
     input input_sel, //0:输入为加数1(add_operand1);1:为加数2(add_operand2)
     input sw_cin,
 ​
      //led灯,用于显示cout
     output led_cout,
     
     //触摸屏相关接口,不需要更改
     output lcd_rst,
     output lcd_cs,
     output lcd_rs,
     output lcd_wr,
     output lcd_rd,
     inout[15:0] lcd_data_io,
     output lcd_bl_ctr,
     inout ct_int,
     inout ct_sda,
     output ct_scl,
     output ct_rstn
     );
 //上面的都是adder_display的对外端口,先买你的是内部的原理,这里是把加法器接好了
  //-----{调用加法模块}begin
     reg  [31:0] adder_operand1;
     reg  [31:0] adder_operand2;
     wire        adder_cin;
     wire [31:0] adder_result  ;
     wire        adder_cout;
     adder adder_module(
         .operand1(adder_operand1),
         .operand2(adder_operand2),
         .cin     (adder_cin     ),
         .result  (adder_result  ),
         .cout    (adder_cout    )
     );
     //输入的端口接到加法器的输入的端口上,输出接到输出的端口上,assign是接线
     assign adder_cin = sw_cin;
     assign led_cout  = adder_cout;
  //-----{调用加法模块}end
 ​
  //---------------------{调用触摸屏模块}begin--------------------//
  //-----{实例化触摸屏}begin
  //此小节不需要更改
     reg         display_valid;
     reg  [39:0] display_name;//五个字符,带上了五个ASCII码,一个8位,一共40
     reg  [31:0] display_value;//数字都是32位的
     wire [5 :0] display_number;//刷新0~44,则一共需要5个
     wire        input_valid;
     wire [31:0] input_value;
 ​
     //实例化LCD屏
     lcd_module lcd_module(
         .clk            (clk           ),   //10Mhz
         .resetn         (resetn        ),
 ​
          //调用触摸屏的接口
         .display_valid  (display_valid ),
         .display_name   (display_name  ),
         .display_value  (display_value ),
         .display_number (display_number),
         .input_valid    (input_valid   ),
         .input_value    (input_value   ),
 ​
          //lcd触摸屏相关接口,不需要更改
         .lcd_rst        (lcd_rst       ),
         .lcd_cs         (lcd_cs        ),
         .lcd_rs         (lcd_rs        ),
         .lcd_wr         (lcd_wr        ),
         .lcd_rd         (lcd_rd        ),
         .lcd_data_io    (lcd_data_io   ),
         .lcd_bl_ctr     (lcd_bl_ctr    ),
         .ct_int         (ct_int        ),
         .ct_sda         (ct_sda        ),
         .ct_scl         (ct_scl        ),
         .ct_rstn        (ct_rstn       )
     ); 
     //-----{实例化触摸屏}end
 ​
 //LCD屏和加法器连线
 //-----{从触摸屏获取输入}begin
 //根据实际需要输入的数修改此小节,
 //建议对每一个数的输入,编写单独一个always块
     
 //当input_sel为0时,表示输入数为加数1,即operand1
     always @(posedge clk) //时钟触发
     begin
         if (!resetn) //当前要不要清零
         begin
             adder_operand1 <= 32'd0;
         end
         else if (input_valid && !input_sel) //检查当前的输入是否点了OK,是不是要输入第一个数
         begin
             adder_operand1 <= input_value; //如果是,则传入到operand1中
         end
     end
     
     //当input_sel为1时,表示输入数为加数2,即operand2
     always @(posedge clk)
     begin
         if (!resetn)
         begin
             adder_operand2 <= 32'd0;
         end
         else if (input_valid && input_sel)
         begin
             adder_operand2 <= input_value;
         end
     end
 //-----{从触摸屏获取输入}end
 ​
 //输出到LCD屏上
 //-----{输出到触摸屏显示}begin
 //根据需要显示的数修改此小节,
 //触摸屏上共有44块显示区域,可显示44组32位数据
 //44块显示区域从1开始编号,编号为1~44,
     always @(posedge clk)
     begin
         case(display_number)  //所有的44个位置,但是这次只用3个
             6'd1 :  //6表示位宽,d为十进制,2为常量2
             begin
                 display_valid <= 1'b1;
                 display_name  <= "ADD_1"; //五个字符
                 display_value <= adder_operand1;  //第一个数
             end
             6'd2 :
             begin
                 display_valid <= 1'b1;
                 display_name  <= "ADD_2";
                 display_value <= adder_operand2;
             end
             6'd3 :
             begin
                 display_valid <= 1'b1;
                 display_name  <= "RESUL";
                 display_value <= adder_result;
             end
             default :
             begin
                 display_valid <= 1'b0;  //1位宽的常量二进制数0
                 display_name  <= 40'd0;  //40位宽
                 display_value <= 32'd0; //32位宽
             end
         endcase
     end
 //-----{输出到触摸屏显示}end
  //----------------------{调用触摸屏模块}end---------------------//
 endmodule
 ​

使用源代码的时候一定要用写字板打开,记事本会乱码,直接导入的时候会对别的项目直接进行更改。添加完文件并且保存以后,会发现顶层模块变成了 adder_display

LCD模块

此时缺少 lcd_module 模块,需要我们直接导入出来,因为此时这个模块是打包好的,因此要手动导入,添加模块,点击 add design source 结尾文件为.dcp

excel 中为接口对应的控制信号编号。

导入 lcd_module 模块后,进行综合实现,实现结束以后,点击 Implemented Design ,可以看到设备的预览图,右上角的 Default Layout 布局改为 IO Planing 会更加的直观。可以看到每一个信号的横纵坐标,即为接口编号,接下来要做的就是让顶层模块的端口接入到试验箱上。去写约束文件

约束文件
  1. 右键点击 Constraints,选择 Add Sources,然后选择 Add or create constraints

  2. 点击 Create File,输入文件名 adder.xdc,点击 OK

  3. adder.xdc 文件中编写以下代码,实现的时候,请将双斜杠注释删去

 //对每个信号绑定实验向上的位置
 set_property PACKAGE_PIN AC19 [get_ports clk]  //将时钟信号,绑定到AC19信号当中
 set_property PACKAGE_PIN H7   [get_ports led_cout]
 set_property PACKAGE_PIN Y3   [get_ports resetn]
 set_property PACKAGE_PIN AC21 [get_ports input_sel]
 set_property PACKAGE_PIN AD24 [get_ports sw_cin]
 //给每个信号定义标准电压为3.3伏
 set_property IOSTANDARD LVCMOS33 [get_ports clk]
 set_property IOSTANDARD LVCMOS33 [get_ports led_cout]
 set_property IOSTANDARD LVCMOS33 [get_ports resetn]
 set_property IOSTANDARD LVCMOS33 [get_ports input_sel]
 set_property IOSTANDARD LVCMOS33 [get_ports sw_cin]
 //其他的内容直接复制过来
 #lcd
 set_property PACKAGE_PIN J25 [get_ports lcd_rst]
 set_property PACKAGE_PIN H18 [get_ports lcd_cs]
 set_property PACKAGE_PIN K16 [get_ports lcd_rs]
 set_property PACKAGE_PIN L8 [get_ports lcd_wr]
 set_property PACKAGE_PIN K8 [get_ports lcd_rd]
 set_property PACKAGE_PIN J15 [get_ports lcd_bl_ctr]
 set_property PACKAGE_PIN H9 [get_ports {lcd_data_io[0]}]
 set_property PACKAGE_PIN K17 [get_ports {lcd_data_io[1]}]
 set_property PACKAGE_PIN J20 [get_ports {lcd_data_io[2]}]
 set_property PACKAGE_PIN M17 [get_ports {lcd_data_io[3]}]
 set_property PACKAGE_PIN L17 [get_ports {lcd_data_io[4]}]
 set_property PACKAGE_PIN L18 [get_ports {lcd_data_io[5]}]
 set_property PACKAGE_PIN L15 [get_ports {lcd_data_io[6]}]
 set_property PACKAGE_PIN M15 [get_ports {lcd_data_io[7]}]
 set_property PACKAGE_PIN M16 [get_ports {lcd_data_io[8]}]
 set_property PACKAGE_PIN L14 [get_ports {lcd_data_io[9]}]
 set_property PACKAGE_PIN M14 [get_ports {lcd_data_io[10]}]
 set_property PACKAGE_PIN F22 [get_ports {lcd_data_io[11]}]
 set_property PACKAGE_PIN G22 [get_ports {lcd_data_io[12]}]
 set_property PACKAGE_PIN G21 [get_ports {lcd_data_io[13]}]
 set_property PACKAGE_PIN H24 [get_ports {lcd_data_io[14]}]
 set_property PACKAGE_PIN J16 [get_ports {lcd_data_io[15]}]
 set_property PACKAGE_PIN L19 [get_ports ct_int]
 set_property PACKAGE_PIN J24 [get_ports ct_sda]
 set_property PACKAGE_PIN H21 [get_ports ct_scl]
 set_property PACKAGE_PIN G24 [get_ports ct_rstn]
 ​
 set_property IOSTANDARD LVCMOS33 [get_ports lcd_rst]
 set_property IOSTANDARD LVCMOS33 [get_ports lcd_cs]
 set_property IOSTANDARD LVCMOS33 [get_ports lcd_rs]
 set_property IOSTANDARD LVCMOS33 [get_ports lcd_wr]
 set_property IOSTANDARD LVCMOS33 [get_ports lcd_rd]
 set_property IOSTANDARD LVCMOS33 [get_ports lcd_bl_ctr]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[0]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[1]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[2]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[3]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[4]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[5]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[6]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[7]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[8]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[9]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[10]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[11]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[12]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[13]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[14]}]
 set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[15]}]
 set_property IOSTANDARD LVCMOS33 [get_ports ct_int]
 set_property IOSTANDARD LVCMOS33 [get_ports ct_sda]
 set_property IOSTANDARD LVCMOS33 [get_ports ct_scl]
 set_property IOSTANDARD LVCMOS33 [get_ports ct_rstn]

约束文件,建立好后就可以进行上箱了

生成位流文件

控制栏拉到最下面,看到 Generate Bitstream 生成位流文件,此时需要重新跑综合和实现。

准备试验箱

一定要将箱子里的线通过小口拉出来再连到电脑上,防止线被夹断。旁边的红色功能为开关。

  1. 试验箱供上电

  2. USB插口插在自己的电脑上

一切准备就绪以后,让试验箱找位流文件,generate stream 下面有一个,open Hardware manager(打开文件管理器) 随后右上角会出现绿色的条,点击 Open target 便会自动开始连接试验箱。找不到的话,一定要断开链接以后,再去链接。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值