从零开始学习vivado——day 3 时序逻辑设计之计数器

本文介绍了如何使用Vivado平台设计并实现了一个基于D触发器的计数器,详细步骤包括创建工程文件、编写Verilog代码(计数逻辑、激励文件和时钟信号)、以及模拟结果显示。作者通过实例学习和分享了Verilog的新语法,如上升沿/下降沿触发和非阻塞赋值等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

研一从零开始学习verilog!!!此时不学何时学!
第一次写博客,以此激励自己努力学习!
我跟的视频教程是b站的一个up主,小梅哥爱漂流。


一、一、vivado是什么?

vivado是一个编译平台,可运行verilog代码,并且进行模拟仿真。


二、计数器的设计

1.创建新的工程文件并编译代码

(1)定义端口

module led_flash(
    Clk,
    Reset_n,
    Led
);
    input Clk;//时钟
    input Reset_n;//复位端
    output reg Led;//灯
    reg [24:0]counter;//计数变量

(2)计数与Led的变化

    always@(posedge Clk or negedge Reset_n)//posedge Clk代表时钟的上升沿,negedge Reset_n代表复位端下降沿
    if(!Reset_n)   //!代表取非,即如果Reset_n为低电平,则counter归零
        counter <=0;//<=为非阻塞赋值,此处可以理解为等于
    else if(counter == 25000000-1)//要计数25000000次,则需要减1,因为清零也算一次
        counter <=0 ;//清零,计数从头开始
    else 
        counter <= counter + 1'd1   ;
    
    always@(posedge Clk or negedge Reset_n)//posedge Clk代表时钟的上升沿,negedge Reset_n代表复位端下降沿
    if(!Reset_n)    //!代表取非,Reset_n为低电平时,Led也为低电平
        Led <=0;
    else if(counter == 25000000-1)
        Led <= !Led;

首先,解读一下代码。
其实注释的都很清楚了,计数器是利用D触发器进行设计的,即在时钟上升沿的时候触发,or negedge Reset_n这段代码其实不加也可以,因为代码里面并没有让Reset_n变为高电平之后再变为低电平。对于counter变量,需要用到if ,else if,else,因为它有三种情况,复位端为低电平时肯定是清零的,因为计数器没有工作,还有当计数器计满的时候也要清零,否则counter会加1.
而Led可以直接利用取反符号达到亮灯和灭灯的目的,因为要设计在1s内,有500ms是亮灯状态,500ms是熄灯状态,即从Led亮的一瞬间开始计时,到下一次灯亮的时间,应该刚好是1s。
时钟的周期为20ns,所以500ms/20ns=25000000次,也就是说counter需要计数25000000之后,Led灯的状态才会翻转。而25000000换算成二进制,有25位,所以定义的时候需要加个[24:0]。
再一点,计数的时候,当counter达到25000000-1的时候,就需要清零和翻转了,因为从25000000-1到0也算一次运行过程,即总共也是计数了25000000次。
因为Led和counter的变化条件都一样,嫌麻烦的话可以利用begin end语句将Led和counter的变化括起来即可。

(3)代码整合

module led_flash(
   Clk,
   Reset_n,
   Led
);
   input Clk;
   input Reset_n;
   output reg Led;
   reg [24:0]counter;
   
   always@(posedge Clk or negedge Reset_n)//posedge Clk代表时钟的上升沿,negedge Reset_n代表复位端下降沿
   if(!Reset_n)   //!代表取非,即如果Reset_n为低电平,则counter归零
       counter <=0;//<=为非阻塞赋值,此处可以理解为等于
   else if(counter == 25000000-1)//要计数25000000次,则需要减1,因为清零也算一次
       counter <=0 ;//清零,计数从头开始
   else 
       counter <= counter + 1'd1   ;
   
   always@(posedge Clk or negedge Reset_n)//posedge Clk代表时钟的上升沿,negedge Reset_n代表复位端下降沿
   if(!Reset_n)    //!代表取非,Reset_n为低电平时,Led也为低电平
       Led <=0;
   else if(counter == 25000000-1)
       Led <= !Led;
//led和counter两个变量分开进行会好一点,如果要合在一起写的话,需要用begin end括起来
//
endmodule

2.创建激励文件

(1)时间尺度

