浅谈《数字逻辑设计》课程中一种轻量化的Verilog编写环境配置

浅谈《数字逻辑设计》课程中一种轻量化的Verilog编写环境配置


副标题:并以此为例探讨大AI时代学生的AI使用素养

更新

  1. 改变了一些格式以及用语
  2. 增加了一些提示
  3. 列出了相关网站、下载链接等
  4. 更改文件结构将 powershell 代码存为 .ps1 文件并存入 \scripts 文件夹内,文件结构更为合理。并修改了相关表述。
  5. Github : https://github.com/zsj6315/-CS-Logic-and-Computer-Design-Fundamentals-Labs

0 前言

好吧其实本文并没有这么严肃... qwq

背景1:数逻课程提供的 ISE 写 verilog 代码难用的要似... 于是想转到 VScode 这种熟悉的平台上写,最好能编写仿真实现烧录全覆盖就好了wow

背景2:FDS 课不是要写 project 并互评嘛,看了一下三个给我评分的老哥,有点绷不住 lmao。第一个人一眼AI,评语像是骈体文,也没啥营养... 第二个人用眼过度视觉疲劳看不到我的 readme.md 和复杂度分析部分就说我没写;还有第三位更是重量级,不会怎么运行我的代码,开始隔空压力我,还说了一句:

In fact when I unified compilated the files in the given way, the code didn't output in the ans.out, but created some .o files, which may indicated something wrong.

很难想象这位是怎么过 C 语言基础的。大家可以搜搜 .o 文件是啥,反正 which may indicated something wrong 这句话承包了我一天的笑点。本来想笑话第一位老哥只会用 AI 的,emm... 后二位还不如用 AI 检查/帮助一下呢。

乐子归乐子,其实这三位老哥无非是当下某些现象的缩影:面对强大的新技术 AI,有的同学食髓知味,不料沉沦于此,反而阻碍了思考;有的同学嗤之以鼻,认为 AI 不过玩具尔,何以辅助学习?殊不知自己不足之处仍然很多,用 AI 辅助大有裨益。

很不幸,我也常有以上之时刻,究其原因,人之惰性使然尔。于此反思,与诸位共勉...

言归正传,近日内成功用 AI 辅助探究解决了背景1里的需求,希望能和大家探讨一下心路历程,顺便分享一下我使用 AI 的一些感触awa

注:本文中使用 DeepSeek,其他的通用大模型 AI 也可以类似分析。对 AI 的回答进行了简化,但意思基本一致。

1 简谈 AI 的搜索

这部分大家比我会用好吧~主要分享一下何时使用 AI 去搜索比较合适呢?

我的观点是:当你觉得这个问题问一个(有相关经验的)人比问搜索引擎答案更加准确的时候,你就主动地使用 AI 的联网搜索而非浏览器。

这是因为这种问题往往是宽泛的,多解的,甚至问题本身可以质疑。

例如:在 XX 地方旅游,我想住酒店,推荐?

问题指向不明(时间价位没讲),多解(价格、位置、环境等各有优势),甚至问题本身可以质疑(会不会民宿更好呢)。这种问题你问浏览器,一定不会有顺心的答案的(还有极多广告)。问 AI 必然客观全面很多。

M(Me) : 我想上海一日游,外滩附近酒店推荐

D(DeepSeek) : 外滩附近是上海的核心区域,酒店选择丰富,适合不同预算和需求。以下根据位置、性价比和特色推荐几家酒店,涵盖高端、中端和经济型,方便你的一日游行程:...

M : 果然 AI 建议更全面,还有各种小 tips

例如:天气预报 / 实时股价

这种确定性的问题,答案要求准确无误,当然直接查浏览器啦。

向 AI 提问的风格我会直接体现在下文。

2 泛选提问:确定方向

不知道大家又没有和我一样讨厌图形化界面的呢... 总感觉还是直接写代码好,感觉对项目的掌控感更强,心里更踏实qaq

于是向 AI 问询:

M: 我想使用 verilog 代码进行编写代码,仿真,实现以及烧录,请你推荐轻量化的平台,不需要图形化界面。每一步可以分别来。

D: 轻量化文本编辑器推荐VimEmacs以及VS Code仿真验证Icarus Verilog + GTKWave综合实现Vivado Tcl 命令行自动化

我一直使用的代码编辑器是 VS Code,于是便确认了大致方向:

VSC + Iverilog + Vivado(tcl)


以下的例子参看:前辈的力量

