Verilog/FPGA/Vivado学习(基于小梅哥Xilinx FPGA)学习笔记

文章目录

相关资源网站(小梅哥FPGA)

  1. 【合集】小梅哥所有【FPGA开发板】【扩展模块】【开发软件】资料下载地址
  2. 【ACX720】ACX720型Xilinx FPGA开发板用户自助服务手册

本篇文章使用的开发板为:
小梅哥 Xilinx FPGA
型号:XC7A35T
芯片型号:XC7A35TFGG484-2

学习文章推荐

  1. 【Verilog HDL 入门教程】 —— 学长带你学Verilog(基础篇)
  2. Verilog入门教程与实例分享
  3. Verilog基础入门
  4. FPGA零基础入门学习路线

以下所有文章内容,均来自小梅哥FPGA教学视频,这个文章只是针对每个教学视频的个人理解总结。

一、整个工程的流程

写一套硬件描述语言,能够在指定的硬件平台上实现相应的功能

  1. 设计定义(让LED一秒闪烁一次)
  2. 设计输入(编写逻辑(使用Verilog代码描述逻辑),画逻辑图,使用IP盒(即提供的成品功能器件))
  3. 综合工具(由专业的EDA软件进行,Quartus、Vivado、ISE),对所写的逻辑内容进行分析,并得到逻辑门级别的电路内容。(如if else就是一个二选一多路器)
  4. 功能仿真(使用专门的仿真工具进行仿真,验证设计的逻辑功能能够实现。对于数字电路来说 ,仿真时基本接近于真实情况的,是可信的)
  5. 布局布线(走线带来延迟,延迟越长,主频越低)(在指定器件上将设计的逻辑电路实现)
  6. 分析性能(1、时序仿真(非常耗费时间),仿真软件进行观察;2、静态时序分析(平时用这个))
  7. 下载到目标板运行,查看运行结果。(板级调试)(使用ILA(嵌入式逻辑分析仪)(Vivado))
  8. 让设计的逻辑在目标板上正常工作(功能正常,性能稳定)

总结

  1. 设计定义
  2. 设计输入
  3. 分析综合(EDA、Vivado、Quartus)
  4. 功能仿真(Modelsim)
  5. 布局布线(Vivado、Quartus)
  6. 分析性能(时序仿真Modelsim、静态时序分析Vivado、Quartus)
  7. 板级调试
  8. OK
    2024.12.22

二、基于Vivado的FPGA开发流程实践(二选一多路器)

打开Vivado并创建工程mux2后,Vivado界面上左边流程
在这里插入图片描述
即该软件就是按照开发流程进行运行的

什么是二选一多路器

当SEL=1时,out=a
当SEL=0时,out=b
在这里插入图片描述

用verilog语言,Vivado软件进行该电路实现

下图要注意符号的运用

1、设计输入:Design Sources中的代码

在这里插入图片描述

用到的语法(Verilog语言)

  1. 定义模块
module xxx(
......
)
endmodule
//定义一个模块
  1. 定义输入输出
input a;
output out;
  1. 赋值语句
assign out = (sel==1)?a:b;
//在sel=1的情况下,将a的值赋予out,否则将b的值赋予out
assign out = ~a;
//将a取反的值赋予out

2、分析和综合:分析设计输入中是否有错误

在这里插入图片描述
分析综合运行后,看message,其中没有报错那么这一步就完成了,接下来就进行仿真验证

3、功能仿真,Simulation Sources中的仿真实现代码(Test_Bench)

在这里插入图片描述
在仿真平台中,通过代码编写,搭建一个仿真平台,取验证之前编写的module是否OK
在这里插入图片描述
我们个人一般用不到ps级别的延时,ns够用

#1 //延时1ns
#2 //延时2ns
#1.001 //延时1.001ns,这是在配置为1ns/1ps情况下是可以实现的

写一个可以测试之前定义的module mux2的功能的仿真代码
在这里插入图片描述

