初识FPGA

113 阅读4分钟

示例一段最初的结构

module mux2_1
(
    input   wire    [0:0]   in_1,           //输入信号1
    input   wire    [0:0]   in_2,           //输入信号2
    input   wire    [0:0]   sel,            //选通信号
    
    output  reg             out
);


always@(*)                  //等效为   always@(sel,in_1,in_2)
    if(sel==1'b1)
        out=in_1;
    else
        out=in_2;
endmodule

->verilog基础

1.认识module和endmodule

  • moduleendmodule是定义硬件模块的核心语法结构
  • 通过module封装代码,实现功能模块化,结束以endmodule
module  名称
(
   代码部分(涉及的输入输出设置)
);
代码(逻辑)部分
endmodule
  • 注意:一个Verilog文件可以包含多个module吗?可以,但不推荐。通常一个文件只定义一个module,便于管理。

2.wire和reg(Verilog中两种数据类型)

  • wire:表示电路中的物理连线,用于连接模块或逻辑元件(一般是直接的简单的逻辑单元)(不可存储值)
  • reg :不属于寄存器(多用于复杂的逻辑处理)(可存储值)
特性wirereg
赋值方式assign或模块端口连接alwaysinitial
默认状态z(高阻态)x(未知状态)

3.assign和always

  • 在Verilog中,assignalways是两种不同的赋值方式,分别对应组合逻辑时序逻辑/复杂逻辑的实现。

    对比:

特性assignalways
赋值方式assign 信号 = 表达式;always @(敏感列表) begin ... end
赋值触发条件右侧表达式任何变化立即更新敏感列表中的信号变化(组合逻辑)或事件(时序逻辑)触发
代码位置模块内部任意位置必须位于过程块(alwaysinitial)内
使用场景简单逻辑(如门电路、多路选择器)复杂逻辑(如if-elsecase多分支)
赋值对象wire类型reg类型
  • 个人的理解:wire配合assign使用,此处应用于简单的逻辑赋值,比如直接给定信号。而reg配合always使用,应用于具有复杂逻辑的场景
  • assign是在芯片上直接实现物理上的连接,而always是进行触发
  • 对于always句式中的敏感列表
//敏感列表的核心作用:
//敏感列表的作用是指定触发条件,当列表中的信号满足特定变化时,`always` 块内的代码会被执行。
// **语法**:`always @(a, b, c)` 或 `always @(*)`(自动推断敏感信号)。```

//方式1: 显式列出所有输入信号(易遗漏,不推荐!)
always @(a, b, sel) begin
    out = sel ? a : b;
end

//方式2: 使用 @(*) 自动推断所有输入信号(推荐!)
always @(*) begin
    out = sel ? a : b;
end

仿真文件

示例一段最初仿真文件

`timescale 1ns/1ns

module  tb_mux2_1();                 //模块定义
reg     in_1;                        //信号声明
reg     in_2;
reg     sel ;

wire    out ;

initial                              //初始化
   begin
       in_1    <=  1'b0;
       in_2    <=  1'b0;
       sel     <=  1'b0;
   end


always  #10 in_1 <= {$random} % 2;   //输入信号生成
always  #10 in_2 <= {$random} % 2;
always  #10 sel <= {$random} % 2;

initial begin                        //初始化
   
       $timeformat(-9,0,"ns",6);
       $monitor("@time %t:in_1=%b in_2=%b sel=%b out=%b",$time,in_1,in_2,sel,out);
end

mux2_1  mux2_1_inst                  //实例化
(
   .in_1(in_1),                     //输入信号
   .in_2(in_2),           
   .sel(sel),            

   .out (out)
);

endmodule

1.定义模块

-说明

-   测试平台没有输入输出端口(空括号 `()`),因为它是仿真的顶层模块。
-   通常以 `tb_` 前缀命名测试平台,表明其测试性质。

2.信号声明

  • 信号包含原模块需要进行测试的信号(可根据类型进行修改,以本测试文件为主体面向测试对象)

3.初始化

  • 初始化使用initial
//initial语法结构
//单个initial在整体代码中只执行一次

//复杂赋值:
initial begin
    语句  
end

//简单赋值:
initial 语句
  • 对于
initial begin                        //初始化
        $timeformat(-9,0,"ns",6);
        $monitor("@time %t:in_1=%b in_2=%b sel=%b out=%b",$time,in_1,in_2,sel,out);
end
  • $timeformat(-9,0,"ns",6); //(ns,保留小数点后0位,时间后缀字符,最小显示六位数)
  • $monitor句式中- 触发条件:当被监测的信号(如 in1in2selout)发生改变时,自动打印信息。
  • $monitor只需调用一次,即可在整个仿真过程中生效。

4.实例化

  • 在Verilog中,实例化模块时,模块名必须与被测文件中定义的模块名完全相同,而实例名可以自定义,但需符合命名规则。
  • 实例名的位置在模块实例化语句中明确指定:
模块名 实例名 (
    .端口1 (连接信号1),
    .端口2 (连接信号2),
    // ...
);
模块名 实例名 (
    .子模块端口1 (父模块信号1), 
    .子模块端口2 (父模块信号2), 
    // ...
);

注:学习过程个人理解