以此实验为例进行讲解,大家可以收藏数逻实验。(我是宝藏矿工)


3 漫长探索:耐心考验

当然写本文时已经大功告成啦,然而中间的试错真的要命啊qwq

诚然 AI 也有犯错误,但是大部分时间是自己降智了,不过成功时获得感很足(

本文给出的代码都是验证过正确性并下板过的,大家放心食用~

Hint:如果想要跟着本文做到底的话,建议先完成 3.3.1 的安装操作,等待安装时再回来做 3.1 等,因为 vivado 安装很耗时间。

3.1 VS Code

这部分网上的教程太多了,VSC 的单独配置就不另外说啦。

需要注意 VSC 与其他东西结合的时候大部分时间需要自己做一些配置,最常见的就是配置环境变量。

配置环境变量基本流程

通常是下载好一个东西之后,在它的文件夹里面找到 bin 这个文件夹,然后复制它的路径,系统设置里搜索环境变量,分别添加至系统变量和环境变量即可。以下为 AI 回答:


步骤 1:打开环境变量设置

  • 右键点击 “此电脑”“属性”“高级系统设置”“环境变量”

步骤 2:编辑用户或系统变量

  • 用户变量:仅对当前用户生效。
  • 系统变量:对所有用户生效(需管理员权限)。
  • 操作示例(添加 Python 到 PATH):
    1. 选中 Path“编辑”“新建” → 输入路径(如 C:\Python39\)。
    2. 点击 “确定” 保存所有窗口。

可以看到比较详尽。网上教程也多,不再赘述。

配置环境变量完成后,最好命令行内输出版本信息以验证下载完毕。

还有就是 VS code 要重启一次,安装的东西才能加载到终端。

3.2 Iverilog

参看这篇文章这篇文章

发现网上已有了详尽的教程,这时果断放弃 AI,选择跟上大佬的脚步。

文章比较详细,不过我自己使用的时候有条件取舍了一些。第二篇文章我只跟着做了第一章的内容,已经够用了。

跟着做比较轻松,这里就不再赘述。直接看例子:

建立文件夹如下:

LAB7
├── build
├── constraints
│   └── fpga.xdc
├── scripts
|   ├── simul.ps1
|   ├── build.ps1
│   ├── build.tcl
|   ├── program.ps1
│   └── program.tcl
└── src
    ├── clkdiv.v
    ├── createnumber.v
    ├── display.v
    ├── MUX41.v
    ├── MyMC14495.v
    ├── testbench_MUX41.v
    ├── testbench_top.v
    └── top.v

文件内容在最后。如果你是在博客园中浏览,请点击右侧大纲跳转到 “4 源码” 部分。

注意:仿真时top.v 中第 23 行 .clk(clkd[17]) 改为 .clk(clkd[3]),第 25 行 .scan(clkd[18 : 17]) 改为 .scan(clkd[8 : 7])。实现时改回来。

保存所有代码后,打开 powershell 脚本文件 simul.ps1 并学习以下代码:

iverilog -o .\build\wave.out .\src\MUX41.v .\src\MyMC14495.v .\src\clkdiv.v .\src\top.v .\src\display.v .\src\createnumber.v .\src\testbench_top.v
vvp -n .\build\wave.out

其意思是 iverilog 将 src\ 中的以上 .v 文件中所有 module 综合在一起,并进行仿真,结果存放在 wave.out 内。随后 vvp 将仿真信息可视化为波形图。

powershell 内运行以下代码:

.\scripts\simul.ps1

输出:

VCD info: dumpfile .\build\wave.vcd opened for output.

提示 vcd 波形图文件生成成功。

这时运行:

gtkwave .\build\wave.vcd

即可查看波形图。以上步骤需要确认 iverilog 已经安装以及配置完毕。配置示例在 3.3.1。其中 vvp 和 gtkwave 都是配套下载好的,无需另外安装。powershell 输入以下代码以检测安装完毕:

iverilog -v
iverilog -help
vvp -help
gtkwave -help

我的做法是 VScode 里下载 WaveTrace 插件,双击 .vcd 文件就可以在 VSC 中直接看,会美观很多。但是只能同时看 8 个元件,完整版要买。

3.3 vivado tcl

3.3.1 安装 vivado 以及配置

实际上我仅使用 VScode + Iverilog 也做了两个 lab,因为可以做到仿真,最后的综合实现依赖 ISE 也不是不能做,但是要抢着用实验室的清朝老电脑,于是还是寻求了一个替代。

首先通过简单的搜索发现下载 vivado 需要注册 amd 的账号,去英文官网注册即可。

这一步有若干问题:

  1. recaptcha 连不上:别连校园网 WIFI,且使用英文官网即可。
  2. 注册邮箱有问题:我自己使用的是 163 没问题,可以多个邮箱都试试。

其余的都跟着填,个人信息好像也不用填准确的。他会给邮箱发一个令牌,从邮件那里跳转就可以注册啦。

接下来就可以用中文官网

登录后选择界面左上角的 产品 > 软件、工具和应用 > 自适应 Soc 和 FPGA > Vivado。

点进去选择 download,进去选 Vivado(硬件开发者),下拉到 2024.2 的 windows,下载即可。

或者直接点这个

运行下好的 exe,按提示来操作,选择都按默认,需填写 amd 账号密码。

发现应用占 23G 空间,安装时需要预留 80 G(说好的轻量化呢 qwq)。不过貌似可以选一些不安装,具体的上网搜一下。

安装好之后找到 bin,配置好环境变量。我按默认地址安装的,地址在 "C:\Xilinx\Vivado\2024.2\bin"

设置里搜索环境变量,点“编辑系统环境变量”,点环境变量。下方系统变量点新建,变量名叫 vivado,值为 C:\Xilinx\Vivado\2024.2\bin。系统变量中选中 Path 这项点编辑,跳出新窗口点新建,输入 C:\Xilinx\Vivado\2024.2\bin。当然路径因人而异,我这个是默认安装的路径,建议按上面的方法确认一下 bin 在哪里。确认输入完后一路点确定。

重启 VScode,在其中运行终端(应该是 PowerShell),输入以下代码确认配置成功:

vivado -version

输出应该有版本号等等。我的输出:

vivado v2024.2 (64-bit)
Tool Version Limit: 2024.11
SW Build 5239630 on Fri Nov 08 22:35:27 MST 2024
IP Build 5239520 on Sun Nov 10 16:12:51 MST 2024
SharedData Build 5239561 on Fri Nov 08 14:39:27 MST 2024
Copyright 1986-2022 Xilinx, Inc. All Rights Reserved.
Copyright 2022-2024 Advanced Micro Devices, Inc. All Rights Reserved.

这样就算配置好了。

3.3.2 综合与实现

问 AI 要代码,中间的过程太过拉扯,就不看了。简单来说就是 vivado 有一个 tcl 控制台可以不开图形界面来做 vivado 的各种操作。

直接解析代码:(跟后面是一样的,已经建好文件夹就不用管了)

# build.tcl

# 1. create project

create_project -help

create_project -force -name fpga_project -dir vivado_project -part xc7k160t
set_property SEVERITY {Warning} [get_drc_checks NSTD-1]
set_property SEVERITY {Warning} [get_drc_checks UCIO-1]

# 2. add files
add_files [list \
  src/clkdiv.v \
  src/createnumber.v \
  src/display.v \
  src/MUX41.v \
  src/MyMC14495.v \
  src/top.v \
]

# 3. constraints
add_files -fileset constrs_1 constraints/fpga.xdc
# get_files -filter {FILE_TYPE == "XDC"}

# 4. syn & imp
launch_runs synth_1 -jobs 4
wait_on_run synth_1

launch_runs impl_1 -jobs 4
wait_on_run impl_1

open_run impl_1
puts "\nChecking Port Constraints:"
foreach port [get_ports *] {
    set loc [get_property PACKAGE_PIN $port]
    set iostd [get_property IOSTANDARD $port]
    if {$loc == ""} {
        puts "ERROR: Port $port has no LOC constraint!"
    } else {
        puts "OK: Port $port -> LOC: $loc | IOSTANDARD: $iostd"
    }
}

# 5. create .bit
launch_runs impl_1 -to_step write_bitstream -jobs 4
wait_on_run impl_1

# 6. close project
close_project
exit

保存以上代码在 .\scripts 里后,打开 powershell 脚本文件 build.ps1 并学习以下代码:

vivado -mode batch -source scripts/build.tcl

其意思是 vivado 这个软件以 batch 模式运行,tcl 控制台运行 build.tcl 内的全部代码。

powershell 里运行如下指令:

.\scripts\build.ps1

等待代码的运行,最后应该成功执行所有指令并正常 exit,则生成 bit 文件成功。

解析:

create_project -force -name fpga_project -dir vivado_project -part xc7k160t
set_property SEVERITY {Warning} [get_drc_checks NSTD-1]
set_property SEVERITY {Warning} [get_drc_checks UCIO-1]

-part xc7k160t 是实验板子的型号,一定要写!!!型号具体因人而异,建议看自己的课件。同一个学校应该差不多

下面两行是说引脚的约束绑定可以放宽松一些。

foreach 那里是检查引脚有没有一一对应,如果 #5 run 出了 error,可以注释掉 #5 部分再跑 tcl 代码,来查看引脚的绑定情况。

后面基本看名字都懂,不懂问问 AI 吧 qwq

成功后当前项目文件夹里找到 vivado_project\fpga_project.runs\impl_1\top.bit,说明实现成功。

3.3.3 烧录

# program.tcl

open_hw_manager
connect_hw_server

# find
set hw_targets [get_hw_targets]
if {[llength $hw_targets] == 0} {
    puts "ERROR: No JTAG cables detected!"
    exit
}

# check the first
set hw_target [lindex $hw_targets 0]
current_hw_target $hw_target
open_hw_target

# get device
set hw_devices [get_hw_devices]
if {[llength $hw_devices] == 0} {
    puts "ERROR: No FPGA devices found on the JTAG chain!"
    exit
}
set hw_device [lindex $hw_devices 0] ;

# copy .bit path
set bitstream_file "vivado_project/fpga_project.runs/impl_1/top.bit"
if {![file exists $bitstream_file]} {
    puts "ERROR: Bitstream file not found: $bitstream_file"
    exit
}

# program
puts "Programming device: $hw_device"
set_property PROGRAM.FILE $bitstream_file $hw_device
program_hw_devices $hw_device

close_hw_target
disconnect_hw_server
close_hw
exit

也是让 AI 写就行。报错就给信息让它改。

set bitstream_file "vivado_project/fpga_project.runs/impl_1/top.bit"

其中的路径是上面提到的 bit 文件的相对路径。

保存以上代码在 .\scripts 里后,打开 powershell 脚本文件 program.ps1 并学习以下代码:

vivado -mode batch -source scripts/program.tcl

powershell 里运行如下指令:

.\scripts\program.ps1

最后应该正常 exit,实验板上实现了通过按钮计分的功能,提示烧录 bit 文件成功。

我在实验室已经验证过本流程完全可行。如果有偏差的问问 AI 吧qaq

3.3.4 彩蛋

拉扯的过程如下:

M:vivado可以不依赖图形编辑界面而直接用代码控制数据流生成和烧录,请你举例说明

D:给代码

M:命令行如何检查vivado_lab是否安装好

D:vivado -version

M:我已经添加了环境变量但仍然显示找不到命令

D:检查环境变量路径是否正确、确认环境变量已生效、...

M:找到问题了,bash命令应该是vivado_lab -version

D:服务器繁忙,请稍后再试。

M:我有一个项目由 a.v b.v c.v 以及 top.v 组成,请你示例如何用 vivado 命令配合 tcl 实现 bit 流的生成以及烧录。

D:给代码

M:我用的是 ucf 文件绑定引脚,请你更改相应部分

D:改代码

M:我已经在 VScode 上建好文件结构了,还需要以 tcl 脚本建立吗?如果确实需要,它建立的文件目录在哪里?

D:分步解答

M:在 build.tcl 中是怎么确定我要下板验证的是哪个模块?

D:通过代码结构隐式指定 / tcl 中显式指定顶层模块

M:这个 tcl 控制台又是什么?在哪里打开,可以直接运行 tcl 代码吗?

D:Vivado 的 Tcl 控制台 是一个内置的交互式命令行工具,...

M:直接在系统内运行 PowerShell,输入 vivado_lab -version 可以正常输出版本信息,但在 VScode 的 PS 中无法识别vivado_lab 指令,是何问题?

D:逐步排查解决

M:(报错信息)运行 build.tcl 报错如上,请你看看怎么回事

D:你遇到的错误是因为命令的参数顺序不正确,应该...

M:ERROR: [Common 17-69] Command failed: File management TCL Commands are not supported

D:Vivado Lab Edition 的限制:...,下载并安装 Vivado Design Suite(非 Lab 版)。

M:绷不住了,就是为了省点空间才下的 Lab 版,还不能综合awa

M:不支持 ucf 了,把这个 ucf 文件转为 xdc 格式

D:OK,拿去运行

M:Unconstrained Logical Port 又是啥问题

D:部分顶层模块的 I/O 端口未在约束文件(XDC)中指定物理引脚位置(LOC)。

M:get_files 没有-fileset 参数啊,你再看看

D:替代方法:获取约束文件列表

M:invalid command name "report_port_status" 什么问题

D:未在正确的上下文中运行命令。应该...

M:你确定有这条指令吗,2024版按照以上运行仍然invalid

D:不行换一个。遍历所有端口并打印 LOC 和 IOSTANDARD(给代码)

M:(part 参数一直不对)SWORD 4.0 的对应 -part 参数是什么

D:(我不到啊)(开始一本正经地胡言乱语)

M:哦原来课件里面有,终于改对了

M:请把烧录指令给我看看

D:敲代码ing...关键说明比特流文件路径:...

M:执行 build.tcl 后都成功了,bit 文件在哪里

D:服务器繁忙,请稍后再试。

M:?woc眼睛不要了

M:烧录又失败了,怎么回事呢?

D:(一通分析后)不如重写一份 tcl,拿去重新试试

M:。。。

以上。早知道就用 Vivado 图形化界面了...

不过:

既然要追求刺激就贯彻到底了

你好骚啊


4 源码

simul.ps1

iverilog -o .\build\wave.out .\src\MUX41.v .\src\MyMC14495.v .\src\clkdiv.v .\src\top.v .\src\display.v .\src\createnumber.v .\src\testbench_top.v
vvp -n .\build\wave.out

clkdiv.v:

module clkdiv (
    input wire clk, rst,
    output reg [31 : 0] clkdiv
);

    initial begin
        clkdiv <= 0;
    end
    
    always @(posedge clk or posedge rst) begin
        if(rst) clkdiv <= 0;
        else clkdiv <= clkdiv + 1'b1;
    end

endmodule

createnumber.v:

module pbdebounce (
    input wire clk_1ms,
    input wire I,
    output reg O
);
    reg [7 : 0] pbshift;
    initial begin
        O = 0;
    end
    always @(posedge clk_1ms) begin
        pbshift = pbshift << 1;
        pbshift[0] = I;
        if(pbshift == 8'h00) O = 0;
        if(pbshift == 8'hFF) O = 1;
    end
endmodule

module create_number (
    input wire clk,
    input wire [3 : 0] btn,
    output reg [3 : 0] n0, n1, n2, n3
);
    

    wire [3 : 0] t0, t1, t2, t3;

    assign t0 = n0 + 1'b1;
    assign t1 = n1 + 1'b1;
    assign t2 = n2 + 1'b1;
    assign t3 = n3 + 1'b1;

    wire [3 : 0] tbtn;

    
    initial begin
        {n3, n2, n1, n0} <= 16'b0001_0010_0011_0100;
    end
    pbdebounce db0(.clk_1ms(clk), .I(btn[0]), .O(tbtn[0]));
    pbdebounce db1(.clk_1ms(clk), .I(btn[1]), .O(tbtn[1]));
    pbdebounce db2(.clk_1ms(clk), .I(btn[2]), .O(tbtn[2]));
    pbdebounce db3(.clk_1ms(clk), .I(btn[3]), .O(tbtn[3]));
    
    always @(posedge tbtn[0]) n0 <= t0; 
    always @(posedge tbtn[1]) n1 <= t1; 
    always @(posedge tbtn[2]) n2 <= t2; 
    always @(posedge tbtn[3]) n3 <= t3; 
    
endmodule

display.v:

module display_sync (
    input wire [3 : 0] I0, I1, I2, I3, points, LES,
    input wire [1 : 0] scan,
    output wire [3 : 0] Hex, AN,
    output wire p, LE
);
    Mux_4_1_b4 Muxb4(.I0(I0), .I1(I1), .I2(I2), .I3(I3), .s(scan), .o(Hex));

    wire [3 : 0] M;
    decoder_2_4 d24_1(.s(scan), .outp(M));
    assign p = (M[0] & points[0]) | (M[1] & points[1]) | (M[2] & points[2]) | (M[3] & points[3]);
    assign LE = (M[0] & LES[0]) | (M[1] & LES[1]) | (M[2] & LES[2]) | (M[3] & LES[3]);
    assign AN = ~M;
    
endmodule

MUX41.v:

module decoder_2_4 (
    input wire [1 : 0] s,
    output wire [3 : 0] outp
);
    assign outp[0] = ~s[0] & ~s[1];
    assign outp[1] = s[0] & ~s[1];
    assign outp[2] = ~s[0] & s[1];
    assign outp[3] = s[0] & s[1];

endmodule

module Mux_4_1_b4 (
    input wire [1 : 0] s,
    input wire [3 : 0] I0, I1, I2, I3,
    output wire [3 : 0] o
);
    wire [3 : 0] Muxer;
    decoder_2_4 Decoder(.s(s), .outp(Muxer));
    assign o[0] = (Muxer[0] & I0[0]) | (Muxer[1] & I1[0]) | (Muxer[2] & I2[0]) | (Muxer[3] & I3[0]);
    assign o[1] = (Muxer[0] & I0[1]) | (Muxer[1] & I1[1]) | (Muxer[2] & I2[1]) | (Muxer[3] & I3[1]);
    assign o[2] = (Muxer[0] & I0[2]) | (Muxer[1] & I1[2]) | (Muxer[2] & I2[2]) | (Muxer[3] & I3[2]);
    assign o[3] = (Muxer[0] & I0[3]) | (Muxer[1] & I1[3]) | (Muxer[2] & I2[3]) | (Muxer[3] & I3[3]);
endmodule

MyMC14495.v:

module MyMC14495 (
    input wire D0, D1, D2, D3,
    input wire LE, point,
    output wire a, b, c, d, e, f, g, p
);

    assign D0n = ~D0;
    assign D1n = ~D1;
    assign D2n = ~D2;
    assign D3n = ~D3;

    assign a = LE | ((D3n & D2n & D1n & D0) | (D3n & D2 & D1n & D0n) | (D3 & D2n & D1 & D0) | (D3 & D2 & D1n & D0));
    assign b = LE | ((D3n & D2 & D1n & D0) | (D2 & D1 & D0n) | (D3 & D2 & D0n) | (D3 & D1 & D0));
    assign c = LE | ((D3n & D2n & D1 & D0n) | (D3 & D2 & D0n) | (D3 & D2 & D1));
    assign d = LE | ((D3n & D2n & D1n & D0) | (D3n & D2 & D1n & D0n) | (D2 & D1 & D0) | (D3 & D2n & D1 & D0n));
    assign e = LE | ((D3n & D0) | (D3n & D2 & D1n) | (D2n & D1n & D0));
    assign f = LE | ((D3n & D2n & D0) | (D3n & D2n & D1) | (D3n & D1 & D0) | (D3 & D2 & D1n & D0));
    assign g = LE | ((D3n & D2n & D1n) | (D3n & D2 & D1 & D0) | (D3 & D2 & D1n & D0n));

    assign p = ~point;
endmodule


testbench_MUX41.v:

`timescale 1ns/1ns

module tb_MUX;
    reg [3 : 0] I0, I1, I2, I3;
    reg [1 : 0] s;
    wire [3 : 0] o;
    reg clk;

    initial begin
        I0 = 0;
        I1 = 0;
        I2 = 0;
        I3 = 0;
        s = 0;
        clk = 0;
    end

    always begin
        #50 clk = ~clk;
    end

    always @(posedge clk) begin
        #1 I0 = {$random};
        #3 I1 = {$random};
        #3 I2 = {$random};
        #3 I3 = {$random};
        #2 s = {$random};
    end

    Mux_4_1_b4 Muxer(.s(s), .I0(I0), .I1(I1), .I2(I2), .I3(I3), .o(o));

    initial begin
        $dumpfile(".\\build\\wave.vcd");
        $dumpvars(0, tb_MUX);
        #10000 $finish;
    end
    
endmodule

testbench_top.v:

`timescale 1ns/1ns

module uut;
    reg [3 : 0] btn;
    reg [7 : 0] SW;
    reg clk;

    wire [3 : 0] AN;
    wire [7 : 0] SEGMENT;
    wire BTNX4;

    initial begin
        btn = 0;
        SW = 8'hFF;
        clk = 0;
    end

    top uut0(.btn(btn), .clk(clk), .SW(SW), .AN(AN), .SEGMENT(SEGMENT), .BTNX4(BTNX4));

    always begin
        #5 clk = ~clk;
    end

    initial begin
        #5000 btn = 4'b0001;
        #10000 btn = 4'b0000;
        #5000 btn = 4'b0001;
        #10000 btn = 4'b0000;
        #5000 btn = 4'b0010;
        #10000 btn = 4'b0000;
    end

    initial begin
        $dumpfile(".\\build\\wave.vcd");
        $dumpvars(0, uut);
        #1000000 $finish;
    end
    
endmodule

top.v:

module top (
    input wire [3 : 0] btn,
    input wire clk,
    input wire [7 : 0] SW,
    output wire [3 : 0] AN,
    output wire [7 : 0] SEGMENT,
    output wire BTNX4,
    output wire Ring
);
    
    reg GND;
    wire RST;
    initial GND <= 0;
    assign BTNX4 = GND;
    assign RST = GND;
    assign Ring = GND; 

    wire [3 : 0] I0, I1, I2, I3, Hex;
    wire [31 : 0] clkd;
    wire P, LE;
    
    clkdiv cd(.clk(clk), .rst(RST), .clkdiv(clkd));
    create_number numc(.clk(clkd[17]), .btn(btn), .n0(I0), .n1(I1), .n2(I2), .n3(I3));

    display_sync ds(.I0(I0), .I1(I1), .I2(I2), .I3(I3), .Hex(Hex), .AN(AN), .scan(clkd[18 : 17]), .points(SW[3 : 0]), .LES(SW[7 : 4]), .p(P), .LE(LE));
    MyMC14495 MyMc(.D0(Hex[0]), .D1(Hex[1]), .D2(Hex[2]), .D3(Hex[3]), .point(P), .LE(LE), 
    .a(SEGMENT[0]), .b(SEGMENT[1]), .c(SEGMENT[2]), .d(SEGMENT[3]), .e(SEGMENT[4]), .f(SEGMENT[5]), .g(SEGMENT[6]), .p(SEGMENT[7]));

endmodule

fpga.xdc:

# clocks
set_property PACKAGE_PIN AC18 [get_ports clk]
set_property IOSTANDARD LVCMOS18 [get_ports clk]
create_clock -period 10.000 -name clk [get_ports clk] 

# SW[7:0]
set_property PACKAGE_PIN AA10 [get_ports {SW[0]}]
set_property IOSTANDARD LVCMOS15 [get_ports {SW[0]}] 

set_property PACKAGE_PIN AB10 [get_ports {SW[1]}]
set_property IOSTANDARD LVCMOS15 [get_ports {SW[1]}]

set_property PACKAGE_PIN AA13 [get_ports {SW[2]}]
set_property IOSTANDARD LVCMOS15 [get_ports {SW[2]}]

set_property PACKAGE_PIN AA12 [get_ports {SW[3]}]
set_property IOSTANDARD LVCMOS15 [get_ports {SW[3]}]

set_property PACKAGE_PIN Y13 [get_ports {SW[4]}]
set_property IOSTANDARD LVCMOS15 [get_ports {SW[4]}] 

set_property PACKAGE_PIN Y12 [get_ports {SW[5]}]
set_property IOSTANDARD LVCMOS15 [get_ports {SW[5]}]

set_property PACKAGE_PIN AD11 [get_ports {SW[6]}]
set_property IOSTANDARD LVCMOS15 [get_ports {SW[6]}]

set_property PACKAGE_PIN AD10 [get_ports {SW[7]}]
set_property IOSTANDARD LVCMOS15 [get_ports {SW[7]}]

# SEGMENT[7:0]
set_property PACKAGE_PIN AB22 [get_ports {SEGMENT[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SEGMENT[0]}]

set_property PACKAGE_PIN AD24 [get_ports {SEGMENT[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SEGMENT[1]}]

set_property PACKAGE_PIN AD23 [get_ports {SEGMENT[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SEGMENT[2]}]

set_property PACKAGE_PIN Y21 [get_ports {SEGMENT[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SEGMENT[3]}]

set_property PACKAGE_PIN W20 [get_ports {SEGMENT[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SEGMENT[4]}]

set_property PACKAGE_PIN AC24 [get_ports {SEGMENT[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SEGMENT[5]}]

set_property PACKAGE_PIN AC23 [get_ports {SEGMENT[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SEGMENT[6]}]

set_property PACKAGE_PIN AA22 [get_ports {SEGMENT[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SEGMENT[7]}]

# AN[3:0]
set_property PACKAGE_PIN AC22 [get_ports {AN[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {AN[3]}]

set_property PACKAGE_PIN AB21 [get_ports {AN[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {AN[2]}]

set_property PACKAGE_PIN AC21 [get_ports {AN[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {AN[1]}]

set_property PACKAGE_PIN AD21 [get_ports {AN[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {AN[0]}]

# btn[3:0] and BTNX4
set_property PACKAGE_PIN V18 [get_ports {btn[3]}]
set_property IOSTANDARD LVCMOS18 [get_ports {btn[3]}]

set_property PACKAGE_PIN V19 [get_ports {btn[2]}]
set_property IOSTANDARD LVCMOS18 [get_ports {btn[2]}]

set_property PACKAGE_PIN V14 [get_ports {btn[1]}]
set_property IOSTANDARD LVCMOS18 [get_ports {btn[1]}]

set_property PACKAGE_PIN W14 [get_ports {btn[0]}]
set_property IOSTANDARD LVCMOS18 [get_ports {btn[0]}]

set_property PACKAGE_PIN W16 [get_ports BTNX4]
set_property IOSTANDARD LVCMOS18 [get_ports BTNX4]

set_property PACKAGE_PIN AF24 [get_ports Ring]
set_property IOSTANDARD LVCMOS33 [get_ports Ring]

build.ps1:

vivado -mode batch -source scripts/build.tcl

build.tcl:

# build.tcl

# 1. create project

create_project -help

create_project -force -name fpga_project -dir vivado_project -part xc7k160t
set_property SEVERITY {Warning} [get_drc_checks NSTD-1]
set_property SEVERITY {Warning} [get_drc_checks UCIO-1]

# 2. add files
add_files [list \
  src/clkdiv.v \
  src/createnumber.v \
  src/display.v \
  src/MUX41.v \
  src/MyMC14495.v \
  src/top.v \
]

# 3. constraints
add_files -fileset constrs_1 constraints/fpga.xdc
# get_files -filter {FILE_TYPE == "XDC"}

# 4. syn & imp
launch_runs synth_1 -jobs 4
wait_on_run synth_1

launch_runs impl_1 -jobs 4
wait_on_run impl_1

open_run impl_1
puts "\nChecking Port Constraints:"
foreach port [get_ports *] {
    set loc [get_property PACKAGE_PIN $port]
    set iostd [get_property IOSTANDARD $port]
    if {$loc == ""} {
        puts "ERROR: Port $port has no LOC constraint!"
    } else {
        puts "OK: Port $port -> LOC: $loc | IOSTANDARD: $iostd"
    }
}

# 5. create .bit
launch_runs impl_1 -to_step write_bitstream -jobs 4
wait_on_run impl_1

# 6. close project
close_project
exit

program.ps1:

vivado -mode batch -source scripts/program.tcl

program.tcl:

# program.tcl

open_hw_manager
connect_hw_server

# find
set hw_targets [get_hw_targets]
if {[llength $hw_targets] == 0} {
    puts "ERROR: No JTAG cables detected!"
    exit
}

# check the first
set hw_target [lindex $hw_targets 0]
current_hw_target $hw_target
open_hw_target

# get device
set hw_devices [get_hw_devices]
if {[llength $hw_devices] == 0} {
    puts "ERROR: No FPGA devices found on the JTAG chain!"
    exit
}
set hw_device [lindex $hw_devices 0] ;

# copy .bit path
set bitstream_file "vivado_project/fpga_project.runs/impl_1/top.bit"
if {![file exists $bitstream_file]} {
    puts "ERROR: Bitstream file not found: $bitstream_file"
    exit
}

# program
puts "Programming device: $hw_device"
set_property PROGRAM.FILE $bitstream_file $hw_device
program_hw_devices $hw_device

close_hw_target
disconnect_hw_server
close_hw
exit

以上。

5 附录

参考文献:

https://zhuanlan.zhihu.com/p/95081329

https://zhuanlan.zhihu.com/p/338497672

https://www.bilibili.com/opus/487517356911938825/?from=readlist

相关网站 / 下载链接:

deepseek

VS code

Iverilog下载

amd 以及其英文官网


真正的彩蛋:

你怎么穿着 VScode 的衣服

真真正正的彩蛋:

好吧用 AI 的心得也没有那么高妙,其实就是不要妄想用 AI 就能偷懒就行。也别见到错了就怀疑自己而盲从 AI,也别一直“俺寻思之力”自己乱搞,尽量多找几个细节问一问。越认真做的就会越快,这时用 AI 就有显著的优势了。

就酱,byebye啦~ 祝大家身体健康,学习进步哇

posted @ 2025-04-02 18:20  Vizing  阅读(185)  评论(0)    收藏  举报