reg s_a; //定义测试平台的输入信号,可接0和1逻辑输入
.a(s_a); //.代表测试端子,a代表module中a端口,(s_a)代表测试平台的reg端口
//这句话的意思是,将测试平台的(s_a)端子与module的a端子通过.来相接
wire out; //定义平台的输出信号,可根据分析输入信号和module来确认这个输出信号
initial begin
......
end
//测试平台的操作

在这里插入图片描述
跑仿真后出现这个界面,这个就是仿真的时序图,
在这里插入图片描述
第一次跑不全,再跑一次
在这里插入图片描述
此时可以看着时序图对out的信号进行对比,看是否是我们需求的信号输出结果。

仿真这一步挺重要的,建议需要掌握

4、布局布线

在这里插入图片描述
在这里插入图片描述
点击红框中的符号可以看到目前的编译状态

5、时序仿真

在这里插入图片描述
在这里插入图片描述
时序仿真电路中可以看到真实的电路中的信号延迟情况,
out都滞后于输入信号的变化,根据时序仿真,可以看出延迟情况,来判断 这个延迟我们能否接受

6、板级调试

(1)IO引脚分配

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
根据上图进行引脚分配

分配完成后,在引脚分配界面进行ctrl+s进行保存,保存名称和项目名称可以一样(不强求,也可以不一样)

(2)程序下载

在这里插入图片描述
点击后就可以进行创建,
创建完成后可以进行程序下载
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这样就下载完成了,之后就可以进行板级调试。
2021.12.22

三、3-8译码器的实现

什么是3-8译码器

在这里插入图片描述

1、设计输入

3-8译码器
创建module,创建3-8译码器的端口
在这里插入图片描述
端口创建完成后,定义端口的输入输出状态
在这里插入图片描述

输入输出状态定义完成后,进行端口之间的逻辑编写
在这里插入图片描述

用到的语法

  1. always

  2. 位拼接
    在这里插入图片描述
    在这里插入图片描述
    d是一个4位数据,通过assign将 a, 1bit常量值为0,b , c这四个数拼在一块赋值给d

  3. 描述多位格式的常用限定符号
    在这里插入图片描述
    这两个表达方式是一样的
    在这里插入图片描述

b  二进制        3‘b101         8'b0000_1010
o  八进制
d  十进制        3'd5           8'd10
h  十六进制                     8'ha
  1. case() endcase
    case括号中,为满足操作的条件
    在满足条件下进行操作,如:\
3‘b000 : out = 8'b0000_0001; 

具体解释为拼接后的{a,b,c}在满足3位bit值是000的情况下,将8bit的0000_0001赋值给out

整个3-8译码器代码为下

在这里插入图片描述

2、分析综合

分析综合的作用,检查设计输入的语法有无问题
在这里插入图片描述
分析之后查看reports,有错误则报红

3、仿真

创建一个测试平台,命名规则一般为(文件名_tb),tb代表test bench
在这里插入图片描述
测试平台的代码具体的流程为

  1. 定义测试平台的时间尺度
  2. 定义一个测试平台
  3. 定义测试平台的端口,标注好reg型(输入)和wire型(输出)
  4. 将模块放置到测试平台上
  5. 将模块中的端口与测试平台的端口互相连接
  6. 定义测试平台的测试动作(给各信号端口激励信号并延时)
  7. end
    在这里插入图片描述
    所有代码完成后,进行仿真
    在这里插入图片描述
    仿真第一次跑会根据仿真设置中的默认跑1000ns来跑,所以第一次的仿真都是1000ns的时间段中的动作
    这里我们可以不用管,直接再次跑,在动作结尾加上 $stop; 即可跑到这里自动停止
    在这里插入图片描述

4、时序仿真

跳过,这个项目中只进行了模拟仿真,并没有进行时仿真
原因是项目比较简单,无进行必要。但也可以进行。这个步骤有无都无所谓