`timescale 1ns/1ns

依旧默认1ns
(2)例化

module led_flash_tb;
    reg Clk;
    reg Reset_n;
    wire Led;//输出为wire型

    led_flash led_flash(
    .Clk(Clk),
    .Reset_n(Reset_n),
    .Led(Led)
);

(3)时钟信号编写

    initial Clk =1;
    always#10 Clk=!Clk;//!是逻辑取反,`是按位取反,对于单信号来说是等价的   整句话就是延时10ns之后,Clk总是取反

10ns让时钟翻转一次即可,因为要设计的计数器是1s的间隔,所以也可以把是时钟的周期延长一点,这个都可以自己修改。
(4)复位端信号编写

    initial begin 
     Reset_n=0;//初始复位端为低电平
     #101;//避开时钟上升沿
     Reset_n=1;//101ns之后复位端变高电平
     #1000000000;
     $stop;//1s之后停止
    end

复位端初始为低电平,Led处于清零状态,错开时钟上升沿之后,将复位端变为高电平,为防止一直运行下去,可以使用stop停止。
(4)代码整合

`timescale 1ns/1ns

module led_flash_tb;
    reg Clk;
    reg Reset_n;
    wire Led;//输出为wire型

    led_flash led_flash(
    .Clk(Clk),
    .Reset_n(Reset_n),
    .Led(Led)
);
 
    initial Clk =1;
    always#10 Clk=!Clk;//!是逻辑取反,`是按位取反,对于单信号来说是等价的   整句话就是延时10ns之后,Clk总是取反
    
    initial begin 
     Reset_n=0;//初始复位端为低电平
     #101;//避开时钟上升沿
     Reset_n=1;//101ns之后复位端变高电平
     #1000000000;
     $stop;//1s之后停止
    end



endmodule

3.仿真结果展示

在这里插入图片描述

可以看出,Led第一次翻转到第二次翻转的间隔为500ms,成功完成设计。


总结

今天学习的是基于D触发器设计的计数器。
新学的语法有:

①上升沿,下降沿

always@(posedge Clk or negedge Reset_n)

poseedge是沿上升沿触发,negedge是沿下降沿触发;
②时钟信号的产生

    initial Clk =1;
    always#10 Clk=!Clk;

③if else内嵌一个if else

    if(!Reset_n)   
        counter <=0;
    else if(counter == 25000000-1)
        counter <=0 ;
    else 
        counter <= counter + 1'd1   ;

④<=为非阻塞赋值,目前还没搞清楚啥意思,后面再说。。。

