verilog初学时候的总结

零,关于写verilog代码写激励的总结:数据结构

1,激励(test_name.v):当作一个总体,:dom

module test_name;函数

reg+wire;(全部的输入输出的,中间变量的不写)工具

name 别名( .xxx(yyy) );设计

always #1 CLK=~CLK;blog

initial begin all input+output初始化 end字符串

附:仿真控制语句及系统任务描述:input

(1)仿真控制语句及系统任务描述:it

$stop //中止运行仿真,modelsim 中可继续仿真
$stop(n) //带参数系统任务,根据参数 0,1 或 2 不一样,输出仿真信息
$finish //结束运行仿真,不可继续仿真
$finish(n) //带参数系统任务,根据参数 0,1 或 2 不一样,输出仿真信息
//0:不输出任何信息
//1:输出当前仿真时刻和位置
//2:输出当前仿真时刻、位置和仿真过程当中用到的 memory 以及 CPU 时间的统计
$random  //产生随机数
$random % n //产生范围-n 到 n 之间的随机数
{$random} % n //产生范围 0 到 n 之间的随机数

(2)仿真终端显示描述
$monitor //仿真打印输出,大印出仿真过程当中的变量,使其终端显示
                                 $monitor($time,,,"clk=%d reset=%d out=%d",clk,reset,out);
$display //终端打印字符串,显示仿真结果等
                                  $display(” Simulation start ! ");或$display(” At time %t,input is %b%b%b,output is %b",$time,a,b,en,z);io

$time //返回 64 位整型时间
$stime  //返回 32 位整型时间
$realtime //实行实型模拟时间

(3)文本输入方式:$readmemb/$readmemh
//激励具备复杂的数据结构
//verilog 提供了读入文本的系统函数
$readmemb/$readmemh("<数据文件名>",<存储器名>);
$readmemb/$readmemh("<数据文件名>",<存储器名>,<起始地址>);
$readmemb/$readmemh("<数据文件名>",<存储器名>,<起始地址>,<结束地址>);
$readmemb:读取二进制数据,读取文件内容只能包含:空白位置,注释行,二进制数数据中不能包含位宽说明和格式说明,每一个数字必须是二进制数字。
$readmemh:读取十六进制数据,读取文件内容只能包含:空白位置,注释行,十六进制数数据中不能包含位宽说明和格式说明,每一个数字必须是十六进制数字。
当地址出如今数据文件中,格式为@hh...h,地址与数字之间不容许空白位置,可出现多个地址
module
reg [7:0] memory[0:3];//声明 8 个 8 位存储单元
integer i;
initial
begin
$readmemh("mem.dat",memory);//读取系统文件到存储器中的给定地址
//显示此时存储器内容
for(i=0;i<4;i=i+1)
$display("Memory[%d]=%h",i,memory[i]);
end
endmodule   

2,程序(name.v):明确输入输出量和另外的控制信号

module name(xxx,yyy);

input + output;(输入输出)

reg +wire; (中间变量)

parameter 之类的申明; 可能状态机或位宽处要用到

always@(posedge CLK)

assign aaa =bbb ^ ccc[ WIDTH-1];

 

3,封装:当作一个总体,明确输入输出和中间变量;

//定义封装模块的时候,只定义总体来  看的reg和wire。中间 模块的连线都用wire。

module contract_name(CLK,RST_n);只能是总体的输入输出变量

input+output; 上述出现过的变量

wire ; 全部的中间变量,不论方向如何;

left_name name1( .xxx(yyy) );

right_name name2( .xxx(yyy) );

assign aaa=bbb;

仿真报错总结:

1, 仿真出现红色线是由于没有初始化,在它原本应该初始化的地方查找错误。

    根据可否输入,在哪一个位置才能输入,进行初始化,不必定在激励那个位置。

2,IP核输入变成了灰色线是由于上层传输中的激励模块里面的元素写漏了,没有从上一层传入到下一层。

     若是并列的两个模块里面,模块A有某个信号(仿真时绿色线),一个模块B里面没有 某个信号(仿真时候蓝色线或绿色线没数据),说明上一层的封装A没有将该信号传给B。

3,仿真时候出现蓝色的线,报错的缘由可能有:

(1),位宽在接入的时候匹配的位宽不对。

(2),上一层模块没有传递接入到这一层模块,  检查上一层的初始化接线。

(3)是否初始化,输出是否例化;输入输出传递是否正确。

 

注意:

1,封装后的模块也极可能仍然要被封装,因此他的格式与程序基本同样,

不一样之处在于别名赋值那里,这就是封装之处。还有封装中间变量都是wire,没有reg;

2,A通常多是输出,B,C通常是中间reg变量,但A通常是用于输出的,否则这么写没意义;

或者在封装模块中,bbb,ccc多是wire中间连线类型的变量;

3,程序中的条件判断:

if(input_name==xxx) output_name<=yyy;

由于输出才是对外的,输入量是从外面其它模块传进来现成的,只能当作条件判断,不能动它的值;

if(reg_name==xxx) state<=yyy;或者 output_name<=yyy;

4,激励中的条件判断:

if(wire_name==xxx) reg_name<=yyy;其分别对应的是输出和输入,跟上面程序中是反的。