查看电路

在这里插入图片描述

5、IO引脚分配

打开综合类工程
在这里插入图片描述
必须打开后才能看到IO planning这个选项
在这里插入图片描述
IO设置中首先将I/O Std电压设定为3.3(LVCMOS33),只针对于目前手中的这个开发板进行的设定
在这里插入图片描述
其次根据引脚对应表,将拨码开关引脚进行设定
在这里插入图片描述
LED灯对应的引脚(高电平点亮)
在这里插入图片描述
根据表进行设定
在这里插入图片描述
设定完成后进行ctrl+s保存

6、生成代码,板级调试

在这里插入图片描述
因为之前没有进行布局布线,即
在这里插入图片描述
在生成BitStream时候会进行检查,没布局布线的话这一步会自动进行,所以这一步在之前省略了
点击在这里插入图片描述之后,等待完成。
下一步连接硬件板卡进行程序下载
之后进行板级调试就OK

三个拨码开关,设定为a,b,c
8个LED灯代表out 0-7
拨码开关不同状态代表不同输出,led显示表示输出

课后题,写4-16译码器代码

1、module设计输入

在这里插入图片描述
代码完成后进行分析综合,即检查下代码有无错误

2、功能仿真

在这里插入图片描述
代码完成后进行分析综合,即检查下代码有无错误
在这里插入图片描述

3、布局布线

4、 时序仿真

5、IO引脚分配

6、生成代码,板级调试

2024.12.23

四、时序逻辑计数器设计

需掌握知识

  1. 什么是D触发器
  2. 怎么组成计数器
  3. 计数器多少位如何取判断和设定
  4. module中如何精确进行延迟触发(延时)
  5. 阻塞赋值和非阻塞赋值初了解

什么是D触发器

在这里插入图片描述
CK上升沿的时候,将D端口的信号输出给Q端口
即D触发器有存储特性,只在CK端口上升沿变化,其余时间一概不管,即Q端口可以存储D的电平状态

什么是计数器

counter = counter+1
在这里插入图片描述
目标,设计1s频率闪烁的LED灯(亮灭各500ms)

目前板子的时钟是50MHz,那么执行每步操作时候为20ns
一个周期为1s,
则信号翻转一次需500ms,500ms需要计数500ms/20ns=25000000

用Verilog代码实现led闪烁

1、设计输入:module编写

  1. 确认module端口,需要有时钟信号Clk,复位信号Reset_n(低电平复位),Led控制信号
    在这里插入图片描述
  2. 确认信号的类型
    时钟信号为输入 input
    复位信号为输入 input
    led信号为输出 output
    counter信号为内部计数,多位D触发器,具体多少位,通过以下计算
    在这里插入图片描述
    即需要25位D触发器,设定为reg类型的counter:reg [24:0]counter;
    即最终的端口定义是这样的
    在这里插入图片描述
    通过这个定义可以看出,内部端口也是需要进行定义的,而且内部端口不再试input和output类型的端口了,这里则使用的试reg寄存器类型来表示。
    同时对需要进行复杂操作的端口也要进行reg的定义,如之后的代码需要让Led端口自行取反,即Led=!Led;
  3. 端口的逻辑操作,两种写法:1:综合在一块写;2:分开写
    综合在一块写(不推荐)
    在这里插入图片描述
    分开写(推荐,构成的电路会更简便)
    在这里插入图片描述

即最终的module代码:
在这里插入图片描述
在这里插入图片描述
这个图试后续运行后发现代码无法实现功能反回来查找问题,各种语句使用的都是“<=”,否则会有问题。
<= :非阻塞赋值
= : 阻塞赋值

阻塞赋值  =
阻塞赋值是顺序执行的。在一个begin - end块中,多条阻塞赋值语句按照顺序依次执行,下一条语句会等待当前阻塞赋值语句执行完成后才开始执行。例如:
always @(posedge clk) begin
    a = b;
    c = a;