<think>嗯,用户现在想知道如何在30天内安排学习内容来完成基于RISC-V RV32I的CPU设计。首先,我需要评估用户可能的技术背景。他们之前询问过学习路径,现在转向时间安排,说明他们可能已经有一定的数字逻辑和Verilog基础,或者至少了解过相关内容。但考虑到是从零开始,可能还是需要巩固基础。 接下来,用户想知道30天是否可行。这取决于每天投入的时间和用户的学习能力。通常,设计一个简单的RV32I CPU,比如单周期或三级流水线,对于有经验的人来说可能需要几周,但新手可能需要更长时间。不过,如果每天投入足够时间,并且有明确计划,30天是有可能的,但可能只能完成基础版本,不包括所有优化和验证步骤。 用户可能需要一个详细的学习计划,分阶段进行。我需要将之前的长期学习路径压缩到30天,合理分配每个阶段的时间。比如,前五天打基础,中间十天设计核心模块,再十天实现和验证,最后五天调试和优化。同时,需要强调关键任务,如数字逻辑、Verilog、RISC-V指令集和模块设计。 还要考虑用户可能遇到的困难,比如理解流水线冒险、验证测试、FPGA工具的使用等。因此,计划中需要包含足够的练习和实际编码时间,以及测试和调试的步骤。可能需要推荐一些资源,如开源项目、测试套件,以及调试工具如ILA的使用。 另外,用户可能希望知道每天的具体任务,所以需要细化到每一天的学习内容和目标。例如,前几天学习数字逻辑和Verilog,之后解析指令集,设计模块,编写代码,最后进行综合和测试。同时,提醒用户时间管理的重要性,避免陷入某个细节而耽误整体进度。 最后,要诚实地指出30天的挑战,可能需要加班加点,但确实可行,尤其是如果用户有较强的学习能力和时间投入。同时,建议从单周期开始,逐步过渡到流水线,以降低复杂度。验证部分可能比较耗时,但使用现有的测试套件可以节省时间。</think>以下是为期30天的**高强度学习与实践计划**,目标完成**RV32I单周期CPU**的FPGA实现(若每天投入6-8小时,可行但需严格遵循进度): --- ### **第一阶段:基础准备(Day 1-7)** | 天数 | 学习目标 | 关键任务 | |------|-----------------------------------|--------------------------------------------------------------------------| | 1 | 数字逻辑基础强化 | - 完成组合逻辑设计:实现4位ALU<br>- 掌握D触发器与寄存器文件设计 | | 2 | Verilog核心语法 | - 编写模块:多路选择器、移位器<br>- 理解`always@(*)`与`always@(posedge clk)`区别 | | 3 | RISC-V指令集速成 | - 手写解码所有**I-type**指令格式<br>- 完成指令->二进制转换练习(至少20条)| | 4 | CPU数据通路初探 | - 绘制单周期数据流图<br>- 标注PC、寄存器堆、ALU、内存接口连接关系 | | 5 | FPGA工具链搭建 | - 安装Vivado/Quartus<br>- 跑通LED闪烁示例工程<br>- 学习ILA调试器使用 | | 6 | 验证方法学 | - 用Verilator搭建测试框架<br>- 编写第一个ALU测试用例(覆盖+/-/AND/OR) | | 7 | **阶段验收** | - 实现并通过测试:32位加法器+寄存器文件<br>- 生成首个比特流并烧录FPGA验证| --- ### **第二阶段:核心实现(Day 8-20)** #### **Week 1: 模块级开发** ```mermaid gantt title 模块开发甘特图 dateFormat YYYY-MM-DD section 取指单元 指令内存模型 :active, des1, 2023-10-08, 2d PC计数器 : des2, after des1, 3d section 译码单元 指令解码器 :crit, des3, 2023-10-10, 4d 立即数生成器 : des4, after des3, 2d section 执行单元 ALU扩展实现 :crit, des5, 2023-10-12, 5d ``` | 关键里程碑 | 代码示例 | |--------------------|-----------------------------------| | **Day 12**完成控制信号生成 | ```verilog // 控制单元片段 case(opcode) 7&#39;b0110011: begin // R-type reg_write = 1&#39;b1; alu_src = 1&#39;b0; end 7&#39;b0000011: begin // lw mem_to_reg = 1&#39;b1; mem_write = 1&#39;b0; end endcase ``` | #### **Week 2: 系统集成** 1. **Day 15**:完成数据通路连接 - 解决关键路径问题:插入流水线寄存器 - 添加冒险检测单元(至少处理RAW冒险) 2. **Day 18**:通过官方测试套件 ```bash # 运行RISCV-Tests示例 make run TEST=rv32ui-p-add # 预期输出:Test passed at PC=0x00000100 ``` 3. **Day 20**:首次上板调试 - 使用ILA捕获异常跳转地址 - 测量实际时钟频率(目标:≥50MHz) --- ### **第三阶段:优化与交付(Day 21-30)** #### **关键优化项** 1. **时序优化**(Day 21-23): - 分析综合报告,重点优化: ```tcl # XDC约束示例 set_max_delay 0.5 -from [get_pins alu/*] set_false_path -through [get_clocks clk2x] ``` 2. **面积优化**(Day 24-26): - 资源占用目标(Artix-7): | 资源类型 | 使用量 | 优化手段 | |----------|---------|------------------------| | LUT | <1500 | 共享加法器/常量折叠 | | FF | <800 | 减少流水线级数 | 3. **验证强化**(Day 27-29): - 覆盖率提升策略: ```systemverilog covergroup cg_alu; coverpoint alu_op { bins add = {4&#39;b0000}; bins sub = {4&#39;b0001}; // ... 其他操作码 } endgroup ``` #### **最终交付(Day 30)** - 提交物清单: ``` /project ├── rtl/ # Verilog源码 ├── sim/ # 测试用例 ├── constraints/ # XDC约束文件 └── doc/ # 架构说明书+时序报告 ``` --- ### **可行性评估** 1. **可实现范围**: - **基础版**:单周期RV32I(支持40+指令) - **进阶版**:三级流水线(需处理数据冒险) 2. **风险预警**: - 最大挑战:**访存延迟处理**(建议使用Block RAM) - 常见错误:未处理指令对齐(需添加`misaligned exception`) 3. **效率提升技巧**: - 复用开源组件(如PicoRV32的CSR模块) - 使用预生成的测试向量(RISCV-Tests) 如果严格按计划执行,每天保持**高质量代码产出+3小时仿真调试**,完成基础版CPU完全可行。建议使用**Vivado非工程模式**(脚本化流程)节省编译时间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值