由于能变更的是寄存器类型的,wire做为传输的连线,只能用来作条件判断。

5,模块能够有多种不一样的组合方式,输入输出连线正确。认定一种封装就坚持写下去,别的封装只是耗时间了,都差很少。

 

一,reg和wire和assign

    wire表示直通,即只要输入有变化,输出立刻无条件地反映,线信号;如:wire  clk; 其中 clk 就是 wire 类型的信号;reg 关键词,寄存器。和线信号不一样,它能够在 always 中被赋值,常常用于时序逻辑中。好比 reg[3:0]led,表示了一组寄存器。

    wire只能被assign连续赋值,reg只能在initial和always中赋值。wire使用在连续赋值语句中,而reg使用在过程赋值语句中。在连续赋值语句中,表达式右侧的计算结果能够当即更新表达式的左侧。在理解上,至关于一个逻辑以后直接连了一条线,这个逻辑对应于表达式的右侧,而这条线就对应于wire。在过程赋值语句中,表达式右侧的计算结果在某种条件的触发下放到一个变量当中,而这个变量能够声明成reg类型的。

        输入端口能够由wire/reg驱动,但输入端口只能是wire;输出端口可使wire/reg类型,输出端口只能驱动wire;若输出端口在过程块中赋值则为reg型,若在过程块外赋值则为net型。用关键词inout声明一个双向端口, inout端口不能声明为reg类型,只能是wire类型;输入和双向端口不能声明为寄存器类型。要是出现不能给input赋值,那么就reg产生一个输出,与它进行封装链接,直接操做reg就能够了;或者在上一层进行输出初始化和赋值。

     assign 用来给 output,inout 以及 wire 这些类型进行连线。assign 至关于一条连线,将表达式右边的电路直
接经过 wire(线)链接到左边,左边信号必须是 wire 型(output 和 inout 属于 wire 型)。当右边变化了左边立马
变化,方便用来描述简单的组合逻辑;

例:wire a,b,c;          assign  c=a&b;

二,case和endcase(用于状态机编写)

case(s)
    1:begin
     ...
    end
    2:begin
      ...
      end
     default:

        begin
           ...
       end
endcase

三,include & define(预处理命令,用于定义常量)
例:

`include myfile.v
`define X = 1;
`deine Y;
`ifdef Y
       Z=1;
`else
       Z=0;
`endif

例:

好比,个人 main_topv 文件想调用 my_lcd.h,能够在main_top.v 写到:

`define VGA_1024_800_60MHz //这句要放在"my_lcd.h"的上面,否则编译不经过
`include "my_lcd.h"

四,verilog 大括号{}做用

一、{ }表示拼接,{第一位,第二位...};

二、{{ }}表示复制,{4{a}}等同于{a,a,a,a};例:{13{1‘b1}}就表示将13个1拼接起来,即13'b1111111111111。

3,verilog中,中括号[]的做用:

          不加中括号直接写 reg Count1,那综合工具会认为Count1为1位的寄存器;

            reg [21:0]Count1;与reg [22:1]Count1;都是定义Count1为22为寄存器。

五,阻塞和非阻塞

1,非阻塞赋值,A 和 B 是同时被赋值的,具体是说在时钟的上升沿来的时刻,A 和 B (或调换 A 和 B )同时被置 1。

always @(posedge clk)
begin
    A <= 1'b1;
    B <= 1'b1;

end

2,阻塞赋值,它少了一个非,表示会阻塞住;这个程序是阻塞和非阻塞的混合使用,不理解的话乱用会致使时序违规,为了更好的理解阻塞赋值:当时钟上升沿来临的时刻,首先 A 会被置 1,而后 B 寄存器再置 1。区别就是 A 和 B 再也不同时置 1。A 要比 B 提早零点几纳秒。这样就出现了前后顺序。这个过程仍是在一个时钟内完成,可是数据到达 B 寄存器相比上面两段程序晚了那么零点几纳秒!当咱们的时钟跑的比较慢的时候,好比 50M,一个周期有 20ns,那么这么短暂的延时基本能够忽略不计,可是随着设计的复杂,时钟速度提升就不能忽略。

always @(posedge clk)
begin
A = 1'b1;
B <= 1'b1;
end

3,时序对比

module unblock
(
input clk_i,
input rst_n_i,
output reg [4:0]result_o
);
reg [3:0]A;
reg [3:0]B;
reg [4:0]C;
reg i;
always @(posedge clk_i )
if(!rst_n_i)
begin
#2
       A <= 4'd4;
       B <= 4'd12;
       C <= 5'd0;
        result_o = 5'd0;
end
else

begin
#2
        C <= A + B;
        result_o <= (C >> 1);
end

 

 

always @(posedge clk_i)
if(!rst_n_i)
begin
       #2 A = 4'd4;
       #0.2 B = 4'd12;
       #0.2 C = 5'd0;
      #0.2 result_o = 5'd0;
end
else begin
        #2 C = A + B;
        #0.2 result_o = (C >> 1);
end
endmodule

阻塞和非阻塞赋值:经过阻塞的方法提早获得 C 的值,再将 C 右移 1 位,达到除以 2 的效果。整个过程耗时一个时钟。