end
当clk上升沿到来时,先执行a = b,将b的值赋给a,然后执行c = a,此时a已经更新为b的值,所以c得到的是更新后的a(也就是b的值)。

应用场景:
常用于组合逻辑电路建模,特别是在需要按照特定顺序进行计算的组合逻辑中。例如,在实现一个简单的算术逻辑单元(ALU)的组合逻辑部分时,按照运算优先级进行计算就可以使用阻塞赋值。
还用于initial块中初始化变量,因为initial块主要是顺序执行的初始化代码,例如:
initial begin
    reg_a = 0;
    reg_b = 1;
end

对仿真的影响:
在仿真过程中,由于阻塞赋值是顺序执行的,所以赋值语句的执行顺序会直接影响变量的值和仿真结果。如果赋值顺序不当,可能会导致不符合预期的结果。
例如,如果在组合逻辑中有相互依赖的阻塞赋值,可能会出现竞争冒险(race condition)现象,导致输出出现毛刺(glitch)。
非阻塞赋值  <=
非阻塞赋值是并发执行的。在一个always块中,所有非阻塞赋值语句在同一时刻开始计算表达式的值,但赋值操作会在当前时间步(time step)结束时同时更新目标变量。例如:
always @(posedge clk) begin
    a <= b;
    c <= a;
end
当clk上升沿到来时,a <= b和c <= a这两个赋值语句会同时开始计算右侧表达式的值。此时计算c <= a时,a的值是更新前的值,等到这个always块执行结束后,a和c才会同时更新。所以c得到的是a的旧值。

应用场景:
主要用于对时序逻辑电路进行建模,特别是在多个寄存器之间需要传递数据,并且这些寄存器的更新应该在时钟沿触发后同时进行的情况。例如,在一个简单的移位寄存器设计中:
always @(posedge clk) begin
    reg1 <= in_data;
    reg2 <= reg1;
    reg3 <= reg2;
end

对仿真的影响:
非阻塞赋值在仿真时会在每个时间步结束时更新所有被赋值的变量,这样可以更好地模拟实际硬件中的寄存器等时序元件的行为。
有助于避免在时序逻辑建模中因为赋值顺序不当而产生的错误,但如果在组合逻辑中错误地使用非阻塞赋值,也可能会导致意想不到的结果,因为组合逻辑通常期望是立即更新输出的。

使用 = ,直接结果是后续仿真的时候高电平只持续了1个周期,即20ns就又变为低电平
使用<=,则可以实现500.00002ms一反转
在这里插入图片描述

为什么是500.000002ms,因为计数是从0开始,计数到25000000时候多计数了一个周期,即20ns,这里需要把计数改为24999999就OK
在这里插入图片描述

用到的语法

 always@(posedge Clk or negedge Reset_n)//当时钟(Clk)的上升沿或者复位(Reset_n)的下降沿时候,这个代码块工作,其他时候不工作。
counter <= 0;//counter清零,<=是非阻塞赋值的意思
counter=counter + 1'd1;//counter自加1

2、分析综合

在这里插入图片描述

3、仿真模拟

在这里插入图片描述
最终在module为以下情况下,进行仿真
在这里插入图片描述
得出下图:即高低电平切换时间为500ms
在这里插入图片描述

查看电路

在这里插入图片描述

4、 IO设定

按键
在这里插入图片描述
LED灯
在这里插入图片描述
时钟引脚为Y18
在这里插入图片描述

5、生成程序、板级调试

2024.12.23

五、8位跑马灯(流水灯)设计

需掌握知识

  1. 如何进行位操作,拼接
  2. 如何实现module中延时触发的条件,通过时钟信号进行判定,通过counter计数进行准确的定时
  3. 代码中上升沿和下降沿的判断, posedge 、negedge
  4. TB中,如何初始化测试平台的配置,通过initial和always进行初始化配置操作

1、设计输入

clk作为一个内部时钟信号,通过外部提供的时钟信号来进行计数器的触发+1
而counter作为加法器的数据记录,在到设定记录数据时候,进行动作的触发,这里指Led灯的跑马状态

实现效果的两种方案,最终的效果是一样的
方案一

  1. 方案一
    通过1的不断移位来进行操作,但需要注意的是,移动到最左端需要有个判断语句,在这次移位完成后将1回复到最右端
  2. 方案二
    通过位拼接的方式,将上一次led的输出数据的第8位即Led[7],移动到下次Led输出数据的第一位,即
Led <= {Led[6:0],Led[7]}

这样操作就实现了首尾循环,实现了跑马灯效果

在这里插入图片描述

代码编写完成后进行分析综合,即通俗的话来说就是编译一下(个人理解,有错请指正)

2、仿真模拟

需编译仿真平台的代码

  1. 定义测试平台的时间尺度
  2. 定义一个测试平台
  3. 定义测试平台的端口,标注好reg型(输入)和wire型(输出)
  4. 将模块放置到测试平台上
  5. 将模块中的端口与测试平台的端口互相连接
  6. 定义测试平台的测试动作(给各信号端口激励信号并延时)
  7. end

在这里插入图片描述

代码编写完成后进行分析综合

当分析综合完成后,发现没有什么错误,那么就可以进行当前测试平台的仿真模拟了
仿真结果如下,5ms作为一个灯的停留周期,再运行到下一个灯上
在这里插入图片描述

4、IO分配设定

根据手册进行IO分配

3、生成代码,板级调试

仿真可以实现我们的要求后,就可以进行下一步,此步骤参考前面几个工程的操作,是通用的,此处不再赘述

2024.12.25

六、在module设计中引用其他module实现跑马灯

同第五块内容,采用加法计数器作为底层module,这个设计的不同就在于另接一个3-8译码器实现跑马灯的灯的驱动

需掌握知识

  1. 主要语法是在module设计中,引用另外一个module的操作
  2. 底层模块驱动的引脚,在顶层模块定义中均为wire型,不能是reg型,如后面代码中,Led输出是由3-8译码器的out驱动,这里out就是底层,而led就是顶层,所以在顶层定义中,将Led定义为
    output [7:0]Led ; //wire此处可以省略不写
    output wire[7:0]Led ; //这个写法也正确

1、设计输入

将之前设计的3-8译码器的source源代码文件添加到本工程中的source文件中
在这里插入图片描述
注意路径,参考该路径找到对应的文件地址进行添加
在这里插入图片描述
同时在Vivado的添加source选项中添加decoder_3_8的source

其实可以直接在decoder的工程中调用,但是为了方便整理,我们将同一个工程中用到的文件放到一起。

代码中,前面的counter计数还是和之前相同
后面增加了counter2,就是为了和3-8译码器的输入端子相接
在这里插入图片描述
这里引入的module同 Test_Bench文件中的引入方式

2、模拟仿真

TB平台上,接run2的接口,就可以实现这个module的仿真
在这里插入图片描述
最终仿真结果如下
在这里插入图片描述

查看设计的电路

在这里插入图片描述

后面过程省略

2024.12.25

使用参数进行时间定义,parameter

在定义时间参数时候,每个if语句中都需要将时间值,即24999999写一次,这样就导致可能某些时候忘记修改其中一个,导致实际程序和预期不符合,这个时候,需要用到

parameter MCNT = 25'd24999999;

parameter进行参数定义,这样在if语句中只用写入MCNT就可代指24999999,修改的时候只需要MCNT对应的赋值就可以同时运用到之后的程序中。

参数的重新定义,defparam、模块直接重定义

在tb平台上,我们想要更快的仿真,如实际上代码上我们定义的是延时500ms,而500ms在仿真上,要跑很久(10s左右),才能跑出来。所以我们在tb平台上,想要把例华好的模块进行参数的重新定义,而且该定义不能影响正常烧录到板子中的程序,此时我们选择在tb平台使用:

defparam led_run_test.MCNT = 25'd24999 ;

这样在仿真时候,仿真就会采用MCNT参数未24999进行快速仿真
在这里插入图片描述

教学视频上有说该种写法错误,defparam不能再tb代码中使用,但我这里验证是可以使用的,包括也可以正常仿真,有问题的话自己多尝试下

另一种写法,在模块放到TB中,先不进行贴标签,先修改参数,具体如下,
在这里插入图片描述
如图片红字所示过程,进行参数修改。这样经验证可以正常按照预期仿真,且不影响实际下载如板子的程序。

2024.12.30

七、参数化设计实现模块的重用

需掌握知识

  1. 计时参数的计算
  2. 参数的重定义的使用(defparam)
  3. 如何例化一个module
  4. 在一个module中,如何例化多个module并对其分别进行操作
  5. tb平台中如何初始化一个端口
  6. tb平台中如何让一个端口在后台一直进行某个逻辑运行操作
  7. 综合时候对综合顺序的要求是什么
  8. 约束文件的创建

8个LED灯不同频率闪烁

50MHz为板子的频率,则执行一步的时间为1/50M=20ns,即板子中的Clk每翻转一次需要20ns
通过计数Clk的翻转次数进行时间统计,如
需要一个周期为1s的时间,即需要500ms翻转一次,一个周期1s翻转2次,时间为:
500ms/20ns = 25000000 ,则需要进行24999999次Clk计数(前文有讲为什么减1)
0.5s:
250ms/20ns = 12500000,则需要进行12499999次Clk计数
0.4s:
200ms/20ns = 10000000,则需要进行9999999次Clk计数
0.3s:
150ms/20ns = 7500000,则需要进行7499999次Clk计数
0.2s:
100ms/20ns = 5000000,则需要进行4999999次Clk计数
0.1s:
50ms/20ns = 2500000,则需要进行2499999次Clk计数

首先定义一个单个led闪烁的程序

在这里插入图片描述

在单个闪烁程序上进一步进行封装,封装为8个led闪烁的module

重新定义一个module,在这个module中例化8个上一个控制单个led闪烁的module,并重新定义计时参数(defparam)
这样就确保在一个module中可进行8个led不同延时闪烁的配置
在这里插入图片描述

在tb上进行模拟验证

在这里插入图片描述
以上代码全部完成后,进行综合,确认代码中有无错误
注意
需将编写的代码从最底层开始综合,如果从最高层开始综合,就可能出现某些代码未被综合而报错的情况。
按照以下顺序综合
在这里插入图片描述
完成以上操作后,进行仿真,通过仿真的时序图,看出我们期望的结果实现了
在这里插入图片描述

通过约束文件的创建进行引脚定义

首先通过Add Sources进行constraints文件的新增,
在这里插入图片描述
新增后通过增加以下配置命令就可进行IO端口的配置与绑定
在这里插入图片描述
以上的配置命令也可以根据前几节讲的,通过以下进行配置
在这里插入图片描述

代码的整体编译与烧录,板级调试

以上操作完成后直接点击红框,则可完成所有操作
在这里插入图片描述
之后进行硬件烧录,之前文章中有提到过
可查看 第二节→程序下载

2025.1.2

八、从计数器到可控线性序列机

需要掌握的知识

  1. 什么是线性序列机
  2. Ctrl输入端口位操作
  3. 时间计时参数的计算
  4. 时间计时位数的计算
  5. 单个时间段内同时操作多个端口
  6. 多个时间段周期循环如何操作,两个counter计数
  7. 本节课就是要让初学者意识到,计数器在可控线性序列机中的重要性
  8. 如何将参数放到模拟波形中可见

课程目标

在这里插入图片描述

LED灯亮0.5s,灭0.5s状态循环

思路:
设计计数器,计数周期位1s
默认亮状态,计数到0.5s时候拉低(亮0.5s),计数到1s时候拉高(灭0.5s)
以50MHz的板子为例,20ns为一Clk变化周期

确认计数器的位数 reg[25:0] count
首先要确保led初始状态为拉高状态:initial Led = 1 ;
count == 25000000-1 时候进行拉低
count == 50000000-1 时候进行拉高

在这里插入图片描述
TB代码
在这里插入图片描述

仿真模拟
在这里插入图片描述
通过参数化设计,进行重新编写
在这里插入图片描述
这样在模拟的时候就只用修改TB的代码,不用再修改module中的代码了

2025.1.3

LED灯亮0.25s、灭0.5s、亮0.75s、灭1s 状态循环

module代码,在同一个项目中时候记得更改module名称,否则名称重复编译不通过
在这里插入图片描述
在这里插入图片描述
TB代码,记得更改计时参数,这样仿真能节省时间
在这里插入图片描述
具体仿真如下:因为周期为25ms,所以仿真比较快
在这里插入图片描述

LED亮灭未知,8个周期,0.25为变化周期,用户随机指定如何亮灭

8个0.25s为一个循环,即一个周期需要2s,计数即2000000000/20=100000000,其中counter有效位为27位
如何指定亮灭模式?
需要有个端口进行输入,来指定这8个0.25周期中,哪一段是亮,那一段是灭。考虑可通过8个拨码开关进行每个周期的指定。
module代码:
在这里插入图片描述
else if太多不太好,所以通过case进行条件判断
在这里插入图片描述

TB平台
在这里插入图片描述
最终仿真
在这里插入图片描述
计数器相当于时间轴的横坐标,我们可以在计数器这个技术周期内,进行任何操作。

LED亮灭未知,8个变化状态一个循环,单个变化状态时间未知,用户随机指定如何亮灭

根据题目进行分析,此时用parameter不行,这个不能随时改变,需要定义输入端口,输入端口可以根据用户需求进行改变

parameter定义出的是常量,是实际数值,在编译时候会换算成数值在代码中,所以该定义编译后就是确定的数值,不能再进行变化

思路,现在以单个状态周期位为之间基准值,
定义counter1,去计数这个时间基准值
定义counter2,去计数这时间基准值运行多少次,按照题目规定,每运行8次为完整一周期
在这里插入图片描述

让多个led按照设置的模式各自在一个周期中进行亮灭

根据题目进行分析,具体的要求与上个题目基本一样,知识需要根据led的数量添加对应的ctrl,以控制不同的led亮灭模式
在这里插入图片描述
同时控制多个信号在什么应用中最普遍?
比如SPI,IIC等这样的总线中,
等等这样的一些总线里面,它有一个最基本的时钟单位,然后控制多个信号,比如说SPI的CS、SCLK、MOSI、MISO,这些同时变化,就可以对应到我们这个程序中的LED0、LED1、LED2。
这个程序不是说要控制LED灯,而是要熟悉这种 控制方式,便于后期其他功能的应用
2025.1.6

思考题:每隔10ms执行一次完整的变化周期

思路:

  1. 复制之前的代码,需要在其中新增一counter,专门计数10ms这个延时时间,每完成这个计时进行一次状态周期的触发
  2. 在状态周期运行的过程中,这个counter停止计数,并清零。
  3. 当led状态周期结束后,触发counter重新计数。
  4. 等待counter计数满足10ms后,再次触发状态周期进行再一次的循环

自己的思路代码:

module counter_led_5(
    Clk,
    Reset_n,
    Ctrl,
    Time,
    Led
    );
    input Clk;
    input Reset_n;
    input [7:0] Ctrl;//8位输入
    input [31:0]Time ;
    output reg Led;
    reg flag = 0;//0为10ms无动作计时,1为led动作过程
    
    reg [31:0]counter ; //每短单独led变化周期计时 
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)    //有复位,则清零计数
        counter <= 0 ;
    else if(counter == Time - 1 )
        counter <= 0 ;
    else if(flag == 1)
        counter  <= counter  + 1'b1 ;
        
    reg [3:0]counter2 ;//led运行标志位计数
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)    //有复位,则清零计数
        counter2 <= 0 ;
    else if(counter == Time - 1 ) //当计数满足Time这个时间后,couter+1
        counter2  <= counter2 + 1'b1 ; 
    else if (counter2 == 8 )//当完成整个led运行周期时候,
        begin
        	counter2 = 0 ;//及时清零运动段数量标志
        	flag <= 0 ;//开启10ms空白段计时
        end
  
    reg  [18:0]counter3;//空白10ms计时
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)    //有复位,则清零计数
        counter3 <= 0 ;
    else if(counter3 == 500000 - 1 )//计时10ms
        begin
            counter3 <= 0 ;
            flag <= 1 ;//当计时满足10ms,led才能动作
        end   
    else if(flag == 0) //当重新计时标志位为0时候,才嗯那个进行10ms计时
        counter3  <= counter3  + 1'b1 ;   
                                
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        Led <= 0 ;
    else if (flag == 0)
        Led <=8'b00000000; 
    else case(counter2)
        0:Led <= Ctrl[0] ;
        1:Led <= Ctrl[1] ;
        2:Led <= Ctrl[2] ;
        3:Led <= Ctrl[3] ;
        4:Led <= Ctrl[4] ;
        5:Led <= Ctrl[5] ;
        6:Led <= Ctrl[6] ;
        7:Led <= Ctrl[7] ;
        default:Led <= Led ; 
    endcase
endmodule

TB代码:
在这里插入图片描述
仿真结果
在这里插入图片描述

老师的思路
在这里插入图片描述
根据图示,自己理解的思路是错误的,是在10ms内进行一个动作周期,而不是动作周期结束后再进行10ms延时。

根据老师的思路,写如下代码
新增EN ,新增counter0

module counter_led_6(
    Clk,
    Reset_n,
    Ctrl,
    Time,
    Led
    );
    input Clk;
    input Reset_n;
    input [7:0] Ctrl;//8位输入
    input [31:0]Time ;
    output reg Led;
   
    //10ms计时器
    reg [18:0]counter0;
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)    //有复位,则清零计数
        counter0 <= 0 ;
    else if(counter0 == 500000-1)
        counter0 <= 0 ;
    else
        counter0  <= counter0  + 1'b1 ;
        
    reg EN;
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)    //有复位,则清零计数
        EN <= 0 ;
    else if(counter0 == 0)
        EN <= 1 ; 
    else if(counter2 == 7)
        EN <= 0 ;    
    
    reg [31:0]counter ; 
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)    //有复位,则清零计数
        counter <= 0 ;
    else if (EN) begin
        if(counter == Time - 1 )
            counter <= 0 ;
        else
            counter  <= counter  + 1'b1 ;
    end
    else
        counter <= 0 ;
        
    reg [2:0]counter2 ;//计数3位,可进行0-7循环计数   
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)    //有复位,则清零计数
        counter2 <= 0 ;
    else if(EN)begin
         if(counter == Time - 1 )
            counter2  <= counter2 + 1'b1 ; 
    end
    else 
       counter2 <= 0 ;
                            
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        Led <= 0 ; 
    else case(counter2)
        0:Led <= Ctrl[0] ;
        1:Led <= Ctrl[1] ;
        2:Led <= Ctrl[2] ;
        3:Led <= Ctrl[3] ;
        4:Led <= Ctrl[4] ;
        5:Led <= Ctrl[5] ;
        6:Led <= Ctrl[6] ;
        7:Led <= Ctrl[7] ;
        default:Led <= Led ; 
    endcase
endmodule

仿真TB:
在这里插入图片描述
2025.01.08

如何将代码中的参数放置到模拟波形参数中可见

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值